diff --git a/.devcontainer/.devcontainer.json b/.devcontainer/.devcontainer.json new file mode 100644 index 00000000000000..2e3cde606ce422 --- /dev/null +++ b/.devcontainer/.devcontainer.json @@ -0,0 +1,19 @@ +{ + "name": "Node.js Core Developer Environment", + "extensions": [ + "github.vscode-pull-request-github", + "ms-vsliveshare.vsliveshare", + "vscode-icons-team.vscode-icons", + "visualstudioexptteam.vscodeintellicode" + ], + "dockerFile": "Dockerfile", + "initializeCommand": "docker system prune -f -a", + "settings": { + "terminal.integrated.profiles.linux": { + "zsh (login)": { + "path": "zsh", + "args": ["-l"] + } + } + } +} diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000000000..3e8e7ecbfbcbdd --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1 @@ +FROM nodejs/devcontainer:nightly diff --git a/.flake8 b/.flake8 deleted file mode 100644 index eee7c33b89d262..00000000000000 --- a/.flake8 +++ /dev/null @@ -1,3 +0,0 @@ -[flake8] -exclude=.git,deps,lib,src,tools/gyp,tools/inspector_protocol,tools/pip,tools/v8_gypfiles/broken -ignore=E1,E2,E3,E4,E5,E7,W5,W6 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d26fb30e3cdc44..49d850eea51a7a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -115,6 +115,8 @@ # Startup +/test/parallel/test-snapshot-* @nodejs/startup +/test/parallel/test-bootstrap-* @nodejs/startup /benchmark/misc/startup.js @nodejs/startup /src/node.cc @nodejs/startup /src/node_realm* @nodejs/startup @nodejs/realm @@ -144,8 +146,7 @@ # Single Executable Applications /deps/postject @nodejs/single-executable /doc/api/single-executable-applications.md @nodejs/single-executable -/doc/contributing/maintaining-postject.md @nodejs/single-executable -/doc/contributing/maintaining-single-executable-application-support.md @nodejs/single-executable +/doc/contributing/maintaining/maintaining-single-executable-application-support.md @nodejs/single-executable /src/node_sea* @nodejs/single-executable /test/fixtures/postject-copy @nodejs/single-executable /test/parallel/test-single-executable-* @nodejs/single-executable diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 27cdab2e7c3cad..7a47cbcadc9d26 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,4 +6,6 @@ updates: directory: / schedule: interval: monthly + commit-message: + prefix: meta open-pull-requests-limit: 10 diff --git a/.github/label-pr-config.yml b/.github/label-pr-config.yml index 85f3d810d1c296..3d877a39227299 100644 --- a/.github/label-pr-config.yml +++ b/.github/label-pr-config.yml @@ -2,12 +2,12 @@ ## earlier entries override later entries subSystemLabels: # src subsystems - /^src\/async-wrap/: c++, async_wrap + /^src\/async_wrap/: c++, async_wrap /^src\/(?:base64|node_buffer|string_)/: c++, buffer /^src\/cares/: c++, cares /^src\/(?:process_wrap|spawn_)/: c++, child_process /^src\/(?:node_)?crypto/: c++, crypto - /^src\/(?:debug-|node_debug)/: c++, debugger + /^src\/debug_/: c++, debugger /^src\/udp_/: c++, dgram /^src\/(?:fs_|node_file|node_stat_watcher)/: c++, fs /^src\/node_http_parser/: c++, http_parser @@ -16,14 +16,13 @@ subSystemLabels: /^src\/(?:connect(?:ion)?|pipe|tcp)_/: c++, net /^src\/node_os/: c++, os /^src\/(?:node_main|signal_)/: c++, process - /^src\/timer_/: c++, timers - /^src\/(?:CNNICHashWhitelist|node_root_certs|tls_)/: c++, tls + /^src\/timer[_s]/: c++, timers + /^src\/node_root_certs/: c++, tls /^src\/tty_/: c++, tty /^src\/node_url/: c++, whatwg-url /^src\/node_util/: c++, util /^src\/node_v8/: c++, v8 engine /^src\/node_contextify/: c++, vm - /^src\/.*win32.*/: c++, windows /^src\/node_zlib/: c++, zlib /^src\/tracing/: c++, tracing /^src\/(?:node_api|js_native_api)/: c++, node-api @@ -35,31 +34,27 @@ subSystemLabels: /^src\/node_bob*/: c++, quic, dont-land-on-v14.x /^src\/node_sea/: single-executable - # don't label python files as c++ - /^src\/.+\.py$/: python, needs-ci - - # properly label changes to v8 inspector integration-related files + # Properly label changes to V8 inspector integration-related files /^src\/inspector_/: c++, inspector, needs-ci - # don't want to label it a c++ update when we're "only" bumping the Node.js version + # Don't want to label it a C++ update when we're "only" bumping the Node.js version /^src\/(?!node_version\.h)/: c++ # BUILDING.md should be marked as 'build' in addition to 'doc' /^BUILDING\.md$/: build, doc - # meta is a very specific label for things that are policy and or meta-info related - /^([A-Z]+$|CODE_OF_CONDUCT|ROADMAP|WORKING_GROUPS|GOVERNANCE|CHANGELOG|\.mail|\.git.+)/: meta - # things that edit top-level .md files are always a doc change + # 'meta' is a very specific label for things that are policy and or meta-info related + /^(?:[A-Z]+$|CODE_OF_CONDUCT|GOVERNANCE|CHANGELOG|\.mail|\.git.+)/: meta + # Things that edit top-level .md files are always a doc change /^\w+\.md$/: doc - # different variants of *Makefile and build files - /^(tools\/)?(Makefile|BSDmakefile|create_android_makefiles|\.travis\.yml)$/: build, needs-ci - /^tools\/(install\.py|genv8constants\.py|getnodeversion\.py|js2c\.py|utils\.py|configure\.d\/.*)$/: build, python, needs-ci + # Different variants of Makefile and build files + /^(?:tools\/)?(?:Makefile|BSDmakefile|create_android_makefiles)$/: build, needs-ci + /^tools\/(?:install\.py|genv8constants\.py|getnodeversion\.py|js2c\.py|utils\.py|configure\.d\/.*)$/: build, python, needs-ci /^vcbuild\.bat$/: build, windows, needs-ci - /^(android-)?configure|node\.gyp|common\.gypi$/: build, needs-ci - # more specific tools + /^(?:android-)?configure|node\.gyp|common\.gypi$/: build, needs-ci + # More specific tools /^tools\/gyp/: tools, build, gyp, needs-ci, dont-land-on-v14.x /^tools\/doc\//: tools, doc /^tools\/icu\//: tools, i18n-api, icu, needs-ci - /^tools\/(?:osx-pkg\.pmdoc|pkgsrc)\//: tools, macos, install - /^tools\/(?:(?:mac)?osx-)/: tools, macos + /^tools\/osx-/: tools, macos /^tools\/test-npm/: tools, test, npm /^tools\/test/: tools, test /^tools\/(?:certdata|mkssldef|mk-ca-bundle)/: tools, openssl, tls @@ -67,16 +62,16 @@ subSystemLabels: /^tools\/[^/]+\.bat$/: tools, windows, needs-ci /^tools\/make-v8/: tools, v8 engine, needs-ci /^tools\/v8_gypfiles/: tools, v8 engine, needs-ci - /^tools\/(code_cache|snapshot)/: needs-ci + /^tools\/snapshot/: needs-ci /^tools\/build-addons.mjs/: needs-ci - # all other tool changes should be marked as such + # All other tool changes should be marked as such /^tools\//: tools - /^\.eslint|\.remark|\.editorconfig/: tools + /^\.eslint|\.editorconfig/: tools /^typings\//: typings ## Dependencies # libuv needs an explicit mapping, as the ordinary /deps/ mapping below would - # end up as libuv changes labeled with "uv" (which is a non-existing label) + # end up as libuv changes labeled with 'uv' (which is a non-existing label) /^deps\/uv\//: libuv /^deps\/v8\/tools\/gen-postmortem-metadata\.py/: v8 engine, python, post-mortem /^deps\/v8\//: v8 engine @@ -85,18 +80,17 @@ subSystemLabels: /^deps\/nghttp2\/nghttp2\.gyp/: build, http2 /^deps\/nghttp2\//: http2 /^deps\/ngtcp2\//: quic, dont-land-on-v14.x - /^deps\/nghttp3\//: quic, dont-land-on-v14.x /^deps\/([^/]+)/: dependencies, $1 ## JS subsystems # Oddities first - /^lib\/(punycode|\w+\/freelist|sys\.js)/: '' + /^lib\/(?:punycode|\w+\/freelist|sys\.js)/: '' /^lib\/constants\.js$/: lib / src - /^lib\/_(debug_agent|debugger)\.js$/: debugger - /^lib(\/\w+)?\/(_)?link(ed)?list/: timers - /^lib\/\w+\/bootstrap_node/: lib / src - /^lib\/\w+\/v8_prof_/: tools - /^lib\/\w+\/socket_list/: net + /^lib\/internal/debugger$/: debugger + /^lib\/internal\/linkedlist\.js$/: timers + /^lib\/internal\/bootstrap/: lib / src + /^lib\/internal\/v8_prof_/: tools + /^lib\/internal\/socket(?:_list|address)\.js$/: net /^lib\/\w+\/streams$/: stream /^lib\/.*http2/: http2 /^lib\/worker_threads.js$/: worker @@ -112,22 +106,21 @@ subSystemLabels: /^lib(?:\/internal)?\/(\w+)(?:\/|$)/: $1 # Subfolders exlusiveLabels: - # more specific tests + # More specific tests /^test\/addons\//: test, addons - /^test\/debugger\//: test, debugger + /^test\/debugger/: test, debugger /^test\/doctool\//: test, doc, tools - /^test\/timers\//: test, timers + /^test\/timers/: test, timers /^test\/pseudo-tty\//: test, tty - /^test\/inspector\//: test, inspector + /^test\/inspector/: test, inspector /^test\/cctest\/test_inspector/: test, inspector - /^test\/cctest\/test_url/: test, whatwg-url /^test\/node-api\//: test, node-api /^test\/js-native-api\//: test, node-api /^test\/async-hooks\//: test, async_hooks /^test\/report\//: test, report /^test\/fixtures\/es-module/: test, esm /^test\/es-module\//: test, esm - /^test\/fixtures\/wpt\/streams//: test, web streams + /^test\/fixtures\/wpt\/streams\//: test, web streams /^test\//: test @@ -138,11 +131,9 @@ exlusiveLabels: # node-api is treated separately since it is not a JS core module but is still # considered a subsystem of sorts /^doc\/api\/n-api.md$/: doc, node-api - # quic - /^doc\/api\/quic.md$/: doc, quic, dont-land-on-v14.x # Add worker label to PRs that affect doc/api/worker_threads.md /^doc\/api\/worker_threads.md$/: doc, worker - # test runner documentation + # test_runner documentation /^doc\/api\/test.md$/: doc, test_runner, dont-land-on-v14.x # Automatically tag JS subsystem-specific API doc changes /^doc\/api\/(\w+)\.md$/: doc, $1 @@ -154,13 +145,14 @@ exlusiveLabels: # More specific benchmarks /^benchmark\/buffers\//: benchmark, buffer - /^benchmark\/(?:arrays|es)\//: benchmark, v8 engine + /^benchmark\/es\//: benchmark, v8 engine /^benchmark\/_http/: benchmark, http /^benchmark\/(?:misc|fixtures)\//: benchmark /^benchmark\/streams\//: benchmark, stream + /^benchmark\/url\//: benchmark, url, whatwg-url /^benchmark\/([^/]+)\//: benchmark, $1 - /^benchmark\//: benchmark + /^benchmark\//: benchmark, performance allJsSubSystems: - assert @@ -172,6 +164,7 @@ allJsSubSystems: - crypto - debugger - dgram + - diagnostics_channel - dns - domain - events @@ -180,11 +173,14 @@ allJsSubSystems: - http - https - http2 + - inspector - module - net - os - path + - perf_hooks - process + - punycode - querystring - quic - readline @@ -194,6 +190,7 @@ allJsSubSystems: - string_decoder - timers - tls + - trace_events - tty - typings - url diff --git a/.github/workflows/daily-wpt-fyi.yml b/.github/workflows/daily-wpt-fyi.yml index 0e2c3df9fcbacb..4c575b76ae2bb8 100644 --- a/.github/workflows/daily-wpt-fyi.yml +++ b/.github/workflows/daily-wpt-fyi.yml @@ -98,7 +98,7 @@ jobs: run: rm -rf deps/undici - name: Checkout undici if: ${{ env.WPT_REPORT != '' }} - uses: actions/checkout@v3 + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: repository: nodejs/undici persist-credentials: false diff --git a/.github/workflows/find-inactive-tsc.yml b/.github/workflows/find-inactive-tsc.yml index 15ccfd5dd82c84..eb53619209b52d 100644 --- a/.github/workflows/find-inactive-tsc.yml +++ b/.github/workflows/find-inactive-tsc.yml @@ -59,3 +59,4 @@ jobs: commit-message: 'meta: move TSC voting member(s) to regular member(s)' labels: meta title: 'meta: move TSC voting member(s) to regular member(s)' + update-pull-request-title-and-body: true diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 96eb4604a20b70..e9b2f776de26a5 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -129,7 +129,7 @@ jobs: run: npx envinfo - name: Lint Python run: | - make lint-py-build || true + make lint-py-build make lint-py lint-yaml: if: github.event.pull_request.draft == false diff --git a/.github/workflows/notify-on-push.yml b/.github/workflows/notify-on-push.yml index 6a307c6ff98245..5be0bd7c7fde64 100644 --- a/.github/workflows/notify-on-push.yml +++ b/.github/workflows/notify-on-push.yml @@ -34,7 +34,7 @@ jobs: permissions: pull-requests: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: persist-credentials: false - name: Check commit message diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 27ca97808cd52a..c758c9a491bb41 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -43,7 +43,7 @@ jobs: persist-credentials: false - name: Run analysis - uses: ossf/scorecard-action@e38b1902ae4f44df626f11ba0734b14fb91f8f86 # v2.1.2 + uses: ossf/scorecard-action@80e868c13c90f172d68d1f4501dee99e2479f7af # v2.1.3 with: results_file: results.sarif results_format: sarif diff --git a/.github/workflows/timezone-update.yml b/.github/workflows/timezone-update.yml index 0c6b6f5fafd92c..e3e4b7323f4743 100644 --- a/.github/workflows/timezone-update.yml +++ b/.github/workflows/timezone-update.yml @@ -59,3 +59,4 @@ jobs: labels: dependencies title: 'deps: update timezone to ${{ env.new_version }}' reviewers: \@nodejs/i18n-api + update-pull-request-title-and-body: true diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml index 5ba655e9d88c72..30f9b4fcebceb8 100644 --- a/.github/workflows/tools.yml +++ b/.github/workflows/tools.yml @@ -5,6 +5,36 @@ on: - cron: 5 0 * * 0 workflow_dispatch: + inputs: + id: + description: The ID of the job to run + required: true + default: all + type: choice + options: + - all + - acorn + - acorn-walk + - ada + - base64 + - brotli + - c-ares + - cjs-module-lexer + - corepack + - doc + - eslint + - googletest + - libuv + - lint-md-dependencies + - llhttp + - nghttp2 + - nghttp3 + - ngtcp2 + - postject + - root-certificates + - simdutf + - undici + - uvwasi permissions: contents: read @@ -69,12 +99,10 @@ jobs: subsystem: deps label: dependencies run: | - NEW_VERSION=$(npm view undici dist-tags.latest) - CURRENT_VERSION=$(node -p "require('./deps/undici/src/package.json').version") - if [ "$NEW_VERSION" != "$CURRENT_VERSION" ]; then - echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV - ./tools/update-undici.sh - fi + ./tools/dep_updaters/update-undici.sh > temp-output + cat temp-output + tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true + rm temp-output - id: postject subsystem: deps,test label: test @@ -163,6 +191,14 @@ jobs: cat temp-output tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true rm temp-output + - id: minimatch + subsystem: deps + label: dependencies + run: | + ./tools/dep_updaters/update-minimatch.sh > temp-output + cat temp-output + tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true + rm temp-output - id: root-certificates subsystem: crypto label: crypto, notable-change @@ -176,18 +212,61 @@ jobs: cat temp-output tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true rm temp-output + - id: ngtcp2 + subsystem: deps + label: dependencies + run: | + ./tools/dep_updaters/update-ngtcp2.sh > temp-output + cat temp-output + tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true + rm temp-output + - id: nghttp3 + subsystem: deps + label: dependencies + run: | + ./tools/dep_updaters/update-nghttp3.sh > temp-output + cat temp-output + tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true + rm temp-output + - id: uvwasi + subsystem: deps + label: dependencies + run: | + ./tools/dep_updaters/update-uvwasi.sh > temp-output + cat temp-output + tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true + rm temp-output + - id: zlib + subsystem: deps + label: dependencies + run: | + ./tools/dep_updaters/update-zlib.sh > temp-output + cat temp-output + tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true + rm temp-output + - id: googletest + subsystem: deps + label: dependencies, test + run: | + ./tools/dep_updaters/update-googletest.sh > temp-output + cat temp-output + tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true + rm temp-output steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + if: github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id with: persist-credentials: false - run: ${{ matrix.run }} + if: github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id env: GITHUB_TOKEN: ${{ secrets.GH_USER_TOKEN }} - name: Generate commit message if not set - if: ${{ env.COMMIT_MSG == '' }} + if: env.COMMIT_MSG == '' && (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) run: | echo "COMMIT_MSG=${{ matrix.subsystem }}: update ${{ matrix.id }} to ${{ env.NEW_VERSION }}" >> "$GITHUB_ENV" - uses: gr2m/create-or-update-pull-request-action@77596e3166f328b24613f7082ab30bf2d93079d5 + if: github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id # Creates a PR or update the Action's existing PR, or # no-op if the base branch is already up-to-date. env: @@ -199,3 +278,4 @@ jobs: commit-message: ${{ env.COMMIT_MSG }} labels: ${{ matrix.label }} title: '${{ matrix.subsystem }}: update ${{ matrix.id }} to ${{ env.NEW_VERSION }}' + update-pull-request-title-and-body: true diff --git a/.github/workflows/update-openssl.yml b/.github/workflows/update-openssl.yml index f79ea550b42818..00060b5fdf7dd6 100644 --- a/.github/workflows/update-openssl.yml +++ b/.github/workflows/update-openssl.yml @@ -14,7 +14,7 @@ jobs: if: github.repository == 'nodejs/node' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: persist-credentials: false - name: Check if update branch already exists @@ -39,7 +39,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GH_USER_TOKEN }} - name: Create PR with first commit if: env.HAS_UPDATE - uses: gr2m/create-or-update-pull-request-action@v1 + uses: gr2m/create-or-update-pull-request-action@df20b2c073090271599a08c55ae26e0c3522b329 # v1.9.2 # Creates a PR with the new OpenSSL source code committed env: GITHUB_TOKEN: ${{ secrets.GH_USER_TOKEN }} @@ -51,6 +51,7 @@ jobs: labels: dependencies title: 'deps: update OpenSSL to ${{ env.NEW_VERSION }}' path: deps/openssl + update-pull-request-title-and-body: true - name: Regenerate platform specific files if: env.HAS_UPDATE run: | @@ -61,7 +62,7 @@ jobs: - name: Add second commit # Adds a second commit to the PR with the generated platform-dependent files if: env.HAS_UPDATE - uses: gr2m/create-or-update-pull-request-action@v1 + uses: gr2m/create-or-update-pull-request-action@df20b2c073090271599a08c55ae26e0c3522b329 # v1.9.2 env: GITHUB_TOKEN: ${{ secrets.GH_USER_TOKEN }} with: diff --git a/.github/workflows/update-v8.yml b/.github/workflows/update-v8.yml new file mode 100644 index 00000000000000..5b7e11303e0edc --- /dev/null +++ b/.github/workflows/update-v8.yml @@ -0,0 +1,55 @@ +name: V8 patch update +on: + schedule: + # Run once a week at 00:05 AM UTC on Sunday. + - cron: 5 0 * * 0 + workflow_dispatch: + +env: + NODE_VERSION: lts/* + +permissions: + contents: read + +jobs: + v8-update: + if: github.repository == 'nodejs/node' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + with: + persist-credentials: false + - name: Cache node modules and update-v8 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 + id: cache-v8-npm + env: + cache-name: cache-v8-npm + with: + path: | + ~/.update-v8 + ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }} + - name: Install Node.js + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Install node-core-utils + run: npm install -g node-core-utils@latest + - name: Check and download new V8 version + run: | + ./tools/dep_updaters/update-v8-patch.sh > temp-output + cat temp-output + tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true + rm temp-output + - uses: gr2m/create-or-update-pull-request-action@77596e3166f328b24613f7082ab30bf2d93079d5 + # Creates a PR or update the Action's existing PR, or + # no-op if the base branch is already up-to-date. + env: + GITHUB_TOKEN: ${{ secrets.GH_USER_TOKEN }} + with: + author: Node.js GitHub Bot + body: This is an automated patch update of V8 to ${{ env.NEW_VERSION }}. + branch: actions/update-v8-patch # Custom branch *just* for this Action. + labels: v8 engine + title: 'deps: patch V8 to ${{ env.NEW_VERSION }}' + update-pull-request-title-and-body: true diff --git a/.gitignore b/.gitignore index b3399fa0df2655..79920931ce4e15 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ .* # Exclude specific dotfiles that we want to track. !deps/**/.* +!.devcontainer/ +!.devcontainer/.devcontainer.json !test/fixtures/**/.* !.clang-format !.cpplint @@ -14,7 +16,6 @@ !.eslintignore !.eslintrc.js !.eslintrc.yaml -!.flake8 !.gitattributes !.github !.gitignore diff --git a/.mailmap b/.mailmap index 142e9133c68265..61d21d68d2a9f8 100644 --- a/.mailmap +++ b/.mailmap @@ -343,6 +343,7 @@ Matteo Collina Matthew Lye Matthew Turner Matthias Bastian +Matthew Aitken Maurice Hayward Maya Lekova Mestery diff --git a/BUILDING.md b/BUILDING.md index 40220f75ad682f..6a6bf24194ce88 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -29,7 +29,7 @@ file a new issue. * [Running Coverage](#running-coverage) * [Building the documentation](#building-the-documentation) * [Building a debug build](#building-a-debug-build) - * [Building an ASAN build](#building-an-asan-build) + * [Building an ASan build](#building-an-asan-build) * [Speeding up frequent rebuilds when developing](#speeding-up-frequent-rebuilds-when-developing) * [Troubleshooting Unix and macOS builds](#troubleshooting-unix-and-macos-builds) * [Windows](#windows) @@ -163,8 +163,8 @@ Binaries at are produced on: | Binary package | Platform and Toolchain | | ----------------------- | ----------------------------------------------------------------------------------------------------------- | | aix-ppc64 | AIX 7.2 TL04 on PPC64BE with GCC 10 | -| darwin-x64 | macOS 10.15, Xcode Command Line Tools 11 with -mmacosx-version-min=10.15 | -| darwin-arm64 (and .pkg) | macOS 11 (arm64), Xcode Command Line Tools 12 with -mmacosx-version-min=10.15 | +| darwin-x64 | macOS 11, Xcode 12 with -mmacosx-version-min=10.15 | +| darwin-arm64 (and .pkg) | macOS 11 (arm64), Xcode 12 with -mmacosx-version-min=10.15 | | linux-arm64 | RHEL 8 with GCC 10[^6] | | linux-armv7l | Cross-compiled on RHEL 8 x64 with [custom GCC toolchain](https://github.com/rvagg/rpi-newer-crosstools)[^7] | | linux-ppc64le | RHEL 8 with gcc-toolset-10[^6] | @@ -507,16 +507,16 @@ $ gdb /opt/node-debug/node core.node.8.1535359906 $ backtrace ``` -#### Building an ASAN build +#### Building an ASan build -[ASAN](https://github.com/google/sanitizers) can help detect various memory -related bugs. ASAN builds are currently only supported on linux. +[ASan](https://github.com/google/sanitizers) can help detect various memory +related bugs. ASan builds are currently only supported on linux. If you want to check it on Windows or macOS or you want a consistent toolchain on Linux, you can try [Docker](https://www.docker.com/products/docker-desktop) (using an image like `gengjiawen/node-build:2020-02-14`). The `--debug` is not necessary and will slow down build and testing, but it can -show clear stacktrace if ASAN hits an issue. +show clear stacktrace if ASan hits an issue. ```console $ ./configure --debug --enable-asan && make -j4 diff --git a/CHANGELOG.md b/CHANGELOG.md index ec32e0e47c4eef..5767b5f5874be4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,8 @@ release. -20.0.0
+20.1.0
+20.0.0
19.9.0
diff --git a/LICENSE b/LICENSE index 26221cb042bdd1..b2f25c4e4b8974 100644 --- a/LICENSE +++ b/LICENSE @@ -121,520 +121,525 @@ The externally maintained libraries used by Node.js are: - ICU, located at deps/icu-small, is licensed as follows: """ - UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE - - See Terms of Use - for definitions of Unicode Inc.’s Data Files and Software. - - NOTICE TO USER: Carefully read the following legal agreement. - BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S - DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), - YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE - TERMS AND CONDITIONS OF THIS AGREEMENT. - IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE - THE DATA FILES OR SOFTWARE. - - COPYRIGHT AND PERMISSION NOTICE - - Copyright © 1991-2022 Unicode, Inc. All rights reserved. - Distributed under the Terms of Use in https://www.unicode.org/copyright.html. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of the Unicode data files and any associated documentation - (the "Data Files") or Unicode software and any associated documentation - (the "Software") to deal in the Data Files or Software - without restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, and/or sell copies of - the Data Files or Software, and to permit persons to whom the Data Files - or Software are furnished to do so, provided that either - (a) this copyright and permission notice appear with all copies - of the Data Files or Software, or - (b) this copyright and permission notice appear in associated - Documentation. - - THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS - NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL 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 THE DATA FILES OR SOFTWARE. - - Except as contained in this notice, the name of a copyright holder - shall not be used in advertising or otherwise to promote the sale, - use or other dealings in these Data Files or Software without prior - written authorization of the copyright holder. - - ---------------------------------------------------------------------- - - Third-Party Software Licenses - - This section contains third-party software notices and/or additional - terms for licensed third-party software components included within ICU - libraries. - - ---------------------------------------------------------------------- - - ICU License - ICU 1.8.1 to ICU 57.1 - - COPYRIGHT AND PERMISSION NOTICE - - Copyright (c) 1995-2016 International Business Machines Corporation and others - 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, and/or sell copies of the Software, and to permit persons - to whom the Software is furnished to do so, provided that the above - copyright notice(s) and this permission notice appear in all copies of - the Software and that both the above copyright notice(s) and this - permission notice appear in supporting documentation. - - 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 - OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY - SPECIAL 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. - - Except as contained in this notice, the name of a copyright holder - shall not be used in advertising or otherwise to promote the sale, use - or other dealings in this Software without prior written authorization - of the copyright holder. - - All trademarks and registered trademarks mentioned herein are the - property of their respective owners. - - ---------------------------------------------------------------------- - - Chinese/Japanese Word Break Dictionary Data (cjdict.txt) - - # The Google Chrome software developed by Google is licensed under - # the BSD license. Other software included in this distribution is - # provided under other licenses, as set forth below. - # - # The BSD License - # http://opensource.org/licenses/bsd-license.php - # Copyright (C) 2006-2008, Google Inc. - # - # 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. - # - # - # The word list in cjdict.txt are generated by combining three word lists - # listed below with further processing for compound word breaking. The - # frequency is generated with an iterative training against Google web - # corpora. - # - # * Libtabe (Chinese) - # - https://sourceforge.net/project/?group_id=1519 - # - Its license terms and conditions are shown below. - # - # * IPADIC (Japanese) - # - http://chasen.aist-nara.ac.jp/chasen/distribution.html - # - Its license terms and conditions are shown below. - # - # ---------COPYING.libtabe ---- BEGIN-------------------- - # - # /* - # * Copyright (c) 1999 TaBE Project. - # * Copyright (c) 1999 Pai-Hsiang Hsiao. - # * 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 the TaBE Project 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 - # * REGENTS 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. - # */ - # - # /* - # * Copyright (c) 1999 Computer Systems and Communication Lab, - # * Institute of Information Science, Academia - # * Sinica. 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 the Computer Systems and Communication Lab - # * 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 - # * REGENTS 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. - # */ - # - # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, - # University of Illinois - # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 - # - # ---------------COPYING.libtabe-----END-------------------------------- - # - # - # ---------------COPYING.ipadic-----BEGIN------------------------------- - # - # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science - # and Technology. All Rights Reserved. - # - # Use, reproduction, and distribution of this software is permitted. - # Any copy of this software, whether in its original form or modified, - # must include both the above copyright notice and the following - # paragraphs. - # - # Nara Institute of Science and Technology (NAIST), - # the copyright holders, disclaims all warranties with regard to this - # software, including all implied warranties of merchantability and - # fitness, in no event shall NAIST be liable for - # any special, 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 tortuous action, arising out - # of or in connection with the use or performance of this software. - # - # A large portion of the dictionary entries - # originate from ICOT Free Software. The following conditions for ICOT - # Free Software applies to the current dictionary as well. - # - # Each User may also freely distribute the Program, whether in its - # original form or modified, to any third party or parties, PROVIDED - # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear - # on, or be attached to, the Program, which is distributed substantially - # in the same form as set out herein and that such intended - # distribution, if actually made, will neither violate or otherwise - # contravene any of the laws and regulations of the countries having - # jurisdiction over the User or the intended distribution itself. - # - # NO WARRANTY - # - # The program was produced on an experimental basis in the course of the - # research and development conducted during the project and is provided - # to users as so produced on an experimental basis. Accordingly, the - # program is provided without any warranty whatsoever, whether express, - # implied, statutory or otherwise. The term "warranty" used herein - # includes, but is not limited to, any warranty of the quality, - # performance, merchantability and fitness for a particular purpose of - # the program and the nonexistence of any infringement or violation of - # any right of any third party. - # - # Each user of the program will agree and understand, and be deemed to - # have agreed and understood, that there is no warranty whatsoever for - # the program and, accordingly, the entire risk arising from or - # otherwise connected with the program is assumed by the user. - # - # Therefore, neither ICOT, the copyright holder, or any other - # organization that participated in or was otherwise related to the - # development of the program and their respective officials, directors, - # officers and other employees shall be held liable for any and all - # damages, including, without limitation, general, special, incidental - # and consequential damages, arising out of or otherwise in connection - # with the use or inability to use the program or any product, material - # or result produced or otherwise obtained by using the program, - # regardless of whether they have been advised of, or otherwise had - # knowledge of, the possibility of such damages at any time during the - # project or thereafter. Each user will be deemed to have agreed to the - # foregoing by his or her commencement of use of the program. The term - # "use" as used herein includes, but is not limited to, the use, - # modification, copying and distribution of the program and the - # production of secondary products from the program. - # - # In the case where the program, whether in its original form or - # modified, was distributed or delivered to or received by a user from - # any person, organization or entity other than ICOT, unless it makes or - # grants independently of ICOT any specific warranty to the user in - # writing, such person, organization or entity, will also be exempted - # from and not be held liable to the user for any such damages as noted - # above as far as the program is concerned. - # - # ---------------COPYING.ipadic-----END---------------------------------- - - ---------------------------------------------------------------------- - - Lao Word Break Dictionary Data (laodict.txt) - - # Copyright (C) 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. - # - # Project: https://github.com/rober42539/lao-dictionary - # Dictionary: https://github.com/rober42539/lao-dictionary/laodict.txt - # License: https://github.com/rober42539/lao-dictionary/LICENSE.txt - # (copied below) - # - # This file is derived from the above dictionary version of Nov 22, 2020 - # ---------------------------------------------------------------------- - # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. - # 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. - # - # 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 HOLDER 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. - # -------------------------------------------------------------------------- - - ---------------------------------------------------------------------- - - Burmese Word Break Dictionary Data (burmesedict.txt) - - # Copyright (c) 2014 International Business Machines Corporation - # and others. All Rights Reserved. - # - # This list is part of a project hosted at: - # github.com/kanyawtech/myanmar-karen-word-lists - # - # -------------------------------------------------------------------------- - # Copyright (c) 2013, LeRoy Benjamin Sharon - # 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 Myanmar Karen Word Lists, 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 HOLDER 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. - # -------------------------------------------------------------------------- - - ---------------------------------------------------------------------- - - Time Zone Database - - ICU uses the public domain data and code derived from Time Zone - Database for its time zone support. The ownership of the TZ database - is explained in BCP 175: Procedure for Maintaining the Time Zone - Database section 7. - - # 7. Database Ownership - # - # The TZ database itself is not an IETF Contribution or an IETF - # document. Rather it is a pre-existing and regularly updated work - # that is in the public domain, and is intended to remain in the - # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do - # not apply to the TZ Database or contributions that individuals make - # to it. Should any claims be made and substantiated against the TZ - # Database, the organization that is providing the IANA - # Considerations defined in this RFC, under the memorandum of - # understanding with the IETF, currently ICANN, may act in accordance - # with all competent court orders. No ownership claims will be made - # by ICANN or the IETF Trust on the database or the code. Any person - # making a contribution to the database or code waives all rights to - # future claims in that contribution or in the TZ Database. - - ---------------------------------------------------------------------- - - Google double-conversion - - Copyright 2006-2011, 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. - - ---------------------------------------------------------------------- - - File: aclocal.m4 (only for ICU4C) - Section: pkg.m4 - Macros to locate and utilise pkg-config. - - Copyright © 2004 Scott James Remnant . - Copyright © 2012-2015 Dan Nicholson - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. - - As a special exception to the GNU General Public License, if you - distribute this file as part of a program that contains a - configuration script generated by Autoconf, you may include it under - the same distribution terms that you use for the rest of that - program. - - (The condition for the exception is fulfilled because - ICU4C includes a configuration script generated by Autoconf, - namely the `configure` script.) - - ---------------------------------------------------------------------- - - File: config.guess (only for ICU4C) - - This file is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - - As a special exception to the GNU General Public License, if you - distribute this file as part of a program that contains a - configuration script generated by Autoconf, you may include it under - the same distribution terms that you use for the rest of that - program. This Exception is an additional permission under section 7 - of the GNU General Public License, version 3 ("GPLv3"). - - (The condition for the exception is fulfilled because - ICU4C includes a configuration script generated by Autoconf, - namely the `configure` script.) - - ---------------------------------------------------------------------- - - File: install-sh (only for ICU4C) - - Copyright 1991 by the Massachusetts Institute of Technology - - Permission to use, copy, modify, distribute, and sell this software and its - documentation for any purpose is hereby granted without fee, provided that - the above copyright notice appear in all copies and that both that - copyright notice and this permission notice appear in supporting - documentation, and that the name of M.I.T. not be used in advertising or - publicity pertaining to distribution of the software without specific, - written prior permission. M.I.T. makes no representations about the - suitability of this software for any purpose. It is provided "as is" - without express or implied warranty. + UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + + See Terms of Use + for definitions of Unicode Inc.’s Data Files and Software. + + NOTICE TO USER: Carefully read the following legal agreement. + BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S + DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), + YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE + TERMS AND CONDITIONS OF THIS AGREEMENT. + IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE + THE DATA FILES OR SOFTWARE. + + COPYRIGHT AND PERMISSION NOTICE + + Copyright © 1991-2023 Unicode, Inc. All rights reserved. + Distributed under the Terms of Use in https://www.unicode.org/copyright.html. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of the Unicode data files and any associated documentation + (the "Data Files") or Unicode software and any associated documentation + (the "Software") to deal in the Data Files or Software + without restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, and/or sell copies of + the Data Files or Software, and to permit persons to whom the Data Files + or Software are furnished to do so, provided that either + (a) this copyright and permission notice appear with all copies + of the Data Files or Software, or + (b) this copyright and permission notice appear in associated + Documentation. + + THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS + NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL 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 THE DATA FILES OR SOFTWARE. + + Except as contained in this notice, the name of a copyright holder + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in these Data Files or Software without prior + written authorization of the copyright holder. + + ---------------------------------------------------------------------- + + Third-Party Software Licenses + + This section contains third-party software notices and/or additional + terms for licensed third-party software components included within ICU + libraries. + + ---------------------------------------------------------------------- + + ICU License - ICU 1.8.1 to ICU 57.1 + + COPYRIGHT AND PERMISSION NOTICE + + Copyright (c) 1995-2016 International Business Machines Corporation and others + 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, and/or sell copies of the Software, and to permit persons + to whom the Software is furnished to do so, provided that the above + copyright notice(s) and this permission notice appear in all copies of + the Software and that both the above copyright notice(s) and this + permission notice appear in supporting documentation. + + 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 + OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY + SPECIAL 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. + + Except as contained in this notice, the name of a copyright holder + shall not be used in advertising or otherwise to promote the sale, use + or other dealings in this Software without prior written authorization + of the copyright holder. + + All trademarks and registered trademarks mentioned herein are the + property of their respective owners. + + ---------------------------------------------------------------------- + + Chinese/Japanese Word Break Dictionary Data (cjdict.txt) + + # The Google Chrome software developed by Google is licensed under + # the BSD license. Other software included in this distribution is + # provided under other licenses, as set forth below. + # + # The BSD License + # http://opensource.org/licenses/bsd-license.php + # Copyright (C) 2006-2008, Google Inc. + # + # 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. + # + # + # The word list in cjdict.txt are generated by combining three word lists + # listed below with further processing for compound word breaking. The + # frequency is generated with an iterative training against Google web + # corpora. + # + # * Libtabe (Chinese) + # - https://sourceforge.net/project/?group_id=1519 + # - Its license terms and conditions are shown below. + # + # * IPADIC (Japanese) + # - http://chasen.aist-nara.ac.jp/chasen/distribution.html + # - Its license terms and conditions are shown below. + # + # ---------COPYING.libtabe ---- BEGIN-------------------- + # + # /* + # * Copyright (c) 1999 TaBE Project. + # * Copyright (c) 1999 Pai-Hsiang Hsiao. + # * 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 the TaBE Project 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 + # * REGENTS 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. + # */ + # + # /* + # * Copyright (c) 1999 Computer Systems and Communication Lab, + # * Institute of Information Science, Academia + # * Sinica. 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 the Computer Systems and Communication Lab + # * 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 + # * REGENTS 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. + # */ + # + # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, + # University of Illinois + # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 + # + # ---------------COPYING.libtabe-----END-------------------------------- + # + # + # ---------------COPYING.ipadic-----BEGIN------------------------------- + # + # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science + # and Technology. All Rights Reserved. + # + # Use, reproduction, and distribution of this software is permitted. + # Any copy of this software, whether in its original form or modified, + # must include both the above copyright notice and the following + # paragraphs. + # + # Nara Institute of Science and Technology (NAIST), + # the copyright holders, disclaims all warranties with regard to this + # software, including all implied warranties of merchantability and + # fitness, in no event shall NAIST be liable for + # any special, 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 tortuous action, arising out + # of or in connection with the use or performance of this software. + # + # A large portion of the dictionary entries + # originate from ICOT Free Software. The following conditions for ICOT + # Free Software applies to the current dictionary as well. + # + # Each User may also freely distribute the Program, whether in its + # original form or modified, to any third party or parties, PROVIDED + # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear + # on, or be attached to, the Program, which is distributed substantially + # in the same form as set out herein and that such intended + # distribution, if actually made, will neither violate or otherwise + # contravene any of the laws and regulations of the countries having + # jurisdiction over the User or the intended distribution itself. + # + # NO WARRANTY + # + # The program was produced on an experimental basis in the course of the + # research and development conducted during the project and is provided + # to users as so produced on an experimental basis. Accordingly, the + # program is provided without any warranty whatsoever, whether express, + # implied, statutory or otherwise. The term "warranty" used herein + # includes, but is not limited to, any warranty of the quality, + # performance, merchantability and fitness for a particular purpose of + # the program and the nonexistence of any infringement or violation of + # any right of any third party. + # + # Each user of the program will agree and understand, and be deemed to + # have agreed and understood, that there is no warranty whatsoever for + # the program and, accordingly, the entire risk arising from or + # otherwise connected with the program is assumed by the user. + # + # Therefore, neither ICOT, the copyright holder, or any other + # organization that participated in or was otherwise related to the + # development of the program and their respective officials, directors, + # officers and other employees shall be held liable for any and all + # damages, including, without limitation, general, special, incidental + # and consequential damages, arising out of or otherwise in connection + # with the use or inability to use the program or any product, material + # or result produced or otherwise obtained by using the program, + # regardless of whether they have been advised of, or otherwise had + # knowledge of, the possibility of such damages at any time during the + # project or thereafter. Each user will be deemed to have agreed to the + # foregoing by his or her commencement of use of the program. The term + # "use" as used herein includes, but is not limited to, the use, + # modification, copying and distribution of the program and the + # production of secondary products from the program. + # + # In the case where the program, whether in its original form or + # modified, was distributed or delivered to or received by a user from + # any person, organization or entity other than ICOT, unless it makes or + # grants independently of ICOT any specific warranty to the user in + # writing, such person, organization or entity, will also be exempted + # from and not be held liable to the user for any such damages as noted + # above as far as the program is concerned. + # + # ---------------COPYING.ipadic-----END---------------------------------- + + ---------------------------------------------------------------------- + + Lao Word Break Dictionary Data (laodict.txt) + + # Copyright (C) 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. + # + # Project: https://github.com/rober42539/lao-dictionary + # Dictionary: https://github.com/rober42539/lao-dictionary/laodict.txt + # License: https://github.com/rober42539/lao-dictionary/LICENSE.txt + # (copied below) + # + # This file is derived from the above dictionary version of Nov 22, 2020 + # ---------------------------------------------------------------------- + # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. + # 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. + # + # 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 HOLDER 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. + # -------------------------------------------------------------------------- + + ---------------------------------------------------------------------- + + Burmese Word Break Dictionary Data (burmesedict.txt) + + # Copyright (c) 2014 International Business Machines Corporation + # and others. All Rights Reserved. + # + # This list is part of a project hosted at: + # github.com/kanyawtech/myanmar-karen-word-lists + # + # -------------------------------------------------------------------------- + # Copyright (c) 2013, LeRoy Benjamin Sharon + # 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 Myanmar Karen Word Lists, 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 HOLDER 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. + # -------------------------------------------------------------------------- + + ---------------------------------------------------------------------- + + Time Zone Database + + ICU uses the public domain data and code derived from Time Zone + Database for its time zone support. The ownership of the TZ database + is explained in BCP 175: Procedure for Maintaining the Time Zone + Database section 7. + + # 7. Database Ownership + # + # The TZ database itself is not an IETF Contribution or an IETF + # document. Rather it is a pre-existing and regularly updated work + # that is in the public domain, and is intended to remain in the + # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do + # not apply to the TZ Database or contributions that individuals make + # to it. Should any claims be made and substantiated against the TZ + # Database, the organization that is providing the IANA + # Considerations defined in this RFC, under the memorandum of + # understanding with the IETF, currently ICANN, may act in accordance + # with all competent court orders. No ownership claims will be made + # by ICANN or the IETF Trust on the database or the code. Any person + # making a contribution to the database or code waives all rights to + # future claims in that contribution or in the TZ Database. + + ---------------------------------------------------------------------- + + Google double-conversion + + Copyright 2006-2011, 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. + + ---------------------------------------------------------------------- + + File: aclocal.m4 (only for ICU4C) + Section: pkg.m4 - Macros to locate and utilise pkg-config. + + + Copyright © 2004 Scott James Remnant . + Copyright © 2012-2015 Dan Nicholson + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + + As a special exception to the GNU General Public License, if you + distribute this file as part of a program that contains a + configuration script generated by Autoconf, you may include it under + the same distribution terms that you use for the rest of that + program. + + + (The condition for the exception is fulfilled because + ICU4C includes a configuration script generated by Autoconf, + namely the `configure` script.) + + ---------------------------------------------------------------------- + + File: config.guess (only for ICU4C) + + + This file is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + + As a special exception to the GNU General Public License, if you + distribute this file as part of a program that contains a + configuration script generated by Autoconf, you may include it under + the same distribution terms that you use for the rest of that + program. This Exception is an additional permission under section 7 + of the GNU General Public License, version 3 ("GPLv3"). + + + (The condition for the exception is fulfilled because + ICU4C includes a configuration script generated by Autoconf, + namely the `configure` script.) + + ---------------------------------------------------------------------- + + File: install-sh (only for ICU4C) + + + Copyright 1991 by the Massachusetts Institute of Technology + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation, and that the name of M.I.T. not be used in advertising or + publicity pertaining to distribution of the software without specific, + written prior permission. M.I.T. makes no representations about the + suitability of this software for any purpose. It is provided "as is" + without express or implied warranty. """ - libuv, located at deps/uv, is licensed as follows: @@ -1360,6 +1365,25 @@ The externally maintained libraries used by Node.js are: CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +- minimatch, located at deps/minimatch, is licensed as follows: + """ + The ISC License + + Copyright (c) 2011-2023 Isaac Z. Schlueter and Contributors + + 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. + """ + - npm, located at deps/npm, is licensed as follows: """ The npm application diff --git a/Makefile b/Makefile index 831f9613233881..25e54f109ef58b 100644 --- a/Makefile +++ b/Makefile @@ -1489,22 +1489,22 @@ cpplint: lint-cpp $(warning Please use lint-cpp instead of cpplint) .PHONY: lint-py-build -# python -m pip install flake8 +# python -m pip install ruff # Try with '--system' if it fails without; the system may have set '--user' lint-py-build: - $(info Pip installing flake8 linter on $(shell $(PYTHON) --version)...) - $(PYTHON) -m pip install --upgrade -t tools/pip/site-packages flake8 || \ - $(PYTHON) -m pip install --upgrade --system -t tools/pip/site-packages flake8 + $(info Pip installing ruff on $(shell $(PYTHON) --version)...) + $(PYTHON) -m pip install --upgrade --target tools/pip/site-packages ruff || \ + $(PYTHON) -m pip install --upgrade --system --target tools/pip/site-packages ruff .PHONY: lint-py -ifneq ("","$(wildcard tools/pip/site-packages/flake8)") -# Lints the Python code with flake8. -# Flag the build if there are Python syntax errors or undefined names +ifneq ("","$(wildcard tools/pip/site-packages/ruff)") +# Lint the Python code with ruff. lint-py: - PYTHONPATH=tools/pip $(PYTHON) -m flake8 --count --show-source --statistics . + tools/pip/site-packages/bin/ruff --version + tools/pip/site-packages/bin/ruff . else lint-py: - $(warning Python linting with flake8 is not available) + $(warning Python linting with ruff is not available) $(warning Run 'make lint-py-build') endif diff --git a/README.md b/README.md index e30f049fb45875..9d4f7b69577fd8 100644 --- a/README.md +++ b/README.md @@ -166,8 +166,6 @@ For information about the governance of the Node.js project, see **Antoine du Hamel** <> (he/him) * [apapirovski](https://github.com/apapirovski) - **Anatoli Papirovski** <> (he/him) -* [BethGriggs](https://github.com/BethGriggs) - - **Beth Griggs** <> (she/her) * [BridgeAR](https://github.com/BridgeAR) - **Ruben Bridgewater** <> (he/him) * [cjihrig](https://github.com/cjihrig) - @@ -207,8 +205,8 @@ For information about the governance of the Node.js project, see #### TSC regular members -* [addaleax](https://github.com/addaleax) - - **Anna Henningsen** <> (she/her) +* [BethGriggs](https://github.com/BethGriggs) - + **Beth Griggs** <> (she/her) * [bnoordhuis](https://github.com/bnoordhuis) - **Ben Noordhuis** <> * [ChALkeR](https://github.com/ChALkeR) - @@ -236,6 +234,8 @@ For information about the governance of the Node.js project, see #### TSC emeriti members +* [addaleax](https://github.com/addaleax) - + **Anna Henningsen** <> (she/her) * [chrisdickinson](https://github.com/chrisdickinson) - **Chris Dickinson** <> * [evanlucas](https://github.com/evanlucas) - @@ -371,6 +371,8 @@ For information about the governance of the Node.js project, see **Juan José Arboleda** <> (he/him) * [JungMinu](https://github.com/JungMinu) - **Minwoo Jung** <> (he/him) +* [KhafraDev](https://github.com/KhafraDev) - + **Matthew Aitken** <> (he/him) * [kuriyosh](https://github.com/kuriyosh) - **Yoshiki Kurihara** <> (he/him) * [legendecas](https://github.com/legendecas) - @@ -705,8 +707,6 @@ maintaining the Node.js project. **Xuguang Mei** <> (he/him) * [Mesteery](https://github.com/Mesteery) - **Mestery** <> (he/him) -* [MoLow](https://github.com/MoLow) - - **Moshe Atlow** <> (he/him) * [PoojaDurgad](https://github.com/PoojaDurgad) - **Pooja Durgad** <> * [RaisinTen](https://github.com/RaisinTen) - diff --git a/SECURITY.md b/SECURITY.md index 02c9f83aa32c1a..e815fbbd538217 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -126,7 +126,7 @@ lead to a loss of confidentiality, integrity, or availability. See . Any unexpected behavior from the data manipulation from Node.js Internal -functions may be considered a vulnerability if they are expoitable via +functions may be considered a vulnerability if they are exploitable via untrusted resources. In addition to addressing vulnerabilities based on the above, the project works @@ -144,7 +144,7 @@ the community they pose. #### Improper Certificate Validation (CWE-295) * Node.js provides APIs to validate handling of Subject Alternative Names (SANs) - in certficates used to connect to a TLS/SSL endpoint. If certificates can be + in certificates used to connect to a TLS/SSL endpoint. If certificates can be crafted which result in incorrect validation by the Node.js APIs that is considered a vulnerability. diff --git a/benchmark/esm/esm-loader-defaultResolve.js b/benchmark/esm/esm-loader-defaultResolve.js new file mode 100644 index 00000000000000..85409a122637d5 --- /dev/null +++ b/benchmark/esm/esm-loader-defaultResolve.js @@ -0,0 +1,51 @@ +// Tests the impact on eager operations required for policies affecting +// general startup, does not test lazy operations +'use strict'; +const fs = require('node:fs'); +const path = require('node:path'); +const common = require('../common.js'); + +const tmpdir = require('../../test/common/tmpdir.js'); +const { pathToFileURL } = require('node:url'); + +const benchmarkDirectory = + path.resolve(tmpdir.path, 'benchmark-import-meta-resolve'); + +const parentURL = pathToFileURL(path.join(benchmarkDirectory, 'entry-point.js')); + +const configs = { + n: [1e3], + specifier: [ + './relative-existing.js', + './relative-nonexistent.js', + 'unprefixed-existing', + 'unprefixed-nonexistent', + 'node:prefixed-nonexistent', + 'node:os', + ], +}; + +const options = { + flags: ['--expose-internals'], +}; + +const bench = common.createBenchmark(main, configs, options); + +function main(conf) { + const { defaultResolve } = require('internal/modules/esm/resolve'); + tmpdir.refresh(); + + fs.mkdirSync(path.join(benchmarkDirectory, 'node_modules', 'unprefixed-existing'), { recursive: true }); + fs.writeFileSync(path.join(benchmarkDirectory, 'node_modules', 'unprefixed-existing', 'index.js'), '\n'); + fs.writeFileSync(path.join(benchmarkDirectory, 'relative-existing.js'), '\n'); + + bench.start(); + + for (let i = 0; i < conf.n; i++) { + try { + defaultResolve(conf.specifier, { parentURL }); + } catch { /* empty */ } + } + + bench.end(conf.n); +} diff --git a/benchmark/events/eventtarget-creation.js b/benchmark/events/eventtarget-creation.js new file mode 100644 index 00000000000000..faf04978d6526b --- /dev/null +++ b/benchmark/events/eventtarget-creation.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common.js'); +const assert = require('node:assert'); + +const bench = common.createBenchmark(main, { + n: [1e6], +}, { flags: ['--expose-internals'] }); + +function main({ n }) { + const { EventTarget } = require('internal/event_target'); + + let target; + + bench.start(); + for (let i = 0; i < n; i++) { + target = new EventTarget(); + } + bench.end(n); + + // Avoid V8 deadcode (elimination) + assert.ok(target); +} diff --git a/benchmark/url/legacy-url-get-prop.js b/benchmark/url/legacy-url-get-prop.js new file mode 100644 index 00000000000000..09be863e963a25 --- /dev/null +++ b/benchmark/url/legacy-url-get-prop.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common.js'); +const url = require('url'); +const assert = require('assert'); + +const bench = common.createBenchmark(main, { + type: common.urlDataTypes, + e: [1], +}); + +function main({ type, e }) { + const data = common.bakeUrlData(type, e, false, false).map((i) => url.parse(i)); + const obj = url.parse(data[0]); + const noDead = { + protocol: obj.protocol, + auth: obj.auth, + host: obj.host, + hostname: obj.hostname, + port: obj.port, + pathname: obj.pathname, + search: obj.search, + hash: obj.hash, + }; + const len = data.length; + // It's necessary to assign the values to an object + // to avoid loop invariant code motion. + bench.start(); + for (let i = 0; i < len; i++) { + const obj = data[i]; + noDead.protocol = obj.protocol; + noDead.auth = obj.auth; + noDead.host = obj.host; + noDead.hostname = obj.hostname; + noDead.port = obj.port; + noDead.pathname = obj.pathname; + noDead.search = obj.search; + noDead.hash = obj.hash; + } + bench.end(len); + assert.ok(noDead); +} diff --git a/benchmark/url/legacy-url-parse.js b/benchmark/url/legacy-url-parse.js new file mode 100644 index 00000000000000..ac893ebf8ad12e --- /dev/null +++ b/benchmark/url/legacy-url-parse.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common.js'); +const url = require('url'); +const assert = require('assert'); + +const bench = common.createBenchmark(main, { + type: common.urlDataTypes, + e: [1], +}); + +function main({ e, type }) { + const data = common.bakeUrlData(type, e, false, false); + let result = url.parse(data[0]); // Avoid dead code elimination + + bench.start(); + for (let i = 0; i < data.length; ++i) { + result = url.parse(data[i]); + } + bench.end(data.length); + + assert.ok(result); +} diff --git a/benchmark/url/legacy-vs-whatwg-url-get-prop.js b/benchmark/url/legacy-vs-whatwg-url-get-prop.js deleted file mode 100644 index df888f13b98775..00000000000000 --- a/benchmark/url/legacy-vs-whatwg-url-get-prop.js +++ /dev/null @@ -1,90 +0,0 @@ -'use strict'; -const common = require('../common.js'); -const url = require('url'); -const URL = url.URL; -const assert = require('assert'); - -const bench = common.createBenchmark(main, { - type: common.urlDataTypes, - method: ['legacy', 'whatwg'], - e: [1], -}); - -function useLegacy(data) { - const obj = url.parse(data[0]); - const noDead = { - protocol: obj.protocol, - auth: obj.auth, - host: obj.host, - hostname: obj.hostname, - port: obj.port, - pathname: obj.pathname, - search: obj.search, - hash: obj.hash, - }; - const len = data.length; - // It's necessary to assign the values to an object - // to avoid loop invariant code motion. - bench.start(); - for (let i = 0; i < len; i++) { - const obj = data[i]; - noDead.protocol = obj.protocol; - noDead.auth = obj.auth; - noDead.host = obj.host; - noDead.hostname = obj.hostname; - noDead.port = obj.port; - noDead.pathname = obj.pathname; - noDead.search = obj.search; - noDead.hash = obj.hash; - } - bench.end(len); - return noDead; -} - -function useWHATWG(data) { - const obj = new URL(data[0]); - const noDead = { - protocol: obj.protocol, - auth: `${obj.username}:${obj.password}`, - host: obj.host, - hostname: obj.hostname, - port: obj.port, - pathname: obj.pathname, - search: obj.search, - hash: obj.hash, - }; - const len = data.length; - bench.start(); - for (let i = 0; i < len; i++) { - const obj = data[i]; - noDead.protocol = obj.protocol; - noDead.auth = `${obj.username}:${obj.password}`; - noDead.host = obj.host; - noDead.hostname = obj.hostname; - noDead.port = obj.port; - noDead.pathname = obj.pathname; - noDead.search = obj.search; - noDead.hash = obj.hash; - } - bench.end(len); - return noDead; -} - -function main({ type, method, e }) { - let data; - let noDead; // Avoid dead code elimination. - switch (method) { - case 'legacy': - data = common.bakeUrlData(type, e, false, false); - noDead = useLegacy(data.map((i) => url.parse(i))); - break; - case 'whatwg': - data = common.bakeUrlData(type, e, false, true); - noDead = useWHATWG(data); - break; - default: - throw new Error(`Unknown method "${method}"`); - } - - assert.ok(noDead); -} diff --git a/benchmark/url/whatwgurl-canParse.js b/benchmark/url/whatwg-url-canParse.js similarity index 100% rename from benchmark/url/whatwgurl-canParse.js rename to benchmark/url/whatwg-url-canParse.js diff --git a/benchmark/url/whatwg-url-get-prop.js b/benchmark/url/whatwg-url-get-prop.js new file mode 100644 index 00000000000000..3a88dd1da6fae6 --- /dev/null +++ b/benchmark/url/whatwg-url-get-prop.js @@ -0,0 +1,40 @@ +'use strict'; +const common = require('../common.js'); +const url = require('url'); +const URL = url.URL; +const assert = require('assert'); + +const bench = common.createBenchmark(main, { + type: common.urlDataTypes, + e: [1], +}); + +function main({ type, e }) { + const data = common.bakeUrlData(type, e, false, true); + const obj = new URL(data[0]); + const noDead = { + protocol: obj.protocol, + auth: `${obj.username}:${obj.password}`, + host: obj.host, + hostname: obj.hostname, + port: obj.port, + pathname: obj.pathname, + search: obj.search, + hash: obj.hash, + }; + const len = data.length; + bench.start(); + for (let i = 0; i < len; i++) { + const obj = data[i]; + noDead.protocol = obj.protocol; + noDead.auth = `${obj.username}:${obj.password}`; + noDead.host = obj.host; + noDead.hostname = obj.hostname; + noDead.port = obj.port; + noDead.pathname = obj.pathname; + noDead.search = obj.search; + noDead.hash = obj.hash; + } + bench.end(len); + assert.ok(noDead); +} diff --git a/benchmark/url/legacy-vs-whatwg-url-parse.js b/benchmark/url/whatwg-url-parse.js similarity index 53% rename from benchmark/url/legacy-vs-whatwg-url-parse.js rename to benchmark/url/whatwg-url-parse.js index a54d81e15fd6ed..0c10b12587a81c 100644 --- a/benchmark/url/legacy-vs-whatwg-url-parse.js +++ b/benchmark/url/whatwg-url-parse.js @@ -8,20 +8,8 @@ const bench = common.createBenchmark(main, { withBase: ['true', 'false'], type: common.urlDataTypes, e: [1], - method: ['legacy', 'whatwg'], }); -function useLegacy(data) { - const len = data.length; - let result = url.parse(data[0]); // Avoid dead code elimination - bench.start(); - for (let i = 0; i < len; ++i) { - result = url.parse(data[i]); - } - bench.end(len); - return result; -} - function useWHATWGWithBase(data) { const len = data.length; let result = new URL(data[0][0], data[0][1]); // Avoid dead code elimination @@ -45,22 +33,10 @@ function useWHATWGWithoutBase(data) { return result; } -function main({ e, method, type, withBase }) { +function main({ e, type, withBase }) { withBase = withBase === 'true'; - let noDead; // Avoid dead code elimination. - let data; - switch (method) { - case 'legacy': - data = common.bakeUrlData(type, e, false, false); - noDead = useLegacy(data); - break; - case 'whatwg': - data = common.bakeUrlData(type, e, withBase, false); - noDead = withBase ? useWHATWGWithBase(data) : useWHATWGWithoutBase(data); - break; - default: - throw new Error(`Unknown method ${method}`); - } + const data = common.bakeUrlData(type, e, withBase, false); + const noDead = withBase ? useWHATWGWithBase(data) : useWHATWGWithoutBase(data); assert.ok(noDead); } diff --git a/benchmark/url/whatwgurl-to-and-from-path.js b/benchmark/url/whatwg-url-to-and-from-path.js similarity index 100% rename from benchmark/url/whatwgurl-to-and-from-path.js rename to benchmark/url/whatwg-url-to-and-from-path.js diff --git a/common.gypi b/common.gypi index 0dcf428002c4c7..2104bba30e2ebb 100644 --- a/common.gypi +++ b/common.gypi @@ -36,7 +36,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.3', + 'v8_embedder_string': '-node.6', ##### V8 defaults for Node.js ##### diff --git a/configure.py b/configure.py index a0deed59c1f3df..83a6faead7613d 100755 --- a/configure.py +++ b/configure.py @@ -12,11 +12,12 @@ import shutil import bz2 import io +from pathlib import Path from distutils.version import StrictVersion # If not run from node/, cd to node/. -os.chdir(os.path.dirname(__file__) or '.') +os.chdir(Path(__file__).parent) original_argv = sys.argv[1:] @@ -25,11 +26,13 @@ CC = os.environ.get('CC', 'cc' if sys.platform == 'darwin' else 'gcc') CXX = os.environ.get('CXX', 'c++' if sys.platform == 'darwin' else 'g++') -sys.path.insert(0, os.path.join('tools', 'gyp', 'pylib')) +tools_path = Path('tools') + +sys.path.insert(0, str(tools_path / 'gyp' / 'pylib')) from gyp.common import GetFlavor # imports in tools/configure.d -sys.path.insert(0, os.path.join('tools', 'configure.d')) +sys.path.insert(0, str(tools_path / 'configure.d')) import nodedownload # imports in tools/ @@ -53,8 +56,7 @@ valid_mips_fpu = ('fp32', 'fp64', 'fpxx') valid_mips_float_abi = ('soft', 'hard') valid_intl_modes = ('none', 'small-icu', 'full-icu', 'system-icu') -with open ('tools/icu/icu_versions.json') as f: - icu_versions = json.load(f) +icu_versions = json.loads((tools_path / 'icu' / 'icu_versions.json').read_text(encoding='utf-8')) shareable_builtins = {'cjs_module_lexer/lexer': 'deps/cjs-module-lexer/lexer.js', 'cjs_module_lexer/dist/lexer': 'deps/cjs-module-lexer/dist/lexer.js', @@ -108,7 +110,7 @@ action='store', dest='dest_cpu', choices=valid_arch, - help='CPU architecture to build for ({0})'.format(', '.join(valid_arch))) + help=f"CPU architecture to build for ({', '.join(valid_arch)})") parser.add_argument('--cross-compiling', action='store_true', @@ -125,7 +127,7 @@ action='store', dest='dest_os', choices=valid_os, - help='operating system to build for ({0})'.format(', '.join(valid_os))) + help=f"operating system to build for ({', '.join(valid_os)})") parser.add_argument('--error-on-warn', action='store_true', @@ -510,39 +512,34 @@ action='store', dest='arm_float_abi', choices=valid_arm_float_abi, - help='specifies which floating-point ABI to use ({0}).'.format( - ', '.join(valid_arm_float_abi))) + help=f"specifies which floating-point ABI to use ({', '.join(valid_arm_float_abi)}).") parser.add_argument('--with-arm-fpu', action='store', dest='arm_fpu', choices=valid_arm_fpu, - help='ARM FPU mode ({0}) [default: %(default)s]'.format( - ', '.join(valid_arm_fpu))) + help=f"ARM FPU mode ({', '.join(valid_arm_fpu)}) [default: %(default)s]") parser.add_argument('--with-mips-arch-variant', action='store', dest='mips_arch_variant', default='r2', choices=valid_mips_arch, - help='MIPS arch variant ({0}) [default: %(default)s]'.format( - ', '.join(valid_mips_arch))) + help=f"MIPS arch variant ({', '.join(valid_mips_arch)}) [default: %(default)s]") parser.add_argument('--with-mips-fpu-mode', action='store', dest='mips_fpu_mode', default='fp32', choices=valid_mips_fpu, - help='MIPS FPU mode ({0}) [default: %(default)s]'.format( - ', '.join(valid_mips_fpu))) + help=f"MIPS FPU mode ({', '.join(valid_mips_fpu)}) [default: %(default)s]") parser.add_argument('--with-mips-float-abi', action='store', dest='mips_float_abi', default='hard', choices=valid_mips_float_abi, - help='MIPS floating-point ABI ({0}) [default: %(default)s]'.format( - ', '.join(valid_mips_float_abi))) + help=f"MIPS floating-point ABI ({', '.join(valid_mips_float_abi)}) [default: %(default)s]") parser.add_argument('--use-largepages', action='store_true', @@ -569,8 +566,7 @@ dest='with_intl', default='full-icu', choices=valid_intl_modes, - help='Intl mode (valid choices: {0}) [default: %(default)s]'.format( - ', '.join(valid_intl_modes))) + help=f"Intl mode (valid choices: {', '.join(valid_intl_modes)}) [default: %(default)s]") intl_optgroup.add_argument('--without-intl', action='store_const', @@ -597,7 +593,7 @@ dest='with_icu_source', help='Intl mode: optional local path to icu/ dir, or path/URL of ' 'the icu4c source archive. ' - 'v%d.x or later recommended.' % icu_versions['minimum_icu']) + f"v{icu_versions['minimum_icu']}.x or later recommended.") intl_optgroup.add_argument('--with-icu-default-data-dir', action='store', @@ -845,7 +841,7 @@ (options, args) = parser.parse_known_args() # Expand ~ in the install prefix now, it gets written to multiple files. -options.prefix = os.path.expanduser(options.prefix or '') +options.prefix = str(Path(options.prefix or '').expanduser()) # set up auto-download list auto_downloads = nodedownload.parse(options.download_list) @@ -853,25 +849,25 @@ def error(msg): prefix = '\033[1m\033[31mERROR\033[0m' if os.isatty(1) else 'ERROR' - print('%s: %s' % (prefix, msg)) + print(f'{prefix}: {msg}') sys.exit(1) def warn(msg): warn.warned = True prefix = '\033[1m\033[93mWARNING\033[0m' if os.isatty(1) else 'WARNING' - print('%s: %s' % (prefix, msg)) + print(f'{prefix}: {msg}') # track if warnings occurred warn.warned = False def info(msg): prefix = '\033[1m\033[32mINFO\033[0m' if os.isatty(1) else 'INFO' - print('%s: %s' % (prefix, msg)) + print(f'{prefix}: {msg}') def print_verbose(x): if not options.verbose: return - if type(x) is str: + if isinstance(x, str): print(x) else: pprint.pprint(x, indent=2) @@ -904,9 +900,11 @@ def pkg_config(pkg): try: proc = subprocess.Popen(shlex.split(pkg_config) + args, stdout=subprocess.PIPE) - val = to_utf8(proc.communicate()[0]).strip() + with proc: + val = to_utf8(proc.communicate()[0]).strip() except OSError as e: - if e.errno != errno.ENOENT: raise e # Unexpected error. + if e.errno != errno.ENOENT: + raise e # Unexpected error. return (None, None, None, None) # No pkg-config/pkgconf installed. retval.append(val) args = ['--silence-errors'] @@ -920,13 +918,14 @@ def try_check_compiler(cc, lang): except OSError: return (False, False, '', '') - proc.stdin.write(b'__clang__ __GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__ ' - b'__clang_major__ __clang_minor__ __clang_patchlevel__') + with proc: + proc.stdin.write(b'__clang__ __GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__ ' + b'__clang_major__ __clang_minor__ __clang_patchlevel__') - if sys.platform == 'zos': - values = (to_utf8(proc.communicate()[0]).split('\n')[-2].split() + ['0'] * 7)[0:7] - else: - values = (to_utf8(proc.communicate()[0]).split() + ['0'] * 7)[0:7] + if sys.platform == 'zos': + values = (to_utf8(proc.communicate()[0]).split('\n')[-2].split() + ['0'] * 7)[0:7] + else: + values = (to_utf8(proc.communicate()[0]).split() + ['0'] * 7)[0:7] is_clang = values[0] == '1' gcc_version = tuple(map(int, values[1:1+3])) @@ -952,12 +951,10 @@ def get_version_helper(cc, regexp): consider adjusting the CC environment variable if you installed it in a non-standard prefix.''') - match = re.search(regexp, to_utf8(proc.communicate()[1])) + with proc: + match = re.search(regexp, to_utf8(proc.communicate()[1])) - if match: - return match.group(2) - else: - return '0.0' + return match.group(2) if match else '0.0' def get_nasm_version(asm): try: @@ -970,13 +967,11 @@ def get_nasm_version(asm): and refer BUILDING.md.''') return '0.0' - match = re.match(r"NASM version ([2-9]\.[0-9][0-9]+)", - to_utf8(proc.communicate()[0])) + with proc: + match = re.match(r"NASM version ([2-9]\.[0-9][0-9]+)", + to_utf8(proc.communicate()[0])) - if match: - return match.group(1) - else: - return '0.0' + return match.group(1) if match else '0.0' def get_llvm_version(cc): return get_version_helper( @@ -1002,14 +997,16 @@ def get_gas_version(cc): consider adjusting the CC environment variable if you installed it in a non-standard prefix.''') - gas_ret = to_utf8(proc.communicate()[1]) + with proc: + gas_ret = to_utf8(proc.communicate()[1]) + match = re.match(r"GNU assembler version ([2-9]\.[0-9]+)", gas_ret) if match: return match.group(1) - else: - warn('Could not recognize `gas`: ' + gas_ret) - return '0.0' + + warn(f'Could not recognize `gas`: {gas_ret}') + return '0.0' # Note: Apple clang self-reports as clang 4.2.0 and gcc 4.2.1. It passes # the version check more by accident than anything else but a more rigorous @@ -1027,26 +1024,22 @@ def check_compiler(o): ok, is_clang, clang_version, gcc_version = try_check_compiler(CXX, 'c++') version_str = ".".join(map(str, clang_version if is_clang else gcc_version)) - print_verbose('Detected %sC++ compiler (CXX=%s) version: %s' % - ('clang ' if is_clang else '', CXX, version_str)) + print_verbose(f"Detected {'clang ' if is_clang else ''}C++ compiler (CXX={CXX}) version: {version_str}") if not ok: - warn('failed to autodetect C++ compiler version (CXX=%s)' % CXX) + warn(f'failed to autodetect C++ compiler version (CXX={CXX})') elif clang_version < (8, 0, 0) if is_clang else gcc_version < (10, 1, 0): - warn('C++ compiler (CXX=%s, %s) too old, need g++ 10.1.0 or clang++ 8.0.0' % - (CXX, version_str)) + warn(f'C++ compiler (CXX={CXX}, {version_str}) too old, need g++ 10.1.0 or clang++ 8.0.0') ok, is_clang, clang_version, gcc_version = try_check_compiler(CC, 'c') version_str = ".".join(map(str, clang_version if is_clang else gcc_version)) - print_verbose('Detected %sC compiler (CC=%s) version: %s' % - ('clang ' if is_clang else '', CC, version_str)) + print_verbose(f"Detected {'clang ' if is_clang else ''}C compiler (CC={CC}) version: {version_str}") if not ok: - warn('failed to autodetect C compiler version (CC=%s)' % CC) + warn(f'failed to autodetect C compiler version (CC={CC})') elif not is_clang and gcc_version < (4, 2, 0): # clang 3.2 is a little white lie because any clang version will probably # do for the C bits. However, we might as well encourage people to upgrade # to a version that is not completely ancient. - warn('C compiler (CC=%s, %s) too old, need gcc 4.2 or clang 3.2' % - (CC, version_str)) + warn(f'C compiler (CC={CC}, {version_str}) too old, need gcc 4.2 or clang 3.2') o['variables']['llvm_version'] = get_llvm_version(CC) if is_clang else '0.0' @@ -1076,8 +1069,9 @@ def cc_macros(cc=None): consider adjusting the CC environment variable if you installed it in a non-standard prefix.''') - p.stdin.write(b'\n') - out = to_utf8(p.communicate()[0]).split('\n') + with p: + p.stdin.write(b'\n') + out = to_utf8(p.communicate()[0]).split('\n') k = {} for line in out: @@ -1134,9 +1128,9 @@ def host_arch_cc(): rtn = 'ia32' # default - for i in matchup: - if i in k and k[i] != '0': - rtn = matchup[i] + for key, value in matchup.items(): + if k.get(key, 0) and k[key] != '0': + rtn = value break if rtn == 'mipsel' and '_LP64' in k: @@ -1195,7 +1189,7 @@ def configure_arm(o): def configure_mips(o, target_arch): - can_use_fpu_instructions = (options.mips_float_abi != 'soft') + can_use_fpu_instructions = options.mips_float_abi != 'soft' o['variables']['v8_can_use_fpu_instructions'] = b(can_use_fpu_instructions) o['variables']['v8_use_mips_abi_hardfloat'] = b(can_use_fpu_instructions) o['variables']['mips_arch_variant'] = options.mips_arch_variant @@ -1207,23 +1201,25 @@ def configure_zos(o): o['variables']['node_static_zoslib'] = b(True) if options.static_zoslib_gyp: # Apply to all Node.js components for now - o['variables']['zoslib_include_dir'] = os.path.dirname(options.static_zoslib_gyp) + '/include' + o['variables']['zoslib_include_dir'] = Path(options.static_zoslib_gyp).parent + '/include' o['include_dirs'] += [o['variables']['zoslib_include_dir']] else: raise Exception('--static-zoslib-gyp= is required.') def clang_version_ge(version_checked): for compiler in [(CC, 'c'), (CXX, 'c++')]: - ok, is_clang, clang_version, gcc_version = \ + _, is_clang, clang_version, _1 = ( try_check_compiler(compiler[0], compiler[1]) + ) if is_clang and clang_version >= version_checked: return True return False def gcc_version_ge(version_checked): for compiler in [(CC, 'c'), (CXX, 'c++')]: - ok, is_clang, clang_version, gcc_version = \ + _, is_clang, _1, gcc_version = ( try_check_compiler(compiler[0], compiler[1]) + ) if is_clang or gcc_version < version_checked: return False return True @@ -1324,7 +1320,7 @@ def configure_node(o): version_checked_str = ".".join(map(str, version_checked)) raise Exception( 'The options --enable-pgo-generate and --enable-pgo-use ' - 'are supported for gcc and gxx %s or newer only.' % (version_checked_str)) + f'are supported for gcc and gxx {version_checked_str} or newer only.') if options.enable_pgo_generate and options.enable_pgo_use: raise Exception( @@ -1347,8 +1343,8 @@ def configure_node(o): gcc_version_checked_str = ".".join(map(str, gcc_version_checked)) clang_version_checked_str = ".".join(map(str, clang_version_checked)) raise Exception( - 'The option --enable-lto is supported for gcc %s+' - 'or clang %s+ only.' % (gcc_version_checked_str, clang_version_checked_str)) + f'The option --enable-lto is supported for gcc {gcc_version_checked_str}+' + f'or clang {clang_version_checked_str}+ only.') o['variables']['enable_lto'] = b(options.enable_lto) @@ -1458,15 +1454,15 @@ def configure_library(lib, output, pkgname=None): if 'msvs_settings' not in output: output['msvs_settings'] = { 'VCLinkerTool': { 'AdditionalOptions': [] } } output['msvs_settings']['VCLinkerTool']['AdditionalOptions'] += [ - '/LIBPATH:%s' % options.__dict__[shared_lib + '_libpath']] + f"/LIBPATH:{options.__dict__[shared_lib + '_libpath']}"] else: output['libraries'] += [ - '-L%s' % options.__dict__[shared_lib + '_libpath']] + f"-L{options.__dict__[shared_lib + '_libpath']}"] elif pkg_libpath: output['libraries'] += [pkg_libpath] default_libs = getattr(options, shared_lib + '_libname') - default_libs = ['-l{0}'.format(l) for l in default_libs.split(',')] + default_libs = [f'-l{l}' for l in default_libs.split(',')] if default_libs: output['libraries'] += default_libs @@ -1528,7 +1524,7 @@ def configure_openssl(o): if options.without_ssl: def without_ssl_error(option): - error('--without-ssl is incompatible with %s' % option) + error(f'--without-ssl is incompatible with {option}') if options.shared_openssl: without_ssl_error('--shared-openssl') if options.openssl_no_asm: @@ -1608,36 +1604,35 @@ def configure_static(o): def write(filename, data): - print_verbose('creating %s' % filename) - with open(filename, 'w+') as f: + print_verbose(f'creating {filename}') + with Path(filename).open(mode='w+', encoding='utf-8') as f: f.write(data) do_not_edit = '# Do not edit. Generated by the configure script.\n' def glob_to_var(dir_base, dir_sub, patch_dir): - list = [] - dir_all = '%s/%s' % (dir_base, dir_sub) + file_list = [] + dir_all = f'{dir_base}/{dir_sub}' files = os.walk(dir_all) for ent in files: - (path, dirs, files) = ent + (_, _1, files) = ent for file in files: if file.endswith(('.cpp', '.c', '.h')): # srcfile uses "slash" as dir separator as its output is consumed by gyp - srcfile = '%s/%s' % (dir_sub, file) + srcfile = f'{dir_sub}/{file}' if patch_dir: - patchfile = '%s/%s/%s' % (dir_base, patch_dir, file) - if os.path.isfile(patchfile): - srcfile = '%s/%s' % (patch_dir, file) - info('Using floating patch "%s" from "%s"' % (patchfile, dir_base)) - list.append(srcfile) + patchfile = Path(dir_base, patch_dir, file) + if patchfile.is_file(): + srcfile = f'{patch_dir}/{file}' + info(f'Using floating patch "{patchfile}" from "{dir_base}"') + file_list.append(srcfile) break - return list + return file_list def configure_intl(o): def icu_download(path): - depFile = 'tools/icu/current_ver.dep' - with open(depFile) as f: - icus = json.load(f) + depFile = tools_path / 'icu' / 'current_ver.dep' + icus = json.loads(depFile.read_text(encoding='utf-8')) # download ICU, if needed if not os.access(options.download_path, os.W_OK): error('''Cannot write to desired download path. @@ -1647,26 +1642,26 @@ def icu_download(path): url = icu['url'] (expectHash, hashAlgo, allAlgos) = nodedownload.findHash(icu) if not expectHash: - error('''Could not find a hash to verify ICU download. - %s may be incorrect. - For the entry %s, - Expected one of these keys: %s''' % (depFile, url, ' '.join(allAlgos))) + error(f'''Could not find a hash to verify ICU download. + {depFile} may be incorrect. + For the entry {url}, + Expected one of these keys: {' '.join(allAlgos)}''') local = url.split('/')[-1] - targetfile = os.path.join(options.download_path, local) - if not os.path.isfile(targetfile): + targetfile = Path(options.download_path, local) + if not targetfile.is_file(): if attemptdownload: nodedownload.retrievefile(url, targetfile) else: - print('Re-using existing %s' % targetfile) - if os.path.isfile(targetfile): - print('Checking file integrity with %s:\r' % hashAlgo) + print(f'Re-using existing {targetfile}') + if targetfile.is_file(): + print(f'Checking file integrity with {hashAlgo}:\r') gotHash = nodedownload.checkHash(targetfile, hashAlgo) - print('%s: %s %s' % (hashAlgo, gotHash, targetfile)) - if (expectHash == gotHash): + print(f'{hashAlgo}: {gotHash} {targetfile}') + if expectHash == gotHash: return targetfile - else: - warn('Expected: %s *MISMATCH*' % expectHash) - warn('\n ** Corrupted ZIP? Delete %s to retry download.\n' % targetfile) + + warn(f'Expected: {expectHash} *MISMATCH*') + warn(f'\n ** Corrupted ZIP? Delete {targetfile} to retry download.\n') return None icu_config = { 'variables': {} @@ -1694,12 +1689,14 @@ def icu_download(path): # use the .gyp given o['variables']['icu_gyp_path'] = options.with_icu_path return + # --with-intl= # set the default if with_intl in (None, 'none'): o['variables']['v8_enable_i18n_support'] = 0 return # no Intl - elif with_intl == 'small-icu': + + if with_intl == 'small-icu': # small ICU (English only) o['variables']['v8_enable_i18n_support'] = 1 o['variables']['icu_small'] = b(True) @@ -1722,8 +1719,7 @@ def icu_download(path): icu_ver_major = icuversion.split('.')[0] o['variables']['icu_ver_major'] = icu_ver_major if int(icu_ver_major) < icu_versions['minimum_icu']: - error('icu4c v%s is too old, v%d.x or later is required.' % - (icuversion, icu_versions['minimum_icu'])) + error(f"icu4c v{icuversion} is too old, v{icu_versions['minimum_icu']}.x or later is required.") # libpath provides linker path which may contain spaces if libpath: o['libraries'] += [libpath] @@ -1744,16 +1740,17 @@ def icu_download(path): icu_full_path = icu_deps_path # icu-tmp is used to download and unpack the ICU tarball. - icu_tmp_path = os.path.join(icu_parent_path, 'icu-tmp') + icu_tmp_path = Path(icu_parent_path, 'icu-tmp') # canned ICU. see tools/icu/README.md to update. canned_icu_dir = 'deps/icu-small' # use the README to verify what the canned ICU is - canned_is_full = os.path.isfile(os.path.join(canned_icu_dir, 'README-FULL-ICU.txt')) - canned_is_small = os.path.isfile(os.path.join(canned_icu_dir, 'README-SMALL-ICU.txt')) + canned_icu_path = Path(canned_icu_dir) + canned_is_full = (canned_icu_path / 'README-FULL-ICU.txt').is_file() + canned_is_small = (canned_icu_path / 'README-SMALL-ICU.txt').is_file() if canned_is_small: - warn('Ignoring %s - in-repo small icu is no longer supported.' % canned_icu_dir) + warn(f'Ignoring {canned_icu_dir} - in-repo small icu is no longer supported.') # We can use 'deps/icu-small' - pre-canned ICU *iff* # - canned_is_full AND @@ -1771,66 +1768,65 @@ def icu_download(path): icu_config['variables']['icu_full_canned'] = 1 # --with-icu-source processing # now, check that they didn't pass --with-icu-source=deps/icu - elif with_icu_source and os.path.abspath(icu_full_path) == os.path.abspath(with_icu_source): - warn('Ignoring redundant --with-icu-source=%s' % with_icu_source) + elif with_icu_source and Path(icu_full_path).resolve() == Path(with_icu_source).resolve(): + warn(f'Ignoring redundant --with-icu-source={with_icu_source}') with_icu_source = None # if with_icu_source is still set, try to use it. if with_icu_source: - if os.path.isdir(icu_full_path): - print('Deleting old ICU source: %s' % icu_full_path) + if Path(icu_full_path).is_dir(): + print(f'Deleting old ICU source: {icu_full_path}') shutil.rmtree(icu_full_path) # now, what path was given? - if os.path.isdir(with_icu_source): + if Path(with_icu_source).is_dir(): # it's a path. Copy it. - print('%s -> %s' % (with_icu_source, icu_full_path)) + print(f'{with_icu_source} -> {icu_full_path}') shutil.copytree(with_icu_source, icu_full_path) else: # could be file or URL. # Set up temporary area - if os.path.isdir(icu_tmp_path): + if Path(icu_tmp_path).is_dir(): shutil.rmtree(icu_tmp_path) - os.mkdir(icu_tmp_path) + icu_tmp_path.mkdir() icu_tarball = None - if os.path.isfile(with_icu_source): + if Path(with_icu_source).is_file(): # it's a file. Try to unpack it. icu_tarball = with_icu_source else: # Can we download it? - local = os.path.join(icu_tmp_path, with_icu_source.split('/')[-1]) # local part + local = icu_tmp_path / with_icu_source.split('/')[-1] # local part icu_tarball = nodedownload.retrievefile(with_icu_source, local) # continue with "icu_tarball" nodedownload.unpack(icu_tarball, icu_tmp_path) # Did it unpack correctly? Should contain 'icu' - tmp_icu = os.path.join(icu_tmp_path, 'icu') - if os.path.isdir(tmp_icu): - os.rename(tmp_icu, icu_full_path) + tmp_icu = icu_tmp_path / 'icu' + if tmp_icu.is_dir(): + tmp_icu.rename(icu_full_path) shutil.rmtree(icu_tmp_path) else: shutil.rmtree(icu_tmp_path) - error('--with-icu-source=%s did not result in an "icu" dir.' % \ - with_icu_source) + error(f'--with-icu-source={with_icu_source} did not result in an "icu" dir.') # ICU mode. (icu-generic.gyp) o['variables']['icu_gyp_path'] = 'tools/icu/icu-generic.gyp' # ICU source dir relative to tools/icu (for .gyp file) o['variables']['icu_path'] = icu_full_path - if not os.path.isdir(icu_full_path): + if not Path(icu_full_path).is_dir(): # can we download (or find) a zipfile? localzip = icu_download(icu_full_path) if localzip: nodedownload.unpack(localzip, icu_parent_path) else: - warn('* ECMA-402 (Intl) support didn\'t find ICU in %s..' % icu_full_path) - if not os.path.isdir(icu_full_path): - error('''Cannot build Intl without ICU in %s. - Fix, or disable with "--with-intl=none"''' % icu_full_path) + warn(f"* ECMA-402 (Intl) support didn't find ICU in {icu_full_path}..") + if not Path(icu_full_path).is_dir(): + error(f'''Cannot build Intl without ICU in {icu_full_path}. + Fix, or disable with "--with-intl=none"''') else: - print_verbose('* Using ICU in %s' % icu_full_path) + print_verbose(f'* Using ICU in {icu_full_path}') # Now, what version of ICU is it? We just need the "major", such as 54. # uvernum.h contains it as a #define. - uvernum_h = os.path.join(icu_full_path, 'source/common/unicode/uvernum.h') - if not os.path.isfile(uvernum_h): - error('Could not load %s - is ICU installed?' % uvernum_h) + uvernum_h = Path(icu_full_path, 'source', 'common', 'unicode', 'uvernum.h') + if not uvernum_h.is_file(): + error(f'Could not load {uvernum_h} - is ICU installed?') icu_ver_major = None matchVerExp = r'^\s*#define\s+U_ICU_VERSION_SHORT\s+"([^"]*)".*' match_version = re.compile(matchVerExp) @@ -1840,27 +1836,24 @@ def icu_download(path): if m: icu_ver_major = str(m.group(1)) if not icu_ver_major: - error('Could not read U_ICU_VERSION_SHORT version from %s' % uvernum_h) + error(f'Could not read U_ICU_VERSION_SHORT version from {uvernum_h}') elif int(icu_ver_major) < icu_versions['minimum_icu']: - error('icu4c v%s.x is too old, v%d.x or later is required.' % - (icu_ver_major, icu_versions['minimum_icu'])) + error(f"icu4c v{icu_ver_major}.x is too old, v{icu_versions['minimum_icu']}.x or later is required.") icu_endianness = sys.byteorder[0] o['variables']['icu_ver_major'] = icu_ver_major o['variables']['icu_endianness'] = icu_endianness - icu_data_file_l = 'icudt%s%s.dat' % (icu_ver_major, 'l') # LE filename - icu_data_file = 'icudt%s%s.dat' % (icu_ver_major, icu_endianness) + icu_data_file_l = f'icudt{icu_ver_major}l.dat' # LE filename + icu_data_file = f'icudt{icu_ver_major}{icu_endianness}.dat' # relative to configure - icu_data_path = os.path.join(icu_full_path, - 'source/data/in', - icu_data_file_l) # LE - compressed_data = '%s.bz2' % (icu_data_path) - if not os.path.isfile(icu_data_path) and os.path.isfile(compressed_data): + icu_data_path = Path(icu_full_path, 'source', 'data', 'in', icu_data_file_l) # LE + compressed_data = f'{icu_data_path}.bz2' + if not icu_data_path.is_file() and Path(compressed_data).is_file(): # unpack. deps/icu is a temporary path - if os.path.isdir(icu_tmp_path): + if icu_tmp_path.is_dir(): shutil.rmtree(icu_tmp_path) - os.mkdir(icu_tmp_path) - icu_data_path = os.path.join(icu_tmp_path, icu_data_file_l) - with open(icu_data_path, 'wb') as outf: + icu_tmp_path.mkdir() + icu_data_path = icu_tmp_path / icu_data_file_l + with icu_data_path.open(mode='wb') as outf: inf = bz2.BZ2File(compressed_data, 'rb') try: shutil.copyfileobj(inf, outf) @@ -1869,20 +1862,18 @@ def icu_download(path): # Now, proceed.. # relative to dep.. - icu_data_in = os.path.join('..','..', icu_data_path) - if not os.path.isfile(icu_data_path) and icu_endianness != 'l': + icu_data_in = Path('..', '..', icu_data_path) + if not icu_data_path.is_file() and icu_endianness != 'l': # use host endianness - icu_data_path = os.path.join(icu_full_path, - 'source/data/in', - icu_data_file) # will be generated - if not os.path.isfile(icu_data_path): + icu_data_path = Path(icu_full_path, 'source', 'data', 'in', icu_data_file) # will be generated + if not icu_data_path.is_file(): # .. and we're not about to build it from .gyp! - error('''ICU prebuilt data file %s does not exist. - See the README.md.''' % icu_data_path) + error(f'''ICU prebuilt data file {icu_data_path} does not exist. + See the README.md.''') # this is the input '.dat' file to use .. icudt*.dat # may be little-endian if from a icu-project.org tarball - o['variables']['icu_data_in'] = icu_data_in + o['variables']['icu_data_in'] = str(icu_data_in) # map from variable name to subdirs icu_src = { @@ -1896,10 +1887,10 @@ def icu_download(path): } # this creates a variable icu_src_XXX for each of the subdirs # with a list of the src files to use - for i in icu_src: - var = 'icu_src_%s' % i - path = '../../%s/source/%s' % (icu_full_path, icu_src[i]) - icu_config['variables'][var] = glob_to_var('tools/icu', path, 'patches/%s/source/%s' % (icu_ver_major, icu_src[i]) ) + for key, value in icu_src.items(): + var = f'icu_src_{key}' + path = f'../../{icu_full_path}/source/{value}' + icu_config['variables'][var] = glob_to_var('tools/icu', path, f'patches/{icu_ver_major}/source/{value}') # calculate platform-specific genccode args # print("platform %s, flavor %s" % (sys.platform, flavor)) # if sys.platform == 'darwin': @@ -1950,8 +1941,9 @@ def configure_section_file(o): warn('''No acceptable ld.gold linker found!''') return 0 - match = re.match(r"^GNU gold.*([0-9]+)\.([0-9]+)$", - proc.communicate()[0].decode("utf-8")) + with proc: + match = re.match(r"^GNU gold.*([0-9]+)\.([0-9]+)$", + proc.communicate()[0].decode("utf-8")) if match: gold_major_version = match.group(1) @@ -1979,23 +1971,25 @@ def make_bin_override(): os.path.realpath(which_python) == os.path.realpath(sys.executable)): return - bin_override = os.path.abspath('out/tools/bin') + bin_override = Path('out', 'tools', 'bin').resolve() try: - os.makedirs(bin_override) + bin_override.mkdir(parents=True) except OSError as e: - if e.errno != errno.EEXIST: raise e + if e.errno != errno.EEXIST: + raise e - python_link = os.path.join(bin_override, 'python') + python_link = bin_override / 'python' try: - os.unlink(python_link) + python_link.unlink() except OSError as e: - if e.errno != errno.ENOENT: raise e + if e.errno != errno.ENOENT: + raise e os.symlink(sys.executable, python_link) # We need to set the environment right now so that when gyp (in run_gyp) # shells out, it finds the right python (specifically at # https://github.com/nodejs/node/blob/d82e107/deps/v8/gypfiles/toolchain.gypi#L43) - os.environ['PATH'] = bin_override + ':' + os.environ['PATH'] + os.environ['PATH'] = str(bin_override) + ':' + os.environ['PATH'] return bin_override @@ -2013,7 +2007,7 @@ def make_bin_override(): # determine the "flavor" (operating system) we're building for, # leveraging gyp's GetFlavor function flavor_params = {} -if (options.dest_os): +if options.dest_os: flavor_params['flavor'] = options.dest_os flavor = GetFlavor(flavor_params) @@ -2037,12 +2031,12 @@ def make_bin_override(): # configure shareable builtins output['variables']['node_builtin_shareable_builtins'] = [] -for builtin in shareable_builtins: +for builtin, value in shareable_builtins.items(): builtin_id = 'node_shared_builtin_' + builtin.replace('/', '_') + '_path' if getattr(options, builtin_id): output['defines'] += [builtin_id.upper() + '=' + getattr(options, builtin_id)] else: - output['variables']['node_builtin_shareable_builtins'] += [shareable_builtins[builtin]] + output['variables']['node_builtin_shareable_builtins'] += [value] # Forward OSS-Fuzz settings output['variables']['ossfuzz'] = b(options.ossfuzz) @@ -2074,7 +2068,7 @@ def make_bin_override(): write('config.status', '#!/bin/sh\nset -x\nexec ./configure ' + ' '.join([shlex.quote(arg) for arg in original_argv]) + '\n') -os.chmod('config.status', 0o775) +Path('config.status').chmod(0o775) config = { @@ -2104,7 +2098,7 @@ def make_bin_override(): # On Windows there's no reason to search for a different python binary. bin_override = None if sys.platform == 'win32' else make_bin_override() if bin_override: - config_str = 'export PATH:=' + bin_override + ':$(PATH)\n' + config_str + config_str = 'export PATH:=' + str(bin_override) + ':$(PATH)\n' + config_str write('config.mk', do_not_edit + config_str) diff --git a/deps/ada/ada.cpp b/deps/ada/ada.cpp index 197cb8ed800f98..dc090542e3e1a5 100644 --- a/deps/ada/ada.cpp +++ b/deps/ada/ada.cpp @@ -1,4 +1,4 @@ -/* auto-generated on 2023-03-30 17:00:48 -0400. Do not edit! */ +/* auto-generated on 2023-04-26 16:43:37 -0400. Do not edit! */ /* begin file src/ada.cpp */ #include "ada.h" /* begin file src/checkers.cpp */ @@ -116,12 +116,13 @@ ada_really_inline constexpr bool verify_dns_length( ADA_PUSH_DISABLE_ALL_WARNINGS /* begin file src/ada_idna.cpp */ -/* auto-generated on 2023-03-28 11:03:13 -0400. Do not edit! */ +/* auto-generated on 2023-04-26 14:14:42 -0400. Do not edit! */ /* begin file src/idna.cpp */ /* begin file src/unicode_transcoding.cpp */ #include #include + namespace ada::idna { size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) { @@ -2750,10 +2751,12 @@ uint32_t find_range_index(uint32_t key) { } bool ascii_has_upper_case(char* input, size_t length) { - auto broadcast = [](uint8_t v) -> uint64_t { return 0x101010101010101 * v; }; + auto broadcast = [](uint8_t v) -> uint64_t { + return 0x101010101010101ull * v; + }; uint64_t broadcast_80 = broadcast(0x80); uint64_t broadcast_Ap = broadcast(128 - 'A'); - uint64_t broadcast_Zp = broadcast(128 - 'Z'); + uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1); size_t i = 0; uint64_t runner{0}; @@ -2772,10 +2775,12 @@ bool ascii_has_upper_case(char* input, size_t length) { } void ascii_map(char* input, size_t length) { - auto broadcast = [](uint8_t v) -> uint64_t { return 0x101010101010101 * v; }; + auto broadcast = [](uint8_t v) -> uint64_t { + return 0x101010101010101ull * v; + }; uint64_t broadcast_80 = broadcast(0x80); uint64_t broadcast_Ap = broadcast(128 - 'A'); - uint64_t broadcast_Zp = broadcast(128 - 'Z'); + uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1); size_t i = 0; for (; i + 7 < length; i += 8) { @@ -7999,9 +8004,10 @@ const char32_t uninorms::decomposition_data[] = { namespace ada::idna { void normalize(std::u32string& input) { - // [Normalize](https://www.unicode.org/reports/tr46/#ProcessingStepNormalize). - // Normalize - // the domain_name string to Unicode Normalization Form C. + /** + * Normalize the domain_name string to Unicode Normalization Form C. + * @see https://www.unicode.org/reports/tr46/#ProcessingStepNormalize + */ ufal::unilib::uninorms::nfc(input); } @@ -8229,7 +8235,6 @@ bool utf32_to_punycode(std::u32string_view input, std::string &out) { } // namespace ada::idna /* end file src/punycode.cpp */ /* begin file src/validity.cpp */ - #include #include @@ -9617,18 +9622,18 @@ constexpr static uint8_t is_forbidden_domain_code_point_table[] = { static_assert(sizeof(is_forbidden_domain_code_point_table) == 256); -inline constexpr bool is_forbidden_domain_code_point(const char c) noexcept { +inline bool is_forbidden_domain_code_point(const char c) noexcept { return is_forbidden_domain_code_point_table[uint8_t(c)]; } -// We return "" on error. For now. -std::string from_ascii_to_ascii(std::string_view ut8_string) { - static const std::string error = ""; - if (std::any_of(ut8_string.begin(), ut8_string.end(), - is_forbidden_domain_code_point)) { - return error; - } +bool contains_forbidden_domain_code_point(std::string_view view) { + return ( + std::any_of(view.begin(), view.end(), is_forbidden_domain_code_point)); +} +// We return "" on error. +static std::string from_ascii_to_ascii(std::string_view ut8_string) { + static const std::string error = ""; // copy and map // we could be more efficient by avoiding the copy when unnecessary. std::string mapped_string = std::string(ut8_string); @@ -9682,7 +9687,7 @@ std::string from_ascii_to_ascii(std::string_view ut8_string) { return out; } -// We return "" on error. For now. +// We return "" on error. std::string to_ascii(std::string_view ut8_string) { if (is_ascii(ut8_string)) { return from_ascii_to_ascii(ut8_string); @@ -9769,11 +9774,6 @@ std::string to_ascii(std::string_view ut8_string) { out.push_back('.'); } } - - if (std::any_of(out.begin(), out.end(), is_forbidden_domain_code_point)) { - return error; - } - return out; } } // namespace ada::idna @@ -9842,10 +9842,12 @@ ADA_POP_DISABLE_WARNINGS namespace ada::unicode { constexpr bool to_lower_ascii(char* input, size_t length) noexcept { - auto broadcast = [](uint8_t v) -> uint64_t { return 0x101010101010101 * v; }; + auto broadcast = [](uint8_t v) -> uint64_t { + return 0x101010101010101ull * v; + }; uint64_t broadcast_80 = broadcast(0x80); uint64_t broadcast_Ap = broadcast(128 - 'A'); - uint64_t broadcast_Zp = broadcast(128 - 'Z'); + uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1); uint64_t non_ascii = 0; size_t i = 0; @@ -9873,7 +9875,9 @@ ada_really_inline constexpr bool has_tabs_or_newline( auto has_zero_byte = [](uint64_t v) { return ((v - 0x0101010101010101) & ~(v)&0x8080808080808080); }; - auto broadcast = [](uint8_t v) -> uint64_t { return 0x101010101010101 * v; }; + auto broadcast = [](uint8_t v) -> uint64_t { + return 0x101010101010101ull * v; + }; size_t i = 0; uint64_t mask1 = broadcast('\r'); uint64_t mask2 = broadcast('\n'); @@ -9961,7 +9965,7 @@ ada_really_inline constexpr bool is_forbidden_domain_code_point( } ada_really_inline constexpr bool contains_forbidden_domain_code_point( - char* input, size_t length) noexcept { + const char* input, size_t length) noexcept { size_t i = 0; uint8_t accumulator{}; for (; i + 4 <= length; i += 4) { @@ -9976,6 +9980,44 @@ ada_really_inline constexpr bool contains_forbidden_domain_code_point( return accumulator; } +constexpr static uint8_t is_forbidden_domain_code_point_table_or_upper[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 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, 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, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + +static_assert(sizeof(is_forbidden_domain_code_point_table_or_upper) == 256); +static_assert(is_forbidden_domain_code_point_table_or_upper[uint8_t('A')] == 2); +static_assert(is_forbidden_domain_code_point_table_or_upper[uint8_t('Z')] == 2); + +ada_really_inline constexpr bool contains_forbidden_domain_code_point_or_upper( + const char* input, size_t length) noexcept { + size_t i = 0; + uint8_t accumulator{}; + for (; i + 4 <= length; i += 4) { + accumulator |= + is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])]; + accumulator |= + is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 1])]; + accumulator |= + is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 2])]; + accumulator |= + is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 3])]; + } + for (; i < length; i++) { + accumulator |= + is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])]; + } + return accumulator; +} + static_assert(unicode::is_forbidden_domain_code_point('%')); static_assert(unicode::is_forbidden_domain_code_point('\x7f')); static_assert(unicode::is_forbidden_domain_code_point('\0')); @@ -10214,7 +10256,8 @@ bool to_ascii(std::optional& out, const std::string_view plain, } // input is a non-empty UTF-8 string, must be percent decoded std::string idna_ascii = ada::idna::to_ascii(input); - if (idna_ascii.empty()) { + if (idna_ascii.empty() || contains_forbidden_domain_code_point( + idna_ascii.data(), idna_ascii.size())) { return false; } out = std::move(idna_ascii); @@ -10463,7 +10506,7 @@ ada_unused std::string get_state(ada::state s) { } } -ada_really_inline std::optional prune_fragment( +ada_really_inline std::optional prune_hash( std::string_view& input) noexcept { // compiles down to 20--30 instructions including a class to memchr (C // function). this function should be quite fast. @@ -10471,10 +10514,10 @@ ada_really_inline std::optional prune_fragment( if (location_of_first == std::string_view::npos) { return std::nullopt; } - std::string_view fragment = input; - fragment.remove_prefix(location_of_first + 1); + std::string_view hash = input; + hash.remove_prefix(location_of_first + 1); input.remove_suffix(input.size() - location_of_first); - return fragment; + return hash; } ada_really_inline bool shorten_path(std::string& path, @@ -10485,9 +10528,9 @@ ada_really_inline bool shorten_path(std::string& path, // If url’s scheme is "file", path’s size is 1, and path[0] is a normalized // Windows drive letter, then return. if (type == ada::scheme::type::FILE && - first_delimiter == std::string_view::npos) { + first_delimiter == std::string_view::npos && !path.empty()) { if (checkers::is_normalized_windows_drive_letter( - std::string_view(path.data() + 1, first_delimiter - 1))) { + helpers::substring(path, 1))) { return false; } } @@ -10509,9 +10552,9 @@ ada_really_inline bool shorten_path(std::string_view& path, // If url’s scheme is "file", path’s size is 1, and path[0] is a normalized // Windows drive letter, then return. if (type == ada::scheme::type::FILE && - first_delimiter == std::string_view::npos) { + first_delimiter == std::string_view::npos && !path.empty()) { if (checkers::is_normalized_windows_drive_letter( - std::string_view(path.data() + 1, first_delimiter - 1))) { + helpers::substring(path, 1))) { return false; } } @@ -10589,7 +10632,9 @@ ada_really_inline size_t find_next_host_delimiter_special( auto index_of_first_set_byte = [](uint64_t v) { return ((((v - 1) & 0x101010101010101) * 0x101010101010101) >> 56) - 1; }; - auto broadcast = [](uint8_t v) -> uint64_t { return 0x101010101010101 * v; }; + auto broadcast = [](uint8_t v) -> uint64_t { + return 0x101010101010101ull * v; + }; size_t i = location; uint64_t mask1 = broadcast(':'); uint64_t mask2 = broadcast('/'); @@ -10652,7 +10697,9 @@ ada_really_inline size_t find_next_host_delimiter(std::string_view view, auto index_of_first_set_byte = [](uint64_t v) { return ((((v - 1) & 0x101010101010101) * 0x101010101010101) >> 56) - 1; }; - auto broadcast = [](uint8_t v) -> uint64_t { return 0x101010101010101 * v; }; + auto broadcast = [](uint8_t v) -> uint64_t { + return 0x101010101010101ull * v; + }; size_t i = location; uint64_t mask1 = broadcast(':'); uint64_t mask2 = broadcast('/'); @@ -10960,8 +11007,8 @@ ada_really_inline void strip_trailing_spaces_from_opaque_path( url_type& url) noexcept { ada_log("helpers::strip_trailing_spaces_from_opaque_path"); if (!url.has_opaque_path) return; - if (url.base_fragment_has_value()) return; - if (url.base_search_has_value()) return; + if (url.has_hash()) return; + if (url.has_search()) return; auto path = std::string(url.get_pathname()); while (!path.empty() && path.back() == ' ') { @@ -10978,7 +11025,9 @@ find_authority_delimiter_special(std::string_view view) noexcept { auto index_of_first_set_byte = [](uint64_t v) { return ((((v - 1) & 0x101010101010101) * 0x101010101010101) >> 56) - 1; }; - auto broadcast = [](uint8_t v) -> uint64_t { return 0x101010101010101 * v; }; + auto broadcast = [](uint8_t v) -> uint64_t { + return 0x101010101010101ull * v; + }; size_t i = 0; uint64_t mask1 = broadcast('@'); uint64_t mask2 = broadcast('/'); @@ -11026,7 +11075,9 @@ find_authority_delimiter(std::string_view view) noexcept { auto index_of_first_set_byte = [](uint64_t v) { return ((((v - 1) & 0x101010101010101) * 0x101010101010101) >> 56) - 1; }; - auto broadcast = [](uint8_t v) -> uint64_t { return 0x101010101010101 * v; }; + auto broadcast = [](uint8_t v) -> uint64_t { + return 0x101010101010101ull * v; + }; size_t i = 0; uint64_t mask1 = broadcast('@'); uint64_t mask2 = broadcast('/'); @@ -11413,7 +11464,7 @@ ada_really_inline bool url::parse_scheme(const std::string_view input) { // If url includes credentials or has a non-null port, and buffer is // "file", then return. - if ((includes_credentials() || port.has_value()) && + if ((has_credentials() || port.has_value()) && parsed_type == ada::scheme::type::FILE) { return true; } @@ -11458,7 +11509,7 @@ ada_really_inline bool url::parse_scheme(const std::string_view input) { // If url includes credentials or has a non-null port, and buffer is // "file", then return. - if ((includes_credentials() || port.has_value()) && _buffer == "file") { + if ((has_credentials() || port.has_value()) && _buffer == "file") { return true; } @@ -11610,7 +11661,7 @@ std::string url::to_string() const { answer.append("\t\"protocol\":\""); helpers::encode_json(get_protocol(), back); answer.append("\",\n"); - if (includes_credentials()) { + if (has_credentials()) { answer.append("\t\"username\":\""); helpers::encode_json(username, back); answer.append("\",\n"); @@ -11633,16 +11684,16 @@ std::string url::to_string() const { answer.append("\",\n"); answer.append("\t\"opaque path\":"); answer.append((has_opaque_path ? "true" : "false")); - if (base_search_has_value()) { + if (has_search()) { answer.append(",\n"); answer.append("\t\"query\":\""); helpers::encode_json(query.value(), back); answer.append("\""); } - if (fragment.has_value()) { + if (hash.has_value()) { answer.append(",\n"); - answer.append("\t\"fragment\":\""); - helpers::encode_json(fragment.value(), back); + answer.append("\t\"hash\":\""); + helpers::encode_json(hash.value(), back); answer.append("\""); } answer.append("\n}"); @@ -11743,9 +11794,8 @@ namespace ada { [[nodiscard]] std::string url::get_hash() const noexcept { // If this’s URL’s fragment is either null or the empty string, then return // the empty string. Return U+0023 (#), followed by this’s URL’s fragment. - return (!fragment.has_value() || (fragment.value().empty())) - ? "" - : "#" + fragment.value(); + return (!hash.has_value() || (hash.value().empty())) ? "" + : "#" + hash.value(); } } // namespace ada @@ -11801,7 +11851,7 @@ bool url::set_host_or_hostname(const std::string_view input) { // empty string, and either url includes credentials or url’s port is // non-null, return. else if (host_view.empty() && - (is_special() || includes_credentials() || port.has_value())) { + (is_special() || has_credentials() || port.has_value())) { return false; } @@ -11901,7 +11951,7 @@ bool url::set_port(const std::string_view input) { void url::set_hash(const std::string_view input) { if (input.empty()) { - fragment = std::nullopt; + hash = std::nullopt; helpers::strip_trailing_spaces_from_opaque_path(*this); return; } @@ -11909,8 +11959,8 @@ void url::set_hash(const std::string_view input) { std::string new_value; new_value = input[0] == '#' ? input.substr(1) : input; helpers::remove_ascii_tab_or_newline(new_value); - fragment = unicode::percent_encode( - new_value, ada::character_sets::FRAGMENT_PERCENT_ENCODE); + hash = unicode::percent_encode(new_value, + ada::character_sets::FRAGMENT_PERCENT_ENCODE); return; } @@ -11976,7 +12026,7 @@ bool url::set_href(const std::string_view input) { port = out->port; path = out->path; query = out->query; - fragment = out->fragment; + hash = out->hash; type = out->type; non_special_scheme = out->non_special_scheme; has_opaque_path = out->has_opaque_path; @@ -12068,7 +12118,7 @@ result_type parse_url(std::string_view user_input, helpers::trim_c0_whitespace(url_data); // Optimization opportunity. Most websites do not have fragment. - std::optional fragment = helpers::prune_fragment(url_data); + std::optional fragment = helpers::prune_hash(url_data); // We add it last so that an implementation like ada::url_aggregator // can append it last to its internal buffer, thus improving performance. @@ -12425,7 +12475,7 @@ result_type parse_url(std::string_view user_input, // Otherwise, if c is not the EOF code point: else if (input_position != input_size) { // Set url’s query to null. - url.clear_base_search(); + url.clear_search(); if constexpr (result_type_is_ada_url) { // Shorten url’s path. helpers::shorten_path(url.path, url.type); @@ -12840,7 +12890,7 @@ result_type parse_url(std::string_view user_input, // Otherwise, if c is not the EOF code point: else if (input_position != input_size) { // Set url’s query to null. - url.clear_base_search(); + url.clear_search(); // If the code point substring from pointer to the end of input does // not start with a Windows drive letter, then shorten url’s path. @@ -12857,11 +12907,7 @@ result_type parse_url(std::string_view user_input, // Otherwise: else { // Set url’s path to an empty list. - if constexpr (result_type_is_ada_url) { - url.path.clear(); - } else { - url.clear_base_pathname(); - } + url.clear_pathname(); url.has_opaque_path = true; } @@ -13053,8 +13099,7 @@ template // If url includes credentials or has a non-null port, and buffer is // "file", then return. - if ((includes_credentials() || - components.port != url_components::omitted) && + if ((has_credentials() || components.port != url_components::omitted) && parsed_type == ada::scheme::type::FILE) { return true; } @@ -13077,7 +13122,7 @@ template // If url’s port is url’s scheme’s default port, then set url’s port to // null. if (components.port == urls_scheme_port) { - clear_base_port(); + clear_port(); } } } else { // slow path @@ -13097,8 +13142,7 @@ template // If url includes credentials or has a non-null port, and buffer is // "file", then return. - if ((includes_credentials() || - components.port != url_components::omitted) && + if ((has_credentials() || components.port != url_components::omitted) && _buffer == "file") { return true; } @@ -13120,7 +13164,7 @@ template // If url’s port is url’s scheme’s default port, then set url’s port to // null. if (components.port == urls_scheme_port) { - clear_base_port(); + clear_port(); } } } @@ -13301,7 +13345,7 @@ bool url_aggregator::set_port(const std::string_view input) { std::string trimmed(input); helpers::remove_ascii_tab_or_newline(trimmed); if (trimmed.empty()) { - clear_base_port(); + clear_port(); return true; } // Input should not start with control characters. @@ -13332,7 +13376,7 @@ bool url_aggregator::set_pathname(const std::string_view input) { if (has_opaque_path) { return false; } - clear_base_pathname(); + clear_pathname(); parse_path(input); if (checkers::begins_with(input, "//") && !has_authority() && !has_dash_dot()) { @@ -13389,7 +13433,7 @@ void url_aggregator::set_search(const std::string_view input) { ADA_ASSERT_TRUE(validate()); ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer)); if (input.empty()) { - clear_base_search(); + clear_search(); helpers::strip_trailing_spaces_from_opaque_path(*this); return; } @@ -13473,23 +13517,50 @@ ada_really_inline bool url_aggregator::parse_host(std::string_view input) { // to ASCII with domain and false. The most common case is an ASCII input, in // which case we do not need to call the expensive 'to_ascii' if a few // conditions are met: no '%' and no 'xn-' subsequence. - std::string _buffer = std::string(input); - // This next function checks that the result is ascii, but we are going to - // to check anyhow with is_forbidden. - // bool is_ascii = - unicode::to_lower_ascii(_buffer.data(), _buffer.size()); - bool is_forbidden = unicode::contains_forbidden_domain_code_point( - _buffer.data(), _buffer.size()); - if (is_forbidden == 0 && _buffer.find("xn-") == std::string_view::npos) { + + // Often, the input does not contain any forbidden code points, and no upper + // case ASCII letter, then we can just copy it to the buffer. We want to + // optimize for such a common case. + uint8_t is_forbidden_or_upper = + unicode::contains_forbidden_domain_code_point_or_upper(input.data(), + input.size()); + // Minor optimization opportunity: + // contains_forbidden_domain_code_point_or_upper could be extend to check for + // the presence of characters that cannot appear in the ipv4 address and we + // could also check whether x and n and - are present, and so we could skip + // some of the checks below. However, the gains are likely to be small, and + // the code would be more complex. + if (is_forbidden_or_upper == 0 && + input.find("xn-") == std::string_view::npos) { // fast path - update_base_hostname(_buffer); + update_base_hostname(input); if (checkers::is_ipv4(get_hostname())) { ada_log("parse_host fast path ipv4"); return parse_ipv4(get_hostname()); } ada_log("parse_host fast path ", get_hostname()); return true; + } else if (is_forbidden_or_upper == 2) { + // We have encountered at least one upper case ASCII letter, let us + // try to convert it to lower case. If there is no 'xn-' in the result, + // we can then use a secondary fast path. + std::string _buffer = std::string(input); + unicode::to_lower_ascii(_buffer.data(), _buffer.size()); + if (input.find("xn-") == std::string_view::npos) { + // secondary fast path when input is not all lower case + update_base_hostname(input); + if (checkers::is_ipv4(get_hostname())) { + ada_log("parse_host fast path ipv4"); + return parse_ipv4(get_hostname()); + } + ada_log("parse_host fast path ", get_hostname()); + return true; + } } + // We have encountered at least one forbidden code point or the input contains + // 'xn-' (case insensitive), so we need to call 'to_ascii' to perform the full + // conversion. + ada_log("parse_host calling to_ascii"); std::optional host = std::string(get_hostname()); is_valid = ada::unicode::to_ascii(host, input, input.find('%')); @@ -13558,7 +13629,7 @@ bool url_aggregator::set_host_or_hostname(const std::string_view input) { // empty string, and either url includes credentials or url’s port is // non-null, return. else if (host_view.empty() && - (is_special() || includes_credentials() || + (is_special() || has_credentials() || components.port != url_components::omitted)) { return false; } @@ -13566,7 +13637,7 @@ bool url_aggregator::set_host_or_hostname(const std::string_view input) { // Let host be the result of host parsing host_view with url is not special. if (host_view.empty()) { if (has_hostname()) { - clear_base_hostname(); // easy! + clear_hostname(); // easy! } else if (has_dash_dot()) { add_authority_slashes_if_needed(); delete_dash_dot(); @@ -13592,7 +13663,7 @@ bool url_aggregator::set_host_or_hostname(const std::string_view input) { if (new_host.empty()) { // Set url’s host to the empty string. - clear_base_hostname(); + clear_hostname(); } else { // Let host be the result of host parsing buffer with url is not special. if (!parse_host(new_host)) { @@ -13604,7 +13675,7 @@ bool url_aggregator::set_host_or_hostname(const std::string_view input) { // If host is "localhost", then set host to the empty string. if (helpers::substring(buffer, components.host_start, components.host_end) == "localhost") { - clear_base_hostname(); + clear_hostname(); } } ADA_ASSERT_TRUE(validate()); @@ -13771,7 +13842,7 @@ std::string ada::url_aggregator::to_string() const { helpers::encode_json(get_protocol(), back); answer.append("\",\n"); - if (includes_credentials()) { + if (has_credentials()) { answer.append("\t\"username\":\""); helpers::encode_json(get_username(), back); answer.append("\",\n"); diff --git a/deps/ada/ada.h b/deps/ada/ada.h index db24fb969c6489..296547569f9b44 100644 --- a/deps/ada/ada.h +++ b/deps/ada/ada.h @@ -1,4 +1,4 @@ -/* auto-generated on 2023-03-30 17:00:48 -0400. Do not edit! */ +/* auto-generated on 2023-04-26 16:43:37 -0400. Do not edit! */ /* begin file include/ada.h */ /** * @file ada.h @@ -8,7 +8,7 @@ #define ADA_H /* begin file include/ada/ada_idna.h */ -/* auto-generated on 2023-03-28 11:03:13 -0400. Do not edit! */ +/* auto-generated on 2023-04-26 14:14:42 -0400. Do not edit! */ /* begin file include/idna.h */ #ifndef ADA_IDNA_H #define ADA_IDNA_H @@ -40,6 +40,7 @@ size_t utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output); #include #include + namespace ada::idna { // If the input is ascii, then the mapping is just -> lower case. @@ -59,6 +60,7 @@ std::u32string map(std::u32string_view input); #include #include + namespace ada::idna { // Normalize the characters according to IDNA (Unicode Normalization Form C). @@ -73,6 +75,7 @@ void normalize(std::u32string& input); #include #include + namespace ada::idna { bool punycode_to_utf32(std::string_view input, std::u32string& out); @@ -109,14 +112,24 @@ bool is_label_valid(const std::u32string_view label); #include namespace ada::idna { + // Converts a domain (e.g., www.google.com) possibly containing international // characters to an ascii domain (with punycode). It will not do percent // decoding: percent decoding should be done prior to calling this function. We // do not remove tabs and spaces, they should have been removed prior to calling // this function. We also do not trim control characters. We also assume that -// the input is not empty. We return "" on error. For now. +// the input is not empty. We return "" on error. +// +// Example: "www.öbb.at" -> "www.xn--bb-eka.at" +// +// This function may accept or even produce invalid domains. std::string to_ascii(std::string_view ut8_string); +// Returns true if the string contains a forbidden code point according to the +// WHATGL URL specification: +// https://url.spec.whatwg.org/#forbidden-domain-code-point +bool contains_forbidden_domain_code_point(std::string_view ascii_string); + bool constexpr begins_with(std::u32string_view view, std::u32string_view prefix); bool constexpr begins_with(std::string_view view, std::string_view prefix); @@ -124,8 +137,6 @@ bool constexpr begins_with(std::string_view view, std::string_view prefix); bool constexpr is_ascii(std::u32string_view view); bool constexpr is_ascii(std::string_view view); -std::string from_ascii_to_ascii(std::string_view ut8_string); - } // namespace ada::idna #endif // ADA_IDNA_TO_ASCII_H @@ -135,8 +146,12 @@ std::string from_ascii_to_ascii(std::string_view ut8_string); #ifndef ADA_IDNA_TO_UNICODE_H #define ADA_IDNA_TO_UNICODE_H +#include + namespace ada::idna { + std::string to_unicode(std::string_view input); + } // namespace ada::idna #endif // ADA_IDNA_TO_UNICODE_H @@ -1095,122 +1110,143 @@ ada_warn_unused std::string to_string(encoding_type type); #ifndef ADA_HELPERS_H #define ADA_HELPERS_H -/* begin file include/ada/url.h */ +/* begin file include/ada/state.h */ /** - * @file url.h - * @brief Declaration for the URL + * @file state.h + * @brief Definitions for the states of the URL state machine. */ -#ifndef ADA_URL_H -#define ADA_URL_H +#ifndef ADA_STATE_H +#define ADA_STATE_H -/* begin file include/ada/checkers.h */ -/** - * @file checkers.h - * @brief Declarations for URL specific checkers used within Ada. - */ -#ifndef ADA_CHECKERS_H -#define ADA_CHECKERS_H +#include -#include -#include +namespace ada { /** - * @namespace ada::checkers - * @brief Includes the definitions for validation functions + * @see https://url.spec.whatwg.org/#url-parsing */ -namespace ada::checkers { +enum class state { + AUTHORITY, + SCHEME_START, + SCHEME, + HOST, + NO_SCHEME, + FRAGMENT, + RELATIVE_SCHEME, + RELATIVE_SLASH, + FILE, + FILE_HOST, + FILE_SLASH, + PATH_OR_AUTHORITY, + SPECIAL_AUTHORITY_IGNORE_SLASHES, + SPECIAL_AUTHORITY_SLASHES, + SPECIAL_RELATIVE_OR_AUTHORITY, + QUERY, + PATH, + PATH_START, + OPAQUE_PATH, + PORT, +}; /** - * Assuming that x is an ASCII letter, this function returns the lower case - * equivalent. - * @details More likely to be inlined by the compiler and constexpr. + * Stringify a URL state machine state. */ -constexpr char to_lower(char x) noexcept; +ada_warn_unused std::string to_string(ada::state s); -/** - * Returns true if the character is an ASCII letter. Equivalent to std::isalpha - * but more likely to be inlined by the compiler. - * - * @attention std::isalpha is not constexpr generally. - */ -constexpr bool is_alpha(char x) noexcept; +} // namespace ada +#endif // ADA_STATE_H +/* end file include/ada/state.h */ +/* begin file include/ada/url_base.h */ /** - * Check whether a string starts with 0x or 0X. The function is only - * safe if input.size() >=2. - * - * @see has_hex_prefix - */ -inline bool has_hex_prefix_unsafe(std::string_view input); -/** - * Check whether a string starts with 0x or 0X. + * @file url_base.h + * @brief Declaration for the basic URL definitions */ -inline bool has_hex_prefix(std::string_view input); +#ifndef ADA_URL_BASE_H +#define ADA_URL_BASE_H +/* begin file include/ada/url_components.h */ /** - * Check whether x is an ASCII digit. More likely to be inlined than - * std::isdigit. + * @file url_components.h + * @brief Declaration for the URL Components */ -constexpr bool is_digit(char x) noexcept; +#ifndef ADA_URL_COMPONENTS_H +#define ADA_URL_COMPONENTS_H + + +#include +#include + +namespace ada { /** - * @details A string starts with a Windows drive letter if all of the following - * are true: + * @brief URL Component representations using offsets. * - * - its length is greater than or equal to 2 - * - its first two code points are a Windows drive letter - * - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F - * (?), or U+0023 (#). + * @details We design the url_components struct so that it is as small + * and simple as possible. This version uses 32 bytes. * - * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter - */ -inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept; - -/** - * @details A normalized Windows drive letter is a Windows drive letter of which - * the second code point is U+003A (:). + * This struct is used to extract components from a single 'href'. */ -inline constexpr bool is_normalized_windows_drive_letter( - std::string_view input) noexcept; +struct url_components { + constexpr static uint32_t omitted = uint32_t(-1); -/** - * @warning Will be removed when Ada supports C++20. - */ -ada_really_inline constexpr bool begins_with(std::string_view view, - std::string_view prefix); + url_components() = default; + url_components(const url_components &u) = default; + url_components(url_components &&u) noexcept = default; + url_components &operator=(url_components &&u) noexcept = default; + url_components &operator=(const url_components &u) = default; + ~url_components() = default; -/** - * Returns true if an input is an ipv4 address. - */ -ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept; + /* + * By using 32-bit integers, we implicitly assume that the URL string + * cannot exceed 4 GB. + * + * https://user:pass@example.com:1234/foo/bar?baz#quux + * | | | | ^^^^| | | + * | | | | | | | `----- hash_start + * | | | | | | `--------- search_start + * | | | | | `----------------- pathname_start + * | | | | `--------------------- port + * | | | `----------------------- host_end + * | | `---------------------------------- host_start + * | `--------------------------------------- username_end + * `--------------------------------------------- protocol_end + */ + uint32_t protocol_end{0}; + /** + * Username end is not `omitted` by default to make username and password + * getters less costly to implement. + */ + uint32_t username_end{0}; + uint32_t host_start{0}; + uint32_t host_end{0}; + uint32_t port{omitted}; + uint32_t pathname_start{0}; + uint32_t search_start{omitted}; + uint32_t hash_start{omitted}; -/** - * Returns a bitset. If the first bit is set, then at least one character needs - * percent encoding. If the second bit is set, a \\ is found. If the third bit - * is set then we have a dot. If the fourth bit is set, then we have a percent - * character. - */ -ada_really_inline constexpr uint8_t path_signature( - std::string_view input) noexcept; + /** + * Check the following conditions: + * protocol_end < username_end < ... < hash_start, + * expect when a value is omitted. It also computes + * a lower bound on the possible string length that may match these + * offsets. + * @return true if the offset values are + * consistent with a possible URL string + */ + bool check_offset_consistency() const noexcept; -/** - * Returns true if the length of the domain name and its labels are according to - * the specifications. The length of the domain must be 255 octets (253 - * characters not including the last 2 which are the empty label reserved at the - * end). When the empty label is included (a dot at the end), the domain name - * can have 254 characters. The length of a label must be at least 1 and at most - * 63 characters. - * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034 - * @see https://www.unicode.org/reports/tr46/#ToASCII - */ -ada_really_inline constexpr bool verify_dns_length( - std::string_view input) noexcept; + /** + * Converts a url_components to JSON stringified version. + */ + std::string to_string() const; -} // namespace ada::checkers +}; // struct url_components -#endif // ADA_CHECKERS_H -/* end file include/ada/checkers.h */ +} // namespace ada +#endif +/* end file include/ada/url_components.h */ /* begin file include/ada/scheme.h */ /** * @file scheme.h @@ -1289,3829 +1325,3806 @@ constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept; #endif // ADA_SCHEME_H /* end file include/ada/scheme.h */ -/* begin file include/ada/serializers.h */ -/** - * @file serializers.h - * @brief Definitions for the URL serializers. - */ -#ifndef ADA_SERIALIZERS_H -#define ADA_SERIALIZERS_H +#include -#include -#include -#include +namespace ada { /** - * @namespace ada::serializers - * @brief Includes the definitions for URL serializers + * @brief Base class of URL implementations + * + * @details A url_base contains a few attributes: is_valid, has_opaque_path and + * type. All non-trivial implementation details are in derived classes such as + * ada::url and ada::url_aggregator. + * + * It is an abstract class that cannot be instantiated directly. */ -namespace ada::serializers { +struct url_base { + virtual ~url_base() = default; -/** - * Finds and returns the longest sequence of 0 values in a ipv6 input. - */ -void find_longest_sequence_of_ipv6_pieces( - const std::array& address, size_t& compress, - size_t& compress_length) noexcept; + /** + * Used for returning the validity from the result of the URL parser. + */ + bool is_valid{true}; -/** - * Serializes an ipv6 address. - * @details An IPv6 address is a 128-bit unsigned integer that identifies a - * network address. - * @see https://url.spec.whatwg.org/#concept-ipv6-serializer - */ -std::string ipv6(const std::array& address) noexcept; + /** + * A URL has an opaque path if its path is a string. + */ + bool has_opaque_path{false}; -/** - * Serializes an ipv4 address. - * @details An IPv4 address is a 32-bit unsigned integer that identifies a - * network address. - * @see https://url.spec.whatwg.org/#concept-ipv4-serializer - */ -std::string ipv4(const uint64_t address) noexcept; + /** + * @private + */ + ada::scheme::type type{ada::scheme::type::NOT_SPECIAL}; -} // namespace ada::serializers + /** + * A URL is special if its scheme is a special scheme. A URL is not special if + * its scheme is not a special scheme. + */ + [[nodiscard]] ada_really_inline bool is_special() const noexcept; -#endif // ADA_SERIALIZERS_H -/* end file include/ada/serializers.h */ -/* begin file include/ada/unicode.h */ -/** - * @file unicode.h - * @brief Definitions for all unicode specific functions. - */ -#ifndef ADA_UNICODE_H -#define ADA_UNICODE_H + /** + * The origin getter steps are to return the serialization of this’s URL’s + * origin. [HTML] + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#concept-url-origin + */ + [[nodiscard]] virtual std::string get_origin() const noexcept = 0; + + /** + * Returns true if this URL has a valid domain as per RFC 1034 and + * corresponding specifications. Among other things, it requires + * that the domain string has fewer than 255 octets. + */ + [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0; + /** + * @private + * + * Return the 'special port' if the URL is special and not 'file'. + * Returns 0 otherwise. + */ + [[nodiscard]] inline uint16_t get_special_port() const noexcept; -#include + /** + * @private + * + * Get the default port if the url's scheme has one, returns 0 otherwise. + */ + [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept; + + /** + * @private + * + * Parse a port (16-bit decimal digit) from the provided input. + * We assume that the input does not contain spaces or tabs + * within the ASCII digits. + * It returns how many bytes were consumed when a number is successfully + * parsed. + * @return On failure, it returns zero. + * @see https://url.spec.whatwg.org/#host-parsing + */ + virtual ada_really_inline size_t parse_port( + std::string_view view, bool check_trailing_content = false) noexcept = 0; + + /** + * Returns a JSON string representation of this URL. + */ + virtual std::string to_string() const = 0; + + /** @private */ + virtual inline void clear_pathname() = 0; + + /** @private */ + virtual inline void clear_search() = 0; + + /** @private */ + virtual inline bool has_hash() const noexcept = 0; + + /** @private */ + virtual inline bool has_search() const noexcept = 0; + +}; // url_base + +} // namespace ada + +#endif +/* end file include/ada/url_base.h */ + +#include #include /** - * @namespace ada::unicode - * @brief Includes the definitions for unicode operations + * @private + * @namespace ada::helpers + * @brief Includes the definitions for helper functions */ -namespace ada::unicode { +namespace ada::helpers { /** - * We receive a UTF-8 string representing a domain name. - * If the string is percent encoded, we apply percent decoding. - * - * Given a domain, we need to identify its labels. - * They are separated by label-separators: - * - * U+002E ( . ) FULL STOP - * U+FF0E ( . ) FULLWIDTH FULL STOP - * U+3002 ( 。 ) IDEOGRAPHIC FULL STOP - * U+FF61 ( 。 ) HALFWIDTH IDEOGRAPHIC FULL STOP - * - * They are all mapped to U+002E. - * - * We process each label into a string that should not exceed 63 octets. - * If the string is already punycode (starts with "xn--"), then we must - * scan it to look for unallowed code points. - * Otherwise, if the string is not pure ASCII, we need to transcode it - * to punycode by following RFC 3454 which requires us to - * - Map characters (see section 3), - * - Normalize (see section 4), - * - Reject forbidden characters, - * - Check for right-to-left characters and if so, check all requirements (see - * section 6), - * - Optionally reject based on unassigned code points (section 7). - * - * The Unicode standard provides a table of code points with a mapping, a list - * of forbidden code points and so forth. This table is subject to change and - * will vary based on the implementation. For Unicode 15, the table is at - * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt - * If you use ICU, they parse this table and map it to code using a Python - * script. - * - * The resulting strings should not exceed 255 octets according to RFC 1035 - * section 2.3.4. ICU checks for label size and domain size, but these errors - * are ignored. + * @private + */ +template +void encode_json(std::string_view view, out_iter out); + +/** + * @private + * This function is used to prune a fragment from a url, and returning the + * removed string if input has fragment. * - * @see https://url.spec.whatwg.org/#concept-domain-to-ascii + * @details prune_hash seeks the first '#' and returns everything after it + * as a string_view, and modifies (in place) the input so that it points at + * everything before the '#'. If no '#' is found, the input is left unchanged + * and std::nullopt is returned. * + * @attention The function is non-allocating and it does not throw. + * @returns Note that the returned string_view might be empty! */ -bool to_ascii(std::optional& out, std::string_view plain, - size_t first_percent); +ada_really_inline std::optional prune_hash( + std::string_view& input) noexcept; /** - * @see https://www.unicode.org/reports/tr46/#ToUnicode + * @private + * Defined by the URL specification, shorten a URLs paths. + * @see https://url.spec.whatwg.org/#shorten-a-urls-path + * @returns Returns true if path is shortened. */ -std::string to_unicode(std::string_view input); +ada_really_inline bool shorten_path(std::string& path, + ada::scheme::type type) noexcept; /** - * Checks if the input has tab or newline characters. - * - * @attention The has_tabs_or_newline function is a bottleneck and it is simple - * enough that compilers like GCC can 'autovectorize it'. + * @private + * Defined by the URL specification, shorten a URLs paths. + * @see https://url.spec.whatwg.org/#shorten-a-urls-path + * @returns Returns true if path is shortened. */ -ada_really_inline constexpr bool has_tabs_or_newline( - std::string_view user_input) noexcept; +ada_really_inline bool shorten_path(std::string_view& path, + ada::scheme::type type) noexcept; /** - * Checks if the input is a forbidden host code point. - * @see https://url.spec.whatwg.org/#forbidden-host-code-point + * @private + * + * Parse the path from the provided input and append to the existing + * (possibly empty) path. The input cannot contain tabs and spaces: it + * is the user's responsibility to check. + * + * The input is expected to be UTF-8. + * + * @see https://url.spec.whatwg.org/ */ -ada_really_inline constexpr bool is_forbidden_host_code_point( - const char c) noexcept; +ada_really_inline void parse_prepared_path(const std::string_view input, + ada::scheme::type type, + std::string& path); /** - * Checks if the input is a forbidden domain code point. - * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + * @private + * Remove and mutate all ASCII tab or newline characters from an input. */ -ada_really_inline constexpr bool contains_forbidden_domain_code_point( - char* input, size_t length) noexcept; +ada_really_inline void remove_ascii_tab_or_newline(std::string& input) noexcept; /** - * Checks if the input is a forbidden doamin code point. - * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + * @private + * Return the substring from input going from index pos to the end. + * This function cannot throw. */ -ada_really_inline constexpr bool is_forbidden_domain_code_point( - const char c) noexcept; +ada_really_inline std::string_view substring(std::string_view input, + size_t pos) noexcept; /** - * Checks if the input is alphanumeric, '+', '-' or '.' + * @private + * Returns true if the string_view points within the string. */ -ada_really_inline constexpr bool is_alnum_plus(const char c) noexcept; +bool overlaps(std::string_view input1, const std::string& input2) noexcept; /** - * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex - * digit. An ASCII upper hex digit is an ASCII digit or a code point in the - * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an - * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive. + * @private + * Return the substring from input going from index pos1 to the pos2 (non + * included). The length of the substring is pos2 - pos1. */ -ada_really_inline constexpr bool is_ascii_hex_digit(const char c) noexcept; +ada_really_inline std::string_view substring(const std::string& input, + size_t pos1, + size_t pos2) noexcept { +#if ADA_DEVELOPMENT_CHECKS + if (pos2 < pos1) { + std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")" + << std::endl; + abort(); + } +#endif + return std::string_view(input.data() + pos1, pos2 - pos1); +} /** - * Checks if the input is a C0 control or space character. - * - * @details A C0 control or space is a C0 control or U+0020 SPACE. - * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION - * SEPARATOR ONE, inclusive. + * @private + * Modify the string_view so that it has the new size pos, assuming that pos <= + * input.size(). This function cannot throw. */ -ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept; +ada_really_inline void resize(std::string_view& input, size_t pos) noexcept; /** - * Checks if the input is a ASCII tab or newline character. - * - * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR. + * @private + * Returns a host's delimiter location depending on the state of the instance, + * and whether a colon was found outside brackets. Used by the host parser. */ -ada_really_inline constexpr bool is_ascii_tab_or_newline(const char c) noexcept; +ada_really_inline std::pair get_host_delimiter_location( + const bool is_special, std::string_view& view) noexcept; /** - * @details A double-dot path segment must be ".." or an ASCII case-insensitive - * match for ".%2e", "%2e.", or "%2e%2e". + * @private + * Removes leading and trailing C0 control and whitespace characters from + * string. */ -ada_really_inline ada_constexpr bool is_double_dot_path_segment( - const std::string_view input) noexcept; +ada_really_inline void trim_c0_whitespace(std::string_view& input) noexcept; /** - * @details A single-dot path segment must be "." or an ASCII case-insensitive - * match for "%2e". + * @private + * @see + * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path */ -ada_really_inline constexpr bool is_single_dot_path_segment( - const std::string_view input) noexcept; +template +ada_really_inline void strip_trailing_spaces_from_opaque_path( + url_type& url) noexcept; /** - * @details ipv4 character might contain 0-9 or a-f character ranges. + * @private + * Reverse the order of the bytes. */ -ada_really_inline constexpr bool is_lowercase_hex(const char c) noexcept; +ada_really_inline uint64_t swap_bytes(uint64_t val) noexcept; /** - * @details Convert hex to binary. + * @private + * Reverse the order of the bytes but only if the system is big endian */ -unsigned constexpr convert_hex_to_binary(char c) noexcept; +ada_really_inline uint64_t swap_bytes_if_big_endian(uint64_t val) noexcept; /** - * first_percent should be = input.find('%') - * - * @todo It would be faster as noexcept maybe, but it could be unsafe since. - * @author Node.js - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245 - * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom + * @private + * Finds the delimiter of a view in authority state. */ -std::string percent_decode(const std::string_view input, size_t first_percent); +ada_really_inline size_t +find_authority_delimiter_special(std::string_view view) noexcept; /** - * Returns a percent-encoding string whether percent encoding was needed or not. - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + * @private + * Finds the delimiter of a view in authority state. */ -std::string percent_encode(const std::string_view input, - const uint8_t character_set[]); +ada_really_inline size_t +find_authority_delimiter(std::string_view view) noexcept; + /** - * Returns a percent-encoded string version of input, while starting the percent - * encoding at the provided index. - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + * @private */ -std::string percent_encode(const std::string_view input, - const uint8_t character_set[], size_t index); +template +inline void inner_concat(std::string& buffer, T t) { + buffer.append(t); +} + /** - * Returns true if percent encoding was needed, in which case, we store - * the percent-encoded content in 'out'. If the boolean 'append' is set to - * true, the content is appended to 'out'. - * If percent encoding is not needed, out is left unchanged. - * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + * @private */ -template -bool percent_encode(const std::string_view input, const uint8_t character_set[], - std::string& out); +template +inline void inner_concat(std::string& buffer, T t, Args... args) { + buffer.append(t); + return inner_concat(buffer, args...); +} + /** - * Returns the index at which percent encoding should start, or (equivalently), - * the length of the prefix that does not require percent encoding. + * Concatenate the arguments and return a string. + * @returns a string */ -ada_really_inline size_t percent_encode_index(const std::string_view input, - const uint8_t character_set[]); +template +std::string concat(Args... args) { + std::string answer; + inner_concat(answer, args...); + return answer; +} + /** - * Lowers the string in-place, assuming that the content is ASCII. - * Return true if the content was ASCII. + * @return Number of leading zeroes. */ -constexpr bool to_lower_ascii(char* input, size_t length) noexcept; -} // namespace ada::unicode +inline int leading_zeroes(uint32_t input_num) noexcept { +#if ADA_REGULAR_VISUAL_STUDIO + unsigned long leading_zero(0); + unsigned long in(input_num); + return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32; +#else + return __builtin_clz(input_num); +#endif // ADA_REGULAR_VISUAL_STUDIO +} -#endif // ADA_UNICODE_H -/* end file include/ada/unicode.h */ -/* begin file include/ada/url_base.h */ /** - * @file url_base.h - * @brief Declaration for the basic URL definitions + * Counts the number of decimal digits necessary to represent x. + * faster than std::to_string(x).size(). + * @return digit count */ -#ifndef ADA_URL_BASE_H -#define ADA_URL_BASE_H +inline int fast_digit_count(uint32_t x) noexcept { + auto int_log2 = [](uint32_t z) -> int { + return 31 - ada::helpers::leading_zeroes(z | 1); + }; + // Compiles to very few instructions. Note that the + // table is static and thus effectively a constant. + // We leave it inside the function because it is meaningless + // outside of it (this comes at no performance cost). + const static uint64_t table[] = { + 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, + 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, + 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, + 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, + 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, + 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, + 42949672960, 42949672960}; + return int((x + table[int_log2(x)]) >> 32); +} +} // namespace ada::helpers -/* begin file include/ada/url_components.h */ +#endif // ADA_HELPERS_H +/* end file include/ada/helpers.h */ +/* begin file include/ada/parser.h */ /** - * @file url_components.h - * @brief Declaration for the URL Components + * @file parser.h + * @brief Definitions for the parser. */ -#ifndef ADA_URL_COMPONENTS_H -#define ADA_URL_COMPONENTS_H - - -#include -#include - -namespace ada { +#ifndef ADA_PARSER_H +#define ADA_PARSER_H +/* begin file include/ada/expected.h */ /** - * @brief URL Component representations using offsets. - * - * @details We design the url_components struct so that it is as small - * and simple as possible. This version uses 32 bytes. - * - * This struct is used to extract components from a single 'href'. + * @file expected.h + * @brief Definitions for std::expected + * @private Excluded from docs through the doxygen file. */ -struct url_components { - constexpr static uint32_t omitted = uint32_t(-1); +/// +// expected - An implementation of std::expected with extensions +// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) +// +// Documentation available at http://tl.tartanllama.xyz/ +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to the +// public domain worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. If not, see +// . +/// - url_components() = default; - url_components(const url_components &u) = default; - url_components(url_components &&u) noexcept = default; - url_components &operator=(url_components &&u) noexcept = default; - url_components &operator=(const url_components &u) = default; - ~url_components() = default; +#ifndef TL_EXPECTED_HPP +#define TL_EXPECTED_HPP - /* - * By using 32-bit integers, we implicitly assume that the URL string - * cannot exceed 4 GB. - * - * https://user:pass@example.com:1234/foo/bar?baz#quux - * | | | | ^^^^| | | - * | | | | | | | `----- hash_start - * | | | | | | `--------- search_start - * | | | | | `----------------- pathname_start - * | | | | `--------------------- port - * | | | `----------------------- host_end - * | | `---------------------------------- host_start - * | `--------------------------------------- username_end - * `--------------------------------------------- protocol_end - */ - uint32_t protocol_end{0}; - /** - * Username end is not `omitted` by default to make username and password - * getters less costly to implement. - */ - uint32_t username_end{0}; - uint32_t host_start{0}; - uint32_t host_end{0}; - uint32_t port{omitted}; - uint32_t pathname_start{0}; - uint32_t search_start{omitted}; - uint32_t hash_start{omitted}; +#define TL_EXPECTED_VERSION_MAJOR 1 +#define TL_EXPECTED_VERSION_MINOR 0 +#define TL_EXPECTED_VERSION_PATCH 1 - /** - * Check the following conditions: - * protocol_end < username_end < ... < hash_start, - * expect when a value is omitted. It also computes - * a lower bound on the possible string length that may match these - * offsets. - * @return true if the offset values are - * consistent with a possible URL string - */ - bool check_offset_consistency() const noexcept; +#include +#include +#include +#include - /** - * @private - * Converts a url_components to JSON stringified version. - */ - std::string to_string() const; +#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#define TL_EXPECTED_EXCEPTIONS_ENABLED +#endif -}; // struct url_components +#if (defined(_MSC_VER) && _MSC_VER == 1900) +#define TL_EXPECTED_MSVC2015 +#define TL_EXPECTED_MSVC2015_CONSTEXPR +#else +#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr +#endif -} // namespace ada +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC49 #endif -/* end file include/ada/url_components.h */ -#include +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC54 +#endif -namespace ada { +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC55 +#endif -/** - * @brief Base class of URL implementations - * - * @details A url_base contains a few attributes: is_valid, has_opaque_path and - * type. All non-trivial implementation details are in derived classes such as - * ada::url and ada::url_aggregator. - * - * It is an abstract class that cannot be instantiated directly. - */ -struct url_base { - virtual ~url_base() = default; +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions - /** - * Used for returning the validity from the result of the URL parser. - */ - bool is_valid{true}; +#define TL_EXPECTED_NO_CONSTRR +// GCC < 5 doesn't support some standard C++11 type traits +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign - /** - * A URL has an opaque path if its path is a string. - */ - bool has_opaque_path{false}; +// This one will be different for GCC 5.7 if it's ever supported +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible - /** - * @private - */ - ada::scheme::type type{ada::scheme::type::NOT_SPECIAL}; +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks +// std::vector for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) +#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +namespace tl { +namespace detail { +template +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; +#ifdef _GLIBCXX_VECTOR +template +struct is_trivially_copy_constructible> : std::false_type {}; +#endif +} // namespace detail +} // namespace tl +#endif - /** - * A URL is special if its scheme is a special scheme. A URL is not special if - * its scheme is not a special scheme. - */ - [[nodiscard]] ada_really_inline bool is_special() const noexcept; +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#else +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#endif - /** - * The origin getter steps are to return the serialization of this’s URL’s - * origin. [HTML] - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#concept-url-origin - */ - [[nodiscard]] virtual std::string get_origin() const noexcept = 0; +#if __cplusplus > 201103L +#define TL_EXPECTED_CXX14 +#endif - /** - * Returns true if this URL has a valid domain as per RFC 1034 and - * corresponding specifications. Among other things, it requires - * that the domain string has fewer than 255 octets. - */ - [[nodiscard]] virtual bool has_valid_domain() const noexcept = 0; +#ifdef TL_EXPECTED_GCC49 +#define TL_EXPECTED_GCC49_CONSTEXPR +#else +#define TL_EXPECTED_GCC49_CONSTEXPR constexpr +#endif - /** - * @private - * - * Return the 'special port' if the URL is special and not 'file'. - * Returns 0 otherwise. - */ - [[nodiscard]] inline uint16_t get_special_port() const noexcept; +#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ + defined(TL_EXPECTED_GCC49)) +#define TL_EXPECTED_11_CONSTEXPR +#else +#define TL_EXPECTED_11_CONSTEXPR constexpr +#endif - /** - * @private - * - * Get the default port if the url's scheme has one, returns 0 otherwise. - */ - [[nodiscard]] ada_really_inline uint16_t scheme_default_port() const noexcept; +namespace tl { +template +class expected; - /** - * @private - * - * Parse a port (16-bit decimal digit) from the provided input. - * We assume that the input does not contain spaces or tabs - * within the ASCII digits. - * It returns how many bytes were consumed when a number is successfully - * parsed. - * @return On failure, it returns zero. - * @see https://url.spec.whatwg.org/#host-parsing - */ - virtual ada_really_inline size_t parse_port( - std::string_view view, bool check_trailing_content = false) noexcept = 0; +#ifndef TL_MONOSTATE_INPLACE_MUTEX +#define TL_MONOSTATE_INPLACE_MUTEX +class monostate {}; - /** - * Returns a JSON string representation of this URL. - */ - virtual std::string to_string() const = 0; +struct in_place_t { + explicit in_place_t() = default; +}; +static constexpr in_place_t in_place{}; +#endif - /** @private */ - virtual inline void clear_base_pathname() = 0; +template +class unexpected { + public: + static_assert(!std::is_same::value, "E must not be void"); - /** @private */ - virtual inline void clear_base_search() = 0; + unexpected() = delete; + constexpr explicit unexpected(const E &e) : m_val(e) {} - /** @private */ - virtual inline bool base_fragment_has_value() const = 0; + constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} - /** @private */ - virtual inline bool base_search_has_value() const = 0; + template ::value>::type * = nullptr> + constexpr explicit unexpected(Args &&...args) + : m_val(std::forward(args)...) {} + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(std::initializer_list l, Args &&...args) + : m_val(l, std::forward(args)...) {} -}; // url_base + constexpr const E &value() const & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } + constexpr const E &&value() const && { return std::move(m_val); } -} // namespace ada + private: + E m_val; +}; +#ifdef __cpp_deduction_guides +template +unexpected(E) -> unexpected; #endif -/* end file include/ada/url_base.h */ -#include -#include -#include -#include -#include -#include +template +constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() == rhs.value(); +} +template +constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() != rhs.value(); +} +template +constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() < rhs.value(); +} +template +constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() <= rhs.value(); +} +template +constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() > rhs.value(); +} +template +constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() >= rhs.value(); +} -namespace ada { +template +unexpected::type> make_unexpected(E &&e) { + return unexpected::type>(std::forward(e)); +} -/** - * @brief Generic URL struct reliant on std::string instantiation. - * - * @details To disambiguate from a valid URL string it can also be referred to - * as a URL record. A URL is a struct that represents a universal identifier. - * Unlike the url_aggregator, the ada::url represents the different components - * of a parsed URL as independent std::string instances. This makes the - * structure heavier and more reliant on memory allocations. When getting - * components from the parsed URL, a new std::string is typically constructed. - * - * @see https://url.spec.whatwg.org/#url-representation - */ -struct url : url_base { - url() = default; - url(const url &u) = default; - url(url &&u) noexcept = default; - url &operator=(url &&u) noexcept = default; - url &operator=(const url &u) = default; - ~url() = default; +struct unexpect_t { + unexpect_t() = default; +}; +static constexpr unexpect_t unexpect{}; - /** - * @private - * A URL’s username is an ASCII string identifying a username. It is initially - * the empty string. - */ - std::string username{}; +namespace detail { +template +[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward(e); +#else +#ifdef _MSC_VER + __assume(0); +#else + __builtin_unreachable(); +#endif +#endif +} - /** - * @private - * A URL’s password is an ASCII string identifying a password. It is initially - * the empty string. - */ - std::string password{}; +#ifndef TL_TRAITS_MUTEX +#define TL_TRAITS_MUTEX +// C++14-style aliases for brevity +template +using remove_const_t = typename std::remove_const::type; +template +using remove_reference_t = typename std::remove_reference::type; +template +using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; - /** - * @private - * A URL’s host is null or a host. It is initially null. - */ - std::optional host{}; +// std::conjunction from C++17 +template +struct conjunction : std::true_type {}; +template +struct conjunction : B {}; +template +struct conjunction + : std::conditional, B>::type {}; - /** - * @private - * A URL’s port is either null or a 16-bit unsigned integer that identifies a - * networking port. It is initially null. - */ - std::optional port{}; +#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#endif - /** - * @private - * A URL’s path is either an ASCII string or a list of zero or more ASCII - * strings, usually identifying a location. - */ - std::string path{}; +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +template +struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; - /** - * @private - * A URL’s query is either null or an ASCII string. It is initially null. - */ - std::optional query{}; +template +struct is_const_or_const_ref : std::false_type {}; +template +struct is_const_or_const_ref : std::true_type {}; +template +struct is_const_or_const_ref : std::true_type {}; +#endif - /** - * @private - * A URL’s fragment is either null or an ASCII string that can be used for - * further processing on the resource the URL’s other components identify. It - * is initially null. - */ - std::optional fragment{}; +// std::invoke from C++17 +// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround +template < + typename Fn, typename... Args, +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND + typename = enable_if_t::value && + is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, int = 0> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} - /** @private */ - inline void update_unencoded_base_hash(std::string_view input); - /** @private */ - inline void update_base_hostname(std::string_view input); - /** @private */ - inline void update_base_search(std::string_view input); - /** @private */ - inline void update_base_search(std::string_view input, - const uint8_t query_percent_encode_set[]); - /** @private */ - inline void update_base_search(std::optional input); - /** @private */ - inline void update_base_pathname(const std::string_view input); - /** @private */ - inline void update_base_username(const std::string_view input); - /** @private */ - inline void update_base_password(const std::string_view input); - /** @private */ - inline void update_base_port(std::optional input); - /** @private */ - inline void clear_base_pathname() override; - /** @private */ - inline void clear_base_search() override; - /** @private */ - inline bool base_fragment_has_value() const override; - /** @private */ - inline bool base_search_has_value() const override; - /** @private set this URL's type to file */ - inline void set_protocol_as_file(); - /** @return true if it has an host but it is the empty string */ - [[nodiscard]] inline bool has_empty_hostname() const noexcept; - /** @return true if it has a host (included an empty host) */ - [[nodiscard]] inline bool has_hostname() const noexcept; - [[nodiscard]] bool has_valid_domain() const noexcept override; +template >::value>> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} - /** - * @private - * - * Parse the path from the provided input. - * Return true on success. Control characters not - * trimmed from the ends (they should have - * been removed if needed). - * - * The input is expected to be UTF-8. - * - * @see https://url.spec.whatwg.org/ - */ - ada_really_inline void parse_path(const std::string_view input); +// std::invoke_result from C++17 +template +struct invoke_result_impl; - /** - * Set the scheme for this URL. The provided scheme should be a valid - * scheme string, be lower-cased, not contain spaces or tabs. It should - * have no spurious trailing or leading content. - */ - inline void set_scheme(std::string &&new_scheme) noexcept; +template +struct invoke_result_impl< + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); +}; - /** - * @private - * - * Take the scheme from another URL. The scheme string is moved from the - * provided url. - */ - inline void copy_scheme(ada::url &&u) noexcept; +template +using invoke_result = invoke_result_impl; - /** - * Returns a JSON string representation of this URL. - */ - std::string to_string() const override; +template +using invoke_result_t = typename invoke_result::type; - /** - * @see https://url.spec.whatwg.org/#dom-url-href - * @see https://url.spec.whatwg.org/#concept-url-serializer - */ - [[nodiscard]] ada_really_inline std::string get_href() const noexcept; +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template +struct is_swappable : std::true_type {}; - /** - * The origin getter steps are to return the serialization of this’s URL’s - * origin. [HTML] - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#concept-url-origin - */ - [[nodiscard]] std::string get_origin() const noexcept override; +template +struct is_nothrow_swappable : std::true_type {}; +#else +// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept +namespace swap_adl_tests { +// if swap ADL finds this then it would call std::swap otherwise (same +// signature) +struct tag {}; - /** - * The protocol getter steps are to return this’s URL’s scheme, followed by - * U+003A (:). - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-protocol - */ - [[nodiscard]] std::string get_protocol() const noexcept; +template +tag swap(T &, T &); +template +tag swap(T (&a)[N], T (&b)[N]); - /** - * Return url’s host, serialized, followed by U+003A (:) and url’s port, - * serialized. - * When there is no host, this function returns the empty string. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-host - */ - [[nodiscard]] std::string get_host() const noexcept; +// helper functions to test if an unqualified swap is possible, and if it +// becomes std::swap +template +std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); - /** - * Return this’s URL’s host, serialized. - * When there is no host, this function returns the empty string. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-hostname - */ - [[nodiscard]] std::string get_hostname() const noexcept; +template +std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); - /** - * The pathname getter steps are to return the result of URL path serializing - * this’s URL. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-pathname - */ - [[nodiscard]] const std::string_view get_pathname() const noexcept; +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; - /** - * Compute the pathname length in bytes witout instantiating a view or a - * string. - * @return size of the pathname in bytes - * @see https://url.spec.whatwg.org/#dom-url-pathname - */ - ada_really_inline size_t get_pathname_length() const noexcept; +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; - /** - * Return U+003F (?), followed by this’s URL’s query. - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#dom-url-search - */ - [[nodiscard]] std::string get_search() const noexcept; +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests - /** - * The username getter steps are to return this’s URL’s username. - * @return a constant reference to the underlying string. - * @see https://url.spec.whatwg.org/#dom-url-username - */ - [[nodiscard]] const std::string &get_username() const noexcept; +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; - /** - * @return Returns true on successful operation. - * @see https://url.spec.whatwg.org/#dom-url-username - */ - bool set_username(const std::string_view input); +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || + is_swappable::value)> {}; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-password - */ - bool set_password(const std::string_view input); +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; +#endif +#endif - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-port - */ - bool set_port(const std::string_view input); +// Trait for checking if a type is a tl::expected +template +struct is_expected_impl : std::false_type {}; +template +struct is_expected_impl> : std::true_type {}; +template +using is_expected = is_expected_impl>; - /** - * This function always succeeds. - * @see https://url.spec.whatwg.org/#dom-url-hash - */ - void set_hash(const std::string_view input); +template +using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value && + !std::is_same, detail::decay_t>::value>; - /** - * This function always succeeds. - * @see https://url.spec.whatwg.org/#dom-url-search - */ - void set_search(const std::string_view input); +template +using expected_enable_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-search - */ - bool set_pathname(const std::string_view input); +template +using is_void_or = conditional_t::value, std::true_type, U>; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-host - */ - bool set_host(const std::string_view input); +template +using is_copy_constructible_or_void = + is_void_or>; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-hostname - */ - bool set_hostname(const std::string_view input); +template +using is_move_constructible_or_void = + is_void_or>; - /** - * @return Returns true on success. - * @see https://url.spec.whatwg.org/#dom-url-protocol - */ - bool set_protocol(const std::string_view input); +template +using is_copy_assignable_or_void = is_void_or>; - /** - * @see https://url.spec.whatwg.org/#dom-url-href - */ - bool set_href(const std::string_view input); +template +using is_move_assignable_or_void = is_void_or>; - /** - * @private - * - * Sets the host or hostname according to override condition. - * Return true on success. - * @see https://url.spec.whatwg.org/#hostname-state - */ - template - bool set_host_or_hostname(std::string_view input); +} // namespace detail - /** - * The password getter steps are to return this’s URL’s password. - * @return a constant reference to the underlying string. - * @see https://url.spec.whatwg.org/#dom-url-password - */ - [[nodiscard]] const std::string &get_password() const noexcept; +namespace detail { +struct no_init_t {}; +static constexpr no_init_t no_init{}; - /** - * Return this’s URL’s port, serialized. - * @return a newly constructed string representing the port. - * @see https://url.spec.whatwg.org/#dom-url-port - */ - [[nodiscard]] std::string get_port() const noexcept; +// Implements the storage of the values, and ensures that the destructor is +// trivial if it can be. +// +// This specialization is for where neither `T` or `E` is trivially +// destructible, so the destructors must be called on destruction of the +// `expected` +template ::value, + bool = std::is_trivially_destructible::value> +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} - /** - * Return U+0023 (#), followed by this’s URL’s fragment. - * @return a newly constructed string representing the hash. - * @see https://url.spec.whatwg.org/#dom-url-hash - */ - [[nodiscard]] std::string get_hash() const noexcept; + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} - /** - * A URL includes credentials if its username or password is not the empty - * string. - */ - [[nodiscard]] ada_really_inline bool includes_credentials() const noexcept; + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} - /** - * @private - * - * A URL cannot have a username/password/port if its host is null or the empty - * string, or its scheme is "file". - */ - [[nodiscard]] inline bool cannot_have_credentials_or_port() const; + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - /** @private */ - ada_really_inline size_t - parse_port(std::string_view view, - bool check_trailing_content = false) noexcept override; + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } else { + m_unexpect.~unexpected(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; - /** - * @private - * - * Take the scheme from another URL. The scheme string is copied from the - * provided url. - */ - inline void copy_scheme(const ada::url &u); +// This specialization is for when both `T` and `E` are trivially-destructible, +// so the destructor of the `expected` can be trivial. +template +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} - /** - * @private - * - * Parse the host from the provided input. We assume that - * the input does not contain spaces or tabs. Control - * characters and spaces are not trimmed (they should have - * been removed if needed). - * Return true on success. - * @see https://url.spec.whatwg.org/#host-parsing - */ - [[nodiscard]] ada_really_inline bool parse_host(std::string_view input); + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} - /** @private */ - template - [[nodiscard]] ada_really_inline bool parse_scheme( - const std::string_view input); + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} - /** - * Useful for implementing efficient serialization for the URL. - * - * https://user:pass@example.com:1234/foo/bar?baz#quux - * | | | | ^^^^| | | - * | | | | | | | `----- hash_start - * | | | | | | `--------- search_start - * | | | | | `----------------- pathname_start - * | | | | `--------------------- port - * | | | `----------------------- host_end - * | | `---------------------------------- host_start - * | `--------------------------------------- username_end - * `--------------------------------------------- protocol_end - * - * Inspired after servo/url - * - * @return a newly constructed component. - * - * @see - * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31 - */ - [[nodiscard]] ada_really_inline ada::url_components get_components() - const noexcept; + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - private: - /** - * @private - * - * Return true on success. - * @see https://url.spec.whatwg.org/#concept-ipv4-parser - */ - [[nodiscard]] bool parse_ipv4(std::string_view input); + ~expected_storage_base() = default; + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; - /** - * @private - * - * Return true on success. - * @see https://url.spec.whatwg.org/#concept-ipv6-parser - */ - [[nodiscard]] bool parse_ipv6(std::string_view input); +// T is trivial, E is not. +template +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) + : m_no_init(), m_has_val(false) {} - /** - * @private - * - * Return true on success. - * @see https://url.spec.whatwg.org/#concept-opaque-host-parser - */ - [[nodiscard]] bool parse_opaque_host(std::string_view input); + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} - /** - * @private - * - * A URL’s scheme is an ASCII string that identifies the type of URL and can - * be used to dispatch a URL for further processing after parsing. It is - * initially the empty string. We only set non_special_scheme when the scheme - * is non-special, otherwise we avoid constructing string. - * - * Special schemes are stored in ada::scheme::details::is_special_list so we - * typically do not need to store them in each url instance. - */ - std::string non_special_scheme{}; + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} -}; // struct url + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} -inline std::ostream &operator<<(std::ostream &out, const ada::url &u); -} // namespace ada + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } -#endif // ADA_URL_H -/* end file include/ada/url.h */ -/* begin file include/ada/state.h */ -/** - * @file state.h - * @brief Definitions for the states of the URL state machine. - */ -#ifndef ADA_STATE_H -#define ADA_STATE_H + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// E is trivial, T is not. +template +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} -#include + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} -namespace ada { + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} -/** - * @see https://url.spec.whatwg.org/#url-parsing - */ -enum class state { - AUTHORITY, - SCHEME_START, - SCHEME, - HOST, - NO_SCHEME, - FRAGMENT, - RELATIVE_SCHEME, - RELATIVE_SLASH, - FILE, - FILE_HOST, - FILE_SLASH, - PATH_OR_AUTHORITY, - SPECIAL_AUTHORITY_IGNORE_SLASHES, - SPECIAL_AUTHORITY_SLASHES, - SPECIAL_RELATIVE_OR_AUTHORITY, - QUERY, - PATH, - PATH_START, - OPAQUE_PATH, - PORT, + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; }; -/** - * Stringify a URL state machine state. - */ -ada_warn_unused std::string to_string(ada::state s); - -} // namespace ada +// `T` is `void`, `E` is trivially-destructible +template +struct expected_storage_base { +#if __GNUC__ <= 5 +// no constexpr for GCC 4/5 bug +#else + TL_EXPECTED_MSVC2015_CONSTEXPR +#endif + expected_storage_base() : m_has_val(true) {} -#endif // ADA_STATE_H -/* end file include/ada/state.h */ + constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} -#include -#include + constexpr expected_storage_base(in_place_t) : m_has_val(true) {} -/** - * @private - * @namespace ada::helpers - * @brief Includes the definitions for helper functions - */ -namespace ada::helpers { + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} -/** - * @private - */ -template -void encode_json(std::string_view view, out_iter out); + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} -/** - * @private - * This function is used to prune a fragment from a url, and returning the - * removed string if input has fragment. - * - * @details prune_fragment seeks the first '#' and returns everything after it - * as a string_view, and modifies (in place) the input so that it points at - * everything before the '#'. If no '#' is found, the input is left unchanged - * and std::nullopt is returned. - * - * @attention The function is non-allocating and it does not throw. - * @returns Note that the returned string_view might be empty! - */ -ada_really_inline std::optional prune_fragment( - std::string_view& input) noexcept; + ~expected_storage_base() = default; + struct dummy {}; + union { + unexpected m_unexpect; + dummy m_val; + }; + bool m_has_val; +}; -/** - * @private - * Defined by the URL specification, shorten a URLs paths. - * @see https://url.spec.whatwg.org/#shorten-a-urls-path - * @returns Returns true if path is shortened. - */ -ada_really_inline bool shorten_path(std::string& path, - ada::scheme::type type) noexcept; - -/** - * @private - * Defined by the URL specification, shorten a URLs paths. - * @see https://url.spec.whatwg.org/#shorten-a-urls-path - * @returns Returns true if path is shortened. - */ -ada_really_inline bool shorten_path(std::string_view& path, - ada::scheme::type type) noexcept; - -/** - * @private - * - * Parse the path from the provided input and append to the existing - * (possibly empty) path. The input cannot contain tabs and spaces: it - * is the user's responsibility to check. - * - * The input is expected to be UTF-8. - * - * @see https://url.spec.whatwg.org/ - */ -ada_really_inline void parse_prepared_path(const std::string_view input, - ada::scheme::type type, - std::string& path); +// `T` is `void`, `E` is not trivially-destructible +template +struct expected_storage_base { + constexpr expected_storage_base() : m_dummy(), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {} -/** - * @private - * Remove and mutate all ASCII tab or newline characters from an input. - */ -ada_really_inline void remove_ascii_tab_or_newline(std::string& input) noexcept; + constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {} -/** - * @private - * Return the substring from input going from index pos to the end. If pos > - * input.size(), it returns an empty string_view. This function cannot throw. - */ -ada_really_inline std::string_view substring(std::string_view input, - size_t pos) noexcept; + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} -/** - * @private - * Returns true if the string_view points within the string. - */ -bool overlaps(std::string_view input1, const std::string& input2) noexcept; + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} -/** - * @private - * Return the substring from input going from index pos1 to the pos2 (non - * included). The length of the substring is pos2 - pos1. - */ -ada_really_inline std::string_view substring(const std::string& input, - size_t pos1, - size_t pos2) noexcept { -#if ADA_DEVELOPMENT_CHECKS - if (pos2 < pos1) { - std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")" - << std::endl; - abort(); + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } } -#endif - return std::string_view(input.data() + pos1, pos2 - pos1); -} - -/** - * @private - * Modify the string_view so that it has the new size pos, assuming that pos <= - * input.size(). This function cannot throw. - */ -ada_really_inline void resize(std::string_view& input, size_t pos) noexcept; - -/** - * @private - * Returns a host's delimiter location depending on the state of the instance, - * and whether a colon was found outside brackets. Used by the host parser. - */ -ada_really_inline std::pair get_host_delimiter_location( - const bool is_special, std::string_view& view) noexcept; -/** - * @private - * Removes leading and trailing C0 control and whitespace characters from - * string. - */ -ada_really_inline void trim_c0_whitespace(std::string_view& input) noexcept; + union { + unexpected m_unexpect; + char m_dummy; + }; + bool m_has_val; +}; -/** - * @private - * @see - * https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path - */ -template -ada_really_inline void strip_trailing_spaces_from_opaque_path( - url_type& url) noexcept; +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; -/** - * @private - * Reverse the order of the bytes. - */ -ada_really_inline uint64_t swap_bytes(uint64_t val) noexcept; + template + void construct(Args &&...args) noexcept { + new (std::addressof(this->m_val)) T(std::forward(args)...); + this->m_has_val = true; + } -/** - * @private - * Reverse the order of the bytes but only if the system is big endian - */ -ada_really_inline uint64_t swap_bytes_if_big_endian(uint64_t val) noexcept; + template + void construct_with(Rhs &&rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward(rhs).get()); + this->m_has_val = true; + } -/** - * @private - * Finds the delimiter of a view in authority state. - */ -ada_really_inline size_t -find_authority_delimiter_special(std::string_view view) noexcept; + template + void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } -/** - * @private - * Finds the delimiter of a view in authority state. - */ -ada_really_inline size_t -find_authority_delimiter(std::string_view view) noexcept; +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED -/** - * @private - */ -template -inline void inner_concat(std::string& buffer, T t) { - buffer.append(t); -} + // These assign overloads ensure that the most efficient assignment + // implementation is used while maintaining the strong exception guarantee. + // The problematic case is where rhs has a value, but *this does not. + // + // This overload handles the case where we can just copy-construct `T` + // directly into place without throwing. + template ::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } -/** - * @private - */ -template -inline void inner_concat(std::string& buffer, T t, Args... args) { - buffer.append(t); - return inner_concat(buffer, args...); -} + // This overload handles the case where we can attempt to create a copy of + // `T`, then no-throw move it into place if the copy was successful. + template ::value && + std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + T tmp = rhs.get(); + geterr().~unexpected(); + construct(std::move(tmp)); + } else { + assign_common(rhs); + } + } -/** - * Concatenate the arguments and return a string. - * @returns a string - */ -template -std::string concat(Args... args) { - std::string answer; - inner_concat(answer, args...); - return answer; -} + // This overload is the worst-case, where we have to move-construct the + // unexpected value into temporary storage, then try to copy the T into place. + // If the construction succeeds, then everything is fine, but if it throws, + // then we move the old unexpected value back into place before rethrowing the + // exception. + template ::value && + !std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); -/** - * @return Number of leading zeroes. - */ -inline int leading_zeroes(uint32_t input_num) noexcept { -#if ADA_REGULAR_VISUAL_STUDIO - unsigned long leading_zero(0); - unsigned long in(input_num); - return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32; +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(rhs.get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } #else - return __builtin_clz(input_num); -#endif // ADA_REGULAR_VISUAL_STUDIO -} - -/** - * Counts the number of decimal digits necessary to represent x. - * faster than std::to_string(x).size(). - * @return digit count - */ -inline int fast_digit_count(uint32_t x) noexcept { - auto int_log2 = [](uint32_t z) -> int { - return 31 - ada::helpers::leading_zeroes(z | 1); - }; - // Compiles to very few instructions. Note that the - // table is static and thus effectively a constant. - // We leave it inside the function because it is meaningless - // outside of it (this comes at no performance cost). - const static uint64_t table[] = { - 4294967296, 8589934582, 8589934582, 8589934582, 12884901788, - 12884901788, 12884901788, 17179868184, 17179868184, 17179868184, - 21474826480, 21474826480, 21474826480, 21474826480, 25769703776, - 25769703776, 25769703776, 30063771072, 30063771072, 30063771072, - 34349738368, 34349738368, 34349738368, 34349738368, 38554705664, - 38554705664, 38554705664, 41949672960, 41949672960, 41949672960, - 42949672960, 42949672960}; - return int((x + table[int_log2(x)]) >> 32); -} -} // namespace ada::helpers - -#endif // ADA_HELPERS_H -/* end file include/ada/helpers.h */ -/* begin file include/ada/parser.h */ -/** - * @file parser.h - * @brief Definitions for the parser. - */ -#ifndef ADA_PARSER_H -#define ADA_PARSER_H - -/* begin file include/ada/expected.h */ -/** - * @file expected.h - * @brief Definitions for std::expected - * @private Excluded from docs through the doxygen file. - */ -/// -// expected - An implementation of std::expected with extensions -// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) -// -// Documentation available at http://tl.tartanllama.xyz/ -// -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to the -// public domain worldwide. This software is distributed without any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. If not, see -// . -/// - -#ifndef TL_EXPECTED_HPP -#define TL_EXPECTED_HPP - -#define TL_EXPECTED_VERSION_MAJOR 1 -#define TL_EXPECTED_VERSION_MINOR 0 -#define TL_EXPECTED_VERSION_PATCH 1 - -#include -#include -#include -#include - -#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) -#define TL_EXPECTED_EXCEPTIONS_ENABLED -#endif - -#if (defined(_MSC_VER) && _MSC_VER == 1900) -#define TL_EXPECTED_MSVC2015 -#define TL_EXPECTED_MSVC2015_CONSTEXPR -#else -#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr -#endif - -#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ - !defined(__clang__)) -#define TL_EXPECTED_GCC49 + construct(rhs.get()); #endif + } else { + assign_common(rhs); + } + } -#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ - !defined(__clang__)) -#define TL_EXPECTED_GCC54 -#endif + // These overloads do the same as above, but for rvalues + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } -#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ - !defined(__clang__)) -#define TL_EXPECTED_GCC55 + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(std::move(rhs).get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(std::move(rhs).get()); #endif + } else { + assign_common(std::move(rhs)); + } + } -#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ - !defined(__clang__)) -// GCC < 5 doesn't support overloading on const&& for member functions +#else -#define TL_EXPECTED_NO_CONSTRR -// GCC < 5 doesn't support some standard C++11 type traits -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - std::has_trivial_copy_constructor -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::has_trivial_copy_assign + // If exceptions are disabled then we can just copy-construct + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } -// This one will be different for GCC 5.7 if it's ever supported -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ - std::is_trivially_destructible + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(rhs); + } + } -// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks -// std::vector for non-copyable types -#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) -#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX -#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX -namespace tl { -namespace detail { -template -struct is_trivially_copy_constructible - : std::is_trivially_copy_constructible {}; -#ifdef _GLIBCXX_VECTOR -template -struct is_trivially_copy_constructible> : std::false_type {}; -#endif -} // namespace detail -} // namespace tl #endif -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - tl::detail::is_trivially_copy_constructible -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::is_trivially_copy_assignable -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ - std::is_trivially_destructible -#else -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - std::is_trivially_copy_constructible -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::is_trivially_copy_assignable -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ - std::is_trivially_destructible -#endif + // The common part of move/copy assigning + template + void assign_common(Rhs &&rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward(rhs).geterr(); + } + } + } -#if __cplusplus > 201103L -#define TL_EXPECTED_CXX14 -#endif + bool has_value() const { return this->m_has_val; } -#ifdef TL_EXPECTED_GCC49 -#define TL_EXPECTED_GCC49_CONSTEXPR -#else -#define TL_EXPECTED_GCC49_CONSTEXPR constexpr + TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } + constexpr const T &get() const & { return this->m_val; } + TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const T &&get() const && { return std::move(this->m_val); } #endif -#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ - defined(TL_EXPECTED_GCC49)) -#define TL_EXPECTED_11_CONSTEXPR -#else -#define TL_EXPECTED_11_CONSTEXPR constexpr + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } #endif -namespace tl { -template -class expected; - -#ifndef TL_MONOSTATE_INPLACE_MUTEX -#define TL_MONOSTATE_INPLACE_MUTEX -class monostate {}; - -struct in_place_t { - explicit in_place_t() = default; + TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } }; -static constexpr in_place_t in_place{}; -#endif +// This base class provides some handy member functions which can be used in +// further derived classes template -class unexpected { - public: - static_assert(!std::is_same::value, "E must not be void"); - - unexpected() = delete; - constexpr explicit unexpected(const E &e) : m_val(e) {} +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; - constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} + template + void construct() noexcept { + this->m_has_val = true; + } - template ::value>::type * = nullptr> - constexpr explicit unexpected(Args &&...args) - : m_val(std::forward(args)...) {} - template < - class U, class... Args, - typename std::enable_if &, Args &&...>::value>::type * = nullptr> - constexpr explicit unexpected(std::initializer_list l, Args &&...args) - : m_val(l, std::forward(args)...) {} + // This function doesn't use its argument, but needs it so that code in + // levels above this can work independently of whether T is void + template + void construct_with(Rhs &&) noexcept { + this->m_has_val = true; + } - constexpr const E &value() const & { return m_val; } - TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } - TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } - constexpr const E &&value() const && { return std::move(m_val); } + template + void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } - private: - E m_val; -}; + template + void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected(); + construct(); + } else { + geterr() = std::forward(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward(rhs).geterr()); + } + } + } -#ifdef __cpp_deduction_guides -template -unexpected(E) -> unexpected; + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } #endif -template -constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() == rhs.value(); -} -template -constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() != rhs.value(); -} -template -constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() < rhs.value(); -} -template -constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() <= rhs.value(); -} -template -constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() > rhs.value(); -} -template -constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() >= rhs.value(); -} + TL_EXPECTED_11_CONSTEXPR void destroy_val() { + // no-op + } +}; -template -unexpected::type> make_unexpected(E &&e) { - return unexpected::type>(std::forward(e)); -} +// This class manages conditionally having a trivial copy constructor +// This specialization is for when T and E are trivially copy constructible +template :: + value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; +}; -struct unexpect_t { - unexpect_t() = default; +// This specialization is for when T or E are not trivially copy constructible +template +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; + + expected_copy_base() = default; + expected_copy_base(const expected_copy_base &rhs) + : expected_operations_base(no_init) { + if (rhs.has_value()) { + this->construct_with(rhs); + } else { + this->construct_error(rhs.geterr()); + } + } + + expected_copy_base(expected_copy_base &&rhs) = default; + expected_copy_base &operator=(const expected_copy_base &rhs) = default; + expected_copy_base &operator=(expected_copy_base &&rhs) = default; }; -static constexpr unexpect_t unexpect{}; -namespace detail { -template -[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - throw std::forward(e); -#else -#ifdef _MSC_VER - __assume(0); +// This class manages conditionally having a trivial move constructor +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_constructible. We +// have to make do with a non-trivial move constructor even if T is trivially +// move constructible +#ifndef TL_EXPECTED_GCC49 +template >::value + &&std::is_trivially_move_constructible::value> +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; +}; #else - __builtin_unreachable(); -#endif +template +struct expected_move_base; #endif -} +template +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; -#ifndef TL_TRAITS_MUTEX -#define TL_TRAITS_MUTEX -// C++14-style aliases for brevity -template -using remove_const_t = typename std::remove_const::type; -template -using remove_reference_t = typename std::remove_reference::type; -template -using decay_t = typename std::decay::type; -template -using enable_if_t = typename std::enable_if::type; -template -using conditional_t = typename std::conditional::type; + expected_move_base() = default; + expected_move_base(const expected_move_base &rhs) = default; -// std::conjunction from C++17 -template -struct conjunction : std::true_type {}; -template -struct conjunction : B {}; -template -struct conjunction - : std::conditional, B>::type {}; + expected_move_base(expected_move_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value) + : expected_copy_base(no_init) { + if (rhs.has_value()) { + this->construct_with(std::move(rhs)); + } else { + this->construct_error(std::move(rhs.geterr())); + } + } + expected_move_base &operator=(const expected_move_base &rhs) = default; + expected_move_base &operator=(expected_move_base &&rhs) = default; +}; -#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L -#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND -#endif +// This class manages conditionally having a trivial copy assignment operator +template >::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value> +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; +}; -// In C++11 mode, there's an issue in libc++'s std::mem_fn -// which results in a hard-error when using it in a noexcept expression -// in some cases. This is a check to workaround the common failing case. -#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND -template -struct is_pointer_to_non_const_member_func : std::false_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; +template +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; -template -struct is_const_or_const_ref : std::false_type {}; -template -struct is_const_or_const_ref : std::true_type {}; -template -struct is_const_or_const_ref : std::true_type {}; -#endif + expected_copy_assign_base() = default; + expected_copy_assign_base(const expected_copy_assign_base &rhs) = default; -// std::invoke from C++17 -// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround -template < - typename Fn, typename... Args, -#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND - typename = enable_if_t::value && - is_const_or_const_ref::value)>, -#endif - typename = enable_if_t>::value>, int = 0> -constexpr auto invoke(Fn &&f, Args &&...args) noexcept( - noexcept(std::mem_fn(f)(std::forward(args)...))) - -> decltype(std::mem_fn(f)(std::forward(args)...)) { - return std::mem_fn(f)(std::forward(args)...); -} + expected_copy_assign_base(expected_copy_assign_base &&rhs) = default; + expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) { + this->assign(rhs); + return *this; + } + expected_copy_assign_base &operator=(expected_copy_assign_base &&rhs) = + default; +}; -template >::value>> -constexpr auto invoke(Fn &&f, Args &&...args) noexcept( - noexcept(std::forward(f)(std::forward(args)...))) - -> decltype(std::forward(f)(std::forward(args)...)) { - return std::forward(f)(std::forward(args)...); -} - -// std::invoke_result from C++17 -template -struct invoke_result_impl; - -template -struct invoke_result_impl< - F, - decltype(detail::invoke(std::declval(), std::declval()...), void()), - Us...> { - using type = - decltype(detail::invoke(std::declval(), std::declval()...)); +// This class manages conditionally having a trivial move assignment operator +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_assignable. We have +// to make do with a non-trivial move assignment operator even if T is trivially +// move assignable +#ifndef TL_EXPECTED_GCC49 +template , + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>:: + value &&std::is_trivially_destructible::value + &&std::is_trivially_move_constructible::value + &&std::is_trivially_move_assignable::value> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; }; +#else +template +struct expected_move_assign_base; +#endif -template -using invoke_result = invoke_result_impl; - -template -using invoke_result_t = typename invoke_result::type; +template +struct expected_move_assign_base + : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; -#if defined(_MSC_VER) && _MSC_VER <= 1900 -// TODO make a version which works with MSVC 2015 -template -struct is_swappable : std::true_type {}; + expected_move_assign_base() = default; + expected_move_assign_base(const expected_move_assign_base &rhs) = default; -template -struct is_nothrow_swappable : std::true_type {}; -#else -// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept -namespace swap_adl_tests { -// if swap ADL finds this then it would call std::swap otherwise (same -// signature) -struct tag {}; + expected_move_assign_base(expected_move_assign_base &&rhs) = default; -template -tag swap(T &, T &); -template -tag swap(T (&a)[N], T (&b)[N]); + expected_move_assign_base &operator=(const expected_move_assign_base &rhs) = + default; -// helper functions to test if an unqualified swap is possible, and if it -// becomes std::swap -template -std::false_type can_swap(...) noexcept(false); -template (), std::declval()))> -std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), - std::declval()))); + expected_move_assign_base & + operator=(expected_move_assign_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value + &&std::is_nothrow_move_assignable::value) { + this->assign(std::move(rhs)); + return *this; + } +}; -template -std::false_type uses_std(...); -template -std::is_same(), std::declval())), tag> -uses_std(int); +// expected_delete_ctor_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible +template ::value && + std::is_copy_constructible::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value)> +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = + default; + expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = + default; +}; -template -struct is_std_swap_noexcept - : std::integral_constant::value && - std::is_nothrow_move_assignable::value> {}; +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = + default; + expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = + default; +}; -template -struct is_std_swap_noexcept : is_std_swap_noexcept {}; +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = + default; + expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = + default; +}; -template -struct is_adl_swap_noexcept - : std::integral_constant(0))> {}; -} // namespace swap_adl_tests +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = + default; + expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = + default; +}; -template -struct is_swappable - : std::integral_constant< - bool, - decltype(detail::swap_adl_tests::can_swap(0))::value && - (!decltype(detail::swap_adl_tests::uses_std(0))::value || - (std::is_move_assignable::value && - std::is_move_constructible::value))> {}; +// expected_delete_assign_base will conditionally delete copy and move +// constructors depending on whether T and E are copy/move constructible + +// assignable +template ::value && + std::is_copy_constructible::value && + is_copy_assignable_or_void::value && + std::is_copy_assignable::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value && + is_move_assignable_or_void::value && + std::is_move_assignable::value)> +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base &operator=(const expected_delete_assign_base &) = + default; + expected_delete_assign_base &operator=( + expected_delete_assign_base &&) noexcept = default; +}; -template -struct is_swappable - : std::integral_constant< - bool, - decltype(detail::swap_adl_tests::can_swap(0))::value && - (!decltype(detail::swap_adl_tests::uses_std( - 0))::value || - is_swappable::value)> {}; +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base &operator=(const expected_delete_assign_base &) = + default; + expected_delete_assign_base &operator=( + expected_delete_assign_base &&) noexcept = delete; +}; -template -struct is_nothrow_swappable - : std::integral_constant< - bool, - is_swappable::value && - ((decltype(detail::swap_adl_tests::uses_std(0))::value && - detail::swap_adl_tests::is_std_swap_noexcept::value) || - (!decltype(detail::swap_adl_tests::uses_std(0))::value && - detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; -#endif -#endif +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base &operator=(const expected_delete_assign_base &) = + delete; + expected_delete_assign_base &operator=( + expected_delete_assign_base &&) noexcept = default; +}; -// Trait for checking if a type is a tl::expected -template -struct is_expected_impl : std::false_type {}; template -struct is_expected_impl> : std::true_type {}; -template -using is_expected = is_expected_impl>; +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base &operator=(const expected_delete_assign_base &) = + delete; + expected_delete_assign_base &operator=( + expected_delete_assign_base &&) noexcept = delete; +}; -template -using expected_enable_forward_value = detail::enable_if_t< - std::is_constructible::value && - !std::is_same, in_place_t>::value && - !std::is_same, detail::decay_t>::value && - !std::is_same, detail::decay_t>::value>; +// This is needed to be able to construct the expected_default_ctor_base which +// follows, while still conditionally deleting the default constructor. +struct default_constructor_tag { + explicit constexpr default_constructor_tag() = default; +}; -template -using expected_enable_from_other = detail::enable_if_t< - std::is_constructible::value && - std::is_constructible::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value>; - -template -using is_void_or = conditional_t::value, std::true_type, U>; - -template -using is_copy_constructible_or_void = - is_void_or>; - -template -using is_move_constructible_or_void = - is_void_or>; +// expected_default_ctor_base will ensure that expected has a deleted default +// consturctor if T is not default constructible. +// This specialization is for when T is default constructible +template ::value || std::is_void::value> +struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = default; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base &operator=( + expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base &operator=( + expected_default_ctor_base &&) noexcept = default; -template -using is_copy_assignable_or_void = is_void_or>; + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; -template -using is_move_assignable_or_void = is_void_or>; +// This specialization is for when T is not default constructible +template +struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = delete; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base &operator=( + expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base &operator=( + expected_default_ctor_base &&) noexcept = default; + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; } // namespace detail -namespace detail { -struct no_init_t {}; -static constexpr no_init_t no_init{}; - -// Implements the storage of the values, and ensures that the destructor is -// trivial if it can be. -// -// This specialization is for where neither `T` or `E` is trivially -// destructible, so the destructors must be called on destruction of the -// `expected` -template ::value, - bool = std::is_trivially_destructible::value> -struct expected_storage_base { - constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} - constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} - - template ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} +template +class bad_expected_access : public std::exception { + public: + explicit bad_expected_access(E e) : m_val(std::move(e)) {} - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} + virtual const char *what() const noexcept override { + return "Bad expected access"; + } - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + const E &error() const & { return m_val; } + E &error() & { return m_val; } + const E &&error() const && { return std::move(m_val); } + E &&error() && { return std::move(m_val); } - ~expected_storage_base() { - if (m_has_val) { - m_val.~T(); - } else { - m_unexpect.~unexpected(); - } - } - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; + private: + E m_val; }; -// This specialization is for when both `T` and `E` are trivially-destructible, -// so the destructor of the `expected` can be trivial. +/// An `expected` object is an object that contains the storage for +/// another object and manages the lifetime of this contained object `T`. +/// Alternatively it could contain the storage for another unexpected object +/// `E`. The contained object may not be initialized after the expected object +/// has been initialized, and may not be destroyed before the expected object +/// has been destroyed. The initialization state of the contained object is +/// tracked by the expected object. template -struct expected_storage_base { - constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} - constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} - - template ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} - - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} +class expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_ctor_base { + static_assert(!std::is_reference::value, "T must not be a reference"); + static_assert(!std::is_same::type>::value, + "T must not be in_place_t"); + static_assert(!std::is_same::type>::value, + "T must not be unexpect_t"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); + static_assert(!std::is_reference::value, "E must not be a reference"); - ~expected_storage_base() = default; - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; -}; + T *valptr() { return std::addressof(this->m_val); } + const T *valptr() const { return std::addressof(this->m_val); } + unexpected *errptr() { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { + return std::addressof(this->m_unexpect); + } -// T is trivial, E is not. -template -struct expected_storage_base { - constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} - TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) - : m_no_init(), m_has_val(false) {} + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } - template ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} + template ::value> * = nullptr> + constexpr const U &val() const { + return this->m_val; + } + constexpr const unexpected &err() const { return this->m_unexpect; } - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + public: + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; - ~expected_storage_base() { - if (!m_has_val) { - m_unexpect.~unexpected(); - } +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward(f)); } - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; -}; +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif -// E is trivial, T is not. -template -struct expected_storage_base { - constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} - constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} +#else + template + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } - template ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif +#endif - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template + TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template + constexpr auto map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif - ~expected_storage_base() { - if (m_has_val) { - m_val.~T(); - } +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template + TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template + constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); } - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; -}; -// `T` is `void`, `E` is trivially-destructible -template -struct expected_storage_base { -#if __GNUC__ <= 5 -// no constexpr for GCC 4/5 bug +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template + TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template + constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } #else - TL_EXPECTED_MSVC2015_CONSTEXPR + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } #endif - expected_storage_base() : m_has_val(true) {} +#endif + template + expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward(f)); + } - constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} + template + expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward(f)); + } - constexpr expected_storage_base(in_place_t) : m_has_val(true) {} + template + expected constexpr or_else(F &&f) const & { + return or_else_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward(f)); + } +#endif + constexpr expected() = default; + constexpr expected(const expected &rhs) = default; + constexpr expected(expected &&rhs) = default; + expected &operator=(const expected &rhs) = default; + expected &operator=(expected &&rhs) = default; template ::value> * = + detail::enable_if_t::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} + constexpr expected(in_place_t, Args &&...args) + : impl_base(in_place, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + T, std::initializer_list &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) + : impl_base(in_place, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} - ~expected_storage_base() = default; - struct dummy {}; - union { - unexpected m_unexpect; - dummy m_val; - }; - bool m_has_val; -}; + template ::value> * = + nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} -// `T` is `void`, `E` is not trivially-destructible -template -struct expected_storage_base { - constexpr expected_storage_base() : m_dummy(), m_has_val(true) {} - constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {} + template < + class G = E, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} - constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {} + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} + constexpr explicit expected(unexpect_t, Args &&...args) + : impl_base(unexpect, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + constexpr explicit expected(unexpect_t, std::initializer_list il, + Args &&...args) + : impl_base(unexpect, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} - ~expected_storage_base() { - if (!m_has_val) { - m_unexpect.~unexpected(); + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); } } - union { - unexpected m_unexpect; - char m_dummy; - }; - bool m_has_val; -}; - -// This base class provides some handy member functions which can be used in -// further derived classes -template -struct expected_operations_base : expected_storage_base { - using expected_storage_base::expected_storage_base; - - template - void construct(Args &&...args) noexcept { - new (std::addressof(this->m_val)) T(std::forward(args)...); - this->m_has_val = true; - } - - template - void construct_with(Rhs &&rhs) noexcept { - new (std::addressof(this->m_val)) T(std::forward(rhs).get()); - this->m_has_val = true; + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } } - template - void construct_error(Args &&...args) noexcept { - new (std::addressof(this->m_unexpect)) - unexpected(std::forward(args)...); - this->m_has_val = false; + template < + class U, class G, + detail::enable_if_t::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } } -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - - // These assign overloads ensure that the most efficient assignment - // implementation is used while maintaining the strong exception guarantee. - // The problematic case is where rhs has a value, but *this does not. - // - // This overload handles the case where we can just copy-construct `T` - // directly into place without throwing. - template ::value> - * = nullptr> - void assign(const expected_operations_base &rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - construct(rhs.get()); + template < + class U, class G, + detail::enable_if_t<(std::is_convertible::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); } else { - assign_common(rhs); + this->construct_error(std::move(rhs.error())); } } - // This overload handles the case where we can attempt to create a copy of - // `T`, then no-throw move it into place if the copy was successful. - template ::value && - std::is_nothrow_move_constructible::value> - * = nullptr> - void assign(const expected_operations_base &rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - T tmp = rhs.get(); - geterr().~unexpected(); - construct(std::move(tmp)); + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); } else { - assign_common(rhs); + err().~unexpected(); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; } + + return *this; } - // This overload is the worst-case, where we have to move-construct the - // unexpected value into temporary storage, then try to copy the T into place. - // If the construction succeeds, then everything is fine, but if it throws, - // then we move the old unexpected value back into place before rethrowing the - // exception. - template ::value && - !std::is_nothrow_move_constructible::value> - * = nullptr> - void assign(const expected_operations_base &rhs) { - if (!this->m_has_val && rhs.m_has_val) { - auto tmp = std::move(geterr()); - geterr().~unexpected(); + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~unexpected(); #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED try { - construct(rhs.get()); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; } catch (...) { - geterr() = std::move(tmp); + err() = std::move(tmp); throw; } #else - construct(rhs.get()); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; #endif - } else { - assign_common(rhs); } + + return *this; } - // These overloads do the same as above, but for rvalues - template ::value> - * = nullptr> - void assign(expected_operations_base &&rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - construct(std::move(rhs).get()); + template ::value && + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; } else { - assign_common(std::move(rhs)); + this->destroy_val(); + ::new (errptr()) unexpected(rhs); + this->m_has_val = false; } + + return *this; } - template ::value> - * = nullptr> - void assign(expected_operations_base &&rhs) { - if (!this->m_has_val && rhs.m_has_val) { - auto tmp = std::move(geterr()); - geterr().~unexpected(); + template ::value && + std::is_move_assignable::value> * = nullptr> + expected &operator=(unexpected &&rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + this->destroy_val(); + ::new (errptr()) unexpected(std::move(rhs)); + this->m_has_val = false; + } + + return *this; + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + } else { + err().~unexpected(); + this->m_has_val = true; + } + ::new (valptr()) T(std::forward(args)...); + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + ::new (valptr()) T(std::forward(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED try { - construct(std::move(rhs).get()); + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; } catch (...) { - geterr() = std::move(tmp); + err() = std::move(tmp); throw; } #else - construct(std::move(rhs).get()); + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; #endif - } else { - assign_common(std::move(rhs)); } } -#else - - // If exceptions are disabled then we can just copy-construct - void assign(const expected_operations_base &rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - construct(rhs.get()); + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); } else { - assign_common(rhs); + err().~unexpected(); + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; } } - void assign(expected_operations_base &&rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - construct(std::move(rhs).get()); + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); } else { - assign_common(rhs); - } - } - -#endif + auto tmp = std::move(err()); + err().~unexpected(); - // The common part of move/copy assigning - template - void assign_common(Rhs &&rhs) { - if (this->m_has_val) { - if (rhs.m_has_val) { - get() = std::forward(rhs).get(); - } else { - destroy_val(); - construct_error(std::forward(rhs).geterr()); - } - } else { - if (!rhs.m_has_val) { - geterr() = std::forward(rhs).geterr(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; } +#else + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; +#endif } } - bool has_value() const { return this->m_has_val; } - - TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } - constexpr const T &get() const & { return this->m_val; } - TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } -#ifndef TL_EXPECTED_NO_CONSTRR - constexpr const T &&get() const && { return std::move(this->m_val); } -#endif + private: + using t_is_void = std::true_type; + using t_is_not_void = std::false_type; + using t_is_nothrow_move_constructible = std::true_type; + using move_constructing_t_can_throw = std::false_type; + using e_is_nothrow_move_constructible = std::true_type; + using move_constructing_e_can_throw = std::false_type; - TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { - return this->m_unexpect; - } - constexpr const unexpected &geterr() const & { return this->m_unexpect; } - TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { - return std::move(this->m_unexpect); - } -#ifndef TL_EXPECTED_NO_CONSTRR - constexpr const unexpected &&geterr() const && { - return std::move(this->m_unexpect); + void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept { + // swapping void is a no-op } -#endif - - TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } -}; -// This base class provides some handy member functions which can be used in -// further derived classes -template -struct expected_operations_base : expected_storage_base { - using expected_storage_base::expected_storage_base; + void swap_where_both_have_value(expected &rhs, t_is_not_void) { + using std::swap; + swap(val(), rhs.val()); + } - template - void construct() noexcept { - this->m_has_val = true; + void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept( + std::is_nothrow_move_constructible::value) { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + std::swap(this->m_has_val, rhs.m_has_val); } - // This function doesn't use its argument, but needs it so that code in - // levels above this can work independently of whether T is void - template - void construct_with(Rhs &&) noexcept { - this->m_has_val = true; + void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { + swap_where_only_one_has_value_and_t_is_not_void( + rhs, typename std::is_nothrow_move_constructible::type{}, + typename std::is_nothrow_move_constructible::type{}); } - template - void construct_error(Args &&...args) noexcept { - new (std::addressof(this->m_unexpect)) - unexpected(std::forward(args)...); - this->m_has_val = false; + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + e_is_nothrow_move_constructible) noexcept { + auto temp = std::move(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); } - template - void assign(Rhs &&rhs) noexcept { - if (!this->m_has_val) { - if (rhs.m_has_val) { - geterr().~unexpected(); - construct(); - } else { - geterr() = std::forward(rhs).geterr(); - } - } else { - if (!rhs.m_has_val) { - construct_error(std::forward(rhs).geterr()); - } + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + move_constructing_e_can_throw) { + auto temp = std::move(val()); + val().~T(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + val() = std::move(temp); + throw; } +#else + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif } - bool has_value() const { return this->m_has_val; } - - TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { - return this->m_unexpect; - } - constexpr const unexpected &geterr() const & { return this->m_unexpect; } - TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { - return std::move(this->m_unexpect); - } -#ifndef TL_EXPECTED_NO_CONSTRR - constexpr const unexpected &&geterr() const && { - return std::move(this->m_unexpect); - } + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, move_constructing_t_can_throw, + e_is_nothrow_move_constructible) { + auto temp = std::move(rhs.err()); + rhs.err().~unexpected_type(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + rhs.err() = std::move(temp); + throw; + } +#else + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); #endif - - TL_EXPECTED_11_CONSTEXPR void destroy_val() { - // no-op } -}; -// This class manages conditionally having a trivial copy constructor -// This specialization is for when T and E are trivially copy constructible -template :: - value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> -struct expected_copy_base : expected_operations_base { - using expected_operations_base::expected_operations_base; -}; - -// This specialization is for when T or E are not trivially copy constructible -template -struct expected_copy_base : expected_operations_base { - using expected_operations_base::expected_operations_base; - - expected_copy_base() = default; - expected_copy_base(const expected_copy_base &rhs) - : expected_operations_base(no_init) { - if (rhs.has_value()) { - this->construct_with(rhs); + public: + template + detail::enable_if_t::value && + detail::is_swappable::value && + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> + swap(expected &rhs) noexcept( + std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value + &&std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value() && rhs.has_value()) { + swap_where_both_have_value(rhs, typename std::is_void::type{}); + } else if (!has_value() && rhs.has_value()) { + rhs.swap(*this); + } else if (has_value()) { + swap_where_only_one_has_value(rhs, typename std::is_void::type{}); } else { - this->construct_error(rhs.geterr()); + using std::swap; + swap(err(), rhs.err()); } } - expected_copy_base(expected_copy_base &&rhs) = default; - expected_copy_base &operator=(const expected_copy_base &rhs) = default; - expected_copy_base &operator=(expected_copy_base &&rhs) = default; -}; + constexpr const T *operator->() const { return valptr(); } + TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); } -// This class manages conditionally having a trivial move constructor -// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it -// doesn't implement an analogue to std::is_trivially_move_constructible. We -// have to make do with a non-trivial move constructor even if T is trivially -// move constructible -#ifndef TL_EXPECTED_GCC49 -template >::value - &&std::is_trivially_move_constructible::value> -struct expected_move_base : expected_copy_base { - using expected_copy_base::expected_copy_base; -}; -#else -template -struct expected_move_base; -#endif -template -struct expected_move_base : expected_copy_base { - using expected_copy_base::expected_copy_base; + template ::value> * = nullptr> + constexpr const U &operator*() const & { + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { + return val(); + } + template ::value> * = nullptr> + constexpr const U &&operator*() const && { + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&operator*() && { + return std::move(val()); + } - expected_move_base() = default; - expected_move_base(const expected_move_base &rhs) = default; + constexpr bool has_value() const noexcept { return this->m_has_val; } + constexpr explicit operator bool() const noexcept { return this->m_has_val; } - expected_move_base(expected_move_base &&rhs) noexcept( - std::is_nothrow_move_constructible::value) - : expected_copy_base(no_init) { - if (rhs.has_value()) { - this->construct_with(std::move(rhs)); - } else { - this->construct_error(std::move(rhs.geterr())); - } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); } - expected_move_base &operator=(const expected_move_base &rhs) = default; - expected_move_base &operator=(expected_move_base &&rhs) = default; -}; -// This class manages conditionally having a trivial copy assignment operator -template >::value - &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value - &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value - &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value> -struct expected_copy_assign_base : expected_move_base { - using expected_move_base::expected_move_base; + constexpr const E &error() const & { return err().value(); } + TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); } + constexpr const E &&error() const && { return std::move(err().value()); } + TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); } + + template + constexpr T value_or(U &&v) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast(std::forward(v)); + } + template + TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move-constructible and convertible to from U&&"); + return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); + } }; -template -struct expected_copy_assign_base : expected_move_base { - using expected_move_base::expected_move_base; +namespace detail { +template +using exp_t = typename detail::decay_t::value_type; +template +using err_t = typename detail::decay_t::error_type; +template +using ret_t = expected>; - expected_copy_assign_base() = default; - expected_copy_assign_base(const expected_copy_assign_base &rhs) = default; +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); - expected_copy_assign_base(expected_copy_assign_base &&rhs) = default; - expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) { - this->assign(rhs); - return *this; - } - expected_copy_assign_base &operator=(expected_copy_assign_base &&rhs) = - default; -}; + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} -// This class manages conditionally having a trivial move assignment operator -// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it -// doesn't implement an analogue to std::is_trivially_move_assignable. We have -// to make do with a non-trivial move assignment operator even if T is trivially -// move assignable -#ifndef TL_EXPECTED_GCC49 -template , - std::is_trivially_move_constructible, - std::is_trivially_move_assignable>>:: - value &&std::is_trivially_destructible::value - &&std::is_trivially_move_constructible::value - &&std::is_trivially_move_assignable::value> -struct expected_move_assign_base : expected_copy_assign_base { - using expected_copy_assign_base::expected_copy_assign_base; -}; +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} #else -template -struct expected_move_assign_base; -#endif +template +struct TC; +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); -template -struct expected_move_assign_base - : expected_copy_assign_base { - using expected_copy_assign_base::expected_copy_assign_base; + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} - expected_move_assign_base() = default; - expected_move_assign_base(const expected_move_assign_base &rhs) = default; +template ())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); - expected_move_assign_base(expected_move_assign_base &&rhs) = default; + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#endif - expected_move_assign_base &operator=(const expected_move_assign_base &rhs) = - default; +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} - expected_move_assign_base & - operator=(expected_move_assign_base &&rhs) noexcept( - std::is_nothrow_move_constructible::value - &&std::is_nothrow_move_assignable::value) { - this->assign(std::move(rhs)); - return *this; +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return result(); } -}; - -// expected_delete_ctor_base will conditionally delete copy and move -// constructors depending on whether T is copy/move constructible -template ::value && - std::is_copy_constructible::value), - bool EnableMove = (is_move_constructible_or_void::value && - std::is_move_constructible::value)> -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; - expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = - default; - expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = - default; -}; -template -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; - expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = - default; - expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = - default; -}; + return result(unexpect, std::forward(exp).error()); +} -template -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; - expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; - expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = - default; - expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = - default; -}; +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} -template -struct expected_delete_ctor_base { - expected_delete_ctor_base() = default; - expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; - expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; - expected_delete_ctor_base &operator=(const expected_delete_ctor_base &) = - default; - expected_delete_ctor_base &operator=(expected_delete_ctor_base &&) noexcept = - default; -}; +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return result(); + } -// expected_delete_assign_base will conditionally delete copy and move -// constructors depending on whether T and E are copy/move constructible + -// assignable -template ::value && - std::is_copy_constructible::value && - is_copy_assignable_or_void::value && - std::is_copy_assignable::value), - bool EnableMove = (is_move_constructible_or_void::value && - std::is_move_constructible::value && - is_move_assignable_or_void::value && - std::is_move_assignable::value)> -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base &) = default; - expected_delete_assign_base(expected_delete_assign_base &&) noexcept = - default; - expected_delete_assign_base &operator=(const expected_delete_assign_base &) = - default; - expected_delete_assign_base &operator=( - expected_delete_assign_base &&) noexcept = default; -}; + return result(unexpect, std::forward(exp).error()); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> -template -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base &) = default; - expected_delete_assign_base(expected_delete_assign_base &&) noexcept = - default; - expected_delete_assign_base &operator=(const expected_delete_assign_base &) = - default; - expected_delete_assign_base &operator=( - expected_delete_assign_base &&) noexcept = delete; -}; +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; -template -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base &) = default; - expected_delete_assign_base(expected_delete_assign_base &&) noexcept = - default; - expected_delete_assign_base &operator=(const expected_delete_assign_base &) = - delete; - expected_delete_assign_base &operator=( - expected_delete_assign_base &&) noexcept = default; -}; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} -template -struct expected_delete_assign_base { - expected_delete_assign_base() = default; - expected_delete_assign_base(const expected_delete_assign_base &) = default; - expected_delete_assign_base(expected_delete_assign_base &&) noexcept = - default; - expected_delete_assign_base &operator=(const expected_delete_assign_base &) = - delete; - expected_delete_assign_base &operator=( - expected_delete_assign_base &&) noexcept = delete; -}; +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> -// This is needed to be able to construct the expected_default_ctor_base which -// follows, while still conditionally deleting the default constructor. -struct default_constructor_tag { - explicit constexpr default_constructor_tag() = default; -}; +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return {}; + } -// expected_default_ctor_base will ensure that expected has a deleted default -// consturctor if T is not default constructible. -// This specialization is for when T is default constructible -template ::value || std::is_void::value> -struct expected_default_ctor_base { - constexpr expected_default_ctor_base() noexcept = default; - constexpr expected_default_ctor_base( - expected_default_ctor_base const &) noexcept = default; - constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = - default; - expected_default_ctor_base &operator=( - expected_default_ctor_base const &) noexcept = default; - expected_default_ctor_base &operator=( - expected_default_ctor_base &&) noexcept = default; + return unexpected>(std::forward(exp).error()); +} - constexpr explicit expected_default_ctor_base(default_constructor_tag) {} -}; +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> -// This specialization is for when T is not default constructible -template -struct expected_default_ctor_base { - constexpr expected_default_ctor_base() noexcept = delete; - constexpr expected_default_ctor_base( - expected_default_ctor_base const &) noexcept = default; - constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = - default; - expected_default_ctor_base &operator=( - expected_default_ctor_base const &) noexcept = default; - expected_default_ctor_base &operator=( - expected_default_ctor_base &&) noexcept = default; +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; - constexpr explicit expected_default_ctor_base(default_constructor_tag) {} -}; -} // namespace detail + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} -template -class bad_expected_access : public std::exception { - public: - explicit bad_expected_access(E e) : m_val(std::move(e)) {} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> - virtual const char *what() const noexcept override { - return "Bad expected access"; +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return {}; } - const E &error() const & { return m_val; } - E &error() & { return m_val; } - const E &&error() const && { return std::move(m_val); } - E &&error() && { return std::move(m_val); } - - private: - E m_val; -}; + return unexpected>(std::forward(exp).error()); +} +#endif -/// An `expected` object is an object that contains the storage for -/// another object and manages the lifetime of this contained object `T`. -/// Alternatively it could contain the storage for another unexpected object -/// `E`. The contained object may not be initialized after the expected object -/// has been initialized, and may not be destroyed before the expected object -/// has been destroyed. The initialization state of the contained object is -/// tracked by the expected object. -template -class expected : private detail::expected_move_assign_base, - private detail::expected_delete_ctor_base, - private detail::expected_delete_assign_base, - private detail::expected_default_ctor_base { - static_assert(!std::is_reference::value, "T must not be a reference"); - static_assert(!std::is_same::type>::value, - "T must not be in_place_t"); - static_assert(!std::is_same::type>::value, - "T must not be unexpect_t"); - static_assert( - !std::is_same>::type>::value, - "T must not be unexpected"); - static_assert(!std::is_reference::value, "E must not be a reference"); - - T *valptr() { return std::addressof(this->m_val); } - const T *valptr() const { return std::addressof(this->m_val); } - unexpected *errptr() { return std::addressof(this->m_unexpect); } - const unexpected *errptr() const { - return std::addressof(this->m_unexpect); +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &val() { - return this->m_val; + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); } - TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } - template ::value> * = nullptr> - constexpr const U &val() const { - return this->m_val; + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); } - constexpr const unexpected &err() const { return this->m_unexpect; } - using impl_base = detail::expected_move_assign_base; - using ctor_base = detail::expected_default_ctor_base; + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} - public: - typedef T value_type; - typedef E error_type; - typedef unexpected unexpected_type; +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template - TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { - return and_then_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { - return and_then_impl(std::move(*this), std::forward(f)); - } - template - constexpr auto and_then(F &&f) const & { - return and_then_impl(*this, std::forward(f)); - } + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr auto and_then(F &&f) const && { - return and_then_impl(std::move(*this), std::forward(f)); +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} #endif +#ifdef TL_EXPECTED_CXX14 +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} #else - template - TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & -> decltype(and_then_impl( - std::declval(), std::forward(f))) { - return and_then_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype(and_then_impl( - std::declval(), std::forward(f))) { - return and_then_impl(std::move(*this), std::forward(f)); - } - template - constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( - std::declval(), std::forward(f))) { - return and_then_impl(*this, std::forward(f)); - } +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( - std::declval(), std::forward(f))) { - return and_then_impl(std::move(*this), std::forward(f)); - } -#endif +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} #endif +} // namespace detail -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template - TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template - constexpr auto map(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - template - constexpr auto map(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( - std::declval(), std::declval())) - map(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#endif -#endif +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); +} +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : true); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() == rhs.error() : false); +} -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template - TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template - constexpr auto transform(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - template - constexpr auto transform(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( - std::declval(), std::declval())) - transform(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } +template +constexpr bool operator==(const expected &x, const U &v) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator==(const U &v, const expected &x) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator!=(const expected &x, const U &v) { + return x.has_value() ? *x != v : true; +} +template +constexpr bool operator!=(const U &v, const expected &x) { + return x.has_value() ? *x != v : true; +} -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#endif -#endif +template +constexpr bool operator==(const expected &x, const unexpected &e) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator==(const unexpected &e, const expected &x) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator!=(const expected &x, const unexpected &e) { + return x.has_value() ? true : x.error() != e.value(); +} +template +constexpr bool operator!=(const unexpected &e, const expected &x) { + return x.has_value() ? true : x.error() != e.value(); +} -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template - TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); - } - template - constexpr auto map_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); - } - template - constexpr auto map_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); - } +template ::value || + std::is_move_constructible::value) && + detail::is_swappable::value && + std::is_move_constructible::value && + detail::is_swappable::value> * = nullptr> +void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} +} // namespace tl -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); - } #endif -#endif - template - expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { - return or_else_impl(*this, std::forward(f)); - } +/* end file include/ada/expected.h */ - template - expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { - return or_else_impl(std::move(*this), std::forward(f)); - } +#include +#include - template - expected constexpr or_else(F &&f) const & { - return or_else_impl(*this, std::forward(f)); - } +/** + * @private + */ +namespace ada { +struct url_aggregator; +struct url; +} // namespace ada -#ifndef TL_EXPECTED_NO_CONSTRR - template - expected constexpr or_else(F &&f) const && { - return or_else_impl(std::move(*this), std::forward(f)); - } -#endif - constexpr expected() = default; - constexpr expected(const expected &rhs) = default; - constexpr expected(expected &&rhs) = default; - expected &operator=(const expected &rhs) = default; - expected &operator=(expected &&rhs) = default; +/** + * @namespace ada::parser + * @brief Includes the definitions for supported parsers + */ +namespace ada::parser { - template ::value> * = - nullptr> - constexpr expected(in_place_t, Args &&...args) - : impl_base(in_place, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} +/** + * Parses a url. + */ +template +result_type parse_url(std::string_view user_input, + const result_type* base_url = nullptr); - template &, Args &&...>::value> * = nullptr> - constexpr expected(in_place_t, std::initializer_list il, Args &&...args) - : impl_base(in_place, il, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} +extern template url_aggregator parse_url( + std::string_view user_input, const url_aggregator* base_url); +extern template url parse_url(std::string_view user_input, + const url* base_url); - template ::value> * = - nullptr, - detail::enable_if_t::value> * = - nullptr> - explicit constexpr expected(const unexpected &e) - : impl_base(unexpect, e.value()), - ctor_base(detail::default_constructor_tag{}) {} +} // namespace ada::parser - template < - class G = E, - detail::enable_if_t::value> * = - nullptr, - detail::enable_if_t::value> * = nullptr> - constexpr expected(unexpected const &e) - : impl_base(unexpect, e.value()), - ctor_base(detail::default_constructor_tag{}) {} +#endif // ADA_PARSER_H +/* end file include/ada/parser.h */ +/* begin file include/ada/scheme-inl.h */ +/** + * @file scheme-inl.h + * @brief Definitions for the URL scheme. + */ +#ifndef ADA_SCHEME_INL_H +#define ADA_SCHEME_INL_H - template < - class G = E, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t::value> * = nullptr> - explicit constexpr expected(unexpected &&e) noexcept( - std::is_nothrow_constructible::value) - : impl_base(unexpect, std::move(e.value())), - ctor_base(detail::default_constructor_tag{}) {} - - template < - class G = E, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t::value> * = nullptr> - constexpr expected(unexpected &&e) noexcept( - std::is_nothrow_constructible::value) - : impl_base(unexpect, std::move(e.value())), - ctor_base(detail::default_constructor_tag{}) {} - template ::value> * = - nullptr> - constexpr explicit expected(unexpect_t, Args &&...args) - : impl_base(unexpect, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} +namespace ada::scheme { - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected(unexpect_t, std::initializer_list il, - Args &&...args) - : impl_base(unexpect, il, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} +/** + * @namespace ada::scheme::details + * @brief Includes the definitions for scheme specific entities + */ +namespace details { +// for use with is_special and get_special_port +// Spaces, if present, are removed from URL. +constexpr std::string_view is_special_list[] = {"http", " ", "https", "ws", + "ftp", "wss", "file", " "}; +// for use with get_special_port +constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0}; +} // namespace details - template ::value && - std::is_convertible::value)> * = - nullptr, - detail::expected_enable_from_other - * = nullptr> - explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) - : ctor_base(detail::default_constructor_tag{}) { - if (rhs.has_value()) { - this->construct(*rhs); - } else { - this->construct_error(rhs.error()); - } +ada_really_inline constexpr bool is_special(std::string_view scheme) { + if (scheme.empty()) { + return false; } - - template ::value && - std::is_convertible::value)> * = - nullptr, - detail::expected_enable_from_other - * = nullptr> - TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) - : ctor_base(detail::default_constructor_tag{}) { - if (rhs.has_value()) { - this->construct(*rhs); - } else { - this->construct_error(rhs.error()); - } + int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1)); +} +constexpr uint16_t get_special_port(std::string_view scheme) noexcept { + if (scheme.empty()) { + return 0; } - - template < - class U, class G, - detail::enable_if_t::value && - std::is_convertible::value)> * = nullptr, - detail::expected_enable_from_other * = nullptr> - explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) - : ctor_base(detail::default_constructor_tag{}) { - if (rhs.has_value()) { - this->construct(std::move(*rhs)); - } else { - this->construct_error(std::move(rhs.error())); - } + int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { + return details::special_ports[hash_value]; + } else { + return 0; } - - template < - class U, class G, - detail::enable_if_t<(std::is_convertible::value && - std::is_convertible::value)> * = nullptr, - detail::expected_enable_from_other * = nullptr> - TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) - : ctor_base(detail::default_constructor_tag{}) { - if (rhs.has_value()) { - this->construct(std::move(*rhs)); - } else { - this->construct_error(std::move(rhs.error())); - } +} +constexpr uint16_t get_special_port(ada::scheme::type type) noexcept { + return details::special_ports[int(type)]; +} +constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept { + if (scheme.empty()) { + return ada::scheme::NOT_SPECIAL; } + int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; + const std::string_view target = details::is_special_list[hash_value]; + if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { + return ada::scheme::type(hash_value); + } else { + return ada::scheme::NOT_SPECIAL; + } +} - template < - class U = T, - detail::enable_if_t::value> * = nullptr, - detail::expected_enable_forward_value * = nullptr> - explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) - : expected(in_place, std::forward(v)) {} - - template < - class U = T, - detail::enable_if_t::value> * = nullptr, - detail::expected_enable_forward_value * = nullptr> - TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) - : expected(in_place, std::forward(v)) {} - - template < - class U = T, class G = T, - detail::enable_if_t::value> * = - nullptr, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t< - (!std::is_same, detail::decay_t>::value && - !detail::conjunction, - std::is_same>>::value && - std::is_constructible::value && - std::is_assignable::value && - std::is_nothrow_move_constructible::value)> * = nullptr> - expected &operator=(U &&v) { - if (has_value()) { - val() = std::forward(v); - } else { - err().~unexpected(); - ::new (valptr()) T(std::forward(v)); - this->m_has_val = true; - } +} // namespace ada::scheme - return *this; - } +#endif // ADA_SCHEME_H +/* end file include/ada/scheme-inl.h */ +/* begin file include/ada/serializers.h */ +/** + * @file serializers.h + * @brief Definitions for the URL serializers. + */ +#ifndef ADA_SERIALIZERS_H +#define ADA_SERIALIZERS_H - template < - class U = T, class G = T, - detail::enable_if_t::value> * = - nullptr, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t< - (!std::is_same, detail::decay_t>::value && - !detail::conjunction, - std::is_same>>::value && - std::is_constructible::value && - std::is_assignable::value && - std::is_nothrow_move_constructible::value)> * = nullptr> - expected &operator=(U &&v) { - if (has_value()) { - val() = std::forward(v); - } else { - auto tmp = std::move(err()); - err().~unexpected(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (valptr()) T(std::forward(v)); - this->m_has_val = true; - } catch (...) { - err() = std::move(tmp); - throw; - } -#else - ::new (valptr()) T(std::forward(v)); - this->m_has_val = true; -#endif - } +#include +#include +#include - return *this; - } +/** + * @namespace ada::serializers + * @brief Includes the definitions for URL serializers + */ +namespace ada::serializers { - template ::value && - std::is_assignable::value> * = nullptr> - expected &operator=(const unexpected &rhs) { - if (!has_value()) { - err() = rhs; - } else { - this->destroy_val(); - ::new (errptr()) unexpected(rhs); - this->m_has_val = false; - } +/** + * Finds and returns the longest sequence of 0 values in a ipv6 input. + */ +void find_longest_sequence_of_ipv6_pieces( + const std::array& address, size_t& compress, + size_t& compress_length) noexcept; - return *this; - } +/** + * Serializes an ipv6 address. + * @details An IPv6 address is a 128-bit unsigned integer that identifies a + * network address. + * @see https://url.spec.whatwg.org/#concept-ipv6-serializer + */ +std::string ipv6(const std::array& address) noexcept; - template ::value && - std::is_move_assignable::value> * = nullptr> - expected &operator=(unexpected &&rhs) noexcept { - if (!has_value()) { - err() = std::move(rhs); - } else { - this->destroy_val(); - ::new (errptr()) unexpected(std::move(rhs)); - this->m_has_val = false; - } +/** + * Serializes an ipv4 address. + * @details An IPv4 address is a 32-bit unsigned integer that identifies a + * network address. + * @see https://url.spec.whatwg.org/#concept-ipv4-serializer + */ +std::string ipv4(const uint64_t address) noexcept; - return *this; - } +} // namespace ada::serializers - template ::value> * = nullptr> - void emplace(Args &&...args) { - if (has_value()) { - val().~T(); - } else { - err().~unexpected(); - this->m_has_val = true; - } - ::new (valptr()) T(std::forward(args)...); - } - - template ::value> * = nullptr> - void emplace(Args &&...args) { - if (has_value()) { - val().~T(); - ::new (valptr()) T(std::forward(args)...); - } else { - auto tmp = std::move(err()); - err().~unexpected(); +#endif // ADA_SERIALIZERS_H +/* end file include/ada/serializers.h */ +/* begin file include/ada/unicode.h */ +/** + * @file unicode.h + * @brief Definitions for all unicode specific functions. + */ +#ifndef ADA_UNICODE_H +#define ADA_UNICODE_H -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (valptr()) T(std::forward(args)...); - this->m_has_val = true; - } catch (...) { - err() = std::move(tmp); - throw; - } -#else - ::new (valptr()) T(std::forward(args)...); - this->m_has_val = true; -#endif - } - } - template &, Args &&...>::value> * = nullptr> - void emplace(std::initializer_list il, Args &&...args) { - if (has_value()) { - T t(il, std::forward(args)...); - val() = std::move(t); - } else { - err().~unexpected(); - ::new (valptr()) T(il, std::forward(args)...); - this->m_has_val = true; - } - } +#include +#include - template &, Args &&...>::value> * = nullptr> - void emplace(std::initializer_list il, Args &&...args) { - if (has_value()) { - T t(il, std::forward(args)...); - val() = std::move(t); - } else { - auto tmp = std::move(err()); - err().~unexpected(); +/** + * @namespace ada::unicode + * @brief Includes the definitions for unicode operations + */ +namespace ada::unicode { -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (valptr()) T(il, std::forward(args)...); - this->m_has_val = true; - } catch (...) { - err() = std::move(tmp); - throw; - } -#else - ::new (valptr()) T(il, std::forward(args)...); - this->m_has_val = true; -#endif - } - } +/** + * We receive a UTF-8 string representing a domain name. + * If the string is percent encoded, we apply percent decoding. + * + * Given a domain, we need to identify its labels. + * They are separated by label-separators: + * + * U+002E ( . ) FULL STOP + * U+FF0E ( . ) FULLWIDTH FULL STOP + * U+3002 ( 。 ) IDEOGRAPHIC FULL STOP + * U+FF61 ( 。 ) HALFWIDTH IDEOGRAPHIC FULL STOP + * + * They are all mapped to U+002E. + * + * We process each label into a string that should not exceed 63 octets. + * If the string is already punycode (starts with "xn--"), then we must + * scan it to look for unallowed code points. + * Otherwise, if the string is not pure ASCII, we need to transcode it + * to punycode by following RFC 3454 which requires us to + * - Map characters (see section 3), + * - Normalize (see section 4), + * - Reject forbidden characters, + * - Check for right-to-left characters and if so, check all requirements (see + * section 6), + * - Optionally reject based on unassigned code points (section 7). + * + * The Unicode standard provides a table of code points with a mapping, a list + * of forbidden code points and so forth. This table is subject to change and + * will vary based on the implementation. For Unicode 15, the table is at + * https://www.unicode.org/Public/idna/15.0.0/IdnaMappingTable.txt + * If you use ICU, they parse this table and map it to code using a Python + * script. + * + * The resulting strings should not exceed 255 octets according to RFC 1035 + * section 2.3.4. ICU checks for label size and domain size, but these errors + * are ignored. + * + * @see https://url.spec.whatwg.org/#concept-domain-to-ascii + * + */ +bool to_ascii(std::optional& out, std::string_view plain, + size_t first_percent); - private: - using t_is_void = std::true_type; - using t_is_not_void = std::false_type; - using t_is_nothrow_move_constructible = std::true_type; - using move_constructing_t_can_throw = std::false_type; - using e_is_nothrow_move_constructible = std::true_type; - using move_constructing_e_can_throw = std::false_type; +/** + * @see https://www.unicode.org/reports/tr46/#ToUnicode + */ +std::string to_unicode(std::string_view input); - void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept { - // swapping void is a no-op - } +/** + * Checks if the input has tab or newline characters. + * + * @attention The has_tabs_or_newline function is a bottleneck and it is simple + * enough that compilers like GCC can 'autovectorize it'. + */ +ada_really_inline constexpr bool has_tabs_or_newline( + std::string_view user_input) noexcept; - void swap_where_both_have_value(expected &rhs, t_is_not_void) { - using std::swap; - swap(val(), rhs.val()); - } +/** + * Checks if the input is a forbidden host code point. + * @see https://url.spec.whatwg.org/#forbidden-host-code-point + */ +ada_really_inline constexpr bool is_forbidden_host_code_point( + const char c) noexcept; - void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept( - std::is_nothrow_move_constructible::value) { - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - std::swap(this->m_has_val, rhs.m_has_val); - } +/** + * Checks if the input contains a forbidden domain code point. + * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + */ +ada_really_inline constexpr bool contains_forbidden_domain_code_point( + const char* input, size_t length) noexcept; - void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { - swap_where_only_one_has_value_and_t_is_not_void( - rhs, typename std::is_nothrow_move_constructible::type{}, - typename std::is_nothrow_move_constructible::type{}); - } +/** + * Checks if the input contains a forbidden domain code point in which case + * the first bit is set to 1. If the input contains an upper case ASCII letter, + * then the second bit is set to 1. + * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + */ +ada_really_inline constexpr bool contains_forbidden_domain_code_point_or_upper( + const char* input, size_t length) noexcept; - void swap_where_only_one_has_value_and_t_is_not_void( - expected &rhs, t_is_nothrow_move_constructible, - e_is_nothrow_move_constructible) noexcept { - auto temp = std::move(val()); - val().~T(); - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - ::new (rhs.valptr()) T(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); - } +/** + * Checks if the input is a forbidden doamin code point. + * @see https://url.spec.whatwg.org/#forbidden-domain-code-point + */ +ada_really_inline constexpr bool is_forbidden_domain_code_point( + const char c) noexcept; - void swap_where_only_one_has_value_and_t_is_not_void( - expected &rhs, t_is_nothrow_move_constructible, - move_constructing_e_can_throw) { - auto temp = std::move(val()); - val().~T(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - ::new (rhs.valptr()) T(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); - } catch (...) { - val() = std::move(temp); - throw; - } -#else - ::new (errptr()) unexpected_type(std::move(rhs.err())); - rhs.err().~unexpected_type(); - ::new (rhs.valptr()) T(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); -#endif - } +/** + * Checks if the input is alphanumeric, '+', '-' or '.' + */ +ada_really_inline constexpr bool is_alnum_plus(const char c) noexcept; - void swap_where_only_one_has_value_and_t_is_not_void( - expected &rhs, move_constructing_t_can_throw, - e_is_nothrow_move_constructible) { - auto temp = std::move(rhs.err()); - rhs.err().~unexpected_type(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (rhs.valptr()) T(std::move(val())); - val().~T(); - ::new (errptr()) unexpected_type(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); - } catch (...) { - rhs.err() = std::move(temp); - throw; - } -#else - ::new (rhs.valptr()) T(std::move(val())); - val().~T(); - ::new (errptr()) unexpected_type(std::move(temp)); - std::swap(this->m_has_val, rhs.m_has_val); -#endif - } +/** + * @details An ASCII hex digit is an ASCII upper hex digit or ASCII lower hex + * digit. An ASCII upper hex digit is an ASCII digit or a code point in the + * range U+0041 (A) to U+0046 (F), inclusive. An ASCII lower hex digit is an + * ASCII digit or a code point in the range U+0061 (a) to U+0066 (f), inclusive. + */ +ada_really_inline constexpr bool is_ascii_hex_digit(const char c) noexcept; - public: - template - detail::enable_if_t::value && - detail::is_swappable::value && - (std::is_nothrow_move_constructible::value || - std::is_nothrow_move_constructible::value)> - swap(expected &rhs) noexcept( - std::is_nothrow_move_constructible::value - &&detail::is_nothrow_swappable::value - &&std::is_nothrow_move_constructible::value - &&detail::is_nothrow_swappable::value) { - if (has_value() && rhs.has_value()) { - swap_where_both_have_value(rhs, typename std::is_void::type{}); - } else if (!has_value() && rhs.has_value()) { - rhs.swap(*this); - } else if (has_value()) { - swap_where_only_one_has_value(rhs, typename std::is_void::type{}); - } else { - using std::swap; - swap(err(), rhs.err()); - } - } +/** + * Checks if the input is a C0 control or space character. + * + * @details A C0 control or space is a C0 control or U+0020 SPACE. + * A C0 control is a code point in the range U+0000 NULL to U+001F INFORMATION + * SEPARATOR ONE, inclusive. + */ +ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept; - constexpr const T *operator->() const { return valptr(); } - TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); } +/** + * Checks if the input is a ASCII tab or newline character. + * + * @details An ASCII tab or newline is U+0009 TAB, U+000A LF, or U+000D CR. + */ +ada_really_inline constexpr bool is_ascii_tab_or_newline(const char c) noexcept; - template ::value> * = nullptr> - constexpr const U &operator*() const & { - return val(); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &operator*() & { - return val(); - } - template ::value> * = nullptr> - constexpr const U &&operator*() const && { - return std::move(val()); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &&operator*() && { - return std::move(val()); - } +/** + * @details A double-dot path segment must be ".." or an ASCII case-insensitive + * match for ".%2e", "%2e.", or "%2e%2e". + */ +ada_really_inline ada_constexpr bool is_double_dot_path_segment( + const std::string_view input) noexcept; - constexpr bool has_value() const noexcept { return this->m_has_val; } - constexpr explicit operator bool() const noexcept { return this->m_has_val; } +/** + * @details A single-dot path segment must be "." or an ASCII case-insensitive + * match for "%2e". + */ +ada_really_inline constexpr bool is_single_dot_path_segment( + const std::string_view input) noexcept; - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR const U &value() const & { - if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); - return val(); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &value() & { - if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); - return val(); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR const U &&value() const && { - if (!has_value()) - detail::throw_exception(bad_expected_access(std::move(err()).value())); - return std::move(val()); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &&value() && { - if (!has_value()) - detail::throw_exception(bad_expected_access(std::move(err()).value())); - return std::move(val()); - } +/** + * @details ipv4 character might contain 0-9 or a-f character ranges. + */ +ada_really_inline constexpr bool is_lowercase_hex(const char c) noexcept; - constexpr const E &error() const & { return err().value(); } - TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); } - constexpr const E &&error() const && { return std::move(err().value()); } - TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); } +/** + * @details Convert hex to binary. + */ +unsigned constexpr convert_hex_to_binary(char c) noexcept; - template - constexpr T value_or(U &&v) const & { - static_assert(std::is_copy_constructible::value && - std::is_convertible::value, - "T must be copy-constructible and convertible to from U&&"); - return bool(*this) ? **this : static_cast(std::forward(v)); - } - template - TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { - static_assert(std::is_move_constructible::value && - std::is_convertible::value, - "T must be move-constructible and convertible to from U&&"); - return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); - } -}; +/** + * first_percent should be = input.find('%') + * + * @todo It would be faster as noexcept maybe, but it could be unsafe since. + * @author Node.js + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L245 + * @see https://encoding.spec.whatwg.org/#utf-8-decode-without-bom + */ +std::string percent_decode(const std::string_view input, size_t first_percent); -namespace detail { -template -using exp_t = typename detail::decay_t::value_type; -template -using err_t = typename detail::decay_t::error_type; -template -using ret_t = expected>; +/** + * Returns a percent-encoding string whether percent encoding was needed or not. + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + */ +std::string percent_encode(const std::string_view input, + const uint8_t character_set[]); +/** + * Returns a percent-encoded string version of input, while starting the percent + * encoding at the provided index. + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + */ +std::string percent_encode(const std::string_view input, + const uint8_t character_set[], size_t index); +/** + * Returns true if percent encoding was needed, in which case, we store + * the percent-encoded content in 'out'. If the boolean 'append' is set to + * true, the content is appended to 'out'. + * If percent encoding is not needed, out is left unchanged. + * @see https://github.com/nodejs/node/blob/main/src/node_url.cc#L226 + */ +template +bool percent_encode(const std::string_view input, const uint8_t character_set[], + std::string& out); +/** + * Returns the index at which percent encoding should start, or (equivalently), + * the length of the prefix that does not require percent encoding. + */ +ada_really_inline size_t percent_encode_index(const std::string_view input, + const uint8_t character_set[]); +/** + * Lowers the string in-place, assuming that the content is ASCII. + * Return true if the content was ASCII. + */ +constexpr bool to_lower_ascii(char* input, size_t length) noexcept; +} // namespace ada::unicode -#ifdef TL_EXPECTED_CXX14 -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval()))> -constexpr auto and_then_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); +#endif // ADA_UNICODE_H +/* end file include/ada/unicode.h */ +/* begin file include/ada/url_base-inl.h */ +/** + * @file url_base-inl.h + * @brief Inline functions for url base + */ +#ifndef ADA_URL_BASE_INL_H +#define ADA_URL_BASE_INL_H - return exp.has_value() - ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, std::forward(exp).error()); -} +/* begin file include/ada/url_aggregator.h */ +/** + * @file url_aggregator.h + * @brief Declaration for the basic URL definitions + */ +#ifndef ADA_URL_AGGREGATOR_H +#define ADA_URL_AGGREGATOR_H -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval()))> -constexpr auto and_then_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, std::forward(exp).error()); -} -#else -template -struct TC; -template (), - *std::declval())), - detail::enable_if_t>::value> * = nullptr> -auto and_then_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); +#include +#include - return exp.has_value() - ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, std::forward(exp).error()); -} +namespace ada { -template ())), - detail::enable_if_t>::value> * = nullptr> -constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); +/** + * @brief Lightweight URL struct. + * + * @details The url_aggregator class aims to minimize temporary memory + * allocation while representing a parsed URL. Internally, it contains a single + * normalized URL (the href), and it makes available the components, mostly + * using std::string_view. + */ +struct url_aggregator : url_base { + url_aggregator() = default; + url_aggregator(const url_aggregator &u) = default; + url_aggregator(url_aggregator &&u) noexcept = default; + url_aggregator &operator=(url_aggregator &&u) noexcept = default; + url_aggregator &operator=(const url_aggregator &u) = default; + ~url_aggregator() = default; - return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, std::forward(exp).error()); -} -#endif + bool set_href(const std::string_view input); + bool set_host(const std::string_view input); + bool set_hostname(const std::string_view input); + bool set_protocol(const std::string_view input); + bool set_username(const std::string_view input); + bool set_password(const std::string_view input); + bool set_port(const std::string_view input); + bool set_pathname(const std::string_view input); + void set_search(const std::string_view input); + void set_hash(const std::string_view input); -#ifdef TL_EXPECTED_CXX14 -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> -constexpr auto expected_map_impl(Exp &&exp, F &&f) { - using result = ret_t>; - return exp.has_value() ? result(detail::invoke(std::forward(f), - *std::forward(exp))) - : result(unexpect, std::forward(exp).error()); -} + [[nodiscard]] bool has_valid_domain() const noexcept override; + /** + * The origin getter steps are to return the serialization of this’s URL’s + * origin. [HTML] + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#concept-url-origin + */ + [[nodiscard]] std::string get_origin() const noexcept override; + /** + * Return the normalized string. + * This function does not allocate memory. + * It is highly efficient. + * @return a constant reference to the underlying normalized URL. + * @see https://url.spec.whatwg.org/#dom-url-href + * @see https://url.spec.whatwg.org/#concept-url-serializer + */ + inline std::string_view get_href() const noexcept; + /** + * The username getter steps are to return this’s URL’s username. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-username + */ + [[nodiscard]] std::string_view get_username() const noexcept; + /** + * The password getter steps are to return this’s URL’s password. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-password + */ + [[nodiscard]] std::string_view get_password() const noexcept; + /** + * Return this’s URL’s port, serialized. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-port + */ + [[nodiscard]] std::string_view get_port() const noexcept; + /** + * Return U+0023 (#), followed by this’s URL’s fragment. + * This function does not allocate memory. + * @return a lightweight std::string_view.. + * @see https://url.spec.whatwg.org/#dom-url-hash + */ + [[nodiscard]] std::string_view get_hash() const noexcept; + /** + * Return url’s host, serialized, followed by U+003A (:) and url’s port, + * serialized. + * This function does not allocate memory. + * When there is no host, this function returns the empty view. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-host + */ + [[nodiscard]] std::string_view get_host() const noexcept; + /** + * Return this’s URL’s host, serialized. + * This function does not allocate memory. + * When there is no host, this function returns the empty view. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-hostname + */ + [[nodiscard]] std::string_view get_hostname() const noexcept; + /** + * The pathname getter steps are to return the result of URL path serializing + * this’s URL. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-pathname + */ + [[nodiscard]] std::string_view get_pathname() const noexcept; + /** + * Compute the pathname length in bytes witout instantiating a view or a + * string. + * @return size of the pathname in bytes + * @see https://url.spec.whatwg.org/#dom-url-pathname + */ + ada_really_inline uint32_t get_pathname_length() const noexcept; + /** + * Return U+003F (?), followed by this’s URL’s query. + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-search + */ + [[nodiscard]] std::string_view get_search() const noexcept; + /** + * The protocol getter steps are to return this’s URL’s scheme, followed by + * U+003A (:). + * This function does not allocate memory. + * @return a lightweight std::string_view. + * @see https://url.spec.whatwg.org/#dom-url-protocol + */ + [[nodiscard]] std::string_view get_protocol() const noexcept; -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> -auto expected_map_impl(Exp &&exp, F &&f) { - using result = expected>; - if (exp.has_value()) { - detail::invoke(std::forward(f), *std::forward(exp)); - return result(); - } + /** + * A URL includes credentials if its username or password is not the empty + * string. + */ + [[nodiscard]] ada_really_inline bool has_credentials() const noexcept; - return result(unexpect, std::forward(exp).error()); -} + /** + * Useful for implementing efficient serialization for the URL. + * + * https://user:pass@example.com:1234/foo/bar?baz#quux + * | | | | ^^^^| | | + * | | | | | | | `----- hash_start + * | | | | | | `--------- search_start + * | | | | | `----------------- pathname_start + * | | | | `--------------------- port + * | | | `----------------------- host_end + * | | `---------------------------------- host_start + * | `--------------------------------------- username_end + * `--------------------------------------------- protocol_end + * + * Inspired after servo/url + * + * @return a constant reference to the underlying component attribute. + * + * @see + * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31 + */ + [[nodiscard]] ada_really_inline const ada::url_components &get_components() + const noexcept; + /** + * Returns a string representation of this URL. + */ + std::string to_string() const override; + /** + * Returns a string diagram of this URL. + */ + std::string to_diagram() const; -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> -constexpr auto expected_map_impl(Exp &&exp, F &&f) { - using result = ret_t>; - return exp.has_value() ? result(detail::invoke(std::forward(f))) - : result(unexpect, std::forward(exp).error()); -} + /** + * Verifies that the parsed URL could be valid. Useful for debugging purposes. + * @return true if the URL is valid, otherwise return true of the offsets are + * possible. + */ + bool validate() const noexcept; -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> -auto expected_map_impl(Exp &&exp, F &&f) { - using result = expected>; - if (exp.has_value()) { - detail::invoke(std::forward(f)); - return result(); - } + /** @return true if it has an host but it is the empty string */ + [[nodiscard]] inline bool has_empty_hostname() const noexcept; + /** @return true if it has a host (included an empty host) */ + [[nodiscard]] inline bool has_hostname() const noexcept; + /** @return true if the URL has a non-empty username */ + [[nodiscard]] inline bool has_non_empty_username() const noexcept; + /** @return true if the URL has a non-empty password */ + [[nodiscard]] inline bool has_non_empty_password() const noexcept; + /** @return true if the URL has a (non default) port */ + [[nodiscard]] inline bool has_port() const noexcept; + /** @return true if the URL has a password */ + [[nodiscard]] inline bool has_password() const noexcept; + /** @return true if the URL has a hash component */ + [[nodiscard]] inline bool has_hash() const noexcept override; + /** @return true if the URL has a search component */ + [[nodiscard]] inline bool has_search() const noexcept override; - return result(unexpect, std::forward(exp).error()); -} -#else -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> + private: + friend ada::url_aggregator ada::parser::parse_url( + std::string_view, const ada::url_aggregator *); + friend void ada::helpers::strip_trailing_spaces_from_opaque_path< + ada::url_aggregator>(ada::url_aggregator &url) noexcept; -constexpr auto expected_map_impl(Exp &&exp, F &&f) - -> ret_t> { - using result = ret_t>; + std::string buffer{}; + url_components components{}; - return exp.has_value() ? result(detail::invoke(std::forward(f), - *std::forward(exp))) - : result(unexpect, std::forward(exp).error()); -} + /** + * Returns true if neither the search, nor the hash nor the pathname + * have been set. + * @return true if the buffer is ready to receive the path. + */ + [[nodiscard]] ada_really_inline bool is_at_path() const noexcept; -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> + inline void add_authority_slashes_if_needed() noexcept; -auto expected_map_impl(Exp &&exp, F &&f) -> expected> { - if (exp.has_value()) { - detail::invoke(std::forward(f), *std::forward(exp)); - return {}; - } + /** + * To optimize performance, you may indicate how much memory to allocate + * within this instance. + */ + inline void reserve(uint32_t capacity); - return unexpected>(std::forward(exp).error()); -} + ada_really_inline size_t + parse_port(std::string_view view, + bool check_trailing_content = false) noexcept override; -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> + /** + * Return true on success. + * @see https://url.spec.whatwg.org/#concept-ipv4-parser + */ + [[nodiscard]] bool parse_ipv4(std::string_view input); -constexpr auto expected_map_impl(Exp &&exp, F &&f) - -> ret_t> { - using result = ret_t>; + /** + * Return true on success. + * @see https://url.spec.whatwg.org/#concept-ipv6-parser + */ + [[nodiscard]] bool parse_ipv6(std::string_view input); - return exp.has_value() ? result(detail::invoke(std::forward(f))) - : result(unexpect, std::forward(exp).error()); -} + /** + * Return true on success. + * @see https://url.spec.whatwg.org/#concept-opaque-host-parser + */ + [[nodiscard]] bool parse_opaque_host(std::string_view input); -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> + ada_really_inline void parse_path(std::string_view input); -auto expected_map_impl(Exp &&exp, F &&f) -> expected> { - if (exp.has_value()) { - detail::invoke(std::forward(f)); - return {}; - } + /** + * A URL cannot have a username/password/port if its host is null or the empty + * string, or its scheme is "file". + */ + [[nodiscard]] inline bool cannot_have_credentials_or_port() const; - return unexpected>(std::forward(exp).error()); -} -#endif + template + bool set_host_or_hostname(const std::string_view input); -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, detail::decay_t>; - return exp.has_value() - ? result(*std::forward(exp)) - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, monostate>; - if (exp.has_value()) { - return result(*std::forward(exp)); - } + ada_really_inline bool parse_host(std::string_view input); - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, detail::decay_t>; - return exp.has_value() - ? result() - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, monostate>; - if (exp.has_value()) { - return result(); - } + inline void update_base_authority(std::string_view base_buffer, + const ada::url_components &base); + inline void update_unencoded_base_hash(std::string_view input); + inline void update_base_hostname(std::string_view input); + inline void update_base_search(std::string_view input); + inline void update_base_search(std::string_view input, + const uint8_t *query_percent_encode_set); + inline void update_base_pathname(const std::string_view input); + inline void update_base_username(const std::string_view input); + inline void append_base_username(const std::string_view input); + inline void update_base_password(const std::string_view input); + inline void append_base_password(const std::string_view input); + inline void update_base_port(uint32_t input); + inline void append_base_pathname(const std::string_view input); + inline uint32_t retrieve_base_port() const; + inline void clear_port(); + inline void clear_hostname(); + inline void clear_hash(); + inline void clear_pathname() override; + inline void clear_search() override; + inline void clear_password(); + inline bool has_dash_dot() const noexcept; + void delete_dash_dot(); + inline void consume_prepared_path(std::string_view input); + template + [[nodiscard]] ada_really_inline bool parse_scheme_with_colon( + const std::string_view input); + ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end, + std::string_view input); + inline bool has_authority() const noexcept; + inline void set_protocol_as_file(); + inline void set_scheme(std::string_view new_scheme) noexcept; + /** + * Fast function to set the scheme from a view with a colon in the + * buffer, does not change type. + */ + inline void set_scheme_from_view_with_colon( + std::string_view new_scheme_with_colon) noexcept; + inline void copy_scheme(const url_aggregator &u) noexcept; - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -#else -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) - -> expected, detail::decay_t> { - using result = expected, detail::decay_t>; +}; // url_aggregator - return exp.has_value() - ? result(*std::forward(exp)) - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} +inline std::ostream &operator<<(std::ostream &out, const ada::url &u); +} // namespace ada -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { - using result = expected, monostate>; - if (exp.has_value()) { - return result(*std::forward(exp)); - } +#endif +/* end file include/ada/url_aggregator.h */ +/* begin file include/ada/checkers.h */ +/** + * @file checkers.h + * @brief Declarations for URL specific checkers used within Ada. + */ +#ifndef ADA_CHECKERS_H +#define ADA_CHECKERS_H - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) - -> expected, detail::decay_t> { - using result = expected, detail::decay_t>; +#include +#include - return exp.has_value() - ? result() - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} +/** + * @namespace ada::checkers + * @brief Includes the definitions for validation functions + */ +namespace ada::checkers { -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { - using result = expected, monostate>; - if (exp.has_value()) { - return result(); - } +/** + * Assuming that x is an ASCII letter, this function returns the lower case + * equivalent. + * @details More likely to be inlined by the compiler and constexpr. + */ +constexpr char to_lower(char x) noexcept; + +/** + * Returns true if the character is an ASCII letter. Equivalent to std::isalpha + * but more likely to be inlined by the compiler. + * + * @attention std::isalpha is not constexpr generally. + */ +constexpr bool is_alpha(char x) noexcept; - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -#endif +/** + * Check whether a string starts with 0x or 0X. The function is only + * safe if input.size() >=2. + * + * @see has_hex_prefix + */ +inline bool has_hex_prefix_unsafe(std::string_view input); +/** + * Check whether a string starts with 0x or 0X. + */ +inline bool has_hex_prefix(std::string_view input); -#ifdef TL_EXPECTED_CXX14 -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto or_else_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() ? std::forward(exp) - : detail::invoke(std::forward(f), - std::forward(exp).error()); -} +/** + * Check whether x is an ASCII digit. More likely to be inlined than + * std::isdigit. + */ +constexpr bool is_digit(char x) noexcept; -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() ? std::forward(exp) - : (detail::invoke(std::forward(f), - std::forward(exp).error()), - std::forward(exp)); -} -#else -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto or_else_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() ? std::forward(exp) - : detail::invoke(std::forward(f), - std::forward(exp).error()); -} +/** + * @details A string starts with a Windows drive letter if all of the following + * are true: + * + * - its length is greater than or equal to 2 + * - its first two code points are a Windows drive letter + * - its length is 2 or its third code point is U+002F (/), U+005C (\), U+003F + * (?), or U+0023 (#). + * + * https://url.spec.whatwg.org/#start-with-a-windows-drive-letter + */ +inline constexpr bool is_windows_drive_letter(std::string_view input) noexcept; -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() ? std::forward(exp) - : (detail::invoke(std::forward(f), - std::forward(exp).error()), - std::forward(exp)); -} -#endif -} // namespace detail +/** + * @details A normalized Windows drive letter is a Windows drive letter of which + * the second code point is U+003A (:). + */ +inline constexpr bool is_normalized_windows_drive_letter( + std::string_view input) noexcept; -template -constexpr bool operator==(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? false - : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); -} -template -constexpr bool operator!=(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? true - : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); -} -template -constexpr bool operator==(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? false - : (!lhs.has_value() ? lhs.error() == rhs.error() : true); -} -template -constexpr bool operator!=(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? true - : (!lhs.has_value() ? lhs.error() == rhs.error() : false); -} +/** + * @warning Will be removed when Ada supports C++20. + */ +ada_really_inline constexpr bool begins_with(std::string_view view, + std::string_view prefix); -template -constexpr bool operator==(const expected &x, const U &v) { - return x.has_value() ? *x == v : false; -} -template -constexpr bool operator==(const U &v, const expected &x) { - return x.has_value() ? *x == v : false; -} -template -constexpr bool operator!=(const expected &x, const U &v) { - return x.has_value() ? *x != v : true; -} -template -constexpr bool operator!=(const U &v, const expected &x) { - return x.has_value() ? *x != v : true; -} +/** + * Returns true if an input is an ipv4 address. + */ +ada_really_inline ada_constexpr bool is_ipv4(std::string_view view) noexcept; -template -constexpr bool operator==(const expected &x, const unexpected &e) { - return x.has_value() ? false : x.error() == e.value(); -} -template -constexpr bool operator==(const unexpected &e, const expected &x) { - return x.has_value() ? false : x.error() == e.value(); -} -template -constexpr bool operator!=(const expected &x, const unexpected &e) { - return x.has_value() ? true : x.error() != e.value(); -} -template -constexpr bool operator!=(const unexpected &e, const expected &x) { - return x.has_value() ? true : x.error() != e.value(); -} +/** + * Returns a bitset. If the first bit is set, then at least one character needs + * percent encoding. If the second bit is set, a \\ is found. If the third bit + * is set then we have a dot. If the fourth bit is set, then we have a percent + * character. + */ +ada_really_inline constexpr uint8_t path_signature( + std::string_view input) noexcept; -template ::value || - std::is_move_constructible::value) && - detail::is_swappable::value && - std::is_move_constructible::value && - detail::is_swappable::value> * = nullptr> -void swap(expected &lhs, - expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { - lhs.swap(rhs); -} -} // namespace tl +/** + * Returns true if the length of the domain name and its labels are according to + * the specifications. The length of the domain must be 255 octets (253 + * characters not including the last 2 which are the empty label reserved at the + * end). When the empty label is included (a dot at the end), the domain name + * can have 254 characters. The length of a label must be at least 1 and at most + * 63 characters. + * @see section 3.1. of https://www.rfc-editor.org/rfc/rfc1034 + * @see https://www.unicode.org/reports/tr46/#ToASCII + */ +ada_really_inline constexpr bool verify_dns_length( + std::string_view input) noexcept; -#endif -/* end file include/ada/expected.h */ -/* begin file include/ada/url_aggregator.h */ +} // namespace ada::checkers + +#endif // ADA_CHECKERS_H +/* end file include/ada/checkers.h */ +/* begin file include/ada/url.h */ /** - * @file url_aggregator.h - * @brief Declaration for the basic URL definitions + * @file url.h + * @brief Declaration for the URL */ -#ifndef ADA_URL_AGGREGATOR_H -#define ADA_URL_AGGREGATOR_H +#ifndef ADA_URL_H +#define ADA_URL_H +#include +#include +#include +#include #include #include namespace ada { /** - * @brief Lightweight URL struct. + * @brief Generic URL struct reliant on std::string instantiation. * - * @details The url_aggregator class aims to minimize temporary memory - * allocation while representing a parsed URL. Internally, it contains a single - * normalized URL (the href), and it makes available the components, mostly - * using std::string_view. + * @details To disambiguate from a valid URL string it can also be referred to + * as a URL record. A URL is a struct that represents a universal identifier. + * Unlike the url_aggregator, the ada::url represents the different components + * of a parsed URL as independent std::string instances. This makes the + * structure heavier and more reliant on memory allocations. When getting + * components from the parsed URL, a new std::string is typically constructed. + * + * @see https://url.spec.whatwg.org/#url-representation */ -struct url_aggregator : url_base { - url_aggregator() = default; - url_aggregator(const url_aggregator &u) = default; - url_aggregator(url_aggregator &&u) noexcept = default; - url_aggregator &operator=(url_aggregator &&u) noexcept = default; - url_aggregator &operator=(const url_aggregator &u) = default; - ~url_aggregator() = default; +struct url : url_base { + url() = default; + url(const url &u) = default; + url(url &&u) noexcept = default; + url &operator=(url &&u) noexcept = default; + url &operator=(const url &u) = default; + ~url() = default; - bool set_href(const std::string_view input); - bool set_host(const std::string_view input); - bool set_hostname(const std::string_view input); - bool set_protocol(const std::string_view input); - bool set_username(const std::string_view input); - bool set_password(const std::string_view input); - bool set_port(const std::string_view input); - bool set_pathname(const std::string_view input); - void set_search(const std::string_view input); - void set_hash(const std::string_view input); - inline void set_scheme(std::string_view new_scheme) noexcept; - /** @private fast function to set the scheme from a view with a colon in the - * buffer, does not change type */ - inline void set_scheme_from_view_with_colon( - std::string_view new_scheme_with_colon) noexcept; + /** + * @private + * A URL’s username is an ASCII string identifying a username. It is initially + * the empty string. + */ + std::string username{}; - inline void copy_scheme(const url_aggregator &u) noexcept; + /** + * @private + * A URL’s password is an ASCII string identifying a password. It is initially + * the empty string. + */ + std::string password{}; - [[nodiscard]] bool has_valid_domain() const noexcept override; + /** + * @private + * A URL’s host is null or a host. It is initially null. + */ + std::optional host{}; - /** @private */ - inline bool has_authority() const noexcept; - /** @private set this URL's type to file */ - inline void set_protocol_as_file(); /** - * The origin getter steps are to return the serialization of this’s URL’s - * origin. [HTML] - * @return a newly allocated string. - * @see https://url.spec.whatwg.org/#concept-url-origin + * @private + * A URL’s port is either null or a 16-bit unsigned integer that identifies a + * networking port. It is initially null. */ - [[nodiscard]] std::string get_origin() const noexcept override; + std::optional port{}; + /** - * Return the normalized string. - * This function does not allocate memory. - * It is highly efficient. - * @return a constant reference to the underlying normalized URL. - * @see https://url.spec.whatwg.org/#dom-url-href - * @see https://url.spec.whatwg.org/#concept-url-serializer + * @private + * A URL’s path is either an ASCII string or a list of zero or more ASCII + * strings, usually identifying a location. */ - inline std::string_view get_href() const noexcept; + std::string path{}; + /** - * The username getter steps are to return this’s URL’s username. - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-username + * @private + * A URL’s query is either null or an ASCII string. It is initially null. */ - [[nodiscard]] std::string_view get_username() const noexcept; + std::optional query{}; + /** - * The password getter steps are to return this’s URL’s password. - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-password + * @private + * A URL’s fragment is either null or an ASCII string that can be used for + * further processing on the resource the URL’s other components identify. It + * is initially null. */ - [[nodiscard]] std::string_view get_password() const noexcept; + std::optional hash{}; + + /** @return true if it has an host but it is the empty string */ + [[nodiscard]] inline bool has_empty_hostname() const noexcept; + /** @return true if the URL has a (non default) port */ + [[nodiscard]] inline bool has_port() const noexcept; + /** @return true if it has a host (included an empty host) */ + [[nodiscard]] inline bool has_hostname() const noexcept; + [[nodiscard]] bool has_valid_domain() const noexcept override; + /** - * Return this’s URL’s port, serialized. - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-port + * Returns a JSON string representation of this URL. */ - [[nodiscard]] std::string_view get_port() const noexcept; + std::string to_string() const override; + /** - * Return U+0023 (#), followed by this’s URL’s fragment. - * This function does not allocate memory. - * @return a lightweight std::string_view.. - * @see https://url.spec.whatwg.org/#dom-url-hash + * @see https://url.spec.whatwg.org/#dom-url-href + * @see https://url.spec.whatwg.org/#concept-url-serializer */ - [[nodiscard]] std::string_view get_hash() const noexcept; + [[nodiscard]] ada_really_inline std::string get_href() const noexcept; + + /** + * The origin getter steps are to return the serialization of this’s URL’s + * origin. [HTML] + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#concept-url-origin + */ + [[nodiscard]] std::string get_origin() const noexcept override; + + /** + * The protocol getter steps are to return this’s URL’s scheme, followed by + * U+003A (:). + * @return a newly allocated string. + * @see https://url.spec.whatwg.org/#dom-url-protocol + */ + [[nodiscard]] std::string get_protocol() const noexcept; + /** * Return url’s host, serialized, followed by U+003A (:) and url’s port, * serialized. - * This function does not allocate memory. - * When there is no host, this function returns the empty view. - * @return a lightweight std::string_view. + * When there is no host, this function returns the empty string. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-host */ - [[nodiscard]] std::string_view get_host() const noexcept; + [[nodiscard]] std::string get_host() const noexcept; + /** * Return this’s URL’s host, serialized. - * This function does not allocate memory. - * When there is no host, this function returns the empty view. - * @return a lightweight std::string_view. + * When there is no host, this function returns the empty string. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-hostname */ - [[nodiscard]] std::string_view get_hostname() const noexcept; + [[nodiscard]] std::string get_hostname() const noexcept; + /** * The pathname getter steps are to return the result of URL path serializing * this’s URL. - * This function does not allocate memory. - * @return a lightweight std::string_view. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-pathname */ - [[nodiscard]] std::string_view get_pathname() const noexcept; - /** - * Returns true if neither the search, nor the hash nor the pathname - * have been set. - * @return true if the buffer is ready to receive the path. - */ - [[nodiscard]] ada_really_inline bool is_at_path() const noexcept; + [[nodiscard]] const std::string_view get_pathname() const noexcept; + /** * Compute the pathname length in bytes witout instantiating a view or a * string. * @return size of the pathname in bytes * @see https://url.spec.whatwg.org/#dom-url-pathname */ - ada_really_inline uint32_t get_pathname_length() const noexcept; + ada_really_inline size_t get_pathname_length() const noexcept; + /** * Return U+003F (?), followed by this’s URL’s query. - * This function does not allocate memory. - * @return a lightweight std::string_view. + * @return a newly allocated string. * @see https://url.spec.whatwg.org/#dom-url-search */ - [[nodiscard]] std::string_view get_search() const noexcept; + [[nodiscard]] std::string get_search() const noexcept; + /** - * The protocol getter steps are to return this’s URL’s scheme, followed by - * U+003A (:). - * This function does not allocate memory. - * @return a lightweight std::string_view. - * @see https://url.spec.whatwg.org/#dom-url-protocol + * The username getter steps are to return this’s URL’s username. + * @return a constant reference to the underlying string. + * @see https://url.spec.whatwg.org/#dom-url-username */ - [[nodiscard]] std::string_view get_protocol() const noexcept; + [[nodiscard]] const std::string &get_username() const noexcept; /** - * A URL includes credentials if its username or password is not the empty - * string. + * @return Returns true on successful operation. + * @see https://url.spec.whatwg.org/#dom-url-username */ - [[nodiscard]] ada_really_inline bool includes_credentials() const noexcept; + bool set_username(const std::string_view input); /** - * @private - * - * A URL cannot have a username/password/port if its host is null or the empty - * string, or its scheme is "file". + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-password */ - [[nodiscard]] inline bool cannot_have_credentials_or_port() const; + bool set_password(const std::string_view input); - /** @private */ - template - bool set_host_or_hostname(const std::string_view input); + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-port + */ + bool set_port(const std::string_view input); - /** @private */ - ada_really_inline bool parse_host(std::string_view input); + /** + * This function always succeeds. + * @see https://url.spec.whatwg.org/#dom-url-hash + */ + void set_hash(const std::string_view input); - /** @private */ - inline void update_base_authority(std::string_view base_buffer, - const ada::url_components &base); - /** @private */ - inline void update_unencoded_base_hash(std::string_view input); - /** @private */ - inline void update_base_hostname(std::string_view input); - /** @private */ - inline void update_base_search(std::string_view input); - /** @private */ - inline void update_base_search(std::string_view input, - const uint8_t *query_percent_encode_set); - /** @private */ - inline void update_base_pathname(const std::string_view input); - /** @private */ - inline void update_base_username(const std::string_view input); - /** @private */ - inline void append_base_username(const std::string_view input); - /** @private */ - inline void update_base_password(const std::string_view input); - /** @private */ - inline void append_base_password(const std::string_view input); - /** @private */ - inline void update_base_port(uint32_t input); - /** @private */ - inline void append_base_pathname(const std::string_view input); - /** @private */ - inline uint32_t retrieve_base_port() const; - /** @private */ - inline void clear_base_port(); - /** @private if there is no hostname, then this function does nothing, if - * there is, we make it empty */ - inline void clear_base_hostname(); - /** @private */ - inline void clear_base_hash(); - /** @private */ - inline void clear_base_pathname() override; - /** @private */ - inline void clear_base_search() override; - /** @private */ - inline void clear_base_password(); - /** @private */ - inline bool base_fragment_has_value() const override; - /** @private */ - inline bool base_search_has_value() const override; - /** @private */ - inline bool has_dash_dot() const noexcept; - /** @private */ - void delete_dash_dot(); - /** @return true if it has an host but it is the empty string */ - [[nodiscard]] inline bool has_empty_hostname() const noexcept; - /** @return true if it has a host (included an empty host) */ - [[nodiscard]] inline bool has_hostname() const noexcept; - /** @private */ - [[nodiscard]] inline bool has_non_empty_username() const noexcept; - /** @private */ - [[nodiscard]] inline bool has_non_empty_password() const noexcept; - /** @private */ - [[nodiscard]] inline bool has_password() const noexcept; - /** @return true if the URL has a (non default) port */ - [[nodiscard]] inline bool has_port() const noexcept; - /** @private */ - inline void consume_prepared_path(std::string_view input); - /** @private */ - template - [[nodiscard]] ada_really_inline bool parse_scheme_with_colon( - const std::string_view input); + /** + * This function always succeeds. + * @see https://url.spec.whatwg.org/#dom-url-search + */ + void set_search(const std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-search + */ + bool set_pathname(const std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-host + */ + bool set_host(const std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-hostname + */ + bool set_hostname(const std::string_view input); + + /** + * @return Returns true on success. + * @see https://url.spec.whatwg.org/#dom-url-protocol + */ + bool set_protocol(const std::string_view input); - /** @private */ - ada_really_inline uint32_t replace_and_resize(uint32_t start, uint32_t end, - std::string_view input); + /** + * @see https://url.spec.whatwg.org/#dom-url-href + */ + bool set_href(const std::string_view input); + + /** + * The password getter steps are to return this’s URL’s password. + * @return a constant reference to the underlying string. + * @see https://url.spec.whatwg.org/#dom-url-password + */ + [[nodiscard]] const std::string &get_password() const noexcept; + + /** + * Return this’s URL’s port, serialized. + * @return a newly constructed string representing the port. + * @see https://url.spec.whatwg.org/#dom-url-port + */ + [[nodiscard]] std::string get_port() const noexcept; + + /** + * Return U+0023 (#), followed by this’s URL’s fragment. + * @return a newly constructed string representing the hash. + * @see https://url.spec.whatwg.org/#dom-url-hash + */ + [[nodiscard]] std::string get_hash() const noexcept; + + /** + * A URL includes credentials if its username or password is not the empty + * string. + */ + [[nodiscard]] ada_really_inline bool has_credentials() const noexcept; /** * Useful for implementing efficient serialization for the URL. @@ -5129,183 +5142,140 @@ struct url_aggregator : url_base { * * Inspired after servo/url * - * @return a constant reference to the underlying component attribute. + * @return a newly constructed component. * * @see * https://github.com/servo/rust-url/blob/b65a45515c10713f6d212e6726719a020203cc98/url/src/quirks.rs#L31 */ - [[nodiscard]] ada_really_inline const ada::url_components &get_components() + [[nodiscard]] ada_really_inline ada::url_components get_components() const noexcept; - /** - * Returns a string representation of this URL. - */ - std::string to_string() const override; - /** - * Returns a string diagram of this URL. - */ - std::string to_diagram() const; + /** @return true if the URL has a hash component */ + [[nodiscard]] inline bool has_hash() const noexcept override; + /** @return true if the URL has a search component */ + [[nodiscard]] inline bool has_search() const noexcept override; - /** - * Verifies that the parsed URL could be valid. Useful for debugging purposes. - * @return true if the URL is valid, otherwise return true of the offsets are - * possible. - */ - bool validate() const noexcept; + private: + friend ada::url ada::parser::parse_url(std::string_view, + const ada::url *); + friend ada::url_aggregator ada::parser::parse_url( + std::string_view, const ada::url_aggregator *); + friend void ada::helpers::strip_trailing_spaces_from_opaque_path( + ada::url &url) noexcept; - /** @private */ - inline void add_authority_slashes_if_needed() noexcept; + inline void update_unencoded_base_hash(std::string_view input); + inline void update_base_hostname(std::string_view input); + inline void update_base_search(std::string_view input); + inline void update_base_search(std::string_view input, + const uint8_t query_percent_encode_set[]); + inline void update_base_search(std::optional input); + inline void update_base_pathname(const std::string_view input); + inline void update_base_username(const std::string_view input); + inline void update_base_password(const std::string_view input); + inline void update_base_port(std::optional input); /** - * @private - * To optimize performance, you may indicate how much memory to allocate - * within this instance. + * Sets the host or hostname according to override condition. + * Return true on success. + * @see https://url.spec.whatwg.org/#hostname-state */ - inline void reserve(uint32_t capacity); - - /** @private */ - ada_really_inline size_t - parse_port(std::string_view view, - bool check_trailing_content = false) noexcept override; - - private: - /** @private */ - std::string buffer{}; - - /** @private */ - url_components components{}; + template + bool set_host_or_hostname(std::string_view input); /** - * @private - * * Return true on success. * @see https://url.spec.whatwg.org/#concept-ipv4-parser */ [[nodiscard]] bool parse_ipv4(std::string_view input); /** - * @private - * * Return true on success. * @see https://url.spec.whatwg.org/#concept-ipv6-parser */ [[nodiscard]] bool parse_ipv6(std::string_view input); /** - * @private - * * Return true on success. * @see https://url.spec.whatwg.org/#concept-opaque-host-parser */ [[nodiscard]] bool parse_opaque_host(std::string_view input); - /** @private */ - ada_really_inline void parse_path(std::string_view input); - -}; // url_aggregator - -inline std::ostream &operator<<(std::ostream &out, const ada::url &u); -} // namespace ada - -#endif -/* end file include/ada/url_aggregator.h */ - -#include -#include + /** + * A URL’s scheme is an ASCII string that identifies the type of URL and can + * be used to dispatch a URL for further processing after parsing. It is + * initially the empty string. We only set non_special_scheme when the scheme + * is non-special, otherwise we avoid constructing string. + * + * Special schemes are stored in ada::scheme::details::is_special_list so we + * typically do not need to store them in each url instance. + */ + std::string non_special_scheme{}; -/** - * @namespace ada::parser - * @brief Includes the definitions for supported parsers - */ -namespace ada::parser { + /** + * A URL cannot have a username/password/port if its host is null or the empty + * string, or its scheme is "file". + */ + [[nodiscard]] inline bool cannot_have_credentials_or_port() const; -/** - * Parses a url. - */ -template -result_type parse_url(std::string_view user_input, - const result_type* base_url = nullptr); + ada_really_inline size_t + parse_port(std::string_view view, + bool check_trailing_content = false) noexcept override; -extern template url_aggregator parse_url( - std::string_view user_input, const url_aggregator* base_url); -extern template url parse_url(std::string_view user_input, - const url* base_url); + /** + * Take the scheme from another URL. The scheme string is copied from the + * provided url. + */ + inline void copy_scheme(const ada::url &u); -} // namespace ada::parser + /** + * Parse the host from the provided input. We assume that + * the input does not contain spaces or tabs. Control + * characters and spaces are not trimmed (they should have + * been removed if needed). + * Return true on success. + * @see https://url.spec.whatwg.org/#host-parsing + */ + [[nodiscard]] ada_really_inline bool parse_host(std::string_view input); -#endif // ADA_PARSER_H -/* end file include/ada/parser.h */ -/* begin file include/ada/scheme-inl.h */ -/** - * @file scheme-inl.h - * @brief Definitions for the URL scheme. - */ -#ifndef ADA_SCHEME_INL_H -#define ADA_SCHEME_INL_H + template + [[nodiscard]] ada_really_inline bool parse_scheme( + const std::string_view input); + inline void clear_pathname() override; + inline void clear_search() override; + inline void set_protocol_as_file(); -namespace ada::scheme { + /** + * Parse the path from the provided input. + * Return true on success. Control characters not + * trimmed from the ends (they should have + * been removed if needed). + * + * The input is expected to be UTF-8. + * + * @see https://url.spec.whatwg.org/ + */ + ada_really_inline void parse_path(const std::string_view input); -/** - * @namespace ada::scheme::details - * @brief Includes the definitions for scheme specific entities - */ -namespace details { -// for use with is_special and get_special_port -// Spaces, if present, are removed from URL. -constexpr std::string_view is_special_list[] = {"http", " ", "https", "ws", - "ftp", "wss", "file", " "}; -// for use with get_special_port -constexpr uint16_t special_ports[] = {80, 0, 443, 80, 21, 443, 0, 0}; -} // namespace details + /** + * Set the scheme for this URL. The provided scheme should be a valid + * scheme string, be lower-cased, not contain spaces or tabs. It should + * have no spurious trailing or leading content. + */ + inline void set_scheme(std::string &&new_scheme) noexcept; -ada_really_inline constexpr bool is_special(std::string_view scheme) { - if (scheme.empty()) { - return false; - } - int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; - const std::string_view target = details::is_special_list[hash_value]; - return (target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1)); -} -constexpr uint16_t get_special_port(std::string_view scheme) noexcept { - if (scheme.empty()) { - return 0; - } - int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; - const std::string_view target = details::is_special_list[hash_value]; - if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { - return details::special_ports[hash_value]; - } else { - return 0; - } -} -constexpr uint16_t get_special_port(ada::scheme::type type) noexcept { - return details::special_ports[int(type)]; -} -constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept { - if (scheme.empty()) { - return ada::scheme::NOT_SPECIAL; - } - int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7; - const std::string_view target = details::is_special_list[hash_value]; - if ((target[0] == scheme[0]) && (target.substr(1) == scheme.substr(1))) { - return ada::scheme::type(hash_value); - } else { - return ada::scheme::NOT_SPECIAL; - } -} + /** + * Take the scheme from another URL. The scheme string is moved from the + * provided url. + */ + inline void copy_scheme(ada::url &&u) noexcept; -} // namespace ada::scheme +}; // struct url -#endif // ADA_SCHEME_H -/* end file include/ada/scheme-inl.h */ -/* begin file include/ada/url_base-inl.h */ -/** - * @file url_base-inl.h - * @brief Inline functions for url base - */ -#ifndef ADA_URL_BASE_INL_H -#define ADA_URL_BASE_INL_H +inline std::ostream &operator<<(std::ostream &out, const ada::url &u); +} // namespace ada +#endif // ADA_URL_H +/* end file include/ada/url.h */ #include #include @@ -5348,10 +5318,12 @@ url_base::scheme_default_port() const noexcept { #endif // ADA_REGULAR_VISUAL_STUDIO namespace ada { -[[nodiscard]] ada_really_inline bool url::includes_credentials() - const noexcept { +[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept { return !username.empty() || !password.empty(); } +[[nodiscard]] ada_really_inline bool url::has_port() const noexcept { + return port.has_value(); +} [[nodiscard]] inline bool url::cannot_have_credentials_or_port() const { return !host.has_value() || host.value().empty() || type == ada::scheme::type::FILE; @@ -5385,7 +5357,7 @@ size_t url::get_pathname_length() const noexcept { return path.size(); } // 2 characters for "//" and 1 character for starting index out.host_start = out.protocol_end + 2; - if (includes_credentials()) { + if (has_credentials()) { out.username_end = uint32_t(out.host_start + username.size()); out.host_start += uint32_t(username.size()); @@ -5438,7 +5410,7 @@ size_t url::get_pathname_length() const noexcept { return path.size(); } } } - if (fragment.has_value()) { + if (hash.has_value()) { out.hash_start = uint32_t(running_index); } @@ -5449,8 +5421,8 @@ inline void url::update_base_hostname(std::string_view input) { host = input; } inline void url::update_unencoded_base_hash(std::string_view input) { // We do the percent encoding - fragment = unicode::percent_encode( - input, ada::character_sets::FRAGMENT_PERCENT_ENCODE); + hash = unicode::percent_encode(input, + ada::character_sets::FRAGMENT_PERCENT_ENCODE); } inline void url::update_base_search(std::string_view input, @@ -5478,15 +5450,17 @@ inline void url::update_base_port(std::optional input) { port = input; } -inline void url::clear_base_pathname() { path = ""; } +inline void url::clear_pathname() { path.clear(); } -inline void url::clear_base_search() { query = std::nullopt; } +inline void url::clear_search() { query = std::nullopt; } -inline bool url::base_fragment_has_value() const { - return fragment.has_value(); +[[nodiscard]] inline bool url::has_hash() const noexcept { + return hash.has_value(); } -inline bool url::base_search_has_value() const { return query.has_value(); } +[[nodiscard]] inline bool url::has_search() const noexcept { + return query.has_value(); +} inline void url::set_protocol_as_file() { type = ada::scheme::type::FILE; } @@ -5513,7 +5487,7 @@ inline void url::copy_scheme(const ada::url &u) { if (host.has_value()) { output += "//"; - if (includes_credentials()) { + if (has_credentials()) { output += username; if (!password.empty()) { output += ":" + get_password(); @@ -5534,8 +5508,8 @@ inline void url::copy_scheme(const ada::url &u) { if (query.has_value()) { output += "?" + query.value(); } - if (fragment.has_value()) { - output += "#" + fragment.value(); + if (hash.has_value()) { + output += "#" + hash.value(); } return output; } @@ -5764,7 +5738,7 @@ inline void url_aggregator::update_base_search(std::string_view input) { ADA_ASSERT_TRUE(validate()); ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer)); if (input.empty()) { - clear_base_search(); + clear_search(); return; } @@ -6002,9 +5976,8 @@ inline void url_aggregator::append_base_username(const std::string_view input) { ADA_ASSERT_TRUE(validate()); } -inline void url_aggregator::clear_base_password() { - ada_log("url_aggregator::clear_base_password ", to_string(), "\n", - to_diagram()); +inline void url_aggregator::clear_password() { + ada_log("url_aggregator::clear_password ", to_string(), "\n", to_diagram()); ADA_ASSERT_TRUE(validate()); if (!has_password()) { return; @@ -6032,7 +6005,7 @@ inline void url_aggregator::update_base_password(const std::string_view input) { // TODO: Optimization opportunity. Merge the following removal functions. if (input.empty()) { - clear_base_password(); + clear_password(); // Remove username too, if it is empty. if (!has_non_empty_username()) { @@ -6133,7 +6106,7 @@ inline void url_aggregator::update_base_port(uint32_t input) { ada_log("url_aggregator::update_base_port"); ADA_ASSERT_TRUE(validate()); if (input == url_components::omitted) { - clear_base_port(); + clear_port(); return; } // calling std::to_string(input.value()) is unfortunate given that the port @@ -6159,8 +6132,8 @@ inline void url_aggregator::update_base_port(uint32_t input) { ADA_ASSERT_TRUE(validate()); } -inline void url_aggregator::clear_base_port() { - ada_log("url_aggregator::clear_base_port"); +inline void url_aggregator::clear_port() { + ada_log("url_aggregator::clear_port"); ADA_ASSERT_TRUE(validate()); if (components.port == url_components::omitted) { return; @@ -6183,8 +6156,8 @@ inline uint32_t url_aggregator::retrieve_base_port() const { return components.port; } -inline void url_aggregator::clear_base_search() { - ada_log("url_aggregator::clear_base_search"); +inline void url_aggregator::clear_search() { + ada_log("url_aggregator::clear_search"); ADA_ASSERT_TRUE(validate()); if (components.search_start == url_components::omitted) { return; @@ -6208,8 +6181,8 @@ inline void url_aggregator::clear_base_search() { ADA_ASSERT_TRUE(validate()); } -inline void url_aggregator::clear_base_hash() { - ada_log("url_aggregator::clear_base_hash"); +inline void url_aggregator::clear_hash() { + ada_log("url_aggregator::clear_hash"); ADA_ASSERT_TRUE(validate()); if (components.hash_start == url_components::omitted) { return; @@ -6225,8 +6198,8 @@ inline void url_aggregator::clear_base_hash() { ADA_ASSERT_TRUE(validate()); } -inline void url_aggregator::clear_base_pathname() { - ada_log("url_aggregator::clear_base_pathname"); +inline void url_aggregator::clear_pathname() { + ada_log("url_aggregator::clear_pathname"); ADA_ASSERT_TRUE(validate()); uint32_t ending_index = uint32_t(buffer.size()); if (components.search_start != url_components::omitted) { @@ -6250,19 +6223,18 @@ inline void url_aggregator::clear_base_pathname() { if (components.hash_start != url_components::omitted) { components.hash_start -= difference; } - ada_log("url_aggregator::clear_base_pathname completed, running checks..."); + ada_log("url_aggregator::clear_pathname completed, running checks..."); #if ADA_DEVELOPMENT_CHECKS ADA_ASSERT_EQUAL(get_pathname(), "", "pathname should have been cleared on buffer=" + buffer + " with " + components.to_string() + "\n" + to_diagram()); #endif ADA_ASSERT_TRUE(validate()); - ada_log( - "url_aggregator::clear_base_pathname completed, running checks... ok"); + ada_log("url_aggregator::clear_pathname completed, running checks... ok"); } -inline void url_aggregator::clear_base_hostname() { - ada_log("url_aggregator::clear_base_hostname"); +inline void url_aggregator::clear_hostname() { + ada_log("url_aggregator::clear_hostname"); ADA_ASSERT_TRUE(validate()); if (!has_authority()) { return; @@ -6296,18 +6268,18 @@ inline void url_aggregator::clear_base_hostname() { ADA_ASSERT_TRUE(validate()); } -inline bool url_aggregator::base_fragment_has_value() const { - ada_log("url_aggregator::base_fragment_has_value"); +[[nodiscard]] inline bool url_aggregator::has_hash() const noexcept { + ada_log("url_aggregator::has_hash"); return components.hash_start != url_components::omitted; } -inline bool url_aggregator::base_search_has_value() const { - ada_log("url_aggregator::base_search_has_value"); +[[nodiscard]] inline bool url_aggregator::has_search() const noexcept { + ada_log("url_aggregator::has_search"); return components.search_start != url_components::omitted; } -ada_really_inline bool url_aggregator::includes_credentials() const noexcept { - ada_log("url_aggregator::includes_credentials"); +ada_really_inline bool url_aggregator::has_credentials() const noexcept { + ada_log("url_aggregator::has_credentials"); return has_non_empty_username() || has_non_empty_password(); } @@ -6449,7 +6421,7 @@ ada_really_inline size_t url_aggregator::parse_port( if (r.ec == std::errc() && scheme_default_port() != parsed_port) { update_base_port(parsed_port); } else { - clear_base_port(); + clear_port(); } } return consumed; @@ -6503,13 +6475,13 @@ inline std::ostream &operator<<(std::ostream &out, #ifndef ADA_ADA_VERSION_H #define ADA_ADA_VERSION_H -#define ADA_VERSION "2.0.0" +#define ADA_VERSION "2.3.0" namespace ada { enum { ADA_VERSION_MAJOR = 2, - ADA_VERSION_MINOR = 0, + ADA_VERSION_MINOR = 3, ADA_VERSION_REVISION = 0, }; diff --git a/deps/icu-small/LICENSE b/deps/icu-small/LICENSE index 80b587723a67f7..69f48eefa82286 100644 --- a/deps/icu-small/LICENSE +++ b/deps/icu-small/LICENSE @@ -1,519 +1,519 @@ -UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE - -See Terms of Use -for definitions of Unicode Inc.’s Data Files and Software. - -NOTICE TO USER: Carefully read the following legal agreement. -BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S -DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), -YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE -TERMS AND CONDITIONS OF THIS AGREEMENT. -IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE -THE DATA FILES OR SOFTWARE. - -COPYRIGHT AND PERMISSION NOTICE - -Copyright © 1991-2022 Unicode, Inc. All rights reserved. -Distributed under the Terms of Use in https://www.unicode.org/copyright.html. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Unicode data files and any associated documentation -(the "Data Files") or Unicode software and any associated documentation -(the "Software") to deal in the Data Files or Software -without restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, and/or sell copies of -the Data Files or Software, and to permit persons to whom the Data Files -or Software are furnished to do so, provided that either -(a) this copyright and permission notice appear with all copies -of the Data Files or Software, or -(b) this copyright and permission notice appear in associated -Documentation. - -THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS -NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL 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 THE DATA FILES OR SOFTWARE. - -Except as contained in this notice, the name of a copyright holder -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in these Data Files or Software without prior -written authorization of the copyright holder. - ----------------------------------------------------------------------- - -Third-Party Software Licenses - -This section contains third-party software notices and/or additional -terms for licensed third-party software components included within ICU -libraries. - ----------------------------------------------------------------------- - -ICU License - ICU 1.8.1 to ICU 57.1 - -COPYRIGHT AND PERMISSION NOTICE - -Copyright (c) 1995-2016 International Business Machines Corporation and others -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, and/or sell copies of the Software, and to permit persons -to whom the Software is furnished to do so, provided that the above -copyright notice(s) and this permission notice appear in all copies of -the Software and that both the above copyright notice(s) and this -permission notice appear in supporting documentation. - -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 -OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY -SPECIAL 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. - -Except as contained in this notice, the name of a copyright holder -shall not be used in advertising or otherwise to promote the sale, use -or other dealings in this Software without prior written authorization -of the copyright holder. - -All trademarks and registered trademarks mentioned herein are the -property of their respective owners. - ----------------------------------------------------------------------- - -Chinese/Japanese Word Break Dictionary Data (cjdict.txt) - - # The Google Chrome software developed by Google is licensed under - # the BSD license. Other software included in this distribution is - # provided under other licenses, as set forth below. - # - # The BSD License - # http://opensource.org/licenses/bsd-license.php - # Copyright (C) 2006-2008, Google Inc. - # - # 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. - # - # - # The word list in cjdict.txt are generated by combining three word lists - # listed below with further processing for compound word breaking. The - # frequency is generated with an iterative training against Google web - # corpora. - # - # * Libtabe (Chinese) - # - https://sourceforge.net/project/?group_id=1519 - # - Its license terms and conditions are shown below. - # - # * IPADIC (Japanese) - # - http://chasen.aist-nara.ac.jp/chasen/distribution.html - # - Its license terms and conditions are shown below. - # - # ---------COPYING.libtabe ---- BEGIN-------------------- - # - # /* - # * Copyright (c) 1999 TaBE Project. - # * Copyright (c) 1999 Pai-Hsiang Hsiao. - # * 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 the TaBE Project 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 - # * REGENTS 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. - # */ - # - # /* - # * Copyright (c) 1999 Computer Systems and Communication Lab, - # * Institute of Information Science, Academia - # * Sinica. 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 the Computer Systems and Communication Lab - # * 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 - # * REGENTS 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. - # */ - # - # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, - # University of Illinois - # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 - # - # ---------------COPYING.libtabe-----END-------------------------------- - # - # - # ---------------COPYING.ipadic-----BEGIN------------------------------- - # - # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science - # and Technology. All Rights Reserved. - # - # Use, reproduction, and distribution of this software is permitted. - # Any copy of this software, whether in its original form or modified, - # must include both the above copyright notice and the following - # paragraphs. - # - # Nara Institute of Science and Technology (NAIST), - # the copyright holders, disclaims all warranties with regard to this - # software, including all implied warranties of merchantability and - # fitness, in no event shall NAIST be liable for - # any special, 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 tortuous action, arising out - # of or in connection with the use or performance of this software. - # - # A large portion of the dictionary entries - # originate from ICOT Free Software. The following conditions for ICOT - # Free Software applies to the current dictionary as well. - # - # Each User may also freely distribute the Program, whether in its - # original form or modified, to any third party or parties, PROVIDED - # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear - # on, or be attached to, the Program, which is distributed substantially - # in the same form as set out herein and that such intended - # distribution, if actually made, will neither violate or otherwise - # contravene any of the laws and regulations of the countries having - # jurisdiction over the User or the intended distribution itself. - # - # NO WARRANTY - # - # The program was produced on an experimental basis in the course of the - # research and development conducted during the project and is provided - # to users as so produced on an experimental basis. Accordingly, the - # program is provided without any warranty whatsoever, whether express, - # implied, statutory or otherwise. The term "warranty" used herein - # includes, but is not limited to, any warranty of the quality, - # performance, merchantability and fitness for a particular purpose of - # the program and the nonexistence of any infringement or violation of - # any right of any third party. - # - # Each user of the program will agree and understand, and be deemed to - # have agreed and understood, that there is no warranty whatsoever for - # the program and, accordingly, the entire risk arising from or - # otherwise connected with the program is assumed by the user. - # - # Therefore, neither ICOT, the copyright holder, or any other - # organization that participated in or was otherwise related to the - # development of the program and their respective officials, directors, - # officers and other employees shall be held liable for any and all - # damages, including, without limitation, general, special, incidental - # and consequential damages, arising out of or otherwise in connection - # with the use or inability to use the program or any product, material - # or result produced or otherwise obtained by using the program, - # regardless of whether they have been advised of, or otherwise had - # knowledge of, the possibility of such damages at any time during the - # project or thereafter. Each user will be deemed to have agreed to the - # foregoing by his or her commencement of use of the program. The term - # "use" as used herein includes, but is not limited to, the use, - # modification, copying and distribution of the program and the - # production of secondary products from the program. - # - # In the case where the program, whether in its original form or - # modified, was distributed or delivered to or received by a user from - # any person, organization or entity other than ICOT, unless it makes or - # grants independently of ICOT any specific warranty to the user in - # writing, such person, organization or entity, will also be exempted - # from and not be held liable to the user for any such damages as noted - # above as far as the program is concerned. - # - # ---------------COPYING.ipadic-----END---------------------------------- - ----------------------------------------------------------------------- - -Lao Word Break Dictionary Data (laodict.txt) - - # Copyright (C) 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. - # - # Project: https://github.com/rober42539/lao-dictionary - # Dictionary: https://github.com/rober42539/lao-dictionary/laodict.txt - # License: https://github.com/rober42539/lao-dictionary/LICENSE.txt - # (copied below) - # - # This file is derived from the above dictionary version of Nov 22, 2020 - # ---------------------------------------------------------------------- - # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. - # 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. - # - # 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 HOLDER 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. - # -------------------------------------------------------------------------- - ----------------------------------------------------------------------- - -Burmese Word Break Dictionary Data (burmesedict.txt) - - # Copyright (c) 2014 International Business Machines Corporation - # and others. All Rights Reserved. - # - # This list is part of a project hosted at: - # github.com/kanyawtech/myanmar-karen-word-lists - # - # -------------------------------------------------------------------------- - # Copyright (c) 2013, LeRoy Benjamin Sharon - # 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 Myanmar Karen Word Lists, 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 HOLDER 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. - # -------------------------------------------------------------------------- - ----------------------------------------------------------------------- - -Time Zone Database - - ICU uses the public domain data and code derived from Time Zone -Database for its time zone support. The ownership of the TZ database -is explained in BCP 175: Procedure for Maintaining the Time Zone -Database section 7. - - # 7. Database Ownership - # - # The TZ database itself is not an IETF Contribution or an IETF - # document. Rather it is a pre-existing and regularly updated work - # that is in the public domain, and is intended to remain in the - # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do - # not apply to the TZ Database or contributions that individuals make - # to it. Should any claims be made and substantiated against the TZ - # Database, the organization that is providing the IANA - # Considerations defined in this RFC, under the memorandum of - # understanding with the IETF, currently ICANN, may act in accordance - # with all competent court orders. No ownership claims will be made - # by ICANN or the IETF Trust on the database or the code. Any person - # making a contribution to the database or code waives all rights to - # future claims in that contribution or in the TZ Database. - ----------------------------------------------------------------------- - -Google double-conversion - -Copyright 2006-2011, 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. - ----------------------------------------------------------------------- - -File: aclocal.m4 (only for ICU4C) -Section: pkg.m4 - Macros to locate and utilise pkg-config. - - -Copyright © 2004 Scott James Remnant . -Copyright © 2012-2015 Dan Nicholson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. - -As a special exception to the GNU General Public License, if you -distribute this file as part of a program that contains a -configuration script generated by Autoconf, you may include it under -the same distribution terms that you use for the rest of that -program. - - -(The condition for the exception is fulfilled because -ICU4C includes a configuration script generated by Autoconf, -namely the `configure` script.) - ----------------------------------------------------------------------- - -File: config.guess (only for ICU4C) - - -This file is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, see . - -As a special exception to the GNU General Public License, if you -distribute this file as part of a program that contains a -configuration script generated by Autoconf, you may include it under -the same distribution terms that you use for the rest of that -program. This Exception is an additional permission under section 7 -of the GNU General Public License, version 3 ("GPLv3"). - - -(The condition for the exception is fulfilled because -ICU4C includes a configuration script generated by Autoconf, -namely the `configure` script.) - ----------------------------------------------------------------------- - -File: install-sh (only for ICU4C) - - -Copyright 1991 by the Massachusetts Institute of Technology - -Permission to use, copy, modify, distribute, and sell this software and its -documentation for any purpose is hereby granted without fee, provided that -the above copyright notice appear in all copies and that both that -copyright notice and this permission notice appear in supporting -documentation, and that the name of M.I.T. not be used in advertising or -publicity pertaining to distribution of the software without specific, -written prior permission. M.I.T. makes no representations about the -suitability of this software for any purpose. It is provided "as is" -without express or implied warranty. +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + +See Terms of Use +for definitions of Unicode Inc.’s Data Files and Software. + +NOTICE TO USER: Carefully read the following legal agreement. +BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S +DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), +YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. +IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE +THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2023 Unicode, Inc. All rights reserved. +Distributed under the Terms of Use in https://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the "Data Files") or Unicode software and any associated documentation +(the "Software") to deal in the Data Files or Software +without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files +or Software are furnished to do so, provided that either +(a) this copyright and permission notice appear with all copies +of the Data Files or Software, or +(b) this copyright and permission notice appear in associated +Documentation. + +THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL 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 THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. + +---------------------------------------------------------------------- + +Third-Party Software Licenses + +This section contains third-party software notices and/or additional +terms for licensed third-party software components included within ICU +libraries. + +---------------------------------------------------------------------- + +ICU License - ICU 1.8.1 to ICU 57.1 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1995-2016 International Business Machines Corporation and others +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, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, provided that the above +copyright notice(s) and this permission notice appear in all copies of +the Software and that both the above copyright notice(s) and this +permission notice appear in supporting documentation. + +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 +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY +SPECIAL 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. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization +of the copyright holder. + +All trademarks and registered trademarks mentioned herein are the +property of their respective owners. + +---------------------------------------------------------------------- + +Chinese/Japanese Word Break Dictionary Data (cjdict.txt) + + # The Google Chrome software developed by Google is licensed under + # the BSD license. Other software included in this distribution is + # provided under other licenses, as set forth below. + # + # The BSD License + # http://opensource.org/licenses/bsd-license.php + # Copyright (C) 2006-2008, Google Inc. + # + # 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. + # + # + # The word list in cjdict.txt are generated by combining three word lists + # listed below with further processing for compound word breaking. The + # frequency is generated with an iterative training against Google web + # corpora. + # + # * Libtabe (Chinese) + # - https://sourceforge.net/project/?group_id=1519 + # - Its license terms and conditions are shown below. + # + # * IPADIC (Japanese) + # - http://chasen.aist-nara.ac.jp/chasen/distribution.html + # - Its license terms and conditions are shown below. + # + # ---------COPYING.libtabe ---- BEGIN-------------------- + # + # /* + # * Copyright (c) 1999 TaBE Project. + # * Copyright (c) 1999 Pai-Hsiang Hsiao. + # * 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 the TaBE Project 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 + # * REGENTS 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. + # */ + # + # /* + # * Copyright (c) 1999 Computer Systems and Communication Lab, + # * Institute of Information Science, Academia + # * Sinica. 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 the Computer Systems and Communication Lab + # * 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 + # * REGENTS 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. + # */ + # + # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, + # University of Illinois + # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 + # + # ---------------COPYING.libtabe-----END-------------------------------- + # + # + # ---------------COPYING.ipadic-----BEGIN------------------------------- + # + # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science + # and Technology. All Rights Reserved. + # + # Use, reproduction, and distribution of this software is permitted. + # Any copy of this software, whether in its original form or modified, + # must include both the above copyright notice and the following + # paragraphs. + # + # Nara Institute of Science and Technology (NAIST), + # the copyright holders, disclaims all warranties with regard to this + # software, including all implied warranties of merchantability and + # fitness, in no event shall NAIST be liable for + # any special, 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 tortuous action, arising out + # of or in connection with the use or performance of this software. + # + # A large portion of the dictionary entries + # originate from ICOT Free Software. The following conditions for ICOT + # Free Software applies to the current dictionary as well. + # + # Each User may also freely distribute the Program, whether in its + # original form or modified, to any third party or parties, PROVIDED + # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear + # on, or be attached to, the Program, which is distributed substantially + # in the same form as set out herein and that such intended + # distribution, if actually made, will neither violate or otherwise + # contravene any of the laws and regulations of the countries having + # jurisdiction over the User or the intended distribution itself. + # + # NO WARRANTY + # + # The program was produced on an experimental basis in the course of the + # research and development conducted during the project and is provided + # to users as so produced on an experimental basis. Accordingly, the + # program is provided without any warranty whatsoever, whether express, + # implied, statutory or otherwise. The term "warranty" used herein + # includes, but is not limited to, any warranty of the quality, + # performance, merchantability and fitness for a particular purpose of + # the program and the nonexistence of any infringement or violation of + # any right of any third party. + # + # Each user of the program will agree and understand, and be deemed to + # have agreed and understood, that there is no warranty whatsoever for + # the program and, accordingly, the entire risk arising from or + # otherwise connected with the program is assumed by the user. + # + # Therefore, neither ICOT, the copyright holder, or any other + # organization that participated in or was otherwise related to the + # development of the program and their respective officials, directors, + # officers and other employees shall be held liable for any and all + # damages, including, without limitation, general, special, incidental + # and consequential damages, arising out of or otherwise in connection + # with the use or inability to use the program or any product, material + # or result produced or otherwise obtained by using the program, + # regardless of whether they have been advised of, or otherwise had + # knowledge of, the possibility of such damages at any time during the + # project or thereafter. Each user will be deemed to have agreed to the + # foregoing by his or her commencement of use of the program. The term + # "use" as used herein includes, but is not limited to, the use, + # modification, copying and distribution of the program and the + # production of secondary products from the program. + # + # In the case where the program, whether in its original form or + # modified, was distributed or delivered to or received by a user from + # any person, organization or entity other than ICOT, unless it makes or + # grants independently of ICOT any specific warranty to the user in + # writing, such person, organization or entity, will also be exempted + # from and not be held liable to the user for any such damages as noted + # above as far as the program is concerned. + # + # ---------------COPYING.ipadic-----END---------------------------------- + +---------------------------------------------------------------------- + +Lao Word Break Dictionary Data (laodict.txt) + + # Copyright (C) 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. + # + # Project: https://github.com/rober42539/lao-dictionary + # Dictionary: https://github.com/rober42539/lao-dictionary/laodict.txt + # License: https://github.com/rober42539/lao-dictionary/LICENSE.txt + # (copied below) + # + # This file is derived from the above dictionary version of Nov 22, 2020 + # ---------------------------------------------------------------------- + # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. + # 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. + # + # 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 HOLDER 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. + # -------------------------------------------------------------------------- + +---------------------------------------------------------------------- + +Burmese Word Break Dictionary Data (burmesedict.txt) + + # Copyright (c) 2014 International Business Machines Corporation + # and others. All Rights Reserved. + # + # This list is part of a project hosted at: + # github.com/kanyawtech/myanmar-karen-word-lists + # + # -------------------------------------------------------------------------- + # Copyright (c) 2013, LeRoy Benjamin Sharon + # 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 Myanmar Karen Word Lists, 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 HOLDER 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. + # -------------------------------------------------------------------------- + +---------------------------------------------------------------------- + +Time Zone Database + + ICU uses the public domain data and code derived from Time Zone +Database for its time zone support. The ownership of the TZ database +is explained in BCP 175: Procedure for Maintaining the Time Zone +Database section 7. + + # 7. Database Ownership + # + # The TZ database itself is not an IETF Contribution or an IETF + # document. Rather it is a pre-existing and regularly updated work + # that is in the public domain, and is intended to remain in the + # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do + # not apply to the TZ Database or contributions that individuals make + # to it. Should any claims be made and substantiated against the TZ + # Database, the organization that is providing the IANA + # Considerations defined in this RFC, under the memorandum of + # understanding with the IETF, currently ICANN, may act in accordance + # with all competent court orders. No ownership claims will be made + # by ICANN or the IETF Trust on the database or the code. Any person + # making a contribution to the database or code waives all rights to + # future claims in that contribution or in the TZ Database. + +---------------------------------------------------------------------- + +Google double-conversion + +Copyright 2006-2011, 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. + +---------------------------------------------------------------------- + +File: aclocal.m4 (only for ICU4C) +Section: pkg.m4 - Macros to locate and utilise pkg-config. + + +Copyright © 2004 Scott James Remnant . +Copyright © 2012-2015 Dan Nicholson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. + +As a special exception to the GNU General Public License, if you +distribute this file as part of a program that contains a +configuration script generated by Autoconf, you may include it under +the same distribution terms that you use for the rest of that +program. + + +(The condition for the exception is fulfilled because +ICU4C includes a configuration script generated by Autoconf, +namely the `configure` script.) + +---------------------------------------------------------------------- + +File: config.guess (only for ICU4C) + + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . + +As a special exception to the GNU General Public License, if you +distribute this file as part of a program that contains a +configuration script generated by Autoconf, you may include it under +the same distribution terms that you use for the rest of that +program. This Exception is an additional permission under section 7 +of the GNU General Public License, version 3 ("GPLv3"). + + +(The condition for the exception is fulfilled because +ICU4C includes a configuration script generated by Autoconf, +namely the `configure` script.) + +---------------------------------------------------------------------- + +File: install-sh (only for ICU4C) + + +Copyright 1991 by the Massachusetts Institute of Technology + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of M.I.T. not be used in advertising or +publicity pertaining to distribution of the software without specific, +written prior permission. M.I.T. makes no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. diff --git a/deps/icu-small/README-FULL-ICU.txt b/deps/icu-small/README-FULL-ICU.txt index 8ca7dfb8647741..5913e149bc5749 100644 --- a/deps/icu-small/README-FULL-ICU.txt +++ b/deps/icu-small/README-FULL-ICU.txt @@ -1,8 +1,8 @@ ICU sources - auto generated by shrink-icu-src.py This directory contains the ICU subset used by --with-intl=full-icu -It is a strict subset of ICU 72 source files with the following exception(s): -* deps/icu-small/source/data/in/icudt72l.dat.bz2 : compressed data file +It is a strict subset of ICU 73 source files with the following exception(s): +* deps/icu-small/source/data/in/icudt73l.dat.bz2 : compressed data file To rebuild this directory, see ../../tools/icu/README.md diff --git a/deps/icu-small/source/.clang-format b/deps/icu-small/source/.clang-format index 83cbf646a43de1..0be679861d688c 100644 --- a/deps/icu-small/source/.clang-format +++ b/deps/icu-small/source/.clang-format @@ -1,11 +1,11 @@ -# © 2020 and later: Unicode, Inc. and others. -# License & terms of use: http://www.unicode.org/copyright.html - ---- -Language: Cpp -BasedOnStyle: LLVM -IndentWidth: 4 -ColumnLimit: 105 -AllowShortBlocksOnASingleLine: false -AllowShortIfStatementsOnASingleLine: true -... +# © 2020 and later: Unicode, Inc. and others. +# License & terms of use: http://www.unicode.org/copyright.html + +--- +Language: Cpp +BasedOnStyle: LLVM +IndentWidth: 4 +ColumnLimit: 105 +AllowShortBlocksOnASingleLine: false +AllowShortIfStatementsOnASingleLine: true +... diff --git a/deps/icu-small/source/common/BUILD.bazel b/deps/icu-small/source/common/BUILD.bazel index e385d3b243faeb..33e93a269fe273 100644 --- a/deps/icu-small/source/common/BUILD.bazel +++ b/deps/icu-small/source/common/BUILD.bazel @@ -1,1213 +1,1214 @@ -# © 2021 and later: Unicode, Inc. and others. -# License & terms of use: http://www.unicode.org/copyright.html - -# This file defines Bazel targets for a subset of ICU4C "common" library header and source files. -# The configuration of dependencies among targets is strongly assisted by the -# file in depstest that maintains such information, at -# icu4c/source/test/depstest/dependencies.txt . - -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") - -package( - default_visibility = ["//visibility:public"], -) - -# When compiling code in the `common` dir, the constant -# `U_COMMON_IMPLEMENTATION` needs to be defined. See -# https://unicode-org.github.io/icu/userguide/howtouseicu#c-with-your-own-build-system . - -# If linker errors occur, then this may be a sign that the dependencies were -# not specified correctly. Use dependencies.txt in depstest for assistance. See -# https://stackoverflow.com/q/66111709/2077918 . - -cc_library( - name = "headers", - hdrs = glob([ - "unicode/*.h", # public - "*.h", # internal - ], - # Instead of using these checked-in files, our Bazel build process - # regenerates them and then uses the new versions. - # Same list of .h files as in icu4c/source/data/unidata/clean.sh. - exclude = ["norm2_nfc_data.h", "propname_data.h", "*_props_data.h"], - ), - # We need to add includes in order to preserve existing source files' - # include directives that use traditional paths, not paths relative to - # Bazel workspace: - # https://stackoverflow.com/a/65635893/2077918 - includes = ["."], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "platform", - srcs = [ - "cmemory.cpp", - "uobject.cpp", - "cstring.cpp", - "cwchar.cpp", - "uinvchar.cpp", - "charstr.cpp", - "unistr.cpp", - "appendable.cpp", - "stringpiece.cpp", - "ustrtrns.cpp", - "ustring.cpp", - "ustrfmt.cpp", - "utf_impl.cpp", - "putil.cpp", - "ucln_cmn.cpp", - "udataswp.cpp", - "umath.cpp", - "umutex.cpp", - "sharedobject.cpp", - "utrace.cpp", - ], - deps = [ - ":headers", - # omit other deps b/c they are sys symbols - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], - linkopts = ["-ldl"], -) - -cc_library( - name = "utrie", - srcs = ["utrie.cpp"], - deps = [":platform"], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "utrie2", - srcs = ["utrie2.cpp"], - deps = [":platform"], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "utrie2_builder", - srcs = ["utrie2_builder.cpp"], - deps = [ - ":utrie", - ":utrie2", - ":platform", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "ucptrie", - srcs = ["ucptrie.cpp"], - deps = [":platform"], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "umutablecptrie", - srcs = ["umutablecptrie.cpp"], - deps = [":ucptrie"], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "bytestrie", - srcs = ["bytestrie.cpp"], - deps = [":platform"], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "bytestriebuilder", - srcs = ["bytestriebuilder.cpp"], - deps = [ - ":bytestrie", - ":stringtriebuilder", - ":sort", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "stringtriebuilder", - srcs = ["stringtriebuilder.cpp"], - deps = [ - ":uhash", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "uhash", - hdrs = [ - "uhash.h", - ], - srcs = [ - "uhash.cpp", - ], - deps = [ - ":headers", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "errorcode", - hdrs = [ - ], - srcs = [ - "errorcode.cpp", - ], - includes = ["."], - deps = [ - ":platform", - ":utypes", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "utypes", - srcs = [ - "utypes.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "uniset", - srcs = [ - "uniset.cpp", - "unifilt.cpp", - "unisetspan.cpp", - "bmpset.cpp", - "util.cpp", - "unifunct.cpp", - "usetiter.cpp", - ], - includes = ["."], - deps = [ - ":patternprops", - ":uvector", - ":headers", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "patternprops", - srcs = [ - "patternprops.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "propsvec", - srcs = [ - "propsvec.cpp", - ], - includes = ["."], - deps = [ - ":sort", - ":utrie2_builder", - ":headers", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "propname", - srcs = [ - "propname.cpp", - "propname_data.h", - ], - includes = ["."], - deps = [ - ":bytestrie", - ":headers", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -# Note: The cc_library target names "uvector32" and "uvector64" match the -# dependencies.txt group names, but the filenames are "uvectr32.*"/"uvectr64.*". -cc_library( - name = "uvector32", - srcs = [ - "uvectr32.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":platform", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "uvector64", - srcs = [ - "uvectr64.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":platform", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "sort", - srcs = [ - "uarrsort.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "uvector", - srcs = [ - "uvector.cpp", - ], - includes = ["."], - deps = [ - ":platform", - ":sort", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "breakiterator", - srcs = [ - "brkiter.cpp", - "brkeng.cpp", - "dictbe.cpp", - "dictionarydata.cpp", - "filteredbrk.cpp", - "lstmbe.cpp", - "rbbi.cpp", - "rbbi_cache.cpp", - "rbbidata.cpp", - "rbbinode.cpp", - "rbbirb.cpp", - "rbbiscan.cpp", - "rbbisetb.cpp", - "rbbistbl.cpp", - "rbbitblb.cpp", - "ubrk.cpp", - ], - includes = ["."], - deps = [ - ":bytestrie", - ":headers", - ":normlzr", - ":resourcebundle", - ":schriter", - ":service_registration", - ":ucharstrie", - ":ucharstriebuilder", - ":uhash", - ":uniset_core", - ":uniset_props", - ":ustack", - ":utext", - ":utrie2_builder", - ":uvector32", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "bytesinkutil", - srcs = [ - "bytesinkutil.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":bytestream", - ":edits", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "bytestream", - srcs = [ - "bytestream.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":platform", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "canonical_iterator", - srcs = [ - "caniter.cpp", - ], - deps = [ - ":normalizer2", - ":usetiter", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "characterproperties", - srcs = [ - "characterproperties.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":emojiprops", - ":ucptrie", - ":umutablecptrie", - ":uniset_core", - ":uprops", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "chariter", - srcs = [ - "chariter.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":platform", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "edits", - srcs = [ - "edits.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":icu_utility", - ":platform", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "filterednormalizer2", - srcs = [ - "filterednormalizer2.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":normalizer2", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "hashtable", - srcs = [ - "uhash_us.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":uhash", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "icu_utility", - srcs = [ - "util.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":patternprops", - ":platform", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "loadednormalizer2", - srcs = [ - "loadednormalizer2impl.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":normalizer2", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "locale_display_names", - srcs = [ - "locdispnames.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":locresdata", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "locresdata", - srcs = [ - "locresdata.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":resourcebundle", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "normlzr", - srcs = [ - "normlzr.cpp", - ], - includes = ["."], - deps = [ - ":filterednormalizer2", - ":headers", - ":schriter", - ":uniset_props", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "parsepos", - srcs = [ - "parsepos.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":platform", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "resourcebundle", - srcs = [ - "localebuilder.cpp", - "locavailable.cpp", - "locbased.cpp", - "locid.cpp", - "loclikely.cpp", - "locmap.cpp", - "resbund.cpp", - "resource.cpp", - "uloc.cpp", - "uloc_tag.cpp", - "uloc_keytype.cpp", - "uresbund.cpp", - "uresdata.cpp", - "wintz.cpp", - ], - includes = ["."], - deps = [ - ":bytesinkutil", - ":errorcode", - ":headers", - ":propname", - ":sort", - ":stringenumeration", - ":ucol_swp", - ":udata", - ":uhash", - ":uscript_props", - ":uvector", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "schriter", - srcs = [ - "schriter.cpp", - "uchriter.cpp", - ], - includes = ["."], - deps = [ - ":chariter", - ":headers", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "service_registration", - srcs = [ - "locutil.cpp", - "serv.cpp", - "servlk.cpp", - "servlkf.cpp", - "servls.cpp", - "servnotf.cpp", - "servrbf.cpp", - "servslkf.cpp", - ], - includes = ["."], - deps = [ - ":hashtable", - ":headers", - ":locale_display_names", - ":resourcebundle", - ":uvector", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "stringenumeration", - srcs = [ - "uenum.cpp", - "ustrenum.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":platform", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "ubidi_props", - srcs = [ - "ubidi_props.cpp", - "ubidi_props_data.h", - ], - includes = ["."], - deps = [ - ":headers", - ":utrie2", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "ucase", - srcs = [ - "ucase.cpp", - "ucase_props_data.h", - ], - includes = ["."], - deps = [ - ":headers", - ":utrie2", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "uchar", - srcs = [ - "uchar.cpp", - "uchar_props_data.h", - ], - includes = ["."], - deps = [ - ":headers", - ":utrie2", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "emojiprops", - srcs = [ - "emojiprops.cpp", - "emojiprops.h", - ], - includes = ["."], - deps = [ - ":headers", - ":ucharstrie", - ":ucharstrieiterator", - ":ucptrie", - ":udata", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "ucharstrie", - srcs = [ - "ucharstrie.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":platform", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "ucharstriebuilder", - srcs = [ - "ucharstriebuilder.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":sort", - ":stringtriebuilder", - ":ucharstrie", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "ucharstrieiterator", - srcs = [ - "ucharstrieiterator.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":ucharstrie", - ":uvector32", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "ucol_swp", - srcs = [ - "ucol_swp.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":utrie_swap", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "udata", - srcs = [ - "restrace.cpp", - "ucmndata.cpp", - "udata.cpp", - "udatamem.cpp", - "umapfile.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":icu_utility", - ":platform", - ":uhash", - "//icu4c/source/stubdata", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "uiter", - srcs = [ - "uiter.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":platform", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "ulist", - srcs = [ - "ulist.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":platform", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "unames", - srcs = [ - "unames.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":uchar", - ":udata", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "unifiedcache", - srcs = [ - "unifiedcache.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":platform", - ":uhash", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "uniset_core", - srcs = [ - "bmpset.cpp", - "unifilt.cpp", - "unifunct.cpp", - "uniset.cpp", - "unisetspan.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":icu_utility", - ":patternprops", - ":uvector", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "uniset_closure", - srcs = [ - "uniset_closure.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":uniset_core", - ":unistr_case_locale", - ":unistr_titlecase_brkiter", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "uniset_props", - srcs = [ - "uniset_props.cpp", - "ruleiter.cpp", - ], - includes = ["."], - deps = [ - ":characterproperties", - ":headers", - ":parsepos", - ":propname", - ":resourcebundle", - ":unames", - ":uniset_core", - ":unistr_case", - ":uprops", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "unistr_case", - srcs = [ - "unistr_case.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":ustring_case", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "unistr_case_locale", - srcs = [ - "unistr_case_locale.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":unistr_case", - ":ustring_case_locale", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "unistr_titlecase_brkiter", - srcs = [ - "unistr_titlecase_brkiter.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":ustr_titlecase_brkiter", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "uprops", - srcs = [ - "uprops.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":emojiprops", - ":loadednormalizer2", - ":normalizer2", - ":ubidi_props", - ":ucase", - ":uchar", - ":unistr_case", - ":ustring_case", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "uscript_props", - srcs = [ - "uscript_props.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":platform", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "uset", - srcs = [ - "uset.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":platform", - ":uniset_core", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "uset_props", - srcs = [ - "uset_props.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":uniset_closure", - ":uniset_core", - ":uniset_props", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "usetiter", - srcs = [ - "usetiter.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":platform", - ":uniset_core", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "ustack", - srcs = [ - "ustack.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":uvector", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "ustr_titlecase_brkiter", - srcs = [ - "ustr_titlecase_brkiter.cpp", - ], - includes = ["."], - deps = [ - ":breakiterator", - ":headers", - ":ucase", - ":ustring_case_locale", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "ustring_case", - srcs = [ - "ustrcase.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":ucase", - ":uchar", - ":edits", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "ustring_case_locale", - srcs = [ - "ustrcase_locale.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":resourcebundle", - ":ustring_case", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "utext", - srcs = [ - "utext.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":ucase", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -cc_library( - name = "utrie_swap", - srcs = [ - "utrie_swap.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":udata", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) - -# This target depends on a header file that contains NFC/NFD normalization data. -# This header file is generated by a script (generate.sh) that invokes the gennorm2 binary. -# See the Unicode update change log (changes.txt). -cc_library( - name = "normalizer2", - srcs = [ - "norm2_nfc_data.h", # generated by gennorm2 - "normalizer2.cpp", - "normalizer2impl.cpp", - ], - includes = ["."], - hdrs = [ - "normalizer2impl.h", - ], - deps = [ - ":headers", - ], - local_defines = [ - "U_COMMON_IMPLEMENTATION", - ], -) +# © 2021 and later: Unicode, Inc. and others. +# License & terms of use: http://www.unicode.org/copyright.html + +# This file defines Bazel targets for a subset of ICU4C "common" library header and source files. +# The configuration of dependencies among targets is strongly assisted by the +# file in depstest that maintains such information, at +# icu4c/source/test/depstest/dependencies.txt . + +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") + +package( + default_visibility = ["//visibility:public"], +) + +# When compiling code in the `common` dir, the constant +# `U_COMMON_IMPLEMENTATION` needs to be defined. See +# https://unicode-org.github.io/icu/userguide/howtouseicu#c-with-your-own-build-system . + +# If linker errors occur, then this may be a sign that the dependencies were +# not specified correctly. Use dependencies.txt in depstest for assistance. See +# https://stackoverflow.com/q/66111709/2077918 . + +cc_library( + name = "headers", + hdrs = glob([ + "unicode/*.h", # public + "*.h", # internal + ], + # Instead of using these checked-in files, our Bazel build process + # regenerates them and then uses the new versions. + # Same list of .h files as in icu4c/source/data/unidata/clean.sh. + exclude = ["norm2_nfc_data.h", "propname_data.h", "*_props_data.h"], + ), + # We need to add includes in order to preserve existing source files' + # include directives that use traditional paths, not paths relative to + # Bazel workspace: + # https://stackoverflow.com/a/65635893/2077918 + includes = ["."], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "platform", + srcs = [ + "cmemory.cpp", + "uobject.cpp", + "cstring.cpp", + "cwchar.cpp", + "uinvchar.cpp", + "charstr.cpp", + "unistr.cpp", + "appendable.cpp", + "stringpiece.cpp", + "ustrtrns.cpp", + "ustring.cpp", + "ustrfmt.cpp", + "utf_impl.cpp", + "putil.cpp", + "ucln_cmn.cpp", + "udataswp.cpp", + "umath.cpp", + "umutex.cpp", + "sharedobject.cpp", + "utrace.cpp", + ], + deps = [ + ":headers", + # omit other deps b/c they are sys symbols + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], + linkopts = ["-ldl"], +) + +cc_library( + name = "utrie", + srcs = ["utrie.cpp"], + deps = [":platform"], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "utrie2", + srcs = ["utrie2.cpp"], + deps = [":platform"], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "utrie2_builder", + srcs = ["utrie2_builder.cpp"], + deps = [ + ":utrie", + ":utrie2", + ":platform", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "ucptrie", + srcs = ["ucptrie.cpp"], + deps = [":platform"], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "umutablecptrie", + srcs = ["umutablecptrie.cpp"], + deps = [":ucptrie"], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "bytestrie", + srcs = ["bytestrie.cpp"], + deps = [":platform"], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "bytestriebuilder", + srcs = ["bytestriebuilder.cpp"], + deps = [ + ":bytestrie", + ":stringtriebuilder", + ":sort", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "stringtriebuilder", + srcs = ["stringtriebuilder.cpp"], + deps = [ + ":uhash", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "uhash", + hdrs = [ + "uhash.h", + ], + srcs = [ + "uhash.cpp", + ], + deps = [ + ":headers", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "errorcode", + hdrs = [ + ], + srcs = [ + "errorcode.cpp", + ], + includes = ["."], + deps = [ + ":platform", + ":utypes", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "utypes", + srcs = [ + "utypes.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "uniset", + srcs = [ + "uniset.cpp", + "unifilt.cpp", + "unisetspan.cpp", + "bmpset.cpp", + "util.cpp", + "unifunct.cpp", + "usetiter.cpp", + ], + includes = ["."], + deps = [ + ":patternprops", + ":uvector", + ":headers", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "patternprops", + srcs = [ + "patternprops.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "propsvec", + srcs = [ + "propsvec.cpp", + ], + includes = ["."], + deps = [ + ":sort", + ":utrie2_builder", + ":headers", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "propname", + srcs = [ + "propname.cpp", + "propname_data.h", + ], + includes = ["."], + deps = [ + ":bytestrie", + ":headers", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +# Note: The cc_library target names "uvector32" and "uvector64" match the +# dependencies.txt group names, but the filenames are "uvectr32.*"/"uvectr64.*". +cc_library( + name = "uvector32", + srcs = [ + "uvectr32.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":platform", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "uvector64", + srcs = [ + "uvectr64.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":platform", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "sort", + srcs = [ + "uarrsort.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "uvector", + srcs = [ + "uvector.cpp", + ], + includes = ["."], + deps = [ + ":platform", + ":sort", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "breakiterator", + srcs = [ + "brkiter.cpp", + "brkeng.cpp", + "dictbe.cpp", + "dictionarydata.cpp", + "filteredbrk.cpp", + "lstmbe.cpp", + "mlbe.cpp", + "rbbi.cpp", + "rbbi_cache.cpp", + "rbbidata.cpp", + "rbbinode.cpp", + "rbbirb.cpp", + "rbbiscan.cpp", + "rbbisetb.cpp", + "rbbistbl.cpp", + "rbbitblb.cpp", + "ubrk.cpp", + ], + includes = ["."], + deps = [ + ":bytestrie", + ":headers", + ":normlzr", + ":resourcebundle", + ":schriter", + ":service_registration", + ":ucharstrie", + ":ucharstriebuilder", + ":uhash", + ":uniset_core", + ":uniset_props", + ":ustack", + ":utext", + ":utrie2_builder", + ":uvector32", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "bytesinkutil", + srcs = [ + "bytesinkutil.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":bytestream", + ":edits", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "bytestream", + srcs = [ + "bytestream.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":platform", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "canonical_iterator", + srcs = [ + "caniter.cpp", + ], + deps = [ + ":normalizer2", + ":usetiter", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "characterproperties", + srcs = [ + "characterproperties.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":emojiprops", + ":ucptrie", + ":umutablecptrie", + ":uniset_core", + ":uprops", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "chariter", + srcs = [ + "chariter.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":platform", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "edits", + srcs = [ + "edits.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":icu_utility", + ":platform", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "filterednormalizer2", + srcs = [ + "filterednormalizer2.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":normalizer2", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "hashtable", + srcs = [ + "uhash_us.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":uhash", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "icu_utility", + srcs = [ + "util.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":patternprops", + ":platform", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "loadednormalizer2", + srcs = [ + "loadednormalizer2impl.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":normalizer2", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "locale_display_names", + srcs = [ + "locdispnames.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":locresdata", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "locresdata", + srcs = [ + "locresdata.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":resourcebundle", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "normlzr", + srcs = [ + "normlzr.cpp", + ], + includes = ["."], + deps = [ + ":filterednormalizer2", + ":headers", + ":schriter", + ":uniset_props", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "parsepos", + srcs = [ + "parsepos.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":platform", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "resourcebundle", + srcs = [ + "localebuilder.cpp", + "locavailable.cpp", + "locbased.cpp", + "locid.cpp", + "loclikely.cpp", + "locmap.cpp", + "resbund.cpp", + "resource.cpp", + "uloc.cpp", + "uloc_tag.cpp", + "uloc_keytype.cpp", + "uresbund.cpp", + "uresdata.cpp", + "wintz.cpp", + ], + includes = ["."], + deps = [ + ":bytesinkutil", + ":errorcode", + ":headers", + ":propname", + ":sort", + ":stringenumeration", + ":ucol_swp", + ":udata", + ":uhash", + ":uscript_props", + ":uvector", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "schriter", + srcs = [ + "schriter.cpp", + "uchriter.cpp", + ], + includes = ["."], + deps = [ + ":chariter", + ":headers", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "service_registration", + srcs = [ + "locutil.cpp", + "serv.cpp", + "servlk.cpp", + "servlkf.cpp", + "servls.cpp", + "servnotf.cpp", + "servrbf.cpp", + "servslkf.cpp", + ], + includes = ["."], + deps = [ + ":hashtable", + ":headers", + ":locale_display_names", + ":resourcebundle", + ":uvector", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "stringenumeration", + srcs = [ + "uenum.cpp", + "ustrenum.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":platform", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "ubidi_props", + srcs = [ + "ubidi_props.cpp", + "ubidi_props_data.h", + ], + includes = ["."], + deps = [ + ":headers", + ":utrie2", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "ucase", + srcs = [ + "ucase.cpp", + "ucase_props_data.h", + ], + includes = ["."], + deps = [ + ":headers", + ":utrie2", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "uchar", + srcs = [ + "uchar.cpp", + "uchar_props_data.h", + ], + includes = ["."], + deps = [ + ":headers", + ":utrie2", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "emojiprops", + srcs = [ + "emojiprops.cpp", + "emojiprops.h", + ], + includes = ["."], + deps = [ + ":headers", + ":ucharstrie", + ":ucharstrieiterator", + ":ucptrie", + ":udata", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "ucharstrie", + srcs = [ + "ucharstrie.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":platform", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "ucharstriebuilder", + srcs = [ + "ucharstriebuilder.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":sort", + ":stringtriebuilder", + ":ucharstrie", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "ucharstrieiterator", + srcs = [ + "ucharstrieiterator.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":ucharstrie", + ":uvector32", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "ucol_swp", + srcs = [ + "ucol_swp.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":utrie_swap", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "udata", + srcs = [ + "restrace.cpp", + "ucmndata.cpp", + "udata.cpp", + "udatamem.cpp", + "umapfile.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":icu_utility", + ":platform", + ":uhash", + "//icu4c/source/stubdata", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "uiter", + srcs = [ + "uiter.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":platform", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "ulist", + srcs = [ + "ulist.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":platform", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "unames", + srcs = [ + "unames.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":uchar", + ":udata", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "unifiedcache", + srcs = [ + "unifiedcache.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":platform", + ":uhash", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "uniset_core", + srcs = [ + "bmpset.cpp", + "unifilt.cpp", + "unifunct.cpp", + "uniset.cpp", + "unisetspan.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":icu_utility", + ":patternprops", + ":uvector", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "uniset_closure", + srcs = [ + "uniset_closure.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":uniset_core", + ":unistr_case_locale", + ":unistr_titlecase_brkiter", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "uniset_props", + srcs = [ + "uniset_props.cpp", + "ruleiter.cpp", + ], + includes = ["."], + deps = [ + ":characterproperties", + ":headers", + ":parsepos", + ":propname", + ":resourcebundle", + ":unames", + ":uniset_core", + ":unistr_case", + ":uprops", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "unistr_case", + srcs = [ + "unistr_case.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":ustring_case", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "unistr_case_locale", + srcs = [ + "unistr_case_locale.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":unistr_case", + ":ustring_case_locale", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "unistr_titlecase_brkiter", + srcs = [ + "unistr_titlecase_brkiter.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":ustr_titlecase_brkiter", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "uprops", + srcs = [ + "uprops.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":emojiprops", + ":loadednormalizer2", + ":normalizer2", + ":ubidi_props", + ":ucase", + ":uchar", + ":unistr_case", + ":ustring_case", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "uscript_props", + srcs = [ + "uscript_props.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":platform", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "uset", + srcs = [ + "uset.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":platform", + ":uniset_core", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "uset_props", + srcs = [ + "uset_props.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":uniset_closure", + ":uniset_core", + ":uniset_props", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "usetiter", + srcs = [ + "usetiter.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":platform", + ":uniset_core", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "ustack", + srcs = [ + "ustack.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":uvector", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "ustr_titlecase_brkiter", + srcs = [ + "ustr_titlecase_brkiter.cpp", + ], + includes = ["."], + deps = [ + ":breakiterator", + ":headers", + ":ucase", + ":ustring_case_locale", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "ustring_case", + srcs = [ + "ustrcase.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":ucase", + ":uchar", + ":edits", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "ustring_case_locale", + srcs = [ + "ustrcase_locale.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":resourcebundle", + ":ustring_case", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "utext", + srcs = [ + "utext.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":ucase", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +cc_library( + name = "utrie_swap", + srcs = [ + "utrie_swap.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":udata", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) + +# This target depends on a header file that contains NFC/NFD normalization data. +# This header file is generated by a script (generate.sh) that invokes the gennorm2 binary. +# See the Unicode update change log (changes.txt). +cc_library( + name = "normalizer2", + srcs = [ + "norm2_nfc_data.h", # generated by gennorm2 + "normalizer2.cpp", + "normalizer2impl.cpp", + ], + includes = ["."], + hdrs = [ + "normalizer2impl.h", + ], + deps = [ + ":headers", + ], + local_defines = [ + "U_COMMON_IMPLEMENTATION", + ], +) diff --git a/deps/icu-small/source/common/appendable.cpp b/deps/icu-small/source/common/appendable.cpp index f9b20180eb7817..12c7b0b15acdf0 100644 --- a/deps/icu-small/source/common/appendable.cpp +++ b/deps/icu-small/source/common/appendable.cpp @@ -1,74 +1,74 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011-2012, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: appendable.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010dec07 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" -#include "unicode/appendable.h" -#include "unicode/utf16.h" - -U_NAMESPACE_BEGIN - -Appendable::~Appendable() {} - -UBool -Appendable::appendCodePoint(UChar32 c) { - if(c<=0xffff) { - return appendCodeUnit((UChar)c); - } else { - return appendCodeUnit(U16_LEAD(c)) && appendCodeUnit(U16_TRAIL(c)); - } -} - -UBool -Appendable::appendString(const UChar *s, int32_t length) { - if(length<0) { - UChar c; - while((c=*s++)!=0) { - if(!appendCodeUnit(c)) { - return false; - } - } - } else if(length>0) { - const UChar *limit=s+length; - do { - if(!appendCodeUnit(*s++)) { - return false; - } - } while(s0) { + const char16_t *limit=s+length; + do { + if(!appendCodeUnit(*s++)) { + return false; + } + } while(s>6; // Named for UTF-8 2-byte lead byte with upper 5 bits. - int32_t trail=start&0x3f; // Named for UTF-8 2-byte trail byte with lower 6 bits. - - // Set one bit indicating an all-one block. - uint32_t bits=(uint32_t)1<>6; - int32_t limitTrail=limit&0x3f; - - if(lead==limitLead) { - // Partial vertical bit column. - while(trail0) { - do { - table[trail++]|=bits; - } while(trail<64); - ++lead; - } - if(lead=0x100) { - break; - } - do { - latin1Contains[start++]=1; - } while(start0x80) { - if(start<0x80) { - start=0x80; - } - break; - } - } - - // Set table7FF[]. - while(start<0x800) { - set32x64Bits(table7FF, start, limit<=0x800 ? limit : 0x800); - if(limit>0x800) { - start=0x800; - break; - } - - start=list[listIndex++]; - if(listIndex0x10000) { - limit=0x10000; - } - - if(start>=6; - bmpBlockBits[start&0x3f]|=0x10001<<(start>>6); - start=(start+1)<<6; // Round up to the next block boundary. - minStart=start; // Ignore further ranges in this block. - } - if(start>6, limit>>6); - } - - if(limit&0x3f) { - // Mixed-value block of 64 code points. - limit>>=6; - bmpBlockBits[limit&0x3f]|=0x10001<<(limit>>6); - limit=(limit+1)<<6; // Round up to the next block boundary. - minStart=limit; // Ignore further ranges in this block. - } - } - } - - if(limit==0x10000) { - break; - } - - start=list[listIndex++]; - if(listIndex(~(0x10001<<0xd)); // Lead byte 0xED. - bits=1<<0xd; - for(i=32; i<64; ++i) { // Second half of 4k block. - bmpBlockBits[i]=(bmpBlockBits[i]&mask)|bits; - } - } else { - mask= static_cast(~(0x10001<<0xd)); // Lead byte 0xED. - for(i=32; i<64; ++i) { // Second half of 4k block. - bmpBlockBits[i]&=mask; - } - } -} - -int32_t BMPSet::findCodePoint(UChar32 c, int32_t lo, int32_t hi) const { - /* Examples: - findCodePoint(c) - set list[] c=0 1 3 4 7 8 - === ============== =========== - [] [110000] 0 0 0 0 0 0 - [\u0000-\u0003] [0, 4, 110000] 1 1 1 2 2 2 - [\u0004-\u0007] [4, 8, 110000] 0 0 0 1 1 2 - [:Any:] [0, 110000] 1 1 1 1 1 1 - */ - - // Return the smallest i such that c < list[i]. Assume - // list[len - 1] == HIGH and that c is legal (0..HIGH-1). - if (c < list[lo]) - return lo; - // High runner test. c is often after the last range, so an - // initial check for this condition pays off. - if (lo >= hi || c >= list[hi-1]) - return hi; - // invariant: c >= list[lo] - // invariant: c < list[hi] - for (;;) { - int32_t i = (lo + hi) >> 1; - if (i == lo) { - break; // Found! - } else if (c < list[i]) { - hi = i; - } else { - lo = i; - } - } - return hi; -} - -UBool -BMPSet::contains(UChar32 c) const { - if((uint32_t)c<=0xff) { - return (UBool)latin1Contains[c]; - } else if((uint32_t)c<=0x7ff) { - return (UBool)((table7FF[c&0x3f]&((uint32_t)1<<(c>>6)))!=0); - } else if((uint32_t)c<0xd800 || (c>=0xe000 && c<=0xffff)) { - int lead=c>>12; - uint32_t twoBits=(bmpBlockBits[(c>>6)&0x3f]>>lead)&0x10001; - if(twoBits<=1) { - // All 64 code points with the same bits 15..6 - // are either in the set or not. - return (UBool)twoBits; - } else { - // Look up the code point in its 4k block of code points. - return containsSlow(c, list4kStarts[lead], list4kStarts[lead+1]); - } - } else if((uint32_t)c<=0x10ffff) { - // surrogate or supplementary code point - return containsSlow(c, list4kStarts[0xd], list4kStarts[0x11]); - } else { - // Out-of-range code points get false, consistent with long-standing - // behavior of UnicodeSet::contains(c). - return false; - } -} - -/* - * Check for sufficient length for trail unit for each surrogate pair. - * Handle single surrogates as surrogate code points as usual in ICU. - */ -const UChar * -BMPSet::span(const UChar *s, const UChar *limit, USetSpanCondition spanCondition) const { - UChar c, c2; - - if(spanCondition) { - // span - do { - c=*s; - if(c<=0xff) { - if(!latin1Contains[c]) { - break; - } - } else if(c<=0x7ff) { - if((table7FF[c&0x3f]&((uint32_t)1<<(c>>6)))==0) { - break; - } - } else if(c<0xd800 || c>=0xe000) { - int lead=c>>12; - uint32_t twoBits=(bmpBlockBits[(c>>6)&0x3f]>>lead)&0x10001; - if(twoBits<=1) { - // All 64 code points with the same bits 15..6 - // are either in the set or not. - if(twoBits==0) { - break; - } - } else { - // Look up the code point in its 4k block of code points. - if(!containsSlow(c, list4kStarts[lead], list4kStarts[lead+1])) { - break; - } - } - } else if(c>=0xdc00 || (s+1)==limit || (c2=s[1])<0xdc00 || c2>=0xe000) { - // surrogate code point - if(!containsSlow(c, list4kStarts[0xd], list4kStarts[0xe])) { - break; - } - } else { - // surrogate pair - if(!containsSlow(U16_GET_SUPPLEMENTARY(c, c2), list4kStarts[0x10], list4kStarts[0x11])) { - break; - } - ++s; - } - } while(++s>6)))!=0) { - break; - } - } else if(c<0xd800 || c>=0xe000) { - int lead=c>>12; - uint32_t twoBits=(bmpBlockBits[(c>>6)&0x3f]>>lead)&0x10001; - if(twoBits<=1) { - // All 64 code points with the same bits 15..6 - // are either in the set or not. - if(twoBits!=0) { - break; - } - } else { - // Look up the code point in its 4k block of code points. - if(containsSlow(c, list4kStarts[lead], list4kStarts[lead+1])) { - break; - } - } - } else if(c>=0xdc00 || (s+1)==limit || (c2=s[1])<0xdc00 || c2>=0xe000) { - // surrogate code point - if(containsSlow(c, list4kStarts[0xd], list4kStarts[0xe])) { - break; - } - } else { - // surrogate pair - if(containsSlow(U16_GET_SUPPLEMENTARY(c, c2), list4kStarts[0x10], list4kStarts[0x11])) { - break; - } - ++s; - } - } while(++s>6)))==0) { - break; - } - } else if(c<0xd800 || c>=0xe000) { - int lead=c>>12; - uint32_t twoBits=(bmpBlockBits[(c>>6)&0x3f]>>lead)&0x10001; - if(twoBits<=1) { - // All 64 code points with the same bits 15..6 - // are either in the set or not. - if(twoBits==0) { - break; - } - } else { - // Look up the code point in its 4k block of code points. - if(!containsSlow(c, list4kStarts[lead], list4kStarts[lead+1])) { - break; - } - } - } else if(c<0xdc00 || s==limit || (c2=*(limit-1))<0xd800 || c2>=0xdc00) { - // surrogate code point - if(!containsSlow(c, list4kStarts[0xd], list4kStarts[0xe])) { - break; - } - } else { - // surrogate pair - if(!containsSlow(U16_GET_SUPPLEMENTARY(c2, c), list4kStarts[0x10], list4kStarts[0x11])) { - break; - } - --limit; - } - if(s==limit) { - return s; - } - } - } else { - // span not - for(;;) { - c=*(--limit); - if(c<=0xff) { - if(latin1Contains[c]) { - break; - } - } else if(c<=0x7ff) { - if((table7FF[c&0x3f]&((uint32_t)1<<(c>>6)))!=0) { - break; - } - } else if(c<0xd800 || c>=0xe000) { - int lead=c>>12; - uint32_t twoBits=(bmpBlockBits[(c>>6)&0x3f]>>lead)&0x10001; - if(twoBits<=1) { - // All 64 code points with the same bits 15..6 - // are either in the set or not. - if(twoBits!=0) { - break; - } - } else { - // Look up the code point in its 4k block of code points. - if(containsSlow(c, list4kStarts[lead], list4kStarts[lead+1])) { - break; - } - } - } else if(c<0xdc00 || s==limit || (c2=*(limit-1))<0xd800 || c2>=0xdc00) { - // surrogate code point - if(containsSlow(c, list4kStarts[0xd], list4kStarts[0xe])) { - break; - } - } else { - // surrogate pair - if(containsSlow(U16_GET_SUPPLEMENTARY(c2, c), list4kStarts[0x10], list4kStarts[0x11])) { - break; - } - --limit; - } - if(s==limit) { - return s; - } - } - } - return limit+1; -} - -/* - * Precheck for sufficient trail bytes at end of string only once per span. - * Check validity. - */ -const uint8_t * -BMPSet::spanUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const { - const uint8_t *limit=s+length; - uint8_t b=*s; - if(U8_IS_SINGLE(b)) { - // Initial all-ASCII span. - if(spanCondition) { - do { - if(!latin1Contains[b] || ++s==limit) { - return s; - } - b=*s; - } while(U8_IS_SINGLE(b)); - } else { - do { - if(latin1Contains[b] || ++s==limit) { - return s; - } - b=*s; - } while(U8_IS_SINGLE(b)); - } - length=(int32_t)(limit-s); - } - - if(spanCondition!=USET_SPAN_NOT_CONTAINED) { - spanCondition=USET_SPAN_CONTAINED; // Pin to 0/1 values. - } - - const uint8_t *limit0=limit; - - /* - * Make sure that the last 1/2/3/4-byte sequence before limit is complete - * or runs into a lead byte. - * In the span loop compare s with limit only once - * per multi-byte character. - * - * Give a trailing illegal sequence the same value as the result of contains(FFFD), - * including it if that is part of the span, otherwise set limit0 to before - * the truncated sequence. - */ - b=*(limit-1); - if((int8_t)b<0) { - // b>=0x80: lead or trail byte - if(b<0xc0) { - // single trail byte, check for preceding 3- or 4-byte lead byte - if(length>=2 && (b=*(limit-2))>=0xe0) { - limit-=2; - if(containsFFFD!=spanCondition) { - limit0=limit; - } - } else if(b<0xc0 && b>=0x80 && length>=3 && (b=*(limit-3))>=0xf0) { - // 4-byte lead byte with only two trail bytes - limit-=3; - if(containsFFFD!=spanCondition) { - limit0=limit; - } - } - } else { - // lead byte with no trail bytes - --limit; - if(containsFFFD!=spanCondition) { - limit0=limit; - } - } - } - - uint8_t t1, t2, t3; - - while(s=0xe0) { - if(b<0xf0) { - if( /* handle U+0000..U+FFFF inline */ - (t1=(uint8_t)(s[0]-0x80)) <= 0x3f && - (t2=(uint8_t)(s[1]-0x80)) <= 0x3f - ) { - b&=0xf; - uint32_t twoBits=(bmpBlockBits[t1]>>b)&0x10001; - if(twoBits<=1) { - // All 64 code points with this lead byte and middle trail byte - // are either in the set or not. - if(twoBits!=(uint32_t)spanCondition) { - return s-1; - } - } else { - // Look up the code point in its 4k block of code points. - UChar32 c=(b<<12)|(t1<<6)|t2; - if(containsSlow(c, list4kStarts[b], list4kStarts[b+1]) != spanCondition) { - return s-1; - } - } - s+=2; - continue; - } - } else if( /* handle U+10000..U+10FFFF inline */ - (t1=(uint8_t)(s[0]-0x80)) <= 0x3f && - (t2=(uint8_t)(s[1]-0x80)) <= 0x3f && - (t3=(uint8_t)(s[2]-0x80)) <= 0x3f - ) { - // Give an illegal sequence the same value as the result of contains(FFFD). - UChar32 c=((UChar32)(b-0xf0)<<18)|((UChar32)t1<<12)|(t2<<6)|t3; - if( ( (0x10000<=c && c<=0x10ffff) ? - containsSlow(c, list4kStarts[0x10], list4kStarts[0x11]) : - containsFFFD - ) != spanCondition - ) { - return s-1; - } - s+=3; - continue; - } - } else { - if( /* handle U+0000..U+07FF inline */ - b>=0xc0 && - (t1=(uint8_t)(*s-0x80)) <= 0x3f - ) { - if((USetSpanCondition)((table7FF[t1]&((uint32_t)1<<(b&0x1f)))!=0) != spanCondition) { - return s-1; - } - ++s; - continue; - } - } - - // Give an illegal sequence the same value as the result of contains(FFFD). - // Handle each byte of an illegal sequence separately to simplify the code; - // no need to optimize error handling. - if(containsFFFD!=spanCondition) { - return s-1; - } - } - - return limit0; -} - -/* - * While going backwards through UTF-8 optimize only for ASCII. - * Unlike UTF-16, UTF-8 is not forward-backward symmetrical, that is, it is not - * possible to tell from the last byte in a multi-byte sequence how many - * preceding bytes there should be. Therefore, going backwards through UTF-8 - * is much harder than going forward. - */ -int32_t -BMPSet::spanBackUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const { - if(spanCondition!=USET_SPAN_NOT_CONTAINED) { - spanCondition=USET_SPAN_CONTAINED; // Pin to 0/1 values. - } - - uint8_t b; - - do { - b=s[--length]; - if(U8_IS_SINGLE(b)) { - // ASCII sub-span - if(spanCondition) { - do { - if(!latin1Contains[b]) { - return length+1; - } else if(length==0) { - return 0; - } - b=s[--length]; - } while(U8_IS_SINGLE(b)); - } else { - do { - if(latin1Contains[b]) { - return length+1; - } else if(length==0) { - return 0; - } - b=s[--length]; - } while(U8_IS_SINGLE(b)); - } - } - - int32_t prev=length; - UChar32 c; - // trail byte: collect a multi-byte character - // (or lead byte in last-trail position) - c=utf8_prevCharSafeBody(s, 0, &length, b, -3); - // c is a valid code point, not ASCII, not a surrogate - if(c<=0x7ff) { - if((USetSpanCondition)((table7FF[c&0x3f]&((uint32_t)1<<(c>>6)))!=0) != spanCondition) { - return prev+1; - } - } else if(c<=0xffff) { - int lead=c>>12; - uint32_t twoBits=(bmpBlockBits[(c>>6)&0x3f]>>lead)&0x10001; - if(twoBits<=1) { - // All 64 code points with the same bits 15..6 - // are either in the set or not. - if(twoBits!=(uint32_t)spanCondition) { - return prev+1; - } - } else { - // Look up the code point in its 4k block of code points. - if(containsSlow(c, list4kStarts[lead], list4kStarts[lead+1]) != spanCondition) { - return prev+1; - } - } - } else { - if(containsSlow(c, list4kStarts[0x10], list4kStarts[0x11]) != spanCondition) { - return prev+1; - } - } - } while(length>0); - return 0; -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2007-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: bmpset.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2007jan29 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" +#include "unicode/uniset.h" +#include "unicode/utf8.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "bmpset.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +BMPSet::BMPSet(const int32_t *parentList, int32_t parentListLength) : + list(parentList), listLength(parentListLength) { + uprv_memset(latin1Contains, 0, sizeof(latin1Contains)); + uprv_memset(table7FF, 0, sizeof(table7FF)); + uprv_memset(bmpBlockBits, 0, sizeof(bmpBlockBits)); + + /* + * Set the list indexes for binary searches for + * U+0800, U+1000, U+2000, .., U+F000, U+10000. + * U+0800 is the first 3-byte-UTF-8 code point. Lower code points are + * looked up in the bit tables. + * The last pair of indexes is for finding supplementary code points. + */ + list4kStarts[0]=findCodePoint(0x800, 0, listLength-1); + int32_t i; + for(i=1; i<=0x10; ++i) { + list4kStarts[i]=findCodePoint(i<<12, list4kStarts[i-1], listLength-1); + } + list4kStarts[0x11]=listLength-1; + containsFFFD=containsSlow(0xfffd, list4kStarts[0xf], list4kStarts[0x10]); + + initBits(); + overrideIllegal(); +} + +BMPSet::BMPSet(const BMPSet &otherBMPSet, const int32_t *newParentList, int32_t newParentListLength) : + containsFFFD(otherBMPSet.containsFFFD), + list(newParentList), listLength(newParentListLength) { + uprv_memcpy(latin1Contains, otherBMPSet.latin1Contains, sizeof(latin1Contains)); + uprv_memcpy(table7FF, otherBMPSet.table7FF, sizeof(table7FF)); + uprv_memcpy(bmpBlockBits, otherBMPSet.bmpBlockBits, sizeof(bmpBlockBits)); + uprv_memcpy(list4kStarts, otherBMPSet.list4kStarts, sizeof(list4kStarts)); +} + +BMPSet::~BMPSet() { +} + +/* + * Set bits in a bit rectangle in "vertical" bit organization. + * start>6; // Named for UTF-8 2-byte lead byte with upper 5 bits. + int32_t trail=start&0x3f; // Named for UTF-8 2-byte trail byte with lower 6 bits. + + // Set one bit indicating an all-one block. + uint32_t bits=(uint32_t)1<>6; + int32_t limitTrail=limit&0x3f; + + if(lead==limitLead) { + // Partial vertical bit column. + while(trail0) { + do { + table[trail++]|=bits; + } while(trail<64); + ++lead; + } + if(lead=0x100) { + break; + } + do { + latin1Contains[start++]=1; + } while(start0x80) { + if(start<0x80) { + start=0x80; + } + break; + } + } + + // Set table7FF[]. + while(start<0x800) { + set32x64Bits(table7FF, start, limit<=0x800 ? limit : 0x800); + if(limit>0x800) { + start=0x800; + break; + } + + start=list[listIndex++]; + if(listIndex0x10000) { + limit=0x10000; + } + + if(start>=6; + bmpBlockBits[start&0x3f]|=0x10001<<(start>>6); + start=(start+1)<<6; // Round up to the next block boundary. + minStart=start; // Ignore further ranges in this block. + } + if(start>6, limit>>6); + } + + if(limit&0x3f) { + // Mixed-value block of 64 code points. + limit>>=6; + bmpBlockBits[limit&0x3f]|=0x10001<<(limit>>6); + limit=(limit+1)<<6; // Round up to the next block boundary. + minStart=limit; // Ignore further ranges in this block. + } + } + } + + if(limit==0x10000) { + break; + } + + start=list[listIndex++]; + if(listIndex(~(0x10001<<0xd)); // Lead byte 0xED. + bits=1<<0xd; + for(i=32; i<64; ++i) { // Second half of 4k block. + bmpBlockBits[i]=(bmpBlockBits[i]&mask)|bits; + } + } else { + mask= static_cast(~(0x10001<<0xd)); // Lead byte 0xED. + for(i=32; i<64; ++i) { // Second half of 4k block. + bmpBlockBits[i]&=mask; + } + } +} + +int32_t BMPSet::findCodePoint(UChar32 c, int32_t lo, int32_t hi) const { + /* Examples: + findCodePoint(c) + set list[] c=0 1 3 4 7 8 + === ============== =========== + [] [110000] 0 0 0 0 0 0 + [\u0000-\u0003] [0, 4, 110000] 1 1 1 2 2 2 + [\u0004-\u0007] [4, 8, 110000] 0 0 0 1 1 2 + [:Any:] [0, 110000] 1 1 1 1 1 1 + */ + + // Return the smallest i such that c < list[i]. Assume + // list[len - 1] == HIGH and that c is legal (0..HIGH-1). + if (c < list[lo]) + return lo; + // High runner test. c is often after the last range, so an + // initial check for this condition pays off. + if (lo >= hi || c >= list[hi-1]) + return hi; + // invariant: c >= list[lo] + // invariant: c < list[hi] + for (;;) { + int32_t i = (lo + hi) >> 1; + if (i == lo) { + break; // Found! + } else if (c < list[i]) { + hi = i; + } else { + lo = i; + } + } + return hi; +} + +UBool +BMPSet::contains(UChar32 c) const { + if((uint32_t)c<=0xff) { + return (UBool)latin1Contains[c]; + } else if((uint32_t)c<=0x7ff) { + return (UBool)((table7FF[c&0x3f]&((uint32_t)1<<(c>>6)))!=0); + } else if((uint32_t)c<0xd800 || (c>=0xe000 && c<=0xffff)) { + int lead=c>>12; + uint32_t twoBits=(bmpBlockBits[(c>>6)&0x3f]>>lead)&0x10001; + if(twoBits<=1) { + // All 64 code points with the same bits 15..6 + // are either in the set or not. + return (UBool)twoBits; + } else { + // Look up the code point in its 4k block of code points. + return containsSlow(c, list4kStarts[lead], list4kStarts[lead+1]); + } + } else if((uint32_t)c<=0x10ffff) { + // surrogate or supplementary code point + return containsSlow(c, list4kStarts[0xd], list4kStarts[0x11]); + } else { + // Out-of-range code points get false, consistent with long-standing + // behavior of UnicodeSet::contains(c). + return false; + } +} + +/* + * Check for sufficient length for trail unit for each surrogate pair. + * Handle single surrogates as surrogate code points as usual in ICU. + */ +const char16_t * +BMPSet::span(const char16_t *s, const char16_t *limit, USetSpanCondition spanCondition) const { + char16_t c, c2; + + if(spanCondition) { + // span + do { + c=*s; + if(c<=0xff) { + if(!latin1Contains[c]) { + break; + } + } else if(c<=0x7ff) { + if((table7FF[c&0x3f]&((uint32_t)1<<(c>>6)))==0) { + break; + } + } else if(c<0xd800 || c>=0xe000) { + int lead=c>>12; + uint32_t twoBits=(bmpBlockBits[(c>>6)&0x3f]>>lead)&0x10001; + if(twoBits<=1) { + // All 64 code points with the same bits 15..6 + // are either in the set or not. + if(twoBits==0) { + break; + } + } else { + // Look up the code point in its 4k block of code points. + if(!containsSlow(c, list4kStarts[lead], list4kStarts[lead+1])) { + break; + } + } + } else if(c>=0xdc00 || (s+1)==limit || (c2=s[1])<0xdc00 || c2>=0xe000) { + // surrogate code point + if(!containsSlow(c, list4kStarts[0xd], list4kStarts[0xe])) { + break; + } + } else { + // surrogate pair + if(!containsSlow(U16_GET_SUPPLEMENTARY(c, c2), list4kStarts[0x10], list4kStarts[0x11])) { + break; + } + ++s; + } + } while(++s>6)))!=0) { + break; + } + } else if(c<0xd800 || c>=0xe000) { + int lead=c>>12; + uint32_t twoBits=(bmpBlockBits[(c>>6)&0x3f]>>lead)&0x10001; + if(twoBits<=1) { + // All 64 code points with the same bits 15..6 + // are either in the set or not. + if(twoBits!=0) { + break; + } + } else { + // Look up the code point in its 4k block of code points. + if(containsSlow(c, list4kStarts[lead], list4kStarts[lead+1])) { + break; + } + } + } else if(c>=0xdc00 || (s+1)==limit || (c2=s[1])<0xdc00 || c2>=0xe000) { + // surrogate code point + if(containsSlow(c, list4kStarts[0xd], list4kStarts[0xe])) { + break; + } + } else { + // surrogate pair + if(containsSlow(U16_GET_SUPPLEMENTARY(c, c2), list4kStarts[0x10], list4kStarts[0x11])) { + break; + } + ++s; + } + } while(++s>6)))==0) { + break; + } + } else if(c<0xd800 || c>=0xe000) { + int lead=c>>12; + uint32_t twoBits=(bmpBlockBits[(c>>6)&0x3f]>>lead)&0x10001; + if(twoBits<=1) { + // All 64 code points with the same bits 15..6 + // are either in the set or not. + if(twoBits==0) { + break; + } + } else { + // Look up the code point in its 4k block of code points. + if(!containsSlow(c, list4kStarts[lead], list4kStarts[lead+1])) { + break; + } + } + } else if(c<0xdc00 || s==limit || (c2=*(limit-1))<0xd800 || c2>=0xdc00) { + // surrogate code point + if(!containsSlow(c, list4kStarts[0xd], list4kStarts[0xe])) { + break; + } + } else { + // surrogate pair + if(!containsSlow(U16_GET_SUPPLEMENTARY(c2, c), list4kStarts[0x10], list4kStarts[0x11])) { + break; + } + --limit; + } + if(s==limit) { + return s; + } + } + } else { + // span not + for(;;) { + c=*(--limit); + if(c<=0xff) { + if(latin1Contains[c]) { + break; + } + } else if(c<=0x7ff) { + if((table7FF[c&0x3f]&((uint32_t)1<<(c>>6)))!=0) { + break; + } + } else if(c<0xd800 || c>=0xe000) { + int lead=c>>12; + uint32_t twoBits=(bmpBlockBits[(c>>6)&0x3f]>>lead)&0x10001; + if(twoBits<=1) { + // All 64 code points with the same bits 15..6 + // are either in the set or not. + if(twoBits!=0) { + break; + } + } else { + // Look up the code point in its 4k block of code points. + if(containsSlow(c, list4kStarts[lead], list4kStarts[lead+1])) { + break; + } + } + } else if(c<0xdc00 || s==limit || (c2=*(limit-1))<0xd800 || c2>=0xdc00) { + // surrogate code point + if(containsSlow(c, list4kStarts[0xd], list4kStarts[0xe])) { + break; + } + } else { + // surrogate pair + if(containsSlow(U16_GET_SUPPLEMENTARY(c2, c), list4kStarts[0x10], list4kStarts[0x11])) { + break; + } + --limit; + } + if(s==limit) { + return s; + } + } + } + return limit+1; +} + +/* + * Precheck for sufficient trail bytes at end of string only once per span. + * Check validity. + */ +const uint8_t * +BMPSet::spanUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const { + const uint8_t *limit=s+length; + uint8_t b=*s; + if(U8_IS_SINGLE(b)) { + // Initial all-ASCII span. + if(spanCondition) { + do { + if(!latin1Contains[b] || ++s==limit) { + return s; + } + b=*s; + } while(U8_IS_SINGLE(b)); + } else { + do { + if(latin1Contains[b] || ++s==limit) { + return s; + } + b=*s; + } while(U8_IS_SINGLE(b)); + } + length=(int32_t)(limit-s); + } + + if(spanCondition!=USET_SPAN_NOT_CONTAINED) { + spanCondition=USET_SPAN_CONTAINED; // Pin to 0/1 values. + } + + const uint8_t *limit0=limit; + + /* + * Make sure that the last 1/2/3/4-byte sequence before limit is complete + * or runs into a lead byte. + * In the span loop compare s with limit only once + * per multi-byte character. + * + * Give a trailing illegal sequence the same value as the result of contains(FFFD), + * including it if that is part of the span, otherwise set limit0 to before + * the truncated sequence. + */ + b=*(limit-1); + if((int8_t)b<0) { + // b>=0x80: lead or trail byte + if(b<0xc0) { + // single trail byte, check for preceding 3- or 4-byte lead byte + if(length>=2 && (b=*(limit-2))>=0xe0) { + limit-=2; + if(containsFFFD!=spanCondition) { + limit0=limit; + } + } else if(b<0xc0 && b>=0x80 && length>=3 && (b=*(limit-3))>=0xf0) { + // 4-byte lead byte with only two trail bytes + limit-=3; + if(containsFFFD!=spanCondition) { + limit0=limit; + } + } + } else { + // lead byte with no trail bytes + --limit; + if(containsFFFD!=spanCondition) { + limit0=limit; + } + } + } + + uint8_t t1, t2, t3; + + while(s=0xe0) { + if(b<0xf0) { + if( /* handle U+0000..U+FFFF inline */ + (t1=(uint8_t)(s[0]-0x80)) <= 0x3f && + (t2=(uint8_t)(s[1]-0x80)) <= 0x3f + ) { + b&=0xf; + uint32_t twoBits=(bmpBlockBits[t1]>>b)&0x10001; + if(twoBits<=1) { + // All 64 code points with this lead byte and middle trail byte + // are either in the set or not. + if(twoBits!=(uint32_t)spanCondition) { + return s-1; + } + } else { + // Look up the code point in its 4k block of code points. + UChar32 c=(b<<12)|(t1<<6)|t2; + if(containsSlow(c, list4kStarts[b], list4kStarts[b+1]) != spanCondition) { + return s-1; + } + } + s+=2; + continue; + } + } else if( /* handle U+10000..U+10FFFF inline */ + (t1=(uint8_t)(s[0]-0x80)) <= 0x3f && + (t2=(uint8_t)(s[1]-0x80)) <= 0x3f && + (t3=(uint8_t)(s[2]-0x80)) <= 0x3f + ) { + // Give an illegal sequence the same value as the result of contains(FFFD). + UChar32 c=((UChar32)(b-0xf0)<<18)|((UChar32)t1<<12)|(t2<<6)|t3; + if( ( (0x10000<=c && c<=0x10ffff) ? + containsSlow(c, list4kStarts[0x10], list4kStarts[0x11]) : + containsFFFD + ) != spanCondition + ) { + return s-1; + } + s+=3; + continue; + } + } else { + if( /* handle U+0000..U+07FF inline */ + b>=0xc0 && + (t1=(uint8_t)(*s-0x80)) <= 0x3f + ) { + if((USetSpanCondition)((table7FF[t1]&((uint32_t)1<<(b&0x1f)))!=0) != spanCondition) { + return s-1; + } + ++s; + continue; + } + } + + // Give an illegal sequence the same value as the result of contains(FFFD). + // Handle each byte of an illegal sequence separately to simplify the code; + // no need to optimize error handling. + if(containsFFFD!=spanCondition) { + return s-1; + } + } + + return limit0; +} + +/* + * While going backwards through UTF-8 optimize only for ASCII. + * Unlike UTF-16, UTF-8 is not forward-backward symmetrical, that is, it is not + * possible to tell from the last byte in a multi-byte sequence how many + * preceding bytes there should be. Therefore, going backwards through UTF-8 + * is much harder than going forward. + */ +int32_t +BMPSet::spanBackUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const { + if(spanCondition!=USET_SPAN_NOT_CONTAINED) { + spanCondition=USET_SPAN_CONTAINED; // Pin to 0/1 values. + } + + uint8_t b; + + do { + b=s[--length]; + if(U8_IS_SINGLE(b)) { + // ASCII sub-span + if(spanCondition) { + do { + if(!latin1Contains[b]) { + return length+1; + } else if(length==0) { + return 0; + } + b=s[--length]; + } while(U8_IS_SINGLE(b)); + } else { + do { + if(latin1Contains[b]) { + return length+1; + } else if(length==0) { + return 0; + } + b=s[--length]; + } while(U8_IS_SINGLE(b)); + } + } + + int32_t prev=length; + UChar32 c; + // trail byte: collect a multi-byte character + // (or lead byte in last-trail position) + c=utf8_prevCharSafeBody(s, 0, &length, b, -3); + // c is a valid code point, not ASCII, not a surrogate + if(c<=0x7ff) { + if((USetSpanCondition)((table7FF[c&0x3f]&((uint32_t)1<<(c>>6)))!=0) != spanCondition) { + return prev+1; + } + } else if(c<=0xffff) { + int lead=c>>12; + uint32_t twoBits=(bmpBlockBits[(c>>6)&0x3f]>>lead)&0x10001; + if(twoBits<=1) { + // All 64 code points with the same bits 15..6 + // are either in the set or not. + if(twoBits!=(uint32_t)spanCondition) { + return prev+1; + } + } else { + // Look up the code point in its 4k block of code points. + if(containsSlow(c, list4kStarts[lead], list4kStarts[lead+1]) != spanCondition) { + return prev+1; + } + } + } else { + if(containsSlow(c, list4kStarts[0x10], list4kStarts[0x11]) != spanCondition) { + return prev+1; + } + } + } while(length>0); + return 0; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/bmpset.h b/deps/icu-small/source/common/bmpset.h index e1982ac669db85..50bc50efc62fe0 100644 --- a/deps/icu-small/source/common/bmpset.h +++ b/deps/icu-small/source/common/bmpset.h @@ -1,164 +1,164 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2007, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: bmpset.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2007jan29 -* created by: Markus W. Scherer -*/ - -#ifndef __BMPSET_H__ -#define __BMPSET_H__ - -#include "unicode/utypes.h" -#include "unicode/uniset.h" - -U_NAMESPACE_BEGIN - -/* - * Helper class for frozen UnicodeSets, implements contains() and span() - * optimized for BMP code points. Structured to be UTF-8-friendly. - * - * Latin-1: Look up bytes. - * 2-byte characters: Bits organized vertically. - * 3-byte characters: Use zero/one/mixed data per 64-block in U+0000..U+FFFF, - * with mixed for illegal ranges. - * Supplementary characters: Binary search over - * the supplementary part of the parent set's inversion list. - */ -class BMPSet : public UMemory { -public: - BMPSet(const int32_t *parentList, int32_t parentListLength); - BMPSet(const BMPSet &otherBMPSet, const int32_t *newParentList, int32_t newParentListLength); - virtual ~BMPSet(); - - virtual UBool contains(UChar32 c) const; - - /* - * Span the initial substring for which each character c has spanCondition==contains(c). - * It must be s0 and spanCondition==0 or 1. - * @return The string pointer which limits the span. - */ - const uint8_t *spanUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const; - - /* - * Span the trailing substring for which each character c has spanCondition==contains(c). - * It must be length>0 and spanCondition==0 or 1. - * @return The start of the span. - */ - int32_t spanBackUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const; - -private: - void initBits(); - void overrideIllegal(); - - /** - * Same as UnicodeSet::findCodePoint(UChar32 c) const except that the - * binary search is restricted for finding code points in a certain range. - * - * For restricting the search for finding in the range start..end, - * pass in - * lo=findCodePoint(start) and - * hi=findCodePoint(end) - * with 0<=lo<=hi0 and spanCondition==0 or 1. + * @return The string pointer which limits the span. + */ + const uint8_t *spanUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const; + + /* + * Span the trailing substring for which each character c has spanCondition==contains(c). + * It must be length>0 and spanCondition==0 or 1. + * @return The start of the span. + */ + int32_t spanBackUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const; + +private: + void initBits(); + void overrideIllegal(); + + /** + * Same as UnicodeSet::findCodePoint(UChar32 c) const except that the + * binary search is restricted for finding code points in a certain range. + * + * For restricting the search for finding in the range start..end, + * pass in + * lo=findCodePoint(start) and + * hi=findCodePoint(end) + * with 0<=lo<=hicontains(c); -} - -int32_t -UnhandledEngine::findBreaks( UText *text, - int32_t /* startPos */, - int32_t endPos, - UVector32 &/*foundBreaks*/, - UBool /* isPhraseBreaking */, - UErrorCode &status) const { - if (U_FAILURE(status)) return 0; - UChar32 c = utext_current32(text); - while((int32_t)utext_getNativeIndex(text) < endPos && fHandled->contains(c)) { - utext_next32(text); // TODO: recast loop to work with post-increment operations. - c = utext_current32(text); - } - return 0; -} - -void -UnhandledEngine::handleCharacter(UChar32 c) { - if (fHandled == nullptr) { - fHandled = new UnicodeSet(); - if (fHandled == nullptr) { - return; - } - } - if (!fHandled->contains(c)) { - UErrorCode status = U_ZERO_ERROR; - // Apply the entire script of the character. - int32_t script = u_getIntPropertyValue(c, UCHAR_SCRIPT); - fHandled->applyIntPropertyValue(UCHAR_SCRIPT, script, status); - } -} - -/* - ****************************************************************** - */ - -ICULanguageBreakFactory::ICULanguageBreakFactory(UErrorCode &/*status*/) { - fEngines = 0; -} - -ICULanguageBreakFactory::~ICULanguageBreakFactory() { - if (fEngines != 0) { - delete fEngines; - } -} - -U_NAMESPACE_END -U_CDECL_BEGIN -static void U_CALLCONV _deleteEngine(void *obj) { - delete (const icu::LanguageBreakEngine *) obj; -} -U_CDECL_END -U_NAMESPACE_BEGIN - -const LanguageBreakEngine * -ICULanguageBreakFactory::getEngineFor(UChar32 c) { - const LanguageBreakEngine *lbe = NULL; - UErrorCode status = U_ZERO_ERROR; - - static UMutex gBreakEngineMutex; - Mutex m(&gBreakEngineMutex); - - if (fEngines == nullptr) { - LocalPointer engines(new UStack(_deleteEngine, nullptr, status), status); - if (U_FAILURE(status) ) { - // Note: no way to return error code to caller. - return nullptr; - } - fEngines = engines.orphan(); - } else { - int32_t i = fEngines->size(); - while (--i >= 0) { - lbe = (const LanguageBreakEngine *)(fEngines->elementAt(i)); - if (lbe != NULL && lbe->handles(c)) { - return lbe; - } - } - } - - // We didn't find an engine. Create one. - lbe = loadEngineFor(c); - if (lbe != nullptr) { - fEngines->push((void *)lbe, status); - } - return U_SUCCESS(status) ? lbe : nullptr; -} - -const LanguageBreakEngine * -ICULanguageBreakFactory::loadEngineFor(UChar32 c) { - UErrorCode status = U_ZERO_ERROR; - UScriptCode code = uscript_getScript(c, &status); - if (U_SUCCESS(status)) { - const LanguageBreakEngine *engine = nullptr; - // Try to use LSTM first - const LSTMData *data = CreateLSTMDataForScript(code, status); - if (U_SUCCESS(status)) { - if (data != nullptr) { - engine = CreateLSTMBreakEngine(code, data, status); - if (U_SUCCESS(status) && engine != nullptr) { - return engine; - } - if (engine != nullptr) { - delete engine; - engine = nullptr; - } else { - DeleteLSTMData(data); - } - } - } - status = U_ZERO_ERROR; // fallback to dictionary based - DictionaryMatcher *m = loadDictionaryMatcherFor(code); - if (m != NULL) { - switch(code) { - case USCRIPT_THAI: - engine = new ThaiBreakEngine(m, status); - break; - case USCRIPT_LAO: - engine = new LaoBreakEngine(m, status); - break; - case USCRIPT_MYANMAR: - engine = new BurmeseBreakEngine(m, status); - break; - case USCRIPT_KHMER: - engine = new KhmerBreakEngine(m, status); - break; - -#if !UCONFIG_NO_NORMALIZATION - // CJK not available w/o normalization - case USCRIPT_HANGUL: - engine = new CjkBreakEngine(m, kKorean, status); - break; - - // use same BreakEngine and dictionary for both Chinese and Japanese - case USCRIPT_HIRAGANA: - case USCRIPT_KATAKANA: - case USCRIPT_HAN: - engine = new CjkBreakEngine(m, kChineseJapanese, status); - break; -#if 0 - // TODO: Have to get some characters with script=common handled - // by CjkBreakEngine (e.g. U+309B). Simply subjecting - // them to CjkBreakEngine does not work. The engine has to - // special-case them. - case USCRIPT_COMMON: - { - UBlockCode block = ublock_getCode(code); - if (block == UBLOCK_HIRAGANA || block == UBLOCK_KATAKANA) - engine = new CjkBreakEngine(dict, kChineseJapanese, status); - break; - } -#endif -#endif - - default: - break; - } - if (engine == NULL) { - delete m; - } - else if (U_FAILURE(status)) { - delete engine; - engine = NULL; - } - return engine; - } - } - return NULL; -} - -DictionaryMatcher * -ICULanguageBreakFactory::loadDictionaryMatcherFor(UScriptCode script) { - UErrorCode status = U_ZERO_ERROR; - // open root from brkitr tree. - UResourceBundle *b = ures_open(U_ICUDATA_BRKITR, "", &status); - b = ures_getByKeyWithFallback(b, "dictionaries", b, &status); - int32_t dictnlength = 0; - const UChar *dictfname = - ures_getStringByKeyWithFallback(b, uscript_getShortName(script), &dictnlength, &status); - if (U_FAILURE(status)) { - ures_close(b); - return NULL; - } - CharString dictnbuf; - CharString ext; - const UChar *extStart = u_memrchr(dictfname, 0x002e, dictnlength); // last dot - if (extStart != NULL) { - int32_t len = (int32_t)(extStart - dictfname); - ext.appendInvariantChars(UnicodeString(false, extStart + 1, dictnlength - len - 1), status); - dictnlength = len; - } - dictnbuf.appendInvariantChars(UnicodeString(false, dictfname, dictnlength), status); - ures_close(b); - - UDataMemory *file = udata_open(U_ICUDATA_BRKITR, ext.data(), dictnbuf.data(), &status); - if (U_SUCCESS(status)) { - // build trie - const uint8_t *data = (const uint8_t *)udata_getMemory(file); - const int32_t *indexes = (const int32_t *)data; - const int32_t offset = indexes[DictionaryData::IX_STRING_TRIE_OFFSET]; - const int32_t trieType = indexes[DictionaryData::IX_TRIE_TYPE] & DictionaryData::TRIE_TYPE_MASK; - DictionaryMatcher *m = NULL; - if (trieType == DictionaryData::TRIE_TYPE_BYTES) { - const int32_t transform = indexes[DictionaryData::IX_TRANSFORM]; - const char *characters = (const char *)(data + offset); - m = new BytesDictionaryMatcher(characters, transform, file); - } - else if (trieType == DictionaryData::TRIE_TYPE_UCHARS) { - const UChar *characters = (const UChar *)(data + offset); - m = new UCharsDictionaryMatcher(characters, file); - } - if (m == NULL) { - // no matcher exists to take ownership - either we are an invalid - // type or memory allocation failed - udata_close(file); - } - return m; - } else if (dictfname != NULL) { - // we don't have a dictionary matcher. - // returning NULL here will cause us to fail to find a dictionary break engine, as expected - status = U_ZERO_ERROR; - return NULL; - } - return NULL; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ************************************************************************************ + * Copyright (C) 2006-2016, International Business Machines Corporation + * and others. All Rights Reserved. + ************************************************************************************ + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/uchar.h" +#include "unicode/uniset.h" +#include "unicode/chariter.h" +#include "unicode/ures.h" +#include "unicode/udata.h" +#include "unicode/putil.h" +#include "unicode/ustring.h" +#include "unicode/uscript.h" +#include "unicode/ucharstrie.h" +#include "unicode/bytestrie.h" + +#include "brkeng.h" +#include "cmemory.h" +#include "dictbe.h" +#include "lstmbe.h" +#include "charstr.h" +#include "dictionarydata.h" +#include "mutex.h" +#include "uvector.h" +#include "umutex.h" +#include "uresimp.h" +#include "ubrkimpl.h" + +U_NAMESPACE_BEGIN + +/* + ****************************************************************** + */ + +LanguageBreakEngine::LanguageBreakEngine() { +} + +LanguageBreakEngine::~LanguageBreakEngine() { +} + +/* + ****************************************************************** + */ + +LanguageBreakFactory::LanguageBreakFactory() { +} + +LanguageBreakFactory::~LanguageBreakFactory() { +} + +/* + ****************************************************************** + */ + +UnhandledEngine::UnhandledEngine(UErrorCode &status) : fHandled(nullptr) { + (void)status; +} + +UnhandledEngine::~UnhandledEngine() { + delete fHandled; + fHandled = nullptr; +} + +UBool +UnhandledEngine::handles(UChar32 c) const { + return fHandled && fHandled->contains(c); +} + +int32_t +UnhandledEngine::findBreaks( UText *text, + int32_t /* startPos */, + int32_t endPos, + UVector32 &/*foundBreaks*/, + UBool /* isPhraseBreaking */, + UErrorCode &status) const { + if (U_FAILURE(status)) return 0; + UChar32 c = utext_current32(text); + while((int32_t)utext_getNativeIndex(text) < endPos && fHandled->contains(c)) { + utext_next32(text); // TODO: recast loop to work with post-increment operations. + c = utext_current32(text); + } + return 0; +} + +void +UnhandledEngine::handleCharacter(UChar32 c) { + if (fHandled == nullptr) { + fHandled = new UnicodeSet(); + if (fHandled == nullptr) { + return; + } + } + if (!fHandled->contains(c)) { + UErrorCode status = U_ZERO_ERROR; + // Apply the entire script of the character. + int32_t script = u_getIntPropertyValue(c, UCHAR_SCRIPT); + fHandled->applyIntPropertyValue(UCHAR_SCRIPT, script, status); + } +} + +/* + ****************************************************************** + */ + +ICULanguageBreakFactory::ICULanguageBreakFactory(UErrorCode &/*status*/) { + fEngines = 0; +} + +ICULanguageBreakFactory::~ICULanguageBreakFactory() { + if (fEngines != 0) { + delete fEngines; + } +} + +U_NAMESPACE_END +U_CDECL_BEGIN +static void U_CALLCONV _deleteEngine(void *obj) { + delete (const icu::LanguageBreakEngine *) obj; +} +U_CDECL_END +U_NAMESPACE_BEGIN + +const LanguageBreakEngine * +ICULanguageBreakFactory::getEngineFor(UChar32 c) { + const LanguageBreakEngine *lbe = nullptr; + UErrorCode status = U_ZERO_ERROR; + + static UMutex gBreakEngineMutex; + Mutex m(&gBreakEngineMutex); + + if (fEngines == nullptr) { + LocalPointer engines(new UStack(_deleteEngine, nullptr, status), status); + if (U_FAILURE(status) ) { + // Note: no way to return error code to caller. + return nullptr; + } + fEngines = engines.orphan(); + } else { + int32_t i = fEngines->size(); + while (--i >= 0) { + lbe = (const LanguageBreakEngine *)(fEngines->elementAt(i)); + if (lbe != nullptr && lbe->handles(c)) { + return lbe; + } + } + } + + // We didn't find an engine. Create one. + lbe = loadEngineFor(c); + if (lbe != nullptr) { + fEngines->push((void *)lbe, status); + } + return U_SUCCESS(status) ? lbe : nullptr; +} + +const LanguageBreakEngine * +ICULanguageBreakFactory::loadEngineFor(UChar32 c) { + UErrorCode status = U_ZERO_ERROR; + UScriptCode code = uscript_getScript(c, &status); + if (U_SUCCESS(status)) { + const LanguageBreakEngine *engine = nullptr; + // Try to use LSTM first + const LSTMData *data = CreateLSTMDataForScript(code, status); + if (U_SUCCESS(status)) { + if (data != nullptr) { + engine = CreateLSTMBreakEngine(code, data, status); + if (U_SUCCESS(status) && engine != nullptr) { + return engine; + } + if (engine != nullptr) { + delete engine; + engine = nullptr; + } else { + DeleteLSTMData(data); + } + } + } + status = U_ZERO_ERROR; // fallback to dictionary based + DictionaryMatcher *m = loadDictionaryMatcherFor(code); + if (m != nullptr) { + switch(code) { + case USCRIPT_THAI: + engine = new ThaiBreakEngine(m, status); + break; + case USCRIPT_LAO: + engine = new LaoBreakEngine(m, status); + break; + case USCRIPT_MYANMAR: + engine = new BurmeseBreakEngine(m, status); + break; + case USCRIPT_KHMER: + engine = new KhmerBreakEngine(m, status); + break; + +#if !UCONFIG_NO_NORMALIZATION + // CJK not available w/o normalization + case USCRIPT_HANGUL: + engine = new CjkBreakEngine(m, kKorean, status); + break; + + // use same BreakEngine and dictionary for both Chinese and Japanese + case USCRIPT_HIRAGANA: + case USCRIPT_KATAKANA: + case USCRIPT_HAN: + engine = new CjkBreakEngine(m, kChineseJapanese, status); + break; +#if 0 + // TODO: Have to get some characters with script=common handled + // by CjkBreakEngine (e.g. U+309B). Simply subjecting + // them to CjkBreakEngine does not work. The engine has to + // special-case them. + case USCRIPT_COMMON: + { + UBlockCode block = ublock_getCode(code); + if (block == UBLOCK_HIRAGANA || block == UBLOCK_KATAKANA) + engine = new CjkBreakEngine(dict, kChineseJapanese, status); + break; + } +#endif +#endif + + default: + break; + } + if (engine == nullptr) { + delete m; + } + else if (U_FAILURE(status)) { + delete engine; + engine = nullptr; + } + return engine; + } + } + return nullptr; +} + +DictionaryMatcher * +ICULanguageBreakFactory::loadDictionaryMatcherFor(UScriptCode script) { + UErrorCode status = U_ZERO_ERROR; + // open root from brkitr tree. + UResourceBundle *b = ures_open(U_ICUDATA_BRKITR, "", &status); + b = ures_getByKeyWithFallback(b, "dictionaries", b, &status); + int32_t dictnlength = 0; + const char16_t *dictfname = + ures_getStringByKeyWithFallback(b, uscript_getShortName(script), &dictnlength, &status); + if (U_FAILURE(status)) { + ures_close(b); + return nullptr; + } + CharString dictnbuf; + CharString ext; + const char16_t *extStart = u_memrchr(dictfname, 0x002e, dictnlength); // last dot + if (extStart != nullptr) { + int32_t len = (int32_t)(extStart - dictfname); + ext.appendInvariantChars(UnicodeString(false, extStart + 1, dictnlength - len - 1), status); + dictnlength = len; + } + dictnbuf.appendInvariantChars(UnicodeString(false, dictfname, dictnlength), status); + ures_close(b); + + UDataMemory *file = udata_open(U_ICUDATA_BRKITR, ext.data(), dictnbuf.data(), &status); + if (U_SUCCESS(status)) { + // build trie + const uint8_t *data = (const uint8_t *)udata_getMemory(file); + const int32_t *indexes = (const int32_t *)data; + const int32_t offset = indexes[DictionaryData::IX_STRING_TRIE_OFFSET]; + const int32_t trieType = indexes[DictionaryData::IX_TRIE_TYPE] & DictionaryData::TRIE_TYPE_MASK; + DictionaryMatcher *m = nullptr; + if (trieType == DictionaryData::TRIE_TYPE_BYTES) { + const int32_t transform = indexes[DictionaryData::IX_TRANSFORM]; + const char *characters = (const char *)(data + offset); + m = new BytesDictionaryMatcher(characters, transform, file); + } + else if (trieType == DictionaryData::TRIE_TYPE_UCHARS) { + const char16_t *characters = (const char16_t *)(data + offset); + m = new UCharsDictionaryMatcher(characters, file); + } + if (m == nullptr) { + // no matcher exists to take ownership - either we are an invalid + // type or memory allocation failed + udata_close(file); + } + return m; + } else if (dictfname != nullptr) { + // we don't have a dictionary matcher. + // returning nullptr here will cause us to fail to find a dictionary break engine, as expected + status = U_ZERO_ERROR; + return nullptr; + } + return nullptr; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ diff --git a/deps/icu-small/source/common/brkeng.h b/deps/icu-small/source/common/brkeng.h index 127ba59e186f23..aff7062d1c0bc0 100644 --- a/deps/icu-small/source/common/brkeng.h +++ b/deps/icu-small/source/common/brkeng.h @@ -1,277 +1,277 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** - ************************************************************************************ - * Copyright (C) 2006-2012, International Business Machines Corporation and others. * - * All Rights Reserved. * - ************************************************************************************ - */ - -#ifndef BRKENG_H -#define BRKENG_H - -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "unicode/utext.h" -#include "unicode/uscript.h" - -U_NAMESPACE_BEGIN - -class UnicodeSet; -class UStack; -class UVector32; -class DictionaryMatcher; - -/******************************************************************* - * LanguageBreakEngine - */ - -/** - *

LanguageBreakEngines implement language-specific knowledge for - * finding text boundaries within a run of characters belonging to a - * specific set. The boundaries will be of a specific kind, e.g. word, - * line, etc.

- * - *

LanguageBreakEngines should normally be implemented so as to - * be shared between threads without locking.

- */ -class LanguageBreakEngine : public UMemory { - public: - - /** - *

Default constructor.

- * - */ - LanguageBreakEngine(); - - /** - *

Virtual destructor.

- */ - virtual ~LanguageBreakEngine(); - - /** - *

Indicate whether this engine handles a particular character for - * a particular kind of break.

- * - * @param c A character which begins a run that the engine might handle - * @return true if this engine handles the particular character and break - * type. - */ - virtual UBool handles(UChar32 c) const = 0; - - /** - *

Find any breaks within a run in the supplied text.

- * - * @param text A UText representing the text. The - * iterator is left at the end of the run of characters which the engine - * is capable of handling. - * @param startPos The start of the run within the supplied text. - * @param endPos The end of the run within the supplied text. - * @param foundBreaks A Vector of int32_t to receive the breaks. - * @param status Information on any errors encountered. - * @return The number of breaks found. - */ - virtual int32_t findBreaks( UText *text, - int32_t startPos, - int32_t endPos, - UVector32 &foundBreaks, - UBool isPhraseBreaking, - UErrorCode &status) const = 0; - -}; - -/******************************************************************* - * LanguageBreakFactory - */ - -/** - *

LanguageBreakFactorys find and return a LanguageBreakEngine - * that can determine breaks for characters in a specific set, if - * such an object can be found.

- * - *

If a LanguageBreakFactory is to be shared between threads, - * appropriate synchronization must be used; there is none internal - * to the factory.

- * - *

A LanguageBreakEngine returned by a LanguageBreakFactory can - * normally be shared between threads without synchronization, unless - * the specific subclass of LanguageBreakFactory indicates otherwise.

- * - *

A LanguageBreakFactory is responsible for deleting any LanguageBreakEngine - * it returns when it itself is deleted, unless the specific subclass of - * LanguageBreakFactory indicates otherwise. Naturally, the factory should - * not be deleted until the LanguageBreakEngines it has returned are no - * longer needed.

- */ -class LanguageBreakFactory : public UMemory { - public: - - /** - *

Default constructor.

- * - */ - LanguageBreakFactory(); - - /** - *

Virtual destructor.

- */ - virtual ~LanguageBreakFactory(); - - /** - *

Find and return a LanguageBreakEngine that can find the desired - * kind of break for the set of characters to which the supplied - * character belongs. It is up to the set of available engines to - * determine what the sets of characters are.

- * - * @param c A character that begins a run for which a LanguageBreakEngine is - * sought. - * @return A LanguageBreakEngine with the desired characteristics, or 0. - */ - virtual const LanguageBreakEngine *getEngineFor(UChar32 c) = 0; - -}; - -/******************************************************************* - * UnhandledEngine - */ - -/** - *

UnhandledEngine is a special subclass of LanguageBreakEngine that - * handles characters that no other LanguageBreakEngine is available to - * handle. It is told the character and the type of break; at its - * discretion it may handle more than the specified character (e.g., - * the entire script to which that character belongs.

- * - *

UnhandledEngines may not be shared between threads without - * external synchronization.

- */ - -class UnhandledEngine : public LanguageBreakEngine { - private: - - /** - * The sets of characters handled. - * @internal - */ - - UnicodeSet *fHandled; - - public: - - /** - *

Default constructor.

- * - */ - UnhandledEngine(UErrorCode &status); - - /** - *

Virtual destructor.

- */ - virtual ~UnhandledEngine(); - - /** - *

Indicate whether this engine handles a particular character for - * a particular kind of break.

- * - * @param c A character which begins a run that the engine might handle - * @return true if this engine handles the particular character and break - * type. - */ - virtual UBool handles(UChar32 c) const override; - - /** - *

Find any breaks within a run in the supplied text.

- * - * @param text A UText representing the text (TODO: UText). The - * iterator is left at the end of the run of characters which the engine - * is capable of handling. - * @param startPos The start of the run within the supplied text. - * @param endPos The end of the run within the supplied text. - * @param foundBreaks An allocated C array of the breaks found, if any - * @param status Information on any errors encountered. - * @return The number of breaks found. - */ - virtual int32_t findBreaks( UText *text, - int32_t startPos, - int32_t endPos, - UVector32 &foundBreaks, - UBool isPhraseBreaking, - UErrorCode &status) const override; - - /** - *

Tell the engine to handle a particular character and break type.

- * - * @param c A character which the engine should handle - */ - virtual void handleCharacter(UChar32 c); - -}; - -/******************************************************************* - * ICULanguageBreakFactory - */ - -/** - *

ICULanguageBreakFactory is the default LanguageBreakFactory for - * ICU. It creates dictionary-based LanguageBreakEngines from dictionary - * data in the ICU data file.

- */ -class ICULanguageBreakFactory : public LanguageBreakFactory { - private: - - /** - * The stack of break engines created by this factory - * @internal - */ - - UStack *fEngines; - - public: - - /** - *

Standard constructor.

- * - */ - ICULanguageBreakFactory(UErrorCode &status); - - /** - *

Virtual destructor.

- */ - virtual ~ICULanguageBreakFactory(); - - /** - *

Find and return a LanguageBreakEngine that can find the desired - * kind of break for the set of characters to which the supplied - * character belongs. It is up to the set of available engines to - * determine what the sets of characters are.

- * - * @param c A character that begins a run for which a LanguageBreakEngine is - * sought. - * @return A LanguageBreakEngine with the desired characteristics, or 0. - */ - virtual const LanguageBreakEngine *getEngineFor(UChar32 c) override; - -protected: - /** - *

Create a LanguageBreakEngine for the set of characters to which - * the supplied character belongs, for the specified break type.

- * - * @param c A character that begins a run for which a LanguageBreakEngine is - * sought. - * @return A LanguageBreakEngine with the desired characteristics, or 0. - */ - virtual const LanguageBreakEngine *loadEngineFor(UChar32 c); - - /** - *

Create a DictionaryMatcher for the specified script and break type.

- * @param script An ISO 15924 script code that identifies the dictionary to be - * created. - * @return A DictionaryMatcher with the desired characteristics, or NULL. - */ - virtual DictionaryMatcher *loadDictionaryMatcherFor(UScriptCode script); -}; - -U_NAMESPACE_END - - /* BRKENG_H */ -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** + ************************************************************************************ + * Copyright (C) 2006-2012, International Business Machines Corporation and others. * + * All Rights Reserved. * + ************************************************************************************ + */ + +#ifndef BRKENG_H +#define BRKENG_H + +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "unicode/utext.h" +#include "unicode/uscript.h" + +U_NAMESPACE_BEGIN + +class UnicodeSet; +class UStack; +class UVector32; +class DictionaryMatcher; + +/******************************************************************* + * LanguageBreakEngine + */ + +/** + *

LanguageBreakEngines implement language-specific knowledge for + * finding text boundaries within a run of characters belonging to a + * specific set. The boundaries will be of a specific kind, e.g. word, + * line, etc.

+ * + *

LanguageBreakEngines should normally be implemented so as to + * be shared between threads without locking.

+ */ +class LanguageBreakEngine : public UMemory { + public: + + /** + *

Default constructor.

+ * + */ + LanguageBreakEngine(); + + /** + *

Virtual destructor.

+ */ + virtual ~LanguageBreakEngine(); + + /** + *

Indicate whether this engine handles a particular character for + * a particular kind of break.

+ * + * @param c A character which begins a run that the engine might handle + * @return true if this engine handles the particular character and break + * type. + */ + virtual UBool handles(UChar32 c) const = 0; + + /** + *

Find any breaks within a run in the supplied text.

+ * + * @param text A UText representing the text. The + * iterator is left at the end of the run of characters which the engine + * is capable of handling. + * @param startPos The start of the run within the supplied text. + * @param endPos The end of the run within the supplied text. + * @param foundBreaks A Vector of int32_t to receive the breaks. + * @param status Information on any errors encountered. + * @return The number of breaks found. + */ + virtual int32_t findBreaks( UText *text, + int32_t startPos, + int32_t endPos, + UVector32 &foundBreaks, + UBool isPhraseBreaking, + UErrorCode &status) const = 0; + +}; + +/******************************************************************* + * LanguageBreakFactory + */ + +/** + *

LanguageBreakFactorys find and return a LanguageBreakEngine + * that can determine breaks for characters in a specific set, if + * such an object can be found.

+ * + *

If a LanguageBreakFactory is to be shared between threads, + * appropriate synchronization must be used; there is none internal + * to the factory.

+ * + *

A LanguageBreakEngine returned by a LanguageBreakFactory can + * normally be shared between threads without synchronization, unless + * the specific subclass of LanguageBreakFactory indicates otherwise.

+ * + *

A LanguageBreakFactory is responsible for deleting any LanguageBreakEngine + * it returns when it itself is deleted, unless the specific subclass of + * LanguageBreakFactory indicates otherwise. Naturally, the factory should + * not be deleted until the LanguageBreakEngines it has returned are no + * longer needed.

+ */ +class LanguageBreakFactory : public UMemory { + public: + + /** + *

Default constructor.

+ * + */ + LanguageBreakFactory(); + + /** + *

Virtual destructor.

+ */ + virtual ~LanguageBreakFactory(); + + /** + *

Find and return a LanguageBreakEngine that can find the desired + * kind of break for the set of characters to which the supplied + * character belongs. It is up to the set of available engines to + * determine what the sets of characters are.

+ * + * @param c A character that begins a run for which a LanguageBreakEngine is + * sought. + * @return A LanguageBreakEngine with the desired characteristics, or 0. + */ + virtual const LanguageBreakEngine *getEngineFor(UChar32 c) = 0; + +}; + +/******************************************************************* + * UnhandledEngine + */ + +/** + *

UnhandledEngine is a special subclass of LanguageBreakEngine that + * handles characters that no other LanguageBreakEngine is available to + * handle. It is told the character and the type of break; at its + * discretion it may handle more than the specified character (e.g., + * the entire script to which that character belongs.

+ * + *

UnhandledEngines may not be shared between threads without + * external synchronization.

+ */ + +class UnhandledEngine : public LanguageBreakEngine { + private: + + /** + * The sets of characters handled. + * @internal + */ + + UnicodeSet *fHandled; + + public: + + /** + *

Default constructor.

+ * + */ + UnhandledEngine(UErrorCode &status); + + /** + *

Virtual destructor.

+ */ + virtual ~UnhandledEngine(); + + /** + *

Indicate whether this engine handles a particular character for + * a particular kind of break.

+ * + * @param c A character which begins a run that the engine might handle + * @return true if this engine handles the particular character and break + * type. + */ + virtual UBool handles(UChar32 c) const override; + + /** + *

Find any breaks within a run in the supplied text.

+ * + * @param text A UText representing the text (TODO: UText). The + * iterator is left at the end of the run of characters which the engine + * is capable of handling. + * @param startPos The start of the run within the supplied text. + * @param endPos The end of the run within the supplied text. + * @param foundBreaks An allocated C array of the breaks found, if any + * @param status Information on any errors encountered. + * @return The number of breaks found. + */ + virtual int32_t findBreaks( UText *text, + int32_t startPos, + int32_t endPos, + UVector32 &foundBreaks, + UBool isPhraseBreaking, + UErrorCode &status) const override; + + /** + *

Tell the engine to handle a particular character and break type.

+ * + * @param c A character which the engine should handle + */ + virtual void handleCharacter(UChar32 c); + +}; + +/******************************************************************* + * ICULanguageBreakFactory + */ + +/** + *

ICULanguageBreakFactory is the default LanguageBreakFactory for + * ICU. It creates dictionary-based LanguageBreakEngines from dictionary + * data in the ICU data file.

+ */ +class ICULanguageBreakFactory : public LanguageBreakFactory { + private: + + /** + * The stack of break engines created by this factory + * @internal + */ + + UStack *fEngines; + + public: + + /** + *

Standard constructor.

+ * + */ + ICULanguageBreakFactory(UErrorCode &status); + + /** + *

Virtual destructor.

+ */ + virtual ~ICULanguageBreakFactory(); + + /** + *

Find and return a LanguageBreakEngine that can find the desired + * kind of break for the set of characters to which the supplied + * character belongs. It is up to the set of available engines to + * determine what the sets of characters are.

+ * + * @param c A character that begins a run for which a LanguageBreakEngine is + * sought. + * @return A LanguageBreakEngine with the desired characteristics, or 0. + */ + virtual const LanguageBreakEngine *getEngineFor(UChar32 c) override; + +protected: + /** + *

Create a LanguageBreakEngine for the set of characters to which + * the supplied character belongs, for the specified break type.

+ * + * @param c A character that begins a run for which a LanguageBreakEngine is + * sought. + * @return A LanguageBreakEngine with the desired characteristics, or 0. + */ + virtual const LanguageBreakEngine *loadEngineFor(UChar32 c); + + /** + *

Create a DictionaryMatcher for the specified script and break type.

+ * @param script An ISO 15924 script code that identifies the dictionary to be + * created. + * @return A DictionaryMatcher with the desired characteristics, or nullptr. + */ + virtual DictionaryMatcher *loadDictionaryMatcherFor(UScriptCode script); +}; + +U_NAMESPACE_END + + /* BRKENG_H */ +#endif diff --git a/deps/icu-small/source/common/brkiter.cpp b/deps/icu-small/source/common/brkiter.cpp index d6996734e3ccc1..673d862614503f 100644 --- a/deps/icu-small/source/common/brkiter.cpp +++ b/deps/icu-small/source/common/brkiter.cpp @@ -1,538 +1,538 @@ -// © 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 brkiter.cpp -* -* Modification History: -* -* Date Name Description -* 02/18/97 aliu Converted from OpenClass. Added DONE. -* 01/13/2000 helena Added UErrorCode parameter to createXXXInstance methods. -***************************************************************************************** -*/ - -// ***************************************************************************** -// This file was generated from the java source file BreakIterator.java -// ***************************************************************************** - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/rbbi.h" -#include "unicode/brkiter.h" -#include "unicode/udata.h" -#include "unicode/ures.h" -#include "unicode/ustring.h" -#include "unicode/filteredbrk.h" -#include "bytesinkutil.h" -#include "ucln_cmn.h" -#include "cstring.h" -#include "umutex.h" -#include "servloc.h" -#include "locbased.h" -#include "uresimp.h" -#include "uassert.h" -#include "ubrkimpl.h" -#include "utracimp.h" -#include "charstr.h" - -// ***************************************************************************** -// class BreakIterator -// This class implements methods for finding the location of boundaries in text. -// Instances of BreakIterator maintain a current position and scan over text -// returning the index of characters where boundaries occur. -// ***************************************************************************** - -U_NAMESPACE_BEGIN - -// ------------------------------------- - -BreakIterator* -BreakIterator::buildInstance(const Locale& loc, const char *type, UErrorCode &status) -{ - char fnbuff[256]; - char ext[4]={'\0'}; - CharString actualLocale; - int32_t size; - const UChar* brkfname = NULL; - UResourceBundle brkRulesStack; - UResourceBundle brkNameStack; - UResourceBundle *brkRules = &brkRulesStack; - UResourceBundle *brkName = &brkNameStack; - RuleBasedBreakIterator *result = NULL; - - if (U_FAILURE(status)) - return NULL; - - ures_initStackObject(brkRules); - ures_initStackObject(brkName); - - // Get the locale - UResourceBundle *b = ures_openNoDefault(U_ICUDATA_BRKITR, loc.getName(), &status); - - // Get the "boundaries" array. - if (U_SUCCESS(status)) { - brkRules = ures_getByKeyWithFallback(b, "boundaries", brkRules, &status); - // Get the string object naming the rules file - brkName = ures_getByKeyWithFallback(brkRules, type, brkName, &status); - // Get the actual string - brkfname = ures_getString(brkName, &size, &status); - U_ASSERT((size_t)size=sizeof(fnbuff)) { - size=0; - if (U_SUCCESS(status)) { - status = U_BUFFER_OVERFLOW_ERROR; - } - } - - // Use the string if we found it - if (U_SUCCESS(status) && brkfname) { - actualLocale.append(ures_getLocaleInternal(brkName, &status), -1, status); - - UChar* extStart=u_strchr(brkfname, 0x002e); - int len = 0; - if(extStart!=NULL){ - len = (int)(extStart-brkfname); - u_UCharsToChars(extStart+1, ext, sizeof(ext)); // nul terminates the buff - u_UCharsToChars(brkfname, fnbuff, len); - } - fnbuff[len]=0; // nul terminate - } - } - - ures_close(brkRules); - ures_close(brkName); - - UDataMemory* file = udata_open(U_ICUDATA_BRKITR, ext, fnbuff, &status); - if (U_FAILURE(status)) { - ures_close(b); - return NULL; - } - - // Create a RuleBasedBreakIterator - result = new RuleBasedBreakIterator(file, uprv_strstr(type, "phrase") != NULL, status); - - // If there is a result, set the valid locale and actual locale, and the kind - if (U_SUCCESS(status) && result != NULL) { - U_LOCALE_BASED(locBased, *(BreakIterator*)result); - locBased.setLocaleIDs(ures_getLocaleByType(b, ULOC_VALID_LOCALE, &status), - actualLocale.data()); - } - - ures_close(b); - - if (U_FAILURE(status) && result != NULL) { // Sometimes redundant check, but simple - delete result; - return NULL; - } - - if (result == NULL) { - udata_close(file); - if (U_SUCCESS(status)) { - status = U_MEMORY_ALLOCATION_ERROR; - } - } - - return result; -} - -// Creates a break iterator for word breaks. -BreakIterator* U_EXPORT2 -BreakIterator::createWordInstance(const Locale& key, UErrorCode& status) -{ - return createInstance(key, UBRK_WORD, status); -} - -// ------------------------------------- - -// Creates a break iterator for line breaks. -BreakIterator* U_EXPORT2 -BreakIterator::createLineInstance(const Locale& key, UErrorCode& status) -{ - return createInstance(key, UBRK_LINE, status); -} - -// ------------------------------------- - -// Creates a break iterator for character breaks. -BreakIterator* U_EXPORT2 -BreakIterator::createCharacterInstance(const Locale& key, UErrorCode& status) -{ - return createInstance(key, UBRK_CHARACTER, status); -} - -// ------------------------------------- - -// Creates a break iterator for sentence breaks. -BreakIterator* U_EXPORT2 -BreakIterator::createSentenceInstance(const Locale& key, UErrorCode& status) -{ - return createInstance(key, UBRK_SENTENCE, status); -} - -// ------------------------------------- - -// Creates a break iterator for title casing breaks. -BreakIterator* U_EXPORT2 -BreakIterator::createTitleInstance(const Locale& key, UErrorCode& status) -{ - return createInstance(key, UBRK_TITLE, status); -} - -// ------------------------------------- - -// Gets all the available locales that has localized text boundary data. -const Locale* U_EXPORT2 -BreakIterator::getAvailableLocales(int32_t& count) -{ - return Locale::getAvailableLocales(count); -} - -// ------------------------------------------ -// -// Constructors, destructor and assignment operator -// -//------------------------------------------- - -BreakIterator::BreakIterator() -{ - *validLocale = *actualLocale = 0; -} - -BreakIterator::BreakIterator(const BreakIterator &other) : UObject(other) { - uprv_strncpy(actualLocale, other.actualLocale, sizeof(actualLocale)); - uprv_strncpy(validLocale, other.validLocale, sizeof(validLocale)); -} - -BreakIterator &BreakIterator::operator =(const BreakIterator &other) { - if (this != &other) { - uprv_strncpy(actualLocale, other.actualLocale, sizeof(actualLocale)); - uprv_strncpy(validLocale, other.validLocale, sizeof(validLocale)); - } - return *this; -} - -BreakIterator::~BreakIterator() -{ -} - -// ------------------------------------------ -// -// Registration -// -//------------------------------------------- -#if !UCONFIG_NO_SERVICE - -// ------------------------------------- - -class ICUBreakIteratorFactory : public ICUResourceBundleFactory { -public: - virtual ~ICUBreakIteratorFactory(); -protected: - virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* /*service*/, UErrorCode& status) const override { - return BreakIterator::makeInstance(loc, kind, status); - } -}; - -ICUBreakIteratorFactory::~ICUBreakIteratorFactory() {} - -// ------------------------------------- - -class ICUBreakIteratorService : public ICULocaleService { -public: - ICUBreakIteratorService() - : ICULocaleService(UNICODE_STRING("Break Iterator", 14)) - { - UErrorCode status = U_ZERO_ERROR; - registerFactory(new ICUBreakIteratorFactory(), status); - } - - virtual ~ICUBreakIteratorService(); - - virtual UObject* cloneInstance(UObject* instance) const override { - return ((BreakIterator*)instance)->clone(); - } - - virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /*actualID*/, UErrorCode& status) const override { - LocaleKey& lkey = (LocaleKey&)key; - int32_t kind = lkey.kind(); - Locale loc; - lkey.currentLocale(loc); - return BreakIterator::makeInstance(loc, kind, status); - } - - virtual UBool isDefault() const override { - return countFactories() == 1; - } -}; - -ICUBreakIteratorService::~ICUBreakIteratorService() {} - -// ------------------------------------- - -// defined in ucln_cmn.h -U_NAMESPACE_END - -static icu::UInitOnce gInitOnceBrkiter {}; -static icu::ICULocaleService* gService = NULL; - - - -/** - * Release all static memory held by breakiterator. - */ -U_CDECL_BEGIN -static UBool U_CALLCONV breakiterator_cleanup(void) { -#if !UCONFIG_NO_SERVICE - if (gService) { - delete gService; - gService = NULL; - } - gInitOnceBrkiter.reset(); -#endif - return true; -} -U_CDECL_END -U_NAMESPACE_BEGIN - -static void U_CALLCONV -initService(void) { - gService = new ICUBreakIteratorService(); - ucln_common_registerCleanup(UCLN_COMMON_BREAKITERATOR, breakiterator_cleanup); -} - -static ICULocaleService* -getService(void) -{ - umtx_initOnce(gInitOnceBrkiter, &initService); - return gService; -} - - -// ------------------------------------- - -static inline UBool -hasService(void) -{ - return !gInitOnceBrkiter.isReset() && getService() != NULL; -} - -// ------------------------------------- - -URegistryKey U_EXPORT2 -BreakIterator::registerInstance(BreakIterator* toAdopt, const Locale& locale, UBreakIteratorType kind, UErrorCode& status) -{ - ICULocaleService *service = getService(); - if (service == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - return service->registerInstance(toAdopt, locale, kind, status); -} - -// ------------------------------------- - -UBool U_EXPORT2 -BreakIterator::unregister(URegistryKey key, UErrorCode& status) -{ - if (U_SUCCESS(status)) { - if (hasService()) { - return gService->unregister(key, status); - } - status = U_MEMORY_ALLOCATION_ERROR; - } - return false; -} - -// ------------------------------------- - -StringEnumeration* U_EXPORT2 -BreakIterator::getAvailableLocales(void) -{ - ICULocaleService *service = getService(); - if (service == NULL) { - return NULL; - } - return service->getAvailableLocales(); -} -#endif /* UCONFIG_NO_SERVICE */ - -// ------------------------------------- - -BreakIterator* -BreakIterator::createInstance(const Locale& loc, int32_t kind, UErrorCode& status) -{ - if (U_FAILURE(status)) { - return NULL; - } - -#if !UCONFIG_NO_SERVICE - if (hasService()) { - Locale actualLoc(""); - BreakIterator *result = (BreakIterator*)gService->get(loc, kind, &actualLoc, status); - // TODO: The way the service code works in ICU 2.8 is that if - // there is a real registered break iterator, the actualLoc - // will be populated, but if the handleDefault path is taken - // (because nothing is registered that can handle the - // requested locale) then the actualLoc comes back empty. In - // that case, the returned object already has its actual/valid - // locale data populated (by makeInstance, which is what - // handleDefault calls), so we don't touch it. YES, A COMMENT - // THIS LONG is a sign of bad code -- so the action item is to - // revisit this in ICU 3.0 and clean it up/fix it/remove it. - if (U_SUCCESS(status) && (result != NULL) && *actualLoc.getName() != 0) { - U_LOCALE_BASED(locBased, *result); - locBased.setLocaleIDs(actualLoc.getName(), actualLoc.getName()); - } - return result; - } - else -#endif - { - return makeInstance(loc, kind, status); - } -} - -// ------------------------------------- -enum { kKeyValueLenMax = 32 }; - -BreakIterator* -BreakIterator::makeInstance(const Locale& loc, int32_t kind, UErrorCode& status) -{ - - if (U_FAILURE(status)) { - return NULL; - } - - BreakIterator *result = NULL; - switch (kind) { - case UBRK_CHARACTER: - { - UTRACE_ENTRY(UTRACE_UBRK_CREATE_CHARACTER); - result = BreakIterator::buildInstance(loc, "grapheme", status); - UTRACE_EXIT_STATUS(status); - } - break; - case UBRK_WORD: - { - UTRACE_ENTRY(UTRACE_UBRK_CREATE_WORD); - result = BreakIterator::buildInstance(loc, "word", status); - UTRACE_EXIT_STATUS(status); - } - break; - case UBRK_LINE: - { - char lb_lw[kKeyValueLenMax]; - UTRACE_ENTRY(UTRACE_UBRK_CREATE_LINE); - uprv_strcpy(lb_lw, "line"); - UErrorCode kvStatus = U_ZERO_ERROR; - CharString value; - CharStringByteSink valueSink(&value); - loc.getKeywordValue("lb", valueSink, kvStatus); - if (U_SUCCESS(kvStatus) && (value == "strict" || value == "normal" || value == "loose")) { - uprv_strcat(lb_lw, "_"); - uprv_strcat(lb_lw, value.data()); - } - // lw=phrase is only supported in Japanese. - if (uprv_strcmp(loc.getLanguage(), "ja") == 0) { - value.clear(); - loc.getKeywordValue("lw", valueSink, kvStatus); - if (U_SUCCESS(kvStatus) && value == "phrase") { - uprv_strcat(lb_lw, "_"); - uprv_strcat(lb_lw, value.data()); - } - } - result = BreakIterator::buildInstance(loc, lb_lw, status); - - UTRACE_DATA1(UTRACE_INFO, "lb_lw=%s", lb_lw); - UTRACE_EXIT_STATUS(status); - } - break; - case UBRK_SENTENCE: - { - UTRACE_ENTRY(UTRACE_UBRK_CREATE_SENTENCE); - result = BreakIterator::buildInstance(loc, "sentence", status); -#if !UCONFIG_NO_FILTERED_BREAK_ITERATION - char ssKeyValue[kKeyValueLenMax] = {0}; - UErrorCode kvStatus = U_ZERO_ERROR; - int32_t kLen = loc.getKeywordValue("ss", ssKeyValue, kKeyValueLenMax, kvStatus); - if (U_SUCCESS(kvStatus) && kLen > 0 && uprv_strcmp(ssKeyValue,"standard")==0) { - FilteredBreakIteratorBuilder* fbiBuilder = FilteredBreakIteratorBuilder::createInstance(loc, kvStatus); - if (U_SUCCESS(kvStatus)) { - result = fbiBuilder->build(result, status); - delete fbiBuilder; - } - } -#endif - UTRACE_EXIT_STATUS(status); - } - break; - case UBRK_TITLE: - { - UTRACE_ENTRY(UTRACE_UBRK_CREATE_TITLE); - result = BreakIterator::buildInstance(loc, "title", status); - UTRACE_EXIT_STATUS(status); - } - break; - default: - status = U_ILLEGAL_ARGUMENT_ERROR; - } - - if (U_FAILURE(status)) { - return NULL; - } - - return result; -} - -Locale -BreakIterator::getLocale(ULocDataLocaleType type, UErrorCode& status) const { - U_LOCALE_BASED(locBased, *this); - return locBased.getLocale(type, status); -} - -const char * -BreakIterator::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const { - U_LOCALE_BASED(locBased, *this); - return locBased.getLocaleID(type, status); -} - - -// This implementation of getRuleStatus is a do-nothing stub, here to -// provide a default implementation for any derived BreakIterator classes that -// do not implement it themselves. -int32_t BreakIterator::getRuleStatus() const { - return 0; -} - -// This implementation of getRuleStatusVec is a do-nothing stub, here to -// provide a default implementation for any derived BreakIterator classes that -// do not implement it themselves. -int32_t BreakIterator::getRuleStatusVec(int32_t *fillInVec, int32_t capacity, UErrorCode &status) { - if (U_FAILURE(status)) { - return 0; - } - if (capacity < 1) { - status = U_BUFFER_OVERFLOW_ERROR; - return 1; - } - *fillInVec = 0; - return 1; -} - -BreakIterator::BreakIterator (const Locale& valid, const Locale& actual) { - U_LOCALE_BASED(locBased, (*this)); - locBased.setLocaleIDs(valid, actual); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ - -//eof +// © 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 brkiter.cpp +* +* Modification History: +* +* Date Name Description +* 02/18/97 aliu Converted from OpenClass. Added DONE. +* 01/13/2000 helena Added UErrorCode parameter to createXXXInstance methods. +***************************************************************************************** +*/ + +// ***************************************************************************** +// This file was generated from the java source file BreakIterator.java +// ***************************************************************************** + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/rbbi.h" +#include "unicode/brkiter.h" +#include "unicode/udata.h" +#include "unicode/ures.h" +#include "unicode/ustring.h" +#include "unicode/filteredbrk.h" +#include "bytesinkutil.h" +#include "ucln_cmn.h" +#include "cstring.h" +#include "umutex.h" +#include "servloc.h" +#include "locbased.h" +#include "uresimp.h" +#include "uassert.h" +#include "ubrkimpl.h" +#include "utracimp.h" +#include "charstr.h" + +// ***************************************************************************** +// class BreakIterator +// This class implements methods for finding the location of boundaries in text. +// Instances of BreakIterator maintain a current position and scan over text +// returning the index of characters where boundaries occur. +// ***************************************************************************** + +U_NAMESPACE_BEGIN + +// ------------------------------------- + +BreakIterator* +BreakIterator::buildInstance(const Locale& loc, const char *type, UErrorCode &status) +{ + char fnbuff[256]; + char ext[4]={'\0'}; + CharString actualLocale; + int32_t size; + const char16_t* brkfname = nullptr; + UResourceBundle brkRulesStack; + UResourceBundle brkNameStack; + UResourceBundle *brkRules = &brkRulesStack; + UResourceBundle *brkName = &brkNameStack; + RuleBasedBreakIterator *result = nullptr; + + if (U_FAILURE(status)) + return nullptr; + + ures_initStackObject(brkRules); + ures_initStackObject(brkName); + + // Get the locale + UResourceBundle *b = ures_openNoDefault(U_ICUDATA_BRKITR, loc.getName(), &status); + + // Get the "boundaries" array. + if (U_SUCCESS(status)) { + brkRules = ures_getByKeyWithFallback(b, "boundaries", brkRules, &status); + // Get the string object naming the rules file + brkName = ures_getByKeyWithFallback(brkRules, type, brkName, &status); + // Get the actual string + brkfname = ures_getString(brkName, &size, &status); + U_ASSERT((size_t)size=sizeof(fnbuff)) { + size=0; + if (U_SUCCESS(status)) { + status = U_BUFFER_OVERFLOW_ERROR; + } + } + + // Use the string if we found it + if (U_SUCCESS(status) && brkfname) { + actualLocale.append(ures_getLocaleInternal(brkName, &status), -1, status); + + char16_t* extStart=u_strchr(brkfname, 0x002e); + int len = 0; + if (extStart != nullptr){ + len = (int)(extStart-brkfname); + u_UCharsToChars(extStart+1, ext, sizeof(ext)); // nul terminates the buff + u_UCharsToChars(brkfname, fnbuff, len); + } + fnbuff[len]=0; // nul terminate + } + } + + ures_close(brkRules); + ures_close(brkName); + + UDataMemory* file = udata_open(U_ICUDATA_BRKITR, ext, fnbuff, &status); + if (U_FAILURE(status)) { + ures_close(b); + return nullptr; + } + + // Create a RuleBasedBreakIterator + result = new RuleBasedBreakIterator(file, uprv_strstr(type, "phrase") != nullptr, status); + + // If there is a result, set the valid locale and actual locale, and the kind + if (U_SUCCESS(status) && result != nullptr) { + U_LOCALE_BASED(locBased, *(BreakIterator*)result); + locBased.setLocaleIDs(ures_getLocaleByType(b, ULOC_VALID_LOCALE, &status), + actualLocale.data()); + } + + ures_close(b); + + if (U_FAILURE(status) && result != nullptr) { // Sometimes redundant check, but simple + delete result; + return nullptr; + } + + if (result == nullptr) { + udata_close(file); + if (U_SUCCESS(status)) { + status = U_MEMORY_ALLOCATION_ERROR; + } + } + + return result; +} + +// Creates a break iterator for word breaks. +BreakIterator* U_EXPORT2 +BreakIterator::createWordInstance(const Locale& key, UErrorCode& status) +{ + return createInstance(key, UBRK_WORD, status); +} + +// ------------------------------------- + +// Creates a break iterator for line breaks. +BreakIterator* U_EXPORT2 +BreakIterator::createLineInstance(const Locale& key, UErrorCode& status) +{ + return createInstance(key, UBRK_LINE, status); +} + +// ------------------------------------- + +// Creates a break iterator for character breaks. +BreakIterator* U_EXPORT2 +BreakIterator::createCharacterInstance(const Locale& key, UErrorCode& status) +{ + return createInstance(key, UBRK_CHARACTER, status); +} + +// ------------------------------------- + +// Creates a break iterator for sentence breaks. +BreakIterator* U_EXPORT2 +BreakIterator::createSentenceInstance(const Locale& key, UErrorCode& status) +{ + return createInstance(key, UBRK_SENTENCE, status); +} + +// ------------------------------------- + +// Creates a break iterator for title casing breaks. +BreakIterator* U_EXPORT2 +BreakIterator::createTitleInstance(const Locale& key, UErrorCode& status) +{ + return createInstance(key, UBRK_TITLE, status); +} + +// ------------------------------------- + +// Gets all the available locales that has localized text boundary data. +const Locale* U_EXPORT2 +BreakIterator::getAvailableLocales(int32_t& count) +{ + return Locale::getAvailableLocales(count); +} + +// ------------------------------------------ +// +// Constructors, destructor and assignment operator +// +//------------------------------------------- + +BreakIterator::BreakIterator() +{ + *validLocale = *actualLocale = 0; +} + +BreakIterator::BreakIterator(const BreakIterator &other) : UObject(other) { + uprv_strncpy(actualLocale, other.actualLocale, sizeof(actualLocale)); + uprv_strncpy(validLocale, other.validLocale, sizeof(validLocale)); +} + +BreakIterator &BreakIterator::operator =(const BreakIterator &other) { + if (this != &other) { + uprv_strncpy(actualLocale, other.actualLocale, sizeof(actualLocale)); + uprv_strncpy(validLocale, other.validLocale, sizeof(validLocale)); + } + return *this; +} + +BreakIterator::~BreakIterator() +{ +} + +// ------------------------------------------ +// +// Registration +// +//------------------------------------------- +#if !UCONFIG_NO_SERVICE + +// ------------------------------------- + +class ICUBreakIteratorFactory : public ICUResourceBundleFactory { +public: + virtual ~ICUBreakIteratorFactory(); +protected: + virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* /*service*/, UErrorCode& status) const override { + return BreakIterator::makeInstance(loc, kind, status); + } +}; + +ICUBreakIteratorFactory::~ICUBreakIteratorFactory() {} + +// ------------------------------------- + +class ICUBreakIteratorService : public ICULocaleService { +public: + ICUBreakIteratorService() + : ICULocaleService(UNICODE_STRING("Break Iterator", 14)) + { + UErrorCode status = U_ZERO_ERROR; + registerFactory(new ICUBreakIteratorFactory(), status); + } + + virtual ~ICUBreakIteratorService(); + + virtual UObject* cloneInstance(UObject* instance) const override { + return ((BreakIterator*)instance)->clone(); + } + + virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /*actualID*/, UErrorCode& status) const override { + LocaleKey& lkey = static_cast(const_cast(key)); + int32_t kind = lkey.kind(); + Locale loc; + lkey.currentLocale(loc); + return BreakIterator::makeInstance(loc, kind, status); + } + + virtual UBool isDefault() const override { + return countFactories() == 1; + } +}; + +ICUBreakIteratorService::~ICUBreakIteratorService() {} + +// ------------------------------------- + +// defined in ucln_cmn.h +U_NAMESPACE_END + +static icu::UInitOnce gInitOnceBrkiter {}; +static icu::ICULocaleService* gService = nullptr; + + + +/** + * Release all static memory held by breakiterator. + */ +U_CDECL_BEGIN +static UBool U_CALLCONV breakiterator_cleanup() { +#if !UCONFIG_NO_SERVICE + if (gService) { + delete gService; + gService = nullptr; + } + gInitOnceBrkiter.reset(); +#endif + return true; +} +U_CDECL_END +U_NAMESPACE_BEGIN + +static void U_CALLCONV +initService() { + gService = new ICUBreakIteratorService(); + ucln_common_registerCleanup(UCLN_COMMON_BREAKITERATOR, breakiterator_cleanup); +} + +static ICULocaleService* +getService() +{ + umtx_initOnce(gInitOnceBrkiter, &initService); + return gService; +} + + +// ------------------------------------- + +static inline UBool +hasService() +{ + return !gInitOnceBrkiter.isReset() && getService() != nullptr; +} + +// ------------------------------------- + +URegistryKey U_EXPORT2 +BreakIterator::registerInstance(BreakIterator* toAdopt, const Locale& locale, UBreakIteratorType kind, UErrorCode& status) +{ + ICULocaleService *service = getService(); + if (service == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + return service->registerInstance(toAdopt, locale, kind, status); +} + +// ------------------------------------- + +UBool U_EXPORT2 +BreakIterator::unregister(URegistryKey key, UErrorCode& status) +{ + if (U_SUCCESS(status)) { + if (hasService()) { + return gService->unregister(key, status); + } + status = U_MEMORY_ALLOCATION_ERROR; + } + return false; +} + +// ------------------------------------- + +StringEnumeration* U_EXPORT2 +BreakIterator::getAvailableLocales() +{ + ICULocaleService *service = getService(); + if (service == nullptr) { + return nullptr; + } + return service->getAvailableLocales(); +} +#endif /* UCONFIG_NO_SERVICE */ + +// ------------------------------------- + +BreakIterator* +BreakIterator::createInstance(const Locale& loc, int32_t kind, UErrorCode& status) +{ + if (U_FAILURE(status)) { + return nullptr; + } + +#if !UCONFIG_NO_SERVICE + if (hasService()) { + Locale actualLoc(""); + BreakIterator *result = (BreakIterator*)gService->get(loc, kind, &actualLoc, status); + // TODO: The way the service code works in ICU 2.8 is that if + // there is a real registered break iterator, the actualLoc + // will be populated, but if the handleDefault path is taken + // (because nothing is registered that can handle the + // requested locale) then the actualLoc comes back empty. In + // that case, the returned object already has its actual/valid + // locale data populated (by makeInstance, which is what + // handleDefault calls), so we don't touch it. YES, A COMMENT + // THIS LONG is a sign of bad code -- so the action item is to + // revisit this in ICU 3.0 and clean it up/fix it/remove it. + if (U_SUCCESS(status) && (result != nullptr) && *actualLoc.getName() != 0) { + U_LOCALE_BASED(locBased, *result); + locBased.setLocaleIDs(actualLoc.getName(), actualLoc.getName()); + } + return result; + } + else +#endif + { + return makeInstance(loc, kind, status); + } +} + +// ------------------------------------- +enum { kKeyValueLenMax = 32 }; + +BreakIterator* +BreakIterator::makeInstance(const Locale& loc, int32_t kind, UErrorCode& status) +{ + + if (U_FAILURE(status)) { + return nullptr; + } + + BreakIterator *result = nullptr; + switch (kind) { + case UBRK_CHARACTER: + { + UTRACE_ENTRY(UTRACE_UBRK_CREATE_CHARACTER); + result = BreakIterator::buildInstance(loc, "grapheme", status); + UTRACE_EXIT_STATUS(status); + } + break; + case UBRK_WORD: + { + UTRACE_ENTRY(UTRACE_UBRK_CREATE_WORD); + result = BreakIterator::buildInstance(loc, "word", status); + UTRACE_EXIT_STATUS(status); + } + break; + case UBRK_LINE: + { + char lb_lw[kKeyValueLenMax]; + UTRACE_ENTRY(UTRACE_UBRK_CREATE_LINE); + uprv_strcpy(lb_lw, "line"); + UErrorCode kvStatus = U_ZERO_ERROR; + CharString value; + CharStringByteSink valueSink(&value); + loc.getKeywordValue("lb", valueSink, kvStatus); + if (U_SUCCESS(kvStatus) && (value == "strict" || value == "normal" || value == "loose")) { + uprv_strcat(lb_lw, "_"); + uprv_strcat(lb_lw, value.data()); + } + // lw=phrase is only supported in Japanese and Korean + if (uprv_strcmp(loc.getLanguage(), "ja") == 0 || uprv_strcmp(loc.getLanguage(), "ko") == 0) { + value.clear(); + loc.getKeywordValue("lw", valueSink, kvStatus); + if (U_SUCCESS(kvStatus) && value == "phrase") { + uprv_strcat(lb_lw, "_"); + uprv_strcat(lb_lw, value.data()); + } + } + result = BreakIterator::buildInstance(loc, lb_lw, status); + + UTRACE_DATA1(UTRACE_INFO, "lb_lw=%s", lb_lw); + UTRACE_EXIT_STATUS(status); + } + break; + case UBRK_SENTENCE: + { + UTRACE_ENTRY(UTRACE_UBRK_CREATE_SENTENCE); + result = BreakIterator::buildInstance(loc, "sentence", status); +#if !UCONFIG_NO_FILTERED_BREAK_ITERATION + char ssKeyValue[kKeyValueLenMax] = {0}; + UErrorCode kvStatus = U_ZERO_ERROR; + int32_t kLen = loc.getKeywordValue("ss", ssKeyValue, kKeyValueLenMax, kvStatus); + if (U_SUCCESS(kvStatus) && kLen > 0 && uprv_strcmp(ssKeyValue,"standard")==0) { + FilteredBreakIteratorBuilder* fbiBuilder = FilteredBreakIteratorBuilder::createInstance(loc, kvStatus); + if (U_SUCCESS(kvStatus)) { + result = fbiBuilder->build(result, status); + delete fbiBuilder; + } + } +#endif + UTRACE_EXIT_STATUS(status); + } + break; + case UBRK_TITLE: + { + UTRACE_ENTRY(UTRACE_UBRK_CREATE_TITLE); + result = BreakIterator::buildInstance(loc, "title", status); + UTRACE_EXIT_STATUS(status); + } + break; + default: + status = U_ILLEGAL_ARGUMENT_ERROR; + } + + if (U_FAILURE(status)) { + return nullptr; + } + + return result; +} + +Locale +BreakIterator::getLocale(ULocDataLocaleType type, UErrorCode& status) const { + U_LOCALE_BASED(locBased, *this); + return locBased.getLocale(type, status); +} + +const char * +BreakIterator::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const { + U_LOCALE_BASED(locBased, *this); + return locBased.getLocaleID(type, status); +} + + +// This implementation of getRuleStatus is a do-nothing stub, here to +// provide a default implementation for any derived BreakIterator classes that +// do not implement it themselves. +int32_t BreakIterator::getRuleStatus() const { + return 0; +} + +// This implementation of getRuleStatusVec is a do-nothing stub, here to +// provide a default implementation for any derived BreakIterator classes that +// do not implement it themselves. +int32_t BreakIterator::getRuleStatusVec(int32_t *fillInVec, int32_t capacity, UErrorCode &status) { + if (U_FAILURE(status)) { + return 0; + } + if (capacity < 1) { + status = U_BUFFER_OVERFLOW_ERROR; + return 1; + } + *fillInVec = 0; + return 1; +} + +BreakIterator::BreakIterator (const Locale& valid, const Locale& actual) { + U_LOCALE_BASED(locBased, (*this)); + locBased.setLocaleIDs(valid, actual); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ + +//eof diff --git a/deps/icu-small/source/common/bytesinkutil.cpp b/deps/icu-small/source/common/bytesinkutil.cpp index a32254a7dbabc0..77140f10da37ec 100644 --- a/deps/icu-small/source/common/bytesinkutil.cpp +++ b/deps/icu-small/source/common/bytesinkutil.cpp @@ -1,161 +1,161 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// bytesinkutil.cpp -// created: 2017sep14 Markus W. Scherer - -#include "unicode/utypes.h" -#include "unicode/bytestream.h" -#include "unicode/edits.h" -#include "unicode/stringoptions.h" -#include "unicode/utf8.h" -#include "unicode/utf16.h" -#include "bytesinkutil.h" -#include "charstr.h" -#include "cmemory.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - -UBool -ByteSinkUtil::appendChange(int32_t length, const char16_t *s16, int32_t s16Length, - ByteSink &sink, Edits *edits, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return false; } - char scratch[200]; - int32_t s8Length = 0; - for (int32_t i = 0; i < s16Length;) { - int32_t capacity; - int32_t desiredCapacity = s16Length - i; - if (desiredCapacity < (INT32_MAX / 3)) { - desiredCapacity *= 3; // max 3 UTF-8 bytes per UTF-16 code unit - } else if (desiredCapacity < (INT32_MAX / 2)) { - desiredCapacity *= 2; - } else { - desiredCapacity = INT32_MAX; - } - char *buffer = sink.GetAppendBuffer(U8_MAX_LENGTH, desiredCapacity, - scratch, UPRV_LENGTHOF(scratch), &capacity); - capacity -= U8_MAX_LENGTH - 1; - int32_t j = 0; - for (; i < s16Length && j < capacity;) { - UChar32 c; - U16_NEXT_UNSAFE(s16, i, c); - U8_APPEND_UNSAFE(buffer, j, c); - } - if (j > (INT32_MAX - s8Length)) { - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return false; - } - sink.Append(buffer, j); - s8Length += j; - } - if (edits != nullptr) { - edits->addReplace(length, s8Length); - } - return true; -} - -UBool -ByteSinkUtil::appendChange(const uint8_t *s, const uint8_t *limit, - const char16_t *s16, int32_t s16Length, - ByteSink &sink, Edits *edits, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return false; } - if ((limit - s) > INT32_MAX) { - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return false; - } - return appendChange((int32_t)(limit - s), s16, s16Length, sink, edits, errorCode); -} - -void -ByteSinkUtil::appendCodePoint(int32_t length, UChar32 c, ByteSink &sink, Edits *edits) { - char s8[U8_MAX_LENGTH]; - int32_t s8Length = 0; - U8_APPEND_UNSAFE(s8, s8Length, c); - if (edits != nullptr) { - edits->addReplace(length, s8Length); - } - sink.Append(s8, s8Length); -} - -namespace { - -// See unicode/utf8.h U8_APPEND_UNSAFE(). -inline uint8_t getTwoByteLead(UChar32 c) { return (uint8_t)((c >> 6) | 0xc0); } -inline uint8_t getTwoByteTrail(UChar32 c) { return (uint8_t)((c & 0x3f) | 0x80); } - -} // namespace - -void -ByteSinkUtil::appendTwoBytes(UChar32 c, ByteSink &sink) { - U_ASSERT(0x80 <= c && c <= 0x7ff); // 2-byte UTF-8 - char s8[2] = { (char)getTwoByteLead(c), (char)getTwoByteTrail(c) }; - sink.Append(s8, 2); -} - -void -ByteSinkUtil::appendNonEmptyUnchanged(const uint8_t *s, int32_t length, - ByteSink &sink, uint32_t options, Edits *edits) { - U_ASSERT(length > 0); - if (edits != nullptr) { - edits->addUnchanged(length); - } - if ((options & U_OMIT_UNCHANGED_TEXT) == 0) { - sink.Append(reinterpret_cast(s), length); - } -} - -UBool -ByteSinkUtil::appendUnchanged(const uint8_t *s, const uint8_t *limit, - ByteSink &sink, uint32_t options, Edits *edits, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return false; } - if ((limit - s) > INT32_MAX) { - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return false; - } - int32_t length = (int32_t)(limit - s); - if (length > 0) { - appendNonEmptyUnchanged(s, length, sink, options, edits); - } - return true; -} - -CharStringByteSink::CharStringByteSink(CharString* dest) : dest_(*dest) { -} - -CharStringByteSink::~CharStringByteSink() = default; - -void -CharStringByteSink::Append(const char* bytes, int32_t n) { - UErrorCode status = U_ZERO_ERROR; - dest_.append(bytes, n, status); - // Any errors are silently ignored. -} - -char* -CharStringByteSink::GetAppendBuffer(int32_t min_capacity, - int32_t desired_capacity_hint, - char* scratch, - int32_t scratch_capacity, - int32_t* result_capacity) { - if (min_capacity < 1 || scratch_capacity < min_capacity) { - *result_capacity = 0; - return nullptr; - } - - UErrorCode status = U_ZERO_ERROR; - char* result = dest_.getAppendBuffer( - min_capacity, - desired_capacity_hint, - *result_capacity, - status); - if (U_SUCCESS(status)) { - return result; - } - - *result_capacity = scratch_capacity; - return scratch; -} - -U_NAMESPACE_END +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// bytesinkutil.cpp +// created: 2017sep14 Markus W. Scherer + +#include "unicode/utypes.h" +#include "unicode/bytestream.h" +#include "unicode/edits.h" +#include "unicode/stringoptions.h" +#include "unicode/utf8.h" +#include "unicode/utf16.h" +#include "bytesinkutil.h" +#include "charstr.h" +#include "cmemory.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +UBool +ByteSinkUtil::appendChange(int32_t length, const char16_t *s16, int32_t s16Length, + ByteSink &sink, Edits *edits, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return false; } + char scratch[200]; + int32_t s8Length = 0; + for (int32_t i = 0; i < s16Length;) { + int32_t capacity; + int32_t desiredCapacity = s16Length - i; + if (desiredCapacity < (INT32_MAX / 3)) { + desiredCapacity *= 3; // max 3 UTF-8 bytes per UTF-16 code unit + } else if (desiredCapacity < (INT32_MAX / 2)) { + desiredCapacity *= 2; + } else { + desiredCapacity = INT32_MAX; + } + char *buffer = sink.GetAppendBuffer(U8_MAX_LENGTH, desiredCapacity, + scratch, UPRV_LENGTHOF(scratch), &capacity); + capacity -= U8_MAX_LENGTH - 1; + int32_t j = 0; + for (; i < s16Length && j < capacity;) { + UChar32 c; + U16_NEXT_UNSAFE(s16, i, c); + U8_APPEND_UNSAFE(buffer, j, c); + } + if (j > (INT32_MAX - s8Length)) { + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return false; + } + sink.Append(buffer, j); + s8Length += j; + } + if (edits != nullptr) { + edits->addReplace(length, s8Length); + } + return true; +} + +UBool +ByteSinkUtil::appendChange(const uint8_t *s, const uint8_t *limit, + const char16_t *s16, int32_t s16Length, + ByteSink &sink, Edits *edits, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return false; } + if ((limit - s) > INT32_MAX) { + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return false; + } + return appendChange((int32_t)(limit - s), s16, s16Length, sink, edits, errorCode); +} + +void +ByteSinkUtil::appendCodePoint(int32_t length, UChar32 c, ByteSink &sink, Edits *edits) { + char s8[U8_MAX_LENGTH]; + int32_t s8Length = 0; + U8_APPEND_UNSAFE(s8, s8Length, c); + if (edits != nullptr) { + edits->addReplace(length, s8Length); + } + sink.Append(s8, s8Length); +} + +namespace { + +// See unicode/utf8.h U8_APPEND_UNSAFE(). +inline uint8_t getTwoByteLead(UChar32 c) { return (uint8_t)((c >> 6) | 0xc0); } +inline uint8_t getTwoByteTrail(UChar32 c) { return (uint8_t)((c & 0x3f) | 0x80); } + +} // namespace + +void +ByteSinkUtil::appendTwoBytes(UChar32 c, ByteSink &sink) { + U_ASSERT(0x80 <= c && c <= 0x7ff); // 2-byte UTF-8 + char s8[2] = { (char)getTwoByteLead(c), (char)getTwoByteTrail(c) }; + sink.Append(s8, 2); +} + +void +ByteSinkUtil::appendNonEmptyUnchanged(const uint8_t *s, int32_t length, + ByteSink &sink, uint32_t options, Edits *edits) { + U_ASSERT(length > 0); + if (edits != nullptr) { + edits->addUnchanged(length); + } + if ((options & U_OMIT_UNCHANGED_TEXT) == 0) { + sink.Append(reinterpret_cast(s), length); + } +} + +UBool +ByteSinkUtil::appendUnchanged(const uint8_t *s, const uint8_t *limit, + ByteSink &sink, uint32_t options, Edits *edits, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return false; } + if ((limit - s) > INT32_MAX) { + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return false; + } + int32_t length = (int32_t)(limit - s); + if (length > 0) { + appendNonEmptyUnchanged(s, length, sink, options, edits); + } + return true; +} + +CharStringByteSink::CharStringByteSink(CharString* dest) : dest_(*dest) { +} + +CharStringByteSink::~CharStringByteSink() = default; + +void +CharStringByteSink::Append(const char* bytes, int32_t n) { + UErrorCode status = U_ZERO_ERROR; + dest_.append(bytes, n, status); + // Any errors are silently ignored. +} + +char* +CharStringByteSink::GetAppendBuffer(int32_t min_capacity, + int32_t desired_capacity_hint, + char* scratch, + int32_t scratch_capacity, + int32_t* result_capacity) { + if (min_capacity < 1 || scratch_capacity < min_capacity) { + *result_capacity = 0; + return nullptr; + } + + UErrorCode status = U_ZERO_ERROR; + char* result = dest_.getAppendBuffer( + min_capacity, + desired_capacity_hint, + *result_capacity, + status); + if (U_SUCCESS(status)) { + return result; + } + + *result_capacity = scratch_capacity; + return scratch; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/bytesinkutil.h b/deps/icu-small/source/common/bytesinkutil.h index 929c71fbee6bc7..391e18b3cb5b99 100644 --- a/deps/icu-small/source/common/bytesinkutil.h +++ b/deps/icu-small/source/common/bytesinkutil.h @@ -1,88 +1,88 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// bytesinkutil.h -// created: 2017sep14 Markus W. Scherer - -#ifndef BYTESINKUTIL_H -#define BYTESINKUTIL_H - -#include "unicode/utypes.h" -#include "unicode/bytestream.h" -#include "unicode/edits.h" -#include "cmemory.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - -class ByteSink; -class CharString; -class Edits; - -class U_COMMON_API ByteSinkUtil { -public: - ByteSinkUtil() = delete; // all static - - /** (length) bytes were mapped to valid (s16, s16Length). */ - static UBool appendChange(int32_t length, - const char16_t *s16, int32_t s16Length, - ByteSink &sink, Edits *edits, UErrorCode &errorCode); - - /** The bytes at [s, limit[ were mapped to valid (s16, s16Length). */ - static UBool appendChange(const uint8_t *s, const uint8_t *limit, - const char16_t *s16, int32_t s16Length, - ByteSink &sink, Edits *edits, UErrorCode &errorCode); - - /** (length) bytes were mapped/changed to valid code point c. */ - static void appendCodePoint(int32_t length, UChar32 c, ByteSink &sink, Edits *edits = nullptr); - - /** The few bytes at [src, nextSrc[ were mapped/changed to valid code point c. */ - static inline void appendCodePoint(const uint8_t *src, const uint8_t *nextSrc, UChar32 c, - ByteSink &sink, Edits *edits = nullptr) { - appendCodePoint((int32_t)(nextSrc - src), c, sink, edits); - } - - /** Append the two-byte character (U+0080..U+07FF). */ - static void appendTwoBytes(UChar32 c, ByteSink &sink); - - static UBool appendUnchanged(const uint8_t *s, int32_t length, - ByteSink &sink, uint32_t options, Edits *edits, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return false; } - if (length > 0) { appendNonEmptyUnchanged(s, length, sink, options, edits); } - return true; - } - - static UBool appendUnchanged(const uint8_t *s, const uint8_t *limit, - ByteSink &sink, uint32_t options, Edits *edits, - UErrorCode &errorCode); - -private: - static void appendNonEmptyUnchanged(const uint8_t *s, int32_t length, - ByteSink &sink, uint32_t options, Edits *edits); -}; - -class U_COMMON_API CharStringByteSink : public ByteSink { -public: - CharStringByteSink(CharString* dest); - ~CharStringByteSink() override; - - CharStringByteSink() = delete; - CharStringByteSink(const CharStringByteSink&) = delete; - CharStringByteSink& operator=(const CharStringByteSink&) = delete; - - void Append(const char* bytes, int32_t n) override; - - char* GetAppendBuffer(int32_t min_capacity, - int32_t desired_capacity_hint, - char* scratch, - int32_t scratch_capacity, - int32_t* result_capacity) override; - -private: - CharString& dest_; -}; - -U_NAMESPACE_END - -#endif //BYTESINKUTIL_H +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// bytesinkutil.h +// created: 2017sep14 Markus W. Scherer + +#ifndef BYTESINKUTIL_H +#define BYTESINKUTIL_H + +#include "unicode/utypes.h" +#include "unicode/bytestream.h" +#include "unicode/edits.h" +#include "cmemory.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +class ByteSink; +class CharString; +class Edits; + +class U_COMMON_API ByteSinkUtil { +public: + ByteSinkUtil() = delete; // all static + + /** (length) bytes were mapped to valid (s16, s16Length). */ + static UBool appendChange(int32_t length, + const char16_t *s16, int32_t s16Length, + ByteSink &sink, Edits *edits, UErrorCode &errorCode); + + /** The bytes at [s, limit[ were mapped to valid (s16, s16Length). */ + static UBool appendChange(const uint8_t *s, const uint8_t *limit, + const char16_t *s16, int32_t s16Length, + ByteSink &sink, Edits *edits, UErrorCode &errorCode); + + /** (length) bytes were mapped/changed to valid code point c. */ + static void appendCodePoint(int32_t length, UChar32 c, ByteSink &sink, Edits *edits = nullptr); + + /** The few bytes at [src, nextSrc[ were mapped/changed to valid code point c. */ + static inline void appendCodePoint(const uint8_t *src, const uint8_t *nextSrc, UChar32 c, + ByteSink &sink, Edits *edits = nullptr) { + appendCodePoint((int32_t)(nextSrc - src), c, sink, edits); + } + + /** Append the two-byte character (U+0080..U+07FF). */ + static void appendTwoBytes(UChar32 c, ByteSink &sink); + + static UBool appendUnchanged(const uint8_t *s, int32_t length, + ByteSink &sink, uint32_t options, Edits *edits, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return false; } + if (length > 0) { appendNonEmptyUnchanged(s, length, sink, options, edits); } + return true; + } + + static UBool appendUnchanged(const uint8_t *s, const uint8_t *limit, + ByteSink &sink, uint32_t options, Edits *edits, + UErrorCode &errorCode); + +private: + static void appendNonEmptyUnchanged(const uint8_t *s, int32_t length, + ByteSink &sink, uint32_t options, Edits *edits); +}; + +class U_COMMON_API CharStringByteSink : public ByteSink { +public: + CharStringByteSink(CharString* dest); + ~CharStringByteSink() override; + + CharStringByteSink() = delete; + CharStringByteSink(const CharStringByteSink&) = delete; + CharStringByteSink& operator=(const CharStringByteSink&) = delete; + + void Append(const char* bytes, int32_t n) override; + + char* GetAppendBuffer(int32_t min_capacity, + int32_t desired_capacity_hint, + char* scratch, + int32_t scratch_capacity, + int32_t* result_capacity) override; + +private: + CharString& dest_; +}; + +U_NAMESPACE_END + +#endif //BYTESINKUTIL_H diff --git a/deps/icu-small/source/common/bytestream.cpp b/deps/icu-small/source/common/bytestream.cpp index c14f206dfe43c8..90780442337c66 100644 --- a/deps/icu-small/source/common/bytestream.cpp +++ b/deps/icu-small/source/common/bytestream.cpp @@ -1,85 +1,85 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// Copyright (C) 2009-2011, International Business Machines -// Corporation and others. All Rights Reserved. -// -// Copyright 2007 Google Inc. All Rights Reserved. -// Author: sanjay@google.com (Sanjay Ghemawat) - -#include "unicode/utypes.h" -#include "unicode/bytestream.h" -#include "cmemory.h" - -U_NAMESPACE_BEGIN - -ByteSink::~ByteSink() {} - -char* ByteSink::GetAppendBuffer(int32_t min_capacity, - int32_t /*desired_capacity_hint*/, - char* scratch, int32_t scratch_capacity, - int32_t* result_capacity) { - if (min_capacity < 1 || scratch_capacity < min_capacity) { - *result_capacity = 0; - return NULL; - } - *result_capacity = scratch_capacity; - return scratch; -} - -void ByteSink::Flush() {} - -CheckedArrayByteSink::CheckedArrayByteSink(char* outbuf, int32_t capacity) - : outbuf_(outbuf), capacity_(capacity < 0 ? 0 : capacity), - size_(0), appended_(0), overflowed_(false) { -} - -CheckedArrayByteSink::~CheckedArrayByteSink() {} - -CheckedArrayByteSink& CheckedArrayByteSink::Reset() { - size_ = appended_ = 0; - overflowed_ = false; - return *this; -} - -void CheckedArrayByteSink::Append(const char* bytes, int32_t n) { - if (n <= 0) { - return; - } - if (n > (INT32_MAX - appended_)) { - // TODO: Report as integer overflow, not merely buffer overflow. - appended_ = INT32_MAX; - overflowed_ = true; - return; - } - appended_ += n; - int32_t available = capacity_ - size_; - if (n > available) { - n = available; - overflowed_ = true; - } - if (n > 0 && bytes != (outbuf_ + size_)) { - uprv_memcpy(outbuf_ + size_, bytes, n); - } - size_ += n; -} - -char* CheckedArrayByteSink::GetAppendBuffer(int32_t min_capacity, - int32_t /*desired_capacity_hint*/, - char* scratch, - int32_t scratch_capacity, - int32_t* result_capacity) { - if (min_capacity < 1 || scratch_capacity < min_capacity) { - *result_capacity = 0; - return NULL; - } - int32_t available = capacity_ - size_; - if (available >= min_capacity) { - *result_capacity = available; - return outbuf_ + size_; - } else { - *result_capacity = scratch_capacity; - return scratch; - } -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// Copyright (C) 2009-2011, International Business Machines +// Corporation and others. All Rights Reserved. +// +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: sanjay@google.com (Sanjay Ghemawat) + +#include "unicode/utypes.h" +#include "unicode/bytestream.h" +#include "cmemory.h" + +U_NAMESPACE_BEGIN + +ByteSink::~ByteSink() {} + +char* ByteSink::GetAppendBuffer(int32_t min_capacity, + int32_t /*desired_capacity_hint*/, + char* scratch, int32_t scratch_capacity, + int32_t* result_capacity) { + if (min_capacity < 1 || scratch_capacity < min_capacity) { + *result_capacity = 0; + return nullptr; + } + *result_capacity = scratch_capacity; + return scratch; +} + +void ByteSink::Flush() {} + +CheckedArrayByteSink::CheckedArrayByteSink(char* outbuf, int32_t capacity) + : outbuf_(outbuf), capacity_(capacity < 0 ? 0 : capacity), + size_(0), appended_(0), overflowed_(false) { +} + +CheckedArrayByteSink::~CheckedArrayByteSink() {} + +CheckedArrayByteSink& CheckedArrayByteSink::Reset() { + size_ = appended_ = 0; + overflowed_ = false; + return *this; +} + +void CheckedArrayByteSink::Append(const char* bytes, int32_t n) { + if (n <= 0) { + return; + } + if (n > (INT32_MAX - appended_)) { + // TODO: Report as integer overflow, not merely buffer overflow. + appended_ = INT32_MAX; + overflowed_ = true; + return; + } + appended_ += n; + int32_t available = capacity_ - size_; + if (n > available) { + n = available; + overflowed_ = true; + } + if (n > 0 && bytes != (outbuf_ + size_)) { + uprv_memcpy(outbuf_ + size_, bytes, n); + } + size_ += n; +} + +char* CheckedArrayByteSink::GetAppendBuffer(int32_t min_capacity, + int32_t /*desired_capacity_hint*/, + char* scratch, + int32_t scratch_capacity, + int32_t* result_capacity) { + if (min_capacity < 1 || scratch_capacity < min_capacity) { + *result_capacity = 0; + return nullptr; + } + int32_t available = capacity_ - size_; + if (available >= min_capacity) { + *result_capacity = available; + return outbuf_ + size_; + } else { + *result_capacity = scratch_capacity; + return scratch; + } +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/bytestrie.cpp b/deps/icu-small/source/common/bytestrie.cpp index c272cc40221f8c..0c6f50ae275f93 100644 --- a/deps/icu-small/source/common/bytestrie.cpp +++ b/deps/icu-small/source/common/bytestrie.cpp @@ -1,441 +1,441 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: bytestrie.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010sep25 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" -#include "unicode/bytestream.h" -#include "unicode/bytestrie.h" -#include "unicode/uobject.h" -#include "cmemory.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - -BytesTrie::~BytesTrie() { - uprv_free(ownedArray_); -} - -// lead byte already shifted right by 1. -int32_t -BytesTrie::readValue(const uint8_t *pos, int32_t leadByte) { - int32_t value; - if(leadByte=kMinValueLead) ? - valueResult(node) : USTRINGTRIE_NO_VALUE; - } -} - -UStringTrieResult -BytesTrie::branchNext(const uint8_t *pos, int32_t length, int32_t inByte) { - // Branch according to the current byte. - if(length==0) { - length=*pos++; - } - ++length; - // The length of the branch is the number of bytes to select from. - // The data structure encodes a binary search. - while(length>kMaxBranchLinearSubNodeLength) { - if(inByte<*pos++) { - length>>=1; - pos=jumpByDelta(pos); - } else { - length=length-(length>>1); - pos=skipDelta(pos); - } - } - // Drop down to linear search for the last few bytes. - // length>=2 because the loop body above sees length>kMaxBranchLinearSubNodeLength>=3 - // and divides length by 2. - do { - if(inByte==*pos++) { - UStringTrieResult result; - int32_t node=*pos; - U_ASSERT(node>=kMinValueLead); - if(node&kValueIsFinal) { - // Leave the final value for getValue() to read. - result=USTRINGTRIE_FINAL_VALUE; - } else { - // Use the non-final value as the jump delta. - ++pos; - // int32_t delta=readValue(pos, node>>1); - node>>=1; - int32_t delta; - if(node=kMinValueLead ? valueResult(node) : USTRINGTRIE_NO_VALUE; - } - pos_=pos; - return result; - } - --length; - pos=skipValue(pos); - } while(length>1); - if(inByte==*pos++) { - pos_=pos; - int32_t node=*pos; - return node>=kMinValueLead ? valueResult(node) : USTRINGTRIE_NO_VALUE; - } else { - stop(); - return USTRINGTRIE_NO_MATCH; - } -} - -UStringTrieResult -BytesTrie::nextImpl(const uint8_t *pos, int32_t inByte) { - for(;;) { - int32_t node=*pos++; - if(node=kMinValueLead) ? - valueResult(node) : USTRINGTRIE_NO_VALUE; - } else { - // No match. - break; - } - } else if(node&kValueIsFinal) { - // No further matching bytes. - break; - } else { - // Skip intermediate value. - pos=skipValue(pos, node); - // The next node must not also be a value node. - U_ASSERT(*pos=0) { - // Remaining part of a linear-match node. - if(inByte==*pos++) { - remainingMatchLength_=--length; - pos_=pos; - int32_t node; - return (length<0 && (node=*pos)>=kMinValueLead) ? - valueResult(node) : USTRINGTRIE_NO_VALUE; - } else { - stop(); - return USTRINGTRIE_NO_MATCH; - } - } - return nextImpl(pos, inByte); -} - -UStringTrieResult -BytesTrie::next(const char *s, int32_t sLength) { - if(sLength<0 ? *s==0 : sLength==0) { - // Empty input. - return current(); - } - const uint8_t *pos=pos_; - if(pos==NULL) { - return USTRINGTRIE_NO_MATCH; - } - int32_t length=remainingMatchLength_; // Actual remaining match length minus 1. - for(;;) { - // Fetch the next input byte, if there is one. - // Continue a linear-match node without rechecking sLength<0. - int32_t inByte; - if(sLength<0) { - for(;;) { - if((inByte=*s++)==0) { - remainingMatchLength_=length; - pos_=pos; - int32_t node; - return (length<0 && (node=*pos)>=kMinValueLead) ? - valueResult(node) : USTRINGTRIE_NO_VALUE; - } - if(length<0) { - remainingMatchLength_=length; - break; - } - if(inByte!=*pos) { - stop(); - return USTRINGTRIE_NO_MATCH; - } - ++pos; - --length; - } - } else { - for(;;) { - if(sLength==0) { - remainingMatchLength_=length; - pos_=pos; - int32_t node; - return (length<0 && (node=*pos)>=kMinValueLead) ? - valueResult(node) : USTRINGTRIE_NO_VALUE; - } - inByte=*s++; - --sLength; - if(length<0) { - remainingMatchLength_=length; - break; - } - if(inByte!=*pos) { - stop(); - return USTRINGTRIE_NO_MATCH; - } - ++pos; - --length; - } - } - for(;;) { - int32_t node=*pos++; - if(nodekMaxBranchLinearSubNodeLength) { - ++pos; // ignore the comparison byte - if(NULL==findUniqueValueFromBranch(jumpByDelta(pos), length>>1, haveUniqueValue, uniqueValue)) { - return NULL; - } - length=length-(length>>1); - pos=skipDelta(pos); - } - do { - ++pos; // ignore a comparison byte - // handle its value - int32_t node=*pos++; - UBool isFinal=(UBool)(node&kValueIsFinal); - int32_t value=readValue(pos, node>>1); - pos=skipValue(pos, node); - if(isFinal) { - if(haveUniqueValue) { - if(value!=uniqueValue) { - return NULL; - } - } else { - uniqueValue=value; - haveUniqueValue=true; - } - } else { - if(!findUniqueValue(pos+value, haveUniqueValue, uniqueValue)) { - return NULL; - } - haveUniqueValue=true; - } - } while(--length>1); - return pos+1; // ignore the last comparison byte -} - -UBool -BytesTrie::findUniqueValue(const uint8_t *pos, UBool haveUniqueValue, int32_t &uniqueValue) { - for(;;) { - int32_t node=*pos++; - if(node>1); - if(haveUniqueValue) { - if(value!=uniqueValue) { - return false; - } - } else { - uniqueValue=value; - haveUniqueValue=true; - } - if(isFinal) { - return true; - } - pos=skipValue(pos, node); - } - } -} - -int32_t -BytesTrie::getNextBytes(ByteSink &out) const { - const uint8_t *pos=pos_; - if(pos==NULL) { - return 0; - } - if(remainingMatchLength_>=0) { - append(out, *pos); // Next byte of a pending linear-match node. - return 1; - } - int32_t node=*pos++; - if(node>=kMinValueLead) { - if(node&kValueIsFinal) { - return 0; - } else { - pos=skipValue(pos, node); - node=*pos++; - U_ASSERT(nodekMaxBranchLinearSubNodeLength) { - ++pos; // ignore the comparison byte - getNextBranchBytes(jumpByDelta(pos), length>>1, out); - length=length-(length>>1); - pos=skipDelta(pos); - } - do { - append(out, *pos++); - pos=skipValue(pos); - } while(--length>1); - append(out, *pos); -} - -void -BytesTrie::append(ByteSink &out, int c) { - char ch=(char)c; - out.Append(&ch, 1); -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: bytestrie.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010sep25 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" +#include "unicode/bytestream.h" +#include "unicode/bytestrie.h" +#include "unicode/uobject.h" +#include "cmemory.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +BytesTrie::~BytesTrie() { + uprv_free(ownedArray_); +} + +// lead byte already shifted right by 1. +int32_t +BytesTrie::readValue(const uint8_t *pos, int32_t leadByte) { + int32_t value; + if(leadByte=kMinValueLead) ? + valueResult(node) : USTRINGTRIE_NO_VALUE; + } +} + +UStringTrieResult +BytesTrie::branchNext(const uint8_t *pos, int32_t length, int32_t inByte) { + // Branch according to the current byte. + if(length==0) { + length=*pos++; + } + ++length; + // The length of the branch is the number of bytes to select from. + // The data structure encodes a binary search. + while(length>kMaxBranchLinearSubNodeLength) { + if(inByte<*pos++) { + length>>=1; + pos=jumpByDelta(pos); + } else { + length=length-(length>>1); + pos=skipDelta(pos); + } + } + // Drop down to linear search for the last few bytes. + // length>=2 because the loop body above sees length>kMaxBranchLinearSubNodeLength>=3 + // and divides length by 2. + do { + if(inByte==*pos++) { + UStringTrieResult result; + int32_t node=*pos; + U_ASSERT(node>=kMinValueLead); + if(node&kValueIsFinal) { + // Leave the final value for getValue() to read. + result=USTRINGTRIE_FINAL_VALUE; + } else { + // Use the non-final value as the jump delta. + ++pos; + // int32_t delta=readValue(pos, node>>1); + node>>=1; + int32_t delta; + if(node=kMinValueLead ? valueResult(node) : USTRINGTRIE_NO_VALUE; + } + pos_=pos; + return result; + } + --length; + pos=skipValue(pos); + } while(length>1); + if(inByte==*pos++) { + pos_=pos; + int32_t node=*pos; + return node>=kMinValueLead ? valueResult(node) : USTRINGTRIE_NO_VALUE; + } else { + stop(); + return USTRINGTRIE_NO_MATCH; + } +} + +UStringTrieResult +BytesTrie::nextImpl(const uint8_t *pos, int32_t inByte) { + for(;;) { + int32_t node=*pos++; + if(node=kMinValueLead) ? + valueResult(node) : USTRINGTRIE_NO_VALUE; + } else { + // No match. + break; + } + } else if(node&kValueIsFinal) { + // No further matching bytes. + break; + } else { + // Skip intermediate value. + pos=skipValue(pos, node); + // The next node must not also be a value node. + U_ASSERT(*pos=0) { + // Remaining part of a linear-match node. + if(inByte==*pos++) { + remainingMatchLength_=--length; + pos_=pos; + int32_t node; + return (length<0 && (node=*pos)>=kMinValueLead) ? + valueResult(node) : USTRINGTRIE_NO_VALUE; + } else { + stop(); + return USTRINGTRIE_NO_MATCH; + } + } + return nextImpl(pos, inByte); +} + +UStringTrieResult +BytesTrie::next(const char *s, int32_t sLength) { + if(sLength<0 ? *s==0 : sLength==0) { + // Empty input. + return current(); + } + const uint8_t *pos=pos_; + if(pos==nullptr) { + return USTRINGTRIE_NO_MATCH; + } + int32_t length=remainingMatchLength_; // Actual remaining match length minus 1. + for(;;) { + // Fetch the next input byte, if there is one. + // Continue a linear-match node without rechecking sLength<0. + int32_t inByte; + if(sLength<0) { + for(;;) { + if((inByte=*s++)==0) { + remainingMatchLength_=length; + pos_=pos; + int32_t node; + return (length<0 && (node=*pos)>=kMinValueLead) ? + valueResult(node) : USTRINGTRIE_NO_VALUE; + } + if(length<0) { + remainingMatchLength_=length; + break; + } + if(inByte!=*pos) { + stop(); + return USTRINGTRIE_NO_MATCH; + } + ++pos; + --length; + } + } else { + for(;;) { + if(sLength==0) { + remainingMatchLength_=length; + pos_=pos; + int32_t node; + return (length<0 && (node=*pos)>=kMinValueLead) ? + valueResult(node) : USTRINGTRIE_NO_VALUE; + } + inByte=*s++; + --sLength; + if(length<0) { + remainingMatchLength_=length; + break; + } + if(inByte!=*pos) { + stop(); + return USTRINGTRIE_NO_MATCH; + } + ++pos; + --length; + } + } + for(;;) { + int32_t node=*pos++; + if(nodekMaxBranchLinearSubNodeLength) { + ++pos; // ignore the comparison byte + if(nullptr==findUniqueValueFromBranch(jumpByDelta(pos), length>>1, haveUniqueValue, uniqueValue)) { + return nullptr; + } + length=length-(length>>1); + pos=skipDelta(pos); + } + do { + ++pos; // ignore a comparison byte + // handle its value + int32_t node=*pos++; + UBool isFinal=(UBool)(node&kValueIsFinal); + int32_t value=readValue(pos, node>>1); + pos=skipValue(pos, node); + if(isFinal) { + if(haveUniqueValue) { + if(value!=uniqueValue) { + return nullptr; + } + } else { + uniqueValue=value; + haveUniqueValue=true; + } + } else { + if(!findUniqueValue(pos+value, haveUniqueValue, uniqueValue)) { + return nullptr; + } + haveUniqueValue=true; + } + } while(--length>1); + return pos+1; // ignore the last comparison byte +} + +UBool +BytesTrie::findUniqueValue(const uint8_t *pos, UBool haveUniqueValue, int32_t &uniqueValue) { + for(;;) { + int32_t node=*pos++; + if(node>1); + if(haveUniqueValue) { + if(value!=uniqueValue) { + return false; + } + } else { + uniqueValue=value; + haveUniqueValue=true; + } + if(isFinal) { + return true; + } + pos=skipValue(pos, node); + } + } +} + +int32_t +BytesTrie::getNextBytes(ByteSink &out) const { + const uint8_t *pos=pos_; + if(pos==nullptr) { + return 0; + } + if(remainingMatchLength_>=0) { + append(out, *pos); // Next byte of a pending linear-match node. + return 1; + } + int32_t node=*pos++; + if(node>=kMinValueLead) { + if(node&kValueIsFinal) { + return 0; + } else { + pos=skipValue(pos, node); + node=*pos++; + U_ASSERT(nodekMaxBranchLinearSubNodeLength) { + ++pos; // ignore the comparison byte + getNextBranchBytes(jumpByDelta(pos), length>>1, out); + length=length-(length>>1); + pos=skipDelta(pos); + } + do { + append(out, *pos++); + pos=skipValue(pos); + } while(--length>1); + append(out, *pos); +} + +void +BytesTrie::append(ByteSink &out, int c) { + char ch=(char)c; + out.Append(&ch, 1); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/bytestriebuilder.cpp b/deps/icu-small/source/common/bytestriebuilder.cpp index ac7d3d867e5f77..d10f3d66fc868d 100644 --- a/deps/icu-small/source/common/bytestriebuilder.cpp +++ b/deps/icu-small/source/common/bytestriebuilder.cpp @@ -1,512 +1,512 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2012, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: bytestriebuilder.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010sep25 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" -#include "unicode/bytestrie.h" -#include "unicode/bytestriebuilder.h" -#include "unicode/stringpiece.h" -#include "charstr.h" -#include "cmemory.h" -#include "uhash.h" -#include "uarrsort.h" -#include "uassert.h" -#include "ustr_imp.h" - -U_NAMESPACE_BEGIN - -/* - * Note: This builder implementation stores (bytes, value) pairs with full copies - * of the byte sequences, until the BytesTrie is built. - * It might(!) take less memory if we collected the data in a temporary, dynamic trie. - */ - -class BytesTrieElement : public UMemory { -public: - // Use compiler's default constructor, initializes nothing. - - void setTo(StringPiece s, int32_t val, CharString &strings, UErrorCode &errorCode); - - StringPiece getString(const CharString &strings) const { - int32_t offset=stringOffset; - int32_t length; - if(offset>=0) { - length=(uint8_t)strings[offset++]; - } else { - offset=~offset; - length=((int32_t)(uint8_t)strings[offset]<<8)|(uint8_t)strings[offset+1]; - offset+=2; - } - return StringPiece(strings.data()+offset, length); - } - int32_t getStringLength(const CharString &strings) const { - int32_t offset=stringOffset; - if(offset>=0) { - return (uint8_t)strings[offset]; - } else { - offset=~offset; - return ((int32_t)(uint8_t)strings[offset]<<8)|(uint8_t)strings[offset+1]; - } - } - - char charAt(int32_t index, const CharString &strings) const { return data(strings)[index]; } - - int32_t getValue() const { return value; } - - int32_t compareStringTo(const BytesTrieElement &o, const CharString &strings) const; - -private: - const char *data(const CharString &strings) const { - int32_t offset=stringOffset; - if(offset>=0) { - ++offset; - } else { - offset=~offset+2; - } - return strings.data()+offset; - } - - // If the stringOffset is non-negative, then the first strings byte contains - // the string length. - // If the stringOffset is negative, then the first two strings bytes contain - // the string length (big-endian), and the offset needs to be bit-inverted. - // (Compared with a stringLength field here, this saves 3 bytes per string for most strings.) - int32_t stringOffset; - int32_t value; -}; - -void -BytesTrieElement::setTo(StringPiece s, int32_t val, - CharString &strings, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return; - } - int32_t length=s.length(); - if(length>0xffff) { - // Too long: We store the length in 1 or 2 bytes. - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return; - } - int32_t offset=strings.length(); - if(length>0xff) { - offset=~offset; - strings.append((char)(length>>8), errorCode); - } - strings.append((char)length, errorCode); - stringOffset=offset; - value=val; - strings.append(s, errorCode); -} - -int32_t -BytesTrieElement::compareStringTo(const BytesTrieElement &other, const CharString &strings) const { - // TODO: add StringPiece::compare(), see ticket #8187 - StringPiece thisString=getString(strings); - StringPiece otherString=other.getString(strings); - int32_t lengthDiff=thisString.length()-otherString.length(); - int32_t commonLength; - if(lengthDiff<=0) { - commonLength=thisString.length(); - } else { - commonLength=otherString.length(); - } - int32_t diff=uprv_memcmp(thisString.data(), otherString.data(), commonLength); - return diff!=0 ? diff : lengthDiff; -} - -BytesTrieBuilder::BytesTrieBuilder(UErrorCode &errorCode) - : strings(NULL), elements(NULL), elementsCapacity(0), elementsLength(0), - bytes(NULL), bytesCapacity(0), bytesLength(0) { - if(U_FAILURE(errorCode)) { - return; - } - strings=new CharString(); - if(strings==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } -} - -BytesTrieBuilder::~BytesTrieBuilder() { - delete strings; - delete[] elements; - uprv_free(bytes); -} - -BytesTrieBuilder & -BytesTrieBuilder::add(StringPiece s, int32_t value, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return *this; - } - if(bytesLength>0) { - // Cannot add elements after building. - errorCode=U_NO_WRITE_PERMISSION; - return *this; - } - if(elementsLength==elementsCapacity) { - int32_t newCapacity; - if(elementsCapacity==0) { - newCapacity=1024; - } else { - newCapacity=4*elementsCapacity; - } - BytesTrieElement *newElements=new BytesTrieElement[newCapacity]; - if(newElements==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return *this; // error instead of dereferencing null - } - if(elementsLength>0) { - uprv_memcpy(newElements, elements, (size_t)elementsLength*sizeof(BytesTrieElement)); - } - delete[] elements; - elements=newElements; - elementsCapacity=newCapacity; - } - elements[elementsLength++].setTo(s, value, *strings, errorCode); - return *this; -} - -U_CDECL_BEGIN - -static int32_t U_CALLCONV -compareElementStrings(const void *context, const void *left, const void *right) { - const CharString *strings=static_cast(context); - const BytesTrieElement *leftElement=static_cast(left); - const BytesTrieElement *rightElement=static_cast(right); - return leftElement->compareStringTo(*rightElement, *strings); -} - -U_CDECL_END - -BytesTrie * -BytesTrieBuilder::build(UStringTrieBuildOption buildOption, UErrorCode &errorCode) { - buildBytes(buildOption, errorCode); - BytesTrie *newTrie=NULL; - if(U_SUCCESS(errorCode)) { - newTrie=new BytesTrie(bytes, bytes+(bytesCapacity-bytesLength)); - if(newTrie==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } else { - bytes=NULL; // The new trie now owns the array. - bytesCapacity=0; - } - } - return newTrie; -} - -StringPiece -BytesTrieBuilder::buildStringPiece(UStringTrieBuildOption buildOption, UErrorCode &errorCode) { - buildBytes(buildOption, errorCode); - StringPiece result; - if(U_SUCCESS(errorCode)) { - result.set(bytes+(bytesCapacity-bytesLength), bytesLength); - } - return result; -} - -void -BytesTrieBuilder::buildBytes(UStringTrieBuildOption buildOption, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return; - } - if(bytes!=NULL && bytesLength>0) { - // Already built. - return; - } - if(bytesLength==0) { - if(elementsLength==0) { - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return; - } - uprv_sortArray(elements, elementsLength, (int32_t)sizeof(BytesTrieElement), - compareElementStrings, strings, - false, // need not be a stable sort - &errorCode); - if(U_FAILURE(errorCode)) { - return; - } - // Duplicate strings are not allowed. - StringPiece prev=elements[0].getString(*strings); - for(int32_t i=1; ilength(); - if(capacity<1024) { - capacity=1024; - } - if(bytesCapacity(uprv_malloc(capacity)); - if(bytes==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - bytesCapacity=0; - return; - } - bytesCapacity=capacity; - } - StringTrieBuilder::build(buildOption, elementsLength, errorCode); - if(bytes==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } -} - -BytesTrieBuilder & -BytesTrieBuilder::clear() { - strings->clear(); - elementsLength=0; - bytesLength=0; - return *this; -} - -int32_t -BytesTrieBuilder::getElementStringLength(int32_t i) const { - return elements[i].getStringLength(*strings); -} - -UChar -BytesTrieBuilder::getElementUnit(int32_t i, int32_t byteIndex) const { - return (uint8_t)elements[i].charAt(byteIndex, *strings); -} - -int32_t -BytesTrieBuilder::getElementValue(int32_t i) const { - return elements[i].getValue(); -} - -int32_t -BytesTrieBuilder::getLimitOfLinearMatch(int32_t first, int32_t last, int32_t byteIndex) const { - const BytesTrieElement &firstElement=elements[first]; - const BytesTrieElement &lastElement=elements[last]; - int32_t minStringLength=firstElement.getStringLength(*strings); - while(++byteIndex0); - return i; -} - -int32_t -BytesTrieBuilder::indexOfElementWithNextUnit(int32_t i, int32_t byteIndex, UChar byte) const { - char b=(char)byte; - while(b==elements[i].charAt(byteIndex, *strings)) { - ++i; - } - return i; -} - -BytesTrieBuilder::BTLinearMatchNode::BTLinearMatchNode(const char *bytes, int32_t len, Node *nextNode) - : LinearMatchNode(len, nextNode), s(bytes) { - hash=static_cast( - static_cast(hash)*37u + static_cast(ustr_hashCharsN(bytes, len))); -} - -bool -BytesTrieBuilder::BTLinearMatchNode::operator==(const Node &other) const { - if(this==&other) { - return true; - } - if(!LinearMatchNode::operator==(other)) { - return false; - } - const BTLinearMatchNode &o=(const BTLinearMatchNode &)other; - return 0==uprv_memcmp(s, o.s, length); -} - -void -BytesTrieBuilder::BTLinearMatchNode::write(StringTrieBuilder &builder) { - BytesTrieBuilder &b=(BytesTrieBuilder &)builder; - next->write(builder); - b.write(s, length); - offset=b.write(b.getMinLinearMatch()+length-1); -} - -StringTrieBuilder::Node * -BytesTrieBuilder::createLinearMatchNode(int32_t i, int32_t byteIndex, int32_t length, - Node *nextNode) const { - return new BTLinearMatchNode( - elements[i].getString(*strings).data()+byteIndex, - length, - nextNode); -} - -UBool -BytesTrieBuilder::ensureCapacity(int32_t length) { - if(bytes==NULL) { - return false; // previous memory allocation had failed - } - if(length>bytesCapacity) { - int32_t newCapacity=bytesCapacity; - do { - newCapacity*=2; - } while(newCapacity<=length); - char *newBytes=static_cast(uprv_malloc(newCapacity)); - if(newBytes==NULL) { - // unable to allocate memory - uprv_free(bytes); - bytes=NULL; - bytesCapacity=0; - return false; - } - uprv_memcpy(newBytes+(newCapacity-bytesLength), - bytes+(bytesCapacity-bytesLength), bytesLength); - uprv_free(bytes); - bytes=newBytes; - bytesCapacity=newCapacity; - } - return true; -} - -int32_t -BytesTrieBuilder::write(int32_t byte) { - int32_t newLength=bytesLength+1; - if(ensureCapacity(newLength)) { - bytesLength=newLength; - bytes[bytesCapacity-bytesLength]=(char)byte; - } - return bytesLength; -} - -int32_t -BytesTrieBuilder::write(const char *b, int32_t length) { - int32_t newLength=bytesLength+length; - if(ensureCapacity(newLength)) { - bytesLength=newLength; - uprv_memcpy(bytes+(bytesCapacity-bytesLength), b, length); - } - return bytesLength; -} - -int32_t -BytesTrieBuilder::writeElementUnits(int32_t i, int32_t byteIndex, int32_t length) { - return write(elements[i].getString(*strings).data()+byteIndex, length); -} - -int32_t -BytesTrieBuilder::writeValueAndFinal(int32_t i, UBool isFinal) { - if(0<=i && i<=BytesTrie::kMaxOneByteValue) { - return write(((BytesTrie::kMinOneByteValueLead+i)<<1)|isFinal); - } - char intBytes[5]; - int32_t length=1; - if(i<0 || i>0xffffff) { - intBytes[0]=(char)BytesTrie::kFiveByteValueLead; - intBytes[1]=(char)((uint32_t)i>>24); - intBytes[2]=(char)((uint32_t)i>>16); - intBytes[3]=(char)((uint32_t)i>>8); - intBytes[4]=(char)i; - length=5; - // } else if(i<=BytesTrie::kMaxOneByteValue) { - // intBytes[0]=(char)(BytesTrie::kMinOneByteValueLead+i); - } else { - if(i<=BytesTrie::kMaxTwoByteValue) { - intBytes[0]=(char)(BytesTrie::kMinTwoByteValueLead+(i>>8)); - } else { - if(i<=BytesTrie::kMaxThreeByteValue) { - intBytes[0]=(char)(BytesTrie::kMinThreeByteValueLead+(i>>16)); - } else { - intBytes[0]=(char)BytesTrie::kFourByteValueLead; - intBytes[1]=(char)(i>>16); - length=2; - } - intBytes[length++]=(char)(i>>8); - } - intBytes[length++]=(char)i; - } - intBytes[0]=(char)((intBytes[0]<<1)|isFinal); - return write(intBytes, length); -} - -int32_t -BytesTrieBuilder::writeValueAndType(UBool hasValue, int32_t value, int32_t node) { - int32_t offset=write(node); - if(hasValue) { - offset=writeValueAndFinal(value, false); - } - return offset; -} - -int32_t -BytesTrieBuilder::writeDeltaTo(int32_t jumpTarget) { - int32_t i=bytesLength-jumpTarget; - U_ASSERT(i>=0); - if(i<=BytesTrie::kMaxOneByteDelta) { - return write(i); - } else { - char intBytes[5]; - return write(intBytes, internalEncodeDelta(i, intBytes)); - } -} - -int32_t -BytesTrieBuilder::internalEncodeDelta(int32_t i, char intBytes[]) { - U_ASSERT(i>=0); - if(i<=BytesTrie::kMaxOneByteDelta) { - intBytes[0]=(char)i; - return 1; - } - int32_t length=1; - if(i<=BytesTrie::kMaxTwoByteDelta) { - intBytes[0]=(char)(BytesTrie::kMinTwoByteDeltaLead+(i>>8)); - } else { - if(i<=BytesTrie::kMaxThreeByteDelta) { - intBytes[0]=(char)(BytesTrie::kMinThreeByteDeltaLead+(i>>16)); - } else { - if(i<=0xffffff) { - intBytes[0]=(char)BytesTrie::kFourByteDeltaLead; - } else { - intBytes[0]=(char)BytesTrie::kFiveByteDeltaLead; - intBytes[1]=(char)(i>>24); - length=2; - } - intBytes[length++]=(char)(i>>16); - } - intBytes[length++]=(char)(i>>8); - } - intBytes[length++]=(char)i; - return length; -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2012, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: bytestriebuilder.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010sep25 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" +#include "unicode/bytestrie.h" +#include "unicode/bytestriebuilder.h" +#include "unicode/stringpiece.h" +#include "charstr.h" +#include "cmemory.h" +#include "uhash.h" +#include "uarrsort.h" +#include "uassert.h" +#include "ustr_imp.h" + +U_NAMESPACE_BEGIN + +/* + * Note: This builder implementation stores (bytes, value) pairs with full copies + * of the byte sequences, until the BytesTrie is built. + * It might(!) take less memory if we collected the data in a temporary, dynamic trie. + */ + +class BytesTrieElement : public UMemory { +public: + // Use compiler's default constructor, initializes nothing. + + void setTo(StringPiece s, int32_t val, CharString &strings, UErrorCode &errorCode); + + StringPiece getString(const CharString &strings) const { + int32_t offset=stringOffset; + int32_t length; + if(offset>=0) { + length=(uint8_t)strings[offset++]; + } else { + offset=~offset; + length=((int32_t)(uint8_t)strings[offset]<<8)|(uint8_t)strings[offset+1]; + offset+=2; + } + return StringPiece(strings.data()+offset, length); + } + int32_t getStringLength(const CharString &strings) const { + int32_t offset=stringOffset; + if(offset>=0) { + return (uint8_t)strings[offset]; + } else { + offset=~offset; + return ((int32_t)(uint8_t)strings[offset]<<8)|(uint8_t)strings[offset+1]; + } + } + + char charAt(int32_t index, const CharString &strings) const { return data(strings)[index]; } + + int32_t getValue() const { return value; } + + int32_t compareStringTo(const BytesTrieElement &o, const CharString &strings) const; + +private: + const char *data(const CharString &strings) const { + int32_t offset=stringOffset; + if(offset>=0) { + ++offset; + } else { + offset=~offset+2; + } + return strings.data()+offset; + } + + // If the stringOffset is non-negative, then the first strings byte contains + // the string length. + // If the stringOffset is negative, then the first two strings bytes contain + // the string length (big-endian), and the offset needs to be bit-inverted. + // (Compared with a stringLength field here, this saves 3 bytes per string for most strings.) + int32_t stringOffset; + int32_t value; +}; + +void +BytesTrieElement::setTo(StringPiece s, int32_t val, + CharString &strings, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return; + } + int32_t length=s.length(); + if(length>0xffff) { + // Too long: We store the length in 1 or 2 bytes. + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return; + } + int32_t offset=strings.length(); + if(length>0xff) { + offset=~offset; + strings.append((char)(length>>8), errorCode); + } + strings.append((char)length, errorCode); + stringOffset=offset; + value=val; + strings.append(s, errorCode); +} + +int32_t +BytesTrieElement::compareStringTo(const BytesTrieElement &other, const CharString &strings) const { + // TODO: add StringPiece::compare(), see ticket #8187 + StringPiece thisString=getString(strings); + StringPiece otherString=other.getString(strings); + int32_t lengthDiff=thisString.length()-otherString.length(); + int32_t commonLength; + if(lengthDiff<=0) { + commonLength=thisString.length(); + } else { + commonLength=otherString.length(); + } + int32_t diff=uprv_memcmp(thisString.data(), otherString.data(), commonLength); + return diff!=0 ? diff : lengthDiff; +} + +BytesTrieBuilder::BytesTrieBuilder(UErrorCode &errorCode) + : strings(nullptr), elements(nullptr), elementsCapacity(0), elementsLength(0), + bytes(nullptr), bytesCapacity(0), bytesLength(0) { + if(U_FAILURE(errorCode)) { + return; + } + strings=new CharString(); + if(strings==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } +} + +BytesTrieBuilder::~BytesTrieBuilder() { + delete strings; + delete[] elements; + uprv_free(bytes); +} + +BytesTrieBuilder & +BytesTrieBuilder::add(StringPiece s, int32_t value, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return *this; + } + if(bytesLength>0) { + // Cannot add elements after building. + errorCode=U_NO_WRITE_PERMISSION; + return *this; + } + if(elementsLength==elementsCapacity) { + int32_t newCapacity; + if(elementsCapacity==0) { + newCapacity=1024; + } else { + newCapacity=4*elementsCapacity; + } + BytesTrieElement *newElements=new BytesTrieElement[newCapacity]; + if(newElements==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return *this; // error instead of dereferencing null + } + if(elementsLength>0) { + uprv_memcpy(newElements, elements, (size_t)elementsLength*sizeof(BytesTrieElement)); + } + delete[] elements; + elements=newElements; + elementsCapacity=newCapacity; + } + elements[elementsLength++].setTo(s, value, *strings, errorCode); + return *this; +} + +U_CDECL_BEGIN + +static int32_t U_CALLCONV +compareElementStrings(const void *context, const void *left, const void *right) { + const CharString *strings=static_cast(context); + const BytesTrieElement *leftElement=static_cast(left); + const BytesTrieElement *rightElement=static_cast(right); + return leftElement->compareStringTo(*rightElement, *strings); +} + +U_CDECL_END + +BytesTrie * +BytesTrieBuilder::build(UStringTrieBuildOption buildOption, UErrorCode &errorCode) { + buildBytes(buildOption, errorCode); + BytesTrie *newTrie=nullptr; + if(U_SUCCESS(errorCode)) { + newTrie=new BytesTrie(bytes, bytes+(bytesCapacity-bytesLength)); + if(newTrie==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } else { + bytes=nullptr; // The new trie now owns the array. + bytesCapacity=0; + } + } + return newTrie; +} + +StringPiece +BytesTrieBuilder::buildStringPiece(UStringTrieBuildOption buildOption, UErrorCode &errorCode) { + buildBytes(buildOption, errorCode); + StringPiece result; + if(U_SUCCESS(errorCode)) { + result.set(bytes+(bytesCapacity-bytesLength), bytesLength); + } + return result; +} + +void +BytesTrieBuilder::buildBytes(UStringTrieBuildOption buildOption, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return; + } + if(bytes!=nullptr && bytesLength>0) { + // Already built. + return; + } + if(bytesLength==0) { + if(elementsLength==0) { + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return; + } + uprv_sortArray(elements, elementsLength, (int32_t)sizeof(BytesTrieElement), + compareElementStrings, strings, + false, // need not be a stable sort + &errorCode); + if(U_FAILURE(errorCode)) { + return; + } + // Duplicate strings are not allowed. + StringPiece prev=elements[0].getString(*strings); + for(int32_t i=1; ilength(); + if(capacity<1024) { + capacity=1024; + } + if(bytesCapacity(uprv_malloc(capacity)); + if(bytes==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + bytesCapacity=0; + return; + } + bytesCapacity=capacity; + } + StringTrieBuilder::build(buildOption, elementsLength, errorCode); + if(bytes==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } +} + +BytesTrieBuilder & +BytesTrieBuilder::clear() { + strings->clear(); + elementsLength=0; + bytesLength=0; + return *this; +} + +int32_t +BytesTrieBuilder::getElementStringLength(int32_t i) const { + return elements[i].getStringLength(*strings); +} + +char16_t +BytesTrieBuilder::getElementUnit(int32_t i, int32_t byteIndex) const { + return (uint8_t)elements[i].charAt(byteIndex, *strings); +} + +int32_t +BytesTrieBuilder::getElementValue(int32_t i) const { + return elements[i].getValue(); +} + +int32_t +BytesTrieBuilder::getLimitOfLinearMatch(int32_t first, int32_t last, int32_t byteIndex) const { + const BytesTrieElement &firstElement=elements[first]; + const BytesTrieElement &lastElement=elements[last]; + int32_t minStringLength=firstElement.getStringLength(*strings); + while(++byteIndex0); + return i; +} + +int32_t +BytesTrieBuilder::indexOfElementWithNextUnit(int32_t i, int32_t byteIndex, char16_t byte) const { + char b=(char)byte; + while(b==elements[i].charAt(byteIndex, *strings)) { + ++i; + } + return i; +} + +BytesTrieBuilder::BTLinearMatchNode::BTLinearMatchNode(const char *bytes, int32_t len, Node *nextNode) + : LinearMatchNode(len, nextNode), s(bytes) { + hash=static_cast( + static_cast(hash)*37u + static_cast(ustr_hashCharsN(bytes, len))); +} + +bool +BytesTrieBuilder::BTLinearMatchNode::operator==(const Node &other) const { + if(this==&other) { + return true; + } + if(!LinearMatchNode::operator==(other)) { + return false; + } + const BTLinearMatchNode &o=static_cast(other); + return 0==uprv_memcmp(s, o.s, length); +} + +void +BytesTrieBuilder::BTLinearMatchNode::write(StringTrieBuilder &builder) { + BytesTrieBuilder &b=static_cast(builder); + next->write(builder); + b.write(s, length); + offset=b.write(b.getMinLinearMatch()+length-1); +} + +StringTrieBuilder::Node * +BytesTrieBuilder::createLinearMatchNode(int32_t i, int32_t byteIndex, int32_t length, + Node *nextNode) const { + return new BTLinearMatchNode( + elements[i].getString(*strings).data()+byteIndex, + length, + nextNode); +} + +UBool +BytesTrieBuilder::ensureCapacity(int32_t length) { + if(bytes==nullptr) { + return false; // previous memory allocation had failed + } + if(length>bytesCapacity) { + int32_t newCapacity=bytesCapacity; + do { + newCapacity*=2; + } while(newCapacity<=length); + char *newBytes=static_cast(uprv_malloc(newCapacity)); + if(newBytes==nullptr) { + // unable to allocate memory + uprv_free(bytes); + bytes=nullptr; + bytesCapacity=0; + return false; + } + uprv_memcpy(newBytes+(newCapacity-bytesLength), + bytes+(bytesCapacity-bytesLength), bytesLength); + uprv_free(bytes); + bytes=newBytes; + bytesCapacity=newCapacity; + } + return true; +} + +int32_t +BytesTrieBuilder::write(int32_t byte) { + int32_t newLength=bytesLength+1; + if(ensureCapacity(newLength)) { + bytesLength=newLength; + bytes[bytesCapacity-bytesLength]=(char)byte; + } + return bytesLength; +} + +int32_t +BytesTrieBuilder::write(const char *b, int32_t length) { + int32_t newLength=bytesLength+length; + if(ensureCapacity(newLength)) { + bytesLength=newLength; + uprv_memcpy(bytes+(bytesCapacity-bytesLength), b, length); + } + return bytesLength; +} + +int32_t +BytesTrieBuilder::writeElementUnits(int32_t i, int32_t byteIndex, int32_t length) { + return write(elements[i].getString(*strings).data()+byteIndex, length); +} + +int32_t +BytesTrieBuilder::writeValueAndFinal(int32_t i, UBool isFinal) { + if(0<=i && i<=BytesTrie::kMaxOneByteValue) { + return write(((BytesTrie::kMinOneByteValueLead+i)<<1)|isFinal); + } + char intBytes[5]; + int32_t length=1; + if(i<0 || i>0xffffff) { + intBytes[0]=(char)BytesTrie::kFiveByteValueLead; + intBytes[1]=(char)((uint32_t)i>>24); + intBytes[2]=(char)((uint32_t)i>>16); + intBytes[3]=(char)((uint32_t)i>>8); + intBytes[4]=(char)i; + length=5; + // } else if(i<=BytesTrie::kMaxOneByteValue) { + // intBytes[0]=(char)(BytesTrie::kMinOneByteValueLead+i); + } else { + if(i<=BytesTrie::kMaxTwoByteValue) { + intBytes[0]=(char)(BytesTrie::kMinTwoByteValueLead+(i>>8)); + } else { + if(i<=BytesTrie::kMaxThreeByteValue) { + intBytes[0]=(char)(BytesTrie::kMinThreeByteValueLead+(i>>16)); + } else { + intBytes[0]=(char)BytesTrie::kFourByteValueLead; + intBytes[1]=(char)(i>>16); + length=2; + } + intBytes[length++]=(char)(i>>8); + } + intBytes[length++]=(char)i; + } + intBytes[0]=(char)((intBytes[0]<<1)|isFinal); + return write(intBytes, length); +} + +int32_t +BytesTrieBuilder::writeValueAndType(UBool hasValue, int32_t value, int32_t node) { + int32_t offset=write(node); + if(hasValue) { + offset=writeValueAndFinal(value, false); + } + return offset; +} + +int32_t +BytesTrieBuilder::writeDeltaTo(int32_t jumpTarget) { + int32_t i=bytesLength-jumpTarget; + U_ASSERT(i>=0); + if(i<=BytesTrie::kMaxOneByteDelta) { + return write(i); + } else { + char intBytes[5]; + return write(intBytes, internalEncodeDelta(i, intBytes)); + } +} + +int32_t +BytesTrieBuilder::internalEncodeDelta(int32_t i, char intBytes[]) { + U_ASSERT(i>=0); + if(i<=BytesTrie::kMaxOneByteDelta) { + intBytes[0]=(char)i; + return 1; + } + int32_t length=1; + if(i<=BytesTrie::kMaxTwoByteDelta) { + intBytes[0]=(char)(BytesTrie::kMinTwoByteDeltaLead+(i>>8)); + } else { + if(i<=BytesTrie::kMaxThreeByteDelta) { + intBytes[0]=(char)(BytesTrie::kMinThreeByteDeltaLead+(i>>16)); + } else { + if(i<=0xffffff) { + intBytes[0]=(char)BytesTrie::kFourByteDeltaLead; + } else { + intBytes[0]=(char)BytesTrie::kFiveByteDeltaLead; + intBytes[1]=(char)(i>>24); + length=2; + } + intBytes[length++]=(char)(i>>16); + } + intBytes[length++]=(char)(i>>8); + } + intBytes[length++]=(char)i; + return length; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/bytestrieiterator.cpp b/deps/icu-small/source/common/bytestrieiterator.cpp index eacb7eedb0d6bd..75eafadb9a80e6 100644 --- a/deps/icu-small/source/common/bytestrieiterator.cpp +++ b/deps/icu-small/source/common/bytestrieiterator.cpp @@ -1,214 +1,214 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2012, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: bytestrieiterator.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010nov03 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" -#include "unicode/bytestrie.h" -#include "unicode/stringpiece.h" -#include "charstr.h" -#include "uvectr32.h" - -U_NAMESPACE_BEGIN - -BytesTrie::Iterator::Iterator(const void *trieBytes, int32_t maxStringLength, - UErrorCode &errorCode) - : bytes_(static_cast(trieBytes)), - pos_(bytes_), initialPos_(bytes_), - remainingMatchLength_(-1), initialRemainingMatchLength_(-1), - str_(NULL), maxLength_(maxStringLength), value_(0), stack_(NULL) { - if(U_FAILURE(errorCode)) { - return; - } - // str_ and stack_ are pointers so that it's easy to turn bytestrie.h into - // a public API header for which we would want it to depend only on - // other public headers. - // Unlike BytesTrie itself, its Iterator performs memory allocations anyway - // via the CharString and UVector32 implementations, so this additional - // cost is minimal. - str_=new CharString(); - stack_=new UVector32(errorCode); - if(U_SUCCESS(errorCode) && (str_==NULL || stack_==NULL)) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } -} - -BytesTrie::Iterator::Iterator(const BytesTrie &trie, int32_t maxStringLength, - UErrorCode &errorCode) - : bytes_(trie.bytes_), pos_(trie.pos_), initialPos_(trie.pos_), - remainingMatchLength_(trie.remainingMatchLength_), - initialRemainingMatchLength_(trie.remainingMatchLength_), - str_(NULL), maxLength_(maxStringLength), value_(0), stack_(NULL) { - if(U_FAILURE(errorCode)) { - return; - } - str_=new CharString(); - stack_=new UVector32(errorCode); - if(U_FAILURE(errorCode)) { - return; - } - if(str_==NULL || stack_==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - int32_t length=remainingMatchLength_; // Actual remaining match length minus 1. - if(length>=0) { - // Pending linear-match node, append remaining bytes to str_. - ++length; - if(maxLength_>0 && length>maxLength_) { - length=maxLength_; // This will leave remainingMatchLength>=0 as a signal. - } - str_->append(reinterpret_cast(pos_), length, errorCode); - pos_+=length; - remainingMatchLength_-=length; - } -} - -BytesTrie::Iterator::~Iterator() { - delete str_; - delete stack_; -} - -BytesTrie::Iterator & -BytesTrie::Iterator::reset() { - pos_=initialPos_; - remainingMatchLength_=initialRemainingMatchLength_; - int32_t length=remainingMatchLength_+1; // Remaining match length. - if(maxLength_>0 && length>maxLength_) { - length=maxLength_; - } - str_->truncate(length); - pos_+=length; - remainingMatchLength_-=length; - stack_->setSize(0); - return *this; -} - -UBool -BytesTrie::Iterator::hasNext() const { return pos_!=NULL || !stack_->isEmpty(); } - -UBool -BytesTrie::Iterator::next(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return false; - } - const uint8_t *pos=pos_; - if(pos==NULL) { - if(stack_->isEmpty()) { - return false; - } - // Pop the state off the stack and continue with the next outbound edge of - // the branch node. - int32_t stackSize=stack_->size(); - int32_t length=stack_->elementAti(stackSize-1); - pos=bytes_+stack_->elementAti(stackSize-2); - stack_->setSize(stackSize-2); - str_->truncate(length&0xffff); - length=(int32_t)((uint32_t)length>>16); - if(length>1) { - pos=branchNext(pos, length, errorCode); - if(pos==NULL) { - return true; // Reached a final value. - } - } else { - str_->append((char)*pos++, errorCode); - } - } - if(remainingMatchLength_>=0) { - // We only get here if we started in a pending linear-match node - // with more than maxLength remaining bytes. - return truncateAndStop(); - } - for(;;) { - int32_t node=*pos++; - if(node>=kMinValueLead) { - // Deliver value for the byte sequence so far. - UBool isFinal=(UBool)(node&kValueIsFinal); - value_=readValue(pos, node>>1); - if(isFinal || (maxLength_>0 && str_->length()==maxLength_)) { - pos_=NULL; - } else { - pos_=skipValue(pos, node); - } - return true; - } - if(maxLength_>0 && str_->length()==maxLength_) { - return truncateAndStop(); - } - if(node0 && str_->length()+length>maxLength_) { - str_->append(reinterpret_cast(pos), - maxLength_-str_->length(), errorCode); - return truncateAndStop(); - } - str_->append(reinterpret_cast(pos), length, errorCode); - pos+=length; - } - } -} - -StringPiece -BytesTrie::Iterator::getString() const { - return str_ == NULL ? StringPiece() : str_->toStringPiece(); -} - -UBool -BytesTrie::Iterator::truncateAndStop() { - pos_=NULL; - value_=-1; // no real value for str - return true; -} - -// Branch node, needs to take the first outbound edge and push state for the rest. -const uint8_t * -BytesTrie::Iterator::branchNext(const uint8_t *pos, int32_t length, UErrorCode &errorCode) { - while(length>kMaxBranchLinearSubNodeLength) { - ++pos; // ignore the comparison byte - // Push state for the greater-or-equal edge. - stack_->addElement((int32_t)(skipDelta(pos)-bytes_), errorCode); - stack_->addElement(((length-(length>>1))<<16)|str_->length(), errorCode); - // Follow the less-than edge. - length>>=1; - pos=jumpByDelta(pos); - } - // List of key-value pairs where values are either final values or jump deltas. - // Read the first (key, value) pair. - uint8_t trieByte=*pos++; - int32_t node=*pos++; - UBool isFinal=(UBool)(node&kValueIsFinal); - int32_t value=readValue(pos, node>>1); - pos=skipValue(pos, node); - stack_->addElement((int32_t)(pos-bytes_), errorCode); - stack_->addElement(((length-1)<<16)|str_->length(), errorCode); - str_->append((char)trieByte, errorCode); - if(isFinal) { - pos_=NULL; - value_=value; - return NULL; - } else { - return pos+value; - } -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2012, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: bytestrieiterator.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010nov03 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" +#include "unicode/bytestrie.h" +#include "unicode/stringpiece.h" +#include "charstr.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +BytesTrie::Iterator::Iterator(const void *trieBytes, int32_t maxStringLength, + UErrorCode &errorCode) + : bytes_(static_cast(trieBytes)), + pos_(bytes_), initialPos_(bytes_), + remainingMatchLength_(-1), initialRemainingMatchLength_(-1), + str_(nullptr), maxLength_(maxStringLength), value_(0), stack_(nullptr) { + if(U_FAILURE(errorCode)) { + return; + } + // str_ and stack_ are pointers so that it's easy to turn bytestrie.h into + // a public API header for which we would want it to depend only on + // other public headers. + // Unlike BytesTrie itself, its Iterator performs memory allocations anyway + // via the CharString and UVector32 implementations, so this additional + // cost is minimal. + str_=new CharString(); + stack_=new UVector32(errorCode); + if(U_SUCCESS(errorCode) && (str_==nullptr || stack_==nullptr)) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } +} + +BytesTrie::Iterator::Iterator(const BytesTrie &trie, int32_t maxStringLength, + UErrorCode &errorCode) + : bytes_(trie.bytes_), pos_(trie.pos_), initialPos_(trie.pos_), + remainingMatchLength_(trie.remainingMatchLength_), + initialRemainingMatchLength_(trie.remainingMatchLength_), + str_(nullptr), maxLength_(maxStringLength), value_(0), stack_(nullptr) { + if(U_FAILURE(errorCode)) { + return; + } + str_=new CharString(); + stack_=new UVector32(errorCode); + if(U_FAILURE(errorCode)) { + return; + } + if(str_==nullptr || stack_==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + int32_t length=remainingMatchLength_; // Actual remaining match length minus 1. + if(length>=0) { + // Pending linear-match node, append remaining bytes to str_. + ++length; + if(maxLength_>0 && length>maxLength_) { + length=maxLength_; // This will leave remainingMatchLength>=0 as a signal. + } + str_->append(reinterpret_cast(pos_), length, errorCode); + pos_+=length; + remainingMatchLength_-=length; + } +} + +BytesTrie::Iterator::~Iterator() { + delete str_; + delete stack_; +} + +BytesTrie::Iterator & +BytesTrie::Iterator::reset() { + pos_=initialPos_; + remainingMatchLength_=initialRemainingMatchLength_; + int32_t length=remainingMatchLength_+1; // Remaining match length. + if(maxLength_>0 && length>maxLength_) { + length=maxLength_; + } + str_->truncate(length); + pos_+=length; + remainingMatchLength_-=length; + stack_->setSize(0); + return *this; +} + +UBool +BytesTrie::Iterator::hasNext() const { return pos_!=nullptr || !stack_->isEmpty(); } + +UBool +BytesTrie::Iterator::next(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return false; + } + const uint8_t *pos=pos_; + if(pos==nullptr) { + if(stack_->isEmpty()) { + return false; + } + // Pop the state off the stack and continue with the next outbound edge of + // the branch node. + int32_t stackSize=stack_->size(); + int32_t length=stack_->elementAti(stackSize-1); + pos=bytes_+stack_->elementAti(stackSize-2); + stack_->setSize(stackSize-2); + str_->truncate(length&0xffff); + length=(int32_t)((uint32_t)length>>16); + if(length>1) { + pos=branchNext(pos, length, errorCode); + if(pos==nullptr) { + return true; // Reached a final value. + } + } else { + str_->append((char)*pos++, errorCode); + } + } + if(remainingMatchLength_>=0) { + // We only get here if we started in a pending linear-match node + // with more than maxLength remaining bytes. + return truncateAndStop(); + } + for(;;) { + int32_t node=*pos++; + if(node>=kMinValueLead) { + // Deliver value for the byte sequence so far. + UBool isFinal=(UBool)(node&kValueIsFinal); + value_=readValue(pos, node>>1); + if(isFinal || (maxLength_>0 && str_->length()==maxLength_)) { + pos_=nullptr; + } else { + pos_=skipValue(pos, node); + } + return true; + } + if(maxLength_>0 && str_->length()==maxLength_) { + return truncateAndStop(); + } + if(node0 && str_->length()+length>maxLength_) { + str_->append(reinterpret_cast(pos), + maxLength_-str_->length(), errorCode); + return truncateAndStop(); + } + str_->append(reinterpret_cast(pos), length, errorCode); + pos+=length; + } + } +} + +StringPiece +BytesTrie::Iterator::getString() const { + return str_ == nullptr ? StringPiece() : str_->toStringPiece(); +} + +UBool +BytesTrie::Iterator::truncateAndStop() { + pos_=nullptr; + value_=-1; // no real value for str + return true; +} + +// Branch node, needs to take the first outbound edge and push state for the rest. +const uint8_t * +BytesTrie::Iterator::branchNext(const uint8_t *pos, int32_t length, UErrorCode &errorCode) { + while(length>kMaxBranchLinearSubNodeLength) { + ++pos; // ignore the comparison byte + // Push state for the greater-or-equal edge. + stack_->addElement((int32_t)(skipDelta(pos)-bytes_), errorCode); + stack_->addElement(((length-(length>>1))<<16)|str_->length(), errorCode); + // Follow the less-than edge. + length>>=1; + pos=jumpByDelta(pos); + } + // List of key-value pairs where values are either final values or jump deltas. + // Read the first (key, value) pair. + uint8_t trieByte=*pos++; + int32_t node=*pos++; + UBool isFinal=(UBool)(node&kValueIsFinal); + int32_t value=readValue(pos, node>>1); + pos=skipValue(pos, node); + stack_->addElement((int32_t)(pos-bytes_), errorCode); + stack_->addElement(((length-1)<<16)|str_->length(), errorCode); + str_->append((char)trieByte, errorCode); + if(isFinal) { + pos_=nullptr; + value_=value; + return nullptr; + } else { + return pos+value; + } +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/caniter.cpp b/deps/icu-small/source/common/caniter.cpp index 81f17265fbbbb7..d9b3aa0e0bd4ac 100644 --- a/deps/icu-small/source/common/caniter.cpp +++ b/deps/icu-small/source/common/caniter.cpp @@ -1,586 +1,586 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ***************************************************************************** - * Copyright (C) 1996-2015, International Business Machines Corporation and - * others. All Rights Reserved. - ***************************************************************************** - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_NORMALIZATION - -#include "unicode/caniter.h" -#include "unicode/normalizer2.h" -#include "unicode/uchar.h" -#include "unicode/uniset.h" -#include "unicode/usetiter.h" -#include "unicode/ustring.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "hash.h" -#include "normalizer2impl.h" - -/** - * This class allows one to iterate through all the strings that are canonically equivalent to a given - * string. For example, here are some sample results: -Results for: {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} -1: \u0041\u030A\u0064\u0307\u0327 - = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} -2: \u0041\u030A\u0064\u0327\u0307 - = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D}{COMBINING CEDILLA}{COMBINING DOT ABOVE} -3: \u0041\u030A\u1E0B\u0327 - = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D WITH DOT ABOVE}{COMBINING CEDILLA} -4: \u0041\u030A\u1E11\u0307 - = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D WITH CEDILLA}{COMBINING DOT ABOVE} -5: \u00C5\u0064\u0307\u0327 - = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} -6: \u00C5\u0064\u0327\u0307 - = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D}{COMBINING CEDILLA}{COMBINING DOT ABOVE} -7: \u00C5\u1E0B\u0327 - = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D WITH DOT ABOVE}{COMBINING CEDILLA} -8: \u00C5\u1E11\u0307 - = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D WITH CEDILLA}{COMBINING DOT ABOVE} -9: \u212B\u0064\u0307\u0327 - = {ANGSTROM SIGN}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} -10: \u212B\u0064\u0327\u0307 - = {ANGSTROM SIGN}{LATIN SMALL LETTER D}{COMBINING CEDILLA}{COMBINING DOT ABOVE} -11: \u212B\u1E0B\u0327 - = {ANGSTROM SIGN}{LATIN SMALL LETTER D WITH DOT ABOVE}{COMBINING CEDILLA} -12: \u212B\u1E11\u0307 - = {ANGSTROM SIGN}{LATIN SMALL LETTER D WITH CEDILLA}{COMBINING DOT ABOVE} - *
Note: the code is intended for use with small strings, and is not suitable for larger ones, - * since it has not been optimized for that situation. - *@author M. Davis - *@draft - */ - -// public - -U_NAMESPACE_BEGIN - -// TODO: add boilerplate methods. - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CanonicalIterator) - -/** - *@param source string to get results for - */ -CanonicalIterator::CanonicalIterator(const UnicodeString &sourceStr, UErrorCode &status) : - pieces(NULL), - pieces_length(0), - pieces_lengths(NULL), - current(NULL), - current_length(0), - nfd(*Normalizer2::getNFDInstance(status)), - nfcImpl(*Normalizer2Factory::getNFCImpl(status)) -{ - if(U_SUCCESS(status) && nfcImpl.ensureCanonIterData(status)) { - setSource(sourceStr, status); - } -} - -CanonicalIterator::~CanonicalIterator() { - cleanPieces(); -} - -void CanonicalIterator::cleanPieces() { - int32_t i = 0; - if(pieces != NULL) { - for(i = 0; i < pieces_length; i++) { - if(pieces[i] != NULL) { - delete[] pieces[i]; - } - } - uprv_free(pieces); - pieces = NULL; - pieces_length = 0; - } - if(pieces_lengths != NULL) { - uprv_free(pieces_lengths); - pieces_lengths = NULL; - } - if(current != NULL) { - uprv_free(current); - current = NULL; - current_length = 0; - } -} - -/** - *@return gets the source: NOTE: it is the NFD form of source - */ -UnicodeString CanonicalIterator::getSource() { - return source; -} - -/** - * Resets the iterator so that one can start again from the beginning. - */ -void CanonicalIterator::reset() { - done = false; - for (int i = 0; i < current_length; ++i) { - current[i] = 0; - } -} - -/** - *@return the next string that is canonically equivalent. The value null is returned when - * the iteration is done. - */ -UnicodeString CanonicalIterator::next() { - int32_t i = 0; - - if (done) { - buffer.setToBogus(); - return buffer; - } - - // delete old contents - buffer.remove(); - - // construct return value - - for (i = 0; i < pieces_length; ++i) { - buffer.append(pieces[i][current[i]]); - } - //String result = buffer.toString(); // not needed - - // find next value for next time - - for (i = current_length - 1; ; --i) { - if (i < 0) { - done = true; - break; - } - current[i]++; - if (current[i] < pieces_lengths[i]) break; // got sequence - current[i] = 0; - } - return buffer; -} - -/** - *@param set the source string to iterate against. This allows the same iterator to be used - * while changing the source string, saving object creation. - */ -void CanonicalIterator::setSource(const UnicodeString &newSource, UErrorCode &status) { - int32_t list_length = 0; - UChar32 cp = 0; - int32_t start = 0; - int32_t i = 0; - UnicodeString *list = NULL; - - nfd.normalize(newSource, source, status); - if(U_FAILURE(status)) { - return; - } - done = false; - - cleanPieces(); - - // catch degenerate case - if (newSource.length() == 0) { - pieces = (UnicodeString **)uprv_malloc(sizeof(UnicodeString *)); - pieces_lengths = (int32_t*)uprv_malloc(1 * sizeof(int32_t)); - pieces_length = 1; - current = (int32_t*)uprv_malloc(1 * sizeof(int32_t)); - current_length = 1; - if (pieces == NULL || pieces_lengths == NULL || current == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - goto CleanPartialInitialization; - } - current[0] = 0; - pieces[0] = new UnicodeString[1]; - pieces_lengths[0] = 1; - if (pieces[0] == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - goto CleanPartialInitialization; - } - return; - } - - - list = new UnicodeString[source.length()]; - if (list == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - goto CleanPartialInitialization; - } - - // i should initially be the number of code units at the - // start of the string - i = U16_LENGTH(source.char32At(0)); - // int32_t i = 1; - // find the segments - // This code iterates through the source string and - // extracts segments that end up on a codepoint that - // doesn't start any decompositions. (Analysis is done - // on the NFD form - see above). - for (; i < source.length(); i += U16_LENGTH(cp)) { - cp = source.char32At(i); - if (nfcImpl.isCanonSegmentStarter(cp)) { - source.extract(start, i-start, list[list_length++]); // add up to i - start = i; - } - } - source.extract(start, i-start, list[list_length++]); // add last one - - - // allocate the arrays, and find the strings that are CE to each segment - pieces = (UnicodeString **)uprv_malloc(list_length * sizeof(UnicodeString *)); - pieces_length = list_length; - pieces_lengths = (int32_t*)uprv_malloc(list_length * sizeof(int32_t)); - current = (int32_t*)uprv_malloc(list_length * sizeof(int32_t)); - current_length = list_length; - if (pieces == NULL || pieces_lengths == NULL || current == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - goto CleanPartialInitialization; - } - - for (i = 0; i < current_length; i++) { - current[i] = 0; - } - // for each segment, get all the combinations that can produce - // it after NFD normalization - for (i = 0; i < pieces_length; ++i) { - //if (PROGRESS) printf("SEGMENT\n"); - pieces[i] = getEquivalents(list[i], pieces_lengths[i], status); - } - - delete[] list; - return; -// Common section to cleanup all local variables and reset object variables. -CleanPartialInitialization: - if (list != NULL) { - delete[] list; - } - cleanPieces(); -} - -/** - * Dumb recursive implementation of permutation. - * TODO: optimize - * @param source the string to find permutations for - * @return the results in a set. - */ -void U_EXPORT2 CanonicalIterator::permute(UnicodeString &source, UBool skipZeros, Hashtable *result, UErrorCode &status) { - if(U_FAILURE(status)) { - return; - } - //if (PROGRESS) printf("Permute: %s\n", UToS(Tr(source))); - int32_t i = 0; - - // optimization: - // if zero or one character, just return a set with it - // we check for length < 2 to keep from counting code points all the time - if (source.length() <= 2 && source.countChar32() <= 1) { - UnicodeString *toPut = new UnicodeString(source); - /* test for NULL */ - if (toPut == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - result->put(source, toPut, status); - return; - } - - // otherwise iterate through the string, and recursively permute all the other characters - UChar32 cp; - Hashtable subpermute(status); - if(U_FAILURE(status)) { - return; - } - subpermute.setValueDeleter(uprv_deleteUObject); - - for (i = 0; i < source.length(); i += U16_LENGTH(cp)) { - cp = source.char32At(i); - const UHashElement *ne = NULL; - int32_t el = UHASH_FIRST; - UnicodeString subPermuteString = source; - - // optimization: - // if the character is canonical combining class zero, - // don't permute it - if (skipZeros && i != 0 && u_getCombiningClass(cp) == 0) { - //System.out.println("Skipping " + Utility.hex(UTF16.valueOf(source, i))); - continue; - } - - subpermute.removeAll(); - - // see what the permutations of the characters before and after this one are - //Hashtable *subpermute = permute(source.substring(0,i) + source.substring(i + UTF16.getCharCount(cp))); - permute(subPermuteString.remove(i, U16_LENGTH(cp)), skipZeros, &subpermute, status); - /* Test for buffer overflows */ - if(U_FAILURE(status)) { - return; - } - // The upper remove is destructive. The question is do we have to make a copy, or we don't care about the contents - // of source at this point. - - // prefix this character to all of them - ne = subpermute.nextElement(el); - while (ne != NULL) { - UnicodeString *permRes = (UnicodeString *)(ne->value.pointer); - UnicodeString *chStr = new UnicodeString(cp); - //test for NULL - if (chStr == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - chStr->append(*permRes); //*((UnicodeString *)(ne->value.pointer)); - //if (PROGRESS) printf(" Piece: %s\n", UToS(*chStr)); - result->put(*chStr, chStr, status); - ne = subpermute.nextElement(el); - } - } - //return result; -} - -// privates - -// we have a segment, in NFD. Find all the strings that are canonically equivalent to it. -UnicodeString* CanonicalIterator::getEquivalents(const UnicodeString &segment, int32_t &result_len, UErrorCode &status) { - Hashtable result(status); - Hashtable permutations(status); - Hashtable basic(status); - if (U_FAILURE(status)) { - return 0; - } - result.setValueDeleter(uprv_deleteUObject); - permutations.setValueDeleter(uprv_deleteUObject); - basic.setValueDeleter(uprv_deleteUObject); - - UChar USeg[256]; - int32_t segLen = segment.extract(USeg, 256, status); - getEquivalents2(&basic, USeg, segLen, status); - - // now get all the permutations - // add only the ones that are canonically equivalent - // TODO: optimize by not permuting any class zero. - - const UHashElement *ne = NULL; - int32_t el = UHASH_FIRST; - //Iterator it = basic.iterator(); - ne = basic.nextElement(el); - //while (it.hasNext()) - while (ne != NULL) { - //String item = (String) it.next(); - UnicodeString item = *((UnicodeString *)(ne->value.pointer)); - - permutations.removeAll(); - permute(item, CANITER_SKIP_ZEROES, &permutations, status); - const UHashElement *ne2 = NULL; - int32_t el2 = UHASH_FIRST; - //Iterator it2 = permutations.iterator(); - ne2 = permutations.nextElement(el2); - //while (it2.hasNext()) - while (ne2 != NULL) { - //String possible = (String) it2.next(); - //UnicodeString *possible = new UnicodeString(*((UnicodeString *)(ne2->value.pointer))); - UnicodeString possible(*((UnicodeString *)(ne2->value.pointer))); - UnicodeString attempt; - nfd.normalize(possible, attempt, status); - - // TODO: check if operator == is semanticaly the same as attempt.equals(segment) - if (attempt==segment) { - //if (PROGRESS) printf("Adding Permutation: %s\n", UToS(Tr(*possible))); - // TODO: use the hashtable just to catch duplicates - store strings directly (somehow). - result.put(possible, new UnicodeString(possible), status); //add(possible); - } else { - //if (PROGRESS) printf("-Skipping Permutation: %s\n", UToS(Tr(*possible))); - } - - ne2 = permutations.nextElement(el2); - } - ne = basic.nextElement(el); - } - - /* Test for buffer overflows */ - if(U_FAILURE(status)) { - return 0; - } - // convert into a String[] to clean up storage - //String[] finalResult = new String[result.size()]; - UnicodeString *finalResult = NULL; - int32_t resultCount; - if((resultCount = result.count()) != 0) { - finalResult = new UnicodeString[resultCount]; - if (finalResult == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - } - else { - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - //result.toArray(finalResult); - result_len = 0; - el = UHASH_FIRST; - ne = result.nextElement(el); - while(ne != NULL) { - finalResult[result_len++] = *((UnicodeString *)(ne->value.pointer)); - ne = result.nextElement(el); - } - - - return finalResult; -} - -Hashtable *CanonicalIterator::getEquivalents2(Hashtable *fillinResult, const UChar *segment, int32_t segLen, UErrorCode &status) { - - if (U_FAILURE(status)) { - return NULL; - } - - //if (PROGRESS) printf("Adding: %s\n", UToS(Tr(segment))); - - UnicodeString toPut(segment, segLen); - - fillinResult->put(toPut, new UnicodeString(toPut), status); - - UnicodeSet starts; - - // cycle through all the characters - UChar32 cp; - for (int32_t i = 0; i < segLen; i += U16_LENGTH(cp)) { - // see if any character is at the start of some decomposition - U16_GET(segment, 0, i, segLen, cp); - if (!nfcImpl.getCanonStartSet(cp, starts)) { - continue; - } - // if so, see which decompositions match - UnicodeSetIterator iter(starts); - while (iter.next()) { - UChar32 cp2 = iter.getCodepoint(); - Hashtable remainder(status); - remainder.setValueDeleter(uprv_deleteUObject); - if (extract(&remainder, cp2, segment, segLen, i, status) == NULL) { - continue; - } - - // there were some matches, so add all the possibilities to the set. - UnicodeString prefix(segment, i); - prefix += cp2; - - int32_t el = UHASH_FIRST; - const UHashElement *ne = remainder.nextElement(el); - while (ne != NULL) { - UnicodeString item = *((UnicodeString *)(ne->value.pointer)); - UnicodeString *toAdd = new UnicodeString(prefix); - /* test for NULL */ - if (toAdd == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - *toAdd += item; - fillinResult->put(*toAdd, toAdd, status); - - //if (PROGRESS) printf("Adding: %s\n", UToS(Tr(*toAdd))); - - ne = remainder.nextElement(el); - } - } - } - - /* Test for buffer overflows */ - if(U_FAILURE(status)) { - return NULL; - } - return fillinResult; -} - -/** - * See if the decomposition of cp2 is at segment starting at segmentPos - * (with canonical rearrangement!) - * If so, take the remainder, and return the equivalents - */ -Hashtable *CanonicalIterator::extract(Hashtable *fillinResult, UChar32 comp, const UChar *segment, int32_t segLen, int32_t segmentPos, UErrorCode &status) { -//Hashtable *CanonicalIterator::extract(UChar32 comp, const UnicodeString &segment, int32_t segLen, int32_t segmentPos, UErrorCode &status) { - //if (PROGRESS) printf(" extract: %s, ", UToS(Tr(UnicodeString(comp)))); - //if (PROGRESS) printf("%s, %i\n", UToS(Tr(segment)), segmentPos); - - if (U_FAILURE(status)) { - return NULL; - } - - UnicodeString temp(comp); - int32_t inputLen=temp.length(); - UnicodeString decompString; - nfd.normalize(temp, decompString, status); - if (U_FAILURE(status)) { - return NULL; - } - if (decompString.isBogus()) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - const UChar *decomp=decompString.getBuffer(); - int32_t decompLen=decompString.length(); - - // See if it matches the start of segment (at segmentPos) - UBool ok = false; - UChar32 cp; - int32_t decompPos = 0; - UChar32 decompCp; - U16_NEXT(decomp, decompPos, decompLen, decompCp); - - int32_t i = segmentPos; - while(i < segLen) { - U16_NEXT(segment, i, segLen, cp); - - if (cp == decompCp) { // if equal, eat another cp from decomp - - //if (PROGRESS) printf(" matches: %s\n", UToS(Tr(UnicodeString(cp)))); - - if (decompPos == decompLen) { // done, have all decomp characters! - temp.append(segment+i, segLen-i); - ok = true; - break; - } - U16_NEXT(decomp, decompPos, decompLen, decompCp); - } else { - //if (PROGRESS) printf(" buffer: %s\n", UToS(Tr(UnicodeString(cp)))); - - // brute force approach - temp.append(cp); - - /* TODO: optimize - // since we know that the classes are monotonically increasing, after zero - // e.g. 0 5 7 9 0 3 - // we can do an optimization - // there are only a few cases that work: zero, less, same, greater - // if both classes are the same, we fail - // if the decomp class < the segment class, we fail - - segClass = getClass(cp); - if (decompClass <= segClass) return null; - */ - } - } - if (!ok) - return NULL; // we failed, characters left over - - //if (PROGRESS) printf("Matches\n"); - - if (inputLen == temp.length()) { - fillinResult->put(UnicodeString(), new UnicodeString(), status); - return fillinResult; // succeed, but no remainder - } - - // brute force approach - // check to make sure result is canonically equivalent - UnicodeString trial; - nfd.normalize(temp, trial, status); - if(U_FAILURE(status) || trial.compare(segment+segmentPos, segLen - segmentPos) != 0) { - return NULL; - } - - return getEquivalents2(fillinResult, temp.getBuffer()+inputLen, temp.length()-inputLen, status); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_NORMALIZATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ***************************************************************************** + * Copyright (C) 1996-2015, International Business Machines Corporation and + * others. All Rights Reserved. + ***************************************************************************** + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_NORMALIZATION + +#include "unicode/caniter.h" +#include "unicode/normalizer2.h" +#include "unicode/uchar.h" +#include "unicode/uniset.h" +#include "unicode/usetiter.h" +#include "unicode/ustring.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "hash.h" +#include "normalizer2impl.h" + +/** + * This class allows one to iterate through all the strings that are canonically equivalent to a given + * string. For example, here are some sample results: +Results for: {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} +1: \u0041\u030A\u0064\u0307\u0327 + = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} +2: \u0041\u030A\u0064\u0327\u0307 + = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D}{COMBINING CEDILLA}{COMBINING DOT ABOVE} +3: \u0041\u030A\u1E0B\u0327 + = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D WITH DOT ABOVE}{COMBINING CEDILLA} +4: \u0041\u030A\u1E11\u0307 + = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D WITH CEDILLA}{COMBINING DOT ABOVE} +5: \u00C5\u0064\u0307\u0327 + = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} +6: \u00C5\u0064\u0327\u0307 + = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D}{COMBINING CEDILLA}{COMBINING DOT ABOVE} +7: \u00C5\u1E0B\u0327 + = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D WITH DOT ABOVE}{COMBINING CEDILLA} +8: \u00C5\u1E11\u0307 + = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D WITH CEDILLA}{COMBINING DOT ABOVE} +9: \u212B\u0064\u0307\u0327 + = {ANGSTROM SIGN}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} +10: \u212B\u0064\u0327\u0307 + = {ANGSTROM SIGN}{LATIN SMALL LETTER D}{COMBINING CEDILLA}{COMBINING DOT ABOVE} +11: \u212B\u1E0B\u0327 + = {ANGSTROM SIGN}{LATIN SMALL LETTER D WITH DOT ABOVE}{COMBINING CEDILLA} +12: \u212B\u1E11\u0307 + = {ANGSTROM SIGN}{LATIN SMALL LETTER D WITH CEDILLA}{COMBINING DOT ABOVE} + *
Note: the code is intended for use with small strings, and is not suitable for larger ones, + * since it has not been optimized for that situation. + *@author M. Davis + *@draft + */ + +// public + +U_NAMESPACE_BEGIN + +// TODO: add boilerplate methods. + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CanonicalIterator) + +/** + *@param source string to get results for + */ +CanonicalIterator::CanonicalIterator(const UnicodeString &sourceStr, UErrorCode &status) : + pieces(nullptr), + pieces_length(0), + pieces_lengths(nullptr), + current(nullptr), + current_length(0), + nfd(*Normalizer2::getNFDInstance(status)), + nfcImpl(*Normalizer2Factory::getNFCImpl(status)) +{ + if(U_SUCCESS(status) && nfcImpl.ensureCanonIterData(status)) { + setSource(sourceStr, status); + } +} + +CanonicalIterator::~CanonicalIterator() { + cleanPieces(); +} + +void CanonicalIterator::cleanPieces() { + int32_t i = 0; + if(pieces != nullptr) { + for(i = 0; i < pieces_length; i++) { + if(pieces[i] != nullptr) { + delete[] pieces[i]; + } + } + uprv_free(pieces); + pieces = nullptr; + pieces_length = 0; + } + if(pieces_lengths != nullptr) { + uprv_free(pieces_lengths); + pieces_lengths = nullptr; + } + if(current != nullptr) { + uprv_free(current); + current = nullptr; + current_length = 0; + } +} + +/** + *@return gets the source: NOTE: it is the NFD form of source + */ +UnicodeString CanonicalIterator::getSource() { + return source; +} + +/** + * Resets the iterator so that one can start again from the beginning. + */ +void CanonicalIterator::reset() { + done = false; + for (int i = 0; i < current_length; ++i) { + current[i] = 0; + } +} + +/** + *@return the next string that is canonically equivalent. The value null is returned when + * the iteration is done. + */ +UnicodeString CanonicalIterator::next() { + int32_t i = 0; + + if (done) { + buffer.setToBogus(); + return buffer; + } + + // delete old contents + buffer.remove(); + + // construct return value + + for (i = 0; i < pieces_length; ++i) { + buffer.append(pieces[i][current[i]]); + } + //String result = buffer.toString(); // not needed + + // find next value for next time + + for (i = current_length - 1; ; --i) { + if (i < 0) { + done = true; + break; + } + current[i]++; + if (current[i] < pieces_lengths[i]) break; // got sequence + current[i] = 0; + } + return buffer; +} + +/** + *@param set the source string to iterate against. This allows the same iterator to be used + * while changing the source string, saving object creation. + */ +void CanonicalIterator::setSource(const UnicodeString &newSource, UErrorCode &status) { + int32_t list_length = 0; + UChar32 cp = 0; + int32_t start = 0; + int32_t i = 0; + UnicodeString *list = nullptr; + + nfd.normalize(newSource, source, status); + if(U_FAILURE(status)) { + return; + } + done = false; + + cleanPieces(); + + // catch degenerate case + if (newSource.length() == 0) { + pieces = (UnicodeString **)uprv_malloc(sizeof(UnicodeString *)); + pieces_lengths = (int32_t*)uprv_malloc(1 * sizeof(int32_t)); + pieces_length = 1; + current = (int32_t*)uprv_malloc(1 * sizeof(int32_t)); + current_length = 1; + if (pieces == nullptr || pieces_lengths == nullptr || current == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + goto CleanPartialInitialization; + } + current[0] = 0; + pieces[0] = new UnicodeString[1]; + pieces_lengths[0] = 1; + if (pieces[0] == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + goto CleanPartialInitialization; + } + return; + } + + + list = new UnicodeString[source.length()]; + if (list == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + goto CleanPartialInitialization; + } + + // i should initially be the number of code units at the + // start of the string + i = U16_LENGTH(source.char32At(0)); + // int32_t i = 1; + // find the segments + // This code iterates through the source string and + // extracts segments that end up on a codepoint that + // doesn't start any decompositions. (Analysis is done + // on the NFD form - see above). + for (; i < source.length(); i += U16_LENGTH(cp)) { + cp = source.char32At(i); + if (nfcImpl.isCanonSegmentStarter(cp)) { + source.extract(start, i-start, list[list_length++]); // add up to i + start = i; + } + } + source.extract(start, i-start, list[list_length++]); // add last one + + + // allocate the arrays, and find the strings that are CE to each segment + pieces = (UnicodeString **)uprv_malloc(list_length * sizeof(UnicodeString *)); + pieces_length = list_length; + pieces_lengths = (int32_t*)uprv_malloc(list_length * sizeof(int32_t)); + current = (int32_t*)uprv_malloc(list_length * sizeof(int32_t)); + current_length = list_length; + if (pieces == nullptr || pieces_lengths == nullptr || current == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + goto CleanPartialInitialization; + } + + for (i = 0; i < current_length; i++) { + current[i] = 0; + } + // for each segment, get all the combinations that can produce + // it after NFD normalization + for (i = 0; i < pieces_length; ++i) { + //if (PROGRESS) printf("SEGMENT\n"); + pieces[i] = getEquivalents(list[i], pieces_lengths[i], status); + } + + delete[] list; + return; +// Common section to cleanup all local variables and reset object variables. +CleanPartialInitialization: + if (list != nullptr) { + delete[] list; + } + cleanPieces(); +} + +/** + * Dumb recursive implementation of permutation. + * TODO: optimize + * @param source the string to find permutations for + * @return the results in a set. + */ +void U_EXPORT2 CanonicalIterator::permute(UnicodeString &source, UBool skipZeros, Hashtable *result, UErrorCode &status) { + if(U_FAILURE(status)) { + return; + } + //if (PROGRESS) printf("Permute: %s\n", UToS(Tr(source))); + int32_t i = 0; + + // optimization: + // if zero or one character, just return a set with it + // we check for length < 2 to keep from counting code points all the time + if (source.length() <= 2 && source.countChar32() <= 1) { + UnicodeString *toPut = new UnicodeString(source); + /* test for nullptr */ + if (toPut == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + result->put(source, toPut, status); + return; + } + + // otherwise iterate through the string, and recursively permute all the other characters + UChar32 cp; + Hashtable subpermute(status); + if(U_FAILURE(status)) { + return; + } + subpermute.setValueDeleter(uprv_deleteUObject); + + for (i = 0; i < source.length(); i += U16_LENGTH(cp)) { + cp = source.char32At(i); + const UHashElement *ne = nullptr; + int32_t el = UHASH_FIRST; + UnicodeString subPermuteString = source; + + // optimization: + // if the character is canonical combining class zero, + // don't permute it + if (skipZeros && i != 0 && u_getCombiningClass(cp) == 0) { + //System.out.println("Skipping " + Utility.hex(UTF16.valueOf(source, i))); + continue; + } + + subpermute.removeAll(); + + // see what the permutations of the characters before and after this one are + //Hashtable *subpermute = permute(source.substring(0,i) + source.substring(i + UTF16.getCharCount(cp))); + permute(subPermuteString.remove(i, U16_LENGTH(cp)), skipZeros, &subpermute, status); + /* Test for buffer overflows */ + if(U_FAILURE(status)) { + return; + } + // The upper remove is destructive. The question is do we have to make a copy, or we don't care about the contents + // of source at this point. + + // prefix this character to all of them + ne = subpermute.nextElement(el); + while (ne != nullptr) { + UnicodeString *permRes = (UnicodeString *)(ne->value.pointer); + UnicodeString *chStr = new UnicodeString(cp); + //test for nullptr + if (chStr == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + chStr->append(*permRes); //*((UnicodeString *)(ne->value.pointer)); + //if (PROGRESS) printf(" Piece: %s\n", UToS(*chStr)); + result->put(*chStr, chStr, status); + ne = subpermute.nextElement(el); + } + } + //return result; +} + +// privates + +// we have a segment, in NFD. Find all the strings that are canonically equivalent to it. +UnicodeString* CanonicalIterator::getEquivalents(const UnicodeString &segment, int32_t &result_len, UErrorCode &status) { + Hashtable result(status); + Hashtable permutations(status); + Hashtable basic(status); + if (U_FAILURE(status)) { + return 0; + } + result.setValueDeleter(uprv_deleteUObject); + permutations.setValueDeleter(uprv_deleteUObject); + basic.setValueDeleter(uprv_deleteUObject); + + char16_t USeg[256]; + int32_t segLen = segment.extract(USeg, 256, status); + getEquivalents2(&basic, USeg, segLen, status); + + // now get all the permutations + // add only the ones that are canonically equivalent + // TODO: optimize by not permuting any class zero. + + const UHashElement *ne = nullptr; + int32_t el = UHASH_FIRST; + //Iterator it = basic.iterator(); + ne = basic.nextElement(el); + //while (it.hasNext()) + while (ne != nullptr) { + //String item = (String) it.next(); + UnicodeString item = *((UnicodeString *)(ne->value.pointer)); + + permutations.removeAll(); + permute(item, CANITER_SKIP_ZEROES, &permutations, status); + const UHashElement *ne2 = nullptr; + int32_t el2 = UHASH_FIRST; + //Iterator it2 = permutations.iterator(); + ne2 = permutations.nextElement(el2); + //while (it2.hasNext()) + while (ne2 != nullptr) { + //String possible = (String) it2.next(); + //UnicodeString *possible = new UnicodeString(*((UnicodeString *)(ne2->value.pointer))); + UnicodeString possible(*((UnicodeString *)(ne2->value.pointer))); + UnicodeString attempt; + nfd.normalize(possible, attempt, status); + + // TODO: check if operator == is semanticaly the same as attempt.equals(segment) + if (attempt==segment) { + //if (PROGRESS) printf("Adding Permutation: %s\n", UToS(Tr(*possible))); + // TODO: use the hashtable just to catch duplicates - store strings directly (somehow). + result.put(possible, new UnicodeString(possible), status); //add(possible); + } else { + //if (PROGRESS) printf("-Skipping Permutation: %s\n", UToS(Tr(*possible))); + } + + ne2 = permutations.nextElement(el2); + } + ne = basic.nextElement(el); + } + + /* Test for buffer overflows */ + if(U_FAILURE(status)) { + return 0; + } + // convert into a String[] to clean up storage + //String[] finalResult = new String[result.size()]; + UnicodeString *finalResult = nullptr; + int32_t resultCount; + if((resultCount = result.count()) != 0) { + finalResult = new UnicodeString[resultCount]; + if (finalResult == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + } + else { + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + //result.toArray(finalResult); + result_len = 0; + el = UHASH_FIRST; + ne = result.nextElement(el); + while(ne != nullptr) { + finalResult[result_len++] = *((UnicodeString *)(ne->value.pointer)); + ne = result.nextElement(el); + } + + + return finalResult; +} + +Hashtable *CanonicalIterator::getEquivalents2(Hashtable *fillinResult, const char16_t *segment, int32_t segLen, UErrorCode &status) { + + if (U_FAILURE(status)) { + return nullptr; + } + + //if (PROGRESS) printf("Adding: %s\n", UToS(Tr(segment))); + + UnicodeString toPut(segment, segLen); + + fillinResult->put(toPut, new UnicodeString(toPut), status); + + UnicodeSet starts; + + // cycle through all the characters + UChar32 cp; + for (int32_t i = 0; i < segLen; i += U16_LENGTH(cp)) { + // see if any character is at the start of some decomposition + U16_GET(segment, 0, i, segLen, cp); + if (!nfcImpl.getCanonStartSet(cp, starts)) { + continue; + } + // if so, see which decompositions match + UnicodeSetIterator iter(starts); + while (iter.next()) { + UChar32 cp2 = iter.getCodepoint(); + Hashtable remainder(status); + remainder.setValueDeleter(uprv_deleteUObject); + if (extract(&remainder, cp2, segment, segLen, i, status) == nullptr) { + continue; + } + + // there were some matches, so add all the possibilities to the set. + UnicodeString prefix(segment, i); + prefix += cp2; + + int32_t el = UHASH_FIRST; + const UHashElement *ne = remainder.nextElement(el); + while (ne != nullptr) { + UnicodeString item = *((UnicodeString *)(ne->value.pointer)); + UnicodeString *toAdd = new UnicodeString(prefix); + /* test for nullptr */ + if (toAdd == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + *toAdd += item; + fillinResult->put(*toAdd, toAdd, status); + + //if (PROGRESS) printf("Adding: %s\n", UToS(Tr(*toAdd))); + + ne = remainder.nextElement(el); + } + } + } + + /* Test for buffer overflows */ + if(U_FAILURE(status)) { + return nullptr; + } + return fillinResult; +} + +/** + * See if the decomposition of cp2 is at segment starting at segmentPos + * (with canonical rearrangement!) + * If so, take the remainder, and return the equivalents + */ +Hashtable *CanonicalIterator::extract(Hashtable *fillinResult, UChar32 comp, const char16_t *segment, int32_t segLen, int32_t segmentPos, UErrorCode &status) { +//Hashtable *CanonicalIterator::extract(UChar32 comp, const UnicodeString &segment, int32_t segLen, int32_t segmentPos, UErrorCode &status) { + //if (PROGRESS) printf(" extract: %s, ", UToS(Tr(UnicodeString(comp)))); + //if (PROGRESS) printf("%s, %i\n", UToS(Tr(segment)), segmentPos); + + if (U_FAILURE(status)) { + return nullptr; + } + + UnicodeString temp(comp); + int32_t inputLen=temp.length(); + UnicodeString decompString; + nfd.normalize(temp, decompString, status); + if (U_FAILURE(status)) { + return nullptr; + } + if (decompString.isBogus()) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + const char16_t *decomp=decompString.getBuffer(); + int32_t decompLen=decompString.length(); + + // See if it matches the start of segment (at segmentPos) + UBool ok = false; + UChar32 cp; + int32_t decompPos = 0; + UChar32 decompCp; + U16_NEXT(decomp, decompPos, decompLen, decompCp); + + int32_t i = segmentPos; + while(i < segLen) { + U16_NEXT(segment, i, segLen, cp); + + if (cp == decompCp) { // if equal, eat another cp from decomp + + //if (PROGRESS) printf(" matches: %s\n", UToS(Tr(UnicodeString(cp)))); + + if (decompPos == decompLen) { // done, have all decomp characters! + temp.append(segment+i, segLen-i); + ok = true; + break; + } + U16_NEXT(decomp, decompPos, decompLen, decompCp); + } else { + //if (PROGRESS) printf(" buffer: %s\n", UToS(Tr(UnicodeString(cp)))); + + // brute force approach + temp.append(cp); + + /* TODO: optimize + // since we know that the classes are monotonically increasing, after zero + // e.g. 0 5 7 9 0 3 + // we can do an optimization + // there are only a few cases that work: zero, less, same, greater + // if both classes are the same, we fail + // if the decomp class < the segment class, we fail + + segClass = getClass(cp); + if (decompClass <= segClass) return null; + */ + } + } + if (!ok) + return nullptr; // we failed, characters left over + + //if (PROGRESS) printf("Matches\n"); + + if (inputLen == temp.length()) { + fillinResult->put(UnicodeString(), new UnicodeString(), status); + return fillinResult; // succeed, but no remainder + } + + // brute force approach + // check to make sure result is canonically equivalent + UnicodeString trial; + nfd.normalize(temp, trial, status); + if(U_FAILURE(status) || trial.compare(segment+segmentPos, segLen - segmentPos) != 0) { + return nullptr; + } + + return getEquivalents2(fillinResult, temp.getBuffer()+inputLen, temp.length()-inputLen, status); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_NORMALIZATION */ diff --git a/deps/icu-small/source/common/capi_helper.h b/deps/icu-small/source/common/capi_helper.h index 54b1db9e331805..58715277938ad0 100644 --- a/deps/icu-small/source/common/capi_helper.h +++ b/deps/icu-small/source/common/capi_helper.h @@ -1,97 +1,97 @@ -// © 2018 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#ifndef __CAPI_HELPER_H__ -#define __CAPI_HELPER_H__ - -#include "unicode/utypes.h" - -U_NAMESPACE_BEGIN - -/** - * An internal helper class to help convert between C and C++ APIs. - */ -template -class IcuCApiHelper { - public: - /** - * Convert from the C type to the C++ type (const version). - */ - static const CPPType* validate(const CType* input, UErrorCode& status); - - /** - * Convert from the C type to the C++ type (non-const version). - */ - static CPPType* validate(CType* input, UErrorCode& status); - - /** - * Convert from the C++ type to the C type (const version). - */ - const CType* exportConstForC() const; - - /** - * Convert from the C++ type to the C type (non-const version). - */ - CType* exportForC(); - - /** - * Invalidates the object. - */ - ~IcuCApiHelper(); - - private: - /** - * While the object is valid, fMagic equals kMagic. - */ - int32_t fMagic = kMagic; -}; - - -template -const CPPType* -IcuCApiHelper::validate(const CType* 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 (static_cast*>(impl)->fMagic != kMagic) { - status = U_INVALID_FORMAT_ERROR; - return nullptr; - } - return impl; -} - -template -CPPType* -IcuCApiHelper::validate(CType* input, UErrorCode& status) { - auto* constInput = static_cast(input); - auto* validated = validate(constInput, status); - return const_cast(validated); -} - -template -const CType* -IcuCApiHelper::exportConstForC() const { - return reinterpret_cast(static_cast(this)); -} - -template -CType* -IcuCApiHelper::exportForC() { - return reinterpret_cast(static_cast(this)); -} - -template -IcuCApiHelper::~IcuCApiHelper() { - // head off application errors by preventing use of of deleted objects. - fMagic = 0; -} - - -U_NAMESPACE_END - -#endif // __CAPI_HELPER_H__ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#ifndef __CAPI_HELPER_H__ +#define __CAPI_HELPER_H__ + +#include "unicode/utypes.h" + +U_NAMESPACE_BEGIN + +/** + * An internal helper class to help convert between C and C++ APIs. + */ +template +class IcuCApiHelper { + public: + /** + * Convert from the C type to the C++ type (const version). + */ + static const CPPType* validate(const CType* input, UErrorCode& status); + + /** + * Convert from the C type to the C++ type (non-const version). + */ + static CPPType* validate(CType* input, UErrorCode& status); + + /** + * Convert from the C++ type to the C type (const version). + */ + const CType* exportConstForC() const; + + /** + * Convert from the C++ type to the C type (non-const version). + */ + CType* exportForC(); + + /** + * Invalidates the object. + */ + ~IcuCApiHelper(); + + private: + /** + * While the object is valid, fMagic equals kMagic. + */ + int32_t fMagic = kMagic; +}; + + +template +const CPPType* +IcuCApiHelper::validate(const CType* 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 (static_cast*>(impl)->fMagic != kMagic) { + status = U_INVALID_FORMAT_ERROR; + return nullptr; + } + return impl; +} + +template +CPPType* +IcuCApiHelper::validate(CType* input, UErrorCode& status) { + auto* constInput = static_cast(input); + auto* validated = validate(constInput, status); + return const_cast(validated); +} + +template +const CType* +IcuCApiHelper::exportConstForC() const { + return reinterpret_cast(static_cast(this)); +} + +template +CType* +IcuCApiHelper::exportForC() { + return reinterpret_cast(static_cast(this)); +} + +template +IcuCApiHelper::~IcuCApiHelper() { + // head off application errors by preventing use of of deleted objects. + fMagic = 0; +} + + +U_NAMESPACE_END + +#endif // __CAPI_HELPER_H__ diff --git a/deps/icu-small/source/common/characterproperties.cpp b/deps/icu-small/source/common/characterproperties.cpp index 2316a391a38cbf..6b922f465a9ffd 100644 --- a/deps/icu-small/source/common/characterproperties.cpp +++ b/deps/icu-small/source/common/characterproperties.cpp @@ -1,411 +1,419 @@ -// © 2018 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// characterproperties.cpp -// created: 2018sep03 Markus W. Scherer - -#include "unicode/utypes.h" -#include "unicode/localpointer.h" -#include "unicode/uchar.h" -#include "unicode/ucpmap.h" -#include "unicode/ucptrie.h" -#include "unicode/umutablecptrie.h" -#include "unicode/uniset.h" -#include "unicode/uscript.h" -#include "unicode/uset.h" -#include "cmemory.h" -#include "emojiprops.h" -#include "mutex.h" -#include "normalizer2impl.h" -#include "uassert.h" -#include "ubidi_props.h" -#include "ucase.h" -#include "ucln_cmn.h" -#include "umutex.h" -#include "uprops.h" - -using icu::LocalPointer; -#if !UCONFIG_NO_NORMALIZATION -using icu::Normalizer2Factory; -using icu::Normalizer2Impl; -#endif -using icu::UInitOnce; -using icu::UnicodeSet; - -namespace { - -UBool U_CALLCONV characterproperties_cleanup(); - -constexpr int32_t NUM_INCLUSIONS = UPROPS_SRC_COUNT + (UCHAR_INT_LIMIT - UCHAR_INT_START); - -struct Inclusion { - UnicodeSet *fSet = nullptr; - UInitOnce fInitOnce {}; -}; -Inclusion gInclusions[NUM_INCLUSIONS]; // cached getInclusions() - -UnicodeSet *sets[UCHAR_BINARY_LIMIT] = {}; - -UCPMap *maps[UCHAR_INT_LIMIT - UCHAR_INT_START] = {}; - -icu::UMutex cpMutex; - -//---------------------------------------------------------------- -// Inclusions list -//---------------------------------------------------------------- - -// USetAdder implementation -// Does not use uset.h to reduce code dependencies -void U_CALLCONV -_set_add(USet *set, UChar32 c) { - ((UnicodeSet *)set)->add(c); -} - -void U_CALLCONV -_set_addRange(USet *set, UChar32 start, UChar32 end) { - ((UnicodeSet *)set)->add(start, end); -} - -void U_CALLCONV -_set_addString(USet *set, const UChar *str, int32_t length) { - ((UnicodeSet *)set)->add(icu::UnicodeString((UBool)(length<0), str, length)); -} - -UBool U_CALLCONV characterproperties_cleanup() { - for (Inclusion &in: gInclusions) { - delete in.fSet; - in.fSet = nullptr; - in.fInitOnce.reset(); - } - for (int32_t i = 0; i < UPRV_LENGTHOF(sets); ++i) { - delete sets[i]; - sets[i] = nullptr; - } - for (int32_t i = 0; i < UPRV_LENGTHOF(maps); ++i) { - ucptrie_close(reinterpret_cast(maps[i])); - maps[i] = nullptr; - } - return true; -} - -void U_CALLCONV initInclusion(UPropertySource src, UErrorCode &errorCode) { - // This function is invoked only via umtx_initOnce(). - U_ASSERT(0 <= src && src < UPROPS_SRC_COUNT); - if (src == UPROPS_SRC_NONE) { - errorCode = U_INTERNAL_PROGRAM_ERROR; - return; - } - U_ASSERT(gInclusions[src].fSet == nullptr); - - LocalPointer incl(new UnicodeSet()); - if (incl.isNull()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - USetAdder sa = { - (USet *)incl.getAlias(), - _set_add, - _set_addRange, - _set_addString, - nullptr, // don't need remove() - nullptr // don't need removeRange() - }; - - switch(src) { - case UPROPS_SRC_CHAR: - uchar_addPropertyStarts(&sa, &errorCode); - break; - case UPROPS_SRC_PROPSVEC: - upropsvec_addPropertyStarts(&sa, &errorCode); - break; - case UPROPS_SRC_CHAR_AND_PROPSVEC: - uchar_addPropertyStarts(&sa, &errorCode); - upropsvec_addPropertyStarts(&sa, &errorCode); - break; -#if !UCONFIG_NO_NORMALIZATION - case UPROPS_SRC_CASE_AND_NORM: { - const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(errorCode); - if(U_SUCCESS(errorCode)) { - impl->addPropertyStarts(&sa, errorCode); - } - ucase_addPropertyStarts(&sa, &errorCode); - break; - } - case UPROPS_SRC_NFC: { - const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(errorCode); - if(U_SUCCESS(errorCode)) { - impl->addPropertyStarts(&sa, errorCode); - } - break; - } - case UPROPS_SRC_NFKC: { - const Normalizer2Impl *impl=Normalizer2Factory::getNFKCImpl(errorCode); - if(U_SUCCESS(errorCode)) { - impl->addPropertyStarts(&sa, errorCode); - } - break; - } - case UPROPS_SRC_NFKC_CF: { - const Normalizer2Impl *impl=Normalizer2Factory::getNFKC_CFImpl(errorCode); - if(U_SUCCESS(errorCode)) { - impl->addPropertyStarts(&sa, errorCode); - } - break; - } - case UPROPS_SRC_NFC_CANON_ITER: { - const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(errorCode); - if(U_SUCCESS(errorCode)) { - impl->addCanonIterPropertyStarts(&sa, errorCode); - } - break; - } -#endif - case UPROPS_SRC_CASE: - ucase_addPropertyStarts(&sa, &errorCode); - break; - case UPROPS_SRC_BIDI: - ubidi_addPropertyStarts(&sa, &errorCode); - break; - case UPROPS_SRC_INPC: - case UPROPS_SRC_INSC: - case UPROPS_SRC_VO: - uprops_addPropertyStarts((UPropertySource)src, &sa, &errorCode); - break; - case UPROPS_SRC_EMOJI: { - const icu::EmojiProps *ep = icu::EmojiProps::getSingleton(errorCode); - if (U_SUCCESS(errorCode)) { - ep->addPropertyStarts(&sa, errorCode); - } - break; - } - default: - errorCode = U_INTERNAL_PROGRAM_ERROR; - break; - } - - if (U_FAILURE(errorCode)) { - return; - } - if (incl->isBogus()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - // Compact for caching. - incl->compact(); - gInclusions[src].fSet = incl.orphan(); - ucln_common_registerCleanup(UCLN_COMMON_CHARACTERPROPERTIES, characterproperties_cleanup); -} - -const UnicodeSet *getInclusionsForSource(UPropertySource src, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return nullptr; } - if (src < 0 || UPROPS_SRC_COUNT <= src) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - Inclusion &i = gInclusions[src]; - umtx_initOnce(i.fInitOnce, &initInclusion, src, errorCode); - return i.fSet; -} - -void U_CALLCONV initIntPropInclusion(UProperty prop, UErrorCode &errorCode) { - // This function is invoked only via umtx_initOnce(). - U_ASSERT(UCHAR_INT_START <= prop && prop < UCHAR_INT_LIMIT); - int32_t inclIndex = UPROPS_SRC_COUNT + (prop - UCHAR_INT_START); - U_ASSERT(gInclusions[inclIndex].fSet == nullptr); - UPropertySource src = uprops_getSource(prop); - const UnicodeSet *incl = getInclusionsForSource(src, errorCode); - if (U_FAILURE(errorCode)) { - return; - } - - LocalPointer intPropIncl(new UnicodeSet(0, 0)); - if (intPropIncl.isNull()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - int32_t numRanges = incl->getRangeCount(); - int32_t prevValue = 0; - for (int32_t i = 0; i < numRanges; ++i) { - UChar32 rangeEnd = incl->getRangeEnd(i); - for (UChar32 c = incl->getRangeStart(i); c <= rangeEnd; ++c) { - // TODO: Get a UCharacterProperty.IntProperty to avoid the property dispatch. - int32_t value = u_getIntPropertyValue(c, prop); - if (value != prevValue) { - intPropIncl->add(c); - prevValue = value; - } - } - } - - if (intPropIncl->isBogus()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - // Compact for caching. - intPropIncl->compact(); - gInclusions[inclIndex].fSet = intPropIncl.orphan(); - ucln_common_registerCleanup(UCLN_COMMON_CHARACTERPROPERTIES, characterproperties_cleanup); -} - -} // namespace - -U_NAMESPACE_BEGIN - -const UnicodeSet *CharacterProperties::getInclusionsForProperty( - UProperty prop, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return nullptr; } - if (UCHAR_INT_START <= prop && prop < UCHAR_INT_LIMIT) { - int32_t inclIndex = UPROPS_SRC_COUNT + (prop - UCHAR_INT_START); - Inclusion &i = gInclusions[inclIndex]; - umtx_initOnce(i.fInitOnce, &initIntPropInclusion, prop, errorCode); - return i.fSet; - } else { - UPropertySource src = uprops_getSource(prop); - return getInclusionsForSource(src, errorCode); - } -} - -U_NAMESPACE_END - -namespace { - -UnicodeSet *makeSet(UProperty property, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return nullptr; } - LocalPointer set(new UnicodeSet()); - if (set.isNull()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - if (UCHAR_BASIC_EMOJI <= property && property <= UCHAR_RGI_EMOJI) { - // property of strings - const icu::EmojiProps *ep = icu::EmojiProps::getSingleton(errorCode); - if (U_FAILURE(errorCode)) { return nullptr; } - USetAdder sa = { - (USet *)set.getAlias(), - _set_add, - _set_addRange, - _set_addString, - nullptr, // don't need remove() - nullptr // don't need removeRange() - }; - ep->addStrings(&sa, property, errorCode); - if (property != UCHAR_BASIC_EMOJI && property != UCHAR_RGI_EMOJI) { - // property of _only_ strings - set->freeze(); - return set.orphan(); - } - } - - const UnicodeSet *inclusions = - icu::CharacterProperties::getInclusionsForProperty(property, errorCode); - if (U_FAILURE(errorCode)) { return nullptr; } - int32_t numRanges = inclusions->getRangeCount(); - UChar32 startHasProperty = -1; - - for (int32_t i = 0; i < numRanges; ++i) { - UChar32 rangeEnd = inclusions->getRangeEnd(i); - for (UChar32 c = inclusions->getRangeStart(i); c <= rangeEnd; ++c) { - // TODO: Get a UCharacterProperty.BinaryProperty to avoid the property dispatch. - if (u_hasBinaryProperty(c, property)) { - if (startHasProperty < 0) { - // Transition from false to true. - startHasProperty = c; - } - } else if (startHasProperty >= 0) { - // Transition from true to false. - set->add(startHasProperty, c - 1); - startHasProperty = -1; - } - } - } - if (startHasProperty >= 0) { - set->add(startHasProperty, 0x10FFFF); - } - set->freeze(); - return set.orphan(); -} - -UCPMap *makeMap(UProperty property, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return nullptr; } - uint32_t nullValue = property == UCHAR_SCRIPT ? USCRIPT_UNKNOWN : 0; - icu::LocalUMutableCPTriePointer mutableTrie( - umutablecptrie_open(nullValue, nullValue, &errorCode)); - const UnicodeSet *inclusions = - icu::CharacterProperties::getInclusionsForProperty(property, errorCode); - if (U_FAILURE(errorCode)) { return nullptr; } - int32_t numRanges = inclusions->getRangeCount(); - UChar32 start = 0; - uint32_t value = nullValue; - - for (int32_t i = 0; i < numRanges; ++i) { - UChar32 rangeEnd = inclusions->getRangeEnd(i); - for (UChar32 c = inclusions->getRangeStart(i); c <= rangeEnd; ++c) { - // TODO: Get a UCharacterProperty.IntProperty to avoid the property dispatch. - uint32_t nextValue = u_getIntPropertyValue(c, property); - if (value != nextValue) { - if (value != nullValue) { - umutablecptrie_setRange(mutableTrie.getAlias(), start, c - 1, value, &errorCode); - } - start = c; - value = nextValue; - } - } - } - if (value != 0) { - umutablecptrie_setRange(mutableTrie.getAlias(), start, 0x10FFFF, value, &errorCode); - } - - UCPTrieType type; - if (property == UCHAR_BIDI_CLASS || property == UCHAR_GENERAL_CATEGORY) { - type = UCPTRIE_TYPE_FAST; - } else { - type = UCPTRIE_TYPE_SMALL; - } - UCPTrieValueWidth valueWidth; - // TODO: UCharacterProperty.IntProperty - int32_t max = u_getIntPropertyMaxValue(property); - if (max <= 0xff) { - valueWidth = UCPTRIE_VALUE_BITS_8; - } else if (max <= 0xffff) { - valueWidth = UCPTRIE_VALUE_BITS_16; - } else { - valueWidth = UCPTRIE_VALUE_BITS_32; - } - return reinterpret_cast( - umutablecptrie_buildImmutable(mutableTrie.getAlias(), type, valueWidth, &errorCode)); -} - -} // namespace - -U_NAMESPACE_USE - -U_CAPI const USet * U_EXPORT2 -u_getBinaryPropertySet(UProperty property, UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { return nullptr; } - if (property < 0 || UCHAR_BINARY_LIMIT <= property) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - Mutex m(&cpMutex); - UnicodeSet *set = sets[property]; - if (set == nullptr) { - sets[property] = set = makeSet(property, *pErrorCode); - } - if (U_FAILURE(*pErrorCode)) { return nullptr; } - return set->toUSet(); -} - -U_CAPI const UCPMap * U_EXPORT2 -u_getIntPropertyMap(UProperty property, UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { return nullptr; } - if (property < UCHAR_INT_START || UCHAR_INT_LIMIT <= property) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - Mutex m(&cpMutex); - UCPMap *map = maps[property - UCHAR_INT_START]; - if (map == nullptr) { - maps[property - UCHAR_INT_START] = map = makeMap(property, *pErrorCode); - } - return map; -} +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// characterproperties.cpp +// created: 2018sep03 Markus W. Scherer + +#include "unicode/utypes.h" +#include "unicode/localpointer.h" +#include "unicode/uchar.h" +#include "unicode/ucpmap.h" +#include "unicode/ucptrie.h" +#include "unicode/umutablecptrie.h" +#include "unicode/uniset.h" +#include "unicode/uscript.h" +#include "unicode/uset.h" +#include "cmemory.h" +#include "emojiprops.h" +#include "mutex.h" +#include "normalizer2impl.h" +#include "uassert.h" +#include "ubidi_props.h" +#include "ucase.h" +#include "ucln_cmn.h" +#include "umutex.h" +#include "uprops.h" + +using icu::LocalPointer; +#if !UCONFIG_NO_NORMALIZATION +using icu::Normalizer2Factory; +using icu::Normalizer2Impl; +#endif +using icu::UInitOnce; +using icu::UnicodeSet; + +namespace { + +UBool U_CALLCONV characterproperties_cleanup(); + +constexpr int32_t NUM_INCLUSIONS = UPROPS_SRC_COUNT + (UCHAR_INT_LIMIT - UCHAR_INT_START); + +struct Inclusion { + UnicodeSet *fSet = nullptr; + UInitOnce fInitOnce {}; +}; +Inclusion gInclusions[NUM_INCLUSIONS]; // cached getInclusions() + +UnicodeSet *sets[UCHAR_BINARY_LIMIT] = {}; + +UCPMap *maps[UCHAR_INT_LIMIT - UCHAR_INT_START] = {}; + +icu::UMutex cpMutex; + +//---------------------------------------------------------------- +// Inclusions list +//---------------------------------------------------------------- + +// USetAdder implementation +// Does not use uset.h to reduce code dependencies +void U_CALLCONV +_set_add(USet *set, UChar32 c) { + ((UnicodeSet *)set)->add(c); +} + +void U_CALLCONV +_set_addRange(USet *set, UChar32 start, UChar32 end) { + ((UnicodeSet *)set)->add(start, end); +} + +void U_CALLCONV +_set_addString(USet *set, const char16_t *str, int32_t length) { + ((UnicodeSet *)set)->add(icu::UnicodeString((UBool)(length<0), str, length)); +} + +UBool U_CALLCONV characterproperties_cleanup() { + for (Inclusion &in: gInclusions) { + delete in.fSet; + in.fSet = nullptr; + in.fInitOnce.reset(); + } + for (int32_t i = 0; i < UPRV_LENGTHOF(sets); ++i) { + delete sets[i]; + sets[i] = nullptr; + } + for (int32_t i = 0; i < UPRV_LENGTHOF(maps); ++i) { + ucptrie_close(reinterpret_cast(maps[i])); + maps[i] = nullptr; + } + return true; +} + +void U_CALLCONV initInclusion(UPropertySource src, UErrorCode &errorCode) { + // This function is invoked only via umtx_initOnce(). + U_ASSERT(0 <= src && src < UPROPS_SRC_COUNT); + if (src == UPROPS_SRC_NONE) { + errorCode = U_INTERNAL_PROGRAM_ERROR; + return; + } + U_ASSERT(gInclusions[src].fSet == nullptr); + + LocalPointer incl(new UnicodeSet()); + if (incl.isNull()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + USetAdder sa = { + (USet *)incl.getAlias(), + _set_add, + _set_addRange, + _set_addString, + nullptr, // don't need remove() + nullptr // don't need removeRange() + }; + + switch(src) { + case UPROPS_SRC_CHAR: + uchar_addPropertyStarts(&sa, &errorCode); + break; + case UPROPS_SRC_PROPSVEC: + upropsvec_addPropertyStarts(&sa, &errorCode); + break; + case UPROPS_SRC_CHAR_AND_PROPSVEC: + uchar_addPropertyStarts(&sa, &errorCode); + upropsvec_addPropertyStarts(&sa, &errorCode); + break; +#if !UCONFIG_NO_NORMALIZATION + case UPROPS_SRC_CASE_AND_NORM: { + const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(errorCode); + if(U_SUCCESS(errorCode)) { + impl->addPropertyStarts(&sa, errorCode); + } + ucase_addPropertyStarts(&sa, &errorCode); + break; + } + case UPROPS_SRC_NFC: { + const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(errorCode); + if(U_SUCCESS(errorCode)) { + impl->addPropertyStarts(&sa, errorCode); + } + break; + } + case UPROPS_SRC_NFKC: { + const Normalizer2Impl *impl=Normalizer2Factory::getNFKCImpl(errorCode); + if(U_SUCCESS(errorCode)) { + impl->addPropertyStarts(&sa, errorCode); + } + break; + } + case UPROPS_SRC_NFKC_CF: { + const Normalizer2Impl *impl=Normalizer2Factory::getNFKC_CFImpl(errorCode); + if(U_SUCCESS(errorCode)) { + impl->addPropertyStarts(&sa, errorCode); + } + break; + } + case UPROPS_SRC_NFC_CANON_ITER: { + const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(errorCode); + if(U_SUCCESS(errorCode)) { + impl->addCanonIterPropertyStarts(&sa, errorCode); + } + break; + } +#endif + case UPROPS_SRC_CASE: + ucase_addPropertyStarts(&sa, &errorCode); + break; + case UPROPS_SRC_BIDI: + ubidi_addPropertyStarts(&sa, &errorCode); + break; + case UPROPS_SRC_INPC: + case UPROPS_SRC_INSC: + case UPROPS_SRC_VO: + uprops_addPropertyStarts((UPropertySource)src, &sa, &errorCode); + break; + case UPROPS_SRC_EMOJI: { + const icu::EmojiProps *ep = icu::EmojiProps::getSingleton(errorCode); + if (U_SUCCESS(errorCode)) { + ep->addPropertyStarts(&sa, errorCode); + } + break; + } + default: + errorCode = U_INTERNAL_PROGRAM_ERROR; + break; + } + + if (U_FAILURE(errorCode)) { + return; + } + if (incl->isBogus()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + // Compact for caching. + incl->compact(); + gInclusions[src].fSet = incl.orphan(); + ucln_common_registerCleanup(UCLN_COMMON_CHARACTERPROPERTIES, characterproperties_cleanup); +} + +const UnicodeSet *getInclusionsForSource(UPropertySource src, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return nullptr; } + if (src < 0 || UPROPS_SRC_COUNT <= src) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + Inclusion &i = gInclusions[src]; + umtx_initOnce(i.fInitOnce, &initInclusion, src, errorCode); + return i.fSet; +} + +void U_CALLCONV initIntPropInclusion(UProperty prop, UErrorCode &errorCode) { + // This function is invoked only via umtx_initOnce(). + U_ASSERT(UCHAR_INT_START <= prop && prop < UCHAR_INT_LIMIT); + int32_t inclIndex = UPROPS_SRC_COUNT + (prop - UCHAR_INT_START); + U_ASSERT(gInclusions[inclIndex].fSet == nullptr); + UPropertySource src = uprops_getSource(prop); + const UnicodeSet *incl = getInclusionsForSource(src, errorCode); + if (U_FAILURE(errorCode)) { + return; + } + + LocalPointer intPropIncl(new UnicodeSet(0, 0)); + if (intPropIncl.isNull()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + int32_t numRanges = incl->getRangeCount(); + int32_t prevValue = 0; + for (int32_t i = 0; i < numRanges; ++i) { + UChar32 rangeEnd = incl->getRangeEnd(i); + for (UChar32 c = incl->getRangeStart(i); c <= rangeEnd; ++c) { + // TODO: Get a UCharacterProperty.IntProperty to avoid the property dispatch. + int32_t value = u_getIntPropertyValue(c, prop); + if (value != prevValue) { + intPropIncl->add(c); + prevValue = value; + } + } + } + + if (intPropIncl->isBogus()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + // Compact for caching. + intPropIncl->compact(); + gInclusions[inclIndex].fSet = intPropIncl.orphan(); + ucln_common_registerCleanup(UCLN_COMMON_CHARACTERPROPERTIES, characterproperties_cleanup); +} + +} // namespace + +U_NAMESPACE_BEGIN + +const UnicodeSet *CharacterProperties::getInclusionsForProperty( + UProperty prop, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return nullptr; } + if (UCHAR_INT_START <= prop && prop < UCHAR_INT_LIMIT) { + int32_t inclIndex = UPROPS_SRC_COUNT + (prop - UCHAR_INT_START); + Inclusion &i = gInclusions[inclIndex]; + umtx_initOnce(i.fInitOnce, &initIntPropInclusion, prop, errorCode); + return i.fSet; + } else { + UPropertySource src = uprops_getSource(prop); + return getInclusionsForSource(src, errorCode); + } +} + +U_NAMESPACE_END + +namespace { + +UnicodeSet *makeSet(UProperty property, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return nullptr; } + LocalPointer set(new UnicodeSet()); + if (set.isNull()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + if (UCHAR_BASIC_EMOJI <= property && property <= UCHAR_RGI_EMOJI) { + // property of strings + const icu::EmojiProps *ep = icu::EmojiProps::getSingleton(errorCode); + if (U_FAILURE(errorCode)) { return nullptr; } + USetAdder sa = { + (USet *)set.getAlias(), + _set_add, + _set_addRange, + _set_addString, + nullptr, // don't need remove() + nullptr // don't need removeRange() + }; + ep->addStrings(&sa, property, errorCode); + if (property != UCHAR_BASIC_EMOJI && property != UCHAR_RGI_EMOJI) { + // property of _only_ strings + set->freeze(); + return set.orphan(); + } + } + + const UnicodeSet *inclusions = + icu::CharacterProperties::getInclusionsForProperty(property, errorCode); + if (U_FAILURE(errorCode)) { return nullptr; } + int32_t numRanges = inclusions->getRangeCount(); + UChar32 startHasProperty = -1; + + for (int32_t i = 0; i < numRanges; ++i) { + UChar32 rangeEnd = inclusions->getRangeEnd(i); + for (UChar32 c = inclusions->getRangeStart(i); c <= rangeEnd; ++c) { + // TODO: Get a UCharacterProperty.BinaryProperty to avoid the property dispatch. + if (u_hasBinaryProperty(c, property)) { + if (startHasProperty < 0) { + // Transition from false to true. + startHasProperty = c; + } + } else if (startHasProperty >= 0) { + // Transition from true to false. + set->add(startHasProperty, c - 1); + startHasProperty = -1; + } + } + } + if (startHasProperty >= 0) { + set->add(startHasProperty, 0x10FFFF); + } + set->freeze(); + return set.orphan(); +} + +UCPMap *makeMap(UProperty property, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return nullptr; } + uint32_t nullValue = property == UCHAR_SCRIPT ? USCRIPT_UNKNOWN : 0; + icu::LocalUMutableCPTriePointer mutableTrie( + umutablecptrie_open(nullValue, nullValue, &errorCode)); + const UnicodeSet *inclusions = + icu::CharacterProperties::getInclusionsForProperty(property, errorCode); + if (U_FAILURE(errorCode)) { return nullptr; } + int32_t numRanges = inclusions->getRangeCount(); + UChar32 start = 0; + uint32_t value = nullValue; + + for (int32_t i = 0; i < numRanges; ++i) { + UChar32 rangeEnd = inclusions->getRangeEnd(i); + for (UChar32 c = inclusions->getRangeStart(i); c <= rangeEnd; ++c) { + // TODO: Get a UCharacterProperty.IntProperty to avoid the property dispatch. + uint32_t nextValue = u_getIntPropertyValue(c, property); + if (value != nextValue) { + if (value != nullValue) { + umutablecptrie_setRange(mutableTrie.getAlias(), start, c - 1, value, &errorCode); + } + start = c; + value = nextValue; + } + } + } + if (value != 0) { + umutablecptrie_setRange(mutableTrie.getAlias(), start, 0x10FFFF, value, &errorCode); + } + + UCPTrieType type; + if (property == UCHAR_BIDI_CLASS || property == UCHAR_GENERAL_CATEGORY) { + type = UCPTRIE_TYPE_FAST; + } else { + type = UCPTRIE_TYPE_SMALL; + } + UCPTrieValueWidth valueWidth; + // TODO: UCharacterProperty.IntProperty + int32_t max = u_getIntPropertyMaxValue(property); + if (max <= 0xff) { + valueWidth = UCPTRIE_VALUE_BITS_8; + } else if (max <= 0xffff) { + valueWidth = UCPTRIE_VALUE_BITS_16; + } else { + valueWidth = UCPTRIE_VALUE_BITS_32; + } + return reinterpret_cast( + umutablecptrie_buildImmutable(mutableTrie.getAlias(), type, valueWidth, &errorCode)); +} + +} // namespace + +U_NAMESPACE_BEGIN + +const UnicodeSet *CharacterProperties::getBinaryPropertySet(UProperty property, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return nullptr; } + if (property < 0 || UCHAR_BINARY_LIMIT <= property) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + Mutex m(&cpMutex); + UnicodeSet *set = sets[property]; + if (set == nullptr) { + sets[property] = set = makeSet(property, errorCode); + } + return set; +} + +U_NAMESPACE_END + +U_NAMESPACE_USE + +U_CAPI const USet * U_EXPORT2 +u_getBinaryPropertySet(UProperty property, UErrorCode *pErrorCode) { + const UnicodeSet *set = CharacterProperties::getBinaryPropertySet(property, *pErrorCode); + return U_SUCCESS(*pErrorCode) ? set->toUSet() : nullptr; +} + +U_CAPI const UCPMap * U_EXPORT2 +u_getIntPropertyMap(UProperty property, UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { return nullptr; } + if (property < UCHAR_INT_START || UCHAR_INT_LIMIT <= property) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + Mutex m(&cpMutex); + UCPMap *map = maps[property - UCHAR_INT_START]; + if (map == nullptr) { + maps[property - UCHAR_INT_START] = map = makeMap(property, *pErrorCode); + } + return map; +} diff --git a/deps/icu-small/source/common/chariter.cpp b/deps/icu-small/source/common/chariter.cpp index 887119a0ebaa15..41ed1ff56e32f3 100644 --- a/deps/icu-small/source/common/chariter.cpp +++ b/deps/icu-small/source/common/chariter.cpp @@ -1,100 +1,100 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#include "unicode/chariter.h" - -U_NAMESPACE_BEGIN - -ForwardCharacterIterator::~ForwardCharacterIterator() {} -ForwardCharacterIterator::ForwardCharacterIterator() -: UObject() -{} -ForwardCharacterIterator::ForwardCharacterIterator(const ForwardCharacterIterator &other) -: UObject(other) -{} - - -CharacterIterator::CharacterIterator() -: textLength(0), pos(0), begin(0), end(0) { -} - -CharacterIterator::CharacterIterator(int32_t length) -: textLength(length), pos(0), begin(0), end(length) { - if(textLength < 0) { - textLength = end = 0; - } -} - -CharacterIterator::CharacterIterator(int32_t length, int32_t position) -: textLength(length), pos(position), begin(0), end(length) { - if(textLength < 0) { - textLength = end = 0; - } - if(pos < 0) { - pos = 0; - } else if(pos > end) { - pos = end; - } -} - -CharacterIterator::CharacterIterator(int32_t length, int32_t textBegin, int32_t textEnd, int32_t position) -: textLength(length), pos(position), begin(textBegin), end(textEnd) { - if(textLength < 0) { - textLength = 0; - } - if(begin < 0) { - begin = 0; - } else if(begin > textLength) { - begin = textLength; - } - if(end < begin) { - end = begin; - } else if(end > textLength) { - end = textLength; - } - if(pos < begin) { - pos = begin; - } else if(pos > end) { - pos = end; - } -} - -CharacterIterator::~CharacterIterator() {} - -CharacterIterator::CharacterIterator(const CharacterIterator &that) : -ForwardCharacterIterator(that), -textLength(that.textLength), pos(that.pos), begin(that.begin), end(that.end) -{ -} - -CharacterIterator & -CharacterIterator::operator=(const CharacterIterator &that) { - ForwardCharacterIterator::operator=(that); - textLength = that.textLength; - pos = that.pos; - begin = that.begin; - end = that.end; - return *this; -} - -// implementing first[32]PostInc() directly in a subclass should be faster -// but these implementations make subclassing a little easier -UChar -CharacterIterator::firstPostInc(void) { - setToStart(); - return nextPostInc(); -} - -UChar32 -CharacterIterator::first32PostInc(void) { - setToStart(); - return next32PostInc(); -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#include "unicode/chariter.h" + +U_NAMESPACE_BEGIN + +ForwardCharacterIterator::~ForwardCharacterIterator() {} +ForwardCharacterIterator::ForwardCharacterIterator() +: UObject() +{} +ForwardCharacterIterator::ForwardCharacterIterator(const ForwardCharacterIterator &other) +: UObject(other) +{} + + +CharacterIterator::CharacterIterator() +: textLength(0), pos(0), begin(0), end(0) { +} + +CharacterIterator::CharacterIterator(int32_t length) +: textLength(length), pos(0), begin(0), end(length) { + if(textLength < 0) { + textLength = end = 0; + } +} + +CharacterIterator::CharacterIterator(int32_t length, int32_t position) +: textLength(length), pos(position), begin(0), end(length) { + if(textLength < 0) { + textLength = end = 0; + } + if(pos < 0) { + pos = 0; + } else if(pos > end) { + pos = end; + } +} + +CharacterIterator::CharacterIterator(int32_t length, int32_t textBegin, int32_t textEnd, int32_t position) +: textLength(length), pos(position), begin(textBegin), end(textEnd) { + if(textLength < 0) { + textLength = 0; + } + if(begin < 0) { + begin = 0; + } else if(begin > textLength) { + begin = textLength; + } + if(end < begin) { + end = begin; + } else if(end > textLength) { + end = textLength; + } + if(pos < begin) { + pos = begin; + } else if(pos > end) { + pos = end; + } +} + +CharacterIterator::~CharacterIterator() {} + +CharacterIterator::CharacterIterator(const CharacterIterator &that) : +ForwardCharacterIterator(that), +textLength(that.textLength), pos(that.pos), begin(that.begin), end(that.end) +{ +} + +CharacterIterator & +CharacterIterator::operator=(const CharacterIterator &that) { + ForwardCharacterIterator::operator=(that); + textLength = that.textLength; + pos = that.pos; + begin = that.begin; + end = that.end; + return *this; +} + +// implementing first[32]PostInc() directly in a subclass should be faster +// but these implementations make subclassing a little easier +char16_t +CharacterIterator::firstPostInc() { + setToStart(); + return nextPostInc(); +} + +UChar32 +CharacterIterator::first32PostInc() { + setToStart(); + return next32PostInc(); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/charstr.cpp b/deps/icu-small/source/common/charstr.cpp index 8a0994c73742ee..6fac8844d88611 100644 --- a/deps/icu-small/source/common/charstr.cpp +++ b/deps/icu-small/source/common/charstr.cpp @@ -1,273 +1,273 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: charstr.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010may19 -* created by: Markus W. Scherer -*/ - -#include - -#include "unicode/utypes.h" -#include "unicode/putil.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "uinvchar.h" -#include "ustr_imp.h" - -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; -} - -char *CharString::cloneData(UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { return nullptr; } - char *p = static_cast(uprv_malloc(len + 1)); - if (p == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - uprv_memcpy(p, buffer.getAlias(), len + 1); - return p; -} - -int32_t CharString::extract(char *dest, int32_t capacity, UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { return len; } - if (capacity < 0 || (capacity > 0 && dest == nullptr)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return len; - } - const char *src = buffer.getAlias(); - if (0 < len && len <= capacity && src != dest) { - uprv_memcpy(dest, src, len); - } - return u_terminateChars(dest, capacity, len, &errorCode); -} - -CharString &CharString::copyFrom(const CharString &s, UErrorCode &errorCode) { - if(U_SUCCESS(errorCode) && this!=&s && ensureCapacity(s.len+1, 0, errorCode)) { - len=s.len; - uprv_memcpy(buffer.getAlias(), s.buffer.getAlias(), len+1); - } - return *this; -} - -int32_t CharString::lastIndexOf(char c) const { - for(int32_t i=len; i>0;) { - if(buffer[--i]==c) { - return i; - } - } - return -1; -} - -bool CharString::contains(StringPiece s) const { - if (s.empty()) { return false; } - const char *p = buffer.getAlias(); - int32_t lastStart = len - s.length(); - for (int32_t i = 0; i <= lastStart; ++i) { - if (uprv_memcmp(p + i, s.data(), s.length()) == 0) { - return true; - } - } - return false; -} - -CharString &CharString::truncate(int32_t newLength) { - if(newLength<0) { - newLength=0; - } - if(newLength(uprv_strlen(s)); - } - if(sLength>0) { - if(s==(buffer.getAlias()+len)) { - // The caller wrote into the getAppendBuffer(). - if(sLength>=(buffer.getCapacity()-len)) { - // The caller wrote too much. - errorCode=U_INTERNAL_PROGRAM_ERROR; - } else { - buffer[len+=sLength]=0; - } - } else if(buffer.getAlias()<=s && s<(buffer.getAlias()+len) && - sLength>=(buffer.getCapacity()-len) - ) { - // (Part of) this string is appended to itself which requires reallocation, - // so we have to make a copy of the substring and append that. - return append(CharString(s, sLength, errorCode), errorCode); - } else if(ensureCapacity(len+sLength+1, 0, errorCode)) { - uprv_memcpy(buffer.getAlias()+len, s, sLength); - buffer[len+=sLength]=0; - } - } - return *this; -} - -CharString &CharString::appendNumber(int32_t number, UErrorCode &status) { - if (number < 0) { - this->append('-', status); - if (U_FAILURE(status)) { - return *this; - } - } - - if (number == 0) { - this->append('0', status); - return *this; - } - - int32_t numLen = 0; - while (number != 0) { - int32_t residue = number % 10; - number /= 10; - this->append(std::abs(residue) + '0', status); - numLen++; - if (U_FAILURE(status)) { - return *this; - } - } - - int32_t start = this->length() - numLen, end = this->length() - 1; - while(start < end) { - std::swap(this->data()[start++], this->data()[end--]); - } - - return *this; -} - -char *CharString::getAppendBuffer(int32_t minCapacity, - int32_t desiredCapacityHint, - int32_t &resultCapacity, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - resultCapacity=0; - return NULL; - } - int32_t appendCapacity=buffer.getCapacity()-len-1; // -1 for NUL - if(appendCapacity>=minCapacity) { - resultCapacity=appendCapacity; - return buffer.getAlias()+len; - } - if(ensureCapacity(len+minCapacity+1, len+desiredCapacityHint+1, errorCode)) { - resultCapacity=buffer.getCapacity()-len-1; - return buffer.getAlias()+len; - } - resultCapacity=0; - return NULL; -} - -CharString &CharString::appendInvariantChars(const UnicodeString &s, UErrorCode &errorCode) { - return appendInvariantChars(s.getBuffer(), s.length(), errorCode); -} - -CharString &CharString::appendInvariantChars(const UChar* uchars, int32_t ucharsLen, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return *this; - } - if (!uprv_isInvariantUString(uchars, ucharsLen)) { - errorCode = U_INVARIANT_CONVERSION_ERROR; - return *this; - } - if(ensureCapacity(len+ucharsLen+1, 0, errorCode)) { - u_UCharsToChars(uchars, buffer.getAlias()+len, ucharsLen); - len += ucharsLen; - buffer[len] = 0; - } - return *this; -} - -UBool CharString::ensureCapacity(int32_t capacity, - int32_t desiredCapacityHint, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return false; - } - if(capacity>buffer.getCapacity()) { - if(desiredCapacityHint==0) { - desiredCapacityHint=capacity+buffer.getCapacity(); - } - if( (desiredCapacityHint<=capacity || buffer.resize(desiredCapacityHint, len+1)==NULL) && - buffer.resize(capacity, len+1)==NULL - ) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return false; - } - } - return true; -} - -CharString &CharString::appendPathPart(StringPiece s, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return *this; - } - if(s.length()==0) { - return *this; - } - char c; - if(len>0 && (c=buffer[len-1])!=U_FILE_SEP_CHAR && c!=U_FILE_ALT_SEP_CHAR) { - append(getDirSepChar(), errorCode); - } - append(s, errorCode); - return *this; -} - -CharString &CharString::ensureEndsWithFileSeparator(UErrorCode &errorCode) { - char c; - if(U_SUCCESS(errorCode) && len>0 && - (c=buffer[len-1])!=U_FILE_SEP_CHAR && c!=U_FILE_ALT_SEP_CHAR) { - append(getDirSepChar(), errorCode); - } - return *this; -} - -char CharString::getDirSepChar() const { - char dirSepChar = U_FILE_SEP_CHAR; -#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) - // We may need to return a different directory separator when building for Cygwin or MSYS2. - if(len>0 && !uprv_strchr(data(), U_FILE_SEP_CHAR) && uprv_strchr(data(), U_FILE_ALT_SEP_CHAR)) - dirSepChar = U_FILE_ALT_SEP_CHAR; -#endif - return dirSepChar; -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: charstr.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010may19 +* created by: Markus W. Scherer +*/ + +#include + +#include "unicode/utypes.h" +#include "unicode/putil.h" +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "uinvchar.h" +#include "ustr_imp.h" + +U_NAMESPACE_BEGIN + +CharString::CharString(CharString&& src) 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) 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; +} + +char *CharString::cloneData(UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { return nullptr; } + char *p = static_cast(uprv_malloc(len + 1)); + if (p == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memcpy(p, buffer.getAlias(), len + 1); + return p; +} + +int32_t CharString::extract(char *dest, int32_t capacity, UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { return len; } + if (capacity < 0 || (capacity > 0 && dest == nullptr)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return len; + } + const char *src = buffer.getAlias(); + if (0 < len && len <= capacity && src != dest) { + uprv_memcpy(dest, src, len); + } + return u_terminateChars(dest, capacity, len, &errorCode); +} + +CharString &CharString::copyFrom(const CharString &s, UErrorCode &errorCode) { + if(U_SUCCESS(errorCode) && this!=&s && ensureCapacity(s.len+1, 0, errorCode)) { + len=s.len; + uprv_memcpy(buffer.getAlias(), s.buffer.getAlias(), len+1); + } + return *this; +} + +int32_t CharString::lastIndexOf(char c) const { + for(int32_t i=len; i>0;) { + if(buffer[--i]==c) { + return i; + } + } + return -1; +} + +bool CharString::contains(StringPiece s) const { + if (s.empty()) { return false; } + const char *p = buffer.getAlias(); + int32_t lastStart = len - s.length(); + for (int32_t i = 0; i <= lastStart; ++i) { + if (uprv_memcmp(p + i, s.data(), s.length()) == 0) { + return true; + } + } + return false; +} + +CharString &CharString::truncate(int32_t newLength) { + if(newLength<0) { + newLength=0; + } + if(newLength(uprv_strlen(s)); + } + if(sLength>0) { + if(s==(buffer.getAlias()+len)) { + // The caller wrote into the getAppendBuffer(). + if(sLength>=(buffer.getCapacity()-len)) { + // The caller wrote too much. + errorCode=U_INTERNAL_PROGRAM_ERROR; + } else { + buffer[len+=sLength]=0; + } + } else if(buffer.getAlias()<=s && s<(buffer.getAlias()+len) && + sLength>=(buffer.getCapacity()-len) + ) { + // (Part of) this string is appended to itself which requires reallocation, + // so we have to make a copy of the substring and append that. + return append(CharString(s, sLength, errorCode), errorCode); + } else if(ensureCapacity(len+sLength+1, 0, errorCode)) { + uprv_memcpy(buffer.getAlias()+len, s, sLength); + buffer[len+=sLength]=0; + } + } + return *this; +} + +CharString &CharString::appendNumber(int32_t number, UErrorCode &status) { + if (number < 0) { + this->append('-', status); + if (U_FAILURE(status)) { + return *this; + } + } + + if (number == 0) { + this->append('0', status); + return *this; + } + + int32_t numLen = 0; + while (number != 0) { + int32_t residue = number % 10; + number /= 10; + this->append(std::abs(residue) + '0', status); + numLen++; + if (U_FAILURE(status)) { + return *this; + } + } + + int32_t start = this->length() - numLen, end = this->length() - 1; + while(start < end) { + std::swap(this->data()[start++], this->data()[end--]); + } + + return *this; +} + +char *CharString::getAppendBuffer(int32_t minCapacity, + int32_t desiredCapacityHint, + int32_t &resultCapacity, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + resultCapacity=0; + return nullptr; + } + int32_t appendCapacity=buffer.getCapacity()-len-1; // -1 for NUL + if(appendCapacity>=minCapacity) { + resultCapacity=appendCapacity; + return buffer.getAlias()+len; + } + if(ensureCapacity(len+minCapacity+1, len+desiredCapacityHint+1, errorCode)) { + resultCapacity=buffer.getCapacity()-len-1; + return buffer.getAlias()+len; + } + resultCapacity=0; + return nullptr; +} + +CharString &CharString::appendInvariantChars(const UnicodeString &s, UErrorCode &errorCode) { + return appendInvariantChars(s.getBuffer(), s.length(), errorCode); +} + +CharString &CharString::appendInvariantChars(const char16_t* uchars, int32_t ucharsLen, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return *this; + } + if (!uprv_isInvariantUString(uchars, ucharsLen)) { + errorCode = U_INVARIANT_CONVERSION_ERROR; + return *this; + } + if(ensureCapacity(len+ucharsLen+1, 0, errorCode)) { + u_UCharsToChars(uchars, buffer.getAlias()+len, ucharsLen); + len += ucharsLen; + buffer[len] = 0; + } + return *this; +} + +UBool CharString::ensureCapacity(int32_t capacity, + int32_t desiredCapacityHint, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return false; + } + if(capacity>buffer.getCapacity()) { + if(desiredCapacityHint==0) { + desiredCapacityHint=capacity+buffer.getCapacity(); + } + if( (desiredCapacityHint<=capacity || buffer.resize(desiredCapacityHint, len+1)==nullptr) && + buffer.resize(capacity, len+1)==nullptr + ) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return false; + } + } + return true; +} + +CharString &CharString::appendPathPart(StringPiece s, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return *this; + } + if(s.length()==0) { + return *this; + } + char c; + if(len>0 && (c=buffer[len-1])!=U_FILE_SEP_CHAR && c!=U_FILE_ALT_SEP_CHAR) { + append(getDirSepChar(), errorCode); + } + append(s, errorCode); + return *this; +} + +CharString &CharString::ensureEndsWithFileSeparator(UErrorCode &errorCode) { + char c; + if(U_SUCCESS(errorCode) && len>0 && + (c=buffer[len-1])!=U_FILE_SEP_CHAR && c!=U_FILE_ALT_SEP_CHAR) { + append(getDirSepChar(), errorCode); + } + return *this; +} + +char CharString::getDirSepChar() const { + char dirSepChar = U_FILE_SEP_CHAR; +#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) + // We may need to return a different directory separator when building for Cygwin or MSYS2. + if(len>0 && !uprv_strchr(data(), U_FILE_SEP_CHAR) && uprv_strchr(data(), U_FILE_ALT_SEP_CHAR)) + dirSepChar = U_FILE_ALT_SEP_CHAR; +#endif + return dirSepChar; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/charstr.h b/deps/icu-small/source/common/charstr.h index 92a75d3d2f91d7..18957d8d362cfe 100644 --- a/deps/icu-small/source/common/charstr.h +++ b/deps/icu-small/source/common/charstr.h @@ -1,193 +1,193 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2001-2015, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/19/2001 aliu Creation. -* 05/19/2010 markus Rewritten from scratch -********************************************************************** -*/ - -#ifndef CHARSTRING_H -#define CHARSTRING_H - -#include "unicode/utypes.h" -#include "unicode/unistr.h" -#include "unicode/uobject.h" -#include "cmemory.h" - -U_NAMESPACE_BEGIN - -// Windows needs us to DLL-export the MaybeStackArray template specialization, -// but MacOS X cannot handle it. Same as in digitlst.h. -#if !U_PLATFORM_IS_DARWIN_BASED -template class U_COMMON_API MaybeStackArray; -#endif - -/** - * ICU-internal char * string class. - * This class does not assume or enforce any particular character encoding. - * Raw bytes can be stored. The string object owns its characters. - * A terminating NUL is stored, but the class does not prevent embedded NUL characters. - * - * This class wants to be convenient but is also deliberately minimalist. - * Please do not add methods if they only add minor convenience. - * For example: - * cs.data()[5]='a'; // no need for setCharAt(5, 'a') - */ -class U_COMMON_API CharString : public UMemory { -public: - CharString() : len(0) { buffer[0]=0; } - CharString(StringPiece s, UErrorCode &errorCode) : len(0) { - buffer[0]=0; - append(s, errorCode); - } - CharString(const CharString &s, UErrorCode &errorCode) : len(0) { - buffer[0]=0; - append(s, errorCode); - } - CharString(const char *s, int32_t sLength, UErrorCode &errorCode) : len(0) { - buffer[0]=0; - append(s, sLength, errorCode); - } - ~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 - * the assignment operator, to make copies explicit and to - * use a UErrorCode where memory allocations might be needed. - */ - CharString ©From(const CharString &other, UErrorCode &errorCode); - - UBool isEmpty() const { return len==0; } - int32_t length() const { return len; } - char operator[](int32_t index) const { return buffer[index]; } - StringPiece toStringPiece() const { return StringPiece(buffer.getAlias(), len); } - - const char *data() const { return buffer.getAlias(); } - char *data() { return buffer.getAlias(); } - /** - * Allocates length()+1 chars and copies the NUL-terminated data(). - * The caller must uprv_free() the result. - */ - char *cloneData(UErrorCode &errorCode) const; - /** - * Copies the contents of the string into dest. - * Checks if there is enough space in dest, extracts the entire string if possible, - * and NUL-terminates dest if possible. - * - * If the string fits into dest but cannot be NUL-terminated (length()==capacity), - * then the error code is set to U_STRING_NOT_TERMINATED_WARNING. - * If the string itself does not fit into dest (length()>capacity), - * then the error code is set to U_BUFFER_OVERFLOW_ERROR. - * - * @param dest Destination string buffer. - * @param capacity Size of the dest buffer (number of chars). - * @param errorCode ICU error code. - * @return length() - */ - int32_t extract(char *dest, int32_t capacity, UErrorCode &errorCode) const; - - bool operator==(StringPiece other) const { - return len == other.length() && (len == 0 || uprv_memcmp(data(), other.data(), len) == 0); - } - bool operator!=(StringPiece other) const { - return !operator==(other); - } - - /** @return last index of c, or -1 if c is not in this string */ - int32_t lastIndexOf(char c) const; - - bool contains(StringPiece s) const; - - CharString &clear() { len=0; buffer[0]=0; return *this; } - CharString &truncate(int32_t newLength); - - CharString &append(char c, UErrorCode &errorCode); - CharString &append(StringPiece s, UErrorCode &errorCode) { - return append(s.data(), s.length(), errorCode); - } - CharString &append(const CharString &s, UErrorCode &errorCode) { - return append(s.data(), s.length(), errorCode); - } - CharString &append(const char *s, int32_t sLength, UErrorCode &status); - - CharString &appendNumber(int32_t number, UErrorCode &status); - - /** - * Returns a writable buffer for appending and writes the buffer's capacity to - * resultCapacity. Guarantees resultCapacity>=minCapacity if U_SUCCESS(). - * There will additionally be space for a terminating NUL right at resultCapacity. - * (This function is similar to ByteSink.GetAppendBuffer().) - * - * The returned buffer is only valid until the next write operation - * on this string. - * - * After writing at most resultCapacity bytes, call append() with the - * pointer returned from this function and the number of bytes written. - * - * @param minCapacity required minimum capacity of the returned buffer; - * must be non-negative - * @param desiredCapacityHint desired capacity of the returned buffer; - * must be non-negative - * @param resultCapacity will be set to the capacity of the returned buffer - * @param errorCode in/out error code - * @return a buffer with resultCapacity>=min_capacity - */ - char *getAppendBuffer(int32_t minCapacity, - int32_t desiredCapacityHint, - int32_t &resultCapacity, - UErrorCode &errorCode); - - CharString &appendInvariantChars(const UnicodeString &s, UErrorCode &errorCode); - CharString &appendInvariantChars(const UChar* uchars, int32_t ucharsLen, UErrorCode& errorCode); - - /** - * Appends a filename/path part, e.g., a directory name. - * First appends a U_FILE_SEP_CHAR or U_FILE_ALT_SEP_CHAR if necessary. - * Does nothing if s is empty. - */ - CharString &appendPathPart(StringPiece s, UErrorCode &errorCode); - - /** - * Appends a U_FILE_SEP_CHAR or U_FILE_ALT_SEP_CHAR if this string is not empty - * and does not already end with a U_FILE_SEP_CHAR or U_FILE_ALT_SEP_CHAR. - */ - CharString &ensureEndsWithFileSeparator(UErrorCode &errorCode); - -private: - MaybeStackArray buffer; - int32_t len; - - UBool ensureCapacity(int32_t capacity, int32_t desiredCapacityHint, UErrorCode &errorCode); - - CharString(const CharString &other) = delete; // forbid copying of this class - CharString &operator=(const CharString &other) = delete; // forbid copying of this class - - /** - * Returns U_FILE_ALT_SEP_CHAR if found in string, and U_FILE_SEP_CHAR is not found. - * Otherwise returns U_FILE_SEP_CHAR. - */ - char getDirSepChar() const; -}; - -U_NAMESPACE_END - -#endif -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2001-2015, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/19/2001 aliu Creation. +* 05/19/2010 markus Rewritten from scratch +********************************************************************** +*/ + +#ifndef CHARSTRING_H +#define CHARSTRING_H + +#include "unicode/utypes.h" +#include "unicode/unistr.h" +#include "unicode/uobject.h" +#include "cmemory.h" + +U_NAMESPACE_BEGIN + +// Windows needs us to DLL-export the MaybeStackArray template specialization, +// but MacOS X cannot handle it. Same as in digitlst.h. +#if !U_PLATFORM_IS_DARWIN_BASED +template class U_COMMON_API MaybeStackArray; +#endif + +/** + * ICU-internal char * string class. + * This class does not assume or enforce any particular character encoding. + * Raw bytes can be stored. The string object owns its characters. + * A terminating NUL is stored, but the class does not prevent embedded NUL characters. + * + * This class wants to be convenient but is also deliberately minimalist. + * Please do not add methods if they only add minor convenience. + * For example: + * cs.data()[5]='a'; // no need for setCharAt(5, 'a') + */ +class U_COMMON_API CharString : public UMemory { +public: + CharString() : len(0) { buffer[0]=0; } + CharString(StringPiece s, UErrorCode &errorCode) : len(0) { + buffer[0]=0; + append(s, errorCode); + } + CharString(const CharString &s, UErrorCode &errorCode) : len(0) { + buffer[0]=0; + append(s, errorCode); + } + CharString(const char *s, int32_t sLength, UErrorCode &errorCode) : len(0) { + buffer[0]=0; + append(s, sLength, errorCode); + } + ~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) 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) noexcept; + + /** + * Replaces this string's contents with the other string's contents. + * CharString does not support the standard copy constructor nor + * the assignment operator, to make copies explicit and to + * use a UErrorCode where memory allocations might be needed. + */ + CharString ©From(const CharString &other, UErrorCode &errorCode); + + UBool isEmpty() const { return len==0; } + int32_t length() const { return len; } + char operator[](int32_t index) const { return buffer[index]; } + StringPiece toStringPiece() const { return StringPiece(buffer.getAlias(), len); } + + const char *data() const { return buffer.getAlias(); } + char *data() { return buffer.getAlias(); } + /** + * Allocates length()+1 chars and copies the NUL-terminated data(). + * The caller must uprv_free() the result. + */ + char *cloneData(UErrorCode &errorCode) const; + /** + * Copies the contents of the string into dest. + * Checks if there is enough space in dest, extracts the entire string if possible, + * and NUL-terminates dest if possible. + * + * If the string fits into dest but cannot be NUL-terminated (length()==capacity), + * then the error code is set to U_STRING_NOT_TERMINATED_WARNING. + * If the string itself does not fit into dest (length()>capacity), + * then the error code is set to U_BUFFER_OVERFLOW_ERROR. + * + * @param dest Destination string buffer. + * @param capacity Size of the dest buffer (number of chars). + * @param errorCode ICU error code. + * @return length() + */ + int32_t extract(char *dest, int32_t capacity, UErrorCode &errorCode) const; + + bool operator==(StringPiece other) const { + return len == other.length() && (len == 0 || uprv_memcmp(data(), other.data(), len) == 0); + } + bool operator!=(StringPiece other) const { + return !operator==(other); + } + + /** @return last index of c, or -1 if c is not in this string */ + int32_t lastIndexOf(char c) const; + + bool contains(StringPiece s) const; + + CharString &clear() { len=0; buffer[0]=0; return *this; } + CharString &truncate(int32_t newLength); + + CharString &append(char c, UErrorCode &errorCode); + CharString &append(StringPiece s, UErrorCode &errorCode) { + return append(s.data(), s.length(), errorCode); + } + CharString &append(const CharString &s, UErrorCode &errorCode) { + return append(s.data(), s.length(), errorCode); + } + CharString &append(const char *s, int32_t sLength, UErrorCode &status); + + CharString &appendNumber(int32_t number, UErrorCode &status); + + /** + * Returns a writable buffer for appending and writes the buffer's capacity to + * resultCapacity. Guarantees resultCapacity>=minCapacity if U_SUCCESS(). + * There will additionally be space for a terminating NUL right at resultCapacity. + * (This function is similar to ByteSink.GetAppendBuffer().) + * + * The returned buffer is only valid until the next write operation + * on this string. + * + * After writing at most resultCapacity bytes, call append() with the + * pointer returned from this function and the number of bytes written. + * + * @param minCapacity required minimum capacity of the returned buffer; + * must be non-negative + * @param desiredCapacityHint desired capacity of the returned buffer; + * must be non-negative + * @param resultCapacity will be set to the capacity of the returned buffer + * @param errorCode in/out error code + * @return a buffer with resultCapacity>=min_capacity + */ + char *getAppendBuffer(int32_t minCapacity, + int32_t desiredCapacityHint, + int32_t &resultCapacity, + UErrorCode &errorCode); + + CharString &appendInvariantChars(const UnicodeString &s, UErrorCode &errorCode); + CharString &appendInvariantChars(const char16_t* uchars, int32_t ucharsLen, UErrorCode& errorCode); + + /** + * Appends a filename/path part, e.g., a directory name. + * First appends a U_FILE_SEP_CHAR or U_FILE_ALT_SEP_CHAR if necessary. + * Does nothing if s is empty. + */ + CharString &appendPathPart(StringPiece s, UErrorCode &errorCode); + + /** + * Appends a U_FILE_SEP_CHAR or U_FILE_ALT_SEP_CHAR if this string is not empty + * and does not already end with a U_FILE_SEP_CHAR or U_FILE_ALT_SEP_CHAR. + */ + CharString &ensureEndsWithFileSeparator(UErrorCode &errorCode); + +private: + MaybeStackArray buffer; + int32_t len; + + UBool ensureCapacity(int32_t capacity, int32_t desiredCapacityHint, UErrorCode &errorCode); + + CharString(const CharString &other) = delete; // forbid copying of this class + CharString &operator=(const CharString &other) = delete; // forbid copying of this class + + /** + * Returns U_FILE_ALT_SEP_CHAR if found in string, and U_FILE_SEP_CHAR is not found. + * Otherwise returns U_FILE_SEP_CHAR. + */ + char getDirSepChar() const; +}; + +U_NAMESPACE_END + +#endif +//eof diff --git a/deps/icu-small/source/common/charstrmap.h b/deps/icu-small/source/common/charstrmap.h index 3320a4620852ce..71b593509cb873 100644 --- a/deps/icu-small/source/common/charstrmap.h +++ b/deps/icu-small/source/common/charstrmap.h @@ -1,55 +1,55 @@ -// © 2020 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// charstrmap.h -// created: 2020sep01 Frank Yung-Fong Tang - -#ifndef __CHARSTRMAP_H__ -#define __CHARSTRMAP_H__ - -#include -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "uhash.h" - -U_NAMESPACE_BEGIN - -/** - * Map of const char * keys & values. - * Stores pointers as is: Does not own/copy/adopt/release strings. - */ -class CharStringMap final : public UMemory { -public: - /** Constructs an unusable non-map. */ - CharStringMap() : map(nullptr) {} - CharStringMap(int32_t size, UErrorCode &errorCode) { - map = uhash_openSize(uhash_hashChars, uhash_compareChars, uhash_compareChars, - size, &errorCode); - } - CharStringMap(CharStringMap &&other) U_NOEXCEPT : map(other.map) { - other.map = nullptr; - } - CharStringMap(const CharStringMap &other) = delete; - ~CharStringMap() { - uhash_close(map); - } - - CharStringMap &operator=(CharStringMap &&other) U_NOEXCEPT { - map = other.map; - other.map = nullptr; - return *this; - } - CharStringMap &operator=(const CharStringMap &other) = delete; - - const char *get(const char *key) const { return static_cast(uhash_get(map, key)); } - void put(const char *key, const char *value, UErrorCode &errorCode) { - uhash_put(map, const_cast(key), const_cast(value), &errorCode); - } - -private: - UHashtable *map; -}; - -U_NAMESPACE_END - -#endif // __CHARSTRMAP_H__ +// © 2020 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// charstrmap.h +// created: 2020sep01 Frank Yung-Fong Tang + +#ifndef __CHARSTRMAP_H__ +#define __CHARSTRMAP_H__ + +#include +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "uhash.h" + +U_NAMESPACE_BEGIN + +/** + * Map of const char * keys & values. + * Stores pointers as is: Does not own/copy/adopt/release strings. + */ +class CharStringMap final : public UMemory { +public: + /** Constructs an unusable non-map. */ + CharStringMap() : map(nullptr) {} + CharStringMap(int32_t size, UErrorCode &errorCode) { + map = uhash_openSize(uhash_hashChars, uhash_compareChars, uhash_compareChars, + size, &errorCode); + } + CharStringMap(CharStringMap &&other) noexcept : map(other.map) { + other.map = nullptr; + } + CharStringMap(const CharStringMap &other) = delete; + ~CharStringMap() { + uhash_close(map); + } + + CharStringMap &operator=(CharStringMap &&other) noexcept { + map = other.map; + other.map = nullptr; + return *this; + } + CharStringMap &operator=(const CharStringMap &other) = delete; + + const char *get(const char *key) const { return static_cast(uhash_get(map, key)); } + void put(const char *key, const char *value, UErrorCode &errorCode) { + uhash_put(map, const_cast(key), const_cast(value), &errorCode); + } + +private: + UHashtable *map; +}; + +U_NAMESPACE_END + +#endif // __CHARSTRMAP_H__ diff --git a/deps/icu-small/source/common/cmemory.cpp b/deps/icu-small/source/common/cmemory.cpp index 64f5034921fe64..58a51a0c0a4521 100644 --- a/deps/icu-small/source/common/cmemory.cpp +++ b/deps/icu-small/source/common/cmemory.cpp @@ -1,138 +1,138 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2002-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* File cmemory.c ICU Heap allocation. -* All ICU heap allocation, both for C and C++ new of ICU -* class types, comes through these functions. -* -* If you have a need to replace ICU allocation, this is the -* place to do it. -* -* Note that uprv_malloc(0) returns a non-NULL pointer, and -* that a subsequent free of that pointer value is a NOP. -* -****************************************************************************** -*/ -#include "unicode/uclean.h" -#include "cmemory.h" -#include "putilimp.h" -#include "uassert.h" -#include - -/* uprv_malloc(0) returns a pointer to this read-only data. */ -static const int32_t zeroMem[] = {0, 0, 0, 0, 0, 0}; - -/* Function Pointers for user-supplied heap functions */ -static const void *pContext; -static UMemAllocFn *pAlloc; -static UMemReallocFn *pRealloc; -static UMemFreeFn *pFree; - -#if U_DEBUG && defined(UPRV_MALLOC_COUNT) -#include -static int n=0; -static long b=0; -#endif - -U_CAPI void * U_EXPORT2 -uprv_malloc(size_t s) { -#if U_DEBUG && defined(UPRV_MALLOC_COUNT) -#if 1 - putchar('>'); - fflush(stdout); -#else - fprintf(stderr,"MALLOC\t#%d\t%ul bytes\t%ul total\n", ++n,s,(b+=s)); fflush(stderr); -#endif -#endif - if (s > 0) { - if (pAlloc) { - return (*pAlloc)(pContext, s); - } else { - return uprv_default_malloc(s); - } - } else { - return (void *)zeroMem; - } -} - -U_CAPI void * U_EXPORT2 -uprv_realloc(void * buffer, size_t size) { -#if U_DEBUG && defined(UPRV_MALLOC_COUNT) - putchar('~'); - fflush(stdout); -#endif - if (buffer == zeroMem) { - return uprv_malloc(size); - } else if (size == 0) { - if (pFree) { - (*pFree)(pContext, buffer); - } else { - uprv_default_free(buffer); - } - return (void *)zeroMem; - } else { - if (pRealloc) { - return (*pRealloc)(pContext, buffer, size); - } else { - return uprv_default_realloc(buffer, size); - } - } -} - -U_CAPI void U_EXPORT2 -uprv_free(void *buffer) { -#if U_DEBUG && defined(UPRV_MALLOC_COUNT) - putchar('<'); - fflush(stdout); -#endif - if (buffer != zeroMem) { - if (pFree) { - (*pFree)(pContext, buffer); - } else { - uprv_default_free(buffer); - } - } -} - -U_CAPI void * U_EXPORT2 -uprv_calloc(size_t num, size_t size) { - void *mem = NULL; - size *= num; - mem = uprv_malloc(size); - if (mem) { - uprv_memset(mem, 0, size); - } - return mem; -} - -U_CAPI void U_EXPORT2 -u_setMemoryFunctions(const void *context, UMemAllocFn *a, UMemReallocFn *r, UMemFreeFn *f, UErrorCode *status) -{ - if (U_FAILURE(*status)) { - return; - } - if (a==NULL || r==NULL || f==NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - pContext = context; - pAlloc = a; - pRealloc = r; - pFree = f; -} - - -U_CFUNC UBool cmemory_cleanup(void) { - pContext = NULL; - pAlloc = NULL; - pRealloc = NULL; - pFree = NULL; - return true; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2002-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* File cmemory.c ICU Heap allocation. +* All ICU heap allocation, both for C and C++ new of ICU +* class types, comes through these functions. +* +* If you have a need to replace ICU allocation, this is the +* place to do it. +* +* Note that uprv_malloc(0) returns a non-nullptr pointer, +* and that a subsequent free of that pointer value is a NOP. +* +****************************************************************************** +*/ +#include "unicode/uclean.h" +#include "cmemory.h" +#include "putilimp.h" +#include "uassert.h" +#include + +/* uprv_malloc(0) returns a pointer to this read-only data. */ +static const int32_t zeroMem[] = {0, 0, 0, 0, 0, 0}; + +/* Function Pointers for user-supplied heap functions */ +static const void *pContext; +static UMemAllocFn *pAlloc; +static UMemReallocFn *pRealloc; +static UMemFreeFn *pFree; + +#if U_DEBUG && defined(UPRV_MALLOC_COUNT) +#include +static int n=0; +static long b=0; +#endif + +U_CAPI void * U_EXPORT2 +uprv_malloc(size_t s) { +#if U_DEBUG && defined(UPRV_MALLOC_COUNT) +#if 1 + putchar('>'); + fflush(stdout); +#else + fprintf(stderr,"MALLOC\t#%d\t%ul bytes\t%ul total\n", ++n,s,(b+=s)); fflush(stderr); +#endif +#endif + if (s > 0) { + if (pAlloc) { + return (*pAlloc)(pContext, s); + } else { + return uprv_default_malloc(s); + } + } else { + return (void *)zeroMem; + } +} + +U_CAPI void * U_EXPORT2 +uprv_realloc(void * buffer, size_t size) { +#if U_DEBUG && defined(UPRV_MALLOC_COUNT) + putchar('~'); + fflush(stdout); +#endif + if (buffer == zeroMem) { + return uprv_malloc(size); + } else if (size == 0) { + if (pFree) { + (*pFree)(pContext, buffer); + } else { + uprv_default_free(buffer); + } + return (void *)zeroMem; + } else { + if (pRealloc) { + return (*pRealloc)(pContext, buffer, size); + } else { + return uprv_default_realloc(buffer, size); + } + } +} + +U_CAPI void U_EXPORT2 +uprv_free(void *buffer) { +#if U_DEBUG && defined(UPRV_MALLOC_COUNT) + putchar('<'); + fflush(stdout); +#endif + if (buffer != zeroMem) { + if (pFree) { + (*pFree)(pContext, buffer); + } else { + uprv_default_free(buffer); + } + } +} + +U_CAPI void * U_EXPORT2 +uprv_calloc(size_t num, size_t size) { + void *mem = nullptr; + size *= num; + mem = uprv_malloc(size); + if (mem) { + uprv_memset(mem, 0, size); + } + return mem; +} + +U_CAPI void U_EXPORT2 +u_setMemoryFunctions(const void *context, UMemAllocFn *a, UMemReallocFn *r, UMemFreeFn *f, UErrorCode *status) +{ + if (U_FAILURE(*status)) { + return; + } + if (a==nullptr || r==nullptr || f==nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + pContext = context; + pAlloc = a; + pRealloc = r; + pFree = f; +} + + +U_CFUNC UBool cmemory_cleanup() { + pContext = nullptr; + pAlloc = nullptr; + pRealloc = nullptr; + pFree = nullptr; + return true; +} diff --git a/deps/icu-small/source/common/cmemory.h b/deps/icu-small/source/common/cmemory.h index f03b7dcce6b9ab..6f5d70dc65828d 100644 --- a/deps/icu-small/source/common/cmemory.h +++ b/deps/icu-small/source/common/cmemory.h @@ -1,900 +1,900 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1997-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* File CMEMORY.H -* -* Contains stdlib.h/string.h memory functions -* -* @author Bertrand A. Damiba -* -* Modification History: -* -* Date Name Description -* 6/20/98 Bertrand Created. -* 05/03/99 stephen Changed from functions to macros. -* -****************************************************************************** -*/ - -#ifndef CMEMORY_H -#define CMEMORY_H - -#include "unicode/utypes.h" - -#include -#include -#include "unicode/localpointer.h" -#include "uassert.h" - -#if U_DEBUG && defined(UPRV_MALLOC_COUNT) -#include -#endif - -// uprv_memcpy and uprv_memmove -#if defined(__clang__) -#define uprv_memcpy(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ - /* Suppress warnings about addresses that will never be NULL */ \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Waddress\"") \ - U_ASSERT(dst != NULL); \ - U_ASSERT(src != NULL); \ - _Pragma("clang diagnostic pop") \ - U_STANDARD_CPP_NAMESPACE memcpy(dst, src, size); \ -} UPRV_BLOCK_MACRO_END -#define uprv_memmove(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ - /* Suppress warnings about addresses that will never be NULL */ \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Waddress\"") \ - U_ASSERT(dst != NULL); \ - U_ASSERT(src != NULL); \ - _Pragma("clang diagnostic pop") \ - U_STANDARD_CPP_NAMESPACE memmove(dst, src, size); \ -} UPRV_BLOCK_MACRO_END -#elif defined(__GNUC__) -#define uprv_memcpy(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ - /* Suppress warnings about addresses that will never be NULL */ \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Waddress\"") \ - U_ASSERT(dst != NULL); \ - U_ASSERT(src != NULL); \ - _Pragma("GCC diagnostic pop") \ - U_STANDARD_CPP_NAMESPACE memcpy(dst, src, size); \ -} UPRV_BLOCK_MACRO_END -#define uprv_memmove(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ - /* Suppress warnings about addresses that will never be NULL */ \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Waddress\"") \ - U_ASSERT(dst != NULL); \ - U_ASSERT(src != NULL); \ - _Pragma("GCC diagnostic pop") \ - U_STANDARD_CPP_NAMESPACE memmove(dst, src, size); \ -} UPRV_BLOCK_MACRO_END -#else -#define uprv_memcpy(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ - U_ASSERT(dst != NULL); \ - U_ASSERT(src != NULL); \ - U_STANDARD_CPP_NAMESPACE memcpy(dst, src, size); \ -} UPRV_BLOCK_MACRO_END -#define uprv_memmove(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ - U_ASSERT(dst != NULL); \ - U_ASSERT(src != NULL); \ - U_STANDARD_CPP_NAMESPACE memmove(dst, src, size); \ -} UPRV_BLOCK_MACRO_END -#endif - -/** - * \def UPRV_LENGTHOF - * Convenience macro to determine the length of a fixed array at compile-time. - * @param array A fixed length array - * @return The length of the array, in elements - * @internal - */ -#define UPRV_LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) -#define uprv_memset(buffer, mark, size) U_STANDARD_CPP_NAMESPACE memset(buffer, mark, size) -#define uprv_memcmp(buffer1, buffer2, size) U_STANDARD_CPP_NAMESPACE memcmp(buffer1, buffer2,size) -#define uprv_memchr(ptr, value, num) U_STANDARD_CPP_NAMESPACE memchr(ptr, value, num) - -U_CAPI void * U_EXPORT2 -uprv_malloc(size_t s) U_MALLOC_ATTR U_ALLOC_SIZE_ATTR(1); - -U_CAPI void * U_EXPORT2 -uprv_realloc(void *mem, size_t size) U_ALLOC_SIZE_ATTR(2); - -U_CAPI void U_EXPORT2 -uprv_free(void *mem); - -U_CAPI void * U_EXPORT2 -uprv_calloc(size_t num, size_t size) U_MALLOC_ATTR U_ALLOC_SIZE_ATTR2(1,2); - -/** - * Get the least significant bits of a pointer (a memory address). - * For example, with a mask of 3, the macro gets the 2 least significant bits, - * which will be 0 if the pointer is 32-bit (4-byte) aligned. - * - * uintptr_t is the most appropriate integer type to cast to. - */ -#define U_POINTER_MASK_LSB(ptr, mask) ((uintptr_t)(ptr) & (mask)) - -/** - * Create & return an instance of "type" in statically allocated storage. - * e.g. - * static std::mutex *myMutex = STATIC_NEW(std::mutex); - * To destroy an object created in this way, invoke the destructor explicitly, e.g. - * myMutex->~mutex(); - * DO NOT use delete. - * DO NOT use with class UMutex, which has specific support for static instances. - * - * STATIC_NEW is intended for use when - * - We want a static (or global) object. - * - We don't want it to ever be destructed, or to explicitly control destruction, - * to avoid use-after-destruction problems. - * - We want to avoid an ordinary heap allocated object, - * to avoid the possibility of memory allocation failures, and - * to avoid memory leak reports, from valgrind, for example. - * This is defined as a macro rather than a template function because each invocation - * must define distinct static storage for the object being returned. - */ -#define STATIC_NEW(type) [] () { \ - alignas(type) static char storage[sizeof(type)]; \ - return new(storage) type();} () - -/** - * Heap clean up function, called from u_cleanup() - * Clears any user heap functions from u_setMemoryFunctions() - * Does NOT deallocate any remaining allocated memory. - */ -U_CFUNC UBool -cmemory_cleanup(void); - -/** - * A function called by uhash_remove, - * uhash_close, or uhash_put to delete - * an existing key or value. - * @param obj A key or value stored in a hashtable - * @see uprv_deleteUObject - */ -typedef void U_CALLCONV UObjectDeleter(void* obj); - -/** - * Deleter for UObject instances. - * Works for all subclasses of UObject because it has a virtual destructor. - */ -U_CAPI void U_EXPORT2 -uprv_deleteUObject(void *obj); - -#ifdef __cplusplus - -#include -#include "unicode/uobject.h" - -U_NAMESPACE_BEGIN - -/** - * "Smart pointer" class, deletes memory via uprv_free(). - * For most methods see the LocalPointerBase base class. - * Adds operator[] for array item access. - * - * @see LocalPointerBase - */ -template -class LocalMemory : public LocalPointerBase { -public: - using LocalPointerBase::operator*; - using LocalPointerBase::operator->; - /** - * Constructor takes ownership. - * @param p simple pointer to an array of T items that is adopted - */ - explicit LocalMemory(T *p=NULL) : LocalPointerBase(p) {} - /** - * Move constructor, leaves src with isNull(). - * @param src source smart pointer - */ - LocalMemory(LocalMemory &&src) U_NOEXCEPT : LocalPointerBase(src.ptr) { - src.ptr=NULL; - } - /** - * Destructor deletes the memory it owns. - */ - ~LocalMemory() { - uprv_free(LocalPointerBase::ptr); - } - /** - * Move assignment operator, leaves src with isNull(). - * The behavior is undefined if *this and src are the same object. - * @param src source smart pointer - * @return *this - */ - LocalMemory &operator=(LocalMemory &&src) U_NOEXCEPT { - uprv_free(LocalPointerBase::ptr); - LocalPointerBase::ptr=src.ptr; - src.ptr=NULL; - return *this; - } - /** - * Swap pointers. - * @param other other smart pointer - */ - void swap(LocalMemory &other) U_NOEXCEPT { - T *temp=LocalPointerBase::ptr; - LocalPointerBase::ptr=other.ptr; - other.ptr=temp; - } - /** - * Non-member LocalMemory swap function. - * @param p1 will get p2's pointer - * @param p2 will get p1's pointer - */ - friend inline void swap(LocalMemory &p1, LocalMemory &p2) U_NOEXCEPT { - p1.swap(p2); - } - /** - * Deletes the array it owns, - * and adopts (takes ownership of) the one passed in. - * @param p simple pointer to an array of T items that is adopted - */ - void adoptInstead(T *p) { - uprv_free(LocalPointerBase::ptr); - LocalPointerBase::ptr=p; - } - /** - * Deletes the array it owns, allocates a new one and reset its bytes to 0. - * Returns the new array pointer. - * If the allocation fails, then the current array is unchanged and - * this method returns NULL. - * @param newCapacity must be >0 - * @return the allocated array pointer, or NULL if the allocation failed - */ - inline T *allocateInsteadAndReset(int32_t newCapacity=1); - /** - * Deletes the array it owns and allocates a new one, copying length T items. - * Returns the new array pointer. - * If the allocation fails, then the current array is unchanged and - * this method returns NULL. - * @param newCapacity must be >0 - * @param length number of T items to be copied from the old array to the new one; - * must be no more than the capacity of the old array, - * which the caller must track because the LocalMemory does not track it - * @return the allocated array pointer, or NULL if the allocation failed - */ - inline T *allocateInsteadAndCopy(int32_t newCapacity=1, int32_t length=0); - /** - * Array item access (writable). - * No index bounds check. - * @param i array index - * @return reference to the array item - */ - T &operator[](ptrdiff_t i) const { return LocalPointerBase::ptr[i]; } -}; - -template -inline T *LocalMemory::allocateInsteadAndReset(int32_t newCapacity) { - if(newCapacity>0) { - T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); - if(p!=NULL) { - uprv_memset(p, 0, newCapacity*sizeof(T)); - uprv_free(LocalPointerBase::ptr); - LocalPointerBase::ptr=p; - } - return p; - } else { - return NULL; - } -} - - -template -inline T *LocalMemory::allocateInsteadAndCopy(int32_t newCapacity, int32_t length) { - if(newCapacity>0) { - T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); - if(p!=NULL) { - if(length>0) { - if(length>newCapacity) { - length=newCapacity; - } - uprv_memcpy(p, LocalPointerBase::ptr, (size_t)length*sizeof(T)); - } - uprv_free(LocalPointerBase::ptr); - LocalPointerBase::ptr=p; - } - return p; - } else { - return NULL; - } -} - -/** - * Simple array/buffer management class using uprv_malloc() and uprv_free(). - * Provides an internal array with fixed capacity. Can alias another array - * or allocate one. - * - * The array address is properly aligned for type T. It might not be properly - * aligned for types larger than T (or larger than the largest subtype of T). - * - * Unlike LocalMemory and LocalArray, this class never adopts - * (takes ownership of) another array. - * - * WARNING: MaybeStackArray only works with primitive (plain-old data) types. - * It does NOT know how to call a destructor! If you work with classes with - * destructors, consider: - * - * - LocalArray in localpointer.h if you know the length ahead of time - * - MaybeStackVector if you know the length at runtime - */ -template -class MaybeStackArray { -public: - // No heap allocation. Use only on the stack. - static void* U_EXPORT2 operator new(size_t) U_NOEXCEPT = delete; - static void* U_EXPORT2 operator new[](size_t) U_NOEXCEPT = delete; -#if U_HAVE_PLACEMENT_NEW - static void* U_EXPORT2 operator new(size_t, void*) U_NOEXCEPT = delete; -#endif - - /** - * Default constructor initializes with internal T[stackCapacity] buffer. - */ - MaybeStackArray() : ptr(stackArray), capacity(stackCapacity), needToRelease(false) {} - /** - * Automatically allocates the heap array if the argument is larger than the stack capacity. - * Intended for use when an approximate capacity is known at compile time but the true - * capacity is not known until runtime. - */ - MaybeStackArray(int32_t newCapacity, UErrorCode status) : MaybeStackArray() { - if (U_FAILURE(status)) { - return; - } - if (capacity < newCapacity) { - if (resize(newCapacity) == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - } - } - /** - * 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 - */ - int32_t getCapacity() const { return capacity; } - /** - * Access without ownership change. - * @return the array pointer - */ - T *getAlias() const { return ptr; } - /** - * Returns the array limit. Simple convenience method. - * @return getAlias()+getCapacity() - */ - T *getArrayLimit() const { return getAlias()+capacity; } - // No "operator T *() const" because that can make - // expressions like mbs[index] ambiguous for some compilers. - /** - * Array item access (const). - * No index bounds check. - * @param i array index - * @return reference to the array item - */ - const T &operator[](ptrdiff_t i) const { return ptr[i]; } - /** - * Array item access (writable). - * No index bounds check. - * @param i array index - * @return reference to the array item - */ - T &operator[](ptrdiff_t i) { return ptr[i]; } - /** - * Deletes the array (if owned) and aliases another one, no transfer of ownership. - * If the arguments are illegal, then the current array is unchanged. - * @param otherArray must not be NULL - * @param otherCapacity must be >0 - */ - void aliasInstead(T *otherArray, int32_t otherCapacity) { - if(otherArray!=NULL && otherCapacity>0) { - releaseArray(); - ptr=otherArray; - capacity=otherCapacity; - needToRelease=false; - } - } - /** - * Deletes the array (if owned) and allocates a new one, copying length T items. - * Returns the new array pointer. - * If the allocation fails, then the current array is unchanged and - * this method returns NULL. - * @param newCapacity can be less than or greater than the current capacity; - * must be >0 - * @param length number of T items to be copied from the old array to the new one - * @return the allocated array pointer, or NULL if the allocation failed - */ - inline T *resize(int32_t newCapacity, int32_t length=0); - /** - * Gives up ownership of the array if owned, or else clones it, - * copying length T items; resets itself to the internal stack array. - * Returns NULL if the allocation failed. - * @param length number of T items to copy when cloning, - * and capacity of the clone when cloning - * @param resultCapacity will be set to the returned array's capacity (output-only) - * @return the array pointer; - * caller becomes responsible for deleting the array - */ - inline T *orphanOrClone(int32_t length, int32_t &resultCapacity); - -protected: - // Resizes the array to the size of src, then copies the contents of src. - void copyFrom(const MaybeStackArray &src, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if (this->resize(src.capacity, 0) == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_memcpy(this->ptr, src.ptr, (size_t)capacity * sizeof(T)); - } - -private: - T *ptr; - int32_t capacity; - UBool needToRelease; - T stackArray[stackCapacity]; - void releaseArray() { - if(needToRelease) { - uprv_free(ptr); - } - } - void resetToStackArray() { - ptr=stackArray; - capacity=stackCapacity; - needToRelease=false; - } - /* No comparison operators with other MaybeStackArray's. */ - bool operator==(const MaybeStackArray & /*other*/) = delete; - bool operator!=(const MaybeStackArray & /*other*/) = delete; - /* No ownership transfer: No copy constructor, no assignment operator. */ - MaybeStackArray(const MaybeStackArray & /*other*/) = delete; - void operator=(const MaybeStackArray & /*other*/) = delete; -}; - -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) { -#if U_DEBUG && defined(UPRV_MALLOC_COUNT) - ::fprintf(::stderr, "MaybeStackArray (resize) alloc %d * %lu\n", newCapacity, sizeof(T)); -#endif - T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); - if(p!=NULL) { - if(length>0) { - if(length>capacity) { - length=capacity; - } - if(length>newCapacity) { - length=newCapacity; - } - uprv_memcpy(p, ptr, (size_t)length*sizeof(T)); - } - releaseArray(); - ptr=p; - capacity=newCapacity; - needToRelease=true; - } - return p; - } else { - return NULL; - } -} - -template -inline T *MaybeStackArray::orphanOrClone(int32_t length, int32_t &resultCapacity) { - T *p; - if(needToRelease) { - p=ptr; - } else if(length<=0) { - return NULL; - } else { - if(length>capacity) { - length=capacity; - } - p=(T *)uprv_malloc(length*sizeof(T)); -#if U_DEBUG && defined(UPRV_MALLOC_COUNT) - ::fprintf(::stderr,"MaybeStacArray (orphan) alloc %d * %lu\n", length,sizeof(T)); -#endif - if(p==NULL) { - return NULL; - } - uprv_memcpy(p, ptr, (size_t)length*sizeof(T)); - } - resultCapacity=length; - resetToStackArray(); - return p; -} - -/** - * Variant of MaybeStackArray that allocates a header struct and an array - * in one contiguous memory block, using uprv_malloc() and uprv_free(). - * Provides internal memory with fixed array capacity. Can alias another memory - * block or allocate one. - * The stackCapacity is the number of T items in the internal memory, - * not counting the H header. - * Unlike LocalMemory and LocalArray, this class never adopts - * (takes ownership of) another memory block. - */ -template -class MaybeStackHeaderAndArray { -public: - // No heap allocation. Use only on the stack. - static void* U_EXPORT2 operator new(size_t) U_NOEXCEPT = delete; - static void* U_EXPORT2 operator new[](size_t) U_NOEXCEPT = delete; -#if U_HAVE_PLACEMENT_NEW - static void* U_EXPORT2 operator new(size_t, void*) U_NOEXCEPT = delete; -#endif - - /** - * Default constructor initializes with internal H+T[stackCapacity] buffer. - */ - MaybeStackHeaderAndArray() : ptr(&stackHeader), capacity(stackCapacity), needToRelease(false) {} - /** - * Destructor deletes the memory (if owned). - */ - ~MaybeStackHeaderAndArray() { releaseMemory(); } - /** - * Returns the array capacity (number of T items). - * @return array capacity - */ - int32_t getCapacity() const { return capacity; } - /** - * Access without ownership change. - * @return the header pointer - */ - H *getAlias() const { return ptr; } - /** - * Returns the array start. - * @return array start, same address as getAlias()+1 - */ - T *getArrayStart() const { return reinterpret_cast(getAlias()+1); } - /** - * Returns the array limit. - * @return array limit - */ - T *getArrayLimit() const { return getArrayStart()+capacity; } - /** - * Access without ownership change. Same as getAlias(). - * A class instance can be used directly in expressions that take a T *. - * @return the header pointer - */ - operator H *() const { return ptr; } - /** - * Array item access (writable). - * No index bounds check. - * @param i array index - * @return reference to the array item - */ - T &operator[](ptrdiff_t i) { return getArrayStart()[i]; } - /** - * Deletes the memory block (if owned) and aliases another one, no transfer of ownership. - * If the arguments are illegal, then the current memory is unchanged. - * @param otherArray must not be NULL - * @param otherCapacity must be >0 - */ - void aliasInstead(H *otherMemory, int32_t otherCapacity) { - if(otherMemory!=NULL && otherCapacity>0) { - releaseMemory(); - ptr=otherMemory; - capacity=otherCapacity; - needToRelease=false; - } - } - /** - * Deletes the memory block (if owned) and allocates a new one, - * copying the header and length T array items. - * Returns the new header pointer. - * If the allocation fails, then the current memory is unchanged and - * this method returns NULL. - * @param newCapacity can be less than or greater than the current capacity; - * must be >0 - * @param length number of T items to be copied from the old array to the new one - * @return the allocated pointer, or NULL if the allocation failed - */ - inline H *resize(int32_t newCapacity, int32_t length=0); - /** - * Gives up ownership of the memory if owned, or else clones it, - * copying the header and length T array items; resets itself to the internal memory. - * Returns NULL if the allocation failed. - * @param length number of T items to copy when cloning, - * and array capacity of the clone when cloning - * @param resultCapacity will be set to the returned array's capacity (output-only) - * @return the header pointer; - * caller becomes responsible for deleting the array - */ - inline H *orphanOrClone(int32_t length, int32_t &resultCapacity); -private: - H *ptr; - int32_t capacity; - UBool needToRelease; - // stackHeader must precede stackArray immediately. - H stackHeader; - T stackArray[stackCapacity]; - void releaseMemory() { - if(needToRelease) { - uprv_free(ptr); - } - } - /* No comparison operators with other MaybeStackHeaderAndArray's. */ - bool operator==(const MaybeStackHeaderAndArray & /*other*/) {return false;} - bool operator!=(const MaybeStackHeaderAndArray & /*other*/) {return true;} - /* No ownership transfer: No copy constructor, no assignment operator. */ - MaybeStackHeaderAndArray(const MaybeStackHeaderAndArray & /*other*/) {} - void operator=(const MaybeStackHeaderAndArray & /*other*/) {} -}; - -template -inline H *MaybeStackHeaderAndArray::resize(int32_t newCapacity, - int32_t length) { - if(newCapacity>=0) { -#if U_DEBUG && defined(UPRV_MALLOC_COUNT) - ::fprintf(::stderr,"MaybeStackHeaderAndArray alloc %d + %d * %ul\n", sizeof(H),newCapacity,sizeof(T)); -#endif - H *p=(H *)uprv_malloc(sizeof(H)+newCapacity*sizeof(T)); - if(p!=NULL) { - if(length<0) { - length=0; - } else if(length>0) { - if(length>capacity) { - length=capacity; - } - if(length>newCapacity) { - length=newCapacity; - } - } - uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T)); - releaseMemory(); - ptr=p; - capacity=newCapacity; - needToRelease=true; - } - return p; - } else { - return NULL; - } -} - -template -inline H *MaybeStackHeaderAndArray::orphanOrClone(int32_t length, - int32_t &resultCapacity) { - H *p; - if(needToRelease) { - p=ptr; - } else { - if(length<0) { - length=0; - } else if(length>capacity) { - length=capacity; - } -#if U_DEBUG && defined(UPRV_MALLOC_COUNT) - ::fprintf(::stderr,"MaybeStackHeaderAndArray (orphan) alloc %ul + %d * %lu\n", sizeof(H),length,sizeof(T)); -#endif - p=(H *)uprv_malloc(sizeof(H)+length*sizeof(T)); - if(p==NULL) { - return NULL; - } - uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T)); - } - resultCapacity=length; - ptr=&stackHeader; - capacity=stackCapacity; - needToRelease=false; - return p; -} - -/** - * A simple memory management class that creates new heap allocated objects (of - * any class that has a public constructor), keeps track of them and eventually - * deletes them all in its own destructor. - * - * A typical use-case would be code like this: - * - * MemoryPool pool; - * - * MyType* o1 = pool.create(); - * if (o1 != nullptr) { - * foo(o1); - * } - * - * MyType* o2 = pool.create(1, 2, 3); - * if (o2 != nullptr) { - * bar(o2); - * } - * - * // MemoryPool will take care of deleting the MyType objects. - * - * It doesn't do anything more than that, and is intentionally kept minimalist. - */ -template -class MemoryPool : public UMemory { -public: - MemoryPool() : fCount(0), fPool() {} - - ~MemoryPool() { - for (int32_t i = 0; i < fCount; ++i) { - delete fPool[i]; - } - } - - MemoryPool(const MemoryPool&) = delete; - MemoryPool& operator=(const MemoryPool&) = delete; - - MemoryPool(MemoryPool&& other) U_NOEXCEPT : fCount(other.fCount), - fPool(std::move(other.fPool)) { - other.fCount = 0; - } - - MemoryPool& operator=(MemoryPool&& other) U_NOEXCEPT { - // Since `this` may contain instances that need to be deleted, we can't - // just throw them away and replace them with `other`. The normal way of - // dealing with this in C++ is to swap `this` and `other`, rather than - // simply overwrite: the destruction of `other` can then take care of - // running MemoryPool::~MemoryPool() over the still-to-be-deallocated - // instances. - std::swap(fCount, other.fCount); - std::swap(fPool, other.fPool); - return *this; - } - - /** - * Creates a new object of typename T, by forwarding any and all arguments - * to the typename T constructor. - * - * @param args Arguments to be forwarded to the typename T constructor. - * @return A pointer to the newly created object, or nullptr on error. - */ - template - T* create(Args&&... args) { - int32_t capacity = fPool.getCapacity(); - if (fCount == capacity && - fPool.resize(capacity == stackCapacity ? 4 * capacity : 2 * capacity, - capacity) == nullptr) { - return nullptr; - } - return fPool[fCount++] = new T(std::forward(args)...); - } - - template - T* createAndCheckErrorCode(UErrorCode &status, Args &&... args) { - if (U_FAILURE(status)) { - return nullptr; - } - T *pointer = this->create(args...); - if (U_SUCCESS(status) && pointer == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return pointer; - } - - /** - * @return Number of elements that have been allocated. - */ - int32_t count() const { - return fCount; - } - -protected: - int32_t fCount; - MaybeStackArray fPool; -}; - -/** - * An internal Vector-like implementation based on MemoryPool. - * - * Heap-allocates each element and stores pointers. - * - * To append an item to the vector, use emplaceBack. - * - * MaybeStackVector vector; - * MyType* element = vector.emplaceBack(); - * if (!element) { - * status = U_MEMORY_ALLOCATION_ERROR; - * } - * // do stuff with element - * - * To loop over the vector, use a for loop with indices: - * - * for (int32_t i = 0; i < vector.length(); i++) { - * MyType* element = vector[i]; - * } - */ -template -class MaybeStackVector : protected MemoryPool { -public: - template - T* emplaceBack(Args&&... args) { - return this->create(args...); - } - - template - T *emplaceBackAndCheckErrorCode(UErrorCode &status, Args &&... args) { - return this->createAndCheckErrorCode(status, args...); - } - - int32_t length() const { - return this->fCount; - } - - T** getAlias() { - return this->fPool.getAlias(); - } - - const T *const *getAlias() const { - return this->fPool.getAlias(); - } - - /** - * Array item access (read-only). - * No index bounds check. - * @param i array index - * @return reference to the array item - */ - const T* operator[](ptrdiff_t i) const { - return this->fPool[i]; - } - - /** - * Array item access (writable). - * No index bounds check. - * @param i array index - * @return reference to the array item - */ - T* operator[](ptrdiff_t i) { - return this->fPool[i]; - } -}; - - -U_NAMESPACE_END - -#endif /* __cplusplus */ -#endif /* CMEMORY_H */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1997-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* File CMEMORY.H +* +* Contains stdlib.h/string.h memory functions +* +* @author Bertrand A. Damiba +* +* Modification History: +* +* Date Name Description +* 6/20/98 Bertrand Created. +* 05/03/99 stephen Changed from functions to macros. +* +****************************************************************************** +*/ + +#ifndef CMEMORY_H +#define CMEMORY_H + +#include "unicode/utypes.h" + +#include +#include +#include "unicode/localpointer.h" +#include "uassert.h" + +#if U_DEBUG && defined(UPRV_MALLOC_COUNT) +#include +#endif + +// uprv_memcpy and uprv_memmove +#if defined(__clang__) +#define uprv_memcpy(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ + /* Suppress warnings about addresses that will never be NULL */ \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Waddress\"") \ + U_ASSERT(dst != NULL); \ + U_ASSERT(src != NULL); \ + _Pragma("clang diagnostic pop") \ + U_STANDARD_CPP_NAMESPACE memcpy(dst, src, size); \ +} UPRV_BLOCK_MACRO_END +#define uprv_memmove(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ + /* Suppress warnings about addresses that will never be NULL */ \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Waddress\"") \ + U_ASSERT(dst != NULL); \ + U_ASSERT(src != NULL); \ + _Pragma("clang diagnostic pop") \ + U_STANDARD_CPP_NAMESPACE memmove(dst, src, size); \ +} UPRV_BLOCK_MACRO_END +#elif defined(__GNUC__) +#define uprv_memcpy(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ + /* Suppress warnings about addresses that will never be NULL */ \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Waddress\"") \ + U_ASSERT(dst != NULL); \ + U_ASSERT(src != NULL); \ + _Pragma("GCC diagnostic pop") \ + U_STANDARD_CPP_NAMESPACE memcpy(dst, src, size); \ +} UPRV_BLOCK_MACRO_END +#define uprv_memmove(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ + /* Suppress warnings about addresses that will never be NULL */ \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Waddress\"") \ + U_ASSERT(dst != NULL); \ + U_ASSERT(src != NULL); \ + _Pragma("GCC diagnostic pop") \ + U_STANDARD_CPP_NAMESPACE memmove(dst, src, size); \ +} UPRV_BLOCK_MACRO_END +#else +#define uprv_memcpy(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ + U_ASSERT(dst != NULL); \ + U_ASSERT(src != NULL); \ + U_STANDARD_CPP_NAMESPACE memcpy(dst, src, size); \ +} UPRV_BLOCK_MACRO_END +#define uprv_memmove(dst, src, size) UPRV_BLOCK_MACRO_BEGIN { \ + U_ASSERT(dst != NULL); \ + U_ASSERT(src != NULL); \ + U_STANDARD_CPP_NAMESPACE memmove(dst, src, size); \ +} UPRV_BLOCK_MACRO_END +#endif + +/** + * \def UPRV_LENGTHOF + * Convenience macro to determine the length of a fixed array at compile-time. + * @param array A fixed length array + * @return The length of the array, in elements + * @internal + */ +#define UPRV_LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) +#define uprv_memset(buffer, mark, size) U_STANDARD_CPP_NAMESPACE memset(buffer, mark, size) +#define uprv_memcmp(buffer1, buffer2, size) U_STANDARD_CPP_NAMESPACE memcmp(buffer1, buffer2,size) +#define uprv_memchr(ptr, value, num) U_STANDARD_CPP_NAMESPACE memchr(ptr, value, num) + +U_CAPI void * U_EXPORT2 +uprv_malloc(size_t s) U_MALLOC_ATTR U_ALLOC_SIZE_ATTR(1); + +U_CAPI void * U_EXPORT2 +uprv_realloc(void *mem, size_t size) U_ALLOC_SIZE_ATTR(2); + +U_CAPI void U_EXPORT2 +uprv_free(void *mem); + +U_CAPI void * U_EXPORT2 +uprv_calloc(size_t num, size_t size) U_MALLOC_ATTR U_ALLOC_SIZE_ATTR2(1,2); + +/** + * Get the least significant bits of a pointer (a memory address). + * For example, with a mask of 3, the macro gets the 2 least significant bits, + * which will be 0 if the pointer is 32-bit (4-byte) aligned. + * + * uintptr_t is the most appropriate integer type to cast to. + */ +#define U_POINTER_MASK_LSB(ptr, mask) ((uintptr_t)(ptr) & (mask)) + +/** + * Create & return an instance of "type" in statically allocated storage. + * e.g. + * static std::mutex *myMutex = STATIC_NEW(std::mutex); + * To destroy an object created in this way, invoke the destructor explicitly, e.g. + * myMutex->~mutex(); + * DO NOT use delete. + * DO NOT use with class UMutex, which has specific support for static instances. + * + * STATIC_NEW is intended for use when + * - We want a static (or global) object. + * - We don't want it to ever be destructed, or to explicitly control destruction, + * to avoid use-after-destruction problems. + * - We want to avoid an ordinary heap allocated object, + * to avoid the possibility of memory allocation failures, and + * to avoid memory leak reports, from valgrind, for example. + * This is defined as a macro rather than a template function because each invocation + * must define distinct static storage for the object being returned. + */ +#define STATIC_NEW(type) [] () { \ + alignas(type) static char storage[sizeof(type)]; \ + return new(storage) type();} () + +/** + * Heap clean up function, called from u_cleanup() + * Clears any user heap functions from u_setMemoryFunctions() + * Does NOT deallocate any remaining allocated memory. + */ +U_CFUNC UBool +cmemory_cleanup(void); + +/** + * A function called by uhash_remove, + * uhash_close, or uhash_put to delete + * an existing key or value. + * @param obj A key or value stored in a hashtable + * @see uprv_deleteUObject + */ +typedef void U_CALLCONV UObjectDeleter(void* obj); + +/** + * Deleter for UObject instances. + * Works for all subclasses of UObject because it has a virtual destructor. + */ +U_CAPI void U_EXPORT2 +uprv_deleteUObject(void *obj); + +#ifdef __cplusplus + +#include +#include "unicode/uobject.h" + +U_NAMESPACE_BEGIN + +/** + * "Smart pointer" class, deletes memory via uprv_free(). + * For most methods see the LocalPointerBase base class. + * Adds operator[] for array item access. + * + * @see LocalPointerBase + */ +template +class LocalMemory : public LocalPointerBase { +public: + using LocalPointerBase::operator*; + using LocalPointerBase::operator->; + /** + * Constructor takes ownership. + * @param p simple pointer to an array of T items that is adopted + */ + explicit LocalMemory(T *p=nullptr) : LocalPointerBase(p) {} + /** + * Move constructor, leaves src with isNull(). + * @param src source smart pointer + */ + LocalMemory(LocalMemory &&src) noexcept : LocalPointerBase(src.ptr) { + src.ptr=nullptr; + } + /** + * Destructor deletes the memory it owns. + */ + ~LocalMemory() { + uprv_free(LocalPointerBase::ptr); + } + /** + * Move assignment operator, leaves src with isNull(). + * The behavior is undefined if *this and src are the same object. + * @param src source smart pointer + * @return *this + */ + LocalMemory &operator=(LocalMemory &&src) noexcept { + uprv_free(LocalPointerBase::ptr); + LocalPointerBase::ptr=src.ptr; + src.ptr=nullptr; + return *this; + } + /** + * Swap pointers. + * @param other other smart pointer + */ + void swap(LocalMemory &other) noexcept { + T *temp=LocalPointerBase::ptr; + LocalPointerBase::ptr=other.ptr; + other.ptr=temp; + } + /** + * Non-member LocalMemory swap function. + * @param p1 will get p2's pointer + * @param p2 will get p1's pointer + */ + friend inline void swap(LocalMemory &p1, LocalMemory &p2) noexcept { + p1.swap(p2); + } + /** + * Deletes the array it owns, + * and adopts (takes ownership of) the one passed in. + * @param p simple pointer to an array of T items that is adopted + */ + void adoptInstead(T *p) { + uprv_free(LocalPointerBase::ptr); + LocalPointerBase::ptr=p; + } + /** + * Deletes the array it owns, allocates a new one and reset its bytes to 0. + * Returns the new array pointer. + * If the allocation fails, then the current array is unchanged and + * this method returns nullptr. + * @param newCapacity must be >0 + * @return the allocated array pointer, or nullptr if the allocation failed + */ + inline T *allocateInsteadAndReset(int32_t newCapacity=1); + /** + * Deletes the array it owns and allocates a new one, copying length T items. + * Returns the new array pointer. + * If the allocation fails, then the current array is unchanged and + * this method returns nullptr. + * @param newCapacity must be >0 + * @param length number of T items to be copied from the old array to the new one; + * must be no more than the capacity of the old array, + * which the caller must track because the LocalMemory does not track it + * @return the allocated array pointer, or nullptr if the allocation failed + */ + inline T *allocateInsteadAndCopy(int32_t newCapacity=1, int32_t length=0); + /** + * Array item access (writable). + * No index bounds check. + * @param i array index + * @return reference to the array item + */ + T &operator[](ptrdiff_t i) const { return LocalPointerBase::ptr[i]; } +}; + +template +inline T *LocalMemory::allocateInsteadAndReset(int32_t newCapacity) { + if(newCapacity>0) { + T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); + if(p!=nullptr) { + uprv_memset(p, 0, newCapacity*sizeof(T)); + uprv_free(LocalPointerBase::ptr); + LocalPointerBase::ptr=p; + } + return p; + } else { + return nullptr; + } +} + + +template +inline T *LocalMemory::allocateInsteadAndCopy(int32_t newCapacity, int32_t length) { + if(newCapacity>0) { + T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); + if(p!=nullptr) { + if(length>0) { + if(length>newCapacity) { + length=newCapacity; + } + uprv_memcpy(p, LocalPointerBase::ptr, (size_t)length*sizeof(T)); + } + uprv_free(LocalPointerBase::ptr); + LocalPointerBase::ptr=p; + } + return p; + } else { + return nullptr; + } +} + +/** + * Simple array/buffer management class using uprv_malloc() and uprv_free(). + * Provides an internal array with fixed capacity. Can alias another array + * or allocate one. + * + * The array address is properly aligned for type T. It might not be properly + * aligned for types larger than T (or larger than the largest subtype of T). + * + * Unlike LocalMemory and LocalArray, this class never adopts + * (takes ownership of) another array. + * + * WARNING: MaybeStackArray only works with primitive (plain-old data) types. + * It does NOT know how to call a destructor! If you work with classes with + * destructors, consider: + * + * - LocalArray in localpointer.h if you know the length ahead of time + * - MaybeStackVector if you know the length at runtime + */ +template +class MaybeStackArray { +public: + // No heap allocation. Use only on the stack. + static void* U_EXPORT2 operator new(size_t) noexcept = delete; + static void* U_EXPORT2 operator new[](size_t) noexcept = delete; +#if U_HAVE_PLACEMENT_NEW + static void* U_EXPORT2 operator new(size_t, void*) noexcept = delete; +#endif + + /** + * Default constructor initializes with internal T[stackCapacity] buffer. + */ + MaybeStackArray() : ptr(stackArray), capacity(stackCapacity), needToRelease(false) {} + /** + * Automatically allocates the heap array if the argument is larger than the stack capacity. + * Intended for use when an approximate capacity is known at compile time but the true + * capacity is not known until runtime. + */ + MaybeStackArray(int32_t newCapacity, UErrorCode status) : MaybeStackArray() { + if (U_FAILURE(status)) { + return; + } + if (capacity < newCapacity) { + if (resize(newCapacity) == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + } + } + /** + * Destructor deletes the array (if owned). + */ + ~MaybeStackArray() { releaseArray(); } + /** + * Move constructor: transfers ownership or copies the stack array. + */ + MaybeStackArray(MaybeStackArray &&src) noexcept; + /** + * Move assignment: transfers ownership or copies the stack array. + */ + MaybeStackArray &operator=(MaybeStackArray &&src) noexcept; + /** + * Returns the array capacity (number of T items). + * @return array capacity + */ + int32_t getCapacity() const { return capacity; } + /** + * Access without ownership change. + * @return the array pointer + */ + T *getAlias() const { return ptr; } + /** + * Returns the array limit. Simple convenience method. + * @return getAlias()+getCapacity() + */ + T *getArrayLimit() const { return getAlias()+capacity; } + // No "operator T *() const" because that can make + // expressions like mbs[index] ambiguous for some compilers. + /** + * Array item access (const). + * No index bounds check. + * @param i array index + * @return reference to the array item + */ + const T &operator[](ptrdiff_t i) const { return ptr[i]; } + /** + * Array item access (writable). + * No index bounds check. + * @param i array index + * @return reference to the array item + */ + T &operator[](ptrdiff_t i) { return ptr[i]; } + /** + * Deletes the array (if owned) and aliases another one, no transfer of ownership. + * If the arguments are illegal, then the current array is unchanged. + * @param otherArray must not be nullptr + * @param otherCapacity must be >0 + */ + void aliasInstead(T *otherArray, int32_t otherCapacity) { + if(otherArray!=nullptr && otherCapacity>0) { + releaseArray(); + ptr=otherArray; + capacity=otherCapacity; + needToRelease=false; + } + } + /** + * Deletes the array (if owned) and allocates a new one, copying length T items. + * Returns the new array pointer. + * If the allocation fails, then the current array is unchanged and + * this method returns nullptr. + * @param newCapacity can be less than or greater than the current capacity; + * must be >0 + * @param length number of T items to be copied from the old array to the new one + * @return the allocated array pointer, or nullptr if the allocation failed + */ + inline T *resize(int32_t newCapacity, int32_t length=0); + /** + * Gives up ownership of the array if owned, or else clones it, + * copying length T items; resets itself to the internal stack array. + * Returns nullptr if the allocation failed. + * @param length number of T items to copy when cloning, + * and capacity of the clone when cloning + * @param resultCapacity will be set to the returned array's capacity (output-only) + * @return the array pointer; + * caller becomes responsible for deleting the array + */ + inline T *orphanOrClone(int32_t length, int32_t &resultCapacity); + +protected: + // Resizes the array to the size of src, then copies the contents of src. + void copyFrom(const MaybeStackArray &src, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + if (this->resize(src.capacity, 0) == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_memcpy(this->ptr, src.ptr, (size_t)capacity * sizeof(T)); + } + +private: + T *ptr; + int32_t capacity; + UBool needToRelease; + T stackArray[stackCapacity]; + void releaseArray() { + if(needToRelease) { + uprv_free(ptr); + } + } + void resetToStackArray() { + ptr=stackArray; + capacity=stackCapacity; + needToRelease=false; + } + /* No comparison operators with other MaybeStackArray's. */ + bool operator==(const MaybeStackArray & /*other*/) = delete; + bool operator!=(const MaybeStackArray & /*other*/) = delete; + /* No ownership transfer: No copy constructor, no assignment operator. */ + MaybeStackArray(const MaybeStackArray & /*other*/) = delete; + void operator=(const MaybeStackArray & /*other*/) = delete; +}; + +template +icu::MaybeStackArray::MaybeStackArray( + MaybeStackArray && src) 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) 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) { +#if U_DEBUG && defined(UPRV_MALLOC_COUNT) + ::fprintf(::stderr, "MaybeStackArray (resize) alloc %d * %lu\n", newCapacity, sizeof(T)); +#endif + T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); + if(p!=nullptr) { + if(length>0) { + if(length>capacity) { + length=capacity; + } + if(length>newCapacity) { + length=newCapacity; + } + uprv_memcpy(p, ptr, (size_t)length*sizeof(T)); + } + releaseArray(); + ptr=p; + capacity=newCapacity; + needToRelease=true; + } + return p; + } else { + return nullptr; + } +} + +template +inline T *MaybeStackArray::orphanOrClone(int32_t length, int32_t &resultCapacity) { + T *p; + if(needToRelease) { + p=ptr; + } else if(length<=0) { + return nullptr; + } else { + if(length>capacity) { + length=capacity; + } + p=(T *)uprv_malloc(length*sizeof(T)); +#if U_DEBUG && defined(UPRV_MALLOC_COUNT) + ::fprintf(::stderr,"MaybeStacArray (orphan) alloc %d * %lu\n", length,sizeof(T)); +#endif + if(p==nullptr) { + return nullptr; + } + uprv_memcpy(p, ptr, (size_t)length*sizeof(T)); + } + resultCapacity=length; + resetToStackArray(); + return p; +} + +/** + * Variant of MaybeStackArray that allocates a header struct and an array + * in one contiguous memory block, using uprv_malloc() and uprv_free(). + * Provides internal memory with fixed array capacity. Can alias another memory + * block or allocate one. + * The stackCapacity is the number of T items in the internal memory, + * not counting the H header. + * Unlike LocalMemory and LocalArray, this class never adopts + * (takes ownership of) another memory block. + */ +template +class MaybeStackHeaderAndArray { +public: + // No heap allocation. Use only on the stack. + static void* U_EXPORT2 operator new(size_t) noexcept = delete; + static void* U_EXPORT2 operator new[](size_t) noexcept = delete; +#if U_HAVE_PLACEMENT_NEW + static void* U_EXPORT2 operator new(size_t, void*) noexcept = delete; +#endif + + /** + * Default constructor initializes with internal H+T[stackCapacity] buffer. + */ + MaybeStackHeaderAndArray() : ptr(&stackHeader), capacity(stackCapacity), needToRelease(false) {} + /** + * Destructor deletes the memory (if owned). + */ + ~MaybeStackHeaderAndArray() { releaseMemory(); } + /** + * Returns the array capacity (number of T items). + * @return array capacity + */ + int32_t getCapacity() const { return capacity; } + /** + * Access without ownership change. + * @return the header pointer + */ + H *getAlias() const { return ptr; } + /** + * Returns the array start. + * @return array start, same address as getAlias()+1 + */ + T *getArrayStart() const { return reinterpret_cast(getAlias()+1); } + /** + * Returns the array limit. + * @return array limit + */ + T *getArrayLimit() const { return getArrayStart()+capacity; } + /** + * Access without ownership change. Same as getAlias(). + * A class instance can be used directly in expressions that take a T *. + * @return the header pointer + */ + operator H *() const { return ptr; } + /** + * Array item access (writable). + * No index bounds check. + * @param i array index + * @return reference to the array item + */ + T &operator[](ptrdiff_t i) { return getArrayStart()[i]; } + /** + * Deletes the memory block (if owned) and aliases another one, no transfer of ownership. + * If the arguments are illegal, then the current memory is unchanged. + * @param otherArray must not be nullptr + * @param otherCapacity must be >0 + */ + void aliasInstead(H *otherMemory, int32_t otherCapacity) { + if(otherMemory!=nullptr && otherCapacity>0) { + releaseMemory(); + ptr=otherMemory; + capacity=otherCapacity; + needToRelease=false; + } + } + /** + * Deletes the memory block (if owned) and allocates a new one, + * copying the header and length T array items. + * Returns the new header pointer. + * If the allocation fails, then the current memory is unchanged and + * this method returns nullptr. + * @param newCapacity can be less than or greater than the current capacity; + * must be >0 + * @param length number of T items to be copied from the old array to the new one + * @return the allocated pointer, or nullptr if the allocation failed + */ + inline H *resize(int32_t newCapacity, int32_t length=0); + /** + * Gives up ownership of the memory if owned, or else clones it, + * copying the header and length T array items; resets itself to the internal memory. + * Returns nullptr if the allocation failed. + * @param length number of T items to copy when cloning, + * and array capacity of the clone when cloning + * @param resultCapacity will be set to the returned array's capacity (output-only) + * @return the header pointer; + * caller becomes responsible for deleting the array + */ + inline H *orphanOrClone(int32_t length, int32_t &resultCapacity); +private: + H *ptr; + int32_t capacity; + UBool needToRelease; + // stackHeader must precede stackArray immediately. + H stackHeader; + T stackArray[stackCapacity]; + void releaseMemory() { + if(needToRelease) { + uprv_free(ptr); + } + } + /* No comparison operators with other MaybeStackHeaderAndArray's. */ + bool operator==(const MaybeStackHeaderAndArray & /*other*/) {return false;} + bool operator!=(const MaybeStackHeaderAndArray & /*other*/) {return true;} + /* No ownership transfer: No copy constructor, no assignment operator. */ + MaybeStackHeaderAndArray(const MaybeStackHeaderAndArray & /*other*/) {} + void operator=(const MaybeStackHeaderAndArray & /*other*/) {} +}; + +template +inline H *MaybeStackHeaderAndArray::resize(int32_t newCapacity, + int32_t length) { + if(newCapacity>=0) { +#if U_DEBUG && defined(UPRV_MALLOC_COUNT) + ::fprintf(::stderr,"MaybeStackHeaderAndArray alloc %d + %d * %ul\n", sizeof(H),newCapacity,sizeof(T)); +#endif + H *p=(H *)uprv_malloc(sizeof(H)+newCapacity*sizeof(T)); + if(p!=nullptr) { + if(length<0) { + length=0; + } else if(length>0) { + if(length>capacity) { + length=capacity; + } + if(length>newCapacity) { + length=newCapacity; + } + } + uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T)); + releaseMemory(); + ptr=p; + capacity=newCapacity; + needToRelease=true; + } + return p; + } else { + return nullptr; + } +} + +template +inline H *MaybeStackHeaderAndArray::orphanOrClone(int32_t length, + int32_t &resultCapacity) { + H *p; + if(needToRelease) { + p=ptr; + } else { + if(length<0) { + length=0; + } else if(length>capacity) { + length=capacity; + } +#if U_DEBUG && defined(UPRV_MALLOC_COUNT) + ::fprintf(::stderr,"MaybeStackHeaderAndArray (orphan) alloc %ul + %d * %lu\n", sizeof(H),length,sizeof(T)); +#endif + p=(H *)uprv_malloc(sizeof(H)+length*sizeof(T)); + if(p==nullptr) { + return nullptr; + } + uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T)); + } + resultCapacity=length; + ptr=&stackHeader; + capacity=stackCapacity; + needToRelease=false; + return p; +} + +/** + * A simple memory management class that creates new heap allocated objects (of + * any class that has a public constructor), keeps track of them and eventually + * deletes them all in its own destructor. + * + * A typical use-case would be code like this: + * + * MemoryPool pool; + * + * MyType* o1 = pool.create(); + * if (o1 != nullptr) { + * foo(o1); + * } + * + * MyType* o2 = pool.create(1, 2, 3); + * if (o2 != nullptr) { + * bar(o2); + * } + * + * // MemoryPool will take care of deleting the MyType objects. + * + * It doesn't do anything more than that, and is intentionally kept minimalist. + */ +template +class MemoryPool : public UMemory { +public: + MemoryPool() : fCount(0), fPool() {} + + ~MemoryPool() { + for (int32_t i = 0; i < fCount; ++i) { + delete fPool[i]; + } + } + + MemoryPool(const MemoryPool&) = delete; + MemoryPool& operator=(const MemoryPool&) = delete; + + MemoryPool(MemoryPool&& other) noexcept : fCount(other.fCount), + fPool(std::move(other.fPool)) { + other.fCount = 0; + } + + MemoryPool& operator=(MemoryPool&& other) noexcept { + // Since `this` may contain instances that need to be deleted, we can't + // just throw them away and replace them with `other`. The normal way of + // dealing with this in C++ is to swap `this` and `other`, rather than + // simply overwrite: the destruction of `other` can then take care of + // running MemoryPool::~MemoryPool() over the still-to-be-deallocated + // instances. + std::swap(fCount, other.fCount); + std::swap(fPool, other.fPool); + return *this; + } + + /** + * Creates a new object of typename T, by forwarding any and all arguments + * to the typename T constructor. + * + * @param args Arguments to be forwarded to the typename T constructor. + * @return A pointer to the newly created object, or nullptr on error. + */ + template + T* create(Args&&... args) { + int32_t capacity = fPool.getCapacity(); + if (fCount == capacity && + fPool.resize(capacity == stackCapacity ? 4 * capacity : 2 * capacity, + capacity) == nullptr) { + return nullptr; + } + return fPool[fCount++] = new T(std::forward(args)...); + } + + template + T* createAndCheckErrorCode(UErrorCode &status, Args &&... args) { + if (U_FAILURE(status)) { + return nullptr; + } + T *pointer = this->create(args...); + if (U_SUCCESS(status) && pointer == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return pointer; + } + + /** + * @return Number of elements that have been allocated. + */ + int32_t count() const { + return fCount; + } + +protected: + int32_t fCount; + MaybeStackArray fPool; +}; + +/** + * An internal Vector-like implementation based on MemoryPool. + * + * Heap-allocates each element and stores pointers. + * + * To append an item to the vector, use emplaceBack. + * + * MaybeStackVector vector; + * MyType* element = vector.emplaceBack(); + * if (!element) { + * status = U_MEMORY_ALLOCATION_ERROR; + * } + * // do stuff with element + * + * To loop over the vector, use a for loop with indices: + * + * for (int32_t i = 0; i < vector.length(); i++) { + * MyType* element = vector[i]; + * } + */ +template +class MaybeStackVector : protected MemoryPool { +public: + template + T* emplaceBack(Args&&... args) { + return this->create(args...); + } + + template + T *emplaceBackAndCheckErrorCode(UErrorCode &status, Args &&... args) { + return this->createAndCheckErrorCode(status, args...); + } + + int32_t length() const { + return this->fCount; + } + + T** getAlias() { + return this->fPool.getAlias(); + } + + const T *const *getAlias() const { + return this->fPool.getAlias(); + } + + /** + * Array item access (read-only). + * No index bounds check. + * @param i array index + * @return reference to the array item + */ + const T* operator[](ptrdiff_t i) const { + return this->fPool[i]; + } + + /** + * Array item access (writable). + * No index bounds check. + * @param i array index + * @return reference to the array item + */ + T* operator[](ptrdiff_t i) { + return this->fPool[i]; + } +}; + + +U_NAMESPACE_END + +#endif /* __cplusplus */ +#endif /* CMEMORY_H */ diff --git a/deps/icu-small/source/common/common.rc b/deps/icu-small/source/common/common.rc index 020abacc0d81fe..a9a09946b6f8c8 100644 --- a/deps/icu-small/source/common/common.rc +++ b/deps/icu-small/source/common/common.rc @@ -1,110 +1,110 @@ -// Do not edit with Microsoft Developer Studio Resource Editor. -// It will permanently substitute version numbers that are intended to be -// picked up by the pre-processor during each build. -// Copyright (C) 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// Copyright (c) 2001-2010 International Business Machines -// Corporation and others. All Rights Reserved. -// -#include "msvcres.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// - -LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "msvcres.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include \0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// -#define STR(s) #s -#define CommaVersionString(a, b, c, d) STR(a) ", " STR(b) ", " STR(c) ", " STR(d) "\0" - -VS_VERSION_INFO VERSIONINFO - FILEVERSION U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM - PRODUCTVERSION U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_DLL - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "00000000" - BEGIN - VALUE "Comments", ICU_WEBSITE "\0" - VALUE "CompanyName", ICU_COMPANY "\0" - VALUE "FileDescription", ICU_PRODUCT_PREFIX " Common DLL\0" - VALUE "FileVersion", CommaVersionString(U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM) - VALUE "LegalCopyright", U_COPYRIGHT_STRING "\0" -#ifdef _DEBUG - VALUE "OriginalFilename", "icuuc" U_ICU_VERSION_SHORT "d.dll\0" -#else - VALUE "OriginalFilename", "icuuc" U_ICU_VERSION_SHORT ".dll\0" -#endif - VALUE "PrivateBuild", "\0" - VALUE "ProductName", ICU_PRODUCT "\0" - VALUE "ProductVersion", CommaVersionString(U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM) - VALUE "SpecialBuild", "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x000, 0000 - END -END - -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// Do not edit with Microsoft Developer Studio Resource Editor. +// It will permanently substitute version numbers that are intended to be +// picked up by the pre-processor during each build. +// Copyright (C) 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// Copyright (c) 2001-2010 International Business Machines +// Corporation and others. All Rights Reserved. +// +#include "msvcres.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "msvcres.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include \0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#define STR(s) #s +#define CommaVersionString(a, b, c, d) STR(a) ", " STR(b) ", " STR(c) ", " STR(d) "\0" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM + PRODUCTVERSION U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "00000000" + BEGIN + VALUE "Comments", ICU_WEBSITE "\0" + VALUE "CompanyName", ICU_COMPANY "\0" + VALUE "FileDescription", ICU_PRODUCT_PREFIX " Common DLL\0" + VALUE "FileVersion", CommaVersionString(U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM) + VALUE "LegalCopyright", U_COPYRIGHT_STRING "\0" +#ifdef _DEBUG + VALUE "OriginalFilename", "icuuc" U_ICU_VERSION_SHORT "d.dll\0" +#else + VALUE "OriginalFilename", "icuuc" U_ICU_VERSION_SHORT ".dll\0" +#endif + VALUE "PrivateBuild", "\0" + VALUE "ProductName", ICU_PRODUCT "\0" + VALUE "ProductVersion", CommaVersionString(U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM) + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x000, 0000 + END +END + +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/deps/icu-small/source/common/cpputils.h b/deps/icu-small/source/common/cpputils.h index 307e570486439f..4005402c895f31 100644 --- a/deps/icu-small/source/common/cpputils.h +++ b/deps/icu-small/source/common/cpputils.h @@ -1,97 +1,97 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1997-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: cpputils.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -*/ - -#ifndef CPPUTILS_H -#define CPPUTILS_H - -#include "unicode/utypes.h" -#include "unicode/unistr.h" -#include "cmemory.h" - -/*==========================================================================*/ -/* Array copy utility functions */ -/*==========================================================================*/ - -static -inline void uprv_arrayCopy(const double* src, double* dst, int32_t count) -{ uprv_memcpy(dst, src, (size_t)count * sizeof(*src)); } - -static -inline void uprv_arrayCopy(const double* src, int32_t srcStart, - double* dst, int32_t dstStart, int32_t count) -{ uprv_memcpy(dst+dstStart, src+srcStart, (size_t)count * sizeof(*src)); } - -static -inline void uprv_arrayCopy(const int8_t* src, int8_t* dst, int32_t count) - { uprv_memcpy(dst, src, (size_t)count * sizeof(*src)); } - -static -inline void uprv_arrayCopy(const int8_t* src, int32_t srcStart, - int8_t* dst, int32_t dstStart, int32_t count) -{ uprv_memcpy(dst+dstStart, src+srcStart, (size_t)count * sizeof(*src)); } - -static -inline void uprv_arrayCopy(const int16_t* src, int16_t* dst, int32_t count) -{ uprv_memcpy(dst, src, (size_t)count * sizeof(*src)); } - -static -inline void uprv_arrayCopy(const int16_t* src, int32_t srcStart, - int16_t* dst, int32_t dstStart, int32_t count) -{ uprv_memcpy(dst+dstStart, src+srcStart, (size_t)count * sizeof(*src)); } - -static -inline void uprv_arrayCopy(const int32_t* src, int32_t* dst, int32_t count) -{ uprv_memcpy(dst, src, (size_t)count * sizeof(*src)); } - -static -inline void uprv_arrayCopy(const int32_t* src, int32_t srcStart, - int32_t* dst, int32_t dstStart, int32_t count) -{ uprv_memcpy(dst+dstStart, src+srcStart, (size_t)count * sizeof(*src)); } - -static -inline void -uprv_arrayCopy(const UChar *src, int32_t srcStart, - UChar *dst, int32_t dstStart, int32_t count) -{ uprv_memcpy(dst+dstStart, src+srcStart, (size_t)count * sizeof(*src)); } - -/** - * Copy an array of UnicodeString OBJECTS (not pointers). - * @internal - */ -static inline void -uprv_arrayCopy(const icu::UnicodeString *src, icu::UnicodeString *dst, int32_t count) -{ while(count-- > 0) *dst++ = *src++; } - -/** - * Copy an array of UnicodeString OBJECTS (not pointers). - * @internal - */ -static inline void -uprv_arrayCopy(const icu::UnicodeString *src, int32_t srcStart, - icu::UnicodeString *dst, int32_t dstStart, int32_t count) -{ uprv_arrayCopy(src+srcStart, dst+dstStart, count); } - -/** - * Checks that the string is readable and writable. - * Sets U_ILLEGAL_ARGUMENT_ERROR if the string isBogus() or has an open getBuffer(). - */ -inline void -uprv_checkCanGetBuffer(const icu::UnicodeString &s, UErrorCode &errorCode) { - if(U_SUCCESS(errorCode) && s.isBogus()) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - } -} - -#endif /* _CPPUTILS */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1997-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: cpputils.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +*/ + +#ifndef CPPUTILS_H +#define CPPUTILS_H + +#include "unicode/utypes.h" +#include "unicode/unistr.h" +#include "cmemory.h" + +/*==========================================================================*/ +/* Array copy utility functions */ +/*==========================================================================*/ + +static +inline void uprv_arrayCopy(const double* src, double* dst, int32_t count) +{ uprv_memcpy(dst, src, (size_t)count * sizeof(*src)); } + +static +inline void uprv_arrayCopy(const double* src, int32_t srcStart, + double* dst, int32_t dstStart, int32_t count) +{ uprv_memcpy(dst+dstStart, src+srcStart, (size_t)count * sizeof(*src)); } + +static +inline void uprv_arrayCopy(const int8_t* src, int8_t* dst, int32_t count) + { uprv_memcpy(dst, src, (size_t)count * sizeof(*src)); } + +static +inline void uprv_arrayCopy(const int8_t* src, int32_t srcStart, + int8_t* dst, int32_t dstStart, int32_t count) +{ uprv_memcpy(dst+dstStart, src+srcStart, (size_t)count * sizeof(*src)); } + +static +inline void uprv_arrayCopy(const int16_t* src, int16_t* dst, int32_t count) +{ uprv_memcpy(dst, src, (size_t)count * sizeof(*src)); } + +static +inline void uprv_arrayCopy(const int16_t* src, int32_t srcStart, + int16_t* dst, int32_t dstStart, int32_t count) +{ uprv_memcpy(dst+dstStart, src+srcStart, (size_t)count * sizeof(*src)); } + +static +inline void uprv_arrayCopy(const int32_t* src, int32_t* dst, int32_t count) +{ uprv_memcpy(dst, src, (size_t)count * sizeof(*src)); } + +static +inline void uprv_arrayCopy(const int32_t* src, int32_t srcStart, + int32_t* dst, int32_t dstStart, int32_t count) +{ uprv_memcpy(dst+dstStart, src+srcStart, (size_t)count * sizeof(*src)); } + +static +inline void +uprv_arrayCopy(const char16_t *src, int32_t srcStart, + char16_t *dst, int32_t dstStart, int32_t count) +{ uprv_memcpy(dst+dstStart, src+srcStart, (size_t)count * sizeof(*src)); } + +/** + * Copy an array of UnicodeString OBJECTS (not pointers). + * @internal + */ +static inline void +uprv_arrayCopy(const icu::UnicodeString *src, icu::UnicodeString *dst, int32_t count) +{ while(count-- > 0) *dst++ = *src++; } + +/** + * Copy an array of UnicodeString OBJECTS (not pointers). + * @internal + */ +static inline void +uprv_arrayCopy(const icu::UnicodeString *src, int32_t srcStart, + icu::UnicodeString *dst, int32_t dstStart, int32_t count) +{ uprv_arrayCopy(src+srcStart, dst+dstStart, count); } + +/** + * Checks that the string is readable and writable. + * Sets U_ILLEGAL_ARGUMENT_ERROR if the string isBogus() or has an open getBuffer(). + */ +inline void +uprv_checkCanGetBuffer(const icu::UnicodeString &s, UErrorCode &errorCode) { + if(U_SUCCESS(errorCode) && s.isBogus()) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + } +} + +#endif /* _CPPUTILS */ diff --git a/deps/icu-small/source/common/cstr.cpp b/deps/icu-small/source/common/cstr.cpp index 24654f8fc22897..0275d86310e977 100644 --- a/deps/icu-small/source/common/cstr.cpp +++ b/deps/icu-small/source/common/cstr.cpp @@ -1,54 +1,54 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015-2016, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: charstr.cpp -*/ -#include "unicode/utypes.h" -#include "unicode/putil.h" -#include "unicode/unistr.h" - -#include "cstr.h" - -#include "charstr.h" -#include "uinvchar.h" - -U_NAMESPACE_BEGIN - -CStr::CStr(const UnicodeString &in) { - UErrorCode status = U_ZERO_ERROR; -#if !UCONFIG_NO_CONVERSION || U_CHARSET_IS_UTF8 - int32_t length = in.extract(0, in.length(), static_cast(NULL), static_cast(0)); - int32_t resultCapacity = 0; - char *buf = s.getAppendBuffer(length, length, resultCapacity, status); - if (U_SUCCESS(status)) { - in.extract(0, in.length(), buf, resultCapacity); - s.append(buf, length, status); - } -#else - // No conversion available. Convert any invariant characters; substitute '?' for the rest. - // Note: can't just call u_UCharsToChars() or CharString.appendInvariantChars() on the - // whole string because they require that the entire input be invariant. - char buf[2]; - for (int i=0; i(nullptr), static_cast(0)); + int32_t resultCapacity = 0; + char *buf = s.getAppendBuffer(length, length, resultCapacity, status); + if (U_SUCCESS(status)) { + in.extract(0, in.length(), buf, resultCapacity); + s.append(buf, length, status); + } +#else + // No conversion available. Convert any invariant characters; substitute '?' for the rest. + // Note: can't just call u_UCharsToChars() or CharString.appendInvariantChars() on the + // whole string because they require that the entire input be invariant. + char buf[2]; + for (int i=0; i -#include -#include "unicode/utypes.h" -#include "cmemory.h" -#include "cstring.h" -#include "uassert.h" - -/* - * We hardcode case conversion for invariant characters to match our expectation - * and the compiler execution charset. - * This prevents problems on systems - * - with non-default casing behavior, like Turkish system locales where - * tolower('I') maps to dotless i and toupper('i') maps to dotted I - * - where there are no lowercase Latin characters at all, or using different - * codes (some old EBCDIC codepages) - * - * This works because the compiler usually runs on a platform where the execution - * charset includes all of the invariant characters at their expected - * code positions, so that the char * string literals in ICU code match - * the char literals here. - * - * Note that the set of lowercase Latin letters is discontiguous in EBCDIC - * and the set of uppercase Latin letters is discontiguous as well. - */ - -U_CAPI UBool U_EXPORT2 -uprv_isASCIILetter(char c) { -#if U_CHARSET_FAMILY==U_EBCDIC_FAMILY - return - ('a'<=c && c<='i') || ('j'<=c && c<='r') || ('s'<=c && c<='z') || - ('A'<=c && c<='I') || ('J'<=c && c<='R') || ('S'<=c && c<='Z'); -#else - return ('a'<=c && c<='z') || ('A'<=c && c<='Z'); -#endif -} - -U_CAPI char U_EXPORT2 -uprv_toupper(char c) { -#if U_CHARSET_FAMILY==U_EBCDIC_FAMILY - if(('a'<=c && c<='i') || ('j'<=c && c<='r') || ('s'<=c && c<='z')) { - c=(char)(c+('A'-'a')); - } -#else - if('a'<=c && c<='z') { - c=(char)(c+('A'-'a')); - } -#endif - return c; -} - - -#if 0 -/* - * Commented out because cstring.h defines uprv_tolower() to be - * the same as either uprv_asciitolower() or uprv_ebcdictolower() - * to reduce the amount of code to cover with tests. - * - * Note that this uprv_tolower() definition is likely to work for most - * charset families, not just ASCII and EBCDIC, because its #else branch - * is written generically. - */ -U_CAPI char U_EXPORT2 -uprv_tolower(char c) { -#if U_CHARSET_FAMILY==U_EBCDIC_FAMILY - if(('A'<=c && c<='I') || ('J'<=c && c<='R') || ('S'<=c && c<='Z')) { - c=(char)(c+('a'-'A')); - } -#else - if('A'<=c && c<='Z') { - c=(char)(c+('a'-'A')); - } -#endif - return c; -} -#endif - -U_CAPI char U_EXPORT2 -uprv_asciitolower(char c) { - if(0x41<=c && c<=0x5a) { - c=(char)(c+0x20); - } - return c; -} - -U_CAPI char U_EXPORT2 -uprv_ebcdictolower(char c) { - if( (0xc1<=(uint8_t)c && (uint8_t)c<=0xc9) || - (0xd1<=(uint8_t)c && (uint8_t)c<=0xd9) || - (0xe2<=(uint8_t)c && (uint8_t)c<=0xe9) - ) { - c=(char)(c-0x40); - } - return c; -} - - -U_CAPI char* U_EXPORT2 -T_CString_toLowerCase(char* str) -{ - char* origPtr = str; - - if (str) { - do - *str = (char)uprv_tolower(*str); - while (*(str++)); - } - - return origPtr; -} - -U_CAPI char* U_EXPORT2 -T_CString_toUpperCase(char* str) -{ - char* origPtr = str; - - if (str) { - do - *str = (char)uprv_toupper(*str); - while (*(str++)); - } - - return origPtr; -} - -/* - * Takes a int32_t and fills in a char* string with that number "radix"-based. - * Does not handle negative values (makes an empty string for them). - * Writes at most 12 chars ("-2147483647" plus NUL). - * Returns the length of the string (not including the NUL). - */ -U_CAPI int32_t U_EXPORT2 -T_CString_integerToString(char* buffer, int32_t v, int32_t radix) -{ - char tbuf[30]; - int32_t tbx = sizeof(tbuf); - uint8_t digit; - int32_t length = 0; - uint32_t uval; - - U_ASSERT(radix>=2 && radix<=16); - uval = (uint32_t) v; - if(v<0 && radix == 10) { - /* Only in base 10 do we conside numbers to be signed. */ - uval = (uint32_t)(-v); - buffer[length++] = '-'; - } - - tbx = sizeof(tbuf)-1; - tbuf[tbx] = 0; /* We are generating the digits backwards. Null term the end. */ - do { - digit = (uint8_t)(uval % radix); - tbuf[--tbx] = (char)(T_CString_itosOffset(digit)); - uval = uval / radix; - } while (uval != 0); - - /* copy converted number into user buffer */ - uprv_strcpy(buffer+length, tbuf+tbx); - length += sizeof(tbuf) - tbx -1; - return length; -} - - - -/* - * Takes a int64_t and fills in a char* string with that number "radix"-based. - * Writes at most 21: chars ("-9223372036854775807" plus NUL). - * Returns the length of the string, not including the terminating NULL. - */ -U_CAPI int32_t U_EXPORT2 -T_CString_int64ToString(char* buffer, int64_t v, uint32_t radix) -{ - char tbuf[30]; - int32_t tbx = sizeof(tbuf); - uint8_t digit; - int32_t length = 0; - uint64_t uval; - - U_ASSERT(radix>=2 && radix<=16); - uval = (uint64_t) v; - if(v<0 && radix == 10) { - /* Only in base 10 do we conside numbers to be signed. */ - uval = (uint64_t)(-v); - buffer[length++] = '-'; - } - - tbx = sizeof(tbuf)-1; - tbuf[tbx] = 0; /* We are generating the digits backwards. Null term the end. */ - do { - digit = (uint8_t)(uval % radix); - tbuf[--tbx] = (char)(T_CString_itosOffset(digit)); - uval = uval / radix; - } while (uval != 0); - - /* copy converted number into user buffer */ - uprv_strcpy(buffer+length, tbuf+tbx); - length += sizeof(tbuf) - tbx -1; - return length; -} - - -U_CAPI int32_t U_EXPORT2 -T_CString_stringToInteger(const char *integerString, int32_t radix) -{ - char *end; - return uprv_strtoul(integerString, &end, radix); - -} - -U_CAPI int U_EXPORT2 -uprv_stricmp(const char *str1, const char *str2) { - if(str1==NULL) { - if(str2==NULL) { - return 0; - } else { - return -1; - } - } else if(str2==NULL) { - return 1; - } else { - /* compare non-NULL strings lexically with lowercase */ - int rc; - unsigned char c1, c2; - - for(;;) { - c1=(unsigned char)*str1; - c2=(unsigned char)*str2; - if(c1==0) { - if(c2==0) { - return 0; - } else { - return -1; - } - } else if(c2==0) { - return 1; - } else { - /* compare non-zero characters with lowercase */ - rc=(int)(unsigned char)uprv_tolower(c1)-(int)(unsigned char)uprv_tolower(c2); - if(rc!=0) { - return rc; - } - } - ++str1; - ++str2; - } - } -} - -U_CAPI int U_EXPORT2 -uprv_strnicmp(const char *str1, const char *str2, uint32_t n) { - if(str1==NULL) { - if(str2==NULL) { - return 0; - } else { - return -1; - } - } else if(str2==NULL) { - return 1; - } else { - /* compare non-NULL strings lexically with lowercase */ - int rc; - unsigned char c1, c2; - - for(; n--;) { - c1=(unsigned char)*str1; - c2=(unsigned char)*str2; - if(c1==0) { - if(c2==0) { - return 0; - } else { - return -1; - } - } else if(c2==0) { - return 1; - } else { - /* compare non-zero characters with lowercase */ - rc=(int)(unsigned char)uprv_tolower(c1)-(int)(unsigned char)uprv_tolower(c2); - if(rc!=0) { - return rc; - } - } - ++str1; - ++str2; - } - } - - return 0; -} - -U_CAPI char* U_EXPORT2 -uprv_strdup(const char *src) { - size_t len = uprv_strlen(src) + 1; - char *dup = (char *) uprv_malloc(len); - - if (dup) { - uprv_memcpy(dup, src, len); - } - - return dup; -} - -U_CAPI char* U_EXPORT2 -uprv_strndup(const char *src, int32_t n) { - char *dup; - - if(n < 0) { - dup = uprv_strdup(src); - } else { - dup = (char*)uprv_malloc(n+1); - if (dup) { - uprv_memcpy(dup, src, n); - dup[n] = 0; - } - } - - return dup; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1997-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* File CSTRING.C +* +* @author Helena Shih +* +* Modification History: +* +* Date Name Description +* 6/18/98 hshih Created +* 09/08/98 stephen Added include for ctype, for Mac Port +* 11/15/99 helena Integrated S/390 IEEE changes. +****************************************************************************** +*/ + + + +#include +#include +#include "unicode/utypes.h" +#include "cmemory.h" +#include "cstring.h" +#include "uassert.h" + +/* + * We hardcode case conversion for invariant characters to match our expectation + * and the compiler execution charset. + * This prevents problems on systems + * - with non-default casing behavior, like Turkish system locales where + * tolower('I') maps to dotless i and toupper('i') maps to dotted I + * - where there are no lowercase Latin characters at all, or using different + * codes (some old EBCDIC codepages) + * + * This works because the compiler usually runs on a platform where the execution + * charset includes all of the invariant characters at their expected + * code positions, so that the char * string literals in ICU code match + * the char literals here. + * + * Note that the set of lowercase Latin letters is discontiguous in EBCDIC + * and the set of uppercase Latin letters is discontiguous as well. + */ + +U_CAPI UBool U_EXPORT2 +uprv_isASCIILetter(char c) { +#if U_CHARSET_FAMILY==U_EBCDIC_FAMILY + return + ('a'<=c && c<='i') || ('j'<=c && c<='r') || ('s'<=c && c<='z') || + ('A'<=c && c<='I') || ('J'<=c && c<='R') || ('S'<=c && c<='Z'); +#else + return ('a'<=c && c<='z') || ('A'<=c && c<='Z'); +#endif +} + +U_CAPI char U_EXPORT2 +uprv_toupper(char c) { +#if U_CHARSET_FAMILY==U_EBCDIC_FAMILY + if(('a'<=c && c<='i') || ('j'<=c && c<='r') || ('s'<=c && c<='z')) { + c=(char)(c+('A'-'a')); + } +#else + if('a'<=c && c<='z') { + c=(char)(c+('A'-'a')); + } +#endif + return c; +} + + +#if 0 +/* + * Commented out because cstring.h defines uprv_tolower() to be + * the same as either uprv_asciitolower() or uprv_ebcdictolower() + * to reduce the amount of code to cover with tests. + * + * Note that this uprv_tolower() definition is likely to work for most + * charset families, not just ASCII and EBCDIC, because its #else branch + * is written generically. + */ +U_CAPI char U_EXPORT2 +uprv_tolower(char c) { +#if U_CHARSET_FAMILY==U_EBCDIC_FAMILY + if(('A'<=c && c<='I') || ('J'<=c && c<='R') || ('S'<=c && c<='Z')) { + c=(char)(c+('a'-'A')); + } +#else + if('A'<=c && c<='Z') { + c=(char)(c+('a'-'A')); + } +#endif + return c; +} +#endif + +U_CAPI char U_EXPORT2 +uprv_asciitolower(char c) { + if(0x41<=c && c<=0x5a) { + c=(char)(c+0x20); + } + return c; +} + +U_CAPI char U_EXPORT2 +uprv_ebcdictolower(char c) { + if( (0xc1<=(uint8_t)c && (uint8_t)c<=0xc9) || + (0xd1<=(uint8_t)c && (uint8_t)c<=0xd9) || + (0xe2<=(uint8_t)c && (uint8_t)c<=0xe9) + ) { + c=(char)(c-0x40); + } + return c; +} + + +U_CAPI char* U_EXPORT2 +T_CString_toLowerCase(char* str) +{ + char* origPtr = str; + + if (str) { + do + *str = (char)uprv_tolower(*str); + while (*(str++)); + } + + return origPtr; +} + +U_CAPI char* U_EXPORT2 +T_CString_toUpperCase(char* str) +{ + char* origPtr = str; + + if (str) { + do + *str = (char)uprv_toupper(*str); + while (*(str++)); + } + + return origPtr; +} + +/* + * Takes a int32_t and fills in a char* string with that number "radix"-based. + * Does not handle negative values (makes an empty string for them). + * Writes at most 12 chars ("-2147483647" plus NUL). + * Returns the length of the string (not including the NUL). + */ +U_CAPI int32_t U_EXPORT2 +T_CString_integerToString(char* buffer, int32_t v, int32_t radix) +{ + char tbuf[30]; + int32_t tbx = sizeof(tbuf); + uint8_t digit; + int32_t length = 0; + uint32_t uval; + + U_ASSERT(radix>=2 && radix<=16); + uval = (uint32_t) v; + if(v<0 && radix == 10) { + /* Only in base 10 do we conside numbers to be signed. */ + uval = (uint32_t)(-v); + buffer[length++] = '-'; + } + + tbx = sizeof(tbuf)-1; + tbuf[tbx] = 0; /* We are generating the digits backwards. Null term the end. */ + do { + digit = (uint8_t)(uval % radix); + tbuf[--tbx] = (char)(T_CString_itosOffset(digit)); + uval = uval / radix; + } while (uval != 0); + + /* copy converted number into user buffer */ + uprv_strcpy(buffer+length, tbuf+tbx); + length += sizeof(tbuf) - tbx -1; + return length; +} + + + +/* + * Takes a int64_t and fills in a char* string with that number "radix"-based. + * Writes at most 21: chars ("-9223372036854775807" plus NUL). + * Returns the length of the string, not including the terminating NUL. + */ +U_CAPI int32_t U_EXPORT2 +T_CString_int64ToString(char* buffer, int64_t v, uint32_t radix) +{ + char tbuf[30]; + int32_t tbx = sizeof(tbuf); + uint8_t digit; + int32_t length = 0; + uint64_t uval; + + U_ASSERT(radix>=2 && radix<=16); + uval = (uint64_t) v; + if(v<0 && radix == 10) { + /* Only in base 10 do we conside numbers to be signed. */ + uval = (uint64_t)(-v); + buffer[length++] = '-'; + } + + tbx = sizeof(tbuf)-1; + tbuf[tbx] = 0; /* We are generating the digits backwards. Null term the end. */ + do { + digit = (uint8_t)(uval % radix); + tbuf[--tbx] = (char)(T_CString_itosOffset(digit)); + uval = uval / radix; + } while (uval != 0); + + /* copy converted number into user buffer */ + uprv_strcpy(buffer+length, tbuf+tbx); + length += sizeof(tbuf) - tbx -1; + return length; +} + + +U_CAPI int32_t U_EXPORT2 +T_CString_stringToInteger(const char *integerString, int32_t radix) +{ + char *end; + return uprv_strtoul(integerString, &end, radix); + +} + +U_CAPI int U_EXPORT2 +uprv_stricmp(const char *str1, const char *str2) { + if(str1==nullptr) { + if(str2==nullptr) { + return 0; + } else { + return -1; + } + } else if(str2==nullptr) { + return 1; + } else { + /* compare non-nullptr strings lexically with lowercase */ + int rc; + unsigned char c1, c2; + + for(;;) { + c1=(unsigned char)*str1; + c2=(unsigned char)*str2; + if(c1==0) { + if(c2==0) { + return 0; + } else { + return -1; + } + } else if(c2==0) { + return 1; + } else { + /* compare non-zero characters with lowercase */ + rc=(int)(unsigned char)uprv_tolower(c1)-(int)(unsigned char)uprv_tolower(c2); + if(rc!=0) { + return rc; + } + } + ++str1; + ++str2; + } + } +} + +U_CAPI int U_EXPORT2 +uprv_strnicmp(const char *str1, const char *str2, uint32_t n) { + if(str1==nullptr) { + if(str2==nullptr) { + return 0; + } else { + return -1; + } + } else if(str2==nullptr) { + return 1; + } else { + /* compare non-nullptr strings lexically with lowercase */ + int rc; + unsigned char c1, c2; + + for(; n--;) { + c1=(unsigned char)*str1; + c2=(unsigned char)*str2; + if(c1==0) { + if(c2==0) { + return 0; + } else { + return -1; + } + } else if(c2==0) { + return 1; + } else { + /* compare non-zero characters with lowercase */ + rc=(int)(unsigned char)uprv_tolower(c1)-(int)(unsigned char)uprv_tolower(c2); + if(rc!=0) { + return rc; + } + } + ++str1; + ++str2; + } + } + + return 0; +} + +U_CAPI char* U_EXPORT2 +uprv_strdup(const char *src) { + size_t len = uprv_strlen(src) + 1; + char *dup = (char *) uprv_malloc(len); + + if (dup) { + uprv_memcpy(dup, src, len); + } + + return dup; +} + +U_CAPI char* U_EXPORT2 +uprv_strndup(const char *src, int32_t n) { + char *dup; + + if(n < 0) { + dup = uprv_strdup(src); + } else { + dup = (char*)uprv_malloc(n+1); + if (dup) { + uprv_memcpy(dup, src, n); + dup[n] = 0; + } + } + + return dup; +} diff --git a/deps/icu-small/source/common/cstring.h b/deps/icu-small/source/common/cstring.h index 3a14e4216c8e51..05ef6335670194 100644 --- a/deps/icu-small/source/common/cstring.h +++ b/deps/icu-small/source/common/cstring.h @@ -1,126 +1,126 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1997-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* File CSTRING.H -* -* Contains CString interface -* -* @author Helena Shih -* -* Modification History: -* -* Date Name Description -* 6/17/98 hshih Created. -* 05/03/99 stephen Changed from functions to macros. -* 06/14/99 stephen Added icu_strncat, icu_strncmp, icu_tolower -* -****************************************************************************** -*/ - -#ifndef CSTRING_H -#define CSTRING_H 1 - -#include "unicode/utypes.h" -#include "cmemory.h" -#include -#include -#include - -#define uprv_strcpy(dst, src) U_STANDARD_CPP_NAMESPACE strcpy(dst, src) -#define uprv_strlen(str) U_STANDARD_CPP_NAMESPACE strlen(str) -#define uprv_strcmp(s1, s2) U_STANDARD_CPP_NAMESPACE strcmp(s1, s2) -#define uprv_strcat(dst, src) U_STANDARD_CPP_NAMESPACE strcat(dst, src) -#define uprv_strchr(s, c) U_STANDARD_CPP_NAMESPACE strchr(s, c) -#define uprv_strstr(s, c) U_STANDARD_CPP_NAMESPACE strstr(s, c) -#define uprv_strrchr(s, c) U_STANDARD_CPP_NAMESPACE strrchr(s, c) -#define uprv_strncpy(dst, src, size) U_STANDARD_CPP_NAMESPACE strncpy(dst, src, size) -#define uprv_strncmp(s1, s2, n) U_STANDARD_CPP_NAMESPACE strncmp(s1, s2, n) -#define uprv_strncat(dst, src, n) U_STANDARD_CPP_NAMESPACE strncat(dst, src, n) - -/** - * Is c an ASCII-repertoire letter a-z or A-Z? - * Note: The implementation is specific to whether ICU is compiled for - * an ASCII-based or EBCDIC-based machine. There just does not seem to be a better name for this. - */ -U_CAPI UBool U_EXPORT2 -uprv_isASCIILetter(char c); - -// NOTE: For u_asciiToUpper that takes a UChar, see ustr_imp.h - -U_CAPI char U_EXPORT2 -uprv_toupper(char c); - - -U_CAPI char U_EXPORT2 -uprv_asciitolower(char c); - -U_CAPI char U_EXPORT2 -uprv_ebcdictolower(char c); - -#if U_CHARSET_FAMILY==U_ASCII_FAMILY -# define uprv_tolower uprv_asciitolower -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY -# define uprv_tolower uprv_ebcdictolower -#else -# error U_CHARSET_FAMILY is not valid -#endif - -#define uprv_strtod(source, end) U_STANDARD_CPP_NAMESPACE strtod(source, end) -#define uprv_strtoul(str, end, base) U_STANDARD_CPP_NAMESPACE strtoul(str, end, base) -#define uprv_strtol(str, end, base) U_STANDARD_CPP_NAMESPACE strtol(str, end, base) - -/* Conversion from a digit to the character with radix base from 2-19 */ -/* May need to use U_UPPER_ORDINAL*/ -#define T_CString_itosOffset(a) ((a)<=9?('0'+(a)):('A'+(a)-10)) - -U_CAPI char* U_EXPORT2 -uprv_strdup(const char *src); - -/** - * uprv_malloc n+1 bytes, and copy n bytes from src into the new string. - * Terminate with a null at offset n. If n is -1, works like uprv_strdup - * @param src - * @param n length of the input string, not including null. - * @return new string (owned by caller, use uprv_free to free). - * @internal - */ -U_CAPI char* U_EXPORT2 -uprv_strndup(const char *src, int32_t n); - -U_CAPI char* U_EXPORT2 -T_CString_toLowerCase(char* str); - -U_CAPI char* U_EXPORT2 -T_CString_toUpperCase(char* str); - -U_CAPI int32_t U_EXPORT2 -T_CString_integerToString(char *buffer, int32_t n, int32_t radix); - -U_CAPI int32_t U_EXPORT2 -T_CString_int64ToString(char *buffer, int64_t n, uint32_t radix); - -U_CAPI int32_t U_EXPORT2 -T_CString_stringToInteger(const char *integerString, int32_t radix); - -/** - * Case-insensitive, language-independent string comparison - * limited to the ASCII character repertoire. - */ -U_CAPI int U_EXPORT2 -uprv_stricmp(const char *str1, const char *str2); - -/** - * Case-insensitive, language-independent string comparison - * limited to the ASCII character repertoire. - */ -U_CAPI int U_EXPORT2 -uprv_strnicmp(const char *str1, const char *str2, uint32_t n); - -#endif /* ! CSTRING_H */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1997-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* File CSTRING.H +* +* Contains CString interface +* +* @author Helena Shih +* +* Modification History: +* +* Date Name Description +* 6/17/98 hshih Created. +* 05/03/99 stephen Changed from functions to macros. +* 06/14/99 stephen Added icu_strncat, icu_strncmp, icu_tolower +* +****************************************************************************** +*/ + +#ifndef CSTRING_H +#define CSTRING_H 1 + +#include "unicode/utypes.h" +#include "cmemory.h" +#include +#include +#include + +#define uprv_strcpy(dst, src) U_STANDARD_CPP_NAMESPACE strcpy(dst, src) +#define uprv_strlen(str) U_STANDARD_CPP_NAMESPACE strlen(str) +#define uprv_strcmp(s1, s2) U_STANDARD_CPP_NAMESPACE strcmp(s1, s2) +#define uprv_strcat(dst, src) U_STANDARD_CPP_NAMESPACE strcat(dst, src) +#define uprv_strchr(s, c) U_STANDARD_CPP_NAMESPACE strchr(s, c) +#define uprv_strstr(s, c) U_STANDARD_CPP_NAMESPACE strstr(s, c) +#define uprv_strrchr(s, c) U_STANDARD_CPP_NAMESPACE strrchr(s, c) +#define uprv_strncpy(dst, src, size) U_STANDARD_CPP_NAMESPACE strncpy(dst, src, size) +#define uprv_strncmp(s1, s2, n) U_STANDARD_CPP_NAMESPACE strncmp(s1, s2, n) +#define uprv_strncat(dst, src, n) U_STANDARD_CPP_NAMESPACE strncat(dst, src, n) + +/** + * Is c an ASCII-repertoire letter a-z or A-Z? + * Note: The implementation is specific to whether ICU is compiled for + * an ASCII-based or EBCDIC-based machine. There just does not seem to be a better name for this. + */ +U_CAPI UBool U_EXPORT2 +uprv_isASCIILetter(char c); + +// NOTE: For u_asciiToUpper that takes a UChar, see ustr_imp.h + +U_CAPI char U_EXPORT2 +uprv_toupper(char c); + + +U_CAPI char U_EXPORT2 +uprv_asciitolower(char c); + +U_CAPI char U_EXPORT2 +uprv_ebcdictolower(char c); + +#if U_CHARSET_FAMILY==U_ASCII_FAMILY +# define uprv_tolower uprv_asciitolower +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY +# define uprv_tolower uprv_ebcdictolower +#else +# error U_CHARSET_FAMILY is not valid +#endif + +#define uprv_strtod(source, end) U_STANDARD_CPP_NAMESPACE strtod(source, end) +#define uprv_strtoul(str, end, base) U_STANDARD_CPP_NAMESPACE strtoul(str, end, base) +#define uprv_strtol(str, end, base) U_STANDARD_CPP_NAMESPACE strtol(str, end, base) + +/* Conversion from a digit to the character with radix base from 2-19 */ +/* May need to use U_UPPER_ORDINAL*/ +#define T_CString_itosOffset(a) ((a)<=9?('0'+(a)):('A'+(a)-10)) + +U_CAPI char* U_EXPORT2 +uprv_strdup(const char *src); + +/** + * uprv_malloc n+1 bytes, and copy n bytes from src into the new string. + * Terminate with a null at offset n. If n is -1, works like uprv_strdup + * @param src + * @param n length of the input string, not including null. + * @return new string (owned by caller, use uprv_free to free). + * @internal + */ +U_CAPI char* U_EXPORT2 +uprv_strndup(const char *src, int32_t n); + +U_CAPI char* U_EXPORT2 +T_CString_toLowerCase(char* str); + +U_CAPI char* U_EXPORT2 +T_CString_toUpperCase(char* str); + +U_CAPI int32_t U_EXPORT2 +T_CString_integerToString(char *buffer, int32_t n, int32_t radix); + +U_CAPI int32_t U_EXPORT2 +T_CString_int64ToString(char *buffer, int64_t n, uint32_t radix); + +U_CAPI int32_t U_EXPORT2 +T_CString_stringToInteger(const char *integerString, int32_t radix); + +/** + * Case-insensitive, language-independent string comparison + * limited to the ASCII character repertoire. + */ +U_CAPI int U_EXPORT2 +uprv_stricmp(const char *str1, const char *str2); + +/** + * Case-insensitive, language-independent string comparison + * limited to the ASCII character repertoire. + */ +U_CAPI int U_EXPORT2 +uprv_strnicmp(const char *str1, const char *str2, uint32_t n); + +#endif /* ! CSTRING_H */ diff --git a/deps/icu-small/source/common/cwchar.cpp b/deps/icu-small/source/common/cwchar.cpp index 20c7d71e0f0769..03004f1fe1cfc4 100644 --- a/deps/icu-small/source/common/cwchar.cpp +++ b/deps/icu-small/source/common/cwchar.cpp @@ -1,55 +1,55 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2001, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: cwchar.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2001may25 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !U_HAVE_WCSCPY - -#include "cwchar.h" - -U_CAPI wchar_t *uprv_wcscat(wchar_t *dst, const wchar_t *src) { - wchar_t *start=dst; - while(*dst!=0) { - ++dst; - } - while((*dst=*src)!=0) { - ++dst; - ++src; - } - return start; -} - -U_CAPI wchar_t *uprv_wcscpy(wchar_t *dst, const wchar_t *src) { - wchar_t *start=dst; - while((*dst=*src)!=0) { - ++dst; - ++src; - } - return start; -} - -U_CAPI size_t uprv_wcslen(const wchar_t *src) { - const wchar_t *start=src; - while(*src!=0) { - ++src; - } - return src-start; -} - -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2001, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: cwchar.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2001may25 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !U_HAVE_WCSCPY + +#include "cwchar.h" + +U_CAPI wchar_t *uprv_wcscat(wchar_t *dst, const wchar_t *src) { + wchar_t *start=dst; + while(*dst!=0) { + ++dst; + } + while((*dst=*src)!=0) { + ++dst; + ++src; + } + return start; +} + +U_CAPI wchar_t *uprv_wcscpy(wchar_t *dst, const wchar_t *src) { + wchar_t *start=dst; + while((*dst=*src)!=0) { + ++dst; + ++src; + } + return start; +} + +U_CAPI size_t uprv_wcslen(const wchar_t *src) { + const wchar_t *start=src; + while(*src!=0) { + ++src; + } + return src-start; +} + +#endif + diff --git a/deps/icu-small/source/common/cwchar.h b/deps/icu-small/source/common/cwchar.h index 8fd041a1b9c568..f10a8a6051f449 100644 --- a/deps/icu-small/source/common/cwchar.h +++ b/deps/icu-small/source/common/cwchar.h @@ -1,58 +1,58 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2001, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: cwchar.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2001may25 -* created by: Markus W. Scherer -* -* This file contains ICU-internal definitions of wchar_t operations. -* These definitions were moved here from cstring.h so that fewer -* ICU implementation files include wchar.h. -*/ - -#ifndef __CWCHAR_H__ -#define __CWCHAR_H__ - -#include -#include -#include "unicode/utypes.h" - -/* Do this after utypes.h so that we have U_HAVE_WCHAR_H . */ -#if U_HAVE_WCHAR_H -# include -#endif - -/*===========================================================================*/ -/* Wide-character functions */ -/*===========================================================================*/ - -/* The following are not available on all systems, defined in wchar.h or string.h. */ -#if U_HAVE_WCSCPY -# define uprv_wcscpy wcscpy -# define uprv_wcscat wcscat -# define uprv_wcslen wcslen -#else -U_CAPI wchar_t* U_EXPORT2 -uprv_wcscpy(wchar_t *dst, const wchar_t *src); -U_CAPI wchar_t* U_EXPORT2 -uprv_wcscat(wchar_t *dst, const wchar_t *src); -U_CAPI size_t U_EXPORT2 -uprv_wcslen(const wchar_t *src); -#endif - -/* The following are part of the ANSI C standard, defined in stdlib.h . */ -#define uprv_wcstombs(mbstr, wcstr, count) U_STANDARD_CPP_NAMESPACE wcstombs(mbstr, wcstr, count) -#define uprv_mbstowcs(wcstr, mbstr, count) U_STANDARD_CPP_NAMESPACE mbstowcs(wcstr, mbstr, count) - - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2001, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: cwchar.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2001may25 +* created by: Markus W. Scherer +* +* This file contains ICU-internal definitions of wchar_t operations. +* These definitions were moved here from cstring.h so that fewer +* ICU implementation files include wchar.h. +*/ + +#ifndef __CWCHAR_H__ +#define __CWCHAR_H__ + +#include +#include +#include "unicode/utypes.h" + +/* Do this after utypes.h so that we have U_HAVE_WCHAR_H . */ +#if U_HAVE_WCHAR_H +# include +#endif + +/*===========================================================================*/ +/* Wide-character functions */ +/*===========================================================================*/ + +/* The following are not available on all systems, defined in wchar.h or string.h. */ +#if U_HAVE_WCSCPY +# define uprv_wcscpy wcscpy +# define uprv_wcscat wcscat +# define uprv_wcslen wcslen +#else +U_CAPI wchar_t* U_EXPORT2 +uprv_wcscpy(wchar_t *dst, const wchar_t *src); +U_CAPI wchar_t* U_EXPORT2 +uprv_wcscat(wchar_t *dst, const wchar_t *src); +U_CAPI size_t U_EXPORT2 +uprv_wcslen(const wchar_t *src); +#endif + +/* The following are part of the ANSI C standard, defined in stdlib.h . */ +#define uprv_wcstombs(mbstr, wcstr, count) U_STANDARD_CPP_NAMESPACE wcstombs(mbstr, wcstr, count) +#define uprv_mbstowcs(wcstr, mbstr, count) U_STANDARD_CPP_NAMESPACE mbstowcs(wcstr, mbstr, count) + + +#endif diff --git a/deps/icu-small/source/common/dictbe.cpp b/deps/icu-small/source/common/dictbe.cpp index 768eb49b95c61e..9c288eb08e54e7 100644 --- a/deps/icu-small/source/common/dictbe.cpp +++ b/deps/icu-small/source/common/dictbe.cpp @@ -1,1484 +1,1503 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** - ******************************************************************************* - * Copyright (C) 2006-2016, International Business Machines Corporation - * and others. All Rights Reserved. - ******************************************************************************* - */ - -#include - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "brkeng.h" -#include "dictbe.h" -#include "unicode/uniset.h" -#include "unicode/chariter.h" -#include "unicode/resbund.h" -#include "unicode/ubrk.h" -#include "unicode/usetiter.h" -#include "ubrkimpl.h" -#include "utracimp.h" -#include "uvectr32.h" -#include "uvector.h" -#include "uassert.h" -#include "unicode/normlzr.h" -#include "cmemory.h" -#include "dictionarydata.h" - -U_NAMESPACE_BEGIN - -/* - ****************************************************************** - */ - -DictionaryBreakEngine::DictionaryBreakEngine() { -} - -DictionaryBreakEngine::~DictionaryBreakEngine() { -} - -UBool -DictionaryBreakEngine::handles(UChar32 c) const { - return fSet.contains(c); -} - -int32_t -DictionaryBreakEngine::findBreaks( UText *text, - int32_t startPos, - int32_t endPos, - UVector32 &foundBreaks, - UBool isPhraseBreaking, - UErrorCode& status) const { - if (U_FAILURE(status)) return 0; - (void)startPos; // TODO: remove this param? - int32_t result = 0; - - // Find the span of characters included in the set. - // The span to break begins at the current position in the text, and - // extends towards the start or end of the text, depending on 'reverse'. - - int32_t start = (int32_t)utext_getNativeIndex(text); - int32_t current; - int32_t rangeStart; - int32_t rangeEnd; - UChar32 c = utext_current32(text); - while((current = (int32_t)utext_getNativeIndex(text)) < endPos && fSet.contains(c)) { - utext_next32(text); // TODO: recast loop for postincrement - c = utext_current32(text); - } - rangeStart = start; - rangeEnd = current; - result = divideUpDictionaryRange(text, rangeStart, rangeEnd, foundBreaks, isPhraseBreaking, status); - utext_setNativeIndex(text, current); - - return result; -} - -void -DictionaryBreakEngine::setCharacters( const UnicodeSet &set ) { - fSet = set; - // Compact for caching - fSet.compact(); -} - -/* - ****************************************************************** - * PossibleWord - */ - -// Helper class for improving readability of the Thai/Lao/Khmer word break -// algorithm. The implementation is completely inline. - -// List size, limited by the maximum number of words in the dictionary -// that form a nested sequence. -static const int32_t POSSIBLE_WORD_LIST_MAX = 20; - -class PossibleWord { -private: - // list of word candidate lengths, in increasing length order - // TODO: bytes would be sufficient for word lengths. - int32_t count; // Count of candidates - int32_t prefix; // The longest match with a dictionary word - int32_t offset; // Offset in the text of these candidates - int32_t mark; // The preferred candidate's offset - int32_t current; // The candidate we're currently looking at - int32_t cuLengths[POSSIBLE_WORD_LIST_MAX]; // Word Lengths, in code units. - int32_t cpLengths[POSSIBLE_WORD_LIST_MAX]; // Word Lengths, in code points. - -public: - PossibleWord() : count(0), prefix(0), offset(-1), mark(0), current(0) {} - ~PossibleWord() {} - - // Fill the list of candidates if needed, select the longest, and return the number found - int32_t candidates( UText *text, DictionaryMatcher *dict, int32_t rangeEnd ); - - // Select the currently marked candidate, point after it in the text, and invalidate self - int32_t acceptMarked( UText *text ); - - // Back up from the current candidate to the next shorter one; return true if that exists - // and point the text after it - UBool backUp( UText *text ); - - // Return the longest prefix this candidate location shares with a dictionary word - // Return value is in code points. - int32_t longestPrefix() { return prefix; } - - // Mark the current candidate as the one we like - void markCurrent() { mark = current; } - - // Get length in code points of the marked word. - int32_t markedCPLength() { return cpLengths[mark]; } -}; - - -int32_t PossibleWord::candidates( UText *text, DictionaryMatcher *dict, int32_t rangeEnd ) { - // TODO: If getIndex is too slow, use offset < 0 and add discardAll() - int32_t start = (int32_t)utext_getNativeIndex(text); - if (start != offset) { - offset = start; - count = dict->matches(text, rangeEnd-start, UPRV_LENGTHOF(cuLengths), cuLengths, cpLengths, NULL, &prefix); - // Dictionary leaves text after longest prefix, not longest word. Back up. - if (count <= 0) { - utext_setNativeIndex(text, start); - } - } - if (count > 0) { - utext_setNativeIndex(text, start+cuLengths[count-1]); - } - current = count-1; - mark = current; - return count; -} - -int32_t -PossibleWord::acceptMarked( UText *text ) { - utext_setNativeIndex(text, offset + cuLengths[mark]); - return cuLengths[mark]; -} - - -UBool -PossibleWord::backUp( UText *text ) { - if (current > 0) { - utext_setNativeIndex(text, offset + cuLengths[--current]); - return true; - } - return false; -} - -/* - ****************************************************************** - * ThaiBreakEngine - */ - -// How many words in a row are "good enough"? -static const int32_t THAI_LOOKAHEAD = 3; - -// Will not combine a non-word with a preceding dictionary word longer than this -static const int32_t THAI_ROOT_COMBINE_THRESHOLD = 3; - -// Will not combine a non-word that shares at least this much prefix with a -// dictionary word, with a preceding word -static const int32_t THAI_PREFIX_COMBINE_THRESHOLD = 3; - -// Elision character -static const int32_t THAI_PAIYANNOI = 0x0E2F; - -// Repeat character -static const int32_t THAI_MAIYAMOK = 0x0E46; - -// Minimum word size -static const int32_t THAI_MIN_WORD = 2; - -// Minimum number of characters for two words -static const int32_t THAI_MIN_WORD_SPAN = THAI_MIN_WORD * 2; - -ThaiBreakEngine::ThaiBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status) - : DictionaryBreakEngine(), - fDictionary(adoptDictionary) -{ - UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); - UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Thai"); - UnicodeSet thaiWordSet(UnicodeString(u"[[:Thai:]&[:LineBreak=SA:]]"), status); - if (U_SUCCESS(status)) { - setCharacters(thaiWordSet); - } - fMarkSet.applyPattern(UnicodeString(u"[[:Thai:]&[:LineBreak=SA:]&[:M:]]"), status); - fMarkSet.add(0x0020); - fEndWordSet = thaiWordSet; - fEndWordSet.remove(0x0E31); // MAI HAN-AKAT - fEndWordSet.remove(0x0E40, 0x0E44); // SARA E through SARA AI MAIMALAI - fBeginWordSet.add(0x0E01, 0x0E2E); // KO KAI through HO NOKHUK - fBeginWordSet.add(0x0E40, 0x0E44); // SARA E through SARA AI MAIMALAI - fSuffixSet.add(THAI_PAIYANNOI); - fSuffixSet.add(THAI_MAIYAMOK); - - // Compact for caching. - fMarkSet.compact(); - fEndWordSet.compact(); - fBeginWordSet.compact(); - fSuffixSet.compact(); - UTRACE_EXIT_STATUS(status); -} - -ThaiBreakEngine::~ThaiBreakEngine() { - delete fDictionary; -} - -int32_t -ThaiBreakEngine::divideUpDictionaryRange( UText *text, - int32_t rangeStart, - int32_t rangeEnd, - UVector32 &foundBreaks, - UBool /* isPhraseBreaking */, - UErrorCode& status) const { - if (U_FAILURE(status)) return 0; - utext_setNativeIndex(text, rangeStart); - utext_moveIndex32(text, THAI_MIN_WORD_SPAN); - if (utext_getNativeIndex(text) >= rangeEnd) { - return 0; // Not enough characters for two words - } - utext_setNativeIndex(text, rangeStart); - - - uint32_t wordsFound = 0; - int32_t cpWordLength = 0; // Word Length in Code Points. - int32_t cuWordLength = 0; // Word length in code units (UText native indexing) - int32_t current; - PossibleWord words[THAI_LOOKAHEAD]; - - utext_setNativeIndex(text, rangeStart); - - while (U_SUCCESS(status) && (current = (int32_t)utext_getNativeIndex(text)) < rangeEnd) { - cpWordLength = 0; - cuWordLength = 0; - - // Look for candidate words at the current position - int32_t candidates = words[wordsFound%THAI_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); - - // If we found exactly one, use that - if (candidates == 1) { - cuWordLength = words[wordsFound % THAI_LOOKAHEAD].acceptMarked(text); - cpWordLength = words[wordsFound % THAI_LOOKAHEAD].markedCPLength(); - wordsFound += 1; - } - // If there was more than one, see which one can take us forward the most words - else if (candidates > 1) { - // If we're already at the end of the range, we're done - if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { - goto foundBest; - } - do { - if (words[(wordsFound + 1) % THAI_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) > 0) { - // Followed by another dictionary word; mark first word as a good candidate - words[wordsFound%THAI_LOOKAHEAD].markCurrent(); - - // If we're already at the end of the range, we're done - if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { - goto foundBest; - } - - // See if any of the possible second words is followed by a third word - do { - // If we find a third word, stop right away - if (words[(wordsFound + 2) % THAI_LOOKAHEAD].candidates(text, fDictionary, rangeEnd)) { - words[wordsFound % THAI_LOOKAHEAD].markCurrent(); - goto foundBest; - } - } - while (words[(wordsFound + 1) % THAI_LOOKAHEAD].backUp(text)); - } - } - while (words[wordsFound % THAI_LOOKAHEAD].backUp(text)); -foundBest: - // Set UText position to after the accepted word. - cuWordLength = words[wordsFound % THAI_LOOKAHEAD].acceptMarked(text); - cpWordLength = words[wordsFound % THAI_LOOKAHEAD].markedCPLength(); - wordsFound += 1; - } - - // We come here after having either found a word or not. We look ahead to the - // next word. If it's not a dictionary word, we will combine it with the word we - // just found (if there is one), but only if the preceding word does not exceed - // the threshold. - // The text iterator should now be positioned at the end of the word we found. - - UChar32 uc = 0; - if ((int32_t)utext_getNativeIndex(text) < rangeEnd && cpWordLength < THAI_ROOT_COMBINE_THRESHOLD) { - // if it is a dictionary word, do nothing. If it isn't, then if there is - // no preceding word, or the non-word shares less than the minimum threshold - // of characters with a dictionary word, then scan to resynchronize - if (words[wordsFound % THAI_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 - && (cuWordLength == 0 - || words[wordsFound%THAI_LOOKAHEAD].longestPrefix() < THAI_PREFIX_COMBINE_THRESHOLD)) { - // Look for a plausible word boundary - int32_t remaining = rangeEnd - (current+cuWordLength); - UChar32 pc; - int32_t chars = 0; - for (;;) { - int32_t pcIndex = (int32_t)utext_getNativeIndex(text); - pc = utext_next32(text); - int32_t pcSize = (int32_t)utext_getNativeIndex(text) - pcIndex; - chars += pcSize; - remaining -= pcSize; - if (remaining <= 0) { - break; - } - uc = utext_current32(text); - if (fEndWordSet.contains(pc) && fBeginWordSet.contains(uc)) { - // Maybe. See if it's in the dictionary. - // NOTE: In the original Apple code, checked that the next - // two characters after uc were not 0x0E4C THANTHAKHAT before - // checking the dictionary. That is just a performance filter, - // but it's not clear it's faster than checking the trie. - int32_t num_candidates = words[(wordsFound + 1) % THAI_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); - utext_setNativeIndex(text, current + cuWordLength + chars); - if (num_candidates > 0) { - break; - } - } - } - - // Bump the word count if there wasn't already one - if (cuWordLength <= 0) { - wordsFound += 1; - } - - // Update the length with the passed-over characters - cuWordLength += chars; - } - else { - // Back up to where we were for next iteration - utext_setNativeIndex(text, current+cuWordLength); - } - } - - // Never stop before a combining mark. - int32_t currPos; - while ((currPos = (int32_t)utext_getNativeIndex(text)) < rangeEnd && fMarkSet.contains(utext_current32(text))) { - utext_next32(text); - cuWordLength += (int32_t)utext_getNativeIndex(text) - currPos; - } - - // Look ahead for possible suffixes if a dictionary word does not follow. - // We do this in code rather than using a rule so that the heuristic - // resynch continues to function. For example, one of the suffix characters - // could be a typo in the middle of a word. - if ((int32_t)utext_getNativeIndex(text) < rangeEnd && cuWordLength > 0) { - if (words[wordsFound%THAI_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 - && fSuffixSet.contains(uc = utext_current32(text))) { - if (uc == THAI_PAIYANNOI) { - if (!fSuffixSet.contains(utext_previous32(text))) { - // Skip over previous end and PAIYANNOI - utext_next32(text); - int32_t paiyannoiIndex = (int32_t)utext_getNativeIndex(text); - utext_next32(text); - cuWordLength += (int32_t)utext_getNativeIndex(text) - paiyannoiIndex; // Add PAIYANNOI to word - uc = utext_current32(text); // Fetch next character - } - else { - // Restore prior position - utext_next32(text); - } - } - if (uc == THAI_MAIYAMOK) { - if (utext_previous32(text) != THAI_MAIYAMOK) { - // Skip over previous end and MAIYAMOK - utext_next32(text); - int32_t maiyamokIndex = (int32_t)utext_getNativeIndex(text); - utext_next32(text); - cuWordLength += (int32_t)utext_getNativeIndex(text) - maiyamokIndex; // Add MAIYAMOK to word - } - else { - // Restore prior position - utext_next32(text); - } - } - } - else { - utext_setNativeIndex(text, current+cuWordLength); - } - } - - // Did we find a word on this iteration? If so, push it on the break stack - if (cuWordLength > 0) { - foundBreaks.push((current+cuWordLength), status); - } - } - - // Don't return a break for the end of the dictionary range if there is one there. - if (foundBreaks.peeki() >= rangeEnd) { - (void) foundBreaks.popi(); - wordsFound -= 1; - } - - return wordsFound; -} - -/* - ****************************************************************** - * LaoBreakEngine - */ - -// How many words in a row are "good enough"? -static const int32_t LAO_LOOKAHEAD = 3; - -// Will not combine a non-word with a preceding dictionary word longer than this -static const int32_t LAO_ROOT_COMBINE_THRESHOLD = 3; - -// Will not combine a non-word that shares at least this much prefix with a -// dictionary word, with a preceding word -static const int32_t LAO_PREFIX_COMBINE_THRESHOLD = 3; - -// Minimum word size -static const int32_t LAO_MIN_WORD = 2; - -// Minimum number of characters for two words -static const int32_t LAO_MIN_WORD_SPAN = LAO_MIN_WORD * 2; - -LaoBreakEngine::LaoBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status) - : DictionaryBreakEngine(), - fDictionary(adoptDictionary) -{ - UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); - UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Laoo"); - UnicodeSet laoWordSet(UnicodeString(u"[[:Laoo:]&[:LineBreak=SA:]]"), status); - if (U_SUCCESS(status)) { - setCharacters(laoWordSet); - } - fMarkSet.applyPattern(UnicodeString(u"[[:Laoo:]&[:LineBreak=SA:]&[:M:]]"), status); - fMarkSet.add(0x0020); - fEndWordSet = laoWordSet; - fEndWordSet.remove(0x0EC0, 0x0EC4); // prefix vowels - fBeginWordSet.add(0x0E81, 0x0EAE); // basic consonants (including holes for corresponding Thai characters) - fBeginWordSet.add(0x0EDC, 0x0EDD); // digraph consonants (no Thai equivalent) - fBeginWordSet.add(0x0EC0, 0x0EC4); // prefix vowels - - // Compact for caching. - fMarkSet.compact(); - fEndWordSet.compact(); - fBeginWordSet.compact(); - UTRACE_EXIT_STATUS(status); -} - -LaoBreakEngine::~LaoBreakEngine() { - delete fDictionary; -} - -int32_t -LaoBreakEngine::divideUpDictionaryRange( UText *text, - int32_t rangeStart, - int32_t rangeEnd, - UVector32 &foundBreaks, - UBool /* isPhraseBreaking */, - UErrorCode& status) const { - if (U_FAILURE(status)) return 0; - if ((rangeEnd - rangeStart) < LAO_MIN_WORD_SPAN) { - return 0; // Not enough characters for two words - } - - uint32_t wordsFound = 0; - int32_t cpWordLength = 0; - int32_t cuWordLength = 0; - int32_t current; - PossibleWord words[LAO_LOOKAHEAD]; - - utext_setNativeIndex(text, rangeStart); - - while (U_SUCCESS(status) && (current = (int32_t)utext_getNativeIndex(text)) < rangeEnd) { - cuWordLength = 0; - cpWordLength = 0; - - // Look for candidate words at the current position - int32_t candidates = words[wordsFound%LAO_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); - - // If we found exactly one, use that - if (candidates == 1) { - cuWordLength = words[wordsFound % LAO_LOOKAHEAD].acceptMarked(text); - cpWordLength = words[wordsFound % LAO_LOOKAHEAD].markedCPLength(); - wordsFound += 1; - } - // If there was more than one, see which one can take us forward the most words - else if (candidates > 1) { - // If we're already at the end of the range, we're done - if (utext_getNativeIndex(text) >= rangeEnd) { - goto foundBest; - } - do { - if (words[(wordsFound + 1) % LAO_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) > 0) { - // Followed by another dictionary word; mark first word as a good candidate - words[wordsFound%LAO_LOOKAHEAD].markCurrent(); - - // If we're already at the end of the range, we're done - if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { - goto foundBest; - } - - // See if any of the possible second words is followed by a third word - do { - // If we find a third word, stop right away - if (words[(wordsFound + 2) % LAO_LOOKAHEAD].candidates(text, fDictionary, rangeEnd)) { - words[wordsFound % LAO_LOOKAHEAD].markCurrent(); - goto foundBest; - } - } - while (words[(wordsFound + 1) % LAO_LOOKAHEAD].backUp(text)); - } - } - while (words[wordsFound % LAO_LOOKAHEAD].backUp(text)); -foundBest: - cuWordLength = words[wordsFound % LAO_LOOKAHEAD].acceptMarked(text); - cpWordLength = words[wordsFound % LAO_LOOKAHEAD].markedCPLength(); - wordsFound += 1; - } - - // We come here after having either found a word or not. We look ahead to the - // next word. If it's not a dictionary word, we will combine it with the word we - // just found (if there is one), but only if the preceding word does not exceed - // the threshold. - // The text iterator should now be positioned at the end of the word we found. - if ((int32_t)utext_getNativeIndex(text) < rangeEnd && cpWordLength < LAO_ROOT_COMBINE_THRESHOLD) { - // if it is a dictionary word, do nothing. If it isn't, then if there is - // no preceding word, or the non-word shares less than the minimum threshold - // of characters with a dictionary word, then scan to resynchronize - if (words[wordsFound % LAO_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 - && (cuWordLength == 0 - || words[wordsFound%LAO_LOOKAHEAD].longestPrefix() < LAO_PREFIX_COMBINE_THRESHOLD)) { - // Look for a plausible word boundary - int32_t remaining = rangeEnd - (current + cuWordLength); - UChar32 pc; - UChar32 uc; - int32_t chars = 0; - for (;;) { - int32_t pcIndex = (int32_t)utext_getNativeIndex(text); - pc = utext_next32(text); - int32_t pcSize = (int32_t)utext_getNativeIndex(text) - pcIndex; - chars += pcSize; - remaining -= pcSize; - if (remaining <= 0) { - break; - } - uc = utext_current32(text); - if (fEndWordSet.contains(pc) && fBeginWordSet.contains(uc)) { - // Maybe. See if it's in the dictionary. - // TODO: this looks iffy; compare with old code. - int32_t num_candidates = words[(wordsFound + 1) % LAO_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); - utext_setNativeIndex(text, current + cuWordLength + chars); - if (num_candidates > 0) { - break; - } - } - } - - // Bump the word count if there wasn't already one - if (cuWordLength <= 0) { - wordsFound += 1; - } - - // Update the length with the passed-over characters - cuWordLength += chars; - } - else { - // Back up to where we were for next iteration - utext_setNativeIndex(text, current + cuWordLength); - } - } - - // Never stop before a combining mark. - int32_t currPos; - while ((currPos = (int32_t)utext_getNativeIndex(text)) < rangeEnd && fMarkSet.contains(utext_current32(text))) { - utext_next32(text); - cuWordLength += (int32_t)utext_getNativeIndex(text) - currPos; - } - - // Look ahead for possible suffixes if a dictionary word does not follow. - // We do this in code rather than using a rule so that the heuristic - // resynch continues to function. For example, one of the suffix characters - // could be a typo in the middle of a word. - // NOT CURRENTLY APPLICABLE TO LAO - - // Did we find a word on this iteration? If so, push it on the break stack - if (cuWordLength > 0) { - foundBreaks.push((current+cuWordLength), status); - } - } - - // Don't return a break for the end of the dictionary range if there is one there. - if (foundBreaks.peeki() >= rangeEnd) { - (void) foundBreaks.popi(); - wordsFound -= 1; - } - - return wordsFound; -} - -/* - ****************************************************************** - * BurmeseBreakEngine - */ - -// How many words in a row are "good enough"? -static const int32_t BURMESE_LOOKAHEAD = 3; - -// Will not combine a non-word with a preceding dictionary word longer than this -static const int32_t BURMESE_ROOT_COMBINE_THRESHOLD = 3; - -// Will not combine a non-word that shares at least this much prefix with a -// dictionary word, with a preceding word -static const int32_t BURMESE_PREFIX_COMBINE_THRESHOLD = 3; - -// Minimum word size -static const int32_t BURMESE_MIN_WORD = 2; - -// Minimum number of characters for two words -static const int32_t BURMESE_MIN_WORD_SPAN = BURMESE_MIN_WORD * 2; - -BurmeseBreakEngine::BurmeseBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status) - : DictionaryBreakEngine(), - fDictionary(adoptDictionary) -{ - UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); - UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Mymr"); - fBeginWordSet.add(0x1000, 0x102A); // basic consonants and independent vowels - fEndWordSet.applyPattern(UnicodeString(u"[[:Mymr:]&[:LineBreak=SA:]]"), status); - fMarkSet.applyPattern(UnicodeString(u"[[:Mymr:]&[:LineBreak=SA:]&[:M:]]"), status); - fMarkSet.add(0x0020); - if (U_SUCCESS(status)) { - setCharacters(fEndWordSet); - } - - // Compact for caching. - fMarkSet.compact(); - fEndWordSet.compact(); - fBeginWordSet.compact(); - UTRACE_EXIT_STATUS(status); -} - -BurmeseBreakEngine::~BurmeseBreakEngine() { - delete fDictionary; -} - -int32_t -BurmeseBreakEngine::divideUpDictionaryRange( UText *text, - int32_t rangeStart, - int32_t rangeEnd, - UVector32 &foundBreaks, - UBool /* isPhraseBreaking */, - UErrorCode& status ) const { - if (U_FAILURE(status)) return 0; - if ((rangeEnd - rangeStart) < BURMESE_MIN_WORD_SPAN) { - return 0; // Not enough characters for two words - } - - uint32_t wordsFound = 0; - int32_t cpWordLength = 0; - int32_t cuWordLength = 0; - int32_t current; - PossibleWord words[BURMESE_LOOKAHEAD]; - - utext_setNativeIndex(text, rangeStart); - - while (U_SUCCESS(status) && (current = (int32_t)utext_getNativeIndex(text)) < rangeEnd) { - cuWordLength = 0; - cpWordLength = 0; - - // Look for candidate words at the current position - int32_t candidates = words[wordsFound%BURMESE_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); - - // If we found exactly one, use that - if (candidates == 1) { - cuWordLength = words[wordsFound % BURMESE_LOOKAHEAD].acceptMarked(text); - cpWordLength = words[wordsFound % BURMESE_LOOKAHEAD].markedCPLength(); - wordsFound += 1; - } - // If there was more than one, see which one can take us forward the most words - else if (candidates > 1) { - // If we're already at the end of the range, we're done - if (utext_getNativeIndex(text) >= rangeEnd) { - goto foundBest; - } - do { - if (words[(wordsFound + 1) % BURMESE_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) > 0) { - // Followed by another dictionary word; mark first word as a good candidate - words[wordsFound%BURMESE_LOOKAHEAD].markCurrent(); - - // If we're already at the end of the range, we're done - if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { - goto foundBest; - } - - // See if any of the possible second words is followed by a third word - do { - // If we find a third word, stop right away - if (words[(wordsFound + 2) % BURMESE_LOOKAHEAD].candidates(text, fDictionary, rangeEnd)) { - words[wordsFound % BURMESE_LOOKAHEAD].markCurrent(); - goto foundBest; - } - } - while (words[(wordsFound + 1) % BURMESE_LOOKAHEAD].backUp(text)); - } - } - while (words[wordsFound % BURMESE_LOOKAHEAD].backUp(text)); -foundBest: - cuWordLength = words[wordsFound % BURMESE_LOOKAHEAD].acceptMarked(text); - cpWordLength = words[wordsFound % BURMESE_LOOKAHEAD].markedCPLength(); - wordsFound += 1; - } - - // We come here after having either found a word or not. We look ahead to the - // next word. If it's not a dictionary word, we will combine it with the word we - // just found (if there is one), but only if the preceding word does not exceed - // the threshold. - // The text iterator should now be positioned at the end of the word we found. - if ((int32_t)utext_getNativeIndex(text) < rangeEnd && cpWordLength < BURMESE_ROOT_COMBINE_THRESHOLD) { - // if it is a dictionary word, do nothing. If it isn't, then if there is - // no preceding word, or the non-word shares less than the minimum threshold - // of characters with a dictionary word, then scan to resynchronize - if (words[wordsFound % BURMESE_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 - && (cuWordLength == 0 - || words[wordsFound%BURMESE_LOOKAHEAD].longestPrefix() < BURMESE_PREFIX_COMBINE_THRESHOLD)) { - // Look for a plausible word boundary - int32_t remaining = rangeEnd - (current + cuWordLength); - UChar32 pc; - UChar32 uc; - int32_t chars = 0; - for (;;) { - int32_t pcIndex = (int32_t)utext_getNativeIndex(text); - pc = utext_next32(text); - int32_t pcSize = (int32_t)utext_getNativeIndex(text) - pcIndex; - chars += pcSize; - remaining -= pcSize; - if (remaining <= 0) { - break; - } - uc = utext_current32(text); - if (fEndWordSet.contains(pc) && fBeginWordSet.contains(uc)) { - // Maybe. See if it's in the dictionary. - // TODO: this looks iffy; compare with old code. - int32_t num_candidates = words[(wordsFound + 1) % BURMESE_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); - utext_setNativeIndex(text, current + cuWordLength + chars); - if (num_candidates > 0) { - break; - } - } - } - - // Bump the word count if there wasn't already one - if (cuWordLength <= 0) { - wordsFound += 1; - } - - // Update the length with the passed-over characters - cuWordLength += chars; - } - else { - // Back up to where we were for next iteration - utext_setNativeIndex(text, current + cuWordLength); - } - } - - // Never stop before a combining mark. - int32_t currPos; - while ((currPos = (int32_t)utext_getNativeIndex(text)) < rangeEnd && fMarkSet.contains(utext_current32(text))) { - utext_next32(text); - cuWordLength += (int32_t)utext_getNativeIndex(text) - currPos; - } - - // Look ahead for possible suffixes if a dictionary word does not follow. - // We do this in code rather than using a rule so that the heuristic - // resynch continues to function. For example, one of the suffix characters - // could be a typo in the middle of a word. - // NOT CURRENTLY APPLICABLE TO BURMESE - - // Did we find a word on this iteration? If so, push it on the break stack - if (cuWordLength > 0) { - foundBreaks.push((current+cuWordLength), status); - } - } - - // Don't return a break for the end of the dictionary range if there is one there. - if (foundBreaks.peeki() >= rangeEnd) { - (void) foundBreaks.popi(); - wordsFound -= 1; - } - - return wordsFound; -} - -/* - ****************************************************************** - * KhmerBreakEngine - */ - -// How many words in a row are "good enough"? -static const int32_t KHMER_LOOKAHEAD = 3; - -// Will not combine a non-word with a preceding dictionary word longer than this -static const int32_t KHMER_ROOT_COMBINE_THRESHOLD = 3; - -// Will not combine a non-word that shares at least this much prefix with a -// dictionary word, with a preceding word -static const int32_t KHMER_PREFIX_COMBINE_THRESHOLD = 3; - -// Minimum word size -static const int32_t KHMER_MIN_WORD = 2; - -// Minimum number of characters for two words -static const int32_t KHMER_MIN_WORD_SPAN = KHMER_MIN_WORD * 2; - -KhmerBreakEngine::KhmerBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status) - : DictionaryBreakEngine(), - fDictionary(adoptDictionary) -{ - UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); - UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Khmr"); - UnicodeSet khmerWordSet(UnicodeString(u"[[:Khmr:]&[:LineBreak=SA:]]"), status); - if (U_SUCCESS(status)) { - setCharacters(khmerWordSet); - } - fMarkSet.applyPattern(UnicodeString(u"[[:Khmr:]&[:LineBreak=SA:]&[:M:]]"), status); - fMarkSet.add(0x0020); - fEndWordSet = khmerWordSet; - fBeginWordSet.add(0x1780, 0x17B3); - //fBeginWordSet.add(0x17A3, 0x17A4); // deprecated vowels - //fEndWordSet.remove(0x17A5, 0x17A9); // Khmer independent vowels that can't end a word - //fEndWordSet.remove(0x17B2); // Khmer independent vowel that can't end a word - fEndWordSet.remove(0x17D2); // KHMER SIGN COENG that combines some following characters - //fEndWordSet.remove(0x17B6, 0x17C5); // Remove dependent vowels -// fEndWordSet.remove(0x0E31); // MAI HAN-AKAT -// fEndWordSet.remove(0x0E40, 0x0E44); // SARA E through SARA AI MAIMALAI -// fBeginWordSet.add(0x0E01, 0x0E2E); // KO KAI through HO NOKHUK -// fBeginWordSet.add(0x0E40, 0x0E44); // SARA E through SARA AI MAIMALAI -// fSuffixSet.add(THAI_PAIYANNOI); -// fSuffixSet.add(THAI_MAIYAMOK); - - // Compact for caching. - fMarkSet.compact(); - fEndWordSet.compact(); - fBeginWordSet.compact(); -// fSuffixSet.compact(); - UTRACE_EXIT_STATUS(status); -} - -KhmerBreakEngine::~KhmerBreakEngine() { - delete fDictionary; -} - -int32_t -KhmerBreakEngine::divideUpDictionaryRange( UText *text, - int32_t rangeStart, - int32_t rangeEnd, - UVector32 &foundBreaks, - UBool /* isPhraseBreaking */, - UErrorCode& status ) const { - if (U_FAILURE(status)) return 0; - if ((rangeEnd - rangeStart) < KHMER_MIN_WORD_SPAN) { - return 0; // Not enough characters for two words - } - - uint32_t wordsFound = 0; - int32_t cpWordLength = 0; - int32_t cuWordLength = 0; - int32_t current; - PossibleWord words[KHMER_LOOKAHEAD]; - - utext_setNativeIndex(text, rangeStart); - - while (U_SUCCESS(status) && (current = (int32_t)utext_getNativeIndex(text)) < rangeEnd) { - cuWordLength = 0; - cpWordLength = 0; - - // Look for candidate words at the current position - int32_t candidates = words[wordsFound%KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); - - // If we found exactly one, use that - if (candidates == 1) { - cuWordLength = words[wordsFound % KHMER_LOOKAHEAD].acceptMarked(text); - cpWordLength = words[wordsFound % KHMER_LOOKAHEAD].markedCPLength(); - wordsFound += 1; - } - - // If there was more than one, see which one can take us forward the most words - else if (candidates > 1) { - // If we're already at the end of the range, we're done - if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { - goto foundBest; - } - do { - if (words[(wordsFound + 1) % KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) > 0) { - // Followed by another dictionary word; mark first word as a good candidate - words[wordsFound % KHMER_LOOKAHEAD].markCurrent(); - - // If we're already at the end of the range, we're done - if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { - goto foundBest; - } - - // See if any of the possible second words is followed by a third word - do { - // If we find a third word, stop right away - if (words[(wordsFound + 2) % KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd)) { - words[wordsFound % KHMER_LOOKAHEAD].markCurrent(); - goto foundBest; - } - } - while (words[(wordsFound + 1) % KHMER_LOOKAHEAD].backUp(text)); - } - } - while (words[wordsFound % KHMER_LOOKAHEAD].backUp(text)); -foundBest: - cuWordLength = words[wordsFound % KHMER_LOOKAHEAD].acceptMarked(text); - cpWordLength = words[wordsFound % KHMER_LOOKAHEAD].markedCPLength(); - wordsFound += 1; - } - - // We come here after having either found a word or not. We look ahead to the - // next word. If it's not a dictionary word, we will combine it with the word we - // just found (if there is one), but only if the preceding word does not exceed - // the threshold. - // The text iterator should now be positioned at the end of the word we found. - if ((int32_t)utext_getNativeIndex(text) < rangeEnd && cpWordLength < KHMER_ROOT_COMBINE_THRESHOLD) { - // if it is a dictionary word, do nothing. If it isn't, then if there is - // no preceding word, or the non-word shares less than the minimum threshold - // of characters with a dictionary word, then scan to resynchronize - if (words[wordsFound % KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 - && (cuWordLength == 0 - || words[wordsFound % KHMER_LOOKAHEAD].longestPrefix() < KHMER_PREFIX_COMBINE_THRESHOLD)) { - // Look for a plausible word boundary - int32_t remaining = rangeEnd - (current+cuWordLength); - UChar32 pc; - UChar32 uc; - int32_t chars = 0; - for (;;) { - int32_t pcIndex = (int32_t)utext_getNativeIndex(text); - pc = utext_next32(text); - int32_t pcSize = (int32_t)utext_getNativeIndex(text) - pcIndex; - chars += pcSize; - remaining -= pcSize; - if (remaining <= 0) { - break; - } - uc = utext_current32(text); - if (fEndWordSet.contains(pc) && fBeginWordSet.contains(uc)) { - // Maybe. See if it's in the dictionary. - int32_t num_candidates = words[(wordsFound + 1) % KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); - utext_setNativeIndex(text, current+cuWordLength+chars); - if (num_candidates > 0) { - break; - } - } - } - - // Bump the word count if there wasn't already one - if (cuWordLength <= 0) { - wordsFound += 1; - } - - // Update the length with the passed-over characters - cuWordLength += chars; - } - else { - // Back up to where we were for next iteration - utext_setNativeIndex(text, current+cuWordLength); - } - } - - // Never stop before a combining mark. - int32_t currPos; - while ((currPos = (int32_t)utext_getNativeIndex(text)) < rangeEnd && fMarkSet.contains(utext_current32(text))) { - utext_next32(text); - cuWordLength += (int32_t)utext_getNativeIndex(text) - currPos; - } - - // Look ahead for possible suffixes if a dictionary word does not follow. - // We do this in code rather than using a rule so that the heuristic - // resynch continues to function. For example, one of the suffix characters - // could be a typo in the middle of a word. -// if ((int32_t)utext_getNativeIndex(text) < rangeEnd && wordLength > 0) { -// if (words[wordsFound%KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 -// && fSuffixSet.contains(uc = utext_current32(text))) { -// if (uc == KHMER_PAIYANNOI) { -// if (!fSuffixSet.contains(utext_previous32(text))) { -// // Skip over previous end and PAIYANNOI -// utext_next32(text); -// utext_next32(text); -// wordLength += 1; // Add PAIYANNOI to word -// uc = utext_current32(text); // Fetch next character -// } -// else { -// // Restore prior position -// utext_next32(text); -// } -// } -// if (uc == KHMER_MAIYAMOK) { -// if (utext_previous32(text) != KHMER_MAIYAMOK) { -// // Skip over previous end and MAIYAMOK -// utext_next32(text); -// utext_next32(text); -// wordLength += 1; // Add MAIYAMOK to word -// } -// else { -// // Restore prior position -// utext_next32(text); -// } -// } -// } -// else { -// utext_setNativeIndex(text, current+wordLength); -// } -// } - - // Did we find a word on this iteration? If so, push it on the break stack - if (cuWordLength > 0) { - foundBreaks.push((current+cuWordLength), status); - } - } - - // Don't return a break for the end of the dictionary range if there is one there. - if (foundBreaks.peeki() >= rangeEnd) { - (void) foundBreaks.popi(); - wordsFound -= 1; - } - - return wordsFound; -} - -#if !UCONFIG_NO_NORMALIZATION -/* - ****************************************************************** - * CjkBreakEngine - */ -static const uint32_t kuint32max = 0xFFFFFFFF; -CjkBreakEngine::CjkBreakEngine(DictionaryMatcher *adoptDictionary, LanguageType type, UErrorCode &status) -: DictionaryBreakEngine(), fDictionary(adoptDictionary) { - UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); - UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Hani"); - nfkcNorm2 = Normalizer2::getNFKCInstance(status); - // Korean dictionary only includes Hangul syllables - fHangulWordSet.applyPattern(UnicodeString(u"[\\uac00-\\ud7a3]"), status); - fHangulWordSet.compact(); - // Digits, open puncutation and Alphabetic characters. - fDigitOrOpenPunctuationOrAlphabetSet.applyPattern( - UnicodeString(u"[[:Nd:][:Pi:][:Ps:][:Alphabetic:]]"), status); - fDigitOrOpenPunctuationOrAlphabetSet.compact(); - fClosePunctuationSet.applyPattern(UnicodeString(u"[[:Pc:][:Pd:][:Pe:][:Pf:][:Po:]]"), status); - fClosePunctuationSet.compact(); - - // handle Korean and Japanese/Chinese using different dictionaries - if (type == kKorean) { - if (U_SUCCESS(status)) { - setCharacters(fHangulWordSet); - } - } else { //Chinese and Japanese - UnicodeSet cjSet(UnicodeString(u"[[:Han:][:Hiragana:][:Katakana:]\\u30fc\\uff70\\uff9e\\uff9f]"), status); - if (U_SUCCESS(status)) { - setCharacters(cjSet); - initJapanesePhraseParameter(status); - } - } - UTRACE_EXIT_STATUS(status); -} - -CjkBreakEngine::~CjkBreakEngine(){ - delete fDictionary; -} - -// The katakanaCost values below are based on the length frequencies of all -// katakana phrases in the dictionary -static const int32_t kMaxKatakanaLength = 8; -static const int32_t kMaxKatakanaGroupLength = 20; -static const uint32_t maxSnlp = 255; - -static inline uint32_t getKatakanaCost(int32_t wordLength){ - //TODO: fill array with actual values from dictionary! - static const uint32_t katakanaCost[kMaxKatakanaLength + 1] - = {8192, 984, 408, 240, 204, 252, 300, 372, 480}; - return (wordLength > kMaxKatakanaLength) ? 8192 : katakanaCost[wordLength]; -} - -static inline bool isKatakana(UChar32 value) { - return (value >= 0x30A1 && value <= 0x30FE && value != 0x30FB) || - (value >= 0xFF66 && value <= 0xFF9f); -} - -// Function for accessing internal utext flags. -// Replicates an internal UText function. - -static inline int32_t utext_i32_flag(int32_t bitIndex) { - return (int32_t)1 << bitIndex; -} - -/* - * @param text A UText representing the text - * @param rangeStart The start of the range of dictionary characters - * @param rangeEnd The end of the range of dictionary characters - * @param foundBreaks vector to receive the break positions - * @return The number of breaks found - */ -int32_t -CjkBreakEngine::divideUpDictionaryRange( UText *inText, - int32_t rangeStart, - int32_t rangeEnd, - UVector32 &foundBreaks, - UBool isPhraseBreaking, - UErrorCode& status) const { - if (U_FAILURE(status)) return 0; - if (rangeStart >= rangeEnd) { - return 0; - } - - // UnicodeString version of input UText, NFKC normalized if necessary. - UnicodeString inString; - - // inputMap[inStringIndex] = corresponding native index from UText inText. - // If NULL then mapping is 1:1 - LocalPointer inputMap; - - // if UText has the input string as one contiguous UTF-16 chunk - if ((inText->providerProperties & utext_i32_flag(UTEXT_PROVIDER_STABLE_CHUNKS)) && - inText->chunkNativeStart <= rangeStart && - inText->chunkNativeLimit >= rangeEnd && - inText->nativeIndexingLimit >= rangeEnd - inText->chunkNativeStart) { - - // Input UText is in one contiguous UTF-16 chunk. - // Use Read-only aliasing UnicodeString. - inString.setTo(false, - inText->chunkContents + rangeStart - inText->chunkNativeStart, - rangeEnd - rangeStart); - } else { - // Copy the text from the original inText (UText) to inString (UnicodeString). - // Create a map from UnicodeString indices -> UText offsets. - utext_setNativeIndex(inText, rangeStart); - int32_t limit = rangeEnd; - U_ASSERT(limit <= utext_nativeLength(inText)); - if (limit > utext_nativeLength(inText)) { - limit = (int32_t)utext_nativeLength(inText); - } - inputMap.adoptInsteadAndCheckErrorCode(new UVector32(status), status); - if (U_FAILURE(status)) { - return 0; - } - while (utext_getNativeIndex(inText) < limit) { - int32_t nativePosition = (int32_t)utext_getNativeIndex(inText); - UChar32 c = utext_next32(inText); - U_ASSERT(c != U_SENTINEL); - inString.append(c); - while (inputMap->size() < inString.length()) { - inputMap->addElement(nativePosition, status); - } - } - inputMap->addElement(limit, status); - } - - - if (!nfkcNorm2->isNormalized(inString, status)) { - UnicodeString normalizedInput; - // normalizedMap[normalizedInput position] == original UText position. - LocalPointer normalizedMap(new UVector32(status), status); - if (U_FAILURE(status)) { - return 0; - } - - UnicodeString fragment; - UnicodeString normalizedFragment; - for (int32_t srcI = 0; srcI < inString.length();) { // Once per normalization chunk - fragment.remove(); - int32_t fragmentStartI = srcI; - UChar32 c = inString.char32At(srcI); - for (;;) { - fragment.append(c); - srcI = inString.moveIndex32(srcI, 1); - if (srcI == inString.length()) { - break; - } - c = inString.char32At(srcI); - if (nfkcNorm2->hasBoundaryBefore(c)) { - break; - } - } - nfkcNorm2->normalize(fragment, normalizedFragment, status); - normalizedInput.append(normalizedFragment); - - // Map every position in the normalized chunk to the start of the chunk - // in the original input. - int32_t fragmentOriginalStart = inputMap.isValid() ? - inputMap->elementAti(fragmentStartI) : fragmentStartI+rangeStart; - while (normalizedMap->size() < normalizedInput.length()) { - normalizedMap->addElement(fragmentOriginalStart, status); - if (U_FAILURE(status)) { - break; - } - } - } - U_ASSERT(normalizedMap->size() == normalizedInput.length()); - int32_t nativeEnd = inputMap.isValid() ? - inputMap->elementAti(inString.length()) : inString.length()+rangeStart; - normalizedMap->addElement(nativeEnd, status); - - inputMap = std::move(normalizedMap); - inString = std::move(normalizedInput); - } - - int32_t numCodePts = inString.countChar32(); - if (numCodePts != inString.length()) { - // There are supplementary characters in the input. - // The dictionary will produce boundary positions in terms of code point indexes, - // not in terms of code unit string indexes. - // Use the inputMap mechanism to take care of this in addition to indexing differences - // from normalization and/or UTF-8 input. - UBool hadExistingMap = inputMap.isValid(); - if (!hadExistingMap) { - inputMap.adoptInsteadAndCheckErrorCode(new UVector32(status), status); - if (U_FAILURE(status)) { - return 0; - } - } - int32_t cpIdx = 0; - for (int32_t cuIdx = 0; ; cuIdx = inString.moveIndex32(cuIdx, 1)) { - U_ASSERT(cuIdx >= cpIdx); - if (hadExistingMap) { - inputMap->setElementAt(inputMap->elementAti(cuIdx), cpIdx); - } else { - inputMap->addElement(cuIdx+rangeStart, status); - } - cpIdx++; - if (cuIdx == inString.length()) { - break; - } - } - } - - // bestSnlp[i] is the snlp of the best segmentation of the first i - // code points in the range to be matched. - UVector32 bestSnlp(numCodePts + 1, status); - bestSnlp.addElement(0, status); - for(int32_t i = 1; i <= numCodePts; i++) { - bestSnlp.addElement(kuint32max, status); - } - - - // prev[i] is the index of the last CJK code point in the previous word in - // the best segmentation of the first i characters. - UVector32 prev(numCodePts + 1, status); - for(int32_t i = 0; i <= numCodePts; i++){ - prev.addElement(-1, status); - } - - const int32_t maxWordSize = 20; - UVector32 values(numCodePts, status); - values.setSize(numCodePts); - UVector32 lengths(numCodePts, status); - lengths.setSize(numCodePts); - - UText fu = UTEXT_INITIALIZER; - utext_openUnicodeString(&fu, &inString, &status); - - // Dynamic programming to find the best segmentation. - - // In outer loop, i is the code point index, - // ix is the corresponding string (code unit) index. - // They differ when the string contains supplementary characters. - int32_t ix = 0; - bool is_prev_katakana = false; - for (int32_t i = 0; i < numCodePts; ++i, ix = inString.moveIndex32(ix, 1)) { - if ((uint32_t)bestSnlp.elementAti(i) == kuint32max) { - continue; - } - - int32_t count; - utext_setNativeIndex(&fu, ix); - count = fDictionary->matches(&fu, maxWordSize, numCodePts, - NULL, lengths.getBuffer(), values.getBuffer(), NULL); - // Note: lengths is filled with code point lengths - // The NULL parameter is the ignored code unit lengths. - - // if there are no single character matches found in the dictionary - // starting with this character, treat character as a 1-character word - // with the highest value possible, i.e. the least likely to occur. - // Exclude Korean characters from this treatment, as they should be left - // together by default. - if ((count == 0 || lengths.elementAti(0) != 1) && - !fHangulWordSet.contains(inString.char32At(ix))) { - values.setElementAt(maxSnlp, count); // 255 - lengths.setElementAt(1, count++); - } - - for (int32_t j = 0; j < count; j++) { - uint32_t newSnlp = (uint32_t)bestSnlp.elementAti(i) + (uint32_t)values.elementAti(j); - int32_t ln_j_i = lengths.elementAti(j) + i; - if (newSnlp < (uint32_t)bestSnlp.elementAti(ln_j_i)) { - bestSnlp.setElementAt(newSnlp, ln_j_i); - prev.setElementAt(i, ln_j_i); - } - } - - // In Japanese, - // Katakana word in single character is pretty rare. So we apply - // the following heuristic to Katakana: any continuous run of Katakana - // characters is considered a candidate word with a default cost - // specified in the katakanaCost table according to its length. - - bool is_katakana = isKatakana(inString.char32At(ix)); - int32_t katakanaRunLength = 1; - if (!is_prev_katakana && is_katakana) { - int32_t j = inString.moveIndex32(ix, 1); - // Find the end of the continuous run of Katakana characters - while (j < inString.length() && katakanaRunLength < kMaxKatakanaGroupLength && - isKatakana(inString.char32At(j))) { - j = inString.moveIndex32(j, 1); - katakanaRunLength++; - } - if (katakanaRunLength < kMaxKatakanaGroupLength) { - uint32_t newSnlp = bestSnlp.elementAti(i) + getKatakanaCost(katakanaRunLength); - if (newSnlp < (uint32_t)bestSnlp.elementAti(i+katakanaRunLength)) { - bestSnlp.setElementAt(newSnlp, i+katakanaRunLength); - prev.setElementAt(i, i+katakanaRunLength); // prev[j] = i; - } - } - } - is_prev_katakana = is_katakana; - } - utext_close(&fu); - - // Start pushing the optimal offset index into t_boundary (t for tentative). - // prev[numCodePts] is guaranteed to be meaningful. - // We'll first push in the reverse order, i.e., - // t_boundary[0] = numCodePts, and afterwards do a swap. - UVector32 t_boundary(numCodePts+1, status); - - int32_t numBreaks = 0; - // No segmentation found, set boundary to end of range - if ((uint32_t)bestSnlp.elementAti(numCodePts) == kuint32max) { - t_boundary.addElement(numCodePts, status); - numBreaks++; - } else if (isPhraseBreaking) { - t_boundary.addElement(numCodePts, status); - if(U_SUCCESS(status)) { - numBreaks++; - int32_t prevIdx = numCodePts; - - int32_t codeUnitIdx = -1; - int32_t prevCodeUnitIdx = -1; - int32_t length = -1; - for (int32_t i = prev.elementAti(numCodePts); i > 0; i = prev.elementAti(i)) { - codeUnitIdx = inString.moveIndex32(0, i); - prevCodeUnitIdx = inString.moveIndex32(0, prevIdx); - // Calculate the length by using the code unit. - length = prevCodeUnitIdx - codeUnitIdx; - prevIdx = i; - // Keep the breakpoint if the pattern is not in the fSkipSet and continuous Katakana - // characters don't occur. - if (!fSkipSet.containsKey(inString.tempSubString(codeUnitIdx, length)) - && (!isKatakana(inString.char32At(inString.moveIndex32(codeUnitIdx, -1))) - || !isKatakana(inString.char32At(codeUnitIdx)))) { - t_boundary.addElement(i, status); - numBreaks++; - } - } - } - } else { - for (int32_t i = numCodePts; i > 0; i = prev.elementAti(i)) { - t_boundary.addElement(i, status); - numBreaks++; - } - U_ASSERT(prev.elementAti(t_boundary.elementAti(numBreaks - 1)) == 0); - } - - // Add a break for the start of the dictionary range if there is not one - // there already. - if (foundBreaks.size() == 0 || foundBreaks.peeki() < rangeStart) { - t_boundary.addElement(0, status); - numBreaks++; - } - - // Now that we're done, convert positions in t_boundary[] (indices in - // the normalized input string) back to indices in the original input UText - // while reversing t_boundary and pushing values to foundBreaks. - int32_t prevCPPos = -1; - int32_t prevUTextPos = -1; - int32_t correctedNumBreaks = 0; - for (int32_t i = numBreaks - 1; i >= 0; i--) { - int32_t cpPos = t_boundary.elementAti(i); - U_ASSERT(cpPos > prevCPPos); - int32_t utextPos = inputMap.isValid() ? inputMap->elementAti(cpPos) : cpPos + rangeStart; - U_ASSERT(utextPos >= prevUTextPos); - if (utextPos > prevUTextPos) { - // Boundaries are added to foundBreaks output in ascending order. - U_ASSERT(foundBreaks.size() == 0 || foundBreaks.peeki() < utextPos); - // In phrase breaking, there has to be a breakpoint between Cj character and close - // punctuation. - // E.g.[携帯電話]正しい選択 -> [携帯▁電話]▁正しい▁選択 -> breakpoint between ] and 正 - if (utextPos != rangeStart - || (isPhraseBreaking && utextPos > 0 - && fClosePunctuationSet.contains(utext_char32At(inText, utextPos - 1)))) { - foundBreaks.push(utextPos, status); - correctedNumBreaks++; - } - } else { - // Normalization expanded the input text, the dictionary found a boundary - // within the expansion, giving two boundaries with the same index in the - // original text. Ignore the second. See ticket #12918. - --numBreaks; - } - prevCPPos = cpPos; - prevUTextPos = utextPos; - } - (void)prevCPPos; // suppress compiler warnings about unused variable - - UChar32 nextChar = utext_char32At(inText, rangeEnd); - if (!foundBreaks.isEmpty() && foundBreaks.peeki() == rangeEnd) { - // In phrase breaking, there has to be a breakpoint between Cj character and - // the number/open punctuation. - // E.g. る文字「そうだ、京都」->る▁文字▁「そうだ、▁京都」-> breakpoint between 字 and「 - // E.g. 乗車率90%程度だろうか -> 乗車▁率▁90%▁程度だろうか -> breakpoint between 率 and 9 - // E.g. しかもロゴがUnicode! -> しかも▁ロゴが▁Unicode!-> breakpoint between が and U - if (isPhraseBreaking) { - if (!fDigitOrOpenPunctuationOrAlphabetSet.contains(nextChar)) { - foundBreaks.popi(); - correctedNumBreaks--; - } - } else { - foundBreaks.popi(); - correctedNumBreaks--; - } - } - - // inString goes out of scope - // inputMap goes out of scope - return correctedNumBreaks; -} - -void CjkBreakEngine::initJapanesePhraseParameter(UErrorCode& error) { - loadJapaneseExtensions(error); - loadHiragana(error); -} - -void CjkBreakEngine::loadJapaneseExtensions(UErrorCode& error) { - const char* tag = "extensions"; - ResourceBundle ja(U_ICUDATA_BRKITR, "ja", error); - if (U_SUCCESS(error)) { - ResourceBundle bundle = ja.get(tag, error); - while (U_SUCCESS(error) && bundle.hasNext()) { - fSkipSet.puti(bundle.getNextString(error), 1, error); - } - } -} - -void CjkBreakEngine::loadHiragana(UErrorCode& error) { - UnicodeSet hiraganaWordSet(UnicodeString(u"[:Hiragana:]"), error); - hiraganaWordSet.compact(); - UnicodeSetIterator iterator(hiraganaWordSet); - while (iterator.next()) { - fSkipSet.puti(UnicodeString(iterator.getCodepoint()), 1, error); - } -} -#endif - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** + ******************************************************************************* + * Copyright (C) 2006-2016, International Business Machines Corporation + * and others. All Rights Reserved. + ******************************************************************************* + */ + +#include + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "brkeng.h" +#include "dictbe.h" +#include "unicode/uniset.h" +#include "unicode/chariter.h" +#include "unicode/resbund.h" +#include "unicode/ubrk.h" +#include "unicode/usetiter.h" +#include "ubrkimpl.h" +#include "utracimp.h" +#include "uvectr32.h" +#include "uvector.h" +#include "uassert.h" +#include "unicode/normlzr.h" +#include "cmemory.h" +#include "dictionarydata.h" + +U_NAMESPACE_BEGIN + +/* + ****************************************************************** + */ + +DictionaryBreakEngine::DictionaryBreakEngine() { +} + +DictionaryBreakEngine::~DictionaryBreakEngine() { +} + +UBool +DictionaryBreakEngine::handles(UChar32 c) const { + return fSet.contains(c); +} + +int32_t +DictionaryBreakEngine::findBreaks( UText *text, + int32_t startPos, + int32_t endPos, + UVector32 &foundBreaks, + UBool isPhraseBreaking, + UErrorCode& status) const { + if (U_FAILURE(status)) return 0; + (void)startPos; // TODO: remove this param? + int32_t result = 0; + + // Find the span of characters included in the set. + // The span to break begins at the current position in the text, and + // extends towards the start or end of the text, depending on 'reverse'. + + int32_t start = (int32_t)utext_getNativeIndex(text); + int32_t current; + int32_t rangeStart; + int32_t rangeEnd; + UChar32 c = utext_current32(text); + while((current = (int32_t)utext_getNativeIndex(text)) < endPos && fSet.contains(c)) { + utext_next32(text); // TODO: recast loop for postincrement + c = utext_current32(text); + } + rangeStart = start; + rangeEnd = current; + result = divideUpDictionaryRange(text, rangeStart, rangeEnd, foundBreaks, isPhraseBreaking, status); + utext_setNativeIndex(text, current); + + return result; +} + +void +DictionaryBreakEngine::setCharacters( const UnicodeSet &set ) { + fSet = set; + // Compact for caching + fSet.compact(); +} + +/* + ****************************************************************** + * PossibleWord + */ + +// Helper class for improving readability of the Thai/Lao/Khmer word break +// algorithm. The implementation is completely inline. + +// List size, limited by the maximum number of words in the dictionary +// that form a nested sequence. +static const int32_t POSSIBLE_WORD_LIST_MAX = 20; + +class PossibleWord { +private: + // list of word candidate lengths, in increasing length order + // TODO: bytes would be sufficient for word lengths. + int32_t count; // Count of candidates + int32_t prefix; // The longest match with a dictionary word + int32_t offset; // Offset in the text of these candidates + int32_t mark; // The preferred candidate's offset + int32_t current; // The candidate we're currently looking at + int32_t cuLengths[POSSIBLE_WORD_LIST_MAX]; // Word Lengths, in code units. + int32_t cpLengths[POSSIBLE_WORD_LIST_MAX]; // Word Lengths, in code points. + +public: + PossibleWord() : count(0), prefix(0), offset(-1), mark(0), current(0) {} + ~PossibleWord() {} + + // Fill the list of candidates if needed, select the longest, and return the number found + int32_t candidates( UText *text, DictionaryMatcher *dict, int32_t rangeEnd ); + + // Select the currently marked candidate, point after it in the text, and invalidate self + int32_t acceptMarked( UText *text ); + + // Back up from the current candidate to the next shorter one; return true if that exists + // and point the text after it + UBool backUp( UText *text ); + + // Return the longest prefix this candidate location shares with a dictionary word + // Return value is in code points. + int32_t longestPrefix() { return prefix; } + + // Mark the current candidate as the one we like + void markCurrent() { mark = current; } + + // Get length in code points of the marked word. + int32_t markedCPLength() { return cpLengths[mark]; } +}; + + +int32_t PossibleWord::candidates( UText *text, DictionaryMatcher *dict, int32_t rangeEnd ) { + // TODO: If getIndex is too slow, use offset < 0 and add discardAll() + int32_t start = (int32_t)utext_getNativeIndex(text); + if (start != offset) { + offset = start; + count = dict->matches(text, rangeEnd-start, UPRV_LENGTHOF(cuLengths), cuLengths, cpLengths, nullptr, &prefix); + // Dictionary leaves text after longest prefix, not longest word. Back up. + if (count <= 0) { + utext_setNativeIndex(text, start); + } + } + if (count > 0) { + utext_setNativeIndex(text, start+cuLengths[count-1]); + } + current = count-1; + mark = current; + return count; +} + +int32_t +PossibleWord::acceptMarked( UText *text ) { + utext_setNativeIndex(text, offset + cuLengths[mark]); + return cuLengths[mark]; +} + + +UBool +PossibleWord::backUp( UText *text ) { + if (current > 0) { + utext_setNativeIndex(text, offset + cuLengths[--current]); + return true; + } + return false; +} + +/* + ****************************************************************** + * ThaiBreakEngine + */ + +// How many words in a row are "good enough"? +static const int32_t THAI_LOOKAHEAD = 3; + +// Will not combine a non-word with a preceding dictionary word longer than this +static const int32_t THAI_ROOT_COMBINE_THRESHOLD = 3; + +// Will not combine a non-word that shares at least this much prefix with a +// dictionary word, with a preceding word +static const int32_t THAI_PREFIX_COMBINE_THRESHOLD = 3; + +// Elision character +static const int32_t THAI_PAIYANNOI = 0x0E2F; + +// Repeat character +static const int32_t THAI_MAIYAMOK = 0x0E46; + +// Minimum word size +static const int32_t THAI_MIN_WORD = 2; + +// Minimum number of characters for two words +static const int32_t THAI_MIN_WORD_SPAN = THAI_MIN_WORD * 2; + +ThaiBreakEngine::ThaiBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status) + : DictionaryBreakEngine(), + fDictionary(adoptDictionary) +{ + UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); + UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Thai"); + UnicodeSet thaiWordSet(UnicodeString(u"[[:Thai:]&[:LineBreak=SA:]]"), status); + if (U_SUCCESS(status)) { + setCharacters(thaiWordSet); + } + fMarkSet.applyPattern(UnicodeString(u"[[:Thai:]&[:LineBreak=SA:]&[:M:]]"), status); + fMarkSet.add(0x0020); + fEndWordSet = thaiWordSet; + fEndWordSet.remove(0x0E31); // MAI HAN-AKAT + fEndWordSet.remove(0x0E40, 0x0E44); // SARA E through SARA AI MAIMALAI + fBeginWordSet.add(0x0E01, 0x0E2E); // KO KAI through HO NOKHUK + fBeginWordSet.add(0x0E40, 0x0E44); // SARA E through SARA AI MAIMALAI + fSuffixSet.add(THAI_PAIYANNOI); + fSuffixSet.add(THAI_MAIYAMOK); + + // Compact for caching. + fMarkSet.compact(); + fEndWordSet.compact(); + fBeginWordSet.compact(); + fSuffixSet.compact(); + UTRACE_EXIT_STATUS(status); +} + +ThaiBreakEngine::~ThaiBreakEngine() { + delete fDictionary; +} + +int32_t +ThaiBreakEngine::divideUpDictionaryRange( UText *text, + int32_t rangeStart, + int32_t rangeEnd, + UVector32 &foundBreaks, + UBool /* isPhraseBreaking */, + UErrorCode& status) const { + if (U_FAILURE(status)) return 0; + utext_setNativeIndex(text, rangeStart); + utext_moveIndex32(text, THAI_MIN_WORD_SPAN); + if (utext_getNativeIndex(text) >= rangeEnd) { + return 0; // Not enough characters for two words + } + utext_setNativeIndex(text, rangeStart); + + + uint32_t wordsFound = 0; + int32_t cpWordLength = 0; // Word Length in Code Points. + int32_t cuWordLength = 0; // Word length in code units (UText native indexing) + int32_t current; + PossibleWord words[THAI_LOOKAHEAD]; + + utext_setNativeIndex(text, rangeStart); + + while (U_SUCCESS(status) && (current = (int32_t)utext_getNativeIndex(text)) < rangeEnd) { + cpWordLength = 0; + cuWordLength = 0; + + // Look for candidate words at the current position + int32_t candidates = words[wordsFound%THAI_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); + + // If we found exactly one, use that + if (candidates == 1) { + cuWordLength = words[wordsFound % THAI_LOOKAHEAD].acceptMarked(text); + cpWordLength = words[wordsFound % THAI_LOOKAHEAD].markedCPLength(); + wordsFound += 1; + } + // If there was more than one, see which one can take us forward the most words + else if (candidates > 1) { + // If we're already at the end of the range, we're done + if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { + goto foundBest; + } + do { + if (words[(wordsFound + 1) % THAI_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) > 0) { + // Followed by another dictionary word; mark first word as a good candidate + words[wordsFound%THAI_LOOKAHEAD].markCurrent(); + + // If we're already at the end of the range, we're done + if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { + goto foundBest; + } + + // See if any of the possible second words is followed by a third word + do { + // If we find a third word, stop right away + if (words[(wordsFound + 2) % THAI_LOOKAHEAD].candidates(text, fDictionary, rangeEnd)) { + words[wordsFound % THAI_LOOKAHEAD].markCurrent(); + goto foundBest; + } + } + while (words[(wordsFound + 1) % THAI_LOOKAHEAD].backUp(text)); + } + } + while (words[wordsFound % THAI_LOOKAHEAD].backUp(text)); +foundBest: + // Set UText position to after the accepted word. + cuWordLength = words[wordsFound % THAI_LOOKAHEAD].acceptMarked(text); + cpWordLength = words[wordsFound % THAI_LOOKAHEAD].markedCPLength(); + wordsFound += 1; + } + + // We come here after having either found a word or not. We look ahead to the + // next word. If it's not a dictionary word, we will combine it with the word we + // just found (if there is one), but only if the preceding word does not exceed + // the threshold. + // The text iterator should now be positioned at the end of the word we found. + + UChar32 uc = 0; + if ((int32_t)utext_getNativeIndex(text) < rangeEnd && cpWordLength < THAI_ROOT_COMBINE_THRESHOLD) { + // if it is a dictionary word, do nothing. If it isn't, then if there is + // no preceding word, or the non-word shares less than the minimum threshold + // of characters with a dictionary word, then scan to resynchronize + if (words[wordsFound % THAI_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 + && (cuWordLength == 0 + || words[wordsFound%THAI_LOOKAHEAD].longestPrefix() < THAI_PREFIX_COMBINE_THRESHOLD)) { + // Look for a plausible word boundary + int32_t remaining = rangeEnd - (current+cuWordLength); + UChar32 pc; + int32_t chars = 0; + for (;;) { + int32_t pcIndex = (int32_t)utext_getNativeIndex(text); + pc = utext_next32(text); + int32_t pcSize = (int32_t)utext_getNativeIndex(text) - pcIndex; + chars += pcSize; + remaining -= pcSize; + if (remaining <= 0) { + break; + } + uc = utext_current32(text); + if (fEndWordSet.contains(pc) && fBeginWordSet.contains(uc)) { + // Maybe. See if it's in the dictionary. + // NOTE: In the original Apple code, checked that the next + // two characters after uc were not 0x0E4C THANTHAKHAT before + // checking the dictionary. That is just a performance filter, + // but it's not clear it's faster than checking the trie. + int32_t num_candidates = words[(wordsFound + 1) % THAI_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); + utext_setNativeIndex(text, current + cuWordLength + chars); + if (num_candidates > 0) { + break; + } + } + } + + // Bump the word count if there wasn't already one + if (cuWordLength <= 0) { + wordsFound += 1; + } + + // Update the length with the passed-over characters + cuWordLength += chars; + } + else { + // Back up to where we were for next iteration + utext_setNativeIndex(text, current+cuWordLength); + } + } + + // Never stop before a combining mark. + int32_t currPos; + while ((currPos = (int32_t)utext_getNativeIndex(text)) < rangeEnd && fMarkSet.contains(utext_current32(text))) { + utext_next32(text); + cuWordLength += (int32_t)utext_getNativeIndex(text) - currPos; + } + + // Look ahead for possible suffixes if a dictionary word does not follow. + // We do this in code rather than using a rule so that the heuristic + // resynch continues to function. For example, one of the suffix characters + // could be a typo in the middle of a word. + if ((int32_t)utext_getNativeIndex(text) < rangeEnd && cuWordLength > 0) { + if (words[wordsFound%THAI_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 + && fSuffixSet.contains(uc = utext_current32(text))) { + if (uc == THAI_PAIYANNOI) { + if (!fSuffixSet.contains(utext_previous32(text))) { + // Skip over previous end and PAIYANNOI + utext_next32(text); + int32_t paiyannoiIndex = (int32_t)utext_getNativeIndex(text); + utext_next32(text); + cuWordLength += (int32_t)utext_getNativeIndex(text) - paiyannoiIndex; // Add PAIYANNOI to word + uc = utext_current32(text); // Fetch next character + } + else { + // Restore prior position + utext_next32(text); + } + } + if (uc == THAI_MAIYAMOK) { + if (utext_previous32(text) != THAI_MAIYAMOK) { + // Skip over previous end and MAIYAMOK + utext_next32(text); + int32_t maiyamokIndex = (int32_t)utext_getNativeIndex(text); + utext_next32(text); + cuWordLength += (int32_t)utext_getNativeIndex(text) - maiyamokIndex; // Add MAIYAMOK to word + } + else { + // Restore prior position + utext_next32(text); + } + } + } + else { + utext_setNativeIndex(text, current+cuWordLength); + } + } + + // Did we find a word on this iteration? If so, push it on the break stack + if (cuWordLength > 0) { + foundBreaks.push((current+cuWordLength), status); + } + } + + // Don't return a break for the end of the dictionary range if there is one there. + if (foundBreaks.peeki() >= rangeEnd) { + (void) foundBreaks.popi(); + wordsFound -= 1; + } + + return wordsFound; +} + +/* + ****************************************************************** + * LaoBreakEngine + */ + +// How many words in a row are "good enough"? +static const int32_t LAO_LOOKAHEAD = 3; + +// Will not combine a non-word with a preceding dictionary word longer than this +static const int32_t LAO_ROOT_COMBINE_THRESHOLD = 3; + +// Will not combine a non-word that shares at least this much prefix with a +// dictionary word, with a preceding word +static const int32_t LAO_PREFIX_COMBINE_THRESHOLD = 3; + +// Minimum word size +static const int32_t LAO_MIN_WORD = 2; + +// Minimum number of characters for two words +static const int32_t LAO_MIN_WORD_SPAN = LAO_MIN_WORD * 2; + +LaoBreakEngine::LaoBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status) + : DictionaryBreakEngine(), + fDictionary(adoptDictionary) +{ + UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); + UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Laoo"); + UnicodeSet laoWordSet(UnicodeString(u"[[:Laoo:]&[:LineBreak=SA:]]"), status); + if (U_SUCCESS(status)) { + setCharacters(laoWordSet); + } + fMarkSet.applyPattern(UnicodeString(u"[[:Laoo:]&[:LineBreak=SA:]&[:M:]]"), status); + fMarkSet.add(0x0020); + fEndWordSet = laoWordSet; + fEndWordSet.remove(0x0EC0, 0x0EC4); // prefix vowels + fBeginWordSet.add(0x0E81, 0x0EAE); // basic consonants (including holes for corresponding Thai characters) + fBeginWordSet.add(0x0EDC, 0x0EDD); // digraph consonants (no Thai equivalent) + fBeginWordSet.add(0x0EC0, 0x0EC4); // prefix vowels + + // Compact for caching. + fMarkSet.compact(); + fEndWordSet.compact(); + fBeginWordSet.compact(); + UTRACE_EXIT_STATUS(status); +} + +LaoBreakEngine::~LaoBreakEngine() { + delete fDictionary; +} + +int32_t +LaoBreakEngine::divideUpDictionaryRange( UText *text, + int32_t rangeStart, + int32_t rangeEnd, + UVector32 &foundBreaks, + UBool /* isPhraseBreaking */, + UErrorCode& status) const { + if (U_FAILURE(status)) return 0; + if ((rangeEnd - rangeStart) < LAO_MIN_WORD_SPAN) { + return 0; // Not enough characters for two words + } + + uint32_t wordsFound = 0; + int32_t cpWordLength = 0; + int32_t cuWordLength = 0; + int32_t current; + PossibleWord words[LAO_LOOKAHEAD]; + + utext_setNativeIndex(text, rangeStart); + + while (U_SUCCESS(status) && (current = (int32_t)utext_getNativeIndex(text)) < rangeEnd) { + cuWordLength = 0; + cpWordLength = 0; + + // Look for candidate words at the current position + int32_t candidates = words[wordsFound%LAO_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); + + // If we found exactly one, use that + if (candidates == 1) { + cuWordLength = words[wordsFound % LAO_LOOKAHEAD].acceptMarked(text); + cpWordLength = words[wordsFound % LAO_LOOKAHEAD].markedCPLength(); + wordsFound += 1; + } + // If there was more than one, see which one can take us forward the most words + else if (candidates > 1) { + // If we're already at the end of the range, we're done + if (utext_getNativeIndex(text) >= rangeEnd) { + goto foundBest; + } + do { + if (words[(wordsFound + 1) % LAO_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) > 0) { + // Followed by another dictionary word; mark first word as a good candidate + words[wordsFound%LAO_LOOKAHEAD].markCurrent(); + + // If we're already at the end of the range, we're done + if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { + goto foundBest; + } + + // See if any of the possible second words is followed by a third word + do { + // If we find a third word, stop right away + if (words[(wordsFound + 2) % LAO_LOOKAHEAD].candidates(text, fDictionary, rangeEnd)) { + words[wordsFound % LAO_LOOKAHEAD].markCurrent(); + goto foundBest; + } + } + while (words[(wordsFound + 1) % LAO_LOOKAHEAD].backUp(text)); + } + } + while (words[wordsFound % LAO_LOOKAHEAD].backUp(text)); +foundBest: + cuWordLength = words[wordsFound % LAO_LOOKAHEAD].acceptMarked(text); + cpWordLength = words[wordsFound % LAO_LOOKAHEAD].markedCPLength(); + wordsFound += 1; + } + + // We come here after having either found a word or not. We look ahead to the + // next word. If it's not a dictionary word, we will combine it with the word we + // just found (if there is one), but only if the preceding word does not exceed + // the threshold. + // The text iterator should now be positioned at the end of the word we found. + if ((int32_t)utext_getNativeIndex(text) < rangeEnd && cpWordLength < LAO_ROOT_COMBINE_THRESHOLD) { + // if it is a dictionary word, do nothing. If it isn't, then if there is + // no preceding word, or the non-word shares less than the minimum threshold + // of characters with a dictionary word, then scan to resynchronize + if (words[wordsFound % LAO_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 + && (cuWordLength == 0 + || words[wordsFound%LAO_LOOKAHEAD].longestPrefix() < LAO_PREFIX_COMBINE_THRESHOLD)) { + // Look for a plausible word boundary + int32_t remaining = rangeEnd - (current + cuWordLength); + UChar32 pc; + UChar32 uc; + int32_t chars = 0; + for (;;) { + int32_t pcIndex = (int32_t)utext_getNativeIndex(text); + pc = utext_next32(text); + int32_t pcSize = (int32_t)utext_getNativeIndex(text) - pcIndex; + chars += pcSize; + remaining -= pcSize; + if (remaining <= 0) { + break; + } + uc = utext_current32(text); + if (fEndWordSet.contains(pc) && fBeginWordSet.contains(uc)) { + // Maybe. See if it's in the dictionary. + // TODO: this looks iffy; compare with old code. + int32_t num_candidates = words[(wordsFound + 1) % LAO_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); + utext_setNativeIndex(text, current + cuWordLength + chars); + if (num_candidates > 0) { + break; + } + } + } + + // Bump the word count if there wasn't already one + if (cuWordLength <= 0) { + wordsFound += 1; + } + + // Update the length with the passed-over characters + cuWordLength += chars; + } + else { + // Back up to where we were for next iteration + utext_setNativeIndex(text, current + cuWordLength); + } + } + + // Never stop before a combining mark. + int32_t currPos; + while ((currPos = (int32_t)utext_getNativeIndex(text)) < rangeEnd && fMarkSet.contains(utext_current32(text))) { + utext_next32(text); + cuWordLength += (int32_t)utext_getNativeIndex(text) - currPos; + } + + // Look ahead for possible suffixes if a dictionary word does not follow. + // We do this in code rather than using a rule so that the heuristic + // resynch continues to function. For example, one of the suffix characters + // could be a typo in the middle of a word. + // NOT CURRENTLY APPLICABLE TO LAO + + // Did we find a word on this iteration? If so, push it on the break stack + if (cuWordLength > 0) { + foundBreaks.push((current+cuWordLength), status); + } + } + + // Don't return a break for the end of the dictionary range if there is one there. + if (foundBreaks.peeki() >= rangeEnd) { + (void) foundBreaks.popi(); + wordsFound -= 1; + } + + return wordsFound; +} + +/* + ****************************************************************** + * BurmeseBreakEngine + */ + +// How many words in a row are "good enough"? +static const int32_t BURMESE_LOOKAHEAD = 3; + +// Will not combine a non-word with a preceding dictionary word longer than this +static const int32_t BURMESE_ROOT_COMBINE_THRESHOLD = 3; + +// Will not combine a non-word that shares at least this much prefix with a +// dictionary word, with a preceding word +static const int32_t BURMESE_PREFIX_COMBINE_THRESHOLD = 3; + +// Minimum word size +static const int32_t BURMESE_MIN_WORD = 2; + +// Minimum number of characters for two words +static const int32_t BURMESE_MIN_WORD_SPAN = BURMESE_MIN_WORD * 2; + +BurmeseBreakEngine::BurmeseBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status) + : DictionaryBreakEngine(), + fDictionary(adoptDictionary) +{ + UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); + UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Mymr"); + fBeginWordSet.add(0x1000, 0x102A); // basic consonants and independent vowels + fEndWordSet.applyPattern(UnicodeString(u"[[:Mymr:]&[:LineBreak=SA:]]"), status); + fMarkSet.applyPattern(UnicodeString(u"[[:Mymr:]&[:LineBreak=SA:]&[:M:]]"), status); + fMarkSet.add(0x0020); + if (U_SUCCESS(status)) { + setCharacters(fEndWordSet); + } + + // Compact for caching. + fMarkSet.compact(); + fEndWordSet.compact(); + fBeginWordSet.compact(); + UTRACE_EXIT_STATUS(status); +} + +BurmeseBreakEngine::~BurmeseBreakEngine() { + delete fDictionary; +} + +int32_t +BurmeseBreakEngine::divideUpDictionaryRange( UText *text, + int32_t rangeStart, + int32_t rangeEnd, + UVector32 &foundBreaks, + UBool /* isPhraseBreaking */, + UErrorCode& status ) const { + if (U_FAILURE(status)) return 0; + if ((rangeEnd - rangeStart) < BURMESE_MIN_WORD_SPAN) { + return 0; // Not enough characters for two words + } + + uint32_t wordsFound = 0; + int32_t cpWordLength = 0; + int32_t cuWordLength = 0; + int32_t current; + PossibleWord words[BURMESE_LOOKAHEAD]; + + utext_setNativeIndex(text, rangeStart); + + while (U_SUCCESS(status) && (current = (int32_t)utext_getNativeIndex(text)) < rangeEnd) { + cuWordLength = 0; + cpWordLength = 0; + + // Look for candidate words at the current position + int32_t candidates = words[wordsFound%BURMESE_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); + + // If we found exactly one, use that + if (candidates == 1) { + cuWordLength = words[wordsFound % BURMESE_LOOKAHEAD].acceptMarked(text); + cpWordLength = words[wordsFound % BURMESE_LOOKAHEAD].markedCPLength(); + wordsFound += 1; + } + // If there was more than one, see which one can take us forward the most words + else if (candidates > 1) { + // If we're already at the end of the range, we're done + if (utext_getNativeIndex(text) >= rangeEnd) { + goto foundBest; + } + do { + if (words[(wordsFound + 1) % BURMESE_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) > 0) { + // Followed by another dictionary word; mark first word as a good candidate + words[wordsFound%BURMESE_LOOKAHEAD].markCurrent(); + + // If we're already at the end of the range, we're done + if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { + goto foundBest; + } + + // See if any of the possible second words is followed by a third word + do { + // If we find a third word, stop right away + if (words[(wordsFound + 2) % BURMESE_LOOKAHEAD].candidates(text, fDictionary, rangeEnd)) { + words[wordsFound % BURMESE_LOOKAHEAD].markCurrent(); + goto foundBest; + } + } + while (words[(wordsFound + 1) % BURMESE_LOOKAHEAD].backUp(text)); + } + } + while (words[wordsFound % BURMESE_LOOKAHEAD].backUp(text)); +foundBest: + cuWordLength = words[wordsFound % BURMESE_LOOKAHEAD].acceptMarked(text); + cpWordLength = words[wordsFound % BURMESE_LOOKAHEAD].markedCPLength(); + wordsFound += 1; + } + + // We come here after having either found a word or not. We look ahead to the + // next word. If it's not a dictionary word, we will combine it with the word we + // just found (if there is one), but only if the preceding word does not exceed + // the threshold. + // The text iterator should now be positioned at the end of the word we found. + if ((int32_t)utext_getNativeIndex(text) < rangeEnd && cpWordLength < BURMESE_ROOT_COMBINE_THRESHOLD) { + // if it is a dictionary word, do nothing. If it isn't, then if there is + // no preceding word, or the non-word shares less than the minimum threshold + // of characters with a dictionary word, then scan to resynchronize + if (words[wordsFound % BURMESE_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 + && (cuWordLength == 0 + || words[wordsFound%BURMESE_LOOKAHEAD].longestPrefix() < BURMESE_PREFIX_COMBINE_THRESHOLD)) { + // Look for a plausible word boundary + int32_t remaining = rangeEnd - (current + cuWordLength); + UChar32 pc; + UChar32 uc; + int32_t chars = 0; + for (;;) { + int32_t pcIndex = (int32_t)utext_getNativeIndex(text); + pc = utext_next32(text); + int32_t pcSize = (int32_t)utext_getNativeIndex(text) - pcIndex; + chars += pcSize; + remaining -= pcSize; + if (remaining <= 0) { + break; + } + uc = utext_current32(text); + if (fEndWordSet.contains(pc) && fBeginWordSet.contains(uc)) { + // Maybe. See if it's in the dictionary. + // TODO: this looks iffy; compare with old code. + int32_t num_candidates = words[(wordsFound + 1) % BURMESE_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); + utext_setNativeIndex(text, current + cuWordLength + chars); + if (num_candidates > 0) { + break; + } + } + } + + // Bump the word count if there wasn't already one + if (cuWordLength <= 0) { + wordsFound += 1; + } + + // Update the length with the passed-over characters + cuWordLength += chars; + } + else { + // Back up to where we were for next iteration + utext_setNativeIndex(text, current + cuWordLength); + } + } + + // Never stop before a combining mark. + int32_t currPos; + while ((currPos = (int32_t)utext_getNativeIndex(text)) < rangeEnd && fMarkSet.contains(utext_current32(text))) { + utext_next32(text); + cuWordLength += (int32_t)utext_getNativeIndex(text) - currPos; + } + + // Look ahead for possible suffixes if a dictionary word does not follow. + // We do this in code rather than using a rule so that the heuristic + // resynch continues to function. For example, one of the suffix characters + // could be a typo in the middle of a word. + // NOT CURRENTLY APPLICABLE TO BURMESE + + // Did we find a word on this iteration? If so, push it on the break stack + if (cuWordLength > 0) { + foundBreaks.push((current+cuWordLength), status); + } + } + + // Don't return a break for the end of the dictionary range if there is one there. + if (foundBreaks.peeki() >= rangeEnd) { + (void) foundBreaks.popi(); + wordsFound -= 1; + } + + return wordsFound; +} + +/* + ****************************************************************** + * KhmerBreakEngine + */ + +// How many words in a row are "good enough"? +static const int32_t KHMER_LOOKAHEAD = 3; + +// Will not combine a non-word with a preceding dictionary word longer than this +static const int32_t KHMER_ROOT_COMBINE_THRESHOLD = 3; + +// Will not combine a non-word that shares at least this much prefix with a +// dictionary word, with a preceding word +static const int32_t KHMER_PREFIX_COMBINE_THRESHOLD = 3; + +// Minimum word size +static const int32_t KHMER_MIN_WORD = 2; + +// Minimum number of characters for two words +static const int32_t KHMER_MIN_WORD_SPAN = KHMER_MIN_WORD * 2; + +KhmerBreakEngine::KhmerBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status) + : DictionaryBreakEngine(), + fDictionary(adoptDictionary) +{ + UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); + UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Khmr"); + UnicodeSet khmerWordSet(UnicodeString(u"[[:Khmr:]&[:LineBreak=SA:]]"), status); + if (U_SUCCESS(status)) { + setCharacters(khmerWordSet); + } + fMarkSet.applyPattern(UnicodeString(u"[[:Khmr:]&[:LineBreak=SA:]&[:M:]]"), status); + fMarkSet.add(0x0020); + fEndWordSet = khmerWordSet; + fBeginWordSet.add(0x1780, 0x17B3); + //fBeginWordSet.add(0x17A3, 0x17A4); // deprecated vowels + //fEndWordSet.remove(0x17A5, 0x17A9); // Khmer independent vowels that can't end a word + //fEndWordSet.remove(0x17B2); // Khmer independent vowel that can't end a word + fEndWordSet.remove(0x17D2); // KHMER SIGN COENG that combines some following characters + //fEndWordSet.remove(0x17B6, 0x17C5); // Remove dependent vowels +// fEndWordSet.remove(0x0E31); // MAI HAN-AKAT +// fEndWordSet.remove(0x0E40, 0x0E44); // SARA E through SARA AI MAIMALAI +// fBeginWordSet.add(0x0E01, 0x0E2E); // KO KAI through HO NOKHUK +// fBeginWordSet.add(0x0E40, 0x0E44); // SARA E through SARA AI MAIMALAI +// fSuffixSet.add(THAI_PAIYANNOI); +// fSuffixSet.add(THAI_MAIYAMOK); + + // Compact for caching. + fMarkSet.compact(); + fEndWordSet.compact(); + fBeginWordSet.compact(); +// fSuffixSet.compact(); + UTRACE_EXIT_STATUS(status); +} + +KhmerBreakEngine::~KhmerBreakEngine() { + delete fDictionary; +} + +int32_t +KhmerBreakEngine::divideUpDictionaryRange( UText *text, + int32_t rangeStart, + int32_t rangeEnd, + UVector32 &foundBreaks, + UBool /* isPhraseBreaking */, + UErrorCode& status ) const { + if (U_FAILURE(status)) return 0; + if ((rangeEnd - rangeStart) < KHMER_MIN_WORD_SPAN) { + return 0; // Not enough characters for two words + } + + uint32_t wordsFound = 0; + int32_t cpWordLength = 0; + int32_t cuWordLength = 0; + int32_t current; + PossibleWord words[KHMER_LOOKAHEAD]; + + utext_setNativeIndex(text, rangeStart); + + while (U_SUCCESS(status) && (current = (int32_t)utext_getNativeIndex(text)) < rangeEnd) { + cuWordLength = 0; + cpWordLength = 0; + + // Look for candidate words at the current position + int32_t candidates = words[wordsFound%KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); + + // If we found exactly one, use that + if (candidates == 1) { + cuWordLength = words[wordsFound % KHMER_LOOKAHEAD].acceptMarked(text); + cpWordLength = words[wordsFound % KHMER_LOOKAHEAD].markedCPLength(); + wordsFound += 1; + } + + // If there was more than one, see which one can take us forward the most words + else if (candidates > 1) { + // If we're already at the end of the range, we're done + if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { + goto foundBest; + } + do { + if (words[(wordsFound + 1) % KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) > 0) { + // Followed by another dictionary word; mark first word as a good candidate + words[wordsFound % KHMER_LOOKAHEAD].markCurrent(); + + // If we're already at the end of the range, we're done + if ((int32_t)utext_getNativeIndex(text) >= rangeEnd) { + goto foundBest; + } + + // See if any of the possible second words is followed by a third word + do { + // If we find a third word, stop right away + if (words[(wordsFound + 2) % KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd)) { + words[wordsFound % KHMER_LOOKAHEAD].markCurrent(); + goto foundBest; + } + } + while (words[(wordsFound + 1) % KHMER_LOOKAHEAD].backUp(text)); + } + } + while (words[wordsFound % KHMER_LOOKAHEAD].backUp(text)); +foundBest: + cuWordLength = words[wordsFound % KHMER_LOOKAHEAD].acceptMarked(text); + cpWordLength = words[wordsFound % KHMER_LOOKAHEAD].markedCPLength(); + wordsFound += 1; + } + + // We come here after having either found a word or not. We look ahead to the + // next word. If it's not a dictionary word, we will combine it with the word we + // just found (if there is one), but only if the preceding word does not exceed + // the threshold. + // The text iterator should now be positioned at the end of the word we found. + if ((int32_t)utext_getNativeIndex(text) < rangeEnd && cpWordLength < KHMER_ROOT_COMBINE_THRESHOLD) { + // if it is a dictionary word, do nothing. If it isn't, then if there is + // no preceding word, or the non-word shares less than the minimum threshold + // of characters with a dictionary word, then scan to resynchronize + if (words[wordsFound % KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 + && (cuWordLength == 0 + || words[wordsFound % KHMER_LOOKAHEAD].longestPrefix() < KHMER_PREFIX_COMBINE_THRESHOLD)) { + // Look for a plausible word boundary + int32_t remaining = rangeEnd - (current+cuWordLength); + UChar32 pc; + UChar32 uc; + int32_t chars = 0; + for (;;) { + int32_t pcIndex = (int32_t)utext_getNativeIndex(text); + pc = utext_next32(text); + int32_t pcSize = (int32_t)utext_getNativeIndex(text) - pcIndex; + chars += pcSize; + remaining -= pcSize; + if (remaining <= 0) { + break; + } + uc = utext_current32(text); + if (fEndWordSet.contains(pc) && fBeginWordSet.contains(uc)) { + // Maybe. See if it's in the dictionary. + int32_t num_candidates = words[(wordsFound + 1) % KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd); + utext_setNativeIndex(text, current+cuWordLength+chars); + if (num_candidates > 0) { + break; + } + } + } + + // Bump the word count if there wasn't already one + if (cuWordLength <= 0) { + wordsFound += 1; + } + + // Update the length with the passed-over characters + cuWordLength += chars; + } + else { + // Back up to where we were for next iteration + utext_setNativeIndex(text, current+cuWordLength); + } + } + + // Never stop before a combining mark. + int32_t currPos; + while ((currPos = (int32_t)utext_getNativeIndex(text)) < rangeEnd && fMarkSet.contains(utext_current32(text))) { + utext_next32(text); + cuWordLength += (int32_t)utext_getNativeIndex(text) - currPos; + } + + // Look ahead for possible suffixes if a dictionary word does not follow. + // We do this in code rather than using a rule so that the heuristic + // resynch continues to function. For example, one of the suffix characters + // could be a typo in the middle of a word. +// if ((int32_t)utext_getNativeIndex(text) < rangeEnd && wordLength > 0) { +// if (words[wordsFound%KHMER_LOOKAHEAD].candidates(text, fDictionary, rangeEnd) <= 0 +// && fSuffixSet.contains(uc = utext_current32(text))) { +// if (uc == KHMER_PAIYANNOI) { +// if (!fSuffixSet.contains(utext_previous32(text))) { +// // Skip over previous end and PAIYANNOI +// utext_next32(text); +// utext_next32(text); +// wordLength += 1; // Add PAIYANNOI to word +// uc = utext_current32(text); // Fetch next character +// } +// else { +// // Restore prior position +// utext_next32(text); +// } +// } +// if (uc == KHMER_MAIYAMOK) { +// if (utext_previous32(text) != KHMER_MAIYAMOK) { +// // Skip over previous end and MAIYAMOK +// utext_next32(text); +// utext_next32(text); +// wordLength += 1; // Add MAIYAMOK to word +// } +// else { +// // Restore prior position +// utext_next32(text); +// } +// } +// } +// else { +// utext_setNativeIndex(text, current+wordLength); +// } +// } + + // Did we find a word on this iteration? If so, push it on the break stack + if (cuWordLength > 0) { + foundBreaks.push((current+cuWordLength), status); + } + } + + // Don't return a break for the end of the dictionary range if there is one there. + if (foundBreaks.peeki() >= rangeEnd) { + (void) foundBreaks.popi(); + wordsFound -= 1; + } + + return wordsFound; +} + +#if !UCONFIG_NO_NORMALIZATION +/* + ****************************************************************** + * CjkBreakEngine + */ +static const uint32_t kuint32max = 0xFFFFFFFF; +CjkBreakEngine::CjkBreakEngine(DictionaryMatcher *adoptDictionary, LanguageType type, UErrorCode &status) +: DictionaryBreakEngine(), fDictionary(adoptDictionary), isCj(false) { + UTRACE_ENTRY(UTRACE_UBRK_CREATE_BREAK_ENGINE); + UTRACE_DATA1(UTRACE_INFO, "dictbe=%s", "Hani"); + fMlBreakEngine = nullptr; + nfkcNorm2 = Normalizer2::getNFKCInstance(status); + // Korean dictionary only includes Hangul syllables + fHangulWordSet.applyPattern(UnicodeString(u"[\\uac00-\\ud7a3]"), status); + fHangulWordSet.compact(); + // Digits, open puncutation and Alphabetic characters. + fDigitOrOpenPunctuationOrAlphabetSet.applyPattern( + UnicodeString(u"[[:Nd:][:Pi:][:Ps:][:Alphabetic:]]"), status); + fDigitOrOpenPunctuationOrAlphabetSet.compact(); + fClosePunctuationSet.applyPattern(UnicodeString(u"[[:Pc:][:Pd:][:Pe:][:Pf:][:Po:]]"), status); + fClosePunctuationSet.compact(); + + // handle Korean and Japanese/Chinese using different dictionaries + if (type == kKorean) { + if (U_SUCCESS(status)) { + setCharacters(fHangulWordSet); + } + } else { // Chinese and Japanese + UnicodeSet cjSet(UnicodeString(u"[[:Han:][:Hiragana:][:Katakana:]\\u30fc\\uff70\\uff9e\\uff9f]"), status); + isCj = true; + if (U_SUCCESS(status)) { + setCharacters(cjSet); +#if UCONFIG_USE_ML_PHRASE_BREAKING + fMlBreakEngine = new MlBreakEngine(fDigitOrOpenPunctuationOrAlphabetSet, + fClosePunctuationSet, status); + if (fMlBreakEngine == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } +#else + initJapanesePhraseParameter(status); +#endif + } + } + UTRACE_EXIT_STATUS(status); +} + +CjkBreakEngine::~CjkBreakEngine(){ + delete fDictionary; + delete fMlBreakEngine; +} + +// The katakanaCost values below are based on the length frequencies of all +// katakana phrases in the dictionary +static const int32_t kMaxKatakanaLength = 8; +static const int32_t kMaxKatakanaGroupLength = 20; +static const uint32_t maxSnlp = 255; + +static inline uint32_t getKatakanaCost(int32_t wordLength){ + //TODO: fill array with actual values from dictionary! + static const uint32_t katakanaCost[kMaxKatakanaLength + 1] + = {8192, 984, 408, 240, 204, 252, 300, 372, 480}; + return (wordLength > kMaxKatakanaLength) ? 8192 : katakanaCost[wordLength]; +} + +static inline bool isKatakana(UChar32 value) { + return (value >= 0x30A1 && value <= 0x30FE && value != 0x30FB) || + (value >= 0xFF66 && value <= 0xFF9f); +} + +// Function for accessing internal utext flags. +// Replicates an internal UText function. + +static inline int32_t utext_i32_flag(int32_t bitIndex) { + return (int32_t)1 << bitIndex; +} + +/* + * @param text A UText representing the text + * @param rangeStart The start of the range of dictionary characters + * @param rangeEnd The end of the range of dictionary characters + * @param foundBreaks vector to receive the break positions + * @return The number of breaks found + */ +int32_t +CjkBreakEngine::divideUpDictionaryRange( UText *inText, + int32_t rangeStart, + int32_t rangeEnd, + UVector32 &foundBreaks, + UBool isPhraseBreaking, + UErrorCode& status) const { + if (U_FAILURE(status)) return 0; + if (rangeStart >= rangeEnd) { + return 0; + } + + // UnicodeString version of input UText, NFKC normalized if necessary. + UnicodeString inString; + + // inputMap[inStringIndex] = corresponding native index from UText inText. + // If nullptr then mapping is 1:1 + LocalPointer inputMap; + + // if UText has the input string as one contiguous UTF-16 chunk + if ((inText->providerProperties & utext_i32_flag(UTEXT_PROVIDER_STABLE_CHUNKS)) && + inText->chunkNativeStart <= rangeStart && + inText->chunkNativeLimit >= rangeEnd && + inText->nativeIndexingLimit >= rangeEnd - inText->chunkNativeStart) { + + // Input UText is in one contiguous UTF-16 chunk. + // Use Read-only aliasing UnicodeString. + inString.setTo(false, + inText->chunkContents + rangeStart - inText->chunkNativeStart, + rangeEnd - rangeStart); + } else { + // Copy the text from the original inText (UText) to inString (UnicodeString). + // Create a map from UnicodeString indices -> UText offsets. + utext_setNativeIndex(inText, rangeStart); + int32_t limit = rangeEnd; + U_ASSERT(limit <= utext_nativeLength(inText)); + if (limit > utext_nativeLength(inText)) { + limit = (int32_t)utext_nativeLength(inText); + } + inputMap.adoptInsteadAndCheckErrorCode(new UVector32(status), status); + if (U_FAILURE(status)) { + return 0; + } + while (utext_getNativeIndex(inText) < limit) { + int32_t nativePosition = (int32_t)utext_getNativeIndex(inText); + UChar32 c = utext_next32(inText); + U_ASSERT(c != U_SENTINEL); + inString.append(c); + while (inputMap->size() < inString.length()) { + inputMap->addElement(nativePosition, status); + } + } + inputMap->addElement(limit, status); + } + + + if (!nfkcNorm2->isNormalized(inString, status)) { + UnicodeString normalizedInput; + // normalizedMap[normalizedInput position] == original UText position. + LocalPointer normalizedMap(new UVector32(status), status); + if (U_FAILURE(status)) { + return 0; + } + + UnicodeString fragment; + UnicodeString normalizedFragment; + for (int32_t srcI = 0; srcI < inString.length();) { // Once per normalization chunk + fragment.remove(); + int32_t fragmentStartI = srcI; + UChar32 c = inString.char32At(srcI); + for (;;) { + fragment.append(c); + srcI = inString.moveIndex32(srcI, 1); + if (srcI == inString.length()) { + break; + } + c = inString.char32At(srcI); + if (nfkcNorm2->hasBoundaryBefore(c)) { + break; + } + } + nfkcNorm2->normalize(fragment, normalizedFragment, status); + normalizedInput.append(normalizedFragment); + + // Map every position in the normalized chunk to the start of the chunk + // in the original input. + int32_t fragmentOriginalStart = inputMap.isValid() ? + inputMap->elementAti(fragmentStartI) : fragmentStartI+rangeStart; + while (normalizedMap->size() < normalizedInput.length()) { + normalizedMap->addElement(fragmentOriginalStart, status); + if (U_FAILURE(status)) { + break; + } + } + } + U_ASSERT(normalizedMap->size() == normalizedInput.length()); + int32_t nativeEnd = inputMap.isValid() ? + inputMap->elementAti(inString.length()) : inString.length()+rangeStart; + normalizedMap->addElement(nativeEnd, status); + + inputMap = std::move(normalizedMap); + inString = std::move(normalizedInput); + } + + int32_t numCodePts = inString.countChar32(); + if (numCodePts != inString.length()) { + // There are supplementary characters in the input. + // The dictionary will produce boundary positions in terms of code point indexes, + // not in terms of code unit string indexes. + // Use the inputMap mechanism to take care of this in addition to indexing differences + // from normalization and/or UTF-8 input. + UBool hadExistingMap = inputMap.isValid(); + if (!hadExistingMap) { + inputMap.adoptInsteadAndCheckErrorCode(new UVector32(status), status); + if (U_FAILURE(status)) { + return 0; + } + } + int32_t cpIdx = 0; + for (int32_t cuIdx = 0; ; cuIdx = inString.moveIndex32(cuIdx, 1)) { + U_ASSERT(cuIdx >= cpIdx); + if (hadExistingMap) { + inputMap->setElementAt(inputMap->elementAti(cuIdx), cpIdx); + } else { + inputMap->addElement(cuIdx+rangeStart, status); + } + cpIdx++; + if (cuIdx == inString.length()) { + break; + } + } + } + +#if UCONFIG_USE_ML_PHRASE_BREAKING + // PhraseBreaking is supported in ja and ko; MlBreakEngine only supports ja. + if (isPhraseBreaking && isCj) { + return fMlBreakEngine->divideUpRange(inText, rangeStart, rangeEnd, foundBreaks, inString, + inputMap, status); + } +#endif + + // bestSnlp[i] is the snlp of the best segmentation of the first i + // code points in the range to be matched. + UVector32 bestSnlp(numCodePts + 1, status); + bestSnlp.addElement(0, status); + for(int32_t i = 1; i <= numCodePts; i++) { + bestSnlp.addElement(kuint32max, status); + } + + + // prev[i] is the index of the last CJK code point in the previous word in + // the best segmentation of the first i characters. + UVector32 prev(numCodePts + 1, status); + for(int32_t i = 0; i <= numCodePts; i++){ + prev.addElement(-1, status); + } + + const int32_t maxWordSize = 20; + UVector32 values(numCodePts, status); + values.setSize(numCodePts); + UVector32 lengths(numCodePts, status); + lengths.setSize(numCodePts); + + UText fu = UTEXT_INITIALIZER; + utext_openUnicodeString(&fu, &inString, &status); + + // Dynamic programming to find the best segmentation. + + // In outer loop, i is the code point index, + // ix is the corresponding string (code unit) index. + // They differ when the string contains supplementary characters. + int32_t ix = 0; + bool is_prev_katakana = false; + for (int32_t i = 0; i < numCodePts; ++i, ix = inString.moveIndex32(ix, 1)) { + if ((uint32_t)bestSnlp.elementAti(i) == kuint32max) { + continue; + } + + int32_t count; + utext_setNativeIndex(&fu, ix); + count = fDictionary->matches(&fu, maxWordSize, numCodePts, + nullptr, lengths.getBuffer(), values.getBuffer(), nullptr); + // Note: lengths is filled with code point lengths + // The nullptr parameter is the ignored code unit lengths. + + // if there are no single character matches found in the dictionary + // starting with this character, treat character as a 1-character word + // with the highest value possible, i.e. the least likely to occur. + // Exclude Korean characters from this treatment, as they should be left + // together by default. + if ((count == 0 || lengths.elementAti(0) != 1) && + !fHangulWordSet.contains(inString.char32At(ix))) { + values.setElementAt(maxSnlp, count); // 255 + lengths.setElementAt(1, count++); + } + + for (int32_t j = 0; j < count; j++) { + uint32_t newSnlp = (uint32_t)bestSnlp.elementAti(i) + (uint32_t)values.elementAti(j); + int32_t ln_j_i = lengths.elementAti(j) + i; + if (newSnlp < (uint32_t)bestSnlp.elementAti(ln_j_i)) { + bestSnlp.setElementAt(newSnlp, ln_j_i); + prev.setElementAt(i, ln_j_i); + } + } + + // In Japanese, + // Katakana word in single character is pretty rare. So we apply + // the following heuristic to Katakana: any continuous run of Katakana + // characters is considered a candidate word with a default cost + // specified in the katakanaCost table according to its length. + + bool is_katakana = isKatakana(inString.char32At(ix)); + int32_t katakanaRunLength = 1; + if (!is_prev_katakana && is_katakana) { + int32_t j = inString.moveIndex32(ix, 1); + // Find the end of the continuous run of Katakana characters + while (j < inString.length() && katakanaRunLength < kMaxKatakanaGroupLength && + isKatakana(inString.char32At(j))) { + j = inString.moveIndex32(j, 1); + katakanaRunLength++; + } + if (katakanaRunLength < kMaxKatakanaGroupLength) { + uint32_t newSnlp = bestSnlp.elementAti(i) + getKatakanaCost(katakanaRunLength); + if (newSnlp < (uint32_t)bestSnlp.elementAti(i+katakanaRunLength)) { + bestSnlp.setElementAt(newSnlp, i+katakanaRunLength); + prev.setElementAt(i, i+katakanaRunLength); // prev[j] = i; + } + } + } + is_prev_katakana = is_katakana; + } + utext_close(&fu); + + // Start pushing the optimal offset index into t_boundary (t for tentative). + // prev[numCodePts] is guaranteed to be meaningful. + // We'll first push in the reverse order, i.e., + // t_boundary[0] = numCodePts, and afterwards do a swap. + UVector32 t_boundary(numCodePts+1, status); + + int32_t numBreaks = 0; + // No segmentation found, set boundary to end of range + if ((uint32_t)bestSnlp.elementAti(numCodePts) == kuint32max) { + t_boundary.addElement(numCodePts, status); + numBreaks++; + } else if (isPhraseBreaking) { + t_boundary.addElement(numCodePts, status); + if(U_SUCCESS(status)) { + numBreaks++; + int32_t prevIdx = numCodePts; + + int32_t codeUnitIdx = -1; + int32_t prevCodeUnitIdx = -1; + int32_t length = -1; + for (int32_t i = prev.elementAti(numCodePts); i > 0; i = prev.elementAti(i)) { + codeUnitIdx = inString.moveIndex32(0, i); + prevCodeUnitIdx = inString.moveIndex32(0, prevIdx); + // Calculate the length by using the code unit. + length = prevCodeUnitIdx - codeUnitIdx; + prevIdx = i; + // Keep the breakpoint if the pattern is not in the fSkipSet and continuous Katakana + // characters don't occur. + if (!fSkipSet.containsKey(inString.tempSubString(codeUnitIdx, length)) + && (!isKatakana(inString.char32At(inString.moveIndex32(codeUnitIdx, -1))) + || !isKatakana(inString.char32At(codeUnitIdx)))) { + t_boundary.addElement(i, status); + numBreaks++; + } + } + } + } else { + for (int32_t i = numCodePts; i > 0; i = prev.elementAti(i)) { + t_boundary.addElement(i, status); + numBreaks++; + } + U_ASSERT(prev.elementAti(t_boundary.elementAti(numBreaks - 1)) == 0); + } + + // Add a break for the start of the dictionary range if there is not one + // there already. + if (foundBreaks.size() == 0 || foundBreaks.peeki() < rangeStart) { + t_boundary.addElement(0, status); + numBreaks++; + } + + // Now that we're done, convert positions in t_boundary[] (indices in + // the normalized input string) back to indices in the original input UText + // while reversing t_boundary and pushing values to foundBreaks. + int32_t prevCPPos = -1; + int32_t prevUTextPos = -1; + int32_t correctedNumBreaks = 0; + for (int32_t i = numBreaks - 1; i >= 0; i--) { + int32_t cpPos = t_boundary.elementAti(i); + U_ASSERT(cpPos > prevCPPos); + int32_t utextPos = inputMap.isValid() ? inputMap->elementAti(cpPos) : cpPos + rangeStart; + U_ASSERT(utextPos >= prevUTextPos); + if (utextPos > prevUTextPos) { + // Boundaries are added to foundBreaks output in ascending order. + U_ASSERT(foundBreaks.size() == 0 || foundBreaks.peeki() < utextPos); + // In phrase breaking, there has to be a breakpoint between Cj character and close + // punctuation. + // E.g.[携帯電話]正しい選択 -> [携帯▁電話]▁正しい▁選択 -> breakpoint between ] and 正 + if (utextPos != rangeStart + || (isPhraseBreaking && utextPos > 0 + && fClosePunctuationSet.contains(utext_char32At(inText, utextPos - 1)))) { + foundBreaks.push(utextPos, status); + correctedNumBreaks++; + } + } else { + // Normalization expanded the input text, the dictionary found a boundary + // within the expansion, giving two boundaries with the same index in the + // original text. Ignore the second. See ticket #12918. + --numBreaks; + } + prevCPPos = cpPos; + prevUTextPos = utextPos; + } + (void)prevCPPos; // suppress compiler warnings about unused variable + + UChar32 nextChar = utext_char32At(inText, rangeEnd); + if (!foundBreaks.isEmpty() && foundBreaks.peeki() == rangeEnd) { + // In phrase breaking, there has to be a breakpoint between Cj character and + // the number/open punctuation. + // E.g. る文字「そうだ、京都」->る▁文字▁「そうだ、▁京都」-> breakpoint between 字 and「 + // E.g. 乗車率90%程度だろうか -> 乗車▁率▁90%▁程度だろうか -> breakpoint between 率 and 9 + // E.g. しかもロゴがUnicode! -> しかも▁ロゴが▁Unicode!-> breakpoint between が and U + if (isPhraseBreaking) { + if (!fDigitOrOpenPunctuationOrAlphabetSet.contains(nextChar)) { + foundBreaks.popi(); + correctedNumBreaks--; + } + } else { + foundBreaks.popi(); + correctedNumBreaks--; + } + } + + // inString goes out of scope + // inputMap goes out of scope + return correctedNumBreaks; +} + +void CjkBreakEngine::initJapanesePhraseParameter(UErrorCode& error) { + loadJapaneseExtensions(error); + loadHiragana(error); +} + +void CjkBreakEngine::loadJapaneseExtensions(UErrorCode& error) { + const char* tag = "extensions"; + ResourceBundle ja(U_ICUDATA_BRKITR, "ja", error); + if (U_SUCCESS(error)) { + ResourceBundle bundle = ja.get(tag, error); + while (U_SUCCESS(error) && bundle.hasNext()) { + fSkipSet.puti(bundle.getNextString(error), 1, error); + } + } +} + +void CjkBreakEngine::loadHiragana(UErrorCode& error) { + UnicodeSet hiraganaWordSet(UnicodeString(u"[:Hiragana:]"), error); + hiraganaWordSet.compact(); + UnicodeSetIterator iterator(hiraganaWordSet); + while (iterator.next()) { + fSkipSet.puti(UnicodeString(iterator.getCodepoint()), 1, error); + } +} +#endif + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ + diff --git a/deps/icu-small/source/common/dictbe.h b/deps/icu-small/source/common/dictbe.h index ca1a3c28b7be80..554c358d9446ae 100644 --- a/deps/icu-small/source/common/dictbe.h +++ b/deps/icu-small/source/common/dictbe.h @@ -1,429 +1,433 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** - ******************************************************************************* - * Copyright (C) 2006-2014, International Business Machines Corporation * - * and others. All Rights Reserved. * - ******************************************************************************* - */ - -#ifndef DICTBE_H -#define DICTBE_H - -#include "unicode/utypes.h" -#include "unicode/uniset.h" -#include "unicode/utext.h" - -#include "brkeng.h" -#include "hash.h" -#include "uvectr32.h" - -U_NAMESPACE_BEGIN - -class DictionaryMatcher; -class Normalizer2; - -/******************************************************************* - * DictionaryBreakEngine - */ - -/** - *

DictionaryBreakEngine is a kind of LanguageBreakEngine that uses a - * dictionary to determine language-specific breaks.

- * - *

After it is constructed a DictionaryBreakEngine may be shared between - * threads without synchronization.

- */ -class DictionaryBreakEngine : public LanguageBreakEngine { - private: - /** - * The set of characters handled by this engine - * @internal - */ - - UnicodeSet fSet; - - public: - - /** - *

Constructor

- */ - DictionaryBreakEngine(); - - /** - *

Virtual destructor.

- */ - virtual ~DictionaryBreakEngine(); - - /** - *

Indicate whether this engine handles a particular character for - * a particular kind of break.

- * - * @param c A character which begins a run that the engine might handle - * @return true if this engine handles the particular character and break - * type. - */ - virtual UBool handles(UChar32 c) const override; - - /** - *

Find any breaks within a run in the supplied text.

- * - * @param text A UText representing the text. The iterator is left at - * the end of the run of characters which the engine is capable of handling - * that starts from the first character in the range. - * @param startPos The start of the run within the supplied text. - * @param endPos The end of the run within the supplied text. - * @param foundBreaks vector of int32_t to receive the break positions - * @param status Information on any errors encountered. - * @return The number of breaks found. - */ - virtual int32_t findBreaks( UText *text, - int32_t startPos, - int32_t endPos, - UVector32 &foundBreaks, - UBool isPhraseBreaking, - UErrorCode& status ) const override; - - protected: - - /** - *

Set the character set handled by this engine.

- * - * @param set A UnicodeSet of the set of characters handled by the engine - */ - virtual void setCharacters( const UnicodeSet &set ); - - /** - *

Divide up a range of known dictionary characters handled by this break engine.

- * - * @param text A UText representing the text - * @param rangeStart The start of the range of dictionary characters - * @param rangeEnd The end of the range of dictionary characters - * @param foundBreaks Output of C array of int32_t break positions, or 0 - * @param status Information on any errors encountered. - * @return The number of breaks found - */ - virtual int32_t divideUpDictionaryRange( UText *text, - int32_t rangeStart, - int32_t rangeEnd, - UVector32 &foundBreaks, - UBool isPhraseBreaking, - UErrorCode& status) const = 0; - -}; - -/******************************************************************* - * ThaiBreakEngine - */ - -/** - *

ThaiBreakEngine is a kind of DictionaryBreakEngine that uses a - * dictionary and heuristics to determine Thai-specific breaks.

- * - *

After it is constructed a ThaiBreakEngine may be shared between - * threads without synchronization.

- */ -class ThaiBreakEngine : public DictionaryBreakEngine { - private: - /** - * The set of characters handled by this engine - * @internal - */ - - UnicodeSet fEndWordSet; - UnicodeSet fBeginWordSet; - UnicodeSet fSuffixSet; - UnicodeSet fMarkSet; - DictionaryMatcher *fDictionary; - - public: - - /** - *

Default constructor.

- * - * @param adoptDictionary A DictionaryMatcher to adopt. Deleted when the - * engine is deleted. - */ - ThaiBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status); - - /** - *

Virtual destructor.

- */ - virtual ~ThaiBreakEngine(); - - protected: - /** - *

Divide up a range of known dictionary characters handled by this break engine.

- * - * @param text A UText representing the text - * @param rangeStart The start of the range of dictionary characters - * @param rangeEnd The end of the range of dictionary characters - * @param foundBreaks Output of C array of int32_t break positions, or 0 - * @param status Information on any errors encountered. - * @return The number of breaks found - */ - virtual int32_t divideUpDictionaryRange( UText *text, - int32_t rangeStart, - int32_t rangeEnd, - UVector32 &foundBreaks, - UBool isPhraseBreaking, - UErrorCode& status) const override; - -}; - -/******************************************************************* - * LaoBreakEngine - */ - -/** - *

LaoBreakEngine is a kind of DictionaryBreakEngine that uses a - * dictionary and heuristics to determine Lao-specific breaks.

- * - *

After it is constructed a LaoBreakEngine may be shared between - * threads without synchronization.

- */ -class LaoBreakEngine : public DictionaryBreakEngine { - private: - /** - * The set of characters handled by this engine - * @internal - */ - - UnicodeSet fEndWordSet; - UnicodeSet fBeginWordSet; - UnicodeSet fMarkSet; - DictionaryMatcher *fDictionary; - - public: - - /** - *

Default constructor.

- * - * @param adoptDictionary A DictionaryMatcher to adopt. Deleted when the - * engine is deleted. - */ - LaoBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status); - - /** - *

Virtual destructor.

- */ - virtual ~LaoBreakEngine(); - - protected: - /** - *

Divide up a range of known dictionary characters handled by this break engine.

- * - * @param text A UText representing the text - * @param rangeStart The start of the range of dictionary characters - * @param rangeEnd The end of the range of dictionary characters - * @param foundBreaks Output of C array of int32_t break positions, or 0 - * @param status Information on any errors encountered. - * @return The number of breaks found - */ - virtual int32_t divideUpDictionaryRange( UText *text, - int32_t rangeStart, - int32_t rangeEnd, - UVector32 &foundBreaks, - UBool isPhraseBreaking, - UErrorCode& status) const override; - -}; - -/******************************************************************* - * BurmeseBreakEngine - */ - -/** - *

BurmeseBreakEngine is a kind of DictionaryBreakEngine that uses a - * DictionaryMatcher and heuristics to determine Burmese-specific breaks.

- * - *

After it is constructed a BurmeseBreakEngine may be shared between - * threads without synchronization.

- */ -class BurmeseBreakEngine : public DictionaryBreakEngine { - private: - /** - * The set of characters handled by this engine - * @internal - */ - - UnicodeSet fEndWordSet; - UnicodeSet fBeginWordSet; - UnicodeSet fMarkSet; - DictionaryMatcher *fDictionary; - - public: - - /** - *

Default constructor.

- * - * @param adoptDictionary A DictionaryMatcher to adopt. Deleted when the - * engine is deleted. - */ - BurmeseBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status); - - /** - *

Virtual destructor.

- */ - virtual ~BurmeseBreakEngine(); - - protected: - /** - *

Divide up a range of known dictionary characters.

- * - * @param text A UText representing the text - * @param rangeStart The start of the range of dictionary characters - * @param rangeEnd The end of the range of dictionary characters - * @param foundBreaks Output of C array of int32_t break positions, or 0 - * @param status Information on any errors encountered. - * @return The number of breaks found - */ - virtual int32_t divideUpDictionaryRange( UText *text, - int32_t rangeStart, - int32_t rangeEnd, - UVector32 &foundBreaks, - UBool isPhraseBreaking, - UErrorCode& status) const override; - -}; - -/******************************************************************* - * KhmerBreakEngine - */ - -/** - *

KhmerBreakEngine is a kind of DictionaryBreakEngine that uses a - * DictionaryMatcher and heuristics to determine Khmer-specific breaks.

- * - *

After it is constructed a KhmerBreakEngine may be shared between - * threads without synchronization.

- */ -class KhmerBreakEngine : public DictionaryBreakEngine { - private: - /** - * The set of characters handled by this engine - * @internal - */ - - UnicodeSet fEndWordSet; - UnicodeSet fBeginWordSet; - UnicodeSet fMarkSet; - DictionaryMatcher *fDictionary; - - public: - - /** - *

Default constructor.

- * - * @param adoptDictionary A DictionaryMatcher to adopt. Deleted when the - * engine is deleted. - */ - KhmerBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status); - - /** - *

Virtual destructor.

- */ - virtual ~KhmerBreakEngine(); - - protected: - /** - *

Divide up a range of known dictionary characters.

- * - * @param text A UText representing the text - * @param rangeStart The start of the range of dictionary characters - * @param rangeEnd The end of the range of dictionary characters - * @param foundBreaks Output of C array of int32_t break positions, or 0 - * @param status Information on any errors encountered. - * @return The number of breaks found - */ - virtual int32_t divideUpDictionaryRange( UText *text, - int32_t rangeStart, - int32_t rangeEnd, - UVector32 &foundBreaks, - UBool isPhraseBreaking, - UErrorCode& status) const override; - -}; - -#if !UCONFIG_NO_NORMALIZATION - -/******************************************************************* - * CjkBreakEngine - */ - -//indicates language/script that the CjkBreakEngine will handle -enum LanguageType { - kKorean, - kChineseJapanese -}; - -/** - *

CjkBreakEngine is a kind of DictionaryBreakEngine that uses a - * dictionary with costs associated with each word and - * Viterbi decoding to determine CJK-specific breaks.

- */ -class CjkBreakEngine : public DictionaryBreakEngine { - protected: - /** - * The set of characters handled by this engine - * @internal - */ - UnicodeSet fHangulWordSet; - UnicodeSet fDigitOrOpenPunctuationOrAlphabetSet; - UnicodeSet fClosePunctuationSet; - - DictionaryMatcher *fDictionary; - const Normalizer2 *nfkcNorm2; - - private: - // Load Japanese extensions. - void loadJapaneseExtensions(UErrorCode& error); - // Load Japanese Hiragana. - void loadHiragana(UErrorCode& error); - // Initialize fSkipSet by loading Japanese Hiragana and extensions. - void initJapanesePhraseParameter(UErrorCode& error); - - Hashtable fSkipSet; - - public: - - /** - *

Default constructor.

- * - * @param adoptDictionary A DictionaryMatcher to adopt. Deleted when the - * engine is deleted. The DictionaryMatcher must contain costs for each word - * in order for the dictionary to work properly. - */ - CjkBreakEngine(DictionaryMatcher *adoptDictionary, LanguageType type, UErrorCode &status); - - /** - *

Virtual destructor.

- */ - virtual ~CjkBreakEngine(); - - protected: - /** - *

Divide up a range of known dictionary characters handled by this break engine.

- * - * @param text A UText representing the text - * @param rangeStart The start of the range of dictionary characters - * @param rangeEnd The end of the range of dictionary characters - * @param foundBreaks Output of C array of int32_t break positions, or 0 - * @param status Information on any errors encountered. - * @return The number of breaks found - */ - virtual int32_t divideUpDictionaryRange( UText *text, - int32_t rangeStart, - int32_t rangeEnd, - UVector32 &foundBreaks, - UBool isPhraseBreaking, - UErrorCode& status) const override; - -}; - -#endif - -U_NAMESPACE_END - - /* DICTBE_H */ -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** + ******************************************************************************* + * Copyright (C) 2006-2014, International Business Machines Corporation * + * and others. All Rights Reserved. * + ******************************************************************************* + */ + +#ifndef DICTBE_H +#define DICTBE_H + +#include "unicode/utypes.h" +#include "unicode/uniset.h" +#include "unicode/utext.h" + +#include "brkeng.h" +#include "hash.h" +#include "mlbe.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +class DictionaryMatcher; +class MlBreakEngine; +class Normalizer2; + +/******************************************************************* + * DictionaryBreakEngine + */ + +/** + *

DictionaryBreakEngine is a kind of LanguageBreakEngine that uses a + * dictionary to determine language-specific breaks.

+ * + *

After it is constructed a DictionaryBreakEngine may be shared between + * threads without synchronization.

+ */ +class DictionaryBreakEngine : public LanguageBreakEngine { + private: + /** + * The set of characters handled by this engine + * @internal + */ + + UnicodeSet fSet; + + public: + + /** + *

Constructor

+ */ + DictionaryBreakEngine(); + + /** + *

Virtual destructor.

+ */ + virtual ~DictionaryBreakEngine(); + + /** + *

Indicate whether this engine handles a particular character for + * a particular kind of break.

+ * + * @param c A character which begins a run that the engine might handle + * @return true if this engine handles the particular character and break + * type. + */ + virtual UBool handles(UChar32 c) const override; + + /** + *

Find any breaks within a run in the supplied text.

+ * + * @param text A UText representing the text. The iterator is left at + * the end of the run of characters which the engine is capable of handling + * that starts from the first character in the range. + * @param startPos The start of the run within the supplied text. + * @param endPos The end of the run within the supplied text. + * @param foundBreaks vector of int32_t to receive the break positions + * @param status Information on any errors encountered. + * @return The number of breaks found. + */ + virtual int32_t findBreaks( UText *text, + int32_t startPos, + int32_t endPos, + UVector32 &foundBreaks, + UBool isPhraseBreaking, + UErrorCode& status ) const override; + + protected: + + /** + *

Set the character set handled by this engine.

+ * + * @param set A UnicodeSet of the set of characters handled by the engine + */ + virtual void setCharacters( const UnicodeSet &set ); + + /** + *

Divide up a range of known dictionary characters handled by this break engine.

+ * + * @param text A UText representing the text + * @param rangeStart The start of the range of dictionary characters + * @param rangeEnd The end of the range of dictionary characters + * @param foundBreaks Output of C array of int32_t break positions, or 0 + * @param status Information on any errors encountered. + * @return The number of breaks found + */ + virtual int32_t divideUpDictionaryRange( UText *text, + int32_t rangeStart, + int32_t rangeEnd, + UVector32 &foundBreaks, + UBool isPhraseBreaking, + UErrorCode& status) const = 0; + +}; + +/******************************************************************* + * ThaiBreakEngine + */ + +/** + *

ThaiBreakEngine is a kind of DictionaryBreakEngine that uses a + * dictionary and heuristics to determine Thai-specific breaks.

+ * + *

After it is constructed a ThaiBreakEngine may be shared between + * threads without synchronization.

+ */ +class ThaiBreakEngine : public DictionaryBreakEngine { + private: + /** + * The set of characters handled by this engine + * @internal + */ + + UnicodeSet fEndWordSet; + UnicodeSet fBeginWordSet; + UnicodeSet fSuffixSet; + UnicodeSet fMarkSet; + DictionaryMatcher *fDictionary; + + public: + + /** + *

Default constructor.

+ * + * @param adoptDictionary A DictionaryMatcher to adopt. Deleted when the + * engine is deleted. + */ + ThaiBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status); + + /** + *

Virtual destructor.

+ */ + virtual ~ThaiBreakEngine(); + + protected: + /** + *

Divide up a range of known dictionary characters handled by this break engine.

+ * + * @param text A UText representing the text + * @param rangeStart The start of the range of dictionary characters + * @param rangeEnd The end of the range of dictionary characters + * @param foundBreaks Output of C array of int32_t break positions, or 0 + * @param status Information on any errors encountered. + * @return The number of breaks found + */ + virtual int32_t divideUpDictionaryRange( UText *text, + int32_t rangeStart, + int32_t rangeEnd, + UVector32 &foundBreaks, + UBool isPhraseBreaking, + UErrorCode& status) const override; + +}; + +/******************************************************************* + * LaoBreakEngine + */ + +/** + *

LaoBreakEngine is a kind of DictionaryBreakEngine that uses a + * dictionary and heuristics to determine Lao-specific breaks.

+ * + *

After it is constructed a LaoBreakEngine may be shared between + * threads without synchronization.

+ */ +class LaoBreakEngine : public DictionaryBreakEngine { + private: + /** + * The set of characters handled by this engine + * @internal + */ + + UnicodeSet fEndWordSet; + UnicodeSet fBeginWordSet; + UnicodeSet fMarkSet; + DictionaryMatcher *fDictionary; + + public: + + /** + *

Default constructor.

+ * + * @param adoptDictionary A DictionaryMatcher to adopt. Deleted when the + * engine is deleted. + */ + LaoBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status); + + /** + *

Virtual destructor.

+ */ + virtual ~LaoBreakEngine(); + + protected: + /** + *

Divide up a range of known dictionary characters handled by this break engine.

+ * + * @param text A UText representing the text + * @param rangeStart The start of the range of dictionary characters + * @param rangeEnd The end of the range of dictionary characters + * @param foundBreaks Output of C array of int32_t break positions, or 0 + * @param status Information on any errors encountered. + * @return The number of breaks found + */ + virtual int32_t divideUpDictionaryRange( UText *text, + int32_t rangeStart, + int32_t rangeEnd, + UVector32 &foundBreaks, + UBool isPhraseBreaking, + UErrorCode& status) const override; + +}; + +/******************************************************************* + * BurmeseBreakEngine + */ + +/** + *

BurmeseBreakEngine is a kind of DictionaryBreakEngine that uses a + * DictionaryMatcher and heuristics to determine Burmese-specific breaks.

+ * + *

After it is constructed a BurmeseBreakEngine may be shared between + * threads without synchronization.

+ */ +class BurmeseBreakEngine : public DictionaryBreakEngine { + private: + /** + * The set of characters handled by this engine + * @internal + */ + + UnicodeSet fEndWordSet; + UnicodeSet fBeginWordSet; + UnicodeSet fMarkSet; + DictionaryMatcher *fDictionary; + + public: + + /** + *

Default constructor.

+ * + * @param adoptDictionary A DictionaryMatcher to adopt. Deleted when the + * engine is deleted. + */ + BurmeseBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status); + + /** + *

Virtual destructor.

+ */ + virtual ~BurmeseBreakEngine(); + + protected: + /** + *

Divide up a range of known dictionary characters.

+ * + * @param text A UText representing the text + * @param rangeStart The start of the range of dictionary characters + * @param rangeEnd The end of the range of dictionary characters + * @param foundBreaks Output of C array of int32_t break positions, or 0 + * @param status Information on any errors encountered. + * @return The number of breaks found + */ + virtual int32_t divideUpDictionaryRange( UText *text, + int32_t rangeStart, + int32_t rangeEnd, + UVector32 &foundBreaks, + UBool isPhraseBreaking, + UErrorCode& status) const override; + +}; + +/******************************************************************* + * KhmerBreakEngine + */ + +/** + *

KhmerBreakEngine is a kind of DictionaryBreakEngine that uses a + * DictionaryMatcher and heuristics to determine Khmer-specific breaks.

+ * + *

After it is constructed a KhmerBreakEngine may be shared between + * threads without synchronization.

+ */ +class KhmerBreakEngine : public DictionaryBreakEngine { + private: + /** + * The set of characters handled by this engine + * @internal + */ + + UnicodeSet fEndWordSet; + UnicodeSet fBeginWordSet; + UnicodeSet fMarkSet; + DictionaryMatcher *fDictionary; + + public: + + /** + *

Default constructor.

+ * + * @param adoptDictionary A DictionaryMatcher to adopt. Deleted when the + * engine is deleted. + */ + KhmerBreakEngine(DictionaryMatcher *adoptDictionary, UErrorCode &status); + + /** + *

Virtual destructor.

+ */ + virtual ~KhmerBreakEngine(); + + protected: + /** + *

Divide up a range of known dictionary characters.

+ * + * @param text A UText representing the text + * @param rangeStart The start of the range of dictionary characters + * @param rangeEnd The end of the range of dictionary characters + * @param foundBreaks Output of C array of int32_t break positions, or 0 + * @param status Information on any errors encountered. + * @return The number of breaks found + */ + virtual int32_t divideUpDictionaryRange( UText *text, + int32_t rangeStart, + int32_t rangeEnd, + UVector32 &foundBreaks, + UBool isPhraseBreaking, + UErrorCode& status) const override; + +}; + +#if !UCONFIG_NO_NORMALIZATION + +/******************************************************************* + * CjkBreakEngine + */ + +//indicates language/script that the CjkBreakEngine will handle +enum LanguageType { + kKorean, + kChineseJapanese +}; + +/** + *

CjkBreakEngine is a kind of DictionaryBreakEngine that uses a + * dictionary with costs associated with each word and + * Viterbi decoding to determine CJK-specific breaks.

+ */ +class CjkBreakEngine : public DictionaryBreakEngine { + protected: + /** + * The set of characters handled by this engine + * @internal + */ + UnicodeSet fHangulWordSet; + UnicodeSet fDigitOrOpenPunctuationOrAlphabetSet; + UnicodeSet fClosePunctuationSet; + + DictionaryMatcher *fDictionary; + const Normalizer2 *nfkcNorm2; + MlBreakEngine *fMlBreakEngine; + bool isCj; + + private: + // Load Japanese extensions. + void loadJapaneseExtensions(UErrorCode& error); + // Load Japanese Hiragana. + void loadHiragana(UErrorCode& error); + // Initialize fSkipSet by loading Japanese Hiragana and extensions. + void initJapanesePhraseParameter(UErrorCode& error); + + Hashtable fSkipSet; + + public: + + /** + *

Default constructor.

+ * + * @param adoptDictionary A DictionaryMatcher to adopt. Deleted when the + * engine is deleted. The DictionaryMatcher must contain costs for each word + * in order for the dictionary to work properly. + */ + CjkBreakEngine(DictionaryMatcher *adoptDictionary, LanguageType type, UErrorCode &status); + + /** + *

Virtual destructor.

+ */ + virtual ~CjkBreakEngine(); + + protected: + /** + *

Divide up a range of known dictionary characters handled by this break engine.

+ * + * @param text A UText representing the text + * @param rangeStart The start of the range of dictionary characters + * @param rangeEnd The end of the range of dictionary characters + * @param foundBreaks Output of C array of int32_t break positions, or 0 + * @param status Information on any errors encountered. + * @return The number of breaks found + */ + virtual int32_t divideUpDictionaryRange( UText *text, + int32_t rangeStart, + int32_t rangeEnd, + UVector32 &foundBreaks, + UBool isPhraseBreaking, + UErrorCode& status) const override; + +}; + +#endif + +U_NAMESPACE_END + + /* DICTBE_H */ +#endif diff --git a/deps/icu-small/source/common/dictionarydata.cpp b/deps/icu-small/source/common/dictionarydata.cpp index 6e2dbee5b618be..ac72e7653f0336 100644 --- a/deps/icu-small/source/common/dictionarydata.cpp +++ b/deps/icu-small/source/common/dictionarydata.cpp @@ -1,242 +1,242 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2014-2016, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* dictionarydata.h -* -* created on: 2012may31 -* created by: Markus W. Scherer & Maxime Serrano -*/ - -#include "dictionarydata.h" -#include "unicode/ucharstrie.h" -#include "unicode/bytestrie.h" -#include "unicode/udata.h" -#include "cmemory.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -U_NAMESPACE_BEGIN - -const int32_t DictionaryData::TRIE_TYPE_BYTES = 0; -const int32_t DictionaryData::TRIE_TYPE_UCHARS = 1; -const int32_t DictionaryData::TRIE_TYPE_MASK = 7; -const int32_t DictionaryData::TRIE_HAS_VALUES = 8; - -const int32_t DictionaryData::TRANSFORM_NONE = 0; -const int32_t DictionaryData::TRANSFORM_TYPE_OFFSET = 0x1000000; -const int32_t DictionaryData::TRANSFORM_TYPE_MASK = 0x7f000000; -const int32_t DictionaryData::TRANSFORM_OFFSET_MASK = 0x1fffff; - -DictionaryMatcher::~DictionaryMatcher() { -} - -UCharsDictionaryMatcher::~UCharsDictionaryMatcher() { - udata_close(file); -} - -int32_t UCharsDictionaryMatcher::getType() const { - return DictionaryData::TRIE_TYPE_UCHARS; -} - -int32_t UCharsDictionaryMatcher::matches(UText *text, int32_t maxLength, int32_t limit, - int32_t *lengths, int32_t *cpLengths, int32_t *values, - int32_t *prefix) const { - - UCharsTrie uct(characters); - int32_t startingTextIndex = (int32_t)utext_getNativeIndex(text); - int32_t wordCount = 0; - int32_t codePointsMatched = 0; - - for (UChar32 c = utext_next32(text); c >= 0; c=utext_next32(text)) { - UStringTrieResult result = (codePointsMatched == 0) ? uct.first(c) : uct.next(c); - int32_t lengthMatched = (int32_t)utext_getNativeIndex(text) - startingTextIndex; - codePointsMatched += 1; - if (USTRINGTRIE_HAS_VALUE(result)) { - if (wordCount < limit) { - if (values != NULL) { - values[wordCount] = uct.getValue(); - } - if (lengths != NULL) { - lengths[wordCount] = lengthMatched; - } - if (cpLengths != NULL) { - cpLengths[wordCount] = codePointsMatched; - } - ++wordCount; - } - if (result == USTRINGTRIE_FINAL_VALUE) { - break; - } - } - else if (result == USTRINGTRIE_NO_MATCH) { - break; - } - if (lengthMatched >= maxLength) { - break; - } - } - - if (prefix != NULL) { - *prefix = codePointsMatched; - } - return wordCount; -} - -BytesDictionaryMatcher::~BytesDictionaryMatcher() { - udata_close(file); -} - -UChar32 BytesDictionaryMatcher::transform(UChar32 c) const { - if ((transformConstant & DictionaryData::TRANSFORM_TYPE_MASK) == DictionaryData::TRANSFORM_TYPE_OFFSET) { - if (c == 0x200D) { - return 0xFF; - } else if (c == 0x200C) { - return 0xFE; - } - int32_t delta = c - (transformConstant & DictionaryData::TRANSFORM_OFFSET_MASK); - if (delta < 0 || 0xFD < delta) { - return U_SENTINEL; - } - return (UChar32)delta; - } - return c; -} - -int32_t BytesDictionaryMatcher::getType() const { - return DictionaryData::TRIE_TYPE_BYTES; -} - -int32_t BytesDictionaryMatcher::matches(UText *text, int32_t maxLength, int32_t limit, - int32_t *lengths, int32_t *cpLengths, int32_t *values, - int32_t *prefix) const { - BytesTrie bt(characters); - int32_t startingTextIndex = (int32_t)utext_getNativeIndex(text); - int32_t wordCount = 0; - int32_t codePointsMatched = 0; - - for (UChar32 c = utext_next32(text); c >= 0; c=utext_next32(text)) { - UStringTrieResult result = (codePointsMatched == 0) ? bt.first(transform(c)) : bt.next(transform(c)); - int32_t lengthMatched = (int32_t)utext_getNativeIndex(text) - startingTextIndex; - codePointsMatched += 1; - if (USTRINGTRIE_HAS_VALUE(result)) { - if (wordCount < limit) { - if (values != NULL) { - values[wordCount] = bt.getValue(); - } - if (lengths != NULL) { - lengths[wordCount] = lengthMatched; - } - if (cpLengths != NULL) { - cpLengths[wordCount] = codePointsMatched; - } - ++wordCount; - } - if (result == USTRINGTRIE_FINAL_VALUE) { - break; - } - } - else if (result == USTRINGTRIE_NO_MATCH) { - break; - } - if (lengthMatched >= maxLength) { - break; - } - } - - if (prefix != NULL) { - *prefix = codePointsMatched; - } - return wordCount; -} - - -U_NAMESPACE_END - -U_NAMESPACE_USE - -U_CAPI int32_t U_EXPORT2 -udict_swap(const UDataSwapper *ds, const void *inData, int32_t length, - void *outData, UErrorCode *pErrorCode) { - const UDataInfo *pInfo; - int32_t headerSize; - const uint8_t *inBytes; - uint8_t *outBytes; - const int32_t *inIndexes; - int32_t indexes[DictionaryData::IX_COUNT]; - int32_t i, offset, size; - - headerSize = udata_swapDataHeader(ds, inData, length, outData, pErrorCode); - if (pErrorCode == NULL || U_FAILURE(*pErrorCode)) return 0; - pInfo = (const UDataInfo *)((const char *)inData + 4); - if (!(pInfo->dataFormat[0] == 0x44 && - pInfo->dataFormat[1] == 0x69 && - pInfo->dataFormat[2] == 0x63 && - pInfo->dataFormat[3] == 0x74 && - pInfo->formatVersion[0] == 1)) { - udata_printError(ds, "udict_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as dictionary data\n", - pInfo->dataFormat[0], pInfo->dataFormat[1], pInfo->dataFormat[2], pInfo->dataFormat[3], pInfo->formatVersion[0]); - *pErrorCode = U_UNSUPPORTED_ERROR; - return 0; - } - - inBytes = (const uint8_t *)inData + headerSize; - outBytes = (uint8_t *)outData + headerSize; - - inIndexes = (const int32_t *)inBytes; - if (length >= 0) { - length -= headerSize; - if (length < (int32_t)(sizeof(indexes))) { - udata_printError(ds, "udict_swap(): too few bytes (%d after header) for dictionary data\n", length); - *pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - } - - for (i = 0; i < DictionaryData::IX_COUNT; i++) { - indexes[i] = udata_readInt32(ds, inIndexes[i]); - } - - size = indexes[DictionaryData::IX_TOTAL_SIZE]; - - if (length >= 0) { - if (length < size) { - udata_printError(ds, "udict_swap(): too few bytes (%d after header) for all of dictionary data\n", length); - *pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - if (inBytes != outBytes) { - uprv_memcpy(outBytes, inBytes, size); - } - - offset = 0; - ds->swapArray32(ds, inBytes, sizeof(indexes), outBytes, pErrorCode); - offset = (int32_t)sizeof(indexes); - int32_t trieType = indexes[DictionaryData::IX_TRIE_TYPE] & DictionaryData::TRIE_TYPE_MASK; - int32_t nextOffset = indexes[DictionaryData::IX_RESERVED1_OFFSET]; - - if (trieType == DictionaryData::TRIE_TYPE_UCHARS) { - ds->swapArray16(ds, inBytes + offset, nextOffset - offset, outBytes + offset, pErrorCode); - } else if (trieType == DictionaryData::TRIE_TYPE_BYTES) { - // nothing to do - } else { - udata_printError(ds, "udict_swap(): unknown trie type!\n"); - *pErrorCode = U_UNSUPPORTED_ERROR; - return 0; - } - - // these next two sections are empty in the current format, - // but may be used later. - offset = nextOffset; - nextOffset = indexes[DictionaryData::IX_RESERVED2_OFFSET]; - offset = nextOffset; - nextOffset = indexes[DictionaryData::IX_TOTAL_SIZE]; - offset = nextOffset; - } - return headerSize + size; -} -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2014-2016, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* dictionarydata.h +* +* created on: 2012may31 +* created by: Markus W. Scherer & Maxime Serrano +*/ + +#include "dictionarydata.h" +#include "unicode/ucharstrie.h" +#include "unicode/bytestrie.h" +#include "unicode/udata.h" +#include "cmemory.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +U_NAMESPACE_BEGIN + +const int32_t DictionaryData::TRIE_TYPE_BYTES = 0; +const int32_t DictionaryData::TRIE_TYPE_UCHARS = 1; +const int32_t DictionaryData::TRIE_TYPE_MASK = 7; +const int32_t DictionaryData::TRIE_HAS_VALUES = 8; + +const int32_t DictionaryData::TRANSFORM_NONE = 0; +const int32_t DictionaryData::TRANSFORM_TYPE_OFFSET = 0x1000000; +const int32_t DictionaryData::TRANSFORM_TYPE_MASK = 0x7f000000; +const int32_t DictionaryData::TRANSFORM_OFFSET_MASK = 0x1fffff; + +DictionaryMatcher::~DictionaryMatcher() { +} + +UCharsDictionaryMatcher::~UCharsDictionaryMatcher() { + udata_close(file); +} + +int32_t UCharsDictionaryMatcher::getType() const { + return DictionaryData::TRIE_TYPE_UCHARS; +} + +int32_t UCharsDictionaryMatcher::matches(UText *text, int32_t maxLength, int32_t limit, + int32_t *lengths, int32_t *cpLengths, int32_t *values, + int32_t *prefix) const { + + UCharsTrie uct(characters); + int32_t startingTextIndex = (int32_t)utext_getNativeIndex(text); + int32_t wordCount = 0; + int32_t codePointsMatched = 0; + + for (UChar32 c = utext_next32(text); c >= 0; c=utext_next32(text)) { + UStringTrieResult result = (codePointsMatched == 0) ? uct.first(c) : uct.next(c); + int32_t lengthMatched = (int32_t)utext_getNativeIndex(text) - startingTextIndex; + codePointsMatched += 1; + if (USTRINGTRIE_HAS_VALUE(result)) { + if (wordCount < limit) { + if (values != nullptr) { + values[wordCount] = uct.getValue(); + } + if (lengths != nullptr) { + lengths[wordCount] = lengthMatched; + } + if (cpLengths != nullptr) { + cpLengths[wordCount] = codePointsMatched; + } + ++wordCount; + } + if (result == USTRINGTRIE_FINAL_VALUE) { + break; + } + } + else if (result == USTRINGTRIE_NO_MATCH) { + break; + } + if (lengthMatched >= maxLength) { + break; + } + } + + if (prefix != nullptr) { + *prefix = codePointsMatched; + } + return wordCount; +} + +BytesDictionaryMatcher::~BytesDictionaryMatcher() { + udata_close(file); +} + +UChar32 BytesDictionaryMatcher::transform(UChar32 c) const { + if ((transformConstant & DictionaryData::TRANSFORM_TYPE_MASK) == DictionaryData::TRANSFORM_TYPE_OFFSET) { + if (c == 0x200D) { + return 0xFF; + } else if (c == 0x200C) { + return 0xFE; + } + int32_t delta = c - (transformConstant & DictionaryData::TRANSFORM_OFFSET_MASK); + if (delta < 0 || 0xFD < delta) { + return U_SENTINEL; + } + return (UChar32)delta; + } + return c; +} + +int32_t BytesDictionaryMatcher::getType() const { + return DictionaryData::TRIE_TYPE_BYTES; +} + +int32_t BytesDictionaryMatcher::matches(UText *text, int32_t maxLength, int32_t limit, + int32_t *lengths, int32_t *cpLengths, int32_t *values, + int32_t *prefix) const { + BytesTrie bt(characters); + int32_t startingTextIndex = (int32_t)utext_getNativeIndex(text); + int32_t wordCount = 0; + int32_t codePointsMatched = 0; + + for (UChar32 c = utext_next32(text); c >= 0; c=utext_next32(text)) { + UStringTrieResult result = (codePointsMatched == 0) ? bt.first(transform(c)) : bt.next(transform(c)); + int32_t lengthMatched = (int32_t)utext_getNativeIndex(text) - startingTextIndex; + codePointsMatched += 1; + if (USTRINGTRIE_HAS_VALUE(result)) { + if (wordCount < limit) { + if (values != nullptr) { + values[wordCount] = bt.getValue(); + } + if (lengths != nullptr) { + lengths[wordCount] = lengthMatched; + } + if (cpLengths != nullptr) { + cpLengths[wordCount] = codePointsMatched; + } + ++wordCount; + } + if (result == USTRINGTRIE_FINAL_VALUE) { + break; + } + } + else if (result == USTRINGTRIE_NO_MATCH) { + break; + } + if (lengthMatched >= maxLength) { + break; + } + } + + if (prefix != nullptr) { + *prefix = codePointsMatched; + } + return wordCount; +} + + +U_NAMESPACE_END + +U_NAMESPACE_USE + +U_CAPI int32_t U_EXPORT2 +udict_swap(const UDataSwapper *ds, const void *inData, int32_t length, + void *outData, UErrorCode *pErrorCode) { + const UDataInfo *pInfo; + int32_t headerSize; + const uint8_t *inBytes; + uint8_t *outBytes; + const int32_t *inIndexes; + int32_t indexes[DictionaryData::IX_COUNT]; + int32_t i, offset, size; + + headerSize = udata_swapDataHeader(ds, inData, length, outData, pErrorCode); + if (pErrorCode == nullptr || U_FAILURE(*pErrorCode)) return 0; + pInfo = (const UDataInfo *)((const char *)inData + 4); + if (!(pInfo->dataFormat[0] == 0x44 && + pInfo->dataFormat[1] == 0x69 && + pInfo->dataFormat[2] == 0x63 && + pInfo->dataFormat[3] == 0x74 && + pInfo->formatVersion[0] == 1)) { + udata_printError(ds, "udict_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as dictionary data\n", + pInfo->dataFormat[0], pInfo->dataFormat[1], pInfo->dataFormat[2], pInfo->dataFormat[3], pInfo->formatVersion[0]); + *pErrorCode = U_UNSUPPORTED_ERROR; + return 0; + } + + inBytes = (const uint8_t *)inData + headerSize; + outBytes = (outData == nullptr) ? nullptr : (uint8_t *)outData + headerSize; + + inIndexes = (const int32_t *)inBytes; + if (length >= 0) { + length -= headerSize; + if (length < (int32_t)(sizeof(indexes))) { + udata_printError(ds, "udict_swap(): too few bytes (%d after header) for dictionary data\n", length); + *pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + } + + for (i = 0; i < DictionaryData::IX_COUNT; i++) { + indexes[i] = udata_readInt32(ds, inIndexes[i]); + } + + size = indexes[DictionaryData::IX_TOTAL_SIZE]; + + if (length >= 0) { + if (length < size) { + udata_printError(ds, "udict_swap(): too few bytes (%d after header) for all of dictionary data\n", length); + *pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + if (inBytes != outBytes) { + uprv_memcpy(outBytes, inBytes, size); + } + + offset = 0; + ds->swapArray32(ds, inBytes, sizeof(indexes), outBytes, pErrorCode); + offset = (int32_t)sizeof(indexes); + int32_t trieType = indexes[DictionaryData::IX_TRIE_TYPE] & DictionaryData::TRIE_TYPE_MASK; + int32_t nextOffset = indexes[DictionaryData::IX_RESERVED1_OFFSET]; + + if (trieType == DictionaryData::TRIE_TYPE_UCHARS) { + ds->swapArray16(ds, inBytes + offset, nextOffset - offset, outBytes + offset, pErrorCode); + } else if (trieType == DictionaryData::TRIE_TYPE_BYTES) { + // nothing to do + } else { + udata_printError(ds, "udict_swap(): unknown trie type!\n"); + *pErrorCode = U_UNSUPPORTED_ERROR; + return 0; + } + + // these next two sections are empty in the current format, + // but may be used later. + offset = nextOffset; + nextOffset = indexes[DictionaryData::IX_RESERVED2_OFFSET]; + offset = nextOffset; + nextOffset = indexes[DictionaryData::IX_TOTAL_SIZE]; + offset = nextOffset; + } + return headerSize + size; +} +#endif diff --git a/deps/icu-small/source/common/dictionarydata.h b/deps/icu-small/source/common/dictionarydata.h index e75716f54b9fe1..8ffca0b85420e4 100644 --- a/deps/icu-small/source/common/dictionarydata.h +++ b/deps/icu-small/source/common/dictionarydata.h @@ -1,191 +1,191 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* dictionarydata.h -* -* created on: 2012may31 -* created by: Markus W. Scherer & Maxime Serrano -*/ - -#ifndef __DICTIONARYDATA_H__ -#define __DICTIONARYDATA_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/utext.h" -#include "unicode/udata.h" -#include "udataswp.h" -#include "unicode/uobject.h" -#include "unicode/ustringtrie.h" - -U_NAMESPACE_BEGIN - -class UCharsTrie; -class BytesTrie; - -class U_COMMON_API DictionaryData : public UMemory { -public: - static const int32_t TRIE_TYPE_BYTES; // = 0; - static const int32_t TRIE_TYPE_UCHARS; // = 1; - static const int32_t TRIE_TYPE_MASK; // = 7; - static const int32_t TRIE_HAS_VALUES; // = 8; - - static const int32_t TRANSFORM_NONE; // = 0; - static const int32_t TRANSFORM_TYPE_OFFSET; // = 0x1000000; - static const int32_t TRANSFORM_TYPE_MASK; // = 0x7f000000; - static const int32_t TRANSFORM_OFFSET_MASK; // = 0x1fffff; - - enum { - // Byte offsets from the start of the data, after the generic header. - IX_STRING_TRIE_OFFSET, - IX_RESERVED1_OFFSET, - IX_RESERVED2_OFFSET, - IX_TOTAL_SIZE, - - // Trie type: TRIE_HAS_VALUES | TRIE_TYPE_BYTES etc. - IX_TRIE_TYPE, - // Transform specification: TRANSFORM_TYPE_OFFSET | 0xe00 etc. - IX_TRANSFORM, - - IX_RESERVED6, - IX_RESERVED7, - IX_COUNT - }; -}; - -/** - * Wrapper class around generic dictionaries, implementing matches(). - * getType() should return a TRIE_TYPE_??? constant from DictionaryData. - * - * All implementations of this interface must be thread-safe if they are to be used inside of the - * dictionary-based break iteration code. - */ -class U_COMMON_API DictionaryMatcher : public UMemory { -public: - DictionaryMatcher() {} - virtual ~DictionaryMatcher(); - // this should emulate CompactTrieDictionary::matches() - /* @param text The text in which to look for matching words. Matching begins - * at the current position of the UText. - * @param maxLength The max length of match to consider. Units are the native indexing - * units of the UText. - * @param limit Capacity of output arrays, which is also the maximum number of - * matching words to be found. - * @param lengths output array, filled with the lengths of the matches, in order, - * from shortest to longest. Lengths are in native indexing units - * of the UText. May be NULL. - * @param cpLengths output array, filled with the lengths of the matches, in order, - * from shortest to longest. Lengths are the number of Unicode code points. - * May be NULL. - * @param values Output array, filled with the values associated with the words found. - * May be NULL. - * @param prefix Output parameter, the code point length of the prefix match, even if that - * prefix didn't lead to a complete word. Will always be >= the cpLength - * of the longest complete word matched. May be NULL. - * @return Number of matching words found. - */ - virtual int32_t matches(UText *text, int32_t maxLength, int32_t limit, - int32_t *lengths, int32_t *cpLengths, int32_t *values, - int32_t *prefix) const = 0; - - /** @return DictionaryData::TRIE_TYPE_XYZ */ - virtual int32_t getType() const = 0; -}; - -// Implementation of the DictionaryMatcher interface for a UCharsTrie dictionary -class U_COMMON_API UCharsDictionaryMatcher : public DictionaryMatcher { -public: - // constructs a new UCharsDictionaryMatcher. - // The UDataMemory * will be closed on this object's destruction. - UCharsDictionaryMatcher(const UChar *c, UDataMemory *f) : characters(c), file(f) { } - virtual ~UCharsDictionaryMatcher(); - virtual int32_t matches(UText *text, int32_t maxLength, int32_t limit, - int32_t *lengths, int32_t *cpLengths, int32_t *values, - int32_t *prefix) const override; - virtual int32_t getType() const override; -private: - const UChar *characters; - UDataMemory *file; -}; - -// Implementation of the DictionaryMatcher interface for a BytesTrie dictionary -class U_COMMON_API BytesDictionaryMatcher : public DictionaryMatcher { -public: - // constructs a new BytesTrieDictionaryMatcher - // the transform constant should be the constant read from the file, not a masked version! - // the UDataMemory * fed in here will be closed on this object's destruction - BytesDictionaryMatcher(const char *c, int32_t t, UDataMemory *f) - : characters(c), transformConstant(t), file(f) { } - virtual ~BytesDictionaryMatcher(); - virtual int32_t matches(UText *text, int32_t maxLength, int32_t limit, - int32_t *lengths, int32_t *cpLengths, int32_t *values, - int32_t *prefix) const override; - virtual int32_t getType() const override; -private: - UChar32 transform(UChar32 c) const; - - const char *characters; - int32_t transformConstant; - UDataMemory *file; -}; - -U_NAMESPACE_END - -U_CAPI int32_t U_EXPORT2 -udict_swap(const UDataSwapper *ds, const void *inData, int32_t length, void *outData, UErrorCode *pErrorCode); - -/** - * Format of dictionary .dict data files. - * Format version 1.0. - * - * A dictionary .dict data file contains a byte-serialized BytesTrie or - * a UChars-serialized UCharsTrie. - * Such files are used in dictionary-based break iteration (DBBI). - * - * For a BytesTrie, a transformation type is specified for - * transforming Unicode strings into byte sequences. - * - * A .dict file begins with a standard ICU data file header - * (DataHeader, see ucmndata.h and unicode/udata.h). - * The UDataInfo.dataVersion field is currently unused (set to 0.0.0.0). - * - * After the header, the file contains the following parts. - * Constants are defined in the DictionaryData class. - * - * For the data structure of BytesTrie & UCharsTrie see - * https://icu.unicode.org/design/struct/tries - * and the bytestrie.h and ucharstrie.h header files. - * - * int32_t indexes[indexesLength]; -- indexesLength=indexes[IX_STRING_TRIE_OFFSET]/4; - * - * The first four indexes are byte offsets in ascending order. - * Each byte offset marks the start of the next part in the data file, - * and the end of the previous one. - * When two consecutive byte offsets are the same, then the corresponding part is empty. - * Byte offsets are offsets from after the header, - * that is, from the beginning of the indexes[]. - * Each part starts at an offset with proper alignment for its data. - * If necessary, the previous part may include padding bytes to achieve this alignment. - * - * trieType=indexes[IX_TRIE_TYPE] defines the trie type. - * transform=indexes[IX_TRANSFORM] defines the Unicode-to-bytes transformation. - * If the transformation type is TRANSFORM_TYPE_OFFSET, - * then the lower 21 bits contain the offset code point. - * Each code point c is mapped to byte b = (c - offset). - * Code points outside the range offset..(offset+0xff) cannot be mapped - * and do not occur in the dictionary. - * - * stringTrie; -- a serialized BytesTrie or UCharsTrie - * - * The dictionary maps strings to specific values (TRIE_HAS_VALUES bit set in trieType), - * or it maps all strings to 0 (TRIE_HAS_VALUES bit not set). - */ - -#endif /* !UCONFIG_NO_BREAK_ITERATION */ -#endif /* __DICTIONARYDATA_H__ */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* dictionarydata.h +* +* created on: 2012may31 +* created by: Markus W. Scherer & Maxime Serrano +*/ + +#ifndef __DICTIONARYDATA_H__ +#define __DICTIONARYDATA_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/utext.h" +#include "unicode/udata.h" +#include "udataswp.h" +#include "unicode/uobject.h" +#include "unicode/ustringtrie.h" + +U_NAMESPACE_BEGIN + +class UCharsTrie; +class BytesTrie; + +class U_COMMON_API DictionaryData : public UMemory { +public: + static const int32_t TRIE_TYPE_BYTES; // = 0; + static const int32_t TRIE_TYPE_UCHARS; // = 1; + static const int32_t TRIE_TYPE_MASK; // = 7; + static const int32_t TRIE_HAS_VALUES; // = 8; + + static const int32_t TRANSFORM_NONE; // = 0; + static const int32_t TRANSFORM_TYPE_OFFSET; // = 0x1000000; + static const int32_t TRANSFORM_TYPE_MASK; // = 0x7f000000; + static const int32_t TRANSFORM_OFFSET_MASK; // = 0x1fffff; + + enum { + // Byte offsets from the start of the data, after the generic header. + IX_STRING_TRIE_OFFSET, + IX_RESERVED1_OFFSET, + IX_RESERVED2_OFFSET, + IX_TOTAL_SIZE, + + // Trie type: TRIE_HAS_VALUES | TRIE_TYPE_BYTES etc. + IX_TRIE_TYPE, + // Transform specification: TRANSFORM_TYPE_OFFSET | 0xe00 etc. + IX_TRANSFORM, + + IX_RESERVED6, + IX_RESERVED7, + IX_COUNT + }; +}; + +/** + * Wrapper class around generic dictionaries, implementing matches(). + * getType() should return a TRIE_TYPE_??? constant from DictionaryData. + * + * All implementations of this interface must be thread-safe if they are to be used inside of the + * dictionary-based break iteration code. + */ +class U_COMMON_API DictionaryMatcher : public UMemory { +public: + DictionaryMatcher() {} + virtual ~DictionaryMatcher(); + // this should emulate CompactTrieDictionary::matches() + /* @param text The text in which to look for matching words. Matching begins + * at the current position of the UText. + * @param maxLength The max length of match to consider. Units are the native indexing + * units of the UText. + * @param limit Capacity of output arrays, which is also the maximum number of + * matching words to be found. + * @param lengths output array, filled with the lengths of the matches, in order, + * from shortest to longest. Lengths are in native indexing units + * of the UText. May be nullptr. + * @param cpLengths output array, filled with the lengths of the matches, in order, + * from shortest to longest. Lengths are the number of Unicode code points. + * May be nullptr. + * @param values Output array, filled with the values associated with the words found. + * May be nullptr. + * @param prefix Output parameter, the code point length of the prefix match, even if that + * prefix didn't lead to a complete word. Will always be >= the cpLength + * of the longest complete word matched. May be nullptr. + * @return Number of matching words found. + */ + virtual int32_t matches(UText *text, int32_t maxLength, int32_t limit, + int32_t *lengths, int32_t *cpLengths, int32_t *values, + int32_t *prefix) const = 0; + + /** @return DictionaryData::TRIE_TYPE_XYZ */ + virtual int32_t getType() const = 0; +}; + +// Implementation of the DictionaryMatcher interface for a UCharsTrie dictionary +class U_COMMON_API UCharsDictionaryMatcher : public DictionaryMatcher { +public: + // constructs a new UCharsDictionaryMatcher. + // The UDataMemory * will be closed on this object's destruction. + UCharsDictionaryMatcher(const char16_t *c, UDataMemory *f) : characters(c), file(f) { } + virtual ~UCharsDictionaryMatcher(); + virtual int32_t matches(UText *text, int32_t maxLength, int32_t limit, + int32_t *lengths, int32_t *cpLengths, int32_t *values, + int32_t *prefix) const override; + virtual int32_t getType() const override; +private: + const char16_t *characters; + UDataMemory *file; +}; + +// Implementation of the DictionaryMatcher interface for a BytesTrie dictionary +class U_COMMON_API BytesDictionaryMatcher : public DictionaryMatcher { +public: + // constructs a new BytesTrieDictionaryMatcher + // the transform constant should be the constant read from the file, not a masked version! + // the UDataMemory * fed in here will be closed on this object's destruction + BytesDictionaryMatcher(const char *c, int32_t t, UDataMemory *f) + : characters(c), transformConstant(t), file(f) { } + virtual ~BytesDictionaryMatcher(); + virtual int32_t matches(UText *text, int32_t maxLength, int32_t limit, + int32_t *lengths, int32_t *cpLengths, int32_t *values, + int32_t *prefix) const override; + virtual int32_t getType() const override; +private: + UChar32 transform(UChar32 c) const; + + const char *characters; + int32_t transformConstant; + UDataMemory *file; +}; + +U_NAMESPACE_END + +U_CAPI int32_t U_EXPORT2 +udict_swap(const UDataSwapper *ds, const void *inData, int32_t length, void *outData, UErrorCode *pErrorCode); + +/** + * Format of dictionary .dict data files. + * Format version 1.0. + * + * A dictionary .dict data file contains a byte-serialized BytesTrie or + * a UChars-serialized UCharsTrie. + * Such files are used in dictionary-based break iteration (DBBI). + * + * For a BytesTrie, a transformation type is specified for + * transforming Unicode strings into byte sequences. + * + * A .dict file begins with a standard ICU data file header + * (DataHeader, see ucmndata.h and unicode/udata.h). + * The UDataInfo.dataVersion field is currently unused (set to 0.0.0.0). + * + * After the header, the file contains the following parts. + * Constants are defined in the DictionaryData class. + * + * For the data structure of BytesTrie & UCharsTrie see + * https://icu.unicode.org/design/struct/tries + * and the bytestrie.h and ucharstrie.h header files. + * + * int32_t indexes[indexesLength]; -- indexesLength=indexes[IX_STRING_TRIE_OFFSET]/4; + * + * The first four indexes are byte offsets in ascending order. + * Each byte offset marks the start of the next part in the data file, + * and the end of the previous one. + * When two consecutive byte offsets are the same, then the corresponding part is empty. + * Byte offsets are offsets from after the header, + * that is, from the beginning of the indexes[]. + * Each part starts at an offset with proper alignment for its data. + * If necessary, the previous part may include padding bytes to achieve this alignment. + * + * trieType=indexes[IX_TRIE_TYPE] defines the trie type. + * transform=indexes[IX_TRANSFORM] defines the Unicode-to-bytes transformation. + * If the transformation type is TRANSFORM_TYPE_OFFSET, + * then the lower 21 bits contain the offset code point. + * Each code point c is mapped to byte b = (c - offset). + * Code points outside the range offset..(offset+0xff) cannot be mapped + * and do not occur in the dictionary. + * + * stringTrie; -- a serialized BytesTrie or UCharsTrie + * + * The dictionary maps strings to specific values (TRIE_HAS_VALUES bit set in trieType), + * or it maps all strings to 0 (TRIE_HAS_VALUES bit not set). + */ + +#endif /* !UCONFIG_NO_BREAK_ITERATION */ +#endif /* __DICTIONARYDATA_H__ */ diff --git a/deps/icu-small/source/common/dtintrv.cpp b/deps/icu-small/source/common/dtintrv.cpp index 011ad8136f3bdb..3149c0519dad3e 100644 --- a/deps/icu-small/source/common/dtintrv.cpp +++ b/deps/icu-small/source/common/dtintrv.cpp @@ -1,63 +1,63 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/******************************************************************************* -* Copyright (C) 2008, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File DTINTRV.CPP -* -******************************************************************************* -*/ - - - -#include "unicode/dtintrv.h" - - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateInterval) - -//DateInterval::DateInterval(){} - - -DateInterval::DateInterval(UDate from, UDate to) -: fromDate(from), - toDate(to) -{} - - -DateInterval::~DateInterval(){} - - -DateInterval::DateInterval(const DateInterval& other) -: UObject(other) { - *this = other; -} - - -DateInterval& -DateInterval::operator=(const DateInterval& other) { - if ( this != &other ) { - fromDate = other.fromDate; - toDate = other.toDate; - } - return *this; -} - - -DateInterval* -DateInterval::clone() const { - return new DateInterval(*this); -} - - -bool -DateInterval::operator==(const DateInterval& other) const { - return ( fromDate == other.fromDate && toDate == other.toDate ); -} - - -U_NAMESPACE_END - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************************* +* Copyright (C) 2008, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTINTRV.CPP +* +******************************************************************************* +*/ + + + +#include "unicode/dtintrv.h" + + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateInterval) + +//DateInterval::DateInterval(){} + + +DateInterval::DateInterval(UDate from, UDate to) +: fromDate(from), + toDate(to) +{} + + +DateInterval::~DateInterval(){} + + +DateInterval::DateInterval(const DateInterval& other) +: UObject(other) { + *this = other; +} + + +DateInterval& +DateInterval::operator=(const DateInterval& other) { + if ( this != &other ) { + fromDate = other.fromDate; + toDate = other.toDate; + } + return *this; +} + + +DateInterval* +DateInterval::clone() const { + return new DateInterval(*this); +} + + +bool +DateInterval::operator==(const DateInterval& other) const { + return ( fromDate == other.fromDate && toDate == other.toDate ); +} + + +U_NAMESPACE_END + diff --git a/deps/icu-small/source/common/edits.cpp b/deps/icu-small/source/common/edits.cpp index 21d7c3f0061063..942f66608377dd 100644 --- a/deps/icu-small/source/common/edits.cpp +++ b/deps/icu-small/source/common/edits.cpp @@ -1,804 +1,804 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// edits.cpp -// created: 2017feb08 Markus W. Scherer - -#include "unicode/edits.h" -#include "unicode/unistr.h" -#include "unicode/utypes.h" -#include "cmemory.h" -#include "uassert.h" -#include "util.h" - -U_NAMESPACE_BEGIN - -namespace { - -// 0000uuuuuuuuuuuu records u+1 unchanged text units. -const int32_t MAX_UNCHANGED_LENGTH = 0x1000; -const int32_t MAX_UNCHANGED = MAX_UNCHANGED_LENGTH - 1; - -// 0mmmnnnccccccccc with m=1..6 records ccc+1 replacements of m:n text units. -const int32_t MAX_SHORT_CHANGE_OLD_LENGTH = 6; -const int32_t MAX_SHORT_CHANGE_NEW_LENGTH = 7; -const int32_t SHORT_CHANGE_NUM_MASK = 0x1ff; -const int32_t MAX_SHORT_CHANGE = 0x6fff; - -// 0111mmmmmmnnnnnn records a replacement of m text units with n. -// m or n = 61: actual length follows in the next edits array unit. -// m or n = 62..63: actual length follows in the next two edits array units. -// Bit 30 of the actual length is in the head unit. -// Trailing units have bit 15 set. -const int32_t LENGTH_IN_1TRAIL = 61; -const int32_t LENGTH_IN_2TRAIL = 62; - -} // namespace - -void Edits::releaseArray() U_NOEXCEPT { - if (array != stackArray) { - uprv_free(array); - } -} - -Edits &Edits::copyArray(const Edits &other) { - if (U_FAILURE(errorCode_)) { - length = delta = numChanges = 0; - return *this; - } - if (length > capacity) { - uint16_t *newArray = (uint16_t *)uprv_malloc((size_t)length * 2); - if (newArray == nullptr) { - length = delta = numChanges = 0; - errorCode_ = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - releaseArray(); - array = newArray; - capacity = length; - } - if (length > 0) { - uprv_memcpy(array, other.array, (size_t)length * 2); - } - return *this; -} - -Edits &Edits::moveArray(Edits &src) U_NOEXCEPT { - if (U_FAILURE(errorCode_)) { - length = delta = numChanges = 0; - return *this; - } - releaseArray(); - if (length > STACK_CAPACITY) { - array = src.array; - capacity = src.capacity; - src.array = src.stackArray; - src.capacity = STACK_CAPACITY; - src.reset(); - return *this; - } - array = stackArray; - capacity = STACK_CAPACITY; - if (length > 0) { - uprv_memcpy(array, src.array, (size_t)length * 2); - } - return *this; -} - -Edits &Edits::operator=(const Edits &other) { - if (this == &other) { return *this; } // self-assignment: no-op - length = other.length; - delta = other.delta; - numChanges = other.numChanges; - errorCode_ = other.errorCode_; - return copyArray(other); -} - -Edits &Edits::operator=(Edits &&src) U_NOEXCEPT { - length = src.length; - delta = src.delta; - numChanges = src.numChanges; - errorCode_ = src.errorCode_; - return moveArray(src); -} - -Edits::~Edits() { - releaseArray(); -} - -void Edits::reset() U_NOEXCEPT { - length = delta = numChanges = 0; - errorCode_ = U_ZERO_ERROR; -} - -void Edits::addUnchanged(int32_t unchangedLength) { - if(U_FAILURE(errorCode_) || unchangedLength == 0) { return; } - if(unchangedLength < 0) { - errorCode_ = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - // Merge into previous unchanged-text record, if any. - int32_t last = lastUnit(); - if(last < MAX_UNCHANGED) { - int32_t remaining = MAX_UNCHANGED - last; - if (remaining >= unchangedLength) { - setLastUnit(last + unchangedLength); - return; - } - setLastUnit(MAX_UNCHANGED); - unchangedLength -= remaining; - } - // Split large lengths into multiple units. - while(unchangedLength >= MAX_UNCHANGED_LENGTH) { - append(MAX_UNCHANGED); - unchangedLength -= MAX_UNCHANGED_LENGTH; - } - // Write a small (remaining) length. - if(unchangedLength > 0) { - append(unchangedLength - 1); - } -} - -void Edits::addReplace(int32_t oldLength, int32_t newLength) { - if(U_FAILURE(errorCode_)) { return; } - if(oldLength < 0 || newLength < 0) { - errorCode_ = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (oldLength == 0 && newLength == 0) { - return; - } - ++numChanges; - int32_t newDelta = newLength - oldLength; - if (newDelta != 0) { - if ((newDelta > 0 && delta >= 0 && newDelta > (INT32_MAX - delta)) || - (newDelta < 0 && delta < 0 && newDelta < (INT32_MIN - delta))) { - // Integer overflow or underflow. - errorCode_ = U_INDEX_OUTOFBOUNDS_ERROR; - return; - } - delta += newDelta; - } - - if(0 < oldLength && oldLength <= MAX_SHORT_CHANGE_OLD_LENGTH && - newLength <= MAX_SHORT_CHANGE_NEW_LENGTH) { - // Merge into previous same-lengths short-replacement record, if any. - int32_t u = (oldLength << 12) | (newLength << 9); - int32_t last = lastUnit(); - if(MAX_UNCHANGED < last && last < MAX_SHORT_CHANGE && - (last & ~SHORT_CHANGE_NUM_MASK) == u && - (last & SHORT_CHANGE_NUM_MASK) < SHORT_CHANGE_NUM_MASK) { - setLastUnit(last + 1); - return; - } - append(u); - return; - } - - int32_t head = 0x7000; - if (oldLength < LENGTH_IN_1TRAIL && newLength < LENGTH_IN_1TRAIL) { - head |= oldLength << 6; - head |= newLength; - append(head); - } else if ((capacity - length) >= 5 || growArray()) { - int32_t limit = length + 1; - if(oldLength < LENGTH_IN_1TRAIL) { - head |= oldLength << 6; - } else if(oldLength <= 0x7fff) { - head |= LENGTH_IN_1TRAIL << 6; - array[limit++] = (uint16_t)(0x8000 | oldLength); - } else { - head |= (LENGTH_IN_2TRAIL + (oldLength >> 30)) << 6; - array[limit++] = (uint16_t)(0x8000 | (oldLength >> 15)); - array[limit++] = (uint16_t)(0x8000 | oldLength); - } - if(newLength < LENGTH_IN_1TRAIL) { - head |= newLength; - } else if(newLength <= 0x7fff) { - head |= LENGTH_IN_1TRAIL; - array[limit++] = (uint16_t)(0x8000 | newLength); - } else { - head |= LENGTH_IN_2TRAIL + (newLength >> 30); - array[limit++] = (uint16_t)(0x8000 | (newLength >> 15)); - array[limit++] = (uint16_t)(0x8000 | newLength); - } - array[length] = (uint16_t)head; - length = limit; - } -} - -void Edits::append(int32_t r) { - if(length < capacity || growArray()) { - array[length++] = (uint16_t)r; - } -} - -UBool Edits::growArray() { - int32_t newCapacity; - if (array == stackArray) { - newCapacity = 2000; - } else if (capacity == INT32_MAX) { - // Not U_BUFFER_OVERFLOW_ERROR because that could be confused on a string transform API - // with a result-string-buffer overflow. - errorCode_ = U_INDEX_OUTOFBOUNDS_ERROR; - return false; - } else if (capacity >= (INT32_MAX / 2)) { - newCapacity = INT32_MAX; - } else { - newCapacity = 2 * capacity; - } - // Grow by at least 5 units so that a maximal change record will fit. - if ((newCapacity - capacity) < 5) { - errorCode_ = U_INDEX_OUTOFBOUNDS_ERROR; - return false; - } - uint16_t *newArray = (uint16_t *)uprv_malloc((size_t)newCapacity * 2); - if (newArray == NULL) { - errorCode_ = U_MEMORY_ALLOCATION_ERROR; - return false; - } - uprv_memcpy(newArray, array, (size_t)length * 2); - releaseArray(); - array = newArray; - capacity = newCapacity; - return true; -} - -UBool Edits::copyErrorTo(UErrorCode &outErrorCode) const { - if (U_FAILURE(outErrorCode)) { return true; } - if (U_SUCCESS(errorCode_)) { return false; } - outErrorCode = errorCode_; - return true; -} - -Edits &Edits::mergeAndAppend(const Edits &ab, const Edits &bc, UErrorCode &errorCode) { - if (copyErrorTo(errorCode)) { return *this; } - // Picture string a --(Edits ab)--> string b --(Edits bc)--> string c. - // Parallel iteration over both Edits. - Iterator abIter = ab.getFineIterator(); - Iterator bcIter = bc.getFineIterator(); - UBool abHasNext = true, bcHasNext = true; - // Copy iterator state into local variables, so that we can modify and subdivide spans. - // ab old & new length, bc old & new length - int32_t aLength = 0, ab_bLength = 0, bc_bLength = 0, cLength = 0; - // When we have different-intermediate-length changes, we accumulate a larger change. - int32_t pending_aLength = 0, pending_cLength = 0; - for (;;) { - // At this point, for each of the two iterators: - // Either we are done with the locally cached current edit, - // and its intermediate-string length has been reset, - // or we will continue to work with a truncated remainder of this edit. - // - // If the current edit is done, and the iterator has not yet reached the end, - // then we fetch the next edit. This is true for at least one of the iterators. - // - // Normally it does not matter whether we fetch from ab and then bc or vice versa. - // However, the result is observably different when - // ab deletions meet bc insertions at the same intermediate-string index. - // Some users expect the bc insertions to come first, so we fetch from bc first. - if (bc_bLength == 0) { - if (bcHasNext && (bcHasNext = bcIter.next(errorCode)) != 0) { - bc_bLength = bcIter.oldLength(); - cLength = bcIter.newLength(); - if (bc_bLength == 0) { - // insertion - if (ab_bLength == 0 || !abIter.hasChange()) { - addReplace(pending_aLength, pending_cLength + cLength); - pending_aLength = pending_cLength = 0; - } else { - pending_cLength += cLength; - } - continue; - } - } - // else see if the other iterator is done, too. - } - if (ab_bLength == 0) { - if (abHasNext && (abHasNext = abIter.next(errorCode)) != 0) { - aLength = abIter.oldLength(); - ab_bLength = abIter.newLength(); - if (ab_bLength == 0) { - // deletion - if (bc_bLength == bcIter.oldLength() || !bcIter.hasChange()) { - addReplace(pending_aLength + aLength, pending_cLength); - pending_aLength = pending_cLength = 0; - } else { - pending_aLength += aLength; - } - continue; - } - } else if (bc_bLength == 0) { - // Both iterators are done at the same time: - // The intermediate-string lengths match. - break; - } else { - // The ab output string is shorter than the bc input string. - if (!copyErrorTo(errorCode)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - } - return *this; - } - } - if (bc_bLength == 0) { - // The bc input string is shorter than the ab output string. - if (!copyErrorTo(errorCode)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - } - return *this; - } - // Done fetching: ab_bLength > 0 && bc_bLength > 0 - - // The current state has two parts: - // - Past: We accumulate a longer ac edit in the "pending" variables. - // - Current: We have copies of the current ab/bc edits in local variables. - // At least one side is newly fetched. - // One side might be a truncated remainder of an edit we fetched earlier. - - if (!abIter.hasChange() && !bcIter.hasChange()) { - // An unchanged span all the way from string a to string c. - if (pending_aLength != 0 || pending_cLength != 0) { - addReplace(pending_aLength, pending_cLength); - pending_aLength = pending_cLength = 0; - } - int32_t unchangedLength = aLength <= cLength ? aLength : cLength; - addUnchanged(unchangedLength); - ab_bLength = aLength -= unchangedLength; - bc_bLength = cLength -= unchangedLength; - // At least one of the unchanged spans is now empty. - continue; - } - if (!abIter.hasChange() && bcIter.hasChange()) { - // Unchanged a->b but changed b->c. - if (ab_bLength >= bc_bLength) { - // Split the longer unchanged span into change + remainder. - addReplace(pending_aLength + bc_bLength, pending_cLength + cLength); - pending_aLength = pending_cLength = 0; - aLength = ab_bLength -= bc_bLength; - bc_bLength = 0; - continue; - } - // Handle the shorter unchanged span below like a change. - } else if (abIter.hasChange() && !bcIter.hasChange()) { - // Changed a->b and then unchanged b->c. - if (ab_bLength <= bc_bLength) { - // Split the longer unchanged span into change + remainder. - addReplace(pending_aLength + aLength, pending_cLength + ab_bLength); - pending_aLength = pending_cLength = 0; - cLength = bc_bLength -= ab_bLength; - ab_bLength = 0; - continue; - } - // Handle the shorter unchanged span below like a change. - } else { // both abIter.hasChange() && bcIter.hasChange() - if (ab_bLength == bc_bLength) { - // Changes on both sides up to the same position. Emit & reset. - addReplace(pending_aLength + aLength, pending_cLength + cLength); - pending_aLength = pending_cLength = 0; - ab_bLength = bc_bLength = 0; - continue; - } - } - // Accumulate the a->c change, reset the shorter side, - // keep a remainder of the longer one. - pending_aLength += aLength; - pending_cLength += cLength; - if (ab_bLength < bc_bLength) { - bc_bLength -= ab_bLength; - cLength = ab_bLength = 0; - } else { // ab_bLength > bc_bLength - ab_bLength -= bc_bLength; - aLength = bc_bLength = 0; - } - } - if (pending_aLength != 0 || pending_cLength != 0) { - addReplace(pending_aLength, pending_cLength); - } - copyErrorTo(errorCode); - return *this; -} - -Edits::Iterator::Iterator(const uint16_t *a, int32_t len, UBool oc, UBool crs) : - array(a), index(0), length(len), remaining(0), - onlyChanges_(oc), coarse(crs), - dir(0), changed(false), oldLength_(0), newLength_(0), - srcIndex(0), replIndex(0), destIndex(0) {} - -int32_t Edits::Iterator::readLength(int32_t head) { - if (head < LENGTH_IN_1TRAIL) { - return head; - } else if (head < LENGTH_IN_2TRAIL) { - U_ASSERT(index < length); - U_ASSERT(array[index] >= 0x8000); - return array[index++] & 0x7fff; - } else { - U_ASSERT((index + 2) <= length); - U_ASSERT(array[index] >= 0x8000); - U_ASSERT(array[index + 1] >= 0x8000); - int32_t len = ((head & 1) << 30) | - ((int32_t)(array[index] & 0x7fff) << 15) | - (array[index + 1] & 0x7fff); - index += 2; - return len; - } -} - -void Edits::Iterator::updateNextIndexes() { - srcIndex += oldLength_; - if (changed) { - replIndex += newLength_; - } - destIndex += newLength_; -} - -void Edits::Iterator::updatePreviousIndexes() { - srcIndex -= oldLength_; - if (changed) { - replIndex -= newLength_; - } - destIndex -= newLength_; -} - -UBool Edits::Iterator::noNext() { - // No change before or beyond the string. - dir = 0; - changed = false; - oldLength_ = newLength_ = 0; - return false; -} - -UBool Edits::Iterator::next(UBool onlyChanges, UErrorCode &errorCode) { - // Forward iteration: Update the string indexes to the limit of the current span, - // and post-increment-read array units to assemble a new span. - // Leaves the array index one after the last unit of that span. - if (U_FAILURE(errorCode)) { return false; } - // We have an errorCode in case we need to start guarding against integer overflows. - // It is also convenient for caller loops if we bail out when an error was set elsewhere. - if (dir > 0) { - updateNextIndexes(); - } else { - if (dir < 0) { - // Turn around from previous() to next(). - // Post-increment-read the same span again. - if (remaining > 0) { - // Fine-grained iterator: - // Stay on the current one of a sequence of compressed changes. - ++index; // next() rests on the index after the sequence unit. - dir = 1; - return true; - } - } - dir = 1; - } - if (remaining >= 1) { - // Fine-grained iterator: Continue a sequence of compressed changes. - if (remaining > 1) { - --remaining; - return true; - } - remaining = 0; - } - if (index >= length) { - return noNext(); - } - int32_t u = array[index++]; - if (u <= MAX_UNCHANGED) { - // Combine adjacent unchanged ranges. - changed = false; - oldLength_ = u + 1; - while (index < length && (u = array[index]) <= MAX_UNCHANGED) { - ++index; - oldLength_ += u + 1; - } - newLength_ = oldLength_; - if (onlyChanges) { - updateNextIndexes(); - if (index >= length) { - return noNext(); - } - // already fetched u > MAX_UNCHANGED at index - ++index; - } else { - return true; - } - } - changed = true; - if (u <= MAX_SHORT_CHANGE) { - int32_t oldLen = u >> 12; - int32_t newLen = (u >> 9) & MAX_SHORT_CHANGE_NEW_LENGTH; - int32_t num = (u & SHORT_CHANGE_NUM_MASK) + 1; - if (coarse) { - oldLength_ = num * oldLen; - newLength_ = num * newLen; - } else { - // Split a sequence of changes that was compressed into one unit. - oldLength_ = oldLen; - newLength_ = newLen; - if (num > 1) { - remaining = num; // This is the first of two or more changes. - } - return true; - } - } else { - U_ASSERT(u <= 0x7fff); - oldLength_ = readLength((u >> 6) & 0x3f); - newLength_ = readLength(u & 0x3f); - if (!coarse) { - return true; - } - } - // Combine adjacent changes. - while (index < length && (u = array[index]) > MAX_UNCHANGED) { - ++index; - if (u <= MAX_SHORT_CHANGE) { - int32_t num = (u & SHORT_CHANGE_NUM_MASK) + 1; - oldLength_ += (u >> 12) * num; - newLength_ += ((u >> 9) & MAX_SHORT_CHANGE_NEW_LENGTH) * num; - } else { - U_ASSERT(u <= 0x7fff); - oldLength_ += readLength((u >> 6) & 0x3f); - newLength_ += readLength(u & 0x3f); - } - } - return true; -} - -UBool Edits::Iterator::previous(UErrorCode &errorCode) { - // Backward iteration: Pre-decrement-read array units to assemble a new span, - // then update the string indexes to the start of that span. - // Leaves the array index on the head unit of that span. - if (U_FAILURE(errorCode)) { return false; } - // We have an errorCode in case we need to start guarding against integer overflows. - // It is also convenient for caller loops if we bail out when an error was set elsewhere. - if (dir >= 0) { - if (dir > 0) { - // Turn around from next() to previous(). - // Set the string indexes to the span limit and - // pre-decrement-read the same span again. - if (remaining > 0) { - // Fine-grained iterator: - // Stay on the current one of a sequence of compressed changes. - --index; // previous() rests on the sequence unit. - dir = -1; - return true; - } - updateNextIndexes(); - } - dir = -1; - } - if (remaining > 0) { - // Fine-grained iterator: Continue a sequence of compressed changes. - int32_t u = array[index]; - U_ASSERT(MAX_UNCHANGED < u && u <= MAX_SHORT_CHANGE); - if (remaining <= (u & SHORT_CHANGE_NUM_MASK)) { - ++remaining; - updatePreviousIndexes(); - return true; - } - remaining = 0; - } - if (index <= 0) { - return noNext(); - } - int32_t u = array[--index]; - if (u <= MAX_UNCHANGED) { - // Combine adjacent unchanged ranges. - changed = false; - oldLength_ = u + 1; - while (index > 0 && (u = array[index - 1]) <= MAX_UNCHANGED) { - --index; - oldLength_ += u + 1; - } - newLength_ = oldLength_; - // No need to handle onlyChanges as long as previous() is called only from findIndex(). - updatePreviousIndexes(); - return true; - } - changed = true; - if (u <= MAX_SHORT_CHANGE) { - int32_t oldLen = u >> 12; - int32_t newLen = (u >> 9) & MAX_SHORT_CHANGE_NEW_LENGTH; - int32_t num = (u & SHORT_CHANGE_NUM_MASK) + 1; - if (coarse) { - oldLength_ = num * oldLen; - newLength_ = num * newLen; - } else { - // Split a sequence of changes that was compressed into one unit. - oldLength_ = oldLen; - newLength_ = newLen; - if (num > 1) { - remaining = 1; // This is the last of two or more changes. - } - updatePreviousIndexes(); - return true; - } - } else { - if (u <= 0x7fff) { - // The change is encoded in u alone. - oldLength_ = readLength((u >> 6) & 0x3f); - newLength_ = readLength(u & 0x3f); - } else { - // Back up to the head of the change, read the lengths, - // and reset the index to the head again. - U_ASSERT(index > 0); - while ((u = array[--index]) > 0x7fff) {} - U_ASSERT(u > MAX_SHORT_CHANGE); - int32_t headIndex = index++; - oldLength_ = readLength((u >> 6) & 0x3f); - newLength_ = readLength(u & 0x3f); - index = headIndex; - } - if (!coarse) { - updatePreviousIndexes(); - return true; - } - } - // Combine adjacent changes. - while (index > 0 && (u = array[index - 1]) > MAX_UNCHANGED) { - --index; - if (u <= MAX_SHORT_CHANGE) { - int32_t num = (u & SHORT_CHANGE_NUM_MASK) + 1; - oldLength_ += (u >> 12) * num; - newLength_ += ((u >> 9) & MAX_SHORT_CHANGE_NEW_LENGTH) * num; - } else if (u <= 0x7fff) { - // Read the lengths, and reset the index to the head again. - int32_t headIndex = index++; - oldLength_ += readLength((u >> 6) & 0x3f); - newLength_ += readLength(u & 0x3f); - index = headIndex; - } - } - updatePreviousIndexes(); - return true; -} - -int32_t Edits::Iterator::findIndex(int32_t i, UBool findSource, UErrorCode &errorCode) { - if (U_FAILURE(errorCode) || i < 0) { return -1; } - int32_t spanStart, spanLength; - if (findSource) { // find source index - spanStart = srcIndex; - spanLength = oldLength_; - } else { // find destination index - spanStart = destIndex; - spanLength = newLength_; - } - if (i < spanStart) { - if (i >= (spanStart / 2)) { - // Search backwards. - for (;;) { - UBool hasPrevious = previous(errorCode); - U_ASSERT(hasPrevious); // because i>=0 and the first span starts at 0 - (void)hasPrevious; // avoid unused-variable warning - spanStart = findSource ? srcIndex : destIndex; - if (i >= spanStart) { - // The index is in the current span. - return 0; - } - if (remaining > 0) { - // Is the index in one of the remaining compressed edits? - // spanStart is the start of the current span, first of the remaining ones. - spanLength = findSource ? oldLength_ : newLength_; - int32_t u = array[index]; - U_ASSERT(MAX_UNCHANGED < u && u <= MAX_SHORT_CHANGE); - int32_t num = (u & SHORT_CHANGE_NUM_MASK) + 1 - remaining; - int32_t len = num * spanLength; - if (i >= (spanStart - len)) { - int32_t n = ((spanStart - i - 1) / spanLength) + 1; - // 1 <= n <= num - srcIndex -= n * oldLength_; - replIndex -= n * newLength_; - destIndex -= n * newLength_; - remaining += n; - return 0; - } - // Skip all of these edits at once. - srcIndex -= num * oldLength_; - replIndex -= num * newLength_; - destIndex -= num * newLength_; - remaining = 0; - } - } - } - // Reset the iterator to the start. - dir = 0; - index = remaining = oldLength_ = newLength_ = srcIndex = replIndex = destIndex = 0; - } else if (i < (spanStart + spanLength)) { - // The index is in the current span. - return 0; - } - while (next(false, errorCode)) { - if (findSource) { - spanStart = srcIndex; - spanLength = oldLength_; - } else { - spanStart = destIndex; - spanLength = newLength_; - } - if (i < (spanStart + spanLength)) { - // The index is in the current span. - return 0; - } - if (remaining > 1) { - // Is the index in one of the remaining compressed edits? - // spanStart is the start of the current span, first of the remaining ones. - int32_t len = remaining * spanLength; - if (i < (spanStart + len)) { - int32_t n = (i - spanStart) / spanLength; // 1 <= n <= remaining - 1 - srcIndex += n * oldLength_; - replIndex += n * newLength_; - destIndex += n * newLength_; - remaining -= n; - return 0; - } - // Make next() skip all of these edits at once. - oldLength_ *= remaining; - newLength_ *= remaining; - remaining = 0; - } - } - return 1; -} - -int32_t Edits::Iterator::destinationIndexFromSourceIndex(int32_t i, UErrorCode &errorCode) { - int32_t where = findIndex(i, true, errorCode); - if (where < 0) { - // Error or before the string. - return 0; - } - if (where > 0 || i == srcIndex) { - // At or after string length, or at start of the found span. - return destIndex; - } - if (changed) { - // In a change span, map to its end. - return destIndex + newLength_; - } else { - // In an unchanged span, offset 1:1 within it. - return destIndex + (i - srcIndex); - } -} - -int32_t Edits::Iterator::sourceIndexFromDestinationIndex(int32_t i, UErrorCode &errorCode) { - int32_t where = findIndex(i, false, errorCode); - if (where < 0) { - // Error or before the string. - return 0; - } - if (where > 0 || i == destIndex) { - // At or after string length, or at start of the found span. - return srcIndex; - } - if (changed) { - // In a change span, map to its end. - return srcIndex + oldLength_; - } else { - // In an unchanged span, offset within it. - return srcIndex + (i - destIndex); - } -} - -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 +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// edits.cpp +// created: 2017feb08 Markus W. Scherer + +#include "unicode/edits.h" +#include "unicode/unistr.h" +#include "unicode/utypes.h" +#include "cmemory.h" +#include "uassert.h" +#include "util.h" + +U_NAMESPACE_BEGIN + +namespace { + +// 0000uuuuuuuuuuuu records u+1 unchanged text units. +const int32_t MAX_UNCHANGED_LENGTH = 0x1000; +const int32_t MAX_UNCHANGED = MAX_UNCHANGED_LENGTH - 1; + +// 0mmmnnnccccccccc with m=1..6 records ccc+1 replacements of m:n text units. +const int32_t MAX_SHORT_CHANGE_OLD_LENGTH = 6; +const int32_t MAX_SHORT_CHANGE_NEW_LENGTH = 7; +const int32_t SHORT_CHANGE_NUM_MASK = 0x1ff; +const int32_t MAX_SHORT_CHANGE = 0x6fff; + +// 0111mmmmmmnnnnnn records a replacement of m text units with n. +// m or n = 61: actual length follows in the next edits array unit. +// m or n = 62..63: actual length follows in the next two edits array units. +// Bit 30 of the actual length is in the head unit. +// Trailing units have bit 15 set. +const int32_t LENGTH_IN_1TRAIL = 61; +const int32_t LENGTH_IN_2TRAIL = 62; + +} // namespace + +void Edits::releaseArray() noexcept { + if (array != stackArray) { + uprv_free(array); + } +} + +Edits &Edits::copyArray(const Edits &other) { + if (U_FAILURE(errorCode_)) { + length = delta = numChanges = 0; + return *this; + } + if (length > capacity) { + uint16_t *newArray = (uint16_t *)uprv_malloc((size_t)length * 2); + if (newArray == nullptr) { + length = delta = numChanges = 0; + errorCode_ = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + releaseArray(); + array = newArray; + capacity = length; + } + if (length > 0) { + uprv_memcpy(array, other.array, (size_t)length * 2); + } + return *this; +} + +Edits &Edits::moveArray(Edits &src) noexcept { + if (U_FAILURE(errorCode_)) { + length = delta = numChanges = 0; + return *this; + } + releaseArray(); + if (length > STACK_CAPACITY) { + array = src.array; + capacity = src.capacity; + src.array = src.stackArray; + src.capacity = STACK_CAPACITY; + src.reset(); + return *this; + } + array = stackArray; + capacity = STACK_CAPACITY; + if (length > 0) { + uprv_memcpy(array, src.array, (size_t)length * 2); + } + return *this; +} + +Edits &Edits::operator=(const Edits &other) { + if (this == &other) { return *this; } // self-assignment: no-op + length = other.length; + delta = other.delta; + numChanges = other.numChanges; + errorCode_ = other.errorCode_; + return copyArray(other); +} + +Edits &Edits::operator=(Edits &&src) noexcept { + length = src.length; + delta = src.delta; + numChanges = src.numChanges; + errorCode_ = src.errorCode_; + return moveArray(src); +} + +Edits::~Edits() { + releaseArray(); +} + +void Edits::reset() noexcept { + length = delta = numChanges = 0; + errorCode_ = U_ZERO_ERROR; +} + +void Edits::addUnchanged(int32_t unchangedLength) { + if(U_FAILURE(errorCode_) || unchangedLength == 0) { return; } + if(unchangedLength < 0) { + errorCode_ = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + // Merge into previous unchanged-text record, if any. + int32_t last = lastUnit(); + if(last < MAX_UNCHANGED) { + int32_t remaining = MAX_UNCHANGED - last; + if (remaining >= unchangedLength) { + setLastUnit(last + unchangedLength); + return; + } + setLastUnit(MAX_UNCHANGED); + unchangedLength -= remaining; + } + // Split large lengths into multiple units. + while(unchangedLength >= MAX_UNCHANGED_LENGTH) { + append(MAX_UNCHANGED); + unchangedLength -= MAX_UNCHANGED_LENGTH; + } + // Write a small (remaining) length. + if(unchangedLength > 0) { + append(unchangedLength - 1); + } +} + +void Edits::addReplace(int32_t oldLength, int32_t newLength) { + if(U_FAILURE(errorCode_)) { return; } + if(oldLength < 0 || newLength < 0) { + errorCode_ = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (oldLength == 0 && newLength == 0) { + return; + } + ++numChanges; + int32_t newDelta = newLength - oldLength; + if (newDelta != 0) { + if ((newDelta > 0 && delta >= 0 && newDelta > (INT32_MAX - delta)) || + (newDelta < 0 && delta < 0 && newDelta < (INT32_MIN - delta))) { + // Integer overflow or underflow. + errorCode_ = U_INDEX_OUTOFBOUNDS_ERROR; + return; + } + delta += newDelta; + } + + if(0 < oldLength && oldLength <= MAX_SHORT_CHANGE_OLD_LENGTH && + newLength <= MAX_SHORT_CHANGE_NEW_LENGTH) { + // Merge into previous same-lengths short-replacement record, if any. + int32_t u = (oldLength << 12) | (newLength << 9); + int32_t last = lastUnit(); + if(MAX_UNCHANGED < last && last < MAX_SHORT_CHANGE && + (last & ~SHORT_CHANGE_NUM_MASK) == u && + (last & SHORT_CHANGE_NUM_MASK) < SHORT_CHANGE_NUM_MASK) { + setLastUnit(last + 1); + return; + } + append(u); + return; + } + + int32_t head = 0x7000; + if (oldLength < LENGTH_IN_1TRAIL && newLength < LENGTH_IN_1TRAIL) { + head |= oldLength << 6; + head |= newLength; + append(head); + } else if ((capacity - length) >= 5 || growArray()) { + int32_t limit = length + 1; + if(oldLength < LENGTH_IN_1TRAIL) { + head |= oldLength << 6; + } else if(oldLength <= 0x7fff) { + head |= LENGTH_IN_1TRAIL << 6; + array[limit++] = (uint16_t)(0x8000 | oldLength); + } else { + head |= (LENGTH_IN_2TRAIL + (oldLength >> 30)) << 6; + array[limit++] = (uint16_t)(0x8000 | (oldLength >> 15)); + array[limit++] = (uint16_t)(0x8000 | oldLength); + } + if(newLength < LENGTH_IN_1TRAIL) { + head |= newLength; + } else if(newLength <= 0x7fff) { + head |= LENGTH_IN_1TRAIL; + array[limit++] = (uint16_t)(0x8000 | newLength); + } else { + head |= LENGTH_IN_2TRAIL + (newLength >> 30); + array[limit++] = (uint16_t)(0x8000 | (newLength >> 15)); + array[limit++] = (uint16_t)(0x8000 | newLength); + } + array[length] = (uint16_t)head; + length = limit; + } +} + +void Edits::append(int32_t r) { + if(length < capacity || growArray()) { + array[length++] = (uint16_t)r; + } +} + +UBool Edits::growArray() { + int32_t newCapacity; + if (array == stackArray) { + newCapacity = 2000; + } else if (capacity == INT32_MAX) { + // Not U_BUFFER_OVERFLOW_ERROR because that could be confused on a string transform API + // with a result-string-buffer overflow. + errorCode_ = U_INDEX_OUTOFBOUNDS_ERROR; + return false; + } else if (capacity >= (INT32_MAX / 2)) { + newCapacity = INT32_MAX; + } else { + newCapacity = 2 * capacity; + } + // Grow by at least 5 units so that a maximal change record will fit. + if ((newCapacity - capacity) < 5) { + errorCode_ = U_INDEX_OUTOFBOUNDS_ERROR; + return false; + } + uint16_t *newArray = (uint16_t *)uprv_malloc((size_t)newCapacity * 2); + if (newArray == nullptr) { + errorCode_ = U_MEMORY_ALLOCATION_ERROR; + return false; + } + uprv_memcpy(newArray, array, (size_t)length * 2); + releaseArray(); + array = newArray; + capacity = newCapacity; + return true; +} + +UBool Edits::copyErrorTo(UErrorCode &outErrorCode) const { + if (U_FAILURE(outErrorCode)) { return true; } + if (U_SUCCESS(errorCode_)) { return false; } + outErrorCode = errorCode_; + return true; +} + +Edits &Edits::mergeAndAppend(const Edits &ab, const Edits &bc, UErrorCode &errorCode) { + if (copyErrorTo(errorCode)) { return *this; } + // Picture string a --(Edits ab)--> string b --(Edits bc)--> string c. + // Parallel iteration over both Edits. + Iterator abIter = ab.getFineIterator(); + Iterator bcIter = bc.getFineIterator(); + UBool abHasNext = true, bcHasNext = true; + // Copy iterator state into local variables, so that we can modify and subdivide spans. + // ab old & new length, bc old & new length + int32_t aLength = 0, ab_bLength = 0, bc_bLength = 0, cLength = 0; + // When we have different-intermediate-length changes, we accumulate a larger change. + int32_t pending_aLength = 0, pending_cLength = 0; + for (;;) { + // At this point, for each of the two iterators: + // Either we are done with the locally cached current edit, + // and its intermediate-string length has been reset, + // or we will continue to work with a truncated remainder of this edit. + // + // If the current edit is done, and the iterator has not yet reached the end, + // then we fetch the next edit. This is true for at least one of the iterators. + // + // Normally it does not matter whether we fetch from ab and then bc or vice versa. + // However, the result is observably different when + // ab deletions meet bc insertions at the same intermediate-string index. + // Some users expect the bc insertions to come first, so we fetch from bc first. + if (bc_bLength == 0) { + if (bcHasNext && (bcHasNext = bcIter.next(errorCode)) != 0) { + bc_bLength = bcIter.oldLength(); + cLength = bcIter.newLength(); + if (bc_bLength == 0) { + // insertion + if (ab_bLength == 0 || !abIter.hasChange()) { + addReplace(pending_aLength, pending_cLength + cLength); + pending_aLength = pending_cLength = 0; + } else { + pending_cLength += cLength; + } + continue; + } + } + // else see if the other iterator is done, too. + } + if (ab_bLength == 0) { + if (abHasNext && (abHasNext = abIter.next(errorCode)) != 0) { + aLength = abIter.oldLength(); + ab_bLength = abIter.newLength(); + if (ab_bLength == 0) { + // deletion + if (bc_bLength == bcIter.oldLength() || !bcIter.hasChange()) { + addReplace(pending_aLength + aLength, pending_cLength); + pending_aLength = pending_cLength = 0; + } else { + pending_aLength += aLength; + } + continue; + } + } else if (bc_bLength == 0) { + // Both iterators are done at the same time: + // The intermediate-string lengths match. + break; + } else { + // The ab output string is shorter than the bc input string. + if (!copyErrorTo(errorCode)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + return *this; + } + } + if (bc_bLength == 0) { + // The bc input string is shorter than the ab output string. + if (!copyErrorTo(errorCode)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + return *this; + } + // Done fetching: ab_bLength > 0 && bc_bLength > 0 + + // The current state has two parts: + // - Past: We accumulate a longer ac edit in the "pending" variables. + // - Current: We have copies of the current ab/bc edits in local variables. + // At least one side is newly fetched. + // One side might be a truncated remainder of an edit we fetched earlier. + + if (!abIter.hasChange() && !bcIter.hasChange()) { + // An unchanged span all the way from string a to string c. + if (pending_aLength != 0 || pending_cLength != 0) { + addReplace(pending_aLength, pending_cLength); + pending_aLength = pending_cLength = 0; + } + int32_t unchangedLength = aLength <= cLength ? aLength : cLength; + addUnchanged(unchangedLength); + ab_bLength = aLength -= unchangedLength; + bc_bLength = cLength -= unchangedLength; + // At least one of the unchanged spans is now empty. + continue; + } + if (!abIter.hasChange() && bcIter.hasChange()) { + // Unchanged a->b but changed b->c. + if (ab_bLength >= bc_bLength) { + // Split the longer unchanged span into change + remainder. + addReplace(pending_aLength + bc_bLength, pending_cLength + cLength); + pending_aLength = pending_cLength = 0; + aLength = ab_bLength -= bc_bLength; + bc_bLength = 0; + continue; + } + // Handle the shorter unchanged span below like a change. + } else if (abIter.hasChange() && !bcIter.hasChange()) { + // Changed a->b and then unchanged b->c. + if (ab_bLength <= bc_bLength) { + // Split the longer unchanged span into change + remainder. + addReplace(pending_aLength + aLength, pending_cLength + ab_bLength); + pending_aLength = pending_cLength = 0; + cLength = bc_bLength -= ab_bLength; + ab_bLength = 0; + continue; + } + // Handle the shorter unchanged span below like a change. + } else { // both abIter.hasChange() && bcIter.hasChange() + if (ab_bLength == bc_bLength) { + // Changes on both sides up to the same position. Emit & reset. + addReplace(pending_aLength + aLength, pending_cLength + cLength); + pending_aLength = pending_cLength = 0; + ab_bLength = bc_bLength = 0; + continue; + } + } + // Accumulate the a->c change, reset the shorter side, + // keep a remainder of the longer one. + pending_aLength += aLength; + pending_cLength += cLength; + if (ab_bLength < bc_bLength) { + bc_bLength -= ab_bLength; + cLength = ab_bLength = 0; + } else { // ab_bLength > bc_bLength + ab_bLength -= bc_bLength; + aLength = bc_bLength = 0; + } + } + if (pending_aLength != 0 || pending_cLength != 0) { + addReplace(pending_aLength, pending_cLength); + } + copyErrorTo(errorCode); + return *this; +} + +Edits::Iterator::Iterator(const uint16_t *a, int32_t len, UBool oc, UBool crs) : + array(a), index(0), length(len), remaining(0), + onlyChanges_(oc), coarse(crs), + dir(0), changed(false), oldLength_(0), newLength_(0), + srcIndex(0), replIndex(0), destIndex(0) {} + +int32_t Edits::Iterator::readLength(int32_t head) { + if (head < LENGTH_IN_1TRAIL) { + return head; + } else if (head < LENGTH_IN_2TRAIL) { + U_ASSERT(index < length); + U_ASSERT(array[index] >= 0x8000); + return array[index++] & 0x7fff; + } else { + U_ASSERT((index + 2) <= length); + U_ASSERT(array[index] >= 0x8000); + U_ASSERT(array[index + 1] >= 0x8000); + int32_t len = ((head & 1) << 30) | + ((int32_t)(array[index] & 0x7fff) << 15) | + (array[index + 1] & 0x7fff); + index += 2; + return len; + } +} + +void Edits::Iterator::updateNextIndexes() { + srcIndex += oldLength_; + if (changed) { + replIndex += newLength_; + } + destIndex += newLength_; +} + +void Edits::Iterator::updatePreviousIndexes() { + srcIndex -= oldLength_; + if (changed) { + replIndex -= newLength_; + } + destIndex -= newLength_; +} + +UBool Edits::Iterator::noNext() { + // No change before or beyond the string. + dir = 0; + changed = false; + oldLength_ = newLength_ = 0; + return false; +} + +UBool Edits::Iterator::next(UBool onlyChanges, UErrorCode &errorCode) { + // Forward iteration: Update the string indexes to the limit of the current span, + // and post-increment-read array units to assemble a new span. + // Leaves the array index one after the last unit of that span. + if (U_FAILURE(errorCode)) { return false; } + // We have an errorCode in case we need to start guarding against integer overflows. + // It is also convenient for caller loops if we bail out when an error was set elsewhere. + if (dir > 0) { + updateNextIndexes(); + } else { + if (dir < 0) { + // Turn around from previous() to next(). + // Post-increment-read the same span again. + if (remaining > 0) { + // Fine-grained iterator: + // Stay on the current one of a sequence of compressed changes. + ++index; // next() rests on the index after the sequence unit. + dir = 1; + return true; + } + } + dir = 1; + } + if (remaining >= 1) { + // Fine-grained iterator: Continue a sequence of compressed changes. + if (remaining > 1) { + --remaining; + return true; + } + remaining = 0; + } + if (index >= length) { + return noNext(); + } + int32_t u = array[index++]; + if (u <= MAX_UNCHANGED) { + // Combine adjacent unchanged ranges. + changed = false; + oldLength_ = u + 1; + while (index < length && (u = array[index]) <= MAX_UNCHANGED) { + ++index; + oldLength_ += u + 1; + } + newLength_ = oldLength_; + if (onlyChanges) { + updateNextIndexes(); + if (index >= length) { + return noNext(); + } + // already fetched u > MAX_UNCHANGED at index + ++index; + } else { + return true; + } + } + changed = true; + if (u <= MAX_SHORT_CHANGE) { + int32_t oldLen = u >> 12; + int32_t newLen = (u >> 9) & MAX_SHORT_CHANGE_NEW_LENGTH; + int32_t num = (u & SHORT_CHANGE_NUM_MASK) + 1; + if (coarse) { + oldLength_ = num * oldLen; + newLength_ = num * newLen; + } else { + // Split a sequence of changes that was compressed into one unit. + oldLength_ = oldLen; + newLength_ = newLen; + if (num > 1) { + remaining = num; // This is the first of two or more changes. + } + return true; + } + } else { + U_ASSERT(u <= 0x7fff); + oldLength_ = readLength((u >> 6) & 0x3f); + newLength_ = readLength(u & 0x3f); + if (!coarse) { + return true; + } + } + // Combine adjacent changes. + while (index < length && (u = array[index]) > MAX_UNCHANGED) { + ++index; + if (u <= MAX_SHORT_CHANGE) { + int32_t num = (u & SHORT_CHANGE_NUM_MASK) + 1; + oldLength_ += (u >> 12) * num; + newLength_ += ((u >> 9) & MAX_SHORT_CHANGE_NEW_LENGTH) * num; + } else { + U_ASSERT(u <= 0x7fff); + oldLength_ += readLength((u >> 6) & 0x3f); + newLength_ += readLength(u & 0x3f); + } + } + return true; +} + +UBool Edits::Iterator::previous(UErrorCode &errorCode) { + // Backward iteration: Pre-decrement-read array units to assemble a new span, + // then update the string indexes to the start of that span. + // Leaves the array index on the head unit of that span. + if (U_FAILURE(errorCode)) { return false; } + // We have an errorCode in case we need to start guarding against integer overflows. + // It is also convenient for caller loops if we bail out when an error was set elsewhere. + if (dir >= 0) { + if (dir > 0) { + // Turn around from next() to previous(). + // Set the string indexes to the span limit and + // pre-decrement-read the same span again. + if (remaining > 0) { + // Fine-grained iterator: + // Stay on the current one of a sequence of compressed changes. + --index; // previous() rests on the sequence unit. + dir = -1; + return true; + } + updateNextIndexes(); + } + dir = -1; + } + if (remaining > 0) { + // Fine-grained iterator: Continue a sequence of compressed changes. + int32_t u = array[index]; + U_ASSERT(MAX_UNCHANGED < u && u <= MAX_SHORT_CHANGE); + if (remaining <= (u & SHORT_CHANGE_NUM_MASK)) { + ++remaining; + updatePreviousIndexes(); + return true; + } + remaining = 0; + } + if (index <= 0) { + return noNext(); + } + int32_t u = array[--index]; + if (u <= MAX_UNCHANGED) { + // Combine adjacent unchanged ranges. + changed = false; + oldLength_ = u + 1; + while (index > 0 && (u = array[index - 1]) <= MAX_UNCHANGED) { + --index; + oldLength_ += u + 1; + } + newLength_ = oldLength_; + // No need to handle onlyChanges as long as previous() is called only from findIndex(). + updatePreviousIndexes(); + return true; + } + changed = true; + if (u <= MAX_SHORT_CHANGE) { + int32_t oldLen = u >> 12; + int32_t newLen = (u >> 9) & MAX_SHORT_CHANGE_NEW_LENGTH; + int32_t num = (u & SHORT_CHANGE_NUM_MASK) + 1; + if (coarse) { + oldLength_ = num * oldLen; + newLength_ = num * newLen; + } else { + // Split a sequence of changes that was compressed into one unit. + oldLength_ = oldLen; + newLength_ = newLen; + if (num > 1) { + remaining = 1; // This is the last of two or more changes. + } + updatePreviousIndexes(); + return true; + } + } else { + if (u <= 0x7fff) { + // The change is encoded in u alone. + oldLength_ = readLength((u >> 6) & 0x3f); + newLength_ = readLength(u & 0x3f); + } else { + // Back up to the head of the change, read the lengths, + // and reset the index to the head again. + U_ASSERT(index > 0); + while ((u = array[--index]) > 0x7fff) {} + U_ASSERT(u > MAX_SHORT_CHANGE); + int32_t headIndex = index++; + oldLength_ = readLength((u >> 6) & 0x3f); + newLength_ = readLength(u & 0x3f); + index = headIndex; + } + if (!coarse) { + updatePreviousIndexes(); + return true; + } + } + // Combine adjacent changes. + while (index > 0 && (u = array[index - 1]) > MAX_UNCHANGED) { + --index; + if (u <= MAX_SHORT_CHANGE) { + int32_t num = (u & SHORT_CHANGE_NUM_MASK) + 1; + oldLength_ += (u >> 12) * num; + newLength_ += ((u >> 9) & MAX_SHORT_CHANGE_NEW_LENGTH) * num; + } else if (u <= 0x7fff) { + // Read the lengths, and reset the index to the head again. + int32_t headIndex = index++; + oldLength_ += readLength((u >> 6) & 0x3f); + newLength_ += readLength(u & 0x3f); + index = headIndex; + } + } + updatePreviousIndexes(); + return true; +} + +int32_t Edits::Iterator::findIndex(int32_t i, UBool findSource, UErrorCode &errorCode) { + if (U_FAILURE(errorCode) || i < 0) { return -1; } + int32_t spanStart, spanLength; + if (findSource) { // find source index + spanStart = srcIndex; + spanLength = oldLength_; + } else { // find destination index + spanStart = destIndex; + spanLength = newLength_; + } + if (i < spanStart) { + if (i >= (spanStart / 2)) { + // Search backwards. + for (;;) { + UBool hasPrevious = previous(errorCode); + U_ASSERT(hasPrevious); // because i>=0 and the first span starts at 0 + (void)hasPrevious; // avoid unused-variable warning + spanStart = findSource ? srcIndex : destIndex; + if (i >= spanStart) { + // The index is in the current span. + return 0; + } + if (remaining > 0) { + // Is the index in one of the remaining compressed edits? + // spanStart is the start of the current span, first of the remaining ones. + spanLength = findSource ? oldLength_ : newLength_; + int32_t u = array[index]; + U_ASSERT(MAX_UNCHANGED < u && u <= MAX_SHORT_CHANGE); + int32_t num = (u & SHORT_CHANGE_NUM_MASK) + 1 - remaining; + int32_t len = num * spanLength; + if (i >= (spanStart - len)) { + int32_t n = ((spanStart - i - 1) / spanLength) + 1; + // 1 <= n <= num + srcIndex -= n * oldLength_; + replIndex -= n * newLength_; + destIndex -= n * newLength_; + remaining += n; + return 0; + } + // Skip all of these edits at once. + srcIndex -= num * oldLength_; + replIndex -= num * newLength_; + destIndex -= num * newLength_; + remaining = 0; + } + } + } + // Reset the iterator to the start. + dir = 0; + index = remaining = oldLength_ = newLength_ = srcIndex = replIndex = destIndex = 0; + } else if (i < (spanStart + spanLength)) { + // The index is in the current span. + return 0; + } + while (next(false, errorCode)) { + if (findSource) { + spanStart = srcIndex; + spanLength = oldLength_; + } else { + spanStart = destIndex; + spanLength = newLength_; + } + if (i < (spanStart + spanLength)) { + // The index is in the current span. + return 0; + } + if (remaining > 1) { + // Is the index in one of the remaining compressed edits? + // spanStart is the start of the current span, first of the remaining ones. + int32_t len = remaining * spanLength; + if (i < (spanStart + len)) { + int32_t n = (i - spanStart) / spanLength; // 1 <= n <= remaining - 1 + srcIndex += n * oldLength_; + replIndex += n * newLength_; + destIndex += n * newLength_; + remaining -= n; + return 0; + } + // Make next() skip all of these edits at once. + oldLength_ *= remaining; + newLength_ *= remaining; + remaining = 0; + } + } + return 1; +} + +int32_t Edits::Iterator::destinationIndexFromSourceIndex(int32_t i, UErrorCode &errorCode) { + int32_t where = findIndex(i, true, errorCode); + if (where < 0) { + // Error or before the string. + return 0; + } + if (where > 0 || i == srcIndex) { + // At or after string length, or at start of the found span. + return destIndex; + } + if (changed) { + // In a change span, map to its end. + return destIndex + newLength_; + } else { + // In an unchanged span, offset 1:1 within it. + return destIndex + (i - srcIndex); + } +} + +int32_t Edits::Iterator::sourceIndexFromDestinationIndex(int32_t i, UErrorCode &errorCode) { + int32_t where = findIndex(i, false, errorCode); + if (where < 0) { + // Error or before the string. + return 0; + } + if (where > 0 || i == destIndex) { + // At or after string length, or at start of the found span. + return srcIndex; + } + if (changed) { + // In a change span, map to its end. + return srcIndex + oldLength_; + } else { + // In an unchanged span, offset within it. + return srcIndex + (i - destIndex); + } +} + +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/emojiprops.cpp b/deps/icu-small/source/common/emojiprops.cpp index d07e07c6ccc79a..b3e09540464683 100644 --- a/deps/icu-small/source/common/emojiprops.cpp +++ b/deps/icu-small/source/common/emojiprops.cpp @@ -1,220 +1,220 @@ -// © 2021 and later: Unicode, Inc. and others. -// License & terms of use: https://www.unicode.org/copyright.html - -// emojiprops.cpp -// created: 2021sep04 Markus W. Scherer - -#include "unicode/utypes.h" -#include "unicode/uchar.h" -#include "unicode/ucharstrie.h" -#include "unicode/ucptrie.h" -#include "unicode/udata.h" -#include "unicode/ustringtrie.h" -#include "unicode/utf16.h" -#include "emojiprops.h" -#include "ucln.h" -#include "ucln_cmn.h" -#include "umutex.h" -#include "uset_imp.h" - -U_NAMESPACE_BEGIN - -namespace { - -EmojiProps *singleton = nullptr; -icu::UInitOnce emojiInitOnce {}; - -UBool U_CALLCONV emojiprops_cleanup() { - delete singleton; - singleton = nullptr; - emojiInitOnce.reset(); - return true; -} - -void U_CALLCONV initSingleton(UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return; } - singleton = new EmojiProps(errorCode); - if (singleton == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - } else if (U_FAILURE(errorCode)) { - delete singleton; - singleton = nullptr; - } - ucln_common_registerCleanup(UCLN_COMMON_EMOJIPROPS, emojiprops_cleanup); -} - -// TODO: turn this into a shared helper function -// Requires the major version to match, and then requires at least the minor version. -UBool udata_isAcceptableMajorMinor( - const UDataInfo &info, const UChar *dataFormat, uint8_t major, uint8_t minor) { - return - info.size >= 20 && - info.isBigEndian == U_IS_BIG_ENDIAN && - info.charsetFamily == U_CHARSET_FAMILY && - info.dataFormat[0] == dataFormat[0] && - info.dataFormat[1] == dataFormat[1] && - info.dataFormat[2] == dataFormat[2] && - info.dataFormat[3] == dataFormat[3] && - info.formatVersion[0] == major && - info.formatVersion[1] >= minor; -} - -} // namespace - -EmojiProps::~EmojiProps() { - udata_close(memory); - ucptrie_close(cpTrie); -} - -const EmojiProps * -EmojiProps::getSingleton(UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return nullptr; } - umtx_initOnce(emojiInitOnce, &initSingleton, errorCode); - return singleton; -} - -UBool U_CALLCONV -EmojiProps::isAcceptable(void * /*context*/, const char * /*type*/, const char * /*name*/, - const UDataInfo *pInfo) { - return udata_isAcceptableMajorMinor(*pInfo, u"Emoj", 1, 0); -} - -void -EmojiProps::load(UErrorCode &errorCode) { - memory = udata_openChoice(nullptr, "icu", "uemoji", isAcceptable, this, &errorCode); - if (U_FAILURE(errorCode)) { return; } - const uint8_t *inBytes = (const uint8_t *)udata_getMemory(memory); - const int32_t *inIndexes = (const int32_t *)inBytes; - int32_t indexesLength = inIndexes[IX_CPTRIE_OFFSET] / 4; - if (indexesLength <= IX_RGI_EMOJI_ZWJ_SEQUENCE_TRIE_OFFSET) { - errorCode = U_INVALID_FORMAT_ERROR; // Not enough indexes. - return; - } - - int32_t i = IX_CPTRIE_OFFSET; - int32_t offset = inIndexes[i++]; - int32_t nextOffset = inIndexes[i]; - cpTrie = ucptrie_openFromBinary(UCPTRIE_TYPE_FAST, UCPTRIE_VALUE_BITS_8, - inBytes + offset, nextOffset - offset, nullptr, &errorCode); - if (U_FAILURE(errorCode)) { - return; - } - - for (i = IX_BASIC_EMOJI_TRIE_OFFSET; i <= IX_RGI_EMOJI_ZWJ_SEQUENCE_TRIE_OFFSET; ++i) { - offset = inIndexes[i]; - nextOffset = inIndexes[i + 1]; - // Set/leave nullptr if there is no UCharsTrie. - const UChar *p = nextOffset > offset ? (const UChar *)(inBytes + offset) : nullptr; - stringTries[getStringTrieIndex(i)] = p; - } -} - -void -EmojiProps::addPropertyStarts(const USetAdder *sa, UErrorCode & /*errorCode*/) const { - // Add the start code point of each same-value range of the trie. - UChar32 start = 0, end; - uint32_t value; - while ((end = ucptrie_getRange(cpTrie, start, UCPMAP_RANGE_NORMAL, 0, - nullptr, nullptr, &value)) >= 0) { - sa->add(sa->set, start); - start = end + 1; - } -} - -UBool -EmojiProps::hasBinaryProperty(UChar32 c, UProperty which) { - UErrorCode errorCode = U_ZERO_ERROR; - const EmojiProps *ep = getSingleton(errorCode); - return U_SUCCESS(errorCode) && ep->hasBinaryPropertyImpl(c, which); -} - -UBool -EmojiProps::hasBinaryPropertyImpl(UChar32 c, UProperty which) const { - if (which < UCHAR_EMOJI || UCHAR_RGI_EMOJI < which) { - return false; - } - // Note: UCHAR_REGIONAL_INDICATOR is a single, hardcoded range implemented elsewhere. - static constexpr int8_t bitFlags[] = { - BIT_EMOJI, // UCHAR_EMOJI=57 - BIT_EMOJI_PRESENTATION, // UCHAR_EMOJI_PRESENTATION=58 - BIT_EMOJI_MODIFIER, // UCHAR_EMOJI_MODIFIER=59 - BIT_EMOJI_MODIFIER_BASE, // UCHAR_EMOJI_MODIFIER_BASE=60 - BIT_EMOJI_COMPONENT, // UCHAR_EMOJI_COMPONENT=61 - -1, // UCHAR_REGIONAL_INDICATOR=62 - -1, // UCHAR_PREPENDED_CONCATENATION_MARK=63 - BIT_EXTENDED_PICTOGRAPHIC, // UCHAR_EXTENDED_PICTOGRAPHIC=64 - BIT_BASIC_EMOJI, // UCHAR_BASIC_EMOJI=65 - -1, // UCHAR_EMOJI_KEYCAP_SEQUENCE=66 - -1, // UCHAR_RGI_EMOJI_MODIFIER_SEQUENCE=67 - -1, // UCHAR_RGI_EMOJI_FLAG_SEQUENCE=68 - -1, // UCHAR_RGI_EMOJI_TAG_SEQUENCE=69 - -1, // UCHAR_RGI_EMOJI_ZWJ_SEQUENCE=70 - BIT_BASIC_EMOJI, // UCHAR_RGI_EMOJI=71 - }; - int32_t bit = bitFlags[which - UCHAR_EMOJI]; - if (bit < 0) { - return false; // not a property that we support in this function - } - uint8_t bits = UCPTRIE_FAST_GET(cpTrie, UCPTRIE_8, c); - return (bits >> bit) & 1; -} - -UBool -EmojiProps::hasBinaryProperty(const UChar *s, int32_t length, UProperty which) { - UErrorCode errorCode = U_ZERO_ERROR; - const EmojiProps *ep = getSingleton(errorCode); - return U_SUCCESS(errorCode) && ep->hasBinaryPropertyImpl(s, length, which); -} - -UBool -EmojiProps::hasBinaryPropertyImpl(const UChar *s, int32_t length, UProperty which) const { - if (s == nullptr && length != 0) { return false; } - if (length <= 0 && (length == 0 || *s == 0)) { return false; } // empty string - // The caller should have delegated single code points to hasBinaryProperty(c, which). - if (which < UCHAR_BASIC_EMOJI || UCHAR_RGI_EMOJI < which) { - return false; - } - UProperty firstProp = which, lastProp = which; - if (which == UCHAR_RGI_EMOJI) { - // RGI_Emoji is the union of the other emoji properties of strings. - firstProp = UCHAR_BASIC_EMOJI; - lastProp = UCHAR_RGI_EMOJI_ZWJ_SEQUENCE; - } - for (int32_t prop = firstProp; prop <= lastProp; ++prop) { - const UChar *trieUChars = stringTries[prop - UCHAR_BASIC_EMOJI]; - if (trieUChars != nullptr) { - UCharsTrie trie(trieUChars); - UStringTrieResult result = trie.next(s, length); - if (USTRINGTRIE_HAS_VALUE(result)) { - return true; - } - } - } - return false; -} - -void -EmojiProps::addStrings(const USetAdder *sa, UProperty which, UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { return; } - if (which < UCHAR_BASIC_EMOJI || UCHAR_RGI_EMOJI < which) { - return; - } - UProperty firstProp = which, lastProp = which; - if (which == UCHAR_RGI_EMOJI) { - // RGI_Emoji is the union of the other emoji properties of strings. - firstProp = UCHAR_BASIC_EMOJI; - lastProp = UCHAR_RGI_EMOJI_ZWJ_SEQUENCE; - } - for (int32_t prop = firstProp; prop <= lastProp; ++prop) { - const UChar *trieUChars = stringTries[prop - UCHAR_BASIC_EMOJI]; - if (trieUChars != nullptr) { - UCharsTrie::Iterator iter(trieUChars, 0, errorCode); - while (iter.next(errorCode)) { - const UnicodeString &s = iter.getString(); - sa->addString(sa->set, s.getBuffer(), s.length()); - } - } - } -} - -U_NAMESPACE_END +// © 2021 and later: Unicode, Inc. and others. +// License & terms of use: https://www.unicode.org/copyright.html + +// emojiprops.cpp +// created: 2021sep04 Markus W. Scherer + +#include "unicode/utypes.h" +#include "unicode/uchar.h" +#include "unicode/ucharstrie.h" +#include "unicode/ucptrie.h" +#include "unicode/udata.h" +#include "unicode/ustringtrie.h" +#include "unicode/utf16.h" +#include "emojiprops.h" +#include "ucln.h" +#include "ucln_cmn.h" +#include "umutex.h" +#include "uset_imp.h" + +U_NAMESPACE_BEGIN + +namespace { + +EmojiProps *singleton = nullptr; +icu::UInitOnce emojiInitOnce {}; + +UBool U_CALLCONV emojiprops_cleanup() { + delete singleton; + singleton = nullptr; + emojiInitOnce.reset(); + return true; +} + +void U_CALLCONV initSingleton(UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return; } + singleton = new EmojiProps(errorCode); + if (singleton == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + } else if (U_FAILURE(errorCode)) { + delete singleton; + singleton = nullptr; + } + ucln_common_registerCleanup(UCLN_COMMON_EMOJIPROPS, emojiprops_cleanup); +} + +// TODO: turn this into a shared helper function +// Requires the major version to match, and then requires at least the minor version. +UBool udata_isAcceptableMajorMinor( + const UDataInfo &info, const char16_t *dataFormat, uint8_t major, uint8_t minor) { + return + info.size >= 20 && + info.isBigEndian == U_IS_BIG_ENDIAN && + info.charsetFamily == U_CHARSET_FAMILY && + info.dataFormat[0] == dataFormat[0] && + info.dataFormat[1] == dataFormat[1] && + info.dataFormat[2] == dataFormat[2] && + info.dataFormat[3] == dataFormat[3] && + info.formatVersion[0] == major && + info.formatVersion[1] >= minor; +} + +} // namespace + +EmojiProps::~EmojiProps() { + udata_close(memory); + ucptrie_close(cpTrie); +} + +const EmojiProps * +EmojiProps::getSingleton(UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return nullptr; } + umtx_initOnce(emojiInitOnce, &initSingleton, errorCode); + return singleton; +} + +UBool U_CALLCONV +EmojiProps::isAcceptable(void * /*context*/, const char * /*type*/, const char * /*name*/, + const UDataInfo *pInfo) { + return udata_isAcceptableMajorMinor(*pInfo, u"Emoj", 1, 0); +} + +void +EmojiProps::load(UErrorCode &errorCode) { + memory = udata_openChoice(nullptr, "icu", "uemoji", isAcceptable, this, &errorCode); + if (U_FAILURE(errorCode)) { return; } + const uint8_t *inBytes = (const uint8_t *)udata_getMemory(memory); + const int32_t *inIndexes = (const int32_t *)inBytes; + int32_t indexesLength = inIndexes[IX_CPTRIE_OFFSET] / 4; + if (indexesLength <= IX_RGI_EMOJI_ZWJ_SEQUENCE_TRIE_OFFSET) { + errorCode = U_INVALID_FORMAT_ERROR; // Not enough indexes. + return; + } + + int32_t i = IX_CPTRIE_OFFSET; + int32_t offset = inIndexes[i++]; + int32_t nextOffset = inIndexes[i]; + cpTrie = ucptrie_openFromBinary(UCPTRIE_TYPE_FAST, UCPTRIE_VALUE_BITS_8, + inBytes + offset, nextOffset - offset, nullptr, &errorCode); + if (U_FAILURE(errorCode)) { + return; + } + + for (i = IX_BASIC_EMOJI_TRIE_OFFSET; i <= IX_RGI_EMOJI_ZWJ_SEQUENCE_TRIE_OFFSET; ++i) { + offset = inIndexes[i]; + nextOffset = inIndexes[i + 1]; + // Set/leave nullptr if there is no UCharsTrie. + const char16_t *p = nextOffset > offset ? (const char16_t *)(inBytes + offset) : nullptr; + stringTries[getStringTrieIndex(i)] = p; + } +} + +void +EmojiProps::addPropertyStarts(const USetAdder *sa, UErrorCode & /*errorCode*/) const { + // Add the start code point of each same-value range of the trie. + UChar32 start = 0, end; + uint32_t value; + while ((end = ucptrie_getRange(cpTrie, start, UCPMAP_RANGE_NORMAL, 0, + nullptr, nullptr, &value)) >= 0) { + sa->add(sa->set, start); + start = end + 1; + } +} + +UBool +EmojiProps::hasBinaryProperty(UChar32 c, UProperty which) { + UErrorCode errorCode = U_ZERO_ERROR; + const EmojiProps *ep = getSingleton(errorCode); + return U_SUCCESS(errorCode) && ep->hasBinaryPropertyImpl(c, which); +} + +UBool +EmojiProps::hasBinaryPropertyImpl(UChar32 c, UProperty which) const { + if (which < UCHAR_EMOJI || UCHAR_RGI_EMOJI < which) { + return false; + } + // Note: UCHAR_REGIONAL_INDICATOR is a single, hardcoded range implemented elsewhere. + static constexpr int8_t bitFlags[] = { + BIT_EMOJI, // UCHAR_EMOJI=57 + BIT_EMOJI_PRESENTATION, // UCHAR_EMOJI_PRESENTATION=58 + BIT_EMOJI_MODIFIER, // UCHAR_EMOJI_MODIFIER=59 + BIT_EMOJI_MODIFIER_BASE, // UCHAR_EMOJI_MODIFIER_BASE=60 + BIT_EMOJI_COMPONENT, // UCHAR_EMOJI_COMPONENT=61 + -1, // UCHAR_REGIONAL_INDICATOR=62 + -1, // UCHAR_PREPENDED_CONCATENATION_MARK=63 + BIT_EXTENDED_PICTOGRAPHIC, // UCHAR_EXTENDED_PICTOGRAPHIC=64 + BIT_BASIC_EMOJI, // UCHAR_BASIC_EMOJI=65 + -1, // UCHAR_EMOJI_KEYCAP_SEQUENCE=66 + -1, // UCHAR_RGI_EMOJI_MODIFIER_SEQUENCE=67 + -1, // UCHAR_RGI_EMOJI_FLAG_SEQUENCE=68 + -1, // UCHAR_RGI_EMOJI_TAG_SEQUENCE=69 + -1, // UCHAR_RGI_EMOJI_ZWJ_SEQUENCE=70 + BIT_BASIC_EMOJI, // UCHAR_RGI_EMOJI=71 + }; + int32_t bit = bitFlags[which - UCHAR_EMOJI]; + if (bit < 0) { + return false; // not a property that we support in this function + } + uint8_t bits = UCPTRIE_FAST_GET(cpTrie, UCPTRIE_8, c); + return (bits >> bit) & 1; +} + +UBool +EmojiProps::hasBinaryProperty(const char16_t *s, int32_t length, UProperty which) { + UErrorCode errorCode = U_ZERO_ERROR; + const EmojiProps *ep = getSingleton(errorCode); + return U_SUCCESS(errorCode) && ep->hasBinaryPropertyImpl(s, length, which); +} + +UBool +EmojiProps::hasBinaryPropertyImpl(const char16_t *s, int32_t length, UProperty which) const { + if (s == nullptr && length != 0) { return false; } + if (length <= 0 && (length == 0 || *s == 0)) { return false; } // empty string + // The caller should have delegated single code points to hasBinaryProperty(c, which). + if (which < UCHAR_BASIC_EMOJI || UCHAR_RGI_EMOJI < which) { + return false; + } + UProperty firstProp = which, lastProp = which; + if (which == UCHAR_RGI_EMOJI) { + // RGI_Emoji is the union of the other emoji properties of strings. + firstProp = UCHAR_BASIC_EMOJI; + lastProp = UCHAR_RGI_EMOJI_ZWJ_SEQUENCE; + } + for (int32_t prop = firstProp; prop <= lastProp; ++prop) { + const char16_t *trieUChars = stringTries[prop - UCHAR_BASIC_EMOJI]; + if (trieUChars != nullptr) { + UCharsTrie trie(trieUChars); + UStringTrieResult result = trie.next(s, length); + if (USTRINGTRIE_HAS_VALUE(result)) { + return true; + } + } + } + return false; +} + +void +EmojiProps::addStrings(const USetAdder *sa, UProperty which, UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { return; } + if (which < UCHAR_BASIC_EMOJI || UCHAR_RGI_EMOJI < which) { + return; + } + UProperty firstProp = which, lastProp = which; + if (which == UCHAR_RGI_EMOJI) { + // RGI_Emoji is the union of the other emoji properties of strings. + firstProp = UCHAR_BASIC_EMOJI; + lastProp = UCHAR_RGI_EMOJI_ZWJ_SEQUENCE; + } + for (int32_t prop = firstProp; prop <= lastProp; ++prop) { + const char16_t *trieUChars = stringTries[prop - UCHAR_BASIC_EMOJI]; + if (trieUChars != nullptr) { + UCharsTrie::Iterator iter(trieUChars, 0, errorCode); + while (iter.next(errorCode)) { + const UnicodeString &s = iter.getString(); + sa->addString(sa->set, s.getBuffer(), s.length()); + } + } + } +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/emojiprops.h b/deps/icu-small/source/common/emojiprops.h index 457847c303ab1a..214f66068c7a68 100644 --- a/deps/icu-small/source/common/emojiprops.h +++ b/deps/icu-small/source/common/emojiprops.h @@ -1,90 +1,90 @@ -// © 2021 and later: Unicode, Inc. and others. -// License & terms of use: https://www.unicode.org/copyright.html - -// emojiprops.h -// created: 2021sep03 Markus W. Scherer - -#ifndef __EMOJIPROPS_H__ -#define __EMOJIPROPS_H__ - -#include "unicode/utypes.h" -#include "unicode/ucptrie.h" -#include "unicode/udata.h" -#include "unicode/uobject.h" -#include "uset_imp.h" - -U_NAMESPACE_BEGIN - -class EmojiProps : public UMemory { -public: - // @internal - EmojiProps(UErrorCode &errorCode) { load(errorCode); } - ~EmojiProps(); - - static const EmojiProps *getSingleton(UErrorCode &errorCode); - static UBool hasBinaryProperty(UChar32 c, UProperty which); - static UBool hasBinaryProperty(const UChar *s, int32_t length, UProperty which); - - void addPropertyStarts(const USetAdder *sa, UErrorCode &errorCode) const; - void addStrings(const USetAdder *sa, UProperty which, UErrorCode &errorCode) const; - - enum { - // Byte offsets from the start of the data, after the generic header, - // in ascending order. - // UCPTrie=CodePointTrie, follows the indexes - IX_CPTRIE_OFFSET, - IX_RESERVED1, - IX_RESERVED2, - IX_RESERVED3, - - // UCharsTrie=CharsTrie - IX_BASIC_EMOJI_TRIE_OFFSET, - IX_EMOJI_KEYCAP_SEQUENCE_TRIE_OFFSET, - IX_RGI_EMOJI_MODIFIER_SEQUENCE_TRIE_OFFSET, - IX_RGI_EMOJI_FLAG_SEQUENCE_TRIE_OFFSET, - IX_RGI_EMOJI_TAG_SEQUENCE_TRIE_OFFSET, - IX_RGI_EMOJI_ZWJ_SEQUENCE_TRIE_OFFSET, - IX_RESERVED10, - IX_RESERVED11, - IX_RESERVED12, - IX_TOTAL_SIZE, - - // Not initially byte offsets. - IX_RESERVED14, - IX_RESERVED15, - IX_COUNT // 16 - }; - - // Properties in the code point trie. - enum { - // https://www.unicode.org/reports/tr51/#Emoji_Properties - BIT_EMOJI, - BIT_EMOJI_PRESENTATION, - BIT_EMOJI_MODIFIER, - BIT_EMOJI_MODIFIER_BASE, - BIT_EMOJI_COMPONENT, - BIT_EXTENDED_PICTOGRAPHIC, - // https://www.unicode.org/reports/tr51/#Emoji_Sets - BIT_BASIC_EMOJI - }; - -private: - static UBool U_CALLCONV - isAcceptable(void *context, const char *type, const char *name, const UDataInfo *pInfo); - /** Input i: One of the IX_..._TRIE_OFFSET indexes into the data file indexes[] array. */ - static int32_t getStringTrieIndex(int32_t i) { - return i - IX_BASIC_EMOJI_TRIE_OFFSET; - } - - void load(UErrorCode &errorCode); - UBool hasBinaryPropertyImpl(UChar32 c, UProperty which) const; - UBool hasBinaryPropertyImpl(const UChar *s, int32_t length, UProperty which) const; - - UDataMemory *memory = nullptr; - UCPTrie *cpTrie = nullptr; - const UChar *stringTries[6] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; -}; - -U_NAMESPACE_END - -#endif // __EMOJIPROPS_H__ +// © 2021 and later: Unicode, Inc. and others. +// License & terms of use: https://www.unicode.org/copyright.html + +// emojiprops.h +// created: 2021sep03 Markus W. Scherer + +#ifndef __EMOJIPROPS_H__ +#define __EMOJIPROPS_H__ + +#include "unicode/utypes.h" +#include "unicode/ucptrie.h" +#include "unicode/udata.h" +#include "unicode/uobject.h" +#include "uset_imp.h" + +U_NAMESPACE_BEGIN + +class EmojiProps : public UMemory { +public: + // @internal + EmojiProps(UErrorCode &errorCode) { load(errorCode); } + ~EmojiProps(); + + static const EmojiProps *getSingleton(UErrorCode &errorCode); + static UBool hasBinaryProperty(UChar32 c, UProperty which); + static UBool hasBinaryProperty(const char16_t *s, int32_t length, UProperty which); + + void addPropertyStarts(const USetAdder *sa, UErrorCode &errorCode) const; + void addStrings(const USetAdder *sa, UProperty which, UErrorCode &errorCode) const; + + enum { + // Byte offsets from the start of the data, after the generic header, + // in ascending order. + // UCPTrie=CodePointTrie, follows the indexes + IX_CPTRIE_OFFSET, + IX_RESERVED1, + IX_RESERVED2, + IX_RESERVED3, + + // UCharsTrie=CharsTrie + IX_BASIC_EMOJI_TRIE_OFFSET, + IX_EMOJI_KEYCAP_SEQUENCE_TRIE_OFFSET, + IX_RGI_EMOJI_MODIFIER_SEQUENCE_TRIE_OFFSET, + IX_RGI_EMOJI_FLAG_SEQUENCE_TRIE_OFFSET, + IX_RGI_EMOJI_TAG_SEQUENCE_TRIE_OFFSET, + IX_RGI_EMOJI_ZWJ_SEQUENCE_TRIE_OFFSET, + IX_RESERVED10, + IX_RESERVED11, + IX_RESERVED12, + IX_TOTAL_SIZE, + + // Not initially byte offsets. + IX_RESERVED14, + IX_RESERVED15, + IX_COUNT // 16 + }; + + // Properties in the code point trie. + enum { + // https://www.unicode.org/reports/tr51/#Emoji_Properties + BIT_EMOJI, + BIT_EMOJI_PRESENTATION, + BIT_EMOJI_MODIFIER, + BIT_EMOJI_MODIFIER_BASE, + BIT_EMOJI_COMPONENT, + BIT_EXTENDED_PICTOGRAPHIC, + // https://www.unicode.org/reports/tr51/#Emoji_Sets + BIT_BASIC_EMOJI + }; + +private: + static UBool U_CALLCONV + isAcceptable(void *context, const char *type, const char *name, const UDataInfo *pInfo); + /** Input i: One of the IX_..._TRIE_OFFSET indexes into the data file indexes[] array. */ + static int32_t getStringTrieIndex(int32_t i) { + return i - IX_BASIC_EMOJI_TRIE_OFFSET; + } + + void load(UErrorCode &errorCode); + UBool hasBinaryPropertyImpl(UChar32 c, UProperty which) const; + UBool hasBinaryPropertyImpl(const char16_t *s, int32_t length, UProperty which) const; + + UDataMemory *memory = nullptr; + UCPTrie *cpTrie = nullptr; + const char16_t *stringTries[6] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; +}; + +U_NAMESPACE_END + +#endif // __EMOJIPROPS_H__ diff --git a/deps/icu-small/source/common/errorcode.cpp b/deps/icu-small/source/common/errorcode.cpp index e7ac43b52739ae..a687b386293350 100644 --- a/deps/icu-small/source/common/errorcode.cpp +++ b/deps/icu-small/source/common/errorcode.cpp @@ -1,42 +1,42 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2009-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: errorcode.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2009mar10 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" -#include "unicode/errorcode.h" - -U_NAMESPACE_BEGIN - -ErrorCode::~ErrorCode() {} - -UErrorCode ErrorCode::reset() { - UErrorCode code = errorCode; - errorCode = U_ZERO_ERROR; - return code; -} - -void ErrorCode::assertSuccess() const { - if(isFailure()) { - handleFailure(); - } -} - -const char* ErrorCode::errorName() const { - return u_errorName(errorCode); -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2009-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: errorcode.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2009mar10 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" +#include "unicode/errorcode.h" + +U_NAMESPACE_BEGIN + +ErrorCode::~ErrorCode() {} + +UErrorCode ErrorCode::reset() { + UErrorCode code = errorCode; + errorCode = U_ZERO_ERROR; + return code; +} + +void ErrorCode::assertSuccess() const { + if(isFailure()) { + handleFailure(); + } +} + +const char* ErrorCode::errorName() const { + return u_errorName(errorCode); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/filteredbrk.cpp b/deps/icu-small/source/common/filteredbrk.cpp index baa1d4e42d2e2a..934a8480241b99 100644 --- a/deps/icu-small/source/common/filteredbrk.cpp +++ b/deps/icu-small/source/common/filteredbrk.cpp @@ -1,736 +1,736 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2014-2015, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#include "unicode/utypes.h" -#if !UCONFIG_NO_BREAK_ITERATION && !UCONFIG_NO_FILTERED_BREAK_ITERATION - -#include "cmemory.h" - -#include "unicode/filteredbrk.h" -#include "unicode/ucharstriebuilder.h" -#include "unicode/ures.h" - -#include "uresimp.h" // ures_getByKeyWithFallback -#include "ubrkimpl.h" // U_ICUDATA_BRKITR -#include "uvector.h" -#include "cmemory.h" -#include "umutex.h" - -U_NAMESPACE_BEGIN - -#ifndef FB_DEBUG -#define FB_DEBUG 0 -#endif - -#if FB_DEBUG -#include -static void _fb_trace(const char *m, const UnicodeString *s, UBool b, int32_t d, const char *f, int l) { - char buf[2048]; - if(s) { - s->extract(0,s->length(),buf,2048); - } else { - strcpy(buf,"NULL"); - } - fprintf(stderr,"%s:%d: %s. s='%s'(%p), b=%c, d=%d\n", - f, l, m, buf, (const void*)s, b?'T':'F',(int)d); -} - -#define FB_TRACE(m,s,b,d) _fb_trace(m,s,b,d,__FILE__,__LINE__) -#else -#define FB_TRACE(m,s,b,d) -#endif - -/** - * Used with sortedInsert() - */ -static int32_t U_CALLCONV compareUnicodeString(UElement t1, UElement t2) { - const UnicodeString &a = *(const UnicodeString*)t1.pointer; - const UnicodeString &b = *(const UnicodeString*)t2.pointer; - return a.compare(b); -} - -/** - * A UVector which implements a set of strings. - */ -class UStringSet : public UVector { - public: - UStringSet(UErrorCode &status) : UVector(uprv_deleteUObject, - uhash_compareUnicodeString, - 1, - status) {} - virtual ~UStringSet(); - /** - * Is this UnicodeSet contained? - */ - inline UBool contains(const UnicodeString& s) { - return contains((void*) &s); - } - using UVector::contains; - /** - * Return the ith UnicodeString alias - */ - inline const UnicodeString* getStringAt(int32_t i) const { - return (const UnicodeString*)elementAt(i); - } - /** - * Adopt the UnicodeString if not already contained. - * Caller no longer owns the pointer in any case. - * @return true if adopted successfully, false otherwise (error, or else duplicate) - */ - inline UBool adopt(UnicodeString *str, UErrorCode &status) { - if(U_FAILURE(status) || contains(*str)) { - delete str; - return false; - } else { - sortedInsert(str, compareUnicodeString, status); - if(U_FAILURE(status)) { - return false; - } - return true; - } - } - /** - * Add by value. - * @return true if successfully adopted. - */ - inline UBool add(const UnicodeString& str, UErrorCode &status) { - if(U_FAILURE(status)) return false; - UnicodeString *t = new UnicodeString(str); - if(t==NULL) { - status = U_MEMORY_ALLOCATION_ERROR; return false; - } - return adopt(t, status); - } - /** - * Remove this string. - * @return true if successfully removed, false otherwise (error, or else it wasn't there) - */ - inline UBool remove(const UnicodeString &s, UErrorCode &status) { - if(U_FAILURE(status)) return false; - return removeElement((void*) &s); - } -}; - -/** - * Virtual, won't be inlined - */ -UStringSet::~UStringSet() {} - -/* ----------------------------------------------------------- */ - - -/* Filtered Break constants */ -static const int32_t kPARTIAL = (1<<0); //< partial - need to run through forward trie -static const int32_t kMATCH = (1<<1); //< exact match - skip this one. -static const int32_t kSuppressInReverse = (1<<0); -static const int32_t kAddToForward = (1<<1); -static const UChar kFULLSTOP = 0x002E; // '.' - -/** - * Shared data for SimpleFilteredSentenceBreakIterator - */ -class SimpleFilteredSentenceBreakData : public UMemory { -public: - SimpleFilteredSentenceBreakData(UCharsTrie *forwards, UCharsTrie *backwards ) - : fForwardsPartialTrie(forwards), fBackwardsTrie(backwards), refcount(1) { } - SimpleFilteredSentenceBreakData *incr() { - umtx_atomic_inc(&refcount); - return this; - } - SimpleFilteredSentenceBreakData *decr() { - if(umtx_atomic_dec(&refcount) <= 0) { - delete this; - } - return 0; - } - virtual ~SimpleFilteredSentenceBreakData(); - - bool hasForwardsPartialTrie() const { return fForwardsPartialTrie.isValid(); } - bool hasBackwardsTrie() const { return fBackwardsTrie.isValid(); } - - const UCharsTrie &getForwardsPartialTrie() const { return *fForwardsPartialTrie; } - const UCharsTrie &getBackwardsTrie() const { return *fBackwardsTrie; } - -private: - // These tries own their data arrays. - // They are shared and must therefore not be modified. - LocalPointer fForwardsPartialTrie; // Has ".a" for "a.M." - LocalPointer fBackwardsTrie; // i.e. ".srM" for Mrs. - u_atomic_int32_t refcount; -}; - -SimpleFilteredSentenceBreakData::~SimpleFilteredSentenceBreakData() {} - -/** - * Concrete implementation - */ -class SimpleFilteredSentenceBreakIterator : public BreakIterator { -public: - SimpleFilteredSentenceBreakIterator(BreakIterator *adopt, UCharsTrie *forwards, UCharsTrie *backwards, UErrorCode &status); - SimpleFilteredSentenceBreakIterator(const SimpleFilteredSentenceBreakIterator& other); - virtual ~SimpleFilteredSentenceBreakIterator(); -private: - SimpleFilteredSentenceBreakData *fData; - LocalPointer fDelegate; - LocalUTextPointer fText; - - /* -- subclass interface -- */ -public: - /* -- cloning and other subclass stuff -- */ - virtual BreakIterator * createBufferClone(void * /*stackBuffer*/, - int32_t &/*BufferSize*/, - UErrorCode &status) override { - // for now - always deep clone - status = U_SAFECLONE_ALLOCATED_WARNING; - return clone(); - } - virtual SimpleFilteredSentenceBreakIterator* clone() const override { return new SimpleFilteredSentenceBreakIterator(*this); } - virtual UClassID getDynamicClassID(void) const override { return NULL; } - virtual bool operator==(const BreakIterator& o) const override { if(this==&o) return true; return false; } - - /* -- text modifying -- */ - virtual void setText(UText *text, UErrorCode &status) override { fDelegate->setText(text,status); } - virtual BreakIterator &refreshInputText(UText *input, UErrorCode &status) override { fDelegate->refreshInputText(input,status); return *this; } - virtual void adoptText(CharacterIterator* it) override { fDelegate->adoptText(it); } - virtual void setText(const UnicodeString &text) override { fDelegate->setText(text); } - - /* -- other functions that are just delegated -- */ - virtual UText *getUText(UText *fillIn, UErrorCode &status) const override { return fDelegate->getUText(fillIn,status); } - virtual CharacterIterator& getText(void) const override { return fDelegate->getText(); } - - /* -- ITERATION -- */ - virtual int32_t first(void) override; - virtual int32_t preceding(int32_t offset) override; - virtual int32_t previous(void) override; - virtual UBool isBoundary(int32_t offset) override; - virtual int32_t current(void) const override { return fDelegate->current(); } // we keep the delegate current, so this should be correct. - - virtual int32_t next(void) override; - - virtual int32_t next(int32_t n) override; - virtual int32_t following(int32_t offset) override; - virtual int32_t last(void) override; - -private: - /** - * Given that the fDelegate has already given its "initial" answer, - * find the NEXT actual (non-excepted) break. - * @param n initial position from delegate - * @return new break position or UBRK_DONE - */ - int32_t internalNext(int32_t n); - /** - * Given that the fDelegate has already given its "initial" answer, - * find the PREV actual (non-excepted) break. - * @param n initial position from delegate - * @return new break position or UBRK_DONE - */ - int32_t internalPrev(int32_t n); - /** - * set up the UText with the value of the fDelegate. - * Call this before calling breakExceptionAt. - * May be able to avoid excess calls - */ - void resetState(UErrorCode &status); - /** - * Is there a match (exception) at this spot? - */ - enum EFBMatchResult { kNoExceptionHere, kExceptionHere }; - /** - * Determine if there is an exception at this spot - * @param n spot to check - * @return kNoExceptionHere or kExceptionHere - **/ - enum EFBMatchResult breakExceptionAt(int32_t n); -}; - -SimpleFilteredSentenceBreakIterator::SimpleFilteredSentenceBreakIterator(const SimpleFilteredSentenceBreakIterator& other) - : BreakIterator(other), fData(other.fData->incr()), fDelegate(other.fDelegate->clone()) -{ -} - - -SimpleFilteredSentenceBreakIterator::SimpleFilteredSentenceBreakIterator(BreakIterator *adopt, UCharsTrie *forwards, UCharsTrie *backwards, UErrorCode &status) : - BreakIterator(adopt->getLocale(ULOC_VALID_LOCALE,status),adopt->getLocale(ULOC_ACTUAL_LOCALE,status)), - fData(new SimpleFilteredSentenceBreakData(forwards, backwards)), - fDelegate(adopt) -{ - if (fData == nullptr) { - delete forwards; - delete backwards; - if (U_SUCCESS(status)) { - status = U_MEMORY_ALLOCATION_ERROR; - } - } -} - -SimpleFilteredSentenceBreakIterator::~SimpleFilteredSentenceBreakIterator() { - fData = fData->decr(); -} - -void SimpleFilteredSentenceBreakIterator::resetState(UErrorCode &status) { - fText.adoptInstead(fDelegate->getUText(fText.orphan(), status)); -} - -SimpleFilteredSentenceBreakIterator::EFBMatchResult -SimpleFilteredSentenceBreakIterator::breakExceptionAt(int32_t n) { - int64_t bestPosn = -1; - int32_t bestValue = -1; - // loops while 'n' points to an exception. - utext_setNativeIndex(fText.getAlias(), n); // from n.. - - //if(debug2) u_printf(" n@ %d\n", n); - // Assume a space is following the '.' (so we handle the case: "Mr. /Brown") - if(utext_previous32(fText.getAlias())==u' ') { // TODO: skip a class of chars here?? - // TODO only do this the 1st time? - //if(debug2) u_printf("skipping prev: |%C| \n", (UChar)uch); - } else { - //if(debug2) u_printf("not skipping prev: |%C| \n", (UChar)uch); - utext_next32(fText.getAlias()); - //if(debug2) u_printf(" -> : |%C| \n", (UChar)uch); - } - - { - // Do not modify the shared trie! - UCharsTrie iter(fData->getBackwardsTrie()); - UChar32 uch; - while((uch=utext_previous32(fText.getAlias()))!=U_SENTINEL) { // more to consume backwards - UStringTrieResult r = iter.nextForCodePoint(uch); - if(USTRINGTRIE_HAS_VALUE(r)) { // remember the best match so far - bestPosn = utext_getNativeIndex(fText.getAlias()); - bestValue = iter.getValue(); - } - if(!USTRINGTRIE_HAS_NEXT(r)) { - break; - } - //if(debug2) u_printf("rev< /%C/ cont?%d @%d\n", (UChar)uch, r, utext_getNativeIndex(fText.getAlias())); - } - } - - //if(bestValue >= 0) { - //if(debug2) u_printf("rev<+/%C/+end of seq.. r=%d, bestPosn=%d, bestValue=%d\n", (UChar)uch, r, bestPosn, bestValue); - //} - - if(bestPosn>=0) { - //if(debug2) u_printf("rev< /%C/ end of seq.. r=%d, bestPosn=%d, bestValue=%d\n", (UChar)uch, r, bestPosn, bestValue); - - //if(USTRINGTRIE_MATCHES(r)) { // matched - so, now what? - //int32_t bestValue = iter.getValue(); - ////if(debug2) u_printf("rev< /%C/ matched, skip..%d bestValue=%d\n", (UChar)uch, r, bestValue); - - if(bestValue == kMATCH) { // exact match! - //if(debug2) u_printf(" exact backward match\n"); - return kExceptionHere; // See if the next is another exception. - } else if(bestValue == kPARTIAL - && fData->hasForwardsPartialTrie()) { // make sure there's a forward trie - //if(debug2) u_printf(" partial backward match\n"); - // We matched the "Ph." in "Ph.D." - now we need to run everything through the forwards trie - // to see if it matches something going forward. - UStringTrieResult rfwd = USTRINGTRIE_INTERMEDIATE_VALUE; - utext_setNativeIndex(fText.getAlias(), bestPosn); // hope that's close .. - //if(debug2) u_printf("Retrying at %d\n", bestPosn); - // Do not modify the shared trie! - UCharsTrie iter(fData->getForwardsPartialTrie()); - UChar32 uch; - while((uch=utext_next32(fText.getAlias()))!=U_SENTINEL && - USTRINGTRIE_HAS_NEXT(rfwd=iter.nextForCodePoint(uch))) { - //if(debug2) u_printf("fwd> /%C/ cont?%d @%d\n", (UChar)uch, rfwd, utext_getNativeIndex(fText.getAlias())); - } - if(USTRINGTRIE_MATCHES(rfwd)) { - //if(debug2) u_printf("fwd> /%C/ == forward match!\n", (UChar)uch); - // only full matches here, nothing to check - // skip the next: - return kExceptionHere; - } else { - //if(debug2) u_printf("fwd> /%C/ no match.\n", (UChar)uch); - // no match (no exception) -return the 'underlying' break - return kNoExceptionHere; - } - } else { - return kNoExceptionHere; // internal error and/or no forwards trie - } - } else { - //if(debug2) u_printf("rev< /%C/ .. no match..%d\n", (UChar)uch, r); // no best match - return kNoExceptionHere; // No match - so exit. Not an exception. - } -} - -// the workhorse single next. -int32_t -SimpleFilteredSentenceBreakIterator::internalNext(int32_t n) { - if(n == UBRK_DONE || // at end or - !fData->hasBackwardsTrie()) { // .. no backwards table loaded == no exceptions - return n; - } - // OK, do we need to break here? - UErrorCode status = U_ZERO_ERROR; - // refresh text - resetState(status); - if(U_FAILURE(status)) return UBRK_DONE; // bail out - int64_t utextLen = utext_nativeLength(fText.getAlias()); - - //if(debug2) u_printf("str, native len=%d\n", utext_nativeLength(fText.getAlias())); - while (n != UBRK_DONE && n != utextLen) { // outer loop runs once per underlying break (from fDelegate). - SimpleFilteredSentenceBreakIterator::EFBMatchResult m = breakExceptionAt(n); - - switch(m) { - case kExceptionHere: - n = fDelegate->next(); // skip this one. Find the next lowerlevel break. - continue; - - default: - case kNoExceptionHere: - return n; - } - } - return n; -} - -int32_t -SimpleFilteredSentenceBreakIterator::internalPrev(int32_t n) { - if(n == 0 || n == UBRK_DONE || // at end or - !fData->hasBackwardsTrie()) { // .. no backwards table loaded == no exceptions - return n; - } - // OK, do we need to break here? - UErrorCode status = U_ZERO_ERROR; - // refresh text - resetState(status); - if(U_FAILURE(status)) return UBRK_DONE; // bail out - - //if(debug2) u_printf("str, native len=%d\n", utext_nativeLength(fText.getAlias())); - while (n != UBRK_DONE && n != 0) { // outer loop runs once per underlying break (from fDelegate). - SimpleFilteredSentenceBreakIterator::EFBMatchResult m = breakExceptionAt(n); - - switch(m) { - case kExceptionHere: - n = fDelegate->previous(); // skip this one. Find the next lowerlevel break. - continue; - - default: - case kNoExceptionHere: - return n; - } - } - return n; -} - - -int32_t -SimpleFilteredSentenceBreakIterator::next() { - return internalNext(fDelegate->next()); -} - -int32_t -SimpleFilteredSentenceBreakIterator::first(void) { - // Don't suppress a break opportunity at the beginning of text. - return fDelegate->first(); -} - -int32_t -SimpleFilteredSentenceBreakIterator::preceding(int32_t offset) { - return internalPrev(fDelegate->preceding(offset)); -} - -int32_t -SimpleFilteredSentenceBreakIterator::previous(void) { - return internalPrev(fDelegate->previous()); -} - -UBool SimpleFilteredSentenceBreakIterator::isBoundary(int32_t offset) { - if (!fDelegate->isBoundary(offset)) return false; // no break to suppress - - if (!fData->hasBackwardsTrie()) return true; // no data = no suppressions - - UErrorCode status = U_ZERO_ERROR; - resetState(status); - - SimpleFilteredSentenceBreakIterator::EFBMatchResult m = breakExceptionAt(offset); - - switch(m) { - case kExceptionHere: - return false; - default: - case kNoExceptionHere: - return true; - } -} - -int32_t -SimpleFilteredSentenceBreakIterator::next(int32_t offset) { - return internalNext(fDelegate->next(offset)); -} - -int32_t -SimpleFilteredSentenceBreakIterator::following(int32_t offset) { - return internalNext(fDelegate->following(offset)); -} - -int32_t -SimpleFilteredSentenceBreakIterator::last(void) { - // Don't suppress a break opportunity at the end of text. - return fDelegate->last(); -} - - -/** - * Concrete implementation of builder class. - */ -class SimpleFilteredBreakIteratorBuilder : public FilteredBreakIteratorBuilder { -public: - virtual ~SimpleFilteredBreakIteratorBuilder(); - SimpleFilteredBreakIteratorBuilder(const Locale &fromLocale, UErrorCode &status); - SimpleFilteredBreakIteratorBuilder(UErrorCode &status); - virtual UBool suppressBreakAfter(const UnicodeString& exception, UErrorCode& status) override; - virtual UBool unsuppressBreakAfter(const UnicodeString& exception, UErrorCode& status) override; - virtual BreakIterator *build(BreakIterator* adoptBreakIterator, UErrorCode& status) override; -private: - UStringSet fSet; -}; - -SimpleFilteredBreakIteratorBuilder::~SimpleFilteredBreakIteratorBuilder() -{ -} - -SimpleFilteredBreakIteratorBuilder::SimpleFilteredBreakIteratorBuilder(UErrorCode &status) - : fSet(status) -{ -} - -SimpleFilteredBreakIteratorBuilder::SimpleFilteredBreakIteratorBuilder(const Locale &fromLocale, UErrorCode &status) - : fSet(status) -{ - if(U_SUCCESS(status)) { - UErrorCode subStatus = U_ZERO_ERROR; - LocalUResourceBundlePointer b(ures_open(U_ICUDATA_BRKITR, fromLocale.getBaseName(), &subStatus)); - if (U_FAILURE(subStatus) || (subStatus == U_USING_DEFAULT_WARNING) ) { - status = subStatus; // copy the failing status -#if FB_DEBUG - fprintf(stderr, "open BUNDLE %s : %s, %s\n", fromLocale.getBaseName(), "[exit]", u_errorName(status)); -#endif - return; // leaves the builder empty, if you try to use it. - } - LocalUResourceBundlePointer exceptions(ures_getByKeyWithFallback(b.getAlias(), "exceptions", NULL, &subStatus)); - if (U_FAILURE(subStatus) || (subStatus == U_USING_DEFAULT_WARNING) ) { - status = subStatus; // copy the failing status -#if FB_DEBUG - fprintf(stderr, "open EXCEPTIONS %s : %s, %s\n", fromLocale.getBaseName(), "[exit]", u_errorName(status)); -#endif - return; // leaves the builder empty, if you try to use it. - } - LocalUResourceBundlePointer breaks(ures_getByKeyWithFallback(exceptions.getAlias(), "SentenceBreak", NULL, &subStatus)); - -#if FB_DEBUG - { - UErrorCode subsub = subStatus; - fprintf(stderr, "open SentenceBreak %s => %s, %s\n", fromLocale.getBaseName(), ures_getLocale(breaks.getAlias(), &subsub), u_errorName(subStatus)); - } -#endif - - if (U_FAILURE(subStatus) || (subStatus == U_USING_DEFAULT_WARNING) ) { - status = subStatus; // copy the failing status -#if FB_DEBUG - fprintf(stderr, "open %s : %s, %s\n", fromLocale.getBaseName(), "[exit]", u_errorName(status)); -#endif - return; // leaves the builder empty, if you try to use it. - } - - LocalUResourceBundlePointer strs; - subStatus = status; // Pick up inherited warning status now - do { - strs.adoptInstead(ures_getNextResource(breaks.getAlias(), strs.orphan(), &subStatus)); - if(strs.isValid() && U_SUCCESS(subStatus)) { - UnicodeString str(ures_getUnicodeString(strs.getAlias(), &status)); - suppressBreakAfter(str, status); // load the string - } - } while (strs.isValid() && U_SUCCESS(subStatus)); - if(U_FAILURE(subStatus)&&subStatus!=U_INDEX_OUTOFBOUNDS_ERROR&&U_SUCCESS(status)) { - status = subStatus; - } - } -} - -UBool -SimpleFilteredBreakIteratorBuilder::suppressBreakAfter(const UnicodeString& exception, UErrorCode& status) -{ - UBool r = fSet.add(exception, status); - FB_TRACE("suppressBreakAfter",&exception,r,0); - return r; -} - -UBool -SimpleFilteredBreakIteratorBuilder::unsuppressBreakAfter(const UnicodeString& exception, UErrorCode& status) -{ - UBool r = fSet.remove(exception, status); - FB_TRACE("unsuppressBreakAfter",&exception,r,0); - return r; -} - -/** - * Jitterbug 2974: MSVC has a bug whereby new X[0] behaves badly. - * Work around this. - * - * Note: "new UnicodeString[subCount]" ends up calling global operator new - * on MSVC2012 for some reason. - */ -static inline UnicodeString* newUnicodeStringArray(size_t count) { - return new UnicodeString[count ? count : 1]; -} - -BreakIterator * -SimpleFilteredBreakIteratorBuilder::build(BreakIterator* adoptBreakIterator, UErrorCode& status) { - LocalPointer adopt(adoptBreakIterator); - - LocalPointer builder(new UCharsTrieBuilder(status), status); - LocalPointer builder2(new UCharsTrieBuilder(status), status); - if(U_FAILURE(status)) { - return NULL; - } - - int32_t revCount = 0; - int32_t fwdCount = 0; - - int32_t subCount = fSet.size(); - - UnicodeString *ustrs_ptr = newUnicodeStringArray(subCount); - - LocalArray ustrs(ustrs_ptr); - - LocalMemory partials; - partials.allocateInsteadAndReset(subCount); - - LocalPointer backwardsTrie; // i.e. ".srM" for Mrs. - LocalPointer forwardsPartialTrie; // Has ".a" for "a.M." - - int n=0; - for ( int32_t i = 0; - i-1 && (nn+1)!=ustrs[i].length()) { - FB_TRACE("partial",&ustrs[i],false,i); - // is partial. - // is it unique? - int sameAs = -1; - for(int j=0;jadd(prefix, kPARTIAL, status); - revCount++; - FB_TRACE("Added partial",&prefix,false, i); - FB_TRACE(u_errorName(status),&ustrs[i],false,i); - partials[i] = kSuppressInReverse | kAddToForward; - } else { - FB_TRACE("NOT adding partial",&prefix,false, i); - FB_TRACE(u_errorName(status),&ustrs[i],false,i); - } - } - } - for(int i=0;iadd(ustrs[i], kMATCH, status); - revCount++; - FB_TRACE(u_errorName(status), &ustrs[i], false, i); - } else { - FB_TRACE("Adding fwd",&ustrs[i], false, i); - - // an optimization would be to only add the portion after the '.' - // for example, for "Ph.D." we store ".hP" in the reverse table. We could just store "D." in the forward, - // instead of "Ph.D." since we already know the "Ph." part is a match. - // would need the trie to be able to hold 0-length strings, though. - builder2->add(ustrs[i], kMATCH, status); // forward - fwdCount++; - //ustrs[i].reverse(); - ////if(debug2) u_printf("SUPPRESS- not Added(%d): /%S/ status=%s\n",partials[i], ustrs[i].getTerminatedBuffer(), u_errorName(status)); - } - } - FB_TRACE("AbbrCount",NULL,false, subCount); - - if(revCount>0) { - backwardsTrie.adoptInstead(builder->build(USTRINGTRIE_BUILD_FAST, status)); - if(U_FAILURE(status)) { - FB_TRACE(u_errorName(status),NULL,false, -1); - return NULL; - } - } - - if(fwdCount>0) { - forwardsPartialTrie.adoptInstead(builder2->build(USTRINGTRIE_BUILD_FAST, status)); - if(U_FAILURE(status)) { - FB_TRACE(u_errorName(status),NULL,false, -1); - return NULL; - } - } - - return new SimpleFilteredSentenceBreakIterator(adopt.orphan(), forwardsPartialTrie.orphan(), backwardsTrie.orphan(), status); -} - - -// ----------- Base class implementation - -FilteredBreakIteratorBuilder::FilteredBreakIteratorBuilder() { -} - -FilteredBreakIteratorBuilder::~FilteredBreakIteratorBuilder() { -} - -FilteredBreakIteratorBuilder * -FilteredBreakIteratorBuilder::createInstance(const Locale& where, UErrorCode& status) { - if(U_FAILURE(status)) return NULL; - LocalPointer ret(new SimpleFilteredBreakIteratorBuilder(where, status), status); - return (U_SUCCESS(status))? ret.orphan(): NULL; -} - -FilteredBreakIteratorBuilder * -FilteredBreakIteratorBuilder::createInstance(UErrorCode &status) { - return createEmptyInstance(status); -} - -FilteredBreakIteratorBuilder * -FilteredBreakIteratorBuilder::createEmptyInstance(UErrorCode& status) { - if(U_FAILURE(status)) return NULL; - LocalPointer ret(new SimpleFilteredBreakIteratorBuilder(status), status); - return (U_SUCCESS(status))? ret.orphan(): NULL; -} - -U_NAMESPACE_END - -#endif //#if !UCONFIG_NO_BREAK_ITERATION && !UCONFIG_NO_FILTERED_BREAK_ITERATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2014-2015, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +*/ + +#include "unicode/utypes.h" +#if !UCONFIG_NO_BREAK_ITERATION && !UCONFIG_NO_FILTERED_BREAK_ITERATION + +#include "cmemory.h" + +#include "unicode/filteredbrk.h" +#include "unicode/ucharstriebuilder.h" +#include "unicode/ures.h" + +#include "uresimp.h" // ures_getByKeyWithFallback +#include "ubrkimpl.h" // U_ICUDATA_BRKITR +#include "uvector.h" +#include "cmemory.h" +#include "umutex.h" + +U_NAMESPACE_BEGIN + +#ifndef FB_DEBUG +#define FB_DEBUG 0 +#endif + +#if FB_DEBUG +#include +static void _fb_trace(const char *m, const UnicodeString *s, UBool b, int32_t d, const char *f, int l) { + char buf[2048]; + if(s) { + s->extract(0,s->length(),buf,2048); + } else { + strcpy(buf,"nullptr"); + } + fprintf(stderr,"%s:%d: %s. s='%s'(%p), b=%c, d=%d\n", + f, l, m, buf, (const void*)s, b?'T':'F',(int)d); +} + +#define FB_TRACE(m,s,b,d) _fb_trace(m,s,b,d,__FILE__,__LINE__) +#else +#define FB_TRACE(m,s,b,d) +#endif + +/** + * Used with sortedInsert() + */ +static int32_t U_CALLCONV compareUnicodeString(UElement t1, UElement t2) { + const UnicodeString &a = *(const UnicodeString*)t1.pointer; + const UnicodeString &b = *(const UnicodeString*)t2.pointer; + return a.compare(b); +} + +/** + * A UVector which implements a set of strings. + */ +class UStringSet : public UVector { + public: + UStringSet(UErrorCode &status) : UVector(uprv_deleteUObject, + uhash_compareUnicodeString, + 1, + status) {} + virtual ~UStringSet(); + /** + * Is this UnicodeSet contained? + */ + inline UBool contains(const UnicodeString& s) { + return contains((void*) &s); + } + using UVector::contains; + /** + * Return the ith UnicodeString alias + */ + inline const UnicodeString* getStringAt(int32_t i) const { + return (const UnicodeString*)elementAt(i); + } + /** + * Adopt the UnicodeString if not already contained. + * Caller no longer owns the pointer in any case. + * @return true if adopted successfully, false otherwise (error, or else duplicate) + */ + inline UBool adopt(UnicodeString *str, UErrorCode &status) { + if(U_FAILURE(status) || contains(*str)) { + delete str; + return false; + } else { + sortedInsert(str, compareUnicodeString, status); + if(U_FAILURE(status)) { + return false; + } + return true; + } + } + /** + * Add by value. + * @return true if successfully adopted. + */ + inline UBool add(const UnicodeString& str, UErrorCode &status) { + if(U_FAILURE(status)) return false; + UnicodeString *t = new UnicodeString(str); + if(t==nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; return false; + } + return adopt(t, status); + } + /** + * Remove this string. + * @return true if successfully removed, false otherwise (error, or else it wasn't there) + */ + inline UBool remove(const UnicodeString &s, UErrorCode &status) { + if(U_FAILURE(status)) return false; + return removeElement((void*) &s); + } +}; + +/** + * Virtual, won't be inlined + */ +UStringSet::~UStringSet() {} + +/* ----------------------------------------------------------- */ + + +/* Filtered Break constants */ +static const int32_t kPARTIAL = (1<<0); //< partial - need to run through forward trie +static const int32_t kMATCH = (1<<1); //< exact match - skip this one. +static const int32_t kSuppressInReverse = (1<<0); +static const int32_t kAddToForward = (1<<1); +static const char16_t kFULLSTOP = 0x002E; // '.' + +/** + * Shared data for SimpleFilteredSentenceBreakIterator + */ +class SimpleFilteredSentenceBreakData : public UMemory { +public: + SimpleFilteredSentenceBreakData(UCharsTrie *forwards, UCharsTrie *backwards ) + : fForwardsPartialTrie(forwards), fBackwardsTrie(backwards), refcount(1) { } + SimpleFilteredSentenceBreakData *incr() { + umtx_atomic_inc(&refcount); + return this; + } + SimpleFilteredSentenceBreakData *decr() { + if(umtx_atomic_dec(&refcount) <= 0) { + delete this; + } + return 0; + } + virtual ~SimpleFilteredSentenceBreakData(); + + bool hasForwardsPartialTrie() const { return fForwardsPartialTrie.isValid(); } + bool hasBackwardsTrie() const { return fBackwardsTrie.isValid(); } + + const UCharsTrie &getForwardsPartialTrie() const { return *fForwardsPartialTrie; } + const UCharsTrie &getBackwardsTrie() const { return *fBackwardsTrie; } + +private: + // These tries own their data arrays. + // They are shared and must therefore not be modified. + LocalPointer fForwardsPartialTrie; // Has ".a" for "a.M." + LocalPointer fBackwardsTrie; // i.e. ".srM" for Mrs. + u_atomic_int32_t refcount; +}; + +SimpleFilteredSentenceBreakData::~SimpleFilteredSentenceBreakData() {} + +/** + * Concrete implementation + */ +class SimpleFilteredSentenceBreakIterator : public BreakIterator { +public: + SimpleFilteredSentenceBreakIterator(BreakIterator *adopt, UCharsTrie *forwards, UCharsTrie *backwards, UErrorCode &status); + SimpleFilteredSentenceBreakIterator(const SimpleFilteredSentenceBreakIterator& other); + virtual ~SimpleFilteredSentenceBreakIterator(); +private: + SimpleFilteredSentenceBreakData *fData; + LocalPointer fDelegate; + LocalUTextPointer fText; + + /* -- subclass interface -- */ +public: + /* -- cloning and other subclass stuff -- */ + virtual BreakIterator * createBufferClone(void * /*stackBuffer*/, + int32_t &/*BufferSize*/, + UErrorCode &status) override { + // for now - always deep clone + status = U_SAFECLONE_ALLOCATED_WARNING; + return clone(); + } + virtual SimpleFilteredSentenceBreakIterator* clone() const override { return new SimpleFilteredSentenceBreakIterator(*this); } + virtual UClassID getDynamicClassID() const override { return nullptr; } + virtual bool operator==(const BreakIterator& o) const override { if(this==&o) return true; return false; } + + /* -- text modifying -- */ + virtual void setText(UText *text, UErrorCode &status) override { fDelegate->setText(text,status); } + virtual BreakIterator &refreshInputText(UText *input, UErrorCode &status) override { fDelegate->refreshInputText(input,status); return *this; } + virtual void adoptText(CharacterIterator* it) override { fDelegate->adoptText(it); } + virtual void setText(const UnicodeString &text) override { fDelegate->setText(text); } + + /* -- other functions that are just delegated -- */ + virtual UText *getUText(UText *fillIn, UErrorCode &status) const override { return fDelegate->getUText(fillIn,status); } + virtual CharacterIterator& getText() const override { return fDelegate->getText(); } + + /* -- ITERATION -- */ + virtual int32_t first() override; + virtual int32_t preceding(int32_t offset) override; + virtual int32_t previous() override; + virtual UBool isBoundary(int32_t offset) override; + virtual int32_t current() const override { return fDelegate->current(); } // we keep the delegate current, so this should be correct. + + virtual int32_t next() override; + + virtual int32_t next(int32_t n) override; + virtual int32_t following(int32_t offset) override; + virtual int32_t last() override; + +private: + /** + * Given that the fDelegate has already given its "initial" answer, + * find the NEXT actual (non-excepted) break. + * @param n initial position from delegate + * @return new break position or UBRK_DONE + */ + int32_t internalNext(int32_t n); + /** + * Given that the fDelegate has already given its "initial" answer, + * find the PREV actual (non-excepted) break. + * @param n initial position from delegate + * @return new break position or UBRK_DONE + */ + int32_t internalPrev(int32_t n); + /** + * set up the UText with the value of the fDelegate. + * Call this before calling breakExceptionAt. + * May be able to avoid excess calls + */ + void resetState(UErrorCode &status); + /** + * Is there a match (exception) at this spot? + */ + enum EFBMatchResult { kNoExceptionHere, kExceptionHere }; + /** + * Determine if there is an exception at this spot + * @param n spot to check + * @return kNoExceptionHere or kExceptionHere + **/ + enum EFBMatchResult breakExceptionAt(int32_t n); +}; + +SimpleFilteredSentenceBreakIterator::SimpleFilteredSentenceBreakIterator(const SimpleFilteredSentenceBreakIterator& other) + : BreakIterator(other), fData(other.fData->incr()), fDelegate(other.fDelegate->clone()) +{ +} + + +SimpleFilteredSentenceBreakIterator::SimpleFilteredSentenceBreakIterator(BreakIterator *adopt, UCharsTrie *forwards, UCharsTrie *backwards, UErrorCode &status) : + BreakIterator(adopt->getLocale(ULOC_VALID_LOCALE,status),adopt->getLocale(ULOC_ACTUAL_LOCALE,status)), + fData(new SimpleFilteredSentenceBreakData(forwards, backwards)), + fDelegate(adopt) +{ + if (fData == nullptr) { + delete forwards; + delete backwards; + if (U_SUCCESS(status)) { + status = U_MEMORY_ALLOCATION_ERROR; + } + } +} + +SimpleFilteredSentenceBreakIterator::~SimpleFilteredSentenceBreakIterator() { + fData = fData->decr(); +} + +void SimpleFilteredSentenceBreakIterator::resetState(UErrorCode &status) { + fText.adoptInstead(fDelegate->getUText(fText.orphan(), status)); +} + +SimpleFilteredSentenceBreakIterator::EFBMatchResult +SimpleFilteredSentenceBreakIterator::breakExceptionAt(int32_t n) { + int64_t bestPosn = -1; + int32_t bestValue = -1; + // loops while 'n' points to an exception. + utext_setNativeIndex(fText.getAlias(), n); // from n.. + + //if(debug2) u_printf(" n@ %d\n", n); + // Assume a space is following the '.' (so we handle the case: "Mr. /Brown") + if(utext_previous32(fText.getAlias())==u' ') { // TODO: skip a class of chars here?? + // TODO only do this the 1st time? + //if(debug2) u_printf("skipping prev: |%C| \n", (char16_t)uch); + } else { + //if(debug2) u_printf("not skipping prev: |%C| \n", (char16_t)uch); + utext_next32(fText.getAlias()); + //if(debug2) u_printf(" -> : |%C| \n", (char16_t)uch); + } + + { + // Do not modify the shared trie! + UCharsTrie iter(fData->getBackwardsTrie()); + UChar32 uch; + while((uch=utext_previous32(fText.getAlias()))!=U_SENTINEL) { // more to consume backwards + UStringTrieResult r = iter.nextForCodePoint(uch); + if(USTRINGTRIE_HAS_VALUE(r)) { // remember the best match so far + bestPosn = utext_getNativeIndex(fText.getAlias()); + bestValue = iter.getValue(); + } + if(!USTRINGTRIE_HAS_NEXT(r)) { + break; + } + //if(debug2) u_printf("rev< /%C/ cont?%d @%d\n", (char16_t)uch, r, utext_getNativeIndex(fText.getAlias())); + } + } + + //if(bestValue >= 0) { + //if(debug2) u_printf("rev<+/%C/+end of seq.. r=%d, bestPosn=%d, bestValue=%d\n", (char16_t)uch, r, bestPosn, bestValue); + //} + + if(bestPosn>=0) { + //if(debug2) u_printf("rev< /%C/ end of seq.. r=%d, bestPosn=%d, bestValue=%d\n", (char16_t)uch, r, bestPosn, bestValue); + + //if(USTRINGTRIE_MATCHES(r)) { // matched - so, now what? + //int32_t bestValue = iter.getValue(); + ////if(debug2) u_printf("rev< /%C/ matched, skip..%d bestValue=%d\n", (char16_t)uch, r, bestValue); + + if(bestValue == kMATCH) { // exact match! + //if(debug2) u_printf(" exact backward match\n"); + return kExceptionHere; // See if the next is another exception. + } else if(bestValue == kPARTIAL + && fData->hasForwardsPartialTrie()) { // make sure there's a forward trie + //if(debug2) u_printf(" partial backward match\n"); + // We matched the "Ph." in "Ph.D." - now we need to run everything through the forwards trie + // to see if it matches something going forward. + UStringTrieResult rfwd = USTRINGTRIE_INTERMEDIATE_VALUE; + utext_setNativeIndex(fText.getAlias(), bestPosn); // hope that's close .. + //if(debug2) u_printf("Retrying at %d\n", bestPosn); + // Do not modify the shared trie! + UCharsTrie iter(fData->getForwardsPartialTrie()); + UChar32 uch; + while((uch=utext_next32(fText.getAlias()))!=U_SENTINEL && + USTRINGTRIE_HAS_NEXT(rfwd=iter.nextForCodePoint(uch))) { + //if(debug2) u_printf("fwd> /%C/ cont?%d @%d\n", (char16_t)uch, rfwd, utext_getNativeIndex(fText.getAlias())); + } + if(USTRINGTRIE_MATCHES(rfwd)) { + //if(debug2) u_printf("fwd> /%C/ == forward match!\n", (char16_t)uch); + // only full matches here, nothing to check + // skip the next: + return kExceptionHere; + } else { + //if(debug2) u_printf("fwd> /%C/ no match.\n", (char16_t)uch); + // no match (no exception) -return the 'underlying' break + return kNoExceptionHere; + } + } else { + return kNoExceptionHere; // internal error and/or no forwards trie + } + } else { + //if(debug2) u_printf("rev< /%C/ .. no match..%d\n", (char16_t)uch, r); // no best match + return kNoExceptionHere; // No match - so exit. Not an exception. + } +} + +// the workhorse single next. +int32_t +SimpleFilteredSentenceBreakIterator::internalNext(int32_t n) { + if(n == UBRK_DONE || // at end or + !fData->hasBackwardsTrie()) { // .. no backwards table loaded == no exceptions + return n; + } + // OK, do we need to break here? + UErrorCode status = U_ZERO_ERROR; + // refresh text + resetState(status); + if(U_FAILURE(status)) return UBRK_DONE; // bail out + int64_t utextLen = utext_nativeLength(fText.getAlias()); + + //if(debug2) u_printf("str, native len=%d\n", utext_nativeLength(fText.getAlias())); + while (n != UBRK_DONE && n != utextLen) { // outer loop runs once per underlying break (from fDelegate). + SimpleFilteredSentenceBreakIterator::EFBMatchResult m = breakExceptionAt(n); + + switch(m) { + case kExceptionHere: + n = fDelegate->next(); // skip this one. Find the next lowerlevel break. + continue; + + default: + case kNoExceptionHere: + return n; + } + } + return n; +} + +int32_t +SimpleFilteredSentenceBreakIterator::internalPrev(int32_t n) { + if(n == 0 || n == UBRK_DONE || // at end or + !fData->hasBackwardsTrie()) { // .. no backwards table loaded == no exceptions + return n; + } + // OK, do we need to break here? + UErrorCode status = U_ZERO_ERROR; + // refresh text + resetState(status); + if(U_FAILURE(status)) return UBRK_DONE; // bail out + + //if(debug2) u_printf("str, native len=%d\n", utext_nativeLength(fText.getAlias())); + while (n != UBRK_DONE && n != 0) { // outer loop runs once per underlying break (from fDelegate). + SimpleFilteredSentenceBreakIterator::EFBMatchResult m = breakExceptionAt(n); + + switch(m) { + case kExceptionHere: + n = fDelegate->previous(); // skip this one. Find the next lowerlevel break. + continue; + + default: + case kNoExceptionHere: + return n; + } + } + return n; +} + + +int32_t +SimpleFilteredSentenceBreakIterator::next() { + return internalNext(fDelegate->next()); +} + +int32_t +SimpleFilteredSentenceBreakIterator::first() { + // Don't suppress a break opportunity at the beginning of text. + return fDelegate->first(); +} + +int32_t +SimpleFilteredSentenceBreakIterator::preceding(int32_t offset) { + return internalPrev(fDelegate->preceding(offset)); +} + +int32_t +SimpleFilteredSentenceBreakIterator::previous() { + return internalPrev(fDelegate->previous()); +} + +UBool SimpleFilteredSentenceBreakIterator::isBoundary(int32_t offset) { + if (!fDelegate->isBoundary(offset)) return false; // no break to suppress + + if (!fData->hasBackwardsTrie()) return true; // no data = no suppressions + + UErrorCode status = U_ZERO_ERROR; + resetState(status); + + SimpleFilteredSentenceBreakIterator::EFBMatchResult m = breakExceptionAt(offset); + + switch(m) { + case kExceptionHere: + return false; + default: + case kNoExceptionHere: + return true; + } +} + +int32_t +SimpleFilteredSentenceBreakIterator::next(int32_t offset) { + return internalNext(fDelegate->next(offset)); +} + +int32_t +SimpleFilteredSentenceBreakIterator::following(int32_t offset) { + return internalNext(fDelegate->following(offset)); +} + +int32_t +SimpleFilteredSentenceBreakIterator::last() { + // Don't suppress a break opportunity at the end of text. + return fDelegate->last(); +} + + +/** + * Concrete implementation of builder class. + */ +class SimpleFilteredBreakIteratorBuilder : public FilteredBreakIteratorBuilder { +public: + virtual ~SimpleFilteredBreakIteratorBuilder(); + SimpleFilteredBreakIteratorBuilder(const Locale &fromLocale, UErrorCode &status); + SimpleFilteredBreakIteratorBuilder(UErrorCode &status); + virtual UBool suppressBreakAfter(const UnicodeString& exception, UErrorCode& status) override; + virtual UBool unsuppressBreakAfter(const UnicodeString& exception, UErrorCode& status) override; + virtual BreakIterator *build(BreakIterator* adoptBreakIterator, UErrorCode& status) override; +private: + UStringSet fSet; +}; + +SimpleFilteredBreakIteratorBuilder::~SimpleFilteredBreakIteratorBuilder() +{ +} + +SimpleFilteredBreakIteratorBuilder::SimpleFilteredBreakIteratorBuilder(UErrorCode &status) + : fSet(status) +{ +} + +SimpleFilteredBreakIteratorBuilder::SimpleFilteredBreakIteratorBuilder(const Locale &fromLocale, UErrorCode &status) + : fSet(status) +{ + if(U_SUCCESS(status)) { + UErrorCode subStatus = U_ZERO_ERROR; + LocalUResourceBundlePointer b(ures_open(U_ICUDATA_BRKITR, fromLocale.getBaseName(), &subStatus)); + if (U_FAILURE(subStatus) || (subStatus == U_USING_DEFAULT_WARNING) ) { + status = subStatus; // copy the failing status +#if FB_DEBUG + fprintf(stderr, "open BUNDLE %s : %s, %s\n", fromLocale.getBaseName(), "[exit]", u_errorName(status)); +#endif + return; // leaves the builder empty, if you try to use it. + } + LocalUResourceBundlePointer exceptions(ures_getByKeyWithFallback(b.getAlias(), "exceptions", nullptr, &subStatus)); + if (U_FAILURE(subStatus) || (subStatus == U_USING_DEFAULT_WARNING) ) { + status = subStatus; // copy the failing status +#if FB_DEBUG + fprintf(stderr, "open EXCEPTIONS %s : %s, %s\n", fromLocale.getBaseName(), "[exit]", u_errorName(status)); +#endif + return; // leaves the builder empty, if you try to use it. + } + LocalUResourceBundlePointer breaks(ures_getByKeyWithFallback(exceptions.getAlias(), "SentenceBreak", nullptr, &subStatus)); + +#if FB_DEBUG + { + UErrorCode subsub = subStatus; + fprintf(stderr, "open SentenceBreak %s => %s, %s\n", fromLocale.getBaseName(), ures_getLocale(breaks.getAlias(), &subsub), u_errorName(subStatus)); + } +#endif + + if (U_FAILURE(subStatus) || (subStatus == U_USING_DEFAULT_WARNING) ) { + status = subStatus; // copy the failing status +#if FB_DEBUG + fprintf(stderr, "open %s : %s, %s\n", fromLocale.getBaseName(), "[exit]", u_errorName(status)); +#endif + return; // leaves the builder empty, if you try to use it. + } + + LocalUResourceBundlePointer strs; + subStatus = status; // Pick up inherited warning status now + do { + strs.adoptInstead(ures_getNextResource(breaks.getAlias(), strs.orphan(), &subStatus)); + if(strs.isValid() && U_SUCCESS(subStatus)) { + UnicodeString str(ures_getUnicodeString(strs.getAlias(), &status)); + suppressBreakAfter(str, status); // load the string + } + } while (strs.isValid() && U_SUCCESS(subStatus)); + if(U_FAILURE(subStatus)&&subStatus!=U_INDEX_OUTOFBOUNDS_ERROR&&U_SUCCESS(status)) { + status = subStatus; + } + } +} + +UBool +SimpleFilteredBreakIteratorBuilder::suppressBreakAfter(const UnicodeString& exception, UErrorCode& status) +{ + UBool r = fSet.add(exception, status); + FB_TRACE("suppressBreakAfter",&exception,r,0); + return r; +} + +UBool +SimpleFilteredBreakIteratorBuilder::unsuppressBreakAfter(const UnicodeString& exception, UErrorCode& status) +{ + UBool r = fSet.remove(exception, status); + FB_TRACE("unsuppressBreakAfter",&exception,r,0); + return r; +} + +/** + * Jitterbug 2974: MSVC has a bug whereby new X[0] behaves badly. + * Work around this. + * + * Note: "new UnicodeString[subCount]" ends up calling global operator new + * on MSVC2012 for some reason. + */ +static inline UnicodeString* newUnicodeStringArray(size_t count) { + return new UnicodeString[count ? count : 1]; +} + +BreakIterator * +SimpleFilteredBreakIteratorBuilder::build(BreakIterator* adoptBreakIterator, UErrorCode& status) { + LocalPointer adopt(adoptBreakIterator); + + LocalPointer builder(new UCharsTrieBuilder(status), status); + LocalPointer builder2(new UCharsTrieBuilder(status), status); + if(U_FAILURE(status)) { + return nullptr; + } + + int32_t revCount = 0; + int32_t fwdCount = 0; + + int32_t subCount = fSet.size(); + + UnicodeString *ustrs_ptr = newUnicodeStringArray(subCount); + + LocalArray ustrs(ustrs_ptr); + + LocalMemory partials; + partials.allocateInsteadAndReset(subCount); + + LocalPointer backwardsTrie; // i.e. ".srM" for Mrs. + LocalPointer forwardsPartialTrie; // Has ".a" for "a.M." + + int n=0; + for ( int32_t i = 0; + i-1 && (nn+1)!=ustrs[i].length()) { + FB_TRACE("partial",&ustrs[i],false,i); + // is partial. + // is it unique? + int sameAs = -1; + for(int j=0;jadd(prefix, kPARTIAL, status); + revCount++; + FB_TRACE("Added partial",&prefix,false, i); + FB_TRACE(u_errorName(status),&ustrs[i],false,i); + partials[i] = kSuppressInReverse | kAddToForward; + } else { + FB_TRACE("NOT adding partial",&prefix,false, i); + FB_TRACE(u_errorName(status),&ustrs[i],false,i); + } + } + } + for(int i=0;iadd(ustrs[i], kMATCH, status); + revCount++; + FB_TRACE(u_errorName(status), &ustrs[i], false, i); + } else { + FB_TRACE("Adding fwd",&ustrs[i], false, i); + + // an optimization would be to only add the portion after the '.' + // for example, for "Ph.D." we store ".hP" in the reverse table. We could just store "D." in the forward, + // instead of "Ph.D." since we already know the "Ph." part is a match. + // would need the trie to be able to hold 0-length strings, though. + builder2->add(ustrs[i], kMATCH, status); // forward + fwdCount++; + //ustrs[i].reverse(); + ////if(debug2) u_printf("SUPPRESS- not Added(%d): /%S/ status=%s\n",partials[i], ustrs[i].getTerminatedBuffer(), u_errorName(status)); + } + } + FB_TRACE("AbbrCount",nullptr,false, subCount); + + if(revCount>0) { + backwardsTrie.adoptInstead(builder->build(USTRINGTRIE_BUILD_FAST, status)); + if(U_FAILURE(status)) { + FB_TRACE(u_errorName(status),nullptr,false, -1); + return nullptr; + } + } + + if(fwdCount>0) { + forwardsPartialTrie.adoptInstead(builder2->build(USTRINGTRIE_BUILD_FAST, status)); + if(U_FAILURE(status)) { + FB_TRACE(u_errorName(status),nullptr,false, -1); + return nullptr; + } + } + + return new SimpleFilteredSentenceBreakIterator(adopt.orphan(), forwardsPartialTrie.orphan(), backwardsTrie.orphan(), status); +} + + +// ----------- Base class implementation + +FilteredBreakIteratorBuilder::FilteredBreakIteratorBuilder() { +} + +FilteredBreakIteratorBuilder::~FilteredBreakIteratorBuilder() { +} + +FilteredBreakIteratorBuilder * +FilteredBreakIteratorBuilder::createInstance(const Locale& where, UErrorCode& status) { + if(U_FAILURE(status)) return nullptr; + LocalPointer ret(new SimpleFilteredBreakIteratorBuilder(where, status), status); + return (U_SUCCESS(status))? ret.orphan(): nullptr; +} + +FilteredBreakIteratorBuilder * +FilteredBreakIteratorBuilder::createInstance(UErrorCode &status) { + return createEmptyInstance(status); +} + +FilteredBreakIteratorBuilder * +FilteredBreakIteratorBuilder::createEmptyInstance(UErrorCode& status) { + if(U_FAILURE(status)) return nullptr; + LocalPointer ret(new SimpleFilteredBreakIteratorBuilder(status), status); + return (U_SUCCESS(status))? ret.orphan(): nullptr; +} + +U_NAMESPACE_END + +#endif //#if !UCONFIG_NO_BREAK_ITERATION && !UCONFIG_NO_FILTERED_BREAK_ITERATION diff --git a/deps/icu-small/source/common/filterednormalizer2.cpp b/deps/icu-small/source/common/filterednormalizer2.cpp index 63f01206e9784d..e04fbea30a5197 100644 --- a/deps/icu-small/source/common/filterednormalizer2.cpp +++ b/deps/icu-small/source/common/filterednormalizer2.cpp @@ -1,363 +1,363 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2009-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: filterednormalizer2.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2009dec10 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_NORMALIZATION - -#include "unicode/edits.h" -#include "unicode/normalizer2.h" -#include "unicode/stringoptions.h" -#include "unicode/uniset.h" -#include "unicode/unistr.h" -#include "unicode/unorm.h" -#include "cpputils.h" - -U_NAMESPACE_BEGIN - -FilteredNormalizer2::~FilteredNormalizer2() {} - -UnicodeString & -FilteredNormalizer2::normalize(const UnicodeString &src, - UnicodeString &dest, - UErrorCode &errorCode) const { - uprv_checkCanGetBuffer(src, errorCode); - if(U_FAILURE(errorCode)) { - dest.setToBogus(); - return dest; - } - if(&dest==&src) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return dest; - } - dest.remove(); - return normalize(src, dest, USET_SPAN_SIMPLE, errorCode); -} - -// Internal: No argument checking, and appends to dest. -// Pass as input spanCondition the one that is likely to yield a non-zero -// span length at the start of src. -// For set=[:age=3.2:], since almost all common characters were in Unicode 3.2, -// USET_SPAN_SIMPLE should be passed in for the start of src -// and USET_SPAN_NOT_CONTAINED should be passed in if we continue after -// an in-filter prefix. -UnicodeString & -FilteredNormalizer2::normalize(const UnicodeString &src, - UnicodeString &dest, - USetSpanCondition spanCondition, - UErrorCode &errorCode) const { - UnicodeString tempDest; // Don't throw away destination buffer between iterations. - for(int32_t prevSpanLimit=0; prevSpanLimitreset(); - } - options |= U_EDITS_NO_RESET; // Do not reset for each span. - normalizeUTF8(options, src.data(), src.length(), sink, edits, USET_SPAN_SIMPLE, errorCode); -} - -void -FilteredNormalizer2::normalizeUTF8(uint32_t options, const char *src, int32_t length, - ByteSink &sink, Edits *edits, - USetSpanCondition spanCondition, - UErrorCode &errorCode) const { - while (length > 0) { - int32_t spanLength = set.spanUTF8(src, length, spanCondition); - if (spanCondition == USET_SPAN_NOT_CONTAINED) { - if (spanLength != 0) { - if (edits != nullptr) { - edits->addUnchanged(spanLength); - } - if ((options & U_OMIT_UNCHANGED_TEXT) == 0) { - sink.Append(src, spanLength); - } - } - spanCondition = USET_SPAN_SIMPLE; - } else { - if (spanLength != 0) { - // Not norm2.normalizeSecondAndAppend() because we do not want - // to modify the non-filter part of dest. - norm2.normalizeUTF8(options, StringPiece(src, spanLength), sink, edits, errorCode); - if (U_FAILURE(errorCode)) { - break; - } - } - spanCondition = USET_SPAN_NOT_CONTAINED; - } - src += spanLength; - length -= spanLength; - } -} - -UnicodeString & -FilteredNormalizer2::normalizeSecondAndAppend(UnicodeString &first, - const UnicodeString &second, - UErrorCode &errorCode) const { - return normalizeSecondAndAppend(first, second, true, errorCode); -} - -UnicodeString & -FilteredNormalizer2::append(UnicodeString &first, - const UnicodeString &second, - UErrorCode &errorCode) const { - return normalizeSecondAndAppend(first, second, false, errorCode); -} - -UnicodeString & -FilteredNormalizer2::normalizeSecondAndAppend(UnicodeString &first, - const UnicodeString &second, - UBool doNormalize, - UErrorCode &errorCode) const { - uprv_checkCanGetBuffer(first, errorCode); - uprv_checkCanGetBuffer(second, errorCode); - if(U_FAILURE(errorCode)) { - return first; - } - if(&first==&second) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return first; - } - if(first.isEmpty()) { - if(doNormalize) { - return normalize(second, first, errorCode); - } else { - return first=second; - } - } - // merge the in-filter suffix of the first string with the in-filter prefix of the second - int32_t prefixLimit=set.span(second, 0, USET_SPAN_SIMPLE); - if(prefixLimit!=0) { - UnicodeString prefix(second.tempSubString(0, prefixLimit)); - int32_t suffixStart=set.spanBack(first, INT32_MAX, USET_SPAN_SIMPLE); - if(suffixStart==0) { - if(doNormalize) { - norm2.normalizeSecondAndAppend(first, prefix, errorCode); - } else { - norm2.append(first, prefix, errorCode); - } - } else { - UnicodeString middle(first, suffixStart, INT32_MAX); - if(doNormalize) { - norm2.normalizeSecondAndAppend(middle, prefix, errorCode); - } else { - norm2.append(middle, prefix, errorCode); - } - first.replace(suffixStart, INT32_MAX, middle); - } - } - if(prefixLimit 0) { - int32_t spanLength = set.spanUTF8(s, length, spanCondition); - if (spanCondition == USET_SPAN_NOT_CONTAINED) { - spanCondition = USET_SPAN_SIMPLE; - } else { - if (!norm2.isNormalizedUTF8(StringPiece(s, spanLength), errorCode) || - U_FAILURE(errorCode)) { - return false; - } - spanCondition = USET_SPAN_NOT_CONTAINED; - } - s += spanLength; - length -= spanLength; - } - return true; -} - -UNormalizationCheckResult -FilteredNormalizer2::quickCheck(const UnicodeString &s, UErrorCode &errorCode) const { - uprv_checkCanGetBuffer(s, errorCode); - if(U_FAILURE(errorCode)) { - return UNORM_MAYBE; - } - UNormalizationCheckResult result=UNORM_YES; - USetSpanCondition spanCondition=USET_SPAN_SIMPLE; - for(int32_t prevSpanLimit=0; prevSpanLimitreset(); + } + options |= U_EDITS_NO_RESET; // Do not reset for each span. + normalizeUTF8(options, src.data(), src.length(), sink, edits, USET_SPAN_SIMPLE, errorCode); +} + +void +FilteredNormalizer2::normalizeUTF8(uint32_t options, const char *src, int32_t length, + ByteSink &sink, Edits *edits, + USetSpanCondition spanCondition, + UErrorCode &errorCode) const { + while (length > 0) { + int32_t spanLength = set.spanUTF8(src, length, spanCondition); + if (spanCondition == USET_SPAN_NOT_CONTAINED) { + if (spanLength != 0) { + if (edits != nullptr) { + edits->addUnchanged(spanLength); + } + if ((options & U_OMIT_UNCHANGED_TEXT) == 0) { + sink.Append(src, spanLength); + } + } + spanCondition = USET_SPAN_SIMPLE; + } else { + if (spanLength != 0) { + // Not norm2.normalizeSecondAndAppend() because we do not want + // to modify the non-filter part of dest. + norm2.normalizeUTF8(options, StringPiece(src, spanLength), sink, edits, errorCode); + if (U_FAILURE(errorCode)) { + break; + } + } + spanCondition = USET_SPAN_NOT_CONTAINED; + } + src += spanLength; + length -= spanLength; + } +} + +UnicodeString & +FilteredNormalizer2::normalizeSecondAndAppend(UnicodeString &first, + const UnicodeString &second, + UErrorCode &errorCode) const { + return normalizeSecondAndAppend(first, second, true, errorCode); +} + +UnicodeString & +FilteredNormalizer2::append(UnicodeString &first, + const UnicodeString &second, + UErrorCode &errorCode) const { + return normalizeSecondAndAppend(first, second, false, errorCode); +} + +UnicodeString & +FilteredNormalizer2::normalizeSecondAndAppend(UnicodeString &first, + const UnicodeString &second, + UBool doNormalize, + UErrorCode &errorCode) const { + uprv_checkCanGetBuffer(first, errorCode); + uprv_checkCanGetBuffer(second, errorCode); + if(U_FAILURE(errorCode)) { + return first; + } + if(&first==&second) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return first; + } + if(first.isEmpty()) { + if(doNormalize) { + return normalize(second, first, errorCode); + } else { + return first=second; + } + } + // merge the in-filter suffix of the first string with the in-filter prefix of the second + int32_t prefixLimit=set.span(second, 0, USET_SPAN_SIMPLE); + if(prefixLimit!=0) { + UnicodeString prefix(second.tempSubString(0, prefixLimit)); + int32_t suffixStart=set.spanBack(first, INT32_MAX, USET_SPAN_SIMPLE); + if(suffixStart==0) { + if(doNormalize) { + norm2.normalizeSecondAndAppend(first, prefix, errorCode); + } else { + norm2.append(first, prefix, errorCode); + } + } else { + UnicodeString middle(first, suffixStart, INT32_MAX); + if(doNormalize) { + norm2.normalizeSecondAndAppend(middle, prefix, errorCode); + } else { + norm2.append(middle, prefix, errorCode); + } + first.replace(suffixStart, INT32_MAX, middle); + } + } + if(prefixLimit 0) { + int32_t spanLength = set.spanUTF8(s, length, spanCondition); + if (spanCondition == USET_SPAN_NOT_CONTAINED) { + spanCondition = USET_SPAN_SIMPLE; + } else { + if (!norm2.isNormalizedUTF8(StringPiece(s, spanLength), errorCode) || + U_FAILURE(errorCode)) { + return false; + } + spanCondition = USET_SPAN_NOT_CONTAINED; + } + s += spanLength; + length -= spanLength; + } + return true; +} + +UNormalizationCheckResult +FilteredNormalizer2::quickCheck(const UnicodeString &s, UErrorCode &errorCode) const { + uprv_checkCanGetBuffer(s, errorCode); + if(U_FAILURE(errorCode)) { + return UNORM_MAYBE; + } + UNormalizationCheckResult result=UNORM_YES; + USetSpanCondition spanCondition=USET_SPAN_SIMPLE; + for(int32_t prevSpanLimit=0; prevSpanLimit -#ifdef __MVS__ /* defined by z/OS compiler */ -#define _POSIX_SOURCE -#include /* 12 Nov 2011 JAM iscics() function */ -#endif -#include "charstr.h" - -using namespace icu; - -#ifndef UPLUG_TRACE -#define UPLUG_TRACE 0 -#endif - -#if UPLUG_TRACE -#include -#define DBG(x) fprintf(stderr, "%s:%d: ",__FILE__,__LINE__); fprintf x -#endif - -/** - * Internal structure of an ICU plugin. - */ - -struct UPlugData { - UPlugEntrypoint *entrypoint; /**< plugin entrypoint */ - uint32_t structSize; /**< initialized to the size of this structure */ - uint32_t token; /**< must be U_PLUG_TOKEN */ - void *lib; /**< plugin library, or NULL */ - char libName[UPLUG_NAME_MAX]; /**< library name */ - char sym[UPLUG_NAME_MAX]; /**< plugin symbol, or NULL */ - char config[UPLUG_NAME_MAX]; /**< configuration data */ - void *context; /**< user context data */ - char name[UPLUG_NAME_MAX]; /**< name of plugin */ - UPlugLevel level; /**< level of plugin */ - UBool awaitingLoad; /**< true if the plugin is awaiting a load call */ - UBool dontUnload; /**< true if plugin must stay resident (leak plugin and lib) */ - UErrorCode pluginStatus; /**< status code of plugin */ -}; - - - -#define UPLUG_LIBRARY_INITIAL_COUNT 8 -#define UPLUG_PLUGIN_INITIAL_COUNT 12 - -/** - * Remove an item - * @param list the full list - * @param listSize the number of entries in the list - * @param memberSize the size of one member - * @param itemToRemove the item number of the member - * @return the new listsize - */ -static int32_t uplug_removeEntryAt(void *list, int32_t listSize, int32_t memberSize, int32_t itemToRemove) { - uint8_t *bytePtr = (uint8_t *)list; - - /* get rid of some bad cases first */ - if(listSize<1) { - return listSize; - } - - /* is there anything to move? */ - if(listSize > itemToRemove+1) { - memmove(bytePtr+(itemToRemove*memberSize), bytePtr+((itemToRemove+1)*memberSize), memberSize); - } - - return listSize-1; -} - - - - -#if U_ENABLE_DYLOAD -/** - * Library management. Internal. - * @internal - */ -struct UPlugLibrary; - -/** - * Library management. Internal. - * @internal - */ -typedef struct UPlugLibrary { - void *lib; /**< library ptr */ - char name[UPLUG_NAME_MAX]; /**< library name */ - uint32_t ref; /**< reference count */ -} UPlugLibrary; - -static UPlugLibrary staticLibraryList[UPLUG_LIBRARY_INITIAL_COUNT]; -static UPlugLibrary * libraryList = staticLibraryList; -static int32_t libraryCount = 0; -static int32_t libraryMax = UPLUG_LIBRARY_INITIAL_COUNT; - -/** - * Search for a library. Doesn't lock - * @param libName libname to search for - * @return the library's struct - */ -static int32_t searchForLibraryName(const char *libName) { - int32_t i; - - for(i=0;i= libraryMax) { - /* Ran out of library slots. Statically allocated because we can't depend on allocating memory.. */ - *status = U_MEMORY_ALLOCATION_ERROR; -#if UPLUG_TRACE - DBG((stderr, "uplug_openLibrary() - out of library slots (max %d)\n", libraryMax)); -#endif - return NULL; - } - /* Some operating systems don't want - DL operations from multiple threads. */ - libraryList[libEntry].lib = uprv_dl_open(libName, status); -#if UPLUG_TRACE - DBG((stderr, "uplug_openLibrary(%s,%s) libEntry %d, lib %p\n", libName, u_errorName(*status), libEntry, lib)); -#endif - - if(libraryList[libEntry].lib == NULL || U_FAILURE(*status)) { - /* cleanup. */ - libraryList[libEntry].lib = NULL; /* failure with open */ - libraryList[libEntry].name[0] = 0; -#if UPLUG_TRACE - DBG((stderr, "uplug_openLibrary(%s,%s) libEntry %d, lib %p\n", libName, u_errorName(*status), libEntry, lib)); -#endif - /* no need to free - just won't increase the count. */ - libraryCount--; - } else { /* is it still there? */ - /* link it in */ - uprv_strncpy(libraryList[libEntry].name,libName,UPLUG_NAME_MAX); - libraryList[libEntry].ref=1; - lib = libraryList[libEntry].lib; - } - - } else { - lib = libraryList[libEntry].lib; - libraryList[libEntry].ref++; - } - return lib; -} - -U_CAPI void U_EXPORT2 -uplug_closeLibrary(void *lib, UErrorCode *status) { - int32_t i; - -#if UPLUG_TRACE - DBG((stderr, "uplug_closeLibrary(%p,%s) list %p\n", lib, u_errorName(*status), (void*)libraryList)); -#endif - if(U_FAILURE(*status)) return; - - for(i=0;i=pastPlug) { - return pluginCount; - } else { - return (d-pluginList)/sizeof(pluginList[0]); - } -} - - -U_CAPI UPlugData * U_EXPORT2 -uplug_nextPlug(UPlugData *prior) { - if(prior==NULL) { - return pluginList; - } else { - UPlugData *nextPlug = &prior[1]; - UPlugData *pastPlug = &pluginList[pluginCount]; - - if(nextPlug>=pastPlug) { - return NULL; - } else { - return nextPlug; - } - } -} - - - -/** - * Call the plugin with some params - */ -static void uplug_callPlug(UPlugData *plug, UPlugReason reason, UErrorCode *status) { - UPlugTokenReturn token; - if(plug==NULL||U_FAILURE(*status)) { - return; - } - token = (*(plug->entrypoint))(plug, reason, status); - if(token!=UPLUG_TOKEN) { - *status = U_INTERNAL_PROGRAM_ERROR; - } -} - - -static void uplug_unloadPlug(UPlugData *plug, UErrorCode *status) { - if(plug->awaitingLoad) { /* shouldn't happen. Plugin hasn't been loaded yet.*/ - *status = U_INTERNAL_PROGRAM_ERROR; - return; - } - if(U_SUCCESS(plug->pluginStatus)) { - /* Don't unload a plug which has a failing load status - means it didn't actually load. */ - uplug_callPlug(plug, UPLUG_REASON_UNLOAD, status); - } -} - -static void uplug_queryPlug(UPlugData *plug, UErrorCode *status) { - if(!plug->awaitingLoad || !(plug->level == UPLUG_LEVEL_UNKNOWN) ) { /* shouldn't happen. Plugin hasn't been loaded yet.*/ - *status = U_INTERNAL_PROGRAM_ERROR; - return; - } - plug->level = UPLUG_LEVEL_INVALID; - uplug_callPlug(plug, UPLUG_REASON_QUERY, status); - if(U_SUCCESS(*status)) { - if(plug->level == UPLUG_LEVEL_INVALID) { - plug->pluginStatus = U_PLUGIN_DIDNT_SET_LEVEL; - plug->awaitingLoad = false; - } - } else { - plug->pluginStatus = U_INTERNAL_PROGRAM_ERROR; - plug->awaitingLoad = false; - } -} - - -static void uplug_loadPlug(UPlugData *plug, UErrorCode *status) { - if(U_FAILURE(*status)) { - return; - } - if(!plug->awaitingLoad || (plug->level < UPLUG_LEVEL_LOW) ) { /* shouldn't happen. Plugin hasn't been loaded yet.*/ - *status = U_INTERNAL_PROGRAM_ERROR; - return; - } - uplug_callPlug(plug, UPLUG_REASON_LOAD, status); - plug->awaitingLoad = false; - if(!U_SUCCESS(*status)) { - plug->pluginStatus = U_INTERNAL_PROGRAM_ERROR; - } -} - -static UPlugData *uplug_allocateEmptyPlug(UErrorCode *status) -{ - UPlugData *plug = NULL; - - if(U_FAILURE(*status)) { - return NULL; - } - - if(pluginCount == UPLUG_PLUGIN_INITIAL_COUNT) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - plug = &pluginList[pluginCount++]; - - plug->token = UPLUG_TOKEN; - plug->structSize = sizeof(UPlugData); - plug->name[0]=0; - plug->level = UPLUG_LEVEL_UNKNOWN; /* initialize to null state */ - plug->awaitingLoad = true; - plug->dontUnload = false; - plug->pluginStatus = U_ZERO_ERROR; - plug->libName[0] = 0; - plug->config[0]=0; - plug->sym[0]=0; - plug->lib=NULL; - plug->entrypoint=NULL; - - - return plug; -} - -static UPlugData *uplug_allocatePlug(UPlugEntrypoint *entrypoint, const char *config, void *lib, const char *symName, - UErrorCode *status) { - UPlugData *plug = uplug_allocateEmptyPlug(status); - if(U_FAILURE(*status)) { - return NULL; - } - - if(config!=NULL) { - uprv_strncpy(plug->config, config, UPLUG_NAME_MAX); - } else { - plug->config[0] = 0; - } - - if(symName!=NULL) { - uprv_strncpy(plug->sym, symName, UPLUG_NAME_MAX); - } else { - plug->sym[0] = 0; - } - - plug->entrypoint = entrypoint; - plug->lib = lib; - uplug_queryPlug(plug, status); - - return plug; -} - -static void uplug_deallocatePlug(UPlugData *plug, UErrorCode *status) { - UErrorCode subStatus = U_ZERO_ERROR; - if(!plug->dontUnload) { -#if U_ENABLE_DYLOAD - uplug_closeLibrary(plug->lib, &subStatus); -#endif - } - plug->lib = NULL; - if(U_SUCCESS(*status) && U_FAILURE(subStatus)) { - *status = subStatus; - } - /* shift plugins up and decrement count. */ - if(U_SUCCESS(*status)) { - /* all ok- remove. */ - pluginCount = uplug_removeEntryAt(pluginList, pluginCount, sizeof(plug[0]), uplug_pluginNumber(plug)); - } else { - /* not ok- leave as a message. */ - plug->awaitingLoad=false; - plug->entrypoint=0; - plug->dontUnload=true; - } -} - -static void uplug_doUnloadPlug(UPlugData *plugToRemove, UErrorCode *status) { - if(plugToRemove != NULL) { - uplug_unloadPlug(plugToRemove, status); - uplug_deallocatePlug(plugToRemove, status); - } -} - -U_CAPI void U_EXPORT2 -uplug_removePlug(UPlugData *plug, UErrorCode *status) { - UPlugData *cursor = NULL; - UPlugData *plugToRemove = NULL; - if(U_FAILURE(*status)) return; - - for(cursor=pluginList;cursor!=NULL;) { - if(cursor==plug) { - plugToRemove = plug; - cursor=NULL; - } else { - cursor = uplug_nextPlug(cursor); - } - } - - uplug_doUnloadPlug(plugToRemove, status); -} - - - - -U_CAPI void U_EXPORT2 -uplug_setPlugNoUnload(UPlugData *data, UBool dontUnload) -{ - data->dontUnload = dontUnload; -} - - -U_CAPI void U_EXPORT2 -uplug_setPlugLevel(UPlugData *data, UPlugLevel level) { - data->level = level; -} - - -U_CAPI UPlugLevel U_EXPORT2 -uplug_getPlugLevel(UPlugData *data) { - return data->level; -} - - -U_CAPI void U_EXPORT2 -uplug_setPlugName(UPlugData *data, const char *name) { - uprv_strncpy(data->name, name, UPLUG_NAME_MAX); -} - - -U_CAPI const char * U_EXPORT2 -uplug_getPlugName(UPlugData *data) { - return data->name; -} - - -U_CAPI const char * U_EXPORT2 -uplug_getSymbolName(UPlugData *data) { - return data->sym; -} - -U_CAPI const char * U_EXPORT2 -uplug_getLibraryName(UPlugData *data, UErrorCode *status) { - if(data->libName[0]) { - return data->libName; - } else { -#if U_ENABLE_DYLOAD - return uplug_findLibrary(data->lib, status); -#else - return NULL; -#endif - } -} - -U_CAPI void * U_EXPORT2 -uplug_getLibrary(UPlugData *data) { - return data->lib; -} - -U_CAPI void * U_EXPORT2 -uplug_getContext(UPlugData *data) { - return data->context; -} - - -U_CAPI void U_EXPORT2 -uplug_setContext(UPlugData *data, void *context) { - data->context = context; -} - -U_CAPI const char* U_EXPORT2 -uplug_getConfiguration(UPlugData *data) { - return data->config; -} - -U_CAPI UPlugData* U_EXPORT2 -uplug_getPlugInternal(int32_t n) { - if(n <0 || n >= pluginCount) { - return NULL; - } else { - return &(pluginList[n]); - } -} - - -U_CAPI UErrorCode U_EXPORT2 -uplug_getPlugLoadStatus(UPlugData *plug) { - return plug->pluginStatus; -} - - - - -/** - * Initialize a plugin from an entrypoint and library - but don't load it. - */ -static UPlugData* uplug_initPlugFromEntrypointAndLibrary(UPlugEntrypoint *entrypoint, const char *config, void *lib, const char *sym, - UErrorCode *status) { - UPlugData *plug = NULL; - - plug = uplug_allocatePlug(entrypoint, config, lib, sym, status); - - if(U_SUCCESS(*status)) { - return plug; - } else { - uplug_deallocatePlug(plug, status); - return NULL; - } -} - -U_CAPI UPlugData* U_EXPORT2 -uplug_loadPlugFromEntrypoint(UPlugEntrypoint *entrypoint, const char *config, UErrorCode *status) { - UPlugData* plug = uplug_initPlugFromEntrypointAndLibrary(entrypoint, config, NULL, NULL, status); - uplug_loadPlug(plug, status); - return plug; -} - -#if U_ENABLE_DYLOAD - -static UPlugData* -uplug_initErrorPlug(const char *libName, const char *sym, const char *config, const char *nameOrError, UErrorCode loadStatus, UErrorCode *status) -{ - UPlugData *plug = uplug_allocateEmptyPlug(status); - if(U_FAILURE(*status)) return NULL; - - plug->pluginStatus = loadStatus; - plug->awaitingLoad = false; /* Won't load. */ - plug->dontUnload = true; /* cannot unload. */ - - if(sym!=NULL) { - uprv_strncpy(plug->sym, sym, UPLUG_NAME_MAX); - } - - if(libName!=NULL) { - uprv_strncpy(plug->libName, libName, UPLUG_NAME_MAX); - } - - if(nameOrError!=NULL) { - uprv_strncpy(plug->name, nameOrError, UPLUG_NAME_MAX); - } - - if(config!=NULL) { - uprv_strncpy(plug->config, config, UPLUG_NAME_MAX); - } - - return plug; -} - -/** - * Fetch a plugin from DLL, and then initialize it from a library- but don't load it. - */ -static UPlugData* -uplug_initPlugFromLibrary(const char *libName, const char *sym, const char *config, UErrorCode *status) { - void *lib = NULL; - UPlugData *plug = NULL; - if(U_FAILURE(*status)) { return NULL; } - lib = uplug_openLibrary(libName, status); - if(lib!=NULL && U_SUCCESS(*status)) { - UPlugEntrypoint *entrypoint = NULL; - entrypoint = (UPlugEntrypoint*)uprv_dlsym_func(lib, sym, status); - - if(entrypoint!=NULL&&U_SUCCESS(*status)) { - plug = uplug_initPlugFromEntrypointAndLibrary(entrypoint, config, lib, sym, status); - if(plug!=NULL&&U_SUCCESS(*status)) { - plug->lib = lib; /* plug takes ownership of library */ - lib = NULL; /* library is now owned by plugin. */ - } - } else { - UErrorCode subStatus = U_ZERO_ERROR; - plug = uplug_initErrorPlug(libName,sym,config,"ERROR: Could not load entrypoint",(lib==NULL)?U_MISSING_RESOURCE_ERROR:*status,&subStatus); - } - if(lib!=NULL) { /* still need to close the lib */ - UErrorCode subStatus = U_ZERO_ERROR; - uplug_closeLibrary(lib, &subStatus); /* don't care here */ - } - } else { - UErrorCode subStatus = U_ZERO_ERROR; - plug = uplug_initErrorPlug(libName,sym,config,"ERROR: could not load library",(lib==NULL)?U_MISSING_RESOURCE_ERROR:*status,&subStatus); - } - return plug; -} - -U_CAPI UPlugData* U_EXPORT2 -uplug_loadPlugFromLibrary(const char *libName, const char *sym, const char *config, UErrorCode *status) { - UPlugData *plug = NULL; - if(U_FAILURE(*status)) { return NULL; } - plug = uplug_initPlugFromLibrary(libName, sym, config, status); - uplug_loadPlug(plug, status); - - return plug; -} - -#endif - -static UPlugLevel gCurrentLevel = UPLUG_LEVEL_LOW; - -U_CAPI UPlugLevel U_EXPORT2 uplug_getCurrentLevel() { - return gCurrentLevel; -} - -static UBool U_CALLCONV uplug_cleanup(void) -{ - int32_t i; - - UPlugData *pluginToRemove; - /* cleanup plugs */ - for(i=0;iawaitingLoad) { - if(pluginToLoad->level == UPLUG_LEVEL_LOW) { - if(currentLevel > UPLUG_LEVEL_LOW) { - pluginToLoad->pluginStatus = U_PLUGIN_TOO_HIGH; - } else { - UPlugLevel newLevel; - uplug_loadPlug(pluginToLoad, &subStatus); - newLevel = uplug_getCurrentLevel(); - if(newLevel > currentLevel) { - pluginToLoad->pluginStatus = U_PLUGIN_CHANGED_LEVEL_WARNING; - currentLevel = newLevel; - } - } - pluginToLoad->awaitingLoad = false; - } - } - } - for(i=0;iawaitingLoad) { - if(pluginToLoad->level == UPLUG_LEVEL_INVALID) { - pluginToLoad->pluginStatus = U_PLUGIN_DIDNT_SET_LEVEL; - } else if(pluginToLoad->level == UPLUG_LEVEL_UNKNOWN) { - pluginToLoad->pluginStatus = U_INTERNAL_PROGRAM_ERROR; - } else { - uplug_loadPlug(pluginToLoad, &subStatus); - } - pluginToLoad->awaitingLoad = false; - } - } - -#if UPLUG_TRACE - DBG((stderr, " Done Loading Plugs. Level: %d\n", (int32_t)uplug_getCurrentLevel())); -#endif -} - -/* Name of the plugin config file */ -static char plugin_file[2048] = ""; -#endif - -U_CAPI const char* U_EXPORT2 -uplug_getPluginFile() { -#if U_ENABLE_DYLOAD && !UCONFIG_NO_FILE_IO - return plugin_file; -#else - return NULL; -#endif -} - - -// uplug_init() is called first thing from u_init(). - -U_CAPI void U_EXPORT2 -uplug_init(UErrorCode *status) { -#if !U_ENABLE_DYLOAD - (void)status; /* unused */ -#elif !UCONFIG_NO_FILE_IO - CharString plugin_dir; - const char *env = getenv("ICU_PLUGINS"); - - if(U_FAILURE(*status)) return; - if(env != NULL) { - plugin_dir.append(env, -1, *status); - } - if(U_FAILURE(*status)) return; - -#if defined(DEFAULT_ICU_PLUGINS) - if(plugin_dir.isEmpty()) { - plugin_dir.append(DEFAULT_ICU_PLUGINS, -1, *status); - } -#endif - -#if UPLUG_TRACE - DBG((stderr, "ICU_PLUGINS=%s\n", plugin_dir.data())); -#endif - - if(!plugin_dir.isEmpty()) { - FILE *f; - - CharString pluginFile; -#ifdef OS390BATCH -/* There are potentially a lot of ways to implement a plugin directory on OS390/zOS */ -/* Keeping in mind that unauthorized file access is logged, monitored, and enforced */ -/* I've chosen to open a DDNAME if BATCH and leave it alone for (presumably) UNIX */ -/* System Services. Alternative techniques might be allocating a member in */ -/* SYS1.PARMLIB or setting an environment variable "ICU_PLUGIN_PATH" (?). The */ -/* DDNAME can be connected to a file in the HFS if need be. */ - - pluginFile.append("//DD:ICUPLUG", -1, *status); /* JAM 20 Oct 2011 */ -#else - pluginFile.append(plugin_dir, *status); - pluginFile.append(U_FILE_SEP_STRING, -1, *status); - pluginFile.append("icuplugins", -1, *status); - pluginFile.append(U_ICU_VERSION_SHORT, -1, *status); - pluginFile.append(".txt", -1, *status); -#endif - -#if UPLUG_TRACE - DBG((stderr, "status=%s\n", u_errorName(*status))); -#endif - - if(U_FAILURE(*status)) { - return; - } - if((size_t)pluginFile.length() > (sizeof(plugin_file)-1)) { - *status = U_BUFFER_OVERFLOW_ERROR; -#if UPLUG_TRACE - DBG((stderr, "status=%s\n", u_errorName(*status))); -#endif - return; - } - - /* plugin_file is not used for processing - it is only used - so that uplug_getPluginFile() works (i.e. icuinfo) - */ - pluginFile.extract(plugin_file, sizeof(plugin_file), *status); - -#if UPLUG_TRACE - DBG((stderr, "pluginfile= %s len %d/%d\n", plugin_file, (int)strlen(plugin_file), (int)sizeof(plugin_file))); -#endif - -#ifdef __MVS__ - if (iscics()) /* 12 Nov 2011 JAM */ - { - f = NULL; - } - else -#endif - { - f = fopen(pluginFile.data(), "r"); - } - - if(f != NULL) { - char linebuf[1024]; - char *p, *libName=NULL, *symName=NULL, *config=NULL; - int32_t line = 0; - - - while(fgets(linebuf,1023,f)) { - line++; - - if(!*linebuf || *linebuf=='#') { - continue; - } else { - p = linebuf; - while(*p&&isspace((int)*p)) - p++; - if(!*p || *p=='#') continue; - libName = p; - while(*p&&!isspace((int)*p)) { - p++; - } - if(!*p || *p=='#') continue; /* no tab after libname */ - *p=0; /* end of libname */ - p++; - while(*p&&isspace((int)*p)) { - p++; - } - if(!*p||*p=='#') continue; /* no symname after libname +tab */ - symName = p; - while(*p&&!isspace((int)*p)) { - p++; - } - - if(*p) { /* has config */ - *p=0; - ++p; - while(*p&&isspace((int)*p)) { - p++; - } - if(*p) { - config = p; - } - } - - /* chop whitespace at the end of the config */ - if(config!=NULL&&*config!=0) { - p = config+strlen(config); - while(p>config&&isspace((int)*(--p))) { - *p=0; - } - } - - /* OK, we're good. */ - { - UErrorCode subStatus = U_ZERO_ERROR; - UPlugData *plug = uplug_initPlugFromLibrary(libName, symName, config, &subStatus); - if(U_FAILURE(subStatus) && U_SUCCESS(*status)) { - *status = subStatus; - } -#if UPLUG_TRACE - DBG((stderr, "PLUGIN libName=[%s], sym=[%s], config=[%s]\n", libName, symName, config)); - DBG((stderr, " -> %p, %s\n", (void*)plug, u_errorName(subStatus))); -#else - (void)plug; /* unused */ -#endif - } - } - } - fclose(f); - } else { -#if UPLUG_TRACE - DBG((stderr, "Can't open plugin file %s\n", plugin_file)); -#endif - } - } - uplug_loadWaitingPlugs(status); -#endif /* U_ENABLE_DYLOAD */ - gCurrentLevel = UPLUG_LEVEL_HIGH; - ucln_registerCleanup(UCLN_UPLUG, uplug_cleanup); -} - -#endif - - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2009-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* FILE NAME : icuplug.c +* +* Date Name Description +* 10/29/2009 sl New. +****************************************************************************** +*/ + +#include "unicode/icuplug.h" + + +#if UCONFIG_ENABLE_PLUGINS + + +#include "icuplugimp.h" +#include "cstring.h" +#include "cmemory.h" +#include "putilimp.h" +#include "ucln.h" +#include +#ifdef __MVS__ /* defined by z/OS compiler */ +#define _POSIX_SOURCE +#include /* 12 Nov 2011 JAM iscics() function */ +#endif +#include "charstr.h" + +using namespace icu; + +#ifndef UPLUG_TRACE +#define UPLUG_TRACE 0 +#endif + +#if UPLUG_TRACE +#include +#define DBG(x) fprintf(stderr, "%s:%d: ",__FILE__,__LINE__); fprintf x +#endif + +/** + * Internal structure of an ICU plugin. + */ + +struct UPlugData { + UPlugEntrypoint *entrypoint; /**< plugin entrypoint */ + uint32_t structSize; /**< initialized to the size of this structure */ + uint32_t token; /**< must be U_PLUG_TOKEN */ + void *lib; /**< plugin library, or nullptr */ + char libName[UPLUG_NAME_MAX]; /**< library name */ + char sym[UPLUG_NAME_MAX]; /**< plugin symbol, or nullptr */ + char config[UPLUG_NAME_MAX]; /**< configuration data */ + void *context; /**< user context data */ + char name[UPLUG_NAME_MAX]; /**< name of plugin */ + UPlugLevel level; /**< level of plugin */ + UBool awaitingLoad; /**< true if the plugin is awaiting a load call */ + UBool dontUnload; /**< true if plugin must stay resident (leak plugin and lib) */ + UErrorCode pluginStatus; /**< status code of plugin */ +}; + + + +#define UPLUG_LIBRARY_INITIAL_COUNT 8 +#define UPLUG_PLUGIN_INITIAL_COUNT 12 + +/** + * Remove an item + * @param list the full list + * @param listSize the number of entries in the list + * @param memberSize the size of one member + * @param itemToRemove the item number of the member + * @return the new listsize + */ +static int32_t uplug_removeEntryAt(void *list, int32_t listSize, int32_t memberSize, int32_t itemToRemove) { + uint8_t *bytePtr = (uint8_t *)list; + + /* get rid of some bad cases first */ + if(listSize<1) { + return listSize; + } + + /* is there anything to move? */ + if(listSize > itemToRemove+1) { + memmove(bytePtr+(itemToRemove*memberSize), bytePtr+((itemToRemove+1)*memberSize), memberSize); + } + + return listSize-1; +} + + + + +#if U_ENABLE_DYLOAD +/** + * Library management. Internal. + * @internal + */ +struct UPlugLibrary; + +/** + * Library management. Internal. + * @internal + */ +typedef struct UPlugLibrary { + void *lib; /**< library ptr */ + char name[UPLUG_NAME_MAX]; /**< library name */ + uint32_t ref; /**< reference count */ +} UPlugLibrary; + +static UPlugLibrary staticLibraryList[UPLUG_LIBRARY_INITIAL_COUNT]; +static UPlugLibrary * libraryList = staticLibraryList; +static int32_t libraryCount = 0; +static int32_t libraryMax = UPLUG_LIBRARY_INITIAL_COUNT; + +/** + * Search for a library. Doesn't lock + * @param libName libname to search for + * @return the library's struct + */ +static int32_t searchForLibraryName(const char *libName) { + int32_t i; + + for(i=0;i= libraryMax) { + /* Ran out of library slots. Statically allocated because we can't depend on allocating memory.. */ + *status = U_MEMORY_ALLOCATION_ERROR; +#if UPLUG_TRACE + DBG((stderr, "uplug_openLibrary() - out of library slots (max %d)\n", libraryMax)); +#endif + return nullptr; + } + /* Some operating systems don't want + DL operations from multiple threads. */ + libraryList[libEntry].lib = uprv_dl_open(libName, status); +#if UPLUG_TRACE + DBG((stderr, "uplug_openLibrary(%s,%s) libEntry %d, lib %p\n", libName, u_errorName(*status), libEntry, lib)); +#endif + + if(libraryList[libEntry].lib == nullptr || U_FAILURE(*status)) { + /* cleanup. */ + libraryList[libEntry].lib = nullptr; /* failure with open */ + libraryList[libEntry].name[0] = 0; +#if UPLUG_TRACE + DBG((stderr, "uplug_openLibrary(%s,%s) libEntry %d, lib %p\n", libName, u_errorName(*status), libEntry, lib)); +#endif + /* no need to free - just won't increase the count. */ + libraryCount--; + } else { /* is it still there? */ + /* link it in */ + uprv_strncpy(libraryList[libEntry].name,libName,UPLUG_NAME_MAX); + libraryList[libEntry].ref=1; + lib = libraryList[libEntry].lib; + } + + } else { + lib = libraryList[libEntry].lib; + libraryList[libEntry].ref++; + } + return lib; +} + +U_CAPI void U_EXPORT2 +uplug_closeLibrary(void *lib, UErrorCode *status) { + int32_t i; + +#if UPLUG_TRACE + DBG((stderr, "uplug_closeLibrary(%p,%s) list %p\n", lib, u_errorName(*status), (void*)libraryList)); +#endif + if(U_FAILURE(*status)) return; + + for(i=0;i=pastPlug) { + return pluginCount; + } else { + return (d-pluginList)/sizeof(pluginList[0]); + } +} + + +U_CAPI UPlugData * U_EXPORT2 +uplug_nextPlug(UPlugData *prior) { + if(prior==nullptr) { + return pluginList; + } else { + UPlugData *nextPlug = &prior[1]; + UPlugData *pastPlug = &pluginList[pluginCount]; + + if(nextPlug>=pastPlug) { + return nullptr; + } else { + return nextPlug; + } + } +} + + + +/** + * Call the plugin with some params + */ +static void uplug_callPlug(UPlugData *plug, UPlugReason reason, UErrorCode *status) { + UPlugTokenReturn token; + if(plug==nullptr||U_FAILURE(*status)) { + return; + } + token = (*(plug->entrypoint))(plug, reason, status); + if(token!=UPLUG_TOKEN) { + *status = U_INTERNAL_PROGRAM_ERROR; + } +} + + +static void uplug_unloadPlug(UPlugData *plug, UErrorCode *status) { + if(plug->awaitingLoad) { /* shouldn't happen. Plugin hasn't been loaded yet.*/ + *status = U_INTERNAL_PROGRAM_ERROR; + return; + } + if(U_SUCCESS(plug->pluginStatus)) { + /* Don't unload a plug which has a failing load status - means it didn't actually load. */ + uplug_callPlug(plug, UPLUG_REASON_UNLOAD, status); + } +} + +static void uplug_queryPlug(UPlugData *plug, UErrorCode *status) { + if(!plug->awaitingLoad || !(plug->level == UPLUG_LEVEL_UNKNOWN) ) { /* shouldn't happen. Plugin hasn't been loaded yet.*/ + *status = U_INTERNAL_PROGRAM_ERROR; + return; + } + plug->level = UPLUG_LEVEL_INVALID; + uplug_callPlug(plug, UPLUG_REASON_QUERY, status); + if(U_SUCCESS(*status)) { + if(plug->level == UPLUG_LEVEL_INVALID) { + plug->pluginStatus = U_PLUGIN_DIDNT_SET_LEVEL; + plug->awaitingLoad = false; + } + } else { + plug->pluginStatus = U_INTERNAL_PROGRAM_ERROR; + plug->awaitingLoad = false; + } +} + + +static void uplug_loadPlug(UPlugData *plug, UErrorCode *status) { + if(U_FAILURE(*status)) { + return; + } + if(!plug->awaitingLoad || (plug->level < UPLUG_LEVEL_LOW) ) { /* shouldn't happen. Plugin hasn't been loaded yet.*/ + *status = U_INTERNAL_PROGRAM_ERROR; + return; + } + uplug_callPlug(plug, UPLUG_REASON_LOAD, status); + plug->awaitingLoad = false; + if(!U_SUCCESS(*status)) { + plug->pluginStatus = U_INTERNAL_PROGRAM_ERROR; + } +} + +static UPlugData *uplug_allocateEmptyPlug(UErrorCode *status) +{ + UPlugData *plug = nullptr; + + if(U_FAILURE(*status)) { + return nullptr; + } + + if(pluginCount == UPLUG_PLUGIN_INITIAL_COUNT) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + plug = &pluginList[pluginCount++]; + + plug->token = UPLUG_TOKEN; + plug->structSize = sizeof(UPlugData); + plug->name[0]=0; + plug->level = UPLUG_LEVEL_UNKNOWN; /* initialize to null state */ + plug->awaitingLoad = true; + plug->dontUnload = false; + plug->pluginStatus = U_ZERO_ERROR; + plug->libName[0] = 0; + plug->config[0]=0; + plug->sym[0]=0; + plug->lib=nullptr; + plug->entrypoint=nullptr; + + + return plug; +} + +static UPlugData *uplug_allocatePlug(UPlugEntrypoint *entrypoint, const char *config, void *lib, const char *symName, + UErrorCode *status) { + UPlugData *plug = uplug_allocateEmptyPlug(status); + if(U_FAILURE(*status)) { + return nullptr; + } + + if(config!=nullptr) { + uprv_strncpy(plug->config, config, UPLUG_NAME_MAX); + } else { + plug->config[0] = 0; + } + + if(symName!=nullptr) { + uprv_strncpy(plug->sym, symName, UPLUG_NAME_MAX); + } else { + plug->sym[0] = 0; + } + + plug->entrypoint = entrypoint; + plug->lib = lib; + uplug_queryPlug(plug, status); + + return plug; +} + +static void uplug_deallocatePlug(UPlugData *plug, UErrorCode *status) { + UErrorCode subStatus = U_ZERO_ERROR; + if(!plug->dontUnload) { +#if U_ENABLE_DYLOAD + uplug_closeLibrary(plug->lib, &subStatus); +#endif + } + plug->lib = nullptr; + if(U_SUCCESS(*status) && U_FAILURE(subStatus)) { + *status = subStatus; + } + /* shift plugins up and decrement count. */ + if(U_SUCCESS(*status)) { + /* all ok- remove. */ + pluginCount = uplug_removeEntryAt(pluginList, pluginCount, sizeof(plug[0]), uplug_pluginNumber(plug)); + } else { + /* not ok- leave as a message. */ + plug->awaitingLoad=false; + plug->entrypoint=0; + plug->dontUnload=true; + } +} + +static void uplug_doUnloadPlug(UPlugData *plugToRemove, UErrorCode *status) { + if(plugToRemove != nullptr) { + uplug_unloadPlug(plugToRemove, status); + uplug_deallocatePlug(plugToRemove, status); + } +} + +U_CAPI void U_EXPORT2 +uplug_removePlug(UPlugData *plug, UErrorCode *status) { + UPlugData *cursor = nullptr; + UPlugData *plugToRemove = nullptr; + if(U_FAILURE(*status)) return; + + for(cursor=pluginList;cursor!=nullptr;) { + if(cursor==plug) { + plugToRemove = plug; + cursor=nullptr; + } else { + cursor = uplug_nextPlug(cursor); + } + } + + uplug_doUnloadPlug(plugToRemove, status); +} + + + + +U_CAPI void U_EXPORT2 +uplug_setPlugNoUnload(UPlugData *data, UBool dontUnload) +{ + data->dontUnload = dontUnload; +} + + +U_CAPI void U_EXPORT2 +uplug_setPlugLevel(UPlugData *data, UPlugLevel level) { + data->level = level; +} + + +U_CAPI UPlugLevel U_EXPORT2 +uplug_getPlugLevel(UPlugData *data) { + return data->level; +} + + +U_CAPI void U_EXPORT2 +uplug_setPlugName(UPlugData *data, const char *name) { + uprv_strncpy(data->name, name, UPLUG_NAME_MAX); +} + + +U_CAPI const char * U_EXPORT2 +uplug_getPlugName(UPlugData *data) { + return data->name; +} + + +U_CAPI const char * U_EXPORT2 +uplug_getSymbolName(UPlugData *data) { + return data->sym; +} + +U_CAPI const char * U_EXPORT2 +uplug_getLibraryName(UPlugData *data, UErrorCode *status) { + if(data->libName[0]) { + return data->libName; + } else { +#if U_ENABLE_DYLOAD + return uplug_findLibrary(data->lib, status); +#else + return nullptr; +#endif + } +} + +U_CAPI void * U_EXPORT2 +uplug_getLibrary(UPlugData *data) { + return data->lib; +} + +U_CAPI void * U_EXPORT2 +uplug_getContext(UPlugData *data) { + return data->context; +} + + +U_CAPI void U_EXPORT2 +uplug_setContext(UPlugData *data, void *context) { + data->context = context; +} + +U_CAPI const char* U_EXPORT2 +uplug_getConfiguration(UPlugData *data) { + return data->config; +} + +U_CAPI UPlugData* U_EXPORT2 +uplug_getPlugInternal(int32_t n) { + if(n <0 || n >= pluginCount) { + return nullptr; + } else { + return &(pluginList[n]); + } +} + + +U_CAPI UErrorCode U_EXPORT2 +uplug_getPlugLoadStatus(UPlugData *plug) { + return plug->pluginStatus; +} + + + + +/** + * Initialize a plugin from an entrypoint and library - but don't load it. + */ +static UPlugData* uplug_initPlugFromEntrypointAndLibrary(UPlugEntrypoint *entrypoint, const char *config, void *lib, const char *sym, + UErrorCode *status) { + UPlugData *plug = nullptr; + + plug = uplug_allocatePlug(entrypoint, config, lib, sym, status); + + if(U_SUCCESS(*status)) { + return plug; + } else { + uplug_deallocatePlug(plug, status); + return nullptr; + } +} + +U_CAPI UPlugData* U_EXPORT2 +uplug_loadPlugFromEntrypoint(UPlugEntrypoint *entrypoint, const char *config, UErrorCode *status) { + UPlugData* plug = uplug_initPlugFromEntrypointAndLibrary(entrypoint, config, nullptr, nullptr, status); + uplug_loadPlug(plug, status); + return plug; +} + +#if U_ENABLE_DYLOAD + +static UPlugData* +uplug_initErrorPlug(const char *libName, const char *sym, const char *config, const char *nameOrError, UErrorCode loadStatus, UErrorCode *status) +{ + UPlugData *plug = uplug_allocateEmptyPlug(status); + if(U_FAILURE(*status)) return nullptr; + + plug->pluginStatus = loadStatus; + plug->awaitingLoad = false; /* Won't load. */ + plug->dontUnload = true; /* cannot unload. */ + + if(sym!=nullptr) { + uprv_strncpy(plug->sym, sym, UPLUG_NAME_MAX); + } + + if(libName!=nullptr) { + uprv_strncpy(plug->libName, libName, UPLUG_NAME_MAX); + } + + if(nameOrError!=nullptr) { + uprv_strncpy(plug->name, nameOrError, UPLUG_NAME_MAX); + } + + if(config!=nullptr) { + uprv_strncpy(plug->config, config, UPLUG_NAME_MAX); + } + + return plug; +} + +/** + * Fetch a plugin from DLL, and then initialize it from a library- but don't load it. + */ +static UPlugData* +uplug_initPlugFromLibrary(const char *libName, const char *sym, const char *config, UErrorCode *status) { + void *lib = nullptr; + UPlugData *plug = nullptr; + if(U_FAILURE(*status)) { return nullptr; } + lib = uplug_openLibrary(libName, status); + if(lib!=nullptr && U_SUCCESS(*status)) { + UPlugEntrypoint *entrypoint = nullptr; + entrypoint = (UPlugEntrypoint*)uprv_dlsym_func(lib, sym, status); + + if(entrypoint!=nullptr&&U_SUCCESS(*status)) { + plug = uplug_initPlugFromEntrypointAndLibrary(entrypoint, config, lib, sym, status); + if(plug!=nullptr&&U_SUCCESS(*status)) { + plug->lib = lib; /* plug takes ownership of library */ + lib = nullptr; /* library is now owned by plugin. */ + } + } else { + UErrorCode subStatus = U_ZERO_ERROR; + plug = uplug_initErrorPlug(libName,sym,config,"ERROR: Could not load entrypoint",(lib==nullptr)?U_MISSING_RESOURCE_ERROR:*status,&subStatus); + } + if(lib!=nullptr) { /* still need to close the lib */ + UErrorCode subStatus = U_ZERO_ERROR; + uplug_closeLibrary(lib, &subStatus); /* don't care here */ + } + } else { + UErrorCode subStatus = U_ZERO_ERROR; + plug = uplug_initErrorPlug(libName,sym,config,"ERROR: could not load library",(lib==nullptr)?U_MISSING_RESOURCE_ERROR:*status,&subStatus); + } + return plug; +} + +U_CAPI UPlugData* U_EXPORT2 +uplug_loadPlugFromLibrary(const char *libName, const char *sym, const char *config, UErrorCode *status) { + UPlugData *plug = nullptr; + if(U_FAILURE(*status)) { return nullptr; } + plug = uplug_initPlugFromLibrary(libName, sym, config, status); + uplug_loadPlug(plug, status); + + return plug; +} + +#endif + +static UPlugLevel gCurrentLevel = UPLUG_LEVEL_LOW; + +U_CAPI UPlugLevel U_EXPORT2 uplug_getCurrentLevel() { + return gCurrentLevel; +} + +static UBool U_CALLCONV uplug_cleanup() +{ + int32_t i; + + UPlugData *pluginToRemove; + /* cleanup plugs */ + for(i=0;iawaitingLoad) { + if(pluginToLoad->level == UPLUG_LEVEL_LOW) { + if(currentLevel > UPLUG_LEVEL_LOW) { + pluginToLoad->pluginStatus = U_PLUGIN_TOO_HIGH; + } else { + UPlugLevel newLevel; + uplug_loadPlug(pluginToLoad, &subStatus); + newLevel = uplug_getCurrentLevel(); + if(newLevel > currentLevel) { + pluginToLoad->pluginStatus = U_PLUGIN_CHANGED_LEVEL_WARNING; + currentLevel = newLevel; + } + } + pluginToLoad->awaitingLoad = false; + } + } + } + for(i=0;iawaitingLoad) { + if(pluginToLoad->level == UPLUG_LEVEL_INVALID) { + pluginToLoad->pluginStatus = U_PLUGIN_DIDNT_SET_LEVEL; + } else if(pluginToLoad->level == UPLUG_LEVEL_UNKNOWN) { + pluginToLoad->pluginStatus = U_INTERNAL_PROGRAM_ERROR; + } else { + uplug_loadPlug(pluginToLoad, &subStatus); + } + pluginToLoad->awaitingLoad = false; + } + } + +#if UPLUG_TRACE + DBG((stderr, " Done Loading Plugs. Level: %d\n", (int32_t)uplug_getCurrentLevel())); +#endif +} + +/* Name of the plugin config file */ +static char plugin_file[2048] = ""; +#endif + +U_CAPI const char* U_EXPORT2 +uplug_getPluginFile() { +#if U_ENABLE_DYLOAD && !UCONFIG_NO_FILE_IO + return plugin_file; +#else + return nullptr; +#endif +} + + +// uplug_init() is called first thing from u_init(). + +U_CAPI void U_EXPORT2 +uplug_init(UErrorCode *status) { +#if !U_ENABLE_DYLOAD + (void)status; /* unused */ +#elif !UCONFIG_NO_FILE_IO + CharString plugin_dir; + const char *env = getenv("ICU_PLUGINS"); + + if(U_FAILURE(*status)) return; + if(env != nullptr) { + plugin_dir.append(env, -1, *status); + } + if(U_FAILURE(*status)) return; + +#if defined(DEFAULT_ICU_PLUGINS) + if(plugin_dir.isEmpty()) { + plugin_dir.append(DEFAULT_ICU_PLUGINS, -1, *status); + } +#endif + +#if UPLUG_TRACE + DBG((stderr, "ICU_PLUGINS=%s\n", plugin_dir.data())); +#endif + + if(!plugin_dir.isEmpty()) { + FILE *f; + + CharString pluginFile; +#ifdef OS390BATCH +/* There are potentially a lot of ways to implement a plugin directory on OS390/zOS */ +/* Keeping in mind that unauthorized file access is logged, monitored, and enforced */ +/* I've chosen to open a DDNAME if BATCH and leave it alone for (presumably) UNIX */ +/* System Services. Alternative techniques might be allocating a member in */ +/* SYS1.PARMLIB or setting an environment variable "ICU_PLUGIN_PATH" (?). The */ +/* DDNAME can be connected to a file in the HFS if need be. */ + + pluginFile.append("//DD:ICUPLUG", -1, *status); /* JAM 20 Oct 2011 */ +#else + pluginFile.append(plugin_dir, *status); + pluginFile.append(U_FILE_SEP_STRING, -1, *status); + pluginFile.append("icuplugins", -1, *status); + pluginFile.append(U_ICU_VERSION_SHORT, -1, *status); + pluginFile.append(".txt", -1, *status); +#endif + +#if UPLUG_TRACE + DBG((stderr, "status=%s\n", u_errorName(*status))); +#endif + + if(U_FAILURE(*status)) { + return; + } + if((size_t)pluginFile.length() > (sizeof(plugin_file)-1)) { + *status = U_BUFFER_OVERFLOW_ERROR; +#if UPLUG_TRACE + DBG((stderr, "status=%s\n", u_errorName(*status))); +#endif + return; + } + + /* plugin_file is not used for processing - it is only used + so that uplug_getPluginFile() works (i.e. icuinfo) + */ + pluginFile.extract(plugin_file, sizeof(plugin_file), *status); + +#if UPLUG_TRACE + DBG((stderr, "pluginfile= %s len %d/%d\n", plugin_file, (int)strlen(plugin_file), (int)sizeof(plugin_file))); +#endif + +#ifdef __MVS__ + if (iscics()) /* 12 Nov 2011 JAM */ + { + f = nullptr; + } + else +#endif + { + f = fopen(pluginFile.data(), "r"); + } + + if(f != nullptr) { + char linebuf[1024]; + char *p, *libName=nullptr, *symName=nullptr, *config=nullptr; + int32_t line = 0; + + + while(fgets(linebuf,1023,f)) { + line++; + + if(!*linebuf || *linebuf=='#') { + continue; + } else { + p = linebuf; + while(*p&&isspace((int)*p)) + p++; + if(!*p || *p=='#') continue; + libName = p; + while(*p&&!isspace((int)*p)) { + p++; + } + if(!*p || *p=='#') continue; /* no tab after libname */ + *p=0; /* end of libname */ + p++; + while(*p&&isspace((int)*p)) { + p++; + } + if(!*p||*p=='#') continue; /* no symname after libname +tab */ + symName = p; + while(*p&&!isspace((int)*p)) { + p++; + } + + if(*p) { /* has config */ + *p=0; + ++p; + while(*p&&isspace((int)*p)) { + p++; + } + if(*p) { + config = p; + } + } + + /* chop whitespace at the end of the config */ + if(config!=nullptr&&*config!=0) { + p = config+strlen(config); + while(p>config&&isspace((int)*(--p))) { + *p=0; + } + } + + /* OK, we're good. */ + { + UErrorCode subStatus = U_ZERO_ERROR; + UPlugData *plug = uplug_initPlugFromLibrary(libName, symName, config, &subStatus); + if(U_FAILURE(subStatus) && U_SUCCESS(*status)) { + *status = subStatus; + } +#if UPLUG_TRACE + DBG((stderr, "PLUGIN libName=[%s], sym=[%s], config=[%s]\n", libName, symName, config)); + DBG((stderr, " -> %p, %s\n", (void*)plug, u_errorName(subStatus))); +#else + (void)plug; /* unused */ +#endif + } + } + } + fclose(f); + } else { +#if UPLUG_TRACE + DBG((stderr, "Can't open plugin file %s\n", plugin_file)); +#endif + } + } + uplug_loadWaitingPlugs(status); +#endif /* U_ENABLE_DYLOAD */ + gCurrentLevel = UPLUG_LEVEL_HIGH; + ucln_registerCleanup(UCLN_UPLUG, uplug_cleanup); +} + +#endif + + diff --git a/deps/icu-small/source/common/icuplugimp.h b/deps/icu-small/source/common/icuplugimp.h index 9df309204e7a1d..daa0eb24335099 100644 --- a/deps/icu-small/source/common/icuplugimp.h +++ b/deps/icu-small/source/common/icuplugimp.h @@ -1,93 +1,93 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2009-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* FILE NAME : icuplugimp.h -* -* Internal functions for the ICU plugin system -* -* Date Name Description -* 10/29/2009 sl New. -****************************************************************************** -*/ - - -#ifndef ICUPLUGIMP_H -#define ICUPLUGIMP_H - -#include "unicode/icuplug.h" - -#if UCONFIG_ENABLE_PLUGINS - -/*========================*/ -/** @{ Library Manipulation - */ - -/** - * Open a library, adding a reference count if needed. - * @param libName library name to load - * @param status error code - * @return the library pointer, or NULL - * @internal internal use only - */ -U_CAPI void * U_EXPORT2 -uplug_openLibrary(const char *libName, UErrorCode *status); - -/** - * Close a library, if its reference count is 0 - * @param lib the library to close - * @param status error code - * @internal internal use only - */ -U_CAPI void U_EXPORT2 -uplug_closeLibrary(void *lib, UErrorCode *status); - -/** - * Get a library's name, or NULL if not found. - * @param lib the library's name - * @param status error code - * @return the library name, or NULL if not found. - * @internal internal use only - */ -U_CAPI char * U_EXPORT2 -uplug_findLibrary(void *lib, UErrorCode *status); - -/** @} */ - -/*========================*/ -/** {@ ICU Plugin internal interfaces - */ - -/** - * Initialize the plugins - * @param status error result - * @internal - Internal use only. - */ -U_CAPI void U_EXPORT2 -uplug_init(UErrorCode *status); - -/** - * Get raw plug N - * @internal - Internal use only - */ -U_CAPI UPlugData* U_EXPORT2 -uplug_getPlugInternal(int32_t n); - -/** - * Get the name of the plugin file. - * @internal - Internal use only. - */ -U_CAPI const char* U_EXPORT2 -uplug_getPluginFile(void); - -/** @} */ - -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2009-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* FILE NAME : icuplugimp.h +* +* Internal functions for the ICU plugin system +* +* Date Name Description +* 10/29/2009 sl New. +****************************************************************************** +*/ + + +#ifndef ICUPLUGIMP_H +#define ICUPLUGIMP_H + +#include "unicode/icuplug.h" + +#if UCONFIG_ENABLE_PLUGINS + +/*========================*/ +/** @{ Library Manipulation + */ + +/** + * Open a library, adding a reference count if needed. + * @param libName library name to load + * @param status error code + * @return the library pointer, or NULL + * @internal internal use only + */ +U_CAPI void * U_EXPORT2 +uplug_openLibrary(const char *libName, UErrorCode *status); + +/** + * Close a library, if its reference count is 0 + * @param lib the library to close + * @param status error code + * @internal internal use only + */ +U_CAPI void U_EXPORT2 +uplug_closeLibrary(void *lib, UErrorCode *status); + +/** + * Get a library's name, or NULL if not found. + * @param lib the library's name + * @param status error code + * @return the library name, or NULL if not found. + * @internal internal use only + */ +U_CAPI char * U_EXPORT2 +uplug_findLibrary(void *lib, UErrorCode *status); + +/** @} */ + +/*========================*/ +/** {@ ICU Plugin internal interfaces + */ + +/** + * Initialize the plugins + * @param status error result + * @internal - Internal use only. + */ +U_CAPI void U_EXPORT2 +uplug_init(UErrorCode *status); + +/** + * Get raw plug N + * @internal - Internal use only + */ +U_CAPI UPlugData* U_EXPORT2 +uplug_getPlugInternal(int32_t n); + +/** + * Get the name of the plugin file. + * @internal - Internal use only. + */ +U_CAPI const char* U_EXPORT2 +uplug_getPluginFile(void); + +/** @} */ + +#endif + +#endif diff --git a/deps/icu-small/source/common/loadednormalizer2impl.cpp b/deps/icu-small/source/common/loadednormalizer2impl.cpp index 24ff629f84f6f7..3faf97c28153a3 100644 --- a/deps/icu-small/source/common/loadednormalizer2impl.cpp +++ b/deps/icu-small/source/common/loadednormalizer2impl.cpp @@ -1,418 +1,418 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* loadednormalizer2impl.cpp -* -* created on: 2014sep03 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_NORMALIZATION - -#include "unicode/udata.h" -#include "unicode/localpointer.h" -#include "unicode/normalizer2.h" -#include "unicode/ucptrie.h" -#include "unicode/unistr.h" -#include "unicode/unorm.h" -#include "cstring.h" -#include "mutex.h" -#include "norm2allmodes.h" -#include "normalizer2impl.h" -#include "uassert.h" -#include "ucln_cmn.h" -#include "uhash.h" - -U_NAMESPACE_BEGIN - -class LoadedNormalizer2Impl : public Normalizer2Impl { -public: - LoadedNormalizer2Impl() : memory(NULL), ownedTrie(NULL) {} - virtual ~LoadedNormalizer2Impl(); - - void load(const char *packageName, const char *name, UErrorCode &errorCode); - -private: - static UBool U_CALLCONV - isAcceptable(void *context, const char *type, const char *name, const UDataInfo *pInfo); - - UDataMemory *memory; - UCPTrie *ownedTrie; -}; - -LoadedNormalizer2Impl::~LoadedNormalizer2Impl() { - udata_close(memory); - ucptrie_close(ownedTrie); -} - -UBool U_CALLCONV -LoadedNormalizer2Impl::isAcceptable(void * /*context*/, - const char * /* type */, const char * /*name*/, - const UDataInfo *pInfo) { - if( - pInfo->size>=20 && - pInfo->isBigEndian==U_IS_BIG_ENDIAN && - pInfo->charsetFamily==U_CHARSET_FAMILY && - pInfo->dataFormat[0]==0x4e && /* dataFormat="Nrm2" */ - pInfo->dataFormat[1]==0x72 && - pInfo->dataFormat[2]==0x6d && - pInfo->dataFormat[3]==0x32 && - pInfo->formatVersion[0]==4 - ) { - // Normalizer2Impl *me=(Normalizer2Impl *)context; - // uprv_memcpy(me->dataVersion, pInfo->dataVersion, 4); - return true; - } else { - return false; - } -} - -void -LoadedNormalizer2Impl::load(const char *packageName, const char *name, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return; - } - memory=udata_openChoice(packageName, "nrm", name, isAcceptable, this, &errorCode); - if(U_FAILURE(errorCode)) { - return; - } - const uint8_t *inBytes=(const uint8_t *)udata_getMemory(memory); - const int32_t *inIndexes=(const int32_t *)inBytes; - int32_t indexesLength=inIndexes[IX_NORM_TRIE_OFFSET]/4; - if(indexesLength<=IX_MIN_LCCC_CP) { - errorCode=U_INVALID_FORMAT_ERROR; // Not enough indexes. - return; - } - - int32_t offset=inIndexes[IX_NORM_TRIE_OFFSET]; - int32_t nextOffset=inIndexes[IX_EXTRA_DATA_OFFSET]; - ownedTrie=ucptrie_openFromBinary(UCPTRIE_TYPE_FAST, UCPTRIE_VALUE_BITS_16, - inBytes+offset, nextOffset-offset, NULL, - &errorCode); - if(U_FAILURE(errorCode)) { - return; - } - - offset=nextOffset; - nextOffset=inIndexes[IX_SMALL_FCD_OFFSET]; - const uint16_t *inExtraData=(const uint16_t *)(inBytes+offset); - - // smallFCD: new in formatVersion 2 - offset=nextOffset; - const uint8_t *inSmallFCD=inBytes+offset; - - init(inIndexes, ownedTrie, inExtraData, inSmallFCD); -} - -// instance cache ---------------------------------------------------------- *** - -Norm2AllModes * -Norm2AllModes::createInstance(const char *packageName, - const char *name, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return NULL; - } - LoadedNormalizer2Impl *impl=new LoadedNormalizer2Impl; - if(impl==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - impl->load(packageName, name, errorCode); - return createInstance(impl, errorCode); -} - -U_CDECL_BEGIN -static UBool U_CALLCONV uprv_loaded_normalizer2_cleanup(); -U_CDECL_END - -#if !NORM2_HARDCODE_NFC_DATA -static Norm2AllModes *nfcSingleton; -static icu::UInitOnce nfcInitOnce {}; -#endif - -static Norm2AllModes *nfkcSingleton; -static icu::UInitOnce nfkcInitOnce {}; - -static Norm2AllModes *nfkc_cfSingleton; -static icu::UInitOnce nfkc_cfInitOnce {}; - -static UHashtable *cache=NULL; - -// UInitOnce singleton initialization function -static void U_CALLCONV initSingletons(const char *what, UErrorCode &errorCode) { -#if !NORM2_HARDCODE_NFC_DATA - if (uprv_strcmp(what, "nfc") == 0) { - nfcSingleton = Norm2AllModes::createInstance(NULL, "nfc", errorCode); - } else -#endif - if (uprv_strcmp(what, "nfkc") == 0) { - nfkcSingleton = Norm2AllModes::createInstance(NULL, "nfkc", errorCode); - } else if (uprv_strcmp(what, "nfkc_cf") == 0) { - nfkc_cfSingleton = Norm2AllModes::createInstance(NULL, "nfkc_cf", errorCode); - } else { - UPRV_UNREACHABLE_EXIT; // Unknown singleton - } - ucln_common_registerCleanup(UCLN_COMMON_LOADED_NORMALIZER2, uprv_loaded_normalizer2_cleanup); -} - -U_CDECL_BEGIN - -static void U_CALLCONV deleteNorm2AllModes(void *allModes) { - delete (Norm2AllModes *)allModes; -} - -static UBool U_CALLCONV uprv_loaded_normalizer2_cleanup() { -#if !NORM2_HARDCODE_NFC_DATA - delete nfcSingleton; - nfcSingleton = NULL; - nfcInitOnce.reset(); -#endif - - delete nfkcSingleton; - nfkcSingleton = NULL; - nfkcInitOnce.reset(); - - delete nfkc_cfSingleton; - nfkc_cfSingleton = NULL; - nfkc_cfInitOnce.reset(); - - uhash_close(cache); - cache=NULL; - return true; -} - -U_CDECL_END - -#if !NORM2_HARDCODE_NFC_DATA -const Norm2AllModes * -Norm2AllModes::getNFCInstance(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return NULL; } - umtx_initOnce(nfcInitOnce, &initSingletons, "nfc", errorCode); - return nfcSingleton; -} -#endif - -const Norm2AllModes * -Norm2AllModes::getNFKCInstance(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return NULL; } - umtx_initOnce(nfkcInitOnce, &initSingletons, "nfkc", errorCode); - return nfkcSingleton; -} - -const Norm2AllModes * -Norm2AllModes::getNFKC_CFInstance(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return NULL; } - umtx_initOnce(nfkc_cfInitOnce, &initSingletons, "nfkc_cf", errorCode); - return nfkc_cfSingleton; -} - -#if !NORM2_HARDCODE_NFC_DATA -const Normalizer2 * -Normalizer2::getNFCInstance(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); - return allModes!=NULL ? &allModes->comp : NULL; -} - -const Normalizer2 * -Normalizer2::getNFDInstance(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); - return allModes!=NULL ? &allModes->decomp : NULL; -} - -const Normalizer2 *Normalizer2Factory::getFCDInstance(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); - return allModes!=NULL ? &allModes->fcd : NULL; -} - -const Normalizer2 *Normalizer2Factory::getFCCInstance(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); - return allModes!=NULL ? &allModes->fcc : NULL; -} - -const Normalizer2Impl * -Normalizer2Factory::getNFCImpl(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); - return allModes!=NULL ? allModes->impl : NULL; -} -#endif - -const Normalizer2 * -Normalizer2::getNFKCInstance(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFKCInstance(errorCode); - return allModes!=NULL ? &allModes->comp : NULL; -} - -const Normalizer2 * -Normalizer2::getNFKDInstance(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFKCInstance(errorCode); - return allModes!=NULL ? &allModes->decomp : NULL; -} - -const Normalizer2 * -Normalizer2::getNFKCCasefoldInstance(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFKC_CFInstance(errorCode); - return allModes!=NULL ? &allModes->comp : NULL; -} - -const Normalizer2 * -Normalizer2::getInstance(const char *packageName, - const char *name, - UNormalization2Mode mode, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return NULL; - } - if(name==NULL || *name==0) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - const Norm2AllModes *allModes=NULL; - if(packageName==NULL) { - if(0==uprv_strcmp(name, "nfc")) { - allModes=Norm2AllModes::getNFCInstance(errorCode); - } else if(0==uprv_strcmp(name, "nfkc")) { - allModes=Norm2AllModes::getNFKCInstance(errorCode); - } else if(0==uprv_strcmp(name, "nfkc_cf")) { - allModes=Norm2AllModes::getNFKC_CFInstance(errorCode); - } - } - if(allModes==NULL && U_SUCCESS(errorCode)) { - { - Mutex lock; - if(cache!=NULL) { - allModes=(Norm2AllModes *)uhash_get(cache, name); - } - } - if(allModes==NULL) { - ucln_common_registerCleanup(UCLN_COMMON_LOADED_NORMALIZER2, uprv_loaded_normalizer2_cleanup); - LocalPointer localAllModes( - Norm2AllModes::createInstance(packageName, name, errorCode)); - if(U_SUCCESS(errorCode)) { - Mutex lock; - if(cache==NULL) { - cache=uhash_open(uhash_hashChars, uhash_compareChars, NULL, &errorCode); - if(U_FAILURE(errorCode)) { - return NULL; - } - uhash_setKeyDeleter(cache, uprv_free); - uhash_setValueDeleter(cache, deleteNorm2AllModes); - } - void *temp=uhash_get(cache, name); - if(temp==NULL) { - int32_t keyLength= static_cast(uprv_strlen(name)+1); - char *nameCopy=(char *)uprv_malloc(keyLength); - if(nameCopy==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uprv_memcpy(nameCopy, name, keyLength); - allModes=localAllModes.getAlias(); - uhash_put(cache, nameCopy, localAllModes.orphan(), &errorCode); - } else { - // race condition - allModes=(Norm2AllModes *)temp; - } - } - } - } - if(allModes!=NULL && U_SUCCESS(errorCode)) { - switch(mode) { - case UNORM2_COMPOSE: - return &allModes->comp; - case UNORM2_DECOMPOSE: - return &allModes->decomp; - case UNORM2_FCD: - return &allModes->fcd; - case UNORM2_COMPOSE_CONTIGUOUS: - return &allModes->fcc; - default: - break; // do nothing - } - } - return NULL; -} - -const Normalizer2 * -Normalizer2Factory::getInstance(UNormalizationMode mode, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return NULL; - } - switch(mode) { - case UNORM_NFD: - return Normalizer2::getNFDInstance(errorCode); - case UNORM_NFKD: - return Normalizer2::getNFKDInstance(errorCode); - case UNORM_NFC: - return Normalizer2::getNFCInstance(errorCode); - case UNORM_NFKC: - return Normalizer2::getNFKCInstance(errorCode); - case UNORM_FCD: - return getFCDInstance(errorCode); - default: // UNORM_NONE - return getNoopInstance(errorCode); - } -} - -const Normalizer2Impl * -Normalizer2Factory::getNFKCImpl(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFKCInstance(errorCode); - return allModes!=NULL ? allModes->impl : NULL; -} - -const Normalizer2Impl * -Normalizer2Factory::getNFKC_CFImpl(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFKC_CFInstance(errorCode); - return allModes!=NULL ? allModes->impl : NULL; -} - -U_NAMESPACE_END - -// C API ------------------------------------------------------------------- *** - -U_NAMESPACE_USE - -U_CAPI const UNormalizer2 * U_EXPORT2 -unorm2_getNFKCInstance(UErrorCode *pErrorCode) { - return (const UNormalizer2 *)Normalizer2::getNFKCInstance(*pErrorCode); -} - -U_CAPI const UNormalizer2 * U_EXPORT2 -unorm2_getNFKDInstance(UErrorCode *pErrorCode) { - return (const UNormalizer2 *)Normalizer2::getNFKDInstance(*pErrorCode); -} - -U_CAPI const UNormalizer2 * U_EXPORT2 -unorm2_getNFKCCasefoldInstance(UErrorCode *pErrorCode) { - return (const UNormalizer2 *)Normalizer2::getNFKCCasefoldInstance(*pErrorCode); -} - -U_CAPI const UNormalizer2 * U_EXPORT2 -unorm2_getInstance(const char *packageName, - const char *name, - UNormalization2Mode mode, - UErrorCode *pErrorCode) { - return (const UNormalizer2 *)Normalizer2::getInstance(packageName, name, mode, *pErrorCode); -} - -U_CFUNC UNormalizationCheckResult -unorm_getQuickCheck(UChar32 c, UNormalizationMode mode) { - if(mode<=UNORM_NONE || UNORM_FCD<=mode) { - return UNORM_YES; - } - UErrorCode errorCode=U_ZERO_ERROR; - const Normalizer2 *norm2=Normalizer2Factory::getInstance(mode, errorCode); - if(U_SUCCESS(errorCode)) { - return ((const Normalizer2WithImpl *)norm2)->getQuickCheck(c); - } else { - return UNORM_MAYBE; - } -} - -#endif // !UCONFIG_NO_NORMALIZATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* loadednormalizer2impl.cpp +* +* created on: 2014sep03 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_NORMALIZATION + +#include "unicode/udata.h" +#include "unicode/localpointer.h" +#include "unicode/normalizer2.h" +#include "unicode/ucptrie.h" +#include "unicode/unistr.h" +#include "unicode/unorm.h" +#include "cstring.h" +#include "mutex.h" +#include "norm2allmodes.h" +#include "normalizer2impl.h" +#include "uassert.h" +#include "ucln_cmn.h" +#include "uhash.h" + +U_NAMESPACE_BEGIN + +class LoadedNormalizer2Impl : public Normalizer2Impl { +public: + LoadedNormalizer2Impl() : memory(nullptr), ownedTrie(nullptr) {} + virtual ~LoadedNormalizer2Impl(); + + void load(const char *packageName, const char *name, UErrorCode &errorCode); + +private: + static UBool U_CALLCONV + isAcceptable(void *context, const char *type, const char *name, const UDataInfo *pInfo); + + UDataMemory *memory; + UCPTrie *ownedTrie; +}; + +LoadedNormalizer2Impl::~LoadedNormalizer2Impl() { + udata_close(memory); + ucptrie_close(ownedTrie); +} + +UBool U_CALLCONV +LoadedNormalizer2Impl::isAcceptable(void * /*context*/, + const char * /* type */, const char * /*name*/, + const UDataInfo *pInfo) { + if( + pInfo->size>=20 && + pInfo->isBigEndian==U_IS_BIG_ENDIAN && + pInfo->charsetFamily==U_CHARSET_FAMILY && + pInfo->dataFormat[0]==0x4e && /* dataFormat="Nrm2" */ + pInfo->dataFormat[1]==0x72 && + pInfo->dataFormat[2]==0x6d && + pInfo->dataFormat[3]==0x32 && + pInfo->formatVersion[0]==4 + ) { + // Normalizer2Impl *me=(Normalizer2Impl *)context; + // uprv_memcpy(me->dataVersion, pInfo->dataVersion, 4); + return true; + } else { + return false; + } +} + +void +LoadedNormalizer2Impl::load(const char *packageName, const char *name, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return; + } + memory=udata_openChoice(packageName, "nrm", name, isAcceptable, this, &errorCode); + if(U_FAILURE(errorCode)) { + return; + } + const uint8_t *inBytes=(const uint8_t *)udata_getMemory(memory); + const int32_t *inIndexes=(const int32_t *)inBytes; + int32_t indexesLength=inIndexes[IX_NORM_TRIE_OFFSET]/4; + if(indexesLength<=IX_MIN_LCCC_CP) { + errorCode=U_INVALID_FORMAT_ERROR; // Not enough indexes. + return; + } + + int32_t offset=inIndexes[IX_NORM_TRIE_OFFSET]; + int32_t nextOffset=inIndexes[IX_EXTRA_DATA_OFFSET]; + ownedTrie=ucptrie_openFromBinary(UCPTRIE_TYPE_FAST, UCPTRIE_VALUE_BITS_16, + inBytes+offset, nextOffset-offset, nullptr, + &errorCode); + if(U_FAILURE(errorCode)) { + return; + } + + offset=nextOffset; + nextOffset=inIndexes[IX_SMALL_FCD_OFFSET]; + const uint16_t *inExtraData=(const uint16_t *)(inBytes+offset); + + // smallFCD: new in formatVersion 2 + offset=nextOffset; + const uint8_t *inSmallFCD=inBytes+offset; + + init(inIndexes, ownedTrie, inExtraData, inSmallFCD); +} + +// instance cache ---------------------------------------------------------- *** + +Norm2AllModes * +Norm2AllModes::createInstance(const char *packageName, + const char *name, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return nullptr; + } + LoadedNormalizer2Impl *impl=new LoadedNormalizer2Impl; + if(impl==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + impl->load(packageName, name, errorCode); + return createInstance(impl, errorCode); +} + +U_CDECL_BEGIN +static UBool U_CALLCONV uprv_loaded_normalizer2_cleanup(); +U_CDECL_END + +#if !NORM2_HARDCODE_NFC_DATA +static Norm2AllModes *nfcSingleton; +static icu::UInitOnce nfcInitOnce {}; +#endif + +static Norm2AllModes *nfkcSingleton; +static icu::UInitOnce nfkcInitOnce {}; + +static Norm2AllModes *nfkc_cfSingleton; +static icu::UInitOnce nfkc_cfInitOnce {}; + +static UHashtable *cache=nullptr; + +// UInitOnce singleton initialization function +static void U_CALLCONV initSingletons(const char *what, UErrorCode &errorCode) { +#if !NORM2_HARDCODE_NFC_DATA + if (uprv_strcmp(what, "nfc") == 0) { + nfcSingleton = Norm2AllModes::createInstance(nullptr, "nfc", errorCode); + } else +#endif + if (uprv_strcmp(what, "nfkc") == 0) { + nfkcSingleton = Norm2AllModes::createInstance(nullptr, "nfkc", errorCode); + } else if (uprv_strcmp(what, "nfkc_cf") == 0) { + nfkc_cfSingleton = Norm2AllModes::createInstance(nullptr, "nfkc_cf", errorCode); + } else { + UPRV_UNREACHABLE_EXIT; // Unknown singleton + } + ucln_common_registerCleanup(UCLN_COMMON_LOADED_NORMALIZER2, uprv_loaded_normalizer2_cleanup); +} + +U_CDECL_BEGIN + +static void U_CALLCONV deleteNorm2AllModes(void *allModes) { + delete (Norm2AllModes *)allModes; +} + +static UBool U_CALLCONV uprv_loaded_normalizer2_cleanup() { +#if !NORM2_HARDCODE_NFC_DATA + delete nfcSingleton; + nfcSingleton = nullptr; + nfcInitOnce.reset(); +#endif + + delete nfkcSingleton; + nfkcSingleton = nullptr; + nfkcInitOnce.reset(); + + delete nfkc_cfSingleton; + nfkc_cfSingleton = nullptr; + nfkc_cfInitOnce.reset(); + + uhash_close(cache); + cache=nullptr; + return true; +} + +U_CDECL_END + +#if !NORM2_HARDCODE_NFC_DATA +const Norm2AllModes * +Norm2AllModes::getNFCInstance(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return nullptr; } + umtx_initOnce(nfcInitOnce, &initSingletons, "nfc", errorCode); + return nfcSingleton; +} +#endif + +const Norm2AllModes * +Norm2AllModes::getNFKCInstance(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return nullptr; } + umtx_initOnce(nfkcInitOnce, &initSingletons, "nfkc", errorCode); + return nfkcSingleton; +} + +const Norm2AllModes * +Norm2AllModes::getNFKC_CFInstance(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return nullptr; } + umtx_initOnce(nfkc_cfInitOnce, &initSingletons, "nfkc_cf", errorCode); + return nfkc_cfSingleton; +} + +#if !NORM2_HARDCODE_NFC_DATA +const Normalizer2 * +Normalizer2::getNFCInstance(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); + return allModes!=nullptr ? &allModes->comp : nullptr; +} + +const Normalizer2 * +Normalizer2::getNFDInstance(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); + return allModes!=nullptr ? &allModes->decomp : nullptr; +} + +const Normalizer2 *Normalizer2Factory::getFCDInstance(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); + return allModes!=nullptr ? &allModes->fcd : nullptr; +} + +const Normalizer2 *Normalizer2Factory::getFCCInstance(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); + return allModes!=nullptr ? &allModes->fcc : nullptr; +} + +const Normalizer2Impl * +Normalizer2Factory::getNFCImpl(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); + return allModes!=nullptr ? allModes->impl : nullptr; +} +#endif + +const Normalizer2 * +Normalizer2::getNFKCInstance(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFKCInstance(errorCode); + return allModes!=nullptr ? &allModes->comp : nullptr; +} + +const Normalizer2 * +Normalizer2::getNFKDInstance(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFKCInstance(errorCode); + return allModes!=nullptr ? &allModes->decomp : nullptr; +} + +const Normalizer2 * +Normalizer2::getNFKCCasefoldInstance(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFKC_CFInstance(errorCode); + return allModes!=nullptr ? &allModes->comp : nullptr; +} + +const Normalizer2 * +Normalizer2::getInstance(const char *packageName, + const char *name, + UNormalization2Mode mode, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return nullptr; + } + if(name==nullptr || *name==0) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + const Norm2AllModes *allModes=nullptr; + if(packageName==nullptr) { + if(0==uprv_strcmp(name, "nfc")) { + allModes=Norm2AllModes::getNFCInstance(errorCode); + } else if(0==uprv_strcmp(name, "nfkc")) { + allModes=Norm2AllModes::getNFKCInstance(errorCode); + } else if(0==uprv_strcmp(name, "nfkc_cf")) { + allModes=Norm2AllModes::getNFKC_CFInstance(errorCode); + } + } + if(allModes==nullptr && U_SUCCESS(errorCode)) { + { + Mutex lock; + if(cache!=nullptr) { + allModes=(Norm2AllModes *)uhash_get(cache, name); + } + } + if(allModes==nullptr) { + ucln_common_registerCleanup(UCLN_COMMON_LOADED_NORMALIZER2, uprv_loaded_normalizer2_cleanup); + LocalPointer localAllModes( + Norm2AllModes::createInstance(packageName, name, errorCode)); + if(U_SUCCESS(errorCode)) { + Mutex lock; + if(cache==nullptr) { + cache=uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &errorCode); + if(U_FAILURE(errorCode)) { + return nullptr; + } + uhash_setKeyDeleter(cache, uprv_free); + uhash_setValueDeleter(cache, deleteNorm2AllModes); + } + void *temp=uhash_get(cache, name); + if(temp==nullptr) { + int32_t keyLength= static_cast(uprv_strlen(name)+1); + char *nameCopy=(char *)uprv_malloc(keyLength); + if(nameCopy==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memcpy(nameCopy, name, keyLength); + allModes=localAllModes.getAlias(); + uhash_put(cache, nameCopy, localAllModes.orphan(), &errorCode); + } else { + // race condition + allModes=(Norm2AllModes *)temp; + } + } + } + } + if(allModes!=nullptr && U_SUCCESS(errorCode)) { + switch(mode) { + case UNORM2_COMPOSE: + return &allModes->comp; + case UNORM2_DECOMPOSE: + return &allModes->decomp; + case UNORM2_FCD: + return &allModes->fcd; + case UNORM2_COMPOSE_CONTIGUOUS: + return &allModes->fcc; + default: + break; // do nothing + } + } + return nullptr; +} + +const Normalizer2 * +Normalizer2Factory::getInstance(UNormalizationMode mode, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return nullptr; + } + switch(mode) { + case UNORM_NFD: + return Normalizer2::getNFDInstance(errorCode); + case UNORM_NFKD: + return Normalizer2::getNFKDInstance(errorCode); + case UNORM_NFC: + return Normalizer2::getNFCInstance(errorCode); + case UNORM_NFKC: + return Normalizer2::getNFKCInstance(errorCode); + case UNORM_FCD: + return getFCDInstance(errorCode); + default: // UNORM_NONE + return getNoopInstance(errorCode); + } +} + +const Normalizer2Impl * +Normalizer2Factory::getNFKCImpl(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFKCInstance(errorCode); + return allModes!=nullptr ? allModes->impl : nullptr; +} + +const Normalizer2Impl * +Normalizer2Factory::getNFKC_CFImpl(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFKC_CFInstance(errorCode); + return allModes!=nullptr ? allModes->impl : nullptr; +} + +U_NAMESPACE_END + +// C API ------------------------------------------------------------------- *** + +U_NAMESPACE_USE + +U_CAPI const UNormalizer2 * U_EXPORT2 +unorm2_getNFKCInstance(UErrorCode *pErrorCode) { + return (const UNormalizer2 *)Normalizer2::getNFKCInstance(*pErrorCode); +} + +U_CAPI const UNormalizer2 * U_EXPORT2 +unorm2_getNFKDInstance(UErrorCode *pErrorCode) { + return (const UNormalizer2 *)Normalizer2::getNFKDInstance(*pErrorCode); +} + +U_CAPI const UNormalizer2 * U_EXPORT2 +unorm2_getNFKCCasefoldInstance(UErrorCode *pErrorCode) { + return (const UNormalizer2 *)Normalizer2::getNFKCCasefoldInstance(*pErrorCode); +} + +U_CAPI const UNormalizer2 * U_EXPORT2 +unorm2_getInstance(const char *packageName, + const char *name, + UNormalization2Mode mode, + UErrorCode *pErrorCode) { + return (const UNormalizer2 *)Normalizer2::getInstance(packageName, name, mode, *pErrorCode); +} + +U_CFUNC UNormalizationCheckResult +unorm_getQuickCheck(UChar32 c, UNormalizationMode mode) { + if(mode<=UNORM_NONE || UNORM_FCD<=mode) { + return UNORM_YES; + } + UErrorCode errorCode=U_ZERO_ERROR; + const Normalizer2 *norm2=Normalizer2Factory::getInstance(mode, errorCode); + if(U_SUCCESS(errorCode)) { + return ((const Normalizer2WithImpl *)norm2)->getQuickCheck(c); + } else { + return UNORM_MAYBE; + } +} + +#endif // !UCONFIG_NO_NORMALIZATION diff --git a/deps/icu-small/source/common/localebuilder.cpp b/deps/icu-small/source/common/localebuilder.cpp index c1e1f2ad682a87..0782738982f78c 100644 --- a/deps/icu-small/source/common/localebuilder.cpp +++ b/deps/icu-small/source/common/localebuilder.cpp @@ -1,468 +1,468 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#include - -#include "bytesinkutil.h" // CharStringByteSink -#include "charstr.h" -#include "cstring.h" -#include "ulocimp.h" -#include "unicode/localebuilder.h" -#include "unicode/locid.h" - -U_NAMESPACE_BEGIN - -#define UPRV_ISDIGIT(c) (((c) >= '0') && ((c) <= '9')) -#define UPRV_ISALPHANUM(c) (uprv_isASCIILetter(c) || UPRV_ISDIGIT(c) ) - -constexpr const char* kAttributeKey = "attribute"; - -static bool _isExtensionSubtags(char key, const char* s, int32_t len) { - switch (uprv_tolower(key)) { - case 'u': - return ultag_isUnicodeExtensionSubtags(s, len); - case 't': - return ultag_isTransformedExtensionSubtags(s, len); - case 'x': - return ultag_isPrivateuseValueSubtags(s, len); - default: - return ultag_isExtensionSubtags(s, len); - } -} - -LocaleBuilder::LocaleBuilder() : UObject(), status_(U_ZERO_ERROR), language_(), - script_(), region_(), variant_(nullptr), extensions_(nullptr) -{ - language_[0] = 0; - script_[0] = 0; - region_[0] = 0; -} - -LocaleBuilder::~LocaleBuilder() -{ - delete variant_; - delete extensions_; -} - -LocaleBuilder& LocaleBuilder::setLocale(const Locale& locale) -{ - clear(); - setLanguage(locale.getLanguage()); - setScript(locale.getScript()); - setRegion(locale.getCountry()); - setVariant(locale.getVariant()); - extensions_ = locale.clone(); - if (extensions_ == nullptr) { - status_ = U_MEMORY_ALLOCATION_ERROR; - } - return *this; -} - -LocaleBuilder& LocaleBuilder::setLanguageTag(StringPiece tag) -{ - Locale l = Locale::forLanguageTag(tag, status_); - if (U_FAILURE(status_)) { return *this; } - // Because setLocale will reset status_ we need to return - // first if we have error in forLanguageTag. - setLocale(l); - return *this; -} - -static void setField(StringPiece input, char* dest, UErrorCode& errorCode, - UBool (*test)(const char*, int32_t)) { - if (U_FAILURE(errorCode)) { return; } - if (input.empty()) { - dest[0] = '\0'; - } else if (test(input.data(), input.length())) { - uprv_memcpy(dest, input.data(), input.length()); - dest[input.length()] = '\0'; - } else { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - } -} - -LocaleBuilder& LocaleBuilder::setLanguage(StringPiece language) -{ - setField(language, language_, status_, &ultag_isLanguageSubtag); - return *this; -} - -LocaleBuilder& LocaleBuilder::setScript(StringPiece script) -{ - setField(script, script_, status_, &ultag_isScriptSubtag); - return *this; -} - -LocaleBuilder& LocaleBuilder::setRegion(StringPiece region) -{ - setField(region, region_, status_, &ultag_isRegionSubtag); - return *this; -} - -static void transform(char* data, int32_t len) { - for (int32_t i = 0; i < len; i++, data++) { - if (*data == '_') { - *data = '-'; - } else { - *data = uprv_tolower(*data); - } - } -} - -LocaleBuilder& LocaleBuilder::setVariant(StringPiece variant) -{ - if (U_FAILURE(status_)) { return *this; } - if (variant.empty()) { - delete variant_; - variant_ = nullptr; - return *this; - } - CharString* new_variant = new CharString(variant, status_); - if (U_FAILURE(status_)) { return *this; } - if (new_variant == nullptr) { - status_ = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - transform(new_variant->data(), new_variant->length()); - if (!ultag_isVariantSubtags(new_variant->data(), new_variant->length())) { - delete new_variant; - status_ = U_ILLEGAL_ARGUMENT_ERROR; - return *this; - } - delete variant_; - variant_ = new_variant; - return *this; -} - -static bool -_isKeywordValue(const char* key, const char* value, int32_t value_len) -{ - if (key[1] == '\0') { - // one char key - return (UPRV_ISALPHANUM(uprv_tolower(key[0])) && - _isExtensionSubtags(key[0], value, value_len)); - } else if (uprv_strcmp(key, kAttributeKey) == 0) { - // unicode attributes - return ultag_isUnicodeLocaleAttributes(value, value_len); - } - // otherwise: unicode extension value - // We need to convert from legacy key/value to unicode - // key/value - const char* unicode_locale_key = uloc_toUnicodeLocaleKey(key); - const char* unicode_locale_type = uloc_toUnicodeLocaleType(key, value); - - return unicode_locale_key && unicode_locale_type && - ultag_isUnicodeLocaleKey(unicode_locale_key, -1) && - ultag_isUnicodeLocaleType(unicode_locale_type, -1); -} - -static void -_copyExtensions(const Locale& from, icu::StringEnumeration *keywords, - Locale& to, bool validate, UErrorCode& errorCode) -{ - if (U_FAILURE(errorCode)) { return; } - LocalPointer ownedKeywords; - if (keywords == nullptr) { - ownedKeywords.adoptInstead(from.createKeywords(errorCode)); - if (U_FAILURE(errorCode) || ownedKeywords.isNull()) { return; } - keywords = ownedKeywords.getAlias(); - } - const char* key; - while ((key = keywords->next(nullptr, errorCode)) != nullptr) { - CharString value; - CharStringByteSink sink(&value); - from.getKeywordValue(key, sink, errorCode); - if (U_FAILURE(errorCode)) { return; } - if (uprv_strcmp(key, kAttributeKey) == 0) { - transform(value.data(), value.length()); - } - if (validate && - !_isKeywordValue(key, value.data(), value.length())) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - to.setKeywordValue(key, value.data(), errorCode); - if (U_FAILURE(errorCode)) { return; } - } -} - -void static -_clearUAttributesAndKeyType(Locale& locale, UErrorCode& errorCode) -{ - // Clear Unicode attributes - locale.setKeywordValue(kAttributeKey, "", errorCode); - - // Clear all Unicode keyword values - LocalPointer iter(locale.createUnicodeKeywords(errorCode)); - if (U_FAILURE(errorCode) || iter.isNull()) { return; } - const char* key; - while ((key = iter->next(nullptr, errorCode)) != nullptr) { - locale.setUnicodeKeywordValue(key, nullptr, errorCode); - } -} - -static void -_setUnicodeExtensions(Locale& locale, const CharString& value, UErrorCode& errorCode) -{ - // Add the unicode extensions to extensions_ - CharString locale_str("und-u-", errorCode); - locale_str.append(value, errorCode); - _copyExtensions( - Locale::forLanguageTag(locale_str.data(), errorCode), nullptr, - locale, false, errorCode); -} - -LocaleBuilder& LocaleBuilder::setExtension(char key, StringPiece value) -{ - if (U_FAILURE(status_)) { return *this; } - if (!UPRV_ISALPHANUM(key)) { - status_ = U_ILLEGAL_ARGUMENT_ERROR; - return *this; - } - CharString value_str(value, status_); - if (U_FAILURE(status_)) { return *this; } - transform(value_str.data(), value_str.length()); - if (!value_str.isEmpty() && - !_isExtensionSubtags(key, value_str.data(), value_str.length())) { - status_ = U_ILLEGAL_ARGUMENT_ERROR; - return *this; - } - if (extensions_ == nullptr) { - extensions_ = Locale::getRoot().clone(); - if (extensions_ == nullptr) { - status_ = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - } - if (uprv_tolower(key) != 'u') { - // for t, x and others extension. - extensions_->setKeywordValue(StringPiece(&key, 1), value_str.data(), - status_); - return *this; - } - _clearUAttributesAndKeyType(*extensions_, status_); - if (U_FAILURE(status_)) { return *this; } - if (!value.empty()) { - _setUnicodeExtensions(*extensions_, value_str, status_); - } - return *this; -} - -LocaleBuilder& LocaleBuilder::setUnicodeLocaleKeyword( - StringPiece key, StringPiece type) -{ - if (U_FAILURE(status_)) { return *this; } - if (!ultag_isUnicodeLocaleKey(key.data(), key.length()) || - (!type.empty() && - !ultag_isUnicodeLocaleType(type.data(), type.length()))) { - status_ = U_ILLEGAL_ARGUMENT_ERROR; - return *this; - } - if (extensions_ == nullptr) { - extensions_ = Locale::getRoot().clone(); - if (extensions_ == nullptr) { - status_ = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - } - extensions_->setUnicodeKeywordValue(key, type, status_); - return *this; -} - -LocaleBuilder& LocaleBuilder::addUnicodeLocaleAttribute( - StringPiece value) -{ - CharString value_str(value, status_); - if (U_FAILURE(status_)) { return *this; } - transform(value_str.data(), value_str.length()); - if (!ultag_isUnicodeLocaleAttribute(value_str.data(), value_str.length())) { - status_ = U_ILLEGAL_ARGUMENT_ERROR; - return *this; - } - if (extensions_ == nullptr) { - extensions_ = Locale::getRoot().clone(); - if (extensions_ == nullptr) { - status_ = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - extensions_->setKeywordValue(kAttributeKey, value_str.data(), status_); - return *this; - } - - CharString attributes; - CharStringByteSink sink(&attributes); - UErrorCode localErrorCode = U_ZERO_ERROR; - extensions_->getKeywordValue(kAttributeKey, sink, localErrorCode); - if (U_FAILURE(localErrorCode)) { - CharString new_attributes(value_str.data(), status_); - // No attributes, set the attribute. - extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_); - return *this; - } - - transform(attributes.data(),attributes.length()); - const char* start = attributes.data(); - const char* limit = attributes.data() + attributes.length(); - CharString new_attributes; - bool inserted = false; - while (start < limit) { - if (!inserted) { - int cmp = uprv_strcmp(start, value_str.data()); - if (cmp == 0) { return *this; } // Found it in attributes: Just return - if (cmp > 0) { - if (!new_attributes.isEmpty()) new_attributes.append('_', status_); - new_attributes.append(value_str.data(), status_); - inserted = true; - } - } - if (!new_attributes.isEmpty()) { - new_attributes.append('_', status_); - } - new_attributes.append(start, status_); - start += uprv_strlen(start) + 1; - } - if (!inserted) { - if (!new_attributes.isEmpty()) { - new_attributes.append('_', status_); - } - new_attributes.append(value_str.data(), status_); - } - // Not yet in the attributes, set the attribute. - extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_); - return *this; -} - -LocaleBuilder& LocaleBuilder::removeUnicodeLocaleAttribute( - StringPiece value) -{ - CharString value_str(value, status_); - if (U_FAILURE(status_)) { return *this; } - transform(value_str.data(), value_str.length()); - if (!ultag_isUnicodeLocaleAttribute(value_str.data(), value_str.length())) { - status_ = U_ILLEGAL_ARGUMENT_ERROR; - return *this; - } - if (extensions_ == nullptr) { return *this; } - UErrorCode localErrorCode = U_ZERO_ERROR; - CharString attributes; - CharStringByteSink sink(&attributes); - extensions_->getKeywordValue(kAttributeKey, sink, localErrorCode); - // get failure, just return - if (U_FAILURE(localErrorCode)) { return *this; } - // Do not have any attributes, just return. - if (attributes.isEmpty()) { return *this; } - - char* p = attributes.data(); - // Replace null terminiator in place for _ and - so later - // we can use uprv_strcmp to compare. - for (int32_t i = 0; i < attributes.length(); i++, p++) { - *p = (*p == '_' || *p == '-') ? '\0' : uprv_tolower(*p); - } - - const char* start = attributes.data(); - const char* limit = attributes.data() + attributes.length(); - CharString new_attributes; - bool found = false; - while (start < limit) { - if (uprv_strcmp(start, value_str.data()) == 0) { - found = true; - } else { - if (!new_attributes.isEmpty()) { - new_attributes.append('_', status_); - } - new_attributes.append(start, status_); - } - start += uprv_strlen(start) + 1; - } - // Found the value in attributes, set the attribute. - if (found) { - extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_); - } - return *this; -} - -LocaleBuilder& LocaleBuilder::clear() -{ - status_ = U_ZERO_ERROR; - language_[0] = 0; - script_[0] = 0; - region_[0] = 0; - delete variant_; - variant_ = nullptr; - clearExtensions(); - return *this; -} - -LocaleBuilder& LocaleBuilder::clearExtensions() -{ - delete extensions_; - extensions_ = nullptr; - return *this; -} - -Locale makeBogusLocale() { - Locale bogus; - bogus.setToBogus(); - return bogus; -} - -void LocaleBuilder::copyExtensionsFrom(const Locale& src, UErrorCode& errorCode) -{ - if (U_FAILURE(errorCode)) { return; } - LocalPointer keywords(src.createKeywords(errorCode)); - if (U_FAILURE(errorCode) || keywords.isNull() || keywords->count(errorCode) == 0) { - // Error, or no extensions to copy. - return; - } - if (extensions_ == nullptr) { - extensions_ = Locale::getRoot().clone(); - if (extensions_ == nullptr) { - status_ = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - _copyExtensions(src, keywords.getAlias(), *extensions_, false, errorCode); -} - -Locale LocaleBuilder::build(UErrorCode& errorCode) -{ - if (U_FAILURE(errorCode)) { - return makeBogusLocale(); - } - if (U_FAILURE(status_)) { - errorCode = status_; - return makeBogusLocale(); - } - CharString locale_str(language_, errorCode); - if (uprv_strlen(script_) > 0) { - locale_str.append('-', errorCode).append(StringPiece(script_), errorCode); - } - if (uprv_strlen(region_) > 0) { - locale_str.append('-', errorCode).append(StringPiece(region_), errorCode); - } - if (variant_ != nullptr) { - locale_str.append('-', errorCode).append(StringPiece(variant_->data()), errorCode); - } - if (U_FAILURE(errorCode)) { - return makeBogusLocale(); - } - Locale product(locale_str.data()); - if (extensions_ != nullptr) { - _copyExtensions(*extensions_, nullptr, product, true, errorCode); - } - if (U_FAILURE(errorCode)) { - return makeBogusLocale(); - } - return product; -} - -UBool LocaleBuilder::copyErrorTo(UErrorCode &outErrorCode) const { - if (U_FAILURE(outErrorCode)) { - // Do not overwrite the older error code - return true; - } - outErrorCode = status_; - return U_FAILURE(outErrorCode); -} - -U_NAMESPACE_END +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include + +#include "bytesinkutil.h" // CharStringByteSink +#include "charstr.h" +#include "cstring.h" +#include "ulocimp.h" +#include "unicode/localebuilder.h" +#include "unicode/locid.h" + +U_NAMESPACE_BEGIN + +#define UPRV_ISDIGIT(c) (((c) >= '0') && ((c) <= '9')) +#define UPRV_ISALPHANUM(c) (uprv_isASCIILetter(c) || UPRV_ISDIGIT(c) ) + +constexpr const char* kAttributeKey = "attribute"; + +static bool _isExtensionSubtags(char key, const char* s, int32_t len) { + switch (uprv_tolower(key)) { + case 'u': + return ultag_isUnicodeExtensionSubtags(s, len); + case 't': + return ultag_isTransformedExtensionSubtags(s, len); + case 'x': + return ultag_isPrivateuseValueSubtags(s, len); + default: + return ultag_isExtensionSubtags(s, len); + } +} + +LocaleBuilder::LocaleBuilder() : UObject(), status_(U_ZERO_ERROR), language_(), + script_(), region_(), variant_(nullptr), extensions_(nullptr) +{ + language_[0] = 0; + script_[0] = 0; + region_[0] = 0; +} + +LocaleBuilder::~LocaleBuilder() +{ + delete variant_; + delete extensions_; +} + +LocaleBuilder& LocaleBuilder::setLocale(const Locale& locale) +{ + clear(); + setLanguage(locale.getLanguage()); + setScript(locale.getScript()); + setRegion(locale.getCountry()); + setVariant(locale.getVariant()); + extensions_ = locale.clone(); + if (extensions_ == nullptr) { + status_ = U_MEMORY_ALLOCATION_ERROR; + } + return *this; +} + +LocaleBuilder& LocaleBuilder::setLanguageTag(StringPiece tag) +{ + Locale l = Locale::forLanguageTag(tag, status_); + if (U_FAILURE(status_)) { return *this; } + // Because setLocale will reset status_ we need to return + // first if we have error in forLanguageTag. + setLocale(l); + return *this; +} + +static void setField(StringPiece input, char* dest, UErrorCode& errorCode, + UBool (*test)(const char*, int32_t)) { + if (U_FAILURE(errorCode)) { return; } + if (input.empty()) { + dest[0] = '\0'; + } else if (test(input.data(), input.length())) { + uprv_memcpy(dest, input.data(), input.length()); + dest[input.length()] = '\0'; + } else { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } +} + +LocaleBuilder& LocaleBuilder::setLanguage(StringPiece language) +{ + setField(language, language_, status_, &ultag_isLanguageSubtag); + return *this; +} + +LocaleBuilder& LocaleBuilder::setScript(StringPiece script) +{ + setField(script, script_, status_, &ultag_isScriptSubtag); + return *this; +} + +LocaleBuilder& LocaleBuilder::setRegion(StringPiece region) +{ + setField(region, region_, status_, &ultag_isRegionSubtag); + return *this; +} + +static void transform(char* data, int32_t len) { + for (int32_t i = 0; i < len; i++, data++) { + if (*data == '_') { + *data = '-'; + } else { + *data = uprv_tolower(*data); + } + } +} + +LocaleBuilder& LocaleBuilder::setVariant(StringPiece variant) +{ + if (U_FAILURE(status_)) { return *this; } + if (variant.empty()) { + delete variant_; + variant_ = nullptr; + return *this; + } + CharString* new_variant = new CharString(variant, status_); + if (U_FAILURE(status_)) { return *this; } + if (new_variant == nullptr) { + status_ = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + transform(new_variant->data(), new_variant->length()); + if (!ultag_isVariantSubtags(new_variant->data(), new_variant->length())) { + delete new_variant; + status_ = U_ILLEGAL_ARGUMENT_ERROR; + return *this; + } + delete variant_; + variant_ = new_variant; + return *this; +} + +static bool +_isKeywordValue(const char* key, const char* value, int32_t value_len) +{ + if (key[1] == '\0') { + // one char key + return (UPRV_ISALPHANUM(uprv_tolower(key[0])) && + _isExtensionSubtags(key[0], value, value_len)); + } else if (uprv_strcmp(key, kAttributeKey) == 0) { + // unicode attributes + return ultag_isUnicodeLocaleAttributes(value, value_len); + } + // otherwise: unicode extension value + // We need to convert from legacy key/value to unicode + // key/value + const char* unicode_locale_key = uloc_toUnicodeLocaleKey(key); + const char* unicode_locale_type = uloc_toUnicodeLocaleType(key, value); + + return unicode_locale_key && unicode_locale_type && + ultag_isUnicodeLocaleKey(unicode_locale_key, -1) && + ultag_isUnicodeLocaleType(unicode_locale_type, -1); +} + +static void +_copyExtensions(const Locale& from, icu::StringEnumeration *keywords, + Locale& to, bool validate, UErrorCode& errorCode) +{ + if (U_FAILURE(errorCode)) { return; } + LocalPointer ownedKeywords; + if (keywords == nullptr) { + ownedKeywords.adoptInstead(from.createKeywords(errorCode)); + if (U_FAILURE(errorCode) || ownedKeywords.isNull()) { return; } + keywords = ownedKeywords.getAlias(); + } + const char* key; + while ((key = keywords->next(nullptr, errorCode)) != nullptr) { + CharString value; + CharStringByteSink sink(&value); + from.getKeywordValue(key, sink, errorCode); + if (U_FAILURE(errorCode)) { return; } + if (uprv_strcmp(key, kAttributeKey) == 0) { + transform(value.data(), value.length()); + } + if (validate && + !_isKeywordValue(key, value.data(), value.length())) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + to.setKeywordValue(key, value.data(), errorCode); + if (U_FAILURE(errorCode)) { return; } + } +} + +void static +_clearUAttributesAndKeyType(Locale& locale, UErrorCode& errorCode) +{ + // Clear Unicode attributes + locale.setKeywordValue(kAttributeKey, "", errorCode); + + // Clear all Unicode keyword values + LocalPointer iter(locale.createUnicodeKeywords(errorCode)); + if (U_FAILURE(errorCode) || iter.isNull()) { return; } + const char* key; + while ((key = iter->next(nullptr, errorCode)) != nullptr) { + locale.setUnicodeKeywordValue(key, nullptr, errorCode); + } +} + +static void +_setUnicodeExtensions(Locale& locale, const CharString& value, UErrorCode& errorCode) +{ + // Add the unicode extensions to extensions_ + CharString locale_str("und-u-", errorCode); + locale_str.append(value, errorCode); + _copyExtensions( + Locale::forLanguageTag(locale_str.data(), errorCode), nullptr, + locale, false, errorCode); +} + +LocaleBuilder& LocaleBuilder::setExtension(char key, StringPiece value) +{ + if (U_FAILURE(status_)) { return *this; } + if (!UPRV_ISALPHANUM(key)) { + status_ = U_ILLEGAL_ARGUMENT_ERROR; + return *this; + } + CharString value_str(value, status_); + if (U_FAILURE(status_)) { return *this; } + transform(value_str.data(), value_str.length()); + if (!value_str.isEmpty() && + !_isExtensionSubtags(key, value_str.data(), value_str.length())) { + status_ = U_ILLEGAL_ARGUMENT_ERROR; + return *this; + } + if (extensions_ == nullptr) { + extensions_ = Locale::getRoot().clone(); + if (extensions_ == nullptr) { + status_ = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + } + if (uprv_tolower(key) != 'u') { + // for t, x and others extension. + extensions_->setKeywordValue(StringPiece(&key, 1), value_str.data(), + status_); + return *this; + } + _clearUAttributesAndKeyType(*extensions_, status_); + if (U_FAILURE(status_)) { return *this; } + if (!value.empty()) { + _setUnicodeExtensions(*extensions_, value_str, status_); + } + return *this; +} + +LocaleBuilder& LocaleBuilder::setUnicodeLocaleKeyword( + StringPiece key, StringPiece type) +{ + if (U_FAILURE(status_)) { return *this; } + if (!ultag_isUnicodeLocaleKey(key.data(), key.length()) || + (!type.empty() && + !ultag_isUnicodeLocaleType(type.data(), type.length()))) { + status_ = U_ILLEGAL_ARGUMENT_ERROR; + return *this; + } + if (extensions_ == nullptr) { + extensions_ = Locale::getRoot().clone(); + if (extensions_ == nullptr) { + status_ = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + } + extensions_->setUnicodeKeywordValue(key, type, status_); + return *this; +} + +LocaleBuilder& LocaleBuilder::addUnicodeLocaleAttribute( + StringPiece value) +{ + CharString value_str(value, status_); + if (U_FAILURE(status_)) { return *this; } + transform(value_str.data(), value_str.length()); + if (!ultag_isUnicodeLocaleAttribute(value_str.data(), value_str.length())) { + status_ = U_ILLEGAL_ARGUMENT_ERROR; + return *this; + } + if (extensions_ == nullptr) { + extensions_ = Locale::getRoot().clone(); + if (extensions_ == nullptr) { + status_ = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + extensions_->setKeywordValue(kAttributeKey, value_str.data(), status_); + return *this; + } + + CharString attributes; + CharStringByteSink sink(&attributes); + UErrorCode localErrorCode = U_ZERO_ERROR; + extensions_->getKeywordValue(kAttributeKey, sink, localErrorCode); + if (U_FAILURE(localErrorCode)) { + CharString new_attributes(value_str.data(), status_); + // No attributes, set the attribute. + extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_); + return *this; + } + + transform(attributes.data(),attributes.length()); + const char* start = attributes.data(); + const char* limit = attributes.data() + attributes.length(); + CharString new_attributes; + bool inserted = false; + while (start < limit) { + if (!inserted) { + int cmp = uprv_strcmp(start, value_str.data()); + if (cmp == 0) { return *this; } // Found it in attributes: Just return + if (cmp > 0) { + if (!new_attributes.isEmpty()) new_attributes.append('_', status_); + new_attributes.append(value_str.data(), status_); + inserted = true; + } + } + if (!new_attributes.isEmpty()) { + new_attributes.append('_', status_); + } + new_attributes.append(start, status_); + start += uprv_strlen(start) + 1; + } + if (!inserted) { + if (!new_attributes.isEmpty()) { + new_attributes.append('_', status_); + } + new_attributes.append(value_str.data(), status_); + } + // Not yet in the attributes, set the attribute. + extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_); + return *this; +} + +LocaleBuilder& LocaleBuilder::removeUnicodeLocaleAttribute( + StringPiece value) +{ + CharString value_str(value, status_); + if (U_FAILURE(status_)) { return *this; } + transform(value_str.data(), value_str.length()); + if (!ultag_isUnicodeLocaleAttribute(value_str.data(), value_str.length())) { + status_ = U_ILLEGAL_ARGUMENT_ERROR; + return *this; + } + if (extensions_ == nullptr) { return *this; } + UErrorCode localErrorCode = U_ZERO_ERROR; + CharString attributes; + CharStringByteSink sink(&attributes); + extensions_->getKeywordValue(kAttributeKey, sink, localErrorCode); + // get failure, just return + if (U_FAILURE(localErrorCode)) { return *this; } + // Do not have any attributes, just return. + if (attributes.isEmpty()) { return *this; } + + char* p = attributes.data(); + // Replace null terminiator in place for _ and - so later + // we can use uprv_strcmp to compare. + for (int32_t i = 0; i < attributes.length(); i++, p++) { + *p = (*p == '_' || *p == '-') ? '\0' : uprv_tolower(*p); + } + + const char* start = attributes.data(); + const char* limit = attributes.data() + attributes.length(); + CharString new_attributes; + bool found = false; + while (start < limit) { + if (uprv_strcmp(start, value_str.data()) == 0) { + found = true; + } else { + if (!new_attributes.isEmpty()) { + new_attributes.append('_', status_); + } + new_attributes.append(start, status_); + } + start += uprv_strlen(start) + 1; + } + // Found the value in attributes, set the attribute. + if (found) { + extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_); + } + return *this; +} + +LocaleBuilder& LocaleBuilder::clear() +{ + status_ = U_ZERO_ERROR; + language_[0] = 0; + script_[0] = 0; + region_[0] = 0; + delete variant_; + variant_ = nullptr; + clearExtensions(); + return *this; +} + +LocaleBuilder& LocaleBuilder::clearExtensions() +{ + delete extensions_; + extensions_ = nullptr; + return *this; +} + +Locale makeBogusLocale() { + Locale bogus; + bogus.setToBogus(); + return bogus; +} + +void LocaleBuilder::copyExtensionsFrom(const Locale& src, UErrorCode& errorCode) +{ + if (U_FAILURE(errorCode)) { return; } + LocalPointer keywords(src.createKeywords(errorCode)); + if (U_FAILURE(errorCode) || keywords.isNull() || keywords->count(errorCode) == 0) { + // Error, or no extensions to copy. + return; + } + if (extensions_ == nullptr) { + extensions_ = Locale::getRoot().clone(); + if (extensions_ == nullptr) { + status_ = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + _copyExtensions(src, keywords.getAlias(), *extensions_, false, errorCode); +} + +Locale LocaleBuilder::build(UErrorCode& errorCode) +{ + if (U_FAILURE(errorCode)) { + return makeBogusLocale(); + } + if (U_FAILURE(status_)) { + errorCode = status_; + return makeBogusLocale(); + } + CharString locale_str(language_, errorCode); + if (uprv_strlen(script_) > 0) { + locale_str.append('-', errorCode).append(StringPiece(script_), errorCode); + } + if (uprv_strlen(region_) > 0) { + locale_str.append('-', errorCode).append(StringPiece(region_), errorCode); + } + if (variant_ != nullptr) { + locale_str.append('-', errorCode).append(StringPiece(variant_->data()), errorCode); + } + if (U_FAILURE(errorCode)) { + return makeBogusLocale(); + } + Locale product(locale_str.data()); + if (extensions_ != nullptr) { + _copyExtensions(*extensions_, nullptr, product, true, errorCode); + } + if (U_FAILURE(errorCode)) { + return makeBogusLocale(); + } + return product; +} + +UBool LocaleBuilder::copyErrorTo(UErrorCode &outErrorCode) const { + if (U_FAILURE(outErrorCode)) { + // Do not overwrite the older error code + return true; + } + outErrorCode = status_; + return U_FAILURE(outErrorCode); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/localefallback_data.h b/deps/icu-small/source/common/localefallback_data.h index da725de42d9a3e..8b9b94e4e2ca7e 100644 --- a/deps/icu-small/source/common/localefallback_data.h +++ b/deps/icu-small/source/common/localefallback_data.h @@ -1,632 +1,1350 @@ -// © 2022 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// Internal static data tables used by uresbund.cpp -// WARNING: This file is mechanically generated by the CLDR-to-ICU tool -// (see tools/cldr/cldr-to-icu/src/main/java/org/unicode/tool/cldrtoicu/generator/ResourcFallbackCodeGenerator.java). -// DO NOT HAND EDIT!!! - -#ifdef INCLUDED_FROM_URESBUND_CPP - -//====================================================================== -// Default script table -const char scriptCodeChars[] = - "Aghb\0Ahom\0Arab\0Armi\0Armn\0Avst\0Bamu\0Bass\0Beng\0Brah\0Cakm\0" - "Cans\0Cari\0Cham\0Cher\0Chrs\0Copt\0Cprt\0Cyrl\0Deva\0Egyp\0Ethi\0" - "Geor\0Gong\0Gonm\0Goth\0Grek\0Gujr\0Guru\0Hans\0Hant\0Hebr\0Hluw\0" - "Hmnp\0Ital\0Jpan\0Kali\0Kana\0Kawi\0Khar\0Khmr\0Kits\0Knda\0Kore\0" - "Lana\0Laoo\0Lepc\0Lina\0Lisu\0Lyci\0Lydi\0Mand\0Mani\0Medf\0Merc\0" - "Mlym\0Mong\0Mroo\0Mymr\0Narb\0Nkoo\0Nshu\0Ogam\0Olck\0Orkh\0Orya\0" - "Osge\0Ougr\0Pauc\0Phli\0Phnx\0Plrd\0Prti\0Rohg\0Runr\0Samr\0Sarb\0" - "Saur\0Sgnw\0Sinh\0Sogd\0Sora\0Soyo\0Syrc\0Tale\0Talu\0Taml\0Tang\0" - "Tavt\0Telu\0Tfng\0Thaa\0Thai\0Tibt\0Tnsa\0Toto\0Ugar\0Vaii\0Wcho\0" - "Xpeo\0Xsux\0Yiii\0"; - -const char dsLocaleIDChars[] = - "ab\0abq\0adp\0ady\0ae\0aeb\0aho\0ajt\0akk\0alt\0am\0apc\0apd\0" - "ar\0arc\0arq\0ars\0ary\0arz\0as\0ase\0av\0avl\0awa\0az_IQ\0az_IR\0" - "az_RU\0ba\0bal\0bap\0bax\0bcq\0be\0bej\0bfq\0bft\0bfy\0bg\0bgc\0" - "bgn\0bgx\0bhb\0bhi\0bho\0bji\0bjj\0blt\0bn\0bo\0bpy\0bqi\0bra\0" - "brh\0brx\0bsq\0bst\0btv\0bua\0byn\0ccp\0ce\0chm\0chr\0cja\0cjm\0" - "ckb\0cmg\0cop\0cr\0crh\0crk\0crl\0csw\0ctd\0cu\0cv\0dar\0dcc\0" - "dgl\0dmf\0doi\0drh\0drs\0dty\0dv\0dz\0egy\0eky\0el\0esg\0ett\0" - "fa\0fia\0fub\0gan\0gbm\0gbz\0gez\0ggn\0gjk\0gju\0glk\0gmv\0gof\0" - "gom\0gon\0got\0grc\0grt\0gu\0gvr\0gwc\0gwt\0ha_CM\0ha_SD\0hak\0" - "haz\0hdy\0he\0hi\0hlu\0hmd\0hnd\0hne\0hnj\0hno\0hoc\0hoj\0hsn\0" - "hy\0ii\0inh\0iu\0iw\0ja\0ji\0jml\0ka\0kaa\0kaw\0kbd\0kby\0kdt\0" - "kfr\0kfy\0khb\0khn\0kht\0khw\0kjg\0kk\0kk_AF\0kk_CN\0kk_IR\0kk_MN\0" - "km\0kn\0ko\0koi\0kok\0kqy\0krc\0kru\0ks\0ktb\0ku_LB\0kum\0kv\0" - "kvx\0kxc\0kxl\0kxm\0kxp\0ky\0ky_CN\0kzh\0lab\0lad\0lah\0lbe\0" - "lcp\0lep\0lez\0lif\0lis\0lki\0lmn\0lo\0lrc\0luz\0lwl\0lzh\0mag\0" - "mai\0man_GN\0mde\0mdf\0mdx\0mfa\0mgp\0mk\0mki\0ml\0mn\0mn_CN\0" - "mni\0mnw\0mr\0mrd\0mrj\0mro\0ms_CC\0mtr\0mvy\0mwr\0mww\0my\0mym\0" - "myv\0myz\0mzn\0nan\0ne\0new\0nnp\0nod\0noe\0non\0nqo\0nsk\0nst\0" - "oj\0ojs\0or\0oru\0os\0osa\0ota\0otk\0oui\0pa\0pa_PK\0pal\0peo\0" - "phl\0phn\0pka\0pnt\0ppa\0pra\0prd\0ps\0raj\0rhg\0rif\0rjs\0rkt\0" - "rmt\0ru\0rue\0ryu\0sa\0sah\0sat\0saz\0sck\0scl\0sd\0sd_IN\0sdh\0" - "sga\0sgw\0shi\0shn\0shu\0si\0skr\0smp\0sog\0sou\0sr\0srb\0srx\0" - "swb\0swv\0syl\0syr\0ta\0taj\0tcy\0tdd\0tdg\0tdh\0te\0tg\0tg_PK\0" - "th\0thl\0thq\0thr\0ti\0tig\0tkt\0trw\0tsd\0tsf\0tsj\0tt\0tts\0" - "txg\0txo\0tyv\0udi\0udm\0ug\0ug_KZ\0ug_MN\0uga\0uk\0unr\0unr_NP\0" - "unx\0ur\0uz_AF\0uz_CN\0vai\0wal\0wbq\0wbr\0wni\0wsg\0wtm\0wuu\0" - "xco\0xcr\0xlc\0xld\0xmf\0xmn\0xmr\0xna\0xnr\0xpr\0xsa\0xsr\0yi\0" - "yue\0yue_CN\0zdj\0zgh\0zh\0zh_AU\0zh_BN\0zh_GB\0zh_GF\0zh_HK\0" - "zh_ID\0zh_MO\0zh_PA\0zh_PF\0zh_PH\0zh_SR\0zh_TH\0zh_TW\0zh_US\0" - "zh_VN\0zhx\0zkt\0"; - -const int32_t defaultScriptTable[] = { - 0, 90, // ab -> Cyrl - 3, 90, // abq -> Cyrl - 7, 465, // adp -> Tibt - 11, 90, // ady -> Cyrl - 15, 25, // ae -> Avst - 18, 10, // aeb -> Arab - 22, 5, // aho -> Ahom - 26, 10, // ajt -> Arab - 30, 500, // akk -> Xsux - 34, 90, // alt -> Cyrl - 38, 105, // am -> Ethi - 41, 10, // apc -> Arab - 45, 10, // apd -> Arab - 49, 10, // ar -> Arab - 52, 15, // arc -> Armi - 56, 10, // arq -> Arab - 60, 10, // ars -> Arab - 64, 10, // ary -> Arab - 68, 10, // arz -> Arab - 72, 40, // as -> Beng - 75, 390, // ase -> Sgnw - 79, 90, // av -> Cyrl - 82, 10, // avl -> Arab - 86, 95, // awa -> Deva - 90, 10, // az_IQ -> Arab - 96, 10, // az_IR -> Arab - 102, 90, // az_RU -> Cyrl - 108, 90, // ba -> Cyrl - 111, 10, // bal -> Arab - 115, 95, // bap -> Deva - 119, 30, // bax -> Bamu - 123, 105, // bcq -> Ethi - 127, 90, // be -> Cyrl - 130, 10, // bej -> Arab - 134, 430, // bfq -> Taml - 138, 10, // bft -> Arab - 142, 95, // bfy -> Deva - 146, 90, // bg -> Cyrl - 149, 95, // bgc -> Deva - 153, 10, // bgn -> Arab - 157, 130, // bgx -> Grek - 161, 95, // bhb -> Deva - 165, 95, // bhi -> Deva - 169, 95, // bho -> Deva - 173, 105, // bji -> Ethi - 177, 95, // bjj -> Deva - 181, 440, // blt -> Tavt - 185, 40, // bn -> Beng - 188, 465, // bo -> Tibt - 191, 40, // bpy -> Beng - 195, 10, // bqi -> Arab - 199, 95, // bra -> Deva - 203, 10, // brh -> Arab - 207, 95, // brx -> Deva - 211, 35, // bsq -> Bass - 215, 105, // bst -> Ethi - 219, 95, // btv -> Deva - 223, 90, // bua -> Cyrl - 227, 105, // byn -> Ethi - 231, 50, // ccp -> Cakm - 235, 90, // ce -> Cyrl - 238, 90, // chm -> Cyrl - 242, 70, // chr -> Cher - 246, 10, // cja -> Arab - 250, 65, // cjm -> Cham - 254, 10, // ckb -> Arab - 258, 410, // cmg -> Soyo - 262, 80, // cop -> Copt - 266, 55, // cr -> Cans - 269, 90, // crh -> Cyrl - 273, 55, // crk -> Cans - 277, 55, // crl -> Cans - 281, 55, // csw -> Cans - 285, 340, // ctd -> Pauc - 289, 90, // cu -> Cyrl - 292, 90, // cv -> Cyrl - 295, 90, // dar -> Cyrl - 299, 10, // dcc -> Arab - 303, 10, // dgl -> Arab - 307, 265, // dmf -> Medf - 311, 95, // doi -> Deva - 315, 280, // drh -> Mong - 319, 105, // drs -> Ethi - 323, 95, // dty -> Deva - 327, 455, // dv -> Thaa - 330, 465, // dz -> Tibt - 333, 100, // egy -> Egyp - 337, 180, // eky -> Kali - 341, 130, // el -> Grek - 344, 120, // esg -> Gonm - 348, 170, // ett -> Ital - 352, 10, // fa -> Arab - 355, 10, // fia -> Arab - 359, 10, // fub -> Arab - 363, 145, // gan -> Hans - 367, 95, // gbm -> Deva - 371, 10, // gbz -> Arab - 375, 105, // gez -> Ethi - 379, 95, // ggn -> Deva - 383, 10, // gjk -> Arab - 387, 10, // gju -> Arab - 391, 10, // glk -> Arab - 395, 105, // gmv -> Ethi - 399, 105, // gof -> Ethi - 403, 95, // gom -> Deva - 407, 445, // gon -> Telu - 411, 125, // got -> Goth - 415, 85, // grc -> Cprt - 419, 40, // grt -> Beng - 423, 135, // gu -> Gujr - 426, 95, // gvr -> Deva - 430, 10, // gwc -> Arab - 434, 10, // gwt -> Arab - 438, 10, // ha_CM -> Arab - 444, 10, // ha_SD -> Arab - 450, 145, // hak -> Hans - 454, 10, // haz -> Arab - 458, 105, // hdy -> Ethi - 462, 155, // he -> Hebr - 465, 95, // hi -> Deva - 468, 160, // hlu -> Hluw - 472, 355, // hmd -> Plrd - 476, 10, // hnd -> Arab - 480, 95, // hne -> Deva - 484, 165, // hnj -> Hmnp - 488, 10, // hno -> Arab - 492, 95, // hoc -> Deva - 496, 95, // hoj -> Deva - 500, 145, // hsn -> Hans - 504, 20, // hy -> Armn - 507, 505, // ii -> Yiii - 510, 90, // inh -> Cyrl - 514, 55, // iu -> Cans - 517, 155, // iw -> Hebr - 520, 175, // ja -> Jpan - 523, 155, // ji -> Hebr - 526, 95, // jml -> Deva - 530, 110, // ka -> Geor - 533, 90, // kaa -> Cyrl - 537, 190, // kaw -> Kawi - 541, 90, // kbd -> Cyrl - 545, 10, // kby -> Arab - 549, 460, // kdt -> Thai - 553, 95, // kfr -> Deva - 557, 95, // kfy -> Deva - 561, 425, // khb -> Talu - 565, 95, // khn -> Deva - 569, 290, // kht -> Mymr - 573, 10, // khw -> Arab - 577, 225, // kjg -> Laoo - 581, 90, // kk -> Cyrl - 584, 10, // kk_AF -> Arab - 590, 10, // kk_CN -> Arab - 596, 10, // kk_IR -> Arab - 602, 10, // kk_MN -> Arab - 608, 200, // km -> Khmr - 611, 210, // kn -> Knda - 614, 215, // ko -> Kore - 617, 90, // koi -> Cyrl - 621, 95, // kok -> Deva - 625, 105, // kqy -> Ethi - 629, 90, // krc -> Cyrl - 633, 95, // kru -> Deva - 637, 10, // ks -> Arab - 640, 105, // ktb -> Ethi - 644, 10, // ku_LB -> Arab - 650, 90, // kum -> Cyrl - 654, 90, // kv -> Cyrl - 657, 10, // kvx -> Arab - 661, 105, // kxc -> Ethi - 665, 95, // kxl -> Deva - 669, 460, // kxm -> Thai - 673, 10, // kxp -> Arab - 677, 90, // ky -> Cyrl - 680, 10, // ky_CN -> Arab - 686, 10, // kzh -> Arab - 690, 235, // lab -> Lina - 694, 155, // lad -> Hebr - 698, 10, // lah -> Arab - 702, 90, // lbe -> Cyrl - 706, 460, // lcp -> Thai - 710, 230, // lep -> Lepc - 714, 90, // lez -> Cyrl - 718, 95, // lif -> Deva - 722, 240, // lis -> Lisu - 726, 10, // lki -> Arab - 730, 445, // lmn -> Telu - 734, 225, // lo -> Laoo - 737, 10, // lrc -> Arab - 741, 10, // luz -> Arab - 745, 460, // lwl -> Thai - 749, 145, // lzh -> Hans - 753, 95, // mag -> Deva - 757, 95, // mai -> Deva - 761, 300, // man_GN -> Nkoo - 768, 10, // mde -> Arab - 772, 90, // mdf -> Cyrl - 776, 105, // mdx -> Ethi - 780, 10, // mfa -> Arab - 784, 95, // mgp -> Deva - 788, 90, // mk -> Cyrl - 791, 10, // mki -> Arab - 795, 275, // ml -> Mlym - 798, 90, // mn -> Cyrl - 801, 280, // mn_CN -> Mong - 807, 40, // mni -> Beng - 811, 290, // mnw -> Mymr - 815, 95, // mr -> Deva - 818, 95, // mrd -> Deva - 822, 90, // mrj -> Cyrl - 826, 285, // mro -> Mroo - 830, 10, // ms_CC -> Arab - 836, 95, // mtr -> Deva - 840, 10, // mvy -> Arab - 844, 95, // mwr -> Deva - 848, 165, // mww -> Hmnp - 852, 290, // my -> Mymr - 855, 105, // mym -> Ethi - 859, 90, // myv -> Cyrl - 863, 255, // myz -> Mand - 867, 10, // mzn -> Arab - 871, 145, // nan -> Hans - 875, 95, // ne -> Deva - 878, 95, // new -> Deva - 882, 490, // nnp -> Wcho - 886, 220, // nod -> Lana - 890, 95, // noe -> Deva - 894, 370, // non -> Runr - 898, 300, // nqo -> Nkoo - 902, 55, // nsk -> Cans - 906, 470, // nst -> Tnsa - 910, 55, // oj -> Cans - 913, 55, // ojs -> Cans - 917, 325, // or -> Orya - 920, 10, // oru -> Arab - 924, 90, // os -> Cyrl - 927, 330, // osa -> Osge - 931, 10, // ota -> Arab - 935, 320, // otk -> Orkh - 939, 335, // oui -> Ougr - 943, 140, // pa -> Guru - 946, 10, // pa_PK -> Arab - 952, 345, // pal -> Phli - 956, 495, // peo -> Xpeo - 960, 10, // phl -> Arab - 964, 350, // phn -> Phnx - 968, 45, // pka -> Brah - 972, 130, // pnt -> Grek - 976, 95, // ppa -> Deva - 980, 195, // pra -> Khar - 984, 10, // prd -> Arab - 988, 10, // ps -> Arab - 991, 95, // raj -> Deva - 995, 365, // rhg -> Rohg - 999, 450, // rif -> Tfng - 1003, 95, // rjs -> Deva - 1007, 40, // rkt -> Beng - 1011, 10, // rmt -> Arab - 1015, 90, // ru -> Cyrl - 1018, 90, // rue -> Cyrl - 1022, 185, // ryu -> Kana - 1026, 95, // sa -> Deva - 1029, 90, // sah -> Cyrl - 1033, 315, // sat -> Olck - 1037, 385, // saz -> Saur - 1041, 95, // sck -> Deva - 1045, 10, // scl -> Arab - 1049, 10, // sd -> Arab - 1052, 95, // sd_IN -> Deva - 1058, 10, // sdh -> Arab - 1062, 310, // sga -> Ogam - 1066, 105, // sgw -> Ethi - 1070, 450, // shi -> Tfng - 1074, 290, // shn -> Mymr - 1078, 10, // shu -> Arab - 1082, 395, // si -> Sinh - 1085, 10, // skr -> Arab - 1089, 375, // smp -> Samr - 1093, 400, // sog -> Sogd - 1097, 460, // sou -> Thai - 1101, 90, // sr -> Cyrl - 1104, 405, // srb -> Sora - 1108, 95, // srx -> Deva - 1112, 10, // swb -> Arab - 1116, 95, // swv -> Deva - 1120, 40, // syl -> Beng - 1124, 415, // syr -> Syrc - 1128, 430, // ta -> Taml - 1131, 95, // taj -> Deva - 1135, 210, // tcy -> Knda - 1139, 420, // tdd -> Tale - 1143, 95, // tdg -> Deva - 1147, 95, // tdh -> Deva - 1151, 445, // te -> Telu - 1154, 90, // tg -> Cyrl - 1157, 10, // tg_PK -> Arab - 1163, 460, // th -> Thai - 1166, 95, // thl -> Deva - 1170, 95, // thq -> Deva - 1174, 95, // thr -> Deva - 1178, 105, // ti -> Ethi - 1181, 105, // tig -> Ethi - 1185, 95, // tkt -> Deva - 1189, 10, // trw -> Arab - 1193, 130, // tsd -> Grek - 1197, 95, // tsf -> Deva - 1201, 465, // tsj -> Tibt - 1205, 90, // tt -> Cyrl - 1208, 460, // tts -> Thai - 1212, 435, // txg -> Tang - 1216, 475, // txo -> Toto - 1220, 90, // tyv -> Cyrl - 1224, 0, // udi -> Aghb - 1228, 90, // udm -> Cyrl - 1232, 10, // ug -> Arab - 1235, 90, // ug_KZ -> Cyrl - 1241, 90, // ug_MN -> Cyrl - 1247, 480, // uga -> Ugar - 1251, 90, // uk -> Cyrl - 1254, 40, // unr -> Beng - 1258, 95, // unr_NP -> Deva - 1265, 40, // unx -> Beng - 1269, 10, // ur -> Arab - 1272, 10, // uz_AF -> Arab - 1278, 90, // uz_CN -> Cyrl - 1284, 485, // vai -> Vaii - 1288, 105, // wal -> Ethi - 1292, 445, // wbq -> Telu - 1296, 95, // wbr -> Deva - 1300, 10, // wni -> Arab - 1304, 115, // wsg -> Gong - 1308, 95, // wtm -> Deva - 1312, 145, // wuu -> Hans - 1316, 75, // xco -> Chrs - 1320, 60, // xcr -> Cari - 1324, 245, // xlc -> Lyci - 1328, 250, // xld -> Lydi - 1332, 110, // xmf -> Geor - 1336, 260, // xmn -> Mani - 1340, 270, // xmr -> Merc - 1344, 295, // xna -> Narb - 1348, 95, // xnr -> Deva - 1352, 360, // xpr -> Prti - 1356, 380, // xsa -> Sarb - 1360, 95, // xsr -> Deva - 1364, 155, // yi -> Hebr - 1367, 150, // yue -> Hant - 1371, 145, // yue_CN -> Hans - 1378, 10, // zdj -> Arab - 1382, 450, // zgh -> Tfng - 1386, 145, // zh -> Hans - 1389, 150, // zh_AU -> Hant - 1395, 150, // zh_BN -> Hant - 1401, 150, // zh_GB -> Hant - 1407, 150, // zh_GF -> Hant - 1413, 150, // zh_HK -> Hant - 1419, 150, // zh_ID -> Hant - 1425, 150, // zh_MO -> Hant - 1431, 150, // zh_PA -> Hant - 1437, 150, // zh_PF -> Hant - 1443, 150, // zh_PH -> Hant - 1449, 150, // zh_SR -> Hant - 1455, 150, // zh_TH -> Hant - 1461, 150, // zh_TW -> Hant - 1467, 150, // zh_US -> Hant - 1473, 150, // zh_VN -> Hant - 1479, 305, // zhx -> Nshu - 1483, 205, // zkt -> Kits -}; - -//====================================================================== -// Parent locale table -const char parentLocaleChars[] = - "az_Arab\0az_Cyrl\0bal_Latn\0blt_Latn\0bm_Nkoo\0bs_Cyrl\0byn_Latn\0" - "cu_Glag\0dje_Arab\0dyo_Arab\0en_001\0en_150\0en_AG\0en_AI\0en_AT\0" - "en_AU\0en_BB\0en_BE\0en_BM\0en_BS\0en_BW\0en_BZ\0en_CC\0en_CH\0" - "en_CK\0en_CM\0en_CX\0en_CY\0en_DE\0en_DG\0en_DK\0en_DM\0en_Dsrt\0" - "en_ER\0en_FI\0en_FJ\0en_FK\0en_FM\0en_GB\0en_GD\0en_GG\0en_GH\0" - "en_GI\0en_GM\0en_GY\0en_HK\0en_IE\0en_IL\0en_IM\0en_IN\0en_IO\0" - "en_JE\0en_JM\0en_KE\0en_KI\0en_KN\0en_KY\0en_LC\0en_LR\0en_LS\0" - "en_MG\0en_MO\0en_MS\0en_MT\0en_MU\0en_MV\0en_MW\0en_MY\0en_NA\0" - "en_NF\0en_NG\0en_NL\0en_NR\0en_NU\0en_NZ\0en_PG\0en_PK\0en_PN\0" - "en_PW\0en_RW\0en_SB\0en_SC\0en_SD\0en_SE\0en_SG\0en_SH\0en_SI\0" - "en_SL\0en_SS\0en_SX\0en_SZ\0en_Shaw\0en_TC\0en_TK\0en_TO\0en_TT\0" - "en_TV\0en_TZ\0en_UG\0en_VC\0en_VG\0en_VU\0en_WS\0en_ZA\0en_ZM\0" - "en_ZW\0es_419\0es_AR\0es_BO\0es_BR\0es_BZ\0es_CL\0es_CO\0es_CR\0" - "es_CU\0es_DO\0es_EC\0es_GT\0es_HN\0es_MX\0es_NI\0es_PA\0es_PE\0" - "es_PR\0es_PY\0es_SV\0es_US\0es_UY\0es_VE\0ff_Adlm\0ff_Arab\0fr_HT\0" - "ha_Arab\0hi_Latn\0ht\0iu_Latn\0kk_Arab\0ks_Deva\0ku_Arab\0ky_Arab\0" - "ky_Latn\0ml_Arab\0mn_Mong\0mni_Mtei\0ms_Arab\0nb\0nn\0no\0pa_Arab\0" - "pt_AO\0pt_CH\0pt_CV\0pt_FR\0pt_GQ\0pt_GW\0pt_LU\0pt_MO\0pt_MZ\0" - "pt_PT\0pt_ST\0pt_TL\0root\0sat_Deva\0sd_Deva\0sd_Khoj\0sd_Sind\0" - "shi_Latn\0so_Arab\0sr_Latn\0sw_Arab\0tg_Arab\0ug_Cyrl\0uz_Arab\0" - "uz_Cyrl\0vai_Latn\0wo_Arab\0yo_Arab\0yue_Hans\0zh_Hant\0zh_Hant_HK\0" - "zh_Hant_MO\0"; - -const int32_t parentLocaleTable[] = { - 0, 1017, // az_Arab -> root - 8, 1017, // az_Cyrl -> root - 16, 1017, // bal_Latn -> root - 25, 1017, // blt_Latn -> root - 34, 1017, // bm_Nkoo -> root - 42, 1017, // bs_Cyrl -> root - 50, 1017, // byn_Latn -> root - 59, 1017, // cu_Glag -> root - 67, 1017, // dje_Arab -> root - 76, 1017, // dyo_Arab -> root - 92, 85, // en_150 -> en_001 - 99, 85, // en_AG -> en_001 - 105, 85, // en_AI -> en_001 - 111, 92, // en_AT -> en_150 - 117, 85, // en_AU -> en_001 - 123, 85, // en_BB -> en_001 - 129, 92, // en_BE -> en_150 - 135, 85, // en_BM -> en_001 - 141, 85, // en_BS -> en_001 - 147, 85, // en_BW -> en_001 - 153, 85, // en_BZ -> en_001 - 159, 85, // en_CC -> en_001 - 165, 92, // en_CH -> en_150 - 171, 85, // en_CK -> en_001 - 177, 85, // en_CM -> en_001 - 183, 85, // en_CX -> en_001 - 189, 85, // en_CY -> en_001 - 195, 92, // en_DE -> en_150 - 201, 85, // en_DG -> en_001 - 207, 92, // en_DK -> en_150 - 213, 85, // en_DM -> en_001 - 219, 1017, // en_Dsrt -> root - 227, 85, // en_ER -> en_001 - 233, 92, // en_FI -> en_150 - 239, 85, // en_FJ -> en_001 - 245, 85, // en_FK -> en_001 - 251, 85, // en_FM -> en_001 - 257, 85, // en_GB -> en_001 - 263, 85, // en_GD -> en_001 - 269, 85, // en_GG -> en_001 - 275, 85, // en_GH -> en_001 - 281, 85, // en_GI -> en_001 - 287, 85, // en_GM -> en_001 - 293, 85, // en_GY -> en_001 - 299, 85, // en_HK -> en_001 - 305, 85, // en_IE -> en_001 - 311, 85, // en_IL -> en_001 - 317, 85, // en_IM -> en_001 - 323, 85, // en_IN -> en_001 - 329, 85, // en_IO -> en_001 - 335, 85, // en_JE -> en_001 - 341, 85, // en_JM -> en_001 - 347, 85, // en_KE -> en_001 - 353, 85, // en_KI -> en_001 - 359, 85, // en_KN -> en_001 - 365, 85, // en_KY -> en_001 - 371, 85, // en_LC -> en_001 - 377, 85, // en_LR -> en_001 - 383, 85, // en_LS -> en_001 - 389, 85, // en_MG -> en_001 - 395, 85, // en_MO -> en_001 - 401, 85, // en_MS -> en_001 - 407, 85, // en_MT -> en_001 - 413, 85, // en_MU -> en_001 - 419, 85, // en_MV -> en_001 - 425, 85, // en_MW -> en_001 - 431, 85, // en_MY -> en_001 - 437, 85, // en_NA -> en_001 - 443, 85, // en_NF -> en_001 - 449, 85, // en_NG -> en_001 - 455, 92, // en_NL -> en_150 - 461, 85, // en_NR -> en_001 - 467, 85, // en_NU -> en_001 - 473, 85, // en_NZ -> en_001 - 479, 85, // en_PG -> en_001 - 485, 85, // en_PK -> en_001 - 491, 85, // en_PN -> en_001 - 497, 85, // en_PW -> en_001 - 503, 85, // en_RW -> en_001 - 509, 85, // en_SB -> en_001 - 515, 85, // en_SC -> en_001 - 521, 85, // en_SD -> en_001 - 527, 92, // en_SE -> en_150 - 533, 85, // en_SG -> en_001 - 539, 85, // en_SH -> en_001 - 545, 92, // en_SI -> en_150 - 551, 85, // en_SL -> en_001 - 557, 85, // en_SS -> en_001 - 563, 85, // en_SX -> en_001 - 569, 85, // en_SZ -> en_001 - 575, 1017, // en_Shaw -> root - 583, 85, // en_TC -> en_001 - 589, 85, // en_TK -> en_001 - 595, 85, // en_TO -> en_001 - 601, 85, // en_TT -> en_001 - 607, 85, // en_TV -> en_001 - 613, 85, // en_TZ -> en_001 - 619, 85, // en_UG -> en_001 - 625, 85, // en_VC -> en_001 - 631, 85, // en_VG -> en_001 - 637, 85, // en_VU -> en_001 - 643, 85, // en_WS -> en_001 - 649, 85, // en_ZA -> en_001 - 655, 85, // en_ZM -> en_001 - 661, 85, // en_ZW -> en_001 - 674, 667, // es_AR -> es_419 - 680, 667, // es_BO -> es_419 - 686, 667, // es_BR -> es_419 - 692, 667, // es_BZ -> es_419 - 698, 667, // es_CL -> es_419 - 704, 667, // es_CO -> es_419 - 710, 667, // es_CR -> es_419 - 716, 667, // es_CU -> es_419 - 722, 667, // es_DO -> es_419 - 728, 667, // es_EC -> es_419 - 734, 667, // es_GT -> es_419 - 740, 667, // es_HN -> es_419 - 746, 667, // es_MX -> es_419 - 752, 667, // es_NI -> es_419 - 758, 667, // es_PA -> es_419 - 764, 667, // es_PE -> es_419 - 770, 667, // es_PR -> es_419 - 776, 667, // es_PY -> es_419 - 782, 667, // es_SV -> es_419 - 788, 667, // es_US -> es_419 - 794, 667, // es_UY -> es_419 - 800, 667, // es_VE -> es_419 - 806, 1017, // ff_Adlm -> root - 814, 1017, // ff_Arab -> root - 828, 1017, // ha_Arab -> root - 836, 323, // hi_Latn -> en_IN - 844, 822, // ht -> fr_HT - 847, 1017, // iu_Latn -> root - 855, 1017, // kk_Arab -> root - 863, 1017, // ks_Deva -> root - 871, 1017, // ku_Arab -> root - 879, 1017, // ky_Arab -> root - 887, 1017, // ky_Latn -> root - 895, 1017, // ml_Arab -> root - 903, 1017, // mn_Mong -> root - 911, 1017, // mni_Mtei -> root - 920, 1017, // ms_Arab -> root - 928, 934, // nb -> no - 931, 934, // nn -> no - 937, 1017, // pa_Arab -> root - 945, 999, // pt_AO -> pt_PT - 951, 999, // pt_CH -> pt_PT - 957, 999, // pt_CV -> pt_PT - 963, 999, // pt_FR -> pt_PT - 969, 999, // pt_GQ -> pt_PT - 975, 999, // pt_GW -> pt_PT - 981, 999, // pt_LU -> pt_PT - 987, 999, // pt_MO -> pt_PT - 993, 999, // pt_MZ -> pt_PT - 1005, 999, // pt_ST -> pt_PT - 1011, 999, // pt_TL -> pt_PT - 1022, 1017, // sat_Deva -> root - 1031, 1017, // sd_Deva -> root - 1039, 1017, // sd_Khoj -> root - 1047, 1017, // sd_Sind -> root - 1055, 1017, // shi_Latn -> root - 1064, 1017, // so_Arab -> root - 1072, 1017, // sr_Latn -> root - 1080, 1017, // sw_Arab -> root - 1088, 1017, // tg_Arab -> root - 1096, 1017, // ug_Cyrl -> root - 1104, 1017, // uz_Arab -> root - 1112, 1017, // uz_Cyrl -> root - 1120, 1017, // vai_Latn -> root - 1129, 1017, // wo_Arab -> root - 1137, 1017, // yo_Arab -> root - 1145, 1017, // yue_Hans -> root - 1154, 1017, // zh_Hant -> root - 1173, 1162, // zh_Hant_MO -> zh_Hant_HK -}; - - -#endif // INCLUDED_FROM_URESBUND_CPP +// © 2022 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// Internal static data tables used by uresbund.cpp +// WARNING: This file is mechanically generated by the CLDR-to-ICU tool +// (see tools/cldr/cldr-to-icu/src/main/java/org/unicode/tool/cldrtoicu/generator/ResourcFallbackCodeGenerator.java). +// DO NOT HAND EDIT!!! + +#ifdef INCLUDED_FROM_URESBUND_CPP + +//====================================================================== +// Default script table +const char scriptCodeChars[] = + "Aghb\0Ahom\0Arab\0Armi\0Armn\0Avst\0Bamu\0Bass\0Batk\0Beng\0Bopo\0" + "Brah\0Cakm\0Cans\0Cari\0Cham\0Cher\0Chrs\0Copt\0Cprt\0Cyrl\0Deva\0" + "Egyp\0Elym\0Ethi\0Geor\0Gong\0Gonm\0Goth\0Gran\0Grek\0Gujr\0Guru\0" + "Hang\0Hani\0Hans\0Hant\0Hebr\0Hluw\0Hmnp\0Ital\0Java\0Jpan\0Kali\0" + "Kana\0Kawi\0Khar\0Khmr\0Kits\0Knda\0Kore\0Lana\0Laoo\0Lepc\0Lina\0" + "Linb\0Lisu\0Lyci\0Lydi\0Mand\0Mani\0Marc\0Medf\0Merc\0Mlym\0Modi\0" + "Mong\0Mroo\0Mtei\0Mymr\0Narb\0Newa\0Nkoo\0Nshu\0Ogam\0Olck\0Orkh\0" + "Orya\0Osge\0Ougr\0Pauc\0Phli\0Phnx\0Plrd\0Prti\0Rjng\0Rohg\0Runr\0" + "Samr\0Sarb\0Saur\0Sgnw\0Sinh\0Sogd\0Sora\0Soyo\0Syrc\0Tagb\0Takr\0" + "Tale\0Talu\0Taml\0Tang\0Tavt\0Telu\0Tfng\0Thaa\0Thai\0Tibt\0Tnsa\0" + "Toto\0Ugar\0Vaii\0Wcho\0Xpeo\0Xsux\0Yiii\0"; + +const char dsLocaleIDChars[] = + "aaf\0aao\0aat\0ab\0abh\0abl\0abq\0abv\0acm\0acq\0acw\0acx\0adf\0" + "adp\0adx\0ady\0ae\0aeb\0aec\0aee\0aeq\0afb\0agi\0agj\0agx\0ahg\0" + "aho\0ahr\0aib\0aij\0ain\0aio\0aiq\0ajp\0ajt\0akk\0akv\0alk\0all\0" + "alr\0alt\0alw\0am\0ams\0amw\0ani\0anp\0anr\0anu\0aot\0apc\0apd\0" + "aph\0aqc\0ar\0arc\0arq\0ars\0ary\0arz\0as\0ase\0ask\0atn\0atv\0" + "auj\0auz\0av\0avd\0avl\0awa\0awn\0axm\0ayh\0ayl\0ayn\0ayp\0az_IQ\0" + "az_IR\0az_RU\0azb\0ba\0bal\0bap\0bax\0bbl\0bcq\0bdv\0bdz\0be\0" + "bee\0bej\0bfb\0bfq\0bft\0bfu\0bfw\0bfy\0bfz\0bg\0bgc\0bgd\0bgn\0" + "bgp\0bgq\0bgw\0bgx\0bha\0bhb\0bhd\0bhe\0bhh\0bhi\0bhj\0bhm\0bhn\0" + "bho\0bht\0bhu\0biy\0bjf\0bji\0bjj\0bjm\0blk\0blt\0bmj\0bn\0bns\0" + "bo\0bph\0bpx\0bpy\0bqi\0bra\0brb\0brd\0brh\0brk\0brv\0brx\0bsh\0" + "bsk\0bsq\0bst\0btd\0btm\0btv\0bua\0bwe\0bxm\0bxu\0byh\0byn\0byw\0" + "bzi\0cbn\0ccp\0cde\0cdh\0cdi\0cdj\0cdm\0cdo\0cdz\0ce\0cgk\0chg\0" + "chm\0chr\0chx\0cih\0cja\0cji\0cjm\0cjy\0ckb\0ckt\0clh\0clw\0cmg\0" + "cna\0cnp\0cog\0cop\0cpg\0cr\0crh\0crj\0crk\0crl\0crm\0csh\0csp\0" + "csw\0ctd\0ctg\0ctn\0ctt\0cu\0cuu\0cv\0czh\0czk\0daq\0dar\0dcc\0" + "ddo\0def\0deh\0der\0dgl\0dhi\0dhn\0dho\0dhw\0dka\0dlg\0dmf\0dmk\0" + "dml\0dng\0dnu\0dnv\0doi\0dox\0dre\0drh\0drq\0drs\0dry\0dso\0dty\0" + "dub\0duh\0dus\0dv\0dwk\0dwz\0dz\0dzl\0ecr\0ecy\0egy\0eky\0el\0" + "emg\0emu\0enf\0enh\0era\0esg\0esh\0ett\0eve\0evn\0fa\0fay\0faz\0" + "fia\0fmu\0fub\0gan\0gaq\0gas\0gau\0gbj\0gbk\0gbl\0gbm\0gbz\0gdb\0" + "gdo\0gdx\0gez\0ggg\0ggn\0gha\0ghe\0ghr\0ght\0gig\0gin\0gjk\0gju\0" + "gld\0glh\0glk\0gmv\0gmy\0goe\0gof\0gok\0gom\0gon\0got\0gra\0grc\0" + "grt\0gru\0gu\0gvr\0gwc\0gwf\0gwt\0gyo\0gzi\0ha_CM\0ha_SD\0hac\0" + "hak\0har\0haz\0hbo\0hdy\0he\0hi\0hii\0hit\0hkh\0hlb\0hlu\0hmd\0" + "hmj\0hmq\0hnd\0hne\0hnj\0hnj_AU\0hnj_CN\0hnj_FR\0hnj_GF\0hnj_LA\0" + "hnj_MM\0hnj_SR\0hnj_TH\0hnj_US\0hnj_VN\0hno\0hoc\0hoh\0hoj\0how\0" + "hoy\0hpo\0hrt\0hrz\0hsn\0hss\0htx\0hut\0huy\0huz\0hy\0hyw\0ii\0" + "imy\0inh\0int\0ior\0iru\0isk\0itk\0itl\0iu\0iw\0ja\0jad\0jat\0" + "jbe\0jbn\0jct\0jda\0jdg\0jdt\0jee\0jge\0ji\0jje\0jkm\0jml\0jna\0" + "jnd\0jnl\0jns\0jog\0jpa\0jpr\0jul\0jun\0juy\0jya\0jye\0ka\0kaa\0" + "kap\0kaw\0kbd\0kbu\0kby\0kca\0kdq\0kdt\0ket\0kex\0key\0kfa\0kfb\0" + "kfc\0kfd\0kfe\0kfh\0kfi\0kfk\0kfm\0kfp\0kfq\0kfr\0kfs\0kfx\0kfy\0" + "kgj\0kgy\0khb\0khf\0khg\0khn\0kht\0khv\0khw\0kif\0kim\0kip\0kjg\0" + "kjh\0kjl\0kjo\0kjp\0kjt\0kk\0kk_AF\0kk_CN\0kk_IR\0kk_MN\0kkf\0" + "kkh\0kkt\0kle\0klj\0klr\0km\0kmj\0kmz\0kn\0ko\0koi\0kok\0kpt\0" + "kpy\0kqd\0kqy\0kra\0krc\0krk\0krr\0kru\0krv\0ks\0ksu\0ksw\0ksz\0" + "ktb\0ktl\0ktp\0ku_LB\0kuf\0kum\0kv\0kva\0kvq\0kvt\0kvx\0kvy\0" + "kxc\0kxf\0kxk\0kxl\0kxm\0kxp\0kxv\0ky\0ky_CN\0kyu\0kyv\0kyw\0" + "kzh\0lab\0lad\0lae\0lah\0lbc\0lbe\0lbf\0lbj\0lbm\0lbo\0lbr\0lcp\0" + "lep\0lez\0lhm\0lhs\0lif\0lis\0lkh\0lki\0lmh\0lmn\0lo\0loy\0lpo\0" + "lrc\0lrk\0lrl\0lsa\0lsd\0lss\0luk\0luu\0luv\0luz\0lwl\0lwm\0lya\0" + "lzh\0mag\0mai\0man_GN\0mby\0mde\0mdf\0mdx\0mdy\0mfa\0mfi\0mgp\0" + "mhj\0mid\0mjl\0mjq\0mjr\0mjt\0mju\0mjv\0mjz\0mk\0mkb\0mke\0mki\0" + "mkm\0ml\0mlf\0mn\0mn_CN\0mni\0mnj\0mns\0mnw\0mpz\0mr\0mra\0mrd\0" + "mrj\0mro\0mrr\0ms_CC\0mtm\0mtr\0mud\0muk\0mut\0muv\0muz\0mvf\0" + "mvy\0mvz\0mwr\0mwt\0mww\0my\0mym\0myv\0myz\0mzn\0nan\0nao\0ncd\0" + "ncq\0ndf\0ne\0neg\0neh\0nei\0new\0ngt\0nio\0nit\0niv\0nli\0nlm\0" + "nlx\0nmm\0nnp\0nod\0noe\0nog\0noi\0non\0nos\0npb\0nqo\0nsd\0nsf\0" + "nsk\0nst\0nsv\0nty\0ntz\0nwc\0nwx\0nyl\0nyq\0oaa\0oac\0oar\0oav\0" + "obm\0obr\0odk\0oht\0oj\0ojs\0okm\0oko\0okz\0ola\0ole\0omk\0omp\0" + "omr\0oon\0or\0ort\0oru\0orv\0os\0osa\0osc\0osi\0ota\0otb\0otk\0" + "oty\0oui\0pa\0pa_PK\0pal\0paq\0pbt\0pcb\0pce\0pcf\0pcg\0pch\0" + "pci\0pcj\0peg\0peo\0pgd\0pgg\0pgl\0pgn\0phd\0phk\0phl\0phn\0pho\0" + "phr\0pht\0phv\0phw\0pi\0pka\0pkr\0plk\0pll\0pmh\0pnt\0ppa\0pra\0" + "prc\0prd\0prp\0prt\0prx\0ps\0psh\0psi\0pst\0pum\0pwo\0pwr\0pww\0" + "pyx\0qxq\0raa\0rab\0raf\0rah\0raj\0rav\0rbb\0rdb\0rei\0rhg\0rji\0" + "rjs\0rka\0rki\0rkt\0rmi\0rmt\0rmz\0rsk\0rtw\0ru\0rue\0rut\0rwr\0" + "ryu\0sa\0sah\0sam\0sat\0saz\0sbn\0sbu\0sck\0scl\0scp\0sct\0scu\0" + "scx\0sd\0sd_IN\0sdb\0sdf\0sdg\0sdh\0sds\0sel\0sfm\0sga\0sgh\0" + "sgj\0sgr\0sgt\0sgw\0sgy\0shd\0shi\0shm\0shn\0shu\0shv\0si\0sia\0" + "sip\0siy\0siz\0sjd\0sjp\0sjt\0skb\0skj\0skr\0slq\0smh\0smp\0smu\0" + "smy\0soa\0sog\0soi\0sou\0spt\0spv\0sqo\0sqq\0sqt\0sr\0srb\0srh\0" + "srx\0srz\0ssh\0sss\0sts\0stv\0sty\0suz\0sva\0swb\0swi\0swv\0syc\0" + "syl\0syn\0syr\0syw\0ta\0tab\0taj\0tbk\0tcn\0tco\0tcx\0tcy\0tda\0" + "tdb\0tdd\0tdg\0tdh\0te\0tes\0tg\0tg_PK\0tge\0tgf\0th\0the\0thf\0" + "thi\0thl\0thm\0thq\0thr\0ths\0ti\0tig\0tij\0tin\0tjl\0tjo\0tkb\0" + "tks\0tkt\0tmk\0tmr\0tnv\0tov\0tpu\0tra\0trg\0trm\0trw\0tsd\0tsf\0" + "tsj\0tt\0tth\0tto\0tts\0tvn\0twm\0txg\0txo\0tyr\0tyv\0ude\0udg\0" + "udi\0udm\0ug\0ug_KZ\0ug_MN\0uga\0ugh\0ugo\0uk\0uki\0ulc\0unr\0" + "unr_NP\0unx\0ur\0urk\0ush\0uum\0uz_AF\0uz_CN\0uzs\0vaa\0vaf\0" + "vah\0vai\0vas\0vav\0vay\0vgr\0vmd\0vmh\0wal\0wbk\0wbq\0wbr\0wlo\0" + "wme\0wne\0wni\0wsg\0wsv\0wtm\0wuu\0xal\0xan\0xas\0xco\0xcr\0xdq\0" + "xhe\0xhm\0xis\0xka\0xkc\0xkj\0xkp\0xlc\0xld\0xly\0xmf\0xmn\0xmr\0" + "xna\0xnr\0xpg\0xpi\0xpm\0xpr\0xrm\0xrn\0xsa\0xsr\0xss\0xub\0xuj\0" + "xve\0xvi\0xwo\0xzh\0yai\0ybh\0ybi\0ydg\0yea\0yej\0yeu\0ygp\0yhd\0" + "yi\0yig\0yih\0yiv\0ykg\0yna\0ynk\0yoi\0yoy\0yrk\0ysd\0ysn\0ysp\0" + "ysr\0ysy\0yud\0yue\0yue_CN\0yug\0yux\0ywq\0ywu\0zau\0zba\0zch\0" + "zdj\0zeh\0zen\0zgb\0zgh\0zgm\0zgn\0zh\0zh_AU\0zh_BN\0zh_GB\0zh_GF\0" + "zh_HK\0zh_ID\0zh_MO\0zh_PA\0zh_PF\0zh_PH\0zh_SR\0zh_TH\0zh_TW\0" + "zh_US\0zh_VN\0zhd\0zhx\0zkb\0zko\0zkt\0zkz\0zlj\0zln\0zlq\0zqe\0" + "zrp\0zum\0zyg\0zyn\0zzj\0"; + +const int32_t defaultScriptTable[] = { + 0, 320, // aaf -> Mlym + 4, 10, // aao -> Arab + 8, 150, // aat -> Grek + 12, 100, // ab -> Cyrl + 15, 10, // abh -> Arab + 19, 425, // abl -> Rjng + 23, 100, // abq -> Cyrl + 27, 10, // abv -> Arab + 31, 10, // acm -> Arab + 35, 10, // acq -> Arab + 39, 10, // acw -> Arab + 43, 10, // acx -> Arab + 47, 10, // adf -> Arab + 51, 540, // adp -> Tibt + 55, 540, // adx -> Tibt + 59, 100, // ady -> Cyrl + 63, 25, // ae -> Avst + 66, 10, // aeb -> Arab + 70, 10, // aec -> Arab + 74, 10, // aee -> Arab + 78, 10, // aeq -> Arab + 82, 10, // afb -> Arab + 86, 105, // agi -> Deva + 90, 120, // agj -> Ethi + 94, 100, // agx -> Cyrl + 98, 120, // ahg -> Ethi + 102, 5, // aho -> Ahom + 106, 105, // ahr -> Deva + 110, 10, // aib -> Arab + 114, 185, // aij -> Hebr + 118, 220, // ain -> Kana + 122, 345, // aio -> Mymr + 126, 10, // aiq -> Arab + 130, 10, // ajp -> Arab + 134, 10, // ajt -> Arab + 138, 575, // akk -> Xsux + 142, 100, // akv -> Cyrl + 146, 260, // alk -> Laoo + 150, 320, // all -> Mlym + 154, 100, // alr -> Cyrl + 158, 100, // alt -> Cyrl + 162, 120, // alw -> Ethi + 166, 120, // am -> Ethi + 169, 210, // ams -> Jpan + 173, 480, // amw -> Syrc + 177, 100, // ani -> Cyrl + 181, 105, // anp -> Deva + 185, 105, // anr -> Deva + 189, 120, // anu -> Ethi + 193, 45, // aot -> Beng + 197, 10, // apc -> Arab + 201, 10, // apd -> Arab + 205, 105, // aph -> Deva + 209, 100, // aqc -> Cyrl + 213, 10, // ar -> Arab + 216, 15, // arc -> Armi + 220, 10, // arq -> Arab + 224, 10, // ars -> Arab + 228, 10, // ary -> Arab + 232, 10, // arz -> Arab + 236, 45, // as -> Beng + 239, 455, // ase -> Sgnw + 243, 10, // ask -> Arab + 247, 10, // atn -> Arab + 251, 100, // atv -> Cyrl + 255, 10, // auj -> Arab + 259, 10, // auz -> Arab + 263, 100, // av -> Cyrl + 266, 10, // avd -> Arab + 270, 10, // avl -> Arab + 274, 105, // awa -> Deva + 278, 120, // awn -> Ethi + 282, 20, // axm -> Armn + 286, 10, // ayh -> Arab + 290, 10, // ayl -> Arab + 294, 10, // ayn -> Arab + 298, 10, // ayp -> Arab + 302, 10, // az_IQ -> Arab + 308, 10, // az_IR -> Arab + 314, 100, // az_RU -> Cyrl + 320, 10, // azb -> Arab + 324, 100, // ba -> Cyrl + 327, 10, // bal -> Arab + 331, 105, // bap -> Deva + 335, 30, // bax -> Bamu + 339, 125, // bbl -> Geor + 343, 120, // bcq -> Ethi + 347, 385, // bdv -> Orya + 351, 10, // bdz -> Arab + 355, 100, // be -> Cyrl + 358, 105, // bee -> Deva + 362, 10, // bej -> Arab + 366, 105, // bfb -> Deva + 370, 505, // bfq -> Taml + 374, 10, // bft -> Arab + 378, 540, // bfu -> Tibt + 382, 385, // bfw -> Orya + 386, 105, // bfy -> Deva + 390, 105, // bfz -> Deva + 394, 100, // bg -> Cyrl + 397, 105, // bgc -> Deva + 401, 105, // bgd -> Deva + 405, 10, // bgn -> Arab + 409, 10, // bgp -> Arab + 413, 105, // bgq -> Deva + 417, 105, // bgw -> Deva + 421, 150, // bgx -> Grek + 425, 105, // bha -> Deva + 429, 105, // bhb -> Deva + 433, 105, // bhd -> Deva + 437, 10, // bhe -> Arab + 441, 100, // bhh -> Cyrl + 445, 105, // bhi -> Deva + 449, 105, // bhj -> Deva + 453, 10, // bhm -> Arab + 457, 480, // bhn -> Syrc + 461, 105, // bho -> Deva + 465, 490, // bht -> Takr + 469, 105, // bhu -> Deva + 473, 105, // biy -> Deva + 477, 480, // bjf -> Syrc + 481, 120, // bji -> Ethi + 485, 105, // bjj -> Deva + 489, 10, // bjm -> Arab + 493, 345, // blk -> Mymr + 497, 515, // blt -> Tavt + 501, 105, // bmj -> Deva + 505, 45, // bn -> Beng + 508, 105, // bns -> Deva + 512, 540, // bo -> Tibt + 515, 100, // bph -> Cyrl + 519, 105, // bpx -> Deva + 523, 45, // bpy -> Beng + 527, 10, // bqi -> Arab + 531, 105, // bra -> Deva + 535, 235, // brb -> Khmr + 539, 105, // brd -> Deva + 543, 10, // brh -> Arab + 547, 10, // brk -> Arab + 551, 260, // brv -> Laoo + 555, 105, // brx -> Deva + 559, 10, // bsh -> Arab + 563, 10, // bsk -> Arab + 567, 35, // bsq -> Bass + 571, 120, // bst -> Ethi + 575, 40, // btd -> Batk + 579, 40, // btm -> Batk + 583, 105, // btv -> Deva + 587, 100, // bua -> Cyrl + 591, 345, // bwe -> Mymr + 595, 100, // bxm -> Cyrl + 599, 330, // bxu -> Mong + 603, 105, // byh -> Deva + 607, 120, // byn -> Ethi + 611, 105, // byw -> Deva + 615, 535, // bzi -> Thai + 619, 535, // cbn -> Thai + 623, 60, // ccp -> Cakm + 627, 520, // cde -> Telu + 631, 105, // cdh -> Deva + 635, 155, // cdi -> Gujr + 639, 105, // cdj -> Deva + 643, 105, // cdm -> Deva + 647, 175, // cdo -> Hans + 651, 45, // cdz -> Beng + 655, 100, // ce -> Cyrl + 658, 540, // cgk -> Tibt + 662, 10, // chg -> Arab + 666, 100, // chm -> Cyrl + 670, 80, // chr -> Cher + 674, 105, // chx -> Deva + 678, 105, // cih -> Deva + 682, 10, // cja -> Arab + 686, 100, // cji -> Cyrl + 690, 75, // cjm -> Cham + 694, 175, // cjy -> Hans + 698, 10, // ckb -> Arab + 702, 100, // ckt -> Cyrl + 706, 10, // clh -> Arab + 710, 100, // clw -> Cyrl + 714, 475, // cmg -> Soyo + 718, 540, // cna -> Tibt + 722, 175, // cnp -> Hans + 726, 535, // cog -> Thai + 730, 90, // cop -> Copt + 734, 150, // cpg -> Grek + 738, 65, // cr -> Cans + 741, 100, // crh -> Cyrl + 745, 65, // crj -> Cans + 749, 65, // crk -> Cans + 753, 65, // crl -> Cans + 757, 65, // crm -> Cans + 761, 345, // csh -> Mymr + 765, 175, // csp -> Hans + 769, 65, // csw -> Cans + 773, 400, // ctd -> Pauc + 777, 45, // ctg -> Beng + 781, 105, // ctn -> Deva + 785, 505, // ctt -> Taml + 789, 100, // cu -> Cyrl + 792, 255, // cuu -> Lana + 796, 100, // cv -> Cyrl + 799, 175, // czh -> Hans + 803, 185, // czk -> Hebr + 807, 105, // daq -> Deva + 811, 100, // dar -> Cyrl + 815, 10, // dcc -> Arab + 819, 100, // ddo -> Cyrl + 823, 10, // def -> Arab + 827, 10, // deh -> Arab + 831, 45, // der -> Beng + 835, 10, // dgl -> Arab + 839, 105, // dhi -> Deva + 843, 155, // dhn -> Gujr + 847, 105, // dho -> Deva + 851, 105, // dhw -> Deva + 855, 540, // dka -> Tibt + 859, 100, // dlg -> Cyrl + 863, 310, // dmf -> Medf + 867, 10, // dmk -> Arab + 871, 10, // dml -> Arab + 875, 100, // dng -> Cyrl + 879, 345, // dnu -> Mymr + 883, 345, // dnv -> Mymr + 887, 105, // doi -> Deva + 891, 120, // dox -> Ethi + 895, 540, // dre -> Tibt + 899, 330, // drh -> Mong + 903, 105, // drq -> Deva + 907, 120, // drs -> Ethi + 911, 105, // dry -> Deva + 915, 385, // dso -> Orya + 919, 105, // dty -> Deva + 923, 155, // dub -> Gujr + 927, 105, // duh -> Deva + 931, 105, // dus -> Deva + 935, 530, // dv -> Thaa + 938, 385, // dwk -> Orya + 942, 105, // dwz -> Deva + 946, 540, // dz -> Tibt + 949, 540, // dzl -> Tibt + 953, 150, // ecr -> Grek + 957, 95, // ecy -> Cprt + 961, 110, // egy -> Egyp + 965, 215, // eky -> Kali + 969, 150, // el -> Grek + 972, 105, // emg -> Deva + 976, 105, // emu -> Deva + 980, 100, // enf -> Cyrl + 984, 100, // enh -> Cyrl + 988, 505, // era -> Taml + 992, 135, // esg -> Gonm + 996, 10, // esh -> Arab + 1000, 200, // ett -> Ital + 1004, 100, // eve -> Cyrl + 1008, 100, // evn -> Cyrl + 1012, 10, // fa -> Arab + 1015, 10, // fay -> Arab + 1019, 10, // faz -> Arab + 1023, 10, // fia -> Arab + 1027, 105, // fmu -> Deva + 1031, 10, // fub -> Arab + 1035, 175, // gan -> Hans + 1039, 385, // gaq -> Orya + 1043, 155, // gas -> Gujr + 1047, 520, // gau -> Telu + 1051, 385, // gbj -> Orya + 1055, 105, // gbk -> Deva + 1059, 155, // gbl -> Gujr + 1063, 105, // gbm -> Deva + 1067, 10, // gbz -> Arab + 1071, 385, // gdb -> Orya + 1075, 100, // gdo -> Cyrl + 1079, 105, // gdx -> Deva + 1083, 120, // gez -> Ethi + 1087, 10, // ggg -> Arab + 1091, 105, // ggn -> Deva + 1095, 10, // gha -> Arab + 1099, 105, // ghe -> Deva + 1103, 10, // ghr -> Arab + 1107, 540, // ght -> Tibt + 1111, 10, // gig -> Arab + 1115, 100, // gin -> Cyrl + 1119, 10, // gjk -> Arab + 1123, 10, // gju -> Arab + 1127, 100, // gld -> Cyrl + 1131, 10, // glh -> Arab + 1135, 10, // glk -> Arab + 1139, 120, // gmv -> Ethi + 1143, 275, // gmy -> Linb + 1147, 540, // goe -> Tibt + 1151, 120, // gof -> Ethi + 1155, 105, // gok -> Deva + 1159, 105, // gom -> Deva + 1163, 520, // gon -> Telu + 1167, 140, // got -> Goth + 1171, 105, // gra -> Deva + 1175, 95, // grc -> Cprt + 1179, 45, // grt -> Beng + 1183, 120, // gru -> Ethi + 1187, 155, // gu -> Gujr + 1190, 105, // gvr -> Deva + 1194, 10, // gwc -> Arab + 1198, 10, // gwf -> Arab + 1202, 10, // gwt -> Arab + 1206, 105, // gyo -> Deva + 1210, 10, // gzi -> Arab + 1214, 10, // ha_CM -> Arab + 1220, 10, // ha_SD -> Arab + 1226, 10, // hac -> Arab + 1230, 175, // hak -> Hans + 1234, 120, // har -> Ethi + 1238, 10, // haz -> Arab + 1242, 185, // hbo -> Hebr + 1246, 120, // hdy -> Ethi + 1250, 185, // he -> Hebr + 1253, 105, // hi -> Deva + 1256, 490, // hii -> Takr + 1260, 575, // hit -> Xsux + 1264, 10, // hkh -> Arab + 1268, 105, // hlb -> Deva + 1272, 190, // hlu -> Hluw + 1276, 415, // hmd -> Plrd + 1280, 50, // hmj -> Bopo + 1284, 50, // hmq -> Bopo + 1288, 10, // hnd -> Arab + 1292, 105, // hne -> Deva + 1296, 195, // hnj -> Hmnp + 1300, 260, // hnj_AU -> Laoo + 1307, 260, // hnj_CN -> Laoo + 1314, 260, // hnj_FR -> Laoo + 1321, 260, // hnj_GF -> Laoo + 1328, 260, // hnj_LA -> Laoo + 1335, 260, // hnj_MM -> Laoo + 1342, 260, // hnj_SR -> Laoo + 1349, 260, // hnj_TH -> Laoo + 1356, 195, // hnj_US -> Hmnp + 1363, 260, // hnj_VN -> Laoo + 1370, 10, // hno -> Arab + 1374, 105, // hoc -> Deva + 1378, 10, // hoh -> Arab + 1382, 105, // hoj -> Deva + 1386, 170, // how -> Hani + 1390, 105, // hoy -> Deva + 1394, 345, // hpo -> Mymr + 1398, 480, // hrt -> Syrc + 1402, 10, // hrz -> Arab + 1406, 175, // hsn -> Hans + 1410, 10, // hss -> Arab + 1414, 575, // htx -> Xsux + 1418, 105, // hut -> Deva + 1422, 185, // huy -> Hebr + 1426, 100, // huz -> Cyrl + 1430, 20, // hy -> Armn + 1433, 20, // hyw -> Armn + 1437, 580, // ii -> Yiii + 1440, 285, // imy -> Lyci + 1444, 100, // inh -> Cyrl + 1448, 345, // int -> Mymr + 1452, 120, // ior -> Ethi + 1456, 505, // iru -> Taml + 1460, 10, // isk -> Arab + 1464, 185, // itk -> Hebr + 1468, 100, // itl -> Cyrl + 1472, 65, // iu -> Cans + 1475, 185, // iw -> Hebr + 1478, 210, // ja -> Jpan + 1481, 10, // jad -> Arab + 1485, 10, // jat -> Arab + 1489, 185, // jbe -> Hebr + 1493, 10, // jbn -> Arab + 1497, 100, // jct -> Cyrl + 1501, 540, // jda -> Tibt + 1505, 10, // jdg -> Arab + 1509, 100, // jdt -> Cyrl + 1513, 105, // jee -> Deva + 1517, 125, // jge -> Geor + 1521, 185, // ji -> Hebr + 1524, 165, // jje -> Hang + 1528, 345, // jkm -> Mymr + 1532, 105, // jml -> Deva + 1536, 490, // jna -> Takr + 1540, 10, // jnd -> Arab + 1544, 105, // jnl -> Deva + 1548, 105, // jns -> Deva + 1552, 10, // jog -> Arab + 1556, 185, // jpa -> Hebr + 1560, 185, // jpr -> Hebr + 1564, 105, // jul -> Deva + 1568, 385, // jun -> Orya + 1572, 385, // juy -> Orya + 1576, 540, // jya -> Tibt + 1580, 185, // jye -> Hebr + 1584, 125, // ka -> Geor + 1587, 100, // kaa -> Cyrl + 1591, 100, // kap -> Cyrl + 1595, 225, // kaw -> Kawi + 1599, 100, // kbd -> Cyrl + 1603, 10, // kbu -> Arab + 1607, 10, // kby -> Arab + 1611, 100, // kca -> Cyrl + 1615, 45, // kdq -> Beng + 1619, 535, // kdt -> Thai + 1623, 100, // ket -> Cyrl + 1627, 105, // kex -> Deva + 1631, 520, // key -> Telu + 1635, 245, // kfa -> Knda + 1639, 105, // kfb -> Deva + 1643, 520, // kfc -> Telu + 1647, 245, // kfd -> Knda + 1651, 505, // kfe -> Taml + 1655, 320, // kfh -> Mlym + 1659, 505, // kfi -> Taml + 1663, 105, // kfk -> Deva + 1667, 10, // kfm -> Arab + 1671, 105, // kfp -> Deva + 1675, 105, // kfq -> Deva + 1679, 105, // kfr -> Deva + 1683, 105, // kfs -> Deva + 1687, 105, // kfx -> Deva + 1691, 105, // kfy -> Deva + 1695, 105, // kgj -> Deva + 1699, 105, // kgy -> Deva + 1703, 500, // khb -> Talu + 1707, 535, // khf -> Thai + 1711, 540, // khg -> Tibt + 1715, 105, // khn -> Deva + 1719, 345, // kht -> Mymr + 1723, 100, // khv -> Cyrl + 1727, 10, // khw -> Arab + 1731, 105, // kif -> Deva + 1735, 100, // kim -> Cyrl + 1739, 105, // kip -> Deva + 1743, 260, // kjg -> Laoo + 1747, 100, // kjh -> Cyrl + 1751, 105, // kjl -> Deva + 1755, 105, // kjo -> Deva + 1759, 345, // kjp -> Mymr + 1763, 535, // kjt -> Thai + 1767, 100, // kk -> Cyrl + 1770, 10, // kk_AF -> Arab + 1776, 10, // kk_CN -> Arab + 1782, 10, // kk_IR -> Arab + 1788, 10, // kk_MN -> Arab + 1794, 540, // kkf -> Tibt + 1798, 255, // kkh -> Lana + 1802, 105, // kkt -> Deva + 1806, 105, // kle -> Deva + 1810, 10, // klj -> Arab + 1814, 105, // klr -> Deva + 1818, 235, // km -> Khmr + 1821, 105, // kmj -> Deva + 1825, 10, // kmz -> Arab + 1829, 245, // kn -> Knda + 1832, 250, // ko -> Kore + 1835, 100, // koi -> Cyrl + 1839, 105, // kok -> Deva + 1843, 100, // kpt -> Cyrl + 1847, 100, // kpy -> Cyrl + 1851, 480, // kqd -> Syrc + 1855, 120, // kqy -> Ethi + 1859, 105, // kra -> Deva + 1863, 100, // krc -> Cyrl + 1867, 100, // krk -> Cyrl + 1871, 235, // krr -> Khmr + 1875, 105, // kru -> Deva + 1879, 235, // krv -> Khmr + 1883, 10, // ks -> Arab + 1886, 345, // ksu -> Mymr + 1890, 345, // ksw -> Mymr + 1894, 105, // ksz -> Deva + 1898, 120, // ktb -> Ethi + 1902, 10, // ktl -> Arab + 1906, 415, // ktp -> Plrd + 1910, 10, // ku_LB -> Arab + 1916, 260, // kuf -> Laoo + 1920, 100, // kum -> Cyrl + 1924, 100, // kv -> Cyrl + 1927, 100, // kva -> Cyrl + 1931, 345, // kvq -> Mymr + 1935, 345, // kvt -> Mymr + 1939, 10, // kvx -> Arab + 1943, 215, // kvy -> Kali + 1947, 120, // kxc -> Ethi + 1951, 345, // kxf -> Mymr + 1955, 345, // kxk -> Mymr + 1959, 105, // kxl -> Deva + 1963, 535, // kxm -> Thai + 1967, 10, // kxp -> Arab + 1971, 385, // kxv -> Orya + 1975, 100, // ky -> Cyrl + 1978, 10, // ky_CN -> Arab + 1984, 215, // kyu -> Kali + 1988, 105, // kyv -> Deva + 1992, 105, // kyw -> Deva + 1996, 10, // kzh -> Arab + 2000, 270, // lab -> Lina + 2004, 185, // lad -> Hebr + 2008, 105, // lae -> Deva + 2012, 10, // lah -> Arab + 2016, 280, // lbc -> Lisu + 2020, 100, // lbe -> Cyrl + 2024, 105, // lbf -> Deva + 2028, 540, // lbj -> Tibt + 2032, 105, // lbm -> Deva + 2036, 260, // lbo -> Laoo + 2040, 105, // lbr -> Deva + 2044, 535, // lcp -> Thai + 2048, 265, // lep -> Lepc + 2052, 100, // lez -> Cyrl + 2056, 105, // lhm -> Deva + 2060, 480, // lhs -> Syrc + 2064, 105, // lif -> Deva + 2068, 280, // lis -> Lisu + 2072, 540, // lkh -> Tibt + 2076, 10, // lki -> Arab + 2080, 105, // lmh -> Deva + 2084, 520, // lmn -> Telu + 2088, 260, // lo -> Laoo + 2091, 105, // loy -> Deva + 2095, 415, // lpo -> Plrd + 2099, 10, // lrc -> Arab + 2103, 10, // lrk -> Arab + 2107, 10, // lrl -> Arab + 2111, 10, // lsa -> Arab + 2115, 185, // lsd -> Hebr + 2119, 10, // lss -> Arab + 2123, 540, // luk -> Tibt + 2127, 105, // luu -> Deva + 2131, 10, // luv -> Arab + 2135, 10, // luz -> Arab + 2139, 535, // lwl -> Thai + 2143, 535, // lwm -> Thai + 2147, 540, // lya -> Tibt + 2151, 175, // lzh -> Hans + 2155, 105, // mag -> Deva + 2159, 105, // mai -> Deva + 2163, 360, // man_GN -> Nkoo + 2170, 10, // mby -> Arab + 2174, 10, // mde -> Arab + 2178, 100, // mdf -> Cyrl + 2182, 120, // mdx -> Ethi + 2186, 120, // mdy -> Ethi + 2190, 10, // mfa -> Arab + 2194, 10, // mfi -> Arab + 2198, 105, // mgp -> Deva + 2202, 10, // mhj -> Arab + 2206, 295, // mid -> Mand + 2210, 105, // mjl -> Deva + 2214, 320, // mjq -> Mlym + 2218, 320, // mjr -> Mlym + 2222, 105, // mjt -> Deva + 2226, 520, // mju -> Telu + 2230, 320, // mjv -> Mlym + 2234, 105, // mjz -> Deva + 2238, 100, // mk -> Cyrl + 2241, 105, // mkb -> Deva + 2245, 105, // mke -> Deva + 2249, 10, // mki -> Arab + 2253, 535, // mkm -> Thai + 2257, 320, // ml -> Mlym + 2260, 535, // mlf -> Thai + 2264, 100, // mn -> Cyrl + 2267, 330, // mn_CN -> Mong + 2273, 45, // mni -> Beng + 2277, 10, // mnj -> Arab + 2281, 100, // mns -> Cyrl + 2285, 345, // mnw -> Mymr + 2289, 535, // mpz -> Thai + 2293, 105, // mr -> Deva + 2296, 535, // mra -> Thai + 2300, 105, // mrd -> Deva + 2304, 100, // mrj -> Cyrl + 2308, 335, // mro -> Mroo + 2312, 105, // mrr -> Deva + 2316, 10, // ms_CC -> Arab + 2322, 100, // mtm -> Cyrl + 2326, 105, // mtr -> Deva + 2330, 100, // mud -> Cyrl + 2334, 540, // muk -> Tibt + 2338, 105, // mut -> Deva + 2342, 505, // muv -> Taml + 2346, 120, // muz -> Ethi + 2350, 330, // mvf -> Mong + 2354, 10, // mvy -> Arab + 2358, 120, // mvz -> Ethi + 2362, 105, // mwr -> Deva + 2366, 345, // mwt -> Mymr + 2370, 195, // mww -> Hmnp + 2374, 345, // my -> Mymr + 2377, 120, // mym -> Ethi + 2381, 100, // myv -> Cyrl + 2385, 295, // myz -> Mand + 2389, 10, // mzn -> Arab + 2393, 175, // nan -> Hans + 2397, 105, // nao -> Deva + 2401, 105, // ncd -> Deva + 2405, 260, // ncq -> Laoo + 2409, 100, // ndf -> Cyrl + 2413, 105, // ne -> Deva + 2416, 100, // neg -> Cyrl + 2420, 540, // neh -> Tibt + 2424, 575, // nei -> Xsux + 2428, 105, // new -> Deva + 2432, 260, // ngt -> Laoo + 2436, 100, // nio -> Cyrl + 2440, 520, // nit -> Telu + 2444, 100, // niv -> Cyrl + 2448, 10, // nli -> Arab + 2452, 10, // nlm -> Arab + 2456, 105, // nlx -> Deva + 2460, 105, // nmm -> Deva + 2464, 565, // nnp -> Wcho + 2468, 255, // nod -> Lana + 2472, 105, // noe -> Deva + 2476, 100, // nog -> Cyrl + 2480, 105, // noi -> Deva + 2484, 435, // non -> Runr + 2488, 580, // nos -> Yiii + 2492, 540, // npb -> Tibt + 2496, 360, // nqo -> Nkoo + 2500, 580, // nsd -> Yiii + 2504, 580, // nsf -> Yiii + 2508, 65, // nsk -> Cans + 2512, 545, // nst -> Tnsa + 2516, 580, // nsv -> Yiii + 2520, 580, // nty -> Yiii + 2524, 10, // ntz -> Arab + 2528, 355, // nwc -> Newa + 2532, 105, // nwx -> Deva + 2536, 535, // nyl -> Thai + 2540, 10, // nyq -> Arab + 2544, 100, // oaa -> Cyrl + 2548, 100, // oac -> Cyrl + 2552, 480, // oar -> Syrc + 2556, 125, // oav -> Geor + 2560, 410, // obm -> Phnx + 2564, 345, // obr -> Mymr + 2568, 10, // odk -> Arab + 2572, 575, // oht -> Xsux + 2576, 65, // oj -> Cans + 2579, 65, // ojs -> Cans + 2583, 165, // okm -> Hang + 2587, 170, // oko -> Hani + 2591, 235, // okz -> Khmr + 2595, 105, // ola -> Deva + 2599, 540, // ole -> Tibt + 2603, 100, // omk -> Cyrl + 2607, 340, // omp -> Mtei + 2611, 325, // omr -> Modi + 2615, 105, // oon -> Deva + 2619, 385, // or -> Orya + 2622, 520, // ort -> Telu + 2626, 10, // oru -> Arab + 2630, 100, // orv -> Cyrl + 2634, 100, // os -> Cyrl + 2637, 390, // osa -> Osge + 2641, 200, // osc -> Ital + 2645, 205, // osi -> Java + 2649, 10, // ota -> Arab + 2653, 540, // otb -> Tibt + 2657, 380, // otk -> Orkh + 2661, 145, // oty -> Gran + 2665, 395, // oui -> Ougr + 2669, 160, // pa -> Guru + 2672, 10, // pa_PK -> Arab + 2678, 405, // pal -> Phli + 2682, 100, // paq -> Cyrl + 2686, 10, // pbt -> Arab + 2690, 235, // pcb -> Khmr + 2694, 345, // pce -> Mymr + 2698, 320, // pcf -> Mlym + 2702, 320, // pcg -> Mlym + 2706, 105, // pch -> Deva + 2710, 105, // pci -> Deva + 2714, 520, // pcj -> Telu + 2718, 385, // peg -> Orya + 2722, 570, // peo -> Xpeo + 2726, 230, // pgd -> Khar + 2730, 105, // pgg -> Deva + 2734, 370, // pgl -> Ogam + 2738, 200, // pgn -> Ital + 2742, 105, // phd -> Deva + 2746, 345, // phk -> Mymr + 2750, 10, // phl -> Arab + 2754, 410, // phn -> Phnx + 2758, 260, // pho -> Laoo + 2762, 10, // phr -> Arab + 2766, 535, // pht -> Thai + 2770, 10, // phv -> Arab + 2774, 105, // phw -> Deva + 2778, 460, // pi -> Sinh + 2781, 55, // pka -> Brah + 2785, 320, // pkr -> Mlym + 2789, 10, // plk -> Arab + 2793, 345, // pll -> Mymr + 2797, 55, // pmh -> Brah + 2801, 150, // pnt -> Grek + 2805, 105, // ppa -> Deva + 2809, 230, // pra -> Khar + 2813, 10, // prc -> Arab + 2817, 10, // prd -> Arab + 2821, 155, // prp -> Gujr + 2825, 535, // prt -> Thai + 2829, 10, // prx -> Arab + 2833, 10, // ps -> Arab + 2836, 10, // psh -> Arab + 2840, 10, // psi -> Arab + 2844, 10, // pst -> Arab + 2848, 105, // pum -> Deva + 2852, 345, // pwo -> Mymr + 2856, 105, // pwr -> Deva + 2860, 535, // pww -> Thai + 2864, 345, // pyx -> Mymr + 2868, 10, // qxq -> Arab + 2872, 105, // raa -> Deva + 2876, 105, // rab -> Deva + 2880, 105, // raf -> Deva + 2884, 45, // rah -> Beng + 2888, 105, // raj -> Deva + 2892, 105, // rav -> Deva + 2896, 345, // rbb -> Mymr + 2900, 10, // rdb -> Arab + 2904, 385, // rei -> Orya + 2908, 430, // rhg -> Rohg + 2912, 105, // rji -> Deva + 2916, 105, // rjs -> Deva + 2920, 235, // rka -> Khmr + 2924, 345, // rki -> Mymr + 2928, 45, // rkt -> Beng + 2932, 20, // rmi -> Armn + 2936, 10, // rmt -> Arab + 2940, 345, // rmz -> Mymr + 2944, 100, // rsk -> Cyrl + 2948, 105, // rtw -> Deva + 2952, 100, // ru -> Cyrl + 2955, 100, // rue -> Cyrl + 2959, 100, // rut -> Cyrl + 2963, 105, // rwr -> Deva + 2967, 220, // ryu -> Kana + 2971, 105, // sa -> Deva + 2974, 100, // sah -> Cyrl + 2978, 440, // sam -> Samr + 2982, 375, // sat -> Olck + 2986, 450, // saz -> Saur + 2990, 10, // sbn -> Arab + 2994, 540, // sbu -> Tibt + 2998, 105, // sck -> Deva + 3002, 10, // scl -> Arab + 3006, 105, // scp -> Deva + 3010, 260, // sct -> Laoo + 3014, 490, // scu -> Takr + 3018, 150, // scx -> Grek + 3022, 10, // sd -> Arab + 3025, 105, // sd_IN -> Deva + 3031, 10, // sdb -> Arab + 3035, 10, // sdf -> Arab + 3039, 10, // sdg -> Arab + 3043, 10, // sdh -> Arab + 3047, 10, // sds -> Arab + 3051, 100, // sel -> Cyrl + 3055, 415, // sfm -> Plrd + 3059, 370, // sga -> Ogam + 3063, 100, // sgh -> Cyrl + 3067, 105, // sgj -> Deva + 3071, 10, // sgr -> Arab + 3075, 540, // sgt -> Tibt + 3079, 120, // sgw -> Ethi + 3083, 10, // sgy -> Arab + 3087, 10, // shd -> Arab + 3091, 525, // shi -> Tfng + 3095, 10, // shm -> Arab + 3099, 345, // shn -> Mymr + 3103, 10, // shu -> Arab + 3107, 10, // shv -> Arab + 3111, 460, // si -> Sinh + 3114, 100, // sia -> Cyrl + 3118, 540, // sip -> Tibt + 3122, 10, // siy -> Arab + 3126, 10, // siz -> Arab + 3130, 100, // sjd -> Cyrl + 3134, 105, // sjp -> Deva + 3138, 100, // sjt -> Cyrl + 3142, 535, // skb -> Thai + 3146, 105, // skj -> Deva + 3150, 10, // skr -> Arab + 3154, 10, // slq -> Arab + 3158, 580, // smh -> Yiii + 3162, 440, // smp -> Samr + 3166, 235, // smu -> Khmr + 3170, 10, // smy -> Arab + 3174, 515, // soa -> Tavt + 3178, 465, // sog -> Sogd + 3182, 105, // soi -> Deva + 3186, 535, // sou -> Thai + 3190, 540, // spt -> Tibt + 3194, 385, // spv -> Orya + 3198, 10, // sqo -> Arab + 3202, 260, // sqq -> Laoo + 3206, 10, // sqt -> Arab + 3210, 100, // sr -> Cyrl + 3213, 470, // srb -> Sora + 3217, 10, // srh -> Arab + 3221, 105, // srx -> Deva + 3225, 10, // srz -> Arab + 3229, 10, // ssh -> Arab + 3233, 260, // sss -> Laoo + 3237, 10, // sts -> Arab + 3241, 120, // stv -> Ethi + 3245, 100, // sty -> Cyrl + 3249, 105, // suz -> Deva + 3253, 125, // sva -> Geor + 3257, 10, // swb -> Arab + 3261, 170, // swi -> Hani + 3265, 105, // swv -> Deva + 3269, 480, // syc -> Syrc + 3273, 45, // syl -> Beng + 3277, 480, // syn -> Syrc + 3281, 480, // syr -> Syrc + 3285, 105, // syw -> Deva + 3289, 505, // ta -> Taml + 3292, 100, // tab -> Cyrl + 3296, 105, // taj -> Deva + 3300, 485, // tbk -> Tagb + 3304, 540, // tcn -> Tibt + 3308, 345, // tco -> Mymr + 3312, 505, // tcx -> Taml + 3316, 245, // tcy -> Knda + 3320, 525, // tda -> Tfng + 3324, 105, // tdb -> Deva + 3328, 495, // tdd -> Tale + 3332, 105, // tdg -> Deva + 3336, 105, // tdh -> Deva + 3340, 520, // te -> Telu + 3343, 205, // tes -> Java + 3347, 100, // tg -> Cyrl + 3350, 10, // tg_PK -> Arab + 3356, 105, // tge -> Deva + 3360, 540, // tgf -> Tibt + 3364, 535, // th -> Thai + 3367, 105, // the -> Deva + 3371, 105, // thf -> Deva + 3375, 495, // thi -> Tale + 3379, 105, // thl -> Deva + 3383, 535, // thm -> Thai + 3387, 105, // thq -> Deva + 3391, 105, // thr -> Deva + 3395, 105, // ths -> Deva + 3399, 120, // ti -> Ethi + 3402, 120, // tig -> Ethi + 3406, 105, // tij -> Deva + 3410, 100, // tin -> Cyrl + 3414, 345, // tjl -> Mymr + 3418, 10, // tjo -> Arab + 3422, 105, // tkb -> Deva + 3426, 10, // tks -> Arab + 3430, 105, // tkt -> Deva + 3434, 105, // tmk -> Deva + 3438, 480, // tmr -> Syrc + 3442, 60, // tnv -> Cakm + 3446, 10, // tov -> Arab + 3450, 235, // tpu -> Khmr + 3454, 10, // tra -> Arab + 3458, 185, // trg -> Hebr + 3462, 10, // trm -> Arab + 3466, 10, // trw -> Arab + 3470, 150, // tsd -> Grek + 3474, 105, // tsf -> Deva + 3478, 540, // tsj -> Tibt + 3482, 100, // tt -> Cyrl + 3485, 260, // tth -> Laoo + 3489, 260, // tto -> Laoo + 3493, 535, // tts -> Thai + 3497, 345, // tvn -> Mymr + 3501, 105, // twm -> Deva + 3505, 510, // txg -> Tang + 3509, 550, // txo -> Toto + 3513, 515, // tyr -> Tavt + 3517, 100, // tyv -> Cyrl + 3521, 100, // ude -> Cyrl + 3525, 320, // udg -> Mlym + 3529, 0, // udi -> Aghb + 3533, 100, // udm -> Cyrl + 3537, 10, // ug -> Arab + 3540, 100, // ug_KZ -> Cyrl + 3546, 100, // ug_MN -> Cyrl + 3552, 555, // uga -> Ugar + 3556, 100, // ugh -> Cyrl + 3560, 535, // ugo -> Thai + 3564, 100, // uk -> Cyrl + 3567, 385, // uki -> Orya + 3571, 100, // ulc -> Cyrl + 3575, 45, // unr -> Beng + 3579, 105, // unr_NP -> Deva + 3586, 45, // unx -> Beng + 3590, 10, // ur -> Arab + 3593, 535, // urk -> Thai + 3597, 10, // ush -> Arab + 3601, 150, // uum -> Grek + 3605, 10, // uz_AF -> Arab + 3611, 100, // uz_CN -> Cyrl + 3617, 10, // uzs -> Arab + 3621, 505, // vaa -> Taml + 3625, 10, // vaf -> Arab + 3629, 105, // vah -> Deva + 3633, 560, // vai -> Vaii + 3637, 105, // vas -> Deva + 3641, 105, // vav -> Deva + 3645, 105, // vay -> Deva + 3649, 10, // vgr -> Arab + 3653, 245, // vmd -> Knda + 3657, 10, // vmh -> Arab + 3661, 120, // wal -> Ethi + 3665, 10, // wbk -> Arab + 3669, 520, // wbq -> Telu + 3673, 105, // wbr -> Deva + 3677, 10, // wlo -> Arab + 3681, 105, // wme -> Deva + 3685, 10, // wne -> Arab + 3689, 10, // wni -> Arab + 3693, 130, // wsg -> Gong + 3697, 10, // wsv -> Arab + 3701, 105, // wtm -> Deva + 3705, 175, // wuu -> Hans + 3709, 100, // xal -> Cyrl + 3713, 120, // xan -> Ethi + 3717, 100, // xas -> Cyrl + 3721, 85, // xco -> Chrs + 3725, 70, // xcr -> Cari + 3729, 100, // xdq -> Cyrl + 3733, 10, // xhe -> Arab + 3737, 235, // xhm -> Khmr + 3741, 385, // xis -> Orya + 3745, 10, // xka -> Arab + 3749, 10, // xkc -> Arab + 3753, 10, // xkj -> Arab + 3757, 10, // xkp -> Arab + 3761, 285, // xlc -> Lyci + 3765, 290, // xld -> Lydi + 3769, 115, // xly -> Elym + 3773, 125, // xmf -> Geor + 3777, 300, // xmn -> Mani + 3781, 315, // xmr -> Merc + 3785, 350, // xna -> Narb + 3789, 105, // xnr -> Deva + 3793, 150, // xpg -> Grek + 3797, 370, // xpi -> Ogam + 3801, 100, // xpm -> Cyrl + 3805, 420, // xpr -> Prti + 3809, 100, // xrm -> Cyrl + 3813, 100, // xrn -> Cyrl + 3817, 445, // xsa -> Sarb + 3821, 105, // xsr -> Deva + 3825, 100, // xss -> Cyrl + 3829, 505, // xub -> Taml + 3833, 505, // xuj -> Taml + 3837, 200, // xve -> Ital + 3841, 10, // xvi -> Arab + 3845, 100, // xwo -> Cyrl + 3849, 305, // xzh -> Marc + 3853, 100, // yai -> Cyrl + 3857, 105, // ybh -> Deva + 3861, 105, // ybi -> Deva + 3865, 10, // ydg -> Arab + 3869, 320, // yea -> Mlym + 3873, 150, // yej -> Grek + 3877, 520, // yeu -> Telu + 3881, 415, // ygp -> Plrd + 3885, 185, // yhd -> Hebr + 3889, 185, // yi -> Hebr + 3892, 580, // yig -> Yiii + 3896, 185, // yih -> Hebr + 3900, 580, // yiv -> Yiii + 3904, 100, // ykg -> Cyrl + 3908, 415, // yna -> Plrd + 3912, 100, // ynk -> Cyrl + 3916, 210, // yoi -> Jpan + 3920, 535, // yoy -> Thai + 3924, 100, // yrk -> Cyrl + 3928, 580, // ysd -> Yiii + 3932, 580, // ysn -> Yiii + 3936, 580, // ysp -> Yiii + 3940, 100, // ysr -> Cyrl + 3944, 415, // ysy -> Plrd + 3948, 185, // yud -> Hebr + 3952, 180, // yue -> Hant + 3956, 175, // yue_CN -> Hans + 3963, 100, // yug -> Cyrl + 3967, 100, // yux -> Cyrl + 3971, 415, // ywq -> Plrd + 3975, 415, // ywu -> Plrd + 3979, 540, // zau -> Tibt + 3983, 10, // zba -> Arab + 3987, 170, // zch -> Hani + 3991, 10, // zdj -> Arab + 3995, 170, // zeh -> Hani + 3999, 525, // zen -> Tfng + 4003, 170, // zgb -> Hani + 4007, 525, // zgh -> Tfng + 4011, 170, // zgm -> Hani + 4015, 170, // zgn -> Hani + 4019, 175, // zh -> Hans + 4022, 180, // zh_AU -> Hant + 4028, 180, // zh_BN -> Hant + 4034, 180, // zh_GB -> Hant + 4040, 180, // zh_GF -> Hant + 4046, 180, // zh_HK -> Hant + 4052, 180, // zh_ID -> Hant + 4058, 180, // zh_MO -> Hant + 4064, 180, // zh_PA -> Hant + 4070, 180, // zh_PF -> Hant + 4076, 180, // zh_PH -> Hant + 4082, 180, // zh_SR -> Hant + 4088, 180, // zh_TH -> Hant + 4094, 180, // zh_TW -> Hant + 4100, 180, // zh_US -> Hant + 4106, 180, // zh_VN -> Hant + 4112, 170, // zhd -> Hani + 4116, 365, // zhx -> Nshu + 4120, 100, // zkb -> Cyrl + 4124, 100, // zko -> Cyrl + 4128, 240, // zkt -> Kits + 4132, 100, // zkz -> Cyrl + 4136, 170, // zlj -> Hani + 4140, 170, // zln -> Hani + 4144, 170, // zlq -> Hani + 4148, 170, // zqe -> Hani + 4152, 185, // zrp -> Hebr + 4156, 10, // zum -> Arab + 4160, 170, // zyg -> Hani + 4164, 170, // zyn -> Hani + 4168, 170, // zzj -> Hani +}; + +//====================================================================== +// Parent locale table +const char parentLocaleChars[] = + "az_Arab\0az_Cyrl\0bal_Latn\0blt_Latn\0bm_Nkoo\0bs_Cyrl\0byn_Latn\0" + "cu_Glag\0dje_Arab\0dyo_Arab\0en_001\0en_150\0en_AG\0en_AI\0en_AT\0" + "en_AU\0en_BB\0en_BE\0en_BM\0en_BS\0en_BW\0en_BZ\0en_CC\0en_CH\0" + "en_CK\0en_CM\0en_CX\0en_CY\0en_DE\0en_DG\0en_DK\0en_DM\0en_Dsrt\0" + "en_ER\0en_FI\0en_FJ\0en_FK\0en_FM\0en_GB\0en_GD\0en_GG\0en_GH\0" + "en_GI\0en_GM\0en_GY\0en_HK\0en_IE\0en_IL\0en_IM\0en_IN\0en_IO\0" + "en_JE\0en_JM\0en_KE\0en_KI\0en_KN\0en_KY\0en_LC\0en_LR\0en_LS\0" + "en_MG\0en_MO\0en_MS\0en_MT\0en_MU\0en_MV\0en_MW\0en_MY\0en_NA\0" + "en_NF\0en_NG\0en_NL\0en_NR\0en_NU\0en_NZ\0en_PG\0en_PK\0en_PN\0" + "en_PW\0en_RW\0en_SB\0en_SC\0en_SD\0en_SE\0en_SG\0en_SH\0en_SI\0" + "en_SL\0en_SS\0en_SX\0en_SZ\0en_Shaw\0en_TC\0en_TK\0en_TO\0en_TT\0" + "en_TV\0en_TZ\0en_UG\0en_VC\0en_VG\0en_VU\0en_WS\0en_ZA\0en_ZM\0" + "en_ZW\0es_419\0es_AR\0es_BO\0es_BR\0es_BZ\0es_CL\0es_CO\0es_CR\0" + "es_CU\0es_DO\0es_EC\0es_GT\0es_HN\0es_MX\0es_NI\0es_PA\0es_PE\0" + "es_PR\0es_PY\0es_SV\0es_US\0es_UY\0es_VE\0ff_Adlm\0ff_Arab\0fr_HT\0" + "ha_Arab\0hi_Latn\0ht\0iu_Latn\0kk_Arab\0ks_Deva\0ku_Arab\0ky_Arab\0" + "ky_Latn\0ml_Arab\0mn_Mong\0mni_Mtei\0ms_Arab\0nb\0nn\0no\0no_NO\0" + "pa_Arab\0pt_AO\0pt_CH\0pt_CV\0pt_FR\0pt_GQ\0pt_GW\0pt_LU\0pt_MO\0" + "pt_MZ\0pt_PT\0pt_ST\0pt_TL\0root\0sat_Deva\0sd_Deva\0sd_Khoj\0" + "sd_Sind\0shi_Latn\0so_Arab\0sr_Latn\0sw_Arab\0tg_Arab\0ug_Cyrl\0" + "uz_Arab\0uz_Cyrl\0vai_Latn\0wo_Arab\0yo_Arab\0yue_Hans\0zh_Hant\0" + "zh_Hant_HK\0zh_Hant_MO\0"; + +const int32_t parentLocaleTable[] = { + 0, 1023, // az_Arab -> root + 8, 1023, // az_Cyrl -> root + 16, 1023, // bal_Latn -> root + 25, 1023, // blt_Latn -> root + 34, 1023, // bm_Nkoo -> root + 42, 1023, // bs_Cyrl -> root + 50, 1023, // byn_Latn -> root + 59, 1023, // cu_Glag -> root + 67, 1023, // dje_Arab -> root + 76, 1023, // dyo_Arab -> root + 92, 85, // en_150 -> en_001 + 99, 85, // en_AG -> en_001 + 105, 85, // en_AI -> en_001 + 111, 92, // en_AT -> en_150 + 117, 85, // en_AU -> en_001 + 123, 85, // en_BB -> en_001 + 129, 92, // en_BE -> en_150 + 135, 85, // en_BM -> en_001 + 141, 85, // en_BS -> en_001 + 147, 85, // en_BW -> en_001 + 153, 85, // en_BZ -> en_001 + 159, 85, // en_CC -> en_001 + 165, 92, // en_CH -> en_150 + 171, 85, // en_CK -> en_001 + 177, 85, // en_CM -> en_001 + 183, 85, // en_CX -> en_001 + 189, 85, // en_CY -> en_001 + 195, 92, // en_DE -> en_150 + 201, 85, // en_DG -> en_001 + 207, 92, // en_DK -> en_150 + 213, 85, // en_DM -> en_001 + 219, 1023, // en_Dsrt -> root + 227, 85, // en_ER -> en_001 + 233, 92, // en_FI -> en_150 + 239, 85, // en_FJ -> en_001 + 245, 85, // en_FK -> en_001 + 251, 85, // en_FM -> en_001 + 257, 85, // en_GB -> en_001 + 263, 85, // en_GD -> en_001 + 269, 85, // en_GG -> en_001 + 275, 85, // en_GH -> en_001 + 281, 85, // en_GI -> en_001 + 287, 85, // en_GM -> en_001 + 293, 85, // en_GY -> en_001 + 299, 85, // en_HK -> en_001 + 305, 85, // en_IE -> en_001 + 311, 85, // en_IL -> en_001 + 317, 85, // en_IM -> en_001 + 323, 85, // en_IN -> en_001 + 329, 85, // en_IO -> en_001 + 335, 85, // en_JE -> en_001 + 341, 85, // en_JM -> en_001 + 347, 85, // en_KE -> en_001 + 353, 85, // en_KI -> en_001 + 359, 85, // en_KN -> en_001 + 365, 85, // en_KY -> en_001 + 371, 85, // en_LC -> en_001 + 377, 85, // en_LR -> en_001 + 383, 85, // en_LS -> en_001 + 389, 85, // en_MG -> en_001 + 395, 85, // en_MO -> en_001 + 401, 85, // en_MS -> en_001 + 407, 85, // en_MT -> en_001 + 413, 85, // en_MU -> en_001 + 419, 85, // en_MV -> en_001 + 425, 85, // en_MW -> en_001 + 431, 85, // en_MY -> en_001 + 437, 85, // en_NA -> en_001 + 443, 85, // en_NF -> en_001 + 449, 85, // en_NG -> en_001 + 455, 92, // en_NL -> en_150 + 461, 85, // en_NR -> en_001 + 467, 85, // en_NU -> en_001 + 473, 85, // en_NZ -> en_001 + 479, 85, // en_PG -> en_001 + 485, 85, // en_PK -> en_001 + 491, 85, // en_PN -> en_001 + 497, 85, // en_PW -> en_001 + 503, 85, // en_RW -> en_001 + 509, 85, // en_SB -> en_001 + 515, 85, // en_SC -> en_001 + 521, 85, // en_SD -> en_001 + 527, 92, // en_SE -> en_150 + 533, 85, // en_SG -> en_001 + 539, 85, // en_SH -> en_001 + 545, 92, // en_SI -> en_150 + 551, 85, // en_SL -> en_001 + 557, 85, // en_SS -> en_001 + 563, 85, // en_SX -> en_001 + 569, 85, // en_SZ -> en_001 + 575, 1023, // en_Shaw -> root + 583, 85, // en_TC -> en_001 + 589, 85, // en_TK -> en_001 + 595, 85, // en_TO -> en_001 + 601, 85, // en_TT -> en_001 + 607, 85, // en_TV -> en_001 + 613, 85, // en_TZ -> en_001 + 619, 85, // en_UG -> en_001 + 625, 85, // en_VC -> en_001 + 631, 85, // en_VG -> en_001 + 637, 85, // en_VU -> en_001 + 643, 85, // en_WS -> en_001 + 649, 85, // en_ZA -> en_001 + 655, 85, // en_ZM -> en_001 + 661, 85, // en_ZW -> en_001 + 674, 667, // es_AR -> es_419 + 680, 667, // es_BO -> es_419 + 686, 667, // es_BR -> es_419 + 692, 667, // es_BZ -> es_419 + 698, 667, // es_CL -> es_419 + 704, 667, // es_CO -> es_419 + 710, 667, // es_CR -> es_419 + 716, 667, // es_CU -> es_419 + 722, 667, // es_DO -> es_419 + 728, 667, // es_EC -> es_419 + 734, 667, // es_GT -> es_419 + 740, 667, // es_HN -> es_419 + 746, 667, // es_MX -> es_419 + 752, 667, // es_NI -> es_419 + 758, 667, // es_PA -> es_419 + 764, 667, // es_PE -> es_419 + 770, 667, // es_PR -> es_419 + 776, 667, // es_PY -> es_419 + 782, 667, // es_SV -> es_419 + 788, 667, // es_US -> es_419 + 794, 667, // es_UY -> es_419 + 800, 667, // es_VE -> es_419 + 806, 1023, // ff_Adlm -> root + 814, 1023, // ff_Arab -> root + 828, 1023, // ha_Arab -> root + 836, 323, // hi_Latn -> en_IN + 844, 822, // ht -> fr_HT + 847, 1023, // iu_Latn -> root + 855, 1023, // kk_Arab -> root + 863, 1023, // ks_Deva -> root + 871, 1023, // ku_Arab -> root + 879, 1023, // ky_Arab -> root + 887, 1023, // ky_Latn -> root + 895, 1023, // ml_Arab -> root + 903, 1023, // mn_Mong -> root + 911, 1023, // mni_Mtei -> root + 920, 1023, // ms_Arab -> root + 928, 934, // nb -> no + 931, 934, // nn -> no + 937, 934, // no_NO -> no + 943, 1023, // pa_Arab -> root + 951, 1005, // pt_AO -> pt_PT + 957, 1005, // pt_CH -> pt_PT + 963, 1005, // pt_CV -> pt_PT + 969, 1005, // pt_FR -> pt_PT + 975, 1005, // pt_GQ -> pt_PT + 981, 1005, // pt_GW -> pt_PT + 987, 1005, // pt_LU -> pt_PT + 993, 1005, // pt_MO -> pt_PT + 999, 1005, // pt_MZ -> pt_PT + 1011, 1005, // pt_ST -> pt_PT + 1017, 1005, // pt_TL -> pt_PT + 1028, 1023, // sat_Deva -> root + 1037, 1023, // sd_Deva -> root + 1045, 1023, // sd_Khoj -> root + 1053, 1023, // sd_Sind -> root + 1061, 1023, // shi_Latn -> root + 1070, 1023, // so_Arab -> root + 1078, 1023, // sr_Latn -> root + 1086, 1023, // sw_Arab -> root + 1094, 1023, // tg_Arab -> root + 1102, 1023, // ug_Cyrl -> root + 1110, 1023, // uz_Arab -> root + 1118, 1023, // uz_Cyrl -> root + 1126, 1023, // vai_Latn -> root + 1135, 1023, // wo_Arab -> root + 1143, 1023, // yo_Arab -> root + 1151, 1023, // yue_Hans -> root + 1160, 1023, // zh_Hant -> root + 1179, 1168, // zh_Hant_MO -> zh_Hant_HK +}; + + +#endif // INCLUDED_FROM_URESBUND_CPP diff --git a/deps/icu-small/source/common/localematcher.cpp b/deps/icu-small/source/common/localematcher.cpp index 2f8664b6f7b158..47fd1b925aa56f 100644 --- a/deps/icu-small/source/common/localematcher.cpp +++ b/deps/icu-small/source/common/localematcher.cpp @@ -1,826 +1,826 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// localematcher.cpp -// created: 2019may08 Markus W. Scherer - -#include "unicode/utypes.h" -#include "unicode/localebuilder.h" -#include "unicode/localematcher.h" -#include "unicode/locid.h" -#include "unicode/stringpiece.h" -#include "unicode/uloc.h" -#include "unicode/uobject.h" -#include "cstring.h" -#include "localeprioritylist.h" -#include "loclikelysubtags.h" -#include "locdistance.h" -#include "lsr.h" -#include "uassert.h" -#include "uhash.h" -#include "ustr_imp.h" -#include "uvector.h" - -#define UND_LSR LSR("und", "", "", LSR::EXPLICIT_LSR) - -/** - * Indicator for the lifetime of desired-locale objects passed into the LocaleMatcher. - * - * @draft ICU 65 - */ -enum ULocMatchLifetime { - /** - * Locale objects are temporary. - * The matcher will make a copy of a locale that will be used beyond one function call. - * - * @draft ICU 65 - */ - ULOCMATCH_TEMPORARY_LOCALES, - /** - * Locale objects are stored at least as long as the matcher is used. - * The matcher will keep only a pointer to a locale that will be used beyond one function call, - * avoiding a copy. - * - * @draft ICU 65 - */ - ULOCMATCH_STORED_LOCALES // TODO: permanent? cached? clone? -}; -#ifndef U_IN_DOXYGEN -typedef enum ULocMatchLifetime ULocMatchLifetime; -#endif - -U_NAMESPACE_BEGIN - -LocaleMatcher::Result::Result(LocaleMatcher::Result &&src) U_NOEXCEPT : - desiredLocale(src.desiredLocale), - supportedLocale(src.supportedLocale), - desiredIndex(src.desiredIndex), - supportedIndex(src.supportedIndex), - desiredIsOwned(src.desiredIsOwned) { - if (desiredIsOwned) { - src.desiredLocale = nullptr; - src.desiredIndex = -1; - src.desiredIsOwned = false; - } -} - -LocaleMatcher::Result::~Result() { - if (desiredIsOwned) { - delete desiredLocale; - } -} - -LocaleMatcher::Result &LocaleMatcher::Result::operator=(LocaleMatcher::Result &&src) U_NOEXCEPT { - this->~Result(); - - desiredLocale = src.desiredLocale; - supportedLocale = src.supportedLocale; - desiredIndex = src.desiredIndex; - supportedIndex = src.supportedIndex; - desiredIsOwned = src.desiredIsOwned; - - if (desiredIsOwned) { - src.desiredLocale = nullptr; - src.desiredIndex = -1; - src.desiredIsOwned = false; - } - return *this; -} - -Locale LocaleMatcher::Result::makeResolvedLocale(UErrorCode &errorCode) const { - if (U_FAILURE(errorCode) || supportedLocale == nullptr) { - return Locale::getRoot(); - } - const Locale *bestDesired = getDesiredLocale(); - if (bestDesired == nullptr || *supportedLocale == *bestDesired) { - return *supportedLocale; - } - LocaleBuilder b; - b.setLocale(*supportedLocale); - - // Copy the region from bestDesired, if there is one. - const char *region = bestDesired->getCountry(); - if (*region != 0) { - b.setRegion(region); - } - - // Copy the variants from bestDesired, if there are any. - // Note that this will override any supportedLocale variants. - // For example, "sco-ulster-fonipa" + "...-fonupa" => "sco-fonupa" (replacing ulster). - const char *variants = bestDesired->getVariant(); - if (*variants != 0) { - b.setVariant(variants); - } - - // Copy the extensions from bestDesired, if there are any. - // C++ note: The following note, copied from Java, may not be true, - // as long as C++ copies by legacy ICU keyword, not by extension singleton. - // Note that this will override any supportedLocale extensions. - // For example, "th-u-nu-latn-ca-buddhist" + "...-u-nu-native" => "th-u-nu-native" - // (replacing calendar). - b.copyExtensionsFrom(*bestDesired, errorCode); - return b.build(errorCode); -} - -LocaleMatcher::Builder::Builder(LocaleMatcher::Builder &&src) U_NOEXCEPT : - errorCode_(src.errorCode_), - supportedLocales_(src.supportedLocales_), - thresholdDistance_(src.thresholdDistance_), - demotion_(src.demotion_), - defaultLocale_(src.defaultLocale_), - withDefault_(src.withDefault_), - favor_(src.favor_), - direction_(src.direction_) { - src.supportedLocales_ = nullptr; - src.defaultLocale_ = nullptr; -} - -LocaleMatcher::Builder::~Builder() { - delete supportedLocales_; - delete defaultLocale_; - delete maxDistanceDesired_; - delete maxDistanceSupported_; -} - -LocaleMatcher::Builder &LocaleMatcher::Builder::operator=(LocaleMatcher::Builder &&src) U_NOEXCEPT { - this->~Builder(); - - errorCode_ = src.errorCode_; - supportedLocales_ = src.supportedLocales_; - thresholdDistance_ = src.thresholdDistance_; - demotion_ = src.demotion_; - defaultLocale_ = src.defaultLocale_; - withDefault_ = src.withDefault_, - favor_ = src.favor_; - direction_ = src.direction_; - - src.supportedLocales_ = nullptr; - src.defaultLocale_ = nullptr; - return *this; -} - -void LocaleMatcher::Builder::clearSupportedLocales() { - if (supportedLocales_ != nullptr) { - supportedLocales_->removeAllElements(); - } -} - -bool LocaleMatcher::Builder::ensureSupportedLocaleVector() { - if (U_FAILURE(errorCode_)) { return false; } - if (supportedLocales_ != nullptr) { return true; } - LocalPointer lpSupportedLocales(new UVector(uprv_deleteUObject, nullptr, errorCode_), errorCode_); - if (U_FAILURE(errorCode_)) { return false; } - supportedLocales_ = lpSupportedLocales.orphan(); - return true; -} - -LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocalesFromListString( - StringPiece locales) { - LocalePriorityList list(locales, errorCode_); - if (U_FAILURE(errorCode_)) { return *this; } - clearSupportedLocales(); - if (!ensureSupportedLocaleVector()) { return *this; } - int32_t length = list.getLengthIncludingRemoved(); - for (int32_t i = 0; i < length; ++i) { - Locale *locale = list.orphanLocaleAt(i); - if (locale == nullptr) { continue; } - supportedLocales_->adoptElement(locale, errorCode_); - if (U_FAILURE(errorCode_)) { - break; - } - } - return *this; -} - -LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocales(Locale::Iterator &locales) { - if (ensureSupportedLocaleVector()) { - clearSupportedLocales(); - while (locales.hasNext() && U_SUCCESS(errorCode_)) { - const Locale &locale = locales.next(); - LocalPointer clone (locale.clone(), errorCode_); - supportedLocales_->adoptElement(clone.orphan(), errorCode_); - } - } - return *this; -} - -LocaleMatcher::Builder &LocaleMatcher::Builder::addSupportedLocale(const Locale &locale) { - if (ensureSupportedLocaleVector()) { - LocalPointer clone(locale.clone(), errorCode_); - supportedLocales_->adoptElement(clone.orphan(), errorCode_); - } - return *this; -} - -LocaleMatcher::Builder &LocaleMatcher::Builder::setNoDefaultLocale() { - if (U_FAILURE(errorCode_)) { return *this; } - delete defaultLocale_; - defaultLocale_ = nullptr; - withDefault_ = false; - return *this; -} - -LocaleMatcher::Builder &LocaleMatcher::Builder::setDefaultLocale(const Locale *defaultLocale) { - if (U_FAILURE(errorCode_)) { return *this; } - Locale *clone = nullptr; - if (defaultLocale != nullptr) { - clone = defaultLocale->clone(); - if (clone == nullptr) { - errorCode_ = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - } - delete defaultLocale_; - defaultLocale_ = clone; - withDefault_ = true; - return *this; -} - -LocaleMatcher::Builder &LocaleMatcher::Builder::setFavorSubtag(ULocMatchFavorSubtag subtag) { - if (U_FAILURE(errorCode_)) { return *this; } - favor_ = subtag; - return *this; -} - -LocaleMatcher::Builder &LocaleMatcher::Builder::setDemotionPerDesiredLocale(ULocMatchDemotion demotion) { - if (U_FAILURE(errorCode_)) { return *this; } - demotion_ = demotion; - return *this; -} - -LocaleMatcher::Builder &LocaleMatcher::Builder::setMaxDistance(const Locale &desired, - const Locale &supported) { - if (U_FAILURE(errorCode_)) { return *this; } - Locale *desiredClone = desired.clone(); - Locale *supportedClone = supported.clone(); - if (desiredClone == nullptr || supportedClone == nullptr) { - delete desiredClone; // in case only one could not be allocated - delete supportedClone; - errorCode_ = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - delete maxDistanceDesired_; - delete maxDistanceSupported_; - maxDistanceDesired_ = desiredClone; - maxDistanceSupported_ = supportedClone; - return *this; -} - -#if 0 -/** - * Internal only! - * - * @param thresholdDistance the thresholdDistance to set, with -1 = default - * @return this Builder object - * @internal - * @deprecated This API is ICU internal only. - */ -@Deprecated -LocaleMatcher::Builder &LocaleMatcher::Builder::internalSetThresholdDistance(int32_t thresholdDistance) { - if (U_FAILURE(errorCode_)) { return *this; } - if (thresholdDistance > 100) { - thresholdDistance = 100; - } - thresholdDistance_ = thresholdDistance; - return *this; -} -#endif - -UBool LocaleMatcher::Builder::copyErrorTo(UErrorCode &outErrorCode) const { - if (U_FAILURE(outErrorCode)) { return true; } - if (U_SUCCESS(errorCode_)) { return false; } - outErrorCode = errorCode_; - return true; -} - -LocaleMatcher LocaleMatcher::Builder::build(UErrorCode &errorCode) const { - if (U_SUCCESS(errorCode) && U_FAILURE(errorCode_)) { - errorCode = errorCode_; - } - return LocaleMatcher(*this, errorCode); -} - -namespace { - -LSR getMaximalLsrOrUnd(const XLikelySubtags &likelySubtags, const Locale &locale, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode) || locale.isBogus() || *locale.getName() == 0 /* "und" */) { - return UND_LSR; - } else { - return likelySubtags.makeMaximizedLsrFrom(locale, errorCode); - } -} - -int32_t hashLSR(const UHashTok token) { - const LSR *lsr = static_cast(token.pointer); - return lsr->hashCode; -} - -UBool compareLSRs(const UHashTok t1, const UHashTok t2) { - const LSR *lsr1 = static_cast(t1.pointer); - const LSR *lsr2 = static_cast(t2.pointer); - return *lsr1 == *lsr2; -} - -} // namespace - -int32_t LocaleMatcher::putIfAbsent(const LSR &lsr, int32_t i, int32_t suppLength, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return suppLength; } - if (!uhash_containsKey(supportedLsrToIndex, &lsr)) { - uhash_putiAllowZero(supportedLsrToIndex, const_cast(&lsr), i, &errorCode); - if (U_SUCCESS(errorCode)) { - supportedLSRs[suppLength] = &lsr; - supportedIndexes[suppLength++] = i; - } - } - return suppLength; -} - -LocaleMatcher::LocaleMatcher(const Builder &builder, UErrorCode &errorCode) : - likelySubtags(*XLikelySubtags::getSingleton(errorCode)), - localeDistance(*LocaleDistance::getSingleton(errorCode)), - thresholdDistance(builder.thresholdDistance_), - demotionPerDesiredLocale(0), - favorSubtag(builder.favor_), - direction(builder.direction_), - supportedLocales(nullptr), lsrs(nullptr), supportedLocalesLength(0), - supportedLsrToIndex(nullptr), - supportedLSRs(nullptr), supportedIndexes(nullptr), supportedLSRsLength(0), - ownedDefaultLocale(nullptr), defaultLocale(nullptr) { - if (U_FAILURE(errorCode)) { return; } - const Locale *def = builder.defaultLocale_; - LSR builderDefaultLSR; - const LSR *defLSR = nullptr; - if (def != nullptr) { - ownedDefaultLocale = def->clone(); - if (ownedDefaultLocale == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - def = ownedDefaultLocale; - builderDefaultLSR = getMaximalLsrOrUnd(likelySubtags, *def, errorCode); - if (U_FAILURE(errorCode)) { return; } - defLSR = &builderDefaultLSR; - } - supportedLocalesLength = builder.supportedLocales_ != nullptr ? - builder.supportedLocales_->size() : 0; - if (supportedLocalesLength > 0) { - // Store the supported locales in input order, - // so that when different types are used (e.g., language tag strings) - // we can return those by parallel index. - supportedLocales = static_cast( - uprv_malloc(supportedLocalesLength * sizeof(const Locale *))); - // Supported LRSs in input order. - // In C++, we store these permanently to simplify ownership management - // in the hash tables. Duplicate LSRs (if any) are unused overhead. - lsrs = new LSR[supportedLocalesLength]; - if (supportedLocales == nullptr || lsrs == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - // If the constructor fails partway, we need null pointers for destructibility. - uprv_memset(supportedLocales, 0, supportedLocalesLength * sizeof(const Locale *)); - for (int32_t i = 0; i < supportedLocalesLength; ++i) { - const Locale &locale = *static_cast(builder.supportedLocales_->elementAt(i)); - supportedLocales[i] = locale.clone(); - if (supportedLocales[i] == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - const Locale &supportedLocale = *supportedLocales[i]; - LSR &lsr = lsrs[i] = getMaximalLsrOrUnd(likelySubtags, supportedLocale, errorCode); - lsr.setHashCode(); - if (U_FAILURE(errorCode)) { return; } - } - - // We need an unordered map from LSR to first supported locale with that LSR, - // and an ordered list of (LSR, supported index) for - // the supported locales in the following order: - // 1. Default locale, if it is supported. - // 2. Priority locales (aka "paradigm locales") in builder order. - // 3. Remaining locales in builder order. - supportedLsrToIndex = uhash_openSize(hashLSR, compareLSRs, uhash_compareLong, - supportedLocalesLength, &errorCode); - if (U_FAILURE(errorCode)) { return; } - supportedLSRs = static_cast( - uprv_malloc(supportedLocalesLength * sizeof(const LSR *))); - supportedIndexes = static_cast( - uprv_malloc(supportedLocalesLength * sizeof(int32_t))); - if (supportedLSRs == nullptr || supportedIndexes == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - int32_t suppLength = 0; - // Determine insertion order. - // Add locales immediately that are equivalent to the default. - MaybeStackArray order(supportedLocalesLength, errorCode); - if (U_FAILURE(errorCode)) { return; } - int32_t numParadigms = 0; - for (int32_t i = 0; i < supportedLocalesLength; ++i) { - const Locale &locale = *supportedLocales[i]; - const LSR &lsr = lsrs[i]; - if (defLSR == nullptr && builder.withDefault_) { - // Implicit default locale = first supported locale, if not turned off. - U_ASSERT(i == 0); - def = &locale; - defLSR = &lsr; - order[i] = 1; - suppLength = putIfAbsent(lsr, 0, suppLength, errorCode); - } else if (defLSR != nullptr && lsr.isEquivalentTo(*defLSR)) { - order[i] = 1; - suppLength = putIfAbsent(lsr, i, suppLength, errorCode); - } else if (localeDistance.isParadigmLSR(lsr)) { - order[i] = 2; - ++numParadigms; - } else { - order[i] = 3; - } - if (U_FAILURE(errorCode)) { return; } - } - // Add supported paradigm locales. - int32_t paradigmLimit = suppLength + numParadigms; - for (int32_t i = 0; i < supportedLocalesLength && suppLength < paradigmLimit; ++i) { - if (order[i] == 2) { - suppLength = putIfAbsent(lsrs[i], i, suppLength, errorCode); - } - } - // Add remaining supported locales. - for (int32_t i = 0; i < supportedLocalesLength; ++i) { - if (order[i] == 3) { - suppLength = putIfAbsent(lsrs[i], i, suppLength, errorCode); - } - } - supportedLSRsLength = suppLength; - // If supportedLSRsLength < supportedLocalesLength then - // we waste as many array slots as there are duplicate supported LSRs, - // but the amount of wasted space is small as long as there are few duplicates. - } - - defaultLocale = def; - - if (builder.demotion_ == ULOCMATCH_DEMOTION_REGION) { - demotionPerDesiredLocale = localeDistance.getDefaultDemotionPerDesiredLocale(); - } - - if (thresholdDistance >= 0) { - // already copied - } else if (builder.maxDistanceDesired_ != nullptr) { - LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, *builder.maxDistanceSupported_, errorCode); - const LSR *pSuppLSR = &suppLSR; - int32_t indexAndDistance = localeDistance.getBestIndexAndDistance( - getMaximalLsrOrUnd(likelySubtags, *builder.maxDistanceDesired_, errorCode), - &pSuppLSR, 1, - LocaleDistance::shiftDistance(100), favorSubtag, direction); - if (U_SUCCESS(errorCode)) { - // +1 for an exclusive threshold from an inclusive max. - thresholdDistance = LocaleDistance::getDistanceFloor(indexAndDistance) + 1; - } else { - thresholdDistance = 0; - } - } else { - thresholdDistance = localeDistance.getDefaultScriptDistance(); - } -} - -LocaleMatcher::LocaleMatcher(LocaleMatcher &&src) U_NOEXCEPT : - likelySubtags(src.likelySubtags), - localeDistance(src.localeDistance), - thresholdDistance(src.thresholdDistance), - demotionPerDesiredLocale(src.demotionPerDesiredLocale), - favorSubtag(src.favorSubtag), - direction(src.direction), - supportedLocales(src.supportedLocales), lsrs(src.lsrs), - supportedLocalesLength(src.supportedLocalesLength), - supportedLsrToIndex(src.supportedLsrToIndex), - supportedLSRs(src.supportedLSRs), - supportedIndexes(src.supportedIndexes), - supportedLSRsLength(src.supportedLSRsLength), - ownedDefaultLocale(src.ownedDefaultLocale), defaultLocale(src.defaultLocale) { - src.supportedLocales = nullptr; - src.lsrs = nullptr; - src.supportedLocalesLength = 0; - src.supportedLsrToIndex = nullptr; - src.supportedLSRs = nullptr; - src.supportedIndexes = nullptr; - src.supportedLSRsLength = 0; - src.ownedDefaultLocale = nullptr; - src.defaultLocale = nullptr; -} - -LocaleMatcher::~LocaleMatcher() { - for (int32_t i = 0; i < supportedLocalesLength; ++i) { - delete supportedLocales[i]; - } - uprv_free(supportedLocales); - delete[] lsrs; - uhash_close(supportedLsrToIndex); - uprv_free(supportedLSRs); - uprv_free(supportedIndexes); - delete ownedDefaultLocale; -} - -LocaleMatcher &LocaleMatcher::operator=(LocaleMatcher &&src) U_NOEXCEPT { - this->~LocaleMatcher(); - - thresholdDistance = src.thresholdDistance; - demotionPerDesiredLocale = src.demotionPerDesiredLocale; - favorSubtag = src.favorSubtag; - direction = src.direction; - supportedLocales = src.supportedLocales; - lsrs = src.lsrs; - supportedLocalesLength = src.supportedLocalesLength; - supportedLsrToIndex = src.supportedLsrToIndex; - supportedLSRs = src.supportedLSRs; - supportedIndexes = src.supportedIndexes; - supportedLSRsLength = src.supportedLSRsLength; - ownedDefaultLocale = src.ownedDefaultLocale; - defaultLocale = src.defaultLocale; - - src.supportedLocales = nullptr; - src.lsrs = nullptr; - src.supportedLocalesLength = 0; - src.supportedLsrToIndex = nullptr; - src.supportedLSRs = nullptr; - src.supportedIndexes = nullptr; - src.supportedLSRsLength = 0; - src.ownedDefaultLocale = nullptr; - src.defaultLocale = nullptr; - return *this; -} - -class LocaleLsrIterator { -public: - LocaleLsrIterator(const XLikelySubtags &likelySubtags, Locale::Iterator &locales, - ULocMatchLifetime lifetime) : - likelySubtags(likelySubtags), locales(locales), lifetime(lifetime) {} - - ~LocaleLsrIterator() { - if (lifetime == ULOCMATCH_TEMPORARY_LOCALES) { - delete remembered; - } - } - - bool hasNext() const { - return locales.hasNext(); - } - - LSR next(UErrorCode &errorCode) { - current = &locales.next(); - return getMaximalLsrOrUnd(likelySubtags, *current, errorCode); - } - - void rememberCurrent(int32_t desiredIndex, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return; } - bestDesiredIndex = desiredIndex; - if (lifetime == ULOCMATCH_STORED_LOCALES) { - remembered = current; - } else { - // ULOCMATCH_TEMPORARY_LOCALES - delete remembered; - remembered = new Locale(*current); - if (remembered == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - } - } - } - - const Locale *orphanRemembered() { - const Locale *rem = remembered; - remembered = nullptr; - return rem; - } - - int32_t getBestDesiredIndex() const { - return bestDesiredIndex; - } - -private: - const XLikelySubtags &likelySubtags; - Locale::Iterator &locales; - ULocMatchLifetime lifetime; - const Locale *current = nullptr, *remembered = nullptr; - int32_t bestDesiredIndex = -1; -}; - -const Locale *LocaleMatcher::getBestMatch(const Locale &desiredLocale, UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { return nullptr; } - int32_t suppIndex = getBestSuppIndex( - getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode), - nullptr, errorCode); - return U_SUCCESS(errorCode) && suppIndex >= 0 ? supportedLocales[suppIndex] : defaultLocale; -} - -const Locale *LocaleMatcher::getBestMatch(Locale::Iterator &desiredLocales, - UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { return nullptr; } - if (!desiredLocales.hasNext()) { - return defaultLocale; - } - LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES); - int32_t suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode); - return U_SUCCESS(errorCode) && suppIndex >= 0 ? supportedLocales[suppIndex] : defaultLocale; -} - -const Locale *LocaleMatcher::getBestMatchForListString( - StringPiece desiredLocaleList, UErrorCode &errorCode) const { - LocalePriorityList list(desiredLocaleList, errorCode); - LocalePriorityList::Iterator iter = list.iterator(); - return getBestMatch(iter, errorCode); -} - -LocaleMatcher::Result LocaleMatcher::getBestMatchResult( - const Locale &desiredLocale, UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { - return Result(nullptr, defaultLocale, -1, -1, false); - } - int32_t suppIndex = getBestSuppIndex( - getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode), - nullptr, errorCode); - if (U_FAILURE(errorCode) || suppIndex < 0) { - return Result(nullptr, defaultLocale, -1, -1, false); - } else { - return Result(&desiredLocale, supportedLocales[suppIndex], 0, suppIndex, false); - } -} - -LocaleMatcher::Result LocaleMatcher::getBestMatchResult( - Locale::Iterator &desiredLocales, UErrorCode &errorCode) const { - if (U_FAILURE(errorCode) || !desiredLocales.hasNext()) { - return Result(nullptr, defaultLocale, -1, -1, false); - } - LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES); - int32_t suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode); - if (U_FAILURE(errorCode) || suppIndex < 0) { - return Result(nullptr, defaultLocale, -1, -1, false); - } else { - return Result(lsrIter.orphanRemembered(), supportedLocales[suppIndex], - lsrIter.getBestDesiredIndex(), suppIndex, true); - } -} - -int32_t LocaleMatcher::getBestSuppIndex(LSR desiredLSR, LocaleLsrIterator *remainingIter, - UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { return -1; } - int32_t desiredIndex = 0; - int32_t bestSupportedLsrIndex = -1; - for (int32_t bestShiftedDistance = LocaleDistance::shiftDistance(thresholdDistance);;) { - // Quick check for exact maximized LSR. - if (supportedLsrToIndex != nullptr) { - desiredLSR.setHashCode(); - UBool found = false; - int32_t suppIndex = uhash_getiAndFound(supportedLsrToIndex, &desiredLSR, &found); - if (found) { - if (remainingIter != nullptr) { - remainingIter->rememberCurrent(desiredIndex, errorCode); - } - return suppIndex; - } - } - int32_t bestIndexAndDistance = localeDistance.getBestIndexAndDistance( - desiredLSR, supportedLSRs, supportedLSRsLength, - bestShiftedDistance, favorSubtag, direction); - if (bestIndexAndDistance >= 0) { - bestShiftedDistance = LocaleDistance::getShiftedDistance(bestIndexAndDistance); - if (remainingIter != nullptr) { - remainingIter->rememberCurrent(desiredIndex, errorCode); - if (U_FAILURE(errorCode)) { return -1; } - } - bestSupportedLsrIndex = LocaleDistance::getIndex(bestIndexAndDistance); - } - if ((bestShiftedDistance -= LocaleDistance::shiftDistance(demotionPerDesiredLocale)) <= 0) { - break; - } - if (remainingIter == nullptr || !remainingIter->hasNext()) { - break; - } - desiredLSR = remainingIter->next(errorCode); - if (U_FAILURE(errorCode)) { return -1; } - ++desiredIndex; - } - if (bestSupportedLsrIndex < 0) { - // no good match - return -1; - } - return supportedIndexes[bestSupportedLsrIndex]; -} - -UBool LocaleMatcher::isMatch(const Locale &desired, const Locale &supported, - UErrorCode &errorCode) const { - LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, supported, errorCode); - if (U_FAILURE(errorCode)) { return 0; } - const LSR *pSuppLSR = &suppLSR; - int32_t indexAndDistance = localeDistance.getBestIndexAndDistance( - getMaximalLsrOrUnd(likelySubtags, desired, errorCode), - &pSuppLSR, 1, - LocaleDistance::shiftDistance(thresholdDistance), favorSubtag, direction); - return indexAndDistance >= 0; -} - -double LocaleMatcher::internalMatch(const Locale &desired, const Locale &supported, UErrorCode &errorCode) const { - // Returns the inverse of the distance: That is, 1-distance(desired, supported). - LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, supported, errorCode); - if (U_FAILURE(errorCode)) { return 0; } - const LSR *pSuppLSR = &suppLSR; - int32_t indexAndDistance = localeDistance.getBestIndexAndDistance( - getMaximalLsrOrUnd(likelySubtags, desired, errorCode), - &pSuppLSR, 1, - LocaleDistance::shiftDistance(thresholdDistance), favorSubtag, direction); - double distance = LocaleDistance::getDistanceDouble(indexAndDistance); - return (100.0 - distance) / 100.0; -} - -U_NAMESPACE_END - -// uloc_acceptLanguage() --------------------------------------------------- *** - -U_NAMESPACE_USE - -namespace { - -class LocaleFromTag { -public: - LocaleFromTag() : locale(Locale::getRoot()) {} - const Locale &operator()(const char *tag) { return locale = Locale(tag); } - -private: - // Store the locale in the converter, rather than return a reference to a temporary, - // or a value which could go out of scope with the caller's reference to it. - Locale locale; -}; - -int32_t acceptLanguage(UEnumeration &supportedLocales, Locale::Iterator &desiredLocales, - char *dest, int32_t capacity, UAcceptResult *acceptResult, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return 0; } - LocaleMatcher::Builder builder; - const char *locString; - while ((locString = uenum_next(&supportedLocales, nullptr, &errorCode)) != nullptr) { - Locale loc(locString); - if (loc.isBogus()) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - builder.addSupportedLocale(loc); - } - LocaleMatcher matcher = builder.build(errorCode); - LocaleMatcher::Result result = matcher.getBestMatchResult(desiredLocales, errorCode); - if (U_FAILURE(errorCode)) { return 0; } - if (result.getDesiredIndex() >= 0) { - if (acceptResult != nullptr) { - *acceptResult = *result.getDesiredLocale() == *result.getSupportedLocale() ? - ULOC_ACCEPT_VALID : ULOC_ACCEPT_FALLBACK; - } - const char *bestStr = result.getSupportedLocale()->getName(); - int32_t bestLength = (int32_t)uprv_strlen(bestStr); - if (bestLength <= capacity) { - uprv_memcpy(dest, bestStr, bestLength); - } - return u_terminateChars(dest, capacity, bestLength, &errorCode); - } else { - if (acceptResult != nullptr) { - *acceptResult = ULOC_ACCEPT_FAILED; - } - return u_terminateChars(dest, capacity, 0, &errorCode); - } -} - -} // namespace - -U_CAPI int32_t U_EXPORT2 -uloc_acceptLanguage(char *result, int32_t resultAvailable, - UAcceptResult *outResult, - const char **acceptList, int32_t acceptListCount, - UEnumeration *availableLocales, - UErrorCode *status) { - if (U_FAILURE(*status)) { return 0; } - if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) || - (acceptList == nullptr ? acceptListCount != 0 : acceptListCount < 0) || - availableLocales == nullptr) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - LocaleFromTag converter; - Locale::ConvertingIterator desiredLocales( - acceptList, acceptList + acceptListCount, converter); - return acceptLanguage(*availableLocales, desiredLocales, - result, resultAvailable, outResult, *status); -} - -U_CAPI int32_t U_EXPORT2 -uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable, - UAcceptResult *outResult, - const char *httpAcceptLanguage, - UEnumeration *availableLocales, - UErrorCode *status) { - if (U_FAILURE(*status)) { return 0; } - if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) || - httpAcceptLanguage == nullptr || availableLocales == nullptr) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - LocalePriorityList list(httpAcceptLanguage, *status); - LocalePriorityList::Iterator desiredLocales = list.iterator(); - return acceptLanguage(*availableLocales, desiredLocales, - result, resultAvailable, outResult, *status); -} +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// localematcher.cpp +// created: 2019may08 Markus W. Scherer + +#include "unicode/utypes.h" +#include "unicode/localebuilder.h" +#include "unicode/localematcher.h" +#include "unicode/locid.h" +#include "unicode/stringpiece.h" +#include "unicode/uloc.h" +#include "unicode/uobject.h" +#include "cstring.h" +#include "localeprioritylist.h" +#include "loclikelysubtags.h" +#include "locdistance.h" +#include "lsr.h" +#include "uassert.h" +#include "uhash.h" +#include "ustr_imp.h" +#include "uvector.h" + +#define UND_LSR LSR("und", "", "", LSR::EXPLICIT_LSR) + +/** + * Indicator for the lifetime of desired-locale objects passed into the LocaleMatcher. + * + * @draft ICU 65 + */ +enum ULocMatchLifetime { + /** + * Locale objects are temporary. + * The matcher will make a copy of a locale that will be used beyond one function call. + * + * @draft ICU 65 + */ + ULOCMATCH_TEMPORARY_LOCALES, + /** + * Locale objects are stored at least as long as the matcher is used. + * The matcher will keep only a pointer to a locale that will be used beyond one function call, + * avoiding a copy. + * + * @draft ICU 65 + */ + ULOCMATCH_STORED_LOCALES // TODO: permanent? cached? clone? +}; +#ifndef U_IN_DOXYGEN +typedef enum ULocMatchLifetime ULocMatchLifetime; +#endif + +U_NAMESPACE_BEGIN + +LocaleMatcher::Result::Result(LocaleMatcher::Result &&src) noexcept : + desiredLocale(src.desiredLocale), + supportedLocale(src.supportedLocale), + desiredIndex(src.desiredIndex), + supportedIndex(src.supportedIndex), + desiredIsOwned(src.desiredIsOwned) { + if (desiredIsOwned) { + src.desiredLocale = nullptr; + src.desiredIndex = -1; + src.desiredIsOwned = false; + } +} + +LocaleMatcher::Result::~Result() { + if (desiredIsOwned) { + delete desiredLocale; + } +} + +LocaleMatcher::Result &LocaleMatcher::Result::operator=(LocaleMatcher::Result &&src) noexcept { + this->~Result(); + + desiredLocale = src.desiredLocale; + supportedLocale = src.supportedLocale; + desiredIndex = src.desiredIndex; + supportedIndex = src.supportedIndex; + desiredIsOwned = src.desiredIsOwned; + + if (desiredIsOwned) { + src.desiredLocale = nullptr; + src.desiredIndex = -1; + src.desiredIsOwned = false; + } + return *this; +} + +Locale LocaleMatcher::Result::makeResolvedLocale(UErrorCode &errorCode) const { + if (U_FAILURE(errorCode) || supportedLocale == nullptr) { + return Locale::getRoot(); + } + const Locale *bestDesired = getDesiredLocale(); + if (bestDesired == nullptr || *supportedLocale == *bestDesired) { + return *supportedLocale; + } + LocaleBuilder b; + b.setLocale(*supportedLocale); + + // Copy the region from bestDesired, if there is one. + const char *region = bestDesired->getCountry(); + if (*region != 0) { + b.setRegion(region); + } + + // Copy the variants from bestDesired, if there are any. + // Note that this will override any supportedLocale variants. + // For example, "sco-ulster-fonipa" + "...-fonupa" => "sco-fonupa" (replacing ulster). + const char *variants = bestDesired->getVariant(); + if (*variants != 0) { + b.setVariant(variants); + } + + // Copy the extensions from bestDesired, if there are any. + // C++ note: The following note, copied from Java, may not be true, + // as long as C++ copies by legacy ICU keyword, not by extension singleton. + // Note that this will override any supportedLocale extensions. + // For example, "th-u-nu-latn-ca-buddhist" + "...-u-nu-native" => "th-u-nu-native" + // (replacing calendar). + b.copyExtensionsFrom(*bestDesired, errorCode); + return b.build(errorCode); +} + +LocaleMatcher::Builder::Builder(LocaleMatcher::Builder &&src) noexcept : + errorCode_(src.errorCode_), + supportedLocales_(src.supportedLocales_), + thresholdDistance_(src.thresholdDistance_), + demotion_(src.demotion_), + defaultLocale_(src.defaultLocale_), + withDefault_(src.withDefault_), + favor_(src.favor_), + direction_(src.direction_) { + src.supportedLocales_ = nullptr; + src.defaultLocale_ = nullptr; +} + +LocaleMatcher::Builder::~Builder() { + delete supportedLocales_; + delete defaultLocale_; + delete maxDistanceDesired_; + delete maxDistanceSupported_; +} + +LocaleMatcher::Builder &LocaleMatcher::Builder::operator=(LocaleMatcher::Builder &&src) noexcept { + this->~Builder(); + + errorCode_ = src.errorCode_; + supportedLocales_ = src.supportedLocales_; + thresholdDistance_ = src.thresholdDistance_; + demotion_ = src.demotion_; + defaultLocale_ = src.defaultLocale_; + withDefault_ = src.withDefault_, + favor_ = src.favor_; + direction_ = src.direction_; + + src.supportedLocales_ = nullptr; + src.defaultLocale_ = nullptr; + return *this; +} + +void LocaleMatcher::Builder::clearSupportedLocales() { + if (supportedLocales_ != nullptr) { + supportedLocales_->removeAllElements(); + } +} + +bool LocaleMatcher::Builder::ensureSupportedLocaleVector() { + if (U_FAILURE(errorCode_)) { return false; } + if (supportedLocales_ != nullptr) { return true; } + LocalPointer lpSupportedLocales(new UVector(uprv_deleteUObject, nullptr, errorCode_), errorCode_); + if (U_FAILURE(errorCode_)) { return false; } + supportedLocales_ = lpSupportedLocales.orphan(); + return true; +} + +LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocalesFromListString( + StringPiece locales) { + LocalePriorityList list(locales, errorCode_); + if (U_FAILURE(errorCode_)) { return *this; } + clearSupportedLocales(); + if (!ensureSupportedLocaleVector()) { return *this; } + int32_t length = list.getLengthIncludingRemoved(); + for (int32_t i = 0; i < length; ++i) { + Locale *locale = list.orphanLocaleAt(i); + if (locale == nullptr) { continue; } + supportedLocales_->adoptElement(locale, errorCode_); + if (U_FAILURE(errorCode_)) { + break; + } + } + return *this; +} + +LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocales(Locale::Iterator &locales) { + if (ensureSupportedLocaleVector()) { + clearSupportedLocales(); + while (locales.hasNext() && U_SUCCESS(errorCode_)) { + const Locale &locale = locales.next(); + LocalPointer clone (locale.clone(), errorCode_); + supportedLocales_->adoptElement(clone.orphan(), errorCode_); + } + } + return *this; +} + +LocaleMatcher::Builder &LocaleMatcher::Builder::addSupportedLocale(const Locale &locale) { + if (ensureSupportedLocaleVector()) { + LocalPointer clone(locale.clone(), errorCode_); + supportedLocales_->adoptElement(clone.orphan(), errorCode_); + } + return *this; +} + +LocaleMatcher::Builder &LocaleMatcher::Builder::setNoDefaultLocale() { + if (U_FAILURE(errorCode_)) { return *this; } + delete defaultLocale_; + defaultLocale_ = nullptr; + withDefault_ = false; + return *this; +} + +LocaleMatcher::Builder &LocaleMatcher::Builder::setDefaultLocale(const Locale *defaultLocale) { + if (U_FAILURE(errorCode_)) { return *this; } + Locale *clone = nullptr; + if (defaultLocale != nullptr) { + clone = defaultLocale->clone(); + if (clone == nullptr) { + errorCode_ = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + } + delete defaultLocale_; + defaultLocale_ = clone; + withDefault_ = true; + return *this; +} + +LocaleMatcher::Builder &LocaleMatcher::Builder::setFavorSubtag(ULocMatchFavorSubtag subtag) { + if (U_FAILURE(errorCode_)) { return *this; } + favor_ = subtag; + return *this; +} + +LocaleMatcher::Builder &LocaleMatcher::Builder::setDemotionPerDesiredLocale(ULocMatchDemotion demotion) { + if (U_FAILURE(errorCode_)) { return *this; } + demotion_ = demotion; + return *this; +} + +LocaleMatcher::Builder &LocaleMatcher::Builder::setMaxDistance(const Locale &desired, + const Locale &supported) { + if (U_FAILURE(errorCode_)) { return *this; } + Locale *desiredClone = desired.clone(); + Locale *supportedClone = supported.clone(); + if (desiredClone == nullptr || supportedClone == nullptr) { + delete desiredClone; // in case only one could not be allocated + delete supportedClone; + errorCode_ = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + delete maxDistanceDesired_; + delete maxDistanceSupported_; + maxDistanceDesired_ = desiredClone; + maxDistanceSupported_ = supportedClone; + return *this; +} + +#if 0 +/** + * Internal only! + * + * @param thresholdDistance the thresholdDistance to set, with -1 = default + * @return this Builder object + * @internal + * @deprecated This API is ICU internal only. + */ +@Deprecated +LocaleMatcher::Builder &LocaleMatcher::Builder::internalSetThresholdDistance(int32_t thresholdDistance) { + if (U_FAILURE(errorCode_)) { return *this; } + if (thresholdDistance > 100) { + thresholdDistance = 100; + } + thresholdDistance_ = thresholdDistance; + return *this; +} +#endif + +UBool LocaleMatcher::Builder::copyErrorTo(UErrorCode &outErrorCode) const { + if (U_FAILURE(outErrorCode)) { return true; } + if (U_SUCCESS(errorCode_)) { return false; } + outErrorCode = errorCode_; + return true; +} + +LocaleMatcher LocaleMatcher::Builder::build(UErrorCode &errorCode) const { + if (U_SUCCESS(errorCode) && U_FAILURE(errorCode_)) { + errorCode = errorCode_; + } + return LocaleMatcher(*this, errorCode); +} + +namespace { + +LSR getMaximalLsrOrUnd(const XLikelySubtags &likelySubtags, const Locale &locale, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode) || locale.isBogus() || *locale.getName() == 0 /* "und" */) { + return UND_LSR; + } else { + return likelySubtags.makeMaximizedLsrFrom(locale, errorCode); + } +} + +int32_t hashLSR(const UHashTok token) { + const LSR *lsr = static_cast(token.pointer); + return lsr->hashCode; +} + +UBool compareLSRs(const UHashTok t1, const UHashTok t2) { + const LSR *lsr1 = static_cast(t1.pointer); + const LSR *lsr2 = static_cast(t2.pointer); + return *lsr1 == *lsr2; +} + +} // namespace + +int32_t LocaleMatcher::putIfAbsent(const LSR &lsr, int32_t i, int32_t suppLength, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return suppLength; } + if (!uhash_containsKey(supportedLsrToIndex, &lsr)) { + uhash_putiAllowZero(supportedLsrToIndex, const_cast(&lsr), i, &errorCode); + if (U_SUCCESS(errorCode)) { + supportedLSRs[suppLength] = &lsr; + supportedIndexes[suppLength++] = i; + } + } + return suppLength; +} + +LocaleMatcher::LocaleMatcher(const Builder &builder, UErrorCode &errorCode) : + likelySubtags(*XLikelySubtags::getSingleton(errorCode)), + localeDistance(*LocaleDistance::getSingleton(errorCode)), + thresholdDistance(builder.thresholdDistance_), + demotionPerDesiredLocale(0), + favorSubtag(builder.favor_), + direction(builder.direction_), + supportedLocales(nullptr), lsrs(nullptr), supportedLocalesLength(0), + supportedLsrToIndex(nullptr), + supportedLSRs(nullptr), supportedIndexes(nullptr), supportedLSRsLength(0), + ownedDefaultLocale(nullptr), defaultLocale(nullptr) { + if (U_FAILURE(errorCode)) { return; } + const Locale *def = builder.defaultLocale_; + LSR builderDefaultLSR; + const LSR *defLSR = nullptr; + if (def != nullptr) { + ownedDefaultLocale = def->clone(); + if (ownedDefaultLocale == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + def = ownedDefaultLocale; + builderDefaultLSR = getMaximalLsrOrUnd(likelySubtags, *def, errorCode); + if (U_FAILURE(errorCode)) { return; } + defLSR = &builderDefaultLSR; + } + supportedLocalesLength = builder.supportedLocales_ != nullptr ? + builder.supportedLocales_->size() : 0; + if (supportedLocalesLength > 0) { + // Store the supported locales in input order, + // so that when different types are used (e.g., language tag strings) + // we can return those by parallel index. + supportedLocales = static_cast( + uprv_malloc(supportedLocalesLength * sizeof(const Locale *))); + // Supported LRSs in input order. + // In C++, we store these permanently to simplify ownership management + // in the hash tables. Duplicate LSRs (if any) are unused overhead. + lsrs = new LSR[supportedLocalesLength]; + if (supportedLocales == nullptr || lsrs == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + // If the constructor fails partway, we need null pointers for destructibility. + uprv_memset(supportedLocales, 0, supportedLocalesLength * sizeof(const Locale *)); + for (int32_t i = 0; i < supportedLocalesLength; ++i) { + const Locale &locale = *static_cast(builder.supportedLocales_->elementAt(i)); + supportedLocales[i] = locale.clone(); + if (supportedLocales[i] == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + const Locale &supportedLocale = *supportedLocales[i]; + LSR &lsr = lsrs[i] = getMaximalLsrOrUnd(likelySubtags, supportedLocale, errorCode); + lsr.setHashCode(); + if (U_FAILURE(errorCode)) { return; } + } + + // We need an unordered map from LSR to first supported locale with that LSR, + // and an ordered list of (LSR, supported index) for + // the supported locales in the following order: + // 1. Default locale, if it is supported. + // 2. Priority locales (aka "paradigm locales") in builder order. + // 3. Remaining locales in builder order. + supportedLsrToIndex = uhash_openSize(hashLSR, compareLSRs, uhash_compareLong, + supportedLocalesLength, &errorCode); + if (U_FAILURE(errorCode)) { return; } + supportedLSRs = static_cast( + uprv_malloc(supportedLocalesLength * sizeof(const LSR *))); + supportedIndexes = static_cast( + uprv_malloc(supportedLocalesLength * sizeof(int32_t))); + if (supportedLSRs == nullptr || supportedIndexes == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + int32_t suppLength = 0; + // Determine insertion order. + // Add locales immediately that are equivalent to the default. + MaybeStackArray order(supportedLocalesLength, errorCode); + if (U_FAILURE(errorCode)) { return; } + int32_t numParadigms = 0; + for (int32_t i = 0; i < supportedLocalesLength; ++i) { + const Locale &locale = *supportedLocales[i]; + const LSR &lsr = lsrs[i]; + if (defLSR == nullptr && builder.withDefault_) { + // Implicit default locale = first supported locale, if not turned off. + U_ASSERT(i == 0); + def = &locale; + defLSR = &lsr; + order[i] = 1; + suppLength = putIfAbsent(lsr, 0, suppLength, errorCode); + } else if (defLSR != nullptr && lsr.isEquivalentTo(*defLSR)) { + order[i] = 1; + suppLength = putIfAbsent(lsr, i, suppLength, errorCode); + } else if (localeDistance.isParadigmLSR(lsr)) { + order[i] = 2; + ++numParadigms; + } else { + order[i] = 3; + } + if (U_FAILURE(errorCode)) { return; } + } + // Add supported paradigm locales. + int32_t paradigmLimit = suppLength + numParadigms; + for (int32_t i = 0; i < supportedLocalesLength && suppLength < paradigmLimit; ++i) { + if (order[i] == 2) { + suppLength = putIfAbsent(lsrs[i], i, suppLength, errorCode); + } + } + // Add remaining supported locales. + for (int32_t i = 0; i < supportedLocalesLength; ++i) { + if (order[i] == 3) { + suppLength = putIfAbsent(lsrs[i], i, suppLength, errorCode); + } + } + supportedLSRsLength = suppLength; + // If supportedLSRsLength < supportedLocalesLength then + // we waste as many array slots as there are duplicate supported LSRs, + // but the amount of wasted space is small as long as there are few duplicates. + } + + defaultLocale = def; + + if (builder.demotion_ == ULOCMATCH_DEMOTION_REGION) { + demotionPerDesiredLocale = localeDistance.getDefaultDemotionPerDesiredLocale(); + } + + if (thresholdDistance >= 0) { + // already copied + } else if (builder.maxDistanceDesired_ != nullptr) { + LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, *builder.maxDistanceSupported_, errorCode); + const LSR *pSuppLSR = &suppLSR; + int32_t indexAndDistance = localeDistance.getBestIndexAndDistance( + getMaximalLsrOrUnd(likelySubtags, *builder.maxDistanceDesired_, errorCode), + &pSuppLSR, 1, + LocaleDistance::shiftDistance(100), favorSubtag, direction); + if (U_SUCCESS(errorCode)) { + // +1 for an exclusive threshold from an inclusive max. + thresholdDistance = LocaleDistance::getDistanceFloor(indexAndDistance) + 1; + } else { + thresholdDistance = 0; + } + } else { + thresholdDistance = localeDistance.getDefaultScriptDistance(); + } +} + +LocaleMatcher::LocaleMatcher(LocaleMatcher &&src) noexcept : + likelySubtags(src.likelySubtags), + localeDistance(src.localeDistance), + thresholdDistance(src.thresholdDistance), + demotionPerDesiredLocale(src.demotionPerDesiredLocale), + favorSubtag(src.favorSubtag), + direction(src.direction), + supportedLocales(src.supportedLocales), lsrs(src.lsrs), + supportedLocalesLength(src.supportedLocalesLength), + supportedLsrToIndex(src.supportedLsrToIndex), + supportedLSRs(src.supportedLSRs), + supportedIndexes(src.supportedIndexes), + supportedLSRsLength(src.supportedLSRsLength), + ownedDefaultLocale(src.ownedDefaultLocale), defaultLocale(src.defaultLocale) { + src.supportedLocales = nullptr; + src.lsrs = nullptr; + src.supportedLocalesLength = 0; + src.supportedLsrToIndex = nullptr; + src.supportedLSRs = nullptr; + src.supportedIndexes = nullptr; + src.supportedLSRsLength = 0; + src.ownedDefaultLocale = nullptr; + src.defaultLocale = nullptr; +} + +LocaleMatcher::~LocaleMatcher() { + for (int32_t i = 0; i < supportedLocalesLength; ++i) { + delete supportedLocales[i]; + } + uprv_free(supportedLocales); + delete[] lsrs; + uhash_close(supportedLsrToIndex); + uprv_free(supportedLSRs); + uprv_free(supportedIndexes); + delete ownedDefaultLocale; +} + +LocaleMatcher &LocaleMatcher::operator=(LocaleMatcher &&src) noexcept { + this->~LocaleMatcher(); + + thresholdDistance = src.thresholdDistance; + demotionPerDesiredLocale = src.demotionPerDesiredLocale; + favorSubtag = src.favorSubtag; + direction = src.direction; + supportedLocales = src.supportedLocales; + lsrs = src.lsrs; + supportedLocalesLength = src.supportedLocalesLength; + supportedLsrToIndex = src.supportedLsrToIndex; + supportedLSRs = src.supportedLSRs; + supportedIndexes = src.supportedIndexes; + supportedLSRsLength = src.supportedLSRsLength; + ownedDefaultLocale = src.ownedDefaultLocale; + defaultLocale = src.defaultLocale; + + src.supportedLocales = nullptr; + src.lsrs = nullptr; + src.supportedLocalesLength = 0; + src.supportedLsrToIndex = nullptr; + src.supportedLSRs = nullptr; + src.supportedIndexes = nullptr; + src.supportedLSRsLength = 0; + src.ownedDefaultLocale = nullptr; + src.defaultLocale = nullptr; + return *this; +} + +class LocaleLsrIterator { +public: + LocaleLsrIterator(const XLikelySubtags &likelySubtags, Locale::Iterator &locales, + ULocMatchLifetime lifetime) : + likelySubtags(likelySubtags), locales(locales), lifetime(lifetime) {} + + ~LocaleLsrIterator() { + if (lifetime == ULOCMATCH_TEMPORARY_LOCALES) { + delete remembered; + } + } + + bool hasNext() const { + return locales.hasNext(); + } + + LSR next(UErrorCode &errorCode) { + current = &locales.next(); + return getMaximalLsrOrUnd(likelySubtags, *current, errorCode); + } + + void rememberCurrent(int32_t desiredIndex, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return; } + bestDesiredIndex = desiredIndex; + if (lifetime == ULOCMATCH_STORED_LOCALES) { + remembered = current; + } else { + // ULOCMATCH_TEMPORARY_LOCALES + delete remembered; + remembered = new Locale(*current); + if (remembered == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + } + } + } + + const Locale *orphanRemembered() { + const Locale *rem = remembered; + remembered = nullptr; + return rem; + } + + int32_t getBestDesiredIndex() const { + return bestDesiredIndex; + } + +private: + const XLikelySubtags &likelySubtags; + Locale::Iterator &locales; + ULocMatchLifetime lifetime; + const Locale *current = nullptr, *remembered = nullptr; + int32_t bestDesiredIndex = -1; +}; + +const Locale *LocaleMatcher::getBestMatch(const Locale &desiredLocale, UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { return nullptr; } + int32_t suppIndex = getBestSuppIndex( + getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode), + nullptr, errorCode); + return U_SUCCESS(errorCode) && suppIndex >= 0 ? supportedLocales[suppIndex] : defaultLocale; +} + +const Locale *LocaleMatcher::getBestMatch(Locale::Iterator &desiredLocales, + UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { return nullptr; } + if (!desiredLocales.hasNext()) { + return defaultLocale; + } + LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES); + int32_t suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode); + return U_SUCCESS(errorCode) && suppIndex >= 0 ? supportedLocales[suppIndex] : defaultLocale; +} + +const Locale *LocaleMatcher::getBestMatchForListString( + StringPiece desiredLocaleList, UErrorCode &errorCode) const { + LocalePriorityList list(desiredLocaleList, errorCode); + LocalePriorityList::Iterator iter = list.iterator(); + return getBestMatch(iter, errorCode); +} + +LocaleMatcher::Result LocaleMatcher::getBestMatchResult( + const Locale &desiredLocale, UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { + return Result(nullptr, defaultLocale, -1, -1, false); + } + int32_t suppIndex = getBestSuppIndex( + getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode), + nullptr, errorCode); + if (U_FAILURE(errorCode) || suppIndex < 0) { + return Result(nullptr, defaultLocale, -1, -1, false); + } else { + return Result(&desiredLocale, supportedLocales[suppIndex], 0, suppIndex, false); + } +} + +LocaleMatcher::Result LocaleMatcher::getBestMatchResult( + Locale::Iterator &desiredLocales, UErrorCode &errorCode) const { + if (U_FAILURE(errorCode) || !desiredLocales.hasNext()) { + return Result(nullptr, defaultLocale, -1, -1, false); + } + LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES); + int32_t suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode); + if (U_FAILURE(errorCode) || suppIndex < 0) { + return Result(nullptr, defaultLocale, -1, -1, false); + } else { + return Result(lsrIter.orphanRemembered(), supportedLocales[suppIndex], + lsrIter.getBestDesiredIndex(), suppIndex, true); + } +} + +int32_t LocaleMatcher::getBestSuppIndex(LSR desiredLSR, LocaleLsrIterator *remainingIter, + UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { return -1; } + int32_t desiredIndex = 0; + int32_t bestSupportedLsrIndex = -1; + for (int32_t bestShiftedDistance = LocaleDistance::shiftDistance(thresholdDistance);;) { + // Quick check for exact maximized LSR. + if (supportedLsrToIndex != nullptr) { + desiredLSR.setHashCode(); + UBool found = false; + int32_t suppIndex = uhash_getiAndFound(supportedLsrToIndex, &desiredLSR, &found); + if (found) { + if (remainingIter != nullptr) { + remainingIter->rememberCurrent(desiredIndex, errorCode); + } + return suppIndex; + } + } + int32_t bestIndexAndDistance = localeDistance.getBestIndexAndDistance( + desiredLSR, supportedLSRs, supportedLSRsLength, + bestShiftedDistance, favorSubtag, direction); + if (bestIndexAndDistance >= 0) { + bestShiftedDistance = LocaleDistance::getShiftedDistance(bestIndexAndDistance); + if (remainingIter != nullptr) { + remainingIter->rememberCurrent(desiredIndex, errorCode); + if (U_FAILURE(errorCode)) { return -1; } + } + bestSupportedLsrIndex = LocaleDistance::getIndex(bestIndexAndDistance); + } + if ((bestShiftedDistance -= LocaleDistance::shiftDistance(demotionPerDesiredLocale)) <= 0) { + break; + } + if (remainingIter == nullptr || !remainingIter->hasNext()) { + break; + } + desiredLSR = remainingIter->next(errorCode); + if (U_FAILURE(errorCode)) { return -1; } + ++desiredIndex; + } + if (bestSupportedLsrIndex < 0) { + // no good match + return -1; + } + return supportedIndexes[bestSupportedLsrIndex]; +} + +UBool LocaleMatcher::isMatch(const Locale &desired, const Locale &supported, + UErrorCode &errorCode) const { + LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, supported, errorCode); + if (U_FAILURE(errorCode)) { return 0; } + const LSR *pSuppLSR = &suppLSR; + int32_t indexAndDistance = localeDistance.getBestIndexAndDistance( + getMaximalLsrOrUnd(likelySubtags, desired, errorCode), + &pSuppLSR, 1, + LocaleDistance::shiftDistance(thresholdDistance), favorSubtag, direction); + return indexAndDistance >= 0; +} + +double LocaleMatcher::internalMatch(const Locale &desired, const Locale &supported, UErrorCode &errorCode) const { + // Returns the inverse of the distance: That is, 1-distance(desired, supported). + LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, supported, errorCode); + if (U_FAILURE(errorCode)) { return 0; } + const LSR *pSuppLSR = &suppLSR; + int32_t indexAndDistance = localeDistance.getBestIndexAndDistance( + getMaximalLsrOrUnd(likelySubtags, desired, errorCode), + &pSuppLSR, 1, + LocaleDistance::shiftDistance(thresholdDistance), favorSubtag, direction); + double distance = LocaleDistance::getDistanceDouble(indexAndDistance); + return (100.0 - distance) / 100.0; +} + +U_NAMESPACE_END + +// uloc_acceptLanguage() --------------------------------------------------- *** + +U_NAMESPACE_USE + +namespace { + +class LocaleFromTag { +public: + LocaleFromTag() : locale(Locale::getRoot()) {} + const Locale &operator()(const char *tag) { return locale = Locale(tag); } + +private: + // Store the locale in the converter, rather than return a reference to a temporary, + // or a value which could go out of scope with the caller's reference to it. + Locale locale; +}; + +int32_t acceptLanguage(UEnumeration &supportedLocales, Locale::Iterator &desiredLocales, + char *dest, int32_t capacity, UAcceptResult *acceptResult, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return 0; } + LocaleMatcher::Builder builder; + const char *locString; + while ((locString = uenum_next(&supportedLocales, nullptr, &errorCode)) != nullptr) { + Locale loc(locString); + if (loc.isBogus()) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + builder.addSupportedLocale(loc); + } + LocaleMatcher matcher = builder.build(errorCode); + LocaleMatcher::Result result = matcher.getBestMatchResult(desiredLocales, errorCode); + if (U_FAILURE(errorCode)) { return 0; } + if (result.getDesiredIndex() >= 0) { + if (acceptResult != nullptr) { + *acceptResult = *result.getDesiredLocale() == *result.getSupportedLocale() ? + ULOC_ACCEPT_VALID : ULOC_ACCEPT_FALLBACK; + } + const char *bestStr = result.getSupportedLocale()->getName(); + int32_t bestLength = (int32_t)uprv_strlen(bestStr); + if (bestLength <= capacity) { + uprv_memcpy(dest, bestStr, bestLength); + } + return u_terminateChars(dest, capacity, bestLength, &errorCode); + } else { + if (acceptResult != nullptr) { + *acceptResult = ULOC_ACCEPT_FAILED; + } + return u_terminateChars(dest, capacity, 0, &errorCode); + } +} + +} // namespace + +U_CAPI int32_t U_EXPORT2 +uloc_acceptLanguage(char *result, int32_t resultAvailable, + UAcceptResult *outResult, + const char **acceptList, int32_t acceptListCount, + UEnumeration *availableLocales, + UErrorCode *status) { + if (U_FAILURE(*status)) { return 0; } + if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) || + (acceptList == nullptr ? acceptListCount != 0 : acceptListCount < 0) || + availableLocales == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + LocaleFromTag converter; + Locale::ConvertingIterator desiredLocales( + acceptList, acceptList + acceptListCount, converter); + return acceptLanguage(*availableLocales, desiredLocales, + result, resultAvailable, outResult, *status); +} + +U_CAPI int32_t U_EXPORT2 +uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable, + UAcceptResult *outResult, + const char *httpAcceptLanguage, + UEnumeration *availableLocales, + UErrorCode *status) { + if (U_FAILURE(*status)) { return 0; } + if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) || + httpAcceptLanguage == nullptr || availableLocales == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + LocalePriorityList list(httpAcceptLanguage, *status); + LocalePriorityList::Iterator desiredLocales = list.iterator(); + return acceptLanguage(*availableLocales, desiredLocales, + result, resultAvailable, outResult, *status); +} diff --git a/deps/icu-small/source/common/localeprioritylist.cpp b/deps/icu-small/source/common/localeprioritylist.cpp index e5ba0a3c777aea..e323e394f58f79 100644 --- a/deps/icu-small/source/common/localeprioritylist.cpp +++ b/deps/icu-small/source/common/localeprioritylist.cpp @@ -1,240 +1,240 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// localeprioritylist.cpp -// created: 2019jul11 Markus W. Scherer - -#include "unicode/utypes.h" -#include "unicode/localpointer.h" -#include "unicode/locid.h" -#include "unicode/stringpiece.h" -#include "unicode/uobject.h" -#include "charstr.h" -#include "cmemory.h" -#include "localeprioritylist.h" -#include "uarrsort.h" -#include "uassert.h" -#include "uhash.h" - -U_NAMESPACE_BEGIN - -namespace { - -int32_t hashLocale(const UHashTok token) { - auto *locale = static_cast(token.pointer); - return locale->hashCode(); -} - -UBool compareLocales(const UHashTok t1, const UHashTok t2) { - auto *l1 = static_cast(t1.pointer); - auto *l2 = static_cast(t2.pointer); - return *l1 == *l2; -} - -constexpr int32_t WEIGHT_ONE = 1000; - -struct LocaleAndWeight { - Locale *locale; - int32_t weight; // 0..1000 = 0.0..1.0 - int32_t index; // force stable sort - - int32_t compare(const LocaleAndWeight &other) const { - int32_t diff = other.weight - weight; // descending: other-this - if (diff != 0) { return diff; } - return index - other.index; - } -}; - -int32_t U_CALLCONV -compareLocaleAndWeight(const void * /*context*/, const void *left, const void *right) { - return static_cast(left)-> - compare(*static_cast(right)); -} - -const char *skipSpaces(const char *p, const char *limit) { - while (p < limit && *p == ' ') { ++p; } - return p; -} - -int32_t findTagLength(const char *p, const char *limit) { - // Look for accept-language delimiters. - // Leave other validation up to the Locale constructor. - const char *q; - for (q = p; q < limit; ++q) { - char c = *q; - if (c == ' ' || c == ',' || c == ';') { break; } - } - return static_cast(q - p); -} - -/** - * Parses and returns a qvalue weight in millis. - * Advances p to after the parsed substring. - * Returns a negative value if parsing fails. - */ -int32_t parseWeight(const char *&p, const char *limit) { - p = skipSpaces(p, limit); - char c; - if (p == limit || ((c = *p) != '0' && c != '1')) { return -1; } - int32_t weight = (c - '0') * 1000; - if (++p == limit || *p != '.') { return weight; } - int32_t multiplier = 100; - while (++p != limit && '0' <= (c = *p) && c <= '9') { - c -= '0'; - if (multiplier > 0) { - weight += c * multiplier; - multiplier /= 10; - } else if (multiplier == 0) { - // round up - if (c >= 5) { ++weight; } - multiplier = -1; - } // else ignore further fraction digits - } - return weight <= WEIGHT_ONE ? weight : -1; // bad if > 1.0 -} - -} // namespace - -/** - * Nothing but a wrapper over a MaybeStackArray of LocaleAndWeight. - * - * This wrapper exists (and is not in an anonymous namespace) - * so that we can forward-declare it in the header file and - * don't have to expose the MaybeStackArray specialization and - * the LocaleAndWeight to code (like the test) that #includes localeprioritylist.h. - * Also, otherwise we would have to do a platform-specific - * template export declaration of some kind for the MaybeStackArray specialization - * to be properly exported from the common DLL. - */ -struct LocaleAndWeightArray : public UMemory { - MaybeStackArray array; -}; - -LocalePriorityList::LocalePriorityList(StringPiece s, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return; } - list = new LocaleAndWeightArray(); - if (list == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - const char *p = s.data(); - const char *limit = p + s.length(); - while ((p = skipSpaces(p, limit)) != limit) { - if (*p == ',') { // empty range field - ++p; - continue; - } - int32_t tagLength = findTagLength(p, limit); - if (tagLength == 0) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - CharString tag(p, tagLength, errorCode); - if (U_FAILURE(errorCode)) { return; } - Locale locale = Locale(tag.data()); - if (locale.isBogus()) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - int32_t weight = WEIGHT_ONE; - if ((p = skipSpaces(p + tagLength, limit)) != limit && *p == ';') { - if ((p = skipSpaces(p + 1, limit)) == limit || *p != 'q' || - (p = skipSpaces(p + 1, limit)) == limit || *p != '=' || - (++p, (weight = parseWeight(p, limit)) < 0)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - p = skipSpaces(p, limit); - } - if (p != limit && *p != ',') { // trailing junk - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - add(locale, weight, errorCode); - if (p == limit) { break; } - ++p; - } - sort(errorCode); -} - -LocalePriorityList::~LocalePriorityList() { - if (list != nullptr) { - for (int32_t i = 0; i < listLength; ++i) { - delete list->array[i].locale; - } - delete list; - } - uhash_close(map); -} - -const Locale *LocalePriorityList::localeAt(int32_t i) const { - return list->array[i].locale; -} - -Locale *LocalePriorityList::orphanLocaleAt(int32_t i) { - if (list == nullptr) { return nullptr; } - LocaleAndWeight &lw = list->array[i]; - Locale *l = lw.locale; - lw.locale = nullptr; - return l; -} - -bool LocalePriorityList::add(const Locale &locale, int32_t weight, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return false; } - if (map == nullptr) { - if (weight <= 0) { return true; } // do not add q=0 - map = uhash_open(hashLocale, compareLocales, uhash_compareLong, &errorCode); - if (U_FAILURE(errorCode)) { return false; } - } - LocalPointer clone; - UBool found = false; - int32_t index = uhash_getiAndFound(map, &locale, &found); - if (found) { - // Duplicate: Remove the old item and append it anew. - LocaleAndWeight &lw = list->array[index]; - clone.adoptInstead(lw.locale); - lw.locale = nullptr; - lw.weight = 0; - ++numRemoved; - } - if (weight <= 0) { // do not add q=0 - if (found) { - // Not strictly necessary but cleaner. - uhash_removei(map, &locale); - } - return true; - } - if (clone.isNull()) { - clone.adoptInstead(locale.clone()); - if (clone.isNull() || (clone->isBogus() && !locale.isBogus())) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return false; - } - } - if (listLength == list->array.getCapacity()) { - int32_t newCapacity = listLength < 50 ? 100 : 4 * listLength; - if (list->array.resize(newCapacity, listLength) == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return false; - } - } - uhash_putiAllowZero(map, clone.getAlias(), listLength, &errorCode); - if (U_FAILURE(errorCode)) { return false; } - LocaleAndWeight &lw = list->array[listLength]; - lw.locale = clone.orphan(); - lw.weight = weight; - lw.index = listLength++; - if (weight < WEIGHT_ONE) { hasWeights = true; } - U_ASSERT(uhash_count(map) == getLength()); - return true; -} - -void LocalePriorityList::sort(UErrorCode &errorCode) { - // Sort by descending weights if there is a mix of weights. - // The comparator forces a stable sort via the item index. - if (U_FAILURE(errorCode) || getLength() <= 1 || !hasWeights) { return; } - uprv_sortArray(list->array.getAlias(), listLength, sizeof(LocaleAndWeight), - compareLocaleAndWeight, nullptr, false, &errorCode); -} - -U_NAMESPACE_END +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// localeprioritylist.cpp +// created: 2019jul11 Markus W. Scherer + +#include "unicode/utypes.h" +#include "unicode/localpointer.h" +#include "unicode/locid.h" +#include "unicode/stringpiece.h" +#include "unicode/uobject.h" +#include "charstr.h" +#include "cmemory.h" +#include "localeprioritylist.h" +#include "uarrsort.h" +#include "uassert.h" +#include "uhash.h" + +U_NAMESPACE_BEGIN + +namespace { + +int32_t hashLocale(const UHashTok token) { + auto *locale = static_cast(token.pointer); + return locale->hashCode(); +} + +UBool compareLocales(const UHashTok t1, const UHashTok t2) { + auto *l1 = static_cast(t1.pointer); + auto *l2 = static_cast(t2.pointer); + return *l1 == *l2; +} + +constexpr int32_t WEIGHT_ONE = 1000; + +struct LocaleAndWeight { + Locale *locale; + int32_t weight; // 0..1000 = 0.0..1.0 + int32_t index; // force stable sort + + int32_t compare(const LocaleAndWeight &other) const { + int32_t diff = other.weight - weight; // descending: other-this + if (diff != 0) { return diff; } + return index - other.index; + } +}; + +int32_t U_CALLCONV +compareLocaleAndWeight(const void * /*context*/, const void *left, const void *right) { + return static_cast(left)-> + compare(*static_cast(right)); +} + +const char *skipSpaces(const char *p, const char *limit) { + while (p < limit && *p == ' ') { ++p; } + return p; +} + +int32_t findTagLength(const char *p, const char *limit) { + // Look for accept-language delimiters. + // Leave other validation up to the Locale constructor. + const char *q; + for (q = p; q < limit; ++q) { + char c = *q; + if (c == ' ' || c == ',' || c == ';') { break; } + } + return static_cast(q - p); +} + +/** + * Parses and returns a qvalue weight in millis. + * Advances p to after the parsed substring. + * Returns a negative value if parsing fails. + */ +int32_t parseWeight(const char *&p, const char *limit) { + p = skipSpaces(p, limit); + char c; + if (p == limit || ((c = *p) != '0' && c != '1')) { return -1; } + int32_t weight = (c - '0') * 1000; + if (++p == limit || *p != '.') { return weight; } + int32_t multiplier = 100; + while (++p != limit && '0' <= (c = *p) && c <= '9') { + c -= '0'; + if (multiplier > 0) { + weight += c * multiplier; + multiplier /= 10; + } else if (multiplier == 0) { + // round up + if (c >= 5) { ++weight; } + multiplier = -1; + } // else ignore further fraction digits + } + return weight <= WEIGHT_ONE ? weight : -1; // bad if > 1.0 +} + +} // namespace + +/** + * Nothing but a wrapper over a MaybeStackArray of LocaleAndWeight. + * + * This wrapper exists (and is not in an anonymous namespace) + * so that we can forward-declare it in the header file and + * don't have to expose the MaybeStackArray specialization and + * the LocaleAndWeight to code (like the test) that #includes localeprioritylist.h. + * Also, otherwise we would have to do a platform-specific + * template export declaration of some kind for the MaybeStackArray specialization + * to be properly exported from the common DLL. + */ +struct LocaleAndWeightArray : public UMemory { + MaybeStackArray array; +}; + +LocalePriorityList::LocalePriorityList(StringPiece s, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return; } + list = new LocaleAndWeightArray(); + if (list == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + const char *p = s.data(); + const char *limit = p + s.length(); + while ((p = skipSpaces(p, limit)) != limit) { + if (*p == ',') { // empty range field + ++p; + continue; + } + int32_t tagLength = findTagLength(p, limit); + if (tagLength == 0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + CharString tag(p, tagLength, errorCode); + if (U_FAILURE(errorCode)) { return; } + Locale locale = Locale(tag.data()); + if (locale.isBogus()) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + int32_t weight = WEIGHT_ONE; + if ((p = skipSpaces(p + tagLength, limit)) != limit && *p == ';') { + if ((p = skipSpaces(p + 1, limit)) == limit || *p != 'q' || + (p = skipSpaces(p + 1, limit)) == limit || *p != '=' || + (++p, (weight = parseWeight(p, limit)) < 0)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + p = skipSpaces(p, limit); + } + if (p != limit && *p != ',') { // trailing junk + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + add(locale, weight, errorCode); + if (p == limit) { break; } + ++p; + } + sort(errorCode); +} + +LocalePriorityList::~LocalePriorityList() { + if (list != nullptr) { + for (int32_t i = 0; i < listLength; ++i) { + delete list->array[i].locale; + } + delete list; + } + uhash_close(map); +} + +const Locale *LocalePriorityList::localeAt(int32_t i) const { + return list->array[i].locale; +} + +Locale *LocalePriorityList::orphanLocaleAt(int32_t i) { + if (list == nullptr) { return nullptr; } + LocaleAndWeight &lw = list->array[i]; + Locale *l = lw.locale; + lw.locale = nullptr; + return l; +} + +bool LocalePriorityList::add(const Locale &locale, int32_t weight, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return false; } + if (map == nullptr) { + if (weight <= 0) { return true; } // do not add q=0 + map = uhash_open(hashLocale, compareLocales, uhash_compareLong, &errorCode); + if (U_FAILURE(errorCode)) { return false; } + } + LocalPointer clone; + UBool found = false; + int32_t index = uhash_getiAndFound(map, &locale, &found); + if (found) { + // Duplicate: Remove the old item and append it anew. + LocaleAndWeight &lw = list->array[index]; + clone.adoptInstead(lw.locale); + lw.locale = nullptr; + lw.weight = 0; + ++numRemoved; + } + if (weight <= 0) { // do not add q=0 + if (found) { + // Not strictly necessary but cleaner. + uhash_removei(map, &locale); + } + return true; + } + if (clone.isNull()) { + clone.adoptInstead(locale.clone()); + if (clone.isNull() || (clone->isBogus() && !locale.isBogus())) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return false; + } + } + if (listLength == list->array.getCapacity()) { + int32_t newCapacity = listLength < 50 ? 100 : 4 * listLength; + if (list->array.resize(newCapacity, listLength) == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return false; + } + } + uhash_putiAllowZero(map, clone.getAlias(), listLength, &errorCode); + if (U_FAILURE(errorCode)) { return false; } + LocaleAndWeight &lw = list->array[listLength]; + lw.locale = clone.orphan(); + lw.weight = weight; + lw.index = listLength++; + if (weight < WEIGHT_ONE) { hasWeights = true; } + U_ASSERT(uhash_count(map) == getLength()); + return true; +} + +void LocalePriorityList::sort(UErrorCode &errorCode) { + // Sort by descending weights if there is a mix of weights. + // The comparator forces a stable sort via the item index. + if (U_FAILURE(errorCode) || getLength() <= 1 || !hasWeights) { return; } + uprv_sortArray(list->array.getAlias(), listLength, sizeof(LocaleAndWeight), + compareLocaleAndWeight, nullptr, false, &errorCode); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/localeprioritylist.h b/deps/icu-small/source/common/localeprioritylist.h index 41e9d3ea081f56..f32d932a0f4a33 100644 --- a/deps/icu-small/source/common/localeprioritylist.h +++ b/deps/icu-small/source/common/localeprioritylist.h @@ -1,115 +1,115 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// localeprioritylist.h -// created: 2019jul11 Markus W. Scherer - -#ifndef __LOCALEPRIORITYLIST_H__ -#define __LOCALEPRIORITYLIST_H__ - -#include "unicode/utypes.h" -#include "unicode/locid.h" -#include "unicode/stringpiece.h" -#include "unicode/uobject.h" - -struct UHashtable; - -U_NAMESPACE_BEGIN - -struct LocaleAndWeightArray; - -/** - * Parses a list of locales from an accept-language string. - * We are a bit more lenient than the spec: - * We accept extra whitespace in more places, empty range fields, - * and any number of qvalue fraction digits. - * - * https://tools.ietf.org/html/rfc2616#section-14.4 - * 14.4 Accept-Language - * - * Accept-Language = "Accept-Language" ":" - * 1#( language-range [ ";" "q" "=" qvalue ] ) - * language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) - * - * Each language-range MAY be given an associated quality value which - * represents an estimate of the user's preference for the languages - * specified by that range. The quality value defaults to "q=1". For - * example, - * - * Accept-Language: da, en-gb;q=0.8, en;q=0.7 - * - * https://tools.ietf.org/html/rfc2616#section-3.9 - * 3.9 Quality Values - * - * HTTP content negotiation (section 12) uses short "floating point" - * numbers to indicate the relative importance ("weight") of various - * negotiable parameters. A weight is normalized to a real number in - * the range 0 through 1, where 0 is the minimum and 1 the maximum - * value. If a parameter has a quality value of 0, then content with - * this parameter is `not acceptable' for the client. HTTP/1.1 - * applications MUST NOT generate more than three digits after the - * decimal point. User configuration of these values SHOULD also be - * limited in this fashion. - * - * qvalue = ( "0" [ "." 0*3DIGIT ] ) - * | ( "1" [ "." 0*3("0") ] ) - */ -class U_COMMON_API LocalePriorityList : public UMemory { -public: - class Iterator : public Locale::Iterator { - public: - UBool hasNext() const override { return count < length; } - - const Locale &next() override { - for(;;) { - const Locale *locale = list.localeAt(index++); - if (locale != nullptr) { - ++count; - return *locale; - } - } - } - - private: - friend class LocalePriorityList; - - Iterator(const LocalePriorityList &list) : list(list), length(list.getLength()) {} - - const LocalePriorityList &list; - int32_t index = 0; - int32_t count = 0; - const int32_t length; - }; - - LocalePriorityList(StringPiece s, UErrorCode &errorCode); - - ~LocalePriorityList(); - - int32_t getLength() const { return listLength - numRemoved; } - - int32_t getLengthIncludingRemoved() const { return listLength; } - - Iterator iterator() const { return Iterator(*this); } - - const Locale *localeAt(int32_t i) const; - - Locale *orphanLocaleAt(int32_t i); - -private: - LocalePriorityList(const LocalePriorityList &) = delete; - LocalePriorityList &operator=(const LocalePriorityList &) = delete; - - bool add(const Locale &locale, int32_t weight, UErrorCode &errorCode); - - void sort(UErrorCode &errorCode); - - LocaleAndWeightArray *list = nullptr; - int32_t listLength = 0; - int32_t numRemoved = 0; - bool hasWeights = false; // other than 1.0 - UHashtable *map = nullptr; -}; - -U_NAMESPACE_END - -#endif // __LOCALEPRIORITYLIST_H__ +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// localeprioritylist.h +// created: 2019jul11 Markus W. Scherer + +#ifndef __LOCALEPRIORITYLIST_H__ +#define __LOCALEPRIORITYLIST_H__ + +#include "unicode/utypes.h" +#include "unicode/locid.h" +#include "unicode/stringpiece.h" +#include "unicode/uobject.h" + +struct UHashtable; + +U_NAMESPACE_BEGIN + +struct LocaleAndWeightArray; + +/** + * Parses a list of locales from an accept-language string. + * We are a bit more lenient than the spec: + * We accept extra whitespace in more places, empty range fields, + * and any number of qvalue fraction digits. + * + * https://tools.ietf.org/html/rfc2616#section-14.4 + * 14.4 Accept-Language + * + * Accept-Language = "Accept-Language" ":" + * 1#( language-range [ ";" "q" "=" qvalue ] ) + * language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) + * + * Each language-range MAY be given an associated quality value which + * represents an estimate of the user's preference for the languages + * specified by that range. The quality value defaults to "q=1". For + * example, + * + * Accept-Language: da, en-gb;q=0.8, en;q=0.7 + * + * https://tools.ietf.org/html/rfc2616#section-3.9 + * 3.9 Quality Values + * + * HTTP content negotiation (section 12) uses short "floating point" + * numbers to indicate the relative importance ("weight") of various + * negotiable parameters. A weight is normalized to a real number in + * the range 0 through 1, where 0 is the minimum and 1 the maximum + * value. If a parameter has a quality value of 0, then content with + * this parameter is `not acceptable' for the client. HTTP/1.1 + * applications MUST NOT generate more than three digits after the + * decimal point. User configuration of these values SHOULD also be + * limited in this fashion. + * + * qvalue = ( "0" [ "." 0*3DIGIT ] ) + * | ( "1" [ "." 0*3("0") ] ) + */ +class U_COMMON_API LocalePriorityList : public UMemory { +public: + class Iterator : public Locale::Iterator { + public: + UBool hasNext() const override { return count < length; } + + const Locale &next() override { + for(;;) { + const Locale *locale = list.localeAt(index++); + if (locale != nullptr) { + ++count; + return *locale; + } + } + } + + private: + friend class LocalePriorityList; + + Iterator(const LocalePriorityList &list) : list(list), length(list.getLength()) {} + + const LocalePriorityList &list; + int32_t index = 0; + int32_t count = 0; + const int32_t length; + }; + + LocalePriorityList(StringPiece s, UErrorCode &errorCode); + + ~LocalePriorityList(); + + int32_t getLength() const { return listLength - numRemoved; } + + int32_t getLengthIncludingRemoved() const { return listLength; } + + Iterator iterator() const { return Iterator(*this); } + + const Locale *localeAt(int32_t i) const; + + Locale *orphanLocaleAt(int32_t i); + +private: + LocalePriorityList(const LocalePriorityList &) = delete; + LocalePriorityList &operator=(const LocalePriorityList &) = delete; + + bool add(const Locale &locale, int32_t weight, UErrorCode &errorCode); + + void sort(UErrorCode &errorCode); + + LocaleAndWeightArray *list = nullptr; + int32_t listLength = 0; + int32_t numRemoved = 0; + bool hasWeights = false; // other than 1.0 + UHashtable *map = nullptr; +}; + +U_NAMESPACE_END + +#endif // __LOCALEPRIORITYLIST_H__ diff --git a/deps/icu-small/source/common/localsvc.h b/deps/icu-small/source/common/localsvc.h index 33640195135bd8..a13099176f35fa 100644 --- a/deps/icu-small/source/common/localsvc.h +++ b/deps/icu-small/source/common/localsvc.h @@ -1,27 +1,27 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -*************************************************************************** -* Copyright (C) 2006 International Business Machines Corporation * -* and others. All rights reserved. * -*************************************************************************** -*/ - -#ifndef LOCALSVC_H -#define LOCALSVC_H - -#include "unicode/utypes.h" - -#if defined(U_LOCAL_SERVICE_HOOK) && U_LOCAL_SERVICE_HOOK -/** - * Prototype for user-supplied service hook. This function is expected to return - * a type of factory object specific to the requested service. - * - * @param what service-specific string identifying the specific user hook - * @param status error status - * @return a service-specific hook, or NULL on failure. - */ -U_CAPI void* uprv_svc_hook(const char *what, UErrorCode *status); -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +*************************************************************************** +* Copyright (C) 2006 International Business Machines Corporation * +* and others. All rights reserved. * +*************************************************************************** +*/ + +#ifndef LOCALSVC_H +#define LOCALSVC_H + +#include "unicode/utypes.h" + +#if defined(U_LOCAL_SERVICE_HOOK) && U_LOCAL_SERVICE_HOOK +/** + * Prototype for user-supplied service hook. This function is expected to return + * a type of factory object specific to the requested service. + * + * @param what service-specific string identifying the specific user hook + * @param status error status + * @return a service-specific hook, or NULL on failure. + */ +U_CAPI void* uprv_svc_hook(const char *what, UErrorCode *status); +#endif + +#endif diff --git a/deps/icu-small/source/common/locavailable.cpp b/deps/icu-small/source/common/locavailable.cpp index cf341e1f74c8b9..b48dc06f5e4a04 100644 --- a/deps/icu-small/source/common/locavailable.cpp +++ b/deps/icu-small/source/common/locavailable.cpp @@ -1,270 +1,270 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1997-2013, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: locavailable.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010feb25 -* created by: Markus W. Scherer -* -* Code for available locales, separated out from other .cpp files -* that then do not depend on resource bundle code and res_index bundles. -*/ - -#include "unicode/errorcode.h" -#include "unicode/utypes.h" -#include "unicode/locid.h" -#include "unicode/uloc.h" -#include "unicode/ures.h" -#include "cmemory.h" -#include "cstring.h" -#include "ucln_cmn.h" -#include "uassert.h" -#include "umutex.h" -#include "uresimp.h" - -// C++ API ----------------------------------------------------------------- *** - -U_NAMESPACE_BEGIN - -static icu::Locale* availableLocaleList = NULL; -static int32_t availableLocaleListCount; -static icu::UInitOnce gInitOnceLocale {}; - -U_NAMESPACE_END - -U_CDECL_BEGIN - -static UBool U_CALLCONV locale_available_cleanup(void) -{ - U_NAMESPACE_USE - - if (availableLocaleList) { - delete []availableLocaleList; - availableLocaleList = NULL; - } - availableLocaleListCount = 0; - gInitOnceLocale.reset(); - - return true; -} - -U_CDECL_END - -U_NAMESPACE_BEGIN - -void U_CALLCONV locale_available_init() { - // This function is a friend of class Locale. - // This function is only invoked via umtx_initOnce(). - - // for now, there is a hardcoded list, so just walk through that list and set it up. - // Note: this function is a friend of class Locale. - availableLocaleListCount = uloc_countAvailable(); - if(availableLocaleListCount) { - availableLocaleList = new Locale[availableLocaleListCount]; - } - if (availableLocaleList == NULL) { - availableLocaleListCount= 0; - } - for (int32_t locCount=availableLocaleListCount-1; locCount>=0; --locCount) { - availableLocaleList[locCount].setFromPOSIXID(uloc_getAvailable(locCount)); - } - ucln_common_registerCleanup(UCLN_COMMON_LOCALE_AVAILABLE, locale_available_cleanup); -} - -const Locale* U_EXPORT2 -Locale::getAvailableLocales(int32_t& count) -{ - umtx_initOnce(gInitOnceLocale, &locale_available_init); - count = availableLocaleListCount; - return availableLocaleList; -} - - -U_NAMESPACE_END - -// C API ------------------------------------------------------------------- *** - -U_NAMESPACE_USE - -/* ### Constants **************************************************/ - -namespace { - -// Enough capacity for the two lists in the res_index.res file -const char** gAvailableLocaleNames[2] = {}; -int32_t gAvailableLocaleCounts[2] = {}; -icu::UInitOnce ginstalledLocalesInitOnce {}; - -class AvailableLocalesSink : public ResourceSink { - public: - void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) U_OVERRIDE { - ResourceTable resIndexTable = value.getTable(status); - if (U_FAILURE(status)) { - return; - } - for (int32_t i = 0; resIndexTable.getKeyAndValue(i, key, value); ++i) { - ULocAvailableType type; - if (uprv_strcmp(key, "InstalledLocales") == 0) { - type = ULOC_AVAILABLE_DEFAULT; - } else if (uprv_strcmp(key, "AliasLocales") == 0) { - type = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES; - } else { - // CLDRVersion, etc. - continue; - } - ResourceTable availableLocalesTable = value.getTable(status); - if (U_FAILURE(status)) { - return; - } - gAvailableLocaleCounts[type] = availableLocalesTable.getSize(); - gAvailableLocaleNames[type] = static_cast( - uprv_malloc(gAvailableLocaleCounts[type] * sizeof(const char*))); - if (gAvailableLocaleNames[type] == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - for (int32_t j = 0; availableLocalesTable.getKeyAndValue(j, key, value); ++j) { - gAvailableLocaleNames[type][j] = key; - } - } - } -}; - -class AvailableLocalesStringEnumeration : public StringEnumeration { - public: - AvailableLocalesStringEnumeration(ULocAvailableType type) : fType(type) { - } - - const char* next(int32_t *resultLength, UErrorCode&) override { - ULocAvailableType actualType = fType; - int32_t actualIndex = fIndex++; - - // If the "combined" list was requested, resolve that now - if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) { - int32_t defaultLocalesCount = gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT]; - if (actualIndex < defaultLocalesCount) { - actualType = ULOC_AVAILABLE_DEFAULT; - } else { - actualIndex -= defaultLocalesCount; - actualType = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES; - } - } - - // Return the requested string - int32_t count = gAvailableLocaleCounts[actualType]; - const char* result; - if (actualIndex < count) { - result = gAvailableLocaleNames[actualType][actualIndex]; - if (resultLength != nullptr) { - *resultLength = static_cast(uprv_strlen(result)); - } - } else { - result = nullptr; - if (resultLength != nullptr) { - *resultLength = 0; - } - } - return result; - } - - void reset(UErrorCode&) override { - fIndex = 0; - } - - int32_t count(UErrorCode&) const override { - if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) { - return gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT] - + gAvailableLocaleCounts[ULOC_AVAILABLE_ONLY_LEGACY_ALIASES]; - } else { - return gAvailableLocaleCounts[fType]; - } - } - - private: - ULocAvailableType fType; - int32_t fIndex = 0; -}; - -/* ### Get available **************************************************/ - -static UBool U_CALLCONV uloc_cleanup(void) { - for (int32_t i = 0; i < UPRV_LENGTHOF(gAvailableLocaleNames); i++) { - uprv_free(gAvailableLocaleNames[i]); - gAvailableLocaleNames[i] = nullptr; - gAvailableLocaleCounts[i] = 0; - } - ginstalledLocalesInitOnce.reset(); - return true; -} - -// Load Installed Locales. This function will be called exactly once -// via the initOnce mechanism. - -static void U_CALLCONV loadInstalledLocales(UErrorCode& status) { - ucln_common_registerCleanup(UCLN_COMMON_ULOC, uloc_cleanup); - - icu::LocalUResourceBundlePointer rb(ures_openDirect(NULL, "res_index", &status)); - AvailableLocalesSink sink; - ures_getAllItemsWithFallback(rb.getAlias(), "", sink, status); -} - -void _load_installedLocales(UErrorCode& status) { - umtx_initOnce(ginstalledLocalesInitOnce, &loadInstalledLocales, status); -} - -} // namespace - -U_CAPI const char* U_EXPORT2 -uloc_getAvailable(int32_t offset) { - icu::ErrorCode status; - _load_installedLocales(status); - if (status.isFailure()) { - return nullptr; - } - if (offset > gAvailableLocaleCounts[0]) { - // *status = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - return gAvailableLocaleNames[0][offset]; -} - -U_CAPI int32_t U_EXPORT2 -uloc_countAvailable() { - icu::ErrorCode status; - _load_installedLocales(status); - if (status.isFailure()) { - return 0; - } - return gAvailableLocaleCounts[0]; -} - -U_CAPI UEnumeration* U_EXPORT2 -uloc_openAvailableByType(ULocAvailableType type, UErrorCode* status) { - if (U_FAILURE(*status)) { - return nullptr; - } - if (type < 0 || type >= ULOC_AVAILABLE_COUNT) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - _load_installedLocales(*status); - if (U_FAILURE(*status)) { - return nullptr; - } - LocalPointer result( - new AvailableLocalesStringEnumeration(type), *status); - if (U_FAILURE(*status)) { - return nullptr; - } - return uenum_openFromStringEnumeration(result.orphan(), status); -} - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1997-2013, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: locavailable.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010feb25 +* created by: Markus W. Scherer +* +* Code for available locales, separated out from other .cpp files +* that then do not depend on resource bundle code and res_index bundles. +*/ + +#include "unicode/errorcode.h" +#include "unicode/utypes.h" +#include "unicode/locid.h" +#include "unicode/uloc.h" +#include "unicode/ures.h" +#include "cmemory.h" +#include "cstring.h" +#include "ucln_cmn.h" +#include "uassert.h" +#include "umutex.h" +#include "uresimp.h" + +// C++ API ----------------------------------------------------------------- *** + +U_NAMESPACE_BEGIN + +static icu::Locale* availableLocaleList = nullptr; +static int32_t availableLocaleListCount; +static icu::UInitOnce gInitOnceLocale {}; + +U_NAMESPACE_END + +U_CDECL_BEGIN + +static UBool U_CALLCONV locale_available_cleanup() +{ + U_NAMESPACE_USE + + if (availableLocaleList) { + delete []availableLocaleList; + availableLocaleList = nullptr; + } + availableLocaleListCount = 0; + gInitOnceLocale.reset(); + + return true; +} + +U_CDECL_END + +U_NAMESPACE_BEGIN + +void U_CALLCONV locale_available_init() { + // This function is a friend of class Locale. + // This function is only invoked via umtx_initOnce(). + + // for now, there is a hardcoded list, so just walk through that list and set it up. + // Note: this function is a friend of class Locale. + availableLocaleListCount = uloc_countAvailable(); + if(availableLocaleListCount) { + availableLocaleList = new Locale[availableLocaleListCount]; + } + if (availableLocaleList == nullptr) { + availableLocaleListCount= 0; + } + for (int32_t locCount=availableLocaleListCount-1; locCount>=0; --locCount) { + availableLocaleList[locCount].setFromPOSIXID(uloc_getAvailable(locCount)); + } + ucln_common_registerCleanup(UCLN_COMMON_LOCALE_AVAILABLE, locale_available_cleanup); +} + +const Locale* U_EXPORT2 +Locale::getAvailableLocales(int32_t& count) +{ + umtx_initOnce(gInitOnceLocale, &locale_available_init); + count = availableLocaleListCount; + return availableLocaleList; +} + + +U_NAMESPACE_END + +// C API ------------------------------------------------------------------- *** + +U_NAMESPACE_USE + +/* ### Constants **************************************************/ + +namespace { + +// Enough capacity for the two lists in the res_index.res file +const char** gAvailableLocaleNames[2] = {}; +int32_t gAvailableLocaleCounts[2] = {}; +icu::UInitOnce ginstalledLocalesInitOnce {}; + +class AvailableLocalesSink : public ResourceSink { + public: + void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override { + ResourceTable resIndexTable = value.getTable(status); + if (U_FAILURE(status)) { + return; + } + for (int32_t i = 0; resIndexTable.getKeyAndValue(i, key, value); ++i) { + ULocAvailableType type; + if (uprv_strcmp(key, "InstalledLocales") == 0) { + type = ULOC_AVAILABLE_DEFAULT; + } else if (uprv_strcmp(key, "AliasLocales") == 0) { + type = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES; + } else { + // CLDRVersion, etc. + continue; + } + ResourceTable availableLocalesTable = value.getTable(status); + if (U_FAILURE(status)) { + return; + } + gAvailableLocaleCounts[type] = availableLocalesTable.getSize(); + gAvailableLocaleNames[type] = static_cast( + uprv_malloc(gAvailableLocaleCounts[type] * sizeof(const char*))); + if (gAvailableLocaleNames[type] == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + for (int32_t j = 0; availableLocalesTable.getKeyAndValue(j, key, value); ++j) { + gAvailableLocaleNames[type][j] = key; + } + } + } +}; + +class AvailableLocalesStringEnumeration : public StringEnumeration { + public: + AvailableLocalesStringEnumeration(ULocAvailableType type) : fType(type) { + } + + const char* next(int32_t *resultLength, UErrorCode&) override { + ULocAvailableType actualType = fType; + int32_t actualIndex = fIndex++; + + // If the "combined" list was requested, resolve that now + if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) { + int32_t defaultLocalesCount = gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT]; + if (actualIndex < defaultLocalesCount) { + actualType = ULOC_AVAILABLE_DEFAULT; + } else { + actualIndex -= defaultLocalesCount; + actualType = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES; + } + } + + // Return the requested string + int32_t count = gAvailableLocaleCounts[actualType]; + const char* result; + if (actualIndex < count) { + result = gAvailableLocaleNames[actualType][actualIndex]; + if (resultLength != nullptr) { + *resultLength = static_cast(uprv_strlen(result)); + } + } else { + result = nullptr; + if (resultLength != nullptr) { + *resultLength = 0; + } + } + return result; + } + + void reset(UErrorCode&) override { + fIndex = 0; + } + + int32_t count(UErrorCode&) const override { + if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) { + return gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT] + + gAvailableLocaleCounts[ULOC_AVAILABLE_ONLY_LEGACY_ALIASES]; + } else { + return gAvailableLocaleCounts[fType]; + } + } + + private: + ULocAvailableType fType; + int32_t fIndex = 0; +}; + +/* ### Get available **************************************************/ + +static UBool U_CALLCONV uloc_cleanup() { + for (int32_t i = 0; i < UPRV_LENGTHOF(gAvailableLocaleNames); i++) { + uprv_free(gAvailableLocaleNames[i]); + gAvailableLocaleNames[i] = nullptr; + gAvailableLocaleCounts[i] = 0; + } + ginstalledLocalesInitOnce.reset(); + return true; +} + +// Load Installed Locales. This function will be called exactly once +// via the initOnce mechanism. + +static void U_CALLCONV loadInstalledLocales(UErrorCode& status) { + ucln_common_registerCleanup(UCLN_COMMON_ULOC, uloc_cleanup); + + icu::LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "res_index", &status)); + AvailableLocalesSink sink; + ures_getAllItemsWithFallback(rb.getAlias(), "", sink, status); +} + +void _load_installedLocales(UErrorCode& status) { + umtx_initOnce(ginstalledLocalesInitOnce, &loadInstalledLocales, status); +} + +} // namespace + +U_CAPI const char* U_EXPORT2 +uloc_getAvailable(int32_t offset) { + icu::ErrorCode status; + _load_installedLocales(status); + if (status.isFailure()) { + return nullptr; + } + if (offset > gAvailableLocaleCounts[0]) { + // *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + return gAvailableLocaleNames[0][offset]; +} + +U_CAPI int32_t U_EXPORT2 +uloc_countAvailable() { + icu::ErrorCode status; + _load_installedLocales(status); + if (status.isFailure()) { + return 0; + } + return gAvailableLocaleCounts[0]; +} + +U_CAPI UEnumeration* U_EXPORT2 +uloc_openAvailableByType(ULocAvailableType type, UErrorCode* status) { + if (U_FAILURE(*status)) { + return nullptr; + } + if (type < 0 || type >= ULOC_AVAILABLE_COUNT) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + _load_installedLocales(*status); + if (U_FAILURE(*status)) { + return nullptr; + } + LocalPointer result( + new AvailableLocalesStringEnumeration(type), *status); + if (U_FAILURE(*status)) { + return nullptr; + } + return uenum_openFromStringEnumeration(result.orphan(), status); +} + diff --git a/deps/icu-small/source/common/locbased.cpp b/deps/icu-small/source/common/locbased.cpp index ff378b4cc78f1d..0440b58580ea1b 100644 --- a/deps/icu-small/source/common/locbased.cpp +++ b/deps/icu-small/source/common/locbased.cpp @@ -1,55 +1,55 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2004-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: January 16 2004 -* Since: ICU 2.8 -********************************************************************** -*/ -#include "locbased.h" -#include "cstring.h" - -U_NAMESPACE_BEGIN - -Locale LocaleBased::getLocale(ULocDataLocaleType type, UErrorCode& status) const { - const char* id = getLocaleID(type, status); - return Locale((id != 0) ? id : ""); -} - -const char* LocaleBased::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const { - if (U_FAILURE(status)) { - return NULL; - } - - switch(type) { - case ULOC_VALID_LOCALE: - return valid; - case ULOC_ACTUAL_LOCALE: - return actual; - default: - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } -} - -void LocaleBased::setLocaleIDs(const char* validID, const char* actualID) { - if (validID != 0) { - uprv_strncpy(valid, validID, ULOC_FULLNAME_CAPACITY); - valid[ULOC_FULLNAME_CAPACITY-1] = 0; // always terminate - } - if (actualID != 0) { - uprv_strncpy(actual, actualID, ULOC_FULLNAME_CAPACITY); - actual[ULOC_FULLNAME_CAPACITY-1] = 0; // always terminate - } -} - -void LocaleBased::setLocaleIDs(const Locale& validID, const Locale& actualID) { - uprv_strcpy(valid, validID.getName()); - uprv_strcpy(actual, actualID.getName()); -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2004-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: January 16 2004 +* Since: ICU 2.8 +********************************************************************** +*/ +#include "locbased.h" +#include "cstring.h" + +U_NAMESPACE_BEGIN + +Locale LocaleBased::getLocale(ULocDataLocaleType type, UErrorCode& status) const { + const char* id = getLocaleID(type, status); + return Locale((id != 0) ? id : ""); +} + +const char* LocaleBased::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const { + if (U_FAILURE(status)) { + return nullptr; + } + + switch(type) { + case ULOC_VALID_LOCALE: + return valid; + case ULOC_ACTUAL_LOCALE: + return actual; + default: + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } +} + +void LocaleBased::setLocaleIDs(const char* validID, const char* actualID) { + if (validID != 0) { + uprv_strncpy(valid, validID, ULOC_FULLNAME_CAPACITY); + valid[ULOC_FULLNAME_CAPACITY-1] = 0; // always terminate + } + if (actualID != 0) { + uprv_strncpy(actual, actualID, ULOC_FULLNAME_CAPACITY); + actual[ULOC_FULLNAME_CAPACITY-1] = 0; // always terminate + } +} + +void LocaleBased::setLocaleIDs(const Locale& validID, const Locale& actualID) { + uprv_strcpy(valid, validID.getName()); + uprv_strcpy(actual, actualID.getName()); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/locbased.h b/deps/icu-small/source/common/locbased.h index 45738863b5ec96..4dad9dcc5f6cf1 100644 --- a/deps/icu-small/source/common/locbased.h +++ b/deps/icu-small/source/common/locbased.h @@ -1,107 +1,107 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2004-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: January 16 2004 -* Since: ICU 2.8 -********************************************************************** -*/ -#ifndef LOCBASED_H -#define LOCBASED_H - -#include "unicode/locid.h" -#include "unicode/uobject.h" - -/** - * Macro to declare a locale LocaleBased wrapper object for the given - * object, which must have two members named `validLocale' and - * `actualLocale' of size ULOC_FULLNAME_CAPACITY - */ -#define U_LOCALE_BASED(varname, objname) \ - LocaleBased varname((objname).validLocale, (objname).actualLocale) - -U_NAMESPACE_BEGIN - -/** - * A utility class that unifies the implementation of getLocale() by - * various ICU services. This class is likely to be removed in the - * ICU 3.0 time frame in favor of an integrated approach with the - * services framework. - * @since ICU 2.8 - */ -class U_COMMON_API LocaleBased : public UMemory { - - public: - - /** - * Construct a LocaleBased wrapper around the two pointers. These - * will be aliased for the lifetime of this object. - */ - inline LocaleBased(char* validAlias, char* actualAlias); - - /** - * Construct a LocaleBased wrapper around the two const pointers. - * These will be aliased for the lifetime of this object. - */ - inline LocaleBased(const char* validAlias, const char* actualAlias); - - /** - * Return locale meta-data for the service object wrapped by this - * object. Either the valid or the actual locale may be - * retrieved. - * @param type either ULOC_VALID_LOCALE or ULOC_ACTUAL_LOCALE - * @param status input-output error code - * @return the indicated locale - */ - Locale getLocale(ULocDataLocaleType type, UErrorCode& status) const; - - /** - * Return the locale ID for the service object wrapped by this - * object. Either the valid or the actual locale may be - * retrieved. - * @param type either ULOC_VALID_LOCALE or ULOC_ACTUAL_LOCALE - * @param status input-output error code - * @return the indicated locale ID - */ - const char* getLocaleID(ULocDataLocaleType type, UErrorCode& status) const; - - /** - * Set the locale meta-data for the service object wrapped by this - * object. If either parameter is zero, it is ignored. - * @param valid the ID of the valid locale - * @param actual the ID of the actual locale - */ - void setLocaleIDs(const char* valid, const char* actual); - - /** - * Set the locale meta-data for the service object wrapped by this - * object. - * @param valid the ID of the valid locale - * @param actual the ID of the actual locale - */ - void setLocaleIDs(const Locale& valid, const Locale& actual); - - private: - - char* valid; - - char* actual; -}; - -inline LocaleBased::LocaleBased(char* validAlias, char* actualAlias) : - valid(validAlias), actual(actualAlias) { -} - -inline LocaleBased::LocaleBased(const char* validAlias, - const char* actualAlias) : - // ugh: cast away const - valid((char*)validAlias), actual((char*)actualAlias) { -} - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2004-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: January 16 2004 +* Since: ICU 2.8 +********************************************************************** +*/ +#ifndef LOCBASED_H +#define LOCBASED_H + +#include "unicode/locid.h" +#include "unicode/uobject.h" + +/** + * Macro to declare a locale LocaleBased wrapper object for the given + * object, which must have two members named `validLocale' and + * `actualLocale' of size ULOC_FULLNAME_CAPACITY + */ +#define U_LOCALE_BASED(varname, objname) \ + LocaleBased varname((objname).validLocale, (objname).actualLocale) + +U_NAMESPACE_BEGIN + +/** + * A utility class that unifies the implementation of getLocale() by + * various ICU services. This class is likely to be removed in the + * ICU 3.0 time frame in favor of an integrated approach with the + * services framework. + * @since ICU 2.8 + */ +class U_COMMON_API LocaleBased : public UMemory { + + public: + + /** + * Construct a LocaleBased wrapper around the two pointers. These + * will be aliased for the lifetime of this object. + */ + inline LocaleBased(char* validAlias, char* actualAlias); + + /** + * Construct a LocaleBased wrapper around the two const pointers. + * These will be aliased for the lifetime of this object. + */ + inline LocaleBased(const char* validAlias, const char* actualAlias); + + /** + * Return locale meta-data for the service object wrapped by this + * object. Either the valid or the actual locale may be + * retrieved. + * @param type either ULOC_VALID_LOCALE or ULOC_ACTUAL_LOCALE + * @param status input-output error code + * @return the indicated locale + */ + Locale getLocale(ULocDataLocaleType type, UErrorCode& status) const; + + /** + * Return the locale ID for the service object wrapped by this + * object. Either the valid or the actual locale may be + * retrieved. + * @param type either ULOC_VALID_LOCALE or ULOC_ACTUAL_LOCALE + * @param status input-output error code + * @return the indicated locale ID + */ + const char* getLocaleID(ULocDataLocaleType type, UErrorCode& status) const; + + /** + * Set the locale meta-data for the service object wrapped by this + * object. If either parameter is zero, it is ignored. + * @param valid the ID of the valid locale + * @param actual the ID of the actual locale + */ + void setLocaleIDs(const char* valid, const char* actual); + + /** + * Set the locale meta-data for the service object wrapped by this + * object. + * @param valid the ID of the valid locale + * @param actual the ID of the actual locale + */ + void setLocaleIDs(const Locale& valid, const Locale& actual); + + private: + + char* valid; + + char* actual; +}; + +inline LocaleBased::LocaleBased(char* validAlias, char* actualAlias) : + valid(validAlias), actual(actualAlias) { +} + +inline LocaleBased::LocaleBased(const char* validAlias, + const char* actualAlias) : + // ugh: cast away const + valid((char*)validAlias), actual((char*)actualAlias) { +} + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/common/locdispnames.cpp b/deps/icu-small/source/common/locdispnames.cpp index 637556cc71db72..46caa8524fe16f 100644 --- a/deps/icu-small/source/common/locdispnames.cpp +++ b/deps/icu-small/source/common/locdispnames.cpp @@ -1,902 +1,902 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1997-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: locdispnames.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010feb25 -* created by: Markus W. Scherer -* -* Code for locale display names, separated out from other .cpp files -* that then do not depend on resource bundle code and display name data. -*/ - -#include "unicode/utypes.h" -#include "unicode/brkiter.h" -#include "unicode/locid.h" -#include "unicode/uenum.h" -#include "unicode/uloc.h" -#include "unicode/ures.h" -#include "unicode/ustring.h" -#include "bytesinkutil.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "putilimp.h" -#include "ulocimp.h" -#include "uresimp.h" -#include "ureslocs.h" -#include "ustr_imp.h" - -// C++ API ----------------------------------------------------------------- *** - -U_NAMESPACE_BEGIN - -UnicodeString& -Locale::getDisplayLanguage(UnicodeString& dispLang) const -{ - return this->getDisplayLanguage(getDefault(), dispLang); -} - -/*We cannot make any assumptions on the size of the output display strings -* Yet, since we are calling through to a C API, we need to set limits on -* buffer size. For all the following getDisplay functions we first attempt -* to fill up a stack allocated buffer. If it is to small we heap allocated -* the exact buffer we need copy it to the UnicodeString and delete it*/ - -UnicodeString& -Locale::getDisplayLanguage(const Locale &displayLocale, - UnicodeString &result) const { - UChar *buffer; - UErrorCode errorCode=U_ZERO_ERROR; - int32_t length; - - buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); - if(buffer==0) { - result.truncate(0); - return result; - } - - length=uloc_getDisplayLanguage(fullName, displayLocale.fullName, - buffer, result.getCapacity(), - &errorCode); - result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); - - if(errorCode==U_BUFFER_OVERFLOW_ERROR) { - buffer=result.getBuffer(length); - if(buffer==0) { - result.truncate(0); - return result; - } - errorCode=U_ZERO_ERROR; - length=uloc_getDisplayLanguage(fullName, displayLocale.fullName, - buffer, result.getCapacity(), - &errorCode); - result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); - } - - return result; -} - -UnicodeString& -Locale::getDisplayScript(UnicodeString& dispScript) const -{ - return this->getDisplayScript(getDefault(), dispScript); -} - -UnicodeString& -Locale::getDisplayScript(const Locale &displayLocale, - UnicodeString &result) const { - UChar *buffer; - UErrorCode errorCode=U_ZERO_ERROR; - int32_t length; - - buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); - if(buffer==0) { - result.truncate(0); - return result; - } - - length=uloc_getDisplayScript(fullName, displayLocale.fullName, - buffer, result.getCapacity(), - &errorCode); - result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); - - if(errorCode==U_BUFFER_OVERFLOW_ERROR) { - buffer=result.getBuffer(length); - if(buffer==0) { - result.truncate(0); - return result; - } - errorCode=U_ZERO_ERROR; - length=uloc_getDisplayScript(fullName, displayLocale.fullName, - buffer, result.getCapacity(), - &errorCode); - result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); - } - - return result; -} - -UnicodeString& -Locale::getDisplayCountry(UnicodeString& dispCntry) const -{ - return this->getDisplayCountry(getDefault(), dispCntry); -} - -UnicodeString& -Locale::getDisplayCountry(const Locale &displayLocale, - UnicodeString &result) const { - UChar *buffer; - UErrorCode errorCode=U_ZERO_ERROR; - int32_t length; - - buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); - if(buffer==0) { - result.truncate(0); - return result; - } - - length=uloc_getDisplayCountry(fullName, displayLocale.fullName, - buffer, result.getCapacity(), - &errorCode); - result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); - - if(errorCode==U_BUFFER_OVERFLOW_ERROR) { - buffer=result.getBuffer(length); - if(buffer==0) { - result.truncate(0); - return result; - } - errorCode=U_ZERO_ERROR; - length=uloc_getDisplayCountry(fullName, displayLocale.fullName, - buffer, result.getCapacity(), - &errorCode); - result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); - } - - return result; -} - -UnicodeString& -Locale::getDisplayVariant(UnicodeString& dispVar) const -{ - return this->getDisplayVariant(getDefault(), dispVar); -} - -UnicodeString& -Locale::getDisplayVariant(const Locale &displayLocale, - UnicodeString &result) const { - UChar *buffer; - UErrorCode errorCode=U_ZERO_ERROR; - int32_t length; - - buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); - if(buffer==0) { - result.truncate(0); - return result; - } - - length=uloc_getDisplayVariant(fullName, displayLocale.fullName, - buffer, result.getCapacity(), - &errorCode); - result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); - - if(errorCode==U_BUFFER_OVERFLOW_ERROR) { - buffer=result.getBuffer(length); - if(buffer==0) { - result.truncate(0); - return result; - } - errorCode=U_ZERO_ERROR; - length=uloc_getDisplayVariant(fullName, displayLocale.fullName, - buffer, result.getCapacity(), - &errorCode); - result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); - } - - return result; -} - -UnicodeString& -Locale::getDisplayName( UnicodeString& name ) const -{ - return this->getDisplayName(getDefault(), name); -} - -UnicodeString& -Locale::getDisplayName(const Locale &displayLocale, - UnicodeString &result) const { - UChar *buffer; - UErrorCode errorCode=U_ZERO_ERROR; - int32_t length; - - buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); - if(buffer==0) { - result.truncate(0); - return result; - } - - length=uloc_getDisplayName(fullName, displayLocale.fullName, - buffer, result.getCapacity(), - &errorCode); - result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); - - if(errorCode==U_BUFFER_OVERFLOW_ERROR) { - buffer=result.getBuffer(length); - if(buffer==0) { - result.truncate(0); - return result; - } - errorCode=U_ZERO_ERROR; - length=uloc_getDisplayName(fullName, displayLocale.fullName, - buffer, result.getCapacity(), - &errorCode); - result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); - } - - return result; -} - -#if ! UCONFIG_NO_BREAK_ITERATION - -// ------------------------------------- -// Gets the objectLocale display name in the default locale language. -UnicodeString& U_EXPORT2 -BreakIterator::getDisplayName(const Locale& objectLocale, - UnicodeString& name) -{ - return objectLocale.getDisplayName(name); -} - -// ------------------------------------- -// Gets the objectLocale display name in the displayLocale language. -UnicodeString& U_EXPORT2 -BreakIterator::getDisplayName(const Locale& objectLocale, - const Locale& displayLocale, - UnicodeString& name) -{ - return objectLocale.getDisplayName(displayLocale, name); -} - -#endif - - -U_NAMESPACE_END - -// C API ------------------------------------------------------------------- *** - -U_NAMESPACE_USE - -/* ### Constants **************************************************/ - -/* These strings describe the resources we attempt to load from - the locale ResourceBundle data file.*/ -static const char _kLanguages[] = "Languages"; -static const char _kScripts[] = "Scripts"; -static const char _kScriptsStandAlone[] = "Scripts%stand-alone"; -static const char _kCountries[] = "Countries"; -static const char _kVariants[] = "Variants"; -static const char _kKeys[] = "Keys"; -static const char _kTypes[] = "Types"; -//static const char _kRootName[] = "root"; -static const char _kCurrency[] = "currency"; -static const char _kCurrencies[] = "Currencies"; -static const char _kLocaleDisplayPattern[] = "localeDisplayPattern"; -static const char _kPattern[] = "pattern"; -static const char _kSeparator[] = "separator"; - -/* ### Display name **************************************************/ - -static int32_t -_getStringOrCopyKey(const char *path, const char *locale, - const char *tableKey, - const char* subTableKey, - const char *itemKey, - const char *substitute, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) { - const UChar *s = NULL; - int32_t length = 0; - - if(itemKey==NULL) { - /* top-level item: normal resource bundle access */ - icu::LocalUResourceBundlePointer rb(ures_open(path, locale, pErrorCode)); - - if(U_SUCCESS(*pErrorCode)) { - s=ures_getStringByKey(rb.getAlias(), tableKey, &length, pErrorCode); - /* see comment about closing rb near "return item;" in _res_getTableStringWithFallback() */ - } - } else { - bool isLanguageCode = (uprv_strncmp(tableKey, _kLanguages, 9) == 0); - /* Language code should not be a number. If it is, set the error code. */ - if (isLanguageCode && uprv_strtol(itemKey, NULL, 10)) { - *pErrorCode = U_MISSING_RESOURCE_ERROR; - } else { - /* second-level item, use special fallback */ - s=uloc_getTableStringWithFallback(path, locale, - tableKey, - subTableKey, - itemKey, - &length, - pErrorCode); - if (U_FAILURE(*pErrorCode) && isLanguageCode && itemKey != nullptr) { - // convert itemKey locale code to canonical form and try again, ICU-20870 - *pErrorCode = U_ZERO_ERROR; - Locale canonKey = Locale::createCanonical(itemKey); - s=uloc_getTableStringWithFallback(path, locale, - tableKey, - subTableKey, - canonKey.getName(), - &length, - pErrorCode); - } - } - } - - if(U_SUCCESS(*pErrorCode)) { - int32_t copyLength=uprv_min(length, destCapacity); - if(copyLength>0 && s != NULL) { - u_memcpy(dest, s, copyLength); - } - } else { - /* no string from a resource bundle: convert the substitute */ - length=(int32_t)uprv_strlen(substitute); - u_charsToUChars(substitute, dest, uprv_min(length, destCapacity)); - *pErrorCode=U_USING_DEFAULT_WARNING; - } - - return u_terminateUChars(dest, destCapacity, length, pErrorCode); -} - -typedef int32_t U_CALLCONV UDisplayNameGetter(const char *, char *, int32_t, UErrorCode *); - -static int32_t -_getDisplayNameForComponent(const char *locale, - const char *displayLocale, - UChar *dest, int32_t destCapacity, - UDisplayNameGetter *getter, - const char *tag, - UErrorCode *pErrorCode) { - char localeBuffer[ULOC_FULLNAME_CAPACITY*4]; - int32_t length; - UErrorCode localStatus; - const char* root = NULL; - - /* argument checking */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - if(destCapacity<0 || (destCapacity>0 && dest==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - localStatus = U_ZERO_ERROR; - length=(*getter)(locale, localeBuffer, sizeof(localeBuffer), &localStatus); - if(U_FAILURE(localStatus) || localStatus==U_STRING_NOT_TERMINATED_WARNING) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - if(length==0) { - // For the display name, we treat this as unknown language (ICU-20273). - if (getter == uloc_getLanguage) { - uprv_strcpy(localeBuffer, "und"); - } else { - return u_terminateUChars(dest, destCapacity, 0, pErrorCode); - } - } - - root = tag == _kCountries ? U_ICUDATA_REGION : U_ICUDATA_LANG; - - return _getStringOrCopyKey(root, displayLocale, - tag, NULL, localeBuffer, - localeBuffer, - dest, destCapacity, - pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayLanguage(const char *locale, - const char *displayLocale, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) { - return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, - uloc_getLanguage, _kLanguages, pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayScript(const char* locale, - const char* displayLocale, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) -{ - UErrorCode err = U_ZERO_ERROR; - int32_t res = _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, - uloc_getScript, _kScriptsStandAlone, &err); - - if (destCapacity == 0 && err == U_BUFFER_OVERFLOW_ERROR) { - // For preflight, return the max of the value and the fallback. - int32_t fallback_res = _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, - uloc_getScript, _kScripts, pErrorCode); - return (fallback_res > res) ? fallback_res : res; - } - if ( err == U_USING_DEFAULT_WARNING ) { - return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, - uloc_getScript, _kScripts, pErrorCode); - } else { - *pErrorCode = err; - return res; - } -} - -static int32_t -uloc_getDisplayScriptInContext(const char* locale, - const char* displayLocale, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) -{ - return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, - uloc_getScript, _kScripts, pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayCountry(const char *locale, - const char *displayLocale, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) { - return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, - uloc_getCountry, _kCountries, pErrorCode); -} - -/* - * TODO separate variant1_variant2_variant3... - * by getting each tag's display string and concatenating them with ", " - * in between - similar to uloc_getDisplayName() - */ -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayVariant(const char *locale, - const char *displayLocale, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) { - return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, - uloc_getVariant, _kVariants, pErrorCode); -} - -/* Instead of having a separate pass for 'special' patterns, reintegrate the two - * so we don't get bitten by preflight bugs again. We can be reasonably efficient - * without two separate code paths, this code isn't that performance-critical. - * - * This code is general enough to deal with patterns that have a prefix or swap the - * language and remainder components, since we gave developers enough rope to do such - * things if they futz with the pattern data. But since we don't give them a way to - * specify a pattern for arbitrary combinations of components, there's not much use in - * that. I don't think our data includes such patterns, the only variable I know if is - * whether there is a space before the open paren, or not. Oh, and zh uses different - * chars than the standard open/close paren (which ja and ko use, btw). - */ -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayName(const char *locale, - const char *displayLocale, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) -{ - static const UChar defaultSeparator[9] = { 0x007b, 0x0030, 0x007d, 0x002c, 0x0020, 0x007b, 0x0031, 0x007d, 0x0000 }; /* "{0}, {1}" */ - static const UChar sub0[4] = { 0x007b, 0x0030, 0x007d , 0x0000 } ; /* {0} */ - static const UChar sub1[4] = { 0x007b, 0x0031, 0x007d , 0x0000 } ; /* {1} */ - static const int32_t subLen = 3; - static const UChar defaultPattern[10] = { - 0x007b, 0x0030, 0x007d, 0x0020, 0x0028, 0x007b, 0x0031, 0x007d, 0x0029, 0x0000 - }; /* {0} ({1}) */ - static const int32_t defaultPatLen = 9; - static const int32_t defaultSub0Pos = 0; - static const int32_t defaultSub1Pos = 5; - - int32_t length; /* of formatted result */ - - const UChar *separator; - int32_t sepLen = 0; - const UChar *pattern; - int32_t patLen = 0; - int32_t sub0Pos, sub1Pos; - - UChar formatOpenParen = 0x0028; // ( - UChar formatReplaceOpenParen = 0x005B; // [ - UChar formatCloseParen = 0x0029; // ) - UChar formatReplaceCloseParen = 0x005D; // ] - - UBool haveLang = true; /* assume true, set false if we find we don't have - a lang component in the locale */ - UBool haveRest = true; /* assume true, set false if we find we don't have - any other component in the locale */ - UBool retry = false; /* set true if we need to retry, see below */ - - int32_t langi = 0; /* index of the language substitution (0 or 1), virtually always 0 */ - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - if(destCapacity<0 || (destCapacity>0 && dest==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - { - UErrorCode status = U_ZERO_ERROR; - - icu::LocalUResourceBundlePointer locbundle( - ures_open(U_ICUDATA_LANG, displayLocale, &status)); - icu::LocalUResourceBundlePointer dspbundle( - ures_getByKeyWithFallback(locbundle.getAlias(), _kLocaleDisplayPattern, NULL, &status)); - - separator=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kSeparator, &sepLen, &status); - pattern=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kPattern, &patLen, &status); - } - - /* If we couldn't find any data, then use the defaults */ - if(sepLen == 0) { - separator = defaultSeparator; - } - /* #10244: Even though separator is now a pattern, it is awkward to handle it as such - * here since we are trying to build the display string in place in the dest buffer, - * and to handle it as a pattern would entail having separate storage for the - * substrings that need to be combined (the first of which may be the result of - * previous such combinations). So for now we continue to treat the portion between - * {0} and {1} as a string to be appended when joining substrings, ignoring anything - * that is before {0} or after {1} (no existing separator pattern has any such thing). - * This is similar to how pattern is handled below. - */ - { - UChar *p0=u_strstr(separator, sub0); - UChar *p1=u_strstr(separator, sub1); - if (p0==NULL || p1==NULL || p1(p1 - separator); - } - - if(patLen==0 || (patLen==defaultPatLen && !u_strncmp(pattern, defaultPattern, patLen))) { - pattern=defaultPattern; - patLen=defaultPatLen; - sub0Pos=defaultSub0Pos; - sub1Pos=defaultSub1Pos; - // use default formatOpenParen etc. set above - } else { /* non-default pattern */ - UChar *p0=u_strstr(pattern, sub0); - UChar *p1=u_strstr(pattern, sub1); - if (p0==NULL || p1==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - sub0Pos = static_cast(p0-pattern); - sub1Pos = static_cast(p1-pattern); - if (sub1Pos < sub0Pos) { /* a very odd pattern */ - int32_t t=sub0Pos; sub0Pos=sub1Pos; sub1Pos=t; - langi=1; - } - if (u_strchr(pattern, 0xFF08) != NULL) { - formatOpenParen = 0xFF08; // fullwidth ( - formatReplaceOpenParen = 0xFF3B; // fullwidth [ - formatCloseParen = 0xFF09; // fullwidth ) - formatReplaceCloseParen = 0xFF3D; // fullwidth ] - } - } - - /* We loop here because there is one case in which after the first pass we could need to - * reextract the data. If there's initial padding before the first element, we put in - * the padding and then write that element. If it turns out there's no second element, - * we didn't need the padding. If we do need the data (no preflight), and the first element - * would have fit but for the padding, we need to reextract. In this case (only) we - * adjust the parameters so padding is not added, and repeat. - */ - do { - UChar* p=dest; - int32_t patPos=0; /* position in the pattern, used for non-substitution portions */ - int32_t langLen=0; /* length of language substitution */ - int32_t langPos=0; /* position in output of language substitution */ - int32_t restLen=0; /* length of 'everything else' substitution */ - int32_t restPos=0; /* position in output of 'everything else' substitution */ - icu::LocalUEnumerationPointer kenum; /* keyword enumeration */ - - /* prefix of pattern, extremely likely to be empty */ - if(sub0Pos) { - if(destCapacity >= sub0Pos) { - while (patPos < sub0Pos) { - *p++ = pattern[patPos++]; - } - } else { - patPos=sub0Pos; - } - length=sub0Pos; - } else { - length=0; - } - - for(int32_t subi=0,resti=0;subi<2;) { /* iterate through patterns 0 and 1*/ - UBool subdone = false; /* set true when ready to move to next substitution */ - - /* prep p and cap for calls to get display components, pin cap to 0 since - they complain if cap is negative */ - int32_t cap=destCapacity-length; - if (cap <= 0) { - cap=0; - } else { - p=dest+length; - } - - if (subi == langi) { /* {0}*/ - if(haveLang) { - langPos=length; - langLen=uloc_getDisplayLanguage(locale, displayLocale, p, cap, pErrorCode); - length+=langLen; - haveLang=langLen>0; - } - subdone=true; - } else { /* {1} */ - if(!haveRest) { - subdone=true; - } else { - int32_t len; /* length of component (plus other stuff) we just fetched */ - switch(resti++) { - case 0: - restPos=length; - len=uloc_getDisplayScriptInContext(locale, displayLocale, p, cap, pErrorCode); - break; - case 1: - len=uloc_getDisplayCountry(locale, displayLocale, p, cap, pErrorCode); - break; - case 2: - len=uloc_getDisplayVariant(locale, displayLocale, p, cap, pErrorCode); - break; - case 3: - kenum.adoptInstead(uloc_openKeywords(locale, pErrorCode)); - U_FALLTHROUGH; - default: { - const char* kw=uenum_next(kenum.getAlias(), &len, pErrorCode); - if (kw == NULL) { - len=0; /* mark that we didn't add a component */ - subdone=true; - } else { - /* incorporating this behavior into the loop made it even more complex, - so just special case it here */ - len = uloc_getDisplayKeyword(kw, displayLocale, p, cap, pErrorCode); - if(len) { - if(len < cap) { - p[len]=0x3d; /* '=', assume we'll need it */ - } - len+=1; - - /* adjust for call to get keyword */ - cap-=len; - if(cap <= 0) { - cap=0; - } else { - p+=len; - } - } - /* reset for call below */ - if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) { - *pErrorCode=U_ZERO_ERROR; - } - int32_t vlen = uloc_getDisplayKeywordValue(locale, kw, displayLocale, - p, cap, pErrorCode); - if(len) { - if(vlen==0) { - --len; /* remove unneeded '=' */ - } - /* restore cap and p to what they were at start */ - cap=destCapacity-length; - if(cap <= 0) { - cap=0; - } else { - p=dest+length; - } - } - len+=vlen; /* total we added for key + '=' + value */ - } - } break; - } /* end switch */ - - if (len>0) { - /* we added a component, so add separator and write it if there's room. */ - if(len+sepLen<=cap) { - const UChar * plimit = p + len; - for (; p < plimit; p++) { - if (*p == formatOpenParen) { - *p = formatReplaceOpenParen; - } else if (*p == formatCloseParen) { - *p = formatReplaceCloseParen; - } - } - for(int32_t i=0;i0; - } - } - } - - if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) { - *pErrorCode=U_ZERO_ERROR; - } - - if(subdone) { - if(haveLang && haveRest) { - /* append internal portion of pattern, the first time, - or last portion of pattern the second time */ - int32_t padLen; - patPos+=subLen; - padLen=(subi==0 ? sub1Pos : patLen)-patPos; - if(length+padLen <= destCapacity) { - p=dest+length; - for(int32_t i=0;i0) { - /* true length is the length of just the component we got. */ - length=haveLang?langLen:restLen; - if(dest && sub0Pos!=0) { - if (sub0Pos+length<=destCapacity) { - /* first component not at start of result, - but we have full component in buffer. */ - u_memmove(dest, dest+(haveLang?langPos:restPos), length); - } else { - /* would have fit, but didn't because of pattern prefix. */ - sub0Pos=0; /* stops initial padding (and a second retry, - so we won't end up here again) */ - retry=true; - } - } - } - - ++subi; /* move on to next substitution */ - } - } - } while(retry); - - return u_terminateUChars(dest, destCapacity, length, pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayKeyword(const char* keyword, - const char* displayLocale, - UChar* dest, - int32_t destCapacity, - UErrorCode* status){ - - /* argument checking */ - if(status==NULL || U_FAILURE(*status)) { - return 0; - } - - if(destCapacity<0 || (destCapacity>0 && dest==NULL)) { - *status=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - - /* pass itemKey=NULL to look for a top-level item */ - return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale, - _kKeys, NULL, - keyword, - keyword, - dest, destCapacity, - status); - -} - - -#define UCURRENCY_DISPLAY_NAME_INDEX 1 - -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayKeywordValue( const char* locale, - const char* keyword, - const char* displayLocale, - UChar* dest, - int32_t destCapacity, - UErrorCode* status){ - - - /* argument checking */ - if(status==NULL || U_FAILURE(*status)) { - return 0; - } - - if(destCapacity<0 || (destCapacity>0 && dest==NULL)) { - *status=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* get the keyword value */ - CharString keywordValue; - { - CharStringByteSink sink(&keywordValue); - ulocimp_getKeywordValue(locale, keyword, sink, status); - } - - /* - * if the keyword is equal to currency .. then to get the display name - * we need to do the fallback ourselves - */ - if(uprv_stricmp(keyword, _kCurrency)==0){ - - int32_t dispNameLen = 0; - const UChar *dispName = NULL; - - icu::LocalUResourceBundlePointer bundle( - ures_open(U_ICUDATA_CURR, displayLocale, status)); - icu::LocalUResourceBundlePointer currencies( - ures_getByKey(bundle.getAlias(), _kCurrencies, NULL, status)); - icu::LocalUResourceBundlePointer currency( - ures_getByKeyWithFallback(currencies.getAlias(), keywordValue.data(), NULL, status)); - - dispName = ures_getStringByIndex(currency.getAlias(), UCURRENCY_DISPLAY_NAME_INDEX, &dispNameLen, status); - - if(U_FAILURE(*status)){ - if(*status == U_MISSING_RESOURCE_ERROR){ - /* we just want to write the value over if nothing is available */ - *status = U_USING_DEFAULT_WARNING; - }else{ - return 0; - } - } - - /* now copy the dispName over if not NULL */ - if(dispName != NULL){ - if(dispNameLen <= destCapacity){ - u_memcpy(dest, dispName, dispNameLen); - return u_terminateUChars(dest, destCapacity, dispNameLen, status); - }else{ - *status = U_BUFFER_OVERFLOW_ERROR; - return dispNameLen; - } - }else{ - /* we have not found the display name for the value .. just copy over */ - if(keywordValue.length() <= destCapacity){ - u_charsToUChars(keywordValue.data(), dest, keywordValue.length()); - return u_terminateUChars(dest, destCapacity, keywordValue.length(), status); - }else{ - *status = U_BUFFER_OVERFLOW_ERROR; - return keywordValue.length(); - } - } - - - }else{ - - return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale, - _kTypes, keyword, - keywordValue.data(), - keywordValue.data(), - dest, destCapacity, - status); - } -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1997-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: locdispnames.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010feb25 +* created by: Markus W. Scherer +* +* Code for locale display names, separated out from other .cpp files +* that then do not depend on resource bundle code and display name data. +*/ + +#include "unicode/utypes.h" +#include "unicode/brkiter.h" +#include "unicode/locid.h" +#include "unicode/uenum.h" +#include "unicode/uloc.h" +#include "unicode/ures.h" +#include "unicode/ustring.h" +#include "bytesinkutil.h" +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "putilimp.h" +#include "ulocimp.h" +#include "uresimp.h" +#include "ureslocs.h" +#include "ustr_imp.h" + +// C++ API ----------------------------------------------------------------- *** + +U_NAMESPACE_BEGIN + +UnicodeString& +Locale::getDisplayLanguage(UnicodeString& dispLang) const +{ + return this->getDisplayLanguage(getDefault(), dispLang); +} + +/*We cannot make any assumptions on the size of the output display strings +* Yet, since we are calling through to a C API, we need to set limits on +* buffer size. For all the following getDisplay functions we first attempt +* to fill up a stack allocated buffer. If it is to small we heap allocated +* the exact buffer we need copy it to the UnicodeString and delete it*/ + +UnicodeString& +Locale::getDisplayLanguage(const Locale &displayLocale, + UnicodeString &result) const { + char16_t *buffer; + UErrorCode errorCode=U_ZERO_ERROR; + int32_t length; + + buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); + if(buffer==0) { + result.truncate(0); + return result; + } + + length=uloc_getDisplayLanguage(fullName, displayLocale.fullName, + buffer, result.getCapacity(), + &errorCode); + result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); + + if(errorCode==U_BUFFER_OVERFLOW_ERROR) { + buffer=result.getBuffer(length); + if(buffer==0) { + result.truncate(0); + return result; + } + errorCode=U_ZERO_ERROR; + length=uloc_getDisplayLanguage(fullName, displayLocale.fullName, + buffer, result.getCapacity(), + &errorCode); + result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); + } + + return result; +} + +UnicodeString& +Locale::getDisplayScript(UnicodeString& dispScript) const +{ + return this->getDisplayScript(getDefault(), dispScript); +} + +UnicodeString& +Locale::getDisplayScript(const Locale &displayLocale, + UnicodeString &result) const { + char16_t *buffer; + UErrorCode errorCode=U_ZERO_ERROR; + int32_t length; + + buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); + if(buffer==0) { + result.truncate(0); + return result; + } + + length=uloc_getDisplayScript(fullName, displayLocale.fullName, + buffer, result.getCapacity(), + &errorCode); + result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); + + if(errorCode==U_BUFFER_OVERFLOW_ERROR) { + buffer=result.getBuffer(length); + if(buffer==0) { + result.truncate(0); + return result; + } + errorCode=U_ZERO_ERROR; + length=uloc_getDisplayScript(fullName, displayLocale.fullName, + buffer, result.getCapacity(), + &errorCode); + result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); + } + + return result; +} + +UnicodeString& +Locale::getDisplayCountry(UnicodeString& dispCntry) const +{ + return this->getDisplayCountry(getDefault(), dispCntry); +} + +UnicodeString& +Locale::getDisplayCountry(const Locale &displayLocale, + UnicodeString &result) const { + char16_t *buffer; + UErrorCode errorCode=U_ZERO_ERROR; + int32_t length; + + buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); + if(buffer==0) { + result.truncate(0); + return result; + } + + length=uloc_getDisplayCountry(fullName, displayLocale.fullName, + buffer, result.getCapacity(), + &errorCode); + result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); + + if(errorCode==U_BUFFER_OVERFLOW_ERROR) { + buffer=result.getBuffer(length); + if(buffer==0) { + result.truncate(0); + return result; + } + errorCode=U_ZERO_ERROR; + length=uloc_getDisplayCountry(fullName, displayLocale.fullName, + buffer, result.getCapacity(), + &errorCode); + result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); + } + + return result; +} + +UnicodeString& +Locale::getDisplayVariant(UnicodeString& dispVar) const +{ + return this->getDisplayVariant(getDefault(), dispVar); +} + +UnicodeString& +Locale::getDisplayVariant(const Locale &displayLocale, + UnicodeString &result) const { + char16_t *buffer; + UErrorCode errorCode=U_ZERO_ERROR; + int32_t length; + + buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); + if(buffer==0) { + result.truncate(0); + return result; + } + + length=uloc_getDisplayVariant(fullName, displayLocale.fullName, + buffer, result.getCapacity(), + &errorCode); + result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); + + if(errorCode==U_BUFFER_OVERFLOW_ERROR) { + buffer=result.getBuffer(length); + if(buffer==0) { + result.truncate(0); + return result; + } + errorCode=U_ZERO_ERROR; + length=uloc_getDisplayVariant(fullName, displayLocale.fullName, + buffer, result.getCapacity(), + &errorCode); + result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); + } + + return result; +} + +UnicodeString& +Locale::getDisplayName( UnicodeString& name ) const +{ + return this->getDisplayName(getDefault(), name); +} + +UnicodeString& +Locale::getDisplayName(const Locale &displayLocale, + UnicodeString &result) const { + char16_t *buffer; + UErrorCode errorCode=U_ZERO_ERROR; + int32_t length; + + buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); + if(buffer==0) { + result.truncate(0); + return result; + } + + length=uloc_getDisplayName(fullName, displayLocale.fullName, + buffer, result.getCapacity(), + &errorCode); + result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); + + if(errorCode==U_BUFFER_OVERFLOW_ERROR) { + buffer=result.getBuffer(length); + if(buffer==0) { + result.truncate(0); + return result; + } + errorCode=U_ZERO_ERROR; + length=uloc_getDisplayName(fullName, displayLocale.fullName, + buffer, result.getCapacity(), + &errorCode); + result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); + } + + return result; +} + +#if ! UCONFIG_NO_BREAK_ITERATION + +// ------------------------------------- +// Gets the objectLocale display name in the default locale language. +UnicodeString& U_EXPORT2 +BreakIterator::getDisplayName(const Locale& objectLocale, + UnicodeString& name) +{ + return objectLocale.getDisplayName(name); +} + +// ------------------------------------- +// Gets the objectLocale display name in the displayLocale language. +UnicodeString& U_EXPORT2 +BreakIterator::getDisplayName(const Locale& objectLocale, + const Locale& displayLocale, + UnicodeString& name) +{ + return objectLocale.getDisplayName(displayLocale, name); +} + +#endif + + +U_NAMESPACE_END + +// C API ------------------------------------------------------------------- *** + +U_NAMESPACE_USE + +/* ### Constants **************************************************/ + +/* These strings describe the resources we attempt to load from + the locale ResourceBundle data file.*/ +static const char _kLanguages[] = "Languages"; +static const char _kScripts[] = "Scripts"; +static const char _kScriptsStandAlone[] = "Scripts%stand-alone"; +static const char _kCountries[] = "Countries"; +static const char _kVariants[] = "Variants"; +static const char _kKeys[] = "Keys"; +static const char _kTypes[] = "Types"; +//static const char _kRootName[] = "root"; +static const char _kCurrency[] = "currency"; +static const char _kCurrencies[] = "Currencies"; +static const char _kLocaleDisplayPattern[] = "localeDisplayPattern"; +static const char _kPattern[] = "pattern"; +static const char _kSeparator[] = "separator"; + +/* ### Display name **************************************************/ + +static int32_t +_getStringOrCopyKey(const char *path, const char *locale, + const char *tableKey, + const char* subTableKey, + const char *itemKey, + const char *substitute, + char16_t *dest, int32_t destCapacity, + UErrorCode *pErrorCode) { + const char16_t *s = nullptr; + int32_t length = 0; + + if(itemKey==nullptr) { + /* top-level item: normal resource bundle access */ + icu::LocalUResourceBundlePointer rb(ures_open(path, locale, pErrorCode)); + + if(U_SUCCESS(*pErrorCode)) { + s=ures_getStringByKey(rb.getAlias(), tableKey, &length, pErrorCode); + /* see comment about closing rb near "return item;" in _res_getTableStringWithFallback() */ + } + } else { + bool isLanguageCode = (uprv_strncmp(tableKey, _kLanguages, 9) == 0); + /* Language code should not be a number. If it is, set the error code. */ + if (isLanguageCode && uprv_strtol(itemKey, nullptr, 10)) { + *pErrorCode = U_MISSING_RESOURCE_ERROR; + } else { + /* second-level item, use special fallback */ + s=uloc_getTableStringWithFallback(path, locale, + tableKey, + subTableKey, + itemKey, + &length, + pErrorCode); + if (U_FAILURE(*pErrorCode) && isLanguageCode && itemKey != nullptr) { + // convert itemKey locale code to canonical form and try again, ICU-20870 + *pErrorCode = U_ZERO_ERROR; + Locale canonKey = Locale::createCanonical(itemKey); + s=uloc_getTableStringWithFallback(path, locale, + tableKey, + subTableKey, + canonKey.getName(), + &length, + pErrorCode); + } + } + } + + if(U_SUCCESS(*pErrorCode)) { + int32_t copyLength=uprv_min(length, destCapacity); + if(copyLength>0 && s != nullptr) { + u_memcpy(dest, s, copyLength); + } + } else { + /* no string from a resource bundle: convert the substitute */ + length=(int32_t)uprv_strlen(substitute); + u_charsToUChars(substitute, dest, uprv_min(length, destCapacity)); + *pErrorCode=U_USING_DEFAULT_WARNING; + } + + return u_terminateUChars(dest, destCapacity, length, pErrorCode); +} + +typedef int32_t U_CALLCONV UDisplayNameGetter(const char *, char *, int32_t, UErrorCode *); + +static int32_t +_getDisplayNameForComponent(const char *locale, + const char *displayLocale, + char16_t *dest, int32_t destCapacity, + UDisplayNameGetter *getter, + const char *tag, + UErrorCode *pErrorCode) { + char localeBuffer[ULOC_FULLNAME_CAPACITY*4]; + int32_t length; + UErrorCode localStatus; + const char* root = nullptr; + + /* argument checking */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + if(destCapacity<0 || (destCapacity>0 && dest==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + localStatus = U_ZERO_ERROR; + length=(*getter)(locale, localeBuffer, sizeof(localeBuffer), &localStatus); + if(U_FAILURE(localStatus) || localStatus==U_STRING_NOT_TERMINATED_WARNING) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + if(length==0) { + // For the display name, we treat this as unknown language (ICU-20273). + if (getter == uloc_getLanguage) { + uprv_strcpy(localeBuffer, "und"); + } else { + return u_terminateUChars(dest, destCapacity, 0, pErrorCode); + } + } + + root = tag == _kCountries ? U_ICUDATA_REGION : U_ICUDATA_LANG; + + return _getStringOrCopyKey(root, displayLocale, + tag, nullptr, localeBuffer, + localeBuffer, + dest, destCapacity, + pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayLanguage(const char *locale, + const char *displayLocale, + char16_t *dest, int32_t destCapacity, + UErrorCode *pErrorCode) { + return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, + uloc_getLanguage, _kLanguages, pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayScript(const char* locale, + const char* displayLocale, + char16_t *dest, int32_t destCapacity, + UErrorCode *pErrorCode) +{ + UErrorCode err = U_ZERO_ERROR; + int32_t res = _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, + uloc_getScript, _kScriptsStandAlone, &err); + + if (destCapacity == 0 && err == U_BUFFER_OVERFLOW_ERROR) { + // For preflight, return the max of the value and the fallback. + int32_t fallback_res = _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, + uloc_getScript, _kScripts, pErrorCode); + return (fallback_res > res) ? fallback_res : res; + } + if ( err == U_USING_DEFAULT_WARNING ) { + return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, + uloc_getScript, _kScripts, pErrorCode); + } else { + *pErrorCode = err; + return res; + } +} + +static int32_t +uloc_getDisplayScriptInContext(const char* locale, + const char* displayLocale, + char16_t *dest, int32_t destCapacity, + UErrorCode *pErrorCode) +{ + return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, + uloc_getScript, _kScripts, pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayCountry(const char *locale, + const char *displayLocale, + char16_t *dest, int32_t destCapacity, + UErrorCode *pErrorCode) { + return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, + uloc_getCountry, _kCountries, pErrorCode); +} + +/* + * TODO separate variant1_variant2_variant3... + * by getting each tag's display string and concatenating them with ", " + * in between - similar to uloc_getDisplayName() + */ +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayVariant(const char *locale, + const char *displayLocale, + char16_t *dest, int32_t destCapacity, + UErrorCode *pErrorCode) { + return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, + uloc_getVariant, _kVariants, pErrorCode); +} + +/* Instead of having a separate pass for 'special' patterns, reintegrate the two + * so we don't get bitten by preflight bugs again. We can be reasonably efficient + * without two separate code paths, this code isn't that performance-critical. + * + * This code is general enough to deal with patterns that have a prefix or swap the + * language and remainder components, since we gave developers enough rope to do such + * things if they futz with the pattern data. But since we don't give them a way to + * specify a pattern for arbitrary combinations of components, there's not much use in + * that. I don't think our data includes such patterns, the only variable I know if is + * whether there is a space before the open paren, or not. Oh, and zh uses different + * chars than the standard open/close paren (which ja and ko use, btw). + */ +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayName(const char *locale, + const char *displayLocale, + char16_t *dest, int32_t destCapacity, + UErrorCode *pErrorCode) +{ + static const char16_t defaultSeparator[9] = { 0x007b, 0x0030, 0x007d, 0x002c, 0x0020, 0x007b, 0x0031, 0x007d, 0x0000 }; /* "{0}, {1}" */ + static const char16_t sub0[4] = { 0x007b, 0x0030, 0x007d , 0x0000 } ; /* {0} */ + static const char16_t sub1[4] = { 0x007b, 0x0031, 0x007d , 0x0000 } ; /* {1} */ + static const int32_t subLen = 3; + static const char16_t defaultPattern[10] = { + 0x007b, 0x0030, 0x007d, 0x0020, 0x0028, 0x007b, 0x0031, 0x007d, 0x0029, 0x0000 + }; /* {0} ({1}) */ + static const int32_t defaultPatLen = 9; + static const int32_t defaultSub0Pos = 0; + static const int32_t defaultSub1Pos = 5; + + int32_t length; /* of formatted result */ + + const char16_t *separator; + int32_t sepLen = 0; + const char16_t *pattern; + int32_t patLen = 0; + int32_t sub0Pos, sub1Pos; + + char16_t formatOpenParen = 0x0028; // ( + char16_t formatReplaceOpenParen = 0x005B; // [ + char16_t formatCloseParen = 0x0029; // ) + char16_t formatReplaceCloseParen = 0x005D; // ] + + UBool haveLang = true; /* assume true, set false if we find we don't have + a lang component in the locale */ + UBool haveRest = true; /* assume true, set false if we find we don't have + any other component in the locale */ + UBool retry = false; /* set true if we need to retry, see below */ + + int32_t langi = 0; /* index of the language substitution (0 or 1), virtually always 0 */ + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + if(destCapacity<0 || (destCapacity>0 && dest==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + { + UErrorCode status = U_ZERO_ERROR; + + icu::LocalUResourceBundlePointer locbundle( + ures_open(U_ICUDATA_LANG, displayLocale, &status)); + icu::LocalUResourceBundlePointer dspbundle( + ures_getByKeyWithFallback(locbundle.getAlias(), _kLocaleDisplayPattern, nullptr, &status)); + + separator=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kSeparator, &sepLen, &status); + pattern=ures_getStringByKeyWithFallback(dspbundle.getAlias(), _kPattern, &patLen, &status); + } + + /* If we couldn't find any data, then use the defaults */ + if(sepLen == 0) { + separator = defaultSeparator; + } + /* #10244: Even though separator is now a pattern, it is awkward to handle it as such + * here since we are trying to build the display string in place in the dest buffer, + * and to handle it as a pattern would entail having separate storage for the + * substrings that need to be combined (the first of which may be the result of + * previous such combinations). So for now we continue to treat the portion between + * {0} and {1} as a string to be appended when joining substrings, ignoring anything + * that is before {0} or after {1} (no existing separator pattern has any such thing). + * This is similar to how pattern is handled below. + */ + { + char16_t *p0=u_strstr(separator, sub0); + char16_t *p1=u_strstr(separator, sub1); + if (p0==nullptr || p1==nullptr || p1(p1 - separator); + } + + if(patLen==0 || (patLen==defaultPatLen && !u_strncmp(pattern, defaultPattern, patLen))) { + pattern=defaultPattern; + patLen=defaultPatLen; + sub0Pos=defaultSub0Pos; + sub1Pos=defaultSub1Pos; + // use default formatOpenParen etc. set above + } else { /* non-default pattern */ + char16_t *p0=u_strstr(pattern, sub0); + char16_t *p1=u_strstr(pattern, sub1); + if (p0==nullptr || p1==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + sub0Pos = static_cast(p0-pattern); + sub1Pos = static_cast(p1-pattern); + if (sub1Pos < sub0Pos) { /* a very odd pattern */ + int32_t t=sub0Pos; sub0Pos=sub1Pos; sub1Pos=t; + langi=1; + } + if (u_strchr(pattern, 0xFF08) != nullptr) { + formatOpenParen = 0xFF08; // fullwidth ( + formatReplaceOpenParen = 0xFF3B; // fullwidth [ + formatCloseParen = 0xFF09; // fullwidth ) + formatReplaceCloseParen = 0xFF3D; // fullwidth ] + } + } + + /* We loop here because there is one case in which after the first pass we could need to + * reextract the data. If there's initial padding before the first element, we put in + * the padding and then write that element. If it turns out there's no second element, + * we didn't need the padding. If we do need the data (no preflight), and the first element + * would have fit but for the padding, we need to reextract. In this case (only) we + * adjust the parameters so padding is not added, and repeat. + */ + do { + char16_t* p=dest; + int32_t patPos=0; /* position in the pattern, used for non-substitution portions */ + int32_t langLen=0; /* length of language substitution */ + int32_t langPos=0; /* position in output of language substitution */ + int32_t restLen=0; /* length of 'everything else' substitution */ + int32_t restPos=0; /* position in output of 'everything else' substitution */ + icu::LocalUEnumerationPointer kenum; /* keyword enumeration */ + + /* prefix of pattern, extremely likely to be empty */ + if(sub0Pos) { + if(destCapacity >= sub0Pos) { + while (patPos < sub0Pos) { + *p++ = pattern[patPos++]; + } + } else { + patPos=sub0Pos; + } + length=sub0Pos; + } else { + length=0; + } + + for(int32_t subi=0,resti=0;subi<2;) { /* iterate through patterns 0 and 1*/ + UBool subdone = false; /* set true when ready to move to next substitution */ + + /* prep p and cap for calls to get display components, pin cap to 0 since + they complain if cap is negative */ + int32_t cap=destCapacity-length; + if (cap <= 0) { + cap=0; + } else { + p=dest+length; + } + + if (subi == langi) { /* {0}*/ + if(haveLang) { + langPos=length; + langLen=uloc_getDisplayLanguage(locale, displayLocale, p, cap, pErrorCode); + length+=langLen; + haveLang=langLen>0; + } + subdone=true; + } else { /* {1} */ + if(!haveRest) { + subdone=true; + } else { + int32_t len; /* length of component (plus other stuff) we just fetched */ + switch(resti++) { + case 0: + restPos=length; + len=uloc_getDisplayScriptInContext(locale, displayLocale, p, cap, pErrorCode); + break; + case 1: + len=uloc_getDisplayCountry(locale, displayLocale, p, cap, pErrorCode); + break; + case 2: + len=uloc_getDisplayVariant(locale, displayLocale, p, cap, pErrorCode); + break; + case 3: + kenum.adoptInstead(uloc_openKeywords(locale, pErrorCode)); + U_FALLTHROUGH; + default: { + const char* kw=uenum_next(kenum.getAlias(), &len, pErrorCode); + if (kw == nullptr) { + len=0; /* mark that we didn't add a component */ + subdone=true; + } else { + /* incorporating this behavior into the loop made it even more complex, + so just special case it here */ + len = uloc_getDisplayKeyword(kw, displayLocale, p, cap, pErrorCode); + if(len) { + if(len < cap) { + p[len]=0x3d; /* '=', assume we'll need it */ + } + len+=1; + + /* adjust for call to get keyword */ + cap-=len; + if(cap <= 0) { + cap=0; + } else { + p+=len; + } + } + /* reset for call below */ + if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) { + *pErrorCode=U_ZERO_ERROR; + } + int32_t vlen = uloc_getDisplayKeywordValue(locale, kw, displayLocale, + p, cap, pErrorCode); + if(len) { + if(vlen==0) { + --len; /* remove unneeded '=' */ + } + /* restore cap and p to what they were at start */ + cap=destCapacity-length; + if(cap <= 0) { + cap=0; + } else { + p=dest+length; + } + } + len+=vlen; /* total we added for key + '=' + value */ + } + } break; + } /* end switch */ + + if (len>0) { + /* we added a component, so add separator and write it if there's room. */ + if(len+sepLen<=cap) { + const char16_t * plimit = p + len; + for (; p < plimit; p++) { + if (*p == formatOpenParen) { + *p = formatReplaceOpenParen; + } else if (*p == formatCloseParen) { + *p = formatReplaceCloseParen; + } + } + for(int32_t i=0;i0; + } + } + } + + if(*pErrorCode == U_BUFFER_OVERFLOW_ERROR) { + *pErrorCode=U_ZERO_ERROR; + } + + if(subdone) { + if(haveLang && haveRest) { + /* append internal portion of pattern, the first time, + or last portion of pattern the second time */ + int32_t padLen; + patPos+=subLen; + padLen=(subi==0 ? sub1Pos : patLen)-patPos; + if(length+padLen <= destCapacity) { + p=dest+length; + for(int32_t i=0;i0) { + /* true length is the length of just the component we got. */ + length=haveLang?langLen:restLen; + if(dest && sub0Pos!=0) { + if (sub0Pos+length<=destCapacity) { + /* first component not at start of result, + but we have full component in buffer. */ + u_memmove(dest, dest+(haveLang?langPos:restPos), length); + } else { + /* would have fit, but didn't because of pattern prefix. */ + sub0Pos=0; /* stops initial padding (and a second retry, + so we won't end up here again) */ + retry=true; + } + } + } + + ++subi; /* move on to next substitution */ + } + } + } while(retry); + + return u_terminateUChars(dest, destCapacity, length, pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayKeyword(const char* keyword, + const char* displayLocale, + char16_t* dest, + int32_t destCapacity, + UErrorCode* status){ + + /* argument checking */ + if(status==nullptr || U_FAILURE(*status)) { + return 0; + } + + if(destCapacity<0 || (destCapacity>0 && dest==nullptr)) { + *status=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + + /* pass itemKey=nullptr to look for a top-level item */ + return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale, + _kKeys, nullptr, + keyword, + keyword, + dest, destCapacity, + status); + +} + + +#define UCURRENCY_DISPLAY_NAME_INDEX 1 + +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayKeywordValue( const char* locale, + const char* keyword, + const char* displayLocale, + char16_t* dest, + int32_t destCapacity, + UErrorCode* status){ + + + /* argument checking */ + if(status==nullptr || U_FAILURE(*status)) { + return 0; + } + + if(destCapacity<0 || (destCapacity>0 && dest==nullptr)) { + *status=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* get the keyword value */ + CharString keywordValue; + { + CharStringByteSink sink(&keywordValue); + ulocimp_getKeywordValue(locale, keyword, sink, status); + } + + /* + * if the keyword is equal to currency .. then to get the display name + * we need to do the fallback ourselves + */ + if(uprv_stricmp(keyword, _kCurrency)==0){ + + int32_t dispNameLen = 0; + const char16_t *dispName = nullptr; + + icu::LocalUResourceBundlePointer bundle( + ures_open(U_ICUDATA_CURR, displayLocale, status)); + icu::LocalUResourceBundlePointer currencies( + ures_getByKey(bundle.getAlias(), _kCurrencies, nullptr, status)); + icu::LocalUResourceBundlePointer currency( + ures_getByKeyWithFallback(currencies.getAlias(), keywordValue.data(), nullptr, status)); + + dispName = ures_getStringByIndex(currency.getAlias(), UCURRENCY_DISPLAY_NAME_INDEX, &dispNameLen, status); + + if(U_FAILURE(*status)){ + if(*status == U_MISSING_RESOURCE_ERROR){ + /* we just want to write the value over if nothing is available */ + *status = U_USING_DEFAULT_WARNING; + }else{ + return 0; + } + } + + /* now copy the dispName over if not nullptr */ + if(dispName != nullptr){ + if(dispNameLen <= destCapacity){ + u_memcpy(dest, dispName, dispNameLen); + return u_terminateUChars(dest, destCapacity, dispNameLen, status); + }else{ + *status = U_BUFFER_OVERFLOW_ERROR; + return dispNameLen; + } + }else{ + /* we have not found the display name for the value .. just copy over */ + if(keywordValue.length() <= destCapacity){ + u_charsToUChars(keywordValue.data(), dest, keywordValue.length()); + return u_terminateUChars(dest, destCapacity, keywordValue.length(), status); + }else{ + *status = U_BUFFER_OVERFLOW_ERROR; + return keywordValue.length(); + } + } + + + }else{ + + return _getStringOrCopyKey(U_ICUDATA_LANG, displayLocale, + _kTypes, keyword, + keywordValue.data(), + keywordValue.data(), + dest, destCapacity, + status); + } +} diff --git a/deps/icu-small/source/common/locdistance.cpp b/deps/icu-small/source/common/locdistance.cpp index fb22fe79ed36a2..46a4729d0fcfb8 100644 --- a/deps/icu-small/source/common/locdistance.cpp +++ b/deps/icu-small/source/common/locdistance.cpp @@ -1,415 +1,415 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// locdistance.cpp -// created: 2019may08 Markus W. Scherer - -#include "unicode/utypes.h" -#include "unicode/bytestrie.h" -#include "unicode/localematcher.h" -#include "unicode/locid.h" -#include "unicode/uobject.h" -#include "unicode/ures.h" -#include "cstring.h" -#include "locdistance.h" -#include "loclikelysubtags.h" -#include "uassert.h" -#include "ucln_cmn.h" -#include "uinvchar.h" -#include "umutex.h" - -U_NAMESPACE_BEGIN - -namespace { - -/** - * Bit flag used on the last character of a subtag in the trie. - * Must be set consistently by the builder and the lookup code. - */ -constexpr int32_t END_OF_SUBTAG = 0x80; -/** Distance value bit flag, set by the builder. */ -constexpr int32_t DISTANCE_SKIP_SCRIPT = 0x80; -/** Distance value bit flag, set by trieNext(). */ -constexpr int32_t DISTANCE_IS_FINAL = 0x100; -constexpr int32_t DISTANCE_IS_FINAL_OR_SKIP_SCRIPT = DISTANCE_IS_FINAL | DISTANCE_SKIP_SCRIPT; - -constexpr int32_t ABOVE_THRESHOLD = 100; - -// Indexes into array of distances. -enum { - IX_DEF_LANG_DISTANCE, - IX_DEF_SCRIPT_DISTANCE, - IX_DEF_REGION_DISTANCE, - IX_MIN_REGION_DISTANCE, - IX_LIMIT -}; - -LocaleDistance *gLocaleDistance = nullptr; -UInitOnce gInitOnce {}; - -UBool U_CALLCONV cleanup() { - delete gLocaleDistance; - gLocaleDistance = nullptr; - gInitOnce.reset(); - return true; -} - -} // namespace - -void U_CALLCONV LocaleDistance::initLocaleDistance(UErrorCode &errorCode) { - // This function is invoked only via umtx_initOnce(). - U_ASSERT(gLocaleDistance == nullptr); - const XLikelySubtags &likely = *XLikelySubtags::getSingleton(errorCode); - if (U_FAILURE(errorCode)) { return; } - const LocaleDistanceData &data = likely.getDistanceData(); - if (data.distanceTrieBytes == nullptr || - data.regionToPartitions == nullptr || data.partitions == nullptr || - // ok if no paradigms - data.distances == nullptr) { - errorCode = U_MISSING_RESOURCE_ERROR; - return; - } - gLocaleDistance = new LocaleDistance(data, likely); - if (gLocaleDistance == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - ucln_common_registerCleanup(UCLN_COMMON_LOCALE_DISTANCE, cleanup); -} - -const LocaleDistance *LocaleDistance::getSingleton(UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return nullptr; } - umtx_initOnce(gInitOnce, &LocaleDistance::initLocaleDistance, errorCode); - return gLocaleDistance; -} - -LocaleDistance::LocaleDistance(const LocaleDistanceData &data, const XLikelySubtags &likely) : - likelySubtags(likely), - trie(data.distanceTrieBytes), - regionToPartitionsIndex(data.regionToPartitions), partitionArrays(data.partitions), - paradigmLSRs(data.paradigms), paradigmLSRsLength(data.paradigmsLength), - defaultLanguageDistance(data.distances[IX_DEF_LANG_DISTANCE]), - defaultScriptDistance(data.distances[IX_DEF_SCRIPT_DISTANCE]), - defaultRegionDistance(data.distances[IX_DEF_REGION_DISTANCE]), - minRegionDistance(data.distances[IX_MIN_REGION_DISTANCE]) { - // For the default demotion value, use the - // default region distance between unrelated Englishes. - // Thus, unless demotion is turned off, - // a mere region difference for one desired locale - // is as good as a perfect match for the next following desired locale. - // As of CLDR 36, we have . - LSR en("en", "Latn", "US", LSR::EXPLICIT_LSR); - LSR enGB("en", "Latn", "GB", LSR::EXPLICIT_LSR); - const LSR *p_enGB = &enGB; - int32_t indexAndDistance = getBestIndexAndDistance(en, &p_enGB, 1, - shiftDistance(50), ULOCMATCH_FAVOR_LANGUAGE, ULOCMATCH_DIRECTION_WITH_ONE_WAY); - defaultDemotionPerDesiredLocale = getDistanceFloor(indexAndDistance); -} - -int32_t LocaleDistance::getBestIndexAndDistance( - const LSR &desired, - const LSR **supportedLSRs, int32_t supportedLSRsLength, - int32_t shiftedThreshold, - ULocMatchFavorSubtag favorSubtag, ULocMatchDirection direction) const { - BytesTrie iter(trie); - // Look up the desired language only once for all supported LSRs. - // Its "distance" is either a match point value of 0, or a non-match negative value. - // Note: The data builder verifies that there are no <*, supported> or rules. - int32_t desLangDistance = trieNext(iter, desired.language, false); - uint64_t desLangState = desLangDistance >= 0 && supportedLSRsLength > 1 ? iter.getState64() : 0; - // Index of the supported LSR with the lowest distance. - int32_t bestIndex = -1; - // Cached lookup info from XLikelySubtags.compareLikely(). - int32_t bestLikelyInfo = -1; - for (int32_t slIndex = 0; slIndex < supportedLSRsLength; ++slIndex) { - const LSR &supported = *supportedLSRs[slIndex]; - bool star = false; - int32_t distance = desLangDistance; - if (distance >= 0) { - U_ASSERT((distance & DISTANCE_IS_FINAL) == 0); - if (slIndex != 0) { - iter.resetToState64(desLangState); - } - distance = trieNext(iter, supported.language, true); - } - // Note: The data builder verifies that there are no rules with "any" (*) language and - // real (non *) script or region subtags. - // This means that if the lookup for either language fails we can use - // the default distances without further lookups. - int32_t flags; - if (distance >= 0) { - flags = distance & DISTANCE_IS_FINAL_OR_SKIP_SCRIPT; - distance &= ~DISTANCE_IS_FINAL_OR_SKIP_SCRIPT; - } else { // <*, *> - if (uprv_strcmp(desired.language, supported.language) == 0) { - distance = 0; - } else { - distance = defaultLanguageDistance; - } - flags = 0; - star = true; - } - U_ASSERT(0 <= distance && distance <= 100); - // Round up the shifted threshold (if fraction bits are not 0) - // for comparison with un-shifted distances until we need fraction bits. - // (If we simply shifted non-zero fraction bits away, then we might ignore a language - // when it's really still a micro distance below the threshold.) - int32_t roundedThreshold = (shiftedThreshold + DISTANCE_FRACTION_MASK) >> DISTANCE_SHIFT; - // We implement "favor subtag" by reducing the language subtag distance - // (unscientifically reducing it to a quarter of the normal value), - // so that the script distance is relatively more important. - // For example, given a default language distance of 80, we reduce it to 20, - // which is below the default threshold of 50, which is the default script distance. - if (favorSubtag == ULOCMATCH_FAVOR_SCRIPT) { - distance >>= 2; - } - // Let distance == roundedThreshold pass until the tie-breaker logic - // at the end of the loop. - if (distance > roundedThreshold) { - continue; - } - - int32_t scriptDistance; - if (star || flags != 0) { - if (uprv_strcmp(desired.script, supported.script) == 0) { - scriptDistance = 0; - } else { - scriptDistance = defaultScriptDistance; - } - } else { - scriptDistance = getDesSuppScriptDistance(iter, iter.getState64(), - desired.script, supported.script); - flags = scriptDistance & DISTANCE_IS_FINAL; - scriptDistance &= ~DISTANCE_IS_FINAL; - } - distance += scriptDistance; - if (distance > roundedThreshold) { - continue; - } - - if (uprv_strcmp(desired.region, supported.region) == 0) { - // regionDistance = 0 - } else if (star || (flags & DISTANCE_IS_FINAL) != 0) { - distance += defaultRegionDistance; - } else { - int32_t remainingThreshold = roundedThreshold - distance; - if (minRegionDistance > remainingThreshold) { - continue; - } - - // From here on we know the regions are not equal. - // Map each region to zero or more partitions. (zero = one non-matching string) - // (Each array of single-character partition strings is encoded as one string.) - // If either side has more than one, then we find the maximum distance. - // This could be optimized by adding some more structure, but probably not worth it. - distance += getRegionPartitionsDistance( - iter, iter.getState64(), - partitionsForRegion(desired), - partitionsForRegion(supported), - remainingThreshold); - } - int32_t shiftedDistance = shiftDistance(distance); - if (shiftedDistance == 0) { - // Distinguish between equivalent but originally unequal locales via an - // additional micro distance. - shiftedDistance |= (desired.flags ^ supported.flags); - if (shiftedDistance < shiftedThreshold) { - if (direction != ULOCMATCH_DIRECTION_ONLY_TWO_WAY || - // Is there also a match when we swap desired/supported? - isMatch(supported, desired, shiftedThreshold, favorSubtag)) { - if (shiftedDistance == 0) { - return slIndex << INDEX_SHIFT; - } - bestIndex = slIndex; - shiftedThreshold = shiftedDistance; - bestLikelyInfo = -1; - } - } - } else { - if (shiftedDistance < shiftedThreshold) { - if (direction != ULOCMATCH_DIRECTION_ONLY_TWO_WAY || - // Is there also a match when we swap desired/supported? - isMatch(supported, desired, shiftedThreshold, favorSubtag)) { - bestIndex = slIndex; - shiftedThreshold = shiftedDistance; - bestLikelyInfo = -1; - } - } else if (shiftedDistance == shiftedThreshold && bestIndex >= 0) { - if (direction != ULOCMATCH_DIRECTION_ONLY_TWO_WAY || - // Is there also a match when we swap desired/supported? - isMatch(supported, desired, shiftedThreshold, favorSubtag)) { - bestLikelyInfo = likelySubtags.compareLikely( - supported, *supportedLSRs[bestIndex], bestLikelyInfo); - if ((bestLikelyInfo & 1) != 0) { - // This supported locale matches as well as the previous best match, - // and neither matches perfectly, - // but this one is "more likely" (has more-default subtags). - bestIndex = slIndex; - } - } - } - } - } - return bestIndex >= 0 ? - (bestIndex << INDEX_SHIFT) | shiftedThreshold : - INDEX_NEG_1 | shiftDistance(ABOVE_THRESHOLD); -} - -int32_t LocaleDistance::getDesSuppScriptDistance( - BytesTrie &iter, uint64_t startState, const char *desired, const char *supported) { - // Note: The data builder verifies that there are no <*, supported> or rules. - int32_t distance = trieNext(iter, desired, false); - if (distance >= 0) { - distance = trieNext(iter, supported, true); - } - if (distance < 0) { - UStringTrieResult result = iter.resetToState64(startState).next(u'*'); // <*, *> - U_ASSERT(USTRINGTRIE_HAS_VALUE(result)); - if (uprv_strcmp(desired, supported) == 0) { - distance = 0; // same script - } else { - distance = iter.getValue(); - U_ASSERT(distance >= 0); - } - if (result == USTRINGTRIE_FINAL_VALUE) { - distance |= DISTANCE_IS_FINAL; - } - } - return distance; -} - -int32_t LocaleDistance::getRegionPartitionsDistance( - BytesTrie &iter, uint64_t startState, - const char *desiredPartitions, const char *supportedPartitions, int32_t threshold) { - char desired = *desiredPartitions++; - char supported = *supportedPartitions++; - U_ASSERT(desired != 0 && supported != 0); - // See if we have single desired/supported partitions, from NUL-terminated - // partition strings without explicit length. - bool suppLengthGt1 = *supportedPartitions != 0; // gt1: more than 1 character - // equivalent to: if (desLength == 1 && suppLength == 1) - if (*desiredPartitions == 0 && !suppLengthGt1) { - // Fastpath for single desired/supported partitions. - UStringTrieResult result = iter.next(uprv_invCharToAscii(desired) | END_OF_SUBTAG); - if (USTRINGTRIE_HAS_NEXT(result)) { - result = iter.next(uprv_invCharToAscii(supported) | END_OF_SUBTAG); - if (USTRINGTRIE_HAS_VALUE(result)) { - return iter.getValue(); - } - } - return getFallbackRegionDistance(iter, startState); - } - - const char *supportedStart = supportedPartitions - 1; // for restart of inner loop - int32_t regionDistance = 0; - // Fall back to * only once, not for each pair of partition strings. - bool star = false; - for (;;) { - // Look up each desired-partition string only once, - // not for each (desired, supported) pair. - UStringTrieResult result = iter.next(uprv_invCharToAscii(desired) | END_OF_SUBTAG); - if (USTRINGTRIE_HAS_NEXT(result)) { - uint64_t desState = suppLengthGt1 ? iter.getState64() : 0; - for (;;) { - result = iter.next(uprv_invCharToAscii(supported) | END_OF_SUBTAG); - int32_t d; - if (USTRINGTRIE_HAS_VALUE(result)) { - d = iter.getValue(); - } else if (star) { - d = 0; - } else { - d = getFallbackRegionDistance(iter, startState); - star = true; - } - if (d > threshold) { - return d; - } else if (regionDistance < d) { - regionDistance = d; - } - if ((supported = *supportedPartitions++) != 0) { - iter.resetToState64(desState); - } else { - break; - } - } - } else if (!star) { - int32_t d = getFallbackRegionDistance(iter, startState); - if (d > threshold) { - return d; - } else if (regionDistance < d) { - regionDistance = d; - } - star = true; - } - if ((desired = *desiredPartitions++) != 0) { - iter.resetToState64(startState); - supportedPartitions = supportedStart; - supported = *supportedPartitions++; - } else { - break; - } - } - return regionDistance; -} - -int32_t LocaleDistance::getFallbackRegionDistance(BytesTrie &iter, uint64_t startState) { -#if U_DEBUG - UStringTrieResult result = -#endif - iter.resetToState64(startState).next(u'*'); // <*, *> - U_ASSERT(USTRINGTRIE_HAS_VALUE(result)); - int32_t distance = iter.getValue(); - U_ASSERT(distance >= 0); - return distance; -} - -int32_t LocaleDistance::trieNext(BytesTrie &iter, const char *s, bool wantValue) { - uint8_t c; - if ((c = *s) == 0) { - return -1; // no empty subtags in the distance data - } - for (;;) { - c = uprv_invCharToAscii(c); - // EBCDIC: If *s is not an invariant character, - // then c is now 0 and will simply not match anything, which is harmless. - uint8_t next = *++s; - if (next != 0) { - if (!USTRINGTRIE_HAS_NEXT(iter.next(c))) { - return -1; - } - } else { - // last character of this subtag - UStringTrieResult result = iter.next(c | END_OF_SUBTAG); - if (wantValue) { - if (USTRINGTRIE_HAS_VALUE(result)) { - int32_t value = iter.getValue(); - if (result == USTRINGTRIE_FINAL_VALUE) { - value |= DISTANCE_IS_FINAL; - } - return value; - } - } else { - if (USTRINGTRIE_HAS_NEXT(result)) { - return 0; - } - } - return -1; - } - c = next; - } -} - -UBool LocaleDistance::isParadigmLSR(const LSR &lsr) const { - // Linear search for a very short list (length 6 as of 2019), - // because we look for equivalence not equality, and - // because it's easy. - // If there are many paradigm LSRs we should use a hash set - // with custom comparator and hasher. - U_ASSERT(paradigmLSRsLength <= 15); - for (int32_t i = 0; i < paradigmLSRsLength; ++i) { - if (lsr.isEquivalentTo(paradigmLSRs[i])) { return true; } - } - return false; -} - -U_NAMESPACE_END +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// locdistance.cpp +// created: 2019may08 Markus W. Scherer + +#include "unicode/utypes.h" +#include "unicode/bytestrie.h" +#include "unicode/localematcher.h" +#include "unicode/locid.h" +#include "unicode/uobject.h" +#include "unicode/ures.h" +#include "cstring.h" +#include "locdistance.h" +#include "loclikelysubtags.h" +#include "uassert.h" +#include "ucln_cmn.h" +#include "uinvchar.h" +#include "umutex.h" + +U_NAMESPACE_BEGIN + +namespace { + +/** + * Bit flag used on the last character of a subtag in the trie. + * Must be set consistently by the builder and the lookup code. + */ +constexpr int32_t END_OF_SUBTAG = 0x80; +/** Distance value bit flag, set by the builder. */ +constexpr int32_t DISTANCE_SKIP_SCRIPT = 0x80; +/** Distance value bit flag, set by trieNext(). */ +constexpr int32_t DISTANCE_IS_FINAL = 0x100; +constexpr int32_t DISTANCE_IS_FINAL_OR_SKIP_SCRIPT = DISTANCE_IS_FINAL | DISTANCE_SKIP_SCRIPT; + +constexpr int32_t ABOVE_THRESHOLD = 100; + +// Indexes into array of distances. +enum { + IX_DEF_LANG_DISTANCE, + IX_DEF_SCRIPT_DISTANCE, + IX_DEF_REGION_DISTANCE, + IX_MIN_REGION_DISTANCE, + IX_LIMIT +}; + +LocaleDistance *gLocaleDistance = nullptr; +UInitOnce gInitOnce {}; + +UBool U_CALLCONV cleanup() { + delete gLocaleDistance; + gLocaleDistance = nullptr; + gInitOnce.reset(); + return true; +} + +} // namespace + +void U_CALLCONV LocaleDistance::initLocaleDistance(UErrorCode &errorCode) { + // This function is invoked only via umtx_initOnce(). + U_ASSERT(gLocaleDistance == nullptr); + const XLikelySubtags &likely = *XLikelySubtags::getSingleton(errorCode); + if (U_FAILURE(errorCode)) { return; } + const LocaleDistanceData &data = likely.getDistanceData(); + if (data.distanceTrieBytes == nullptr || + data.regionToPartitions == nullptr || data.partitions == nullptr || + // ok if no paradigms + data.distances == nullptr) { + errorCode = U_MISSING_RESOURCE_ERROR; + return; + } + gLocaleDistance = new LocaleDistance(data, likely); + if (gLocaleDistance == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + ucln_common_registerCleanup(UCLN_COMMON_LOCALE_DISTANCE, cleanup); +} + +const LocaleDistance *LocaleDistance::getSingleton(UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return nullptr; } + umtx_initOnce(gInitOnce, &LocaleDistance::initLocaleDistance, errorCode); + return gLocaleDistance; +} + +LocaleDistance::LocaleDistance(const LocaleDistanceData &data, const XLikelySubtags &likely) : + likelySubtags(likely), + trie(data.distanceTrieBytes), + regionToPartitionsIndex(data.regionToPartitions), partitionArrays(data.partitions), + paradigmLSRs(data.paradigms), paradigmLSRsLength(data.paradigmsLength), + defaultLanguageDistance(data.distances[IX_DEF_LANG_DISTANCE]), + defaultScriptDistance(data.distances[IX_DEF_SCRIPT_DISTANCE]), + defaultRegionDistance(data.distances[IX_DEF_REGION_DISTANCE]), + minRegionDistance(data.distances[IX_MIN_REGION_DISTANCE]) { + // For the default demotion value, use the + // default region distance between unrelated Englishes. + // Thus, unless demotion is turned off, + // a mere region difference for one desired locale + // is as good as a perfect match for the next following desired locale. + // As of CLDR 36, we have . + LSR en("en", "Latn", "US", LSR::EXPLICIT_LSR); + LSR enGB("en", "Latn", "GB", LSR::EXPLICIT_LSR); + const LSR *p_enGB = &enGB; + int32_t indexAndDistance = getBestIndexAndDistance(en, &p_enGB, 1, + shiftDistance(50), ULOCMATCH_FAVOR_LANGUAGE, ULOCMATCH_DIRECTION_WITH_ONE_WAY); + defaultDemotionPerDesiredLocale = getDistanceFloor(indexAndDistance); +} + +int32_t LocaleDistance::getBestIndexAndDistance( + const LSR &desired, + const LSR **supportedLSRs, int32_t supportedLSRsLength, + int32_t shiftedThreshold, + ULocMatchFavorSubtag favorSubtag, ULocMatchDirection direction) const { + BytesTrie iter(trie); + // Look up the desired language only once for all supported LSRs. + // Its "distance" is either a match point value of 0, or a non-match negative value. + // Note: The data builder verifies that there are no <*, supported> or rules. + int32_t desLangDistance = trieNext(iter, desired.language, false); + uint64_t desLangState = desLangDistance >= 0 && supportedLSRsLength > 1 ? iter.getState64() : 0; + // Index of the supported LSR with the lowest distance. + int32_t bestIndex = -1; + // Cached lookup info from XLikelySubtags.compareLikely(). + int32_t bestLikelyInfo = -1; + for (int32_t slIndex = 0; slIndex < supportedLSRsLength; ++slIndex) { + const LSR &supported = *supportedLSRs[slIndex]; + bool star = false; + int32_t distance = desLangDistance; + if (distance >= 0) { + U_ASSERT((distance & DISTANCE_IS_FINAL) == 0); + if (slIndex != 0) { + iter.resetToState64(desLangState); + } + distance = trieNext(iter, supported.language, true); + } + // Note: The data builder verifies that there are no rules with "any" (*) language and + // real (non *) script or region subtags. + // This means that if the lookup for either language fails we can use + // the default distances without further lookups. + int32_t flags; + if (distance >= 0) { + flags = distance & DISTANCE_IS_FINAL_OR_SKIP_SCRIPT; + distance &= ~DISTANCE_IS_FINAL_OR_SKIP_SCRIPT; + } else { // <*, *> + if (uprv_strcmp(desired.language, supported.language) == 0) { + distance = 0; + } else { + distance = defaultLanguageDistance; + } + flags = 0; + star = true; + } + U_ASSERT(0 <= distance && distance <= 100); + // Round up the shifted threshold (if fraction bits are not 0) + // for comparison with un-shifted distances until we need fraction bits. + // (If we simply shifted non-zero fraction bits away, then we might ignore a language + // when it's really still a micro distance below the threshold.) + int32_t roundedThreshold = (shiftedThreshold + DISTANCE_FRACTION_MASK) >> DISTANCE_SHIFT; + // We implement "favor subtag" by reducing the language subtag distance + // (unscientifically reducing it to a quarter of the normal value), + // so that the script distance is relatively more important. + // For example, given a default language distance of 80, we reduce it to 20, + // which is below the default threshold of 50, which is the default script distance. + if (favorSubtag == ULOCMATCH_FAVOR_SCRIPT) { + distance >>= 2; + } + // Let distance == roundedThreshold pass until the tie-breaker logic + // at the end of the loop. + if (distance > roundedThreshold) { + continue; + } + + int32_t scriptDistance; + if (star || flags != 0) { + if (uprv_strcmp(desired.script, supported.script) == 0) { + scriptDistance = 0; + } else { + scriptDistance = defaultScriptDistance; + } + } else { + scriptDistance = getDesSuppScriptDistance(iter, iter.getState64(), + desired.script, supported.script); + flags = scriptDistance & DISTANCE_IS_FINAL; + scriptDistance &= ~DISTANCE_IS_FINAL; + } + distance += scriptDistance; + if (distance > roundedThreshold) { + continue; + } + + if (uprv_strcmp(desired.region, supported.region) == 0) { + // regionDistance = 0 + } else if (star || (flags & DISTANCE_IS_FINAL) != 0) { + distance += defaultRegionDistance; + } else { + int32_t remainingThreshold = roundedThreshold - distance; + if (minRegionDistance > remainingThreshold) { + continue; + } + + // From here on we know the regions are not equal. + // Map each region to zero or more partitions. (zero = one non-matching string) + // (Each array of single-character partition strings is encoded as one string.) + // If either side has more than one, then we find the maximum distance. + // This could be optimized by adding some more structure, but probably not worth it. + distance += getRegionPartitionsDistance( + iter, iter.getState64(), + partitionsForRegion(desired), + partitionsForRegion(supported), + remainingThreshold); + } + int32_t shiftedDistance = shiftDistance(distance); + if (shiftedDistance == 0) { + // Distinguish between equivalent but originally unequal locales via an + // additional micro distance. + shiftedDistance |= (desired.flags ^ supported.flags); + if (shiftedDistance < shiftedThreshold) { + if (direction != ULOCMATCH_DIRECTION_ONLY_TWO_WAY || + // Is there also a match when we swap desired/supported? + isMatch(supported, desired, shiftedThreshold, favorSubtag)) { + if (shiftedDistance == 0) { + return slIndex << INDEX_SHIFT; + } + bestIndex = slIndex; + shiftedThreshold = shiftedDistance; + bestLikelyInfo = -1; + } + } + } else { + if (shiftedDistance < shiftedThreshold) { + if (direction != ULOCMATCH_DIRECTION_ONLY_TWO_WAY || + // Is there also a match when we swap desired/supported? + isMatch(supported, desired, shiftedThreshold, favorSubtag)) { + bestIndex = slIndex; + shiftedThreshold = shiftedDistance; + bestLikelyInfo = -1; + } + } else if (shiftedDistance == shiftedThreshold && bestIndex >= 0) { + if (direction != ULOCMATCH_DIRECTION_ONLY_TWO_WAY || + // Is there also a match when we swap desired/supported? + isMatch(supported, desired, shiftedThreshold, favorSubtag)) { + bestLikelyInfo = likelySubtags.compareLikely( + supported, *supportedLSRs[bestIndex], bestLikelyInfo); + if ((bestLikelyInfo & 1) != 0) { + // This supported locale matches as well as the previous best match, + // and neither matches perfectly, + // but this one is "more likely" (has more-default subtags). + bestIndex = slIndex; + } + } + } + } + } + return bestIndex >= 0 ? + (bestIndex << INDEX_SHIFT) | shiftedThreshold : + INDEX_NEG_1 | shiftDistance(ABOVE_THRESHOLD); +} + +int32_t LocaleDistance::getDesSuppScriptDistance( + BytesTrie &iter, uint64_t startState, const char *desired, const char *supported) { + // Note: The data builder verifies that there are no <*, supported> or rules. + int32_t distance = trieNext(iter, desired, false); + if (distance >= 0) { + distance = trieNext(iter, supported, true); + } + if (distance < 0) { + UStringTrieResult result = iter.resetToState64(startState).next(u'*'); // <*, *> + U_ASSERT(USTRINGTRIE_HAS_VALUE(result)); + if (uprv_strcmp(desired, supported) == 0) { + distance = 0; // same script + } else { + distance = iter.getValue(); + U_ASSERT(distance >= 0); + } + if (result == USTRINGTRIE_FINAL_VALUE) { + distance |= DISTANCE_IS_FINAL; + } + } + return distance; +} + +int32_t LocaleDistance::getRegionPartitionsDistance( + BytesTrie &iter, uint64_t startState, + const char *desiredPartitions, const char *supportedPartitions, int32_t threshold) { + char desired = *desiredPartitions++; + char supported = *supportedPartitions++; + U_ASSERT(desired != 0 && supported != 0); + // See if we have single desired/supported partitions, from NUL-terminated + // partition strings without explicit length. + bool suppLengthGt1 = *supportedPartitions != 0; // gt1: more than 1 character + // equivalent to: if (desLength == 1 && suppLength == 1) + if (*desiredPartitions == 0 && !suppLengthGt1) { + // Fastpath for single desired/supported partitions. + UStringTrieResult result = iter.next(uprv_invCharToAscii(desired) | END_OF_SUBTAG); + if (USTRINGTRIE_HAS_NEXT(result)) { + result = iter.next(uprv_invCharToAscii(supported) | END_OF_SUBTAG); + if (USTRINGTRIE_HAS_VALUE(result)) { + return iter.getValue(); + } + } + return getFallbackRegionDistance(iter, startState); + } + + const char *supportedStart = supportedPartitions - 1; // for restart of inner loop + int32_t regionDistance = 0; + // Fall back to * only once, not for each pair of partition strings. + bool star = false; + for (;;) { + // Look up each desired-partition string only once, + // not for each (desired, supported) pair. + UStringTrieResult result = iter.next(uprv_invCharToAscii(desired) | END_OF_SUBTAG); + if (USTRINGTRIE_HAS_NEXT(result)) { + uint64_t desState = suppLengthGt1 ? iter.getState64() : 0; + for (;;) { + result = iter.next(uprv_invCharToAscii(supported) | END_OF_SUBTAG); + int32_t d; + if (USTRINGTRIE_HAS_VALUE(result)) { + d = iter.getValue(); + } else if (star) { + d = 0; + } else { + d = getFallbackRegionDistance(iter, startState); + star = true; + } + if (d > threshold) { + return d; + } else if (regionDistance < d) { + regionDistance = d; + } + if ((supported = *supportedPartitions++) != 0) { + iter.resetToState64(desState); + } else { + break; + } + } + } else if (!star) { + int32_t d = getFallbackRegionDistance(iter, startState); + if (d > threshold) { + return d; + } else if (regionDistance < d) { + regionDistance = d; + } + star = true; + } + if ((desired = *desiredPartitions++) != 0) { + iter.resetToState64(startState); + supportedPartitions = supportedStart; + supported = *supportedPartitions++; + } else { + break; + } + } + return regionDistance; +} + +int32_t LocaleDistance::getFallbackRegionDistance(BytesTrie &iter, uint64_t startState) { +#if U_DEBUG + UStringTrieResult result = +#endif + iter.resetToState64(startState).next(u'*'); // <*, *> + U_ASSERT(USTRINGTRIE_HAS_VALUE(result)); + int32_t distance = iter.getValue(); + U_ASSERT(distance >= 0); + return distance; +} + +int32_t LocaleDistance::trieNext(BytesTrie &iter, const char *s, bool wantValue) { + uint8_t c; + if ((c = *s) == 0) { + return -1; // no empty subtags in the distance data + } + for (;;) { + c = uprv_invCharToAscii(c); + // EBCDIC: If *s is not an invariant character, + // then c is now 0 and will simply not match anything, which is harmless. + uint8_t next = *++s; + if (next != 0) { + if (!USTRINGTRIE_HAS_NEXT(iter.next(c))) { + return -1; + } + } else { + // last character of this subtag + UStringTrieResult result = iter.next(c | END_OF_SUBTAG); + if (wantValue) { + if (USTRINGTRIE_HAS_VALUE(result)) { + int32_t value = iter.getValue(); + if (result == USTRINGTRIE_FINAL_VALUE) { + value |= DISTANCE_IS_FINAL; + } + return value; + } + } else { + if (USTRINGTRIE_HAS_NEXT(result)) { + return 0; + } + } + return -1; + } + c = next; + } +} + +UBool LocaleDistance::isParadigmLSR(const LSR &lsr) const { + // Linear search for a very short list (length 6 as of 2019), + // because we look for equivalence not equality, and + // because it's easy. + // If there are many paradigm LSRs we should use a hash set + // with custom comparator and hasher. + U_ASSERT(paradigmLSRsLength <= 15); + for (int32_t i = 0; i < paradigmLSRsLength; ++i) { + if (lsr.isEquivalentTo(paradigmLSRs[i])) { return true; } + } + return false; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/locdistance.h b/deps/icu-small/source/common/locdistance.h index 51b777e6272312..20009d0860ac49 100644 --- a/deps/icu-small/source/common/locdistance.h +++ b/deps/icu-small/source/common/locdistance.h @@ -1,151 +1,151 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// locdistance.h -// created: 2019may08 Markus W. Scherer - -#ifndef __LOCDISTANCE_H__ -#define __LOCDISTANCE_H__ - -#include "unicode/utypes.h" -#include "unicode/bytestrie.h" -#include "unicode/localematcher.h" -#include "unicode/locid.h" -#include "unicode/uobject.h" -#include "lsr.h" - -U_NAMESPACE_BEGIN - -struct LocaleDistanceData; - -/** - * Offline-built data for LocaleMatcher. - * Mostly but not only the data for mapping locales to their maximized forms. - */ -class LocaleDistance final : public UMemory { -public: - static const LocaleDistance *getSingleton(UErrorCode &errorCode); - - static int32_t shiftDistance(int32_t distance) { - return distance << DISTANCE_SHIFT; - } - - static int32_t getShiftedDistance(int32_t indexAndDistance) { - return indexAndDistance & DISTANCE_MASK; - } - - static double getDistanceDouble(int32_t indexAndDistance) { - double shiftedDistance = getShiftedDistance(indexAndDistance); - return shiftedDistance / (1 << DISTANCE_SHIFT); - } - - static int32_t getDistanceFloor(int32_t indexAndDistance) { - return (indexAndDistance & DISTANCE_MASK) >> DISTANCE_SHIFT; - } - - static int32_t getIndex(int32_t indexAndDistance) { - // assert indexAndDistance >= 0; - return indexAndDistance >> INDEX_SHIFT; - } - - /** - * Finds the supported LSR with the smallest distance from the desired one. - * Equivalent LSR subtags must be normalized into a canonical form. - * - *

Returns the index of the lowest-distance supported LSR in the high bits - * (negative if none has a distance below the threshold), - * and its distance (0..ABOVE_THRESHOLD) in the low bits. - */ - int32_t getBestIndexAndDistance(const LSR &desired, - const LSR **supportedLSRs, int32_t supportedLSRsLength, - int32_t shiftedThreshold, - ULocMatchFavorSubtag favorSubtag, - ULocMatchDirection direction) const; - - UBool isParadigmLSR(const LSR &lsr) const; - - int32_t getDefaultScriptDistance() const { - return defaultScriptDistance; - } - - int32_t getDefaultDemotionPerDesiredLocale() const { - return defaultDemotionPerDesiredLocale; - } - -private: - // The distance is shifted left to gain some fraction bits. - static constexpr int32_t DISTANCE_SHIFT = 3; - static constexpr int32_t DISTANCE_FRACTION_MASK = 7; - // 7 bits for 0..100 - static constexpr int32_t DISTANCE_INT_SHIFT = 7; - static constexpr int32_t INDEX_SHIFT = DISTANCE_INT_SHIFT + DISTANCE_SHIFT; - static constexpr int32_t DISTANCE_MASK = 0x3ff; - // tic constexpr int32_t MAX_INDEX = 0x1fffff; // avoids sign bit - static constexpr int32_t INDEX_NEG_1 = 0xfffffc00; - - LocaleDistance(const LocaleDistanceData &data, const XLikelySubtags &likely); - LocaleDistance(const LocaleDistance &other) = delete; - LocaleDistance &operator=(const LocaleDistance &other) = delete; - - static void initLocaleDistance(UErrorCode &errorCode); - - UBool isMatch(const LSR &desired, const LSR &supported, - int32_t shiftedThreshold, ULocMatchFavorSubtag favorSubtag) const { - const LSR *pSupp = &supported; - return getBestIndexAndDistance( - desired, &pSupp, 1, - shiftedThreshold, favorSubtag, ULOCMATCH_DIRECTION_WITH_ONE_WAY) >= 0; - } - - static int32_t getDesSuppScriptDistance(BytesTrie &iter, uint64_t startState, - const char *desired, const char *supported); - - static int32_t getRegionPartitionsDistance( - BytesTrie &iter, uint64_t startState, - const char *desiredPartitions, const char *supportedPartitions, - int32_t threshold); - - static int32_t getFallbackRegionDistance(BytesTrie &iter, uint64_t startState); - - static int32_t trieNext(BytesTrie &iter, const char *s, bool wantValue); - - const char *partitionsForRegion(const LSR &lsr) const { - // ill-formed region -> one non-matching string - int32_t pIndex = regionToPartitionsIndex[lsr.regionIndex]; - return partitionArrays[pIndex]; - } - - int32_t getDefaultRegionDistance() const { - return defaultRegionDistance; - } - - const XLikelySubtags &likelySubtags; - - // The trie maps each dlang+slang+dscript+sscript+dregion+sregion - // (encoded in ASCII with bit 7 set on the last character of each subtag) to a distance. - // There is also a trie value for each subsequence of whole subtags. - // One '*' is used for a (desired, supported) pair of "und", "Zzzz"/"", or "ZZ"/"". - BytesTrie trie; - - /** - * Maps each region to zero or more single-character partitions. - */ - const uint8_t *regionToPartitionsIndex; - const char **partitionArrays; - - /** - * Used to get the paradigm region for a cluster, if there is one. - */ - const LSR *paradigmLSRs; - int32_t paradigmLSRsLength; - - int32_t defaultLanguageDistance; - int32_t defaultScriptDistance; - int32_t defaultRegionDistance; - int32_t minRegionDistance; - int32_t defaultDemotionPerDesiredLocale; -}; - -U_NAMESPACE_END - -#endif // __LOCDISTANCE_H__ +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// locdistance.h +// created: 2019may08 Markus W. Scherer + +#ifndef __LOCDISTANCE_H__ +#define __LOCDISTANCE_H__ + +#include "unicode/utypes.h" +#include "unicode/bytestrie.h" +#include "unicode/localematcher.h" +#include "unicode/locid.h" +#include "unicode/uobject.h" +#include "lsr.h" + +U_NAMESPACE_BEGIN + +struct LocaleDistanceData; + +/** + * Offline-built data for LocaleMatcher. + * Mostly but not only the data for mapping locales to their maximized forms. + */ +class LocaleDistance final : public UMemory { +public: + static const LocaleDistance *getSingleton(UErrorCode &errorCode); + + static int32_t shiftDistance(int32_t distance) { + return distance << DISTANCE_SHIFT; + } + + static int32_t getShiftedDistance(int32_t indexAndDistance) { + return indexAndDistance & DISTANCE_MASK; + } + + static double getDistanceDouble(int32_t indexAndDistance) { + double shiftedDistance = getShiftedDistance(indexAndDistance); + return shiftedDistance / (1 << DISTANCE_SHIFT); + } + + static int32_t getDistanceFloor(int32_t indexAndDistance) { + return (indexAndDistance & DISTANCE_MASK) >> DISTANCE_SHIFT; + } + + static int32_t getIndex(int32_t indexAndDistance) { + // assert indexAndDistance >= 0; + return indexAndDistance >> INDEX_SHIFT; + } + + /** + * Finds the supported LSR with the smallest distance from the desired one. + * Equivalent LSR subtags must be normalized into a canonical form. + * + *

Returns the index of the lowest-distance supported LSR in the high bits + * (negative if none has a distance below the threshold), + * and its distance (0..ABOVE_THRESHOLD) in the low bits. + */ + int32_t getBestIndexAndDistance(const LSR &desired, + const LSR **supportedLSRs, int32_t supportedLSRsLength, + int32_t shiftedThreshold, + ULocMatchFavorSubtag favorSubtag, + ULocMatchDirection direction) const; + + UBool isParadigmLSR(const LSR &lsr) const; + + int32_t getDefaultScriptDistance() const { + return defaultScriptDistance; + } + + int32_t getDefaultDemotionPerDesiredLocale() const { + return defaultDemotionPerDesiredLocale; + } + +private: + // The distance is shifted left to gain some fraction bits. + static constexpr int32_t DISTANCE_SHIFT = 3; + static constexpr int32_t DISTANCE_FRACTION_MASK = 7; + // 7 bits for 0..100 + static constexpr int32_t DISTANCE_INT_SHIFT = 7; + static constexpr int32_t INDEX_SHIFT = DISTANCE_INT_SHIFT + DISTANCE_SHIFT; + static constexpr int32_t DISTANCE_MASK = 0x3ff; + // tic constexpr int32_t MAX_INDEX = 0x1fffff; // avoids sign bit + static constexpr int32_t INDEX_NEG_1 = 0xfffffc00; + + LocaleDistance(const LocaleDistanceData &data, const XLikelySubtags &likely); + LocaleDistance(const LocaleDistance &other) = delete; + LocaleDistance &operator=(const LocaleDistance &other) = delete; + + static void initLocaleDistance(UErrorCode &errorCode); + + UBool isMatch(const LSR &desired, const LSR &supported, + int32_t shiftedThreshold, ULocMatchFavorSubtag favorSubtag) const { + const LSR *pSupp = &supported; + return getBestIndexAndDistance( + desired, &pSupp, 1, + shiftedThreshold, favorSubtag, ULOCMATCH_DIRECTION_WITH_ONE_WAY) >= 0; + } + + static int32_t getDesSuppScriptDistance(BytesTrie &iter, uint64_t startState, + const char *desired, const char *supported); + + static int32_t getRegionPartitionsDistance( + BytesTrie &iter, uint64_t startState, + const char *desiredPartitions, const char *supportedPartitions, + int32_t threshold); + + static int32_t getFallbackRegionDistance(BytesTrie &iter, uint64_t startState); + + static int32_t trieNext(BytesTrie &iter, const char *s, bool wantValue); + + const char *partitionsForRegion(const LSR &lsr) const { + // ill-formed region -> one non-matching string + int32_t pIndex = regionToPartitionsIndex[lsr.regionIndex]; + return partitionArrays[pIndex]; + } + + int32_t getDefaultRegionDistance() const { + return defaultRegionDistance; + } + + const XLikelySubtags &likelySubtags; + + // The trie maps each dlang+slang+dscript+sscript+dregion+sregion + // (encoded in ASCII with bit 7 set on the last character of each subtag) to a distance. + // There is also a trie value for each subsequence of whole subtags. + // One '*' is used for a (desired, supported) pair of "und", "Zzzz"/"", or "ZZ"/"". + BytesTrie trie; + + /** + * Maps each region to zero or more single-character partitions. + */ + const uint8_t *regionToPartitionsIndex; + const char **partitionArrays; + + /** + * Used to get the paradigm region for a cluster, if there is one. + */ + const LSR *paradigmLSRs; + int32_t paradigmLSRsLength; + + int32_t defaultLanguageDistance; + int32_t defaultScriptDistance; + int32_t defaultRegionDistance; + int32_t minRegionDistance; + int32_t defaultDemotionPerDesiredLocale; +}; + +U_NAMESPACE_END + +#endif // __LOCDISTANCE_H__ diff --git a/deps/icu-small/source/common/locdspnm.cpp b/deps/icu-small/source/common/locdspnm.cpp index 401f1fecbff1c0..abf411e5ab1c2f 100644 --- a/deps/icu-small/source/common/locdspnm.cpp +++ b/deps/icu-small/source/common/locdspnm.cpp @@ -1,1136 +1,1136 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/locdspnm.h" -#include "unicode/simpleformatter.h" -#include "unicode/ucasemap.h" -#include "unicode/ures.h" -#include "unicode/udisplaycontext.h" -#include "unicode/brkiter.h" -#include "unicode/ucurr.h" -#include "cmemory.h" -#include "cstring.h" -#include "mutex.h" -#include "ulocimp.h" -#include "umutex.h" -#include "ureslocs.h" -#include "uresimp.h" - -#include - -/** - * Concatenate a number of null-terminated strings to buffer, leaving a - * null-terminated string. The last argument should be the null pointer. - * Return the length of the string in the buffer, not counting the trailing - * null. Return -1 if there is an error (buffer is null, or buflen < 1). - */ -static int32_t ncat(char *buffer, uint32_t buflen, ...) { - va_list args; - char *str; - char *p = buffer; - const char* e = buffer + buflen - 1; - - if (buffer == NULL || buflen < 1) { - return -1; - } - - va_start(args, buflen); - while ((str = va_arg(args, char *)) != 0) { - char c; - while (p != e && (c = *str++) != 0) { - *p++ = c; - } - } - *p = 0; - va_end(args); - - return static_cast(p - buffer); -} - -U_NAMESPACE_BEGIN - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -// Access resource data for locale components. -// Wrap code in uloc.c for now. -class ICUDataTable { - const char* path; - Locale locale; - -public: - ICUDataTable(const char* path, const Locale& locale); - ~ICUDataTable(); - - const Locale& getLocale(); - - UnicodeString& get(const char* tableKey, const char* itemKey, - UnicodeString& result) const; - UnicodeString& get(const char* tableKey, const char* subTableKey, const char* itemKey, - UnicodeString& result) const; - - UnicodeString& getNoFallback(const char* tableKey, const char* itemKey, - UnicodeString &result) const; - UnicodeString& getNoFallback(const char* tableKey, const char* subTableKey, const char* itemKey, - UnicodeString &result) const; -}; - -inline UnicodeString & -ICUDataTable::get(const char* tableKey, const char* itemKey, UnicodeString& result) const { - return get(tableKey, NULL, itemKey, result); -} - -inline UnicodeString & -ICUDataTable::getNoFallback(const char* tableKey, const char* itemKey, UnicodeString& result) const { - return getNoFallback(tableKey, NULL, itemKey, result); -} - -ICUDataTable::ICUDataTable(const char* path, const Locale& locale) - : path(NULL), locale(Locale::getRoot()) -{ - if (path) { - int32_t len = static_cast(uprv_strlen(path)); - this->path = (const char*) uprv_malloc(len + 1); - if (this->path) { - uprv_strcpy((char *)this->path, path); - this->locale = locale; - } - } -} - -ICUDataTable::~ICUDataTable() { - if (path) { - uprv_free((void*) path); - path = NULL; - } -} - -const Locale& -ICUDataTable::getLocale() { - return locale; -} - -UnicodeString & -ICUDataTable::get(const char* tableKey, const char* subTableKey, const char* itemKey, - UnicodeString &result) const { - UErrorCode status = U_ZERO_ERROR; - int32_t len = 0; - - const UChar *s = uloc_getTableStringWithFallback(path, locale.getName(), - tableKey, subTableKey, itemKey, - &len, &status); - if (U_SUCCESS(status) && len > 0) { - return result.setTo(s, len); - } - return result.setTo(UnicodeString(itemKey, -1, US_INV)); -} - -UnicodeString & -ICUDataTable::getNoFallback(const char* tableKey, const char* subTableKey, const char* itemKey, - UnicodeString& result) const { - UErrorCode status = U_ZERO_ERROR; - int32_t len = 0; - - const UChar *s = uloc_getTableStringWithFallback(path, locale.getName(), - tableKey, subTableKey, itemKey, - &len, &status); - if (U_SUCCESS(status)) { - return result.setTo(s, len); - } - - result.setToBogus(); - return result; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -LocaleDisplayNames::~LocaleDisplayNames() {} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -#if 0 // currently unused - -class DefaultLocaleDisplayNames : public LocaleDisplayNames { - UDialectHandling dialectHandling; - -public: - // constructor - DefaultLocaleDisplayNames(UDialectHandling dialectHandling); - - virtual ~DefaultLocaleDisplayNames(); - - virtual const Locale& getLocale() const; - virtual UDialectHandling getDialectHandling() const; - - virtual UnicodeString& localeDisplayName(const Locale& locale, - UnicodeString& result) const; - virtual UnicodeString& localeDisplayName(const char* localeId, - UnicodeString& result) const; - virtual UnicodeString& languageDisplayName(const char* lang, - UnicodeString& result) const; - virtual UnicodeString& scriptDisplayName(const char* script, - UnicodeString& result) const; - virtual UnicodeString& scriptDisplayName(UScriptCode scriptCode, - UnicodeString& result) const; - virtual UnicodeString& regionDisplayName(const char* region, - UnicodeString& result) const; - virtual UnicodeString& variantDisplayName(const char* variant, - UnicodeString& result) const; - virtual UnicodeString& keyDisplayName(const char* key, - UnicodeString& result) const; - virtual UnicodeString& keyValueDisplayName(const char* key, - const char* value, - UnicodeString& result) const; -}; - -DefaultLocaleDisplayNames::DefaultLocaleDisplayNames(UDialectHandling dialectHandling) - : dialectHandling(dialectHandling) { -} - -DefaultLocaleDisplayNames::~DefaultLocaleDisplayNames() { -} - -const Locale& -DefaultLocaleDisplayNames::getLocale() const { - return Locale::getRoot(); -} - -UDialectHandling -DefaultLocaleDisplayNames::getDialectHandling() const { - return dialectHandling; -} - -UnicodeString& -DefaultLocaleDisplayNames::localeDisplayName(const Locale& locale, - UnicodeString& result) const { - return result = UnicodeString(locale.getName(), -1, US_INV); -} - -UnicodeString& -DefaultLocaleDisplayNames::localeDisplayName(const char* localeId, - UnicodeString& result) const { - return result = UnicodeString(localeId, -1, US_INV); -} - -UnicodeString& -DefaultLocaleDisplayNames::languageDisplayName(const char* lang, - UnicodeString& result) const { - return result = UnicodeString(lang, -1, US_INV); -} - -UnicodeString& -DefaultLocaleDisplayNames::scriptDisplayName(const char* script, - UnicodeString& result) const { - return result = UnicodeString(script, -1, US_INV); -} - -UnicodeString& -DefaultLocaleDisplayNames::scriptDisplayName(UScriptCode scriptCode, - UnicodeString& result) const { - const char* name = uscript_getName(scriptCode); - if (name) { - return result = UnicodeString(name, -1, US_INV); - } - return result.remove(); -} - -UnicodeString& -DefaultLocaleDisplayNames::regionDisplayName(const char* region, - UnicodeString& result) const { - return result = UnicodeString(region, -1, US_INV); -} - -UnicodeString& -DefaultLocaleDisplayNames::variantDisplayName(const char* variant, - UnicodeString& result) const { - return result = UnicodeString(variant, -1, US_INV); -} - -UnicodeString& -DefaultLocaleDisplayNames::keyDisplayName(const char* key, - UnicodeString& result) const { - return result = UnicodeString(key, -1, US_INV); -} - -UnicodeString& -DefaultLocaleDisplayNames::keyValueDisplayName(const char* /* key */, - const char* value, - UnicodeString& result) const { - return result = UnicodeString(value, -1, US_INV); -} - -#endif // currently unused class DefaultLocaleDisplayNames - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -class LocaleDisplayNamesImpl : public LocaleDisplayNames { - Locale locale; - UDialectHandling dialectHandling; - ICUDataTable langData; - ICUDataTable regionData; - SimpleFormatter separatorFormat; - SimpleFormatter format; - SimpleFormatter keyTypeFormat; - UDisplayContext capitalizationContext; -#if !UCONFIG_NO_BREAK_ITERATION - BreakIterator* capitalizationBrkIter; -#else - UObject* capitalizationBrkIter; -#endif - UnicodeString formatOpenParen; - UnicodeString formatReplaceOpenParen; - UnicodeString formatCloseParen; - UnicodeString formatReplaceCloseParen; - UDisplayContext nameLength; - UDisplayContext substitute; - - // Constants for capitalization context usage types. - enum CapContextUsage { - kCapContextUsageLanguage, - kCapContextUsageScript, - kCapContextUsageTerritory, - kCapContextUsageVariant, - kCapContextUsageKey, - kCapContextUsageKeyValue, - kCapContextUsageCount - }; - // Capitalization transforms. For each usage type, indicates whether to titlecase for - // the context specified in capitalizationContext (which we know at construction time) - UBool fCapitalization[kCapContextUsageCount]; - -public: - // constructor - LocaleDisplayNamesImpl(const Locale& locale, UDialectHandling dialectHandling); - LocaleDisplayNamesImpl(const Locale& locale, UDisplayContext *contexts, int32_t length); - virtual ~LocaleDisplayNamesImpl(); - - virtual const Locale& getLocale() const override; - virtual UDialectHandling getDialectHandling() const override; - virtual UDisplayContext getContext(UDisplayContextType type) const override; - - virtual UnicodeString& localeDisplayName(const Locale& locale, - UnicodeString& result) const override; - virtual UnicodeString& localeDisplayName(const char* localeId, - UnicodeString& result) const override; - virtual UnicodeString& languageDisplayName(const char* lang, - UnicodeString& result) const override; - virtual UnicodeString& scriptDisplayName(const char* script, - UnicodeString& result) const override; - virtual UnicodeString& scriptDisplayName(UScriptCode scriptCode, - UnicodeString& result) const override; - virtual UnicodeString& regionDisplayName(const char* region, - UnicodeString& result) const override; - virtual UnicodeString& variantDisplayName(const char* variant, - UnicodeString& result) const override; - virtual UnicodeString& keyDisplayName(const char* key, - UnicodeString& result) const override; - virtual UnicodeString& keyValueDisplayName(const char* key, - const char* value, - UnicodeString& result) const override; -private: - UnicodeString& localeIdName(const char* localeId, - UnicodeString& result, bool substitute) const; - UnicodeString& appendWithSep(UnicodeString& buffer, const UnicodeString& src) const; - UnicodeString& adjustForUsageAndContext(CapContextUsage usage, UnicodeString& result) const; - UnicodeString& scriptDisplayName(const char* script, UnicodeString& result, UBool skipAdjust) const; - UnicodeString& regionDisplayName(const char* region, UnicodeString& result, UBool skipAdjust) const; - UnicodeString& variantDisplayName(const char* variant, UnicodeString& result, UBool skipAdjust) const; - UnicodeString& keyDisplayName(const char* key, UnicodeString& result, UBool skipAdjust) const; - UnicodeString& keyValueDisplayName(const char* key, const char* value, - UnicodeString& result, UBool skipAdjust) const; - void initialize(void); - - struct CapitalizationContextSink; -}; - -LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale, - UDialectHandling dialectHandling) - : dialectHandling(dialectHandling) - , langData(U_ICUDATA_LANG, locale) - , regionData(U_ICUDATA_REGION, locale) - , capitalizationContext(UDISPCTX_CAPITALIZATION_NONE) - , capitalizationBrkIter(NULL) - , nameLength(UDISPCTX_LENGTH_FULL) - , substitute(UDISPCTX_SUBSTITUTE) -{ - initialize(); -} - -LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale, - UDisplayContext *contexts, int32_t length) - : dialectHandling(ULDN_STANDARD_NAMES) - , langData(U_ICUDATA_LANG, locale) - , regionData(U_ICUDATA_REGION, locale) - , capitalizationContext(UDISPCTX_CAPITALIZATION_NONE) - , capitalizationBrkIter(NULL) - , nameLength(UDISPCTX_LENGTH_FULL) - , substitute(UDISPCTX_SUBSTITUTE) -{ - while (length-- > 0) { - UDisplayContext value = *contexts++; - UDisplayContextType selector = (UDisplayContextType)((uint32_t)value >> 8); - switch (selector) { - case UDISPCTX_TYPE_DIALECT_HANDLING: - dialectHandling = (UDialectHandling)value; - break; - case UDISPCTX_TYPE_CAPITALIZATION: - capitalizationContext = value; - break; - case UDISPCTX_TYPE_DISPLAY_LENGTH: - nameLength = value; - break; - case UDISPCTX_TYPE_SUBSTITUTE_HANDLING: - substitute = value; - break; - default: - break; - } - } - initialize(); -} - -struct LocaleDisplayNamesImpl::CapitalizationContextSink : public ResourceSink { - UBool hasCapitalizationUsage; - LocaleDisplayNamesImpl& parent; - - CapitalizationContextSink(LocaleDisplayNamesImpl& _parent) - : hasCapitalizationUsage(false), parent(_parent) {} - virtual ~CapitalizationContextSink(); - - virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, - UErrorCode &errorCode) override { - ResourceTable contexts = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int i = 0; contexts.getKeyAndValue(i, key, value); ++i) { - - CapContextUsage usageEnum; - if (uprv_strcmp(key, "key") == 0) { - usageEnum = kCapContextUsageKey; - } else if (uprv_strcmp(key, "keyValue") == 0) { - usageEnum = kCapContextUsageKeyValue; - } else if (uprv_strcmp(key, "languages") == 0) { - usageEnum = kCapContextUsageLanguage; - } else if (uprv_strcmp(key, "script") == 0) { - usageEnum = kCapContextUsageScript; - } else if (uprv_strcmp(key, "territory") == 0) { - usageEnum = kCapContextUsageTerritory; - } else if (uprv_strcmp(key, "variant") == 0) { - usageEnum = kCapContextUsageVariant; - } else { - continue; - } - - int32_t len = 0; - const int32_t* intVector = value.getIntVector(len, errorCode); - if (U_FAILURE(errorCode)) { return; } - if (len < 2) { continue; } - - int32_t titlecaseInt = (parent.capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU) ? intVector[0] : intVector[1]; - if (titlecaseInt == 0) { continue; } - - parent.fCapitalization[usageEnum] = true; - hasCapitalizationUsage = true; - } - } -}; - -// Virtual destructors must be defined out of line. -LocaleDisplayNamesImpl::CapitalizationContextSink::~CapitalizationContextSink() {} - -void -LocaleDisplayNamesImpl::initialize(void) { - LocaleDisplayNamesImpl *nonConstThis = (LocaleDisplayNamesImpl *)this; - nonConstThis->locale = langData.getLocale() == Locale::getRoot() - ? regionData.getLocale() - : langData.getLocale(); - - UnicodeString sep; - langData.getNoFallback("localeDisplayPattern", "separator", sep); - if (sep.isBogus()) { - sep = UnicodeString("{0}, {1}", -1, US_INV); - } - UErrorCode status = U_ZERO_ERROR; - separatorFormat.applyPatternMinMaxArguments(sep, 2, 2, status); - - UnicodeString pattern; - langData.getNoFallback("localeDisplayPattern", "pattern", pattern); - if (pattern.isBogus()) { - pattern = UnicodeString("{0} ({1})", -1, US_INV); - } - format.applyPatternMinMaxArguments(pattern, 2, 2, status); - if (pattern.indexOf((UChar)0xFF08) >= 0) { - formatOpenParen.setTo((UChar)0xFF08); // fullwidth ( - formatReplaceOpenParen.setTo((UChar)0xFF3B); // fullwidth [ - formatCloseParen.setTo((UChar)0xFF09); // fullwidth ) - formatReplaceCloseParen.setTo((UChar)0xFF3D); // fullwidth ] - } else { - formatOpenParen.setTo((UChar)0x0028); // ( - formatReplaceOpenParen.setTo((UChar)0x005B); // [ - formatCloseParen.setTo((UChar)0x0029); // ) - formatReplaceCloseParen.setTo((UChar)0x005D); // ] - } - - UnicodeString ktPattern; - langData.get("localeDisplayPattern", "keyTypePattern", ktPattern); - if (ktPattern.isBogus()) { - ktPattern = UnicodeString("{0}={1}", -1, US_INV); - } - keyTypeFormat.applyPatternMinMaxArguments(ktPattern, 2, 2, status); - - uprv_memset(fCapitalization, 0, sizeof(fCapitalization)); -#if !UCONFIG_NO_BREAK_ITERATION - // Only get the context data if we need it! This is a const object so we know now... - // Also check whether we will need a break iterator (depends on the data) - UBool needBrkIter = false; - if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_STANDALONE) { - LocalUResourceBundlePointer resource(ures_open(NULL, locale.getName(), &status)); - if (U_FAILURE(status)) { return; } - CapitalizationContextSink sink(*this); - ures_getAllItemsWithFallback(resource.getAlias(), "contextTransforms", sink, status); - if (status == U_MISSING_RESOURCE_ERROR) { - // Silently ignore. Not every locale has contextTransforms. - status = U_ZERO_ERROR; - } else if (U_FAILURE(status)) { - return; - } - needBrkIter = sink.hasCapitalizationUsage; - } - // Get a sentence break iterator if we will need it - if (needBrkIter || capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) { - status = U_ZERO_ERROR; - capitalizationBrkIter = BreakIterator::createSentenceInstance(locale, status); - if (U_FAILURE(status)) { - delete capitalizationBrkIter; - capitalizationBrkIter = NULL; - } - } -#endif -} - -LocaleDisplayNamesImpl::~LocaleDisplayNamesImpl() { -#if !UCONFIG_NO_BREAK_ITERATION - delete capitalizationBrkIter; -#endif -} - -const Locale& -LocaleDisplayNamesImpl::getLocale() const { - return locale; -} - -UDialectHandling -LocaleDisplayNamesImpl::getDialectHandling() const { - return dialectHandling; -} - -UDisplayContext -LocaleDisplayNamesImpl::getContext(UDisplayContextType type) const { - switch (type) { - case UDISPCTX_TYPE_DIALECT_HANDLING: - return (UDisplayContext)dialectHandling; - case UDISPCTX_TYPE_CAPITALIZATION: - return capitalizationContext; - case UDISPCTX_TYPE_DISPLAY_LENGTH: - return nameLength; - case UDISPCTX_TYPE_SUBSTITUTE_HANDLING: - return substitute; - default: - break; - } - return (UDisplayContext)0; -} - -UnicodeString& -LocaleDisplayNamesImpl::adjustForUsageAndContext(CapContextUsage usage, - UnicodeString& result) const { -#if !UCONFIG_NO_BREAK_ITERATION - // check to see whether we need to titlecase result - if ( result.length() > 0 && u_islower(result.char32At(0)) && capitalizationBrkIter!= NULL && - ( capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || fCapitalization[usage] ) ) { - // note fCapitalization[usage] won't be set unless capitalizationContext is UI_LIST_OR_MENU or STANDALONE - static UMutex capitalizationBrkIterLock; - Mutex lock(&capitalizationBrkIterLock); - result.toTitle(capitalizationBrkIter, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); - } -#endif - return result; -} - -UnicodeString& -LocaleDisplayNamesImpl::localeDisplayName(const Locale& loc, - UnicodeString& result) const { - if (loc.isBogus()) { - result.setToBogus(); - return result; - } - UnicodeString resultName; - - const char* lang = loc.getLanguage(); - if (uprv_strlen(lang) == 0) { - lang = "root"; - } - const char* script = loc.getScript(); - const char* country = loc.getCountry(); - const char* variant = loc.getVariant(); - - UBool hasScript = uprv_strlen(script) > 0; - UBool hasCountry = uprv_strlen(country) > 0; - UBool hasVariant = uprv_strlen(variant) > 0; - - if (dialectHandling == ULDN_DIALECT_NAMES) { - char buffer[ULOC_FULLNAME_CAPACITY]; - do { // loop construct is so we can break early out of search - if (hasScript && hasCountry) { - ncat(buffer, ULOC_FULLNAME_CAPACITY, lang, "_", script, "_", country, (char *)0); - localeIdName(buffer, resultName, false); - if (!resultName.isBogus()) { - hasScript = false; - hasCountry = false; - break; - } - } - if (hasScript) { - ncat(buffer, ULOC_FULLNAME_CAPACITY, lang, "_", script, (char *)0); - localeIdName(buffer, resultName, false); - if (!resultName.isBogus()) { - hasScript = false; - break; - } - } - if (hasCountry) { - ncat(buffer, ULOC_FULLNAME_CAPACITY, lang, "_", country, (char*)0); - localeIdName(buffer, resultName, false); - if (!resultName.isBogus()) { - hasCountry = false; - break; - } - } - } while (false); - } - if (resultName.isBogus() || resultName.isEmpty()) { - localeIdName(lang, resultName, substitute == UDISPCTX_SUBSTITUTE); - if (resultName.isBogus()) { - result.setToBogus(); - return result; - } - } - - UnicodeString resultRemainder; - UnicodeString temp; - UErrorCode status = U_ZERO_ERROR; - - if (hasScript) { - UnicodeString script_str = scriptDisplayName(script, temp, true); - if (script_str.isBogus()) { - result.setToBogus(); - return result; - } - resultRemainder.append(script_str); - } - if (hasCountry) { - UnicodeString region_str = regionDisplayName(country, temp, true); - if (region_str.isBogus()) { - result.setToBogus(); - return result; - } - appendWithSep(resultRemainder, region_str); - } - if (hasVariant) { - UnicodeString variant_str = variantDisplayName(variant, temp, true); - if (variant_str.isBogus()) { - result.setToBogus(); - return result; - } - appendWithSep(resultRemainder, variant_str); - } - resultRemainder.findAndReplace(formatOpenParen, formatReplaceOpenParen); - resultRemainder.findAndReplace(formatCloseParen, formatReplaceCloseParen); - - LocalPointer e(loc.createKeywords(status)); - if (e.isValid() && U_SUCCESS(status)) { - UnicodeString temp2; - char value[ULOC_KEYWORD_AND_VALUES_CAPACITY]; // sigh, no ULOC_VALUE_CAPACITY - const char* key; - while ((key = e->next((int32_t *)0, status)) != NULL) { - value[0] = 0; - loc.getKeywordValue(key, value, ULOC_KEYWORD_AND_VALUES_CAPACITY, status); - if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { - return result; - } - keyDisplayName(key, temp, true); - temp.findAndReplace(formatOpenParen, formatReplaceOpenParen); - temp.findAndReplace(formatCloseParen, formatReplaceCloseParen); - keyValueDisplayName(key, value, temp2, true); - temp2.findAndReplace(formatOpenParen, formatReplaceOpenParen); - temp2.findAndReplace(formatCloseParen, formatReplaceCloseParen); - if (temp2 != UnicodeString(value, -1, US_INV)) { - appendWithSep(resultRemainder, temp2); - } else if (temp != UnicodeString(key, -1, US_INV)) { - UnicodeString temp3; - keyTypeFormat.format(temp, temp2, temp3, status); - appendWithSep(resultRemainder, temp3); - } else { - appendWithSep(resultRemainder, temp) - .append((UChar)0x3d /* = */) - .append(temp2); - } - } - } - - if (!resultRemainder.isEmpty()) { - format.format(resultName, resultRemainder, result.remove(), status); - return adjustForUsageAndContext(kCapContextUsageLanguage, result); - } - - result = resultName; - return adjustForUsageAndContext(kCapContextUsageLanguage, result); -} - -UnicodeString& -LocaleDisplayNamesImpl::appendWithSep(UnicodeString& buffer, const UnicodeString& src) const { - if (buffer.isEmpty()) { - buffer.setTo(src); - } else { - const UnicodeString *values[2] = { &buffer, &src }; - UErrorCode status = U_ZERO_ERROR; - separatorFormat.formatAndReplace(values, 2, buffer, NULL, 0, status); - } - return buffer; -} - -UnicodeString& -LocaleDisplayNamesImpl::localeDisplayName(const char* localeId, - UnicodeString& result) const { - return localeDisplayName(Locale(localeId), result); -} - -// private -UnicodeString& -LocaleDisplayNamesImpl::localeIdName(const char* localeId, - UnicodeString& result, bool substitute) const { - if (nameLength == UDISPCTX_LENGTH_SHORT) { - langData.getNoFallback("Languages%short", localeId, result); - if (!result.isBogus()) { - return result; - } - } - langData.getNoFallback("Languages", localeId, result); - if (result.isBogus() && uprv_strchr(localeId, '_') == NULL) { - // Canonicalize lang and try again, ICU-20870 - // (only for language codes without script or region) - Locale canonLocale = Locale::createCanonical(localeId); - const char* canonLocId = canonLocale.getName(); - if (nameLength == UDISPCTX_LENGTH_SHORT) { - langData.getNoFallback("Languages%short", canonLocId, result); - if (!result.isBogus()) { - return result; - } - } - langData.getNoFallback("Languages", canonLocId, result); - } - if (result.isBogus() && substitute) { - // use key, this is what langData.get (with fallback) falls back to. - result.setTo(UnicodeString(localeId, -1, US_INV)); // use key ( - } - return result; -} - -UnicodeString& -LocaleDisplayNamesImpl::languageDisplayName(const char* lang, - UnicodeString& result) const { - if (uprv_strcmp("root", lang) == 0 || uprv_strchr(lang, '_') != NULL) { - return result = UnicodeString(lang, -1, US_INV); - } - if (nameLength == UDISPCTX_LENGTH_SHORT) { - langData.getNoFallback("Languages%short", lang, result); - if (!result.isBogus()) { - return adjustForUsageAndContext(kCapContextUsageLanguage, result); - } - } - langData.getNoFallback("Languages", lang, result); - if (result.isBogus()) { - // Canonicalize lang and try again, ICU-20870 - Locale canonLocale = Locale::createCanonical(lang); - const char* canonLocId = canonLocale.getName(); - if (nameLength == UDISPCTX_LENGTH_SHORT) { - langData.getNoFallback("Languages%short", canonLocId, result); - if (!result.isBogus()) { - return adjustForUsageAndContext(kCapContextUsageLanguage, result); - } - } - langData.getNoFallback("Languages", canonLocId, result); - } - if (result.isBogus() && substitute == UDISPCTX_SUBSTITUTE) { - // use key, this is what langData.get (with fallback) falls back to. - result.setTo(UnicodeString(lang, -1, US_INV)); // use key ( - } - return adjustForUsageAndContext(kCapContextUsageLanguage, result); -} - -UnicodeString& -LocaleDisplayNamesImpl::scriptDisplayName(const char* script, - UnicodeString& result, - UBool skipAdjust) const { - if (nameLength == UDISPCTX_LENGTH_SHORT) { - langData.getNoFallback("Scripts%short", script, result); - if (!result.isBogus()) { - return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageScript, result); - } - } - if (substitute == UDISPCTX_SUBSTITUTE) { - langData.get("Scripts", script, result); - } else { - langData.getNoFallback("Scripts", script, result); - } - return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageScript, result); -} - -UnicodeString& -LocaleDisplayNamesImpl::scriptDisplayName(const char* script, - UnicodeString& result) const { - return scriptDisplayName(script, result, false); -} - -UnicodeString& -LocaleDisplayNamesImpl::scriptDisplayName(UScriptCode scriptCode, - UnicodeString& result) const { - return scriptDisplayName(uscript_getName(scriptCode), result, false); -} - -UnicodeString& -LocaleDisplayNamesImpl::regionDisplayName(const char* region, - UnicodeString& result, - UBool skipAdjust) const { - if (nameLength == UDISPCTX_LENGTH_SHORT) { - regionData.getNoFallback("Countries%short", region, result); - if (!result.isBogus()) { - return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageTerritory, result); - } - } - if (substitute == UDISPCTX_SUBSTITUTE) { - regionData.get("Countries", region, result); - } else { - regionData.getNoFallback("Countries", region, result); - } - return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageTerritory, result); -} - -UnicodeString& -LocaleDisplayNamesImpl::regionDisplayName(const char* region, - UnicodeString& result) const { - return regionDisplayName(region, result, false); -} - - -UnicodeString& -LocaleDisplayNamesImpl::variantDisplayName(const char* variant, - UnicodeString& result, - UBool skipAdjust) const { - // don't have a resource for short variant names - if (substitute == UDISPCTX_SUBSTITUTE) { - langData.get("Variants", variant, result); - } else { - langData.getNoFallback("Variants", variant, result); - } - return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageVariant, result); -} - -UnicodeString& -LocaleDisplayNamesImpl::variantDisplayName(const char* variant, - UnicodeString& result) const { - return variantDisplayName(variant, result, false); -} - -UnicodeString& -LocaleDisplayNamesImpl::keyDisplayName(const char* key, - UnicodeString& result, - UBool skipAdjust) const { - // don't have a resource for short key names - if (substitute == UDISPCTX_SUBSTITUTE) { - langData.get("Keys", key, result); - } else { - langData.getNoFallback("Keys", key, result); - } - return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKey, result); -} - -UnicodeString& -LocaleDisplayNamesImpl::keyDisplayName(const char* key, - UnicodeString& result) const { - return keyDisplayName(key, result, false); -} - -UnicodeString& -LocaleDisplayNamesImpl::keyValueDisplayName(const char* key, - const char* value, - UnicodeString& result, - UBool skipAdjust) const { - if (uprv_strcmp(key, "currency") == 0) { - // ICU4C does not have ICU4J CurrencyDisplayInfo equivalent for now. - UErrorCode sts = U_ZERO_ERROR; - UnicodeString ustrValue(value, -1, US_INV); - int32_t len; - const UChar *currencyName = ucurr_getName(ustrValue.getTerminatedBuffer(), - locale.getBaseName(), UCURR_LONG_NAME, nullptr /* isChoiceFormat */, &len, &sts); - if (U_FAILURE(sts)) { - // Return the value as is on failure - result = ustrValue; - return result; - } - result.setTo(currencyName, len); - return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKeyValue, result); - } - - if (nameLength == UDISPCTX_LENGTH_SHORT) { - langData.getNoFallback("Types%short", key, value, result); - if (!result.isBogus()) { - return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKeyValue, result); - } - } - if (substitute == UDISPCTX_SUBSTITUTE) { - langData.get("Types", key, value, result); - } else { - langData.getNoFallback("Types", key, value, result); - } - return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKeyValue, result); -} - -UnicodeString& -LocaleDisplayNamesImpl::keyValueDisplayName(const char* key, - const char* value, - UnicodeString& result) const { - return keyValueDisplayName(key, value, result, false); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -LocaleDisplayNames* -LocaleDisplayNames::createInstance(const Locale& locale, - UDialectHandling dialectHandling) { - return new LocaleDisplayNamesImpl(locale, dialectHandling); -} - -LocaleDisplayNames* -LocaleDisplayNames::createInstance(const Locale& locale, - UDisplayContext *contexts, int32_t length) { - if (contexts == NULL) { - length = 0; - } - return new LocaleDisplayNamesImpl(locale, contexts, length); -} - -U_NAMESPACE_END - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -U_NAMESPACE_USE - -U_CAPI ULocaleDisplayNames * U_EXPORT2 -uldn_open(const char * locale, - UDialectHandling dialectHandling, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return 0; - } - if (locale == NULL) { - locale = uloc_getDefault(); - } - return (ULocaleDisplayNames *)LocaleDisplayNames::createInstance(Locale(locale), dialectHandling); -} - -U_CAPI ULocaleDisplayNames * U_EXPORT2 -uldn_openForContext(const char * locale, - UDisplayContext *contexts, int32_t length, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return 0; - } - if (locale == NULL) { - locale = uloc_getDefault(); - } - return (ULocaleDisplayNames *)LocaleDisplayNames::createInstance(Locale(locale), contexts, length); -} - - -U_CAPI void U_EXPORT2 -uldn_close(ULocaleDisplayNames *ldn) { - delete (LocaleDisplayNames *)ldn; -} - -U_CAPI const char * U_EXPORT2 -uldn_getLocale(const ULocaleDisplayNames *ldn) { - if (ldn) { - return ((const LocaleDisplayNames *)ldn)->getLocale().getName(); - } - return NULL; -} - -U_CAPI UDialectHandling U_EXPORT2 -uldn_getDialectHandling(const ULocaleDisplayNames *ldn) { - if (ldn) { - return ((const LocaleDisplayNames *)ldn)->getDialectHandling(); - } - return ULDN_STANDARD_NAMES; -} - -U_CAPI UDisplayContext U_EXPORT2 -uldn_getContext(const ULocaleDisplayNames *ldn, - UDisplayContextType type, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return (UDisplayContext)0; - } - return ((const LocaleDisplayNames *)ldn)->getContext(type); -} - -U_CAPI int32_t U_EXPORT2 -uldn_localeDisplayName(const ULocaleDisplayNames *ldn, - const char *locale, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return 0; - } - if (ldn == NULL || locale == NULL || (result == NULL && maxResultSize > 0) || maxResultSize < 0) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString temp(result, 0, maxResultSize); - ((const LocaleDisplayNames *)ldn)->localeDisplayName(locale, temp); - if (temp.isBogus()) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - return temp.extract(result, maxResultSize, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uldn_languageDisplayName(const ULocaleDisplayNames *ldn, - const char *lang, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return 0; - } - if (ldn == NULL || lang == NULL || (result == NULL && maxResultSize > 0) || maxResultSize < 0) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString temp(result, 0, maxResultSize); - ((const LocaleDisplayNames *)ldn)->languageDisplayName(lang, temp); - return temp.extract(result, maxResultSize, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uldn_scriptDisplayName(const ULocaleDisplayNames *ldn, - const char *script, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return 0; - } - if (ldn == NULL || script == NULL || (result == NULL && maxResultSize > 0) || maxResultSize < 0) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString temp(result, 0, maxResultSize); - ((const LocaleDisplayNames *)ldn)->scriptDisplayName(script, temp); - return temp.extract(result, maxResultSize, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uldn_scriptCodeDisplayName(const ULocaleDisplayNames *ldn, - UScriptCode scriptCode, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode) { - return uldn_scriptDisplayName(ldn, uscript_getName(scriptCode), result, maxResultSize, pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uldn_regionDisplayName(const ULocaleDisplayNames *ldn, - const char *region, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return 0; - } - if (ldn == NULL || region == NULL || (result == NULL && maxResultSize > 0) || maxResultSize < 0) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString temp(result, 0, maxResultSize); - ((const LocaleDisplayNames *)ldn)->regionDisplayName(region, temp); - return temp.extract(result, maxResultSize, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uldn_variantDisplayName(const ULocaleDisplayNames *ldn, - const char *variant, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return 0; - } - if (ldn == NULL || variant == NULL || (result == NULL && maxResultSize > 0) || maxResultSize < 0) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString temp(result, 0, maxResultSize); - ((const LocaleDisplayNames *)ldn)->variantDisplayName(variant, temp); - return temp.extract(result, maxResultSize, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uldn_keyDisplayName(const ULocaleDisplayNames *ldn, - const char *key, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return 0; - } - if (ldn == NULL || key == NULL || (result == NULL && maxResultSize > 0) || maxResultSize < 0) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString temp(result, 0, maxResultSize); - ((const LocaleDisplayNames *)ldn)->keyDisplayName(key, temp); - return temp.extract(result, maxResultSize, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uldn_keyValueDisplayName(const ULocaleDisplayNames *ldn, - const char *key, - const char *value, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return 0; - } - if (ldn == NULL || key == NULL || value == NULL || (result == NULL && maxResultSize > 0) - || maxResultSize < 0) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString temp(result, 0, maxResultSize); - ((const LocaleDisplayNames *)ldn)->keyValueDisplayName(key, value, temp); - return temp.extract(result, maxResultSize, *pErrorCode); -} - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/locdspnm.h" +#include "unicode/simpleformatter.h" +#include "unicode/ucasemap.h" +#include "unicode/ures.h" +#include "unicode/udisplaycontext.h" +#include "unicode/brkiter.h" +#include "unicode/ucurr.h" +#include "cmemory.h" +#include "cstring.h" +#include "mutex.h" +#include "ulocimp.h" +#include "umutex.h" +#include "ureslocs.h" +#include "uresimp.h" + +#include + +/** + * Concatenate a number of null-terminated strings to buffer, leaving a + * null-terminated string. The last argument should be the null pointer. + * Return the length of the string in the buffer, not counting the trailing + * null. Return -1 if there is an error (buffer is null, or buflen < 1). + */ +static int32_t ncat(char *buffer, uint32_t buflen, ...) { + va_list args; + char *str; + char *p = buffer; + const char* e = buffer + buflen - 1; + + if (buffer == nullptr || buflen < 1) { + return -1; + } + + va_start(args, buflen); + while ((str = va_arg(args, char *)) != 0) { + char c; + while (p != e && (c = *str++) != 0) { + *p++ = c; + } + } + *p = 0; + va_end(args); + + return static_cast(p - buffer); +} + +U_NAMESPACE_BEGIN + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// Access resource data for locale components. +// Wrap code in uloc.c for now. +class ICUDataTable { + const char* path; + Locale locale; + +public: + ICUDataTable(const char* path, const Locale& locale); + ~ICUDataTable(); + + const Locale& getLocale(); + + UnicodeString& get(const char* tableKey, const char* itemKey, + UnicodeString& result) const; + UnicodeString& get(const char* tableKey, const char* subTableKey, const char* itemKey, + UnicodeString& result) const; + + UnicodeString& getNoFallback(const char* tableKey, const char* itemKey, + UnicodeString &result) const; + UnicodeString& getNoFallback(const char* tableKey, const char* subTableKey, const char* itemKey, + UnicodeString &result) const; +}; + +inline UnicodeString & +ICUDataTable::get(const char* tableKey, const char* itemKey, UnicodeString& result) const { + return get(tableKey, nullptr, itemKey, result); +} + +inline UnicodeString & +ICUDataTable::getNoFallback(const char* tableKey, const char* itemKey, UnicodeString& result) const { + return getNoFallback(tableKey, nullptr, itemKey, result); +} + +ICUDataTable::ICUDataTable(const char* path, const Locale& locale) + : path(nullptr), locale(Locale::getRoot()) +{ + if (path) { + int32_t len = static_cast(uprv_strlen(path)); + this->path = (const char*) uprv_malloc(len + 1); + if (this->path) { + uprv_strcpy((char *)this->path, path); + this->locale = locale; + } + } +} + +ICUDataTable::~ICUDataTable() { + if (path) { + uprv_free((void*) path); + path = nullptr; + } +} + +const Locale& +ICUDataTable::getLocale() { + return locale; +} + +UnicodeString & +ICUDataTable::get(const char* tableKey, const char* subTableKey, const char* itemKey, + UnicodeString &result) const { + UErrorCode status = U_ZERO_ERROR; + int32_t len = 0; + + const char16_t *s = uloc_getTableStringWithFallback(path, locale.getName(), + tableKey, subTableKey, itemKey, + &len, &status); + if (U_SUCCESS(status) && len > 0) { + return result.setTo(s, len); + } + return result.setTo(UnicodeString(itemKey, -1, US_INV)); +} + +UnicodeString & +ICUDataTable::getNoFallback(const char* tableKey, const char* subTableKey, const char* itemKey, + UnicodeString& result) const { + UErrorCode status = U_ZERO_ERROR; + int32_t len = 0; + + const char16_t *s = uloc_getTableStringWithFallback(path, locale.getName(), + tableKey, subTableKey, itemKey, + &len, &status); + if (U_SUCCESS(status)) { + return result.setTo(s, len); + } + + result.setToBogus(); + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +LocaleDisplayNames::~LocaleDisplayNames() {} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#if 0 // currently unused + +class DefaultLocaleDisplayNames : public LocaleDisplayNames { + UDialectHandling dialectHandling; + +public: + // constructor + DefaultLocaleDisplayNames(UDialectHandling dialectHandling); + + virtual ~DefaultLocaleDisplayNames(); + + virtual const Locale& getLocale() const; + virtual UDialectHandling getDialectHandling() const; + + virtual UnicodeString& localeDisplayName(const Locale& locale, + UnicodeString& result) const; + virtual UnicodeString& localeDisplayName(const char* localeId, + UnicodeString& result) const; + virtual UnicodeString& languageDisplayName(const char* lang, + UnicodeString& result) const; + virtual UnicodeString& scriptDisplayName(const char* script, + UnicodeString& result) const; + virtual UnicodeString& scriptDisplayName(UScriptCode scriptCode, + UnicodeString& result) const; + virtual UnicodeString& regionDisplayName(const char* region, + UnicodeString& result) const; + virtual UnicodeString& variantDisplayName(const char* variant, + UnicodeString& result) const; + virtual UnicodeString& keyDisplayName(const char* key, + UnicodeString& result) const; + virtual UnicodeString& keyValueDisplayName(const char* key, + const char* value, + UnicodeString& result) const; +}; + +DefaultLocaleDisplayNames::DefaultLocaleDisplayNames(UDialectHandling dialectHandling) + : dialectHandling(dialectHandling) { +} + +DefaultLocaleDisplayNames::~DefaultLocaleDisplayNames() { +} + +const Locale& +DefaultLocaleDisplayNames::getLocale() const { + return Locale::getRoot(); +} + +UDialectHandling +DefaultLocaleDisplayNames::getDialectHandling() const { + return dialectHandling; +} + +UnicodeString& +DefaultLocaleDisplayNames::localeDisplayName(const Locale& locale, + UnicodeString& result) const { + return result = UnicodeString(locale.getName(), -1, US_INV); +} + +UnicodeString& +DefaultLocaleDisplayNames::localeDisplayName(const char* localeId, + UnicodeString& result) const { + return result = UnicodeString(localeId, -1, US_INV); +} + +UnicodeString& +DefaultLocaleDisplayNames::languageDisplayName(const char* lang, + UnicodeString& result) const { + return result = UnicodeString(lang, -1, US_INV); +} + +UnicodeString& +DefaultLocaleDisplayNames::scriptDisplayName(const char* script, + UnicodeString& result) const { + return result = UnicodeString(script, -1, US_INV); +} + +UnicodeString& +DefaultLocaleDisplayNames::scriptDisplayName(UScriptCode scriptCode, + UnicodeString& result) const { + const char* name = uscript_getName(scriptCode); + if (name) { + return result = UnicodeString(name, -1, US_INV); + } + return result.remove(); +} + +UnicodeString& +DefaultLocaleDisplayNames::regionDisplayName(const char* region, + UnicodeString& result) const { + return result = UnicodeString(region, -1, US_INV); +} + +UnicodeString& +DefaultLocaleDisplayNames::variantDisplayName(const char* variant, + UnicodeString& result) const { + return result = UnicodeString(variant, -1, US_INV); +} + +UnicodeString& +DefaultLocaleDisplayNames::keyDisplayName(const char* key, + UnicodeString& result) const { + return result = UnicodeString(key, -1, US_INV); +} + +UnicodeString& +DefaultLocaleDisplayNames::keyValueDisplayName(const char* /* key */, + const char* value, + UnicodeString& result) const { + return result = UnicodeString(value, -1, US_INV); +} + +#endif // currently unused class DefaultLocaleDisplayNames + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +class LocaleDisplayNamesImpl : public LocaleDisplayNames { + Locale locale; + UDialectHandling dialectHandling; + ICUDataTable langData; + ICUDataTable regionData; + SimpleFormatter separatorFormat; + SimpleFormatter format; + SimpleFormatter keyTypeFormat; + UDisplayContext capitalizationContext; +#if !UCONFIG_NO_BREAK_ITERATION + BreakIterator* capitalizationBrkIter; +#else + UObject* capitalizationBrkIter; +#endif + UnicodeString formatOpenParen; + UnicodeString formatReplaceOpenParen; + UnicodeString formatCloseParen; + UnicodeString formatReplaceCloseParen; + UDisplayContext nameLength; + UDisplayContext substitute; + + // Constants for capitalization context usage types. + enum CapContextUsage { + kCapContextUsageLanguage, + kCapContextUsageScript, + kCapContextUsageTerritory, + kCapContextUsageVariant, + kCapContextUsageKey, + kCapContextUsageKeyValue, + kCapContextUsageCount + }; + // Capitalization transforms. For each usage type, indicates whether to titlecase for + // the context specified in capitalizationContext (which we know at construction time) + UBool fCapitalization[kCapContextUsageCount]; + +public: + // constructor + LocaleDisplayNamesImpl(const Locale& locale, UDialectHandling dialectHandling); + LocaleDisplayNamesImpl(const Locale& locale, UDisplayContext *contexts, int32_t length); + virtual ~LocaleDisplayNamesImpl(); + + virtual const Locale& getLocale() const override; + virtual UDialectHandling getDialectHandling() const override; + virtual UDisplayContext getContext(UDisplayContextType type) const override; + + virtual UnicodeString& localeDisplayName(const Locale& locale, + UnicodeString& result) const override; + virtual UnicodeString& localeDisplayName(const char* localeId, + UnicodeString& result) const override; + virtual UnicodeString& languageDisplayName(const char* lang, + UnicodeString& result) const override; + virtual UnicodeString& scriptDisplayName(const char* script, + UnicodeString& result) const override; + virtual UnicodeString& scriptDisplayName(UScriptCode scriptCode, + UnicodeString& result) const override; + virtual UnicodeString& regionDisplayName(const char* region, + UnicodeString& result) const override; + virtual UnicodeString& variantDisplayName(const char* variant, + UnicodeString& result) const override; + virtual UnicodeString& keyDisplayName(const char* key, + UnicodeString& result) const override; + virtual UnicodeString& keyValueDisplayName(const char* key, + const char* value, + UnicodeString& result) const override; +private: + UnicodeString& localeIdName(const char* localeId, + UnicodeString& result, bool substitute) const; + UnicodeString& appendWithSep(UnicodeString& buffer, const UnicodeString& src) const; + UnicodeString& adjustForUsageAndContext(CapContextUsage usage, UnicodeString& result) const; + UnicodeString& scriptDisplayName(const char* script, UnicodeString& result, UBool skipAdjust) const; + UnicodeString& regionDisplayName(const char* region, UnicodeString& result, UBool skipAdjust) const; + UnicodeString& variantDisplayName(const char* variant, UnicodeString& result, UBool skipAdjust) const; + UnicodeString& keyDisplayName(const char* key, UnicodeString& result, UBool skipAdjust) const; + UnicodeString& keyValueDisplayName(const char* key, const char* value, + UnicodeString& result, UBool skipAdjust) const; + void initialize(); + + struct CapitalizationContextSink; +}; + +LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale, + UDialectHandling dialectHandling) + : dialectHandling(dialectHandling) + , langData(U_ICUDATA_LANG, locale) + , regionData(U_ICUDATA_REGION, locale) + , capitalizationContext(UDISPCTX_CAPITALIZATION_NONE) + , capitalizationBrkIter(nullptr) + , nameLength(UDISPCTX_LENGTH_FULL) + , substitute(UDISPCTX_SUBSTITUTE) +{ + initialize(); +} + +LocaleDisplayNamesImpl::LocaleDisplayNamesImpl(const Locale& locale, + UDisplayContext *contexts, int32_t length) + : dialectHandling(ULDN_STANDARD_NAMES) + , langData(U_ICUDATA_LANG, locale) + , regionData(U_ICUDATA_REGION, locale) + , capitalizationContext(UDISPCTX_CAPITALIZATION_NONE) + , capitalizationBrkIter(nullptr) + , nameLength(UDISPCTX_LENGTH_FULL) + , substitute(UDISPCTX_SUBSTITUTE) +{ + while (length-- > 0) { + UDisplayContext value = *contexts++; + UDisplayContextType selector = (UDisplayContextType)((uint32_t)value >> 8); + switch (selector) { + case UDISPCTX_TYPE_DIALECT_HANDLING: + dialectHandling = (UDialectHandling)value; + break; + case UDISPCTX_TYPE_CAPITALIZATION: + capitalizationContext = value; + break; + case UDISPCTX_TYPE_DISPLAY_LENGTH: + nameLength = value; + break; + case UDISPCTX_TYPE_SUBSTITUTE_HANDLING: + substitute = value; + break; + default: + break; + } + } + initialize(); +} + +struct LocaleDisplayNamesImpl::CapitalizationContextSink : public ResourceSink { + UBool hasCapitalizationUsage; + LocaleDisplayNamesImpl& parent; + + CapitalizationContextSink(LocaleDisplayNamesImpl& _parent) + : hasCapitalizationUsage(false), parent(_parent) {} + virtual ~CapitalizationContextSink(); + + virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, + UErrorCode &errorCode) override { + ResourceTable contexts = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + for (int i = 0; contexts.getKeyAndValue(i, key, value); ++i) { + + CapContextUsage usageEnum; + if (uprv_strcmp(key, "key") == 0) { + usageEnum = kCapContextUsageKey; + } else if (uprv_strcmp(key, "keyValue") == 0) { + usageEnum = kCapContextUsageKeyValue; + } else if (uprv_strcmp(key, "languages") == 0) { + usageEnum = kCapContextUsageLanguage; + } else if (uprv_strcmp(key, "script") == 0) { + usageEnum = kCapContextUsageScript; + } else if (uprv_strcmp(key, "territory") == 0) { + usageEnum = kCapContextUsageTerritory; + } else if (uprv_strcmp(key, "variant") == 0) { + usageEnum = kCapContextUsageVariant; + } else { + continue; + } + + int32_t len = 0; + const int32_t* intVector = value.getIntVector(len, errorCode); + if (U_FAILURE(errorCode)) { return; } + if (len < 2) { continue; } + + int32_t titlecaseInt = (parent.capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU) ? intVector[0] : intVector[1]; + if (titlecaseInt == 0) { continue; } + + parent.fCapitalization[usageEnum] = true; + hasCapitalizationUsage = true; + } + } +}; + +// Virtual destructors must be defined out of line. +LocaleDisplayNamesImpl::CapitalizationContextSink::~CapitalizationContextSink() {} + +void +LocaleDisplayNamesImpl::initialize() { + LocaleDisplayNamesImpl *nonConstThis = (LocaleDisplayNamesImpl *)this; + nonConstThis->locale = langData.getLocale() == Locale::getRoot() + ? regionData.getLocale() + : langData.getLocale(); + + UnicodeString sep; + langData.getNoFallback("localeDisplayPattern", "separator", sep); + if (sep.isBogus()) { + sep = UnicodeString("{0}, {1}", -1, US_INV); + } + UErrorCode status = U_ZERO_ERROR; + separatorFormat.applyPatternMinMaxArguments(sep, 2, 2, status); + + UnicodeString pattern; + langData.getNoFallback("localeDisplayPattern", "pattern", pattern); + if (pattern.isBogus()) { + pattern = UnicodeString("{0} ({1})", -1, US_INV); + } + format.applyPatternMinMaxArguments(pattern, 2, 2, status); + if (pattern.indexOf((char16_t)0xFF08) >= 0) { + formatOpenParen.setTo((char16_t)0xFF08); // fullwidth ( + formatReplaceOpenParen.setTo((char16_t)0xFF3B); // fullwidth [ + formatCloseParen.setTo((char16_t)0xFF09); // fullwidth ) + formatReplaceCloseParen.setTo((char16_t)0xFF3D); // fullwidth ] + } else { + formatOpenParen.setTo((char16_t)0x0028); // ( + formatReplaceOpenParen.setTo((char16_t)0x005B); // [ + formatCloseParen.setTo((char16_t)0x0029); // ) + formatReplaceCloseParen.setTo((char16_t)0x005D); // ] + } + + UnicodeString ktPattern; + langData.get("localeDisplayPattern", "keyTypePattern", ktPattern); + if (ktPattern.isBogus()) { + ktPattern = UnicodeString("{0}={1}", -1, US_INV); + } + keyTypeFormat.applyPatternMinMaxArguments(ktPattern, 2, 2, status); + + uprv_memset(fCapitalization, 0, sizeof(fCapitalization)); +#if !UCONFIG_NO_BREAK_ITERATION + // Only get the context data if we need it! This is a const object so we know now... + // Also check whether we will need a break iterator (depends on the data) + UBool needBrkIter = false; + if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_STANDALONE) { + LocalUResourceBundlePointer resource(ures_open(nullptr, locale.getName(), &status)); + if (U_FAILURE(status)) { return; } + CapitalizationContextSink sink(*this); + ures_getAllItemsWithFallback(resource.getAlias(), "contextTransforms", sink, status); + if (status == U_MISSING_RESOURCE_ERROR) { + // Silently ignore. Not every locale has contextTransforms. + status = U_ZERO_ERROR; + } else if (U_FAILURE(status)) { + return; + } + needBrkIter = sink.hasCapitalizationUsage; + } + // Get a sentence break iterator if we will need it + if (needBrkIter || capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) { + status = U_ZERO_ERROR; + capitalizationBrkIter = BreakIterator::createSentenceInstance(locale, status); + if (U_FAILURE(status)) { + delete capitalizationBrkIter; + capitalizationBrkIter = nullptr; + } + } +#endif +} + +LocaleDisplayNamesImpl::~LocaleDisplayNamesImpl() { +#if !UCONFIG_NO_BREAK_ITERATION + delete capitalizationBrkIter; +#endif +} + +const Locale& +LocaleDisplayNamesImpl::getLocale() const { + return locale; +} + +UDialectHandling +LocaleDisplayNamesImpl::getDialectHandling() const { + return dialectHandling; +} + +UDisplayContext +LocaleDisplayNamesImpl::getContext(UDisplayContextType type) const { + switch (type) { + case UDISPCTX_TYPE_DIALECT_HANDLING: + return (UDisplayContext)dialectHandling; + case UDISPCTX_TYPE_CAPITALIZATION: + return capitalizationContext; + case UDISPCTX_TYPE_DISPLAY_LENGTH: + return nameLength; + case UDISPCTX_TYPE_SUBSTITUTE_HANDLING: + return substitute; + default: + break; + } + return (UDisplayContext)0; +} + +UnicodeString& +LocaleDisplayNamesImpl::adjustForUsageAndContext(CapContextUsage usage, + UnicodeString& result) const { +#if !UCONFIG_NO_BREAK_ITERATION + // check to see whether we need to titlecase result + if ( result.length() > 0 && u_islower(result.char32At(0)) && capitalizationBrkIter!= nullptr && + ( capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || fCapitalization[usage] ) ) { + // note fCapitalization[usage] won't be set unless capitalizationContext is UI_LIST_OR_MENU or STANDALONE + static UMutex capitalizationBrkIterLock; + Mutex lock(&capitalizationBrkIterLock); + result.toTitle(capitalizationBrkIter, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); + } +#endif + return result; +} + +UnicodeString& +LocaleDisplayNamesImpl::localeDisplayName(const Locale& loc, + UnicodeString& result) const { + if (loc.isBogus()) { + result.setToBogus(); + return result; + } + UnicodeString resultName; + + const char* lang = loc.getLanguage(); + if (uprv_strlen(lang) == 0) { + lang = "root"; + } + const char* script = loc.getScript(); + const char* country = loc.getCountry(); + const char* variant = loc.getVariant(); + + UBool hasScript = uprv_strlen(script) > 0; + UBool hasCountry = uprv_strlen(country) > 0; + UBool hasVariant = uprv_strlen(variant) > 0; + + if (dialectHandling == ULDN_DIALECT_NAMES) { + char buffer[ULOC_FULLNAME_CAPACITY]; + do { // loop construct is so we can break early out of search + if (hasScript && hasCountry) { + ncat(buffer, ULOC_FULLNAME_CAPACITY, lang, "_", script, "_", country, (char *)0); + localeIdName(buffer, resultName, false); + if (!resultName.isBogus()) { + hasScript = false; + hasCountry = false; + break; + } + } + if (hasScript) { + ncat(buffer, ULOC_FULLNAME_CAPACITY, lang, "_", script, (char *)0); + localeIdName(buffer, resultName, false); + if (!resultName.isBogus()) { + hasScript = false; + break; + } + } + if (hasCountry) { + ncat(buffer, ULOC_FULLNAME_CAPACITY, lang, "_", country, (char*)0); + localeIdName(buffer, resultName, false); + if (!resultName.isBogus()) { + hasCountry = false; + break; + } + } + } while (false); + } + if (resultName.isBogus() || resultName.isEmpty()) { + localeIdName(lang, resultName, substitute == UDISPCTX_SUBSTITUTE); + if (resultName.isBogus()) { + result.setToBogus(); + return result; + } + } + + UnicodeString resultRemainder; + UnicodeString temp; + UErrorCode status = U_ZERO_ERROR; + + if (hasScript) { + UnicodeString script_str = scriptDisplayName(script, temp, true); + if (script_str.isBogus()) { + result.setToBogus(); + return result; + } + resultRemainder.append(script_str); + } + if (hasCountry) { + UnicodeString region_str = regionDisplayName(country, temp, true); + if (region_str.isBogus()) { + result.setToBogus(); + return result; + } + appendWithSep(resultRemainder, region_str); + } + if (hasVariant) { + UnicodeString variant_str = variantDisplayName(variant, temp, true); + if (variant_str.isBogus()) { + result.setToBogus(); + return result; + } + appendWithSep(resultRemainder, variant_str); + } + resultRemainder.findAndReplace(formatOpenParen, formatReplaceOpenParen); + resultRemainder.findAndReplace(formatCloseParen, formatReplaceCloseParen); + + LocalPointer e(loc.createKeywords(status)); + if (e.isValid() && U_SUCCESS(status)) { + UnicodeString temp2; + char value[ULOC_KEYWORD_AND_VALUES_CAPACITY]; // sigh, no ULOC_VALUE_CAPACITY + const char* key; + while ((key = e->next((int32_t *)0, status)) != nullptr) { + value[0] = 0; + loc.getKeywordValue(key, value, ULOC_KEYWORD_AND_VALUES_CAPACITY, status); + if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { + return result; + } + keyDisplayName(key, temp, true); + temp.findAndReplace(formatOpenParen, formatReplaceOpenParen); + temp.findAndReplace(formatCloseParen, formatReplaceCloseParen); + keyValueDisplayName(key, value, temp2, true); + temp2.findAndReplace(formatOpenParen, formatReplaceOpenParen); + temp2.findAndReplace(formatCloseParen, formatReplaceCloseParen); + if (temp2 != UnicodeString(value, -1, US_INV)) { + appendWithSep(resultRemainder, temp2); + } else if (temp != UnicodeString(key, -1, US_INV)) { + UnicodeString temp3; + keyTypeFormat.format(temp, temp2, temp3, status); + appendWithSep(resultRemainder, temp3); + } else { + appendWithSep(resultRemainder, temp) + .append((char16_t)0x3d /* = */) + .append(temp2); + } + } + } + + if (!resultRemainder.isEmpty()) { + format.format(resultName, resultRemainder, result.remove(), status); + return adjustForUsageAndContext(kCapContextUsageLanguage, result); + } + + result = resultName; + return adjustForUsageAndContext(kCapContextUsageLanguage, result); +} + +UnicodeString& +LocaleDisplayNamesImpl::appendWithSep(UnicodeString& buffer, const UnicodeString& src) const { + if (buffer.isEmpty()) { + buffer.setTo(src); + } else { + const UnicodeString *values[2] = { &buffer, &src }; + UErrorCode status = U_ZERO_ERROR; + separatorFormat.formatAndReplace(values, 2, buffer, nullptr, 0, status); + } + return buffer; +} + +UnicodeString& +LocaleDisplayNamesImpl::localeDisplayName(const char* localeId, + UnicodeString& result) const { + return localeDisplayName(Locale(localeId), result); +} + +// private +UnicodeString& +LocaleDisplayNamesImpl::localeIdName(const char* localeId, + UnicodeString& result, bool substitute) const { + if (nameLength == UDISPCTX_LENGTH_SHORT) { + langData.getNoFallback("Languages%short", localeId, result); + if (!result.isBogus()) { + return result; + } + } + langData.getNoFallback("Languages", localeId, result); + if (result.isBogus() && uprv_strchr(localeId, '_') == nullptr) { + // Canonicalize lang and try again, ICU-20870 + // (only for language codes without script or region) + Locale canonLocale = Locale::createCanonical(localeId); + const char* canonLocId = canonLocale.getName(); + if (nameLength == UDISPCTX_LENGTH_SHORT) { + langData.getNoFallback("Languages%short", canonLocId, result); + if (!result.isBogus()) { + return result; + } + } + langData.getNoFallback("Languages", canonLocId, result); + } + if (result.isBogus() && substitute) { + // use key, this is what langData.get (with fallback) falls back to. + result.setTo(UnicodeString(localeId, -1, US_INV)); // use key ( + } + return result; +} + +UnicodeString& +LocaleDisplayNamesImpl::languageDisplayName(const char* lang, + UnicodeString& result) const { + if (uprv_strcmp("root", lang) == 0 || uprv_strchr(lang, '_') != nullptr) { + return result = UnicodeString(lang, -1, US_INV); + } + if (nameLength == UDISPCTX_LENGTH_SHORT) { + langData.getNoFallback("Languages%short", lang, result); + if (!result.isBogus()) { + return adjustForUsageAndContext(kCapContextUsageLanguage, result); + } + } + langData.getNoFallback("Languages", lang, result); + if (result.isBogus()) { + // Canonicalize lang and try again, ICU-20870 + Locale canonLocale = Locale::createCanonical(lang); + const char* canonLocId = canonLocale.getName(); + if (nameLength == UDISPCTX_LENGTH_SHORT) { + langData.getNoFallback("Languages%short", canonLocId, result); + if (!result.isBogus()) { + return adjustForUsageAndContext(kCapContextUsageLanguage, result); + } + } + langData.getNoFallback("Languages", canonLocId, result); + } + if (result.isBogus() && substitute == UDISPCTX_SUBSTITUTE) { + // use key, this is what langData.get (with fallback) falls back to. + result.setTo(UnicodeString(lang, -1, US_INV)); // use key ( + } + return adjustForUsageAndContext(kCapContextUsageLanguage, result); +} + +UnicodeString& +LocaleDisplayNamesImpl::scriptDisplayName(const char* script, + UnicodeString& result, + UBool skipAdjust) const { + if (nameLength == UDISPCTX_LENGTH_SHORT) { + langData.getNoFallback("Scripts%short", script, result); + if (!result.isBogus()) { + return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageScript, result); + } + } + if (substitute == UDISPCTX_SUBSTITUTE) { + langData.get("Scripts", script, result); + } else { + langData.getNoFallback("Scripts", script, result); + } + return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageScript, result); +} + +UnicodeString& +LocaleDisplayNamesImpl::scriptDisplayName(const char* script, + UnicodeString& result) const { + return scriptDisplayName(script, result, false); +} + +UnicodeString& +LocaleDisplayNamesImpl::scriptDisplayName(UScriptCode scriptCode, + UnicodeString& result) const { + return scriptDisplayName(uscript_getName(scriptCode), result, false); +} + +UnicodeString& +LocaleDisplayNamesImpl::regionDisplayName(const char* region, + UnicodeString& result, + UBool skipAdjust) const { + if (nameLength == UDISPCTX_LENGTH_SHORT) { + regionData.getNoFallback("Countries%short", region, result); + if (!result.isBogus()) { + return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageTerritory, result); + } + } + if (substitute == UDISPCTX_SUBSTITUTE) { + regionData.get("Countries", region, result); + } else { + regionData.getNoFallback("Countries", region, result); + } + return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageTerritory, result); +} + +UnicodeString& +LocaleDisplayNamesImpl::regionDisplayName(const char* region, + UnicodeString& result) const { + return regionDisplayName(region, result, false); +} + + +UnicodeString& +LocaleDisplayNamesImpl::variantDisplayName(const char* variant, + UnicodeString& result, + UBool skipAdjust) const { + // don't have a resource for short variant names + if (substitute == UDISPCTX_SUBSTITUTE) { + langData.get("Variants", variant, result); + } else { + langData.getNoFallback("Variants", variant, result); + } + return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageVariant, result); +} + +UnicodeString& +LocaleDisplayNamesImpl::variantDisplayName(const char* variant, + UnicodeString& result) const { + return variantDisplayName(variant, result, false); +} + +UnicodeString& +LocaleDisplayNamesImpl::keyDisplayName(const char* key, + UnicodeString& result, + UBool skipAdjust) const { + // don't have a resource for short key names + if (substitute == UDISPCTX_SUBSTITUTE) { + langData.get("Keys", key, result); + } else { + langData.getNoFallback("Keys", key, result); + } + return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKey, result); +} + +UnicodeString& +LocaleDisplayNamesImpl::keyDisplayName(const char* key, + UnicodeString& result) const { + return keyDisplayName(key, result, false); +} + +UnicodeString& +LocaleDisplayNamesImpl::keyValueDisplayName(const char* key, + const char* value, + UnicodeString& result, + UBool skipAdjust) const { + if (uprv_strcmp(key, "currency") == 0) { + // ICU4C does not have ICU4J CurrencyDisplayInfo equivalent for now. + UErrorCode sts = U_ZERO_ERROR; + UnicodeString ustrValue(value, -1, US_INV); + int32_t len; + const char16_t *currencyName = ucurr_getName(ustrValue.getTerminatedBuffer(), + locale.getBaseName(), UCURR_LONG_NAME, nullptr /* isChoiceFormat */, &len, &sts); + if (U_FAILURE(sts)) { + // Return the value as is on failure + result = ustrValue; + return result; + } + result.setTo(currencyName, len); + return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKeyValue, result); + } + + if (nameLength == UDISPCTX_LENGTH_SHORT) { + langData.getNoFallback("Types%short", key, value, result); + if (!result.isBogus()) { + return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKeyValue, result); + } + } + if (substitute == UDISPCTX_SUBSTITUTE) { + langData.get("Types", key, value, result); + } else { + langData.getNoFallback("Types", key, value, result); + } + return skipAdjust? result: adjustForUsageAndContext(kCapContextUsageKeyValue, result); +} + +UnicodeString& +LocaleDisplayNamesImpl::keyValueDisplayName(const char* key, + const char* value, + UnicodeString& result) const { + return keyValueDisplayName(key, value, result, false); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +LocaleDisplayNames* +LocaleDisplayNames::createInstance(const Locale& locale, + UDialectHandling dialectHandling) { + return new LocaleDisplayNamesImpl(locale, dialectHandling); +} + +LocaleDisplayNames* +LocaleDisplayNames::createInstance(const Locale& locale, + UDisplayContext *contexts, int32_t length) { + if (contexts == nullptr) { + length = 0; + } + return new LocaleDisplayNamesImpl(locale, contexts, length); +} + +U_NAMESPACE_END + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +U_NAMESPACE_USE + +U_CAPI ULocaleDisplayNames * U_EXPORT2 +uldn_open(const char * locale, + UDialectHandling dialectHandling, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return 0; + } + if (locale == nullptr) { + locale = uloc_getDefault(); + } + return (ULocaleDisplayNames *)LocaleDisplayNames::createInstance(Locale(locale), dialectHandling); +} + +U_CAPI ULocaleDisplayNames * U_EXPORT2 +uldn_openForContext(const char * locale, + UDisplayContext *contexts, int32_t length, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return 0; + } + if (locale == nullptr) { + locale = uloc_getDefault(); + } + return (ULocaleDisplayNames *)LocaleDisplayNames::createInstance(Locale(locale), contexts, length); +} + + +U_CAPI void U_EXPORT2 +uldn_close(ULocaleDisplayNames *ldn) { + delete (LocaleDisplayNames *)ldn; +} + +U_CAPI const char * U_EXPORT2 +uldn_getLocale(const ULocaleDisplayNames *ldn) { + if (ldn) { + return ((const LocaleDisplayNames *)ldn)->getLocale().getName(); + } + return nullptr; +} + +U_CAPI UDialectHandling U_EXPORT2 +uldn_getDialectHandling(const ULocaleDisplayNames *ldn) { + if (ldn) { + return ((const LocaleDisplayNames *)ldn)->getDialectHandling(); + } + return ULDN_STANDARD_NAMES; +} + +U_CAPI UDisplayContext U_EXPORT2 +uldn_getContext(const ULocaleDisplayNames *ldn, + UDisplayContextType type, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return (UDisplayContext)0; + } + return ((const LocaleDisplayNames *)ldn)->getContext(type); +} + +U_CAPI int32_t U_EXPORT2 +uldn_localeDisplayName(const ULocaleDisplayNames *ldn, + const char *locale, + char16_t *result, + int32_t maxResultSize, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return 0; + } + if (ldn == nullptr || locale == nullptr || (result == nullptr && maxResultSize > 0) || maxResultSize < 0) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString temp(result, 0, maxResultSize); + ((const LocaleDisplayNames *)ldn)->localeDisplayName(locale, temp); + if (temp.isBogus()) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + return temp.extract(result, maxResultSize, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uldn_languageDisplayName(const ULocaleDisplayNames *ldn, + const char *lang, + char16_t *result, + int32_t maxResultSize, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return 0; + } + if (ldn == nullptr || lang == nullptr || (result == nullptr && maxResultSize > 0) || maxResultSize < 0) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString temp(result, 0, maxResultSize); + ((const LocaleDisplayNames *)ldn)->languageDisplayName(lang, temp); + return temp.extract(result, maxResultSize, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uldn_scriptDisplayName(const ULocaleDisplayNames *ldn, + const char *script, + char16_t *result, + int32_t maxResultSize, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return 0; + } + if (ldn == nullptr || script == nullptr || (result == nullptr && maxResultSize > 0) || maxResultSize < 0) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString temp(result, 0, maxResultSize); + ((const LocaleDisplayNames *)ldn)->scriptDisplayName(script, temp); + return temp.extract(result, maxResultSize, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uldn_scriptCodeDisplayName(const ULocaleDisplayNames *ldn, + UScriptCode scriptCode, + char16_t *result, + int32_t maxResultSize, + UErrorCode *pErrorCode) { + return uldn_scriptDisplayName(ldn, uscript_getName(scriptCode), result, maxResultSize, pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uldn_regionDisplayName(const ULocaleDisplayNames *ldn, + const char *region, + char16_t *result, + int32_t maxResultSize, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return 0; + } + if (ldn == nullptr || region == nullptr || (result == nullptr && maxResultSize > 0) || maxResultSize < 0) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString temp(result, 0, maxResultSize); + ((const LocaleDisplayNames *)ldn)->regionDisplayName(region, temp); + return temp.extract(result, maxResultSize, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uldn_variantDisplayName(const ULocaleDisplayNames *ldn, + const char *variant, + char16_t *result, + int32_t maxResultSize, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return 0; + } + if (ldn == nullptr || variant == nullptr || (result == nullptr && maxResultSize > 0) || maxResultSize < 0) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString temp(result, 0, maxResultSize); + ((const LocaleDisplayNames *)ldn)->variantDisplayName(variant, temp); + return temp.extract(result, maxResultSize, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uldn_keyDisplayName(const ULocaleDisplayNames *ldn, + const char *key, + char16_t *result, + int32_t maxResultSize, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return 0; + } + if (ldn == nullptr || key == nullptr || (result == nullptr && maxResultSize > 0) || maxResultSize < 0) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString temp(result, 0, maxResultSize); + ((const LocaleDisplayNames *)ldn)->keyDisplayName(key, temp); + return temp.extract(result, maxResultSize, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uldn_keyValueDisplayName(const ULocaleDisplayNames *ldn, + const char *key, + const char *value, + char16_t *result, + int32_t maxResultSize, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return 0; + } + if (ldn == nullptr || key == nullptr || value == nullptr || (result == nullptr && maxResultSize > 0) + || maxResultSize < 0) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString temp(result, 0, maxResultSize); + ((const LocaleDisplayNames *)ldn)->keyValueDisplayName(key, value, temp); + return temp.extract(result, maxResultSize, *pErrorCode); +} + +#endif diff --git a/deps/icu-small/source/common/locid.cpp b/deps/icu-small/source/common/locid.cpp index 5cd083866c701b..4be2a3b4c97c7c 100644 --- a/deps/icu-small/source/common/locid.cpp +++ b/deps/icu-small/source/common/locid.cpp @@ -1,2734 +1,2734 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 1997-2016, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** -* -* File locid.cpp -* -* Created by: Richard Gillam -* -* Modification History: -* -* Date Name Description -* 02/11/97 aliu Changed gLocPath to fgDataDirectory and added -* methods to get and set it. -* 04/02/97 aliu Made operator!= inline; fixed return value -* of getName(). -* 04/15/97 aliu Cleanup for AIX/Win32. -* 04/24/97 aliu Numerous changes per code review. -* 08/18/98 stephen Changed getDisplayName() -* Added SIMPLIFIED_CHINESE, TRADITIONAL_CHINESE -* Added getISOCountries(), getISOLanguages(), -* getLanguagesForCountry() -* 03/16/99 bertrand rehaul. -* 07/21/99 stephen Added U_CFUNC setDefault -* 11/09/99 weiv Added const char * getName() const; -* 04/12/00 srl removing unicodestring api's and cached hash code -* 08/10/01 grhoten Change the static Locales to accessor functions -****************************************************************************** -*/ - -#include - -#include "unicode/bytestream.h" -#include "unicode/locid.h" -#include "unicode/localebuilder.h" -#include "unicode/strenum.h" -#include "unicode/stringpiece.h" -#include "unicode/uloc.h" -#include "unicode/ures.h" - -#include "bytesinkutil.h" -#include "charstr.h" -#include "charstrmap.h" -#include "cmemory.h" -#include "cstring.h" -#include "mutex.h" -#include "putilimp.h" -#include "uassert.h" -#include "ucln_cmn.h" -#include "uhash.h" -#include "ulocimp.h" -#include "umutex.h" -#include "uniquecharstr.h" -#include "ustr_imp.h" -#include "uvector.h" - -U_CDECL_BEGIN -static UBool U_CALLCONV locale_cleanup(void); -U_CDECL_END - -U_NAMESPACE_BEGIN - -static Locale *gLocaleCache = NULL; -static UInitOnce gLocaleCacheInitOnce {}; - -// gDefaultLocaleMutex protects all access to gDefaultLocalesHashT and gDefaultLocale. -static UMutex gDefaultLocaleMutex; -static UHashtable *gDefaultLocalesHashT = NULL; -static Locale *gDefaultLocale = NULL; - -/** - * \def ULOC_STRING_LIMIT - * strings beyond this value crash in CharString - */ -#define ULOC_STRING_LIMIT 357913941 - -U_NAMESPACE_END - -typedef enum ELocalePos { - eENGLISH, - eFRENCH, - eGERMAN, - eITALIAN, - eJAPANESE, - eKOREAN, - eCHINESE, - - eFRANCE, - eGERMANY, - eITALY, - eJAPAN, - eKOREA, - eCHINA, /* Alias for PRC */ - eTAIWAN, - eUK, - eUS, - eCANADA, - eCANADA_FRENCH, - eROOT, - - - //eDEFAULT, - eMAX_LOCALES -} ELocalePos; - -U_CDECL_BEGIN -// -// Deleter function for Locales owned by the default Locale hash table/ -// -static void U_CALLCONV -deleteLocale(void *obj) { - delete (icu::Locale *) obj; -} - -static UBool U_CALLCONV locale_cleanup(void) -{ - U_NAMESPACE_USE - - delete [] gLocaleCache; - gLocaleCache = NULL; - gLocaleCacheInitOnce.reset(); - - if (gDefaultLocalesHashT) { - uhash_close(gDefaultLocalesHashT); // Automatically deletes all elements, using deleter func. - gDefaultLocalesHashT = NULL; - } - gDefaultLocale = NULL; - return true; -} - - -static void U_CALLCONV locale_init(UErrorCode &status) { - U_NAMESPACE_USE - - U_ASSERT(gLocaleCache == NULL); - gLocaleCache = new Locale[(int)eMAX_LOCALES]; - if (gLocaleCache == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); - gLocaleCache[eROOT] = Locale(""); - gLocaleCache[eENGLISH] = Locale("en"); - gLocaleCache[eFRENCH] = Locale("fr"); - gLocaleCache[eGERMAN] = Locale("de"); - gLocaleCache[eITALIAN] = Locale("it"); - gLocaleCache[eJAPANESE] = Locale("ja"); - gLocaleCache[eKOREAN] = Locale("ko"); - gLocaleCache[eCHINESE] = Locale("zh"); - gLocaleCache[eFRANCE] = Locale("fr", "FR"); - gLocaleCache[eGERMANY] = Locale("de", "DE"); - gLocaleCache[eITALY] = Locale("it", "IT"); - gLocaleCache[eJAPAN] = Locale("ja", "JP"); - gLocaleCache[eKOREA] = Locale("ko", "KR"); - gLocaleCache[eCHINA] = Locale("zh", "CN"); - gLocaleCache[eTAIWAN] = Locale("zh", "TW"); - gLocaleCache[eUK] = Locale("en", "GB"); - gLocaleCache[eUS] = Locale("en", "US"); - gLocaleCache[eCANADA] = Locale("en", "CA"); - gLocaleCache[eCANADA_FRENCH] = Locale("fr", "CA"); -} - -U_CDECL_END - -U_NAMESPACE_BEGIN - -Locale *locale_set_default_internal(const char *id, UErrorCode& status) { - // Synchronize this entire function. - Mutex lock(&gDefaultLocaleMutex); - - UBool canonicalize = false; - - // If given a NULL string for the locale id, grab the default - // name from the system. - // (Different from most other locale APIs, where a null name means use - // the current ICU default locale.) - if (id == NULL) { - id = uprv_getDefaultLocaleID(); // This function not thread safe? TODO: verify. - canonicalize = true; // always canonicalize host ID - } - - CharString localeNameBuf; - { - CharStringByteSink sink(&localeNameBuf); - if (canonicalize) { - ulocimp_canonicalize(id, sink, &status); - } else { - ulocimp_getName(id, sink, &status); - } - } - - if (U_FAILURE(status)) { - return gDefaultLocale; - } - - if (gDefaultLocalesHashT == NULL) { - gDefaultLocalesHashT = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); - if (U_FAILURE(status)) { - return gDefaultLocale; - } - uhash_setValueDeleter(gDefaultLocalesHashT, deleteLocale); - ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); - } - - Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf.data()); - if (newDefault == NULL) { - newDefault = new Locale(Locale::eBOGUS); - if (newDefault == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return gDefaultLocale; - } - newDefault->init(localeNameBuf.data(), false); - uhash_put(gDefaultLocalesHashT, (char*) newDefault->getName(), newDefault, &status); - if (U_FAILURE(status)) { - return gDefaultLocale; - } - } - gDefaultLocale = newDefault; - return gDefaultLocale; -} - -U_NAMESPACE_END - -/* sfb 07/21/99 */ -U_CFUNC void -locale_set_default(const char *id) -{ - U_NAMESPACE_USE - UErrorCode status = U_ZERO_ERROR; - locale_set_default_internal(id, status); -} -/* end */ - -U_CFUNC const char * -locale_get_default(void) -{ - U_NAMESPACE_USE - return Locale::getDefault().getName(); -} - - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale) - -/*Character separating the posix id fields*/ -// '_' -// In the platform codepage. -#define SEP_CHAR '_' -#define NULL_CHAR '\0' - -Locale::~Locale() -{ - if ((baseName != fullName) && (baseName != fullNameBuffer)) { - uprv_free(baseName); - } - baseName = NULL; - /*if fullName is on the heap, we free it*/ - if (fullName != fullNameBuffer) - { - uprv_free(fullName); - fullName = NULL; - } -} - -Locale::Locale() - : UObject(), fullName(fullNameBuffer), baseName(NULL) -{ - init(NULL, false); -} - -/* - * Internal constructor to allow construction of a locale object with - * NO side effects. (Default constructor tries to get - * the default locale.) - */ -Locale::Locale(Locale::ELocaleType) - : UObject(), fullName(fullNameBuffer), baseName(NULL) -{ - setToBogus(); -} - - -Locale::Locale( const char * newLanguage, - const char * newCountry, - const char * newVariant, - const char * newKeywords) - : UObject(), fullName(fullNameBuffer), baseName(NULL) -{ - if( (newLanguage==NULL) && (newCountry == NULL) && (newVariant == NULL) ) - { - init(NULL, false); /* shortcut */ - } - else - { - UErrorCode status = U_ZERO_ERROR; - int32_t lsize = 0; - int32_t csize = 0; - int32_t vsize = 0; - int32_t ksize = 0; - - // Check the sizes of the input strings. - - // Language - if ( newLanguage != NULL ) - { - lsize = (int32_t)uprv_strlen(newLanguage); - if ( lsize < 0 || lsize > ULOC_STRING_LIMIT ) { // int32 wrap - setToBogus(); - return; - } - } - - CharString togo(newLanguage, lsize, status); // start with newLanguage - - // _Country - if ( newCountry != NULL ) - { - csize = (int32_t)uprv_strlen(newCountry); - if ( csize < 0 || csize > ULOC_STRING_LIMIT ) { // int32 wrap - setToBogus(); - return; - } - } - - // _Variant - if ( newVariant != NULL ) - { - // remove leading _'s - while(newVariant[0] == SEP_CHAR) - { - newVariant++; - } - - // remove trailing _'s - vsize = (int32_t)uprv_strlen(newVariant); - if ( vsize < 0 || vsize > ULOC_STRING_LIMIT ) { // int32 wrap - setToBogus(); - return; - } - while( (vsize>1) && (newVariant[vsize-1] == SEP_CHAR) ) - { - vsize--; - } - } - - if ( newKeywords != NULL) - { - ksize = (int32_t)uprv_strlen(newKeywords); - if ( ksize < 0 || ksize > ULOC_STRING_LIMIT ) { - setToBogus(); - return; - } - } - - // We've checked the input sizes, now build up the full locale string.. - - // newLanguage is already copied - - if ( ( vsize != 0 ) || (csize != 0) ) // at least: __v - { // ^ - togo.append(SEP_CHAR, status); - } - - if ( csize != 0 ) - { - togo.append(newCountry, status); - } - - if ( vsize != 0) - { - togo.append(SEP_CHAR, status) - .append(newVariant, vsize, status); - } - - if ( ksize != 0) - { - if (uprv_strchr(newKeywords, '=')) { - togo.append('@', status); /* keyword parsing */ - } - else { - togo.append('_', status); /* Variant parsing with a script */ - if ( vsize == 0) { - togo.append('_', status); /* No country found */ - } - } - togo.append(newKeywords, status); - } - - if (U_FAILURE(status)) { - // Something went wrong with appending, etc. - setToBogus(); - return; - } - // Parse it, because for example 'language' might really be a complete - // string. - init(togo.data(), false); - } -} - -Locale::Locale(const Locale &other) - : UObject(other), fullName(fullNameBuffer), baseName(NULL) -{ - *this = other; -} - -Locale::Locale(Locale&& other) U_NOEXCEPT - : UObject(other), fullName(fullNameBuffer), baseName(fullName) { - *this = std::move(other); -} - -Locale& Locale::operator=(const Locale& other) { - if (this == &other) { - return *this; - } - - setToBogus(); - - if (other.fullName == other.fullNameBuffer) { - uprv_strcpy(fullNameBuffer, other.fullNameBuffer); - } else if (other.fullName == nullptr) { - fullName = nullptr; - } else { - fullName = uprv_strdup(other.fullName); - if (fullName == nullptr) return *this; - } - - if (other.baseName == other.fullName) { - baseName = fullName; - } else if (other.baseName != nullptr) { - baseName = uprv_strdup(other.baseName); - if (baseName == nullptr) return *this; - } - - uprv_strcpy(language, other.language); - uprv_strcpy(script, other.script); - uprv_strcpy(country, other.country); - - variantBegin = other.variantBegin; - fIsBogus = other.fIsBogus; - - return *this; -} - -Locale& Locale::operator=(Locale&& other) U_NOEXCEPT { - if ((baseName != fullName) && (baseName != fullNameBuffer)) uprv_free(baseName); - if (fullName != fullNameBuffer) uprv_free(fullName); - - if (other.fullName == other.fullNameBuffer || other.baseName == other.fullNameBuffer) { - uprv_strcpy(fullNameBuffer, other.fullNameBuffer); - } - if (other.fullName == other.fullNameBuffer) { - fullName = fullNameBuffer; - } else { - fullName = other.fullName; - } - - if (other.baseName == other.fullNameBuffer) { - baseName = fullNameBuffer; - } else if (other.baseName == other.fullName) { - baseName = fullName; - } else { - baseName = other.baseName; - } - - uprv_strcpy(language, other.language); - uprv_strcpy(script, other.script); - uprv_strcpy(country, other.country); - - variantBegin = other.variantBegin; - fIsBogus = other.fIsBogus; - - other.baseName = other.fullName = other.fullNameBuffer; - - return *this; -} - -Locale * -Locale::clone() const { - return new Locale(*this); -} - -bool -Locale::operator==( const Locale& other) const -{ - return (uprv_strcmp(other.fullName, fullName) == 0); -} - -namespace { - -UInitOnce gKnownCanonicalizedInitOnce {}; -UHashtable *gKnownCanonicalized = nullptr; - -static const char* const KNOWN_CANONICALIZED[] = { - "c", - // Commonly used locales known are already canonicalized - "af", "af_ZA", "am", "am_ET", "ar", "ar_001", "as", "as_IN", "az", "az_AZ", - "be", "be_BY", "bg", "bg_BG", "bn", "bn_IN", "bs", "bs_BA", "ca", "ca_ES", - "cs", "cs_CZ", "cy", "cy_GB", "da", "da_DK", "de", "de_DE", "el", "el_GR", - "en", "en_GB", "en_US", "es", "es_419", "es_ES", "et", "et_EE", "eu", - "eu_ES", "fa", "fa_IR", "fi", "fi_FI", "fil", "fil_PH", "fr", "fr_FR", - "ga", "ga_IE", "gl", "gl_ES", "gu", "gu_IN", "he", "he_IL", "hi", "hi_IN", - "hr", "hr_HR", "hu", "hu_HU", "hy", "hy_AM", "id", "id_ID", "is", "is_IS", - "it", "it_IT", "ja", "ja_JP", "jv", "jv_ID", "ka", "ka_GE", "kk", "kk_KZ", - "km", "km_KH", "kn", "kn_IN", "ko", "ko_KR", "ky", "ky_KG", "lo", "lo_LA", - "lt", "lt_LT", "lv", "lv_LV", "mk", "mk_MK", "ml", "ml_IN", "mn", "mn_MN", - "mr", "mr_IN", "ms", "ms_MY", "my", "my_MM", "nb", "nb_NO", "ne", "ne_NP", - "nl", "nl_NL", "no", "or", "or_IN", "pa", "pa_IN", "pl", "pl_PL", "ps", "ps_AF", - "pt", "pt_BR", "pt_PT", "ro", "ro_RO", "ru", "ru_RU", "sd", "sd_IN", "si", - "si_LK", "sk", "sk_SK", "sl", "sl_SI", "so", "so_SO", "sq", "sq_AL", "sr", - "sr_Cyrl_RS", "sr_Latn", "sr_RS", "sv", "sv_SE", "sw", "sw_TZ", "ta", - "ta_IN", "te", "te_IN", "th", "th_TH", "tk", "tk_TM", "tr", "tr_TR", "uk", - "uk_UA", "ur", "ur_PK", "uz", "uz_UZ", "vi", "vi_VN", "yue", "yue_Hant", - "yue_Hant_HK", "yue_HK", "zh", "zh_CN", "zh_Hans", "zh_Hans_CN", "zh_Hant", - "zh_Hant_TW", "zh_TW", "zu", "zu_ZA" -}; - -static UBool U_CALLCONV cleanupKnownCanonicalized() { - gKnownCanonicalizedInitOnce.reset(); - if (gKnownCanonicalized) { uhash_close(gKnownCanonicalized); } - return true; -} - -static void U_CALLCONV loadKnownCanonicalized(UErrorCode &status) { - ucln_common_registerCleanup(UCLN_COMMON_LOCALE_KNOWN_CANONICALIZED, - cleanupKnownCanonicalized); - LocalUHashtablePointer newKnownCanonicalizedMap( - uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status)); - for (int32_t i = 0; - U_SUCCESS(status) && i < UPRV_LENGTHOF(KNOWN_CANONICALIZED); - i++) { - uhash_puti(newKnownCanonicalizedMap.getAlias(), - (void*)KNOWN_CANONICALIZED[i], - 1, &status); - } - if (U_FAILURE(status)) { - return; - } - - gKnownCanonicalized = newKnownCanonicalizedMap.orphan(); -} - -class AliasData; - -/** - * A Builder class to build the alias data. - */ -class AliasDataBuilder { -public: - AliasDataBuilder() { - } - - // Build the AliasData from resource. - AliasData* build(UErrorCode &status); - -private: - void readAlias(UResourceBundle* alias, - UniqueCharStrings* strings, - LocalMemory& types, - LocalMemory& replacementIndexes, - int32_t &length, - void (*checkType)(const char* type), - void (*checkReplacement)(const UnicodeString& replacement), - UErrorCode &status); - - // Read the languageAlias data from alias to - // strings+types+replacementIndexes - // The number of record will be stored into length. - // Allocate length items for types, to store the type field. - // Allocate length items for replacementIndexes, - // to store the index in the strings for the replacement script. - void readLanguageAlias(UResourceBundle* alias, - UniqueCharStrings* strings, - LocalMemory& types, - LocalMemory& replacementIndexes, - int32_t &length, - UErrorCode &status); - - // Read the scriptAlias data from alias to - // strings+types+replacementIndexes - // Allocate length items for types, to store the type field. - // Allocate length items for replacementIndexes, - // to store the index in the strings for the replacement script. - void readScriptAlias(UResourceBundle* alias, - UniqueCharStrings* strings, - LocalMemory& types, - LocalMemory& replacementIndexes, - int32_t &length, UErrorCode &status); - - // Read the territoryAlias data from alias to - // strings+types+replacementIndexes - // Allocate length items for types, to store the type field. - // Allocate length items for replacementIndexes, - // to store the index in the strings for the replacement script. - void readTerritoryAlias(UResourceBundle* alias, - UniqueCharStrings* strings, - LocalMemory& types, - LocalMemory& replacementIndexes, - int32_t &length, UErrorCode &status); - - // Read the variantAlias data from alias to - // strings+types+replacementIndexes - // Allocate length items for types, to store the type field. - // Allocate length items for replacementIndexes, - // to store the index in the strings for the replacement variant. - void readVariantAlias(UResourceBundle* alias, - UniqueCharStrings* strings, - LocalMemory& types, - LocalMemory& replacementIndexes, - int32_t &length, UErrorCode &status); - - // Read the subdivisionAlias data from alias to - // strings+types+replacementIndexes - // Allocate length items for types, to store the type field. - // Allocate length items for replacementIndexes, - // to store the index in the strings for the replacement variant. - void readSubdivisionAlias(UResourceBundle* alias, - UniqueCharStrings* strings, - LocalMemory& types, - LocalMemory& replacementIndexes, - int32_t &length, UErrorCode &status); -}; - -/** - * A class to hold the Alias Data. - */ -class AliasData : public UMemory { -public: - static const AliasData* singleton(UErrorCode& status) { - if (U_FAILURE(status)) { - // Do not get into loadData if the status already has error. - return nullptr; - } - umtx_initOnce(AliasData::gInitOnce, &AliasData::loadData, status); - return gSingleton; - } - - const CharStringMap& languageMap() const { return language; } - const CharStringMap& scriptMap() const { return script; } - const CharStringMap& territoryMap() const { return territory; } - const CharStringMap& variantMap() const { return variant; } - const CharStringMap& subdivisionMap() const { return subdivision; } - - static void U_CALLCONV loadData(UErrorCode &status); - static UBool U_CALLCONV cleanup(); - - static UInitOnce gInitOnce; - -private: - AliasData(CharStringMap languageMap, - CharStringMap scriptMap, - CharStringMap territoryMap, - CharStringMap variantMap, - CharStringMap subdivisionMap, - CharString* strings) - : language(std::move(languageMap)), - script(std::move(scriptMap)), - territory(std::move(territoryMap)), - variant(std::move(variantMap)), - subdivision(std::move(subdivisionMap)), - strings(strings) { - } - - ~AliasData() { - delete strings; - } - - static const AliasData* gSingleton; - - CharStringMap language; - CharStringMap script; - CharStringMap territory; - CharStringMap variant; - CharStringMap subdivision; - CharString* strings; - - friend class AliasDataBuilder; -}; - - -const AliasData* AliasData::gSingleton = nullptr; -UInitOnce AliasData::gInitOnce {}; - -UBool U_CALLCONV -AliasData::cleanup() -{ - gInitOnce.reset(); - delete gSingleton; - return true; -} - -void -AliasDataBuilder::readAlias( - UResourceBundle* alias, - UniqueCharStrings* strings, - LocalMemory& types, - LocalMemory& replacementIndexes, - int32_t &length, - void (*checkType)(const char* type), - void (*checkReplacement)(const UnicodeString& replacement), - UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - length = ures_getSize(alias); - const char** rawTypes = types.allocateInsteadAndCopy(length); - if (rawTypes == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - int32_t* rawIndexes = replacementIndexes.allocateInsteadAndCopy(length); - if (rawIndexes == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - for (int i = 0; U_SUCCESS(status) && ures_hasNext(alias); i++) { - LocalUResourceBundlePointer res( - ures_getNextResource(alias, nullptr, &status)); - const char* aliasFrom = ures_getKey(res.getAlias()); - UnicodeString aliasTo = - ures_getUnicodeStringByKey(res.getAlias(), "replacement", &status); - if (U_FAILURE(status)) return; - - checkType(aliasFrom); - checkReplacement(aliasTo); - - rawTypes[i] = aliasFrom; - rawIndexes[i] = strings->add(aliasTo, status); - } -} - -/** - * Read the languageAlias data from alias to strings+types+replacementIndexes. - * Allocate length items for types, to store the type field. Allocate length - * items for replacementIndexes, to store the index in the strings for the - * replacement language. - */ -void -AliasDataBuilder::readLanguageAlias( - UResourceBundle* alias, - UniqueCharStrings* strings, - LocalMemory& types, - LocalMemory& replacementIndexes, - int32_t &length, - UErrorCode &status) -{ - return readAlias( - alias, strings, types, replacementIndexes, length, -#if U_DEBUG - [](const char* type) { - // Assert the aliasFrom only contains the following possibilities - // language_REGION_variant - // language_REGION - // language_variant - // language - // und_variant - Locale test(type); - // Assert no script in aliasFrom - U_ASSERT(test.getScript()[0] == '\0'); - // Assert when language is und, no REGION in aliasFrom. - U_ASSERT(test.getLanguage()[0] != '\0' || test.getCountry()[0] == '\0'); - }, -#else - [](const char*) {}, -#endif - [](const UnicodeString&) {}, status); -} - -/** - * Read the scriptAlias data from alias to strings+types+replacementIndexes. - * Allocate length items for types, to store the type field. Allocate length - * items for replacementIndexes, to store the index in the strings for the - * replacement script. - */ -void -AliasDataBuilder::readScriptAlias( - UResourceBundle* alias, - UniqueCharStrings* strings, - LocalMemory& types, - LocalMemory& replacementIndexes, - int32_t &length, - UErrorCode &status) -{ - return readAlias( - alias, strings, types, replacementIndexes, length, -#if U_DEBUG - [](const char* type) { - U_ASSERT(uprv_strlen(type) == 4); - }, - [](const UnicodeString& replacement) { - U_ASSERT(replacement.length() == 4); - }, -#else - [](const char*) {}, - [](const UnicodeString&) { }, -#endif - status); -} - -/** - * Read the territoryAlias data from alias to strings+types+replacementIndexes. - * Allocate length items for types, to store the type field. Allocate length - * items for replacementIndexes, to store the index in the strings for the - * replacement regions. - */ -void -AliasDataBuilder::readTerritoryAlias( - UResourceBundle* alias, - UniqueCharStrings* strings, - LocalMemory& types, - LocalMemory& replacementIndexes, - int32_t &length, - UErrorCode &status) -{ - return readAlias( - alias, strings, types, replacementIndexes, length, -#if U_DEBUG - [](const char* type) { - U_ASSERT(uprv_strlen(type) == 2 || uprv_strlen(type) == 3); - }, -#else - [](const char*) {}, -#endif - [](const UnicodeString&) { }, - status); -} - -/** - * Read the variantAlias data from alias to strings+types+replacementIndexes. - * Allocate length items for types, to store the type field. Allocate length - * items for replacementIndexes, to store the index in the strings for the - * replacement variant. - */ -void -AliasDataBuilder::readVariantAlias( - UResourceBundle* alias, - UniqueCharStrings* strings, - LocalMemory& types, - LocalMemory& replacementIndexes, - int32_t &length, - UErrorCode &status) -{ - return readAlias( - alias, strings, types, replacementIndexes, length, -#if U_DEBUG - [](const char* type) { - U_ASSERT(uprv_strlen(type) >= 4 && uprv_strlen(type) <= 8); - U_ASSERT(uprv_strlen(type) != 4 || - (type[0] >= '0' && type[0] <= '9')); - }, - [](const UnicodeString& replacement) { - U_ASSERT(replacement.length() >= 4 && replacement.length() <= 8); - U_ASSERT(replacement.length() != 4 || - (replacement.charAt(0) >= u'0' && - replacement.charAt(0) <= u'9')); - }, -#else - [](const char*) {}, - [](const UnicodeString&) { }, -#endif - status); -} - -/** - * Read the subdivisionAlias data from alias to strings+types+replacementIndexes. - * Allocate length items for types, to store the type field. Allocate length - * items for replacementIndexes, to store the index in the strings for the - * replacement regions. - */ -void -AliasDataBuilder::readSubdivisionAlias( - UResourceBundle* alias, - UniqueCharStrings* strings, - LocalMemory& types, - LocalMemory& replacementIndexes, - int32_t &length, - UErrorCode &status) -{ - return readAlias( - alias, strings, types, replacementIndexes, length, -#if U_DEBUG - [](const char* type) { - U_ASSERT(uprv_strlen(type) >= 3 && uprv_strlen(type) <= 8); - }, -#else - [](const char*) {}, -#endif - [](const UnicodeString&) { }, - status); -} - -/** - * Initializes the alias data from the ICU resource bundles. The alias data - * contains alias of language, country, script and variants. - * - * If the alias data has already loaded, then this method simply returns without - * doing anything meaningful. - */ -void U_CALLCONV -AliasData::loadData(UErrorCode &status) -{ -#ifdef LOCALE_CANONICALIZATION_DEBUG - UDate start = uprv_getRawUTCtime(); -#endif // LOCALE_CANONICALIZATION_DEBUG - ucln_common_registerCleanup(UCLN_COMMON_LOCALE_ALIAS, cleanup); - AliasDataBuilder builder; - gSingleton = builder.build(status); -#ifdef LOCALE_CANONICALIZATION_DEBUG - UDate end = uprv_getRawUTCtime(); - printf("AliasData::loadData took total %f ms\n", end - start); -#endif // LOCALE_CANONICALIZATION_DEBUG -} - -/** - * Build the alias data from resources. - */ -AliasData* -AliasDataBuilder::build(UErrorCode &status) { - LocalUResourceBundlePointer metadata( - ures_openDirect(nullptr, "metadata", &status)); - LocalUResourceBundlePointer metadataAlias( - ures_getByKey(metadata.getAlias(), "alias", nullptr, &status)); - LocalUResourceBundlePointer languageAlias( - ures_getByKey(metadataAlias.getAlias(), "language", nullptr, &status)); - LocalUResourceBundlePointer scriptAlias( - ures_getByKey(metadataAlias.getAlias(), "script", nullptr, &status)); - LocalUResourceBundlePointer territoryAlias( - ures_getByKey(metadataAlias.getAlias(), "territory", nullptr, &status)); - LocalUResourceBundlePointer variantAlias( - ures_getByKey(metadataAlias.getAlias(), "variant", nullptr, &status)); - LocalUResourceBundlePointer subdivisionAlias( - ures_getByKey(metadataAlias.getAlias(), "subdivision", nullptr, &status)); - - if (U_FAILURE(status)) { - return nullptr; - } - int32_t languagesLength = 0, scriptLength = 0, territoryLength = 0, - variantLength = 0, subdivisionLength = 0; - - // Read the languageAlias into languageTypes, languageReplacementIndexes - // and strings - UniqueCharStrings strings(status); - LocalMemory languageTypes; - LocalMemory languageReplacementIndexes; - readLanguageAlias(languageAlias.getAlias(), - &strings, - languageTypes, - languageReplacementIndexes, - languagesLength, - status); - - // Read the scriptAlias into scriptTypes, scriptReplacementIndexes - // and strings - LocalMemory scriptTypes; - LocalMemory scriptReplacementIndexes; - readScriptAlias(scriptAlias.getAlias(), - &strings, - scriptTypes, - scriptReplacementIndexes, - scriptLength, - status); - - // Read the territoryAlias into territoryTypes, territoryReplacementIndexes - // and strings - LocalMemory territoryTypes; - LocalMemory territoryReplacementIndexes; - readTerritoryAlias(territoryAlias.getAlias(), - &strings, - territoryTypes, - territoryReplacementIndexes, - territoryLength, status); - - // Read the variantAlias into variantTypes, variantReplacementIndexes - // and strings - LocalMemory variantTypes; - LocalMemory variantReplacementIndexes; - readVariantAlias(variantAlias.getAlias(), - &strings, - variantTypes, - variantReplacementIndexes, - variantLength, status); - - // Read the subdivisionAlias into subdivisionTypes, subdivisionReplacementIndexes - // and strings - LocalMemory subdivisionTypes; - LocalMemory subdivisionReplacementIndexes; - readSubdivisionAlias(subdivisionAlias.getAlias(), - &strings, - subdivisionTypes, - subdivisionReplacementIndexes, - subdivisionLength, status); - - if (U_FAILURE(status)) { - return nullptr; - } - - // We can only use strings after freeze it. - strings.freeze(); - - // Build the languageMap from languageTypes & languageReplacementIndexes - CharStringMap languageMap(490, status); - for (int32_t i = 0; U_SUCCESS(status) && i < languagesLength; i++) { - languageMap.put(languageTypes[i], - strings.get(languageReplacementIndexes[i]), - status); - } - - // Build the scriptMap from scriptTypes & scriptReplacementIndexes - CharStringMap scriptMap(1, status); - for (int32_t i = 0; U_SUCCESS(status) && i < scriptLength; i++) { - scriptMap.put(scriptTypes[i], - strings.get(scriptReplacementIndexes[i]), - status); - } - - // Build the territoryMap from territoryTypes & territoryReplacementIndexes - CharStringMap territoryMap(650, status); - for (int32_t i = 0; U_SUCCESS(status) && i < territoryLength; i++) { - territoryMap.put(territoryTypes[i], - strings.get(territoryReplacementIndexes[i]), - status); - } - - // Build the variantMap from variantTypes & variantReplacementIndexes. - CharStringMap variantMap(2, status); - for (int32_t i = 0; U_SUCCESS(status) && i < variantLength; i++) { - variantMap.put(variantTypes[i], - strings.get(variantReplacementIndexes[i]), - status); - } - - // Build the subdivisionMap from subdivisionTypes & subdivisionReplacementIndexes. - CharStringMap subdivisionMap(2, status); - for (int32_t i = 0; U_SUCCESS(status) && i < subdivisionLength; i++) { - subdivisionMap.put(subdivisionTypes[i], - strings.get(subdivisionReplacementIndexes[i]), - status); - } - - if (U_FAILURE(status)) { - return nullptr; - } - - // copy hashtables - auto *data = new AliasData( - std::move(languageMap), - std::move(scriptMap), - std::move(territoryMap), - std::move(variantMap), - std::move(subdivisionMap), - strings.orphanCharStrings()); - - if (data == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return data; -} - -/** - * A class that find the replacement values of locale fields by using AliasData. - */ -class AliasReplacer { -public: - AliasReplacer(UErrorCode status) : - language(nullptr), script(nullptr), region(nullptr), - extensions(nullptr), variants(status), - data(nullptr) { - } - ~AliasReplacer() { - } - - // Check the fields inside locale, if need to replace fields, - // place the the replaced locale ID in out and return true. - // Otherwise return false for no replacement or error. - bool replace( - const Locale& locale, CharString& out, UErrorCode& status); - -private: - const char* language; - const char* script; - const char* region; - const char* extensions; - UVector variants; - - const AliasData* data; - - inline bool notEmpty(const char* str) { - return str && str[0] != NULL_CHAR; - } - - /** - * If replacement is neither null nor empty and input is either null or empty, - * return replacement. - * If replacement is neither null nor empty but input is not empty, return input. - * If replacement is either null or empty and type is either null or empty, - * return input. - * Otherwise return null. - * replacement input type return - * AAA nullptr * AAA - * AAA BBB * BBB - * nullptr || "" CCC nullptr CCC - * nullptr || "" * DDD nullptr - */ - inline const char* deleteOrReplace( - const char* input, const char* type, const char* replacement) { - return notEmpty(replacement) ? - ((input == nullptr) ? replacement : input) : - ((type == nullptr) ? input : nullptr); - } - - inline bool same(const char* a, const char* b) { - if (a == nullptr && b == nullptr) { - return true; - } - if ((a == nullptr && b != nullptr) || - (a != nullptr && b == nullptr)) { - return false; - } - return uprv_strcmp(a, b) == 0; - } - - // Gather fields and generate locale ID into out. - CharString& outputToString(CharString& out, UErrorCode status); - - // Generate the lookup key. - CharString& generateKey(const char* language, const char* region, - const char* variant, CharString& out, - UErrorCode status); - - void parseLanguageReplacement(const char* replacement, - const char*& replaceLanguage, - const char*& replaceScript, - const char*& replaceRegion, - const char*& replaceVariant, - const char*& replaceExtensions, - UVector& toBeFreed, - UErrorCode& status); - - // Replace by using languageAlias. - bool replaceLanguage(bool checkLanguage, bool checkRegion, - bool checkVariants, UVector& toBeFreed, - UErrorCode& status); - - // Replace by using territoryAlias. - bool replaceTerritory(UVector& toBeFreed, UErrorCode& status); - - // Replace by using scriptAlias. - bool replaceScript(UErrorCode& status); - - // Replace by using variantAlias. - bool replaceVariant(UErrorCode& status); - - // Replace by using subdivisionAlias. - bool replaceSubdivision(StringPiece subdivision, - CharString& output, UErrorCode& status); - - // Replace transformed extensions. - bool replaceTransformedExtensions( - CharString& transformedExtensions, CharString& output, UErrorCode& status); -}; - -CharString& -AliasReplacer::generateKey( - const char* language, const char* region, const char* variant, - CharString& out, UErrorCode status) -{ - out.append(language, status); - if (notEmpty(region)) { - out.append(SEP_CHAR, status) - .append(region, status); - } - if (notEmpty(variant)) { - out.append(SEP_CHAR, status) - .append(variant, status); - } - return out; -} - -void -AliasReplacer::parseLanguageReplacement( - const char* replacement, - const char*& replacedLanguage, - const char*& replacedScript, - const char*& replacedRegion, - const char*& replacedVariant, - const char*& replacedExtensions, - UVector& toBeFreed, - UErrorCode& status) -{ - if (U_FAILURE(status)) { - return; - } - replacedScript = replacedRegion = replacedVariant - = replacedExtensions = nullptr; - if (uprv_strchr(replacement, '_') == nullptr) { - replacedLanguage = replacement; - // reach the end, just return it. - return; - } - // We have multiple field so we have to allocate and parse - CharString* str = new CharString( - replacement, (int32_t)uprv_strlen(replacement), status); - LocalPointer lpStr(str, status); - toBeFreed.adoptElement(lpStr.orphan(), status); - if (U_FAILURE(status)) { - return; - } - char* data = str->data(); - replacedLanguage = (const char*) data; - char* endOfField = uprv_strchr(data, '_'); - *endOfField = '\0'; // null terminiate it. - endOfField++; - const char* start = endOfField; - endOfField = (char*) uprv_strchr(start, '_'); - size_t len = 0; - if (endOfField == nullptr) { - len = uprv_strlen(start); - } else { - len = endOfField - start; - *endOfField = '\0'; // null terminiate it. - } - if (len == 4 && uprv_isASCIILetter(*start)) { - // Got a script - replacedScript = start; - if (endOfField == nullptr) { - return; - } - start = endOfField++; - endOfField = (char*)uprv_strchr(start, '_'); - if (endOfField == nullptr) { - len = uprv_strlen(start); - } else { - len = endOfField - start; - *endOfField = '\0'; // null terminiate it. - } - } - if (len >= 2 && len <= 3) { - // Got a region - replacedRegion = start; - if (endOfField == nullptr) { - return; - } - start = endOfField++; - endOfField = (char*)uprv_strchr(start, '_'); - if (endOfField == nullptr) { - len = uprv_strlen(start); - } else { - len = endOfField - start; - *endOfField = '\0'; // null terminiate it. - } - } - if (len >= 4) { - // Got a variant - replacedVariant = start; - if (endOfField == nullptr) { - return; - } - start = endOfField++; - } - replacedExtensions = start; -} - -bool -AliasReplacer::replaceLanguage( - bool checkLanguage, bool checkRegion, - bool checkVariants, UVector& toBeFreed, UErrorCode& status) -{ - if (U_FAILURE(status)) { - return false; - } - if ( (checkRegion && region == nullptr) || - (checkVariants && variants.size() == 0)) { - // Nothing to search. - return false; - } - int32_t variant_size = checkVariants ? variants.size() : 1; - // Since we may have more than one variant, we need to loop through them. - const char* searchLanguage = checkLanguage ? language : "und"; - const char* searchRegion = checkRegion ? region : nullptr; - const char* searchVariant = nullptr; - for (int32_t variant_index = 0; - variant_index < variant_size; - variant_index++) { - if (checkVariants) { - U_ASSERT(variant_index < variant_size); - searchVariant = (const char*)(variants.elementAt(variant_index)); - } - - if (searchVariant != nullptr && uprv_strlen(searchVariant) < 4) { - // Do not consider ill-formed variant subtag. - searchVariant = nullptr; - } - CharString typeKey; - generateKey(searchLanguage, searchRegion, searchVariant, typeKey, - status); - if (U_FAILURE(status)) { - return false; - } - const char *replacement = data->languageMap().get(typeKey.data()); - if (replacement == nullptr) { - // Found no replacement data. - continue; - } - - const char* replacedLanguage = nullptr; - const char* replacedScript = nullptr; - const char* replacedRegion = nullptr; - const char* replacedVariant = nullptr; - const char* replacedExtensions = nullptr; - parseLanguageReplacement(replacement, - replacedLanguage, - replacedScript, - replacedRegion, - replacedVariant, - replacedExtensions, - toBeFreed, - status); - replacedLanguage = - (replacedLanguage != nullptr && uprv_strcmp(replacedLanguage, "und") == 0) ? - language : replacedLanguage; - replacedScript = deleteOrReplace(script, nullptr, replacedScript); - replacedRegion = deleteOrReplace(region, searchRegion, replacedRegion); - replacedVariant = deleteOrReplace( - searchVariant, searchVariant, replacedVariant); - - if ( same(language, replacedLanguage) && - same(script, replacedScript) && - same(region, replacedRegion) && - same(searchVariant, replacedVariant) && - replacedExtensions == nullptr) { - // Replacement produce no changes. - continue; - } - - language = replacedLanguage; - region = replacedRegion; - script = replacedScript; - if (searchVariant != nullptr) { - if (notEmpty(replacedVariant)) { - variants.setElementAt((void*)replacedVariant, variant_index); - } else { - variants.removeElementAt(variant_index); - } - } - if (replacedExtensions != nullptr) { - // DO NOTHING - // UTS35 does not specify what should we do if we have extensions in the - // replacement. Currently we know only the following 4 "BCP47 LegacyRules" have - // extensions in them languageAlias: - // i_default => en_x_i_default - // i_enochian => und_x_i_enochian - // i_mingo => see_x_i_mingo - // zh_min => nan_x_zh_min - // But all of them are already changed by code inside ultag_parse() before - // hitting this code. - } - - // Something changed by language alias data. - return true; - } - // Nothing changed by language alias data. - return false; -} - -bool -AliasReplacer::replaceTerritory(UVector& toBeFreed, UErrorCode& status) -{ - if (U_FAILURE(status)) { - return false; - } - if (region == nullptr) { - // No region to search. - return false; - } - const char *replacement = data->territoryMap().get(region); - if (replacement == nullptr) { - // Found no replacement data for this region. - return false; - } - const char* replacedRegion = replacement; - const char* firstSpace = uprv_strchr(replacement, ' '); - if (firstSpace != nullptr) { - // If there are are more than one region in the replacement. - // We need to check which one match based on the language. - // Cannot use nullptr for language because that will construct - // the default locale, in that case, use "und" to get the correct - // locale. - Locale l = LocaleBuilder() - .setLanguage(language == nullptr ? "und" : language) - .setScript(script) - .build(status); - l.addLikelySubtags(status); - const char* likelyRegion = l.getCountry(); - LocalPointer item; - if (likelyRegion != nullptr && uprv_strlen(likelyRegion) > 0) { - size_t len = uprv_strlen(likelyRegion); - const char* foundInReplacement = uprv_strstr(replacement, - likelyRegion); - if (foundInReplacement != nullptr) { - // Assuming the case there are no three letter region code in - // the replacement of territoryAlias - U_ASSERT(foundInReplacement == replacement || - *(foundInReplacement-1) == ' '); - U_ASSERT(foundInReplacement[len] == ' ' || - foundInReplacement[len] == '\0'); - item.adoptInsteadAndCheckErrorCode( - new CharString(foundInReplacement, (int32_t)len, status), status); - } - } - if (item.isNull() && U_SUCCESS(status)) { - item.adoptInsteadAndCheckErrorCode( - new CharString(replacement, - (int32_t)(firstSpace - replacement), status), status); - } - if (U_FAILURE(status)) { return false; } - replacedRegion = item->data(); - toBeFreed.adoptElement(item.orphan(), status); - if (U_FAILURE(status)) { return false; } - } - U_ASSERT(!same(region, replacedRegion)); - region = replacedRegion; - // The region is changed by data in territory alias. - return true; -} - -bool -AliasReplacer::replaceScript(UErrorCode& status) -{ - if (U_FAILURE(status)) { - return false; - } - if (script == nullptr) { - // No script to search. - return false; - } - const char *replacement = data->scriptMap().get(script); - if (replacement == nullptr) { - // Found no replacement data for this script. - return false; - } - U_ASSERT(!same(script, replacement)); - script = replacement; - // The script is changed by data in script alias. - return true; -} - -bool -AliasReplacer::replaceVariant(UErrorCode& status) -{ - if (U_FAILURE(status)) { - return false; - } - // Since we may have more than one variant, we need to loop through them. - for (int32_t i = 0; i < variants.size(); i++) { - const char *variant = (const char*)(variants.elementAt(i)); - const char *replacement = data->variantMap().get(variant); - if (replacement == nullptr) { - // Found no replacement data for this variant. - continue; - } - U_ASSERT((uprv_strlen(replacement) >= 5 && - uprv_strlen(replacement) <= 8) || - (uprv_strlen(replacement) == 4 && - replacement[0] >= '0' && - replacement[0] <= '9')); - if (!same(variant, replacement)) { - variants.setElementAt((void*)replacement, i); - // Special hack to handle hepburn-heploc => alalc97 - if (uprv_strcmp(variant, "heploc") == 0) { - for (int32_t j = 0; j < variants.size(); j++) { - if (uprv_strcmp((const char*)(variants.elementAt(j)), - "hepburn") == 0) { - variants.removeElementAt(j); - } - } - } - return true; - } - } - return false; -} - -bool -AliasReplacer::replaceSubdivision( - StringPiece subdivision, CharString& output, UErrorCode& status) -{ - if (U_FAILURE(status)) { - return false; - } - const char *replacement = data->subdivisionMap().get(subdivision.data()); - if (replacement != nullptr) { - const char* firstSpace = uprv_strchr(replacement, ' '); - // Found replacement data for this subdivision. - size_t len = (firstSpace != nullptr) ? - (firstSpace - replacement) : uprv_strlen(replacement); - if (2 <= len && len <= 8) { - output.append(replacement, (int32_t)len, status); - if (2 == len) { - // Add 'zzzz' based on changes to UTS #35 for CLDR-14312. - output.append("zzzz", 4, status); - } - } - return true; - } - return false; -} - -bool -AliasReplacer::replaceTransformedExtensions( - CharString& transformedExtensions, CharString& output, UErrorCode& status) -{ - // The content of the transformedExtensions will be modified in this - // function to NULL-terminating (tkey-tvalue) pairs. - if (U_FAILURE(status)) { - return false; - } - int32_t len = transformedExtensions.length(); - const char* str = transformedExtensions.data(); - const char* tkey = ultag_getTKeyStart(str); - int32_t tlangLen = (tkey == str) ? 0 : - ((tkey == nullptr) ? len : static_cast((tkey - str - 1))); - CharStringByteSink sink(&output); - if (tlangLen > 0) { - Locale tlang = LocaleBuilder() - .setLanguageTag(StringPiece(str, tlangLen)) - .build(status); - tlang.canonicalize(status); - tlang.toLanguageTag(sink, status); - if (U_FAILURE(status)) { - return false; - } - T_CString_toLowerCase(output.data()); - } - if (tkey != nullptr) { - // We need to sort the tfields by tkey - UVector tfields(status); - if (U_FAILURE(status)) { - return false; - } - do { - const char* tvalue = uprv_strchr(tkey, '-'); - if (tvalue == nullptr) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - const char* nextTKey = ultag_getTKeyStart(tvalue); - if (nextTKey != nullptr) { - *((char*)(nextTKey-1)) = '\0'; // NULL terminate tvalue - } - tfields.insertElementAt((void*)tkey, tfields.size(), status); - if (U_FAILURE(status)) { - return false; - } - tkey = nextTKey; - } while (tkey != nullptr); - tfields.sort([](UElement e1, UElement e2) -> int32_t { - return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer); - }, status); - for (int32_t i = 0; i < tfields.size(); i++) { - if (output.length() > 0) { - output.append('-', status); - } - const char* tfield = (const char*) tfields.elementAt(i); - const char* tvalue = uprv_strchr(tfield, '-'); - if (tvalue == nullptr) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - // Split the "tkey-tvalue" pair string so that we can canonicalize the tvalue. - *((char*)tvalue++) = '\0'; // NULL terminate tkey - output.append(tfield, status).append('-', status); - const char* bcpTValue = ulocimp_toBcpType(tfield, tvalue, nullptr, nullptr); - output.append((bcpTValue == nullptr) ? tvalue : bcpTValue, status); - } - } - if (U_FAILURE(status)) { - return false; - } - return true; -} - -CharString& -AliasReplacer::outputToString( - CharString& out, UErrorCode status) -{ - out.append(language, status); - if (notEmpty(script)) { - out.append(SEP_CHAR, status) - .append(script, status); - } - if (notEmpty(region)) { - out.append(SEP_CHAR, status) - .append(region, status); - } - if (variants.size() > 0) { - if (!notEmpty(script) && !notEmpty(region)) { - out.append(SEP_CHAR, status); - } - variants.sort([](UElement e1, UElement e2) -> int32_t { - return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer); - }, status); - int32_t variantsStart = out.length(); - for (int32_t i = 0; i < variants.size(); i++) { - out.append(SEP_CHAR, status) - .append((const char*)(variants.elementAt(i)), - status); - } - T_CString_toUpperCase(out.data() + variantsStart); - } - if (notEmpty(extensions)) { - CharString tmp("und_", status); - tmp.append(extensions, status); - Locale tmpLocale(tmp.data()); - // only support x extension inside CLDR for now. - U_ASSERT(extensions[0] == 'x'); - out.append(tmpLocale.getName() + 1, status); - } - return out; -} - -bool -AliasReplacer::replace(const Locale& locale, CharString& out, UErrorCode& status) -{ - data = AliasData::singleton(status); - if (U_FAILURE(status)) { - return false; - } - U_ASSERT(data != nullptr); - out.clear(); - language = locale.getLanguage(); - if (!notEmpty(language)) { - language = nullptr; - } - script = locale.getScript(); - if (!notEmpty(script)) { - script = nullptr; - } - region = locale.getCountry(); - if (!notEmpty(region)) { - region = nullptr; - } - const char* variantsStr = locale.getVariant(); - CharString variantsBuff(variantsStr, -1, status); - if (!variantsBuff.isEmpty()) { - if (U_FAILURE(status)) { return false; } - char* start = variantsBuff.data(); - T_CString_toLowerCase(start); - char* end; - while ((end = uprv_strchr(start, SEP_CHAR)) != nullptr && - U_SUCCESS(status)) { - *end = NULL_CHAR; // null terminate inside variantsBuff - variants.addElement(start, status); - start = end + 1; - } - variants.addElement(start, status); - } - if (U_FAILURE(status)) { return false; } - - // Sort the variants - variants.sort([](UElement e1, UElement e2) -> int32_t { - return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer); - }, status); - - // A changed count to assert when loop too many times. - int changed = 0; - // A UVector to to hold CharString allocated by the replace* method - // and freed when out of scope from his function. - UVector stringsToBeFreed([](void *obj){ delete ((CharString*) obj); }, - nullptr, 10, status); - while (U_SUCCESS(status)) { - // Something wrong with the data cause looping here more than 10 times - // already. - U_ASSERT(changed < 5); - // From observation of key in data/misc/metadata.txt - // we know currently we only need to search in the following combination - // of fields for type in languageAlias: - // * lang_region_variant - // * lang_region - // * lang_variant - // * lang - // * und_variant - // This assumption is ensured by the U_ASSERT in readLanguageAlias - // - // lang REGION variant - if ( replaceLanguage(true, true, true, stringsToBeFreed, status) || - replaceLanguage(true, true, false, stringsToBeFreed, status) || - replaceLanguage(true, false, true, stringsToBeFreed, status) || - replaceLanguage(true, false, false, stringsToBeFreed, status) || - replaceLanguage(false,false, true, stringsToBeFreed, status) || - replaceTerritory(stringsToBeFreed, status) || - replaceScript(status) || - replaceVariant(status)) { - // Some values in data is changed, try to match from the beginning - // again. - changed++; - continue; - } - // Nothing changed. Break out. - break; - } // while(1) - - if (U_FAILURE(status)) { return false; } - // Nothing changed and we know the order of the variants are not change - // because we have no variant or only one. - const char* extensionsStr = locale_getKeywordsStart(locale.getName()); - if (changed == 0 && variants.size() <= 1 && extensionsStr == nullptr) { - return false; - } - outputToString(out, status); - if (U_FAILURE(status)) { - return false; - } - if (extensionsStr != nullptr) { - changed = 0; - Locale temp(locale); - LocalPointer iter(locale.createKeywords(status)); - if (U_SUCCESS(status) && !iter.isNull()) { - const char* key; - while ((key = iter->next(nullptr, status)) != nullptr) { - if (uprv_strcmp("sd", key) == 0 || uprv_strcmp("rg", key) == 0 || - uprv_strcmp("t", key) == 0) { - CharString value; - CharStringByteSink valueSink(&value); - locale.getKeywordValue(key, valueSink, status); - if (U_FAILURE(status)) { - status = U_ZERO_ERROR; - continue; - } - CharString replacement; - if (uprv_strlen(key) == 2) { - if (replaceSubdivision(value.toStringPiece(), replacement, status)) { - changed++; - temp.setKeywordValue(key, replacement.data(), status); - } - } else { - U_ASSERT(uprv_strcmp(key, "t") == 0); - if (replaceTransformedExtensions(value, replacement, status)) { - changed++; - temp.setKeywordValue(key, replacement.data(), status); - } - } - if (U_FAILURE(status)) { - return false; - } - } - } - } - if (changed != 0) { - extensionsStr = locale_getKeywordsStart(temp.getName()); - } - out.append(extensionsStr, status); - } - if (U_FAILURE(status)) { - return false; - } - // If the tag is not changed, return. - if (uprv_strcmp(out.data(), locale.getName()) == 0) { - out.clear(); - return false; - } - return true; -} - -// Return true if the locale is changed during canonicalization. -// The replaced value then will be put into out. -bool -canonicalizeLocale(const Locale& locale, CharString& out, UErrorCode& status) -{ - AliasReplacer replacer(status); - return replacer.replace(locale, out, status); -} - -// Function to optimize for known cases without so we can skip the loading -// of resources in the startup time until we really need it. -bool -isKnownCanonicalizedLocale(const char* locale, UErrorCode& status) -{ - if ( uprv_strcmp(locale, "c") == 0 || - uprv_strcmp(locale, "en") == 0 || - uprv_strcmp(locale, "en_US") == 0) { - return true; - } - - // common well-known Canonicalized. - umtx_initOnce(gKnownCanonicalizedInitOnce, - &loadKnownCanonicalized, status); - if (U_FAILURE(status)) { - return false; - } - U_ASSERT(gKnownCanonicalized != nullptr); - return uhash_geti(gKnownCanonicalized, locale) != 0; -} - -} // namespace - -// Function for testing. -U_CAPI const char* const* -ulocimp_getKnownCanonicalizedLocaleForTest(int32_t* length) -{ - *length = UPRV_LENGTHOF(KNOWN_CANONICALIZED); - return KNOWN_CANONICALIZED; -} - -// Function for testing. -U_CAPI bool -ulocimp_isCanonicalizedLocaleForTest(const char* localeName) -{ - Locale l(localeName); - UErrorCode status = U_ZERO_ERROR; - CharString temp; - return !canonicalizeLocale(l, temp, status) && U_SUCCESS(status); -} - -/*This function initializes a Locale from a C locale ID*/ -Locale& Locale::init(const char* localeID, UBool canonicalize) -{ - fIsBogus = false; - /* Free our current storage */ - if ((baseName != fullName) && (baseName != fullNameBuffer)) { - uprv_free(baseName); - } - baseName = NULL; - if(fullName != fullNameBuffer) { - uprv_free(fullName); - fullName = fullNameBuffer; - } - - // not a loop: - // just an easy way to have a common error-exit - // without goto and without another function - do { - char *separator; - char *field[5] = {0}; - int32_t fieldLen[5] = {0}; - int32_t fieldIdx; - int32_t variantField; - int32_t length; - UErrorCode err; - - if(localeID == NULL) { - // not an error, just set the default locale - return *this = getDefault(); - } - - /* preset all fields to empty */ - language[0] = script[0] = country[0] = 0; - - // "canonicalize" the locale ID to ICU/Java format - err = U_ZERO_ERROR; - length = canonicalize ? - uloc_canonicalize(localeID, fullName, sizeof(fullNameBuffer), &err) : - uloc_getName(localeID, fullName, sizeof(fullNameBuffer), &err); - - if(err == U_BUFFER_OVERFLOW_ERROR || length >= (int32_t)sizeof(fullNameBuffer)) { - U_ASSERT(baseName == nullptr); - /*Go to heap for the fullName if necessary*/ - fullName = (char *)uprv_malloc(sizeof(char)*(length + 1)); - if(fullName == 0) { - fullName = fullNameBuffer; - break; // error: out of memory - } - err = U_ZERO_ERROR; - length = canonicalize ? - uloc_canonicalize(localeID, fullName, length+1, &err) : - uloc_getName(localeID, fullName, length+1, &err); - } - if(U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) { - /* should never occur */ - break; - } - - variantBegin = length; - - /* after uloc_getName/canonicalize() we know that only '_' are separators */ - /* But _ could also appeared in timezone such as "en@timezone=America/Los_Angeles" */ - separator = field[0] = fullName; - fieldIdx = 1; - char* at = uprv_strchr(fullName, '@'); - while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR)) != 0 && - fieldIdx < UPRV_LENGTHOF(field)-1 && - (at == nullptr || separator < at)) { - field[fieldIdx] = separator + 1; - fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]); - fieldIdx++; - } - // variant may contain @foo or .foo POSIX cruft; remove it - separator = uprv_strchr(field[fieldIdx-1], '@'); - char* sep2 = uprv_strchr(field[fieldIdx-1], '.'); - if (separator!=NULL || sep2!=NULL) { - if (separator==NULL || (sep2!=NULL && separator > sep2)) { - separator = sep2; - } - fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]); - } else { - fieldLen[fieldIdx-1] = length - (int32_t)(field[fieldIdx-1] - fullName); - } - - if (fieldLen[0] >= (int32_t)(sizeof(language))) - { - break; // error: the language field is too long - } - - variantField = 1; /* Usually the 2nd one, except when a script or country is also used. */ - if (fieldLen[0] > 0) { - /* We have a language */ - uprv_memcpy(language, fullName, fieldLen[0]); - language[fieldLen[0]] = 0; - } - if (fieldLen[1] == 4 && uprv_isASCIILetter(field[1][0]) && - uprv_isASCIILetter(field[1][1]) && uprv_isASCIILetter(field[1][2]) && - uprv_isASCIILetter(field[1][3])) { - /* We have at least a script */ - uprv_memcpy(script, field[1], fieldLen[1]); - script[fieldLen[1]] = 0; - variantField++; - } - - if (fieldLen[variantField] == 2 || fieldLen[variantField] == 3) { - /* We have a country */ - uprv_memcpy(country, field[variantField], fieldLen[variantField]); - country[fieldLen[variantField]] = 0; - variantField++; - } else if (fieldLen[variantField] == 0) { - variantField++; /* script or country empty but variant in next field (i.e. en__POSIX) */ - } - - if (fieldLen[variantField] > 0) { - /* We have a variant */ - variantBegin = (int32_t)(field[variantField] - fullName); - } - - err = U_ZERO_ERROR; - initBaseName(err); - if (U_FAILURE(err)) { - break; - } - - if (canonicalize) { - if (!isKnownCanonicalizedLocale(fullName, err)) { - CharString replaced; - // Not sure it is already canonicalized - if (canonicalizeLocale(*this, replaced, err)) { - U_ASSERT(U_SUCCESS(err)); - // If need replacement, call init again. - init(replaced.data(), false); - } - if (U_FAILURE(err)) { - break; - } - } - } // if (canonicalize) { - - // successful end of init() - return *this; - } while(0); /*loop doesn't iterate*/ - - // when an error occurs, then set this object to "bogus" (there is no UErrorCode here) - setToBogus(); - - return *this; -} - -/* - * Set up the base name. - * If there are no key words, it's exactly the full name. - * If key words exist, it's the full name truncated at the '@' character. - * Need to set up both at init() and after setting a keyword. - */ -void -Locale::initBaseName(UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - U_ASSERT(baseName==NULL || baseName==fullName); - const char *atPtr = uprv_strchr(fullName, '@'); - const char *eqPtr = uprv_strchr(fullName, '='); - if (atPtr && eqPtr && atPtr < eqPtr) { - // Key words exist. - int32_t baseNameLength = (int32_t)(atPtr - fullName); - baseName = (char *)uprv_malloc(baseNameLength + 1); - if (baseName == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_strncpy(baseName, fullName, baseNameLength); - baseName[baseNameLength] = 0; - - // The original computation of variantBegin leaves it equal to the length - // of fullName if there is no variant. It should instead be - // the length of the baseName. - if (variantBegin > baseNameLength) { - variantBegin = baseNameLength; - } - } else { - baseName = fullName; - } -} - - -int32_t -Locale::hashCode() const -{ - return ustr_hashCharsN(fullName, static_cast(uprv_strlen(fullName))); -} - -void -Locale::setToBogus() { - /* Free our current storage */ - if((baseName != fullName) && (baseName != fullNameBuffer)) { - uprv_free(baseName); - } - baseName = NULL; - if(fullName != fullNameBuffer) { - uprv_free(fullName); - fullName = fullNameBuffer; - } - *fullNameBuffer = 0; - *language = 0; - *script = 0; - *country = 0; - fIsBogus = true; - variantBegin = 0; -} - -const Locale& U_EXPORT2 -Locale::getDefault() -{ - { - Mutex lock(&gDefaultLocaleMutex); - if (gDefaultLocale != NULL) { - return *gDefaultLocale; - } - } - UErrorCode status = U_ZERO_ERROR; - return *locale_set_default_internal(NULL, status); -} - - - -void U_EXPORT2 -Locale::setDefault( const Locale& newLocale, - UErrorCode& status) -{ - if (U_FAILURE(status)) { - return; - } - - /* Set the default from the full name string of the supplied locale. - * This is a convenient way to access the default locale caching mechanisms. - */ - const char *localeID = newLocale.getName(); - locale_set_default_internal(localeID, status); -} - -void -Locale::addLikelySubtags(UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - - CharString maximizedLocaleID; - { - CharStringByteSink sink(&maximizedLocaleID); - ulocimp_addLikelySubtags(fullName, sink, &status); - } - - if (U_FAILURE(status)) { - return; - } - - init(maximizedLocaleID.data(), /*canonicalize=*/false); - if (isBogus()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } -} - -void -Locale::minimizeSubtags(UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - - CharString minimizedLocaleID; - { - CharStringByteSink sink(&minimizedLocaleID); - ulocimp_minimizeSubtags(fullName, sink, &status); - } - - if (U_FAILURE(status)) { - return; - } - - init(minimizedLocaleID.data(), /*canonicalize=*/false); - if (isBogus()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } -} - -void -Locale::canonicalize(UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - if (isBogus()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - CharString uncanonicalized(fullName, status); - if (U_FAILURE(status)) { - return; - } - init(uncanonicalized.data(), /*canonicalize=*/true); - if (isBogus()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } -} - -Locale U_EXPORT2 -Locale::forLanguageTag(StringPiece tag, UErrorCode& status) -{ - Locale result(Locale::eBOGUS); - - if (U_FAILURE(status)) { - return result; - } - - // If a BCP 47 language tag is passed as the language parameter to the - // normal Locale constructor, it will actually fall back to invoking - // uloc_forLanguageTag() to parse it if it somehow is able to detect that - // the string actually is BCP 47. This works well for things like strings - // using BCP 47 extensions, but it does not at all work for things like - // legacy language tags (marked as “Type: grandfathered” in BCP 47, - // e.g., "en-GB-oed") which are possible to also - // interpret as ICU locale IDs and because of that won't trigger the BCP 47 - // parsing. Therefore the code here explicitly calls uloc_forLanguageTag() - // and then Locale::init(), instead of just calling the normal constructor. - - CharString localeID; - int32_t parsedLength; - { - CharStringByteSink sink(&localeID); - ulocimp_forLanguageTag( - tag.data(), - tag.length(), - sink, - &parsedLength, - &status); - } - - if (U_FAILURE(status)) { - return result; - } - - if (parsedLength != tag.size()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return result; - } - - result.init(localeID.data(), /*canonicalize=*/false); - if (result.isBogus()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } - return result; -} - -void -Locale::toLanguageTag(ByteSink& sink, UErrorCode& status) const -{ - if (U_FAILURE(status)) { - return; - } - - if (fIsBogus) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - ulocimp_toLanguageTag(fullName, sink, /*strict=*/false, &status); -} - -Locale U_EXPORT2 -Locale::createFromName (const char *name) -{ - if (name) { - Locale l(""); - l.init(name, false); - return l; - } - else { - return getDefault(); - } -} - -Locale U_EXPORT2 -Locale::createCanonical(const char* name) { - Locale loc(""); - loc.init(name, true); - return loc; -} - -const char * -Locale::getISO3Language() const -{ - return uloc_getISO3Language(fullName); -} - - -const char * -Locale::getISO3Country() const -{ - return uloc_getISO3Country(fullName); -} - -/** - * Return the LCID value as specified in the "LocaleID" resource for this - * locale. The LocaleID must be expressed as a hexadecimal number, from - * one to four digits. If the LocaleID resource is not present, or is - * in an incorrect format, 0 is returned. The LocaleID is for use in - * Windows (it is an LCID), but is available on all platforms. - */ -uint32_t -Locale::getLCID() const -{ - return uloc_getLCID(fullName); -} - -const char* const* U_EXPORT2 Locale::getISOCountries() -{ - return uloc_getISOCountries(); -} - -const char* const* U_EXPORT2 Locale::getISOLanguages() -{ - return uloc_getISOLanguages(); -} - -// Set the locale's data based on a posix id. -void Locale::setFromPOSIXID(const char *posixID) -{ - init(posixID, true); -} - -const Locale & U_EXPORT2 -Locale::getRoot(void) -{ - return getLocale(eROOT); -} - -const Locale & U_EXPORT2 -Locale::getEnglish(void) -{ - return getLocale(eENGLISH); -} - -const Locale & U_EXPORT2 -Locale::getFrench(void) -{ - return getLocale(eFRENCH); -} - -const Locale & U_EXPORT2 -Locale::getGerman(void) -{ - return getLocale(eGERMAN); -} - -const Locale & U_EXPORT2 -Locale::getItalian(void) -{ - return getLocale(eITALIAN); -} - -const Locale & U_EXPORT2 -Locale::getJapanese(void) -{ - return getLocale(eJAPANESE); -} - -const Locale & U_EXPORT2 -Locale::getKorean(void) -{ - return getLocale(eKOREAN); -} - -const Locale & U_EXPORT2 -Locale::getChinese(void) -{ - return getLocale(eCHINESE); -} - -const Locale & U_EXPORT2 -Locale::getSimplifiedChinese(void) -{ - return getLocale(eCHINA); -} - -const Locale & U_EXPORT2 -Locale::getTraditionalChinese(void) -{ - return getLocale(eTAIWAN); -} - - -const Locale & U_EXPORT2 -Locale::getFrance(void) -{ - return getLocale(eFRANCE); -} - -const Locale & U_EXPORT2 -Locale::getGermany(void) -{ - return getLocale(eGERMANY); -} - -const Locale & U_EXPORT2 -Locale::getItaly(void) -{ - return getLocale(eITALY); -} - -const Locale & U_EXPORT2 -Locale::getJapan(void) -{ - return getLocale(eJAPAN); -} - -const Locale & U_EXPORT2 -Locale::getKorea(void) -{ - return getLocale(eKOREA); -} - -const Locale & U_EXPORT2 -Locale::getChina(void) -{ - return getLocale(eCHINA); -} - -const Locale & U_EXPORT2 -Locale::getPRC(void) -{ - return getLocale(eCHINA); -} - -const Locale & U_EXPORT2 -Locale::getTaiwan(void) -{ - return getLocale(eTAIWAN); -} - -const Locale & U_EXPORT2 -Locale::getUK(void) -{ - return getLocale(eUK); -} - -const Locale & U_EXPORT2 -Locale::getUS(void) -{ - return getLocale(eUS); -} - -const Locale & U_EXPORT2 -Locale::getCanada(void) -{ - return getLocale(eCANADA); -} - -const Locale & U_EXPORT2 -Locale::getCanadaFrench(void) -{ - return getLocale(eCANADA_FRENCH); -} - -const Locale & -Locale::getLocale(int locid) -{ - Locale *localeCache = getLocaleCache(); - U_ASSERT((locid < eMAX_LOCALES)&&(locid>=0)); - if (localeCache == NULL) { - // Failure allocating the locale cache. - // The best we can do is return a NULL reference. - locid = 0; - } - return localeCache[locid]; /*operating on NULL*/ -} - -/* -This function is defined this way in order to get around static -initialization and static destruction. - */ -Locale * -Locale::getLocaleCache(void) -{ - UErrorCode status = U_ZERO_ERROR; - umtx_initOnce(gLocaleCacheInitOnce, locale_init, status); - return gLocaleCache; -} - -class KeywordEnumeration : public StringEnumeration { -private: - char *keywords; - char *current; - int32_t length; - UnicodeString currUSKey; - static const char fgClassID;/* Warning this is used beyond the typical RTTI usage. */ - -public: - static UClassID U_EXPORT2 getStaticClassID(void) { return (UClassID)&fgClassID; } - virtual UClassID getDynamicClassID(void) const override { return getStaticClassID(); } -public: - KeywordEnumeration(const char *keys, int32_t keywordLen, int32_t currentIndex, UErrorCode &status) - : keywords((char *)&fgClassID), current((char *)&fgClassID), length(0) { - if(U_SUCCESS(status) && keywordLen != 0) { - if(keys == NULL || keywordLen < 0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } else { - keywords = (char *)uprv_malloc(keywordLen+1); - if (keywords == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } - else { - uprv_memcpy(keywords, keys, keywordLen); - keywords[keywordLen] = 0; - current = keywords + currentIndex; - length = keywordLen; - } - } - } - } - - virtual ~KeywordEnumeration(); - - virtual StringEnumeration * clone() const override - { - UErrorCode status = U_ZERO_ERROR; - return new KeywordEnumeration(keywords, length, (int32_t)(current - keywords), status); - } - - virtual int32_t count(UErrorCode &/*status*/) const override { - char *kw = keywords; - int32_t result = 0; - while(*kw) { - result++; - kw += uprv_strlen(kw)+1; - } - return result; - } - - virtual const char* next(int32_t* resultLength, UErrorCode& status) override { - const char* result; - int32_t len; - if(U_SUCCESS(status) && *current != 0) { - result = current; - len = (int32_t)uprv_strlen(current); - current += len+1; - if(resultLength != NULL) { - *resultLength = len; - } - } else { - if(resultLength != NULL) { - *resultLength = 0; - } - result = NULL; - } - return result; - } - - virtual const UnicodeString* snext(UErrorCode& status) override { - int32_t resultLength = 0; - const char *s = next(&resultLength, status); - return setChars(s, resultLength, status); - } - - virtual void reset(UErrorCode& /*status*/) override { - current = keywords; - } -}; - -const char KeywordEnumeration::fgClassID = '\0'; - -KeywordEnumeration::~KeywordEnumeration() { - uprv_free(keywords); -} - -// A wrapper around KeywordEnumeration that calls uloc_toUnicodeLocaleKey() in -// the next() method for each keyword before returning it. -class UnicodeKeywordEnumeration : public KeywordEnumeration { -public: - using KeywordEnumeration::KeywordEnumeration; - virtual ~UnicodeKeywordEnumeration(); - - virtual const char* next(int32_t* resultLength, UErrorCode& status) override { - const char* legacy_key = KeywordEnumeration::next(nullptr, status); - while (U_SUCCESS(status) && legacy_key != nullptr) { - const char* key = uloc_toUnicodeLocaleKey(legacy_key); - if (key != nullptr) { - if (resultLength != nullptr) { - *resultLength = static_cast(uprv_strlen(key)); - } - return key; - } - // Not a Unicode keyword, could be a t, x or other, continue to look at the next one. - legacy_key = KeywordEnumeration::next(nullptr, status); - } - if (resultLength != nullptr) *resultLength = 0; - return nullptr; - } -}; - -// Out-of-line virtual destructor to serve as the "key function". -UnicodeKeywordEnumeration::~UnicodeKeywordEnumeration() = default; - -StringEnumeration * -Locale::createKeywords(UErrorCode &status) const -{ - StringEnumeration *result = NULL; - - if (U_FAILURE(status)) { - return result; - } - - const char* variantStart = uprv_strchr(fullName, '@'); - const char* assignment = uprv_strchr(fullName, '='); - if(variantStart) { - if(assignment > variantStart) { - CharString keywords; - CharStringByteSink sink(&keywords); - ulocimp_getKeywords(variantStart+1, '@', sink, false, &status); - if (U_SUCCESS(status) && !keywords.isEmpty()) { - result = new KeywordEnumeration(keywords.data(), keywords.length(), 0, status); - if (!result) { - status = U_MEMORY_ALLOCATION_ERROR; - } - } - } else { - status = U_INVALID_FORMAT_ERROR; - } - } - return result; -} - -StringEnumeration * -Locale::createUnicodeKeywords(UErrorCode &status) const -{ - StringEnumeration *result = NULL; - - if (U_FAILURE(status)) { - return result; - } - - const char* variantStart = uprv_strchr(fullName, '@'); - const char* assignment = uprv_strchr(fullName, '='); - if(variantStart) { - if(assignment > variantStart) { - CharString keywords; - CharStringByteSink sink(&keywords); - ulocimp_getKeywords(variantStart+1, '@', sink, false, &status); - if (U_SUCCESS(status) && !keywords.isEmpty()) { - result = new UnicodeKeywordEnumeration(keywords.data(), keywords.length(), 0, status); - if (!result) { - status = U_MEMORY_ALLOCATION_ERROR; - } - } - } else { - status = U_INVALID_FORMAT_ERROR; - } - } - return result; -} - -int32_t -Locale::getKeywordValue(const char* keywordName, char *buffer, int32_t bufLen, UErrorCode &status) const -{ - return uloc_getKeywordValue(fullName, keywordName, buffer, bufLen, &status); -} - -void -Locale::getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - - if (fIsBogus) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - // TODO: Remove the need for a const char* to a NUL terminated buffer. - const CharString keywordName_nul(keywordName, status); - if (U_FAILURE(status)) { - return; - } - - ulocimp_getKeywordValue(fullName, keywordName_nul.data(), sink, &status); -} - -void -Locale::getUnicodeKeywordValue(StringPiece keywordName, - ByteSink& sink, - UErrorCode& status) const { - // TODO: Remove the need for a const char* to a NUL terminated buffer. - const CharString keywordName_nul(keywordName, status); - if (U_FAILURE(status)) { - return; - } - - const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data()); - - if (legacy_key == nullptr) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - CharString legacy_value; - { - CharStringByteSink sink(&legacy_value); - getKeywordValue(legacy_key, sink, status); - } - - if (U_FAILURE(status)) { - return; - } - - const char* unicode_value = uloc_toUnicodeLocaleType( - keywordName_nul.data(), legacy_value.data()); - - if (unicode_value == nullptr) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - sink.Append(unicode_value, static_cast(uprv_strlen(unicode_value))); -} - -void -Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status) -{ - if (U_FAILURE(status)) { - return; - } - if (status == U_STRING_NOT_TERMINATED_WARNING) { - status = U_ZERO_ERROR; - } - int32_t bufferLength = uprv_max((int32_t)(uprv_strlen(fullName) + 1), ULOC_FULLNAME_CAPACITY); - int32_t newLength = uloc_setKeywordValue(keywordName, keywordValue, fullName, - bufferLength, &status) + 1; - U_ASSERT(status != U_STRING_NOT_TERMINATED_WARNING); - /* Handle the case the current buffer is not enough to hold the new id */ - if (status == U_BUFFER_OVERFLOW_ERROR) { - U_ASSERT(newLength > bufferLength); - char* newFullName = (char *)uprv_malloc(newLength); - if (newFullName == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_strcpy(newFullName, fullName); - if (fullName != fullNameBuffer) { - // if full Name is already on the heap, need to free it. - uprv_free(fullName); - if (baseName == fullName) { - baseName = newFullName; // baseName should not point to freed memory. - } - } - fullName = newFullName; - status = U_ZERO_ERROR; - uloc_setKeywordValue(keywordName, keywordValue, fullName, newLength, &status); - U_ASSERT(status != U_STRING_NOT_TERMINATED_WARNING); - } else { - U_ASSERT(newLength <= bufferLength); - } - if (U_SUCCESS(status) && baseName == fullName) { - // May have added the first keyword, meaning that the fullName is no longer also the baseName. - initBaseName(status); - } -} - -void -Locale::setKeywordValue(StringPiece keywordName, - StringPiece keywordValue, - UErrorCode& status) { - // TODO: Remove the need for a const char* to a NUL terminated buffer. - const CharString keywordName_nul(keywordName, status); - const CharString keywordValue_nul(keywordValue, status); - setKeywordValue(keywordName_nul.data(), keywordValue_nul.data(), status); -} - -void -Locale::setUnicodeKeywordValue(StringPiece keywordName, - StringPiece keywordValue, - UErrorCode& status) { - // TODO: Remove the need for a const char* to a NUL terminated buffer. - const CharString keywordName_nul(keywordName, status); - const CharString keywordValue_nul(keywordValue, status); - - if (U_FAILURE(status)) { - return; - } - - const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data()); - - if (legacy_key == nullptr) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - const char* legacy_value = nullptr; - - if (!keywordValue_nul.isEmpty()) { - legacy_value = - uloc_toLegacyType(keywordName_nul.data(), keywordValue_nul.data()); - - if (legacy_value == nullptr) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - } - - setKeywordValue(legacy_key, legacy_value, status); -} - -const char * -Locale::getBaseName() const { - return baseName; -} - -Locale::Iterator::~Iterator() = default; - -//eof -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 1997-2016, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** +* +* File locid.cpp +* +* Created by: Richard Gillam +* +* Modification History: +* +* Date Name Description +* 02/11/97 aliu Changed gLocPath to fgDataDirectory and added +* methods to get and set it. +* 04/02/97 aliu Made operator!= inline; fixed return value +* of getName(). +* 04/15/97 aliu Cleanup for AIX/Win32. +* 04/24/97 aliu Numerous changes per code review. +* 08/18/98 stephen Changed getDisplayName() +* Added SIMPLIFIED_CHINESE, TRADITIONAL_CHINESE +* Added getISOCountries(), getISOLanguages(), +* getLanguagesForCountry() +* 03/16/99 bertrand rehaul. +* 07/21/99 stephen Added U_CFUNC setDefault +* 11/09/99 weiv Added const char * getName() const; +* 04/12/00 srl removing unicodestring api's and cached hash code +* 08/10/01 grhoten Change the static Locales to accessor functions +****************************************************************************** +*/ + +#include + +#include "unicode/bytestream.h" +#include "unicode/locid.h" +#include "unicode/localebuilder.h" +#include "unicode/strenum.h" +#include "unicode/stringpiece.h" +#include "unicode/uloc.h" +#include "unicode/ures.h" + +#include "bytesinkutil.h" +#include "charstr.h" +#include "charstrmap.h" +#include "cmemory.h" +#include "cstring.h" +#include "mutex.h" +#include "putilimp.h" +#include "uassert.h" +#include "ucln_cmn.h" +#include "uhash.h" +#include "ulocimp.h" +#include "umutex.h" +#include "uniquecharstr.h" +#include "ustr_imp.h" +#include "uvector.h" + +U_CDECL_BEGIN +static UBool U_CALLCONV locale_cleanup(); +U_CDECL_END + +U_NAMESPACE_BEGIN + +static Locale *gLocaleCache = nullptr; +static UInitOnce gLocaleCacheInitOnce {}; + +// gDefaultLocaleMutex protects all access to gDefaultLocalesHashT and gDefaultLocale. +static UMutex gDefaultLocaleMutex; +static UHashtable *gDefaultLocalesHashT = nullptr; +static Locale *gDefaultLocale = nullptr; + +/** + * \def ULOC_STRING_LIMIT + * strings beyond this value crash in CharString + */ +#define ULOC_STRING_LIMIT 357913941 + +U_NAMESPACE_END + +typedef enum ELocalePos { + eENGLISH, + eFRENCH, + eGERMAN, + eITALIAN, + eJAPANESE, + eKOREAN, + eCHINESE, + + eFRANCE, + eGERMANY, + eITALY, + eJAPAN, + eKOREA, + eCHINA, /* Alias for PRC */ + eTAIWAN, + eUK, + eUS, + eCANADA, + eCANADA_FRENCH, + eROOT, + + + //eDEFAULT, + eMAX_LOCALES +} ELocalePos; + +U_CDECL_BEGIN +// +// Deleter function for Locales owned by the default Locale hash table/ +// +static void U_CALLCONV +deleteLocale(void *obj) { + delete (icu::Locale *) obj; +} + +static UBool U_CALLCONV locale_cleanup() +{ + U_NAMESPACE_USE + + delete [] gLocaleCache; + gLocaleCache = nullptr; + gLocaleCacheInitOnce.reset(); + + if (gDefaultLocalesHashT) { + uhash_close(gDefaultLocalesHashT); // Automatically deletes all elements, using deleter func. + gDefaultLocalesHashT = nullptr; + } + gDefaultLocale = nullptr; + return true; +} + + +static void U_CALLCONV locale_init(UErrorCode &status) { + U_NAMESPACE_USE + + U_ASSERT(gLocaleCache == nullptr); + gLocaleCache = new Locale[(int)eMAX_LOCALES]; + if (gLocaleCache == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); + gLocaleCache[eROOT] = Locale(""); + gLocaleCache[eENGLISH] = Locale("en"); + gLocaleCache[eFRENCH] = Locale("fr"); + gLocaleCache[eGERMAN] = Locale("de"); + gLocaleCache[eITALIAN] = Locale("it"); + gLocaleCache[eJAPANESE] = Locale("ja"); + gLocaleCache[eKOREAN] = Locale("ko"); + gLocaleCache[eCHINESE] = Locale("zh"); + gLocaleCache[eFRANCE] = Locale("fr", "FR"); + gLocaleCache[eGERMANY] = Locale("de", "DE"); + gLocaleCache[eITALY] = Locale("it", "IT"); + gLocaleCache[eJAPAN] = Locale("ja", "JP"); + gLocaleCache[eKOREA] = Locale("ko", "KR"); + gLocaleCache[eCHINA] = Locale("zh", "CN"); + gLocaleCache[eTAIWAN] = Locale("zh", "TW"); + gLocaleCache[eUK] = Locale("en", "GB"); + gLocaleCache[eUS] = Locale("en", "US"); + gLocaleCache[eCANADA] = Locale("en", "CA"); + gLocaleCache[eCANADA_FRENCH] = Locale("fr", "CA"); +} + +U_CDECL_END + +U_NAMESPACE_BEGIN + +Locale *locale_set_default_internal(const char *id, UErrorCode& status) { + // Synchronize this entire function. + Mutex lock(&gDefaultLocaleMutex); + + UBool canonicalize = false; + + // If given a nullptr string for the locale id, grab the default + // name from the system. + // (Different from most other locale APIs, where a null name means use + // the current ICU default locale.) + if (id == nullptr) { + id = uprv_getDefaultLocaleID(); // This function not thread safe? TODO: verify. + canonicalize = true; // always canonicalize host ID + } + + CharString localeNameBuf; + { + CharStringByteSink sink(&localeNameBuf); + if (canonicalize) { + ulocimp_canonicalize(id, sink, &status); + } else { + ulocimp_getName(id, sink, &status); + } + } + + if (U_FAILURE(status)) { + return gDefaultLocale; + } + + if (gDefaultLocalesHashT == nullptr) { + gDefaultLocalesHashT = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status); + if (U_FAILURE(status)) { + return gDefaultLocale; + } + uhash_setValueDeleter(gDefaultLocalesHashT, deleteLocale); + ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); + } + + Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf.data()); + if (newDefault == nullptr) { + newDefault = new Locale(Locale::eBOGUS); + if (newDefault == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return gDefaultLocale; + } + newDefault->init(localeNameBuf.data(), false); + uhash_put(gDefaultLocalesHashT, (char*) newDefault->getName(), newDefault, &status); + if (U_FAILURE(status)) { + return gDefaultLocale; + } + } + gDefaultLocale = newDefault; + return gDefaultLocale; +} + +U_NAMESPACE_END + +/* sfb 07/21/99 */ +U_CFUNC void +locale_set_default(const char *id) +{ + U_NAMESPACE_USE + UErrorCode status = U_ZERO_ERROR; + locale_set_default_internal(id, status); +} +/* end */ + +U_CFUNC const char * +locale_get_default() +{ + U_NAMESPACE_USE + return Locale::getDefault().getName(); +} + + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale) + +/*Character separating the posix id fields*/ +// '_' +// In the platform codepage. +#define SEP_CHAR '_' +#define NULL_CHAR '\0' + +Locale::~Locale() +{ + if ((baseName != fullName) && (baseName != fullNameBuffer)) { + uprv_free(baseName); + } + baseName = nullptr; + /*if fullName is on the heap, we free it*/ + if (fullName != fullNameBuffer) + { + uprv_free(fullName); + fullName = nullptr; + } +} + +Locale::Locale() + : UObject(), fullName(fullNameBuffer), baseName(nullptr) +{ + init(nullptr, false); +} + +/* + * Internal constructor to allow construction of a locale object with + * NO side effects. (Default constructor tries to get + * the default locale.) + */ +Locale::Locale(Locale::ELocaleType) + : UObject(), fullName(fullNameBuffer), baseName(nullptr) +{ + setToBogus(); +} + + +Locale::Locale( const char * newLanguage, + const char * newCountry, + const char * newVariant, + const char * newKeywords) + : UObject(), fullName(fullNameBuffer), baseName(nullptr) +{ + if( (newLanguage==nullptr) && (newCountry == nullptr) && (newVariant == nullptr) ) + { + init(nullptr, false); /* shortcut */ + } + else + { + UErrorCode status = U_ZERO_ERROR; + int32_t lsize = 0; + int32_t csize = 0; + int32_t vsize = 0; + int32_t ksize = 0; + + // Check the sizes of the input strings. + + // Language + if ( newLanguage != nullptr ) + { + lsize = (int32_t)uprv_strlen(newLanguage); + if ( lsize < 0 || lsize > ULOC_STRING_LIMIT ) { // int32 wrap + setToBogus(); + return; + } + } + + CharString togo(newLanguage, lsize, status); // start with newLanguage + + // _Country + if ( newCountry != nullptr ) + { + csize = (int32_t)uprv_strlen(newCountry); + if ( csize < 0 || csize > ULOC_STRING_LIMIT ) { // int32 wrap + setToBogus(); + return; + } + } + + // _Variant + if ( newVariant != nullptr ) + { + // remove leading _'s + while(newVariant[0] == SEP_CHAR) + { + newVariant++; + } + + // remove trailing _'s + vsize = (int32_t)uprv_strlen(newVariant); + if ( vsize < 0 || vsize > ULOC_STRING_LIMIT ) { // int32 wrap + setToBogus(); + return; + } + while( (vsize>1) && (newVariant[vsize-1] == SEP_CHAR) ) + { + vsize--; + } + } + + if ( newKeywords != nullptr) + { + ksize = (int32_t)uprv_strlen(newKeywords); + if ( ksize < 0 || ksize > ULOC_STRING_LIMIT ) { + setToBogus(); + return; + } + } + + // We've checked the input sizes, now build up the full locale string.. + + // newLanguage is already copied + + if ( ( vsize != 0 ) || (csize != 0) ) // at least: __v + { // ^ + togo.append(SEP_CHAR, status); + } + + if ( csize != 0 ) + { + togo.append(newCountry, status); + } + + if ( vsize != 0) + { + togo.append(SEP_CHAR, status) + .append(newVariant, vsize, status); + } + + if ( ksize != 0) + { + if (uprv_strchr(newKeywords, '=')) { + togo.append('@', status); /* keyword parsing */ + } + else { + togo.append('_', status); /* Variant parsing with a script */ + if ( vsize == 0) { + togo.append('_', status); /* No country found */ + } + } + togo.append(newKeywords, status); + } + + if (U_FAILURE(status)) { + // Something went wrong with appending, etc. + setToBogus(); + return; + } + // Parse it, because for example 'language' might really be a complete + // string. + init(togo.data(), false); + } +} + +Locale::Locale(const Locale &other) + : UObject(other), fullName(fullNameBuffer), baseName(nullptr) +{ + *this = other; +} + +Locale::Locale(Locale&& other) noexcept + : UObject(other), fullName(fullNameBuffer), baseName(fullName) { + *this = std::move(other); +} + +Locale& Locale::operator=(const Locale& other) { + if (this == &other) { + return *this; + } + + setToBogus(); + + if (other.fullName == other.fullNameBuffer) { + uprv_strcpy(fullNameBuffer, other.fullNameBuffer); + } else if (other.fullName == nullptr) { + fullName = nullptr; + } else { + fullName = uprv_strdup(other.fullName); + if (fullName == nullptr) return *this; + } + + if (other.baseName == other.fullName) { + baseName = fullName; + } else if (other.baseName != nullptr) { + baseName = uprv_strdup(other.baseName); + if (baseName == nullptr) return *this; + } + + uprv_strcpy(language, other.language); + uprv_strcpy(script, other.script); + uprv_strcpy(country, other.country); + + variantBegin = other.variantBegin; + fIsBogus = other.fIsBogus; + + return *this; +} + +Locale& Locale::operator=(Locale&& other) noexcept { + if ((baseName != fullName) && (baseName != fullNameBuffer)) uprv_free(baseName); + if (fullName != fullNameBuffer) uprv_free(fullName); + + if (other.fullName == other.fullNameBuffer || other.baseName == other.fullNameBuffer) { + uprv_strcpy(fullNameBuffer, other.fullNameBuffer); + } + if (other.fullName == other.fullNameBuffer) { + fullName = fullNameBuffer; + } else { + fullName = other.fullName; + } + + if (other.baseName == other.fullNameBuffer) { + baseName = fullNameBuffer; + } else if (other.baseName == other.fullName) { + baseName = fullName; + } else { + baseName = other.baseName; + } + + uprv_strcpy(language, other.language); + uprv_strcpy(script, other.script); + uprv_strcpy(country, other.country); + + variantBegin = other.variantBegin; + fIsBogus = other.fIsBogus; + + other.baseName = other.fullName = other.fullNameBuffer; + + return *this; +} + +Locale * +Locale::clone() const { + return new Locale(*this); +} + +bool +Locale::operator==( const Locale& other) const +{ + return (uprv_strcmp(other.fullName, fullName) == 0); +} + +namespace { + +UInitOnce gKnownCanonicalizedInitOnce {}; +UHashtable *gKnownCanonicalized = nullptr; + +static const char* const KNOWN_CANONICALIZED[] = { + "c", + // Commonly used locales known are already canonicalized + "af", "af_ZA", "am", "am_ET", "ar", "ar_001", "as", "as_IN", "az", "az_AZ", + "be", "be_BY", "bg", "bg_BG", "bn", "bn_IN", "bs", "bs_BA", "ca", "ca_ES", + "cs", "cs_CZ", "cy", "cy_GB", "da", "da_DK", "de", "de_DE", "el", "el_GR", + "en", "en_GB", "en_US", "es", "es_419", "es_ES", "et", "et_EE", "eu", + "eu_ES", "fa", "fa_IR", "fi", "fi_FI", "fil", "fil_PH", "fr", "fr_FR", + "ga", "ga_IE", "gl", "gl_ES", "gu", "gu_IN", "he", "he_IL", "hi", "hi_IN", + "hr", "hr_HR", "hu", "hu_HU", "hy", "hy_AM", "id", "id_ID", "is", "is_IS", + "it", "it_IT", "ja", "ja_JP", "jv", "jv_ID", "ka", "ka_GE", "kk", "kk_KZ", + "km", "km_KH", "kn", "kn_IN", "ko", "ko_KR", "ky", "ky_KG", "lo", "lo_LA", + "lt", "lt_LT", "lv", "lv_LV", "mk", "mk_MK", "ml", "ml_IN", "mn", "mn_MN", + "mr", "mr_IN", "ms", "ms_MY", "my", "my_MM", "nb", "nb_NO", "ne", "ne_NP", + "nl", "nl_NL", "no", "or", "or_IN", "pa", "pa_IN", "pl", "pl_PL", "ps", "ps_AF", + "pt", "pt_BR", "pt_PT", "ro", "ro_RO", "ru", "ru_RU", "sd", "sd_IN", "si", + "si_LK", "sk", "sk_SK", "sl", "sl_SI", "so", "so_SO", "sq", "sq_AL", "sr", + "sr_Cyrl_RS", "sr_Latn", "sr_RS", "sv", "sv_SE", "sw", "sw_TZ", "ta", + "ta_IN", "te", "te_IN", "th", "th_TH", "tk", "tk_TM", "tr", "tr_TR", "uk", + "uk_UA", "ur", "ur_PK", "uz", "uz_UZ", "vi", "vi_VN", "yue", "yue_Hant", + "yue_Hant_HK", "yue_HK", "zh", "zh_CN", "zh_Hans", "zh_Hans_CN", "zh_Hant", + "zh_Hant_TW", "zh_TW", "zu", "zu_ZA" +}; + +static UBool U_CALLCONV cleanupKnownCanonicalized() { + gKnownCanonicalizedInitOnce.reset(); + if (gKnownCanonicalized) { uhash_close(gKnownCanonicalized); } + return true; +} + +static void U_CALLCONV loadKnownCanonicalized(UErrorCode &status) { + ucln_common_registerCleanup(UCLN_COMMON_LOCALE_KNOWN_CANONICALIZED, + cleanupKnownCanonicalized); + LocalUHashtablePointer newKnownCanonicalizedMap( + uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status)); + for (int32_t i = 0; + U_SUCCESS(status) && i < UPRV_LENGTHOF(KNOWN_CANONICALIZED); + i++) { + uhash_puti(newKnownCanonicalizedMap.getAlias(), + (void*)KNOWN_CANONICALIZED[i], + 1, &status); + } + if (U_FAILURE(status)) { + return; + } + + gKnownCanonicalized = newKnownCanonicalizedMap.orphan(); +} + +class AliasData; + +/** + * A Builder class to build the alias data. + */ +class AliasDataBuilder { +public: + AliasDataBuilder() { + } + + // Build the AliasData from resource. + AliasData* build(UErrorCode &status); + +private: + void readAlias(UResourceBundle* alias, + UniqueCharStrings* strings, + LocalMemory& types, + LocalMemory& replacementIndexes, + int32_t &length, + void (*checkType)(const char* type), + void (*checkReplacement)(const UnicodeString& replacement), + UErrorCode &status); + + // Read the languageAlias data from alias to + // strings+types+replacementIndexes + // The number of record will be stored into length. + // Allocate length items for types, to store the type field. + // Allocate length items for replacementIndexes, + // to store the index in the strings for the replacement script. + void readLanguageAlias(UResourceBundle* alias, + UniqueCharStrings* strings, + LocalMemory& types, + LocalMemory& replacementIndexes, + int32_t &length, + UErrorCode &status); + + // Read the scriptAlias data from alias to + // strings+types+replacementIndexes + // Allocate length items for types, to store the type field. + // Allocate length items for replacementIndexes, + // to store the index in the strings for the replacement script. + void readScriptAlias(UResourceBundle* alias, + UniqueCharStrings* strings, + LocalMemory& types, + LocalMemory& replacementIndexes, + int32_t &length, UErrorCode &status); + + // Read the territoryAlias data from alias to + // strings+types+replacementIndexes + // Allocate length items for types, to store the type field. + // Allocate length items for replacementIndexes, + // to store the index in the strings for the replacement script. + void readTerritoryAlias(UResourceBundle* alias, + UniqueCharStrings* strings, + LocalMemory& types, + LocalMemory& replacementIndexes, + int32_t &length, UErrorCode &status); + + // Read the variantAlias data from alias to + // strings+types+replacementIndexes + // Allocate length items for types, to store the type field. + // Allocate length items for replacementIndexes, + // to store the index in the strings for the replacement variant. + void readVariantAlias(UResourceBundle* alias, + UniqueCharStrings* strings, + LocalMemory& types, + LocalMemory& replacementIndexes, + int32_t &length, UErrorCode &status); + + // Read the subdivisionAlias data from alias to + // strings+types+replacementIndexes + // Allocate length items for types, to store the type field. + // Allocate length items for replacementIndexes, + // to store the index in the strings for the replacement variant. + void readSubdivisionAlias(UResourceBundle* alias, + UniqueCharStrings* strings, + LocalMemory& types, + LocalMemory& replacementIndexes, + int32_t &length, UErrorCode &status); +}; + +/** + * A class to hold the Alias Data. + */ +class AliasData : public UMemory { +public: + static const AliasData* singleton(UErrorCode& status) { + if (U_FAILURE(status)) { + // Do not get into loadData if the status already has error. + return nullptr; + } + umtx_initOnce(AliasData::gInitOnce, &AliasData::loadData, status); + return gSingleton; + } + + const CharStringMap& languageMap() const { return language; } + const CharStringMap& scriptMap() const { return script; } + const CharStringMap& territoryMap() const { return territory; } + const CharStringMap& variantMap() const { return variant; } + const CharStringMap& subdivisionMap() const { return subdivision; } + + static void U_CALLCONV loadData(UErrorCode &status); + static UBool U_CALLCONV cleanup(); + + static UInitOnce gInitOnce; + +private: + AliasData(CharStringMap languageMap, + CharStringMap scriptMap, + CharStringMap territoryMap, + CharStringMap variantMap, + CharStringMap subdivisionMap, + CharString* strings) + : language(std::move(languageMap)), + script(std::move(scriptMap)), + territory(std::move(territoryMap)), + variant(std::move(variantMap)), + subdivision(std::move(subdivisionMap)), + strings(strings) { + } + + ~AliasData() { + delete strings; + } + + static const AliasData* gSingleton; + + CharStringMap language; + CharStringMap script; + CharStringMap territory; + CharStringMap variant; + CharStringMap subdivision; + CharString* strings; + + friend class AliasDataBuilder; +}; + + +const AliasData* AliasData::gSingleton = nullptr; +UInitOnce AliasData::gInitOnce {}; + +UBool U_CALLCONV +AliasData::cleanup() +{ + gInitOnce.reset(); + delete gSingleton; + return true; +} + +void +AliasDataBuilder::readAlias( + UResourceBundle* alias, + UniqueCharStrings* strings, + LocalMemory& types, + LocalMemory& replacementIndexes, + int32_t &length, + void (*checkType)(const char* type), + void (*checkReplacement)(const UnicodeString& replacement), + UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + length = ures_getSize(alias); + const char** rawTypes = types.allocateInsteadAndCopy(length); + if (rawTypes == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + int32_t* rawIndexes = replacementIndexes.allocateInsteadAndCopy(length); + if (rawIndexes == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + for (int i = 0; U_SUCCESS(status) && ures_hasNext(alias); i++) { + LocalUResourceBundlePointer res( + ures_getNextResource(alias, nullptr, &status)); + const char* aliasFrom = ures_getKey(res.getAlias()); + UnicodeString aliasTo = + ures_getUnicodeStringByKey(res.getAlias(), "replacement", &status); + if (U_FAILURE(status)) return; + + checkType(aliasFrom); + checkReplacement(aliasTo); + + rawTypes[i] = aliasFrom; + rawIndexes[i] = strings->add(aliasTo, status); + } +} + +/** + * Read the languageAlias data from alias to strings+types+replacementIndexes. + * Allocate length items for types, to store the type field. Allocate length + * items for replacementIndexes, to store the index in the strings for the + * replacement language. + */ +void +AliasDataBuilder::readLanguageAlias( + UResourceBundle* alias, + UniqueCharStrings* strings, + LocalMemory& types, + LocalMemory& replacementIndexes, + int32_t &length, + UErrorCode &status) +{ + return readAlias( + alias, strings, types, replacementIndexes, length, +#if U_DEBUG + [](const char* type) { + // Assert the aliasFrom only contains the following possibilities + // language_REGION_variant + // language_REGION + // language_variant + // language + // und_variant + Locale test(type); + // Assert no script in aliasFrom + U_ASSERT(test.getScript()[0] == '\0'); + // Assert when language is und, no REGION in aliasFrom. + U_ASSERT(test.getLanguage()[0] != '\0' || test.getCountry()[0] == '\0'); + }, +#else + [](const char*) {}, +#endif + [](const UnicodeString&) {}, status); +} + +/** + * Read the scriptAlias data from alias to strings+types+replacementIndexes. + * Allocate length items for types, to store the type field. Allocate length + * items for replacementIndexes, to store the index in the strings for the + * replacement script. + */ +void +AliasDataBuilder::readScriptAlias( + UResourceBundle* alias, + UniqueCharStrings* strings, + LocalMemory& types, + LocalMemory& replacementIndexes, + int32_t &length, + UErrorCode &status) +{ + return readAlias( + alias, strings, types, replacementIndexes, length, +#if U_DEBUG + [](const char* type) { + U_ASSERT(uprv_strlen(type) == 4); + }, + [](const UnicodeString& replacement) { + U_ASSERT(replacement.length() == 4); + }, +#else + [](const char*) {}, + [](const UnicodeString&) { }, +#endif + status); +} + +/** + * Read the territoryAlias data from alias to strings+types+replacementIndexes. + * Allocate length items for types, to store the type field. Allocate length + * items for replacementIndexes, to store the index in the strings for the + * replacement regions. + */ +void +AliasDataBuilder::readTerritoryAlias( + UResourceBundle* alias, + UniqueCharStrings* strings, + LocalMemory& types, + LocalMemory& replacementIndexes, + int32_t &length, + UErrorCode &status) +{ + return readAlias( + alias, strings, types, replacementIndexes, length, +#if U_DEBUG + [](const char* type) { + U_ASSERT(uprv_strlen(type) == 2 || uprv_strlen(type) == 3); + }, +#else + [](const char*) {}, +#endif + [](const UnicodeString&) { }, + status); +} + +/** + * Read the variantAlias data from alias to strings+types+replacementIndexes. + * Allocate length items for types, to store the type field. Allocate length + * items for replacementIndexes, to store the index in the strings for the + * replacement variant. + */ +void +AliasDataBuilder::readVariantAlias( + UResourceBundle* alias, + UniqueCharStrings* strings, + LocalMemory& types, + LocalMemory& replacementIndexes, + int32_t &length, + UErrorCode &status) +{ + return readAlias( + alias, strings, types, replacementIndexes, length, +#if U_DEBUG + [](const char* type) { + U_ASSERT(uprv_strlen(type) >= 4 && uprv_strlen(type) <= 8); + U_ASSERT(uprv_strlen(type) != 4 || + (type[0] >= '0' && type[0] <= '9')); + }, + [](const UnicodeString& replacement) { + U_ASSERT(replacement.length() >= 4 && replacement.length() <= 8); + U_ASSERT(replacement.length() != 4 || + (replacement.charAt(0) >= u'0' && + replacement.charAt(0) <= u'9')); + }, +#else + [](const char*) {}, + [](const UnicodeString&) { }, +#endif + status); +} + +/** + * Read the subdivisionAlias data from alias to strings+types+replacementIndexes. + * Allocate length items for types, to store the type field. Allocate length + * items for replacementIndexes, to store the index in the strings for the + * replacement regions. + */ +void +AliasDataBuilder::readSubdivisionAlias( + UResourceBundle* alias, + UniqueCharStrings* strings, + LocalMemory& types, + LocalMemory& replacementIndexes, + int32_t &length, + UErrorCode &status) +{ + return readAlias( + alias, strings, types, replacementIndexes, length, +#if U_DEBUG + [](const char* type) { + U_ASSERT(uprv_strlen(type) >= 3 && uprv_strlen(type) <= 8); + }, +#else + [](const char*) {}, +#endif + [](const UnicodeString&) { }, + status); +} + +/** + * Initializes the alias data from the ICU resource bundles. The alias data + * contains alias of language, country, script and variants. + * + * If the alias data has already loaded, then this method simply returns without + * doing anything meaningful. + */ +void U_CALLCONV +AliasData::loadData(UErrorCode &status) +{ +#ifdef LOCALE_CANONICALIZATION_DEBUG + UDate start = uprv_getRawUTCtime(); +#endif // LOCALE_CANONICALIZATION_DEBUG + ucln_common_registerCleanup(UCLN_COMMON_LOCALE_ALIAS, cleanup); + AliasDataBuilder builder; + gSingleton = builder.build(status); +#ifdef LOCALE_CANONICALIZATION_DEBUG + UDate end = uprv_getRawUTCtime(); + printf("AliasData::loadData took total %f ms\n", end - start); +#endif // LOCALE_CANONICALIZATION_DEBUG +} + +/** + * Build the alias data from resources. + */ +AliasData* +AliasDataBuilder::build(UErrorCode &status) { + LocalUResourceBundlePointer metadata( + ures_openDirect(nullptr, "metadata", &status)); + LocalUResourceBundlePointer metadataAlias( + ures_getByKey(metadata.getAlias(), "alias", nullptr, &status)); + LocalUResourceBundlePointer languageAlias( + ures_getByKey(metadataAlias.getAlias(), "language", nullptr, &status)); + LocalUResourceBundlePointer scriptAlias( + ures_getByKey(metadataAlias.getAlias(), "script", nullptr, &status)); + LocalUResourceBundlePointer territoryAlias( + ures_getByKey(metadataAlias.getAlias(), "territory", nullptr, &status)); + LocalUResourceBundlePointer variantAlias( + ures_getByKey(metadataAlias.getAlias(), "variant", nullptr, &status)); + LocalUResourceBundlePointer subdivisionAlias( + ures_getByKey(metadataAlias.getAlias(), "subdivision", nullptr, &status)); + + if (U_FAILURE(status)) { + return nullptr; + } + int32_t languagesLength = 0, scriptLength = 0, territoryLength = 0, + variantLength = 0, subdivisionLength = 0; + + // Read the languageAlias into languageTypes, languageReplacementIndexes + // and strings + UniqueCharStrings strings(status); + LocalMemory languageTypes; + LocalMemory languageReplacementIndexes; + readLanguageAlias(languageAlias.getAlias(), + &strings, + languageTypes, + languageReplacementIndexes, + languagesLength, + status); + + // Read the scriptAlias into scriptTypes, scriptReplacementIndexes + // and strings + LocalMemory scriptTypes; + LocalMemory scriptReplacementIndexes; + readScriptAlias(scriptAlias.getAlias(), + &strings, + scriptTypes, + scriptReplacementIndexes, + scriptLength, + status); + + // Read the territoryAlias into territoryTypes, territoryReplacementIndexes + // and strings + LocalMemory territoryTypes; + LocalMemory territoryReplacementIndexes; + readTerritoryAlias(territoryAlias.getAlias(), + &strings, + territoryTypes, + territoryReplacementIndexes, + territoryLength, status); + + // Read the variantAlias into variantTypes, variantReplacementIndexes + // and strings + LocalMemory variantTypes; + LocalMemory variantReplacementIndexes; + readVariantAlias(variantAlias.getAlias(), + &strings, + variantTypes, + variantReplacementIndexes, + variantLength, status); + + // Read the subdivisionAlias into subdivisionTypes, subdivisionReplacementIndexes + // and strings + LocalMemory subdivisionTypes; + LocalMemory subdivisionReplacementIndexes; + readSubdivisionAlias(subdivisionAlias.getAlias(), + &strings, + subdivisionTypes, + subdivisionReplacementIndexes, + subdivisionLength, status); + + if (U_FAILURE(status)) { + return nullptr; + } + + // We can only use strings after freeze it. + strings.freeze(); + + // Build the languageMap from languageTypes & languageReplacementIndexes + CharStringMap languageMap(490, status); + for (int32_t i = 0; U_SUCCESS(status) && i < languagesLength; i++) { + languageMap.put(languageTypes[i], + strings.get(languageReplacementIndexes[i]), + status); + } + + // Build the scriptMap from scriptTypes & scriptReplacementIndexes + CharStringMap scriptMap(1, status); + for (int32_t i = 0; U_SUCCESS(status) && i < scriptLength; i++) { + scriptMap.put(scriptTypes[i], + strings.get(scriptReplacementIndexes[i]), + status); + } + + // Build the territoryMap from territoryTypes & territoryReplacementIndexes + CharStringMap territoryMap(650, status); + for (int32_t i = 0; U_SUCCESS(status) && i < territoryLength; i++) { + territoryMap.put(territoryTypes[i], + strings.get(territoryReplacementIndexes[i]), + status); + } + + // Build the variantMap from variantTypes & variantReplacementIndexes. + CharStringMap variantMap(2, status); + for (int32_t i = 0; U_SUCCESS(status) && i < variantLength; i++) { + variantMap.put(variantTypes[i], + strings.get(variantReplacementIndexes[i]), + status); + } + + // Build the subdivisionMap from subdivisionTypes & subdivisionReplacementIndexes. + CharStringMap subdivisionMap(2, status); + for (int32_t i = 0; U_SUCCESS(status) && i < subdivisionLength; i++) { + subdivisionMap.put(subdivisionTypes[i], + strings.get(subdivisionReplacementIndexes[i]), + status); + } + + if (U_FAILURE(status)) { + return nullptr; + } + + // copy hashtables + auto *data = new AliasData( + std::move(languageMap), + std::move(scriptMap), + std::move(territoryMap), + std::move(variantMap), + std::move(subdivisionMap), + strings.orphanCharStrings()); + + if (data == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return data; +} + +/** + * A class that find the replacement values of locale fields by using AliasData. + */ +class AliasReplacer { +public: + AliasReplacer(UErrorCode status) : + language(nullptr), script(nullptr), region(nullptr), + extensions(nullptr), variants(status), + data(nullptr) { + } + ~AliasReplacer() { + } + + // Check the fields inside locale, if need to replace fields, + // place the the replaced locale ID in out and return true. + // Otherwise return false for no replacement or error. + bool replace( + const Locale& locale, CharString& out, UErrorCode& status); + +private: + const char* language; + const char* script; + const char* region; + const char* extensions; + UVector variants; + + const AliasData* data; + + inline bool notEmpty(const char* str) { + return str && str[0] != NULL_CHAR; + } + + /** + * If replacement is neither null nor empty and input is either null or empty, + * return replacement. + * If replacement is neither null nor empty but input is not empty, return input. + * If replacement is either null or empty and type is either null or empty, + * return input. + * Otherwise return null. + * replacement input type return + * AAA nullptr * AAA + * AAA BBB * BBB + * nullptr || "" CCC nullptr CCC + * nullptr || "" * DDD nullptr + */ + inline const char* deleteOrReplace( + const char* input, const char* type, const char* replacement) { + return notEmpty(replacement) ? + ((input == nullptr) ? replacement : input) : + ((type == nullptr) ? input : nullptr); + } + + inline bool same(const char* a, const char* b) { + if (a == nullptr && b == nullptr) { + return true; + } + if ((a == nullptr && b != nullptr) || + (a != nullptr && b == nullptr)) { + return false; + } + return uprv_strcmp(a, b) == 0; + } + + // Gather fields and generate locale ID into out. + CharString& outputToString(CharString& out, UErrorCode status); + + // Generate the lookup key. + CharString& generateKey(const char* language, const char* region, + const char* variant, CharString& out, + UErrorCode status); + + void parseLanguageReplacement(const char* replacement, + const char*& replaceLanguage, + const char*& replaceScript, + const char*& replaceRegion, + const char*& replaceVariant, + const char*& replaceExtensions, + UVector& toBeFreed, + UErrorCode& status); + + // Replace by using languageAlias. + bool replaceLanguage(bool checkLanguage, bool checkRegion, + bool checkVariants, UVector& toBeFreed, + UErrorCode& status); + + // Replace by using territoryAlias. + bool replaceTerritory(UVector& toBeFreed, UErrorCode& status); + + // Replace by using scriptAlias. + bool replaceScript(UErrorCode& status); + + // Replace by using variantAlias. + bool replaceVariant(UErrorCode& status); + + // Replace by using subdivisionAlias. + bool replaceSubdivision(StringPiece subdivision, + CharString& output, UErrorCode& status); + + // Replace transformed extensions. + bool replaceTransformedExtensions( + CharString& transformedExtensions, CharString& output, UErrorCode& status); +}; + +CharString& +AliasReplacer::generateKey( + const char* language, const char* region, const char* variant, + CharString& out, UErrorCode status) +{ + out.append(language, status); + if (notEmpty(region)) { + out.append(SEP_CHAR, status) + .append(region, status); + } + if (notEmpty(variant)) { + out.append(SEP_CHAR, status) + .append(variant, status); + } + return out; +} + +void +AliasReplacer::parseLanguageReplacement( + const char* replacement, + const char*& replacedLanguage, + const char*& replacedScript, + const char*& replacedRegion, + const char*& replacedVariant, + const char*& replacedExtensions, + UVector& toBeFreed, + UErrorCode& status) +{ + if (U_FAILURE(status)) { + return; + } + replacedScript = replacedRegion = replacedVariant + = replacedExtensions = nullptr; + if (uprv_strchr(replacement, '_') == nullptr) { + replacedLanguage = replacement; + // reach the end, just return it. + return; + } + // We have multiple field so we have to allocate and parse + CharString* str = new CharString( + replacement, (int32_t)uprv_strlen(replacement), status); + LocalPointer lpStr(str, status); + toBeFreed.adoptElement(lpStr.orphan(), status); + if (U_FAILURE(status)) { + return; + } + char* data = str->data(); + replacedLanguage = (const char*) data; + char* endOfField = uprv_strchr(data, '_'); + *endOfField = '\0'; // null terminiate it. + endOfField++; + const char* start = endOfField; + endOfField = (char*) uprv_strchr(start, '_'); + size_t len = 0; + if (endOfField == nullptr) { + len = uprv_strlen(start); + } else { + len = endOfField - start; + *endOfField = '\0'; // null terminiate it. + } + if (len == 4 && uprv_isASCIILetter(*start)) { + // Got a script + replacedScript = start; + if (endOfField == nullptr) { + return; + } + start = endOfField++; + endOfField = (char*)uprv_strchr(start, '_'); + if (endOfField == nullptr) { + len = uprv_strlen(start); + } else { + len = endOfField - start; + *endOfField = '\0'; // null terminiate it. + } + } + if (len >= 2 && len <= 3) { + // Got a region + replacedRegion = start; + if (endOfField == nullptr) { + return; + } + start = endOfField++; + endOfField = (char*)uprv_strchr(start, '_'); + if (endOfField == nullptr) { + len = uprv_strlen(start); + } else { + len = endOfField - start; + *endOfField = '\0'; // null terminiate it. + } + } + if (len >= 4) { + // Got a variant + replacedVariant = start; + if (endOfField == nullptr) { + return; + } + start = endOfField++; + } + replacedExtensions = start; +} + +bool +AliasReplacer::replaceLanguage( + bool checkLanguage, bool checkRegion, + bool checkVariants, UVector& toBeFreed, UErrorCode& status) +{ + if (U_FAILURE(status)) { + return false; + } + if ( (checkRegion && region == nullptr) || + (checkVariants && variants.size() == 0)) { + // Nothing to search. + return false; + } + int32_t variant_size = checkVariants ? variants.size() : 1; + // Since we may have more than one variant, we need to loop through them. + const char* searchLanguage = checkLanguage ? language : "und"; + const char* searchRegion = checkRegion ? region : nullptr; + const char* searchVariant = nullptr; + for (int32_t variant_index = 0; + variant_index < variant_size; + variant_index++) { + if (checkVariants) { + U_ASSERT(variant_index < variant_size); + searchVariant = (const char*)(variants.elementAt(variant_index)); + } + + if (searchVariant != nullptr && uprv_strlen(searchVariant) < 4) { + // Do not consider ill-formed variant subtag. + searchVariant = nullptr; + } + CharString typeKey; + generateKey(searchLanguage, searchRegion, searchVariant, typeKey, + status); + if (U_FAILURE(status)) { + return false; + } + const char *replacement = data->languageMap().get(typeKey.data()); + if (replacement == nullptr) { + // Found no replacement data. + continue; + } + + const char* replacedLanguage = nullptr; + const char* replacedScript = nullptr; + const char* replacedRegion = nullptr; + const char* replacedVariant = nullptr; + const char* replacedExtensions = nullptr; + parseLanguageReplacement(replacement, + replacedLanguage, + replacedScript, + replacedRegion, + replacedVariant, + replacedExtensions, + toBeFreed, + status); + replacedLanguage = + (replacedLanguage != nullptr && uprv_strcmp(replacedLanguage, "und") == 0) ? + language : replacedLanguage; + replacedScript = deleteOrReplace(script, nullptr, replacedScript); + replacedRegion = deleteOrReplace(region, searchRegion, replacedRegion); + replacedVariant = deleteOrReplace( + searchVariant, searchVariant, replacedVariant); + + if ( same(language, replacedLanguage) && + same(script, replacedScript) && + same(region, replacedRegion) && + same(searchVariant, replacedVariant) && + replacedExtensions == nullptr) { + // Replacement produce no changes. + continue; + } + + language = replacedLanguage; + region = replacedRegion; + script = replacedScript; + if (searchVariant != nullptr) { + if (notEmpty(replacedVariant)) { + variants.setElementAt((void*)replacedVariant, variant_index); + } else { + variants.removeElementAt(variant_index); + } + } + if (replacedExtensions != nullptr) { + // DO NOTHING + // UTS35 does not specify what should we do if we have extensions in the + // replacement. Currently we know only the following 4 "BCP47 LegacyRules" have + // extensions in them languageAlias: + // i_default => en_x_i_default + // i_enochian => und_x_i_enochian + // i_mingo => see_x_i_mingo + // zh_min => nan_x_zh_min + // But all of them are already changed by code inside ultag_parse() before + // hitting this code. + } + + // Something changed by language alias data. + return true; + } + // Nothing changed by language alias data. + return false; +} + +bool +AliasReplacer::replaceTerritory(UVector& toBeFreed, UErrorCode& status) +{ + if (U_FAILURE(status)) { + return false; + } + if (region == nullptr) { + // No region to search. + return false; + } + const char *replacement = data->territoryMap().get(region); + if (replacement == nullptr) { + // Found no replacement data for this region. + return false; + } + const char* replacedRegion = replacement; + const char* firstSpace = uprv_strchr(replacement, ' '); + if (firstSpace != nullptr) { + // If there are are more than one region in the replacement. + // We need to check which one match based on the language. + // Cannot use nullptr for language because that will construct + // the default locale, in that case, use "und" to get the correct + // locale. + Locale l = LocaleBuilder() + .setLanguage(language == nullptr ? "und" : language) + .setScript(script) + .build(status); + l.addLikelySubtags(status); + const char* likelyRegion = l.getCountry(); + LocalPointer item; + if (likelyRegion != nullptr && uprv_strlen(likelyRegion) > 0) { + size_t len = uprv_strlen(likelyRegion); + const char* foundInReplacement = uprv_strstr(replacement, + likelyRegion); + if (foundInReplacement != nullptr) { + // Assuming the case there are no three letter region code in + // the replacement of territoryAlias + U_ASSERT(foundInReplacement == replacement || + *(foundInReplacement-1) == ' '); + U_ASSERT(foundInReplacement[len] == ' ' || + foundInReplacement[len] == '\0'); + item.adoptInsteadAndCheckErrorCode( + new CharString(foundInReplacement, (int32_t)len, status), status); + } + } + if (item.isNull() && U_SUCCESS(status)) { + item.adoptInsteadAndCheckErrorCode( + new CharString(replacement, + (int32_t)(firstSpace - replacement), status), status); + } + if (U_FAILURE(status)) { return false; } + replacedRegion = item->data(); + toBeFreed.adoptElement(item.orphan(), status); + if (U_FAILURE(status)) { return false; } + } + U_ASSERT(!same(region, replacedRegion)); + region = replacedRegion; + // The region is changed by data in territory alias. + return true; +} + +bool +AliasReplacer::replaceScript(UErrorCode& status) +{ + if (U_FAILURE(status)) { + return false; + } + if (script == nullptr) { + // No script to search. + return false; + } + const char *replacement = data->scriptMap().get(script); + if (replacement == nullptr) { + // Found no replacement data for this script. + return false; + } + U_ASSERT(!same(script, replacement)); + script = replacement; + // The script is changed by data in script alias. + return true; +} + +bool +AliasReplacer::replaceVariant(UErrorCode& status) +{ + if (U_FAILURE(status)) { + return false; + } + // Since we may have more than one variant, we need to loop through them. + for (int32_t i = 0; i < variants.size(); i++) { + const char *variant = (const char*)(variants.elementAt(i)); + const char *replacement = data->variantMap().get(variant); + if (replacement == nullptr) { + // Found no replacement data for this variant. + continue; + } + U_ASSERT((uprv_strlen(replacement) >= 5 && + uprv_strlen(replacement) <= 8) || + (uprv_strlen(replacement) == 4 && + replacement[0] >= '0' && + replacement[0] <= '9')); + if (!same(variant, replacement)) { + variants.setElementAt((void*)replacement, i); + // Special hack to handle hepburn-heploc => alalc97 + if (uprv_strcmp(variant, "heploc") == 0) { + for (int32_t j = 0; j < variants.size(); j++) { + if (uprv_strcmp((const char*)(variants.elementAt(j)), + "hepburn") == 0) { + variants.removeElementAt(j); + } + } + } + return true; + } + } + return false; +} + +bool +AliasReplacer::replaceSubdivision( + StringPiece subdivision, CharString& output, UErrorCode& status) +{ + if (U_FAILURE(status)) { + return false; + } + const char *replacement = data->subdivisionMap().get(subdivision.data()); + if (replacement != nullptr) { + const char* firstSpace = uprv_strchr(replacement, ' '); + // Found replacement data for this subdivision. + size_t len = (firstSpace != nullptr) ? + (firstSpace - replacement) : uprv_strlen(replacement); + if (2 <= len && len <= 8) { + output.append(replacement, (int32_t)len, status); + if (2 == len) { + // Add 'zzzz' based on changes to UTS #35 for CLDR-14312. + output.append("zzzz", 4, status); + } + } + return true; + } + return false; +} + +bool +AliasReplacer::replaceTransformedExtensions( + CharString& transformedExtensions, CharString& output, UErrorCode& status) +{ + // The content of the transformedExtensions will be modified in this + // function to NUL-terminating (tkey-tvalue) pairs. + if (U_FAILURE(status)) { + return false; + } + int32_t len = transformedExtensions.length(); + const char* str = transformedExtensions.data(); + const char* tkey = ultag_getTKeyStart(str); + int32_t tlangLen = (tkey == str) ? 0 : + ((tkey == nullptr) ? len : static_cast((tkey - str - 1))); + CharStringByteSink sink(&output); + if (tlangLen > 0) { + Locale tlang = LocaleBuilder() + .setLanguageTag(StringPiece(str, tlangLen)) + .build(status); + tlang.canonicalize(status); + tlang.toLanguageTag(sink, status); + if (U_FAILURE(status)) { + return false; + } + T_CString_toLowerCase(output.data()); + } + if (tkey != nullptr) { + // We need to sort the tfields by tkey + UVector tfields(status); + if (U_FAILURE(status)) { + return false; + } + do { + const char* tvalue = uprv_strchr(tkey, '-'); + if (tvalue == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + const char* nextTKey = ultag_getTKeyStart(tvalue); + if (nextTKey != nullptr) { + *((char*)(nextTKey-1)) = '\0'; // NUL terminate tvalue + } + tfields.insertElementAt((void*)tkey, tfields.size(), status); + if (U_FAILURE(status)) { + return false; + } + tkey = nextTKey; + } while (tkey != nullptr); + tfields.sort([](UElement e1, UElement e2) -> int32_t { + return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer); + }, status); + for (int32_t i = 0; i < tfields.size(); i++) { + if (output.length() > 0) { + output.append('-', status); + } + const char* tfield = (const char*) tfields.elementAt(i); + const char* tvalue = uprv_strchr(tfield, '-'); + if (tvalue == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + // Split the "tkey-tvalue" pair string so that we can canonicalize the tvalue. + *((char*)tvalue++) = '\0'; // NUL terminate tkey + output.append(tfield, status).append('-', status); + const char* bcpTValue = ulocimp_toBcpType(tfield, tvalue, nullptr, nullptr); + output.append((bcpTValue == nullptr) ? tvalue : bcpTValue, status); + } + } + if (U_FAILURE(status)) { + return false; + } + return true; +} + +CharString& +AliasReplacer::outputToString( + CharString& out, UErrorCode status) +{ + out.append(language, status); + if (notEmpty(script)) { + out.append(SEP_CHAR, status) + .append(script, status); + } + if (notEmpty(region)) { + out.append(SEP_CHAR, status) + .append(region, status); + } + if (variants.size() > 0) { + if (!notEmpty(script) && !notEmpty(region)) { + out.append(SEP_CHAR, status); + } + variants.sort([](UElement e1, UElement e2) -> int32_t { + return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer); + }, status); + int32_t variantsStart = out.length(); + for (int32_t i = 0; i < variants.size(); i++) { + out.append(SEP_CHAR, status) + .append((const char*)(variants.elementAt(i)), + status); + } + T_CString_toUpperCase(out.data() + variantsStart); + } + if (notEmpty(extensions)) { + CharString tmp("und_", status); + tmp.append(extensions, status); + Locale tmpLocale(tmp.data()); + // only support x extension inside CLDR for now. + U_ASSERT(extensions[0] == 'x'); + out.append(tmpLocale.getName() + 1, status); + } + return out; +} + +bool +AliasReplacer::replace(const Locale& locale, CharString& out, UErrorCode& status) +{ + data = AliasData::singleton(status); + if (U_FAILURE(status)) { + return false; + } + U_ASSERT(data != nullptr); + out.clear(); + language = locale.getLanguage(); + if (!notEmpty(language)) { + language = nullptr; + } + script = locale.getScript(); + if (!notEmpty(script)) { + script = nullptr; + } + region = locale.getCountry(); + if (!notEmpty(region)) { + region = nullptr; + } + const char* variantsStr = locale.getVariant(); + CharString variantsBuff(variantsStr, -1, status); + if (!variantsBuff.isEmpty()) { + if (U_FAILURE(status)) { return false; } + char* start = variantsBuff.data(); + T_CString_toLowerCase(start); + char* end; + while ((end = uprv_strchr(start, SEP_CHAR)) != nullptr && + U_SUCCESS(status)) { + *end = NULL_CHAR; // null terminate inside variantsBuff + variants.addElement(start, status); + start = end + 1; + } + variants.addElement(start, status); + } + if (U_FAILURE(status)) { return false; } + + // Sort the variants + variants.sort([](UElement e1, UElement e2) -> int32_t { + return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer); + }, status); + + // A changed count to assert when loop too many times. + int changed = 0; + // A UVector to to hold CharString allocated by the replace* method + // and freed when out of scope from his function. + UVector stringsToBeFreed([](void *obj){ delete ((CharString*) obj); }, + nullptr, 10, status); + while (U_SUCCESS(status)) { + // Something wrong with the data cause looping here more than 10 times + // already. + U_ASSERT(changed < 5); + // From observation of key in data/misc/metadata.txt + // we know currently we only need to search in the following combination + // of fields for type in languageAlias: + // * lang_region_variant + // * lang_region + // * lang_variant + // * lang + // * und_variant + // This assumption is ensured by the U_ASSERT in readLanguageAlias + // + // lang REGION variant + if ( replaceLanguage(true, true, true, stringsToBeFreed, status) || + replaceLanguage(true, true, false, stringsToBeFreed, status) || + replaceLanguage(true, false, true, stringsToBeFreed, status) || + replaceLanguage(true, false, false, stringsToBeFreed, status) || + replaceLanguage(false,false, true, stringsToBeFreed, status) || + replaceTerritory(stringsToBeFreed, status) || + replaceScript(status) || + replaceVariant(status)) { + // Some values in data is changed, try to match from the beginning + // again. + changed++; + continue; + } + // Nothing changed. Break out. + break; + } // while(1) + + if (U_FAILURE(status)) { return false; } + // Nothing changed and we know the order of the variants are not change + // because we have no variant or only one. + const char* extensionsStr = locale_getKeywordsStart(locale.getName()); + if (changed == 0 && variants.size() <= 1 && extensionsStr == nullptr) { + return false; + } + outputToString(out, status); + if (U_FAILURE(status)) { + return false; + } + if (extensionsStr != nullptr) { + changed = 0; + Locale temp(locale); + LocalPointer iter(locale.createKeywords(status)); + if (U_SUCCESS(status) && !iter.isNull()) { + const char* key; + while ((key = iter->next(nullptr, status)) != nullptr) { + if (uprv_strcmp("sd", key) == 0 || uprv_strcmp("rg", key) == 0 || + uprv_strcmp("t", key) == 0) { + CharString value; + CharStringByteSink valueSink(&value); + locale.getKeywordValue(key, valueSink, status); + if (U_FAILURE(status)) { + status = U_ZERO_ERROR; + continue; + } + CharString replacement; + if (uprv_strlen(key) == 2) { + if (replaceSubdivision(value.toStringPiece(), replacement, status)) { + changed++; + temp.setKeywordValue(key, replacement.data(), status); + } + } else { + U_ASSERT(uprv_strcmp(key, "t") == 0); + if (replaceTransformedExtensions(value, replacement, status)) { + changed++; + temp.setKeywordValue(key, replacement.data(), status); + } + } + if (U_FAILURE(status)) { + return false; + } + } + } + } + if (changed != 0) { + extensionsStr = locale_getKeywordsStart(temp.getName()); + } + out.append(extensionsStr, status); + } + if (U_FAILURE(status)) { + return false; + } + // If the tag is not changed, return. + if (uprv_strcmp(out.data(), locale.getName()) == 0) { + out.clear(); + return false; + } + return true; +} + +// Return true if the locale is changed during canonicalization. +// The replaced value then will be put into out. +bool +canonicalizeLocale(const Locale& locale, CharString& out, UErrorCode& status) +{ + AliasReplacer replacer(status); + return replacer.replace(locale, out, status); +} + +// Function to optimize for known cases without so we can skip the loading +// of resources in the startup time until we really need it. +bool +isKnownCanonicalizedLocale(const char* locale, UErrorCode& status) +{ + if ( uprv_strcmp(locale, "c") == 0 || + uprv_strcmp(locale, "en") == 0 || + uprv_strcmp(locale, "en_US") == 0) { + return true; + } + + // common well-known Canonicalized. + umtx_initOnce(gKnownCanonicalizedInitOnce, + &loadKnownCanonicalized, status); + if (U_FAILURE(status)) { + return false; + } + U_ASSERT(gKnownCanonicalized != nullptr); + return uhash_geti(gKnownCanonicalized, locale) != 0; +} + +} // namespace + +// Function for testing. +U_CAPI const char* const* +ulocimp_getKnownCanonicalizedLocaleForTest(int32_t* length) +{ + *length = UPRV_LENGTHOF(KNOWN_CANONICALIZED); + return KNOWN_CANONICALIZED; +} + +// Function for testing. +U_CAPI bool +ulocimp_isCanonicalizedLocaleForTest(const char* localeName) +{ + Locale l(localeName); + UErrorCode status = U_ZERO_ERROR; + CharString temp; + return !canonicalizeLocale(l, temp, status) && U_SUCCESS(status); +} + +/*This function initializes a Locale from a C locale ID*/ +Locale& Locale::init(const char* localeID, UBool canonicalize) +{ + fIsBogus = false; + /* Free our current storage */ + if ((baseName != fullName) && (baseName != fullNameBuffer)) { + uprv_free(baseName); + } + baseName = nullptr; + if(fullName != fullNameBuffer) { + uprv_free(fullName); + fullName = fullNameBuffer; + } + + // not a loop: + // just an easy way to have a common error-exit + // without goto and without another function + do { + char *separator; + char *field[5] = {0}; + int32_t fieldLen[5] = {0}; + int32_t fieldIdx; + int32_t variantField; + int32_t length; + UErrorCode err; + + if(localeID == nullptr) { + // not an error, just set the default locale + return *this = getDefault(); + } + + /* preset all fields to empty */ + language[0] = script[0] = country[0] = 0; + + // "canonicalize" the locale ID to ICU/Java format + err = U_ZERO_ERROR; + length = canonicalize ? + uloc_canonicalize(localeID, fullName, sizeof(fullNameBuffer), &err) : + uloc_getName(localeID, fullName, sizeof(fullNameBuffer), &err); + + if(err == U_BUFFER_OVERFLOW_ERROR || length >= (int32_t)sizeof(fullNameBuffer)) { + U_ASSERT(baseName == nullptr); + /*Go to heap for the fullName if necessary*/ + fullName = (char *)uprv_malloc(sizeof(char)*(length + 1)); + if(fullName == 0) { + fullName = fullNameBuffer; + break; // error: out of memory + } + err = U_ZERO_ERROR; + length = canonicalize ? + uloc_canonicalize(localeID, fullName, length+1, &err) : + uloc_getName(localeID, fullName, length+1, &err); + } + if(U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) { + /* should never occur */ + break; + } + + variantBegin = length; + + /* after uloc_getName/canonicalize() we know that only '_' are separators */ + /* But _ could also appeared in timezone such as "en@timezone=America/Los_Angeles" */ + separator = field[0] = fullName; + fieldIdx = 1; + char* at = uprv_strchr(fullName, '@'); + while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR)) != 0 && + fieldIdx < UPRV_LENGTHOF(field)-1 && + (at == nullptr || separator < at)) { + field[fieldIdx] = separator + 1; + fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]); + fieldIdx++; + } + // variant may contain @foo or .foo POSIX cruft; remove it + separator = uprv_strchr(field[fieldIdx-1], '@'); + char* sep2 = uprv_strchr(field[fieldIdx-1], '.'); + if (separator!=nullptr || sep2!=nullptr) { + if (separator==nullptr || (sep2!=nullptr && separator > sep2)) { + separator = sep2; + } + fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]); + } else { + fieldLen[fieldIdx-1] = length - (int32_t)(field[fieldIdx-1] - fullName); + } + + if (fieldLen[0] >= (int32_t)(sizeof(language))) + { + break; // error: the language field is too long + } + + variantField = 1; /* Usually the 2nd one, except when a script or country is also used. */ + if (fieldLen[0] > 0) { + /* We have a language */ + uprv_memcpy(language, fullName, fieldLen[0]); + language[fieldLen[0]] = 0; + } + if (fieldLen[1] == 4 && uprv_isASCIILetter(field[1][0]) && + uprv_isASCIILetter(field[1][1]) && uprv_isASCIILetter(field[1][2]) && + uprv_isASCIILetter(field[1][3])) { + /* We have at least a script */ + uprv_memcpy(script, field[1], fieldLen[1]); + script[fieldLen[1]] = 0; + variantField++; + } + + if (fieldLen[variantField] == 2 || fieldLen[variantField] == 3) { + /* We have a country */ + uprv_memcpy(country, field[variantField], fieldLen[variantField]); + country[fieldLen[variantField]] = 0; + variantField++; + } else if (fieldLen[variantField] == 0) { + variantField++; /* script or country empty but variant in next field (i.e. en__POSIX) */ + } + + if (fieldLen[variantField] > 0) { + /* We have a variant */ + variantBegin = (int32_t)(field[variantField] - fullName); + } + + err = U_ZERO_ERROR; + initBaseName(err); + if (U_FAILURE(err)) { + break; + } + + if (canonicalize) { + if (!isKnownCanonicalizedLocale(fullName, err)) { + CharString replaced; + // Not sure it is already canonicalized + if (canonicalizeLocale(*this, replaced, err)) { + U_ASSERT(U_SUCCESS(err)); + // If need replacement, call init again. + init(replaced.data(), false); + } + if (U_FAILURE(err)) { + break; + } + } + } // if (canonicalize) { + + // successful end of init() + return *this; + } while(0); /*loop doesn't iterate*/ + + // when an error occurs, then set this object to "bogus" (there is no UErrorCode here) + setToBogus(); + + return *this; +} + +/* + * Set up the base name. + * If there are no key words, it's exactly the full name. + * If key words exist, it's the full name truncated at the '@' character. + * Need to set up both at init() and after setting a keyword. + */ +void +Locale::initBaseName(UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + U_ASSERT(baseName==nullptr || baseName==fullName); + const char *atPtr = uprv_strchr(fullName, '@'); + const char *eqPtr = uprv_strchr(fullName, '='); + if (atPtr && eqPtr && atPtr < eqPtr) { + // Key words exist. + int32_t baseNameLength = (int32_t)(atPtr - fullName); + baseName = (char *)uprv_malloc(baseNameLength + 1); + if (baseName == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_strncpy(baseName, fullName, baseNameLength); + baseName[baseNameLength] = 0; + + // The original computation of variantBegin leaves it equal to the length + // of fullName if there is no variant. It should instead be + // the length of the baseName. + if (variantBegin > baseNameLength) { + variantBegin = baseNameLength; + } + } else { + baseName = fullName; + } +} + + +int32_t +Locale::hashCode() const +{ + return ustr_hashCharsN(fullName, static_cast(uprv_strlen(fullName))); +} + +void +Locale::setToBogus() { + /* Free our current storage */ + if((baseName != fullName) && (baseName != fullNameBuffer)) { + uprv_free(baseName); + } + baseName = nullptr; + if(fullName != fullNameBuffer) { + uprv_free(fullName); + fullName = fullNameBuffer; + } + *fullNameBuffer = 0; + *language = 0; + *script = 0; + *country = 0; + fIsBogus = true; + variantBegin = 0; +} + +const Locale& U_EXPORT2 +Locale::getDefault() +{ + { + Mutex lock(&gDefaultLocaleMutex); + if (gDefaultLocale != nullptr) { + return *gDefaultLocale; + } + } + UErrorCode status = U_ZERO_ERROR; + return *locale_set_default_internal(nullptr, status); +} + + + +void U_EXPORT2 +Locale::setDefault( const Locale& newLocale, + UErrorCode& status) +{ + if (U_FAILURE(status)) { + return; + } + + /* Set the default from the full name string of the supplied locale. + * This is a convenient way to access the default locale caching mechanisms. + */ + const char *localeID = newLocale.getName(); + locale_set_default_internal(localeID, status); +} + +void +Locale::addLikelySubtags(UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + + CharString maximizedLocaleID; + { + CharStringByteSink sink(&maximizedLocaleID); + ulocimp_addLikelySubtags(fullName, sink, &status); + } + + if (U_FAILURE(status)) { + return; + } + + init(maximizedLocaleID.data(), /*canonicalize=*/false); + if (isBogus()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } +} + +void +Locale::minimizeSubtags(UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + + CharString minimizedLocaleID; + { + CharStringByteSink sink(&minimizedLocaleID); + ulocimp_minimizeSubtags(fullName, sink, &status); + } + + if (U_FAILURE(status)) { + return; + } + + init(minimizedLocaleID.data(), /*canonicalize=*/false); + if (isBogus()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } +} + +void +Locale::canonicalize(UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (isBogus()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + CharString uncanonicalized(fullName, status); + if (U_FAILURE(status)) { + return; + } + init(uncanonicalized.data(), /*canonicalize=*/true); + if (isBogus()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } +} + +Locale U_EXPORT2 +Locale::forLanguageTag(StringPiece tag, UErrorCode& status) +{ + Locale result(Locale::eBOGUS); + + if (U_FAILURE(status)) { + return result; + } + + // If a BCP 47 language tag is passed as the language parameter to the + // normal Locale constructor, it will actually fall back to invoking + // uloc_forLanguageTag() to parse it if it somehow is able to detect that + // the string actually is BCP 47. This works well for things like strings + // using BCP 47 extensions, but it does not at all work for things like + // legacy language tags (marked as “Type: grandfathered” in BCP 47, + // e.g., "en-GB-oed") which are possible to also + // interpret as ICU locale IDs and because of that won't trigger the BCP 47 + // parsing. Therefore the code here explicitly calls uloc_forLanguageTag() + // and then Locale::init(), instead of just calling the normal constructor. + + CharString localeID; + int32_t parsedLength; + { + CharStringByteSink sink(&localeID); + ulocimp_forLanguageTag( + tag.data(), + tag.length(), + sink, + &parsedLength, + &status); + } + + if (U_FAILURE(status)) { + return result; + } + + if (parsedLength != tag.size()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return result; + } + + result.init(localeID.data(), /*canonicalize=*/false); + if (result.isBogus()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } + return result; +} + +void +Locale::toLanguageTag(ByteSink& sink, UErrorCode& status) const +{ + if (U_FAILURE(status)) { + return; + } + + if (fIsBogus) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + ulocimp_toLanguageTag(fullName, sink, /*strict=*/false, &status); +} + +Locale U_EXPORT2 +Locale::createFromName (const char *name) +{ + if (name) { + Locale l(""); + l.init(name, false); + return l; + } + else { + return getDefault(); + } +} + +Locale U_EXPORT2 +Locale::createCanonical(const char* name) { + Locale loc(""); + loc.init(name, true); + return loc; +} + +const char * +Locale::getISO3Language() const +{ + return uloc_getISO3Language(fullName); +} + + +const char * +Locale::getISO3Country() const +{ + return uloc_getISO3Country(fullName); +} + +/** + * Return the LCID value as specified in the "LocaleID" resource for this + * locale. The LocaleID must be expressed as a hexadecimal number, from + * one to four digits. If the LocaleID resource is not present, or is + * in an incorrect format, 0 is returned. The LocaleID is for use in + * Windows (it is an LCID), but is available on all platforms. + */ +uint32_t +Locale::getLCID() const +{ + return uloc_getLCID(fullName); +} + +const char* const* U_EXPORT2 Locale::getISOCountries() +{ + return uloc_getISOCountries(); +} + +const char* const* U_EXPORT2 Locale::getISOLanguages() +{ + return uloc_getISOLanguages(); +} + +// Set the locale's data based on a posix id. +void Locale::setFromPOSIXID(const char *posixID) +{ + init(posixID, true); +} + +const Locale & U_EXPORT2 +Locale::getRoot() +{ + return getLocale(eROOT); +} + +const Locale & U_EXPORT2 +Locale::getEnglish() +{ + return getLocale(eENGLISH); +} + +const Locale & U_EXPORT2 +Locale::getFrench() +{ + return getLocale(eFRENCH); +} + +const Locale & U_EXPORT2 +Locale::getGerman() +{ + return getLocale(eGERMAN); +} + +const Locale & U_EXPORT2 +Locale::getItalian() +{ + return getLocale(eITALIAN); +} + +const Locale & U_EXPORT2 +Locale::getJapanese() +{ + return getLocale(eJAPANESE); +} + +const Locale & U_EXPORT2 +Locale::getKorean() +{ + return getLocale(eKOREAN); +} + +const Locale & U_EXPORT2 +Locale::getChinese() +{ + return getLocale(eCHINESE); +} + +const Locale & U_EXPORT2 +Locale::getSimplifiedChinese() +{ + return getLocale(eCHINA); +} + +const Locale & U_EXPORT2 +Locale::getTraditionalChinese() +{ + return getLocale(eTAIWAN); +} + + +const Locale & U_EXPORT2 +Locale::getFrance() +{ + return getLocale(eFRANCE); +} + +const Locale & U_EXPORT2 +Locale::getGermany() +{ + return getLocale(eGERMANY); +} + +const Locale & U_EXPORT2 +Locale::getItaly() +{ + return getLocale(eITALY); +} + +const Locale & U_EXPORT2 +Locale::getJapan() +{ + return getLocale(eJAPAN); +} + +const Locale & U_EXPORT2 +Locale::getKorea() +{ + return getLocale(eKOREA); +} + +const Locale & U_EXPORT2 +Locale::getChina() +{ + return getLocale(eCHINA); +} + +const Locale & U_EXPORT2 +Locale::getPRC() +{ + return getLocale(eCHINA); +} + +const Locale & U_EXPORT2 +Locale::getTaiwan() +{ + return getLocale(eTAIWAN); +} + +const Locale & U_EXPORT2 +Locale::getUK() +{ + return getLocale(eUK); +} + +const Locale & U_EXPORT2 +Locale::getUS() +{ + return getLocale(eUS); +} + +const Locale & U_EXPORT2 +Locale::getCanada() +{ + return getLocale(eCANADA); +} + +const Locale & U_EXPORT2 +Locale::getCanadaFrench() +{ + return getLocale(eCANADA_FRENCH); +} + +const Locale & +Locale::getLocale(int locid) +{ + Locale *localeCache = getLocaleCache(); + U_ASSERT((locid < eMAX_LOCALES)&&(locid>=0)); + if (localeCache == nullptr) { + // Failure allocating the locale cache. + // The best we can do is return a nullptr reference. + locid = 0; + } + return localeCache[locid]; /*operating on nullptr*/ +} + +/* +This function is defined this way in order to get around static +initialization and static destruction. + */ +Locale * +Locale::getLocaleCache() +{ + UErrorCode status = U_ZERO_ERROR; + umtx_initOnce(gLocaleCacheInitOnce, locale_init, status); + return gLocaleCache; +} + +class KeywordEnumeration : public StringEnumeration { +private: + char *keywords; + char *current; + int32_t length; + UnicodeString currUSKey; + static const char fgClassID;/* Warning this is used beyond the typical RTTI usage. */ + +public: + static UClassID U_EXPORT2 getStaticClassID() { return (UClassID)&fgClassID; } + virtual UClassID getDynamicClassID() const override { return getStaticClassID(); } +public: + KeywordEnumeration(const char *keys, int32_t keywordLen, int32_t currentIndex, UErrorCode &status) + : keywords((char *)&fgClassID), current((char *)&fgClassID), length(0) { + if(U_SUCCESS(status) && keywordLen != 0) { + if(keys == nullptr || keywordLen < 0) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } else { + keywords = (char *)uprv_malloc(keywordLen+1); + if (keywords == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + else { + uprv_memcpy(keywords, keys, keywordLen); + keywords[keywordLen] = 0; + current = keywords + currentIndex; + length = keywordLen; + } + } + } + } + + virtual ~KeywordEnumeration(); + + virtual StringEnumeration * clone() const override + { + UErrorCode status = U_ZERO_ERROR; + return new KeywordEnumeration(keywords, length, (int32_t)(current - keywords), status); + } + + virtual int32_t count(UErrorCode &/*status*/) const override { + char *kw = keywords; + int32_t result = 0; + while(*kw) { + result++; + kw += uprv_strlen(kw)+1; + } + return result; + } + + virtual const char* next(int32_t* resultLength, UErrorCode& status) override { + const char* result; + int32_t len; + if(U_SUCCESS(status) && *current != 0) { + result = current; + len = (int32_t)uprv_strlen(current); + current += len+1; + if(resultLength != nullptr) { + *resultLength = len; + } + } else { + if(resultLength != nullptr) { + *resultLength = 0; + } + result = nullptr; + } + return result; + } + + virtual const UnicodeString* snext(UErrorCode& status) override { + int32_t resultLength = 0; + const char *s = next(&resultLength, status); + return setChars(s, resultLength, status); + } + + virtual void reset(UErrorCode& /*status*/) override { + current = keywords; + } +}; + +const char KeywordEnumeration::fgClassID = '\0'; + +KeywordEnumeration::~KeywordEnumeration() { + uprv_free(keywords); +} + +// A wrapper around KeywordEnumeration that calls uloc_toUnicodeLocaleKey() in +// the next() method for each keyword before returning it. +class UnicodeKeywordEnumeration : public KeywordEnumeration { +public: + using KeywordEnumeration::KeywordEnumeration; + virtual ~UnicodeKeywordEnumeration(); + + virtual const char* next(int32_t* resultLength, UErrorCode& status) override { + const char* legacy_key = KeywordEnumeration::next(nullptr, status); + while (U_SUCCESS(status) && legacy_key != nullptr) { + const char* key = uloc_toUnicodeLocaleKey(legacy_key); + if (key != nullptr) { + if (resultLength != nullptr) { + *resultLength = static_cast(uprv_strlen(key)); + } + return key; + } + // Not a Unicode keyword, could be a t, x or other, continue to look at the next one. + legacy_key = KeywordEnumeration::next(nullptr, status); + } + if (resultLength != nullptr) *resultLength = 0; + return nullptr; + } +}; + +// Out-of-line virtual destructor to serve as the "key function". +UnicodeKeywordEnumeration::~UnicodeKeywordEnumeration() = default; + +StringEnumeration * +Locale::createKeywords(UErrorCode &status) const +{ + StringEnumeration *result = nullptr; + + if (U_FAILURE(status)) { + return result; + } + + const char* variantStart = uprv_strchr(fullName, '@'); + const char* assignment = uprv_strchr(fullName, '='); + if(variantStart) { + if(assignment > variantStart) { + CharString keywords; + CharStringByteSink sink(&keywords); + ulocimp_getKeywords(variantStart+1, '@', sink, false, &status); + if (U_SUCCESS(status) && !keywords.isEmpty()) { + result = new KeywordEnumeration(keywords.data(), keywords.length(), 0, status); + if (!result) { + status = U_MEMORY_ALLOCATION_ERROR; + } + } + } else { + status = U_INVALID_FORMAT_ERROR; + } + } + return result; +} + +StringEnumeration * +Locale::createUnicodeKeywords(UErrorCode &status) const +{ + StringEnumeration *result = nullptr; + + if (U_FAILURE(status)) { + return result; + } + + const char* variantStart = uprv_strchr(fullName, '@'); + const char* assignment = uprv_strchr(fullName, '='); + if(variantStart) { + if(assignment > variantStart) { + CharString keywords; + CharStringByteSink sink(&keywords); + ulocimp_getKeywords(variantStart+1, '@', sink, false, &status); + if (U_SUCCESS(status) && !keywords.isEmpty()) { + result = new UnicodeKeywordEnumeration(keywords.data(), keywords.length(), 0, status); + if (!result) { + status = U_MEMORY_ALLOCATION_ERROR; + } + } + } else { + status = U_INVALID_FORMAT_ERROR; + } + } + return result; +} + +int32_t +Locale::getKeywordValue(const char* keywordName, char *buffer, int32_t bufLen, UErrorCode &status) const +{ + return uloc_getKeywordValue(fullName, keywordName, buffer, bufLen, &status); +} + +void +Locale::getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + + if (fIsBogus) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + // TODO: Remove the need for a const char* to a NUL terminated buffer. + const CharString keywordName_nul(keywordName, status); + if (U_FAILURE(status)) { + return; + } + + ulocimp_getKeywordValue(fullName, keywordName_nul.data(), sink, &status); +} + +void +Locale::getUnicodeKeywordValue(StringPiece keywordName, + ByteSink& sink, + UErrorCode& status) const { + // TODO: Remove the need for a const char* to a NUL terminated buffer. + const CharString keywordName_nul(keywordName, status); + if (U_FAILURE(status)) { + return; + } + + const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data()); + + if (legacy_key == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + CharString legacy_value; + { + CharStringByteSink sink(&legacy_value); + getKeywordValue(legacy_key, sink, status); + } + + if (U_FAILURE(status)) { + return; + } + + const char* unicode_value = uloc_toUnicodeLocaleType( + keywordName_nul.data(), legacy_value.data()); + + if (unicode_value == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + sink.Append(unicode_value, static_cast(uprv_strlen(unicode_value))); +} + +void +Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status) +{ + if (U_FAILURE(status)) { + return; + } + if (status == U_STRING_NOT_TERMINATED_WARNING) { + status = U_ZERO_ERROR; + } + int32_t bufferLength = uprv_max((int32_t)(uprv_strlen(fullName) + 1), ULOC_FULLNAME_CAPACITY); + int32_t newLength = uloc_setKeywordValue(keywordName, keywordValue, fullName, + bufferLength, &status) + 1; + U_ASSERT(status != U_STRING_NOT_TERMINATED_WARNING); + /* Handle the case the current buffer is not enough to hold the new id */ + if (status == U_BUFFER_OVERFLOW_ERROR) { + U_ASSERT(newLength > bufferLength); + char* newFullName = (char *)uprv_malloc(newLength); + if (newFullName == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_strcpy(newFullName, fullName); + if (fullName != fullNameBuffer) { + // if full Name is already on the heap, need to free it. + uprv_free(fullName); + if (baseName == fullName) { + baseName = newFullName; // baseName should not point to freed memory. + } + } + fullName = newFullName; + status = U_ZERO_ERROR; + uloc_setKeywordValue(keywordName, keywordValue, fullName, newLength, &status); + U_ASSERT(status != U_STRING_NOT_TERMINATED_WARNING); + } else { + U_ASSERT(newLength <= bufferLength); + } + if (U_SUCCESS(status) && baseName == fullName) { + // May have added the first keyword, meaning that the fullName is no longer also the baseName. + initBaseName(status); + } +} + +void +Locale::setKeywordValue(StringPiece keywordName, + StringPiece keywordValue, + UErrorCode& status) { + // TODO: Remove the need for a const char* to a NUL terminated buffer. + const CharString keywordName_nul(keywordName, status); + const CharString keywordValue_nul(keywordValue, status); + setKeywordValue(keywordName_nul.data(), keywordValue_nul.data(), status); +} + +void +Locale::setUnicodeKeywordValue(StringPiece keywordName, + StringPiece keywordValue, + UErrorCode& status) { + // TODO: Remove the need for a const char* to a NUL terminated buffer. + const CharString keywordName_nul(keywordName, status); + const CharString keywordValue_nul(keywordValue, status); + + if (U_FAILURE(status)) { + return; + } + + const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data()); + + if (legacy_key == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + const char* legacy_value = nullptr; + + if (!keywordValue_nul.isEmpty()) { + legacy_value = + uloc_toLegacyType(keywordName_nul.data(), keywordValue_nul.data()); + + if (legacy_value == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + } + + setKeywordValue(legacy_key, legacy_value, status); +} + +const char * +Locale::getBaseName() const { + return baseName; +} + +Locale::Iterator::~Iterator() = default; + +//eof +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/loclikely.cpp b/deps/icu-small/source/common/loclikely.cpp index ec0dca28a45a69..d161d59799f8ba 100644 --- a/deps/icu-small/source/common/loclikely.cpp +++ b/deps/icu-small/source/common/loclikely.cpp @@ -1,1415 +1,1415 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1997-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: loclikely.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010feb25 -* created by: Markus W. Scherer -* -* Code for likely and minimized locale subtags, separated out from other .cpp files -* that then do not depend on resource bundle code and likely-subtags data. -*/ - -#include "unicode/bytestream.h" -#include "unicode/utypes.h" -#include "unicode/locid.h" -#include "unicode/putil.h" -#include "unicode/uchar.h" -#include "unicode/uloc.h" -#include "unicode/ures.h" -#include "unicode/uscript.h" -#include "bytesinkutil.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "ulocimp.h" -#include "ustr_imp.h" - -/** - * These are the canonical strings for unknown languages, scripts and regions. - **/ -static const char* const unknownLanguage = "und"; -static const char* const unknownScript = "Zzzz"; -static const char* const unknownRegion = "ZZ"; - -/** - * This function looks for the localeID in the likelySubtags resource. - * - * @param localeID The tag to find. - * @param buffer A buffer to hold the matching entry - * @param bufferLength The length of the output buffer - * @return A pointer to "buffer" if found, or a null pointer if not. - */ -static const char* U_CALLCONV -findLikelySubtags(const char* localeID, - char* buffer, - int32_t bufferLength, - UErrorCode* err) { - const char* result = NULL; - - if (!U_FAILURE(*err)) { - int32_t resLen = 0; - const UChar* s = NULL; - UErrorCode tmpErr = U_ZERO_ERROR; - icu::LocalUResourceBundlePointer subtags(ures_openDirect(NULL, "likelySubtags", &tmpErr)); - if (U_SUCCESS(tmpErr)) { - icu::CharString und; - if (localeID != NULL) { - if (*localeID == '\0') { - localeID = unknownLanguage; - } else if (*localeID == '_') { - und.append(unknownLanguage, *err); - und.append(localeID, *err); - if (U_FAILURE(*err)) { - return NULL; - } - localeID = und.data(); - } - } - s = ures_getStringByKey(subtags.getAlias(), localeID, &resLen, &tmpErr); - - if (U_FAILURE(tmpErr)) { - /* - * If a resource is missing, it's not really an error, it's - * just that we don't have any data for that particular locale ID. - */ - if (tmpErr != U_MISSING_RESOURCE_ERROR) { - *err = tmpErr; - } - } - else if (resLen >= bufferLength) { - /* The buffer should never overflow. */ - *err = U_INTERNAL_PROGRAM_ERROR; - } - else { - u_UCharsToChars(s, buffer, resLen + 1); - if (resLen >= 3 && - uprv_strnicmp(buffer, unknownLanguage, 3) == 0 && - (resLen == 3 || buffer[3] == '_')) { - uprv_memmove(buffer, buffer + 3, resLen - 3 + 1); - } - result = buffer; - } - } else { - *err = tmpErr; - } - } - - return result; -} - -/** - * Append a tag to a buffer, adding the separator if necessary. The buffer - * must be large enough to contain the resulting tag plus any separator - * necessary. The tag must not be a zero-length string. - * - * @param tag The tag to add. - * @param tagLength The length of the tag. - * @param buffer The output buffer. - * @param bufferLength The length of the output buffer. This is an input/output parameter. - **/ -static void U_CALLCONV -appendTag( - const char* tag, - int32_t tagLength, - char* buffer, - int32_t* bufferLength, - UBool withSeparator) { - - if (withSeparator) { - buffer[*bufferLength] = '_'; - ++(*bufferLength); - } - - uprv_memmove( - &buffer[*bufferLength], - tag, - tagLength); - - *bufferLength += tagLength; -} - -/** - * Create a tag string from the supplied parameters. The lang, script and region - * parameters may be NULL pointers. If they are, their corresponding length parameters - * must be less than or equal to 0. - * - * If any of the language, script or region parameters are empty, and the alternateTags - * parameter is not NULL, it will be parsed for potential language, script and region tags - * to be used when constructing the new tag. If the alternateTags parameter is NULL, or - * it contains no language tag, the default tag for the unknown language is used. - * - * If the length of the new string exceeds the capacity of the output buffer, - * the function copies as many bytes to the output buffer as it can, and returns - * the error U_BUFFER_OVERFLOW_ERROR. - * - * If an illegal argument is provided, the function returns the error - * U_ILLEGAL_ARGUMENT_ERROR. - * - * Note that this function can return the warning U_STRING_NOT_TERMINATED_WARNING if - * the tag string fits in the output buffer, but the null terminator doesn't. - * - * @param lang The language tag to use. - * @param langLength The length of the language tag. - * @param script The script tag to use. - * @param scriptLength The length of the script tag. - * @param region The region tag to use. - * @param regionLength The length of the region tag. - * @param trailing Any trailing data to append to the new tag. - * @param trailingLength The length of the trailing data. - * @param alternateTags A string containing any alternate tags. - * @param sink The output sink receiving the tag string. - * @param err A pointer to a UErrorCode for error reporting. - **/ -static void U_CALLCONV -createTagStringWithAlternates( - const char* lang, - int32_t langLength, - const char* script, - int32_t scriptLength, - const char* region, - int32_t regionLength, - const char* trailing, - int32_t trailingLength, - const char* alternateTags, - icu::ByteSink& sink, - UErrorCode* err) { - - if (U_FAILURE(*err)) { - goto error; - } - else if (langLength >= ULOC_LANG_CAPACITY || - scriptLength >= ULOC_SCRIPT_CAPACITY || - regionLength >= ULOC_COUNTRY_CAPACITY) { - goto error; - } - else { - /** - * ULOC_FULLNAME_CAPACITY will provide enough capacity - * that we can build a string that contains the language, - * script and region code without worrying about overrunning - * the user-supplied buffer. - **/ - char tagBuffer[ULOC_FULLNAME_CAPACITY]; - int32_t tagLength = 0; - UBool regionAppended = false; - - if (langLength > 0) { - appendTag( - lang, - langLength, - tagBuffer, - &tagLength, - /*withSeparator=*/false); - } - else if (alternateTags == NULL) { - /* - * Use the empty string for an unknown language, if - * we found no language. - */ - } - else { - /* - * Parse the alternateTags string for the language. - */ - char alternateLang[ULOC_LANG_CAPACITY]; - int32_t alternateLangLength = sizeof(alternateLang); - - alternateLangLength = - uloc_getLanguage( - alternateTags, - alternateLang, - alternateLangLength, - err); - if(U_FAILURE(*err) || - alternateLangLength >= ULOC_LANG_CAPACITY) { - goto error; - } - else if (alternateLangLength == 0) { - /* - * Use the empty string for an unknown language, if - * we found no language. - */ - } - else { - appendTag( - alternateLang, - alternateLangLength, - tagBuffer, - &tagLength, - /*withSeparator=*/false); - } - } - - if (scriptLength > 0) { - appendTag( - script, - scriptLength, - tagBuffer, - &tagLength, - /*withSeparator=*/true); - } - else if (alternateTags != NULL) { - /* - * Parse the alternateTags string for the script. - */ - char alternateScript[ULOC_SCRIPT_CAPACITY]; - - const int32_t alternateScriptLength = - uloc_getScript( - alternateTags, - alternateScript, - sizeof(alternateScript), - err); - - if (U_FAILURE(*err) || - alternateScriptLength >= ULOC_SCRIPT_CAPACITY) { - goto error; - } - else if (alternateScriptLength > 0) { - appendTag( - alternateScript, - alternateScriptLength, - tagBuffer, - &tagLength, - /*withSeparator=*/true); - } - } - - if (regionLength > 0) { - appendTag( - region, - regionLength, - tagBuffer, - &tagLength, - /*withSeparator=*/true); - - regionAppended = true; - } - else if (alternateTags != NULL) { - /* - * Parse the alternateTags string for the region. - */ - char alternateRegion[ULOC_COUNTRY_CAPACITY]; - - const int32_t alternateRegionLength = - uloc_getCountry( - alternateTags, - alternateRegion, - sizeof(alternateRegion), - err); - if (U_FAILURE(*err) || - alternateRegionLength >= ULOC_COUNTRY_CAPACITY) { - goto error; - } - else if (alternateRegionLength > 0) { - appendTag( - alternateRegion, - alternateRegionLength, - tagBuffer, - &tagLength, - /*withSeparator=*/true); - - regionAppended = true; - } - } - - /** - * Copy the partial tag from our internal buffer to the supplied - * target. - **/ - sink.Append(tagBuffer, tagLength); - - if (trailingLength > 0) { - if (*trailing != '@') { - sink.Append("_", 1); - if (!regionAppended) { - /* extra separator is required */ - sink.Append("_", 1); - } - } - - /* - * Copy the trailing data into the supplied buffer. - */ - sink.Append(trailing, trailingLength); - } - - return; - } - -error: - - /** - * An overflow indicates the locale ID passed in - * is ill-formed. If we got here, and there was - * no previous error, it's an implicit overflow. - **/ - if (*err == U_BUFFER_OVERFLOW_ERROR || - U_SUCCESS(*err)) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } -} - -/** - * Create a tag string from the supplied parameters. The lang, script and region - * parameters may be NULL pointers. If they are, their corresponding length parameters - * must be less than or equal to 0. If the lang parameter is an empty string, the - * default value for an unknown language is written to the output buffer. - * - * If the length of the new string exceeds the capacity of the output buffer, - * the function copies as many bytes to the output buffer as it can, and returns - * the error U_BUFFER_OVERFLOW_ERROR. - * - * If an illegal argument is provided, the function returns the error - * U_ILLEGAL_ARGUMENT_ERROR. - * - * @param lang The language tag to use. - * @param langLength The length of the language tag. - * @param script The script tag to use. - * @param scriptLength The length of the script tag. - * @param region The region tag to use. - * @param regionLength The length of the region tag. - * @param trailing Any trailing data to append to the new tag. - * @param trailingLength The length of the trailing data. - * @param sink The output sink receiving the tag string. - * @param err A pointer to a UErrorCode for error reporting. - **/ -static void U_CALLCONV -createTagString( - const char* lang, - int32_t langLength, - const char* script, - int32_t scriptLength, - const char* region, - int32_t regionLength, - const char* trailing, - int32_t trailingLength, - icu::ByteSink& sink, - UErrorCode* err) -{ - createTagStringWithAlternates( - lang, - langLength, - script, - scriptLength, - region, - regionLength, - trailing, - trailingLength, - NULL, - sink, - err); -} - -/** - * Parse the language, script, and region subtags from a tag string, and copy the - * results into the corresponding output parameters. The buffers are null-terminated, - * unless overflow occurs. - * - * The langLength, scriptLength, and regionLength parameters are input/output - * parameters, and must contain the capacity of their corresponding buffers on - * input. On output, they will contain the actual length of the buffers, not - * including the null terminator. - * - * If the length of any of the output subtags exceeds the capacity of the corresponding - * buffer, the function copies as many bytes to the output buffer as it can, and returns - * the error U_BUFFER_OVERFLOW_ERROR. It will not parse any more subtags once overflow - * occurs. - * - * If an illegal argument is provided, the function returns the error - * U_ILLEGAL_ARGUMENT_ERROR. - * - * @param localeID The locale ID to parse. - * @param lang The language tag buffer. - * @param langLength The length of the language tag. - * @param script The script tag buffer. - * @param scriptLength The length of the script tag. - * @param region The region tag buffer. - * @param regionLength The length of the region tag. - * @param err A pointer to a UErrorCode for error reporting. - * @return The number of chars of the localeID parameter consumed. - **/ -static int32_t U_CALLCONV -parseTagString( - const char* localeID, - char* lang, - int32_t* langLength, - char* script, - int32_t* scriptLength, - char* region, - int32_t* regionLength, - UErrorCode* err) -{ - const char* position = localeID; - int32_t subtagLength = 0; - - if(U_FAILURE(*err) || - localeID == NULL || - lang == NULL || - langLength == NULL || - script == NULL || - scriptLength == NULL || - region == NULL || - regionLength == NULL) { - goto error; - } - - subtagLength = ulocimp_getLanguage(position, &position, *err).extract(lang, *langLength, *err); - - /* - * Note that we explicit consider U_STRING_NOT_TERMINATED_WARNING - * to be an error, because it indicates the user-supplied tag is - * not well-formed. - */ - if(U_FAILURE(*err)) { - goto error; - } - - *langLength = subtagLength; - - /* - * If no language was present, use the empty string instead. - * Otherwise, move past any separator. - */ - if (_isIDSeparator(*position)) { - ++position; - } - - subtagLength = ulocimp_getScript(position, &position, *err).extract(script, *scriptLength, *err); - - if(U_FAILURE(*err)) { - goto error; - } - - *scriptLength = subtagLength; - - if (*scriptLength > 0) { - if (uprv_strnicmp(script, unknownScript, *scriptLength) == 0) { - /** - * If the script part is the "unknown" script, then don't return it. - **/ - *scriptLength = 0; - } - - /* - * Move past any separator. - */ - if (_isIDSeparator(*position)) { - ++position; - } - } - - subtagLength = ulocimp_getCountry(position, &position, *err).extract(region, *regionLength, *err); - - if(U_FAILURE(*err)) { - goto error; - } - - *regionLength = subtagLength; - - if (*regionLength > 0) { - if (uprv_strnicmp(region, unknownRegion, *regionLength) == 0) { - /** - * If the region part is the "unknown" region, then don't return it. - **/ - *regionLength = 0; - } - } else if (*position != 0 && *position != '@') { - /* back up over consumed trailing separator */ - --position; - } - -exit: - - return (int32_t)(position - localeID); - -error: - - /** - * If we get here, we have no explicit error, it's the result of an - * illegal argument. - **/ - if (!U_FAILURE(*err)) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } - - goto exit; -} - -static UBool U_CALLCONV -createLikelySubtagsString( - const char* lang, - int32_t langLength, - const char* script, - int32_t scriptLength, - const char* region, - int32_t regionLength, - const char* variants, - int32_t variantsLength, - icu::ByteSink& sink, - UErrorCode* err) { - /** - * ULOC_FULLNAME_CAPACITY will provide enough capacity - * that we can build a string that contains the language, - * script and region code without worrying about overrunning - * the user-supplied buffer. - **/ - char likelySubtagsBuffer[ULOC_FULLNAME_CAPACITY]; - - if(U_FAILURE(*err)) { - goto error; - } - - /** - * Try the language with the script and region first. - **/ - if (scriptLength > 0 && regionLength > 0) { - - const char* likelySubtags = NULL; - - icu::CharString tagBuffer; - { - icu::CharStringByteSink sink(&tagBuffer); - createTagString( - lang, - langLength, - script, - scriptLength, - region, - regionLength, - NULL, - 0, - sink, - err); - } - if(U_FAILURE(*err)) { - goto error; - } - - likelySubtags = - findLikelySubtags( - tagBuffer.data(), - likelySubtagsBuffer, - sizeof(likelySubtagsBuffer), - err); - if(U_FAILURE(*err)) { - goto error; - } - - if (likelySubtags != NULL) { - /* Always use the language tag from the - maximal string, since it may be more - specific than the one provided. */ - createTagStringWithAlternates( - NULL, - 0, - NULL, - 0, - NULL, - 0, - variants, - variantsLength, - likelySubtags, - sink, - err); - return true; - } - } - - /** - * Try the language with just the script. - **/ - if (scriptLength > 0) { - - const char* likelySubtags = NULL; - - icu::CharString tagBuffer; - { - icu::CharStringByteSink sink(&tagBuffer); - createTagString( - lang, - langLength, - script, - scriptLength, - NULL, - 0, - NULL, - 0, - sink, - err); - } - if(U_FAILURE(*err)) { - goto error; - } - - likelySubtags = - findLikelySubtags( - tagBuffer.data(), - likelySubtagsBuffer, - sizeof(likelySubtagsBuffer), - err); - if(U_FAILURE(*err)) { - goto error; - } - - if (likelySubtags != NULL) { - /* Always use the language tag from the - maximal string, since it may be more - specific than the one provided. */ - createTagStringWithAlternates( - NULL, - 0, - NULL, - 0, - region, - regionLength, - variants, - variantsLength, - likelySubtags, - sink, - err); - return true; - } - } - - /** - * Try the language with just the region. - **/ - if (regionLength > 0) { - - const char* likelySubtags = NULL; - - icu::CharString tagBuffer; - { - icu::CharStringByteSink sink(&tagBuffer); - createTagString( - lang, - langLength, - NULL, - 0, - region, - regionLength, - NULL, - 0, - sink, - err); - } - if(U_FAILURE(*err)) { - goto error; - } - - likelySubtags = - findLikelySubtags( - tagBuffer.data(), - likelySubtagsBuffer, - sizeof(likelySubtagsBuffer), - err); - if(U_FAILURE(*err)) { - goto error; - } - - if (likelySubtags != NULL) { - /* Always use the language tag from the - maximal string, since it may be more - specific than the one provided. */ - createTagStringWithAlternates( - NULL, - 0, - script, - scriptLength, - NULL, - 0, - variants, - variantsLength, - likelySubtags, - sink, - err); - return true; - } - } - - /** - * Finally, try just the language. - **/ - { - const char* likelySubtags = NULL; - - icu::CharString tagBuffer; - { - icu::CharStringByteSink sink(&tagBuffer); - createTagString( - lang, - langLength, - NULL, - 0, - NULL, - 0, - NULL, - 0, - sink, - err); - } - if(U_FAILURE(*err)) { - goto error; - } - - likelySubtags = - findLikelySubtags( - tagBuffer.data(), - likelySubtagsBuffer, - sizeof(likelySubtagsBuffer), - err); - if(U_FAILURE(*err)) { - goto error; - } - - if (likelySubtags != NULL) { - /* Always use the language tag from the - maximal string, since it may be more - specific than the one provided. */ - createTagStringWithAlternates( - NULL, - 0, - script, - scriptLength, - region, - regionLength, - variants, - variantsLength, - likelySubtags, - sink, - err); - return true; - } - } - - return false; - -error: - - if (!U_FAILURE(*err)) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } - - return false; -} - -#define CHECK_TRAILING_VARIANT_SIZE(trailing, trailingLength) UPRV_BLOCK_MACRO_BEGIN { \ - int32_t count = 0; \ - int32_t i; \ - for (i = 0; i < trailingLength; i++) { \ - if (trailing[i] == '-' || trailing[i] == '_') { \ - count = 0; \ - if (count > 8) { \ - goto error; \ - } \ - } else if (trailing[i] == '@') { \ - break; \ - } else if (count > 8) { \ - goto error; \ - } else { \ - count++; \ - } \ - } \ -} UPRV_BLOCK_MACRO_END - -static UBool -_uloc_addLikelySubtags(const char* localeID, - icu::ByteSink& sink, - UErrorCode* err) { - char lang[ULOC_LANG_CAPACITY]; - int32_t langLength = sizeof(lang); - char script[ULOC_SCRIPT_CAPACITY]; - int32_t scriptLength = sizeof(script); - char region[ULOC_COUNTRY_CAPACITY]; - int32_t regionLength = sizeof(region); - const char* trailing = ""; - int32_t trailingLength = 0; - int32_t trailingIndex = 0; - UBool success = false; - - if(U_FAILURE(*err)) { - goto error; - } - if (localeID == NULL) { - goto error; - } - - trailingIndex = parseTagString( - localeID, - lang, - &langLength, - script, - &scriptLength, - region, - ®ionLength, - err); - if(U_FAILURE(*err)) { - /* Overflow indicates an illegal argument error */ - if (*err == U_BUFFER_OVERFLOW_ERROR) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } - - goto error; - } - - /* Find the length of the trailing portion. */ - while (_isIDSeparator(localeID[trailingIndex])) { - trailingIndex++; - } - trailing = &localeID[trailingIndex]; - trailingLength = (int32_t)uprv_strlen(trailing); - - CHECK_TRAILING_VARIANT_SIZE(trailing, trailingLength); - - success = - createLikelySubtagsString( - lang, - langLength, - script, - scriptLength, - region, - regionLength, - trailing, - trailingLength, - sink, - err); - - if (!success) { - const int32_t localIDLength = (int32_t)uprv_strlen(localeID); - - /* - * If we get here, we need to return localeID. - */ - sink.Append(localeID, localIDLength); - } - - return success; - -error: - - if (!U_FAILURE(*err)) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } - return false; -} - -// Add likely subtags to the sink -// return true if the value in the sink is produced by a match during the lookup -// return false if the value in the sink is the same as input because there are -// no match after the lookup. -static UBool _ulocimp_addLikelySubtags(const char*, icu::ByteSink&, UErrorCode*); - -static void -_uloc_minimizeSubtags(const char* localeID, - icu::ByteSink& sink, - UErrorCode* err) { - icu::CharString maximizedTagBuffer; - - char lang[ULOC_LANG_CAPACITY]; - int32_t langLength = sizeof(lang); - char script[ULOC_SCRIPT_CAPACITY]; - int32_t scriptLength = sizeof(script); - char region[ULOC_COUNTRY_CAPACITY]; - int32_t regionLength = sizeof(region); - const char* trailing = ""; - int32_t trailingLength = 0; - int32_t trailingIndex = 0; - UBool successGetMax = false; - - if(U_FAILURE(*err)) { - goto error; - } - else if (localeID == NULL) { - goto error; - } - - trailingIndex = - parseTagString( - localeID, - lang, - &langLength, - script, - &scriptLength, - region, - ®ionLength, - err); - if(U_FAILURE(*err)) { - - /* Overflow indicates an illegal argument error */ - if (*err == U_BUFFER_OVERFLOW_ERROR) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } - - goto error; - } - - /* Find the spot where the variants or the keywords begin, if any. */ - while (_isIDSeparator(localeID[trailingIndex])) { - trailingIndex++; - } - trailing = &localeID[trailingIndex]; - trailingLength = (int32_t)uprv_strlen(trailing); - - CHECK_TRAILING_VARIANT_SIZE(trailing, trailingLength); - - { - icu::CharString base; - { - icu::CharStringByteSink baseSink(&base); - createTagString( - lang, - langLength, - script, - scriptLength, - region, - regionLength, - NULL, - 0, - baseSink, - err); - } - - /** - * First, we need to first get the maximization - * from AddLikelySubtags. - **/ - { - icu::CharStringByteSink maxSink(&maximizedTagBuffer); - successGetMax = _ulocimp_addLikelySubtags(base.data(), maxSink, err); - } - } - - if(U_FAILURE(*err)) { - goto error; - } - - if (!successGetMax) { - /** - * If we got here, return the locale ID parameter unchanged. - **/ - const int32_t localeIDLength = (int32_t)uprv_strlen(localeID); - sink.Append(localeID, localeIDLength); - return; - } - - // In the following, the lang, script, region are referring to those in - // the maximizedTagBuffer, not the one in the localeID. - langLength = sizeof(lang); - scriptLength = sizeof(script); - regionLength = sizeof(region); - parseTagString( - maximizedTagBuffer.data(), - lang, - &langLength, - script, - &scriptLength, - region, - ®ionLength, - err); - if(U_FAILURE(*err)) { - goto error; - } - - /** - * Start first with just the language. - **/ - { - icu::CharString tagBuffer; - { - icu::CharStringByteSink tagSink(&tagBuffer); - createLikelySubtagsString( - lang, - langLength, - NULL, - 0, - NULL, - 0, - NULL, - 0, - tagSink, - err); - } - - if(U_FAILURE(*err)) { - goto error; - } - else if (!tagBuffer.isEmpty() && - uprv_strnicmp( - maximizedTagBuffer.data(), - tagBuffer.data(), - tagBuffer.length()) == 0) { - - createTagString( - lang, - langLength, - NULL, - 0, - NULL, - 0, - trailing, - trailingLength, - sink, - err); - return; - } - } - - /** - * Next, try the language and region. - **/ - if (regionLength > 0) { - - icu::CharString tagBuffer; - { - icu::CharStringByteSink tagSink(&tagBuffer); - createLikelySubtagsString( - lang, - langLength, - NULL, - 0, - region, - regionLength, - NULL, - 0, - tagSink, - err); - } - - if(U_FAILURE(*err)) { - goto error; - } - else if (!tagBuffer.isEmpty() && - uprv_strnicmp( - maximizedTagBuffer.data(), - tagBuffer.data(), - tagBuffer.length()) == 0) { - - createTagString( - lang, - langLength, - NULL, - 0, - region, - regionLength, - trailing, - trailingLength, - sink, - err); - return; - } - } - - /** - * Finally, try the language and script. This is our last chance, - * since trying with all three subtags would only yield the - * maximal version that we already have. - **/ - if (scriptLength > 0) { - icu::CharString tagBuffer; - { - icu::CharStringByteSink tagSink(&tagBuffer); - createLikelySubtagsString( - lang, - langLength, - script, - scriptLength, - NULL, - 0, - NULL, - 0, - tagSink, - err); - } - - if(U_FAILURE(*err)) { - goto error; - } - else if (!tagBuffer.isEmpty() && - uprv_strnicmp( - maximizedTagBuffer.data(), - tagBuffer.data(), - tagBuffer.length()) == 0) { - - createTagString( - lang, - langLength, - script, - scriptLength, - NULL, - 0, - trailing, - trailingLength, - sink, - err); - return; - } - } - - { - /** - * If we got here, return the max + trail. - **/ - createTagString( - lang, - langLength, - script, - scriptLength, - region, - regionLength, - trailing, - trailingLength, - sink, - err); - return; - } - -error: - - if (!U_FAILURE(*err)) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } -} - -static int32_t -do_canonicalize(const char* localeID, - char* buffer, - int32_t bufferCapacity, - UErrorCode* err) -{ - int32_t canonicalizedSize = uloc_canonicalize( - localeID, - buffer, - bufferCapacity, - err); - - if (*err == U_STRING_NOT_TERMINATED_WARNING || - *err == U_BUFFER_OVERFLOW_ERROR) { - return canonicalizedSize; - } - else if (U_FAILURE(*err)) { - - return -1; - } - else { - return canonicalizedSize; - } -} - -U_CAPI int32_t U_EXPORT2 -uloc_addLikelySubtags(const char* localeID, - char* maximizedLocaleID, - int32_t maximizedLocaleIDCapacity, - UErrorCode* status) { - if (U_FAILURE(*status)) { - return 0; - } - - icu::CheckedArrayByteSink sink( - maximizedLocaleID, maximizedLocaleIDCapacity); - - ulocimp_addLikelySubtags(localeID, sink, status); - int32_t reslen = sink.NumberOfBytesAppended(); - - if (U_FAILURE(*status)) { - return sink.Overflowed() ? reslen : -1; - } - - if (sink.Overflowed()) { - *status = U_BUFFER_OVERFLOW_ERROR; - } else { - u_terminateChars( - maximizedLocaleID, maximizedLocaleIDCapacity, reslen, status); - } - - return reslen; -} - -static UBool -_ulocimp_addLikelySubtags(const char* localeID, - icu::ByteSink& sink, - UErrorCode* status) { - PreflightingLocaleIDBuffer localeBuffer; - do { - localeBuffer.requestedCapacity = do_canonicalize(localeID, localeBuffer.getBuffer(), - localeBuffer.getCapacity(), status); - } while (localeBuffer.needToTryAgain(status)); - - if (U_SUCCESS(*status)) { - return _uloc_addLikelySubtags(localeBuffer.getBuffer(), sink, status); - } else { - return false; - } -} - -U_CAPI void U_EXPORT2 -ulocimp_addLikelySubtags(const char* localeID, - icu::ByteSink& sink, - UErrorCode* status) { - _ulocimp_addLikelySubtags(localeID, sink, status); -} - -U_CAPI int32_t U_EXPORT2 -uloc_minimizeSubtags(const char* localeID, - char* minimizedLocaleID, - int32_t minimizedLocaleIDCapacity, - UErrorCode* status) { - if (U_FAILURE(*status)) { - return 0; - } - - icu::CheckedArrayByteSink sink( - minimizedLocaleID, minimizedLocaleIDCapacity); - - ulocimp_minimizeSubtags(localeID, sink, status); - int32_t reslen = sink.NumberOfBytesAppended(); - - if (U_FAILURE(*status)) { - return sink.Overflowed() ? reslen : -1; - } - - if (sink.Overflowed()) { - *status = U_BUFFER_OVERFLOW_ERROR; - } else { - u_terminateChars( - minimizedLocaleID, minimizedLocaleIDCapacity, reslen, status); - } - - return reslen; -} - -U_CAPI void U_EXPORT2 -ulocimp_minimizeSubtags(const char* localeID, - icu::ByteSink& sink, - UErrorCode* status) { - PreflightingLocaleIDBuffer localeBuffer; - do { - localeBuffer.requestedCapacity = do_canonicalize(localeID, localeBuffer.getBuffer(), - localeBuffer.getCapacity(), status); - } while (localeBuffer.needToTryAgain(status)); - - _uloc_minimizeSubtags(localeBuffer.getBuffer(), sink, status); -} - -// Pairs of (language subtag, + or -) for finding out fast if common languages -// are LTR (minus) or RTL (plus). -static const char LANG_DIR_STRING[] = - "root-en-es-pt-zh-ja-ko-de-fr-it-ar+he+fa+ru-nl-pl-th-tr-"; - -// Implemented here because this calls ulocimp_addLikelySubtags(). -U_CAPI UBool U_EXPORT2 -uloc_isRightToLeft(const char *locale) { - UErrorCode errorCode = U_ZERO_ERROR; - char script[8]; - int32_t scriptLength = uloc_getScript(locale, script, UPRV_LENGTHOF(script), &errorCode); - if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING || - scriptLength == 0) { - // Fastpath: We know the likely scripts and their writing direction - // for some common languages. - errorCode = U_ZERO_ERROR; - char lang[8]; - int32_t langLength = uloc_getLanguage(locale, lang, UPRV_LENGTHOF(lang), &errorCode); - if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING) { - return false; - } - if (langLength > 0) { - const char* langPtr = uprv_strstr(LANG_DIR_STRING, lang); - if (langPtr != NULL) { - switch (langPtr[langLength]) { - case '-': return false; - case '+': return true; - default: break; // partial match of a longer code - } - } - } - // Otherwise, find the likely script. - errorCode = U_ZERO_ERROR; - icu::CharString likely; - { - icu::CharStringByteSink sink(&likely); - ulocimp_addLikelySubtags(locale, sink, &errorCode); - } - if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING) { - return false; - } - scriptLength = uloc_getScript(likely.data(), script, UPRV_LENGTHOF(script), &errorCode); - if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING || - scriptLength == 0) { - return false; - } - } - UScriptCode scriptCode = (UScriptCode)u_getPropertyValueEnum(UCHAR_SCRIPT, script); - return uscript_isRightToLeft(scriptCode); -} - -U_NAMESPACE_BEGIN - -UBool -Locale::isRightToLeft() const { - return uloc_isRightToLeft(getBaseName()); -} - -U_NAMESPACE_END - -// The following must at least allow for rg key value (6) plus terminator (1). -#define ULOC_RG_BUFLEN 8 - -U_CAPI int32_t U_EXPORT2 -ulocimp_getRegionForSupplementalData(const char *localeID, UBool inferRegion, - char *region, int32_t regionCapacity, UErrorCode* status) { - if (U_FAILURE(*status)) { - return 0; - } - char rgBuf[ULOC_RG_BUFLEN]; - UErrorCode rgStatus = U_ZERO_ERROR; - - // First check for rg keyword value - int32_t rgLen = uloc_getKeywordValue(localeID, "rg", rgBuf, ULOC_RG_BUFLEN, &rgStatus); - if (U_FAILURE(rgStatus) || rgLen != 6) { - rgLen = 0; - } else { - // rgBuf guaranteed to be zero terminated here, with text len 6 - char *rgPtr = rgBuf; - for (; *rgPtr!= 0; rgPtr++) { - *rgPtr = uprv_toupper(*rgPtr); - } - rgLen = (uprv_strcmp(rgBuf+2, "ZZZZ") == 0)? 2: 0; - } - - if (rgLen == 0) { - // No valid rg keyword value, try for unicode_region_subtag - rgLen = uloc_getCountry(localeID, rgBuf, ULOC_RG_BUFLEN, status); - if (U_FAILURE(*status)) { - rgLen = 0; - } else if (rgLen == 0 && inferRegion) { - // no unicode_region_subtag but inferRegion true, try likely subtags - rgStatus = U_ZERO_ERROR; - icu::CharString locBuf; - { - icu::CharStringByteSink sink(&locBuf); - ulocimp_addLikelySubtags(localeID, sink, &rgStatus); - } - if (U_SUCCESS(rgStatus)) { - rgLen = uloc_getCountry(locBuf.data(), rgBuf, ULOC_RG_BUFLEN, status); - if (U_FAILURE(*status)) { - rgLen = 0; - } - } - } - } - - rgBuf[rgLen] = 0; - uprv_strncpy(region, rgBuf, regionCapacity); - return u_terminateChars(region, regionCapacity, rgLen, status); -} - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1997-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: loclikely.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010feb25 +* created by: Markus W. Scherer +* +* Code for likely and minimized locale subtags, separated out from other .cpp files +* that then do not depend on resource bundle code and likely-subtags data. +*/ + +#include "unicode/bytestream.h" +#include "unicode/utypes.h" +#include "unicode/locid.h" +#include "unicode/putil.h" +#include "unicode/uchar.h" +#include "unicode/uloc.h" +#include "unicode/ures.h" +#include "unicode/uscript.h" +#include "bytesinkutil.h" +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "ulocimp.h" +#include "ustr_imp.h" + +/** + * These are the canonical strings for unknown languages, scripts and regions. + **/ +static const char* const unknownLanguage = "und"; +static const char* const unknownScript = "Zzzz"; +static const char* const unknownRegion = "ZZ"; + +/** + * This function looks for the localeID in the likelySubtags resource. + * + * @param localeID The tag to find. + * @param buffer A buffer to hold the matching entry + * @param bufferLength The length of the output buffer + * @return A pointer to "buffer" if found, or a null pointer if not. + */ +static const char* U_CALLCONV +findLikelySubtags(const char* localeID, + char* buffer, + int32_t bufferLength, + UErrorCode* err) { + const char* result = nullptr; + + if (!U_FAILURE(*err)) { + int32_t resLen = 0; + const char16_t* s = nullptr; + UErrorCode tmpErr = U_ZERO_ERROR; + icu::LocalUResourceBundlePointer subtags(ures_openDirect(nullptr, "likelySubtags", &tmpErr)); + if (U_SUCCESS(tmpErr)) { + icu::CharString und; + if (localeID != nullptr) { + if (*localeID == '\0') { + localeID = unknownLanguage; + } else if (*localeID == '_') { + und.append(unknownLanguage, *err); + und.append(localeID, *err); + if (U_FAILURE(*err)) { + return nullptr; + } + localeID = und.data(); + } + } + s = ures_getStringByKey(subtags.getAlias(), localeID, &resLen, &tmpErr); + + if (U_FAILURE(tmpErr)) { + /* + * If a resource is missing, it's not really an error, it's + * just that we don't have any data for that particular locale ID. + */ + if (tmpErr != U_MISSING_RESOURCE_ERROR) { + *err = tmpErr; + } + } + else if (resLen >= bufferLength) { + /* The buffer should never overflow. */ + *err = U_INTERNAL_PROGRAM_ERROR; + } + else { + u_UCharsToChars(s, buffer, resLen + 1); + if (resLen >= 3 && + uprv_strnicmp(buffer, unknownLanguage, 3) == 0 && + (resLen == 3 || buffer[3] == '_')) { + uprv_memmove(buffer, buffer + 3, resLen - 3 + 1); + } + result = buffer; + } + } else { + *err = tmpErr; + } + } + + return result; +} + +/** + * Append a tag to a buffer, adding the separator if necessary. The buffer + * must be large enough to contain the resulting tag plus any separator + * necessary. The tag must not be a zero-length string. + * + * @param tag The tag to add. + * @param tagLength The length of the tag. + * @param buffer The output buffer. + * @param bufferLength The length of the output buffer. This is an input/output parameter. + **/ +static void U_CALLCONV +appendTag( + const char* tag, + int32_t tagLength, + char* buffer, + int32_t* bufferLength, + UBool withSeparator) { + + if (withSeparator) { + buffer[*bufferLength] = '_'; + ++(*bufferLength); + } + + uprv_memmove( + &buffer[*bufferLength], + tag, + tagLength); + + *bufferLength += tagLength; +} + +/** + * Create a tag string from the supplied parameters. The lang, script and region + * parameters may be nullptr pointers. If they are, their corresponding length parameters + * must be less than or equal to 0. + * + * If any of the language, script or region parameters are empty, and the alternateTags + * parameter is not nullptr, it will be parsed for potential language, script and region tags + * to be used when constructing the new tag. If the alternateTags parameter is nullptr, or + * it contains no language tag, the default tag for the unknown language is used. + * + * If the length of the new string exceeds the capacity of the output buffer, + * the function copies as many bytes to the output buffer as it can, and returns + * the error U_BUFFER_OVERFLOW_ERROR. + * + * If an illegal argument is provided, the function returns the error + * U_ILLEGAL_ARGUMENT_ERROR. + * + * Note that this function can return the warning U_STRING_NOT_TERMINATED_WARNING if + * the tag string fits in the output buffer, but the null terminator doesn't. + * + * @param lang The language tag to use. + * @param langLength The length of the language tag. + * @param script The script tag to use. + * @param scriptLength The length of the script tag. + * @param region The region tag to use. + * @param regionLength The length of the region tag. + * @param trailing Any trailing data to append to the new tag. + * @param trailingLength The length of the trailing data. + * @param alternateTags A string containing any alternate tags. + * @param sink The output sink receiving the tag string. + * @param err A pointer to a UErrorCode for error reporting. + **/ +static void U_CALLCONV +createTagStringWithAlternates( + const char* lang, + int32_t langLength, + const char* script, + int32_t scriptLength, + const char* region, + int32_t regionLength, + const char* trailing, + int32_t trailingLength, + const char* alternateTags, + icu::ByteSink& sink, + UErrorCode* err) { + + if (U_FAILURE(*err)) { + goto error; + } + else if (langLength >= ULOC_LANG_CAPACITY || + scriptLength >= ULOC_SCRIPT_CAPACITY || + regionLength >= ULOC_COUNTRY_CAPACITY) { + goto error; + } + else { + /** + * ULOC_FULLNAME_CAPACITY will provide enough capacity + * that we can build a string that contains the language, + * script and region code without worrying about overrunning + * the user-supplied buffer. + **/ + char tagBuffer[ULOC_FULLNAME_CAPACITY]; + int32_t tagLength = 0; + UBool regionAppended = false; + + if (langLength > 0) { + appendTag( + lang, + langLength, + tagBuffer, + &tagLength, + /*withSeparator=*/false); + } + else if (alternateTags == nullptr) { + /* + * Use the empty string for an unknown language, if + * we found no language. + */ + } + else { + /* + * Parse the alternateTags string for the language. + */ + char alternateLang[ULOC_LANG_CAPACITY]; + int32_t alternateLangLength = sizeof(alternateLang); + + alternateLangLength = + uloc_getLanguage( + alternateTags, + alternateLang, + alternateLangLength, + err); + if(U_FAILURE(*err) || + alternateLangLength >= ULOC_LANG_CAPACITY) { + goto error; + } + else if (alternateLangLength == 0) { + /* + * Use the empty string for an unknown language, if + * we found no language. + */ + } + else { + appendTag( + alternateLang, + alternateLangLength, + tagBuffer, + &tagLength, + /*withSeparator=*/false); + } + } + + if (scriptLength > 0) { + appendTag( + script, + scriptLength, + tagBuffer, + &tagLength, + /*withSeparator=*/true); + } + else if (alternateTags != nullptr) { + /* + * Parse the alternateTags string for the script. + */ + char alternateScript[ULOC_SCRIPT_CAPACITY]; + + const int32_t alternateScriptLength = + uloc_getScript( + alternateTags, + alternateScript, + sizeof(alternateScript), + err); + + if (U_FAILURE(*err) || + alternateScriptLength >= ULOC_SCRIPT_CAPACITY) { + goto error; + } + else if (alternateScriptLength > 0) { + appendTag( + alternateScript, + alternateScriptLength, + tagBuffer, + &tagLength, + /*withSeparator=*/true); + } + } + + if (regionLength > 0) { + appendTag( + region, + regionLength, + tagBuffer, + &tagLength, + /*withSeparator=*/true); + + regionAppended = true; + } + else if (alternateTags != nullptr) { + /* + * Parse the alternateTags string for the region. + */ + char alternateRegion[ULOC_COUNTRY_CAPACITY]; + + const int32_t alternateRegionLength = + uloc_getCountry( + alternateTags, + alternateRegion, + sizeof(alternateRegion), + err); + if (U_FAILURE(*err) || + alternateRegionLength >= ULOC_COUNTRY_CAPACITY) { + goto error; + } + else if (alternateRegionLength > 0) { + appendTag( + alternateRegion, + alternateRegionLength, + tagBuffer, + &tagLength, + /*withSeparator=*/true); + + regionAppended = true; + } + } + + /** + * Copy the partial tag from our internal buffer to the supplied + * target. + **/ + sink.Append(tagBuffer, tagLength); + + if (trailingLength > 0) { + if (*trailing != '@') { + sink.Append("_", 1); + if (!regionAppended) { + /* extra separator is required */ + sink.Append("_", 1); + } + } + + /* + * Copy the trailing data into the supplied buffer. + */ + sink.Append(trailing, trailingLength); + } + + return; + } + +error: + + /** + * An overflow indicates the locale ID passed in + * is ill-formed. If we got here, and there was + * no previous error, it's an implicit overflow. + **/ + if (*err == U_BUFFER_OVERFLOW_ERROR || + U_SUCCESS(*err)) { + *err = U_ILLEGAL_ARGUMENT_ERROR; + } +} + +/** + * Create a tag string from the supplied parameters. The lang, script and region + * parameters may be nullptr pointers. If they are, their corresponding length parameters + * must be less than or equal to 0. If the lang parameter is an empty string, the + * default value for an unknown language is written to the output buffer. + * + * If the length of the new string exceeds the capacity of the output buffer, + * the function copies as many bytes to the output buffer as it can, and returns + * the error U_BUFFER_OVERFLOW_ERROR. + * + * If an illegal argument is provided, the function returns the error + * U_ILLEGAL_ARGUMENT_ERROR. + * + * @param lang The language tag to use. + * @param langLength The length of the language tag. + * @param script The script tag to use. + * @param scriptLength The length of the script tag. + * @param region The region tag to use. + * @param regionLength The length of the region tag. + * @param trailing Any trailing data to append to the new tag. + * @param trailingLength The length of the trailing data. + * @param sink The output sink receiving the tag string. + * @param err A pointer to a UErrorCode for error reporting. + **/ +static void U_CALLCONV +createTagString( + const char* lang, + int32_t langLength, + const char* script, + int32_t scriptLength, + const char* region, + int32_t regionLength, + const char* trailing, + int32_t trailingLength, + icu::ByteSink& sink, + UErrorCode* err) +{ + createTagStringWithAlternates( + lang, + langLength, + script, + scriptLength, + region, + regionLength, + trailing, + trailingLength, + nullptr, + sink, + err); +} + +/** + * Parse the language, script, and region subtags from a tag string, and copy the + * results into the corresponding output parameters. The buffers are null-terminated, + * unless overflow occurs. + * + * The langLength, scriptLength, and regionLength parameters are input/output + * parameters, and must contain the capacity of their corresponding buffers on + * input. On output, they will contain the actual length of the buffers, not + * including the null terminator. + * + * If the length of any of the output subtags exceeds the capacity of the corresponding + * buffer, the function copies as many bytes to the output buffer as it can, and returns + * the error U_BUFFER_OVERFLOW_ERROR. It will not parse any more subtags once overflow + * occurs. + * + * If an illegal argument is provided, the function returns the error + * U_ILLEGAL_ARGUMENT_ERROR. + * + * @param localeID The locale ID to parse. + * @param lang The language tag buffer. + * @param langLength The length of the language tag. + * @param script The script tag buffer. + * @param scriptLength The length of the script tag. + * @param region The region tag buffer. + * @param regionLength The length of the region tag. + * @param err A pointer to a UErrorCode for error reporting. + * @return The number of chars of the localeID parameter consumed. + **/ +static int32_t U_CALLCONV +parseTagString( + const char* localeID, + char* lang, + int32_t* langLength, + char* script, + int32_t* scriptLength, + char* region, + int32_t* regionLength, + UErrorCode* err) +{ + const char* position = localeID; + int32_t subtagLength = 0; + + if(U_FAILURE(*err) || + localeID == nullptr || + lang == nullptr || + langLength == nullptr || + script == nullptr || + scriptLength == nullptr || + region == nullptr || + regionLength == nullptr) { + goto error; + } + + subtagLength = ulocimp_getLanguage(position, &position, *err).extract(lang, *langLength, *err); + + /* + * Note that we explicit consider U_STRING_NOT_TERMINATED_WARNING + * to be an error, because it indicates the user-supplied tag is + * not well-formed. + */ + if(U_FAILURE(*err)) { + goto error; + } + + *langLength = subtagLength; + + /* + * If no language was present, use the empty string instead. + * Otherwise, move past any separator. + */ + if (_isIDSeparator(*position)) { + ++position; + } + + subtagLength = ulocimp_getScript(position, &position, *err).extract(script, *scriptLength, *err); + + if(U_FAILURE(*err)) { + goto error; + } + + *scriptLength = subtagLength; + + if (*scriptLength > 0) { + if (uprv_strnicmp(script, unknownScript, *scriptLength) == 0) { + /** + * If the script part is the "unknown" script, then don't return it. + **/ + *scriptLength = 0; + } + + /* + * Move past any separator. + */ + if (_isIDSeparator(*position)) { + ++position; + } + } + + subtagLength = ulocimp_getCountry(position, &position, *err).extract(region, *regionLength, *err); + + if(U_FAILURE(*err)) { + goto error; + } + + *regionLength = subtagLength; + + if (*regionLength > 0) { + if (uprv_strnicmp(region, unknownRegion, *regionLength) == 0) { + /** + * If the region part is the "unknown" region, then don't return it. + **/ + *regionLength = 0; + } + } else if (*position != 0 && *position != '@') { + /* back up over consumed trailing separator */ + --position; + } + +exit: + + return (int32_t)(position - localeID); + +error: + + /** + * If we get here, we have no explicit error, it's the result of an + * illegal argument. + **/ + if (!U_FAILURE(*err)) { + *err = U_ILLEGAL_ARGUMENT_ERROR; + } + + goto exit; +} + +static UBool U_CALLCONV +createLikelySubtagsString( + const char* lang, + int32_t langLength, + const char* script, + int32_t scriptLength, + const char* region, + int32_t regionLength, + const char* variants, + int32_t variantsLength, + icu::ByteSink& sink, + UErrorCode* err) { + /** + * ULOC_FULLNAME_CAPACITY will provide enough capacity + * that we can build a string that contains the language, + * script and region code without worrying about overrunning + * the user-supplied buffer. + **/ + char likelySubtagsBuffer[ULOC_FULLNAME_CAPACITY]; + + if(U_FAILURE(*err)) { + goto error; + } + + /** + * Try the language with the script and region first. + **/ + if (scriptLength > 0 && regionLength > 0) { + + const char* likelySubtags = nullptr; + + icu::CharString tagBuffer; + { + icu::CharStringByteSink sink(&tagBuffer); + createTagString( + lang, + langLength, + script, + scriptLength, + region, + regionLength, + nullptr, + 0, + sink, + err); + } + if(U_FAILURE(*err)) { + goto error; + } + + likelySubtags = + findLikelySubtags( + tagBuffer.data(), + likelySubtagsBuffer, + sizeof(likelySubtagsBuffer), + err); + if(U_FAILURE(*err)) { + goto error; + } + + if (likelySubtags != nullptr) { + /* Always use the language tag from the + maximal string, since it may be more + specific than the one provided. */ + createTagStringWithAlternates( + nullptr, + 0, + nullptr, + 0, + nullptr, + 0, + variants, + variantsLength, + likelySubtags, + sink, + err); + return true; + } + } + + /** + * Try the language with just the script. + **/ + if (scriptLength > 0) { + + const char* likelySubtags = nullptr; + + icu::CharString tagBuffer; + { + icu::CharStringByteSink sink(&tagBuffer); + createTagString( + lang, + langLength, + script, + scriptLength, + nullptr, + 0, + nullptr, + 0, + sink, + err); + } + if(U_FAILURE(*err)) { + goto error; + } + + likelySubtags = + findLikelySubtags( + tagBuffer.data(), + likelySubtagsBuffer, + sizeof(likelySubtagsBuffer), + err); + if(U_FAILURE(*err)) { + goto error; + } + + if (likelySubtags != nullptr) { + /* Always use the language tag from the + maximal string, since it may be more + specific than the one provided. */ + createTagStringWithAlternates( + nullptr, + 0, + nullptr, + 0, + region, + regionLength, + variants, + variantsLength, + likelySubtags, + sink, + err); + return true; + } + } + + /** + * Try the language with just the region. + **/ + if (regionLength > 0) { + + const char* likelySubtags = nullptr; + + icu::CharString tagBuffer; + { + icu::CharStringByteSink sink(&tagBuffer); + createTagString( + lang, + langLength, + nullptr, + 0, + region, + regionLength, + nullptr, + 0, + sink, + err); + } + if(U_FAILURE(*err)) { + goto error; + } + + likelySubtags = + findLikelySubtags( + tagBuffer.data(), + likelySubtagsBuffer, + sizeof(likelySubtagsBuffer), + err); + if(U_FAILURE(*err)) { + goto error; + } + + if (likelySubtags != nullptr) { + /* Always use the language tag from the + maximal string, since it may be more + specific than the one provided. */ + createTagStringWithAlternates( + nullptr, + 0, + script, + scriptLength, + nullptr, + 0, + variants, + variantsLength, + likelySubtags, + sink, + err); + return true; + } + } + + /** + * Finally, try just the language. + **/ + { + const char* likelySubtags = nullptr; + + icu::CharString tagBuffer; + { + icu::CharStringByteSink sink(&tagBuffer); + createTagString( + lang, + langLength, + nullptr, + 0, + nullptr, + 0, + nullptr, + 0, + sink, + err); + } + if(U_FAILURE(*err)) { + goto error; + } + + likelySubtags = + findLikelySubtags( + tagBuffer.data(), + likelySubtagsBuffer, + sizeof(likelySubtagsBuffer), + err); + if(U_FAILURE(*err)) { + goto error; + } + + if (likelySubtags != nullptr) { + /* Always use the language tag from the + maximal string, since it may be more + specific than the one provided. */ + createTagStringWithAlternates( + nullptr, + 0, + script, + scriptLength, + region, + regionLength, + variants, + variantsLength, + likelySubtags, + sink, + err); + return true; + } + } + + return false; + +error: + + if (!U_FAILURE(*err)) { + *err = U_ILLEGAL_ARGUMENT_ERROR; + } + + return false; +} + +#define CHECK_TRAILING_VARIANT_SIZE(trailing, trailingLength) UPRV_BLOCK_MACRO_BEGIN { \ + int32_t count = 0; \ + int32_t i; \ + for (i = 0; i < trailingLength; i++) { \ + if (trailing[i] == '-' || trailing[i] == '_') { \ + count = 0; \ + if (count > 8) { \ + goto error; \ + } \ + } else if (trailing[i] == '@') { \ + break; \ + } else if (count > 8) { \ + goto error; \ + } else { \ + count++; \ + } \ + } \ +} UPRV_BLOCK_MACRO_END + +static UBool +_uloc_addLikelySubtags(const char* localeID, + icu::ByteSink& sink, + UErrorCode* err) { + char lang[ULOC_LANG_CAPACITY]; + int32_t langLength = sizeof(lang); + char script[ULOC_SCRIPT_CAPACITY]; + int32_t scriptLength = sizeof(script); + char region[ULOC_COUNTRY_CAPACITY]; + int32_t regionLength = sizeof(region); + const char* trailing = ""; + int32_t trailingLength = 0; + int32_t trailingIndex = 0; + UBool success = false; + + if(U_FAILURE(*err)) { + goto error; + } + if (localeID == nullptr) { + goto error; + } + + trailingIndex = parseTagString( + localeID, + lang, + &langLength, + script, + &scriptLength, + region, + ®ionLength, + err); + if(U_FAILURE(*err)) { + /* Overflow indicates an illegal argument error */ + if (*err == U_BUFFER_OVERFLOW_ERROR) { + *err = U_ILLEGAL_ARGUMENT_ERROR; + } + + goto error; + } + + /* Find the length of the trailing portion. */ + while (_isIDSeparator(localeID[trailingIndex])) { + trailingIndex++; + } + trailing = &localeID[trailingIndex]; + trailingLength = (int32_t)uprv_strlen(trailing); + + CHECK_TRAILING_VARIANT_SIZE(trailing, trailingLength); + + success = + createLikelySubtagsString( + lang, + langLength, + script, + scriptLength, + region, + regionLength, + trailing, + trailingLength, + sink, + err); + + if (!success) { + const int32_t localIDLength = (int32_t)uprv_strlen(localeID); + + /* + * If we get here, we need to return localeID. + */ + sink.Append(localeID, localIDLength); + } + + return success; + +error: + + if (!U_FAILURE(*err)) { + *err = U_ILLEGAL_ARGUMENT_ERROR; + } + return false; +} + +// Add likely subtags to the sink +// return true if the value in the sink is produced by a match during the lookup +// return false if the value in the sink is the same as input because there are +// no match after the lookup. +static UBool _ulocimp_addLikelySubtags(const char*, icu::ByteSink&, UErrorCode*); + +static void +_uloc_minimizeSubtags(const char* localeID, + icu::ByteSink& sink, + UErrorCode* err) { + icu::CharString maximizedTagBuffer; + + char lang[ULOC_LANG_CAPACITY]; + int32_t langLength = sizeof(lang); + char script[ULOC_SCRIPT_CAPACITY]; + int32_t scriptLength = sizeof(script); + char region[ULOC_COUNTRY_CAPACITY]; + int32_t regionLength = sizeof(region); + const char* trailing = ""; + int32_t trailingLength = 0; + int32_t trailingIndex = 0; + UBool successGetMax = false; + + if(U_FAILURE(*err)) { + goto error; + } + else if (localeID == nullptr) { + goto error; + } + + trailingIndex = + parseTagString( + localeID, + lang, + &langLength, + script, + &scriptLength, + region, + ®ionLength, + err); + if(U_FAILURE(*err)) { + + /* Overflow indicates an illegal argument error */ + if (*err == U_BUFFER_OVERFLOW_ERROR) { + *err = U_ILLEGAL_ARGUMENT_ERROR; + } + + goto error; + } + + /* Find the spot where the variants or the keywords begin, if any. */ + while (_isIDSeparator(localeID[trailingIndex])) { + trailingIndex++; + } + trailing = &localeID[trailingIndex]; + trailingLength = (int32_t)uprv_strlen(trailing); + + CHECK_TRAILING_VARIANT_SIZE(trailing, trailingLength); + + { + icu::CharString base; + { + icu::CharStringByteSink baseSink(&base); + createTagString( + lang, + langLength, + script, + scriptLength, + region, + regionLength, + nullptr, + 0, + baseSink, + err); + } + + /** + * First, we need to first get the maximization + * from AddLikelySubtags. + **/ + { + icu::CharStringByteSink maxSink(&maximizedTagBuffer); + successGetMax = _ulocimp_addLikelySubtags(base.data(), maxSink, err); + } + } + + if(U_FAILURE(*err)) { + goto error; + } + + if (!successGetMax) { + /** + * If we got here, return the locale ID parameter unchanged. + **/ + const int32_t localeIDLength = (int32_t)uprv_strlen(localeID); + sink.Append(localeID, localeIDLength); + return; + } + + // In the following, the lang, script, region are referring to those in + // the maximizedTagBuffer, not the one in the localeID. + langLength = sizeof(lang); + scriptLength = sizeof(script); + regionLength = sizeof(region); + parseTagString( + maximizedTagBuffer.data(), + lang, + &langLength, + script, + &scriptLength, + region, + ®ionLength, + err); + if(U_FAILURE(*err)) { + goto error; + } + + /** + * Start first with just the language. + **/ + { + icu::CharString tagBuffer; + { + icu::CharStringByteSink tagSink(&tagBuffer); + createLikelySubtagsString( + lang, + langLength, + nullptr, + 0, + nullptr, + 0, + nullptr, + 0, + tagSink, + err); + } + + if(U_FAILURE(*err)) { + goto error; + } + else if (!tagBuffer.isEmpty() && + uprv_strnicmp( + maximizedTagBuffer.data(), + tagBuffer.data(), + tagBuffer.length()) == 0) { + + createTagString( + lang, + langLength, + nullptr, + 0, + nullptr, + 0, + trailing, + trailingLength, + sink, + err); + return; + } + } + + /** + * Next, try the language and region. + **/ + if (regionLength > 0) { + + icu::CharString tagBuffer; + { + icu::CharStringByteSink tagSink(&tagBuffer); + createLikelySubtagsString( + lang, + langLength, + nullptr, + 0, + region, + regionLength, + nullptr, + 0, + tagSink, + err); + } + + if(U_FAILURE(*err)) { + goto error; + } + else if (!tagBuffer.isEmpty() && + uprv_strnicmp( + maximizedTagBuffer.data(), + tagBuffer.data(), + tagBuffer.length()) == 0) { + + createTagString( + lang, + langLength, + nullptr, + 0, + region, + regionLength, + trailing, + trailingLength, + sink, + err); + return; + } + } + + /** + * Finally, try the language and script. This is our last chance, + * since trying with all three subtags would only yield the + * maximal version that we already have. + **/ + if (scriptLength > 0) { + icu::CharString tagBuffer; + { + icu::CharStringByteSink tagSink(&tagBuffer); + createLikelySubtagsString( + lang, + langLength, + script, + scriptLength, + nullptr, + 0, + nullptr, + 0, + tagSink, + err); + } + + if(U_FAILURE(*err)) { + goto error; + } + else if (!tagBuffer.isEmpty() && + uprv_strnicmp( + maximizedTagBuffer.data(), + tagBuffer.data(), + tagBuffer.length()) == 0) { + + createTagString( + lang, + langLength, + script, + scriptLength, + nullptr, + 0, + trailing, + trailingLength, + sink, + err); + return; + } + } + + { + /** + * If we got here, return the max + trail. + **/ + createTagString( + lang, + langLength, + script, + scriptLength, + region, + regionLength, + trailing, + trailingLength, + sink, + err); + return; + } + +error: + + if (!U_FAILURE(*err)) { + *err = U_ILLEGAL_ARGUMENT_ERROR; + } +} + +static int32_t +do_canonicalize(const char* localeID, + char* buffer, + int32_t bufferCapacity, + UErrorCode* err) +{ + int32_t canonicalizedSize = uloc_canonicalize( + localeID, + buffer, + bufferCapacity, + err); + + if (*err == U_STRING_NOT_TERMINATED_WARNING || + *err == U_BUFFER_OVERFLOW_ERROR) { + return canonicalizedSize; + } + else if (U_FAILURE(*err)) { + + return -1; + } + else { + return canonicalizedSize; + } +} + +U_CAPI int32_t U_EXPORT2 +uloc_addLikelySubtags(const char* localeID, + char* maximizedLocaleID, + int32_t maximizedLocaleIDCapacity, + UErrorCode* status) { + if (U_FAILURE(*status)) { + return 0; + } + + icu::CheckedArrayByteSink sink( + maximizedLocaleID, maximizedLocaleIDCapacity); + + ulocimp_addLikelySubtags(localeID, sink, status); + int32_t reslen = sink.NumberOfBytesAppended(); + + if (U_FAILURE(*status)) { + return sink.Overflowed() ? reslen : -1; + } + + if (sink.Overflowed()) { + *status = U_BUFFER_OVERFLOW_ERROR; + } else { + u_terminateChars( + maximizedLocaleID, maximizedLocaleIDCapacity, reslen, status); + } + + return reslen; +} + +static UBool +_ulocimp_addLikelySubtags(const char* localeID, + icu::ByteSink& sink, + UErrorCode* status) { + PreflightingLocaleIDBuffer localeBuffer; + do { + localeBuffer.requestedCapacity = do_canonicalize(localeID, localeBuffer.getBuffer(), + localeBuffer.getCapacity(), status); + } while (localeBuffer.needToTryAgain(status)); + + if (U_SUCCESS(*status)) { + return _uloc_addLikelySubtags(localeBuffer.getBuffer(), sink, status); + } else { + return false; + } +} + +U_CAPI void U_EXPORT2 +ulocimp_addLikelySubtags(const char* localeID, + icu::ByteSink& sink, + UErrorCode* status) { + _ulocimp_addLikelySubtags(localeID, sink, status); +} + +U_CAPI int32_t U_EXPORT2 +uloc_minimizeSubtags(const char* localeID, + char* minimizedLocaleID, + int32_t minimizedLocaleIDCapacity, + UErrorCode* status) { + if (U_FAILURE(*status)) { + return 0; + } + + icu::CheckedArrayByteSink sink( + minimizedLocaleID, minimizedLocaleIDCapacity); + + ulocimp_minimizeSubtags(localeID, sink, status); + int32_t reslen = sink.NumberOfBytesAppended(); + + if (U_FAILURE(*status)) { + return sink.Overflowed() ? reslen : -1; + } + + if (sink.Overflowed()) { + *status = U_BUFFER_OVERFLOW_ERROR; + } else { + u_terminateChars( + minimizedLocaleID, minimizedLocaleIDCapacity, reslen, status); + } + + return reslen; +} + +U_CAPI void U_EXPORT2 +ulocimp_minimizeSubtags(const char* localeID, + icu::ByteSink& sink, + UErrorCode* status) { + PreflightingLocaleIDBuffer localeBuffer; + do { + localeBuffer.requestedCapacity = do_canonicalize(localeID, localeBuffer.getBuffer(), + localeBuffer.getCapacity(), status); + } while (localeBuffer.needToTryAgain(status)); + + _uloc_minimizeSubtags(localeBuffer.getBuffer(), sink, status); +} + +// Pairs of (language subtag, + or -) for finding out fast if common languages +// are LTR (minus) or RTL (plus). +static const char LANG_DIR_STRING[] = + "root-en-es-pt-zh-ja-ko-de-fr-it-ar+he+fa+ru-nl-pl-th-tr-"; + +// Implemented here because this calls ulocimp_addLikelySubtags(). +U_CAPI UBool U_EXPORT2 +uloc_isRightToLeft(const char *locale) { + UErrorCode errorCode = U_ZERO_ERROR; + char script[8]; + int32_t scriptLength = uloc_getScript(locale, script, UPRV_LENGTHOF(script), &errorCode); + if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING || + scriptLength == 0) { + // Fastpath: We know the likely scripts and their writing direction + // for some common languages. + errorCode = U_ZERO_ERROR; + char lang[8]; + int32_t langLength = uloc_getLanguage(locale, lang, UPRV_LENGTHOF(lang), &errorCode); + if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING) { + return false; + } + if (langLength > 0) { + const char* langPtr = uprv_strstr(LANG_DIR_STRING, lang); + if (langPtr != nullptr) { + switch (langPtr[langLength]) { + case '-': return false; + case '+': return true; + default: break; // partial match of a longer code + } + } + } + // Otherwise, find the likely script. + errorCode = U_ZERO_ERROR; + icu::CharString likely; + { + icu::CharStringByteSink sink(&likely); + ulocimp_addLikelySubtags(locale, sink, &errorCode); + } + if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING) { + return false; + } + scriptLength = uloc_getScript(likely.data(), script, UPRV_LENGTHOF(script), &errorCode); + if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING || + scriptLength == 0) { + return false; + } + } + UScriptCode scriptCode = (UScriptCode)u_getPropertyValueEnum(UCHAR_SCRIPT, script); + return uscript_isRightToLeft(scriptCode); +} + +U_NAMESPACE_BEGIN + +UBool +Locale::isRightToLeft() const { + return uloc_isRightToLeft(getBaseName()); +} + +U_NAMESPACE_END + +// The following must at least allow for rg key value (6) plus terminator (1). +#define ULOC_RG_BUFLEN 8 + +U_CAPI int32_t U_EXPORT2 +ulocimp_getRegionForSupplementalData(const char *localeID, UBool inferRegion, + char *region, int32_t regionCapacity, UErrorCode* status) { + if (U_FAILURE(*status)) { + return 0; + } + char rgBuf[ULOC_RG_BUFLEN]; + UErrorCode rgStatus = U_ZERO_ERROR; + + // First check for rg keyword value + int32_t rgLen = uloc_getKeywordValue(localeID, "rg", rgBuf, ULOC_RG_BUFLEN, &rgStatus); + if (U_FAILURE(rgStatus) || rgLen != 6) { + rgLen = 0; + } else { + // rgBuf guaranteed to be zero terminated here, with text len 6 + char *rgPtr = rgBuf; + for (; *rgPtr!= 0; rgPtr++) { + *rgPtr = uprv_toupper(*rgPtr); + } + rgLen = (uprv_strcmp(rgBuf+2, "ZZZZ") == 0)? 2: 0; + } + + if (rgLen == 0) { + // No valid rg keyword value, try for unicode_region_subtag + rgLen = uloc_getCountry(localeID, rgBuf, ULOC_RG_BUFLEN, status); + if (U_FAILURE(*status)) { + rgLen = 0; + } else if (rgLen == 0 && inferRegion) { + // no unicode_region_subtag but inferRegion true, try likely subtags + rgStatus = U_ZERO_ERROR; + icu::CharString locBuf; + { + icu::CharStringByteSink sink(&locBuf); + ulocimp_addLikelySubtags(localeID, sink, &rgStatus); + } + if (U_SUCCESS(rgStatus)) { + rgLen = uloc_getCountry(locBuf.data(), rgBuf, ULOC_RG_BUFLEN, status); + if (U_FAILURE(*status)) { + rgLen = 0; + } + } + } + } + + rgBuf[rgLen] = 0; + uprv_strncpy(region, rgBuf, regionCapacity); + return u_terminateChars(region, regionCapacity, rgLen, status); +} + diff --git a/deps/icu-small/source/common/loclikelysubtags.cpp b/deps/icu-small/source/common/loclikelysubtags.cpp index e913c66a35b9d0..14e8990215d856 100644 --- a/deps/icu-small/source/common/loclikelysubtags.cpp +++ b/deps/icu-small/source/common/loclikelysubtags.cpp @@ -1,683 +1,683 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// loclikelysubtags.cpp -// created: 2019may08 Markus W. Scherer - -#include -#include "unicode/utypes.h" -#include "unicode/bytestrie.h" -#include "unicode/localpointer.h" -#include "unicode/locid.h" -#include "unicode/uobject.h" -#include "unicode/ures.h" -#include "charstr.h" -#include "cstring.h" -#include "loclikelysubtags.h" -#include "lsr.h" -#include "uassert.h" -#include "ucln_cmn.h" -#include "uhash.h" -#include "uinvchar.h" -#include "umutex.h" -#include "uniquecharstr.h" -#include "uresdata.h" -#include "uresimp.h" - -U_NAMESPACE_BEGIN - -namespace { - -constexpr char PSEUDO_ACCENTS_PREFIX = '\''; // -XA, -PSACCENT -constexpr char PSEUDO_BIDI_PREFIX = '+'; // -XB, -PSBIDI -constexpr char PSEUDO_CRACKED_PREFIX = ','; // -XC, -PSCRACK - -} // namespace - -LocaleDistanceData::LocaleDistanceData(LocaleDistanceData &&data) : - distanceTrieBytes(data.distanceTrieBytes), - regionToPartitions(data.regionToPartitions), - partitions(data.partitions), - paradigms(data.paradigms), paradigmsLength(data.paradigmsLength), - distances(data.distances) { - data.partitions = nullptr; - data.paradigms = nullptr; -} - -LocaleDistanceData::~LocaleDistanceData() { - uprv_free(partitions); - delete[] paradigms; -} - -// TODO(ICU-20777): Rename to just LikelySubtagsData. -struct XLikelySubtagsData { - UResourceBundle *langInfoBundle = nullptr; - UniqueCharStrings strings; - CharStringMap languageAliases; - CharStringMap regionAliases; - const uint8_t *trieBytes = nullptr; - LSR *lsrs = nullptr; - int32_t lsrsLength = 0; - - LocaleDistanceData distanceData; - - XLikelySubtagsData(UErrorCode &errorCode) : strings(errorCode) {} - - ~XLikelySubtagsData() { - ures_close(langInfoBundle); - delete[] lsrs; - } - - void load(UErrorCode &errorCode) { - langInfoBundle = ures_openDirect(nullptr, "langInfo", &errorCode); - if (U_FAILURE(errorCode)) { return; } - StackUResourceBundle stackTempBundle; - ResourceDataValue value; - ures_getValueWithFallback(langInfoBundle, "likely", stackTempBundle.getAlias(), - value, errorCode); - ResourceTable likelyTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - - // Read all strings in the resource bundle and convert them to invariant char *. - LocalMemory languageIndexes, regionIndexes, lsrSubtagIndexes; - int32_t languagesLength = 0, regionsLength = 0, lsrSubtagsLength = 0; - if (!readStrings(likelyTable, "languageAliases", value, - languageIndexes, languagesLength, errorCode) || - !readStrings(likelyTable, "regionAliases", value, - regionIndexes, regionsLength, errorCode) || - !readStrings(likelyTable, "lsrs", value, - lsrSubtagIndexes,lsrSubtagsLength, errorCode)) { - return; - } - if ((languagesLength & 1) != 0 || - (regionsLength & 1) != 0 || - (lsrSubtagsLength % 3) != 0) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - if (lsrSubtagsLength == 0) { - errorCode = U_MISSING_RESOURCE_ERROR; - return; - } - - if (!likelyTable.findValue("trie", value)) { - errorCode = U_MISSING_RESOURCE_ERROR; - return; - } - int32_t length; - trieBytes = value.getBinary(length, errorCode); - if (U_FAILURE(errorCode)) { return; } - - // Also read distance/matcher data if available, - // to open & keep only one resource bundle pointer - // and to use one single UniqueCharStrings. - UErrorCode matchErrorCode = U_ZERO_ERROR; - ures_getValueWithFallback(langInfoBundle, "match", stackTempBundle.getAlias(), - value, matchErrorCode); - LocalMemory partitionIndexes, paradigmSubtagIndexes; - int32_t partitionsLength = 0, paradigmSubtagsLength = 0; - if (U_SUCCESS(matchErrorCode)) { - ResourceTable matchTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - - if (matchTable.findValue("trie", value)) { - distanceData.distanceTrieBytes = value.getBinary(length, errorCode); - if (U_FAILURE(errorCode)) { return; } - } - - if (matchTable.findValue("regionToPartitions", value)) { - distanceData.regionToPartitions = value.getBinary(length, errorCode); - if (U_FAILURE(errorCode)) { return; } - if (length < LSR::REGION_INDEX_LIMIT) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - } - - if (!readStrings(matchTable, "partitions", value, - partitionIndexes, partitionsLength, errorCode) || - !readStrings(matchTable, "paradigms", value, - paradigmSubtagIndexes, paradigmSubtagsLength, errorCode)) { - return; - } - if ((paradigmSubtagsLength % 3) != 0) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - - if (matchTable.findValue("distances", value)) { - distanceData.distances = value.getIntVector(length, errorCode); - if (U_FAILURE(errorCode)) { return; } - if (length < 4) { // LocaleDistance IX_LIMIT - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - } - } else if (matchErrorCode == U_MISSING_RESOURCE_ERROR) { - // ok for likely subtags - } else { // error other than missing resource - errorCode = matchErrorCode; - return; - } - - // Fetch & store invariant-character versions of strings - // only after we have collected and de-duplicated all of them. - strings.freeze(); - - languageAliases = CharStringMap(languagesLength / 2, errorCode); - for (int32_t i = 0; i < languagesLength; i += 2) { - languageAliases.put(strings.get(languageIndexes[i]), - strings.get(languageIndexes[i + 1]), errorCode); - } - - regionAliases = CharStringMap(regionsLength / 2, errorCode); - for (int32_t i = 0; i < regionsLength; i += 2) { - regionAliases.put(strings.get(regionIndexes[i]), - strings.get(regionIndexes[i + 1]), errorCode); - } - if (U_FAILURE(errorCode)) { return; } - - lsrsLength = lsrSubtagsLength / 3; - lsrs = new LSR[lsrsLength]; - if (lsrs == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - for (int32_t i = 0, j = 0; i < lsrSubtagsLength; i += 3, ++j) { - lsrs[j] = LSR(strings.get(lsrSubtagIndexes[i]), - strings.get(lsrSubtagIndexes[i + 1]), - strings.get(lsrSubtagIndexes[i + 2]), - LSR::IMPLICIT_LSR); - } - - if (partitionsLength > 0) { - distanceData.partitions = static_cast( - uprv_malloc(partitionsLength * sizeof(const char *))); - if (distanceData.partitions == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - for (int32_t i = 0; i < partitionsLength; ++i) { - distanceData.partitions[i] = strings.get(partitionIndexes[i]); - } - } - - if (paradigmSubtagsLength > 0) { - distanceData.paradigmsLength = paradigmSubtagsLength / 3; - LSR *paradigms = new LSR[distanceData.paradigmsLength]; - if (paradigms == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - for (int32_t i = 0, j = 0; i < paradigmSubtagsLength; i += 3, ++j) { - paradigms[j] = LSR(strings.get(paradigmSubtagIndexes[i]), - strings.get(paradigmSubtagIndexes[i + 1]), - strings.get(paradigmSubtagIndexes[i + 2]), - LSR::DONT_CARE_FLAGS); - } - distanceData.paradigms = paradigms; - } - } - -private: - bool readStrings(const ResourceTable &table, const char *key, ResourceValue &value, - LocalMemory &indexes, int32_t &length, UErrorCode &errorCode) { - if (table.findValue(key, value)) { - ResourceArray stringArray = value.getArray(errorCode); - if (U_FAILURE(errorCode)) { return false; } - length = stringArray.getSize(); - if (length == 0) { return true; } - int32_t *rawIndexes = indexes.allocateInsteadAndCopy(length); - if (rawIndexes == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return false; - } - for (int i = 0; i < length; ++i) { - stringArray.getValue(i, value); // returns true because i < length - rawIndexes[i] = strings.add(value.getUnicodeString(errorCode), errorCode); - if (U_FAILURE(errorCode)) { return false; } - } - } - return true; - } -}; - -namespace { - -XLikelySubtags *gLikelySubtags = nullptr; -UInitOnce gInitOnce {}; - -UBool U_CALLCONV cleanup() { - delete gLikelySubtags; - gLikelySubtags = nullptr; - gInitOnce.reset(); - return true; -} - -} // namespace - -void U_CALLCONV XLikelySubtags::initLikelySubtags(UErrorCode &errorCode) { - // This function is invoked only via umtx_initOnce(). - U_ASSERT(gLikelySubtags == nullptr); - XLikelySubtagsData data(errorCode); - data.load(errorCode); - if (U_FAILURE(errorCode)) { return; } - gLikelySubtags = new XLikelySubtags(data); - if (gLikelySubtags == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - ucln_common_registerCleanup(UCLN_COMMON_LIKELY_SUBTAGS, cleanup); -} - -const XLikelySubtags *XLikelySubtags::getSingleton(UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return nullptr; } - umtx_initOnce(gInitOnce, &XLikelySubtags::initLikelySubtags, errorCode); - return gLikelySubtags; -} - -XLikelySubtags::XLikelySubtags(XLikelySubtagsData &data) : - langInfoBundle(data.langInfoBundle), - strings(data.strings.orphanCharStrings()), - languageAliases(std::move(data.languageAliases)), - regionAliases(std::move(data.regionAliases)), - trie(data.trieBytes), - lsrs(data.lsrs), -#if U_DEBUG - lsrsLength(data.lsrsLength), -#endif - distanceData(std::move(data.distanceData)) { - data.langInfoBundle = nullptr; - data.lsrs = nullptr; - - // Cache the result of looking up language="und" encoded as "*", and "und-Zzzz" ("**"). - UStringTrieResult result = trie.next(u'*'); - U_ASSERT(USTRINGTRIE_HAS_NEXT(result)); - trieUndState = trie.getState64(); - result = trie.next(u'*'); - U_ASSERT(USTRINGTRIE_HAS_NEXT(result)); - trieUndZzzzState = trie.getState64(); - result = trie.next(u'*'); - U_ASSERT(USTRINGTRIE_HAS_VALUE(result)); - defaultLsrIndex = trie.getValue(); - trie.reset(); - - for (char16_t c = u'a'; c <= u'z'; ++c) { - result = trie.next(c); - if (result == USTRINGTRIE_NO_VALUE) { - trieFirstLetterStates[c - u'a'] = trie.getState64(); - } - trie.reset(); - } -} - -XLikelySubtags::~XLikelySubtags() { - ures_close(langInfoBundle); - delete strings; - delete[] lsrs; -} - -LSR XLikelySubtags::makeMaximizedLsrFrom(const Locale &locale, UErrorCode &errorCode) const { - const char *name = locale.getName(); - if (uprv_isAtSign(name[0]) && name[1] == 'x' && name[2] == '=') { // name.startsWith("@x=") - // Private use language tag x-subtag-subtag... which CLDR changes to - // und-x-subtag-subtag... - return LSR(name, "", "", LSR::EXPLICIT_LSR); - } - return makeMaximizedLsr(locale.getLanguage(), locale.getScript(), locale.getCountry(), - locale.getVariant(), errorCode); -} - -namespace { - -const char *getCanonical(const CharStringMap &aliases, const char *alias) { - const char *canonical = aliases.get(alias); - return canonical == nullptr ? alias : canonical; -} - -} // namespace - -LSR XLikelySubtags::makeMaximizedLsr(const char *language, const char *script, const char *region, - const char *variant, UErrorCode &errorCode) const { - // Handle pseudolocales like en-XA, ar-XB, fr-PSCRACK. - // They should match only themselves, - // not other locales with what looks like the same language and script subtags. - char c1; - if (region[0] == 'X' && (c1 = region[1]) != 0 && region[2] == 0) { - switch (c1) { - case 'A': - return LSR(PSEUDO_ACCENTS_PREFIX, language, script, region, - LSR::EXPLICIT_LSR, errorCode); - case 'B': - return LSR(PSEUDO_BIDI_PREFIX, language, script, region, - LSR::EXPLICIT_LSR, errorCode); - case 'C': - return LSR(PSEUDO_CRACKED_PREFIX, language, script, region, - LSR::EXPLICIT_LSR, errorCode); - default: // normal locale - break; - } - } - - if (variant[0] == 'P' && variant[1] == 'S') { - int32_t lsrFlags = *region == 0 ? - LSR::EXPLICIT_LANGUAGE | LSR::EXPLICIT_SCRIPT : LSR::EXPLICIT_LSR; - if (uprv_strcmp(variant, "PSACCENT") == 0) { - return LSR(PSEUDO_ACCENTS_PREFIX, language, script, - *region == 0 ? "XA" : region, lsrFlags, errorCode); - } else if (uprv_strcmp(variant, "PSBIDI") == 0) { - return LSR(PSEUDO_BIDI_PREFIX, language, script, - *region == 0 ? "XB" : region, lsrFlags, errorCode); - } else if (uprv_strcmp(variant, "PSCRACK") == 0) { - return LSR(PSEUDO_CRACKED_PREFIX, language, script, - *region == 0 ? "XC" : region, lsrFlags, errorCode); - } - // else normal locale - } - - language = getCanonical(languageAliases, language); - // (We have no script mappings.) - region = getCanonical(regionAliases, region); - return maximize(language, script, region); -} - -LSR XLikelySubtags::maximize(const char *language, const char *script, const char *region) const { - if (uprv_strcmp(language, "und") == 0) { - language = ""; - } - if (uprv_strcmp(script, "Zzzz") == 0) { - script = ""; - } - if (uprv_strcmp(region, "ZZ") == 0) { - region = ""; - } - if (*script != 0 && *region != 0 && *language != 0) { - return LSR(language, script, region, LSR::EXPLICIT_LSR); // already maximized - } - - uint32_t retainOldMask = 0; - BytesTrie iter(trie); - uint64_t state; - int32_t value; - // Small optimization: Array lookup for first language letter. - int32_t c0; - if (0 <= (c0 = uprv_lowerOrdinal(language[0])) && c0 <= 25 && - language[1] != 0 && // language.length() >= 2 - (state = trieFirstLetterStates[c0]) != 0) { - value = trieNext(iter.resetToState64(state), language, 1); - } else { - value = trieNext(iter, language, 0); - } - if (value >= 0) { - if (*language != 0) { - retainOldMask |= 4; - } - state = iter.getState64(); - } else { - retainOldMask |= 4; - iter.resetToState64(trieUndState); // "und" ("*") - state = 0; - } - - if (value > 0) { - // Intermediate or final value from just language. - if (value == SKIP_SCRIPT) { - value = 0; - } - if (*script != 0) { - retainOldMask |= 2; - } - } else { - value = trieNext(iter, script, 0); - if (value >= 0) { - if (*script != 0) { - retainOldMask |= 2; - } - state = iter.getState64(); - } else { - retainOldMask |= 2; - if (state == 0) { - iter.resetToState64(trieUndZzzzState); // "und-Zzzz" ("**") - } else { - iter.resetToState64(state); - value = trieNext(iter, "", 0); - U_ASSERT(value >= 0); - state = iter.getState64(); - } - } - } - - if (value > 0) { - // Final value from just language or language+script. - if (*region != 0) { - retainOldMask |= 1; - } - } else { - value = trieNext(iter, region, 0); - if (value >= 0) { - if (*region != 0) { - retainOldMask |= 1; - } - } else { - retainOldMask |= 1; - if (state == 0) { - value = defaultLsrIndex; - } else { - iter.resetToState64(state); - value = trieNext(iter, "", 0); - U_ASSERT(value > 0); - } - } - } - U_ASSERT(value < lsrsLength); - const LSR &result = lsrs[value]; - - if (*language == 0) { - language = "und"; - } - - if (retainOldMask == 0) { - // Quickly return a copy of the lookup-result LSR - // without new allocation of the subtags. - return LSR(result.language, result.script, result.region, result.flags); - } - if ((retainOldMask & 4) == 0) { - language = result.language; - } - if ((retainOldMask & 2) == 0) { - script = result.script; - } - if ((retainOldMask & 1) == 0) { - region = result.region; - } - // retainOldMask flags = LSR explicit-subtag flags - return LSR(language, script, region, retainOldMask); -} - -int32_t XLikelySubtags::compareLikely(const LSR &lsr, const LSR &other, int32_t likelyInfo) const { - // If likelyInfo >= 0: - // likelyInfo bit 1 is set if the previous comparison with lsr - // was for equal language and script. - // Otherwise the scripts differed. - if (uprv_strcmp(lsr.language, other.language) != 0) { - return 0xfffffffc; // negative, lsr not better than other - } - if (uprv_strcmp(lsr.script, other.script) != 0) { - int32_t index; - if (likelyInfo >= 0 && (likelyInfo & 2) == 0) { - index = likelyInfo >> 2; - } else { - index = getLikelyIndex(lsr.language, ""); - likelyInfo = index << 2; - } - const LSR &likely = lsrs[index]; - if (uprv_strcmp(lsr.script, likely.script) == 0) { - return likelyInfo | 1; - } else { - return likelyInfo & ~1; - } - } - if (uprv_strcmp(lsr.region, other.region) != 0) { - int32_t index; - if (likelyInfo >= 0 && (likelyInfo & 2) != 0) { - index = likelyInfo >> 2; - } else { - index = getLikelyIndex(lsr.language, lsr.region); - likelyInfo = (index << 2) | 2; - } - const LSR &likely = lsrs[index]; - if (uprv_strcmp(lsr.region, likely.region) == 0) { - return likelyInfo | 1; - } else { - return likelyInfo & ~1; - } - } - return likelyInfo & ~1; // lsr not better than other -} - -// Subset of maximize(). -int32_t XLikelySubtags::getLikelyIndex(const char *language, const char *script) const { - if (uprv_strcmp(language, "und") == 0) { - language = ""; - } - if (uprv_strcmp(script, "Zzzz") == 0) { - script = ""; - } - - BytesTrie iter(trie); - uint64_t state; - int32_t value; - // Small optimization: Array lookup for first language letter. - int32_t c0; - if (0 <= (c0 = uprv_lowerOrdinal(language[0])) && c0 <= 25 && - language[1] != 0 && // language.length() >= 2 - (state = trieFirstLetterStates[c0]) != 0) { - value = trieNext(iter.resetToState64(state), language, 1); - } else { - value = trieNext(iter, language, 0); - } - if (value >= 0) { - state = iter.getState64(); - } else { - iter.resetToState64(trieUndState); // "und" ("*") - state = 0; - } - - if (value > 0) { - // Intermediate or final value from just language. - if (value == SKIP_SCRIPT) { - value = 0; - } - } else { - value = trieNext(iter, script, 0); - if (value >= 0) { - state = iter.getState64(); - } else { - if (state == 0) { - iter.resetToState64(trieUndZzzzState); // "und-Zzzz" ("**") - } else { - iter.resetToState64(state); - value = trieNext(iter, "", 0); - U_ASSERT(value >= 0); - state = iter.getState64(); - } - } - } - - if (value > 0) { - // Final value from just language or language+script. - } else { - value = trieNext(iter, "", 0); - U_ASSERT(value > 0); - } - U_ASSERT(value < lsrsLength); - return value; -} - -int32_t XLikelySubtags::trieNext(BytesTrie &iter, const char *s, int32_t i) { - UStringTrieResult result; - uint8_t c; - if ((c = s[i]) == 0) { - result = iter.next(u'*'); - } else { - for (;;) { - c = uprv_invCharToAscii(c); - // EBCDIC: If s[i] is not an invariant character, - // then c is now 0 and will simply not match anything, which is harmless. - uint8_t next = s[++i]; - if (next != 0) { - if (!USTRINGTRIE_HAS_NEXT(iter.next(c))) { - return -1; - } - } else { - // last character of this subtag - result = iter.next(c | 0x80); - break; - } - c = next; - } - } - switch (result) { - case USTRINGTRIE_NO_MATCH: return -1; - case USTRINGTRIE_NO_VALUE: return 0; - case USTRINGTRIE_INTERMEDIATE_VALUE: - U_ASSERT(iter.getValue() == SKIP_SCRIPT); - return SKIP_SCRIPT; - case USTRINGTRIE_FINAL_VALUE: return iter.getValue(); - default: return -1; - } -} - -// TODO(ICU-20777): Switch Locale/uloc_ likely-subtags API from the old code -// in loclikely.cpp to this new code, including activating this -// minimizeSubtags() function. The LocaleMatcher does not minimize. -#if 0 -LSR XLikelySubtags::minimizeSubtags(const char *languageIn, const char *scriptIn, - const char *regionIn, ULocale.Minimize fieldToFavor, - UErrorCode &errorCode) const { - LSR result = maximize(languageIn, scriptIn, regionIn); - - // We could try just a series of checks, like: - // LSR result2 = addLikelySubtags(languageIn, "", ""); - // if result.equals(result2) return result2; - // However, we can optimize 2 of the cases: - // (languageIn, "", "") - // (languageIn, "", regionIn) - - // value00 = lookup(result.language, "", "") - BytesTrie iter = new BytesTrie(trie); - int value = trieNext(iter, result.language, 0); - U_ASSERT(value >= 0); - if (value == 0) { - value = trieNext(iter, "", 0); - U_ASSERT(value >= 0); - if (value == 0) { - value = trieNext(iter, "", 0); - } - } - U_ASSERT(value > 0); - LSR value00 = lsrs[value]; - boolean favorRegionOk = false; - if (result.script.equals(value00.script)) { //script is default - if (result.region.equals(value00.region)) { - return new LSR(result.language, "", "", LSR.DONT_CARE_FLAGS); - } else if (fieldToFavor == ULocale.Minimize.FAVOR_REGION) { - return new LSR(result.language, "", result.region, LSR.DONT_CARE_FLAGS); - } else { - favorRegionOk = true; - } - } - - // The last case is not as easy to optimize. - // Maybe do later, but for now use the straightforward code. - LSR result2 = maximize(languageIn, scriptIn, ""); - if (result2.equals(result)) { - return new LSR(result.language, result.script, "", LSR.DONT_CARE_FLAGS); - } else if (favorRegionOk) { - return new LSR(result.language, "", result.region, LSR.DONT_CARE_FLAGS); - } - return result; -} -#endif - -U_NAMESPACE_END +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// loclikelysubtags.cpp +// created: 2019may08 Markus W. Scherer + +#include +#include "unicode/utypes.h" +#include "unicode/bytestrie.h" +#include "unicode/localpointer.h" +#include "unicode/locid.h" +#include "unicode/uobject.h" +#include "unicode/ures.h" +#include "charstr.h" +#include "cstring.h" +#include "loclikelysubtags.h" +#include "lsr.h" +#include "uassert.h" +#include "ucln_cmn.h" +#include "uhash.h" +#include "uinvchar.h" +#include "umutex.h" +#include "uniquecharstr.h" +#include "uresdata.h" +#include "uresimp.h" + +U_NAMESPACE_BEGIN + +namespace { + +constexpr char PSEUDO_ACCENTS_PREFIX = '\''; // -XA, -PSACCENT +constexpr char PSEUDO_BIDI_PREFIX = '+'; // -XB, -PSBIDI +constexpr char PSEUDO_CRACKED_PREFIX = ','; // -XC, -PSCRACK + +} // namespace + +LocaleDistanceData::LocaleDistanceData(LocaleDistanceData &&data) : + distanceTrieBytes(data.distanceTrieBytes), + regionToPartitions(data.regionToPartitions), + partitions(data.partitions), + paradigms(data.paradigms), paradigmsLength(data.paradigmsLength), + distances(data.distances) { + data.partitions = nullptr; + data.paradigms = nullptr; +} + +LocaleDistanceData::~LocaleDistanceData() { + uprv_free(partitions); + delete[] paradigms; +} + +// TODO(ICU-20777): Rename to just LikelySubtagsData. +struct XLikelySubtagsData { + UResourceBundle *langInfoBundle = nullptr; + UniqueCharStrings strings; + CharStringMap languageAliases; + CharStringMap regionAliases; + const uint8_t *trieBytes = nullptr; + LSR *lsrs = nullptr; + int32_t lsrsLength = 0; + + LocaleDistanceData distanceData; + + XLikelySubtagsData(UErrorCode &errorCode) : strings(errorCode) {} + + ~XLikelySubtagsData() { + ures_close(langInfoBundle); + delete[] lsrs; + } + + void load(UErrorCode &errorCode) { + langInfoBundle = ures_openDirect(nullptr, "langInfo", &errorCode); + if (U_FAILURE(errorCode)) { return; } + StackUResourceBundle stackTempBundle; + ResourceDataValue value; + ures_getValueWithFallback(langInfoBundle, "likely", stackTempBundle.getAlias(), + value, errorCode); + ResourceTable likelyTable = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + + // Read all strings in the resource bundle and convert them to invariant char *. + LocalMemory languageIndexes, regionIndexes, lsrSubtagIndexes; + int32_t languagesLength = 0, regionsLength = 0, lsrSubtagsLength = 0; + if (!readStrings(likelyTable, "languageAliases", value, + languageIndexes, languagesLength, errorCode) || + !readStrings(likelyTable, "regionAliases", value, + regionIndexes, regionsLength, errorCode) || + !readStrings(likelyTable, "lsrs", value, + lsrSubtagIndexes,lsrSubtagsLength, errorCode)) { + return; + } + if ((languagesLength & 1) != 0 || + (regionsLength & 1) != 0 || + (lsrSubtagsLength % 3) != 0) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + if (lsrSubtagsLength == 0) { + errorCode = U_MISSING_RESOURCE_ERROR; + return; + } + + if (!likelyTable.findValue("trie", value)) { + errorCode = U_MISSING_RESOURCE_ERROR; + return; + } + int32_t length; + trieBytes = value.getBinary(length, errorCode); + if (U_FAILURE(errorCode)) { return; } + + // Also read distance/matcher data if available, + // to open & keep only one resource bundle pointer + // and to use one single UniqueCharStrings. + UErrorCode matchErrorCode = U_ZERO_ERROR; + ures_getValueWithFallback(langInfoBundle, "match", stackTempBundle.getAlias(), + value, matchErrorCode); + LocalMemory partitionIndexes, paradigmSubtagIndexes; + int32_t partitionsLength = 0, paradigmSubtagsLength = 0; + if (U_SUCCESS(matchErrorCode)) { + ResourceTable matchTable = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + + if (matchTable.findValue("trie", value)) { + distanceData.distanceTrieBytes = value.getBinary(length, errorCode); + if (U_FAILURE(errorCode)) { return; } + } + + if (matchTable.findValue("regionToPartitions", value)) { + distanceData.regionToPartitions = value.getBinary(length, errorCode); + if (U_FAILURE(errorCode)) { return; } + if (length < LSR::REGION_INDEX_LIMIT) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + } + + if (!readStrings(matchTable, "partitions", value, + partitionIndexes, partitionsLength, errorCode) || + !readStrings(matchTable, "paradigms", value, + paradigmSubtagIndexes, paradigmSubtagsLength, errorCode)) { + return; + } + if ((paradigmSubtagsLength % 3) != 0) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + + if (matchTable.findValue("distances", value)) { + distanceData.distances = value.getIntVector(length, errorCode); + if (U_FAILURE(errorCode)) { return; } + if (length < 4) { // LocaleDistance IX_LIMIT + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + } + } else if (matchErrorCode == U_MISSING_RESOURCE_ERROR) { + // ok for likely subtags + } else { // error other than missing resource + errorCode = matchErrorCode; + return; + } + + // Fetch & store invariant-character versions of strings + // only after we have collected and de-duplicated all of them. + strings.freeze(); + + languageAliases = CharStringMap(languagesLength / 2, errorCode); + for (int32_t i = 0; i < languagesLength; i += 2) { + languageAliases.put(strings.get(languageIndexes[i]), + strings.get(languageIndexes[i + 1]), errorCode); + } + + regionAliases = CharStringMap(regionsLength / 2, errorCode); + for (int32_t i = 0; i < regionsLength; i += 2) { + regionAliases.put(strings.get(regionIndexes[i]), + strings.get(regionIndexes[i + 1]), errorCode); + } + if (U_FAILURE(errorCode)) { return; } + + lsrsLength = lsrSubtagsLength / 3; + lsrs = new LSR[lsrsLength]; + if (lsrs == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + for (int32_t i = 0, j = 0; i < lsrSubtagsLength; i += 3, ++j) { + lsrs[j] = LSR(strings.get(lsrSubtagIndexes[i]), + strings.get(lsrSubtagIndexes[i + 1]), + strings.get(lsrSubtagIndexes[i + 2]), + LSR::IMPLICIT_LSR); + } + + if (partitionsLength > 0) { + distanceData.partitions = static_cast( + uprv_malloc(partitionsLength * sizeof(const char *))); + if (distanceData.partitions == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + for (int32_t i = 0; i < partitionsLength; ++i) { + distanceData.partitions[i] = strings.get(partitionIndexes[i]); + } + } + + if (paradigmSubtagsLength > 0) { + distanceData.paradigmsLength = paradigmSubtagsLength / 3; + LSR *paradigms = new LSR[distanceData.paradigmsLength]; + if (paradigms == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + for (int32_t i = 0, j = 0; i < paradigmSubtagsLength; i += 3, ++j) { + paradigms[j] = LSR(strings.get(paradigmSubtagIndexes[i]), + strings.get(paradigmSubtagIndexes[i + 1]), + strings.get(paradigmSubtagIndexes[i + 2]), + LSR::DONT_CARE_FLAGS); + } + distanceData.paradigms = paradigms; + } + } + +private: + bool readStrings(const ResourceTable &table, const char *key, ResourceValue &value, + LocalMemory &indexes, int32_t &length, UErrorCode &errorCode) { + if (table.findValue(key, value)) { + ResourceArray stringArray = value.getArray(errorCode); + if (U_FAILURE(errorCode)) { return false; } + length = stringArray.getSize(); + if (length == 0) { return true; } + int32_t *rawIndexes = indexes.allocateInsteadAndCopy(length); + if (rawIndexes == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return false; + } + for (int i = 0; i < length; ++i) { + stringArray.getValue(i, value); // returns true because i < length + rawIndexes[i] = strings.add(value.getUnicodeString(errorCode), errorCode); + if (U_FAILURE(errorCode)) { return false; } + } + } + return true; + } +}; + +namespace { + +XLikelySubtags *gLikelySubtags = nullptr; +UInitOnce gInitOnce {}; + +UBool U_CALLCONV cleanup() { + delete gLikelySubtags; + gLikelySubtags = nullptr; + gInitOnce.reset(); + return true; +} + +} // namespace + +void U_CALLCONV XLikelySubtags::initLikelySubtags(UErrorCode &errorCode) { + // This function is invoked only via umtx_initOnce(). + U_ASSERT(gLikelySubtags == nullptr); + XLikelySubtagsData data(errorCode); + data.load(errorCode); + if (U_FAILURE(errorCode)) { return; } + gLikelySubtags = new XLikelySubtags(data); + if (gLikelySubtags == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + ucln_common_registerCleanup(UCLN_COMMON_LIKELY_SUBTAGS, cleanup); +} + +const XLikelySubtags *XLikelySubtags::getSingleton(UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return nullptr; } + umtx_initOnce(gInitOnce, &XLikelySubtags::initLikelySubtags, errorCode); + return gLikelySubtags; +} + +XLikelySubtags::XLikelySubtags(XLikelySubtagsData &data) : + langInfoBundle(data.langInfoBundle), + strings(data.strings.orphanCharStrings()), + languageAliases(std::move(data.languageAliases)), + regionAliases(std::move(data.regionAliases)), + trie(data.trieBytes), + lsrs(data.lsrs), +#if U_DEBUG + lsrsLength(data.lsrsLength), +#endif + distanceData(std::move(data.distanceData)) { + data.langInfoBundle = nullptr; + data.lsrs = nullptr; + + // Cache the result of looking up language="und" encoded as "*", and "und-Zzzz" ("**"). + UStringTrieResult result = trie.next(u'*'); + U_ASSERT(USTRINGTRIE_HAS_NEXT(result)); + trieUndState = trie.getState64(); + result = trie.next(u'*'); + U_ASSERT(USTRINGTRIE_HAS_NEXT(result)); + trieUndZzzzState = trie.getState64(); + result = trie.next(u'*'); + U_ASSERT(USTRINGTRIE_HAS_VALUE(result)); + defaultLsrIndex = trie.getValue(); + trie.reset(); + + for (char16_t c = u'a'; c <= u'z'; ++c) { + result = trie.next(c); + if (result == USTRINGTRIE_NO_VALUE) { + trieFirstLetterStates[c - u'a'] = trie.getState64(); + } + trie.reset(); + } +} + +XLikelySubtags::~XLikelySubtags() { + ures_close(langInfoBundle); + delete strings; + delete[] lsrs; +} + +LSR XLikelySubtags::makeMaximizedLsrFrom(const Locale &locale, UErrorCode &errorCode) const { + const char *name = locale.getName(); + if (uprv_isAtSign(name[0]) && name[1] == 'x' && name[2] == '=') { // name.startsWith("@x=") + // Private use language tag x-subtag-subtag... which CLDR changes to + // und-x-subtag-subtag... + return LSR(name, "", "", LSR::EXPLICIT_LSR); + } + return makeMaximizedLsr(locale.getLanguage(), locale.getScript(), locale.getCountry(), + locale.getVariant(), errorCode); +} + +namespace { + +const char *getCanonical(const CharStringMap &aliases, const char *alias) { + const char *canonical = aliases.get(alias); + return canonical == nullptr ? alias : canonical; +} + +} // namespace + +LSR XLikelySubtags::makeMaximizedLsr(const char *language, const char *script, const char *region, + const char *variant, UErrorCode &errorCode) const { + // Handle pseudolocales like en-XA, ar-XB, fr-PSCRACK. + // They should match only themselves, + // not other locales with what looks like the same language and script subtags. + char c1; + if (region[0] == 'X' && (c1 = region[1]) != 0 && region[2] == 0) { + switch (c1) { + case 'A': + return LSR(PSEUDO_ACCENTS_PREFIX, language, script, region, + LSR::EXPLICIT_LSR, errorCode); + case 'B': + return LSR(PSEUDO_BIDI_PREFIX, language, script, region, + LSR::EXPLICIT_LSR, errorCode); + case 'C': + return LSR(PSEUDO_CRACKED_PREFIX, language, script, region, + LSR::EXPLICIT_LSR, errorCode); + default: // normal locale + break; + } + } + + if (variant[0] == 'P' && variant[1] == 'S') { + int32_t lsrFlags = *region == 0 ? + LSR::EXPLICIT_LANGUAGE | LSR::EXPLICIT_SCRIPT : LSR::EXPLICIT_LSR; + if (uprv_strcmp(variant, "PSACCENT") == 0) { + return LSR(PSEUDO_ACCENTS_PREFIX, language, script, + *region == 0 ? "XA" : region, lsrFlags, errorCode); + } else if (uprv_strcmp(variant, "PSBIDI") == 0) { + return LSR(PSEUDO_BIDI_PREFIX, language, script, + *region == 0 ? "XB" : region, lsrFlags, errorCode); + } else if (uprv_strcmp(variant, "PSCRACK") == 0) { + return LSR(PSEUDO_CRACKED_PREFIX, language, script, + *region == 0 ? "XC" : region, lsrFlags, errorCode); + } + // else normal locale + } + + language = getCanonical(languageAliases, language); + // (We have no script mappings.) + region = getCanonical(regionAliases, region); + return maximize(language, script, region); +} + +LSR XLikelySubtags::maximize(const char *language, const char *script, const char *region) const { + if (uprv_strcmp(language, "und") == 0) { + language = ""; + } + if (uprv_strcmp(script, "Zzzz") == 0) { + script = ""; + } + if (uprv_strcmp(region, "ZZ") == 0) { + region = ""; + } + if (*script != 0 && *region != 0 && *language != 0) { + return LSR(language, script, region, LSR::EXPLICIT_LSR); // already maximized + } + + uint32_t retainOldMask = 0; + BytesTrie iter(trie); + uint64_t state; + int32_t value; + // Small optimization: Array lookup for first language letter. + int32_t c0; + if (0 <= (c0 = uprv_lowerOrdinal(language[0])) && c0 <= 25 && + language[1] != 0 && // language.length() >= 2 + (state = trieFirstLetterStates[c0]) != 0) { + value = trieNext(iter.resetToState64(state), language, 1); + } else { + value = trieNext(iter, language, 0); + } + if (value >= 0) { + if (*language != 0) { + retainOldMask |= 4; + } + state = iter.getState64(); + } else { + retainOldMask |= 4; + iter.resetToState64(trieUndState); // "und" ("*") + state = 0; + } + + if (value > 0) { + // Intermediate or final value from just language. + if (value == SKIP_SCRIPT) { + value = 0; + } + if (*script != 0) { + retainOldMask |= 2; + } + } else { + value = trieNext(iter, script, 0); + if (value >= 0) { + if (*script != 0) { + retainOldMask |= 2; + } + state = iter.getState64(); + } else { + retainOldMask |= 2; + if (state == 0) { + iter.resetToState64(trieUndZzzzState); // "und-Zzzz" ("**") + } else { + iter.resetToState64(state); + value = trieNext(iter, "", 0); + U_ASSERT(value >= 0); + state = iter.getState64(); + } + } + } + + if (value > 0) { + // Final value from just language or language+script. + if (*region != 0) { + retainOldMask |= 1; + } + } else { + value = trieNext(iter, region, 0); + if (value >= 0) { + if (*region != 0) { + retainOldMask |= 1; + } + } else { + retainOldMask |= 1; + if (state == 0) { + value = defaultLsrIndex; + } else { + iter.resetToState64(state); + value = trieNext(iter, "", 0); + U_ASSERT(value > 0); + } + } + } + U_ASSERT(value < lsrsLength); + const LSR &result = lsrs[value]; + + if (*language == 0) { + language = "und"; + } + + if (retainOldMask == 0) { + // Quickly return a copy of the lookup-result LSR + // without new allocation of the subtags. + return LSR(result.language, result.script, result.region, result.flags); + } + if ((retainOldMask & 4) == 0) { + language = result.language; + } + if ((retainOldMask & 2) == 0) { + script = result.script; + } + if ((retainOldMask & 1) == 0) { + region = result.region; + } + // retainOldMask flags = LSR explicit-subtag flags + return LSR(language, script, region, retainOldMask); +} + +int32_t XLikelySubtags::compareLikely(const LSR &lsr, const LSR &other, int32_t likelyInfo) const { + // If likelyInfo >= 0: + // likelyInfo bit 1 is set if the previous comparison with lsr + // was for equal language and script. + // Otherwise the scripts differed. + if (uprv_strcmp(lsr.language, other.language) != 0) { + return 0xfffffffc; // negative, lsr not better than other + } + if (uprv_strcmp(lsr.script, other.script) != 0) { + int32_t index; + if (likelyInfo >= 0 && (likelyInfo & 2) == 0) { + index = likelyInfo >> 2; + } else { + index = getLikelyIndex(lsr.language, ""); + likelyInfo = index << 2; + } + const LSR &likely = lsrs[index]; + if (uprv_strcmp(lsr.script, likely.script) == 0) { + return likelyInfo | 1; + } else { + return likelyInfo & ~1; + } + } + if (uprv_strcmp(lsr.region, other.region) != 0) { + int32_t index; + if (likelyInfo >= 0 && (likelyInfo & 2) != 0) { + index = likelyInfo >> 2; + } else { + index = getLikelyIndex(lsr.language, lsr.region); + likelyInfo = (index << 2) | 2; + } + const LSR &likely = lsrs[index]; + if (uprv_strcmp(lsr.region, likely.region) == 0) { + return likelyInfo | 1; + } else { + return likelyInfo & ~1; + } + } + return likelyInfo & ~1; // lsr not better than other +} + +// Subset of maximize(). +int32_t XLikelySubtags::getLikelyIndex(const char *language, const char *script) const { + if (uprv_strcmp(language, "und") == 0) { + language = ""; + } + if (uprv_strcmp(script, "Zzzz") == 0) { + script = ""; + } + + BytesTrie iter(trie); + uint64_t state; + int32_t value; + // Small optimization: Array lookup for first language letter. + int32_t c0; + if (0 <= (c0 = uprv_lowerOrdinal(language[0])) && c0 <= 25 && + language[1] != 0 && // language.length() >= 2 + (state = trieFirstLetterStates[c0]) != 0) { + value = trieNext(iter.resetToState64(state), language, 1); + } else { + value = trieNext(iter, language, 0); + } + if (value >= 0) { + state = iter.getState64(); + } else { + iter.resetToState64(trieUndState); // "und" ("*") + state = 0; + } + + if (value > 0) { + // Intermediate or final value from just language. + if (value == SKIP_SCRIPT) { + value = 0; + } + } else { + value = trieNext(iter, script, 0); + if (value >= 0) { + state = iter.getState64(); + } else { + if (state == 0) { + iter.resetToState64(trieUndZzzzState); // "und-Zzzz" ("**") + } else { + iter.resetToState64(state); + value = trieNext(iter, "", 0); + U_ASSERT(value >= 0); + state = iter.getState64(); + } + } + } + + if (value > 0) { + // Final value from just language or language+script. + } else { + value = trieNext(iter, "", 0); + U_ASSERT(value > 0); + } + U_ASSERT(value < lsrsLength); + return value; +} + +int32_t XLikelySubtags::trieNext(BytesTrie &iter, const char *s, int32_t i) { + UStringTrieResult result; + uint8_t c; + if ((c = s[i]) == 0) { + result = iter.next(u'*'); + } else { + for (;;) { + c = uprv_invCharToAscii(c); + // EBCDIC: If s[i] is not an invariant character, + // then c is now 0 and will simply not match anything, which is harmless. + uint8_t next = s[++i]; + if (next != 0) { + if (!USTRINGTRIE_HAS_NEXT(iter.next(c))) { + return -1; + } + } else { + // last character of this subtag + result = iter.next(c | 0x80); + break; + } + c = next; + } + } + switch (result) { + case USTRINGTRIE_NO_MATCH: return -1; + case USTRINGTRIE_NO_VALUE: return 0; + case USTRINGTRIE_INTERMEDIATE_VALUE: + U_ASSERT(iter.getValue() == SKIP_SCRIPT); + return SKIP_SCRIPT; + case USTRINGTRIE_FINAL_VALUE: return iter.getValue(); + default: return -1; + } +} + +// TODO(ICU-20777): Switch Locale/uloc_ likely-subtags API from the old code +// in loclikely.cpp to this new code, including activating this +// minimizeSubtags() function. The LocaleMatcher does not minimize. +#if 0 +LSR XLikelySubtags::minimizeSubtags(const char *languageIn, const char *scriptIn, + const char *regionIn, ULocale.Minimize fieldToFavor, + UErrorCode &errorCode) const { + LSR result = maximize(languageIn, scriptIn, regionIn); + + // We could try just a series of checks, like: + // LSR result2 = addLikelySubtags(languageIn, "", ""); + // if result.equals(result2) return result2; + // However, we can optimize 2 of the cases: + // (languageIn, "", "") + // (languageIn, "", regionIn) + + // value00 = lookup(result.language, "", "") + BytesTrie iter = new BytesTrie(trie); + int value = trieNext(iter, result.language, 0); + U_ASSERT(value >= 0); + if (value == 0) { + value = trieNext(iter, "", 0); + U_ASSERT(value >= 0); + if (value == 0) { + value = trieNext(iter, "", 0); + } + } + U_ASSERT(value > 0); + LSR value00 = lsrs[value]; + boolean favorRegionOk = false; + if (result.script.equals(value00.script)) { //script is default + if (result.region.equals(value00.region)) { + return new LSR(result.language, "", "", LSR.DONT_CARE_FLAGS); + } else if (fieldToFavor == ULocale.Minimize.FAVOR_REGION) { + return new LSR(result.language, "", result.region, LSR.DONT_CARE_FLAGS); + } else { + favorRegionOk = true; + } + } + + // The last case is not as easy to optimize. + // Maybe do later, but for now use the straightforward code. + LSR result2 = maximize(languageIn, scriptIn, ""); + if (result2.equals(result)) { + return new LSR(result.language, result.script, "", LSR.DONT_CARE_FLAGS); + } else if (favorRegionOk) { + return new LSR(result.language, "", result.region, LSR.DONT_CARE_FLAGS); + } + return result; +} +#endif + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/loclikelysubtags.h b/deps/icu-small/source/common/loclikelysubtags.h index 14a01a5eac7eba..926ab1634f3c5a 100644 --- a/deps/icu-small/source/common/loclikelysubtags.h +++ b/deps/icu-small/source/common/loclikelysubtags.h @@ -1,121 +1,121 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// loclikelysubtags.h -// created: 2019may08 Markus W. Scherer - -#ifndef __LOCLIKELYSUBTAGS_H__ -#define __LOCLIKELYSUBTAGS_H__ - -#include -#include "unicode/utypes.h" -#include "unicode/bytestrie.h" -#include "unicode/locid.h" -#include "unicode/uobject.h" -#include "unicode/ures.h" -#include "charstrmap.h" -#include "lsr.h" - -U_NAMESPACE_BEGIN - -struct XLikelySubtagsData; - -struct LocaleDistanceData { - LocaleDistanceData() = default; - LocaleDistanceData(LocaleDistanceData &&data); - ~LocaleDistanceData(); - - const uint8_t *distanceTrieBytes = nullptr; - const uint8_t *regionToPartitions = nullptr; - const char **partitions = nullptr; - const LSR *paradigms = nullptr; - int32_t paradigmsLength = 0; - const int32_t *distances = nullptr; - -private: - LocaleDistanceData &operator=(const LocaleDistanceData &) = delete; -}; - -// TODO(ICU-20777): Rename to just LikelySubtags. -class XLikelySubtags final : public UMemory { -public: - ~XLikelySubtags(); - - static constexpr int32_t SKIP_SCRIPT = 1; - - // VisibleForTesting - static const XLikelySubtags *getSingleton(UErrorCode &errorCode); - - // VisibleForTesting - LSR makeMaximizedLsrFrom(const Locale &locale, UErrorCode &errorCode) const; - - /** - * Tests whether lsr is "more likely" than other. - * For example, fr-Latn-FR is more likely than fr-Latn-CH because - * FR is the default region for fr-Latn. - * - * The likelyInfo caches lookup information between calls. - * The return value is an updated likelyInfo value, - * with bit 0 set if lsr is "more likely". - * The initial value of likelyInfo must be negative. - */ - int32_t compareLikely(const LSR &lsr, const LSR &other, int32_t likelyInfo) const; - - // TODO(ICU-20777): Switch Locale/uloc_ likely-subtags API from the old code - // in loclikely.cpp to this new code, including activating this - // minimizeSubtags() function. The LocaleMatcher does not minimize. -#if 0 - LSR minimizeSubtags(const char *languageIn, const char *scriptIn, const char *regionIn, - ULocale.Minimize fieldToFavor, UErrorCode &errorCode) const; -#endif - - // visible for LocaleDistance - const LocaleDistanceData &getDistanceData() const { return distanceData; } - -private: - XLikelySubtags(XLikelySubtagsData &data); - XLikelySubtags(const XLikelySubtags &other) = delete; - XLikelySubtags &operator=(const XLikelySubtags &other) = delete; - - static void initLikelySubtags(UErrorCode &errorCode); - - LSR makeMaximizedLsr(const char *language, const char *script, const char *region, - const char *variant, UErrorCode &errorCode) const; - - /** - * Raw access to addLikelySubtags. Input must be in canonical format, eg "en", not "eng" or "EN". - */ - LSR maximize(const char *language, const char *script, const char *region) const; - - int32_t getLikelyIndex(const char *language, const char *script) const; - - static int32_t trieNext(BytesTrie &iter, const char *s, int32_t i); - - UResourceBundle *langInfoBundle; - // We could store the strings by value, except that if there were few enough strings, - // moving the contents could copy it to a different array, - // invalidating the pointers stored in the maps. - CharString *strings; - CharStringMap languageAliases; - CharStringMap regionAliases; - - // The trie maps each lang+script+region (encoded in ASCII) to an index into lsrs. - // There is also a trie value for each intermediate lang and lang+script. - // '*' is used instead of "und", "Zzzz"/"" and "ZZ"/"". - BytesTrie trie; - uint64_t trieUndState; - uint64_t trieUndZzzzState; - int32_t defaultLsrIndex; - uint64_t trieFirstLetterStates[26]; - const LSR *lsrs; -#if U_DEBUG - int32_t lsrsLength; -#endif - - // distance/matcher data: see comment in XLikelySubtagsData::load() - LocaleDistanceData distanceData; -}; - -U_NAMESPACE_END - -#endif // __LOCLIKELYSUBTAGS_H__ +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// loclikelysubtags.h +// created: 2019may08 Markus W. Scherer + +#ifndef __LOCLIKELYSUBTAGS_H__ +#define __LOCLIKELYSUBTAGS_H__ + +#include +#include "unicode/utypes.h" +#include "unicode/bytestrie.h" +#include "unicode/locid.h" +#include "unicode/uobject.h" +#include "unicode/ures.h" +#include "charstrmap.h" +#include "lsr.h" + +U_NAMESPACE_BEGIN + +struct XLikelySubtagsData; + +struct LocaleDistanceData { + LocaleDistanceData() = default; + LocaleDistanceData(LocaleDistanceData &&data); + ~LocaleDistanceData(); + + const uint8_t *distanceTrieBytes = nullptr; + const uint8_t *regionToPartitions = nullptr; + const char **partitions = nullptr; + const LSR *paradigms = nullptr; + int32_t paradigmsLength = 0; + const int32_t *distances = nullptr; + +private: + LocaleDistanceData &operator=(const LocaleDistanceData &) = delete; +}; + +// TODO(ICU-20777): Rename to just LikelySubtags. +class XLikelySubtags final : public UMemory { +public: + ~XLikelySubtags(); + + static constexpr int32_t SKIP_SCRIPT = 1; + + // VisibleForTesting + static const XLikelySubtags *getSingleton(UErrorCode &errorCode); + + // VisibleForTesting + LSR makeMaximizedLsrFrom(const Locale &locale, UErrorCode &errorCode) const; + + /** + * Tests whether lsr is "more likely" than other. + * For example, fr-Latn-FR is more likely than fr-Latn-CH because + * FR is the default region for fr-Latn. + * + * The likelyInfo caches lookup information between calls. + * The return value is an updated likelyInfo value, + * with bit 0 set if lsr is "more likely". + * The initial value of likelyInfo must be negative. + */ + int32_t compareLikely(const LSR &lsr, const LSR &other, int32_t likelyInfo) const; + + // TODO(ICU-20777): Switch Locale/uloc_ likely-subtags API from the old code + // in loclikely.cpp to this new code, including activating this + // minimizeSubtags() function. The LocaleMatcher does not minimize. +#if 0 + LSR minimizeSubtags(const char *languageIn, const char *scriptIn, const char *regionIn, + ULocale.Minimize fieldToFavor, UErrorCode &errorCode) const; +#endif + + // visible for LocaleDistance + const LocaleDistanceData &getDistanceData() const { return distanceData; } + +private: + XLikelySubtags(XLikelySubtagsData &data); + XLikelySubtags(const XLikelySubtags &other) = delete; + XLikelySubtags &operator=(const XLikelySubtags &other) = delete; + + static void initLikelySubtags(UErrorCode &errorCode); + + LSR makeMaximizedLsr(const char *language, const char *script, const char *region, + const char *variant, UErrorCode &errorCode) const; + + /** + * Raw access to addLikelySubtags. Input must be in canonical format, eg "en", not "eng" or "EN". + */ + LSR maximize(const char *language, const char *script, const char *region) const; + + int32_t getLikelyIndex(const char *language, const char *script) const; + + static int32_t trieNext(BytesTrie &iter, const char *s, int32_t i); + + UResourceBundle *langInfoBundle; + // We could store the strings by value, except that if there were few enough strings, + // moving the contents could copy it to a different array, + // invalidating the pointers stored in the maps. + CharString *strings; + CharStringMap languageAliases; + CharStringMap regionAliases; + + // The trie maps each lang+script+region (encoded in ASCII) to an index into lsrs. + // There is also a trie value for each intermediate lang and lang+script. + // '*' is used instead of "und", "Zzzz"/"" and "ZZ"/"". + BytesTrie trie; + uint64_t trieUndState; + uint64_t trieUndZzzzState; + int32_t defaultLsrIndex; + uint64_t trieFirstLetterStates[26]; + const LSR *lsrs; +#if U_DEBUG + int32_t lsrsLength; +#endif + + // distance/matcher data: see comment in XLikelySubtagsData::load() + LocaleDistanceData distanceData; +}; + +U_NAMESPACE_END + +#endif // __LOCLIKELYSUBTAGS_H__ diff --git a/deps/icu-small/source/common/locmap.cpp b/deps/icu-small/source/common/locmap.cpp index 78cfd1ca86b62d..4ba063607073dd 100644 --- a/deps/icu-small/source/common/locmap.cpp +++ b/deps/icu-small/source/common/locmap.cpp @@ -1,1315 +1,1315 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 1996-2016, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - * - * Provides functionality for mapping between - * LCID and Posix IDs or ICU locale to codepage - * - * Note: All classes and code in this file are - * intended for internal use only. - * - * Methods of interest: - * unsigned long convertToLCID(const char*); - * const char* convertToPosix(unsigned long); - * - * Kathleen Wilson, 4/30/96 - * - * Date Name Description - * 3/11/97 aliu Fixed off-by-one bug in assignment operator. Added - * setId() method and safety check against - * MAX_ID_LENGTH. - * 04/23/99 stephen Added C wrapper for convertToPosix. - * 09/18/00 george Removed the memory leaks. - * 08/23/01 george Convert to C - */ - -#include "locmap.h" -#include "bytesinkutil.h" -#include "charstr.h" -#include "cstring.h" -#include "cmemory.h" -#include "ulocimp.h" -#include "unicode/uloc.h" - -#if U_PLATFORM_HAS_WIN32_API && UCONFIG_USE_WINDOWS_LCID_MAPPING_API -#include -#include // LCIDToLocaleName and LocaleNameToLCID -#endif - -/* - * Note: - * The mapping from Win32 locale ID numbers to POSIX locale strings should - * be the faster one. - * - * Windows LCIDs are defined at https://msdn.microsoft.com/en-us/library/cc233965.aspx - * [MS-LCID] Windows Language Code Identifier (LCID) Reference - */ - -/* -//////////////////////////////////////////////// -// -// Internal Classes for LCID <--> POSIX Mapping -// -///////////////////////////////////////////////// -*/ - -typedef struct ILcidPosixElement -{ - const uint32_t hostID; - const char * const posixID; -} ILcidPosixElement; - -typedef struct ILcidPosixMap -{ - const uint32_t numRegions; - const struct ILcidPosixElement* const regionMaps; -} ILcidPosixMap; - - -/* -///////////////////////////////////////////////// -// -// Easy macros to make the LCID <--> POSIX Mapping -// -///////////////////////////////////////////////// -*/ - -/** - * The standard one language/one country mapping for LCID. - * The first element must be the language, and the following - * elements are the language with the country. - * @param hostID LCID in host format such as 0x044d - * @param languageID posix ID of just the language such as 'de' - * @param posixID posix ID of the language_TERRITORY such as 'de_CH' - */ -#define ILCID_POSIX_ELEMENT_ARRAY(hostID, languageID, posixID) \ -static const ILcidPosixElement locmap_ ## languageID [] = { \ - {LANGUAGE_LCID(hostID), #languageID}, /* parent locale */ \ - {hostID, #posixID}, \ -}; - -/** - * Define a subtable by ID - * @param id the POSIX ID, either a language or language_TERRITORY - */ -#define ILCID_POSIX_SUBTABLE(id) \ -static const ILcidPosixElement locmap_ ## id [] = - - -/** - * Create the map for the posixID. This macro supposes that the language string - * name is the same as the global variable name, and that the first element - * in the ILcidPosixElement is just the language. - * @param _posixID the full POSIX ID for this entry. - */ -#define ILCID_POSIX_MAP(_posixID) \ - {UPRV_LENGTHOF(locmap_ ## _posixID), locmap_ ## _posixID} - -/* -//////////////////////////////////////////// -// -// Create the table of LCID to POSIX Mapping -// None of it should be dynamically created. -// -// Keep static locale variables inside the function so that -// it can be created properly during static init. -// -// Note: This table should be updated periodically. Check the [MS-LCID] Windows Language Code Identifier -// (LCID) Reference defined at https://msdn.microsoft.com/en-us/library/cc233965.aspx -// -// Microsoft is moving away from LCID in favor of locale name as of Vista. This table needs to be -// maintained for support of older Windows version. -// Update: Windows 7 (091130) -// -// Note: Microsoft assign a different LCID if a locale has a sorting variant. POSIX IDs below may contain -// @collation=XXX, but no other keywords are allowed (at least for now). When uprv_convertToLCID() is -// called from uloc_getLCID(), keywords other than collation are already removed. If we really need -// to support other keywords in this mapping data, we must update the implementation. -//////////////////////////////////////////// -*/ - -// TODO: For Windows ideally this table would be a list of exceptions rather than a complete list as -// LocaleNameToLCID and LCIDToLocaleName provide 90% of these. - -ILCID_POSIX_ELEMENT_ARRAY(0x0436, af, af_ZA) - -ILCID_POSIX_SUBTABLE(ar) { - {0x01, "ar"}, - {0x3801, "ar_AE"}, - {0x3c01, "ar_BH"}, - {0x1401, "ar_DZ"}, - {0x0c01, "ar_EG"}, - {0x0801, "ar_IQ"}, - {0x2c01, "ar_JO"}, - {0x3401, "ar_KW"}, - {0x3001, "ar_LB"}, - {0x1001, "ar_LY"}, - {0x1801, "ar_MA"}, - {0x1801, "ar_MO"}, - {0x2001, "ar_OM"}, - {0x4001, "ar_QA"}, - {0x0401, "ar_SA"}, - {0x2801, "ar_SY"}, - {0x1c01, "ar_TN"}, - {0x2401, "ar_YE"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x044d, as, as_IN) -ILCID_POSIX_ELEMENT_ARRAY(0x045e, am, am_ET) -ILCID_POSIX_ELEMENT_ARRAY(0x047a, arn,arn_CL) - -ILCID_POSIX_SUBTABLE(az) { - {0x2c, "az"}, - {0x082c, "az_Cyrl_AZ"}, /* Cyrillic based */ - {0x742c, "az_Cyrl"}, /* Cyrillic based */ - {0x042c, "az_Latn_AZ"}, /* Latin based */ - {0x782c, "az_Latn"}, /* Latin based */ - {0x042c, "az_AZ"} /* Latin based */ -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x046d, ba, ba_RU) -ILCID_POSIX_ELEMENT_ARRAY(0x0423, be, be_BY) - -/*ILCID_POSIX_SUBTABLE(ber) { - {0x5f, "ber"}, - {0x045f, "ber_Arab_DZ"}, - {0x045f, "ber_Arab"}, - {0x085f, "ber_Latn_DZ"}, - {0x085f, "ber_Latn"} -};*/ - -ILCID_POSIX_ELEMENT_ARRAY(0x0402, bg, bg_BG) - -ILCID_POSIX_SUBTABLE(bin) { - {0x66, "bin"}, - {0x0466, "bin_NG"} -}; - -ILCID_POSIX_SUBTABLE(bn) { - {0x45, "bn"}, - {0x0845, "bn_BD"}, - {0x0445, "bn_IN"} -}; - -ILCID_POSIX_SUBTABLE(bo) { - {0x51, "bo"}, - {0x0851, "bo_BT"}, - {0x0451, "bo_CN"}, - {0x0c51, "dz_BT"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x047e, br, br_FR) - -ILCID_POSIX_SUBTABLE(ca) { - {0x03, "ca"}, - {0x0403, "ca_ES"}, - {0x0803, "ca_ES_VALENCIA"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0483, co, co_FR) - -ILCID_POSIX_SUBTABLE(chr) { - {0x05c, "chr"}, - {0x7c5c, "chr_Cher"}, - {0x045c, "chr_Cher_US"}, - {0x045c, "chr_US"} -}; - -// ICU has chosen different names for these. -ILCID_POSIX_SUBTABLE(ckb) { - {0x92, "ckb"}, - {0x7c92, "ckb_Arab"}, - {0x0492, "ckb_Arab_IQ"} -}; - -/* Declared as cs_CZ to get around compiler errors on z/OS, which defines cs as a function */ -ILCID_POSIX_ELEMENT_ARRAY(0x0405, cs, cs_CZ) - -ILCID_POSIX_ELEMENT_ARRAY(0x0452, cy, cy_GB) -ILCID_POSIX_ELEMENT_ARRAY(0x0406, da, da_DK) - -// Windows doesn't know POSIX or BCP47 Unicode phonebook sort names -ILCID_POSIX_SUBTABLE(de) { - {0x07, "de"}, - {0x0c07, "de_AT"}, - {0x0807, "de_CH"}, - {0x0407, "de_DE"}, - {0x1407, "de_LI"}, - {0x1007, "de_LU"}, - {0x10407,"de_DE@collation=phonebook"}, /*This is really de_DE_PHONEBOOK on Windows*/ - {0x10407,"de@collation=phonebook"} /*This is really de_DE_PHONEBOOK on Windows*/ -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0465, dv, dv_MV) -ILCID_POSIX_ELEMENT_ARRAY(0x0408, el, el_GR) - -// Windows uses an empty string for 'invariant' -ILCID_POSIX_SUBTABLE(en) { - {0x09, "en"}, - {0x0c09, "en_AU"}, - {0x2809, "en_BZ"}, - {0x1009, "en_CA"}, - {0x0809, "en_GB"}, - {0x3c09, "en_HK"}, - {0x3809, "en_ID"}, - {0x1809, "en_IE"}, - {0x4009, "en_IN"}, - {0x2009, "en_JM"}, - {0x4409, "en_MY"}, - {0x1409, "en_NZ"}, - {0x3409, "en_PH"}, - {0x4809, "en_SG"}, - {0x2C09, "en_TT"}, - {0x0409, "en_US"}, - {0x007f, "en_US_POSIX"}, /* duplicate for round-tripping */ - {0x2409, "en_029"}, - {0x1c09, "en_ZA"}, - {0x3009, "en_ZW"}, - {0x2409, "en_VI"}, /* Virgin Islands AKA Caribbean Islands (en_CB). On Windows8+ This is 0x1000 or dynamically assigned */ - {0x0409, "en_AS"}, /* Alias for en_US. Leave last. On Windows8+ This is 0x1000 or dynamically assigned */ - {0x0409, "en_GU"}, /* Alias for en_US. Leave last. On Windows8+ This is 0x1000 or dynamically assigned */ - {0x0409, "en_MH"}, /* Alias for en_US. Leave last. On Windows8+ This is 0x1000 or dynamically assigned */ - {0x0409, "en_MP"}, /* Alias for en_US. Leave last. On Windows8+ This is 0x1000 or dynamically assigned */ - {0x0409, "en_UM"} /* Alias for en_US. Leave last. On Windows8+ This is 0x1000 or dynamically assigned */ -}; - -ILCID_POSIX_SUBTABLE(en_US_POSIX) { - {0x007f, "en_US_POSIX"} /* duplicate for roundtripping */ -}; - -// Windows doesn't know POSIX or BCP47 Unicode traditional sort names -ILCID_POSIX_SUBTABLE(es) { - {0x0a, "es"}, - {0x2c0a, "es_AR"}, - {0x400a, "es_BO"}, - {0x340a, "es_CL"}, - {0x240a, "es_CO"}, - {0x140a, "es_CR"}, - {0x5c0a, "es_CU"}, - {0x1c0a, "es_DO"}, - {0x300a, "es_EC"}, - {0x0c0a, "es_ES"}, /*Modern sort.*/ - {0x100a, "es_GT"}, - {0x480a, "es_HN"}, - {0x080a, "es_MX"}, - {0x4c0a, "es_NI"}, - {0x180a, "es_PA"}, - {0x280a, "es_PE"}, - {0x500a, "es_PR"}, - {0x3c0a, "es_PY"}, - {0x440a, "es_SV"}, - {0x540a, "es_US"}, - {0x380a, "es_UY"}, - {0x200a, "es_VE"}, - {0x580a, "es_419"}, - {0x040a, "es_ES@collation=traditional"}, - {0x040a, "es@collation=traditional"} // Windows will treat this as es-ES@collation=traditional -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0425, et, et_EE) -ILCID_POSIX_ELEMENT_ARRAY(0x042d, eu, eu_ES) - -/* ISO-639 doesn't distinguish between Persian and Dari.*/ -ILCID_POSIX_SUBTABLE(fa) { - {0x29, "fa"}, - {0x0429, "fa_IR"}, /* Persian/Farsi (Iran) */ - {0x048c, "fa_AF"} /* Persian/Dari (Afghanistan) */ -}; - - -/* duplicate for roundtripping */ -ILCID_POSIX_SUBTABLE(fa_AF) { - {0x8c, "fa_AF"}, /* Persian/Dari (Afghanistan) */ - {0x048c, "fa_AF"} /* Persian/Dari (Afghanistan) */ -}; - -ILCID_POSIX_SUBTABLE(ff) { - {0x67, "ff"}, - {0x7c67, "ff_Latn"}, - {0x0867, "ff_Latn_SN"}, - {0x0467, "ff_NG"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x040b, fi, fi_FI) -ILCID_POSIX_ELEMENT_ARRAY(0x0464, fil,fil_PH) -ILCID_POSIX_ELEMENT_ARRAY(0x0438, fo, fo_FO) - -ILCID_POSIX_SUBTABLE(fr) { - {0x0c, "fr"}, - {0x080c, "fr_BE"}, - {0x0c0c, "fr_CA"}, - {0x240c, "fr_CD"}, - {0x240c, "fr_CG"}, - {0x100c, "fr_CH"}, - {0x300c, "fr_CI"}, - {0x2c0c, "fr_CM"}, - {0x040c, "fr_FR"}, - {0x3c0c, "fr_HT"}, - {0x140c, "fr_LU"}, - {0x380c, "fr_MA"}, - {0x180c, "fr_MC"}, - {0x340c, "fr_ML"}, - {0x200c, "fr_RE"}, - {0x280c, "fr_SN"}, - {0xe40c, "fr_015"}, - {0x1c0c, "fr_029"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0467, fuv, fuv_NG) - -ILCID_POSIX_ELEMENT_ARRAY(0x0462, fy, fy_NL) - -ILCID_POSIX_SUBTABLE(ga) { /* Gaelic (Ireland) */ - {0x3c, "ga"}, - {0x083c, "ga_IE"}, - {0x043c, "gd_GB"} -}; - -ILCID_POSIX_SUBTABLE(gd) { /* Gaelic (Scotland) */ - {0x91, "gd"}, - {0x0491, "gd_GB"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0456, gl, gl_ES) -ILCID_POSIX_ELEMENT_ARRAY(0x0447, gu, gu_IN) -ILCID_POSIX_ELEMENT_ARRAY(0x0474, gn, gn_PY) -ILCID_POSIX_ELEMENT_ARRAY(0x0484, gsw,gsw_FR) - -ILCID_POSIX_SUBTABLE(ha) { - {0x68, "ha"}, - {0x7c68, "ha_Latn"}, - {0x0468, "ha_Latn_NG"}, -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0475, haw,haw_US) -ILCID_POSIX_ELEMENT_ARRAY(0x040d, he, he_IL) -ILCID_POSIX_ELEMENT_ARRAY(0x0439, hi, hi_IN) - -/* This LCID is really four different locales.*/ -ILCID_POSIX_SUBTABLE(hr) { - {0x1a, "hr"}, - {0x141a, "bs_Latn_BA"}, /* Bosnian, Bosnia and Herzegovina */ - {0x681a, "bs_Latn"}, /* Bosnian, Bosnia and Herzegovina */ - {0x141a, "bs_BA"}, /* Bosnian, Bosnia and Herzegovina */ - {0x781a, "bs"}, /* Bosnian */ - {0x201a, "bs_Cyrl_BA"}, /* Bosnian, Bosnia and Herzegovina */ - {0x641a, "bs_Cyrl"}, /* Bosnian, Bosnia and Herzegovina */ - {0x101a, "hr_BA"}, /* Croatian in Bosnia */ - {0x041a, "hr_HR"}, /* Croatian*/ - {0x2c1a, "sr_Latn_ME"}, - {0x241a, "sr_Latn_RS"}, - {0x181a, "sr_Latn_BA"}, /* Serbo-Croatian in Bosnia */ - {0x081a, "sr_Latn_CS"}, /* Serbo-Croatian*/ - {0x701a, "sr_Latn"}, /* It's 0x1a or 0x081a, pick one to make the test program happy. */ - {0x1c1a, "sr_Cyrl_BA"}, /* Serbo-Croatian in Bosnia */ - {0x0c1a, "sr_Cyrl_CS"}, /* Serbian*/ - {0x301a, "sr_Cyrl_ME"}, - {0x281a, "sr_Cyrl_RS"}, - {0x6c1a, "sr_Cyrl"}, /* It's 0x1a or 0x0c1a, pick one to make the test program happy. */ - {0x7c1a, "sr"} /* In CLDR sr is sr_Cyrl. */ -}; - -ILCID_POSIX_SUBTABLE(hsb) { - {0x2E, "hsb"}, - {0x042E, "hsb_DE"}, - {0x082E, "dsb_DE"}, - {0x7C2E, "dsb"}, -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x040e, hu, hu_HU) -ILCID_POSIX_ELEMENT_ARRAY(0x042b, hy, hy_AM) - -ILCID_POSIX_SUBTABLE(ibb) { - {0x69, "ibb"}, - {0x0469, "ibb_NG"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0421, id, id_ID) -ILCID_POSIX_ELEMENT_ARRAY(0x0470, ig, ig_NG) -ILCID_POSIX_ELEMENT_ARRAY(0x0478, ii, ii_CN) -ILCID_POSIX_ELEMENT_ARRAY(0x040f, is, is_IS) - -ILCID_POSIX_SUBTABLE(it) { - {0x10, "it"}, - {0x0810, "it_CH"}, - {0x0410, "it_IT"} -}; - -ILCID_POSIX_SUBTABLE(iu) { - {0x5d, "iu"}, - {0x045d, "iu_Cans_CA"}, - {0x785d, "iu_Cans"}, - {0x085d, "iu_Latn_CA"}, - {0x7c5d, "iu_Latn"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x040d, iw, iw_IL) /*Left in for compatibility*/ -ILCID_POSIX_ELEMENT_ARRAY(0x0411, ja, ja_JP) -ILCID_POSIX_ELEMENT_ARRAY(0x0437, ka, ka_GE) -ILCID_POSIX_ELEMENT_ARRAY(0x043f, kk, kk_KZ) -ILCID_POSIX_ELEMENT_ARRAY(0x046f, kl, kl_GL) -ILCID_POSIX_ELEMENT_ARRAY(0x0453, km, km_KH) -ILCID_POSIX_ELEMENT_ARRAY(0x044b, kn, kn_IN) - -ILCID_POSIX_SUBTABLE(ko) { - {0x12, "ko"}, - {0x0812, "ko_KP"}, - {0x0412, "ko_KR"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0457, kok, kok_IN) -ILCID_POSIX_ELEMENT_ARRAY(0x0471, kr, kr_NG) - -ILCID_POSIX_SUBTABLE(ks) { /* We could add PK and CN too */ - {0x60, "ks"}, - {0x0460, "ks_Arab_IN"}, - {0x0860, "ks_Deva_IN"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0440, ky, ky_KG) /* Kyrgyz is spoken in Kyrgyzstan */ - -ILCID_POSIX_SUBTABLE(la) { - {0x76, "la"}, - {0x0476, "la_001"}, - {0x0476, "la_IT"} /*Left in for compatibility*/ -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x046e, lb, lb_LU) -ILCID_POSIX_ELEMENT_ARRAY(0x0454, lo, lo_LA) -ILCID_POSIX_ELEMENT_ARRAY(0x0427, lt, lt_LT) -ILCID_POSIX_ELEMENT_ARRAY(0x0426, lv, lv_LV) -ILCID_POSIX_ELEMENT_ARRAY(0x0481, mi, mi_NZ) -ILCID_POSIX_ELEMENT_ARRAY(0x042f, mk, mk_MK) -ILCID_POSIX_ELEMENT_ARRAY(0x044c, ml, ml_IN) - -ILCID_POSIX_SUBTABLE(mn) { - {0x50, "mn"}, - {0x0450, "mn_MN"}, - {0x7c50, "mn_Mong"}, - {0x0850, "mn_Mong_CN"}, - {0x0850, "mn_CN"}, - {0x7850, "mn_Cyrl"}, - {0x0c50, "mn_Mong_MN"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0458, mni,mni_IN) -ILCID_POSIX_ELEMENT_ARRAY(0x047c, moh,moh_CA) -ILCID_POSIX_ELEMENT_ARRAY(0x044e, mr, mr_IN) - -ILCID_POSIX_SUBTABLE(ms) { - {0x3e, "ms"}, - {0x083e, "ms_BN"}, /* Brunei Darussalam*/ - {0x043e, "ms_MY"} /* Malaysia*/ -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x043a, mt, mt_MT) -ILCID_POSIX_ELEMENT_ARRAY(0x0455, my, my_MM) - -ILCID_POSIX_SUBTABLE(ne) { - {0x61, "ne"}, - {0x0861, "ne_IN"}, /* India*/ - {0x0461, "ne_NP"} /* Nepal*/ -}; - -ILCID_POSIX_SUBTABLE(nl) { - {0x13, "nl"}, - {0x0813, "nl_BE"}, - {0x0413, "nl_NL"} -}; - -/* The "no" locale split into nb and nn. By default in ICU, "no" is nb.*/ -// TODO: Not all of these are needed on Windows, but I don't know how ICU treats preferred ones here. -ILCID_POSIX_SUBTABLE(no) { - {0x14, "no"}, /* really nb_NO - actually Windows differentiates between neutral (no region) and specific (with region) */ - {0x7c14, "nb"}, /* really nb */ - {0x0414, "nb_NO"}, /* really nb_NO. Keep first in the 414 list. */ - {0x0414, "no_NO"}, /* really nb_NO */ - {0x0814, "nn_NO"}, /* really nn_NO. Keep first in the 814 list. */ - {0x7814, "nn"}, /* It's 0x14 or 0x814, pick one to make the test program happy. */ - {0x0814, "no_NO_NY"}/* really nn_NO */ -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x046c, nso,nso_ZA) /* TODO: Verify the ISO-639 code */ -ILCID_POSIX_ELEMENT_ARRAY(0x0482, oc, oc_FR) - -ILCID_POSIX_SUBTABLE(om) { /* TODO: Verify the country */ - {0x72, "om"}, - {0x0472, "om_ET"}, - {0x0472, "gaz_ET"} -}; - -/* Declared as or_IN to get around compiler errors*/ -ILCID_POSIX_SUBTABLE(or_IN) { - {0x48, "or"}, - {0x0448, "or_IN"}, -}; - -ILCID_POSIX_SUBTABLE(pa) { - {0x46, "pa"}, - {0x0446, "pa_IN"}, - {0x0846, "pa_Arab_PK"}, - {0x0846, "pa_PK"} -}; - -ILCID_POSIX_SUBTABLE(pap) { - {0x79, "pap"}, - {0x0479, "pap_029"}, - {0x0479, "pap_AN"} /*Left in for compatibility*/ -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0415, pl, pl_PL) -ILCID_POSIX_ELEMENT_ARRAY(0x0463, ps, ps_AF) - -ILCID_POSIX_SUBTABLE(pt) { - {0x16, "pt"}, - {0x0416, "pt_BR"}, - {0x0816, "pt_PT"} -}; - -ILCID_POSIX_SUBTABLE(qu) { - {0x6b, "qu"}, - {0x046b, "qu_BO"}, - {0x086b, "qu_EC"}, - {0x0C6b, "qu_PE"}, - {0x046b, "quz_BO"}, - {0x086b, "quz_EC"}, - {0x0C6b, "quz_PE"} -}; - -ILCID_POSIX_SUBTABLE(quc) { - {0x93, "quc"}, - {0x0493, "quc_CO"}, - /* - "quc_Latn_GT" is an exceptional case. Language ID of "quc" - is 0x93, but LCID of "quc_Latn_GT" is 0x486, which should be - under the group of "qut". "qut" is a retired ISO 639-3 language - code for West Central Quiche, and merged to "quc". - It looks Windows previously reserved "qut" for K'iche', but, - decided to use "quc" when adding a locale for K'iche' (Guatemala). - - This data structure used here assumes language ID bits in - LCID is unique for alphabetic language code. But this is not true - for "quc_Latn_GT". If we don't have the data below, LCID look up - by alphabetic locale ID (POSIX) will fail. The same entry is found - under "qut" below, which is required for reverse look up. - */ - {0x0486, "quc_Latn_GT"} -}; - -ILCID_POSIX_SUBTABLE(qut) { - {0x86, "qut"}, - {0x0486, "qut_GT"}, - /* - See the note in "quc" above. - */ - {0x0486, "quc_Latn_GT"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0417, rm, rm_CH) - -ILCID_POSIX_SUBTABLE(ro) { - {0x18, "ro"}, - {0x0418, "ro_RO"}, - {0x0818, "ro_MD"} -}; - -// TODO: This is almost certainly 'wrong'. 0 in Windows is a synonym for LOCALE_USER_DEFAULT. -// More likely this is a similar concept to the Windows 0x7f Invariant locale "" -// (Except that it's not invariant in ICU) -ILCID_POSIX_SUBTABLE(root) { - {0x00, "root"} -}; - -ILCID_POSIX_SUBTABLE(ru) { - {0x19, "ru"}, - {0x0419, "ru_RU"}, - {0x0819, "ru_MD"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0487, rw, rw_RW) -ILCID_POSIX_ELEMENT_ARRAY(0x044f, sa, sa_IN) -ILCID_POSIX_ELEMENT_ARRAY(0x0485, sah,sah_RU) - -ILCID_POSIX_SUBTABLE(sd) { - {0x59, "sd"}, - {0x0459, "sd_Deva_IN"}, - {0x0459, "sd_IN"}, - {0x0859, "sd_Arab_PK"}, - {0x0859, "sd_PK"}, - {0x7c59, "sd_Arab"} -}; - -ILCID_POSIX_SUBTABLE(se) { - {0x3b, "se"}, - {0x0c3b, "se_FI"}, - {0x043b, "se_NO"}, - {0x083b, "se_SE"}, - {0x783b, "sma"}, - {0x183b, "sma_NO"}, - {0x1c3b, "sma_SE"}, - {0x7c3b, "smj"}, - {0x703b, "smn"}, - {0x743b, "sms"}, - {0x103b, "smj_NO"}, - {0x143b, "smj_SE"}, - {0x243b, "smn_FI"}, - {0x203b, "sms_FI"}, -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x045b, si, si_LK) -ILCID_POSIX_ELEMENT_ARRAY(0x041b, sk, sk_SK) -ILCID_POSIX_ELEMENT_ARRAY(0x0424, sl, sl_SI) - -ILCID_POSIX_SUBTABLE(so) { - {0x77, "so"}, - {0x0477, "so_SO"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x041c, sq, sq_AL) -ILCID_POSIX_ELEMENT_ARRAY(0x0430, st, st_ZA) - -ILCID_POSIX_SUBTABLE(sv) { - {0x1d, "sv"}, - {0x081d, "sv_FI"}, - {0x041d, "sv_SE"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0441, sw, sw_KE) -ILCID_POSIX_ELEMENT_ARRAY(0x045A, syr, syr_SY) - -ILCID_POSIX_SUBTABLE(ta) { - {0x49, "ta"}, - {0x0449, "ta_IN"}, - {0x0849, "ta_LK"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x044a, te, te_IN) - -/* Cyrillic based by default */ -ILCID_POSIX_SUBTABLE(tg) { - {0x28, "tg"}, - {0x7c28, "tg_Cyrl"}, - {0x0428, "tg_Cyrl_TJ"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x041e, th, th_TH) - -ILCID_POSIX_SUBTABLE(ti) { - {0x73, "ti"}, - {0x0873, "ti_ER"}, - {0x0473, "ti_ET"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0442, tk, tk_TM) - -ILCID_POSIX_SUBTABLE(tn) { - {0x32, "tn"}, - {0x0832, "tn_BW"}, - {0x0432, "tn_ZA"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x041f, tr, tr_TR) -ILCID_POSIX_ELEMENT_ARRAY(0x0431, ts, ts_ZA) -ILCID_POSIX_ELEMENT_ARRAY(0x0444, tt, tt_RU) - -ILCID_POSIX_SUBTABLE(tzm) { - {0x5f, "tzm"}, - {0x7c5f, "tzm_Latn"}, - {0x085f, "tzm_Latn_DZ"}, - {0x105f, "tzm_Tfng_MA"}, - {0x045f, "tzm_Arab_MA"}, - {0x045f, "tmz"} -}; - -ILCID_POSIX_SUBTABLE(ug) { - {0x80, "ug"}, - {0x0480, "ug_CN"}, - {0x0480, "ug_Arab_CN"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0422, uk, uk_UA) - -ILCID_POSIX_SUBTABLE(ur) { - {0x20, "ur"}, - {0x0820, "ur_IN"}, - {0x0420, "ur_PK"} -}; - -ILCID_POSIX_SUBTABLE(uz) { - {0x43, "uz"}, - {0x0843, "uz_Cyrl_UZ"}, /* Cyrillic based */ - {0x7843, "uz_Cyrl"}, /* Cyrillic based */ - {0x0843, "uz_UZ"}, /* Cyrillic based */ - {0x0443, "uz_Latn_UZ"}, /* Latin based */ - {0x7c43, "uz_Latn"} /* Latin based */ -}; - -ILCID_POSIX_SUBTABLE(ve) { /* TODO: Verify the country */ - {0x33, "ve"}, - {0x0433, "ve_ZA"}, - {0x0433, "ven_ZA"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x042a, vi, vi_VN) -ILCID_POSIX_ELEMENT_ARRAY(0x0488, wo, wo_SN) -ILCID_POSIX_ELEMENT_ARRAY(0x0434, xh, xh_ZA) - -ILCID_POSIX_SUBTABLE(yi) { - {0x003d, "yi"}, - {0x043d, "yi_001"} -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x046a, yo, yo_NG) - -// Windows & ICU tend to different names for some of these -// TODO: Windows probably does not need all of these entries, but I don't know how the precedence works. -ILCID_POSIX_SUBTABLE(zh) { - {0x0004, "zh_Hans"}, - {0x7804, "zh"}, - {0x0804, "zh_CN"}, - {0x0804, "zh_Hans_CN"}, - {0x0c04, "zh_Hant_HK"}, - {0x0c04, "zh_HK"}, - {0x1404, "zh_Hant_MO"}, - {0x1404, "zh_MO"}, - {0x1004, "zh_Hans_SG"}, - {0x1004, "zh_SG"}, - {0x0404, "zh_Hant_TW"}, - {0x7c04, "zh_Hant"}, - {0x0404, "zh_TW"}, - {0x30404,"zh_Hant_TW"}, /* Bopomofo order */ - {0x30404,"zh_TW"}, /* Bopomofo order */ - {0x20004,"zh@collation=stroke"}, - {0x20404,"zh_Hant@collation=stroke"}, - {0x20404,"zh_Hant_TW@collation=stroke"}, - {0x20404,"zh_TW@collation=stroke"}, - {0x20804,"zh_Hans@collation=stroke"}, - {0x20804,"zh_Hans_CN@collation=stroke"}, - {0x20804,"zh_CN@collation=stroke"} - // TODO: Alternate collations for other LCIDs are missing, eg: 0x50804 -}; - -ILCID_POSIX_ELEMENT_ARRAY(0x0435, zu, zu_ZA) - -/* This must be static and grouped by LCID. */ -static const ILcidPosixMap gPosixIDmap[] = { - ILCID_POSIX_MAP(af), /* af Afrikaans 0x36 */ - ILCID_POSIX_MAP(am), /* am Amharic 0x5e */ - ILCID_POSIX_MAP(ar), /* ar Arabic 0x01 */ - ILCID_POSIX_MAP(arn), /* arn Araucanian/Mapudungun 0x7a */ - ILCID_POSIX_MAP(as), /* as Assamese 0x4d */ - ILCID_POSIX_MAP(az), /* az Azerbaijani 0x2c */ - ILCID_POSIX_MAP(ba), /* ba Bashkir 0x6d */ - ILCID_POSIX_MAP(be), /* be Belarusian 0x23 */ -/* ILCID_POSIX_MAP(ber), ber Berber/Tamazight 0x5f */ - ILCID_POSIX_MAP(bg), /* bg Bulgarian 0x02 */ - ILCID_POSIX_MAP(bin), /* bin Edo 0x66 */ - ILCID_POSIX_MAP(bn), /* bn Bengali; Bangla 0x45 */ - ILCID_POSIX_MAP(bo), /* bo Tibetan 0x51 */ - ILCID_POSIX_MAP(br), /* br Breton 0x7e */ - ILCID_POSIX_MAP(ca), /* ca Catalan 0x03 */ - ILCID_POSIX_MAP(chr), /* chr Cherokee 0x5c */ - ILCID_POSIX_MAP(ckb), /* ckb Sorani (Central Kurdish) 0x92 */ - ILCID_POSIX_MAP(co), /* co Corsican 0x83 */ - ILCID_POSIX_MAP(cs), /* cs Czech 0x05 */ - ILCID_POSIX_MAP(cy), /* cy Welsh 0x52 */ - ILCID_POSIX_MAP(da), /* da Danish 0x06 */ - ILCID_POSIX_MAP(de), /* de German 0x07 */ - ILCID_POSIX_MAP(dv), /* dv Divehi 0x65 */ - ILCID_POSIX_MAP(el), /* el Greek 0x08 */ - ILCID_POSIX_MAP(en), /* en English 0x09 */ - ILCID_POSIX_MAP(en_US_POSIX), /* invariant 0x7f */ - ILCID_POSIX_MAP(es), /* es Spanish 0x0a */ - ILCID_POSIX_MAP(et), /* et Estonian 0x25 */ - ILCID_POSIX_MAP(eu), /* eu Basque 0x2d */ - ILCID_POSIX_MAP(fa), /* fa Persian/Farsi 0x29 */ - ILCID_POSIX_MAP(fa_AF), /* fa Persian/Dari 0x8c */ - ILCID_POSIX_MAP(ff), /* ff Fula 0x67 */ - ILCID_POSIX_MAP(fi), /* fi Finnish 0x0b */ - ILCID_POSIX_MAP(fil), /* fil Filipino 0x64 */ - ILCID_POSIX_MAP(fo), /* fo Faroese 0x38 */ - ILCID_POSIX_MAP(fr), /* fr French 0x0c */ - ILCID_POSIX_MAP(fuv), /* fuv Fulfulde - Nigeria 0x67 */ - ILCID_POSIX_MAP(fy), /* fy Frisian 0x62 */ - ILCID_POSIX_MAP(ga), /* * Gaelic (Ireland,Scotland) 0x3c */ - ILCID_POSIX_MAP(gd), /* gd Gaelic (United Kingdom) 0x91 */ - ILCID_POSIX_MAP(gl), /* gl Galician 0x56 */ - ILCID_POSIX_MAP(gn), /* gn Guarani 0x74 */ - ILCID_POSIX_MAP(gsw), /* gsw Alemanic/Alsatian/Swiss German 0x84 */ - ILCID_POSIX_MAP(gu), /* gu Gujarati 0x47 */ - ILCID_POSIX_MAP(ha), /* ha Hausa 0x68 */ - ILCID_POSIX_MAP(haw), /* haw Hawaiian 0x75 */ - ILCID_POSIX_MAP(he), /* he Hebrew (formerly iw) 0x0d */ - ILCID_POSIX_MAP(hi), /* hi Hindi 0x39 */ - ILCID_POSIX_MAP(hr), /* * Croatian and others 0x1a */ - ILCID_POSIX_MAP(hsb), /* hsb Upper Sorbian 0x2e */ - ILCID_POSIX_MAP(hu), /* hu Hungarian 0x0e */ - ILCID_POSIX_MAP(hy), /* hy Armenian 0x2b */ - ILCID_POSIX_MAP(ibb), /* ibb Ibibio - Nigeria 0x69 */ - ILCID_POSIX_MAP(id), /* id Indonesian (formerly in) 0x21 */ - ILCID_POSIX_MAP(ig), /* ig Igbo 0x70 */ - ILCID_POSIX_MAP(ii), /* ii Sichuan Yi 0x78 */ - ILCID_POSIX_MAP(is), /* is Icelandic 0x0f */ - ILCID_POSIX_MAP(it), /* it Italian 0x10 */ - ILCID_POSIX_MAP(iu), /* iu Inuktitut 0x5d */ - ILCID_POSIX_MAP(iw), /* iw Hebrew 0x0d */ - ILCID_POSIX_MAP(ja), /* ja Japanese 0x11 */ - ILCID_POSIX_MAP(ka), /* ka Georgian 0x37 */ - ILCID_POSIX_MAP(kk), /* kk Kazakh 0x3f */ - ILCID_POSIX_MAP(kl), /* kl Kalaallisut 0x6f */ - ILCID_POSIX_MAP(km), /* km Khmer 0x53 */ - ILCID_POSIX_MAP(kn), /* kn Kannada 0x4b */ - ILCID_POSIX_MAP(ko), /* ko Korean 0x12 */ - ILCID_POSIX_MAP(kok), /* kok Konkani 0x57 */ - ILCID_POSIX_MAP(kr), /* kr Kanuri 0x71 */ - ILCID_POSIX_MAP(ks), /* ks Kashmiri 0x60 */ - ILCID_POSIX_MAP(ky), /* ky Kyrgyz 0x40 */ - ILCID_POSIX_MAP(lb), /* lb Luxembourgish 0x6e */ - ILCID_POSIX_MAP(la), /* la Latin 0x76 */ - ILCID_POSIX_MAP(lo), /* lo Lao 0x54 */ - ILCID_POSIX_MAP(lt), /* lt Lithuanian 0x27 */ - ILCID_POSIX_MAP(lv), /* lv Latvian, Lettish 0x26 */ - ILCID_POSIX_MAP(mi), /* mi Maori 0x81 */ - ILCID_POSIX_MAP(mk), /* mk Macedonian 0x2f */ - ILCID_POSIX_MAP(ml), /* ml Malayalam 0x4c */ - ILCID_POSIX_MAP(mn), /* mn Mongolian 0x50 */ - ILCID_POSIX_MAP(mni), /* mni Manipuri 0x58 */ - ILCID_POSIX_MAP(moh), /* moh Mohawk 0x7c */ - ILCID_POSIX_MAP(mr), /* mr Marathi 0x4e */ - ILCID_POSIX_MAP(ms), /* ms Malay 0x3e */ - ILCID_POSIX_MAP(mt), /* mt Maltese 0x3a */ - ILCID_POSIX_MAP(my), /* my Burmese 0x55 */ -/* ILCID_POSIX_MAP(nb), // no Norwegian 0x14 */ - ILCID_POSIX_MAP(ne), /* ne Nepali 0x61 */ - ILCID_POSIX_MAP(nl), /* nl Dutch 0x13 */ -/* ILCID_POSIX_MAP(nn), // no Norwegian 0x14 */ - ILCID_POSIX_MAP(no), /* * Norwegian 0x14 */ - ILCID_POSIX_MAP(nso), /* nso Sotho, Northern (Sepedi dialect) 0x6c */ - ILCID_POSIX_MAP(oc), /* oc Occitan 0x82 */ - ILCID_POSIX_MAP(om), /* om Oromo 0x72 */ - ILCID_POSIX_MAP(or_IN), /* or Oriya 0x48 */ - ILCID_POSIX_MAP(pa), /* pa Punjabi 0x46 */ - ILCID_POSIX_MAP(pap), /* pap Papiamentu 0x79 */ - ILCID_POSIX_MAP(pl), /* pl Polish 0x15 */ - ILCID_POSIX_MAP(ps), /* ps Pashto 0x63 */ - ILCID_POSIX_MAP(pt), /* pt Portuguese 0x16 */ - ILCID_POSIX_MAP(qu), /* qu Quechua 0x6B */ - ILCID_POSIX_MAP(quc), /* quc K'iche 0x93 */ - ILCID_POSIX_MAP(qut), /* qut K'iche 0x86 */ - ILCID_POSIX_MAP(rm), /* rm Raeto-Romance/Romansh 0x17 */ - ILCID_POSIX_MAP(ro), /* ro Romanian 0x18 */ - ILCID_POSIX_MAP(root), /* root 0x00 */ - ILCID_POSIX_MAP(ru), /* ru Russian 0x19 */ - ILCID_POSIX_MAP(rw), /* rw Kinyarwanda 0x87 */ - ILCID_POSIX_MAP(sa), /* sa Sanskrit 0x4f */ - ILCID_POSIX_MAP(sah), /* sah Yakut 0x85 */ - ILCID_POSIX_MAP(sd), /* sd Sindhi 0x59 */ - ILCID_POSIX_MAP(se), /* se Sami 0x3b */ -/* ILCID_POSIX_MAP(sh), // sh Serbo-Croatian 0x1a */ - ILCID_POSIX_MAP(si), /* si Sinhalese 0x5b */ - ILCID_POSIX_MAP(sk), /* sk Slovak 0x1b */ - ILCID_POSIX_MAP(sl), /* sl Slovenian 0x24 */ - ILCID_POSIX_MAP(so), /* so Somali 0x77 */ - ILCID_POSIX_MAP(sq), /* sq Albanian 0x1c */ -/* ILCID_POSIX_MAP(sr), // sr Serbian 0x1a */ - ILCID_POSIX_MAP(st), /* st Sutu 0x30 */ - ILCID_POSIX_MAP(sv), /* sv Swedish 0x1d */ - ILCID_POSIX_MAP(sw), /* sw Swahili 0x41 */ - ILCID_POSIX_MAP(syr), /* syr Syriac 0x5A */ - ILCID_POSIX_MAP(ta), /* ta Tamil 0x49 */ - ILCID_POSIX_MAP(te), /* te Telugu 0x4a */ - ILCID_POSIX_MAP(tg), /* tg Tajik 0x28 */ - ILCID_POSIX_MAP(th), /* th Thai 0x1e */ - ILCID_POSIX_MAP(ti), /* ti Tigrigna 0x73 */ - ILCID_POSIX_MAP(tk), /* tk Turkmen 0x42 */ - ILCID_POSIX_MAP(tn), /* tn Tswana 0x32 */ - ILCID_POSIX_MAP(tr), /* tr Turkish 0x1f */ - ILCID_POSIX_MAP(ts), /* ts Tsonga 0x31 */ - ILCID_POSIX_MAP(tt), /* tt Tatar 0x44 */ - ILCID_POSIX_MAP(tzm), /* tzm Tamazight 0x5f */ - ILCID_POSIX_MAP(ug), /* ug Uighur 0x80 */ - ILCID_POSIX_MAP(uk), /* uk Ukrainian 0x22 */ - ILCID_POSIX_MAP(ur), /* ur Urdu 0x20 */ - ILCID_POSIX_MAP(uz), /* uz Uzbek 0x43 */ - ILCID_POSIX_MAP(ve), /* ve Venda 0x33 */ - ILCID_POSIX_MAP(vi), /* vi Vietnamese 0x2a */ - ILCID_POSIX_MAP(wo), /* wo Wolof 0x88 */ - ILCID_POSIX_MAP(xh), /* xh Xhosa 0x34 */ - ILCID_POSIX_MAP(yi), /* yi Yiddish 0x3d */ - ILCID_POSIX_MAP(yo), /* yo Yoruba 0x6a */ - ILCID_POSIX_MAP(zh), /* zh Chinese 0x04 */ - ILCID_POSIX_MAP(zu), /* zu Zulu 0x35 */ -}; - -static const uint32_t gLocaleCount = UPRV_LENGTHOF(gPosixIDmap); - -/** - * Do not call this function. It is called by hostID. - * The function is not private because this struct must stay as a C struct, - * and this is an internal class. - */ -static int32_t -idCmp(const char* id1, const char* id2) -{ - int32_t diffIdx = 0; - while (*id1 == *id2 && *id1 != 0) { - diffIdx++; - id1++; - id2++; - } - return diffIdx; -} - -/** - * Searches for a Windows LCID - * - * @param posixID the Posix style locale id. - * @param status gets set to U_ILLEGAL_ARGUMENT_ERROR when the Posix ID has - * no equivalent Windows LCID. - * @return the LCID - */ -static uint32_t -getHostID(const ILcidPosixMap *this_0, const char* posixID, UErrorCode* status) -{ - int32_t bestIdx = 0; - int32_t bestIdxDiff = 0; - int32_t posixIDlen = (int32_t)uprv_strlen(posixID); - uint32_t idx; - - for (idx = 0; idx < this_0->numRegions; idx++ ) { - int32_t sameChars = idCmp(posixID, this_0->regionMaps[idx].posixID); - if (sameChars > bestIdxDiff && this_0->regionMaps[idx].posixID[sameChars] == 0) { - if (posixIDlen == sameChars) { - /* Exact match */ - return this_0->regionMaps[idx].hostID; - } - bestIdxDiff = sameChars; - bestIdx = idx; - } - } - /* We asked for something unusual, like en_ZZ, and we try to return the number for the same language. */ - /* We also have to make sure that sid and si and similar string subsets don't match. */ - if ((posixID[bestIdxDiff] == '_' || posixID[bestIdxDiff] == '@') - && this_0->regionMaps[bestIdx].posixID[bestIdxDiff] == 0) - { - *status = U_USING_FALLBACK_WARNING; - return this_0->regionMaps[bestIdx].hostID; - } - - /*no match found */ - *status = U_ILLEGAL_ARGUMENT_ERROR; - return this_0->regionMaps->hostID; -} - -static const char* -getPosixID(const ILcidPosixMap *this_0, uint32_t hostID) -{ - uint32_t i; - for (i = 0; i < this_0->numRegions; i++) - { - if (this_0->regionMaps[i].hostID == hostID) - { - return this_0->regionMaps[i].posixID; - } - } - - /* If you get here, then no matching region was found, - so return the language id with the wild card region. */ - return this_0->regionMaps[0].posixID; -} - -/* -////////////////////////////////////// -// -// LCID --> POSIX -// -///////////////////////////////////// -*/ -#if U_PLATFORM_HAS_WIN32_API && UCONFIG_USE_WINDOWS_LCID_MAPPING_API -/* - * Various language tags needs to be changed: - * quz -> qu - * prs -> fa - */ -#define FIX_LANGUAGE_ID_TAG(buffer, len) \ - if (len >= 3) { \ - if (buffer[0] == 'q' && buffer[1] == 'u' && buffer[2] == 'z') {\ - buffer[2] = 0; \ - uprv_strcat(buffer, buffer+3); \ - } else if (buffer[0] == 'p' && buffer[1] == 'r' && buffer[2] == 's') {\ - buffer[0] = 'f'; buffer[1] = 'a'; buffer[2] = 0; \ - uprv_strcat(buffer, buffer+3); \ - } \ - } - -#endif - -U_CAPI int32_t -uprv_convertToPosix(uint32_t hostid, char *posixID, int32_t posixIDCapacity, UErrorCode* status) -{ - uint16_t langID; - uint32_t localeIndex; - UBool bLookup = true; - const char *pPosixID = NULL; - -#if U_PLATFORM_HAS_WIN32_API && UCONFIG_USE_WINDOWS_LCID_MAPPING_API - static_assert(ULOC_FULLNAME_CAPACITY > LOCALE_NAME_MAX_LENGTH, "Windows locale names have smaller length than ICU locale names."); - - char locName[LOCALE_NAME_MAX_LENGTH] = {}; - - // Note: Windows primary lang ID 0x92 in LCID is used for Central Kurdish and - // GetLocaleInfo() maps such LCID to "ku". However, CLDR uses "ku" for - // Northern Kurdish and "ckb" for Central Kurdish. For this reason, we cannot - // use the Windows API to resolve locale ID for this specific case. - if ((hostid & 0x3FF) != 0x92) { - int32_t tmpLen = 0; - char16_t windowsLocaleName[LOCALE_NAME_MAX_LENGTH] = {}; - - // Note: LOCALE_ALLOW_NEUTRAL_NAMES was enabled in Windows7+, prior versions did not handle neutral (no-region) locale names. - tmpLen = LCIDToLocaleName(hostid, (PWSTR)windowsLocaleName, UPRV_LENGTHOF(windowsLocaleName), LOCALE_ALLOW_NEUTRAL_NAMES); - if (tmpLen > 1) { - int32_t i = 0; - // Only need to look up in table if have _, eg for de-de_phoneb type alternate sort. - bLookup = false; - for (i = 0; i < UPRV_LENGTHOF(locName); i++) - { - locName[i] = (char)(windowsLocaleName[i]); - - // Windows locale name may contain sorting variant, such as "es-ES_tradnl". - // In such cases, we need special mapping data found in the hardcoded table - // in this source file. - if (windowsLocaleName[i] == L'_') - { - // Keep the base locale, without variant - // TODO: Should these be mapped from _phoneb to @collation=phonebook, etc.? - locName[i] = '\0'; - tmpLen = i; - bLookup = true; - break; - } - else if (windowsLocaleName[i] == L'-') - { - // Windows names use -, ICU uses _ - locName[i] = '_'; - } - else if (windowsLocaleName[i] == L'\0') - { - // No point in doing more work than necessary - break; - } - } - // TODO: Need to understand this better, why isn't it an alias? - FIX_LANGUAGE_ID_TAG(locName, tmpLen); - pPosixID = locName; - } - } -#endif - - if (bLookup) { - const char *pCandidate = NULL; - langID = LANGUAGE_LCID(hostid); - - for (localeIndex = 0; localeIndex < gLocaleCount; localeIndex++) { - if (langID == gPosixIDmap[localeIndex].regionMaps->hostID) { - pCandidate = getPosixID(&gPosixIDmap[localeIndex], hostid); - break; - } - } - - /* On Windows, when locale name has a variant, we still look up the hardcoded table. - If a match in the hardcoded table is longer than the Windows locale name without - variant, we use the one as the result */ - if (pCandidate && (pPosixID == NULL || uprv_strlen(pCandidate) > uprv_strlen(pPosixID))) { - pPosixID = pCandidate; - } - } - - if (pPosixID) { - int32_t resLen = static_cast(uprv_strlen(pPosixID)); - int32_t copyLen = resLen <= posixIDCapacity ? resLen : posixIDCapacity; - uprv_memcpy(posixID, pPosixID, copyLen); - if (resLen < posixIDCapacity) { - posixID[resLen] = 0; - if (*status == U_STRING_NOT_TERMINATED_WARNING) { - *status = U_ZERO_ERROR; - } - } else if (resLen == posixIDCapacity) { - *status = U_STRING_NOT_TERMINATED_WARNING; - } else { - *status = U_BUFFER_OVERFLOW_ERROR; - } - return resLen; - } - - /* no match found */ - *status = U_ILLEGAL_ARGUMENT_ERROR; - return -1; -} - -/* -////////////////////////////////////// -// -// POSIX --> LCID -// This should only be called from uloc_getLCID. -// The locale ID must be in canonical form. -// -///////////////////////////////////// -*/ -U_CAPI uint32_t -uprv_convertToLCIDPlatform(const char* localeID, UErrorCode* status) -{ - if (U_FAILURE(*status)) { - return 0; - } - - // The purpose of this function is to leverage the Windows platform name->lcid - // conversion functionality when available. -#if U_PLATFORM_HAS_WIN32_API && UCONFIG_USE_WINDOWS_LCID_MAPPING_API - int32_t len; - char baseName[ULOC_FULLNAME_CAPACITY] = {}; - const char * mylocaleID = localeID; - - // Check any for keywords. - if (uprv_strchr(localeID, '@')) - { - icu::CharString collVal; - { - icu::CharStringByteSink sink(&collVal); - ulocimp_getKeywordValue(localeID, "collation", sink, status); - } - if (U_SUCCESS(*status) && !collVal.isEmpty()) - { - // If it contains the keyword collation, return 0 so that the LCID lookup table will be used. - return 0; - } - else - { - // If the locale ID contains keywords other than collation, just use the base name. - len = uloc_getBaseName(localeID, baseName, UPRV_LENGTHOF(baseName) - 1, status); - - if (U_SUCCESS(*status) && len > 0) - { - baseName[len] = 0; - mylocaleID = baseName; - } - } - } - - char asciiBCP47Tag[LOCALE_NAME_MAX_LENGTH] = {}; - // this will change it from de_DE@collation=phonebook to de-DE-u-co-phonebk form - (void)uloc_toLanguageTag(mylocaleID, asciiBCP47Tag, UPRV_LENGTHOF(asciiBCP47Tag), false, status); - - if (U_SUCCESS(*status)) - { - // Need it to be UTF-16, not 8-bit - wchar_t bcp47Tag[LOCALE_NAME_MAX_LENGTH] = {}; - int32_t i; - for (i = 0; i < UPRV_LENGTHOF(bcp47Tag); i++) - { - if (asciiBCP47Tag[i] == '\0') - { - break; - } - else - { - // Copy the character - bcp47Tag[i] = static_cast(asciiBCP47Tag[i]); - } - } - - if (i < (UPRV_LENGTHOF(bcp47Tag) - 1)) - { - // Ensure it's null terminated - bcp47Tag[i] = L'\0'; - LCID lcid = LocaleNameToLCID(bcp47Tag, LOCALE_ALLOW_NEUTRAL_NAMES); - if (lcid > 0) - { - // Found LCID from windows, return that one, unless its completely ambiguous - // LOCALE_USER_DEFAULT and transients are OK because they will round trip - // for this process. - if (lcid != LOCALE_CUSTOM_UNSPECIFIED) - { - return lcid; - } - } - } - } -#else - (void) localeID; // Suppress unused variable warning. -#endif - - // Nothing found, or not implemented. - return 0; -} - -U_CAPI uint32_t -uprv_convertToLCID(const char *langID, const char* posixID, UErrorCode* status) -{ - // This function does the table lookup when native platform name->lcid conversion isn't available, - // or for locales that don't follow patterns the platform expects. - uint32_t low = 0; - uint32_t high = gLocaleCount; - uint32_t mid; - uint32_t oldmid = 0; - int32_t compVal; - - uint32_t value = 0; - uint32_t fallbackValue = (uint32_t)-1; - UErrorCode myStatus; - uint32_t idx; - - /* Check for incomplete id. */ - if (!langID || !posixID || uprv_strlen(langID) < 2 || uprv_strlen(posixID) < 2) { - return 0; - } - - /*Binary search for the map entry for normal cases */ - - while (high > low) /*binary search*/{ - - mid = (high+low) >> 1; /*Finds median*/ - - if (mid == oldmid) - break; - - compVal = uprv_strcmp(langID, gPosixIDmap[mid].regionMaps->posixID); - if (compVal < 0){ - high = mid; - } - else if (compVal > 0){ - low = mid; - } - else /*we found it*/{ - return getHostID(&gPosixIDmap[mid], posixID, status); - } - oldmid = mid; - } - - /* - * Sometimes we can't do a binary search on posixID because some LCIDs - * go to different locales. We hit one of those special cases. - */ - for (idx = 0; idx < gLocaleCount; idx++ ) { - myStatus = U_ZERO_ERROR; - value = getHostID(&gPosixIDmap[idx], posixID, &myStatus); - if (myStatus == U_ZERO_ERROR) { - return value; - } - else if (myStatus == U_USING_FALLBACK_WARNING) { - fallbackValue = value; - } - } - - if (fallbackValue != (uint32_t)-1) { - *status = U_USING_FALLBACK_WARNING; - return fallbackValue; - } - - /* no match found */ - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; /* return international (root) */ -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 1996-2016, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + * + * Provides functionality for mapping between + * LCID and Posix IDs or ICU locale to codepage + * + * Note: All classes and code in this file are + * intended for internal use only. + * + * Methods of interest: + * unsigned long convertToLCID(const char*); + * const char* convertToPosix(unsigned long); + * + * Kathleen Wilson, 4/30/96 + * + * Date Name Description + * 3/11/97 aliu Fixed off-by-one bug in assignment operator. Added + * setId() method and safety check against + * MAX_ID_LENGTH. + * 04/23/99 stephen Added C wrapper for convertToPosix. + * 09/18/00 george Removed the memory leaks. + * 08/23/01 george Convert to C + */ + +#include "locmap.h" +#include "bytesinkutil.h" +#include "charstr.h" +#include "cstring.h" +#include "cmemory.h" +#include "ulocimp.h" +#include "unicode/uloc.h" + +#if U_PLATFORM_HAS_WIN32_API && UCONFIG_USE_WINDOWS_LCID_MAPPING_API +#include +#include // LCIDToLocaleName and LocaleNameToLCID +#endif + +/* + * Note: + * The mapping from Win32 locale ID numbers to POSIX locale strings should + * be the faster one. + * + * Windows LCIDs are defined at https://msdn.microsoft.com/en-us/library/cc233965.aspx + * [MS-LCID] Windows Language Code Identifier (LCID) Reference + */ + +/* +//////////////////////////////////////////////// +// +// Internal Classes for LCID <--> POSIX Mapping +// +///////////////////////////////////////////////// +*/ + +typedef struct ILcidPosixElement +{ + const uint32_t hostID; + const char * const posixID; +} ILcidPosixElement; + +typedef struct ILcidPosixMap +{ + const uint32_t numRegions; + const struct ILcidPosixElement* const regionMaps; +} ILcidPosixMap; + + +/* +///////////////////////////////////////////////// +// +// Easy macros to make the LCID <--> POSIX Mapping +// +///////////////////////////////////////////////// +*/ + +/** + * The standard one language/one country mapping for LCID. + * The first element must be the language, and the following + * elements are the language with the country. + * @param hostID LCID in host format such as 0x044d + * @param languageID posix ID of just the language such as 'de' + * @param posixID posix ID of the language_TERRITORY such as 'de_CH' + */ +#define ILCID_POSIX_ELEMENT_ARRAY(hostID, languageID, posixID) \ +static const ILcidPosixElement locmap_ ## languageID [] = { \ + {LANGUAGE_LCID(hostID), #languageID}, /* parent locale */ \ + {hostID, #posixID}, \ +}; + +/** + * Define a subtable by ID + * @param id the POSIX ID, either a language or language_TERRITORY + */ +#define ILCID_POSIX_SUBTABLE(id) \ +static const ILcidPosixElement locmap_ ## id [] = + + +/** + * Create the map for the posixID. This macro supposes that the language string + * name is the same as the global variable name, and that the first element + * in the ILcidPosixElement is just the language. + * @param _posixID the full POSIX ID for this entry. + */ +#define ILCID_POSIX_MAP(_posixID) \ + {UPRV_LENGTHOF(locmap_ ## _posixID), locmap_ ## _posixID} + +/* +//////////////////////////////////////////// +// +// Create the table of LCID to POSIX Mapping +// None of it should be dynamically created. +// +// Keep static locale variables inside the function so that +// it can be created properly during static init. +// +// Note: This table should be updated periodically. Check the [MS-LCID] Windows Language Code Identifier +// (LCID) Reference defined at https://msdn.microsoft.com/en-us/library/cc233965.aspx +// +// Microsoft is moving away from LCID in favor of locale name as of Vista. This table needs to be +// maintained for support of older Windows version. +// Update: Windows 7 (091130) +// +// Note: Microsoft assign a different LCID if a locale has a sorting variant. POSIX IDs below may contain +// @collation=XXX, but no other keywords are allowed (at least for now). When uprv_convertToLCID() is +// called from uloc_getLCID(), keywords other than collation are already removed. If we really need +// to support other keywords in this mapping data, we must update the implementation. +//////////////////////////////////////////// +*/ + +// TODO: For Windows ideally this table would be a list of exceptions rather than a complete list as +// LocaleNameToLCID and LCIDToLocaleName provide 90% of these. + +ILCID_POSIX_ELEMENT_ARRAY(0x0436, af, af_ZA) + +ILCID_POSIX_SUBTABLE(ar) { + {0x01, "ar"}, + {0x3801, "ar_AE"}, + {0x3c01, "ar_BH"}, + {0x1401, "ar_DZ"}, + {0x0c01, "ar_EG"}, + {0x0801, "ar_IQ"}, + {0x2c01, "ar_JO"}, + {0x3401, "ar_KW"}, + {0x3001, "ar_LB"}, + {0x1001, "ar_LY"}, + {0x1801, "ar_MA"}, + {0x1801, "ar_MO"}, + {0x2001, "ar_OM"}, + {0x4001, "ar_QA"}, + {0x0401, "ar_SA"}, + {0x2801, "ar_SY"}, + {0x1c01, "ar_TN"}, + {0x2401, "ar_YE"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x044d, as, as_IN) +ILCID_POSIX_ELEMENT_ARRAY(0x045e, am, am_ET) +ILCID_POSIX_ELEMENT_ARRAY(0x047a, arn,arn_CL) + +ILCID_POSIX_SUBTABLE(az) { + {0x2c, "az"}, + {0x082c, "az_Cyrl_AZ"}, /* Cyrillic based */ + {0x742c, "az_Cyrl"}, /* Cyrillic based */ + {0x042c, "az_Latn_AZ"}, /* Latin based */ + {0x782c, "az_Latn"}, /* Latin based */ + {0x042c, "az_AZ"} /* Latin based */ +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x046d, ba, ba_RU) +ILCID_POSIX_ELEMENT_ARRAY(0x0423, be, be_BY) + +/*ILCID_POSIX_SUBTABLE(ber) { + {0x5f, "ber"}, + {0x045f, "ber_Arab_DZ"}, + {0x045f, "ber_Arab"}, + {0x085f, "ber_Latn_DZ"}, + {0x085f, "ber_Latn"} +};*/ + +ILCID_POSIX_ELEMENT_ARRAY(0x0402, bg, bg_BG) + +ILCID_POSIX_SUBTABLE(bin) { + {0x66, "bin"}, + {0x0466, "bin_NG"} +}; + +ILCID_POSIX_SUBTABLE(bn) { + {0x45, "bn"}, + {0x0845, "bn_BD"}, + {0x0445, "bn_IN"} +}; + +ILCID_POSIX_SUBTABLE(bo) { + {0x51, "bo"}, + {0x0851, "bo_BT"}, + {0x0451, "bo_CN"}, + {0x0c51, "dz_BT"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x047e, br, br_FR) + +ILCID_POSIX_SUBTABLE(ca) { + {0x03, "ca"}, + {0x0403, "ca_ES"}, + {0x0803, "ca_ES_VALENCIA"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0483, co, co_FR) + +ILCID_POSIX_SUBTABLE(chr) { + {0x05c, "chr"}, + {0x7c5c, "chr_Cher"}, + {0x045c, "chr_Cher_US"}, + {0x045c, "chr_US"} +}; + +// ICU has chosen different names for these. +ILCID_POSIX_SUBTABLE(ckb) { + {0x92, "ckb"}, + {0x7c92, "ckb_Arab"}, + {0x0492, "ckb_Arab_IQ"} +}; + +/* Declared as cs_CZ to get around compiler errors on z/OS, which defines cs as a function */ +ILCID_POSIX_ELEMENT_ARRAY(0x0405, cs, cs_CZ) + +ILCID_POSIX_ELEMENT_ARRAY(0x0452, cy, cy_GB) +ILCID_POSIX_ELEMENT_ARRAY(0x0406, da, da_DK) + +// Windows doesn't know POSIX or BCP47 Unicode phonebook sort names +ILCID_POSIX_SUBTABLE(de) { + {0x07, "de"}, + {0x0c07, "de_AT"}, + {0x0807, "de_CH"}, + {0x0407, "de_DE"}, + {0x1407, "de_LI"}, + {0x1007, "de_LU"}, + {0x10407,"de_DE@collation=phonebook"}, /*This is really de_DE_PHONEBOOK on Windows*/ + {0x10407,"de@collation=phonebook"} /*This is really de_DE_PHONEBOOK on Windows*/ +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0465, dv, dv_MV) +ILCID_POSIX_ELEMENT_ARRAY(0x0408, el, el_GR) + +// Windows uses an empty string for 'invariant' +ILCID_POSIX_SUBTABLE(en) { + {0x09, "en"}, + {0x0c09, "en_AU"}, + {0x2809, "en_BZ"}, + {0x1009, "en_CA"}, + {0x0809, "en_GB"}, + {0x3c09, "en_HK"}, + {0x3809, "en_ID"}, + {0x1809, "en_IE"}, + {0x4009, "en_IN"}, + {0x2009, "en_JM"}, + {0x4409, "en_MY"}, + {0x1409, "en_NZ"}, + {0x3409, "en_PH"}, + {0x4809, "en_SG"}, + {0x2C09, "en_TT"}, + {0x0409, "en_US"}, + {0x007f, "en_US_POSIX"}, /* duplicate for round-tripping */ + {0x2409, "en_029"}, + {0x1c09, "en_ZA"}, + {0x3009, "en_ZW"}, + {0x2409, "en_VI"}, /* Virgin Islands AKA Caribbean Islands (en_CB). On Windows8+ This is 0x1000 or dynamically assigned */ + {0x0409, "en_AS"}, /* Alias for en_US. Leave last. On Windows8+ This is 0x1000 or dynamically assigned */ + {0x0409, "en_GU"}, /* Alias for en_US. Leave last. On Windows8+ This is 0x1000 or dynamically assigned */ + {0x0409, "en_MH"}, /* Alias for en_US. Leave last. On Windows8+ This is 0x1000 or dynamically assigned */ + {0x0409, "en_MP"}, /* Alias for en_US. Leave last. On Windows8+ This is 0x1000 or dynamically assigned */ + {0x0409, "en_UM"} /* Alias for en_US. Leave last. On Windows8+ This is 0x1000 or dynamically assigned */ +}; + +ILCID_POSIX_SUBTABLE(en_US_POSIX) { + {0x007f, "en_US_POSIX"} /* duplicate for roundtripping */ +}; + +// Windows doesn't know POSIX or BCP47 Unicode traditional sort names +ILCID_POSIX_SUBTABLE(es) { + {0x0a, "es"}, + {0x2c0a, "es_AR"}, + {0x400a, "es_BO"}, + {0x340a, "es_CL"}, + {0x240a, "es_CO"}, + {0x140a, "es_CR"}, + {0x5c0a, "es_CU"}, + {0x1c0a, "es_DO"}, + {0x300a, "es_EC"}, + {0x0c0a, "es_ES"}, /*Modern sort.*/ + {0x100a, "es_GT"}, + {0x480a, "es_HN"}, + {0x080a, "es_MX"}, + {0x4c0a, "es_NI"}, + {0x180a, "es_PA"}, + {0x280a, "es_PE"}, + {0x500a, "es_PR"}, + {0x3c0a, "es_PY"}, + {0x440a, "es_SV"}, + {0x540a, "es_US"}, + {0x380a, "es_UY"}, + {0x200a, "es_VE"}, + {0x580a, "es_419"}, + {0x040a, "es_ES@collation=traditional"}, + {0x040a, "es@collation=traditional"} // Windows will treat this as es-ES@collation=traditional +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0425, et, et_EE) +ILCID_POSIX_ELEMENT_ARRAY(0x042d, eu, eu_ES) + +/* ISO-639 doesn't distinguish between Persian and Dari.*/ +ILCID_POSIX_SUBTABLE(fa) { + {0x29, "fa"}, + {0x0429, "fa_IR"}, /* Persian/Farsi (Iran) */ + {0x048c, "fa_AF"} /* Persian/Dari (Afghanistan) */ +}; + + +/* duplicate for roundtripping */ +ILCID_POSIX_SUBTABLE(fa_AF) { + {0x8c, "fa_AF"}, /* Persian/Dari (Afghanistan) */ + {0x048c, "fa_AF"} /* Persian/Dari (Afghanistan) */ +}; + +ILCID_POSIX_SUBTABLE(ff) { + {0x67, "ff"}, + {0x7c67, "ff_Latn"}, + {0x0867, "ff_Latn_SN"}, + {0x0467, "ff_NG"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x040b, fi, fi_FI) +ILCID_POSIX_ELEMENT_ARRAY(0x0464, fil,fil_PH) +ILCID_POSIX_ELEMENT_ARRAY(0x0438, fo, fo_FO) + +ILCID_POSIX_SUBTABLE(fr) { + {0x0c, "fr"}, + {0x080c, "fr_BE"}, + {0x0c0c, "fr_CA"}, + {0x240c, "fr_CD"}, + {0x240c, "fr_CG"}, + {0x100c, "fr_CH"}, + {0x300c, "fr_CI"}, + {0x2c0c, "fr_CM"}, + {0x040c, "fr_FR"}, + {0x3c0c, "fr_HT"}, + {0x140c, "fr_LU"}, + {0x380c, "fr_MA"}, + {0x180c, "fr_MC"}, + {0x340c, "fr_ML"}, + {0x200c, "fr_RE"}, + {0x280c, "fr_SN"}, + {0xe40c, "fr_015"}, + {0x1c0c, "fr_029"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0467, fuv, fuv_NG) + +ILCID_POSIX_ELEMENT_ARRAY(0x0462, fy, fy_NL) + +ILCID_POSIX_SUBTABLE(ga) { /* Gaelic (Ireland) */ + {0x3c, "ga"}, + {0x083c, "ga_IE"}, + {0x043c, "gd_GB"} +}; + +ILCID_POSIX_SUBTABLE(gd) { /* Gaelic (Scotland) */ + {0x91, "gd"}, + {0x0491, "gd_GB"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0456, gl, gl_ES) +ILCID_POSIX_ELEMENT_ARRAY(0x0447, gu, gu_IN) +ILCID_POSIX_ELEMENT_ARRAY(0x0474, gn, gn_PY) +ILCID_POSIX_ELEMENT_ARRAY(0x0484, gsw,gsw_FR) + +ILCID_POSIX_SUBTABLE(ha) { + {0x68, "ha"}, + {0x7c68, "ha_Latn"}, + {0x0468, "ha_Latn_NG"}, +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0475, haw,haw_US) +ILCID_POSIX_ELEMENT_ARRAY(0x040d, he, he_IL) +ILCID_POSIX_ELEMENT_ARRAY(0x0439, hi, hi_IN) + +/* This LCID is really four different locales.*/ +ILCID_POSIX_SUBTABLE(hr) { + {0x1a, "hr"}, + {0x141a, "bs_Latn_BA"}, /* Bosnian, Bosnia and Herzegovina */ + {0x681a, "bs_Latn"}, /* Bosnian, Bosnia and Herzegovina */ + {0x141a, "bs_BA"}, /* Bosnian, Bosnia and Herzegovina */ + {0x781a, "bs"}, /* Bosnian */ + {0x201a, "bs_Cyrl_BA"}, /* Bosnian, Bosnia and Herzegovina */ + {0x641a, "bs_Cyrl"}, /* Bosnian, Bosnia and Herzegovina */ + {0x101a, "hr_BA"}, /* Croatian in Bosnia */ + {0x041a, "hr_HR"}, /* Croatian*/ + {0x2c1a, "sr_Latn_ME"}, + {0x241a, "sr_Latn_RS"}, + {0x181a, "sr_Latn_BA"}, /* Serbo-Croatian in Bosnia */ + {0x081a, "sr_Latn_CS"}, /* Serbo-Croatian*/ + {0x701a, "sr_Latn"}, /* It's 0x1a or 0x081a, pick one to make the test program happy. */ + {0x1c1a, "sr_Cyrl_BA"}, /* Serbo-Croatian in Bosnia */ + {0x0c1a, "sr_Cyrl_CS"}, /* Serbian*/ + {0x301a, "sr_Cyrl_ME"}, + {0x281a, "sr_Cyrl_RS"}, + {0x6c1a, "sr_Cyrl"}, /* It's 0x1a or 0x0c1a, pick one to make the test program happy. */ + {0x7c1a, "sr"} /* In CLDR sr is sr_Cyrl. */ +}; + +ILCID_POSIX_SUBTABLE(hsb) { + {0x2E, "hsb"}, + {0x042E, "hsb_DE"}, + {0x082E, "dsb_DE"}, + {0x7C2E, "dsb"}, +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x040e, hu, hu_HU) +ILCID_POSIX_ELEMENT_ARRAY(0x042b, hy, hy_AM) + +ILCID_POSIX_SUBTABLE(ibb) { + {0x69, "ibb"}, + {0x0469, "ibb_NG"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0421, id, id_ID) +ILCID_POSIX_ELEMENT_ARRAY(0x0470, ig, ig_NG) +ILCID_POSIX_ELEMENT_ARRAY(0x0478, ii, ii_CN) +ILCID_POSIX_ELEMENT_ARRAY(0x040f, is, is_IS) + +ILCID_POSIX_SUBTABLE(it) { + {0x10, "it"}, + {0x0810, "it_CH"}, + {0x0410, "it_IT"} +}; + +ILCID_POSIX_SUBTABLE(iu) { + {0x5d, "iu"}, + {0x045d, "iu_Cans_CA"}, + {0x785d, "iu_Cans"}, + {0x085d, "iu_Latn_CA"}, + {0x7c5d, "iu_Latn"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x040d, iw, iw_IL) /*Left in for compatibility*/ +ILCID_POSIX_ELEMENT_ARRAY(0x0411, ja, ja_JP) +ILCID_POSIX_ELEMENT_ARRAY(0x0437, ka, ka_GE) +ILCID_POSIX_ELEMENT_ARRAY(0x043f, kk, kk_KZ) +ILCID_POSIX_ELEMENT_ARRAY(0x046f, kl, kl_GL) +ILCID_POSIX_ELEMENT_ARRAY(0x0453, km, km_KH) +ILCID_POSIX_ELEMENT_ARRAY(0x044b, kn, kn_IN) + +ILCID_POSIX_SUBTABLE(ko) { + {0x12, "ko"}, + {0x0812, "ko_KP"}, + {0x0412, "ko_KR"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0457, kok, kok_IN) +ILCID_POSIX_ELEMENT_ARRAY(0x0471, kr, kr_NG) + +ILCID_POSIX_SUBTABLE(ks) { /* We could add PK and CN too */ + {0x60, "ks"}, + {0x0460, "ks_Arab_IN"}, + {0x0860, "ks_Deva_IN"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0440, ky, ky_KG) /* Kyrgyz is spoken in Kyrgyzstan */ + +ILCID_POSIX_SUBTABLE(la) { + {0x76, "la"}, + {0x0476, "la_001"}, + {0x0476, "la_IT"} /*Left in for compatibility*/ +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x046e, lb, lb_LU) +ILCID_POSIX_ELEMENT_ARRAY(0x0454, lo, lo_LA) +ILCID_POSIX_ELEMENT_ARRAY(0x0427, lt, lt_LT) +ILCID_POSIX_ELEMENT_ARRAY(0x0426, lv, lv_LV) +ILCID_POSIX_ELEMENT_ARRAY(0x0481, mi, mi_NZ) +ILCID_POSIX_ELEMENT_ARRAY(0x042f, mk, mk_MK) +ILCID_POSIX_ELEMENT_ARRAY(0x044c, ml, ml_IN) + +ILCID_POSIX_SUBTABLE(mn) { + {0x50, "mn"}, + {0x0450, "mn_MN"}, + {0x7c50, "mn_Mong"}, + {0x0850, "mn_Mong_CN"}, + {0x0850, "mn_CN"}, + {0x7850, "mn_Cyrl"}, + {0x0c50, "mn_Mong_MN"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0458, mni,mni_IN) +ILCID_POSIX_ELEMENT_ARRAY(0x047c, moh,moh_CA) +ILCID_POSIX_ELEMENT_ARRAY(0x044e, mr, mr_IN) + +ILCID_POSIX_SUBTABLE(ms) { + {0x3e, "ms"}, + {0x083e, "ms_BN"}, /* Brunei Darussalam*/ + {0x043e, "ms_MY"} /* Malaysia*/ +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x043a, mt, mt_MT) +ILCID_POSIX_ELEMENT_ARRAY(0x0455, my, my_MM) + +ILCID_POSIX_SUBTABLE(ne) { + {0x61, "ne"}, + {0x0861, "ne_IN"}, /* India*/ + {0x0461, "ne_NP"} /* Nepal*/ +}; + +ILCID_POSIX_SUBTABLE(nl) { + {0x13, "nl"}, + {0x0813, "nl_BE"}, + {0x0413, "nl_NL"} +}; + +/* The "no" locale split into nb and nn. By default in ICU, "no" is nb.*/ +// TODO: Not all of these are needed on Windows, but I don't know how ICU treats preferred ones here. +ILCID_POSIX_SUBTABLE(no) { + {0x14, "no"}, /* really nb_NO - actually Windows differentiates between neutral (no region) and specific (with region) */ + {0x7c14, "nb"}, /* really nb */ + {0x0414, "nb_NO"}, /* really nb_NO. Keep first in the 414 list. */ + {0x0414, "no_NO"}, /* really nb_NO */ + {0x0814, "nn_NO"}, /* really nn_NO. Keep first in the 814 list. */ + {0x7814, "nn"}, /* It's 0x14 or 0x814, pick one to make the test program happy. */ + {0x0814, "no_NO_NY"}/* really nn_NO */ +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x046c, nso,nso_ZA) /* TODO: Verify the ISO-639 code */ +ILCID_POSIX_ELEMENT_ARRAY(0x0482, oc, oc_FR) + +ILCID_POSIX_SUBTABLE(om) { /* TODO: Verify the country */ + {0x72, "om"}, + {0x0472, "om_ET"}, + {0x0472, "gaz_ET"} +}; + +/* Declared as or_IN to get around compiler errors*/ +ILCID_POSIX_SUBTABLE(or_IN) { + {0x48, "or"}, + {0x0448, "or_IN"}, +}; + +ILCID_POSIX_SUBTABLE(pa) { + {0x46, "pa"}, + {0x0446, "pa_IN"}, + {0x0846, "pa_Arab_PK"}, + {0x0846, "pa_PK"} +}; + +ILCID_POSIX_SUBTABLE(pap) { + {0x79, "pap"}, + {0x0479, "pap_029"}, + {0x0479, "pap_AN"} /*Left in for compatibility*/ +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0415, pl, pl_PL) +ILCID_POSIX_ELEMENT_ARRAY(0x0463, ps, ps_AF) + +ILCID_POSIX_SUBTABLE(pt) { + {0x16, "pt"}, + {0x0416, "pt_BR"}, + {0x0816, "pt_PT"} +}; + +ILCID_POSIX_SUBTABLE(qu) { + {0x6b, "qu"}, + {0x046b, "qu_BO"}, + {0x086b, "qu_EC"}, + {0x0C6b, "qu_PE"}, + {0x046b, "quz_BO"}, + {0x086b, "quz_EC"}, + {0x0C6b, "quz_PE"} +}; + +ILCID_POSIX_SUBTABLE(quc) { + {0x93, "quc"}, + {0x0493, "quc_CO"}, + /* + "quc_Latn_GT" is an exceptional case. Language ID of "quc" + is 0x93, but LCID of "quc_Latn_GT" is 0x486, which should be + under the group of "qut". "qut" is a retired ISO 639-3 language + code for West Central Quiche, and merged to "quc". + It looks Windows previously reserved "qut" for K'iche', but, + decided to use "quc" when adding a locale for K'iche' (Guatemala). + + This data structure used here assumes language ID bits in + LCID is unique for alphabetic language code. But this is not true + for "quc_Latn_GT". If we don't have the data below, LCID look up + by alphabetic locale ID (POSIX) will fail. The same entry is found + under "qut" below, which is required for reverse look up. + */ + {0x0486, "quc_Latn_GT"} +}; + +ILCID_POSIX_SUBTABLE(qut) { + {0x86, "qut"}, + {0x0486, "qut_GT"}, + /* + See the note in "quc" above. + */ + {0x0486, "quc_Latn_GT"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0417, rm, rm_CH) + +ILCID_POSIX_SUBTABLE(ro) { + {0x18, "ro"}, + {0x0418, "ro_RO"}, + {0x0818, "ro_MD"} +}; + +// TODO: This is almost certainly 'wrong'. 0 in Windows is a synonym for LOCALE_USER_DEFAULT. +// More likely this is a similar concept to the Windows 0x7f Invariant locale "" +// (Except that it's not invariant in ICU) +ILCID_POSIX_SUBTABLE(root) { + {0x00, "root"} +}; + +ILCID_POSIX_SUBTABLE(ru) { + {0x19, "ru"}, + {0x0419, "ru_RU"}, + {0x0819, "ru_MD"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0487, rw, rw_RW) +ILCID_POSIX_ELEMENT_ARRAY(0x044f, sa, sa_IN) +ILCID_POSIX_ELEMENT_ARRAY(0x0485, sah,sah_RU) + +ILCID_POSIX_SUBTABLE(sd) { + {0x59, "sd"}, + {0x0459, "sd_Deva_IN"}, + {0x0459, "sd_IN"}, + {0x0859, "sd_Arab_PK"}, + {0x0859, "sd_PK"}, + {0x7c59, "sd_Arab"} +}; + +ILCID_POSIX_SUBTABLE(se) { + {0x3b, "se"}, + {0x0c3b, "se_FI"}, + {0x043b, "se_NO"}, + {0x083b, "se_SE"}, + {0x783b, "sma"}, + {0x183b, "sma_NO"}, + {0x1c3b, "sma_SE"}, + {0x7c3b, "smj"}, + {0x703b, "smn"}, + {0x743b, "sms"}, + {0x103b, "smj_NO"}, + {0x143b, "smj_SE"}, + {0x243b, "smn_FI"}, + {0x203b, "sms_FI"}, +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x045b, si, si_LK) +ILCID_POSIX_ELEMENT_ARRAY(0x041b, sk, sk_SK) +ILCID_POSIX_ELEMENT_ARRAY(0x0424, sl, sl_SI) + +ILCID_POSIX_SUBTABLE(so) { + {0x77, "so"}, + {0x0477, "so_SO"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x041c, sq, sq_AL) +ILCID_POSIX_ELEMENT_ARRAY(0x0430, st, st_ZA) + +ILCID_POSIX_SUBTABLE(sv) { + {0x1d, "sv"}, + {0x081d, "sv_FI"}, + {0x041d, "sv_SE"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0441, sw, sw_KE) +ILCID_POSIX_ELEMENT_ARRAY(0x045A, syr, syr_SY) + +ILCID_POSIX_SUBTABLE(ta) { + {0x49, "ta"}, + {0x0449, "ta_IN"}, + {0x0849, "ta_LK"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x044a, te, te_IN) + +/* Cyrillic based by default */ +ILCID_POSIX_SUBTABLE(tg) { + {0x28, "tg"}, + {0x7c28, "tg_Cyrl"}, + {0x0428, "tg_Cyrl_TJ"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x041e, th, th_TH) + +ILCID_POSIX_SUBTABLE(ti) { + {0x73, "ti"}, + {0x0873, "ti_ER"}, + {0x0473, "ti_ET"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0442, tk, tk_TM) + +ILCID_POSIX_SUBTABLE(tn) { + {0x32, "tn"}, + {0x0832, "tn_BW"}, + {0x0432, "tn_ZA"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x041f, tr, tr_TR) +ILCID_POSIX_ELEMENT_ARRAY(0x0431, ts, ts_ZA) +ILCID_POSIX_ELEMENT_ARRAY(0x0444, tt, tt_RU) + +ILCID_POSIX_SUBTABLE(tzm) { + {0x5f, "tzm"}, + {0x7c5f, "tzm_Latn"}, + {0x085f, "tzm_Latn_DZ"}, + {0x105f, "tzm_Tfng_MA"}, + {0x045f, "tzm_Arab_MA"}, + {0x045f, "tmz"} +}; + +ILCID_POSIX_SUBTABLE(ug) { + {0x80, "ug"}, + {0x0480, "ug_CN"}, + {0x0480, "ug_Arab_CN"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0422, uk, uk_UA) + +ILCID_POSIX_SUBTABLE(ur) { + {0x20, "ur"}, + {0x0820, "ur_IN"}, + {0x0420, "ur_PK"} +}; + +ILCID_POSIX_SUBTABLE(uz) { + {0x43, "uz"}, + {0x0843, "uz_Cyrl_UZ"}, /* Cyrillic based */ + {0x7843, "uz_Cyrl"}, /* Cyrillic based */ + {0x0843, "uz_UZ"}, /* Cyrillic based */ + {0x0443, "uz_Latn_UZ"}, /* Latin based */ + {0x7c43, "uz_Latn"} /* Latin based */ +}; + +ILCID_POSIX_SUBTABLE(ve) { /* TODO: Verify the country */ + {0x33, "ve"}, + {0x0433, "ve_ZA"}, + {0x0433, "ven_ZA"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x042a, vi, vi_VN) +ILCID_POSIX_ELEMENT_ARRAY(0x0488, wo, wo_SN) +ILCID_POSIX_ELEMENT_ARRAY(0x0434, xh, xh_ZA) + +ILCID_POSIX_SUBTABLE(yi) { + {0x003d, "yi"}, + {0x043d, "yi_001"} +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x046a, yo, yo_NG) + +// Windows & ICU tend to different names for some of these +// TODO: Windows probably does not need all of these entries, but I don't know how the precedence works. +ILCID_POSIX_SUBTABLE(zh) { + {0x0004, "zh_Hans"}, + {0x7804, "zh"}, + {0x0804, "zh_CN"}, + {0x0804, "zh_Hans_CN"}, + {0x0c04, "zh_Hant_HK"}, + {0x0c04, "zh_HK"}, + {0x1404, "zh_Hant_MO"}, + {0x1404, "zh_MO"}, + {0x1004, "zh_Hans_SG"}, + {0x1004, "zh_SG"}, + {0x0404, "zh_Hant_TW"}, + {0x7c04, "zh_Hant"}, + {0x0404, "zh_TW"}, + {0x30404,"zh_Hant_TW"}, /* Bopomofo order */ + {0x30404,"zh_TW"}, /* Bopomofo order */ + {0x20004,"zh@collation=stroke"}, + {0x20404,"zh_Hant@collation=stroke"}, + {0x20404,"zh_Hant_TW@collation=stroke"}, + {0x20404,"zh_TW@collation=stroke"}, + {0x20804,"zh_Hans@collation=stroke"}, + {0x20804,"zh_Hans_CN@collation=stroke"}, + {0x20804,"zh_CN@collation=stroke"} + // TODO: Alternate collations for other LCIDs are missing, eg: 0x50804 +}; + +ILCID_POSIX_ELEMENT_ARRAY(0x0435, zu, zu_ZA) + +/* This must be static and grouped by LCID. */ +static const ILcidPosixMap gPosixIDmap[] = { + ILCID_POSIX_MAP(af), /* af Afrikaans 0x36 */ + ILCID_POSIX_MAP(am), /* am Amharic 0x5e */ + ILCID_POSIX_MAP(ar), /* ar Arabic 0x01 */ + ILCID_POSIX_MAP(arn), /* arn Araucanian/Mapudungun 0x7a */ + ILCID_POSIX_MAP(as), /* as Assamese 0x4d */ + ILCID_POSIX_MAP(az), /* az Azerbaijani 0x2c */ + ILCID_POSIX_MAP(ba), /* ba Bashkir 0x6d */ + ILCID_POSIX_MAP(be), /* be Belarusian 0x23 */ +/* ILCID_POSIX_MAP(ber), ber Berber/Tamazight 0x5f */ + ILCID_POSIX_MAP(bg), /* bg Bulgarian 0x02 */ + ILCID_POSIX_MAP(bin), /* bin Edo 0x66 */ + ILCID_POSIX_MAP(bn), /* bn Bengali; Bangla 0x45 */ + ILCID_POSIX_MAP(bo), /* bo Tibetan 0x51 */ + ILCID_POSIX_MAP(br), /* br Breton 0x7e */ + ILCID_POSIX_MAP(ca), /* ca Catalan 0x03 */ + ILCID_POSIX_MAP(chr), /* chr Cherokee 0x5c */ + ILCID_POSIX_MAP(ckb), /* ckb Sorani (Central Kurdish) 0x92 */ + ILCID_POSIX_MAP(co), /* co Corsican 0x83 */ + ILCID_POSIX_MAP(cs), /* cs Czech 0x05 */ + ILCID_POSIX_MAP(cy), /* cy Welsh 0x52 */ + ILCID_POSIX_MAP(da), /* da Danish 0x06 */ + ILCID_POSIX_MAP(de), /* de German 0x07 */ + ILCID_POSIX_MAP(dv), /* dv Divehi 0x65 */ + ILCID_POSIX_MAP(el), /* el Greek 0x08 */ + ILCID_POSIX_MAP(en), /* en English 0x09 */ + ILCID_POSIX_MAP(en_US_POSIX), /* invariant 0x7f */ + ILCID_POSIX_MAP(es), /* es Spanish 0x0a */ + ILCID_POSIX_MAP(et), /* et Estonian 0x25 */ + ILCID_POSIX_MAP(eu), /* eu Basque 0x2d */ + ILCID_POSIX_MAP(fa), /* fa Persian/Farsi 0x29 */ + ILCID_POSIX_MAP(fa_AF), /* fa Persian/Dari 0x8c */ + ILCID_POSIX_MAP(ff), /* ff Fula 0x67 */ + ILCID_POSIX_MAP(fi), /* fi Finnish 0x0b */ + ILCID_POSIX_MAP(fil), /* fil Filipino 0x64 */ + ILCID_POSIX_MAP(fo), /* fo Faroese 0x38 */ + ILCID_POSIX_MAP(fr), /* fr French 0x0c */ + ILCID_POSIX_MAP(fuv), /* fuv Fulfulde - Nigeria 0x67 */ + ILCID_POSIX_MAP(fy), /* fy Frisian 0x62 */ + ILCID_POSIX_MAP(ga), /* * Gaelic (Ireland,Scotland) 0x3c */ + ILCID_POSIX_MAP(gd), /* gd Gaelic (United Kingdom) 0x91 */ + ILCID_POSIX_MAP(gl), /* gl Galician 0x56 */ + ILCID_POSIX_MAP(gn), /* gn Guarani 0x74 */ + ILCID_POSIX_MAP(gsw), /* gsw Alemanic/Alsatian/Swiss German 0x84 */ + ILCID_POSIX_MAP(gu), /* gu Gujarati 0x47 */ + ILCID_POSIX_MAP(ha), /* ha Hausa 0x68 */ + ILCID_POSIX_MAP(haw), /* haw Hawaiian 0x75 */ + ILCID_POSIX_MAP(he), /* he Hebrew (formerly iw) 0x0d */ + ILCID_POSIX_MAP(hi), /* hi Hindi 0x39 */ + ILCID_POSIX_MAP(hr), /* * Croatian and others 0x1a */ + ILCID_POSIX_MAP(hsb), /* hsb Upper Sorbian 0x2e */ + ILCID_POSIX_MAP(hu), /* hu Hungarian 0x0e */ + ILCID_POSIX_MAP(hy), /* hy Armenian 0x2b */ + ILCID_POSIX_MAP(ibb), /* ibb Ibibio - Nigeria 0x69 */ + ILCID_POSIX_MAP(id), /* id Indonesian (formerly in) 0x21 */ + ILCID_POSIX_MAP(ig), /* ig Igbo 0x70 */ + ILCID_POSIX_MAP(ii), /* ii Sichuan Yi 0x78 */ + ILCID_POSIX_MAP(is), /* is Icelandic 0x0f */ + ILCID_POSIX_MAP(it), /* it Italian 0x10 */ + ILCID_POSIX_MAP(iu), /* iu Inuktitut 0x5d */ + ILCID_POSIX_MAP(iw), /* iw Hebrew 0x0d */ + ILCID_POSIX_MAP(ja), /* ja Japanese 0x11 */ + ILCID_POSIX_MAP(ka), /* ka Georgian 0x37 */ + ILCID_POSIX_MAP(kk), /* kk Kazakh 0x3f */ + ILCID_POSIX_MAP(kl), /* kl Kalaallisut 0x6f */ + ILCID_POSIX_MAP(km), /* km Khmer 0x53 */ + ILCID_POSIX_MAP(kn), /* kn Kannada 0x4b */ + ILCID_POSIX_MAP(ko), /* ko Korean 0x12 */ + ILCID_POSIX_MAP(kok), /* kok Konkani 0x57 */ + ILCID_POSIX_MAP(kr), /* kr Kanuri 0x71 */ + ILCID_POSIX_MAP(ks), /* ks Kashmiri 0x60 */ + ILCID_POSIX_MAP(ky), /* ky Kyrgyz 0x40 */ + ILCID_POSIX_MAP(lb), /* lb Luxembourgish 0x6e */ + ILCID_POSIX_MAP(la), /* la Latin 0x76 */ + ILCID_POSIX_MAP(lo), /* lo Lao 0x54 */ + ILCID_POSIX_MAP(lt), /* lt Lithuanian 0x27 */ + ILCID_POSIX_MAP(lv), /* lv Latvian, Lettish 0x26 */ + ILCID_POSIX_MAP(mi), /* mi Maori 0x81 */ + ILCID_POSIX_MAP(mk), /* mk Macedonian 0x2f */ + ILCID_POSIX_MAP(ml), /* ml Malayalam 0x4c */ + ILCID_POSIX_MAP(mn), /* mn Mongolian 0x50 */ + ILCID_POSIX_MAP(mni), /* mni Manipuri 0x58 */ + ILCID_POSIX_MAP(moh), /* moh Mohawk 0x7c */ + ILCID_POSIX_MAP(mr), /* mr Marathi 0x4e */ + ILCID_POSIX_MAP(ms), /* ms Malay 0x3e */ + ILCID_POSIX_MAP(mt), /* mt Maltese 0x3a */ + ILCID_POSIX_MAP(my), /* my Burmese 0x55 */ +/* ILCID_POSIX_MAP(nb), // no Norwegian 0x14 */ + ILCID_POSIX_MAP(ne), /* ne Nepali 0x61 */ + ILCID_POSIX_MAP(nl), /* nl Dutch 0x13 */ +/* ILCID_POSIX_MAP(nn), // no Norwegian 0x14 */ + ILCID_POSIX_MAP(no), /* * Norwegian 0x14 */ + ILCID_POSIX_MAP(nso), /* nso Sotho, Northern (Sepedi dialect) 0x6c */ + ILCID_POSIX_MAP(oc), /* oc Occitan 0x82 */ + ILCID_POSIX_MAP(om), /* om Oromo 0x72 */ + ILCID_POSIX_MAP(or_IN), /* or Oriya 0x48 */ + ILCID_POSIX_MAP(pa), /* pa Punjabi 0x46 */ + ILCID_POSIX_MAP(pap), /* pap Papiamentu 0x79 */ + ILCID_POSIX_MAP(pl), /* pl Polish 0x15 */ + ILCID_POSIX_MAP(ps), /* ps Pashto 0x63 */ + ILCID_POSIX_MAP(pt), /* pt Portuguese 0x16 */ + ILCID_POSIX_MAP(qu), /* qu Quechua 0x6B */ + ILCID_POSIX_MAP(quc), /* quc K'iche 0x93 */ + ILCID_POSIX_MAP(qut), /* qut K'iche 0x86 */ + ILCID_POSIX_MAP(rm), /* rm Raeto-Romance/Romansh 0x17 */ + ILCID_POSIX_MAP(ro), /* ro Romanian 0x18 */ + ILCID_POSIX_MAP(root), /* root 0x00 */ + ILCID_POSIX_MAP(ru), /* ru Russian 0x19 */ + ILCID_POSIX_MAP(rw), /* rw Kinyarwanda 0x87 */ + ILCID_POSIX_MAP(sa), /* sa Sanskrit 0x4f */ + ILCID_POSIX_MAP(sah), /* sah Yakut 0x85 */ + ILCID_POSIX_MAP(sd), /* sd Sindhi 0x59 */ + ILCID_POSIX_MAP(se), /* se Sami 0x3b */ +/* ILCID_POSIX_MAP(sh), // sh Serbo-Croatian 0x1a */ + ILCID_POSIX_MAP(si), /* si Sinhalese 0x5b */ + ILCID_POSIX_MAP(sk), /* sk Slovak 0x1b */ + ILCID_POSIX_MAP(sl), /* sl Slovenian 0x24 */ + ILCID_POSIX_MAP(so), /* so Somali 0x77 */ + ILCID_POSIX_MAP(sq), /* sq Albanian 0x1c */ +/* ILCID_POSIX_MAP(sr), // sr Serbian 0x1a */ + ILCID_POSIX_MAP(st), /* st Sutu 0x30 */ + ILCID_POSIX_MAP(sv), /* sv Swedish 0x1d */ + ILCID_POSIX_MAP(sw), /* sw Swahili 0x41 */ + ILCID_POSIX_MAP(syr), /* syr Syriac 0x5A */ + ILCID_POSIX_MAP(ta), /* ta Tamil 0x49 */ + ILCID_POSIX_MAP(te), /* te Telugu 0x4a */ + ILCID_POSIX_MAP(tg), /* tg Tajik 0x28 */ + ILCID_POSIX_MAP(th), /* th Thai 0x1e */ + ILCID_POSIX_MAP(ti), /* ti Tigrigna 0x73 */ + ILCID_POSIX_MAP(tk), /* tk Turkmen 0x42 */ + ILCID_POSIX_MAP(tn), /* tn Tswana 0x32 */ + ILCID_POSIX_MAP(tr), /* tr Turkish 0x1f */ + ILCID_POSIX_MAP(ts), /* ts Tsonga 0x31 */ + ILCID_POSIX_MAP(tt), /* tt Tatar 0x44 */ + ILCID_POSIX_MAP(tzm), /* tzm Tamazight 0x5f */ + ILCID_POSIX_MAP(ug), /* ug Uighur 0x80 */ + ILCID_POSIX_MAP(uk), /* uk Ukrainian 0x22 */ + ILCID_POSIX_MAP(ur), /* ur Urdu 0x20 */ + ILCID_POSIX_MAP(uz), /* uz Uzbek 0x43 */ + ILCID_POSIX_MAP(ve), /* ve Venda 0x33 */ + ILCID_POSIX_MAP(vi), /* vi Vietnamese 0x2a */ + ILCID_POSIX_MAP(wo), /* wo Wolof 0x88 */ + ILCID_POSIX_MAP(xh), /* xh Xhosa 0x34 */ + ILCID_POSIX_MAP(yi), /* yi Yiddish 0x3d */ + ILCID_POSIX_MAP(yo), /* yo Yoruba 0x6a */ + ILCID_POSIX_MAP(zh), /* zh Chinese 0x04 */ + ILCID_POSIX_MAP(zu), /* zu Zulu 0x35 */ +}; + +static const uint32_t gLocaleCount = UPRV_LENGTHOF(gPosixIDmap); + +/** + * Do not call this function. It is called by hostID. + * The function is not private because this struct must stay as a C struct, + * and this is an internal class. + */ +static int32_t +idCmp(const char* id1, const char* id2) +{ + int32_t diffIdx = 0; + while (*id1 == *id2 && *id1 != 0) { + diffIdx++; + id1++; + id2++; + } + return diffIdx; +} + +/** + * Searches for a Windows LCID + * + * @param posixID the Posix style locale id. + * @param status gets set to U_ILLEGAL_ARGUMENT_ERROR when the Posix ID has + * no equivalent Windows LCID. + * @return the LCID + */ +static uint32_t +getHostID(const ILcidPosixMap *this_0, const char* posixID, UErrorCode* status) +{ + int32_t bestIdx = 0; + int32_t bestIdxDiff = 0; + int32_t posixIDlen = (int32_t)uprv_strlen(posixID); + uint32_t idx; + + for (idx = 0; idx < this_0->numRegions; idx++ ) { + int32_t sameChars = idCmp(posixID, this_0->regionMaps[idx].posixID); + if (sameChars > bestIdxDiff && this_0->regionMaps[idx].posixID[sameChars] == 0) { + if (posixIDlen == sameChars) { + /* Exact match */ + return this_0->regionMaps[idx].hostID; + } + bestIdxDiff = sameChars; + bestIdx = idx; + } + } + /* We asked for something unusual, like en_ZZ, and we try to return the number for the same language. */ + /* We also have to make sure that sid and si and similar string subsets don't match. */ + if ((posixID[bestIdxDiff] == '_' || posixID[bestIdxDiff] == '@') + && this_0->regionMaps[bestIdx].posixID[bestIdxDiff] == 0) + { + *status = U_USING_FALLBACK_WARNING; + return this_0->regionMaps[bestIdx].hostID; + } + + /*no match found */ + *status = U_ILLEGAL_ARGUMENT_ERROR; + return this_0->regionMaps->hostID; +} + +static const char* +getPosixID(const ILcidPosixMap *this_0, uint32_t hostID) +{ + uint32_t i; + for (i = 0; i < this_0->numRegions; i++) + { + if (this_0->regionMaps[i].hostID == hostID) + { + return this_0->regionMaps[i].posixID; + } + } + + /* If you get here, then no matching region was found, + so return the language id with the wild card region. */ + return this_0->regionMaps[0].posixID; +} + +/* +////////////////////////////////////// +// +// LCID --> POSIX +// +///////////////////////////////////// +*/ +#if U_PLATFORM_HAS_WIN32_API && UCONFIG_USE_WINDOWS_LCID_MAPPING_API +/* + * Various language tags needs to be changed: + * quz -> qu + * prs -> fa + */ +#define FIX_LANGUAGE_ID_TAG(buffer, len) \ + if (len >= 3) { \ + if (buffer[0] == 'q' && buffer[1] == 'u' && buffer[2] == 'z') {\ + buffer[2] = 0; \ + uprv_strcat(buffer, buffer+3); \ + } else if (buffer[0] == 'p' && buffer[1] == 'r' && buffer[2] == 's') {\ + buffer[0] = 'f'; buffer[1] = 'a'; buffer[2] = 0; \ + uprv_strcat(buffer, buffer+3); \ + } \ + } + +#endif + +U_CAPI int32_t +uprv_convertToPosix(uint32_t hostid, char *posixID, int32_t posixIDCapacity, UErrorCode* status) +{ + uint16_t langID; + uint32_t localeIndex; + UBool bLookup = true; + const char *pPosixID = nullptr; + +#if U_PLATFORM_HAS_WIN32_API && UCONFIG_USE_WINDOWS_LCID_MAPPING_API + static_assert(ULOC_FULLNAME_CAPACITY > LOCALE_NAME_MAX_LENGTH, "Windows locale names have smaller length than ICU locale names."); + + char locName[LOCALE_NAME_MAX_LENGTH] = {}; + + // Note: Windows primary lang ID 0x92 in LCID is used for Central Kurdish and + // GetLocaleInfo() maps such LCID to "ku". However, CLDR uses "ku" for + // Northern Kurdish and "ckb" for Central Kurdish. For this reason, we cannot + // use the Windows API to resolve locale ID for this specific case. + if ((hostid & 0x3FF) != 0x92) { + int32_t tmpLen = 0; + char16_t windowsLocaleName[LOCALE_NAME_MAX_LENGTH] = {}; + + // Note: LOCALE_ALLOW_NEUTRAL_NAMES was enabled in Windows7+, prior versions did not handle neutral (no-region) locale names. + tmpLen = LCIDToLocaleName(hostid, (PWSTR)windowsLocaleName, UPRV_LENGTHOF(windowsLocaleName), LOCALE_ALLOW_NEUTRAL_NAMES); + if (tmpLen > 1) { + int32_t i = 0; + // Only need to look up in table if have _, eg for de-de_phoneb type alternate sort. + bLookup = false; + for (i = 0; i < UPRV_LENGTHOF(locName); i++) + { + locName[i] = (char)(windowsLocaleName[i]); + + // Windows locale name may contain sorting variant, such as "es-ES_tradnl". + // In such cases, we need special mapping data found in the hardcoded table + // in this source file. + if (windowsLocaleName[i] == L'_') + { + // Keep the base locale, without variant + // TODO: Should these be mapped from _phoneb to @collation=phonebook, etc.? + locName[i] = '\0'; + tmpLen = i; + bLookup = true; + break; + } + else if (windowsLocaleName[i] == L'-') + { + // Windows names use -, ICU uses _ + locName[i] = '_'; + } + else if (windowsLocaleName[i] == L'\0') + { + // No point in doing more work than necessary + break; + } + } + // TODO: Need to understand this better, why isn't it an alias? + FIX_LANGUAGE_ID_TAG(locName, tmpLen); + pPosixID = locName; + } + } +#endif + + if (bLookup) { + const char *pCandidate = nullptr; + langID = LANGUAGE_LCID(hostid); + + for (localeIndex = 0; localeIndex < gLocaleCount; localeIndex++) { + if (langID == gPosixIDmap[localeIndex].regionMaps->hostID) { + pCandidate = getPosixID(&gPosixIDmap[localeIndex], hostid); + break; + } + } + + /* On Windows, when locale name has a variant, we still look up the hardcoded table. + If a match in the hardcoded table is longer than the Windows locale name without + variant, we use the one as the result */ + if (pCandidate && (pPosixID == nullptr || uprv_strlen(pCandidate) > uprv_strlen(pPosixID))) { + pPosixID = pCandidate; + } + } + + if (pPosixID) { + int32_t resLen = static_cast(uprv_strlen(pPosixID)); + int32_t copyLen = resLen <= posixIDCapacity ? resLen : posixIDCapacity; + uprv_memcpy(posixID, pPosixID, copyLen); + if (resLen < posixIDCapacity) { + posixID[resLen] = 0; + if (*status == U_STRING_NOT_TERMINATED_WARNING) { + *status = U_ZERO_ERROR; + } + } else if (resLen == posixIDCapacity) { + *status = U_STRING_NOT_TERMINATED_WARNING; + } else { + *status = U_BUFFER_OVERFLOW_ERROR; + } + return resLen; + } + + /* no match found */ + *status = U_ILLEGAL_ARGUMENT_ERROR; + return -1; +} + +/* +////////////////////////////////////// +// +// POSIX --> LCID +// This should only be called from uloc_getLCID. +// The locale ID must be in canonical form. +// +///////////////////////////////////// +*/ +U_CAPI uint32_t +uprv_convertToLCIDPlatform(const char* localeID, UErrorCode* status) +{ + if (U_FAILURE(*status)) { + return 0; + } + + // The purpose of this function is to leverage the Windows platform name->lcid + // conversion functionality when available. +#if U_PLATFORM_HAS_WIN32_API && UCONFIG_USE_WINDOWS_LCID_MAPPING_API + int32_t len; + char baseName[ULOC_FULLNAME_CAPACITY] = {}; + const char * mylocaleID = localeID; + + // Check any for keywords. + if (uprv_strchr(localeID, '@')) + { + icu::CharString collVal; + { + icu::CharStringByteSink sink(&collVal); + ulocimp_getKeywordValue(localeID, "collation", sink, status); + } + if (U_SUCCESS(*status) && !collVal.isEmpty()) + { + // If it contains the keyword collation, return 0 so that the LCID lookup table will be used. + return 0; + } + else + { + // If the locale ID contains keywords other than collation, just use the base name. + len = uloc_getBaseName(localeID, baseName, UPRV_LENGTHOF(baseName) - 1, status); + + if (U_SUCCESS(*status) && len > 0) + { + baseName[len] = 0; + mylocaleID = baseName; + } + } + } + + char asciiBCP47Tag[LOCALE_NAME_MAX_LENGTH] = {}; + // this will change it from de_DE@collation=phonebook to de-DE-u-co-phonebk form + (void)uloc_toLanguageTag(mylocaleID, asciiBCP47Tag, UPRV_LENGTHOF(asciiBCP47Tag), false, status); + + if (U_SUCCESS(*status)) + { + // Need it to be UTF-16, not 8-bit + wchar_t bcp47Tag[LOCALE_NAME_MAX_LENGTH] = {}; + int32_t i; + for (i = 0; i < UPRV_LENGTHOF(bcp47Tag); i++) + { + if (asciiBCP47Tag[i] == '\0') + { + break; + } + else + { + // Copy the character + bcp47Tag[i] = static_cast(asciiBCP47Tag[i]); + } + } + + if (i < (UPRV_LENGTHOF(bcp47Tag) - 1)) + { + // Ensure it's null terminated + bcp47Tag[i] = L'\0'; + LCID lcid = LocaleNameToLCID(bcp47Tag, LOCALE_ALLOW_NEUTRAL_NAMES); + if (lcid > 0) + { + // Found LCID from windows, return that one, unless its completely ambiguous + // LOCALE_USER_DEFAULT and transients are OK because they will round trip + // for this process. + if (lcid != LOCALE_CUSTOM_UNSPECIFIED) + { + return lcid; + } + } + } + } +#else + (void) localeID; // Suppress unused variable warning. +#endif + + // Nothing found, or not implemented. + return 0; +} + +U_CAPI uint32_t +uprv_convertToLCID(const char *langID, const char* posixID, UErrorCode* status) +{ + // This function does the table lookup when native platform name->lcid conversion isn't available, + // or for locales that don't follow patterns the platform expects. + uint32_t low = 0; + uint32_t high = gLocaleCount; + uint32_t mid; + uint32_t oldmid = 0; + int32_t compVal; + + uint32_t value = 0; + uint32_t fallbackValue = (uint32_t)-1; + UErrorCode myStatus; + uint32_t idx; + + /* Check for incomplete id. */ + if (!langID || !posixID || uprv_strlen(langID) < 2 || uprv_strlen(posixID) < 2) { + return 0; + } + + /*Binary search for the map entry for normal cases */ + + while (high > low) /*binary search*/{ + + mid = (high+low) >> 1; /*Finds median*/ + + if (mid == oldmid) + break; + + compVal = uprv_strcmp(langID, gPosixIDmap[mid].regionMaps->posixID); + if (compVal < 0){ + high = mid; + } + else if (compVal > 0){ + low = mid; + } + else /*we found it*/{ + return getHostID(&gPosixIDmap[mid], posixID, status); + } + oldmid = mid; + } + + /* + * Sometimes we can't do a binary search on posixID because some LCIDs + * go to different locales. We hit one of those special cases. + */ + for (idx = 0; idx < gLocaleCount; idx++ ) { + myStatus = U_ZERO_ERROR; + value = getHostID(&gPosixIDmap[idx], posixID, &myStatus); + if (myStatus == U_ZERO_ERROR) { + return value; + } + else if (myStatus == U_USING_FALLBACK_WARNING) { + fallbackValue = value; + } + } + + if (fallbackValue != (uint32_t)-1) { + *status = U_USING_FALLBACK_WARNING; + return fallbackValue; + } + + /* no match found */ + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; /* return international (root) */ +} diff --git a/deps/icu-small/source/common/locmap.h b/deps/icu-small/source/common/locmap.h index e669873a143bfd..6f37300145cb36 100644 --- a/deps/icu-small/source/common/locmap.h +++ b/deps/icu-small/source/common/locmap.h @@ -1,40 +1,40 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1996-2013, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* File locmap.h : Locale Mapping Classes -* -* -* Created by: Helena Shih -* -* Modification History: -* -* Date Name Description -* 3/11/97 aliu Added setId(). -* 4/20/99 Madhu Added T_convertToPosix() -* 09/18/00 george Removed the memory leaks. -* 08/23/01 george Convert to C -*============================================================================ -*/ - -#ifndef LOCMAP_H -#define LOCMAP_H - -#include "unicode/utypes.h" - -#define LANGUAGE_LCID(hostID) (uint16_t)(0x03FF & hostID) - -U_CAPI int32_t uprv_convertToPosix(uint32_t hostid, char* posixID, int32_t posixIDCapacity, UErrorCode* status); - -/* Don't call these functions directly. Use uloc_getLCID instead. */ -U_CAPI uint32_t uprv_convertToLCIDPlatform(const char* localeID, UErrorCode* status); // Leverage platform conversion if possible -U_CAPI uint32_t uprv_convertToLCID(const char* langID, const char* posixID, UErrorCode* status); - -#endif /* LOCMAP_H */ - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1996-2013, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* File locmap.h : Locale Mapping Classes +* +* +* Created by: Helena Shih +* +* Modification History: +* +* Date Name Description +* 3/11/97 aliu Added setId(). +* 4/20/99 Madhu Added T_convertToPosix() +* 09/18/00 george Removed the memory leaks. +* 08/23/01 george Convert to C +*============================================================================ +*/ + +#ifndef LOCMAP_H +#define LOCMAP_H + +#include "unicode/utypes.h" + +#define LANGUAGE_LCID(hostID) (uint16_t)(0x03FF & hostID) + +U_CAPI int32_t uprv_convertToPosix(uint32_t hostid, char* posixID, int32_t posixIDCapacity, UErrorCode* status); + +/* Don't call these functions directly. Use uloc_getLCID instead. */ +U_CAPI uint32_t uprv_convertToLCIDPlatform(const char* localeID, UErrorCode* status); // Leverage platform conversion if possible +U_CAPI uint32_t uprv_convertToLCID(const char* langID, const char* posixID, UErrorCode* status); + +#endif /* LOCMAP_H */ + diff --git a/deps/icu-small/source/common/locresdata.cpp b/deps/icu-small/source/common/locresdata.cpp index d1d9a4729f107b..60590ba6fd708d 100644 --- a/deps/icu-small/source/common/locresdata.cpp +++ b/deps/icu-small/source/common/locresdata.cpp @@ -1,220 +1,220 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1997-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: loclikely.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010feb25 -* created by: Markus W. Scherer -* -* Code for miscellaneous locale-related resource bundle data access, -* separated out from other .cpp files -* that then do not depend on resource bundle code and this data. -*/ - -#include "unicode/utypes.h" -#include "unicode/putil.h" -#include "unicode/uloc.h" -#include "unicode/ures.h" -#include "cstring.h" -#include "ulocimp.h" -#include "uresimp.h" - -/* - * Lookup a resource bundle table item with fallback on the table level. - * Regular resource bundle lookups perform fallback to parent locale bundles - * and eventually the root bundle, but only for top-level items. - * This function takes the name of a top-level table and of an item in that table - * and performs a lookup of both, falling back until a bundle contains a table - * with this item. - * - * Note: Only the opening of entire bundles falls back through the default locale - * before root. Once a bundle is open, item lookups do not go through the - * default locale because that would result in a mix of languages that is - * unpredictable to the programmer and most likely useless. - */ -U_CAPI const UChar * U_EXPORT2 -uloc_getTableStringWithFallback(const char *path, const char *locale, - const char *tableKey, const char *subTableKey, - const char *itemKey, - int32_t *pLength, - UErrorCode *pErrorCode) -{ -/* char localeBuffer[ULOC_FULLNAME_CAPACITY*4];*/ - const UChar *item=NULL; - UErrorCode errorCode; - char explicitFallbackName[ULOC_FULLNAME_CAPACITY] = {0}; - - /* - * open the bundle for the current locale - * this falls back through the locale's chain to root - */ - errorCode=U_ZERO_ERROR; - icu::LocalUResourceBundlePointer rb(ures_open(path, locale, &errorCode)); - - if(U_FAILURE(errorCode)) { - /* total failure, not even root could be opened */ - *pErrorCode=errorCode; - return NULL; - } else if(errorCode==U_USING_DEFAULT_WARNING || - (errorCode==U_USING_FALLBACK_WARNING && *pErrorCode!=U_USING_DEFAULT_WARNING) - ) { - /* set the "strongest" error code (success->fallback->default->failure) */ - *pErrorCode=errorCode; - } - - for(;;){ - icu::StackUResourceBundle table; - icu::StackUResourceBundle subTable; - ures_getByKeyWithFallback(rb.getAlias(), tableKey, table.getAlias(), &errorCode); - - if (subTableKey != NULL) { - /* - ures_getByKeyWithFallback(table.getAlias(), subTableKey, subTable.getAlias(), &errorCode); - item = ures_getStringByKeyWithFallback(subTable.getAlias(), itemKey, pLength, &errorCode); - if(U_FAILURE(errorCode)){ - *pErrorCode = errorCode; - } - - break;*/ - - ures_getByKeyWithFallback(table.getAlias(), subTableKey, table.getAlias(), &errorCode); - } - if(U_SUCCESS(errorCode)){ - item = ures_getStringByKeyWithFallback(table.getAlias(), itemKey, pLength, &errorCode); - if(U_FAILURE(errorCode)){ - const char* replacement = NULL; - *pErrorCode = errorCode; /*save the errorCode*/ - errorCode = U_ZERO_ERROR; - /* may be a deprecated code */ - if(uprv_strcmp(tableKey, "Countries")==0){ - replacement = uloc_getCurrentCountryID(itemKey); - }else if(uprv_strcmp(tableKey, "Languages")==0){ - replacement = uloc_getCurrentLanguageID(itemKey); - } - /*pointer comparison is ok since uloc_getCurrentCountryID & uloc_getCurrentLanguageID return the key itself is replacement is not found*/ - if(replacement!=NULL && itemKey != replacement){ - item = ures_getStringByKeyWithFallback(table.getAlias(), replacement, pLength, &errorCode); - if(U_SUCCESS(errorCode)){ - *pErrorCode = errorCode; - break; - } - } - }else{ - break; - } - } - - if(U_FAILURE(errorCode)){ - - /* still can't figure out ?.. try the fallback mechanism */ - int32_t len = 0; - const UChar* fallbackLocale = NULL; - *pErrorCode = errorCode; - errorCode = U_ZERO_ERROR; - - fallbackLocale = ures_getStringByKeyWithFallback(table.getAlias(), "Fallback", &len, &errorCode); - if(U_FAILURE(errorCode)){ - *pErrorCode = errorCode; - break; - } - - u_UCharsToChars(fallbackLocale, explicitFallbackName, len); - - /* guard against recursive fallback */ - if(uprv_strcmp(explicitFallbackName, locale)==0){ - *pErrorCode = U_INTERNAL_PROGRAM_ERROR; - break; - } - rb.adoptInstead(ures_open(path, explicitFallbackName, &errorCode)); - if(U_FAILURE(errorCode)){ - *pErrorCode = errorCode; - break; - } - /* succeeded in opening the fallback bundle .. continue and try to fetch the item */ - }else{ - break; - } - } - - return item; -} - -static ULayoutType -_uloc_getOrientationHelper(const char* localeId, - const char* key, - UErrorCode *status) -{ - ULayoutType result = ULOC_LAYOUT_UNKNOWN; - - if (!U_FAILURE(*status)) { - int32_t length = 0; - char localeBuffer[ULOC_FULLNAME_CAPACITY]; - - uloc_canonicalize(localeId, localeBuffer, sizeof(localeBuffer), status); - - if (!U_FAILURE(*status)) { - const UChar* const value = - uloc_getTableStringWithFallback( - NULL, - localeBuffer, - "layout", - NULL, - key, - &length, - status); - - if (!U_FAILURE(*status) && length != 0) { - switch(value[0]) - { - case 0x0062: /* 'b' */ - result = ULOC_LAYOUT_BTT; - break; - case 0x006C: /* 'l' */ - result = ULOC_LAYOUT_LTR; - break; - case 0x0072: /* 'r' */ - result = ULOC_LAYOUT_RTL; - break; - case 0x0074: /* 't' */ - result = ULOC_LAYOUT_TTB; - break; - default: - *status = U_INTERNAL_PROGRAM_ERROR; - break; - } - } - } - } - - return result; -} - -U_CAPI ULayoutType U_EXPORT2 -uloc_getCharacterOrientation(const char* localeId, - UErrorCode *status) -{ - return _uloc_getOrientationHelper(localeId, "characters", status); -} - -/** - * Get the layout line orientation for the specified locale. - * - * @param localeID locale name - * @param status Error status - * @return an enum indicating the layout orientation for lines. - */ -U_CAPI ULayoutType U_EXPORT2 -uloc_getLineOrientation(const char* localeId, - UErrorCode *status) -{ - return _uloc_getOrientationHelper(localeId, "lines", status); -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1997-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: loclikely.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010feb25 +* created by: Markus W. Scherer +* +* Code for miscellaneous locale-related resource bundle data access, +* separated out from other .cpp files +* that then do not depend on resource bundle code and this data. +*/ + +#include "unicode/utypes.h" +#include "unicode/putil.h" +#include "unicode/uloc.h" +#include "unicode/ures.h" +#include "cstring.h" +#include "ulocimp.h" +#include "uresimp.h" + +/* + * Lookup a resource bundle table item with fallback on the table level. + * Regular resource bundle lookups perform fallback to parent locale bundles + * and eventually the root bundle, but only for top-level items. + * This function takes the name of a top-level table and of an item in that table + * and performs a lookup of both, falling back until a bundle contains a table + * with this item. + * + * Note: Only the opening of entire bundles falls back through the default locale + * before root. Once a bundle is open, item lookups do not go through the + * default locale because that would result in a mix of languages that is + * unpredictable to the programmer and most likely useless. + */ +U_CAPI const char16_t * U_EXPORT2 +uloc_getTableStringWithFallback(const char *path, const char *locale, + const char *tableKey, const char *subTableKey, + const char *itemKey, + int32_t *pLength, + UErrorCode *pErrorCode) +{ +/* char localeBuffer[ULOC_FULLNAME_CAPACITY*4];*/ + const char16_t *item=nullptr; + UErrorCode errorCode; + char explicitFallbackName[ULOC_FULLNAME_CAPACITY] = {0}; + + /* + * open the bundle for the current locale + * this falls back through the locale's chain to root + */ + errorCode=U_ZERO_ERROR; + icu::LocalUResourceBundlePointer rb(ures_open(path, locale, &errorCode)); + + if(U_FAILURE(errorCode)) { + /* total failure, not even root could be opened */ + *pErrorCode=errorCode; + return nullptr; + } else if(errorCode==U_USING_DEFAULT_WARNING || + (errorCode==U_USING_FALLBACK_WARNING && *pErrorCode!=U_USING_DEFAULT_WARNING) + ) { + /* set the "strongest" error code (success->fallback->default->failure) */ + *pErrorCode=errorCode; + } + + for(;;){ + icu::StackUResourceBundle table; + icu::StackUResourceBundle subTable; + ures_getByKeyWithFallback(rb.getAlias(), tableKey, table.getAlias(), &errorCode); + + if (subTableKey != nullptr) { + /* + ures_getByKeyWithFallback(table.getAlias(), subTableKey, subTable.getAlias(), &errorCode); + item = ures_getStringByKeyWithFallback(subTable.getAlias(), itemKey, pLength, &errorCode); + if(U_FAILURE(errorCode)){ + *pErrorCode = errorCode; + } + + break;*/ + + ures_getByKeyWithFallback(table.getAlias(), subTableKey, table.getAlias(), &errorCode); + } + if(U_SUCCESS(errorCode)){ + item = ures_getStringByKeyWithFallback(table.getAlias(), itemKey, pLength, &errorCode); + if(U_FAILURE(errorCode)){ + const char* replacement = nullptr; + *pErrorCode = errorCode; /*save the errorCode*/ + errorCode = U_ZERO_ERROR; + /* may be a deprecated code */ + if(uprv_strcmp(tableKey, "Countries")==0){ + replacement = uloc_getCurrentCountryID(itemKey); + }else if(uprv_strcmp(tableKey, "Languages")==0){ + replacement = uloc_getCurrentLanguageID(itemKey); + } + /*pointer comparison is ok since uloc_getCurrentCountryID & uloc_getCurrentLanguageID return the key itself is replacement is not found*/ + if(replacement!=nullptr && itemKey != replacement){ + item = ures_getStringByKeyWithFallback(table.getAlias(), replacement, pLength, &errorCode); + if(U_SUCCESS(errorCode)){ + *pErrorCode = errorCode; + break; + } + } + }else{ + break; + } + } + + if(U_FAILURE(errorCode)){ + + /* still can't figure out ?.. try the fallback mechanism */ + int32_t len = 0; + const char16_t* fallbackLocale = nullptr; + *pErrorCode = errorCode; + errorCode = U_ZERO_ERROR; + + fallbackLocale = ures_getStringByKeyWithFallback(table.getAlias(), "Fallback", &len, &errorCode); + if(U_FAILURE(errorCode)){ + *pErrorCode = errorCode; + break; + } + + u_UCharsToChars(fallbackLocale, explicitFallbackName, len); + + /* guard against recursive fallback */ + if(uprv_strcmp(explicitFallbackName, locale)==0){ + *pErrorCode = U_INTERNAL_PROGRAM_ERROR; + break; + } + rb.adoptInstead(ures_open(path, explicitFallbackName, &errorCode)); + if(U_FAILURE(errorCode)){ + *pErrorCode = errorCode; + break; + } + /* succeeded in opening the fallback bundle .. continue and try to fetch the item */ + }else{ + break; + } + } + + return item; +} + +static ULayoutType +_uloc_getOrientationHelper(const char* localeId, + const char* key, + UErrorCode *status) +{ + ULayoutType result = ULOC_LAYOUT_UNKNOWN; + + if (!U_FAILURE(*status)) { + int32_t length = 0; + char localeBuffer[ULOC_FULLNAME_CAPACITY]; + + uloc_canonicalize(localeId, localeBuffer, sizeof(localeBuffer), status); + + if (!U_FAILURE(*status)) { + const char16_t* const value = + uloc_getTableStringWithFallback( + nullptr, + localeBuffer, + "layout", + nullptr, + key, + &length, + status); + + if (!U_FAILURE(*status) && length != 0) { + switch(value[0]) + { + case 0x0062: /* 'b' */ + result = ULOC_LAYOUT_BTT; + break; + case 0x006C: /* 'l' */ + result = ULOC_LAYOUT_LTR; + break; + case 0x0072: /* 'r' */ + result = ULOC_LAYOUT_RTL; + break; + case 0x0074: /* 't' */ + result = ULOC_LAYOUT_TTB; + break; + default: + *status = U_INTERNAL_PROGRAM_ERROR; + break; + } + } + } + } + + return result; +} + +U_CAPI ULayoutType U_EXPORT2 +uloc_getCharacterOrientation(const char* localeId, + UErrorCode *status) +{ + return _uloc_getOrientationHelper(localeId, "characters", status); +} + +/** + * Get the layout line orientation for the specified locale. + * + * @param localeID locale name + * @param status Error status + * @return an enum indicating the layout orientation for lines. + */ +U_CAPI ULayoutType U_EXPORT2 +uloc_getLineOrientation(const char* localeId, + UErrorCode *status) +{ + return _uloc_getOrientationHelper(localeId, "lines", status); +} diff --git a/deps/icu-small/source/common/locutil.cpp b/deps/icu-small/source/common/locutil.cpp index 6e2bd497f81eae..6fd0230e619d60 100644 --- a/deps/icu-small/source/common/locutil.cpp +++ b/deps/icu-small/source/common/locutil.cpp @@ -1,275 +1,275 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ******************************************************************************* - * Copyright (C) 2002-2014, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************************* - */ -#include "unicode/utypes.h" - -#if !UCONFIG_NO_SERVICE || !UCONFIG_NO_TRANSLITERATION - -#include "unicode/resbund.h" -#include "unicode/uenum.h" -#include "cmemory.h" -#include "ustrfmt.h" -#include "locutil.h" -#include "charstr.h" -#include "ucln_cmn.h" -#include "uassert.h" -#include "umutex.h" - -// see LocaleUtility::getAvailableLocaleNames -static icu::UInitOnce LocaleUtilityInitOnce {}; -static icu::Hashtable * LocaleUtility_cache = NULL; - -#define UNDERSCORE_CHAR ((UChar)0x005f) -#define AT_SIGN_CHAR ((UChar)64) -#define PERIOD_CHAR ((UChar)46) - -/* - ****************************************************************** - */ - -/** - * Release all static memory held by Locale Utility. - */ -U_CDECL_BEGIN -static UBool U_CALLCONV service_cleanup(void) { - if (LocaleUtility_cache) { - delete LocaleUtility_cache; - LocaleUtility_cache = NULL; - } - return true; -} - - -static void U_CALLCONV locale_utility_init(UErrorCode &status) { - using namespace icu; - U_ASSERT(LocaleUtility_cache == NULL); - ucln_common_registerCleanup(UCLN_COMMON_SERVICE, service_cleanup); - LocaleUtility_cache = new Hashtable(status); - if (U_FAILURE(status)) { - delete LocaleUtility_cache; - LocaleUtility_cache = NULL; - return; - } - if (LocaleUtility_cache == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - LocaleUtility_cache->setValueDeleter(uhash_deleteHashtable); -} - -U_CDECL_END - -U_NAMESPACE_BEGIN - -UnicodeString& -LocaleUtility::canonicalLocaleString(const UnicodeString* id, UnicodeString& result) -{ - if (id == NULL) { - result.setToBogus(); - } else { - // Fix case only (no other changes) up to the first '@' or '.' or - // end of string, whichever comes first. In 3.0 I changed this to - // stop at first '@' or '.'. It used to run out to the end of - // string. My fix makes the tests pass but is probably - // structurally incorrect. See below. [alan 3.0] - - // TODO: Doug, you might want to revise this... - result = *id; - int32_t i = 0; - int32_t end = result.indexOf(AT_SIGN_CHAR); - int32_t n = result.indexOf(PERIOD_CHAR); - if (n >= 0 && n < end) { - end = n; - } - if (end < 0) { - end = result.length(); - } - n = result.indexOf(UNDERSCORE_CHAR); - if (n < 0) { - n = end; - } - for (; i < n; ++i) { - UChar c = result.charAt(i); - if (c >= 0x0041 && c <= 0x005a) { - c += 0x20; - result.setCharAt(i, c); - } - } - for (n = end; i < n; ++i) { - UChar c = result.charAt(i); - if (c >= 0x0061 && c <= 0x007a) { - c -= 0x20; - result.setCharAt(i, c); - } - } - } - return result; - -#if 0 - // This code does a proper full level 2 canonicalization of id. - // It's nasty to go from UChar to char to char to UChar -- but - // that's what you have to do to use the uloc_canonicalize - // function on UnicodeStrings. - - // I ended up doing the alternate fix (see above) not for - // performance reasons, although performance will certainly be - // better, but because doing a full level 2 canonicalization - // causes some tests to fail. [alan 3.0] - - // TODO: Doug, you might want to revisit this... - result.setToBogus(); - if (id != 0) { - int32_t buflen = id->length() + 8; // space for NUL - char* buf = (char*) uprv_malloc(buflen); - char* canon = (buf == 0) ? 0 : (char*) uprv_malloc(buflen); - if (buf != 0 && canon != 0) { - U_ASSERT(id->extract(0, INT32_MAX, buf, buflen) < buflen); - UErrorCode ec = U_ZERO_ERROR; - uloc_canonicalize(buf, canon, buflen, &ec); - if (U_SUCCESS(ec)) { - result = UnicodeString(canon); - } - } - uprv_free(buf); - uprv_free(canon); - } - return result; -#endif -} - -Locale& -LocaleUtility::initLocaleFromName(const UnicodeString& id, Locale& result) -{ - enum { BUFLEN = 128 }; // larger than ever needed - - if (id.isBogus() || id.length() >= BUFLEN) { - result.setToBogus(); - } else { - /* - * We need to convert from a UnicodeString to char * in order to - * create a Locale. - * - * Problem: Locale ID strings may contain '@' which is a variant - * character and cannot be handled by invariant-character conversion. - * - * Hack: Since ICU code can handle locale IDs with multiple encodings - * of '@' (at least for EBCDIC; it's not known to be a problem for - * ASCII-based systems), - * we use regular invariant-character conversion for everything else - * and manually convert U+0040 into a compiler-char-constant '@'. - * While this compilation-time constant may not match the runtime - * encoding of '@', it should be one of the encodings which ICU - * recognizes. - * - * There should be only at most one '@' in a locale ID. - */ - char buffer[BUFLEN]; - int32_t prev, i; - prev = 0; - for(;;) { - i = id.indexOf((UChar)0x40, prev); - if(i < 0) { - // no @ between prev and the rest of the string - id.extract(prev, INT32_MAX, buffer + prev, BUFLEN - prev, US_INV); - break; // done - } else { - // normal invariant-character conversion for text between @s - id.extract(prev, i - prev, buffer + prev, BUFLEN - prev, US_INV); - // manually "convert" U+0040 at id[i] into '@' at buffer[i] - buffer[i] = '@'; - prev = i + 1; - } - } - result = Locale::createFromName(buffer); - } - return result; -} - -UnicodeString& -LocaleUtility::initNameFromLocale(const Locale& locale, UnicodeString& result) -{ - if (locale.isBogus()) { - result.setToBogus(); - } else { - result.append(UnicodeString(locale.getName(), -1, US_INV)); - } - return result; -} - -const Hashtable* -LocaleUtility::getAvailableLocaleNames(const UnicodeString& bundleID) -{ - // LocaleUtility_cache is a hash-of-hashes. The top-level keys - // are path strings ('bundleID') passed to - // ures_openAvailableLocales. The top-level values are - // second-level hashes. The second-level keys are result strings - // from ures_openAvailableLocales. The second-level values are - // garbage ((void*)1 or other random pointer). - - UErrorCode status = U_ZERO_ERROR; - umtx_initOnce(LocaleUtilityInitOnce, locale_utility_init, status); - Hashtable *cache = LocaleUtility_cache; - if (cache == NULL) { - // Catastrophic failure. - return NULL; - } - - Hashtable* htp; - umtx_lock(NULL); - htp = (Hashtable*) cache->get(bundleID); - umtx_unlock(NULL); - - if (htp == NULL) { - htp = new Hashtable(status); - if (htp && U_SUCCESS(status)) { - CharString cbundleID; - cbundleID.appendInvariantChars(bundleID, status); - const char* path = cbundleID.isEmpty() ? NULL : cbundleID.data(); - icu::LocalUEnumerationPointer uenum(ures_openAvailableLocales(path, &status)); - for (;;) { - const UChar* id = uenum_unext(uenum.getAlias(), NULL, &status); - if (id == NULL) { - break; - } - htp->put(UnicodeString(id), (void*)htp, status); - } - if (U_FAILURE(status)) { - delete htp; - return NULL; - } - umtx_lock(NULL); - Hashtable *t = static_cast(cache->get(bundleID)); - if (t != NULL) { - // Another thread raced through this code, creating the cache entry first. - // Discard ours and return theirs. - umtx_unlock(NULL); - delete htp; - htp = t; - } else { - cache->put(bundleID, (void*)htp, status); - umtx_unlock(NULL); - } - } - } - return htp; -} - -UBool -LocaleUtility::isFallbackOf(const UnicodeString& root, const UnicodeString& child) -{ - return child.indexOf(root) == 0 && - (child.length() == root.length() || - child.charAt(root.length()) == UNDERSCORE_CHAR); -} - -U_NAMESPACE_END - -/* !UCONFIG_NO_SERVICE */ -#endif - - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************************* + * Copyright (C) 2002-2014, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************************* + */ +#include "unicode/utypes.h" + +#if !UCONFIG_NO_SERVICE || !UCONFIG_NO_TRANSLITERATION + +#include "unicode/resbund.h" +#include "unicode/uenum.h" +#include "cmemory.h" +#include "ustrfmt.h" +#include "locutil.h" +#include "charstr.h" +#include "ucln_cmn.h" +#include "uassert.h" +#include "umutex.h" + +// see LocaleUtility::getAvailableLocaleNames +static icu::UInitOnce LocaleUtilityInitOnce {}; +static icu::Hashtable * LocaleUtility_cache = nullptr; + +#define UNDERSCORE_CHAR ((char16_t)0x005f) +#define AT_SIGN_CHAR ((char16_t)64) +#define PERIOD_CHAR ((char16_t)46) + +/* + ****************************************************************** + */ + +/** + * Release all static memory held by Locale Utility. + */ +U_CDECL_BEGIN +static UBool U_CALLCONV service_cleanup() { + if (LocaleUtility_cache) { + delete LocaleUtility_cache; + LocaleUtility_cache = nullptr; + } + return true; +} + + +static void U_CALLCONV locale_utility_init(UErrorCode &status) { + using namespace icu; + U_ASSERT(LocaleUtility_cache == nullptr); + ucln_common_registerCleanup(UCLN_COMMON_SERVICE, service_cleanup); + LocaleUtility_cache = new Hashtable(status); + if (U_FAILURE(status)) { + delete LocaleUtility_cache; + LocaleUtility_cache = nullptr; + return; + } + if (LocaleUtility_cache == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + LocaleUtility_cache->setValueDeleter(uhash_deleteHashtable); +} + +U_CDECL_END + +U_NAMESPACE_BEGIN + +UnicodeString& +LocaleUtility::canonicalLocaleString(const UnicodeString* id, UnicodeString& result) +{ + if (id == nullptr) { + result.setToBogus(); + } else { + // Fix case only (no other changes) up to the first '@' or '.' or + // end of string, whichever comes first. In 3.0 I changed this to + // stop at first '@' or '.'. It used to run out to the end of + // string. My fix makes the tests pass but is probably + // structurally incorrect. See below. [alan 3.0] + + // TODO: Doug, you might want to revise this... + result = *id; + int32_t i = 0; + int32_t end = result.indexOf(AT_SIGN_CHAR); + int32_t n = result.indexOf(PERIOD_CHAR); + if (n >= 0 && n < end) { + end = n; + } + if (end < 0) { + end = result.length(); + } + n = result.indexOf(UNDERSCORE_CHAR); + if (n < 0) { + n = end; + } + for (; i < n; ++i) { + char16_t c = result.charAt(i); + if (c >= 0x0041 && c <= 0x005a) { + c += 0x20; + result.setCharAt(i, c); + } + } + for (n = end; i < n; ++i) { + char16_t c = result.charAt(i); + if (c >= 0x0061 && c <= 0x007a) { + c -= 0x20; + result.setCharAt(i, c); + } + } + } + return result; + +#if 0 + // This code does a proper full level 2 canonicalization of id. + // It's nasty to go from char16_t to char to char to char16_t -- but + // that's what you have to do to use the uloc_canonicalize + // function on UnicodeStrings. + + // I ended up doing the alternate fix (see above) not for + // performance reasons, although performance will certainly be + // better, but because doing a full level 2 canonicalization + // causes some tests to fail. [alan 3.0] + + // TODO: Doug, you might want to revisit this... + result.setToBogus(); + if (id != 0) { + int32_t buflen = id->length() + 8; // space for NUL + char* buf = (char*) uprv_malloc(buflen); + char* canon = (buf == 0) ? 0 : (char*) uprv_malloc(buflen); + if (buf != 0 && canon != 0) { + U_ASSERT(id->extract(0, INT32_MAX, buf, buflen) < buflen); + UErrorCode ec = U_ZERO_ERROR; + uloc_canonicalize(buf, canon, buflen, &ec); + if (U_SUCCESS(ec)) { + result = UnicodeString(canon); + } + } + uprv_free(buf); + uprv_free(canon); + } + return result; +#endif +} + +Locale& +LocaleUtility::initLocaleFromName(const UnicodeString& id, Locale& result) +{ + enum { BUFLEN = 128 }; // larger than ever needed + + if (id.isBogus() || id.length() >= BUFLEN) { + result.setToBogus(); + } else { + /* + * We need to convert from a UnicodeString to char * in order to + * create a Locale. + * + * Problem: Locale ID strings may contain '@' which is a variant + * character and cannot be handled by invariant-character conversion. + * + * Hack: Since ICU code can handle locale IDs with multiple encodings + * of '@' (at least for EBCDIC; it's not known to be a problem for + * ASCII-based systems), + * we use regular invariant-character conversion for everything else + * and manually convert U+0040 into a compiler-char-constant '@'. + * While this compilation-time constant may not match the runtime + * encoding of '@', it should be one of the encodings which ICU + * recognizes. + * + * There should be only at most one '@' in a locale ID. + */ + char buffer[BUFLEN]; + int32_t prev, i; + prev = 0; + for(;;) { + i = id.indexOf((char16_t)0x40, prev); + if(i < 0) { + // no @ between prev and the rest of the string + id.extract(prev, INT32_MAX, buffer + prev, BUFLEN - prev, US_INV); + break; // done + } else { + // normal invariant-character conversion for text between @s + id.extract(prev, i - prev, buffer + prev, BUFLEN - prev, US_INV); + // manually "convert" U+0040 at id[i] into '@' at buffer[i] + buffer[i] = '@'; + prev = i + 1; + } + } + result = Locale::createFromName(buffer); + } + return result; +} + +UnicodeString& +LocaleUtility::initNameFromLocale(const Locale& locale, UnicodeString& result) +{ + if (locale.isBogus()) { + result.setToBogus(); + } else { + result.append(UnicodeString(locale.getName(), -1, US_INV)); + } + return result; +} + +const Hashtable* +LocaleUtility::getAvailableLocaleNames(const UnicodeString& bundleID) +{ + // LocaleUtility_cache is a hash-of-hashes. The top-level keys + // are path strings ('bundleID') passed to + // ures_openAvailableLocales. The top-level values are + // second-level hashes. The second-level keys are result strings + // from ures_openAvailableLocales. The second-level values are + // garbage ((void*)1 or other random pointer). + + UErrorCode status = U_ZERO_ERROR; + umtx_initOnce(LocaleUtilityInitOnce, locale_utility_init, status); + Hashtable *cache = LocaleUtility_cache; + if (cache == nullptr) { + // Catastrophic failure. + return nullptr; + } + + Hashtable* htp; + umtx_lock(nullptr); + htp = (Hashtable*) cache->get(bundleID); + umtx_unlock(nullptr); + + if (htp == nullptr) { + htp = new Hashtable(status); + if (htp && U_SUCCESS(status)) { + CharString cbundleID; + cbundleID.appendInvariantChars(bundleID, status); + const char* path = cbundleID.isEmpty() ? nullptr : cbundleID.data(); + icu::LocalUEnumerationPointer uenum(ures_openAvailableLocales(path, &status)); + for (;;) { + const char16_t* id = uenum_unext(uenum.getAlias(), nullptr, &status); + if (id == nullptr) { + break; + } + htp->put(UnicodeString(id), (void*)htp, status); + } + if (U_FAILURE(status)) { + delete htp; + return nullptr; + } + umtx_lock(nullptr); + Hashtable *t = static_cast(cache->get(bundleID)); + if (t != nullptr) { + // Another thread raced through this code, creating the cache entry first. + // Discard ours and return theirs. + umtx_unlock(nullptr); + delete htp; + htp = t; + } else { + cache->put(bundleID, (void*)htp, status); + umtx_unlock(nullptr); + } + } + } + return htp; +} + +UBool +LocaleUtility::isFallbackOf(const UnicodeString& root, const UnicodeString& child) +{ + return child.indexOf(root) == 0 && + (child.length() == root.length() || + child.charAt(root.length()) == UNDERSCORE_CHAR); +} + +U_NAMESPACE_END + +/* !UCONFIG_NO_SERVICE */ +#endif + + diff --git a/deps/icu-small/source/common/locutil.h b/deps/icu-small/source/common/locutil.h index 31bfffd7a5920f..cd1f28b071b0e9 100644 --- a/deps/icu-small/source/common/locutil.h +++ b/deps/icu-small/source/common/locutil.h @@ -1,39 +1,39 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** - ******************************************************************************* - * Copyright (C) 2002-2005, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - * - ******************************************************************************* - */ -#ifndef LOCUTIL_H -#define LOCUTIL_H - -#include "unicode/utypes.h" -#include "hash.h" - -#if !UCONFIG_NO_SERVICE || !UCONFIG_NO_TRANSLITERATION - - -U_NAMESPACE_BEGIN - -// temporary utility functions, till I know where to find them -// in header so tests can also access them - -class U_COMMON_API LocaleUtility { -public: - static UnicodeString& canonicalLocaleString(const UnicodeString* id, UnicodeString& result); - static Locale& initLocaleFromName(const UnicodeString& id, Locale& result); - static UnicodeString& initNameFromLocale(const Locale& locale, UnicodeString& result); - static const Hashtable* getAvailableLocaleNames(const UnicodeString& bundleID); - static UBool isFallbackOf(const UnicodeString& root, const UnicodeString& child); -}; - -U_NAMESPACE_END - - -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** + ******************************************************************************* + * Copyright (C) 2002-2005, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + * + ******************************************************************************* + */ +#ifndef LOCUTIL_H +#define LOCUTIL_H + +#include "unicode/utypes.h" +#include "hash.h" + +#if !UCONFIG_NO_SERVICE || !UCONFIG_NO_TRANSLITERATION + + +U_NAMESPACE_BEGIN + +// temporary utility functions, till I know where to find them +// in header so tests can also access them + +class U_COMMON_API LocaleUtility { +public: + static UnicodeString& canonicalLocaleString(const UnicodeString* id, UnicodeString& result); + static Locale& initLocaleFromName(const UnicodeString& id, Locale& result); + static UnicodeString& initNameFromLocale(const Locale& locale, UnicodeString& result); + static const Hashtable* getAvailableLocaleNames(const UnicodeString& bundleID); + static UBool isFallbackOf(const UnicodeString& root, const UnicodeString& child); +}; + +U_NAMESPACE_END + + +#endif + +#endif diff --git a/deps/icu-small/source/common/lsr.cpp b/deps/icu-small/source/common/lsr.cpp index 1f0b69ab0fd101..b68bb3779fa6bc 100644 --- a/deps/icu-small/source/common/lsr.cpp +++ b/deps/icu-small/source/common/lsr.cpp @@ -1,114 +1,114 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// lsr.cpp -// created: 2019may08 Markus W. Scherer - -#include "unicode/utypes.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "lsr.h" -#include "uinvchar.h" -#include "ustr_imp.h" - -U_NAMESPACE_BEGIN - -LSR::LSR(char prefix, const char *lang, const char *scr, const char *r, int32_t f, - UErrorCode &errorCode) : - language(nullptr), script(nullptr), region(r), - regionIndex(indexForRegion(region)), flags(f) { - if (U_SUCCESS(errorCode)) { - CharString langScript; - langScript.append(prefix, errorCode).append(lang, errorCode).append('\0', errorCode); - int32_t scriptOffset = langScript.length(); - langScript.append(prefix, errorCode).append(scr, errorCode); - owned = langScript.cloneData(errorCode); - if (U_SUCCESS(errorCode)) { - language = owned; - script = owned + scriptOffset; - } - } -} - -LSR::LSR(LSR &&other) U_NOEXCEPT : - language(other.language), script(other.script), region(other.region), owned(other.owned), - regionIndex(other.regionIndex), flags(other.flags), - hashCode(other.hashCode) { - if (owned != nullptr) { - other.language = other.script = ""; - other.owned = nullptr; - other.hashCode = 0; - } -} - -void LSR::deleteOwned() { - uprv_free(owned); -} - -LSR &LSR::operator=(LSR &&other) U_NOEXCEPT { - this->~LSR(); - language = other.language; - script = other.script; - region = other.region; - regionIndex = other.regionIndex; - flags = other.flags; - owned = other.owned; - hashCode = other.hashCode; - if (owned != nullptr) { - other.language = other.script = ""; - other.owned = nullptr; - other.hashCode = 0; - } - return *this; -} - -UBool LSR::isEquivalentTo(const LSR &other) const { - return - uprv_strcmp(language, other.language) == 0 && - uprv_strcmp(script, other.script) == 0 && - regionIndex == other.regionIndex && - // Compare regions if both are ill-formed (and their indexes are 0). - (regionIndex > 0 || uprv_strcmp(region, other.region) == 0); -} - -bool LSR::operator==(const LSR &other) const { - return - uprv_strcmp(language, other.language) == 0 && - uprv_strcmp(script, other.script) == 0 && - regionIndex == other.regionIndex && - // Compare regions if both are ill-formed (and their indexes are 0). - (regionIndex > 0 || uprv_strcmp(region, other.region) == 0) && - flags == other.flags; -} - -int32_t LSR::indexForRegion(const char *region) { - int32_t c = region[0]; - int32_t a = c - '0'; - if (0 <= a && a <= 9) { // digits: "419" - int32_t b = region[1] - '0'; - if (b < 0 || 9 < b) { return 0; } - c = region[2] - '0'; - if (c < 0 || 9 < c || region[3] != 0) { return 0; } - return (10 * a + b) * 10 + c + 1; - } else { // letters: "DE" - a = uprv_upperOrdinal(c); - if (a < 0 || 25 < a) { return 0; } - int32_t b = uprv_upperOrdinal(region[1]); - if (b < 0 || 25 < b || region[2] != 0) { return 0; } - return 26 * a + b + 1001; - } - return 0; -} - -LSR &LSR::setHashCode() { - if (hashCode == 0) { - uint32_t h = ustr_hashCharsN(language, static_cast(uprv_strlen(language))); - h = h * 37 + ustr_hashCharsN(script, static_cast(uprv_strlen(script))); - h = h * 37 + regionIndex; - hashCode = h * 37 + flags; - } - return *this; -} - -U_NAMESPACE_END +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// lsr.cpp +// created: 2019may08 Markus W. Scherer + +#include "unicode/utypes.h" +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "lsr.h" +#include "uinvchar.h" +#include "ustr_imp.h" + +U_NAMESPACE_BEGIN + +LSR::LSR(char prefix, const char *lang, const char *scr, const char *r, int32_t f, + UErrorCode &errorCode) : + language(nullptr), script(nullptr), region(r), + regionIndex(indexForRegion(region)), flags(f) { + if (U_SUCCESS(errorCode)) { + CharString langScript; + langScript.append(prefix, errorCode).append(lang, errorCode).append('\0', errorCode); + int32_t scriptOffset = langScript.length(); + langScript.append(prefix, errorCode).append(scr, errorCode); + owned = langScript.cloneData(errorCode); + if (U_SUCCESS(errorCode)) { + language = owned; + script = owned + scriptOffset; + } + } +} + +LSR::LSR(LSR &&other) noexcept : + language(other.language), script(other.script), region(other.region), owned(other.owned), + regionIndex(other.regionIndex), flags(other.flags), + hashCode(other.hashCode) { + if (owned != nullptr) { + other.language = other.script = ""; + other.owned = nullptr; + other.hashCode = 0; + } +} + +void LSR::deleteOwned() { + uprv_free(owned); +} + +LSR &LSR::operator=(LSR &&other) noexcept { + this->~LSR(); + language = other.language; + script = other.script; + region = other.region; + regionIndex = other.regionIndex; + flags = other.flags; + owned = other.owned; + hashCode = other.hashCode; + if (owned != nullptr) { + other.language = other.script = ""; + other.owned = nullptr; + other.hashCode = 0; + } + return *this; +} + +UBool LSR::isEquivalentTo(const LSR &other) const { + return + uprv_strcmp(language, other.language) == 0 && + uprv_strcmp(script, other.script) == 0 && + regionIndex == other.regionIndex && + // Compare regions if both are ill-formed (and their indexes are 0). + (regionIndex > 0 || uprv_strcmp(region, other.region) == 0); +} + +bool LSR::operator==(const LSR &other) const { + return + uprv_strcmp(language, other.language) == 0 && + uprv_strcmp(script, other.script) == 0 && + regionIndex == other.regionIndex && + // Compare regions if both are ill-formed (and their indexes are 0). + (regionIndex > 0 || uprv_strcmp(region, other.region) == 0) && + flags == other.flags; +} + +int32_t LSR::indexForRegion(const char *region) { + int32_t c = region[0]; + int32_t a = c - '0'; + if (0 <= a && a <= 9) { // digits: "419" + int32_t b = region[1] - '0'; + if (b < 0 || 9 < b) { return 0; } + c = region[2] - '0'; + if (c < 0 || 9 < c || region[3] != 0) { return 0; } + return (10 * a + b) * 10 + c + 1; + } else { // letters: "DE" + a = uprv_upperOrdinal(c); + if (a < 0 || 25 < a) { return 0; } + int32_t b = uprv_upperOrdinal(region[1]); + if (b < 0 || 25 < b || region[2] != 0) { return 0; } + return 26 * a + b + 1001; + } + return 0; +} + +LSR &LSR::setHashCode() { + if (hashCode == 0) { + uint32_t h = ustr_hashCharsN(language, static_cast(uprv_strlen(language))); + h = h * 37 + ustr_hashCharsN(script, static_cast(uprv_strlen(script))); + h = h * 37 + regionIndex; + hashCode = h * 37 + flags; + } + return *this; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/lsr.h b/deps/icu-small/source/common/lsr.h index af993c11d60916..19b215f7707a0a 100644 --- a/deps/icu-small/source/common/lsr.h +++ b/deps/icu-small/source/common/lsr.h @@ -1,82 +1,82 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// lsr.h -// created: 2019may08 Markus W. Scherer - -#ifndef __LSR_H__ -#define __LSR_H__ - -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "cstring.h" - -U_NAMESPACE_BEGIN - -struct LSR final : public UMemory { - static constexpr int32_t REGION_INDEX_LIMIT = 1001 + 26 * 26; - - static constexpr int32_t EXPLICIT_LSR = 7; - static constexpr int32_t EXPLICIT_LANGUAGE = 4; - static constexpr int32_t EXPLICIT_SCRIPT = 2; - static constexpr int32_t EXPLICIT_REGION = 1; - static constexpr int32_t IMPLICIT_LSR = 0; - static constexpr int32_t DONT_CARE_FLAGS = 0; - - const char *language; - const char *script; - const char *region; - char *owned = nullptr; - /** Index for region, 0 if ill-formed. @see indexForRegion */ - int32_t regionIndex = 0; - int32_t flags = 0; - /** Only set for LSRs that will be used in a hash table. */ - int32_t hashCode = 0; - - LSR() : language("und"), script(""), region("") {} - - /** Constructor which aliases all subtag pointers. */ - LSR(const char *lang, const char *scr, const char *r, int32_t f) : - language(lang), script(scr), region(r), - regionIndex(indexForRegion(region)), flags(f) {} - /** - * Constructor which prepends the prefix to the language and script, - * copies those into owned memory, and aliases the region. - */ - LSR(char prefix, const char *lang, const char *scr, const char *r, int32_t f, - UErrorCode &errorCode); - LSR(LSR &&other) U_NOEXCEPT; - LSR(const LSR &other) = delete; - inline ~LSR() { - // Pure inline code for almost all instances. - if (owned != nullptr) { - deleteOwned(); - } - } - - LSR &operator=(LSR &&other) U_NOEXCEPT; - LSR &operator=(const LSR &other) = delete; - - /** - * Returns a positive index (>0) for a well-formed region code. - * Do not rely on a particular region->index mapping; it may change. - * Returns 0 for ill-formed strings. - */ - static int32_t indexForRegion(const char *region); - - UBool isEquivalentTo(const LSR &other) const; - bool operator==(const LSR &other) const; - - inline bool operator!=(const LSR &other) const { - return !operator==(other); - } - - LSR &setHashCode(); - -private: - void deleteOwned(); -}; - -U_NAMESPACE_END - -#endif // __LSR_H__ +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// lsr.h +// created: 2019may08 Markus W. Scherer + +#ifndef __LSR_H__ +#define __LSR_H__ + +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "cstring.h" + +U_NAMESPACE_BEGIN + +struct LSR final : public UMemory { + static constexpr int32_t REGION_INDEX_LIMIT = 1001 + 26 * 26; + + static constexpr int32_t EXPLICIT_LSR = 7; + static constexpr int32_t EXPLICIT_LANGUAGE = 4; + static constexpr int32_t EXPLICIT_SCRIPT = 2; + static constexpr int32_t EXPLICIT_REGION = 1; + static constexpr int32_t IMPLICIT_LSR = 0; + static constexpr int32_t DONT_CARE_FLAGS = 0; + + const char *language; + const char *script; + const char *region; + char *owned = nullptr; + /** Index for region, 0 if ill-formed. @see indexForRegion */ + int32_t regionIndex = 0; + int32_t flags = 0; + /** Only set for LSRs that will be used in a hash table. */ + int32_t hashCode = 0; + + LSR() : language("und"), script(""), region("") {} + + /** Constructor which aliases all subtag pointers. */ + LSR(const char *lang, const char *scr, const char *r, int32_t f) : + language(lang), script(scr), region(r), + regionIndex(indexForRegion(region)), flags(f) {} + /** + * Constructor which prepends the prefix to the language and script, + * copies those into owned memory, and aliases the region. + */ + LSR(char prefix, const char *lang, const char *scr, const char *r, int32_t f, + UErrorCode &errorCode); + LSR(LSR &&other) noexcept; + LSR(const LSR &other) = delete; + inline ~LSR() { + // Pure inline code for almost all instances. + if (owned != nullptr) { + deleteOwned(); + } + } + + LSR &operator=(LSR &&other) noexcept; + LSR &operator=(const LSR &other) = delete; + + /** + * Returns a positive index (>0) for a well-formed region code. + * Do not rely on a particular region->index mapping; it may change. + * Returns 0 for ill-formed strings. + */ + static int32_t indexForRegion(const char *region); + + UBool isEquivalentTo(const LSR &other) const; + bool operator==(const LSR &other) const; + + inline bool operator!=(const LSR &other) const { + return !operator==(other); + } + + LSR &setHashCode(); + +private: + void deleteOwned(); +}; + +U_NAMESPACE_END + +#endif // __LSR_H__ diff --git a/deps/icu-small/source/common/lstmbe.cpp b/deps/icu-small/source/common/lstmbe.cpp index f6114cdfe25e19..1e66fa50043c9d 100644 --- a/deps/icu-small/source/common/lstmbe.cpp +++ b/deps/icu-small/source/common/lstmbe.cpp @@ -1,856 +1,856 @@ -// © 2021 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#include -#include - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "brkeng.h" -#include "charstr.h" -#include "cmemory.h" -#include "lstmbe.h" -#include "putilimp.h" -#include "uassert.h" -#include "ubrkimpl.h" -#include "uresimp.h" -#include "uvectr32.h" -#include "uvector.h" - -#include "unicode/brkiter.h" -#include "unicode/resbund.h" -#include "unicode/ubrk.h" -#include "unicode/uniset.h" -#include "unicode/ustring.h" -#include "unicode/utf.h" - -U_NAMESPACE_BEGIN - -// Uncomment the following #define to debug. -// #define LSTM_DEBUG 1 -// #define LSTM_VECTORIZER_DEBUG 1 - -/** - * Interface for reading 1D array. - */ -class ReadArray1D { -public: - virtual ~ReadArray1D(); - virtual int32_t d1() const = 0; - virtual float get(int32_t i) const = 0; - -#ifdef LSTM_DEBUG - void print() const { - printf("\n["); - for (int32_t i = 0; i < d1(); i++) { - printf("%0.8e ", get(i)); - if (i % 4 == 3) printf("\n"); - } - printf("]\n"); - } -#endif -}; - -ReadArray1D::~ReadArray1D() -{ -} - -/** - * Interface for reading 2D array. - */ -class ReadArray2D { -public: - virtual ~ReadArray2D(); - virtual int32_t d1() const = 0; - virtual int32_t d2() const = 0; - virtual float get(int32_t i, int32_t j) const = 0; -}; - -ReadArray2D::~ReadArray2D() -{ -} - -/** - * A class to index a float array as a 1D Array without owning the pointer or - * copy the data. - */ -class ConstArray1D : public ReadArray1D { -public: - ConstArray1D() : data_(nullptr), d1_(0) {} - - ConstArray1D(const float* data, int32_t d1) : data_(data), d1_(d1) {} - - virtual ~ConstArray1D(); - - // Init the object, the object does not own the data nor copy. - // It is designed to directly use data from memory mapped resources. - void init(const int32_t* data, int32_t d1) { - U_ASSERT(IEEE_754 == 1); - data_ = reinterpret_cast(data); - d1_ = d1; - } - - // ReadArray1D methods. - virtual int32_t d1() const override { return d1_; } - virtual float get(int32_t i) const override { - U_ASSERT(i < d1_); - return data_[i]; - } - -private: - const float* data_; - int32_t d1_; -}; - -ConstArray1D::~ConstArray1D() -{ -} - -/** - * A class to index a float array as a 2D Array without owning the pointer or - * copy the data. - */ -class ConstArray2D : public ReadArray2D { -public: - ConstArray2D() : data_(nullptr), d1_(0), d2_(0) {} - - ConstArray2D(const float* data, int32_t d1, int32_t d2) - : data_(data), d1_(d1), d2_(d2) {} - - virtual ~ConstArray2D(); - - // Init the object, the object does not own the data nor copy. - // It is designed to directly use data from memory mapped resources. - void init(const int32_t* data, int32_t d1, int32_t d2) { - U_ASSERT(IEEE_754 == 1); - data_ = reinterpret_cast(data); - d1_ = d1; - d2_ = d2; - } - - // ReadArray2D methods. - inline int32_t d1() const override { return d1_; } - inline int32_t d2() const override { return d2_; } - float get(int32_t i, int32_t j) const override { - U_ASSERT(i < d1_); - U_ASSERT(j < d2_); - return data_[i * d2_ + j]; - } - - // Expose the ith row as a ConstArray1D - inline ConstArray1D row(int32_t i) const { - U_ASSERT(i < d1_); - return ConstArray1D(data_ + i * d2_, d2_); - } - -private: - const float* data_; - int32_t d1_; - int32_t d2_; -}; - -ConstArray2D::~ConstArray2D() -{ -} - -/** - * A class to allocate data as a writable 1D array. - * This is the main class implement matrix operation. - */ -class Array1D : public ReadArray1D { -public: - Array1D() : memory_(nullptr), data_(nullptr), d1_(0) {} - Array1D(int32_t d1, UErrorCode &status) - : memory_(uprv_malloc(d1 * sizeof(float))), - data_((float*)memory_), d1_(d1) { - if (U_SUCCESS(status)) { - if (memory_ == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - clear(); - } - } - - virtual ~Array1D(); - - // A special constructor which does not own the memory but writeable - // as a slice of an array. - Array1D(float* data, int32_t d1) - : memory_(nullptr), data_(data), d1_(d1) {} - - // ReadArray1D methods. - virtual int32_t d1() const override { return d1_; } - virtual float get(int32_t i) const override { - U_ASSERT(i < d1_); - return data_[i]; - } - - // Return the index which point to the max data in the array. - inline int32_t maxIndex() const { - int32_t index = 0; - float max = data_[0]; - for (int32_t i = 1; i < d1_; i++) { - if (data_[i] > max) { - max = data_[i]; - index = i; - } - } - return index; - } - - // Slice part of the array to a new one. - inline Array1D slice(int32_t from, int32_t size) const { - U_ASSERT(from >= 0); - U_ASSERT(from < d1_); - U_ASSERT(from + size <= d1_); - return Array1D(data_ + from, size); - } - - // Add dot product of a 1D array and a 2D array into this one. - inline Array1D& addDotProduct(const ReadArray1D& a, const ReadArray2D& b) { - U_ASSERT(a.d1() == b.d1()); - U_ASSERT(b.d2() == d1()); - for (int32_t i = 0; i < d1(); i++) { - for (int32_t j = 0; j < a.d1(); j++) { - data_[i] += a.get(j) * b.get(j, i); - } - } - return *this; - } - - // Hadamard Product the values of another array of the same size into this one. - inline Array1D& hadamardProduct(const ReadArray1D& a) { - U_ASSERT(a.d1() == d1()); - for (int32_t i = 0; i < d1(); i++) { - data_[i] *= a.get(i); - } - return *this; - } - - // Add the Hadamard Product of two arrays of the same size into this one. - inline Array1D& addHadamardProduct(const ReadArray1D& a, const ReadArray1D& b) { - U_ASSERT(a.d1() == d1()); - U_ASSERT(b.d1() == d1()); - for (int32_t i = 0; i < d1(); i++) { - data_[i] += a.get(i) * b.get(i); - } - return *this; - } - - // Add the values of another array of the same size into this one. - inline Array1D& add(const ReadArray1D& a) { - U_ASSERT(a.d1() == d1()); - for (int32_t i = 0; i < d1(); i++) { - data_[i] += a.get(i); - } - return *this; - } - - // Assign the values of another array of the same size into this one. - inline Array1D& assign(const ReadArray1D& a) { - U_ASSERT(a.d1() == d1()); - for (int32_t i = 0; i < d1(); i++) { - data_[i] = a.get(i); - } - return *this; - } - - // Apply tanh to all the elements in the array. - inline Array1D& tanh() { - return tanh(*this); - } - - // Apply tanh of a and store into this array. - inline Array1D& tanh(const Array1D& a) { - U_ASSERT(a.d1() == d1()); - for (int32_t i = 0; i < d1_; i++) { - data_[i] = std::tanh(a.get(i)); - } - return *this; - } - - // Apply sigmoid to all the elements in the array. - inline Array1D& sigmoid() { - for (int32_t i = 0; i < d1_; i++) { - data_[i] = 1.0f/(1.0f + expf(-data_[i])); - } - return *this; - } - - inline Array1D& clear() { - uprv_memset(data_, 0, d1_ * sizeof(float)); - return *this; - } - -private: - void* memory_; - float* data_; - int32_t d1_; -}; - -Array1D::~Array1D() -{ - uprv_free(memory_); -} - -class Array2D : public ReadArray2D { -public: - Array2D() : memory_(nullptr), data_(nullptr), d1_(0), d2_(0) {} - Array2D(int32_t d1, int32_t d2, UErrorCode &status) - : memory_(uprv_malloc(d1 * d2 * sizeof(float))), - data_((float*)memory_), d1_(d1), d2_(d2) { - if (U_SUCCESS(status)) { - if (memory_ == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - clear(); - } - } - virtual ~Array2D(); - - // ReadArray2D methods. - virtual int32_t d1() const override { return d1_; } - virtual int32_t d2() const override { return d2_; } - virtual float get(int32_t i, int32_t j) const override { - U_ASSERT(i < d1_); - U_ASSERT(j < d2_); - return data_[i * d2_ + j]; - } - - inline Array1D row(int32_t i) const { - U_ASSERT(i < d1_); - return Array1D(data_ + i * d2_, d2_); - } - - inline Array2D& clear() { - uprv_memset(data_, 0, d1_ * d2_ * sizeof(float)); - return *this; - } - -private: - void* memory_; - float* data_; - int32_t d1_; - int32_t d2_; -}; - -Array2D::~Array2D() -{ - uprv_free(memory_); -} - -typedef enum { - BEGIN, - INSIDE, - END, - SINGLE -} LSTMClass; - -typedef enum { - UNKNOWN, - CODE_POINTS, - GRAPHEME_CLUSTER, -} EmbeddingType; - -struct LSTMData : public UMemory { - LSTMData(UResourceBundle* rb, UErrorCode &status); - ~LSTMData(); - UHashtable* fDict; - EmbeddingType fType; - const UChar* fName; - ConstArray2D fEmbedding; - ConstArray2D fForwardW; - ConstArray2D fForwardU; - ConstArray1D fForwardB; - ConstArray2D fBackwardW; - ConstArray2D fBackwardU; - ConstArray1D fBackwardB; - ConstArray2D fOutputW; - ConstArray1D fOutputB; - -private: - UResourceBundle* fBundle; -}; - -LSTMData::LSTMData(UResourceBundle* rb, UErrorCode &status) - : fDict(nullptr), fType(UNKNOWN), fName(nullptr), - fBundle(rb) -{ - if (U_FAILURE(status)) { - return; - } - if (IEEE_754 != 1) { - status = U_UNSUPPORTED_ERROR; - return; - } - LocalUResourceBundlePointer embeddings_res( - ures_getByKey(rb, "embeddings", nullptr, &status)); - int32_t embedding_size = ures_getInt(embeddings_res.getAlias(), &status); - LocalUResourceBundlePointer hunits_res( - ures_getByKey(rb, "hunits", nullptr, &status)); - if (U_FAILURE(status)) return; - int32_t hunits = ures_getInt(hunits_res.getAlias(), &status); - const UChar* type = ures_getStringByKey(rb, "type", nullptr, &status); - if (U_FAILURE(status)) return; - if (u_strCompare(type, -1, u"codepoints", -1, false) == 0) { - fType = CODE_POINTS; - } else if (u_strCompare(type, -1, u"graphclust", -1, false) == 0) { - fType = GRAPHEME_CLUSTER; - } - fName = ures_getStringByKey(rb, "model", nullptr, &status); - LocalUResourceBundlePointer dataRes(ures_getByKey(rb, "data", nullptr, &status)); - if (U_FAILURE(status)) return; - int32_t data_len = 0; - const int32_t* data = ures_getIntVector(dataRes.getAlias(), &data_len, &status); - fDict = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status); - - StackUResourceBundle stackTempBundle; - ResourceDataValue value; - ures_getValueWithFallback(rb, "dict", stackTempBundle.getAlias(), value, status); - ResourceArray stringArray = value.getArray(status); - int32_t num_index = stringArray.getSize(); - if (U_FAILURE(status)) { return; } - - // put dict into hash - int32_t stringLength; - for (int32_t idx = 0; idx < num_index; idx++) { - stringArray.getValue(idx, value); - const UChar* str = value.getString(stringLength, status); - uhash_putiAllowZero(fDict, (void*)str, idx, &status); - if (U_FAILURE(status)) return; -#ifdef LSTM_VECTORIZER_DEBUG - printf("Assign ["); - while (*str != 0x0000) { - printf("U+%04x ", *str); - str++; - } - printf("] map to %d\n", idx-1); -#endif - } - int32_t mat1_size = (num_index + 1) * embedding_size; - int32_t mat2_size = embedding_size * 4 * hunits; - int32_t mat3_size = hunits * 4 * hunits; - int32_t mat4_size = 4 * hunits; - int32_t mat5_size = mat2_size; - int32_t mat6_size = mat3_size; - int32_t mat7_size = mat4_size; - int32_t mat8_size = 2 * hunits * 4; -#if U_DEBUG - int32_t mat9_size = 4; - U_ASSERT(data_len == mat1_size + mat2_size + mat3_size + mat4_size + mat5_size + - mat6_size + mat7_size + mat8_size + mat9_size); -#endif - - fEmbedding.init(data, (num_index + 1), embedding_size); - data += mat1_size; - fForwardW.init(data, embedding_size, 4 * hunits); - data += mat2_size; - fForwardU.init(data, hunits, 4 * hunits); - data += mat3_size; - fForwardB.init(data, 4 * hunits); - data += mat4_size; - fBackwardW.init(data, embedding_size, 4 * hunits); - data += mat5_size; - fBackwardU.init(data, hunits, 4 * hunits); - data += mat6_size; - fBackwardB.init(data, 4 * hunits); - data += mat7_size; - fOutputW.init(data, 2 * hunits, 4); - data += mat8_size; - fOutputB.init(data, 4); -} - -LSTMData::~LSTMData() { - uhash_close(fDict); - ures_close(fBundle); -} - -class Vectorizer : public UMemory { -public: - Vectorizer(UHashtable* dict) : fDict(dict) {} - virtual ~Vectorizer(); - virtual void vectorize(UText *text, int32_t startPos, int32_t endPos, - UVector32 &offsets, UVector32 &indices, - UErrorCode &status) const = 0; -protected: - int32_t stringToIndex(const UChar* str) const { - UBool found = false; - int32_t ret = uhash_getiAndFound(fDict, (const void*)str, &found); - if (!found) { - ret = fDict->count; - } -#ifdef LSTM_VECTORIZER_DEBUG - printf("["); - while (*str != 0x0000) { - printf("U+%04x ", *str); - str++; - } - printf("] map to %d\n", ret); -#endif - return ret; - } - -private: - UHashtable* fDict; -}; - -Vectorizer::~Vectorizer() -{ -} - -class CodePointsVectorizer : public Vectorizer { -public: - CodePointsVectorizer(UHashtable* dict) : Vectorizer(dict) {} - virtual ~CodePointsVectorizer(); - virtual void vectorize(UText *text, int32_t startPos, int32_t endPos, - UVector32 &offsets, UVector32 &indices, - UErrorCode &status) const override; -}; - -CodePointsVectorizer::~CodePointsVectorizer() -{ -} - -void CodePointsVectorizer::vectorize( - UText *text, int32_t startPos, int32_t endPos, - UVector32 &offsets, UVector32 &indices, UErrorCode &status) const -{ - if (offsets.ensureCapacity(endPos - startPos, status) && - indices.ensureCapacity(endPos - startPos, status)) { - if (U_FAILURE(status)) return; - utext_setNativeIndex(text, startPos); - int32_t current; - UChar str[2] = {0, 0}; - while (U_SUCCESS(status) && - (current = (int32_t)utext_getNativeIndex(text)) < endPos) { - // Since the LSTMBreakEngine is currently only accept chars in BMP, - // we can ignore the possibility of hitting supplementary code - // point. - str[0] = (UChar) utext_next32(text); - U_ASSERT(!U_IS_SURROGATE(str[0])); - offsets.addElement(current, status); - indices.addElement(stringToIndex(str), status); - } - } -} - -class GraphemeClusterVectorizer : public Vectorizer { -public: - GraphemeClusterVectorizer(UHashtable* dict) - : Vectorizer(dict) - { - } - virtual ~GraphemeClusterVectorizer(); - virtual void vectorize(UText *text, int32_t startPos, int32_t endPos, - UVector32 &offsets, UVector32 &indices, - UErrorCode &status) const override; -}; - -GraphemeClusterVectorizer::~GraphemeClusterVectorizer() -{ -} - -constexpr int32_t MAX_GRAPHEME_CLSTER_LENGTH = 10; - -void GraphemeClusterVectorizer::vectorize( - UText *text, int32_t startPos, int32_t endPos, - UVector32 &offsets, UVector32 &indices, UErrorCode &status) const -{ - if (U_FAILURE(status)) return; - if (!offsets.ensureCapacity(endPos - startPos, status) || - !indices.ensureCapacity(endPos - startPos, status)) { - return; - } - if (U_FAILURE(status)) return; - LocalPointer graphemeIter(BreakIterator::createCharacterInstance(Locale(), status)); - if (U_FAILURE(status)) return; - graphemeIter->setText(text, status); - if (U_FAILURE(status)) return; - - if (startPos != 0) { - graphemeIter->preceding(startPos); - } - int32_t last = startPos; - int32_t current = startPos; - UChar str[MAX_GRAPHEME_CLSTER_LENGTH]; - while ((current = graphemeIter->next()) != BreakIterator::DONE) { - if (current >= endPos) { - break; - } - if (current > startPos) { - utext_extract(text, last, current, str, MAX_GRAPHEME_CLSTER_LENGTH, &status); - if (U_FAILURE(status)) return; - offsets.addElement(last, status); - indices.addElement(stringToIndex(str), status); - if (U_FAILURE(status)) return; - } - last = current; - } - if (U_FAILURE(status) || last >= endPos) { - return; - } - utext_extract(text, last, endPos, str, MAX_GRAPHEME_CLSTER_LENGTH, &status); - if (U_SUCCESS(status)) { - offsets.addElement(last, status); - indices.addElement(stringToIndex(str), status); - } -} - -// Computing LSTM as stated in -// https://en.wikipedia.org/wiki/Long_short-term_memory#LSTM_with_a_forget_gate -// ifco is temp array allocate outside which does not need to be -// input/output value but could avoid unnecessary memory alloc/free if passing -// in. -void compute( - int32_t hunits, - const ReadArray2D& W, const ReadArray2D& U, const ReadArray1D& b, - const ReadArray1D& x, Array1D& h, Array1D& c, - Array1D& ifco) -{ - // ifco = x * W + h * U + b - ifco.assign(b) - .addDotProduct(x, W) - .addDotProduct(h, U); - - ifco.slice(0*hunits, hunits).sigmoid(); // i: sigmod - ifco.slice(1*hunits, hunits).sigmoid(); // f: sigmoid - ifco.slice(2*hunits, hunits).tanh(); // c_: tanh - ifco.slice(3*hunits, hunits).sigmoid(); // o: sigmod - - c.hadamardProduct(ifco.slice(hunits, hunits)) - .addHadamardProduct(ifco.slice(0, hunits), ifco.slice(2*hunits, hunits)); - - h.tanh(c) - .hadamardProduct(ifco.slice(3*hunits, hunits)); -} - -// Minimum word size -static const int32_t MIN_WORD = 2; - -// Minimum number of characters for two words -static const int32_t MIN_WORD_SPAN = MIN_WORD * 2; - -int32_t -LSTMBreakEngine::divideUpDictionaryRange( UText *text, - int32_t startPos, - int32_t endPos, - UVector32 &foundBreaks, - UBool /* isPhraseBreaking */, - UErrorCode& status) const { - if (U_FAILURE(status)) return 0; - int32_t beginFoundBreakSize = foundBreaks.size(); - utext_setNativeIndex(text, startPos); - utext_moveIndex32(text, MIN_WORD_SPAN); - if (utext_getNativeIndex(text) >= endPos) { - return 0; // Not enough characters for two words - } - utext_setNativeIndex(text, startPos); - - UVector32 offsets(status); - UVector32 indices(status); - if (U_FAILURE(status)) return 0; - fVectorizer->vectorize(text, startPos, endPos, offsets, indices, status); - if (U_FAILURE(status)) return 0; - int32_t* offsetsBuf = offsets.getBuffer(); - int32_t* indicesBuf = indices.getBuffer(); - - int32_t input_seq_len = indices.size(); - int32_t hunits = fData->fForwardU.d1(); - - // ----- Begin of all the Array memory allocation needed for this function - // Allocate temp array used inside compute() - Array1D ifco(4 * hunits, status); - - Array1D c(hunits, status); - Array1D logp(4, status); - - // TODO: limit size of hBackward. If input_seq_len is too big, we could - // run out of memory. - // Backward LSTM - Array2D hBackward(input_seq_len, hunits, status); - - // Allocate fbRow and slice the internal array in two. - Array1D fbRow(2 * hunits, status); - - // ----- End of all the Array memory allocation needed for this function - if (U_FAILURE(status)) return 0; - - // To save the needed memory usage, the following is different from the - // Python or ICU4X implementation. We first perform the Backward LSTM - // and then merge the iteration of the forward LSTM and the output layer - // together because we only neetdto remember the h[t-1] for Forward LSTM. - for (int32_t i = input_seq_len - 1; i >= 0; i--) { - Array1D hRow = hBackward.row(i); - if (i != input_seq_len - 1) { - hRow.assign(hBackward.row(i+1)); - } -#ifdef LSTM_DEBUG - printf("hRow %d\n", i); - hRow.print(); - printf("indicesBuf[%d] = %d\n", i, indicesBuf[i]); - printf("fData->fEmbedding.row(indicesBuf[%d]):\n", i); - fData->fEmbedding.row(indicesBuf[i]).print(); -#endif // LSTM_DEBUG - compute(hunits, - fData->fBackwardW, fData->fBackwardU, fData->fBackwardB, - fData->fEmbedding.row(indicesBuf[i]), - hRow, c, ifco); - } - - - Array1D forwardRow = fbRow.slice(0, hunits); // point to first half of data in fbRow. - Array1D backwardRow = fbRow.slice(hunits, hunits); // point to second half of data n fbRow. - - // The following iteration merge the forward LSTM and the output layer - // together. - c.clear(); // reuse c since it is the same size. - for (int32_t i = 0; i < input_seq_len; i++) { -#ifdef LSTM_DEBUG - printf("forwardRow %d\n", i); - forwardRow.print(); -#endif // LSTM_DEBUG - // Forward LSTM - // Calculate the result into forwardRow, which point to the data in the first half - // of fbRow. - compute(hunits, - fData->fForwardW, fData->fForwardU, fData->fForwardB, - fData->fEmbedding.row(indicesBuf[i]), - forwardRow, c, ifco); - - // assign the data from hBackward.row(i) to second half of fbRowa. - backwardRow.assign(hBackward.row(i)); - - logp.assign(fData->fOutputB).addDotProduct(fbRow, fData->fOutputW); -#ifdef LSTM_DEBUG - printf("backwardRow %d\n", i); - backwardRow.print(); - printf("logp %d\n", i); - logp.print(); -#endif // LSTM_DEBUG - - // current = argmax(logp) - LSTMClass current = (LSTMClass)logp.maxIndex(); - // BIES logic. - if (current == BEGIN || current == SINGLE) { - if (i != 0) { - foundBreaks.addElement(offsetsBuf[i], status); - if (U_FAILURE(status)) return 0; - } - } - } - return foundBreaks.size() - beginFoundBreakSize; -} - -Vectorizer* createVectorizer(const LSTMData* data, UErrorCode &status) { - if (U_FAILURE(status)) { - return nullptr; - } - switch (data->fType) { - case CODE_POINTS: - return new CodePointsVectorizer(data->fDict); - break; - case GRAPHEME_CLUSTER: - return new GraphemeClusterVectorizer(data->fDict); - break; - default: - break; - } - UPRV_UNREACHABLE_EXIT; -} - -LSTMBreakEngine::LSTMBreakEngine(const LSTMData* data, const UnicodeSet& set, UErrorCode &status) - : DictionaryBreakEngine(), fData(data), fVectorizer(createVectorizer(fData, status)) -{ - if (U_FAILURE(status)) { - fData = nullptr; // If failure, we should not delete fData in destructor because the caller will do so. - return; - } - setCharacters(set); -} - -LSTMBreakEngine::~LSTMBreakEngine() { - delete fData; - delete fVectorizer; -} - -const UChar* LSTMBreakEngine::name() const { - return fData->fName; -} - -UnicodeString defaultLSTM(UScriptCode script, UErrorCode& status) { - // open root from brkitr tree. - UResourceBundle *b = ures_open(U_ICUDATA_BRKITR, "", &status); - b = ures_getByKeyWithFallback(b, "lstm", b, &status); - UnicodeString result = ures_getUnicodeStringByKey(b, uscript_getShortName(script), &status); - ures_close(b); - return result; -} - -U_CAPI const LSTMData* U_EXPORT2 CreateLSTMDataForScript(UScriptCode script, UErrorCode& status) -{ - if (script != USCRIPT_KHMER && script != USCRIPT_LAO && script != USCRIPT_MYANMAR && script != USCRIPT_THAI) { - return nullptr; - } - UnicodeString name = defaultLSTM(script, status); - if (U_FAILURE(status)) return nullptr; - CharString namebuf; - namebuf.appendInvariantChars(name, status).truncate(namebuf.lastIndexOf('.')); - - LocalUResourceBundlePointer rb( - ures_openDirect(U_ICUDATA_BRKITR, namebuf.data(), &status)); - if (U_FAILURE(status)) return nullptr; - - return CreateLSTMData(rb.orphan(), status); -} - -U_CAPI const LSTMData* U_EXPORT2 CreateLSTMData(UResourceBundle* rb, UErrorCode& status) -{ - return new LSTMData(rb, status); -} - -U_CAPI const LanguageBreakEngine* U_EXPORT2 -CreateLSTMBreakEngine(UScriptCode script, const LSTMData* data, UErrorCode& status) -{ - UnicodeString unicodeSetString; - switch(script) { - case USCRIPT_THAI: - unicodeSetString = UnicodeString(u"[[:Thai:]&[:LineBreak=SA:]]"); - break; - case USCRIPT_MYANMAR: - unicodeSetString = UnicodeString(u"[[:Mymr:]&[:LineBreak=SA:]]"); - break; - default: - delete data; - return nullptr; - } - UnicodeSet unicodeSet; - unicodeSet.applyPattern(unicodeSetString, status); - const LanguageBreakEngine* engine = new LSTMBreakEngine(data, unicodeSet, status); - if (U_FAILURE(status) || engine == nullptr) { - if (engine != nullptr) { - delete engine; - } else { - status = U_MEMORY_ALLOCATION_ERROR; - } - return nullptr; - } - return engine; -} - -U_CAPI void U_EXPORT2 DeleteLSTMData(const LSTMData* data) -{ - delete data; -} - -U_CAPI const UChar* U_EXPORT2 LSTMDataName(const LSTMData* data) -{ - return data->fName; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ +// © 2021 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include +#include + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "brkeng.h" +#include "charstr.h" +#include "cmemory.h" +#include "lstmbe.h" +#include "putilimp.h" +#include "uassert.h" +#include "ubrkimpl.h" +#include "uresimp.h" +#include "uvectr32.h" +#include "uvector.h" + +#include "unicode/brkiter.h" +#include "unicode/resbund.h" +#include "unicode/ubrk.h" +#include "unicode/uniset.h" +#include "unicode/ustring.h" +#include "unicode/utf.h" + +U_NAMESPACE_BEGIN + +// Uncomment the following #define to debug. +// #define LSTM_DEBUG 1 +// #define LSTM_VECTORIZER_DEBUG 1 + +/** + * Interface for reading 1D array. + */ +class ReadArray1D { +public: + virtual ~ReadArray1D(); + virtual int32_t d1() const = 0; + virtual float get(int32_t i) const = 0; + +#ifdef LSTM_DEBUG + void print() const { + printf("\n["); + for (int32_t i = 0; i < d1(); i++) { + printf("%0.8e ", get(i)); + if (i % 4 == 3) printf("\n"); + } + printf("]\n"); + } +#endif +}; + +ReadArray1D::~ReadArray1D() +{ +} + +/** + * Interface for reading 2D array. + */ +class ReadArray2D { +public: + virtual ~ReadArray2D(); + virtual int32_t d1() const = 0; + virtual int32_t d2() const = 0; + virtual float get(int32_t i, int32_t j) const = 0; +}; + +ReadArray2D::~ReadArray2D() +{ +} + +/** + * A class to index a float array as a 1D Array without owning the pointer or + * copy the data. + */ +class ConstArray1D : public ReadArray1D { +public: + ConstArray1D() : data_(nullptr), d1_(0) {} + + ConstArray1D(const float* data, int32_t d1) : data_(data), d1_(d1) {} + + virtual ~ConstArray1D(); + + // Init the object, the object does not own the data nor copy. + // It is designed to directly use data from memory mapped resources. + void init(const int32_t* data, int32_t d1) { + U_ASSERT(IEEE_754 == 1); + data_ = reinterpret_cast(data); + d1_ = d1; + } + + // ReadArray1D methods. + virtual int32_t d1() const override { return d1_; } + virtual float get(int32_t i) const override { + U_ASSERT(i < d1_); + return data_[i]; + } + +private: + const float* data_; + int32_t d1_; +}; + +ConstArray1D::~ConstArray1D() +{ +} + +/** + * A class to index a float array as a 2D Array without owning the pointer or + * copy the data. + */ +class ConstArray2D : public ReadArray2D { +public: + ConstArray2D() : data_(nullptr), d1_(0), d2_(0) {} + + ConstArray2D(const float* data, int32_t d1, int32_t d2) + : data_(data), d1_(d1), d2_(d2) {} + + virtual ~ConstArray2D(); + + // Init the object, the object does not own the data nor copy. + // It is designed to directly use data from memory mapped resources. + void init(const int32_t* data, int32_t d1, int32_t d2) { + U_ASSERT(IEEE_754 == 1); + data_ = reinterpret_cast(data); + d1_ = d1; + d2_ = d2; + } + + // ReadArray2D methods. + inline int32_t d1() const override { return d1_; } + inline int32_t d2() const override { return d2_; } + float get(int32_t i, int32_t j) const override { + U_ASSERT(i < d1_); + U_ASSERT(j < d2_); + return data_[i * d2_ + j]; + } + + // Expose the ith row as a ConstArray1D + inline ConstArray1D row(int32_t i) const { + U_ASSERT(i < d1_); + return ConstArray1D(data_ + i * d2_, d2_); + } + +private: + const float* data_; + int32_t d1_; + int32_t d2_; +}; + +ConstArray2D::~ConstArray2D() +{ +} + +/** + * A class to allocate data as a writable 1D array. + * This is the main class implement matrix operation. + */ +class Array1D : public ReadArray1D { +public: + Array1D() : memory_(nullptr), data_(nullptr), d1_(0) {} + Array1D(int32_t d1, UErrorCode &status) + : memory_(uprv_malloc(d1 * sizeof(float))), + data_((float*)memory_), d1_(d1) { + if (U_SUCCESS(status)) { + if (memory_ == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + clear(); + } + } + + virtual ~Array1D(); + + // A special constructor which does not own the memory but writeable + // as a slice of an array. + Array1D(float* data, int32_t d1) + : memory_(nullptr), data_(data), d1_(d1) {} + + // ReadArray1D methods. + virtual int32_t d1() const override { return d1_; } + virtual float get(int32_t i) const override { + U_ASSERT(i < d1_); + return data_[i]; + } + + // Return the index which point to the max data in the array. + inline int32_t maxIndex() const { + int32_t index = 0; + float max = data_[0]; + for (int32_t i = 1; i < d1_; i++) { + if (data_[i] > max) { + max = data_[i]; + index = i; + } + } + return index; + } + + // Slice part of the array to a new one. + inline Array1D slice(int32_t from, int32_t size) const { + U_ASSERT(from >= 0); + U_ASSERT(from < d1_); + U_ASSERT(from + size <= d1_); + return Array1D(data_ + from, size); + } + + // Add dot product of a 1D array and a 2D array into this one. + inline Array1D& addDotProduct(const ReadArray1D& a, const ReadArray2D& b) { + U_ASSERT(a.d1() == b.d1()); + U_ASSERT(b.d2() == d1()); + for (int32_t i = 0; i < d1(); i++) { + for (int32_t j = 0; j < a.d1(); j++) { + data_[i] += a.get(j) * b.get(j, i); + } + } + return *this; + } + + // Hadamard Product the values of another array of the same size into this one. + inline Array1D& hadamardProduct(const ReadArray1D& a) { + U_ASSERT(a.d1() == d1()); + for (int32_t i = 0; i < d1(); i++) { + data_[i] *= a.get(i); + } + return *this; + } + + // Add the Hadamard Product of two arrays of the same size into this one. + inline Array1D& addHadamardProduct(const ReadArray1D& a, const ReadArray1D& b) { + U_ASSERT(a.d1() == d1()); + U_ASSERT(b.d1() == d1()); + for (int32_t i = 0; i < d1(); i++) { + data_[i] += a.get(i) * b.get(i); + } + return *this; + } + + // Add the values of another array of the same size into this one. + inline Array1D& add(const ReadArray1D& a) { + U_ASSERT(a.d1() == d1()); + for (int32_t i = 0; i < d1(); i++) { + data_[i] += a.get(i); + } + return *this; + } + + // Assign the values of another array of the same size into this one. + inline Array1D& assign(const ReadArray1D& a) { + U_ASSERT(a.d1() == d1()); + for (int32_t i = 0; i < d1(); i++) { + data_[i] = a.get(i); + } + return *this; + } + + // Apply tanh to all the elements in the array. + inline Array1D& tanh() { + return tanh(*this); + } + + // Apply tanh of a and store into this array. + inline Array1D& tanh(const Array1D& a) { + U_ASSERT(a.d1() == d1()); + for (int32_t i = 0; i < d1_; i++) { + data_[i] = std::tanh(a.get(i)); + } + return *this; + } + + // Apply sigmoid to all the elements in the array. + inline Array1D& sigmoid() { + for (int32_t i = 0; i < d1_; i++) { + data_[i] = 1.0f/(1.0f + expf(-data_[i])); + } + return *this; + } + + inline Array1D& clear() { + uprv_memset(data_, 0, d1_ * sizeof(float)); + return *this; + } + +private: + void* memory_; + float* data_; + int32_t d1_; +}; + +Array1D::~Array1D() +{ + uprv_free(memory_); +} + +class Array2D : public ReadArray2D { +public: + Array2D() : memory_(nullptr), data_(nullptr), d1_(0), d2_(0) {} + Array2D(int32_t d1, int32_t d2, UErrorCode &status) + : memory_(uprv_malloc(d1 * d2 * sizeof(float))), + data_((float*)memory_), d1_(d1), d2_(d2) { + if (U_SUCCESS(status)) { + if (memory_ == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + clear(); + } + } + virtual ~Array2D(); + + // ReadArray2D methods. + virtual int32_t d1() const override { return d1_; } + virtual int32_t d2() const override { return d2_; } + virtual float get(int32_t i, int32_t j) const override { + U_ASSERT(i < d1_); + U_ASSERT(j < d2_); + return data_[i * d2_ + j]; + } + + inline Array1D row(int32_t i) const { + U_ASSERT(i < d1_); + return Array1D(data_ + i * d2_, d2_); + } + + inline Array2D& clear() { + uprv_memset(data_, 0, d1_ * d2_ * sizeof(float)); + return *this; + } + +private: + void* memory_; + float* data_; + int32_t d1_; + int32_t d2_; +}; + +Array2D::~Array2D() +{ + uprv_free(memory_); +} + +typedef enum { + BEGIN, + INSIDE, + END, + SINGLE +} LSTMClass; + +typedef enum { + UNKNOWN, + CODE_POINTS, + GRAPHEME_CLUSTER, +} EmbeddingType; + +struct LSTMData : public UMemory { + LSTMData(UResourceBundle* rb, UErrorCode &status); + ~LSTMData(); + UHashtable* fDict; + EmbeddingType fType; + const char16_t* fName; + ConstArray2D fEmbedding; + ConstArray2D fForwardW; + ConstArray2D fForwardU; + ConstArray1D fForwardB; + ConstArray2D fBackwardW; + ConstArray2D fBackwardU; + ConstArray1D fBackwardB; + ConstArray2D fOutputW; + ConstArray1D fOutputB; + +private: + UResourceBundle* fBundle; +}; + +LSTMData::LSTMData(UResourceBundle* rb, UErrorCode &status) + : fDict(nullptr), fType(UNKNOWN), fName(nullptr), + fBundle(rb) +{ + if (U_FAILURE(status)) { + return; + } + if (IEEE_754 != 1) { + status = U_UNSUPPORTED_ERROR; + return; + } + LocalUResourceBundlePointer embeddings_res( + ures_getByKey(rb, "embeddings", nullptr, &status)); + int32_t embedding_size = ures_getInt(embeddings_res.getAlias(), &status); + LocalUResourceBundlePointer hunits_res( + ures_getByKey(rb, "hunits", nullptr, &status)); + if (U_FAILURE(status)) return; + int32_t hunits = ures_getInt(hunits_res.getAlias(), &status); + const char16_t* type = ures_getStringByKey(rb, "type", nullptr, &status); + if (U_FAILURE(status)) return; + if (u_strCompare(type, -1, u"codepoints", -1, false) == 0) { + fType = CODE_POINTS; + } else if (u_strCompare(type, -1, u"graphclust", -1, false) == 0) { + fType = GRAPHEME_CLUSTER; + } + fName = ures_getStringByKey(rb, "model", nullptr, &status); + LocalUResourceBundlePointer dataRes(ures_getByKey(rb, "data", nullptr, &status)); + if (U_FAILURE(status)) return; + int32_t data_len = 0; + const int32_t* data = ures_getIntVector(dataRes.getAlias(), &data_len, &status); + fDict = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status); + + StackUResourceBundle stackTempBundle; + ResourceDataValue value; + ures_getValueWithFallback(rb, "dict", stackTempBundle.getAlias(), value, status); + ResourceArray stringArray = value.getArray(status); + int32_t num_index = stringArray.getSize(); + if (U_FAILURE(status)) { return; } + + // put dict into hash + int32_t stringLength; + for (int32_t idx = 0; idx < num_index; idx++) { + stringArray.getValue(idx, value); + const char16_t* str = value.getString(stringLength, status); + uhash_putiAllowZero(fDict, (void*)str, idx, &status); + if (U_FAILURE(status)) return; +#ifdef LSTM_VECTORIZER_DEBUG + printf("Assign ["); + while (*str != 0x0000) { + printf("U+%04x ", *str); + str++; + } + printf("] map to %d\n", idx-1); +#endif + } + int32_t mat1_size = (num_index + 1) * embedding_size; + int32_t mat2_size = embedding_size * 4 * hunits; + int32_t mat3_size = hunits * 4 * hunits; + int32_t mat4_size = 4 * hunits; + int32_t mat5_size = mat2_size; + int32_t mat6_size = mat3_size; + int32_t mat7_size = mat4_size; + int32_t mat8_size = 2 * hunits * 4; +#if U_DEBUG + int32_t mat9_size = 4; + U_ASSERT(data_len == mat1_size + mat2_size + mat3_size + mat4_size + mat5_size + + mat6_size + mat7_size + mat8_size + mat9_size); +#endif + + fEmbedding.init(data, (num_index + 1), embedding_size); + data += mat1_size; + fForwardW.init(data, embedding_size, 4 * hunits); + data += mat2_size; + fForwardU.init(data, hunits, 4 * hunits); + data += mat3_size; + fForwardB.init(data, 4 * hunits); + data += mat4_size; + fBackwardW.init(data, embedding_size, 4 * hunits); + data += mat5_size; + fBackwardU.init(data, hunits, 4 * hunits); + data += mat6_size; + fBackwardB.init(data, 4 * hunits); + data += mat7_size; + fOutputW.init(data, 2 * hunits, 4); + data += mat8_size; + fOutputB.init(data, 4); +} + +LSTMData::~LSTMData() { + uhash_close(fDict); + ures_close(fBundle); +} + +class Vectorizer : public UMemory { +public: + Vectorizer(UHashtable* dict) : fDict(dict) {} + virtual ~Vectorizer(); + virtual void vectorize(UText *text, int32_t startPos, int32_t endPos, + UVector32 &offsets, UVector32 &indices, + UErrorCode &status) const = 0; +protected: + int32_t stringToIndex(const char16_t* str) const { + UBool found = false; + int32_t ret = uhash_getiAndFound(fDict, (const void*)str, &found); + if (!found) { + ret = fDict->count; + } +#ifdef LSTM_VECTORIZER_DEBUG + printf("["); + while (*str != 0x0000) { + printf("U+%04x ", *str); + str++; + } + printf("] map to %d\n", ret); +#endif + return ret; + } + +private: + UHashtable* fDict; +}; + +Vectorizer::~Vectorizer() +{ +} + +class CodePointsVectorizer : public Vectorizer { +public: + CodePointsVectorizer(UHashtable* dict) : Vectorizer(dict) {} + virtual ~CodePointsVectorizer(); + virtual void vectorize(UText *text, int32_t startPos, int32_t endPos, + UVector32 &offsets, UVector32 &indices, + UErrorCode &status) const override; +}; + +CodePointsVectorizer::~CodePointsVectorizer() +{ +} + +void CodePointsVectorizer::vectorize( + UText *text, int32_t startPos, int32_t endPos, + UVector32 &offsets, UVector32 &indices, UErrorCode &status) const +{ + if (offsets.ensureCapacity(endPos - startPos, status) && + indices.ensureCapacity(endPos - startPos, status)) { + if (U_FAILURE(status)) return; + utext_setNativeIndex(text, startPos); + int32_t current; + char16_t str[2] = {0, 0}; + while (U_SUCCESS(status) && + (current = (int32_t)utext_getNativeIndex(text)) < endPos) { + // Since the LSTMBreakEngine is currently only accept chars in BMP, + // we can ignore the possibility of hitting supplementary code + // point. + str[0] = (char16_t) utext_next32(text); + U_ASSERT(!U_IS_SURROGATE(str[0])); + offsets.addElement(current, status); + indices.addElement(stringToIndex(str), status); + } + } +} + +class GraphemeClusterVectorizer : public Vectorizer { +public: + GraphemeClusterVectorizer(UHashtable* dict) + : Vectorizer(dict) + { + } + virtual ~GraphemeClusterVectorizer(); + virtual void vectorize(UText *text, int32_t startPos, int32_t endPos, + UVector32 &offsets, UVector32 &indices, + UErrorCode &status) const override; +}; + +GraphemeClusterVectorizer::~GraphemeClusterVectorizer() +{ +} + +constexpr int32_t MAX_GRAPHEME_CLSTER_LENGTH = 10; + +void GraphemeClusterVectorizer::vectorize( + UText *text, int32_t startPos, int32_t endPos, + UVector32 &offsets, UVector32 &indices, UErrorCode &status) const +{ + if (U_FAILURE(status)) return; + if (!offsets.ensureCapacity(endPos - startPos, status) || + !indices.ensureCapacity(endPos - startPos, status)) { + return; + } + if (U_FAILURE(status)) return; + LocalPointer graphemeIter(BreakIterator::createCharacterInstance(Locale(), status)); + if (U_FAILURE(status)) return; + graphemeIter->setText(text, status); + if (U_FAILURE(status)) return; + + if (startPos != 0) { + graphemeIter->preceding(startPos); + } + int32_t last = startPos; + int32_t current = startPos; + char16_t str[MAX_GRAPHEME_CLSTER_LENGTH]; + while ((current = graphemeIter->next()) != BreakIterator::DONE) { + if (current >= endPos) { + break; + } + if (current > startPos) { + utext_extract(text, last, current, str, MAX_GRAPHEME_CLSTER_LENGTH, &status); + if (U_FAILURE(status)) return; + offsets.addElement(last, status); + indices.addElement(stringToIndex(str), status); + if (U_FAILURE(status)) return; + } + last = current; + } + if (U_FAILURE(status) || last >= endPos) { + return; + } + utext_extract(text, last, endPos, str, MAX_GRAPHEME_CLSTER_LENGTH, &status); + if (U_SUCCESS(status)) { + offsets.addElement(last, status); + indices.addElement(stringToIndex(str), status); + } +} + +// Computing LSTM as stated in +// https://en.wikipedia.org/wiki/Long_short-term_memory#LSTM_with_a_forget_gate +// ifco is temp array allocate outside which does not need to be +// input/output value but could avoid unnecessary memory alloc/free if passing +// in. +void compute( + int32_t hunits, + const ReadArray2D& W, const ReadArray2D& U, const ReadArray1D& b, + const ReadArray1D& x, Array1D& h, Array1D& c, + Array1D& ifco) +{ + // ifco = x * W + h * U + b + ifco.assign(b) + .addDotProduct(x, W) + .addDotProduct(h, U); + + ifco.slice(0*hunits, hunits).sigmoid(); // i: sigmod + ifco.slice(1*hunits, hunits).sigmoid(); // f: sigmoid + ifco.slice(2*hunits, hunits).tanh(); // c_: tanh + ifco.slice(3*hunits, hunits).sigmoid(); // o: sigmod + + c.hadamardProduct(ifco.slice(hunits, hunits)) + .addHadamardProduct(ifco.slice(0, hunits), ifco.slice(2*hunits, hunits)); + + h.tanh(c) + .hadamardProduct(ifco.slice(3*hunits, hunits)); +} + +// Minimum word size +static const int32_t MIN_WORD = 2; + +// Minimum number of characters for two words +static const int32_t MIN_WORD_SPAN = MIN_WORD * 2; + +int32_t +LSTMBreakEngine::divideUpDictionaryRange( UText *text, + int32_t startPos, + int32_t endPos, + UVector32 &foundBreaks, + UBool /* isPhraseBreaking */, + UErrorCode& status) const { + if (U_FAILURE(status)) return 0; + int32_t beginFoundBreakSize = foundBreaks.size(); + utext_setNativeIndex(text, startPos); + utext_moveIndex32(text, MIN_WORD_SPAN); + if (utext_getNativeIndex(text) >= endPos) { + return 0; // Not enough characters for two words + } + utext_setNativeIndex(text, startPos); + + UVector32 offsets(status); + UVector32 indices(status); + if (U_FAILURE(status)) return 0; + fVectorizer->vectorize(text, startPos, endPos, offsets, indices, status); + if (U_FAILURE(status)) return 0; + int32_t* offsetsBuf = offsets.getBuffer(); + int32_t* indicesBuf = indices.getBuffer(); + + int32_t input_seq_len = indices.size(); + int32_t hunits = fData->fForwardU.d1(); + + // ----- Begin of all the Array memory allocation needed for this function + // Allocate temp array used inside compute() + Array1D ifco(4 * hunits, status); + + Array1D c(hunits, status); + Array1D logp(4, status); + + // TODO: limit size of hBackward. If input_seq_len is too big, we could + // run out of memory. + // Backward LSTM + Array2D hBackward(input_seq_len, hunits, status); + + // Allocate fbRow and slice the internal array in two. + Array1D fbRow(2 * hunits, status); + + // ----- End of all the Array memory allocation needed for this function + if (U_FAILURE(status)) return 0; + + // To save the needed memory usage, the following is different from the + // Python or ICU4X implementation. We first perform the Backward LSTM + // and then merge the iteration of the forward LSTM and the output layer + // together because we only neetdto remember the h[t-1] for Forward LSTM. + for (int32_t i = input_seq_len - 1; i >= 0; i--) { + Array1D hRow = hBackward.row(i); + if (i != input_seq_len - 1) { + hRow.assign(hBackward.row(i+1)); + } +#ifdef LSTM_DEBUG + printf("hRow %d\n", i); + hRow.print(); + printf("indicesBuf[%d] = %d\n", i, indicesBuf[i]); + printf("fData->fEmbedding.row(indicesBuf[%d]):\n", i); + fData->fEmbedding.row(indicesBuf[i]).print(); +#endif // LSTM_DEBUG + compute(hunits, + fData->fBackwardW, fData->fBackwardU, fData->fBackwardB, + fData->fEmbedding.row(indicesBuf[i]), + hRow, c, ifco); + } + + + Array1D forwardRow = fbRow.slice(0, hunits); // point to first half of data in fbRow. + Array1D backwardRow = fbRow.slice(hunits, hunits); // point to second half of data n fbRow. + + // The following iteration merge the forward LSTM and the output layer + // together. + c.clear(); // reuse c since it is the same size. + for (int32_t i = 0; i < input_seq_len; i++) { +#ifdef LSTM_DEBUG + printf("forwardRow %d\n", i); + forwardRow.print(); +#endif // LSTM_DEBUG + // Forward LSTM + // Calculate the result into forwardRow, which point to the data in the first half + // of fbRow. + compute(hunits, + fData->fForwardW, fData->fForwardU, fData->fForwardB, + fData->fEmbedding.row(indicesBuf[i]), + forwardRow, c, ifco); + + // assign the data from hBackward.row(i) to second half of fbRowa. + backwardRow.assign(hBackward.row(i)); + + logp.assign(fData->fOutputB).addDotProduct(fbRow, fData->fOutputW); +#ifdef LSTM_DEBUG + printf("backwardRow %d\n", i); + backwardRow.print(); + printf("logp %d\n", i); + logp.print(); +#endif // LSTM_DEBUG + + // current = argmax(logp) + LSTMClass current = (LSTMClass)logp.maxIndex(); + // BIES logic. + if (current == BEGIN || current == SINGLE) { + if (i != 0) { + foundBreaks.addElement(offsetsBuf[i], status); + if (U_FAILURE(status)) return 0; + } + } + } + return foundBreaks.size() - beginFoundBreakSize; +} + +Vectorizer* createVectorizer(const LSTMData* data, UErrorCode &status) { + if (U_FAILURE(status)) { + return nullptr; + } + switch (data->fType) { + case CODE_POINTS: + return new CodePointsVectorizer(data->fDict); + break; + case GRAPHEME_CLUSTER: + return new GraphemeClusterVectorizer(data->fDict); + break; + default: + break; + } + UPRV_UNREACHABLE_EXIT; +} + +LSTMBreakEngine::LSTMBreakEngine(const LSTMData* data, const UnicodeSet& set, UErrorCode &status) + : DictionaryBreakEngine(), fData(data), fVectorizer(createVectorizer(fData, status)) +{ + if (U_FAILURE(status)) { + fData = nullptr; // If failure, we should not delete fData in destructor because the caller will do so. + return; + } + setCharacters(set); +} + +LSTMBreakEngine::~LSTMBreakEngine() { + delete fData; + delete fVectorizer; +} + +const char16_t* LSTMBreakEngine::name() const { + return fData->fName; +} + +UnicodeString defaultLSTM(UScriptCode script, UErrorCode& status) { + // open root from brkitr tree. + UResourceBundle *b = ures_open(U_ICUDATA_BRKITR, "", &status); + b = ures_getByKeyWithFallback(b, "lstm", b, &status); + UnicodeString result = ures_getUnicodeStringByKey(b, uscript_getShortName(script), &status); + ures_close(b); + return result; +} + +U_CAPI const LSTMData* U_EXPORT2 CreateLSTMDataForScript(UScriptCode script, UErrorCode& status) +{ + if (script != USCRIPT_KHMER && script != USCRIPT_LAO && script != USCRIPT_MYANMAR && script != USCRIPT_THAI) { + return nullptr; + } + UnicodeString name = defaultLSTM(script, status); + if (U_FAILURE(status)) return nullptr; + CharString namebuf; + namebuf.appendInvariantChars(name, status).truncate(namebuf.lastIndexOf('.')); + + LocalUResourceBundlePointer rb( + ures_openDirect(U_ICUDATA_BRKITR, namebuf.data(), &status)); + if (U_FAILURE(status)) return nullptr; + + return CreateLSTMData(rb.orphan(), status); +} + +U_CAPI const LSTMData* U_EXPORT2 CreateLSTMData(UResourceBundle* rb, UErrorCode& status) +{ + return new LSTMData(rb, status); +} + +U_CAPI const LanguageBreakEngine* U_EXPORT2 +CreateLSTMBreakEngine(UScriptCode script, const LSTMData* data, UErrorCode& status) +{ + UnicodeString unicodeSetString; + switch(script) { + case USCRIPT_THAI: + unicodeSetString = UnicodeString(u"[[:Thai:]&[:LineBreak=SA:]]"); + break; + case USCRIPT_MYANMAR: + unicodeSetString = UnicodeString(u"[[:Mymr:]&[:LineBreak=SA:]]"); + break; + default: + delete data; + return nullptr; + } + UnicodeSet unicodeSet; + unicodeSet.applyPattern(unicodeSetString, status); + const LanguageBreakEngine* engine = new LSTMBreakEngine(data, unicodeSet, status); + if (U_FAILURE(status) || engine == nullptr) { + if (engine != nullptr) { + delete engine; + } else { + status = U_MEMORY_ALLOCATION_ERROR; + } + return nullptr; + } + return engine; +} + +U_CAPI void U_EXPORT2 DeleteLSTMData(const LSTMData* data) +{ + delete data; +} + +U_CAPI const char16_t* U_EXPORT2 LSTMDataName(const LSTMData* data) +{ + return data->fName; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ diff --git a/deps/icu-small/source/common/lstmbe.h b/deps/icu-small/source/common/lstmbe.h index ffdf805eca265d..b6eb89e4b59df2 100644 --- a/deps/icu-small/source/common/lstmbe.h +++ b/deps/icu-small/source/common/lstmbe.h @@ -1,88 +1,88 @@ -// © 2021 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#ifndef LSTMBE_H -#define LSTMBE_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/uniset.h" -#include "unicode/ures.h" -#include "unicode/utext.h" -#include "unicode/utypes.h" - -#include "brkeng.h" -#include "dictbe.h" -#include "uvectr32.h" - -U_NAMESPACE_BEGIN - -class Vectorizer; -struct LSTMData; - -/******************************************************************* - * LSTMBreakEngine - */ - -/** - *

LSTMBreakEngine is a kind of DictionaryBreakEngine that uses a - * LSTM to determine language-specific breaks.

- * - *

After it is constructed a LSTMBreakEngine may be shared between - * threads without synchronization.

- */ -class LSTMBreakEngine : public DictionaryBreakEngine { -public: - /** - *

Constructor.

- */ - LSTMBreakEngine(const LSTMData* data, const UnicodeSet& set, UErrorCode &status); - - /** - *

Virtual destructor.

- */ - virtual ~LSTMBreakEngine(); - - virtual const UChar* name() const; - -protected: - /** - *

Divide up a range of known dictionary characters handled by this break engine.

- * - * @param text A UText representing the text - * @param rangeStart The start of the range of dictionary characters - * @param rangeEnd The end of the range of dictionary characters - * @param foundBreaks Output of C array of int32_t break positions, or 0 - * @param status Information on any errors encountered. - * @return The number of breaks found - */ - virtual int32_t divideUpDictionaryRange(UText *text, - int32_t rangeStart, - int32_t rangeEnd, - UVector32 &foundBreaks, - UBool isPhraseBreaking, - UErrorCode& status) const override; -private: - const LSTMData* fData; - const Vectorizer* fVectorizer; -}; - -U_CAPI const LanguageBreakEngine* U_EXPORT2 CreateLSTMBreakEngine( - UScriptCode script, const LSTMData* data, UErrorCode& status); - -U_CAPI const LSTMData* U_EXPORT2 CreateLSTMData( - UResourceBundle* rb, UErrorCode& status); - -U_CAPI const LSTMData* U_EXPORT2 CreateLSTMDataForScript( - UScriptCode script, UErrorCode& status); - -U_CAPI void U_EXPORT2 DeleteLSTMData(const LSTMData* data); -U_CAPI const UChar* U_EXPORT2 LSTMDataName(const LSTMData* data); - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ - -#endif /* LSTMBE_H */ +// © 2021 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#ifndef LSTMBE_H +#define LSTMBE_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/uniset.h" +#include "unicode/ures.h" +#include "unicode/utext.h" +#include "unicode/utypes.h" + +#include "brkeng.h" +#include "dictbe.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +class Vectorizer; +struct LSTMData; + +/******************************************************************* + * LSTMBreakEngine + */ + +/** + *

LSTMBreakEngine is a kind of DictionaryBreakEngine that uses a + * LSTM to determine language-specific breaks.

+ * + *

After it is constructed a LSTMBreakEngine may be shared between + * threads without synchronization.

+ */ +class LSTMBreakEngine : public DictionaryBreakEngine { +public: + /** + *

Constructor.

+ */ + LSTMBreakEngine(const LSTMData* data, const UnicodeSet& set, UErrorCode &status); + + /** + *

Virtual destructor.

+ */ + virtual ~LSTMBreakEngine(); + + virtual const char16_t* name() const; + +protected: + /** + *

Divide up a range of known dictionary characters handled by this break engine.

+ * + * @param text A UText representing the text + * @param rangeStart The start of the range of dictionary characters + * @param rangeEnd The end of the range of dictionary characters + * @param foundBreaks Output of C array of int32_t break positions, or 0 + * @param status Information on any errors encountered. + * @return The number of breaks found + */ + virtual int32_t divideUpDictionaryRange(UText *text, + int32_t rangeStart, + int32_t rangeEnd, + UVector32 &foundBreaks, + UBool isPhraseBreaking, + UErrorCode& status) const override; +private: + const LSTMData* fData; + const Vectorizer* fVectorizer; +}; + +U_CAPI const LanguageBreakEngine* U_EXPORT2 CreateLSTMBreakEngine( + UScriptCode script, const LSTMData* data, UErrorCode& status); + +U_CAPI const LSTMData* U_EXPORT2 CreateLSTMData( + UResourceBundle* rb, UErrorCode& status); + +U_CAPI const LSTMData* U_EXPORT2 CreateLSTMDataForScript( + UScriptCode script, UErrorCode& status); + +U_CAPI void U_EXPORT2 DeleteLSTMData(const LSTMData* data); +U_CAPI const char16_t* U_EXPORT2 LSTMDataName(const LSTMData* data); + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ + +#endif /* LSTMBE_H */ diff --git a/deps/icu-small/source/common/messageimpl.h b/deps/icu-small/source/common/messageimpl.h index 061df9189d5383..e72b31b3dd9a0f 100644 --- a/deps/icu-small/source/common/messageimpl.h +++ b/deps/icu-small/source/common/messageimpl.h @@ -1,65 +1,65 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: messageimpl.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2011apr04 -* created by: Markus W. Scherer -*/ - -#ifndef __MESSAGEIMPL_H__ -#define __MESSAGEIMPL_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/messagepattern.h" - -U_NAMESPACE_BEGIN - -/** - * Helper functions for use of MessagePattern. - * In Java, these are package-private methods in MessagePattern itself. - * In C++, they are declared here and implemented in messagepattern.cpp. - */ -class U_COMMON_API MessageImpl { -public: - /** - * @return true if getApostropheMode()==UMSGPAT_APOS_DOUBLE_REQUIRED - */ - static UBool jdkAposMode(const MessagePattern &msgPattern) { - return msgPattern.getApostropheMode()==UMSGPAT_APOS_DOUBLE_REQUIRED; - } - - /** - * Appends the s[start, limit[ substring to sb, but with only half of the apostrophes - * according to JDK pattern behavior. - */ - static void appendReducedApostrophes(const UnicodeString &s, int32_t start, int32_t limit, - UnicodeString &sb); - - /** - * Appends the sub-message to the result string. - * Omits SKIP_SYNTAX and appends whole arguments using appendReducedApostrophes(). - */ - static UnicodeString &appendSubMessageWithoutSkipSyntax(const MessagePattern &msgPattern, - int32_t msgStart, - UnicodeString &result); - -private: - MessageImpl() = delete; // no constructor: all static methods -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_FORMATTING - -#endif // __MESSAGEIMPL_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: messageimpl.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2011apr04 +* created by: Markus W. Scherer +*/ + +#ifndef __MESSAGEIMPL_H__ +#define __MESSAGEIMPL_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/messagepattern.h" + +U_NAMESPACE_BEGIN + +/** + * Helper functions for use of MessagePattern. + * In Java, these are package-private methods in MessagePattern itself. + * In C++, they are declared here and implemented in messagepattern.cpp. + */ +class U_COMMON_API MessageImpl { +public: + /** + * @return true if getApostropheMode()==UMSGPAT_APOS_DOUBLE_REQUIRED + */ + static UBool jdkAposMode(const MessagePattern &msgPattern) { + return msgPattern.getApostropheMode()==UMSGPAT_APOS_DOUBLE_REQUIRED; + } + + /** + * Appends the s[start, limit[ substring to sb, but with only half of the apostrophes + * according to JDK pattern behavior. + */ + static void appendReducedApostrophes(const UnicodeString &s, int32_t start, int32_t limit, + UnicodeString &sb); + + /** + * Appends the sub-message to the result string. + * Omits SKIP_SYNTAX and appends whole arguments using appendReducedApostrophes(). + */ + static UnicodeString &appendSubMessageWithoutSkipSyntax(const MessagePattern &msgPattern, + int32_t msgStart, + UnicodeString &result); + +private: + MessageImpl() = delete; // no constructor: all static methods +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING + +#endif // __MESSAGEIMPL_H__ diff --git a/deps/icu-small/source/common/messagepattern.cpp b/deps/icu-small/source/common/messagepattern.cpp index 52afab5f026fa7..51251de7c1f646 100644 --- a/deps/icu-small/source/common/messagepattern.cpp +++ b/deps/icu-small/source/common/messagepattern.cpp @@ -1,1233 +1,1233 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011-2012, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: messagepattern.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2011mar14 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/messagepattern.h" -#include "unicode/unistr.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "cstring.h" -#include "messageimpl.h" -#include "patternprops.h" -#include "putilimp.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - -// Unicode character/code point constants ---------------------------------- *** - -static const UChar u_pound=0x23; -static const UChar u_apos=0x27; -static const UChar u_plus=0x2B; -static const UChar u_comma=0x2C; -static const UChar u_minus=0x2D; -static const UChar u_dot=0x2E; -static const UChar u_colon=0x3A; -static const UChar u_lessThan=0x3C; -static const UChar u_equal=0x3D; -static const UChar u_A=0x41; -static const UChar u_C=0x43; -static const UChar u_D=0x44; -static const UChar u_E=0x45; -static const UChar u_H=0x48; -static const UChar u_I=0x49; -static const UChar u_L=0x4C; -static const UChar u_N=0x4E; -static const UChar u_O=0x4F; -static const UChar u_P=0x50; -static const UChar u_R=0x52; -static const UChar u_S=0x53; -static const UChar u_T=0x54; -static const UChar u_U=0x55; -static const UChar u_Z=0x5A; -static const UChar u_a=0x61; -static const UChar u_c=0x63; -static const UChar u_d=0x64; -static const UChar u_e=0x65; -static const UChar u_f=0x66; -static const UChar u_h=0x68; -static const UChar u_i=0x69; -static const UChar u_l=0x6C; -static const UChar u_n=0x6E; -static const UChar u_o=0x6F; -static const UChar u_p=0x70; -static const UChar u_r=0x72; -static const UChar u_s=0x73; -static const UChar u_t=0x74; -static const UChar u_u=0x75; -static const UChar u_z=0x7A; -static const UChar u_leftCurlyBrace=0x7B; -static const UChar u_pipe=0x7C; -static const UChar u_rightCurlyBrace=0x7D; -static const UChar u_lessOrEqual=0x2264; // U+2264 is <= - -static const UChar kOffsetColon[]={ // "offset:" - u_o, u_f, u_f, u_s, u_e, u_t, u_colon -}; - -static const UChar kOther[]={ // "other" - u_o, u_t, u_h, u_e, u_r -}; - -// MessagePatternList ------------------------------------------------------ *** - -template -class MessagePatternList : public UMemory { -public: - MessagePatternList() {} - void copyFrom(const MessagePatternList &other, - int32_t length, - UErrorCode &errorCode); - UBool ensureCapacityForOneMore(int32_t oldLength, UErrorCode &errorCode); - UBool equals(const MessagePatternList &other, int32_t length) const { - for(int32_t i=0; i a; -}; - -template -void -MessagePatternList::copyFrom( - const MessagePatternList &other, - int32_t length, - UErrorCode &errorCode) { - if(U_SUCCESS(errorCode) && length>0) { - if(length>a.getCapacity() && NULL==a.resize(length)) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_memcpy(a.getAlias(), other.a.getAlias(), (size_t)length*sizeof(T)); - } -} - -template -UBool -MessagePatternList::ensureCapacityForOneMore(int32_t oldLength, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return false; - } - if(a.getCapacity()>oldLength || a.resize(2*oldLength, oldLength)!=NULL) { - return true; - } - errorCode=U_MEMORY_ALLOCATION_ERROR; - return false; -} - -// MessagePatternList specializations -------------------------------------- *** - -class MessagePatternDoubleList : public MessagePatternList { -}; - -class MessagePatternPartsList : public MessagePatternList { -}; - -// MessagePattern constructors etc. ---------------------------------------- *** - -MessagePattern::MessagePattern(UErrorCode &errorCode) - : aposMode(UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE), - partsList(NULL), parts(NULL), partsLength(0), - numericValuesList(NULL), numericValues(NULL), numericValuesLength(0), - hasArgNames(false), hasArgNumbers(false), needsAutoQuoting(false) { - init(errorCode); -} - -MessagePattern::MessagePattern(UMessagePatternApostropheMode mode, UErrorCode &errorCode) - : aposMode(mode), - partsList(NULL), parts(NULL), partsLength(0), - numericValuesList(NULL), numericValues(NULL), numericValuesLength(0), - hasArgNames(false), hasArgNumbers(false), needsAutoQuoting(false) { - init(errorCode); -} - -MessagePattern::MessagePattern(const UnicodeString &pattern, UParseError *parseError, UErrorCode &errorCode) - : aposMode(UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE), - partsList(NULL), parts(NULL), partsLength(0), - numericValuesList(NULL), numericValues(NULL), numericValuesLength(0), - hasArgNames(false), hasArgNumbers(false), needsAutoQuoting(false) { - if(init(errorCode)) { - parse(pattern, parseError, errorCode); - } -} - -UBool -MessagePattern::init(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return false; - } - partsList=new MessagePatternPartsList(); - if(partsList==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return false; - } - parts=partsList->a.getAlias(); - return true; -} - -MessagePattern::MessagePattern(const MessagePattern &other) - : UObject(other), aposMode(other.aposMode), msg(other.msg), - partsList(NULL), parts(NULL), partsLength(0), - numericValuesList(NULL), numericValues(NULL), numericValuesLength(0), - hasArgNames(other.hasArgNames), hasArgNumbers(other.hasArgNumbers), - needsAutoQuoting(other.needsAutoQuoting) { - UErrorCode errorCode=U_ZERO_ERROR; - if(!copyStorage(other, errorCode)) { - clear(); - } -} - -MessagePattern & -MessagePattern::operator=(const MessagePattern &other) { - if(this==&other) { - return *this; - } - aposMode=other.aposMode; - msg=other.msg; - hasArgNames=other.hasArgNames; - hasArgNumbers=other.hasArgNumbers; - needsAutoQuoting=other.needsAutoQuoting; - UErrorCode errorCode=U_ZERO_ERROR; - if(!copyStorage(other, errorCode)) { - clear(); - } - return *this; -} - -UBool -MessagePattern::copyStorage(const MessagePattern &other, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return false; - } - parts=NULL; - partsLength=0; - numericValues=NULL; - numericValuesLength=0; - if(partsList==NULL) { - partsList=new MessagePatternPartsList(); - if(partsList==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return false; - } - parts=partsList->a.getAlias(); - } - if(other.partsLength>0) { - partsList->copyFrom(*other.partsList, other.partsLength, errorCode); - if(U_FAILURE(errorCode)) { - return false; - } - parts=partsList->a.getAlias(); - partsLength=other.partsLength; - } - if(other.numericValuesLength>0) { - if(numericValuesList==NULL) { - numericValuesList=new MessagePatternDoubleList(); - if(numericValuesList==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return false; - } - numericValues=numericValuesList->a.getAlias(); - } - numericValuesList->copyFrom( - *other.numericValuesList, other.numericValuesLength, errorCode); - if(U_FAILURE(errorCode)) { - return false; - } - numericValues=numericValuesList->a.getAlias(); - numericValuesLength=other.numericValuesLength; - } - return true; -} - -MessagePattern::~MessagePattern() { - delete partsList; - delete numericValuesList; -} - -// MessagePattern API ------------------------------------------------------ *** - -MessagePattern & -MessagePattern::parse(const UnicodeString &pattern, UParseError *parseError, UErrorCode &errorCode) { - preParse(pattern, parseError, errorCode); - parseMessage(0, 0, 0, UMSGPAT_ARG_TYPE_NONE, parseError, errorCode); - postParse(); - return *this; -} - -MessagePattern & -MessagePattern::parseChoiceStyle(const UnicodeString &pattern, - UParseError *parseError, UErrorCode &errorCode) { - preParse(pattern, parseError, errorCode); - parseChoiceStyle(0, 0, parseError, errorCode); - postParse(); - return *this; -} - -MessagePattern & -MessagePattern::parsePluralStyle(const UnicodeString &pattern, - UParseError *parseError, UErrorCode &errorCode) { - preParse(pattern, parseError, errorCode); - parsePluralOrSelectStyle(UMSGPAT_ARG_TYPE_PLURAL, 0, 0, parseError, errorCode); - postParse(); - return *this; -} - -MessagePattern & -MessagePattern::parseSelectStyle(const UnicodeString &pattern, - UParseError *parseError, UErrorCode &errorCode) { - preParse(pattern, parseError, errorCode); - parsePluralOrSelectStyle(UMSGPAT_ARG_TYPE_SELECT, 0, 0, parseError, errorCode); - postParse(); - return *this; -} - -void -MessagePattern::clear() { - // Mostly the same as preParse(). - msg.remove(); - hasArgNames=hasArgNumbers=false; - needsAutoQuoting=false; - partsLength=0; - numericValuesLength=0; -} - -bool -MessagePattern::operator==(const MessagePattern &other) const { - if(this==&other) { - return true; - } - return - aposMode==other.aposMode && - msg==other.msg && - // parts.equals(o.parts) - partsLength==other.partsLength && - (partsLength==0 || partsList->equals(*other.partsList, partsLength)); - // No need to compare numericValues if msg and parts are the same. -} - -int32_t -MessagePattern::hashCode() const { - int32_t hash=(aposMode*37+msg.hashCode())*37+partsLength; - for(int32_t i=0; i0;) { - const Part &part=getPart(--i); - if(part.getType()==UMSGPAT_PART_TYPE_INSERT_CHAR) { - modified.insert(part.index, (UChar)part.value); - } - } - return modified; -} - -double -MessagePattern::getNumericValue(const Part &part) const { - UMessagePatternPartType type=part.type; - if(type==UMSGPAT_PART_TYPE_ARG_INT) { - return part.value; - } else if(type==UMSGPAT_PART_TYPE_ARG_DOUBLE) { - return numericValues[part.value]; - } else { - return UMSGPAT_NO_NUMERIC_VALUE; - } -} - -/** - * Returns the "offset:" value of a PluralFormat argument, or 0 if none is specified. - * @param pluralStart the index of the first PluralFormat argument style part. (0..countParts()-1) - * @return the "offset:" value. - * @draft ICU 4.8 - */ -double -MessagePattern::getPluralOffset(int32_t pluralStart) const { - const Part &part=getPart(pluralStart); - if(Part::hasNumericValue(part.type)) { - return getNumericValue(part); - } else { - return 0; - } -} - -// MessagePattern::Part ---------------------------------------------------- *** - -bool -MessagePattern::Part::operator==(const Part &other) const { - if(this==&other) { - return true; - } - return - type==other.type && - index==other.index && - length==other.length && - value==other.value && - limitPartIndex==other.limitPartIndex; -} - -// MessagePattern parser --------------------------------------------------- *** - -void -MessagePattern::preParse(const UnicodeString &pattern, UParseError *parseError, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return; - } - if(parseError!=NULL) { - parseError->line=0; - parseError->offset=0; - parseError->preContext[0]=0; - parseError->postContext[0]=0; - } - msg=pattern; - hasArgNames=hasArgNumbers=false; - needsAutoQuoting=false; - partsLength=0; - numericValuesLength=0; -} - -void -MessagePattern::postParse() { - if(partsList!=NULL) { - parts=partsList->a.getAlias(); - } - if(numericValuesList!=NULL) { - numericValues=numericValuesList->a.getAlias(); - } -} - -int32_t -MessagePattern::parseMessage(int32_t index, int32_t msgStartLength, - int32_t nestingLevel, UMessagePatternArgType parentType, - UParseError *parseError, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return 0; - } - if(nestingLevel>Part::MAX_VALUE) { - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - int32_t msgStart=partsLength; - addPart(UMSGPAT_PART_TYPE_MSG_START, index, msgStartLength, nestingLevel, errorCode); - index+=msgStartLength; - for(;;) { // while(index=msg.length()) { - break; - } - UChar c=msg.charAt(index++); - if(c==u_apos) { - if(index==msg.length()) { - // The apostrophe is the last character in the pattern. - // Add a Part for auto-quoting. - addPart(UMSGPAT_PART_TYPE_INSERT_CHAR, index, 0, - u_apos, errorCode); // value=char to be inserted - needsAutoQuoting=true; - } else { - c=msg.charAt(index); - if(c==u_apos) { - // double apostrophe, skip the second one - addPart(UMSGPAT_PART_TYPE_SKIP_SYNTAX, index++, 1, 0, errorCode); - } else if( - aposMode==UMSGPAT_APOS_DOUBLE_REQUIRED || - c==u_leftCurlyBrace || c==u_rightCurlyBrace || - (parentType==UMSGPAT_ARG_TYPE_CHOICE && c==u_pipe) || - (UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(parentType) && c==u_pound) - ) { - // skip the quote-starting apostrophe - addPart(UMSGPAT_PART_TYPE_SKIP_SYNTAX, index-1, 1, 0, errorCode); - // find the end of the quoted literal text - for(;;) { - index=msg.indexOf(u_apos, index+1); - if(index>=0) { - if(/*(index+1)0 && c==u_rightCurlyBrace) || - (parentType==UMSGPAT_ARG_TYPE_CHOICE && c==u_pipe)) { - // Finish the message before the terminator. - // In a choice style, report the "}" substring only for the following ARG_LIMIT, - // not for this MSG_LIMIT. - int32_t limitLength=(parentType==UMSGPAT_ARG_TYPE_CHOICE && c==u_rightCurlyBrace) ? 0 : 1; - addLimitPart(msgStart, UMSGPAT_PART_TYPE_MSG_LIMIT, index-1, limitLength, - nestingLevel, errorCode); - if(parentType==UMSGPAT_ARG_TYPE_CHOICE) { - // Let the choice style parser see the '}' or '|'. - return index-1; - } else { - // continue parsing after the '}' - return index; - } - } // else: c is part of literal text - } - if(nestingLevel>0 && !inTopLevelChoiceMessage(nestingLevel, parentType)) { - setParseError(parseError, 0); // Unmatched '{' braces in message. - errorCode=U_UNMATCHED_BRACES; - return 0; - } - addLimitPart(msgStart, UMSGPAT_PART_TYPE_MSG_LIMIT, index, 0, nestingLevel, errorCode); - return index; -} - -int32_t -MessagePattern::parseArg(int32_t index, int32_t argStartLength, int32_t nestingLevel, - UParseError *parseError, UErrorCode &errorCode) { - int32_t argStart=partsLength; - UMessagePatternArgType argType=UMSGPAT_ARG_TYPE_NONE; - addPart(UMSGPAT_PART_TYPE_ARG_START, index, argStartLength, argType, errorCode); - if(U_FAILURE(errorCode)) { - return 0; - } - int32_t nameIndex=index=skipWhiteSpace(index+argStartLength); - if(index==msg.length()) { - setParseError(parseError, 0); // Unmatched '{' braces in message. - errorCode=U_UNMATCHED_BRACES; - return 0; - } - // parse argument name or number - index=skipIdentifier(index); - int32_t number=parseArgNumber(nameIndex, index); - if(number>=0) { - int32_t length=index-nameIndex; - if(length>Part::MAX_LENGTH || number>Part::MAX_VALUE) { - setParseError(parseError, nameIndex); // Argument number too large. - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - hasArgNumbers=true; - addPart(UMSGPAT_PART_TYPE_ARG_NUMBER, nameIndex, length, number, errorCode); - } else if(number==UMSGPAT_ARG_NAME_NOT_NUMBER) { - int32_t length=index-nameIndex; - if(length>Part::MAX_LENGTH) { - setParseError(parseError, nameIndex); // Argument name too long. - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - hasArgNames=true; - addPart(UMSGPAT_PART_TYPE_ARG_NAME, nameIndex, length, 0, errorCode); - } else { // number<-1 (ARG_NAME_NOT_VALID) - setParseError(parseError, nameIndex); // Bad argument syntax. - errorCode=U_PATTERN_SYNTAX_ERROR; - return 0; - } - index=skipWhiteSpace(index); - if(index==msg.length()) { - setParseError(parseError, 0); // Unmatched '{' braces in message. - errorCode=U_UNMATCHED_BRACES; - return 0; - } - UChar c=msg.charAt(index); - if(c==u_rightCurlyBrace) { - // all done - } else if(c!=u_comma) { - setParseError(parseError, nameIndex); // Bad argument syntax. - errorCode=U_PATTERN_SYNTAX_ERROR; - return 0; - } else /* ',' */ { - // parse argument type: case-sensitive a-zA-Z - int32_t typeIndex=index=skipWhiteSpace(index+1); - while(indexPart::MAX_LENGTH) { - setParseError(parseError, nameIndex); // Argument type name too long. - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - argType=UMSGPAT_ARG_TYPE_SIMPLE; - if(length==6) { - // case-insensitive comparisons for complex-type names - if(isChoice(typeIndex)) { - argType=UMSGPAT_ARG_TYPE_CHOICE; - } else if(isPlural(typeIndex)) { - argType=UMSGPAT_ARG_TYPE_PLURAL; - } else if(isSelect(typeIndex)) { - argType=UMSGPAT_ARG_TYPE_SELECT; - } - } else if(length==13) { - if(isSelect(typeIndex) && isOrdinal(typeIndex+6)) { - argType=UMSGPAT_ARG_TYPE_SELECTORDINAL; - } - } - // change the ARG_START type from NONE to argType - partsList->a[argStart].value=(int16_t)argType; - if(argType==UMSGPAT_ARG_TYPE_SIMPLE) { - addPart(UMSGPAT_PART_TYPE_ARG_TYPE, typeIndex, length, 0, errorCode); - } - // look for an argument style (pattern) - if(c==u_rightCurlyBrace) { - if(argType!=UMSGPAT_ARG_TYPE_SIMPLE) { - setParseError(parseError, nameIndex); // No style field for complex argument. - errorCode=U_PATTERN_SYNTAX_ERROR; - return 0; - } - } else /* ',' */ { - ++index; - if(argType==UMSGPAT_ARG_TYPE_SIMPLE) { - index=parseSimpleStyle(index, parseError, errorCode); - } else if(argType==UMSGPAT_ARG_TYPE_CHOICE) { - index=parseChoiceStyle(index, nestingLevel, parseError, errorCode); - } else { - index=parsePluralOrSelectStyle(argType, index, nestingLevel, parseError, errorCode); - } - } - } - // Argument parsing stopped on the '}'. - addLimitPart(argStart, UMSGPAT_PART_TYPE_ARG_LIMIT, index, 1, argType, errorCode); - return index+1; -} - -int32_t -MessagePattern::parseSimpleStyle(int32_t index, UParseError *parseError, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return 0; - } - int32_t start=index; - int32_t nestedBraces=0; - while(index0) { - --nestedBraces; - } else { - int32_t length=--index-start; - if(length>Part::MAX_LENGTH) { - setParseError(parseError, start); // Argument style text too long. - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - addPart(UMSGPAT_PART_TYPE_ARG_STYLE, start, length, 0, errorCode); - return index; - } - } // c is part of literal text - } - setParseError(parseError, 0); // Unmatched '{' braces in message. - errorCode=U_UNMATCHED_BRACES; - return 0; -} - -int32_t -MessagePattern::parseChoiceStyle(int32_t index, int32_t nestingLevel, - UParseError *parseError, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return 0; - } - int32_t start=index; - index=skipWhiteSpace(index); - if(index==msg.length() || msg.charAt(index)==u_rightCurlyBrace) { - setParseError(parseError, 0); // Missing choice argument pattern. - errorCode=U_PATTERN_SYNTAX_ERROR; - return 0; - } - for(;;) { - // The choice argument style contains |-separated (number, separator, message) triples. - // Parse the number. - int32_t numberIndex=index; - index=skipDouble(index); - int32_t length=index-numberIndex; - if(length==0) { - setParseError(parseError, start); // Bad choice pattern syntax. - errorCode=U_PATTERN_SYNTAX_ERROR; - return 0; - } - if(length>Part::MAX_LENGTH) { - setParseError(parseError, numberIndex); // Choice number too long. - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - parseDouble(numberIndex, index, true, parseError, errorCode); // adds ARG_INT or ARG_DOUBLE - if(U_FAILURE(errorCode)) { - return 0; - } - // Parse the separator. - index=skipWhiteSpace(index); - if(index==msg.length()) { - setParseError(parseError, start); // Bad choice pattern syntax. - errorCode=U_PATTERN_SYNTAX_ERROR; - return 0; - } - UChar c=msg.charAt(index); - if(!(c==u_pound || c==u_lessThan || c==u_lessOrEqual)) { // U+2264 is <= - setParseError(parseError, start); // Expected choice separator (#<\u2264) instead of c. - errorCode=U_PATTERN_SYNTAX_ERROR; - return 0; - } - addPart(UMSGPAT_PART_TYPE_ARG_SELECTOR, index, 1, 0, errorCode); - // Parse the message fragment. - index=parseMessage(++index, 0, nestingLevel+1, UMSGPAT_ARG_TYPE_CHOICE, parseError, errorCode); - if(U_FAILURE(errorCode)) { - return 0; - } - // parseMessage(..., CHOICE) returns the index of the terminator, or msg.length(). - if(index==msg.length()) { - return index; - } - if(msg.charAt(index)==u_rightCurlyBrace) { - if(!inMessageFormatPattern(nestingLevel)) { - setParseError(parseError, start); // Bad choice pattern syntax. - errorCode=U_PATTERN_SYNTAX_ERROR; - return 0; - } - return index; - } // else the terminator is '|' - index=skipWhiteSpace(index+1); - } -} - -int32_t -MessagePattern::parsePluralOrSelectStyle(UMessagePatternArgType argType, - int32_t index, int32_t nestingLevel, - UParseError *parseError, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return 0; - } - int32_t start=index; - UBool isEmpty=true; - UBool hasOther=false; - for(;;) { - // First, collect the selector looking for a small set of terminators. - // It would be a little faster to consider the syntax of each possible - // token right here, but that makes the code too complicated. - index=skipWhiteSpace(index); - UBool eos=index==msg.length(); - if(eos || msg.charAt(index)==u_rightCurlyBrace) { - if(eos==inMessageFormatPattern(nestingLevel)) { - setParseError(parseError, start); // Bad plural/select pattern syntax. - errorCode=U_PATTERN_SYNTAX_ERROR; - return 0; - } - if(!hasOther) { - setParseError(parseError, 0); // Missing 'other' keyword in plural/select pattern. - errorCode=U_DEFAULT_KEYWORD_MISSING; - return 0; - } - return index; - } - int32_t selectorIndex=index; - if(UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType) && msg.charAt(selectorIndex)==u_equal) { - // explicit-value plural selector: =double - index=skipDouble(index+1); - int32_t length=index-selectorIndex; - if(length==1) { - setParseError(parseError, start); // Bad plural/select pattern syntax. - errorCode=U_PATTERN_SYNTAX_ERROR; - return 0; - } - if(length>Part::MAX_LENGTH) { - setParseError(parseError, selectorIndex); // Argument selector too long. - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - addPart(UMSGPAT_PART_TYPE_ARG_SELECTOR, selectorIndex, length, 0, errorCode); - parseDouble(selectorIndex+1, index, false, - parseError, errorCode); // adds ARG_INT or ARG_DOUBLE - } else { - index=skipIdentifier(index); - int32_t length=index-selectorIndex; - if(length==0) { - setParseError(parseError, start); // Bad plural/select pattern syntax. - errorCode=U_PATTERN_SYNTAX_ERROR; - return 0; - } - // Note: The ':' in "offset:" is just beyond the skipIdentifier() range. - if( UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType) && length==6 && indexPart::MAX_LENGTH) { - setParseError(parseError, valueIndex); // Plural offset value too long. - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - parseDouble(valueIndex, index, false, - parseError, errorCode); // adds ARG_INT or ARG_DOUBLE - if(U_FAILURE(errorCode)) { - return 0; - } - isEmpty=false; - continue; // no message fragment after the offset - } else { - // normal selector word - if(length>Part::MAX_LENGTH) { - setParseError(parseError, selectorIndex); // Argument selector too long. - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - addPart(UMSGPAT_PART_TYPE_ARG_SELECTOR, selectorIndex, length, 0, errorCode); - if(0==msg.compare(selectorIndex, length, kOther, 0, 5)) { - hasOther=true; - } - } - } - if(U_FAILURE(errorCode)) { - return 0; - } - - // parse the message fragment following the selector - index=skipWhiteSpace(index); - if(index==msg.length() || msg.charAt(index)!=u_leftCurlyBrace) { - setParseError(parseError, selectorIndex); // No message fragment after plural/select selector. - errorCode=U_PATTERN_SYNTAX_ERROR; - return 0; - } - index=parseMessage(index, 1, nestingLevel+1, argType, parseError, errorCode); - if(U_FAILURE(errorCode)) { - return 0; - } - isEmpty=false; - } -} - -int32_t -MessagePattern::parseArgNumber(const UnicodeString &s, int32_t start, int32_t limit) { - // If the identifier contains only ASCII digits, then it is an argument _number_ - // and must not have leading zeros (except "0" itself). - // Otherwise it is an argument _name_. - if(start>=limit) { - return UMSGPAT_ARG_NAME_NOT_VALID; - } - int32_t number; - // Defer numeric errors until we know there are only digits. - UBool badNumber; - UChar c=s.charAt(start++); - if(c==0x30) { - if(start==limit) { - return 0; - } else { - number=0; - badNumber=true; // leading zero - } - } else if(0x31<=c && c<=0x39) { - number=c-0x30; - badNumber=false; - } else { - return UMSGPAT_ARG_NAME_NOT_NUMBER; - } - while(start=INT32_MAX/10) { - badNumber=true; // overflow - } - number=number*10+(c-0x30); - } else { - return UMSGPAT_ARG_NAME_NOT_NUMBER; - } - } - // There are only ASCII digits. - if(badNumber) { - return UMSGPAT_ARG_NAME_NOT_VALID; - } else { - return number; - } -} - -void -MessagePattern::parseDouble(int32_t start, int32_t limit, UBool allowInfinity, - UParseError *parseError, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return; - } - U_ASSERT(start(Part::MAX_VALUE+isNegative)) { - break; // not a small-enough integer - } - if(index==limit) { - addPart(UMSGPAT_PART_TYPE_ARG_INT, start, limit-start, - isNegative!=0 ? -value : value, errorCode); - return; - } - c=msg.charAt(index++); - } - // Let Double.parseDouble() throw a NumberFormatException. - char numberChars[128]; - int32_t capacity=(int32_t)sizeof(numberChars); - int32_t length=limit-start; - if(length>=capacity) { - break; // number too long - } - msg.extract(start, length, numberChars, capacity, US_INV); - if((int32_t)uprv_strlen(numberChars)0x39 && c!=u_e && c!=u_E && c!=0x221e)) { - break; - } - ++index; - } - return index; -} - -UBool -MessagePattern::isArgTypeChar(UChar32 c) { - return (u_a<=c && c<=u_z) || (u_A<=c && c<=u_Z); -} - -UBool -MessagePattern::isChoice(int32_t index) { - UChar c; - return - ((c=msg.charAt(index++))==u_c || c==u_C) && - ((c=msg.charAt(index++))==u_h || c==u_H) && - ((c=msg.charAt(index++))==u_o || c==u_O) && - ((c=msg.charAt(index++))==u_i || c==u_I) && - ((c=msg.charAt(index++))==u_c || c==u_C) && - ((c=msg.charAt(index))==u_e || c==u_E); -} - -UBool -MessagePattern::isPlural(int32_t index) { - UChar c; - return - ((c=msg.charAt(index++))==u_p || c==u_P) && - ((c=msg.charAt(index++))==u_l || c==u_L) && - ((c=msg.charAt(index++))==u_u || c==u_U) && - ((c=msg.charAt(index++))==u_r || c==u_R) && - ((c=msg.charAt(index++))==u_a || c==u_A) && - ((c=msg.charAt(index))==u_l || c==u_L); -} - -UBool -MessagePattern::isSelect(int32_t index) { - UChar c; - return - ((c=msg.charAt(index++))==u_s || c==u_S) && - ((c=msg.charAt(index++))==u_e || c==u_E) && - ((c=msg.charAt(index++))==u_l || c==u_L) && - ((c=msg.charAt(index++))==u_e || c==u_E) && - ((c=msg.charAt(index++))==u_c || c==u_C) && - ((c=msg.charAt(index))==u_t || c==u_T); -} - -UBool -MessagePattern::isOrdinal(int32_t index) { - UChar c; - return - ((c=msg.charAt(index++))==u_o || c==u_O) && - ((c=msg.charAt(index++))==u_r || c==u_R) && - ((c=msg.charAt(index++))==u_d || c==u_D) && - ((c=msg.charAt(index++))==u_i || c==u_I) && - ((c=msg.charAt(index++))==u_n || c==u_N) && - ((c=msg.charAt(index++))==u_a || c==u_A) && - ((c=msg.charAt(index))==u_l || c==u_L); -} - -UBool -MessagePattern::inMessageFormatPattern(int32_t nestingLevel) { - return nestingLevel>0 || partsList->a[0].type==UMSGPAT_PART_TYPE_MSG_START; -} - -UBool -MessagePattern::inTopLevelChoiceMessage(int32_t nestingLevel, UMessagePatternArgType parentType) { - return - nestingLevel==1 && - parentType==UMSGPAT_ARG_TYPE_CHOICE && - partsList->a[0].type!=UMSGPAT_PART_TYPE_MSG_START; -} - -void -MessagePattern::addPart(UMessagePatternPartType type, int32_t index, int32_t length, - int32_t value, UErrorCode &errorCode) { - if(partsList->ensureCapacityForOneMore(partsLength, errorCode)) { - Part &part=partsList->a[partsLength++]; - part.type=type; - part.index=index; - part.length=(uint16_t)length; - part.value=(int16_t)value; - part.limitPartIndex=0; - } -} - -void -MessagePattern::addLimitPart(int32_t start, - UMessagePatternPartType type, int32_t index, int32_t length, - int32_t value, UErrorCode &errorCode) { - partsList->a[start].limitPartIndex=partsLength; - addPart(type, index, length, value, errorCode); -} - -void -MessagePattern::addArgDoublePart(double numericValue, int32_t start, int32_t length, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return; - } - int32_t numericIndex=numericValuesLength; - if(numericValuesList==NULL) { - numericValuesList=new MessagePatternDoubleList(); - if(numericValuesList==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - } else if(!numericValuesList->ensureCapacityForOneMore(numericValuesLength, errorCode)) { - return; - } else { - if(numericIndex>Part::MAX_VALUE) { - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return; - } - } - numericValuesList->a[numericValuesLength++]=numericValue; - addPart(UMSGPAT_PART_TYPE_ARG_DOUBLE, start, length, numericIndex, errorCode); -} - -void -MessagePattern::setParseError(UParseError *parseError, int32_t index) { - if(parseError==NULL) { - return; - } - parseError->offset=index; - - // Set preContext to some of msg before index. - // Avoid splitting a surrogate pair. - int32_t length=index; - if(length>=U_PARSE_CONTEXT_LEN) { - length=U_PARSE_CONTEXT_LEN-1; - if(length>0 && U16_IS_TRAIL(msg[index-length])) { - --length; - } - } - msg.extract(index-length, length, parseError->preContext); - parseError->preContext[length]=0; - - // Set postContext to some of msg starting at index. - length=msg.length()-index; - if(length>=U_PARSE_CONTEXT_LEN) { - length=U_PARSE_CONTEXT_LEN-1; - if(length>0 && U16_IS_LEAD(msg[index+length-1])) { - --length; - } - } - msg.extract(index, length, parseError->postContext); - parseError->postContext[length]=0; -} - -// MessageImpl ------------------------------------------------------------- *** - -void -MessageImpl::appendReducedApostrophes(const UnicodeString &s, int32_t start, int32_t limit, - UnicodeString &sb) { - int32_t doubleApos=-1; - for(;;) { - int32_t i=s.indexOf(u_apos, start); - if(i<0 || i>=limit) { - sb.append(s, start, limit-start); - break; - } - if(i==doubleApos) { - // Double apostrophe at start-1 and start==i, append one. - sb.append(u_apos); - ++start; - doubleApos=-1; - } else { - // Append text between apostrophes and skip this one. - sb.append(s, start, i-start); - doubleApos=start=i+1; - } - } -} - -// Ported from second half of ICU4J SelectFormat.format(String). -UnicodeString & -MessageImpl::appendSubMessageWithoutSkipSyntax(const MessagePattern &msgPattern, - int32_t msgStart, - UnicodeString &result) { - const UnicodeString &msgString=msgPattern.getPatternString(); - int32_t prevIndex=msgPattern.getPart(msgStart).getLimit(); - for(int32_t i=msgStart;;) { - const MessagePattern::Part &part=msgPattern.getPart(++i); - UMessagePatternPartType type=part.getType(); - int32_t index=part.getIndex(); - if(type==UMSGPAT_PART_TYPE_MSG_LIMIT) { - return result.append(msgString, prevIndex, index-prevIndex); - } else if(type==UMSGPAT_PART_TYPE_SKIP_SYNTAX) { - result.append(msgString, prevIndex, index-prevIndex); - prevIndex=part.getLimit(); - } else if(type==UMSGPAT_PART_TYPE_ARG_START) { - result.append(msgString, prevIndex, index-prevIndex); - prevIndex=index; - i=msgPattern.getLimitPartIndex(i); - index=msgPattern.getPart(i).getLimit(); - appendReducedApostrophes(msgString, prevIndex, index, result); - prevIndex=index; - } - } -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_FORMATTING +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2011-2012, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: messagepattern.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2011mar14 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/messagepattern.h" +#include "unicode/unistr.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "cstring.h" +#include "messageimpl.h" +#include "patternprops.h" +#include "putilimp.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +// Unicode character/code point constants ---------------------------------- *** + +static const char16_t u_pound=0x23; +static const char16_t u_apos=0x27; +static const char16_t u_plus=0x2B; +static const char16_t u_comma=0x2C; +static const char16_t u_minus=0x2D; +static const char16_t u_dot=0x2E; +static const char16_t u_colon=0x3A; +static const char16_t u_lessThan=0x3C; +static const char16_t u_equal=0x3D; +static const char16_t u_A=0x41; +static const char16_t u_C=0x43; +static const char16_t u_D=0x44; +static const char16_t u_E=0x45; +static const char16_t u_H=0x48; +static const char16_t u_I=0x49; +static const char16_t u_L=0x4C; +static const char16_t u_N=0x4E; +static const char16_t u_O=0x4F; +static const char16_t u_P=0x50; +static const char16_t u_R=0x52; +static const char16_t u_S=0x53; +static const char16_t u_T=0x54; +static const char16_t u_U=0x55; +static const char16_t u_Z=0x5A; +static const char16_t u_a=0x61; +static const char16_t u_c=0x63; +static const char16_t u_d=0x64; +static const char16_t u_e=0x65; +static const char16_t u_f=0x66; +static const char16_t u_h=0x68; +static const char16_t u_i=0x69; +static const char16_t u_l=0x6C; +static const char16_t u_n=0x6E; +static const char16_t u_o=0x6F; +static const char16_t u_p=0x70; +static const char16_t u_r=0x72; +static const char16_t u_s=0x73; +static const char16_t u_t=0x74; +static const char16_t u_u=0x75; +static const char16_t u_z=0x7A; +static const char16_t u_leftCurlyBrace=0x7B; +static const char16_t u_pipe=0x7C; +static const char16_t u_rightCurlyBrace=0x7D; +static const char16_t u_lessOrEqual=0x2264; // U+2264 is <= + +static const char16_t kOffsetColon[]={ // "offset:" + u_o, u_f, u_f, u_s, u_e, u_t, u_colon +}; + +static const char16_t kOther[]={ // "other" + u_o, u_t, u_h, u_e, u_r +}; + +// MessagePatternList ------------------------------------------------------ *** + +template +class MessagePatternList : public UMemory { +public: + MessagePatternList() {} + void copyFrom(const MessagePatternList &other, + int32_t length, + UErrorCode &errorCode); + UBool ensureCapacityForOneMore(int32_t oldLength, UErrorCode &errorCode); + UBool equals(const MessagePatternList &other, int32_t length) const { + for(int32_t i=0; i a; +}; + +template +void +MessagePatternList::copyFrom( + const MessagePatternList &other, + int32_t length, + UErrorCode &errorCode) { + if(U_SUCCESS(errorCode) && length>0) { + if(length>a.getCapacity() && nullptr==a.resize(length)) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_memcpy(a.getAlias(), other.a.getAlias(), (size_t)length*sizeof(T)); + } +} + +template +UBool +MessagePatternList::ensureCapacityForOneMore(int32_t oldLength, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return false; + } + if(a.getCapacity()>oldLength || a.resize(2*oldLength, oldLength)!=nullptr) { + return true; + } + errorCode=U_MEMORY_ALLOCATION_ERROR; + return false; +} + +// MessagePatternList specializations -------------------------------------- *** + +class MessagePatternDoubleList : public MessagePatternList { +}; + +class MessagePatternPartsList : public MessagePatternList { +}; + +// MessagePattern constructors etc. ---------------------------------------- *** + +MessagePattern::MessagePattern(UErrorCode &errorCode) + : aposMode(UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE), + partsList(nullptr), parts(nullptr), partsLength(0), + numericValuesList(nullptr), numericValues(nullptr), numericValuesLength(0), + hasArgNames(false), hasArgNumbers(false), needsAutoQuoting(false) { + init(errorCode); +} + +MessagePattern::MessagePattern(UMessagePatternApostropheMode mode, UErrorCode &errorCode) + : aposMode(mode), + partsList(nullptr), parts(nullptr), partsLength(0), + numericValuesList(nullptr), numericValues(nullptr), numericValuesLength(0), + hasArgNames(false), hasArgNumbers(false), needsAutoQuoting(false) { + init(errorCode); +} + +MessagePattern::MessagePattern(const UnicodeString &pattern, UParseError *parseError, UErrorCode &errorCode) + : aposMode(UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE), + partsList(nullptr), parts(nullptr), partsLength(0), + numericValuesList(nullptr), numericValues(nullptr), numericValuesLength(0), + hasArgNames(false), hasArgNumbers(false), needsAutoQuoting(false) { + if(init(errorCode)) { + parse(pattern, parseError, errorCode); + } +} + +UBool +MessagePattern::init(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return false; + } + partsList=new MessagePatternPartsList(); + if(partsList==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return false; + } + parts=partsList->a.getAlias(); + return true; +} + +MessagePattern::MessagePattern(const MessagePattern &other) + : UObject(other), aposMode(other.aposMode), msg(other.msg), + partsList(nullptr), parts(nullptr), partsLength(0), + numericValuesList(nullptr), numericValues(nullptr), numericValuesLength(0), + hasArgNames(other.hasArgNames), hasArgNumbers(other.hasArgNumbers), + needsAutoQuoting(other.needsAutoQuoting) { + UErrorCode errorCode=U_ZERO_ERROR; + if(!copyStorage(other, errorCode)) { + clear(); + } +} + +MessagePattern & +MessagePattern::operator=(const MessagePattern &other) { + if(this==&other) { + return *this; + } + aposMode=other.aposMode; + msg=other.msg; + hasArgNames=other.hasArgNames; + hasArgNumbers=other.hasArgNumbers; + needsAutoQuoting=other.needsAutoQuoting; + UErrorCode errorCode=U_ZERO_ERROR; + if(!copyStorage(other, errorCode)) { + clear(); + } + return *this; +} + +UBool +MessagePattern::copyStorage(const MessagePattern &other, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return false; + } + parts=nullptr; + partsLength=0; + numericValues=nullptr; + numericValuesLength=0; + if(partsList==nullptr) { + partsList=new MessagePatternPartsList(); + if(partsList==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return false; + } + parts=partsList->a.getAlias(); + } + if(other.partsLength>0) { + partsList->copyFrom(*other.partsList, other.partsLength, errorCode); + if(U_FAILURE(errorCode)) { + return false; + } + parts=partsList->a.getAlias(); + partsLength=other.partsLength; + } + if(other.numericValuesLength>0) { + if(numericValuesList==nullptr) { + numericValuesList=new MessagePatternDoubleList(); + if(numericValuesList==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return false; + } + numericValues=numericValuesList->a.getAlias(); + } + numericValuesList->copyFrom( + *other.numericValuesList, other.numericValuesLength, errorCode); + if(U_FAILURE(errorCode)) { + return false; + } + numericValues=numericValuesList->a.getAlias(); + numericValuesLength=other.numericValuesLength; + } + return true; +} + +MessagePattern::~MessagePattern() { + delete partsList; + delete numericValuesList; +} + +// MessagePattern API ------------------------------------------------------ *** + +MessagePattern & +MessagePattern::parse(const UnicodeString &pattern, UParseError *parseError, UErrorCode &errorCode) { + preParse(pattern, parseError, errorCode); + parseMessage(0, 0, 0, UMSGPAT_ARG_TYPE_NONE, parseError, errorCode); + postParse(); + return *this; +} + +MessagePattern & +MessagePattern::parseChoiceStyle(const UnicodeString &pattern, + UParseError *parseError, UErrorCode &errorCode) { + preParse(pattern, parseError, errorCode); + parseChoiceStyle(0, 0, parseError, errorCode); + postParse(); + return *this; +} + +MessagePattern & +MessagePattern::parsePluralStyle(const UnicodeString &pattern, + UParseError *parseError, UErrorCode &errorCode) { + preParse(pattern, parseError, errorCode); + parsePluralOrSelectStyle(UMSGPAT_ARG_TYPE_PLURAL, 0, 0, parseError, errorCode); + postParse(); + return *this; +} + +MessagePattern & +MessagePattern::parseSelectStyle(const UnicodeString &pattern, + UParseError *parseError, UErrorCode &errorCode) { + preParse(pattern, parseError, errorCode); + parsePluralOrSelectStyle(UMSGPAT_ARG_TYPE_SELECT, 0, 0, parseError, errorCode); + postParse(); + return *this; +} + +void +MessagePattern::clear() { + // Mostly the same as preParse(). + msg.remove(); + hasArgNames=hasArgNumbers=false; + needsAutoQuoting=false; + partsLength=0; + numericValuesLength=0; +} + +bool +MessagePattern::operator==(const MessagePattern &other) const { + if(this==&other) { + return true; + } + return + aposMode==other.aposMode && + msg==other.msg && + // parts.equals(o.parts) + partsLength==other.partsLength && + (partsLength==0 || partsList->equals(*other.partsList, partsLength)); + // No need to compare numericValues if msg and parts are the same. +} + +int32_t +MessagePattern::hashCode() const { + int32_t hash=(aposMode*37+msg.hashCode())*37+partsLength; + for(int32_t i=0; i0;) { + const Part &part=getPart(--i); + if(part.getType()==UMSGPAT_PART_TYPE_INSERT_CHAR) { + modified.insert(part.index, (char16_t)part.value); + } + } + return modified; +} + +double +MessagePattern::getNumericValue(const Part &part) const { + UMessagePatternPartType type=part.type; + if(type==UMSGPAT_PART_TYPE_ARG_INT) { + return part.value; + } else if(type==UMSGPAT_PART_TYPE_ARG_DOUBLE) { + return numericValues[part.value]; + } else { + return UMSGPAT_NO_NUMERIC_VALUE; + } +} + +/** + * Returns the "offset:" value of a PluralFormat argument, or 0 if none is specified. + * @param pluralStart the index of the first PluralFormat argument style part. (0..countParts()-1) + * @return the "offset:" value. + * @draft ICU 4.8 + */ +double +MessagePattern::getPluralOffset(int32_t pluralStart) const { + const Part &part=getPart(pluralStart); + if(Part::hasNumericValue(part.type)) { + return getNumericValue(part); + } else { + return 0; + } +} + +// MessagePattern::Part ---------------------------------------------------- *** + +bool +MessagePattern::Part::operator==(const Part &other) const { + if(this==&other) { + return true; + } + return + type==other.type && + index==other.index && + length==other.length && + value==other.value && + limitPartIndex==other.limitPartIndex; +} + +// MessagePattern parser --------------------------------------------------- *** + +void +MessagePattern::preParse(const UnicodeString &pattern, UParseError *parseError, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return; + } + if(parseError!=nullptr) { + parseError->line=0; + parseError->offset=0; + parseError->preContext[0]=0; + parseError->postContext[0]=0; + } + msg=pattern; + hasArgNames=hasArgNumbers=false; + needsAutoQuoting=false; + partsLength=0; + numericValuesLength=0; +} + +void +MessagePattern::postParse() { + if(partsList!=nullptr) { + parts=partsList->a.getAlias(); + } + if(numericValuesList!=nullptr) { + numericValues=numericValuesList->a.getAlias(); + } +} + +int32_t +MessagePattern::parseMessage(int32_t index, int32_t msgStartLength, + int32_t nestingLevel, UMessagePatternArgType parentType, + UParseError *parseError, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return 0; + } + if(nestingLevel>Part::MAX_VALUE) { + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + int32_t msgStart=partsLength; + addPart(UMSGPAT_PART_TYPE_MSG_START, index, msgStartLength, nestingLevel, errorCode); + index+=msgStartLength; + for(;;) { // while(index=msg.length()) { + break; + } + char16_t c=msg.charAt(index++); + if(c==u_apos) { + if(index==msg.length()) { + // The apostrophe is the last character in the pattern. + // Add a Part for auto-quoting. + addPart(UMSGPAT_PART_TYPE_INSERT_CHAR, index, 0, + u_apos, errorCode); // value=char to be inserted + needsAutoQuoting=true; + } else { + c=msg.charAt(index); + if(c==u_apos) { + // double apostrophe, skip the second one + addPart(UMSGPAT_PART_TYPE_SKIP_SYNTAX, index++, 1, 0, errorCode); + } else if( + aposMode==UMSGPAT_APOS_DOUBLE_REQUIRED || + c==u_leftCurlyBrace || c==u_rightCurlyBrace || + (parentType==UMSGPAT_ARG_TYPE_CHOICE && c==u_pipe) || + (UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(parentType) && c==u_pound) + ) { + // skip the quote-starting apostrophe + addPart(UMSGPAT_PART_TYPE_SKIP_SYNTAX, index-1, 1, 0, errorCode); + // find the end of the quoted literal text + for(;;) { + index=msg.indexOf(u_apos, index+1); + if(index>=0) { + if(/*(index+1)0 && c==u_rightCurlyBrace) || + (parentType==UMSGPAT_ARG_TYPE_CHOICE && c==u_pipe)) { + // Finish the message before the terminator. + // In a choice style, report the "}" substring only for the following ARG_LIMIT, + // not for this MSG_LIMIT. + int32_t limitLength=(parentType==UMSGPAT_ARG_TYPE_CHOICE && c==u_rightCurlyBrace) ? 0 : 1; + addLimitPart(msgStart, UMSGPAT_PART_TYPE_MSG_LIMIT, index-1, limitLength, + nestingLevel, errorCode); + if(parentType==UMSGPAT_ARG_TYPE_CHOICE) { + // Let the choice style parser see the '}' or '|'. + return index-1; + } else { + // continue parsing after the '}' + return index; + } + } // else: c is part of literal text + } + if(nestingLevel>0 && !inTopLevelChoiceMessage(nestingLevel, parentType)) { + setParseError(parseError, 0); // Unmatched '{' braces in message. + errorCode=U_UNMATCHED_BRACES; + return 0; + } + addLimitPart(msgStart, UMSGPAT_PART_TYPE_MSG_LIMIT, index, 0, nestingLevel, errorCode); + return index; +} + +int32_t +MessagePattern::parseArg(int32_t index, int32_t argStartLength, int32_t nestingLevel, + UParseError *parseError, UErrorCode &errorCode) { + int32_t argStart=partsLength; + UMessagePatternArgType argType=UMSGPAT_ARG_TYPE_NONE; + addPart(UMSGPAT_PART_TYPE_ARG_START, index, argStartLength, argType, errorCode); + if(U_FAILURE(errorCode)) { + return 0; + } + int32_t nameIndex=index=skipWhiteSpace(index+argStartLength); + if(index==msg.length()) { + setParseError(parseError, 0); // Unmatched '{' braces in message. + errorCode=U_UNMATCHED_BRACES; + return 0; + } + // parse argument name or number + index=skipIdentifier(index); + int32_t number=parseArgNumber(nameIndex, index); + if(number>=0) { + int32_t length=index-nameIndex; + if(length>Part::MAX_LENGTH || number>Part::MAX_VALUE) { + setParseError(parseError, nameIndex); // Argument number too large. + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + hasArgNumbers=true; + addPart(UMSGPAT_PART_TYPE_ARG_NUMBER, nameIndex, length, number, errorCode); + } else if(number==UMSGPAT_ARG_NAME_NOT_NUMBER) { + int32_t length=index-nameIndex; + if(length>Part::MAX_LENGTH) { + setParseError(parseError, nameIndex); // Argument name too long. + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + hasArgNames=true; + addPart(UMSGPAT_PART_TYPE_ARG_NAME, nameIndex, length, 0, errorCode); + } else { // number<-1 (ARG_NAME_NOT_VALID) + setParseError(parseError, nameIndex); // Bad argument syntax. + errorCode=U_PATTERN_SYNTAX_ERROR; + return 0; + } + index=skipWhiteSpace(index); + if(index==msg.length()) { + setParseError(parseError, 0); // Unmatched '{' braces in message. + errorCode=U_UNMATCHED_BRACES; + return 0; + } + char16_t c=msg.charAt(index); + if(c==u_rightCurlyBrace) { + // all done + } else if(c!=u_comma) { + setParseError(parseError, nameIndex); // Bad argument syntax. + errorCode=U_PATTERN_SYNTAX_ERROR; + return 0; + } else /* ',' */ { + // parse argument type: case-sensitive a-zA-Z + int32_t typeIndex=index=skipWhiteSpace(index+1); + while(indexPart::MAX_LENGTH) { + setParseError(parseError, nameIndex); // Argument type name too long. + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + argType=UMSGPAT_ARG_TYPE_SIMPLE; + if(length==6) { + // case-insensitive comparisons for complex-type names + if(isChoice(typeIndex)) { + argType=UMSGPAT_ARG_TYPE_CHOICE; + } else if(isPlural(typeIndex)) { + argType=UMSGPAT_ARG_TYPE_PLURAL; + } else if(isSelect(typeIndex)) { + argType=UMSGPAT_ARG_TYPE_SELECT; + } + } else if(length==13) { + if(isSelect(typeIndex) && isOrdinal(typeIndex+6)) { + argType=UMSGPAT_ARG_TYPE_SELECTORDINAL; + } + } + // change the ARG_START type from NONE to argType + partsList->a[argStart].value=(int16_t)argType; + if(argType==UMSGPAT_ARG_TYPE_SIMPLE) { + addPart(UMSGPAT_PART_TYPE_ARG_TYPE, typeIndex, length, 0, errorCode); + } + // look for an argument style (pattern) + if(c==u_rightCurlyBrace) { + if(argType!=UMSGPAT_ARG_TYPE_SIMPLE) { + setParseError(parseError, nameIndex); // No style field for complex argument. + errorCode=U_PATTERN_SYNTAX_ERROR; + return 0; + } + } else /* ',' */ { + ++index; + if(argType==UMSGPAT_ARG_TYPE_SIMPLE) { + index=parseSimpleStyle(index, parseError, errorCode); + } else if(argType==UMSGPAT_ARG_TYPE_CHOICE) { + index=parseChoiceStyle(index, nestingLevel, parseError, errorCode); + } else { + index=parsePluralOrSelectStyle(argType, index, nestingLevel, parseError, errorCode); + } + } + } + // Argument parsing stopped on the '}'. + addLimitPart(argStart, UMSGPAT_PART_TYPE_ARG_LIMIT, index, 1, argType, errorCode); + return index+1; +} + +int32_t +MessagePattern::parseSimpleStyle(int32_t index, UParseError *parseError, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return 0; + } + int32_t start=index; + int32_t nestedBraces=0; + while(index0) { + --nestedBraces; + } else { + int32_t length=--index-start; + if(length>Part::MAX_LENGTH) { + setParseError(parseError, start); // Argument style text too long. + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + addPart(UMSGPAT_PART_TYPE_ARG_STYLE, start, length, 0, errorCode); + return index; + } + } // c is part of literal text + } + setParseError(parseError, 0); // Unmatched '{' braces in message. + errorCode=U_UNMATCHED_BRACES; + return 0; +} + +int32_t +MessagePattern::parseChoiceStyle(int32_t index, int32_t nestingLevel, + UParseError *parseError, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return 0; + } + int32_t start=index; + index=skipWhiteSpace(index); + if(index==msg.length() || msg.charAt(index)==u_rightCurlyBrace) { + setParseError(parseError, 0); // Missing choice argument pattern. + errorCode=U_PATTERN_SYNTAX_ERROR; + return 0; + } + for(;;) { + // The choice argument style contains |-separated (number, separator, message) triples. + // Parse the number. + int32_t numberIndex=index; + index=skipDouble(index); + int32_t length=index-numberIndex; + if(length==0) { + setParseError(parseError, start); // Bad choice pattern syntax. + errorCode=U_PATTERN_SYNTAX_ERROR; + return 0; + } + if(length>Part::MAX_LENGTH) { + setParseError(parseError, numberIndex); // Choice number too long. + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + parseDouble(numberIndex, index, true, parseError, errorCode); // adds ARG_INT or ARG_DOUBLE + if(U_FAILURE(errorCode)) { + return 0; + } + // Parse the separator. + index=skipWhiteSpace(index); + if(index==msg.length()) { + setParseError(parseError, start); // Bad choice pattern syntax. + errorCode=U_PATTERN_SYNTAX_ERROR; + return 0; + } + char16_t c=msg.charAt(index); + if(!(c==u_pound || c==u_lessThan || c==u_lessOrEqual)) { // U+2264 is <= + setParseError(parseError, start); // Expected choice separator (#<\u2264) instead of c. + errorCode=U_PATTERN_SYNTAX_ERROR; + return 0; + } + addPart(UMSGPAT_PART_TYPE_ARG_SELECTOR, index, 1, 0, errorCode); + // Parse the message fragment. + index=parseMessage(++index, 0, nestingLevel+1, UMSGPAT_ARG_TYPE_CHOICE, parseError, errorCode); + if(U_FAILURE(errorCode)) { + return 0; + } + // parseMessage(..., CHOICE) returns the index of the terminator, or msg.length(). + if(index==msg.length()) { + return index; + } + if(msg.charAt(index)==u_rightCurlyBrace) { + if(!inMessageFormatPattern(nestingLevel)) { + setParseError(parseError, start); // Bad choice pattern syntax. + errorCode=U_PATTERN_SYNTAX_ERROR; + return 0; + } + return index; + } // else the terminator is '|' + index=skipWhiteSpace(index+1); + } +} + +int32_t +MessagePattern::parsePluralOrSelectStyle(UMessagePatternArgType argType, + int32_t index, int32_t nestingLevel, + UParseError *parseError, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return 0; + } + int32_t start=index; + UBool isEmpty=true; + UBool hasOther=false; + for(;;) { + // First, collect the selector looking for a small set of terminators. + // It would be a little faster to consider the syntax of each possible + // token right here, but that makes the code too complicated. + index=skipWhiteSpace(index); + UBool eos=index==msg.length(); + if(eos || msg.charAt(index)==u_rightCurlyBrace) { + if(eos==inMessageFormatPattern(nestingLevel)) { + setParseError(parseError, start); // Bad plural/select pattern syntax. + errorCode=U_PATTERN_SYNTAX_ERROR; + return 0; + } + if(!hasOther) { + setParseError(parseError, 0); // Missing 'other' keyword in plural/select pattern. + errorCode=U_DEFAULT_KEYWORD_MISSING; + return 0; + } + return index; + } + int32_t selectorIndex=index; + if(UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType) && msg.charAt(selectorIndex)==u_equal) { + // explicit-value plural selector: =double + index=skipDouble(index+1); + int32_t length=index-selectorIndex; + if(length==1) { + setParseError(parseError, start); // Bad plural/select pattern syntax. + errorCode=U_PATTERN_SYNTAX_ERROR; + return 0; + } + if(length>Part::MAX_LENGTH) { + setParseError(parseError, selectorIndex); // Argument selector too long. + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + addPart(UMSGPAT_PART_TYPE_ARG_SELECTOR, selectorIndex, length, 0, errorCode); + parseDouble(selectorIndex+1, index, false, + parseError, errorCode); // adds ARG_INT or ARG_DOUBLE + } else { + index=skipIdentifier(index); + int32_t length=index-selectorIndex; + if(length==0) { + setParseError(parseError, start); // Bad plural/select pattern syntax. + errorCode=U_PATTERN_SYNTAX_ERROR; + return 0; + } + // Note: The ':' in "offset:" is just beyond the skipIdentifier() range. + if( UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType) && length==6 && indexPart::MAX_LENGTH) { + setParseError(parseError, valueIndex); // Plural offset value too long. + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + parseDouble(valueIndex, index, false, + parseError, errorCode); // adds ARG_INT or ARG_DOUBLE + if(U_FAILURE(errorCode)) { + return 0; + } + isEmpty=false; + continue; // no message fragment after the offset + } else { + // normal selector word + if(length>Part::MAX_LENGTH) { + setParseError(parseError, selectorIndex); // Argument selector too long. + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + addPart(UMSGPAT_PART_TYPE_ARG_SELECTOR, selectorIndex, length, 0, errorCode); + if(0==msg.compare(selectorIndex, length, kOther, 0, 5)) { + hasOther=true; + } + } + } + if(U_FAILURE(errorCode)) { + return 0; + } + + // parse the message fragment following the selector + index=skipWhiteSpace(index); + if(index==msg.length() || msg.charAt(index)!=u_leftCurlyBrace) { + setParseError(parseError, selectorIndex); // No message fragment after plural/select selector. + errorCode=U_PATTERN_SYNTAX_ERROR; + return 0; + } + index=parseMessage(index, 1, nestingLevel+1, argType, parseError, errorCode); + if(U_FAILURE(errorCode)) { + return 0; + } + isEmpty=false; + } +} + +int32_t +MessagePattern::parseArgNumber(const UnicodeString &s, int32_t start, int32_t limit) { + // If the identifier contains only ASCII digits, then it is an argument _number_ + // and must not have leading zeros (except "0" itself). + // Otherwise it is an argument _name_. + if(start>=limit) { + return UMSGPAT_ARG_NAME_NOT_VALID; + } + int32_t number; + // Defer numeric errors until we know there are only digits. + UBool badNumber; + char16_t c=s.charAt(start++); + if(c==0x30) { + if(start==limit) { + return 0; + } else { + number=0; + badNumber=true; // leading zero + } + } else if(0x31<=c && c<=0x39) { + number=c-0x30; + badNumber=false; + } else { + return UMSGPAT_ARG_NAME_NOT_NUMBER; + } + while(start=INT32_MAX/10) { + badNumber=true; // overflow + } + number=number*10+(c-0x30); + } else { + return UMSGPAT_ARG_NAME_NOT_NUMBER; + } + } + // There are only ASCII digits. + if(badNumber) { + return UMSGPAT_ARG_NAME_NOT_VALID; + } else { + return number; + } +} + +void +MessagePattern::parseDouble(int32_t start, int32_t limit, UBool allowInfinity, + UParseError *parseError, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return; + } + U_ASSERT(start(Part::MAX_VALUE+isNegative)) { + break; // not a small-enough integer + } + if(index==limit) { + addPart(UMSGPAT_PART_TYPE_ARG_INT, start, limit-start, + isNegative!=0 ? -value : value, errorCode); + return; + } + c=msg.charAt(index++); + } + // Let Double.parseDouble() throw a NumberFormatException. + char numberChars[128]; + int32_t capacity=(int32_t)sizeof(numberChars); + int32_t length=limit-start; + if(length>=capacity) { + break; // number too long + } + msg.extract(start, length, numberChars, capacity, US_INV); + if((int32_t)uprv_strlen(numberChars)0x39 && c!=u_e && c!=u_E && c!=0x221e)) { + break; + } + ++index; + } + return index; +} + +UBool +MessagePattern::isArgTypeChar(UChar32 c) { + return (u_a<=c && c<=u_z) || (u_A<=c && c<=u_Z); +} + +UBool +MessagePattern::isChoice(int32_t index) { + char16_t c; + return + ((c=msg.charAt(index++))==u_c || c==u_C) && + ((c=msg.charAt(index++))==u_h || c==u_H) && + ((c=msg.charAt(index++))==u_o || c==u_O) && + ((c=msg.charAt(index++))==u_i || c==u_I) && + ((c=msg.charAt(index++))==u_c || c==u_C) && + ((c=msg.charAt(index))==u_e || c==u_E); +} + +UBool +MessagePattern::isPlural(int32_t index) { + char16_t c; + return + ((c=msg.charAt(index++))==u_p || c==u_P) && + ((c=msg.charAt(index++))==u_l || c==u_L) && + ((c=msg.charAt(index++))==u_u || c==u_U) && + ((c=msg.charAt(index++))==u_r || c==u_R) && + ((c=msg.charAt(index++))==u_a || c==u_A) && + ((c=msg.charAt(index))==u_l || c==u_L); +} + +UBool +MessagePattern::isSelect(int32_t index) { + char16_t c; + return + ((c=msg.charAt(index++))==u_s || c==u_S) && + ((c=msg.charAt(index++))==u_e || c==u_E) && + ((c=msg.charAt(index++))==u_l || c==u_L) && + ((c=msg.charAt(index++))==u_e || c==u_E) && + ((c=msg.charAt(index++))==u_c || c==u_C) && + ((c=msg.charAt(index))==u_t || c==u_T); +} + +UBool +MessagePattern::isOrdinal(int32_t index) { + char16_t c; + return + ((c=msg.charAt(index++))==u_o || c==u_O) && + ((c=msg.charAt(index++))==u_r || c==u_R) && + ((c=msg.charAt(index++))==u_d || c==u_D) && + ((c=msg.charAt(index++))==u_i || c==u_I) && + ((c=msg.charAt(index++))==u_n || c==u_N) && + ((c=msg.charAt(index++))==u_a || c==u_A) && + ((c=msg.charAt(index))==u_l || c==u_L); +} + +UBool +MessagePattern::inMessageFormatPattern(int32_t nestingLevel) { + return nestingLevel>0 || partsList->a[0].type==UMSGPAT_PART_TYPE_MSG_START; +} + +UBool +MessagePattern::inTopLevelChoiceMessage(int32_t nestingLevel, UMessagePatternArgType parentType) { + return + nestingLevel==1 && + parentType==UMSGPAT_ARG_TYPE_CHOICE && + partsList->a[0].type!=UMSGPAT_PART_TYPE_MSG_START; +} + +void +MessagePattern::addPart(UMessagePatternPartType type, int32_t index, int32_t length, + int32_t value, UErrorCode &errorCode) { + if(partsList->ensureCapacityForOneMore(partsLength, errorCode)) { + Part &part=partsList->a[partsLength++]; + part.type=type; + part.index=index; + part.length=(uint16_t)length; + part.value=(int16_t)value; + part.limitPartIndex=0; + } +} + +void +MessagePattern::addLimitPart(int32_t start, + UMessagePatternPartType type, int32_t index, int32_t length, + int32_t value, UErrorCode &errorCode) { + partsList->a[start].limitPartIndex=partsLength; + addPart(type, index, length, value, errorCode); +} + +void +MessagePattern::addArgDoublePart(double numericValue, int32_t start, int32_t length, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return; + } + int32_t numericIndex=numericValuesLength; + if(numericValuesList==nullptr) { + numericValuesList=new MessagePatternDoubleList(); + if(numericValuesList==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + } else if(!numericValuesList->ensureCapacityForOneMore(numericValuesLength, errorCode)) { + return; + } else { + if(numericIndex>Part::MAX_VALUE) { + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return; + } + } + numericValuesList->a[numericValuesLength++]=numericValue; + addPart(UMSGPAT_PART_TYPE_ARG_DOUBLE, start, length, numericIndex, errorCode); +} + +void +MessagePattern::setParseError(UParseError *parseError, int32_t index) { + if(parseError==nullptr) { + return; + } + parseError->offset=index; + + // Set preContext to some of msg before index. + // Avoid splitting a surrogate pair. + int32_t length=index; + if(length>=U_PARSE_CONTEXT_LEN) { + length=U_PARSE_CONTEXT_LEN-1; + if(length>0 && U16_IS_TRAIL(msg[index-length])) { + --length; + } + } + msg.extract(index-length, length, parseError->preContext); + parseError->preContext[length]=0; + + // Set postContext to some of msg starting at index. + length=msg.length()-index; + if(length>=U_PARSE_CONTEXT_LEN) { + length=U_PARSE_CONTEXT_LEN-1; + if(length>0 && U16_IS_LEAD(msg[index+length-1])) { + --length; + } + } + msg.extract(index, length, parseError->postContext); + parseError->postContext[length]=0; +} + +// MessageImpl ------------------------------------------------------------- *** + +void +MessageImpl::appendReducedApostrophes(const UnicodeString &s, int32_t start, int32_t limit, + UnicodeString &sb) { + int32_t doubleApos=-1; + for(;;) { + int32_t i=s.indexOf(u_apos, start); + if(i<0 || i>=limit) { + sb.append(s, start, limit-start); + break; + } + if(i==doubleApos) { + // Double apostrophe at start-1 and start==i, append one. + sb.append(u_apos); + ++start; + doubleApos=-1; + } else { + // Append text between apostrophes and skip this one. + sb.append(s, start, i-start); + doubleApos=start=i+1; + } + } +} + +// Ported from second half of ICU4J SelectFormat.format(String). +UnicodeString & +MessageImpl::appendSubMessageWithoutSkipSyntax(const MessagePattern &msgPattern, + int32_t msgStart, + UnicodeString &result) { + const UnicodeString &msgString=msgPattern.getPatternString(); + int32_t prevIndex=msgPattern.getPart(msgStart).getLimit(); + for(int32_t i=msgStart;;) { + const MessagePattern::Part &part=msgPattern.getPart(++i); + UMessagePatternPartType type=part.getType(); + int32_t index=part.getIndex(); + if(type==UMSGPAT_PART_TYPE_MSG_LIMIT) { + return result.append(msgString, prevIndex, index-prevIndex); + } else if(type==UMSGPAT_PART_TYPE_SKIP_SYNTAX) { + result.append(msgString, prevIndex, index-prevIndex); + prevIndex=part.getLimit(); + } else if(type==UMSGPAT_PART_TYPE_ARG_START) { + result.append(msgString, prevIndex, index-prevIndex); + prevIndex=index; + i=msgPattern.getLimitPartIndex(i); + index=msgPattern.getPart(i).getLimit(); + appendReducedApostrophes(msgString, prevIndex, index, result); + prevIndex=index; + } + } +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/common/mlbe.cpp b/deps/icu-small/source/common/mlbe.cpp new file mode 100644 index 00000000000000..437a8ae48a8496 --- /dev/null +++ b/deps/icu-small/source/common/mlbe.cpp @@ -0,0 +1,270 @@ +// © 2022 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "cmemory.h" +#include "mlbe.h" +#include "uassert.h" +#include "ubrkimpl.h" +#include "unicode/resbund.h" +#include "unicode/udata.h" +#include "unicode/utf16.h" +#include "uresimp.h" +#include "util.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +enum class ModelIndex { kUWStart = 0, kBWStart = 6, kTWStart = 9 }; + +MlBreakEngine::MlBreakEngine(const UnicodeSet &digitOrOpenPunctuationOrAlphabetSet, + const UnicodeSet &closePunctuationSet, UErrorCode &status) + : fDigitOrOpenPunctuationOrAlphabetSet(digitOrOpenPunctuationOrAlphabetSet), + fClosePunctuationSet(closePunctuationSet), + fNegativeSum(0) { + if (U_FAILURE(status)) { + return; + } + loadMLModel(status); +} + +MlBreakEngine::~MlBreakEngine() {} + +int32_t MlBreakEngine::divideUpRange(UText *inText, int32_t rangeStart, int32_t rangeEnd, + UVector32 &foundBreaks, const UnicodeString &inString, + const LocalPointer &inputMap, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return 0; + } + if (rangeStart >= rangeEnd) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + UVector32 boundary(inString.countChar32() + 1, status); + if (U_FAILURE(status)) { + return 0; + } + int32_t numBreaks = 0; + int32_t codePointLength = inString.countChar32(); + // The ML algorithm groups six char and evaluates whether the 4th char is a breakpoint. + // In each iteration, it evaluates the 4th char and then moves forward one char like a sliding + // window. Initially, the first six values in the indexList are [-1, -1, 0, 1, 2, 3]. After + // moving forward, finally the last six values in the indexList are + // [length-4, length-3, length-2, length-1, -1, -1]. The "+4" here means four extra "-1". + int32_t indexSize = codePointLength + 4; + int32_t *indexList = (int32_t *)uprv_malloc(indexSize * sizeof(int32_t)); + if (indexList == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + int32_t numCodeUnits = initIndexList(inString, indexList, status); + + // Add a break for the start. + boundary.addElement(0, status); + numBreaks++; + if (U_FAILURE(status)) return 0; + + for (int32_t idx = 0; idx + 1 < codePointLength && U_SUCCESS(status); idx++) { + numBreaks = + evaluateBreakpoint(inString, indexList, idx, numCodeUnits, numBreaks, boundary, status); + if (idx + 4 < codePointLength) { + indexList[idx + 6] = numCodeUnits; + numCodeUnits += U16_LENGTH(inString.char32At(indexList[idx + 6])); + } + } + uprv_free(indexList); + + if (U_FAILURE(status)) return 0; + + // Add a break for the end if there is not one there already. + if (boundary.lastElementi() != inString.countChar32()) { + boundary.addElement(inString.countChar32(), status); + numBreaks++; + } + + int32_t prevCPPos = -1; + int32_t prevUTextPos = -1; + int32_t correctedNumBreaks = 0; + for (int32_t i = 0; i < numBreaks; i++) { + int32_t cpPos = boundary.elementAti(i); + int32_t utextPos = inputMap.isValid() ? inputMap->elementAti(cpPos) : cpPos + rangeStart; + U_ASSERT(cpPos > prevCPPos); + U_ASSERT(utextPos >= prevUTextPos); + + if (utextPos > prevUTextPos) { + if (utextPos != rangeStart || + (utextPos > 0 && + fClosePunctuationSet.contains(utext_char32At(inText, utextPos - 1)))) { + foundBreaks.push(utextPos, status); + correctedNumBreaks++; + } + } else { + // Normalization expanded the input text, the dictionary found a boundary + // within the expansion, giving two boundaries with the same index in the + // original text. Ignore the second. See ticket #12918. + --numBreaks; + } + prevCPPos = cpPos; + prevUTextPos = utextPos; + } + (void)prevCPPos; // suppress compiler warnings about unused variable + + UChar32 nextChar = utext_char32At(inText, rangeEnd); + if (!foundBreaks.isEmpty() && foundBreaks.peeki() == rangeEnd) { + // In phrase breaking, there has to be a breakpoint between Cj character and + // the number/open punctuation. + // E.g. る文字「そうだ、京都」->る▁文字▁「そうだ、▁京都」-> breakpoint between 字 and「 + // E.g. 乗車率90%程度だろうか -> 乗車▁率▁90%▁程度だろうか -> breakpoint between 率 and 9 + // E.g. しかもロゴがUnicode! -> しかも▁ロゴが▁Unicode!-> breakpoint between が and U + if (!fDigitOrOpenPunctuationOrAlphabetSet.contains(nextChar)) { + foundBreaks.popi(); + correctedNumBreaks--; + } + } + + return correctedNumBreaks; +} + +int32_t MlBreakEngine::evaluateBreakpoint(const UnicodeString &inString, int32_t *indexList, + int32_t startIdx, int32_t numCodeUnits, int32_t numBreaks, + UVector32 &boundary, UErrorCode &status) const { + if (U_FAILURE(status)) { + return numBreaks; + } + int32_t start = 0, end = 0; + int32_t score = fNegativeSum; + + for (int i = 0; i < 6; i++) { + // UW1 ~ UW6 + start = startIdx + i; + if (indexList[start] != -1) { + end = (indexList[start + 1] != -1) ? indexList[start + 1] : numCodeUnits; + score += fModel[static_cast(ModelIndex::kUWStart) + i].geti( + inString.tempSubString(indexList[start], end - indexList[start])); + } + } + for (int i = 0; i < 3; i++) { + // BW1 ~ BW3 + start = startIdx + i + 1; + if (indexList[start] != -1 && indexList[start + 1] != -1) { + end = (indexList[start + 2] != -1) ? indexList[start + 2] : numCodeUnits; + score += fModel[static_cast(ModelIndex::kBWStart) + i].geti( + inString.tempSubString(indexList[start], end - indexList[start])); + } + } + for (int i = 0; i < 4; i++) { + // TW1 ~ TW4 + start = startIdx + i; + if (indexList[start] != -1 && indexList[start + 1] != -1 && indexList[start + 2] != -1) { + end = (indexList[start + 3] != -1) ? indexList[start + 3] : numCodeUnits; + score += fModel[static_cast(ModelIndex::kTWStart) + i].geti( + inString.tempSubString(indexList[start], end - indexList[start])); + } + } + + if (score > 0) { + boundary.addElement(startIdx + 1, status); + numBreaks++; + } + return numBreaks; +} + +int32_t MlBreakEngine::initIndexList(const UnicodeString &inString, int32_t *indexList, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return 0; + } + int32_t index = 0; + int32_t length = inString.countChar32(); + // Set all (lenght+4) items inside indexLength to -1 presuming -1 is 4 bytes of 0xff. + uprv_memset(indexList, 0xff, (length + 4) * sizeof(int32_t)); + if (length > 0) { + indexList[2] = 0; + index = U16_LENGTH(inString.char32At(0)); + if (length > 1) { + indexList[3] = index; + index += U16_LENGTH(inString.char32At(index)); + if (length > 2) { + indexList[4] = index; + index += U16_LENGTH(inString.char32At(index)); + if (length > 3) { + indexList[5] = index; + index += U16_LENGTH(inString.char32At(index)); + } + } + } + } + return index; +} + +void MlBreakEngine::loadMLModel(UErrorCode &error) { + // BudouX's model consists of thirteen categories, each of which is make up of pairs of the + // feature and its score. As integrating it into jaml.txt, we define thirteen kinds of key and + // value to represent the feature and the corresponding score respectively. + + if (U_FAILURE(error)) return; + + UnicodeString key; + StackUResourceBundle stackTempBundle; + ResourceDataValue modelKey; + + LocalUResourceBundlePointer rbp(ures_openDirect(U_ICUDATA_BRKITR, "jaml", &error)); + UResourceBundle *rb = rbp.getAlias(); + if (U_FAILURE(error)) return; + + int32_t index = 0; + initKeyValue(rb, "UW1Keys", "UW1Values", fModel[index++], error); + initKeyValue(rb, "UW2Keys", "UW2Values", fModel[index++], error); + initKeyValue(rb, "UW3Keys", "UW3Values", fModel[index++], error); + initKeyValue(rb, "UW4Keys", "UW4Values", fModel[index++], error); + initKeyValue(rb, "UW5Keys", "UW5Values", fModel[index++], error); + initKeyValue(rb, "UW6Keys", "UW6Values", fModel[index++], error); + initKeyValue(rb, "BW1Keys", "BW1Values", fModel[index++], error); + initKeyValue(rb, "BW2Keys", "BW2Values", fModel[index++], error); + initKeyValue(rb, "BW3Keys", "BW3Values", fModel[index++], error); + initKeyValue(rb, "TW1Keys", "TW1Values", fModel[index++], error); + initKeyValue(rb, "TW2Keys", "TW2Values", fModel[index++], error); + initKeyValue(rb, "TW3Keys", "TW3Values", fModel[index++], error); + initKeyValue(rb, "TW4Keys", "TW4Values", fModel[index++], error); + fNegativeSum /= 2; +} + +void MlBreakEngine::initKeyValue(UResourceBundle *rb, const char *keyName, const char *valueName, + Hashtable &model, UErrorCode &error) { + int32_t keySize = 0; + int32_t valueSize = 0; + int32_t stringLength = 0; + UnicodeString key; + StackUResourceBundle stackTempBundle; + ResourceDataValue modelKey; + + // get modelValues + LocalUResourceBundlePointer modelValue(ures_getByKey(rb, valueName, nullptr, &error)); + const int32_t *value = ures_getIntVector(modelValue.getAlias(), &valueSize, &error); + if (U_FAILURE(error)) return; + + // get modelKeys + ures_getValueWithFallback(rb, keyName, stackTempBundle.getAlias(), modelKey, error); + ResourceArray stringArray = modelKey.getArray(error); + keySize = stringArray.getSize(); + if (U_FAILURE(error)) return; + + for (int32_t idx = 0; idx < keySize; idx++) { + stringArray.getValue(idx, modelKey); + key = UnicodeString(modelKey.getString(stringLength, error)); + if (U_SUCCESS(error)) { + U_ASSERT(idx < valueSize); + fNegativeSum -= value[idx]; + model.puti(key, value[idx], error); + } + } +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ diff --git a/deps/icu-small/source/common/mlbe.h b/deps/icu-small/source/common/mlbe.h new file mode 100644 index 00000000000000..da753f02331c65 --- /dev/null +++ b/deps/icu-small/source/common/mlbe.h @@ -0,0 +1,116 @@ +// © 2022 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#ifndef MLBREAKENGINE_H +#define MLBREAKENGINE_H + +#include "hash.h" +#include "unicode/resbund.h" +#include "unicode/uniset.h" +#include "unicode/utext.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +#if !UCONFIG_NO_BREAK_ITERATION + +/** + * A machine learning break engine for the phrase breaking in Japanese. + */ +class MlBreakEngine : public UMemory { + public: + /** + * Constructor. + * + * @param digitOrOpenPunctuationOrAlphabetSet An UnicodeSet with the digit, open punctuation and + * alphabet. + * @param closePunctuationSet An UnicodeSet with close punctuation. + * @param status Information on any errors encountered. + */ + MlBreakEngine(const UnicodeSet &digitOrOpenPunctuationOrAlphabetSet, + const UnicodeSet &closePunctuationSet, UErrorCode &status); + + /** + * Virtual destructor. + */ + virtual ~MlBreakEngine(); + + public: + /** + * Divide up a range of characters handled by this break engine. + * + * @param inText A UText representing the text + * @param rangeStart The start of the range of the characters + * @param rangeEnd The end of the range of the characters + * @param foundBreaks Output of C array of int32_t break positions, or 0 + * @param inString The normalized string of text ranging from rangeStart to rangeEnd + * @param inputMap The vector storing the native index of inText + * @param status Information on any errors encountered. + * @return The number of breaks found + */ + int32_t divideUpRange(UText *inText, int32_t rangeStart, int32_t rangeEnd, + UVector32 &foundBreaks, const UnicodeString &inString, + const LocalPointer &inputMap, UErrorCode &status) const; + + private: + /** + * Load the machine learning's model file. + * + * @param error Information on any errors encountered. + */ + void loadMLModel(UErrorCode &error); + + /** + * In the machine learning's model file, specify the name of the key and value to load the + * corresponding feature and its score. + * + * @param rb A ResouceBundle corresponding to the model file. + * @param keyName The kay name in the model file. + * @param valueName The value name in the model file. + * @param model A hashtable to store the pairs of the feature and its score. + * @param error Information on any errors encountered. + */ + void initKeyValue(UResourceBundle *rb, const char *keyName, const char *valueName, + Hashtable &model, UErrorCode &error); + + /** + * Initialize the index list from the input string. + * + * @param inString A input string to be segmented. + * @param indexList A code unit index list of inString. + * @param status Information on any errors encountered. + * @return The number of code units of the first four characters in inString. + */ + int32_t initIndexList(const UnicodeString &inString, int32_t *indexList, + UErrorCode &status) const; + + /** + * Evaluate whether the index is a potential breakpoint. + * + * @param inString A input string to be segmented. + * @param indexList A code unit index list of the inString. + * @param startIdx The start index of the indexList. + * @param numCodeUnits The current code unit boundary of the indexList. + * @param numBreaks The accumulated number of breakpoints. + * @param boundary A vector including the index of the breakpoint. + * @param status Information on any errors encountered. + * @return The number of breakpoints + */ + int32_t evaluateBreakpoint(const UnicodeString &inString, int32_t *indexList, int32_t startIdx, + int32_t numCodeUnits, int32_t numBreaks, UVector32 &boundary, + UErrorCode &status) const; + + void printUnicodeString(const UnicodeString &s) const; + + UnicodeSet fDigitOrOpenPunctuationOrAlphabetSet; + UnicodeSet fClosePunctuationSet; + Hashtable fModel[13]; // {UW1, UW2, ... UW6, BW1, ... BW3, TW1, TW2, ... TW4} 6+3+4= 13 + int32_t fNegativeSum; +}; + +#endif + +U_NAMESPACE_END + +/* MLBREAKENGINE_H */ +#endif diff --git a/deps/icu-small/source/common/msvcres.h b/deps/icu-small/source/common/msvcres.h index d71b5ac922ab85..19fa6802cd8d56 100644 --- a/deps/icu-small/source/common/msvcres.h +++ b/deps/icu-small/source/common/msvcres.h @@ -1,25 +1,25 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -//{{NO_DEPENDENCIES}} -// Copyright (c) 2003-2010 International Business Machines -// Corporation and others. All Rights Reserved. -// -// Used by common.rc and other .rc files. -//Do not edit with Microsoft Developer Studio because it will modify this -//header the wrong way. This is here to prevent Visual Studio .NET from -//unnessarily building the resource files when it's not needed. -// - -/* -These are defined before unicode/uversion.h in order to prevent -STLPort's broken stddef.h from being used when rc.exe parses this file. -*/ -#define _STLP_OUTERMOST_HEADER_ID 0 -#define _STLP_WINCE 1 - -#include "unicode/uversion.h" - -#define ICU_WEBSITE "https://icu.unicode.org/" -#define ICU_COMPANY "The ICU Project" -#define ICU_PRODUCT_PREFIX "ICU" -#define ICU_PRODUCT "International Components for Unicode" +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +//{{NO_DEPENDENCIES}} +// Copyright (c) 2003-2010 International Business Machines +// Corporation and others. All Rights Reserved. +// +// Used by common.rc and other .rc files. +//Do not edit with Microsoft Developer Studio because it will modify this +//header the wrong way. This is here to prevent Visual Studio .NET from +//unnessarily building the resource files when it's not needed. +// + +/* +These are defined before unicode/uversion.h in order to prevent +STLPort's broken stddef.h from being used when rc.exe parses this file. +*/ +#define _STLP_OUTERMOST_HEADER_ID 0 +#define _STLP_WINCE 1 + +#include "unicode/uversion.h" + +#define ICU_WEBSITE "https://icu.unicode.org/" +#define ICU_COMPANY "The ICU Project" +#define ICU_PRODUCT_PREFIX "ICU" +#define ICU_PRODUCT "International Components for Unicode" diff --git a/deps/icu-small/source/common/mutex.h b/deps/icu-small/source/common/mutex.h index 44b1f90ba04dd2..5ffdbe21901a3c 100644 --- a/deps/icu-small/source/common/mutex.h +++ b/deps/icu-small/source/common/mutex.h @@ -1,77 +1,77 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1997-2013, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -*/ -//---------------------------------------------------------------------------- -// File: mutex.h -// -// Lightweight C++ wrapper for umtx_ C mutex functions -// -// Author: Alan Liu 1/31/97 -// History: -// 06/04/97 helena Updated setImplementation as per feedback from 5/21 drop. -// 04/07/1999 srl refocused as a thin wrapper -// -//---------------------------------------------------------------------------- -#ifndef MUTEX_H -#define MUTEX_H - -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "umutex.h" - -U_NAMESPACE_BEGIN - -/** - * Mutex is a helper class for convenient locking and unlocking of a UMutex. - * - * Creating a local scope Mutex will lock a UMutex, holding the lock until the Mutex - * goes out of scope. - * - * If no UMutex is specified, the ICU global mutex is implied. - * - * For example: - * - * static UMutex myMutex; - * - * void Function(int arg1, int arg2) - * { - * static Object* foo; // Shared read-write object - * Mutex mutex(&myMutex); // or no args for the global lock - * foo->Method(); - * // When 'mutex' goes out of scope and gets destroyed here, the lock is released - * } - * - * Note: Do NOT use the form 'Mutex mutex();' as that merely forward-declares a function - * returning a Mutex. This is a common mistake which silently slips through the - * compiler!! - */ - -class U_COMMON_API Mutex : public UMemory { -public: - Mutex(UMutex *mutex = nullptr) : fMutex(mutex) { - umtx_lock(fMutex); - } - ~Mutex() { - umtx_unlock(fMutex); - } - - Mutex(const Mutex &other) = delete; // forbid assigning of this class - Mutex &operator=(const Mutex &other) = delete; // forbid copying of this class - void *operator new(size_t s) = delete; // forbid heap allocation. Locals only. - -private: - UMutex *fMutex; -}; - - -U_NAMESPACE_END - -#endif //_MUTEX_ -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1997-2013, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +*/ +//---------------------------------------------------------------------------- +// File: mutex.h +// +// Lightweight C++ wrapper for umtx_ C mutex functions +// +// Author: Alan Liu 1/31/97 +// History: +// 06/04/97 helena Updated setImplementation as per feedback from 5/21 drop. +// 04/07/1999 srl refocused as a thin wrapper +// +//---------------------------------------------------------------------------- +#ifndef MUTEX_H +#define MUTEX_H + +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "umutex.h" + +U_NAMESPACE_BEGIN + +/** + * Mutex is a helper class for convenient locking and unlocking of a UMutex. + * + * Creating a local scope Mutex will lock a UMutex, holding the lock until the Mutex + * goes out of scope. + * + * If no UMutex is specified, the ICU global mutex is implied. + * + * For example: + * + * static UMutex myMutex; + * + * void Function(int arg1, int arg2) + * { + * static Object* foo; // Shared read-write object + * Mutex mutex(&myMutex); // or no args for the global lock + * foo->Method(); + * // When 'mutex' goes out of scope and gets destroyed here, the lock is released + * } + * + * Note: Do NOT use the form 'Mutex mutex();' as that merely forward-declares a function + * returning a Mutex. This is a common mistake which silently slips through the + * compiler!! + */ + +class U_COMMON_API Mutex : public UMemory { +public: + Mutex(UMutex *mutex = nullptr) : fMutex(mutex) { + umtx_lock(fMutex); + } + ~Mutex() { + umtx_unlock(fMutex); + } + + Mutex(const Mutex &other) = delete; // forbid assigning of this class + Mutex &operator=(const Mutex &other) = delete; // forbid copying of this class + void *operator new(size_t s) = delete; // forbid heap allocation. Locals only. + +private: + UMutex *fMutex; +}; + + +U_NAMESPACE_END + +#endif //_MUTEX_ +//eof diff --git a/deps/icu-small/source/common/norm2_nfc_data.h b/deps/icu-small/source/common/norm2_nfc_data.h index ebe3e6ba90657a..68c2edbd4d6595 100644 --- a/deps/icu-small/source/common/norm2_nfc_data.h +++ b/deps/icu-small/source/common/norm2_nfc_data.h @@ -1,1154 +1,1154 @@ -// Copyright (C) 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// Copyright (C) 1999-2016, International Business Machines -// Corporation and others. All Rights Reserved. -// -// file name: norm2_nfc_data.h -// -// machine-generated by: icu/source/tools/gennorm2/n2builder.cpp - -#ifdef INCLUDED_FROM_NORMALIZER2_CPP - -static const UVersionInfo norm2_nfc_data_formatVersion={4,0,0,0}; -static const UVersionInfo norm2_nfc_data_dataVersion={0xf,0,0,0}; - -static const int32_t norm2_nfc_data_indexes[Normalizer2Impl::IX_COUNT]={ -0x50,0x4cb8,0x8920,0x8a20,0x8a20,0x8a20,0x8a20,0x8a20,0xc0,0x300,0xae2,0x29e0,0x3c66,0xfc00,0x1288,0x3b9c, -0x3c34,0x3c66,0x300,0 -}; - -static const uint16_t norm2_nfc_data_trieIndex[1788]={ -0,0x40,0x7b,0xbb,0xfb,0x13a,0x17a,0x1b2,0x1f2,0x226,0x254,0x226,0x294,0x2d4,0x313,0x353, -0x393,0x3d2,0x40f,0x44e,0x226,0x226,0x488,0x4c8,0x4f8,0x530,0x226,0x570,0x59f,0x5de,0x226,0x5f3, -0x631,0x65f,0x687,0x6bd,0x6fd,0x73a,0x75a,0x799,0x7d8,0x815,0x834,0x871,0x75a,0x8aa,0x8d8,0x917, -0x834,0x951,0x968,0x9a8,0x9bf,0x9fe,0x226,0xa34,0xa54,0xa8f,0xa9b,0xad6,0xafe,0xb3b,0xb7b,0xbb5, -0xbd0,0x226,0xc0b,0x226,0xc4b,0xc6a,0xca0,0xcdd,0x226,0x226,0x226,0x226,0x226,0xd00,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0xd2c,0x226,0x226,0xd61, -0x226,0x226,0xd7f,0x226,0xda9,0x226,0x226,0x226,0xde5,0xe05,0xe45,0xe84,0xebf,0xeff,0xf33,0xf5f, -0x839,0x226,0x226,0xf93,0x226,0x226,0x226,0xfd3,0x1013,0x1053,0x1093,0x10d3,0x1113,0x1153,0x1193,0x11d3, -0x1213,0x226,0x226,0x1243,0x1274,0x226,0x12a4,0x12d7,0x1314,0x1353,0x1393,0x13c9,0x13f7,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1422,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0xcee,0x226,0x143f,0x226,0x147f,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x14bf,0x14f9,0x1537,0x1577,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x15b6,0x15f4,0x1614,0x226,0x226,0x226,0x226, -0x164e,0x226,0x226,0x1676,0x16a8,0x16d6,0x83d,0x16e9,0x226,0x226,0x16f9,0x1739,0x226,0x226,0x226,0x1451, -0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781, -0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791, -0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785, -0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779, -0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789, -0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d, -0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d, -0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781, -0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791, -0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785, -0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x17c5,0x226, -0x1805,0x1840,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x1880,0x18c0,0x1900,0x1940,0x1980,0x19c0,0x1a00,0x1a40,0x1a63,0x1aa3,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1ac3,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x67e,0x68e,0x6a6,0x6c5,0x6da,0x6da,0x6da,0x6de,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0xc0b,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x54f,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x40c, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1af6,0x226,0x226,0x1b06,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0xdf7,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1b16,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1607,0x226,0x226,0x226,0x226,0x66b,0x226,0x226,0x226, -0x226,0x1b20,0x54f,0x226,0x226,0x1b30,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x81c,0x226,0x226, -0x1b40,0x226,0x1b50,0x1b5d,0x1b69,0x226,0x226,0x226,0x226,0x414,0x226,0x1b74,0x1b84,0x226,0x226,0x226, -0x811,0x226,0x226,0x226,0x226,0x1b94,0x226,0x226,0x226,0x1b9f,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x1ba6,0x226,0x226,0x226,0x226,0x1bb1,0x1bc0,0x927,0x1bce,0x412,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x1bdc,0x7c9,0x226,0x226,0x226,0x226,0x226,0x1bec,0x1bfb,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x907,0x1c03,0x1c13,0x226, -0x226,0x226,0x9eb,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1c1d,0x226,0x226,0x226,0x226,0x226, -0x226,0x817,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1c1a, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1c2d, -0x811,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x87e,0x226,0x226,0x226,0x81e,0x81b, -0x226,0x226,0x226,0x226,0x819,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x9eb,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0xc05,0x226,0x226,0x226, -0x226,0x81b,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0xc08,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x1c3d,0x226,0x226,0x226,0xf2c,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1c4d,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x1c4f,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1c5e,0x1c6e,0x1c7c,0x1c89,0x226, -0x1c95,0x1ca3,0x1cb3,0x226,0x226,0x226,0x226,0xd1b,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x1cc3,0x1ccb,0x1cd9,0x226,0x226,0x226,0x226,0x226,0x4f9,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0xf2c,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x7c9,0x226,0x226,0x226,0x4fc,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1ce4,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1cf4,0x226,0x226,0x226,0x226, -0x226,0x226,0x1d00,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1d10,0x1d20, -0x1d30,0x1d40,0x1d50,0x1d60,0x1d70,0x1d80,0x1d90,0x1da0,0x1db0,0x1dc0,0x1dd0,0x1de0,0x1df0,0x1e00,0x1e10,0x1e20, -0x1e30,0x1e40,0x1e50,0x1e60,0x1e70,0x1e80,0x1e90,0x1ea0,0x1eb0,0x1ec0,0x1ed0,0x1ee0,0x1ef0,0x1f00,0x1f10,0x1f20, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, -0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x408,0x428, -0xc4,0xc4,0xc4,0x448,0x457,0x46d,0x489,0x4a6,0x4c2,0x4df,0x4fc,0x51b,0x538,0x552,0xc4,0xc4, -0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4, -0xc4,0xc4,0xc4,0x567,0xc4,0x57b,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4, -0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4, -0xc4,0xc4,0xc4,0xc4,0x59b,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0x5a6,0x5c3,0xc4, -0xc4,0xc4,0xc4,0xc4,0xc4,0x5e3,0x5f9,0x60b,0xc4,0x61e,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4, -0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4, -0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0x63e,0x65e -}; - -static const uint16_t norm2_nfc_data_trieData[7984]={ -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,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,0x5e8,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,0x128a,0x1290,0xae4,0x1296,0xafa, -0xb04,0x5f4,0xb0e,0x129c,0x12a2,0xb18,0x12a8,0x12ae,0x12b4,0x12ba,0xb2e,1,0x12c0,0x12c6,0x12cc,0xb38, -0xb4e,0xb60,1,0x5fc,0x12d2,0x12d8,0x12de,0xb6a,0x12e4,1,1,0x12ea,0x12f0,0xb80,0x12f6,0xb96, -0xba0,0x600,0xbaa,0x12fc,0x1302,0xbb4,0x1308,0x130e,0x1314,0x131a,0xbca,1,0x1320,0x1326,0x132c,0xbd4, -0xbea,0xbfc,1,0x608,0x1332,0x1338,0x133e,0xc06,0x1344,1,0x134a,0x1350,0x1356,0xc1c,0xc32,0x135d, -0x1363,0x1368,0x136e,0x1374,0x137a,0x1380,0x1386,0x138c,0x1392,0x1398,0x139e,1,1,0xc48,0xc56,0x13a4, -0x13aa,0x13b0,0x13b6,0x13bd,0x13c3,0x13c8,0x13ce,0x13d4,0x13da,0x13e0,0x13e6,0x13ec,0x13f2,0x13f9,0x13ff,0x1404, -0x140a,1,1,0x1410,0x1416,0x141c,0x1422,0x1428,0x142e,0x1435,0x143b,0x1440,1,1,1,0x1447, -0x144d,0x1453,0x1459,1,0x145e,0x1464,0x146b,0x1471,0x1476,0x147c,1,1,1,0x1482,0x1488,0x148f, -0x1495,0x149a,0x14a0,1,1,1,0xc64,0xc72,0x14a6,0x14ac,0x14b2,0x14b8,1,1,0x14be,0x14c4, -0x14cb,0x14d1,0x14d6,0x14dc,0xc80,0xc8a,0x14e2,0x14e8,0x14ef,0x14f5,0xc94,0xc9e,0x14fb,0x1501,0x1506,0x150c, -1,1,0xca8,0xcb2,0xcbc,0xcc6,0x1512,0x1518,0x151e,0x1524,0x152a,0x1530,0x1537,0x153d,0x1542,0x1548, -0x154e,0x1554,0x155a,0x1560,0x1566,0x156c,0x1572,0x1578,0x157e,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,0xcd0,0xcea,1,1,1,1, -1,1,1,1,1,1,1,1,1,0xd04,0xd1e,1,1,1,1,1, -1,0x610,1,1,1,1,1,1,1,1,1,1,1,1,1,0x1584, -0x158a,0x1590,0x1596,0x159c,0x15a2,0x15a8,0x15ae,0x15b6,0x15c0,0x15ca,0x15d4,0x15de,0x15e8,0x15f2,0x15fc,1, -0x1606,0x1610,0x161a,0x1624,0x162d,0x1633,1,1,0x1638,0x163e,0x1644,0x164a,0xd38,0xd42,0x1653,0x165d, -0x1665,0x166b,0x1671,1,1,1,0x1676,0x167c,1,1,0x1682,0x1688,0x1690,0x169a,0x16a3,0x16a9, -0x16af,0x16b5,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,0x175c,1,1, -0x1762,0x1768,1,1,1,1,1,1,0xd4c,0xd56,0xd60,0xd6a,0x1770,0x177a,0x1784,0x178e, -0xd74,0xd7e,0x1798,0x17a2,0x17aa,0x17b0,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,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,0x3c36,0x3c3c,0xfdcc,0x3c42,0x3c48,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,0x29e1,1,1,1,1,1,1,1, -1,1,0x29e5,1,1,1,1,1,0x17b7,0x17bd,0x29e9,0x17c3,0x17c9,0x17cf,1,0x17d5, -1,0x17db,0x17e1,0x17e9,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,0x17f1,0x17f7,0xd88, -0x17fd,0xd92,0x1803,0x180b,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,0xd9c,0xdae,0x1813, -0x1819,0xdc0,1,1,1,0x76c,0x181f,0x1825,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,0x182b,0x1831,1,0x1837,1,1,0x774,0x183d,1,1,1,1,0x1843, -0x1849,0x184f,1,0x778,1,1,0x780,1,0x784,0x790,0x798,0x79c,0x1855,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,0x185b,0x804,1,1, -1,0x808,1,1,1,0x80c,1,1,1,0x81c,1,1,1,0x820,1,0x824, -1,1,0x1861,0x1867,1,0x186d,1,1,0x828,0x1873,1,1,1,1,0x1879,0x187f, -0x1885,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,0x82c,0x830,0x188b,0x1891,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,0x1897, -0x189d,1,1,1,1,1,1,1,1,1,1,1,1,1,0x18a3,0x18a9, -0x18af,0x18b5,1,1,0x18bb,0x18c1,0x834,0x838,0x18c7,0x18cd,0x18d3,0x18d9,0x18df,0x18e5,1,1, -0x18eb,0x18f1,0x18f7,0x18fd,0x1903,0x1909,0x83c,0x840,0x190f,0x1915,0x191b,0x1921,0x1927,0x192d,0x1933,0x1939, -0x193f,0x1945,0x194b,0x1951,1,1,0x1957,0x195d,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,0x1962,0x1968,0x196f,0x1975,0x197b,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, -0x1981,0x858,0x1987,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,0x85c,0x198d,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, -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, -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,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,0xffcc,0xffcc,0xffcc,0xffcc,1,0xffcc,0xffcc,0xffcc,0xffcc, -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,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,1,1,1,1,1,1,1,1,0xffcc, -0xffb8,0xffb8,0xffb8,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,0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,0xffb8,0xffb8, -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,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,0x864,0x1993,1,1,1,1,1,1,0x868,0x1999,1, -0x86c,0x199f,1,1,1,1,1,1,1,0xfc0e,1,1,1,1,1,1, -1,1,1,1,1,1,1,0xfe12,1,1,1,0xffcc,0xffb8,0xffcc,0xffcc,1, -1,1,0x29ec,0x29f2,0x29f8,0x29fe,0x2a04,0x2a0a,0x2a10,0x2a16,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,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, -0x870,1,1,1,0x19a5,0x19ab,0xfe12,1,1,1,1,1,1,1,1,1, -0xfc00,1,1,1,1,0x2a1c,0x2a22,1,0x2a28,1,1,1,1,1,1,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,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,0x2a2e,1,1,0x2a34,1, -1,1,1,1,0xfe0e,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,0x2a3a,0x2a40, -0x2a46,1,1,0x2a4c,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,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,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,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,0x878,0x19b1,1,1,0x19b7,0x19bd,0xfe12,1,1,1,1,1,1,1,1, -0xfc00,0xfc00,1,1,1,1,0x2a52,0x2a58,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,0x884,1,0x19c3,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,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,0x888,0x890,1, -1,0x19c9,0x19cf,0x19d5,0xfe12,1,1,1,1,1,1,1,1,1,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,1,1,1,1,1, -1,1,1,1,1,1,1,0x894,1,0x19db,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,1,1,1,1,1,1,1,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,0x19e1,1,0xfc00,1,1,1,0x89c,0x19e7, -0x19ed,1,0xdca,0x19f5,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,1,1,1,1,1, -1,1,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,0x8a8,0x8b0,1,1,0x19fd,0x1a03,0x1a09,0xfe12,1,1,1,1, -1,1,1,1,1,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,1,1,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,0x1a0f,1, -0xdd4,0x1a17,0x1a1f,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, -1,1,1,1,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,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,1,1,1,1,1,1,1,1,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,0xfe12,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,1,1,1,1,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,1, -1,1,1,0xffb8,1,0xffb8,1,0xffb0,1,1,1,1,1,1,0x2a5f,1, -1,1,1,1,1,1,1,1,0x2a65,1,1,1,1,0x2a6b,1,1, -1,1,0x2a71,1,1,1,1,0x2a77,1,1,1,1,1,1,1,1, -1,1,1,1,0x2a7d,1,1,1,1,1,1,1,0xff02,0xff04,0x3c50,0xff08, -0x3c58,0x2a82,1,0x2a88,1,0xff04,0xff04,0xff04,0xff04,1,1,0xff04,0x3c60,0xffcc,0xffcc,0xfe12, -1,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1,1,0x2a8f,1, -1,1,1,1,1,1,1,1,0x2a95,1,1,1,1,0x2a9b,1,1, -1,1,0x2aa1,1,1,1,1,0x2aa7,1,1,1,1,1,1,1,1, -1,1,1,1,0x2aad,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,1,1,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,0x1a25,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,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,1,1,1,1,1,1, -1,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,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,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,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,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,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,1,1,1, -1,1,1,1,1,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,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,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,0xfe12,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,1,1,1,1,1,1,1,1,1,1,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,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,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,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,1,1,1,1,1,1,1,1,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,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8, -0xffcc,0xffcc,0xffb8,1,0xffb8,0xffcc,0xffcc,0xffb8,0xffb8,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,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,0x8c4,0x1a2b,0x8c8,0x1a31,0x8cc,0x1a37,0x8d0,0x1a3d,0x8d4,0x1a43,1,1, -0x8d8,0x1a49,1,1,1,1,1,1,1,1,1,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,0xfc00,1,1,1,1,0x8dc,0x1a4f,0x8e0,0x1a55,0x8e4,0x8e8,0x1a5b, -0x1a61,0x8ec,0x1a67,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,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,1,1,1,1,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, -1,1,1,1,1,1,1,1,1,1,1,1,1,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,0xfe12,0xfe12,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,0xffcc,0xffcc,0xffcc, -0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffd0,0xffc8,0xffc8,0xffb8,0xffb4,0xffcc,0xffd2, -0xffb8,0xffcc,0xffb8,0x1a6c,0x1a72,0x1a78,0x1a7e,0x1a85,0x1a8b,0x1a91,0x1a97,0x1a9f,0x1aa9,0x1ab0,0x1ab6,0x1abc, -0x1ac2,0x1ac8,0x1ace,0x1ad5,0x1adb,0x1ae0,0x1ae6,0x1aee,0x1af8,0x1b02,0x1b0c,0x1b14,0x1b1a,0x1b20,0x1b26,0x1b2f, -0x1b39,0x1b41,0x1b47,0x1b4c,0x1b52,0x1b58,0x1b5e,0x1b64,0x1b6a,0x1b70,0x1b76,0x1b7d,0x1b83,0x1b88,0x1b8e,0x1b94, -0x1b9a,0x1ba2,0x1bac,0x1bb4,0x1bba,0x1bc0,0x1bc6,0x1bcc,0x1bd2,0xdde,0xde8,0x1bda,0x1be4,0x1bec,0x1bf2,0x1bf8, -0x1bfe,0x1c04,0x1c0a,0x1c10,0x1c16,0x1c1d,0x1c23,0x1c28,0x1c2e,0x1c34,0x1c3a,0x1c40,0x1c46,0x1c4c,0x1c52,0x1c5a, -0x1c64,0x1c6e,0x1c78,0x1c82,0x1c8c,0x1c96,0x1ca0,0x1ca9,0x1caf,0x1cb5,0x1cbb,0x1cc0,0x1cc6,0xdf2,0xdfc,0x1cce, -0x1cd8,0x1ce0,0x1ce6,0x1cec,0x1cf2,0xe06,0xe10,0x1cfa,0x1d04,0x1d0e,0x1d18,0x1d22,0x1d2c,0x1d34,0x1d3a,0x1d40, -0x1d46,0x1d4c,0x1d52,0x1d58,0x1d5e,0x1d64,0x1d6a,0x1d70,0x1d76,0x1d7c,0x1d82,0x1d8a,0x1d94,0x1d9e,0x1da8,0x1db0, -0x1db6,0x1dbd,0x1dc3,0x1dc8,0x1dce,0x1dd4,0x1dda,0x1de0,0x1de6,0x1dec,0x1df2,0x1df9,0x1dff,0x1e05,0x1e0b,0x1e11, -0x1e17,0x1e1c,0x1e22,0x1e28,0x1e2e,0x1e35,0x1e3b,0x1e41,0x1e47,0x1e4c,0x1e52,0x1e58,0x1e5e,1,0x1e65,1, -1,1,1,0xe1a,0xe28,0x1e6a,0x1e70,0x1e78,0x1e82,0x1e8c,0x1e96,0x1ea0,0x1eaa,0x1eb4,0x1ebe,0x1ec8, -0x1ed2,0x1edc,0x1ee6,0x1ef0,0x1efa,0x1f04,0x1f0e,0x1f18,0x1f22,0x1f2c,0x1f36,0xe36,0xe40,0x1f3e,0x1f44,0x1f4a, -0x1f50,0x1f58,0x1f62,0x1f6c,0x1f76,0x1f80,0x1f8a,0x1f94,0x1f9e,0x1fa8,0x1fb2,0x1fba,0x1fc0,0x1fc6,0x1fcc,0xe4a, -0xe54,0x1fd2,0x1fd8,0x1fe0,0x1fea,0x1ff4,0x1ffe,0x2008,0x2012,0x201c,0x2026,0x2030,0x203a,0x2044,0x204e,0x2058, -0x2062,0x206c,0x2076,0x2080,0x208a,0x2094,0x209e,0x20a6,0x20ac,0x20b2,0x20b8,0x20c0,0x20ca,0x20d4,0x20de,0x20e8, -0x20f2,0x20fc,0x2106,0x2110,0x211a,0x2122,0x2128,0x212f,0x2135,0x213a,0x2140,0x2146,0x214c,1,1,1, -1,1,1,0xe5e,0xe74,0xe8c,0xe9a,0xea8,0xeb6,0xec4,0xed2,0xede,0xef4,0xf0c,0xf1a,0xf28, -0xf36,0xf44,0xf52,0xf5e,0xf6c,0x2155,0x215f,0x2169,0x2173,1,1,0xf7a,0xf88,0x217d,0x2187,0x2191, -0x219b,1,1,0xf96,0xfac,0xfc4,0xfd2,0xfe0,0xfee,0xffc,0x100a,0x1016,0x102c,0x1044,0x1052,0x1060, -0x106e,0x107c,0x108a,0x1096,0x10a8,0x21a5,0x21af,0x21b9,0x21c3,0x21cd,0x21d7,0x10ba,0x10cc,0x21e1,0x21eb,0x21f5, -0x21ff,0x2209,0x2213,0x10de,0x10ec,0x221d,0x2227,0x2231,0x223b,1,1,0x10fa,0x1108,0x2245,0x224f,0x2259, -0x2263,1,1,0x1116,0x1128,0x226d,0x2277,0x2281,0x228b,0x2295,0x229f,1,0x113a,1,0x22a9,1, -0x22b3,1,0x22bd,0x114c,0x1162,0x117a,0x1188,0x1196,0x11a4,0x11b2,0x11c0,0x11cc,0x11e2,0x11fa,0x1208,0x1216, -0x1224,0x1232,0x1240,0x124c,0x3b9e,0x22c5,0x3ba6,0x1256,0x3bae,0x22cb,0x3bb6,0x22d1,0x3bbe,0x22d7,0x3bc6,0x1260, -0x3bce,1,1,0x22de,0x22e8,0x22f7,0x2307,0x2317,0x2327,0x2337,0x2347,0x2352,0x235c,0x236b,0x237b,0x238b, -0x239b,0x23ab,0x23bb,0x23c6,0x23d0,0x23df,0x23ef,0x23ff,0x240f,0x241f,0x242f,0x243a,0x2444,0x2453,0x2463,0x2473, -0x2483,0x2493,0x24a3,0x24ae,0x24b8,0x24c7,0x24d7,0x24e7,0x24f7,0x2507,0x2517,0x2522,0x252c,0x253b,0x254b,0x255b, -0x256b,0x257b,0x258b,0x2595,0x259b,0x25a3,0x25aa,0x25b3,1,0x126a,0x25bd,0x25c5,0x25cb,0x25d1,0x3bd6,0x25d6, -1,0x2ab2,0x8f0,1,0x25dd,0x25e5,0x25ec,0x25f5,1,0x1274,0x25ff,0x2607,0x3bde,0x260d,0x3be6,0x2612, -0x2619,0x261f,0x2625,0x262b,0x2631,0x2639,0x3bf0,1,1,0x2641,0x2649,0x2651,0x2657,0x265d,0x3bfa,1, -0x2663,0x2669,0x266f,0x2675,0x267b,0x2683,0x3c04,0x268b,0x2691,0x2697,0x269f,0x26a7,0x26ad,0x26b3,0x3c0e,0x26b9, -0x26bf,0x3c16,0x2ab7,1,1,0x26c7,0x26ce,0x26d7,1,0x127e,0x26e1,0x26e9,0x3c1e,0x26ef,0x3c26,0x26f4, -0x2abb,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,1,1,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,0x2abe,1,1,1,0x2ac2,0x3c2e, -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,0x26fb,0x2701, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,0x2707,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,0x270d,0x2713,0x2719,0x914,1,0x918,1,0x91c,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,0x920,0x271f,1,1,1,0x924,0x2725,1,0x928, -0x272b,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,0x92c,0x2731,0x930,0x2737,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,0x273d,1,0x938,0x2743,0x93c,1,0x2749,0x940,0x274f,1,1,1, -0x944,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,0x2755,0x948,0x275b,1,0x94c,0x950,1,1,1,1,1,1,1, -0x2761,0x2767,0x276d,0x2773,0x2779,0x954,0x958,0x277f,0x2785,0x95c,0x960,0x278b,0x2791,0x964,0x968,0x96c, -0x970,1,1,0x2797,0x279d,0x974,0x978,0x27a3,0x27a9,0x97c,0x980,0x27af,0x27b5,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,0x27bb, -0x27c1,0x27c7,0x27cd,1,1,0x99c,0x9a0,0x9a4,0x9a8,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,0x27d3,0x27d9,0x27df,0x27e5,1,1,1, -1,1,1,0x27eb,0x27f1,0x27f7,0x27fd,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0x2ac7,0x2acb,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x2acf,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,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,1,1,1,1,1,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,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,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,0x9ac, -1,1,1,1,0x9b0,0x2803,0x9b4,0x2809,0x9b8,0x280f,0x9bc,0x2815,0x9c0,0x281b,0x9c4,0x2821, -0x9c8,0x2827,0x9cc,0x282d,0x9d0,0x2833,0x9d4,0x2839,0x9d8,0x283f,0x9dc,0x2845,1,0x9e0,0x284b,0x9e4, -0x2851,0x9e8,0x2857,1,1,1,1,1,0x9ec,0x285d,0x2863,0x9f4,0x2869,0x286f,0x9fc,0x2875, -0x287b,0xa04,0x2881,0x2887,0xa0c,0x288d,0x2893,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,0x2899,1,1,1,1, -0xfc10,0xfc10,1,1,0xa14,0x289f,1,1,1,1,1,1,1,0xa18,1,1, -1,1,0xa1c,0x28a5,0xa20,0x28ab,0xa24,0x28b1,0xa28,0x28b7,0xa2c,0x28bd,0xa30,0x28c3,0xa34,0x28c9, -0xa38,0x28cf,0xa3c,0x28d5,0xa40,0x28db,0xa44,0x28e1,0xa48,0x28e7,1,0xa4c,0x28ed,0xa50,0x28f3,0xa54, -0x28f9,1,1,1,1,1,0xa58,0x28ff,0x2905,0xa60,0x290b,0x2911,0xa68,0x2917,0x291d,0xa70, -0x2923,0x2929,0xa78,0x292f,0x2935,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,0xa80,0xa84,0xa88,0xa8c,1,0x293b,1,1,0x2941,0x2947, -0x294d,0x2953,1,1,0xa90,0x2959,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,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,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,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,0xffcc,0xffcc,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, -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,1,1,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,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,1,1,1,1,1,1,1, -1,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,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,0xae2,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289, -0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289, -0x1289,0x1289,0x1289,0x1289,0x1289,0xae2,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289, -0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289, -0x1289,0xae2,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289, -0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0xae2,0x1289,0x1289, -0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289, -0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,0x3c66,1,0x3c66,0x3c66,0x3c66,0x3c66,0x3c66,0x3c66,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x3c66, -0x3c66,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,0x3c66,1,1,1,1,0x3c66,1,1,1,0x3c66,0x3c66,0x3c66, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x3b97,1, -0x2ad5,0x2ad9,0x2add,0x2ae1,0x2ae5,0x2ae9,0x2aed,0x2af1,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,0x2c31,0x2c35,0x2c39,0x2c3d,0x2b21,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,0x2d41,0x2d45,0x2d49, -0x2d4d,0x2c89,0x2d51,0x2d55,0x2d59,0x2d5d,0x2d61,0x2d65,0x2d69,0x2d6d,0x2c49,0x2d71,0x2d75,0x2d79,0x2d7d,0x2d81, -0x2d85,0x2d89,0x2d8d,0x2d91,0x2d95,0x2d99,0x2d9d,0x2da1,0x2da5,0x2da9,0x2dad,0x2db1,0x2db5,0x2db9,0x2dbd,0x2b21, -0x2dc1,0x2dc5,0x2dc9,0x2dcd,0x2dd1,0x2dd5,0x2dd9,0x2ddd,0x2de1,0x2de5,0x2de9,0x2ded,0x2df1,0x2df5,0x2df9,0x2dfd, -0x2e01,0x2e05,0x2e09,0x2e0d,0x2e11,0x2e15,0x2e19,0x2e1d,0x2e21,0x2e25,0x2e29,0x2c51,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,0x2ee5,0x2ee9,0x2eed,0x2ef1,1,1, -0x2ef5,1,0x2ef9,1,1,0x2efd,0x2f01,0x2f05,0x2f09,0x2f0d,0x2f11,0x2f15,0x2f19,0x2f1d,0x2f21,1, -0x2f25,1,0x2f29,1,1,0x2f2d,0x2f31,1,1,1,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,0x2fd9,0x2fdd,0x2fe1,0x2fe5,0x2d25,0x2fe9,0x2fed,0x2ff1,0x2ff5,0x2ff9,0x2ffd,0x2ffd,0x3001, -0x3005,0x3009,0x300d,0x3011,0x3015,0x3019,0x301d,0x2f2d,0x3021,0x3025,0x3029,0x302d,0x3031,0x3037,1,1, -0x303b,0x303f,0x3043,0x3047,0x304b,0x304f,0x3053,0x3057,0x2f65,0x305b,0x305f,0x3063,0x2ef5,0x3067,0x306b,0x306f, -0x3073,0x3077,0x307b,0x307f,0x3083,0x3087,0x308b,0x308f,0x3093,0x2f89,0x3097,0x2f8d,0x309b,0x309f,0x30a3,0x30a7, -0x30ab,0x2ef9,0x2b75,0x30af,0x30b3,0x30b7,0x2c8d,0x2de9,0x30bb,0x30bf,0x2fa9,0x30c3,0x2fad,0x30c7,0x30cb,0x30cf, -0x2f01,0x30d3,0x30d7,0x30db,0x30df,0x30e3,0x2f05,0x30e7,0x30eb,0x30ef,0x30f3,0x30f7,0x30fb,0x2fe5,0x30ff,0x3103, -0x2d25,0x3107,0x2ff5,0x310b,0x310f,0x3113,0x3117,0x311b,0x3009,0x311f,0x2f29,0x3123,0x300d,0x2c41,0x3127,0x3011, -0x312b,0x3019,0x312f,0x3133,0x3137,0x313b,0x313f,0x3021,0x2f19,0x3143,0x3025,0x3147,0x3029,0x314b,0x2af1,0x314f, -0x3155,0x315b,0x3161,0x3165,0x3169,0x316d,0x3173,0x3179,0x317f,0x3183,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0x3186,0xfe34,0x318c,1,1,1,1,1,1,1,1,1,1,0x3192,0x3198,0x31a0, -0x31aa,0x31b2,0x31b8,0x31be,0x31c4,0x31ca,0x31d0,0x31d6,0x31dc,0x31e2,1,0x31e8,0x31ee,0x31f4,0x31fa,0x3200, -1,0x3206,1,0x320c,0x3212,1,0x3218,0x321e,1,0x3224,0x322a,0x3230,0x3236,0x323c,0x3242,0x3248, -0x324e,0x3254,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,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,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8, -0xffb8,0xffcc,0xffcc,1,1,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,0xffcc,0xfe02, -0xffb8,1,1,1,1,0xfe12,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,1,1, -1,1,1,1,1,1,0xffb8,0xffb8,0xffcc,0xffcc,0xffcc,0xffb8,0xffcc,0xffb8,0xffb8,0xffb8, -1,1,0xffcc,0xffb8,0xffcc,0xffb8,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,0xfe12, -1,1,1,1,1,1,1,1,1,0xa94,0x295f,0xa9a,0x2969,1,1,1, -1,1,0xaa0,1,1,1,1,1,0x2973,1,1,1,1,1,1,1, -1,1,0xfe12,0xfc0e,1,1,1,1,1,1,1,0xfc00,1,1,1,1, -1,1,0x297d,0x2987,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,0xfe12,0xfe0e,1,1,1,1,1,1,1,1,1,0xfe0e, -0xfe12,1,1,1,1,1,1,1,1,1,1,1,0xfe0e,0xfe0e,1,0xfc00, -1,1,1,1,1,1,1,0xab2,1,1,1,0x2991,0x299b,0xfe12,1,1, -1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,0xfe12,1, -1,1,0xfe0e,1,1,1,1,1,1,1,1,1,0xfc00,1,1,1, -1,1,1,1,1,0xabe,0xfc00,0x29a5,0x29af,0xfc00,0x29b9,1,1,0xfe12,0xfe0e,1, -1,1,1,1,1,1,1,1,1,1,1,0xad0,0xad6,0x29c3,0x29cd,1, -1,1,0xfe12,0xfe0e,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,0xfc00,1,1, -1,1,0xadc,1,1,0x29d7,1,1,1,1,0xfe12,0xfe12,1,0xfe02,0xfe02,0xfe02, -0xfe02,0xfe02,1,1,1,1,1,1,1,1,1,1,1,0xfe0c,0xfe0c,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,0x325a,0x3264,0x3278,0x3290, -0x32a8,0x32c0,0x32d8,0xffb0,0xffb0,0xfe02,0xfe02,0xfe02,1,1,1,0xffc4,0xffb0,0xffb0,0xffb0,1, -1,1,1,1,1,1,1,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,1,1,0xffcc,0xffcc, -0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,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,0x32e6,0x32f0, -0x3304,0x331c,0x3334,0x334c,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,1,1,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, -0xffd0,0xffd0,0xffb8,0xffcc,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, -0x335b,0x335f,0x3363,0x3367,0x336d,0x2f4d,0x3371,0x3375,0x3379,0x337d,0x2f51,0x3381,0x3385,0x3389,0x2f55,0x338f, -0x3393,0x3397,0x339b,0x33a1,0x33a5,0x33a9,0x33ad,0x33b3,0x33b7,0x33bb,0x33bf,0x303f,0x33c3,0x33c9,0x33cd,0x33d1, -0x33d5,0x33d9,0x33dd,0x33e1,0x33e5,0x3053,0x2f59,0x2f5d,0x3057,0x33e9,0x33ed,0x2c59,0x33f1,0x2f61,0x33f5,0x33f9, -0x33fd,0x3401,0x3401,0x3401,0x3405,0x340b,0x340f,0x3413,0x3417,0x341d,0x3421,0x3425,0x3429,0x342d,0x3431,0x3435, -0x3439,0x343d,0x3441,0x3445,0x3449,0x344d,0x344d,0x305f,0x3451,0x3455,0x3459,0x345d,0x2f69,0x3461,0x3465,0x3469, -0x2ebd,0x346d,0x3471,0x3475,0x3479,0x347d,0x3481,0x3485,0x3489,0x348d,0x3493,0x3497,0x349b,0x349f,0x34a3,0x34a7, -0x34ab,0x34b1,0x34b7,0x34bb,0x34bf,0x34c3,0x34c7,0x34cb,0x34cf,0x34d3,0x34d7,0x34d7,0x34db,0x34e1,0x34e5,0x2c49, -0x34e9,0x34ed,0x34f3,0x34f7,0x34fb,0x34ff,0x3503,0x3507,0x2f7d,0x350b,0x350f,0x3513,0x3519,0x351d,0x3523,0x3527, -0x352b,0x352f,0x3533,0x3537,0x353b,0x353f,0x3543,0x3547,0x354b,0x354f,0x3555,0x3559,0x355d,0x3561,0x2b71,0x3565, -0x356b,0x356f,0x356f,0x3575,0x3579,0x3579,0x357d,0x3581,0x3587,0x358d,0x3591,0x3595,0x3599,0x359d,0x35a1,0x35a5, -0x35a9,0x35ad,0x35b1,0x2f81,0x35b5,0x35bb,0x35bf,0x35c3,0x308f,0x35c3,0x35c7,0x2f89,0x35cb,0x35cf,0x35d3,0x35d7, -0x2f8d,0x2b05,0x35db,0x35df,0x35e3,0x35e7,0x35eb,0x35ef,0x35f3,0x35f9,0x35fd,0x3601,0x3605,0x3609,0x360d,0x3613, -0x3617,0x361b,0x361f,0x3623,0x3627,0x362b,0x362f,0x3633,0x2f91,0x3637,0x363b,0x3641,0x3645,0x3649,0x364d,0x2f99, -0x3651,0x3655,0x3659,0x365d,0x3661,0x3665,0x3669,0x366d,0x2b75,0x30af,0x3671,0x3675,0x3679,0x367d,0x3683,0x3687, -0x368b,0x368f,0x2f9d,0x3693,0x3699,0x369d,0x36a1,0x3161,0x36a5,0x36a9,0x36ad,0x36b1,0x36b5,0x36bb,0x36bf,0x36c3, -0x36c7,0x36cd,0x36d1,0x36d5,0x36d9,0x2c8d,0x36dd,0x36e1,0x36e7,0x36ed,0x36f3,0x36f7,0x36fd,0x3701,0x3705,0x3709, -0x370d,0x2fa1,0x2de9,0x3711,0x3715,0x3719,0x371d,0x3723,0x3727,0x372b,0x372f,0x30bf,0x3733,0x3737,0x373d,0x3741, -0x3745,0x374b,0x3751,0x3755,0x30c3,0x3759,0x375d,0x3761,0x3765,0x3769,0x376d,0x3771,0x3777,0x377b,0x3781,0x3785, -0x378b,0x30cb,0x378f,0x3793,0x3799,0x379d,0x37a1,0x37a7,0x37ad,0x37b1,0x37b5,0x37b9,0x37bd,0x37bd,0x37c1,0x37c5, -0x30d3,0x37c9,0x37cd,0x37d1,0x37d5,0x37d9,0x37df,0x37e3,0x2c55,0x37e9,0x37ef,0x37f3,0x37f9,0x37ff,0x3805,0x3809, -0x30eb,0x380d,0x3813,0x3819,0x381f,0x3825,0x3829,0x3829,0x30ef,0x3169,0x382d,0x3831,0x3835,0x3839,0x383f,0x2bbd, -0x30f7,0x3843,0x3847,0x2fcd,0x384d,0x3853,0x2f15,0x3859,0x385d,0x2fdd,0x3861,0x3865,0x3869,0x386f,0x386f,0x3875, -0x3879,0x387d,0x3883,0x3887,0x388b,0x388f,0x3895,0x3899,0x389d,0x38a1,0x38a5,0x38a9,0x38af,0x38b3,0x38b7,0x38bb, -0x38bf,0x38c3,0x38c7,0x38cd,0x38d3,0x38d7,0x38dd,0x38e1,0x38e7,0x38eb,0x2ff5,0x38ef,0x38f5,0x38fb,0x38ff,0x3905, -0x3909,0x390f,0x3913,0x3917,0x391b,0x391f,0x3923,0x3927,0x392d,0x3933,0x3939,0x3575,0x393f,0x3943,0x3947,0x394b, -0x394f,0x3953,0x3957,0x395b,0x395f,0x3963,0x3967,0x396b,0x2c9d,0x3971,0x3975,0x3979,0x397d,0x3981,0x3985,0x3001, -0x3989,0x398d,0x3991,0x3995,0x3999,0x399f,0x39a5,0x39ab,0x39af,0x39b3,0x39b7,0x39bb,0x39c1,0x39c5,0x39cb,0x39cf, -0x39d3,0x39d9,0x39df,0x39e3,0x2ba9,0x39e7,0x39eb,0x39ef,0x39f3,0x39f7,0x39fb,0x3113,0x39ff,0x3a03,0x3a07,0x3a0b, -0x3a0f,0x3a13,0x3a17,0x3a1b,0x3a1f,0x3a23,0x3a29,0x3a2d,0x3a31,0x3a35,0x3a39,0x3a3d,0x3a43,0x3a49,0x3a4d,0x3a51, -0x3127,0x312b,0x3a55,0x3a59,0x3a5f,0x3a63,0x3a67,0x3a6b,0x3a6f,0x3a75,0x3a7b,0x3a7f,0x3a83,0x3a87,0x3a8d,0x312f, -0x3a91,0x3a97,0x3a9d,0x3aa1,0x3aa5,0x3aa9,0x3aaf,0x3ab3,0x3ab7,0x3abb,0x3abf,0x3ac3,0x3ac7,0x3acb,0x3ad1,0x3ad5, -0x3ad9,0x3add,0x3ae3,0x3ae7,0x3aeb,0x3aef,0x3af3,0x3af9,0x3aff,0x3b03,0x3b07,0x3b0b,0x3b11,0x3b15,0x3147,0x3147, -0x3b1b,0x3b1f,0x3b25,0x3b29,0x3b2d,0x3b31,0x3b35,0x3b39,0x3b3d,0x3b41,0x314b,0x3b47,0x3b4b,0x3b4f,0x3b53,0x3b57, -0x3b5b,0x3b61,0x3b65,0x3b6b,0x3b71,0x3b77,0x3b7b,0x3b7f,0x3b83,0x3b87,0x3b8b,0x3b8f,0x3b93,0x3b97,1,1 -}; - -static const UCPTrie norm2_nfc_data_trie={ - norm2_nfc_data_trieIndex, - { norm2_nfc_data_trieData }, - 1788, 7984, - 0x2fc00, 0x30, - 0, 0, - 0, 0, - 0xc4, 0x226, - 0x1, -}; - -static const uint16_t norm2_nfc_data_extraData[7732]={ -0xffff,0xffff,0x8670,0x44dc,0x8670,0x44c0,0x8670,0x44de,0x600,0x180,0x602,0x182,0x604,0x185,0x606,0x186, -0x608,0x200,0x60c,0x205,0x60e,0x44d,0x610,0x189,0x612,0x3d44,0x614,0x18b,0x618,0x39a,0x61e,0x400, -0x622,0x404,0x646,0x3d41,0x64a,0x3c00,0x8650,0x208,0x60e,0x3c04,0x646,0x3c08,0x8662,0x3c0c,0x602,0x20c, -0x604,0x210,0x60e,0x214,0x618,0x218,0x864e,0x18f,0x60e,0x3c14,0x618,0x21c,0x646,0x3c18,0x64e,0x3c20, -0x65a,0x3c24,0x8662,0x3c1c,0x600,0x190,0x602,0x192,0x604,0x195,0x606,0x3d78,0x608,0x225,0x60c,0x228, -0x60e,0x22c,0x610,0x196,0x612,0x3d74,0x618,0x234,0x61e,0x408,0x622,0x40c,0x646,0x3d71,0x64e,0x451, -0x650,0x230,0x65a,0x3c30,0x8660,0x3c34,0x860e,0x3c3c,0x602,0x3e8,0x604,0x238,0x608,0x3c40,0x60c,0x23c, -0x60e,0x240,0x618,0x3cc,0x864e,0x244,0x604,0x248,0x60e,0x3c44,0x610,0x3c4c,0x618,0x43c,0x646,0x3c48, -0x64e,0x3c50,0x865c,0x3c54,0x600,0x198,0x602,0x19a,0x604,0x19c,0x606,0x250,0x608,0x254,0x60c,0x258, -0x60e,0x260,0x610,0x19f,0x612,0x3d90,0x618,0x39e,0x61e,0x410,0x622,0x414,0x646,0x3d94,0x650,0x25c, -0x8660,0x3c58,0x8604,0x268,0x602,0x3c60,0x618,0x3d0,0x646,0x3c64,0x64e,0x26c,0x8662,0x3c68,0x602,0x272, -0x618,0x27a,0x646,0x3c6d,0x64e,0x276,0x65a,0x3c78,0x8662,0x3c74,0x602,0x3c7c,0x60e,0x3c80,0x8646,0x3c84, -0x600,0x3f0,0x602,0x286,0x606,0x1a2,0x60e,0x3c88,0x618,0x28e,0x646,0x3c8c,0x64e,0x28a,0x65a,0x3c94, -0x8662,0x3c90,0x600,0x1a4,0x602,0x1a6,0x604,0x1a9,0x606,0x1ab,0x608,0x299,0x60c,0x29c,0x60e,0x45d, -0x610,0x1ad,0x612,0x3d9c,0x616,0x2a0,0x618,0x3a2,0x61e,0x418,0x622,0x41c,0x636,0x341,0x646,0x3d99, -0x8650,0x3d5,0x602,0x3ca8,0x860e,0x3cac,0x602,0x2a8,0x60e,0x3cb0,0x618,0x2b0,0x61e,0x420,0x622,0x424, -0x646,0x3cb5,0x64e,0x2ac,0x8662,0x3cbc,0x602,0x2b5,0x604,0x2b8,0x60e,0x3cc0,0x618,0x2c1,0x646,0x3cc5, -0x64c,0x430,0x864e,0x2bc,0x60e,0x3cd4,0x618,0x2c8,0x646,0x3cd8,0x64c,0x434,0x64e,0x2c4,0x65a,0x3ce0, -0x8662,0x3cdc,0x600,0x1b2,0x602,0x1b4,0x604,0x1b6,0x606,0x2d1,0x608,0x2d5,0x60c,0x2d8,0x610,0x1b9, -0x612,0x3dcc,0x614,0x2dc,0x616,0x2e0,0x618,0x3a6,0x61e,0x428,0x622,0x42c,0x636,0x35f,0x646,0x3dc8, -0x648,0x3ce4,0x650,0x2e4,0x65a,0x3cec,0x8660,0x3ce8,0x606,0x3cf8,0x8646,0x3cfc,0x600,0x3d00,0x602,0x3d04, -0x604,0x2e8,0x60e,0x3d0c,0x610,0x3d08,0x8646,0x3d10,0x60e,0x3d14,0x8610,0x3d18,0x600,0x3de4,0x602,0x1ba, -0x604,0x2ec,0x606,0x3df0,0x608,0x464,0x60e,0x3d1c,0x610,0x2f0,0x612,0x3dec,0x8646,0x3de8,0x602,0x2f2, -0x604,0x3d20,0x60e,0x2f6,0x618,0x2fa,0x646,0x3d24,0x8662,0x3d28,0x600,0x1c0,0x602,0x1c2,0x604,0x1c5, -0x606,0x1c6,0x608,0x202,0x60c,0x207,0x60e,0x44f,0x610,0x1c9,0x612,0x3d46,0x614,0x1cb,0x618,0x39c, -0x61e,0x402,0x622,0x406,0x646,0x3d43,0x64a,0x3c02,0x8650,0x20a,0x60e,0x3c06,0x646,0x3c0a,0x8662,0x3c0e, -0x602,0x20e,0x604,0x212,0x60e,0x216,0x618,0x21a,0x864e,0x1cf,0x60e,0x3c16,0x618,0x21e,0x646,0x3c1a, -0x64e,0x3c22,0x65a,0x3c26,0x8662,0x3c1e,0x600,0x1d0,0x602,0x1d2,0x604,0x1d5,0x606,0x3d7a,0x608,0x227, -0x60c,0x22a,0x60e,0x22e,0x610,0x1d6,0x612,0x3d76,0x618,0x236,0x61e,0x40a,0x622,0x40e,0x646,0x3d73, -0x64e,0x453,0x650,0x232,0x65a,0x3c32,0x8660,0x3c36,0x860e,0x3c3e,0x602,0x3ea,0x604,0x23a,0x608,0x3c42, -0x60c,0x23e,0x60e,0x242,0x618,0x3ce,0x864e,0x246,0x604,0x24a,0x60e,0x3c46,0x610,0x3c4e,0x618,0x43e, -0x646,0x3c4a,0x64e,0x3c52,0x65c,0x3c56,0x8662,0x3d2c,0x600,0x1d8,0x602,0x1da,0x604,0x1dc,0x606,0x252, -0x608,0x256,0x60c,0x25a,0x610,0x1df,0x612,0x3d92,0x618,0x3a0,0x61e,0x412,0x622,0x416,0x646,0x3d96, -0x650,0x25e,0x8660,0x3c5a,0x604,0x26a,0x8618,0x3e0,0x602,0x3c62,0x618,0x3d2,0x646,0x3c66,0x64e,0x26e, -0x8662,0x3c6a,0x602,0x274,0x618,0x27c,0x646,0x3c6f,0x64e,0x278,0x65a,0x3c7a,0x8662,0x3c76,0x602,0x3c7e, -0x60e,0x3c82,0x8646,0x3c86,0x600,0x3f2,0x602,0x288,0x606,0x1e2,0x60e,0x3c8a,0x618,0x290,0x646,0x3c8e, -0x64e,0x28c,0x65a,0x3c96,0x8662,0x3c92,0x600,0x1e4,0x602,0x1e6,0x604,0x1e9,0x606,0x1eb,0x608,0x29b, -0x60c,0x29e,0x60e,0x45f,0x610,0x1ed,0x612,0x3d9e,0x616,0x2a2,0x618,0x3a4,0x61e,0x41a,0x622,0x41e, -0x636,0x343,0x646,0x3d9b,0x8650,0x3d7,0x602,0x3caa,0x860e,0x3cae,0x602,0x2aa,0x60e,0x3cb2,0x618,0x2b2, -0x61e,0x422,0x622,0x426,0x646,0x3cb7,0x64e,0x2ae,0x8662,0x3cbe,0x602,0x2b7,0x604,0x2ba,0x60e,0x3cc2, -0x618,0x2c3,0x646,0x3cc7,0x64c,0x432,0x864e,0x2be,0x60e,0x3cd6,0x610,0x3d2e,0x618,0x2ca,0x646,0x3cda, -0x64c,0x436,0x64e,0x2c6,0x65a,0x3ce2,0x8662,0x3cde,0x600,0x1f2,0x602,0x1f4,0x604,0x1f6,0x606,0x2d3, -0x608,0x2d7,0x60c,0x2da,0x610,0x1f9,0x612,0x3dce,0x614,0x2de,0x616,0x2e2,0x618,0x3a8,0x61e,0x42a, -0x622,0x42e,0x636,0x361,0x646,0x3dca,0x648,0x3ce6,0x650,0x2e6,0x65a,0x3cee,0x8660,0x3cea,0x606,0x3cfa, -0x8646,0x3cfe,0x600,0x3d02,0x602,0x3d06,0x604,0x2ea,0x60e,0x3d0e,0x610,0x3d0a,0x614,0x3d30,0x8646,0x3d12, -0x60e,0x3d16,0x8610,0x3d1a,0x600,0x3de6,0x602,0x1fa,0x604,0x2ee,0x606,0x3df2,0x608,0x466,0x60e,0x3d1e, -0x610,0x1fe,0x612,0x3dee,0x614,0x3d32,0x8646,0x3dea,0x602,0x2f4,0x604,0x3d22,0x60e,0x2f8,0x618,0x2fc, -0x646,0x3d26,0x8662,0x3d2a,0x600,0x3fda,0x602,0x70a,0x8684,0x3f82,0x602,0x3f8,0x8608,0x3c4,0x8602,0x3fc, -0x602,0x3fa,0x8608,0x3c6,0x8602,0x3fe,0x860e,0x3d36,0x8618,0x3dc,0x8618,0x3de,0x600,0x3f74,0x602,0x70c, -0x608,0x3f72,0x60c,0x3f70,0x626,0x3e11,0x628,0x3e13,0x868a,0x3f78,0x600,0x3f90,0x602,0x710,0x626,0x3e31, -0x8628,0x3e33,0x600,0x3f94,0x602,0x712,0x626,0x3e51,0x628,0x3e53,0x868a,0x3f98,0x600,0x3fb4,0x602,0x714, -0x608,0x3fb2,0x60c,0x3fb0,0x610,0x754,0x626,0x3e71,0x8628,0x3e73,0x600,0x3ff0,0x602,0x718,0x626,0x3e91, -0x8628,0x3e93,0x8628,0x3fd8,0x600,0x3fd4,0x602,0x71c,0x608,0x3fd2,0x60c,0x3fd0,0x610,0x756,0x8628,0x3eb3, -0x600,0x3ff4,0x602,0x71e,0x626,0x3ed1,0x628,0x3ed3,0x868a,0x3ff8,0x600,0x3ee1,0x602,0x759,0x608,0x3f62, -0x60c,0x3f60,0x626,0x3e01,0x628,0x3e03,0x684,0x3f6d,0x868a,0x3f66,0x600,0x3ee4,0x602,0x75a,0x626,0x3e21, -0x8628,0x3e23,0x600,0x3ee9,0x602,0x75d,0x626,0x3e41,0x628,0x3e43,0x684,0x3f8d,0x868a,0x3f86,0x600,0x3eec, -0x602,0x75e,0x608,0x3fa2,0x60c,0x3fa0,0x610,0x795,0x626,0x3e61,0x628,0x3e63,0x8684,0x3fac,0x600,0x3ef0, -0x602,0x798,0x626,0x3e81,0x8628,0x3e83,0x626,0x3fc8,0x8628,0x3fca,0x600,0x3ef4,0x602,0x79a,0x608,0x3fc2, -0x60c,0x3fc0,0x610,0x797,0x626,0x3ea1,0x628,0x3ea3,0x8684,0x3fcc,0x600,0x3ef9,0x602,0x79d,0x626,0x3ec1, -0x628,0x3ec3,0x684,0x3fed,0x868a,0x3fe6,0x602,0x7a6,0x8610,0x7a8,0x8610,0x80e,0x60c,0x9a0,0x8610,0x9a4, -0x8602,0x806,0x600,0x800,0x60c,0x9ac,0x8610,0x802,0x60c,0x982,0x8610,0x9b8,0x8610,0x9bc,0x600,0x81a, -0x608,0x9c4,0x60c,0x832,0x8610,0x9c8,0x8602,0x818,0x8610,0x9cc,0x608,0x9dc,0x60c,0x81c,0x610,0x9e0, -0x8616,0x9e4,0x8610,0x9e8,0x8610,0x9f0,0x8610,0x9d8,0x60c,0x9a2,0x8610,0x9a6,0x8602,0x8a6,0x600,0x8a0, -0x60c,0x9ae,0x8610,0x8a2,0x60c,0x984,0x8610,0x9ba,0x8610,0x9be,0x600,0x8ba,0x608,0x9c6,0x60c,0x872, -0x8610,0x9ca,0x8602,0x8b8,0x8610,0x9ce,0x608,0x9de,0x60c,0x8bc,0x610,0x9e2,0x8616,0x9e6,0x8610,0x9ea, -0x8610,0x9f2,0x8610,0x9da,0x8610,0x8ae,0x861e,0x8ec,0x861e,0x8ee,0x8610,0x9b4,0x8610,0x9b6,0x8610,0x9d4, -0x8610,0x9d6,0xca6,0xc44,0xca8,0xc46,0x8caa,0xc4a,0x8ca8,0xc48,0x8ca8,0xc4c,0x8ca8,0xd84,0x8ca8,0xda6, -0x8ca8,0xd80,0x9278,0x1252,0x9278,0x1262,0x9278,0x1268,0x137c,0x1396,0x93ae,0x1398,0x167c,0x1696,0x16ac,0x1690, -0x96ae,0x1698,0x97ae,0x1728,0x177c,0x1794,0x97ae,0x1798,0x977c,0x1796,0x98ac,0x1890,0x99aa,0x1980,0x1984,0x1995, -0x19aa,0x198e,0x99ac,0x1990,0x1a7c,0x1a94,0x9aae,0x1a98,0x9a7c,0x1a96,0x1b94,0x1bb4,0x1b9e,0x1bb9,0x9bbe,0x1bbc, -0xa05c,0x204c,0xb66a,0x360c,0xb66a,0x3610,0xb66a,0x3614,0xb66a,0x3618,0xb66a,0x361c,0xb66a,0x3624,0xb66a,0x3676, -0xb66a,0x367a,0xb66a,0x3680,0xb66a,0x3682,0xb66a,0x3686,0x600,0x3f9a,0x602,0x3f9c,0x8684,0x3f9e,0x600,0x3fba, -0x602,0x3fbc,0x8684,0x3fbe,0x8670,0x4334,0x8670,0x4336,0x8670,0x435c,0x8670,0x439a,0x8670,0x439e,0x8670,0x439c, -0x8670,0x4408,0x8670,0x4412,0x8670,0x4418,0x8670,0x4448,0x8670,0x444c,0x8670,0x4482,0x8670,0x4488,0x8670,0x448e, -0x8670,0x4492,0x8670,0x44da,0x8670,0x44c4,0x8670,0x44e0,0x8670,0x44e2,0x8670,0x44e8,0x8670,0x44ea,0x8670,0x44f0, -0x8670,0x44f2,0x8670,0x4500,0x8670,0x4502,0x8670,0x45c0,0x8670,0x45c2,0x8670,0x4508,0x8670,0x450a,0x8670,0x4510, -0x8670,0x4512,0x8670,0x45c4,0x8670,0x45c6,0x8670,0x4558,0x8670,0x455a,0x8670,0x455c,0x8670,0x455e,0x8670,0x45d4, -0x8670,0x45d6,0x8670,0x45d8,0x8670,0x45da,0xe132,0x6128,0xe132,0x6098,0xe132,0x609c,0xe132,0x60a0,0xe132,0x60a4, -0xe132,0x60a8,0xe132,0x60ac,0xe132,0x60b0,0xe132,0x60b4,0xe132,0x60b8,0xe132,0x60bc,0xe132,0x60c0,0xe132,0x60c4, -0xe132,0x60ca,0xe132,0x60ce,0xe132,0x60d2,0x6132,0x60e0,0xe134,0x60e2,0x6132,0x60e6,0xe134,0x60e8,0x6132,0x60ec, -0xe134,0x60ee,0x6132,0x60f2,0xe134,0x60f4,0x6132,0x60f8,0xe134,0x60fa,0xe132,0x613c,0xe132,0x61e8,0xe132,0x6158, -0xe132,0x615c,0xe132,0x6160,0xe132,0x6164,0xe132,0x6168,0xe132,0x616c,0xe132,0x6170,0xe132,0x6174,0xe132,0x6178, -0xe132,0x617c,0xe132,0x6180,0xe132,0x6184,0xe132,0x618a,0xe132,0x618e,0xe132,0x6192,0x6132,0x61a0,0xe134,0x61a2, -0x6132,0x61a6,0xe134,0x61a8,0x6132,0x61ac,0xe134,0x61ae,0x6132,0x61b2,0xe134,0x61b4,0x6132,0x61b8,0xe134,0x61ba, -0xe132,0x61ee,0xe132,0x61f0,0xe132,0x61f2,0xe132,0x61f4,0xe132,0x61fc,0xb489,0x2e82,0x2134,0xb489,0x2e82,0x2138, -0xb489,0x2e82,0x2156,0xb489,0x49c2,0x225c,0xb489,0x49c2,0x225e,0x3489,0xcf82,0x2696,0xb489,0xd5c2,0x2698,0x348b, -0x2c02,0x2978,0x348b,0x2e82,0x2976,0xb48b,0x2f42,0x297c,0xb48b,0x6bc2,0x2b74,0xb48b,0x6bc2,0x2b76,0xb48d,0x4c02, -0x3270,2,0xe602,0x41,0x302,0x600,0x3d4c,0x602,0x3d48,0x606,0x3d54,0x8612,0x3d50,0xe602,0x41,0x308, -0x8608,0x3bc,0xe602,0x41,0x30a,0x8602,0x3f4,0xca02,0x43,0x327,0x8602,0x3c10,0xe602,0x45,0x302,0x600, -0x3d80,0x602,0x3d7c,0x606,0x3d88,0x8612,0x3d84,0xe602,0x49,0x308,0x8602,0x3c5c,0xe602,0x4f,0x302,0x600, -0x3da4,0x602,0x3da0,0x606,0x3dac,0x8612,0x3da8,0xe602,0x4f,0x303,0x602,0x3c98,0x608,0x458,0x8610,0x3c9c, -0xe602,0x4f,0x308,0x8608,0x454,0xe602,0x55,0x308,0x600,0x3b6,0x602,0x3ae,0x608,0x3aa,0x8618,0x3b2, -0xe602,0x61,0x302,0x600,0x3d4e,0x602,0x3d4a,0x606,0x3d56,0x8612,0x3d52,0xe602,0x61,0x308,0x8608,0x3be, -0xe602,0x61,0x30a,0x8602,0x3f6,0xca02,0x63,0x327,0x8602,0x3c12,0xe602,0x65,0x302,0x600,0x3d82,0x602, -0x3d7e,0x606,0x3d8a,0x8612,0x3d86,0xe602,0x69,0x308,0x8602,0x3c5e,0xe602,0x6f,0x302,0x600,0x3da6,0x602, -0x3da2,0x606,0x3dae,0x8612,0x3daa,0xe602,0x6f,0x303,0x602,0x3c9a,0x608,0x45a,0x8610,0x3c9e,0xe602,0x6f, -0x308,0x8608,0x456,0xe602,0x75,0x308,0x600,0x3b8,0x602,0x3b0,0x608,0x3ac,0x8618,0x3b4,0xe602,0x41, -0x306,0x600,0x3d60,0x602,0x3d5c,0x606,0x3d68,0x8612,0x3d64,0xe602,0x61,0x306,0x600,0x3d62,0x602,0x3d5e, -0x606,0x3d6a,0x8612,0x3d66,0xe602,0x45,0x304,0x600,0x3c28,0x8602,0x3c2c,0xe602,0x65,0x304,0x600,0x3c2a, -0x8602,0x3c2e,0xe602,0x4f,0x304,0x600,0x3ca0,0x8602,0x3ca4,0xe602,0x6f,0x304,0x600,0x3ca2,0x8602,0x3ca6, -0xe602,0x53,0x301,0x860e,0x3cc8,0xe602,0x73,0x301,0x860e,0x3cca,0xe602,0x53,0x30c,0x860e,0x3ccc,0xe602, -0x73,0x30c,0x860e,0x3cce,0xe602,0x55,0x303,0x8602,0x3cf0,0xe602,0x75,0x303,0x8602,0x3cf2,0xe602,0x55, -0x304,0x8610,0x3cf4,0xe602,0x75,0x304,0x8610,0x3cf6,0xd802,0x4f,0x31b,0x600,0x3db8,0x602,0x3db4,0x606, -0x3dc0,0x612,0x3dbc,0x8646,0x3dc4,0xd802,0x6f,0x31b,0x600,0x3dba,0x602,0x3db6,0x606,0x3dc2,0x612,0x3dbe, -0x8646,0x3dc6,0xd802,0x55,0x31b,0x600,0x3dd4,0x602,0x3dd0,0x606,0x3ddc,0x612,0x3dd8,0x8646,0x3de0,0xd802, -0x75,0x31b,0x600,0x3dd6,0x602,0x3dd2,0x606,0x3dde,0x612,0x3dda,0x8646,0x3de2,0xca02,0x4f,0x328,0x8608, -0x3d8,0xca02,0x6f,0x328,0x8608,0x3da,0xe602,0x41,0x307,0x8608,0x3c0,0xe602,0x61,0x307,0x8608,0x3c2, -0xca02,0x45,0x327,0x860c,0x3c38,0xca02,0x65,0x327,0x860c,0x3c3a,0xe602,0x4f,0x307,0x8608,0x460,0xe602, -0x6f,0x307,0x8608,0x462,0xe602,0x3b1,0x301,0x868a,0x3f68,0xe602,0x3b7,0x301,0x868a,0x3f88,0xe602,0x3b9, -0x308,0x600,0x3fa4,0x602,0x720,0x8684,0x3fae,0xe602,0x3c5,0x308,0x600,0x3fc4,0x602,0x760,0x8684,0x3fce, -0xe602,0x3c9,0x301,0x868a,0x3fe8,2,0xcc6,0xcc2,0x99aa,0x1996,2,0xdd9,0xdcf,0x9b94,0x1bba,0xdc02, -0x4c,0x323,0x8608,0x3c70,0xdc02,0x6c,0x323,0x8608,0x3c72,0xdc02,0x52,0x323,0x8608,0x3cb8,0xdc02,0x72, -0x323,0x8608,0x3cba,0xdc02,0x53,0x323,0x860e,0x3cd0,0xdc02,0x73,0x323,0x860e,0x3cd2,0xdc02,0x41,0x323, -0x604,0x3d58,0x860c,0x3d6c,0xdc02,0x61,0x323,0x604,0x3d5a,0x860c,0x3d6e,0xdc02,0x45,0x323,0x8604,0x3d8c, -0xdc02,0x65,0x323,0x8604,0x3d8e,0xdc02,0x4f,0x323,0x8604,0x3db0,0xdc02,0x6f,0x323,0x8604,0x3db2,0xe602, -0x3b1,0x313,0x600,0x3e05,0x602,0x3e09,0x684,0x3e0d,0x868a,0x3f00,0xe602,0x3b1,0x314,0x600,0x3e07,0x602, -0x3e0b,0x684,0x3e0f,0x868a,0x3f02,0x1f00,0xe643,0x3b1,0x313,0x300,0x868a,0x3f04,0x1f01,0xe643,0x3b1,0x314, -0x300,0x868a,0x3f06,0x1f00,0xe643,0x3b1,0x313,0x301,0x868a,0x3f08,0x1f01,0xe643,0x3b1,0x314,0x301,0x868a, -0x3f0a,0x1f00,0xe643,0x3b1,0x313,0x342,0x868a,0x3f0c,0x1f01,0xe643,0x3b1,0x314,0x342,0x868a,0x3f0e,0xe602, -0x391,0x313,0x600,0x3e15,0x602,0x3e19,0x684,0x3e1d,0x868a,0x3f10,0xe602,0x391,0x314,0x600,0x3e17,0x602, -0x3e1b,0x684,0x3e1f,0x868a,0x3f12,0x1f08,0xe643,0x391,0x313,0x300,0x868a,0x3f14,0x1f09,0xe643,0x391,0x314, -0x300,0x868a,0x3f16,0x1f08,0xe643,0x391,0x313,0x301,0x868a,0x3f18,0x1f09,0xe643,0x391,0x314,0x301,0x868a, -0x3f1a,0x1f08,0xe643,0x391,0x313,0x342,0x868a,0x3f1c,0x1f09,0xe643,0x391,0x314,0x342,0x868a,0x3f1e,0xe602, -0x3b5,0x313,0x600,0x3e24,0x8602,0x3e28,0xe602,0x3b5,0x314,0x600,0x3e26,0x8602,0x3e2a,0xe602,0x395,0x313, -0x600,0x3e34,0x8602,0x3e38,0xe602,0x395,0x314,0x600,0x3e36,0x8602,0x3e3a,0xe602,0x3b7,0x313,0x600,0x3e45, -0x602,0x3e49,0x684,0x3e4d,0x868a,0x3f20,0xe602,0x3b7,0x314,0x600,0x3e47,0x602,0x3e4b,0x684,0x3e4f,0x868a, -0x3f22,0x1f20,0xe643,0x3b7,0x313,0x300,0x868a,0x3f24,0x1f21,0xe643,0x3b7,0x314,0x300,0x868a,0x3f26,0x1f20, -0xe643,0x3b7,0x313,0x301,0x868a,0x3f28,0x1f21,0xe643,0x3b7,0x314,0x301,0x868a,0x3f2a,0x1f20,0xe643,0x3b7, -0x313,0x342,0x868a,0x3f2c,0x1f21,0xe643,0x3b7,0x314,0x342,0x868a,0x3f2e,0xe602,0x397,0x313,0x600,0x3e55, -0x602,0x3e59,0x684,0x3e5d,0x868a,0x3f30,0xe602,0x397,0x314,0x600,0x3e57,0x602,0x3e5b,0x684,0x3e5f,0x868a, -0x3f32,0x1f28,0xe643,0x397,0x313,0x300,0x868a,0x3f34,0x1f29,0xe643,0x397,0x314,0x300,0x868a,0x3f36,0x1f28, -0xe643,0x397,0x313,0x301,0x868a,0x3f38,0x1f29,0xe643,0x397,0x314,0x301,0x868a,0x3f3a,0x1f28,0xe643,0x397, -0x313,0x342,0x868a,0x3f3c,0x1f29,0xe643,0x397,0x314,0x342,0x868a,0x3f3e,0xe602,0x3b9,0x313,0x600,0x3e64, -0x602,0x3e68,0x8684,0x3e6c,0xe602,0x3b9,0x314,0x600,0x3e66,0x602,0x3e6a,0x8684,0x3e6e,0xe602,0x399,0x313, -0x600,0x3e74,0x602,0x3e78,0x8684,0x3e7c,0xe602,0x399,0x314,0x600,0x3e76,0x602,0x3e7a,0x8684,0x3e7e,0xe602, -0x3bf,0x313,0x600,0x3e84,0x8602,0x3e88,0xe602,0x3bf,0x314,0x600,0x3e86,0x8602,0x3e8a,0xe602,0x39f,0x313, -0x600,0x3e94,0x8602,0x3e98,0xe602,0x39f,0x314,0x600,0x3e96,0x8602,0x3e9a,0xe602,0x3c5,0x313,0x600,0x3ea4, -0x602,0x3ea8,0x8684,0x3eac,0xe602,0x3c5,0x314,0x600,0x3ea6,0x602,0x3eaa,0x8684,0x3eae,0xe602,0x3a5,0x314, -0x600,0x3eb6,0x602,0x3eba,0x8684,0x3ebe,0xe602,0x3c9,0x313,0x600,0x3ec5,0x602,0x3ec9,0x684,0x3ecd,0x868a, -0x3f40,0xe602,0x3c9,0x314,0x600,0x3ec7,0x602,0x3ecb,0x684,0x3ecf,0x868a,0x3f42,0x1f60,0xe643,0x3c9,0x313, -0x300,0x868a,0x3f44,0x1f61,0xe643,0x3c9,0x314,0x300,0x868a,0x3f46,0x1f60,0xe643,0x3c9,0x313,0x301,0x868a, -0x3f48,0x1f61,0xe643,0x3c9,0x314,0x301,0x868a,0x3f4a,0x1f60,0xe643,0x3c9,0x313,0x342,0x868a,0x3f4c,0x1f61, -0xe643,0x3c9,0x314,0x342,0x868a,0x3f4e,0xe602,0x3a9,0x313,0x600,0x3ed5,0x602,0x3ed9,0x684,0x3edd,0x868a, -0x3f50,0xe602,0x3a9,0x314,0x600,0x3ed7,0x602,0x3edb,0x684,0x3edf,0x868a,0x3f52,0x1f68,0xe643,0x3a9,0x313, -0x300,0x868a,0x3f54,0x1f69,0xe643,0x3a9,0x314,0x300,0x868a,0x3f56,0x1f68,0xe643,0x3a9,0x313,0x301,0x868a, -0x3f58,0x1f69,0xe643,0x3a9,0x314,0x301,0x868a,0x3f5a,0x1f68,0xe643,0x3a9,0x313,0x342,0x868a,0x3f5c,0x1f69, -0xe643,0x3a9,0x314,0x342,0x868a,0x3f5e,0xe602,0x3b1,0x300,0x868a,0x3f64,0xe602,0x3b7,0x300,0x868a,0x3f84, -0xe602,0x3c9,0x300,0x868a,0x3fe4,0xe602,0x3b1,0x342,0x868a,0x3f6e,0xe602,0x3b7,0x342,0x868a,0x3f8e,0xe602, -0x3c9,0x342,0x868a,0x3fee,3,0xe602,0x41,0x300,0xe602,0x41,0x301,0xe602,0x41,0x303,0xe602,0x45, -0x300,0xe602,0x45,0x301,0xe602,0x45,0x308,0xe602,0x49,0x300,0xe602,0x49,0x301,0xe602,0x49,0x302, -0xe602,0x4e,0x303,0xe602,0x4f,0x300,0xe602,0x4f,0x301,0xe602,0x55,0x300,0xe602,0x55,0x301,0xe602, -0x55,0x302,0xe602,0x59,0x301,0xe602,0x61,0x300,0xe602,0x61,0x301,0xe602,0x61,0x303,0xe602,0x65, -0x300,0xe602,0x65,0x301,0xe602,0x65,0x308,0xe602,0x69,0x300,0xe602,0x69,0x301,0xe602,0x69,0x302, -0xe602,0x6e,0x303,0xe602,0x6f,0x300,0xe602,0x6f,0x301,0xe602,0x75,0x300,0xe602,0x75,0x301,0xe602, -0x75,0x302,0xe602,0x79,0x301,0xe602,0x79,0x308,0xe602,0x41,0x304,0xe602,0x61,0x304,0xca02,0x41, -0x328,0xca02,0x61,0x328,0xe602,0x43,0x301,0xe602,0x63,0x301,0xe602,0x43,0x302,0xe602,0x63,0x302, -0xe602,0x43,0x307,0xe602,0x63,0x307,0xe602,0x43,0x30c,0xe602,0x63,0x30c,0xe602,0x44,0x30c,0xe602, -0x64,0x30c,0xe602,0x45,0x306,0xe602,0x65,0x306,0xe602,0x45,0x307,0xe602,0x65,0x307,0xca02,0x45, -0x328,0xca02,0x65,0x328,0xe602,0x45,0x30c,0xe602,0x65,0x30c,0xe602,0x47,0x302,0xe602,0x67,0x302, -0xe602,0x47,0x306,0xe602,0x67,0x306,0xe602,0x47,0x307,0xe602,0x67,0x307,0xca02,0x47,0x327,0xca02, -0x67,0x327,0xe602,0x48,0x302,0xe602,0x68,0x302,0xe602,0x49,0x303,0xe602,0x69,0x303,0xe602,0x49, -0x304,0xe602,0x69,0x304,0xe602,0x49,0x306,0xe602,0x69,0x306,0xca02,0x49,0x328,0xca02,0x69,0x328, -0xe602,0x49,0x307,0xe602,0x4a,0x302,0xe602,0x6a,0x302,0xca02,0x4b,0x327,0xca02,0x6b,0x327,0xe602, -0x4c,0x301,0xe602,0x6c,0x301,0xca02,0x4c,0x327,0xca02,0x6c,0x327,0xe602,0x4c,0x30c,0xe602,0x6c, -0x30c,0xe602,0x4e,0x301,0xe602,0x6e,0x301,0xca02,0x4e,0x327,0xca02,0x6e,0x327,0xe602,0x4e,0x30c, -0xe602,0x6e,0x30c,0xe602,0x4f,0x306,0xe602,0x6f,0x306,0xe602,0x4f,0x30b,0xe602,0x6f,0x30b,0xe602, -0x52,0x301,0xe602,0x72,0x301,0xca02,0x52,0x327,0xca02,0x72,0x327,0xe602,0x52,0x30c,0xe602,0x72, -0x30c,0xe602,0x53,0x302,0xe602,0x73,0x302,0xca02,0x53,0x327,0xca02,0x73,0x327,0xca02,0x54,0x327, -0xca02,0x74,0x327,0xe602,0x54,0x30c,0xe602,0x74,0x30c,0xe602,0x55,0x306,0xe602,0x75,0x306,0xe602, -0x55,0x30a,0xe602,0x75,0x30a,0xe602,0x55,0x30b,0xe602,0x75,0x30b,0xca02,0x55,0x328,0xca02,0x75, -0x328,0xe602,0x57,0x302,0xe602,0x77,0x302,0xe602,0x59,0x302,0xe602,0x79,0x302,0xe602,0x59,0x308, -0xe602,0x5a,0x301,0xe602,0x7a,0x301,0xe602,0x5a,0x307,0xe602,0x7a,0x307,0xe602,0x5a,0x30c,0xe602, -0x7a,0x30c,0xe602,0x41,0x30c,0xe602,0x61,0x30c,0xe602,0x49,0x30c,0xe602,0x69,0x30c,0xe602,0x4f, -0x30c,0xe602,0x6f,0x30c,0xe602,0x55,0x30c,0xe602,0x75,0x30c,0xdc,0xe643,0x55,0x308,0x304,0xfc, -0xe643,0x75,0x308,0x304,0xdc,0xe643,0x55,0x308,0x301,0xfc,0xe643,0x75,0x308,0x301,0xdc,0xe643, -0x55,0x308,0x30c,0xfc,0xe643,0x75,0x308,0x30c,0xdc,0xe643,0x55,0x308,0x300,0xfc,0xe643,0x75, -0x308,0x300,0xc4,0xe643,0x41,0x308,0x304,0xe4,0xe643,0x61,0x308,0x304,0x226,0xe643,0x41,0x307, -0x304,0x227,0xe643,0x61,0x307,0x304,0xe602,0xc6,0x304,0xe602,0xe6,0x304,0xe602,0x47,0x30c,0xe602, -0x67,0x30c,0xe602,0x4b,0x30c,0xe602,0x6b,0x30c,0x1ea,0xe643,0x4f,0x328,0x304,0x1eb,0xe643,0x6f, -0x328,0x304,0xe602,0x1b7,0x30c,0xe602,0x292,0x30c,0xe602,0x6a,0x30c,0xe602,0x47,0x301,0xe602,0x67, -0x301,0xe602,0x4e,0x300,0xe602,0x6e,0x300,0xc5,0xe643,0x41,0x30a,0x301,0xe5,0xe643,0x61,0x30a, -0x301,0xe602,0xc6,0x301,0xe602,0xe6,0x301,0xe602,0xd8,0x301,0xe602,0xf8,0x301,0xe602,0x41,0x30f, -0xe602,0x61,0x30f,0xe602,0x41,0x311,0xe602,0x61,0x311,0xe602,0x45,0x30f,0xe602,0x65,0x30f,0xe602, -0x45,0x311,0xe602,0x65,0x311,0xe602,0x49,0x30f,0xe602,0x69,0x30f,0xe602,0x49,0x311,0xe602,0x69, -0x311,0xe602,0x4f,0x30f,0xe602,0x6f,0x30f,0xe602,0x4f,0x311,0xe602,0x6f,0x311,0xe602,0x52,0x30f, -0xe602,0x72,0x30f,0xe602,0x52,0x311,0xe602,0x72,0x311,0xe602,0x55,0x30f,0xe602,0x75,0x30f,0xe602, -0x55,0x311,0xe602,0x75,0x311,0xdc02,0x53,0x326,0xdc02,0x73,0x326,0xdc02,0x54,0x326,0xdc02,0x74, -0x326,0xe602,0x48,0x30c,0xe602,0x68,0x30c,0xd6,0xe643,0x4f,0x308,0x304,0xf6,0xe643,0x6f,0x308, -0x304,0xd5,0xe643,0x4f,0x303,0x304,0xf5,0xe643,0x6f,0x303,0x304,0x22e,0xe643,0x4f,0x307,0x304, -0x22f,0xe643,0x6f,0x307,0x304,0xe602,0x59,0x304,0xe602,0x79,0x304,0xe602,0xa8,0x301,0xe602,0x391, -0x301,0xe602,0x395,0x301,0xe602,0x397,0x301,0xe602,0x399,0x301,0xe602,0x39f,0x301,0xe602,0x3a5,0x301, -0xe602,0x3a9,0x301,0x3ca,0xe643,0x3b9,0x308,0x301,0xe602,0x399,0x308,0xe602,0x3a5,0x308,0xe602,0x3b5, -0x301,0xe602,0x3b9,0x301,0x3cb,0xe643,0x3c5,0x308,0x301,0xe602,0x3bf,0x301,0xe602,0x3c5,0x301,0xe602, -0x3d2,0x301,0xe602,0x3d2,0x308,0xe602,0x415,0x300,0xe602,0x415,0x308,0xe602,0x413,0x301,0xe602,0x406, -0x308,0xe602,0x41a,0x301,0xe602,0x418,0x300,0xe602,0x423,0x306,0xe602,0x418,0x306,0xe602,0x438,0x306, -0xe602,0x435,0x300,0xe602,0x435,0x308,0xe602,0x433,0x301,0xe602,0x456,0x308,0xe602,0x43a,0x301,0xe602, -0x438,0x300,0xe602,0x443,0x306,0xe602,0x474,0x30f,0xe602,0x475,0x30f,0xe602,0x416,0x306,0xe602,0x436, -0x306,0xe602,0x410,0x306,0xe602,0x430,0x306,0xe602,0x410,0x308,0xe602,0x430,0x308,0xe602,0x415,0x306, -0xe602,0x435,0x306,0xe602,0x4d8,0x308,0xe602,0x4d9,0x308,0xe602,0x416,0x308,0xe602,0x436,0x308,0xe602, -0x417,0x308,0xe602,0x437,0x308,0xe602,0x418,0x304,0xe602,0x438,0x304,0xe602,0x418,0x308,0xe602,0x438, -0x308,0xe602,0x41e,0x308,0xe602,0x43e,0x308,0xe602,0x4e8,0x308,0xe602,0x4e9,0x308,0xe602,0x42d,0x308, -0xe602,0x44d,0x308,0xe602,0x423,0x304,0xe602,0x443,0x304,0xe602,0x423,0x308,0xe602,0x443,0x308,0xe602, -0x423,0x30b,0xe602,0x443,0x30b,0xe602,0x427,0x308,0xe602,0x447,0x308,0xe602,0x42b,0x308,0xe602,0x44b, -0x308,0xe602,0x627,0x653,0xe602,0x627,0x654,0xe602,0x648,0x654,0xdc02,0x627,0x655,0xe602,0x64a,0x654, -0xe602,0x6d5,0x654,0xe602,0x6c1,0x654,0xe602,0x6d2,0x654,0x702,0x928,0x93c,0x702,0x930,0x93c,0x702, -0x933,0x93c,2,0x9c7,0x9be,2,0x9c7,0x9d7,2,0xb47,0xb56,2,0xb47,0xb3e,2,0xb47, -0xb57,2,0xb92,0xbd7,2,0xbc6,0xbbe,2,0xbc7,0xbbe,2,0xbc6,0xbd7,0x5b02,0xc46,0xc56, -2,0xcbf,0xcd5,2,0xcc6,0xcd5,2,0xcc6,0xcd6,0xcca,0x43,0xcc6,0xcc2,0xcd5,2,0xd46, -0xd3e,2,0xd47,0xd3e,2,0xd46,0xd57,0x902,0xdd9,0xdca,0xddc,0x943,0xdd9,0xdcf,0xdca,2, -0xdd9,0xddf,2,0x1025,0x102e,2,0x1b05,0x1b35,2,0x1b07,0x1b35,2,0x1b09,0x1b35,2,0x1b0b, -0x1b35,2,0x1b0d,0x1b35,2,0x1b11,0x1b35,2,0x1b3a,0x1b35,2,0x1b3c,0x1b35,2,0x1b3e,0x1b35, -2,0x1b3f,0x1b35,2,0x1b42,0x1b35,0xdc02,0x41,0x325,0xdc02,0x61,0x325,0xe602,0x42,0x307,0xe602, -0x62,0x307,0xdc02,0x42,0x323,0xdc02,0x62,0x323,0xdc02,0x42,0x331,0xdc02,0x62,0x331,0xc7,0xe643, -0x43,0x327,0x301,0xe7,0xe643,0x63,0x327,0x301,0xe602,0x44,0x307,0xe602,0x64,0x307,0xdc02,0x44, -0x323,0xdc02,0x64,0x323,0xdc02,0x44,0x331,0xdc02,0x64,0x331,0xca02,0x44,0x327,0xca02,0x64,0x327, -0xdc02,0x44,0x32d,0xdc02,0x64,0x32d,0x112,0xe643,0x45,0x304,0x300,0x113,0xe643,0x65,0x304,0x300, -0x112,0xe643,0x45,0x304,0x301,0x113,0xe643,0x65,0x304,0x301,0xdc02,0x45,0x32d,0xdc02,0x65,0x32d, -0xdc02,0x45,0x330,0xdc02,0x65,0x330,0x228,0xe643,0x45,0x327,0x306,0x229,0xe643,0x65,0x327,0x306, -0xe602,0x46,0x307,0xe602,0x66,0x307,0xe602,0x47,0x304,0xe602,0x67,0x304,0xe602,0x48,0x307,0xe602, -0x68,0x307,0xdc02,0x48,0x323,0xdc02,0x68,0x323,0xe602,0x48,0x308,0xe602,0x68,0x308,0xca02,0x48, -0x327,0xca02,0x68,0x327,0xdc02,0x48,0x32e,0xdc02,0x68,0x32e,0xdc02,0x49,0x330,0xdc02,0x69,0x330, -0xcf,0xe643,0x49,0x308,0x301,0xef,0xe643,0x69,0x308,0x301,0xe602,0x4b,0x301,0xe602,0x6b,0x301, -0xdc02,0x4b,0x323,0xdc02,0x6b,0x323,0xdc02,0x4b,0x331,0xdc02,0x6b,0x331,0x1e36,0xe643,0x4c,0x323, -0x304,0x1e37,0xe643,0x6c,0x323,0x304,0xdc02,0x4c,0x331,0xdc02,0x6c,0x331,0xdc02,0x4c,0x32d,0xdc02, -0x6c,0x32d,0xe602,0x4d,0x301,0xe602,0x6d,0x301,0xe602,0x4d,0x307,0xe602,0x6d,0x307,0xdc02,0x4d, -0x323,0xdc02,0x6d,0x323,0xe602,0x4e,0x307,0xe602,0x6e,0x307,0xdc02,0x4e,0x323,0xdc02,0x6e,0x323, -0xdc02,0x4e,0x331,0xdc02,0x6e,0x331,0xdc02,0x4e,0x32d,0xdc02,0x6e,0x32d,0xd5,0xe643,0x4f,0x303, -0x301,0xf5,0xe643,0x6f,0x303,0x301,0xd5,0xe643,0x4f,0x303,0x308,0xf5,0xe643,0x6f,0x303,0x308, -0x14c,0xe643,0x4f,0x304,0x300,0x14d,0xe643,0x6f,0x304,0x300,0x14c,0xe643,0x4f,0x304,0x301,0x14d, -0xe643,0x6f,0x304,0x301,0xe602,0x50,0x301,0xe602,0x70,0x301,0xe602,0x50,0x307,0xe602,0x70,0x307, -0xe602,0x52,0x307,0xe602,0x72,0x307,0x1e5a,0xe643,0x52,0x323,0x304,0x1e5b,0xe643,0x72,0x323,0x304, -0xdc02,0x52,0x331,0xdc02,0x72,0x331,0xe602,0x53,0x307,0xe602,0x73,0x307,0x15a,0xe643,0x53,0x301, -0x307,0x15b,0xe643,0x73,0x301,0x307,0x160,0xe643,0x53,0x30c,0x307,0x161,0xe643,0x73,0x30c,0x307, -0x1e62,0xe643,0x53,0x323,0x307,0x1e63,0xe643,0x73,0x323,0x307,0xe602,0x54,0x307,0xe602,0x74,0x307, -0xdc02,0x54,0x323,0xdc02,0x74,0x323,0xdc02,0x54,0x331,0xdc02,0x74,0x331,0xdc02,0x54,0x32d,0xdc02, -0x74,0x32d,0xdc02,0x55,0x324,0xdc02,0x75,0x324,0xdc02,0x55,0x330,0xdc02,0x75,0x330,0xdc02,0x55, -0x32d,0xdc02,0x75,0x32d,0x168,0xe643,0x55,0x303,0x301,0x169,0xe643,0x75,0x303,0x301,0x16a,0xe643, -0x55,0x304,0x308,0x16b,0xe643,0x75,0x304,0x308,0xe602,0x56,0x303,0xe602,0x76,0x303,0xdc02,0x56, -0x323,0xdc02,0x76,0x323,0xe602,0x57,0x300,0xe602,0x77,0x300,0xe602,0x57,0x301,0xe602,0x77,0x301, -0xe602,0x57,0x308,0xe602,0x77,0x308,0xe602,0x57,0x307,0xe602,0x77,0x307,0xdc02,0x57,0x323,0xdc02, -0x77,0x323,0xe602,0x58,0x307,0xe602,0x78,0x307,0xe602,0x58,0x308,0xe602,0x78,0x308,0xe602,0x59, -0x307,0xe602,0x79,0x307,0xe602,0x5a,0x302,0xe602,0x7a,0x302,0xdc02,0x5a,0x323,0xdc02,0x7a,0x323, -0xdc02,0x5a,0x331,0xdc02,0x7a,0x331,0xdc02,0x68,0x331,0xe602,0x74,0x308,0xe602,0x77,0x30a,0xe602, -0x79,0x30a,0xe602,0x17f,0x307,0xe602,0x41,0x309,0xe602,0x61,0x309,0xc2,0xe643,0x41,0x302,0x301, -0xe2,0xe643,0x61,0x302,0x301,0xc2,0xe643,0x41,0x302,0x300,0xe2,0xe643,0x61,0x302,0x300,0xc2, -0xe643,0x41,0x302,0x309,0xe2,0xe643,0x61,0x302,0x309,0xc2,0xe643,0x41,0x302,0x303,0xe2,0xe643, -0x61,0x302,0x303,0x1ea0,0xe643,0x41,0x323,0x302,0x1ea1,0xe643,0x61,0x323,0x302,0x102,0xe643,0x41, -0x306,0x301,0x103,0xe643,0x61,0x306,0x301,0x102,0xe643,0x41,0x306,0x300,0x103,0xe643,0x61,0x306, -0x300,0x102,0xe643,0x41,0x306,0x309,0x103,0xe643,0x61,0x306,0x309,0x102,0xe643,0x41,0x306,0x303, -0x103,0xe643,0x61,0x306,0x303,0x1ea0,0xe643,0x41,0x323,0x306,0x1ea1,0xe643,0x61,0x323,0x306,0xe602, -0x45,0x309,0xe602,0x65,0x309,0xe602,0x45,0x303,0xe602,0x65,0x303,0xca,0xe643,0x45,0x302,0x301, -0xea,0xe643,0x65,0x302,0x301,0xca,0xe643,0x45,0x302,0x300,0xea,0xe643,0x65,0x302,0x300,0xca, -0xe643,0x45,0x302,0x309,0xea,0xe643,0x65,0x302,0x309,0xca,0xe643,0x45,0x302,0x303,0xea,0xe643, -0x65,0x302,0x303,0x1eb8,0xe643,0x45,0x323,0x302,0x1eb9,0xe643,0x65,0x323,0x302,0xe602,0x49,0x309, -0xe602,0x69,0x309,0xdc02,0x49,0x323,0xdc02,0x69,0x323,0xe602,0x4f,0x309,0xe602,0x6f,0x309,0xd4, -0xe643,0x4f,0x302,0x301,0xf4,0xe643,0x6f,0x302,0x301,0xd4,0xe643,0x4f,0x302,0x300,0xf4,0xe643, -0x6f,0x302,0x300,0xd4,0xe643,0x4f,0x302,0x309,0xf4,0xe643,0x6f,0x302,0x309,0xd4,0xe643,0x4f, -0x302,0x303,0xf4,0xe643,0x6f,0x302,0x303,0x1ecc,0xe643,0x4f,0x323,0x302,0x1ecd,0xe643,0x6f,0x323, -0x302,0x1a0,0xe643,0x4f,0x31b,0x301,0x1a1,0xe643,0x6f,0x31b,0x301,0x1a0,0xe643,0x4f,0x31b,0x300, -0x1a1,0xe643,0x6f,0x31b,0x300,0x1a0,0xe643,0x4f,0x31b,0x309,0x1a1,0xe643,0x6f,0x31b,0x309,0x1a0, -0xe643,0x4f,0x31b,0x303,0x1a1,0xe643,0x6f,0x31b,0x303,0x1a0,0xdc43,0x4f,0x31b,0x323,0x1a1,0xdc43, -0x6f,0x31b,0x323,0xdc02,0x55,0x323,0xdc02,0x75,0x323,0xe602,0x55,0x309,0xe602,0x75,0x309,0x1af, -0xe643,0x55,0x31b,0x301,0x1b0,0xe643,0x75,0x31b,0x301,0x1af,0xe643,0x55,0x31b,0x300,0x1b0,0xe643, -0x75,0x31b,0x300,0x1af,0xe643,0x55,0x31b,0x309,0x1b0,0xe643,0x75,0x31b,0x309,0x1af,0xe643,0x55, -0x31b,0x303,0x1b0,0xe643,0x75,0x31b,0x303,0x1af,0xdc43,0x55,0x31b,0x323,0x1b0,0xdc43,0x75,0x31b, -0x323,0xe602,0x59,0x300,0xe602,0x79,0x300,0xdc02,0x59,0x323,0xdc02,0x79,0x323,0xe602,0x59,0x309, -0xe602,0x79,0x309,0xe602,0x59,0x303,0xe602,0x79,0x303,0x1f10,0xe643,0x3b5,0x313,0x300,0x1f11,0xe643, -0x3b5,0x314,0x300,0x1f10,0xe643,0x3b5,0x313,0x301,0x1f11,0xe643,0x3b5,0x314,0x301,0x1f18,0xe643,0x395, -0x313,0x300,0x1f19,0xe643,0x395,0x314,0x300,0x1f18,0xe643,0x395,0x313,0x301,0x1f19,0xe643,0x395,0x314, -0x301,0x1f30,0xe643,0x3b9,0x313,0x300,0x1f31,0xe643,0x3b9,0x314,0x300,0x1f30,0xe643,0x3b9,0x313,0x301, -0x1f31,0xe643,0x3b9,0x314,0x301,0x1f30,0xe643,0x3b9,0x313,0x342,0x1f31,0xe643,0x3b9,0x314,0x342,0x1f38, -0xe643,0x399,0x313,0x300,0x1f39,0xe643,0x399,0x314,0x300,0x1f38,0xe643,0x399,0x313,0x301,0x1f39,0xe643, -0x399,0x314,0x301,0x1f38,0xe643,0x399,0x313,0x342,0x1f39,0xe643,0x399,0x314,0x342,0x1f40,0xe643,0x3bf, -0x313,0x300,0x1f41,0xe643,0x3bf,0x314,0x300,0x1f40,0xe643,0x3bf,0x313,0x301,0x1f41,0xe643,0x3bf,0x314, -0x301,0x1f48,0xe643,0x39f,0x313,0x300,0x1f49,0xe643,0x39f,0x314,0x300,0x1f48,0xe643,0x39f,0x313,0x301, -0x1f49,0xe643,0x39f,0x314,0x301,0x1f50,0xe643,0x3c5,0x313,0x300,0x1f51,0xe643,0x3c5,0x314,0x300,0x1f50, -0xe643,0x3c5,0x313,0x301,0x1f51,0xe643,0x3c5,0x314,0x301,0x1f50,0xe643,0x3c5,0x313,0x342,0x1f51,0xe643, -0x3c5,0x314,0x342,0x1f59,0xe643,0x3a5,0x314,0x300,0x1f59,0xe643,0x3a5,0x314,0x301,0x1f59,0xe643,0x3a5, -0x314,0x342,0xe602,0x3b5,0x300,0xe602,0x3b9,0x300,0xe602,0x3bf,0x300,0xe602,0x3c5,0x300,0x1f00,0xf043, -0x3b1,0x313,0x345,0x1f01,0xf043,0x3b1,0x314,0x345,0x1f02,0x345,2,0xf044,0x3b1,0x313,0x300,0x345, -0x1f03,0x345,2,0xf044,0x3b1,0x314,0x300,0x345,0x1f04,0x345,2,0xf044,0x3b1,0x313,0x301,0x345, -0x1f05,0x345,2,0xf044,0x3b1,0x314,0x301,0x345,0x1f06,0x345,2,0xf044,0x3b1,0x313,0x342,0x345, -0x1f07,0x345,2,0xf044,0x3b1,0x314,0x342,0x345,0x1f08,0xf043,0x391,0x313,0x345,0x1f09,0xf043,0x391, -0x314,0x345,0x1f0a,0x345,2,0xf044,0x391,0x313,0x300,0x345,0x1f0b,0x345,2,0xf044,0x391,0x314, -0x300,0x345,0x1f0c,0x345,2,0xf044,0x391,0x313,0x301,0x345,0x1f0d,0x345,2,0xf044,0x391,0x314, -0x301,0x345,0x1f0e,0x345,2,0xf044,0x391,0x313,0x342,0x345,0x1f0f,0x345,2,0xf044,0x391,0x314, -0x342,0x345,0x1f20,0xf043,0x3b7,0x313,0x345,0x1f21,0xf043,0x3b7,0x314,0x345,0x1f22,0x345,2,0xf044, -0x3b7,0x313,0x300,0x345,0x1f23,0x345,2,0xf044,0x3b7,0x314,0x300,0x345,0x1f24,0x345,2,0xf044, -0x3b7,0x313,0x301,0x345,0x1f25,0x345,2,0xf044,0x3b7,0x314,0x301,0x345,0x1f26,0x345,2,0xf044, -0x3b7,0x313,0x342,0x345,0x1f27,0x345,2,0xf044,0x3b7,0x314,0x342,0x345,0x1f28,0xf043,0x397,0x313, -0x345,0x1f29,0xf043,0x397,0x314,0x345,0x1f2a,0x345,2,0xf044,0x397,0x313,0x300,0x345,0x1f2b,0x345, -2,0xf044,0x397,0x314,0x300,0x345,0x1f2c,0x345,2,0xf044,0x397,0x313,0x301,0x345,0x1f2d,0x345, -2,0xf044,0x397,0x314,0x301,0x345,0x1f2e,0x345,2,0xf044,0x397,0x313,0x342,0x345,0x1f2f,0x345, -2,0xf044,0x397,0x314,0x342,0x345,0x1f60,0xf043,0x3c9,0x313,0x345,0x1f61,0xf043,0x3c9,0x314,0x345, -0x1f62,0x345,2,0xf044,0x3c9,0x313,0x300,0x345,0x1f63,0x345,2,0xf044,0x3c9,0x314,0x300,0x345, -0x1f64,0x345,2,0xf044,0x3c9,0x313,0x301,0x345,0x1f65,0x345,2,0xf044,0x3c9,0x314,0x301,0x345, -0x1f66,0x345,2,0xf044,0x3c9,0x313,0x342,0x345,0x1f67,0x345,2,0xf044,0x3c9,0x314,0x342,0x345, -0x1f68,0xf043,0x3a9,0x313,0x345,0x1f69,0xf043,0x3a9,0x314,0x345,0x1f6a,0x345,2,0xf044,0x3a9,0x313, -0x300,0x345,0x1f6b,0x345,2,0xf044,0x3a9,0x314,0x300,0x345,0x1f6c,0x345,2,0xf044,0x3a9,0x313, -0x301,0x345,0x1f6d,0x345,2,0xf044,0x3a9,0x314,0x301,0x345,0x1f6e,0x345,2,0xf044,0x3a9,0x313, -0x342,0x345,0x1f6f,0x345,2,0xf044,0x3a9,0x314,0x342,0x345,0xe602,0x3b1,0x306,0xe602,0x3b1,0x304, -0x1f70,0xf043,0x3b1,0x300,0x345,0xf002,0x3b1,0x345,0x3ac,0xf043,0x3b1,0x301,0x345,0x1fb6,0xf043,0x3b1, -0x342,0x345,0xe602,0x391,0x306,0xe602,0x391,0x304,0xe602,0x391,0x300,0xf002,0x391,0x345,0xe602,0xa8, -0x342,0x1f74,0xf043,0x3b7,0x300,0x345,0xf002,0x3b7,0x345,0x3ae,0xf043,0x3b7,0x301,0x345,0x1fc6,0xf043, -0x3b7,0x342,0x345,0xe602,0x395,0x300,0xe602,0x397,0x300,0xf002,0x397,0x345,0xe602,0x1fbf,0x300,0xe602, -0x1fbf,0x301,0xe602,0x1fbf,0x342,0xe602,0x3b9,0x306,0xe602,0x3b9,0x304,0x3ca,0xe643,0x3b9,0x308,0x300, -0xe602,0x3b9,0x342,0x3ca,0xe643,0x3b9,0x308,0x342,0xe602,0x399,0x306,0xe602,0x399,0x304,0xe602,0x399, -0x300,0xe602,0x1ffe,0x300,0xe602,0x1ffe,0x301,0xe602,0x1ffe,0x342,0xe602,0x3c5,0x306,0xe602,0x3c5,0x304, -0x3cb,0xe643,0x3c5,0x308,0x300,0xe602,0x3c1,0x313,0xe602,0x3c1,0x314,0xe602,0x3c5,0x342,0x3cb,0xe643, -0x3c5,0x308,0x342,0xe602,0x3a5,0x306,0xe602,0x3a5,0x304,0xe602,0x3a5,0x300,0xe602,0x3a1,0x314,0xe602, -0xa8,0x300,0x1f7c,0xf043,0x3c9,0x300,0x345,0xf002,0x3c9,0x345,0x3ce,0xf043,0x3c9,0x301,0x345,0x1ff6, -0xf043,0x3c9,0x342,0x345,0xe602,0x39f,0x300,0xe602,0x3a9,0x300,0xf002,0x3a9,0x345,0x102,0x2190,0x338, -0x102,0x2192,0x338,0x102,0x2194,0x338,0x102,0x21d0,0x338,0x102,0x21d4,0x338,0x102,0x21d2,0x338,0x102, -0x2203,0x338,0x102,0x2208,0x338,0x102,0x220b,0x338,0x102,0x2223,0x338,0x102,0x2225,0x338,0x102,0x223c, -0x338,0x102,0x2243,0x338,0x102,0x2245,0x338,0x102,0x2248,0x338,0x102,0x3d,0x338,0x102,0x2261,0x338, -0x102,0x224d,0x338,0x102,0x3c,0x338,0x102,0x3e,0x338,0x102,0x2264,0x338,0x102,0x2265,0x338,0x102, -0x2272,0x338,0x102,0x2273,0x338,0x102,0x2276,0x338,0x102,0x2277,0x338,0x102,0x227a,0x338,0x102,0x227b, -0x338,0x102,0x2282,0x338,0x102,0x2283,0x338,0x102,0x2286,0x338,0x102,0x2287,0x338,0x102,0x22a2,0x338, -0x102,0x22a8,0x338,0x102,0x22a9,0x338,0x102,0x22ab,0x338,0x102,0x227c,0x338,0x102,0x227d,0x338,0x102, -0x2291,0x338,0x102,0x2292,0x338,0x102,0x22b2,0x338,0x102,0x22b3,0x338,0x102,0x22b4,0x338,0x102,0x22b5, -0x338,0x802,0x304b,0x3099,0x802,0x304d,0x3099,0x802,0x304f,0x3099,0x802,0x3051,0x3099,0x802,0x3053,0x3099, -0x802,0x3055,0x3099,0x802,0x3057,0x3099,0x802,0x3059,0x3099,0x802,0x305b,0x3099,0x802,0x305d,0x3099,0x802, -0x305f,0x3099,0x802,0x3061,0x3099,0x802,0x3064,0x3099,0x802,0x3066,0x3099,0x802,0x3068,0x3099,0x802,0x306f, -0x3099,0x802,0x306f,0x309a,0x802,0x3072,0x3099,0x802,0x3072,0x309a,0x802,0x3075,0x3099,0x802,0x3075,0x309a, -0x802,0x3078,0x3099,0x802,0x3078,0x309a,0x802,0x307b,0x3099,0x802,0x307b,0x309a,0x802,0x3046,0x3099,0x802, -0x309d,0x3099,0x802,0x30ab,0x3099,0x802,0x30ad,0x3099,0x802,0x30af,0x3099,0x802,0x30b1,0x3099,0x802,0x30b3, -0x3099,0x802,0x30b5,0x3099,0x802,0x30b7,0x3099,0x802,0x30b9,0x3099,0x802,0x30bb,0x3099,0x802,0x30bd,0x3099, -0x802,0x30bf,0x3099,0x802,0x30c1,0x3099,0x802,0x30c4,0x3099,0x802,0x30c6,0x3099,0x802,0x30c8,0x3099,0x802, -0x30cf,0x3099,0x802,0x30cf,0x309a,0x802,0x30d2,0x3099,0x802,0x30d2,0x309a,0x802,0x30d5,0x3099,0x802,0x30d5, -0x309a,0x802,0x30d8,0x3099,0x802,0x30d8,0x309a,0x802,0x30db,0x3099,0x802,0x30db,0x309a,0x802,0x30a6,0x3099, -0x802,0x30ef,0x3099,0x802,0x30f0,0x3099,0x802,0x30f1,0x3099,0x802,0x30f2,0x3099,0x802,0x30fd,0x3099,0x704, -0xd804,0xdc99,0xd804,0xdcba,0x704,0xd804,0xdc9b,0xd804,0xdcba,0x704,0xd804,0xdca5,0xd804,0xdcba,4,0xd804, -0xdd31,0xd804,0xdd27,4,0xd804,0xdd32,0xd804,0xdd27,4,0xd804,0xdf47,0xd804,0xdf3e,4,0xd804,0xdf47, -0xd804,0xdf57,4,0xd805,0xdcb9,0xd805,0xdcba,4,0xd805,0xdcb9,0xd805,0xdcb0,4,0xd805,0xdcb9,0xd805, -0xdcbd,4,0xd805,0xddb8,0xd805,0xddaf,4,0xd805,0xddb9,0xd805,0xddaf,4,0xd806,0xdd35,0xd806,0xdd30, -1,0x2b9,1,0x3b,1,0xb7,0x702,0x915,0x93c,0x702,0x916,0x93c,0x702,0x917,0x93c,0x702, -0x91c,0x93c,0x702,0x921,0x93c,0x702,0x922,0x93c,0x702,0x92b,0x93c,0x702,0x92f,0x93c,0x702,0x9a1, -0x9bc,0x702,0x9a2,0x9bc,0x702,0x9af,0x9bc,0x702,0xa32,0xa3c,0x702,0xa38,0xa3c,0x702,0xa16,0xa3c, -0x702,0xa17,0xa3c,0x702,0xa1c,0xa3c,0x702,0xa2b,0xa3c,0x702,0xb21,0xb3c,0x702,0xb22,0xb3c,2, -0xf42,0xfb7,2,0xf4c,0xfb7,2,0xf51,0xfb7,2,0xf56,0xfb7,2,0xf5b,0xfb7,2,0xf40, -0xfb5,0x8202,0xfb2,0xf80,0x8202,0xfb3,0xf80,2,0xf92,0xfb7,2,0xf9c,0xfb7,2,0xfa1,0xfb7, -2,0xfa6,0xfb7,2,0xfab,0xfb7,2,0xf90,0xfb5,1,0x3b9,1,0x60,1,0xb4,1, -0x3a9,1,0x4b,1,0x3008,1,0x3009,0x102,0x2add,0x338,1,0x8c48,1,0x66f4,1,0x8eca, -1,0x8cc8,1,0x6ed1,1,0x4e32,1,0x53e5,1,0x9f9c,1,0x5951,1,0x91d1,1,0x5587, -1,0x5948,1,0x61f6,1,0x7669,1,0x7f85,1,0x863f,1,0x87ba,1,0x88f8,1,0x908f, -1,0x6a02,1,0x6d1b,1,0x70d9,1,0x73de,1,0x843d,1,0x916a,1,0x99f1,1,0x4e82, -1,0x5375,1,0x6b04,1,0x721b,1,0x862d,1,0x9e1e,1,0x5d50,1,0x6feb,1,0x85cd, -1,0x8964,1,0x62c9,1,0x81d8,1,0x881f,1,0x5eca,1,0x6717,1,0x6d6a,1,0x72fc, -1,0x90ce,1,0x4f86,1,0x51b7,1,0x52de,1,0x64c4,1,0x6ad3,1,0x7210,1,0x76e7, -1,0x8001,1,0x8606,1,0x865c,1,0x8def,1,0x9732,1,0x9b6f,1,0x9dfa,1,0x788c, -1,0x797f,1,0x7da0,1,0x83c9,1,0x9304,1,0x9e7f,1,0x8ad6,1,0x58df,1,0x5f04, -1,0x7c60,1,0x807e,1,0x7262,1,0x78ca,1,0x8cc2,1,0x96f7,1,0x58d8,1,0x5c62, -1,0x6a13,1,0x6dda,1,0x6f0f,1,0x7d2f,1,0x7e37,1,0x964b,1,0x52d2,1,0x808b, -1,0x51dc,1,0x51cc,1,0x7a1c,1,0x7dbe,1,0x83f1,1,0x9675,1,0x8b80,1,0x62cf, -1,0x8afe,1,0x4e39,1,0x5be7,1,0x6012,1,0x7387,1,0x7570,1,0x5317,1,0x78fb, -1,0x4fbf,1,0x5fa9,1,0x4e0d,1,0x6ccc,1,0x6578,1,0x7d22,1,0x53c3,1,0x585e, -1,0x7701,1,0x8449,1,0x8aaa,1,0x6bba,1,0x8fb0,1,0x6c88,1,0x62fe,1,0x82e5, -1,0x63a0,1,0x7565,1,0x4eae,1,0x5169,1,0x51c9,1,0x6881,1,0x7ce7,1,0x826f, -1,0x8ad2,1,0x91cf,1,0x52f5,1,0x5442,1,0x5973,1,0x5eec,1,0x65c5,1,0x6ffe, -1,0x792a,1,0x95ad,1,0x9a6a,1,0x9e97,1,0x9ece,1,0x529b,1,0x66c6,1,0x6b77, -1,0x8f62,1,0x5e74,1,0x6190,1,0x6200,1,0x649a,1,0x6f23,1,0x7149,1,0x7489, -1,0x79ca,1,0x7df4,1,0x806f,1,0x8f26,1,0x84ee,1,0x9023,1,0x934a,1,0x5217, -1,0x52a3,1,0x54bd,1,0x70c8,1,0x88c2,1,0x5ec9,1,0x5ff5,1,0x637b,1,0x6bae, -1,0x7c3e,1,0x7375,1,0x4ee4,1,0x56f9,1,0x5dba,1,0x601c,1,0x73b2,1,0x7469, -1,0x7f9a,1,0x8046,1,0x9234,1,0x96f6,1,0x9748,1,0x9818,1,0x4f8b,1,0x79ae, -1,0x91b4,1,0x96b8,1,0x60e1,1,0x4e86,1,0x50da,1,0x5bee,1,0x5c3f,1,0x6599, -1,0x71ce,1,0x7642,1,0x84fc,1,0x907c,1,0x9f8d,1,0x6688,1,0x962e,1,0x5289, -1,0x677b,1,0x67f3,1,0x6d41,1,0x6e9c,1,0x7409,1,0x7559,1,0x786b,1,0x7d10, -1,0x985e,1,0x516d,1,0x622e,1,0x9678,1,0x502b,1,0x5d19,1,0x6dea,1,0x8f2a, -1,0x5f8b,1,0x6144,1,0x6817,1,0x9686,1,0x5229,1,0x540f,1,0x5c65,1,0x6613, -1,0x674e,1,0x68a8,1,0x6ce5,1,0x7406,1,0x75e2,1,0x7f79,1,0x88cf,1,0x88e1, -1,0x91cc,1,0x96e2,1,0x533f,1,0x6eba,1,0x541d,1,0x71d0,1,0x7498,1,0x85fa, -1,0x96a3,1,0x9c57,1,0x9e9f,1,0x6797,1,0x6dcb,1,0x81e8,1,0x7acb,1,0x7b20, -1,0x7c92,1,0x72c0,1,0x7099,1,0x8b58,1,0x4ec0,1,0x8336,1,0x523a,1,0x5207, -1,0x5ea6,1,0x62d3,1,0x7cd6,1,0x5b85,1,0x6d1e,1,0x66b4,1,0x8f3b,1,0x884c, -1,0x964d,1,0x898b,1,0x5ed3,1,0x5140,1,0x55c0,1,0x585a,1,0x6674,1,0x51de, -1,0x732a,1,0x76ca,1,0x793c,1,0x795e,1,0x7965,1,0x798f,1,0x9756,1,0x7cbe, -1,0x7fbd,1,0x8612,1,0x8af8,1,0x9038,1,0x90fd,1,0x98ef,1,0x98fc,1,0x9928, -1,0x9db4,1,0x90de,1,0x96b7,1,0x4fae,1,0x50e7,1,0x514d,1,0x52c9,1,0x52e4, -1,0x5351,1,0x559d,1,0x5606,1,0x5668,1,0x5840,1,0x58a8,1,0x5c64,1,0x5c6e, -1,0x6094,1,0x6168,1,0x618e,1,0x61f2,1,0x654f,1,0x65e2,1,0x6691,1,0x6885, -1,0x6d77,1,0x6e1a,1,0x6f22,1,0x716e,1,0x722b,1,0x7422,1,0x7891,1,0x793e, -1,0x7949,1,0x7948,1,0x7950,1,0x7956,1,0x795d,1,0x798d,1,0x798e,1,0x7a40, -1,0x7a81,1,0x7bc0,1,0x7e09,1,0x7e41,1,0x7f72,1,0x8005,1,0x81ed,1,0x8279, -1,0x8457,1,0x8910,1,0x8996,1,0x8b01,1,0x8b39,1,0x8cd3,1,0x8d08,1,0x8fb6, -1,0x96e3,1,0x97ff,1,0x983b,1,0x6075,2,0xd850,0xdeee,1,0x8218,1,0x4e26,1, -0x51b5,1,0x5168,1,0x4f80,1,0x5145,1,0x5180,1,0x52c7,1,0x52fa,1,0x5555,1, -0x5599,1,0x55e2,1,0x58b3,1,0x5944,1,0x5954,1,0x5a62,1,0x5b28,1,0x5ed2,1, -0x5ed9,1,0x5f69,1,0x5fad,1,0x60d8,1,0x614e,1,0x6108,1,0x6160,1,0x6234,1, -0x63c4,1,0x641c,1,0x6452,1,0x6556,1,0x671b,1,0x6756,1,0x6b79,1,0x6edb,1, -0x6ecb,1,0x701e,1,0x77a7,1,0x7235,1,0x72af,1,0x7471,1,0x7506,1,0x753b,1, -0x761d,1,0x761f,1,0x76db,1,0x76f4,1,0x774a,1,0x7740,1,0x78cc,1,0x7ab1,1, -0x7c7b,1,0x7d5b,1,0x7f3e,1,0x8352,1,0x83ef,1,0x8779,1,0x8941,1,0x8986,1, -0x8abf,1,0x8acb,1,0x8aed,1,0x8b8a,1,0x8f38,1,0x9072,1,0x9199,1,0x9276,1, -0x967c,1,0x97db,1,0x980b,1,0x9b12,2,0xd84a,0xdc4a,2,0xd84a,0xdc44,2,0xd84c,0xdfd5, -1,0x3b9d,1,0x4018,1,0x4039,2,0xd854,0xde49,2,0xd857,0xdcd0,2,0xd85f,0xded3,1, -0x9f43,1,0x9f8e,0xe02,0x5d9,0x5b4,0x1102,0x5f2,0x5b7,0x1802,0x5e9,0x5c1,0x1902,0x5e9,0x5c2,0xfb49, -0x1843,0x5e9,0x5bc,0x5c1,0xfb49,0x1943,0x5e9,0x5bc,0x5c2,0x1102,0x5d0,0x5b7,0x1202,0x5d0,0x5b8,0x1502, -0x5d0,0x5bc,0x1502,0x5d1,0x5bc,0x1502,0x5d2,0x5bc,0x1502,0x5d3,0x5bc,0x1502,0x5d4,0x5bc,0x1502,0x5d5, -0x5bc,0x1502,0x5d6,0x5bc,0x1502,0x5d8,0x5bc,0x1502,0x5d9,0x5bc,0x1502,0x5da,0x5bc,0x1502,0x5db,0x5bc, -0x1502,0x5dc,0x5bc,0x1502,0x5de,0x5bc,0x1502,0x5e0,0x5bc,0x1502,0x5e1,0x5bc,0x1502,0x5e3,0x5bc,0x1502, -0x5e4,0x5bc,0x1502,0x5e6,0x5bc,0x1502,0x5e7,0x5bc,0x1502,0x5e8,0x5bc,0x1502,0x5e9,0x5bc,0x1502,0x5ea, -0x5bc,0x1302,0x5d5,0x5b9,0x1702,0x5d1,0x5bf,0x1702,0x5db,0x5bf,0x1702,0x5e4,0x5bf,0xd804,0xd834,0xdd57, -0xd834,0xdd65,0xd804,0xd834,0xdd58,0xd834,0xdd65,0xd834,0xdd5f,0xd834,0xdd6e,4,0xd846,0xd834,0xdd58,0xd834, -0xdd65,0xd834,0xdd6e,0xd834,0xdd5f,0xd834,0xdd6f,4,0xd846,0xd834,0xdd58,0xd834,0xdd65,0xd834,0xdd6f,0xd834, -0xdd5f,0xd834,0xdd70,4,0xd846,0xd834,0xdd58,0xd834,0xdd65,0xd834,0xdd70,0xd834,0xdd5f,0xd834,0xdd71,4, -0xd846,0xd834,0xdd58,0xd834,0xdd65,0xd834,0xdd71,0xd834,0xdd5f,0xd834,0xdd72,4,0xd846,0xd834,0xdd58,0xd834, -0xdd65,0xd834,0xdd72,0xd804,0xd834,0xddb9,0xd834,0xdd65,0xd804,0xd834,0xddba,0xd834,0xdd65,0xd834,0xddbb,0xd834, -0xdd6e,4,0xd846,0xd834,0xddb9,0xd834,0xdd65,0xd834,0xdd6e,0xd834,0xddbc,0xd834,0xdd6e,4,0xd846,0xd834, -0xddba,0xd834,0xdd65,0xd834,0xdd6e,0xd834,0xddbb,0xd834,0xdd6f,4,0xd846,0xd834,0xddb9,0xd834,0xdd65,0xd834, -0xdd6f,0xd834,0xddbc,0xd834,0xdd6f,4,0xd846,0xd834,0xddba,0xd834,0xdd65,0xd834,0xdd6f,1,0x4e3d,1, -0x4e38,1,0x4e41,2,0xd840,0xdd22,1,0x4f60,1,0x4fbb,1,0x5002,1,0x507a,1,0x5099, -1,0x50cf,1,0x349e,2,0xd841,0xde3a,1,0x5154,1,0x5164,1,0x5177,2,0xd841,0xdd1c, -1,0x34b9,1,0x5167,1,0x518d,2,0xd841,0xdd4b,1,0x5197,1,0x51a4,1,0x4ecc,1, -0x51ac,2,0xd864,0xdddf,1,0x51f5,1,0x5203,1,0x34df,1,0x523b,1,0x5246,1,0x5272, -1,0x5277,1,0x3515,1,0x5305,1,0x5306,1,0x5349,1,0x535a,1,0x5373,1,0x537d, -1,0x537f,2,0xd842,0xde2c,1,0x7070,1,0x53ca,1,0x53df,2,0xd842,0xdf63,1,0x53eb, -1,0x53f1,1,0x5406,1,0x549e,1,0x5438,1,0x5448,1,0x5468,1,0x54a2,1,0x54f6, -1,0x5510,1,0x5553,1,0x5563,1,0x5584,1,0x55ab,1,0x55b3,1,0x55c2,1,0x5716, -1,0x5717,1,0x5651,1,0x5674,1,0x58ee,1,0x57ce,1,0x57f4,1,0x580d,1,0x578b, -1,0x5832,1,0x5831,1,0x58ac,2,0xd845,0xdce4,1,0x58f2,1,0x58f7,1,0x5906,1, -0x591a,1,0x5922,1,0x5962,2,0xd845,0xdea8,2,0xd845,0xdeea,1,0x59ec,1,0x5a1b,1, -0x5a27,1,0x59d8,1,0x5a66,1,0x36ee,1,0x36fc,1,0x5b08,1,0x5b3e,2,0xd846,0xddc8, -1,0x5bc3,1,0x5bd8,1,0x5bf3,2,0xd846,0xdf18,1,0x5bff,1,0x5c06,1,0x5f53,1, -0x5c22,1,0x3781,1,0x5c60,1,0x5cc0,1,0x5c8d,2,0xd847,0xdde4,1,0x5d43,2,0xd847, -0xdde6,1,0x5d6e,1,0x5d6b,1,0x5d7c,1,0x5de1,1,0x5de2,1,0x382f,1,0x5dfd,1, -0x5e28,1,0x5e3d,1,0x5e69,1,0x3862,2,0xd848,0xdd83,1,0x387c,1,0x5eb0,1,0x5eb3, -1,0x5eb6,2,0xd868,0xdf92,1,0x5efe,2,0xd848,0xdf31,1,0x8201,1,0x5f22,1,0x38c7, -2,0xd84c,0xdeb8,2,0xd858,0xddda,1,0x5f62,1,0x5f6b,1,0x38e3,1,0x5f9a,1,0x5fcd, -1,0x5fd7,1,0x5ff9,1,0x6081,1,0x393a,1,0x391c,2,0xd849,0xded4,1,0x60c7,1, -0x6148,1,0x614c,1,0x617a,1,0x61b2,1,0x61a4,1,0x61af,1,0x61de,1,0x6210,1, -0x621b,1,0x625d,1,0x62b1,1,0x62d4,1,0x6350,2,0xd84a,0xdf0c,1,0x633d,1,0x62fc, -1,0x6368,1,0x6383,1,0x63e4,2,0xd84a,0xdff1,1,0x6422,1,0x63c5,1,0x63a9,1, -0x3a2e,1,0x6469,1,0x647e,1,0x649d,1,0x6477,1,0x3a6c,1,0x656c,2,0xd84c,0xdc0a, -1,0x65e3,1,0x66f8,1,0x6649,1,0x3b19,1,0x3b08,1,0x3ae4,1,0x5192,1,0x5195, -1,0x6700,1,0x669c,1,0x80ad,1,0x43d9,1,0x6721,1,0x675e,1,0x6753,2,0xd84c, -0xdfc3,1,0x3b49,1,0x67fa,1,0x6785,1,0x6852,2,0xd84d,0xdc6d,1,0x688e,1,0x681f, -1,0x6914,1,0x6942,1,0x69a3,1,0x69ea,1,0x6aa8,2,0xd84d,0xdea3,1,0x6adb,1, -0x3c18,1,0x6b21,2,0xd84e,0xdca7,1,0x6b54,1,0x3c4e,1,0x6b72,1,0x6b9f,1,0x6bbb, -2,0xd84e,0xde8d,2,0xd847,0xdd0b,2,0xd84e,0xdefa,1,0x6c4e,2,0xd84f,0xdcbc,1,0x6cbf, -1,0x6ccd,1,0x6c67,1,0x6d16,1,0x6d3e,1,0x6d69,1,0x6d78,1,0x6d85,2,0xd84f, -0xdd1e,1,0x6d34,1,0x6e2f,1,0x6e6e,1,0x3d33,1,0x6ec7,2,0xd84f,0xded1,1,0x6df9, -1,0x6f6e,2,0xd84f,0xdf5e,2,0xd84f,0xdf8e,1,0x6fc6,1,0x7039,1,0x701b,1,0x3d96, -1,0x704a,1,0x707d,1,0x7077,1,0x70ad,2,0xd841,0xdd25,1,0x7145,2,0xd850,0xde63, -1,0x719c,2,0xd850,0xdfab,1,0x7228,1,0x7250,2,0xd851,0xde08,1,0x7280,1,0x7295, -2,0xd851,0xdf35,2,0xd852,0xdc14,1,0x737a,1,0x738b,1,0x3eac,1,0x73a5,1,0x3eb8, -1,0x7447,1,0x745c,1,0x7485,1,0x74ca,1,0x3f1b,1,0x7524,2,0xd853,0xdc36,1, -0x753e,2,0xd853,0xdc92,2,0xd848,0xdd9f,1,0x7610,2,0xd853,0xdfa1,2,0xd853,0xdfb8,2, -0xd854,0xdc44,1,0x3ffc,1,0x4008,2,0xd854,0xdcf3,2,0xd854,0xdcf2,2,0xd854,0xdd19,2, -0xd854,0xdd33,1,0x771e,1,0x771f,1,0x778b,1,0x4046,1,0x4096,2,0xd855,0xdc1d,1, -0x784e,1,0x40e3,2,0xd855,0xde26,2,0xd855,0xde9a,2,0xd855,0xdec5,1,0x79eb,1,0x412f, -1,0x7a4a,1,0x7a4f,2,0xd856,0xdd7c,2,0xd856,0xdea7,1,0x7aee,1,0x4202,2,0xd856, -0xdfab,1,0x7bc6,1,0x7bc9,1,0x4227,2,0xd857,0xdc80,1,0x7cd2,1,0x42a0,1,0x7ce8, -1,0x7ce3,1,0x7d00,2,0xd857,0xdf86,1,0x7d63,1,0x4301,1,0x7dc7,1,0x7e02,1, -0x7e45,1,0x4334,2,0xd858,0xde28,2,0xd858,0xde47,1,0x4359,2,0xd858,0xded9,1,0x7f7a, -2,0xd858,0xdf3e,1,0x7f95,1,0x7ffa,2,0xd859,0xdcda,2,0xd859,0xdd23,1,0x8060,2, -0xd859,0xdda8,1,0x8070,2,0xd84c,0xdf5f,1,0x43d5,1,0x80b2,1,0x8103,1,0x440b,1, -0x813e,1,0x5ab5,2,0xd859,0xdfa7,2,0xd859,0xdfb5,2,0xd84c,0xdf93,2,0xd84c,0xdf9c,1, -0x8204,1,0x8f9e,1,0x446b,1,0x8291,1,0x828b,1,0x829d,1,0x52b3,1,0x82b1,1, -0x82b3,1,0x82bd,1,0x82e6,2,0xd85a,0xdf3c,1,0x831d,1,0x8363,1,0x83ad,1,0x8323, -1,0x83bd,1,0x83e7,1,0x8353,1,0x83ca,1,0x83cc,1,0x83dc,2,0xd85b,0xdc36,2, -0xd85b,0xdd6b,2,0xd85b,0xdcd5,1,0x452b,1,0x84f1,1,0x84f3,1,0x8516,2,0xd85c,0xdfca, -1,0x8564,2,0xd85b,0xdf2c,1,0x455d,1,0x4561,2,0xd85b,0xdfb1,2,0xd85c,0xdcd2,1, -0x456b,1,0x8650,1,0x8667,1,0x8669,1,0x86a9,1,0x8688,1,0x870e,1,0x86e2,1, -0x8728,1,0x876b,1,0x8786,1,0x45d7,1,0x87e1,1,0x8801,1,0x45f9,1,0x8860,1, -0x8863,2,0xd85d,0xde67,1,0x88d7,1,0x88de,1,0x4635,1,0x88fa,1,0x34bb,2,0xd85e, -0xdcae,2,0xd85e,0xdd66,1,0x46be,1,0x46c7,1,0x8aa0,1,0x8c55,2,0xd85f,0xdca8,1, -0x8cab,1,0x8cc1,1,0x8d1b,1,0x8d77,2,0xd85f,0xdf2f,2,0xd842,0xdc04,1,0x8dcb,1, -0x8dbc,1,0x8df0,2,0xd842,0xdcde,1,0x8ed4,2,0xd861,0xddd2,2,0xd861,0xdded,1,0x9094, -1,0x90f1,1,0x9111,2,0xd861,0xdf2e,1,0x911b,1,0x9238,1,0x92d7,1,0x92d8,1, -0x927c,1,0x93f9,1,0x9415,2,0xd862,0xdffa,1,0x958b,1,0x4995,1,0x95b7,2,0xd863, -0xdd77,1,0x49e6,1,0x96c3,1,0x5db2,1,0x9723,2,0xd864,0xdd45,2,0xd864,0xde1a,1, -0x4a6e,1,0x4a76,1,0x97e0,2,0xd865,0xdc0a,1,0x4ab2,2,0xd865,0xdc96,1,0x9829,2, -0xd865,0xddb6,1,0x98e2,1,0x4b33,1,0x9929,1,0x99a7,1,0x99c2,1,0x99fe,1,0x4bce, -2,0xd866,0xdf30,1,0x9c40,1,0x9cfd,1,0x4cce,1,0x4ced,1,0x9d67,2,0xd868,0xdcce, -1,0x4cf8,2,0xd868,0xdd05,2,0xd868,0xde0e,2,0xd868,0xde91,1,0x9ebb,1,0x4d56,1, -0x9ef9,1,0x9efe,1,0x9f05,1,0x9f0f,1,0x9f16,1,0x9f3b,2,0xd869,0xde00,0x3ac,0xe642, -0x3b1,0x301,0x3ad,0xe642,0x3b5,0x301,0x3ae,0xe642,0x3b7,0x301,0x3af,0xe642,0x3b9,0x301,0x3cc,0xe642, -0x3bf,0x301,0x3cd,0xe642,0x3c5,0x301,0x3ce,0xe642,0x3c9,0x301,0x386,0xe642,0x391,0x301,0x388,0xe642, -0x395,0x301,0x389,0xe642,0x397,0x301,0x390,1,0xe643,0x3b9,0x308,0x301,0x38a,0xe642,0x399,0x301, -0x3b0,1,0xe643,0x3c5,0x308,0x301,0x38e,0xe642,0x3a5,0x301,0x385,0xe642,0xa8,0x301,0x38c,0xe642, -0x39f,0x301,0x38f,0xe642,0x3a9,0x301,0xc5,0xe642,0x41,0x30a,0xe6e6,0xe681,0x300,0xe6e6,0xe681,0x301, -0xe6e6,0xe681,0x313,0xe6e6,0xe682,0x308,0x301,0x8100,0x8282,0xf71,0xf72,0x8100,0x8482,0xf71,0xf74,0x8100, -0x8282,0xf71,0xf80,0 -}; - -static const uint8_t norm2_nfc_data_smallFCD[256]={ -0xc0,0xef,3,0x7f,0xdf,0x70,0xcf,0x87,0xd7,0xe6,0x66,0x46,0x66,0x46,0x66,0x5b, -0x12,0,0,4,0,0,0,0x43,0x20,2,0x69,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, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0x98,0,0xc3,0x66,0xe0,0x80,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -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,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,7,0,0,2,0 -}; - -#endif // INCLUDED_FROM_NORMALIZER2_CPP +// Copyright (C) 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// Copyright (C) 1999-2016, International Business Machines +// Corporation and others. All Rights Reserved. +// +// file name: norm2_nfc_data.h +// +// machine-generated by: icu/source/tools/gennorm2/n2builder.cpp + +#ifdef INCLUDED_FROM_NORMALIZER2_CPP + +static const UVersionInfo norm2_nfc_data_formatVersion={4,0,0,0}; +static const UVersionInfo norm2_nfc_data_dataVersion={0xf,0,0,0}; + +static const int32_t norm2_nfc_data_indexes[Normalizer2Impl::IX_COUNT]={ +0x50,0x4cb8,0x8920,0x8a20,0x8a20,0x8a20,0x8a20,0x8a20,0xc0,0x300,0xae2,0x29e0,0x3c66,0xfc00,0x1288,0x3b9c, +0x3c34,0x3c66,0x300,0 +}; + +static const uint16_t norm2_nfc_data_trieIndex[1788]={ +0,0x40,0x7b,0xbb,0xfb,0x13a,0x17a,0x1b2,0x1f2,0x226,0x254,0x226,0x294,0x2d4,0x313,0x353, +0x393,0x3d2,0x40f,0x44e,0x226,0x226,0x488,0x4c8,0x4f8,0x530,0x226,0x570,0x59f,0x5de,0x226,0x5f3, +0x631,0x65f,0x687,0x6bd,0x6fd,0x73a,0x75a,0x799,0x7d8,0x815,0x834,0x871,0x75a,0x8aa,0x8d8,0x917, +0x834,0x951,0x968,0x9a8,0x9bf,0x9fe,0x226,0xa34,0xa54,0xa8f,0xa9b,0xad6,0xafe,0xb3b,0xb7b,0xbb5, +0xbd0,0x226,0xc0b,0x226,0xc4b,0xc6a,0xca0,0xcdd,0x226,0x226,0x226,0x226,0x226,0xd00,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0xd2c,0x226,0x226,0xd61, +0x226,0x226,0xd7f,0x226,0xda9,0x226,0x226,0x226,0xde5,0xe05,0xe45,0xe84,0xebf,0xeff,0xf33,0xf5f, +0x839,0x226,0x226,0xf93,0x226,0x226,0x226,0xfd3,0x1013,0x1053,0x1093,0x10d3,0x1113,0x1153,0x1193,0x11d3, +0x1213,0x226,0x226,0x1243,0x1274,0x226,0x12a4,0x12d7,0x1314,0x1353,0x1393,0x13c9,0x13f7,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1422,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0xcee,0x226,0x143f,0x226,0x147f,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x14bf,0x14f9,0x1537,0x1577,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x15b6,0x15f4,0x1614,0x226,0x226,0x226,0x226, +0x164e,0x226,0x226,0x1676,0x16a8,0x16d6,0x83d,0x16e9,0x226,0x226,0x16f9,0x1739,0x226,0x226,0x226,0x1451, +0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781, +0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791, +0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785, +0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779, +0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789, +0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d, +0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d, +0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781, +0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791, +0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785, +0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x178d,0x1779,0x1781,0x1789,0x1791,0x177d,0x1785,0x17c5,0x226, +0x1805,0x1840,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x1880,0x18c0,0x1900,0x1940,0x1980,0x19c0,0x1a00,0x1a40,0x1a63,0x1aa3,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1ac3,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x67e,0x68e,0x6a6,0x6c5,0x6da,0x6da,0x6da,0x6de,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0xc0b,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x54f,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x40c, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1af6,0x226,0x226,0x1b06,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0xdf7,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1b16,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1607,0x226,0x226,0x226,0x226,0x66b,0x226,0x226,0x226, +0x226,0x1b20,0x54f,0x226,0x226,0x1b30,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x81c,0x226,0x226, +0x1b40,0x226,0x1b50,0x1b5d,0x1b69,0x226,0x226,0x226,0x226,0x414,0x226,0x1b74,0x1b84,0x226,0x226,0x226, +0x811,0x226,0x226,0x226,0x226,0x1b94,0x226,0x226,0x226,0x1b9f,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x1ba6,0x226,0x226,0x226,0x226,0x1bb1,0x1bc0,0x927,0x1bce,0x412,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x1bdc,0x7c9,0x226,0x226,0x226,0x226,0x226,0x1bec,0x1bfb,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x907,0x1c03,0x1c13,0x226, +0x226,0x226,0x9eb,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1c1d,0x226,0x226,0x226,0x226,0x226, +0x226,0x817,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1c1a, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1c2d, +0x811,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x87e,0x226,0x226,0x226,0x81e,0x81b, +0x226,0x226,0x226,0x226,0x819,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x9eb,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0xc05,0x226,0x226,0x226, +0x226,0x81b,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0xc08,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x1c3d,0x226,0x226,0x226,0xf2c,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1c4d,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x1c4f,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1c5e,0x1c6e,0x1c7c,0x1c89,0x226, +0x1c95,0x1ca3,0x1cb3,0x226,0x226,0x226,0x226,0xd1b,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x1cc3,0x1ccb,0x1cd9,0x226,0x226,0x226,0x226,0x226,0x4f9,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0xf2c,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x7c9,0x226,0x226,0x226,0x4fc,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1ce4,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1cf4,0x226,0x226,0x226,0x226, +0x226,0x226,0x1d00,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x1d10,0x1d20, +0x1d30,0x1d40,0x1d50,0x1d60,0x1d70,0x1d80,0x1d90,0x1da0,0x1db0,0x1dc0,0x1dd0,0x1de0,0x1df0,0x1e00,0x1e10,0x1e20, +0x1e30,0x1e40,0x1e50,0x1e60,0x1e70,0x1e80,0x1e90,0x1ea0,0x1eb0,0x1ec0,0x1ed0,0x1ee0,0x1ef0,0x1f00,0x1f10,0x1f20, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226, +0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x226,0x408,0x428, +0xc4,0xc4,0xc4,0x448,0x457,0x46d,0x489,0x4a6,0x4c2,0x4df,0x4fc,0x51b,0x538,0x552,0xc4,0xc4, +0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4, +0xc4,0xc4,0xc4,0x567,0xc4,0x57b,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4, +0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4, +0xc4,0xc4,0xc4,0xc4,0x59b,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0x5a6,0x5c3,0xc4, +0xc4,0xc4,0xc4,0xc4,0xc4,0x5e3,0x5f9,0x60b,0xc4,0x61e,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4, +0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4, +0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0x63e,0x65e +}; + +static const uint16_t norm2_nfc_data_trieData[7984]={ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,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,0x5e8,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0x128a,0x1290,0xae4,0x1296,0xafa, +0xb04,0x5f4,0xb0e,0x129c,0x12a2,0xb18,0x12a8,0x12ae,0x12b4,0x12ba,0xb2e,1,0x12c0,0x12c6,0x12cc,0xb38, +0xb4e,0xb60,1,0x5fc,0x12d2,0x12d8,0x12de,0xb6a,0x12e4,1,1,0x12ea,0x12f0,0xb80,0x12f6,0xb96, +0xba0,0x600,0xbaa,0x12fc,0x1302,0xbb4,0x1308,0x130e,0x1314,0x131a,0xbca,1,0x1320,0x1326,0x132c,0xbd4, +0xbea,0xbfc,1,0x608,0x1332,0x1338,0x133e,0xc06,0x1344,1,0x134a,0x1350,0x1356,0xc1c,0xc32,0x135d, +0x1363,0x1368,0x136e,0x1374,0x137a,0x1380,0x1386,0x138c,0x1392,0x1398,0x139e,1,1,0xc48,0xc56,0x13a4, +0x13aa,0x13b0,0x13b6,0x13bd,0x13c3,0x13c8,0x13ce,0x13d4,0x13da,0x13e0,0x13e6,0x13ec,0x13f2,0x13f9,0x13ff,0x1404, +0x140a,1,1,0x1410,0x1416,0x141c,0x1422,0x1428,0x142e,0x1435,0x143b,0x1440,1,1,1,0x1447, +0x144d,0x1453,0x1459,1,0x145e,0x1464,0x146b,0x1471,0x1476,0x147c,1,1,1,0x1482,0x1488,0x148f, +0x1495,0x149a,0x14a0,1,1,1,0xc64,0xc72,0x14a6,0x14ac,0x14b2,0x14b8,1,1,0x14be,0x14c4, +0x14cb,0x14d1,0x14d6,0x14dc,0xc80,0xc8a,0x14e2,0x14e8,0x14ef,0x14f5,0xc94,0xc9e,0x14fb,0x1501,0x1506,0x150c, +1,1,0xca8,0xcb2,0xcbc,0xcc6,0x1512,0x1518,0x151e,0x1524,0x152a,0x1530,0x1537,0x153d,0x1542,0x1548, +0x154e,0x1554,0x155a,0x1560,0x1566,0x156c,0x1572,0x1578,0x157e,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,0xcd0,0xcea,1,1,1,1, +1,1,1,1,1,1,1,1,1,0xd04,0xd1e,1,1,1,1,1, +1,0x610,1,1,1,1,1,1,1,1,1,1,1,1,1,0x1584, +0x158a,0x1590,0x1596,0x159c,0x15a2,0x15a8,0x15ae,0x15b6,0x15c0,0x15ca,0x15d4,0x15de,0x15e8,0x15f2,0x15fc,1, +0x1606,0x1610,0x161a,0x1624,0x162d,0x1633,1,1,0x1638,0x163e,0x1644,0x164a,0xd38,0xd42,0x1653,0x165d, +0x1665,0x166b,0x1671,1,1,1,0x1676,0x167c,1,1,0x1682,0x1688,0x1690,0x169a,0x16a3,0x16a9, +0x16af,0x16b5,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,0x175c,1,1, +0x1762,0x1768,1,1,1,1,1,1,0xd4c,0xd56,0xd60,0xd6a,0x1770,0x177a,0x1784,0x178e, +0xd74,0xd7e,0x1798,0x17a2,0x17aa,0x17b0,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,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,0x3c36,0x3c3c,0xfdcc,0x3c42,0x3c48,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,0x29e1,1,1,1,1,1,1,1, +1,1,0x29e5,1,1,1,1,1,0x17b7,0x17bd,0x29e9,0x17c3,0x17c9,0x17cf,1,0x17d5, +1,0x17db,0x17e1,0x17e9,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,0x17f1,0x17f7,0xd88, +0x17fd,0xd92,0x1803,0x180b,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,0xd9c,0xdae,0x1813, +0x1819,0xdc0,1,1,1,0x76c,0x181f,0x1825,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,0x182b,0x1831,1,0x1837,1,1,0x774,0x183d,1,1,1,1,0x1843, +0x1849,0x184f,1,0x778,1,1,0x780,1,0x784,0x790,0x798,0x79c,0x1855,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,0x185b,0x804,1,1, +1,0x808,1,1,1,0x80c,1,1,1,0x81c,1,1,1,0x820,1,0x824, +1,1,0x1861,0x1867,1,0x186d,1,1,0x828,0x1873,1,1,1,1,0x1879,0x187f, +0x1885,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,0x82c,0x830,0x188b,0x1891,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,0x1897, +0x189d,1,1,1,1,1,1,1,1,1,1,1,1,1,0x18a3,0x18a9, +0x18af,0x18b5,1,1,0x18bb,0x18c1,0x834,0x838,0x18c7,0x18cd,0x18d3,0x18d9,0x18df,0x18e5,1,1, +0x18eb,0x18f1,0x18f7,0x18fd,0x1903,0x1909,0x83c,0x840,0x190f,0x1915,0x191b,0x1921,0x1927,0x192d,0x1933,0x1939, +0x193f,0x1945,0x194b,0x1951,1,1,0x1957,0x195d,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,0x1962,0x1968,0x196f,0x1975,0x197b,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, +0x1981,0x858,0x1987,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,0x85c,0x198d,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, +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, +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,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,0xffcc,0xffcc,0xffcc,0xffcc,1,0xffcc,0xffcc,0xffcc,0xffcc, +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,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,1,1,1,1,1,1,1,1,0xffcc, +0xffb8,0xffb8,0xffb8,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,0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,0xffb8,0xffb8, +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,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,0x864,0x1993,1,1,1,1,1,1,0x868,0x1999,1, +0x86c,0x199f,1,1,1,1,1,1,1,0xfc0e,1,1,1,1,1,1, +1,1,1,1,1,1,1,0xfe12,1,1,1,0xffcc,0xffb8,0xffcc,0xffcc,1, +1,1,0x29ec,0x29f2,0x29f8,0x29fe,0x2a04,0x2a0a,0x2a10,0x2a16,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,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, +0x870,1,1,1,0x19a5,0x19ab,0xfe12,1,1,1,1,1,1,1,1,1, +0xfc00,1,1,1,1,0x2a1c,0x2a22,1,0x2a28,1,1,1,1,1,1,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,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0x2a2e,1,1,0x2a34,1, +1,1,1,1,0xfe0e,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,0x2a3a,0x2a40, +0x2a46,1,1,0x2a4c,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,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,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,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,0x878,0x19b1,1,1,0x19b7,0x19bd,0xfe12,1,1,1,1,1,1,1,1, +0xfc00,0xfc00,1,1,1,1,0x2a52,0x2a58,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,0x884,1,0x19c3,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,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,0x888,0x890,1, +1,0x19c9,0x19cf,0x19d5,0xfe12,1,1,1,1,1,1,1,1,1,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,1,1,1,1,1, +1,1,1,1,1,1,1,0x894,1,0x19db,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,1,1,1,1,1,1,1,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,0x19e1,1,0xfc00,1,1,1,0x89c,0x19e7, +0x19ed,1,0xdca,0x19f5,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,1,1,1,1,1, +1,1,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,0x8a8,0x8b0,1,1,0x19fd,0x1a03,0x1a09,0xfe12,1,1,1,1, +1,1,1,1,1,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,1,1,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,0x1a0f,1, +0xdd4,0x1a17,0x1a1f,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, +1,1,1,1,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,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,1,1,1,1,1,1,1,1,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,0xfe12,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,1,1,1,1,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,1, +1,1,1,0xffb8,1,0xffb8,1,0xffb0,1,1,1,1,1,1,0x2a5f,1, +1,1,1,1,1,1,1,1,0x2a65,1,1,1,1,0x2a6b,1,1, +1,1,0x2a71,1,1,1,1,0x2a77,1,1,1,1,1,1,1,1, +1,1,1,1,0x2a7d,1,1,1,1,1,1,1,0xff02,0xff04,0x3c50,0xff08, +0x3c58,0x2a82,1,0x2a88,1,0xff04,0xff04,0xff04,0xff04,1,1,0xff04,0x3c60,0xffcc,0xffcc,0xfe12, +1,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1,1,0x2a8f,1, +1,1,1,1,1,1,1,1,0x2a95,1,1,1,1,0x2a9b,1,1, +1,1,0x2aa1,1,1,1,1,0x2aa7,1,1,1,1,1,1,1,1, +1,1,1,1,0x2aad,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,1,1,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,0x1a25,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,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,1,1,1,1,1,1, +1,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,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,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,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,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,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,1,1,1, +1,1,1,1,1,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,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,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,0xfe12,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,1,1,1,1,1,1,1,1,1,1,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,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,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,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,1,1,1,1,1,1,1,1,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,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8, +0xffcc,0xffcc,0xffb8,1,0xffb8,0xffcc,0xffcc,0xffb8,0xffb8,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,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,0x8c4,0x1a2b,0x8c8,0x1a31,0x8cc,0x1a37,0x8d0,0x1a3d,0x8d4,0x1a43,1,1, +0x8d8,0x1a49,1,1,1,1,1,1,1,1,1,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,0xfc00,1,1,1,1,0x8dc,0x1a4f,0x8e0,0x1a55,0x8e4,0x8e8,0x1a5b, +0x1a61,0x8ec,0x1a67,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,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,1,1,1,1,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, +1,1,1,1,1,1,1,1,1,1,1,1,1,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,0xfe12,0xfe12,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,0xffcc,0xffcc,0xffcc, +0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffd0,0xffc8,0xffc8,0xffb8,0xffb4,0xffcc,0xffd2, +0xffb8,0xffcc,0xffb8,0x1a6c,0x1a72,0x1a78,0x1a7e,0x1a85,0x1a8b,0x1a91,0x1a97,0x1a9f,0x1aa9,0x1ab0,0x1ab6,0x1abc, +0x1ac2,0x1ac8,0x1ace,0x1ad5,0x1adb,0x1ae0,0x1ae6,0x1aee,0x1af8,0x1b02,0x1b0c,0x1b14,0x1b1a,0x1b20,0x1b26,0x1b2f, +0x1b39,0x1b41,0x1b47,0x1b4c,0x1b52,0x1b58,0x1b5e,0x1b64,0x1b6a,0x1b70,0x1b76,0x1b7d,0x1b83,0x1b88,0x1b8e,0x1b94, +0x1b9a,0x1ba2,0x1bac,0x1bb4,0x1bba,0x1bc0,0x1bc6,0x1bcc,0x1bd2,0xdde,0xde8,0x1bda,0x1be4,0x1bec,0x1bf2,0x1bf8, +0x1bfe,0x1c04,0x1c0a,0x1c10,0x1c16,0x1c1d,0x1c23,0x1c28,0x1c2e,0x1c34,0x1c3a,0x1c40,0x1c46,0x1c4c,0x1c52,0x1c5a, +0x1c64,0x1c6e,0x1c78,0x1c82,0x1c8c,0x1c96,0x1ca0,0x1ca9,0x1caf,0x1cb5,0x1cbb,0x1cc0,0x1cc6,0xdf2,0xdfc,0x1cce, +0x1cd8,0x1ce0,0x1ce6,0x1cec,0x1cf2,0xe06,0xe10,0x1cfa,0x1d04,0x1d0e,0x1d18,0x1d22,0x1d2c,0x1d34,0x1d3a,0x1d40, +0x1d46,0x1d4c,0x1d52,0x1d58,0x1d5e,0x1d64,0x1d6a,0x1d70,0x1d76,0x1d7c,0x1d82,0x1d8a,0x1d94,0x1d9e,0x1da8,0x1db0, +0x1db6,0x1dbd,0x1dc3,0x1dc8,0x1dce,0x1dd4,0x1dda,0x1de0,0x1de6,0x1dec,0x1df2,0x1df9,0x1dff,0x1e05,0x1e0b,0x1e11, +0x1e17,0x1e1c,0x1e22,0x1e28,0x1e2e,0x1e35,0x1e3b,0x1e41,0x1e47,0x1e4c,0x1e52,0x1e58,0x1e5e,1,0x1e65,1, +1,1,1,0xe1a,0xe28,0x1e6a,0x1e70,0x1e78,0x1e82,0x1e8c,0x1e96,0x1ea0,0x1eaa,0x1eb4,0x1ebe,0x1ec8, +0x1ed2,0x1edc,0x1ee6,0x1ef0,0x1efa,0x1f04,0x1f0e,0x1f18,0x1f22,0x1f2c,0x1f36,0xe36,0xe40,0x1f3e,0x1f44,0x1f4a, +0x1f50,0x1f58,0x1f62,0x1f6c,0x1f76,0x1f80,0x1f8a,0x1f94,0x1f9e,0x1fa8,0x1fb2,0x1fba,0x1fc0,0x1fc6,0x1fcc,0xe4a, +0xe54,0x1fd2,0x1fd8,0x1fe0,0x1fea,0x1ff4,0x1ffe,0x2008,0x2012,0x201c,0x2026,0x2030,0x203a,0x2044,0x204e,0x2058, +0x2062,0x206c,0x2076,0x2080,0x208a,0x2094,0x209e,0x20a6,0x20ac,0x20b2,0x20b8,0x20c0,0x20ca,0x20d4,0x20de,0x20e8, +0x20f2,0x20fc,0x2106,0x2110,0x211a,0x2122,0x2128,0x212f,0x2135,0x213a,0x2140,0x2146,0x214c,1,1,1, +1,1,1,0xe5e,0xe74,0xe8c,0xe9a,0xea8,0xeb6,0xec4,0xed2,0xede,0xef4,0xf0c,0xf1a,0xf28, +0xf36,0xf44,0xf52,0xf5e,0xf6c,0x2155,0x215f,0x2169,0x2173,1,1,0xf7a,0xf88,0x217d,0x2187,0x2191, +0x219b,1,1,0xf96,0xfac,0xfc4,0xfd2,0xfe0,0xfee,0xffc,0x100a,0x1016,0x102c,0x1044,0x1052,0x1060, +0x106e,0x107c,0x108a,0x1096,0x10a8,0x21a5,0x21af,0x21b9,0x21c3,0x21cd,0x21d7,0x10ba,0x10cc,0x21e1,0x21eb,0x21f5, +0x21ff,0x2209,0x2213,0x10de,0x10ec,0x221d,0x2227,0x2231,0x223b,1,1,0x10fa,0x1108,0x2245,0x224f,0x2259, +0x2263,1,1,0x1116,0x1128,0x226d,0x2277,0x2281,0x228b,0x2295,0x229f,1,0x113a,1,0x22a9,1, +0x22b3,1,0x22bd,0x114c,0x1162,0x117a,0x1188,0x1196,0x11a4,0x11b2,0x11c0,0x11cc,0x11e2,0x11fa,0x1208,0x1216, +0x1224,0x1232,0x1240,0x124c,0x3b9e,0x22c5,0x3ba6,0x1256,0x3bae,0x22cb,0x3bb6,0x22d1,0x3bbe,0x22d7,0x3bc6,0x1260, +0x3bce,1,1,0x22de,0x22e8,0x22f7,0x2307,0x2317,0x2327,0x2337,0x2347,0x2352,0x235c,0x236b,0x237b,0x238b, +0x239b,0x23ab,0x23bb,0x23c6,0x23d0,0x23df,0x23ef,0x23ff,0x240f,0x241f,0x242f,0x243a,0x2444,0x2453,0x2463,0x2473, +0x2483,0x2493,0x24a3,0x24ae,0x24b8,0x24c7,0x24d7,0x24e7,0x24f7,0x2507,0x2517,0x2522,0x252c,0x253b,0x254b,0x255b, +0x256b,0x257b,0x258b,0x2595,0x259b,0x25a3,0x25aa,0x25b3,1,0x126a,0x25bd,0x25c5,0x25cb,0x25d1,0x3bd6,0x25d6, +1,0x2ab2,0x8f0,1,0x25dd,0x25e5,0x25ec,0x25f5,1,0x1274,0x25ff,0x2607,0x3bde,0x260d,0x3be6,0x2612, +0x2619,0x261f,0x2625,0x262b,0x2631,0x2639,0x3bf0,1,1,0x2641,0x2649,0x2651,0x2657,0x265d,0x3bfa,1, +0x2663,0x2669,0x266f,0x2675,0x267b,0x2683,0x3c04,0x268b,0x2691,0x2697,0x269f,0x26a7,0x26ad,0x26b3,0x3c0e,0x26b9, +0x26bf,0x3c16,0x2ab7,1,1,0x26c7,0x26ce,0x26d7,1,0x127e,0x26e1,0x26e9,0x3c1e,0x26ef,0x3c26,0x26f4, +0x2abb,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,1,1,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,0x2abe,1,1,1,0x2ac2,0x3c2e, +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,0x26fb,0x2701, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,0x2707,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,0x270d,0x2713,0x2719,0x914,1,0x918,1,0x91c,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0x920,0x271f,1,1,1,0x924,0x2725,1,0x928, +0x272b,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0x92c,0x2731,0x930,0x2737,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,0x273d,1,0x938,0x2743,0x93c,1,0x2749,0x940,0x274f,1,1,1, +0x944,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,0x2755,0x948,0x275b,1,0x94c,0x950,1,1,1,1,1,1,1, +0x2761,0x2767,0x276d,0x2773,0x2779,0x954,0x958,0x277f,0x2785,0x95c,0x960,0x278b,0x2791,0x964,0x968,0x96c, +0x970,1,1,0x2797,0x279d,0x974,0x978,0x27a3,0x27a9,0x97c,0x980,0x27af,0x27b5,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,0x27bb, +0x27c1,0x27c7,0x27cd,1,1,0x99c,0x9a0,0x9a4,0x9a8,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,0x27d3,0x27d9,0x27df,0x27e5,1,1,1, +1,1,1,0x27eb,0x27f1,0x27f7,0x27fd,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0x2ac7,0x2acb,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x2acf,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,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,1,1,1,1,1,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,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,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,0x9ac, +1,1,1,1,0x9b0,0x2803,0x9b4,0x2809,0x9b8,0x280f,0x9bc,0x2815,0x9c0,0x281b,0x9c4,0x2821, +0x9c8,0x2827,0x9cc,0x282d,0x9d0,0x2833,0x9d4,0x2839,0x9d8,0x283f,0x9dc,0x2845,1,0x9e0,0x284b,0x9e4, +0x2851,0x9e8,0x2857,1,1,1,1,1,0x9ec,0x285d,0x2863,0x9f4,0x2869,0x286f,0x9fc,0x2875, +0x287b,0xa04,0x2881,0x2887,0xa0c,0x288d,0x2893,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0x2899,1,1,1,1, +0xfc10,0xfc10,1,1,0xa14,0x289f,1,1,1,1,1,1,1,0xa18,1,1, +1,1,0xa1c,0x28a5,0xa20,0x28ab,0xa24,0x28b1,0xa28,0x28b7,0xa2c,0x28bd,0xa30,0x28c3,0xa34,0x28c9, +0xa38,0x28cf,0xa3c,0x28d5,0xa40,0x28db,0xa44,0x28e1,0xa48,0x28e7,1,0xa4c,0x28ed,0xa50,0x28f3,0xa54, +0x28f9,1,1,1,1,1,0xa58,0x28ff,0x2905,0xa60,0x290b,0x2911,0xa68,0x2917,0x291d,0xa70, +0x2923,0x2929,0xa78,0x292f,0x2935,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,0xa80,0xa84,0xa88,0xa8c,1,0x293b,1,1,0x2941,0x2947, +0x294d,0x2953,1,1,0xa90,0x2959,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,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,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,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,0xffcc,0xffcc,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, +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,1,1,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,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,1,1,1,1,1,1,1, +1,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,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,0xae2,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289, +0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289, +0x1289,0x1289,0x1289,0x1289,0x1289,0xae2,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289, +0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289, +0x1289,0xae2,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289, +0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0xae2,0x1289,0x1289, +0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289, +0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,0x1289,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,0x3c66,1,0x3c66,0x3c66,0x3c66,0x3c66,0x3c66,0x3c66,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x3c66, +0x3c66,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,0x3c66,1,1,1,1,0x3c66,1,1,1,0x3c66,0x3c66,0x3c66, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x3b97,1, +0x2ad5,0x2ad9,0x2add,0x2ae1,0x2ae5,0x2ae9,0x2aed,0x2af1,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,0x2c31,0x2c35,0x2c39,0x2c3d,0x2b21,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,0x2d41,0x2d45,0x2d49, +0x2d4d,0x2c89,0x2d51,0x2d55,0x2d59,0x2d5d,0x2d61,0x2d65,0x2d69,0x2d6d,0x2c49,0x2d71,0x2d75,0x2d79,0x2d7d,0x2d81, +0x2d85,0x2d89,0x2d8d,0x2d91,0x2d95,0x2d99,0x2d9d,0x2da1,0x2da5,0x2da9,0x2dad,0x2db1,0x2db5,0x2db9,0x2dbd,0x2b21, +0x2dc1,0x2dc5,0x2dc9,0x2dcd,0x2dd1,0x2dd5,0x2dd9,0x2ddd,0x2de1,0x2de5,0x2de9,0x2ded,0x2df1,0x2df5,0x2df9,0x2dfd, +0x2e01,0x2e05,0x2e09,0x2e0d,0x2e11,0x2e15,0x2e19,0x2e1d,0x2e21,0x2e25,0x2e29,0x2c51,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,0x2ee5,0x2ee9,0x2eed,0x2ef1,1,1, +0x2ef5,1,0x2ef9,1,1,0x2efd,0x2f01,0x2f05,0x2f09,0x2f0d,0x2f11,0x2f15,0x2f19,0x2f1d,0x2f21,1, +0x2f25,1,0x2f29,1,1,0x2f2d,0x2f31,1,1,1,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,0x2fd9,0x2fdd,0x2fe1,0x2fe5,0x2d25,0x2fe9,0x2fed,0x2ff1,0x2ff5,0x2ff9,0x2ffd,0x2ffd,0x3001, +0x3005,0x3009,0x300d,0x3011,0x3015,0x3019,0x301d,0x2f2d,0x3021,0x3025,0x3029,0x302d,0x3031,0x3037,1,1, +0x303b,0x303f,0x3043,0x3047,0x304b,0x304f,0x3053,0x3057,0x2f65,0x305b,0x305f,0x3063,0x2ef5,0x3067,0x306b,0x306f, +0x3073,0x3077,0x307b,0x307f,0x3083,0x3087,0x308b,0x308f,0x3093,0x2f89,0x3097,0x2f8d,0x309b,0x309f,0x30a3,0x30a7, +0x30ab,0x2ef9,0x2b75,0x30af,0x30b3,0x30b7,0x2c8d,0x2de9,0x30bb,0x30bf,0x2fa9,0x30c3,0x2fad,0x30c7,0x30cb,0x30cf, +0x2f01,0x30d3,0x30d7,0x30db,0x30df,0x30e3,0x2f05,0x30e7,0x30eb,0x30ef,0x30f3,0x30f7,0x30fb,0x2fe5,0x30ff,0x3103, +0x2d25,0x3107,0x2ff5,0x310b,0x310f,0x3113,0x3117,0x311b,0x3009,0x311f,0x2f29,0x3123,0x300d,0x2c41,0x3127,0x3011, +0x312b,0x3019,0x312f,0x3133,0x3137,0x313b,0x313f,0x3021,0x2f19,0x3143,0x3025,0x3147,0x3029,0x314b,0x2af1,0x314f, +0x3155,0x315b,0x3161,0x3165,0x3169,0x316d,0x3173,0x3179,0x317f,0x3183,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0x3186,0xfe34,0x318c,1,1,1,1,1,1,1,1,1,1,0x3192,0x3198,0x31a0, +0x31aa,0x31b2,0x31b8,0x31be,0x31c4,0x31ca,0x31d0,0x31d6,0x31dc,0x31e2,1,0x31e8,0x31ee,0x31f4,0x31fa,0x3200, +1,0x3206,1,0x320c,0x3212,1,0x3218,0x321e,1,0x3224,0x322a,0x3230,0x3236,0x323c,0x3242,0x3248, +0x324e,0x3254,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,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,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8, +0xffb8,0xffcc,0xffcc,1,1,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,0xffcc,0xfe02, +0xffb8,1,1,1,1,0xfe12,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,1,1, +1,1,1,1,1,1,0xffb8,0xffb8,0xffcc,0xffcc,0xffcc,0xffb8,0xffcc,0xffb8,0xffb8,0xffb8, +1,1,0xffcc,0xffb8,0xffcc,0xffb8,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,0xfe12, +1,1,1,1,1,1,1,1,1,0xa94,0x295f,0xa9a,0x2969,1,1,1, +1,1,0xaa0,1,1,1,1,1,0x2973,1,1,1,1,1,1,1, +1,1,0xfe12,0xfc0e,1,1,1,1,1,1,1,0xfc00,1,1,1,1, +1,1,0x297d,0x2987,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,0xfe12,0xfe0e,1,1,1,1,1,1,1,1,1,0xfe0e, +0xfe12,1,1,1,1,1,1,1,1,1,1,1,0xfe0e,0xfe0e,1,0xfc00, +1,1,1,1,1,1,1,0xab2,1,1,1,0x2991,0x299b,0xfe12,1,1, +1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,0xfe12,1, +1,1,0xfe0e,1,1,1,1,1,1,1,1,1,0xfc00,1,1,1, +1,1,1,1,1,0xabe,0xfc00,0x29a5,0x29af,0xfc00,0x29b9,1,1,0xfe12,0xfe0e,1, +1,1,1,1,1,1,1,1,1,1,1,0xad0,0xad6,0x29c3,0x29cd,1, +1,1,0xfe12,0xfe0e,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,0xfc00,1,1, +1,1,0xadc,1,1,0x29d7,1,1,1,1,0xfe12,0xfe12,1,0xfe02,0xfe02,0xfe02, +0xfe02,0xfe02,1,1,1,1,1,1,1,1,1,1,1,0xfe0c,0xfe0c,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,0x325a,0x3264,0x3278,0x3290, +0x32a8,0x32c0,0x32d8,0xffb0,0xffb0,0xfe02,0xfe02,0xfe02,1,1,1,0xffc4,0xffb0,0xffb0,0xffb0,1, +1,1,1,1,1,1,1,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,1,1,0xffcc,0xffcc, +0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,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,0x32e6,0x32f0, +0x3304,0x331c,0x3334,0x334c,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,1,1,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, +0xffd0,0xffd0,0xffb8,0xffcc,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, +0x335b,0x335f,0x3363,0x3367,0x336d,0x2f4d,0x3371,0x3375,0x3379,0x337d,0x2f51,0x3381,0x3385,0x3389,0x2f55,0x338f, +0x3393,0x3397,0x339b,0x33a1,0x33a5,0x33a9,0x33ad,0x33b3,0x33b7,0x33bb,0x33bf,0x303f,0x33c3,0x33c9,0x33cd,0x33d1, +0x33d5,0x33d9,0x33dd,0x33e1,0x33e5,0x3053,0x2f59,0x2f5d,0x3057,0x33e9,0x33ed,0x2c59,0x33f1,0x2f61,0x33f5,0x33f9, +0x33fd,0x3401,0x3401,0x3401,0x3405,0x340b,0x340f,0x3413,0x3417,0x341d,0x3421,0x3425,0x3429,0x342d,0x3431,0x3435, +0x3439,0x343d,0x3441,0x3445,0x3449,0x344d,0x344d,0x305f,0x3451,0x3455,0x3459,0x345d,0x2f69,0x3461,0x3465,0x3469, +0x2ebd,0x346d,0x3471,0x3475,0x3479,0x347d,0x3481,0x3485,0x3489,0x348d,0x3493,0x3497,0x349b,0x349f,0x34a3,0x34a7, +0x34ab,0x34b1,0x34b7,0x34bb,0x34bf,0x34c3,0x34c7,0x34cb,0x34cf,0x34d3,0x34d7,0x34d7,0x34db,0x34e1,0x34e5,0x2c49, +0x34e9,0x34ed,0x34f3,0x34f7,0x34fb,0x34ff,0x3503,0x3507,0x2f7d,0x350b,0x350f,0x3513,0x3519,0x351d,0x3523,0x3527, +0x352b,0x352f,0x3533,0x3537,0x353b,0x353f,0x3543,0x3547,0x354b,0x354f,0x3555,0x3559,0x355d,0x3561,0x2b71,0x3565, +0x356b,0x356f,0x356f,0x3575,0x3579,0x3579,0x357d,0x3581,0x3587,0x358d,0x3591,0x3595,0x3599,0x359d,0x35a1,0x35a5, +0x35a9,0x35ad,0x35b1,0x2f81,0x35b5,0x35bb,0x35bf,0x35c3,0x308f,0x35c3,0x35c7,0x2f89,0x35cb,0x35cf,0x35d3,0x35d7, +0x2f8d,0x2b05,0x35db,0x35df,0x35e3,0x35e7,0x35eb,0x35ef,0x35f3,0x35f9,0x35fd,0x3601,0x3605,0x3609,0x360d,0x3613, +0x3617,0x361b,0x361f,0x3623,0x3627,0x362b,0x362f,0x3633,0x2f91,0x3637,0x363b,0x3641,0x3645,0x3649,0x364d,0x2f99, +0x3651,0x3655,0x3659,0x365d,0x3661,0x3665,0x3669,0x366d,0x2b75,0x30af,0x3671,0x3675,0x3679,0x367d,0x3683,0x3687, +0x368b,0x368f,0x2f9d,0x3693,0x3699,0x369d,0x36a1,0x3161,0x36a5,0x36a9,0x36ad,0x36b1,0x36b5,0x36bb,0x36bf,0x36c3, +0x36c7,0x36cd,0x36d1,0x36d5,0x36d9,0x2c8d,0x36dd,0x36e1,0x36e7,0x36ed,0x36f3,0x36f7,0x36fd,0x3701,0x3705,0x3709, +0x370d,0x2fa1,0x2de9,0x3711,0x3715,0x3719,0x371d,0x3723,0x3727,0x372b,0x372f,0x30bf,0x3733,0x3737,0x373d,0x3741, +0x3745,0x374b,0x3751,0x3755,0x30c3,0x3759,0x375d,0x3761,0x3765,0x3769,0x376d,0x3771,0x3777,0x377b,0x3781,0x3785, +0x378b,0x30cb,0x378f,0x3793,0x3799,0x379d,0x37a1,0x37a7,0x37ad,0x37b1,0x37b5,0x37b9,0x37bd,0x37bd,0x37c1,0x37c5, +0x30d3,0x37c9,0x37cd,0x37d1,0x37d5,0x37d9,0x37df,0x37e3,0x2c55,0x37e9,0x37ef,0x37f3,0x37f9,0x37ff,0x3805,0x3809, +0x30eb,0x380d,0x3813,0x3819,0x381f,0x3825,0x3829,0x3829,0x30ef,0x3169,0x382d,0x3831,0x3835,0x3839,0x383f,0x2bbd, +0x30f7,0x3843,0x3847,0x2fcd,0x384d,0x3853,0x2f15,0x3859,0x385d,0x2fdd,0x3861,0x3865,0x3869,0x386f,0x386f,0x3875, +0x3879,0x387d,0x3883,0x3887,0x388b,0x388f,0x3895,0x3899,0x389d,0x38a1,0x38a5,0x38a9,0x38af,0x38b3,0x38b7,0x38bb, +0x38bf,0x38c3,0x38c7,0x38cd,0x38d3,0x38d7,0x38dd,0x38e1,0x38e7,0x38eb,0x2ff5,0x38ef,0x38f5,0x38fb,0x38ff,0x3905, +0x3909,0x390f,0x3913,0x3917,0x391b,0x391f,0x3923,0x3927,0x392d,0x3933,0x3939,0x3575,0x393f,0x3943,0x3947,0x394b, +0x394f,0x3953,0x3957,0x395b,0x395f,0x3963,0x3967,0x396b,0x2c9d,0x3971,0x3975,0x3979,0x397d,0x3981,0x3985,0x3001, +0x3989,0x398d,0x3991,0x3995,0x3999,0x399f,0x39a5,0x39ab,0x39af,0x39b3,0x39b7,0x39bb,0x39c1,0x39c5,0x39cb,0x39cf, +0x39d3,0x39d9,0x39df,0x39e3,0x2ba9,0x39e7,0x39eb,0x39ef,0x39f3,0x39f7,0x39fb,0x3113,0x39ff,0x3a03,0x3a07,0x3a0b, +0x3a0f,0x3a13,0x3a17,0x3a1b,0x3a1f,0x3a23,0x3a29,0x3a2d,0x3a31,0x3a35,0x3a39,0x3a3d,0x3a43,0x3a49,0x3a4d,0x3a51, +0x3127,0x312b,0x3a55,0x3a59,0x3a5f,0x3a63,0x3a67,0x3a6b,0x3a6f,0x3a75,0x3a7b,0x3a7f,0x3a83,0x3a87,0x3a8d,0x312f, +0x3a91,0x3a97,0x3a9d,0x3aa1,0x3aa5,0x3aa9,0x3aaf,0x3ab3,0x3ab7,0x3abb,0x3abf,0x3ac3,0x3ac7,0x3acb,0x3ad1,0x3ad5, +0x3ad9,0x3add,0x3ae3,0x3ae7,0x3aeb,0x3aef,0x3af3,0x3af9,0x3aff,0x3b03,0x3b07,0x3b0b,0x3b11,0x3b15,0x3147,0x3147, +0x3b1b,0x3b1f,0x3b25,0x3b29,0x3b2d,0x3b31,0x3b35,0x3b39,0x3b3d,0x3b41,0x314b,0x3b47,0x3b4b,0x3b4f,0x3b53,0x3b57, +0x3b5b,0x3b61,0x3b65,0x3b6b,0x3b71,0x3b77,0x3b7b,0x3b7f,0x3b83,0x3b87,0x3b8b,0x3b8f,0x3b93,0x3b97,1,1 +}; + +static const UCPTrie norm2_nfc_data_trie={ + norm2_nfc_data_trieIndex, + { norm2_nfc_data_trieData }, + 1788, 7984, + 0x2fc00, 0x30, + 0, 0, + 0, 0, + 0xc4, 0x226, + 0x1, +}; + +static const uint16_t norm2_nfc_data_extraData[7732]={ +0xffff,0xffff,0x8670,0x44dc,0x8670,0x44c0,0x8670,0x44de,0x600,0x180,0x602,0x182,0x604,0x185,0x606,0x186, +0x608,0x200,0x60c,0x205,0x60e,0x44d,0x610,0x189,0x612,0x3d44,0x614,0x18b,0x618,0x39a,0x61e,0x400, +0x622,0x404,0x646,0x3d41,0x64a,0x3c00,0x8650,0x208,0x60e,0x3c04,0x646,0x3c08,0x8662,0x3c0c,0x602,0x20c, +0x604,0x210,0x60e,0x214,0x618,0x218,0x864e,0x18f,0x60e,0x3c14,0x618,0x21c,0x646,0x3c18,0x64e,0x3c20, +0x65a,0x3c24,0x8662,0x3c1c,0x600,0x190,0x602,0x192,0x604,0x195,0x606,0x3d78,0x608,0x225,0x60c,0x228, +0x60e,0x22c,0x610,0x196,0x612,0x3d74,0x618,0x234,0x61e,0x408,0x622,0x40c,0x646,0x3d71,0x64e,0x451, +0x650,0x230,0x65a,0x3c30,0x8660,0x3c34,0x860e,0x3c3c,0x602,0x3e8,0x604,0x238,0x608,0x3c40,0x60c,0x23c, +0x60e,0x240,0x618,0x3cc,0x864e,0x244,0x604,0x248,0x60e,0x3c44,0x610,0x3c4c,0x618,0x43c,0x646,0x3c48, +0x64e,0x3c50,0x865c,0x3c54,0x600,0x198,0x602,0x19a,0x604,0x19c,0x606,0x250,0x608,0x254,0x60c,0x258, +0x60e,0x260,0x610,0x19f,0x612,0x3d90,0x618,0x39e,0x61e,0x410,0x622,0x414,0x646,0x3d94,0x650,0x25c, +0x8660,0x3c58,0x8604,0x268,0x602,0x3c60,0x618,0x3d0,0x646,0x3c64,0x64e,0x26c,0x8662,0x3c68,0x602,0x272, +0x618,0x27a,0x646,0x3c6d,0x64e,0x276,0x65a,0x3c78,0x8662,0x3c74,0x602,0x3c7c,0x60e,0x3c80,0x8646,0x3c84, +0x600,0x3f0,0x602,0x286,0x606,0x1a2,0x60e,0x3c88,0x618,0x28e,0x646,0x3c8c,0x64e,0x28a,0x65a,0x3c94, +0x8662,0x3c90,0x600,0x1a4,0x602,0x1a6,0x604,0x1a9,0x606,0x1ab,0x608,0x299,0x60c,0x29c,0x60e,0x45d, +0x610,0x1ad,0x612,0x3d9c,0x616,0x2a0,0x618,0x3a2,0x61e,0x418,0x622,0x41c,0x636,0x341,0x646,0x3d99, +0x8650,0x3d5,0x602,0x3ca8,0x860e,0x3cac,0x602,0x2a8,0x60e,0x3cb0,0x618,0x2b0,0x61e,0x420,0x622,0x424, +0x646,0x3cb5,0x64e,0x2ac,0x8662,0x3cbc,0x602,0x2b5,0x604,0x2b8,0x60e,0x3cc0,0x618,0x2c1,0x646,0x3cc5, +0x64c,0x430,0x864e,0x2bc,0x60e,0x3cd4,0x618,0x2c8,0x646,0x3cd8,0x64c,0x434,0x64e,0x2c4,0x65a,0x3ce0, +0x8662,0x3cdc,0x600,0x1b2,0x602,0x1b4,0x604,0x1b6,0x606,0x2d1,0x608,0x2d5,0x60c,0x2d8,0x610,0x1b9, +0x612,0x3dcc,0x614,0x2dc,0x616,0x2e0,0x618,0x3a6,0x61e,0x428,0x622,0x42c,0x636,0x35f,0x646,0x3dc8, +0x648,0x3ce4,0x650,0x2e4,0x65a,0x3cec,0x8660,0x3ce8,0x606,0x3cf8,0x8646,0x3cfc,0x600,0x3d00,0x602,0x3d04, +0x604,0x2e8,0x60e,0x3d0c,0x610,0x3d08,0x8646,0x3d10,0x60e,0x3d14,0x8610,0x3d18,0x600,0x3de4,0x602,0x1ba, +0x604,0x2ec,0x606,0x3df0,0x608,0x464,0x60e,0x3d1c,0x610,0x2f0,0x612,0x3dec,0x8646,0x3de8,0x602,0x2f2, +0x604,0x3d20,0x60e,0x2f6,0x618,0x2fa,0x646,0x3d24,0x8662,0x3d28,0x600,0x1c0,0x602,0x1c2,0x604,0x1c5, +0x606,0x1c6,0x608,0x202,0x60c,0x207,0x60e,0x44f,0x610,0x1c9,0x612,0x3d46,0x614,0x1cb,0x618,0x39c, +0x61e,0x402,0x622,0x406,0x646,0x3d43,0x64a,0x3c02,0x8650,0x20a,0x60e,0x3c06,0x646,0x3c0a,0x8662,0x3c0e, +0x602,0x20e,0x604,0x212,0x60e,0x216,0x618,0x21a,0x864e,0x1cf,0x60e,0x3c16,0x618,0x21e,0x646,0x3c1a, +0x64e,0x3c22,0x65a,0x3c26,0x8662,0x3c1e,0x600,0x1d0,0x602,0x1d2,0x604,0x1d5,0x606,0x3d7a,0x608,0x227, +0x60c,0x22a,0x60e,0x22e,0x610,0x1d6,0x612,0x3d76,0x618,0x236,0x61e,0x40a,0x622,0x40e,0x646,0x3d73, +0x64e,0x453,0x650,0x232,0x65a,0x3c32,0x8660,0x3c36,0x860e,0x3c3e,0x602,0x3ea,0x604,0x23a,0x608,0x3c42, +0x60c,0x23e,0x60e,0x242,0x618,0x3ce,0x864e,0x246,0x604,0x24a,0x60e,0x3c46,0x610,0x3c4e,0x618,0x43e, +0x646,0x3c4a,0x64e,0x3c52,0x65c,0x3c56,0x8662,0x3d2c,0x600,0x1d8,0x602,0x1da,0x604,0x1dc,0x606,0x252, +0x608,0x256,0x60c,0x25a,0x610,0x1df,0x612,0x3d92,0x618,0x3a0,0x61e,0x412,0x622,0x416,0x646,0x3d96, +0x650,0x25e,0x8660,0x3c5a,0x604,0x26a,0x8618,0x3e0,0x602,0x3c62,0x618,0x3d2,0x646,0x3c66,0x64e,0x26e, +0x8662,0x3c6a,0x602,0x274,0x618,0x27c,0x646,0x3c6f,0x64e,0x278,0x65a,0x3c7a,0x8662,0x3c76,0x602,0x3c7e, +0x60e,0x3c82,0x8646,0x3c86,0x600,0x3f2,0x602,0x288,0x606,0x1e2,0x60e,0x3c8a,0x618,0x290,0x646,0x3c8e, +0x64e,0x28c,0x65a,0x3c96,0x8662,0x3c92,0x600,0x1e4,0x602,0x1e6,0x604,0x1e9,0x606,0x1eb,0x608,0x29b, +0x60c,0x29e,0x60e,0x45f,0x610,0x1ed,0x612,0x3d9e,0x616,0x2a2,0x618,0x3a4,0x61e,0x41a,0x622,0x41e, +0x636,0x343,0x646,0x3d9b,0x8650,0x3d7,0x602,0x3caa,0x860e,0x3cae,0x602,0x2aa,0x60e,0x3cb2,0x618,0x2b2, +0x61e,0x422,0x622,0x426,0x646,0x3cb7,0x64e,0x2ae,0x8662,0x3cbe,0x602,0x2b7,0x604,0x2ba,0x60e,0x3cc2, +0x618,0x2c3,0x646,0x3cc7,0x64c,0x432,0x864e,0x2be,0x60e,0x3cd6,0x610,0x3d2e,0x618,0x2ca,0x646,0x3cda, +0x64c,0x436,0x64e,0x2c6,0x65a,0x3ce2,0x8662,0x3cde,0x600,0x1f2,0x602,0x1f4,0x604,0x1f6,0x606,0x2d3, +0x608,0x2d7,0x60c,0x2da,0x610,0x1f9,0x612,0x3dce,0x614,0x2de,0x616,0x2e2,0x618,0x3a8,0x61e,0x42a, +0x622,0x42e,0x636,0x361,0x646,0x3dca,0x648,0x3ce6,0x650,0x2e6,0x65a,0x3cee,0x8660,0x3cea,0x606,0x3cfa, +0x8646,0x3cfe,0x600,0x3d02,0x602,0x3d06,0x604,0x2ea,0x60e,0x3d0e,0x610,0x3d0a,0x614,0x3d30,0x8646,0x3d12, +0x60e,0x3d16,0x8610,0x3d1a,0x600,0x3de6,0x602,0x1fa,0x604,0x2ee,0x606,0x3df2,0x608,0x466,0x60e,0x3d1e, +0x610,0x1fe,0x612,0x3dee,0x614,0x3d32,0x8646,0x3dea,0x602,0x2f4,0x604,0x3d22,0x60e,0x2f8,0x618,0x2fc, +0x646,0x3d26,0x8662,0x3d2a,0x600,0x3fda,0x602,0x70a,0x8684,0x3f82,0x602,0x3f8,0x8608,0x3c4,0x8602,0x3fc, +0x602,0x3fa,0x8608,0x3c6,0x8602,0x3fe,0x860e,0x3d36,0x8618,0x3dc,0x8618,0x3de,0x600,0x3f74,0x602,0x70c, +0x608,0x3f72,0x60c,0x3f70,0x626,0x3e11,0x628,0x3e13,0x868a,0x3f78,0x600,0x3f90,0x602,0x710,0x626,0x3e31, +0x8628,0x3e33,0x600,0x3f94,0x602,0x712,0x626,0x3e51,0x628,0x3e53,0x868a,0x3f98,0x600,0x3fb4,0x602,0x714, +0x608,0x3fb2,0x60c,0x3fb0,0x610,0x754,0x626,0x3e71,0x8628,0x3e73,0x600,0x3ff0,0x602,0x718,0x626,0x3e91, +0x8628,0x3e93,0x8628,0x3fd8,0x600,0x3fd4,0x602,0x71c,0x608,0x3fd2,0x60c,0x3fd0,0x610,0x756,0x8628,0x3eb3, +0x600,0x3ff4,0x602,0x71e,0x626,0x3ed1,0x628,0x3ed3,0x868a,0x3ff8,0x600,0x3ee1,0x602,0x759,0x608,0x3f62, +0x60c,0x3f60,0x626,0x3e01,0x628,0x3e03,0x684,0x3f6d,0x868a,0x3f66,0x600,0x3ee4,0x602,0x75a,0x626,0x3e21, +0x8628,0x3e23,0x600,0x3ee9,0x602,0x75d,0x626,0x3e41,0x628,0x3e43,0x684,0x3f8d,0x868a,0x3f86,0x600,0x3eec, +0x602,0x75e,0x608,0x3fa2,0x60c,0x3fa0,0x610,0x795,0x626,0x3e61,0x628,0x3e63,0x8684,0x3fac,0x600,0x3ef0, +0x602,0x798,0x626,0x3e81,0x8628,0x3e83,0x626,0x3fc8,0x8628,0x3fca,0x600,0x3ef4,0x602,0x79a,0x608,0x3fc2, +0x60c,0x3fc0,0x610,0x797,0x626,0x3ea1,0x628,0x3ea3,0x8684,0x3fcc,0x600,0x3ef9,0x602,0x79d,0x626,0x3ec1, +0x628,0x3ec3,0x684,0x3fed,0x868a,0x3fe6,0x602,0x7a6,0x8610,0x7a8,0x8610,0x80e,0x60c,0x9a0,0x8610,0x9a4, +0x8602,0x806,0x600,0x800,0x60c,0x9ac,0x8610,0x802,0x60c,0x982,0x8610,0x9b8,0x8610,0x9bc,0x600,0x81a, +0x608,0x9c4,0x60c,0x832,0x8610,0x9c8,0x8602,0x818,0x8610,0x9cc,0x608,0x9dc,0x60c,0x81c,0x610,0x9e0, +0x8616,0x9e4,0x8610,0x9e8,0x8610,0x9f0,0x8610,0x9d8,0x60c,0x9a2,0x8610,0x9a6,0x8602,0x8a6,0x600,0x8a0, +0x60c,0x9ae,0x8610,0x8a2,0x60c,0x984,0x8610,0x9ba,0x8610,0x9be,0x600,0x8ba,0x608,0x9c6,0x60c,0x872, +0x8610,0x9ca,0x8602,0x8b8,0x8610,0x9ce,0x608,0x9de,0x60c,0x8bc,0x610,0x9e2,0x8616,0x9e6,0x8610,0x9ea, +0x8610,0x9f2,0x8610,0x9da,0x8610,0x8ae,0x861e,0x8ec,0x861e,0x8ee,0x8610,0x9b4,0x8610,0x9b6,0x8610,0x9d4, +0x8610,0x9d6,0xca6,0xc44,0xca8,0xc46,0x8caa,0xc4a,0x8ca8,0xc48,0x8ca8,0xc4c,0x8ca8,0xd84,0x8ca8,0xda6, +0x8ca8,0xd80,0x9278,0x1252,0x9278,0x1262,0x9278,0x1268,0x137c,0x1396,0x93ae,0x1398,0x167c,0x1696,0x16ac,0x1690, +0x96ae,0x1698,0x97ae,0x1728,0x177c,0x1794,0x97ae,0x1798,0x977c,0x1796,0x98ac,0x1890,0x99aa,0x1980,0x1984,0x1995, +0x19aa,0x198e,0x99ac,0x1990,0x1a7c,0x1a94,0x9aae,0x1a98,0x9a7c,0x1a96,0x1b94,0x1bb4,0x1b9e,0x1bb9,0x9bbe,0x1bbc, +0xa05c,0x204c,0xb66a,0x360c,0xb66a,0x3610,0xb66a,0x3614,0xb66a,0x3618,0xb66a,0x361c,0xb66a,0x3624,0xb66a,0x3676, +0xb66a,0x367a,0xb66a,0x3680,0xb66a,0x3682,0xb66a,0x3686,0x600,0x3f9a,0x602,0x3f9c,0x8684,0x3f9e,0x600,0x3fba, +0x602,0x3fbc,0x8684,0x3fbe,0x8670,0x4334,0x8670,0x4336,0x8670,0x435c,0x8670,0x439a,0x8670,0x439e,0x8670,0x439c, +0x8670,0x4408,0x8670,0x4412,0x8670,0x4418,0x8670,0x4448,0x8670,0x444c,0x8670,0x4482,0x8670,0x4488,0x8670,0x448e, +0x8670,0x4492,0x8670,0x44da,0x8670,0x44c4,0x8670,0x44e0,0x8670,0x44e2,0x8670,0x44e8,0x8670,0x44ea,0x8670,0x44f0, +0x8670,0x44f2,0x8670,0x4500,0x8670,0x4502,0x8670,0x45c0,0x8670,0x45c2,0x8670,0x4508,0x8670,0x450a,0x8670,0x4510, +0x8670,0x4512,0x8670,0x45c4,0x8670,0x45c6,0x8670,0x4558,0x8670,0x455a,0x8670,0x455c,0x8670,0x455e,0x8670,0x45d4, +0x8670,0x45d6,0x8670,0x45d8,0x8670,0x45da,0xe132,0x6128,0xe132,0x6098,0xe132,0x609c,0xe132,0x60a0,0xe132,0x60a4, +0xe132,0x60a8,0xe132,0x60ac,0xe132,0x60b0,0xe132,0x60b4,0xe132,0x60b8,0xe132,0x60bc,0xe132,0x60c0,0xe132,0x60c4, +0xe132,0x60ca,0xe132,0x60ce,0xe132,0x60d2,0x6132,0x60e0,0xe134,0x60e2,0x6132,0x60e6,0xe134,0x60e8,0x6132,0x60ec, +0xe134,0x60ee,0x6132,0x60f2,0xe134,0x60f4,0x6132,0x60f8,0xe134,0x60fa,0xe132,0x613c,0xe132,0x61e8,0xe132,0x6158, +0xe132,0x615c,0xe132,0x6160,0xe132,0x6164,0xe132,0x6168,0xe132,0x616c,0xe132,0x6170,0xe132,0x6174,0xe132,0x6178, +0xe132,0x617c,0xe132,0x6180,0xe132,0x6184,0xe132,0x618a,0xe132,0x618e,0xe132,0x6192,0x6132,0x61a0,0xe134,0x61a2, +0x6132,0x61a6,0xe134,0x61a8,0x6132,0x61ac,0xe134,0x61ae,0x6132,0x61b2,0xe134,0x61b4,0x6132,0x61b8,0xe134,0x61ba, +0xe132,0x61ee,0xe132,0x61f0,0xe132,0x61f2,0xe132,0x61f4,0xe132,0x61fc,0xb489,0x2e82,0x2134,0xb489,0x2e82,0x2138, +0xb489,0x2e82,0x2156,0xb489,0x49c2,0x225c,0xb489,0x49c2,0x225e,0x3489,0xcf82,0x2696,0xb489,0xd5c2,0x2698,0x348b, +0x2c02,0x2978,0x348b,0x2e82,0x2976,0xb48b,0x2f42,0x297c,0xb48b,0x6bc2,0x2b74,0xb48b,0x6bc2,0x2b76,0xb48d,0x4c02, +0x3270,2,0xe602,0x41,0x302,0x600,0x3d4c,0x602,0x3d48,0x606,0x3d54,0x8612,0x3d50,0xe602,0x41,0x308, +0x8608,0x3bc,0xe602,0x41,0x30a,0x8602,0x3f4,0xca02,0x43,0x327,0x8602,0x3c10,0xe602,0x45,0x302,0x600, +0x3d80,0x602,0x3d7c,0x606,0x3d88,0x8612,0x3d84,0xe602,0x49,0x308,0x8602,0x3c5c,0xe602,0x4f,0x302,0x600, +0x3da4,0x602,0x3da0,0x606,0x3dac,0x8612,0x3da8,0xe602,0x4f,0x303,0x602,0x3c98,0x608,0x458,0x8610,0x3c9c, +0xe602,0x4f,0x308,0x8608,0x454,0xe602,0x55,0x308,0x600,0x3b6,0x602,0x3ae,0x608,0x3aa,0x8618,0x3b2, +0xe602,0x61,0x302,0x600,0x3d4e,0x602,0x3d4a,0x606,0x3d56,0x8612,0x3d52,0xe602,0x61,0x308,0x8608,0x3be, +0xe602,0x61,0x30a,0x8602,0x3f6,0xca02,0x63,0x327,0x8602,0x3c12,0xe602,0x65,0x302,0x600,0x3d82,0x602, +0x3d7e,0x606,0x3d8a,0x8612,0x3d86,0xe602,0x69,0x308,0x8602,0x3c5e,0xe602,0x6f,0x302,0x600,0x3da6,0x602, +0x3da2,0x606,0x3dae,0x8612,0x3daa,0xe602,0x6f,0x303,0x602,0x3c9a,0x608,0x45a,0x8610,0x3c9e,0xe602,0x6f, +0x308,0x8608,0x456,0xe602,0x75,0x308,0x600,0x3b8,0x602,0x3b0,0x608,0x3ac,0x8618,0x3b4,0xe602,0x41, +0x306,0x600,0x3d60,0x602,0x3d5c,0x606,0x3d68,0x8612,0x3d64,0xe602,0x61,0x306,0x600,0x3d62,0x602,0x3d5e, +0x606,0x3d6a,0x8612,0x3d66,0xe602,0x45,0x304,0x600,0x3c28,0x8602,0x3c2c,0xe602,0x65,0x304,0x600,0x3c2a, +0x8602,0x3c2e,0xe602,0x4f,0x304,0x600,0x3ca0,0x8602,0x3ca4,0xe602,0x6f,0x304,0x600,0x3ca2,0x8602,0x3ca6, +0xe602,0x53,0x301,0x860e,0x3cc8,0xe602,0x73,0x301,0x860e,0x3cca,0xe602,0x53,0x30c,0x860e,0x3ccc,0xe602, +0x73,0x30c,0x860e,0x3cce,0xe602,0x55,0x303,0x8602,0x3cf0,0xe602,0x75,0x303,0x8602,0x3cf2,0xe602,0x55, +0x304,0x8610,0x3cf4,0xe602,0x75,0x304,0x8610,0x3cf6,0xd802,0x4f,0x31b,0x600,0x3db8,0x602,0x3db4,0x606, +0x3dc0,0x612,0x3dbc,0x8646,0x3dc4,0xd802,0x6f,0x31b,0x600,0x3dba,0x602,0x3db6,0x606,0x3dc2,0x612,0x3dbe, +0x8646,0x3dc6,0xd802,0x55,0x31b,0x600,0x3dd4,0x602,0x3dd0,0x606,0x3ddc,0x612,0x3dd8,0x8646,0x3de0,0xd802, +0x75,0x31b,0x600,0x3dd6,0x602,0x3dd2,0x606,0x3dde,0x612,0x3dda,0x8646,0x3de2,0xca02,0x4f,0x328,0x8608, +0x3d8,0xca02,0x6f,0x328,0x8608,0x3da,0xe602,0x41,0x307,0x8608,0x3c0,0xe602,0x61,0x307,0x8608,0x3c2, +0xca02,0x45,0x327,0x860c,0x3c38,0xca02,0x65,0x327,0x860c,0x3c3a,0xe602,0x4f,0x307,0x8608,0x460,0xe602, +0x6f,0x307,0x8608,0x462,0xe602,0x3b1,0x301,0x868a,0x3f68,0xe602,0x3b7,0x301,0x868a,0x3f88,0xe602,0x3b9, +0x308,0x600,0x3fa4,0x602,0x720,0x8684,0x3fae,0xe602,0x3c5,0x308,0x600,0x3fc4,0x602,0x760,0x8684,0x3fce, +0xe602,0x3c9,0x301,0x868a,0x3fe8,2,0xcc6,0xcc2,0x99aa,0x1996,2,0xdd9,0xdcf,0x9b94,0x1bba,0xdc02, +0x4c,0x323,0x8608,0x3c70,0xdc02,0x6c,0x323,0x8608,0x3c72,0xdc02,0x52,0x323,0x8608,0x3cb8,0xdc02,0x72, +0x323,0x8608,0x3cba,0xdc02,0x53,0x323,0x860e,0x3cd0,0xdc02,0x73,0x323,0x860e,0x3cd2,0xdc02,0x41,0x323, +0x604,0x3d58,0x860c,0x3d6c,0xdc02,0x61,0x323,0x604,0x3d5a,0x860c,0x3d6e,0xdc02,0x45,0x323,0x8604,0x3d8c, +0xdc02,0x65,0x323,0x8604,0x3d8e,0xdc02,0x4f,0x323,0x8604,0x3db0,0xdc02,0x6f,0x323,0x8604,0x3db2,0xe602, +0x3b1,0x313,0x600,0x3e05,0x602,0x3e09,0x684,0x3e0d,0x868a,0x3f00,0xe602,0x3b1,0x314,0x600,0x3e07,0x602, +0x3e0b,0x684,0x3e0f,0x868a,0x3f02,0x1f00,0xe643,0x3b1,0x313,0x300,0x868a,0x3f04,0x1f01,0xe643,0x3b1,0x314, +0x300,0x868a,0x3f06,0x1f00,0xe643,0x3b1,0x313,0x301,0x868a,0x3f08,0x1f01,0xe643,0x3b1,0x314,0x301,0x868a, +0x3f0a,0x1f00,0xe643,0x3b1,0x313,0x342,0x868a,0x3f0c,0x1f01,0xe643,0x3b1,0x314,0x342,0x868a,0x3f0e,0xe602, +0x391,0x313,0x600,0x3e15,0x602,0x3e19,0x684,0x3e1d,0x868a,0x3f10,0xe602,0x391,0x314,0x600,0x3e17,0x602, +0x3e1b,0x684,0x3e1f,0x868a,0x3f12,0x1f08,0xe643,0x391,0x313,0x300,0x868a,0x3f14,0x1f09,0xe643,0x391,0x314, +0x300,0x868a,0x3f16,0x1f08,0xe643,0x391,0x313,0x301,0x868a,0x3f18,0x1f09,0xe643,0x391,0x314,0x301,0x868a, +0x3f1a,0x1f08,0xe643,0x391,0x313,0x342,0x868a,0x3f1c,0x1f09,0xe643,0x391,0x314,0x342,0x868a,0x3f1e,0xe602, +0x3b5,0x313,0x600,0x3e24,0x8602,0x3e28,0xe602,0x3b5,0x314,0x600,0x3e26,0x8602,0x3e2a,0xe602,0x395,0x313, +0x600,0x3e34,0x8602,0x3e38,0xe602,0x395,0x314,0x600,0x3e36,0x8602,0x3e3a,0xe602,0x3b7,0x313,0x600,0x3e45, +0x602,0x3e49,0x684,0x3e4d,0x868a,0x3f20,0xe602,0x3b7,0x314,0x600,0x3e47,0x602,0x3e4b,0x684,0x3e4f,0x868a, +0x3f22,0x1f20,0xe643,0x3b7,0x313,0x300,0x868a,0x3f24,0x1f21,0xe643,0x3b7,0x314,0x300,0x868a,0x3f26,0x1f20, +0xe643,0x3b7,0x313,0x301,0x868a,0x3f28,0x1f21,0xe643,0x3b7,0x314,0x301,0x868a,0x3f2a,0x1f20,0xe643,0x3b7, +0x313,0x342,0x868a,0x3f2c,0x1f21,0xe643,0x3b7,0x314,0x342,0x868a,0x3f2e,0xe602,0x397,0x313,0x600,0x3e55, +0x602,0x3e59,0x684,0x3e5d,0x868a,0x3f30,0xe602,0x397,0x314,0x600,0x3e57,0x602,0x3e5b,0x684,0x3e5f,0x868a, +0x3f32,0x1f28,0xe643,0x397,0x313,0x300,0x868a,0x3f34,0x1f29,0xe643,0x397,0x314,0x300,0x868a,0x3f36,0x1f28, +0xe643,0x397,0x313,0x301,0x868a,0x3f38,0x1f29,0xe643,0x397,0x314,0x301,0x868a,0x3f3a,0x1f28,0xe643,0x397, +0x313,0x342,0x868a,0x3f3c,0x1f29,0xe643,0x397,0x314,0x342,0x868a,0x3f3e,0xe602,0x3b9,0x313,0x600,0x3e64, +0x602,0x3e68,0x8684,0x3e6c,0xe602,0x3b9,0x314,0x600,0x3e66,0x602,0x3e6a,0x8684,0x3e6e,0xe602,0x399,0x313, +0x600,0x3e74,0x602,0x3e78,0x8684,0x3e7c,0xe602,0x399,0x314,0x600,0x3e76,0x602,0x3e7a,0x8684,0x3e7e,0xe602, +0x3bf,0x313,0x600,0x3e84,0x8602,0x3e88,0xe602,0x3bf,0x314,0x600,0x3e86,0x8602,0x3e8a,0xe602,0x39f,0x313, +0x600,0x3e94,0x8602,0x3e98,0xe602,0x39f,0x314,0x600,0x3e96,0x8602,0x3e9a,0xe602,0x3c5,0x313,0x600,0x3ea4, +0x602,0x3ea8,0x8684,0x3eac,0xe602,0x3c5,0x314,0x600,0x3ea6,0x602,0x3eaa,0x8684,0x3eae,0xe602,0x3a5,0x314, +0x600,0x3eb6,0x602,0x3eba,0x8684,0x3ebe,0xe602,0x3c9,0x313,0x600,0x3ec5,0x602,0x3ec9,0x684,0x3ecd,0x868a, +0x3f40,0xe602,0x3c9,0x314,0x600,0x3ec7,0x602,0x3ecb,0x684,0x3ecf,0x868a,0x3f42,0x1f60,0xe643,0x3c9,0x313, +0x300,0x868a,0x3f44,0x1f61,0xe643,0x3c9,0x314,0x300,0x868a,0x3f46,0x1f60,0xe643,0x3c9,0x313,0x301,0x868a, +0x3f48,0x1f61,0xe643,0x3c9,0x314,0x301,0x868a,0x3f4a,0x1f60,0xe643,0x3c9,0x313,0x342,0x868a,0x3f4c,0x1f61, +0xe643,0x3c9,0x314,0x342,0x868a,0x3f4e,0xe602,0x3a9,0x313,0x600,0x3ed5,0x602,0x3ed9,0x684,0x3edd,0x868a, +0x3f50,0xe602,0x3a9,0x314,0x600,0x3ed7,0x602,0x3edb,0x684,0x3edf,0x868a,0x3f52,0x1f68,0xe643,0x3a9,0x313, +0x300,0x868a,0x3f54,0x1f69,0xe643,0x3a9,0x314,0x300,0x868a,0x3f56,0x1f68,0xe643,0x3a9,0x313,0x301,0x868a, +0x3f58,0x1f69,0xe643,0x3a9,0x314,0x301,0x868a,0x3f5a,0x1f68,0xe643,0x3a9,0x313,0x342,0x868a,0x3f5c,0x1f69, +0xe643,0x3a9,0x314,0x342,0x868a,0x3f5e,0xe602,0x3b1,0x300,0x868a,0x3f64,0xe602,0x3b7,0x300,0x868a,0x3f84, +0xe602,0x3c9,0x300,0x868a,0x3fe4,0xe602,0x3b1,0x342,0x868a,0x3f6e,0xe602,0x3b7,0x342,0x868a,0x3f8e,0xe602, +0x3c9,0x342,0x868a,0x3fee,3,0xe602,0x41,0x300,0xe602,0x41,0x301,0xe602,0x41,0x303,0xe602,0x45, +0x300,0xe602,0x45,0x301,0xe602,0x45,0x308,0xe602,0x49,0x300,0xe602,0x49,0x301,0xe602,0x49,0x302, +0xe602,0x4e,0x303,0xe602,0x4f,0x300,0xe602,0x4f,0x301,0xe602,0x55,0x300,0xe602,0x55,0x301,0xe602, +0x55,0x302,0xe602,0x59,0x301,0xe602,0x61,0x300,0xe602,0x61,0x301,0xe602,0x61,0x303,0xe602,0x65, +0x300,0xe602,0x65,0x301,0xe602,0x65,0x308,0xe602,0x69,0x300,0xe602,0x69,0x301,0xe602,0x69,0x302, +0xe602,0x6e,0x303,0xe602,0x6f,0x300,0xe602,0x6f,0x301,0xe602,0x75,0x300,0xe602,0x75,0x301,0xe602, +0x75,0x302,0xe602,0x79,0x301,0xe602,0x79,0x308,0xe602,0x41,0x304,0xe602,0x61,0x304,0xca02,0x41, +0x328,0xca02,0x61,0x328,0xe602,0x43,0x301,0xe602,0x63,0x301,0xe602,0x43,0x302,0xe602,0x63,0x302, +0xe602,0x43,0x307,0xe602,0x63,0x307,0xe602,0x43,0x30c,0xe602,0x63,0x30c,0xe602,0x44,0x30c,0xe602, +0x64,0x30c,0xe602,0x45,0x306,0xe602,0x65,0x306,0xe602,0x45,0x307,0xe602,0x65,0x307,0xca02,0x45, +0x328,0xca02,0x65,0x328,0xe602,0x45,0x30c,0xe602,0x65,0x30c,0xe602,0x47,0x302,0xe602,0x67,0x302, +0xe602,0x47,0x306,0xe602,0x67,0x306,0xe602,0x47,0x307,0xe602,0x67,0x307,0xca02,0x47,0x327,0xca02, +0x67,0x327,0xe602,0x48,0x302,0xe602,0x68,0x302,0xe602,0x49,0x303,0xe602,0x69,0x303,0xe602,0x49, +0x304,0xe602,0x69,0x304,0xe602,0x49,0x306,0xe602,0x69,0x306,0xca02,0x49,0x328,0xca02,0x69,0x328, +0xe602,0x49,0x307,0xe602,0x4a,0x302,0xe602,0x6a,0x302,0xca02,0x4b,0x327,0xca02,0x6b,0x327,0xe602, +0x4c,0x301,0xe602,0x6c,0x301,0xca02,0x4c,0x327,0xca02,0x6c,0x327,0xe602,0x4c,0x30c,0xe602,0x6c, +0x30c,0xe602,0x4e,0x301,0xe602,0x6e,0x301,0xca02,0x4e,0x327,0xca02,0x6e,0x327,0xe602,0x4e,0x30c, +0xe602,0x6e,0x30c,0xe602,0x4f,0x306,0xe602,0x6f,0x306,0xe602,0x4f,0x30b,0xe602,0x6f,0x30b,0xe602, +0x52,0x301,0xe602,0x72,0x301,0xca02,0x52,0x327,0xca02,0x72,0x327,0xe602,0x52,0x30c,0xe602,0x72, +0x30c,0xe602,0x53,0x302,0xe602,0x73,0x302,0xca02,0x53,0x327,0xca02,0x73,0x327,0xca02,0x54,0x327, +0xca02,0x74,0x327,0xe602,0x54,0x30c,0xe602,0x74,0x30c,0xe602,0x55,0x306,0xe602,0x75,0x306,0xe602, +0x55,0x30a,0xe602,0x75,0x30a,0xe602,0x55,0x30b,0xe602,0x75,0x30b,0xca02,0x55,0x328,0xca02,0x75, +0x328,0xe602,0x57,0x302,0xe602,0x77,0x302,0xe602,0x59,0x302,0xe602,0x79,0x302,0xe602,0x59,0x308, +0xe602,0x5a,0x301,0xe602,0x7a,0x301,0xe602,0x5a,0x307,0xe602,0x7a,0x307,0xe602,0x5a,0x30c,0xe602, +0x7a,0x30c,0xe602,0x41,0x30c,0xe602,0x61,0x30c,0xe602,0x49,0x30c,0xe602,0x69,0x30c,0xe602,0x4f, +0x30c,0xe602,0x6f,0x30c,0xe602,0x55,0x30c,0xe602,0x75,0x30c,0xdc,0xe643,0x55,0x308,0x304,0xfc, +0xe643,0x75,0x308,0x304,0xdc,0xe643,0x55,0x308,0x301,0xfc,0xe643,0x75,0x308,0x301,0xdc,0xe643, +0x55,0x308,0x30c,0xfc,0xe643,0x75,0x308,0x30c,0xdc,0xe643,0x55,0x308,0x300,0xfc,0xe643,0x75, +0x308,0x300,0xc4,0xe643,0x41,0x308,0x304,0xe4,0xe643,0x61,0x308,0x304,0x226,0xe643,0x41,0x307, +0x304,0x227,0xe643,0x61,0x307,0x304,0xe602,0xc6,0x304,0xe602,0xe6,0x304,0xe602,0x47,0x30c,0xe602, +0x67,0x30c,0xe602,0x4b,0x30c,0xe602,0x6b,0x30c,0x1ea,0xe643,0x4f,0x328,0x304,0x1eb,0xe643,0x6f, +0x328,0x304,0xe602,0x1b7,0x30c,0xe602,0x292,0x30c,0xe602,0x6a,0x30c,0xe602,0x47,0x301,0xe602,0x67, +0x301,0xe602,0x4e,0x300,0xe602,0x6e,0x300,0xc5,0xe643,0x41,0x30a,0x301,0xe5,0xe643,0x61,0x30a, +0x301,0xe602,0xc6,0x301,0xe602,0xe6,0x301,0xe602,0xd8,0x301,0xe602,0xf8,0x301,0xe602,0x41,0x30f, +0xe602,0x61,0x30f,0xe602,0x41,0x311,0xe602,0x61,0x311,0xe602,0x45,0x30f,0xe602,0x65,0x30f,0xe602, +0x45,0x311,0xe602,0x65,0x311,0xe602,0x49,0x30f,0xe602,0x69,0x30f,0xe602,0x49,0x311,0xe602,0x69, +0x311,0xe602,0x4f,0x30f,0xe602,0x6f,0x30f,0xe602,0x4f,0x311,0xe602,0x6f,0x311,0xe602,0x52,0x30f, +0xe602,0x72,0x30f,0xe602,0x52,0x311,0xe602,0x72,0x311,0xe602,0x55,0x30f,0xe602,0x75,0x30f,0xe602, +0x55,0x311,0xe602,0x75,0x311,0xdc02,0x53,0x326,0xdc02,0x73,0x326,0xdc02,0x54,0x326,0xdc02,0x74, +0x326,0xe602,0x48,0x30c,0xe602,0x68,0x30c,0xd6,0xe643,0x4f,0x308,0x304,0xf6,0xe643,0x6f,0x308, +0x304,0xd5,0xe643,0x4f,0x303,0x304,0xf5,0xe643,0x6f,0x303,0x304,0x22e,0xe643,0x4f,0x307,0x304, +0x22f,0xe643,0x6f,0x307,0x304,0xe602,0x59,0x304,0xe602,0x79,0x304,0xe602,0xa8,0x301,0xe602,0x391, +0x301,0xe602,0x395,0x301,0xe602,0x397,0x301,0xe602,0x399,0x301,0xe602,0x39f,0x301,0xe602,0x3a5,0x301, +0xe602,0x3a9,0x301,0x3ca,0xe643,0x3b9,0x308,0x301,0xe602,0x399,0x308,0xe602,0x3a5,0x308,0xe602,0x3b5, +0x301,0xe602,0x3b9,0x301,0x3cb,0xe643,0x3c5,0x308,0x301,0xe602,0x3bf,0x301,0xe602,0x3c5,0x301,0xe602, +0x3d2,0x301,0xe602,0x3d2,0x308,0xe602,0x415,0x300,0xe602,0x415,0x308,0xe602,0x413,0x301,0xe602,0x406, +0x308,0xe602,0x41a,0x301,0xe602,0x418,0x300,0xe602,0x423,0x306,0xe602,0x418,0x306,0xe602,0x438,0x306, +0xe602,0x435,0x300,0xe602,0x435,0x308,0xe602,0x433,0x301,0xe602,0x456,0x308,0xe602,0x43a,0x301,0xe602, +0x438,0x300,0xe602,0x443,0x306,0xe602,0x474,0x30f,0xe602,0x475,0x30f,0xe602,0x416,0x306,0xe602,0x436, +0x306,0xe602,0x410,0x306,0xe602,0x430,0x306,0xe602,0x410,0x308,0xe602,0x430,0x308,0xe602,0x415,0x306, +0xe602,0x435,0x306,0xe602,0x4d8,0x308,0xe602,0x4d9,0x308,0xe602,0x416,0x308,0xe602,0x436,0x308,0xe602, +0x417,0x308,0xe602,0x437,0x308,0xe602,0x418,0x304,0xe602,0x438,0x304,0xe602,0x418,0x308,0xe602,0x438, +0x308,0xe602,0x41e,0x308,0xe602,0x43e,0x308,0xe602,0x4e8,0x308,0xe602,0x4e9,0x308,0xe602,0x42d,0x308, +0xe602,0x44d,0x308,0xe602,0x423,0x304,0xe602,0x443,0x304,0xe602,0x423,0x308,0xe602,0x443,0x308,0xe602, +0x423,0x30b,0xe602,0x443,0x30b,0xe602,0x427,0x308,0xe602,0x447,0x308,0xe602,0x42b,0x308,0xe602,0x44b, +0x308,0xe602,0x627,0x653,0xe602,0x627,0x654,0xe602,0x648,0x654,0xdc02,0x627,0x655,0xe602,0x64a,0x654, +0xe602,0x6d5,0x654,0xe602,0x6c1,0x654,0xe602,0x6d2,0x654,0x702,0x928,0x93c,0x702,0x930,0x93c,0x702, +0x933,0x93c,2,0x9c7,0x9be,2,0x9c7,0x9d7,2,0xb47,0xb56,2,0xb47,0xb3e,2,0xb47, +0xb57,2,0xb92,0xbd7,2,0xbc6,0xbbe,2,0xbc7,0xbbe,2,0xbc6,0xbd7,0x5b02,0xc46,0xc56, +2,0xcbf,0xcd5,2,0xcc6,0xcd5,2,0xcc6,0xcd6,0xcca,0x43,0xcc6,0xcc2,0xcd5,2,0xd46, +0xd3e,2,0xd47,0xd3e,2,0xd46,0xd57,0x902,0xdd9,0xdca,0xddc,0x943,0xdd9,0xdcf,0xdca,2, +0xdd9,0xddf,2,0x1025,0x102e,2,0x1b05,0x1b35,2,0x1b07,0x1b35,2,0x1b09,0x1b35,2,0x1b0b, +0x1b35,2,0x1b0d,0x1b35,2,0x1b11,0x1b35,2,0x1b3a,0x1b35,2,0x1b3c,0x1b35,2,0x1b3e,0x1b35, +2,0x1b3f,0x1b35,2,0x1b42,0x1b35,0xdc02,0x41,0x325,0xdc02,0x61,0x325,0xe602,0x42,0x307,0xe602, +0x62,0x307,0xdc02,0x42,0x323,0xdc02,0x62,0x323,0xdc02,0x42,0x331,0xdc02,0x62,0x331,0xc7,0xe643, +0x43,0x327,0x301,0xe7,0xe643,0x63,0x327,0x301,0xe602,0x44,0x307,0xe602,0x64,0x307,0xdc02,0x44, +0x323,0xdc02,0x64,0x323,0xdc02,0x44,0x331,0xdc02,0x64,0x331,0xca02,0x44,0x327,0xca02,0x64,0x327, +0xdc02,0x44,0x32d,0xdc02,0x64,0x32d,0x112,0xe643,0x45,0x304,0x300,0x113,0xe643,0x65,0x304,0x300, +0x112,0xe643,0x45,0x304,0x301,0x113,0xe643,0x65,0x304,0x301,0xdc02,0x45,0x32d,0xdc02,0x65,0x32d, +0xdc02,0x45,0x330,0xdc02,0x65,0x330,0x228,0xe643,0x45,0x327,0x306,0x229,0xe643,0x65,0x327,0x306, +0xe602,0x46,0x307,0xe602,0x66,0x307,0xe602,0x47,0x304,0xe602,0x67,0x304,0xe602,0x48,0x307,0xe602, +0x68,0x307,0xdc02,0x48,0x323,0xdc02,0x68,0x323,0xe602,0x48,0x308,0xe602,0x68,0x308,0xca02,0x48, +0x327,0xca02,0x68,0x327,0xdc02,0x48,0x32e,0xdc02,0x68,0x32e,0xdc02,0x49,0x330,0xdc02,0x69,0x330, +0xcf,0xe643,0x49,0x308,0x301,0xef,0xe643,0x69,0x308,0x301,0xe602,0x4b,0x301,0xe602,0x6b,0x301, +0xdc02,0x4b,0x323,0xdc02,0x6b,0x323,0xdc02,0x4b,0x331,0xdc02,0x6b,0x331,0x1e36,0xe643,0x4c,0x323, +0x304,0x1e37,0xe643,0x6c,0x323,0x304,0xdc02,0x4c,0x331,0xdc02,0x6c,0x331,0xdc02,0x4c,0x32d,0xdc02, +0x6c,0x32d,0xe602,0x4d,0x301,0xe602,0x6d,0x301,0xe602,0x4d,0x307,0xe602,0x6d,0x307,0xdc02,0x4d, +0x323,0xdc02,0x6d,0x323,0xe602,0x4e,0x307,0xe602,0x6e,0x307,0xdc02,0x4e,0x323,0xdc02,0x6e,0x323, +0xdc02,0x4e,0x331,0xdc02,0x6e,0x331,0xdc02,0x4e,0x32d,0xdc02,0x6e,0x32d,0xd5,0xe643,0x4f,0x303, +0x301,0xf5,0xe643,0x6f,0x303,0x301,0xd5,0xe643,0x4f,0x303,0x308,0xf5,0xe643,0x6f,0x303,0x308, +0x14c,0xe643,0x4f,0x304,0x300,0x14d,0xe643,0x6f,0x304,0x300,0x14c,0xe643,0x4f,0x304,0x301,0x14d, +0xe643,0x6f,0x304,0x301,0xe602,0x50,0x301,0xe602,0x70,0x301,0xe602,0x50,0x307,0xe602,0x70,0x307, +0xe602,0x52,0x307,0xe602,0x72,0x307,0x1e5a,0xe643,0x52,0x323,0x304,0x1e5b,0xe643,0x72,0x323,0x304, +0xdc02,0x52,0x331,0xdc02,0x72,0x331,0xe602,0x53,0x307,0xe602,0x73,0x307,0x15a,0xe643,0x53,0x301, +0x307,0x15b,0xe643,0x73,0x301,0x307,0x160,0xe643,0x53,0x30c,0x307,0x161,0xe643,0x73,0x30c,0x307, +0x1e62,0xe643,0x53,0x323,0x307,0x1e63,0xe643,0x73,0x323,0x307,0xe602,0x54,0x307,0xe602,0x74,0x307, +0xdc02,0x54,0x323,0xdc02,0x74,0x323,0xdc02,0x54,0x331,0xdc02,0x74,0x331,0xdc02,0x54,0x32d,0xdc02, +0x74,0x32d,0xdc02,0x55,0x324,0xdc02,0x75,0x324,0xdc02,0x55,0x330,0xdc02,0x75,0x330,0xdc02,0x55, +0x32d,0xdc02,0x75,0x32d,0x168,0xe643,0x55,0x303,0x301,0x169,0xe643,0x75,0x303,0x301,0x16a,0xe643, +0x55,0x304,0x308,0x16b,0xe643,0x75,0x304,0x308,0xe602,0x56,0x303,0xe602,0x76,0x303,0xdc02,0x56, +0x323,0xdc02,0x76,0x323,0xe602,0x57,0x300,0xe602,0x77,0x300,0xe602,0x57,0x301,0xe602,0x77,0x301, +0xe602,0x57,0x308,0xe602,0x77,0x308,0xe602,0x57,0x307,0xe602,0x77,0x307,0xdc02,0x57,0x323,0xdc02, +0x77,0x323,0xe602,0x58,0x307,0xe602,0x78,0x307,0xe602,0x58,0x308,0xe602,0x78,0x308,0xe602,0x59, +0x307,0xe602,0x79,0x307,0xe602,0x5a,0x302,0xe602,0x7a,0x302,0xdc02,0x5a,0x323,0xdc02,0x7a,0x323, +0xdc02,0x5a,0x331,0xdc02,0x7a,0x331,0xdc02,0x68,0x331,0xe602,0x74,0x308,0xe602,0x77,0x30a,0xe602, +0x79,0x30a,0xe602,0x17f,0x307,0xe602,0x41,0x309,0xe602,0x61,0x309,0xc2,0xe643,0x41,0x302,0x301, +0xe2,0xe643,0x61,0x302,0x301,0xc2,0xe643,0x41,0x302,0x300,0xe2,0xe643,0x61,0x302,0x300,0xc2, +0xe643,0x41,0x302,0x309,0xe2,0xe643,0x61,0x302,0x309,0xc2,0xe643,0x41,0x302,0x303,0xe2,0xe643, +0x61,0x302,0x303,0x1ea0,0xe643,0x41,0x323,0x302,0x1ea1,0xe643,0x61,0x323,0x302,0x102,0xe643,0x41, +0x306,0x301,0x103,0xe643,0x61,0x306,0x301,0x102,0xe643,0x41,0x306,0x300,0x103,0xe643,0x61,0x306, +0x300,0x102,0xe643,0x41,0x306,0x309,0x103,0xe643,0x61,0x306,0x309,0x102,0xe643,0x41,0x306,0x303, +0x103,0xe643,0x61,0x306,0x303,0x1ea0,0xe643,0x41,0x323,0x306,0x1ea1,0xe643,0x61,0x323,0x306,0xe602, +0x45,0x309,0xe602,0x65,0x309,0xe602,0x45,0x303,0xe602,0x65,0x303,0xca,0xe643,0x45,0x302,0x301, +0xea,0xe643,0x65,0x302,0x301,0xca,0xe643,0x45,0x302,0x300,0xea,0xe643,0x65,0x302,0x300,0xca, +0xe643,0x45,0x302,0x309,0xea,0xe643,0x65,0x302,0x309,0xca,0xe643,0x45,0x302,0x303,0xea,0xe643, +0x65,0x302,0x303,0x1eb8,0xe643,0x45,0x323,0x302,0x1eb9,0xe643,0x65,0x323,0x302,0xe602,0x49,0x309, +0xe602,0x69,0x309,0xdc02,0x49,0x323,0xdc02,0x69,0x323,0xe602,0x4f,0x309,0xe602,0x6f,0x309,0xd4, +0xe643,0x4f,0x302,0x301,0xf4,0xe643,0x6f,0x302,0x301,0xd4,0xe643,0x4f,0x302,0x300,0xf4,0xe643, +0x6f,0x302,0x300,0xd4,0xe643,0x4f,0x302,0x309,0xf4,0xe643,0x6f,0x302,0x309,0xd4,0xe643,0x4f, +0x302,0x303,0xf4,0xe643,0x6f,0x302,0x303,0x1ecc,0xe643,0x4f,0x323,0x302,0x1ecd,0xe643,0x6f,0x323, +0x302,0x1a0,0xe643,0x4f,0x31b,0x301,0x1a1,0xe643,0x6f,0x31b,0x301,0x1a0,0xe643,0x4f,0x31b,0x300, +0x1a1,0xe643,0x6f,0x31b,0x300,0x1a0,0xe643,0x4f,0x31b,0x309,0x1a1,0xe643,0x6f,0x31b,0x309,0x1a0, +0xe643,0x4f,0x31b,0x303,0x1a1,0xe643,0x6f,0x31b,0x303,0x1a0,0xdc43,0x4f,0x31b,0x323,0x1a1,0xdc43, +0x6f,0x31b,0x323,0xdc02,0x55,0x323,0xdc02,0x75,0x323,0xe602,0x55,0x309,0xe602,0x75,0x309,0x1af, +0xe643,0x55,0x31b,0x301,0x1b0,0xe643,0x75,0x31b,0x301,0x1af,0xe643,0x55,0x31b,0x300,0x1b0,0xe643, +0x75,0x31b,0x300,0x1af,0xe643,0x55,0x31b,0x309,0x1b0,0xe643,0x75,0x31b,0x309,0x1af,0xe643,0x55, +0x31b,0x303,0x1b0,0xe643,0x75,0x31b,0x303,0x1af,0xdc43,0x55,0x31b,0x323,0x1b0,0xdc43,0x75,0x31b, +0x323,0xe602,0x59,0x300,0xe602,0x79,0x300,0xdc02,0x59,0x323,0xdc02,0x79,0x323,0xe602,0x59,0x309, +0xe602,0x79,0x309,0xe602,0x59,0x303,0xe602,0x79,0x303,0x1f10,0xe643,0x3b5,0x313,0x300,0x1f11,0xe643, +0x3b5,0x314,0x300,0x1f10,0xe643,0x3b5,0x313,0x301,0x1f11,0xe643,0x3b5,0x314,0x301,0x1f18,0xe643,0x395, +0x313,0x300,0x1f19,0xe643,0x395,0x314,0x300,0x1f18,0xe643,0x395,0x313,0x301,0x1f19,0xe643,0x395,0x314, +0x301,0x1f30,0xe643,0x3b9,0x313,0x300,0x1f31,0xe643,0x3b9,0x314,0x300,0x1f30,0xe643,0x3b9,0x313,0x301, +0x1f31,0xe643,0x3b9,0x314,0x301,0x1f30,0xe643,0x3b9,0x313,0x342,0x1f31,0xe643,0x3b9,0x314,0x342,0x1f38, +0xe643,0x399,0x313,0x300,0x1f39,0xe643,0x399,0x314,0x300,0x1f38,0xe643,0x399,0x313,0x301,0x1f39,0xe643, +0x399,0x314,0x301,0x1f38,0xe643,0x399,0x313,0x342,0x1f39,0xe643,0x399,0x314,0x342,0x1f40,0xe643,0x3bf, +0x313,0x300,0x1f41,0xe643,0x3bf,0x314,0x300,0x1f40,0xe643,0x3bf,0x313,0x301,0x1f41,0xe643,0x3bf,0x314, +0x301,0x1f48,0xe643,0x39f,0x313,0x300,0x1f49,0xe643,0x39f,0x314,0x300,0x1f48,0xe643,0x39f,0x313,0x301, +0x1f49,0xe643,0x39f,0x314,0x301,0x1f50,0xe643,0x3c5,0x313,0x300,0x1f51,0xe643,0x3c5,0x314,0x300,0x1f50, +0xe643,0x3c5,0x313,0x301,0x1f51,0xe643,0x3c5,0x314,0x301,0x1f50,0xe643,0x3c5,0x313,0x342,0x1f51,0xe643, +0x3c5,0x314,0x342,0x1f59,0xe643,0x3a5,0x314,0x300,0x1f59,0xe643,0x3a5,0x314,0x301,0x1f59,0xe643,0x3a5, +0x314,0x342,0xe602,0x3b5,0x300,0xe602,0x3b9,0x300,0xe602,0x3bf,0x300,0xe602,0x3c5,0x300,0x1f00,0xf043, +0x3b1,0x313,0x345,0x1f01,0xf043,0x3b1,0x314,0x345,0x1f02,0x345,2,0xf044,0x3b1,0x313,0x300,0x345, +0x1f03,0x345,2,0xf044,0x3b1,0x314,0x300,0x345,0x1f04,0x345,2,0xf044,0x3b1,0x313,0x301,0x345, +0x1f05,0x345,2,0xf044,0x3b1,0x314,0x301,0x345,0x1f06,0x345,2,0xf044,0x3b1,0x313,0x342,0x345, +0x1f07,0x345,2,0xf044,0x3b1,0x314,0x342,0x345,0x1f08,0xf043,0x391,0x313,0x345,0x1f09,0xf043,0x391, +0x314,0x345,0x1f0a,0x345,2,0xf044,0x391,0x313,0x300,0x345,0x1f0b,0x345,2,0xf044,0x391,0x314, +0x300,0x345,0x1f0c,0x345,2,0xf044,0x391,0x313,0x301,0x345,0x1f0d,0x345,2,0xf044,0x391,0x314, +0x301,0x345,0x1f0e,0x345,2,0xf044,0x391,0x313,0x342,0x345,0x1f0f,0x345,2,0xf044,0x391,0x314, +0x342,0x345,0x1f20,0xf043,0x3b7,0x313,0x345,0x1f21,0xf043,0x3b7,0x314,0x345,0x1f22,0x345,2,0xf044, +0x3b7,0x313,0x300,0x345,0x1f23,0x345,2,0xf044,0x3b7,0x314,0x300,0x345,0x1f24,0x345,2,0xf044, +0x3b7,0x313,0x301,0x345,0x1f25,0x345,2,0xf044,0x3b7,0x314,0x301,0x345,0x1f26,0x345,2,0xf044, +0x3b7,0x313,0x342,0x345,0x1f27,0x345,2,0xf044,0x3b7,0x314,0x342,0x345,0x1f28,0xf043,0x397,0x313, +0x345,0x1f29,0xf043,0x397,0x314,0x345,0x1f2a,0x345,2,0xf044,0x397,0x313,0x300,0x345,0x1f2b,0x345, +2,0xf044,0x397,0x314,0x300,0x345,0x1f2c,0x345,2,0xf044,0x397,0x313,0x301,0x345,0x1f2d,0x345, +2,0xf044,0x397,0x314,0x301,0x345,0x1f2e,0x345,2,0xf044,0x397,0x313,0x342,0x345,0x1f2f,0x345, +2,0xf044,0x397,0x314,0x342,0x345,0x1f60,0xf043,0x3c9,0x313,0x345,0x1f61,0xf043,0x3c9,0x314,0x345, +0x1f62,0x345,2,0xf044,0x3c9,0x313,0x300,0x345,0x1f63,0x345,2,0xf044,0x3c9,0x314,0x300,0x345, +0x1f64,0x345,2,0xf044,0x3c9,0x313,0x301,0x345,0x1f65,0x345,2,0xf044,0x3c9,0x314,0x301,0x345, +0x1f66,0x345,2,0xf044,0x3c9,0x313,0x342,0x345,0x1f67,0x345,2,0xf044,0x3c9,0x314,0x342,0x345, +0x1f68,0xf043,0x3a9,0x313,0x345,0x1f69,0xf043,0x3a9,0x314,0x345,0x1f6a,0x345,2,0xf044,0x3a9,0x313, +0x300,0x345,0x1f6b,0x345,2,0xf044,0x3a9,0x314,0x300,0x345,0x1f6c,0x345,2,0xf044,0x3a9,0x313, +0x301,0x345,0x1f6d,0x345,2,0xf044,0x3a9,0x314,0x301,0x345,0x1f6e,0x345,2,0xf044,0x3a9,0x313, +0x342,0x345,0x1f6f,0x345,2,0xf044,0x3a9,0x314,0x342,0x345,0xe602,0x3b1,0x306,0xe602,0x3b1,0x304, +0x1f70,0xf043,0x3b1,0x300,0x345,0xf002,0x3b1,0x345,0x3ac,0xf043,0x3b1,0x301,0x345,0x1fb6,0xf043,0x3b1, +0x342,0x345,0xe602,0x391,0x306,0xe602,0x391,0x304,0xe602,0x391,0x300,0xf002,0x391,0x345,0xe602,0xa8, +0x342,0x1f74,0xf043,0x3b7,0x300,0x345,0xf002,0x3b7,0x345,0x3ae,0xf043,0x3b7,0x301,0x345,0x1fc6,0xf043, +0x3b7,0x342,0x345,0xe602,0x395,0x300,0xe602,0x397,0x300,0xf002,0x397,0x345,0xe602,0x1fbf,0x300,0xe602, +0x1fbf,0x301,0xe602,0x1fbf,0x342,0xe602,0x3b9,0x306,0xe602,0x3b9,0x304,0x3ca,0xe643,0x3b9,0x308,0x300, +0xe602,0x3b9,0x342,0x3ca,0xe643,0x3b9,0x308,0x342,0xe602,0x399,0x306,0xe602,0x399,0x304,0xe602,0x399, +0x300,0xe602,0x1ffe,0x300,0xe602,0x1ffe,0x301,0xe602,0x1ffe,0x342,0xe602,0x3c5,0x306,0xe602,0x3c5,0x304, +0x3cb,0xe643,0x3c5,0x308,0x300,0xe602,0x3c1,0x313,0xe602,0x3c1,0x314,0xe602,0x3c5,0x342,0x3cb,0xe643, +0x3c5,0x308,0x342,0xe602,0x3a5,0x306,0xe602,0x3a5,0x304,0xe602,0x3a5,0x300,0xe602,0x3a1,0x314,0xe602, +0xa8,0x300,0x1f7c,0xf043,0x3c9,0x300,0x345,0xf002,0x3c9,0x345,0x3ce,0xf043,0x3c9,0x301,0x345,0x1ff6, +0xf043,0x3c9,0x342,0x345,0xe602,0x39f,0x300,0xe602,0x3a9,0x300,0xf002,0x3a9,0x345,0x102,0x2190,0x338, +0x102,0x2192,0x338,0x102,0x2194,0x338,0x102,0x21d0,0x338,0x102,0x21d4,0x338,0x102,0x21d2,0x338,0x102, +0x2203,0x338,0x102,0x2208,0x338,0x102,0x220b,0x338,0x102,0x2223,0x338,0x102,0x2225,0x338,0x102,0x223c, +0x338,0x102,0x2243,0x338,0x102,0x2245,0x338,0x102,0x2248,0x338,0x102,0x3d,0x338,0x102,0x2261,0x338, +0x102,0x224d,0x338,0x102,0x3c,0x338,0x102,0x3e,0x338,0x102,0x2264,0x338,0x102,0x2265,0x338,0x102, +0x2272,0x338,0x102,0x2273,0x338,0x102,0x2276,0x338,0x102,0x2277,0x338,0x102,0x227a,0x338,0x102,0x227b, +0x338,0x102,0x2282,0x338,0x102,0x2283,0x338,0x102,0x2286,0x338,0x102,0x2287,0x338,0x102,0x22a2,0x338, +0x102,0x22a8,0x338,0x102,0x22a9,0x338,0x102,0x22ab,0x338,0x102,0x227c,0x338,0x102,0x227d,0x338,0x102, +0x2291,0x338,0x102,0x2292,0x338,0x102,0x22b2,0x338,0x102,0x22b3,0x338,0x102,0x22b4,0x338,0x102,0x22b5, +0x338,0x802,0x304b,0x3099,0x802,0x304d,0x3099,0x802,0x304f,0x3099,0x802,0x3051,0x3099,0x802,0x3053,0x3099, +0x802,0x3055,0x3099,0x802,0x3057,0x3099,0x802,0x3059,0x3099,0x802,0x305b,0x3099,0x802,0x305d,0x3099,0x802, +0x305f,0x3099,0x802,0x3061,0x3099,0x802,0x3064,0x3099,0x802,0x3066,0x3099,0x802,0x3068,0x3099,0x802,0x306f, +0x3099,0x802,0x306f,0x309a,0x802,0x3072,0x3099,0x802,0x3072,0x309a,0x802,0x3075,0x3099,0x802,0x3075,0x309a, +0x802,0x3078,0x3099,0x802,0x3078,0x309a,0x802,0x307b,0x3099,0x802,0x307b,0x309a,0x802,0x3046,0x3099,0x802, +0x309d,0x3099,0x802,0x30ab,0x3099,0x802,0x30ad,0x3099,0x802,0x30af,0x3099,0x802,0x30b1,0x3099,0x802,0x30b3, +0x3099,0x802,0x30b5,0x3099,0x802,0x30b7,0x3099,0x802,0x30b9,0x3099,0x802,0x30bb,0x3099,0x802,0x30bd,0x3099, +0x802,0x30bf,0x3099,0x802,0x30c1,0x3099,0x802,0x30c4,0x3099,0x802,0x30c6,0x3099,0x802,0x30c8,0x3099,0x802, +0x30cf,0x3099,0x802,0x30cf,0x309a,0x802,0x30d2,0x3099,0x802,0x30d2,0x309a,0x802,0x30d5,0x3099,0x802,0x30d5, +0x309a,0x802,0x30d8,0x3099,0x802,0x30d8,0x309a,0x802,0x30db,0x3099,0x802,0x30db,0x309a,0x802,0x30a6,0x3099, +0x802,0x30ef,0x3099,0x802,0x30f0,0x3099,0x802,0x30f1,0x3099,0x802,0x30f2,0x3099,0x802,0x30fd,0x3099,0x704, +0xd804,0xdc99,0xd804,0xdcba,0x704,0xd804,0xdc9b,0xd804,0xdcba,0x704,0xd804,0xdca5,0xd804,0xdcba,4,0xd804, +0xdd31,0xd804,0xdd27,4,0xd804,0xdd32,0xd804,0xdd27,4,0xd804,0xdf47,0xd804,0xdf3e,4,0xd804,0xdf47, +0xd804,0xdf57,4,0xd805,0xdcb9,0xd805,0xdcba,4,0xd805,0xdcb9,0xd805,0xdcb0,4,0xd805,0xdcb9,0xd805, +0xdcbd,4,0xd805,0xddb8,0xd805,0xddaf,4,0xd805,0xddb9,0xd805,0xddaf,4,0xd806,0xdd35,0xd806,0xdd30, +1,0x2b9,1,0x3b,1,0xb7,0x702,0x915,0x93c,0x702,0x916,0x93c,0x702,0x917,0x93c,0x702, +0x91c,0x93c,0x702,0x921,0x93c,0x702,0x922,0x93c,0x702,0x92b,0x93c,0x702,0x92f,0x93c,0x702,0x9a1, +0x9bc,0x702,0x9a2,0x9bc,0x702,0x9af,0x9bc,0x702,0xa32,0xa3c,0x702,0xa38,0xa3c,0x702,0xa16,0xa3c, +0x702,0xa17,0xa3c,0x702,0xa1c,0xa3c,0x702,0xa2b,0xa3c,0x702,0xb21,0xb3c,0x702,0xb22,0xb3c,2, +0xf42,0xfb7,2,0xf4c,0xfb7,2,0xf51,0xfb7,2,0xf56,0xfb7,2,0xf5b,0xfb7,2,0xf40, +0xfb5,0x8202,0xfb2,0xf80,0x8202,0xfb3,0xf80,2,0xf92,0xfb7,2,0xf9c,0xfb7,2,0xfa1,0xfb7, +2,0xfa6,0xfb7,2,0xfab,0xfb7,2,0xf90,0xfb5,1,0x3b9,1,0x60,1,0xb4,1, +0x3a9,1,0x4b,1,0x3008,1,0x3009,0x102,0x2add,0x338,1,0x8c48,1,0x66f4,1,0x8eca, +1,0x8cc8,1,0x6ed1,1,0x4e32,1,0x53e5,1,0x9f9c,1,0x5951,1,0x91d1,1,0x5587, +1,0x5948,1,0x61f6,1,0x7669,1,0x7f85,1,0x863f,1,0x87ba,1,0x88f8,1,0x908f, +1,0x6a02,1,0x6d1b,1,0x70d9,1,0x73de,1,0x843d,1,0x916a,1,0x99f1,1,0x4e82, +1,0x5375,1,0x6b04,1,0x721b,1,0x862d,1,0x9e1e,1,0x5d50,1,0x6feb,1,0x85cd, +1,0x8964,1,0x62c9,1,0x81d8,1,0x881f,1,0x5eca,1,0x6717,1,0x6d6a,1,0x72fc, +1,0x90ce,1,0x4f86,1,0x51b7,1,0x52de,1,0x64c4,1,0x6ad3,1,0x7210,1,0x76e7, +1,0x8001,1,0x8606,1,0x865c,1,0x8def,1,0x9732,1,0x9b6f,1,0x9dfa,1,0x788c, +1,0x797f,1,0x7da0,1,0x83c9,1,0x9304,1,0x9e7f,1,0x8ad6,1,0x58df,1,0x5f04, +1,0x7c60,1,0x807e,1,0x7262,1,0x78ca,1,0x8cc2,1,0x96f7,1,0x58d8,1,0x5c62, +1,0x6a13,1,0x6dda,1,0x6f0f,1,0x7d2f,1,0x7e37,1,0x964b,1,0x52d2,1,0x808b, +1,0x51dc,1,0x51cc,1,0x7a1c,1,0x7dbe,1,0x83f1,1,0x9675,1,0x8b80,1,0x62cf, +1,0x8afe,1,0x4e39,1,0x5be7,1,0x6012,1,0x7387,1,0x7570,1,0x5317,1,0x78fb, +1,0x4fbf,1,0x5fa9,1,0x4e0d,1,0x6ccc,1,0x6578,1,0x7d22,1,0x53c3,1,0x585e, +1,0x7701,1,0x8449,1,0x8aaa,1,0x6bba,1,0x8fb0,1,0x6c88,1,0x62fe,1,0x82e5, +1,0x63a0,1,0x7565,1,0x4eae,1,0x5169,1,0x51c9,1,0x6881,1,0x7ce7,1,0x826f, +1,0x8ad2,1,0x91cf,1,0x52f5,1,0x5442,1,0x5973,1,0x5eec,1,0x65c5,1,0x6ffe, +1,0x792a,1,0x95ad,1,0x9a6a,1,0x9e97,1,0x9ece,1,0x529b,1,0x66c6,1,0x6b77, +1,0x8f62,1,0x5e74,1,0x6190,1,0x6200,1,0x649a,1,0x6f23,1,0x7149,1,0x7489, +1,0x79ca,1,0x7df4,1,0x806f,1,0x8f26,1,0x84ee,1,0x9023,1,0x934a,1,0x5217, +1,0x52a3,1,0x54bd,1,0x70c8,1,0x88c2,1,0x5ec9,1,0x5ff5,1,0x637b,1,0x6bae, +1,0x7c3e,1,0x7375,1,0x4ee4,1,0x56f9,1,0x5dba,1,0x601c,1,0x73b2,1,0x7469, +1,0x7f9a,1,0x8046,1,0x9234,1,0x96f6,1,0x9748,1,0x9818,1,0x4f8b,1,0x79ae, +1,0x91b4,1,0x96b8,1,0x60e1,1,0x4e86,1,0x50da,1,0x5bee,1,0x5c3f,1,0x6599, +1,0x71ce,1,0x7642,1,0x84fc,1,0x907c,1,0x9f8d,1,0x6688,1,0x962e,1,0x5289, +1,0x677b,1,0x67f3,1,0x6d41,1,0x6e9c,1,0x7409,1,0x7559,1,0x786b,1,0x7d10, +1,0x985e,1,0x516d,1,0x622e,1,0x9678,1,0x502b,1,0x5d19,1,0x6dea,1,0x8f2a, +1,0x5f8b,1,0x6144,1,0x6817,1,0x9686,1,0x5229,1,0x540f,1,0x5c65,1,0x6613, +1,0x674e,1,0x68a8,1,0x6ce5,1,0x7406,1,0x75e2,1,0x7f79,1,0x88cf,1,0x88e1, +1,0x91cc,1,0x96e2,1,0x533f,1,0x6eba,1,0x541d,1,0x71d0,1,0x7498,1,0x85fa, +1,0x96a3,1,0x9c57,1,0x9e9f,1,0x6797,1,0x6dcb,1,0x81e8,1,0x7acb,1,0x7b20, +1,0x7c92,1,0x72c0,1,0x7099,1,0x8b58,1,0x4ec0,1,0x8336,1,0x523a,1,0x5207, +1,0x5ea6,1,0x62d3,1,0x7cd6,1,0x5b85,1,0x6d1e,1,0x66b4,1,0x8f3b,1,0x884c, +1,0x964d,1,0x898b,1,0x5ed3,1,0x5140,1,0x55c0,1,0x585a,1,0x6674,1,0x51de, +1,0x732a,1,0x76ca,1,0x793c,1,0x795e,1,0x7965,1,0x798f,1,0x9756,1,0x7cbe, +1,0x7fbd,1,0x8612,1,0x8af8,1,0x9038,1,0x90fd,1,0x98ef,1,0x98fc,1,0x9928, +1,0x9db4,1,0x90de,1,0x96b7,1,0x4fae,1,0x50e7,1,0x514d,1,0x52c9,1,0x52e4, +1,0x5351,1,0x559d,1,0x5606,1,0x5668,1,0x5840,1,0x58a8,1,0x5c64,1,0x5c6e, +1,0x6094,1,0x6168,1,0x618e,1,0x61f2,1,0x654f,1,0x65e2,1,0x6691,1,0x6885, +1,0x6d77,1,0x6e1a,1,0x6f22,1,0x716e,1,0x722b,1,0x7422,1,0x7891,1,0x793e, +1,0x7949,1,0x7948,1,0x7950,1,0x7956,1,0x795d,1,0x798d,1,0x798e,1,0x7a40, +1,0x7a81,1,0x7bc0,1,0x7e09,1,0x7e41,1,0x7f72,1,0x8005,1,0x81ed,1,0x8279, +1,0x8457,1,0x8910,1,0x8996,1,0x8b01,1,0x8b39,1,0x8cd3,1,0x8d08,1,0x8fb6, +1,0x96e3,1,0x97ff,1,0x983b,1,0x6075,2,0xd850,0xdeee,1,0x8218,1,0x4e26,1, +0x51b5,1,0x5168,1,0x4f80,1,0x5145,1,0x5180,1,0x52c7,1,0x52fa,1,0x5555,1, +0x5599,1,0x55e2,1,0x58b3,1,0x5944,1,0x5954,1,0x5a62,1,0x5b28,1,0x5ed2,1, +0x5ed9,1,0x5f69,1,0x5fad,1,0x60d8,1,0x614e,1,0x6108,1,0x6160,1,0x6234,1, +0x63c4,1,0x641c,1,0x6452,1,0x6556,1,0x671b,1,0x6756,1,0x6b79,1,0x6edb,1, +0x6ecb,1,0x701e,1,0x77a7,1,0x7235,1,0x72af,1,0x7471,1,0x7506,1,0x753b,1, +0x761d,1,0x761f,1,0x76db,1,0x76f4,1,0x774a,1,0x7740,1,0x78cc,1,0x7ab1,1, +0x7c7b,1,0x7d5b,1,0x7f3e,1,0x8352,1,0x83ef,1,0x8779,1,0x8941,1,0x8986,1, +0x8abf,1,0x8acb,1,0x8aed,1,0x8b8a,1,0x8f38,1,0x9072,1,0x9199,1,0x9276,1, +0x967c,1,0x97db,1,0x980b,1,0x9b12,2,0xd84a,0xdc4a,2,0xd84a,0xdc44,2,0xd84c,0xdfd5, +1,0x3b9d,1,0x4018,1,0x4039,2,0xd854,0xde49,2,0xd857,0xdcd0,2,0xd85f,0xded3,1, +0x9f43,1,0x9f8e,0xe02,0x5d9,0x5b4,0x1102,0x5f2,0x5b7,0x1802,0x5e9,0x5c1,0x1902,0x5e9,0x5c2,0xfb49, +0x1843,0x5e9,0x5bc,0x5c1,0xfb49,0x1943,0x5e9,0x5bc,0x5c2,0x1102,0x5d0,0x5b7,0x1202,0x5d0,0x5b8,0x1502, +0x5d0,0x5bc,0x1502,0x5d1,0x5bc,0x1502,0x5d2,0x5bc,0x1502,0x5d3,0x5bc,0x1502,0x5d4,0x5bc,0x1502,0x5d5, +0x5bc,0x1502,0x5d6,0x5bc,0x1502,0x5d8,0x5bc,0x1502,0x5d9,0x5bc,0x1502,0x5da,0x5bc,0x1502,0x5db,0x5bc, +0x1502,0x5dc,0x5bc,0x1502,0x5de,0x5bc,0x1502,0x5e0,0x5bc,0x1502,0x5e1,0x5bc,0x1502,0x5e3,0x5bc,0x1502, +0x5e4,0x5bc,0x1502,0x5e6,0x5bc,0x1502,0x5e7,0x5bc,0x1502,0x5e8,0x5bc,0x1502,0x5e9,0x5bc,0x1502,0x5ea, +0x5bc,0x1302,0x5d5,0x5b9,0x1702,0x5d1,0x5bf,0x1702,0x5db,0x5bf,0x1702,0x5e4,0x5bf,0xd804,0xd834,0xdd57, +0xd834,0xdd65,0xd804,0xd834,0xdd58,0xd834,0xdd65,0xd834,0xdd5f,0xd834,0xdd6e,4,0xd846,0xd834,0xdd58,0xd834, +0xdd65,0xd834,0xdd6e,0xd834,0xdd5f,0xd834,0xdd6f,4,0xd846,0xd834,0xdd58,0xd834,0xdd65,0xd834,0xdd6f,0xd834, +0xdd5f,0xd834,0xdd70,4,0xd846,0xd834,0xdd58,0xd834,0xdd65,0xd834,0xdd70,0xd834,0xdd5f,0xd834,0xdd71,4, +0xd846,0xd834,0xdd58,0xd834,0xdd65,0xd834,0xdd71,0xd834,0xdd5f,0xd834,0xdd72,4,0xd846,0xd834,0xdd58,0xd834, +0xdd65,0xd834,0xdd72,0xd804,0xd834,0xddb9,0xd834,0xdd65,0xd804,0xd834,0xddba,0xd834,0xdd65,0xd834,0xddbb,0xd834, +0xdd6e,4,0xd846,0xd834,0xddb9,0xd834,0xdd65,0xd834,0xdd6e,0xd834,0xddbc,0xd834,0xdd6e,4,0xd846,0xd834, +0xddba,0xd834,0xdd65,0xd834,0xdd6e,0xd834,0xddbb,0xd834,0xdd6f,4,0xd846,0xd834,0xddb9,0xd834,0xdd65,0xd834, +0xdd6f,0xd834,0xddbc,0xd834,0xdd6f,4,0xd846,0xd834,0xddba,0xd834,0xdd65,0xd834,0xdd6f,1,0x4e3d,1, +0x4e38,1,0x4e41,2,0xd840,0xdd22,1,0x4f60,1,0x4fbb,1,0x5002,1,0x507a,1,0x5099, +1,0x50cf,1,0x349e,2,0xd841,0xde3a,1,0x5154,1,0x5164,1,0x5177,2,0xd841,0xdd1c, +1,0x34b9,1,0x5167,1,0x518d,2,0xd841,0xdd4b,1,0x5197,1,0x51a4,1,0x4ecc,1, +0x51ac,2,0xd864,0xdddf,1,0x51f5,1,0x5203,1,0x34df,1,0x523b,1,0x5246,1,0x5272, +1,0x5277,1,0x3515,1,0x5305,1,0x5306,1,0x5349,1,0x535a,1,0x5373,1,0x537d, +1,0x537f,2,0xd842,0xde2c,1,0x7070,1,0x53ca,1,0x53df,2,0xd842,0xdf63,1,0x53eb, +1,0x53f1,1,0x5406,1,0x549e,1,0x5438,1,0x5448,1,0x5468,1,0x54a2,1,0x54f6, +1,0x5510,1,0x5553,1,0x5563,1,0x5584,1,0x55ab,1,0x55b3,1,0x55c2,1,0x5716, +1,0x5717,1,0x5651,1,0x5674,1,0x58ee,1,0x57ce,1,0x57f4,1,0x580d,1,0x578b, +1,0x5832,1,0x5831,1,0x58ac,2,0xd845,0xdce4,1,0x58f2,1,0x58f7,1,0x5906,1, +0x591a,1,0x5922,1,0x5962,2,0xd845,0xdea8,2,0xd845,0xdeea,1,0x59ec,1,0x5a1b,1, +0x5a27,1,0x59d8,1,0x5a66,1,0x36ee,1,0x36fc,1,0x5b08,1,0x5b3e,2,0xd846,0xddc8, +1,0x5bc3,1,0x5bd8,1,0x5bf3,2,0xd846,0xdf18,1,0x5bff,1,0x5c06,1,0x5f53,1, +0x5c22,1,0x3781,1,0x5c60,1,0x5cc0,1,0x5c8d,2,0xd847,0xdde4,1,0x5d43,2,0xd847, +0xdde6,1,0x5d6e,1,0x5d6b,1,0x5d7c,1,0x5de1,1,0x5de2,1,0x382f,1,0x5dfd,1, +0x5e28,1,0x5e3d,1,0x5e69,1,0x3862,2,0xd848,0xdd83,1,0x387c,1,0x5eb0,1,0x5eb3, +1,0x5eb6,2,0xd868,0xdf92,1,0x5efe,2,0xd848,0xdf31,1,0x8201,1,0x5f22,1,0x38c7, +2,0xd84c,0xdeb8,2,0xd858,0xddda,1,0x5f62,1,0x5f6b,1,0x38e3,1,0x5f9a,1,0x5fcd, +1,0x5fd7,1,0x5ff9,1,0x6081,1,0x393a,1,0x391c,2,0xd849,0xded4,1,0x60c7,1, +0x6148,1,0x614c,1,0x617a,1,0x61b2,1,0x61a4,1,0x61af,1,0x61de,1,0x6210,1, +0x621b,1,0x625d,1,0x62b1,1,0x62d4,1,0x6350,2,0xd84a,0xdf0c,1,0x633d,1,0x62fc, +1,0x6368,1,0x6383,1,0x63e4,2,0xd84a,0xdff1,1,0x6422,1,0x63c5,1,0x63a9,1, +0x3a2e,1,0x6469,1,0x647e,1,0x649d,1,0x6477,1,0x3a6c,1,0x656c,2,0xd84c,0xdc0a, +1,0x65e3,1,0x66f8,1,0x6649,1,0x3b19,1,0x3b08,1,0x3ae4,1,0x5192,1,0x5195, +1,0x6700,1,0x669c,1,0x80ad,1,0x43d9,1,0x6721,1,0x675e,1,0x6753,2,0xd84c, +0xdfc3,1,0x3b49,1,0x67fa,1,0x6785,1,0x6852,2,0xd84d,0xdc6d,1,0x688e,1,0x681f, +1,0x6914,1,0x6942,1,0x69a3,1,0x69ea,1,0x6aa8,2,0xd84d,0xdea3,1,0x6adb,1, +0x3c18,1,0x6b21,2,0xd84e,0xdca7,1,0x6b54,1,0x3c4e,1,0x6b72,1,0x6b9f,1,0x6bbb, +2,0xd84e,0xde8d,2,0xd847,0xdd0b,2,0xd84e,0xdefa,1,0x6c4e,2,0xd84f,0xdcbc,1,0x6cbf, +1,0x6ccd,1,0x6c67,1,0x6d16,1,0x6d3e,1,0x6d69,1,0x6d78,1,0x6d85,2,0xd84f, +0xdd1e,1,0x6d34,1,0x6e2f,1,0x6e6e,1,0x3d33,1,0x6ec7,2,0xd84f,0xded1,1,0x6df9, +1,0x6f6e,2,0xd84f,0xdf5e,2,0xd84f,0xdf8e,1,0x6fc6,1,0x7039,1,0x701b,1,0x3d96, +1,0x704a,1,0x707d,1,0x7077,1,0x70ad,2,0xd841,0xdd25,1,0x7145,2,0xd850,0xde63, +1,0x719c,2,0xd850,0xdfab,1,0x7228,1,0x7250,2,0xd851,0xde08,1,0x7280,1,0x7295, +2,0xd851,0xdf35,2,0xd852,0xdc14,1,0x737a,1,0x738b,1,0x3eac,1,0x73a5,1,0x3eb8, +1,0x7447,1,0x745c,1,0x7485,1,0x74ca,1,0x3f1b,1,0x7524,2,0xd853,0xdc36,1, +0x753e,2,0xd853,0xdc92,2,0xd848,0xdd9f,1,0x7610,2,0xd853,0xdfa1,2,0xd853,0xdfb8,2, +0xd854,0xdc44,1,0x3ffc,1,0x4008,2,0xd854,0xdcf3,2,0xd854,0xdcf2,2,0xd854,0xdd19,2, +0xd854,0xdd33,1,0x771e,1,0x771f,1,0x778b,1,0x4046,1,0x4096,2,0xd855,0xdc1d,1, +0x784e,1,0x40e3,2,0xd855,0xde26,2,0xd855,0xde9a,2,0xd855,0xdec5,1,0x79eb,1,0x412f, +1,0x7a4a,1,0x7a4f,2,0xd856,0xdd7c,2,0xd856,0xdea7,1,0x7aee,1,0x4202,2,0xd856, +0xdfab,1,0x7bc6,1,0x7bc9,1,0x4227,2,0xd857,0xdc80,1,0x7cd2,1,0x42a0,1,0x7ce8, +1,0x7ce3,1,0x7d00,2,0xd857,0xdf86,1,0x7d63,1,0x4301,1,0x7dc7,1,0x7e02,1, +0x7e45,1,0x4334,2,0xd858,0xde28,2,0xd858,0xde47,1,0x4359,2,0xd858,0xded9,1,0x7f7a, +2,0xd858,0xdf3e,1,0x7f95,1,0x7ffa,2,0xd859,0xdcda,2,0xd859,0xdd23,1,0x8060,2, +0xd859,0xdda8,1,0x8070,2,0xd84c,0xdf5f,1,0x43d5,1,0x80b2,1,0x8103,1,0x440b,1, +0x813e,1,0x5ab5,2,0xd859,0xdfa7,2,0xd859,0xdfb5,2,0xd84c,0xdf93,2,0xd84c,0xdf9c,1, +0x8204,1,0x8f9e,1,0x446b,1,0x8291,1,0x828b,1,0x829d,1,0x52b3,1,0x82b1,1, +0x82b3,1,0x82bd,1,0x82e6,2,0xd85a,0xdf3c,1,0x831d,1,0x8363,1,0x83ad,1,0x8323, +1,0x83bd,1,0x83e7,1,0x8353,1,0x83ca,1,0x83cc,1,0x83dc,2,0xd85b,0xdc36,2, +0xd85b,0xdd6b,2,0xd85b,0xdcd5,1,0x452b,1,0x84f1,1,0x84f3,1,0x8516,2,0xd85c,0xdfca, +1,0x8564,2,0xd85b,0xdf2c,1,0x455d,1,0x4561,2,0xd85b,0xdfb1,2,0xd85c,0xdcd2,1, +0x456b,1,0x8650,1,0x8667,1,0x8669,1,0x86a9,1,0x8688,1,0x870e,1,0x86e2,1, +0x8728,1,0x876b,1,0x8786,1,0x45d7,1,0x87e1,1,0x8801,1,0x45f9,1,0x8860,1, +0x8863,2,0xd85d,0xde67,1,0x88d7,1,0x88de,1,0x4635,1,0x88fa,1,0x34bb,2,0xd85e, +0xdcae,2,0xd85e,0xdd66,1,0x46be,1,0x46c7,1,0x8aa0,1,0x8c55,2,0xd85f,0xdca8,1, +0x8cab,1,0x8cc1,1,0x8d1b,1,0x8d77,2,0xd85f,0xdf2f,2,0xd842,0xdc04,1,0x8dcb,1, +0x8dbc,1,0x8df0,2,0xd842,0xdcde,1,0x8ed4,2,0xd861,0xddd2,2,0xd861,0xdded,1,0x9094, +1,0x90f1,1,0x9111,2,0xd861,0xdf2e,1,0x911b,1,0x9238,1,0x92d7,1,0x92d8,1, +0x927c,1,0x93f9,1,0x9415,2,0xd862,0xdffa,1,0x958b,1,0x4995,1,0x95b7,2,0xd863, +0xdd77,1,0x49e6,1,0x96c3,1,0x5db2,1,0x9723,2,0xd864,0xdd45,2,0xd864,0xde1a,1, +0x4a6e,1,0x4a76,1,0x97e0,2,0xd865,0xdc0a,1,0x4ab2,2,0xd865,0xdc96,1,0x9829,2, +0xd865,0xddb6,1,0x98e2,1,0x4b33,1,0x9929,1,0x99a7,1,0x99c2,1,0x99fe,1,0x4bce, +2,0xd866,0xdf30,1,0x9c40,1,0x9cfd,1,0x4cce,1,0x4ced,1,0x9d67,2,0xd868,0xdcce, +1,0x4cf8,2,0xd868,0xdd05,2,0xd868,0xde0e,2,0xd868,0xde91,1,0x9ebb,1,0x4d56,1, +0x9ef9,1,0x9efe,1,0x9f05,1,0x9f0f,1,0x9f16,1,0x9f3b,2,0xd869,0xde00,0x3ac,0xe642, +0x3b1,0x301,0x3ad,0xe642,0x3b5,0x301,0x3ae,0xe642,0x3b7,0x301,0x3af,0xe642,0x3b9,0x301,0x3cc,0xe642, +0x3bf,0x301,0x3cd,0xe642,0x3c5,0x301,0x3ce,0xe642,0x3c9,0x301,0x386,0xe642,0x391,0x301,0x388,0xe642, +0x395,0x301,0x389,0xe642,0x397,0x301,0x390,1,0xe643,0x3b9,0x308,0x301,0x38a,0xe642,0x399,0x301, +0x3b0,1,0xe643,0x3c5,0x308,0x301,0x38e,0xe642,0x3a5,0x301,0x385,0xe642,0xa8,0x301,0x38c,0xe642, +0x39f,0x301,0x38f,0xe642,0x3a9,0x301,0xc5,0xe642,0x41,0x30a,0xe6e6,0xe681,0x300,0xe6e6,0xe681,0x301, +0xe6e6,0xe681,0x313,0xe6e6,0xe682,0x308,0x301,0x8100,0x8282,0xf71,0xf72,0x8100,0x8482,0xf71,0xf74,0x8100, +0x8282,0xf71,0xf80,0 +}; + +static const uint8_t norm2_nfc_data_smallFCD[256]={ +0xc0,0xef,3,0x7f,0xdf,0x70,0xcf,0x87,0xd7,0xe6,0x66,0x46,0x66,0x46,0x66,0x5b, +0x12,0,0,4,0,0,0,0x43,0x20,2,0x69,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, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0x98,0,0xc3,0x66,0xe0,0x80,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +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,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,7,0,0,2,0 +}; + +#endif // INCLUDED_FROM_NORMALIZER2_CPP diff --git a/deps/icu-small/source/common/norm2allmodes.h b/deps/icu-small/source/common/norm2allmodes.h index 584835da57b6be..866fa14725907e 100644 --- a/deps/icu-small/source/common/norm2allmodes.h +++ b/deps/icu-small/source/common/norm2allmodes.h @@ -1,405 +1,405 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* norm2allmodes.h -* -* created on: 2014sep07 -* created by: Markus W. Scherer -*/ - -#ifndef __NORM2ALLMODES_H__ -#define __NORM2ALLMODES_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_NORMALIZATION - -#include "unicode/edits.h" -#include "unicode/normalizer2.h" -#include "unicode/stringoptions.h" -#include "unicode/unistr.h" -#include "cpputils.h" -#include "normalizer2impl.h" - -U_NAMESPACE_BEGIN - -// Intermediate class: -// Has Normalizer2Impl and does boilerplate argument checking and setup. -class Normalizer2WithImpl : public Normalizer2 { -public: - Normalizer2WithImpl(const Normalizer2Impl &ni) : impl(ni) {} - virtual ~Normalizer2WithImpl(); - - // normalize - virtual UnicodeString & - normalize(const UnicodeString &src, - UnicodeString &dest, - UErrorCode &errorCode) const U_OVERRIDE { - if(U_FAILURE(errorCode)) { - dest.setToBogus(); - return dest; - } - const UChar *sArray=src.getBuffer(); - if(&dest==&src || sArray==NULL) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - dest.setToBogus(); - return dest; - } - dest.remove(); - ReorderingBuffer buffer(impl, dest); - if(buffer.init(src.length(), errorCode)) { - normalize(sArray, sArray+src.length(), buffer, errorCode); - } - return dest; - } - virtual void - normalize(const UChar *src, const UChar *limit, - ReorderingBuffer &buffer, UErrorCode &errorCode) const = 0; - - // normalize and append - virtual UnicodeString & - normalizeSecondAndAppend(UnicodeString &first, - const UnicodeString &second, - UErrorCode &errorCode) const U_OVERRIDE { - return normalizeSecondAndAppend(first, second, true, errorCode); - } - virtual UnicodeString & - append(UnicodeString &first, - const UnicodeString &second, - UErrorCode &errorCode) const U_OVERRIDE { - return normalizeSecondAndAppend(first, second, false, errorCode); - } - UnicodeString & - normalizeSecondAndAppend(UnicodeString &first, - const UnicodeString &second, - UBool doNormalize, - UErrorCode &errorCode) const { - uprv_checkCanGetBuffer(first, errorCode); - if(U_FAILURE(errorCode)) { - return first; - } - const UChar *secondArray=second.getBuffer(); - if(&first==&second || secondArray==NULL) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return first; - } - int32_t firstLength=first.length(); - UnicodeString safeMiddle; - { - ReorderingBuffer buffer(impl, first); - if(buffer.init(firstLength+second.length(), errorCode)) { - normalizeAndAppend(secondArray, secondArray+second.length(), doNormalize, - safeMiddle, buffer, errorCode); - } - } // The ReorderingBuffer destructor finalizes the first string. - if(U_FAILURE(errorCode)) { - // Restore the modified suffix of the first string. - first.replace(firstLength-safeMiddle.length(), 0x7fffffff, safeMiddle); - } - return first; - } - virtual void - normalizeAndAppend(const UChar *src, const UChar *limit, UBool doNormalize, - UnicodeString &safeMiddle, - ReorderingBuffer &buffer, UErrorCode &errorCode) const = 0; - virtual UBool - getDecomposition(UChar32 c, UnicodeString &decomposition) const U_OVERRIDE { - UChar buffer[4]; - int32_t length; - const UChar *d=impl.getDecomposition(c, buffer, length); - if(d==NULL) { - return false; - } - if(d==buffer) { - decomposition.setTo(buffer, length); // copy the string (Jamos from Hangul syllable c) - } else { - decomposition.setTo(false, d, length); // read-only alias - } - return true; - } - virtual UBool - getRawDecomposition(UChar32 c, UnicodeString &decomposition) const U_OVERRIDE { - UChar buffer[30]; - int32_t length; - const UChar *d=impl.getRawDecomposition(c, buffer, length); - if(d==NULL) { - return false; - } - if(d==buffer) { - decomposition.setTo(buffer, length); // copy the string (algorithmic decomposition) - } else { - decomposition.setTo(false, d, length); // read-only alias - } - return true; - } - virtual UChar32 - composePair(UChar32 a, UChar32 b) const U_OVERRIDE { - return impl.composePair(a, b); - } - - virtual uint8_t - getCombiningClass(UChar32 c) const U_OVERRIDE { - return impl.getCC(impl.getNorm16(c)); - } - - // quick checks - virtual UBool - isNormalized(const UnicodeString &s, UErrorCode &errorCode) const U_OVERRIDE { - if(U_FAILURE(errorCode)) { - return false; - } - const UChar *sArray=s.getBuffer(); - if(sArray==NULL) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - const UChar *sLimit=sArray+s.length(); - return sLimit==spanQuickCheckYes(sArray, sLimit, errorCode); - } - virtual UNormalizationCheckResult - quickCheck(const UnicodeString &s, UErrorCode &errorCode) const U_OVERRIDE { - return Normalizer2WithImpl::isNormalized(s, errorCode) ? UNORM_YES : UNORM_NO; - } - virtual int32_t - spanQuickCheckYes(const UnicodeString &s, UErrorCode &errorCode) const U_OVERRIDE { - if(U_FAILURE(errorCode)) { - return 0; - } - const UChar *sArray=s.getBuffer(); - if(sArray==NULL) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - return (int32_t)(spanQuickCheckYes(sArray, sArray+s.length(), errorCode)-sArray); - } - virtual const UChar * - spanQuickCheckYes(const UChar *src, const UChar *limit, UErrorCode &errorCode) const = 0; - - virtual UNormalizationCheckResult getQuickCheck(UChar32) const { - return UNORM_YES; - } - - const Normalizer2Impl &impl; -}; - -class DecomposeNormalizer2 : public Normalizer2WithImpl { -public: - DecomposeNormalizer2(const Normalizer2Impl &ni) : Normalizer2WithImpl(ni) {} - virtual ~DecomposeNormalizer2(); - -private: - virtual void - normalize(const UChar *src, const UChar *limit, - ReorderingBuffer &buffer, UErrorCode &errorCode) const U_OVERRIDE { - impl.decompose(src, limit, &buffer, errorCode); - } - using Normalizer2WithImpl::normalize; // Avoid warning about hiding base class function. - virtual void - normalizeAndAppend(const UChar *src, const UChar *limit, UBool doNormalize, - UnicodeString &safeMiddle, - ReorderingBuffer &buffer, UErrorCode &errorCode) const U_OVERRIDE { - impl.decomposeAndAppend(src, limit, doNormalize, safeMiddle, buffer, errorCode); - } - - void - normalizeUTF8(uint32_t options, StringPiece src, ByteSink &sink, - Edits *edits, UErrorCode &errorCode) const U_OVERRIDE { - if (U_FAILURE(errorCode)) { - return; - } - if (edits != nullptr && (options & U_EDITS_NO_RESET) == 0) { - edits->reset(); - } - const uint8_t *s = reinterpret_cast(src.data()); - impl.decomposeUTF8(options, s, s + src.length(), &sink, edits, errorCode); - sink.Flush(); - } - virtual UBool - isNormalizedUTF8(StringPiece sp, UErrorCode &errorCode) const U_OVERRIDE { - if(U_FAILURE(errorCode)) { - return false; - } - const uint8_t *s = reinterpret_cast(sp.data()); - const uint8_t *sLimit = s + sp.length(); - return sLimit == impl.decomposeUTF8(0, s, sLimit, nullptr, nullptr, errorCode); - } - - virtual const UChar * - spanQuickCheckYes(const UChar *src, const UChar *limit, UErrorCode &errorCode) const U_OVERRIDE { - return impl.decompose(src, limit, NULL, errorCode); - } - using Normalizer2WithImpl::spanQuickCheckYes; // Avoid warning about hiding base class function. - virtual UNormalizationCheckResult getQuickCheck(UChar32 c) const U_OVERRIDE { - return impl.isDecompYes(impl.getNorm16(c)) ? UNORM_YES : UNORM_NO; - } - virtual UBool hasBoundaryBefore(UChar32 c) const U_OVERRIDE { - return impl.hasDecompBoundaryBefore(c); - } - virtual UBool hasBoundaryAfter(UChar32 c) const U_OVERRIDE { - return impl.hasDecompBoundaryAfter(c); - } - virtual UBool isInert(UChar32 c) const U_OVERRIDE { - return impl.isDecompInert(c); - } -}; - -class ComposeNormalizer2 : public Normalizer2WithImpl { -public: - ComposeNormalizer2(const Normalizer2Impl &ni, UBool fcc) : - Normalizer2WithImpl(ni), onlyContiguous(fcc) {} - virtual ~ComposeNormalizer2(); - -private: - virtual void - normalize(const UChar *src, const UChar *limit, - ReorderingBuffer &buffer, UErrorCode &errorCode) const U_OVERRIDE { - impl.compose(src, limit, onlyContiguous, true, buffer, errorCode); - } - using Normalizer2WithImpl::normalize; // Avoid warning about hiding base class function. - - void - normalizeUTF8(uint32_t options, StringPiece src, ByteSink &sink, - Edits *edits, UErrorCode &errorCode) const U_OVERRIDE { - if (U_FAILURE(errorCode)) { - return; - } - if (edits != nullptr && (options & U_EDITS_NO_RESET) == 0) { - edits->reset(); - } - const uint8_t *s = reinterpret_cast(src.data()); - impl.composeUTF8(options, onlyContiguous, s, s + src.length(), - &sink, edits, errorCode); - sink.Flush(); - } - - virtual void - normalizeAndAppend(const UChar *src, const UChar *limit, UBool doNormalize, - UnicodeString &safeMiddle, - ReorderingBuffer &buffer, UErrorCode &errorCode) const U_OVERRIDE { - impl.composeAndAppend(src, limit, doNormalize, onlyContiguous, safeMiddle, buffer, errorCode); - } - - virtual UBool - isNormalized(const UnicodeString &s, UErrorCode &errorCode) const U_OVERRIDE { - if(U_FAILURE(errorCode)) { - return false; - } - const UChar *sArray=s.getBuffer(); - if(sArray==NULL) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - UnicodeString temp; - ReorderingBuffer buffer(impl, temp); - if(!buffer.init(5, errorCode)) { // small destCapacity for substring normalization - return false; - } - return impl.compose(sArray, sArray+s.length(), onlyContiguous, false, buffer, errorCode); - } - virtual UBool - isNormalizedUTF8(StringPiece sp, UErrorCode &errorCode) const U_OVERRIDE { - if(U_FAILURE(errorCode)) { - return false; - } - const uint8_t *s = reinterpret_cast(sp.data()); - return impl.composeUTF8(0, onlyContiguous, s, s + sp.length(), nullptr, nullptr, errorCode); - } - virtual UNormalizationCheckResult - quickCheck(const UnicodeString &s, UErrorCode &errorCode) const U_OVERRIDE { - if(U_FAILURE(errorCode)) { - return UNORM_MAYBE; - } - const UChar *sArray=s.getBuffer(); - if(sArray==NULL) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return UNORM_MAYBE; - } - UNormalizationCheckResult qcResult=UNORM_YES; - impl.composeQuickCheck(sArray, sArray+s.length(), onlyContiguous, &qcResult); - return qcResult; - } - virtual const UChar * - spanQuickCheckYes(const UChar *src, const UChar *limit, UErrorCode &) const U_OVERRIDE { - return impl.composeQuickCheck(src, limit, onlyContiguous, NULL); - } - using Normalizer2WithImpl::spanQuickCheckYes; // Avoid warning about hiding base class function. - virtual UNormalizationCheckResult getQuickCheck(UChar32 c) const U_OVERRIDE { - return impl.getCompQuickCheck(impl.getNorm16(c)); - } - virtual UBool hasBoundaryBefore(UChar32 c) const U_OVERRIDE { - return impl.hasCompBoundaryBefore(c); - } - virtual UBool hasBoundaryAfter(UChar32 c) const U_OVERRIDE { - return impl.hasCompBoundaryAfter(c, onlyContiguous); - } - virtual UBool isInert(UChar32 c) const U_OVERRIDE { - return impl.isCompInert(c, onlyContiguous); - } - - const UBool onlyContiguous; -}; - -class FCDNormalizer2 : public Normalizer2WithImpl { -public: - FCDNormalizer2(const Normalizer2Impl &ni) : Normalizer2WithImpl(ni) {} - virtual ~FCDNormalizer2(); - -private: - virtual void - normalize(const UChar *src, const UChar *limit, - ReorderingBuffer &buffer, UErrorCode &errorCode) const U_OVERRIDE { - impl.makeFCD(src, limit, &buffer, errorCode); - } - using Normalizer2WithImpl::normalize; // Avoid warning about hiding base class function. - virtual void - normalizeAndAppend(const UChar *src, const UChar *limit, UBool doNormalize, - UnicodeString &safeMiddle, - ReorderingBuffer &buffer, UErrorCode &errorCode) const U_OVERRIDE { - impl.makeFCDAndAppend(src, limit, doNormalize, safeMiddle, buffer, errorCode); - } - virtual const UChar * - spanQuickCheckYes(const UChar *src, const UChar *limit, UErrorCode &errorCode) const U_OVERRIDE { - return impl.makeFCD(src, limit, NULL, errorCode); - } - using Normalizer2WithImpl::spanQuickCheckYes; // Avoid warning about hiding base class function. - virtual UBool hasBoundaryBefore(UChar32 c) const U_OVERRIDE { - return impl.hasFCDBoundaryBefore(c); - } - virtual UBool hasBoundaryAfter(UChar32 c) const U_OVERRIDE { - return impl.hasFCDBoundaryAfter(c); - } - virtual UBool isInert(UChar32 c) const U_OVERRIDE { - return impl.isFCDInert(c); - } -}; - -struct Norm2AllModes : public UMemory { - Norm2AllModes(Normalizer2Impl *i) - : impl(i), comp(*i, false), decomp(*i), fcd(*i), fcc(*i, true) {} - ~Norm2AllModes(); - - static Norm2AllModes *createInstance(Normalizer2Impl *impl, UErrorCode &errorCode); - static Norm2AllModes *createNFCInstance(UErrorCode &errorCode); - static Norm2AllModes *createInstance(const char *packageName, - const char *name, - UErrorCode &errorCode); - - static const Norm2AllModes *getNFCInstance(UErrorCode &errorCode); - static const Norm2AllModes *getNFKCInstance(UErrorCode &errorCode); - static const Norm2AllModes *getNFKC_CFInstance(UErrorCode &errorCode); - - Normalizer2Impl *impl; - ComposeNormalizer2 comp; - DecomposeNormalizer2 decomp; - FCDNormalizer2 fcd; - ComposeNormalizer2 fcc; -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_NORMALIZATION -#endif // __NORM2ALLMODES_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* norm2allmodes.h +* +* created on: 2014sep07 +* created by: Markus W. Scherer +*/ + +#ifndef __NORM2ALLMODES_H__ +#define __NORM2ALLMODES_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_NORMALIZATION + +#include "unicode/edits.h" +#include "unicode/normalizer2.h" +#include "unicode/stringoptions.h" +#include "unicode/unistr.h" +#include "cpputils.h" +#include "normalizer2impl.h" + +U_NAMESPACE_BEGIN + +// Intermediate class: +// Has Normalizer2Impl and does boilerplate argument checking and setup. +class Normalizer2WithImpl : public Normalizer2 { +public: + Normalizer2WithImpl(const Normalizer2Impl &ni) : impl(ni) {} + virtual ~Normalizer2WithImpl(); + + // normalize + virtual UnicodeString & + normalize(const UnicodeString &src, + UnicodeString &dest, + UErrorCode &errorCode) const override { + if(U_FAILURE(errorCode)) { + dest.setToBogus(); + return dest; + } + const char16_t *sArray=src.getBuffer(); + if(&dest==&src || sArray==nullptr) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + dest.setToBogus(); + return dest; + } + dest.remove(); + ReorderingBuffer buffer(impl, dest); + if(buffer.init(src.length(), errorCode)) { + normalize(sArray, sArray+src.length(), buffer, errorCode); + } + return dest; + } + virtual void + normalize(const char16_t *src, const char16_t *limit, + ReorderingBuffer &buffer, UErrorCode &errorCode) const = 0; + + // normalize and append + virtual UnicodeString & + normalizeSecondAndAppend(UnicodeString &first, + const UnicodeString &second, + UErrorCode &errorCode) const override { + return normalizeSecondAndAppend(first, second, true, errorCode); + } + virtual UnicodeString & + append(UnicodeString &first, + const UnicodeString &second, + UErrorCode &errorCode) const override { + return normalizeSecondAndAppend(first, second, false, errorCode); + } + UnicodeString & + normalizeSecondAndAppend(UnicodeString &first, + const UnicodeString &second, + UBool doNormalize, + UErrorCode &errorCode) const { + uprv_checkCanGetBuffer(first, errorCode); + if(U_FAILURE(errorCode)) { + return first; + } + const char16_t *secondArray=second.getBuffer(); + if(&first==&second || secondArray==nullptr) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return first; + } + int32_t firstLength=first.length(); + UnicodeString safeMiddle; + { + ReorderingBuffer buffer(impl, first); + if(buffer.init(firstLength+second.length(), errorCode)) { + normalizeAndAppend(secondArray, secondArray+second.length(), doNormalize, + safeMiddle, buffer, errorCode); + } + } // The ReorderingBuffer destructor finalizes the first string. + if(U_FAILURE(errorCode)) { + // Restore the modified suffix of the first string. + first.replace(firstLength-safeMiddle.length(), 0x7fffffff, safeMiddle); + } + return first; + } + virtual void + normalizeAndAppend(const char16_t *src, const char16_t *limit, UBool doNormalize, + UnicodeString &safeMiddle, + ReorderingBuffer &buffer, UErrorCode &errorCode) const = 0; + virtual UBool + getDecomposition(UChar32 c, UnicodeString &decomposition) const override { + char16_t buffer[4]; + int32_t length; + const char16_t *d=impl.getDecomposition(c, buffer, length); + if(d==nullptr) { + return false; + } + if(d==buffer) { + decomposition.setTo(buffer, length); // copy the string (Jamos from Hangul syllable c) + } else { + decomposition.setTo(false, d, length); // read-only alias + } + return true; + } + virtual UBool + getRawDecomposition(UChar32 c, UnicodeString &decomposition) const override { + char16_t buffer[30]; + int32_t length; + const char16_t *d=impl.getRawDecomposition(c, buffer, length); + if(d==nullptr) { + return false; + } + if(d==buffer) { + decomposition.setTo(buffer, length); // copy the string (algorithmic decomposition) + } else { + decomposition.setTo(false, d, length); // read-only alias + } + return true; + } + virtual UChar32 + composePair(UChar32 a, UChar32 b) const override { + return impl.composePair(a, b); + } + + virtual uint8_t + getCombiningClass(UChar32 c) const override { + return impl.getCC(impl.getNorm16(c)); + } + + // quick checks + virtual UBool + isNormalized(const UnicodeString &s, UErrorCode &errorCode) const override { + if(U_FAILURE(errorCode)) { + return false; + } + const char16_t *sArray=s.getBuffer(); + if(sArray==nullptr) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + const char16_t *sLimit=sArray+s.length(); + return sLimit==spanQuickCheckYes(sArray, sLimit, errorCode); + } + virtual UNormalizationCheckResult + quickCheck(const UnicodeString &s, UErrorCode &errorCode) const override { + return Normalizer2WithImpl::isNormalized(s, errorCode) ? UNORM_YES : UNORM_NO; + } + virtual int32_t + spanQuickCheckYes(const UnicodeString &s, UErrorCode &errorCode) const override { + if(U_FAILURE(errorCode)) { + return 0; + } + const char16_t *sArray=s.getBuffer(); + if(sArray==nullptr) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + return (int32_t)(spanQuickCheckYes(sArray, sArray+s.length(), errorCode)-sArray); + } + virtual const char16_t * + spanQuickCheckYes(const char16_t *src, const char16_t *limit, UErrorCode &errorCode) const = 0; + + virtual UNormalizationCheckResult getQuickCheck(UChar32) const { + return UNORM_YES; + } + + const Normalizer2Impl &impl; +}; + +class DecomposeNormalizer2 : public Normalizer2WithImpl { +public: + DecomposeNormalizer2(const Normalizer2Impl &ni) : Normalizer2WithImpl(ni) {} + virtual ~DecomposeNormalizer2(); + +private: + virtual void + normalize(const char16_t *src, const char16_t *limit, + ReorderingBuffer &buffer, UErrorCode &errorCode) const override { + impl.decompose(src, limit, &buffer, errorCode); + } + using Normalizer2WithImpl::normalize; // Avoid warning about hiding base class function. + virtual void + normalizeAndAppend(const char16_t *src, const char16_t *limit, UBool doNormalize, + UnicodeString &safeMiddle, + ReorderingBuffer &buffer, UErrorCode &errorCode) const override { + impl.decomposeAndAppend(src, limit, doNormalize, safeMiddle, buffer, errorCode); + } + + void + normalizeUTF8(uint32_t options, StringPiece src, ByteSink &sink, + Edits *edits, UErrorCode &errorCode) const override { + if (U_FAILURE(errorCode)) { + return; + } + if (edits != nullptr && (options & U_EDITS_NO_RESET) == 0) { + edits->reset(); + } + const uint8_t *s = reinterpret_cast(src.data()); + impl.decomposeUTF8(options, s, s + src.length(), &sink, edits, errorCode); + sink.Flush(); + } + virtual UBool + isNormalizedUTF8(StringPiece sp, UErrorCode &errorCode) const override { + if(U_FAILURE(errorCode)) { + return false; + } + const uint8_t *s = reinterpret_cast(sp.data()); + const uint8_t *sLimit = s + sp.length(); + return sLimit == impl.decomposeUTF8(0, s, sLimit, nullptr, nullptr, errorCode); + } + + virtual const char16_t * + spanQuickCheckYes(const char16_t *src, const char16_t *limit, UErrorCode &errorCode) const override { + return impl.decompose(src, limit, nullptr, errorCode); + } + using Normalizer2WithImpl::spanQuickCheckYes; // Avoid warning about hiding base class function. + virtual UNormalizationCheckResult getQuickCheck(UChar32 c) const override { + return impl.isDecompYes(impl.getNorm16(c)) ? UNORM_YES : UNORM_NO; + } + virtual UBool hasBoundaryBefore(UChar32 c) const override { + return impl.hasDecompBoundaryBefore(c); + } + virtual UBool hasBoundaryAfter(UChar32 c) const override { + return impl.hasDecompBoundaryAfter(c); + } + virtual UBool isInert(UChar32 c) const override { + return impl.isDecompInert(c); + } +}; + +class ComposeNormalizer2 : public Normalizer2WithImpl { +public: + ComposeNormalizer2(const Normalizer2Impl &ni, UBool fcc) : + Normalizer2WithImpl(ni), onlyContiguous(fcc) {} + virtual ~ComposeNormalizer2(); + +private: + virtual void + normalize(const char16_t *src, const char16_t *limit, + ReorderingBuffer &buffer, UErrorCode &errorCode) const override { + impl.compose(src, limit, onlyContiguous, true, buffer, errorCode); + } + using Normalizer2WithImpl::normalize; // Avoid warning about hiding base class function. + + void + normalizeUTF8(uint32_t options, StringPiece src, ByteSink &sink, + Edits *edits, UErrorCode &errorCode) const override { + if (U_FAILURE(errorCode)) { + return; + } + if (edits != nullptr && (options & U_EDITS_NO_RESET) == 0) { + edits->reset(); + } + const uint8_t *s = reinterpret_cast(src.data()); + impl.composeUTF8(options, onlyContiguous, s, s + src.length(), + &sink, edits, errorCode); + sink.Flush(); + } + + virtual void + normalizeAndAppend(const char16_t *src, const char16_t *limit, UBool doNormalize, + UnicodeString &safeMiddle, + ReorderingBuffer &buffer, UErrorCode &errorCode) const override { + impl.composeAndAppend(src, limit, doNormalize, onlyContiguous, safeMiddle, buffer, errorCode); + } + + virtual UBool + isNormalized(const UnicodeString &s, UErrorCode &errorCode) const override { + if(U_FAILURE(errorCode)) { + return false; + } + const char16_t *sArray=s.getBuffer(); + if(sArray==nullptr) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + UnicodeString temp; + ReorderingBuffer buffer(impl, temp); + if(!buffer.init(5, errorCode)) { // small destCapacity for substring normalization + return false; + } + return impl.compose(sArray, sArray+s.length(), onlyContiguous, false, buffer, errorCode); + } + virtual UBool + isNormalizedUTF8(StringPiece sp, UErrorCode &errorCode) const override { + if(U_FAILURE(errorCode)) { + return false; + } + const uint8_t *s = reinterpret_cast(sp.data()); + return impl.composeUTF8(0, onlyContiguous, s, s + sp.length(), nullptr, nullptr, errorCode); + } + virtual UNormalizationCheckResult + quickCheck(const UnicodeString &s, UErrorCode &errorCode) const override { + if(U_FAILURE(errorCode)) { + return UNORM_MAYBE; + } + const char16_t *sArray=s.getBuffer(); + if(sArray==nullptr) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return UNORM_MAYBE; + } + UNormalizationCheckResult qcResult=UNORM_YES; + impl.composeQuickCheck(sArray, sArray+s.length(), onlyContiguous, &qcResult); + return qcResult; + } + virtual const char16_t * + spanQuickCheckYes(const char16_t *src, const char16_t *limit, UErrorCode &) const override { + return impl.composeQuickCheck(src, limit, onlyContiguous, nullptr); + } + using Normalizer2WithImpl::spanQuickCheckYes; // Avoid warning about hiding base class function. + virtual UNormalizationCheckResult getQuickCheck(UChar32 c) const override { + return impl.getCompQuickCheck(impl.getNorm16(c)); + } + virtual UBool hasBoundaryBefore(UChar32 c) const override { + return impl.hasCompBoundaryBefore(c); + } + virtual UBool hasBoundaryAfter(UChar32 c) const override { + return impl.hasCompBoundaryAfter(c, onlyContiguous); + } + virtual UBool isInert(UChar32 c) const override { + return impl.isCompInert(c, onlyContiguous); + } + + const UBool onlyContiguous; +}; + +class FCDNormalizer2 : public Normalizer2WithImpl { +public: + FCDNormalizer2(const Normalizer2Impl &ni) : Normalizer2WithImpl(ni) {} + virtual ~FCDNormalizer2(); + +private: + virtual void + normalize(const char16_t *src, const char16_t *limit, + ReorderingBuffer &buffer, UErrorCode &errorCode) const override { + impl.makeFCD(src, limit, &buffer, errorCode); + } + using Normalizer2WithImpl::normalize; // Avoid warning about hiding base class function. + virtual void + normalizeAndAppend(const char16_t *src, const char16_t *limit, UBool doNormalize, + UnicodeString &safeMiddle, + ReorderingBuffer &buffer, UErrorCode &errorCode) const override { + impl.makeFCDAndAppend(src, limit, doNormalize, safeMiddle, buffer, errorCode); + } + virtual const char16_t * + spanQuickCheckYes(const char16_t *src, const char16_t *limit, UErrorCode &errorCode) const override { + return impl.makeFCD(src, limit, nullptr, errorCode); + } + using Normalizer2WithImpl::spanQuickCheckYes; // Avoid warning about hiding base class function. + virtual UBool hasBoundaryBefore(UChar32 c) const override { + return impl.hasFCDBoundaryBefore(c); + } + virtual UBool hasBoundaryAfter(UChar32 c) const override { + return impl.hasFCDBoundaryAfter(c); + } + virtual UBool isInert(UChar32 c) const override { + return impl.isFCDInert(c); + } +}; + +struct Norm2AllModes : public UMemory { + Norm2AllModes(Normalizer2Impl *i) + : impl(i), comp(*i, false), decomp(*i), fcd(*i), fcc(*i, true) {} + ~Norm2AllModes(); + + static Norm2AllModes *createInstance(Normalizer2Impl *impl, UErrorCode &errorCode); + static Norm2AllModes *createNFCInstance(UErrorCode &errorCode); + static Norm2AllModes *createInstance(const char *packageName, + const char *name, + UErrorCode &errorCode); + + static const Norm2AllModes *getNFCInstance(UErrorCode &errorCode); + static const Norm2AllModes *getNFKCInstance(UErrorCode &errorCode); + static const Norm2AllModes *getNFKC_CFInstance(UErrorCode &errorCode); + + Normalizer2Impl *impl; + ComposeNormalizer2 comp; + DecomposeNormalizer2 decomp; + FCDNormalizer2 fcd; + ComposeNormalizer2 fcc; +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_NORMALIZATION +#endif // __NORM2ALLMODES_H__ diff --git a/deps/icu-small/source/common/normalizer2.cpp b/deps/icu-small/source/common/normalizer2.cpp index 3617264490e2be..d2c4eaa43d762f 100644 --- a/deps/icu-small/source/common/normalizer2.cpp +++ b/deps/icu-small/source/common/normalizer2.cpp @@ -1,572 +1,572 @@ -// © 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. -* -******************************************************************************* -* file name: normalizer2.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2009nov22 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_NORMALIZATION - -#include "unicode/edits.h" -#include "unicode/normalizer2.h" -#include "unicode/stringoptions.h" -#include "unicode/unistr.h" -#include "unicode/unorm.h" -#include "cstring.h" -#include "mutex.h" -#include "norm2allmodes.h" -#include "normalizer2impl.h" -#include "uassert.h" -#include "ucln_cmn.h" - -using icu::Normalizer2Impl; - -#if NORM2_HARDCODE_NFC_DATA -// NFC/NFD data machine-generated by gennorm2 --csource -#define INCLUDED_FROM_NORMALIZER2_CPP -#include "norm2_nfc_data.h" -#endif - -U_NAMESPACE_BEGIN - -// Public API dispatch via Normalizer2 subclasses -------------------------- *** - -Normalizer2::~Normalizer2() {} - -void -Normalizer2::normalizeUTF8(uint32_t /*options*/, StringPiece src, ByteSink &sink, - Edits *edits, UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { - return; - } - if (edits != nullptr) { - errorCode = U_UNSUPPORTED_ERROR; - return; - } - UnicodeString src16 = UnicodeString::fromUTF8(src); - normalize(src16, errorCode).toUTF8(sink); -} - -UBool -Normalizer2::getRawDecomposition(UChar32, UnicodeString &) const { - return false; -} - -UChar32 -Normalizer2::composePair(UChar32, UChar32) const { - return U_SENTINEL; -} - -uint8_t -Normalizer2::getCombiningClass(UChar32 /*c*/) const { - return 0; -} - -UBool -Normalizer2::isNormalizedUTF8(StringPiece s, UErrorCode &errorCode) const { - return U_SUCCESS(errorCode) && isNormalized(UnicodeString::fromUTF8(s), errorCode); -} - -// Normalizer2 implementation for the old UNORM_NONE. -class NoopNormalizer2 : public Normalizer2 { - virtual ~NoopNormalizer2(); - - virtual UnicodeString & - normalize(const UnicodeString &src, - UnicodeString &dest, - UErrorCode &errorCode) const U_OVERRIDE { - if(U_SUCCESS(errorCode)) { - if(&dest!=&src) { - dest=src; - } else { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - } - } - return dest; - } - virtual void - normalizeUTF8(uint32_t options, StringPiece src, ByteSink &sink, - Edits *edits, UErrorCode &errorCode) const U_OVERRIDE { - if(U_SUCCESS(errorCode)) { - if (edits != nullptr) { - if ((options & U_EDITS_NO_RESET) == 0) { - edits->reset(); - } - edits->addUnchanged(src.length()); - } - if ((options & U_OMIT_UNCHANGED_TEXT) == 0) { - sink.Append(src.data(), src.length()); - } - sink.Flush(); - } - } - - virtual UnicodeString & - normalizeSecondAndAppend(UnicodeString &first, - const UnicodeString &second, - UErrorCode &errorCode) const U_OVERRIDE { - if(U_SUCCESS(errorCode)) { - if(&first!=&second) { - first.append(second); - } else { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - } - } - return first; - } - virtual UnicodeString & - append(UnicodeString &first, - const UnicodeString &second, - UErrorCode &errorCode) const U_OVERRIDE { - if(U_SUCCESS(errorCode)) { - if(&first!=&second) { - first.append(second); - } else { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - } - } - return first; - } - virtual UBool - getDecomposition(UChar32, UnicodeString &) const U_OVERRIDE { - return false; - } - // No need to U_OVERRIDE the default getRawDecomposition(). - virtual UBool - isNormalized(const UnicodeString &, UErrorCode &errorCode) const U_OVERRIDE { - return U_SUCCESS(errorCode); - } - virtual UBool - isNormalizedUTF8(StringPiece, UErrorCode &errorCode) const U_OVERRIDE { - return U_SUCCESS(errorCode); - } - virtual UNormalizationCheckResult - quickCheck(const UnicodeString &, UErrorCode &) const U_OVERRIDE { - return UNORM_YES; - } - virtual int32_t - spanQuickCheckYes(const UnicodeString &s, UErrorCode &) const U_OVERRIDE { - return s.length(); - } - virtual UBool hasBoundaryBefore(UChar32) const U_OVERRIDE { return true; } - virtual UBool hasBoundaryAfter(UChar32) const U_OVERRIDE { return true; } - virtual UBool isInert(UChar32) const U_OVERRIDE { return true; } -}; - -NoopNormalizer2::~NoopNormalizer2() {} - -Normalizer2WithImpl::~Normalizer2WithImpl() {} - -DecomposeNormalizer2::~DecomposeNormalizer2() {} - -ComposeNormalizer2::~ComposeNormalizer2() {} - -FCDNormalizer2::~FCDNormalizer2() {} - -// instance cache ---------------------------------------------------------- *** - -U_CDECL_BEGIN -static UBool U_CALLCONV uprv_normalizer2_cleanup(); -U_CDECL_END - -static Normalizer2 *noopSingleton; -static icu::UInitOnce noopInitOnce {}; - -static void U_CALLCONV initNoopSingleton(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return; - } - noopSingleton=new NoopNormalizer2; - if(noopSingleton==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - ucln_common_registerCleanup(UCLN_COMMON_NORMALIZER2, uprv_normalizer2_cleanup); -} - -const Normalizer2 *Normalizer2Factory::getNoopInstance(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return NULL; } - umtx_initOnce(noopInitOnce, &initNoopSingleton, errorCode); - return noopSingleton; -} - -const Normalizer2Impl * -Normalizer2Factory::getImpl(const Normalizer2 *norm2) { - return &((Normalizer2WithImpl *)norm2)->impl; -} - -Norm2AllModes::~Norm2AllModes() { - delete impl; -} - -Norm2AllModes * -Norm2AllModes::createInstance(Normalizer2Impl *impl, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - delete impl; - return NULL; - } - Norm2AllModes *allModes=new Norm2AllModes(impl); - if(allModes==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - delete impl; - return NULL; - } - return allModes; -} - -#if NORM2_HARDCODE_NFC_DATA -Norm2AllModes * -Norm2AllModes::createNFCInstance(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return NULL; - } - Normalizer2Impl *impl=new Normalizer2Impl; - if(impl==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - impl->init(norm2_nfc_data_indexes, &norm2_nfc_data_trie, - norm2_nfc_data_extraData, norm2_nfc_data_smallFCD); - return createInstance(impl, errorCode); -} - -static Norm2AllModes *nfcSingleton; - -static icu::UInitOnce nfcInitOnce {}; - -static void U_CALLCONV initNFCSingleton(UErrorCode &errorCode) { - nfcSingleton=Norm2AllModes::createNFCInstance(errorCode); - ucln_common_registerCleanup(UCLN_COMMON_NORMALIZER2, uprv_normalizer2_cleanup); -} - -const Norm2AllModes * -Norm2AllModes::getNFCInstance(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return NULL; } - umtx_initOnce(nfcInitOnce, &initNFCSingleton, errorCode); - return nfcSingleton; -} - -const Normalizer2 * -Normalizer2::getNFCInstance(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); - return allModes!=NULL ? &allModes->comp : NULL; -} - -const Normalizer2 * -Normalizer2::getNFDInstance(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); - return allModes!=NULL ? &allModes->decomp : NULL; -} - -const Normalizer2 *Normalizer2Factory::getFCDInstance(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); - return allModes!=NULL ? &allModes->fcd : NULL; -} - -const Normalizer2 *Normalizer2Factory::getFCCInstance(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); - return allModes!=NULL ? &allModes->fcc : NULL; -} - -const Normalizer2Impl * -Normalizer2Factory::getNFCImpl(UErrorCode &errorCode) { - const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); - return allModes!=NULL ? allModes->impl : NULL; -} -#endif // NORM2_HARDCODE_NFC_DATA - -U_CDECL_BEGIN - -static UBool U_CALLCONV uprv_normalizer2_cleanup() { - delete noopSingleton; - noopSingleton = NULL; - noopInitOnce.reset(); -#if NORM2_HARDCODE_NFC_DATA - delete nfcSingleton; - nfcSingleton = NULL; - nfcInitOnce.reset(); -#endif - return true; -} - -U_CDECL_END - -U_NAMESPACE_END - -// C API ------------------------------------------------------------------- *** - -U_NAMESPACE_USE - -U_CAPI const UNormalizer2 * U_EXPORT2 -unorm2_getNFCInstance(UErrorCode *pErrorCode) { - return (const UNormalizer2 *)Normalizer2::getNFCInstance(*pErrorCode); -} - -U_CAPI const UNormalizer2 * U_EXPORT2 -unorm2_getNFDInstance(UErrorCode *pErrorCode) { - return (const UNormalizer2 *)Normalizer2::getNFDInstance(*pErrorCode); -} - -U_CAPI void U_EXPORT2 -unorm2_close(UNormalizer2 *norm2) { - delete (Normalizer2 *)norm2; -} - -U_CAPI int32_t U_EXPORT2 -unorm2_normalize(const UNormalizer2 *norm2, - const UChar *src, int32_t length, - UChar *dest, int32_t capacity, - UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if( (src==NULL ? length!=0 : length<-1) || - (dest==NULL ? capacity!=0 : capacity<0) || - (src==dest && src!=NULL) - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString destString(dest, 0, capacity); - // length==0: Nothing to do, and n2wi->normalize(NULL, NULL, buffer, ...) would crash. - if(length!=0) { - const Normalizer2 *n2=(const Normalizer2 *)norm2; - const Normalizer2WithImpl *n2wi=dynamic_cast(n2); - if(n2wi!=NULL) { - // Avoid duplicate argument checking and support NUL-terminated src. - ReorderingBuffer buffer(n2wi->impl, destString); - if(buffer.init(length, *pErrorCode)) { - n2wi->normalize(src, length>=0 ? src+length : NULL, buffer, *pErrorCode); - } - } else { - UnicodeString srcString(length<0, src, length); - n2->normalize(srcString, destString, *pErrorCode); - } - } - return destString.extract(dest, capacity, *pErrorCode); -} - -static int32_t -normalizeSecondAndAppend(const UNormalizer2 *norm2, - UChar *first, int32_t firstLength, int32_t firstCapacity, - const UChar *second, int32_t secondLength, - UBool doNormalize, - UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if( (second==NULL ? secondLength!=0 : secondLength<-1) || - (first==NULL ? (firstCapacity!=0 || firstLength!=0) : - (firstCapacity<0 || firstLength<-1)) || - (first==second && first!=NULL) - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString firstString(first, firstLength, firstCapacity); - firstLength=firstString.length(); // In case it was -1. - // secondLength==0: Nothing to do, and n2wi->normalizeAndAppend(NULL, NULL, buffer, ...) would crash. - if(secondLength!=0) { - const Normalizer2 *n2=(const Normalizer2 *)norm2; - const Normalizer2WithImpl *n2wi=dynamic_cast(n2); - if(n2wi!=NULL) { - // Avoid duplicate argument checking and support NUL-terminated src. - UnicodeString safeMiddle; - { - ReorderingBuffer buffer(n2wi->impl, firstString); - if(buffer.init(firstLength+secondLength+1, *pErrorCode)) { // destCapacity>=-1 - n2wi->normalizeAndAppend(second, secondLength>=0 ? second+secondLength : NULL, - doNormalize, safeMiddle, buffer, *pErrorCode); - } - } // The ReorderingBuffer destructor finalizes firstString. - if(U_FAILURE(*pErrorCode) || firstString.length()>firstCapacity) { - // Restore the modified suffix of the first string. - // This does not restore first[] array contents between firstLength and firstCapacity. - // (That might be uninitialized memory, as far as we know.) - if(first!=NULL) { /* don't dereference NULL */ - safeMiddle.extract(0, 0x7fffffff, first+firstLength-safeMiddle.length()); - if(firstLengthnormalizeSecondAndAppend(firstString, secondString, *pErrorCode); - } else { - n2->append(firstString, secondString, *pErrorCode); - } - } - } - return firstString.extract(first, firstCapacity, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -unorm2_normalizeSecondAndAppend(const UNormalizer2 *norm2, - UChar *first, int32_t firstLength, int32_t firstCapacity, - const UChar *second, int32_t secondLength, - UErrorCode *pErrorCode) { - return normalizeSecondAndAppend(norm2, - first, firstLength, firstCapacity, - second, secondLength, - true, pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -unorm2_append(const UNormalizer2 *norm2, - UChar *first, int32_t firstLength, int32_t firstCapacity, - const UChar *second, int32_t secondLength, - UErrorCode *pErrorCode) { - return normalizeSecondAndAppend(norm2, - first, firstLength, firstCapacity, - second, secondLength, - false, pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -unorm2_getDecomposition(const UNormalizer2 *norm2, - UChar32 c, UChar *decomposition, int32_t capacity, - UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if(decomposition==NULL ? capacity!=0 : capacity<0) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString destString(decomposition, 0, capacity); - if(reinterpret_cast(norm2)->getDecomposition(c, destString)) { - return destString.extract(decomposition, capacity, *pErrorCode); - } else { - return -1; - } -} - -U_CAPI int32_t U_EXPORT2 -unorm2_getRawDecomposition(const UNormalizer2 *norm2, - UChar32 c, UChar *decomposition, int32_t capacity, - UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if(decomposition==NULL ? capacity!=0 : capacity<0) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString destString(decomposition, 0, capacity); - if(reinterpret_cast(norm2)->getRawDecomposition(c, destString)) { - return destString.extract(decomposition, capacity, *pErrorCode); - } else { - return -1; - } -} - -U_CAPI UChar32 U_EXPORT2 -unorm2_composePair(const UNormalizer2 *norm2, UChar32 a, UChar32 b) { - return reinterpret_cast(norm2)->composePair(a, b); -} - -U_CAPI uint8_t U_EXPORT2 -unorm2_getCombiningClass(const UNormalizer2 *norm2, UChar32 c) { - return reinterpret_cast(norm2)->getCombiningClass(c); -} - -U_CAPI UBool U_EXPORT2 -unorm2_isNormalized(const UNormalizer2 *norm2, - const UChar *s, int32_t length, - UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if((s==NULL && length!=0) || length<-1) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString sString(length<0, s, length); - return ((const Normalizer2 *)norm2)->isNormalized(sString, *pErrorCode); -} - -U_CAPI UNormalizationCheckResult U_EXPORT2 -unorm2_quickCheck(const UNormalizer2 *norm2, - const UChar *s, int32_t length, - UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return UNORM_NO; - } - if((s==NULL && length!=0) || length<-1) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return UNORM_NO; - } - UnicodeString sString(length<0, s, length); - return ((const Normalizer2 *)norm2)->quickCheck(sString, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -unorm2_spanQuickCheckYes(const UNormalizer2 *norm2, - const UChar *s, int32_t length, - UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if((s==NULL && length!=0) || length<-1) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString sString(length<0, s, length); - return ((const Normalizer2 *)norm2)->spanQuickCheckYes(sString, *pErrorCode); -} - -U_CAPI UBool U_EXPORT2 -unorm2_hasBoundaryBefore(const UNormalizer2 *norm2, UChar32 c) { - return ((const Normalizer2 *)norm2)->hasBoundaryBefore(c); -} - -U_CAPI UBool U_EXPORT2 -unorm2_hasBoundaryAfter(const UNormalizer2 *norm2, UChar32 c) { - return ((const Normalizer2 *)norm2)->hasBoundaryAfter(c); -} - -U_CAPI UBool U_EXPORT2 -unorm2_isInert(const UNormalizer2 *norm2, UChar32 c) { - return ((const Normalizer2 *)norm2)->isInert(c); -} - -// Some properties APIs ---------------------------------------------------- *** - -U_CAPI uint8_t U_EXPORT2 -u_getCombiningClass(UChar32 c) { - UErrorCode errorCode=U_ZERO_ERROR; - const Normalizer2 *nfd=Normalizer2::getNFDInstance(errorCode); - if(U_SUCCESS(errorCode)) { - return nfd->getCombiningClass(c); - } else { - return 0; - } -} - -U_CFUNC uint16_t -unorm_getFCD16(UChar32 c) { - UErrorCode errorCode=U_ZERO_ERROR; - const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(errorCode); - if(U_SUCCESS(errorCode)) { - return impl->getFCD16(c); - } else { - return 0; - } -} - -#endif // !UCONFIG_NO_NORMALIZATION +// © 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. +* +******************************************************************************* +* file name: normalizer2.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2009nov22 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_NORMALIZATION + +#include "unicode/edits.h" +#include "unicode/normalizer2.h" +#include "unicode/stringoptions.h" +#include "unicode/unistr.h" +#include "unicode/unorm.h" +#include "cstring.h" +#include "mutex.h" +#include "norm2allmodes.h" +#include "normalizer2impl.h" +#include "uassert.h" +#include "ucln_cmn.h" + +using icu::Normalizer2Impl; + +#if NORM2_HARDCODE_NFC_DATA +// NFC/NFD data machine-generated by gennorm2 --csource +#define INCLUDED_FROM_NORMALIZER2_CPP +#include "norm2_nfc_data.h" +#endif + +U_NAMESPACE_BEGIN + +// Public API dispatch via Normalizer2 subclasses -------------------------- *** + +Normalizer2::~Normalizer2() {} + +void +Normalizer2::normalizeUTF8(uint32_t /*options*/, StringPiece src, ByteSink &sink, + Edits *edits, UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { + return; + } + if (edits != nullptr) { + errorCode = U_UNSUPPORTED_ERROR; + return; + } + UnicodeString src16 = UnicodeString::fromUTF8(src); + normalize(src16, errorCode).toUTF8(sink); +} + +UBool +Normalizer2::getRawDecomposition(UChar32, UnicodeString &) const { + return false; +} + +UChar32 +Normalizer2::composePair(UChar32, UChar32) const { + return U_SENTINEL; +} + +uint8_t +Normalizer2::getCombiningClass(UChar32 /*c*/) const { + return 0; +} + +UBool +Normalizer2::isNormalizedUTF8(StringPiece s, UErrorCode &errorCode) const { + return U_SUCCESS(errorCode) && isNormalized(UnicodeString::fromUTF8(s), errorCode); +} + +// Normalizer2 implementation for the old UNORM_NONE. +class NoopNormalizer2 : public Normalizer2 { + virtual ~NoopNormalizer2(); + + virtual UnicodeString & + normalize(const UnicodeString &src, + UnicodeString &dest, + UErrorCode &errorCode) const override { + if(U_SUCCESS(errorCode)) { + if(&dest!=&src) { + dest=src; + } else { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + } + } + return dest; + } + virtual void + normalizeUTF8(uint32_t options, StringPiece src, ByteSink &sink, + Edits *edits, UErrorCode &errorCode) const override { + if(U_SUCCESS(errorCode)) { + if (edits != nullptr) { + if ((options & U_EDITS_NO_RESET) == 0) { + edits->reset(); + } + edits->addUnchanged(src.length()); + } + if ((options & U_OMIT_UNCHANGED_TEXT) == 0) { + sink.Append(src.data(), src.length()); + } + sink.Flush(); + } + } + + virtual UnicodeString & + normalizeSecondAndAppend(UnicodeString &first, + const UnicodeString &second, + UErrorCode &errorCode) const override { + if(U_SUCCESS(errorCode)) { + if(&first!=&second) { + first.append(second); + } else { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + } + } + return first; + } + virtual UnicodeString & + append(UnicodeString &first, + const UnicodeString &second, + UErrorCode &errorCode) const override { + if(U_SUCCESS(errorCode)) { + if(&first!=&second) { + first.append(second); + } else { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + } + } + return first; + } + virtual UBool + getDecomposition(UChar32, UnicodeString &) const override { + return false; + } + // No need to override the default getRawDecomposition(). + virtual UBool + isNormalized(const UnicodeString &, UErrorCode &errorCode) const override { + return U_SUCCESS(errorCode); + } + virtual UBool + isNormalizedUTF8(StringPiece, UErrorCode &errorCode) const override { + return U_SUCCESS(errorCode); + } + virtual UNormalizationCheckResult + quickCheck(const UnicodeString &, UErrorCode &) const override { + return UNORM_YES; + } + virtual int32_t + spanQuickCheckYes(const UnicodeString &s, UErrorCode &) const override { + return s.length(); + } + virtual UBool hasBoundaryBefore(UChar32) const override { return true; } + virtual UBool hasBoundaryAfter(UChar32) const override { return true; } + virtual UBool isInert(UChar32) const override { return true; } +}; + +NoopNormalizer2::~NoopNormalizer2() {} + +Normalizer2WithImpl::~Normalizer2WithImpl() {} + +DecomposeNormalizer2::~DecomposeNormalizer2() {} + +ComposeNormalizer2::~ComposeNormalizer2() {} + +FCDNormalizer2::~FCDNormalizer2() {} + +// instance cache ---------------------------------------------------------- *** + +U_CDECL_BEGIN +static UBool U_CALLCONV uprv_normalizer2_cleanup(); +U_CDECL_END + +static Normalizer2 *noopSingleton; +static icu::UInitOnce noopInitOnce {}; + +static void U_CALLCONV initNoopSingleton(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return; + } + noopSingleton=new NoopNormalizer2; + if(noopSingleton==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + ucln_common_registerCleanup(UCLN_COMMON_NORMALIZER2, uprv_normalizer2_cleanup); +} + +const Normalizer2 *Normalizer2Factory::getNoopInstance(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return nullptr; } + umtx_initOnce(noopInitOnce, &initNoopSingleton, errorCode); + return noopSingleton; +} + +const Normalizer2Impl * +Normalizer2Factory::getImpl(const Normalizer2 *norm2) { + return &((Normalizer2WithImpl *)norm2)->impl; +} + +Norm2AllModes::~Norm2AllModes() { + delete impl; +} + +Norm2AllModes * +Norm2AllModes::createInstance(Normalizer2Impl *impl, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + delete impl; + return nullptr; + } + Norm2AllModes *allModes=new Norm2AllModes(impl); + if(allModes==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + delete impl; + return nullptr; + } + return allModes; +} + +#if NORM2_HARDCODE_NFC_DATA +Norm2AllModes * +Norm2AllModes::createNFCInstance(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return nullptr; + } + Normalizer2Impl *impl=new Normalizer2Impl; + if(impl==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + impl->init(norm2_nfc_data_indexes, &norm2_nfc_data_trie, + norm2_nfc_data_extraData, norm2_nfc_data_smallFCD); + return createInstance(impl, errorCode); +} + +static Norm2AllModes *nfcSingleton; + +static icu::UInitOnce nfcInitOnce {}; + +static void U_CALLCONV initNFCSingleton(UErrorCode &errorCode) { + nfcSingleton=Norm2AllModes::createNFCInstance(errorCode); + ucln_common_registerCleanup(UCLN_COMMON_NORMALIZER2, uprv_normalizer2_cleanup); +} + +const Norm2AllModes * +Norm2AllModes::getNFCInstance(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return nullptr; } + umtx_initOnce(nfcInitOnce, &initNFCSingleton, errorCode); + return nfcSingleton; +} + +const Normalizer2 * +Normalizer2::getNFCInstance(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); + return allModes!=nullptr ? &allModes->comp : nullptr; +} + +const Normalizer2 * +Normalizer2::getNFDInstance(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); + return allModes!=nullptr ? &allModes->decomp : nullptr; +} + +const Normalizer2 *Normalizer2Factory::getFCDInstance(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); + return allModes!=nullptr ? &allModes->fcd : nullptr; +} + +const Normalizer2 *Normalizer2Factory::getFCCInstance(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); + return allModes!=nullptr ? &allModes->fcc : nullptr; +} + +const Normalizer2Impl * +Normalizer2Factory::getNFCImpl(UErrorCode &errorCode) { + const Norm2AllModes *allModes=Norm2AllModes::getNFCInstance(errorCode); + return allModes!=nullptr ? allModes->impl : nullptr; +} +#endif // NORM2_HARDCODE_NFC_DATA + +U_CDECL_BEGIN + +static UBool U_CALLCONV uprv_normalizer2_cleanup() { + delete noopSingleton; + noopSingleton = nullptr; + noopInitOnce.reset(); +#if NORM2_HARDCODE_NFC_DATA + delete nfcSingleton; + nfcSingleton = nullptr; + nfcInitOnce.reset(); +#endif + return true; +} + +U_CDECL_END + +U_NAMESPACE_END + +// C API ------------------------------------------------------------------- *** + +U_NAMESPACE_USE + +U_CAPI const UNormalizer2 * U_EXPORT2 +unorm2_getNFCInstance(UErrorCode *pErrorCode) { + return (const UNormalizer2 *)Normalizer2::getNFCInstance(*pErrorCode); +} + +U_CAPI const UNormalizer2 * U_EXPORT2 +unorm2_getNFDInstance(UErrorCode *pErrorCode) { + return (const UNormalizer2 *)Normalizer2::getNFDInstance(*pErrorCode); +} + +U_CAPI void U_EXPORT2 +unorm2_close(UNormalizer2 *norm2) { + delete (Normalizer2 *)norm2; +} + +U_CAPI int32_t U_EXPORT2 +unorm2_normalize(const UNormalizer2 *norm2, + const char16_t *src, int32_t length, + char16_t *dest, int32_t capacity, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if( (src==nullptr ? length!=0 : length<-1) || + (dest==nullptr ? capacity!=0 : capacity<0) || + (src==dest && src!=nullptr) + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString destString(dest, 0, capacity); + // length==0: Nothing to do, and n2wi->normalize(nullptr, nullptr, buffer, ...) would crash. + if(length!=0) { + const Normalizer2 *n2=(const Normalizer2 *)norm2; + const Normalizer2WithImpl *n2wi=dynamic_cast(n2); + if(n2wi!=nullptr) { + // Avoid duplicate argument checking and support NUL-terminated src. + ReorderingBuffer buffer(n2wi->impl, destString); + if(buffer.init(length, *pErrorCode)) { + n2wi->normalize(src, length>=0 ? src+length : nullptr, buffer, *pErrorCode); + } + } else { + UnicodeString srcString(length<0, src, length); + n2->normalize(srcString, destString, *pErrorCode); + } + } + return destString.extract(dest, capacity, *pErrorCode); +} + +static int32_t +normalizeSecondAndAppend(const UNormalizer2 *norm2, + char16_t *first, int32_t firstLength, int32_t firstCapacity, + const char16_t *second, int32_t secondLength, + UBool doNormalize, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if( (second==nullptr ? secondLength!=0 : secondLength<-1) || + (first==nullptr ? (firstCapacity!=0 || firstLength!=0) : + (firstCapacity<0 || firstLength<-1)) || + (first==second && first!=nullptr) + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString firstString(first, firstLength, firstCapacity); + firstLength=firstString.length(); // In case it was -1. + // secondLength==0: Nothing to do, and n2wi->normalizeAndAppend(nullptr, nullptr, buffer, ...) would crash. + if(secondLength!=0) { + const Normalizer2 *n2=(const Normalizer2 *)norm2; + const Normalizer2WithImpl *n2wi=dynamic_cast(n2); + if(n2wi!=nullptr) { + // Avoid duplicate argument checking and support NUL-terminated src. + UnicodeString safeMiddle; + { + ReorderingBuffer buffer(n2wi->impl, firstString); + if(buffer.init(firstLength+secondLength+1, *pErrorCode)) { // destCapacity>=-1 + n2wi->normalizeAndAppend(second, secondLength>=0 ? second+secondLength : nullptr, + doNormalize, safeMiddle, buffer, *pErrorCode); + } + } // The ReorderingBuffer destructor finalizes firstString. + if(U_FAILURE(*pErrorCode) || firstString.length()>firstCapacity) { + // Restore the modified suffix of the first string. + // This does not restore first[] array contents between firstLength and firstCapacity. + // (That might be uninitialized memory, as far as we know.) + if(first!=nullptr) { /* don't dereference nullptr */ + safeMiddle.extract(0, 0x7fffffff, first+firstLength-safeMiddle.length()); + if(firstLengthnormalizeSecondAndAppend(firstString, secondString, *pErrorCode); + } else { + n2->append(firstString, secondString, *pErrorCode); + } + } + } + return firstString.extract(first, firstCapacity, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +unorm2_normalizeSecondAndAppend(const UNormalizer2 *norm2, + char16_t *first, int32_t firstLength, int32_t firstCapacity, + const char16_t *second, int32_t secondLength, + UErrorCode *pErrorCode) { + return normalizeSecondAndAppend(norm2, + first, firstLength, firstCapacity, + second, secondLength, + true, pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +unorm2_append(const UNormalizer2 *norm2, + char16_t *first, int32_t firstLength, int32_t firstCapacity, + const char16_t *second, int32_t secondLength, + UErrorCode *pErrorCode) { + return normalizeSecondAndAppend(norm2, + first, firstLength, firstCapacity, + second, secondLength, + false, pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +unorm2_getDecomposition(const UNormalizer2 *norm2, + UChar32 c, char16_t *decomposition, int32_t capacity, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(decomposition==nullptr ? capacity!=0 : capacity<0) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString destString(decomposition, 0, capacity); + if(reinterpret_cast(norm2)->getDecomposition(c, destString)) { + return destString.extract(decomposition, capacity, *pErrorCode); + } else { + return -1; + } +} + +U_CAPI int32_t U_EXPORT2 +unorm2_getRawDecomposition(const UNormalizer2 *norm2, + UChar32 c, char16_t *decomposition, int32_t capacity, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(decomposition==nullptr ? capacity!=0 : capacity<0) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString destString(decomposition, 0, capacity); + if(reinterpret_cast(norm2)->getRawDecomposition(c, destString)) { + return destString.extract(decomposition, capacity, *pErrorCode); + } else { + return -1; + } +} + +U_CAPI UChar32 U_EXPORT2 +unorm2_composePair(const UNormalizer2 *norm2, UChar32 a, UChar32 b) { + return reinterpret_cast(norm2)->composePair(a, b); +} + +U_CAPI uint8_t U_EXPORT2 +unorm2_getCombiningClass(const UNormalizer2 *norm2, UChar32 c) { + return reinterpret_cast(norm2)->getCombiningClass(c); +} + +U_CAPI UBool U_EXPORT2 +unorm2_isNormalized(const UNormalizer2 *norm2, + const char16_t *s, int32_t length, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if((s==nullptr && length!=0) || length<-1) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString sString(length<0, s, length); + return ((const Normalizer2 *)norm2)->isNormalized(sString, *pErrorCode); +} + +U_CAPI UNormalizationCheckResult U_EXPORT2 +unorm2_quickCheck(const UNormalizer2 *norm2, + const char16_t *s, int32_t length, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return UNORM_NO; + } + if((s==nullptr && length!=0) || length<-1) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return UNORM_NO; + } + UnicodeString sString(length<0, s, length); + return ((const Normalizer2 *)norm2)->quickCheck(sString, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +unorm2_spanQuickCheckYes(const UNormalizer2 *norm2, + const char16_t *s, int32_t length, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if((s==nullptr && length!=0) || length<-1) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString sString(length<0, s, length); + return ((const Normalizer2 *)norm2)->spanQuickCheckYes(sString, *pErrorCode); +} + +U_CAPI UBool U_EXPORT2 +unorm2_hasBoundaryBefore(const UNormalizer2 *norm2, UChar32 c) { + return ((const Normalizer2 *)norm2)->hasBoundaryBefore(c); +} + +U_CAPI UBool U_EXPORT2 +unorm2_hasBoundaryAfter(const UNormalizer2 *norm2, UChar32 c) { + return ((const Normalizer2 *)norm2)->hasBoundaryAfter(c); +} + +U_CAPI UBool U_EXPORT2 +unorm2_isInert(const UNormalizer2 *norm2, UChar32 c) { + return ((const Normalizer2 *)norm2)->isInert(c); +} + +// Some properties APIs ---------------------------------------------------- *** + +U_CAPI uint8_t U_EXPORT2 +u_getCombiningClass(UChar32 c) { + UErrorCode errorCode=U_ZERO_ERROR; + const Normalizer2 *nfd=Normalizer2::getNFDInstance(errorCode); + if(U_SUCCESS(errorCode)) { + return nfd->getCombiningClass(c); + } else { + return 0; + } +} + +U_CFUNC uint16_t +unorm_getFCD16(UChar32 c) { + UErrorCode errorCode=U_ZERO_ERROR; + const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(errorCode); + if(U_SUCCESS(errorCode)) { + return impl->getFCD16(c); + } else { + return 0; + } +} + +#endif // !UCONFIG_NO_NORMALIZATION diff --git a/deps/icu-small/source/common/normalizer2impl.cpp b/deps/icu-small/source/common/normalizer2impl.cpp index d7e05e44d725bc..39372a241a3568 100644 --- a/deps/icu-small/source/common/normalizer2impl.cpp +++ b/deps/icu-small/source/common/normalizer2impl.cpp @@ -1,2806 +1,2806 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2009-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: normalizer2impl.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2009nov22 -* created by: Markus W. Scherer -*/ - -// #define UCPTRIE_DEBUG - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_NORMALIZATION - -#include "unicode/bytestream.h" -#include "unicode/edits.h" -#include "unicode/normalizer2.h" -#include "unicode/stringoptions.h" -#include "unicode/ucptrie.h" -#include "unicode/udata.h" -#include "unicode/umutablecptrie.h" -#include "unicode/ustring.h" -#include "unicode/utf16.h" -#include "unicode/utf8.h" -#include "bytesinkutil.h" -#include "cmemory.h" -#include "mutex.h" -#include "normalizer2impl.h" -#include "putilimp.h" -#include "uassert.h" -#include "ucptrie_impl.h" -#include "uset_imp.h" -#include "uvector.h" - -U_NAMESPACE_BEGIN - -namespace { - -/** - * UTF-8 lead byte for minNoMaybeCP. - * Can be lower than the actual lead byte for c. - * Typically U+0300 for NFC/NFD, U+00A0 for NFKC/NFKD, U+0041 for NFKC_Casefold. - */ -inline uint8_t leadByteForCP(UChar32 c) { - if (c <= 0x7f) { - return (uint8_t)c; - } else if (c <= 0x7ff) { - return (uint8_t)(0xc0+(c>>6)); - } else { - // Should not occur because ccc(U+0300)!=0. - return 0xe0; - } -} - -/** - * Returns the code point from one single well-formed UTF-8 byte sequence - * between cpStart and cpLimit. - * - * Trie UTF-8 macros do not assemble whole code points (for efficiency). - * When we do need the code point, we call this function. - * We should not need it for normalization-inert data (norm16==0). - * Illegal sequences yield the error value norm16==0 just like real normalization-inert code points. - */ -UChar32 codePointFromValidUTF8(const uint8_t *cpStart, const uint8_t *cpLimit) { - // Similar to U8_NEXT_UNSAFE(s, i, c). - U_ASSERT(cpStart < cpLimit); - uint8_t c = *cpStart; - switch(cpLimit-cpStart) { - case 1: - return c; - case 2: - return ((c&0x1f)<<6) | (cpStart[1]&0x3f); - case 3: - // no need for (c&0xf) because the upper bits are truncated after <<12 in the cast to (UChar) - return (UChar)((c<<12) | ((cpStart[1]&0x3f)<<6) | (cpStart[2]&0x3f)); - case 4: - return ((c&7)<<18) | ((cpStart[1]&0x3f)<<12) | ((cpStart[2]&0x3f)<<6) | (cpStart[3]&0x3f); - default: - UPRV_UNREACHABLE_EXIT; // Should not occur. - } -} - -/** - * Returns the last code point in [start, p[ if it is valid and in U+1000..U+D7FF. - * Otherwise returns a negative value. - */ -UChar32 previousHangulOrJamo(const uint8_t *start, const uint8_t *p) { - if ((p - start) >= 3) { - p -= 3; - uint8_t l = *p; - uint8_t t1, t2; - if (0xe1 <= l && l <= 0xed && - (t1 = (uint8_t)(p[1] - 0x80)) <= 0x3f && - (t2 = (uint8_t)(p[2] - 0x80)) <= 0x3f && - (l < 0xed || t1 <= 0x1f)) { - return ((l & 0xf) << 12) | (t1 << 6) | t2; - } - } - return U_SENTINEL; -} - -/** - * Returns the offset from the Jamo T base if [src, limit[ starts with a single Jamo T code point. - * Otherwise returns a negative value. - */ -int32_t getJamoTMinusBase(const uint8_t *src, const uint8_t *limit) { - // Jamo T: E1 86 A8..E1 87 82 - if ((limit - src) >= 3 && *src == 0xe1) { - if (src[1] == 0x86) { - uint8_t t = src[2]; - // The first Jamo T is U+11A8 but JAMO_T_BASE is 11A7. - // Offset 0 does not correspond to any conjoining Jamo. - if (0xa8 <= t && t <= 0xbf) { - return t - 0xa7; - } - } else if (src[1] == 0x87) { - uint8_t t = src[2]; - if ((int8_t)t <= (int8_t)0x82u) { - return t - (0xa7 - 0x40); - } - } - } - return -1; -} - -void -appendCodePointDelta(const uint8_t *cpStart, const uint8_t *cpLimit, int32_t delta, - ByteSink &sink, Edits *edits) { - char buffer[U8_MAX_LENGTH]; - int32_t length; - int32_t cpLength = (int32_t)(cpLimit - cpStart); - if (cpLength == 1) { - // The builder makes ASCII map to ASCII. - buffer[0] = (uint8_t)(*cpStart + delta); - length = 1; - } else { - int32_t trail = *(cpLimit-1) + delta; - if (0x80 <= trail && trail <= 0xbf) { - // The delta only changes the last trail byte. - --cpLimit; - length = 0; - do { buffer[length++] = *cpStart++; } while (cpStart < cpLimit); - buffer[length++] = (uint8_t)trail; - } else { - // Decode the code point, add the delta, re-encode. - UChar32 c = codePointFromValidUTF8(cpStart, cpLimit) + delta; - length = 0; - U8_APPEND_UNSAFE(buffer, length, c); - } - } - if (edits != nullptr) { - edits->addReplace(cpLength, length); - } - sink.Append(buffer, length); -} - -} // namespace - -// ReorderingBuffer -------------------------------------------------------- *** - -ReorderingBuffer::ReorderingBuffer(const Normalizer2Impl &ni, UnicodeString &dest, - UErrorCode &errorCode) : - impl(ni), str(dest), - start(str.getBuffer(8)), reorderStart(start), limit(start), - remainingCapacity(str.getCapacity()), lastCC(0) { - if (start == nullptr && U_SUCCESS(errorCode)) { - // getBuffer() already did str.setToBogus() - errorCode = U_MEMORY_ALLOCATION_ERROR; - } -} - -UBool ReorderingBuffer::init(int32_t destCapacity, UErrorCode &errorCode) { - int32_t length=str.length(); - start=str.getBuffer(destCapacity); - if(start==NULL) { - // getBuffer() already did str.setToBogus() - errorCode=U_MEMORY_ALLOCATION_ERROR; - return false; - } - limit=start+length; - remainingCapacity=str.getCapacity()-length; - reorderStart=start; - if(start==limit) { - lastCC=0; - } else { - setIterator(); - lastCC=previousCC(); - // Set reorderStart after the last code point with cc<=1 if there is one. - if(lastCC>1) { - while(previousCC()>1) {} - } - reorderStart=codePointLimit; - } - return true; -} - -UBool ReorderingBuffer::equals(const UChar *otherStart, const UChar *otherLimit) const { - int32_t length=(int32_t)(limit-start); - return - length==(int32_t)(otherLimit-otherStart) && - 0==u_memcmp(start, otherStart, length); -} - -UBool ReorderingBuffer::equals(const uint8_t *otherStart, const uint8_t *otherLimit) const { - U_ASSERT((otherLimit - otherStart) <= INT32_MAX); // ensured by caller - int32_t length = (int32_t)(limit - start); - int32_t otherLength = (int32_t)(otherLimit - otherStart); - // For equal strings, UTF-8 is at least as long as UTF-16, and at most three times as long. - if (otherLength < length || (otherLength / 3) > length) { - return false; - } - // Compare valid strings from between normalization boundaries. - // (Invalid sequences are normalization-inert.) - for (int32_t i = 0, j = 0;;) { - if (i >= length) { - return j >= otherLength; - } else if (j >= otherLength) { - return false; - } - // Not at the end of either string yet. - UChar32 c, other; - U16_NEXT_UNSAFE(start, i, c); - U8_NEXT_UNSAFE(otherStart, j, other); - if (c != other) { - return false; - } - } -} - -UBool ReorderingBuffer::appendSupplementary(UChar32 c, uint8_t cc, UErrorCode &errorCode) { - if(remainingCapacity<2 && !resize(2, errorCode)) { - return false; - } - if(lastCC<=cc || cc==0) { - limit[0]=U16_LEAD(c); - limit[1]=U16_TRAIL(c); - limit+=2; - lastCC=cc; - if(cc<=1) { - reorderStart=limit; - } - } else { - insert(c, cc); - } - remainingCapacity-=2; - return true; -} - -UBool ReorderingBuffer::append(const UChar *s, int32_t length, UBool isNFD, - uint8_t leadCC, uint8_t trailCC, - UErrorCode &errorCode) { - if(length==0) { - return true; - } - if(remainingCapacity=codePointStart) { - return 0; - } - UChar32 c=*--codePointStart; - UChar c2; - if(U16_IS_TRAIL(c) && startcc;) {} - // insert c at codePointLimit, after the character with prevCC<=cc - UChar *q=limit; - UChar *r=limit+=U16_LENGTH(c); - do { - *--r=*--q; - } while(codePointLimit!=q); - writeCodePoint(q, c); - if(cc<=1) { - reorderStart=r; - } -} - -// Normalizer2Impl --------------------------------------------------------- *** - -struct CanonIterData : public UMemory { - CanonIterData(UErrorCode &errorCode); - ~CanonIterData(); - void addToStartSet(UChar32 origin, UChar32 decompLead, UErrorCode &errorCode); - UMutableCPTrie *mutableTrie; - UCPTrie *trie; - UVector canonStartSets; // contains UnicodeSet * -}; - -Normalizer2Impl::~Normalizer2Impl() { - delete fCanonIterData; -} - -void -Normalizer2Impl::init(const int32_t *inIndexes, const UCPTrie *inTrie, - const uint16_t *inExtraData, const uint8_t *inSmallFCD) { - minDecompNoCP = static_cast(inIndexes[IX_MIN_DECOMP_NO_CP]); - minCompNoMaybeCP = static_cast(inIndexes[IX_MIN_COMP_NO_MAYBE_CP]); - minLcccCP = static_cast(inIndexes[IX_MIN_LCCC_CP]); - - minYesNo = static_cast(inIndexes[IX_MIN_YES_NO]); - minYesNoMappingsOnly = static_cast(inIndexes[IX_MIN_YES_NO_MAPPINGS_ONLY]); - minNoNo = static_cast(inIndexes[IX_MIN_NO_NO]); - minNoNoCompBoundaryBefore = static_cast(inIndexes[IX_MIN_NO_NO_COMP_BOUNDARY_BEFORE]); - minNoNoCompNoMaybeCC = static_cast(inIndexes[IX_MIN_NO_NO_COMP_NO_MAYBE_CC]); - minNoNoEmpty = static_cast(inIndexes[IX_MIN_NO_NO_EMPTY]); - limitNoNo = static_cast(inIndexes[IX_LIMIT_NO_NO]); - minMaybeYes = static_cast(inIndexes[IX_MIN_MAYBE_YES]); - U_ASSERT((minMaybeYes & 7) == 0); // 8-aligned for noNoDelta bit fields - centerNoNoDelta = (minMaybeYes >> DELTA_SHIFT) - MAX_DELTA - 1; - - normTrie=inTrie; - - maybeYesCompositions=inExtraData; - extraData=maybeYesCompositions+((MIN_NORMAL_MAYBE_YES-minMaybeYes)>>OFFSET_SHIFT); - - smallFCD=inSmallFCD; -} - -U_CDECL_BEGIN - -static uint32_t U_CALLCONV -segmentStarterMapper(const void * /*context*/, uint32_t value) { - return value&CANON_NOT_SEGMENT_STARTER; -} - -U_CDECL_END - -void -Normalizer2Impl::addLcccChars(UnicodeSet &set) const { - UChar32 start = 0, end; - uint32_t norm16; - while ((end = ucptrie_getRange(normTrie, start, UCPMAP_RANGE_FIXED_LEAD_SURROGATES, INERT, - nullptr, nullptr, &norm16)) >= 0) { - if (norm16 > Normalizer2Impl::MIN_NORMAL_MAYBE_YES && - norm16 != Normalizer2Impl::JAMO_VT) { - set.add(start, end); - } else if (minNoNoCompNoMaybeCC <= norm16 && norm16 < limitNoNo) { - uint16_t fcd16 = getFCD16(start); - if (fcd16 > 0xff) { set.add(start, end); } - } - start = end + 1; - } -} - -void -Normalizer2Impl::addPropertyStarts(const USetAdder *sa, UErrorCode & /*errorCode*/) const { - // Add the start code point of each same-value range of the trie. - UChar32 start = 0, end; - uint32_t value; - while ((end = ucptrie_getRange(normTrie, start, UCPMAP_RANGE_FIXED_LEAD_SURROGATES, INERT, - nullptr, nullptr, &value)) >= 0) { - sa->add(sa->set, start); - if (start != end && isAlgorithmicNoNo((uint16_t)value) && - (value & Normalizer2Impl::DELTA_TCCC_MASK) > Normalizer2Impl::DELTA_TCCC_1) { - // Range of code points with same-norm16-value algorithmic decompositions. - // They might have different non-zero FCD16 values. - uint16_t prevFCD16 = getFCD16(start); - while (++start <= end) { - uint16_t fcd16 = getFCD16(start); - if (fcd16 != prevFCD16) { - sa->add(sa->set, start); - prevFCD16 = fcd16; - } - } - } - start = end + 1; - } - - /* add Hangul LV syllables and LV+1 because of skippables */ - for(UChar c=Hangul::HANGUL_BASE; cadd(sa->set, c); - sa->add(sa->set, c+1); - } - sa->add(sa->set, Hangul::HANGUL_LIMIT); /* add Hangul+1 to continue with other properties */ -} - -void -Normalizer2Impl::addCanonIterPropertyStarts(const USetAdder *sa, UErrorCode &errorCode) const { - // Add the start code point of each same-value range of the canonical iterator data trie. - if (!ensureCanonIterData(errorCode)) { return; } - // Currently only used for the SEGMENT_STARTER property. - UChar32 start = 0, end; - uint32_t value; - while ((end = ucptrie_getRange(fCanonIterData->trie, start, UCPMAP_RANGE_NORMAL, 0, - segmentStarterMapper, nullptr, &value)) >= 0) { - sa->add(sa->set, start); - start = end + 1; - } -} - -const UChar * -Normalizer2Impl::copyLowPrefixFromNulTerminated(const UChar *src, - UChar32 minNeedDataCP, - ReorderingBuffer *buffer, - UErrorCode &errorCode) const { - // Make some effort to support NUL-terminated strings reasonably. - // Take the part of the fast quick check loop that does not look up - // data and check the first part of the string. - // After this prefix, determine the string length to simplify the rest - // of the code. - const UChar *prevSrc=src; - UChar c; - while((c=*src++)appendZeroCC(prevSrc, src, errorCode); - } - } - return src; -} - -UnicodeString & -Normalizer2Impl::decompose(const UnicodeString &src, UnicodeString &dest, - UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { - dest.setToBogus(); - return dest; - } - const UChar *sArray=src.getBuffer(); - if(&dest==&src || sArray==NULL) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - dest.setToBogus(); - return dest; - } - decompose(sArray, sArray+src.length(), dest, src.length(), errorCode); - return dest; -} - -void -Normalizer2Impl::decompose(const UChar *src, const UChar *limit, - UnicodeString &dest, - int32_t destLengthEstimate, - UErrorCode &errorCode) const { - if(destLengthEstimate<0 && limit!=NULL) { - destLengthEstimate=(int32_t)(limit-src); - } - dest.remove(); - ReorderingBuffer buffer(*this, dest); - if(buffer.init(destLengthEstimate, errorCode)) { - decompose(src, limit, &buffer, errorCode); - } -} - -// Dual functionality: -// buffer!=NULL: normalize -// buffer==NULL: isNormalized/spanQuickCheckYes -const UChar * -Normalizer2Impl::decompose(const UChar *src, const UChar *limit, - ReorderingBuffer *buffer, - UErrorCode &errorCode) const { - UChar32 minNoCP=minDecompNoCP; - if(limit==NULL) { - src=copyLowPrefixFromNulTerminated(src, minNoCP, buffer, errorCode); - if(U_FAILURE(errorCode)) { - return src; - } - limit=u_strchr(src, 0); - } - - const UChar *prevSrc; - UChar32 c=0; - uint16_t norm16=0; - - // only for quick check - const UChar *prevBoundary=src; - uint8_t prevCC=0; - - for(;;) { - // count code units below the minimum or with irrelevant data for the quick check - for(prevSrc=src; src!=limit;) { - if( (c=*src)appendZeroCC(prevSrc, src, errorCode)) { - break; - } - } else { - prevCC=0; - prevBoundary=src; - } - } - if(src==limit) { - break; - } - - // Check one above-minimum, relevant code point. - src+=U16_LENGTH(c); - if(buffer!=NULL) { - if(!decompose(c, norm16, *buffer, errorCode)) { - break; - } - } else { - if(isDecompYes(norm16)) { - uint8_t cc=getCCFromYesOrMaybe(norm16); - if(prevCC<=cc || cc==0) { - prevCC=cc; - if(cc<=1) { - prevBoundary=src; - } - continue; - } - } - return prevBoundary; // "no" or cc out of order - } - } - return src; -} - -// Decompose a short piece of text which is likely to contain characters that -// fail the quick check loop and/or where the quick check loop's overhead -// is unlikely to be amortized. -// Called by the compose() and makeFCD() implementations. -const UChar * -Normalizer2Impl::decomposeShort(const UChar *src, const UChar *limit, - UBool stopAtCompBoundary, UBool onlyContiguous, - ReorderingBuffer &buffer, UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { - return nullptr; - } - while(src= limitNoNo) { - if (isMaybeOrNonZeroCC(norm16)) { - return buffer.append(c, getCCFromYesOrMaybe(norm16), errorCode); - } - // Maps to an isCompYesAndZeroCC. - c=mapAlgorithmic(c, norm16); - norm16=getRawNorm16(c); - } - if (norm16 < minYesNo) { - // c does not decompose - return buffer.append(c, 0, errorCode); - } else if(isHangulLV(norm16) || isHangulLVT(norm16)) { - // Hangul syllable: decompose algorithmically - UChar jamos[3]; - return buffer.appendZeroCC(jamos, jamos+Hangul::decompose(c, jamos), errorCode); - } - // c decomposes, get everything from the variable-length extra data - const uint16_t *mapping=getMapping(norm16); - uint16_t firstUnit=*mapping; - int32_t length=firstUnit&MAPPING_LENGTH_MASK; - uint8_t leadCC, trailCC; - trailCC=(uint8_t)(firstUnit>>8); - if(firstUnit&MAPPING_HAS_CCC_LCCC_WORD) { - leadCC=(uint8_t)(*(mapping-1)>>8); - } else { - leadCC=0; - } - return buffer.append((const UChar *)mapping+1, length, true, leadCC, trailCC, errorCode); -} - -// Dual functionality: -// sink != nullptr: normalize -// sink == nullptr: isNormalized/spanQuickCheckYes -const uint8_t * -Normalizer2Impl::decomposeUTF8(uint32_t options, - const uint8_t *src, const uint8_t *limit, - ByteSink *sink, Edits *edits, UErrorCode &errorCode) const { - U_ASSERT(limit != nullptr); - UnicodeString s16; - uint8_t minNoLead = leadByteForCP(minDecompNoCP); - - const uint8_t *prevBoundary = src; - // only for quick check - uint8_t prevCC = 0; - - for (;;) { - // Fast path: Scan over a sequence of characters below the minimum "no" code point, - // or with (decompYes && ccc==0) properties. - const uint8_t *fastStart = src; - const uint8_t *prevSrc; - uint16_t norm16 = 0; - - for (;;) { - if (src == limit) { - if (prevBoundary != limit && sink != nullptr) { - ByteSinkUtil::appendUnchanged(prevBoundary, limit, - *sink, options, edits, errorCode); - } - return src; - } - if (*src < minNoLead) { - ++src; - } else { - prevSrc = src; - UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, src, limit, norm16); - if (!isMostDecompYesAndZeroCC(norm16)) { - break; - } - } - } - // isMostDecompYesAndZeroCC(norm16) is false, that is, norm16>=minYesNo, - // and the current character at [prevSrc..src[ is not a common case with cc=0 - // (MIN_NORMAL_MAYBE_YES or JAMO_VT). - // It could still be a maybeYes with cc=0. - if (prevSrc != fastStart) { - // The fast path looped over yes/0 characters before the current one. - if (sink != nullptr && - !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, - *sink, options, edits, errorCode)) { - break; - } - prevBoundary = prevSrc; - prevCC = 0; - } - - // Medium-fast path: Quick check. - if (isMaybeOrNonZeroCC(norm16)) { - // Does not decompose. - uint8_t cc = getCCFromYesOrMaybe(norm16); - if (prevCC <= cc || cc == 0) { - prevCC = cc; - if (cc <= 1) { - if (sink != nullptr && - !ByteSinkUtil::appendUnchanged(prevBoundary, src, - *sink, options, edits, errorCode)) { - break; - } - prevBoundary = src; - } - continue; - } - } - if (sink == nullptr) { - return prevBoundary; // quick check: "no" or cc out of order - } - - // Slow path - // Decompose up to and including the current character. - if (prevBoundary != prevSrc && norm16HasDecompBoundaryBefore(norm16)) { - if (!ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, - *sink, options, edits, errorCode)) { - break; - } - prevBoundary = prevSrc; - } - ReorderingBuffer buffer(*this, s16, errorCode); - if (U_FAILURE(errorCode)) { - break; - } - decomposeShort(prevBoundary, src, STOP_AT_LIMIT, false /* onlyContiguous */, - buffer, errorCode); - // Decompose until the next boundary. - if (buffer.getLastCC() > 1) { - src = decomposeShort(src, limit, STOP_AT_DECOMP_BOUNDARY, false /* onlyContiguous */, - buffer, errorCode); - } - if (U_FAILURE(errorCode)) { - break; - } - if ((src - prevSrc) > INT32_MAX) { // guard before buffer.equals() - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - break; - } - // We already know there was a change if the original character decomposed; - // otherwise compare. - if (isMaybeOrNonZeroCC(norm16) && buffer.equals(prevBoundary, src)) { - if (!ByteSinkUtil::appendUnchanged(prevBoundary, src, - *sink, options, edits, errorCode)) { - break; - } - } else { - if (!ByteSinkUtil::appendChange(prevBoundary, src, buffer.getStart(), buffer.length(), - *sink, edits, errorCode)) { - break; - } - } - prevBoundary = src; - prevCC = 0; - } - return src; -} - -const uint8_t * -Normalizer2Impl::decomposeShort(const uint8_t *src, const uint8_t *limit, - StopAt stopAt, UBool onlyContiguous, - ReorderingBuffer &buffer, UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { - return nullptr; - } - while (src < limit) { - const uint8_t *prevSrc = src; - uint16_t norm16; - UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, src, limit, norm16); - // Get the decomposition and the lead and trail cc's. - UChar32 c = U_SENTINEL; - if (norm16 >= limitNoNo) { - if (isMaybeOrNonZeroCC(norm16)) { - // No comp boundaries around this character. - uint8_t cc = getCCFromYesOrMaybe(norm16); - if (cc == 0 && stopAt == STOP_AT_DECOMP_BOUNDARY) { - return prevSrc; - } - c = codePointFromValidUTF8(prevSrc, src); - if (!buffer.append(c, cc, errorCode)) { - return nullptr; - } - if (stopAt == STOP_AT_DECOMP_BOUNDARY && buffer.getLastCC() <= 1) { - return src; - } - continue; - } - // Maps to an isCompYesAndZeroCC. - if (stopAt != STOP_AT_LIMIT) { - return prevSrc; - } - c = codePointFromValidUTF8(prevSrc, src); - c = mapAlgorithmic(c, norm16); - norm16 = getRawNorm16(c); - } else if (stopAt != STOP_AT_LIMIT && norm16 < minNoNoCompNoMaybeCC) { - return prevSrc; - } - // norm16!=INERT guarantees that [prevSrc, src[ is valid UTF-8. - // We do not see invalid UTF-8 here because - // its norm16==INERT is normalization-inert, - // so it gets copied unchanged in the fast path, - // and we stop the slow path where invalid UTF-8 begins. - // c >= 0 is the result of an algorithmic mapping. - U_ASSERT(c >= 0 || norm16 != INERT); - if (norm16 < minYesNo) { - if (c < 0) { - c = codePointFromValidUTF8(prevSrc, src); - } - // does not decompose - if (!buffer.append(c, 0, errorCode)) { - return nullptr; - } - } else if (isHangulLV(norm16) || isHangulLVT(norm16)) { - // Hangul syllable: decompose algorithmically - if (c < 0) { - c = codePointFromValidUTF8(prevSrc, src); - } - char16_t jamos[3]; - if (!buffer.appendZeroCC(jamos, jamos+Hangul::decompose(c, jamos), errorCode)) { - return nullptr; - } - } else { - // The character decomposes, get everything from the variable-length extra data. - const uint16_t *mapping = getMapping(norm16); - uint16_t firstUnit = *mapping; - int32_t length = firstUnit & MAPPING_LENGTH_MASK; - uint8_t trailCC = (uint8_t)(firstUnit >> 8); - uint8_t leadCC; - if (firstUnit & MAPPING_HAS_CCC_LCCC_WORD) { - leadCC = (uint8_t)(*(mapping-1) >> 8); - } else { - leadCC = 0; - } - if (leadCC == 0 && stopAt == STOP_AT_DECOMP_BOUNDARY) { - return prevSrc; - } - if (!buffer.append((const char16_t *)mapping+1, length, true, leadCC, trailCC, errorCode)) { - return nullptr; - } - } - if ((stopAt == STOP_AT_COMP_BOUNDARY && norm16HasCompBoundaryAfter(norm16, onlyContiguous)) || - (stopAt == STOP_AT_DECOMP_BOUNDARY && buffer.getLastCC() <= 1)) { - return src; - } - } - return src; -} - -const UChar * -Normalizer2Impl::getDecomposition(UChar32 c, UChar buffer[4], int32_t &length) const { - uint16_t norm16; - if(c>7)&1)-1; - uint16_t rm0=*rawMapping; - if(rm0<=MAPPING_LENGTH_MASK) { - length=rm0; - return (const UChar *)rawMapping-rm0; - } else { - // Copy the normal mapping and replace its first two code units with rm0. - buffer[0]=(UChar)rm0; - u_memcpy(buffer+1, (const UChar *)mapping+1+2, mLength-2); - length=mLength-1; - return buffer; - } - } else { - length=mLength; - return (const UChar *)mapping+1; - } -} - -void Normalizer2Impl::decomposeAndAppend(const UChar *src, const UChar *limit, - UBool doDecompose, - UnicodeString &safeMiddle, - ReorderingBuffer &buffer, - UErrorCode &errorCode) const { - buffer.copyReorderableSuffixTo(safeMiddle); - if(doDecompose) { - decompose(src, limit, &buffer, errorCode); - return; - } - // Just merge the strings at the boundary. - bool isFirst = true; - uint8_t firstCC = 0, prevCC = 0, cc; - const UChar *p = src; - while (p != limit) { - const UChar *codePointStart = p; - UChar32 c; - uint16_t norm16; - UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, p, limit, c, norm16); - if ((cc = getCC(norm16)) == 0) { - p = codePointStart; - break; - } - if (isFirst) { - firstCC = cc; - isFirst = false; - } - prevCC = cc; - } - if(limit==NULL) { // appendZeroCC() needs limit!=NULL - limit=u_strchr(p, 0); - } - - if (buffer.append(src, (int32_t)(p - src), false, firstCC, prevCC, errorCode)) { - buffer.appendZeroCC(p, limit, errorCode); - } -} - -UBool Normalizer2Impl::hasDecompBoundaryBefore(UChar32 c) const { - return c < minLcccCP || (c <= 0xffff && !singleLeadMightHaveNonZeroFCD16(c)) || - norm16HasDecompBoundaryBefore(getNorm16(c)); -} - -UBool Normalizer2Impl::norm16HasDecompBoundaryBefore(uint16_t norm16) const { - if (norm16 < minNoNoCompNoMaybeCC) { - return true; - } - if (norm16 >= limitNoNo) { - return norm16 <= MIN_NORMAL_MAYBE_YES || norm16 == JAMO_VT; - } - // c decomposes, get everything from the variable-length extra data - const uint16_t *mapping=getMapping(norm16); - uint16_t firstUnit=*mapping; - // true if leadCC==0 (hasFCDBoundaryBefore()) - return (firstUnit&MAPPING_HAS_CCC_LCCC_WORD)==0 || (*(mapping-1)&0xff00)==0; -} - -UBool Normalizer2Impl::hasDecompBoundaryAfter(UChar32 c) const { - if (c < minDecompNoCP) { - return true; - } - if (c <= 0xffff && !singleLeadMightHaveNonZeroFCD16(c)) { - return true; - } - return norm16HasDecompBoundaryAfter(getNorm16(c)); -} - -UBool Normalizer2Impl::norm16HasDecompBoundaryAfter(uint16_t norm16) const { - if(norm16 <= minYesNo || isHangulLVT(norm16)) { - return true; - } - if (norm16 >= limitNoNo) { - if (isMaybeOrNonZeroCC(norm16)) { - return norm16 <= MIN_NORMAL_MAYBE_YES || norm16 == JAMO_VT; - } - // Maps to an isCompYesAndZeroCC. - return (norm16 & DELTA_TCCC_MASK) <= DELTA_TCCC_1; - } - // c decomposes, get everything from the variable-length extra data - const uint16_t *mapping=getMapping(norm16); - uint16_t firstUnit=*mapping; - // decomp after-boundary: same as hasFCDBoundaryAfter(), - // fcd16<=1 || trailCC==0 - if(firstUnit>0x1ff) { - return false; // trailCC>1 - } - if(firstUnit<=0xff) { - return true; // trailCC==0 - } - // if(trailCC==1) test leadCC==0, same as checking for before-boundary - // true if leadCC==0 (hasFCDBoundaryBefore()) - return (firstUnit&MAPPING_HAS_CCC_LCCC_WORD)==0 || (*(mapping-1)&0xff00)==0; -} - -/* - * Finds the recomposition result for - * a forward-combining "lead" character, - * specified with a pointer to its compositions list, - * and a backward-combining "trail" character. - * - * If the lead and trail characters combine, then this function returns - * the following "compositeAndFwd" value: - * Bits 21..1 composite character - * Bit 0 set if the composite is a forward-combining starter - * otherwise it returns -1. - * - * The compositions list has (trail, compositeAndFwd) pair entries, - * encoded as either pairs or triples of 16-bit units. - * The last entry has the high bit of its first unit set. - * - * The list is sorted by ascending trail characters (there are no duplicates). - * A linear search is used. - * - * See normalizer2impl.h for a more detailed description - * of the compositions list format. - */ -int32_t Normalizer2Impl::combine(const uint16_t *list, UChar32 trail) { - uint16_t key1, firstUnit; - if(trail(firstUnit=*list)) { - list+=2+(firstUnit&COMP_1_TRIPLE); - } - if(key1==(firstUnit&COMP_1_TRAIL_MASK)) { - if(firstUnit&COMP_1_TRIPLE) { - return ((int32_t)list[1]<<16)|list[2]; - } else { - return list[1]; - } - } - } else { - // trail character is 3400..10FFFF - // result entry has 3 units - key1=(uint16_t)(COMP_1_TRAIL_LIMIT+ - (((trail>>COMP_1_TRAIL_SHIFT))& - ~COMP_1_TRIPLE)); - uint16_t key2=(uint16_t)(trail<(firstUnit=*list)) { - list+=2+(firstUnit&COMP_1_TRIPLE); - } else if(key1==(firstUnit&COMP_1_TRAIL_MASK)) { - if(key2>(secondUnit=list[1])) { - if(firstUnit&COMP_1_LAST_TUPLE) { - break; - } else { - list+=3; - } - } else if(key2==(secondUnit&COMP_2_TRAIL_MASK)) { - return ((int32_t)(secondUnit&~COMP_2_TRAIL_MASK)<<16)|list[2]; - } else { - break; - } - } else { - break; - } - } - } - return -1; -} - -/** - * @param list some character's compositions list - * @param set recursively receives the composites from these compositions - */ -void Normalizer2Impl::addComposites(const uint16_t *list, UnicodeSet &set) const { - uint16_t firstUnit; - int32_t compositeAndFwd; - do { - firstUnit=*list; - if((firstUnit&COMP_1_TRIPLE)==0) { - compositeAndFwd=list[1]; - list+=2; - } else { - compositeAndFwd=(((int32_t)list[1]&~COMP_2_TRAIL_MASK)<<16)|list[2]; - list+=3; - } - UChar32 composite=compositeAndFwd>>1; - if((compositeAndFwd&1)!=0) { - addComposites(getCompositionsListForComposite(getRawNorm16(composite)), set); - } - set.add(composite); - } while((firstUnit&COMP_1_LAST_TUPLE)==0); -} - -/* - * Recomposes the buffer text starting at recomposeStartIndex - * (which is in NFD - decomposed and canonically ordered), - * and truncates the buffer contents. - * - * Note that recomposition never lengthens the text: - * Any character consists of either one or two code units; - * a composition may contain at most one more code unit than the original starter, - * while the combining mark that is removed has at least one code unit. - */ -void Normalizer2Impl::recompose(ReorderingBuffer &buffer, int32_t recomposeStartIndex, - UBool onlyContiguous) const { - UChar *p=buffer.getStart()+recomposeStartIndex; - UChar *limit=buffer.getLimit(); - if(p==limit) { - return; - } - - UChar *starter, *pRemove, *q, *r; - const uint16_t *compositionsList; - UChar32 c, compositeAndFwd; - uint16_t norm16; - uint8_t cc, prevCC; - UBool starterIsSupplementary; - - // Some of the following variables are not used until we have a forward-combining starter - // and are only initialized now to avoid compiler warnings. - compositionsList=NULL; // used as indicator for whether we have a forward-combining starter - starter=NULL; - starterIsSupplementary=false; - prevCC=0; - - for(;;) { - UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, p, limit, c, norm16); - cc=getCCFromYesOrMaybe(norm16); - if( // this character combines backward and - isMaybe(norm16) && - // we have seen a starter that combines forward and - compositionsList!=NULL && - // the backward-combining character is not blocked - (prevCC=0) { - // The starter and the combining mark (c) do combine. - UChar32 composite=compositeAndFwd>>1; - - // Replace the starter with the composite, remove the combining mark. - pRemove=p-U16_LENGTH(c); // pRemove & p: start & limit of the combining mark - if(starterIsSupplementary) { - if(U_IS_SUPPLEMENTARY(composite)) { - // both are supplementary - starter[0]=U16_LEAD(composite); - starter[1]=U16_TRAIL(composite); - } else { - *starter=(UChar)composite; - // The composite is shorter than the starter, - // move the intermediate characters forward one. - starterIsSupplementary=false; - q=starter+1; - r=q+1; - while(rminYesNo) { // composite 'a' has both mapping & compositions list - list+= // mapping pointer - 1+ // +1 to skip the first unit with the mapping length - (*list&MAPPING_LENGTH_MASK); // + mapping length - } - } - } else if(norm16>1; -#else - int32_t compositeAndFwd=combine(list, b); - return compositeAndFwd>=0 ? compositeAndFwd>>1 : U_SENTINEL; -#endif -} - -// Very similar to composeQuickCheck(): Make the same changes in both places if relevant. -// doCompose: normalize -// !doCompose: isNormalized (buffer must be empty and initialized) -UBool -Normalizer2Impl::compose(const UChar *src, const UChar *limit, - UBool onlyContiguous, - UBool doCompose, - ReorderingBuffer &buffer, - UErrorCode &errorCode) const { - const UChar *prevBoundary=src; - UChar32 minNoMaybeCP=minCompNoMaybeCP; - if(limit==NULL) { - src=copyLowPrefixFromNulTerminated(src, minNoMaybeCP, - doCompose ? &buffer : NULL, - errorCode); - if(U_FAILURE(errorCode)) { - return false; - } - limit=u_strchr(src, 0); - if (prevBoundary != src) { - if (hasCompBoundaryAfter(*(src-1), onlyContiguous)) { - prevBoundary = src; - } else { - buffer.removeSuffix(1); - prevBoundary = --src; - } - } - } - - for (;;) { - // Fast path: Scan over a sequence of characters below the minimum "no or maybe" code point, - // or with (compYes && ccc==0) properties. - const UChar *prevSrc; - UChar32 c = 0; - uint16_t norm16 = 0; - for (;;) { - if (src == limit) { - if (prevBoundary != limit && doCompose) { - buffer.appendZeroCC(prevBoundary, limit, errorCode); - } - return true; - } - if( (c=*src)=minNoNo. - // The current character is either a "noNo" (has a mapping) - // or a "maybeYes" (combines backward) - // or a "yesYes" with ccc!=0. - // It is not a Hangul syllable or Jamo L because those have "yes" properties. - - // Medium-fast path: Handle cases that do not require full decomposition and recomposition. - if (!isMaybeOrNonZeroCC(norm16)) { // minNoNo <= norm16 < minMaybeYes - if (!doCompose) { - return false; - } - // Fast path for mapping a character that is immediately surrounded by boundaries. - // In this case, we need not decompose around the current character. - if (isDecompNoAlgorithmic(norm16)) { - // Maps to a single isCompYesAndZeroCC character - // which also implies hasCompBoundaryBefore. - if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) || - hasCompBoundaryBefore(src, limit)) { - if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) { - break; - } - if(!buffer.append(mapAlgorithmic(c, norm16), 0, errorCode)) { - break; - } - prevBoundary = src; - continue; - } - } else if (norm16 < minNoNoCompBoundaryBefore) { - // The mapping is comp-normalized which also implies hasCompBoundaryBefore. - if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) || - hasCompBoundaryBefore(src, limit)) { - if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) { - break; - } - const UChar *mapping = reinterpret_cast(getMapping(norm16)); - int32_t length = *mapping++ & MAPPING_LENGTH_MASK; - if(!buffer.appendZeroCC(mapping, mapping + length, errorCode)) { - break; - } - prevBoundary = src; - continue; - } - } else if (norm16 >= minNoNoEmpty) { - // The current character maps to nothing. - // Simply omit it from the output if there is a boundary before _or_ after it. - // The character itself implies no boundaries. - if (hasCompBoundaryBefore(src, limit) || - hasCompBoundaryAfter(prevBoundary, prevSrc, onlyContiguous)) { - if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) { - break; - } - prevBoundary = src; - continue; - } - } - // Other "noNo" type, or need to examine more text around this character: - // Fall through to the slow path. - } else if (isJamoVT(norm16) && prevBoundary != prevSrc) { - UChar prev=*(prevSrc-1); - if(c= 0) { - UChar32 syllable = Hangul::HANGUL_BASE + - (l*Hangul::JAMO_V_COUNT + (c-Hangul::JAMO_V_BASE)) * - Hangul::JAMO_T_COUNT + t; - --prevSrc; // Replace the Jamo L as well. - if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) { - break; - } - if(!buffer.appendBMP((UChar)syllable, 0, errorCode)) { - break; - } - prevBoundary = src; - continue; - } - // If we see L+V+x where x!=T then we drop to the slow path, - // decompose and recompose. - // This is to deal with NFKC finding normal L and V but a - // compatibility variant of a T. - // We need to either fully compose that combination here - // (which would complicate the code and may not work with strange custom data) - // or use the slow path. - } - } else if (Hangul::isHangulLV(prev)) { - // The current character is a Jamo Trailing consonant, - // compose with previous Hangul LV that does not contain a Jamo T. - if (!doCompose) { - return false; - } - UChar32 syllable = prev + c - Hangul::JAMO_T_BASE; - --prevSrc; // Replace the Hangul LV as well. - if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) { - break; - } - if(!buffer.appendBMP((UChar)syllable, 0, errorCode)) { - break; - } - prevBoundary = src; - continue; - } - // No matching context, or may need to decompose surrounding text first: - // Fall through to the slow path. - } else if (norm16 > JAMO_VT) { // norm16 >= MIN_YES_YES_WITH_CC - // One or more combining marks that do not combine-back: - // Check for canonical order, copy unchanged if ok and - // if followed by a character with a boundary-before. - uint8_t cc = getCCFromNormalYesOrMaybe(norm16); // cc!=0 - if (onlyContiguous /* FCC */ && getPreviousTrailCC(prevBoundary, prevSrc) > cc) { - // Fails FCD test, need to decompose and contiguously recompose. - if (!doCompose) { - return false; - } - } else { - // If !onlyContiguous (not FCC), then we ignore the tccc of - // the previous character which passed the quick check "yes && ccc==0" test. - const UChar *nextSrc; - uint16_t n16; - for (;;) { - if (src == limit) { - if (doCompose) { - buffer.appendZeroCC(prevBoundary, limit, errorCode); - } - return true; - } - uint8_t prevCC = cc; - nextSrc = src; - UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, nextSrc, limit, c, n16); - if (n16 >= MIN_YES_YES_WITH_CC) { - cc = getCCFromNormalYesOrMaybe(n16); - if (prevCC > cc) { - if (!doCompose) { - return false; - } - break; - } - } else { - break; - } - src = nextSrc; - } - // src is after the last in-order combining mark. - // If there is a boundary here, then we continue with no change. - if (norm16HasCompBoundaryBefore(n16)) { - if (isCompYesAndZeroCC(n16)) { - src = nextSrc; - } - continue; - } - // Use the slow path. There is no boundary in [prevSrc, src[. - } - } - - // Slow path: Find the nearest boundaries around the current character, - // decompose and recompose. - if (prevBoundary != prevSrc && !norm16HasCompBoundaryBefore(norm16)) { - const UChar *p = prevSrc; - UCPTRIE_FAST_U16_PREV(normTrie, UCPTRIE_16, prevBoundary, p, c, norm16); - if (!norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { - prevSrc = p; - } - } - if (doCompose && prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) { - break; - } - int32_t recomposeStartIndex=buffer.length(); - // We know there is not a boundary here. - decomposeShort(prevSrc, src, false /* !stopAtCompBoundary */, onlyContiguous, - buffer, errorCode); - // Decompose until the next boundary. - src = decomposeShort(src, limit, true /* stopAtCompBoundary */, onlyContiguous, - buffer, errorCode); - if (U_FAILURE(errorCode)) { - break; - } - if ((src - prevSrc) > INT32_MAX) { // guard before buffer.equals() - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return true; - } - recompose(buffer, recomposeStartIndex, onlyContiguous); - if(!doCompose) { - if(!buffer.equals(prevSrc, src)) { - return false; - } - buffer.remove(); - } - prevBoundary=src; - } - return true; -} - -// Very similar to compose(): Make the same changes in both places if relevant. -// pQCResult==NULL: spanQuickCheckYes -// pQCResult!=NULL: quickCheck (*pQCResult must be UNORM_YES) -const UChar * -Normalizer2Impl::composeQuickCheck(const UChar *src, const UChar *limit, - UBool onlyContiguous, - UNormalizationCheckResult *pQCResult) const { - const UChar *prevBoundary=src; - UChar32 minNoMaybeCP=minCompNoMaybeCP; - if(limit==NULL) { - UErrorCode errorCode=U_ZERO_ERROR; - src=copyLowPrefixFromNulTerminated(src, minNoMaybeCP, NULL, errorCode); - limit=u_strchr(src, 0); - if (prevBoundary != src) { - if (hasCompBoundaryAfter(*(src-1), onlyContiguous)) { - prevBoundary = src; - } else { - prevBoundary = --src; - } - } - } - - for(;;) { - // Fast path: Scan over a sequence of characters below the minimum "no or maybe" code point, - // or with (compYes && ccc==0) properties. - const UChar *prevSrc; - UChar32 c = 0; - uint16_t norm16 = 0; - for (;;) { - if(src==limit) { - return src; - } - if( (c=*src)=minNoNo. - // The current character is either a "noNo" (has a mapping) - // or a "maybeYes" (combines backward) - // or a "yesYes" with ccc!=0. - // It is not a Hangul syllable or Jamo L because those have "yes" properties. - - uint16_t prevNorm16 = INERT; - if (prevBoundary != prevSrc) { - if (norm16HasCompBoundaryBefore(norm16)) { - prevBoundary = prevSrc; - } else { - const UChar *p = prevSrc; - uint16_t n16; - UCPTRIE_FAST_U16_PREV(normTrie, UCPTRIE_16, prevBoundary, p, c, n16); - if (norm16HasCompBoundaryAfter(n16, onlyContiguous)) { - prevBoundary = prevSrc; - } else { - prevBoundary = p; - prevNorm16 = n16; - } - } - } - - if(isMaybeOrNonZeroCC(norm16)) { - uint8_t cc=getCCFromYesOrMaybe(norm16); - if (onlyContiguous /* FCC */ && cc != 0 && - getTrailCCFromCompYesAndZeroCC(prevNorm16) > cc) { - // The [prevBoundary..prevSrc[ character - // passed the quick check "yes && ccc==0" test - // but is out of canonical order with the current combining mark. - } else { - // If !onlyContiguous (not FCC), then we ignore the tccc of - // the previous character which passed the quick check "yes && ccc==0" test. - const UChar *nextSrc; - for (;;) { - if (norm16 < MIN_YES_YES_WITH_CC) { - if (pQCResult != nullptr) { - *pQCResult = UNORM_MAYBE; - } else { - return prevBoundary; - } - } - if (src == limit) { - return src; - } - uint8_t prevCC = cc; - nextSrc = src; - UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, nextSrc, limit, c, norm16); - if (isMaybeOrNonZeroCC(norm16)) { - cc = getCCFromYesOrMaybe(norm16); - if (!(prevCC <= cc || cc == 0)) { - break; - } - } else { - break; - } - src = nextSrc; - } - // src is after the last in-order combining mark. - if (isCompYesAndZeroCC(norm16)) { - prevBoundary = src; - src = nextSrc; - continue; - } - } - } - if(pQCResult!=NULL) { - *pQCResult=UNORM_NO; - } - return prevBoundary; - } -} - -void Normalizer2Impl::composeAndAppend(const UChar *src, const UChar *limit, - UBool doCompose, - UBool onlyContiguous, - UnicodeString &safeMiddle, - ReorderingBuffer &buffer, - UErrorCode &errorCode) const { - if(!buffer.isEmpty()) { - const UChar *firstStarterInSrc=findNextCompBoundary(src, limit, onlyContiguous); - if(src!=firstStarterInSrc) { - const UChar *lastStarterInDest=findPreviousCompBoundary(buffer.getStart(), - buffer.getLimit(), onlyContiguous); - int32_t destSuffixLength=(int32_t)(buffer.getLimit()-lastStarterInDest); - UnicodeString middle(lastStarterInDest, destSuffixLength); - buffer.removeSuffix(destSuffixLength); - safeMiddle=middle; - middle.append(src, (int32_t)(firstStarterInSrc-src)); - const UChar *middleStart=middle.getBuffer(); - compose(middleStart, middleStart+middle.length(), onlyContiguous, - true, buffer, errorCode); - if(U_FAILURE(errorCode)) { - return; - } - src=firstStarterInSrc; - } - } - if(doCompose) { - compose(src, limit, onlyContiguous, true, buffer, errorCode); - } else { - if(limit==NULL) { // appendZeroCC() needs limit!=NULL - limit=u_strchr(src, 0); - } - buffer.appendZeroCC(src, limit, errorCode); - } -} - -UBool -Normalizer2Impl::composeUTF8(uint32_t options, UBool onlyContiguous, - const uint8_t *src, const uint8_t *limit, - ByteSink *sink, Edits *edits, UErrorCode &errorCode) const { - U_ASSERT(limit != nullptr); - UnicodeString s16; - uint8_t minNoMaybeLead = leadByteForCP(minCompNoMaybeCP); - const uint8_t *prevBoundary = src; - - for (;;) { - // Fast path: Scan over a sequence of characters below the minimum "no or maybe" code point, - // or with (compYes && ccc==0) properties. - const uint8_t *prevSrc; - uint16_t norm16 = 0; - for (;;) { - if (src == limit) { - if (prevBoundary != limit && sink != nullptr) { - ByteSinkUtil::appendUnchanged(prevBoundary, limit, - *sink, options, edits, errorCode); - } - return true; - } - if (*src < minNoMaybeLead) { - ++src; - } else { - prevSrc = src; - UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, src, limit, norm16); - if (!isCompYesAndZeroCC(norm16)) { - break; - } - } - } - // isCompYesAndZeroCC(norm16) is false, that is, norm16>=minNoNo. - // The current character is either a "noNo" (has a mapping) - // or a "maybeYes" (combines backward) - // or a "yesYes" with ccc!=0. - // It is not a Hangul syllable or Jamo L because those have "yes" properties. - - // Medium-fast path: Handle cases that do not require full decomposition and recomposition. - if (!isMaybeOrNonZeroCC(norm16)) { // minNoNo <= norm16 < minMaybeYes - if (sink == nullptr) { - return false; - } - // Fast path for mapping a character that is immediately surrounded by boundaries. - // In this case, we need not decompose around the current character. - if (isDecompNoAlgorithmic(norm16)) { - // Maps to a single isCompYesAndZeroCC character - // which also implies hasCompBoundaryBefore. - if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) || - hasCompBoundaryBefore(src, limit)) { - if (prevBoundary != prevSrc && - !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, - *sink, options, edits, errorCode)) { - break; - } - appendCodePointDelta(prevSrc, src, getAlgorithmicDelta(norm16), *sink, edits); - prevBoundary = src; - continue; - } - } else if (norm16 < minNoNoCompBoundaryBefore) { - // The mapping is comp-normalized which also implies hasCompBoundaryBefore. - if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) || - hasCompBoundaryBefore(src, limit)) { - if (prevBoundary != prevSrc && - !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, - *sink, options, edits, errorCode)) { - break; - } - const uint16_t *mapping = getMapping(norm16); - int32_t length = *mapping++ & MAPPING_LENGTH_MASK; - if (!ByteSinkUtil::appendChange(prevSrc, src, (const UChar *)mapping, length, - *sink, edits, errorCode)) { - break; - } - prevBoundary = src; - continue; - } - } else if (norm16 >= minNoNoEmpty) { - // The current character maps to nothing. - // Simply omit it from the output if there is a boundary before _or_ after it. - // The character itself implies no boundaries. - if (hasCompBoundaryBefore(src, limit) || - hasCompBoundaryAfter(prevBoundary, prevSrc, onlyContiguous)) { - if (prevBoundary != prevSrc && - !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, - *sink, options, edits, errorCode)) { - break; - } - if (edits != nullptr) { - edits->addReplace((int32_t)(src - prevSrc), 0); - } - prevBoundary = src; - continue; - } - } - // Other "noNo" type, or need to examine more text around this character: - // Fall through to the slow path. - } else if (isJamoVT(norm16)) { - // Jamo L: E1 84 80..92 - // Jamo V: E1 85 A1..B5 - // Jamo T: E1 86 A8..E1 87 82 - U_ASSERT((src - prevSrc) == 3 && *prevSrc == 0xe1); - UChar32 prev = previousHangulOrJamo(prevBoundary, prevSrc); - if (prevSrc[1] == 0x85) { - // The current character is a Jamo Vowel, - // compose with previous Jamo L and following Jamo T. - UChar32 l = prev - Hangul::JAMO_L_BASE; - if ((uint32_t)l < Hangul::JAMO_L_COUNT) { - if (sink == nullptr) { - return false; - } - int32_t t = getJamoTMinusBase(src, limit); - if (t >= 0) { - // The next character is a Jamo T. - src += 3; - } else if (hasCompBoundaryBefore(src, limit)) { - // No Jamo T follows, not even via decomposition. - t = 0; - } - if (t >= 0) { - UChar32 syllable = Hangul::HANGUL_BASE + - (l*Hangul::JAMO_V_COUNT + (prevSrc[2]-0xa1)) * - Hangul::JAMO_T_COUNT + t; - prevSrc -= 3; // Replace the Jamo L as well. - if (prevBoundary != prevSrc && - !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, - *sink, options, edits, errorCode)) { - break; - } - ByteSinkUtil::appendCodePoint(prevSrc, src, syllable, *sink, edits); - prevBoundary = src; - continue; - } - // If we see L+V+x where x!=T then we drop to the slow path, - // decompose and recompose. - // This is to deal with NFKC finding normal L and V but a - // compatibility variant of a T. - // We need to either fully compose that combination here - // (which would complicate the code and may not work with strange custom data) - // or use the slow path. - } - } else if (Hangul::isHangulLV(prev)) { - // The current character is a Jamo Trailing consonant, - // compose with previous Hangul LV that does not contain a Jamo T. - if (sink == nullptr) { - return false; - } - UChar32 syllable = prev + getJamoTMinusBase(prevSrc, src); - prevSrc -= 3; // Replace the Hangul LV as well. - if (prevBoundary != prevSrc && - !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, - *sink, options, edits, errorCode)) { - break; - } - ByteSinkUtil::appendCodePoint(prevSrc, src, syllable, *sink, edits); - prevBoundary = src; - continue; - } - // No matching context, or may need to decompose surrounding text first: - // Fall through to the slow path. - } else if (norm16 > JAMO_VT) { // norm16 >= MIN_YES_YES_WITH_CC - // One or more combining marks that do not combine-back: - // Check for canonical order, copy unchanged if ok and - // if followed by a character with a boundary-before. - uint8_t cc = getCCFromNormalYesOrMaybe(norm16); // cc!=0 - if (onlyContiguous /* FCC */ && getPreviousTrailCC(prevBoundary, prevSrc) > cc) { - // Fails FCD test, need to decompose and contiguously recompose. - if (sink == nullptr) { - return false; - } - } else { - // If !onlyContiguous (not FCC), then we ignore the tccc of - // the previous character which passed the quick check "yes && ccc==0" test. - const uint8_t *nextSrc; - uint16_t n16; - for (;;) { - if (src == limit) { - if (sink != nullptr) { - ByteSinkUtil::appendUnchanged(prevBoundary, limit, - *sink, options, edits, errorCode); - } - return true; - } - uint8_t prevCC = cc; - nextSrc = src; - UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, nextSrc, limit, n16); - if (n16 >= MIN_YES_YES_WITH_CC) { - cc = getCCFromNormalYesOrMaybe(n16); - if (prevCC > cc) { - if (sink == nullptr) { - return false; - } - break; - } - } else { - break; - } - src = nextSrc; - } - // src is after the last in-order combining mark. - // If there is a boundary here, then we continue with no change. - if (norm16HasCompBoundaryBefore(n16)) { - if (isCompYesAndZeroCC(n16)) { - src = nextSrc; - } - continue; - } - // Use the slow path. There is no boundary in [prevSrc, src[. - } - } - - // Slow path: Find the nearest boundaries around the current character, - // decompose and recompose. - if (prevBoundary != prevSrc && !norm16HasCompBoundaryBefore(norm16)) { - const uint8_t *p = prevSrc; - UCPTRIE_FAST_U8_PREV(normTrie, UCPTRIE_16, prevBoundary, p, norm16); - if (!norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { - prevSrc = p; - } - } - ReorderingBuffer buffer(*this, s16, errorCode); - if (U_FAILURE(errorCode)) { - break; - } - // We know there is not a boundary here. - decomposeShort(prevSrc, src, STOP_AT_LIMIT, onlyContiguous, - buffer, errorCode); - // Decompose until the next boundary. - src = decomposeShort(src, limit, STOP_AT_COMP_BOUNDARY, onlyContiguous, - buffer, errorCode); - if (U_FAILURE(errorCode)) { - break; - } - if ((src - prevSrc) > INT32_MAX) { // guard before buffer.equals() - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return true; - } - recompose(buffer, 0, onlyContiguous); - if (!buffer.equals(prevSrc, src)) { - if (sink == nullptr) { - return false; - } - if (prevBoundary != prevSrc && - !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, - *sink, options, edits, errorCode)) { - break; - } - if (!ByteSinkUtil::appendChange(prevSrc, src, buffer.getStart(), buffer.length(), - *sink, edits, errorCode)) { - break; - } - prevBoundary = src; - } - } - return true; -} - -UBool Normalizer2Impl::hasCompBoundaryBefore(const UChar *src, const UChar *limit) const { - if (src == limit || *src < minCompNoMaybeCP) { - return true; - } - UChar32 c; - uint16_t norm16; - UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, src, limit, c, norm16); - return norm16HasCompBoundaryBefore(norm16); -} - -UBool Normalizer2Impl::hasCompBoundaryBefore(const uint8_t *src, const uint8_t *limit) const { - if (src == limit) { - return true; - } - uint16_t norm16; - UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, src, limit, norm16); - return norm16HasCompBoundaryBefore(norm16); -} - -UBool Normalizer2Impl::hasCompBoundaryAfter(const UChar *start, const UChar *p, - UBool onlyContiguous) const { - if (start == p) { - return true; - } - UChar32 c; - uint16_t norm16; - UCPTRIE_FAST_U16_PREV(normTrie, UCPTRIE_16, start, p, c, norm16); - return norm16HasCompBoundaryAfter(norm16, onlyContiguous); -} - -UBool Normalizer2Impl::hasCompBoundaryAfter(const uint8_t *start, const uint8_t *p, - UBool onlyContiguous) const { - if (start == p) { - return true; - } - uint16_t norm16; - UCPTRIE_FAST_U8_PREV(normTrie, UCPTRIE_16, start, p, norm16); - return norm16HasCompBoundaryAfter(norm16, onlyContiguous); -} - -const UChar *Normalizer2Impl::findPreviousCompBoundary(const UChar *start, const UChar *p, - UBool onlyContiguous) const { - while (p != start) { - const UChar *codePointLimit = p; - UChar32 c; - uint16_t norm16; - UCPTRIE_FAST_U16_PREV(normTrie, UCPTRIE_16, start, p, c, norm16); - if (norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { - return codePointLimit; - } - if (hasCompBoundaryBefore(c, norm16)) { - return p; - } - } - return p; -} - -const UChar *Normalizer2Impl::findNextCompBoundary(const UChar *p, const UChar *limit, - UBool onlyContiguous) const { - while (p != limit) { - const UChar *codePointStart = p; - UChar32 c; - uint16_t norm16; - UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, p, limit, c, norm16); - if (hasCompBoundaryBefore(c, norm16)) { - return codePointStart; - } - if (norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { - return p; - } - } - return p; -} - -uint8_t Normalizer2Impl::getPreviousTrailCC(const UChar *start, const UChar *p) const { - if (start == p) { - return 0; - } - int32_t i = (int32_t)(p - start); - UChar32 c; - U16_PREV(start, 0, i, c); - return (uint8_t)getFCD16(c); -} - -uint8_t Normalizer2Impl::getPreviousTrailCC(const uint8_t *start, const uint8_t *p) const { - if (start == p) { - return 0; - } - int32_t i = (int32_t)(p - start); - UChar32 c; - U8_PREV(start, 0, i, c); - return (uint8_t)getFCD16(c); -} - -// Note: normalizer2impl.cpp r30982 (2011-nov-27) -// still had getFCDTrie() which built and cached an FCD trie. -// That provided faster access to FCD data than getFCD16FromNormData() -// but required synchronization and consumed some 10kB of heap memory -// in any process that uses FCD (e.g., via collation). -// minDecompNoCP etc. and smallFCD[] are intended to help with any loss of performance, -// at least for ASCII & CJK. - -// Ticket 20907 - The optimizer in MSVC/Visual Studio versions below 16.4 has trouble with this -// function on Windows ARM64. As a work-around, we disable optimizations for this function. -// This work-around could/should be removed once the following versions of Visual Studio are no -// longer supported: All versions of VS2017, and versions of VS2019 below 16.4. -#if (defined(_MSC_VER) && (defined(_M_ARM64)) && (_MSC_VER < 1924)) -#pragma optimize( "", off ) -#endif -// Gets the FCD value from the regular normalization data. -uint16_t Normalizer2Impl::getFCD16FromNormData(UChar32 c) const { - uint16_t norm16=getNorm16(c); - if (norm16 >= limitNoNo) { - if(norm16>=MIN_NORMAL_MAYBE_YES) { - // combining mark - norm16=getCCFromNormalYesOrMaybe(norm16); - return norm16|(norm16<<8); - } else if(norm16>=minMaybeYes) { - return 0; - } else { // isDecompNoAlgorithmic(norm16) - uint16_t deltaTrailCC = norm16 & DELTA_TCCC_MASK; - if (deltaTrailCC <= DELTA_TCCC_1) { - return deltaTrailCC >> OFFSET_SHIFT; - } - // Maps to an isCompYesAndZeroCC. - c=mapAlgorithmic(c, norm16); - norm16=getRawNorm16(c); - } - } - if(norm16<=minYesNo || isHangulLVT(norm16)) { - // no decomposition or Hangul syllable, all zeros - return 0; - } - // c decomposes, get everything from the variable-length extra data - const uint16_t *mapping=getMapping(norm16); - uint16_t firstUnit=*mapping; - norm16=firstUnit>>8; // tccc - if(firstUnit&MAPPING_HAS_CCC_LCCC_WORD) { - norm16|=*(mapping-1)&0xff00; // lccc - } - return norm16; -} -#if (defined(_MSC_VER) && (defined(_M_ARM64)) && (_MSC_VER < 1924)) -#pragma optimize( "", on ) -#endif - -// Dual functionality: -// buffer!=NULL: normalize -// buffer==NULL: isNormalized/quickCheck/spanQuickCheckYes -const UChar * -Normalizer2Impl::makeFCD(const UChar *src, const UChar *limit, - ReorderingBuffer *buffer, - UErrorCode &errorCode) const { - // Tracks the last FCD-safe boundary, before lccc=0 or after properly-ordered tccc<=1. - // Similar to the prevBoundary in the compose() implementation. - const UChar *prevBoundary=src; - int32_t prevFCD16=0; - if(limit==NULL) { - src=copyLowPrefixFromNulTerminated(src, minLcccCP, buffer, errorCode); - if(U_FAILURE(errorCode)) { - return src; - } - if(prevBoundary1) { - --prevBoundary; - } - } - limit=u_strchr(src, 0); - } - - // Note: In this function we use buffer->appendZeroCC() because we track - // the lead and trail combining classes here, rather than leaving it to - // the ReorderingBuffer. - // The exception is the call to decomposeShort() which uses the buffer - // in the normal way. - - const UChar *prevSrc; - UChar32 c=0; - uint16_t fcd16=0; - - for(;;) { - // count code units with lccc==0 - for(prevSrc=src; src!=limit;) { - if((c=*src)appendZeroCC(prevSrc, src, errorCode)) { - break; - } - if(src==limit) { - break; - } - prevBoundary=src; - // We know that the previous character's lccc==0. - if(prevFCD16<0) { - // Fetching the fcd16 value was deferred for this below-minLcccCP code point. - UChar32 prev=~prevFCD16; - if(prev1) { - --prevBoundary; - } - } - } else { - const UChar *p=src-1; - if(U16_IS_TRAIL(*p) && prevSrc

1) { - prevBoundary=p; - } - } - // The start of the current character (c). - prevSrc=src; - } else if(src==limit) { - break; - } - - src+=U16_LENGTH(c); - // The current character (c) at [prevSrc..src[ has a non-zero lead combining class. - // Check for proper order, and decompose locally if necessary. - if((prevFCD16&0xff)<=(fcd16>>8)) { - // proper order: prev tccc <= current lccc - if((fcd16&0xff)<=1) { - prevBoundary=src; - } - if(buffer!=NULL && !buffer->appendZeroCC(c, errorCode)) { - break; - } - prevFCD16=fcd16; - continue; - } else if(buffer==NULL) { - return prevBoundary; // quick check "no" - } else { - /* - * Back out the part of the source that we copied or appended - * already but is now going to be decomposed. - * prevSrc is set to after what was copied/appended. - */ - buffer->removeSuffix((int32_t)(prevSrc-prevBoundary)); - /* - * Find the part of the source that needs to be decomposed, - * up to the next safe boundary. - */ - src=findNextFCDBoundary(src, limit); - /* - * The source text does not fulfill the conditions for FCD. - * Decompose and reorder a limited piece of the text. - */ - decomposeShort(prevBoundary, src, false, false, *buffer, errorCode); - if (U_FAILURE(errorCode)) { - break; - } - prevBoundary=src; - prevFCD16=0; - } - } - return src; -} - -void Normalizer2Impl::makeFCDAndAppend(const UChar *src, const UChar *limit, - UBool doMakeFCD, - UnicodeString &safeMiddle, - ReorderingBuffer &buffer, - UErrorCode &errorCode) const { - if(!buffer.isEmpty()) { - const UChar *firstBoundaryInSrc=findNextFCDBoundary(src, limit); - if(src!=firstBoundaryInSrc) { - const UChar *lastBoundaryInDest=findPreviousFCDBoundary(buffer.getStart(), - buffer.getLimit()); - int32_t destSuffixLength=(int32_t)(buffer.getLimit()-lastBoundaryInDest); - UnicodeString middle(lastBoundaryInDest, destSuffixLength); - buffer.removeSuffix(destSuffixLength); - safeMiddle=middle; - middle.append(src, (int32_t)(firstBoundaryInSrc-src)); - const UChar *middleStart=middle.getBuffer(); - makeFCD(middleStart, middleStart+middle.length(), &buffer, errorCode); - if(U_FAILURE(errorCode)) { - return; - } - src=firstBoundaryInSrc; - } - } - if(doMakeFCD) { - makeFCD(src, limit, &buffer, errorCode); - } else { - if(limit==NULL) { // appendZeroCC() needs limit!=NULL - limit=u_strchr(src, 0); - } - buffer.appendZeroCC(src, limit, errorCode); - } -} - -const UChar *Normalizer2Impl::findPreviousFCDBoundary(const UChar *start, const UChar *p) const { - while(start lpSet(new UnicodeSet, errorCode); - set=lpSet.getAlias(); - if(U_FAILURE(errorCode)) { - return; - } - UChar32 firstOrigin=(UChar32)(canonValue&CANON_VALUE_MASK); - canonValue=(canonValue&~CANON_VALUE_MASK)|CANON_HAS_SET|(uint32_t)canonStartSets.size(); - umutablecptrie_set(mutableTrie, decompLead, canonValue, &errorCode); - canonStartSets.adoptElement(lpSet.orphan(), errorCode); - if (U_FAILURE(errorCode)) { - return; - } - if(firstOrigin!=0) { - set->add(firstOrigin); - } - } else { - set=(UnicodeSet *)canonStartSets[(int32_t)(canonValue&CANON_VALUE_MASK)]; - } - set->add(origin); - } -} - -// C++ class for friend access to private Normalizer2Impl members. -class InitCanonIterData { -public: - static void doInit(Normalizer2Impl *impl, UErrorCode &errorCode); -}; - -U_CDECL_BEGIN - -// UInitOnce instantiation function for CanonIterData -static void U_CALLCONV -initCanonIterData(Normalizer2Impl *impl, UErrorCode &errorCode) { - InitCanonIterData::doInit(impl, errorCode); -} - -U_CDECL_END - -void InitCanonIterData::doInit(Normalizer2Impl *impl, UErrorCode &errorCode) { - U_ASSERT(impl->fCanonIterData == NULL); - impl->fCanonIterData = new CanonIterData(errorCode); - if (impl->fCanonIterData == NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } - if (U_SUCCESS(errorCode)) { - UChar32 start = 0, end; - uint32_t value; - while ((end = ucptrie_getRange(impl->normTrie, start, - UCPMAP_RANGE_FIXED_LEAD_SURROGATES, Normalizer2Impl::INERT, - nullptr, nullptr, &value)) >= 0) { - // Call Normalizer2Impl::makeCanonIterDataFromNorm16() for a range of same-norm16 characters. - if (value != Normalizer2Impl::INERT) { - impl->makeCanonIterDataFromNorm16(start, end, value, *impl->fCanonIterData, errorCode); - } - start = end + 1; - } -#ifdef UCPTRIE_DEBUG - umutablecptrie_setName(impl->fCanonIterData->mutableTrie, "CanonIterData"); -#endif - impl->fCanonIterData->trie = umutablecptrie_buildImmutable( - impl->fCanonIterData->mutableTrie, UCPTRIE_TYPE_SMALL, UCPTRIE_VALUE_BITS_32, &errorCode); - umutablecptrie_close(impl->fCanonIterData->mutableTrie); - impl->fCanonIterData->mutableTrie = nullptr; - } - if (U_FAILURE(errorCode)) { - delete impl->fCanonIterData; - impl->fCanonIterData = NULL; - } -} - -void Normalizer2Impl::makeCanonIterDataFromNorm16(UChar32 start, UChar32 end, const uint16_t norm16, - CanonIterData &newData, - UErrorCode &errorCode) const { - if(isInert(norm16) || (minYesNo<=norm16 && norm16 minYesNo) { - // c decomposes, get everything from the variable-length extra data - const uint16_t *mapping=getMapping(norm16_2); - uint16_t firstUnit=*mapping; - int32_t length=firstUnit&MAPPING_LENGTH_MASK; - if((firstUnit&MAPPING_HAS_CCC_LCCC_WORD)!=0) { - if(c==c2 && (*(mapping-1)&0xff)!=0) { - newValue|=CANON_NOT_SEGMENT_STARTER; // original c has cc!=0 - } - } - // Skip empty mappings (no characters in the decomposition). - if(length!=0) { - ++mapping; // skip over the firstUnit - // add c to first code point's start set - int32_t i=0; - U16_NEXT_UNSAFE(mapping, i, c2); - newData.addToStartSet(c, c2, errorCode); - // Set CANON_NOT_SEGMENT_STARTER for each remaining code point of a - // one-way mapping. A 2-way mapping is possible here after - // intermediate algorithmic mapping. - if(norm16_2>=minNoNo) { - while(i(this); - umtx_initOnce(me->fCanonIterDataInitOnce, &initCanonIterData, me, errorCode); - return U_SUCCESS(errorCode); -} - -int32_t Normalizer2Impl::getCanonValue(UChar32 c) const { - return (int32_t)ucptrie_get(fCanonIterData->trie, c); -} - -const UnicodeSet &Normalizer2Impl::getCanonStartSet(int32_t n) const { - return *(const UnicodeSet *)fCanonIterData->canonStartSets[n]; -} - -UBool Normalizer2Impl::isCanonSegmentStarter(UChar32 c) const { - return getCanonValue(c)>=0; -} - -UBool Normalizer2Impl::getCanonStartSet(UChar32 c, UnicodeSet &set) const { - int32_t canonValue=getCanonValue(c)&~CANON_NOT_SEGMENT_STARTER; - if(canonValue==0) { - return false; - } - set.clear(); - int32_t value=canonValue&CANON_VALUE_MASK; - if((canonValue&CANON_HAS_SET)!=0) { - set.addAll(getCanonStartSet(value)); - } else if(value!=0) { - set.add(value); - } - if((canonValue&CANON_HAS_COMPOSITIONS)!=0) { - uint16_t norm16=getRawNorm16(c); - if(norm16==JAMO_L) { - UChar32 syllable= - (UChar32)(Hangul::HANGUL_BASE+(c-Hangul::JAMO_L_BASE)*Hangul::JAMO_VT_COUNT); - set.add(syllable, syllable+Hangul::JAMO_VT_COUNT-1); - } else { - addComposites(getCompositionsList(norm16), set); - } - } - return true; -} - -U_NAMESPACE_END - -// Normalizer2 data swapping ----------------------------------------------- *** - -U_NAMESPACE_USE - -U_CAPI int32_t U_EXPORT2 -unorm2_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const UDataInfo *pInfo; - int32_t headerSize; - - const uint8_t *inBytes; - uint8_t *outBytes; - - const int32_t *inIndexes; - int32_t indexes[Normalizer2Impl::IX_TOTAL_SIZE+1]; - - int32_t i, offset, nextOffset, size; - - /* udata_swapDataHeader checks the arguments */ - headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - /* check data format and format version */ - pInfo=(const UDataInfo *)((const char *)inData+4); - uint8_t formatVersion0=pInfo->formatVersion[0]; - if(!( - pInfo->dataFormat[0]==0x4e && /* dataFormat="Nrm2" */ - pInfo->dataFormat[1]==0x72 && - pInfo->dataFormat[2]==0x6d && - pInfo->dataFormat[3]==0x32 && - (1<=formatVersion0 && formatVersion0<=4) - )) { - udata_printError(ds, "unorm2_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as Normalizer2 data\n", - pInfo->dataFormat[0], pInfo->dataFormat[1], - pInfo->dataFormat[2], pInfo->dataFormat[3], - pInfo->formatVersion[0]); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - inBytes=(const uint8_t *)inData+headerSize; - outBytes=(uint8_t *)outData+headerSize; - - inIndexes=(const int32_t *)inBytes; - int32_t minIndexesLength; - if(formatVersion0==1) { - minIndexesLength=Normalizer2Impl::IX_MIN_MAYBE_YES+1; - } else if(formatVersion0==2) { - minIndexesLength=Normalizer2Impl::IX_MIN_YES_NO_MAPPINGS_ONLY+1; - } else { - minIndexesLength=Normalizer2Impl::IX_MIN_LCCC_CP+1; - } - - if(length>=0) { - length-=headerSize; - if(length=0) { - if(lengthswapArray32(ds, inBytes, nextOffset-offset, outBytes, pErrorCode); - offset=nextOffset; - - /* swap the trie */ - nextOffset=indexes[Normalizer2Impl::IX_EXTRA_DATA_OFFSET]; - utrie_swapAnyVersion(ds, inBytes+offset, nextOffset-offset, outBytes+offset, pErrorCode); - offset=nextOffset; - - /* swap the uint16_t extraData[] */ - nextOffset=indexes[Normalizer2Impl::IX_SMALL_FCD_OFFSET]; - ds->swapArray16(ds, inBytes+offset, nextOffset-offset, outBytes+offset, pErrorCode); - offset=nextOffset; - - /* no need to swap the uint8_t smallFCD[] (new in formatVersion 2) */ - nextOffset=indexes[Normalizer2Impl::IX_SMALL_FCD_OFFSET+1]; - offset=nextOffset; - - U_ASSERT(offset==size); - } - - return headerSize+size; -} - -#endif // !UCONFIG_NO_NORMALIZATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2009-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: normalizer2impl.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2009nov22 +* created by: Markus W. Scherer +*/ + +// #define UCPTRIE_DEBUG + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_NORMALIZATION + +#include "unicode/bytestream.h" +#include "unicode/edits.h" +#include "unicode/normalizer2.h" +#include "unicode/stringoptions.h" +#include "unicode/ucptrie.h" +#include "unicode/udata.h" +#include "unicode/umutablecptrie.h" +#include "unicode/ustring.h" +#include "unicode/utf16.h" +#include "unicode/utf8.h" +#include "bytesinkutil.h" +#include "cmemory.h" +#include "mutex.h" +#include "normalizer2impl.h" +#include "putilimp.h" +#include "uassert.h" +#include "ucptrie_impl.h" +#include "uset_imp.h" +#include "uvector.h" + +U_NAMESPACE_BEGIN + +namespace { + +/** + * UTF-8 lead byte for minNoMaybeCP. + * Can be lower than the actual lead byte for c. + * Typically U+0300 for NFC/NFD, U+00A0 for NFKC/NFKD, U+0041 for NFKC_Casefold. + */ +inline uint8_t leadByteForCP(UChar32 c) { + if (c <= 0x7f) { + return (uint8_t)c; + } else if (c <= 0x7ff) { + return (uint8_t)(0xc0+(c>>6)); + } else { + // Should not occur because ccc(U+0300)!=0. + return 0xe0; + } +} + +/** + * Returns the code point from one single well-formed UTF-8 byte sequence + * between cpStart and cpLimit. + * + * Trie UTF-8 macros do not assemble whole code points (for efficiency). + * When we do need the code point, we call this function. + * We should not need it for normalization-inert data (norm16==0). + * Illegal sequences yield the error value norm16==0 just like real normalization-inert code points. + */ +UChar32 codePointFromValidUTF8(const uint8_t *cpStart, const uint8_t *cpLimit) { + // Similar to U8_NEXT_UNSAFE(s, i, c). + U_ASSERT(cpStart < cpLimit); + uint8_t c = *cpStart; + switch(cpLimit-cpStart) { + case 1: + return c; + case 2: + return ((c&0x1f)<<6) | (cpStart[1]&0x3f); + case 3: + // no need for (c&0xf) because the upper bits are truncated after <<12 in the cast to (char16_t) + return (char16_t)((c<<12) | ((cpStart[1]&0x3f)<<6) | (cpStart[2]&0x3f)); + case 4: + return ((c&7)<<18) | ((cpStart[1]&0x3f)<<12) | ((cpStart[2]&0x3f)<<6) | (cpStart[3]&0x3f); + default: + UPRV_UNREACHABLE_EXIT; // Should not occur. + } +} + +/** + * Returns the last code point in [start, p[ if it is valid and in U+1000..U+D7FF. + * Otherwise returns a negative value. + */ +UChar32 previousHangulOrJamo(const uint8_t *start, const uint8_t *p) { + if ((p - start) >= 3) { + p -= 3; + uint8_t l = *p; + uint8_t t1, t2; + if (0xe1 <= l && l <= 0xed && + (t1 = (uint8_t)(p[1] - 0x80)) <= 0x3f && + (t2 = (uint8_t)(p[2] - 0x80)) <= 0x3f && + (l < 0xed || t1 <= 0x1f)) { + return ((l & 0xf) << 12) | (t1 << 6) | t2; + } + } + return U_SENTINEL; +} + +/** + * Returns the offset from the Jamo T base if [src, limit[ starts with a single Jamo T code point. + * Otherwise returns a negative value. + */ +int32_t getJamoTMinusBase(const uint8_t *src, const uint8_t *limit) { + // Jamo T: E1 86 A8..E1 87 82 + if ((limit - src) >= 3 && *src == 0xe1) { + if (src[1] == 0x86) { + uint8_t t = src[2]; + // The first Jamo T is U+11A8 but JAMO_T_BASE is 11A7. + // Offset 0 does not correspond to any conjoining Jamo. + if (0xa8 <= t && t <= 0xbf) { + return t - 0xa7; + } + } else if (src[1] == 0x87) { + uint8_t t = src[2]; + if ((int8_t)t <= (int8_t)0x82u) { + return t - (0xa7 - 0x40); + } + } + } + return -1; +} + +void +appendCodePointDelta(const uint8_t *cpStart, const uint8_t *cpLimit, int32_t delta, + ByteSink &sink, Edits *edits) { + char buffer[U8_MAX_LENGTH]; + int32_t length; + int32_t cpLength = (int32_t)(cpLimit - cpStart); + if (cpLength == 1) { + // The builder makes ASCII map to ASCII. + buffer[0] = (uint8_t)(*cpStart + delta); + length = 1; + } else { + int32_t trail = *(cpLimit-1) + delta; + if (0x80 <= trail && trail <= 0xbf) { + // The delta only changes the last trail byte. + --cpLimit; + length = 0; + do { buffer[length++] = *cpStart++; } while (cpStart < cpLimit); + buffer[length++] = (uint8_t)trail; + } else { + // Decode the code point, add the delta, re-encode. + UChar32 c = codePointFromValidUTF8(cpStart, cpLimit) + delta; + length = 0; + U8_APPEND_UNSAFE(buffer, length, c); + } + } + if (edits != nullptr) { + edits->addReplace(cpLength, length); + } + sink.Append(buffer, length); +} + +} // namespace + +// ReorderingBuffer -------------------------------------------------------- *** + +ReorderingBuffer::ReorderingBuffer(const Normalizer2Impl &ni, UnicodeString &dest, + UErrorCode &errorCode) : + impl(ni), str(dest), + start(str.getBuffer(8)), reorderStart(start), limit(start), + remainingCapacity(str.getCapacity()), lastCC(0) { + if (start == nullptr && U_SUCCESS(errorCode)) { + // getBuffer() already did str.setToBogus() + errorCode = U_MEMORY_ALLOCATION_ERROR; + } +} + +UBool ReorderingBuffer::init(int32_t destCapacity, UErrorCode &errorCode) { + int32_t length=str.length(); + start=str.getBuffer(destCapacity); + if(start==nullptr) { + // getBuffer() already did str.setToBogus() + errorCode=U_MEMORY_ALLOCATION_ERROR; + return false; + } + limit=start+length; + remainingCapacity=str.getCapacity()-length; + reorderStart=start; + if(start==limit) { + lastCC=0; + } else { + setIterator(); + lastCC=previousCC(); + // Set reorderStart after the last code point with cc<=1 if there is one. + if(lastCC>1) { + while(previousCC()>1) {} + } + reorderStart=codePointLimit; + } + return true; +} + +UBool ReorderingBuffer::equals(const char16_t *otherStart, const char16_t *otherLimit) const { + int32_t length=(int32_t)(limit-start); + return + length==(int32_t)(otherLimit-otherStart) && + 0==u_memcmp(start, otherStart, length); +} + +UBool ReorderingBuffer::equals(const uint8_t *otherStart, const uint8_t *otherLimit) const { + U_ASSERT((otherLimit - otherStart) <= INT32_MAX); // ensured by caller + int32_t length = (int32_t)(limit - start); + int32_t otherLength = (int32_t)(otherLimit - otherStart); + // For equal strings, UTF-8 is at least as long as UTF-16, and at most three times as long. + if (otherLength < length || (otherLength / 3) > length) { + return false; + } + // Compare valid strings from between normalization boundaries. + // (Invalid sequences are normalization-inert.) + for (int32_t i = 0, j = 0;;) { + if (i >= length) { + return j >= otherLength; + } else if (j >= otherLength) { + return false; + } + // Not at the end of either string yet. + UChar32 c, other; + U16_NEXT_UNSAFE(start, i, c); + U8_NEXT_UNSAFE(otherStart, j, other); + if (c != other) { + return false; + } + } +} + +UBool ReorderingBuffer::appendSupplementary(UChar32 c, uint8_t cc, UErrorCode &errorCode) { + if(remainingCapacity<2 && !resize(2, errorCode)) { + return false; + } + if(lastCC<=cc || cc==0) { + limit[0]=U16_LEAD(c); + limit[1]=U16_TRAIL(c); + limit+=2; + lastCC=cc; + if(cc<=1) { + reorderStart=limit; + } + } else { + insert(c, cc); + } + remainingCapacity-=2; + return true; +} + +UBool ReorderingBuffer::append(const char16_t *s, int32_t length, UBool isNFD, + uint8_t leadCC, uint8_t trailCC, + UErrorCode &errorCode) { + if(length==0) { + return true; + } + if(remainingCapacity=codePointStart) { + return 0; + } + UChar32 c=*--codePointStart; + char16_t c2; + if(U16_IS_TRAIL(c) && startcc;) {} + // insert c at codePointLimit, after the character with prevCC<=cc + char16_t *q=limit; + char16_t *r=limit+=U16_LENGTH(c); + do { + *--r=*--q; + } while(codePointLimit!=q); + writeCodePoint(q, c); + if(cc<=1) { + reorderStart=r; + } +} + +// Normalizer2Impl --------------------------------------------------------- *** + +struct CanonIterData : public UMemory { + CanonIterData(UErrorCode &errorCode); + ~CanonIterData(); + void addToStartSet(UChar32 origin, UChar32 decompLead, UErrorCode &errorCode); + UMutableCPTrie *mutableTrie; + UCPTrie *trie; + UVector canonStartSets; // contains UnicodeSet * +}; + +Normalizer2Impl::~Normalizer2Impl() { + delete fCanonIterData; +} + +void +Normalizer2Impl::init(const int32_t *inIndexes, const UCPTrie *inTrie, + const uint16_t *inExtraData, const uint8_t *inSmallFCD) { + minDecompNoCP = static_cast(inIndexes[IX_MIN_DECOMP_NO_CP]); + minCompNoMaybeCP = static_cast(inIndexes[IX_MIN_COMP_NO_MAYBE_CP]); + minLcccCP = static_cast(inIndexes[IX_MIN_LCCC_CP]); + + minYesNo = static_cast(inIndexes[IX_MIN_YES_NO]); + minYesNoMappingsOnly = static_cast(inIndexes[IX_MIN_YES_NO_MAPPINGS_ONLY]); + minNoNo = static_cast(inIndexes[IX_MIN_NO_NO]); + minNoNoCompBoundaryBefore = static_cast(inIndexes[IX_MIN_NO_NO_COMP_BOUNDARY_BEFORE]); + minNoNoCompNoMaybeCC = static_cast(inIndexes[IX_MIN_NO_NO_COMP_NO_MAYBE_CC]); + minNoNoEmpty = static_cast(inIndexes[IX_MIN_NO_NO_EMPTY]); + limitNoNo = static_cast(inIndexes[IX_LIMIT_NO_NO]); + minMaybeYes = static_cast(inIndexes[IX_MIN_MAYBE_YES]); + U_ASSERT((minMaybeYes & 7) == 0); // 8-aligned for noNoDelta bit fields + centerNoNoDelta = (minMaybeYes >> DELTA_SHIFT) - MAX_DELTA - 1; + + normTrie=inTrie; + + maybeYesCompositions=inExtraData; + extraData=maybeYesCompositions+((MIN_NORMAL_MAYBE_YES-minMaybeYes)>>OFFSET_SHIFT); + + smallFCD=inSmallFCD; +} + +U_CDECL_BEGIN + +static uint32_t U_CALLCONV +segmentStarterMapper(const void * /*context*/, uint32_t value) { + return value&CANON_NOT_SEGMENT_STARTER; +} + +U_CDECL_END + +void +Normalizer2Impl::addLcccChars(UnicodeSet &set) const { + UChar32 start = 0, end; + uint32_t norm16; + while ((end = ucptrie_getRange(normTrie, start, UCPMAP_RANGE_FIXED_LEAD_SURROGATES, INERT, + nullptr, nullptr, &norm16)) >= 0) { + if (norm16 > Normalizer2Impl::MIN_NORMAL_MAYBE_YES && + norm16 != Normalizer2Impl::JAMO_VT) { + set.add(start, end); + } else if (minNoNoCompNoMaybeCC <= norm16 && norm16 < limitNoNo) { + uint16_t fcd16 = getFCD16(start); + if (fcd16 > 0xff) { set.add(start, end); } + } + start = end + 1; + } +} + +void +Normalizer2Impl::addPropertyStarts(const USetAdder *sa, UErrorCode & /*errorCode*/) const { + // Add the start code point of each same-value range of the trie. + UChar32 start = 0, end; + uint32_t value; + while ((end = ucptrie_getRange(normTrie, start, UCPMAP_RANGE_FIXED_LEAD_SURROGATES, INERT, + nullptr, nullptr, &value)) >= 0) { + sa->add(sa->set, start); + if (start != end && isAlgorithmicNoNo((uint16_t)value) && + (value & Normalizer2Impl::DELTA_TCCC_MASK) > Normalizer2Impl::DELTA_TCCC_1) { + // Range of code points with same-norm16-value algorithmic decompositions. + // They might have different non-zero FCD16 values. + uint16_t prevFCD16 = getFCD16(start); + while (++start <= end) { + uint16_t fcd16 = getFCD16(start); + if (fcd16 != prevFCD16) { + sa->add(sa->set, start); + prevFCD16 = fcd16; + } + } + } + start = end + 1; + } + + /* add Hangul LV syllables and LV+1 because of skippables */ + for(char16_t c=Hangul::HANGUL_BASE; cadd(sa->set, c); + sa->add(sa->set, c+1); + } + sa->add(sa->set, Hangul::HANGUL_LIMIT); /* add Hangul+1 to continue with other properties */ +} + +void +Normalizer2Impl::addCanonIterPropertyStarts(const USetAdder *sa, UErrorCode &errorCode) const { + // Add the start code point of each same-value range of the canonical iterator data trie. + if (!ensureCanonIterData(errorCode)) { return; } + // Currently only used for the SEGMENT_STARTER property. + UChar32 start = 0, end; + uint32_t value; + while ((end = ucptrie_getRange(fCanonIterData->trie, start, UCPMAP_RANGE_NORMAL, 0, + segmentStarterMapper, nullptr, &value)) >= 0) { + sa->add(sa->set, start); + start = end + 1; + } +} + +const char16_t * +Normalizer2Impl::copyLowPrefixFromNulTerminated(const char16_t *src, + UChar32 minNeedDataCP, + ReorderingBuffer *buffer, + UErrorCode &errorCode) const { + // Make some effort to support NUL-terminated strings reasonably. + // Take the part of the fast quick check loop that does not look up + // data and check the first part of the string. + // After this prefix, determine the string length to simplify the rest + // of the code. + const char16_t *prevSrc=src; + char16_t c; + while((c=*src++)appendZeroCC(prevSrc, src, errorCode); + } + } + return src; +} + +UnicodeString & +Normalizer2Impl::decompose(const UnicodeString &src, UnicodeString &dest, + UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + dest.setToBogus(); + return dest; + } + const char16_t *sArray=src.getBuffer(); + if(&dest==&src || sArray==nullptr) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + dest.setToBogus(); + return dest; + } + decompose(sArray, sArray+src.length(), dest, src.length(), errorCode); + return dest; +} + +void +Normalizer2Impl::decompose(const char16_t *src, const char16_t *limit, + UnicodeString &dest, + int32_t destLengthEstimate, + UErrorCode &errorCode) const { + if(destLengthEstimate<0 && limit!=nullptr) { + destLengthEstimate=(int32_t)(limit-src); + } + dest.remove(); + ReorderingBuffer buffer(*this, dest); + if(buffer.init(destLengthEstimate, errorCode)) { + decompose(src, limit, &buffer, errorCode); + } +} + +// Dual functionality: +// buffer!=nullptr: normalize +// buffer==nullptr: isNormalized/spanQuickCheckYes +const char16_t * +Normalizer2Impl::decompose(const char16_t *src, const char16_t *limit, + ReorderingBuffer *buffer, + UErrorCode &errorCode) const { + UChar32 minNoCP=minDecompNoCP; + if(limit==nullptr) { + src=copyLowPrefixFromNulTerminated(src, minNoCP, buffer, errorCode); + if(U_FAILURE(errorCode)) { + return src; + } + limit=u_strchr(src, 0); + } + + const char16_t *prevSrc; + UChar32 c=0; + uint16_t norm16=0; + + // only for quick check + const char16_t *prevBoundary=src; + uint8_t prevCC=0; + + for(;;) { + // count code units below the minimum or with irrelevant data for the quick check + for(prevSrc=src; src!=limit;) { + if( (c=*src)appendZeroCC(prevSrc, src, errorCode)) { + break; + } + } else { + prevCC=0; + prevBoundary=src; + } + } + if(src==limit) { + break; + } + + // Check one above-minimum, relevant code point. + src+=U16_LENGTH(c); + if(buffer!=nullptr) { + if(!decompose(c, norm16, *buffer, errorCode)) { + break; + } + } else { + if(isDecompYes(norm16)) { + uint8_t cc=getCCFromYesOrMaybe(norm16); + if(prevCC<=cc || cc==0) { + prevCC=cc; + if(cc<=1) { + prevBoundary=src; + } + continue; + } + } + return prevBoundary; // "no" or cc out of order + } + } + return src; +} + +// Decompose a short piece of text which is likely to contain characters that +// fail the quick check loop and/or where the quick check loop's overhead +// is unlikely to be amortized. +// Called by the compose() and makeFCD() implementations. +const char16_t * +Normalizer2Impl::decomposeShort(const char16_t *src, const char16_t *limit, + UBool stopAtCompBoundary, UBool onlyContiguous, + ReorderingBuffer &buffer, UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { + return nullptr; + } + while(src= limitNoNo) { + if (isMaybeOrNonZeroCC(norm16)) { + return buffer.append(c, getCCFromYesOrMaybe(norm16), errorCode); + } + // Maps to an isCompYesAndZeroCC. + c=mapAlgorithmic(c, norm16); + norm16=getRawNorm16(c); + } + if (norm16 < minYesNo) { + // c does not decompose + return buffer.append(c, 0, errorCode); + } else if(isHangulLV(norm16) || isHangulLVT(norm16)) { + // Hangul syllable: decompose algorithmically + char16_t jamos[3]; + return buffer.appendZeroCC(jamos, jamos+Hangul::decompose(c, jamos), errorCode); + } + // c decomposes, get everything from the variable-length extra data + const uint16_t *mapping=getMapping(norm16); + uint16_t firstUnit=*mapping; + int32_t length=firstUnit&MAPPING_LENGTH_MASK; + uint8_t leadCC, trailCC; + trailCC=(uint8_t)(firstUnit>>8); + if(firstUnit&MAPPING_HAS_CCC_LCCC_WORD) { + leadCC=(uint8_t)(*(mapping-1)>>8); + } else { + leadCC=0; + } + return buffer.append((const char16_t *)mapping+1, length, true, leadCC, trailCC, errorCode); +} + +// Dual functionality: +// sink != nullptr: normalize +// sink == nullptr: isNormalized/spanQuickCheckYes +const uint8_t * +Normalizer2Impl::decomposeUTF8(uint32_t options, + const uint8_t *src, const uint8_t *limit, + ByteSink *sink, Edits *edits, UErrorCode &errorCode) const { + U_ASSERT(limit != nullptr); + UnicodeString s16; + uint8_t minNoLead = leadByteForCP(minDecompNoCP); + + const uint8_t *prevBoundary = src; + // only for quick check + uint8_t prevCC = 0; + + for (;;) { + // Fast path: Scan over a sequence of characters below the minimum "no" code point, + // or with (decompYes && ccc==0) properties. + const uint8_t *fastStart = src; + const uint8_t *prevSrc; + uint16_t norm16 = 0; + + for (;;) { + if (src == limit) { + if (prevBoundary != limit && sink != nullptr) { + ByteSinkUtil::appendUnchanged(prevBoundary, limit, + *sink, options, edits, errorCode); + } + return src; + } + if (*src < minNoLead) { + ++src; + } else { + prevSrc = src; + UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, src, limit, norm16); + if (!isMostDecompYesAndZeroCC(norm16)) { + break; + } + } + } + // isMostDecompYesAndZeroCC(norm16) is false, that is, norm16>=minYesNo, + // and the current character at [prevSrc..src[ is not a common case with cc=0 + // (MIN_NORMAL_MAYBE_YES or JAMO_VT). + // It could still be a maybeYes with cc=0. + if (prevSrc != fastStart) { + // The fast path looped over yes/0 characters before the current one. + if (sink != nullptr && + !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, + *sink, options, edits, errorCode)) { + break; + } + prevBoundary = prevSrc; + prevCC = 0; + } + + // Medium-fast path: Quick check. + if (isMaybeOrNonZeroCC(norm16)) { + // Does not decompose. + uint8_t cc = getCCFromYesOrMaybe(norm16); + if (prevCC <= cc || cc == 0) { + prevCC = cc; + if (cc <= 1) { + if (sink != nullptr && + !ByteSinkUtil::appendUnchanged(prevBoundary, src, + *sink, options, edits, errorCode)) { + break; + } + prevBoundary = src; + } + continue; + } + } + if (sink == nullptr) { + return prevBoundary; // quick check: "no" or cc out of order + } + + // Slow path + // Decompose up to and including the current character. + if (prevBoundary != prevSrc && norm16HasDecompBoundaryBefore(norm16)) { + if (!ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, + *sink, options, edits, errorCode)) { + break; + } + prevBoundary = prevSrc; + } + ReorderingBuffer buffer(*this, s16, errorCode); + if (U_FAILURE(errorCode)) { + break; + } + decomposeShort(prevBoundary, src, STOP_AT_LIMIT, false /* onlyContiguous */, + buffer, errorCode); + // Decompose until the next boundary. + if (buffer.getLastCC() > 1) { + src = decomposeShort(src, limit, STOP_AT_DECOMP_BOUNDARY, false /* onlyContiguous */, + buffer, errorCode); + } + if (U_FAILURE(errorCode)) { + break; + } + if ((src - prevSrc) > INT32_MAX) { // guard before buffer.equals() + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + break; + } + // We already know there was a change if the original character decomposed; + // otherwise compare. + if (isMaybeOrNonZeroCC(norm16) && buffer.equals(prevBoundary, src)) { + if (!ByteSinkUtil::appendUnchanged(prevBoundary, src, + *sink, options, edits, errorCode)) { + break; + } + } else { + if (!ByteSinkUtil::appendChange(prevBoundary, src, buffer.getStart(), buffer.length(), + *sink, edits, errorCode)) { + break; + } + } + prevBoundary = src; + prevCC = 0; + } + return src; +} + +const uint8_t * +Normalizer2Impl::decomposeShort(const uint8_t *src, const uint8_t *limit, + StopAt stopAt, UBool onlyContiguous, + ReorderingBuffer &buffer, UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { + return nullptr; + } + while (src < limit) { + const uint8_t *prevSrc = src; + uint16_t norm16; + UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, src, limit, norm16); + // Get the decomposition and the lead and trail cc's. + UChar32 c = U_SENTINEL; + if (norm16 >= limitNoNo) { + if (isMaybeOrNonZeroCC(norm16)) { + // No comp boundaries around this character. + uint8_t cc = getCCFromYesOrMaybe(norm16); + if (cc == 0 && stopAt == STOP_AT_DECOMP_BOUNDARY) { + return prevSrc; + } + c = codePointFromValidUTF8(prevSrc, src); + if (!buffer.append(c, cc, errorCode)) { + return nullptr; + } + if (stopAt == STOP_AT_DECOMP_BOUNDARY && buffer.getLastCC() <= 1) { + return src; + } + continue; + } + // Maps to an isCompYesAndZeroCC. + if (stopAt != STOP_AT_LIMIT) { + return prevSrc; + } + c = codePointFromValidUTF8(prevSrc, src); + c = mapAlgorithmic(c, norm16); + norm16 = getRawNorm16(c); + } else if (stopAt != STOP_AT_LIMIT && norm16 < minNoNoCompNoMaybeCC) { + return prevSrc; + } + // norm16!=INERT guarantees that [prevSrc, src[ is valid UTF-8. + // We do not see invalid UTF-8 here because + // its norm16==INERT is normalization-inert, + // so it gets copied unchanged in the fast path, + // and we stop the slow path where invalid UTF-8 begins. + // c >= 0 is the result of an algorithmic mapping. + U_ASSERT(c >= 0 || norm16 != INERT); + if (norm16 < minYesNo) { + if (c < 0) { + c = codePointFromValidUTF8(prevSrc, src); + } + // does not decompose + if (!buffer.append(c, 0, errorCode)) { + return nullptr; + } + } else if (isHangulLV(norm16) || isHangulLVT(norm16)) { + // Hangul syllable: decompose algorithmically + if (c < 0) { + c = codePointFromValidUTF8(prevSrc, src); + } + char16_t jamos[3]; + if (!buffer.appendZeroCC(jamos, jamos+Hangul::decompose(c, jamos), errorCode)) { + return nullptr; + } + } else { + // The character decomposes, get everything from the variable-length extra data. + const uint16_t *mapping = getMapping(norm16); + uint16_t firstUnit = *mapping; + int32_t length = firstUnit & MAPPING_LENGTH_MASK; + uint8_t trailCC = (uint8_t)(firstUnit >> 8); + uint8_t leadCC; + if (firstUnit & MAPPING_HAS_CCC_LCCC_WORD) { + leadCC = (uint8_t)(*(mapping-1) >> 8); + } else { + leadCC = 0; + } + if (leadCC == 0 && stopAt == STOP_AT_DECOMP_BOUNDARY) { + return prevSrc; + } + if (!buffer.append((const char16_t *)mapping+1, length, true, leadCC, trailCC, errorCode)) { + return nullptr; + } + } + if ((stopAt == STOP_AT_COMP_BOUNDARY && norm16HasCompBoundaryAfter(norm16, onlyContiguous)) || + (stopAt == STOP_AT_DECOMP_BOUNDARY && buffer.getLastCC() <= 1)) { + return src; + } + } + return src; +} + +const char16_t * +Normalizer2Impl::getDecomposition(UChar32 c, char16_t buffer[4], int32_t &length) const { + uint16_t norm16; + if(c>7)&1)-1; + uint16_t rm0=*rawMapping; + if(rm0<=MAPPING_LENGTH_MASK) { + length=rm0; + return (const char16_t *)rawMapping-rm0; + } else { + // Copy the normal mapping and replace its first two code units with rm0. + buffer[0]=(char16_t)rm0; + u_memcpy(buffer+1, (const char16_t *)mapping+1+2, mLength-2); + length=mLength-1; + return buffer; + } + } else { + length=mLength; + return (const char16_t *)mapping+1; + } +} + +void Normalizer2Impl::decomposeAndAppend(const char16_t *src, const char16_t *limit, + UBool doDecompose, + UnicodeString &safeMiddle, + ReorderingBuffer &buffer, + UErrorCode &errorCode) const { + buffer.copyReorderableSuffixTo(safeMiddle); + if(doDecompose) { + decompose(src, limit, &buffer, errorCode); + return; + } + // Just merge the strings at the boundary. + bool isFirst = true; + uint8_t firstCC = 0, prevCC = 0, cc; + const char16_t *p = src; + while (p != limit) { + const char16_t *codePointStart = p; + UChar32 c; + uint16_t norm16; + UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, p, limit, c, norm16); + if ((cc = getCC(norm16)) == 0) { + p = codePointStart; + break; + } + if (isFirst) { + firstCC = cc; + isFirst = false; + } + prevCC = cc; + } + if(limit==nullptr) { // appendZeroCC() needs limit!=nullptr + limit=u_strchr(p, 0); + } + + if (buffer.append(src, (int32_t)(p - src), false, firstCC, prevCC, errorCode)) { + buffer.appendZeroCC(p, limit, errorCode); + } +} + +UBool Normalizer2Impl::hasDecompBoundaryBefore(UChar32 c) const { + return c < minLcccCP || (c <= 0xffff && !singleLeadMightHaveNonZeroFCD16(c)) || + norm16HasDecompBoundaryBefore(getNorm16(c)); +} + +UBool Normalizer2Impl::norm16HasDecompBoundaryBefore(uint16_t norm16) const { + if (norm16 < minNoNoCompNoMaybeCC) { + return true; + } + if (norm16 >= limitNoNo) { + return norm16 <= MIN_NORMAL_MAYBE_YES || norm16 == JAMO_VT; + } + // c decomposes, get everything from the variable-length extra data + const uint16_t *mapping=getMapping(norm16); + uint16_t firstUnit=*mapping; + // true if leadCC==0 (hasFCDBoundaryBefore()) + return (firstUnit&MAPPING_HAS_CCC_LCCC_WORD)==0 || (*(mapping-1)&0xff00)==0; +} + +UBool Normalizer2Impl::hasDecompBoundaryAfter(UChar32 c) const { + if (c < minDecompNoCP) { + return true; + } + if (c <= 0xffff && !singleLeadMightHaveNonZeroFCD16(c)) { + return true; + } + return norm16HasDecompBoundaryAfter(getNorm16(c)); +} + +UBool Normalizer2Impl::norm16HasDecompBoundaryAfter(uint16_t norm16) const { + if(norm16 <= minYesNo || isHangulLVT(norm16)) { + return true; + } + if (norm16 >= limitNoNo) { + if (isMaybeOrNonZeroCC(norm16)) { + return norm16 <= MIN_NORMAL_MAYBE_YES || norm16 == JAMO_VT; + } + // Maps to an isCompYesAndZeroCC. + return (norm16 & DELTA_TCCC_MASK) <= DELTA_TCCC_1; + } + // c decomposes, get everything from the variable-length extra data + const uint16_t *mapping=getMapping(norm16); + uint16_t firstUnit=*mapping; + // decomp after-boundary: same as hasFCDBoundaryAfter(), + // fcd16<=1 || trailCC==0 + if(firstUnit>0x1ff) { + return false; // trailCC>1 + } + if(firstUnit<=0xff) { + return true; // trailCC==0 + } + // if(trailCC==1) test leadCC==0, same as checking for before-boundary + // true if leadCC==0 (hasFCDBoundaryBefore()) + return (firstUnit&MAPPING_HAS_CCC_LCCC_WORD)==0 || (*(mapping-1)&0xff00)==0; +} + +/* + * Finds the recomposition result for + * a forward-combining "lead" character, + * specified with a pointer to its compositions list, + * and a backward-combining "trail" character. + * + * If the lead and trail characters combine, then this function returns + * the following "compositeAndFwd" value: + * Bits 21..1 composite character + * Bit 0 set if the composite is a forward-combining starter + * otherwise it returns -1. + * + * The compositions list has (trail, compositeAndFwd) pair entries, + * encoded as either pairs or triples of 16-bit units. + * The last entry has the high bit of its first unit set. + * + * The list is sorted by ascending trail characters (there are no duplicates). + * A linear search is used. + * + * See normalizer2impl.h for a more detailed description + * of the compositions list format. + */ +int32_t Normalizer2Impl::combine(const uint16_t *list, UChar32 trail) { + uint16_t key1, firstUnit; + if(trail(firstUnit=*list)) { + list+=2+(firstUnit&COMP_1_TRIPLE); + } + if(key1==(firstUnit&COMP_1_TRAIL_MASK)) { + if(firstUnit&COMP_1_TRIPLE) { + return ((int32_t)list[1]<<16)|list[2]; + } else { + return list[1]; + } + } + } else { + // trail character is 3400..10FFFF + // result entry has 3 units + key1=(uint16_t)(COMP_1_TRAIL_LIMIT+ + (((trail>>COMP_1_TRAIL_SHIFT))& + ~COMP_1_TRIPLE)); + uint16_t key2=(uint16_t)(trail<(firstUnit=*list)) { + list+=2+(firstUnit&COMP_1_TRIPLE); + } else if(key1==(firstUnit&COMP_1_TRAIL_MASK)) { + if(key2>(secondUnit=list[1])) { + if(firstUnit&COMP_1_LAST_TUPLE) { + break; + } else { + list+=3; + } + } else if(key2==(secondUnit&COMP_2_TRAIL_MASK)) { + return ((int32_t)(secondUnit&~COMP_2_TRAIL_MASK)<<16)|list[2]; + } else { + break; + } + } else { + break; + } + } + } + return -1; +} + +/** + * @param list some character's compositions list + * @param set recursively receives the composites from these compositions + */ +void Normalizer2Impl::addComposites(const uint16_t *list, UnicodeSet &set) const { + uint16_t firstUnit; + int32_t compositeAndFwd; + do { + firstUnit=*list; + if((firstUnit&COMP_1_TRIPLE)==0) { + compositeAndFwd=list[1]; + list+=2; + } else { + compositeAndFwd=(((int32_t)list[1]&~COMP_2_TRAIL_MASK)<<16)|list[2]; + list+=3; + } + UChar32 composite=compositeAndFwd>>1; + if((compositeAndFwd&1)!=0) { + addComposites(getCompositionsListForComposite(getRawNorm16(composite)), set); + } + set.add(composite); + } while((firstUnit&COMP_1_LAST_TUPLE)==0); +} + +/* + * Recomposes the buffer text starting at recomposeStartIndex + * (which is in NFD - decomposed and canonically ordered), + * and truncates the buffer contents. + * + * Note that recomposition never lengthens the text: + * Any character consists of either one or two code units; + * a composition may contain at most one more code unit than the original starter, + * while the combining mark that is removed has at least one code unit. + */ +void Normalizer2Impl::recompose(ReorderingBuffer &buffer, int32_t recomposeStartIndex, + UBool onlyContiguous) const { + char16_t *p=buffer.getStart()+recomposeStartIndex; + char16_t *limit=buffer.getLimit(); + if(p==limit) { + return; + } + + char16_t *starter, *pRemove, *q, *r; + const uint16_t *compositionsList; + UChar32 c, compositeAndFwd; + uint16_t norm16; + uint8_t cc, prevCC; + UBool starterIsSupplementary; + + // Some of the following variables are not used until we have a forward-combining starter + // and are only initialized now to avoid compiler warnings. + compositionsList=nullptr; // used as indicator for whether we have a forward-combining starter + starter=nullptr; + starterIsSupplementary=false; + prevCC=0; + + for(;;) { + UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, p, limit, c, norm16); + cc=getCCFromYesOrMaybe(norm16); + if( // this character combines backward and + isMaybe(norm16) && + // we have seen a starter that combines forward and + compositionsList!=nullptr && + // the backward-combining character is not blocked + (prevCC=0) { + // The starter and the combining mark (c) do combine. + UChar32 composite=compositeAndFwd>>1; + + // Replace the starter with the composite, remove the combining mark. + pRemove=p-U16_LENGTH(c); // pRemove & p: start & limit of the combining mark + if(starterIsSupplementary) { + if(U_IS_SUPPLEMENTARY(composite)) { + // both are supplementary + starter[0]=U16_LEAD(composite); + starter[1]=U16_TRAIL(composite); + } else { + *starter=(char16_t)composite; + // The composite is shorter than the starter, + // move the intermediate characters forward one. + starterIsSupplementary=false; + q=starter+1; + r=q+1; + while(rminYesNo) { // composite 'a' has both mapping & compositions list + list+= // mapping pointer + 1+ // +1 to skip the first unit with the mapping length + (*list&MAPPING_LENGTH_MASK); // + mapping length + } + } + } else if(norm16>1; +#else + int32_t compositeAndFwd=combine(list, b); + return compositeAndFwd>=0 ? compositeAndFwd>>1 : U_SENTINEL; +#endif +} + +// Very similar to composeQuickCheck(): Make the same changes in both places if relevant. +// doCompose: normalize +// !doCompose: isNormalized (buffer must be empty and initialized) +UBool +Normalizer2Impl::compose(const char16_t *src, const char16_t *limit, + UBool onlyContiguous, + UBool doCompose, + ReorderingBuffer &buffer, + UErrorCode &errorCode) const { + const char16_t *prevBoundary=src; + UChar32 minNoMaybeCP=minCompNoMaybeCP; + if(limit==nullptr) { + src=copyLowPrefixFromNulTerminated(src, minNoMaybeCP, + doCompose ? &buffer : nullptr, + errorCode); + if(U_FAILURE(errorCode)) { + return false; + } + limit=u_strchr(src, 0); + if (prevBoundary != src) { + if (hasCompBoundaryAfter(*(src-1), onlyContiguous)) { + prevBoundary = src; + } else { + buffer.removeSuffix(1); + prevBoundary = --src; + } + } + } + + for (;;) { + // Fast path: Scan over a sequence of characters below the minimum "no or maybe" code point, + // or with (compYes && ccc==0) properties. + const char16_t *prevSrc; + UChar32 c = 0; + uint16_t norm16 = 0; + for (;;) { + if (src == limit) { + if (prevBoundary != limit && doCompose) { + buffer.appendZeroCC(prevBoundary, limit, errorCode); + } + return true; + } + if( (c=*src)=minNoNo. + // The current character is either a "noNo" (has a mapping) + // or a "maybeYes" (combines backward) + // or a "yesYes" with ccc!=0. + // It is not a Hangul syllable or Jamo L because those have "yes" properties. + + // Medium-fast path: Handle cases that do not require full decomposition and recomposition. + if (!isMaybeOrNonZeroCC(norm16)) { // minNoNo <= norm16 < minMaybeYes + if (!doCompose) { + return false; + } + // Fast path for mapping a character that is immediately surrounded by boundaries. + // In this case, we need not decompose around the current character. + if (isDecompNoAlgorithmic(norm16)) { + // Maps to a single isCompYesAndZeroCC character + // which also implies hasCompBoundaryBefore. + if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) || + hasCompBoundaryBefore(src, limit)) { + if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) { + break; + } + if(!buffer.append(mapAlgorithmic(c, norm16), 0, errorCode)) { + break; + } + prevBoundary = src; + continue; + } + } else if (norm16 < minNoNoCompBoundaryBefore) { + // The mapping is comp-normalized which also implies hasCompBoundaryBefore. + if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) || + hasCompBoundaryBefore(src, limit)) { + if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) { + break; + } + const char16_t *mapping = reinterpret_cast(getMapping(norm16)); + int32_t length = *mapping++ & MAPPING_LENGTH_MASK; + if(!buffer.appendZeroCC(mapping, mapping + length, errorCode)) { + break; + } + prevBoundary = src; + continue; + } + } else if (norm16 >= minNoNoEmpty) { + // The current character maps to nothing. + // Simply omit it from the output if there is a boundary before _or_ after it. + // The character itself implies no boundaries. + if (hasCompBoundaryBefore(src, limit) || + hasCompBoundaryAfter(prevBoundary, prevSrc, onlyContiguous)) { + if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) { + break; + } + prevBoundary = src; + continue; + } + } + // Other "noNo" type, or need to examine more text around this character: + // Fall through to the slow path. + } else if (isJamoVT(norm16) && prevBoundary != prevSrc) { + char16_t prev=*(prevSrc-1); + if(c= 0) { + UChar32 syllable = Hangul::HANGUL_BASE + + (l*Hangul::JAMO_V_COUNT + (c-Hangul::JAMO_V_BASE)) * + Hangul::JAMO_T_COUNT + t; + --prevSrc; // Replace the Jamo L as well. + if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) { + break; + } + if(!buffer.appendBMP((char16_t)syllable, 0, errorCode)) { + break; + } + prevBoundary = src; + continue; + } + // If we see L+V+x where x!=T then we drop to the slow path, + // decompose and recompose. + // This is to deal with NFKC finding normal L and V but a + // compatibility variant of a T. + // We need to either fully compose that combination here + // (which would complicate the code and may not work with strange custom data) + // or use the slow path. + } + } else if (Hangul::isHangulLV(prev)) { + // The current character is a Jamo Trailing consonant, + // compose with previous Hangul LV that does not contain a Jamo T. + if (!doCompose) { + return false; + } + UChar32 syllable = prev + c - Hangul::JAMO_T_BASE; + --prevSrc; // Replace the Hangul LV as well. + if (prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) { + break; + } + if(!buffer.appendBMP((char16_t)syllable, 0, errorCode)) { + break; + } + prevBoundary = src; + continue; + } + // No matching context, or may need to decompose surrounding text first: + // Fall through to the slow path. + } else if (norm16 > JAMO_VT) { // norm16 >= MIN_YES_YES_WITH_CC + // One or more combining marks that do not combine-back: + // Check for canonical order, copy unchanged if ok and + // if followed by a character with a boundary-before. + uint8_t cc = getCCFromNormalYesOrMaybe(norm16); // cc!=0 + if (onlyContiguous /* FCC */ && getPreviousTrailCC(prevBoundary, prevSrc) > cc) { + // Fails FCD test, need to decompose and contiguously recompose. + if (!doCompose) { + return false; + } + } else { + // If !onlyContiguous (not FCC), then we ignore the tccc of + // the previous character which passed the quick check "yes && ccc==0" test. + const char16_t *nextSrc; + uint16_t n16; + for (;;) { + if (src == limit) { + if (doCompose) { + buffer.appendZeroCC(prevBoundary, limit, errorCode); + } + return true; + } + uint8_t prevCC = cc; + nextSrc = src; + UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, nextSrc, limit, c, n16); + if (n16 >= MIN_YES_YES_WITH_CC) { + cc = getCCFromNormalYesOrMaybe(n16); + if (prevCC > cc) { + if (!doCompose) { + return false; + } + break; + } + } else { + break; + } + src = nextSrc; + } + // src is after the last in-order combining mark. + // If there is a boundary here, then we continue with no change. + if (norm16HasCompBoundaryBefore(n16)) { + if (isCompYesAndZeroCC(n16)) { + src = nextSrc; + } + continue; + } + // Use the slow path. There is no boundary in [prevSrc, src[. + } + } + + // Slow path: Find the nearest boundaries around the current character, + // decompose and recompose. + if (prevBoundary != prevSrc && !norm16HasCompBoundaryBefore(norm16)) { + const char16_t *p = prevSrc; + UCPTRIE_FAST_U16_PREV(normTrie, UCPTRIE_16, prevBoundary, p, c, norm16); + if (!norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { + prevSrc = p; + } + } + if (doCompose && prevBoundary != prevSrc && !buffer.appendZeroCC(prevBoundary, prevSrc, errorCode)) { + break; + } + int32_t recomposeStartIndex=buffer.length(); + // We know there is not a boundary here. + decomposeShort(prevSrc, src, false /* !stopAtCompBoundary */, onlyContiguous, + buffer, errorCode); + // Decompose until the next boundary. + src = decomposeShort(src, limit, true /* stopAtCompBoundary */, onlyContiguous, + buffer, errorCode); + if (U_FAILURE(errorCode)) { + break; + } + if ((src - prevSrc) > INT32_MAX) { // guard before buffer.equals() + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return true; + } + recompose(buffer, recomposeStartIndex, onlyContiguous); + if(!doCompose) { + if(!buffer.equals(prevSrc, src)) { + return false; + } + buffer.remove(); + } + prevBoundary=src; + } + return true; +} + +// Very similar to compose(): Make the same changes in both places if relevant. +// pQCResult==nullptr: spanQuickCheckYes +// pQCResult!=nullptr: quickCheck (*pQCResult must be UNORM_YES) +const char16_t * +Normalizer2Impl::composeQuickCheck(const char16_t *src, const char16_t *limit, + UBool onlyContiguous, + UNormalizationCheckResult *pQCResult) const { + const char16_t *prevBoundary=src; + UChar32 minNoMaybeCP=minCompNoMaybeCP; + if(limit==nullptr) { + UErrorCode errorCode=U_ZERO_ERROR; + src=copyLowPrefixFromNulTerminated(src, minNoMaybeCP, nullptr, errorCode); + limit=u_strchr(src, 0); + if (prevBoundary != src) { + if (hasCompBoundaryAfter(*(src-1), onlyContiguous)) { + prevBoundary = src; + } else { + prevBoundary = --src; + } + } + } + + for(;;) { + // Fast path: Scan over a sequence of characters below the minimum "no or maybe" code point, + // or with (compYes && ccc==0) properties. + const char16_t *prevSrc; + UChar32 c = 0; + uint16_t norm16 = 0; + for (;;) { + if(src==limit) { + return src; + } + if( (c=*src)=minNoNo. + // The current character is either a "noNo" (has a mapping) + // or a "maybeYes" (combines backward) + // or a "yesYes" with ccc!=0. + // It is not a Hangul syllable or Jamo L because those have "yes" properties. + + uint16_t prevNorm16 = INERT; + if (prevBoundary != prevSrc) { + if (norm16HasCompBoundaryBefore(norm16)) { + prevBoundary = prevSrc; + } else { + const char16_t *p = prevSrc; + uint16_t n16; + UCPTRIE_FAST_U16_PREV(normTrie, UCPTRIE_16, prevBoundary, p, c, n16); + if (norm16HasCompBoundaryAfter(n16, onlyContiguous)) { + prevBoundary = prevSrc; + } else { + prevBoundary = p; + prevNorm16 = n16; + } + } + } + + if(isMaybeOrNonZeroCC(norm16)) { + uint8_t cc=getCCFromYesOrMaybe(norm16); + if (onlyContiguous /* FCC */ && cc != 0 && + getTrailCCFromCompYesAndZeroCC(prevNorm16) > cc) { + // The [prevBoundary..prevSrc[ character + // passed the quick check "yes && ccc==0" test + // but is out of canonical order with the current combining mark. + } else { + // If !onlyContiguous (not FCC), then we ignore the tccc of + // the previous character which passed the quick check "yes && ccc==0" test. + const char16_t *nextSrc; + for (;;) { + if (norm16 < MIN_YES_YES_WITH_CC) { + if (pQCResult != nullptr) { + *pQCResult = UNORM_MAYBE; + } else { + return prevBoundary; + } + } + if (src == limit) { + return src; + } + uint8_t prevCC = cc; + nextSrc = src; + UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, nextSrc, limit, c, norm16); + if (isMaybeOrNonZeroCC(norm16)) { + cc = getCCFromYesOrMaybe(norm16); + if (!(prevCC <= cc || cc == 0)) { + break; + } + } else { + break; + } + src = nextSrc; + } + // src is after the last in-order combining mark. + if (isCompYesAndZeroCC(norm16)) { + prevBoundary = src; + src = nextSrc; + continue; + } + } + } + if(pQCResult!=nullptr) { + *pQCResult=UNORM_NO; + } + return prevBoundary; + } +} + +void Normalizer2Impl::composeAndAppend(const char16_t *src, const char16_t *limit, + UBool doCompose, + UBool onlyContiguous, + UnicodeString &safeMiddle, + ReorderingBuffer &buffer, + UErrorCode &errorCode) const { + if(!buffer.isEmpty()) { + const char16_t *firstStarterInSrc=findNextCompBoundary(src, limit, onlyContiguous); + if(src!=firstStarterInSrc) { + const char16_t *lastStarterInDest=findPreviousCompBoundary(buffer.getStart(), + buffer.getLimit(), onlyContiguous); + int32_t destSuffixLength=(int32_t)(buffer.getLimit()-lastStarterInDest); + UnicodeString middle(lastStarterInDest, destSuffixLength); + buffer.removeSuffix(destSuffixLength); + safeMiddle=middle; + middle.append(src, (int32_t)(firstStarterInSrc-src)); + const char16_t *middleStart=middle.getBuffer(); + compose(middleStart, middleStart+middle.length(), onlyContiguous, + true, buffer, errorCode); + if(U_FAILURE(errorCode)) { + return; + } + src=firstStarterInSrc; + } + } + if(doCompose) { + compose(src, limit, onlyContiguous, true, buffer, errorCode); + } else { + if(limit==nullptr) { // appendZeroCC() needs limit!=nullptr + limit=u_strchr(src, 0); + } + buffer.appendZeroCC(src, limit, errorCode); + } +} + +UBool +Normalizer2Impl::composeUTF8(uint32_t options, UBool onlyContiguous, + const uint8_t *src, const uint8_t *limit, + ByteSink *sink, Edits *edits, UErrorCode &errorCode) const { + U_ASSERT(limit != nullptr); + UnicodeString s16; + uint8_t minNoMaybeLead = leadByteForCP(minCompNoMaybeCP); + const uint8_t *prevBoundary = src; + + for (;;) { + // Fast path: Scan over a sequence of characters below the minimum "no or maybe" code point, + // or with (compYes && ccc==0) properties. + const uint8_t *prevSrc; + uint16_t norm16 = 0; + for (;;) { + if (src == limit) { + if (prevBoundary != limit && sink != nullptr) { + ByteSinkUtil::appendUnchanged(prevBoundary, limit, + *sink, options, edits, errorCode); + } + return true; + } + if (*src < minNoMaybeLead) { + ++src; + } else { + prevSrc = src; + UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, src, limit, norm16); + if (!isCompYesAndZeroCC(norm16)) { + break; + } + } + } + // isCompYesAndZeroCC(norm16) is false, that is, norm16>=minNoNo. + // The current character is either a "noNo" (has a mapping) + // or a "maybeYes" (combines backward) + // or a "yesYes" with ccc!=0. + // It is not a Hangul syllable or Jamo L because those have "yes" properties. + + // Medium-fast path: Handle cases that do not require full decomposition and recomposition. + if (!isMaybeOrNonZeroCC(norm16)) { // minNoNo <= norm16 < minMaybeYes + if (sink == nullptr) { + return false; + } + // Fast path for mapping a character that is immediately surrounded by boundaries. + // In this case, we need not decompose around the current character. + if (isDecompNoAlgorithmic(norm16)) { + // Maps to a single isCompYesAndZeroCC character + // which also implies hasCompBoundaryBefore. + if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) || + hasCompBoundaryBefore(src, limit)) { + if (prevBoundary != prevSrc && + !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, + *sink, options, edits, errorCode)) { + break; + } + appendCodePointDelta(prevSrc, src, getAlgorithmicDelta(norm16), *sink, edits); + prevBoundary = src; + continue; + } + } else if (norm16 < minNoNoCompBoundaryBefore) { + // The mapping is comp-normalized which also implies hasCompBoundaryBefore. + if (norm16HasCompBoundaryAfter(norm16, onlyContiguous) || + hasCompBoundaryBefore(src, limit)) { + if (prevBoundary != prevSrc && + !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, + *sink, options, edits, errorCode)) { + break; + } + const uint16_t *mapping = getMapping(norm16); + int32_t length = *mapping++ & MAPPING_LENGTH_MASK; + if (!ByteSinkUtil::appendChange(prevSrc, src, (const char16_t *)mapping, length, + *sink, edits, errorCode)) { + break; + } + prevBoundary = src; + continue; + } + } else if (norm16 >= minNoNoEmpty) { + // The current character maps to nothing. + // Simply omit it from the output if there is a boundary before _or_ after it. + // The character itself implies no boundaries. + if (hasCompBoundaryBefore(src, limit) || + hasCompBoundaryAfter(prevBoundary, prevSrc, onlyContiguous)) { + if (prevBoundary != prevSrc && + !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, + *sink, options, edits, errorCode)) { + break; + } + if (edits != nullptr) { + edits->addReplace((int32_t)(src - prevSrc), 0); + } + prevBoundary = src; + continue; + } + } + // Other "noNo" type, or need to examine more text around this character: + // Fall through to the slow path. + } else if (isJamoVT(norm16)) { + // Jamo L: E1 84 80..92 + // Jamo V: E1 85 A1..B5 + // Jamo T: E1 86 A8..E1 87 82 + U_ASSERT((src - prevSrc) == 3 && *prevSrc == 0xe1); + UChar32 prev = previousHangulOrJamo(prevBoundary, prevSrc); + if (prevSrc[1] == 0x85) { + // The current character is a Jamo Vowel, + // compose with previous Jamo L and following Jamo T. + UChar32 l = prev - Hangul::JAMO_L_BASE; + if ((uint32_t)l < Hangul::JAMO_L_COUNT) { + if (sink == nullptr) { + return false; + } + int32_t t = getJamoTMinusBase(src, limit); + if (t >= 0) { + // The next character is a Jamo T. + src += 3; + } else if (hasCompBoundaryBefore(src, limit)) { + // No Jamo T follows, not even via decomposition. + t = 0; + } + if (t >= 0) { + UChar32 syllable = Hangul::HANGUL_BASE + + (l*Hangul::JAMO_V_COUNT + (prevSrc[2]-0xa1)) * + Hangul::JAMO_T_COUNT + t; + prevSrc -= 3; // Replace the Jamo L as well. + if (prevBoundary != prevSrc && + !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, + *sink, options, edits, errorCode)) { + break; + } + ByteSinkUtil::appendCodePoint(prevSrc, src, syllable, *sink, edits); + prevBoundary = src; + continue; + } + // If we see L+V+x where x!=T then we drop to the slow path, + // decompose and recompose. + // This is to deal with NFKC finding normal L and V but a + // compatibility variant of a T. + // We need to either fully compose that combination here + // (which would complicate the code and may not work with strange custom data) + // or use the slow path. + } + } else if (Hangul::isHangulLV(prev)) { + // The current character is a Jamo Trailing consonant, + // compose with previous Hangul LV that does not contain a Jamo T. + if (sink == nullptr) { + return false; + } + UChar32 syllable = prev + getJamoTMinusBase(prevSrc, src); + prevSrc -= 3; // Replace the Hangul LV as well. + if (prevBoundary != prevSrc && + !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, + *sink, options, edits, errorCode)) { + break; + } + ByteSinkUtil::appendCodePoint(prevSrc, src, syllable, *sink, edits); + prevBoundary = src; + continue; + } + // No matching context, or may need to decompose surrounding text first: + // Fall through to the slow path. + } else if (norm16 > JAMO_VT) { // norm16 >= MIN_YES_YES_WITH_CC + // One or more combining marks that do not combine-back: + // Check for canonical order, copy unchanged if ok and + // if followed by a character with a boundary-before. + uint8_t cc = getCCFromNormalYesOrMaybe(norm16); // cc!=0 + if (onlyContiguous /* FCC */ && getPreviousTrailCC(prevBoundary, prevSrc) > cc) { + // Fails FCD test, need to decompose and contiguously recompose. + if (sink == nullptr) { + return false; + } + } else { + // If !onlyContiguous (not FCC), then we ignore the tccc of + // the previous character which passed the quick check "yes && ccc==0" test. + const uint8_t *nextSrc; + uint16_t n16; + for (;;) { + if (src == limit) { + if (sink != nullptr) { + ByteSinkUtil::appendUnchanged(prevBoundary, limit, + *sink, options, edits, errorCode); + } + return true; + } + uint8_t prevCC = cc; + nextSrc = src; + UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, nextSrc, limit, n16); + if (n16 >= MIN_YES_YES_WITH_CC) { + cc = getCCFromNormalYesOrMaybe(n16); + if (prevCC > cc) { + if (sink == nullptr) { + return false; + } + break; + } + } else { + break; + } + src = nextSrc; + } + // src is after the last in-order combining mark. + // If there is a boundary here, then we continue with no change. + if (norm16HasCompBoundaryBefore(n16)) { + if (isCompYesAndZeroCC(n16)) { + src = nextSrc; + } + continue; + } + // Use the slow path. There is no boundary in [prevSrc, src[. + } + } + + // Slow path: Find the nearest boundaries around the current character, + // decompose and recompose. + if (prevBoundary != prevSrc && !norm16HasCompBoundaryBefore(norm16)) { + const uint8_t *p = prevSrc; + UCPTRIE_FAST_U8_PREV(normTrie, UCPTRIE_16, prevBoundary, p, norm16); + if (!norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { + prevSrc = p; + } + } + ReorderingBuffer buffer(*this, s16, errorCode); + if (U_FAILURE(errorCode)) { + break; + } + // We know there is not a boundary here. + decomposeShort(prevSrc, src, STOP_AT_LIMIT, onlyContiguous, + buffer, errorCode); + // Decompose until the next boundary. + src = decomposeShort(src, limit, STOP_AT_COMP_BOUNDARY, onlyContiguous, + buffer, errorCode); + if (U_FAILURE(errorCode)) { + break; + } + if ((src - prevSrc) > INT32_MAX) { // guard before buffer.equals() + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return true; + } + recompose(buffer, 0, onlyContiguous); + if (!buffer.equals(prevSrc, src)) { + if (sink == nullptr) { + return false; + } + if (prevBoundary != prevSrc && + !ByteSinkUtil::appendUnchanged(prevBoundary, prevSrc, + *sink, options, edits, errorCode)) { + break; + } + if (!ByteSinkUtil::appendChange(prevSrc, src, buffer.getStart(), buffer.length(), + *sink, edits, errorCode)) { + break; + } + prevBoundary = src; + } + } + return true; +} + +UBool Normalizer2Impl::hasCompBoundaryBefore(const char16_t *src, const char16_t *limit) const { + if (src == limit || *src < minCompNoMaybeCP) { + return true; + } + UChar32 c; + uint16_t norm16; + UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, src, limit, c, norm16); + return norm16HasCompBoundaryBefore(norm16); +} + +UBool Normalizer2Impl::hasCompBoundaryBefore(const uint8_t *src, const uint8_t *limit) const { + if (src == limit) { + return true; + } + uint16_t norm16; + UCPTRIE_FAST_U8_NEXT(normTrie, UCPTRIE_16, src, limit, norm16); + return norm16HasCompBoundaryBefore(norm16); +} + +UBool Normalizer2Impl::hasCompBoundaryAfter(const char16_t *start, const char16_t *p, + UBool onlyContiguous) const { + if (start == p) { + return true; + } + UChar32 c; + uint16_t norm16; + UCPTRIE_FAST_U16_PREV(normTrie, UCPTRIE_16, start, p, c, norm16); + return norm16HasCompBoundaryAfter(norm16, onlyContiguous); +} + +UBool Normalizer2Impl::hasCompBoundaryAfter(const uint8_t *start, const uint8_t *p, + UBool onlyContiguous) const { + if (start == p) { + return true; + } + uint16_t norm16; + UCPTRIE_FAST_U8_PREV(normTrie, UCPTRIE_16, start, p, norm16); + return norm16HasCompBoundaryAfter(norm16, onlyContiguous); +} + +const char16_t *Normalizer2Impl::findPreviousCompBoundary(const char16_t *start, const char16_t *p, + UBool onlyContiguous) const { + while (p != start) { + const char16_t *codePointLimit = p; + UChar32 c; + uint16_t norm16; + UCPTRIE_FAST_U16_PREV(normTrie, UCPTRIE_16, start, p, c, norm16); + if (norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { + return codePointLimit; + } + if (hasCompBoundaryBefore(c, norm16)) { + return p; + } + } + return p; +} + +const char16_t *Normalizer2Impl::findNextCompBoundary(const char16_t *p, const char16_t *limit, + UBool onlyContiguous) const { + while (p != limit) { + const char16_t *codePointStart = p; + UChar32 c; + uint16_t norm16; + UCPTRIE_FAST_U16_NEXT(normTrie, UCPTRIE_16, p, limit, c, norm16); + if (hasCompBoundaryBefore(c, norm16)) { + return codePointStart; + } + if (norm16HasCompBoundaryAfter(norm16, onlyContiguous)) { + return p; + } + } + return p; +} + +uint8_t Normalizer2Impl::getPreviousTrailCC(const char16_t *start, const char16_t *p) const { + if (start == p) { + return 0; + } + int32_t i = (int32_t)(p - start); + UChar32 c; + U16_PREV(start, 0, i, c); + return (uint8_t)getFCD16(c); +} + +uint8_t Normalizer2Impl::getPreviousTrailCC(const uint8_t *start, const uint8_t *p) const { + if (start == p) { + return 0; + } + int32_t i = (int32_t)(p - start); + UChar32 c; + U8_PREV(start, 0, i, c); + return (uint8_t)getFCD16(c); +} + +// Note: normalizer2impl.cpp r30982 (2011-nov-27) +// still had getFCDTrie() which built and cached an FCD trie. +// That provided faster access to FCD data than getFCD16FromNormData() +// but required synchronization and consumed some 10kB of heap memory +// in any process that uses FCD (e.g., via collation). +// minDecompNoCP etc. and smallFCD[] are intended to help with any loss of performance, +// at least for ASCII & CJK. + +// Ticket 20907 - The optimizer in MSVC/Visual Studio versions below 16.4 has trouble with this +// function on Windows ARM64. As a work-around, we disable optimizations for this function. +// This work-around could/should be removed once the following versions of Visual Studio are no +// longer supported: All versions of VS2017, and versions of VS2019 below 16.4. +#if (defined(_MSC_VER) && (defined(_M_ARM64)) && (_MSC_VER < 1924)) +#pragma optimize( "", off ) +#endif +// Gets the FCD value from the regular normalization data. +uint16_t Normalizer2Impl::getFCD16FromNormData(UChar32 c) const { + uint16_t norm16=getNorm16(c); + if (norm16 >= limitNoNo) { + if(norm16>=MIN_NORMAL_MAYBE_YES) { + // combining mark + norm16=getCCFromNormalYesOrMaybe(norm16); + return norm16|(norm16<<8); + } else if(norm16>=minMaybeYes) { + return 0; + } else { // isDecompNoAlgorithmic(norm16) + uint16_t deltaTrailCC = norm16 & DELTA_TCCC_MASK; + if (deltaTrailCC <= DELTA_TCCC_1) { + return deltaTrailCC >> OFFSET_SHIFT; + } + // Maps to an isCompYesAndZeroCC. + c=mapAlgorithmic(c, norm16); + norm16=getRawNorm16(c); + } + } + if(norm16<=minYesNo || isHangulLVT(norm16)) { + // no decomposition or Hangul syllable, all zeros + return 0; + } + // c decomposes, get everything from the variable-length extra data + const uint16_t *mapping=getMapping(norm16); + uint16_t firstUnit=*mapping; + norm16=firstUnit>>8; // tccc + if(firstUnit&MAPPING_HAS_CCC_LCCC_WORD) { + norm16|=*(mapping-1)&0xff00; // lccc + } + return norm16; +} +#if (defined(_MSC_VER) && (defined(_M_ARM64)) && (_MSC_VER < 1924)) +#pragma optimize( "", on ) +#endif + +// Dual functionality: +// buffer!=nullptr: normalize +// buffer==nullptr: isNormalized/quickCheck/spanQuickCheckYes +const char16_t * +Normalizer2Impl::makeFCD(const char16_t *src, const char16_t *limit, + ReorderingBuffer *buffer, + UErrorCode &errorCode) const { + // Tracks the last FCD-safe boundary, before lccc=0 or after properly-ordered tccc<=1. + // Similar to the prevBoundary in the compose() implementation. + const char16_t *prevBoundary=src; + int32_t prevFCD16=0; + if(limit==nullptr) { + src=copyLowPrefixFromNulTerminated(src, minLcccCP, buffer, errorCode); + if(U_FAILURE(errorCode)) { + return src; + } + if(prevBoundary1) { + --prevBoundary; + } + } + limit=u_strchr(src, 0); + } + + // Note: In this function we use buffer->appendZeroCC() because we track + // the lead and trail combining classes here, rather than leaving it to + // the ReorderingBuffer. + // The exception is the call to decomposeShort() which uses the buffer + // in the normal way. + + const char16_t *prevSrc; + UChar32 c=0; + uint16_t fcd16=0; + + for(;;) { + // count code units with lccc==0 + for(prevSrc=src; src!=limit;) { + if((c=*src)appendZeroCC(prevSrc, src, errorCode)) { + break; + } + if(src==limit) { + break; + } + prevBoundary=src; + // We know that the previous character's lccc==0. + if(prevFCD16<0) { + // Fetching the fcd16 value was deferred for this below-minLcccCP code point. + UChar32 prev=~prevFCD16; + if(prev1) { + --prevBoundary; + } + } + } else { + const char16_t *p=src-1; + if(U16_IS_TRAIL(*p) && prevSrc

1) { + prevBoundary=p; + } + } + // The start of the current character (c). + prevSrc=src; + } else if(src==limit) { + break; + } + + src+=U16_LENGTH(c); + // The current character (c) at [prevSrc..src[ has a non-zero lead combining class. + // Check for proper order, and decompose locally if necessary. + if((prevFCD16&0xff)<=(fcd16>>8)) { + // proper order: prev tccc <= current lccc + if((fcd16&0xff)<=1) { + prevBoundary=src; + } + if(buffer!=nullptr && !buffer->appendZeroCC(c, errorCode)) { + break; + } + prevFCD16=fcd16; + continue; + } else if(buffer==nullptr) { + return prevBoundary; // quick check "no" + } else { + /* + * Back out the part of the source that we copied or appended + * already but is now going to be decomposed. + * prevSrc is set to after what was copied/appended. + */ + buffer->removeSuffix((int32_t)(prevSrc-prevBoundary)); + /* + * Find the part of the source that needs to be decomposed, + * up to the next safe boundary. + */ + src=findNextFCDBoundary(src, limit); + /* + * The source text does not fulfill the conditions for FCD. + * Decompose and reorder a limited piece of the text. + */ + decomposeShort(prevBoundary, src, false, false, *buffer, errorCode); + if (U_FAILURE(errorCode)) { + break; + } + prevBoundary=src; + prevFCD16=0; + } + } + return src; +} + +void Normalizer2Impl::makeFCDAndAppend(const char16_t *src, const char16_t *limit, + UBool doMakeFCD, + UnicodeString &safeMiddle, + ReorderingBuffer &buffer, + UErrorCode &errorCode) const { + if(!buffer.isEmpty()) { + const char16_t *firstBoundaryInSrc=findNextFCDBoundary(src, limit); + if(src!=firstBoundaryInSrc) { + const char16_t *lastBoundaryInDest=findPreviousFCDBoundary(buffer.getStart(), + buffer.getLimit()); + int32_t destSuffixLength=(int32_t)(buffer.getLimit()-lastBoundaryInDest); + UnicodeString middle(lastBoundaryInDest, destSuffixLength); + buffer.removeSuffix(destSuffixLength); + safeMiddle=middle; + middle.append(src, (int32_t)(firstBoundaryInSrc-src)); + const char16_t *middleStart=middle.getBuffer(); + makeFCD(middleStart, middleStart+middle.length(), &buffer, errorCode); + if(U_FAILURE(errorCode)) { + return; + } + src=firstBoundaryInSrc; + } + } + if(doMakeFCD) { + makeFCD(src, limit, &buffer, errorCode); + } else { + if(limit==nullptr) { // appendZeroCC() needs limit!=nullptr + limit=u_strchr(src, 0); + } + buffer.appendZeroCC(src, limit, errorCode); + } +} + +const char16_t *Normalizer2Impl::findPreviousFCDBoundary(const char16_t *start, const char16_t *p) const { + while(start lpSet(new UnicodeSet, errorCode); + set=lpSet.getAlias(); + if(U_FAILURE(errorCode)) { + return; + } + UChar32 firstOrigin=(UChar32)(canonValue&CANON_VALUE_MASK); + canonValue=(canonValue&~CANON_VALUE_MASK)|CANON_HAS_SET|(uint32_t)canonStartSets.size(); + umutablecptrie_set(mutableTrie, decompLead, canonValue, &errorCode); + canonStartSets.adoptElement(lpSet.orphan(), errorCode); + if (U_FAILURE(errorCode)) { + return; + } + if(firstOrigin!=0) { + set->add(firstOrigin); + } + } else { + set=(UnicodeSet *)canonStartSets[(int32_t)(canonValue&CANON_VALUE_MASK)]; + } + set->add(origin); + } +} + +// C++ class for friend access to private Normalizer2Impl members. +class InitCanonIterData { +public: + static void doInit(Normalizer2Impl *impl, UErrorCode &errorCode); +}; + +U_CDECL_BEGIN + +// UInitOnce instantiation function for CanonIterData +static void U_CALLCONV +initCanonIterData(Normalizer2Impl *impl, UErrorCode &errorCode) { + InitCanonIterData::doInit(impl, errorCode); +} + +U_CDECL_END + +void InitCanonIterData::doInit(Normalizer2Impl *impl, UErrorCode &errorCode) { + U_ASSERT(impl->fCanonIterData == nullptr); + impl->fCanonIterData = new CanonIterData(errorCode); + if (impl->fCanonIterData == nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } + if (U_SUCCESS(errorCode)) { + UChar32 start = 0, end; + uint32_t value; + while ((end = ucptrie_getRange(impl->normTrie, start, + UCPMAP_RANGE_FIXED_LEAD_SURROGATES, Normalizer2Impl::INERT, + nullptr, nullptr, &value)) >= 0) { + // Call Normalizer2Impl::makeCanonIterDataFromNorm16() for a range of same-norm16 characters. + if (value != Normalizer2Impl::INERT) { + impl->makeCanonIterDataFromNorm16(start, end, value, *impl->fCanonIterData, errorCode); + } + start = end + 1; + } +#ifdef UCPTRIE_DEBUG + umutablecptrie_setName(impl->fCanonIterData->mutableTrie, "CanonIterData"); +#endif + impl->fCanonIterData->trie = umutablecptrie_buildImmutable( + impl->fCanonIterData->mutableTrie, UCPTRIE_TYPE_SMALL, UCPTRIE_VALUE_BITS_32, &errorCode); + umutablecptrie_close(impl->fCanonIterData->mutableTrie); + impl->fCanonIterData->mutableTrie = nullptr; + } + if (U_FAILURE(errorCode)) { + delete impl->fCanonIterData; + impl->fCanonIterData = nullptr; + } +} + +void Normalizer2Impl::makeCanonIterDataFromNorm16(UChar32 start, UChar32 end, const uint16_t norm16, + CanonIterData &newData, + UErrorCode &errorCode) const { + if(isInert(norm16) || (minYesNo<=norm16 && norm16 minYesNo) { + // c decomposes, get everything from the variable-length extra data + const uint16_t *mapping=getMapping(norm16_2); + uint16_t firstUnit=*mapping; + int32_t length=firstUnit&MAPPING_LENGTH_MASK; + if((firstUnit&MAPPING_HAS_CCC_LCCC_WORD)!=0) { + if(c==c2 && (*(mapping-1)&0xff)!=0) { + newValue|=CANON_NOT_SEGMENT_STARTER; // original c has cc!=0 + } + } + // Skip empty mappings (no characters in the decomposition). + if(length!=0) { + ++mapping; // skip over the firstUnit + // add c to first code point's start set + int32_t i=0; + U16_NEXT_UNSAFE(mapping, i, c2); + newData.addToStartSet(c, c2, errorCode); + // Set CANON_NOT_SEGMENT_STARTER for each remaining code point of a + // one-way mapping. A 2-way mapping is possible here after + // intermediate algorithmic mapping. + if(norm16_2>=minNoNo) { + while(i(this); + umtx_initOnce(me->fCanonIterDataInitOnce, &initCanonIterData, me, errorCode); + return U_SUCCESS(errorCode); +} + +int32_t Normalizer2Impl::getCanonValue(UChar32 c) const { + return (int32_t)ucptrie_get(fCanonIterData->trie, c); +} + +const UnicodeSet &Normalizer2Impl::getCanonStartSet(int32_t n) const { + return *(const UnicodeSet *)fCanonIterData->canonStartSets[n]; +} + +UBool Normalizer2Impl::isCanonSegmentStarter(UChar32 c) const { + return getCanonValue(c)>=0; +} + +UBool Normalizer2Impl::getCanonStartSet(UChar32 c, UnicodeSet &set) const { + int32_t canonValue=getCanonValue(c)&~CANON_NOT_SEGMENT_STARTER; + if(canonValue==0) { + return false; + } + set.clear(); + int32_t value=canonValue&CANON_VALUE_MASK; + if((canonValue&CANON_HAS_SET)!=0) { + set.addAll(getCanonStartSet(value)); + } else if(value!=0) { + set.add(value); + } + if((canonValue&CANON_HAS_COMPOSITIONS)!=0) { + uint16_t norm16=getRawNorm16(c); + if(norm16==JAMO_L) { + UChar32 syllable= + (UChar32)(Hangul::HANGUL_BASE+(c-Hangul::JAMO_L_BASE)*Hangul::JAMO_VT_COUNT); + set.add(syllable, syllable+Hangul::JAMO_VT_COUNT-1); + } else { + addComposites(getCompositionsList(norm16), set); + } + } + return true; +} + +U_NAMESPACE_END + +// Normalizer2 data swapping ----------------------------------------------- *** + +U_NAMESPACE_USE + +U_CAPI int32_t U_EXPORT2 +unorm2_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const UDataInfo *pInfo; + int32_t headerSize; + + const uint8_t *inBytes; + uint8_t *outBytes; + + const int32_t *inIndexes; + int32_t indexes[Normalizer2Impl::IX_TOTAL_SIZE+1]; + + int32_t i, offset, nextOffset, size; + + /* udata_swapDataHeader checks the arguments */ + headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + /* check data format and format version */ + pInfo=(const UDataInfo *)((const char *)inData+4); + uint8_t formatVersion0=pInfo->formatVersion[0]; + if(!( + pInfo->dataFormat[0]==0x4e && /* dataFormat="Nrm2" */ + pInfo->dataFormat[1]==0x72 && + pInfo->dataFormat[2]==0x6d && + pInfo->dataFormat[3]==0x32 && + (1<=formatVersion0 && formatVersion0<=4) + )) { + udata_printError(ds, "unorm2_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as Normalizer2 data\n", + pInfo->dataFormat[0], pInfo->dataFormat[1], + pInfo->dataFormat[2], pInfo->dataFormat[3], + pInfo->formatVersion[0]); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + inBytes=(const uint8_t *)inData+headerSize; + outBytes=(outData == nullptr) ? nullptr : (uint8_t *)outData+headerSize; + + inIndexes=(const int32_t *)inBytes; + int32_t minIndexesLength; + if(formatVersion0==1) { + minIndexesLength=Normalizer2Impl::IX_MIN_MAYBE_YES+1; + } else if(formatVersion0==2) { + minIndexesLength=Normalizer2Impl::IX_MIN_YES_NO_MAPPINGS_ONLY+1; + } else { + minIndexesLength=Normalizer2Impl::IX_MIN_LCCC_CP+1; + } + + if(length>=0) { + length-=headerSize; + if(length=0) { + if(lengthswapArray32(ds, inBytes, nextOffset-offset, outBytes, pErrorCode); + offset=nextOffset; + + /* swap the trie */ + nextOffset=indexes[Normalizer2Impl::IX_EXTRA_DATA_OFFSET]; + utrie_swapAnyVersion(ds, inBytes+offset, nextOffset-offset, outBytes+offset, pErrorCode); + offset=nextOffset; + + /* swap the uint16_t extraData[] */ + nextOffset=indexes[Normalizer2Impl::IX_SMALL_FCD_OFFSET]; + ds->swapArray16(ds, inBytes+offset, nextOffset-offset, outBytes+offset, pErrorCode); + offset=nextOffset; + + /* no need to swap the uint8_t smallFCD[] (new in formatVersion 2) */ + nextOffset=indexes[Normalizer2Impl::IX_SMALL_FCD_OFFSET+1]; + offset=nextOffset; + + U_ASSERT(offset==size); + } + + return headerSize+size; +} + +#endif // !UCONFIG_NO_NORMALIZATION diff --git a/deps/icu-small/source/common/normalizer2impl.h b/deps/icu-small/source/common/normalizer2impl.h index 449e7783848b91..5e13c8821c9aa3 100644 --- a/deps/icu-small/source/common/normalizer2impl.h +++ b/deps/icu-small/source/common/normalizer2impl.h @@ -1,987 +1,987 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2009-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: normalizer2impl.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2009nov22 -* created by: Markus W. Scherer -*/ - -#ifndef __NORMALIZER2IMPL_H__ -#define __NORMALIZER2IMPL_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_NORMALIZATION - -#include "unicode/normalizer2.h" -#include "unicode/ucptrie.h" -#include "unicode/unistr.h" -#include "unicode/unorm.h" -#include "unicode/utf.h" -#include "unicode/utf16.h" -#include "mutex.h" -#include "udataswp.h" -#include "uset_imp.h" - -// When the nfc.nrm data is *not* hardcoded into the common library -// (with this constant set to 0), -// then it needs to be built into the data package: -// Add nfc.nrm to icu4c/source/data/Makefile.in DAT_FILES_SHORT -#define NORM2_HARDCODE_NFC_DATA 1 - -U_NAMESPACE_BEGIN - -struct CanonIterData; - -class ByteSink; -class Edits; -class InitCanonIterData; -class LcccContext; - -class U_COMMON_API Hangul { -public: - /* Korean Hangul and Jamo constants */ - enum { - JAMO_L_BASE=0x1100, /* "lead" jamo */ - JAMO_L_END=0x1112, - JAMO_V_BASE=0x1161, /* "vowel" jamo */ - JAMO_V_END=0x1175, - JAMO_T_BASE=0x11a7, /* "trail" jamo */ - JAMO_T_END=0x11c2, - - HANGUL_BASE=0xac00, - HANGUL_END=0xd7a3, - - JAMO_L_COUNT=19, - JAMO_V_COUNT=21, - JAMO_T_COUNT=28, - - JAMO_VT_COUNT=JAMO_V_COUNT*JAMO_T_COUNT, - - HANGUL_COUNT=JAMO_L_COUNT*JAMO_V_COUNT*JAMO_T_COUNT, - HANGUL_LIMIT=HANGUL_BASE+HANGUL_COUNT - }; - - static inline UBool isHangul(UChar32 c) { - return HANGUL_BASE<=c && c(INERT) : - UCPTRIE_FAST_GET(normTrie, UCPTRIE_16, c); - } - uint16_t getRawNorm16(UChar32 c) const { return UCPTRIE_FAST_GET(normTrie, UCPTRIE_16, c); } - - UNormalizationCheckResult getCompQuickCheck(uint16_t norm16) const { - if(norm16=MIN_NORMAL_MAYBE_YES) { - return getCCFromNormalYesOrMaybe(norm16); - } - if(norm16> OFFSET_SHIFT); - } - static uint8_t getCCFromYesOrMaybe(uint16_t norm16) { - return norm16>=MIN_NORMAL_MAYBE_YES ? getCCFromNormalYesOrMaybe(norm16) : 0; - } - uint8_t getCCFromYesOrMaybeCP(UChar32 c) const { - if (c < minCompNoMaybeCP) { return 0; } - return getCCFromYesOrMaybe(getNorm16(c)); - } - - /** - * Returns the FCD data for code point c. - * @param c A Unicode code point. - * @return The lccc(c) in bits 15..8 and tccc(c) in bits 7..0. - */ - uint16_t getFCD16(UChar32 c) const { - if(c>8]; - if(bits==0) { return false; } - return (UBool)((bits>>((lead>>5)&7))&1); - } - /** Returns the FCD value from the regular normalization data. */ - uint16_t getFCD16FromNormData(UChar32 c) const; - - /** - * Gets the decomposition for one code point. - * @param c code point - * @param buffer out-only buffer for algorithmic decompositions - * @param length out-only, takes the length of the decomposition, if any - * @return pointer to the decomposition, or NULL if none - */ - const UChar *getDecomposition(UChar32 c, UChar buffer[4], int32_t &length) const; - - /** - * Gets the raw decomposition for one code point. - * @param c code point - * @param buffer out-only buffer for algorithmic decompositions - * @param length out-only, takes the length of the decomposition, if any - * @return pointer to the decomposition, or NULL if none - */ - const UChar *getRawDecomposition(UChar32 c, UChar buffer[30], int32_t &length) const; - - UChar32 composePair(UChar32 a, UChar32 b) const; - - UBool isCanonSegmentStarter(UChar32 c) const; - UBool getCanonStartSet(UChar32 c, UnicodeSet &set) const; - - enum { - // Fixed norm16 values. - MIN_YES_YES_WITH_CC=0xfe02, - JAMO_VT=0xfe00, - MIN_NORMAL_MAYBE_YES=0xfc00, - JAMO_L=2, // offset=1 hasCompBoundaryAfter=false - INERT=1, // offset=0 hasCompBoundaryAfter=true - - // norm16 bit 0 is comp-boundary-after. - HAS_COMP_BOUNDARY_AFTER=1, - OFFSET_SHIFT=1, - - // For algorithmic one-way mappings, norm16 bits 2..1 indicate the - // tccc (0, 1, >1) for quick FCC boundary-after tests. - DELTA_TCCC_0=0, - DELTA_TCCC_1=2, - DELTA_TCCC_GT_1=4, - DELTA_TCCC_MASK=6, - DELTA_SHIFT=3, - - MAX_DELTA=0x40 - }; - - enum { - // Byte offsets from the start of the data, after the generic header. - IX_NORM_TRIE_OFFSET, - IX_EXTRA_DATA_OFFSET, - IX_SMALL_FCD_OFFSET, - IX_RESERVED3_OFFSET, - IX_RESERVED4_OFFSET, - IX_RESERVED5_OFFSET, - IX_RESERVED6_OFFSET, - IX_TOTAL_SIZE, - - // Code point thresholds for quick check codes. - IX_MIN_DECOMP_NO_CP, - IX_MIN_COMP_NO_MAYBE_CP, - - // Norm16 value thresholds for quick check combinations and types of extra data. - - /** Mappings & compositions in [minYesNo..minYesNoMappingsOnly[. */ - IX_MIN_YES_NO, - /** Mappings are comp-normalized. */ - IX_MIN_NO_NO, - IX_LIMIT_NO_NO, - IX_MIN_MAYBE_YES, - - /** Mappings only in [minYesNoMappingsOnly..minNoNo[. */ - IX_MIN_YES_NO_MAPPINGS_ONLY, - /** Mappings are not comp-normalized but have a comp boundary before. */ - IX_MIN_NO_NO_COMP_BOUNDARY_BEFORE, - /** Mappings do not have a comp boundary before. */ - IX_MIN_NO_NO_COMP_NO_MAYBE_CC, - /** Mappings to the empty string. */ - IX_MIN_NO_NO_EMPTY, - - IX_MIN_LCCC_CP, - IX_RESERVED19, - IX_COUNT - }; - - enum { - MAPPING_HAS_CCC_LCCC_WORD=0x80, - MAPPING_HAS_RAW_MAPPING=0x40, - // unused bit 0x20, - MAPPING_LENGTH_MASK=0x1f - }; - - enum { - COMP_1_LAST_TUPLE=0x8000, - COMP_1_TRIPLE=1, - COMP_1_TRAIL_LIMIT=0x3400, - COMP_1_TRAIL_MASK=0x7ffe, - COMP_1_TRAIL_SHIFT=9, // 10-1 for the "triple" bit - COMP_2_TRAIL_SHIFT=6, - COMP_2_TRAIL_MASK=0xffc0 - }; - - // higher-level functionality ------------------------------------------ *** - - // NFD without an NFD Normalizer2 instance. - UnicodeString &decompose(const UnicodeString &src, UnicodeString &dest, - UErrorCode &errorCode) const; - /** - * Decomposes [src, limit[ and writes the result to dest. - * limit can be NULL if src is NUL-terminated. - * destLengthEstimate is the initial dest buffer capacity and can be -1. - */ - void decompose(const UChar *src, const UChar *limit, - UnicodeString &dest, int32_t destLengthEstimate, - UErrorCode &errorCode) const; - - const UChar *decompose(const UChar *src, const UChar *limit, - ReorderingBuffer *buffer, UErrorCode &errorCode) const; - void decomposeAndAppend(const UChar *src, const UChar *limit, - UBool doDecompose, - UnicodeString &safeMiddle, - ReorderingBuffer &buffer, - UErrorCode &errorCode) const; - - /** sink==nullptr: isNormalized()/spanQuickCheckYes() */ - const uint8_t *decomposeUTF8(uint32_t options, - const uint8_t *src, const uint8_t *limit, - ByteSink *sink, Edits *edits, UErrorCode &errorCode) const; - - UBool compose(const UChar *src, const UChar *limit, - UBool onlyContiguous, - UBool doCompose, - ReorderingBuffer &buffer, - UErrorCode &errorCode) const; - const UChar *composeQuickCheck(const UChar *src, const UChar *limit, - UBool onlyContiguous, - UNormalizationCheckResult *pQCResult) const; - void composeAndAppend(const UChar *src, const UChar *limit, - UBool doCompose, - UBool onlyContiguous, - UnicodeString &safeMiddle, - ReorderingBuffer &buffer, - UErrorCode &errorCode) const; - - /** sink==nullptr: isNormalized() */ - UBool composeUTF8(uint32_t options, UBool onlyContiguous, - const uint8_t *src, const uint8_t *limit, - ByteSink *sink, icu::Edits *edits, UErrorCode &errorCode) const; - - const UChar *makeFCD(const UChar *src, const UChar *limit, - ReorderingBuffer *buffer, UErrorCode &errorCode) const; - void makeFCDAndAppend(const UChar *src, const UChar *limit, - UBool doMakeFCD, - UnicodeString &safeMiddle, - ReorderingBuffer &buffer, - UErrorCode &errorCode) const; - - UBool hasDecompBoundaryBefore(UChar32 c) const; - UBool norm16HasDecompBoundaryBefore(uint16_t norm16) const; - UBool hasDecompBoundaryAfter(UChar32 c) const; - UBool norm16HasDecompBoundaryAfter(uint16_t norm16) const; - UBool isDecompInert(UChar32 c) const { return isDecompYesAndZeroCC(getNorm16(c)); } - - UBool hasCompBoundaryBefore(UChar32 c) const { - return c=minMaybeYes; } - static UBool isInert(uint16_t norm16) { return norm16==INERT; } - static UBool isJamoL(uint16_t norm16) { return norm16==JAMO_L; } - static UBool isJamoVT(uint16_t norm16) { return norm16==JAMO_VT; } - uint16_t hangulLVT() const { return minYesNoMappingsOnly|HAS_COMP_BOUNDARY_AFTER; } - UBool isHangulLV(uint16_t norm16) const { return norm16==minYesNo; } - UBool isHangulLVT(uint16_t norm16) const { - return norm16==hangulLVT(); - } - UBool isCompYesAndZeroCC(uint16_t norm16) const { return norm16=MIN_YES_YES_WITH_CC || norm16=limitNoNo; } - - // For use with isCompYes(). - // Perhaps the compiler can combine the two tests for MIN_YES_YES_WITH_CC. - // static uint8_t getCCFromYes(uint16_t norm16) { - // return norm16>=MIN_YES_YES_WITH_CC ? getCCFromNormalYesOrMaybe(norm16) : 0; - // } - uint8_t getCCFromNoNo(uint16_t norm16) const { - const uint16_t *mapping=getMapping(norm16); - if(*mapping&MAPPING_HAS_CCC_LCCC_WORD) { - return (uint8_t)*(mapping-1); - } else { - return 0; - } - } - // requires that the [cpStart..cpLimit[ character passes isCompYesAndZeroCC() - uint8_t getTrailCCFromCompYesAndZeroCC(uint16_t norm16) const { - if(norm16<=minYesNo) { - return 0; // yesYes and Hangul LV have ccc=tccc=0 - } else { - // For Hangul LVT we harmlessly fetch a firstUnit with tccc=0 here. - return (uint8_t)(*getMapping(norm16)>>8); // tccc from yesNo - } - } - uint8_t getPreviousTrailCC(const UChar *start, const UChar *p) const; - uint8_t getPreviousTrailCC(const uint8_t *start, const uint8_t *p) const; - - // Requires algorithmic-NoNo. - UChar32 mapAlgorithmic(UChar32 c, uint16_t norm16) const { - return c+(norm16>>DELTA_SHIFT)-centerNoNoDelta; - } - UChar32 getAlgorithmicDelta(uint16_t norm16) const { - return (norm16>>DELTA_SHIFT)-centerNoNoDelta; - } - - // Requires minYesNo>OFFSET_SHIFT); } - const uint16_t *getCompositionsListForDecompYes(uint16_t norm16) const { - if(norm16>OFFSET_SHIFT); - } - /** - * @param c code point must have compositions - * @return compositions list pointer - */ - const uint16_t *getCompositionsList(uint16_t norm16) const { - return isDecompYes(norm16) ? - getCompositionsListForDecompYes(norm16) : - getCompositionsListForComposite(norm16); - } - - const UChar *copyLowPrefixFromNulTerminated(const UChar *src, - UChar32 minNeedDataCP, - ReorderingBuffer *buffer, - UErrorCode &errorCode) const; - - enum StopAt { STOP_AT_LIMIT, STOP_AT_DECOMP_BOUNDARY, STOP_AT_COMP_BOUNDARY }; - - const UChar *decomposeShort(const UChar *src, const UChar *limit, - UBool stopAtCompBoundary, UBool onlyContiguous, - ReorderingBuffer &buffer, UErrorCode &errorCode) const; - UBool decompose(UChar32 c, uint16_t norm16, - ReorderingBuffer &buffer, UErrorCode &errorCode) const; - - const uint8_t *decomposeShort(const uint8_t *src, const uint8_t *limit, - StopAt stopAt, UBool onlyContiguous, - ReorderingBuffer &buffer, UErrorCode &errorCode) const; - - static int32_t combine(const uint16_t *list, UChar32 trail); - void addComposites(const uint16_t *list, UnicodeSet &set) const; - void recompose(ReorderingBuffer &buffer, int32_t recomposeStartIndex, - UBool onlyContiguous) const; - - UBool hasCompBoundaryBefore(UChar32 c, uint16_t norm16) const { - return c(INERT) : + UCPTRIE_FAST_GET(normTrie, UCPTRIE_16, c); + } + uint16_t getRawNorm16(UChar32 c) const { return UCPTRIE_FAST_GET(normTrie, UCPTRIE_16, c); } + + UNormalizationCheckResult getCompQuickCheck(uint16_t norm16) const { + if(norm16=MIN_NORMAL_MAYBE_YES) { + return getCCFromNormalYesOrMaybe(norm16); + } + if(norm16> OFFSET_SHIFT); + } + static uint8_t getCCFromYesOrMaybe(uint16_t norm16) { + return norm16>=MIN_NORMAL_MAYBE_YES ? getCCFromNormalYesOrMaybe(norm16) : 0; + } + uint8_t getCCFromYesOrMaybeCP(UChar32 c) const { + if (c < minCompNoMaybeCP) { return 0; } + return getCCFromYesOrMaybe(getNorm16(c)); + } + + /** + * Returns the FCD data for code point c. + * @param c A Unicode code point. + * @return The lccc(c) in bits 15..8 and tccc(c) in bits 7..0. + */ + uint16_t getFCD16(UChar32 c) const { + if(c>8]; + if(bits==0) { return false; } + return (UBool)((bits>>((lead>>5)&7))&1); + } + /** Returns the FCD value from the regular normalization data. */ + uint16_t getFCD16FromNormData(UChar32 c) const; + + /** + * Gets the decomposition for one code point. + * @param c code point + * @param buffer out-only buffer for algorithmic decompositions + * @param length out-only, takes the length of the decomposition, if any + * @return pointer to the decomposition, or NULL if none + */ + const char16_t *getDecomposition(UChar32 c, char16_t buffer[4], int32_t &length) const; + + /** + * Gets the raw decomposition for one code point. + * @param c code point + * @param buffer out-only buffer for algorithmic decompositions + * @param length out-only, takes the length of the decomposition, if any + * @return pointer to the decomposition, or NULL if none + */ + const char16_t *getRawDecomposition(UChar32 c, char16_t buffer[30], int32_t &length) const; + + UChar32 composePair(UChar32 a, UChar32 b) const; + + UBool isCanonSegmentStarter(UChar32 c) const; + UBool getCanonStartSet(UChar32 c, UnicodeSet &set) const; + + enum { + // Fixed norm16 values. + MIN_YES_YES_WITH_CC=0xfe02, + JAMO_VT=0xfe00, + MIN_NORMAL_MAYBE_YES=0xfc00, + JAMO_L=2, // offset=1 hasCompBoundaryAfter=false + INERT=1, // offset=0 hasCompBoundaryAfter=true + + // norm16 bit 0 is comp-boundary-after. + HAS_COMP_BOUNDARY_AFTER=1, + OFFSET_SHIFT=1, + + // For algorithmic one-way mappings, norm16 bits 2..1 indicate the + // tccc (0, 1, >1) for quick FCC boundary-after tests. + DELTA_TCCC_0=0, + DELTA_TCCC_1=2, + DELTA_TCCC_GT_1=4, + DELTA_TCCC_MASK=6, + DELTA_SHIFT=3, + + MAX_DELTA=0x40 + }; + + enum { + // Byte offsets from the start of the data, after the generic header. + IX_NORM_TRIE_OFFSET, + IX_EXTRA_DATA_OFFSET, + IX_SMALL_FCD_OFFSET, + IX_RESERVED3_OFFSET, + IX_RESERVED4_OFFSET, + IX_RESERVED5_OFFSET, + IX_RESERVED6_OFFSET, + IX_TOTAL_SIZE, + + // Code point thresholds for quick check codes. + IX_MIN_DECOMP_NO_CP, + IX_MIN_COMP_NO_MAYBE_CP, + + // Norm16 value thresholds for quick check combinations and types of extra data. + + /** Mappings & compositions in [minYesNo..minYesNoMappingsOnly[. */ + IX_MIN_YES_NO, + /** Mappings are comp-normalized. */ + IX_MIN_NO_NO, + IX_LIMIT_NO_NO, + IX_MIN_MAYBE_YES, + + /** Mappings only in [minYesNoMappingsOnly..minNoNo[. */ + IX_MIN_YES_NO_MAPPINGS_ONLY, + /** Mappings are not comp-normalized but have a comp boundary before. */ + IX_MIN_NO_NO_COMP_BOUNDARY_BEFORE, + /** Mappings do not have a comp boundary before. */ + IX_MIN_NO_NO_COMP_NO_MAYBE_CC, + /** Mappings to the empty string. */ + IX_MIN_NO_NO_EMPTY, + + IX_MIN_LCCC_CP, + IX_RESERVED19, + IX_COUNT + }; + + enum { + MAPPING_HAS_CCC_LCCC_WORD=0x80, + MAPPING_HAS_RAW_MAPPING=0x40, + // unused bit 0x20, + MAPPING_LENGTH_MASK=0x1f + }; + + enum { + COMP_1_LAST_TUPLE=0x8000, + COMP_1_TRIPLE=1, + COMP_1_TRAIL_LIMIT=0x3400, + COMP_1_TRAIL_MASK=0x7ffe, + COMP_1_TRAIL_SHIFT=9, // 10-1 for the "triple" bit + COMP_2_TRAIL_SHIFT=6, + COMP_2_TRAIL_MASK=0xffc0 + }; + + // higher-level functionality ------------------------------------------ *** + + // NFD without an NFD Normalizer2 instance. + UnicodeString &decompose(const UnicodeString &src, UnicodeString &dest, + UErrorCode &errorCode) const; + /** + * Decomposes [src, limit[ and writes the result to dest. + * limit can be NULL if src is NUL-terminated. + * destLengthEstimate is the initial dest buffer capacity and can be -1. + */ + void decompose(const char16_t *src, const char16_t *limit, + UnicodeString &dest, int32_t destLengthEstimate, + UErrorCode &errorCode) const; + + const char16_t *decompose(const char16_t *src, const char16_t *limit, + ReorderingBuffer *buffer, UErrorCode &errorCode) const; + void decomposeAndAppend(const char16_t *src, const char16_t *limit, + UBool doDecompose, + UnicodeString &safeMiddle, + ReorderingBuffer &buffer, + UErrorCode &errorCode) const; + + /** sink==nullptr: isNormalized()/spanQuickCheckYes() */ + const uint8_t *decomposeUTF8(uint32_t options, + const uint8_t *src, const uint8_t *limit, + ByteSink *sink, Edits *edits, UErrorCode &errorCode) const; + + UBool compose(const char16_t *src, const char16_t *limit, + UBool onlyContiguous, + UBool doCompose, + ReorderingBuffer &buffer, + UErrorCode &errorCode) const; + const char16_t *composeQuickCheck(const char16_t *src, const char16_t *limit, + UBool onlyContiguous, + UNormalizationCheckResult *pQCResult) const; + void composeAndAppend(const char16_t *src, const char16_t *limit, + UBool doCompose, + UBool onlyContiguous, + UnicodeString &safeMiddle, + ReorderingBuffer &buffer, + UErrorCode &errorCode) const; + + /** sink==nullptr: isNormalized() */ + UBool composeUTF8(uint32_t options, UBool onlyContiguous, + const uint8_t *src, const uint8_t *limit, + ByteSink *sink, icu::Edits *edits, UErrorCode &errorCode) const; + + const char16_t *makeFCD(const char16_t *src, const char16_t *limit, + ReorderingBuffer *buffer, UErrorCode &errorCode) const; + void makeFCDAndAppend(const char16_t *src, const char16_t *limit, + UBool doMakeFCD, + UnicodeString &safeMiddle, + ReorderingBuffer &buffer, + UErrorCode &errorCode) const; + + UBool hasDecompBoundaryBefore(UChar32 c) const; + UBool norm16HasDecompBoundaryBefore(uint16_t norm16) const; + UBool hasDecompBoundaryAfter(UChar32 c) const; + UBool norm16HasDecompBoundaryAfter(uint16_t norm16) const; + UBool isDecompInert(UChar32 c) const { return isDecompYesAndZeroCC(getNorm16(c)); } + + UBool hasCompBoundaryBefore(UChar32 c) const { + return c=minMaybeYes; } + static UBool isInert(uint16_t norm16) { return norm16==INERT; } + static UBool isJamoL(uint16_t norm16) { return norm16==JAMO_L; } + static UBool isJamoVT(uint16_t norm16) { return norm16==JAMO_VT; } + uint16_t hangulLVT() const { return minYesNoMappingsOnly|HAS_COMP_BOUNDARY_AFTER; } + UBool isHangulLV(uint16_t norm16) const { return norm16==minYesNo; } + UBool isHangulLVT(uint16_t norm16) const { + return norm16==hangulLVT(); + } + UBool isCompYesAndZeroCC(uint16_t norm16) const { return norm16=MIN_YES_YES_WITH_CC || norm16=limitNoNo; } + + // For use with isCompYes(). + // Perhaps the compiler can combine the two tests for MIN_YES_YES_WITH_CC. + // static uint8_t getCCFromYes(uint16_t norm16) { + // return norm16>=MIN_YES_YES_WITH_CC ? getCCFromNormalYesOrMaybe(norm16) : 0; + // } + uint8_t getCCFromNoNo(uint16_t norm16) const { + const uint16_t *mapping=getMapping(norm16); + if(*mapping&MAPPING_HAS_CCC_LCCC_WORD) { + return (uint8_t)*(mapping-1); + } else { + return 0; + } + } + // requires that the [cpStart..cpLimit[ character passes isCompYesAndZeroCC() + uint8_t getTrailCCFromCompYesAndZeroCC(uint16_t norm16) const { + if(norm16<=minYesNo) { + return 0; // yesYes and Hangul LV have ccc=tccc=0 + } else { + // For Hangul LVT we harmlessly fetch a firstUnit with tccc=0 here. + return (uint8_t)(*getMapping(norm16)>>8); // tccc from yesNo + } + } + uint8_t getPreviousTrailCC(const char16_t *start, const char16_t *p) const; + uint8_t getPreviousTrailCC(const uint8_t *start, const uint8_t *p) const; + + // Requires algorithmic-NoNo. + UChar32 mapAlgorithmic(UChar32 c, uint16_t norm16) const { + return c+(norm16>>DELTA_SHIFT)-centerNoNoDelta; + } + UChar32 getAlgorithmicDelta(uint16_t norm16) const { + return (norm16>>DELTA_SHIFT)-centerNoNoDelta; + } + + // Requires minYesNo>OFFSET_SHIFT); } + const uint16_t *getCompositionsListForDecompYes(uint16_t norm16) const { + if(norm16>OFFSET_SHIFT); + } + /** + * @param c code point must have compositions + * @return compositions list pointer + */ + const uint16_t *getCompositionsList(uint16_t norm16) const { + return isDecompYes(norm16) ? + getCompositionsListForDecompYes(norm16) : + getCompositionsListForComposite(norm16); + } + + const char16_t *copyLowPrefixFromNulTerminated(const char16_t *src, + UChar32 minNeedDataCP, + ReorderingBuffer *buffer, + UErrorCode &errorCode) const; + + enum StopAt { STOP_AT_LIMIT, STOP_AT_DECOMP_BOUNDARY, STOP_AT_COMP_BOUNDARY }; + + const char16_t *decomposeShort(const char16_t *src, const char16_t *limit, + UBool stopAtCompBoundary, UBool onlyContiguous, + ReorderingBuffer &buffer, UErrorCode &errorCode) const; + UBool decompose(UChar32 c, uint16_t norm16, + ReorderingBuffer &buffer, UErrorCode &errorCode) const; + + const uint8_t *decomposeShort(const uint8_t *src, const uint8_t *limit, + StopAt stopAt, UBool onlyContiguous, + ReorderingBuffer &buffer, UErrorCode &errorCode) const; + + static int32_t combine(const uint16_t *list, UChar32 trail); + void addComposites(const uint16_t *list, UnicodeSet &set) const; + void recompose(ReorderingBuffer &buffer, int32_t recomposeStartIndex, + UBool onlyContiguous) const; + + UBool hasCompBoundaryBefore(UChar32 c, uint16_t norm16) const { + return cclone()), - currentIndex(copy.currentIndex), nextIndex(copy.nextIndex), - buffer(copy.buffer), bufferPos(copy.bufferPos) -{ - init(); -} - -void -Normalizer::init() { - UErrorCode errorCode=U_ZERO_ERROR; - fNorm2=Normalizer2Factory::getInstance(fUMode, errorCode); - if(fOptions&UNORM_UNICODE_3_2) { - delete fFilteredNorm2; - fNorm2=fFilteredNorm2= - new FilteredNormalizer2(*fNorm2, *uniset_getUnicode32Instance(errorCode)); - } - if(U_FAILURE(errorCode)) { - errorCode=U_ZERO_ERROR; - fNorm2=Normalizer2Factory::getNoopInstance(errorCode); - } -} - -Normalizer::~Normalizer() -{ - delete fFilteredNorm2; - delete text; -} - -Normalizer* -Normalizer::clone() const -{ - return new Normalizer(*this); -} - -/** - * Generates a hash code for this iterator. - */ -int32_t Normalizer::hashCode() const -{ - return text->hashCode() + fUMode + fOptions + buffer.hashCode() + bufferPos + currentIndex + nextIndex; -} - -bool Normalizer::operator==(const Normalizer& that) const -{ - return - this==&that || - (fUMode==that.fUMode && - fOptions==that.fOptions && - *text==*that.text && - buffer==that.buffer && - bufferPos==that.bufferPos && - nextIndex==that.nextIndex); -} - -//------------------------------------------------------------------------- -// Static utility methods -//------------------------------------------------------------------------- - -void U_EXPORT2 -Normalizer::normalize(const UnicodeString& source, - UNormalizationMode mode, int32_t options, - UnicodeString& result, - UErrorCode &status) { - if(source.isBogus() || U_FAILURE(status)) { - result.setToBogus(); - if(U_SUCCESS(status)) { - status=U_ILLEGAL_ARGUMENT_ERROR; - } - } else { - UnicodeString localDest; - UnicodeString *dest; - - if(&source!=&result) { - dest=&result; - } else { - // the source and result strings are the same object, use a temporary one - dest=&localDest; - } - const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, status); - if(U_SUCCESS(status)) { - if(options&UNORM_UNICODE_3_2) { - FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(status)). - normalize(source, *dest, status); - } else { - n2->normalize(source, *dest, status); - } - } - if(dest==&localDest && U_SUCCESS(status)) { - result=*dest; - } - } -} - -void U_EXPORT2 -Normalizer::compose(const UnicodeString& source, - UBool compat, int32_t options, - UnicodeString& result, - UErrorCode &status) { - normalize(source, compat ? UNORM_NFKC : UNORM_NFC, options, result, status); -} - -void U_EXPORT2 -Normalizer::decompose(const UnicodeString& source, - UBool compat, int32_t options, - UnicodeString& result, - UErrorCode &status) { - normalize(source, compat ? UNORM_NFKD : UNORM_NFD, options, result, status); -} - -UNormalizationCheckResult -Normalizer::quickCheck(const UnicodeString& source, - UNormalizationMode mode, int32_t options, - UErrorCode &status) { - const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, status); - if(U_SUCCESS(status)) { - if(options&UNORM_UNICODE_3_2) { - return FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(status)). - quickCheck(source, status); - } else { - return n2->quickCheck(source, status); - } - } else { - return UNORM_MAYBE; - } -} - -UBool -Normalizer::isNormalized(const UnicodeString& source, - UNormalizationMode mode, int32_t options, - UErrorCode &status) { - const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, status); - if(U_SUCCESS(status)) { - if(options&UNORM_UNICODE_3_2) { - return FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(status)). - isNormalized(source, status); - } else { - return n2->isNormalized(source, status); - } - } else { - return false; - } -} - -UnicodeString & U_EXPORT2 -Normalizer::concatenate(const UnicodeString &left, const UnicodeString &right, - UnicodeString &result, - UNormalizationMode mode, int32_t options, - UErrorCode &errorCode) { - if(left.isBogus() || right.isBogus() || U_FAILURE(errorCode)) { - result.setToBogus(); - if(U_SUCCESS(errorCode)) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - } - } else { - UnicodeString localDest; - UnicodeString *dest; - - if(&right!=&result) { - dest=&result; - } else { - // the right and result strings are the same object, use a temporary one - dest=&localDest; - } - *dest=left; - const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, errorCode); - if(U_SUCCESS(errorCode)) { - if(options&UNORM_UNICODE_3_2) { - FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(errorCode)). - append(*dest, right, errorCode); - } else { - n2->append(*dest, right, errorCode); - } - } - if(dest==&localDest && U_SUCCESS(errorCode)) { - result=*dest; - } - } - return result; -} - -//------------------------------------------------------------------------- -// Iteration API -//------------------------------------------------------------------------- - -/** - * Return the current character in the normalized text. - */ -UChar32 Normalizer::current() { - if(bufferPos0 || previousNormalize()) { - UChar32 c=buffer.char32At(bufferPos-1); - bufferPos-=U16_LENGTH(c); - return c; - } else { - return DONE; - } -} - -void Normalizer::reset() { - currentIndex=nextIndex=text->setToStart(); - clearBuffer(); -} - -void -Normalizer::setIndexOnly(int32_t index) { - text->setIndex(index); // pins index - currentIndex=nextIndex=text->getIndex(); - clearBuffer(); -} - -/** - * Return the first character in the normalized text. This resets - * the Normalizer's position to the beginning of the text. - */ -UChar32 Normalizer::first() { - reset(); - return next(); -} - -/** - * Return the last character in the normalized text. This resets - * the Normalizer's position to be just before the - * the input text corresponding to that normalized character. - */ -UChar32 Normalizer::last() { - currentIndex=nextIndex=text->setToEnd(); - clearBuffer(); - return previous(); -} - -/** - * Retrieve the current iteration position in the input text that is - * being normalized. This method is useful in applications such as - * searching, where you need to be able to determine the position in - * the input text that corresponds to a given normalized output character. - *

- * Note: This method sets the position in the input, while - * {@link #next} and {@link #previous} iterate through characters in the - * output. This means that there is not necessarily a one-to-one - * correspondence between characters returned by next and - * previous and the indices passed to and returned from - * setIndex and {@link #getIndex}. - * - */ -int32_t Normalizer::getIndex() const { - if(bufferPosCharacterIterator or the start (i.e. 0) of the String - * over which this Normalizer is iterating - */ -int32_t Normalizer::startIndex() const { - return text->startIndex(); -} - -/** - * Retrieve the index of the end of the input text. This is the end index - * of the CharacterIterator or the length of the String - * over which this Normalizer is iterating - */ -int32_t Normalizer::endIndex() const { - return text->endIndex(); -} - -//------------------------------------------------------------------------- -// Property access methods -//------------------------------------------------------------------------- - -void -Normalizer::setMode(UNormalizationMode newMode) -{ - fUMode = newMode; - init(); -} - -UNormalizationMode -Normalizer::getUMode() const -{ - return fUMode; -} - -void -Normalizer::setOption(int32_t option, - UBool value) -{ - if (value) { - fOptions |= option; - } else { - fOptions &= (~option); - } - init(); -} - -UBool -Normalizer::getOption(int32_t option) const -{ - return (fOptions & option) != 0; -} - -/** - * Set the input text over which this Normalizer will iterate. - * The iteration position is set to the beginning of the input text. - */ -void -Normalizer::setText(const UnicodeString& newText, - UErrorCode &status) -{ - if (U_FAILURE(status)) { - return; - } - CharacterIterator *newIter = new StringCharacterIterator(newText); - if (newIter == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - delete text; - text = newIter; - reset(); -} - -/** - * Set the input text over which this Normalizer will iterate. - * The iteration position is set to the beginning of the string. - */ -void -Normalizer::setText(const CharacterIterator& newText, - UErrorCode &status) -{ - if (U_FAILURE(status)) { - return; - } - CharacterIterator *newIter = newText.clone(); - if (newIter == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - delete text; - text = newIter; - reset(); -} - -void -Normalizer::setText(ConstChar16Ptr newText, - int32_t length, - UErrorCode &status) -{ - if (U_FAILURE(status)) { - return; - } - CharacterIterator *newIter = new UCharCharacterIterator(newText, length); - if (newIter == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - delete text; - text = newIter; - reset(); -} - -/** - * Copies the text under iteration into the UnicodeString referred to by "result". - * @param result Receives a copy of the text under iteration. - */ -void -Normalizer::getText(UnicodeString& result) -{ - text->getText(result); -} - -//------------------------------------------------------------------------- -// Private utility methods -//------------------------------------------------------------------------- - -void Normalizer::clearBuffer() { - buffer.remove(); - bufferPos=0; -} - -UBool -Normalizer::nextNormalize() { - clearBuffer(); - currentIndex=nextIndex; - text->setIndex(nextIndex); - if(!text->hasNext()) { - return false; - } - // Skip at least one character so we make progress. - UnicodeString segment(text->next32PostInc()); - while(text->hasNext()) { - UChar32 c; - if(fNorm2->hasBoundaryBefore(c=text->next32PostInc())) { - text->move32(-1, CharacterIterator::kCurrent); - break; - } - segment.append(c); - } - nextIndex=text->getIndex(); - UErrorCode errorCode=U_ZERO_ERROR; - fNorm2->normalize(segment, buffer, errorCode); - return U_SUCCESS(errorCode) && !buffer.isEmpty(); -} - -UBool -Normalizer::previousNormalize() { - clearBuffer(); - nextIndex=currentIndex; - text->setIndex(currentIndex); - if(!text->hasPrevious()) { - return false; - } - UnicodeString segment; - while(text->hasPrevious()) { - UChar32 c=text->previous32(); - segment.insert(0, c); - if(fNorm2->hasBoundaryBefore(c)) { - break; - } - } - currentIndex=text->getIndex(); - UErrorCode errorCode=U_ZERO_ERROR; - fNorm2->normalize(segment, buffer, errorCode); - bufferPos=buffer.length(); - return U_SUCCESS(errorCode) && !buffer.isEmpty(); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_NORMALIZATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ************************************************************************* + * COPYRIGHT: + * Copyright (c) 1996-2012, International Business Machines Corporation and + * others. All Rights Reserved. + ************************************************************************* + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_NORMALIZATION + +#include "unicode/uniset.h" +#include "unicode/unistr.h" +#include "unicode/chariter.h" +#include "unicode/schriter.h" +#include "unicode/uchriter.h" +#include "unicode/normlzr.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "normalizer2impl.h" +#include "uprops.h" // for uniset_getUnicode32Instance() + +#if defined(move32) + // System can define move32 intrinsics, but the char iters define move32 method + // using same undef trick in headers, so undef here to re-enable the method. +#undef move32 +#endif + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Normalizer) + +//------------------------------------------------------------------------- +// Constructors and other boilerplate +//------------------------------------------------------------------------- + +Normalizer::Normalizer(const UnicodeString& str, UNormalizationMode mode) : + UObject(), fFilteredNorm2(nullptr), fNorm2(nullptr), fUMode(mode), fOptions(0), + text(new StringCharacterIterator(str)), + currentIndex(0), nextIndex(0), + buffer(), bufferPos(0) +{ + init(); +} + +Normalizer::Normalizer(ConstChar16Ptr str, int32_t length, UNormalizationMode mode) : + UObject(), fFilteredNorm2(nullptr), fNorm2(nullptr), fUMode(mode), fOptions(0), + text(new UCharCharacterIterator(str, length)), + currentIndex(0), nextIndex(0), + buffer(), bufferPos(0) +{ + init(); +} + +Normalizer::Normalizer(const CharacterIterator& iter, UNormalizationMode mode) : + UObject(), fFilteredNorm2(nullptr), fNorm2(nullptr), fUMode(mode), fOptions(0), + text(iter.clone()), + currentIndex(0), nextIndex(0), + buffer(), bufferPos(0) +{ + init(); +} + +Normalizer::Normalizer(const Normalizer ©) : + UObject(copy), fFilteredNorm2(nullptr), fNorm2(nullptr), fUMode(copy.fUMode), fOptions(copy.fOptions), + text(copy.text->clone()), + currentIndex(copy.currentIndex), nextIndex(copy.nextIndex), + buffer(copy.buffer), bufferPos(copy.bufferPos) +{ + init(); +} + +void +Normalizer::init() { + UErrorCode errorCode=U_ZERO_ERROR; + fNorm2=Normalizer2Factory::getInstance(fUMode, errorCode); + if(fOptions&UNORM_UNICODE_3_2) { + delete fFilteredNorm2; + fNorm2=fFilteredNorm2= + new FilteredNormalizer2(*fNorm2, *uniset_getUnicode32Instance(errorCode)); + } + if(U_FAILURE(errorCode)) { + errorCode=U_ZERO_ERROR; + fNorm2=Normalizer2Factory::getNoopInstance(errorCode); + } +} + +Normalizer::~Normalizer() +{ + delete fFilteredNorm2; + delete text; +} + +Normalizer* +Normalizer::clone() const +{ + return new Normalizer(*this); +} + +/** + * Generates a hash code for this iterator. + */ +int32_t Normalizer::hashCode() const +{ + return text->hashCode() + fUMode + fOptions + buffer.hashCode() + bufferPos + currentIndex + nextIndex; +} + +bool Normalizer::operator==(const Normalizer& that) const +{ + return + this==&that || + (fUMode==that.fUMode && + fOptions==that.fOptions && + *text==*that.text && + buffer==that.buffer && + bufferPos==that.bufferPos && + nextIndex==that.nextIndex); +} + +//------------------------------------------------------------------------- +// Static utility methods +//------------------------------------------------------------------------- + +void U_EXPORT2 +Normalizer::normalize(const UnicodeString& source, + UNormalizationMode mode, int32_t options, + UnicodeString& result, + UErrorCode &status) { + if(source.isBogus() || U_FAILURE(status)) { + result.setToBogus(); + if(U_SUCCESS(status)) { + status=U_ILLEGAL_ARGUMENT_ERROR; + } + } else { + UnicodeString localDest; + UnicodeString *dest; + + if(&source!=&result) { + dest=&result; + } else { + // the source and result strings are the same object, use a temporary one + dest=&localDest; + } + const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, status); + if(U_SUCCESS(status)) { + if(options&UNORM_UNICODE_3_2) { + FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(status)). + normalize(source, *dest, status); + } else { + n2->normalize(source, *dest, status); + } + } + if(dest==&localDest && U_SUCCESS(status)) { + result=*dest; + } + } +} + +void U_EXPORT2 +Normalizer::compose(const UnicodeString& source, + UBool compat, int32_t options, + UnicodeString& result, + UErrorCode &status) { + normalize(source, compat ? UNORM_NFKC : UNORM_NFC, options, result, status); +} + +void U_EXPORT2 +Normalizer::decompose(const UnicodeString& source, + UBool compat, int32_t options, + UnicodeString& result, + UErrorCode &status) { + normalize(source, compat ? UNORM_NFKD : UNORM_NFD, options, result, status); +} + +UNormalizationCheckResult +Normalizer::quickCheck(const UnicodeString& source, + UNormalizationMode mode, int32_t options, + UErrorCode &status) { + const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, status); + if(U_SUCCESS(status)) { + if(options&UNORM_UNICODE_3_2) { + return FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(status)). + quickCheck(source, status); + } else { + return n2->quickCheck(source, status); + } + } else { + return UNORM_MAYBE; + } +} + +UBool +Normalizer::isNormalized(const UnicodeString& source, + UNormalizationMode mode, int32_t options, + UErrorCode &status) { + const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, status); + if(U_SUCCESS(status)) { + if(options&UNORM_UNICODE_3_2) { + return FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(status)). + isNormalized(source, status); + } else { + return n2->isNormalized(source, status); + } + } else { + return false; + } +} + +UnicodeString & U_EXPORT2 +Normalizer::concatenate(const UnicodeString &left, const UnicodeString &right, + UnicodeString &result, + UNormalizationMode mode, int32_t options, + UErrorCode &errorCode) { + if(left.isBogus() || right.isBogus() || U_FAILURE(errorCode)) { + result.setToBogus(); + if(U_SUCCESS(errorCode)) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + } + } else { + UnicodeString localDest; + UnicodeString *dest; + + if(&right!=&result) { + dest=&result; + } else { + // the right and result strings are the same object, use a temporary one + dest=&localDest; + } + *dest=left; + const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, errorCode); + if(U_SUCCESS(errorCode)) { + if(options&UNORM_UNICODE_3_2) { + FilteredNormalizer2(*n2, *uniset_getUnicode32Instance(errorCode)). + append(*dest, right, errorCode); + } else { + n2->append(*dest, right, errorCode); + } + } + if(dest==&localDest && U_SUCCESS(errorCode)) { + result=*dest; + } + } + return result; +} + +//------------------------------------------------------------------------- +// Iteration API +//------------------------------------------------------------------------- + +/** + * Return the current character in the normalized text. + */ +UChar32 Normalizer::current() { + if(bufferPos0 || previousNormalize()) { + UChar32 c=buffer.char32At(bufferPos-1); + bufferPos-=U16_LENGTH(c); + return c; + } else { + return DONE; + } +} + +void Normalizer::reset() { + currentIndex=nextIndex=text->setToStart(); + clearBuffer(); +} + +void +Normalizer::setIndexOnly(int32_t index) { + text->setIndex(index); // pins index + currentIndex=nextIndex=text->getIndex(); + clearBuffer(); +} + +/** + * Return the first character in the normalized text. This resets + * the Normalizer's position to the beginning of the text. + */ +UChar32 Normalizer::first() { + reset(); + return next(); +} + +/** + * Return the last character in the normalized text. This resets + * the Normalizer's position to be just before the + * the input text corresponding to that normalized character. + */ +UChar32 Normalizer::last() { + currentIndex=nextIndex=text->setToEnd(); + clearBuffer(); + return previous(); +} + +/** + * Retrieve the current iteration position in the input text that is + * being normalized. This method is useful in applications such as + * searching, where you need to be able to determine the position in + * the input text that corresponds to a given normalized output character. + *

+ * Note: This method sets the position in the input, while + * {@link #next} and {@link #previous} iterate through characters in the + * output. This means that there is not necessarily a one-to-one + * correspondence between characters returned by next and + * previous and the indices passed to and returned from + * setIndex and {@link #getIndex}. + * + */ +int32_t Normalizer::getIndex() const { + if(bufferPosCharacterIterator or the start (i.e. 0) of the String + * over which this Normalizer is iterating + */ +int32_t Normalizer::startIndex() const { + return text->startIndex(); +} + +/** + * Retrieve the index of the end of the input text. This is the end index + * of the CharacterIterator or the length of the String + * over which this Normalizer is iterating + */ +int32_t Normalizer::endIndex() const { + return text->endIndex(); +} + +//------------------------------------------------------------------------- +// Property access methods +//------------------------------------------------------------------------- + +void +Normalizer::setMode(UNormalizationMode newMode) +{ + fUMode = newMode; + init(); +} + +UNormalizationMode +Normalizer::getUMode() const +{ + return fUMode; +} + +void +Normalizer::setOption(int32_t option, + UBool value) +{ + if (value) { + fOptions |= option; + } else { + fOptions &= (~option); + } + init(); +} + +UBool +Normalizer::getOption(int32_t option) const +{ + return (fOptions & option) != 0; +} + +/** + * Set the input text over which this Normalizer will iterate. + * The iteration position is set to the beginning of the input text. + */ +void +Normalizer::setText(const UnicodeString& newText, + UErrorCode &status) +{ + if (U_FAILURE(status)) { + return; + } + CharacterIterator *newIter = new StringCharacterIterator(newText); + if (newIter == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + delete text; + text = newIter; + reset(); +} + +/** + * Set the input text over which this Normalizer will iterate. + * The iteration position is set to the beginning of the string. + */ +void +Normalizer::setText(const CharacterIterator& newText, + UErrorCode &status) +{ + if (U_FAILURE(status)) { + return; + } + CharacterIterator *newIter = newText.clone(); + if (newIter == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + delete text; + text = newIter; + reset(); +} + +void +Normalizer::setText(ConstChar16Ptr newText, + int32_t length, + UErrorCode &status) +{ + if (U_FAILURE(status)) { + return; + } + CharacterIterator *newIter = new UCharCharacterIterator(newText, length); + if (newIter == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + delete text; + text = newIter; + reset(); +} + +/** + * Copies the text under iteration into the UnicodeString referred to by "result". + * @param result Receives a copy of the text under iteration. + */ +void +Normalizer::getText(UnicodeString& result) +{ + text->getText(result); +} + +//------------------------------------------------------------------------- +// Private utility methods +//------------------------------------------------------------------------- + +void Normalizer::clearBuffer() { + buffer.remove(); + bufferPos=0; +} + +UBool +Normalizer::nextNormalize() { + clearBuffer(); + currentIndex=nextIndex; + text->setIndex(nextIndex); + if(!text->hasNext()) { + return false; + } + // Skip at least one character so we make progress. + UnicodeString segment(text->next32PostInc()); + while(text->hasNext()) { + UChar32 c; + if(fNorm2->hasBoundaryBefore(c=text->next32PostInc())) { + text->move32(-1, CharacterIterator::kCurrent); + break; + } + segment.append(c); + } + nextIndex=text->getIndex(); + UErrorCode errorCode=U_ZERO_ERROR; + fNorm2->normalize(segment, buffer, errorCode); + return U_SUCCESS(errorCode) && !buffer.isEmpty(); +} + +UBool +Normalizer::previousNormalize() { + clearBuffer(); + nextIndex=currentIndex; + text->setIndex(currentIndex); + if(!text->hasPrevious()) { + return false; + } + UnicodeString segment; + while(text->hasPrevious()) { + UChar32 c=text->previous32(); + segment.insert(0, c); + if(fNorm2->hasBoundaryBefore(c)) { + break; + } + } + currentIndex=text->getIndex(); + UErrorCode errorCode=U_ZERO_ERROR; + fNorm2->normalize(segment, buffer, errorCode); + bufferPos=buffer.length(); + return U_SUCCESS(errorCode) && !buffer.isEmpty(); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_NORMALIZATION */ diff --git a/deps/icu-small/source/common/parsepos.cpp b/deps/icu-small/source/common/parsepos.cpp index 56c6c788136846..c017c975c31b31 100644 --- a/deps/icu-small/source/common/parsepos.cpp +++ b/deps/icu-small/source/common/parsepos.cpp @@ -1,23 +1,23 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2003-2003, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#include "unicode/parsepos.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ParsePosition) - -ParsePosition::~ParsePosition() {} - -ParsePosition * -ParsePosition::clone() const { - return new ParsePosition(*this); -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2003-2003, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#include "unicode/parsepos.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ParsePosition) + +ParsePosition::~ParsePosition() {} + +ParsePosition * +ParsePosition::clone() const { + return new ParsePosition(*this); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/patternprops.cpp b/deps/icu-small/source/common/patternprops.cpp index da3243d301064d..c4632a97f948bf 100644 --- a/deps/icu-small/source/common/patternprops.cpp +++ b/deps/icu-small/source/common/patternprops.cpp @@ -1,230 +1,230 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: patternprops.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2011mar13 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" -#include "patternprops.h" - -U_NAMESPACE_BEGIN - -/* - * One byte per Latin-1 character. - * Bit 0 is set if either Pattern property is true, - * bit 1 if Pattern_Syntax is true, - * bit 2 if Pattern_White_Space is true. - * That is, Pattern_Syntax is encoded as 3 and Pattern_White_Space as 5. - */ -static const uint8_t latin1[256]={ - // WS: 9..D - 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // WS: 20 Syntax: 21..2F - 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - // Syntax: 3A..40 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // Syntax: 5B..5E - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, - // Syntax: 60 - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // Syntax: 7B..7E - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, - // WS: 85 - 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // Syntax: A1..A7, A9, AB, AC, AE - 0, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 3, 0, 3, 0, - // Syntax: B0, B1, B6, BB, BF - 3, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 3, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // Syntax: D7 - 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // Syntax: F7 - 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* - * One byte per 32 characters from U+2000..U+303F indexing into - * a small table of 32-bit data words. - * The first two data words are all-zeros and all-ones. - */ -static const uint8_t index2000[130]={ - 2, 3, 4, 0, 0, 0, 0, 0, // 20xx - 0, 0, 0, 0, 5, 1, 1, 1, // 21xx - 1, 1, 1, 1, 1, 1, 1, 1, // 22xx - 1, 1, 1, 1, 1, 1, 1, 1, // 23xx - 1, 1, 1, 0, 0, 0, 0, 0, // 24xx - 1, 1, 1, 1, 1, 1, 1, 1, // 25xx - 1, 1, 1, 1, 1, 1, 1, 1, // 26xx - 1, 1, 1, 6, 7, 1, 1, 1, // 27xx - 1, 1, 1, 1, 1, 1, 1, 1, // 28xx - 1, 1, 1, 1, 1, 1, 1, 1, // 29xx - 1, 1, 1, 1, 1, 1, 1, 1, // 2Axx - 1, 1, 1, 1, 1, 1, 1, 1, // 2Bxx - 0, 0, 0, 0, 0, 0, 0, 0, // 2Cxx - 0, 0, 0, 0, 0, 0, 0, 0, // 2Dxx - 1, 1, 1, 1, 0, 0, 0, 0, // 2Exx - 0, 0, 0, 0, 0, 0, 0, 0, // 2Fxx - 8, 9 // 3000..303F -}; - -/* - * One 32-bit integer per 32 characters. Ranges of all-false and all-true - * are mapped to the first two values, other ranges map to appropriate bit patterns. - */ -static const uint32_t syntax2000[]={ - 0, - 0xffffffff, - 0xffff0000, // 2: 2010..201F - 0x7fff00ff, // 3: 2020..2027, 2030..203E - 0x7feffffe, // 4: 2041..2053, 2055..205E - 0xffff0000, // 5: 2190..219F - 0x003fffff, // 6: 2760..2775 - 0xfff00000, // 7: 2794..279F - 0xffffff0e, // 8: 3001..3003, 3008..301F - 0x00010001 // 9: 3020, 3030 -}; - -/* - * Same as syntax2000, but with additional bits set for the - * Pattern_White_Space characters 200E 200F 2028 2029. - */ -static const uint32_t syntaxOrWhiteSpace2000[]={ - 0, - 0xffffffff, - 0xffffc000, // 2: 200E..201F - 0x7fff03ff, // 3: 2020..2029, 2030..203E - 0x7feffffe, // 4: 2041..2053, 2055..205E - 0xffff0000, // 5: 2190..219F - 0x003fffff, // 6: 2760..2775 - 0xfff00000, // 7: 2794..279F - 0xffffff0e, // 8: 3001..3003, 3008..301F - 0x00010001 // 9: 3020, 3030 -}; - -UBool -PatternProps::isSyntax(UChar32 c) { - if(c<0) { - return false; - } else if(c<=0xff) { - return (UBool)(latin1[c]>>1)&1; - } else if(c<0x2010) { - return false; - } else if(c<=0x3030) { - uint32_t bits=syntax2000[index2000[(c-0x2000)>>5]]; - return (UBool)((bits>>(c&0x1f))&1); - } else if(0xfd3e<=c && c<=0xfe46) { - return c<=0xfd3f || 0xfe45<=c; - } else { - return false; - } -} - -UBool -PatternProps::isSyntaxOrWhiteSpace(UChar32 c) { - if(c<0) { - return false; - } else if(c<=0xff) { - return (UBool)(latin1[c]&1); - } else if(c<0x200e) { - return false; - } else if(c<=0x3030) { - uint32_t bits=syntaxOrWhiteSpace2000[index2000[(c-0x2000)>>5]]; - return (UBool)((bits>>(c&0x1f))&1); - } else if(0xfd3e<=c && c<=0xfe46) { - return c<=0xfd3f || 0xfe45<=c; - } else { - return false; - } -} - -UBool -PatternProps::isWhiteSpace(UChar32 c) { - if(c<0) { - return false; - } else if(c<=0xff) { - return (UBool)(latin1[c]>>2)&1; - } else if(0x200e<=c && c<=0x2029) { - return c<=0x200f || 0x2028<=c; - } else { - return false; - } -} - -const UChar * -PatternProps::skipWhiteSpace(const UChar *s, int32_t length) { - while(length>0 && isWhiteSpace(*s)) { - ++s; - --length; - } - return s; -} - -int32_t -PatternProps::skipWhiteSpace(const UnicodeString& s, int32_t start) { - int32_t i = start; - int32_t length = s.length(); - while(i0 && !isSyntaxOrWhiteSpace(*s)) { - ++s; - --length; - } - return s; -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: patternprops.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2011mar13 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" +#include "patternprops.h" + +U_NAMESPACE_BEGIN + +/* + * One byte per Latin-1 character. + * Bit 0 is set if either Pattern property is true, + * bit 1 if Pattern_Syntax is true, + * bit 2 if Pattern_White_Space is true. + * That is, Pattern_Syntax is encoded as 3 and Pattern_White_Space as 5. + */ +static const uint8_t latin1[256]={ + // WS: 9..D + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // WS: 20 Syntax: 21..2F + 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + // Syntax: 3A..40 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Syntax: 5B..5E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, + // Syntax: 60 + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Syntax: 7B..7E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, + // WS: 85 + 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Syntax: A1..A7, A9, AB, AC, AE + 0, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 3, 0, 3, 0, + // Syntax: B0, B1, B6, BB, BF + 3, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Syntax: D7 + 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Syntax: F7 + 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * One byte per 32 characters from U+2000..U+303F indexing into + * a small table of 32-bit data words. + * The first two data words are all-zeros and all-ones. + */ +static const uint8_t index2000[130]={ + 2, 3, 4, 0, 0, 0, 0, 0, // 20xx + 0, 0, 0, 0, 5, 1, 1, 1, // 21xx + 1, 1, 1, 1, 1, 1, 1, 1, // 22xx + 1, 1, 1, 1, 1, 1, 1, 1, // 23xx + 1, 1, 1, 0, 0, 0, 0, 0, // 24xx + 1, 1, 1, 1, 1, 1, 1, 1, // 25xx + 1, 1, 1, 1, 1, 1, 1, 1, // 26xx + 1, 1, 1, 6, 7, 1, 1, 1, // 27xx + 1, 1, 1, 1, 1, 1, 1, 1, // 28xx + 1, 1, 1, 1, 1, 1, 1, 1, // 29xx + 1, 1, 1, 1, 1, 1, 1, 1, // 2Axx + 1, 1, 1, 1, 1, 1, 1, 1, // 2Bxx + 0, 0, 0, 0, 0, 0, 0, 0, // 2Cxx + 0, 0, 0, 0, 0, 0, 0, 0, // 2Dxx + 1, 1, 1, 1, 0, 0, 0, 0, // 2Exx + 0, 0, 0, 0, 0, 0, 0, 0, // 2Fxx + 8, 9 // 3000..303F +}; + +/* + * One 32-bit integer per 32 characters. Ranges of all-false and all-true + * are mapped to the first two values, other ranges map to appropriate bit patterns. + */ +static const uint32_t syntax2000[]={ + 0, + 0xffffffff, + 0xffff0000, // 2: 2010..201F + 0x7fff00ff, // 3: 2020..2027, 2030..203E + 0x7feffffe, // 4: 2041..2053, 2055..205E + 0xffff0000, // 5: 2190..219F + 0x003fffff, // 6: 2760..2775 + 0xfff00000, // 7: 2794..279F + 0xffffff0e, // 8: 3001..3003, 3008..301F + 0x00010001 // 9: 3020, 3030 +}; + +/* + * Same as syntax2000, but with additional bits set for the + * Pattern_White_Space characters 200E 200F 2028 2029. + */ +static const uint32_t syntaxOrWhiteSpace2000[]={ + 0, + 0xffffffff, + 0xffffc000, // 2: 200E..201F + 0x7fff03ff, // 3: 2020..2029, 2030..203E + 0x7feffffe, // 4: 2041..2053, 2055..205E + 0xffff0000, // 5: 2190..219F + 0x003fffff, // 6: 2760..2775 + 0xfff00000, // 7: 2794..279F + 0xffffff0e, // 8: 3001..3003, 3008..301F + 0x00010001 // 9: 3020, 3030 +}; + +UBool +PatternProps::isSyntax(UChar32 c) { + if(c<0) { + return false; + } else if(c<=0xff) { + return (UBool)(latin1[c]>>1)&1; + } else if(c<0x2010) { + return false; + } else if(c<=0x3030) { + uint32_t bits=syntax2000[index2000[(c-0x2000)>>5]]; + return (UBool)((bits>>(c&0x1f))&1); + } else if(0xfd3e<=c && c<=0xfe46) { + return c<=0xfd3f || 0xfe45<=c; + } else { + return false; + } +} + +UBool +PatternProps::isSyntaxOrWhiteSpace(UChar32 c) { + if(c<0) { + return false; + } else if(c<=0xff) { + return (UBool)(latin1[c]&1); + } else if(c<0x200e) { + return false; + } else if(c<=0x3030) { + uint32_t bits=syntaxOrWhiteSpace2000[index2000[(c-0x2000)>>5]]; + return (UBool)((bits>>(c&0x1f))&1); + } else if(0xfd3e<=c && c<=0xfe46) { + return c<=0xfd3f || 0xfe45<=c; + } else { + return false; + } +} + +UBool +PatternProps::isWhiteSpace(UChar32 c) { + if(c<0) { + return false; + } else if(c<=0xff) { + return (UBool)(latin1[c]>>2)&1; + } else if(0x200e<=c && c<=0x2029) { + return c<=0x200f || 0x2028<=c; + } else { + return false; + } +} + +const char16_t * +PatternProps::skipWhiteSpace(const char16_t *s, int32_t length) { + while(length>0 && isWhiteSpace(*s)) { + ++s; + --length; + } + return s; +} + +int32_t +PatternProps::skipWhiteSpace(const UnicodeString& s, int32_t start) { + int32_t i = start; + int32_t length = s.length(); + while(i0 && !isSyntaxOrWhiteSpace(*s)) { + ++s; + --length; + } + return s; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/patternprops.h b/deps/icu-small/source/common/patternprops.h index 4ead56e1cdb364..db1a51ba9fac3b 100644 --- a/deps/icu-small/source/common/patternprops.h +++ b/deps/icu-small/source/common/patternprops.h @@ -1,98 +1,98 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: patternprops.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2011mar13 -* created by: Markus W. Scherer -*/ - -#ifndef __PATTERNPROPS_H__ -#define __PATTERNPROPS_H__ - -#include "unicode/unistr.h" -#include "unicode/utypes.h" - -U_NAMESPACE_BEGIN - -/** - * Implements the immutable Unicode properties Pattern_Syntax and Pattern_White_Space. - * Hardcodes these properties, does not load data, does not depend on other ICU classes. - *

- * Note: Both properties include ASCII as well as non-ASCII, non-Latin-1 code points, - * and both properties only include BMP code points (no supplementary ones). - * Pattern_Syntax includes some unassigned code points. - *

- * [:Pattern_White_Space:] = - * [\u0009-\u000D\ \u0085\u200E\u200F\u2028\u2029] - *

- * [:Pattern_Syntax:] = - * [!-/\:-@\[-\^`\{-~\u00A1-\u00A7\u00A9\u00AB\u00AC\u00AE - * \u00B0\u00B1\u00B6\u00BB\u00BF\u00D7\u00F7 - * \u2010-\u2027\u2030-\u203E\u2041-\u2053\u2055-\u205E - * \u2190-\u245F\u2500-\u2775\u2794-\u2BFF\u2E00-\u2E7F - * \u3001-\u3003\u3008-\u3020\u3030\uFD3E\uFD3F\uFE45\uFE46] - * @author mscherer - */ -class U_COMMON_API PatternProps { -public: - /** - * @return true if c is a Pattern_Syntax code point. - */ - static UBool isSyntax(UChar32 c); - - /** - * @return true if c is a Pattern_Syntax or Pattern_White_Space code point. - */ - static UBool isSyntaxOrWhiteSpace(UChar32 c); - - /** - * @return true if c is a Pattern_White_Space character. - */ - static UBool isWhiteSpace(UChar32 c); - - /** - * Skips over Pattern_White_Space starting at s. - * @return The smallest pointer at or after s with a non-white space character. - */ - static const UChar *skipWhiteSpace(const UChar *s, int32_t length); - - /** - * Skips over Pattern_White_Space starting at index start in s. - * @return The smallest index at or after start with a non-white space character. - */ - static int32_t skipWhiteSpace(const UnicodeString &s, int32_t start); - - /** - * @return s except with leading and trailing Pattern_White_Space removed and length adjusted. - */ - static const UChar *trimWhiteSpace(const UChar *s, int32_t &length); - - /** - * Tests whether the string contains a "pattern identifier", that is, - * whether it contains only non-Pattern_White_Space, non-Pattern_Syntax characters. - * @return true if there are no Pattern_White_Space or Pattern_Syntax characters in s. - */ - static UBool isIdentifier(const UChar *s, int32_t length); - - /** - * Skips over a "pattern identifier" starting at index s. - * @return The smallest pointer at or after s with - * a Pattern_White_Space or Pattern_Syntax character. - */ - static const UChar *skipIdentifier(const UChar *s, int32_t length); - -private: - PatternProps() = delete; // no constructor: all static methods -}; - -U_NAMESPACE_END - -#endif // __PATTERNPROPS_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: patternprops.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2011mar13 +* created by: Markus W. Scherer +*/ + +#ifndef __PATTERNPROPS_H__ +#define __PATTERNPROPS_H__ + +#include "unicode/unistr.h" +#include "unicode/utypes.h" + +U_NAMESPACE_BEGIN + +/** + * Implements the immutable Unicode properties Pattern_Syntax and Pattern_White_Space. + * Hardcodes these properties, does not load data, does not depend on other ICU classes. + *

+ * Note: Both properties include ASCII as well as non-ASCII, non-Latin-1 code points, + * and both properties only include BMP code points (no supplementary ones). + * Pattern_Syntax includes some unassigned code points. + *

+ * [:Pattern_White_Space:] = + * [\u0009-\u000D\ \u0085\u200E\u200F\u2028\u2029] + *

+ * [:Pattern_Syntax:] = + * [!-/\:-@\[-\^`\{-~\u00A1-\u00A7\u00A9\u00AB\u00AC\u00AE + * \u00B0\u00B1\u00B6\u00BB\u00BF\u00D7\u00F7 + * \u2010-\u2027\u2030-\u203E\u2041-\u2053\u2055-\u205E + * \u2190-\u245F\u2500-\u2775\u2794-\u2BFF\u2E00-\u2E7F + * \u3001-\u3003\u3008-\u3020\u3030\uFD3E\uFD3F\uFE45\uFE46] + * @author mscherer + */ +class U_COMMON_API PatternProps { +public: + /** + * @return true if c is a Pattern_Syntax code point. + */ + static UBool isSyntax(UChar32 c); + + /** + * @return true if c is a Pattern_Syntax or Pattern_White_Space code point. + */ + static UBool isSyntaxOrWhiteSpace(UChar32 c); + + /** + * @return true if c is a Pattern_White_Space character. + */ + static UBool isWhiteSpace(UChar32 c); + + /** + * Skips over Pattern_White_Space starting at s. + * @return The smallest pointer at or after s with a non-white space character. + */ + static const char16_t *skipWhiteSpace(const char16_t *s, int32_t length); + + /** + * Skips over Pattern_White_Space starting at index start in s. + * @return The smallest index at or after start with a non-white space character. + */ + static int32_t skipWhiteSpace(const UnicodeString &s, int32_t start); + + /** + * @return s except with leading and trailing Pattern_White_Space removed and length adjusted. + */ + static const char16_t *trimWhiteSpace(const char16_t *s, int32_t &length); + + /** + * Tests whether the string contains a "pattern identifier", that is, + * whether it contains only non-Pattern_White_Space, non-Pattern_Syntax characters. + * @return true if there are no Pattern_White_Space or Pattern_Syntax characters in s. + */ + static UBool isIdentifier(const char16_t *s, int32_t length); + + /** + * Skips over a "pattern identifier" starting at index s. + * @return The smallest pointer at or after s with + * a Pattern_White_Space or Pattern_Syntax character. + */ + static const char16_t *skipIdentifier(const char16_t *s, int32_t length); + +private: + PatternProps() = delete; // no constructor: all static methods +}; + +U_NAMESPACE_END + +#endif // __PATTERNPROPS_H__ diff --git a/deps/icu-small/source/common/pluralmap.cpp b/deps/icu-small/source/common/pluralmap.cpp index ec87f0198e19eb..3ae7707b832c12 100644 --- a/deps/icu-small/source/common/pluralmap.cpp +++ b/deps/icu-small/source/common/pluralmap.cpp @@ -1,44 +1,44 @@ -// © 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/unistr.h" -#include "charstr.h" -#include "cstring.h" -#include "pluralmap.h" - -U_NAMESPACE_BEGIN - -static const char * const gPluralForms[] = { - "other", "zero", "one", "two", "few", "many"}; - -PluralMapBase::Category -PluralMapBase::toCategory(const char *pluralForm) { - for (int32_t i = 0; i < UPRV_LENGTHOF(gPluralForms); ++i) { - if (uprv_strcmp(pluralForm, gPluralForms[i]) == 0) { - return static_cast(i); - } - } - return NONE; -} - -PluralMapBase::Category -PluralMapBase::toCategory(const UnicodeString &pluralForm) { - CharString cCategory; - UErrorCode status = U_ZERO_ERROR; - cCategory.appendInvariantChars(pluralForm, status); - return U_FAILURE(status) ? NONE : toCategory(cCategory.data()); -} - -const char *PluralMapBase::getCategoryName(Category c) { - int32_t index = c; - return (index < 0 || index >= UPRV_LENGTHOF(gPluralForms)) ? - NULL : gPluralForms[index]; -} - - -U_NAMESPACE_END - +// © 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/unistr.h" +#include "charstr.h" +#include "cstring.h" +#include "pluralmap.h" + +U_NAMESPACE_BEGIN + +static const char * const gPluralForms[] = { + "other", "zero", "one", "two", "few", "many"}; + +PluralMapBase::Category +PluralMapBase::toCategory(const char *pluralForm) { + for (int32_t i = 0; i < UPRV_LENGTHOF(gPluralForms); ++i) { + if (uprv_strcmp(pluralForm, gPluralForms[i]) == 0) { + return static_cast(i); + } + } + return NONE; +} + +PluralMapBase::Category +PluralMapBase::toCategory(const UnicodeString &pluralForm) { + CharString cCategory; + UErrorCode status = U_ZERO_ERROR; + cCategory.appendInvariantChars(pluralForm, status); + return U_FAILURE(status) ? NONE : toCategory(cCategory.data()); +} + +const char *PluralMapBase::getCategoryName(Category c) { + int32_t index = c; + return (index < 0 || index >= UPRV_LENGTHOF(gPluralForms)) ? + nullptr : gPluralForms[index]; +} + + +U_NAMESPACE_END + diff --git a/deps/icu-small/source/common/pluralmap.h b/deps/icu-small/source/common/pluralmap.h index 4988fd2699f385..5ae02466865288 100644 --- a/deps/icu-small/source/common/pluralmap.h +++ b/deps/icu-small/source/common/pluralmap.h @@ -1,292 +1,292 @@ -// © 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 pluralmap.h - PluralMap class that maps plural categories to values. -****************************************************************************** -*/ - -#ifndef __PLURAL_MAP_H__ -#define __PLURAL_MAP_H__ - -#include "unicode/uobject.h" -#include "cmemory.h" - -U_NAMESPACE_BEGIN - -class UnicodeString; - -class U_COMMON_API PluralMapBase : public UMemory { -public: - /** - * The names of all the plural categories. NONE is not an actual plural - * category, but rather represents the absence of a plural category. - */ - enum Category { - NONE = -1, - OTHER, - ZERO, - ONE, - TWO, - FEW, - MANY, - CATEGORY_COUNT - }; - - /** - * Converts a category name such as "zero", "one", "two", "few", "many" - * or "other" to a category enum. Returns NONE for an unrecognized - * category name. - */ - static Category toCategory(const char *categoryName); - - /** - * Converts a category name such as "zero", "one", "two", "few", "many" - * or "other" to a category enum. Returns NONE for unrecognized - * category name. - */ - static Category toCategory(const UnicodeString &categoryName); - - /** - * Converts a category to a name. - * Passing NONE or CATEGORY_COUNT for category returns NULL. - */ - static const char *getCategoryName(Category category); -}; - -/** - * A Map of plural categories to values. It maintains ownership of the - * values. - * - * Type T is the value type. T must provide the following: - * 1) Default constructor - * 2) Copy constructor - * 3) Assignment operator - * 4) Must extend UMemory - */ -template -class PluralMap : public PluralMapBase { -public: - /** - * Other category is maps to a copy of the default value. - */ - PluralMap() : fOtherVariant() { - initializeNew(); - } - - /** - * Other category is mapped to otherVariant. - */ - PluralMap(const T &otherVariant) : fOtherVariant(otherVariant) { - initializeNew(); - } - - PluralMap(const PluralMap &other) : fOtherVariant(other.fOtherVariant) { - fVariants[0] = &fOtherVariant; - for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) { - fVariants[i] = other.fVariants[i] ? - new T(*other.fVariants[i]) : NULL; - } - } - - PluralMap &operator=(const PluralMap &other) { - if (this == &other) { - return *this; - } - for (int32_t i = 0; i < UPRV_LENGTHOF(fVariants); ++i) { - if (fVariants[i] != NULL && other.fVariants[i] != NULL) { - *fVariants[i] = *other.fVariants[i]; - } else if (fVariants[i] != NULL) { - delete fVariants[i]; - fVariants[i] = NULL; - } else if (other.fVariants[i] != NULL) { - fVariants[i] = new T(*other.fVariants[i]); - } else { - // do nothing - } - } - return *this; - } - - ~PluralMap() { - for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) { - delete fVariants[i]; - } - } - - /** - * Removes all mappings and makes 'other' point to the default value. - */ - void clear() { - *fVariants[0] = T(); - for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) { - delete fVariants[i]; - fVariants[i] = NULL; - } - } - - /** - * Iterates through the mappings in this instance, set index to NONE - * prior to using. Call next repeatedly to get the values until it - * returns NULL. Each time next returns, caller may pass index - * to getCategoryName() to get the name of the plural category. - * When this function returns NULL, index is CATEGORY_COUNT - */ - const T *next(Category &index) const { - int32_t idx = index; - ++idx; - for (; idx < UPRV_LENGTHOF(fVariants); ++idx) { - if (fVariants[idx] != NULL) { - index = static_cast(idx); - return fVariants[idx]; - } - } - index = static_cast(idx); - return NULL; - } - - /** - * non const version of next. - */ - T *nextMutable(Category &index) { - const T *result = next(index); - return const_cast(result); - } - - /** - * Returns the 'other' variant. - * Same as calling get(OTHER). - */ - const T &getOther() const { - return get(OTHER); - } - - /** - * Returns the value associated with a category. - * If no value found, or v is NONE or CATEGORY_COUNT, falls - * back to returning the value for the 'other' category. - */ - const T &get(Category v) const { - int32_t index = v; - if (index < 0 || index >= UPRV_LENGTHOF(fVariants) || fVariants[index] == NULL) { - return *fVariants[0]; - } - return *fVariants[index]; - } - - /** - * Convenience routine to get the value by category name. Otherwise - * works just like get(Category). - */ - const T &get(const char *category) const { - return get(toCategory(category)); - } - - /** - * Convenience routine to get the value by category name as a - * UnicodeString. Otherwise works just like get(category). - */ - const T &get(const UnicodeString &category) const { - return get(toCategory(category)); - } - - /** - * Returns a pointer to the value associated with a category - * that caller can safely modify. If the value was defaulting to the 'other' - * variant because no explicit value was stored, this method creates a - * new value using the default constructor at the returned pointer. - * - * @param category the category with the value to change. - * @param status error returned here if index is NONE or CATEGORY_COUNT - * or memory could not be allocated, or any other error happens. - */ - T *getMutable( - Category category, - UErrorCode &status) { - return getMutable(category, NULL, status); - } - - /** - * Convenience routine to get a mutable pointer to a value by category name. - * Otherwise works just like getMutable(Category, UErrorCode &). - * reports an error if the category name is invalid. - */ - T *getMutable( - const char *category, - UErrorCode &status) { - return getMutable(toCategory(category), NULL, status); - } - - /** - * Just like getMutable(Category, UErrorCode &) but copies defaultValue to - * returned pointer if it was defaulting to the 'other' variant - * because no explicit value was stored. - */ - T *getMutableWithDefault( - Category category, - const T &defaultValue, - UErrorCode &status) { - return getMutable(category, &defaultValue, status); - } - - /** - * Returns true if this object equals rhs. - */ - UBool equals( - const PluralMap &rhs, - UBool (*eqFunc)(const T &, const T &)) const { - for (int32_t i = 0; i < UPRV_LENGTHOF(fVariants); ++i) { - if (fVariants[i] == rhs.fVariants[i]) { - continue; - } - if (fVariants[i] == NULL || rhs.fVariants[i] == NULL) { - return false; - } - if (!eqFunc(*fVariants[i], *rhs.fVariants[i])) { - return false; - } - } - return true; - } - -private: - T fOtherVariant; - T* fVariants[6]; - - T *getMutable( - Category category, - const T *defaultValue, - UErrorCode &status) { - if (U_FAILURE(status)) { - return NULL; - } - int32_t index = category; - if (index < 0 || index >= UPRV_LENGTHOF(fVariants)) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - if (fVariants[index] == NULL) { - fVariants[index] = defaultValue == NULL ? - new T() : new T(*defaultValue); - } - if (!fVariants[index]) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return fVariants[index]; - } - - void initializeNew() { - fVariants[0] = &fOtherVariant; - for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) { - fVariants[i] = NULL; - } - } -}; - -U_NAMESPACE_END - -#endif +// © 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 pluralmap.h - PluralMap class that maps plural categories to values. +****************************************************************************** +*/ + +#ifndef __PLURAL_MAP_H__ +#define __PLURAL_MAP_H__ + +#include "unicode/uobject.h" +#include "cmemory.h" + +U_NAMESPACE_BEGIN + +class UnicodeString; + +class U_COMMON_API PluralMapBase : public UMemory { +public: + /** + * The names of all the plural categories. NONE is not an actual plural + * category, but rather represents the absence of a plural category. + */ + enum Category { + NONE = -1, + OTHER, + ZERO, + ONE, + TWO, + FEW, + MANY, + CATEGORY_COUNT + }; + + /** + * Converts a category name such as "zero", "one", "two", "few", "many" + * or "other" to a category enum. Returns NONE for an unrecognized + * category name. + */ + static Category toCategory(const char *categoryName); + + /** + * Converts a category name such as "zero", "one", "two", "few", "many" + * or "other" to a category enum. Returns NONE for unrecognized + * category name. + */ + static Category toCategory(const UnicodeString &categoryName); + + /** + * Converts a category to a name. + * Passing NONE or CATEGORY_COUNT for category returns nullptr. + */ + static const char *getCategoryName(Category category); +}; + +/** + * A Map of plural categories to values. It maintains ownership of the + * values. + * + * Type T is the value type. T must provide the following: + * 1) Default constructor + * 2) Copy constructor + * 3) Assignment operator + * 4) Must extend UMemory + */ +template +class PluralMap : public PluralMapBase { +public: + /** + * Other category is maps to a copy of the default value. + */ + PluralMap() : fOtherVariant() { + initializeNew(); + } + + /** + * Other category is mapped to otherVariant. + */ + PluralMap(const T &otherVariant) : fOtherVariant(otherVariant) { + initializeNew(); + } + + PluralMap(const PluralMap &other) : fOtherVariant(other.fOtherVariant) { + fVariants[0] = &fOtherVariant; + for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) { + fVariants[i] = other.fVariants[i] ? + new T(*other.fVariants[i]) : nullptr; + } + } + + PluralMap &operator=(const PluralMap &other) { + if (this == &other) { + return *this; + } + for (int32_t i = 0; i < UPRV_LENGTHOF(fVariants); ++i) { + if (fVariants[i] != nullptr && other.fVariants[i] != nullptr) { + *fVariants[i] = *other.fVariants[i]; + } else if (fVariants[i] != nullptr) { + delete fVariants[i]; + fVariants[i] = nullptr; + } else if (other.fVariants[i] != nullptr) { + fVariants[i] = new T(*other.fVariants[i]); + } else { + // do nothing + } + } + return *this; + } + + ~PluralMap() { + for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) { + delete fVariants[i]; + } + } + + /** + * Removes all mappings and makes 'other' point to the default value. + */ + void clear() { + *fVariants[0] = T(); + for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) { + delete fVariants[i]; + fVariants[i] = nullptr; + } + } + + /** + * Iterates through the mappings in this instance, set index to NONE + * prior to using. Call next repeatedly to get the values until it + * returns nullptr. Each time next returns, caller may pass index + * to getCategoryName() to get the name of the plural category. + * When this function returns nullptr, index is CATEGORY_COUNT + */ + const T *next(Category &index) const { + int32_t idx = index; + ++idx; + for (; idx < UPRV_LENGTHOF(fVariants); ++idx) { + if (fVariants[idx] != nullptr) { + index = static_cast(idx); + return fVariants[idx]; + } + } + index = static_cast(idx); + return nullptr; + } + + /** + * non const version of next. + */ + T *nextMutable(Category &index) { + const T *result = next(index); + return const_cast(result); + } + + /** + * Returns the 'other' variant. + * Same as calling get(OTHER). + */ + const T &getOther() const { + return get(OTHER); + } + + /** + * Returns the value associated with a category. + * If no value found, or v is NONE or CATEGORY_COUNT, falls + * back to returning the value for the 'other' category. + */ + const T &get(Category v) const { + int32_t index = v; + if (index < 0 || index >= UPRV_LENGTHOF(fVariants) || fVariants[index] == nullptr) { + return *fVariants[0]; + } + return *fVariants[index]; + } + + /** + * Convenience routine to get the value by category name. Otherwise + * works just like get(Category). + */ + const T &get(const char *category) const { + return get(toCategory(category)); + } + + /** + * Convenience routine to get the value by category name as a + * UnicodeString. Otherwise works just like get(category). + */ + const T &get(const UnicodeString &category) const { + return get(toCategory(category)); + } + + /** + * Returns a pointer to the value associated with a category + * that caller can safely modify. If the value was defaulting to the 'other' + * variant because no explicit value was stored, this method creates a + * new value using the default constructor at the returned pointer. + * + * @param category the category with the value to change. + * @param status error returned here if index is NONE or CATEGORY_COUNT + * or memory could not be allocated, or any other error happens. + */ + T *getMutable( + Category category, + UErrorCode &status) { + return getMutable(category, nullptr, status); + } + + /** + * Convenience routine to get a mutable pointer to a value by category name. + * Otherwise works just like getMutable(Category, UErrorCode &). + * reports an error if the category name is invalid. + */ + T *getMutable( + const char *category, + UErrorCode &status) { + return getMutable(toCategory(category), nullptr, status); + } + + /** + * Just like getMutable(Category, UErrorCode &) but copies defaultValue to + * returned pointer if it was defaulting to the 'other' variant + * because no explicit value was stored. + */ + T *getMutableWithDefault( + Category category, + const T &defaultValue, + UErrorCode &status) { + return getMutable(category, &defaultValue, status); + } + + /** + * Returns true if this object equals rhs. + */ + UBool equals( + const PluralMap &rhs, + UBool (*eqFunc)(const T &, const T &)) const { + for (int32_t i = 0; i < UPRV_LENGTHOF(fVariants); ++i) { + if (fVariants[i] == rhs.fVariants[i]) { + continue; + } + if (fVariants[i] == nullptr || rhs.fVariants[i] == nullptr) { + return false; + } + if (!eqFunc(*fVariants[i], *rhs.fVariants[i])) { + return false; + } + } + return true; + } + +private: + T fOtherVariant; + T* fVariants[6]; + + T *getMutable( + Category category, + const T *defaultValue, + UErrorCode &status) { + if (U_FAILURE(status)) { + return nullptr; + } + int32_t index = category; + if (index < 0 || index >= UPRV_LENGTHOF(fVariants)) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + if (fVariants[index] == nullptr) { + fVariants[index] = defaultValue == nullptr ? + new T() : new T(*defaultValue); + } + if (!fVariants[index]) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return fVariants[index]; + } + + void initializeNew() { + fVariants[0] = &fOtherVariant; + for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) { + fVariants[i] = nullptr; + } + } +}; + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/common/propname.cpp b/deps/icu-small/source/common/propname.cpp index 8f0045fdac541a..0fd4e2884a295a 100644 --- a/deps/icu-small/source/common/propname.cpp +++ b/deps/icu-small/source/common/propname.cpp @@ -1,328 +1,334 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: October 30 2002 -* Since: ICU 2.4 -* 2010nov19 Markus Scherer Rewrite for formatVersion 2. -********************************************************************** -*/ -#include "propname.h" -#include "unicode/uchar.h" -#include "unicode/udata.h" -#include "unicode/uscript.h" -#include "umutex.h" -#include "cmemory.h" -#include "cstring.h" -#include "uarrsort.h" -#include "uinvchar.h" - -#define INCLUDED_FROM_PROPNAME_CPP -#include "propname_data.h" - -U_CDECL_BEGIN - -/** - * Get the next non-ignorable ASCII character from a property name - * and lowercases it. - * @return ((advance count for the name)<<8)|character - */ -static inline int32_t -getASCIIPropertyNameChar(const char *name) { - int32_t i; - char c; - - /* Ignore delimiters '-', '_', and ASCII White_Space */ - for(i=0; - (c=name[i++])==0x2d || c==0x5f || - c==0x20 || (0x09<=c && c<=0x0d); - ) {} - - if(c!=0) { - return (i<<8)|(uint8_t)uprv_asciitolower((char)c); - } else { - return i<<8; - } -} - -/** - * Get the next non-ignorable EBCDIC character from a property name - * and lowercases it. - * @return ((advance count for the name)<<8)|character - */ -static inline int32_t -getEBCDICPropertyNameChar(const char *name) { - int32_t i; - char c; - - /* Ignore delimiters '-', '_', and EBCDIC White_Space */ - for(i=0; - (c=name[i++])==0x60 || c==0x6d || - c==0x40 || c==0x05 || c==0x15 || c==0x25 || c==0x0b || c==0x0c || c==0x0d; - ) {} - - if(c!=0) { - return (i<<8)|(uint8_t)uprv_ebcdictolower((char)c); - } else { - return i<<8; - } -} - -/** - * Unicode property names and property value names are compared "loosely". - * - * UCD.html 4.0.1 says: - * For all property names, property value names, and for property values for - * Enumerated, Binary, or Catalog properties, use the following - * loose matching rule: - * - * LM3. Ignore case, whitespace, underscore ('_'), and hyphens. - * - * This function does just that, for (char *) name strings. - * It is almost identical to ucnv_compareNames() but also ignores - * C0 White_Space characters (U+0009..U+000d, and U+0085 on EBCDIC). - * - * @internal - */ - -U_CAPI int32_t U_EXPORT2 -uprv_compareASCIIPropertyNames(const char *name1, const char *name2) { - int32_t rc, r1, r2; - - for(;;) { - r1=getASCIIPropertyNameChar(name1); - r2=getASCIIPropertyNameChar(name2); - - /* If we reach the ends of both strings then they match */ - if(((r1|r2)&0xff)==0) { - return 0; - } - - /* Compare the lowercased characters */ - if(r1!=r2) { - rc=(r1&0xff)-(r2&0xff); - if(rc!=0) { - return rc; - } - } - - name1+=r1>>8; - name2+=r2>>8; - } -} - -U_CAPI int32_t U_EXPORT2 -uprv_compareEBCDICPropertyNames(const char *name1, const char *name2) { - int32_t rc, r1, r2; - - for(;;) { - r1=getEBCDICPropertyNameChar(name1); - r2=getEBCDICPropertyNameChar(name2); - - /* If we reach the ends of both strings then they match */ - if(((r1|r2)&0xff)==0) { - return 0; - } - - /* Compare the lowercased characters */ - if(r1!=r2) { - rc=(r1&0xff)-(r2&0xff); - if(rc!=0) { - return rc; - } - } - - name1+=r1>>8; - name2+=r2>>8; - } -} - -U_CDECL_END - -U_NAMESPACE_BEGIN - -int32_t PropNameData::findProperty(int32_t property) { - int32_t i=1; // valueMaps index, initially after numRanges - for(int32_t numRanges=valueMaps[0]; numRanges>0; --numRanges) { - // Read and skip the start and limit of this range. - int32_t start=valueMaps[i]; - int32_t limit=valueMaps[i+1]; - i+=2; - if(property0; --numRanges) { - // Read and skip the start and limit of this range. - int32_t start=valueMaps[valueMapIndex]; - int32_t limit=valueMaps[valueMapIndex+1]; - valueMapIndex+=2; - if(value0; --nameIndex) { - nameGroup=uprv_strchr(nameGroup, 0)+1; - } - if(*nameGroup==0) { - return NULL; // no name (Property[Value]Aliases.txt has "n/a") - } - return nameGroup; -} - -UBool PropNameData::containsName(BytesTrie &trie, const char *name) { - if(name==NULL) { - return false; - } - UStringTrieResult result=USTRINGTRIE_NO_VALUE; - char c; - while((c=*name++)!=0) { - c=uprv_invCharToLowercaseAscii(c); - // Ignore delimiters '-', '_', and ASCII White_Space. - if(c==0x2d || c==0x5f || c==0x20 || (0x09<=c && c<=0x0d)) { - continue; - } - if(!USTRINGTRIE_HAS_NEXT(result)) { - return false; - } - result=trie.next((uint8_t)c); - } - return USTRINGTRIE_HAS_VALUE(result); -} - -const char *PropNameData::getPropertyName(int32_t property, int32_t nameChoice) { - int32_t valueMapIndex=findProperty(property); - if(valueMapIndex==0) { - return NULL; // Not a known property. - } - return getName(nameGroups+valueMaps[valueMapIndex], nameChoice); -} - -const char *PropNameData::getPropertyValueName(int32_t property, int32_t value, int32_t nameChoice) { - int32_t valueMapIndex=findProperty(property); - if(valueMapIndex==0) { - return NULL; // Not a known property. - } - int32_t nameGroupOffset=findPropertyValueNameGroup(valueMaps[valueMapIndex+1], value); - if(nameGroupOffset==0) { - return NULL; - } - return getName(nameGroups+nameGroupOffset, nameChoice); -} - -int32_t PropNameData::getPropertyOrValueEnum(int32_t bytesTrieOffset, const char *alias) { - BytesTrie trie(bytesTries+bytesTrieOffset); - if(containsName(trie, alias)) { - return trie.getValue(); - } else { - return UCHAR_INVALID_CODE; - } -} - -int32_t PropNameData::getPropertyEnum(const char *alias) { - return getPropertyOrValueEnum(0, alias); -} - -int32_t PropNameData::getPropertyValueEnum(int32_t property, const char *alias) { - int32_t valueMapIndex=findProperty(property); - if(valueMapIndex==0) { - return UCHAR_INVALID_CODE; // Not a known property. - } - valueMapIndex=valueMaps[valueMapIndex+1]; - if(valueMapIndex==0) { - return UCHAR_INVALID_CODE; // The property does not have named values. - } - // valueMapIndex is the start of the property's valueMap, - // where the first word is the BytesTrie offset. - return getPropertyOrValueEnum(valueMaps[valueMapIndex], alias); -} -U_NAMESPACE_END - -//---------------------------------------------------------------------- -// Public API implementation - -U_CAPI const char* U_EXPORT2 -u_getPropertyName(UProperty property, - UPropertyNameChoice nameChoice) { - U_NAMESPACE_USE - return PropNameData::getPropertyName(property, nameChoice); -} - -U_CAPI UProperty U_EXPORT2 -u_getPropertyEnum(const char* alias) { - U_NAMESPACE_USE - return (UProperty)PropNameData::getPropertyEnum(alias); -} - -U_CAPI const char* U_EXPORT2 -u_getPropertyValueName(UProperty property, - int32_t value, - UPropertyNameChoice nameChoice) { - U_NAMESPACE_USE - return PropNameData::getPropertyValueName(property, value, nameChoice); -} - -U_CAPI int32_t U_EXPORT2 -u_getPropertyValueEnum(UProperty property, - const char* alias) { - U_NAMESPACE_USE - return PropNameData::getPropertyValueEnum(property, alias); -} - -U_CAPI const char* U_EXPORT2 -uscript_getName(UScriptCode scriptCode){ - return u_getPropertyValueName(UCHAR_SCRIPT, scriptCode, - U_LONG_PROPERTY_NAME); -} - -U_CAPI const char* U_EXPORT2 -uscript_getShortName(UScriptCode scriptCode){ - return u_getPropertyValueName(UCHAR_SCRIPT, scriptCode, - U_SHORT_PROPERTY_NAME); -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: October 30 2002 +* Since: ICU 2.4 +* 2010nov19 Markus Scherer Rewrite for formatVersion 2. +********************************************************************** +*/ +#include "propname.h" +#include "unicode/uchar.h" +#include "unicode/udata.h" +#include "unicode/uscript.h" +#include "umutex.h" +#include "cmemory.h" +#include "cstring.h" +#include "uarrsort.h" +#include "uinvchar.h" + +#define INCLUDED_FROM_PROPNAME_CPP +#include "propname_data.h" + +U_CDECL_BEGIN + +/** + * Get the next non-ignorable ASCII character from a property name + * and lowercases it. + * @return ((advance count for the name)<<8)|character + */ +static inline int32_t +getASCIIPropertyNameChar(const char *name) { + int32_t i; + char c; + + /* Ignore delimiters '-', '_', and ASCII White_Space */ + for(i=0; + (c=name[i++])==0x2d || c==0x5f || + c==0x20 || (0x09<=c && c<=0x0d); + ) {} + + if(c!=0) { + return (i<<8)|(uint8_t)uprv_asciitolower((char)c); + } else { + return i<<8; + } +} + +/** + * Get the next non-ignorable EBCDIC character from a property name + * and lowercases it. + * @return ((advance count for the name)<<8)|character + */ +static inline int32_t +getEBCDICPropertyNameChar(const char *name) { + int32_t i; + char c; + + /* Ignore delimiters '-', '_', and EBCDIC White_Space */ + for(i=0; + (c=name[i++])==0x60 || c==0x6d || + c==0x40 || c==0x05 || c==0x15 || c==0x25 || c==0x0b || c==0x0c || c==0x0d; + ) {} + + if(c!=0) { + return (i<<8)|(uint8_t)uprv_ebcdictolower((char)c); + } else { + return i<<8; + } +} + +/** + * Unicode property names and property value names are compared "loosely". + * + * UCD.html 4.0.1 says: + * For all property names, property value names, and for property values for + * Enumerated, Binary, or Catalog properties, use the following + * loose matching rule: + * + * LM3. Ignore case, whitespace, underscore ('_'), and hyphens. + * + * This function does just that, for (char *) name strings. + * It is almost identical to ucnv_compareNames() but also ignores + * C0 White_Space characters (U+0009..U+000d, and U+0085 on EBCDIC). + * + * @internal + */ + +U_CAPI int32_t U_EXPORT2 +uprv_compareASCIIPropertyNames(const char *name1, const char *name2) { + int32_t rc, r1, r2; + + for(;;) { + r1=getASCIIPropertyNameChar(name1); + r2=getASCIIPropertyNameChar(name2); + + /* If we reach the ends of both strings then they match */ + if(((r1|r2)&0xff)==0) { + return 0; + } + + /* Compare the lowercased characters */ + if(r1!=r2) { + rc=(r1&0xff)-(r2&0xff); + if(rc!=0) { + return rc; + } + } + + name1+=r1>>8; + name2+=r2>>8; + } +} + +U_CAPI int32_t U_EXPORT2 +uprv_compareEBCDICPropertyNames(const char *name1, const char *name2) { + int32_t rc, r1, r2; + + for(;;) { + r1=getEBCDICPropertyNameChar(name1); + r2=getEBCDICPropertyNameChar(name2); + + /* If we reach the ends of both strings then they match */ + if(((r1|r2)&0xff)==0) { + return 0; + } + + /* Compare the lowercased characters */ + if(r1!=r2) { + rc=(r1&0xff)-(r2&0xff); + if(rc!=0) { + return rc; + } + } + + name1+=r1>>8; + name2+=r2>>8; + } +} + +U_CDECL_END + +U_NAMESPACE_BEGIN + +int32_t PropNameData::findProperty(int32_t property) { + int32_t i=1; // valueMaps index, initially after numRanges + for(int32_t numRanges=valueMaps[0]; numRanges>0; --numRanges) { + // Read and skip the start and limit of this range. + int32_t start=valueMaps[i]; + int32_t limit=valueMaps[i+1]; + i+=2; + if(property0; --numRanges) { + // Read and skip the start and limit of this range. + int32_t start=valueMaps[valueMapIndex]; + int32_t limit=valueMaps[valueMapIndex+1]; + valueMapIndex+=2; + if(value0; --nameIndex) { + nameGroup=uprv_strchr(nameGroup, 0)+1; + } + if(*nameGroup==0) { + return nullptr; // no name (Property[Value]Aliases.txt has "n/a") + } + return nameGroup; +} + +UBool PropNameData::containsName(BytesTrie &trie, const char *name) { + if(name==nullptr) { + return false; + } + UStringTrieResult result=USTRINGTRIE_NO_VALUE; + char c; + while((c=*name++)!=0) { + c=uprv_invCharToLowercaseAscii(c); + // Ignore delimiters '-', '_', and ASCII White_Space. + if(c==0x2d || c==0x5f || c==0x20 || (0x09<=c && c<=0x0d)) { + continue; + } + if(!USTRINGTRIE_HAS_NEXT(result)) { + return false; + } + result=trie.next((uint8_t)c); + } + return USTRINGTRIE_HAS_VALUE(result); +} + +const char *PropNameData::getPropertyName(int32_t property, int32_t nameChoice) { + int32_t valueMapIndex=findProperty(property); + if(valueMapIndex==0) { + return nullptr; // Not a known property. + } + return getName(nameGroups+valueMaps[valueMapIndex], nameChoice); +} + +const char *PropNameData::getPropertyValueName(int32_t property, int32_t value, int32_t nameChoice) { + int32_t valueMapIndex=findProperty(property); + if(valueMapIndex==0) { + return nullptr; // Not a known property. + } + int32_t nameGroupOffset=findPropertyValueNameGroup(valueMaps[valueMapIndex+1], value); + if(nameGroupOffset==0) { + return nullptr; + } + return getName(nameGroups+nameGroupOffset, nameChoice); +} + +int32_t PropNameData::getPropertyOrValueEnum(int32_t bytesTrieOffset, const char *alias) { + BytesTrie trie(bytesTries+bytesTrieOffset); + if(containsName(trie, alias)) { + return trie.getValue(); + } else { + return UCHAR_INVALID_CODE; + } +} + +int32_t PropNameData::getPropertyEnum(const char *alias) { + return getPropertyOrValueEnum(0, alias); +} + +int32_t PropNameData::getPropertyValueEnum(int32_t property, const char *alias) { + int32_t valueMapIndex=findProperty(property); + if(valueMapIndex==0) { + return UCHAR_INVALID_CODE; // Not a known property. + } + valueMapIndex=valueMaps[valueMapIndex+1]; + if(valueMapIndex==0) { + return UCHAR_INVALID_CODE; // The property does not have named values. + } + // valueMapIndex is the start of the property's valueMap, + // where the first word is the BytesTrie offset. + return getPropertyOrValueEnum(valueMaps[valueMapIndex], alias); +} +U_NAMESPACE_END + +//---------------------------------------------------------------------- +// Public API implementation + +U_CAPI const char* U_EXPORT2 +u_getPropertyName(UProperty property, + UPropertyNameChoice nameChoice) UPRV_NO_SANITIZE_UNDEFINED { + // The nameChoice is really an integer with a couple of named constants. + // Unicode allows for names other than short and long ones. + // If present, these will be returned for U_LONG_PROPERTY_NAME + i, where i=1, 2,... + U_NAMESPACE_USE + return PropNameData::getPropertyName(property, nameChoice); +} + +U_CAPI UProperty U_EXPORT2 +u_getPropertyEnum(const char* alias) { + U_NAMESPACE_USE + return (UProperty)PropNameData::getPropertyEnum(alias); +} + +U_CAPI const char* U_EXPORT2 +u_getPropertyValueName(UProperty property, + int32_t value, + UPropertyNameChoice nameChoice) UPRV_NO_SANITIZE_UNDEFINED { + // The nameChoice is really an integer with a couple of named constants. + // Unicode allows for names other than short and long ones. + // If present, these will be returned for U_LONG_PROPERTY_NAME + i, where i=1, 2,... + U_NAMESPACE_USE + return PropNameData::getPropertyValueName(property, value, nameChoice); +} + +U_CAPI int32_t U_EXPORT2 +u_getPropertyValueEnum(UProperty property, + const char* alias) { + U_NAMESPACE_USE + return PropNameData::getPropertyValueEnum(property, alias); +} + +U_CAPI const char* U_EXPORT2 +uscript_getName(UScriptCode scriptCode){ + return u_getPropertyValueName(UCHAR_SCRIPT, scriptCode, + U_LONG_PROPERTY_NAME); +} + +U_CAPI const char* U_EXPORT2 +uscript_getShortName(UScriptCode scriptCode){ + return u_getPropertyValueName(UCHAR_SCRIPT, scriptCode, + U_SHORT_PROPERTY_NAME); +} diff --git a/deps/icu-small/source/common/propname.h b/deps/icu-small/source/common/propname.h index 1a8ced5b879062..0a80e944bf97a2 100644 --- a/deps/icu-small/source/common/propname.h +++ b/deps/icu-small/source/common/propname.h @@ -1,212 +1,212 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: October 30 2002 -* Since: ICU 2.4 -* 2010nov19 Markus Scherer Rewrite for formatVersion 2. -********************************************************************** -*/ -#ifndef PROPNAME_H -#define PROPNAME_H - -#include "unicode/utypes.h" -#include "unicode/bytestrie.h" -#include "unicode/uchar.h" -#include "udataswp.h" -#include "uprops.h" - -/* - * This header defines the in-memory layout of the property names data - * structure representing the UCD data files PropertyAliases.txt and - * PropertyValueAliases.txt. It is used by: - * propname.cpp - reads data - * genpname - creates data - */ - -/* low-level char * property name comparison -------------------------------- */ - -U_CDECL_BEGIN - -/** - * \var uprv_comparePropertyNames - * Unicode property names and property value names are compared "loosely". - * - * UCD.html 4.0.1 says: - * For all property names, property value names, and for property values for - * Enumerated, Binary, or Catalog properties, use the following - * loose matching rule: - * - * LM3. Ignore case, whitespace, underscore ('_'), and hyphens. - * - * This function does just that, for (char *) name strings. - * It is almost identical to ucnv_compareNames() but also ignores - * C0 White_Space characters (U+0009..U+000d, and U+0085 on EBCDIC). - * - * @internal - */ - -U_CAPI int32_t U_EXPORT2 -uprv_compareASCIIPropertyNames(const char *name1, const char *name2); - -U_CAPI int32_t U_EXPORT2 -uprv_compareEBCDICPropertyNames(const char *name1, const char *name2); - -#if U_CHARSET_FAMILY==U_ASCII_FAMILY -# define uprv_comparePropertyNames uprv_compareASCIIPropertyNames -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY -# define uprv_comparePropertyNames uprv_compareEBCDICPropertyNames -#else -# error U_CHARSET_FAMILY is not valid -#endif - -U_CDECL_END - -/* UDataMemory structure and signatures ------------------------------------- */ - -#define PNAME_DATA_NAME "pnames" -#define PNAME_DATA_TYPE "icu" - -/* Fields in UDataInfo: */ - -/* PNAME_SIG[] is encoded as numeric literals for compatibility with the HP compiler */ -#define PNAME_SIG_0 ((uint8_t)0x70) /* p */ -#define PNAME_SIG_1 ((uint8_t)0x6E) /* n */ -#define PNAME_SIG_2 ((uint8_t)0x61) /* a */ -#define PNAME_SIG_3 ((uint8_t)0x6D) /* m */ - -U_NAMESPACE_BEGIN - -class PropNameData { -public: - enum { - // Byte offsets from the start of the data, after the generic header. - IX_VALUE_MAPS_OFFSET, - IX_BYTE_TRIES_OFFSET, - IX_NAME_GROUPS_OFFSET, - IX_RESERVED3_OFFSET, - IX_RESERVED4_OFFSET, - IX_TOTAL_SIZE, - - // Other values. - IX_MAX_NAME_LENGTH, - IX_RESERVED7, - IX_COUNT - }; - - static const char *getPropertyName(int32_t property, int32_t nameChoice); - static const char *getPropertyValueName(int32_t property, int32_t value, int32_t nameChoice); - - static int32_t getPropertyEnum(const char *alias); - static int32_t getPropertyValueEnum(int32_t property, const char *alias); - -private: - static int32_t findProperty(int32_t property); - static int32_t findPropertyValueNameGroup(int32_t valueMapIndex, int32_t value); - static const char *getName(const char *nameGroup, int32_t nameIndex); - static UBool containsName(BytesTrie &trie, const char *name); - - static int32_t getPropertyOrValueEnum(int32_t bytesTrieOffset, const char *alias); - - static const int32_t indexes[]; - static const int32_t valueMaps[]; - static const uint8_t bytesTries[]; - static const char nameGroups[]; -}; - -/* - * pnames.icu formatVersion 2 - * - * formatVersion 2 is new in ICU 4.8. - * In ICU 4.8, the pnames.icu data file is used only in ICU4J. - * ICU4C 4.8 has the same data structures hardcoded in source/common/propname_data.h. - * - * For documentation of pnames.icu formatVersion 1 see ICU4C 4.6 (2010-dec-01) - * or earlier versions of this header file (source/common/propname.h). - * - * The pnames.icu begins with the standard ICU DataHeader/UDataInfo. - * After that: - * - * int32_t indexes[8]; - * - * (See the PropNameData::IX_... constants.) - * - * The first 6 indexes are byte offsets from the beginning of the data - * (beginning of indexes[]) to following structures. - * The length of each structure is the difference between its offset - * and the next one. - * All offsets are filled in: Where there is no data between two offsets, - * those two offsets are the same. - * The last offset (indexes[PropNameData::IX_TOTAL_SIZE]) indicates the - * total number of bytes in the file. (Not counting the standard headers.) - * - * The sixth index (indexes[PropNameData::IX_MAX_NAME_LENGTH]) has the - * maximum length of any Unicode property (or property value) alias. - * (Without normalization, that is, including underscores etc.) - * - * int32_t valueMaps[]; - * - * The valueMaps[] begins with a map from UProperty enums to properties, - * followed by the per-property value maps from property values to names, - * for those properties that have named values. - * (Binary & enumerated, plus General_Category_Mask.) - * - * valueMaps[0] contains the number of UProperty enum ranges. - * For each range: - * int32_t start, limit -- first and last+1 UProperty enum of a dense range - * Followed by (limit-start) pairs of - * int32_t nameGroupOffset; - * Offset into nameGroups[] for the property's names/aliases. - * int32_t valueMapIndex; - * Offset of the property's value map in the valueMaps[] array. - * If the valueMapIndex is 0, then the property does not have named values. - * - * For each property's value map: - * int32_t bytesTrieOffset; -- Offset into bytesTries[] for name->value mapping. - * int32_t numRanges; - * If numRanges is in the range 1..15, then that many ranges of values follow. - * Per range: - * int32_t start, limit -- first and last+1 UProperty enum of a range - * Followed by (limit-start) entries of - * int32_t nameGroupOffset; - * Offset into nameGroups[] for the property value's names/aliases. - * If the nameGroupOffset is 0, then this is not a named value for this property. - * (That is, the ranges need not be dense.) - * If numRanges is >=0x10, then (numRanges-0x10) sorted values - * and then (numRanges-0x10) corresponding nameGroupOffsets follow. - * Values are sorted as signed integers. - * In this case, the set of values is dense; no nameGroupOffset will be 0. - * - * For both properties and property values, ranges are sorted by their start/limit values. - * - * uint8_t bytesTries[]; - * - * This is a sequence of BytesTrie structures, byte-serialized tries for - * mapping from names/aliases to values. - * The first one maps from property names/aliases to UProperty enum constants. - * The following ones are indexed by property value map bytesTrieOffsets - * for mapping each property's names/aliases to their property values. - * - * char nameGroups[]; - * - * This is a sequence of property name groups. - * Each group is a list of names/aliases (invariant-character strings) for - * one property or property value, in the order of UCharNameChoice. - * The first byte of each group is the number of names in the group. - * It is followed by that many NUL-terminated strings. - * The first string is for the short name; if there is no short name, - * then the first string is empty. - * The second string is the long name. Further strings are additional aliases. - * - * The first name group is for a property rather than a property value, - * so that a nameGroupOffset of 0 can be used to indicate "no value" - * in a property's sparse value ranges. - */ - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: October 30 2002 +* Since: ICU 2.4 +* 2010nov19 Markus Scherer Rewrite for formatVersion 2. +********************************************************************** +*/ +#ifndef PROPNAME_H +#define PROPNAME_H + +#include "unicode/utypes.h" +#include "unicode/bytestrie.h" +#include "unicode/uchar.h" +#include "udataswp.h" +#include "uprops.h" + +/* + * This header defines the in-memory layout of the property names data + * structure representing the UCD data files PropertyAliases.txt and + * PropertyValueAliases.txt. It is used by: + * propname.cpp - reads data + * genpname - creates data + */ + +/* low-level char * property name comparison -------------------------------- */ + +U_CDECL_BEGIN + +/** + * \var uprv_comparePropertyNames + * Unicode property names and property value names are compared "loosely". + * + * UCD.html 4.0.1 says: + * For all property names, property value names, and for property values for + * Enumerated, Binary, or Catalog properties, use the following + * loose matching rule: + * + * LM3. Ignore case, whitespace, underscore ('_'), and hyphens. + * + * This function does just that, for (char *) name strings. + * It is almost identical to ucnv_compareNames() but also ignores + * C0 White_Space characters (U+0009..U+000d, and U+0085 on EBCDIC). + * + * @internal + */ + +U_CAPI int32_t U_EXPORT2 +uprv_compareASCIIPropertyNames(const char *name1, const char *name2); + +U_CAPI int32_t U_EXPORT2 +uprv_compareEBCDICPropertyNames(const char *name1, const char *name2); + +#if U_CHARSET_FAMILY==U_ASCII_FAMILY +# define uprv_comparePropertyNames uprv_compareASCIIPropertyNames +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY +# define uprv_comparePropertyNames uprv_compareEBCDICPropertyNames +#else +# error U_CHARSET_FAMILY is not valid +#endif + +U_CDECL_END + +/* UDataMemory structure and signatures ------------------------------------- */ + +#define PNAME_DATA_NAME "pnames" +#define PNAME_DATA_TYPE "icu" + +/* Fields in UDataInfo: */ + +/* PNAME_SIG[] is encoded as numeric literals for compatibility with the HP compiler */ +#define PNAME_SIG_0 ((uint8_t)0x70) /* p */ +#define PNAME_SIG_1 ((uint8_t)0x6E) /* n */ +#define PNAME_SIG_2 ((uint8_t)0x61) /* a */ +#define PNAME_SIG_3 ((uint8_t)0x6D) /* m */ + +U_NAMESPACE_BEGIN + +class PropNameData { +public: + enum { + // Byte offsets from the start of the data, after the generic header. + IX_VALUE_MAPS_OFFSET, + IX_BYTE_TRIES_OFFSET, + IX_NAME_GROUPS_OFFSET, + IX_RESERVED3_OFFSET, + IX_RESERVED4_OFFSET, + IX_TOTAL_SIZE, + + // Other values. + IX_MAX_NAME_LENGTH, + IX_RESERVED7, + IX_COUNT + }; + + static const char *getPropertyName(int32_t property, int32_t nameChoice); + static const char *getPropertyValueName(int32_t property, int32_t value, int32_t nameChoice); + + static int32_t getPropertyEnum(const char *alias); + static int32_t getPropertyValueEnum(int32_t property, const char *alias); + +private: + static int32_t findProperty(int32_t property); + static int32_t findPropertyValueNameGroup(int32_t valueMapIndex, int32_t value); + static const char *getName(const char *nameGroup, int32_t nameIndex); + static UBool containsName(BytesTrie &trie, const char *name); + + static int32_t getPropertyOrValueEnum(int32_t bytesTrieOffset, const char *alias); + + static const int32_t indexes[]; + static const int32_t valueMaps[]; + static const uint8_t bytesTries[]; + static const char nameGroups[]; +}; + +/* + * pnames.icu formatVersion 2 + * + * formatVersion 2 is new in ICU 4.8. + * In ICU 4.8, the pnames.icu data file is used only in ICU4J. + * ICU4C 4.8 has the same data structures hardcoded in source/common/propname_data.h. + * + * For documentation of pnames.icu formatVersion 1 see ICU4C 4.6 (2010-dec-01) + * or earlier versions of this header file (source/common/propname.h). + * + * The pnames.icu begins with the standard ICU DataHeader/UDataInfo. + * After that: + * + * int32_t indexes[8]; + * + * (See the PropNameData::IX_... constants.) + * + * The first 6 indexes are byte offsets from the beginning of the data + * (beginning of indexes[]) to following structures. + * The length of each structure is the difference between its offset + * and the next one. + * All offsets are filled in: Where there is no data between two offsets, + * those two offsets are the same. + * The last offset (indexes[PropNameData::IX_TOTAL_SIZE]) indicates the + * total number of bytes in the file. (Not counting the standard headers.) + * + * The sixth index (indexes[PropNameData::IX_MAX_NAME_LENGTH]) has the + * maximum length of any Unicode property (or property value) alias. + * (Without normalization, that is, including underscores etc.) + * + * int32_t valueMaps[]; + * + * The valueMaps[] begins with a map from UProperty enums to properties, + * followed by the per-property value maps from property values to names, + * for those properties that have named values. + * (Binary & enumerated, plus General_Category_Mask.) + * + * valueMaps[0] contains the number of UProperty enum ranges. + * For each range: + * int32_t start, limit -- first and last+1 UProperty enum of a dense range + * Followed by (limit-start) pairs of + * int32_t nameGroupOffset; + * Offset into nameGroups[] for the property's names/aliases. + * int32_t valueMapIndex; + * Offset of the property's value map in the valueMaps[] array. + * If the valueMapIndex is 0, then the property does not have named values. + * + * For each property's value map: + * int32_t bytesTrieOffset; -- Offset into bytesTries[] for name->value mapping. + * int32_t numRanges; + * If numRanges is in the range 1..15, then that many ranges of values follow. + * Per range: + * int32_t start, limit -- first and last+1 UProperty enum of a range + * Followed by (limit-start) entries of + * int32_t nameGroupOffset; + * Offset into nameGroups[] for the property value's names/aliases. + * If the nameGroupOffset is 0, then this is not a named value for this property. + * (That is, the ranges need not be dense.) + * If numRanges is >=0x10, then (numRanges-0x10) sorted values + * and then (numRanges-0x10) corresponding nameGroupOffsets follow. + * Values are sorted as signed integers. + * In this case, the set of values is dense; no nameGroupOffset will be 0. + * + * For both properties and property values, ranges are sorted by their start/limit values. + * + * uint8_t bytesTries[]; + * + * This is a sequence of BytesTrie structures, byte-serialized tries for + * mapping from names/aliases to values. + * The first one maps from property names/aliases to UProperty enum constants. + * The following ones are indexed by property value map bytesTrieOffsets + * for mapping each property's names/aliases to their property values. + * + * char nameGroups[]; + * + * This is a sequence of property name groups. + * Each group is a list of names/aliases (invariant-character strings) for + * one property or property value, in the order of UCharNameChoice. + * The first byte of each group is the number of names in the group. + * It is followed by that many NUL-terminated strings. + * The first string is for the short name; if there is no short name, + * then the first string is empty. + * The second string is the long name. Further strings are additional aliases. + * + * The first name group is for a property rather than a property value, + * so that a nameGroupOffset of 0 can be used to indicate "no value" + * in a property's sparse value ranges. + */ + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/common/propname_data.h b/deps/icu-small/source/common/propname_data.h index 1e247874b6aa66..b2780bca3c7075 100644 --- a/deps/icu-small/source/common/propname_data.h +++ b/deps/icu-small/source/common/propname_data.h @@ -1,1984 +1,1984 @@ -// Copyright (C) 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// Copyright (C) 1999-2016, International Business Machines -// Corporation and others. All Rights Reserved. -// -// file name: propname_data.h -// -// machine-generated by: icu/tools/unicode/c/genprops/pnamesbuilder.cpp - -#ifdef INCLUDED_FROM_PROPNAME_CPP - -U_NAMESPACE_BEGIN - -const int32_t PropNameData::indexes[8]={0x20,0x1660,0x5294,0xacd0,0xacd0,0xacd0,0x31,0}; - -const int32_t PropNameData::valueMaps[1424]={ -6,0,0x48,0,0xf1,0x368,0xf1,0x37e,0xf1,0x393,0xf1,0x3a9,0xf1,0x3b4,0xf1,0x3d5, -0xf1,0x3e5,0xf1,0x3f4,0xf1,0x402,0xf1,0x426,0xf1,0x43d,0xf1,0x455,0xf1,0x46c,0xf1,0x47b, -0xf1,0x48a,0xf1,0x49b,0xf1,0x4a9,0xf1,0x4bb,0xf1,0x4d5,0xf1,0x4f0,0xf1,0x505,0xf1,0x522, -0xf1,0x533,0xf1,0x53e,0xf1,0x55d,0xf1,0x573,0xf1,0x584,0xf1,0x594,0xf1,0x5af,0xf1,0x5c8, -0xf1,0x5d9,0xf1,0x5f3,0xf1,0x606,0xf1,0x616,0xf1,0x630,0xf1,0x649,0xf1,0x660,0xf1,0x674, -0xf1,0x68a,0xf1,0x69e,0xf1,0x6b4,0xf1,0x6ce,0xf1,0x6e6,0xf1,0x702,0xf1,0x70a,0xf1,0x712, -0xf1,0x71a,0xf1,0x722,0xf1,0x72b,0xf1,0x738,0xf1,0x74b,0xf1,0x768,0xf1,0x785,0xf1,0x7a2, -0xf1,0x7c0,0xf1,0x7de,0xf1,0x802,0xf1,0x80f,0xf1,0x829,0xf1,0x83e,0xf1,0x859,0xf1,0x870, -0xf1,0x887,0xf1,0x8a9,0xf1,0x8c8,0xf1,0x8e1,0xf1,0x90e,0xf1,0x947,0xf1,0x978,0xf1,0x9a7, -0xf1,0x9d6,0xf1,0x1000,0x1019,0x9eb,0x16d,0xc0b,0x188,0x3279,0xf7,0x3298,0x2d4,0x33d6,0x2ea,0x3430, -0x2f4,0x368d,0x316,0x3fb8,0x382,0x4028,0x38c,0x42c2,0x3bb,0x4300,0x3c3,0x4e45,0x48f,0x4ec3,0x499,0x4ee8, -0x49f,0x4f02,0x4a5,0x4f23,0x4ac,0x4f3d,0xf7,0x4f62,0xf7,0x4f88,0x4b3,0x5032,0x4c9,0x50ab,0x4dc,0x515d, -0x4f7,0x5194,0x4fe,0x5374,0x512,0x57f4,0x53a,0x2000,0x2001,0x5853,0x542,0x3000,0x3001,0x58df,0,0x4000, -0x400e,0x58f1,0,0x58fa,0,0x5914,0,0x5925,0,0x5936,0,0x594c,0,0x5955,0,0x5972, -0,0x5990,0,0x59ae,0,0x59cc,0,0x59e2,0,0x59f6,0,0x5a0c,0,0x7000,0x7001,0x5a25, -0,0x844,0x12,0,1,0x12,0x20,0x862,0x4a,0,1,6,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,0x5e,0x68,0x79,0x84,0x91,0x9e,0xab,0xb8,0xc5,0xd2, -0xdf,0xec,0xf9,0x106,0x113,0x120,0x12d,0x13a,0x147,0x154,0x161,0x16e,0x17b,0x188,0x195,0x1a2, -0x1af,0x1bc,0x1c9,0x1d6,0x1e3,0x1f0,0x1fd,0x20c,0x21b,0x22a,0x239,0x248,0x257,0x266,0x275,0x28f, -0x2a3,0x2b7,0x2d2,0x2e1,0x2ea,0x2fa,0x302,0x30b,0x31a,0x323,0x333,0x344,0x355,0xa03,1,0, -0x17,0x9fa,0xa0b,0xa1c,0xa30,0xa47,0xa5f,0xa71,0xa86,0xa9d,0xab2,0xac2,0xad4,0xaf1,0xb0d,0xb1f, -0xb3c,0xb58,0xb74,0xb89,0xb9e,0xbb8,0xbd3,0xbee,0xba5,1,0,0x148,0xc16,0xc23,0xc36,0xc5e, -0xc7c,0xc9a,0xcb2,0xcdd,0xd07,0xd1f,0xd32,0xd45,0xd54,0xd63,0xd72,0xd81,0xd98,0xda9,0xdbc,0xdcf, -0xddc,0xde9,0xdf8,0xe09,0xe1e,0xe2f,0xe3a,0xe43,0xe54,0xe65,0xe78,0xe8a,0xe9d,0xeb0,0xeef,0xefc, -0xf09,0xf16,0xf2b,0xf5b,0xf75,0xf96,0xfc1,0xfe4,0x1042,0x1069,0x1084,0x1093,0x10ba,0x10e2,0x1105,0x1128, -0x1152,0x116b,0x118a,0x11ad,0x11d1,0x11e4,0x11fe,0x1228,0x1240,0x1268,0x1291,0x12a4,0x12b7,0x12ca,0x12f1,0x1300, -0x1320,0x134e,0x136c,0x139a,0x13b6,0x13d1,0x13ea,0x1403,0x1424,0x1454,0x1473,0x1495,0x14c9,0x14f6,0x153b,0x155c, -0x1586,0x15a7,0x15d0,0x15e3,0x1616,0x162d,0x163c,0x164d,0x1678,0x168f,0x16c0,0x16ee,0x1731,0x173c,0x1775,0x1786, -0x1797,0x17a4,0x17b7,0x17f1,0x1815,0x1839,0x1873,0x18ab,0x18d6,0x18ee,0x191a,0x1946,0x1953,0x1962,0x197f,0x19a1, -0x19cf,0x19ef,0x1a16,0x1a3d,0x1a5c,0x1a6f,0x1a80,0x1a91,0x1ab6,0x1adb,0x1b02,0x1b36,0x1b63,0x1b81,0x1b94,0x1bad, -0x1be6,0x1bf5,0x1c15,0x1c37,0x1c59,0x1c70,0x1c87,0x1cb4,0x1ccd,0x1ce6,0x1d17,0x1d41,0x1d5c,0x1d6f,0x1d8e,0x1d97, -0x1daa,0x1dc8,0x1de6,0x1df9,0x1e10,0x1e25,0x1e5a,0x1e7e,0x1e93,0x1ea2,0x1eb5,0x1ed9,0x1ee2,0x1f06,0x1f1d,0x1f30, -0x1f3f,0x1f4a,0x1f6b,0x1f83,0x1f92,0x1fa1,0x1fb0,0x1fc7,0x1fdc,0x1ff1,0x202a,0x203d,0x2059,0x2064,0x2071,0x209f, -0x20c3,0x20e6,0x20f9,0x211b,0x212e,0x2149,0x216c,0x218f,0x21b4,0x21c5,0x21f4,0x2221,0x2238,0x2253,0x2262,0x228d, -0x22c5,0x22ff,0x232d,0x233e,0x234b,0x236f,0x237e,0x239a,0x23b4,0x23d1,0x2409,0x241e,0x244b,0x246a,0x2498,0x24b8, -0x24ec,0x24fb,0x2525,0x2548,0x2573,0x257e,0x258f,0x25aa,0x25ce,0x25db,0x25f0,0x2617,0x2642,0x2679,0x268c,0x269d, -0x26cd,0x26de,0x26ed,0x2702,0x2720,0x2733,0x2746,0x275d,0x277a,0x2785,0x278e,0x27b0,0x27c5,0x27ea,0x2801,0x282a, -0x2845,0x285a,0x2873,0x2894,0x28c9,0x28da,0x290b,0x292f,0x2940,0x2959,0x2964,0x2991,0x29b3,0x29e1,0x2a14,0x2a23, -0x2a34,0x2a51,0x2a93,0x2aba,0x2ac7,0x2adc,0x2b00,0x2b26,0x2b5f,0x2b70,0x2b94,0x2b9f,0x2bac,0x2bbb,0x2be0,0x2c0e, -0x2c2a,0x2c47,0x2c54,0x2c65,0x2c83,0x2ca6,0x2cc3,0x2cd0,0x2cf0,0x2d0d,0x2d2e,0x2d57,0x2d68,0x2d87,0x2da0,0x2db9, -0x2dca,0x2e13,0x2e24,0x2e3d,0x2e6c,0x2e99,0x2ebe,0x2f00,0x2f1c,0x2f2b,0x2f42,0x2f70,0x2f89,0x2fb2,0x2fcc,0x3007, -0x3025,0x3034,0x3054,0x306f,0x3093,0x30af,0x30cd,0x30eb,0x3102,0x3111,0x311c,0x3159,0x316c,0x3196,0x31b6,0x31e4, -0x3208,0x3230,0x3255,0x3260,0x1fa9,1,0,0x12,0x32af,0x32bf,0x32d2,0x32e2,0x32f2,0x3301,0x3311,0x3323, -0x3336,0x3348,0x3358,0x3368,0x3377,0x3386,0x3396,0x33a3,0x33b2,0x33c6,0x2067,1,0,6,0x33eb,0x33f6, -0x3403,0x3410,0x341d,0x3428,0x20ab,1,0,0x1e,0x3445,0x3454,0x3469,0x347e,0x3493,0x34a7,0x34b8,0x34cc, -0x34df,0x34f0,0x3509,0x351b,0x352c,0x3540,0x3553,0x356b,0x357d,0x3588,0x3598,0x35a6,0x35bb,0x35d0,0x35e6,0x3600, -0x3616,0x3626,0x363a,0x364e,0x365f,0x3677,0x22d6,1,0,0x68,0x369f,0x36c2,0x36cb,0x36d8,0x36e3,0x36ec, -0x36f7,0x3700,0x3719,0x371e,0x3727,0x3744,0x374d,0x375a,0x3763,0x3787,0x378e,0x3797,0x37aa,0x37b5,0x37be,0x37c9, -0x37e2,0x37eb,0x37fa,0x3805,0x380e,0x3819,0x3822,0x3829,0x3832,0x383d,0x3846,0x385f,0x3868,0x3875,0x3880,0x3891, -0x389c,0x38b1,0x38c8,0x38d1,0x38da,0x38f3,0x38fe,0x3907,0x3910,0x3927,0x3944,0x394f,0x3960,0x396b,0x3972,0x397f, -0x398c,0x39b9,0x39ce,0x39d7,0x39f2,0x3a15,0x3a36,0x3a57,0x3a7c,0x3aa3,0x3ac4,0x3ae7,0x3b08,0x3b2f,0x3b50,0x3b75, -0x3b94,0x3bb3,0x3bd2,0x3bef,0x3c10,0x3c31,0x3c54,0x3c79,0x3c98,0x3cb7,0x3cd8,0x3cff,0x3d24,0x3d43,0x3d64,0x3d87, -0x3da2,0x3dbb,0x3dd6,0x3def,0x3e0c,0x3e27,0x3e44,0x3e63,0x3e80,0x3e9d,0x3ebc,0x3ed9,0x3ef4,0x3f11,0x3f2e,0x3f61, -0x3f88,0x3f9b,0x2639,1,0,6,0x3fc9,0x3fd8,0x3fe8,0x3ff8,0x4008,0x4019,0x2697,1,0,0x2b, -0x4037,0x4043,0x4051,0x4060,0x406f,0x407f,0x4090,0x40a4,0x40b9,0x40cf,0x40e2,0x40f6,0x4106,0x410f,0x411a,0x412a, -0x4146,0x4158,0x4166,0x4175,0x4181,0x4196,0x41aa,0x41bd,0x41cb,0x41df,0x41ed,0x41f7,0x4209,0x4215,0x4223,0x4233, -0x423a,0x4241,0x4248,0x424f,0x4256,0x426c,0x428d,0x870,0x429f,0x42aa,0x42b9,0x28f0,1,0,4,0x42d3, -0x42de,0x42ea,0x42f4,0x2916,1,0,0xc8,0x430b,0x4318,0x432d,0x433a,0x4349,0x4357,0x4366,0x4375,0x4387, -0x4396,0x43a4,0x43b5,0x43c4,0x43d3,0x43e0,0x43ec,0x43fb,0x440a,0x4414,0x4421,0x442e,0x443d,0x444b,0x445a,0x4466, -0x4470,0x447c,0x448c,0x449c,0x44aa,0x44b6,0x44c7,0x44d3,0x44df,0x44ed,0x44fa,0x4506,0x4513,0xe2f,0x4520,0x452e, -0x4548,0x4551,0x455f,0x456d,0x4579,0x4588,0x4596,0x45a4,0x45b0,0x45bf,0x45cd,0x45db,0x45e8,0x45f7,0x4612,0x4621, -0x4632,0x4643,0x4656,0x4668,0x4677,0x4689,0x4698,0x46a4,0x46af,0x1f3f,0x46bc,0x46c7,0x46d2,0x46dd,0x46e8,0x4703, -0x470e,0x4719,0x4724,0x4737,0x474b,0x4756,0x4765,0x4774,0x477f,0x478a,0x4797,0x47a6,0x47b4,0x47bf,0x47da,0x47e4, -0x47f5,0x4806,0x4815,0x4826,0x4831,0x483c,0x4847,0x4852,0x485d,0x4868,0x4873,0x487d,0x4888,0x4898,0x48a3,0x48b1, -0x48be,0x48c9,0x48d8,0x48e5,0x48f2,0x4901,0x490e,0x491f,0x4931,0x4941,0x494c,0x495f,0x4976,0x4984,0x4991,0x499c, -0x49a9,0x49ba,0x49d6,0x49ec,0x49f7,0x4a14,0x4a24,0x4a33,0x4a3e,0x4a49,0x2059,0x4a55,0x4a60,0x4a78,0x4a88,0x4a97, -0x4aa5,0x4ab3,0x4abe,0x4ac9,0x4add,0x4af4,0x4b0c,0x4b1c,0x4b2c,0x4b3c,0x4b4e,0x4b59,0x4b64,0x4b6e,0x4b7a,0x4b88, -0x4b9b,0x4ba7,0x4bb4,0x4bbf,0x4bdb,0x4be8,0x4bf6,0x4c0f,0x2959,0x4c1e,0x277a,0x4c2b,0x4c39,0x4c4b,0x4c59,0x4c65, -0x4c75,0x2b94,0x4c83,0x4c8f,0x4c9a,0x4ca5,0x4cb0,0x4cc4,0x4cd2,0x4ce9,0x4cf5,0x4d09,0x4d17,0x4d29,0x4d3f,0x4d4d, -0x4d5f,0x4d6d,0x4d8a,0x4d9c,0x4da9,0x4dba,0x4dcc,0x4de6,0x4df3,0x4e06,0x4e17,0x3111,0x4e24,0x3255,0x4e33,0x3370, -1,0,6,0x4e5f,0x4e72,0x4e82,0x4e90,0x4ea1,0x4eb1,0x33cc,0x12,0,1,0x4edb,0x4ee1,0x33d9, -0x12,0,1,0x4edb,0x4ee1,0x33e6,1,0,3,0x4edb,0x4ee1,0x4f1a,0x33fc,1,0,3, -0x4edb,0x4ee1,0x4f1a,0x3412,1,0,0x12,0x4fa4,0x4fae,0x4fba,0x4fc1,0x4fcc,0x4fd1,0x4fd8,0x4fdf,0x4fe8, -0x4fed,0x4ff2,0x5002,0x870,0x429f,0x500e,0x42aa,0x501e,0x42b9,0x34bb,1,0,0xf,0x4fa4,0x5045,0x504f, -0x5059,0x5064,0x4175,0x506e,0x507a,0x5082,0x5089,0x5093,0x4fba,0x4fc1,0x4fd1,0x509d,0x3542,1,0,0x17, -0x4fa4,0x50ba,0x5059,0x50c6,0x50d3,0x50e1,0x4175,0x50ec,0x4fba,0x50fd,0x4fd1,0x510c,0x511a,0x870,0x428d,0x5126, -0x5137,0x429f,0x500e,0x42aa,0x501e,0x42b9,0x5148,0x365f,1,0,3,0x517b,0x5183,0x518b,0x3678,1, -0,0x10,0x51b4,0x51bb,0x51ca,0x51eb,0x520e,0x5219,0x5238,0x524f,0x525c,0x5265,0x5284,0x52b7,0x52d2,0x5301, -0x531e,0x5343,0x3711,1,0,0x24,0x5392,0x539f,0x53b2,0x53bf,0x53ec,0x5411,0x5426,0x5445,0x5466,0x5493, -0x54cc,0x54ef,0x5512,0x553f,0x5574,0x559b,0x55c4,0x55fb,0x562a,0x564b,0x5670,0x567f,0x56a2,0x56b9,0x56c6,0x56d5, -0x56f2,0x570b,0x572e,0x5753,0x576c,0x5781,0x5790,0x57a1,0x57ae,0x57cf,0x38e1,1,0,4,0x580d,0x5818, -0x5830,0x5848,0x391d,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,0x3445,0x3454,0x3469,0x347e,0x5881,0x3493, -0x34a7,0x5877,0x34b8,0x34cc,0x34df,0x5892,0x34f0,0x3509,0x351b,0x58a9,0x352c,0x3540,0x3553,0x58d2,0x356b,0x357d, -0x3588,0x3598,0x586e,0x35a6,0x35bb,0x35d0,0x35e6,0x3600,0x3616,0x3626,0x363a,0x364e,0x58c8,0x365f,0x3677,0x58b3 -}; - -const uint8_t PropNameData::bytesTries[15412]={ -0,0x15,0x6d,0xc3,0xc7,0x73,0xc2,0x12,0x76,0x7a,0x76,0x6a,0x77,0xa2,0x52,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,3,0x61,0x2e,0x65,0x4c,0x6f,0xc3,0x18,0x73,0x69,0x1e,0x72,0x69,0x61,0x74, -0x69,0x6f,0x6e,0x73,0x65,0x6c,0x65,0x63,0x74,0x6f,0x72,0x69,0x10,0x72,0x1f,0x74, -0x69,0x63,0x61,0x6c,0x6f,0x72,0x69,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f,0x6e,0xc3, -0x18,3,0x62,0xc3,0x14,0x68,0x32,0x6f,0x42,0x73,0x13,0x70,0x61,0x63,0x65,0x5f, -0x17,0x69,0x74,0x65,0x73,0x70,0x61,0x63,0x65,0x5f,0x16,0x72,0x64,0x62,0x72,0x65, -0x61,0x6b,0xc3,0x14,0x73,0xa2,0x49,0x74,0xa4,0x3b,0x75,3,0x63,0xd9,0x40,0xc, -0x69,0x52,0x6e,0x58,0x70,0x12,0x70,0x65,0x72,0x5c,0x13,0x63,0x61,0x73,0x65,0x5c, -0x16,0x6d,0x61,0x70,0x70,0x69,0x6e,0x67,0xd9,0x40,0xc,0x12,0x64,0x65,0x6f,0x5b, -0x10,0x69,1,0x63,0x3e,0x66,0x1b,0x69,0x65,0x64,0x69,0x64,0x65,0x6f,0x67,0x72, -0x61,0x70,0x68,0x5b,0x17,0x6f,0x64,0x65,0x31,0x6e,0x61,0x6d,0x65,0xd9,0x40,0xb, -0xa,0x69,0x84,0x70,0x19,0x70,0x30,0x74,0x36,0x75,0x10,0x63,0xd9,0x40,9,0x12, -0x61,0x63,0x65,0x5f,1,0x63,0xd9,0x40,8,0x65,0x11,0x72,0x6d,0x67,0x69,0x3c, -0x6c,0xa2,0x5f,0x6f,0x17,0x66,0x74,0x64,0x6f,0x74,0x74,0x65,0x64,0x57,0x13,0x6d, -0x70,0x6c,0x65,3,0x63,0x50,0x6c,0x68,0x74,0x8a,0x75,0x1e,0x70,0x70,0x65,0x72, -0x63,0x61,0x73,0x65,0x6d,0x61,0x70,0x70,0x69,0x6e,0x67,0xd9,0x40,9,0x19,0x61, -0x73,0x65,0x66,0x6f,0x6c,0x64,0x69,0x6e,0x67,0xd9,0x40,6,0x1e,0x6f,0x77,0x65, -0x72,0x63,0x61,0x73,0x65,0x6d,0x61,0x70,0x70,0x69,0x6e,0x67,0xd9,0x40,7,0x1e, -0x69,0x74,0x6c,0x65,0x63,0x61,0x73,0x65,0x6d,0x61,0x70,0x70,0x69,0x6e,0x67,0xd9, -0x40,8,0x10,0x63,0xd9,0x40,7,0x62,0xc3,0x13,0x63,0x34,0x64,0x57,0x65,0x6e, -0x66,0x10,0x63,0xd9,0x40,6,0xc2,0xa,2,0x66,0xd9,0x40,6,0x72,0x28,0x78, -0xd9,0x70,0,0x12,0x69,0x70,0x74,0xc2,0xa,0x19,0x65,0x78,0x74,0x65,0x6e,0x73, -0x69,0x6f,0x6e,0x73,0xd9,0x70,0,1,0x67,0x6a,0x6e,1,0x73,0x54,0x74,0x13, -0x65,0x6e,0x63,0x65,1,0x62,0x34,0x74,0x16,0x65,0x72,0x6d,0x69,0x6e,0x61,0x6c, -0x67,0x13,0x72,0x65,0x61,0x6b,0xc3,0x13,0x14,0x69,0x74,0x69,0x76,0x65,0x65,1, -0x6d,0x2e,0x73,0x13,0x74,0x61,0x72,0x74,0x73,0x19,0x65,0x6e,0x74,0x73,0x74,0x61, -0x72,0x74,0x65,0x72,0x73,3,0x63,0x66,0x65,0x72,0x69,0x98,0x72,0x19,0x61,0x69, -0x6c,0x63,0x61,0x6e,0x6f,0x6e,0x69,0x63,0x1f,0x61,0x6c,0x63,0x6f,0x6d,0x62,0x69, -0x6e,0x69,0x6e,0x67,0x63,0x6c,0x61,0x73,0x73,0xc3,0x11,0xd8,0x40,0xa,0x11,0x63, -0x63,0xc3,0x11,0x11,0x72,0x6d,0x58,0x1e,0x69,0x6e,0x61,0x6c,0x70,0x75,0x6e,0x63, -0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0x59,0x1d,0x74,0x6c,0x65,0x63,0x61,0x73,0x65, -0x6d,0x61,0x70,0x70,0x69,0x6e,0x67,0xd9,0x40,0xa,0x6d,0xa2,0x76,0x6e,0xa2,0x78, -0x70,0xa4,0x3e,0x71,0xa4,0x90,0x72,3,0x61,0x2c,0x65,0x36,0x67,0x54,0x69,0x9d, -0x14,0x64,0x69,0x63,0x61,0x6c,0x55,0x1e,0x67,0x69,0x6f,0x6e,0x61,0x6c,0x69,0x6e, -0x64,0x69,0x63,0x61,0x74,0x6f,0x72,0x9d,0x15,0x69,0x65,0x6d,0x6f,0x6a,0x69,0xa2, -0x47,3,0x66,0x44,0x6d,0x5c,0x74,0x7c,0x7a,0x19,0x77,0x6a,0x73,0x65,0x71,0x75, -0x65,0x6e,0x63,0x65,0xa3,0x46,0x1a,0x6c,0x61,0x67,0x73,0x65,0x71,0x75,0x65,0x6e, -0x63,0x65,0xa3,0x44,0x1e,0x6f,0x64,0x69,0x66,0x69,0x65,0x72,0x73,0x65,0x71,0x75, -0x65,0x6e,0x63,0x65,0xa3,0x43,0x19,0x61,0x67,0x73,0x65,0x71,0x75,0x65,0x6e,0x63, -0x65,0xa3,0x45,0x12,0x61,0x74,0x68,0x4f,6,0x6f,0x39,0x6f,0x32,0x74,0xc3,9, -0x75,0x54,0x76,0xd9,0x30,0,0x12,0x6e,0x63,0x68,0x1f,0x61,0x72,0x61,0x63,0x74, -0x65,0x72,0x63,0x6f,0x64,0x65,0x70,0x6f,0x69,0x6e,0x74,0x51,0x14,0x6d,0x65,0x72, -0x69,0x63,1,0x74,0x32,0x76,0x13,0x61,0x6c,0x75,0x65,0xd9,0x30,0,0x12,0x79, -0x70,0x65,0xc3,9,0x61,0xa2,0x77,0x63,0xa2,0x82,0x66,2,0x63,0x98,0x64,0xa2, -0x53,0x6b,1,0x63,0x56,0x64,1,0x69,0x42,0x71,1,0x63,0xc3,0xd,0x75,0x17, -0x69,0x63,0x6b,0x63,0x68,0x65,0x63,0x6b,0xc3,0xd,0x13,0x6e,0x65,0x72,0x74,0x6d, -1,0x69,0x42,0x71,1,0x63,0xc3,0xf,0x75,0x17,0x69,0x63,0x6b,0x63,0x68,0x65, -0x63,0x6b,0xc3,0xf,0x13,0x6e,0x65,0x72,0x74,0x71,1,0x69,0x42,0x71,1,0x63, -0xc3,0xe,0x75,0x17,0x69,0x63,0x6b,0x63,0x68,0x65,0x63,0x6b,0xc3,0xe,0x13,0x6e, -0x65,0x72,0x74,0x6f,1,0x69,0x42,0x71,1,0x63,0xc3,0xc,0x75,0x17,0x69,0x63, -0x6b,0x63,0x68,0x65,0x63,0x6b,0xc3,0xc,0x13,0x6e,0x65,0x72,0x74,0x6b,0xd8,0x40, -5,1,0x31,0xd9,0x40,0xb,0x6d,0x10,0x65,0xd9,0x40,5,0x12,0x68,0x61,0x72, -0x51,2,0x61,0x6c,0x63,0xa2,0x4c,0x72,1,0x65,0x2a,0x69,0x11,0x6e,0x74,0x7f, -0x16,0x70,0x65,0x6e,0x64,0x65,0x64,0x63,0x1f,0x6f,0x6e,0x63,0x61,0x74,0x65,0x6e, -0x61,0x74,0x69,0x6f,0x6e,0x6d,0x61,0x72,0x6b,0x9f,0x10,0x74,2,0x73,0x2c,0x74, -0x30,0x77,0x10,0x73,0x77,0x11,0x79,0x6e,0x75,0x12,0x65,0x72,0x6e,1,0x73,0x38, -0x77,0x18,0x68,0x69,0x74,0x65,0x73,0x70,0x61,0x63,0x65,0x77,0x14,0x79,0x6e,0x74, -0x61,0x78,0x75,0x10,0x6d,0x9f,1,0x6d,0x3c,0x75,0x1a,0x6f,0x74,0x61,0x74,0x69, -0x6f,0x6e,0x6d,0x61,0x72,0x6b,0x53,0x12,0x61,0x72,0x6b,0x53,0x66,0xc1,0xf8,0x69, -0xc1,0x3c,0x69,0xa2,0x6f,0x6a,0xa4,9,0x6c,4,0x62,0xc3,8,0x63,0x8c,0x65, -0x98,0x69,0xa2,0x56,0x6f,2,0x65,0x4b,0x67,0x4c,0x77,0x11,0x65,0x72,0x4c,0x13, -0x63,0x61,0x73,0x65,0x4c,0x16,0x6d,0x61,0x70,0x70,0x69,0x6e,0x67,0xd9,0x40,4, -0x11,0x69,0x63,0x1f,0x61,0x6c,0x6f,0x72,0x64,0x65,0x72,0x65,0x78,0x63,0x65,0x70, -0x74,0x69,0x6f,0x6e,0x4b,0xd8,0x40,4,0x11,0x63,0x63,0xc3,0x10,0x18,0x61,0x64, -0x63,0x61,0x6e,0x6f,0x6e,0x69,0x63,0x1f,0x61,0x6c,0x63,0x6f,0x6d,0x62,0x69,0x6e, -0x69,0x6e,0x67,0x63,0x6c,0x61,0x73,0x73,0xc3,0x10,0x16,0x6e,0x65,0x62,0x72,0x65, -0x61,0x6b,0xc3,8,2,0x64,0x4a,0x6e,0xa2,0x5b,0x73,1,0x63,0xd9,0x40,3, -0x6f,0x16,0x63,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0xd9,0x40,3,2,0x63,0x80,0x65, -0x90,0x73,0x40,1,0x62,0x52,0x74,0x46,1,0x61,0x40,0x72,0x1c,0x69,0x6e,0x61, -0x72,0x79,0x6f,0x70,0x65,0x72,0x61,0x74,0x6f,0x72,0x47,0x11,0x72,0x74,0x41,0x44, -0x1c,0x69,0x6e,0x61,0x72,0x79,0x6f,0x70,0x65,0x72,0x61,0x74,0x6f,0x72,0x45,0x3e, -0x16,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3f,0x10,0x6f,0x42,0x16,0x67,0x72,0x61, -0x70,0x68,0x69,0x63,0x43,2,0x64,0x2e,0x70,0x86,0x73,0x10,0x63,0xc3,0x17,0x11, -0x69,0x63,1,0x70,0x46,0x73,0x1e,0x79,0x6c,0x6c,0x61,0x62,0x69,0x63,0x63,0x61, -0x74,0x65,0x67,0x6f,0x72,0x79,0xc3,0x17,0x10,0x6f,0x1f,0x73,0x69,0x74,0x69,0x6f, -0x6e,0x61,0x6c,0x63,0x61,0x74,0x65,0x67,0x6f,0x72,0x79,0xc3,0x16,0x10,0x63,0xc3, -0x16,2,0x67,0xc3,6,0x6f,0x26,0x74,0xc3,7,0x11,0x69,0x6e,1,0x63,0x4a, -0x69,0x11,0x6e,0x67,1,0x67,0x2e,0x74,0x12,0x79,0x70,0x65,0xc3,7,0x13,0x72, -0x6f,0x75,0x70,0xc3,6,0x48,0x15,0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x49,0x66,0x86, -0x67,0xa2,0x4a,0x68,3,0x61,0x36,0x65,0x58,0x73,0x68,0x79,0x13,0x70,0x68,0x65, -0x6e,0x3d,0x1f,0x6e,0x67,0x75,0x6c,0x73,0x79,0x6c,0x6c,0x61,0x62,0x6c,0x65,0x74, -0x79,0x70,0x65,0xc3,0xb,0x10,0x78,0x3a,0x14,0x64,0x69,0x67,0x69,0x74,0x3b,0x10, -0x74,0xc3,0xb,0x16,0x75,0x6c,0x6c,0x63,0x6f,0x6d,0x70,0x1f,0x6f,0x73,0x69,0x74, -0x69,0x6f,0x6e,0x65,0x78,0x63,0x6c,0x75,0x73,0x69,0x6f,0x6e,0x33,2,0x63,0xa2, -0x44,0x65,0xa2,0x4b,0x72,3,0x61,0x34,0x62,0x84,0x65,0x8a,0x6c,0x12,0x69,0x6e, -0x6b,0x39,0x11,0x70,0x68,0x7c,0x12,0x65,0x6d,0x65,3,0x62,0x5e,0x63,0x30,0x65, -0x48,0x6c,0x12,0x69,0x6e,0x6b,0x39,0x1a,0x6c,0x75,0x73,0x74,0x65,0x72,0x62,0x72, -0x65,0x61,0x6b,0xc3,0x12,0x14,0x78,0x74,0x65,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,0xa2,0x62,0xa2,0xd0,0x63,0xa4,0x4f, -0x64,0xa6,0x1c,0x65,5,0x6d,0x75,0x6d,0x6e,0x70,0xa2,0x6b,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,3,0x63,0x44,0x6b,0x54,0x6d,0x70, -0x70,0x1a,0x72,0x65,0x73,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f,0x6e,0x95,0x17,0x6f, -0x6d,0x70,0x6f,0x6e,0x65,0x6e,0x74,0x9b,0x1c,0x65,0x79,0x63,0x61,0x70,0x73,0x65, -0x71,0x75,0x65,0x6e,0x63,0x65,0xa3,0x42,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,5,0x6c,0x22, -0x6c,0x36,0x6d,0x52,0x70,1,0x62,0xd9,0x40,0xd,0x74,0xc3,0x15,2,0x61,0x32, -0x6b,0xc3,1,0x6f,0x11,0x63,0x6b,0xc3,1,0x11,0x6e,0x6b,0x7b,0x10,0x67,0xd9, -0x40,1,0x61,0xa2,0x4f,0x63,0xc3,0,0x69,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,0x17,0x73,0x69,0x63,0x65,0x6d,0x6f,0x6a,0x69,0xa3,0x41,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,0xb,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, -0x64,0x45,0x64,0x4e,0x68,0x88,0x69,1,0x6f,0x26,0x73,0xa3,0xf0,0x1a,0x74,0x61, -0x73,0x75,0x62,0x73,0x63,0x72,0x69,0x70,0x74,0xa3,0xf0,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,0x12,0x61,0x6e,0x72,0x2c,0x15, -0x65,0x61,0x64,0x69,0x6e,0x67,0x2d,0x61,0xa2,0x7b,0x62,0xa2,0xd4,0x63,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,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,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,0xc9,0x20,0x74,0xc2,0x30,0x77,0x89,0x77, -0x86,0x79,0xa2,0x46,0x7a,1,0x61,0x58,0x6e,0x1a,0x61,0x6d,0x65,0x6e,0x6e,0x79, -0x6d,0x75,0x73,0x69,0x63,0xa4,0x40,0x19,0x61,0x6c,0x6e,0x6f,0x74,0x61,0x74,0x69, -0x6f,0x6e,0xa5,0x40,0x1c,0x6e,0x61,0x62,0x61,0x7a,0x61,0x72,0x73,0x71,0x75,0x61, -0x72,0x65,0xa5,0x18,0x10,0x61,1,0x6e,0x36,0x72,0x16,0x61,0x6e,0x67,0x63,0x69, -0x74,0x69,0xa3,0xfc,0x12,0x63,0x68,0x6f,0xa5,0x2c,1,0x65,0x88,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,0x13,0x7a,0x69,0x64,0x69,0xa5,0x34,0x74,0xa2,0x65,0x75,0xa4,0x4f,0x76,3, -0x61,0x3c,0x65,0x80,0x69,0xa2,0x50,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,0x15,0x74,0x68,0x6b,0x75,0x71,0x69,0xa5,0x3f, -5,0x69,0x3f,0x69,0x5a,0x6f,0x8c,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,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,0x11,0x74,0x6f, -0xa5,0x3d,0x61,0x3e,0x65,0xa2,0xa0,0x68,0x10,0x61,1,0x61,0x24,0x69,0x53,0x11, -0x6e,0x61,0x3d,4,0x67,0x8e,0x69,0xa2,0x49,0x6b,0xa2,0x72,0x6d,0xa2,0x74,0x6e, -0x10,0x67,1,0x73,0x68,0x75,0x10,0x74,0xa4,0x10,1,0x63,0x40,0x73,0x11,0x75, -0x70,0xa4,0x33,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa5,0x33,0x18,0x6f,0x6d, -0x70,0x6f,0x6e,0x65,0x6e,0x74,0x73,0xa5,0x11,0x10,0x61,0xa5,0x3c,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,0x48,0x12,0x73,0x75,0x70,0xa4,0x2b,0x16,0x70,0x6c, -0x65,0x6d,0x65,0x6e,0x74,0xa5,0x2b,0x13,0x6c,0x75,0x67,0x75,0x4b,2,0x63,0x8c, -0x67,0xa2,0x41,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, -0xa2,0xad,0x10,0x61,0xa5,0x3e,0x11,0x61,0x73,0x62,0x12,0x65,0x78,0x74,0xa2,0xad, -0x10,0x61,0xa5,0x3e,0x15,0x61,0x72,0x69,0x74,0x69,0x63,0xa3,0x78,0x70,0xc3,0x4b, -0x70,0xa6,0x61,0x72,0xa8,0x1d,0x73,7,0x6f,0xc1,0xbe,0x6f,0xa2,0x69,0x70,0xa2, -0x85,0x75,0xa2,0xa4,0x79,2,0x6c,0x50,0x6d,0x62,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,0x13,0x62,0x6f,0x6c,0x73, -1,0x61,0x4c,0x66,0x10,0x6f,0x1f,0x72,0x6c,0x65,0x67,0x61,0x63,0x79,0x63,0x6f, -0x6d,0x70,0x75,0x74,0x69,0x6e,0x67,0xa5,0x32,0x1f,0x6e,0x64,0x70,0x69,0x63,0x74, -0x6f,0x67,0x72,0x61,0x70,0x68,0x73,0x65,0x78,0x74,1,0x61,0xa5,0x2a,0x65,0x14, -0x6e,0x64,0x65,0x64,0x61,0xa5,0x2a,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,0x88,0x68,0xa2,0x48,0x69,0xa2, -0x71,0x6d,0x12,0x61,0x6c,0x6c,1,0x66,0x46,0x6b,0x15,0x61,0x6e,0x61,0x65,0x78, -0x74,0xa4,0x29,0x15,0x65,0x6e,0x73,0x69,0x6f,0x6e,0xa5,0x29,0x12,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,0xf0,0x6e,0xa8, -0x23,0x6f,6,0x70,0x63,0x70,0x56,0x72,0x8a,0x73,0xa2,0x4c,0x74,0x10,0x74,0x1f, -0x6f,0x6d,0x61,0x6e,0x73,0x69,0x79,0x61,0x71,0x6e,0x75,0x6d,0x62,0x65,0x72,0x73, -0xa5,0x28,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,1,0x61,0x30,0x6d,0x13,0x61,0x6e,0x79, -0x61,0xa3,0x7a,0x11,0x67,0x65,0xa5,0xf,0x63,0xa2,0x7b,0x67,0xa2,0x7b,0x6c,1, -0x63,0xa2,0x6c,0x64,6,0x70,0x42,0x70,0x3a,0x73,0x5a,0x74,0x88,0x75,0x14,0x79, -0x67,0x68,0x75,0x72,0xa5,0x3b,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,0x14,0x75,0x72,0x6b,0x69,0x63,0xa3,0xbf,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,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,6,0x6b,0x3b,0x6b,0x56,0x6f,0x5a,0x75,0x64,0x79,0x11,0x69, -0x61,0x1f,0x6b,0x65,0x6e,0x67,0x70,0x75,0x61,0x63,0x68,0x75,0x65,0x68,0x6d,0x6f, -0x6e,0x67,0xa5,0x27,0x10,0x6f,0xa3,0x92,0x14,0x62,0x6c,0x6f,0x63,0x6b,0x21,1, -0x6d,0x2c,0x73,0x11,0x68,0x75,0xa5,0x15,0x17,0x62,0x65,0x72,0x66,0x6f,0x72,0x6d, -0x73,0x7b,0x61,0x44,0x62,0x21,0x65,0x10,0x77,1,0x61,0xa5,0xe,0x74,0x14,0x61, -0x69,0x6c,0x75,0x65,0xa3,0x8b,2,0x62,0x3c,0x67,0x4a,0x6e,0x17,0x64,0x69,0x6e, -0x61,0x67,0x61,0x72,0x69,0xa5,0x26,0x15,0x61,0x74,0x61,0x65,0x61,0x6e,0xa3,0xef, -0x16,0x6d,0x75,0x6e,0x64,0x61,0x72,0x69,0xa5,0x47,0x67,0xc4,0x5d,0x6a,0xc1,0xe4, -0x6a,0xa2,0xdf,0x6b,0xa2,0xf8,0x6c,4,0x61,0x54,0x65,0xa2,0x6b,0x69,0xa2,0x82, -0x6f,0xa2,0xc1,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,0x96,0x65,0x11, -0x78,0x74,6,0x64,0x21,0x64,0xa3,0x95,0x65,0x2c,0x66,0xa5,0x39,0x67,0xa5,0x3a, -0xa2,0xe7,0x13,0x6e,0x64,0x65,0x64,6,0x64,0xc,0x64,0xa3,0x95,0x65,0xa3,0xe7, -0x66,0xa5,0x39,0x67,0xa5,0x3a,0x61,0x2a,0x62,0x29,0x63,0xa3,0x94,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,0x4e,0x6e,0x54,0x73,0x10,0x75,0xa2,0xb0,0x12,0x73,0x75,0x70, -0xa4,0x31,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa5,0x31,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, -0xa2,0x43,0x68,4,0x61,0x40,0x69,0x50,0x6d,0x6e,0x6f,0x86,0x75,0x15,0x64,0x61, -0x77,0x61,0x64,0x69,0xa3,0xe6,0x16,0x72,0x6f,0x73,0x68,0x74,0x68,0x69,0xa3,0x89, -0x1d,0x74,0x61,0x6e,0x73,0x6d,0x61,0x6c,0x6c,0x73,0x63,0x72,0x69,0x70,0x74,0xa5, -0x30,0x11,0x65,0x72,0x68,0x16,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0x71,0x12, -0x6a,0x6b,0x69,0xa3,0xe5,5,0x74,0x35,0x74,0x34,0x77,0x7a,0x79,0x13,0x61,0x68, -0x6c,0x69,0xa3,0xa2,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,0x10,0x69,0xa5,0x46,0x69,0xa2,0x4e, -0x6b,0xa2,0x51,0x6e,3,0x61,0x34,0x62,0x84,0x67,0x8a,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,2,0x61,0xa5,0x13,0x62,0xa5,0x38,0x65,0x13, -0x6e,0x64,0x65,0x64,1,0x61,0xa5,0x13,0x62,0xa5,0x38,0x11,0x75,0x6e,0xa3,0x42, -0x11,0x78,0x69,0x96,0x17,0x72,0x61,0x64,0x69,0x63,0x61,0x6c,0x73,0x97,0x12,0x74, -0x68,0x69,0xa3,0xc1,0x1c,0x74,0x6f,0x76,0x69,0x6b,0x6e,0x75,0x6d,0x65,0x72,0x61, -0x6c,0x73,0xa5,0x45,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,0xa4,0xc,0x62,0xa6,0x59, -0x63,0xa8,0x2e,0x64,0xac,0xe3,0x65,5,0x6d,0xa9,0x6d,0x94,0x6e,0xa2,0x41,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, -2,0x61,0xa3,0xc8,0x62,0xa5,0x37,0x65,0x13,0x6e,0x64,0x65,0x64,0xa2,0x85,1, -0x61,0xa3,0xc8,0x62,0xa5,0x37,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,0x4a,0x67,0x76,0x6c,1,0x62,0x30,0x79,0x13,0x6d,0x61,0x69, -0x63,0xa5,0x25,0x13,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,0x1f,0x79,0x70,0x74,0x69,0x61,0x6e,0x68,0x69,0x65,0x72,0x6f,0x67,0x6c, -0x79,0x70,0x68,1,0x66,0x26,0x73,0xa3,0xc2,0x1c,0x6f,0x72,0x6d,0x61,0x74,0x63, -0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x73,0xa5,0x24,7,0x6e,0xc0,0xf2,0x6e,0x3e,0x72, -0xa2,0x5d,0x73,0xa2,0xe5,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,0x7e,0x72,0x12,0x6f,0x77,0x73,0x7d,0x12,0x62,0x69,0x63,0x38, -3,0x65,0x4a,0x6d,0x80,0x70,0xa2,0x50,0x73,0x11,0x75,0x70,0xa2,0x80,0x16,0x70, -0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0x80,0x11,0x78,0x74,3,0x61,0xa3,0xd2,0x62, -0xa5,0x35,0x63,0xa5,0x41,0x65,0x13,0x6e,0x64,0x65,0x64,2,0x61,0xa3,0xd2,0x62, -0xa5,0x35,0x63,0xa5,0x41,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,0x60,0x6f,0xa2,0x69,0x75,0xa4,0x24,0x79,1,0x70,0xa2,0x44, -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,4,0x61,0xa3,0x9e,0x62,0xa3,0xa0,0x63,0xa5,9,0x64,0xa5, -0x43,0x65,0x13,0x6e,0x64,0x65,0x64,3,0x61,0xa3,0x9e,0x62,0xa3,0xa0,0x63,0xa5, -9,0x64,0xa5,0x43,0x10,0x72,1,0x69,0x34,0x6f,0x15,0x6d,0x69,0x6e,0x6f,0x61, -0x6e,0xa5,0x36,0x1a,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,0x4c,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,0xe4,0x68,0xa4,0xe,0x6a,0x10,0x6b,0xa2,0x47, -4,0x63,0x8c,0x65,0xa2,0x80,0x72,0xa2,0x9b,0x73,0xa2,0xad,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,7,0x65,0x6b,0x65,0xa5,0, -0x66,0xa5,0x12,0x67,0xa5,0x2e,0x68,0xa5,0x42,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,7,0x65,0xc,0x65,0xa5,0,0x66, -0xa5,0x12,0x67,0xa5,0x2e,0x68,0xa5,0x42,0x61,0xa3,0x46,0x62,0xa3,0x5e,0x63,0xa3, -0xc5,0x64,0xa3,0xd1,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,2,0x61,0x3a,0x65,0x4c,0x6f,0x16, -0x72,0x61,0x73,0x6d,0x69,0x61,0x6e,0xa5,0x2d,1,0x6b,0x26,0x6d,0xa3,0xa4,0x11, -0x6d,0x61,0xa3,0xd4,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,3,0x65,0x3e,0x69,0x8e,0x6f,0xa2, -0x71,0x75,0x15,0x70,0x6c,0x6f,0x79,0x61,0x6e,0xa3,0xe1,1,0x73,0x60,0x76,0x16, -0x61,0x6e,0x61,0x67,0x61,0x72,0x69,0x3e,0x12,0x65,0x78,0x74,0xa2,0xb3,1,0x61, -0xa5,0x44,0x65,0x13,0x6e,0x64,0x65,0x64,0xa2,0xb3,0x10,0x61,0xa5,0x44,0x13,0x65, -0x72,0x65,0x74,0xa3,0x5a,2,0x61,0x3a,0x6e,0x82,0x76,0x16,0x65,0x73,0x61,0x6b, -0x75,0x72,0x75,0xa5,0x2f,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,0x14,0x67,0x62,0x61,0x74, -0x73,0x91,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,0x13,0x6e,0xc1,0xf,0x74,0x76,0x74,0x4c,0x76, -0x9a,0x77,0xa2,0x48,0x79,0xa2,0x49,0x7a,1,0x61,0x2c,0x68,0x12,0x61,0x69,0x6e, -0x8b,0x11,0x69,0x6e,0x85,2,0x61,0x36,0x65,0x3c,0x68,0x14,0x69,0x6e,0x79,0x65, -0x68,0xa3,0x66,1,0x68,0x71,0x77,0x73,1,0x68,0x28,0x74,0x10,0x68,0x77,0x16, -0x6d,0x61,0x72,0x62,0x75,0x74,0x61,0x74,0x13,0x67,0x6f,0x61,0x6c,0x3d,0x1a,0x65, -0x72,0x74,0x69,0x63,0x61,0x6c,0x74,0x61,0x69,0x6c,0xa3,0x67,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,0x6e,0xa2,0x4c,0x70,0xa2,0x69,0x71,0xa2,0x69,0x72,0xa2,0x6f,0x73, -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,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,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,0x67,0xc1,0xc7,0x67,0xa4,0x52, -0x68,0xa4,0x59,0x6b,0xa4,0x99,0x6c,0xa4,0xb2,0x6d,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,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,0x11,0x61,0x6d,0x4c,0x12, -0x61,0x64,0x68,0x4f,0x61,0x6e,0x62,0xa2,0x54,0x64,0xa2,0x70,0x65,0x31,0x66,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,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,0xc4,0x6f,0x74,0xc1,0x91,0x77,0x96, -0x77,0xa2,0x4c,0x78,0xa2,0x70,0x79,0xa2,0x7a,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,2, -0x61,0x30,0x63,0x5a,0x6f,0x11,0x6c,0x65,0xa3,0x9b,1,0x6e,0x3c,0x72,0x10,0x61, -0xa2,0x92,0x15,0x6e,0x67,0x63,0x69,0x74,0x69,0xa3,0x92,0x12,0x63,0x68,0x6f,0xa3, -0xbc,0x11,0x68,0x6f,0xa3,0xbc,1,0x70,0x2c,0x73,0x11,0x75,0x78,0xa3,0x65,0x11, -0x65,0x6f,0x9b,1,0x65,0x2c,0x69,0x72,0x11,0x69,0x69,0x73,0x11,0x7a,0x69,0xa2, -0xc0,0x11,0x64,0x69,0xa3,0xc0,0x74,0x66,0x75,0xa2,0xde,0x76,1,0x61,0x48,0x69, -1,0x73,0x38,0x74,0x10,0x68,0xa2,0xc5,0x13,0x6b,0x75,0x71,0x69,0xa3,0xc5,0x10, -0x70,0xa3,0x64,0x10,0x69,0xa2,0x63,0x10,0x69,0xa3,0x63,7,0x68,0x3e,0x68,0x34, -0x69,0x48,0x6e,0x86,0x6f,0x11,0x74,0x6f,0xa3,0xc4,0x10,0x61,1,0x61,0x24,0x69, -0x6d,0x6a,0x11,0x6e,0x61,0x6b,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,0x73,0x61,0xa3,0xc3,0x61,0x36,0x65,0xa2, -0x65,0x66,0xa2,0x71,0x67,0x11,0x6c,0x67,0x75,6,0x6c,0x28,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,1,0x73,0x2a,0x75,0x10,0x74,0xa3,0x9a, -0x10,0x61,0xa3,0xc3,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,0x11,0x6e,0x67,0x99,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,0xa4,1,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,6,0x6b,0x36,0x6b,0x56, -0x73,0x6e,0x75,0x74,0x79,0x11,0x69,0x61,0x1f,0x6b,0x65,0x6e,0x67,0x70,0x75,0x61, -0x63,0x68,0x75,0x65,0x68,0x6d,0x6f,0x6e,0x67,0xa3,0xba,1,0x67,0x2e,0x6f,0xa2, -0x57,0x10,0x6f,0xa3,0x57,0x10,0x62,0xa3,0x84,0x11,0x68,0x75,0xa3,0x96,0x12,0x73, -0x68,0x75,0xa3,0x96,0x61,0x42,0x62,0x9e,0x65,0x10,0x77,1,0x61,0xa3,0xaa,0x74, -0x14,0x61,0x69,0x6c,0x75,0x65,0x97,3,0x62,0x32,0x67,0x40,0x6e,0x56,0x72,0x10, -0x62,0xa3,0x8e,0x15,0x61,0x74,0x61,0x65,0x61,0x6e,0xa3,0x8f,0x10,0x6d,0xa2,0xc7, -0x15,0x75,0x6e,0x64,0x61,0x72,0x69,0xa3,0xc7,0x10,0x64,0xa2,0xbb,0x16,0x69,0x6e, -0x61,0x67,0x61,0x72,0x69,0xa3,0xbb,0x11,0x61,0x74,0xa3,0x8f,4,0x67,0x3c,0x6c, -0x4e,0x72,0xa2,0x8e,0x73,0xa2,0x9c,0x75,0x11,0x67,0x72,0xa3,0xc2,1,0x61,0x2a, -0x68,0x11,0x61,0x6d,0x5b,0x10,0x6d,0x5b,1,0x63,0xa2,0x6a,0x64,6,0x70,0x41, -0x70,0x3a,0x73,0x58,0x74,0x86,0x75,0x14,0x79,0x67,0x68,0x75,0x72,0xa3,0xc2,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,0x14,0x75,0x72,0x6b,0x69,0x63,0xa3, -0x58,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,2,0x61,0x36,0x67,0x3c,0x6d,0x10,0x61,0x84,0x12,0x6e,0x79,0x61, -0x85,0x11,0x67,0x65,0xa3,0xab,0x10,0x65,0xa3,0xab,0x68,0xc3,0x15,0x6b,0xc2,0x2c, -0x6b,0xa4,0x17,0x6c,0xa4,0xba,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,6,0x6e,0x1a,0x6e,0x34,0x6f, -0x38,0x70,0x3e,0x74,0x11,0x68,0x69,0xa3,0x78,0x11,0x64,0x61,0x4b,0x11,0x72,0x65, -0xa3,0x77,0x11,0x65,0x6c,0xa3,0x8a,0x61,0x32,0x68,0xa2,0x44,0x69,0x11,0x74,0x73, -0xa3,0xbf,5,0x74,0x23,0x74,0x34,0x77,0x56,0x79,0x13,0x61,0x68,0x6c,0x69,0xa3, -0x4f,0x14,0x61,0x6b,0x61,0x6e,0x61,0x4c,0x19,0x6f,0x72,0x68,0x69,0x72,0x61,0x67, -0x61,0x6e,0x61,0x8d,0x10,0x69,0xa3,0xc6,0x69,0x38,0x6c,0x40,0x6e,1,0x61,0x4d, -0x6e,0x12,0x61,0x64,0x61,0x4b,0x12,0x74,0x68,0x69,0xa3,0x78,0x10,0x69,0xa3,0x4f, -4,0x61,0x40,0x69,0x52,0x6d,0x70,0x6f,0x7c,0x75,0x15,0x64,0x61,0x77,0x61,0x64, -0x69,0xa3,0x91,0x10,0x72,0x92,0x15,0x6f,0x73,0x68,0x74,0x68,0x69,0x93,0x1d,0x74, -0x61,0x6e,0x73,0x6d,0x61,0x6c,0x6c,0x73,0x63,0x72,0x69,0x70,0x74,0xa3,0xbf,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, -0x95,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,0x1e,0x6c,0x34,0x6d,0x3a,0x72,0x48,0x75,0x11, -0x6e,0x67,0xa3,0x4c,0x11,0x75,0x77,0xa3,0x9c,0x10,0x6e,1,0x67,0xa3,0x4b,0x70, -0xa3,0xba,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,0xc1,0xd,0x64,0xa2,0x7a,0x65,0xa2,0xc1,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,4,0x65,0x46,0x69,0x6c,0x6f,0x8c,0x73,0x9a,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,1,0x61,0x36, -0x76,0x16,0x65,0x73,0x61,0x6b,0x75,0x72,0x75,0xa3,0xbe,0x10,0x6b,0xa3,0xbe,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,1,0x62,0x36,0x79,0x10,0x6d, -0xa2,0xb9,0x12,0x61,0x69,0x63,0xa3,0xb9,0x10,0x61,0xa2,0x88,0x12,0x73,0x61,0x6e, -0xa3,0x88,0x61,0xa2,0xc9,0x62,0xa4,0x2e,0x63,6,0x6f,0x52,0x6f,0x76,0x70,0x92, -0x75,0xa2,0x41,0x79,1,0x70,0x3e,0x72,2,0x69,0x2a,0x6c,0x31,0x73,0xa3,0x44, -0x13,0x6c,0x6c,0x69,0x63,0x31,0x10,0x72,1,0x69,0x34,0x6f,0x15,0x6d,0x69,0x6e, -0x6f,0x61,0x6e,0xa3,0xc1,0x11,0x6f,0x74,0x7f,1,0x6d,0x30,0x70,0x10,0x74,0x2e, -0x11,0x69,0x63,0x2f,0x12,0x6d,0x6f,0x6e,0x21,1,0x6d,0x28,0x72,0x10,0x74,0x7f, -0x10,0x6e,0xa3,0xc1,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,3,0x61,0x32,0x65,0x44,0x6f,0x52,0x72,0x10,0x73,0xa3,0xbd, -1,0x6b,0x26,0x6d,0xa3,0x42,0x11,0x6d,0x61,0xa3,0x76,0x10,0x72,0x2c,0x13,0x6f, -0x6b,0x65,0x65,0x2d,0x16,0x72,0x61,0x73,0x6d,0x69,0x61,0x6e,0xa3,0xbd,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,6,0x6f,0x65,0x6f,0x4a,0x72,0x5c,0x74, -0x64,0x76,0x1d,0x69,0x73,0x75,0x61,0x6c,0x6f,0x72,0x64,0x65,0x72,0x6c,0x65,0x66, -0x74,0x3d,0x18,0x76,0x65,0x72,0x73,0x74,0x72,0x75,0x63,0x6b,0x2d,0x13,0x69,0x67, -0x68,0x74,0x2f,0x11,0x6f,0x70,0x30,0x12,0x61,0x6e,0x64,2,0x62,0x32,0x6c,0x62, -0x72,0x13,0x69,0x67,0x68,0x74,0x3b,0x14,0x6f,0x74,0x74,0x6f,0x6d,0x32,0x12,0x61, -0x6e,0x64,1,0x6c,0x2e,0x72,0x13,0x69,0x67,0x68,0x74,0x35,0x12,0x65,0x66,0x74, -0x3f,0x12,0x65,0x66,0x74,0x36,0x17,0x61,0x6e,0x64,0x72,0x69,0x67,0x68,0x74,0x39, -0x62,0x2c,0x6c,0x5c,0x6e,0x10,0x61,0x21,0x14,0x6f,0x74,0x74,0x6f,0x6d,0x22,0x12, -0x61,0x6e,0x64,1,0x6c,0x2e,0x72,0x13,0x69,0x67,0x68,0x74,0x27,0x12,0x65,0x66, -0x74,0x25,0x12,0x65,0x66,0x74,0x28,0x17,0x61,0x6e,0x64,0x72,0x69,0x67,0x68,0x74, -0x2b,0xd,0x6e,0xaa,0x72,0x70,0x72,0x92,0x73,0xa2,0x46,0x74,0xa2,0x54,0x76,1, -0x69,0x60,0x6f,0x12,0x77,0x65,0x6c,0x62,1,0x64,0x3a,0x69,0x19,0x6e,0x64,0x65, -0x70,0x65,0x6e,0x64,0x65,0x6e,0x74,0x67,0x17,0x65,0x70,0x65,0x6e,0x64,0x65,0x6e, -0x74,0x65,1,0x72,0x2e,0x73,0x13,0x61,0x72,0x67,0x61,0x61,0x12,0x61,0x6d,0x61, -0x5f,0x1d,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x73,0x68,0x69,0x66,0x74,0x65,0x72, -0x57,0x1e,0x79,0x6c,0x6c,0x61,0x62,0x6c,0x65,0x6d,0x6f,0x64,0x69,0x66,0x69,0x65, -0x72,0x59,0x12,0x6f,0x6e,0x65,1,0x6c,0x2c,0x6d,0x12,0x61,0x72,0x6b,0x5d,0x14, -0x65,0x74,0x74,0x65,0x72,0x5b,0x6e,0x3c,0x6f,0x7c,0x70,0x18,0x75,0x72,0x65,0x6b, -0x69,0x6c,0x6c,0x65,0x72,0x55,1,0x6f,0x4c,0x75,1,0x6b,0x3c,0x6d,0x12,0x62, -0x65,0x72,0x50,0x15,0x6a,0x6f,0x69,0x6e,0x65,0x72,0x53,0x11,0x74,0x61,0x4f,0x16, -0x6e,0x6a,0x6f,0x69,0x6e,0x65,0x72,0x4d,0x13,0x74,0x68,0x65,0x72,0x21,0x67,0x3e, -0x67,0x4a,0x69,0x64,0x6a,0x82,0x6d,0x1d,0x6f,0x64,0x69,0x66,0x79,0x69,0x6e,0x67, -0x6c,0x65,0x74,0x74,0x65,0x72,0x4b,0x1c,0x65,0x6d,0x69,0x6e,0x61,0x74,0x69,0x6f, -0x6e,0x6d,0x61,0x72,0x6b,0x45,0x1e,0x6e,0x76,0x69,0x73,0x69,0x62,0x6c,0x65,0x73, -0x74,0x61,0x63,0x6b,0x65,0x72,0x47,0x14,0x6f,0x69,0x6e,0x65,0x72,0x49,0x61,0xa2, -0xba,0x62,0xa2,0xc0,0x63,1,0x61,0xa2,0xa2,0x6f,0x16,0x6e,0x73,0x6f,0x6e,0x61, -0x6e,0x74,0x2a,8,0x6b,0x67,0x6b,0x48,0x6d,0x52,0x70,0x5c,0x73,0xa2,0x42,0x77, -0x19,0x69,0x74,0x68,0x73,0x74,0x61,0x63,0x6b,0x65,0x72,0x43,0x14,0x69,0x6c,0x6c, -0x65,0x72,0x35,0x14,0x65,0x64,0x69,0x61,0x6c,0x37,1,0x6c,0x52,0x72,0x10,0x65, -1,0x63,0x2e,0x66,0x13,0x69,0x78,0x65,0x64,0x3d,0x19,0x65,0x64,0x69,0x6e,0x67, -0x72,0x65,0x70,0x68,0x61,0x3b,0x18,0x61,0x63,0x65,0x68,0x6f,0x6c,0x64,0x65,0x72, -0x39,0x10,0x75,1,0x62,0x3e,0x63,0x1b,0x63,0x65,0x65,0x64,0x69,0x6e,0x67,0x72, -0x65,0x70,0x68,0x61,0x41,0x15,0x6a,0x6f,0x69,0x6e,0x65,0x64,0x3f,0x64,0x4c,0x66, -0x52,0x68,0x5a,0x69,0x1e,0x6e,0x69,0x74,0x69,0x61,0x6c,0x70,0x6f,0x73,0x74,0x66, -0x69,0x78,0x65,0x64,0x33,0x12,0x65,0x61,0x64,0x2d,0x13,0x69,0x6e,0x61,0x6c,0x2f, -0x18,0x65,0x61,0x64,0x6c,0x65,0x74,0x74,0x65,0x72,0x31,0x1d,0x6e,0x74,0x69,0x6c, -0x6c,0x61,0x74,0x69,0x6f,0x6e,0x6d,0x61,0x72,0x6b,0x29,0x16,0x76,0x61,0x67,0x72, -0x61,0x68,0x61,0x23,1,0x69,0x4a,0x72,0x10,0x61,0x1f,0x68,0x6d,0x69,0x6a,0x6f, -0x69,0x6e,0x69,0x6e,0x67,0x6e,0x75,0x6d,0x62,0x65,0x72,0x27,0x12,0x6e,0x64,0x75, -0x25,2,0x72,0x38,0x74,0x46,0x75,0x26,0x15,0x70,0x72,0x69,0x67,0x68,0x74,0x27, -0x20,0x15,0x6f,0x74,0x61,0x74,0x65,0x64,0x21,1,0x72,0x24,0x75,0x25,0x22,0x18, -0x61,0x6e,0x73,0x66,0x6f,0x72,0x6d,0x65,0x64,1,0x72,0x32,0x75,0x15,0x70,0x72, -0x69,0x67,0x68,0x74,0x25,0x15,0x6f,0x74,0x61,0x74,0x65,0x64,0x23,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[23100]={ -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, -2,'O','V',0,'O','v','e','r','l','a','y',0,2,'H','A','N','R',0,'H','a','n','_','R','e','a','d','i','n','g',0, -2,'N','K',0,'N','u','k','t','a',0,2,'K','V',0,'K','a','n','a','_','V','o','i','c','i','n','g',0, -2,'V','R',0,'V','i','r','a','m','a',0,2,'C','C','C','1','0',0,'C','C','C','1','0',0, -2,'C','C','C','1','1',0,'C','C','C','1','1',0,2,'C','C','C','1','2',0,'C','C','C','1','2',0, -2,'C','C','C','1','3',0,'C','C','C','1','3',0,2,'C','C','C','1','4',0,'C','C','C','1','4',0, -2,'C','C','C','1','5',0,'C','C','C','1','5',0,2,'C','C','C','1','6',0,'C','C','C','1','6',0, -2,'C','C','C','1','7',0,'C','C','C','1','7',0,2,'C','C','C','1','8',0,'C','C','C','1','8',0, -2,'C','C','C','1','9',0,'C','C','C','1','9',0,2,'C','C','C','2','0',0,'C','C','C','2','0',0, -2,'C','C','C','2','1',0,'C','C','C','2','1',0,2,'C','C','C','2','2',0,'C','C','C','2','2',0, -2,'C','C','C','2','3',0,'C','C','C','2','3',0,2,'C','C','C','2','4',0,'C','C','C','2','4',0, -2,'C','C','C','2','5',0,'C','C','C','2','5',0,2,'C','C','C','2','6',0,'C','C','C','2','6',0, -2,'C','C','C','2','7',0,'C','C','C','2','7',0,2,'C','C','C','2','8',0,'C','C','C','2','8',0, -2,'C','C','C','2','9',0,'C','C','C','2','9',0,2,'C','C','C','3','0',0,'C','C','C','3','0',0, -2,'C','C','C','3','1',0,'C','C','C','3','1',0,2,'C','C','C','3','2',0,'C','C','C','3','2',0, -2,'C','C','C','3','3',0,'C','C','C','3','3',0,2,'C','C','C','3','4',0,'C','C','C','3','4',0, -2,'C','C','C','3','5',0,'C','C','C','3','5',0,2,'C','C','C','3','6',0,'C','C','C','3','6',0, -2,'C','C','C','8','4',0,'C','C','C','8','4',0,2,'C','C','C','9','1',0,'C','C','C','9','1',0, -2,'C','C','C','1','0','3',0,'C','C','C','1','0','3',0,2,'C','C','C','1','0','7',0,'C','C','C','1','0','7',0, -2,'C','C','C','1','1','8',0,'C','C','C','1','1','8',0,2,'C','C','C','1','2','2',0,'C','C','C','1','2','2',0, -2,'C','C','C','1','2','9',0,'C','C','C','1','2','9',0,2,'C','C','C','1','3','0',0,'C','C','C','1','3','0',0, -2,'C','C','C','1','3','2',0,'C','C','C','1','3','2',0,2,'C','C','C','1','3','3',0,'C','C','C','1','3','3',0, -2,'A','T','B','L',0,'A','t','t','a','c','h','e','d','_','B','e','l','o','w','_','L','e','f','t',0, -2,'A','T','B',0,'A','t','t','a','c','h','e','d','_','B','e','l','o','w',0, -2,'A','T','A',0,'A','t','t','a','c','h','e','d','_','A','b','o','v','e',0, -2,'A','T','A','R',0,'A','t','t','a','c','h','e','d','_','A','b','o','v','e','_','R','i','g','h','t',0, -2,'B','L',0,'B','e','l','o','w','_','L','e','f','t',0,2,'B',0,'B','e','l','o','w',0, -2,'B','R',0,'B','e','l','o','w','_','R','i','g','h','t',0, -2,'L',0,'L','e','f','t',0,2,'R',0,'R','i','g','h','t',0, -2,'A','L',0,'A','b','o','v','e','_','L','e','f','t',0,2,'A',0,'A','b','o','v','e',0, -2,'A','R',0,'A','b','o','v','e','_','R','i','g','h','t',0, -2,'D','B',0,'D','o','u','b','l','e','_','B','e','l','o','w',0, -2,'D','A',0,'D','o','u','b','l','e','_','A','b','o','v','e',0, -2,'I','S',0,'I','o','t','a','_','S','u','b','s','c','r','i','p','t',0, -2,'A','H','e','x',0,'A','S','C','I','I','_','H','e','x','_','D','i','g','i','t',0, -2,'B','i','d','i','_','C',0,'B','i','d','i','_','C','o','n','t','r','o','l',0, -2,'B','i','d','i','_','M',0,'B','i','d','i','_','M','i','r','r','o','r','e','d',0, -2,'D','a','s','h',0,'D','a','s','h',0,2,'D','I',0,'D','e','f','a','u','l','t','_','I','g','n','o','r','a','b','l','e', -'_','C','o','d','e','_','P','o','i','n','t',0,2,'D','e','p',0,'D','e','p','r','e','c','a','t','e','d',0, -2,'D','i','a',0,'D','i','a','c','r','i','t','i','c',0,2,'E','x','t',0,'E','x','t','e','n','d','e','r',0, -2,'C','o','m','p','_','E','x',0,'F','u','l','l','_','C','o','m','p','o','s','i','t','i','o','n','_','E','x','c','l','u','s', -'i','o','n',0,2,'G','r','_','B','a','s','e',0,'G','r','a','p','h','e','m','e','_','B','a','s','e',0, -2,'G','r','_','E','x','t',0,'G','r','a','p','h','e','m','e','_','E','x','t','e','n','d',0, -2,'G','r','_','L','i','n','k',0,'G','r','a','p','h','e','m','e','_','L','i','n','k',0, -2,'H','e','x',0,'H','e','x','_','D','i','g','i','t',0,2,'H','y','p','h','e','n',0,'H','y','p','h','e','n',0, -2,'I','D','C',0,'I','D','_','C','o','n','t','i','n','u','e',0, -2,'I','D','S',0,'I','D','_','S','t','a','r','t',0,2,'I','d','e','o',0,'I','d','e','o','g','r','a','p','h','i','c',0, -2,'I','D','S','B',0,'I','D','S','_','B','i','n','a','r','y','_','O','p','e','r','a','t','o','r',0, -2,'I','D','S','T',0,'I','D','S','_','T','r','i','n','a','r','y','_','O','p','e','r','a','t','o','r',0, -2,'J','o','i','n','_','C',0,'J','o','i','n','_','C','o','n','t','r','o','l',0, -2,'L','O','E',0,'L','o','g','i','c','a','l','_','O','r','d','e','r','_','E','x','c','e','p','t','i','o','n',0, -2,'L','o','w','e','r',0,'L','o','w','e','r','c','a','s','e',0, -2,'M','a','t','h',0,'M','a','t','h',0,2,'N','C','h','a','r',0,'N','o','n','c','h','a','r','a','c','t','e','r','_','C', -'o','d','e','_','P','o','i','n','t',0,2,'Q','M','a','r','k',0,'Q','u','o','t','a','t','i','o','n','_','M','a','r','k',0, -2,'R','a','d','i','c','a','l',0,'R','a','d','i','c','a','l',0, -2,'S','D',0,'S','o','f','t','_','D','o','t','t','e','d',0, -2,'T','e','r','m',0,'T','e','r','m','i','n','a','l','_','P','u','n','c','t','u','a','t','i','o','n',0, -2,'U','I','d','e','o',0,'U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h',0, -2,'U','p','p','e','r',0,'U','p','p','e','r','c','a','s','e',0, -3,'W','S','p','a','c','e',0,'W','h','i','t','e','_','S','p','a','c','e',0,'s','p','a','c','e',0, -2,'X','I','D','C',0,'X','I','D','_','C','o','n','t','i','n','u','e',0, -2,'X','I','D','S',0,'X','I','D','_','S','t','a','r','t',0, -2,'S','e','n','s','i','t','i','v','e',0,'C','a','s','e','_','S','e','n','s','i','t','i','v','e',0, -2,'S','T','e','r','m',0,'S','e','n','t','e','n','c','e','_','T','e','r','m','i','n','a','l',0, -2,'V','S',0,'V','a','r','i','a','t','i','o','n','_','S','e','l','e','c','t','o','r',0, -2,'n','f','d','i','n','e','r','t',0,'N','F','D','_','I','n','e','r','t',0, -2,'n','f','k','d','i','n','e','r','t',0,'N','F','K','D','_','I','n','e','r','t',0, -2,'n','f','c','i','n','e','r','t',0,'N','F','C','_','I','n','e','r','t',0, -2,'n','f','k','c','i','n','e','r','t',0,'N','F','K','C','_','I','n','e','r','t',0, -2,'s','e','g','s','t','a','r','t',0,'S','e','g','m','e','n','t','_','S','t','a','r','t','e','r',0, -2,'P','a','t','_','S','y','n',0,'P','a','t','t','e','r','n','_','S','y','n','t','a','x',0, -2,'P','a','t','_','W','S',0,'P','a','t','t','e','r','n','_','W','h','i','t','e','_','S','p','a','c','e',0, -2,0,'a','l','n','u','m',0,2,0,'b','l','a','n','k',0, -2,0,'g','r','a','p','h',0,2,0,'p','r','i','n','t',0, -2,0,'x','d','i','g','i','t',0,2,'C','a','s','e','d',0,'C','a','s','e','d',0, -2,'C','I',0,'C','a','s','e','_','I','g','n','o','r','a','b','l','e',0, -2,'C','W','L',0,'C','h','a','n','g','e','s','_','W','h','e','n','_','L','o','w','e','r','c','a','s','e','d',0, -2,'C','W','U',0,'C','h','a','n','g','e','s','_','W','h','e','n','_','U','p','p','e','r','c','a','s','e','d',0, -2,'C','W','T',0,'C','h','a','n','g','e','s','_','W','h','e','n','_','T','i','t','l','e','c','a','s','e','d',0, -2,'C','W','C','F',0,'C','h','a','n','g','e','s','_','W','h','e','n','_','C','a','s','e','f','o','l','d','e','d',0, -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','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,'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','a','s','i','c','_','E','m','o','j','i',0,'B','a','s','i','c','_','E','m','o','j','i',0, -2,'E','m','o','j','i','_','K','e','y','c','a','p','_','S','e','q','u','e','n','c','e',0,'E','m','o','j','i','_','K','e','y', -'c','a','p','_','S','e','q','u','e','n','c','e',0,2,'R','G','I','_','E','m','o','j','i','_','M','o','d','i','f','i','e','r', -'_','S','e','q','u','e','n','c','e',0,'R','G','I','_','E','m','o','j','i','_','M','o','d','i','f','i','e','r','_','S','e','q', -'u','e','n','c','e',0,2,'R','G','I','_','E','m','o','j','i','_','F','l','a','g','_','S','e','q','u','e','n','c','e',0, -'R','G','I','_','E','m','o','j','i','_','F','l','a','g','_','S','e','q','u','e','n','c','e',0, -2,'R','G','I','_','E','m','o','j','i','_','T','a','g','_','S','e','q','u','e','n','c','e',0, -'R','G','I','_','E','m','o','j','i','_','T','a','g','_','S','e','q','u','e','n','c','e',0, -2,'R','G','I','_','E','m','o','j','i','_','Z','W','J','_','S','e','q','u','e','n','c','e',0, -'R','G','I','_','E','m','o','j','i','_','Z','W','J','_','S','e','q','u','e','n','c','e',0, -2,'R','G','I','_','E','m','o','j','i',0,'R','G','I','_','E','m','o','j','i',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, -2,'E','S',0,'E','u','r','o','p','e','a','n','_','S','e','p','a','r','a','t','o','r',0, -2,'E','T',0,'E','u','r','o','p','e','a','n','_','T','e','r','m','i','n','a','t','o','r',0, -2,'A','N',0,'A','r','a','b','i','c','_','N','u','m','b','e','r',0, -2,'C','S',0,'C','o','m','m','o','n','_','S','e','p','a','r','a','t','o','r',0, -2,'B',0,'P','a','r','a','g','r','a','p','h','_','S','e','p','a','r','a','t','o','r',0, -2,'S',0,'S','e','g','m','e','n','t','_','S','e','p','a','r','a','t','o','r',0, -2,'W','S',0,'W','h','i','t','e','_','S','p','a','c','e',0, -2,'O','N',0,'O','t','h','e','r','_','N','e','u','t','r','a','l',0, -2,'L','R','E',0,'L','e','f','t','_','T','o','_','R','i','g','h','t','_','E','m','b','e','d','d','i','n','g',0, -2,'L','R','O',0,'L','e','f','t','_','T','o','_','R','i','g','h','t','_','O','v','e','r','r','i','d','e',0, -2,'A','L',0,'A','r','a','b','i','c','_','L','e','t','t','e','r',0, -2,'R','L','E',0,'R','i','g','h','t','_','T','o','_','L','e','f','t','_','E','m','b','e','d','d','i','n','g',0, -2,'R','L','O',0,'R','i','g','h','t','_','T','o','_','L','e','f','t','_','O','v','e','r','r','i','d','e',0, -2,'P','D','F',0,'P','o','p','_','D','i','r','e','c','t','i','o','n','a','l','_','F','o','r','m','a','t',0, -2,'N','S','M',0,'N','o','n','s','p','a','c','i','n','g','_','M','a','r','k',0, -2,'B','N',0,'B','o','u','n','d','a','r','y','_','N','e','u','t','r','a','l',0, -2,'F','S','I',0,'F','i','r','s','t','_','S','t','r','o','n','g','_','I','s','o','l','a','t','e',0, -2,'L','R','I',0,'L','e','f','t','_','T','o','_','R','i','g','h','t','_','I','s','o','l','a','t','e',0, -2,'R','L','I',0,'R','i','g','h','t','_','T','o','_','L','e','f','t','_','I','s','o','l','a','t','e',0, -2,'P','D','I',0,'P','o','p','_','D','i','r','e','c','t','i','o','n','a','l','_','I','s','o','l','a','t','e',0, -2,'b','l','k',0,'B','l','o','c','k',0,2,'N','B',0,'N','o','_','B','l','o','c','k',0, -2,'A','S','C','I','I',0,'B','a','s','i','c','_','L','a','t','i','n',0, -3,'L','a','t','i','n','_','1','_','S','u','p',0,'L','a','t','i','n','_','1','_','S','u','p','p','l','e','m','e','n','t',0, -'L','a','t','i','n','_','1',0,2,'L','a','t','i','n','_','E','x','t','_','A',0,'L','a','t','i','n','_','E','x','t','e','n', -'d','e','d','_','A',0,2,'L','a','t','i','n','_','E','x','t','_','B',0,'L','a','t','i','n','_','E','x','t','e','n','d','e', -'d','_','B',0,2,'I','P','A','_','E','x','t',0,'I','P','A','_','E','x','t','e','n','s','i','o','n','s',0, -2,'M','o','d','i','f','i','e','r','_','L','e','t','t','e','r','s',0,'S','p','a','c','i','n','g','_','M','o','d','i','f','i', -'e','r','_','L','e','t','t','e','r','s',0,2,'D','i','a','c','r','i','t','i','c','a','l','s',0, -'C','o','m','b','i','n','i','n','g','_','D','i','a','c','r','i','t','i','c','a','l','_','M','a','r','k','s',0, -2,'G','r','e','e','k',0,'G','r','e','e','k','_','A','n','d','_','C','o','p','t','i','c',0, -2,'C','y','r','i','l','l','i','c',0,'C','y','r','i','l','l','i','c',0, -2,'A','r','m','e','n','i','a','n',0,'A','r','m','e','n','i','a','n',0, -2,'H','e','b','r','e','w',0,'H','e','b','r','e','w',0,2,'A','r','a','b','i','c',0,'A','r','a','b','i','c',0, -2,'S','y','r','i','a','c',0,'S','y','r','i','a','c',0,2,'T','h','a','a','n','a',0,'T','h','a','a','n','a',0, -2,'D','e','v','a','n','a','g','a','r','i',0,'D','e','v','a','n','a','g','a','r','i',0, -2,'B','e','n','g','a','l','i',0,'B','e','n','g','a','l','i',0, -2,'G','u','r','m','u','k','h','i',0,'G','u','r','m','u','k','h','i',0, -2,'G','u','j','a','r','a','t','i',0,'G','u','j','a','r','a','t','i',0, -2,'O','r','i','y','a',0,'O','r','i','y','a',0,2,'T','a','m','i','l',0,'T','a','m','i','l',0, -2,'T','e','l','u','g','u',0,'T','e','l','u','g','u',0,2,'K','a','n','n','a','d','a',0, -'K','a','n','n','a','d','a',0,2,'M','a','l','a','y','a','l','a','m',0,'M','a','l','a','y','a','l','a','m',0, -2,'S','i','n','h','a','l','a',0,'S','i','n','h','a','l','a',0, -2,'T','h','a','i',0,'T','h','a','i',0,2,'L','a','o',0,'L','a','o',0, -2,'T','i','b','e','t','a','n',0,'T','i','b','e','t','a','n',0, -2,'M','y','a','n','m','a','r',0,'M','y','a','n','m','a','r',0, -2,'G','e','o','r','g','i','a','n',0,'G','e','o','r','g','i','a','n',0, -2,'J','a','m','o',0,'H','a','n','g','u','l','_','J','a','m','o',0, -2,'E','t','h','i','o','p','i','c',0,'E','t','h','i','o','p','i','c',0, -2,'C','h','e','r','o','k','e','e',0,'C','h','e','r','o','k','e','e',0, -3,'U','C','A','S',0,'U','n','i','f','i','e','d','_','C','a','n','a','d','i','a','n','_','A','b','o','r','i','g','i','n','a', -'l','_','S','y','l','l','a','b','i','c','s',0,'C','a','n','a','d','i','a','n','_','S','y','l','l','a','b','i','c','s',0, -2,'O','g','h','a','m',0,'O','g','h','a','m',0,2,'R','u','n','i','c',0,'R','u','n','i','c',0, -2,'K','h','m','e','r',0,'K','h','m','e','r',0,2,'M','o','n','g','o','l','i','a','n',0, -'M','o','n','g','o','l','i','a','n',0,2,'L','a','t','i','n','_','E','x','t','_','A','d','d','i','t','i','o','n','a','l',0, -'L','a','t','i','n','_','E','x','t','e','n','d','e','d','_','A','d','d','i','t','i','o','n','a','l',0, -2,'G','r','e','e','k','_','E','x','t',0,'G','r','e','e','k','_','E','x','t','e','n','d','e','d',0, -2,'P','u','n','c','t','u','a','t','i','o','n',0,'G','e','n','e','r','a','l','_','P','u','n','c','t','u','a','t','i','o','n', -0,2,'S','u','p','e','r','_','A','n','d','_','S','u','b',0,'S','u','p','e','r','s','c','r','i','p','t','s','_','A','n','d', -'_','S','u','b','s','c','r','i','p','t','s',0,2,'C','u','r','r','e','n','c','y','_','S','y','m','b','o','l','s',0, -'C','u','r','r','e','n','c','y','_','S','y','m','b','o','l','s',0, -3,'D','i','a','c','r','i','t','i','c','a','l','s','_','F','o','r','_','S','y','m','b','o','l','s',0, -'C','o','m','b','i','n','i','n','g','_','D','i','a','c','r','i','t','i','c','a','l','_','M','a','r','k','s','_','F','o','r','_', -'S','y','m','b','o','l','s',0,'C','o','m','b','i','n','i','n','g','_','M','a','r','k','s','_','F','o','r','_','S','y','m','b', -'o','l','s',0,2,'L','e','t','t','e','r','l','i','k','e','_','S','y','m','b','o','l','s',0, -'L','e','t','t','e','r','l','i','k','e','_','S','y','m','b','o','l','s',0, -2,'N','u','m','b','e','r','_','F','o','r','m','s',0,'N','u','m','b','e','r','_','F','o','r','m','s',0, -2,'A','r','r','o','w','s',0,'A','r','r','o','w','s',0,2,'M','a','t','h','_','O','p','e','r','a','t','o','r','s',0, -'M','a','t','h','e','m','a','t','i','c','a','l','_','O','p','e','r','a','t','o','r','s',0, -2,'M','i','s','c','_','T','e','c','h','n','i','c','a','l',0,'M','i','s','c','e','l','l','a','n','e','o','u','s','_','T','e', -'c','h','n','i','c','a','l',0,2,'C','o','n','t','r','o','l','_','P','i','c','t','u','r','e','s',0, -'C','o','n','t','r','o','l','_','P','i','c','t','u','r','e','s',0, -2,'O','C','R',0,'O','p','t','i','c','a','l','_','C','h','a','r','a','c','t','e','r','_','R','e','c','o','g','n','i','t','i', -'o','n',0,2,'E','n','c','l','o','s','e','d','_','A','l','p','h','a','n','u','m',0,'E','n','c','l','o','s','e','d','_','A', -'l','p','h','a','n','u','m','e','r','i','c','s',0,2,'B','o','x','_','D','r','a','w','i','n','g',0, -'B','o','x','_','D','r','a','w','i','n','g',0,2,'B','l','o','c','k','_','E','l','e','m','e','n','t','s',0, -'B','l','o','c','k','_','E','l','e','m','e','n','t','s',0,2,'G','e','o','m','e','t','r','i','c','_','S','h','a','p','e','s', -0,'G','e','o','m','e','t','r','i','c','_','S','h','a','p','e','s',0, -2,'M','i','s','c','_','S','y','m','b','o','l','s',0,'M','i','s','c','e','l','l','a','n','e','o','u','s','_','S','y','m','b', -'o','l','s',0,2,'D','i','n','g','b','a','t','s',0,'D','i','n','g','b','a','t','s',0, -2,'B','r','a','i','l','l','e',0,'B','r','a','i','l','l','e','_','P','a','t','t','e','r','n','s',0, -2,'C','J','K','_','R','a','d','i','c','a','l','s','_','S','u','p',0,'C','J','K','_','R','a','d','i','c','a','l','s','_','S', -'u','p','p','l','e','m','e','n','t',0,2,'K','a','n','g','x','i',0,'K','a','n','g','x','i','_','R','a','d','i','c','a','l', -'s',0,2,'I','D','C',0,'I','d','e','o','g','r','a','p','h','i','c','_','D','e','s','c','r','i','p','t','i','o','n','_','C', -'h','a','r','a','c','t','e','r','s',0,2,'C','J','K','_','S','y','m','b','o','l','s',0,'C','J','K','_','S','y','m','b','o', -'l','s','_','A','n','d','_','P','u','n','c','t','u','a','t','i','o','n',0, -2,'H','i','r','a','g','a','n','a',0,'H','i','r','a','g','a','n','a',0, -2,'K','a','t','a','k','a','n','a',0,'K','a','t','a','k','a','n','a',0, -2,'B','o','p','o','m','o','f','o',0,'B','o','p','o','m','o','f','o',0, -2,'C','o','m','p','a','t','_','J','a','m','o',0,'H','a','n','g','u','l','_','C','o','m','p','a','t','i','b','i','l','i','t', -'y','_','J','a','m','o',0,2,'K','a','n','b','u','n',0,'K','a','n','b','u','n',0, -2,'B','o','p','o','m','o','f','o','_','E','x','t',0,'B','o','p','o','m','o','f','o','_','E','x','t','e','n','d','e','d',0, -2,'E','n','c','l','o','s','e','d','_','C','J','K',0,'E','n','c','l','o','s','e','d','_','C','J','K','_','L','e','t','t','e', -'r','s','_','A','n','d','_','M','o','n','t','h','s',0,2,'C','J','K','_','C','o','m','p','a','t',0, -'C','J','K','_','C','o','m','p','a','t','i','b','i','l','i','t','y',0, -2,'C','J','K','_','E','x','t','_','A',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h', -'s','_','E','x','t','e','n','s','i','o','n','_','A',0,2,'C','J','K',0,'C','J','K','_','U','n','i','f','i','e','d','_','I', -'d','e','o','g','r','a','p','h','s',0,2,'Y','i','_','S','y','l','l','a','b','l','e','s',0, -'Y','i','_','S','y','l','l','a','b','l','e','s',0,2,'Y','i','_','R','a','d','i','c','a','l','s',0, -'Y','i','_','R','a','d','i','c','a','l','s',0,2,'H','a','n','g','u','l',0,'H','a','n','g','u','l','_','S','y','l','l','a', -'b','l','e','s',0,2,'H','i','g','h','_','S','u','r','r','o','g','a','t','e','s',0,'H','i','g','h','_','S','u','r','r','o', -'g','a','t','e','s',0,2,'H','i','g','h','_','P','U','_','S','u','r','r','o','g','a','t','e','s',0, -'H','i','g','h','_','P','r','i','v','a','t','e','_','U','s','e','_','S','u','r','r','o','g','a','t','e','s',0, -2,'L','o','w','_','S','u','r','r','o','g','a','t','e','s',0,'L','o','w','_','S','u','r','r','o','g','a','t','e','s',0, -3,'P','U','A',0,'P','r','i','v','a','t','e','_','U','s','e','_','A','r','e','a',0,'P','r','i','v','a','t','e','_','U','s', -'e',0,2,'C','J','K','_','C','o','m','p','a','t','_','I','d','e','o','g','r','a','p','h','s',0, -'C','J','K','_','C','o','m','p','a','t','i','b','i','l','i','t','y','_','I','d','e','o','g','r','a','p','h','s',0, -2,'A','l','p','h','a','b','e','t','i','c','_','P','F',0,'A','l','p','h','a','b','e','t','i','c','_','P','r','e','s','e','n', -'t','a','t','i','o','n','_','F','o','r','m','s',0,3,'A','r','a','b','i','c','_','P','F','_','A',0, -'A','r','a','b','i','c','_','P','r','e','s','e','n','t','a','t','i','o','n','_','F','o','r','m','s','_','A',0, -'A','r','a','b','i','c','_','P','r','e','s','e','n','t','a','t','i','o','n','_','F','o','r','m','s','-','A',0, -2,'H','a','l','f','_','M','a','r','k','s',0,'C','o','m','b','i','n','i','n','g','_','H','a','l','f','_','M','a','r','k','s', -0,2,'C','J','K','_','C','o','m','p','a','t','_','F','o','r','m','s',0,'C','J','K','_','C','o','m','p','a','t','i','b','i', -'l','i','t','y','_','F','o','r','m','s',0,2,'S','m','a','l','l','_','F','o','r','m','s',0, -'S','m','a','l','l','_','F','o','r','m','_','V','a','r','i','a','n','t','s',0, -2,'A','r','a','b','i','c','_','P','F','_','B',0,'A','r','a','b','i','c','_','P','r','e','s','e','n','t','a','t','i','o','n', -'_','F','o','r','m','s','_','B',0,2,'S','p','e','c','i','a','l','s',0,'S','p','e','c','i','a','l','s',0, -2,'H','a','l','f','_','A','n','d','_','F','u','l','l','_','F','o','r','m','s',0,'H','a','l','f','w','i','d','t','h','_','A', -'n','d','_','F','u','l','l','w','i','d','t','h','_','F','o','r','m','s',0, -2,'O','l','d','_','I','t','a','l','i','c',0,'O','l','d','_','I','t','a','l','i','c',0, -2,'G','o','t','h','i','c',0,'G','o','t','h','i','c',0,2,'D','e','s','e','r','e','t',0, -'D','e','s','e','r','e','t',0,2,'B','y','z','a','n','t','i','n','e','_','M','u','s','i','c',0, -'B','y','z','a','n','t','i','n','e','_','M','u','s','i','c','a','l','_','S','y','m','b','o','l','s',0, -2,'M','u','s','i','c',0,'M','u','s','i','c','a','l','_','S','y','m','b','o','l','s',0, -2,'M','a','t','h','_','A','l','p','h','a','n','u','m',0,'M','a','t','h','e','m','a','t','i','c','a','l','_','A','l','p','h', -'a','n','u','m','e','r','i','c','_','S','y','m','b','o','l','s',0, -2,'C','J','K','_','E','x','t','_','B',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h', -'s','_','E','x','t','e','n','s','i','o','n','_','B',0,2,'C','J','K','_','C','o','m','p','a','t','_','I','d','e','o','g','r', -'a','p','h','s','_','S','u','p',0,'C','J','K','_','C','o','m','p','a','t','i','b','i','l','i','t','y','_','I','d','e','o','g', -'r','a','p','h','s','_','S','u','p','p','l','e','m','e','n','t',0, -2,'T','a','g','s',0,'T','a','g','s',0,3,'C','y','r','i','l','l','i','c','_','S','u','p',0, -'C','y','r','i','l','l','i','c','_','S','u','p','p','l','e','m','e','n','t',0,'C','y','r','i','l','l','i','c','_','S','u','p', -'p','l','e','m','e','n','t','a','r','y',0,2,'T','a','g','a','l','o','g',0,'T','a','g','a','l','o','g',0, -2,'H','a','n','u','n','o','o',0,'H','a','n','u','n','o','o',0, -2,'B','u','h','i','d',0,'B','u','h','i','d',0,2,'T','a','g','b','a','n','w','a',0,'T','a','g','b','a','n','w','a',0, -2,'M','i','s','c','_','M','a','t','h','_','S','y','m','b','o','l','s','_','A',0,'M','i','s','c','e','l','l','a','n','e','o', -'u','s','_','M','a','t','h','e','m','a','t','i','c','a','l','_','S','y','m','b','o','l','s','_','A',0, -2,'S','u','p','_','A','r','r','o','w','s','_','A',0,'S','u','p','p','l','e','m','e','n','t','a','l','_','A','r','r','o','w', -'s','_','A',0,2,'S','u','p','_','A','r','r','o','w','s','_','B',0,'S','u','p','p','l','e','m','e','n','t','a','l','_','A', -'r','r','o','w','s','_','B',0,2,'M','i','s','c','_','M','a','t','h','_','S','y','m','b','o','l','s','_','B',0, -'M','i','s','c','e','l','l','a','n','e','o','u','s','_','M','a','t','h','e','m','a','t','i','c','a','l','_','S','y','m','b','o', -'l','s','_','B',0,2,'S','u','p','_','M','a','t','h','_','O','p','e','r','a','t','o','r','s',0, -'S','u','p','p','l','e','m','e','n','t','a','l','_','M','a','t','h','e','m','a','t','i','c','a','l','_','O','p','e','r','a','t', -'o','r','s',0,2,'K','a','t','a','k','a','n','a','_','E','x','t',0,'K','a','t','a','k','a','n','a','_','P','h','o','n','e', -'t','i','c','_','E','x','t','e','n','s','i','o','n','s',0,2,'V','S',0,'V','a','r','i','a','t','i','o','n','_','S','e','l', -'e','c','t','o','r','s',0,2,'S','u','p','_','P','U','A','_','A',0,'S','u','p','p','l','e','m','e','n','t','a','r','y','_', -'P','r','i','v','a','t','e','_','U','s','e','_','A','r','e','a','_','A',0, -2,'S','u','p','_','P','U','A','_','B',0,'S','u','p','p','l','e','m','e','n','t','a','r','y','_','P','r','i','v','a','t','e', -'_','U','s','e','_','A','r','e','a','_','B',0,2,'L','i','m','b','u',0,'L','i','m','b','u',0, -2,'T','a','i','_','L','e',0,'T','a','i','_','L','e',0,2,'K','h','m','e','r','_','S','y','m','b','o','l','s',0, -'K','h','m','e','r','_','S','y','m','b','o','l','s',0,2,'P','h','o','n','e','t','i','c','_','E','x','t',0, -'P','h','o','n','e','t','i','c','_','E','x','t','e','n','s','i','o','n','s',0, -2,'M','i','s','c','_','A','r','r','o','w','s',0,'M','i','s','c','e','l','l','a','n','e','o','u','s','_','S','y','m','b','o', -'l','s','_','A','n','d','_','A','r','r','o','w','s',0,2,'Y','i','j','i','n','g',0,'Y','i','j','i','n','g','_','H','e','x', -'a','g','r','a','m','_','S','y','m','b','o','l','s',0,2,'L','i','n','e','a','r','_','B','_','S','y','l','l','a','b','a','r', -'y',0,'L','i','n','e','a','r','_','B','_','S','y','l','l','a','b','a','r','y',0, -2,'L','i','n','e','a','r','_','B','_','I','d','e','o','g','r','a','m','s',0,'L','i','n','e','a','r','_','B','_','I','d','e', -'o','g','r','a','m','s',0,2,'A','e','g','e','a','n','_','N','u','m','b','e','r','s',0,'A','e','g','e','a','n','_','N','u', -'m','b','e','r','s',0,2,'U','g','a','r','i','t','i','c',0,'U','g','a','r','i','t','i','c',0, -2,'S','h','a','v','i','a','n',0,'S','h','a','v','i','a','n',0, -2,'O','s','m','a','n','y','a',0,'O','s','m','a','n','y','a',0, -2,'C','y','p','r','i','o','t','_','S','y','l','l','a','b','a','r','y',0,'C','y','p','r','i','o','t','_','S','y','l','l','a', -'b','a','r','y',0,2,'T','a','i','_','X','u','a','n','_','J','i','n','g',0,'T','a','i','_','X','u','a','n','_','J','i','n', -'g','_','S','y','m','b','o','l','s',0,2,'V','S','_','S','u','p',0,'V','a','r','i','a','t','i','o','n','_','S','e','l','e', -'c','t','o','r','s','_','S','u','p','p','l','e','m','e','n','t',0, -2,'A','n','c','i','e','n','t','_','G','r','e','e','k','_','M','u','s','i','c',0,'A','n','c','i','e','n','t','_','G','r','e', -'e','k','_','M','u','s','i','c','a','l','_','N','o','t','a','t','i','o','n',0, -2,'A','n','c','i','e','n','t','_','G','r','e','e','k','_','N','u','m','b','e','r','s',0,'A','n','c','i','e','n','t','_','G', -'r','e','e','k','_','N','u','m','b','e','r','s',0,2,'A','r','a','b','i','c','_','S','u','p',0, -'A','r','a','b','i','c','_','S','u','p','p','l','e','m','e','n','t',0, -2,'B','u','g','i','n','e','s','e',0,'B','u','g','i','n','e','s','e',0, -2,'C','J','K','_','S','t','r','o','k','e','s',0,'C','J','K','_','S','t','r','o','k','e','s',0, -2,'D','i','a','c','r','i','t','i','c','a','l','s','_','S','u','p',0,'C','o','m','b','i','n','i','n','g','_','D','i','a','c', -'r','i','t','i','c','a','l','_','M','a','r','k','s','_','S','u','p','p','l','e','m','e','n','t',0, -2,'C','o','p','t','i','c',0,'C','o','p','t','i','c',0,2,'E','t','h','i','o','p','i','c','_','E','x','t',0, -'E','t','h','i','o','p','i','c','_','E','x','t','e','n','d','e','d',0, -2,'E','t','h','i','o','p','i','c','_','S','u','p',0,'E','t','h','i','o','p','i','c','_','S','u','p','p','l','e','m','e','n', -'t',0,2,'G','e','o','r','g','i','a','n','_','S','u','p',0,'G','e','o','r','g','i','a','n','_','S','u','p','p','l','e','m', -'e','n','t',0,2,'G','l','a','g','o','l','i','t','i','c',0,'G','l','a','g','o','l','i','t','i','c',0, -2,'K','h','a','r','o','s','h','t','h','i',0,'K','h','a','r','o','s','h','t','h','i',0, -2,'M','o','d','i','f','i','e','r','_','T','o','n','e','_','L','e','t','t','e','r','s',0,'M','o','d','i','f','i','e','r','_', -'T','o','n','e','_','L','e','t','t','e','r','s',0,2,'N','e','w','_','T','a','i','_','L','u','e',0, -'N','e','w','_','T','a','i','_','L','u','e',0,2,'O','l','d','_','P','e','r','s','i','a','n',0, -'O','l','d','_','P','e','r','s','i','a','n',0,2,'P','h','o','n','e','t','i','c','_','E','x','t','_','S','u','p',0, -'P','h','o','n','e','t','i','c','_','E','x','t','e','n','s','i','o','n','s','_','S','u','p','p','l','e','m','e','n','t',0, -2,'S','u','p','_','P','u','n','c','t','u','a','t','i','o','n',0,'S','u','p','p','l','e','m','e','n','t','a','l','_','P','u', -'n','c','t','u','a','t','i','o','n',0,2,'S','y','l','o','t','i','_','N','a','g','r','i',0, -'S','y','l','o','t','i','_','N','a','g','r','i',0,2,'T','i','f','i','n','a','g','h',0,'T','i','f','i','n','a','g','h',0, -2,'V','e','r','t','i','c','a','l','_','F','o','r','m','s',0,'V','e','r','t','i','c','a','l','_','F','o','r','m','s',0, -2,'N','K','o',0,'N','K','o',0,2,'B','a','l','i','n','e','s','e',0,'B','a','l','i','n','e','s','e',0, -2,'L','a','t','i','n','_','E','x','t','_','C',0,'L','a','t','i','n','_','E','x','t','e','n','d','e','d','_','C',0, -2,'L','a','t','i','n','_','E','x','t','_','D',0,'L','a','t','i','n','_','E','x','t','e','n','d','e','d','_','D',0, -2,'P','h','a','g','s','_','P','a',0,'P','h','a','g','s','_','P','a',0, -2,'P','h','o','e','n','i','c','i','a','n',0,'P','h','o','e','n','i','c','i','a','n',0, -2,'C','u','n','e','i','f','o','r','m',0,'C','u','n','e','i','f','o','r','m',0, -2,'C','u','n','e','i','f','o','r','m','_','N','u','m','b','e','r','s',0,'C','u','n','e','i','f','o','r','m','_','N','u','m', -'b','e','r','s','_','A','n','d','_','P','u','n','c','t','u','a','t','i','o','n',0, -2,'C','o','u','n','t','i','n','g','_','R','o','d',0,'C','o','u','n','t','i','n','g','_','R','o','d','_','N','u','m','e','r', -'a','l','s',0,2,'S','u','n','d','a','n','e','s','e',0,'S','u','n','d','a','n','e','s','e',0, -2,'L','e','p','c','h','a',0,'L','e','p','c','h','a',0,2,'O','l','_','C','h','i','k','i',0, -'O','l','_','C','h','i','k','i',0,2,'C','y','r','i','l','l','i','c','_','E','x','t','_','A',0, -'C','y','r','i','l','l','i','c','_','E','x','t','e','n','d','e','d','_','A',0, -2,'V','a','i',0,'V','a','i',0,2,'C','y','r','i','l','l','i','c','_','E','x','t','_','B',0, -'C','y','r','i','l','l','i','c','_','E','x','t','e','n','d','e','d','_','B',0, -2,'S','a','u','r','a','s','h','t','r','a',0,'S','a','u','r','a','s','h','t','r','a',0, -2,'K','a','y','a','h','_','L','i',0,'K','a','y','a','h','_','L','i',0, -2,'R','e','j','a','n','g',0,'R','e','j','a','n','g',0,2,'C','h','a','m',0,'C','h','a','m',0, -2,'A','n','c','i','e','n','t','_','S','y','m','b','o','l','s',0,'A','n','c','i','e','n','t','_','S','y','m','b','o','l','s', -0,2,'P','h','a','i','s','t','o','s',0,'P','h','a','i','s','t','o','s','_','D','i','s','c',0, -2,'L','y','c','i','a','n',0,'L','y','c','i','a','n',0,2,'C','a','r','i','a','n',0,'C','a','r','i','a','n',0, -2,'L','y','d','i','a','n',0,'L','y','d','i','a','n',0,2,'M','a','h','j','o','n','g',0, -'M','a','h','j','o','n','g','_','T','i','l','e','s',0,2,'D','o','m','i','n','o',0,'D','o','m','i','n','o','_','T','i','l', -'e','s',0,2,'S','a','m','a','r','i','t','a','n',0,'S','a','m','a','r','i','t','a','n',0, -2,'U','C','A','S','_','E','x','t',0,'U','n','i','f','i','e','d','_','C','a','n','a','d','i','a','n','_','A','b','o','r','i', -'g','i','n','a','l','_','S','y','l','l','a','b','i','c','s','_','E','x','t','e','n','d','e','d',0, -2,'T','a','i','_','T','h','a','m',0,'T','a','i','_','T','h','a','m',0, -2,'V','e','d','i','c','_','E','x','t',0,'V','e','d','i','c','_','E','x','t','e','n','s','i','o','n','s',0, -2,'L','i','s','u',0,'L','i','s','u',0,2,'B','a','m','u','m',0,'B','a','m','u','m',0, -2,'I','n','d','i','c','_','N','u','m','b','e','r','_','F','o','r','m','s',0,'C','o','m','m','o','n','_','I','n','d','i','c', -'_','N','u','m','b','e','r','_','F','o','r','m','s',0,2,'D','e','v','a','n','a','g','a','r','i','_','E','x','t',0, -'D','e','v','a','n','a','g','a','r','i','_','E','x','t','e','n','d','e','d',0, -2,'J','a','m','o','_','E','x','t','_','A',0,'H','a','n','g','u','l','_','J','a','m','o','_','E','x','t','e','n','d','e','d', -'_','A',0,2,'J','a','v','a','n','e','s','e',0,'J','a','v','a','n','e','s','e',0, -2,'M','y','a','n','m','a','r','_','E','x','t','_','A',0,'M','y','a','n','m','a','r','_','E','x','t','e','n','d','e','d','_', -'A',0,2,'T','a','i','_','V','i','e','t',0,'T','a','i','_','V','i','e','t',0, -2,'M','e','e','t','e','i','_','M','a','y','e','k',0,'M','e','e','t','e','i','_','M','a','y','e','k',0, -2,'J','a','m','o','_','E','x','t','_','B',0,'H','a','n','g','u','l','_','J','a','m','o','_','E','x','t','e','n','d','e','d', -'_','B',0,2,'I','m','p','e','r','i','a','l','_','A','r','a','m','a','i','c',0,'I','m','p','e','r','i','a','l','_','A','r', -'a','m','a','i','c',0,2,'O','l','d','_','S','o','u','t','h','_','A','r','a','b','i','a','n',0, -'O','l','d','_','S','o','u','t','h','_','A','r','a','b','i','a','n',0, -2,'A','v','e','s','t','a','n',0,'A','v','e','s','t','a','n',0, -2,'I','n','s','c','r','i','p','t','i','o','n','a','l','_','P','a','r','t','h','i','a','n',0, -'I','n','s','c','r','i','p','t','i','o','n','a','l','_','P','a','r','t','h','i','a','n',0, -2,'I','n','s','c','r','i','p','t','i','o','n','a','l','_','P','a','h','l','a','v','i',0,'I','n','s','c','r','i','p','t','i', -'o','n','a','l','_','P','a','h','l','a','v','i',0,2,'O','l','d','_','T','u','r','k','i','c',0, -'O','l','d','_','T','u','r','k','i','c',0,2,'R','u','m','i',0,'R','u','m','i','_','N','u','m','e','r','a','l','_','S','y', -'m','b','o','l','s',0,2,'K','a','i','t','h','i',0,'K','a','i','t','h','i',0, -2,'E','g','y','p','t','i','a','n','_','H','i','e','r','o','g','l','y','p','h','s',0,'E','g','y','p','t','i','a','n','_','H', -'i','e','r','o','g','l','y','p','h','s',0,2,'E','n','c','l','o','s','e','d','_','A','l','p','h','a','n','u','m','_','S','u', -'p',0,'E','n','c','l','o','s','e','d','_','A','l','p','h','a','n','u','m','e','r','i','c','_','S','u','p','p','l','e','m','e', -'n','t',0,2,'E','n','c','l','o','s','e','d','_','I','d','e','o','g','r','a','p','h','i','c','_','S','u','p',0, -'E','n','c','l','o','s','e','d','_','I','d','e','o','g','r','a','p','h','i','c','_','S','u','p','p','l','e','m','e','n','t',0, -2,'C','J','K','_','E','x','t','_','C',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h', -'s','_','E','x','t','e','n','s','i','o','n','_','C',0,2,'M','a','n','d','a','i','c',0,'M','a','n','d','a','i','c',0, -2,'B','a','t','a','k',0,'B','a','t','a','k',0,2,'E','t','h','i','o','p','i','c','_','E','x','t','_','A',0, -'E','t','h','i','o','p','i','c','_','E','x','t','e','n','d','e','d','_','A',0, -2,'B','r','a','h','m','i',0,'B','r','a','h','m','i',0,2,'B','a','m','u','m','_','S','u','p',0, -'B','a','m','u','m','_','S','u','p','p','l','e','m','e','n','t',0, -2,'K','a','n','a','_','S','u','p',0,'K','a','n','a','_','S','u','p','p','l','e','m','e','n','t',0, -2,'P','l','a','y','i','n','g','_','C','a','r','d','s',0,'P','l','a','y','i','n','g','_','C','a','r','d','s',0, -2,'M','i','s','c','_','P','i','c','t','o','g','r','a','p','h','s',0,'M','i','s','c','e','l','l','a','n','e','o','u','s','_', -'S','y','m','b','o','l','s','_','A','n','d','_','P','i','c','t','o','g','r','a','p','h','s',0, -2,'E','m','o','t','i','c','o','n','s',0,'E','m','o','t','i','c','o','n','s',0, -2,'T','r','a','n','s','p','o','r','t','_','A','n','d','_','M','a','p',0,'T','r','a','n','s','p','o','r','t','_','A','n','d', -'_','M','a','p','_','S','y','m','b','o','l','s',0,2,'A','l','c','h','e','m','i','c','a','l',0, -'A','l','c','h','e','m','i','c','a','l','_','S','y','m','b','o','l','s',0, -2,'C','J','K','_','E','x','t','_','D',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h', -'s','_','E','x','t','e','n','s','i','o','n','_','D',0,2,'A','r','a','b','i','c','_','E','x','t','_','A',0, -'A','r','a','b','i','c','_','E','x','t','e','n','d','e','d','_','A',0, -2,'A','r','a','b','i','c','_','M','a','t','h',0,'A','r','a','b','i','c','_','M','a','t','h','e','m','a','t','i','c','a','l', -'_','A','l','p','h','a','b','e','t','i','c','_','S','y','m','b','o','l','s',0, -2,'C','h','a','k','m','a',0,'C','h','a','k','m','a',0,2,'M','e','e','t','e','i','_','M','a','y','e','k','_','E','x','t', -0,'M','e','e','t','e','i','_','M','a','y','e','k','_','E','x','t','e','n','s','i','o','n','s',0, -2,'M','e','r','o','i','t','i','c','_','C','u','r','s','i','v','e',0,'M','e','r','o','i','t','i','c','_','C','u','r','s','i', -'v','e',0,2,'M','e','r','o','i','t','i','c','_','H','i','e','r','o','g','l','y','p','h','s',0, -'M','e','r','o','i','t','i','c','_','H','i','e','r','o','g','l','y','p','h','s',0, -2,'M','i','a','o',0,'M','i','a','o',0,2,'S','h','a','r','a','d','a',0,'S','h','a','r','a','d','a',0, -2,'S','o','r','a','_','S','o','m','p','e','n','g',0,'S','o','r','a','_','S','o','m','p','e','n','g',0, -2,'S','u','n','d','a','n','e','s','e','_','S','u','p',0,'S','u','n','d','a','n','e','s','e','_','S','u','p','p','l','e','m', -'e','n','t',0,2,'T','a','k','r','i',0,'T','a','k','r','i',0, -2,'B','a','s','s','a','_','V','a','h',0,'B','a','s','s','a','_','V','a','h',0, -2,'C','a','u','c','a','s','i','a','n','_','A','l','b','a','n','i','a','n',0,'C','a','u','c','a','s','i','a','n','_','A','l', -'b','a','n','i','a','n',0,2,'C','o','p','t','i','c','_','E','p','a','c','t','_','N','u','m','b','e','r','s',0, -'C','o','p','t','i','c','_','E','p','a','c','t','_','N','u','m','b','e','r','s',0, -2,'D','i','a','c','r','i','t','i','c','a','l','s','_','E','x','t',0,'C','o','m','b','i','n','i','n','g','_','D','i','a','c', -'r','i','t','i','c','a','l','_','M','a','r','k','s','_','E','x','t','e','n','d','e','d',0, -2,'D','u','p','l','o','y','a','n',0,'D','u','p','l','o','y','a','n',0, -2,'E','l','b','a','s','a','n',0,'E','l','b','a','s','a','n',0, -2,'G','e','o','m','e','t','r','i','c','_','S','h','a','p','e','s','_','E','x','t',0,'G','e','o','m','e','t','r','i','c','_', -'S','h','a','p','e','s','_','E','x','t','e','n','d','e','d',0, -2,'G','r','a','n','t','h','a',0,'G','r','a','n','t','h','a',0, -2,'K','h','o','j','k','i',0,'K','h','o','j','k','i',0,2,'K','h','u','d','a','w','a','d','i',0, -'K','h','u','d','a','w','a','d','i',0,2,'L','a','t','i','n','_','E','x','t','_','E',0,'L','a','t','i','n','_','E','x','t', -'e','n','d','e','d','_','E',0,2,'L','i','n','e','a','r','_','A',0,'L','i','n','e','a','r','_','A',0, -2,'M','a','h','a','j','a','n','i',0,'M','a','h','a','j','a','n','i',0, -2,'M','a','n','i','c','h','a','e','a','n',0,'M','a','n','i','c','h','a','e','a','n',0, -2,'M','e','n','d','e','_','K','i','k','a','k','u','i',0,'M','e','n','d','e','_','K','i','k','a','k','u','i',0, -2,'M','o','d','i',0,'M','o','d','i',0,2,'M','r','o',0,'M','r','o',0, -2,'M','y','a','n','m','a','r','_','E','x','t','_','B',0,'M','y','a','n','m','a','r','_','E','x','t','e','n','d','e','d','_', -'B',0,2,'N','a','b','a','t','a','e','a','n',0,'N','a','b','a','t','a','e','a','n',0, -2,'O','l','d','_','N','o','r','t','h','_','A','r','a','b','i','a','n',0,'O','l','d','_','N','o','r','t','h','_','A','r','a', -'b','i','a','n',0,2,'O','l','d','_','P','e','r','m','i','c',0,'O','l','d','_','P','e','r','m','i','c',0, -2,'O','r','n','a','m','e','n','t','a','l','_','D','i','n','g','b','a','t','s',0,'O','r','n','a','m','e','n','t','a','l','_', -'D','i','n','g','b','a','t','s',0,2,'P','a','h','a','w','h','_','H','m','o','n','g',0,'P','a','h','a','w','h','_','H','m', -'o','n','g',0,2,'P','a','l','m','y','r','e','n','e',0,'P','a','l','m','y','r','e','n','e',0, -2,'P','a','u','_','C','i','n','_','H','a','u',0,'P','a','u','_','C','i','n','_','H','a','u',0, -2,'P','s','a','l','t','e','r','_','P','a','h','l','a','v','i',0,'P','s','a','l','t','e','r','_','P','a','h','l','a','v','i', -0,2,'S','h','o','r','t','h','a','n','d','_','F','o','r','m','a','t','_','C','o','n','t','r','o','l','s',0, -'S','h','o','r','t','h','a','n','d','_','F','o','r','m','a','t','_','C','o','n','t','r','o','l','s',0, -2,'S','i','d','d','h','a','m',0,'S','i','d','d','h','a','m',0, -2,'S','i','n','h','a','l','a','_','A','r','c','h','a','i','c','_','N','u','m','b','e','r','s',0, -'S','i','n','h','a','l','a','_','A','r','c','h','a','i','c','_','N','u','m','b','e','r','s',0, -2,'S','u','p','_','A','r','r','o','w','s','_','C',0,'S','u','p','p','l','e','m','e','n','t','a','l','_','A','r','r','o','w', -'s','_','C',0,2,'T','i','r','h','u','t','a',0,'T','i','r','h','u','t','a',0, -2,'W','a','r','a','n','g','_','C','i','t','i',0,'W','a','r','a','n','g','_','C','i','t','i',0, -2,'A','h','o','m',0,'A','h','o','m',0,2,'A','n','a','t','o','l','i','a','n','_','H','i','e','r','o','g','l','y','p','h', -'s',0,'A','n','a','t','o','l','i','a','n','_','H','i','e','r','o','g','l','y','p','h','s',0, -2,'C','h','e','r','o','k','e','e','_','S','u','p',0,'C','h','e','r','o','k','e','e','_','S','u','p','p','l','e','m','e','n', -'t',0,2,'C','J','K','_','E','x','t','_','E',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a', -'p','h','s','_','E','x','t','e','n','s','i','o','n','_','E',0, -2,'E','a','r','l','y','_','D','y','n','a','s','t','i','c','_','C','u','n','e','i','f','o','r','m',0, -'E','a','r','l','y','_','D','y','n','a','s','t','i','c','_','C','u','n','e','i','f','o','r','m',0, -2,'H','a','t','r','a','n',0,'H','a','t','r','a','n',0,2,'M','u','l','t','a','n','i',0, -'M','u','l','t','a','n','i',0,2,'O','l','d','_','H','u','n','g','a','r','i','a','n',0,'O','l','d','_','H','u','n','g','a', -'r','i','a','n',0,2,'S','u','p','_','S','y','m','b','o','l','s','_','A','n','d','_','P','i','c','t','o','g','r','a','p','h', -'s',0,'S','u','p','p','l','e','m','e','n','t','a','l','_','S','y','m','b','o','l','s','_','A','n','d','_','P','i','c','t','o', -'g','r','a','p','h','s',0,2,'S','u','t','t','o','n','_','S','i','g','n','W','r','i','t','i','n','g',0, -'S','u','t','t','o','n','_','S','i','g','n','W','r','i','t','i','n','g',0, -2,'A','d','l','a','m',0,'A','d','l','a','m',0,2,'B','h','a','i','k','s','u','k','i',0, -'B','h','a','i','k','s','u','k','i',0,2,'C','y','r','i','l','l','i','c','_','E','x','t','_','C',0, -'C','y','r','i','l','l','i','c','_','E','x','t','e','n','d','e','d','_','C',0, -2,'G','l','a','g','o','l','i','t','i','c','_','S','u','p',0,'G','l','a','g','o','l','i','t','i','c','_','S','u','p','p','l', -'e','m','e','n','t',0,2,'I','d','e','o','g','r','a','p','h','i','c','_','S','y','m','b','o','l','s',0, -'I','d','e','o','g','r','a','p','h','i','c','_','S','y','m','b','o','l','s','_','A','n','d','_','P','u','n','c','t','u','a','t', -'i','o','n',0,2,'M','a','r','c','h','e','n',0,'M','a','r','c','h','e','n',0, -2,'M','o','n','g','o','l','i','a','n','_','S','u','p',0,'M','o','n','g','o','l','i','a','n','_','S','u','p','p','l','e','m', -'e','n','t',0,2,'N','e','w','a',0,'N','e','w','a',0,2,'O','s','a','g','e',0,'O','s','a','g','e',0, -2,'T','a','n','g','u','t',0,'T','a','n','g','u','t',0,2,'T','a','n','g','u','t','_','C','o','m','p','o','n','e','n','t', -'s',0,'T','a','n','g','u','t','_','C','o','m','p','o','n','e','n','t','s',0, -2,'C','J','K','_','E','x','t','_','F',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h', -'s','_','E','x','t','e','n','s','i','o','n','_','F',0,2,'K','a','n','a','_','E','x','t','_','A',0, -'K','a','n','a','_','E','x','t','e','n','d','e','d','_','A',0, -2,'M','a','s','a','r','a','m','_','G','o','n','d','i',0,'M','a','s','a','r','a','m','_','G','o','n','d','i',0, -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','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,'E','g','y','p','t','i','a','n','_','H','i','e','r','o','g','l','y','p','h','_','F','o','r','m','a','t','_','C','o','n','t', -'r','o','l','s',0,'E','g','y','p','t','i','a','n','_','H','i','e','r','o','g','l','y','p','h','_','F','o','r','m','a','t','_', -'C','o','n','t','r','o','l','s',0,2,'E','l','y','m','a','i','c',0,'E','l','y','m','a','i','c',0, -2,'N','a','n','d','i','n','a','g','a','r','i',0,'N','a','n','d','i','n','a','g','a','r','i',0, -2,'N','y','i','a','k','e','n','g','_','P','u','a','c','h','u','e','_','H','m','o','n','g',0, -'N','y','i','a','k','e','n','g','_','P','u','a','c','h','u','e','_','H','m','o','n','g',0, -2,'O','t','t','o','m','a','n','_','S','i','y','a','q','_','N','u','m','b','e','r','s',0,'O','t','t','o','m','a','n','_','S', -'i','y','a','q','_','N','u','m','b','e','r','s',0,2,'S','m','a','l','l','_','K','a','n','a','_','E','x','t',0, -'S','m','a','l','l','_','K','a','n','a','_','E','x','t','e','n','s','i','o','n',0, -2,'S','y','m','b','o','l','s','_','A','n','d','_','P','i','c','t','o','g','r','a','p','h','s','_','E','x','t','_','A',0, -'S','y','m','b','o','l','s','_','A','n','d','_','P','i','c','t','o','g','r','a','p','h','s','_','E','x','t','e','n','d','e','d', -'_','A',0,2,'T','a','m','i','l','_','S','u','p',0,'T','a','m','i','l','_','S','u','p','p','l','e','m','e','n','t',0, -2,'W','a','n','c','h','o',0,'W','a','n','c','h','o',0,2,'C','h','o','r','a','s','m','i','a','n',0, -'C','h','o','r','a','s','m','i','a','n',0,2,'C','J','K','_','E','x','t','_','G',0,'C','J','K','_','U','n','i','f','i','e', -'d','_','I','d','e','o','g','r','a','p','h','s','_','E','x','t','e','n','s','i','o','n','_','G',0, -2,'D','i','v','e','s','_','A','k','u','r','u',0,'D','i','v','e','s','_','A','k','u','r','u',0, -2,'K','h','i','t','a','n','_','S','m','a','l','l','_','S','c','r','i','p','t',0,'K','h','i','t','a','n','_','S','m','a','l', -'l','_','S','c','r','i','p','t',0,2,'L','i','s','u','_','S','u','p',0,'L','i','s','u','_','S','u','p','p','l','e','m','e', -'n','t',0,2,'S','y','m','b','o','l','s','_','F','o','r','_','L','e','g','a','c','y','_','C','o','m','p','u','t','i','n','g', -0,'S','y','m','b','o','l','s','_','F','o','r','_','L','e','g','a','c','y','_','C','o','m','p','u','t','i','n','g',0, -2,'T','a','n','g','u','t','_','S','u','p',0,'T','a','n','g','u','t','_','S','u','p','p','l','e','m','e','n','t',0, -2,'Y','e','z','i','d','i',0,'Y','e','z','i','d','i',0,2,'A','r','a','b','i','c','_','E','x','t','_','B',0, -'A','r','a','b','i','c','_','E','x','t','e','n','d','e','d','_','B',0, -2,'C','y','p','r','o','_','M','i','n','o','a','n',0,'C','y','p','r','o','_','M','i','n','o','a','n',0, -2,'E','t','h','i','o','p','i','c','_','E','x','t','_','B',0,'E','t','h','i','o','p','i','c','_','E','x','t','e','n','d','e', -'d','_','B',0,2,'K','a','n','a','_','E','x','t','_','B',0,'K','a','n','a','_','E','x','t','e','n','d','e','d','_','B',0, -2,'L','a','t','i','n','_','E','x','t','_','F',0,'L','a','t','i','n','_','E','x','t','e','n','d','e','d','_','F',0, -2,'L','a','t','i','n','_','E','x','t','_','G',0,'L','a','t','i','n','_','E','x','t','e','n','d','e','d','_','G',0, -2,'O','l','d','_','U','y','g','h','u','r',0,'O','l','d','_','U','y','g','h','u','r',0, -2,'T','a','n','g','s','a',0,'T','a','n','g','s','a',0,2,'T','o','t','o',0,'T','o','t','o',0, -2,'U','C','A','S','_','E','x','t','_','A',0,'U','n','i','f','i','e','d','_','C','a','n','a','d','i','a','n','_','A','b','o', -'r','i','g','i','n','a','l','_','S','y','l','l','a','b','i','c','s','_','E','x','t','e','n','d','e','d','_','A',0, -2,'V','i','t','h','k','u','q','i',0,'V','i','t','h','k','u','q','i',0, -2,'Z','n','a','m','e','n','n','y','_','M','u','s','i','c',0,'Z','n','a','m','e','n','n','y','_','M','u','s','i','c','a','l', -'_','N','o','t','a','t','i','o','n',0,2,'A','r','a','b','i','c','_','E','x','t','_','C',0, -'A','r','a','b','i','c','_','E','x','t','e','n','d','e','d','_','C',0, -2,'C','J','K','_','E','x','t','_','H',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h', -'s','_','E','x','t','e','n','s','i','o','n','_','H',0,2,'C','y','r','i','l','l','i','c','_','E','x','t','_','D',0, -'C','y','r','i','l','l','i','c','_','E','x','t','e','n','d','e','d','_','D',0, -2,'D','e','v','a','n','a','g','a','r','i','_','E','x','t','_','A',0,'D','e','v','a','n','a','g','a','r','i','_','E','x','t', -'e','n','d','e','d','_','A',0,2,'K','a','k','t','o','v','i','k','_','N','u','m','e','r','a','l','s',0, -'K','a','k','t','o','v','i','k','_','N','u','m','e','r','a','l','s',0, -2,'K','a','w','i',0,'K','a','w','i',0,2,'N','a','g','_','M','u','n','d','a','r','i',0, -'N','a','g','_','M','u','n','d','a','r','i',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, -3,'E','n','c',0,'C','i','r','c','l','e',0,'e','n','c',0, -3,'F','i','n',0,'F','i','n','a','l',0,'f','i','n',0,3,'F','o','n','t',0,'F','o','n','t',0, -'f','o','n','t',0,3,'F','r','a',0,'F','r','a','c','t','i','o','n',0,'f','r','a',0, -3,'I','n','i','t',0,'I','n','i','t','i','a','l',0,'i','n','i','t',0, -3,'I','s','o',0,'I','s','o','l','a','t','e','d',0,'i','s','o',0, -3,'M','e','d',0,'M','e','d','i','a','l',0,'m','e','d',0, -3,'N','a','r',0,'N','a','r','r','o','w',0,'n','a','r',0, -3,'N','b',0,'N','o','b','r','e','a','k',0,'n','b',0,3,'S','m','l',0,'S','m','a','l','l',0, -'s','m','l',0,3,'S','q','r',0,'S','q','u','a','r','e',0,'s','q','r',0, -3,'S','u','b',0,'S','u','b',0,'s','u','b',0,3,'S','u','p',0,'S','u','p','e','r',0, -'s','u','p',0,3,'V','e','r','t',0,'V','e','r','t','i','c','a','l',0,'v','e','r','t',0, -3,'W','i','d','e',0,'W','i','d','e',0,'w','i','d','e',0, -2,'e','a',0,'E','a','s','t','_','A','s','i','a','n','_','W','i','d','t','h',0, -2,'N',0,'N','e','u','t','r','a','l',0,2,'A',0,'A','m','b','i','g','u','o','u','s',0, -2,'H',0,'H','a','l','f','w','i','d','t','h',0,2,'F',0,'F','u','l','l','w','i','d','t','h',0, -2,'N','a',0,'N','a','r','r','o','w',0,2,'W',0,'W','i','d','e',0, -2,'g','c',0,'G','e','n','e','r','a','l','_','C','a','t','e','g','o','r','y',0, -2,'C','n',0,'U','n','a','s','s','i','g','n','e','d',0,2,'L','u',0,'U','p','p','e','r','c','a','s','e','_','L','e','t', -'t','e','r',0,2,'L','l',0,'L','o','w','e','r','c','a','s','e','_','L','e','t','t','e','r',0, -2,'L','t',0,'T','i','t','l','e','c','a','s','e','_','L','e','t','t','e','r',0, -2,'L','m',0,'M','o','d','i','f','i','e','r','_','L','e','t','t','e','r',0, -2,'L','o',0,'O','t','h','e','r','_','L','e','t','t','e','r',0, -2,'M','n',0,'N','o','n','s','p','a','c','i','n','g','_','M','a','r','k',0, -2,'M','e',0,'E','n','c','l','o','s','i','n','g','_','M','a','r','k',0, -2,'M','c',0,'S','p','a','c','i','n','g','_','M','a','r','k',0, -3,'N','d',0,'D','e','c','i','m','a','l','_','N','u','m','b','e','r',0,'d','i','g','i','t',0, -2,'N','l',0,'L','e','t','t','e','r','_','N','u','m','b','e','r',0, -2,'N','o',0,'O','t','h','e','r','_','N','u','m','b','e','r',0, -2,'Z','s',0,'S','p','a','c','e','_','S','e','p','a','r','a','t','o','r',0, -2,'Z','l',0,'L','i','n','e','_','S','e','p','a','r','a','t','o','r',0, -2,'Z','p',0,'P','a','r','a','g','r','a','p','h','_','S','e','p','a','r','a','t','o','r',0, -3,'C','c',0,'C','o','n','t','r','o','l',0,'c','n','t','r','l',0, -2,'C','f',0,'F','o','r','m','a','t',0,2,'C','o',0,'P','r','i','v','a','t','e','_','U','s','e',0, -2,'C','s',0,'S','u','r','r','o','g','a','t','e',0,2,'P','d',0,'D','a','s','h','_','P','u','n','c','t','u','a','t','i', -'o','n',0,2,'P','s',0,'O','p','e','n','_','P','u','n','c','t','u','a','t','i','o','n',0, -2,'P','e',0,'C','l','o','s','e','_','P','u','n','c','t','u','a','t','i','o','n',0, -2,'P','c',0,'C','o','n','n','e','c','t','o','r','_','P','u','n','c','t','u','a','t','i','o','n',0, -2,'P','o',0,'O','t','h','e','r','_','P','u','n','c','t','u','a','t','i','o','n',0, -2,'S','m',0,'M','a','t','h','_','S','y','m','b','o','l',0, -2,'S','c',0,'C','u','r','r','e','n','c','y','_','S','y','m','b','o','l',0, -2,'S','k',0,'M','o','d','i','f','i','e','r','_','S','y','m','b','o','l',0, -2,'S','o',0,'O','t','h','e','r','_','S','y','m','b','o','l',0, -2,'P','i',0,'I','n','i','t','i','a','l','_','P','u','n','c','t','u','a','t','i','o','n',0, -2,'P','f',0,'F','i','n','a','l','_','P','u','n','c','t','u','a','t','i','o','n',0, -2,'j','g',0,'J','o','i','n','i','n','g','_','G','r','o','u','p',0, -2,'N','o','_','J','o','i','n','i','n','g','_','G','r','o','u','p',0,'N','o','_','J','o','i','n','i','n','g','_','G','r','o', -'u','p',0,2,'A','i','n',0,'A','i','n',0,2,'A','l','a','p','h',0,'A','l','a','p','h',0, -2,'A','l','e','f',0,'A','l','e','f',0,2,'B','e','h',0,'B','e','h',0, -2,'B','e','t','h',0,'B','e','t','h',0,2,'D','a','l',0,'D','a','l',0, -2,'D','a','l','a','t','h','_','R','i','s','h',0,'D','a','l','a','t','h','_','R','i','s','h',0, -2,'E',0,'E',0,2,'F','e','h',0,'F','e','h',0,2,'F','i','n','a','l','_','S','e','m','k','a','t','h',0, -'F','i','n','a','l','_','S','e','m','k','a','t','h',0,2,'G','a','f',0,'G','a','f',0, -2,'G','a','m','a','l',0,'G','a','m','a','l',0,2,'H','a','h',0,'H','a','h',0, -2,'T','e','h','_','M','a','r','b','u','t','a','_','G','o','a','l',0,'H','a','m','z','a','_','O','n','_','H','e','h','_','G', -'o','a','l',0,2,'H','e',0,'H','e',0,2,'H','e','h',0,'H','e','h',0, -2,'H','e','h','_','G','o','a','l',0,'H','e','h','_','G','o','a','l',0, -2,'H','e','t','h',0,'H','e','t','h',0,2,'K','a','f',0,'K','a','f',0, -2,'K','a','p','h',0,'K','a','p','h',0,2,'K','n','o','t','t','e','d','_','H','e','h',0, -'K','n','o','t','t','e','d','_','H','e','h',0,2,'L','a','m',0,'L','a','m',0, -2,'L','a','m','a','d','h',0,'L','a','m','a','d','h',0,2,'M','e','e','m',0,'M','e','e','m',0, -2,'M','i','m',0,'M','i','m',0,2,'N','o','o','n',0,'N','o','o','n',0, -2,'N','u','n',0,'N','u','n',0,2,'P','e',0,'P','e',0, -2,'Q','a','f',0,'Q','a','f',0,2,'Q','a','p','h',0,'Q','a','p','h',0, -2,'R','e','h',0,'R','e','h',0,2,'R','e','v','e','r','s','e','d','_','P','e',0,'R','e','v','e','r','s','e','d','_','P', -'e',0,2,'S','a','d',0,'S','a','d',0,2,'S','a','d','h','e',0,'S','a','d','h','e',0, -2,'S','e','e','n',0,'S','e','e','n',0,2,'S','e','m','k','a','t','h',0,'S','e','m','k','a','t','h',0, -2,'S','h','i','n',0,'S','h','i','n',0,2,'S','w','a','s','h','_','K','a','f',0,'S','w','a','s','h','_','K','a','f',0, -2,'S','y','r','i','a','c','_','W','a','w',0,'S','y','r','i','a','c','_','W','a','w',0, -2,'T','a','h',0,'T','a','h',0,2,'T','a','w',0,'T','a','w',0, -2,'T','e','h','_','M','a','r','b','u','t','a',0,'T','e','h','_','M','a','r','b','u','t','a',0, -2,'T','e','t','h',0,'T','e','t','h',0,2,'W','a','w',0,'W','a','w',0, -2,'Y','e','h',0,'Y','e','h',0,2,'Y','e','h','_','B','a','r','r','e','e',0,'Y','e','h','_','B','a','r','r','e','e',0, -2,'Y','e','h','_','W','i','t','h','_','T','a','i','l',0,'Y','e','h','_','W','i','t','h','_','T','a','i','l',0, -2,'Y','u','d','h',0,'Y','u','d','h',0,2,'Y','u','d','h','_','H','e',0,'Y','u','d','h','_','H','e',0, -2,'Z','a','i','n',0,'Z','a','i','n',0,2,'F','e',0,'F','e',0, -2,'K','h','a','p','h',0,'K','h','a','p','h',0,2,'Z','h','a','i','n',0,'Z','h','a','i','n',0, -2,'B','u','r','u','s','h','a','s','k','i','_','Y','e','h','_','B','a','r','r','e','e',0,'B','u','r','u','s','h','a','s','k', -'i','_','Y','e','h','_','B','a','r','r','e','e',0,2,'F','a','r','s','i','_','Y','e','h',0, -'F','a','r','s','i','_','Y','e','h',0,2,'N','y','a',0,'N','y','a',0, -2,'R','o','h','i','n','g','y','a','_','Y','e','h',0,'R','o','h','i','n','g','y','a','_','Y','e','h',0, -2,'M','a','n','i','c','h','a','e','a','n','_','A','l','e','p','h',0,'M','a','n','i','c','h','a','e','a','n','_','A','l','e', -'p','h',0,2,'M','a','n','i','c','h','a','e','a','n','_','A','y','i','n',0,'M','a','n','i','c','h','a','e','a','n','_','A', -'y','i','n',0,2,'M','a','n','i','c','h','a','e','a','n','_','B','e','t','h',0,'M','a','n','i','c','h','a','e','a','n','_', -'B','e','t','h',0,2,'M','a','n','i','c','h','a','e','a','n','_','D','a','l','e','t','h',0, -'M','a','n','i','c','h','a','e','a','n','_','D','a','l','e','t','h',0, -2,'M','a','n','i','c','h','a','e','a','n','_','D','h','a','m','e','d','h',0,'M','a','n','i','c','h','a','e','a','n','_','D', -'h','a','m','e','d','h',0,2,'M','a','n','i','c','h','a','e','a','n','_','F','i','v','e',0, -'M','a','n','i','c','h','a','e','a','n','_','F','i','v','e',0, -2,'M','a','n','i','c','h','a','e','a','n','_','G','i','m','e','l',0,'M','a','n','i','c','h','a','e','a','n','_','G','i','m', -'e','l',0,2,'M','a','n','i','c','h','a','e','a','n','_','H','e','t','h',0,'M','a','n','i','c','h','a','e','a','n','_','H', -'e','t','h',0,2,'M','a','n','i','c','h','a','e','a','n','_','H','u','n','d','r','e','d',0, -'M','a','n','i','c','h','a','e','a','n','_','H','u','n','d','r','e','d',0, -2,'M','a','n','i','c','h','a','e','a','n','_','K','a','p','h',0,'M','a','n','i','c','h','a','e','a','n','_','K','a','p','h', -0,2,'M','a','n','i','c','h','a','e','a','n','_','L','a','m','e','d','h',0,'M','a','n','i','c','h','a','e','a','n','_','L', -'a','m','e','d','h',0,2,'M','a','n','i','c','h','a','e','a','n','_','M','e','m',0,'M','a','n','i','c','h','a','e','a','n', -'_','M','e','m',0,2,'M','a','n','i','c','h','a','e','a','n','_','N','u','n',0,'M','a','n','i','c','h','a','e','a','n','_', -'N','u','n',0,2,'M','a','n','i','c','h','a','e','a','n','_','O','n','e',0,'M','a','n','i','c','h','a','e','a','n','_','O', -'n','e',0,2,'M','a','n','i','c','h','a','e','a','n','_','P','e',0,'M','a','n','i','c','h','a','e','a','n','_','P','e',0, -2,'M','a','n','i','c','h','a','e','a','n','_','Q','o','p','h',0,'M','a','n','i','c','h','a','e','a','n','_','Q','o','p','h', -0,2,'M','a','n','i','c','h','a','e','a','n','_','R','e','s','h',0,'M','a','n','i','c','h','a','e','a','n','_','R','e','s', -'h',0,2,'M','a','n','i','c','h','a','e','a','n','_','S','a','d','h','e',0,'M','a','n','i','c','h','a','e','a','n','_','S', -'a','d','h','e',0,2,'M','a','n','i','c','h','a','e','a','n','_','S','a','m','e','k','h',0, -'M','a','n','i','c','h','a','e','a','n','_','S','a','m','e','k','h',0, -2,'M','a','n','i','c','h','a','e','a','n','_','T','a','w',0,'M','a','n','i','c','h','a','e','a','n','_','T','a','w',0, -2,'M','a','n','i','c','h','a','e','a','n','_','T','e','n',0,'M','a','n','i','c','h','a','e','a','n','_','T','e','n',0, -2,'M','a','n','i','c','h','a','e','a','n','_','T','e','t','h',0,'M','a','n','i','c','h','a','e','a','n','_','T','e','t','h', -0,2,'M','a','n','i','c','h','a','e','a','n','_','T','h','a','m','e','d','h',0,'M','a','n','i','c','h','a','e','a','n','_', -'T','h','a','m','e','d','h',0,2,'M','a','n','i','c','h','a','e','a','n','_','T','w','e','n','t','y',0, -'M','a','n','i','c','h','a','e','a','n','_','T','w','e','n','t','y',0, -2,'M','a','n','i','c','h','a','e','a','n','_','W','a','w',0,'M','a','n','i','c','h','a','e','a','n','_','W','a','w',0, -2,'M','a','n','i','c','h','a','e','a','n','_','Y','o','d','h',0,'M','a','n','i','c','h','a','e','a','n','_','Y','o','d','h', -0,2,'M','a','n','i','c','h','a','e','a','n','_','Z','a','y','i','n',0,'M','a','n','i','c','h','a','e','a','n','_','Z','a', -'y','i','n',0,2,'S','t','r','a','i','g','h','t','_','W','a','w',0,'S','t','r','a','i','g','h','t','_','W','a','w',0, -2,'A','f','r','i','c','a','n','_','F','e','h',0,'A','f','r','i','c','a','n','_','F','e','h',0, -2,'A','f','r','i','c','a','n','_','N','o','o','n',0,'A','f','r','i','c','a','n','_','N','o','o','n',0, -2,'A','f','r','i','c','a','n','_','Q','a','f',0,'A','f','r','i','c','a','n','_','Q','a','f',0, -2,'M','a','l','a','y','a','l','a','m','_','B','h','a',0,'M','a','l','a','y','a','l','a','m','_','B','h','a',0, -2,'M','a','l','a','y','a','l','a','m','_','J','a',0,'M','a','l','a','y','a','l','a','m','_','J','a',0, -2,'M','a','l','a','y','a','l','a','m','_','L','l','a',0,'M','a','l','a','y','a','l','a','m','_','L','l','a',0, -2,'M','a','l','a','y','a','l','a','m','_','L','l','l','a',0,'M','a','l','a','y','a','l','a','m','_','L','l','l','a',0, -2,'M','a','l','a','y','a','l','a','m','_','N','g','a',0,'M','a','l','a','y','a','l','a','m','_','N','g','a',0, -2,'M','a','l','a','y','a','l','a','m','_','N','n','a',0,'M','a','l','a','y','a','l','a','m','_','N','n','a',0, -2,'M','a','l','a','y','a','l','a','m','_','N','n','n','a',0,'M','a','l','a','y','a','l','a','m','_','N','n','n','a',0, -2,'M','a','l','a','y','a','l','a','m','_','N','y','a',0,'M','a','l','a','y','a','l','a','m','_','N','y','a',0, -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,'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,'T','h','i','n','_','Y','e','h',0,'T','h','i','n','_','Y','e','h',0, -2,'V','e','r','t','i','c','a','l','_','T','a','i','l',0,'V','e','r','t','i','c','a','l','_','T','a','i','l',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, -2,'R',0,'R','i','g','h','t','_','J','o','i','n','i','n','g',0, -2,'T',0,'T','r','a','n','s','p','a','r','e','n','t',0,2,'l','b',0,'L','i','n','e','_','B','r','e','a','k',0, -2,'X','X',0,'U','n','k','n','o','w','n',0,2,'A','I',0,'A','m','b','i','g','u','o','u','s',0, -2,'A','L',0,'A','l','p','h','a','b','e','t','i','c',0,2,'B','2',0,'B','r','e','a','k','_','B','o','t','h',0, -2,'B','A',0,'B','r','e','a','k','_','A','f','t','e','r',0, -2,'B','B',0,'B','r','e','a','k','_','B','e','f','o','r','e',0, -2,'B','K',0,'M','a','n','d','a','t','o','r','y','_','B','r','e','a','k',0, -2,'C','B',0,'C','o','n','t','i','n','g','e','n','t','_','B','r','e','a','k',0, -2,'C','L',0,'C','l','o','s','e','_','P','u','n','c','t','u','a','t','i','o','n',0, -2,'C','M',0,'C','o','m','b','i','n','i','n','g','_','M','a','r','k',0, -2,'C','R',0,'C','a','r','r','i','a','g','e','_','R','e','t','u','r','n',0, -2,'E','X',0,'E','x','c','l','a','m','a','t','i','o','n',0, -2,'G','L',0,'G','l','u','e',0,2,'H','Y',0,'H','y','p','h','e','n',0, -2,'I','D',0,'I','d','e','o','g','r','a','p','h','i','c',0, -3,'I','N',0,'I','n','s','e','p','a','r','a','b','l','e',0,'I','n','s','e','p','e','r','a','b','l','e',0, -2,'I','S',0,'I','n','f','i','x','_','N','u','m','e','r','i','c',0, -2,'L','F',0,'L','i','n','e','_','F','e','e','d',0,2,'N','S',0,'N','o','n','s','t','a','r','t','e','r',0, -2,'N','U',0,'N','u','m','e','r','i','c',0,2,'O','P',0,'O','p','e','n','_','P','u','n','c','t','u','a','t','i','o','n', -0,2,'P','O',0,'P','o','s','t','f','i','x','_','N','u','m','e','r','i','c',0, -2,'P','R',0,'P','r','e','f','i','x','_','N','u','m','e','r','i','c',0, -2,'Q','U',0,'Q','u','o','t','a','t','i','o','n',0,2,'S','A',0,'C','o','m','p','l','e','x','_','C','o','n','t','e','x', -'t',0,2,'S','G',0,'S','u','r','r','o','g','a','t','e',0, -2,'S','P',0,'S','p','a','c','e',0,2,'S','Y',0,'B','r','e','a','k','_','S','y','m','b','o','l','s',0, -2,'Z','W',0,'Z','W','S','p','a','c','e',0,2,'N','L',0,'N','e','x','t','_','L','i','n','e',0, -2,'W','J',0,'W','o','r','d','_','J','o','i','n','e','r',0, -2,'H','2',0,'H','2',0,2,'H','3',0,'H','3',0,2,'J','L',0,'J','L',0, -2,'J','T',0,'J','T',0,2,'J','V',0,'J','V',0,2,'C','P',0,'C','l','o','s','e','_','P','a','r','e','n','t','h','e', -'s','i','s',0,2,'C','J',0,'C','o','n','d','i','t','i','o','n','a','l','_','J','a','p','a','n','e','s','e','_','S','t','a', -'r','t','e','r',0,2,'H','L',0,'H','e','b','r','e','w','_','L','e','t','t','e','r',0, -2,'E','B',0,'E','_','B','a','s','e',0,2,'E','M',0,'E','_','M','o','d','i','f','i','e','r',0, -2,'Z','W','J',0,'Z','W','J',0,2,'n','t',0,'N','u','m','e','r','i','c','_','T','y','p','e',0, -2,'N','o','n','e',0,'N','o','n','e',0,2,'D','e',0,'D','e','c','i','m','a','l',0, -2,'D','i',0,'D','i','g','i','t',0,2,'N','u',0,'N','u','m','e','r','i','c',0, -2,'s','c',0,'S','c','r','i','p','t',0,2,'Z','y','y','y',0,'C','o','m','m','o','n',0, -3,'Z','i','n','h',0,'I','n','h','e','r','i','t','e','d',0,'Q','a','a','i',0, -2,'A','r','a','b',0,'A','r','a','b','i','c',0,2,'A','r','m','n',0,'A','r','m','e','n','i','a','n',0, -2,'B','e','n','g',0,'B','e','n','g','a','l','i',0,2,'B','o','p','o',0,'B','o','p','o','m','o','f','o',0, -2,'C','h','e','r',0,'C','h','e','r','o','k','e','e',0,3,'C','o','p','t',0,'C','o','p','t','i','c',0, -'Q','a','a','c',0,2,'C','y','r','l',0,'C','y','r','i','l','l','i','c',0, -2,'D','s','r','t',0,'D','e','s','e','r','e','t',0,2,'D','e','v','a',0,'D','e','v','a','n','a','g','a','r','i',0, -2,'E','t','h','i',0,'E','t','h','i','o','p','i','c',0,2,'G','e','o','r',0,'G','e','o','r','g','i','a','n',0, -2,'G','o','t','h',0,'G','o','t','h','i','c',0,2,'G','r','e','k',0,'G','r','e','e','k',0, -2,'G','u','j','r',0,'G','u','j','a','r','a','t','i',0,2,'G','u','r','u',0,'G','u','r','m','u','k','h','i',0, -2,'H','a','n','i',0,'H','a','n',0,2,'H','a','n','g',0,'H','a','n','g','u','l',0, -2,'H','e','b','r',0,'H','e','b','r','e','w',0,2,'H','i','r','a',0,'H','i','r','a','g','a','n','a',0, -2,'K','n','d','a',0,'K','a','n','n','a','d','a',0,2,'K','a','n','a',0,'K','a','t','a','k','a','n','a',0, -2,'K','h','m','r',0,'K','h','m','e','r',0,2,'L','a','o','o',0,'L','a','o',0, -2,'L','a','t','n',0,'L','a','t','i','n',0,2,'M','l','y','m',0,'M','a','l','a','y','a','l','a','m',0, -2,'M','o','n','g',0,'M','o','n','g','o','l','i','a','n',0, -2,'M','y','m','r',0,'M','y','a','n','m','a','r',0,2,'O','g','a','m',0,'O','g','h','a','m',0, -2,'I','t','a','l',0,'O','l','d','_','I','t','a','l','i','c',0, -2,'O','r','y','a',0,'O','r','i','y','a',0,2,'R','u','n','r',0,'R','u','n','i','c',0, -2,'S','i','n','h',0,'S','i','n','h','a','l','a',0,2,'S','y','r','c',0,'S','y','r','i','a','c',0, -2,'T','a','m','l',0,'T','a','m','i','l',0,2,'T','e','l','u',0,'T','e','l','u','g','u',0, -2,'T','h','a','a',0,'T','h','a','a','n','a',0,2,'T','i','b','t',0,'T','i','b','e','t','a','n',0, -2,'C','a','n','s',0,'C','a','n','a','d','i','a','n','_','A','b','o','r','i','g','i','n','a','l',0, -2,'Y','i','i','i',0,'Y','i',0,2,'T','g','l','g',0,'T','a','g','a','l','o','g',0, -2,'H','a','n','o',0,'H','a','n','u','n','o','o',0,2,'B','u','h','d',0,'B','u','h','i','d',0, -2,'T','a','g','b',0,'T','a','g','b','a','n','w','a',0,2,'B','r','a','i',0,'B','r','a','i','l','l','e',0, -2,'C','p','r','t',0,'C','y','p','r','i','o','t',0,2,'L','i','m','b',0,'L','i','m','b','u',0, -2,'L','i','n','b',0,'L','i','n','e','a','r','_','B',0,2,'O','s','m','a',0,'O','s','m','a','n','y','a',0, -2,'S','h','a','w',0,'S','h','a','v','i','a','n',0,2,'T','a','l','e',0,'T','a','i','_','L','e',0, -2,'U','g','a','r',0,'U','g','a','r','i','t','i','c',0,2,'H','r','k','t',0,'K','a','t','a','k','a','n','a','_','O','r', -'_','H','i','r','a','g','a','n','a',0,2,'B','u','g','i',0,'B','u','g','i','n','e','s','e',0, -2,'G','l','a','g',0,'G','l','a','g','o','l','i','t','i','c',0, -2,'K','h','a','r',0,'K','h','a','r','o','s','h','t','h','i',0, -2,'S','y','l','o',0,'S','y','l','o','t','i','_','N','a','g','r','i',0, -2,'T','a','l','u',0,'N','e','w','_','T','a','i','_','L','u','e',0, -2,'T','f','n','g',0,'T','i','f','i','n','a','g','h',0,2,'X','p','e','o',0,'O','l','d','_','P','e','r','s','i','a','n', -0,2,'B','a','l','i',0,'B','a','l','i','n','e','s','e',0, -2,'B','a','t','k',0,'B','a','t','a','k',0,2,'B','l','i','s',0,'B','l','i','s',0, -2,'B','r','a','h',0,'B','r','a','h','m','i',0,2,'C','i','r','t',0,'C','i','r','t',0, -2,'C','y','r','s',0,'C','y','r','s',0,2,'E','g','y','d',0,'E','g','y','d',0, -2,'E','g','y','h',0,'E','g','y','h',0,2,'E','g','y','p',0,'E','g','y','p','t','i','a','n','_','H','i','e','r','o','g', -'l','y','p','h','s',0,2,'G','e','o','k',0,'G','e','o','k',0, -2,'H','a','n','s',0,'H','a','n','s',0,2,'H','a','n','t',0,'H','a','n','t',0, -2,'H','m','n','g',0,'P','a','h','a','w','h','_','H','m','o','n','g',0, -2,'H','u','n','g',0,'O','l','d','_','H','u','n','g','a','r','i','a','n',0, -2,'I','n','d','s',0,'I','n','d','s',0,2,'J','a','v','a',0,'J','a','v','a','n','e','s','e',0, -2,'K','a','l','i',0,'K','a','y','a','h','_','L','i',0,2,'L','a','t','f',0,'L','a','t','f',0, -2,'L','a','t','g',0,'L','a','t','g',0,2,'L','e','p','c',0,'L','e','p','c','h','a',0, -2,'L','i','n','a',0,'L','i','n','e','a','r','_','A',0,2,'M','a','n','d',0,'M','a','n','d','a','i','c',0, -2,'M','a','y','a',0,'M','a','y','a',0,2,'M','e','r','o',0,'M','e','r','o','i','t','i','c','_','H','i','e','r','o','g', -'l','y','p','h','s',0,2,'N','k','o','o',0,'N','k','o',0, -2,'O','r','k','h',0,'O','l','d','_','T','u','r','k','i','c',0, -2,'P','e','r','m',0,'O','l','d','_','P','e','r','m','i','c',0, -2,'P','h','a','g',0,'P','h','a','g','s','_','P','a',0,2,'P','h','n','x',0,'P','h','o','e','n','i','c','i','a','n',0, -2,'P','l','r','d',0,'M','i','a','o',0,2,'R','o','r','o',0,'R','o','r','o',0, -2,'S','a','r','a',0,'S','a','r','a',0,2,'S','y','r','e',0,'S','y','r','e',0, -2,'S','y','r','j',0,'S','y','r','j',0,2,'S','y','r','n',0,'S','y','r','n',0, -2,'T','e','n','g',0,'T','e','n','g',0,2,'V','a','i','i',0,'V','a','i',0, -2,'V','i','s','p',0,'V','i','s','p',0,2,'X','s','u','x',0,'C','u','n','e','i','f','o','r','m',0, -2,'Z','x','x','x',0,'Z','x','x','x',0,2,'Z','z','z','z',0,'U','n','k','n','o','w','n',0, -2,'C','a','r','i',0,'C','a','r','i','a','n',0,2,'J','p','a','n',0,'J','p','a','n',0, -2,'L','a','n','a',0,'T','a','i','_','T','h','a','m',0,2,'L','y','c','i',0,'L','y','c','i','a','n',0, -2,'L','y','d','i',0,'L','y','d','i','a','n',0,2,'O','l','c','k',0,'O','l','_','C','h','i','k','i',0, -2,'R','j','n','g',0,'R','e','j','a','n','g',0,2,'S','a','u','r',0,'S','a','u','r','a','s','h','t','r','a',0, -2,'S','g','n','w',0,'S','i','g','n','W','r','i','t','i','n','g',0, -2,'S','u','n','d',0,'S','u','n','d','a','n','e','s','e',0, -2,'M','o','o','n',0,'M','o','o','n',0,2,'M','t','e','i',0,'M','e','e','t','e','i','_','M','a','y','e','k',0, -2,'A','r','m','i',0,'I','m','p','e','r','i','a','l','_','A','r','a','m','a','i','c',0, -2,'A','v','s','t',0,'A','v','e','s','t','a','n',0,2,'C','a','k','m',0,'C','h','a','k','m','a',0, -2,'K','o','r','e',0,'K','o','r','e',0,2,'K','t','h','i',0,'K','a','i','t','h','i',0, -2,'M','a','n','i',0,'M','a','n','i','c','h','a','e','a','n',0, -2,'P','h','l','i',0,'I','n','s','c','r','i','p','t','i','o','n','a','l','_','P','a','h','l','a','v','i',0, -2,'P','h','l','p',0,'P','s','a','l','t','e','r','_','P','a','h','l','a','v','i',0, -2,'P','h','l','v',0,'P','h','l','v',0,2,'P','r','t','i',0,'I','n','s','c','r','i','p','t','i','o','n','a','l','_','P', -'a','r','t','h','i','a','n',0,2,'S','a','m','r',0,'S','a','m','a','r','i','t','a','n',0, -2,'T','a','v','t',0,'T','a','i','_','V','i','e','t',0,2,'Z','m','t','h',0,'Z','m','t','h',0, -2,'Z','s','y','m',0,'Z','s','y','m',0,2,'B','a','m','u',0,'B','a','m','u','m',0, -2,'N','k','g','b',0,'N','k','g','b',0,2,'S','a','r','b',0,'O','l','d','_','S','o','u','t','h','_','A','r','a','b','i', -'a','n',0,2,'B','a','s','s',0,'B','a','s','s','a','_','V','a','h',0, -2,'D','u','p','l',0,'D','u','p','l','o','y','a','n',0,2,'E','l','b','a',0,'E','l','b','a','s','a','n',0, -2,'G','r','a','n',0,'G','r','a','n','t','h','a',0,2,'K','p','e','l',0,'K','p','e','l',0, -2,'L','o','m','a',0,'L','o','m','a',0,2,'M','e','n','d',0,'M','e','n','d','e','_','K','i','k','a','k','u','i',0, -2,'M','e','r','c',0,'M','e','r','o','i','t','i','c','_','C','u','r','s','i','v','e',0, -2,'N','a','r','b',0,'O','l','d','_','N','o','r','t','h','_','A','r','a','b','i','a','n',0, -2,'N','b','a','t',0,'N','a','b','a','t','a','e','a','n',0, -2,'P','a','l','m',0,'P','a','l','m','y','r','e','n','e',0, -2,'S','i','n','d',0,'K','h','u','d','a','w','a','d','i',0, -2,'W','a','r','a',0,'W','a','r','a','n','g','_','C','i','t','i',0, -2,'A','f','a','k',0,'A','f','a','k',0,2,'J','u','r','c',0,'J','u','r','c',0, -2,'M','r','o','o',0,'M','r','o',0,2,'N','s','h','u',0,'N','u','s','h','u',0, -2,'S','h','r','d',0,'S','h','a','r','a','d','a',0,2,'S','o','r','a',0,'S','o','r','a','_','S','o','m','p','e','n','g', -0,2,'T','a','k','r',0,'T','a','k','r','i',0,2,'T','a','n','g',0,'T','a','n','g','u','t',0, -2,'W','o','l','e',0,'W','o','l','e',0,2,'H','l','u','w',0,'A','n','a','t','o','l','i','a','n','_','H','i','e','r','o', -'g','l','y','p','h','s',0,2,'K','h','o','j',0,'K','h','o','j','k','i',0, -2,'T','i','r','h',0,'T','i','r','h','u','t','a',0,2,'A','g','h','b',0,'C','a','u','c','a','s','i','a','n','_','A','l', -'b','a','n','i','a','n',0,2,'M','a','h','j',0,'M','a','h','a','j','a','n','i',0, -2,'H','a','t','r',0,'H','a','t','r','a','n',0,2,'M','u','l','t',0,'M','u','l','t','a','n','i',0, -2,'P','a','u','c',0,'P','a','u','_','C','i','n','_','H','a','u',0, -2,'S','i','d','d',0,'S','i','d','d','h','a','m',0,2,'A','d','l','m',0,'A','d','l','a','m',0, -2,'B','h','k','s',0,'B','h','a','i','k','s','u','k','i',0, -2,'M','a','r','c',0,'M','a','r','c','h','e','n',0,2,'O','s','g','e',0,'O','s','a','g','e',0, -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,'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,'E','l','y','m',0,'E','l','y','m','a','i','c',0,2,'H','m','n','p',0,'N','y','i','a','k','e','n','g','_','P','u','a', -'c','h','u','e','_','H','m','o','n','g',0,2,'N','a','n','d',0,'N','a','n','d','i','n','a','g','a','r','i',0, -2,'W','c','h','o',0,'W','a','n','c','h','o',0,2,'C','h','r','s',0,'C','h','o','r','a','s','m','i','a','n',0, -2,'D','i','a','k',0,'D','i','v','e','s','_','A','k','u','r','u',0, -2,'K','i','t','s',0,'K','h','i','t','a','n','_','S','m','a','l','l','_','S','c','r','i','p','t',0, -2,'Y','e','z','i',0,'Y','e','z','i','d','i',0,2,'C','p','m','n',0,'C','y','p','r','o','_','M','i','n','o','a','n',0, -2,'O','u','g','r',0,'O','l','d','_','U','y','g','h','u','r',0, -2,'T','n','s','a',0,'T','a','n','g','s','a',0,2,'V','i','t','h',0,'V','i','t','h','k','u','q','i',0, -2,'N','a','g','m',0,'N','a','g','_','M','u','n','d','a','r','i',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, -2,'L','V',0,'L','V','_','S','y','l','l','a','b','l','e',0, -2,'L','V','T',0,'L','V','T','_','S','y','l','l','a','b','l','e',0, -2,'N','F','D','_','Q','C',0,'N','F','D','_','Q','u','i','c','k','_','C','h','e','c','k',0, -2,'N',0,'N','o',0,2,'Y',0,'Y','e','s',0,2,'N','F','K','D','_','Q','C',0,'N','F','K','D','_','Q','u','i','c','k', -'_','C','h','e','c','k',0,2,'N','F','C','_','Q','C',0,'N','F','C','_','Q','u','i','c','k','_','C','h','e','c','k',0, -2,'M',0,'M','a','y','b','e',0,2,'N','F','K','C','_','Q','C',0,'N','F','K','C','_','Q','u','i','c','k','_','C','h','e', -'c','k',0,2,'l','c','c','c',0,'L','e','a','d','_','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,'t','c','c','c',0,'T','r','a','i','l','_','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,'G','C','B',0,'G','r','a','p','h','e','m','e','_','C','l','u','s', -'t','e','r','_','B','r','e','a','k',0,2,'X','X',0,'O','t','h','e','r',0, -2,'C','N',0,'C','o','n','t','r','o','l',0,2,'C','R',0,'C','R',0, -2,'E','X',0,'E','x','t','e','n','d',0,2,'L',0,'L',0, -2,'L','F',0,'L','F',0,2,'L','V',0,'L','V',0,2,'L','V','T',0,'L','V','T',0, -2,'T',0,'T',0,2,'V',0,'V',0,2,'S','M',0,'S','p','a','c','i','n','g','M','a','r','k',0, -2,'P','P',0,'P','r','e','p','e','n','d',0,2,'E','B','G',0,'E','_','B','a','s','e','_','G','A','Z',0, -2,'G','A','Z',0,'G','l','u','e','_','A','f','t','e','r','_','Z','w','j',0, -2,'S','B',0,'S','e','n','t','e','n','c','e','_','B','r','e','a','k',0, -2,'A','T',0,'A','T','e','r','m',0,2,'C','L',0,'C','l','o','s','e',0, -2,'F','O',0,'F','o','r','m','a','t',0,2,'L','O',0,'L','o','w','e','r',0, -2,'L','E',0,'O','L','e','t','t','e','r',0,2,'S','E',0,'S','e','p',0, -2,'S','P',0,'S','p',0,2,'S','T',0,'S','T','e','r','m',0, -2,'U','P',0,'U','p','p','e','r',0,2,'S','C',0,'S','C','o','n','t','i','n','u','e',0, -2,'W','B',0,'W','o','r','d','_','B','r','e','a','k',0,2,'L','E',0,'A','L','e','t','t','e','r',0, -2,'K','A',0,'K','a','t','a','k','a','n','a',0,2,'M','L',0,'M','i','d','L','e','t','t','e','r',0, -2,'M','N',0,'M','i','d','N','u','m',0,2,'E','X',0,'E','x','t','e','n','d','N','u','m','L','e','t',0, -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,'I','n','P','C',0,'I','n','d','i','c','_','P','o','s','i','t','i','o','n','a','l','_', -'C','a','t','e','g','o','r','y',0,2,'N','A',0,'N','A',0, -2,'B','o','t','t','o','m',0,'B','o','t','t','o','m',0,2,'B','o','t','t','o','m','_','A','n','d','_','L','e','f','t',0, -'B','o','t','t','o','m','_','A','n','d','_','L','e','f','t',0, -2,'B','o','t','t','o','m','_','A','n','d','_','R','i','g','h','t',0,'B','o','t','t','o','m','_','A','n','d','_','R','i','g', -'h','t',0,2,'L','e','f','t',0,'L','e','f','t',0,2,'L','e','f','t','_','A','n','d','_','R','i','g','h','t',0, -'L','e','f','t','_','A','n','d','_','R','i','g','h','t',0,2,'O','v','e','r','s','t','r','u','c','k',0, -'O','v','e','r','s','t','r','u','c','k',0,2,'R','i','g','h','t',0,'R','i','g','h','t',0, -2,'T','o','p',0,'T','o','p',0,2,'T','o','p','_','A','n','d','_','B','o','t','t','o','m',0, -'T','o','p','_','A','n','d','_','B','o','t','t','o','m',0,2,'T','o','p','_','A','n','d','_','B','o','t','t','o','m','_','A', -'n','d','_','R','i','g','h','t',0,'T','o','p','_','A','n','d','_','B','o','t','t','o','m','_','A','n','d','_','R','i','g','h', -'t',0,2,'T','o','p','_','A','n','d','_','L','e','f','t',0,'T','o','p','_','A','n','d','_','L','e','f','t',0, -2,'T','o','p','_','A','n','d','_','L','e','f','t','_','A','n','d','_','R','i','g','h','t',0, -'T','o','p','_','A','n','d','_','L','e','f','t','_','A','n','d','_','R','i','g','h','t',0, -2,'T','o','p','_','A','n','d','_','R','i','g','h','t',0,'T','o','p','_','A','n','d','_','R','i','g','h','t',0, -2,'V','i','s','u','a','l','_','O','r','d','e','r','_','L','e','f','t',0,'V','i','s','u','a','l','_','O','r','d','e','r','_', -'L','e','f','t',0,2,'T','o','p','_','A','n','d','_','B','o','t','t','o','m','_','A','n','d','_','L','e','f','t',0, -'T','o','p','_','A','n','d','_','B','o','t','t','o','m','_','A','n','d','_','L','e','f','t',0, -2,'I','n','S','C',0,'I','n','d','i','c','_','S','y','l','l','a','b','i','c','_','C','a','t','e','g','o','r','y',0, -2,'O','t','h','e','r',0,'O','t','h','e','r',0,2,'A','v','a','g','r','a','h','a',0,'A','v','a','g','r','a','h','a',0, -2,'B','i','n','d','u',0,'B','i','n','d','u',0,2,'B','r','a','h','m','i','_','J','o','i','n','i','n','g','_','N','u','m', -'b','e','r',0,'B','r','a','h','m','i','_','J','o','i','n','i','n','g','_','N','u','m','b','e','r',0, -2,'C','a','n','t','i','l','l','a','t','i','o','n','_','M','a','r','k',0,'C','a','n','t','i','l','l','a','t','i','o','n','_', -'M','a','r','k',0,2,'C','o','n','s','o','n','a','n','t',0,'C','o','n','s','o','n','a','n','t',0, -2,'C','o','n','s','o','n','a','n','t','_','D','e','a','d',0,'C','o','n','s','o','n','a','n','t','_','D','e','a','d',0, -2,'C','o','n','s','o','n','a','n','t','_','F','i','n','a','l',0,'C','o','n','s','o','n','a','n','t','_','F','i','n','a','l', -0,2,'C','o','n','s','o','n','a','n','t','_','H','e','a','d','_','L','e','t','t','e','r',0, -'C','o','n','s','o','n','a','n','t','_','H','e','a','d','_','L','e','t','t','e','r',0, -2,'C','o','n','s','o','n','a','n','t','_','I','n','i','t','i','a','l','_','P','o','s','t','f','i','x','e','d',0, -'C','o','n','s','o','n','a','n','t','_','I','n','i','t','i','a','l','_','P','o','s','t','f','i','x','e','d',0, -2,'C','o','n','s','o','n','a','n','t','_','K','i','l','l','e','r',0,'C','o','n','s','o','n','a','n','t','_','K','i','l','l', -'e','r',0,2,'C','o','n','s','o','n','a','n','t','_','M','e','d','i','a','l',0,'C','o','n','s','o','n','a','n','t','_','M', -'e','d','i','a','l',0,2,'C','o','n','s','o','n','a','n','t','_','P','l','a','c','e','h','o','l','d','e','r',0, -'C','o','n','s','o','n','a','n','t','_','P','l','a','c','e','h','o','l','d','e','r',0, -2,'C','o','n','s','o','n','a','n','t','_','P','r','e','c','e','d','i','n','g','_','R','e','p','h','a',0, -'C','o','n','s','o','n','a','n','t','_','P','r','e','c','e','d','i','n','g','_','R','e','p','h','a',0, -2,'C','o','n','s','o','n','a','n','t','_','P','r','e','f','i','x','e','d',0,'C','o','n','s','o','n','a','n','t','_','P','r', -'e','f','i','x','e','d',0,2,'C','o','n','s','o','n','a','n','t','_','S','u','b','j','o','i','n','e','d',0, -'C','o','n','s','o','n','a','n','t','_','S','u','b','j','o','i','n','e','d',0, -2,'C','o','n','s','o','n','a','n','t','_','S','u','c','c','e','e','d','i','n','g','_','R','e','p','h','a',0, -'C','o','n','s','o','n','a','n','t','_','S','u','c','c','e','e','d','i','n','g','_','R','e','p','h','a',0, -2,'C','o','n','s','o','n','a','n','t','_','W','i','t','h','_','S','t','a','c','k','e','r',0, -'C','o','n','s','o','n','a','n','t','_','W','i','t','h','_','S','t','a','c','k','e','r',0, -2,'G','e','m','i','n','a','t','i','o','n','_','M','a','r','k',0,'G','e','m','i','n','a','t','i','o','n','_','M','a','r','k', -0,2,'I','n','v','i','s','i','b','l','e','_','S','t','a','c','k','e','r',0,'I','n','v','i','s','i','b','l','e','_','S','t', -'a','c','k','e','r',0,2,'J','o','i','n','e','r',0,'J','o','i','n','e','r',0, -2,'M','o','d','i','f','y','i','n','g','_','L','e','t','t','e','r',0,'M','o','d','i','f','y','i','n','g','_','L','e','t','t', -'e','r',0,2,'N','o','n','_','J','o','i','n','e','r',0,'N','o','n','_','J','o','i','n','e','r',0, -2,'N','u','k','t','a',0,'N','u','k','t','a',0,2,'N','u','m','b','e','r',0,'N','u','m','b','e','r',0, -2,'N','u','m','b','e','r','_','J','o','i','n','e','r',0,'N','u','m','b','e','r','_','J','o','i','n','e','r',0, -2,'P','u','r','e','_','K','i','l','l','e','r',0,'P','u','r','e','_','K','i','l','l','e','r',0, -2,'R','e','g','i','s','t','e','r','_','S','h','i','f','t','e','r',0,'R','e','g','i','s','t','e','r','_','S','h','i','f','t', -'e','r',0,2,'S','y','l','l','a','b','l','e','_','M','o','d','i','f','i','e','r',0,'S','y','l','l','a','b','l','e','_','M', -'o','d','i','f','i','e','r',0,2,'T','o','n','e','_','L','e','t','t','e','r',0,'T','o','n','e','_','L','e','t','t','e','r', -0,2,'T','o','n','e','_','M','a','r','k',0,'T','o','n','e','_','M','a','r','k',0, -2,'V','i','r','a','m','a',0,'V','i','r','a','m','a',0,2,'V','i','s','a','r','g','a',0, -'V','i','s','a','r','g','a',0,2,'V','o','w','e','l',0,'V','o','w','e','l',0, -2,'V','o','w','e','l','_','D','e','p','e','n','d','e','n','t',0,'V','o','w','e','l','_','D','e','p','e','n','d','e','n','t', -0,2,'V','o','w','e','l','_','I','n','d','e','p','e','n','d','e','n','t',0,'V','o','w','e','l','_','I','n','d','e','p','e', -'n','d','e','n','t',0,2,'v','o',0,'V','e','r','t','i','c','a','l','_','O','r','i','e','n','t','a','t','i','o','n',0, -2,'R',0,'R','o','t','a','t','e','d',0,2,'T','r',0,'T','r','a','n','s','f','o','r','m','e','d','_','R','o','t','a','t', -'e','d',0,2,'T','u',0,'T','r','a','n','s','f','o','r','m','e','d','_','U','p','r','i','g','h','t',0, -2,'U',0,'U','p','r','i','g','h','t',0,2,'g','c','m',0,'G','e','n','e','r','a','l','_','C','a','t','e','g','o','r','y', -'_','M','a','s','k',0,2,'C',0,'O','t','h','e','r',0,2,'L',0,'L','e','t','t','e','r',0, -2,'L','C',0,'C','a','s','e','d','_','L','e','t','t','e','r',0, -3,'M',0,'M','a','r','k',0,'C','o','m','b','i','n','i','n','g','_','M','a','r','k',0, -2,'N',0,'N','u','m','b','e','r',0,3,'P',0,'P','u','n','c','t','u','a','t','i','o','n',0, -'p','u','n','c','t',0,2,'S',0,'S','y','m','b','o','l',0, -2,'Z',0,'S','e','p','a','r','a','t','o','r',0,2,'n','v',0,'N','u','m','e','r','i','c','_','V','a','l','u','e',0, -2,'a','g','e',0,'A','g','e',0,2,'b','m','g',0,'B','i','d','i','_','M','i','r','r','o','r','i','n','g','_','G','l','y', -'p','h',0,2,'c','f',0,'C','a','s','e','_','F','o','l','d','i','n','g',0, -2,'i','s','c',0,'I','S','O','_','C','o','m','m','e','n','t',0, -2,'l','c',0,'L','o','w','e','r','c','a','s','e','_','M','a','p','p','i','n','g',0, -2,'n','a',0,'N','a','m','e',0,3,'s','c','f',0,'S','i','m','p','l','e','_','C','a','s','e','_','F','o','l','d','i','n', -'g',0,'s','f','c',0,2,'s','l','c',0,'S','i','m','p','l','e','_','L','o','w','e','r','c','a','s','e','_','M','a','p','p', -'i','n','g',0,2,'s','t','c',0,'S','i','m','p','l','e','_','T','i','t','l','e','c','a','s','e','_','M','a','p','p','i','n', -'g',0,2,'s','u','c',0,'S','i','m','p','l','e','_','U','p','p','e','r','c','a','s','e','_','M','a','p','p','i','n','g',0, -2,'t','c',0,'T','i','t','l','e','c','a','s','e','_','M','a','p','p','i','n','g',0, -2,'n','a','1',0,'U','n','i','c','o','d','e','_','1','_','N','a','m','e',0, -2,'u','c',0,'U','p','p','e','r','c','a','s','e','_','M','a','p','p','i','n','g',0, -2,'b','p','b',0,'B','i','d','i','_','P','a','i','r','e','d','_','B','r','a','c','k','e','t',0, -2,'s','c','x',0,'S','c','r','i','p','t','_','E','x','t','e','n','s','i','o','n','s',0 -}; - -U_NAMESPACE_END - -#endif // INCLUDED_FROM_PROPNAME_CPP +// Copyright (C) 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// Copyright (C) 1999-2016, International Business Machines +// Corporation and others. All Rights Reserved. +// +// file name: propname_data.h +// +// machine-generated by: icu/tools/unicode/c/genprops/pnamesbuilder.cpp + +#ifdef INCLUDED_FROM_PROPNAME_CPP + +U_NAMESPACE_BEGIN + +const int32_t PropNameData::indexes[8]={0x20,0x1660,0x5294,0xacd0,0xacd0,0xacd0,0x31,0}; + +const int32_t PropNameData::valueMaps[1424]={ +6,0,0x48,0,0xf1,0x368,0xf1,0x37e,0xf1,0x393,0xf1,0x3a9,0xf1,0x3b4,0xf1,0x3d5, +0xf1,0x3e5,0xf1,0x3f4,0xf1,0x402,0xf1,0x426,0xf1,0x43d,0xf1,0x455,0xf1,0x46c,0xf1,0x47b, +0xf1,0x48a,0xf1,0x49b,0xf1,0x4a9,0xf1,0x4bb,0xf1,0x4d5,0xf1,0x4f0,0xf1,0x505,0xf1,0x522, +0xf1,0x533,0xf1,0x53e,0xf1,0x55d,0xf1,0x573,0xf1,0x584,0xf1,0x594,0xf1,0x5af,0xf1,0x5c8, +0xf1,0x5d9,0xf1,0x5f3,0xf1,0x606,0xf1,0x616,0xf1,0x630,0xf1,0x649,0xf1,0x660,0xf1,0x674, +0xf1,0x68a,0xf1,0x69e,0xf1,0x6b4,0xf1,0x6ce,0xf1,0x6e6,0xf1,0x702,0xf1,0x70a,0xf1,0x712, +0xf1,0x71a,0xf1,0x722,0xf1,0x72b,0xf1,0x738,0xf1,0x74b,0xf1,0x768,0xf1,0x785,0xf1,0x7a2, +0xf1,0x7c0,0xf1,0x7de,0xf1,0x802,0xf1,0x80f,0xf1,0x829,0xf1,0x83e,0xf1,0x859,0xf1,0x870, +0xf1,0x887,0xf1,0x8a9,0xf1,0x8c8,0xf1,0x8e1,0xf1,0x90e,0xf1,0x947,0xf1,0x978,0xf1,0x9a7, +0xf1,0x9d6,0xf1,0x1000,0x1019,0x9eb,0x16d,0xc0b,0x188,0x3279,0xf7,0x3298,0x2d4,0x33d6,0x2ea,0x3430, +0x2f4,0x368d,0x316,0x3fb8,0x382,0x4028,0x38c,0x42c2,0x3bb,0x4300,0x3c3,0x4e45,0x48f,0x4ec3,0x499,0x4ee8, +0x49f,0x4f02,0x4a5,0x4f23,0x4ac,0x4f3d,0xf7,0x4f62,0xf7,0x4f88,0x4b3,0x5032,0x4c9,0x50ab,0x4dc,0x515d, +0x4f7,0x5194,0x4fe,0x5374,0x512,0x57f4,0x53a,0x2000,0x2001,0x5853,0x542,0x3000,0x3001,0x58df,0,0x4000, +0x400e,0x58f1,0,0x58fa,0,0x5914,0,0x5925,0,0x5936,0,0x594c,0,0x5955,0,0x5972, +0,0x5990,0,0x59ae,0,0x59cc,0,0x59e2,0,0x59f6,0,0x5a0c,0,0x7000,0x7001,0x5a25, +0,0x844,0x12,0,1,0x12,0x20,0x862,0x4a,0,1,6,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,0x5e,0x68,0x79,0x84,0x91,0x9e,0xab,0xb8,0xc5,0xd2, +0xdf,0xec,0xf9,0x106,0x113,0x120,0x12d,0x13a,0x147,0x154,0x161,0x16e,0x17b,0x188,0x195,0x1a2, +0x1af,0x1bc,0x1c9,0x1d6,0x1e3,0x1f0,0x1fd,0x20c,0x21b,0x22a,0x239,0x248,0x257,0x266,0x275,0x28f, +0x2a3,0x2b7,0x2d2,0x2e1,0x2ea,0x2fa,0x302,0x30b,0x31a,0x323,0x333,0x344,0x355,0xa03,1,0, +0x17,0x9fa,0xa0b,0xa1c,0xa30,0xa47,0xa5f,0xa71,0xa86,0xa9d,0xab2,0xac2,0xad4,0xaf1,0xb0d,0xb1f, +0xb3c,0xb58,0xb74,0xb89,0xb9e,0xbb8,0xbd3,0xbee,0xba5,1,0,0x148,0xc16,0xc23,0xc36,0xc5e, +0xc7c,0xc9a,0xcb2,0xcdd,0xd07,0xd1f,0xd32,0xd45,0xd54,0xd63,0xd72,0xd81,0xd98,0xda9,0xdbc,0xdcf, +0xddc,0xde9,0xdf8,0xe09,0xe1e,0xe2f,0xe3a,0xe43,0xe54,0xe65,0xe78,0xe8a,0xe9d,0xeb0,0xeef,0xefc, +0xf09,0xf16,0xf2b,0xf5b,0xf75,0xf96,0xfc1,0xfe4,0x1042,0x1069,0x1084,0x1093,0x10ba,0x10e2,0x1105,0x1128, +0x1152,0x116b,0x118a,0x11ad,0x11d1,0x11e4,0x11fe,0x1228,0x1240,0x1268,0x1291,0x12a4,0x12b7,0x12ca,0x12f1,0x1300, +0x1320,0x134e,0x136c,0x139a,0x13b6,0x13d1,0x13ea,0x1403,0x1424,0x1454,0x1473,0x1495,0x14c9,0x14f6,0x153b,0x155c, +0x1586,0x15a7,0x15d0,0x15e3,0x1616,0x162d,0x163c,0x164d,0x1678,0x168f,0x16c0,0x16ee,0x1731,0x173c,0x1775,0x1786, +0x1797,0x17a4,0x17b7,0x17f1,0x1815,0x1839,0x1873,0x18ab,0x18d6,0x18ee,0x191a,0x1946,0x1953,0x1962,0x197f,0x19a1, +0x19cf,0x19ef,0x1a16,0x1a3d,0x1a5c,0x1a6f,0x1a80,0x1a91,0x1ab6,0x1adb,0x1b02,0x1b36,0x1b63,0x1b81,0x1b94,0x1bad, +0x1be6,0x1bf5,0x1c15,0x1c37,0x1c59,0x1c70,0x1c87,0x1cb4,0x1ccd,0x1ce6,0x1d17,0x1d41,0x1d5c,0x1d6f,0x1d8e,0x1d97, +0x1daa,0x1dc8,0x1de6,0x1df9,0x1e10,0x1e25,0x1e5a,0x1e7e,0x1e93,0x1ea2,0x1eb5,0x1ed9,0x1ee2,0x1f06,0x1f1d,0x1f30, +0x1f3f,0x1f4a,0x1f6b,0x1f83,0x1f92,0x1fa1,0x1fb0,0x1fc7,0x1fdc,0x1ff1,0x202a,0x203d,0x2059,0x2064,0x2071,0x209f, +0x20c3,0x20e6,0x20f9,0x211b,0x212e,0x2149,0x216c,0x218f,0x21b4,0x21c5,0x21f4,0x2221,0x2238,0x2253,0x2262,0x228d, +0x22c5,0x22ff,0x232d,0x233e,0x234b,0x236f,0x237e,0x239a,0x23b4,0x23d1,0x2409,0x241e,0x244b,0x246a,0x2498,0x24b8, +0x24ec,0x24fb,0x2525,0x2548,0x2573,0x257e,0x258f,0x25aa,0x25ce,0x25db,0x25f0,0x2617,0x2642,0x2679,0x268c,0x269d, +0x26cd,0x26de,0x26ed,0x2702,0x2720,0x2733,0x2746,0x275d,0x277a,0x2785,0x278e,0x27b0,0x27c5,0x27ea,0x2801,0x282a, +0x2845,0x285a,0x2873,0x2894,0x28c9,0x28da,0x290b,0x292f,0x2940,0x2959,0x2964,0x2991,0x29b3,0x29e1,0x2a14,0x2a23, +0x2a34,0x2a51,0x2a93,0x2aba,0x2ac7,0x2adc,0x2b00,0x2b26,0x2b5f,0x2b70,0x2b94,0x2b9f,0x2bac,0x2bbb,0x2be0,0x2c0e, +0x2c2a,0x2c47,0x2c54,0x2c65,0x2c83,0x2ca6,0x2cc3,0x2cd0,0x2cf0,0x2d0d,0x2d2e,0x2d57,0x2d68,0x2d87,0x2da0,0x2db9, +0x2dca,0x2e13,0x2e24,0x2e3d,0x2e6c,0x2e99,0x2ebe,0x2f00,0x2f1c,0x2f2b,0x2f42,0x2f70,0x2f89,0x2fb2,0x2fcc,0x3007, +0x3025,0x3034,0x3054,0x306f,0x3093,0x30af,0x30cd,0x30eb,0x3102,0x3111,0x311c,0x3159,0x316c,0x3196,0x31b6,0x31e4, +0x3208,0x3230,0x3255,0x3260,0x1fa9,1,0,0x12,0x32af,0x32bf,0x32d2,0x32e2,0x32f2,0x3301,0x3311,0x3323, +0x3336,0x3348,0x3358,0x3368,0x3377,0x3386,0x3396,0x33a3,0x33b2,0x33c6,0x2067,1,0,6,0x33eb,0x33f6, +0x3403,0x3410,0x341d,0x3428,0x20ab,1,0,0x1e,0x3445,0x3454,0x3469,0x347e,0x3493,0x34a7,0x34b8,0x34cc, +0x34df,0x34f0,0x3509,0x351b,0x352c,0x3540,0x3553,0x356b,0x357d,0x3588,0x3598,0x35a6,0x35bb,0x35d0,0x35e6,0x3600, +0x3616,0x3626,0x363a,0x364e,0x365f,0x3677,0x22d6,1,0,0x68,0x369f,0x36c2,0x36cb,0x36d8,0x36e3,0x36ec, +0x36f7,0x3700,0x3719,0x371e,0x3727,0x3744,0x374d,0x375a,0x3763,0x3787,0x378e,0x3797,0x37aa,0x37b5,0x37be,0x37c9, +0x37e2,0x37eb,0x37fa,0x3805,0x380e,0x3819,0x3822,0x3829,0x3832,0x383d,0x3846,0x385f,0x3868,0x3875,0x3880,0x3891, +0x389c,0x38b1,0x38c8,0x38d1,0x38da,0x38f3,0x38fe,0x3907,0x3910,0x3927,0x3944,0x394f,0x3960,0x396b,0x3972,0x397f, +0x398c,0x39b9,0x39ce,0x39d7,0x39f2,0x3a15,0x3a36,0x3a57,0x3a7c,0x3aa3,0x3ac4,0x3ae7,0x3b08,0x3b2f,0x3b50,0x3b75, +0x3b94,0x3bb3,0x3bd2,0x3bef,0x3c10,0x3c31,0x3c54,0x3c79,0x3c98,0x3cb7,0x3cd8,0x3cff,0x3d24,0x3d43,0x3d64,0x3d87, +0x3da2,0x3dbb,0x3dd6,0x3def,0x3e0c,0x3e27,0x3e44,0x3e63,0x3e80,0x3e9d,0x3ebc,0x3ed9,0x3ef4,0x3f11,0x3f2e,0x3f61, +0x3f88,0x3f9b,0x2639,1,0,6,0x3fc9,0x3fd8,0x3fe8,0x3ff8,0x4008,0x4019,0x2697,1,0,0x2b, +0x4037,0x4043,0x4051,0x4060,0x406f,0x407f,0x4090,0x40a4,0x40b9,0x40cf,0x40e2,0x40f6,0x4106,0x410f,0x411a,0x412a, +0x4146,0x4158,0x4166,0x4175,0x4181,0x4196,0x41aa,0x41bd,0x41cb,0x41df,0x41ed,0x41f7,0x4209,0x4215,0x4223,0x4233, +0x423a,0x4241,0x4248,0x424f,0x4256,0x426c,0x428d,0x870,0x429f,0x42aa,0x42b9,0x28f0,1,0,4,0x42d3, +0x42de,0x42ea,0x42f4,0x2916,1,0,0xc8,0x430b,0x4318,0x432d,0x433a,0x4349,0x4357,0x4366,0x4375,0x4387, +0x4396,0x43a4,0x43b5,0x43c4,0x43d3,0x43e0,0x43ec,0x43fb,0x440a,0x4414,0x4421,0x442e,0x443d,0x444b,0x445a,0x4466, +0x4470,0x447c,0x448c,0x449c,0x44aa,0x44b6,0x44c7,0x44d3,0x44df,0x44ed,0x44fa,0x4506,0x4513,0xe2f,0x4520,0x452e, +0x4548,0x4551,0x455f,0x456d,0x4579,0x4588,0x4596,0x45a4,0x45b0,0x45bf,0x45cd,0x45db,0x45e8,0x45f7,0x4612,0x4621, +0x4632,0x4643,0x4656,0x4668,0x4677,0x4689,0x4698,0x46a4,0x46af,0x1f3f,0x46bc,0x46c7,0x46d2,0x46dd,0x46e8,0x4703, +0x470e,0x4719,0x4724,0x4737,0x474b,0x4756,0x4765,0x4774,0x477f,0x478a,0x4797,0x47a6,0x47b4,0x47bf,0x47da,0x47e4, +0x47f5,0x4806,0x4815,0x4826,0x4831,0x483c,0x4847,0x4852,0x485d,0x4868,0x4873,0x487d,0x4888,0x4898,0x48a3,0x48b1, +0x48be,0x48c9,0x48d8,0x48e5,0x48f2,0x4901,0x490e,0x491f,0x4931,0x4941,0x494c,0x495f,0x4976,0x4984,0x4991,0x499c, +0x49a9,0x49ba,0x49d6,0x49ec,0x49f7,0x4a14,0x4a24,0x4a33,0x4a3e,0x4a49,0x2059,0x4a55,0x4a60,0x4a78,0x4a88,0x4a97, +0x4aa5,0x4ab3,0x4abe,0x4ac9,0x4add,0x4af4,0x4b0c,0x4b1c,0x4b2c,0x4b3c,0x4b4e,0x4b59,0x4b64,0x4b6e,0x4b7a,0x4b88, +0x4b9b,0x4ba7,0x4bb4,0x4bbf,0x4bdb,0x4be8,0x4bf6,0x4c0f,0x2959,0x4c1e,0x277a,0x4c2b,0x4c39,0x4c4b,0x4c59,0x4c65, +0x4c75,0x2b94,0x4c83,0x4c8f,0x4c9a,0x4ca5,0x4cb0,0x4cc4,0x4cd2,0x4ce9,0x4cf5,0x4d09,0x4d17,0x4d29,0x4d3f,0x4d4d, +0x4d5f,0x4d6d,0x4d8a,0x4d9c,0x4da9,0x4dba,0x4dcc,0x4de6,0x4df3,0x4e06,0x4e17,0x3111,0x4e24,0x3255,0x4e33,0x3370, +1,0,6,0x4e5f,0x4e72,0x4e82,0x4e90,0x4ea1,0x4eb1,0x33cc,0x12,0,1,0x4edb,0x4ee1,0x33d9, +0x12,0,1,0x4edb,0x4ee1,0x33e6,1,0,3,0x4edb,0x4ee1,0x4f1a,0x33fc,1,0,3, +0x4edb,0x4ee1,0x4f1a,0x3412,1,0,0x12,0x4fa4,0x4fae,0x4fba,0x4fc1,0x4fcc,0x4fd1,0x4fd8,0x4fdf,0x4fe8, +0x4fed,0x4ff2,0x5002,0x870,0x429f,0x500e,0x42aa,0x501e,0x42b9,0x34bb,1,0,0xf,0x4fa4,0x5045,0x504f, +0x5059,0x5064,0x4175,0x506e,0x507a,0x5082,0x5089,0x5093,0x4fba,0x4fc1,0x4fd1,0x509d,0x3542,1,0,0x17, +0x4fa4,0x50ba,0x5059,0x50c6,0x50d3,0x50e1,0x4175,0x50ec,0x4fba,0x50fd,0x4fd1,0x510c,0x511a,0x870,0x428d,0x5126, +0x5137,0x429f,0x500e,0x42aa,0x501e,0x42b9,0x5148,0x365f,1,0,3,0x517b,0x5183,0x518b,0x3678,1, +0,0x10,0x51b4,0x51bb,0x51ca,0x51eb,0x520e,0x5219,0x5238,0x524f,0x525c,0x5265,0x5284,0x52b7,0x52d2,0x5301, +0x531e,0x5343,0x3711,1,0,0x24,0x5392,0x539f,0x53b2,0x53bf,0x53ec,0x5411,0x5426,0x5445,0x5466,0x5493, +0x54cc,0x54ef,0x5512,0x553f,0x5574,0x559b,0x55c4,0x55fb,0x562a,0x564b,0x5670,0x567f,0x56a2,0x56b9,0x56c6,0x56d5, +0x56f2,0x570b,0x572e,0x5753,0x576c,0x5781,0x5790,0x57a1,0x57ae,0x57cf,0x38e1,1,0,4,0x580d,0x5818, +0x5830,0x5848,0x391d,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,0x3445,0x3454,0x3469,0x347e,0x5881,0x3493, +0x34a7,0x5877,0x34b8,0x34cc,0x34df,0x5892,0x34f0,0x3509,0x351b,0x58a9,0x352c,0x3540,0x3553,0x58d2,0x356b,0x357d, +0x3588,0x3598,0x586e,0x35a6,0x35bb,0x35d0,0x35e6,0x3600,0x3616,0x3626,0x363a,0x364e,0x58c8,0x365f,0x3677,0x58b3 +}; + +const uint8_t PropNameData::bytesTries[15412]={ +0,0x15,0x6d,0xc3,0xc7,0x73,0xc2,0x12,0x76,0x7a,0x76,0x6a,0x77,0xa2,0x52,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,3,0x61,0x2e,0x65,0x4c,0x6f,0xc3,0x18,0x73,0x69,0x1e,0x72,0x69,0x61,0x74, +0x69,0x6f,0x6e,0x73,0x65,0x6c,0x65,0x63,0x74,0x6f,0x72,0x69,0x10,0x72,0x1f,0x74, +0x69,0x63,0x61,0x6c,0x6f,0x72,0x69,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f,0x6e,0xc3, +0x18,3,0x62,0xc3,0x14,0x68,0x32,0x6f,0x42,0x73,0x13,0x70,0x61,0x63,0x65,0x5f, +0x17,0x69,0x74,0x65,0x73,0x70,0x61,0x63,0x65,0x5f,0x16,0x72,0x64,0x62,0x72,0x65, +0x61,0x6b,0xc3,0x14,0x73,0xa2,0x49,0x74,0xa4,0x3b,0x75,3,0x63,0xd9,0x40,0xc, +0x69,0x52,0x6e,0x58,0x70,0x12,0x70,0x65,0x72,0x5c,0x13,0x63,0x61,0x73,0x65,0x5c, +0x16,0x6d,0x61,0x70,0x70,0x69,0x6e,0x67,0xd9,0x40,0xc,0x12,0x64,0x65,0x6f,0x5b, +0x10,0x69,1,0x63,0x3e,0x66,0x1b,0x69,0x65,0x64,0x69,0x64,0x65,0x6f,0x67,0x72, +0x61,0x70,0x68,0x5b,0x17,0x6f,0x64,0x65,0x31,0x6e,0x61,0x6d,0x65,0xd9,0x40,0xb, +0xa,0x69,0x84,0x70,0x19,0x70,0x30,0x74,0x36,0x75,0x10,0x63,0xd9,0x40,9,0x12, +0x61,0x63,0x65,0x5f,1,0x63,0xd9,0x40,8,0x65,0x11,0x72,0x6d,0x67,0x69,0x3c, +0x6c,0xa2,0x5f,0x6f,0x17,0x66,0x74,0x64,0x6f,0x74,0x74,0x65,0x64,0x57,0x13,0x6d, +0x70,0x6c,0x65,3,0x63,0x50,0x6c,0x68,0x74,0x8a,0x75,0x1e,0x70,0x70,0x65,0x72, +0x63,0x61,0x73,0x65,0x6d,0x61,0x70,0x70,0x69,0x6e,0x67,0xd9,0x40,9,0x19,0x61, +0x73,0x65,0x66,0x6f,0x6c,0x64,0x69,0x6e,0x67,0xd9,0x40,6,0x1e,0x6f,0x77,0x65, +0x72,0x63,0x61,0x73,0x65,0x6d,0x61,0x70,0x70,0x69,0x6e,0x67,0xd9,0x40,7,0x1e, +0x69,0x74,0x6c,0x65,0x63,0x61,0x73,0x65,0x6d,0x61,0x70,0x70,0x69,0x6e,0x67,0xd9, +0x40,8,0x10,0x63,0xd9,0x40,7,0x62,0xc3,0x13,0x63,0x34,0x64,0x57,0x65,0x6e, +0x66,0x10,0x63,0xd9,0x40,6,0xc2,0xa,2,0x66,0xd9,0x40,6,0x72,0x28,0x78, +0xd9,0x70,0,0x12,0x69,0x70,0x74,0xc2,0xa,0x19,0x65,0x78,0x74,0x65,0x6e,0x73, +0x69,0x6f,0x6e,0x73,0xd9,0x70,0,1,0x67,0x6a,0x6e,1,0x73,0x54,0x74,0x13, +0x65,0x6e,0x63,0x65,1,0x62,0x34,0x74,0x16,0x65,0x72,0x6d,0x69,0x6e,0x61,0x6c, +0x67,0x13,0x72,0x65,0x61,0x6b,0xc3,0x13,0x14,0x69,0x74,0x69,0x76,0x65,0x65,1, +0x6d,0x2e,0x73,0x13,0x74,0x61,0x72,0x74,0x73,0x19,0x65,0x6e,0x74,0x73,0x74,0x61, +0x72,0x74,0x65,0x72,0x73,3,0x63,0x66,0x65,0x72,0x69,0x98,0x72,0x19,0x61,0x69, +0x6c,0x63,0x61,0x6e,0x6f,0x6e,0x69,0x63,0x1f,0x61,0x6c,0x63,0x6f,0x6d,0x62,0x69, +0x6e,0x69,0x6e,0x67,0x63,0x6c,0x61,0x73,0x73,0xc3,0x11,0xd8,0x40,0xa,0x11,0x63, +0x63,0xc3,0x11,0x11,0x72,0x6d,0x58,0x1e,0x69,0x6e,0x61,0x6c,0x70,0x75,0x6e,0x63, +0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0x59,0x1d,0x74,0x6c,0x65,0x63,0x61,0x73,0x65, +0x6d,0x61,0x70,0x70,0x69,0x6e,0x67,0xd9,0x40,0xa,0x6d,0xa2,0x76,0x6e,0xa2,0x78, +0x70,0xa4,0x3e,0x71,0xa4,0x90,0x72,3,0x61,0x2c,0x65,0x36,0x67,0x54,0x69,0x9d, +0x14,0x64,0x69,0x63,0x61,0x6c,0x55,0x1e,0x67,0x69,0x6f,0x6e,0x61,0x6c,0x69,0x6e, +0x64,0x69,0x63,0x61,0x74,0x6f,0x72,0x9d,0x15,0x69,0x65,0x6d,0x6f,0x6a,0x69,0xa2, +0x47,3,0x66,0x44,0x6d,0x5c,0x74,0x7c,0x7a,0x19,0x77,0x6a,0x73,0x65,0x71,0x75, +0x65,0x6e,0x63,0x65,0xa3,0x46,0x1a,0x6c,0x61,0x67,0x73,0x65,0x71,0x75,0x65,0x6e, +0x63,0x65,0xa3,0x44,0x1e,0x6f,0x64,0x69,0x66,0x69,0x65,0x72,0x73,0x65,0x71,0x75, +0x65,0x6e,0x63,0x65,0xa3,0x43,0x19,0x61,0x67,0x73,0x65,0x71,0x75,0x65,0x6e,0x63, +0x65,0xa3,0x45,0x12,0x61,0x74,0x68,0x4f,6,0x6f,0x39,0x6f,0x32,0x74,0xc3,9, +0x75,0x54,0x76,0xd9,0x30,0,0x12,0x6e,0x63,0x68,0x1f,0x61,0x72,0x61,0x63,0x74, +0x65,0x72,0x63,0x6f,0x64,0x65,0x70,0x6f,0x69,0x6e,0x74,0x51,0x14,0x6d,0x65,0x72, +0x69,0x63,1,0x74,0x32,0x76,0x13,0x61,0x6c,0x75,0x65,0xd9,0x30,0,0x12,0x79, +0x70,0x65,0xc3,9,0x61,0xa2,0x77,0x63,0xa2,0x82,0x66,2,0x63,0x98,0x64,0xa2, +0x53,0x6b,1,0x63,0x56,0x64,1,0x69,0x42,0x71,1,0x63,0xc3,0xd,0x75,0x17, +0x69,0x63,0x6b,0x63,0x68,0x65,0x63,0x6b,0xc3,0xd,0x13,0x6e,0x65,0x72,0x74,0x6d, +1,0x69,0x42,0x71,1,0x63,0xc3,0xf,0x75,0x17,0x69,0x63,0x6b,0x63,0x68,0x65, +0x63,0x6b,0xc3,0xf,0x13,0x6e,0x65,0x72,0x74,0x71,1,0x69,0x42,0x71,1,0x63, +0xc3,0xe,0x75,0x17,0x69,0x63,0x6b,0x63,0x68,0x65,0x63,0x6b,0xc3,0xe,0x13,0x6e, +0x65,0x72,0x74,0x6f,1,0x69,0x42,0x71,1,0x63,0xc3,0xc,0x75,0x17,0x69,0x63, +0x6b,0x63,0x68,0x65,0x63,0x6b,0xc3,0xc,0x13,0x6e,0x65,0x72,0x74,0x6b,0xd8,0x40, +5,1,0x31,0xd9,0x40,0xb,0x6d,0x10,0x65,0xd9,0x40,5,0x12,0x68,0x61,0x72, +0x51,2,0x61,0x6c,0x63,0xa2,0x4c,0x72,1,0x65,0x2a,0x69,0x11,0x6e,0x74,0x7f, +0x16,0x70,0x65,0x6e,0x64,0x65,0x64,0x63,0x1f,0x6f,0x6e,0x63,0x61,0x74,0x65,0x6e, +0x61,0x74,0x69,0x6f,0x6e,0x6d,0x61,0x72,0x6b,0x9f,0x10,0x74,2,0x73,0x2c,0x74, +0x30,0x77,0x10,0x73,0x77,0x11,0x79,0x6e,0x75,0x12,0x65,0x72,0x6e,1,0x73,0x38, +0x77,0x18,0x68,0x69,0x74,0x65,0x73,0x70,0x61,0x63,0x65,0x77,0x14,0x79,0x6e,0x74, +0x61,0x78,0x75,0x10,0x6d,0x9f,1,0x6d,0x3c,0x75,0x1a,0x6f,0x74,0x61,0x74,0x69, +0x6f,0x6e,0x6d,0x61,0x72,0x6b,0x53,0x12,0x61,0x72,0x6b,0x53,0x66,0xc1,0xf8,0x69, +0xc1,0x3c,0x69,0xa2,0x6f,0x6a,0xa4,9,0x6c,4,0x62,0xc3,8,0x63,0x8c,0x65, +0x98,0x69,0xa2,0x56,0x6f,2,0x65,0x4b,0x67,0x4c,0x77,0x11,0x65,0x72,0x4c,0x13, +0x63,0x61,0x73,0x65,0x4c,0x16,0x6d,0x61,0x70,0x70,0x69,0x6e,0x67,0xd9,0x40,4, +0x11,0x69,0x63,0x1f,0x61,0x6c,0x6f,0x72,0x64,0x65,0x72,0x65,0x78,0x63,0x65,0x70, +0x74,0x69,0x6f,0x6e,0x4b,0xd8,0x40,4,0x11,0x63,0x63,0xc3,0x10,0x18,0x61,0x64, +0x63,0x61,0x6e,0x6f,0x6e,0x69,0x63,0x1f,0x61,0x6c,0x63,0x6f,0x6d,0x62,0x69,0x6e, +0x69,0x6e,0x67,0x63,0x6c,0x61,0x73,0x73,0xc3,0x10,0x16,0x6e,0x65,0x62,0x72,0x65, +0x61,0x6b,0xc3,8,2,0x64,0x4a,0x6e,0xa2,0x5b,0x73,1,0x63,0xd9,0x40,3, +0x6f,0x16,0x63,0x6f,0x6d,0x6d,0x65,0x6e,0x74,0xd9,0x40,3,2,0x63,0x80,0x65, +0x90,0x73,0x40,1,0x62,0x52,0x74,0x46,1,0x61,0x40,0x72,0x1c,0x69,0x6e,0x61, +0x72,0x79,0x6f,0x70,0x65,0x72,0x61,0x74,0x6f,0x72,0x47,0x11,0x72,0x74,0x41,0x44, +0x1c,0x69,0x6e,0x61,0x72,0x79,0x6f,0x70,0x65,0x72,0x61,0x74,0x6f,0x72,0x45,0x3e, +0x16,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3f,0x10,0x6f,0x42,0x16,0x67,0x72,0x61, +0x70,0x68,0x69,0x63,0x43,2,0x64,0x2e,0x70,0x86,0x73,0x10,0x63,0xc3,0x17,0x11, +0x69,0x63,1,0x70,0x46,0x73,0x1e,0x79,0x6c,0x6c,0x61,0x62,0x69,0x63,0x63,0x61, +0x74,0x65,0x67,0x6f,0x72,0x79,0xc3,0x17,0x10,0x6f,0x1f,0x73,0x69,0x74,0x69,0x6f, +0x6e,0x61,0x6c,0x63,0x61,0x74,0x65,0x67,0x6f,0x72,0x79,0xc3,0x16,0x10,0x63,0xc3, +0x16,2,0x67,0xc3,6,0x6f,0x26,0x74,0xc3,7,0x11,0x69,0x6e,1,0x63,0x4a, +0x69,0x11,0x6e,0x67,1,0x67,0x2e,0x74,0x12,0x79,0x70,0x65,0xc3,7,0x13,0x72, +0x6f,0x75,0x70,0xc3,6,0x48,0x15,0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x49,0x66,0x86, +0x67,0xa2,0x4a,0x68,3,0x61,0x36,0x65,0x58,0x73,0x68,0x79,0x13,0x70,0x68,0x65, +0x6e,0x3d,0x1f,0x6e,0x67,0x75,0x6c,0x73,0x79,0x6c,0x6c,0x61,0x62,0x6c,0x65,0x74, +0x79,0x70,0x65,0xc3,0xb,0x10,0x78,0x3a,0x14,0x64,0x69,0x67,0x69,0x74,0x3b,0x10, +0x74,0xc3,0xb,0x16,0x75,0x6c,0x6c,0x63,0x6f,0x6d,0x70,0x1f,0x6f,0x73,0x69,0x74, +0x69,0x6f,0x6e,0x65,0x78,0x63,0x6c,0x75,0x73,0x69,0x6f,0x6e,0x33,2,0x63,0xa2, +0x44,0x65,0xa2,0x4b,0x72,3,0x61,0x34,0x62,0x84,0x65,0x8a,0x6c,0x12,0x69,0x6e, +0x6b,0x39,0x11,0x70,0x68,0x7c,0x12,0x65,0x6d,0x65,3,0x62,0x5e,0x63,0x30,0x65, +0x48,0x6c,0x12,0x69,0x6e,0x6b,0x39,0x1a,0x6c,0x75,0x73,0x74,0x65,0x72,0x62,0x72, +0x65,0x61,0x6b,0xc3,0x12,0x14,0x78,0x74,0x65,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,0xa2,0x62,0xa2,0xd0,0x63,0xa4,0x4f, +0x64,0xa6,0x1c,0x65,5,0x6d,0x75,0x6d,0x6e,0x70,0xa2,0x6b,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,3,0x63,0x44,0x6b,0x54,0x6d,0x70, +0x70,0x1a,0x72,0x65,0x73,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f,0x6e,0x95,0x17,0x6f, +0x6d,0x70,0x6f,0x6e,0x65,0x6e,0x74,0x9b,0x1c,0x65,0x79,0x63,0x61,0x70,0x73,0x65, +0x71,0x75,0x65,0x6e,0x63,0x65,0xa3,0x42,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,5,0x6c,0x22, +0x6c,0x36,0x6d,0x52,0x70,1,0x62,0xd9,0x40,0xd,0x74,0xc3,0x15,2,0x61,0x32, +0x6b,0xc3,1,0x6f,0x11,0x63,0x6b,0xc3,1,0x11,0x6e,0x6b,0x7b,0x10,0x67,0xd9, +0x40,1,0x61,0xa2,0x4f,0x63,0xc3,0,0x69,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,0x17,0x73,0x69,0x63,0x65,0x6d,0x6f,0x6a,0x69,0xa3,0x41,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,0xb,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, +0x64,0x45,0x64,0x4e,0x68,0x88,0x69,1,0x6f,0x26,0x73,0xa3,0xf0,0x1a,0x74,0x61, +0x73,0x75,0x62,0x73,0x63,0x72,0x69,0x70,0x74,0xa3,0xf0,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,0x12,0x61,0x6e,0x72,0x2c,0x15, +0x65,0x61,0x64,0x69,0x6e,0x67,0x2d,0x61,0xa2,0x7b,0x62,0xa2,0xd4,0x63,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,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,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,0xc9,0x20,0x74,0xc2,0x30,0x77,0x89,0x77, +0x86,0x79,0xa2,0x46,0x7a,1,0x61,0x58,0x6e,0x1a,0x61,0x6d,0x65,0x6e,0x6e,0x79, +0x6d,0x75,0x73,0x69,0x63,0xa4,0x40,0x19,0x61,0x6c,0x6e,0x6f,0x74,0x61,0x74,0x69, +0x6f,0x6e,0xa5,0x40,0x1c,0x6e,0x61,0x62,0x61,0x7a,0x61,0x72,0x73,0x71,0x75,0x61, +0x72,0x65,0xa5,0x18,0x10,0x61,1,0x6e,0x36,0x72,0x16,0x61,0x6e,0x67,0x63,0x69, +0x74,0x69,0xa3,0xfc,0x12,0x63,0x68,0x6f,0xa5,0x2c,1,0x65,0x88,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,0x13,0x7a,0x69,0x64,0x69,0xa5,0x34,0x74,0xa2,0x65,0x75,0xa4,0x4f,0x76,3, +0x61,0x3c,0x65,0x80,0x69,0xa2,0x50,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,0x15,0x74,0x68,0x6b,0x75,0x71,0x69,0xa5,0x3f, +5,0x69,0x3f,0x69,0x5a,0x6f,0x8c,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,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,0x11,0x74,0x6f, +0xa5,0x3d,0x61,0x3e,0x65,0xa2,0xa0,0x68,0x10,0x61,1,0x61,0x24,0x69,0x53,0x11, +0x6e,0x61,0x3d,4,0x67,0x8e,0x69,0xa2,0x49,0x6b,0xa2,0x72,0x6d,0xa2,0x74,0x6e, +0x10,0x67,1,0x73,0x68,0x75,0x10,0x74,0xa4,0x10,1,0x63,0x40,0x73,0x11,0x75, +0x70,0xa4,0x33,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa5,0x33,0x18,0x6f,0x6d, +0x70,0x6f,0x6e,0x65,0x6e,0x74,0x73,0xa5,0x11,0x10,0x61,0xa5,0x3c,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,0x48,0x12,0x73,0x75,0x70,0xa4,0x2b,0x16,0x70,0x6c, +0x65,0x6d,0x65,0x6e,0x74,0xa5,0x2b,0x13,0x6c,0x75,0x67,0x75,0x4b,2,0x63,0x8c, +0x67,0xa2,0x41,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, +0xa2,0xad,0x10,0x61,0xa5,0x3e,0x11,0x61,0x73,0x62,0x12,0x65,0x78,0x74,0xa2,0xad, +0x10,0x61,0xa5,0x3e,0x15,0x61,0x72,0x69,0x74,0x69,0x63,0xa3,0x78,0x70,0xc3,0x4b, +0x70,0xa6,0x61,0x72,0xa8,0x1d,0x73,7,0x6f,0xc1,0xbe,0x6f,0xa2,0x69,0x70,0xa2, +0x85,0x75,0xa2,0xa4,0x79,2,0x6c,0x50,0x6d,0x62,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,0x13,0x62,0x6f,0x6c,0x73, +1,0x61,0x4c,0x66,0x10,0x6f,0x1f,0x72,0x6c,0x65,0x67,0x61,0x63,0x79,0x63,0x6f, +0x6d,0x70,0x75,0x74,0x69,0x6e,0x67,0xa5,0x32,0x1f,0x6e,0x64,0x70,0x69,0x63,0x74, +0x6f,0x67,0x72,0x61,0x70,0x68,0x73,0x65,0x78,0x74,1,0x61,0xa5,0x2a,0x65,0x14, +0x6e,0x64,0x65,0x64,0x61,0xa5,0x2a,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,0x88,0x68,0xa2,0x48,0x69,0xa2, +0x71,0x6d,0x12,0x61,0x6c,0x6c,1,0x66,0x46,0x6b,0x15,0x61,0x6e,0x61,0x65,0x78, +0x74,0xa4,0x29,0x15,0x65,0x6e,0x73,0x69,0x6f,0x6e,0xa5,0x29,0x12,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,0xf0,0x6e,0xa8, +0x23,0x6f,6,0x70,0x63,0x70,0x56,0x72,0x8a,0x73,0xa2,0x4c,0x74,0x10,0x74,0x1f, +0x6f,0x6d,0x61,0x6e,0x73,0x69,0x79,0x61,0x71,0x6e,0x75,0x6d,0x62,0x65,0x72,0x73, +0xa5,0x28,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,1,0x61,0x30,0x6d,0x13,0x61,0x6e,0x79, +0x61,0xa3,0x7a,0x11,0x67,0x65,0xa5,0xf,0x63,0xa2,0x7b,0x67,0xa2,0x7b,0x6c,1, +0x63,0xa2,0x6c,0x64,6,0x70,0x42,0x70,0x3a,0x73,0x5a,0x74,0x88,0x75,0x14,0x79, +0x67,0x68,0x75,0x72,0xa5,0x3b,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,0x14,0x75,0x72,0x6b,0x69,0x63,0xa3,0xbf,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,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,6,0x6b,0x3b,0x6b,0x56,0x6f,0x5a,0x75,0x64,0x79,0x11,0x69, +0x61,0x1f,0x6b,0x65,0x6e,0x67,0x70,0x75,0x61,0x63,0x68,0x75,0x65,0x68,0x6d,0x6f, +0x6e,0x67,0xa5,0x27,0x10,0x6f,0xa3,0x92,0x14,0x62,0x6c,0x6f,0x63,0x6b,0x21,1, +0x6d,0x2c,0x73,0x11,0x68,0x75,0xa5,0x15,0x17,0x62,0x65,0x72,0x66,0x6f,0x72,0x6d, +0x73,0x7b,0x61,0x44,0x62,0x21,0x65,0x10,0x77,1,0x61,0xa5,0xe,0x74,0x14,0x61, +0x69,0x6c,0x75,0x65,0xa3,0x8b,2,0x62,0x3c,0x67,0x4a,0x6e,0x17,0x64,0x69,0x6e, +0x61,0x67,0x61,0x72,0x69,0xa5,0x26,0x15,0x61,0x74,0x61,0x65,0x61,0x6e,0xa3,0xef, +0x16,0x6d,0x75,0x6e,0x64,0x61,0x72,0x69,0xa5,0x47,0x67,0xc4,0x5d,0x6a,0xc1,0xe4, +0x6a,0xa2,0xdf,0x6b,0xa2,0xf8,0x6c,4,0x61,0x54,0x65,0xa2,0x6b,0x69,0xa2,0x82, +0x6f,0xa2,0xc1,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,0x96,0x65,0x11, +0x78,0x74,6,0x64,0x21,0x64,0xa3,0x95,0x65,0x2c,0x66,0xa5,0x39,0x67,0xa5,0x3a, +0xa2,0xe7,0x13,0x6e,0x64,0x65,0x64,6,0x64,0xc,0x64,0xa3,0x95,0x65,0xa3,0xe7, +0x66,0xa5,0x39,0x67,0xa5,0x3a,0x61,0x2a,0x62,0x29,0x63,0xa3,0x94,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,0x4e,0x6e,0x54,0x73,0x10,0x75,0xa2,0xb0,0x12,0x73,0x75,0x70, +0xa4,0x31,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa5,0x31,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, +0xa2,0x43,0x68,4,0x61,0x40,0x69,0x50,0x6d,0x6e,0x6f,0x86,0x75,0x15,0x64,0x61, +0x77,0x61,0x64,0x69,0xa3,0xe6,0x16,0x72,0x6f,0x73,0x68,0x74,0x68,0x69,0xa3,0x89, +0x1d,0x74,0x61,0x6e,0x73,0x6d,0x61,0x6c,0x6c,0x73,0x63,0x72,0x69,0x70,0x74,0xa5, +0x30,0x11,0x65,0x72,0x68,0x16,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0x71,0x12, +0x6a,0x6b,0x69,0xa3,0xe5,5,0x74,0x35,0x74,0x34,0x77,0x7a,0x79,0x13,0x61,0x68, +0x6c,0x69,0xa3,0xa2,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,0x10,0x69,0xa5,0x46,0x69,0xa2,0x4e, +0x6b,0xa2,0x51,0x6e,3,0x61,0x34,0x62,0x84,0x67,0x8a,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,2,0x61,0xa5,0x13,0x62,0xa5,0x38,0x65,0x13, +0x6e,0x64,0x65,0x64,1,0x61,0xa5,0x13,0x62,0xa5,0x38,0x11,0x75,0x6e,0xa3,0x42, +0x11,0x78,0x69,0x96,0x17,0x72,0x61,0x64,0x69,0x63,0x61,0x6c,0x73,0x97,0x12,0x74, +0x68,0x69,0xa3,0xc1,0x1c,0x74,0x6f,0x76,0x69,0x6b,0x6e,0x75,0x6d,0x65,0x72,0x61, +0x6c,0x73,0xa5,0x45,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,0xa4,0xc,0x62,0xa6,0x59, +0x63,0xa8,0x2e,0x64,0xac,0xe3,0x65,5,0x6d,0xa9,0x6d,0x94,0x6e,0xa2,0x41,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, +2,0x61,0xa3,0xc8,0x62,0xa5,0x37,0x65,0x13,0x6e,0x64,0x65,0x64,0xa2,0x85,1, +0x61,0xa3,0xc8,0x62,0xa5,0x37,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,0x4a,0x67,0x76,0x6c,1,0x62,0x30,0x79,0x13,0x6d,0x61,0x69, +0x63,0xa5,0x25,0x13,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,0x1f,0x79,0x70,0x74,0x69,0x61,0x6e,0x68,0x69,0x65,0x72,0x6f,0x67,0x6c, +0x79,0x70,0x68,1,0x66,0x26,0x73,0xa3,0xc2,0x1c,0x6f,0x72,0x6d,0x61,0x74,0x63, +0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x73,0xa5,0x24,7,0x6e,0xc0,0xf2,0x6e,0x3e,0x72, +0xa2,0x5d,0x73,0xa2,0xe5,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,0x7e,0x72,0x12,0x6f,0x77,0x73,0x7d,0x12,0x62,0x69,0x63,0x38, +3,0x65,0x4a,0x6d,0x80,0x70,0xa2,0x50,0x73,0x11,0x75,0x70,0xa2,0x80,0x16,0x70, +0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0x80,0x11,0x78,0x74,3,0x61,0xa3,0xd2,0x62, +0xa5,0x35,0x63,0xa5,0x41,0x65,0x13,0x6e,0x64,0x65,0x64,2,0x61,0xa3,0xd2,0x62, +0xa5,0x35,0x63,0xa5,0x41,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,0x60,0x6f,0xa2,0x69,0x75,0xa4,0x24,0x79,1,0x70,0xa2,0x44, +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,4,0x61,0xa3,0x9e,0x62,0xa3,0xa0,0x63,0xa5,9,0x64,0xa5, +0x43,0x65,0x13,0x6e,0x64,0x65,0x64,3,0x61,0xa3,0x9e,0x62,0xa3,0xa0,0x63,0xa5, +9,0x64,0xa5,0x43,0x10,0x72,1,0x69,0x34,0x6f,0x15,0x6d,0x69,0x6e,0x6f,0x61, +0x6e,0xa5,0x36,0x1a,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,0x4c,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,0xe4,0x68,0xa4,0xe,0x6a,0x10,0x6b,0xa2,0x47, +4,0x63,0x8c,0x65,0xa2,0x80,0x72,0xa2,0x9b,0x73,0xa2,0xad,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,7,0x65,0x6b,0x65,0xa5,0, +0x66,0xa5,0x12,0x67,0xa5,0x2e,0x68,0xa5,0x42,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,7,0x65,0xc,0x65,0xa5,0,0x66, +0xa5,0x12,0x67,0xa5,0x2e,0x68,0xa5,0x42,0x61,0xa3,0x46,0x62,0xa3,0x5e,0x63,0xa3, +0xc5,0x64,0xa3,0xd1,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,2,0x61,0x3a,0x65,0x4c,0x6f,0x16, +0x72,0x61,0x73,0x6d,0x69,0x61,0x6e,0xa5,0x2d,1,0x6b,0x26,0x6d,0xa3,0xa4,0x11, +0x6d,0x61,0xa3,0xd4,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,3,0x65,0x3e,0x69,0x8e,0x6f,0xa2, +0x71,0x75,0x15,0x70,0x6c,0x6f,0x79,0x61,0x6e,0xa3,0xe1,1,0x73,0x60,0x76,0x16, +0x61,0x6e,0x61,0x67,0x61,0x72,0x69,0x3e,0x12,0x65,0x78,0x74,0xa2,0xb3,1,0x61, +0xa5,0x44,0x65,0x13,0x6e,0x64,0x65,0x64,0xa2,0xb3,0x10,0x61,0xa5,0x44,0x13,0x65, +0x72,0x65,0x74,0xa3,0x5a,2,0x61,0x3a,0x6e,0x82,0x76,0x16,0x65,0x73,0x61,0x6b, +0x75,0x72,0x75,0xa5,0x2f,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,0x14,0x67,0x62,0x61,0x74, +0x73,0x91,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,0x13,0x6e,0xc1,0xf,0x74,0x76,0x74,0x4c,0x76, +0x9a,0x77,0xa2,0x48,0x79,0xa2,0x49,0x7a,1,0x61,0x2c,0x68,0x12,0x61,0x69,0x6e, +0x8b,0x11,0x69,0x6e,0x85,2,0x61,0x36,0x65,0x3c,0x68,0x14,0x69,0x6e,0x79,0x65, +0x68,0xa3,0x66,1,0x68,0x71,0x77,0x73,1,0x68,0x28,0x74,0x10,0x68,0x77,0x16, +0x6d,0x61,0x72,0x62,0x75,0x74,0x61,0x74,0x13,0x67,0x6f,0x61,0x6c,0x3d,0x1a,0x65, +0x72,0x74,0x69,0x63,0x61,0x6c,0x74,0x61,0x69,0x6c,0xa3,0x67,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,0x6e,0xa2,0x4c,0x70,0xa2,0x69,0x71,0xa2,0x69,0x72,0xa2,0x6f,0x73, +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,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,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,0x67,0xc1,0xc7,0x67,0xa4,0x52, +0x68,0xa4,0x59,0x6b,0xa4,0x99,0x6c,0xa4,0xb2,0x6d,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,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,0x11,0x61,0x6d,0x4c,0x12, +0x61,0x64,0x68,0x4f,0x61,0x6e,0x62,0xa2,0x54,0x64,0xa2,0x70,0x65,0x31,0x66,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,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,0xc4,0x6f,0x74,0xc1,0x91,0x77,0x96, +0x77,0xa2,0x4c,0x78,0xa2,0x70,0x79,0xa2,0x7a,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,2, +0x61,0x30,0x63,0x5a,0x6f,0x11,0x6c,0x65,0xa3,0x9b,1,0x6e,0x3c,0x72,0x10,0x61, +0xa2,0x92,0x15,0x6e,0x67,0x63,0x69,0x74,0x69,0xa3,0x92,0x12,0x63,0x68,0x6f,0xa3, +0xbc,0x11,0x68,0x6f,0xa3,0xbc,1,0x70,0x2c,0x73,0x11,0x75,0x78,0xa3,0x65,0x11, +0x65,0x6f,0x9b,1,0x65,0x2c,0x69,0x72,0x11,0x69,0x69,0x73,0x11,0x7a,0x69,0xa2, +0xc0,0x11,0x64,0x69,0xa3,0xc0,0x74,0x66,0x75,0xa2,0xde,0x76,1,0x61,0x48,0x69, +1,0x73,0x38,0x74,0x10,0x68,0xa2,0xc5,0x13,0x6b,0x75,0x71,0x69,0xa3,0xc5,0x10, +0x70,0xa3,0x64,0x10,0x69,0xa2,0x63,0x10,0x69,0xa3,0x63,7,0x68,0x3e,0x68,0x34, +0x69,0x48,0x6e,0x86,0x6f,0x11,0x74,0x6f,0xa3,0xc4,0x10,0x61,1,0x61,0x24,0x69, +0x6d,0x6a,0x11,0x6e,0x61,0x6b,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,0x73,0x61,0xa3,0xc3,0x61,0x36,0x65,0xa2, +0x65,0x66,0xa2,0x71,0x67,0x11,0x6c,0x67,0x75,6,0x6c,0x28,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,1,0x73,0x2a,0x75,0x10,0x74,0xa3,0x9a, +0x10,0x61,0xa3,0xc3,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,0x11,0x6e,0x67,0x99,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,0xa4,1,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,6,0x6b,0x36,0x6b,0x56, +0x73,0x6e,0x75,0x74,0x79,0x11,0x69,0x61,0x1f,0x6b,0x65,0x6e,0x67,0x70,0x75,0x61, +0x63,0x68,0x75,0x65,0x68,0x6d,0x6f,0x6e,0x67,0xa3,0xba,1,0x67,0x2e,0x6f,0xa2, +0x57,0x10,0x6f,0xa3,0x57,0x10,0x62,0xa3,0x84,0x11,0x68,0x75,0xa3,0x96,0x12,0x73, +0x68,0x75,0xa3,0x96,0x61,0x42,0x62,0x9e,0x65,0x10,0x77,1,0x61,0xa3,0xaa,0x74, +0x14,0x61,0x69,0x6c,0x75,0x65,0x97,3,0x62,0x32,0x67,0x40,0x6e,0x56,0x72,0x10, +0x62,0xa3,0x8e,0x15,0x61,0x74,0x61,0x65,0x61,0x6e,0xa3,0x8f,0x10,0x6d,0xa2,0xc7, +0x15,0x75,0x6e,0x64,0x61,0x72,0x69,0xa3,0xc7,0x10,0x64,0xa2,0xbb,0x16,0x69,0x6e, +0x61,0x67,0x61,0x72,0x69,0xa3,0xbb,0x11,0x61,0x74,0xa3,0x8f,4,0x67,0x3c,0x6c, +0x4e,0x72,0xa2,0x8e,0x73,0xa2,0x9c,0x75,0x11,0x67,0x72,0xa3,0xc2,1,0x61,0x2a, +0x68,0x11,0x61,0x6d,0x5b,0x10,0x6d,0x5b,1,0x63,0xa2,0x6a,0x64,6,0x70,0x41, +0x70,0x3a,0x73,0x58,0x74,0x86,0x75,0x14,0x79,0x67,0x68,0x75,0x72,0xa3,0xc2,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,0x14,0x75,0x72,0x6b,0x69,0x63,0xa3, +0x58,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,2,0x61,0x36,0x67,0x3c,0x6d,0x10,0x61,0x84,0x12,0x6e,0x79,0x61, +0x85,0x11,0x67,0x65,0xa3,0xab,0x10,0x65,0xa3,0xab,0x68,0xc3,0x15,0x6b,0xc2,0x2c, +0x6b,0xa4,0x17,0x6c,0xa4,0xba,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,6,0x6e,0x1a,0x6e,0x34,0x6f, +0x38,0x70,0x3e,0x74,0x11,0x68,0x69,0xa3,0x78,0x11,0x64,0x61,0x4b,0x11,0x72,0x65, +0xa3,0x77,0x11,0x65,0x6c,0xa3,0x8a,0x61,0x32,0x68,0xa2,0x44,0x69,0x11,0x74,0x73, +0xa3,0xbf,5,0x74,0x23,0x74,0x34,0x77,0x56,0x79,0x13,0x61,0x68,0x6c,0x69,0xa3, +0x4f,0x14,0x61,0x6b,0x61,0x6e,0x61,0x4c,0x19,0x6f,0x72,0x68,0x69,0x72,0x61,0x67, +0x61,0x6e,0x61,0x8d,0x10,0x69,0xa3,0xc6,0x69,0x38,0x6c,0x40,0x6e,1,0x61,0x4d, +0x6e,0x12,0x61,0x64,0x61,0x4b,0x12,0x74,0x68,0x69,0xa3,0x78,0x10,0x69,0xa3,0x4f, +4,0x61,0x40,0x69,0x52,0x6d,0x70,0x6f,0x7c,0x75,0x15,0x64,0x61,0x77,0x61,0x64, +0x69,0xa3,0x91,0x10,0x72,0x92,0x15,0x6f,0x73,0x68,0x74,0x68,0x69,0x93,0x1d,0x74, +0x61,0x6e,0x73,0x6d,0x61,0x6c,0x6c,0x73,0x63,0x72,0x69,0x70,0x74,0xa3,0xbf,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, +0x95,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,0x1e,0x6c,0x34,0x6d,0x3a,0x72,0x48,0x75,0x11, +0x6e,0x67,0xa3,0x4c,0x11,0x75,0x77,0xa3,0x9c,0x10,0x6e,1,0x67,0xa3,0x4b,0x70, +0xa3,0xba,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,0xc1,0xd,0x64,0xa2,0x7a,0x65,0xa2,0xc1,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,4,0x65,0x46,0x69,0x6c,0x6f,0x8c,0x73,0x9a,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,1,0x61,0x36, +0x76,0x16,0x65,0x73,0x61,0x6b,0x75,0x72,0x75,0xa3,0xbe,0x10,0x6b,0xa3,0xbe,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,1,0x62,0x36,0x79,0x10,0x6d, +0xa2,0xb9,0x12,0x61,0x69,0x63,0xa3,0xb9,0x10,0x61,0xa2,0x88,0x12,0x73,0x61,0x6e, +0xa3,0x88,0x61,0xa2,0xc9,0x62,0xa4,0x2e,0x63,6,0x6f,0x52,0x6f,0x76,0x70,0x92, +0x75,0xa2,0x41,0x79,1,0x70,0x3e,0x72,2,0x69,0x2a,0x6c,0x31,0x73,0xa3,0x44, +0x13,0x6c,0x6c,0x69,0x63,0x31,0x10,0x72,1,0x69,0x34,0x6f,0x15,0x6d,0x69,0x6e, +0x6f,0x61,0x6e,0xa3,0xc1,0x11,0x6f,0x74,0x7f,1,0x6d,0x30,0x70,0x10,0x74,0x2e, +0x11,0x69,0x63,0x2f,0x12,0x6d,0x6f,0x6e,0x21,1,0x6d,0x28,0x72,0x10,0x74,0x7f, +0x10,0x6e,0xa3,0xc1,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,3,0x61,0x32,0x65,0x44,0x6f,0x52,0x72,0x10,0x73,0xa3,0xbd, +1,0x6b,0x26,0x6d,0xa3,0x42,0x11,0x6d,0x61,0xa3,0x76,0x10,0x72,0x2c,0x13,0x6f, +0x6b,0x65,0x65,0x2d,0x16,0x72,0x61,0x73,0x6d,0x69,0x61,0x6e,0xa3,0xbd,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,6,0x6f,0x65,0x6f,0x4a,0x72,0x5c,0x74, +0x64,0x76,0x1d,0x69,0x73,0x75,0x61,0x6c,0x6f,0x72,0x64,0x65,0x72,0x6c,0x65,0x66, +0x74,0x3d,0x18,0x76,0x65,0x72,0x73,0x74,0x72,0x75,0x63,0x6b,0x2d,0x13,0x69,0x67, +0x68,0x74,0x2f,0x11,0x6f,0x70,0x30,0x12,0x61,0x6e,0x64,2,0x62,0x32,0x6c,0x62, +0x72,0x13,0x69,0x67,0x68,0x74,0x3b,0x14,0x6f,0x74,0x74,0x6f,0x6d,0x32,0x12,0x61, +0x6e,0x64,1,0x6c,0x2e,0x72,0x13,0x69,0x67,0x68,0x74,0x35,0x12,0x65,0x66,0x74, +0x3f,0x12,0x65,0x66,0x74,0x36,0x17,0x61,0x6e,0x64,0x72,0x69,0x67,0x68,0x74,0x39, +0x62,0x2c,0x6c,0x5c,0x6e,0x10,0x61,0x21,0x14,0x6f,0x74,0x74,0x6f,0x6d,0x22,0x12, +0x61,0x6e,0x64,1,0x6c,0x2e,0x72,0x13,0x69,0x67,0x68,0x74,0x27,0x12,0x65,0x66, +0x74,0x25,0x12,0x65,0x66,0x74,0x28,0x17,0x61,0x6e,0x64,0x72,0x69,0x67,0x68,0x74, +0x2b,0xd,0x6e,0xaa,0x72,0x70,0x72,0x92,0x73,0xa2,0x46,0x74,0xa2,0x54,0x76,1, +0x69,0x60,0x6f,0x12,0x77,0x65,0x6c,0x62,1,0x64,0x3a,0x69,0x19,0x6e,0x64,0x65, +0x70,0x65,0x6e,0x64,0x65,0x6e,0x74,0x67,0x17,0x65,0x70,0x65,0x6e,0x64,0x65,0x6e, +0x74,0x65,1,0x72,0x2e,0x73,0x13,0x61,0x72,0x67,0x61,0x61,0x12,0x61,0x6d,0x61, +0x5f,0x1d,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x73,0x68,0x69,0x66,0x74,0x65,0x72, +0x57,0x1e,0x79,0x6c,0x6c,0x61,0x62,0x6c,0x65,0x6d,0x6f,0x64,0x69,0x66,0x69,0x65, +0x72,0x59,0x12,0x6f,0x6e,0x65,1,0x6c,0x2c,0x6d,0x12,0x61,0x72,0x6b,0x5d,0x14, +0x65,0x74,0x74,0x65,0x72,0x5b,0x6e,0x3c,0x6f,0x7c,0x70,0x18,0x75,0x72,0x65,0x6b, +0x69,0x6c,0x6c,0x65,0x72,0x55,1,0x6f,0x4c,0x75,1,0x6b,0x3c,0x6d,0x12,0x62, +0x65,0x72,0x50,0x15,0x6a,0x6f,0x69,0x6e,0x65,0x72,0x53,0x11,0x74,0x61,0x4f,0x16, +0x6e,0x6a,0x6f,0x69,0x6e,0x65,0x72,0x4d,0x13,0x74,0x68,0x65,0x72,0x21,0x67,0x3e, +0x67,0x4a,0x69,0x64,0x6a,0x82,0x6d,0x1d,0x6f,0x64,0x69,0x66,0x79,0x69,0x6e,0x67, +0x6c,0x65,0x74,0x74,0x65,0x72,0x4b,0x1c,0x65,0x6d,0x69,0x6e,0x61,0x74,0x69,0x6f, +0x6e,0x6d,0x61,0x72,0x6b,0x45,0x1e,0x6e,0x76,0x69,0x73,0x69,0x62,0x6c,0x65,0x73, +0x74,0x61,0x63,0x6b,0x65,0x72,0x47,0x14,0x6f,0x69,0x6e,0x65,0x72,0x49,0x61,0xa2, +0xba,0x62,0xa2,0xc0,0x63,1,0x61,0xa2,0xa2,0x6f,0x16,0x6e,0x73,0x6f,0x6e,0x61, +0x6e,0x74,0x2a,8,0x6b,0x67,0x6b,0x48,0x6d,0x52,0x70,0x5c,0x73,0xa2,0x42,0x77, +0x19,0x69,0x74,0x68,0x73,0x74,0x61,0x63,0x6b,0x65,0x72,0x43,0x14,0x69,0x6c,0x6c, +0x65,0x72,0x35,0x14,0x65,0x64,0x69,0x61,0x6c,0x37,1,0x6c,0x52,0x72,0x10,0x65, +1,0x63,0x2e,0x66,0x13,0x69,0x78,0x65,0x64,0x3d,0x19,0x65,0x64,0x69,0x6e,0x67, +0x72,0x65,0x70,0x68,0x61,0x3b,0x18,0x61,0x63,0x65,0x68,0x6f,0x6c,0x64,0x65,0x72, +0x39,0x10,0x75,1,0x62,0x3e,0x63,0x1b,0x63,0x65,0x65,0x64,0x69,0x6e,0x67,0x72, +0x65,0x70,0x68,0x61,0x41,0x15,0x6a,0x6f,0x69,0x6e,0x65,0x64,0x3f,0x64,0x4c,0x66, +0x52,0x68,0x5a,0x69,0x1e,0x6e,0x69,0x74,0x69,0x61,0x6c,0x70,0x6f,0x73,0x74,0x66, +0x69,0x78,0x65,0x64,0x33,0x12,0x65,0x61,0x64,0x2d,0x13,0x69,0x6e,0x61,0x6c,0x2f, +0x18,0x65,0x61,0x64,0x6c,0x65,0x74,0x74,0x65,0x72,0x31,0x1d,0x6e,0x74,0x69,0x6c, +0x6c,0x61,0x74,0x69,0x6f,0x6e,0x6d,0x61,0x72,0x6b,0x29,0x16,0x76,0x61,0x67,0x72, +0x61,0x68,0x61,0x23,1,0x69,0x4a,0x72,0x10,0x61,0x1f,0x68,0x6d,0x69,0x6a,0x6f, +0x69,0x6e,0x69,0x6e,0x67,0x6e,0x75,0x6d,0x62,0x65,0x72,0x27,0x12,0x6e,0x64,0x75, +0x25,2,0x72,0x38,0x74,0x46,0x75,0x26,0x15,0x70,0x72,0x69,0x67,0x68,0x74,0x27, +0x20,0x15,0x6f,0x74,0x61,0x74,0x65,0x64,0x21,1,0x72,0x24,0x75,0x25,0x22,0x18, +0x61,0x6e,0x73,0x66,0x6f,0x72,0x6d,0x65,0x64,1,0x72,0x32,0x75,0x15,0x70,0x72, +0x69,0x67,0x68,0x74,0x25,0x15,0x6f,0x74,0x61,0x74,0x65,0x64,0x23,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[23100]={ +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, +2,'O','V',0,'O','v','e','r','l','a','y',0,2,'H','A','N','R',0,'H','a','n','_','R','e','a','d','i','n','g',0, +2,'N','K',0,'N','u','k','t','a',0,2,'K','V',0,'K','a','n','a','_','V','o','i','c','i','n','g',0, +2,'V','R',0,'V','i','r','a','m','a',0,2,'C','C','C','1','0',0,'C','C','C','1','0',0, +2,'C','C','C','1','1',0,'C','C','C','1','1',0,2,'C','C','C','1','2',0,'C','C','C','1','2',0, +2,'C','C','C','1','3',0,'C','C','C','1','3',0,2,'C','C','C','1','4',0,'C','C','C','1','4',0, +2,'C','C','C','1','5',0,'C','C','C','1','5',0,2,'C','C','C','1','6',0,'C','C','C','1','6',0, +2,'C','C','C','1','7',0,'C','C','C','1','7',0,2,'C','C','C','1','8',0,'C','C','C','1','8',0, +2,'C','C','C','1','9',0,'C','C','C','1','9',0,2,'C','C','C','2','0',0,'C','C','C','2','0',0, +2,'C','C','C','2','1',0,'C','C','C','2','1',0,2,'C','C','C','2','2',0,'C','C','C','2','2',0, +2,'C','C','C','2','3',0,'C','C','C','2','3',0,2,'C','C','C','2','4',0,'C','C','C','2','4',0, +2,'C','C','C','2','5',0,'C','C','C','2','5',0,2,'C','C','C','2','6',0,'C','C','C','2','6',0, +2,'C','C','C','2','7',0,'C','C','C','2','7',0,2,'C','C','C','2','8',0,'C','C','C','2','8',0, +2,'C','C','C','2','9',0,'C','C','C','2','9',0,2,'C','C','C','3','0',0,'C','C','C','3','0',0, +2,'C','C','C','3','1',0,'C','C','C','3','1',0,2,'C','C','C','3','2',0,'C','C','C','3','2',0, +2,'C','C','C','3','3',0,'C','C','C','3','3',0,2,'C','C','C','3','4',0,'C','C','C','3','4',0, +2,'C','C','C','3','5',0,'C','C','C','3','5',0,2,'C','C','C','3','6',0,'C','C','C','3','6',0, +2,'C','C','C','8','4',0,'C','C','C','8','4',0,2,'C','C','C','9','1',0,'C','C','C','9','1',0, +2,'C','C','C','1','0','3',0,'C','C','C','1','0','3',0,2,'C','C','C','1','0','7',0,'C','C','C','1','0','7',0, +2,'C','C','C','1','1','8',0,'C','C','C','1','1','8',0,2,'C','C','C','1','2','2',0,'C','C','C','1','2','2',0, +2,'C','C','C','1','2','9',0,'C','C','C','1','2','9',0,2,'C','C','C','1','3','0',0,'C','C','C','1','3','0',0, +2,'C','C','C','1','3','2',0,'C','C','C','1','3','2',0,2,'C','C','C','1','3','3',0,'C','C','C','1','3','3',0, +2,'A','T','B','L',0,'A','t','t','a','c','h','e','d','_','B','e','l','o','w','_','L','e','f','t',0, +2,'A','T','B',0,'A','t','t','a','c','h','e','d','_','B','e','l','o','w',0, +2,'A','T','A',0,'A','t','t','a','c','h','e','d','_','A','b','o','v','e',0, +2,'A','T','A','R',0,'A','t','t','a','c','h','e','d','_','A','b','o','v','e','_','R','i','g','h','t',0, +2,'B','L',0,'B','e','l','o','w','_','L','e','f','t',0,2,'B',0,'B','e','l','o','w',0, +2,'B','R',0,'B','e','l','o','w','_','R','i','g','h','t',0, +2,'L',0,'L','e','f','t',0,2,'R',0,'R','i','g','h','t',0, +2,'A','L',0,'A','b','o','v','e','_','L','e','f','t',0,2,'A',0,'A','b','o','v','e',0, +2,'A','R',0,'A','b','o','v','e','_','R','i','g','h','t',0, +2,'D','B',0,'D','o','u','b','l','e','_','B','e','l','o','w',0, +2,'D','A',0,'D','o','u','b','l','e','_','A','b','o','v','e',0, +2,'I','S',0,'I','o','t','a','_','S','u','b','s','c','r','i','p','t',0, +2,'A','H','e','x',0,'A','S','C','I','I','_','H','e','x','_','D','i','g','i','t',0, +2,'B','i','d','i','_','C',0,'B','i','d','i','_','C','o','n','t','r','o','l',0, +2,'B','i','d','i','_','M',0,'B','i','d','i','_','M','i','r','r','o','r','e','d',0, +2,'D','a','s','h',0,'D','a','s','h',0,2,'D','I',0,'D','e','f','a','u','l','t','_','I','g','n','o','r','a','b','l','e', +'_','C','o','d','e','_','P','o','i','n','t',0,2,'D','e','p',0,'D','e','p','r','e','c','a','t','e','d',0, +2,'D','i','a',0,'D','i','a','c','r','i','t','i','c',0,2,'E','x','t',0,'E','x','t','e','n','d','e','r',0, +2,'C','o','m','p','_','E','x',0,'F','u','l','l','_','C','o','m','p','o','s','i','t','i','o','n','_','E','x','c','l','u','s', +'i','o','n',0,2,'G','r','_','B','a','s','e',0,'G','r','a','p','h','e','m','e','_','B','a','s','e',0, +2,'G','r','_','E','x','t',0,'G','r','a','p','h','e','m','e','_','E','x','t','e','n','d',0, +2,'G','r','_','L','i','n','k',0,'G','r','a','p','h','e','m','e','_','L','i','n','k',0, +2,'H','e','x',0,'H','e','x','_','D','i','g','i','t',0,2,'H','y','p','h','e','n',0,'H','y','p','h','e','n',0, +2,'I','D','C',0,'I','D','_','C','o','n','t','i','n','u','e',0, +2,'I','D','S',0,'I','D','_','S','t','a','r','t',0,2,'I','d','e','o',0,'I','d','e','o','g','r','a','p','h','i','c',0, +2,'I','D','S','B',0,'I','D','S','_','B','i','n','a','r','y','_','O','p','e','r','a','t','o','r',0, +2,'I','D','S','T',0,'I','D','S','_','T','r','i','n','a','r','y','_','O','p','e','r','a','t','o','r',0, +2,'J','o','i','n','_','C',0,'J','o','i','n','_','C','o','n','t','r','o','l',0, +2,'L','O','E',0,'L','o','g','i','c','a','l','_','O','r','d','e','r','_','E','x','c','e','p','t','i','o','n',0, +2,'L','o','w','e','r',0,'L','o','w','e','r','c','a','s','e',0, +2,'M','a','t','h',0,'M','a','t','h',0,2,'N','C','h','a','r',0,'N','o','n','c','h','a','r','a','c','t','e','r','_','C', +'o','d','e','_','P','o','i','n','t',0,2,'Q','M','a','r','k',0,'Q','u','o','t','a','t','i','o','n','_','M','a','r','k',0, +2,'R','a','d','i','c','a','l',0,'R','a','d','i','c','a','l',0, +2,'S','D',0,'S','o','f','t','_','D','o','t','t','e','d',0, +2,'T','e','r','m',0,'T','e','r','m','i','n','a','l','_','P','u','n','c','t','u','a','t','i','o','n',0, +2,'U','I','d','e','o',0,'U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h',0, +2,'U','p','p','e','r',0,'U','p','p','e','r','c','a','s','e',0, +3,'W','S','p','a','c','e',0,'W','h','i','t','e','_','S','p','a','c','e',0,'s','p','a','c','e',0, +2,'X','I','D','C',0,'X','I','D','_','C','o','n','t','i','n','u','e',0, +2,'X','I','D','S',0,'X','I','D','_','S','t','a','r','t',0, +2,'S','e','n','s','i','t','i','v','e',0,'C','a','s','e','_','S','e','n','s','i','t','i','v','e',0, +2,'S','T','e','r','m',0,'S','e','n','t','e','n','c','e','_','T','e','r','m','i','n','a','l',0, +2,'V','S',0,'V','a','r','i','a','t','i','o','n','_','S','e','l','e','c','t','o','r',0, +2,'n','f','d','i','n','e','r','t',0,'N','F','D','_','I','n','e','r','t',0, +2,'n','f','k','d','i','n','e','r','t',0,'N','F','K','D','_','I','n','e','r','t',0, +2,'n','f','c','i','n','e','r','t',0,'N','F','C','_','I','n','e','r','t',0, +2,'n','f','k','c','i','n','e','r','t',0,'N','F','K','C','_','I','n','e','r','t',0, +2,'s','e','g','s','t','a','r','t',0,'S','e','g','m','e','n','t','_','S','t','a','r','t','e','r',0, +2,'P','a','t','_','S','y','n',0,'P','a','t','t','e','r','n','_','S','y','n','t','a','x',0, +2,'P','a','t','_','W','S',0,'P','a','t','t','e','r','n','_','W','h','i','t','e','_','S','p','a','c','e',0, +2,0,'a','l','n','u','m',0,2,0,'b','l','a','n','k',0, +2,0,'g','r','a','p','h',0,2,0,'p','r','i','n','t',0, +2,0,'x','d','i','g','i','t',0,2,'C','a','s','e','d',0,'C','a','s','e','d',0, +2,'C','I',0,'C','a','s','e','_','I','g','n','o','r','a','b','l','e',0, +2,'C','W','L',0,'C','h','a','n','g','e','s','_','W','h','e','n','_','L','o','w','e','r','c','a','s','e','d',0, +2,'C','W','U',0,'C','h','a','n','g','e','s','_','W','h','e','n','_','U','p','p','e','r','c','a','s','e','d',0, +2,'C','W','T',0,'C','h','a','n','g','e','s','_','W','h','e','n','_','T','i','t','l','e','c','a','s','e','d',0, +2,'C','W','C','F',0,'C','h','a','n','g','e','s','_','W','h','e','n','_','C','a','s','e','f','o','l','d','e','d',0, +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','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,'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','a','s','i','c','_','E','m','o','j','i',0,'B','a','s','i','c','_','E','m','o','j','i',0, +2,'E','m','o','j','i','_','K','e','y','c','a','p','_','S','e','q','u','e','n','c','e',0,'E','m','o','j','i','_','K','e','y', +'c','a','p','_','S','e','q','u','e','n','c','e',0,2,'R','G','I','_','E','m','o','j','i','_','M','o','d','i','f','i','e','r', +'_','S','e','q','u','e','n','c','e',0,'R','G','I','_','E','m','o','j','i','_','M','o','d','i','f','i','e','r','_','S','e','q', +'u','e','n','c','e',0,2,'R','G','I','_','E','m','o','j','i','_','F','l','a','g','_','S','e','q','u','e','n','c','e',0, +'R','G','I','_','E','m','o','j','i','_','F','l','a','g','_','S','e','q','u','e','n','c','e',0, +2,'R','G','I','_','E','m','o','j','i','_','T','a','g','_','S','e','q','u','e','n','c','e',0, +'R','G','I','_','E','m','o','j','i','_','T','a','g','_','S','e','q','u','e','n','c','e',0, +2,'R','G','I','_','E','m','o','j','i','_','Z','W','J','_','S','e','q','u','e','n','c','e',0, +'R','G','I','_','E','m','o','j','i','_','Z','W','J','_','S','e','q','u','e','n','c','e',0, +2,'R','G','I','_','E','m','o','j','i',0,'R','G','I','_','E','m','o','j','i',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, +2,'E','S',0,'E','u','r','o','p','e','a','n','_','S','e','p','a','r','a','t','o','r',0, +2,'E','T',0,'E','u','r','o','p','e','a','n','_','T','e','r','m','i','n','a','t','o','r',0, +2,'A','N',0,'A','r','a','b','i','c','_','N','u','m','b','e','r',0, +2,'C','S',0,'C','o','m','m','o','n','_','S','e','p','a','r','a','t','o','r',0, +2,'B',0,'P','a','r','a','g','r','a','p','h','_','S','e','p','a','r','a','t','o','r',0, +2,'S',0,'S','e','g','m','e','n','t','_','S','e','p','a','r','a','t','o','r',0, +2,'W','S',0,'W','h','i','t','e','_','S','p','a','c','e',0, +2,'O','N',0,'O','t','h','e','r','_','N','e','u','t','r','a','l',0, +2,'L','R','E',0,'L','e','f','t','_','T','o','_','R','i','g','h','t','_','E','m','b','e','d','d','i','n','g',0, +2,'L','R','O',0,'L','e','f','t','_','T','o','_','R','i','g','h','t','_','O','v','e','r','r','i','d','e',0, +2,'A','L',0,'A','r','a','b','i','c','_','L','e','t','t','e','r',0, +2,'R','L','E',0,'R','i','g','h','t','_','T','o','_','L','e','f','t','_','E','m','b','e','d','d','i','n','g',0, +2,'R','L','O',0,'R','i','g','h','t','_','T','o','_','L','e','f','t','_','O','v','e','r','r','i','d','e',0, +2,'P','D','F',0,'P','o','p','_','D','i','r','e','c','t','i','o','n','a','l','_','F','o','r','m','a','t',0, +2,'N','S','M',0,'N','o','n','s','p','a','c','i','n','g','_','M','a','r','k',0, +2,'B','N',0,'B','o','u','n','d','a','r','y','_','N','e','u','t','r','a','l',0, +2,'F','S','I',0,'F','i','r','s','t','_','S','t','r','o','n','g','_','I','s','o','l','a','t','e',0, +2,'L','R','I',0,'L','e','f','t','_','T','o','_','R','i','g','h','t','_','I','s','o','l','a','t','e',0, +2,'R','L','I',0,'R','i','g','h','t','_','T','o','_','L','e','f','t','_','I','s','o','l','a','t','e',0, +2,'P','D','I',0,'P','o','p','_','D','i','r','e','c','t','i','o','n','a','l','_','I','s','o','l','a','t','e',0, +2,'b','l','k',0,'B','l','o','c','k',0,2,'N','B',0,'N','o','_','B','l','o','c','k',0, +2,'A','S','C','I','I',0,'B','a','s','i','c','_','L','a','t','i','n',0, +3,'L','a','t','i','n','_','1','_','S','u','p',0,'L','a','t','i','n','_','1','_','S','u','p','p','l','e','m','e','n','t',0, +'L','a','t','i','n','_','1',0,2,'L','a','t','i','n','_','E','x','t','_','A',0,'L','a','t','i','n','_','E','x','t','e','n', +'d','e','d','_','A',0,2,'L','a','t','i','n','_','E','x','t','_','B',0,'L','a','t','i','n','_','E','x','t','e','n','d','e', +'d','_','B',0,2,'I','P','A','_','E','x','t',0,'I','P','A','_','E','x','t','e','n','s','i','o','n','s',0, +2,'M','o','d','i','f','i','e','r','_','L','e','t','t','e','r','s',0,'S','p','a','c','i','n','g','_','M','o','d','i','f','i', +'e','r','_','L','e','t','t','e','r','s',0,2,'D','i','a','c','r','i','t','i','c','a','l','s',0, +'C','o','m','b','i','n','i','n','g','_','D','i','a','c','r','i','t','i','c','a','l','_','M','a','r','k','s',0, +2,'G','r','e','e','k',0,'G','r','e','e','k','_','A','n','d','_','C','o','p','t','i','c',0, +2,'C','y','r','i','l','l','i','c',0,'C','y','r','i','l','l','i','c',0, +2,'A','r','m','e','n','i','a','n',0,'A','r','m','e','n','i','a','n',0, +2,'H','e','b','r','e','w',0,'H','e','b','r','e','w',0,2,'A','r','a','b','i','c',0,'A','r','a','b','i','c',0, +2,'S','y','r','i','a','c',0,'S','y','r','i','a','c',0,2,'T','h','a','a','n','a',0,'T','h','a','a','n','a',0, +2,'D','e','v','a','n','a','g','a','r','i',0,'D','e','v','a','n','a','g','a','r','i',0, +2,'B','e','n','g','a','l','i',0,'B','e','n','g','a','l','i',0, +2,'G','u','r','m','u','k','h','i',0,'G','u','r','m','u','k','h','i',0, +2,'G','u','j','a','r','a','t','i',0,'G','u','j','a','r','a','t','i',0, +2,'O','r','i','y','a',0,'O','r','i','y','a',0,2,'T','a','m','i','l',0,'T','a','m','i','l',0, +2,'T','e','l','u','g','u',0,'T','e','l','u','g','u',0,2,'K','a','n','n','a','d','a',0, +'K','a','n','n','a','d','a',0,2,'M','a','l','a','y','a','l','a','m',0,'M','a','l','a','y','a','l','a','m',0, +2,'S','i','n','h','a','l','a',0,'S','i','n','h','a','l','a',0, +2,'T','h','a','i',0,'T','h','a','i',0,2,'L','a','o',0,'L','a','o',0, +2,'T','i','b','e','t','a','n',0,'T','i','b','e','t','a','n',0, +2,'M','y','a','n','m','a','r',0,'M','y','a','n','m','a','r',0, +2,'G','e','o','r','g','i','a','n',0,'G','e','o','r','g','i','a','n',0, +2,'J','a','m','o',0,'H','a','n','g','u','l','_','J','a','m','o',0, +2,'E','t','h','i','o','p','i','c',0,'E','t','h','i','o','p','i','c',0, +2,'C','h','e','r','o','k','e','e',0,'C','h','e','r','o','k','e','e',0, +3,'U','C','A','S',0,'U','n','i','f','i','e','d','_','C','a','n','a','d','i','a','n','_','A','b','o','r','i','g','i','n','a', +'l','_','S','y','l','l','a','b','i','c','s',0,'C','a','n','a','d','i','a','n','_','S','y','l','l','a','b','i','c','s',0, +2,'O','g','h','a','m',0,'O','g','h','a','m',0,2,'R','u','n','i','c',0,'R','u','n','i','c',0, +2,'K','h','m','e','r',0,'K','h','m','e','r',0,2,'M','o','n','g','o','l','i','a','n',0, +'M','o','n','g','o','l','i','a','n',0,2,'L','a','t','i','n','_','E','x','t','_','A','d','d','i','t','i','o','n','a','l',0, +'L','a','t','i','n','_','E','x','t','e','n','d','e','d','_','A','d','d','i','t','i','o','n','a','l',0, +2,'G','r','e','e','k','_','E','x','t',0,'G','r','e','e','k','_','E','x','t','e','n','d','e','d',0, +2,'P','u','n','c','t','u','a','t','i','o','n',0,'G','e','n','e','r','a','l','_','P','u','n','c','t','u','a','t','i','o','n', +0,2,'S','u','p','e','r','_','A','n','d','_','S','u','b',0,'S','u','p','e','r','s','c','r','i','p','t','s','_','A','n','d', +'_','S','u','b','s','c','r','i','p','t','s',0,2,'C','u','r','r','e','n','c','y','_','S','y','m','b','o','l','s',0, +'C','u','r','r','e','n','c','y','_','S','y','m','b','o','l','s',0, +3,'D','i','a','c','r','i','t','i','c','a','l','s','_','F','o','r','_','S','y','m','b','o','l','s',0, +'C','o','m','b','i','n','i','n','g','_','D','i','a','c','r','i','t','i','c','a','l','_','M','a','r','k','s','_','F','o','r','_', +'S','y','m','b','o','l','s',0,'C','o','m','b','i','n','i','n','g','_','M','a','r','k','s','_','F','o','r','_','S','y','m','b', +'o','l','s',0,2,'L','e','t','t','e','r','l','i','k','e','_','S','y','m','b','o','l','s',0, +'L','e','t','t','e','r','l','i','k','e','_','S','y','m','b','o','l','s',0, +2,'N','u','m','b','e','r','_','F','o','r','m','s',0,'N','u','m','b','e','r','_','F','o','r','m','s',0, +2,'A','r','r','o','w','s',0,'A','r','r','o','w','s',0,2,'M','a','t','h','_','O','p','e','r','a','t','o','r','s',0, +'M','a','t','h','e','m','a','t','i','c','a','l','_','O','p','e','r','a','t','o','r','s',0, +2,'M','i','s','c','_','T','e','c','h','n','i','c','a','l',0,'M','i','s','c','e','l','l','a','n','e','o','u','s','_','T','e', +'c','h','n','i','c','a','l',0,2,'C','o','n','t','r','o','l','_','P','i','c','t','u','r','e','s',0, +'C','o','n','t','r','o','l','_','P','i','c','t','u','r','e','s',0, +2,'O','C','R',0,'O','p','t','i','c','a','l','_','C','h','a','r','a','c','t','e','r','_','R','e','c','o','g','n','i','t','i', +'o','n',0,2,'E','n','c','l','o','s','e','d','_','A','l','p','h','a','n','u','m',0,'E','n','c','l','o','s','e','d','_','A', +'l','p','h','a','n','u','m','e','r','i','c','s',0,2,'B','o','x','_','D','r','a','w','i','n','g',0, +'B','o','x','_','D','r','a','w','i','n','g',0,2,'B','l','o','c','k','_','E','l','e','m','e','n','t','s',0, +'B','l','o','c','k','_','E','l','e','m','e','n','t','s',0,2,'G','e','o','m','e','t','r','i','c','_','S','h','a','p','e','s', +0,'G','e','o','m','e','t','r','i','c','_','S','h','a','p','e','s',0, +2,'M','i','s','c','_','S','y','m','b','o','l','s',0,'M','i','s','c','e','l','l','a','n','e','o','u','s','_','S','y','m','b', +'o','l','s',0,2,'D','i','n','g','b','a','t','s',0,'D','i','n','g','b','a','t','s',0, +2,'B','r','a','i','l','l','e',0,'B','r','a','i','l','l','e','_','P','a','t','t','e','r','n','s',0, +2,'C','J','K','_','R','a','d','i','c','a','l','s','_','S','u','p',0,'C','J','K','_','R','a','d','i','c','a','l','s','_','S', +'u','p','p','l','e','m','e','n','t',0,2,'K','a','n','g','x','i',0,'K','a','n','g','x','i','_','R','a','d','i','c','a','l', +'s',0,2,'I','D','C',0,'I','d','e','o','g','r','a','p','h','i','c','_','D','e','s','c','r','i','p','t','i','o','n','_','C', +'h','a','r','a','c','t','e','r','s',0,2,'C','J','K','_','S','y','m','b','o','l','s',0,'C','J','K','_','S','y','m','b','o', +'l','s','_','A','n','d','_','P','u','n','c','t','u','a','t','i','o','n',0, +2,'H','i','r','a','g','a','n','a',0,'H','i','r','a','g','a','n','a',0, +2,'K','a','t','a','k','a','n','a',0,'K','a','t','a','k','a','n','a',0, +2,'B','o','p','o','m','o','f','o',0,'B','o','p','o','m','o','f','o',0, +2,'C','o','m','p','a','t','_','J','a','m','o',0,'H','a','n','g','u','l','_','C','o','m','p','a','t','i','b','i','l','i','t', +'y','_','J','a','m','o',0,2,'K','a','n','b','u','n',0,'K','a','n','b','u','n',0, +2,'B','o','p','o','m','o','f','o','_','E','x','t',0,'B','o','p','o','m','o','f','o','_','E','x','t','e','n','d','e','d',0, +2,'E','n','c','l','o','s','e','d','_','C','J','K',0,'E','n','c','l','o','s','e','d','_','C','J','K','_','L','e','t','t','e', +'r','s','_','A','n','d','_','M','o','n','t','h','s',0,2,'C','J','K','_','C','o','m','p','a','t',0, +'C','J','K','_','C','o','m','p','a','t','i','b','i','l','i','t','y',0, +2,'C','J','K','_','E','x','t','_','A',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h', +'s','_','E','x','t','e','n','s','i','o','n','_','A',0,2,'C','J','K',0,'C','J','K','_','U','n','i','f','i','e','d','_','I', +'d','e','o','g','r','a','p','h','s',0,2,'Y','i','_','S','y','l','l','a','b','l','e','s',0, +'Y','i','_','S','y','l','l','a','b','l','e','s',0,2,'Y','i','_','R','a','d','i','c','a','l','s',0, +'Y','i','_','R','a','d','i','c','a','l','s',0,2,'H','a','n','g','u','l',0,'H','a','n','g','u','l','_','S','y','l','l','a', +'b','l','e','s',0,2,'H','i','g','h','_','S','u','r','r','o','g','a','t','e','s',0,'H','i','g','h','_','S','u','r','r','o', +'g','a','t','e','s',0,2,'H','i','g','h','_','P','U','_','S','u','r','r','o','g','a','t','e','s',0, +'H','i','g','h','_','P','r','i','v','a','t','e','_','U','s','e','_','S','u','r','r','o','g','a','t','e','s',0, +2,'L','o','w','_','S','u','r','r','o','g','a','t','e','s',0,'L','o','w','_','S','u','r','r','o','g','a','t','e','s',0, +3,'P','U','A',0,'P','r','i','v','a','t','e','_','U','s','e','_','A','r','e','a',0,'P','r','i','v','a','t','e','_','U','s', +'e',0,2,'C','J','K','_','C','o','m','p','a','t','_','I','d','e','o','g','r','a','p','h','s',0, +'C','J','K','_','C','o','m','p','a','t','i','b','i','l','i','t','y','_','I','d','e','o','g','r','a','p','h','s',0, +2,'A','l','p','h','a','b','e','t','i','c','_','P','F',0,'A','l','p','h','a','b','e','t','i','c','_','P','r','e','s','e','n', +'t','a','t','i','o','n','_','F','o','r','m','s',0,3,'A','r','a','b','i','c','_','P','F','_','A',0, +'A','r','a','b','i','c','_','P','r','e','s','e','n','t','a','t','i','o','n','_','F','o','r','m','s','_','A',0, +'A','r','a','b','i','c','_','P','r','e','s','e','n','t','a','t','i','o','n','_','F','o','r','m','s','-','A',0, +2,'H','a','l','f','_','M','a','r','k','s',0,'C','o','m','b','i','n','i','n','g','_','H','a','l','f','_','M','a','r','k','s', +0,2,'C','J','K','_','C','o','m','p','a','t','_','F','o','r','m','s',0,'C','J','K','_','C','o','m','p','a','t','i','b','i', +'l','i','t','y','_','F','o','r','m','s',0,2,'S','m','a','l','l','_','F','o','r','m','s',0, +'S','m','a','l','l','_','F','o','r','m','_','V','a','r','i','a','n','t','s',0, +2,'A','r','a','b','i','c','_','P','F','_','B',0,'A','r','a','b','i','c','_','P','r','e','s','e','n','t','a','t','i','o','n', +'_','F','o','r','m','s','_','B',0,2,'S','p','e','c','i','a','l','s',0,'S','p','e','c','i','a','l','s',0, +2,'H','a','l','f','_','A','n','d','_','F','u','l','l','_','F','o','r','m','s',0,'H','a','l','f','w','i','d','t','h','_','A', +'n','d','_','F','u','l','l','w','i','d','t','h','_','F','o','r','m','s',0, +2,'O','l','d','_','I','t','a','l','i','c',0,'O','l','d','_','I','t','a','l','i','c',0, +2,'G','o','t','h','i','c',0,'G','o','t','h','i','c',0,2,'D','e','s','e','r','e','t',0, +'D','e','s','e','r','e','t',0,2,'B','y','z','a','n','t','i','n','e','_','M','u','s','i','c',0, +'B','y','z','a','n','t','i','n','e','_','M','u','s','i','c','a','l','_','S','y','m','b','o','l','s',0, +2,'M','u','s','i','c',0,'M','u','s','i','c','a','l','_','S','y','m','b','o','l','s',0, +2,'M','a','t','h','_','A','l','p','h','a','n','u','m',0,'M','a','t','h','e','m','a','t','i','c','a','l','_','A','l','p','h', +'a','n','u','m','e','r','i','c','_','S','y','m','b','o','l','s',0, +2,'C','J','K','_','E','x','t','_','B',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h', +'s','_','E','x','t','e','n','s','i','o','n','_','B',0,2,'C','J','K','_','C','o','m','p','a','t','_','I','d','e','o','g','r', +'a','p','h','s','_','S','u','p',0,'C','J','K','_','C','o','m','p','a','t','i','b','i','l','i','t','y','_','I','d','e','o','g', +'r','a','p','h','s','_','S','u','p','p','l','e','m','e','n','t',0, +2,'T','a','g','s',0,'T','a','g','s',0,3,'C','y','r','i','l','l','i','c','_','S','u','p',0, +'C','y','r','i','l','l','i','c','_','S','u','p','p','l','e','m','e','n','t',0,'C','y','r','i','l','l','i','c','_','S','u','p', +'p','l','e','m','e','n','t','a','r','y',0,2,'T','a','g','a','l','o','g',0,'T','a','g','a','l','o','g',0, +2,'H','a','n','u','n','o','o',0,'H','a','n','u','n','o','o',0, +2,'B','u','h','i','d',0,'B','u','h','i','d',0,2,'T','a','g','b','a','n','w','a',0,'T','a','g','b','a','n','w','a',0, +2,'M','i','s','c','_','M','a','t','h','_','S','y','m','b','o','l','s','_','A',0,'M','i','s','c','e','l','l','a','n','e','o', +'u','s','_','M','a','t','h','e','m','a','t','i','c','a','l','_','S','y','m','b','o','l','s','_','A',0, +2,'S','u','p','_','A','r','r','o','w','s','_','A',0,'S','u','p','p','l','e','m','e','n','t','a','l','_','A','r','r','o','w', +'s','_','A',0,2,'S','u','p','_','A','r','r','o','w','s','_','B',0,'S','u','p','p','l','e','m','e','n','t','a','l','_','A', +'r','r','o','w','s','_','B',0,2,'M','i','s','c','_','M','a','t','h','_','S','y','m','b','o','l','s','_','B',0, +'M','i','s','c','e','l','l','a','n','e','o','u','s','_','M','a','t','h','e','m','a','t','i','c','a','l','_','S','y','m','b','o', +'l','s','_','B',0,2,'S','u','p','_','M','a','t','h','_','O','p','e','r','a','t','o','r','s',0, +'S','u','p','p','l','e','m','e','n','t','a','l','_','M','a','t','h','e','m','a','t','i','c','a','l','_','O','p','e','r','a','t', +'o','r','s',0,2,'K','a','t','a','k','a','n','a','_','E','x','t',0,'K','a','t','a','k','a','n','a','_','P','h','o','n','e', +'t','i','c','_','E','x','t','e','n','s','i','o','n','s',0,2,'V','S',0,'V','a','r','i','a','t','i','o','n','_','S','e','l', +'e','c','t','o','r','s',0,2,'S','u','p','_','P','U','A','_','A',0,'S','u','p','p','l','e','m','e','n','t','a','r','y','_', +'P','r','i','v','a','t','e','_','U','s','e','_','A','r','e','a','_','A',0, +2,'S','u','p','_','P','U','A','_','B',0,'S','u','p','p','l','e','m','e','n','t','a','r','y','_','P','r','i','v','a','t','e', +'_','U','s','e','_','A','r','e','a','_','B',0,2,'L','i','m','b','u',0,'L','i','m','b','u',0, +2,'T','a','i','_','L','e',0,'T','a','i','_','L','e',0,2,'K','h','m','e','r','_','S','y','m','b','o','l','s',0, +'K','h','m','e','r','_','S','y','m','b','o','l','s',0,2,'P','h','o','n','e','t','i','c','_','E','x','t',0, +'P','h','o','n','e','t','i','c','_','E','x','t','e','n','s','i','o','n','s',0, +2,'M','i','s','c','_','A','r','r','o','w','s',0,'M','i','s','c','e','l','l','a','n','e','o','u','s','_','S','y','m','b','o', +'l','s','_','A','n','d','_','A','r','r','o','w','s',0,2,'Y','i','j','i','n','g',0,'Y','i','j','i','n','g','_','H','e','x', +'a','g','r','a','m','_','S','y','m','b','o','l','s',0,2,'L','i','n','e','a','r','_','B','_','S','y','l','l','a','b','a','r', +'y',0,'L','i','n','e','a','r','_','B','_','S','y','l','l','a','b','a','r','y',0, +2,'L','i','n','e','a','r','_','B','_','I','d','e','o','g','r','a','m','s',0,'L','i','n','e','a','r','_','B','_','I','d','e', +'o','g','r','a','m','s',0,2,'A','e','g','e','a','n','_','N','u','m','b','e','r','s',0,'A','e','g','e','a','n','_','N','u', +'m','b','e','r','s',0,2,'U','g','a','r','i','t','i','c',0,'U','g','a','r','i','t','i','c',0, +2,'S','h','a','v','i','a','n',0,'S','h','a','v','i','a','n',0, +2,'O','s','m','a','n','y','a',0,'O','s','m','a','n','y','a',0, +2,'C','y','p','r','i','o','t','_','S','y','l','l','a','b','a','r','y',0,'C','y','p','r','i','o','t','_','S','y','l','l','a', +'b','a','r','y',0,2,'T','a','i','_','X','u','a','n','_','J','i','n','g',0,'T','a','i','_','X','u','a','n','_','J','i','n', +'g','_','S','y','m','b','o','l','s',0,2,'V','S','_','S','u','p',0,'V','a','r','i','a','t','i','o','n','_','S','e','l','e', +'c','t','o','r','s','_','S','u','p','p','l','e','m','e','n','t',0, +2,'A','n','c','i','e','n','t','_','G','r','e','e','k','_','M','u','s','i','c',0,'A','n','c','i','e','n','t','_','G','r','e', +'e','k','_','M','u','s','i','c','a','l','_','N','o','t','a','t','i','o','n',0, +2,'A','n','c','i','e','n','t','_','G','r','e','e','k','_','N','u','m','b','e','r','s',0,'A','n','c','i','e','n','t','_','G', +'r','e','e','k','_','N','u','m','b','e','r','s',0,2,'A','r','a','b','i','c','_','S','u','p',0, +'A','r','a','b','i','c','_','S','u','p','p','l','e','m','e','n','t',0, +2,'B','u','g','i','n','e','s','e',0,'B','u','g','i','n','e','s','e',0, +2,'C','J','K','_','S','t','r','o','k','e','s',0,'C','J','K','_','S','t','r','o','k','e','s',0, +2,'D','i','a','c','r','i','t','i','c','a','l','s','_','S','u','p',0,'C','o','m','b','i','n','i','n','g','_','D','i','a','c', +'r','i','t','i','c','a','l','_','M','a','r','k','s','_','S','u','p','p','l','e','m','e','n','t',0, +2,'C','o','p','t','i','c',0,'C','o','p','t','i','c',0,2,'E','t','h','i','o','p','i','c','_','E','x','t',0, +'E','t','h','i','o','p','i','c','_','E','x','t','e','n','d','e','d',0, +2,'E','t','h','i','o','p','i','c','_','S','u','p',0,'E','t','h','i','o','p','i','c','_','S','u','p','p','l','e','m','e','n', +'t',0,2,'G','e','o','r','g','i','a','n','_','S','u','p',0,'G','e','o','r','g','i','a','n','_','S','u','p','p','l','e','m', +'e','n','t',0,2,'G','l','a','g','o','l','i','t','i','c',0,'G','l','a','g','o','l','i','t','i','c',0, +2,'K','h','a','r','o','s','h','t','h','i',0,'K','h','a','r','o','s','h','t','h','i',0, +2,'M','o','d','i','f','i','e','r','_','T','o','n','e','_','L','e','t','t','e','r','s',0,'M','o','d','i','f','i','e','r','_', +'T','o','n','e','_','L','e','t','t','e','r','s',0,2,'N','e','w','_','T','a','i','_','L','u','e',0, +'N','e','w','_','T','a','i','_','L','u','e',0,2,'O','l','d','_','P','e','r','s','i','a','n',0, +'O','l','d','_','P','e','r','s','i','a','n',0,2,'P','h','o','n','e','t','i','c','_','E','x','t','_','S','u','p',0, +'P','h','o','n','e','t','i','c','_','E','x','t','e','n','s','i','o','n','s','_','S','u','p','p','l','e','m','e','n','t',0, +2,'S','u','p','_','P','u','n','c','t','u','a','t','i','o','n',0,'S','u','p','p','l','e','m','e','n','t','a','l','_','P','u', +'n','c','t','u','a','t','i','o','n',0,2,'S','y','l','o','t','i','_','N','a','g','r','i',0, +'S','y','l','o','t','i','_','N','a','g','r','i',0,2,'T','i','f','i','n','a','g','h',0,'T','i','f','i','n','a','g','h',0, +2,'V','e','r','t','i','c','a','l','_','F','o','r','m','s',0,'V','e','r','t','i','c','a','l','_','F','o','r','m','s',0, +2,'N','K','o',0,'N','K','o',0,2,'B','a','l','i','n','e','s','e',0,'B','a','l','i','n','e','s','e',0, +2,'L','a','t','i','n','_','E','x','t','_','C',0,'L','a','t','i','n','_','E','x','t','e','n','d','e','d','_','C',0, +2,'L','a','t','i','n','_','E','x','t','_','D',0,'L','a','t','i','n','_','E','x','t','e','n','d','e','d','_','D',0, +2,'P','h','a','g','s','_','P','a',0,'P','h','a','g','s','_','P','a',0, +2,'P','h','o','e','n','i','c','i','a','n',0,'P','h','o','e','n','i','c','i','a','n',0, +2,'C','u','n','e','i','f','o','r','m',0,'C','u','n','e','i','f','o','r','m',0, +2,'C','u','n','e','i','f','o','r','m','_','N','u','m','b','e','r','s',0,'C','u','n','e','i','f','o','r','m','_','N','u','m', +'b','e','r','s','_','A','n','d','_','P','u','n','c','t','u','a','t','i','o','n',0, +2,'C','o','u','n','t','i','n','g','_','R','o','d',0,'C','o','u','n','t','i','n','g','_','R','o','d','_','N','u','m','e','r', +'a','l','s',0,2,'S','u','n','d','a','n','e','s','e',0,'S','u','n','d','a','n','e','s','e',0, +2,'L','e','p','c','h','a',0,'L','e','p','c','h','a',0,2,'O','l','_','C','h','i','k','i',0, +'O','l','_','C','h','i','k','i',0,2,'C','y','r','i','l','l','i','c','_','E','x','t','_','A',0, +'C','y','r','i','l','l','i','c','_','E','x','t','e','n','d','e','d','_','A',0, +2,'V','a','i',0,'V','a','i',0,2,'C','y','r','i','l','l','i','c','_','E','x','t','_','B',0, +'C','y','r','i','l','l','i','c','_','E','x','t','e','n','d','e','d','_','B',0, +2,'S','a','u','r','a','s','h','t','r','a',0,'S','a','u','r','a','s','h','t','r','a',0, +2,'K','a','y','a','h','_','L','i',0,'K','a','y','a','h','_','L','i',0, +2,'R','e','j','a','n','g',0,'R','e','j','a','n','g',0,2,'C','h','a','m',0,'C','h','a','m',0, +2,'A','n','c','i','e','n','t','_','S','y','m','b','o','l','s',0,'A','n','c','i','e','n','t','_','S','y','m','b','o','l','s', +0,2,'P','h','a','i','s','t','o','s',0,'P','h','a','i','s','t','o','s','_','D','i','s','c',0, +2,'L','y','c','i','a','n',0,'L','y','c','i','a','n',0,2,'C','a','r','i','a','n',0,'C','a','r','i','a','n',0, +2,'L','y','d','i','a','n',0,'L','y','d','i','a','n',0,2,'M','a','h','j','o','n','g',0, +'M','a','h','j','o','n','g','_','T','i','l','e','s',0,2,'D','o','m','i','n','o',0,'D','o','m','i','n','o','_','T','i','l', +'e','s',0,2,'S','a','m','a','r','i','t','a','n',0,'S','a','m','a','r','i','t','a','n',0, +2,'U','C','A','S','_','E','x','t',0,'U','n','i','f','i','e','d','_','C','a','n','a','d','i','a','n','_','A','b','o','r','i', +'g','i','n','a','l','_','S','y','l','l','a','b','i','c','s','_','E','x','t','e','n','d','e','d',0, +2,'T','a','i','_','T','h','a','m',0,'T','a','i','_','T','h','a','m',0, +2,'V','e','d','i','c','_','E','x','t',0,'V','e','d','i','c','_','E','x','t','e','n','s','i','o','n','s',0, +2,'L','i','s','u',0,'L','i','s','u',0,2,'B','a','m','u','m',0,'B','a','m','u','m',0, +2,'I','n','d','i','c','_','N','u','m','b','e','r','_','F','o','r','m','s',0,'C','o','m','m','o','n','_','I','n','d','i','c', +'_','N','u','m','b','e','r','_','F','o','r','m','s',0,2,'D','e','v','a','n','a','g','a','r','i','_','E','x','t',0, +'D','e','v','a','n','a','g','a','r','i','_','E','x','t','e','n','d','e','d',0, +2,'J','a','m','o','_','E','x','t','_','A',0,'H','a','n','g','u','l','_','J','a','m','o','_','E','x','t','e','n','d','e','d', +'_','A',0,2,'J','a','v','a','n','e','s','e',0,'J','a','v','a','n','e','s','e',0, +2,'M','y','a','n','m','a','r','_','E','x','t','_','A',0,'M','y','a','n','m','a','r','_','E','x','t','e','n','d','e','d','_', +'A',0,2,'T','a','i','_','V','i','e','t',0,'T','a','i','_','V','i','e','t',0, +2,'M','e','e','t','e','i','_','M','a','y','e','k',0,'M','e','e','t','e','i','_','M','a','y','e','k',0, +2,'J','a','m','o','_','E','x','t','_','B',0,'H','a','n','g','u','l','_','J','a','m','o','_','E','x','t','e','n','d','e','d', +'_','B',0,2,'I','m','p','e','r','i','a','l','_','A','r','a','m','a','i','c',0,'I','m','p','e','r','i','a','l','_','A','r', +'a','m','a','i','c',0,2,'O','l','d','_','S','o','u','t','h','_','A','r','a','b','i','a','n',0, +'O','l','d','_','S','o','u','t','h','_','A','r','a','b','i','a','n',0, +2,'A','v','e','s','t','a','n',0,'A','v','e','s','t','a','n',0, +2,'I','n','s','c','r','i','p','t','i','o','n','a','l','_','P','a','r','t','h','i','a','n',0, +'I','n','s','c','r','i','p','t','i','o','n','a','l','_','P','a','r','t','h','i','a','n',0, +2,'I','n','s','c','r','i','p','t','i','o','n','a','l','_','P','a','h','l','a','v','i',0,'I','n','s','c','r','i','p','t','i', +'o','n','a','l','_','P','a','h','l','a','v','i',0,2,'O','l','d','_','T','u','r','k','i','c',0, +'O','l','d','_','T','u','r','k','i','c',0,2,'R','u','m','i',0,'R','u','m','i','_','N','u','m','e','r','a','l','_','S','y', +'m','b','o','l','s',0,2,'K','a','i','t','h','i',0,'K','a','i','t','h','i',0, +2,'E','g','y','p','t','i','a','n','_','H','i','e','r','o','g','l','y','p','h','s',0,'E','g','y','p','t','i','a','n','_','H', +'i','e','r','o','g','l','y','p','h','s',0,2,'E','n','c','l','o','s','e','d','_','A','l','p','h','a','n','u','m','_','S','u', +'p',0,'E','n','c','l','o','s','e','d','_','A','l','p','h','a','n','u','m','e','r','i','c','_','S','u','p','p','l','e','m','e', +'n','t',0,2,'E','n','c','l','o','s','e','d','_','I','d','e','o','g','r','a','p','h','i','c','_','S','u','p',0, +'E','n','c','l','o','s','e','d','_','I','d','e','o','g','r','a','p','h','i','c','_','S','u','p','p','l','e','m','e','n','t',0, +2,'C','J','K','_','E','x','t','_','C',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h', +'s','_','E','x','t','e','n','s','i','o','n','_','C',0,2,'M','a','n','d','a','i','c',0,'M','a','n','d','a','i','c',0, +2,'B','a','t','a','k',0,'B','a','t','a','k',0,2,'E','t','h','i','o','p','i','c','_','E','x','t','_','A',0, +'E','t','h','i','o','p','i','c','_','E','x','t','e','n','d','e','d','_','A',0, +2,'B','r','a','h','m','i',0,'B','r','a','h','m','i',0,2,'B','a','m','u','m','_','S','u','p',0, +'B','a','m','u','m','_','S','u','p','p','l','e','m','e','n','t',0, +2,'K','a','n','a','_','S','u','p',0,'K','a','n','a','_','S','u','p','p','l','e','m','e','n','t',0, +2,'P','l','a','y','i','n','g','_','C','a','r','d','s',0,'P','l','a','y','i','n','g','_','C','a','r','d','s',0, +2,'M','i','s','c','_','P','i','c','t','o','g','r','a','p','h','s',0,'M','i','s','c','e','l','l','a','n','e','o','u','s','_', +'S','y','m','b','o','l','s','_','A','n','d','_','P','i','c','t','o','g','r','a','p','h','s',0, +2,'E','m','o','t','i','c','o','n','s',0,'E','m','o','t','i','c','o','n','s',0, +2,'T','r','a','n','s','p','o','r','t','_','A','n','d','_','M','a','p',0,'T','r','a','n','s','p','o','r','t','_','A','n','d', +'_','M','a','p','_','S','y','m','b','o','l','s',0,2,'A','l','c','h','e','m','i','c','a','l',0, +'A','l','c','h','e','m','i','c','a','l','_','S','y','m','b','o','l','s',0, +2,'C','J','K','_','E','x','t','_','D',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h', +'s','_','E','x','t','e','n','s','i','o','n','_','D',0,2,'A','r','a','b','i','c','_','E','x','t','_','A',0, +'A','r','a','b','i','c','_','E','x','t','e','n','d','e','d','_','A',0, +2,'A','r','a','b','i','c','_','M','a','t','h',0,'A','r','a','b','i','c','_','M','a','t','h','e','m','a','t','i','c','a','l', +'_','A','l','p','h','a','b','e','t','i','c','_','S','y','m','b','o','l','s',0, +2,'C','h','a','k','m','a',0,'C','h','a','k','m','a',0,2,'M','e','e','t','e','i','_','M','a','y','e','k','_','E','x','t', +0,'M','e','e','t','e','i','_','M','a','y','e','k','_','E','x','t','e','n','s','i','o','n','s',0, +2,'M','e','r','o','i','t','i','c','_','C','u','r','s','i','v','e',0,'M','e','r','o','i','t','i','c','_','C','u','r','s','i', +'v','e',0,2,'M','e','r','o','i','t','i','c','_','H','i','e','r','o','g','l','y','p','h','s',0, +'M','e','r','o','i','t','i','c','_','H','i','e','r','o','g','l','y','p','h','s',0, +2,'M','i','a','o',0,'M','i','a','o',0,2,'S','h','a','r','a','d','a',0,'S','h','a','r','a','d','a',0, +2,'S','o','r','a','_','S','o','m','p','e','n','g',0,'S','o','r','a','_','S','o','m','p','e','n','g',0, +2,'S','u','n','d','a','n','e','s','e','_','S','u','p',0,'S','u','n','d','a','n','e','s','e','_','S','u','p','p','l','e','m', +'e','n','t',0,2,'T','a','k','r','i',0,'T','a','k','r','i',0, +2,'B','a','s','s','a','_','V','a','h',0,'B','a','s','s','a','_','V','a','h',0, +2,'C','a','u','c','a','s','i','a','n','_','A','l','b','a','n','i','a','n',0,'C','a','u','c','a','s','i','a','n','_','A','l', +'b','a','n','i','a','n',0,2,'C','o','p','t','i','c','_','E','p','a','c','t','_','N','u','m','b','e','r','s',0, +'C','o','p','t','i','c','_','E','p','a','c','t','_','N','u','m','b','e','r','s',0, +2,'D','i','a','c','r','i','t','i','c','a','l','s','_','E','x','t',0,'C','o','m','b','i','n','i','n','g','_','D','i','a','c', +'r','i','t','i','c','a','l','_','M','a','r','k','s','_','E','x','t','e','n','d','e','d',0, +2,'D','u','p','l','o','y','a','n',0,'D','u','p','l','o','y','a','n',0, +2,'E','l','b','a','s','a','n',0,'E','l','b','a','s','a','n',0, +2,'G','e','o','m','e','t','r','i','c','_','S','h','a','p','e','s','_','E','x','t',0,'G','e','o','m','e','t','r','i','c','_', +'S','h','a','p','e','s','_','E','x','t','e','n','d','e','d',0, +2,'G','r','a','n','t','h','a',0,'G','r','a','n','t','h','a',0, +2,'K','h','o','j','k','i',0,'K','h','o','j','k','i',0,2,'K','h','u','d','a','w','a','d','i',0, +'K','h','u','d','a','w','a','d','i',0,2,'L','a','t','i','n','_','E','x','t','_','E',0,'L','a','t','i','n','_','E','x','t', +'e','n','d','e','d','_','E',0,2,'L','i','n','e','a','r','_','A',0,'L','i','n','e','a','r','_','A',0, +2,'M','a','h','a','j','a','n','i',0,'M','a','h','a','j','a','n','i',0, +2,'M','a','n','i','c','h','a','e','a','n',0,'M','a','n','i','c','h','a','e','a','n',0, +2,'M','e','n','d','e','_','K','i','k','a','k','u','i',0,'M','e','n','d','e','_','K','i','k','a','k','u','i',0, +2,'M','o','d','i',0,'M','o','d','i',0,2,'M','r','o',0,'M','r','o',0, +2,'M','y','a','n','m','a','r','_','E','x','t','_','B',0,'M','y','a','n','m','a','r','_','E','x','t','e','n','d','e','d','_', +'B',0,2,'N','a','b','a','t','a','e','a','n',0,'N','a','b','a','t','a','e','a','n',0, +2,'O','l','d','_','N','o','r','t','h','_','A','r','a','b','i','a','n',0,'O','l','d','_','N','o','r','t','h','_','A','r','a', +'b','i','a','n',0,2,'O','l','d','_','P','e','r','m','i','c',0,'O','l','d','_','P','e','r','m','i','c',0, +2,'O','r','n','a','m','e','n','t','a','l','_','D','i','n','g','b','a','t','s',0,'O','r','n','a','m','e','n','t','a','l','_', +'D','i','n','g','b','a','t','s',0,2,'P','a','h','a','w','h','_','H','m','o','n','g',0,'P','a','h','a','w','h','_','H','m', +'o','n','g',0,2,'P','a','l','m','y','r','e','n','e',0,'P','a','l','m','y','r','e','n','e',0, +2,'P','a','u','_','C','i','n','_','H','a','u',0,'P','a','u','_','C','i','n','_','H','a','u',0, +2,'P','s','a','l','t','e','r','_','P','a','h','l','a','v','i',0,'P','s','a','l','t','e','r','_','P','a','h','l','a','v','i', +0,2,'S','h','o','r','t','h','a','n','d','_','F','o','r','m','a','t','_','C','o','n','t','r','o','l','s',0, +'S','h','o','r','t','h','a','n','d','_','F','o','r','m','a','t','_','C','o','n','t','r','o','l','s',0, +2,'S','i','d','d','h','a','m',0,'S','i','d','d','h','a','m',0, +2,'S','i','n','h','a','l','a','_','A','r','c','h','a','i','c','_','N','u','m','b','e','r','s',0, +'S','i','n','h','a','l','a','_','A','r','c','h','a','i','c','_','N','u','m','b','e','r','s',0, +2,'S','u','p','_','A','r','r','o','w','s','_','C',0,'S','u','p','p','l','e','m','e','n','t','a','l','_','A','r','r','o','w', +'s','_','C',0,2,'T','i','r','h','u','t','a',0,'T','i','r','h','u','t','a',0, +2,'W','a','r','a','n','g','_','C','i','t','i',0,'W','a','r','a','n','g','_','C','i','t','i',0, +2,'A','h','o','m',0,'A','h','o','m',0,2,'A','n','a','t','o','l','i','a','n','_','H','i','e','r','o','g','l','y','p','h', +'s',0,'A','n','a','t','o','l','i','a','n','_','H','i','e','r','o','g','l','y','p','h','s',0, +2,'C','h','e','r','o','k','e','e','_','S','u','p',0,'C','h','e','r','o','k','e','e','_','S','u','p','p','l','e','m','e','n', +'t',0,2,'C','J','K','_','E','x','t','_','E',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a', +'p','h','s','_','E','x','t','e','n','s','i','o','n','_','E',0, +2,'E','a','r','l','y','_','D','y','n','a','s','t','i','c','_','C','u','n','e','i','f','o','r','m',0, +'E','a','r','l','y','_','D','y','n','a','s','t','i','c','_','C','u','n','e','i','f','o','r','m',0, +2,'H','a','t','r','a','n',0,'H','a','t','r','a','n',0,2,'M','u','l','t','a','n','i',0, +'M','u','l','t','a','n','i',0,2,'O','l','d','_','H','u','n','g','a','r','i','a','n',0,'O','l','d','_','H','u','n','g','a', +'r','i','a','n',0,2,'S','u','p','_','S','y','m','b','o','l','s','_','A','n','d','_','P','i','c','t','o','g','r','a','p','h', +'s',0,'S','u','p','p','l','e','m','e','n','t','a','l','_','S','y','m','b','o','l','s','_','A','n','d','_','P','i','c','t','o', +'g','r','a','p','h','s',0,2,'S','u','t','t','o','n','_','S','i','g','n','W','r','i','t','i','n','g',0, +'S','u','t','t','o','n','_','S','i','g','n','W','r','i','t','i','n','g',0, +2,'A','d','l','a','m',0,'A','d','l','a','m',0,2,'B','h','a','i','k','s','u','k','i',0, +'B','h','a','i','k','s','u','k','i',0,2,'C','y','r','i','l','l','i','c','_','E','x','t','_','C',0, +'C','y','r','i','l','l','i','c','_','E','x','t','e','n','d','e','d','_','C',0, +2,'G','l','a','g','o','l','i','t','i','c','_','S','u','p',0,'G','l','a','g','o','l','i','t','i','c','_','S','u','p','p','l', +'e','m','e','n','t',0,2,'I','d','e','o','g','r','a','p','h','i','c','_','S','y','m','b','o','l','s',0, +'I','d','e','o','g','r','a','p','h','i','c','_','S','y','m','b','o','l','s','_','A','n','d','_','P','u','n','c','t','u','a','t', +'i','o','n',0,2,'M','a','r','c','h','e','n',0,'M','a','r','c','h','e','n',0, +2,'M','o','n','g','o','l','i','a','n','_','S','u','p',0,'M','o','n','g','o','l','i','a','n','_','S','u','p','p','l','e','m', +'e','n','t',0,2,'N','e','w','a',0,'N','e','w','a',0,2,'O','s','a','g','e',0,'O','s','a','g','e',0, +2,'T','a','n','g','u','t',0,'T','a','n','g','u','t',0,2,'T','a','n','g','u','t','_','C','o','m','p','o','n','e','n','t', +'s',0,'T','a','n','g','u','t','_','C','o','m','p','o','n','e','n','t','s',0, +2,'C','J','K','_','E','x','t','_','F',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h', +'s','_','E','x','t','e','n','s','i','o','n','_','F',0,2,'K','a','n','a','_','E','x','t','_','A',0, +'K','a','n','a','_','E','x','t','e','n','d','e','d','_','A',0, +2,'M','a','s','a','r','a','m','_','G','o','n','d','i',0,'M','a','s','a','r','a','m','_','G','o','n','d','i',0, +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','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,'E','g','y','p','t','i','a','n','_','H','i','e','r','o','g','l','y','p','h','_','F','o','r','m','a','t','_','C','o','n','t', +'r','o','l','s',0,'E','g','y','p','t','i','a','n','_','H','i','e','r','o','g','l','y','p','h','_','F','o','r','m','a','t','_', +'C','o','n','t','r','o','l','s',0,2,'E','l','y','m','a','i','c',0,'E','l','y','m','a','i','c',0, +2,'N','a','n','d','i','n','a','g','a','r','i',0,'N','a','n','d','i','n','a','g','a','r','i',0, +2,'N','y','i','a','k','e','n','g','_','P','u','a','c','h','u','e','_','H','m','o','n','g',0, +'N','y','i','a','k','e','n','g','_','P','u','a','c','h','u','e','_','H','m','o','n','g',0, +2,'O','t','t','o','m','a','n','_','S','i','y','a','q','_','N','u','m','b','e','r','s',0,'O','t','t','o','m','a','n','_','S', +'i','y','a','q','_','N','u','m','b','e','r','s',0,2,'S','m','a','l','l','_','K','a','n','a','_','E','x','t',0, +'S','m','a','l','l','_','K','a','n','a','_','E','x','t','e','n','s','i','o','n',0, +2,'S','y','m','b','o','l','s','_','A','n','d','_','P','i','c','t','o','g','r','a','p','h','s','_','E','x','t','_','A',0, +'S','y','m','b','o','l','s','_','A','n','d','_','P','i','c','t','o','g','r','a','p','h','s','_','E','x','t','e','n','d','e','d', +'_','A',0,2,'T','a','m','i','l','_','S','u','p',0,'T','a','m','i','l','_','S','u','p','p','l','e','m','e','n','t',0, +2,'W','a','n','c','h','o',0,'W','a','n','c','h','o',0,2,'C','h','o','r','a','s','m','i','a','n',0, +'C','h','o','r','a','s','m','i','a','n',0,2,'C','J','K','_','E','x','t','_','G',0,'C','J','K','_','U','n','i','f','i','e', +'d','_','I','d','e','o','g','r','a','p','h','s','_','E','x','t','e','n','s','i','o','n','_','G',0, +2,'D','i','v','e','s','_','A','k','u','r','u',0,'D','i','v','e','s','_','A','k','u','r','u',0, +2,'K','h','i','t','a','n','_','S','m','a','l','l','_','S','c','r','i','p','t',0,'K','h','i','t','a','n','_','S','m','a','l', +'l','_','S','c','r','i','p','t',0,2,'L','i','s','u','_','S','u','p',0,'L','i','s','u','_','S','u','p','p','l','e','m','e', +'n','t',0,2,'S','y','m','b','o','l','s','_','F','o','r','_','L','e','g','a','c','y','_','C','o','m','p','u','t','i','n','g', +0,'S','y','m','b','o','l','s','_','F','o','r','_','L','e','g','a','c','y','_','C','o','m','p','u','t','i','n','g',0, +2,'T','a','n','g','u','t','_','S','u','p',0,'T','a','n','g','u','t','_','S','u','p','p','l','e','m','e','n','t',0, +2,'Y','e','z','i','d','i',0,'Y','e','z','i','d','i',0,2,'A','r','a','b','i','c','_','E','x','t','_','B',0, +'A','r','a','b','i','c','_','E','x','t','e','n','d','e','d','_','B',0, +2,'C','y','p','r','o','_','M','i','n','o','a','n',0,'C','y','p','r','o','_','M','i','n','o','a','n',0, +2,'E','t','h','i','o','p','i','c','_','E','x','t','_','B',0,'E','t','h','i','o','p','i','c','_','E','x','t','e','n','d','e', +'d','_','B',0,2,'K','a','n','a','_','E','x','t','_','B',0,'K','a','n','a','_','E','x','t','e','n','d','e','d','_','B',0, +2,'L','a','t','i','n','_','E','x','t','_','F',0,'L','a','t','i','n','_','E','x','t','e','n','d','e','d','_','F',0, +2,'L','a','t','i','n','_','E','x','t','_','G',0,'L','a','t','i','n','_','E','x','t','e','n','d','e','d','_','G',0, +2,'O','l','d','_','U','y','g','h','u','r',0,'O','l','d','_','U','y','g','h','u','r',0, +2,'T','a','n','g','s','a',0,'T','a','n','g','s','a',0,2,'T','o','t','o',0,'T','o','t','o',0, +2,'U','C','A','S','_','E','x','t','_','A',0,'U','n','i','f','i','e','d','_','C','a','n','a','d','i','a','n','_','A','b','o', +'r','i','g','i','n','a','l','_','S','y','l','l','a','b','i','c','s','_','E','x','t','e','n','d','e','d','_','A',0, +2,'V','i','t','h','k','u','q','i',0,'V','i','t','h','k','u','q','i',0, +2,'Z','n','a','m','e','n','n','y','_','M','u','s','i','c',0,'Z','n','a','m','e','n','n','y','_','M','u','s','i','c','a','l', +'_','N','o','t','a','t','i','o','n',0,2,'A','r','a','b','i','c','_','E','x','t','_','C',0, +'A','r','a','b','i','c','_','E','x','t','e','n','d','e','d','_','C',0, +2,'C','J','K','_','E','x','t','_','H',0,'C','J','K','_','U','n','i','f','i','e','d','_','I','d','e','o','g','r','a','p','h', +'s','_','E','x','t','e','n','s','i','o','n','_','H',0,2,'C','y','r','i','l','l','i','c','_','E','x','t','_','D',0, +'C','y','r','i','l','l','i','c','_','E','x','t','e','n','d','e','d','_','D',0, +2,'D','e','v','a','n','a','g','a','r','i','_','E','x','t','_','A',0,'D','e','v','a','n','a','g','a','r','i','_','E','x','t', +'e','n','d','e','d','_','A',0,2,'K','a','k','t','o','v','i','k','_','N','u','m','e','r','a','l','s',0, +'K','a','k','t','o','v','i','k','_','N','u','m','e','r','a','l','s',0, +2,'K','a','w','i',0,'K','a','w','i',0,2,'N','a','g','_','M','u','n','d','a','r','i',0, +'N','a','g','_','M','u','n','d','a','r','i',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, +3,'E','n','c',0,'C','i','r','c','l','e',0,'e','n','c',0, +3,'F','i','n',0,'F','i','n','a','l',0,'f','i','n',0,3,'F','o','n','t',0,'F','o','n','t',0, +'f','o','n','t',0,3,'F','r','a',0,'F','r','a','c','t','i','o','n',0,'f','r','a',0, +3,'I','n','i','t',0,'I','n','i','t','i','a','l',0,'i','n','i','t',0, +3,'I','s','o',0,'I','s','o','l','a','t','e','d',0,'i','s','o',0, +3,'M','e','d',0,'M','e','d','i','a','l',0,'m','e','d',0, +3,'N','a','r',0,'N','a','r','r','o','w',0,'n','a','r',0, +3,'N','b',0,'N','o','b','r','e','a','k',0,'n','b',0,3,'S','m','l',0,'S','m','a','l','l',0, +'s','m','l',0,3,'S','q','r',0,'S','q','u','a','r','e',0,'s','q','r',0, +3,'S','u','b',0,'S','u','b',0,'s','u','b',0,3,'S','u','p',0,'S','u','p','e','r',0, +'s','u','p',0,3,'V','e','r','t',0,'V','e','r','t','i','c','a','l',0,'v','e','r','t',0, +3,'W','i','d','e',0,'W','i','d','e',0,'w','i','d','e',0, +2,'e','a',0,'E','a','s','t','_','A','s','i','a','n','_','W','i','d','t','h',0, +2,'N',0,'N','e','u','t','r','a','l',0,2,'A',0,'A','m','b','i','g','u','o','u','s',0, +2,'H',0,'H','a','l','f','w','i','d','t','h',0,2,'F',0,'F','u','l','l','w','i','d','t','h',0, +2,'N','a',0,'N','a','r','r','o','w',0,2,'W',0,'W','i','d','e',0, +2,'g','c',0,'G','e','n','e','r','a','l','_','C','a','t','e','g','o','r','y',0, +2,'C','n',0,'U','n','a','s','s','i','g','n','e','d',0,2,'L','u',0,'U','p','p','e','r','c','a','s','e','_','L','e','t', +'t','e','r',0,2,'L','l',0,'L','o','w','e','r','c','a','s','e','_','L','e','t','t','e','r',0, +2,'L','t',0,'T','i','t','l','e','c','a','s','e','_','L','e','t','t','e','r',0, +2,'L','m',0,'M','o','d','i','f','i','e','r','_','L','e','t','t','e','r',0, +2,'L','o',0,'O','t','h','e','r','_','L','e','t','t','e','r',0, +2,'M','n',0,'N','o','n','s','p','a','c','i','n','g','_','M','a','r','k',0, +2,'M','e',0,'E','n','c','l','o','s','i','n','g','_','M','a','r','k',0, +2,'M','c',0,'S','p','a','c','i','n','g','_','M','a','r','k',0, +3,'N','d',0,'D','e','c','i','m','a','l','_','N','u','m','b','e','r',0,'d','i','g','i','t',0, +2,'N','l',0,'L','e','t','t','e','r','_','N','u','m','b','e','r',0, +2,'N','o',0,'O','t','h','e','r','_','N','u','m','b','e','r',0, +2,'Z','s',0,'S','p','a','c','e','_','S','e','p','a','r','a','t','o','r',0, +2,'Z','l',0,'L','i','n','e','_','S','e','p','a','r','a','t','o','r',0, +2,'Z','p',0,'P','a','r','a','g','r','a','p','h','_','S','e','p','a','r','a','t','o','r',0, +3,'C','c',0,'C','o','n','t','r','o','l',0,'c','n','t','r','l',0, +2,'C','f',0,'F','o','r','m','a','t',0,2,'C','o',0,'P','r','i','v','a','t','e','_','U','s','e',0, +2,'C','s',0,'S','u','r','r','o','g','a','t','e',0,2,'P','d',0,'D','a','s','h','_','P','u','n','c','t','u','a','t','i', +'o','n',0,2,'P','s',0,'O','p','e','n','_','P','u','n','c','t','u','a','t','i','o','n',0, +2,'P','e',0,'C','l','o','s','e','_','P','u','n','c','t','u','a','t','i','o','n',0, +2,'P','c',0,'C','o','n','n','e','c','t','o','r','_','P','u','n','c','t','u','a','t','i','o','n',0, +2,'P','o',0,'O','t','h','e','r','_','P','u','n','c','t','u','a','t','i','o','n',0, +2,'S','m',0,'M','a','t','h','_','S','y','m','b','o','l',0, +2,'S','c',0,'C','u','r','r','e','n','c','y','_','S','y','m','b','o','l',0, +2,'S','k',0,'M','o','d','i','f','i','e','r','_','S','y','m','b','o','l',0, +2,'S','o',0,'O','t','h','e','r','_','S','y','m','b','o','l',0, +2,'P','i',0,'I','n','i','t','i','a','l','_','P','u','n','c','t','u','a','t','i','o','n',0, +2,'P','f',0,'F','i','n','a','l','_','P','u','n','c','t','u','a','t','i','o','n',0, +2,'j','g',0,'J','o','i','n','i','n','g','_','G','r','o','u','p',0, +2,'N','o','_','J','o','i','n','i','n','g','_','G','r','o','u','p',0,'N','o','_','J','o','i','n','i','n','g','_','G','r','o', +'u','p',0,2,'A','i','n',0,'A','i','n',0,2,'A','l','a','p','h',0,'A','l','a','p','h',0, +2,'A','l','e','f',0,'A','l','e','f',0,2,'B','e','h',0,'B','e','h',0, +2,'B','e','t','h',0,'B','e','t','h',0,2,'D','a','l',0,'D','a','l',0, +2,'D','a','l','a','t','h','_','R','i','s','h',0,'D','a','l','a','t','h','_','R','i','s','h',0, +2,'E',0,'E',0,2,'F','e','h',0,'F','e','h',0,2,'F','i','n','a','l','_','S','e','m','k','a','t','h',0, +'F','i','n','a','l','_','S','e','m','k','a','t','h',0,2,'G','a','f',0,'G','a','f',0, +2,'G','a','m','a','l',0,'G','a','m','a','l',0,2,'H','a','h',0,'H','a','h',0, +2,'T','e','h','_','M','a','r','b','u','t','a','_','G','o','a','l',0,'H','a','m','z','a','_','O','n','_','H','e','h','_','G', +'o','a','l',0,2,'H','e',0,'H','e',0,2,'H','e','h',0,'H','e','h',0, +2,'H','e','h','_','G','o','a','l',0,'H','e','h','_','G','o','a','l',0, +2,'H','e','t','h',0,'H','e','t','h',0,2,'K','a','f',0,'K','a','f',0, +2,'K','a','p','h',0,'K','a','p','h',0,2,'K','n','o','t','t','e','d','_','H','e','h',0, +'K','n','o','t','t','e','d','_','H','e','h',0,2,'L','a','m',0,'L','a','m',0, +2,'L','a','m','a','d','h',0,'L','a','m','a','d','h',0,2,'M','e','e','m',0,'M','e','e','m',0, +2,'M','i','m',0,'M','i','m',0,2,'N','o','o','n',0,'N','o','o','n',0, +2,'N','u','n',0,'N','u','n',0,2,'P','e',0,'P','e',0, +2,'Q','a','f',0,'Q','a','f',0,2,'Q','a','p','h',0,'Q','a','p','h',0, +2,'R','e','h',0,'R','e','h',0,2,'R','e','v','e','r','s','e','d','_','P','e',0,'R','e','v','e','r','s','e','d','_','P', +'e',0,2,'S','a','d',0,'S','a','d',0,2,'S','a','d','h','e',0,'S','a','d','h','e',0, +2,'S','e','e','n',0,'S','e','e','n',0,2,'S','e','m','k','a','t','h',0,'S','e','m','k','a','t','h',0, +2,'S','h','i','n',0,'S','h','i','n',0,2,'S','w','a','s','h','_','K','a','f',0,'S','w','a','s','h','_','K','a','f',0, +2,'S','y','r','i','a','c','_','W','a','w',0,'S','y','r','i','a','c','_','W','a','w',0, +2,'T','a','h',0,'T','a','h',0,2,'T','a','w',0,'T','a','w',0, +2,'T','e','h','_','M','a','r','b','u','t','a',0,'T','e','h','_','M','a','r','b','u','t','a',0, +2,'T','e','t','h',0,'T','e','t','h',0,2,'W','a','w',0,'W','a','w',0, +2,'Y','e','h',0,'Y','e','h',0,2,'Y','e','h','_','B','a','r','r','e','e',0,'Y','e','h','_','B','a','r','r','e','e',0, +2,'Y','e','h','_','W','i','t','h','_','T','a','i','l',0,'Y','e','h','_','W','i','t','h','_','T','a','i','l',0, +2,'Y','u','d','h',0,'Y','u','d','h',0,2,'Y','u','d','h','_','H','e',0,'Y','u','d','h','_','H','e',0, +2,'Z','a','i','n',0,'Z','a','i','n',0,2,'F','e',0,'F','e',0, +2,'K','h','a','p','h',0,'K','h','a','p','h',0,2,'Z','h','a','i','n',0,'Z','h','a','i','n',0, +2,'B','u','r','u','s','h','a','s','k','i','_','Y','e','h','_','B','a','r','r','e','e',0,'B','u','r','u','s','h','a','s','k', +'i','_','Y','e','h','_','B','a','r','r','e','e',0,2,'F','a','r','s','i','_','Y','e','h',0, +'F','a','r','s','i','_','Y','e','h',0,2,'N','y','a',0,'N','y','a',0, +2,'R','o','h','i','n','g','y','a','_','Y','e','h',0,'R','o','h','i','n','g','y','a','_','Y','e','h',0, +2,'M','a','n','i','c','h','a','e','a','n','_','A','l','e','p','h',0,'M','a','n','i','c','h','a','e','a','n','_','A','l','e', +'p','h',0,2,'M','a','n','i','c','h','a','e','a','n','_','A','y','i','n',0,'M','a','n','i','c','h','a','e','a','n','_','A', +'y','i','n',0,2,'M','a','n','i','c','h','a','e','a','n','_','B','e','t','h',0,'M','a','n','i','c','h','a','e','a','n','_', +'B','e','t','h',0,2,'M','a','n','i','c','h','a','e','a','n','_','D','a','l','e','t','h',0, +'M','a','n','i','c','h','a','e','a','n','_','D','a','l','e','t','h',0, +2,'M','a','n','i','c','h','a','e','a','n','_','D','h','a','m','e','d','h',0,'M','a','n','i','c','h','a','e','a','n','_','D', +'h','a','m','e','d','h',0,2,'M','a','n','i','c','h','a','e','a','n','_','F','i','v','e',0, +'M','a','n','i','c','h','a','e','a','n','_','F','i','v','e',0, +2,'M','a','n','i','c','h','a','e','a','n','_','G','i','m','e','l',0,'M','a','n','i','c','h','a','e','a','n','_','G','i','m', +'e','l',0,2,'M','a','n','i','c','h','a','e','a','n','_','H','e','t','h',0,'M','a','n','i','c','h','a','e','a','n','_','H', +'e','t','h',0,2,'M','a','n','i','c','h','a','e','a','n','_','H','u','n','d','r','e','d',0, +'M','a','n','i','c','h','a','e','a','n','_','H','u','n','d','r','e','d',0, +2,'M','a','n','i','c','h','a','e','a','n','_','K','a','p','h',0,'M','a','n','i','c','h','a','e','a','n','_','K','a','p','h', +0,2,'M','a','n','i','c','h','a','e','a','n','_','L','a','m','e','d','h',0,'M','a','n','i','c','h','a','e','a','n','_','L', +'a','m','e','d','h',0,2,'M','a','n','i','c','h','a','e','a','n','_','M','e','m',0,'M','a','n','i','c','h','a','e','a','n', +'_','M','e','m',0,2,'M','a','n','i','c','h','a','e','a','n','_','N','u','n',0,'M','a','n','i','c','h','a','e','a','n','_', +'N','u','n',0,2,'M','a','n','i','c','h','a','e','a','n','_','O','n','e',0,'M','a','n','i','c','h','a','e','a','n','_','O', +'n','e',0,2,'M','a','n','i','c','h','a','e','a','n','_','P','e',0,'M','a','n','i','c','h','a','e','a','n','_','P','e',0, +2,'M','a','n','i','c','h','a','e','a','n','_','Q','o','p','h',0,'M','a','n','i','c','h','a','e','a','n','_','Q','o','p','h', +0,2,'M','a','n','i','c','h','a','e','a','n','_','R','e','s','h',0,'M','a','n','i','c','h','a','e','a','n','_','R','e','s', +'h',0,2,'M','a','n','i','c','h','a','e','a','n','_','S','a','d','h','e',0,'M','a','n','i','c','h','a','e','a','n','_','S', +'a','d','h','e',0,2,'M','a','n','i','c','h','a','e','a','n','_','S','a','m','e','k','h',0, +'M','a','n','i','c','h','a','e','a','n','_','S','a','m','e','k','h',0, +2,'M','a','n','i','c','h','a','e','a','n','_','T','a','w',0,'M','a','n','i','c','h','a','e','a','n','_','T','a','w',0, +2,'M','a','n','i','c','h','a','e','a','n','_','T','e','n',0,'M','a','n','i','c','h','a','e','a','n','_','T','e','n',0, +2,'M','a','n','i','c','h','a','e','a','n','_','T','e','t','h',0,'M','a','n','i','c','h','a','e','a','n','_','T','e','t','h', +0,2,'M','a','n','i','c','h','a','e','a','n','_','T','h','a','m','e','d','h',0,'M','a','n','i','c','h','a','e','a','n','_', +'T','h','a','m','e','d','h',0,2,'M','a','n','i','c','h','a','e','a','n','_','T','w','e','n','t','y',0, +'M','a','n','i','c','h','a','e','a','n','_','T','w','e','n','t','y',0, +2,'M','a','n','i','c','h','a','e','a','n','_','W','a','w',0,'M','a','n','i','c','h','a','e','a','n','_','W','a','w',0, +2,'M','a','n','i','c','h','a','e','a','n','_','Y','o','d','h',0,'M','a','n','i','c','h','a','e','a','n','_','Y','o','d','h', +0,2,'M','a','n','i','c','h','a','e','a','n','_','Z','a','y','i','n',0,'M','a','n','i','c','h','a','e','a','n','_','Z','a', +'y','i','n',0,2,'S','t','r','a','i','g','h','t','_','W','a','w',0,'S','t','r','a','i','g','h','t','_','W','a','w',0, +2,'A','f','r','i','c','a','n','_','F','e','h',0,'A','f','r','i','c','a','n','_','F','e','h',0, +2,'A','f','r','i','c','a','n','_','N','o','o','n',0,'A','f','r','i','c','a','n','_','N','o','o','n',0, +2,'A','f','r','i','c','a','n','_','Q','a','f',0,'A','f','r','i','c','a','n','_','Q','a','f',0, +2,'M','a','l','a','y','a','l','a','m','_','B','h','a',0,'M','a','l','a','y','a','l','a','m','_','B','h','a',0, +2,'M','a','l','a','y','a','l','a','m','_','J','a',0,'M','a','l','a','y','a','l','a','m','_','J','a',0, +2,'M','a','l','a','y','a','l','a','m','_','L','l','a',0,'M','a','l','a','y','a','l','a','m','_','L','l','a',0, +2,'M','a','l','a','y','a','l','a','m','_','L','l','l','a',0,'M','a','l','a','y','a','l','a','m','_','L','l','l','a',0, +2,'M','a','l','a','y','a','l','a','m','_','N','g','a',0,'M','a','l','a','y','a','l','a','m','_','N','g','a',0, +2,'M','a','l','a','y','a','l','a','m','_','N','n','a',0,'M','a','l','a','y','a','l','a','m','_','N','n','a',0, +2,'M','a','l','a','y','a','l','a','m','_','N','n','n','a',0,'M','a','l','a','y','a','l','a','m','_','N','n','n','a',0, +2,'M','a','l','a','y','a','l','a','m','_','N','y','a',0,'M','a','l','a','y','a','l','a','m','_','N','y','a',0, +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,'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,'T','h','i','n','_','Y','e','h',0,'T','h','i','n','_','Y','e','h',0, +2,'V','e','r','t','i','c','a','l','_','T','a','i','l',0,'V','e','r','t','i','c','a','l','_','T','a','i','l',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, +2,'R',0,'R','i','g','h','t','_','J','o','i','n','i','n','g',0, +2,'T',0,'T','r','a','n','s','p','a','r','e','n','t',0,2,'l','b',0,'L','i','n','e','_','B','r','e','a','k',0, +2,'X','X',0,'U','n','k','n','o','w','n',0,2,'A','I',0,'A','m','b','i','g','u','o','u','s',0, +2,'A','L',0,'A','l','p','h','a','b','e','t','i','c',0,2,'B','2',0,'B','r','e','a','k','_','B','o','t','h',0, +2,'B','A',0,'B','r','e','a','k','_','A','f','t','e','r',0, +2,'B','B',0,'B','r','e','a','k','_','B','e','f','o','r','e',0, +2,'B','K',0,'M','a','n','d','a','t','o','r','y','_','B','r','e','a','k',0, +2,'C','B',0,'C','o','n','t','i','n','g','e','n','t','_','B','r','e','a','k',0, +2,'C','L',0,'C','l','o','s','e','_','P','u','n','c','t','u','a','t','i','o','n',0, +2,'C','M',0,'C','o','m','b','i','n','i','n','g','_','M','a','r','k',0, +2,'C','R',0,'C','a','r','r','i','a','g','e','_','R','e','t','u','r','n',0, +2,'E','X',0,'E','x','c','l','a','m','a','t','i','o','n',0, +2,'G','L',0,'G','l','u','e',0,2,'H','Y',0,'H','y','p','h','e','n',0, +2,'I','D',0,'I','d','e','o','g','r','a','p','h','i','c',0, +3,'I','N',0,'I','n','s','e','p','a','r','a','b','l','e',0,'I','n','s','e','p','e','r','a','b','l','e',0, +2,'I','S',0,'I','n','f','i','x','_','N','u','m','e','r','i','c',0, +2,'L','F',0,'L','i','n','e','_','F','e','e','d',0,2,'N','S',0,'N','o','n','s','t','a','r','t','e','r',0, +2,'N','U',0,'N','u','m','e','r','i','c',0,2,'O','P',0,'O','p','e','n','_','P','u','n','c','t','u','a','t','i','o','n', +0,2,'P','O',0,'P','o','s','t','f','i','x','_','N','u','m','e','r','i','c',0, +2,'P','R',0,'P','r','e','f','i','x','_','N','u','m','e','r','i','c',0, +2,'Q','U',0,'Q','u','o','t','a','t','i','o','n',0,2,'S','A',0,'C','o','m','p','l','e','x','_','C','o','n','t','e','x', +'t',0,2,'S','G',0,'S','u','r','r','o','g','a','t','e',0, +2,'S','P',0,'S','p','a','c','e',0,2,'S','Y',0,'B','r','e','a','k','_','S','y','m','b','o','l','s',0, +2,'Z','W',0,'Z','W','S','p','a','c','e',0,2,'N','L',0,'N','e','x','t','_','L','i','n','e',0, +2,'W','J',0,'W','o','r','d','_','J','o','i','n','e','r',0, +2,'H','2',0,'H','2',0,2,'H','3',0,'H','3',0,2,'J','L',0,'J','L',0, +2,'J','T',0,'J','T',0,2,'J','V',0,'J','V',0,2,'C','P',0,'C','l','o','s','e','_','P','a','r','e','n','t','h','e', +'s','i','s',0,2,'C','J',0,'C','o','n','d','i','t','i','o','n','a','l','_','J','a','p','a','n','e','s','e','_','S','t','a', +'r','t','e','r',0,2,'H','L',0,'H','e','b','r','e','w','_','L','e','t','t','e','r',0, +2,'E','B',0,'E','_','B','a','s','e',0,2,'E','M',0,'E','_','M','o','d','i','f','i','e','r',0, +2,'Z','W','J',0,'Z','W','J',0,2,'n','t',0,'N','u','m','e','r','i','c','_','T','y','p','e',0, +2,'N','o','n','e',0,'N','o','n','e',0,2,'D','e',0,'D','e','c','i','m','a','l',0, +2,'D','i',0,'D','i','g','i','t',0,2,'N','u',0,'N','u','m','e','r','i','c',0, +2,'s','c',0,'S','c','r','i','p','t',0,2,'Z','y','y','y',0,'C','o','m','m','o','n',0, +3,'Z','i','n','h',0,'I','n','h','e','r','i','t','e','d',0,'Q','a','a','i',0, +2,'A','r','a','b',0,'A','r','a','b','i','c',0,2,'A','r','m','n',0,'A','r','m','e','n','i','a','n',0, +2,'B','e','n','g',0,'B','e','n','g','a','l','i',0,2,'B','o','p','o',0,'B','o','p','o','m','o','f','o',0, +2,'C','h','e','r',0,'C','h','e','r','o','k','e','e',0,3,'C','o','p','t',0,'C','o','p','t','i','c',0, +'Q','a','a','c',0,2,'C','y','r','l',0,'C','y','r','i','l','l','i','c',0, +2,'D','s','r','t',0,'D','e','s','e','r','e','t',0,2,'D','e','v','a',0,'D','e','v','a','n','a','g','a','r','i',0, +2,'E','t','h','i',0,'E','t','h','i','o','p','i','c',0,2,'G','e','o','r',0,'G','e','o','r','g','i','a','n',0, +2,'G','o','t','h',0,'G','o','t','h','i','c',0,2,'G','r','e','k',0,'G','r','e','e','k',0, +2,'G','u','j','r',0,'G','u','j','a','r','a','t','i',0,2,'G','u','r','u',0,'G','u','r','m','u','k','h','i',0, +2,'H','a','n','i',0,'H','a','n',0,2,'H','a','n','g',0,'H','a','n','g','u','l',0, +2,'H','e','b','r',0,'H','e','b','r','e','w',0,2,'H','i','r','a',0,'H','i','r','a','g','a','n','a',0, +2,'K','n','d','a',0,'K','a','n','n','a','d','a',0,2,'K','a','n','a',0,'K','a','t','a','k','a','n','a',0, +2,'K','h','m','r',0,'K','h','m','e','r',0,2,'L','a','o','o',0,'L','a','o',0, +2,'L','a','t','n',0,'L','a','t','i','n',0,2,'M','l','y','m',0,'M','a','l','a','y','a','l','a','m',0, +2,'M','o','n','g',0,'M','o','n','g','o','l','i','a','n',0, +2,'M','y','m','r',0,'M','y','a','n','m','a','r',0,2,'O','g','a','m',0,'O','g','h','a','m',0, +2,'I','t','a','l',0,'O','l','d','_','I','t','a','l','i','c',0, +2,'O','r','y','a',0,'O','r','i','y','a',0,2,'R','u','n','r',0,'R','u','n','i','c',0, +2,'S','i','n','h',0,'S','i','n','h','a','l','a',0,2,'S','y','r','c',0,'S','y','r','i','a','c',0, +2,'T','a','m','l',0,'T','a','m','i','l',0,2,'T','e','l','u',0,'T','e','l','u','g','u',0, +2,'T','h','a','a',0,'T','h','a','a','n','a',0,2,'T','i','b','t',0,'T','i','b','e','t','a','n',0, +2,'C','a','n','s',0,'C','a','n','a','d','i','a','n','_','A','b','o','r','i','g','i','n','a','l',0, +2,'Y','i','i','i',0,'Y','i',0,2,'T','g','l','g',0,'T','a','g','a','l','o','g',0, +2,'H','a','n','o',0,'H','a','n','u','n','o','o',0,2,'B','u','h','d',0,'B','u','h','i','d',0, +2,'T','a','g','b',0,'T','a','g','b','a','n','w','a',0,2,'B','r','a','i',0,'B','r','a','i','l','l','e',0, +2,'C','p','r','t',0,'C','y','p','r','i','o','t',0,2,'L','i','m','b',0,'L','i','m','b','u',0, +2,'L','i','n','b',0,'L','i','n','e','a','r','_','B',0,2,'O','s','m','a',0,'O','s','m','a','n','y','a',0, +2,'S','h','a','w',0,'S','h','a','v','i','a','n',0,2,'T','a','l','e',0,'T','a','i','_','L','e',0, +2,'U','g','a','r',0,'U','g','a','r','i','t','i','c',0,2,'H','r','k','t',0,'K','a','t','a','k','a','n','a','_','O','r', +'_','H','i','r','a','g','a','n','a',0,2,'B','u','g','i',0,'B','u','g','i','n','e','s','e',0, +2,'G','l','a','g',0,'G','l','a','g','o','l','i','t','i','c',0, +2,'K','h','a','r',0,'K','h','a','r','o','s','h','t','h','i',0, +2,'S','y','l','o',0,'S','y','l','o','t','i','_','N','a','g','r','i',0, +2,'T','a','l','u',0,'N','e','w','_','T','a','i','_','L','u','e',0, +2,'T','f','n','g',0,'T','i','f','i','n','a','g','h',0,2,'X','p','e','o',0,'O','l','d','_','P','e','r','s','i','a','n', +0,2,'B','a','l','i',0,'B','a','l','i','n','e','s','e',0, +2,'B','a','t','k',0,'B','a','t','a','k',0,2,'B','l','i','s',0,'B','l','i','s',0, +2,'B','r','a','h',0,'B','r','a','h','m','i',0,2,'C','i','r','t',0,'C','i','r','t',0, +2,'C','y','r','s',0,'C','y','r','s',0,2,'E','g','y','d',0,'E','g','y','d',0, +2,'E','g','y','h',0,'E','g','y','h',0,2,'E','g','y','p',0,'E','g','y','p','t','i','a','n','_','H','i','e','r','o','g', +'l','y','p','h','s',0,2,'G','e','o','k',0,'G','e','o','k',0, +2,'H','a','n','s',0,'H','a','n','s',0,2,'H','a','n','t',0,'H','a','n','t',0, +2,'H','m','n','g',0,'P','a','h','a','w','h','_','H','m','o','n','g',0, +2,'H','u','n','g',0,'O','l','d','_','H','u','n','g','a','r','i','a','n',0, +2,'I','n','d','s',0,'I','n','d','s',0,2,'J','a','v','a',0,'J','a','v','a','n','e','s','e',0, +2,'K','a','l','i',0,'K','a','y','a','h','_','L','i',0,2,'L','a','t','f',0,'L','a','t','f',0, +2,'L','a','t','g',0,'L','a','t','g',0,2,'L','e','p','c',0,'L','e','p','c','h','a',0, +2,'L','i','n','a',0,'L','i','n','e','a','r','_','A',0,2,'M','a','n','d',0,'M','a','n','d','a','i','c',0, +2,'M','a','y','a',0,'M','a','y','a',0,2,'M','e','r','o',0,'M','e','r','o','i','t','i','c','_','H','i','e','r','o','g', +'l','y','p','h','s',0,2,'N','k','o','o',0,'N','k','o',0, +2,'O','r','k','h',0,'O','l','d','_','T','u','r','k','i','c',0, +2,'P','e','r','m',0,'O','l','d','_','P','e','r','m','i','c',0, +2,'P','h','a','g',0,'P','h','a','g','s','_','P','a',0,2,'P','h','n','x',0,'P','h','o','e','n','i','c','i','a','n',0, +2,'P','l','r','d',0,'M','i','a','o',0,2,'R','o','r','o',0,'R','o','r','o',0, +2,'S','a','r','a',0,'S','a','r','a',0,2,'S','y','r','e',0,'S','y','r','e',0, +2,'S','y','r','j',0,'S','y','r','j',0,2,'S','y','r','n',0,'S','y','r','n',0, +2,'T','e','n','g',0,'T','e','n','g',0,2,'V','a','i','i',0,'V','a','i',0, +2,'V','i','s','p',0,'V','i','s','p',0,2,'X','s','u','x',0,'C','u','n','e','i','f','o','r','m',0, +2,'Z','x','x','x',0,'Z','x','x','x',0,2,'Z','z','z','z',0,'U','n','k','n','o','w','n',0, +2,'C','a','r','i',0,'C','a','r','i','a','n',0,2,'J','p','a','n',0,'J','p','a','n',0, +2,'L','a','n','a',0,'T','a','i','_','T','h','a','m',0,2,'L','y','c','i',0,'L','y','c','i','a','n',0, +2,'L','y','d','i',0,'L','y','d','i','a','n',0,2,'O','l','c','k',0,'O','l','_','C','h','i','k','i',0, +2,'R','j','n','g',0,'R','e','j','a','n','g',0,2,'S','a','u','r',0,'S','a','u','r','a','s','h','t','r','a',0, +2,'S','g','n','w',0,'S','i','g','n','W','r','i','t','i','n','g',0, +2,'S','u','n','d',0,'S','u','n','d','a','n','e','s','e',0, +2,'M','o','o','n',0,'M','o','o','n',0,2,'M','t','e','i',0,'M','e','e','t','e','i','_','M','a','y','e','k',0, +2,'A','r','m','i',0,'I','m','p','e','r','i','a','l','_','A','r','a','m','a','i','c',0, +2,'A','v','s','t',0,'A','v','e','s','t','a','n',0,2,'C','a','k','m',0,'C','h','a','k','m','a',0, +2,'K','o','r','e',0,'K','o','r','e',0,2,'K','t','h','i',0,'K','a','i','t','h','i',0, +2,'M','a','n','i',0,'M','a','n','i','c','h','a','e','a','n',0, +2,'P','h','l','i',0,'I','n','s','c','r','i','p','t','i','o','n','a','l','_','P','a','h','l','a','v','i',0, +2,'P','h','l','p',0,'P','s','a','l','t','e','r','_','P','a','h','l','a','v','i',0, +2,'P','h','l','v',0,'P','h','l','v',0,2,'P','r','t','i',0,'I','n','s','c','r','i','p','t','i','o','n','a','l','_','P', +'a','r','t','h','i','a','n',0,2,'S','a','m','r',0,'S','a','m','a','r','i','t','a','n',0, +2,'T','a','v','t',0,'T','a','i','_','V','i','e','t',0,2,'Z','m','t','h',0,'Z','m','t','h',0, +2,'Z','s','y','m',0,'Z','s','y','m',0,2,'B','a','m','u',0,'B','a','m','u','m',0, +2,'N','k','g','b',0,'N','k','g','b',0,2,'S','a','r','b',0,'O','l','d','_','S','o','u','t','h','_','A','r','a','b','i', +'a','n',0,2,'B','a','s','s',0,'B','a','s','s','a','_','V','a','h',0, +2,'D','u','p','l',0,'D','u','p','l','o','y','a','n',0,2,'E','l','b','a',0,'E','l','b','a','s','a','n',0, +2,'G','r','a','n',0,'G','r','a','n','t','h','a',0,2,'K','p','e','l',0,'K','p','e','l',0, +2,'L','o','m','a',0,'L','o','m','a',0,2,'M','e','n','d',0,'M','e','n','d','e','_','K','i','k','a','k','u','i',0, +2,'M','e','r','c',0,'M','e','r','o','i','t','i','c','_','C','u','r','s','i','v','e',0, +2,'N','a','r','b',0,'O','l','d','_','N','o','r','t','h','_','A','r','a','b','i','a','n',0, +2,'N','b','a','t',0,'N','a','b','a','t','a','e','a','n',0, +2,'P','a','l','m',0,'P','a','l','m','y','r','e','n','e',0, +2,'S','i','n','d',0,'K','h','u','d','a','w','a','d','i',0, +2,'W','a','r','a',0,'W','a','r','a','n','g','_','C','i','t','i',0, +2,'A','f','a','k',0,'A','f','a','k',0,2,'J','u','r','c',0,'J','u','r','c',0, +2,'M','r','o','o',0,'M','r','o',0,2,'N','s','h','u',0,'N','u','s','h','u',0, +2,'S','h','r','d',0,'S','h','a','r','a','d','a',0,2,'S','o','r','a',0,'S','o','r','a','_','S','o','m','p','e','n','g', +0,2,'T','a','k','r',0,'T','a','k','r','i',0,2,'T','a','n','g',0,'T','a','n','g','u','t',0, +2,'W','o','l','e',0,'W','o','l','e',0,2,'H','l','u','w',0,'A','n','a','t','o','l','i','a','n','_','H','i','e','r','o', +'g','l','y','p','h','s',0,2,'K','h','o','j',0,'K','h','o','j','k','i',0, +2,'T','i','r','h',0,'T','i','r','h','u','t','a',0,2,'A','g','h','b',0,'C','a','u','c','a','s','i','a','n','_','A','l', +'b','a','n','i','a','n',0,2,'M','a','h','j',0,'M','a','h','a','j','a','n','i',0, +2,'H','a','t','r',0,'H','a','t','r','a','n',0,2,'M','u','l','t',0,'M','u','l','t','a','n','i',0, +2,'P','a','u','c',0,'P','a','u','_','C','i','n','_','H','a','u',0, +2,'S','i','d','d',0,'S','i','d','d','h','a','m',0,2,'A','d','l','m',0,'A','d','l','a','m',0, +2,'B','h','k','s',0,'B','h','a','i','k','s','u','k','i',0, +2,'M','a','r','c',0,'M','a','r','c','h','e','n',0,2,'O','s','g','e',0,'O','s','a','g','e',0, +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,'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,'E','l','y','m',0,'E','l','y','m','a','i','c',0,2,'H','m','n','p',0,'N','y','i','a','k','e','n','g','_','P','u','a', +'c','h','u','e','_','H','m','o','n','g',0,2,'N','a','n','d',0,'N','a','n','d','i','n','a','g','a','r','i',0, +2,'W','c','h','o',0,'W','a','n','c','h','o',0,2,'C','h','r','s',0,'C','h','o','r','a','s','m','i','a','n',0, +2,'D','i','a','k',0,'D','i','v','e','s','_','A','k','u','r','u',0, +2,'K','i','t','s',0,'K','h','i','t','a','n','_','S','m','a','l','l','_','S','c','r','i','p','t',0, +2,'Y','e','z','i',0,'Y','e','z','i','d','i',0,2,'C','p','m','n',0,'C','y','p','r','o','_','M','i','n','o','a','n',0, +2,'O','u','g','r',0,'O','l','d','_','U','y','g','h','u','r',0, +2,'T','n','s','a',0,'T','a','n','g','s','a',0,2,'V','i','t','h',0,'V','i','t','h','k','u','q','i',0, +2,'N','a','g','m',0,'N','a','g','_','M','u','n','d','a','r','i',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, +2,'L','V',0,'L','V','_','S','y','l','l','a','b','l','e',0, +2,'L','V','T',0,'L','V','T','_','S','y','l','l','a','b','l','e',0, +2,'N','F','D','_','Q','C',0,'N','F','D','_','Q','u','i','c','k','_','C','h','e','c','k',0, +2,'N',0,'N','o',0,2,'Y',0,'Y','e','s',0,2,'N','F','K','D','_','Q','C',0,'N','F','K','D','_','Q','u','i','c','k', +'_','C','h','e','c','k',0,2,'N','F','C','_','Q','C',0,'N','F','C','_','Q','u','i','c','k','_','C','h','e','c','k',0, +2,'M',0,'M','a','y','b','e',0,2,'N','F','K','C','_','Q','C',0,'N','F','K','C','_','Q','u','i','c','k','_','C','h','e', +'c','k',0,2,'l','c','c','c',0,'L','e','a','d','_','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,'t','c','c','c',0,'T','r','a','i','l','_','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,'G','C','B',0,'G','r','a','p','h','e','m','e','_','C','l','u','s', +'t','e','r','_','B','r','e','a','k',0,2,'X','X',0,'O','t','h','e','r',0, +2,'C','N',0,'C','o','n','t','r','o','l',0,2,'C','R',0,'C','R',0, +2,'E','X',0,'E','x','t','e','n','d',0,2,'L',0,'L',0, +2,'L','F',0,'L','F',0,2,'L','V',0,'L','V',0,2,'L','V','T',0,'L','V','T',0, +2,'T',0,'T',0,2,'V',0,'V',0,2,'S','M',0,'S','p','a','c','i','n','g','M','a','r','k',0, +2,'P','P',0,'P','r','e','p','e','n','d',0,2,'E','B','G',0,'E','_','B','a','s','e','_','G','A','Z',0, +2,'G','A','Z',0,'G','l','u','e','_','A','f','t','e','r','_','Z','w','j',0, +2,'S','B',0,'S','e','n','t','e','n','c','e','_','B','r','e','a','k',0, +2,'A','T',0,'A','T','e','r','m',0,2,'C','L',0,'C','l','o','s','e',0, +2,'F','O',0,'F','o','r','m','a','t',0,2,'L','O',0,'L','o','w','e','r',0, +2,'L','E',0,'O','L','e','t','t','e','r',0,2,'S','E',0,'S','e','p',0, +2,'S','P',0,'S','p',0,2,'S','T',0,'S','T','e','r','m',0, +2,'U','P',0,'U','p','p','e','r',0,2,'S','C',0,'S','C','o','n','t','i','n','u','e',0, +2,'W','B',0,'W','o','r','d','_','B','r','e','a','k',0,2,'L','E',0,'A','L','e','t','t','e','r',0, +2,'K','A',0,'K','a','t','a','k','a','n','a',0,2,'M','L',0,'M','i','d','L','e','t','t','e','r',0, +2,'M','N',0,'M','i','d','N','u','m',0,2,'E','X',0,'E','x','t','e','n','d','N','u','m','L','e','t',0, +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,'I','n','P','C',0,'I','n','d','i','c','_','P','o','s','i','t','i','o','n','a','l','_', +'C','a','t','e','g','o','r','y',0,2,'N','A',0,'N','A',0, +2,'B','o','t','t','o','m',0,'B','o','t','t','o','m',0,2,'B','o','t','t','o','m','_','A','n','d','_','L','e','f','t',0, +'B','o','t','t','o','m','_','A','n','d','_','L','e','f','t',0, +2,'B','o','t','t','o','m','_','A','n','d','_','R','i','g','h','t',0,'B','o','t','t','o','m','_','A','n','d','_','R','i','g', +'h','t',0,2,'L','e','f','t',0,'L','e','f','t',0,2,'L','e','f','t','_','A','n','d','_','R','i','g','h','t',0, +'L','e','f','t','_','A','n','d','_','R','i','g','h','t',0,2,'O','v','e','r','s','t','r','u','c','k',0, +'O','v','e','r','s','t','r','u','c','k',0,2,'R','i','g','h','t',0,'R','i','g','h','t',0, +2,'T','o','p',0,'T','o','p',0,2,'T','o','p','_','A','n','d','_','B','o','t','t','o','m',0, +'T','o','p','_','A','n','d','_','B','o','t','t','o','m',0,2,'T','o','p','_','A','n','d','_','B','o','t','t','o','m','_','A', +'n','d','_','R','i','g','h','t',0,'T','o','p','_','A','n','d','_','B','o','t','t','o','m','_','A','n','d','_','R','i','g','h', +'t',0,2,'T','o','p','_','A','n','d','_','L','e','f','t',0,'T','o','p','_','A','n','d','_','L','e','f','t',0, +2,'T','o','p','_','A','n','d','_','L','e','f','t','_','A','n','d','_','R','i','g','h','t',0, +'T','o','p','_','A','n','d','_','L','e','f','t','_','A','n','d','_','R','i','g','h','t',0, +2,'T','o','p','_','A','n','d','_','R','i','g','h','t',0,'T','o','p','_','A','n','d','_','R','i','g','h','t',0, +2,'V','i','s','u','a','l','_','O','r','d','e','r','_','L','e','f','t',0,'V','i','s','u','a','l','_','O','r','d','e','r','_', +'L','e','f','t',0,2,'T','o','p','_','A','n','d','_','B','o','t','t','o','m','_','A','n','d','_','L','e','f','t',0, +'T','o','p','_','A','n','d','_','B','o','t','t','o','m','_','A','n','d','_','L','e','f','t',0, +2,'I','n','S','C',0,'I','n','d','i','c','_','S','y','l','l','a','b','i','c','_','C','a','t','e','g','o','r','y',0, +2,'O','t','h','e','r',0,'O','t','h','e','r',0,2,'A','v','a','g','r','a','h','a',0,'A','v','a','g','r','a','h','a',0, +2,'B','i','n','d','u',0,'B','i','n','d','u',0,2,'B','r','a','h','m','i','_','J','o','i','n','i','n','g','_','N','u','m', +'b','e','r',0,'B','r','a','h','m','i','_','J','o','i','n','i','n','g','_','N','u','m','b','e','r',0, +2,'C','a','n','t','i','l','l','a','t','i','o','n','_','M','a','r','k',0,'C','a','n','t','i','l','l','a','t','i','o','n','_', +'M','a','r','k',0,2,'C','o','n','s','o','n','a','n','t',0,'C','o','n','s','o','n','a','n','t',0, +2,'C','o','n','s','o','n','a','n','t','_','D','e','a','d',0,'C','o','n','s','o','n','a','n','t','_','D','e','a','d',0, +2,'C','o','n','s','o','n','a','n','t','_','F','i','n','a','l',0,'C','o','n','s','o','n','a','n','t','_','F','i','n','a','l', +0,2,'C','o','n','s','o','n','a','n','t','_','H','e','a','d','_','L','e','t','t','e','r',0, +'C','o','n','s','o','n','a','n','t','_','H','e','a','d','_','L','e','t','t','e','r',0, +2,'C','o','n','s','o','n','a','n','t','_','I','n','i','t','i','a','l','_','P','o','s','t','f','i','x','e','d',0, +'C','o','n','s','o','n','a','n','t','_','I','n','i','t','i','a','l','_','P','o','s','t','f','i','x','e','d',0, +2,'C','o','n','s','o','n','a','n','t','_','K','i','l','l','e','r',0,'C','o','n','s','o','n','a','n','t','_','K','i','l','l', +'e','r',0,2,'C','o','n','s','o','n','a','n','t','_','M','e','d','i','a','l',0,'C','o','n','s','o','n','a','n','t','_','M', +'e','d','i','a','l',0,2,'C','o','n','s','o','n','a','n','t','_','P','l','a','c','e','h','o','l','d','e','r',0, +'C','o','n','s','o','n','a','n','t','_','P','l','a','c','e','h','o','l','d','e','r',0, +2,'C','o','n','s','o','n','a','n','t','_','P','r','e','c','e','d','i','n','g','_','R','e','p','h','a',0, +'C','o','n','s','o','n','a','n','t','_','P','r','e','c','e','d','i','n','g','_','R','e','p','h','a',0, +2,'C','o','n','s','o','n','a','n','t','_','P','r','e','f','i','x','e','d',0,'C','o','n','s','o','n','a','n','t','_','P','r', +'e','f','i','x','e','d',0,2,'C','o','n','s','o','n','a','n','t','_','S','u','b','j','o','i','n','e','d',0, +'C','o','n','s','o','n','a','n','t','_','S','u','b','j','o','i','n','e','d',0, +2,'C','o','n','s','o','n','a','n','t','_','S','u','c','c','e','e','d','i','n','g','_','R','e','p','h','a',0, +'C','o','n','s','o','n','a','n','t','_','S','u','c','c','e','e','d','i','n','g','_','R','e','p','h','a',0, +2,'C','o','n','s','o','n','a','n','t','_','W','i','t','h','_','S','t','a','c','k','e','r',0, +'C','o','n','s','o','n','a','n','t','_','W','i','t','h','_','S','t','a','c','k','e','r',0, +2,'G','e','m','i','n','a','t','i','o','n','_','M','a','r','k',0,'G','e','m','i','n','a','t','i','o','n','_','M','a','r','k', +0,2,'I','n','v','i','s','i','b','l','e','_','S','t','a','c','k','e','r',0,'I','n','v','i','s','i','b','l','e','_','S','t', +'a','c','k','e','r',0,2,'J','o','i','n','e','r',0,'J','o','i','n','e','r',0, +2,'M','o','d','i','f','y','i','n','g','_','L','e','t','t','e','r',0,'M','o','d','i','f','y','i','n','g','_','L','e','t','t', +'e','r',0,2,'N','o','n','_','J','o','i','n','e','r',0,'N','o','n','_','J','o','i','n','e','r',0, +2,'N','u','k','t','a',0,'N','u','k','t','a',0,2,'N','u','m','b','e','r',0,'N','u','m','b','e','r',0, +2,'N','u','m','b','e','r','_','J','o','i','n','e','r',0,'N','u','m','b','e','r','_','J','o','i','n','e','r',0, +2,'P','u','r','e','_','K','i','l','l','e','r',0,'P','u','r','e','_','K','i','l','l','e','r',0, +2,'R','e','g','i','s','t','e','r','_','S','h','i','f','t','e','r',0,'R','e','g','i','s','t','e','r','_','S','h','i','f','t', +'e','r',0,2,'S','y','l','l','a','b','l','e','_','M','o','d','i','f','i','e','r',0,'S','y','l','l','a','b','l','e','_','M', +'o','d','i','f','i','e','r',0,2,'T','o','n','e','_','L','e','t','t','e','r',0,'T','o','n','e','_','L','e','t','t','e','r', +0,2,'T','o','n','e','_','M','a','r','k',0,'T','o','n','e','_','M','a','r','k',0, +2,'V','i','r','a','m','a',0,'V','i','r','a','m','a',0,2,'V','i','s','a','r','g','a',0, +'V','i','s','a','r','g','a',0,2,'V','o','w','e','l',0,'V','o','w','e','l',0, +2,'V','o','w','e','l','_','D','e','p','e','n','d','e','n','t',0,'V','o','w','e','l','_','D','e','p','e','n','d','e','n','t', +0,2,'V','o','w','e','l','_','I','n','d','e','p','e','n','d','e','n','t',0,'V','o','w','e','l','_','I','n','d','e','p','e', +'n','d','e','n','t',0,2,'v','o',0,'V','e','r','t','i','c','a','l','_','O','r','i','e','n','t','a','t','i','o','n',0, +2,'R',0,'R','o','t','a','t','e','d',0,2,'T','r',0,'T','r','a','n','s','f','o','r','m','e','d','_','R','o','t','a','t', +'e','d',0,2,'T','u',0,'T','r','a','n','s','f','o','r','m','e','d','_','U','p','r','i','g','h','t',0, +2,'U',0,'U','p','r','i','g','h','t',0,2,'g','c','m',0,'G','e','n','e','r','a','l','_','C','a','t','e','g','o','r','y', +'_','M','a','s','k',0,2,'C',0,'O','t','h','e','r',0,2,'L',0,'L','e','t','t','e','r',0, +2,'L','C',0,'C','a','s','e','d','_','L','e','t','t','e','r',0, +3,'M',0,'M','a','r','k',0,'C','o','m','b','i','n','i','n','g','_','M','a','r','k',0, +2,'N',0,'N','u','m','b','e','r',0,3,'P',0,'P','u','n','c','t','u','a','t','i','o','n',0, +'p','u','n','c','t',0,2,'S',0,'S','y','m','b','o','l',0, +2,'Z',0,'S','e','p','a','r','a','t','o','r',0,2,'n','v',0,'N','u','m','e','r','i','c','_','V','a','l','u','e',0, +2,'a','g','e',0,'A','g','e',0,2,'b','m','g',0,'B','i','d','i','_','M','i','r','r','o','r','i','n','g','_','G','l','y', +'p','h',0,2,'c','f',0,'C','a','s','e','_','F','o','l','d','i','n','g',0, +2,'i','s','c',0,'I','S','O','_','C','o','m','m','e','n','t',0, +2,'l','c',0,'L','o','w','e','r','c','a','s','e','_','M','a','p','p','i','n','g',0, +2,'n','a',0,'N','a','m','e',0,3,'s','c','f',0,'S','i','m','p','l','e','_','C','a','s','e','_','F','o','l','d','i','n', +'g',0,'s','f','c',0,2,'s','l','c',0,'S','i','m','p','l','e','_','L','o','w','e','r','c','a','s','e','_','M','a','p','p', +'i','n','g',0,2,'s','t','c',0,'S','i','m','p','l','e','_','T','i','t','l','e','c','a','s','e','_','M','a','p','p','i','n', +'g',0,2,'s','u','c',0,'S','i','m','p','l','e','_','U','p','p','e','r','c','a','s','e','_','M','a','p','p','i','n','g',0, +2,'t','c',0,'T','i','t','l','e','c','a','s','e','_','M','a','p','p','i','n','g',0, +2,'n','a','1',0,'U','n','i','c','o','d','e','_','1','_','N','a','m','e',0, +2,'u','c',0,'U','p','p','e','r','c','a','s','e','_','M','a','p','p','i','n','g',0, +2,'b','p','b',0,'B','i','d','i','_','P','a','i','r','e','d','_','B','r','a','c','k','e','t',0, +2,'s','c','x',0,'S','c','r','i','p','t','_','E','x','t','e','n','s','i','o','n','s',0 +}; + +U_NAMESPACE_END + +#endif // INCLUDED_FROM_PROPNAME_CPP diff --git a/deps/icu-small/source/common/propsvec.cpp b/deps/icu-small/source/common/propsvec.cpp index e5caa4b9d2c46f..2bfd516805fa36 100644 --- a/deps/icu-small/source/common/propsvec.cpp +++ b/deps/icu-small/source/common/propsvec.cpp @@ -1,529 +1,529 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: propsvec.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002feb22 -* created by: Markus W. Scherer -* -* Store bits (Unicode character properties) in bit set vectors. -*/ - -#include -#include "unicode/utypes.h" -#include "cmemory.h" -#include "utrie.h" -#include "utrie2.h" -#include "uarrsort.h" -#include "propsvec.h" -#include "uassert.h" - -struct UPropsVectors { - uint32_t *v; - int32_t columns; /* number of columns, plus two for start & limit values */ - int32_t maxRows; - int32_t rows; - int32_t prevRow; /* search optimization: remember last row seen */ - UBool isCompacted; -}; - -#define UPVEC_INITIAL_ROWS (1<<12) -#define UPVEC_MEDIUM_ROWS ((int32_t)1<<16) -#define UPVEC_MAX_ROWS (UPVEC_MAX_CP+1) - -U_CAPI UPropsVectors * U_EXPORT2 -upvec_open(int32_t columns, UErrorCode *pErrorCode) { - UPropsVectors *pv; - uint32_t *v, *row; - uint32_t cp; - - if(U_FAILURE(*pErrorCode)) { - return NULL; - } - if(columns<1) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - columns+=2; /* count range start and limit columns */ - - pv=(UPropsVectors *)uprv_malloc(sizeof(UPropsVectors)); - v=(uint32_t *)uprv_malloc(UPVEC_INITIAL_ROWS*columns*4); - if(pv==NULL || v==NULL) { - uprv_free(pv); - uprv_free(v); - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uprv_memset(pv, 0, sizeof(UPropsVectors)); - pv->v=v; - pv->columns=columns; - pv->maxRows=UPVEC_INITIAL_ROWS; - pv->rows=2+(UPVEC_MAX_CP-UPVEC_FIRST_SPECIAL_CP); - - /* set the all-Unicode row and the special-value rows */ - row=pv->v; - uprv_memset(row, 0, pv->rows*columns*4); - row[0]=0; - row[1]=0x110000; - row+=columns; - for(cp=UPVEC_FIRST_SPECIAL_CP; cp<=UPVEC_MAX_CP; ++cp) { - row[0]=cp; - row[1]=cp+1; - row+=columns; - } - return pv; -} - -U_CAPI void U_EXPORT2 -upvec_close(UPropsVectors *pv) { - if(pv!=NULL) { - uprv_free(pv->v); - uprv_free(pv); - } -} - -static uint32_t * -_findRow(UPropsVectors *pv, UChar32 rangeStart) { - uint32_t *row; - int32_t columns, i, start, limit, prevRow; - - columns=pv->columns; - limit=pv->rows; - prevRow=pv->prevRow; - - /* check the vicinity of the last-seen row (start searching with an unrolled loop) */ - row=pv->v+prevRow*columns; - if(rangeStart>=(UChar32)row[0]) { - if(rangeStart<(UChar32)row[1]) { - /* same row as last seen */ - return row; - } else if(rangeStart<(UChar32)(row+=columns)[1]) { - /* next row after the last one */ - pv->prevRow=prevRow+1; - return row; - } else if(rangeStart<(UChar32)(row+=columns)[1]) { - /* second row after the last one */ - pv->prevRow=prevRow+2; - return row; - } else if((rangeStart-(UChar32)row[1])<10) { - /* we are close, continue looping */ - prevRow+=2; - do { - ++prevRow; - row+=columns; - } while(rangeStart>=(UChar32)row[1]); - pv->prevRow=prevRow; - return row; - } - } else if(rangeStart<(UChar32)pv->v[1]) { - /* the very first row */ - pv->prevRow=0; - return pv->v; - } - - /* do a binary search for the start of the range */ - start=0; - while(startv+i*columns; - if(rangeStart<(UChar32)row[0]) { - limit=i; - } else if(rangeStart<(UChar32)row[1]) { - pv->prevRow=i; - return row; - } else { - start=i; - } - } - - /* must be found because all ranges together always cover all of Unicode */ - pv->prevRow=start; - return pv->v+start*columns; -} - -U_CAPI void U_EXPORT2 -upvec_setValue(UPropsVectors *pv, - UChar32 start, UChar32 end, - int32_t column, - uint32_t value, uint32_t mask, - UErrorCode *pErrorCode) { - uint32_t *firstRow, *lastRow; - int32_t columns; - UChar32 limit; - UBool splitFirstRow, splitLastRow; - - /* argument checking */ - if(U_FAILURE(*pErrorCode)) { - return; - } - if( pv==NULL || - start<0 || start>end || end>UPVEC_MAX_CP || - column<0 || column>=(pv->columns-2) - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if(pv->isCompacted) { - *pErrorCode=U_NO_WRITE_PERMISSION; - return; - } - limit=end+1; - - /* initialize */ - columns=pv->columns; - column+=2; /* skip range start and limit columns */ - value&=mask; - - /* find the rows whose ranges overlap with the input range */ - - /* find the first and last rows, always successful */ - firstRow=_findRow(pv, start); - lastRow=_findRow(pv, end); - - /* - * Rows need to be split if they partially overlap with the - * input range (only possible for the first and last rows) - * and if their value differs from the input value. - */ - splitFirstRow= (UBool)(start!=(UChar32)firstRow[0] && value!=(firstRow[column]&mask)); - splitLastRow= (UBool)(limit!=(UChar32)lastRow[1] && value!=(lastRow[column]&mask)); - - /* split first/last rows if necessary */ - if(splitFirstRow || splitLastRow) { - int32_t count, rows; - - rows=pv->rows; - if((rows+splitFirstRow+splitLastRow)>pv->maxRows) { - uint32_t *newVectors; - int32_t newMaxRows; - - if(pv->maxRowsmaxRowsv, (size_t)rows*columns*4); - firstRow=newVectors+(firstRow-pv->v); - lastRow=newVectors+(lastRow-pv->v); - uprv_free(pv->v); - pv->v=newVectors; - pv->maxRows=newMaxRows; - } - - /* count the number of row cells to move after the last row, and move them */ - count = (int32_t)((pv->v+rows*columns)-(lastRow+columns)); - if(count>0) { - uprv_memmove( - lastRow+(1+splitFirstRow+splitLastRow)*columns, - lastRow+columns, - count*4); - } - pv->rows=rows+splitFirstRow+splitLastRow; - - /* split the first row, and move the firstRow pointer to the second part */ - if(splitFirstRow) { - /* copy all affected rows up one and move the lastRow pointer */ - count = (int32_t)((lastRow-firstRow)+columns); - uprv_memmove(firstRow+columns, firstRow, (size_t)count*4); - lastRow+=columns; - - /* split the range and move the firstRow pointer */ - firstRow[1]=firstRow[columns]=(uint32_t)start; - firstRow+=columns; - } - - /* split the last row */ - if(splitLastRow) { - /* copy the last row data */ - uprv_memcpy(lastRow+columns, lastRow, (size_t)columns*4); - - /* split the range and move the firstRow pointer */ - lastRow[1]=lastRow[columns]=(uint32_t)limit; - } - } - - /* set the "row last seen" to the last row for the range */ - pv->prevRow=(int32_t)((lastRow-(pv->v))/columns); - - /* set the input value in all remaining rows */ - firstRow+=column; - lastRow+=column; - mask=~mask; - for(;;) { - *firstRow=(*firstRow&mask)|value; - if(firstRow==lastRow) { - break; - } - firstRow+=columns; - } -} - -U_CAPI uint32_t U_EXPORT2 -upvec_getValue(const UPropsVectors *pv, UChar32 c, int32_t column) { - uint32_t *row; - UPropsVectors *ncpv; - - if(pv->isCompacted || c<0 || c>UPVEC_MAX_CP || column<0 || column>=(pv->columns-2)) { - return 0; - } - ncpv=(UPropsVectors *)pv; - row=_findRow(ncpv, c); - return row[2+column]; -} - -U_CAPI uint32_t * U_EXPORT2 -upvec_getRow(const UPropsVectors *pv, int32_t rowIndex, - UChar32 *pRangeStart, UChar32 *pRangeEnd) { - uint32_t *row; - int32_t columns; - - if(pv->isCompacted || rowIndex<0 || rowIndex>=pv->rows) { - return NULL; - } - - columns=pv->columns; - row=pv->v+rowIndex*columns; - if(pRangeStart!=NULL) { - *pRangeStart=(UChar32)row[0]; - } - if(pRangeEnd!=NULL) { - *pRangeEnd=(UChar32)row[1]-1; - } - return row+2; -} - -static int32_t U_CALLCONV -upvec_compareRows(const void *context, const void *l, const void *r) { - const uint32_t *left=(const uint32_t *)l, *right=(const uint32_t *)r; - const UPropsVectors *pv=(const UPropsVectors *)context; - int32_t i, count, columns; - - count=columns=pv->columns; /* includes start/limit columns */ - - /* start comparing after start/limit but wrap around to them */ - i=2; - do { - if(left[i]!=right[i]) { - return left[i]0); - - return 0; -} - -U_CAPI void U_EXPORT2 -upvec_compact(UPropsVectors *pv, UPVecCompactHandler *handler, void *context, UErrorCode *pErrorCode) { - uint32_t *row; - int32_t i, columns, valueColumns, rows, count; - UChar32 start, limit; - - /* argument checking */ - if(U_FAILURE(*pErrorCode)) { - return; - } - if(handler==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if(pv->isCompacted) { - return; - } - - /* Set the flag now: Sorting and compacting destroys the builder data structure. */ - pv->isCompacted=true; - - rows=pv->rows; - columns=pv->columns; - U_ASSERT(columns>=3); /* upvec_open asserts this */ - valueColumns=columns-2; /* not counting start & limit */ - - /* sort the properties vectors to find unique vector values */ - uprv_sortArray(pv->v, rows, columns*4, - upvec_compareRows, pv, false, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return; - } - - /* - * Find and set the special values. - * This has to do almost the same work as the compaction below, - * to find the indexes where the special-value rows will move. - */ - row=pv->v; - count=-valueColumns; - for(i=0; i=UPVEC_FIRST_SPECIAL_CP) { - handler(context, start, start, count, row+2, valueColumns, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return; - } - } - - row+=columns; - } - - /* count is at the beginning of the last vector, add valueColumns to include that last vector */ - count+=valueColumns; - - /* Call the handler once more to signal the start of delivering real values. */ - handler(context, UPVEC_START_REAL_VALUES_CP, UPVEC_START_REAL_VALUES_CP, - count, row-valueColumns, valueColumns, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return; - } - - /* - * Move vector contents up to a contiguous array with only unique - * vector values, and call the handler function for each vector. - * - * This destroys the Properties Vector structure and replaces it - * with an array of just vector values. - */ - row=pv->v; - count=-valueColumns; - for(i=0; iv+count, valueColumns*4)) { - count+=valueColumns; - uprv_memmove(pv->v+count, row+2, (size_t)valueColumns*4); - } - - if(startv+count, valueColumns, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return; - } - } - - row+=columns; - } - - /* count is at the beginning of the last vector, add one to include that last vector */ - pv->rows=count/valueColumns+1; -} - -U_CAPI const uint32_t * U_EXPORT2 -upvec_getArray(const UPropsVectors *pv, int32_t *pRows, int32_t *pColumns) { - if(!pv->isCompacted) { - return NULL; - } - if(pRows!=NULL) { - *pRows=pv->rows; - } - if(pColumns!=NULL) { - *pColumns=pv->columns-2; - } - return pv->v; -} - -U_CAPI uint32_t * U_EXPORT2 -upvec_cloneArray(const UPropsVectors *pv, - int32_t *pRows, int32_t *pColumns, UErrorCode *pErrorCode) { - uint32_t *clonedArray; - int32_t byteLength; - - if(U_FAILURE(*pErrorCode)) { - return NULL; - } - if(!pv->isCompacted) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - byteLength=pv->rows*(pv->columns-2)*4; - clonedArray=(uint32_t *)uprv_malloc(byteLength); - if(clonedArray==NULL) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uprv_memcpy(clonedArray, pv->v, byteLength); - if(pRows!=NULL) { - *pRows=pv->rows; - } - if(pColumns!=NULL) { - *pColumns=pv->columns-2; - } - return clonedArray; -} - -U_CAPI UTrie2 * U_EXPORT2 -upvec_compactToUTrie2WithRowIndexes(UPropsVectors *pv, UErrorCode *pErrorCode) { - UPVecToUTrie2Context toUTrie2={ NULL, 0, 0, 0 }; - upvec_compact(pv, upvec_compactToUTrie2Handler, &toUTrie2, pErrorCode); - utrie2_freeze(toUTrie2.trie, UTRIE2_16_VALUE_BITS, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - utrie2_close(toUTrie2.trie); - toUTrie2.trie=NULL; - } - return toUTrie2.trie; -} - -/* - * TODO(markus): Add upvec_16BitsToUTrie2() function that enumerates all rows, extracts - * some 16-bit field and builds and returns a UTrie2. - */ - -U_CAPI void U_CALLCONV -upvec_compactToUTrie2Handler(void *context, - UChar32 start, UChar32 end, - int32_t rowIndex, uint32_t *row, int32_t columns, - UErrorCode *pErrorCode) { - (void)row; - (void)columns; - UPVecToUTrie2Context *toUTrie2=(UPVecToUTrie2Context *)context; - if(starttrie, start, end, (uint32_t)rowIndex, true, pErrorCode); - } else { - switch(start) { - case UPVEC_INITIAL_VALUE_CP: - toUTrie2->initialValue=rowIndex; - break; - case UPVEC_ERROR_VALUE_CP: - toUTrie2->errorValue=rowIndex; - break; - case UPVEC_START_REAL_VALUES_CP: - toUTrie2->maxValue=rowIndex; - if(rowIndex>0xffff) { - /* too many rows for a 16-bit trie */ - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - } else { - toUTrie2->trie=utrie2_open(toUTrie2->initialValue, - toUTrie2->errorValue, pErrorCode); - } - break; - default: - break; - } - } -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: propsvec.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002feb22 +* created by: Markus W. Scherer +* +* Store bits (Unicode character properties) in bit set vectors. +*/ + +#include +#include "unicode/utypes.h" +#include "cmemory.h" +#include "utrie.h" +#include "utrie2.h" +#include "uarrsort.h" +#include "propsvec.h" +#include "uassert.h" + +struct UPropsVectors { + uint32_t *v; + int32_t columns; /* number of columns, plus two for start & limit values */ + int32_t maxRows; + int32_t rows; + int32_t prevRow; /* search optimization: remember last row seen */ + UBool isCompacted; +}; + +#define UPVEC_INITIAL_ROWS (1<<12) +#define UPVEC_MEDIUM_ROWS ((int32_t)1<<16) +#define UPVEC_MAX_ROWS (UPVEC_MAX_CP+1) + +U_CAPI UPropsVectors * U_EXPORT2 +upvec_open(int32_t columns, UErrorCode *pErrorCode) { + UPropsVectors *pv; + uint32_t *v, *row; + uint32_t cp; + + if(U_FAILURE(*pErrorCode)) { + return nullptr; + } + if(columns<1) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + columns+=2; /* count range start and limit columns */ + + pv=(UPropsVectors *)uprv_malloc(sizeof(UPropsVectors)); + v=(uint32_t *)uprv_malloc(UPVEC_INITIAL_ROWS*columns*4); + if(pv==nullptr || v==nullptr) { + uprv_free(pv); + uprv_free(v); + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memset(pv, 0, sizeof(UPropsVectors)); + pv->v=v; + pv->columns=columns; + pv->maxRows=UPVEC_INITIAL_ROWS; + pv->rows=2+(UPVEC_MAX_CP-UPVEC_FIRST_SPECIAL_CP); + + /* set the all-Unicode row and the special-value rows */ + row=pv->v; + uprv_memset(row, 0, pv->rows*columns*4); + row[0]=0; + row[1]=0x110000; + row+=columns; + for(cp=UPVEC_FIRST_SPECIAL_CP; cp<=UPVEC_MAX_CP; ++cp) { + row[0]=cp; + row[1]=cp+1; + row+=columns; + } + return pv; +} + +U_CAPI void U_EXPORT2 +upvec_close(UPropsVectors *pv) { + if(pv!=nullptr) { + uprv_free(pv->v); + uprv_free(pv); + } +} + +static uint32_t * +_findRow(UPropsVectors *pv, UChar32 rangeStart) { + uint32_t *row; + int32_t columns, i, start, limit, prevRow; + + columns=pv->columns; + limit=pv->rows; + prevRow=pv->prevRow; + + /* check the vicinity of the last-seen row (start searching with an unrolled loop) */ + row=pv->v+prevRow*columns; + if(rangeStart>=(UChar32)row[0]) { + if(rangeStart<(UChar32)row[1]) { + /* same row as last seen */ + return row; + } else if(rangeStart<(UChar32)(row+=columns)[1]) { + /* next row after the last one */ + pv->prevRow=prevRow+1; + return row; + } else if(rangeStart<(UChar32)(row+=columns)[1]) { + /* second row after the last one */ + pv->prevRow=prevRow+2; + return row; + } else if((rangeStart-(UChar32)row[1])<10) { + /* we are close, continue looping */ + prevRow+=2; + do { + ++prevRow; + row+=columns; + } while(rangeStart>=(UChar32)row[1]); + pv->prevRow=prevRow; + return row; + } + } else if(rangeStart<(UChar32)pv->v[1]) { + /* the very first row */ + pv->prevRow=0; + return pv->v; + } + + /* do a binary search for the start of the range */ + start=0; + while(startv+i*columns; + if(rangeStart<(UChar32)row[0]) { + limit=i; + } else if(rangeStart<(UChar32)row[1]) { + pv->prevRow=i; + return row; + } else { + start=i; + } + } + + /* must be found because all ranges together always cover all of Unicode */ + pv->prevRow=start; + return pv->v+start*columns; +} + +U_CAPI void U_EXPORT2 +upvec_setValue(UPropsVectors *pv, + UChar32 start, UChar32 end, + int32_t column, + uint32_t value, uint32_t mask, + UErrorCode *pErrorCode) { + uint32_t *firstRow, *lastRow; + int32_t columns; + UChar32 limit; + UBool splitFirstRow, splitLastRow; + + /* argument checking */ + if(U_FAILURE(*pErrorCode)) { + return; + } + if( pv==nullptr || + start<0 || start>end || end>UPVEC_MAX_CP || + column<0 || column>=(pv->columns-2) + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if(pv->isCompacted) { + *pErrorCode=U_NO_WRITE_PERMISSION; + return; + } + limit=end+1; + + /* initialize */ + columns=pv->columns; + column+=2; /* skip range start and limit columns */ + value&=mask; + + /* find the rows whose ranges overlap with the input range */ + + /* find the first and last rows, always successful */ + firstRow=_findRow(pv, start); + lastRow=_findRow(pv, end); + + /* + * Rows need to be split if they partially overlap with the + * input range (only possible for the first and last rows) + * and if their value differs from the input value. + */ + splitFirstRow= (UBool)(start!=(UChar32)firstRow[0] && value!=(firstRow[column]&mask)); + splitLastRow= (UBool)(limit!=(UChar32)lastRow[1] && value!=(lastRow[column]&mask)); + + /* split first/last rows if necessary */ + if(splitFirstRow || splitLastRow) { + int32_t count, rows; + + rows=pv->rows; + if((rows+splitFirstRow+splitLastRow)>pv->maxRows) { + uint32_t *newVectors; + int32_t newMaxRows; + + if(pv->maxRowsmaxRowsv, (size_t)rows*columns*4); + firstRow=newVectors+(firstRow-pv->v); + lastRow=newVectors+(lastRow-pv->v); + uprv_free(pv->v); + pv->v=newVectors; + pv->maxRows=newMaxRows; + } + + /* count the number of row cells to move after the last row, and move them */ + count = (int32_t)((pv->v+rows*columns)-(lastRow+columns)); + if(count>0) { + uprv_memmove( + lastRow+(1+splitFirstRow+splitLastRow)*columns, + lastRow+columns, + count*4); + } + pv->rows=rows+splitFirstRow+splitLastRow; + + /* split the first row, and move the firstRow pointer to the second part */ + if(splitFirstRow) { + /* copy all affected rows up one and move the lastRow pointer */ + count = (int32_t)((lastRow-firstRow)+columns); + uprv_memmove(firstRow+columns, firstRow, (size_t)count*4); + lastRow+=columns; + + /* split the range and move the firstRow pointer */ + firstRow[1]=firstRow[columns]=(uint32_t)start; + firstRow+=columns; + } + + /* split the last row */ + if(splitLastRow) { + /* copy the last row data */ + uprv_memcpy(lastRow+columns, lastRow, (size_t)columns*4); + + /* split the range and move the firstRow pointer */ + lastRow[1]=lastRow[columns]=(uint32_t)limit; + } + } + + /* set the "row last seen" to the last row for the range */ + pv->prevRow=(int32_t)((lastRow-(pv->v))/columns); + + /* set the input value in all remaining rows */ + firstRow+=column; + lastRow+=column; + mask=~mask; + for(;;) { + *firstRow=(*firstRow&mask)|value; + if(firstRow==lastRow) { + break; + } + firstRow+=columns; + } +} + +U_CAPI uint32_t U_EXPORT2 +upvec_getValue(const UPropsVectors *pv, UChar32 c, int32_t column) { + uint32_t *row; + UPropsVectors *ncpv; + + if(pv->isCompacted || c<0 || c>UPVEC_MAX_CP || column<0 || column>=(pv->columns-2)) { + return 0; + } + ncpv=(UPropsVectors *)pv; + row=_findRow(ncpv, c); + return row[2+column]; +} + +U_CAPI uint32_t * U_EXPORT2 +upvec_getRow(const UPropsVectors *pv, int32_t rowIndex, + UChar32 *pRangeStart, UChar32 *pRangeEnd) { + uint32_t *row; + int32_t columns; + + if(pv->isCompacted || rowIndex<0 || rowIndex>=pv->rows) { + return nullptr; + } + + columns=pv->columns; + row=pv->v+rowIndex*columns; + if(pRangeStart!=nullptr) { + *pRangeStart=(UChar32)row[0]; + } + if(pRangeEnd!=nullptr) { + *pRangeEnd=(UChar32)row[1]-1; + } + return row+2; +} + +static int32_t U_CALLCONV +upvec_compareRows(const void *context, const void *l, const void *r) { + const uint32_t *left=(const uint32_t *)l, *right=(const uint32_t *)r; + const UPropsVectors *pv=(const UPropsVectors *)context; + int32_t i, count, columns; + + count=columns=pv->columns; /* includes start/limit columns */ + + /* start comparing after start/limit but wrap around to them */ + i=2; + do { + if(left[i]!=right[i]) { + return left[i]0); + + return 0; +} + +U_CAPI void U_EXPORT2 +upvec_compact(UPropsVectors *pv, UPVecCompactHandler *handler, void *context, UErrorCode *pErrorCode) { + uint32_t *row; + int32_t i, columns, valueColumns, rows, count; + UChar32 start, limit; + + /* argument checking */ + if(U_FAILURE(*pErrorCode)) { + return; + } + if(handler==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if(pv->isCompacted) { + return; + } + + /* Set the flag now: Sorting and compacting destroys the builder data structure. */ + pv->isCompacted=true; + + rows=pv->rows; + columns=pv->columns; + U_ASSERT(columns>=3); /* upvec_open asserts this */ + valueColumns=columns-2; /* not counting start & limit */ + + /* sort the properties vectors to find unique vector values */ + uprv_sortArray(pv->v, rows, columns*4, + upvec_compareRows, pv, false, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return; + } + + /* + * Find and set the special values. + * This has to do almost the same work as the compaction below, + * to find the indexes where the special-value rows will move. + */ + row=pv->v; + count=-valueColumns; + for(i=0; i=UPVEC_FIRST_SPECIAL_CP) { + handler(context, start, start, count, row+2, valueColumns, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return; + } + } + + row+=columns; + } + + /* count is at the beginning of the last vector, add valueColumns to include that last vector */ + count+=valueColumns; + + /* Call the handler once more to signal the start of delivering real values. */ + handler(context, UPVEC_START_REAL_VALUES_CP, UPVEC_START_REAL_VALUES_CP, + count, row-valueColumns, valueColumns, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return; + } + + /* + * Move vector contents up to a contiguous array with only unique + * vector values, and call the handler function for each vector. + * + * This destroys the Properties Vector structure and replaces it + * with an array of just vector values. + */ + row=pv->v; + count=-valueColumns; + for(i=0; iv+count, valueColumns*4)) { + count+=valueColumns; + uprv_memmove(pv->v+count, row+2, (size_t)valueColumns*4); + } + + if(startv+count, valueColumns, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return; + } + } + + row+=columns; + } + + /* count is at the beginning of the last vector, add one to include that last vector */ + pv->rows=count/valueColumns+1; +} + +U_CAPI const uint32_t * U_EXPORT2 +upvec_getArray(const UPropsVectors *pv, int32_t *pRows, int32_t *pColumns) { + if(!pv->isCompacted) { + return nullptr; + } + if(pRows!=nullptr) { + *pRows=pv->rows; + } + if(pColumns!=nullptr) { + *pColumns=pv->columns-2; + } + return pv->v; +} + +U_CAPI uint32_t * U_EXPORT2 +upvec_cloneArray(const UPropsVectors *pv, + int32_t *pRows, int32_t *pColumns, UErrorCode *pErrorCode) { + uint32_t *clonedArray; + int32_t byteLength; + + if(U_FAILURE(*pErrorCode)) { + return nullptr; + } + if(!pv->isCompacted) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + byteLength=pv->rows*(pv->columns-2)*4; + clonedArray=(uint32_t *)uprv_malloc(byteLength); + if(clonedArray==nullptr) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memcpy(clonedArray, pv->v, byteLength); + if(pRows!=nullptr) { + *pRows=pv->rows; + } + if(pColumns!=nullptr) { + *pColumns=pv->columns-2; + } + return clonedArray; +} + +U_CAPI UTrie2 * U_EXPORT2 +upvec_compactToUTrie2WithRowIndexes(UPropsVectors *pv, UErrorCode *pErrorCode) { + UPVecToUTrie2Context toUTrie2={ nullptr, 0, 0, 0 }; + upvec_compact(pv, upvec_compactToUTrie2Handler, &toUTrie2, pErrorCode); + utrie2_freeze(toUTrie2.trie, UTRIE2_16_VALUE_BITS, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + utrie2_close(toUTrie2.trie); + toUTrie2.trie=nullptr; + } + return toUTrie2.trie; +} + +/* + * TODO(markus): Add upvec_16BitsToUTrie2() function that enumerates all rows, extracts + * some 16-bit field and builds and returns a UTrie2. + */ + +U_CAPI void U_CALLCONV +upvec_compactToUTrie2Handler(void *context, + UChar32 start, UChar32 end, + int32_t rowIndex, uint32_t *row, int32_t columns, + UErrorCode *pErrorCode) { + (void)row; + (void)columns; + UPVecToUTrie2Context *toUTrie2=(UPVecToUTrie2Context *)context; + if(starttrie, start, end, (uint32_t)rowIndex, true, pErrorCode); + } else { + switch(start) { + case UPVEC_INITIAL_VALUE_CP: + toUTrie2->initialValue=rowIndex; + break; + case UPVEC_ERROR_VALUE_CP: + toUTrie2->errorValue=rowIndex; + break; + case UPVEC_START_REAL_VALUES_CP: + toUTrie2->maxValue=rowIndex; + if(rowIndex>0xffff) { + /* too many rows for a 16-bit trie */ + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + } else { + toUTrie2->trie=utrie2_open(toUTrie2->initialValue, + toUTrie2->errorValue, pErrorCode); + } + break; + default: + break; + } + } +} diff --git a/deps/icu-small/source/common/propsvec.h b/deps/icu-small/source/common/propsvec.h index 39080615ea3811..01fe6daaf691a7 100644 --- a/deps/icu-small/source/common/propsvec.h +++ b/deps/icu-small/source/common/propsvec.h @@ -1,178 +1,178 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2010, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: propsvec.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002feb22 -* created by: Markus W. Scherer -* -* Store bits (Unicode character properties) in bit set vectors. -*/ - -#ifndef __UPROPSVEC_H__ -#define __UPROPSVEC_H__ - -#include "unicode/utypes.h" -#include "utrie.h" -#include "utrie2.h" - -U_CDECL_BEGIN - -/** - * Unicode Properties Vectors associated with code point ranges. - * - * Rows of uint32_t integers in a contiguous array store - * the range limits and the properties vectors. - * - * Logically, each row has a certain number of uint32_t values, - * which is set via the upvec_open() "columns" parameter. - * - * Internally, two additional columns are stored. - * In each internal row, - * row[0] contains the start code point and - * row[1] contains the limit code point, - * which is the start of the next range. - * - * Initially, there is only one "normal" row for - * range [0..0x110000[ with values 0. - * There are additional rows for special purposes, see UPVEC_FIRST_SPECIAL_CP. - * - * It would be possible to store only one range boundary per row, - * but self-contained rows allow to later sort them by contents. - */ -struct UPropsVectors; -typedef struct UPropsVectors UPropsVectors; - -/* - * Special pseudo code points for storing the initialValue and the errorValue, - * which are used to initialize a UTrie2 or similar. - */ -#define UPVEC_FIRST_SPECIAL_CP 0x110000 -#define UPVEC_INITIAL_VALUE_CP 0x110000 -#define UPVEC_ERROR_VALUE_CP 0x110001 -#define UPVEC_MAX_CP 0x110001 - -/* - * Special pseudo code point used in upvec_compact() signalling the end of - * delivering special values and the beginning of delivering real ones. - * Stable value, unlike UPVEC_MAX_CP which might grow over time. - */ -#define UPVEC_START_REAL_VALUES_CP 0x200000 - -/* - * Open a UPropsVectors object. - * @param columns Number of value integers (uint32_t) per row. - */ -U_CAPI UPropsVectors * U_EXPORT2 -upvec_open(int32_t columns, UErrorCode *pErrorCode); - -U_CAPI void U_EXPORT2 -upvec_close(UPropsVectors *pv); - -/* - * In rows for code points [start..end], select the column, - * reset the mask bits and set the value bits (ANDed with the mask). - * - * Will set U_NO_WRITE_PERMISSION if called after upvec_compact(). - */ -U_CAPI void U_EXPORT2 -upvec_setValue(UPropsVectors *pv, - UChar32 start, UChar32 end, - int32_t column, - uint32_t value, uint32_t mask, - UErrorCode *pErrorCode); - -/* - * Logically const but must not be used on the same pv concurrently! - * Always returns 0 if called after upvec_compact(). - */ -U_CAPI uint32_t U_EXPORT2 -upvec_getValue(const UPropsVectors *pv, UChar32 c, int32_t column); - -/* - * pRangeStart and pRangeEnd can be NULL. - * @return NULL if rowIndex out of range and for illegal arguments, - * or if called after upvec_compact() - */ -U_CAPI uint32_t * U_EXPORT2 -upvec_getRow(const UPropsVectors *pv, int32_t rowIndex, - UChar32 *pRangeStart, UChar32 *pRangeEnd); - -/* - * Compact the vectors: - * - modify the memory - * - keep only unique vectors - * - store them contiguously from the beginning of the memory - * - for each (non-unique) row, call the handler function - * - * The handler's rowIndex is the index of the row in the compacted - * memory block. - * (Therefore, it starts at 0 increases in increments of the columns value.) - * - * In a first phase, only special values are delivered (each exactly once), - * with start==end both equalling a special pseudo code point. - * Then the handler is called once more with start==end==UPVEC_START_REAL_VALUES_CP - * where rowIndex is the length of the compacted array, - * and the row is arbitrary (but not NULL). - * Then, in the second phase, the handler is called for each row of real values. - */ -typedef void U_CALLCONV -UPVecCompactHandler(void *context, - UChar32 start, UChar32 end, - int32_t rowIndex, uint32_t *row, int32_t columns, - UErrorCode *pErrorCode); - -U_CAPI void U_EXPORT2 -upvec_compact(UPropsVectors *pv, UPVecCompactHandler *handler, void *context, UErrorCode *pErrorCode); - -/* - * Get the vectors array after calling upvec_compact(). - * The caller must not modify nor release the returned array. - * Returns NULL if called before upvec_compact(). - */ -U_CAPI const uint32_t * U_EXPORT2 -upvec_getArray(const UPropsVectors *pv, int32_t *pRows, int32_t *pColumns); - -/* - * Get a clone of the vectors array after calling upvec_compact(). - * The caller owns the returned array and must uprv_free() it. - * Returns NULL if called before upvec_compact(). - */ -U_CAPI uint32_t * U_EXPORT2 -upvec_cloneArray(const UPropsVectors *pv, - int32_t *pRows, int32_t *pColumns, UErrorCode *pErrorCode); - -/* - * Call upvec_compact(), create a 16-bit UTrie2 with indexes into the compacted - * vectors array, and freeze the trie. - */ -U_CAPI UTrie2 * U_EXPORT2 -upvec_compactToUTrie2WithRowIndexes(UPropsVectors *pv, UErrorCode *pErrorCode); - -struct UPVecToUTrie2Context { - UTrie2 *trie; - int32_t initialValue; - int32_t errorValue; - int32_t maxValue; -}; -typedef struct UPVecToUTrie2Context UPVecToUTrie2Context; - -/* context=UPVecToUTrie2Context, creates the trie and stores the rowIndex values */ -U_CAPI void U_CALLCONV -upvec_compactToUTrie2Handler(void *context, - UChar32 start, UChar32 end, - int32_t rowIndex, uint32_t *row, int32_t columns, - UErrorCode *pErrorCode); - -U_CDECL_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2010, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: propsvec.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002feb22 +* created by: Markus W. Scherer +* +* Store bits (Unicode character properties) in bit set vectors. +*/ + +#ifndef __UPROPSVEC_H__ +#define __UPROPSVEC_H__ + +#include "unicode/utypes.h" +#include "utrie.h" +#include "utrie2.h" + +U_CDECL_BEGIN + +/** + * Unicode Properties Vectors associated with code point ranges. + * + * Rows of uint32_t integers in a contiguous array store + * the range limits and the properties vectors. + * + * Logically, each row has a certain number of uint32_t values, + * which is set via the upvec_open() "columns" parameter. + * + * Internally, two additional columns are stored. + * In each internal row, + * row[0] contains the start code point and + * row[1] contains the limit code point, + * which is the start of the next range. + * + * Initially, there is only one "normal" row for + * range [0..0x110000[ with values 0. + * There are additional rows for special purposes, see UPVEC_FIRST_SPECIAL_CP. + * + * It would be possible to store only one range boundary per row, + * but self-contained rows allow to later sort them by contents. + */ +struct UPropsVectors; +typedef struct UPropsVectors UPropsVectors; + +/* + * Special pseudo code points for storing the initialValue and the errorValue, + * which are used to initialize a UTrie2 or similar. + */ +#define UPVEC_FIRST_SPECIAL_CP 0x110000 +#define UPVEC_INITIAL_VALUE_CP 0x110000 +#define UPVEC_ERROR_VALUE_CP 0x110001 +#define UPVEC_MAX_CP 0x110001 + +/* + * Special pseudo code point used in upvec_compact() signalling the end of + * delivering special values and the beginning of delivering real ones. + * Stable value, unlike UPVEC_MAX_CP which might grow over time. + */ +#define UPVEC_START_REAL_VALUES_CP 0x200000 + +/* + * Open a UPropsVectors object. + * @param columns Number of value integers (uint32_t) per row. + */ +U_CAPI UPropsVectors * U_EXPORT2 +upvec_open(int32_t columns, UErrorCode *pErrorCode); + +U_CAPI void U_EXPORT2 +upvec_close(UPropsVectors *pv); + +/* + * In rows for code points [start..end], select the column, + * reset the mask bits and set the value bits (ANDed with the mask). + * + * Will set U_NO_WRITE_PERMISSION if called after upvec_compact(). + */ +U_CAPI void U_EXPORT2 +upvec_setValue(UPropsVectors *pv, + UChar32 start, UChar32 end, + int32_t column, + uint32_t value, uint32_t mask, + UErrorCode *pErrorCode); + +/* + * Logically const but must not be used on the same pv concurrently! + * Always returns 0 if called after upvec_compact(). + */ +U_CAPI uint32_t U_EXPORT2 +upvec_getValue(const UPropsVectors *pv, UChar32 c, int32_t column); + +/* + * pRangeStart and pRangeEnd can be NULL. + * @return NULL if rowIndex out of range and for illegal arguments, + * or if called after upvec_compact() + */ +U_CAPI uint32_t * U_EXPORT2 +upvec_getRow(const UPropsVectors *pv, int32_t rowIndex, + UChar32 *pRangeStart, UChar32 *pRangeEnd); + +/* + * Compact the vectors: + * - modify the memory + * - keep only unique vectors + * - store them contiguously from the beginning of the memory + * - for each (non-unique) row, call the handler function + * + * The handler's rowIndex is the index of the row in the compacted + * memory block. + * (Therefore, it starts at 0 increases in increments of the columns value.) + * + * In a first phase, only special values are delivered (each exactly once), + * with start==end both equalling a special pseudo code point. + * Then the handler is called once more with start==end==UPVEC_START_REAL_VALUES_CP + * where rowIndex is the length of the compacted array, + * and the row is arbitrary (but not NULL). + * Then, in the second phase, the handler is called for each row of real values. + */ +typedef void U_CALLCONV +UPVecCompactHandler(void *context, + UChar32 start, UChar32 end, + int32_t rowIndex, uint32_t *row, int32_t columns, + UErrorCode *pErrorCode); + +U_CAPI void U_EXPORT2 +upvec_compact(UPropsVectors *pv, UPVecCompactHandler *handler, void *context, UErrorCode *pErrorCode); + +/* + * Get the vectors array after calling upvec_compact(). + * The caller must not modify nor release the returned array. + * Returns NULL if called before upvec_compact(). + */ +U_CAPI const uint32_t * U_EXPORT2 +upvec_getArray(const UPropsVectors *pv, int32_t *pRows, int32_t *pColumns); + +/* + * Get a clone of the vectors array after calling upvec_compact(). + * The caller owns the returned array and must uprv_free() it. + * Returns NULL if called before upvec_compact(). + */ +U_CAPI uint32_t * U_EXPORT2 +upvec_cloneArray(const UPropsVectors *pv, + int32_t *pRows, int32_t *pColumns, UErrorCode *pErrorCode); + +/* + * Call upvec_compact(), create a 16-bit UTrie2 with indexes into the compacted + * vectors array, and freeze the trie. + */ +U_CAPI UTrie2 * U_EXPORT2 +upvec_compactToUTrie2WithRowIndexes(UPropsVectors *pv, UErrorCode *pErrorCode); + +struct UPVecToUTrie2Context { + UTrie2 *trie; + int32_t initialValue; + int32_t errorValue; + int32_t maxValue; +}; +typedef struct UPVecToUTrie2Context UPVecToUTrie2Context; + +/* context=UPVecToUTrie2Context, creates the trie and stores the rowIndex values */ +U_CAPI void U_CALLCONV +upvec_compactToUTrie2Handler(void *context, + UChar32 start, UChar32 end, + int32_t rowIndex, uint32_t *row, int32_t columns, + UErrorCode *pErrorCode); + +U_CDECL_END + +#endif diff --git a/deps/icu-small/source/common/punycode.cpp b/deps/icu-small/source/common/punycode.cpp index f95722da27d1f0..8905a38de8c67d 100644 --- a/deps/icu-small/source/common/punycode.cpp +++ b/deps/icu-small/source/common/punycode.cpp @@ -1,590 +1,590 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: punycode.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002jan31 -* created by: Markus W. Scherer -*/ - - -/* This ICU code derived from: */ -/* -punycode.c 0.4.0 (2001-Nov-17-Sat) -http://www.cs.berkeley.edu/~amc/idn/ -Adam M. Costello -http://www.nicemice.net/amc/ - -Disclaimer and license - - Regarding this entire document or any portion of it (including - the pseudocode and C code), the author makes no guarantees and - is not responsible for any damage resulting from its use. The - author grants irrevocable permission to anyone to use, modify, - and distribute it in any way that does not diminish the rights - of anyone else to use, modify, and distribute it, provided that - redistributed derivative works do not contain misleading author or - version information. Derivative works need not be licensed under - similar terms. -*/ -/* - * ICU modifications: - * - ICU data types and coding conventions - * - ICU string buffer handling with implicit source lengths - * and destination preflighting - * - UTF-16 handling - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_IDNA - -#include "unicode/ustring.h" -#include "unicode/utf.h" -#include "unicode/utf16.h" -#include "ustr_imp.h" -#include "cstring.h" -#include "cmemory.h" -#include "punycode.h" -#include "uassert.h" - - -/* Punycode ----------------------------------------------------------------- */ - -/* Punycode parameters for Bootstring */ -#define BASE 36 -#define TMIN 1 -#define TMAX 26 -#define SKEW 38 -#define DAMP 700 -#define INITIAL_BIAS 72 -#define INITIAL_N 0x80 - -/* "Basic" Unicode/ASCII code points */ -#define _HYPHEN 0X2d -#define DELIMITER _HYPHEN - -#define _ZERO_ 0X30 -#define _NINE 0x39 - -#define _SMALL_A 0X61 -#define _SMALL_Z 0X7a - -#define _CAPITAL_A 0X41 -#define _CAPITAL_Z 0X5a - -#define IS_BASIC(c) ((c)<0x80) -#define IS_BASIC_UPPERCASE(c) (_CAPITAL_A<=(c) && (c)<=_CAPITAL_Z) - -/** - * digitToBasic() returns the basic code point whose value - * (when used for representing integers) is d, which must be in the - * range 0 to BASE-1. The lowercase form is used unless the uppercase flag is - * nonzero, in which case the uppercase form is used. - */ -static inline char -digitToBasic(int32_t digit, UBool uppercase) { - /* 0..25 map to ASCII a..z or A..Z */ - /* 26..35 map to ASCII 0..9 */ - if(digit<26) { - if(uppercase) { - return (char)(_CAPITAL_A+digit); - } else { - return (char)(_SMALL_A+digit); - } - } else { - return (char)((_ZERO_-26)+digit); - } -} - -/** - * @return the numeric value of a basic code point (for use in representing integers) - * in the range 0 to BASE-1, or a negative value if cp is invalid. - */ -static int32_t decodeDigit(int32_t cp) { - if(cp<=u'Z') { - if(cp<=u'9') { - if(cp 26..35 - } - } else { - return cp-u'A'; // A-Z -> 0..25 - } - } else if(cp<=u'z') { - return cp-'a'; // a..z -> 0..25 - } else { - return -1; - } -} - -static inline char -asciiCaseMap(char b, UBool uppercase) { - if(uppercase) { - if(_SMALL_A<=b && b<=_SMALL_Z) { - b-=(_SMALL_A-_CAPITAL_A); - } - } else { - if(_CAPITAL_A<=b && b<=_CAPITAL_Z) { - b+=(_SMALL_A-_CAPITAL_A); - } - } - return b; -} - -/* Punycode-specific Bootstring code ---------------------------------------- */ - -/* - * The following code omits the {parts} of the pseudo-algorithm in the spec - * that are not used with the Punycode parameter set. - */ - -/* Bias adaptation function. */ -static int32_t -adaptBias(int32_t delta, int32_t length, UBool firstTime) { - int32_t count; - - if(firstTime) { - delta/=DAMP; - } else { - delta/=2; - } - - delta+=delta/length; - for(count=0; delta>((BASE-TMIN)*TMAX)/2; count+=BASE) { - delta/=(BASE-TMIN); - } - - return count+(((BASE-TMIN+1)*delta)/(delta+SKEW)); -} - -namespace { - -// ICU-13727: Limit input length for n^2 algorithm -// where well-formed strings are at most 59 characters long. -constexpr int32_t ENCODE_MAX_CODE_UNITS=1000; -constexpr int32_t DECODE_MAX_CHARS=2000; - -} // namespace - -// encode -U_CAPI int32_t -u_strToPunycode(const UChar *src, int32_t srcLength, - UChar *dest, int32_t destCapacity, - const UBool *caseFlags, - UErrorCode *pErrorCode) { - - int32_t cpBuffer[ENCODE_MAX_CODE_UNITS]; - int32_t n, delta, handledCPCount, basicLength, destLength, bias, j, m, q, k, t, srcCPCount; - UChar c, c2; - - /* argument checking */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - if(src==NULL || srcLength<-1 || (dest==NULL && destCapacity!=0)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - if (srcLength>ENCODE_MAX_CODE_UNITS) { - *pErrorCode=U_INPUT_TOO_LONG_ERROR; - return 0; - } - - /* - * Handle the basic code points and - * convert extended ones to UTF-32 in cpBuffer (caseFlag in sign bit): - */ - srcCPCount=destLength=0; - if(srcLength==-1) { - /* NUL-terminated input */ - for(j=0; /* no condition */; ++j) { - if((c=src[j])==0) { - break; - } - if(j>=ENCODE_MAX_CODE_UNITS) { - *pErrorCode=U_INPUT_TOO_LONG_ERROR; - return 0; - } - if(IS_BASIC(c)) { - cpBuffer[srcCPCount++]=0; - if(destLength0) { - if(destLength state to , but guard against overflow: - */ - if(m-n>(0x7fffffff-handledCPCount-delta)/(handledCPCount+1)) { - *pErrorCode=U_INTERNAL_PROGRAM_ERROR; - return 0; - } - delta+=(m-n)*(handledCPCount+1); - n=m; - - /* Encode a sequence of same code points n */ - for(j=0; jTMAX) { - t=TMAX; - } - */ - - t=k-bias; - if(t=(bias+TMAX)) { - t=TMAX; - } - - if(qDECODE_MAX_CHARS) { - *pErrorCode=U_INPUT_TOO_LONG_ERROR; - return 0; - } - - /* - * Handle the basic code points: - * Let basicLength be the number of input code points - * before the last delimiter, or 0 if there is none, - * then copy the first basicLength code points to the output. - * - * The two following loops iterate backward. - */ - for(j=srcLength; j>0;) { - if(src[--j]==DELIMITER) { - break; - } - } - destLength=basicLength=destCPCount=j; - U_ASSERT(destLength>=0); - - while(j>0) { - b=src[--j]; - if(!IS_BASIC(b)) { - *pErrorCode=U_INVALID_CHAR_FOUND; - return 0; - } - - if(j0 ? basicLength+1 : 0; in=srcLength) { - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - return 0; - } - - digit=decodeDigit(src[in++]); - if(digit<0) { - *pErrorCode=U_INVALID_CHAR_FOUND; - return 0; - } - if(digit>(0x7fffffff-i)/w) { - /* integer overflow */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - return 0; - } - - i+=digit*w; - /** RAM: comment out the old code for conformance with draft-ietf-idn-punycode-03.txt - t=k-bias; - if(tTMAX) { - t=TMAX; - } - */ - t=k-bias; - if(t=(bias+TMAX)) { - t=TMAX; - } - if(digit0x7fffffff/(BASE-t)) { - /* integer overflow */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - return 0; - } - w*=BASE-t; - } - - /* - * Modification from sample code: - * Increments destCPCount here, - * where needed instead of in for() loop tail. - */ - ++destCPCount; - bias=adaptBias(i-oldi, destCPCount, (UBool)(oldi==0)); - - /* - * i was supposed to wrap around from (incremented) destCPCount to 0, - * incrementing n each time, so we'll fix that now: - */ - if(i/destCPCount>(0x7fffffff-n)) { - /* integer overflow */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - return 0; - } - - n+=i/destCPCount; - i%=destCPCount; - /* not needed for Punycode: */ - /* if (decode_digit(n) <= BASE) return punycode_invalid_input; */ - - if(n>0x10ffff || U_IS_SURROGATE(n)) { - /* Unicode code point overflow */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - return 0; - } - - /* Insert n at position i of the output: */ - cpLength=U16_LENGTH(n); - if(dest!=NULL && ((destLength+cpLength)<=destCapacity)) { - int32_t codeUnitIndex; - - /* - * Handle indexes when supplementary code points are present. - * - * In almost all cases, there will be only BMP code points before i - * and even in the entire string. - * This is handled with the same efficiency as with UTF-32. - * - * Only the rare cases with supplementary code points are handled - * more slowly - but not too bad since this is an insertion anyway. - */ - if(i<=firstSupplementaryIndex) { - codeUnitIndex=i; - if(cpLength>1) { - firstSupplementaryIndex=codeUnitIndex; - } else { - ++firstSupplementaryIndex; - } - } else { - codeUnitIndex=firstSupplementaryIndex; - U16_FWD_N(dest, codeUnitIndex, destLength, i-codeUnitIndex); - } - - /* use the UChar index codeUnitIndex instead of the code point index i */ - if(codeUnitIndex=0); - ++i; - } - - return u_terminateUChars(dest, destCapacity, destLength, pErrorCode); -} - -/* ### check notes on overflow handling - only necessary if not IDNA? are these Punycode functions to be public? */ - -#endif /* #if !UCONFIG_NO_IDNA */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: punycode.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002jan31 +* created by: Markus W. Scherer +*/ + + +/* This ICU code derived from: */ +/* +punycode.c 0.4.0 (2001-Nov-17-Sat) +http://www.cs.berkeley.edu/~amc/idn/ +Adam M. Costello +http://www.nicemice.net/amc/ + +Disclaimer and license + + Regarding this entire document or any portion of it (including + the pseudocode and C code), the author makes no guarantees and + is not responsible for any damage resulting from its use. The + author grants irrevocable permission to anyone to use, modify, + and distribute it in any way that does not diminish the rights + of anyone else to use, modify, and distribute it, provided that + redistributed derivative works do not contain misleading author or + version information. Derivative works need not be licensed under + similar terms. +*/ +/* + * ICU modifications: + * - ICU data types and coding conventions + * - ICU string buffer handling with implicit source lengths + * and destination preflighting + * - UTF-16 handling + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_IDNA + +#include "unicode/ustring.h" +#include "unicode/utf.h" +#include "unicode/utf16.h" +#include "ustr_imp.h" +#include "cstring.h" +#include "cmemory.h" +#include "punycode.h" +#include "uassert.h" + + +/* Punycode ----------------------------------------------------------------- */ + +/* Punycode parameters for Bootstring */ +#define BASE 36 +#define TMIN 1 +#define TMAX 26 +#define SKEW 38 +#define DAMP 700 +#define INITIAL_BIAS 72 +#define INITIAL_N 0x80 + +/* "Basic" Unicode/ASCII code points */ +#define _HYPHEN 0X2d +#define DELIMITER _HYPHEN + +#define _ZERO_ 0X30 +#define _NINE 0x39 + +#define _SMALL_A 0X61 +#define _SMALL_Z 0X7a + +#define _CAPITAL_A 0X41 +#define _CAPITAL_Z 0X5a + +#define IS_BASIC(c) ((c)<0x80) +#define IS_BASIC_UPPERCASE(c) (_CAPITAL_A<=(c) && (c)<=_CAPITAL_Z) + +/** + * digitToBasic() returns the basic code point whose value + * (when used for representing integers) is d, which must be in the + * range 0 to BASE-1. The lowercase form is used unless the uppercase flag is + * nonzero, in which case the uppercase form is used. + */ +static inline char +digitToBasic(int32_t digit, UBool uppercase) { + /* 0..25 map to ASCII a..z or A..Z */ + /* 26..35 map to ASCII 0..9 */ + if(digit<26) { + if(uppercase) { + return (char)(_CAPITAL_A+digit); + } else { + return (char)(_SMALL_A+digit); + } + } else { + return (char)((_ZERO_-26)+digit); + } +} + +/** + * @return the numeric value of a basic code point (for use in representing integers) + * in the range 0 to BASE-1, or a negative value if cp is invalid. + */ +static int32_t decodeDigit(int32_t cp) { + if(cp<=u'Z') { + if(cp<=u'9') { + if(cp 26..35 + } + } else { + return cp-u'A'; // A-Z -> 0..25 + } + } else if(cp<=u'z') { + return cp-'a'; // a..z -> 0..25 + } else { + return -1; + } +} + +static inline char +asciiCaseMap(char b, UBool uppercase) { + if(uppercase) { + if(_SMALL_A<=b && b<=_SMALL_Z) { + b-=(_SMALL_A-_CAPITAL_A); + } + } else { + if(_CAPITAL_A<=b && b<=_CAPITAL_Z) { + b+=(_SMALL_A-_CAPITAL_A); + } + } + return b; +} + +/* Punycode-specific Bootstring code ---------------------------------------- */ + +/* + * The following code omits the {parts} of the pseudo-algorithm in the spec + * that are not used with the Punycode parameter set. + */ + +/* Bias adaptation function. */ +static int32_t +adaptBias(int32_t delta, int32_t length, UBool firstTime) { + int32_t count; + + if(firstTime) { + delta/=DAMP; + } else { + delta/=2; + } + + delta+=delta/length; + for(count=0; delta>((BASE-TMIN)*TMAX)/2; count+=BASE) { + delta/=(BASE-TMIN); + } + + return count+(((BASE-TMIN+1)*delta)/(delta+SKEW)); +} + +namespace { + +// ICU-13727: Limit input length for n^2 algorithm +// where well-formed strings are at most 59 characters long. +constexpr int32_t ENCODE_MAX_CODE_UNITS=1000; +constexpr int32_t DECODE_MAX_CHARS=2000; + +} // namespace + +// encode +U_CAPI int32_t +u_strToPunycode(const char16_t *src, int32_t srcLength, + char16_t *dest, int32_t destCapacity, + const UBool *caseFlags, + UErrorCode *pErrorCode) { + + int32_t cpBuffer[ENCODE_MAX_CODE_UNITS]; + int32_t n, delta, handledCPCount, basicLength, destLength, bias, j, m, q, k, t, srcCPCount; + char16_t c, c2; + + /* argument checking */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + if(src==nullptr || srcLength<-1 || (dest==nullptr && destCapacity!=0)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + if (srcLength>ENCODE_MAX_CODE_UNITS) { + *pErrorCode=U_INPUT_TOO_LONG_ERROR; + return 0; + } + + /* + * Handle the basic code points and + * convert extended ones to UTF-32 in cpBuffer (caseFlag in sign bit): + */ + srcCPCount=destLength=0; + if(srcLength==-1) { + /* NUL-terminated input */ + for(j=0; /* no condition */; ++j) { + if((c=src[j])==0) { + break; + } + if(j>=ENCODE_MAX_CODE_UNITS) { + *pErrorCode=U_INPUT_TOO_LONG_ERROR; + return 0; + } + if(IS_BASIC(c)) { + cpBuffer[srcCPCount++]=0; + if(destLength0) { + if(destLength state to , but guard against overflow: + */ + if(m-n>(0x7fffffff-handledCPCount-delta)/(handledCPCount+1)) { + *pErrorCode=U_INTERNAL_PROGRAM_ERROR; + return 0; + } + delta+=(m-n)*(handledCPCount+1); + n=m; + + /* Encode a sequence of same code points n */ + for(j=0; jTMAX) { + t=TMAX; + } + */ + + t=k-bias; + if(t=(bias+TMAX)) { + t=TMAX; + } + + if(qDECODE_MAX_CHARS) { + *pErrorCode=U_INPUT_TOO_LONG_ERROR; + return 0; + } + + /* + * Handle the basic code points: + * Let basicLength be the number of input code points + * before the last delimiter, or 0 if there is none, + * then copy the first basicLength code points to the output. + * + * The two following loops iterate backward. + */ + for(j=srcLength; j>0;) { + if(src[--j]==DELIMITER) { + break; + } + } + destLength=basicLength=destCPCount=j; + U_ASSERT(destLength>=0); + + while(j>0) { + b=src[--j]; + if(!IS_BASIC(b)) { + *pErrorCode=U_INVALID_CHAR_FOUND; + return 0; + } + + if(j0 ? basicLength+1 : 0; in=srcLength) { + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + return 0; + } + + digit=decodeDigit(src[in++]); + if(digit<0) { + *pErrorCode=U_INVALID_CHAR_FOUND; + return 0; + } + if(digit>(0x7fffffff-i)/w) { + /* integer overflow */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + return 0; + } + + i+=digit*w; + /** RAM: comment out the old code for conformance with draft-ietf-idn-punycode-03.txt + t=k-bias; + if(tTMAX) { + t=TMAX; + } + */ + t=k-bias; + if(t=(bias+TMAX)) { + t=TMAX; + } + if(digit0x7fffffff/(BASE-t)) { + /* integer overflow */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + return 0; + } + w*=BASE-t; + } + + /* + * Modification from sample code: + * Increments destCPCount here, + * where needed instead of in for() loop tail. + */ + ++destCPCount; + bias=adaptBias(i-oldi, destCPCount, (UBool)(oldi==0)); + + /* + * i was supposed to wrap around from (incremented) destCPCount to 0, + * incrementing n each time, so we'll fix that now: + */ + if(i/destCPCount>(0x7fffffff-n)) { + /* integer overflow */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + return 0; + } + + n+=i/destCPCount; + i%=destCPCount; + /* not needed for Punycode: */ + /* if (decode_digit(n) <= BASE) return punycode_invalid_input; */ + + if(n>0x10ffff || U_IS_SURROGATE(n)) { + /* Unicode code point overflow */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + return 0; + } + + /* Insert n at position i of the output: */ + cpLength=U16_LENGTH(n); + if(dest!=nullptr && ((destLength+cpLength)<=destCapacity)) { + int32_t codeUnitIndex; + + /* + * Handle indexes when supplementary code points are present. + * + * In almost all cases, there will be only BMP code points before i + * and even in the entire string. + * This is handled with the same efficiency as with UTF-32. + * + * Only the rare cases with supplementary code points are handled + * more slowly - but not too bad since this is an insertion anyway. + */ + if(i<=firstSupplementaryIndex) { + codeUnitIndex=i; + if(cpLength>1) { + firstSupplementaryIndex=codeUnitIndex; + } else { + ++firstSupplementaryIndex; + } + } else { + codeUnitIndex=firstSupplementaryIndex; + U16_FWD_N(dest, codeUnitIndex, destLength, i-codeUnitIndex); + } + + /* use the char16_t index codeUnitIndex instead of the code point index i */ + if(codeUnitIndex=0); + ++i; + } + + return u_terminateUChars(dest, destCapacity, destLength, pErrorCode); +} + +/* ### check notes on overflow handling - only necessary if not IDNA? are these Punycode functions to be public? */ + +#endif /* #if !UCONFIG_NO_IDNA */ diff --git a/deps/icu-small/source/common/punycode.h b/deps/icu-small/source/common/punycode.h index 9e28f770c407ec..a6b43db714c8fa 100644 --- a/deps/icu-small/source/common/punycode.h +++ b/deps/icu-small/source/common/punycode.h @@ -1,120 +1,120 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2003, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: punycode.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002jan31 -* created by: Markus W. Scherer -*/ - -/* This ICU code derived from: */ -/* -punycode.c 0.4.0 (2001-Nov-17-Sat) -http://www.cs.berkeley.edu/~amc/idn/ -Adam M. Costello -http://www.nicemice.net/amc/ -*/ - -#ifndef __PUNYCODE_H__ -#define __PUNYCODE_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_IDNA - -/** - * u_strToPunycode() converts Unicode to Punycode. - * - * The input string must not contain single, unpaired surrogates. - * The output will be represented as an array of ASCII code points. - * - * The output string is NUL-terminated according to normal ICU - * string output rules. - * - * @param src Input Unicode string. - * This function handles a limited amount of code points - * (the limit is >=64). - * U_INDEX_OUTOFBOUNDS_ERROR is set if the limit is exceeded. - * @param srcLength Number of UChars in src, or -1 if NUL-terminated. - * @param dest Output Punycode array. - * @param destCapacity Size of dest. - * @param caseFlags Vector of boolean values, one per input UChar, - * indicating that the corresponding character is to be - * marked for the decoder optionally - * uppercasing (true) or lowercasing (false) - * the character. - * ASCII characters are output directly in the case as marked. - * Flags corresponding to trail surrogates are ignored. - * If caseFlags==NULL then input characters are not - * case-mapped. - * @param pErrorCode ICU in/out error code parameter. - * U_INVALID_CHAR_FOUND if src contains - * unmatched single surrogates. - * U_INDEX_OUTOFBOUNDS_ERROR if src contains - * too many code points. - * @return Number of ASCII characters in puny. - * - * @see u_strFromPunycode - */ -U_CAPI int32_t -u_strToPunycode(const UChar *src, int32_t srcLength, - UChar *dest, int32_t destCapacity, - const UBool *caseFlags, - UErrorCode *pErrorCode); - -/** - * u_strFromPunycode() converts Punycode to Unicode. - * The Unicode string will be at most as long (in UChars) - * than the Punycode string (in chars). - * - * @param src Input Punycode string. - * @param srcLength Length of puny, or -1 if NUL-terminated - * @param dest Output Unicode string buffer. - * @param destCapacity Size of dest in number of UChars, - * and of caseFlags in numbers of UBools. - * @param caseFlags Output array for case flags as - * defined by the Punycode string. - * The caller should uppercase (true) or lowercase (FASLE) - * the corresponding character in dest. - * For supplementary characters, only the lead surrogate - * is marked, and false is stored for the trail surrogate. - * This is redundant and not necessary for ASCII characters - * because they are already in the case indicated. - * Can be NULL if the case flags are not needed. - * @param pErrorCode ICU in/out error code parameter. - * U_INVALID_CHAR_FOUND if a non-ASCII character - * precedes the last delimiter ('-'), - * or if an invalid character (not a-zA-Z0-9) is found - * after the last delimiter. - * U_ILLEGAL_CHAR_FOUND if the delta sequence is ill-formed. - * @return Number of UChars written to dest. - * - * @see u_strToPunycode - */ -U_CAPI int32_t -u_strFromPunycode(const UChar *src, int32_t srcLength, - UChar *dest, int32_t destCapacity, - UBool *caseFlags, - UErrorCode *pErrorCode); - -#endif /* #if !UCONFIG_NO_IDNA */ - -#endif - -/* - * Hey, Emacs, please set the following: - * - * Local Variables: - * indent-tabs-mode: nil - * End: - * - */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2003, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: punycode.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002jan31 +* created by: Markus W. Scherer +*/ + +/* This ICU code derived from: */ +/* +punycode.c 0.4.0 (2001-Nov-17-Sat) +http://www.cs.berkeley.edu/~amc/idn/ +Adam M. Costello +http://www.nicemice.net/amc/ +*/ + +#ifndef __PUNYCODE_H__ +#define __PUNYCODE_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_IDNA + +/** + * u_strToPunycode() converts Unicode to Punycode. + * + * The input string must not contain single, unpaired surrogates. + * The output will be represented as an array of ASCII code points. + * + * The output string is NUL-terminated according to normal ICU + * string output rules. + * + * @param src Input Unicode string. + * This function handles a limited amount of code points + * (the limit is >=64). + * U_INDEX_OUTOFBOUNDS_ERROR is set if the limit is exceeded. + * @param srcLength Number of UChars in src, or -1 if NUL-terminated. + * @param dest Output Punycode array. + * @param destCapacity Size of dest. + * @param caseFlags Vector of boolean values, one per input UChar, + * indicating that the corresponding character is to be + * marked for the decoder optionally + * uppercasing (true) or lowercasing (false) + * the character. + * ASCII characters are output directly in the case as marked. + * Flags corresponding to trail surrogates are ignored. + * If caseFlags==NULL then input characters are not + * case-mapped. + * @param pErrorCode ICU in/out error code parameter. + * U_INVALID_CHAR_FOUND if src contains + * unmatched single surrogates. + * U_INDEX_OUTOFBOUNDS_ERROR if src contains + * too many code points. + * @return Number of ASCII characters in puny. + * + * @see u_strFromPunycode + */ +U_CAPI int32_t +u_strToPunycode(const UChar *src, int32_t srcLength, + UChar *dest, int32_t destCapacity, + const UBool *caseFlags, + UErrorCode *pErrorCode); + +/** + * u_strFromPunycode() converts Punycode to Unicode. + * The Unicode string will be at most as long (in UChars) + * than the Punycode string (in chars). + * + * @param src Input Punycode string. + * @param srcLength Length of puny, or -1 if NUL-terminated + * @param dest Output Unicode string buffer. + * @param destCapacity Size of dest in number of UChars, + * and of caseFlags in numbers of UBools. + * @param caseFlags Output array for case flags as + * defined by the Punycode string. + * The caller should uppercase (true) or lowercase (FASLE) + * the corresponding character in dest. + * For supplementary characters, only the lead surrogate + * is marked, and false is stored for the trail surrogate. + * This is redundant and not necessary for ASCII characters + * because they are already in the case indicated. + * Can be NULL if the case flags are not needed. + * @param pErrorCode ICU in/out error code parameter. + * U_INVALID_CHAR_FOUND if a non-ASCII character + * precedes the last delimiter ('-'), + * or if an invalid character (not a-zA-Z0-9) is found + * after the last delimiter. + * U_ILLEGAL_CHAR_FOUND if the delta sequence is ill-formed. + * @return Number of UChars written to dest. + * + * @see u_strToPunycode + */ +U_CAPI int32_t +u_strFromPunycode(const UChar *src, int32_t srcLength, + UChar *dest, int32_t destCapacity, + UBool *caseFlags, + UErrorCode *pErrorCode); + +#endif /* #if !UCONFIG_NO_IDNA */ + +#endif + +/* + * Hey, Emacs, please set the following: + * + * Local Variables: + * indent-tabs-mode: nil + * End: + * + */ diff --git a/deps/icu-small/source/common/putil.cpp b/deps/icu-small/source/common/putil.cpp index f27c8737d213f8..e15ce4dcfcd797 100644 --- a/deps/icu-small/source/common/putil.cpp +++ b/deps/icu-small/source/common/putil.cpp @@ -1,2491 +1,2491 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1997-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* FILE NAME : putil.c (previously putil.cpp and ptypes.cpp) -* -* Date Name Description -* 04/14/97 aliu Creation. -* 04/24/97 aliu Added getDefaultDataDirectory() and -* getDefaultLocaleID(). -* 04/28/97 aliu Rewritten to assume Unix and apply general methods -* for assumed case. Non-UNIX platforms must be -* special-cased. Rewrote numeric methods dealing -* with NaN and Infinity to be platform independent -* over all IEEE 754 platforms. -* 05/13/97 aliu Restored sign of timezone -* (semantics are hours West of GMT) -* 06/16/98 erm Added IEEE_754 stuff, cleaned up isInfinite, isNan, -* nextDouble.. -* 07/22/98 stephen Added remainder, max, min, trunc -* 08/13/98 stephen Added isNegativeInfinity, isPositiveInfinity -* 08/24/98 stephen Added longBitsFromDouble -* 09/08/98 stephen Minor changes for Mac Port -* 03/02/99 stephen Removed openFile(). Added AS400 support. -* Fixed EBCDIC tables -* 04/15/99 stephen Converted to C. -* 06/28/99 stephen Removed mutex locking in u_isBigEndian(). -* 08/04/99 jeffrey R. Added OS/2 changes -* 11/15/99 helena Integrated S/390 IEEE support. -* 04/26/01 Barry N. OS/400 support for uprv_getDefaultLocaleID -* 08/15/01 Steven H. OS/400 support for uprv_getDefaultCodepage -* 01/03/08 Steven L. Fake Time Support -****************************************************************************** -*/ - -// Defines _XOPEN_SOURCE for access to POSIX functions. -// Must be before any other #includes. -#include "uposixdefs.h" - -// First, the platform type. Need this for U_PLATFORM. -#include "unicode/platform.h" - -#if U_PLATFORM == U_PF_MINGW && defined __STRICT_ANSI__ -/* tzset isn't defined in strict ANSI on MinGW. */ -#undef __STRICT_ANSI__ -#endif - -/* - * Cygwin with GCC requires inclusion of time.h after the above disabling strict asci mode statement. - */ -#include - -#if !U_PLATFORM_USES_ONLY_WIN32_API -#include -#endif - -/* include the rest of the ICU headers */ -#include "unicode/putil.h" -#include "unicode/ustring.h" -#include "putilimp.h" -#include "uassert.h" -#include "umutex.h" -#include "cmemory.h" -#include "cstring.h" -#include "locmap.h" -#include "ucln_cmn.h" -#include "charstr.h" - -/* Include standard headers. */ -#include -#include -#include -#include -#include -#include - -#ifndef U_COMMON_IMPLEMENTATION -#error U_COMMON_IMPLEMENTATION not set - must be set for all ICU source files in common/ - see https://unicode-org.github.io/icu/userguide/howtouseicu -#endif - - -/* include system headers */ -#if U_PLATFORM_USES_ONLY_WIN32_API - /* - * TODO: U_PLATFORM_USES_ONLY_WIN32_API includes MinGW. - * Should Cygwin be included as well (U_PLATFORM_HAS_WIN32_API) - * to use native APIs as much as possible? - */ -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -# define VC_EXTRALEAN -# define NOUSER -# define NOSERVICE -# define NOIME -# define NOMCX -# include -# include "unicode/uloc.h" -# include "wintz.h" -#elif U_PLATFORM == U_PF_OS400 -# include -# include /* error code structure */ -# include -# include /* EPT_CALL macro - this include must be after all other "QSYSINCs" */ -# include /* For uprv_maximumPtr */ -#elif U_PLATFORM == U_PF_OS390 -# include "unicode/ucnv.h" /* Needed for UCNV_SWAP_LFNL_OPTION_STRING */ -#elif U_PLATFORM_IS_DARWIN_BASED || U_PLATFORM_IS_LINUX_BASED || U_PLATFORM == U_PF_BSD || U_PLATFORM == U_PF_SOLARIS -# include -# include -# if U_PLATFORM == U_PF_SOLARIS -# ifndef _XPG4_2 -# define _XPG4_2 -# endif -# elif U_PLATFORM == U_PF_ANDROID -# include -# include -# endif -#elif U_PLATFORM == U_PF_QNX -# include -#endif - - -/* - * Only include langinfo.h if we have a way to get the codeset. If we later - * depend on more feature, we can test on U_HAVE_NL_LANGINFO. - * - */ - -#if U_HAVE_NL_LANGINFO_CODESET -#include -#endif - -/** - * Simple things (presence of functions, etc) should just go in configure.in and be added to - * icucfg.h via autoheader. - */ -#if U_PLATFORM_IMPLEMENTS_POSIX -# if U_PLATFORM == U_PF_OS400 -# define HAVE_DLFCN_H 0 -# define HAVE_DLOPEN 0 -# else -# ifndef HAVE_DLFCN_H -# define HAVE_DLFCN_H 1 -# endif -# ifndef HAVE_DLOPEN -# define HAVE_DLOPEN 1 -# endif -# endif -# ifndef HAVE_GETTIMEOFDAY -# define HAVE_GETTIMEOFDAY 1 -# endif -#else -# define HAVE_DLFCN_H 0 -# define HAVE_DLOPEN 0 -# define HAVE_GETTIMEOFDAY 0 -#endif - -U_NAMESPACE_USE - -/* Define the extension for data files, again... */ -#define DATA_TYPE "dat" - -/* Leave this copyright notice here! */ -static const char copyright[] = U_COPYRIGHT_STRING; - -/* floating point implementations ------------------------------------------- */ - -/* We return QNAN rather than SNAN*/ -#define SIGN 0x80000000U - -/* Make it easy to define certain types of constants */ -typedef union { - int64_t i64; /* This must be defined first in order to allow the initialization to work. This is a C89 feature. */ - double d64; -} BitPatternConversion; -static const BitPatternConversion gNan = { (int64_t) INT64_C(0x7FF8000000000000) }; -static const BitPatternConversion gInf = { (int64_t) INT64_C(0x7FF0000000000000) }; - -/*--------------------------------------------------------------------------- - Platform utilities - Our general strategy is to assume we're on a POSIX platform. Platforms which - are non-POSIX must declare themselves so. The default POSIX implementation - will sometimes work for non-POSIX platforms as well (e.g., the NaN-related - functions). - ---------------------------------------------------------------------------*/ - -#if U_PLATFORM_USES_ONLY_WIN32_API || U_PLATFORM == U_PF_OS400 -# undef U_POSIX_LOCALE -#else -# define U_POSIX_LOCALE 1 -#endif - -/* - WARNING! u_topNBytesOfDouble and u_bottomNBytesOfDouble - can't be properly optimized by the gcc compiler sometimes (i.e. gcc 3.2). -*/ -#if !IEEE_754 -static char* -u_topNBytesOfDouble(double* d, int n) -{ -#if U_IS_BIG_ENDIAN - return (char*)d; -#else - return (char*)(d + 1) - n; -#endif -} - -static char* -u_bottomNBytesOfDouble(double* d, int n) -{ -#if U_IS_BIG_ENDIAN - return (char*)(d + 1) - n; -#else - return (char*)d; -#endif -} -#endif /* !IEEE_754 */ - -#if IEEE_754 -static UBool -u_signBit(double d) { - uint8_t hiByte; -#if U_IS_BIG_ENDIAN - hiByte = *(uint8_t *)&d; -#else - hiByte = *(((uint8_t *)&d) + sizeof(double) - 1); -#endif - return (hiByte & 0x80) != 0; -} -#endif - - - -#if defined (U_DEBUG_FAKETIME) -/* Override the clock to test things without having to move the system clock. - * Assumes POSIX gettimeofday() will function - */ -UDate fakeClock_t0 = 0; /** Time to start the clock from **/ -UDate fakeClock_dt = 0; /** Offset (fake time - real time) **/ -UBool fakeClock_set = false; /** True if fake clock has spun up **/ - -static UDate getUTCtime_real() { - struct timeval posixTime; - gettimeofday(&posixTime, NULL); - return (UDate)(((int64_t)posixTime.tv_sec * U_MILLIS_PER_SECOND) + (posixTime.tv_usec/1000)); -} - -static UDate getUTCtime_fake() { - static UMutex fakeClockMutex; - umtx_lock(&fakeClockMutex); - if(!fakeClock_set) { - UDate real = getUTCtime_real(); - const char *fake_start = getenv("U_FAKETIME_START"); - if((fake_start!=NULL) && (fake_start[0]!=0)) { - sscanf(fake_start,"%lf",&fakeClock_t0); - fakeClock_dt = fakeClock_t0 - real; - fprintf(stderr,"U_DEBUG_FAKETIME was set at compile time, so the ICU clock will start at a preset value\n" - "env variable U_FAKETIME_START=%.0f (%s) for an offset of %.0f ms from the current time %.0f\n", - fakeClock_t0, fake_start, fakeClock_dt, real); - } else { - fakeClock_dt = 0; - fprintf(stderr,"U_DEBUG_FAKETIME was set at compile time, but U_FAKETIME_START was not set.\n" - "Set U_FAKETIME_START to the number of milliseconds since 1/1/1970 to set the ICU clock.\n"); - } - fakeClock_set = true; - } - umtx_unlock(&fakeClockMutex); - - return getUTCtime_real() + fakeClock_dt; -} -#endif - -#if U_PLATFORM_USES_ONLY_WIN32_API -typedef union { - int64_t int64; - FILETIME fileTime; -} FileTimeConversion; /* This is like a ULARGE_INTEGER */ - -/* Number of 100 nanoseconds from 1/1/1601 to 1/1/1970 */ -#define EPOCH_BIAS INT64_C(116444736000000000) -#define HECTONANOSECOND_PER_MILLISECOND 10000 - -#endif - -/*--------------------------------------------------------------------------- - Universal Implementations - These are designed to work on all platforms. Try these, and if they - don't work on your platform, then special case your platform with new - implementations. ----------------------------------------------------------------------------*/ - -U_CAPI UDate U_EXPORT2 -uprv_getUTCtime() -{ -#if defined(U_DEBUG_FAKETIME) - return getUTCtime_fake(); /* Hook for overriding the clock */ -#else - return uprv_getRawUTCtime(); -#endif -} - -/* Return UTC (GMT) time measured in milliseconds since 0:00 on 1/1/70.*/ -U_CAPI UDate U_EXPORT2 -uprv_getRawUTCtime() -{ -#if U_PLATFORM_USES_ONLY_WIN32_API - - FileTimeConversion winTime; - GetSystemTimeAsFileTime(&winTime.fileTime); - return (UDate)((winTime.int64 - EPOCH_BIAS) / HECTONANOSECOND_PER_MILLISECOND); -#else - -#if HAVE_GETTIMEOFDAY - struct timeval posixTime; - gettimeofday(&posixTime, NULL); - return (UDate)(((int64_t)posixTime.tv_sec * U_MILLIS_PER_SECOND) + (posixTime.tv_usec/1000)); -#else - time_t epochtime; - time(&epochtime); - return (UDate)epochtime * U_MILLIS_PER_SECOND; -#endif - -#endif -} - -/*----------------------------------------------------------------------------- - IEEE 754 - These methods detect and return NaN and infinity values for doubles - conforming to IEEE 754. Platforms which support this standard include X86, - Mac 680x0, Mac PowerPC, AIX RS/6000, and most others. - If this doesn't work on your platform, you have non-IEEE floating-point, and - will need to code your own versions. A naive implementation is to return 0.0 - for getNaN and getInfinity, and false for isNaN and isInfinite. - ---------------------------------------------------------------------------*/ - -U_CAPI UBool U_EXPORT2 -uprv_isNaN(double number) -{ -#if IEEE_754 - BitPatternConversion convertedNumber; - convertedNumber.d64 = number; - /* Infinity is 0x7FF0000000000000U. Anything greater than that is a NaN */ - return (UBool)((convertedNumber.i64 & U_INT64_MAX) > gInf.i64); - -#elif U_PLATFORM == U_PF_OS390 - uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number, - sizeof(uint32_t)); - uint32_t lowBits = *(uint32_t*)u_bottomNBytesOfDouble(&number, - sizeof(uint32_t)); - - return ((highBits & 0x7F080000L) == 0x7F080000L) && - (lowBits == 0x00000000L); - -#else - /* If your platform doesn't support IEEE 754 but *does* have an NaN value,*/ - /* you'll need to replace this default implementation with what's correct*/ - /* for your platform.*/ - return number != number; -#endif -} - -U_CAPI UBool U_EXPORT2 -uprv_isInfinite(double number) -{ -#if IEEE_754 - BitPatternConversion convertedNumber; - convertedNumber.d64 = number; - /* Infinity is exactly 0x7FF0000000000000U. */ - return (UBool)((convertedNumber.i64 & U_INT64_MAX) == gInf.i64); -#elif U_PLATFORM == U_PF_OS390 - uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number, - sizeof(uint32_t)); - uint32_t lowBits = *(uint32_t*)u_bottomNBytesOfDouble(&number, - sizeof(uint32_t)); - - return ((highBits & ~SIGN) == 0x70FF0000L) && (lowBits == 0x00000000L); - -#else - /* If your platform doesn't support IEEE 754 but *does* have an infinity*/ - /* value, you'll need to replace this default implementation with what's*/ - /* correct for your platform.*/ - return number == (2.0 * number); -#endif -} - -U_CAPI UBool U_EXPORT2 -uprv_isPositiveInfinity(double number) -{ -#if IEEE_754 || U_PLATFORM == U_PF_OS390 - return (UBool)(number > 0 && uprv_isInfinite(number)); -#else - return uprv_isInfinite(number); -#endif -} - -U_CAPI UBool U_EXPORT2 -uprv_isNegativeInfinity(double number) -{ -#if IEEE_754 || U_PLATFORM == U_PF_OS390 - return (UBool)(number < 0 && uprv_isInfinite(number)); - -#else - uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number, - sizeof(uint32_t)); - return((highBits & SIGN) && uprv_isInfinite(number)); - -#endif -} - -U_CAPI double U_EXPORT2 -uprv_getNaN() -{ -#if IEEE_754 || U_PLATFORM == U_PF_OS390 - return gNan.d64; -#else - /* If your platform doesn't support IEEE 754 but *does* have an NaN value,*/ - /* you'll need to replace this default implementation with what's correct*/ - /* for your platform.*/ - return 0.0; -#endif -} - -U_CAPI double U_EXPORT2 -uprv_getInfinity() -{ -#if IEEE_754 || U_PLATFORM == U_PF_OS390 - return gInf.d64; -#else - /* If your platform doesn't support IEEE 754 but *does* have an infinity*/ - /* value, you'll need to replace this default implementation with what's*/ - /* correct for your platform.*/ - return 0.0; -#endif -} - -U_CAPI double U_EXPORT2 -uprv_floor(double x) -{ - return floor(x); -} - -U_CAPI double U_EXPORT2 -uprv_ceil(double x) -{ - return ceil(x); -} - -U_CAPI double U_EXPORT2 -uprv_round(double x) -{ - return uprv_floor(x + 0.5); -} - -U_CAPI double U_EXPORT2 -uprv_fabs(double x) -{ - return fabs(x); -} - -U_CAPI double U_EXPORT2 -uprv_modf(double x, double* y) -{ - return modf(x, y); -} - -U_CAPI double U_EXPORT2 -uprv_fmod(double x, double y) -{ - return fmod(x, y); -} - -U_CAPI double U_EXPORT2 -uprv_pow(double x, double y) -{ - /* This is declared as "double pow(double x, double y)" */ - return pow(x, y); -} - -U_CAPI double U_EXPORT2 -uprv_pow10(int32_t x) -{ - return pow(10.0, (double)x); -} - -U_CAPI double U_EXPORT2 -uprv_fmax(double x, double y) -{ -#if IEEE_754 - /* first handle NaN*/ - if(uprv_isNaN(x) || uprv_isNaN(y)) - return uprv_getNaN(); - - /* check for -0 and 0*/ - if(x == 0.0 && y == 0.0 && u_signBit(x)) - return y; - -#endif - - /* this should work for all flt point w/o NaN and Inf special cases */ - return (x > y ? x : y); -} - -U_CAPI double U_EXPORT2 -uprv_fmin(double x, double y) -{ -#if IEEE_754 - /* first handle NaN*/ - if(uprv_isNaN(x) || uprv_isNaN(y)) - return uprv_getNaN(); - - /* check for -0 and 0*/ - if(x == 0.0 && y == 0.0 && u_signBit(y)) - return y; - -#endif - - /* this should work for all flt point w/o NaN and Inf special cases */ - 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 - * This is different than calling floor() or ceil(): - * floor(3.3) = 3, floor(-3.3) = -4 - * ceil(3.3) = 4, ceil(-3.3) = -3 - */ -U_CAPI double U_EXPORT2 -uprv_trunc(double d) -{ -#if IEEE_754 - /* handle error cases*/ - if(uprv_isNaN(d)) - return uprv_getNaN(); - if(uprv_isInfinite(d)) - return uprv_getInfinity(); - - if(u_signBit(d)) /* Signbit() picks up -0.0; d<0 does not. */ - return ceil(d); - else - return floor(d); - -#else - return d >= 0 ? floor(d) : ceil(d); - -#endif -} - -/** - * Return the largest positive number that can be represented by an integer - * type of arbitrary bit length. - */ -U_CAPI double U_EXPORT2 -uprv_maxMantissa(void) -{ - return pow(2.0, DBL_MANT_DIG + 1.0) - 1.0; -} - -U_CAPI double U_EXPORT2 -uprv_log(double d) -{ - return log(d); -} - -U_CAPI void * U_EXPORT2 -uprv_maximumPtr(void * base) -{ -#if U_PLATFORM == U_PF_OS400 - /* - * With the provided function we should never be out of range of a given segment - * (a traditional/typical segment that is). Our segments have 5 bytes for the - * id and 3 bytes for the offset. The key is that the casting takes care of - * only retrieving the offset portion minus x1000. Hence, the smallest offset - * seen in a program is x001000 and when casted to an int would be 0. - * That's why we can only add 0xffefff. Otherwise, we would exceed the segment. - * - * Currently, 16MB is the current addressing limitation on i5/OS if the activation is - * non-TERASPACE. If it is TERASPACE it is 2GB - 4k(header information). - * This function determines the activation based on the pointer that is passed in and - * calculates the appropriate maximum available size for - * each pointer type (TERASPACE and non-TERASPACE) - * - * Unlike other operating systems, the pointer model isn't determined at - * compile time on i5/OS. - */ - if ((base != NULL) && (_TESTPTR(base, _C_TERASPACE_CHECK))) { - /* if it is a TERASPACE pointer the max is 2GB - 4k */ - return ((void *)(((char *)base)-((uint32_t)(base))+((uint32_t)0x7fffefff))); - } - /* otherwise 16MB since NULL ptr is not checkable or the ptr is not TERASPACE */ - return ((void *)(((char *)base)-((uint32_t)(base))+((uint32_t)0xffefff))); - -#else - return U_MAX_PTR(base); -#endif -} - -/*--------------------------------------------------------------------------- - Platform-specific Implementations - Try these, and if they don't work on your platform, then special case your - platform with new implementations. - ---------------------------------------------------------------------------*/ - -/* Generic time zone layer -------------------------------------------------- */ - -/* Time zone utilities */ -U_CAPI void U_EXPORT2 -uprv_tzset() -{ -#if defined(U_TZSET) - U_TZSET(); -#else - /* no initialization*/ -#endif -} - -U_CAPI int32_t U_EXPORT2 -uprv_timezone() -{ -#ifdef U_TIMEZONE - return U_TIMEZONE; -#else - time_t t, t1, t2; - struct tm tmrec; - int32_t tdiff = 0; - - time(&t); - uprv_memcpy( &tmrec, localtime(&t), sizeof(tmrec) ); -#if U_PLATFORM != U_PF_IPHONE - UBool dst_checked = (tmrec.tm_isdst != 0); /* daylight savings time is checked*/ -#endif - t1 = mktime(&tmrec); /* local time in seconds*/ - uprv_memcpy( &tmrec, gmtime(&t), sizeof(tmrec) ); - t2 = mktime(&tmrec); /* GMT (or UTC) in seconds*/ - tdiff = t2 - t1; - -#if U_PLATFORM != U_PF_IPHONE - /* imitate NT behaviour, which returns same timezone offset to GMT for - winter and summer. - This does not work on all platforms. For instance, on glibc on Linux - and on Mac OS 10.5, tdiff calculated above remains the same - regardless of whether DST is in effect or not. iOS is another - platform where this does not work. Linux + glibc and Mac OS 10.5 - have U_TIMEZONE defined so that this code is not reached. - */ - if (dst_checked) - tdiff += 3600; -#endif - return tdiff; -#endif -} - -/* Note that U_TZNAME does *not* have to be tzname, but if it is, - some platforms need to have it declared here. */ - -#if defined(U_TZNAME) && (U_PLATFORM == U_PF_IRIX || U_PLATFORM_IS_DARWIN_BASED) -/* RS6000 and others reject char **tzname. */ -extern U_IMPORT char *U_TZNAME[]; -#endif - -#if !UCONFIG_NO_FILE_IO && ((U_PLATFORM_IS_DARWIN_BASED && (U_PLATFORM != U_PF_IPHONE || defined(U_TIMEZONE))) || U_PLATFORM_IS_LINUX_BASED || U_PLATFORM == U_PF_BSD || U_PLATFORM == U_PF_SOLARIS) -/* These platforms are likely to use Olson timezone IDs. */ -/* common targets of the symbolic link at TZDEFAULT are: - * "/usr/share/zoneinfo/" default, older Linux distros, macOS to 10.12 - * "../usr/share/zoneinfo/" newer Linux distros: Red Hat Enterprise Linux 7, Ubuntu 16, SuSe Linux 12 - * "/usr/share/lib/zoneinfo/" Solaris - * "../usr/share/lib/zoneinfo/" Solaris - * "/var/db/timezone/zoneinfo/" macOS 10.13 - * To avoid checking lots of paths, just check that the target path - * before the ends with "/zoneinfo/", and the is valid. - */ - -#define CHECK_LOCALTIME_LINK 1 -#if U_PLATFORM_IS_DARWIN_BASED -#include -#define TZZONEINFO (TZDIR "/") -#elif U_PLATFORM == U_PF_SOLARIS -#define TZDEFAULT "/etc/localtime" -#define TZZONEINFO "/usr/share/lib/zoneinfo/" -#define TZ_ENV_CHECK "localtime" -#else -#define TZDEFAULT "/etc/localtime" -#define TZZONEINFO "/usr/share/zoneinfo/" -#endif -#define TZZONEINFOTAIL "/zoneinfo/" -#if U_HAVE_DIRENT_H -#define TZFILE_SKIP "posixrules" /* tz file to skip when searching. */ -/* Some Linux distributions have 'localtime' in /usr/share/zoneinfo - symlinked to /etc/localtime, which makes searchForTZFile return - 'localtime' when it's the first match. */ -#define TZFILE_SKIP2 "localtime" -#define SEARCH_TZFILE -#include /* Needed to search through system timezone files */ -#endif -static char gTimeZoneBuffer[PATH_MAX]; -static char *gTimeZoneBufferPtr = NULL; -#endif - -#if !U_PLATFORM_USES_ONLY_WIN32_API -#define isNonDigit(ch) (ch < '0' || '9' < ch) -#define isDigit(ch) ('0' <= ch && ch <= '9') -static UBool isValidOlsonID(const char *id) { - int32_t idx = 0; - int32_t idxMax = 0; - - /* Determine if this is something like Iceland (Olson ID) - or AST4ADT (non-Olson ID) */ - while (id[idx] && isNonDigit(id[idx]) && id[idx] != ',') { - idx++; - } - - /* Allow at maximum 2 numbers at the end of the id to support zone id's - like GMT+11. */ - idxMax = idx + 2; - while (id[idx] && isDigit(id[idx]) && idx < idxMax) { - idx++; - } - - /* If we went through the whole string, then it might be okay. - The timezone is sometimes set to "CST-7CDT", "CST6CDT5,J129,J131/19:30", - "GRNLNDST3GRNLNDDT" or similar, so we cannot use it. - The rest of the time it could be an Olson ID. George */ - return (UBool)(id[idx] == 0 - || uprv_strcmp(id, "PST8PDT") == 0 - || uprv_strcmp(id, "MST7MDT") == 0 - || uprv_strcmp(id, "CST6CDT") == 0 - || uprv_strcmp(id, "EST5EDT") == 0); -} - -/* On some Unix-like OS, 'posix' subdirectory in - /usr/share/zoneinfo replicates the top-level contents. 'right' - subdirectory has the same set of files, but individual files - are different from those in the top-level directory or 'posix' - because 'right' has files for TAI (Int'l Atomic Time) while 'posix' - has files for UTC. - When the first match for /etc/localtime is in either of them - (usually in posix because 'right' has different file contents), - or TZ environment variable points to one of them, createTimeZone - fails because, say, 'posix/America/New_York' is not an Olson - timezone id ('America/New_York' is). So, we have to skip - 'posix/' and 'right/' at the beginning. */ -static void skipZoneIDPrefix(const char** id) { - if (uprv_strncmp(*id, "posix/", 6) == 0 - || uprv_strncmp(*id, "right/", 6) == 0) - { - *id += 6; - } -} -#endif - -#if defined(U_TZNAME) && !U_PLATFORM_USES_ONLY_WIN32_API - -#define CONVERT_HOURS_TO_SECONDS(offset) (int32_t)(offset*3600) -typedef struct OffsetZoneMapping { - int32_t offsetSeconds; - int32_t daylightType; /* 0=U_DAYLIGHT_NONE, 1=daylight in June-U_DAYLIGHT_JUNE, 2=daylight in December=U_DAYLIGHT_DECEMBER*/ - const char *stdID; - const char *dstID; - const char *olsonID; -} OffsetZoneMapping; - -enum { U_DAYLIGHT_NONE=0,U_DAYLIGHT_JUNE=1,U_DAYLIGHT_DECEMBER=2 }; - -/* -This list tries to disambiguate a set of abbreviated timezone IDs and offsets -and maps it to an Olson ID. -Before adding anything to this list, take a look at -icu/source/tools/tzcode/tz.alias -Sometimes no daylight savings (0) is important to define due to aliases. -This list can be tested with icu/source/test/compat/tzone.pl -More values could be added to daylightType to increase precision. -*/ -static const struct OffsetZoneMapping OFFSET_ZONE_MAPPINGS[] = { - {-45900, 2, "CHAST", "CHADT", "Pacific/Chatham"}, - {-43200, 1, "PETT", "PETST", "Asia/Kamchatka"}, - {-43200, 2, "NZST", "NZDT", "Pacific/Auckland"}, - {-43200, 1, "ANAT", "ANAST", "Asia/Anadyr"}, - {-39600, 1, "MAGT", "MAGST", "Asia/Magadan"}, - {-37800, 2, "LHST", "LHST", "Australia/Lord_Howe"}, - {-36000, 2, "EST", "EST", "Australia/Sydney"}, - {-36000, 1, "SAKT", "SAKST", "Asia/Sakhalin"}, - {-36000, 1, "VLAT", "VLAST", "Asia/Vladivostok"}, - {-34200, 2, "CST", "CST", "Australia/South"}, - {-32400, 1, "YAKT", "YAKST", "Asia/Yakutsk"}, - {-32400, 1, "CHOT", "CHOST", "Asia/Choibalsan"}, - {-31500, 2, "CWST", "CWST", "Australia/Eucla"}, - {-28800, 1, "IRKT", "IRKST", "Asia/Irkutsk"}, - {-28800, 1, "ULAT", "ULAST", "Asia/Ulaanbaatar"}, - {-28800, 2, "WST", "WST", "Australia/West"}, - {-25200, 1, "HOVT", "HOVST", "Asia/Hovd"}, - {-25200, 1, "KRAT", "KRAST", "Asia/Krasnoyarsk"}, - {-21600, 1, "NOVT", "NOVST", "Asia/Novosibirsk"}, - {-21600, 1, "OMST", "OMSST", "Asia/Omsk"}, - {-18000, 1, "YEKT", "YEKST", "Asia/Yekaterinburg"}, - {-14400, 1, "SAMT", "SAMST", "Europe/Samara"}, - {-14400, 1, "AMT", "AMST", "Asia/Yerevan"}, - {-14400, 1, "AZT", "AZST", "Asia/Baku"}, - {-10800, 1, "AST", "ADT", "Asia/Baghdad"}, - {-10800, 1, "MSK", "MSD", "Europe/Moscow"}, - {-10800, 1, "VOLT", "VOLST", "Europe/Volgograd"}, - {-7200, 0, "EET", "CEST", "Africa/Tripoli"}, - {-7200, 1, "EET", "EEST", "Europe/Athens"}, /* Conflicts with Africa/Cairo */ - {-7200, 1, "IST", "IDT", "Asia/Jerusalem"}, - {-3600, 0, "CET", "WEST", "Africa/Algiers"}, - {-3600, 2, "WAT", "WAST", "Africa/Windhoek"}, - {0, 1, "GMT", "IST", "Europe/Dublin"}, - {0, 1, "GMT", "BST", "Europe/London"}, - {0, 0, "WET", "WEST", "Africa/Casablanca"}, - {0, 0, "WET", "WET", "Africa/El_Aaiun"}, - {3600, 1, "AZOT", "AZOST", "Atlantic/Azores"}, - {3600, 1, "EGT", "EGST", "America/Scoresbysund"}, - {10800, 1, "PMST", "PMDT", "America/Miquelon"}, - {10800, 2, "UYT", "UYST", "America/Montevideo"}, - {10800, 1, "WGT", "WGST", "America/Godthab"}, - {10800, 2, "BRT", "BRST", "Brazil/East"}, - {12600, 1, "NST", "NDT", "America/St_Johns"}, - {14400, 1, "AST", "ADT", "Canada/Atlantic"}, - {14400, 2, "AMT", "AMST", "America/Cuiaba"}, - {14400, 2, "CLT", "CLST", "Chile/Continental"}, - {14400, 2, "FKT", "FKST", "Atlantic/Stanley"}, - {14400, 2, "PYT", "PYST", "America/Asuncion"}, - {18000, 1, "CST", "CDT", "America/Havana"}, - {18000, 1, "EST", "EDT", "US/Eastern"}, /* Conflicts with America/Grand_Turk */ - {21600, 2, "EAST", "EASST", "Chile/EasterIsland"}, - {21600, 0, "CST", "MDT", "Canada/Saskatchewan"}, - {21600, 0, "CST", "CDT", "America/Guatemala"}, - {21600, 1, "CST", "CDT", "US/Central"}, /* Conflicts with Mexico/General */ - {25200, 1, "MST", "MDT", "US/Mountain"}, /* Conflicts with Mexico/BajaSur */ - {28800, 0, "PST", "PST", "Pacific/Pitcairn"}, - {28800, 1, "PST", "PDT", "US/Pacific"}, /* Conflicts with Mexico/BajaNorte */ - {32400, 1, "AKST", "AKDT", "US/Alaska"}, - {36000, 1, "HAST", "HADT", "US/Aleutian"} -}; - -/*#define DEBUG_TZNAME*/ - -static const char* remapShortTimeZone(const char *stdID, const char *dstID, int32_t daylightType, int32_t offset) -{ - int32_t idx; -#ifdef DEBUG_TZNAME - fprintf(stderr, "TZ=%s std=%s dst=%s daylight=%d offset=%d\n", getenv("TZ"), stdID, dstID, daylightType, offset); -#endif - for (idx = 0; idx < UPRV_LENGTHOF(OFFSET_ZONE_MAPPINGS); idx++) - { - if (offset == OFFSET_ZONE_MAPPINGS[idx].offsetSeconds - && daylightType == OFFSET_ZONE_MAPPINGS[idx].daylightType - && strcmp(OFFSET_ZONE_MAPPINGS[idx].stdID, stdID) == 0 - && strcmp(OFFSET_ZONE_MAPPINGS[idx].dstID, dstID) == 0) - { - return OFFSET_ZONE_MAPPINGS[idx].olsonID; - } - } - return NULL; -} -#endif - -#ifdef SEARCH_TZFILE -#define MAX_READ_SIZE 512 - -typedef struct DefaultTZInfo { - char* defaultTZBuffer; - int64_t defaultTZFileSize; - FILE* defaultTZFilePtr; - UBool defaultTZstatus; - int32_t defaultTZPosition; -} DefaultTZInfo; - -/* - * This method compares the two files given to see if they are a match. - * It is currently use to compare two TZ files. - */ -static UBool compareBinaryFiles(const char* defaultTZFileName, const char* TZFileName, DefaultTZInfo* tzInfo) { - FILE* file; - int64_t sizeFile; - int64_t sizeFileLeft; - int32_t sizeFileRead; - int32_t sizeFileToRead; - char bufferFile[MAX_READ_SIZE]; - UBool result = true; - - if (tzInfo->defaultTZFilePtr == NULL) { - tzInfo->defaultTZFilePtr = fopen(defaultTZFileName, "r"); - } - file = fopen(TZFileName, "r"); - - tzInfo->defaultTZPosition = 0; /* reset position to begin search */ - - if (file != NULL && tzInfo->defaultTZFilePtr != NULL) { - /* First check that the file size are equal. */ - if (tzInfo->defaultTZFileSize == 0) { - fseek(tzInfo->defaultTZFilePtr, 0, SEEK_END); - tzInfo->defaultTZFileSize = ftell(tzInfo->defaultTZFilePtr); - } - fseek(file, 0, SEEK_END); - sizeFile = ftell(file); - sizeFileLeft = sizeFile; - - if (sizeFile != tzInfo->defaultTZFileSize) { - result = false; - } else { - /* Store the data from the files in separate buffers and - * compare each byte to determine equality. - */ - if (tzInfo->defaultTZBuffer == NULL) { - rewind(tzInfo->defaultTZFilePtr); - tzInfo->defaultTZBuffer = (char*)uprv_malloc(sizeof(char) * tzInfo->defaultTZFileSize); - sizeFileRead = fread(tzInfo->defaultTZBuffer, 1, tzInfo->defaultTZFileSize, tzInfo->defaultTZFilePtr); - } - rewind(file); - while(sizeFileLeft > 0) { - uprv_memset(bufferFile, 0, MAX_READ_SIZE); - sizeFileToRead = sizeFileLeft < MAX_READ_SIZE ? sizeFileLeft : MAX_READ_SIZE; - - sizeFileRead = fread(bufferFile, 1, sizeFileToRead, file); - if (memcmp(tzInfo->defaultTZBuffer + tzInfo->defaultTZPosition, bufferFile, sizeFileRead) != 0) { - result = false; - break; - } - sizeFileLeft -= sizeFileRead; - tzInfo->defaultTZPosition += sizeFileRead; - } - } - } else { - result = false; - } - - if (file != NULL) { - fclose(file); - } - - return result; -} - - -/* dirent also lists two entries: "." and ".." that we can safely ignore. */ -#define SKIP1 "." -#define SKIP2 ".." -static UBool U_CALLCONV putil_cleanup(void); -static CharString *gSearchTZFileResult = NULL; - -/* - * This method recursively traverses the directory given for a matching TZ file and returns the first match. - * This function is not thread safe - it uses a global, gSearchTZFileResult, to hold its results. - */ -static char* searchForTZFile(const char* path, DefaultTZInfo* tzInfo) { - DIR* dirp = NULL; - struct dirent* dirEntry = NULL; - char* result = NULL; - UErrorCode status = U_ZERO_ERROR; - - /* Save the current path */ - CharString curpath(path, -1, status); - if (U_FAILURE(status)) { - goto cleanupAndReturn; - } - - dirp = opendir(path); - if (dirp == NULL) { - goto cleanupAndReturn; - } - - if (gSearchTZFileResult == NULL) { - gSearchTZFileResult = new CharString; - if (gSearchTZFileResult == NULL) { - goto cleanupAndReturn; - } - ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup); - } - - /* Check each entry in the directory. */ - while((dirEntry = readdir(dirp)) != NULL) { - const char* dirName = dirEntry->d_name; - if (uprv_strcmp(dirName, SKIP1) != 0 && uprv_strcmp(dirName, SKIP2) != 0 - && uprv_strcmp(TZFILE_SKIP, dirName) != 0 && uprv_strcmp(TZFILE_SKIP2, dirName) != 0) { - /* Create a newpath with the new entry to test each entry in the directory. */ - CharString newpath(curpath, status); - newpath.append(dirName, -1, status); - if (U_FAILURE(status)) { - break; - } - - DIR* subDirp = NULL; - if ((subDirp = opendir(newpath.data())) != NULL) { - /* If this new path is a directory, make a recursive call with the newpath. */ - closedir(subDirp); - newpath.append('/', status); - if (U_FAILURE(status)) { - break; - } - result = searchForTZFile(newpath.data(), tzInfo); - /* - Have to get out here. Otherwise, we'd keep looking - and return the first match in the top-level directory - if there's a match in the top-level. If not, this function - would return NULL and set gTimeZoneBufferPtr to NULL in initDefault(). - It worked without this in most cases because we have a fallback of calling - localtime_r to figure out the default timezone. - */ - if (result != NULL) - break; - } else { - if(compareBinaryFiles(TZDEFAULT, newpath.data(), tzInfo)) { - int32_t amountToSkip = sizeof(TZZONEINFO) - 1; - if (amountToSkip > newpath.length()) { - amountToSkip = newpath.length(); - } - const char* zoneid = newpath.data() + amountToSkip; - skipZoneIDPrefix(&zoneid); - gSearchTZFileResult->clear(); - gSearchTZFileResult->append(zoneid, -1, status); - if (U_FAILURE(status)) { - break; - } - result = gSearchTZFileResult->data(); - /* Get out after the first one found. */ - break; - } - } - } - } - - cleanupAndReturn: - if (dirp) { - closedir(dirp); - } - return result; -} -#endif - -#if U_PLATFORM == U_PF_ANDROID -typedef int(system_property_read_callback)(const prop_info* info, - void (*callback)(void* cookie, - const char* name, - const char* value, - uint32_t serial), - void* cookie); -typedef int(system_property_get)(const char*, char*); - -static char gAndroidTimeZone[PROP_VALUE_MAX] = { '\0' }; - -static void u_property_read(void* cookie, const char* name, const char* value, - uint32_t serial) { - uprv_strcpy((char* )cookie, value); -} -#endif - -U_CAPI void U_EXPORT2 -uprv_tzname_clear_cache(void) -{ -#if U_PLATFORM == U_PF_ANDROID - /* Android's timezone is stored in system property. */ - gAndroidTimeZone[0] = '\0'; - void* libc = dlopen("libc.so", RTLD_NOLOAD); - if (libc) { - /* Android API 26+ has new API to get system property and old API - * (__system_property_get) is deprecated */ - system_property_read_callback* property_read_callback = - (system_property_read_callback*)dlsym( - libc, "__system_property_read_callback"); - if (property_read_callback) { - const prop_info* info = - __system_property_find("persist.sys.timezone"); - if (info) { - property_read_callback(info, &u_property_read, gAndroidTimeZone); - } - } else { - system_property_get* property_get = - (system_property_get*)dlsym(libc, "__system_property_get"); - if (property_get) { - property_get("persist.sys.timezone", gAndroidTimeZone); - } - } - dlclose(libc); - } -#endif - -#if defined(CHECK_LOCALTIME_LINK) && !defined(DEBUG_SKIP_LOCALTIME_LINK) - gTimeZoneBufferPtr = NULL; -#endif -} - -U_CAPI const char* U_EXPORT2 -uprv_tzname(int n) -{ - (void)n; // Avoid unreferenced parameter warning. - const char *tzid = NULL; -#if U_PLATFORM_USES_ONLY_WIN32_API - tzid = uprv_detectWindowsTimeZone(); - - if (tzid != NULL) { - return tzid; - } - -#ifndef U_TZNAME - // The return value is free'd in timezone.cpp on Windows because - // the other code path returns a pointer to a heap location. - // If we don't have a name already, then tzname wouldn't be any - // better, so just fall back. - return uprv_strdup(""); -#endif // !U_TZNAME - -#else - -/*#if U_PLATFORM_IS_DARWIN_BASED - int ret; - - tzid = getenv("TZFILE"); - if (tzid != NULL) { - return tzid; - } -#endif*/ - -/* This code can be temporarily disabled to test tzname resolution later on. */ -#ifndef DEBUG_TZNAME -#if U_PLATFORM == U_PF_ANDROID - tzid = gAndroidTimeZone; -#else - tzid = getenv("TZ"); -#endif - if (tzid != NULL && isValidOlsonID(tzid) -#if U_PLATFORM == U_PF_SOLARIS - /* Don't misinterpret TZ "localtime" on Solaris as a time zone name. */ - && uprv_strcmp(tzid, TZ_ENV_CHECK) != 0 -#endif - ) { - /* The colon forces tzset() to treat the remainder as zoneinfo path */ - if (tzid[0] == ':') { - tzid++; - } - /* This might be a good Olson ID. */ - skipZoneIDPrefix(&tzid); - return tzid; - } - /* else U_TZNAME will give a better result. */ -#endif - -#if defined(CHECK_LOCALTIME_LINK) && !defined(DEBUG_SKIP_LOCALTIME_LINK) - /* Caller must handle threading issues */ - if (gTimeZoneBufferPtr == NULL) { - /* - This is a trick to look at the name of the link to get the Olson ID - because the tzfile contents is underspecified. - This isn't guaranteed to work because it may not be a symlink. - */ - int32_t ret = (int32_t)readlink(TZDEFAULT, gTimeZoneBuffer, sizeof(gTimeZoneBuffer)-1); - if (0 < ret) { - int32_t tzZoneInfoTailLen = uprv_strlen(TZZONEINFOTAIL); - gTimeZoneBuffer[ret] = 0; - char * tzZoneInfoTailPtr = uprv_strstr(gTimeZoneBuffer, TZZONEINFOTAIL); - - if (tzZoneInfoTailPtr != NULL - && isValidOlsonID(tzZoneInfoTailPtr + tzZoneInfoTailLen)) - { - return (gTimeZoneBufferPtr = tzZoneInfoTailPtr + tzZoneInfoTailLen); - } - } else { -#if defined(SEARCH_TZFILE) - DefaultTZInfo* tzInfo = (DefaultTZInfo*)uprv_malloc(sizeof(DefaultTZInfo)); - if (tzInfo != NULL) { - tzInfo->defaultTZBuffer = NULL; - tzInfo->defaultTZFileSize = 0; - tzInfo->defaultTZFilePtr = NULL; - tzInfo->defaultTZstatus = false; - tzInfo->defaultTZPosition = 0; - - gTimeZoneBufferPtr = searchForTZFile(TZZONEINFO, tzInfo); - - /* Free previously allocated memory */ - if (tzInfo->defaultTZBuffer != NULL) { - uprv_free(tzInfo->defaultTZBuffer); - } - if (tzInfo->defaultTZFilePtr != NULL) { - fclose(tzInfo->defaultTZFilePtr); - } - uprv_free(tzInfo); - } - - if (gTimeZoneBufferPtr != NULL && isValidOlsonID(gTimeZoneBufferPtr)) { - return gTimeZoneBufferPtr; - } -#endif - } - } - else { - return gTimeZoneBufferPtr; - } -#endif -#endif - -#ifdef U_TZNAME -#if U_PLATFORM_USES_ONLY_WIN32_API - /* The return value is free'd in timezone.cpp on Windows because - * the other code path returns a pointer to a heap location. */ - return uprv_strdup(U_TZNAME[n]); -#else - /* - U_TZNAME is usually a non-unique abbreviation, which isn't normally usable. - So we remap the abbreviation to an olson ID. - - Since Windows exposes a little more timezone information, - we normally don't use this code on Windows because - uprv_detectWindowsTimeZone should have already given the correct answer. - */ - { - struct tm juneSol, decemberSol; - int daylightType; - static const time_t juneSolstice=1182478260; /*2007-06-21 18:11 UT*/ - static const time_t decemberSolstice=1198332540; /*2007-12-22 06:09 UT*/ - - /* This probing will tell us when daylight savings occurs. */ - localtime_r(&juneSolstice, &juneSol); - localtime_r(&decemberSolstice, &decemberSol); - if(decemberSol.tm_isdst > 0) { - daylightType = U_DAYLIGHT_DECEMBER; - } else if(juneSol.tm_isdst > 0) { - daylightType = U_DAYLIGHT_JUNE; - } else { - daylightType = U_DAYLIGHT_NONE; - } - tzid = remapShortTimeZone(U_TZNAME[0], U_TZNAME[1], daylightType, uprv_timezone()); - if (tzid != NULL) { - return tzid; - } - } - return U_TZNAME[n]; -#endif -#else - return ""; -#endif -} - -/* Get and set the ICU data directory --------------------------------------- */ - -static icu::UInitOnce gDataDirInitOnce {}; -static char *gDataDirectory = NULL; - -UInitOnce gTimeZoneFilesInitOnce {}; -static CharString *gTimeZoneFilesDirectory = NULL; - -#if U_POSIX_LOCALE || U_PLATFORM_USES_ONLY_WIN32_API - static const char *gCorrectedPOSIXLocale = NULL; /* Sometimes heap allocated */ - static bool gCorrectedPOSIXLocaleHeapAllocated = false; -#endif - -static UBool U_CALLCONV putil_cleanup(void) -{ - if (gDataDirectory && *gDataDirectory) { - uprv_free(gDataDirectory); - } - gDataDirectory = NULL; - gDataDirInitOnce.reset(); - - delete gTimeZoneFilesDirectory; - gTimeZoneFilesDirectory = NULL; - gTimeZoneFilesInitOnce.reset(); - -#ifdef SEARCH_TZFILE - delete gSearchTZFileResult; - gSearchTZFileResult = NULL; -#endif - -#if U_POSIX_LOCALE || U_PLATFORM_USES_ONLY_WIN32_API - if (gCorrectedPOSIXLocale && gCorrectedPOSIXLocaleHeapAllocated) { - uprv_free(const_cast(gCorrectedPOSIXLocale)); - gCorrectedPOSIXLocale = NULL; - gCorrectedPOSIXLocaleHeapAllocated = false; - } -#endif - return true; -} - -/* - * Set the data directory. - * Make a copy of the passed string, and set the global data dir to point to it. - */ -U_CAPI void U_EXPORT2 -u_setDataDirectory(const char *directory) { - char *newDataDir; - int32_t length; - - if(directory==NULL || *directory==0) { - /* A small optimization to prevent the malloc and copy when the - shared library is used, and this is a way to make sure that NULL - is never returned. - */ - newDataDir = (char *)""; - } - else { - length=(int32_t)uprv_strlen(directory); - newDataDir = (char *)uprv_malloc(length + 2); - /* Exit out if newDataDir could not be created. */ - if (newDataDir == NULL) { - return; - } - uprv_strcpy(newDataDir, directory); - -#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) - { - char *p; - while((p = uprv_strchr(newDataDir, U_FILE_ALT_SEP_CHAR)) != NULL) { - *p = U_FILE_SEP_CHAR; - } - } -#endif - } - - if (gDataDirectory && *gDataDirectory) { - uprv_free(gDataDirectory); - } - gDataDirectory = newDataDir; - ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup); -} - -U_CAPI UBool U_EXPORT2 -uprv_pathIsAbsolute(const char *path) -{ - if(!path || !*path) { - return false; - } - - if(*path == U_FILE_SEP_CHAR) { - return true; - } - -#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) - if(*path == U_FILE_ALT_SEP_CHAR) { - return true; - } -#endif - -#if U_PLATFORM_USES_ONLY_WIN32_API - if( (((path[0] >= 'A') && (path[0] <= 'Z')) || - ((path[0] >= 'a') && (path[0] <= 'z'))) && - path[1] == ':' ) { - return true; - } -#endif - - return false; -} - -/* Backup setting of ICU_DATA_DIR_PREFIX_ENV_VAR - (needed for some Darwin ICU build environments) */ -#if U_PLATFORM_IS_DARWIN_BASED && defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR -# if !defined(ICU_DATA_DIR_PREFIX_ENV_VAR) -# define ICU_DATA_DIR_PREFIX_ENV_VAR "IPHONE_SIMULATOR_ROOT" -# endif -#endif - -#if defined(ICU_DATA_DIR_WINDOWS) -// Helper function to get the ICU Data Directory under the Windows directory location. -static BOOL U_CALLCONV getIcuDataDirectoryUnderWindowsDirectory(char* directoryBuffer, UINT bufferLength) -{ - wchar_t windowsPath[MAX_PATH]; - char windowsPathUtf8[MAX_PATH]; - - UINT length = GetSystemWindowsDirectoryW(windowsPath, UPRV_LENGTHOF(windowsPath)); - if ((length > 0) && (length < (UPRV_LENGTHOF(windowsPath) - 1))) { - // Convert UTF-16 to a UTF-8 string. - UErrorCode status = U_ZERO_ERROR; - int32_t windowsPathUtf8Len = 0; - u_strToUTF8(windowsPathUtf8, static_cast(UPRV_LENGTHOF(windowsPathUtf8)), - &windowsPathUtf8Len, reinterpret_cast(windowsPath), -1, &status); - - if (U_SUCCESS(status) && (status != U_STRING_NOT_TERMINATED_WARNING) && - (windowsPathUtf8Len < (UPRV_LENGTHOF(windowsPathUtf8) - 1))) { - // Ensure it always has a separator, so we can append the ICU data path. - if (windowsPathUtf8[windowsPathUtf8Len - 1] != U_FILE_SEP_CHAR) { - windowsPathUtf8[windowsPathUtf8Len++] = U_FILE_SEP_CHAR; - windowsPathUtf8[windowsPathUtf8Len] = '\0'; - } - // Check if the concatenated string will fit. - if ((windowsPathUtf8Len + UPRV_LENGTHOF(ICU_DATA_DIR_WINDOWS)) < bufferLength) { - uprv_strcpy(directoryBuffer, windowsPathUtf8); - uprv_strcat(directoryBuffer, ICU_DATA_DIR_WINDOWS); - return true; - } - } - } - - return false; -} -#endif - -static void U_CALLCONV dataDirectoryInitFn() { - /* If we already have the directory, then return immediately. Will happen if user called - * u_setDataDirectory(). - */ - if (gDataDirectory) { - return; - } - - const char *path = NULL; -#if defined(ICU_DATA_DIR_PREFIX_ENV_VAR) - char datadir_path_buffer[PATH_MAX]; -#endif - - /* - When ICU_NO_USER_DATA_OVERRIDE is defined, users aren't allowed to - override ICU's data with the ICU_DATA environment variable. This prevents - problems where multiple custom copies of ICU's specific version of data - are installed on a system. Either the application must define the data - directory with u_setDataDirectory, define ICU_DATA_DIR when compiling - ICU, set the data with udata_setCommonData or trust that all of the - required data is contained in ICU's data library that contains - the entry point defined by U_ICUDATA_ENTRY_POINT. - - There may also be some platforms where environment variables - are not allowed. - */ -# if !defined(ICU_NO_USER_DATA_OVERRIDE) && !UCONFIG_NO_FILE_IO - /* First try to get the environment variable */ -# if U_PLATFORM_HAS_WINUWP_API == 0 // Windows UWP does not support getenv - path=getenv("ICU_DATA"); -# endif -# endif - - /* ICU_DATA_DIR may be set as a compile option. - * U_ICU_DATA_DEFAULT_DIR is provided and is set by ICU at compile time - * and is used only when data is built in archive mode eliminating the need - * for ICU_DATA_DIR to be set. U_ICU_DATA_DEFAULT_DIR is set to the installation - * directory of the data dat file. Users should use ICU_DATA_DIR if they want to - * set their own path. - */ -#if defined(ICU_DATA_DIR) || defined(U_ICU_DATA_DEFAULT_DIR) - if(path==NULL || *path==0) { -# if defined(ICU_DATA_DIR_PREFIX_ENV_VAR) - const char *prefix = getenv(ICU_DATA_DIR_PREFIX_ENV_VAR); -# endif -# ifdef ICU_DATA_DIR - path=ICU_DATA_DIR; -# else - path=U_ICU_DATA_DEFAULT_DIR; -# endif -# if defined(ICU_DATA_DIR_PREFIX_ENV_VAR) - if (prefix != NULL) { - snprintf(datadir_path_buffer, PATH_MAX, "%s%s", prefix, path); - path=datadir_path_buffer; - } -# endif - } -#endif - -#if defined(ICU_DATA_DIR_WINDOWS) - char datadir_path_buffer[MAX_PATH]; - if (getIcuDataDirectoryUnderWindowsDirectory(datadir_path_buffer, UPRV_LENGTHOF(datadir_path_buffer))) { - path = datadir_path_buffer; - } -#endif - - if(path==NULL) { - /* It looks really bad, set it to something. */ - path = ""; - } - - u_setDataDirectory(path); - return; -} - -U_CAPI const char * U_EXPORT2 -u_getDataDirectory(void) { - umtx_initOnce(gDataDirInitOnce, &dataDirectoryInitFn); - return gDataDirectory; -} - -static void setTimeZoneFilesDir(const char *path, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - gTimeZoneFilesDirectory->clear(); - gTimeZoneFilesDirectory->append(path, status); -#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) - char *p = gTimeZoneFilesDirectory->data(); - while ((p = uprv_strchr(p, U_FILE_ALT_SEP_CHAR)) != NULL) { - *p = U_FILE_SEP_CHAR; - } -#endif -} - -#define TO_STRING(x) TO_STRING_2(x) -#define TO_STRING_2(x) #x - -static void U_CALLCONV TimeZoneDataDirInitFn(UErrorCode &status) { - U_ASSERT(gTimeZoneFilesDirectory == NULL); - ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup); - gTimeZoneFilesDirectory = new CharString(); - if (gTimeZoneFilesDirectory == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - const char *dir = ""; - -#if defined(ICU_TIMEZONE_FILES_DIR_PREFIX_ENV_VAR) - char timezonefilesdir_path_buffer[PATH_MAX]; - const char *prefix = getenv(ICU_TIMEZONE_FILES_DIR_PREFIX_ENV_VAR); -#endif - -#if U_PLATFORM_HAS_WINUWP_API == 1 -// The UWP version does not support the environment variable setting. - -# if defined(ICU_DATA_DIR_WINDOWS) - // When using the Windows system data, we can possibly pick up time zone data from the Windows directory. - char datadir_path_buffer[MAX_PATH]; - if (getIcuDataDirectoryUnderWindowsDirectory(datadir_path_buffer, UPRV_LENGTHOF(datadir_path_buffer))) { - dir = datadir_path_buffer; - } -# endif - -#else - dir = getenv("ICU_TIMEZONE_FILES_DIR"); -#endif // U_PLATFORM_HAS_WINUWP_API - -#if defined(U_TIMEZONE_FILES_DIR) - if (dir == NULL) { - // Build time configuration setting. - dir = TO_STRING(U_TIMEZONE_FILES_DIR); - } -#endif - - if (dir == NULL) { - dir = ""; - } - -#if defined(ICU_TIMEZONE_FILES_DIR_PREFIX_ENV_VAR) - if (prefix != NULL) { - snprintf(timezonefilesdir_path_buffer, PATH_MAX, "%s%s", prefix, dir); - dir = timezonefilesdir_path_buffer; - } -#endif - - setTimeZoneFilesDir(dir, status); -} - - -U_CAPI const char * U_EXPORT2 -u_getTimeZoneFilesDirectory(UErrorCode *status) { - umtx_initOnce(gTimeZoneFilesInitOnce, &TimeZoneDataDirInitFn, *status); - return U_SUCCESS(*status) ? gTimeZoneFilesDirectory->data() : ""; -} - -U_CAPI void U_EXPORT2 -u_setTimeZoneFilesDirectory(const char *path, UErrorCode *status) { - umtx_initOnce(gTimeZoneFilesInitOnce, &TimeZoneDataDirInitFn, *status); - setTimeZoneFilesDir(path, *status); - - // Note: this function does some extra churn, first setting based on the - // environment, then immediately replacing with the value passed in. - // The logic is simpler that way, and performance shouldn't be an issue. -} - - -#if U_POSIX_LOCALE -/* A helper function used by uprv_getPOSIXIDForDefaultLocale and - * uprv_getPOSIXIDForDefaultCodepage. Returns the posix locale id for - * LC_CTYPE and LC_MESSAGES. It doesn't support other locale categories. - */ -static const char *uprv_getPOSIXIDForCategory(int category) -{ - const char* posixID = NULL; - if (category == LC_MESSAGES || category == LC_CTYPE) { - /* - * On Solaris two different calls to setlocale can result in - * different values. Only get this value once. - * - * We must check this first because an application can set this. - * - * LC_ALL can't be used because it's platform dependent. The LANG - * environment variable seems to affect LC_CTYPE variable by default. - * Here is what setlocale(LC_ALL, NULL) can return. - * HPUX can return 'C C C C C C C' - * Solaris can return /en_US/C/C/C/C/C on the second try. - * Linux can return LC_CTYPE=C;LC_NUMERIC=C;... - * - * The default codepage detection also needs to use LC_CTYPE. - * - * Do not call setlocale(LC_*, "")! Using an empty string instead - * of NULL, will modify the libc behavior. - */ - posixID = setlocale(category, NULL); - if ((posixID == 0) - || (uprv_strcmp("C", posixID) == 0) - || (uprv_strcmp("POSIX", posixID) == 0)) - { - /* Maybe we got some garbage. Try something more reasonable */ - posixID = getenv("LC_ALL"); - /* Solaris speaks POSIX - See IEEE Std 1003.1-2008 - * This is needed to properly handle empty env. variables - */ -#if U_PLATFORM == U_PF_SOLARIS - if ((posixID == 0) || (posixID[0] == '\0')) { - posixID = getenv(category == LC_MESSAGES ? "LC_MESSAGES" : "LC_CTYPE"); - if ((posixID == 0) || (posixID[0] == '\0')) { -#else - if (posixID == 0) { - posixID = getenv(category == LC_MESSAGES ? "LC_MESSAGES" : "LC_CTYPE"); - if (posixID == 0) { -#endif - posixID = getenv("LANG"); - } - } - } - } - if ((posixID==0) - || (uprv_strcmp("C", posixID) == 0) - || (uprv_strcmp("POSIX", posixID) == 0)) - { - /* Nothing worked. Give it a nice POSIX default value. */ - posixID = "en_US_POSIX"; - // Note: this test will not catch 'C.UTF-8', - // that will be handled in uprv_getDefaultLocaleID(). - // Leave this mapping here for the uprv_getPOSIXIDForDefaultCodepage() - // caller which expects to see "en_US_POSIX" in many branches. - } - return posixID; -} - -/* Return just the POSIX id for the default locale, whatever happens to be in - * it. It gets the value from LC_MESSAGES and indirectly from LC_ALL and LANG. - */ -static const char *uprv_getPOSIXIDForDefaultLocale(void) -{ - static const char* posixID = NULL; - if (posixID == 0) { - posixID = uprv_getPOSIXIDForCategory(LC_MESSAGES); - } - return posixID; -} - -#if !U_CHARSET_IS_UTF8 -/* Return just the POSIX id for the default codepage, whatever happens to be in - * it. It gets the value from LC_CTYPE and indirectly from LC_ALL and LANG. - */ -static const char *uprv_getPOSIXIDForDefaultCodepage(void) -{ - static const char* posixID = NULL; - if (posixID == 0) { - posixID = uprv_getPOSIXIDForCategory(LC_CTYPE); - } - return posixID; -} -#endif -#endif - -/* NOTE: The caller should handle thread safety */ -U_CAPI const char* U_EXPORT2 -uprv_getDefaultLocaleID() -{ -#if U_POSIX_LOCALE -/* - Note that: (a '!' means the ID is improper somehow) - LC_ALL ----> default_loc codepage --------------------------------------------------------- - ab.CD ab CD - ab@CD ab__CD - - ab@CD.EF ab__CD EF - - ab_CD.EF@GH ab_CD_GH EF - -Some 'improper' ways to do the same as above: - ! ab_CD@GH.EF ab_CD_GH EF - ! ab_CD.EF@GH.IJ ab_CD_GH EF - ! ab_CD@ZZ.EF@GH.IJ ab_CD_GH EF - - _CD@GH _CD_GH - - _CD.EF@GH _CD_GH EF - -The variant cannot have dots in it. -The 'rightmost' variant (@xxx) wins. -The leftmost codepage (.xxx) wins. -*/ - const char* posixID = uprv_getPOSIXIDForDefaultLocale(); - - /* Format: (no spaces) - ll [ _CC ] [ . MM ] [ @ VV] - - l = lang, C = ctry, M = charmap, V = variant - */ - - if (gCorrectedPOSIXLocale != nullptr) { - return gCorrectedPOSIXLocale; - } - - // Copy the ID into owned memory. - // Over-allocate in case we replace "C" with "en_US_POSIX" (+10), + null termination - char *correctedPOSIXLocale = static_cast(uprv_malloc(uprv_strlen(posixID) + 10 + 1)); - if (correctedPOSIXLocale == nullptr) { - return nullptr; - } - uprv_strcpy(correctedPOSIXLocale, posixID); - - char *limit; - if ((limit = uprv_strchr(correctedPOSIXLocale, '.')) != nullptr) { - *limit = 0; - } - if ((limit = uprv_strchr(correctedPOSIXLocale, '@')) != nullptr) { - *limit = 0; - } - - if ((uprv_strcmp("C", correctedPOSIXLocale) == 0) // no @ variant - || (uprv_strcmp("POSIX", correctedPOSIXLocale) == 0)) { - // Raw input was C.* or POSIX.*, Give it a nice POSIX default value. - // (The "C"/"POSIX" case is handled in uprv_getPOSIXIDForCategory()) - uprv_strcpy(correctedPOSIXLocale, "en_US_POSIX"); - } - - /* Note that we scan the *uncorrected* ID. */ - const char *p; - if ((p = uprv_strrchr(posixID, '@')) != nullptr) { - p++; - - /* Take care of any special cases here.. */ - if (!uprv_strcmp(p, "nynorsk")) { - p = "NY"; - /* Don't worry about no__NY. In practice, it won't appear. */ - } - - if (uprv_strchr(correctedPOSIXLocale,'_') == nullptr) { - uprv_strcat(correctedPOSIXLocale, "__"); /* aa@b -> aa__b (note this can make the new locale 1 char longer) */ - } - else { - uprv_strcat(correctedPOSIXLocale, "_"); /* aa_CC@b -> aa_CC_b */ - } - - const char *q; - if ((q = uprv_strchr(p, '.')) != nullptr) { - /* How big will the resulting string be? */ - int32_t len = (int32_t)(uprv_strlen(correctedPOSIXLocale) + (q-p)); - uprv_strncat(correctedPOSIXLocale, p, q-p); // do not include charset - correctedPOSIXLocale[len] = 0; - } - else { - /* Anything following the @ sign */ - uprv_strcat(correctedPOSIXLocale, p); - } - - /* Should there be a map from 'no@nynorsk' -> no_NO_NY here? - * How about 'russian' -> 'ru'? - * Many of the other locales using ISO codes will be handled by the - * canonicalization functions in uloc_getDefault. - */ - } - - if (gCorrectedPOSIXLocale == nullptr) { - gCorrectedPOSIXLocale = correctedPOSIXLocale; - gCorrectedPOSIXLocaleHeapAllocated = true; - ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup); - correctedPOSIXLocale = nullptr; - } - posixID = gCorrectedPOSIXLocale; - - if (correctedPOSIXLocale != nullptr) { /* Was already set - clean up. */ - uprv_free(correctedPOSIXLocale); - } - - return posixID; - -#elif U_PLATFORM_USES_ONLY_WIN32_API -#define POSIX_LOCALE_CAPACITY 64 - UErrorCode status = U_ZERO_ERROR; - char *correctedPOSIXLocale = nullptr; - - // If we have already figured this out just use the cached value - if (gCorrectedPOSIXLocale != nullptr) { - return gCorrectedPOSIXLocale; - } - - // No cached value, need to determine the current value - static WCHAR windowsLocale[LOCALE_NAME_MAX_LENGTH] = {}; - int length = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, windowsLocale, LOCALE_NAME_MAX_LENGTH); - - // Now we should have a Windows locale name that needs converted to the POSIX style. - if (length > 0) // If length is 0, then the GetLocaleInfoEx failed. - { - // First we need to go from UTF-16 to char (and also convert from _ to - while we're at it.) - char modifiedWindowsLocale[LOCALE_NAME_MAX_LENGTH] = {}; - - int32_t i; - for (i = 0; i < UPRV_LENGTHOF(modifiedWindowsLocale); i++) - { - if (windowsLocale[i] == '_') - { - modifiedWindowsLocale[i] = '-'; - } - else - { - modifiedWindowsLocale[i] = static_cast(windowsLocale[i]); - } - - if (modifiedWindowsLocale[i] == '\0') - { - break; - } - } - - if (i >= UPRV_LENGTHOF(modifiedWindowsLocale)) - { - // Ran out of room, can't really happen, maybe we'll be lucky about a matching - // locale when tags are dropped - modifiedWindowsLocale[UPRV_LENGTHOF(modifiedWindowsLocale) - 1] = '\0'; - } - - // Now normalize the resulting name - correctedPOSIXLocale = static_cast(uprv_malloc(POSIX_LOCALE_CAPACITY + 1)); - /* TODO: Should we just exit on memory allocation failure? */ - if (correctedPOSIXLocale) - { - int32_t posixLen = uloc_canonicalize(modifiedWindowsLocale, correctedPOSIXLocale, POSIX_LOCALE_CAPACITY, &status); - if (U_SUCCESS(status)) - { - *(correctedPOSIXLocale + posixLen) = 0; - gCorrectedPOSIXLocale = correctedPOSIXLocale; - gCorrectedPOSIXLocaleHeapAllocated = true; - ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup); - } - else - { - uprv_free(correctedPOSIXLocale); - } - } - } - - // If unable to find a locale we can agree upon, use en-US by default - if (gCorrectedPOSIXLocale == nullptr) { - gCorrectedPOSIXLocale = "en_US"; - } - return gCorrectedPOSIXLocale; - -#elif U_PLATFORM == U_PF_OS400 - /* locales are process scoped and are by definition thread safe */ - static char correctedLocale[64]; - const char *localeID = getenv("LC_ALL"); - char *p; - - if (localeID == NULL) - localeID = getenv("LANG"); - if (localeID == NULL) - localeID = setlocale(LC_ALL, NULL); - /* Make sure we have something... */ - if (localeID == NULL) - return "en_US_POSIX"; - - /* Extract the locale name from the path. */ - if((p = uprv_strrchr(localeID, '/')) != NULL) - { - /* Increment p to start of locale name. */ - p++; - localeID = p; - } - - /* Copy to work location. */ - uprv_strcpy(correctedLocale, localeID); - - /* Strip off the '.locale' extension. */ - if((p = uprv_strchr(correctedLocale, '.')) != NULL) { - *p = 0; - } - - /* Upper case the locale name. */ - T_CString_toUpperCase(correctedLocale); - - /* See if we are using the POSIX locale. Any of the - * following are equivalent and use the same QLGPGCMA - * (POSIX) locale. - * QLGPGCMA2 means UCS2 - * QLGPGCMA_4 means UTF-32 - * QLGPGCMA_8 means UTF-8 - */ - if ((uprv_strcmp("C", correctedLocale) == 0) || - (uprv_strcmp("POSIX", correctedLocale) == 0) || - (uprv_strncmp("QLGPGCMA", correctedLocale, 8) == 0)) - { - uprv_strcpy(correctedLocale, "en_US_POSIX"); - } - else - { - int16_t LocaleLen; - - /* Lower case the lang portion. */ - for(p = correctedLocale; *p != 0 && *p != '_'; p++) - { - *p = uprv_tolower(*p); - } - - /* Adjust for Euro. After '_E' add 'URO'. */ - LocaleLen = uprv_strlen(correctedLocale); - if (correctedLocale[LocaleLen - 2] == '_' && - correctedLocale[LocaleLen - 1] == 'E') - { - uprv_strcat(correctedLocale, "URO"); - } - - /* If using Lotus-based locale then convert to - * equivalent non Lotus. - */ - else if (correctedLocale[LocaleLen - 2] == '_' && - correctedLocale[LocaleLen - 1] == 'L') - { - correctedLocale[LocaleLen - 2] = 0; - } - - /* There are separate simplified and traditional - * locales called zh_HK_S and zh_HK_T. - */ - else if (uprv_strncmp(correctedLocale, "zh_HK", 5) == 0) - { - uprv_strcpy(correctedLocale, "zh_HK"); - } - - /* A special zh_CN_GBK locale... - */ - else if (uprv_strcmp(correctedLocale, "zh_CN_GBK") == 0) - { - uprv_strcpy(correctedLocale, "zh_CN"); - } - - } - - return correctedLocale; -#endif - -} - -#if !U_CHARSET_IS_UTF8 -#if U_POSIX_LOCALE -/* -Due to various platform differences, one platform may specify a charset, -when they really mean a different charset. Remap the names so that they are -compatible with ICU. Only conflicting/ambiguous aliases should be resolved -here. Before adding anything to this function, please consider adding unique -names to the ICU alias table in the data directory. -*/ -static const char* -remapPlatformDependentCodepage(const char *locale, const char *name) { - if (locale != NULL && *locale == 0) { - /* Make sure that an empty locale is handled the same way. */ - locale = NULL; - } - if (name == NULL) { - return NULL; - } -#if U_PLATFORM == U_PF_AIX - if (uprv_strcmp(name, "IBM-943") == 0) { - /* Use the ASCII compatible ibm-943 */ - name = "Shift-JIS"; - } - else if (uprv_strcmp(name, "IBM-1252") == 0) { - /* Use the windows-1252 that contains the Euro */ - name = "IBM-5348"; - } -#elif U_PLATFORM == U_PF_SOLARIS - if (locale != NULL && uprv_strcmp(name, "EUC") == 0) { - /* Solaris underspecifies the "EUC" name. */ - if (uprv_strcmp(locale, "zh_CN") == 0) { - name = "EUC-CN"; - } - else if (uprv_strcmp(locale, "zh_TW") == 0) { - name = "EUC-TW"; - } - else if (uprv_strcmp(locale, "ko_KR") == 0) { - name = "EUC-KR"; - } - } - else if (uprv_strcmp(name, "eucJP") == 0) { - /* - ibm-954 is the best match. - ibm-33722 is the default for eucJP (similar to Windows). - */ - name = "eucjis"; - } - else if (uprv_strcmp(name, "646") == 0) { - /* - * The default codepage given by Solaris is 646 but the C library routines treat it as if it was - * ISO-8859-1 instead of US-ASCII(646). - */ - name = "ISO-8859-1"; - } -#elif U_PLATFORM_IS_DARWIN_BASED - if (locale == NULL && *name == 0) { - /* - No locale was specified, and an empty name was passed in. - This usually indicates that nl_langinfo didn't return valid information. - Mac OS X uses UTF-8 by default (especially the locale data and console). - */ - name = "UTF-8"; - } - else if (uprv_strcmp(name, "CP949") == 0) { - /* Remap CP949 to a similar codepage to avoid issues with backslash and won symbol. */ - name = "EUC-KR"; - } - else if (locale != NULL && uprv_strcmp(locale, "en_US_POSIX") != 0 && uprv_strcmp(name, "US-ASCII") == 0) { - /* - * For non C/POSIX locale, default the code page to UTF-8 instead of US-ASCII. - */ - name = "UTF-8"; - } -#elif U_PLATFORM == U_PF_BSD - if (uprv_strcmp(name, "CP949") == 0) { - /* Remap CP949 to a similar codepage to avoid issues with backslash and won symbol. */ - name = "EUC-KR"; - } -#elif U_PLATFORM == U_PF_HPUX - if (locale != NULL && uprv_strcmp(locale, "zh_HK") == 0 && uprv_strcmp(name, "big5") == 0) { - /* HP decided to extend big5 as hkbig5 even though it's not compatible :-( */ - /* zh_TW.big5 is not the same charset as zh_HK.big5! */ - name = "hkbig5"; - } - else if (uprv_strcmp(name, "eucJP") == 0) { - /* - ibm-1350 is the best match, but unavailable. - ibm-954 is mostly a superset of ibm-1350. - ibm-33722 is the default for eucJP (similar to Windows). - */ - name = "eucjis"; - } -#elif U_PLATFORM == U_PF_LINUX - if (locale != NULL && uprv_strcmp(name, "euc") == 0) { - /* Linux underspecifies the "EUC" name. */ - if (uprv_strcmp(locale, "korean") == 0) { - name = "EUC-KR"; - } - else if (uprv_strcmp(locale, "japanese") == 0) { - /* See comment below about eucJP */ - name = "eucjis"; - } - } - else if (uprv_strcmp(name, "eucjp") == 0) { - /* - ibm-1350 is the best match, but unavailable. - ibm-954 is mostly a superset of ibm-1350. - ibm-33722 is the default for eucJP (similar to Windows). - */ - name = "eucjis"; - } - else if (locale != NULL && uprv_strcmp(locale, "en_US_POSIX") != 0 && - (uprv_strcmp(name, "ANSI_X3.4-1968") == 0 || uprv_strcmp(name, "US-ASCII") == 0)) { - /* - * For non C/POSIX locale, default the code page to UTF-8 instead of US-ASCII. - */ - name = "UTF-8"; - } - /* - * Linux returns ANSI_X3.4-1968 for C/POSIX, but the call site takes care of - * it by falling back to 'US-ASCII' when NULL is returned from this - * function. So, we don't have to worry about it here. - */ -#endif - /* return NULL when "" is passed in */ - if (*name == 0) { - name = NULL; - } - return name; -} - -static const char* -getCodepageFromPOSIXID(const char *localeName, char * buffer, int32_t buffCapacity) -{ - char localeBuf[100]; - const char *name = NULL; - char *variant = NULL; - - if (localeName != NULL && (name = (uprv_strchr(localeName, '.'))) != NULL) { - size_t localeCapacity = uprv_min(sizeof(localeBuf), (name-localeName)+1); - uprv_strncpy(localeBuf, localeName, localeCapacity); - localeBuf[localeCapacity-1] = 0; /* ensure NULL termination */ - name = uprv_strncpy(buffer, name+1, buffCapacity); - buffer[buffCapacity-1] = 0; /* ensure NULL termination */ - if ((variant = const_cast(uprv_strchr(name, '@'))) != NULL) { - *variant = 0; - } - name = remapPlatformDependentCodepage(localeBuf, name); - } - return name; -} -#endif - -static const char* -int_getDefaultCodepage() -{ -#if U_PLATFORM == U_PF_OS400 - uint32_t ccsid = 37; /* Default to ibm-37 */ - static char codepage[64]; - Qwc_JOBI0400_t jobinfo; - Qus_EC_t error = { sizeof(Qus_EC_t) }; /* SPI error code */ - - EPT_CALL(QUSRJOBI)(&jobinfo, sizeof(jobinfo), "JOBI0400", - "* ", " ", &error); - - if (error.Bytes_Available == 0) { - if (jobinfo.Coded_Char_Set_ID != 0xFFFF) { - ccsid = (uint32_t)jobinfo.Coded_Char_Set_ID; - } - else if (jobinfo.Default_Coded_Char_Set_Id != 0xFFFF) { - ccsid = (uint32_t)jobinfo.Default_Coded_Char_Set_Id; - } - /* else use the default */ - } - sprintf(codepage,"ibm-%d", ccsid); - return codepage; - -#elif U_PLATFORM == U_PF_OS390 - static char codepage[64]; - - strncpy(codepage, nl_langinfo(CODESET),63-strlen(UCNV_SWAP_LFNL_OPTION_STRING)); - strcat(codepage,UCNV_SWAP_LFNL_OPTION_STRING); - codepage[63] = 0; /* NULL terminate */ - - return codepage; - -#elif U_PLATFORM_USES_ONLY_WIN32_API - static char codepage[64]; - DWORD codepageNumber = 0; - -#if U_PLATFORM_HAS_WINUWP_API == 1 - // UWP doesn't have a direct API to get the default ACP as Microsoft would rather - // have folks use Unicode than a "system" code page, however this is the same - // codepage as the system default locale codepage. (FWIW, the system locale is - // ONLY used for codepage, it should never be used for anything else) - GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, - (LPWSTR)&codepageNumber, sizeof(codepageNumber) / sizeof(WCHAR)); -#else - // Win32 apps can call GetACP - codepageNumber = GetACP(); -#endif - // Special case for UTF-8 - if (codepageNumber == 65001) - { - return "UTF-8"; - } - // Windows codepages can look like windows-1252, so format the found number - // the numbers are eclectic, however all valid system code pages, besides UTF-8 - // are between 3 and 19999 - if (codepageNumber > 0 && codepageNumber < 20000) - { - sprintf(codepage, "windows-%ld", codepageNumber); - return codepage; - } - // If the codepage number call failed then return UTF-8 - return "UTF-8"; - -#elif U_POSIX_LOCALE - static char codesetName[100]; - const char *localeName = NULL; - const char *name = NULL; - - localeName = uprv_getPOSIXIDForDefaultCodepage(); - uprv_memset(codesetName, 0, sizeof(codesetName)); - /* On Solaris nl_langinfo returns C locale values unless setlocale - * was called earlier. - */ -#if (U_HAVE_NL_LANGINFO_CODESET && U_PLATFORM != U_PF_SOLARIS) - /* When available, check nl_langinfo first because it usually gives more - useful names. It depends on LC_CTYPE. - nl_langinfo may use the same buffer as setlocale. */ - { - const char *codeset = nl_langinfo(U_NL_LANGINFO_CODESET); -#if U_PLATFORM_IS_DARWIN_BASED || U_PLATFORM_IS_LINUX_BASED - /* - * On Linux and MacOSX, ensure that default codepage for non C/POSIX locale is UTF-8 - * instead of ASCII. - */ - if (uprv_strcmp(localeName, "en_US_POSIX") != 0) { - codeset = remapPlatformDependentCodepage(localeName, codeset); - } else -#endif - { - codeset = remapPlatformDependentCodepage(NULL, codeset); - } - - if (codeset != NULL) { - uprv_strncpy(codesetName, codeset, sizeof(codesetName)); - codesetName[sizeof(codesetName)-1] = 0; - return codesetName; - } - } -#endif - - /* Use setlocale in a nice way, and then check some environment variables. - Maybe the application used setlocale already. - */ - uprv_memset(codesetName, 0, sizeof(codesetName)); - name = getCodepageFromPOSIXID(localeName, codesetName, sizeof(codesetName)); - if (name) { - /* if we can find the codeset name from setlocale, return that. */ - return name; - } - - if (*codesetName == 0) - { - /* Everything failed. Return US ASCII (ISO 646). */ - (void)uprv_strcpy(codesetName, "US-ASCII"); - } - return codesetName; -#else - return "US-ASCII"; -#endif -} - - -U_CAPI const char* U_EXPORT2 -uprv_getDefaultCodepage() -{ - static char const *name = NULL; - umtx_lock(NULL); - if (name == NULL) { - name = int_getDefaultCodepage(); - } - umtx_unlock(NULL); - return name; -} -#endif /* !U_CHARSET_IS_UTF8 */ - - -/* end of platform-specific implementation -------------- */ - -/* version handling --------------------------------------------------------- */ - -U_CAPI void U_EXPORT2 -u_versionFromString(UVersionInfo versionArray, const char *versionString) { - char *end; - uint16_t part=0; - - if(versionArray==NULL) { - return; - } - - if(versionString!=NULL) { - for(;;) { - versionArray[part]=(uint8_t)uprv_strtoul(versionString, &end, 10); - if(end==versionString || ++part==U_MAX_VERSION_LENGTH || *end!=U_VERSION_DELIMITER) { - break; - } - versionString=end+1; - } - } - - while(partU_MAX_VERSION_STRING_LENGTH) { - len = U_MAX_VERSION_STRING_LENGTH; - } - u_UCharsToChars(versionString, versionChars, len); - versionChars[len]=0; - u_versionFromString(versionArray, versionChars); - } -} - -U_CAPI void U_EXPORT2 -u_versionToString(const UVersionInfo versionArray, char *versionString) { - uint16_t count, part; - uint8_t field; - - if(versionString==NULL) { - return; - } - - if(versionArray==NULL) { - versionString[0]=0; - return; - } - - /* count how many fields need to be written */ - for(count=4; count>0 && versionArray[count-1]==0; --count) { - } - - if(count <= 1) { - count = 2; - } - - /* write the first part */ - /* write the decimal field value */ - field=versionArray[0]; - if(field>=100) { - *versionString++=(char)('0'+field/100); - field%=100; - } - if(field>=10) { - *versionString++=(char)('0'+field/10); - field%=10; - } - *versionString++=(char)('0'+field); - - /* write the following parts */ - for(part=1; part=100) { - *versionString++=(char)('0'+field/100); - field%=100; - } - if(field>=10) { - *versionString++=(char)('0'+field/10); - field%=10; - } - *versionString++=(char)('0'+field); - } - - /* NUL-terminate */ - *versionString=0; -} - -U_CAPI void U_EXPORT2 -u_getVersion(UVersionInfo versionArray) { - (void)copyright; // Suppress unused variable warning from clang. - u_versionFromString(versionArray, U_ICU_VERSION); -} - -/** - * icucfg.h dependent code - */ - -#if U_ENABLE_DYLOAD && HAVE_DLOPEN && !U_PLATFORM_USES_ONLY_WIN32_API - -#if HAVE_DLFCN_H -#ifdef __MVS__ -#ifndef __SUSV3 -#define __SUSV3 1 -#endif -#endif -#include -#endif /* HAVE_DLFCN_H */ - -U_CAPI void * U_EXPORT2 -uprv_dl_open(const char *libName, UErrorCode *status) { - void *ret = NULL; - if(U_FAILURE(*status)) return ret; - ret = dlopen(libName, RTLD_NOW|RTLD_GLOBAL); - if(ret==NULL) { -#ifdef U_TRACE_DYLOAD - printf("dlerror on dlopen(%s): %s\n", libName, dlerror()); -#endif - *status = U_MISSING_RESOURCE_ERROR; - } - return ret; -} - -U_CAPI void U_EXPORT2 -uprv_dl_close(void *lib, UErrorCode *status) { - if(U_FAILURE(*status)) return; - dlclose(lib); -} - -U_CAPI UVoidFunction* U_EXPORT2 -uprv_dlsym_func(void *lib, const char* sym, UErrorCode *status) { - union { - UVoidFunction *fp; - void *vp; - } uret; - uret.fp = NULL; - if(U_FAILURE(*status)) return uret.fp; - uret.vp = dlsym(lib, sym); - if(uret.vp == NULL) { -#ifdef U_TRACE_DYLOAD - printf("dlerror on dlsym(%p,%s): %s\n", lib,sym, dlerror()); -#endif - *status = U_MISSING_RESOURCE_ERROR; - } - return uret.fp; -} - -#elif U_ENABLE_DYLOAD && U_PLATFORM_USES_ONLY_WIN32_API && !U_PLATFORM_HAS_WINUWP_API - -/* Windows API implementation. */ -// Note: UWP does not expose/allow these APIs, so the UWP version gets the null implementation. */ - -U_CAPI void * U_EXPORT2 -uprv_dl_open(const char *libName, UErrorCode *status) { - HMODULE lib = NULL; - - if(U_FAILURE(*status)) return NULL; - - lib = LoadLibraryA(libName); - - if(lib==NULL) { - *status = U_MISSING_RESOURCE_ERROR; - } - - return (void*)lib; -} - -U_CAPI void U_EXPORT2 -uprv_dl_close(void *lib, UErrorCode *status) { - HMODULE handle = (HMODULE)lib; - if(U_FAILURE(*status)) return; - - FreeLibrary(handle); - - return; -} - -U_CAPI UVoidFunction* U_EXPORT2 -uprv_dlsym_func(void *lib, const char* sym, UErrorCode *status) { - HMODULE handle = (HMODULE)lib; - UVoidFunction* addr = NULL; - - if(U_FAILURE(*status) || lib==NULL) return NULL; - - addr = (UVoidFunction*)GetProcAddress(handle, sym); - - if(addr==NULL) { - DWORD lastError = GetLastError(); - if(lastError == ERROR_PROC_NOT_FOUND) { - *status = U_MISSING_RESOURCE_ERROR; - } else { - *status = U_UNSUPPORTED_ERROR; /* other unknown error. */ - } - } - - return addr; -} - -#else - -/* No dynamic loading, null (nonexistent) implementation. */ - -U_CAPI void * U_EXPORT2 -uprv_dl_open(const char *libName, UErrorCode *status) { - (void)libName; - if(U_FAILURE(*status)) return NULL; - *status = U_UNSUPPORTED_ERROR; - return NULL; -} - -U_CAPI void U_EXPORT2 -uprv_dl_close(void *lib, UErrorCode *status) { - (void)lib; - if(U_FAILURE(*status)) return; - *status = U_UNSUPPORTED_ERROR; - return; -} - -U_CAPI UVoidFunction* U_EXPORT2 -uprv_dlsym_func(void *lib, const char* sym, UErrorCode *status) { - (void)lib; - (void)sym; - if(U_SUCCESS(*status)) { - *status = U_UNSUPPORTED_ERROR; - } - return (UVoidFunction*)NULL; -} - -#endif - -/* - * Hey, Emacs, please set the following: - * - * Local Variables: - * indent-tabs-mode: nil - * End: - * - */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1997-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* FILE NAME : putil.c (previously putil.cpp and ptypes.cpp) +* +* Date Name Description +* 04/14/97 aliu Creation. +* 04/24/97 aliu Added getDefaultDataDirectory() and +* getDefaultLocaleID(). +* 04/28/97 aliu Rewritten to assume Unix and apply general methods +* for assumed case. Non-UNIX platforms must be +* special-cased. Rewrote numeric methods dealing +* with NaN and Infinity to be platform independent +* over all IEEE 754 platforms. +* 05/13/97 aliu Restored sign of timezone +* (semantics are hours West of GMT) +* 06/16/98 erm Added IEEE_754 stuff, cleaned up isInfinite, isNan, +* nextDouble.. +* 07/22/98 stephen Added remainder, max, min, trunc +* 08/13/98 stephen Added isNegativeInfinity, isPositiveInfinity +* 08/24/98 stephen Added longBitsFromDouble +* 09/08/98 stephen Minor changes for Mac Port +* 03/02/99 stephen Removed openFile(). Added AS400 support. +* Fixed EBCDIC tables +* 04/15/99 stephen Converted to C. +* 06/28/99 stephen Removed mutex locking in u_isBigEndian(). +* 08/04/99 jeffrey R. Added OS/2 changes +* 11/15/99 helena Integrated S/390 IEEE support. +* 04/26/01 Barry N. OS/400 support for uprv_getDefaultLocaleID +* 08/15/01 Steven H. OS/400 support for uprv_getDefaultCodepage +* 01/03/08 Steven L. Fake Time Support +****************************************************************************** +*/ + +// Defines _XOPEN_SOURCE for access to POSIX functions. +// Must be before any other #includes. +#include "uposixdefs.h" + +// First, the platform type. Need this for U_PLATFORM. +#include "unicode/platform.h" + +#if U_PLATFORM == U_PF_MINGW && defined __STRICT_ANSI__ +/* tzset isn't defined in strict ANSI on MinGW. */ +#undef __STRICT_ANSI__ +#endif + +/* + * Cygwin with GCC requires inclusion of time.h after the above disabling strict asci mode statement. + */ +#include + +#if !U_PLATFORM_USES_ONLY_WIN32_API +#include +#endif + +/* include the rest of the ICU headers */ +#include "unicode/putil.h" +#include "unicode/ustring.h" +#include "putilimp.h" +#include "uassert.h" +#include "umutex.h" +#include "cmemory.h" +#include "cstring.h" +#include "locmap.h" +#include "ucln_cmn.h" +#include "charstr.h" + +/* Include standard headers. */ +#include +#include +#include +#include +#include +#include + +#ifndef U_COMMON_IMPLEMENTATION +#error U_COMMON_IMPLEMENTATION not set - must be set for all ICU source files in common/ - see https://unicode-org.github.io/icu/userguide/howtouseicu +#endif + + +/* include system headers */ +#if U_PLATFORM_USES_ONLY_WIN32_API + /* + * TODO: U_PLATFORM_USES_ONLY_WIN32_API includes MinGW. + * Should Cygwin be included as well (U_PLATFORM_HAS_WIN32_API) + * to use native APIs as much as possible? + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +# define VC_EXTRALEAN +# define NOUSER +# define NOSERVICE +# define NOIME +# define NOMCX +# include +# include "unicode/uloc.h" +# include "wintz.h" +#elif U_PLATFORM == U_PF_OS400 +# include +# include /* error code structure */ +# include +# include /* EPT_CALL macro - this include must be after all other "QSYSINCs" */ +# include /* For uprv_maximumPtr */ +#elif U_PLATFORM == U_PF_OS390 +# include "unicode/ucnv.h" /* Needed for UCNV_SWAP_LFNL_OPTION_STRING */ +#elif U_PLATFORM_IS_DARWIN_BASED || U_PLATFORM_IS_LINUX_BASED || U_PLATFORM == U_PF_BSD || U_PLATFORM == U_PF_SOLARIS +# include +# include +# if U_PLATFORM == U_PF_SOLARIS +# ifndef _XPG4_2 +# define _XPG4_2 +# endif +# elif U_PLATFORM == U_PF_ANDROID +# include +# include +# endif +#elif U_PLATFORM == U_PF_QNX +# include +#endif + + +/* + * Only include langinfo.h if we have a way to get the codeset. If we later + * depend on more feature, we can test on U_HAVE_NL_LANGINFO. + * + */ + +#if U_HAVE_NL_LANGINFO_CODESET +#include +#endif + +/** + * Simple things (presence of functions, etc) should just go in configure.in and be added to + * icucfg.h via autoheader. + */ +#if U_PLATFORM_IMPLEMENTS_POSIX +# if U_PLATFORM == U_PF_OS400 +# define HAVE_DLFCN_H 0 +# define HAVE_DLOPEN 0 +# else +# ifndef HAVE_DLFCN_H +# define HAVE_DLFCN_H 1 +# endif +# ifndef HAVE_DLOPEN +# define HAVE_DLOPEN 1 +# endif +# endif +# ifndef HAVE_GETTIMEOFDAY +# define HAVE_GETTIMEOFDAY 1 +# endif +#else +# define HAVE_DLFCN_H 0 +# define HAVE_DLOPEN 0 +# define HAVE_GETTIMEOFDAY 0 +#endif + +U_NAMESPACE_USE + +/* Define the extension for data files, again... */ +#define DATA_TYPE "dat" + +/* Leave this copyright notice here! */ +static const char copyright[] = U_COPYRIGHT_STRING; + +/* floating point implementations ------------------------------------------- */ + +/* We return QNAN rather than SNAN*/ +#define SIGN 0x80000000U + +/* Make it easy to define certain types of constants */ +typedef union { + int64_t i64; /* This must be defined first in order to allow the initialization to work. This is a C89 feature. */ + double d64; +} BitPatternConversion; +static const BitPatternConversion gNan = { (int64_t) INT64_C(0x7FF8000000000000) }; +static const BitPatternConversion gInf = { (int64_t) INT64_C(0x7FF0000000000000) }; + +/*--------------------------------------------------------------------------- + Platform utilities + Our general strategy is to assume we're on a POSIX platform. Platforms which + are non-POSIX must declare themselves so. The default POSIX implementation + will sometimes work for non-POSIX platforms as well (e.g., the NaN-related + functions). + ---------------------------------------------------------------------------*/ + +#if U_PLATFORM_USES_ONLY_WIN32_API || U_PLATFORM == U_PF_OS400 +# undef U_POSIX_LOCALE +#else +# define U_POSIX_LOCALE 1 +#endif + +/* + WARNING! u_topNBytesOfDouble and u_bottomNBytesOfDouble + can't be properly optimized by the gcc compiler sometimes (i.e. gcc 3.2). +*/ +#if !IEEE_754 +static char* +u_topNBytesOfDouble(double* d, int n) +{ +#if U_IS_BIG_ENDIAN + return (char*)d; +#else + return (char*)(d + 1) - n; +#endif +} + +static char* +u_bottomNBytesOfDouble(double* d, int n) +{ +#if U_IS_BIG_ENDIAN + return (char*)(d + 1) - n; +#else + return (char*)d; +#endif +} +#endif /* !IEEE_754 */ + +#if IEEE_754 +static UBool +u_signBit(double d) { + uint8_t hiByte; +#if U_IS_BIG_ENDIAN + hiByte = *(uint8_t *)&d; +#else + hiByte = *(((uint8_t *)&d) + sizeof(double) - 1); +#endif + return (hiByte & 0x80) != 0; +} +#endif + + + +#if defined (U_DEBUG_FAKETIME) +/* Override the clock to test things without having to move the system clock. + * Assumes POSIX gettimeofday() will function + */ +UDate fakeClock_t0 = 0; /** Time to start the clock from **/ +UDate fakeClock_dt = 0; /** Offset (fake time - real time) **/ +UBool fakeClock_set = false; /** True if fake clock has spun up **/ + +static UDate getUTCtime_real() { + struct timeval posixTime; + gettimeofday(&posixTime, nullptr); + return (UDate)(((int64_t)posixTime.tv_sec * U_MILLIS_PER_SECOND) + (posixTime.tv_usec/1000)); +} + +static UDate getUTCtime_fake() { + static UMutex fakeClockMutex; + umtx_lock(&fakeClockMutex); + if(!fakeClock_set) { + UDate real = getUTCtime_real(); + const char *fake_start = getenv("U_FAKETIME_START"); + if((fake_start!=nullptr) && (fake_start[0]!=0)) { + sscanf(fake_start,"%lf",&fakeClock_t0); + fakeClock_dt = fakeClock_t0 - real; + fprintf(stderr,"U_DEBUG_FAKETIME was set at compile time, so the ICU clock will start at a preset value\n" + "env variable U_FAKETIME_START=%.0f (%s) for an offset of %.0f ms from the current time %.0f\n", + fakeClock_t0, fake_start, fakeClock_dt, real); + } else { + fakeClock_dt = 0; + fprintf(stderr,"U_DEBUG_FAKETIME was set at compile time, but U_FAKETIME_START was not set.\n" + "Set U_FAKETIME_START to the number of milliseconds since 1/1/1970 to set the ICU clock.\n"); + } + fakeClock_set = true; + } + umtx_unlock(&fakeClockMutex); + + return getUTCtime_real() + fakeClock_dt; +} +#endif + +#if U_PLATFORM_USES_ONLY_WIN32_API +typedef union { + int64_t int64; + FILETIME fileTime; +} FileTimeConversion; /* This is like a ULARGE_INTEGER */ + +/* Number of 100 nanoseconds from 1/1/1601 to 1/1/1970 */ +#define EPOCH_BIAS INT64_C(116444736000000000) +#define HECTONANOSECOND_PER_MILLISECOND 10000 + +#endif + +/*--------------------------------------------------------------------------- + Universal Implementations + These are designed to work on all platforms. Try these, and if they + don't work on your platform, then special case your platform with new + implementations. +---------------------------------------------------------------------------*/ + +U_CAPI UDate U_EXPORT2 +uprv_getUTCtime() +{ +#if defined(U_DEBUG_FAKETIME) + return getUTCtime_fake(); /* Hook for overriding the clock */ +#else + return uprv_getRawUTCtime(); +#endif +} + +/* Return UTC (GMT) time measured in milliseconds since 0:00 on 1/1/70.*/ +U_CAPI UDate U_EXPORT2 +uprv_getRawUTCtime() +{ +#if U_PLATFORM_USES_ONLY_WIN32_API + + FileTimeConversion winTime; + GetSystemTimeAsFileTime(&winTime.fileTime); + return (UDate)((winTime.int64 - EPOCH_BIAS) / HECTONANOSECOND_PER_MILLISECOND); +#else + +#if HAVE_GETTIMEOFDAY + struct timeval posixTime; + gettimeofday(&posixTime, nullptr); + return (UDate)(((int64_t)posixTime.tv_sec * U_MILLIS_PER_SECOND) + (posixTime.tv_usec/1000)); +#else + time_t epochtime; + time(&epochtime); + return (UDate)epochtime * U_MILLIS_PER_SECOND; +#endif + +#endif +} + +/*----------------------------------------------------------------------------- + IEEE 754 + These methods detect and return NaN and infinity values for doubles + conforming to IEEE 754. Platforms which support this standard include X86, + Mac 680x0, Mac PowerPC, AIX RS/6000, and most others. + If this doesn't work on your platform, you have non-IEEE floating-point, and + will need to code your own versions. A naive implementation is to return 0.0 + for getNaN and getInfinity, and false for isNaN and isInfinite. + ---------------------------------------------------------------------------*/ + +U_CAPI UBool U_EXPORT2 +uprv_isNaN(double number) +{ +#if IEEE_754 + BitPatternConversion convertedNumber; + convertedNumber.d64 = number; + /* Infinity is 0x7FF0000000000000U. Anything greater than that is a NaN */ + return (UBool)((convertedNumber.i64 & U_INT64_MAX) > gInf.i64); + +#elif U_PLATFORM == U_PF_OS390 + uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number, + sizeof(uint32_t)); + uint32_t lowBits = *(uint32_t*)u_bottomNBytesOfDouble(&number, + sizeof(uint32_t)); + + return ((highBits & 0x7F080000L) == 0x7F080000L) && + (lowBits == 0x00000000L); + +#else + /* If your platform doesn't support IEEE 754 but *does* have an NaN value,*/ + /* you'll need to replace this default implementation with what's correct*/ + /* for your platform.*/ + return number != number; +#endif +} + +U_CAPI UBool U_EXPORT2 +uprv_isInfinite(double number) +{ +#if IEEE_754 + BitPatternConversion convertedNumber; + convertedNumber.d64 = number; + /* Infinity is exactly 0x7FF0000000000000U. */ + return (UBool)((convertedNumber.i64 & U_INT64_MAX) == gInf.i64); +#elif U_PLATFORM == U_PF_OS390 + uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number, + sizeof(uint32_t)); + uint32_t lowBits = *(uint32_t*)u_bottomNBytesOfDouble(&number, + sizeof(uint32_t)); + + return ((highBits & ~SIGN) == 0x70FF0000L) && (lowBits == 0x00000000L); + +#else + /* If your platform doesn't support IEEE 754 but *does* have an infinity*/ + /* value, you'll need to replace this default implementation with what's*/ + /* correct for your platform.*/ + return number == (2.0 * number); +#endif +} + +U_CAPI UBool U_EXPORT2 +uprv_isPositiveInfinity(double number) +{ +#if IEEE_754 || U_PLATFORM == U_PF_OS390 + return (UBool)(number > 0 && uprv_isInfinite(number)); +#else + return uprv_isInfinite(number); +#endif +} + +U_CAPI UBool U_EXPORT2 +uprv_isNegativeInfinity(double number) +{ +#if IEEE_754 || U_PLATFORM == U_PF_OS390 + return (UBool)(number < 0 && uprv_isInfinite(number)); + +#else + uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number, + sizeof(uint32_t)); + return((highBits & SIGN) && uprv_isInfinite(number)); + +#endif +} + +U_CAPI double U_EXPORT2 +uprv_getNaN() +{ +#if IEEE_754 || U_PLATFORM == U_PF_OS390 + return gNan.d64; +#else + /* If your platform doesn't support IEEE 754 but *does* have an NaN value,*/ + /* you'll need to replace this default implementation with what's correct*/ + /* for your platform.*/ + return 0.0; +#endif +} + +U_CAPI double U_EXPORT2 +uprv_getInfinity() +{ +#if IEEE_754 || U_PLATFORM == U_PF_OS390 + return gInf.d64; +#else + /* If your platform doesn't support IEEE 754 but *does* have an infinity*/ + /* value, you'll need to replace this default implementation with what's*/ + /* correct for your platform.*/ + return 0.0; +#endif +} + +U_CAPI double U_EXPORT2 +uprv_floor(double x) +{ + return floor(x); +} + +U_CAPI double U_EXPORT2 +uprv_ceil(double x) +{ + return ceil(x); +} + +U_CAPI double U_EXPORT2 +uprv_round(double x) +{ + return uprv_floor(x + 0.5); +} + +U_CAPI double U_EXPORT2 +uprv_fabs(double x) +{ + return fabs(x); +} + +U_CAPI double U_EXPORT2 +uprv_modf(double x, double* y) +{ + return modf(x, y); +} + +U_CAPI double U_EXPORT2 +uprv_fmod(double x, double y) +{ + return fmod(x, y); +} + +U_CAPI double U_EXPORT2 +uprv_pow(double x, double y) +{ + /* This is declared as "double pow(double x, double y)" */ + return pow(x, y); +} + +U_CAPI double U_EXPORT2 +uprv_pow10(int32_t x) +{ + return pow(10.0, (double)x); +} + +U_CAPI double U_EXPORT2 +uprv_fmax(double x, double y) +{ +#if IEEE_754 + /* first handle NaN*/ + if(uprv_isNaN(x) || uprv_isNaN(y)) + return uprv_getNaN(); + + /* check for -0 and 0*/ + if(x == 0.0 && y == 0.0 && u_signBit(x)) + return y; + +#endif + + /* this should work for all flt point w/o NaN and Inf special cases */ + return (x > y ? x : y); +} + +U_CAPI double U_EXPORT2 +uprv_fmin(double x, double y) +{ +#if IEEE_754 + /* first handle NaN*/ + if(uprv_isNaN(x) || uprv_isNaN(y)) + return uprv_getNaN(); + + /* check for -0 and 0*/ + if(x == 0.0 && y == 0.0 && u_signBit(y)) + return y; + +#endif + + /* this should work for all flt point w/o NaN and Inf special cases */ + 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 + * This is different than calling floor() or ceil(): + * floor(3.3) = 3, floor(-3.3) = -4 + * ceil(3.3) = 4, ceil(-3.3) = -3 + */ +U_CAPI double U_EXPORT2 +uprv_trunc(double d) +{ +#if IEEE_754 + /* handle error cases*/ + if(uprv_isNaN(d)) + return uprv_getNaN(); + if(uprv_isInfinite(d)) + return uprv_getInfinity(); + + if(u_signBit(d)) /* Signbit() picks up -0.0; d<0 does not. */ + return ceil(d); + else + return floor(d); + +#else + return d >= 0 ? floor(d) : ceil(d); + +#endif +} + +/** + * Return the largest positive number that can be represented by an integer + * type of arbitrary bit length. + */ +U_CAPI double U_EXPORT2 +uprv_maxMantissa() +{ + return pow(2.0, DBL_MANT_DIG + 1.0) - 1.0; +} + +U_CAPI double U_EXPORT2 +uprv_log(double d) +{ + return log(d); +} + +U_CAPI void * U_EXPORT2 +uprv_maximumPtr(void * base) +{ +#if U_PLATFORM == U_PF_OS400 + /* + * With the provided function we should never be out of range of a given segment + * (a traditional/typical segment that is). Our segments have 5 bytes for the + * id and 3 bytes for the offset. The key is that the casting takes care of + * only retrieving the offset portion minus x1000. Hence, the smallest offset + * seen in a program is x001000 and when casted to an int would be 0. + * That's why we can only add 0xffefff. Otherwise, we would exceed the segment. + * + * Currently, 16MB is the current addressing limitation on i5/OS if the activation is + * non-TERASPACE. If it is TERASPACE it is 2GB - 4k(header information). + * This function determines the activation based on the pointer that is passed in and + * calculates the appropriate maximum available size for + * each pointer type (TERASPACE and non-TERASPACE) + * + * Unlike other operating systems, the pointer model isn't determined at + * compile time on i5/OS. + */ + if ((base != nullptr) && (_TESTPTR(base, _C_TERASPACE_CHECK))) { + /* if it is a TERASPACE pointer the max is 2GB - 4k */ + return ((void *)(((char *)base)-((uint32_t)(base))+((uint32_t)0x7fffefff))); + } + /* otherwise 16MB since nullptr ptr is not checkable or the ptr is not TERASPACE */ + return ((void *)(((char *)base)-((uint32_t)(base))+((uint32_t)0xffefff))); + +#else + return U_MAX_PTR(base); +#endif +} + +/*--------------------------------------------------------------------------- + Platform-specific Implementations + Try these, and if they don't work on your platform, then special case your + platform with new implementations. + ---------------------------------------------------------------------------*/ + +/* Generic time zone layer -------------------------------------------------- */ + +/* Time zone utilities */ +U_CAPI void U_EXPORT2 +uprv_tzset() +{ +#if defined(U_TZSET) + U_TZSET(); +#else + /* no initialization*/ +#endif +} + +U_CAPI int32_t U_EXPORT2 +uprv_timezone() +{ +#ifdef U_TIMEZONE + return U_TIMEZONE; +#else + time_t t, t1, t2; + struct tm tmrec; + int32_t tdiff = 0; + + time(&t); + uprv_memcpy( &tmrec, localtime(&t), sizeof(tmrec) ); +#if U_PLATFORM != U_PF_IPHONE + UBool dst_checked = (tmrec.tm_isdst != 0); /* daylight savings time is checked*/ +#endif + t1 = mktime(&tmrec); /* local time in seconds*/ + uprv_memcpy( &tmrec, gmtime(&t), sizeof(tmrec) ); + t2 = mktime(&tmrec); /* GMT (or UTC) in seconds*/ + tdiff = t2 - t1; + +#if U_PLATFORM != U_PF_IPHONE + /* imitate NT behaviour, which returns same timezone offset to GMT for + winter and summer. + This does not work on all platforms. For instance, on glibc on Linux + and on Mac OS 10.5, tdiff calculated above remains the same + regardless of whether DST is in effect or not. iOS is another + platform where this does not work. Linux + glibc and Mac OS 10.5 + have U_TIMEZONE defined so that this code is not reached. + */ + if (dst_checked) + tdiff += 3600; +#endif + return tdiff; +#endif +} + +/* Note that U_TZNAME does *not* have to be tzname, but if it is, + some platforms need to have it declared here. */ + +#if defined(U_TZNAME) && (U_PLATFORM == U_PF_IRIX || U_PLATFORM_IS_DARWIN_BASED) +/* RS6000 and others reject char **tzname. */ +extern U_IMPORT char *U_TZNAME[]; +#endif + +#if !UCONFIG_NO_FILE_IO && ((U_PLATFORM_IS_DARWIN_BASED && (U_PLATFORM != U_PF_IPHONE || defined(U_TIMEZONE))) || U_PLATFORM_IS_LINUX_BASED || U_PLATFORM == U_PF_BSD || U_PLATFORM == U_PF_SOLARIS) +/* These platforms are likely to use Olson timezone IDs. */ +/* common targets of the symbolic link at TZDEFAULT are: + * "/usr/share/zoneinfo/" default, older Linux distros, macOS to 10.12 + * "../usr/share/zoneinfo/" newer Linux distros: Red Hat Enterprise Linux 7, Ubuntu 16, SuSe Linux 12 + * "/usr/share/lib/zoneinfo/" Solaris + * "../usr/share/lib/zoneinfo/" Solaris + * "/var/db/timezone/zoneinfo/" macOS 10.13 + * To avoid checking lots of paths, just check that the target path + * before the ends with "/zoneinfo/", and the is valid. + */ + +#define CHECK_LOCALTIME_LINK 1 +#if U_PLATFORM_IS_DARWIN_BASED +#include +#define TZZONEINFO (TZDIR "/") +#elif U_PLATFORM == U_PF_SOLARIS +#define TZDEFAULT "/etc/localtime" +#define TZZONEINFO "/usr/share/lib/zoneinfo/" +#define TZ_ENV_CHECK "localtime" +#else +#define TZDEFAULT "/etc/localtime" +#define TZZONEINFO "/usr/share/zoneinfo/" +#endif +#define TZZONEINFOTAIL "/zoneinfo/" +#if U_HAVE_DIRENT_H +#define TZFILE_SKIP "posixrules" /* tz file to skip when searching. */ +/* Some Linux distributions have 'localtime' in /usr/share/zoneinfo + symlinked to /etc/localtime, which makes searchForTZFile return + 'localtime' when it's the first match. */ +#define TZFILE_SKIP2 "localtime" +#define SEARCH_TZFILE +#include /* Needed to search through system timezone files */ +#endif +static char gTimeZoneBuffer[PATH_MAX]; +static const char *gTimeZoneBufferPtr = nullptr; +#endif + +#if !U_PLATFORM_USES_ONLY_WIN32_API +#define isNonDigit(ch) (ch < '0' || '9' < ch) +#define isDigit(ch) ('0' <= ch && ch <= '9') +static UBool isValidOlsonID(const char *id) { + int32_t idx = 0; + int32_t idxMax = 0; + + /* Determine if this is something like Iceland (Olson ID) + or AST4ADT (non-Olson ID) */ + while (id[idx] && isNonDigit(id[idx]) && id[idx] != ',') { + idx++; + } + + /* Allow at maximum 2 numbers at the end of the id to support zone id's + like GMT+11. */ + idxMax = idx + 2; + while (id[idx] && isDigit(id[idx]) && idx < idxMax) { + idx++; + } + + /* If we went through the whole string, then it might be okay. + The timezone is sometimes set to "CST-7CDT", "CST6CDT5,J129,J131/19:30", + "GRNLNDST3GRNLNDDT" or similar, so we cannot use it. + The rest of the time it could be an Olson ID. George */ + return (UBool)(id[idx] == 0 + || uprv_strcmp(id, "PST8PDT") == 0 + || uprv_strcmp(id, "MST7MDT") == 0 + || uprv_strcmp(id, "CST6CDT") == 0 + || uprv_strcmp(id, "EST5EDT") == 0); +} + +/* On some Unix-like OS, 'posix' subdirectory in + /usr/share/zoneinfo replicates the top-level contents. 'right' + subdirectory has the same set of files, but individual files + are different from those in the top-level directory or 'posix' + because 'right' has files for TAI (Int'l Atomic Time) while 'posix' + has files for UTC. + When the first match for /etc/localtime is in either of them + (usually in posix because 'right' has different file contents), + or TZ environment variable points to one of them, createTimeZone + fails because, say, 'posix/America/New_York' is not an Olson + timezone id ('America/New_York' is). So, we have to skip + 'posix/' and 'right/' at the beginning. */ +static void skipZoneIDPrefix(const char** id) { + if (uprv_strncmp(*id, "posix/", 6) == 0 + || uprv_strncmp(*id, "right/", 6) == 0) + { + *id += 6; + } +} +#endif + +#if defined(U_TZNAME) && !U_PLATFORM_USES_ONLY_WIN32_API + +#define CONVERT_HOURS_TO_SECONDS(offset) (int32_t)(offset*3600) +typedef struct OffsetZoneMapping { + int32_t offsetSeconds; + int32_t daylightType; /* 0=U_DAYLIGHT_NONE, 1=daylight in June-U_DAYLIGHT_JUNE, 2=daylight in December=U_DAYLIGHT_DECEMBER*/ + const char *stdID; + const char *dstID; + const char *olsonID; +} OffsetZoneMapping; + +enum { U_DAYLIGHT_NONE=0,U_DAYLIGHT_JUNE=1,U_DAYLIGHT_DECEMBER=2 }; + +/* +This list tries to disambiguate a set of abbreviated timezone IDs and offsets +and maps it to an Olson ID. +Before adding anything to this list, take a look at +icu/source/tools/tzcode/tz.alias +Sometimes no daylight savings (0) is important to define due to aliases. +This list can be tested with icu/source/test/compat/tzone.pl +More values could be added to daylightType to increase precision. +*/ +static const struct OffsetZoneMapping OFFSET_ZONE_MAPPINGS[] = { + {-45900, 2, "CHAST", "CHADT", "Pacific/Chatham"}, + {-43200, 1, "PETT", "PETST", "Asia/Kamchatka"}, + {-43200, 2, "NZST", "NZDT", "Pacific/Auckland"}, + {-43200, 1, "ANAT", "ANAST", "Asia/Anadyr"}, + {-39600, 1, "MAGT", "MAGST", "Asia/Magadan"}, + {-37800, 2, "LHST", "LHST", "Australia/Lord_Howe"}, + {-36000, 2, "EST", "EST", "Australia/Sydney"}, + {-36000, 1, "SAKT", "SAKST", "Asia/Sakhalin"}, + {-36000, 1, "VLAT", "VLAST", "Asia/Vladivostok"}, + {-34200, 2, "CST", "CST", "Australia/South"}, + {-32400, 1, "YAKT", "YAKST", "Asia/Yakutsk"}, + {-32400, 1, "CHOT", "CHOST", "Asia/Choibalsan"}, + {-31500, 2, "CWST", "CWST", "Australia/Eucla"}, + {-28800, 1, "IRKT", "IRKST", "Asia/Irkutsk"}, + {-28800, 1, "ULAT", "ULAST", "Asia/Ulaanbaatar"}, + {-28800, 2, "WST", "WST", "Australia/West"}, + {-25200, 1, "HOVT", "HOVST", "Asia/Hovd"}, + {-25200, 1, "KRAT", "KRAST", "Asia/Krasnoyarsk"}, + {-21600, 1, "NOVT", "NOVST", "Asia/Novosibirsk"}, + {-21600, 1, "OMST", "OMSST", "Asia/Omsk"}, + {-18000, 1, "YEKT", "YEKST", "Asia/Yekaterinburg"}, + {-14400, 1, "SAMT", "SAMST", "Europe/Samara"}, + {-14400, 1, "AMT", "AMST", "Asia/Yerevan"}, + {-14400, 1, "AZT", "AZST", "Asia/Baku"}, + {-10800, 1, "AST", "ADT", "Asia/Baghdad"}, + {-10800, 1, "MSK", "MSD", "Europe/Moscow"}, + {-10800, 1, "VOLT", "VOLST", "Europe/Volgograd"}, + {-7200, 0, "EET", "CEST", "Africa/Tripoli"}, + {-7200, 1, "EET", "EEST", "Europe/Athens"}, /* Conflicts with Africa/Cairo */ + {-7200, 1, "IST", "IDT", "Asia/Jerusalem"}, + {-3600, 0, "CET", "WEST", "Africa/Algiers"}, + {-3600, 2, "WAT", "WAST", "Africa/Windhoek"}, + {0, 1, "GMT", "IST", "Europe/Dublin"}, + {0, 1, "GMT", "BST", "Europe/London"}, + {0, 0, "WET", "WEST", "Africa/Casablanca"}, + {0, 0, "WET", "WET", "Africa/El_Aaiun"}, + {3600, 1, "AZOT", "AZOST", "Atlantic/Azores"}, + {3600, 1, "EGT", "EGST", "America/Scoresbysund"}, + {10800, 1, "PMST", "PMDT", "America/Miquelon"}, + {10800, 2, "UYT", "UYST", "America/Montevideo"}, + {10800, 1, "WGT", "WGST", "America/Godthab"}, + {10800, 2, "BRT", "BRST", "Brazil/East"}, + {12600, 1, "NST", "NDT", "America/St_Johns"}, + {14400, 1, "AST", "ADT", "Canada/Atlantic"}, + {14400, 2, "AMT", "AMST", "America/Cuiaba"}, + {14400, 2, "CLT", "CLST", "Chile/Continental"}, + {14400, 2, "FKT", "FKST", "Atlantic/Stanley"}, + {14400, 2, "PYT", "PYST", "America/Asuncion"}, + {18000, 1, "CST", "CDT", "America/Havana"}, + {18000, 1, "EST", "EDT", "US/Eastern"}, /* Conflicts with America/Grand_Turk */ + {21600, 2, "EAST", "EASST", "Chile/EasterIsland"}, + {21600, 0, "CST", "MDT", "Canada/Saskatchewan"}, + {21600, 0, "CST", "CDT", "America/Guatemala"}, + {21600, 1, "CST", "CDT", "US/Central"}, /* Conflicts with Mexico/General */ + {25200, 1, "MST", "MDT", "US/Mountain"}, /* Conflicts with Mexico/BajaSur */ + {28800, 0, "PST", "PST", "Pacific/Pitcairn"}, + {28800, 1, "PST", "PDT", "US/Pacific"}, /* Conflicts with Mexico/BajaNorte */ + {32400, 1, "AKST", "AKDT", "US/Alaska"}, + {36000, 1, "HAST", "HADT", "US/Aleutian"} +}; + +/*#define DEBUG_TZNAME*/ + +static const char* remapShortTimeZone(const char *stdID, const char *dstID, int32_t daylightType, int32_t offset) +{ + int32_t idx; +#ifdef DEBUG_TZNAME + fprintf(stderr, "TZ=%s std=%s dst=%s daylight=%d offset=%d\n", getenv("TZ"), stdID, dstID, daylightType, offset); +#endif + for (idx = 0; idx < UPRV_LENGTHOF(OFFSET_ZONE_MAPPINGS); idx++) + { + if (offset == OFFSET_ZONE_MAPPINGS[idx].offsetSeconds + && daylightType == OFFSET_ZONE_MAPPINGS[idx].daylightType + && strcmp(OFFSET_ZONE_MAPPINGS[idx].stdID, stdID) == 0 + && strcmp(OFFSET_ZONE_MAPPINGS[idx].dstID, dstID) == 0) + { + return OFFSET_ZONE_MAPPINGS[idx].olsonID; + } + } + return nullptr; +} +#endif + +#ifdef SEARCH_TZFILE +#define MAX_READ_SIZE 512 + +typedef struct DefaultTZInfo { + char* defaultTZBuffer; + int64_t defaultTZFileSize; + FILE* defaultTZFilePtr; + UBool defaultTZstatus; + int32_t defaultTZPosition; +} DefaultTZInfo; + +/* + * This method compares the two files given to see if they are a match. + * It is currently use to compare two TZ files. + */ +static UBool compareBinaryFiles(const char* defaultTZFileName, const char* TZFileName, DefaultTZInfo* tzInfo) { + FILE* file; + int64_t sizeFile; + int64_t sizeFileLeft; + int32_t sizeFileRead; + int32_t sizeFileToRead; + char bufferFile[MAX_READ_SIZE]; + UBool result = true; + + if (tzInfo->defaultTZFilePtr == nullptr) { + tzInfo->defaultTZFilePtr = fopen(defaultTZFileName, "r"); + } + file = fopen(TZFileName, "r"); + + tzInfo->defaultTZPosition = 0; /* reset position to begin search */ + + if (file != nullptr && tzInfo->defaultTZFilePtr != nullptr) { + /* First check that the file size are equal. */ + if (tzInfo->defaultTZFileSize == 0) { + fseek(tzInfo->defaultTZFilePtr, 0, SEEK_END); + tzInfo->defaultTZFileSize = ftell(tzInfo->defaultTZFilePtr); + } + fseek(file, 0, SEEK_END); + sizeFile = ftell(file); + sizeFileLeft = sizeFile; + + if (sizeFile != tzInfo->defaultTZFileSize) { + result = false; + } else { + /* Store the data from the files in separate buffers and + * compare each byte to determine equality. + */ + if (tzInfo->defaultTZBuffer == nullptr) { + rewind(tzInfo->defaultTZFilePtr); + tzInfo->defaultTZBuffer = (char*)uprv_malloc(sizeof(char) * tzInfo->defaultTZFileSize); + sizeFileRead = fread(tzInfo->defaultTZBuffer, 1, tzInfo->defaultTZFileSize, tzInfo->defaultTZFilePtr); + } + rewind(file); + while(sizeFileLeft > 0) { + uprv_memset(bufferFile, 0, MAX_READ_SIZE); + sizeFileToRead = sizeFileLeft < MAX_READ_SIZE ? sizeFileLeft : MAX_READ_SIZE; + + sizeFileRead = fread(bufferFile, 1, sizeFileToRead, file); + if (memcmp(tzInfo->defaultTZBuffer + tzInfo->defaultTZPosition, bufferFile, sizeFileRead) != 0) { + result = false; + break; + } + sizeFileLeft -= sizeFileRead; + tzInfo->defaultTZPosition += sizeFileRead; + } + } + } else { + result = false; + } + + if (file != nullptr) { + fclose(file); + } + + return result; +} + + +/* dirent also lists two entries: "." and ".." that we can safely ignore. */ +#define SKIP1 "." +#define SKIP2 ".." +static UBool U_CALLCONV putil_cleanup(); +static CharString *gSearchTZFileResult = nullptr; + +/* + * This method recursively traverses the directory given for a matching TZ file and returns the first match. + * This function is not thread safe - it uses a global, gSearchTZFileResult, to hold its results. + */ +static char* searchForTZFile(const char* path, DefaultTZInfo* tzInfo) { + DIR* dirp = nullptr; + struct dirent* dirEntry = nullptr; + char* result = nullptr; + UErrorCode status = U_ZERO_ERROR; + + /* Save the current path */ + CharString curpath(path, -1, status); + if (U_FAILURE(status)) { + goto cleanupAndReturn; + } + + dirp = opendir(path); + if (dirp == nullptr) { + goto cleanupAndReturn; + } + + if (gSearchTZFileResult == nullptr) { + gSearchTZFileResult = new CharString; + if (gSearchTZFileResult == nullptr) { + goto cleanupAndReturn; + } + ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup); + } + + /* Check each entry in the directory. */ + while((dirEntry = readdir(dirp)) != nullptr) { + const char* dirName = dirEntry->d_name; + if (uprv_strcmp(dirName, SKIP1) != 0 && uprv_strcmp(dirName, SKIP2) != 0 + && uprv_strcmp(TZFILE_SKIP, dirName) != 0 && uprv_strcmp(TZFILE_SKIP2, dirName) != 0) { + /* Create a newpath with the new entry to test each entry in the directory. */ + CharString newpath(curpath, status); + newpath.append(dirName, -1, status); + if (U_FAILURE(status)) { + break; + } + + DIR* subDirp = nullptr; + if ((subDirp = opendir(newpath.data())) != nullptr) { + /* If this new path is a directory, make a recursive call with the newpath. */ + closedir(subDirp); + newpath.append('/', status); + if (U_FAILURE(status)) { + break; + } + result = searchForTZFile(newpath.data(), tzInfo); + /* + Have to get out here. Otherwise, we'd keep looking + and return the first match in the top-level directory + if there's a match in the top-level. If not, this function + would return nullptr and set gTimeZoneBufferPtr to nullptr in initDefault(). + It worked without this in most cases because we have a fallback of calling + localtime_r to figure out the default timezone. + */ + if (result != nullptr) + break; + } else { + if(compareBinaryFiles(TZDEFAULT, newpath.data(), tzInfo)) { + int32_t amountToSkip = sizeof(TZZONEINFO) - 1; + if (amountToSkip > newpath.length()) { + amountToSkip = newpath.length(); + } + const char* zoneid = newpath.data() + amountToSkip; + skipZoneIDPrefix(&zoneid); + gSearchTZFileResult->clear(); + gSearchTZFileResult->append(zoneid, -1, status); + if (U_FAILURE(status)) { + break; + } + result = gSearchTZFileResult->data(); + /* Get out after the first one found. */ + break; + } + } + } + } + + cleanupAndReturn: + if (dirp) { + closedir(dirp); + } + return result; +} +#endif + +#if U_PLATFORM == U_PF_ANDROID +typedef int(system_property_read_callback)(const prop_info* info, + void (*callback)(void* cookie, + const char* name, + const char* value, + uint32_t serial), + void* cookie); +typedef int(system_property_get)(const char*, char*); + +static char gAndroidTimeZone[PROP_VALUE_MAX] = { '\0' }; + +static void u_property_read(void* cookie, const char* name, const char* value, + uint32_t serial) { + uprv_strcpy((char* )cookie, value); +} +#endif + +U_CAPI void U_EXPORT2 +uprv_tzname_clear_cache() +{ +#if U_PLATFORM == U_PF_ANDROID + /* Android's timezone is stored in system property. */ + gAndroidTimeZone[0] = '\0'; + void* libc = dlopen("libc.so", RTLD_NOLOAD); + if (libc) { + /* Android API 26+ has new API to get system property and old API + * (__system_property_get) is deprecated */ + system_property_read_callback* property_read_callback = + (system_property_read_callback*)dlsym( + libc, "__system_property_read_callback"); + if (property_read_callback) { + const prop_info* info = + __system_property_find("persist.sys.timezone"); + if (info) { + property_read_callback(info, &u_property_read, gAndroidTimeZone); + } + } else { + system_property_get* property_get = + (system_property_get*)dlsym(libc, "__system_property_get"); + if (property_get) { + property_get("persist.sys.timezone", gAndroidTimeZone); + } + } + dlclose(libc); + } +#endif + +#if defined(CHECK_LOCALTIME_LINK) && !defined(DEBUG_SKIP_LOCALTIME_LINK) + gTimeZoneBufferPtr = nullptr; +#endif +} + +U_CAPI const char* U_EXPORT2 +uprv_tzname(int n) +{ + (void)n; // Avoid unreferenced parameter warning. + const char *tzid = nullptr; +#if U_PLATFORM_USES_ONLY_WIN32_API + tzid = uprv_detectWindowsTimeZone(); + + if (tzid != nullptr) { + return tzid; + } + +#ifndef U_TZNAME + // The return value is free'd in timezone.cpp on Windows because + // the other code path returns a pointer to a heap location. + // If we don't have a name already, then tzname wouldn't be any + // better, so just fall back. + return uprv_strdup(""); +#endif // !U_TZNAME + +#else + +/*#if U_PLATFORM_IS_DARWIN_BASED + int ret; + + tzid = getenv("TZFILE"); + if (tzid != nullptr) { + return tzid; + } +#endif*/ + +/* This code can be temporarily disabled to test tzname resolution later on. */ +#ifndef DEBUG_TZNAME +#if U_PLATFORM == U_PF_ANDROID + tzid = gAndroidTimeZone; +#else + tzid = getenv("TZ"); +#endif + if (tzid != nullptr && isValidOlsonID(tzid) +#if U_PLATFORM == U_PF_SOLARIS + /* Don't misinterpret TZ "localtime" on Solaris as a time zone name. */ + && uprv_strcmp(tzid, TZ_ENV_CHECK) != 0 +#endif + ) { + /* The colon forces tzset() to treat the remainder as zoneinfo path */ + if (tzid[0] == ':') { + tzid++; + } + /* This might be a good Olson ID. */ + skipZoneIDPrefix(&tzid); + return tzid; + } + /* else U_TZNAME will give a better result. */ +#endif + +#if defined(CHECK_LOCALTIME_LINK) && !defined(DEBUG_SKIP_LOCALTIME_LINK) + /* Caller must handle threading issues */ + if (gTimeZoneBufferPtr == nullptr) { + /* + This is a trick to look at the name of the link to get the Olson ID + because the tzfile contents is underspecified. + This isn't guaranteed to work because it may not be a symlink. + */ + char *ret = realpath(TZDEFAULT, gTimeZoneBuffer); + if (ret != nullptr && uprv_strcmp(TZDEFAULT, gTimeZoneBuffer) != 0) { + int32_t tzZoneInfoTailLen = uprv_strlen(TZZONEINFOTAIL); + const char *tzZoneInfoTailPtr = uprv_strstr(gTimeZoneBuffer, TZZONEINFOTAIL); + if (tzZoneInfoTailPtr != nullptr) { + tzZoneInfoTailPtr += tzZoneInfoTailLen; + skipZoneIDPrefix(&tzZoneInfoTailPtr); + if (isValidOlsonID(tzZoneInfoTailPtr)) { + return (gTimeZoneBufferPtr = tzZoneInfoTailPtr); + } + } + } else { +#if defined(SEARCH_TZFILE) + DefaultTZInfo* tzInfo = (DefaultTZInfo*)uprv_malloc(sizeof(DefaultTZInfo)); + if (tzInfo != nullptr) { + tzInfo->defaultTZBuffer = nullptr; + tzInfo->defaultTZFileSize = 0; + tzInfo->defaultTZFilePtr = nullptr; + tzInfo->defaultTZstatus = false; + tzInfo->defaultTZPosition = 0; + + gTimeZoneBufferPtr = searchForTZFile(TZZONEINFO, tzInfo); + + /* Free previously allocated memory */ + if (tzInfo->defaultTZBuffer != nullptr) { + uprv_free(tzInfo->defaultTZBuffer); + } + if (tzInfo->defaultTZFilePtr != nullptr) { + fclose(tzInfo->defaultTZFilePtr); + } + uprv_free(tzInfo); + } + + if (gTimeZoneBufferPtr != nullptr && isValidOlsonID(gTimeZoneBufferPtr)) { + return gTimeZoneBufferPtr; + } +#endif + } + } + else { + return gTimeZoneBufferPtr; + } +#endif +#endif + +#ifdef U_TZNAME +#if U_PLATFORM_USES_ONLY_WIN32_API + /* The return value is free'd in timezone.cpp on Windows because + * the other code path returns a pointer to a heap location. */ + return uprv_strdup(U_TZNAME[n]); +#else + /* + U_TZNAME is usually a non-unique abbreviation, which isn't normally usable. + So we remap the abbreviation to an olson ID. + + Since Windows exposes a little more timezone information, + we normally don't use this code on Windows because + uprv_detectWindowsTimeZone should have already given the correct answer. + */ + { + struct tm juneSol, decemberSol; + int daylightType; + static const time_t juneSolstice=1182478260; /*2007-06-21 18:11 UT*/ + static const time_t decemberSolstice=1198332540; /*2007-12-22 06:09 UT*/ + + /* This probing will tell us when daylight savings occurs. */ + localtime_r(&juneSolstice, &juneSol); + localtime_r(&decemberSolstice, &decemberSol); + if(decemberSol.tm_isdst > 0) { + daylightType = U_DAYLIGHT_DECEMBER; + } else if(juneSol.tm_isdst > 0) { + daylightType = U_DAYLIGHT_JUNE; + } else { + daylightType = U_DAYLIGHT_NONE; + } + tzid = remapShortTimeZone(U_TZNAME[0], U_TZNAME[1], daylightType, uprv_timezone()); + if (tzid != nullptr) { + return tzid; + } + } + return U_TZNAME[n]; +#endif +#else + return ""; +#endif +} + +/* Get and set the ICU data directory --------------------------------------- */ + +static icu::UInitOnce gDataDirInitOnce {}; +static char *gDataDirectory = nullptr; + +UInitOnce gTimeZoneFilesInitOnce {}; +static CharString *gTimeZoneFilesDirectory = nullptr; + +#if U_POSIX_LOCALE || U_PLATFORM_USES_ONLY_WIN32_API + static const char *gCorrectedPOSIXLocale = nullptr; /* Sometimes heap allocated */ + static bool gCorrectedPOSIXLocaleHeapAllocated = false; +#endif + +static UBool U_CALLCONV putil_cleanup() +{ + if (gDataDirectory && *gDataDirectory) { + uprv_free(gDataDirectory); + } + gDataDirectory = nullptr; + gDataDirInitOnce.reset(); + + delete gTimeZoneFilesDirectory; + gTimeZoneFilesDirectory = nullptr; + gTimeZoneFilesInitOnce.reset(); + +#ifdef SEARCH_TZFILE + delete gSearchTZFileResult; + gSearchTZFileResult = nullptr; +#endif + +#if U_POSIX_LOCALE || U_PLATFORM_USES_ONLY_WIN32_API + if (gCorrectedPOSIXLocale && gCorrectedPOSIXLocaleHeapAllocated) { + uprv_free(const_cast(gCorrectedPOSIXLocale)); + gCorrectedPOSIXLocale = nullptr; + gCorrectedPOSIXLocaleHeapAllocated = false; + } +#endif + return true; +} + +/* + * Set the data directory. + * Make a copy of the passed string, and set the global data dir to point to it. + */ +U_CAPI void U_EXPORT2 +u_setDataDirectory(const char *directory) { + char *newDataDir; + int32_t length; + + if(directory==nullptr || *directory==0) { + /* A small optimization to prevent the malloc and copy when the + shared library is used, and this is a way to make sure that nullptr + is never returned. + */ + newDataDir = (char *)""; + } + else { + length=(int32_t)uprv_strlen(directory); + newDataDir = (char *)uprv_malloc(length + 2); + /* Exit out if newDataDir could not be created. */ + if (newDataDir == nullptr) { + return; + } + uprv_strcpy(newDataDir, directory); + +#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) + { + char *p; + while((p = uprv_strchr(newDataDir, U_FILE_ALT_SEP_CHAR)) != nullptr) { + *p = U_FILE_SEP_CHAR; + } + } +#endif + } + + if (gDataDirectory && *gDataDirectory) { + uprv_free(gDataDirectory); + } + gDataDirectory = newDataDir; + ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup); +} + +U_CAPI UBool U_EXPORT2 +uprv_pathIsAbsolute(const char *path) +{ + if(!path || !*path) { + return false; + } + + if(*path == U_FILE_SEP_CHAR) { + return true; + } + +#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) + if(*path == U_FILE_ALT_SEP_CHAR) { + return true; + } +#endif + +#if U_PLATFORM_USES_ONLY_WIN32_API + if( (((path[0] >= 'A') && (path[0] <= 'Z')) || + ((path[0] >= 'a') && (path[0] <= 'z'))) && + path[1] == ':' ) { + return true; + } +#endif + + return false; +} + +/* Backup setting of ICU_DATA_DIR_PREFIX_ENV_VAR + (needed for some Darwin ICU build environments) */ +#if U_PLATFORM_IS_DARWIN_BASED && defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR +# if !defined(ICU_DATA_DIR_PREFIX_ENV_VAR) +# define ICU_DATA_DIR_PREFIX_ENV_VAR "IPHONE_SIMULATOR_ROOT" +# endif +#endif + +#if defined(ICU_DATA_DIR_WINDOWS) +// Helper function to get the ICU Data Directory under the Windows directory location. +static BOOL U_CALLCONV getIcuDataDirectoryUnderWindowsDirectory(char* directoryBuffer, UINT bufferLength) +{ + wchar_t windowsPath[MAX_PATH]; + char windowsPathUtf8[MAX_PATH]; + + UINT length = GetSystemWindowsDirectoryW(windowsPath, UPRV_LENGTHOF(windowsPath)); + if ((length > 0) && (length < (UPRV_LENGTHOF(windowsPath) - 1))) { + // Convert UTF-16 to a UTF-8 string. + UErrorCode status = U_ZERO_ERROR; + int32_t windowsPathUtf8Len = 0; + u_strToUTF8(windowsPathUtf8, static_cast(UPRV_LENGTHOF(windowsPathUtf8)), + &windowsPathUtf8Len, reinterpret_cast(windowsPath), -1, &status); + + if (U_SUCCESS(status) && (status != U_STRING_NOT_TERMINATED_WARNING) && + (windowsPathUtf8Len < (UPRV_LENGTHOF(windowsPathUtf8) - 1))) { + // Ensure it always has a separator, so we can append the ICU data path. + if (windowsPathUtf8[windowsPathUtf8Len - 1] != U_FILE_SEP_CHAR) { + windowsPathUtf8[windowsPathUtf8Len++] = U_FILE_SEP_CHAR; + windowsPathUtf8[windowsPathUtf8Len] = '\0'; + } + // Check if the concatenated string will fit. + if ((windowsPathUtf8Len + UPRV_LENGTHOF(ICU_DATA_DIR_WINDOWS)) < bufferLength) { + uprv_strcpy(directoryBuffer, windowsPathUtf8); + uprv_strcat(directoryBuffer, ICU_DATA_DIR_WINDOWS); + return true; + } + } + } + + return false; +} +#endif + +static void U_CALLCONV dataDirectoryInitFn() { + /* If we already have the directory, then return immediately. Will happen if user called + * u_setDataDirectory(). + */ + if (gDataDirectory) { + return; + } + + const char *path = nullptr; +#if defined(ICU_DATA_DIR_PREFIX_ENV_VAR) + char datadir_path_buffer[PATH_MAX]; +#endif + + /* + When ICU_NO_USER_DATA_OVERRIDE is defined, users aren't allowed to + override ICU's data with the ICU_DATA environment variable. This prevents + problems where multiple custom copies of ICU's specific version of data + are installed on a system. Either the application must define the data + directory with u_setDataDirectory, define ICU_DATA_DIR when compiling + ICU, set the data with udata_setCommonData or trust that all of the + required data is contained in ICU's data library that contains + the entry point defined by U_ICUDATA_ENTRY_POINT. + + There may also be some platforms where environment variables + are not allowed. + */ +# if !defined(ICU_NO_USER_DATA_OVERRIDE) && !UCONFIG_NO_FILE_IO + /* First try to get the environment variable */ +# if U_PLATFORM_HAS_WINUWP_API == 0 // Windows UWP does not support getenv + path=getenv("ICU_DATA"); +# endif +# endif + + /* ICU_DATA_DIR may be set as a compile option. + * U_ICU_DATA_DEFAULT_DIR is provided and is set by ICU at compile time + * and is used only when data is built in archive mode eliminating the need + * for ICU_DATA_DIR to be set. U_ICU_DATA_DEFAULT_DIR is set to the installation + * directory of the data dat file. Users should use ICU_DATA_DIR if they want to + * set their own path. + */ +#if defined(ICU_DATA_DIR) || defined(U_ICU_DATA_DEFAULT_DIR) + if(path==nullptr || *path==0) { +# if defined(ICU_DATA_DIR_PREFIX_ENV_VAR) + const char *prefix = getenv(ICU_DATA_DIR_PREFIX_ENV_VAR); +# endif +# ifdef ICU_DATA_DIR + path=ICU_DATA_DIR; +# else + path=U_ICU_DATA_DEFAULT_DIR; +# endif +# if defined(ICU_DATA_DIR_PREFIX_ENV_VAR) + if (prefix != nullptr) { + snprintf(datadir_path_buffer, sizeof(datadir_path_buffer), "%s%s", prefix, path); + path=datadir_path_buffer; + } +# endif + } +#endif + +#if defined(ICU_DATA_DIR_WINDOWS) + char datadir_path_buffer[MAX_PATH]; + if (getIcuDataDirectoryUnderWindowsDirectory(datadir_path_buffer, UPRV_LENGTHOF(datadir_path_buffer))) { + path = datadir_path_buffer; + } +#endif + + if(path==nullptr) { + /* It looks really bad, set it to something. */ + path = ""; + } + + u_setDataDirectory(path); + return; +} + +U_CAPI const char * U_EXPORT2 +u_getDataDirectory() { + umtx_initOnce(gDataDirInitOnce, &dataDirectoryInitFn); + return gDataDirectory; +} + +static void setTimeZoneFilesDir(const char *path, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + gTimeZoneFilesDirectory->clear(); + gTimeZoneFilesDirectory->append(path, status); +#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) + char *p = gTimeZoneFilesDirectory->data(); + while ((p = uprv_strchr(p, U_FILE_ALT_SEP_CHAR)) != nullptr) { + *p = U_FILE_SEP_CHAR; + } +#endif +} + +#define TO_STRING(x) TO_STRING_2(x) +#define TO_STRING_2(x) #x + +static void U_CALLCONV TimeZoneDataDirInitFn(UErrorCode &status) { + U_ASSERT(gTimeZoneFilesDirectory == nullptr); + ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup); + gTimeZoneFilesDirectory = new CharString(); + if (gTimeZoneFilesDirectory == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + const char *dir = ""; + +#if defined(ICU_TIMEZONE_FILES_DIR_PREFIX_ENV_VAR) + char timezonefilesdir_path_buffer[PATH_MAX]; + const char *prefix = getenv(ICU_TIMEZONE_FILES_DIR_PREFIX_ENV_VAR); +#endif + +#if U_PLATFORM_HAS_WINUWP_API == 1 +// The UWP version does not support the environment variable setting. + +# if defined(ICU_DATA_DIR_WINDOWS) + // When using the Windows system data, we can possibly pick up time zone data from the Windows directory. + char datadir_path_buffer[MAX_PATH]; + if (getIcuDataDirectoryUnderWindowsDirectory(datadir_path_buffer, UPRV_LENGTHOF(datadir_path_buffer))) { + dir = datadir_path_buffer; + } +# endif + +#else + dir = getenv("ICU_TIMEZONE_FILES_DIR"); +#endif // U_PLATFORM_HAS_WINUWP_API + +#if defined(U_TIMEZONE_FILES_DIR) + if (dir == nullptr) { + // Build time configuration setting. + dir = TO_STRING(U_TIMEZONE_FILES_DIR); + } +#endif + + if (dir == nullptr) { + dir = ""; + } + +#if defined(ICU_TIMEZONE_FILES_DIR_PREFIX_ENV_VAR) + if (prefix != nullptr) { + snprintf(timezonefilesdir_path_buffer, sizeof(timezonefilesdir_path_buffer), "%s%s", prefix, dir); + dir = timezonefilesdir_path_buffer; + } +#endif + + setTimeZoneFilesDir(dir, status); +} + + +U_CAPI const char * U_EXPORT2 +u_getTimeZoneFilesDirectory(UErrorCode *status) { + umtx_initOnce(gTimeZoneFilesInitOnce, &TimeZoneDataDirInitFn, *status); + return U_SUCCESS(*status) ? gTimeZoneFilesDirectory->data() : ""; +} + +U_CAPI void U_EXPORT2 +u_setTimeZoneFilesDirectory(const char *path, UErrorCode *status) { + umtx_initOnce(gTimeZoneFilesInitOnce, &TimeZoneDataDirInitFn, *status); + setTimeZoneFilesDir(path, *status); + + // Note: this function does some extra churn, first setting based on the + // environment, then immediately replacing with the value passed in. + // The logic is simpler that way, and performance shouldn't be an issue. +} + + +#if U_POSIX_LOCALE +/* A helper function used by uprv_getPOSIXIDForDefaultLocale and + * uprv_getPOSIXIDForDefaultCodepage. Returns the posix locale id for + * LC_CTYPE and LC_MESSAGES. It doesn't support other locale categories. + */ +static const char *uprv_getPOSIXIDForCategory(int category) +{ + const char* posixID = nullptr; + if (category == LC_MESSAGES || category == LC_CTYPE) { + /* + * On Solaris two different calls to setlocale can result in + * different values. Only get this value once. + * + * We must check this first because an application can set this. + * + * LC_ALL can't be used because it's platform dependent. The LANG + * environment variable seems to affect LC_CTYPE variable by default. + * Here is what setlocale(LC_ALL, nullptr) can return. + * HPUX can return 'C C C C C C C' + * Solaris can return /en_US/C/C/C/C/C on the second try. + * Linux can return LC_CTYPE=C;LC_NUMERIC=C;... + * + * The default codepage detection also needs to use LC_CTYPE. + * + * Do not call setlocale(LC_*, "")! Using an empty string instead + * of nullptr, will modify the libc behavior. + */ + posixID = setlocale(category, nullptr); + if ((posixID == 0) + || (uprv_strcmp("C", posixID) == 0) + || (uprv_strcmp("POSIX", posixID) == 0)) + { + /* Maybe we got some garbage. Try something more reasonable */ + posixID = getenv("LC_ALL"); + /* Solaris speaks POSIX - See IEEE Std 1003.1-2008 + * This is needed to properly handle empty env. variables + */ +#if U_PLATFORM == U_PF_SOLARIS + if ((posixID == 0) || (posixID[0] == '\0')) { + posixID = getenv(category == LC_MESSAGES ? "LC_MESSAGES" : "LC_CTYPE"); + if ((posixID == 0) || (posixID[0] == '\0')) { +#else + if (posixID == 0) { + posixID = getenv(category == LC_MESSAGES ? "LC_MESSAGES" : "LC_CTYPE"); + if (posixID == 0) { +#endif + posixID = getenv("LANG"); + } + } + } + } + if ((posixID==0) + || (uprv_strcmp("C", posixID) == 0) + || (uprv_strcmp("POSIX", posixID) == 0)) + { + /* Nothing worked. Give it a nice POSIX default value. */ + posixID = "en_US_POSIX"; + // Note: this test will not catch 'C.UTF-8', + // that will be handled in uprv_getDefaultLocaleID(). + // Leave this mapping here for the uprv_getPOSIXIDForDefaultCodepage() + // caller which expects to see "en_US_POSIX" in many branches. + } + return posixID; +} + +/* Return just the POSIX id for the default locale, whatever happens to be in + * it. It gets the value from LC_MESSAGES and indirectly from LC_ALL and LANG. + */ +static const char *uprv_getPOSIXIDForDefaultLocale() +{ + static const char* posixID = nullptr; + if (posixID == 0) { + posixID = uprv_getPOSIXIDForCategory(LC_MESSAGES); + } + return posixID; +} + +#if !U_CHARSET_IS_UTF8 +/* Return just the POSIX id for the default codepage, whatever happens to be in + * it. It gets the value from LC_CTYPE and indirectly from LC_ALL and LANG. + */ +static const char *uprv_getPOSIXIDForDefaultCodepage() +{ + static const char* posixID = nullptr; + if (posixID == 0) { + posixID = uprv_getPOSIXIDForCategory(LC_CTYPE); + } + return posixID; +} +#endif +#endif + +/* NOTE: The caller should handle thread safety */ +U_CAPI const char* U_EXPORT2 +uprv_getDefaultLocaleID() +{ +#if U_POSIX_LOCALE +/* + Note that: (a '!' means the ID is improper somehow) + LC_ALL ----> default_loc codepage +-------------------------------------------------------- + ab.CD ab CD + ab@CD ab__CD - + ab@CD.EF ab__CD EF + + ab_CD.EF@GH ab_CD_GH EF + +Some 'improper' ways to do the same as above: + ! ab_CD@GH.EF ab_CD_GH EF + ! ab_CD.EF@GH.IJ ab_CD_GH EF + ! ab_CD@ZZ.EF@GH.IJ ab_CD_GH EF + + _CD@GH _CD_GH - + _CD.EF@GH _CD_GH EF + +The variant cannot have dots in it. +The 'rightmost' variant (@xxx) wins. +The leftmost codepage (.xxx) wins. +*/ + const char* posixID = uprv_getPOSIXIDForDefaultLocale(); + + /* Format: (no spaces) + ll [ _CC ] [ . MM ] [ @ VV] + + l = lang, C = ctry, M = charmap, V = variant + */ + + if (gCorrectedPOSIXLocale != nullptr) { + return gCorrectedPOSIXLocale; + } + + // Copy the ID into owned memory. + // Over-allocate in case we replace "C" with "en_US_POSIX" (+10), + null termination + char *correctedPOSIXLocale = static_cast(uprv_malloc(uprv_strlen(posixID) + 10 + 1)); + if (correctedPOSIXLocale == nullptr) { + return nullptr; + } + uprv_strcpy(correctedPOSIXLocale, posixID); + + char *limit; + if ((limit = uprv_strchr(correctedPOSIXLocale, '.')) != nullptr) { + *limit = 0; + } + if ((limit = uprv_strchr(correctedPOSIXLocale, '@')) != nullptr) { + *limit = 0; + } + + if ((uprv_strcmp("C", correctedPOSIXLocale) == 0) // no @ variant + || (uprv_strcmp("POSIX", correctedPOSIXLocale) == 0)) { + // Raw input was C.* or POSIX.*, Give it a nice POSIX default value. + // (The "C"/"POSIX" case is handled in uprv_getPOSIXIDForCategory()) + uprv_strcpy(correctedPOSIXLocale, "en_US_POSIX"); + } + + /* Note that we scan the *uncorrected* ID. */ + const char *p; + if ((p = uprv_strrchr(posixID, '@')) != nullptr) { + p++; + + /* Take care of any special cases here.. */ + if (!uprv_strcmp(p, "nynorsk")) { + p = "NY"; + /* Don't worry about no__NY. In practice, it won't appear. */ + } + + if (uprv_strchr(correctedPOSIXLocale,'_') == nullptr) { + uprv_strcat(correctedPOSIXLocale, "__"); /* aa@b -> aa__b (note this can make the new locale 1 char longer) */ + } + else { + uprv_strcat(correctedPOSIXLocale, "_"); /* aa_CC@b -> aa_CC_b */ + } + + const char *q; + if ((q = uprv_strchr(p, '.')) != nullptr) { + /* How big will the resulting string be? */ + int32_t len = (int32_t)(uprv_strlen(correctedPOSIXLocale) + (q-p)); + uprv_strncat(correctedPOSIXLocale, p, q-p); // do not include charset + correctedPOSIXLocale[len] = 0; + } + else { + /* Anything following the @ sign */ + uprv_strcat(correctedPOSIXLocale, p); + } + + /* Should there be a map from 'no@nynorsk' -> no_NO_NY here? + * How about 'russian' -> 'ru'? + * Many of the other locales using ISO codes will be handled by the + * canonicalization functions in uloc_getDefault. + */ + } + + if (gCorrectedPOSIXLocale == nullptr) { + gCorrectedPOSIXLocale = correctedPOSIXLocale; + gCorrectedPOSIXLocaleHeapAllocated = true; + ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup); + correctedPOSIXLocale = nullptr; + } + posixID = gCorrectedPOSIXLocale; + + if (correctedPOSIXLocale != nullptr) { /* Was already set - clean up. */ + uprv_free(correctedPOSIXLocale); + } + + return posixID; + +#elif U_PLATFORM_USES_ONLY_WIN32_API +#define POSIX_LOCALE_CAPACITY 64 + UErrorCode status = U_ZERO_ERROR; + char *correctedPOSIXLocale = nullptr; + + // If we have already figured this out just use the cached value + if (gCorrectedPOSIXLocale != nullptr) { + return gCorrectedPOSIXLocale; + } + + // No cached value, need to determine the current value + static WCHAR windowsLocale[LOCALE_NAME_MAX_LENGTH] = {}; + int length = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, windowsLocale, LOCALE_NAME_MAX_LENGTH); + + // Now we should have a Windows locale name that needs converted to the POSIX style. + if (length > 0) // If length is 0, then the GetLocaleInfoEx failed. + { + // First we need to go from UTF-16 to char (and also convert from _ to - while we're at it.) + char modifiedWindowsLocale[LOCALE_NAME_MAX_LENGTH] = {}; + + int32_t i; + for (i = 0; i < UPRV_LENGTHOF(modifiedWindowsLocale); i++) + { + if (windowsLocale[i] == '_') + { + modifiedWindowsLocale[i] = '-'; + } + else + { + modifiedWindowsLocale[i] = static_cast(windowsLocale[i]); + } + + if (modifiedWindowsLocale[i] == '\0') + { + break; + } + } + + if (i >= UPRV_LENGTHOF(modifiedWindowsLocale)) + { + // Ran out of room, can't really happen, maybe we'll be lucky about a matching + // locale when tags are dropped + modifiedWindowsLocale[UPRV_LENGTHOF(modifiedWindowsLocale) - 1] = '\0'; + } + + // Now normalize the resulting name + correctedPOSIXLocale = static_cast(uprv_malloc(POSIX_LOCALE_CAPACITY + 1)); + /* TODO: Should we just exit on memory allocation failure? */ + if (correctedPOSIXLocale) + { + int32_t posixLen = uloc_canonicalize(modifiedWindowsLocale, correctedPOSIXLocale, POSIX_LOCALE_CAPACITY, &status); + if (U_SUCCESS(status)) + { + *(correctedPOSIXLocale + posixLen) = 0; + gCorrectedPOSIXLocale = correctedPOSIXLocale; + gCorrectedPOSIXLocaleHeapAllocated = true; + ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup); + } + else + { + uprv_free(correctedPOSIXLocale); + } + } + } + + // If unable to find a locale we can agree upon, use en-US by default + if (gCorrectedPOSIXLocale == nullptr) { + gCorrectedPOSIXLocale = "en_US"; + } + return gCorrectedPOSIXLocale; + +#elif U_PLATFORM == U_PF_OS400 + /* locales are process scoped and are by definition thread safe */ + static char correctedLocale[64]; + const char *localeID = getenv("LC_ALL"); + char *p; + + if (localeID == nullptr) + localeID = getenv("LANG"); + if (localeID == nullptr) + localeID = setlocale(LC_ALL, nullptr); + /* Make sure we have something... */ + if (localeID == nullptr) + return "en_US_POSIX"; + + /* Extract the locale name from the path. */ + if((p = uprv_strrchr(localeID, '/')) != nullptr) + { + /* Increment p to start of locale name. */ + p++; + localeID = p; + } + + /* Copy to work location. */ + uprv_strcpy(correctedLocale, localeID); + + /* Strip off the '.locale' extension. */ + if((p = uprv_strchr(correctedLocale, '.')) != nullptr) { + *p = 0; + } + + /* Upper case the locale name. */ + T_CString_toUpperCase(correctedLocale); + + /* See if we are using the POSIX locale. Any of the + * following are equivalent and use the same QLGPGCMA + * (POSIX) locale. + * QLGPGCMA2 means UCS2 + * QLGPGCMA_4 means UTF-32 + * QLGPGCMA_8 means UTF-8 + */ + if ((uprv_strcmp("C", correctedLocale) == 0) || + (uprv_strcmp("POSIX", correctedLocale) == 0) || + (uprv_strncmp("QLGPGCMA", correctedLocale, 8) == 0)) + { + uprv_strcpy(correctedLocale, "en_US_POSIX"); + } + else + { + int16_t LocaleLen; + + /* Lower case the lang portion. */ + for(p = correctedLocale; *p != 0 && *p != '_'; p++) + { + *p = uprv_tolower(*p); + } + + /* Adjust for Euro. After '_E' add 'URO'. */ + LocaleLen = uprv_strlen(correctedLocale); + if (correctedLocale[LocaleLen - 2] == '_' && + correctedLocale[LocaleLen - 1] == 'E') + { + uprv_strcat(correctedLocale, "URO"); + } + + /* If using Lotus-based locale then convert to + * equivalent non Lotus. + */ + else if (correctedLocale[LocaleLen - 2] == '_' && + correctedLocale[LocaleLen - 1] == 'L') + { + correctedLocale[LocaleLen - 2] = 0; + } + + /* There are separate simplified and traditional + * locales called zh_HK_S and zh_HK_T. + */ + else if (uprv_strncmp(correctedLocale, "zh_HK", 5) == 0) + { + uprv_strcpy(correctedLocale, "zh_HK"); + } + + /* A special zh_CN_GBK locale... + */ + else if (uprv_strcmp(correctedLocale, "zh_CN_GBK") == 0) + { + uprv_strcpy(correctedLocale, "zh_CN"); + } + + } + + return correctedLocale; +#endif + +} + +#if !U_CHARSET_IS_UTF8 +#if U_POSIX_LOCALE +/* +Due to various platform differences, one platform may specify a charset, +when they really mean a different charset. Remap the names so that they are +compatible with ICU. Only conflicting/ambiguous aliases should be resolved +here. Before adding anything to this function, please consider adding unique +names to the ICU alias table in the data directory. +*/ +static const char* +remapPlatformDependentCodepage(const char *locale, const char *name) { + if (locale != nullptr && *locale == 0) { + /* Make sure that an empty locale is handled the same way. */ + locale = nullptr; + } + if (name == nullptr) { + return nullptr; + } +#if U_PLATFORM == U_PF_AIX + if (uprv_strcmp(name, "IBM-943") == 0) { + /* Use the ASCII compatible ibm-943 */ + name = "Shift-JIS"; + } + else if (uprv_strcmp(name, "IBM-1252") == 0) { + /* Use the windows-1252 that contains the Euro */ + name = "IBM-5348"; + } +#elif U_PLATFORM == U_PF_SOLARIS + if (locale != nullptr && uprv_strcmp(name, "EUC") == 0) { + /* Solaris underspecifies the "EUC" name. */ + if (uprv_strcmp(locale, "zh_CN") == 0) { + name = "EUC-CN"; + } + else if (uprv_strcmp(locale, "zh_TW") == 0) { + name = "EUC-TW"; + } + else if (uprv_strcmp(locale, "ko_KR") == 0) { + name = "EUC-KR"; + } + } + else if (uprv_strcmp(name, "eucJP") == 0) { + /* + ibm-954 is the best match. + ibm-33722 is the default for eucJP (similar to Windows). + */ + name = "eucjis"; + } + else if (uprv_strcmp(name, "646") == 0) { + /* + * The default codepage given by Solaris is 646 but the C library routines treat it as if it was + * ISO-8859-1 instead of US-ASCII(646). + */ + name = "ISO-8859-1"; + } +#elif U_PLATFORM_IS_DARWIN_BASED + if (locale == nullptr && *name == 0) { + /* + No locale was specified, and an empty name was passed in. + This usually indicates that nl_langinfo didn't return valid information. + Mac OS X uses UTF-8 by default (especially the locale data and console). + */ + name = "UTF-8"; + } + else if (uprv_strcmp(name, "CP949") == 0) { + /* Remap CP949 to a similar codepage to avoid issues with backslash and won symbol. */ + name = "EUC-KR"; + } + else if (locale != nullptr && uprv_strcmp(locale, "en_US_POSIX") != 0 && uprv_strcmp(name, "US-ASCII") == 0) { + /* + * For non C/POSIX locale, default the code page to UTF-8 instead of US-ASCII. + */ + name = "UTF-8"; + } +#elif U_PLATFORM == U_PF_BSD + if (uprv_strcmp(name, "CP949") == 0) { + /* Remap CP949 to a similar codepage to avoid issues with backslash and won symbol. */ + name = "EUC-KR"; + } +#elif U_PLATFORM == U_PF_HPUX + if (locale != nullptr && uprv_strcmp(locale, "zh_HK") == 0 && uprv_strcmp(name, "big5") == 0) { + /* HP decided to extend big5 as hkbig5 even though it's not compatible :-( */ + /* zh_TW.big5 is not the same charset as zh_HK.big5! */ + name = "hkbig5"; + } + else if (uprv_strcmp(name, "eucJP") == 0) { + /* + ibm-1350 is the best match, but unavailable. + ibm-954 is mostly a superset of ibm-1350. + ibm-33722 is the default for eucJP (similar to Windows). + */ + name = "eucjis"; + } +#elif U_PLATFORM == U_PF_LINUX + if (locale != nullptr && uprv_strcmp(name, "euc") == 0) { + /* Linux underspecifies the "EUC" name. */ + if (uprv_strcmp(locale, "korean") == 0) { + name = "EUC-KR"; + } + else if (uprv_strcmp(locale, "japanese") == 0) { + /* See comment below about eucJP */ + name = "eucjis"; + } + } + else if (uprv_strcmp(name, "eucjp") == 0) { + /* + ibm-1350 is the best match, but unavailable. + ibm-954 is mostly a superset of ibm-1350. + ibm-33722 is the default for eucJP (similar to Windows). + */ + name = "eucjis"; + } + else if (locale != nullptr && uprv_strcmp(locale, "en_US_POSIX") != 0 && + (uprv_strcmp(name, "ANSI_X3.4-1968") == 0 || uprv_strcmp(name, "US-ASCII") == 0)) { + /* + * For non C/POSIX locale, default the code page to UTF-8 instead of US-ASCII. + */ + name = "UTF-8"; + } + /* + * Linux returns ANSI_X3.4-1968 for C/POSIX, but the call site takes care of + * it by falling back to 'US-ASCII' when nullptr is returned from this + * function. So, we don't have to worry about it here. + */ +#endif + /* return nullptr when "" is passed in */ + if (*name == 0) { + name = nullptr; + } + return name; +} + +static const char* +getCodepageFromPOSIXID(const char *localeName, char * buffer, int32_t buffCapacity) +{ + char localeBuf[100]; + const char *name = nullptr; + char *variant = nullptr; + + if (localeName != nullptr && (name = (uprv_strchr(localeName, '.'))) != nullptr) { + size_t localeCapacity = uprv_min(sizeof(localeBuf), (name-localeName)+1); + uprv_strncpy(localeBuf, localeName, localeCapacity); + localeBuf[localeCapacity-1] = 0; /* ensure NUL termination */ + name = uprv_strncpy(buffer, name+1, buffCapacity); + buffer[buffCapacity-1] = 0; /* ensure NUL termination */ + if ((variant = const_cast(uprv_strchr(name, '@'))) != nullptr) { + *variant = 0; + } + name = remapPlatformDependentCodepage(localeBuf, name); + } + return name; +} +#endif + +static const char* +int_getDefaultCodepage() +{ +#if U_PLATFORM == U_PF_OS400 + uint32_t ccsid = 37; /* Default to ibm-37 */ + static char codepage[64]; + Qwc_JOBI0400_t jobinfo; + Qus_EC_t error = { sizeof(Qus_EC_t) }; /* SPI error code */ + + EPT_CALL(QUSRJOBI)(&jobinfo, sizeof(jobinfo), "JOBI0400", + "* ", " ", &error); + + if (error.Bytes_Available == 0) { + if (jobinfo.Coded_Char_Set_ID != 0xFFFF) { + ccsid = (uint32_t)jobinfo.Coded_Char_Set_ID; + } + else if (jobinfo.Default_Coded_Char_Set_Id != 0xFFFF) { + ccsid = (uint32_t)jobinfo.Default_Coded_Char_Set_Id; + } + /* else use the default */ + } + snprintf(codepage, sizeof(codepage), "ibm-%d", ccsid); + return codepage; + +#elif U_PLATFORM == U_PF_OS390 + static char codepage[64]; + + strncpy(codepage, nl_langinfo(CODESET),63-strlen(UCNV_SWAP_LFNL_OPTION_STRING)); + strcat(codepage,UCNV_SWAP_LFNL_OPTION_STRING); + codepage[63] = 0; /* NUL terminate */ + + return codepage; + +#elif U_PLATFORM_USES_ONLY_WIN32_API + static char codepage[64]; + DWORD codepageNumber = 0; + +#if U_PLATFORM_HAS_WINUWP_API == 1 + // UWP doesn't have a direct API to get the default ACP as Microsoft would rather + // have folks use Unicode than a "system" code page, however this is the same + // codepage as the system default locale codepage. (FWIW, the system locale is + // ONLY used for codepage, it should never be used for anything else) + GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, + (LPWSTR)&codepageNumber, sizeof(codepageNumber) / sizeof(WCHAR)); +#else + // Win32 apps can call GetACP + codepageNumber = GetACP(); +#endif + // Special case for UTF-8 + if (codepageNumber == 65001) + { + return "UTF-8"; + } + // Windows codepages can look like windows-1252, so format the found number + // the numbers are eclectic, however all valid system code pages, besides UTF-8 + // are between 3 and 19999 + if (codepageNumber > 0 && codepageNumber < 20000) + { + snprintf(codepage, sizeof(codepage), "windows-%ld", codepageNumber); + return codepage; + } + // If the codepage number call failed then return UTF-8 + return "UTF-8"; + +#elif U_POSIX_LOCALE + static char codesetName[100]; + const char *localeName = nullptr; + const char *name = nullptr; + + localeName = uprv_getPOSIXIDForDefaultCodepage(); + uprv_memset(codesetName, 0, sizeof(codesetName)); + /* On Solaris nl_langinfo returns C locale values unless setlocale + * was called earlier. + */ +#if (U_HAVE_NL_LANGINFO_CODESET && U_PLATFORM != U_PF_SOLARIS) + /* When available, check nl_langinfo first because it usually gives more + useful names. It depends on LC_CTYPE. + nl_langinfo may use the same buffer as setlocale. */ + { + const char *codeset = nl_langinfo(U_NL_LANGINFO_CODESET); +#if U_PLATFORM_IS_DARWIN_BASED || U_PLATFORM_IS_LINUX_BASED + /* + * On Linux and MacOSX, ensure that default codepage for non C/POSIX locale is UTF-8 + * instead of ASCII. + */ + if (uprv_strcmp(localeName, "en_US_POSIX") != 0) { + codeset = remapPlatformDependentCodepage(localeName, codeset); + } else +#endif + { + codeset = remapPlatformDependentCodepage(nullptr, codeset); + } + + if (codeset != nullptr) { + uprv_strncpy(codesetName, codeset, sizeof(codesetName)); + codesetName[sizeof(codesetName)-1] = 0; + return codesetName; + } + } +#endif + + /* Use setlocale in a nice way, and then check some environment variables. + Maybe the application used setlocale already. + */ + uprv_memset(codesetName, 0, sizeof(codesetName)); + name = getCodepageFromPOSIXID(localeName, codesetName, sizeof(codesetName)); + if (name) { + /* if we can find the codeset name from setlocale, return that. */ + return name; + } + + if (*codesetName == 0) + { + /* Everything failed. Return US ASCII (ISO 646). */ + (void)uprv_strcpy(codesetName, "US-ASCII"); + } + return codesetName; +#else + return "US-ASCII"; +#endif +} + + +U_CAPI const char* U_EXPORT2 +uprv_getDefaultCodepage() +{ + static char const *name = nullptr; + umtx_lock(nullptr); + if (name == nullptr) { + name = int_getDefaultCodepage(); + } + umtx_unlock(nullptr); + return name; +} +#endif /* !U_CHARSET_IS_UTF8 */ + + +/* end of platform-specific implementation -------------- */ + +/* version handling --------------------------------------------------------- */ + +U_CAPI void U_EXPORT2 +u_versionFromString(UVersionInfo versionArray, const char *versionString) { + char *end; + uint16_t part=0; + + if(versionArray==nullptr) { + return; + } + + if(versionString!=nullptr) { + for(;;) { + versionArray[part]=(uint8_t)uprv_strtoul(versionString, &end, 10); + if(end==versionString || ++part==U_MAX_VERSION_LENGTH || *end!=U_VERSION_DELIMITER) { + break; + } + versionString=end+1; + } + } + + while(partU_MAX_VERSION_STRING_LENGTH) { + len = U_MAX_VERSION_STRING_LENGTH; + } + u_UCharsToChars(versionString, versionChars, len); + versionChars[len]=0; + u_versionFromString(versionArray, versionChars); + } +} + +U_CAPI void U_EXPORT2 +u_versionToString(const UVersionInfo versionArray, char *versionString) { + uint16_t count, part; + uint8_t field; + + if(versionString==nullptr) { + return; + } + + if(versionArray==nullptr) { + versionString[0]=0; + return; + } + + /* count how many fields need to be written */ + for(count=4; count>0 && versionArray[count-1]==0; --count) { + } + + if(count <= 1) { + count = 2; + } + + /* write the first part */ + /* write the decimal field value */ + field=versionArray[0]; + if(field>=100) { + *versionString++=(char)('0'+field/100); + field%=100; + } + if(field>=10) { + *versionString++=(char)('0'+field/10); + field%=10; + } + *versionString++=(char)('0'+field); + + /* write the following parts */ + for(part=1; part=100) { + *versionString++=(char)('0'+field/100); + field%=100; + } + if(field>=10) { + *versionString++=(char)('0'+field/10); + field%=10; + } + *versionString++=(char)('0'+field); + } + + /* NUL-terminate */ + *versionString=0; +} + +U_CAPI void U_EXPORT2 +u_getVersion(UVersionInfo versionArray) { + (void)copyright; // Suppress unused variable warning from clang. + u_versionFromString(versionArray, U_ICU_VERSION); +} + +/** + * icucfg.h dependent code + */ + +#if U_ENABLE_DYLOAD && HAVE_DLOPEN && !U_PLATFORM_USES_ONLY_WIN32_API + +#if HAVE_DLFCN_H +#ifdef __MVS__ +#ifndef __SUSV3 +#define __SUSV3 1 +#endif +#endif +#include +#endif /* HAVE_DLFCN_H */ + +U_CAPI void * U_EXPORT2 +uprv_dl_open(const char *libName, UErrorCode *status) { + void *ret = nullptr; + if(U_FAILURE(*status)) return ret; + ret = dlopen(libName, RTLD_NOW|RTLD_GLOBAL); + if(ret==nullptr) { +#ifdef U_TRACE_DYLOAD + printf("dlerror on dlopen(%s): %s\n", libName, dlerror()); +#endif + *status = U_MISSING_RESOURCE_ERROR; + } + return ret; +} + +U_CAPI void U_EXPORT2 +uprv_dl_close(void *lib, UErrorCode *status) { + if(U_FAILURE(*status)) return; + dlclose(lib); +} + +U_CAPI UVoidFunction* U_EXPORT2 +uprv_dlsym_func(void *lib, const char* sym, UErrorCode *status) { + union { + UVoidFunction *fp; + void *vp; + } uret; + uret.fp = nullptr; + if(U_FAILURE(*status)) return uret.fp; + uret.vp = dlsym(lib, sym); + if(uret.vp == nullptr) { +#ifdef U_TRACE_DYLOAD + printf("dlerror on dlsym(%p,%s): %s\n", lib,sym, dlerror()); +#endif + *status = U_MISSING_RESOURCE_ERROR; + } + return uret.fp; +} + +#elif U_ENABLE_DYLOAD && U_PLATFORM_USES_ONLY_WIN32_API && !U_PLATFORM_HAS_WINUWP_API + +/* Windows API implementation. */ +// Note: UWP does not expose/allow these APIs, so the UWP version gets the null implementation. */ + +U_CAPI void * U_EXPORT2 +uprv_dl_open(const char *libName, UErrorCode *status) { + HMODULE lib = nullptr; + + if(U_FAILURE(*status)) return nullptr; + + lib = LoadLibraryA(libName); + + if(lib==nullptr) { + *status = U_MISSING_RESOURCE_ERROR; + } + + return (void*)lib; +} + +U_CAPI void U_EXPORT2 +uprv_dl_close(void *lib, UErrorCode *status) { + HMODULE handle = (HMODULE)lib; + if(U_FAILURE(*status)) return; + + FreeLibrary(handle); + + return; +} + +U_CAPI UVoidFunction* U_EXPORT2 +uprv_dlsym_func(void *lib, const char* sym, UErrorCode *status) { + HMODULE handle = (HMODULE)lib; + UVoidFunction* addr = nullptr; + + if(U_FAILURE(*status) || lib==nullptr) return nullptr; + + addr = (UVoidFunction*)GetProcAddress(handle, sym); + + if(addr==nullptr) { + DWORD lastError = GetLastError(); + if(lastError == ERROR_PROC_NOT_FOUND) { + *status = U_MISSING_RESOURCE_ERROR; + } else { + *status = U_UNSUPPORTED_ERROR; /* other unknown error. */ + } + } + + return addr; +} + +#else + +/* No dynamic loading, null (nonexistent) implementation. */ + +U_CAPI void * U_EXPORT2 +uprv_dl_open(const char *libName, UErrorCode *status) { + (void)libName; + if(U_FAILURE(*status)) return nullptr; + *status = U_UNSUPPORTED_ERROR; + return nullptr; +} + +U_CAPI void U_EXPORT2 +uprv_dl_close(void *lib, UErrorCode *status) { + (void)lib; + if(U_FAILURE(*status)) return; + *status = U_UNSUPPORTED_ERROR; + return; +} + +U_CAPI UVoidFunction* U_EXPORT2 +uprv_dlsym_func(void *lib, const char* sym, UErrorCode *status) { + (void)lib; + (void)sym; + if(U_SUCCESS(*status)) { + *status = U_UNSUPPORTED_ERROR; + } + return (UVoidFunction*)nullptr; +} + +#endif + +/* + * Hey, Emacs, please set the following: + * + * Local Variables: + * indent-tabs-mode: nil + * End: + * + */ diff --git a/deps/icu-small/source/common/putilimp.h b/deps/icu-small/source/common/putilimp.h index 5b95a68418c428..64b72b2d381422 100644 --- a/deps/icu-small/source/common/putilimp.h +++ b/deps/icu-small/source/common/putilimp.h @@ -1,615 +1,615 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1997-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* FILE NAME : putilimp.h -* -* Date Name Description -* 10/17/04 grhoten Move internal functions from putil.h to this file. -****************************************************************************** -*/ - -#ifndef PUTILIMP_H -#define PUTILIMP_H - -#include "unicode/utypes.h" -#include "unicode/putil.h" - -/** - * \def U_SIGNED_RIGHT_SHIFT_IS_ARITHMETIC - * Nearly all CPUs and compilers implement a right-shift of a signed integer - * as an Arithmetic Shift Right which copies the sign bit (the Most Significant Bit (MSB)) - * into the vacated bits (sign extension). - * For example, (int32_t)0xfff5fff3>>4 becomes 0xffff5fff and -1>>1=-1. - * - * This can be useful for storing a signed value in the upper bits - * and another bit field in the lower bits. - * The signed value can be retrieved by simple right-shifting. - * - * This is consistent with the Java language. - * - * However, the C standard allows compilers to implement a right-shift of a signed integer - * as a Logical Shift Right which copies a 0 into the vacated bits. - * For example, (int32_t)0xfff5fff3>>4 becomes 0x0fff5fff and -1>>1=0x7fffffff. - * - * Code that depends on the natural behavior should be guarded with this macro, - * with an alternate path for unusual platforms. - * @internal - */ -#ifdef U_SIGNED_RIGHT_SHIFT_IS_ARITHMETIC - /* Use the predefined value. */ -#else - /* - * Nearly all CPUs & compilers implement a right-shift of a signed integer - * as an Arithmetic Shift Right (with sign extension). - */ -# define U_SIGNED_RIGHT_SHIFT_IS_ARITHMETIC 1 -#endif - -/** Define this to 1 if your platform supports IEEE 754 floating point, - to 0 if it does not. */ -#ifndef IEEE_754 -# define IEEE_754 1 -#endif - -/** - * uintptr_t is an optional part of the standard definitions in stdint.h. - * The opengroup.org documentation for stdint.h says - * "On XSI-conformant systems, the intptr_t and uintptr_t types are required; - * otherwise, they are optional." - * We assume that when uintptr_t is defined, UINTPTR_MAX is defined as well. - * - * Do not use ptrdiff_t since it is signed. size_t is unsigned. - */ -/* TODO: This check fails on some z environments. Filed a ticket #9357 for this. */ -#if !defined(__intptr_t_defined) && !defined(UINTPTR_MAX) && (U_PLATFORM != U_PF_OS390) -typedef size_t uintptr_t; -#endif - -/*===========================================================================*/ -/** @{ Information about POSIX support */ -/*===========================================================================*/ - -#ifdef U_HAVE_NL_LANGINFO_CODESET - /* Use the predefined value. */ -#elif U_PLATFORM_USES_ONLY_WIN32_API || U_PLATFORM == U_PF_ANDROID || U_PLATFORM == U_PF_QNX -# define U_HAVE_NL_LANGINFO_CODESET 0 -#else -# define U_HAVE_NL_LANGINFO_CODESET 1 -#endif - -#ifdef U_NL_LANGINFO_CODESET - /* Use the predefined value. */ -#elif !U_HAVE_NL_LANGINFO_CODESET -# define U_NL_LANGINFO_CODESET -1 -#elif U_PLATFORM == U_PF_OS400 - /* not defined */ -#else -# define U_NL_LANGINFO_CODESET CODESET -#endif - -#if defined(U_TZSET) || defined(U_HAVE_TZSET) - /* Use the predefined value. */ -#elif U_PLATFORM_USES_ONLY_WIN32_API - // UWP doesn't support tzset or environment variables for tz -#if U_PLATFORM_HAS_WINUWP_API == 0 -# define U_TZSET _tzset -#endif -#elif U_PLATFORM == U_PF_OS400 - /* not defined */ -#else -# define U_TZSET tzset -#endif - -#if defined(U_TIMEZONE) || defined(U_HAVE_TIMEZONE) - /* Use the predefined value. */ -#elif U_PLATFORM == U_PF_ANDROID -# define U_TIMEZONE timezone -#elif defined(__UCLIBC__) - // uClibc does not have __timezone or _timezone. -#elif defined(_NEWLIB_VERSION) -# define U_TIMEZONE _timezone -#elif defined(__GLIBC__) - // glibc -# define U_TIMEZONE __timezone -#elif U_PLATFORM_IS_LINUX_BASED - // not defined -#elif U_PLATFORM_USES_ONLY_WIN32_API -# define U_TIMEZONE _timezone -#elif U_PLATFORM == U_PF_BSD && !defined(__NetBSD__) - /* not defined */ -#elif U_PLATFORM == U_PF_OS400 - /* not defined */ -#elif U_PLATFORM == U_PF_IPHONE - /* not defined */ -#else -# define U_TIMEZONE timezone -#endif - -#if defined(U_TZNAME) || defined(U_HAVE_TZNAME) - /* Use the predefined value. */ -#elif U_PLATFORM_USES_ONLY_WIN32_API - /* not usable on all windows platforms */ -#if U_PLATFORM_HAS_WINUWP_API == 0 -# define U_TZNAME _tzname -#endif -#elif U_PLATFORM == U_PF_OS400 - /* not defined */ -#else -# define U_TZNAME tzname -#endif - -#ifdef U_HAVE_MMAP - /* Use the predefined value. */ -#elif U_PLATFORM_USES_ONLY_WIN32_API -# define U_HAVE_MMAP 0 -#else -# define U_HAVE_MMAP 1 -#endif - -#ifdef U_HAVE_POPEN - /* Use the predefined value. */ -#elif U_PLATFORM_USES_ONLY_WIN32_API -# define U_HAVE_POPEN 0 -#elif U_PLATFORM == U_PF_OS400 -# define U_HAVE_POPEN 0 -#else -# define U_HAVE_POPEN 1 -#endif - -/** - * \def U_HAVE_DIRENT_H - * Defines whether dirent.h is available. - * @internal - */ -#ifdef U_HAVE_DIRENT_H - /* Use the predefined value. */ -#elif U_PLATFORM_USES_ONLY_WIN32_API -# define U_HAVE_DIRENT_H 0 -#else -# define U_HAVE_DIRENT_H 1 -#endif - -/** @} */ - -/*===========================================================================*/ -/** @{ Programs used by ICU code */ -/*===========================================================================*/ - -/** - * \def U_MAKE_IS_NMAKE - * Defines whether the "make" program is Windows nmake. - */ -#ifdef U_MAKE_IS_NMAKE - /* Use the predefined value. */ -#elif U_PLATFORM == U_PF_WINDOWS -# define U_MAKE_IS_NMAKE 1 -#else -# define U_MAKE_IS_NMAKE 0 -#endif - -/** @} */ - -/*==========================================================================*/ -/* Platform utilities */ -/*==========================================================================*/ - -/** - * Platform utilities isolates the platform dependencies of the - * library. For each platform which this code is ported to, these - * functions may have to be re-implemented. - */ - -/** - * Floating point utility to determine if a double is Not a Number (NaN). - * @internal - */ -U_CAPI UBool U_EXPORT2 uprv_isNaN(double d); -/** - * Floating point utility to determine if a double has an infinite value. - * @internal - */ -U_CAPI UBool U_EXPORT2 uprv_isInfinite(double d); -/** - * Floating point utility to determine if a double has a positive infinite value. - * @internal - */ -U_CAPI UBool U_EXPORT2 uprv_isPositiveInfinity(double d); -/** - * Floating point utility to determine if a double has a negative infinite value. - * @internal - */ -U_CAPI UBool U_EXPORT2 uprv_isNegativeInfinity(double d); -/** - * Floating point utility that returns a Not a Number (NaN) value. - * @internal - */ -U_CAPI double U_EXPORT2 uprv_getNaN(void); -/** - * Floating point utility that returns an infinite value. - * @internal - */ -U_CAPI double U_EXPORT2 uprv_getInfinity(void); - -/** - * Floating point utility to truncate a double. - * @internal - */ -U_CAPI double U_EXPORT2 uprv_trunc(double d); -/** - * Floating point utility to calculate the floor of a double. - * @internal - */ -U_CAPI double U_EXPORT2 uprv_floor(double d); -/** - * Floating point utility to calculate the ceiling of a double. - * @internal - */ -U_CAPI double U_EXPORT2 uprv_ceil(double d); -/** - * Floating point utility to calculate the absolute value of a double. - * @internal - */ -U_CAPI double U_EXPORT2 uprv_fabs(double d); -/** - * Floating point utility to calculate the fractional and integer parts of a double. - * @internal - */ -U_CAPI double U_EXPORT2 uprv_modf(double d, double* pinteger); -/** - * Floating point utility to calculate the remainder of a double divided by another double. - * @internal - */ -U_CAPI double U_EXPORT2 uprv_fmod(double d, double y); -/** - * Floating point utility to calculate d to the power of exponent (d^exponent). - * @internal - */ -U_CAPI double U_EXPORT2 uprv_pow(double d, double exponent); -/** - * Floating point utility to calculate 10 to the power of exponent (10^exponent). - * @internal - */ -U_CAPI double U_EXPORT2 uprv_pow10(int32_t exponent); -/** - * Floating point utility to calculate the maximum value of two doubles. - * @internal - */ -U_CAPI double U_EXPORT2 uprv_fmax(double d, double y); -/** - * Floating point utility to calculate the minimum value of two doubles. - * @internal - */ -U_CAPI double U_EXPORT2 uprv_fmin(double d, double y); -/** - * Private utility to calculate the maximum value of two integers. - * @internal - */ -U_CAPI int32_t U_EXPORT2 uprv_max(int32_t d, int32_t y); -/** - * Private utility to calculate the minimum value of two integers. - * @internal - */ -U_CAPI int32_t U_EXPORT2 uprv_min(int32_t d, int32_t y); - -#if U_IS_BIG_ENDIAN -# define uprv_isNegative(number) (*((signed char *)&(number))<0) -#else -# define uprv_isNegative(number) (*((signed char *)&(number)+sizeof(number)-1)<0) -#endif - -/** - * Return the largest positive number that can be represented by an integer - * type of arbitrary bit length. - * @internal - */ -U_CAPI double U_EXPORT2 uprv_maxMantissa(void); - -/** - * Floating point utility to calculate the logarithm of a double. - * @internal - */ -U_CAPI double U_EXPORT2 uprv_log(double d); - -/** - * Does common notion of rounding e.g. uprv_floor(x + 0.5); - * @param x the double number - * @return the rounded double - * @internal - */ -U_CAPI 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_CAPI 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_CAPI 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. - * - * @param x the double number - * @return the number of digits after the decimal point in a double number x. - * @internal - */ -/*U_CAPI int32_t U_EXPORT2 uprv_digitsAfterDecimal(double x);*/ -#endif - -#if !U_CHARSET_IS_UTF8 -/** - * Please use ucnv_getDefaultName() instead. - * Return the default codepage for this platform and locale. - * This function can call setlocale() on Unix platforms. Please read the - * platform documentation on setlocale() before calling this function. - * @return the default codepage for this platform - * @internal - */ -U_CAPI const char* U_EXPORT2 uprv_getDefaultCodepage(void); -#endif - -/** - * Please use uloc_getDefault() instead. - * Return the default locale ID string by querying the system, or - * zero if one cannot be found. - * This function can call setlocale() on Unix platforms. Please read the - * platform documentation on setlocale() before calling this function. - * @return the default locale ID string - * @internal - */ -U_CAPI const char* U_EXPORT2 uprv_getDefaultLocaleID(void); - -/** - * Time zone utilities - * - * Wrappers for C runtime library functions relating to timezones. - * The t_tzset() function (similar to tzset) uses the current setting - * of the environment variable TZ to assign values to three global - * variables: daylight, timezone, and tzname. These variables have the - * following meanings, and are declared in <time.h>. - * - * daylight Nonzero if daylight-saving-time zone (DST) is specified - * in TZ; otherwise, 0. Default value is 1. - * timezone Difference in seconds between coordinated universal - * time and local time. E.g., -28,800 for PST (GMT-8hrs) - * tzname(0) Three-letter time-zone name derived from TZ environment - * variable. E.g., "PST". - * tzname(1) Three-letter DST zone name derived from TZ environment - * variable. E.g., "PDT". If DST zone is omitted from TZ, - * tzname(1) is an empty string. - * - * Notes: For example, to set the TZ environment variable to correspond - * to the current time zone in Germany, you can use one of the - * following statements: - * - * set TZ=GST1GDT - * set TZ=GST+1GDT - * - * If the TZ value is not set, t_tzset() attempts to use the time zone - * information specified by the operating system. Under Windows NT - * and Windows 95, this information is specified in the Control Panel's - * Date/Time application. - * @internal - */ -U_CAPI void U_EXPORT2 uprv_tzset(void); - -/** - * Difference in seconds between coordinated universal - * time and local time. E.g., -28,800 for PST (GMT-8hrs) - * @return the difference in seconds between coordinated universal time and local time. - * @internal - */ -U_CAPI int32_t U_EXPORT2 uprv_timezone(void); - -/** - * tzname(0) Three-letter time-zone name derived from TZ environment - * variable. E.g., "PST". - * tzname(1) Three-letter DST zone name derived from TZ environment - * variable. E.g., "PDT". If DST zone is omitted from TZ, - * tzname(1) is an empty string. - * @internal - */ -U_CAPI const char* U_EXPORT2 uprv_tzname(int n); - -/** - * Reset the global tzname cache. - * @internal - */ -U_CAPI void uprv_tzname_clear_cache(void); - -/** - * Get UTC (GMT) time measured in milliseconds since 0:00 on 1/1/1970. - * This function is affected by 'faketime' and should be the bottleneck for all user-visible ICU time functions. - * @return the UTC time measured in milliseconds - * @internal - */ -U_CAPI UDate U_EXPORT2 uprv_getUTCtime(void); - -/** - * Get UTC (GMT) time measured in milliseconds since 0:00 on 1/1/1970. - * This function is not affected by 'faketime', so it should only be used by low level test functions- not by anything that - * exposes time to the end user. - * @return the UTC time measured in milliseconds - * @internal - */ -U_CAPI UDate U_EXPORT2 uprv_getRawUTCtime(void); - -/** - * Determine whether a pathname is absolute or not, as defined by the platform. - * @param path Pathname to test - * @return true if the path is absolute - * @internal (ICU 3.0) - */ -U_CAPI UBool U_EXPORT2 uprv_pathIsAbsolute(const char *path); - -/** - * Use U_MAX_PTR instead of this function. - * @param void pointer to test - * @return the largest possible pointer greater than the base - * @internal (ICU 3.8) - */ -U_CAPI void * U_EXPORT2 uprv_maximumPtr(void *base); - -/** - * Maximum value of a (void*) - use to indicate the limit of an 'infinite' buffer. - * In fact, buffer sizes must not exceed 2GB so that the difference between - * the buffer limit and the buffer start can be expressed in an int32_t. - * - * The definition of U_MAX_PTR must fulfill the following conditions: - * - return the largest possible pointer greater than base - * - return a valid pointer according to the machine architecture (AS/400, 64-bit, etc.) - * - avoid wrapping around at high addresses - * - make sure that the returned pointer is not farther from base than 0x7fffffff bytes - * - * @param base The beginning of a buffer to find the maximum offset from - * @internal - */ -#ifndef U_MAX_PTR -# if U_PLATFORM == U_PF_OS390 && !defined(_LP64) - /* We have 31-bit pointers. */ -# define U_MAX_PTR(base) ((void *)0x7fffffff) -# elif U_PLATFORM == U_PF_OS400 -# define U_MAX_PTR(base) uprv_maximumPtr((void *)base) -# elif 0 - /* - * For platforms where pointers are scalar values (which is normal, but unlike i5/OS) - * but that do not define uintptr_t. - * - * However, this does not work on modern compilers: - * The C++ standard does not define pointer overflow, and allows compilers to - * assume that p+u>p for any pointer p and any integer u>0. - * Thus, modern compilers optimize away the ">" comparison. - * (See ICU tickets #7187 and #8096.) - */ -# define U_MAX_PTR(base) \ - ((void *)(((char *)(base)+0x7fffffffu) > (char *)(base) \ - ? ((char *)(base)+0x7fffffffu) \ - : (char *)-1)) -# else - /* Default version. C++ standard compliant for scalar pointers. */ -# define U_MAX_PTR(base) \ - ((void *)(((uintptr_t)(base)+0x7fffffffu) > (uintptr_t)(base) \ - ? ((uintptr_t)(base)+0x7fffffffu) \ - : (uintptr_t)-1)) -# endif -#endif - - -#ifdef __cplusplus -/** - * Pin a buffer capacity such that doing pointer arithmetic - * on the destination pointer and capacity cannot overflow. - * - * The pinned capacity must fulfill the following conditions (for positive capacities): - * - dest + capacity is a valid pointer according to the machine architecture (AS/400, 64-bit, etc.) - * - (dest + capacity) >= dest - * - The size (in bytes) of T[capacity] does not exceed 0x7fffffff - * - * @param dest the destination buffer pointer. - * @param capacity the requested buffer capacity, in units of type T. - * @return the pinned capacity. - * @internal - */ -template -inline int32_t pinCapacity(T *dest, int32_t capacity) { - if (capacity <= 0) { return capacity; } - - uintptr_t destInt = (uintptr_t)dest; - uintptr_t maxInt; - -# if U_PLATFORM == U_PF_OS390 && !defined(_LP64) - // We have 31-bit pointers. - maxInt = 0x7fffffff; -# elif U_PLATFORM == U_PF_OS400 - maxInt = (uintptr_t)uprv_maximumPtr((void *)dest); -# else - maxInt = destInt + 0x7fffffffu; - if (maxInt < destInt) { - // Less than 2GB to the end of the address space. - // Pin to that to prevent address overflow. - maxInt = (uintptr_t)-1; - } -# endif - - uintptr_t maxBytes = maxInt - destInt; // max. 2GB - int32_t maxCapacity = (int32_t)(maxBytes / sizeof(T)); - return capacity <= maxCapacity ? capacity : maxCapacity; -} -#endif // __cplusplus - -/* Dynamic Library Functions */ - -typedef void (UVoidFunction)(void); - -#if U_ENABLE_DYLOAD -/** - * Load a library - * @internal (ICU 4.4) - */ -U_CAPI void * U_EXPORT2 uprv_dl_open(const char *libName, UErrorCode *status); - -/** - * Close a library - * @internal (ICU 4.4) - */ -U_CAPI void U_EXPORT2 uprv_dl_close( void *lib, UErrorCode *status); - -/** - * Extract a symbol from a library (function) - * @internal (ICU 4.8) - */ -U_CAPI UVoidFunction* U_EXPORT2 uprv_dlsym_func( void *lib, const char *symbolName, UErrorCode *status); - -/** - * Extract a symbol from a library (function) - * Not implemented, no clients. - * @internal - */ -/* U_CAPI void * U_EXPORT2 uprv_dlsym_data( void *lib, const char *symbolName, UErrorCode *status); */ - -#endif - -/** - * Define malloc and related functions - * @internal - */ -#if U_PLATFORM == U_PF_OS400 -# define uprv_default_malloc(x) _C_TS_malloc(x) -# define uprv_default_realloc(x,y) _C_TS_realloc(x,y) -# define uprv_default_free(x) _C_TS_free(x) -/* also _C_TS_calloc(x) */ -#else -/* C defaults */ -# define uprv_default_malloc(x) malloc(x) -# define uprv_default_realloc(x,y) realloc(x,y) -# define uprv_default_free(x) free(x) -#endif - - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1997-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* FILE NAME : putilimp.h +* +* Date Name Description +* 10/17/04 grhoten Move internal functions from putil.h to this file. +****************************************************************************** +*/ + +#ifndef PUTILIMP_H +#define PUTILIMP_H + +#include "unicode/utypes.h" +#include "unicode/putil.h" + +/** + * \def U_SIGNED_RIGHT_SHIFT_IS_ARITHMETIC + * Nearly all CPUs and compilers implement a right-shift of a signed integer + * as an Arithmetic Shift Right which copies the sign bit (the Most Significant Bit (MSB)) + * into the vacated bits (sign extension). + * For example, (int32_t)0xfff5fff3>>4 becomes 0xffff5fff and -1>>1=-1. + * + * This can be useful for storing a signed value in the upper bits + * and another bit field in the lower bits. + * The signed value can be retrieved by simple right-shifting. + * + * This is consistent with the Java language. + * + * However, the C standard allows compilers to implement a right-shift of a signed integer + * as a Logical Shift Right which copies a 0 into the vacated bits. + * For example, (int32_t)0xfff5fff3>>4 becomes 0x0fff5fff and -1>>1=0x7fffffff. + * + * Code that depends on the natural behavior should be guarded with this macro, + * with an alternate path for unusual platforms. + * @internal + */ +#ifdef U_SIGNED_RIGHT_SHIFT_IS_ARITHMETIC + /* Use the predefined value. */ +#else + /* + * Nearly all CPUs & compilers implement a right-shift of a signed integer + * as an Arithmetic Shift Right (with sign extension). + */ +# define U_SIGNED_RIGHT_SHIFT_IS_ARITHMETIC 1 +#endif + +/** Define this to 1 if your platform supports IEEE 754 floating point, + to 0 if it does not. */ +#ifndef IEEE_754 +# define IEEE_754 1 +#endif + +/** + * uintptr_t is an optional part of the standard definitions in stdint.h. + * The opengroup.org documentation for stdint.h says + * "On XSI-conformant systems, the intptr_t and uintptr_t types are required; + * otherwise, they are optional." + * We assume that when uintptr_t is defined, UINTPTR_MAX is defined as well. + * + * Do not use ptrdiff_t since it is signed. size_t is unsigned. + */ +/* TODO: This check fails on some z environments. Filed a ticket #9357 for this. */ +#if !defined(__intptr_t_defined) && !defined(UINTPTR_MAX) && (U_PLATFORM != U_PF_OS390) +typedef size_t uintptr_t; +#endif + +/*===========================================================================*/ +/** @{ Information about POSIX support */ +/*===========================================================================*/ + +#ifdef U_HAVE_NL_LANGINFO_CODESET + /* Use the predefined value. */ +#elif U_PLATFORM_USES_ONLY_WIN32_API || U_PLATFORM == U_PF_ANDROID || U_PLATFORM == U_PF_QNX +# define U_HAVE_NL_LANGINFO_CODESET 0 +#else +# define U_HAVE_NL_LANGINFO_CODESET 1 +#endif + +#ifdef U_NL_LANGINFO_CODESET + /* Use the predefined value. */ +#elif !U_HAVE_NL_LANGINFO_CODESET +# define U_NL_LANGINFO_CODESET -1 +#elif U_PLATFORM == U_PF_OS400 + /* not defined */ +#else +# define U_NL_LANGINFO_CODESET CODESET +#endif + +#if defined(U_TZSET) || defined(U_HAVE_TZSET) + /* Use the predefined value. */ +#elif U_PLATFORM_USES_ONLY_WIN32_API + // UWP doesn't support tzset or environment variables for tz +#if U_PLATFORM_HAS_WINUWP_API == 0 +# define U_TZSET _tzset +#endif +#elif U_PLATFORM == U_PF_OS400 + /* not defined */ +#else +# define U_TZSET tzset +#endif + +#if defined(U_TIMEZONE) || defined(U_HAVE_TIMEZONE) + /* Use the predefined value. */ +#elif U_PLATFORM == U_PF_ANDROID +# define U_TIMEZONE timezone +#elif defined(__UCLIBC__) + // uClibc does not have __timezone or _timezone. +#elif defined(_NEWLIB_VERSION) +# define U_TIMEZONE _timezone +#elif defined(__GLIBC__) + // glibc +# define U_TIMEZONE __timezone +#elif U_PLATFORM_IS_LINUX_BASED + // not defined +#elif U_PLATFORM_USES_ONLY_WIN32_API +# define U_TIMEZONE _timezone +#elif U_PLATFORM == U_PF_BSD && !defined(__NetBSD__) + /* not defined */ +#elif U_PLATFORM == U_PF_OS400 + /* not defined */ +#elif U_PLATFORM == U_PF_IPHONE + /* not defined */ +#else +# define U_TIMEZONE timezone +#endif + +#if defined(U_TZNAME) || defined(U_HAVE_TZNAME) + /* Use the predefined value. */ +#elif U_PLATFORM_USES_ONLY_WIN32_API + /* not usable on all windows platforms */ +#if U_PLATFORM_HAS_WINUWP_API == 0 +# define U_TZNAME _tzname +#endif +#elif U_PLATFORM == U_PF_OS400 + /* not defined */ +#else +# define U_TZNAME tzname +#endif + +#ifdef U_HAVE_MMAP + /* Use the predefined value. */ +#elif U_PLATFORM_USES_ONLY_WIN32_API +# define U_HAVE_MMAP 0 +#else +# define U_HAVE_MMAP 1 +#endif + +#ifdef U_HAVE_POPEN + /* Use the predefined value. */ +#elif U_PLATFORM_USES_ONLY_WIN32_API +# define U_HAVE_POPEN 0 +#elif U_PLATFORM == U_PF_OS400 +# define U_HAVE_POPEN 0 +#else +# define U_HAVE_POPEN 1 +#endif + +/** + * \def U_HAVE_DIRENT_H + * Defines whether dirent.h is available. + * @internal + */ +#ifdef U_HAVE_DIRENT_H + /* Use the predefined value. */ +#elif U_PLATFORM_USES_ONLY_WIN32_API +# define U_HAVE_DIRENT_H 0 +#else +# define U_HAVE_DIRENT_H 1 +#endif + +/** @} */ + +/*===========================================================================*/ +/** @{ Programs used by ICU code */ +/*===========================================================================*/ + +/** + * \def U_MAKE_IS_NMAKE + * Defines whether the "make" program is Windows nmake. + */ +#ifdef U_MAKE_IS_NMAKE + /* Use the predefined value. */ +#elif U_PLATFORM == U_PF_WINDOWS +# define U_MAKE_IS_NMAKE 1 +#else +# define U_MAKE_IS_NMAKE 0 +#endif + +/** @} */ + +/*==========================================================================*/ +/* Platform utilities */ +/*==========================================================================*/ + +/** + * Platform utilities isolates the platform dependencies of the + * library. For each platform which this code is ported to, these + * functions may have to be re-implemented. + */ + +/** + * Floating point utility to determine if a double is Not a Number (NaN). + * @internal + */ +U_CAPI UBool U_EXPORT2 uprv_isNaN(double d); +/** + * Floating point utility to determine if a double has an infinite value. + * @internal + */ +U_CAPI UBool U_EXPORT2 uprv_isInfinite(double d); +/** + * Floating point utility to determine if a double has a positive infinite value. + * @internal + */ +U_CAPI UBool U_EXPORT2 uprv_isPositiveInfinity(double d); +/** + * Floating point utility to determine if a double has a negative infinite value. + * @internal + */ +U_CAPI UBool U_EXPORT2 uprv_isNegativeInfinity(double d); +/** + * Floating point utility that returns a Not a Number (NaN) value. + * @internal + */ +U_CAPI double U_EXPORT2 uprv_getNaN(void); +/** + * Floating point utility that returns an infinite value. + * @internal + */ +U_CAPI double U_EXPORT2 uprv_getInfinity(void); + +/** + * Floating point utility to truncate a double. + * @internal + */ +U_CAPI double U_EXPORT2 uprv_trunc(double d); +/** + * Floating point utility to calculate the floor of a double. + * @internal + */ +U_CAPI double U_EXPORT2 uprv_floor(double d); +/** + * Floating point utility to calculate the ceiling of a double. + * @internal + */ +U_CAPI double U_EXPORT2 uprv_ceil(double d); +/** + * Floating point utility to calculate the absolute value of a double. + * @internal + */ +U_CAPI double U_EXPORT2 uprv_fabs(double d); +/** + * Floating point utility to calculate the fractional and integer parts of a double. + * @internal + */ +U_CAPI double U_EXPORT2 uprv_modf(double d, double* pinteger); +/** + * Floating point utility to calculate the remainder of a double divided by another double. + * @internal + */ +U_CAPI double U_EXPORT2 uprv_fmod(double d, double y); +/** + * Floating point utility to calculate d to the power of exponent (d^exponent). + * @internal + */ +U_CAPI double U_EXPORT2 uprv_pow(double d, double exponent); +/** + * Floating point utility to calculate 10 to the power of exponent (10^exponent). + * @internal + */ +U_CAPI double U_EXPORT2 uprv_pow10(int32_t exponent); +/** + * Floating point utility to calculate the maximum value of two doubles. + * @internal + */ +U_CAPI double U_EXPORT2 uprv_fmax(double d, double y); +/** + * Floating point utility to calculate the minimum value of two doubles. + * @internal + */ +U_CAPI double U_EXPORT2 uprv_fmin(double d, double y); +/** + * Private utility to calculate the maximum value of two integers. + * @internal + */ +U_CAPI int32_t U_EXPORT2 uprv_max(int32_t d, int32_t y); +/** + * Private utility to calculate the minimum value of two integers. + * @internal + */ +U_CAPI int32_t U_EXPORT2 uprv_min(int32_t d, int32_t y); + +#if U_IS_BIG_ENDIAN +# define uprv_isNegative(number) (*((signed char *)&(number))<0) +#else +# define uprv_isNegative(number) (*((signed char *)&(number)+sizeof(number)-1)<0) +#endif + +/** + * Return the largest positive number that can be represented by an integer + * type of arbitrary bit length. + * @internal + */ +U_CAPI double U_EXPORT2 uprv_maxMantissa(void); + +/** + * Floating point utility to calculate the logarithm of a double. + * @internal + */ +U_CAPI double U_EXPORT2 uprv_log(double d); + +/** + * Does common notion of rounding e.g. uprv_floor(x + 0.5); + * @param x the double number + * @return the rounded double + * @internal + */ +U_CAPI 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_CAPI 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_CAPI 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. + * + * @param x the double number + * @return the number of digits after the decimal point in a double number x. + * @internal + */ +/*U_CAPI int32_t U_EXPORT2 uprv_digitsAfterDecimal(double x);*/ +#endif + +#if !U_CHARSET_IS_UTF8 +/** + * Please use ucnv_getDefaultName() instead. + * Return the default codepage for this platform and locale. + * This function can call setlocale() on Unix platforms. Please read the + * platform documentation on setlocale() before calling this function. + * @return the default codepage for this platform + * @internal + */ +U_CAPI const char* U_EXPORT2 uprv_getDefaultCodepage(void); +#endif + +/** + * Please use uloc_getDefault() instead. + * Return the default locale ID string by querying the system, or + * zero if one cannot be found. + * This function can call setlocale() on Unix platforms. Please read the + * platform documentation on setlocale() before calling this function. + * @return the default locale ID string + * @internal + */ +U_CAPI const char* U_EXPORT2 uprv_getDefaultLocaleID(void); + +/** + * Time zone utilities + * + * Wrappers for C runtime library functions relating to timezones. + * The t_tzset() function (similar to tzset) uses the current setting + * of the environment variable TZ to assign values to three global + * variables: daylight, timezone, and tzname. These variables have the + * following meanings, and are declared in <time.h>. + * + * daylight Nonzero if daylight-saving-time zone (DST) is specified + * in TZ; otherwise, 0. Default value is 1. + * timezone Difference in seconds between coordinated universal + * time and local time. E.g., -28,800 for PST (GMT-8hrs) + * tzname(0) Three-letter time-zone name derived from TZ environment + * variable. E.g., "PST". + * tzname(1) Three-letter DST zone name derived from TZ environment + * variable. E.g., "PDT". If DST zone is omitted from TZ, + * tzname(1) is an empty string. + * + * Notes: For example, to set the TZ environment variable to correspond + * to the current time zone in Germany, you can use one of the + * following statements: + * + * set TZ=GST1GDT + * set TZ=GST+1GDT + * + * If the TZ value is not set, t_tzset() attempts to use the time zone + * information specified by the operating system. Under Windows NT + * and Windows 95, this information is specified in the Control Panel's + * Date/Time application. + * @internal + */ +U_CAPI void U_EXPORT2 uprv_tzset(void); + +/** + * Difference in seconds between coordinated universal + * time and local time. E.g., -28,800 for PST (GMT-8hrs) + * @return the difference in seconds between coordinated universal time and local time. + * @internal + */ +U_CAPI int32_t U_EXPORT2 uprv_timezone(void); + +/** + * tzname(0) Three-letter time-zone name derived from TZ environment + * variable. E.g., "PST". + * tzname(1) Three-letter DST zone name derived from TZ environment + * variable. E.g., "PDT". If DST zone is omitted from TZ, + * tzname(1) is an empty string. + * @internal + */ +U_CAPI const char* U_EXPORT2 uprv_tzname(int n); + +/** + * Reset the global tzname cache. + * @internal + */ +U_CAPI void uprv_tzname_clear_cache(void); + +/** + * Get UTC (GMT) time measured in milliseconds since 0:00 on 1/1/1970. + * This function is affected by 'faketime' and should be the bottleneck for all user-visible ICU time functions. + * @return the UTC time measured in milliseconds + * @internal + */ +U_CAPI UDate U_EXPORT2 uprv_getUTCtime(void); + +/** + * Get UTC (GMT) time measured in milliseconds since 0:00 on 1/1/1970. + * This function is not affected by 'faketime', so it should only be used by low level test functions- not by anything that + * exposes time to the end user. + * @return the UTC time measured in milliseconds + * @internal + */ +U_CAPI UDate U_EXPORT2 uprv_getRawUTCtime(void); + +/** + * Determine whether a pathname is absolute or not, as defined by the platform. + * @param path Pathname to test + * @return true if the path is absolute + * @internal (ICU 3.0) + */ +U_CAPI UBool U_EXPORT2 uprv_pathIsAbsolute(const char *path); + +/** + * Use U_MAX_PTR instead of this function. + * @param void pointer to test + * @return the largest possible pointer greater than the base + * @internal (ICU 3.8) + */ +U_CAPI void * U_EXPORT2 uprv_maximumPtr(void *base); + +/** + * Maximum value of a (void*) - use to indicate the limit of an 'infinite' buffer. + * In fact, buffer sizes must not exceed 2GB so that the difference between + * the buffer limit and the buffer start can be expressed in an int32_t. + * + * The definition of U_MAX_PTR must fulfill the following conditions: + * - return the largest possible pointer greater than base + * - return a valid pointer according to the machine architecture (AS/400, 64-bit, etc.) + * - avoid wrapping around at high addresses + * - make sure that the returned pointer is not farther from base than 0x7fffffff bytes + * + * @param base The beginning of a buffer to find the maximum offset from + * @internal + */ +#ifndef U_MAX_PTR +# if U_PLATFORM == U_PF_OS390 && !defined(_LP64) + /* We have 31-bit pointers. */ +# define U_MAX_PTR(base) ((void *)0x7fffffff) +# elif U_PLATFORM == U_PF_OS400 +# define U_MAX_PTR(base) uprv_maximumPtr((void *)base) +# elif 0 + /* + * For platforms where pointers are scalar values (which is normal, but unlike i5/OS) + * but that do not define uintptr_t. + * + * However, this does not work on modern compilers: + * The C++ standard does not define pointer overflow, and allows compilers to + * assume that p+u>p for any pointer p and any integer u>0. + * Thus, modern compilers optimize away the ">" comparison. + * (See ICU tickets #7187 and #8096.) + */ +# define U_MAX_PTR(base) \ + ((void *)(((char *)(base)+0x7fffffffu) > (char *)(base) \ + ? ((char *)(base)+0x7fffffffu) \ + : (char *)-1)) +# else + /* Default version. C++ standard compliant for scalar pointers. */ +# define U_MAX_PTR(base) \ + ((void *)(((uintptr_t)(base)+0x7fffffffu) > (uintptr_t)(base) \ + ? ((uintptr_t)(base)+0x7fffffffu) \ + : (uintptr_t)-1)) +# endif +#endif + + +#ifdef __cplusplus +/** + * Pin a buffer capacity such that doing pointer arithmetic + * on the destination pointer and capacity cannot overflow. + * + * The pinned capacity must fulfill the following conditions (for positive capacities): + * - dest + capacity is a valid pointer according to the machine architecture (AS/400, 64-bit, etc.) + * - (dest + capacity) >= dest + * - The size (in bytes) of T[capacity] does not exceed 0x7fffffff + * + * @param dest the destination buffer pointer. + * @param capacity the requested buffer capacity, in units of type T. + * @return the pinned capacity. + * @internal + */ +template +inline int32_t pinCapacity(T *dest, int32_t capacity) { + if (capacity <= 0) { return capacity; } + + uintptr_t destInt = (uintptr_t)dest; + uintptr_t maxInt; + +# if U_PLATFORM == U_PF_OS390 && !defined(_LP64) + // We have 31-bit pointers. + maxInt = 0x7fffffff; +# elif U_PLATFORM == U_PF_OS400 + maxInt = (uintptr_t)uprv_maximumPtr((void *)dest); +# else + maxInt = destInt + 0x7fffffffu; + if (maxInt < destInt) { + // Less than 2GB to the end of the address space. + // Pin to that to prevent address overflow. + maxInt = (uintptr_t)-1; + } +# endif + + uintptr_t maxBytes = maxInt - destInt; // max. 2GB + int32_t maxCapacity = (int32_t)(maxBytes / sizeof(T)); + return capacity <= maxCapacity ? capacity : maxCapacity; +} +#endif // __cplusplus + +/* Dynamic Library Functions */ + +typedef void (UVoidFunction)(void); + +#if U_ENABLE_DYLOAD +/** + * Load a library + * @internal (ICU 4.4) + */ +U_CAPI void * U_EXPORT2 uprv_dl_open(const char *libName, UErrorCode *status); + +/** + * Close a library + * @internal (ICU 4.4) + */ +U_CAPI void U_EXPORT2 uprv_dl_close( void *lib, UErrorCode *status); + +/** + * Extract a symbol from a library (function) + * @internal (ICU 4.8) + */ +U_CAPI UVoidFunction* U_EXPORT2 uprv_dlsym_func( void *lib, const char *symbolName, UErrorCode *status); + +/** + * Extract a symbol from a library (function) + * Not implemented, no clients. + * @internal + */ +/* U_CAPI void * U_EXPORT2 uprv_dlsym_data( void *lib, const char *symbolName, UErrorCode *status); */ + +#endif + +/** + * Define malloc and related functions + * @internal + */ +#if U_PLATFORM == U_PF_OS400 +# define uprv_default_malloc(x) _C_TS_malloc(x) +# define uprv_default_realloc(x,y) _C_TS_realloc(x,y) +# define uprv_default_free(x) _C_TS_free(x) +/* also _C_TS_calloc(x) */ +#else +/* C defaults */ +# define uprv_default_malloc(x) malloc(x) +# define uprv_default_realloc(x,y) realloc(x,y) +# define uprv_default_free(x) free(x) +#endif + + +#endif diff --git a/deps/icu-small/source/common/rbbi.cpp b/deps/icu-small/source/common/rbbi.cpp index 2769263894b7bd..7f071ad692fd49 100644 --- a/deps/icu-small/source/common/rbbi.cpp +++ b/deps/icu-small/source/common/rbbi.cpp @@ -1,1316 +1,1286 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -*************************************************************************** -* Copyright (C) 1999-2016 International Business Machines Corporation -* and others. All rights reserved. -*************************************************************************** -*/ -// -// file: rbbi.cpp Contains the implementation of the rule based break iterator -// runtime engine and the API implementation for -// class RuleBasedBreakIterator -// - -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include - -#include "unicode/rbbi.h" -#include "unicode/schriter.h" -#include "unicode/uchriter.h" -#include "unicode/uclean.h" -#include "unicode/udata.h" - -#include "brkeng.h" -#include "ucln_cmn.h" -#include "cmemory.h" -#include "cstring.h" -#include "localsvc.h" -#include "rbbidata.h" -#include "rbbi_cache.h" -#include "rbbirb.h" -#include "uassert.h" -#include "umutex.h" -#include "uvectr32.h" - -#ifdef RBBI_DEBUG -static UBool gTrace = false; -#endif - -U_NAMESPACE_BEGIN - -// The state number of the starting state -constexpr int32_t START_STATE = 1; - -// The state-transition value indicating "stop" -constexpr int32_t STOP_STATE = 0; - - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedBreakIterator) - - -//======================================================================= -// constructors -//======================================================================= - -/** - * Constructs a RuleBasedBreakIterator that uses the already-created - * tables object that is passed in as a parameter. - */ -RuleBasedBreakIterator::RuleBasedBreakIterator(RBBIDataHeader* data, UErrorCode &status) - : fSCharIter(UnicodeString()) -{ - init(status); - fData = new RBBIDataWrapper(data, status); // status checked in constructor - if (U_FAILURE(status)) {return;} - if(fData == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (fData->fForwardTable->fLookAheadResultsSize > 0) { - fLookAheadMatches = static_cast( - uprv_malloc(fData->fForwardTable->fLookAheadResultsSize * sizeof(int32_t))); - if (fLookAheadMatches == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } -} - -//------------------------------------------------------------------------------- -// -// Constructor from a UDataMemory handle to precompiled break rules -// stored in an ICU data file. This construcotr is private API, -// only for internal use. -// -//------------------------------------------------------------------------------- -RuleBasedBreakIterator::RuleBasedBreakIterator(UDataMemory* udm, UBool isPhraseBreaking, - UErrorCode &status) : RuleBasedBreakIterator(udm, status) -{ - fIsPhraseBreaking = isPhraseBreaking; -} - -// -// Construct from precompiled binary rules (tables). This constructor is public API, -// taking the rules as a (const uint8_t *) to match the type produced by getBinaryRules(). -// -RuleBasedBreakIterator::RuleBasedBreakIterator(const uint8_t *compiledRules, - uint32_t ruleLength, - UErrorCode &status) - : fSCharIter(UnicodeString()) -{ - init(status); - if (U_FAILURE(status)) { - return; - } - if (compiledRules == NULL || ruleLength < sizeof(RBBIDataHeader)) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - const RBBIDataHeader *data = (const RBBIDataHeader *)compiledRules; - if (data->fLength > ruleLength) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - fData = new RBBIDataWrapper(data, RBBIDataWrapper::kDontAdopt, status); - if (U_FAILURE(status)) {return;} - if(fData == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (fData->fForwardTable->fLookAheadResultsSize > 0) { - fLookAheadMatches = static_cast( - uprv_malloc(fData->fForwardTable->fLookAheadResultsSize * sizeof(int32_t))); - if (fLookAheadMatches == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } -} - - -//------------------------------------------------------------------------------- -// -// Constructor from a UDataMemory handle to precompiled break rules -// stored in an ICU data file. -// -//------------------------------------------------------------------------------- -RuleBasedBreakIterator::RuleBasedBreakIterator(UDataMemory* udm, UErrorCode &status) - : fSCharIter(UnicodeString()) -{ - init(status); - fData = new RBBIDataWrapper(udm, status); // status checked in constructor - if (U_FAILURE(status)) {return;} - if(fData == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (fData->fForwardTable->fLookAheadResultsSize > 0) { - fLookAheadMatches = static_cast( - uprv_malloc(fData->fForwardTable->fLookAheadResultsSize * sizeof(int32_t))); - if (fLookAheadMatches == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } -} - - - -//------------------------------------------------------------------------------- -// -// Constructor from a set of rules supplied as a string. -// -//------------------------------------------------------------------------------- -RuleBasedBreakIterator::RuleBasedBreakIterator( const UnicodeString &rules, - UParseError &parseError, - UErrorCode &status) - : fSCharIter(UnicodeString()) -{ - init(status); - if (U_FAILURE(status)) {return;} - RuleBasedBreakIterator *bi = (RuleBasedBreakIterator *) - RBBIRuleBuilder::createRuleBasedBreakIterator(rules, &parseError, status); - // Note: This is a bit awkward. The RBBI ruleBuilder has a factory method that - // creates and returns a complete RBBI. From here, in a constructor, we - // can't just return the object created by the builder factory, hence - // the assignment of the factory created object to "this". - if (U_SUCCESS(status)) { - *this = *bi; - delete bi; - } -} - - -//------------------------------------------------------------------------------- -// -// Default Constructor. Create an empty shell that can be set up later. -// Used when creating a RuleBasedBreakIterator from a set -// of rules. -//------------------------------------------------------------------------------- -RuleBasedBreakIterator::RuleBasedBreakIterator() - : fSCharIter(UnicodeString()) -{ - UErrorCode status = U_ZERO_ERROR; - init(status); -} - - -//------------------------------------------------------------------------------- -// -// Copy constructor. Will produce a break iterator with the same behavior, -// and which iterates over the same text, as the one passed in. -// -//------------------------------------------------------------------------------- -RuleBasedBreakIterator::RuleBasedBreakIterator(const RuleBasedBreakIterator& other) -: BreakIterator(other), - fSCharIter(UnicodeString()) -{ - UErrorCode status = U_ZERO_ERROR; - this->init(status); - *this = other; -} - - -/** - * Destructor - */ -RuleBasedBreakIterator::~RuleBasedBreakIterator() { - if (fCharIter != &fSCharIter) { - // fCharIter was adopted from the outside. - delete fCharIter; - } - fCharIter = nullptr; - - utext_close(&fText); - - if (fData != nullptr) { - fData->removeReference(); - fData = nullptr; - } - delete fBreakCache; - fBreakCache = nullptr; - - delete fDictionaryCache; - fDictionaryCache = nullptr; - - delete fLanguageBreakEngines; - fLanguageBreakEngines = nullptr; - - delete fUnhandledBreakEngine; - fUnhandledBreakEngine = nullptr; - - uprv_free(fLookAheadMatches); - fLookAheadMatches = nullptr; -} - -/** - * Assignment operator. Sets this iterator to have the same behavior, - * and iterate over the same text, as the one passed in. - * TODO: needs better handling of memory allocation errors. - */ -RuleBasedBreakIterator& -RuleBasedBreakIterator::operator=(const RuleBasedBreakIterator& that) { - if (this == &that) { - return *this; - } - BreakIterator::operator=(that); - - if (fLanguageBreakEngines != NULL) { - delete fLanguageBreakEngines; - fLanguageBreakEngines = NULL; // Just rebuild for now - } - // TODO: clone fLanguageBreakEngines from "that" - UErrorCode status = U_ZERO_ERROR; - utext_clone(&fText, &that.fText, false, true, &status); - - if (fCharIter != &fSCharIter) { - delete fCharIter; - } - fCharIter = &fSCharIter; - - if (that.fCharIter != NULL && that.fCharIter != &that.fSCharIter) { - // This is a little bit tricky - it will initially appear that - // this->fCharIter is adopted, even if that->fCharIter was - // not adopted. That's ok. - fCharIter = that.fCharIter->clone(); - } - fSCharIter = that.fSCharIter; - if (fCharIter == NULL) { - fCharIter = &fSCharIter; - } - - if (fData != NULL) { - fData->removeReference(); - fData = NULL; - } - if (that.fData != NULL) { - fData = that.fData->addReference(); - } - - uprv_free(fLookAheadMatches); - fLookAheadMatches = nullptr; - if (fData && fData->fForwardTable->fLookAheadResultsSize > 0) { - fLookAheadMatches = static_cast( - uprv_malloc(fData->fForwardTable->fLookAheadResultsSize * sizeof(int32_t))); - } - - - fPosition = that.fPosition; - fRuleStatusIndex = that.fRuleStatusIndex; - fDone = that.fDone; - - // TODO: both the dictionary and the main cache need to be copied. - // Current position could be within a dictionary range. Trying to continue - // the iteration without the caches present would go to the rules, with - // the assumption that the current position is on a rule boundary. - fBreakCache->reset(fPosition, fRuleStatusIndex); - fDictionaryCache->reset(); - - return *this; -} - - - -//----------------------------------------------------------------------------- -// -// init() Shared initialization routine. Used by all the constructors. -// Initializes all fields, leaving the object in a consistent state. -// -//----------------------------------------------------------------------------- -void RuleBasedBreakIterator::init(UErrorCode &status) { - fCharIter = nullptr; - fData = nullptr; - fPosition = 0; - fRuleStatusIndex = 0; - fDone = false; - fDictionaryCharCount = 0; - fLanguageBreakEngines = nullptr; - fUnhandledBreakEngine = nullptr; - fBreakCache = nullptr; - fDictionaryCache = nullptr; - fLookAheadMatches = nullptr; - fIsPhraseBreaking = false; - - // Note: IBM xlC is unable to assign or initialize member fText from UTEXT_INITIALIZER. - // fText = UTEXT_INITIALIZER; - static const UText initializedUText = UTEXT_INITIALIZER; - uprv_memcpy(&fText, &initializedUText, sizeof(UText)); - - if (U_FAILURE(status)) { - return; - } - - utext_openUChars(&fText, NULL, 0, &status); - fDictionaryCache = new DictionaryCache(this, status); - fBreakCache = new BreakCache(this, status); - if (U_SUCCESS(status) && (fDictionaryCache == NULL || fBreakCache == NULL)) { - status = U_MEMORY_ALLOCATION_ERROR; - } - -#ifdef RBBI_DEBUG - static UBool debugInitDone = false; - if (debugInitDone == false) { - char *debugEnv = getenv("U_RBBIDEBUG"); - if (debugEnv && uprv_strstr(debugEnv, "trace")) { - gTrace = true; - } - debugInitDone = true; - } -#endif -} - - - -//----------------------------------------------------------------------------- -// -// clone - Returns a newly-constructed RuleBasedBreakIterator with the same -// behavior, and iterating over the same text, as this one. -// Virtual function: does the right thing with subclasses. -// -//----------------------------------------------------------------------------- -RuleBasedBreakIterator* -RuleBasedBreakIterator::clone() const { - return new RuleBasedBreakIterator(*this); -} - -/** - * Equality operator. Returns true if both BreakIterators are of the - * same class, have the same behavior, and iterate over the same text. - */ -bool -RuleBasedBreakIterator::operator==(const BreakIterator& that) const { - if (typeid(*this) != typeid(that)) { - return false; - } - if (this == &that) { - return true; - } - - // The base class BreakIterator carries no state that participates in equality, - // and does not implement an equality function that would otherwise be - // checked at this point. - - const RuleBasedBreakIterator& that2 = (const RuleBasedBreakIterator&) that; - - if (!utext_equals(&fText, &that2.fText)) { - // The two break iterators are operating on different text, - // or have a different iteration position. - // Note that fText's position is always the same as the break iterator's position. - return false; - } - - if (!(fPosition == that2.fPosition && - fRuleStatusIndex == that2.fRuleStatusIndex && - fDone == that2.fDone)) { - return false; - } - - if (that2.fData == fData || - (fData != NULL && that2.fData != NULL && *that2.fData == *fData)) { - // The two break iterators are using the same rules. - return true; - } - return false; -} - -/** - * Compute a hash code for this BreakIterator - * @return A hash code - */ -int32_t -RuleBasedBreakIterator::hashCode(void) const { - int32_t hash = 0; - if (fData != NULL) { - hash = fData->hashCode(); - } - return hash; -} - - -void RuleBasedBreakIterator::setText(UText *ut, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - fBreakCache->reset(); - fDictionaryCache->reset(); - utext_clone(&fText, ut, false, true, &status); - - // Set up a dummy CharacterIterator to be returned if anyone - // calls getText(). With input from UText, there is no reasonable - // way to return a characterIterator over the actual input text. - // Return one over an empty string instead - this is the closest - // we can come to signaling a failure. - // (GetText() is obsolete, this failure is sort of OK) - fSCharIter.setText(UnicodeString()); - - if (fCharIter != &fSCharIter) { - // existing fCharIter was adopted from the outside. Delete it now. - delete fCharIter; - } - fCharIter = &fSCharIter; - - this->first(); -} - - -UText *RuleBasedBreakIterator::getUText(UText *fillIn, UErrorCode &status) const { - UText *result = utext_clone(fillIn, &fText, false, true, &status); - return result; -} - - -//======================================================================= -// BreakIterator overrides -//======================================================================= - -/** - * Return a CharacterIterator over the text being analyzed. - */ -CharacterIterator& -RuleBasedBreakIterator::getText() const { - return *fCharIter; -} - -/** - * Set the iterator to analyze a new piece of text. This function resets - * the current iteration position to the beginning of the text. - * @param newText An iterator over the text to analyze. - */ -void -RuleBasedBreakIterator::adoptText(CharacterIterator* newText) { - // If we are holding a CharacterIterator adopted from a - // previous call to this function, delete it now. - if (fCharIter != &fSCharIter) { - delete fCharIter; - } - - fCharIter = newText; - UErrorCode status = U_ZERO_ERROR; - fBreakCache->reset(); - fDictionaryCache->reset(); - if (newText==NULL || newText->startIndex() != 0) { - // startIndex !=0 wants to be an error, but there's no way to report it. - // Make the iterator text be an empty string. - utext_openUChars(&fText, NULL, 0, &status); - } else { - utext_openCharacterIterator(&fText, newText, &status); - } - this->first(); -} - -/** - * Set the iterator to analyze a new piece of text. This function resets - * the current iteration position to the beginning of the text. - * @param newText An iterator over the text to analyze. - */ -void -RuleBasedBreakIterator::setText(const UnicodeString& newText) { - UErrorCode status = U_ZERO_ERROR; - fBreakCache->reset(); - fDictionaryCache->reset(); - utext_openConstUnicodeString(&fText, &newText, &status); - - // Set up a character iterator on the string. - // Needed in case someone calls getText(). - // Can not, unfortunately, do this lazily on the (probably never) - // call to getText(), because getText is const. - fSCharIter.setText(newText); - - if (fCharIter != &fSCharIter) { - // old fCharIter was adopted from the outside. Delete it. - delete fCharIter; - } - fCharIter = &fSCharIter; - - this->first(); -} - - -/** - * Provide a new UText for the input text. Must reference text with contents identical - * to the original. - * Intended for use with text data originating in Java (garbage collected) environments - * where the data may be moved in memory at arbitrary times. - */ -RuleBasedBreakIterator &RuleBasedBreakIterator::refreshInputText(UText *input, UErrorCode &status) { - if (U_FAILURE(status)) { - return *this; - } - if (input == NULL) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return *this; - } - int64_t pos = utext_getNativeIndex(&fText); - // Shallow read-only clone of the new UText into the existing input UText - utext_clone(&fText, input, false, true, &status); - if (U_FAILURE(status)) { - return *this; - } - utext_setNativeIndex(&fText, pos); - if (utext_getNativeIndex(&fText) != pos) { - // Sanity check. The new input utext is supposed to have the exact same - // contents as the old. If we can't set to the same position, it doesn't. - // The contents underlying the old utext might be invalid at this point, - // so it's not safe to check directly. - status = U_ILLEGAL_ARGUMENT_ERROR; - } - return *this; -} - - -/** - * Sets the current iteration position to the beginning of the text, position zero. - * @return The new iterator position, which is zero. - */ -int32_t RuleBasedBreakIterator::first(void) { - UErrorCode status = U_ZERO_ERROR; - if (!fBreakCache->seek(0)) { - fBreakCache->populateNear(0, status); - } - fBreakCache->current(); - U_ASSERT(fPosition == 0); - return 0; -} - -/** - * Sets the current iteration position to the end of the text. - * @return The text's past-the-end offset. - */ -int32_t RuleBasedBreakIterator::last(void) { - int32_t endPos = (int32_t)utext_nativeLength(&fText); - UBool endShouldBeBoundary = isBoundary(endPos); // Has side effect of setting iterator position. - (void)endShouldBeBoundary; - U_ASSERT(endShouldBeBoundary); - U_ASSERT(fPosition == endPos); - return endPos; -} - -/** - * Advances the iterator either forward or backward the specified number of steps. - * Negative values move backward, and positive values move forward. This is - * equivalent to repeatedly calling next() or previous(). - * @param n The number of steps to move. The sign indicates the direction - * (negative is backwards, and positive is forwards). - * @return The character offset of the boundary position n boundaries away from - * the current one. - */ -int32_t RuleBasedBreakIterator::next(int32_t n) { - int32_t result = 0; - if (n > 0) { - for (; n > 0 && result != UBRK_DONE; --n) { - result = next(); - } - } else if (n < 0) { - for (; n < 0 && result != UBRK_DONE; ++n) { - result = previous(); - } - } else { - result = current(); - } - return result; -} - -/** - * Advances the iterator to the next boundary position. - * @return The position of the first boundary after this one. - */ -int32_t RuleBasedBreakIterator::next(void) { - fBreakCache->next(); - return fDone ? UBRK_DONE : fPosition; -} - -/** - * Move the iterator backwards, to the boundary preceding the current one. - * - * Starts from the current position within fText. - * Starting position need not be on a boundary. - * - * @return The position of the boundary position immediately preceding the starting position. - */ -int32_t RuleBasedBreakIterator::previous(void) { - UErrorCode status = U_ZERO_ERROR; - fBreakCache->previous(status); - return fDone ? UBRK_DONE : fPosition; -} - -/** - * Sets the iterator to refer to the first boundary position following - * the specified position. - * @param startPos The position from which to begin searching for a break position. - * @return The position of the first break after the current position. - */ -int32_t RuleBasedBreakIterator::following(int32_t startPos) { - // if the supplied position is before the beginning, return the - // text's starting offset - if (startPos < 0) { - return first(); - } - - // Move requested offset to a code point start. It might be on a trail surrogate, - // or on a trail byte if the input is UTF-8. Or it may be beyond the end of the text. - utext_setNativeIndex(&fText, startPos); - startPos = (int32_t)utext_getNativeIndex(&fText); - - UErrorCode status = U_ZERO_ERROR; - fBreakCache->following(startPos, status); - return fDone ? UBRK_DONE : fPosition; -} - -/** - * Sets the iterator to refer to the last boundary position before the - * specified position. - * @param offset The position to begin searching for a break from. - * @return The position of the last boundary before the starting position. - */ -int32_t RuleBasedBreakIterator::preceding(int32_t offset) { - if (offset > utext_nativeLength(&fText)) { - return last(); - } - - // Move requested offset to a code point start. It might be on a trail surrogate, - // or on a trail byte if the input is UTF-8. - - utext_setNativeIndex(&fText, offset); - int32_t adjustedOffset = static_cast(utext_getNativeIndex(&fText)); - - UErrorCode status = U_ZERO_ERROR; - fBreakCache->preceding(adjustedOffset, status); - return fDone ? UBRK_DONE : fPosition; -} - -/** - * Returns true if the specified position is a boundary position. As a side - * effect, leaves the iterator pointing to the first boundary position at - * or after "offset". - * - * @param offset the offset to check. - * @return True if "offset" is a boundary position. - */ -UBool RuleBasedBreakIterator::isBoundary(int32_t offset) { - // out-of-range indexes are never boundary positions - if (offset < 0) { - first(); // For side effects on current position, tag values. - return false; - } - - // Adjust offset to be on a code point boundary and not beyond the end of the text. - // 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); - int32_t adjustedOffset = static_cast(utext_getNativeIndex(&fText)); - - bool result = false; - UErrorCode status = U_ZERO_ERROR; - if (fBreakCache->seek(adjustedOffset) || fBreakCache->populateNear(adjustedOffset, status)) { - result = (fBreakCache->current() == offset); - } - - if (result && adjustedOffset < offset && utext_char32At(&fText, offset) == U_SENTINEL) { - // Original offset is beyond the end of the text. Return false, it's not a boundary, - // but the iteration position remains set to the end of the text, which is a boundary. - return false; - } - if (!result) { - // Not on a boundary. isBoundary() must leave iterator on the following boundary. - // Cache->seek(), above, left us on the preceding boundary, so advance one. - next(); - } - return result; -} - - -/** - * Returns the current iteration position. - * @return The current iteration position. - */ -int32_t RuleBasedBreakIterator::current(void) const { - return fPosition; -} - - -//======================================================================= -// implementation -//======================================================================= - -// -// RBBIRunMode - the state machine runs an extra iteration at the beginning and end -// of user text. A variable with this enum type keeps track of where we -// are. The state machine only fetches user input while in the RUN mode. -// -enum RBBIRunMode { - RBBI_START, // state machine processing is before first char of input - RBBI_RUN, // state machine processing is in the user text - RBBI_END // state machine processing is after end of user text. -}; - - -// Wrapper functions to select the appropriate handleNext() or handleSafePrevious() -// instantiation, based on whether an 8 or 16 bit table is required. -// -// These Trie access functions will be inlined within the handleNext()/Previous() instantions. -static inline uint16_t TrieFunc8(const UCPTrie *trie, UChar32 c) { - return UCPTRIE_FAST_GET(trie, UCPTRIE_8, c); -} - -static inline uint16_t TrieFunc16(const UCPTrie *trie, UChar32 c) { - return UCPTRIE_FAST_GET(trie, UCPTRIE_16, c); -} - -int32_t RuleBasedBreakIterator::handleNext() { - const RBBIStateTable *statetable = fData->fForwardTable; - bool use8BitsTrie = ucptrie_getValueWidth(fData->fTrie) == UCPTRIE_VALUE_BITS_8; - if (statetable->fFlags & RBBI_8BITS_ROWS) { - if (use8BitsTrie) { - return handleNext(); - } else { - return handleNext(); - } - } else { - if (use8BitsTrie) { - return handleNext(); - } else { - return handleNext(); - } - } -} - -int32_t RuleBasedBreakIterator::handleSafePrevious(int32_t fromPosition) { - const RBBIStateTable *statetable = fData->fReverseTable; - bool use8BitsTrie = ucptrie_getValueWidth(fData->fTrie) == UCPTRIE_VALUE_BITS_8; - if (statetable->fFlags & RBBI_8BITS_ROWS) { - if (use8BitsTrie) { - return handleSafePrevious(fromPosition); - } else { - return handleSafePrevious(fromPosition); - } - } else { - if (use8BitsTrie) { - return handleSafePrevious(fromPosition); - } else { - return handleSafePrevious(fromPosition); - } - } -} - - -//----------------------------------------------------------------------------------- -// -// handleNext() -// Run the state machine to find a boundary -// -//----------------------------------------------------------------------------------- -template -int32_t RuleBasedBreakIterator::handleNext() { - int32_t state; - uint16_t category = 0; - RBBIRunMode mode; - - RowType *row; - UChar32 c; - int32_t result = 0; - int32_t initialPosition = 0; - const RBBIStateTable *statetable = fData->fForwardTable; - const char *tableData = statetable->fTableData; - uint32_t tableRowLen = statetable->fRowLen; - uint32_t dictStart = statetable->fDictCategoriesStart; - #ifdef RBBI_DEBUG - if (gTrace) { - RBBIDebugPuts("Handle Next pos char state category"); - } - #endif - - // handleNext always sets the break tag value. - // Set the default for it. - fRuleStatusIndex = 0; - - fDictionaryCharCount = 0; - - // if we're already at the end of the text, return DONE. - initialPosition = fPosition; - UTEXT_SETNATIVEINDEX(&fText, initialPosition); - result = initialPosition; - c = UTEXT_NEXT32(&fText); - if (c==U_SENTINEL) { - fDone = true; - return UBRK_DONE; - } - - // Set the initial state for the state machine - state = START_STATE; - row = (RowType *) - //(statetable->fTableData + (statetable->fRowLen * state)); - (tableData + tableRowLen * state); - - - mode = RBBI_RUN; - if (statetable->fFlags & RBBI_BOF_REQUIRED) { - category = 2; - mode = RBBI_START; - } - - - // loop until we reach the end 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; - } - - // - // 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. - category = trieFunc(fData->fTrie, c); - fDictionaryCharCount += (category >= dictStart); - } - - #ifdef RBBI_DEBUG - if (gTrace) { - RBBIDebugPrintf(" %4" PRId64 " ", utext_getNativeIndex(&fText)); - if (0x20<=c && c<0x7f) { - RBBIDebugPrintf("\"%c\" ", c); - } else { - RBBIDebugPrintf("%5x ", c); - } - RBBIDebugPrintf("%3d %3d\n", state, category); - } - #endif - - // 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 = (RowType *) - // (statetable->fTableData + (statetable->fRowLen * state)); - (tableData + tableRowLen * state); - - - uint16_t accepting = row->fAccepting; - if (accepting == ACCEPTING_UNCONDITIONAL) { - // Match found, common case. - if (mode != RBBI_START) { - result = (int32_t)UTEXT_GETNATIVEINDEX(&fText); - } - fRuleStatusIndex = row->fTagsIdx; // Remember the break status (tag) values. - } else if (accepting > ACCEPTING_UNCONDITIONAL) { - // Lookahead match is completed. - U_ASSERT(accepting < fData->fForwardTable->fLookAheadResultsSize); - int32_t lookaheadResult = fLookAheadMatches[accepting]; - if (lookaheadResult >= 0) { - fRuleStatusIndex = row->fTagsIdx; - fPosition = lookaheadResult; - return lookaheadResult; - } - } - - // If we are at the position of the '/' in a look-ahead (hard break) rule; - // record the current position, to be returned later, if the full rule matches. - // TODO: Move this check before the previous check of fAccepting. - // This would enable hard-break rules with no following context. - // But there are line break test failures when trying this. Investigate. - // Issue ICU-20837 - uint16_t rule = row->fLookAhead; - U_ASSERT(rule == 0 || rule > ACCEPTING_UNCONDITIONAL); - U_ASSERT(rule == 0 || rule < fData->fForwardTable->fLookAheadResultsSize); - if (rule > ACCEPTING_UNCONDITIONAL) { - int32_t pos = (int32_t)UTEXT_GETNATIVEINDEX(&fText); - fLookAheadMatches[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. - break; - } - - // Advance to the next character. - // 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_NEXT32(&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_next32(&fText); - result = (int32_t)utext_getNativeIndex(&fText); - fRuleStatusIndex = 0; - } - - // Leave the iterator at our result position. - fPosition = result; - #ifdef RBBI_DEBUG - if (gTrace) { - RBBIDebugPrintf("result = %d\n\n", result); - } - #endif - return result; -} - - -//----------------------------------------------------------------------------------- -// -// handleSafePrevious() -// -// Iterate backwards using the safe reverse rules. -// The logic of this function is similar to handleNext(), but simpler -// because the safe table does not require as many options. -// -//----------------------------------------------------------------------------------- -template -int32_t RuleBasedBreakIterator::handleSafePrevious(int32_t fromPosition) { - - int32_t state; - uint16_t category = 0; - RowType *row; - UChar32 c; - int32_t result = 0; - - const RBBIStateTable *stateTable = fData->fReverseTable; - UTEXT_SETNATIVEINDEX(&fText, fromPosition); - #ifdef RBBI_DEBUG - if (gTrace) { - RBBIDebugPuts("Handle Previous pos char state category"); - } - #endif - - // if we're already at the start of the text, return DONE. - if (fData == NULL || UTEXT_GETNATIVEINDEX(&fText)==0) { - return BreakIterator::DONE; - } - - // Set the initial state for the state machine - c = UTEXT_PREVIOUS32(&fText); - state = START_STATE; - row = (RowType *) - (stateTable->fTableData + (stateTable->fRowLen * state)); - - // loop until we reach the start of the text or transition to state 0 - // - 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. - // - // Off the dictionary flag bit. For reverse iteration it is not used. - category = trieFunc(fData->fTrie, c); - - #ifdef RBBI_DEBUG - if (gTrace) { - RBBIDebugPrintf(" %4d ", (int32_t)utext_getNativeIndex(&fText)); - if (0x20<=c && c<0x7f) { - RBBIDebugPrintf("\"%c\" ", c); - } else { - RBBIDebugPrintf("%5x ", c); - } - RBBIDebugPrintf("%3d %3d\n", state, category); - } - #endif - - // 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 = (RowType *) - (stateTable->fTableData + (stateTable->fRowLen * state)); - - if (state == STOP_STATE) { - // This is the normal exit from the lookup state machine. - // Transition to state zero means we have found a safe point. - break; - } - } - - // The state machine is done. Check whether it found a match... - result = (int32_t)UTEXT_GETNATIVEINDEX(&fText); - #ifdef RBBI_DEBUG - if (gTrace) { - RBBIDebugPrintf("result = %d\n\n", result); - } - #endif - return result; -} - - -//------------------------------------------------------------------------------- -// -// getRuleStatus() Return the break rule tag associated with the current -// iterator position. If the iterator arrived at its current -// position by iterating forwards, the value will have been -// cached by the handleNext() function. -// -//------------------------------------------------------------------------------- - -int32_t RuleBasedBreakIterator::getRuleStatus() const { - - // fLastRuleStatusIndex indexes to the start of the appropriate status record - // (the number of status values.) - // This function returns the last (largest) of the array of status values. - int32_t idx = fRuleStatusIndex + fData->fRuleStatusTable[fRuleStatusIndex]; - int32_t tagVal = fData->fRuleStatusTable[idx]; - - return tagVal; -} - - -int32_t RuleBasedBreakIterator::getRuleStatusVec( - int32_t *fillInVec, int32_t capacity, UErrorCode &status) { - if (U_FAILURE(status)) { - return 0; - } - - int32_t numVals = fData->fRuleStatusTable[fRuleStatusIndex]; - int32_t numValsToCopy = numVals; - if (numVals > capacity) { - status = U_BUFFER_OVERFLOW_ERROR; - numValsToCopy = capacity; - } - int i; - for (i=0; ifRuleStatusTable[fRuleStatusIndex + i + 1]; - } - return numVals; -} - - - -//------------------------------------------------------------------------------- -// -// getBinaryRules Access to the compiled form of the rules, -// for use by build system tools that save the data -// for standard iterator types. -// -//------------------------------------------------------------------------------- -const uint8_t *RuleBasedBreakIterator::getBinaryRules(uint32_t &length) { - const uint8_t *retPtr = NULL; - length = 0; - - if (fData != NULL) { - retPtr = (const uint8_t *)fData->fHeader; - length = fData->fHeader->fLength; - } - return retPtr; -} - - -RuleBasedBreakIterator *RuleBasedBreakIterator::createBufferClone( - void * /*stackBuffer*/, int32_t &bufferSize, UErrorCode &status) { - if (U_FAILURE(status)){ - return NULL; - } - - if (bufferSize == 0) { - bufferSize = 1; // preflighting for deprecated functionality - return NULL; - } - - BreakIterator *clonedBI = clone(); - if (clonedBI == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - status = U_SAFECLONE_ALLOCATED_WARNING; - } - return (RuleBasedBreakIterator *)clonedBI; -} - -U_NAMESPACE_END - - -static icu::UStack *gLanguageBreakFactories = nullptr; -static const icu::UnicodeString *gEmptyString = nullptr; -static icu::UInitOnce gLanguageBreakFactoriesInitOnce {}; -static icu::UInitOnce gRBBIInitOnce {}; - -/** - * Release all static memory held by breakiterator. - */ -U_CDECL_BEGIN -UBool U_CALLCONV rbbi_cleanup(void) { - delete gLanguageBreakFactories; - gLanguageBreakFactories = nullptr; - delete gEmptyString; - gEmptyString = nullptr; - gLanguageBreakFactoriesInitOnce.reset(); - gRBBIInitOnce.reset(); - return true; -} -U_CDECL_END - -U_CDECL_BEGIN -static void U_CALLCONV _deleteFactory(void *obj) { - delete (icu::LanguageBreakFactory *) obj; -} -U_CDECL_END -U_NAMESPACE_BEGIN - -static void U_CALLCONV rbbiInit() { - gEmptyString = new UnicodeString(); - ucln_common_registerCleanup(UCLN_COMMON_RBBI, rbbi_cleanup); -} - -static void U_CALLCONV initLanguageFactories() { - UErrorCode status = U_ZERO_ERROR; - U_ASSERT(gLanguageBreakFactories == NULL); - gLanguageBreakFactories = new UStack(_deleteFactory, NULL, status); - if (gLanguageBreakFactories != NULL && U_SUCCESS(status)) { - ICULanguageBreakFactory *builtIn = new ICULanguageBreakFactory(status); - gLanguageBreakFactories->push(builtIn, status); -#ifdef U_LOCAL_SERVICE_HOOK - LanguageBreakFactory *extra = (LanguageBreakFactory *)uprv_svc_hook("languageBreakFactory", &status); - if (extra != NULL) { - gLanguageBreakFactories->push(extra, status); - } -#endif - } - ucln_common_registerCleanup(UCLN_COMMON_RBBI, rbbi_cleanup); -} - - -static const LanguageBreakEngine* -getLanguageBreakEngineFromFactory(UChar32 c) -{ - umtx_initOnce(gLanguageBreakFactoriesInitOnce, &initLanguageFactories); - if (gLanguageBreakFactories == NULL) { - return NULL; - } - - int32_t i = gLanguageBreakFactories->size(); - const LanguageBreakEngine *lbe = NULL; - while (--i >= 0) { - LanguageBreakFactory *factory = (LanguageBreakFactory *)(gLanguageBreakFactories->elementAt(i)); - lbe = factory->getEngineFor(c); - if (lbe != NULL) { - break; - } - } - return lbe; -} - - -//------------------------------------------------------------------------------- -// -// getLanguageBreakEngine Find an appropriate LanguageBreakEngine for the -// the character c. -// -//------------------------------------------------------------------------------- -const LanguageBreakEngine * -RuleBasedBreakIterator::getLanguageBreakEngine(UChar32 c) { - const LanguageBreakEngine *lbe = NULL; - UErrorCode status = U_ZERO_ERROR; - - if (fLanguageBreakEngines == NULL) { - fLanguageBreakEngines = new UStack(status); - if (fLanguageBreakEngines == NULL || U_FAILURE(status)) { - delete fLanguageBreakEngines; - fLanguageBreakEngines = 0; - return NULL; - } - } - - int32_t i = fLanguageBreakEngines->size(); - while (--i >= 0) { - lbe = (const LanguageBreakEngine *)(fLanguageBreakEngines->elementAt(i)); - if (lbe->handles(c)) { - return lbe; - } - } - - // No existing dictionary took the character. See if a factory wants to - // give us a new LanguageBreakEngine for this character. - lbe = getLanguageBreakEngineFromFactory(c); - - // If we got one, use it and push it on our stack. - if (lbe != NULL) { - fLanguageBreakEngines->push((void *)lbe, status); - // Even if we can't remember it, we can keep looking it up, so - // return it even if the push fails. - return lbe; - } - - // No engine is forthcoming for this character. Add it to the - // reject set. Create the reject break engine if needed. - if (fUnhandledBreakEngine == NULL) { - fUnhandledBreakEngine = new UnhandledEngine(status); - if (U_SUCCESS(status) && fUnhandledBreakEngine == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - // Put it last so that scripts for which we have an engine get tried - // first. - fLanguageBreakEngines->insertElementAt(fUnhandledBreakEngine, 0, status); - // If we can't insert it, or creation failed, get rid of it - U_ASSERT(!fLanguageBreakEngines->hasDeleter()); - if (U_FAILURE(status)) { - delete fUnhandledBreakEngine; - fUnhandledBreakEngine = 0; - return NULL; - } - } - - // Tell the reject engine about the character; at its discretion, it may - // add more than just the one character. - fUnhandledBreakEngine->handleCharacter(c); - - return fUnhandledBreakEngine; -} - -void RuleBasedBreakIterator::dumpCache() { - fBreakCache->dumpCache(); -} - -void RuleBasedBreakIterator::dumpTables() { - fData->printData(); -} - -/** - * Returns the description used to create this iterator - */ - -const UnicodeString& -RuleBasedBreakIterator::getRules() const { - if (fData != NULL) { - return fData->getRuleSourceString(); - } else { - umtx_initOnce(gRBBIInitOnce, &rbbiInit); - return *gEmptyString; - } -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +*************************************************************************** +* Copyright (C) 1999-2016 International Business Machines Corporation +* and others. All rights reserved. +*************************************************************************** +*/ +// +// file: rbbi.cpp Contains the implementation of the rule based break iterator +// runtime engine and the API implementation for +// class RuleBasedBreakIterator +// + +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include + +#include "unicode/rbbi.h" +#include "unicode/schriter.h" +#include "unicode/uchriter.h" +#include "unicode/uclean.h" +#include "unicode/udata.h" + +#include "brkeng.h" +#include "ucln_cmn.h" +#include "cmemory.h" +#include "cstring.h" +#include "localsvc.h" +#include "rbbidata.h" +#include "rbbi_cache.h" +#include "rbbirb.h" +#include "uassert.h" +#include "umutex.h" +#include "uvectr32.h" + +#ifdef RBBI_DEBUG +static UBool gTrace = false; +#endif + +U_NAMESPACE_BEGIN + +// The state number of the starting state +constexpr int32_t START_STATE = 1; + +// The state-transition value indicating "stop" +constexpr int32_t STOP_STATE = 0; + + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedBreakIterator) + + +//======================================================================= +// constructors +//======================================================================= + +/** + * Constructs a RuleBasedBreakIterator that uses the already-created + * tables object that is passed in as a parameter. + */ +RuleBasedBreakIterator::RuleBasedBreakIterator(RBBIDataHeader* data, UErrorCode &status) + : RuleBasedBreakIterator(&status) +{ + fData = new RBBIDataWrapper(data, status); // status checked in constructor + if (U_FAILURE(status)) {return;} + if(fData == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (fData->fForwardTable->fLookAheadResultsSize > 0) { + fLookAheadMatches = static_cast( + uprv_malloc(fData->fForwardTable->fLookAheadResultsSize * sizeof(int32_t))); + if (fLookAheadMatches == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } +} + +//------------------------------------------------------------------------------- +// +// Constructor from a UDataMemory handle to precompiled break rules +// stored in an ICU data file. This construcotr is private API, +// only for internal use. +// +//------------------------------------------------------------------------------- +RuleBasedBreakIterator::RuleBasedBreakIterator(UDataMemory* udm, UBool isPhraseBreaking, + UErrorCode &status) : RuleBasedBreakIterator(udm, status) +{ + fIsPhraseBreaking = isPhraseBreaking; +} + +// +// Construct from precompiled binary rules (tables). This constructor is public API, +// taking the rules as a (const uint8_t *) to match the type produced by getBinaryRules(). +// +RuleBasedBreakIterator::RuleBasedBreakIterator(const uint8_t *compiledRules, + uint32_t ruleLength, + UErrorCode &status) + : RuleBasedBreakIterator(&status) +{ + if (U_FAILURE(status)) { + return; + } + if (compiledRules == nullptr || ruleLength < sizeof(RBBIDataHeader)) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + const RBBIDataHeader *data = (const RBBIDataHeader *)compiledRules; + if (data->fLength > ruleLength) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + fData = new RBBIDataWrapper(data, RBBIDataWrapper::kDontAdopt, status); + if (U_FAILURE(status)) {return;} + if(fData == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (fData->fForwardTable->fLookAheadResultsSize > 0) { + fLookAheadMatches = static_cast( + uprv_malloc(fData->fForwardTable->fLookAheadResultsSize * sizeof(int32_t))); + if (fLookAheadMatches == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } +} + + +//------------------------------------------------------------------------------- +// +// Constructor from a UDataMemory handle to precompiled break rules +// stored in an ICU data file. +// +//------------------------------------------------------------------------------- +RuleBasedBreakIterator::RuleBasedBreakIterator(UDataMemory* udm, UErrorCode &status) + : RuleBasedBreakIterator(&status) +{ + fData = new RBBIDataWrapper(udm, status); // status checked in constructor + if (U_FAILURE(status)) {return;} + if(fData == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (fData->fForwardTable->fLookAheadResultsSize > 0) { + fLookAheadMatches = static_cast( + uprv_malloc(fData->fForwardTable->fLookAheadResultsSize * sizeof(int32_t))); + if (fLookAheadMatches == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } +} + + + +//------------------------------------------------------------------------------- +// +// Constructor from a set of rules supplied as a string. +// +//------------------------------------------------------------------------------- +RuleBasedBreakIterator::RuleBasedBreakIterator( const UnicodeString &rules, + UParseError &parseError, + UErrorCode &status) + : RuleBasedBreakIterator(&status) +{ + if (U_FAILURE(status)) {return;} + RuleBasedBreakIterator *bi = (RuleBasedBreakIterator *) + RBBIRuleBuilder::createRuleBasedBreakIterator(rules, &parseError, status); + // Note: This is a bit awkward. The RBBI ruleBuilder has a factory method that + // creates and returns a complete RBBI. From here, in a constructor, we + // can't just return the object created by the builder factory, hence + // the assignment of the factory created object to "this". + if (U_SUCCESS(status)) { + *this = *bi; + delete bi; + } +} + + +//------------------------------------------------------------------------------- +// +// Default Constructor. Create an empty shell that can be set up later. +// Used when creating a RuleBasedBreakIterator from a set +// of rules. +//------------------------------------------------------------------------------- +RuleBasedBreakIterator::RuleBasedBreakIterator() + : RuleBasedBreakIterator(nullptr) +{ +} + +/** + * Simple Constructor with an error code. + * Handles common initialization for all other constructors. + */ +RuleBasedBreakIterator::RuleBasedBreakIterator(UErrorCode *status) { + UErrorCode ec = U_ZERO_ERROR; + if (status == nullptr) { + status = &ec; + } + utext_openUChars(&fText, nullptr, 0, status); + LocalPointer lpDictionaryCache(new DictionaryCache(this, *status), *status); + LocalPointer lpBreakCache(new BreakCache(this, *status), *status); + if (U_FAILURE(*status)) { + fErrorCode = *status; + return; + } + fDictionaryCache = lpDictionaryCache.orphan(); + fBreakCache = lpBreakCache.orphan(); + +#ifdef RBBI_DEBUG + static UBool debugInitDone = false; + if (debugInitDone == false) { + char *debugEnv = getenv("U_RBBIDEBUG"); + if (debugEnv && uprv_strstr(debugEnv, "trace")) { + gTrace = true; + } + debugInitDone = true; + } +#endif +} + + +//------------------------------------------------------------------------------- +// +// Copy constructor. Will produce a break iterator with the same behavior, +// and which iterates over the same text, as the one passed in. +// +//------------------------------------------------------------------------------- +RuleBasedBreakIterator::RuleBasedBreakIterator(const RuleBasedBreakIterator& other) +: RuleBasedBreakIterator() +{ + *this = other; +} + + +/** + * Destructor + */ +RuleBasedBreakIterator::~RuleBasedBreakIterator() { + if (fCharIter != &fSCharIter) { + // fCharIter was adopted from the outside. + delete fCharIter; + } + fCharIter = nullptr; + + utext_close(&fText); + + if (fData != nullptr) { + fData->removeReference(); + fData = nullptr; + } + delete fBreakCache; + fBreakCache = nullptr; + + delete fDictionaryCache; + fDictionaryCache = nullptr; + + delete fLanguageBreakEngines; + fLanguageBreakEngines = nullptr; + + delete fUnhandledBreakEngine; + fUnhandledBreakEngine = nullptr; + + uprv_free(fLookAheadMatches); + fLookAheadMatches = nullptr; +} + +/** + * Assignment operator. Sets this iterator to have the same behavior, + * and iterate over the same text, as the one passed in. + * TODO: needs better handling of memory allocation errors. + */ +RuleBasedBreakIterator& +RuleBasedBreakIterator::operator=(const RuleBasedBreakIterator& that) { + if (this == &that) { + return *this; + } + BreakIterator::operator=(that); + + if (fLanguageBreakEngines != nullptr) { + delete fLanguageBreakEngines; + fLanguageBreakEngines = nullptr; // Just rebuild for now + } + // TODO: clone fLanguageBreakEngines from "that" + UErrorCode status = U_ZERO_ERROR; + utext_clone(&fText, &that.fText, false, true, &status); + + if (fCharIter != &fSCharIter) { + delete fCharIter; + } + fCharIter = &fSCharIter; + + if (that.fCharIter != nullptr && that.fCharIter != &that.fSCharIter) { + // This is a little bit tricky - it will initially appear that + // this->fCharIter is adopted, even if that->fCharIter was + // not adopted. That's ok. + fCharIter = that.fCharIter->clone(); + } + fSCharIter = that.fSCharIter; + if (fCharIter == nullptr) { + fCharIter = &fSCharIter; + } + + if (fData != nullptr) { + fData->removeReference(); + fData = nullptr; + } + if (that.fData != nullptr) { + fData = that.fData->addReference(); + } + + uprv_free(fLookAheadMatches); + fLookAheadMatches = nullptr; + if (fData && fData->fForwardTable->fLookAheadResultsSize > 0) { + fLookAheadMatches = static_cast( + uprv_malloc(fData->fForwardTable->fLookAheadResultsSize * sizeof(int32_t))); + } + + + fPosition = that.fPosition; + fRuleStatusIndex = that.fRuleStatusIndex; + fDone = that.fDone; + + // TODO: both the dictionary and the main cache need to be copied. + // Current position could be within a dictionary range. Trying to continue + // the iteration without the caches present would go to the rules, with + // the assumption that the current position is on a rule boundary. + fBreakCache->reset(fPosition, fRuleStatusIndex); + fDictionaryCache->reset(); + + return *this; +} + +//----------------------------------------------------------------------------- +// +// clone - Returns a newly-constructed RuleBasedBreakIterator with the same +// behavior, and iterating over the same text, as this one. +// Virtual function: does the right thing with subclasses. +// +//----------------------------------------------------------------------------- +RuleBasedBreakIterator* +RuleBasedBreakIterator::clone() const { + return new RuleBasedBreakIterator(*this); +} + +/** + * Equality operator. Returns true if both BreakIterators are of the + * same class, have the same behavior, and iterate over the same text. + */ +bool +RuleBasedBreakIterator::operator==(const BreakIterator& that) const { + if (typeid(*this) != typeid(that)) { + return false; + } + if (this == &that) { + return true; + } + + // The base class BreakIterator carries no state that participates in equality, + // and does not implement an equality function that would otherwise be + // checked at this point. + + const RuleBasedBreakIterator& that2 = static_cast(that); + + if (!utext_equals(&fText, &that2.fText)) { + // The two break iterators are operating on different text, + // or have a different iteration position. + // Note that fText's position is always the same as the break iterator's position. + return false; + } + + if (!(fPosition == that2.fPosition && + fRuleStatusIndex == that2.fRuleStatusIndex && + fDone == that2.fDone)) { + return false; + } + + if (that2.fData == fData || + (fData != nullptr && that2.fData != nullptr && *that2.fData == *fData)) { + // The two break iterators are using the same rules. + return true; + } + return false; +} + +/** + * Compute a hash code for this BreakIterator + * @return A hash code + */ +int32_t +RuleBasedBreakIterator::hashCode() const { + int32_t hash = 0; + if (fData != nullptr) { + hash = fData->hashCode(); + } + return hash; +} + + +void RuleBasedBreakIterator::setText(UText *ut, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + fBreakCache->reset(); + fDictionaryCache->reset(); + utext_clone(&fText, ut, false, true, &status); + + // Set up a dummy CharacterIterator to be returned if anyone + // calls getText(). With input from UText, there is no reasonable + // way to return a characterIterator over the actual input text. + // Return one over an empty string instead - this is the closest + // we can come to signaling a failure. + // (GetText() is obsolete, this failure is sort of OK) + fSCharIter.setText(u"", 0); + + if (fCharIter != &fSCharIter) { + // existing fCharIter was adopted from the outside. Delete it now. + delete fCharIter; + } + fCharIter = &fSCharIter; + + this->first(); +} + + +UText *RuleBasedBreakIterator::getUText(UText *fillIn, UErrorCode &status) const { + UText *result = utext_clone(fillIn, &fText, false, true, &status); + return result; +} + + +//======================================================================= +// BreakIterator overrides +//======================================================================= + +/** + * Return a CharacterIterator over the text being analyzed. + */ +CharacterIterator& +RuleBasedBreakIterator::getText() const { + return *fCharIter; +} + +/** + * Set the iterator to analyze a new piece of text. This function resets + * the current iteration position to the beginning of the text. + * @param newText An iterator over the text to analyze. + */ +void +RuleBasedBreakIterator::adoptText(CharacterIterator* newText) { + // If we are holding a CharacterIterator adopted from a + // previous call to this function, delete it now. + if (fCharIter != &fSCharIter) { + delete fCharIter; + } + + fCharIter = newText; + UErrorCode status = U_ZERO_ERROR; + fBreakCache->reset(); + fDictionaryCache->reset(); + if (newText==nullptr || newText->startIndex() != 0) { + // startIndex !=0 wants to be an error, but there's no way to report it. + // Make the iterator text be an empty string. + utext_openUChars(&fText, nullptr, 0, &status); + } else { + utext_openCharacterIterator(&fText, newText, &status); + } + this->first(); +} + +/** + * Set the iterator to analyze a new piece of text. This function resets + * the current iteration position to the beginning of the text. + * @param newText An iterator over the text to analyze. + */ +void +RuleBasedBreakIterator::setText(const UnicodeString& newText) { + UErrorCode status = U_ZERO_ERROR; + fBreakCache->reset(); + fDictionaryCache->reset(); + utext_openConstUnicodeString(&fText, &newText, &status); + + // Set up a character iterator on the string. + // Needed in case someone calls getText(). + // Can not, unfortunately, do this lazily on the (probably never) + // call to getText(), because getText is const. + fSCharIter.setText(newText.getBuffer(), newText.length()); + + if (fCharIter != &fSCharIter) { + // old fCharIter was adopted from the outside. Delete it. + delete fCharIter; + } + fCharIter = &fSCharIter; + + this->first(); +} + + +/** + * Provide a new UText for the input text. Must reference text with contents identical + * to the original. + * Intended for use with text data originating in Java (garbage collected) environments + * where the data may be moved in memory at arbitrary times. + */ +RuleBasedBreakIterator &RuleBasedBreakIterator::refreshInputText(UText *input, UErrorCode &status) { + if (U_FAILURE(status)) { + return *this; + } + if (input == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return *this; + } + int64_t pos = utext_getNativeIndex(&fText); + // Shallow read-only clone of the new UText into the existing input UText + utext_clone(&fText, input, false, true, &status); + if (U_FAILURE(status)) { + return *this; + } + utext_setNativeIndex(&fText, pos); + if (utext_getNativeIndex(&fText) != pos) { + // Sanity check. The new input utext is supposed to have the exact same + // contents as the old. If we can't set to the same position, it doesn't. + // The contents underlying the old utext might be invalid at this point, + // so it's not safe to check directly. + status = U_ILLEGAL_ARGUMENT_ERROR; + } + return *this; +} + + +/** + * Sets the current iteration position to the beginning of the text, position zero. + * @return The new iterator position, which is zero. + */ +int32_t RuleBasedBreakIterator::first() { + UErrorCode status = U_ZERO_ERROR; + if (!fBreakCache->seek(0)) { + fBreakCache->populateNear(0, status); + } + fBreakCache->current(); + U_ASSERT(fPosition == 0); + return 0; +} + +/** + * Sets the current iteration position to the end of the text. + * @return The text's past-the-end offset. + */ +int32_t RuleBasedBreakIterator::last() { + int32_t endPos = (int32_t)utext_nativeLength(&fText); + UBool endShouldBeBoundary = isBoundary(endPos); // Has side effect of setting iterator position. + (void)endShouldBeBoundary; + U_ASSERT(endShouldBeBoundary); + U_ASSERT(fPosition == endPos); + return endPos; +} + +/** + * Advances the iterator either forward or backward the specified number of steps. + * Negative values move backward, and positive values move forward. This is + * equivalent to repeatedly calling next() or previous(). + * @param n The number of steps to move. The sign indicates the direction + * (negative is backwards, and positive is forwards). + * @return The character offset of the boundary position n boundaries away from + * the current one. + */ +int32_t RuleBasedBreakIterator::next(int32_t n) { + int32_t result = 0; + if (n > 0) { + for (; n > 0 && result != UBRK_DONE; --n) { + result = next(); + } + } else if (n < 0) { + for (; n < 0 && result != UBRK_DONE; ++n) { + result = previous(); + } + } else { + result = current(); + } + return result; +} + +/** + * Advances the iterator to the next boundary position. + * @return The position of the first boundary after this one. + */ +int32_t RuleBasedBreakIterator::next() { + fBreakCache->next(); + return fDone ? UBRK_DONE : fPosition; +} + +/** + * Move the iterator backwards, to the boundary preceding the current one. + * + * Starts from the current position within fText. + * Starting position need not be on a boundary. + * + * @return The position of the boundary position immediately preceding the starting position. + */ +int32_t RuleBasedBreakIterator::previous() { + UErrorCode status = U_ZERO_ERROR; + fBreakCache->previous(status); + return fDone ? UBRK_DONE : fPosition; +} + +/** + * Sets the iterator to refer to the first boundary position following + * the specified position. + * @param startPos The position from which to begin searching for a break position. + * @return The position of the first break after the current position. + */ +int32_t RuleBasedBreakIterator::following(int32_t startPos) { + // if the supplied position is before the beginning, return the + // text's starting offset + if (startPos < 0) { + return first(); + } + + // Move requested offset to a code point start. It might be on a trail surrogate, + // or on a trail byte if the input is UTF-8. Or it may be beyond the end of the text. + utext_setNativeIndex(&fText, startPos); + startPos = (int32_t)utext_getNativeIndex(&fText); + + UErrorCode status = U_ZERO_ERROR; + fBreakCache->following(startPos, status); + return fDone ? UBRK_DONE : fPosition; +} + +/** + * Sets the iterator to refer to the last boundary position before the + * specified position. + * @param offset The position to begin searching for a break from. + * @return The position of the last boundary before the starting position. + */ +int32_t RuleBasedBreakIterator::preceding(int32_t offset) { + if (offset > utext_nativeLength(&fText)) { + return last(); + } + + // Move requested offset to a code point start. It might be on a trail surrogate, + // or on a trail byte if the input is UTF-8. + + utext_setNativeIndex(&fText, offset); + int32_t adjustedOffset = static_cast(utext_getNativeIndex(&fText)); + + UErrorCode status = U_ZERO_ERROR; + fBreakCache->preceding(adjustedOffset, status); + return fDone ? UBRK_DONE : fPosition; +} + +/** + * Returns true if the specified position is a boundary position. As a side + * effect, leaves the iterator pointing to the first boundary position at + * or after "offset". + * + * @param offset the offset to check. + * @return True if "offset" is a boundary position. + */ +UBool RuleBasedBreakIterator::isBoundary(int32_t offset) { + // out-of-range indexes are never boundary positions + if (offset < 0) { + first(); // For side effects on current position, tag values. + return false; + } + + // Adjust offset to be on a code point boundary and not beyond the end of the text. + // 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); + int32_t adjustedOffset = static_cast(utext_getNativeIndex(&fText)); + + bool result = false; + UErrorCode status = U_ZERO_ERROR; + if (fBreakCache->seek(adjustedOffset) || fBreakCache->populateNear(adjustedOffset, status)) { + result = (fBreakCache->current() == offset); + } + + if (result && adjustedOffset < offset && utext_char32At(&fText, offset) == U_SENTINEL) { + // Original offset is beyond the end of the text. Return false, it's not a boundary, + // but the iteration position remains set to the end of the text, which is a boundary. + return false; + } + if (!result) { + // Not on a boundary. isBoundary() must leave iterator on the following boundary. + // Cache->seek(), above, left us on the preceding boundary, so advance one. + next(); + } + return result; +} + + +/** + * Returns the current iteration position. + * @return The current iteration position. + */ +int32_t RuleBasedBreakIterator::current() const { + return fPosition; +} + + +//======================================================================= +// implementation +//======================================================================= + +// +// RBBIRunMode - the state machine runs an extra iteration at the beginning and end +// of user text. A variable with this enum type keeps track of where we +// are. The state machine only fetches user input while in the RUN mode. +// +enum RBBIRunMode { + RBBI_START, // state machine processing is before first char of input + RBBI_RUN, // state machine processing is in the user text + RBBI_END // state machine processing is after end of user text. +}; + + +// Wrapper functions to select the appropriate handleNext() or handleSafePrevious() +// instantiation, based on whether an 8 or 16 bit table is required. +// +// These Trie access functions will be inlined within the handleNext()/Previous() instantions. +static inline uint16_t TrieFunc8(const UCPTrie *trie, UChar32 c) { + return UCPTRIE_FAST_GET(trie, UCPTRIE_8, c); +} + +static inline uint16_t TrieFunc16(const UCPTrie *trie, UChar32 c) { + return UCPTRIE_FAST_GET(trie, UCPTRIE_16, c); +} + +int32_t RuleBasedBreakIterator::handleNext() { + const RBBIStateTable *statetable = fData->fForwardTable; + bool use8BitsTrie = ucptrie_getValueWidth(fData->fTrie) == UCPTRIE_VALUE_BITS_8; + if (statetable->fFlags & RBBI_8BITS_ROWS) { + if (use8BitsTrie) { + return handleNext(); + } else { + return handleNext(); + } + } else { + if (use8BitsTrie) { + return handleNext(); + } else { + return handleNext(); + } + } +} + +int32_t RuleBasedBreakIterator::handleSafePrevious(int32_t fromPosition) { + const RBBIStateTable *statetable = fData->fReverseTable; + bool use8BitsTrie = ucptrie_getValueWidth(fData->fTrie) == UCPTRIE_VALUE_BITS_8; + if (statetable->fFlags & RBBI_8BITS_ROWS) { + if (use8BitsTrie) { + return handleSafePrevious(fromPosition); + } else { + return handleSafePrevious(fromPosition); + } + } else { + if (use8BitsTrie) { + return handleSafePrevious(fromPosition); + } else { + return handleSafePrevious(fromPosition); + } + } +} + + +//----------------------------------------------------------------------------------- +// +// handleNext() +// Run the state machine to find a boundary +// +//----------------------------------------------------------------------------------- +template +int32_t RuleBasedBreakIterator::handleNext() { + int32_t state; + uint16_t category = 0; + RBBIRunMode mode; + + RowType *row; + UChar32 c; + int32_t result = 0; + int32_t initialPosition = 0; + const RBBIStateTable *statetable = fData->fForwardTable; + const char *tableData = statetable->fTableData; + uint32_t tableRowLen = statetable->fRowLen; + uint32_t dictStart = statetable->fDictCategoriesStart; + #ifdef RBBI_DEBUG + if (gTrace) { + RBBIDebugPuts("Handle Next pos char state category"); + } + #endif + + // handleNext always sets the break tag value. + // Set the default for it. + fRuleStatusIndex = 0; + + fDictionaryCharCount = 0; + + // if we're already at the end of the text, return DONE. + initialPosition = fPosition; + UTEXT_SETNATIVEINDEX(&fText, initialPosition); + result = initialPosition; + c = UTEXT_NEXT32(&fText); + if (c==U_SENTINEL) { + fDone = true; + return UBRK_DONE; + } + + // Set the initial state for the state machine + state = START_STATE; + row = (RowType *) + //(statetable->fTableData + (statetable->fRowLen * state)); + (tableData + tableRowLen * state); + + + mode = RBBI_RUN; + if (statetable->fFlags & RBBI_BOF_REQUIRED) { + category = 2; + mode = RBBI_START; + } + + + // loop until we reach the end 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; + } + + // + // 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. + category = trieFunc(fData->fTrie, c); + fDictionaryCharCount += (category >= dictStart); + } + + #ifdef RBBI_DEBUG + if (gTrace) { + RBBIDebugPrintf(" %4" PRId64 " ", utext_getNativeIndex(&fText)); + if (0x20<=c && c<0x7f) { + RBBIDebugPrintf("\"%c\" ", c); + } else { + RBBIDebugPrintf("%5x ", c); + } + RBBIDebugPrintf("%3d %3d\n", state, category); + } + #endif + + // 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 = (RowType *) + // (statetable->fTableData + (statetable->fRowLen * state)); + (tableData + tableRowLen * state); + + + uint16_t accepting = row->fAccepting; + if (accepting == ACCEPTING_UNCONDITIONAL) { + // Match found, common case. + if (mode != RBBI_START) { + result = (int32_t)UTEXT_GETNATIVEINDEX(&fText); + } + fRuleStatusIndex = row->fTagsIdx; // Remember the break status (tag) values. + } else if (accepting > ACCEPTING_UNCONDITIONAL) { + // Lookahead match is completed. + U_ASSERT(accepting < fData->fForwardTable->fLookAheadResultsSize); + int32_t lookaheadResult = fLookAheadMatches[accepting]; + if (lookaheadResult >= 0) { + fRuleStatusIndex = row->fTagsIdx; + fPosition = lookaheadResult; + return lookaheadResult; + } + } + + // If we are at the position of the '/' in a look-ahead (hard break) rule; + // record the current position, to be returned later, if the full rule matches. + // TODO: Move this check before the previous check of fAccepting. + // This would enable hard-break rules with no following context. + // But there are line break test failures when trying this. Investigate. + // Issue ICU-20837 + uint16_t rule = row->fLookAhead; + U_ASSERT(rule == 0 || rule > ACCEPTING_UNCONDITIONAL); + U_ASSERT(rule == 0 || rule < fData->fForwardTable->fLookAheadResultsSize); + if (rule > ACCEPTING_UNCONDITIONAL) { + int32_t pos = (int32_t)UTEXT_GETNATIVEINDEX(&fText); + fLookAheadMatches[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. + break; + } + + // Advance to the next character. + // 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_NEXT32(&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_next32(&fText); + result = (int32_t)utext_getNativeIndex(&fText); + fRuleStatusIndex = 0; + } + + // Leave the iterator at our result position. + fPosition = result; + #ifdef RBBI_DEBUG + if (gTrace) { + RBBIDebugPrintf("result = %d\n\n", result); + } + #endif + return result; +} + + +//----------------------------------------------------------------------------------- +// +// handleSafePrevious() +// +// Iterate backwards using the safe reverse rules. +// The logic of this function is similar to handleNext(), but simpler +// because the safe table does not require as many options. +// +//----------------------------------------------------------------------------------- +template +int32_t RuleBasedBreakIterator::handleSafePrevious(int32_t fromPosition) { + + int32_t state; + uint16_t category = 0; + RowType *row; + UChar32 c; + int32_t result = 0; + + const RBBIStateTable *stateTable = fData->fReverseTable; + UTEXT_SETNATIVEINDEX(&fText, fromPosition); + #ifdef RBBI_DEBUG + if (gTrace) { + RBBIDebugPuts("Handle Previous pos char state category"); + } + #endif + + // if we're already at the start of the text, return DONE. + if (fData == nullptr || UTEXT_GETNATIVEINDEX(&fText)==0) { + return BreakIterator::DONE; + } + + // Set the initial state for the state machine + c = UTEXT_PREVIOUS32(&fText); + state = START_STATE; + row = (RowType *) + (stateTable->fTableData + (stateTable->fRowLen * state)); + + // loop until we reach the start of the text or transition to state 0 + // + 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. + // + // Off the dictionary flag bit. For reverse iteration it is not used. + category = trieFunc(fData->fTrie, c); + + #ifdef RBBI_DEBUG + if (gTrace) { + RBBIDebugPrintf(" %4d ", (int32_t)utext_getNativeIndex(&fText)); + if (0x20<=c && c<0x7f) { + RBBIDebugPrintf("\"%c\" ", c); + } else { + RBBIDebugPrintf("%5x ", c); + } + RBBIDebugPrintf("%3d %3d\n", state, category); + } + #endif + + // 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 = (RowType *) + (stateTable->fTableData + (stateTable->fRowLen * state)); + + if (state == STOP_STATE) { + // This is the normal exit from the lookup state machine. + // Transition to state zero means we have found a safe point. + break; + } + } + + // The state machine is done. Check whether it found a match... + result = (int32_t)UTEXT_GETNATIVEINDEX(&fText); + #ifdef RBBI_DEBUG + if (gTrace) { + RBBIDebugPrintf("result = %d\n\n", result); + } + #endif + return result; +} + + +//------------------------------------------------------------------------------- +// +// getRuleStatus() Return the break rule tag associated with the current +// iterator position. If the iterator arrived at its current +// position by iterating forwards, the value will have been +// cached by the handleNext() function. +// +//------------------------------------------------------------------------------- + +int32_t RuleBasedBreakIterator::getRuleStatus() const { + + // fLastRuleStatusIndex indexes to the start of the appropriate status record + // (the number of status values.) + // This function returns the last (largest) of the array of status values. + int32_t idx = fRuleStatusIndex + fData->fRuleStatusTable[fRuleStatusIndex]; + int32_t tagVal = fData->fRuleStatusTable[idx]; + + return tagVal; +} + + +int32_t RuleBasedBreakIterator::getRuleStatusVec( + int32_t *fillInVec, int32_t capacity, UErrorCode &status) { + if (U_FAILURE(status)) { + return 0; + } + + int32_t numVals = fData->fRuleStatusTable[fRuleStatusIndex]; + int32_t numValsToCopy = numVals; + if (numVals > capacity) { + status = U_BUFFER_OVERFLOW_ERROR; + numValsToCopy = capacity; + } + int i; + for (i=0; ifRuleStatusTable[fRuleStatusIndex + i + 1]; + } + return numVals; +} + + + +//------------------------------------------------------------------------------- +// +// getBinaryRules Access to the compiled form of the rules, +// for use by build system tools that save the data +// for standard iterator types. +// +//------------------------------------------------------------------------------- +const uint8_t *RuleBasedBreakIterator::getBinaryRules(uint32_t &length) { + const uint8_t *retPtr = nullptr; + length = 0; + + if (fData != nullptr) { + retPtr = (const uint8_t *)fData->fHeader; + length = fData->fHeader->fLength; + } + return retPtr; +} + + +RuleBasedBreakIterator *RuleBasedBreakIterator::createBufferClone( + void * /*stackBuffer*/, int32_t &bufferSize, UErrorCode &status) { + if (U_FAILURE(status)){ + return nullptr; + } + + if (bufferSize == 0) { + bufferSize = 1; // preflighting for deprecated functionality + return nullptr; + } + + BreakIterator *clonedBI = clone(); + if (clonedBI == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + status = U_SAFECLONE_ALLOCATED_WARNING; + } + return (RuleBasedBreakIterator *)clonedBI; +} + +U_NAMESPACE_END + + +static icu::UStack *gLanguageBreakFactories = nullptr; +static const icu::UnicodeString *gEmptyString = nullptr; +static icu::UInitOnce gLanguageBreakFactoriesInitOnce {}; +static icu::UInitOnce gRBBIInitOnce {}; + +/** + * Release all static memory held by breakiterator. + */ +U_CDECL_BEGIN +UBool U_CALLCONV rbbi_cleanup() { + delete gLanguageBreakFactories; + gLanguageBreakFactories = nullptr; + delete gEmptyString; + gEmptyString = nullptr; + gLanguageBreakFactoriesInitOnce.reset(); + gRBBIInitOnce.reset(); + return true; +} +U_CDECL_END + +U_CDECL_BEGIN +static void U_CALLCONV _deleteFactory(void *obj) { + delete (icu::LanguageBreakFactory *) obj; +} +U_CDECL_END +U_NAMESPACE_BEGIN + +static void U_CALLCONV rbbiInit() { + gEmptyString = new UnicodeString(); + ucln_common_registerCleanup(UCLN_COMMON_RBBI, rbbi_cleanup); +} + +static void U_CALLCONV initLanguageFactories() { + UErrorCode status = U_ZERO_ERROR; + U_ASSERT(gLanguageBreakFactories == nullptr); + gLanguageBreakFactories = new UStack(_deleteFactory, nullptr, status); + if (gLanguageBreakFactories != nullptr && U_SUCCESS(status)) { + ICULanguageBreakFactory *builtIn = new ICULanguageBreakFactory(status); + gLanguageBreakFactories->push(builtIn, status); +#ifdef U_LOCAL_SERVICE_HOOK + LanguageBreakFactory *extra = (LanguageBreakFactory *)uprv_svc_hook("languageBreakFactory", &status); + if (extra != nullptr) { + gLanguageBreakFactories->push(extra, status); + } +#endif + } + ucln_common_registerCleanup(UCLN_COMMON_RBBI, rbbi_cleanup); +} + + +static const LanguageBreakEngine* +getLanguageBreakEngineFromFactory(UChar32 c) +{ + umtx_initOnce(gLanguageBreakFactoriesInitOnce, &initLanguageFactories); + if (gLanguageBreakFactories == nullptr) { + return nullptr; + } + + int32_t i = gLanguageBreakFactories->size(); + const LanguageBreakEngine *lbe = nullptr; + while (--i >= 0) { + LanguageBreakFactory *factory = (LanguageBreakFactory *)(gLanguageBreakFactories->elementAt(i)); + lbe = factory->getEngineFor(c); + if (lbe != nullptr) { + break; + } + } + return lbe; +} + + +//------------------------------------------------------------------------------- +// +// getLanguageBreakEngine Find an appropriate LanguageBreakEngine for the +// the character c. +// +//------------------------------------------------------------------------------- +const LanguageBreakEngine * +RuleBasedBreakIterator::getLanguageBreakEngine(UChar32 c) { + const LanguageBreakEngine *lbe = nullptr; + UErrorCode status = U_ZERO_ERROR; + + if (fLanguageBreakEngines == nullptr) { + fLanguageBreakEngines = new UStack(status); + if (fLanguageBreakEngines == nullptr || U_FAILURE(status)) { + delete fLanguageBreakEngines; + fLanguageBreakEngines = 0; + return nullptr; + } + } + + int32_t i = fLanguageBreakEngines->size(); + while (--i >= 0) { + lbe = (const LanguageBreakEngine *)(fLanguageBreakEngines->elementAt(i)); + if (lbe->handles(c)) { + return lbe; + } + } + + // No existing dictionary took the character. See if a factory wants to + // give us a new LanguageBreakEngine for this character. + lbe = getLanguageBreakEngineFromFactory(c); + + // If we got one, use it and push it on our stack. + if (lbe != nullptr) { + fLanguageBreakEngines->push((void *)lbe, status); + // Even if we can't remember it, we can keep looking it up, so + // return it even if the push fails. + return lbe; + } + + // No engine is forthcoming for this character. Add it to the + // reject set. Create the reject break engine if needed. + if (fUnhandledBreakEngine == nullptr) { + fUnhandledBreakEngine = new UnhandledEngine(status); + if (U_SUCCESS(status) && fUnhandledBreakEngine == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + // Put it last so that scripts for which we have an engine get tried + // first. + fLanguageBreakEngines->insertElementAt(fUnhandledBreakEngine, 0, status); + // If we can't insert it, or creation failed, get rid of it + U_ASSERT(!fLanguageBreakEngines->hasDeleter()); + if (U_FAILURE(status)) { + delete fUnhandledBreakEngine; + fUnhandledBreakEngine = 0; + return nullptr; + } + } + + // Tell the reject engine about the character; at its discretion, it may + // add more than just the one character. + fUnhandledBreakEngine->handleCharacter(c); + + return fUnhandledBreakEngine; +} + +void RuleBasedBreakIterator::dumpCache() { + fBreakCache->dumpCache(); +} + +void RuleBasedBreakIterator::dumpTables() { + fData->printData(); +} + +/** + * Returns the description used to create this iterator + */ + +const UnicodeString& +RuleBasedBreakIterator::getRules() const { + if (fData != nullptr) { + return fData->getRuleSourceString(); + } else { + umtx_initOnce(gRBBIInitOnce, &rbbiInit); + return *gEmptyString; + } +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ diff --git a/deps/icu-small/source/common/rbbi_cache.cpp b/deps/icu-small/source/common/rbbi_cache.cpp index 45e02528cf9b28..0acf8cbbbc5b49 100644 --- a/deps/icu-small/source/common/rbbi_cache.cpp +++ b/deps/icu-small/source/common/rbbi_cache.cpp @@ -1,701 +1,701 @@ -// Copyright (C) 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// file: rbbi_cache.cpp - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/ubrk.h" -#include "unicode/rbbi.h" - -#include "rbbi_cache.h" - -#include "brkeng.h" -#include "cmemory.h" -#include "rbbidata.h" -#include "rbbirb.h" -#include "uassert.h" -#include "uvectr32.h" - -U_NAMESPACE_BEGIN - -/* - * DictionaryCache implementation - */ - -RuleBasedBreakIterator::DictionaryCache::DictionaryCache(RuleBasedBreakIterator *bi, UErrorCode &status) : - fBI(bi), fBreaks(status), fPositionInCache(-1), - fStart(0), fLimit(0), fFirstRuleStatusIndex(0), fOtherRuleStatusIndex(0) { -} - -RuleBasedBreakIterator::DictionaryCache::~DictionaryCache() { -} - -void RuleBasedBreakIterator::DictionaryCache::reset() { - fPositionInCache = -1; - fStart = 0; - fLimit = 0; - fFirstRuleStatusIndex = 0; - fOtherRuleStatusIndex = 0; - fBreaks.removeAllElements(); -} - -UBool RuleBasedBreakIterator::DictionaryCache::following(int32_t fromPos, int32_t *result, int32_t *statusIndex) { - if (fromPos >= fLimit || fromPos < fStart) { - fPositionInCache = -1; - return false; - } - - // Sequential iteration, move from previous boundary to the following - - int32_t r = 0; - if (fPositionInCache >= 0 && fPositionInCache < fBreaks.size() && fBreaks.elementAti(fPositionInCache) == fromPos) { - ++fPositionInCache; - if (fPositionInCache >= fBreaks.size()) { - fPositionInCache = -1; - return false; - } - r = fBreaks.elementAti(fPositionInCache); - U_ASSERT(r > fromPos); - *result = r; - *statusIndex = fOtherRuleStatusIndex; - return true; - } - - // Random indexing. Linear search for the boundary following the given position. - - for (fPositionInCache = 0; fPositionInCache < fBreaks.size(); ++fPositionInCache) { - r= fBreaks.elementAti(fPositionInCache); - if (r > fromPos) { - *result = r; - *statusIndex = fOtherRuleStatusIndex; - return true; - } - } - UPRV_UNREACHABLE_EXIT; -} - - -UBool RuleBasedBreakIterator::DictionaryCache::preceding(int32_t fromPos, int32_t *result, int32_t *statusIndex) { - if (fromPos <= fStart || fromPos > fLimit) { - fPositionInCache = -1; - return false; - } - - if (fromPos == fLimit) { - fPositionInCache = fBreaks.size() - 1; - if (fPositionInCache >= 0) { - U_ASSERT(fBreaks.elementAti(fPositionInCache) == fromPos); - } - } - - int32_t r; - if (fPositionInCache > 0 && fPositionInCache < fBreaks.size() && fBreaks.elementAti(fPositionInCache) == fromPos) { - --fPositionInCache; - r = fBreaks.elementAti(fPositionInCache); - U_ASSERT(r < fromPos); - *result = r; - *statusIndex = ( r== fStart) ? fFirstRuleStatusIndex : fOtherRuleStatusIndex; - return true; - } - - if (fPositionInCache == 0) { - fPositionInCache = -1; - return false; - } - - for (fPositionInCache = fBreaks.size()-1; fPositionInCache >= 0; --fPositionInCache) { - r = fBreaks.elementAti(fPositionInCache); - if (r < fromPos) { - *result = r; - *statusIndex = ( r == fStart) ? fFirstRuleStatusIndex : fOtherRuleStatusIndex; - return true; - } - } - UPRV_UNREACHABLE_EXIT; -} - -void RuleBasedBreakIterator::DictionaryCache::populateDictionary(int32_t startPos, int32_t endPos, - int32_t firstRuleStatus, int32_t otherRuleStatus) { - if ((endPos - startPos) <= 1) { - return; - } - - reset(); - fFirstRuleStatusIndex = firstRuleStatus; - fOtherRuleStatusIndex = otherRuleStatus; - - int32_t rangeStart = startPos; - int32_t rangeEnd = endPos; - - uint16_t category; - int32_t current; - UErrorCode status = U_ZERO_ERROR; - int32_t foundBreakCount = 0; - UText *text = &fBI->fText; - - // Loop through the text, looking for ranges of dictionary characters. - // For each span, find the appropriate break engine, and ask it to find - // any breaks within the span. - - utext_setNativeIndex(text, rangeStart); - UChar32 c = utext_current32(text); - category = ucptrie_get(fBI->fData->fTrie, c); - uint32_t dictStart = fBI->fData->fForwardTable->fDictCategoriesStart; - - while(U_SUCCESS(status)) { - while((current = (int32_t)UTEXT_GETNATIVEINDEX(text)) < rangeEnd - && (category < dictStart)) { - utext_next32(text); // TODO: cleaner loop structure. - c = utext_current32(text); - category = ucptrie_get(fBI->fData->fTrie, c); - } - if (current >= rangeEnd) { - break; - } - - // We now have a dictionary character. Get the appropriate language object - // to deal with it. - const LanguageBreakEngine *lbe = fBI->getLanguageBreakEngine(c); - - // Ask the language object if there are any breaks. It will add them to the cache and - // leave the text pointer on the other side of its range, ready to search for the next one. - if (lbe != NULL) { - foundBreakCount += lbe->findBreaks(text, rangeStart, rangeEnd, fBreaks, fBI->fIsPhraseBreaking, status); - } - - // Reload the loop variables for the next go-round - c = utext_current32(text); - category = ucptrie_get(fBI->fData->fTrie, c); - } - - // If we found breaks, ensure that the first and last entries are - // the original starting and ending position. And initialize the - // cache iteration position to the first entry. - - // printf("foundBreakCount = %d\n", foundBreakCount); - if (foundBreakCount > 0) { - U_ASSERT(foundBreakCount == fBreaks.size()); - if (startPos < fBreaks.elementAti(0)) { - // The dictionary did not place a boundary at the start of the segment of text. - // Add one now. This should not commonly happen, but it would be easy for interactions - // of the rules for dictionary segments and the break engine implementations to - // inadvertently cause it. Cover it here, just in case. - fBreaks.insertElementAt(startPos, 0, status); - } - if (endPos > fBreaks.peeki()) { - fBreaks.push(endPos, status); - } - fPositionInCache = 0; - // Note: Dictionary matching may extend beyond the original limit. - fStart = fBreaks.elementAti(0); - fLimit = fBreaks.peeki(); - } else { - // there were no language-based breaks, even though the segment contained - // dictionary characters. Subsequent attempts to fetch boundaries from the dictionary cache - // for this range will fail, and the calling code will fall back to the rule based boundaries. - } -} - - -/* - * BreakCache implementation - */ - -RuleBasedBreakIterator::BreakCache::BreakCache(RuleBasedBreakIterator *bi, UErrorCode &status) : - fBI(bi), fSideBuffer(status) { - reset(); -} - - -RuleBasedBreakIterator::BreakCache::~BreakCache() { -} - - -void RuleBasedBreakIterator::BreakCache::reset(int32_t pos, int32_t ruleStatus) { - fStartBufIdx = 0; - fEndBufIdx = 0; - fTextIdx = pos; - fBufIdx = 0; - fBoundaries[0] = pos; - fStatuses[0] = (uint16_t)ruleStatus; -} - - -int32_t RuleBasedBreakIterator::BreakCache::current() { - fBI->fPosition = fTextIdx; - fBI->fRuleStatusIndex = fStatuses[fBufIdx]; - fBI->fDone = false; - return fTextIdx; -} - - -void RuleBasedBreakIterator::BreakCache::following(int32_t startPos, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if (startPos == fTextIdx || seek(startPos) || populateNear(startPos, status)) { - // startPos is in the cache. Do a next() from that position. - // TODO: an awkward set of interactions with bi->fDone - // seek() does not clear it; it can't because of interactions with populateNear(). - // next() does not clear it in the fast-path case, where everything matters. Maybe it should. - // So clear it here, for the case where seek() succeeded on an iterator that had previously run off the end. - fBI->fDone = false; - next(); - } - return; -} - - -void RuleBasedBreakIterator::BreakCache::preceding(int32_t startPos, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if (startPos == fTextIdx || seek(startPos) || populateNear(startPos, status)) { - if (startPos == fTextIdx) { - previous(status); - } else { - // seek() leaves the BreakCache positioned at the preceding boundary - // if the requested position is between two boundaries. - // current() pushes the BreakCache position out to the BreakIterator itself. - U_ASSERT(startPos > fTextIdx); - current(); - } - } - return; -} - - -/* - * Out-of-line code for BreakCache::next(). - * Cache does not already contain the boundary - */ -void RuleBasedBreakIterator::BreakCache::nextOL() { - fBI->fDone = !populateFollowing(); - fBI->fPosition = fTextIdx; - fBI->fRuleStatusIndex = fStatuses[fBufIdx]; - return; -} - - -void RuleBasedBreakIterator::BreakCache::previous(UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - int32_t initialBufIdx = fBufIdx; - if (fBufIdx == fStartBufIdx) { - // At start of cache. Prepend to it. - populatePreceding(status); - } else { - // Cache already holds the next boundary - fBufIdx = modChunkSize(fBufIdx - 1); - fTextIdx = fBoundaries[fBufIdx]; - } - fBI->fDone = (fBufIdx == initialBufIdx); - fBI->fPosition = fTextIdx; - fBI->fRuleStatusIndex = fStatuses[fBufIdx]; - return; -} - - -UBool RuleBasedBreakIterator::BreakCache::seek(int32_t pos) { - if (pos < fBoundaries[fStartBufIdx] || pos > fBoundaries[fEndBufIdx]) { - return false; - } - if (pos == fBoundaries[fStartBufIdx]) { - // Common case: seek(0), from BreakIterator::first() - fBufIdx = fStartBufIdx; - fTextIdx = fBoundaries[fBufIdx]; - return true; - } - if (pos == fBoundaries[fEndBufIdx]) { - fBufIdx = fEndBufIdx; - fTextIdx = fBoundaries[fBufIdx]; - return true; - } - - int32_t min = fStartBufIdx; - int32_t max = fEndBufIdx; - while (min != max) { - int32_t probe = (min + max + (min>max ? CACHE_SIZE : 0)) / 2; - probe = modChunkSize(probe); - if (fBoundaries[probe] > pos) { - max = probe; - } else { - min = modChunkSize(probe + 1); - } - } - U_ASSERT(fBoundaries[max] > pos); - fBufIdx = modChunkSize(max - 1); - fTextIdx = fBoundaries[fBufIdx]; - U_ASSERT(fTextIdx <= pos); - return true; -} - - -UBool RuleBasedBreakIterator::BreakCache::populateNear(int32_t position, UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - U_ASSERT(position < fBoundaries[fStartBufIdx] || position > fBoundaries[fEndBufIdx]); - - // Add boundaries to the cache near the specified position. - // The given position need not be a boundary itself. - // The input position must be within the range of the text, and - // on a code point boundary. - // If the requested position is a break boundary, leave the iteration - // position on it. - // If the requested position is not a boundary, leave the iteration - // position on the preceding boundary and include both the - // preceding and following boundaries in the cache. - // Additional boundaries, either preceding or following, may be added - // to the cache as a side effect. - - // If the requested position is not near already cached positions, clear the existing cache, - // find a near-by boundary and begin new cache contents there. - - // Threshold for a text position to be considered near to existing cache contents. - // TODO: See issue ICU-22024 "perf tuning of Cache needed." - // This value is subject to change. See the ticket for more details. - static constexpr int32_t CACHE_NEAR = 15; - - int32_t aBoundary = -1; - int32_t ruleStatusIndex = 0; - bool retainCache = false; - if ((position > fBoundaries[fStartBufIdx] - CACHE_NEAR) && position < (fBoundaries[fEndBufIdx] + CACHE_NEAR)) { - // Requested position is near the existing cache. Retain it. - retainCache = true; - } else if (position <= CACHE_NEAR) { - // Requested position is near the start of the text. Fill cache from start, skipping - // the need to find a safe point. - retainCache = false; - aBoundary = 0; - } else { - // Requested position is not near the existing cache. - // Find a safe point to refill the cache from. - int32_t backupPos = fBI->handleSafePrevious(position); - - if (fBoundaries[fEndBufIdx] < position && fBoundaries[fEndBufIdx] >= (backupPos - CACHE_NEAR)) { - // The requested position is beyond the end of the existing cache, but the - // reverse rules produced a position near or before the cached region. - // Retain the existing cache, and fill from the end of it. - retainCache = true; - } else if (backupPos < CACHE_NEAR) { - // The safe reverse rules moved us to near the start of text. - // Take that (index 0) as the backup boundary, avoiding the complication - // (in the following block) of moving forward from the safe point to a known boundary. - // - // Retain the cache if it begins not too far from the requested position. - aBoundary = 0; - retainCache = (fBoundaries[fStartBufIdx] <= (position + CACHE_NEAR)); - } else { - // The safe reverse rules produced a position that is neither near the existing - // cache, nor near the start of text. - // Advance to the boundary following. - // 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. - retainCache = false; - fBI->fPosition = backupPos; - aBoundary = fBI->handleNext(); - if (aBoundary != UBRK_DONE && 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. - } - } - if (aBoundary == UBRK_DONE) { - // Note (Andy Heninger): I don't think this condition can occur, but it's hard - // to prove that it can't. We ran off the end of the string looking a boundary - // following a safe point; choose the end of the string as that boundary. - aBoundary = utext_nativeLength(&fBI->fText); - } - ruleStatusIndex = fBI->fRuleStatusIndex; - } - } - - if (!retainCache) { - U_ASSERT(aBoundary != -1); - 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. - - if (fBoundaries[fEndBufIdx] < position) { - // The last position in the cache precedes the requested position. - // Add following position(s) to the cache. - while (fBoundaries[fEndBufIdx] < position) { - if (!populateFollowing()) { - UPRV_UNREACHABLE_EXIT; - } - } - fBufIdx = fEndBufIdx; // Set iterator position to the end of the buffer. - fTextIdx = fBoundaries[fBufIdx]; // Required because populateFollowing may add extra boundaries. - while (fTextIdx > position) { // Move backwards to a position at or preceding the requested pos. - previous(status); - } - return true; - } - - if (fBoundaries[fStartBufIdx] > position) { - // The first position in the cache is beyond the requested position. - // back up more until we get a boundary <= the requested position. - while (fBoundaries[fStartBufIdx] > position) { - populatePreceding(status); - } - fBufIdx = fStartBufIdx; // Set iterator position to the start of the buffer. - fTextIdx = fBoundaries[fBufIdx]; // Required because populatePreceding may add extra boundaries. - while (fTextIdx < position) { // Move forwards to a position at or following the requested pos. - next(); - } - if (fTextIdx > position) { - // If position is not itself a boundary, the next() loop above will overshoot. - // Back up one, leaving cache position at the boundary preceding the requested position. - previous(status); - } - return true; - } - - U_ASSERT(fTextIdx == position); - return true; -} - - - -UBool RuleBasedBreakIterator::BreakCache::populateFollowing() { - int32_t fromPosition = fBoundaries[fEndBufIdx]; - int32_t fromRuleStatusIdx = fStatuses[fEndBufIdx]; - int32_t pos = 0; - int32_t ruleStatusIdx = 0; - - if (fBI->fDictionaryCache->following(fromPosition, &pos, &ruleStatusIdx)) { - addFollowing(pos, ruleStatusIdx, UpdateCachePosition); - return true; - } - - fBI->fPosition = fromPosition; - pos = fBI->handleNext(); - if (pos == UBRK_DONE) { - return false; - } - - ruleStatusIdx = fBI->fRuleStatusIndex; - if (fBI->fDictionaryCharCount > 0) { - // The text segment obtained from the rules includes dictionary characters. - // Subdivide it, with subdivided results going into the dictionary cache. - fBI->fDictionaryCache->populateDictionary(fromPosition, pos, fromRuleStatusIdx, ruleStatusIdx); - if (fBI->fDictionaryCache->following(fromPosition, &pos, &ruleStatusIdx)) { - addFollowing(pos, ruleStatusIdx, UpdateCachePosition); - return true; - // TODO: may want to move a sizable chunk of dictionary cache to break cache at this point. - // But be careful with interactions with populateNear(). - } - } - - // Rule based segment did not include dictionary characters. - // Or, it did contain dictionary chars, but the dictionary segmenter didn't handle them, - // meaning that we didn't take the return, above. - // Add its end point to the cache. - addFollowing(pos, ruleStatusIdx, UpdateCachePosition); - - // Add several non-dictionary boundaries at this point, to optimize straight forward iteration. - // (subsequent calls to BreakIterator::next() will take the fast path, getting cached results. - // - for (int count=0; count<6; ++count) { - pos = fBI->handleNext(); - if (pos == UBRK_DONE || fBI->fDictionaryCharCount > 0) { - break; - } - addFollowing(pos, fBI->fRuleStatusIndex, RetainCachePosition); - } - - return true; -} - - -UBool RuleBasedBreakIterator::BreakCache::populatePreceding(UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - - int32_t fromPosition = fBoundaries[fStartBufIdx]; - if (fromPosition == 0) { - return false; - } - - int32_t position = 0; - int32_t positionStatusIdx = 0; - - if (fBI->fDictionaryCache->preceding(fromPosition, &position, &positionStatusIdx)) { - addPreceding(position, positionStatusIdx, UpdateCachePosition); - return true; - } - - int32_t backupPosition = fromPosition; - - // Find a boundary somewhere preceding the first already-cached boundary - do { - backupPosition = backupPosition - 30; - if (backupPosition <= 0) { - backupPosition = 0; - } else { - backupPosition = fBI->handleSafePrevious(backupPosition); - } - if (backupPosition == UBRK_DONE || backupPosition == 0) { - position = 0; - positionStatusIdx = 0; - } else { - // 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); - - // Find boundaries between the one we just located and the first already-cached boundary - // Put them in a side buffer, because we don't yet know where they will fall in the circular cache buffer.. - - fSideBuffer.removeAllElements(); - fSideBuffer.addElement(position, status); - fSideBuffer.addElement(positionStatusIdx, status); - - do { - int32_t prevPosition = fBI->fPosition = position; - int32_t prevStatusIdx = positionStatusIdx; - position = fBI->handleNext(); - positionStatusIdx = fBI->fRuleStatusIndex; - if (position == UBRK_DONE) { - break; - } - - UBool segmentHandledByDictionary = false; - if (fBI->fDictionaryCharCount != 0) { - // Segment from the rules includes dictionary characters. - // Subdivide it, with subdivided results going into the dictionary cache. - int32_t dictSegEndPosition = position; - fBI->fDictionaryCache->populateDictionary(prevPosition, dictSegEndPosition, prevStatusIdx, positionStatusIdx); - while (fBI->fDictionaryCache->following(prevPosition, &position, &positionStatusIdx)) { - segmentHandledByDictionary = true; - U_ASSERT(position > prevPosition); - if (position >= fromPosition) { - break; - } - U_ASSERT(position <= dictSegEndPosition); - fSideBuffer.addElement(position, status); - fSideBuffer.addElement(positionStatusIdx, status); - prevPosition = position; - } - U_ASSERT(position==dictSegEndPosition || position>=fromPosition); - } - - if (!segmentHandledByDictionary && position < fromPosition) { - fSideBuffer.addElement(position, status); - fSideBuffer.addElement(positionStatusIdx, status); - } - } while (position < fromPosition); - - // Move boundaries from the side buffer to the main circular buffer. - UBool success = false; - if (!fSideBuffer.isEmpty()) { - positionStatusIdx = fSideBuffer.popi(); - position = fSideBuffer.popi(); - addPreceding(position, positionStatusIdx, UpdateCachePosition); - success = true; - } - - while (!fSideBuffer.isEmpty()) { - positionStatusIdx = fSideBuffer.popi(); - position = fSideBuffer.popi(); - if (!addPreceding(position, positionStatusIdx, RetainCachePosition)) { - // No space in circular buffer to hold a new preceding result while - // also retaining the current cache (iteration) position. - // Bailing out is safe; the cache will refill again if needed. - break; - } - } - - return success; -} - - -void RuleBasedBreakIterator::BreakCache::addFollowing(int32_t position, int32_t ruleStatusIdx, UpdatePositionValues update) { - U_ASSERT(position > fBoundaries[fEndBufIdx]); - U_ASSERT(ruleStatusIdx <= UINT16_MAX); - int32_t nextIdx = modChunkSize(fEndBufIdx + 1); - if (nextIdx == fStartBufIdx) { - fStartBufIdx = modChunkSize(fStartBufIdx + 6); // TODO: experiment. Probably revert to 1. - } - fBoundaries[nextIdx] = position; - fStatuses[nextIdx] = static_cast(ruleStatusIdx); - fEndBufIdx = nextIdx; - if (update == UpdateCachePosition) { - // Set current position to the newly added boundary. - fBufIdx = nextIdx; - fTextIdx = position; - } else { - // Retaining the original cache position. - // Check if the added boundary wraps around the buffer, and would over-write the original position. - // It's the responsibility of callers of this function to not add too many. - U_ASSERT(nextIdx != fBufIdx); - } -} - -bool RuleBasedBreakIterator::BreakCache::addPreceding(int32_t position, int32_t ruleStatusIdx, UpdatePositionValues update) { - U_ASSERT(position < fBoundaries[fStartBufIdx]); - U_ASSERT(ruleStatusIdx <= UINT16_MAX); - int32_t nextIdx = modChunkSize(fStartBufIdx - 1); - if (nextIdx == fEndBufIdx) { - if (fBufIdx == fEndBufIdx && update == RetainCachePosition) { - // Failure. The insertion of the new boundary would claim the buffer position that is the - // current iteration position. And we also want to retain the current iteration position. - // (The buffer is already completely full of entries that precede the iteration position.) - return false; - } - fEndBufIdx = modChunkSize(fEndBufIdx - 1); - } - fBoundaries[nextIdx] = position; - fStatuses[nextIdx] = static_cast(ruleStatusIdx); - fStartBufIdx = nextIdx; - if (update == UpdateCachePosition) { - fBufIdx = nextIdx; - fTextIdx = position; - } - return true; -} - - -void RuleBasedBreakIterator::BreakCache::dumpCache() { -#ifdef RBBI_DEBUG - RBBIDebugPrintf("fTextIdx:%d fBufIdx:%d\n", fTextIdx, fBufIdx); - for (int32_t i=fStartBufIdx; ; i=modChunkSize(i+1)) { - RBBIDebugPrintf("%d %d\n", i, fBoundaries[i]); - if (i == fEndBufIdx) { - break; - } - } -#endif -} - -U_NAMESPACE_END - -#endif // #if !UCONFIG_NO_BREAK_ITERATION +// Copyright (C) 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// file: rbbi_cache.cpp + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/ubrk.h" +#include "unicode/rbbi.h" + +#include "rbbi_cache.h" + +#include "brkeng.h" +#include "cmemory.h" +#include "rbbidata.h" +#include "rbbirb.h" +#include "uassert.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +/* + * DictionaryCache implementation + */ + +RuleBasedBreakIterator::DictionaryCache::DictionaryCache(RuleBasedBreakIterator *bi, UErrorCode &status) : + fBI(bi), fBreaks(status), fPositionInCache(-1), + fStart(0), fLimit(0), fFirstRuleStatusIndex(0), fOtherRuleStatusIndex(0) { +} + +RuleBasedBreakIterator::DictionaryCache::~DictionaryCache() { +} + +void RuleBasedBreakIterator::DictionaryCache::reset() { + fPositionInCache = -1; + fStart = 0; + fLimit = 0; + fFirstRuleStatusIndex = 0; + fOtherRuleStatusIndex = 0; + fBreaks.removeAllElements(); +} + +UBool RuleBasedBreakIterator::DictionaryCache::following(int32_t fromPos, int32_t *result, int32_t *statusIndex) { + if (fromPos >= fLimit || fromPos < fStart) { + fPositionInCache = -1; + return false; + } + + // Sequential iteration, move from previous boundary to the following + + int32_t r = 0; + if (fPositionInCache >= 0 && fPositionInCache < fBreaks.size() && fBreaks.elementAti(fPositionInCache) == fromPos) { + ++fPositionInCache; + if (fPositionInCache >= fBreaks.size()) { + fPositionInCache = -1; + return false; + } + r = fBreaks.elementAti(fPositionInCache); + U_ASSERT(r > fromPos); + *result = r; + *statusIndex = fOtherRuleStatusIndex; + return true; + } + + // Random indexing. Linear search for the boundary following the given position. + + for (fPositionInCache = 0; fPositionInCache < fBreaks.size(); ++fPositionInCache) { + r= fBreaks.elementAti(fPositionInCache); + if (r > fromPos) { + *result = r; + *statusIndex = fOtherRuleStatusIndex; + return true; + } + } + UPRV_UNREACHABLE_EXIT; +} + + +UBool RuleBasedBreakIterator::DictionaryCache::preceding(int32_t fromPos, int32_t *result, int32_t *statusIndex) { + if (fromPos <= fStart || fromPos > fLimit) { + fPositionInCache = -1; + return false; + } + + if (fromPos == fLimit) { + fPositionInCache = fBreaks.size() - 1; + if (fPositionInCache >= 0) { + U_ASSERT(fBreaks.elementAti(fPositionInCache) == fromPos); + } + } + + int32_t r; + if (fPositionInCache > 0 && fPositionInCache < fBreaks.size() && fBreaks.elementAti(fPositionInCache) == fromPos) { + --fPositionInCache; + r = fBreaks.elementAti(fPositionInCache); + U_ASSERT(r < fromPos); + *result = r; + *statusIndex = ( r== fStart) ? fFirstRuleStatusIndex : fOtherRuleStatusIndex; + return true; + } + + if (fPositionInCache == 0) { + fPositionInCache = -1; + return false; + } + + for (fPositionInCache = fBreaks.size()-1; fPositionInCache >= 0; --fPositionInCache) { + r = fBreaks.elementAti(fPositionInCache); + if (r < fromPos) { + *result = r; + *statusIndex = ( r == fStart) ? fFirstRuleStatusIndex : fOtherRuleStatusIndex; + return true; + } + } + UPRV_UNREACHABLE_EXIT; +} + +void RuleBasedBreakIterator::DictionaryCache::populateDictionary(int32_t startPos, int32_t endPos, + int32_t firstRuleStatus, int32_t otherRuleStatus) { + if ((endPos - startPos) <= 1) { + return; + } + + reset(); + fFirstRuleStatusIndex = firstRuleStatus; + fOtherRuleStatusIndex = otherRuleStatus; + + int32_t rangeStart = startPos; + int32_t rangeEnd = endPos; + + uint16_t category; + int32_t current; + UErrorCode status = U_ZERO_ERROR; + int32_t foundBreakCount = 0; + UText *text = &fBI->fText; + + // Loop through the text, looking for ranges of dictionary characters. + // For each span, find the appropriate break engine, and ask it to find + // any breaks within the span. + + utext_setNativeIndex(text, rangeStart); + UChar32 c = utext_current32(text); + category = ucptrie_get(fBI->fData->fTrie, c); + uint32_t dictStart = fBI->fData->fForwardTable->fDictCategoriesStart; + + while(U_SUCCESS(status)) { + while((current = (int32_t)UTEXT_GETNATIVEINDEX(text)) < rangeEnd + && (category < dictStart)) { + utext_next32(text); // TODO: cleaner loop structure. + c = utext_current32(text); + category = ucptrie_get(fBI->fData->fTrie, c); + } + if (current >= rangeEnd) { + break; + } + + // We now have a dictionary character. Get the appropriate language object + // to deal with it. + const LanguageBreakEngine *lbe = fBI->getLanguageBreakEngine(c); + + // Ask the language object if there are any breaks. It will add them to the cache and + // leave the text pointer on the other side of its range, ready to search for the next one. + if (lbe != nullptr) { + foundBreakCount += lbe->findBreaks(text, rangeStart, rangeEnd, fBreaks, fBI->fIsPhraseBreaking, status); + } + + // Reload the loop variables for the next go-round + c = utext_current32(text); + category = ucptrie_get(fBI->fData->fTrie, c); + } + + // If we found breaks, ensure that the first and last entries are + // the original starting and ending position. And initialize the + // cache iteration position to the first entry. + + // printf("foundBreakCount = %d\n", foundBreakCount); + if (foundBreakCount > 0) { + U_ASSERT(foundBreakCount == fBreaks.size()); + if (startPos < fBreaks.elementAti(0)) { + // The dictionary did not place a boundary at the start of the segment of text. + // Add one now. This should not commonly happen, but it would be easy for interactions + // of the rules for dictionary segments and the break engine implementations to + // inadvertently cause it. Cover it here, just in case. + fBreaks.insertElementAt(startPos, 0, status); + } + if (endPos > fBreaks.peeki()) { + fBreaks.push(endPos, status); + } + fPositionInCache = 0; + // Note: Dictionary matching may extend beyond the original limit. + fStart = fBreaks.elementAti(0); + fLimit = fBreaks.peeki(); + } else { + // there were no language-based breaks, even though the segment contained + // dictionary characters. Subsequent attempts to fetch boundaries from the dictionary cache + // for this range will fail, and the calling code will fall back to the rule based boundaries. + } +} + + +/* + * BreakCache implementation + */ + +RuleBasedBreakIterator::BreakCache::BreakCache(RuleBasedBreakIterator *bi, UErrorCode &status) : + fBI(bi), fSideBuffer(status) { + reset(); +} + + +RuleBasedBreakIterator::BreakCache::~BreakCache() { +} + + +void RuleBasedBreakIterator::BreakCache::reset(int32_t pos, int32_t ruleStatus) { + fStartBufIdx = 0; + fEndBufIdx = 0; + fTextIdx = pos; + fBufIdx = 0; + fBoundaries[0] = pos; + fStatuses[0] = (uint16_t)ruleStatus; +} + + +int32_t RuleBasedBreakIterator::BreakCache::current() { + fBI->fPosition = fTextIdx; + fBI->fRuleStatusIndex = fStatuses[fBufIdx]; + fBI->fDone = false; + return fTextIdx; +} + + +void RuleBasedBreakIterator::BreakCache::following(int32_t startPos, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + if (startPos == fTextIdx || seek(startPos) || populateNear(startPos, status)) { + // startPos is in the cache. Do a next() from that position. + // TODO: an awkward set of interactions with bi->fDone + // seek() does not clear it; it can't because of interactions with populateNear(). + // next() does not clear it in the fast-path case, where everything matters. Maybe it should. + // So clear it here, for the case where seek() succeeded on an iterator that had previously run off the end. + fBI->fDone = false; + next(); + } + return; +} + + +void RuleBasedBreakIterator::BreakCache::preceding(int32_t startPos, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + if (startPos == fTextIdx || seek(startPos) || populateNear(startPos, status)) { + if (startPos == fTextIdx) { + previous(status); + } else { + // seek() leaves the BreakCache positioned at the preceding boundary + // if the requested position is between two boundaries. + // current() pushes the BreakCache position out to the BreakIterator itself. + U_ASSERT(startPos > fTextIdx); + current(); + } + } + return; +} + + +/* + * Out-of-line code for BreakCache::next(). + * Cache does not already contain the boundary + */ +void RuleBasedBreakIterator::BreakCache::nextOL() { + fBI->fDone = !populateFollowing(); + fBI->fPosition = fTextIdx; + fBI->fRuleStatusIndex = fStatuses[fBufIdx]; + return; +} + + +void RuleBasedBreakIterator::BreakCache::previous(UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + int32_t initialBufIdx = fBufIdx; + if (fBufIdx == fStartBufIdx) { + // At start of cache. Prepend to it. + populatePreceding(status); + } else { + // Cache already holds the next boundary + fBufIdx = modChunkSize(fBufIdx - 1); + fTextIdx = fBoundaries[fBufIdx]; + } + fBI->fDone = (fBufIdx == initialBufIdx); + fBI->fPosition = fTextIdx; + fBI->fRuleStatusIndex = fStatuses[fBufIdx]; + return; +} + + +UBool RuleBasedBreakIterator::BreakCache::seek(int32_t pos) { + if (pos < fBoundaries[fStartBufIdx] || pos > fBoundaries[fEndBufIdx]) { + return false; + } + if (pos == fBoundaries[fStartBufIdx]) { + // Common case: seek(0), from BreakIterator::first() + fBufIdx = fStartBufIdx; + fTextIdx = fBoundaries[fBufIdx]; + return true; + } + if (pos == fBoundaries[fEndBufIdx]) { + fBufIdx = fEndBufIdx; + fTextIdx = fBoundaries[fBufIdx]; + return true; + } + + int32_t min = fStartBufIdx; + int32_t max = fEndBufIdx; + while (min != max) { + int32_t probe = (min + max + (min>max ? CACHE_SIZE : 0)) / 2; + probe = modChunkSize(probe); + if (fBoundaries[probe] > pos) { + max = probe; + } else { + min = modChunkSize(probe + 1); + } + } + U_ASSERT(fBoundaries[max] > pos); + fBufIdx = modChunkSize(max - 1); + fTextIdx = fBoundaries[fBufIdx]; + U_ASSERT(fTextIdx <= pos); + return true; +} + + +UBool RuleBasedBreakIterator::BreakCache::populateNear(int32_t position, UErrorCode &status) { + if (U_FAILURE(status)) { + return false; + } + U_ASSERT(position < fBoundaries[fStartBufIdx] || position > fBoundaries[fEndBufIdx]); + + // Add boundaries to the cache near the specified position. + // The given position need not be a boundary itself. + // The input position must be within the range of the text, and + // on a code point boundary. + // If the requested position is a break boundary, leave the iteration + // position on it. + // If the requested position is not a boundary, leave the iteration + // position on the preceding boundary and include both the + // preceding and following boundaries in the cache. + // Additional boundaries, either preceding or following, may be added + // to the cache as a side effect. + + // If the requested position is not near already cached positions, clear the existing cache, + // find a near-by boundary and begin new cache contents there. + + // Threshold for a text position to be considered near to existing cache contents. + // TODO: See issue ICU-22024 "perf tuning of Cache needed." + // This value is subject to change. See the ticket for more details. + static constexpr int32_t CACHE_NEAR = 15; + + int32_t aBoundary = -1; + int32_t ruleStatusIndex = 0; + bool retainCache = false; + if ((position > fBoundaries[fStartBufIdx] - CACHE_NEAR) && position < (fBoundaries[fEndBufIdx] + CACHE_NEAR)) { + // Requested position is near the existing cache. Retain it. + retainCache = true; + } else if (position <= CACHE_NEAR) { + // Requested position is near the start of the text. Fill cache from start, skipping + // the need to find a safe point. + retainCache = false; + aBoundary = 0; + } else { + // Requested position is not near the existing cache. + // Find a safe point to refill the cache from. + int32_t backupPos = fBI->handleSafePrevious(position); + + if (fBoundaries[fEndBufIdx] < position && fBoundaries[fEndBufIdx] >= (backupPos - CACHE_NEAR)) { + // The requested position is beyond the end of the existing cache, but the + // reverse rules produced a position near or before the cached region. + // Retain the existing cache, and fill from the end of it. + retainCache = true; + } else if (backupPos < CACHE_NEAR) { + // The safe reverse rules moved us to near the start of text. + // Take that (index 0) as the backup boundary, avoiding the complication + // (in the following block) of moving forward from the safe point to a known boundary. + // + // Retain the cache if it begins not too far from the requested position. + aBoundary = 0; + retainCache = (fBoundaries[fStartBufIdx] <= (position + CACHE_NEAR)); + } else { + // The safe reverse rules produced a position that is neither near the existing + // cache, nor near the start of text. + // Advance to the boundary following. + // 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. + retainCache = false; + fBI->fPosition = backupPos; + aBoundary = fBI->handleNext(); + if (aBoundary != UBRK_DONE && 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. + } + } + if (aBoundary == UBRK_DONE) { + // Note (Andy Heninger): I don't think this condition can occur, but it's hard + // to prove that it can't. We ran off the end of the string looking a boundary + // following a safe point; choose the end of the string as that boundary. + aBoundary = utext_nativeLength(&fBI->fText); + } + ruleStatusIndex = fBI->fRuleStatusIndex; + } + } + + if (!retainCache) { + U_ASSERT(aBoundary != -1); + 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. + + if (fBoundaries[fEndBufIdx] < position) { + // The last position in the cache precedes the requested position. + // Add following position(s) to the cache. + while (fBoundaries[fEndBufIdx] < position) { + if (!populateFollowing()) { + UPRV_UNREACHABLE_EXIT; + } + } + fBufIdx = fEndBufIdx; // Set iterator position to the end of the buffer. + fTextIdx = fBoundaries[fBufIdx]; // Required because populateFollowing may add extra boundaries. + while (fTextIdx > position) { // Move backwards to a position at or preceding the requested pos. + previous(status); + } + return true; + } + + if (fBoundaries[fStartBufIdx] > position) { + // The first position in the cache is beyond the requested position. + // back up more until we get a boundary <= the requested position. + while (fBoundaries[fStartBufIdx] > position) { + populatePreceding(status); + } + fBufIdx = fStartBufIdx; // Set iterator position to the start of the buffer. + fTextIdx = fBoundaries[fBufIdx]; // Required because populatePreceding may add extra boundaries. + while (fTextIdx < position) { // Move forwards to a position at or following the requested pos. + next(); + } + if (fTextIdx > position) { + // If position is not itself a boundary, the next() loop above will overshoot. + // Back up one, leaving cache position at the boundary preceding the requested position. + previous(status); + } + return true; + } + + U_ASSERT(fTextIdx == position); + return true; +} + + + +UBool RuleBasedBreakIterator::BreakCache::populateFollowing() { + int32_t fromPosition = fBoundaries[fEndBufIdx]; + int32_t fromRuleStatusIdx = fStatuses[fEndBufIdx]; + int32_t pos = 0; + int32_t ruleStatusIdx = 0; + + if (fBI->fDictionaryCache->following(fromPosition, &pos, &ruleStatusIdx)) { + addFollowing(pos, ruleStatusIdx, UpdateCachePosition); + return true; + } + + fBI->fPosition = fromPosition; + pos = fBI->handleNext(); + if (pos == UBRK_DONE) { + return false; + } + + ruleStatusIdx = fBI->fRuleStatusIndex; + if (fBI->fDictionaryCharCount > 0) { + // The text segment obtained from the rules includes dictionary characters. + // Subdivide it, with subdivided results going into the dictionary cache. + fBI->fDictionaryCache->populateDictionary(fromPosition, pos, fromRuleStatusIdx, ruleStatusIdx); + if (fBI->fDictionaryCache->following(fromPosition, &pos, &ruleStatusIdx)) { + addFollowing(pos, ruleStatusIdx, UpdateCachePosition); + return true; + // TODO: may want to move a sizable chunk of dictionary cache to break cache at this point. + // But be careful with interactions with populateNear(). + } + } + + // Rule based segment did not include dictionary characters. + // Or, it did contain dictionary chars, but the dictionary segmenter didn't handle them, + // meaning that we didn't take the return, above. + // Add its end point to the cache. + addFollowing(pos, ruleStatusIdx, UpdateCachePosition); + + // Add several non-dictionary boundaries at this point, to optimize straight forward iteration. + // (subsequent calls to BreakIterator::next() will take the fast path, getting cached results. + // + for (int count=0; count<6; ++count) { + pos = fBI->handleNext(); + if (pos == UBRK_DONE || fBI->fDictionaryCharCount > 0) { + break; + } + addFollowing(pos, fBI->fRuleStatusIndex, RetainCachePosition); + } + + return true; +} + + +UBool RuleBasedBreakIterator::BreakCache::populatePreceding(UErrorCode &status) { + if (U_FAILURE(status)) { + return false; + } + + int32_t fromPosition = fBoundaries[fStartBufIdx]; + if (fromPosition == 0) { + return false; + } + + int32_t position = 0; + int32_t positionStatusIdx = 0; + + if (fBI->fDictionaryCache->preceding(fromPosition, &position, &positionStatusIdx)) { + addPreceding(position, positionStatusIdx, UpdateCachePosition); + return true; + } + + int32_t backupPosition = fromPosition; + + // Find a boundary somewhere preceding the first already-cached boundary + do { + backupPosition = backupPosition - 30; + if (backupPosition <= 0) { + backupPosition = 0; + } else { + backupPosition = fBI->handleSafePrevious(backupPosition); + } + if (backupPosition == UBRK_DONE || backupPosition == 0) { + position = 0; + positionStatusIdx = 0; + } else { + // 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); + + // Find boundaries between the one we just located and the first already-cached boundary + // Put them in a side buffer, because we don't yet know where they will fall in the circular cache buffer.. + + fSideBuffer.removeAllElements(); + fSideBuffer.addElement(position, status); + fSideBuffer.addElement(positionStatusIdx, status); + + do { + int32_t prevPosition = fBI->fPosition = position; + int32_t prevStatusIdx = positionStatusIdx; + position = fBI->handleNext(); + positionStatusIdx = fBI->fRuleStatusIndex; + if (position == UBRK_DONE) { + break; + } + + UBool segmentHandledByDictionary = false; + if (fBI->fDictionaryCharCount != 0) { + // Segment from the rules includes dictionary characters. + // Subdivide it, with subdivided results going into the dictionary cache. + int32_t dictSegEndPosition = position; + fBI->fDictionaryCache->populateDictionary(prevPosition, dictSegEndPosition, prevStatusIdx, positionStatusIdx); + while (fBI->fDictionaryCache->following(prevPosition, &position, &positionStatusIdx)) { + segmentHandledByDictionary = true; + U_ASSERT(position > prevPosition); + if (position >= fromPosition) { + break; + } + U_ASSERT(position <= dictSegEndPosition); + fSideBuffer.addElement(position, status); + fSideBuffer.addElement(positionStatusIdx, status); + prevPosition = position; + } + U_ASSERT(position==dictSegEndPosition || position>=fromPosition); + } + + if (!segmentHandledByDictionary && position < fromPosition) { + fSideBuffer.addElement(position, status); + fSideBuffer.addElement(positionStatusIdx, status); + } + } while (position < fromPosition); + + // Move boundaries from the side buffer to the main circular buffer. + UBool success = false; + if (!fSideBuffer.isEmpty()) { + positionStatusIdx = fSideBuffer.popi(); + position = fSideBuffer.popi(); + addPreceding(position, positionStatusIdx, UpdateCachePosition); + success = true; + } + + while (!fSideBuffer.isEmpty()) { + positionStatusIdx = fSideBuffer.popi(); + position = fSideBuffer.popi(); + if (!addPreceding(position, positionStatusIdx, RetainCachePosition)) { + // No space in circular buffer to hold a new preceding result while + // also retaining the current cache (iteration) position. + // Bailing out is safe; the cache will refill again if needed. + break; + } + } + + return success; +} + + +void RuleBasedBreakIterator::BreakCache::addFollowing(int32_t position, int32_t ruleStatusIdx, UpdatePositionValues update) { + U_ASSERT(position > fBoundaries[fEndBufIdx]); + U_ASSERT(ruleStatusIdx <= UINT16_MAX); + int32_t nextIdx = modChunkSize(fEndBufIdx + 1); + if (nextIdx == fStartBufIdx) { + fStartBufIdx = modChunkSize(fStartBufIdx + 6); // TODO: experiment. Probably revert to 1. + } + fBoundaries[nextIdx] = position; + fStatuses[nextIdx] = static_cast(ruleStatusIdx); + fEndBufIdx = nextIdx; + if (update == UpdateCachePosition) { + // Set current position to the newly added boundary. + fBufIdx = nextIdx; + fTextIdx = position; + } else { + // Retaining the original cache position. + // Check if the added boundary wraps around the buffer, and would over-write the original position. + // It's the responsibility of callers of this function to not add too many. + U_ASSERT(nextIdx != fBufIdx); + } +} + +bool RuleBasedBreakIterator::BreakCache::addPreceding(int32_t position, int32_t ruleStatusIdx, UpdatePositionValues update) { + U_ASSERT(position < fBoundaries[fStartBufIdx]); + U_ASSERT(ruleStatusIdx <= UINT16_MAX); + int32_t nextIdx = modChunkSize(fStartBufIdx - 1); + if (nextIdx == fEndBufIdx) { + if (fBufIdx == fEndBufIdx && update == RetainCachePosition) { + // Failure. The insertion of the new boundary would claim the buffer position that is the + // current iteration position. And we also want to retain the current iteration position. + // (The buffer is already completely full of entries that precede the iteration position.) + return false; + } + fEndBufIdx = modChunkSize(fEndBufIdx - 1); + } + fBoundaries[nextIdx] = position; + fStatuses[nextIdx] = static_cast(ruleStatusIdx); + fStartBufIdx = nextIdx; + if (update == UpdateCachePosition) { + fBufIdx = nextIdx; + fTextIdx = position; + } + return true; +} + + +void RuleBasedBreakIterator::BreakCache::dumpCache() { +#ifdef RBBI_DEBUG + RBBIDebugPrintf("fTextIdx:%d fBufIdx:%d\n", fTextIdx, fBufIdx); + for (int32_t i=fStartBufIdx; ; i=modChunkSize(i+1)) { + RBBIDebugPrintf("%d %d\n", i, fBoundaries[i]); + if (i == fEndBufIdx) { + break; + } + } +#endif +} + +U_NAMESPACE_END + +#endif // #if !UCONFIG_NO_BREAK_ITERATION diff --git a/deps/icu-small/source/common/rbbi_cache.h b/deps/icu-small/source/common/rbbi_cache.h index 597312e85c4536..97901f438d3207 100644 --- a/deps/icu-small/source/common/rbbi_cache.h +++ b/deps/icu-small/source/common/rbbi_cache.h @@ -1,203 +1,203 @@ -// Copyright (C) 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// file: rbbi_cache.h -// -#ifndef RBBI_CACHE_H -#define RBBI_CACHE_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/rbbi.h" -#include "unicode/uobject.h" - -#include "uvectr32.h" - -U_NAMESPACE_BEGIN - -/* DictionaryCache stores the boundaries obtained from a run of dictionary characters. - * Dictionary boundaries are moved first to this cache, then from here - * to the main BreakCache, where they may inter-leave with non-dictionary - * boundaries. The public BreakIterator API always fetches directly - * from the main BreakCache, not from here. - * - * In common situations, the number of boundaries in a single dictionary run - * should be quite small, it will be terminated by punctuation, spaces, - * or any other non-dictionary characters. The main BreakCache may end - * up with boundaries from multiple dictionary based runs. - * - * The boundaries are stored in a simple ArrayList (vector), with the - * assumption that they will be accessed sequentially. - */ -class RuleBasedBreakIterator::DictionaryCache: public UMemory { - public: - DictionaryCache(RuleBasedBreakIterator *bi, UErrorCode &status); - ~DictionaryCache(); - - void reset(); - - UBool following(int32_t fromPos, int32_t *pos, int32_t *statusIndex); - UBool preceding(int32_t fromPos, int32_t *pos, int32_t *statusIndex); - - /** - * Populate the cache with the dictionary based boundaries within a region of text. - * @param startPos The start position of a range of text - * @param endPos The end position of a range of text - * @param firstRuleStatus The rule status index that applies to the break at startPos - * @param otherRuleStatus The rule status index that applies to boundaries other than startPos - * @internal - */ - void populateDictionary(int32_t startPos, int32_t endPos, - int32_t firstRuleStatus, int32_t otherRuleStatus); - - - - RuleBasedBreakIterator *fBI; - - UVector32 fBreaks; // A vector containing the boundaries. - int32_t fPositionInCache; // Index in fBreaks of last boundary returned by following() - // or preceding(). Optimizes sequential access. - int32_t fStart; // Text position of first boundary in cache. - int32_t fLimit; // Last boundary in cache. Which is the limit of the - // text segment being handled by the dictionary. - int32_t fFirstRuleStatusIndex; // Rule status info for first boundary. - int32_t fOtherRuleStatusIndex; // Rule status info for 2nd through last boundaries. -}; - - -/* - * class BreakCache - * - * Cache of break boundary positions and rule status values. - * Break iterator API functions, next(), previous(), etc., will use cached results - * when possible, and otherwise cache new results as they are obtained. - * - * Uniformly caches both dictionary and rule based (non-dictionary) boundaries. - * - * The cache is implemented as a single circular buffer. - */ - -/* - * size of the circular cache buffer. - */ - -class RuleBasedBreakIterator::BreakCache: public UMemory { - public: - BreakCache(RuleBasedBreakIterator *bi, UErrorCode &status); - virtual ~BreakCache(); - void reset(int32_t pos = 0, int32_t ruleStatus = 0); - void next() { if (fBufIdx == fEndBufIdx) { - nextOL(); - } else { - fBufIdx = modChunkSize(fBufIdx + 1); - fTextIdx = fBI->fPosition = fBoundaries[fBufIdx]; - fBI->fRuleStatusIndex = fStatuses[fBufIdx]; - } - } - - - void nextOL(); - void previous(UErrorCode &status); - - // Move the iteration state to the position following the startPosition. - // Input position must be pinned to the input length. - void following(int32_t startPosition, UErrorCode &status); - - void preceding(int32_t startPosition, UErrorCode &status); - - /* - * Update the state of the public BreakIterator (fBI) to reflect the - * current state of the break iterator cache (this). - */ - int32_t current(); - - /** - * Add boundaries to the cache near the specified position. - * The given position need not be a boundary itself. - * The input position must be within the range of the text, and - * on a code point boundary. - * If the requested position is a break boundary, leave the iteration - * position on it. - * If the requested position is not a boundary, leave the iteration - * position on the preceding boundary and include both the - * preceding and following boundaries in the cache. - * Additional boundaries, either preceding or following, may be added - * to the cache as a side effect. - * - * Return false if the operation failed. - */ - UBool populateNear(int32_t position, UErrorCode &status); - - /** - * Add boundary(s) to the cache following the current last boundary. - * Return false if at the end of the text, and no more boundaries can be added. - * Leave iteration position at the first newly added boundary, or unchanged if no boundary was added. - */ - UBool populateFollowing(); - - /** - * Add one or more boundaries to the cache preceding the first currently cached boundary. - * Leave the iteration position on the first added boundary. - * Return false if no boundaries could be added (if at the start of the text.) - */ - UBool populatePreceding(UErrorCode &status); - - enum UpdatePositionValues { - RetainCachePosition = 0, - UpdateCachePosition = 1 - }; - - /* - * Add the boundary following the current position. - * The current position can be left as it was, or changed to the newly added boundary, - * as specified by the update parameter. - */ - void addFollowing(int32_t position, int32_t ruleStatusIdx, UpdatePositionValues update); - - - /* - * Add the boundary preceding the current position. - * The current position can be left as it was, or changed to the newly added boundary, - * as specified by the update parameter. - */ - bool addPreceding(int32_t position, int32_t ruleStatusIdx, UpdatePositionValues update); - - /** - * Set the cache position to the specified position, or, if the position - * falls between to cached boundaries, to the preceding boundary. - * Fails if the requested position is outside of the range of boundaries currently held by the cache. - * The startPosition must be on a code point boundary. - * - * Return true if successful, false if the specified position is after - * the last cached boundary or before the first. - */ - UBool seek(int32_t startPosition); - - void dumpCache(); - - private: - static inline int32_t modChunkSize(int index) { return index & (CACHE_SIZE - 1); } - - static constexpr int32_t CACHE_SIZE = 128; - static_assert((CACHE_SIZE & (CACHE_SIZE-1)) == 0, "CACHE_SIZE must be power of two."); - - RuleBasedBreakIterator *fBI; - int32_t fStartBufIdx; - int32_t fEndBufIdx; // inclusive - - int32_t fTextIdx; - int32_t fBufIdx; - - int32_t fBoundaries[CACHE_SIZE]; - uint16_t fStatuses[CACHE_SIZE]; - - UVector32 fSideBuffer; -}; - -U_NAMESPACE_END - -#endif // #if !UCONFIG_NO_BREAK_ITERATION - -#endif // RBBI_CACHE_H +// Copyright (C) 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// file: rbbi_cache.h +// +#ifndef RBBI_CACHE_H +#define RBBI_CACHE_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/rbbi.h" +#include "unicode/uobject.h" + +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +/* DictionaryCache stores the boundaries obtained from a run of dictionary characters. + * Dictionary boundaries are moved first to this cache, then from here + * to the main BreakCache, where they may inter-leave with non-dictionary + * boundaries. The public BreakIterator API always fetches directly + * from the main BreakCache, not from here. + * + * In common situations, the number of boundaries in a single dictionary run + * should be quite small, it will be terminated by punctuation, spaces, + * or any other non-dictionary characters. The main BreakCache may end + * up with boundaries from multiple dictionary based runs. + * + * The boundaries are stored in a simple ArrayList (vector), with the + * assumption that they will be accessed sequentially. + */ +class RuleBasedBreakIterator::DictionaryCache: public UMemory { + public: + DictionaryCache(RuleBasedBreakIterator *bi, UErrorCode &status); + ~DictionaryCache(); + + void reset(); + + UBool following(int32_t fromPos, int32_t *pos, int32_t *statusIndex); + UBool preceding(int32_t fromPos, int32_t *pos, int32_t *statusIndex); + + /** + * Populate the cache with the dictionary based boundaries within a region of text. + * @param startPos The start position of a range of text + * @param endPos The end position of a range of text + * @param firstRuleStatus The rule status index that applies to the break at startPos + * @param otherRuleStatus The rule status index that applies to boundaries other than startPos + * @internal + */ + void populateDictionary(int32_t startPos, int32_t endPos, + int32_t firstRuleStatus, int32_t otherRuleStatus); + + + + RuleBasedBreakIterator *fBI; + + UVector32 fBreaks; // A vector containing the boundaries. + int32_t fPositionInCache; // Index in fBreaks of last boundary returned by following() + // or preceding(). Optimizes sequential access. + int32_t fStart; // Text position of first boundary in cache. + int32_t fLimit; // Last boundary in cache. Which is the limit of the + // text segment being handled by the dictionary. + int32_t fFirstRuleStatusIndex; // Rule status info for first boundary. + int32_t fOtherRuleStatusIndex; // Rule status info for 2nd through last boundaries. +}; + + +/* + * class BreakCache + * + * Cache of break boundary positions and rule status values. + * Break iterator API functions, next(), previous(), etc., will use cached results + * when possible, and otherwise cache new results as they are obtained. + * + * Uniformly caches both dictionary and rule based (non-dictionary) boundaries. + * + * The cache is implemented as a single circular buffer. + */ + +/* + * size of the circular cache buffer. + */ + +class RuleBasedBreakIterator::BreakCache: public UMemory { + public: + BreakCache(RuleBasedBreakIterator *bi, UErrorCode &status); + virtual ~BreakCache(); + void reset(int32_t pos = 0, int32_t ruleStatus = 0); + void next() { if (fBufIdx == fEndBufIdx) { + nextOL(); + } else { + fBufIdx = modChunkSize(fBufIdx + 1); + fTextIdx = fBI->fPosition = fBoundaries[fBufIdx]; + fBI->fRuleStatusIndex = fStatuses[fBufIdx]; + } + } + + + void nextOL(); + void previous(UErrorCode &status); + + // Move the iteration state to the position following the startPosition. + // Input position must be pinned to the input length. + void following(int32_t startPosition, UErrorCode &status); + + void preceding(int32_t startPosition, UErrorCode &status); + + /* + * Update the state of the public BreakIterator (fBI) to reflect the + * current state of the break iterator cache (this). + */ + int32_t current(); + + /** + * Add boundaries to the cache near the specified position. + * The given position need not be a boundary itself. + * The input position must be within the range of the text, and + * on a code point boundary. + * If the requested position is a break boundary, leave the iteration + * position on it. + * If the requested position is not a boundary, leave the iteration + * position on the preceding boundary and include both the + * preceding and following boundaries in the cache. + * Additional boundaries, either preceding or following, may be added + * to the cache as a side effect. + * + * Return false if the operation failed. + */ + UBool populateNear(int32_t position, UErrorCode &status); + + /** + * Add boundary(s) to the cache following the current last boundary. + * Return false if at the end of the text, and no more boundaries can be added. + * Leave iteration position at the first newly added boundary, or unchanged if no boundary was added. + */ + UBool populateFollowing(); + + /** + * Add one or more boundaries to the cache preceding the first currently cached boundary. + * Leave the iteration position on the first added boundary. + * Return false if no boundaries could be added (if at the start of the text.) + */ + UBool populatePreceding(UErrorCode &status); + + enum UpdatePositionValues { + RetainCachePosition = 0, + UpdateCachePosition = 1 + }; + + /* + * Add the boundary following the current position. + * The current position can be left as it was, or changed to the newly added boundary, + * as specified by the update parameter. + */ + void addFollowing(int32_t position, int32_t ruleStatusIdx, UpdatePositionValues update); + + + /* + * Add the boundary preceding the current position. + * The current position can be left as it was, or changed to the newly added boundary, + * as specified by the update parameter. + */ + bool addPreceding(int32_t position, int32_t ruleStatusIdx, UpdatePositionValues update); + + /** + * Set the cache position to the specified position, or, if the position + * falls between to cached boundaries, to the preceding boundary. + * Fails if the requested position is outside of the range of boundaries currently held by the cache. + * The startPosition must be on a code point boundary. + * + * Return true if successful, false if the specified position is after + * the last cached boundary or before the first. + */ + UBool seek(int32_t startPosition); + + void dumpCache(); + + private: + static inline int32_t modChunkSize(int index) { return index & (CACHE_SIZE - 1); } + + static constexpr int32_t CACHE_SIZE = 128; + static_assert((CACHE_SIZE & (CACHE_SIZE-1)) == 0, "CACHE_SIZE must be power of two."); + + RuleBasedBreakIterator *fBI; + int32_t fStartBufIdx; + int32_t fEndBufIdx; // inclusive + + int32_t fTextIdx; + int32_t fBufIdx; + + int32_t fBoundaries[CACHE_SIZE]; + uint16_t fStatuses[CACHE_SIZE]; + + UVector32 fSideBuffer; +}; + +U_NAMESPACE_END + +#endif // #if !UCONFIG_NO_BREAK_ITERATION + +#endif // RBBI_CACHE_H diff --git a/deps/icu-small/source/common/rbbicst.pl b/deps/icu-small/source/common/rbbicst.pl old mode 100755 new mode 100644 index 65907b0f63f8cc..a233f7158310f0 --- a/deps/icu-small/source/common/rbbicst.pl +++ b/deps/icu-small/source/common/rbbicst.pl @@ -1,459 +1,459 @@ -#************************************************************************** -# Copyright (C) 2016 and later: Unicode, Inc. and others. -# License & terms of use: http://www.unicode.org/copyright.html -#************************************************************************** -#************************************************************************** -# Copyright (C) 2002-2016 International Business Machines Corporation -# and others. All rights reserved. -#************************************************************************** -# -# rbbicst Compile the RBBI rule parser state table data into initialized C data. -# Usage: -# cd icu/source/common -# perl rbbicst.pl < rbbirpt.txt > rbbirpt.h -# perl rbbicst.pl -j < rbbirpt.txt > RBBIRuleParseTable.java -# -# The output file, rbbrpt.h, is included by some of the .cpp rbbi -# implementation files. This perl script is NOT run as part -# of a normal ICU build. It is run by hand when needed, and the -# rbbirpt.h generated file is put back into cvs. -# -# See rbbirpt.txt for a description of the input format for this script. -# - -if ($ARGV[0] eq "-j") { - $javaOutput = 1; - shift @ARGV; -} - - -$num_states = 1; # Always the state number for the line being compiled. -$line_num = 0; # The line number in the input file. - -$states{"pop"} = 255; # Add the "pop" to the list of defined state names. - # This prevents any state from being labelled with "pop", - # and resolves references to "pop" in the next state field. - -line_loop: while (<>) { - chomp(); - $line = $_; - @fields = split(); - $line_num++; - - # Remove # comments, which are any fields beginning with a #, plus all - # that follow on the line. - for ($i=0; $i<@fields; $i++) { - if ($fields[$i] =~ /^#/) { - @fields = @fields[0 .. $i-1]; - last; - } - } - # ignore blank lines, and those with no fields left after stripping comments.. - if (@fields == 0) { - next; - } - - # - # State Label: handling. - # Does the first token end with a ":"? If so, it's the name of a state. - # Put in a hash, together with the current state number, - # so that we can later look up the number from the name. - # - if (@fields[0] =~ /.*:$/) { - $state_name = @fields[0]; - $state_name =~ s/://; # strip off the colon from the state name. - - if ($states{$state_name} != 0) { - print " rbbicst: at line $line-num duplicate definition of state $state_name\n"; - } - $states{$state_name} = $num_states; - $stateNames[$num_states] = $state_name; - - # if the label was the only thing on this line, go on to the next line, - # otherwise assume that a state definition is on the same line and fall through. - if (@fields == 1) { - next line_loop; - } - shift @fields; # shift off label field in preparation - # for handling the rest of the line. - } - - # - # State Transition line. - # syntax is this, - # character [n] target-state [^push-state] [function-name] - # where - # [something] is an optional something - # character is either a single quoted character e.g. '[' - # or a name of a character class, e.g. white_space - # - - $state_line_num[$num_states] = $line_num; # remember line number with each state - # so we can make better error messages later. - # - # First field, character class or literal character for this transition. - # - if ($fields[0] =~ /^'.'$/) { - # We've got a quoted literal character. - $state_literal_chars[$num_states] = $fields[0]; - $state_literal_chars[$num_states] =~ s/'//g; - } else { - # We've got the name of a character class. - $state_char_class[$num_states] = $fields[0]; - if ($fields[0] =~ /[\W]/) { - print " rbbicsts: at line $line_num, bad character literal or character class name.\n"; - print " scanning $fields[0]\n"; - exit(-1); - } - } - shift @fields; - - # - # do the 'n' flag - # - $state_flag[$num_states] = "false"; - if ($fields[0] eq "n") { - $state_flag[$num_states] = "true"; - shift @fields; - } - - # - # do the destination state. - # - $state_dest_state[$num_states] = $fields[0]; - if ($fields[0] eq "") { - print " rbbicsts: at line $line_num, destination state missing.\n"; - exit(-1); - } - shift @fields; - - # - # do the push state, if present. - # - if ($fields[0] =~ /^\^/) { - $fields[0] =~ s/^\^//; - $state_push_state[$num_states] = $fields[0]; - if ($fields[0] eq "" ) { - print " rbbicsts: at line $line_num, expected state after ^ (no spaces).\n"; - exit(-1); - } - shift @fields; - } - - # - # Lastly, do the optional action name. - # - if ($fields[0] ne "") { - $state_func_name[$num_states] = $fields[0]; - shift @fields; - } - - # - # There should be no fields left on the line at this point. - # - if (@fields > 0) { - print " rbbicsts: at line $line_num, unexpected extra stuff on input line.\n"; - print " scanning $fields[0]\n"; - } - $num_states++; -} - -# -# We've read in the whole file, now go back and output the -# C source code for the state transition table. -# -# We read all states first, before writing anything, so that the state numbers -# for the destination states are all available to be written. -# - -# -# Make hashes for the names of the character classes and -# for the names of the actions that appeared. -# -for ($state=1; $state < $num_states; $state++) { - if ($state_char_class[$state] ne "") { - if ($charClasses{$state_char_class[$state]} == 0) { - $charClasses{$state_char_class[$state]} = 1; - } - } - if ($state_func_name[$state] eq "") { - $state_func_name[$state] = "doNOP"; - } - if ($actions{$state_action_name[$state]} == 0) { - $actions{$state_func_name[$state]} = 1; - } -} - -# -# Check that all of the destination states have been defined -# -# -$states{"exit"} = 0; # Predefined state name, terminates state machine. -for ($state=1; $state<$num_states; $state++) { - if ($states{$state_dest_state[$state]} == 0 && $state_dest_state[$state] ne "exit") { - print "Error at line $state_line_num[$state]: target state \"$state_dest_state[$state]\" is not defined.\n"; - $errors++; - } - if ($state_push_state[$state] ne "" && $states{$state_push_state[$state]} == 0) { - print "Error at line $state_line_num[$state]: target state \"$state_push_state[$state]\" is not defined.\n"; - $errors++; - } -} - -die if ($errors>0); - -# -# Assign numbers to each of the character classes classes used. -# Sets are numbered from 128 - 250 -# The values 0-127 in the state table are used for matching -# individual ASCII characters (the only thing that can appear in the rules.) -# The "set" names appearing in the code below (default, etc.) need special -# handling because they do not correspond to a normal set of characters, -# but trigger special handling by code in the state machine. -# -$i = 128; -foreach $setName (sort keys %charClasses) { - if ($setName eq "default") { - $charClasses{$setName} = 255;} - elsif ($setName eq "escaped") { - $charClasses{$setName} = 254;} - elsif ($setName eq "escapedP") { - $charClasses{$setName} = 253;} - elsif ($setName eq "eof") { - $charClasses{$setName} = 252;} - else { - # Normal (single) character class. Number them. - $charClasses{$setName} = $i; - $i++; - } -} - - -my ($sec, $min, $hour, , $day, $mon, $year, $wday, $yday, $isdst) = localtime; -$year += 1900; - -if ($javaOutput) { - print "/*\n"; - print " *******************************************************************************\n"; - print " * Copyright (C) 2003-$year,\n"; - print " * International Business Machines Corporation and others. All Rights Reserved.\n"; - print " *******************************************************************************\n"; - print " */\n"; - print " \n"; - print "package com.ibm.icu.text;\n"; - print " \n"; - print "/**\n"; - print " * Generated Java File. Do not edit by hand.\n"; - print " * This file contains the state table for the ICU Rule Based Break Iterator\n"; - print " * rule parser.\n"; - print " * It is generated by the Perl script \"rbbicst.pl\" from\n"; - print " * the rule parser state definitions file \"rbbirpt.txt\".\n"; - print " * \@internal \n"; - print " *\n"; - print " */\n"; - - print "class RBBIRuleParseTable\n"; - print "{\n"; - - # - # Emit the constants for the actions to be performed. - # - $n = 1; - foreach $act (sort keys %actions) { - print " static final short $act = $n;\n"; - $n++; - } - print " \n"; - - # - # Emit constants for char class names - # - foreach $setName (sort keys %charClasses) { - print " static final short kRuleSet_$setName = $charClasses{$setName};\n"; - } - print "\n\n"; - - - print " static class RBBIRuleTableElement { \n"; - print " short fAction; \n"; - print " short fCharClass; \n"; - print " short fNextState; \n"; - print " short fPushState; \n"; - print " boolean fNextChar; \n"; - print " String fStateName; \n"; - print " RBBIRuleTableElement(short a, int cc, int ns, int ps, boolean nc, String sn) { \n"; - print " fAction = a; \n"; - print " fCharClass = (short)cc; \n"; - print " fNextState = (short)ns; \n"; - print " fPushState = (short)ps; \n"; - print " fNextChar = nc; \n"; - print " fStateName = sn; \n"; - print " } \n"; - print " }; \n"; - print " \n"; - - - print " static RBBIRuleTableElement[] gRuleParseStateTable = { \n "; - print " new RBBIRuleTableElement(doNOP, 0, 0,0, true, null ) // 0 \n"; #output the unused state 0. - for ($state=1; $state < $num_states; $state++) { - print " , new RBBIRuleTableElement($state_func_name[$state],"; - if ($state_literal_chars[$state] ne "") { - $c = $state_literal_chars[$state]; - print("'$c', "); - }else { - print " $charClasses{$state_char_class[$state]},"; - } - print " $states{$state_dest_state[$state]},"; - - # The push-state field is optional. If omitted, fill field with a zero, which flags - # the state machine that there is no push state. - if ($state_push_state[$state] eq "") { - print "0, "; - } else { - print " $states{$state_push_state[$state]},"; - } - print " $state_flag[$state], "; - - # if this is the first row of the table for this state, put out the state name. - if ($stateNames[$state] ne "") { - print " \"$stateNames[$state]\") "; - } else { - print " null ) "; - } - - # Put out a comment showing the number (index) of this state row, - print " // $state "; - print "\n"; - } - print " };\n"; - - print "}; \n"; - -} -else -{ - # - # C++ Output ... - # - - - print "//---------------------------------------------------------------------------------\n"; - print "//\n"; - print "// Generated Header File. Do not edit by hand.\n"; - print "// This file contains the state table for the ICU Rule Based Break Iterator\n"; - print "// rule parser.\n"; - print "// It is generated by the Perl script \"rbbicst.pl\" from\n"; - print "// the rule parser state definitions file \"rbbirpt.txt\".\n"; - print "//\n"; - print "// Copyright (C) 2002-$year International Business Machines Corporation \n"; - print "// and others. All rights reserved. \n"; - print "//\n"; - print "//---------------------------------------------------------------------------------\n"; - print "#ifndef RBBIRPT_H\n"; - print "#define RBBIRPT_H\n"; - print "\n"; - print "#include \"unicode/utypes.h\"\n"; - print "\n"; - print "U_NAMESPACE_BEGIN\n"; - - # - # Emit the constants for indices of Unicode Sets - # Define one constant for each of the character classes encountered. - # At the same time, store the index corresponding to the set name back into hash. - # - print "//\n"; - print "// Character classes for RBBI rule scanning.\n"; - print "//\n"; - foreach $setName (sort keys %charClasses) { - if ($charClasses{$setName} < 250) { - # Normal character class. - print " static const uint8_t kRuleSet_$setName = $charClasses{$setName};\n"; - } - } - print "\n\n"; - - # - # Emit the enum for the actions to be performed. - # - print "enum RBBI_RuleParseAction {\n"; - foreach $act (sort keys %actions) { - print " $act,\n"; - } - print " rbbiLastAction};\n\n"; - - # - # Emit the struct definition for transition table elements. - # - print "//-------------------------------------------------------------------------------\n"; - print "//\n"; - print "// RBBIRuleTableEl represents the structure of a row in the transition table\n"; - print "// for the rule parser state machine.\n"; - print "//-------------------------------------------------------------------------------\n"; - print "struct RBBIRuleTableEl {\n"; - print " RBBI_RuleParseAction fAction;\n"; - print " uint8_t fCharClass; // 0-127: an individual ASCII character\n"; - print " // 128-255: character class index\n"; - print " uint8_t fNextState; // 0-250: normal next-stat numbers\n"; - print " // 255: pop next-state from stack.\n"; - print " uint8_t fPushState;\n"; - print " UBool fNextChar;\n"; - print "};\n\n"; - - # - # emit the state transition table - # - print "static const struct RBBIRuleTableEl gRuleParseStateTable[] = {\n"; - print " {doNOP, 0, 0, 0, true}\n"; # State 0 is a dummy. Real states start with index = 1. - for ($state=1; $state < $num_states; $state++) { - print " , {$state_func_name[$state],"; - if ($state_literal_chars[$state] ne "") { - $c = $state_literal_chars[$state]; - printf(" %d /* $c */,", ord($c)); # use numeric value, so EBCDIC machines are ok. - }else { - print " $charClasses{$state_char_class[$state]},"; - } - print " $states{$state_dest_state[$state]},"; - - # The push-state field is optional. If omitted, fill field with a zero, which flags - # the state machine that there is no push state. - if ($state_push_state[$state] eq "") { - print "0, "; - } else { - print " $states{$state_push_state[$state]},"; - } - print " $state_flag[$state]} "; - - # Put out a C++ comment showing the number (index) of this state row, - # and, if this is the first row of the table for this state, the state name. - print " // $state "; - if ($stateNames[$state] ne "") { - print " $stateNames[$state]"; - } - print "\n"; - }; - print " };\n"; - - - # - # emit a mapping array from state numbers to state names. - # - # This array is used for producing debugging output from the rule parser. - # - print "#ifdef RBBI_DEBUG\n"; - print "static const char * const RBBIRuleStateNames[] = {"; - for ($state=0; $state<$num_states; $state++) { - if ($stateNames[$state] ne "") { - print " \"$stateNames[$state]\",\n"; - } else { - print " 0,\n"; - } - } - print " 0};\n"; - print "#endif\n\n"; - - print "U_NAMESPACE_END\n"; - print "#endif\n"; -} - - - +#************************************************************************** +# Copyright (C) 2016 and later: Unicode, Inc. and others. +# License & terms of use: http://www.unicode.org/copyright.html +#************************************************************************** +#************************************************************************** +# Copyright (C) 2002-2016 International Business Machines Corporation +# and others. All rights reserved. +#************************************************************************** +# +# rbbicst Compile the RBBI rule parser state table data into initialized C data. +# Usage: +# cd icu/source/common +# perl rbbicst.pl < rbbirpt.txt > rbbirpt.h +# perl rbbicst.pl -j < rbbirpt.txt > RBBIRuleParseTable.java +# +# The output file, rbbrpt.h, is included by some of the .cpp rbbi +# implementation files. This perl script is NOT run as part +# of a normal ICU build. It is run by hand when needed, and the +# rbbirpt.h generated file is put back into cvs. +# +# See rbbirpt.txt for a description of the input format for this script. +# + +if ($ARGV[0] eq "-j") { + $javaOutput = 1; + shift @ARGV; +} + + +$num_states = 1; # Always the state number for the line being compiled. +$line_num = 0; # The line number in the input file. + +$states{"pop"} = 255; # Add the "pop" to the list of defined state names. + # This prevents any state from being labelled with "pop", + # and resolves references to "pop" in the next state field. + +line_loop: while (<>) { + chomp(); + $line = $_; + @fields = split(); + $line_num++; + + # Remove # comments, which are any fields beginning with a #, plus all + # that follow on the line. + for ($i=0; $i<@fields; $i++) { + if ($fields[$i] =~ /^#/) { + @fields = @fields[0 .. $i-1]; + last; + } + } + # ignore blank lines, and those with no fields left after stripping comments.. + if (@fields == 0) { + next; + } + + # + # State Label: handling. + # Does the first token end with a ":"? If so, it's the name of a state. + # Put in a hash, together with the current state number, + # so that we can later look up the number from the name. + # + if (@fields[0] =~ /.*:$/) { + $state_name = @fields[0]; + $state_name =~ s/://; # strip off the colon from the state name. + + if ($states{$state_name} != 0) { + print " rbbicst: at line $line-num duplicate definition of state $state_name\n"; + } + $states{$state_name} = $num_states; + $stateNames[$num_states] = $state_name; + + # if the label was the only thing on this line, go on to the next line, + # otherwise assume that a state definition is on the same line and fall through. + if (@fields == 1) { + next line_loop; + } + shift @fields; # shift off label field in preparation + # for handling the rest of the line. + } + + # + # State Transition line. + # syntax is this, + # character [n] target-state [^push-state] [function-name] + # where + # [something] is an optional something + # character is either a single quoted character e.g. '[' + # or a name of a character class, e.g. white_space + # + + $state_line_num[$num_states] = $line_num; # remember line number with each state + # so we can make better error messages later. + # + # First field, character class or literal character for this transition. + # + if ($fields[0] =~ /^'.'$/) { + # We've got a quoted literal character. + $state_literal_chars[$num_states] = $fields[0]; + $state_literal_chars[$num_states] =~ s/'//g; + } else { + # We've got the name of a character class. + $state_char_class[$num_states] = $fields[0]; + if ($fields[0] =~ /[\W]/) { + print " rbbicsts: at line $line_num, bad character literal or character class name.\n"; + print " scanning $fields[0]\n"; + exit(-1); + } + } + shift @fields; + + # + # do the 'n' flag + # + $state_flag[$num_states] = "false"; + if ($fields[0] eq "n") { + $state_flag[$num_states] = "true"; + shift @fields; + } + + # + # do the destination state. + # + $state_dest_state[$num_states] = $fields[0]; + if ($fields[0] eq "") { + print " rbbicsts: at line $line_num, destination state missing.\n"; + exit(-1); + } + shift @fields; + + # + # do the push state, if present. + # + if ($fields[0] =~ /^\^/) { + $fields[0] =~ s/^\^//; + $state_push_state[$num_states] = $fields[0]; + if ($fields[0] eq "" ) { + print " rbbicsts: at line $line_num, expected state after ^ (no spaces).\n"; + exit(-1); + } + shift @fields; + } + + # + # Lastly, do the optional action name. + # + if ($fields[0] ne "") { + $state_func_name[$num_states] = $fields[0]; + shift @fields; + } + + # + # There should be no fields left on the line at this point. + # + if (@fields > 0) { + print " rbbicsts: at line $line_num, unexpected extra stuff on input line.\n"; + print " scanning $fields[0]\n"; + } + $num_states++; +} + +# +# We've read in the whole file, now go back and output the +# C source code for the state transition table. +# +# We read all states first, before writing anything, so that the state numbers +# for the destination states are all available to be written. +# + +# +# Make hashes for the names of the character classes and +# for the names of the actions that appeared. +# +for ($state=1; $state < $num_states; $state++) { + if ($state_char_class[$state] ne "") { + if ($charClasses{$state_char_class[$state]} == 0) { + $charClasses{$state_char_class[$state]} = 1; + } + } + if ($state_func_name[$state] eq "") { + $state_func_name[$state] = "doNOP"; + } + if ($actions{$state_action_name[$state]} == 0) { + $actions{$state_func_name[$state]} = 1; + } +} + +# +# Check that all of the destination states have been defined +# +# +$states{"exit"} = 0; # Predefined state name, terminates state machine. +for ($state=1; $state<$num_states; $state++) { + if ($states{$state_dest_state[$state]} == 0 && $state_dest_state[$state] ne "exit") { + print "Error at line $state_line_num[$state]: target state \"$state_dest_state[$state]\" is not defined.\n"; + $errors++; + } + if ($state_push_state[$state] ne "" && $states{$state_push_state[$state]} == 0) { + print "Error at line $state_line_num[$state]: target state \"$state_push_state[$state]\" is not defined.\n"; + $errors++; + } +} + +die if ($errors>0); + +# +# Assign numbers to each of the character classes classes used. +# Sets are numbered from 128 - 250 +# The values 0-127 in the state table are used for matching +# individual ASCII characters (the only thing that can appear in the rules.) +# The "set" names appearing in the code below (default, etc.) need special +# handling because they do not correspond to a normal set of characters, +# but trigger special handling by code in the state machine. +# +$i = 128; +foreach $setName (sort keys %charClasses) { + if ($setName eq "default") { + $charClasses{$setName} = 255;} + elsif ($setName eq "escaped") { + $charClasses{$setName} = 254;} + elsif ($setName eq "escapedP") { + $charClasses{$setName} = 253;} + elsif ($setName eq "eof") { + $charClasses{$setName} = 252;} + else { + # Normal (single) character class. Number them. + $charClasses{$setName} = $i; + $i++; + } +} + + +my ($sec, $min, $hour, , $day, $mon, $year, $wday, $yday, $isdst) = localtime; +$year += 1900; + +if ($javaOutput) { + print "/*\n"; + print " *******************************************************************************\n"; + print " * Copyright (C) 2003-$year,\n"; + print " * International Business Machines Corporation and others. All Rights Reserved.\n"; + print " *******************************************************************************\n"; + print " */\n"; + print " \n"; + print "package com.ibm.icu.text;\n"; + print " \n"; + print "/**\n"; + print " * Generated Java File. Do not edit by hand.\n"; + print " * This file contains the state table for the ICU Rule Based Break Iterator\n"; + print " * rule parser.\n"; + print " * It is generated by the Perl script \"rbbicst.pl\" from\n"; + print " * the rule parser state definitions file \"rbbirpt.txt\".\n"; + print " * \@internal \n"; + print " *\n"; + print " */\n"; + + print "class RBBIRuleParseTable\n"; + print "{\n"; + + # + # Emit the constants for the actions to be performed. + # + $n = 1; + foreach $act (sort keys %actions) { + print " static final short $act = $n;\n"; + $n++; + } + print " \n"; + + # + # Emit constants for char class names + # + foreach $setName (sort keys %charClasses) { + print " static final short kRuleSet_$setName = $charClasses{$setName};\n"; + } + print "\n\n"; + + + print " static class RBBIRuleTableElement { \n"; + print " short fAction; \n"; + print " short fCharClass; \n"; + print " short fNextState; \n"; + print " short fPushState; \n"; + print " boolean fNextChar; \n"; + print " String fStateName; \n"; + print " RBBIRuleTableElement(short a, int cc, int ns, int ps, boolean nc, String sn) { \n"; + print " fAction = a; \n"; + print " fCharClass = (short)cc; \n"; + print " fNextState = (short)ns; \n"; + print " fPushState = (short)ps; \n"; + print " fNextChar = nc; \n"; + print " fStateName = sn; \n"; + print " } \n"; + print " }; \n"; + print " \n"; + + + print " static RBBIRuleTableElement[] gRuleParseStateTable = { \n "; + print " new RBBIRuleTableElement(doNOP, 0, 0,0, true, null ) // 0 \n"; #output the unused state 0. + for ($state=1; $state < $num_states; $state++) { + print " , new RBBIRuleTableElement($state_func_name[$state],"; + if ($state_literal_chars[$state] ne "") { + $c = $state_literal_chars[$state]; + print("'$c', "); + }else { + print " $charClasses{$state_char_class[$state]},"; + } + print " $states{$state_dest_state[$state]},"; + + # The push-state field is optional. If omitted, fill field with a zero, which flags + # the state machine that there is no push state. + if ($state_push_state[$state] eq "") { + print "0, "; + } else { + print " $states{$state_push_state[$state]},"; + } + print " $state_flag[$state], "; + + # if this is the first row of the table for this state, put out the state name. + if ($stateNames[$state] ne "") { + print " \"$stateNames[$state]\") "; + } else { + print " null ) "; + } + + # Put out a comment showing the number (index) of this state row, + print " // $state "; + print "\n"; + } + print " };\n"; + + print "}; \n"; + +} +else +{ + # + # C++ Output ... + # + + + print "//---------------------------------------------------------------------------------\n"; + print "//\n"; + print "// Generated Header File. Do not edit by hand.\n"; + print "// This file contains the state table for the ICU Rule Based Break Iterator\n"; + print "// rule parser.\n"; + print "// It is generated by the Perl script \"rbbicst.pl\" from\n"; + print "// the rule parser state definitions file \"rbbirpt.txt\".\n"; + print "//\n"; + print "// Copyright (C) 2002-$year International Business Machines Corporation \n"; + print "// and others. All rights reserved. \n"; + print "//\n"; + print "//---------------------------------------------------------------------------------\n"; + print "#ifndef RBBIRPT_H\n"; + print "#define RBBIRPT_H\n"; + print "\n"; + print "#include \"unicode/utypes.h\"\n"; + print "\n"; + print "U_NAMESPACE_BEGIN\n"; + + # + # Emit the constants for indices of Unicode Sets + # Define one constant for each of the character classes encountered. + # At the same time, store the index corresponding to the set name back into hash. + # + print "//\n"; + print "// Character classes for RBBI rule scanning.\n"; + print "//\n"; + foreach $setName (sort keys %charClasses) { + if ($charClasses{$setName} < 250) { + # Normal character class. + print " static const uint8_t kRuleSet_$setName = $charClasses{$setName};\n"; + } + } + print "\n\n"; + + # + # Emit the enum for the actions to be performed. + # + print "enum RBBI_RuleParseAction {\n"; + foreach $act (sort keys %actions) { + print " $act,\n"; + } + print " rbbiLastAction};\n\n"; + + # + # Emit the struct definition for transition table elements. + # + print "//-------------------------------------------------------------------------------\n"; + print "//\n"; + print "// RBBIRuleTableEl represents the structure of a row in the transition table\n"; + print "// for the rule parser state machine.\n"; + print "//-------------------------------------------------------------------------------\n"; + print "struct RBBIRuleTableEl {\n"; + print " RBBI_RuleParseAction fAction;\n"; + print " uint8_t fCharClass; // 0-127: an individual ASCII character\n"; + print " // 128-255: character class index\n"; + print " uint8_t fNextState; // 0-250: normal next-stat numbers\n"; + print " // 255: pop next-state from stack.\n"; + print " uint8_t fPushState;\n"; + print " UBool fNextChar;\n"; + print "};\n\n"; + + # + # emit the state transition table + # + print "static const struct RBBIRuleTableEl gRuleParseStateTable[] = {\n"; + print " {doNOP, 0, 0, 0, true}\n"; # State 0 is a dummy. Real states start with index = 1. + for ($state=1; $state < $num_states; $state++) { + print " , {$state_func_name[$state],"; + if ($state_literal_chars[$state] ne "") { + $c = $state_literal_chars[$state]; + printf(" %d /* $c */,", ord($c)); # use numeric value, so EBCDIC machines are ok. + }else { + print " $charClasses{$state_char_class[$state]},"; + } + print " $states{$state_dest_state[$state]},"; + + # The push-state field is optional. If omitted, fill field with a zero, which flags + # the state machine that there is no push state. + if ($state_push_state[$state] eq "") { + print "0, "; + } else { + print " $states{$state_push_state[$state]},"; + } + print " $state_flag[$state]} "; + + # Put out a C++ comment showing the number (index) of this state row, + # and, if this is the first row of the table for this state, the state name. + print " // $state "; + if ($stateNames[$state] ne "") { + print " $stateNames[$state]"; + } + print "\n"; + }; + print " };\n"; + + + # + # emit a mapping array from state numbers to state names. + # + # This array is used for producing debugging output from the rule parser. + # + print "#ifdef RBBI_DEBUG\n"; + print "static const char * const RBBIRuleStateNames[] = {"; + for ($state=0; $state<$num_states; $state++) { + if ($stateNames[$state] ne "") { + print " \"$stateNames[$state]\",\n"; + } else { + print " 0,\n"; + } + } + print " 0};\n"; + print "#endif\n\n"; + + print "U_NAMESPACE_END\n"; + print "#endif\n"; +} + + + diff --git a/deps/icu-small/source/common/rbbidata.cpp b/deps/icu-small/source/common/rbbidata.cpp index f50fc458a51003..c3072523a5cdc3 100644 --- a/deps/icu-small/source/common/rbbidata.cpp +++ b/deps/icu-small/source/common/rbbidata.cpp @@ -1,476 +1,476 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -*************************************************************************** -* Copyright (C) 1999-2014 International Business Machines Corporation * -* and others. All rights reserved. * -*************************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/ucptrie.h" -#include "unicode/utypes.h" -#include "rbbidata.h" -#include "rbbirb.h" -#include "udatamem.h" -#include "cmemory.h" -#include "cstring.h" -#include "umutex.h" - -#include "uassert.h" - - -U_NAMESPACE_BEGIN - -//----------------------------------------------------------------------------- -// -// Constructors. -// -//----------------------------------------------------------------------------- -RBBIDataWrapper::RBBIDataWrapper(const RBBIDataHeader *data, UErrorCode &status) { - init0(); - init(data, status); -} - -RBBIDataWrapper::RBBIDataWrapper(const RBBIDataHeader *data, enum EDontAdopt, UErrorCode &status) { - init0(); - init(data, status); - fDontFreeData = true; -} - -RBBIDataWrapper::RBBIDataWrapper(UDataMemory* udm, UErrorCode &status) { - init0(); - if (U_FAILURE(status)) { - return; - } - const DataHeader *dh = udm->pHeader; - int32_t headerSize = dh->dataHeader.headerSize; - if ( !(headerSize >= 20 && - dh->info.isBigEndian == U_IS_BIG_ENDIAN && - dh->info.charsetFamily == U_CHARSET_FAMILY && - dh->info.dataFormat[0] == 0x42 && // dataFormat="Brk " - dh->info.dataFormat[1] == 0x72 && - dh->info.dataFormat[2] == 0x6b && - dh->info.dataFormat[3] == 0x20 && - isDataVersionAcceptable(dh->info.formatVersion)) - ) { - status = U_INVALID_FORMAT_ERROR; - return; - } - const char *dataAsBytes = reinterpret_cast(dh); - const RBBIDataHeader *rbbidh = reinterpret_cast(dataAsBytes + headerSize); - init(rbbidh, status); - fUDataMem = udm; -} - -UBool RBBIDataWrapper::isDataVersionAcceptable(const UVersionInfo version) { - return RBBI_DATA_FORMAT_VERSION[0] == version[0]; -} - - -//----------------------------------------------------------------------------- -// -// init(). Does most of the work of construction, shared between the -// constructors. -// -//----------------------------------------------------------------------------- -void RBBIDataWrapper::init0() { - fHeader = NULL; - fForwardTable = NULL; - fReverseTable = NULL; - fRuleSource = NULL; - fRuleStatusTable = NULL; - fTrie = NULL; - fUDataMem = NULL; - fRefCount = 0; - fDontFreeData = true; -} - -void RBBIDataWrapper::init(const RBBIDataHeader *data, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - fHeader = data; - if (fHeader->fMagic != 0xb1a0 || !isDataVersionAcceptable(fHeader->fFormatVersion)) { - status = U_INVALID_FORMAT_ERROR; - return; - } - // Note: in ICU version 3.2 and earlier, there was a formatVersion 1 - // that is no longer supported. At that time fFormatVersion was - // an int32_t field, rather than an array of 4 bytes. - - fDontFreeData = false; - if (data->fFTableLen != 0) { - fForwardTable = (RBBIStateTable *)((char *)data + fHeader->fFTable); - } - if (data->fRTableLen != 0) { - fReverseTable = (RBBIStateTable *)((char *)data + fHeader->fRTable); - } - - fTrie = ucptrie_openFromBinary(UCPTRIE_TYPE_FAST, - UCPTRIE_VALUE_BITS_ANY, - (uint8_t *)data + fHeader->fTrie, - fHeader->fTrieLen, - nullptr, // *actual length - &status); - if (U_FAILURE(status)) { - return; - } - - UCPTrieValueWidth width = ucptrie_getValueWidth(fTrie); - if (!(width == UCPTRIE_VALUE_BITS_8 || width == UCPTRIE_VALUE_BITS_16)) { - status = U_INVALID_FORMAT_ERROR; - return; - } - - fRuleSource = ((char *)data + fHeader->fRuleSource); - fRuleString = UnicodeString::fromUTF8(StringPiece(fRuleSource, fHeader->fRuleSourceLen)); - U_ASSERT(data->fRuleSourceLen > 0); - - fRuleStatusTable = (int32_t *)((char *)data + fHeader->fStatusTable); - fStatusMaxIdx = data->fStatusTableLen / sizeof(int32_t); - - fRefCount = 1; - -#ifdef RBBI_DEBUG - char *debugEnv = getenv("U_RBBIDEBUG"); - if (debugEnv && uprv_strstr(debugEnv, "data")) {this->printData();} -#endif -} - - -//----------------------------------------------------------------------------- -// -// Destructor. Don't call this - use removeReference() instead. -// -//----------------------------------------------------------------------------- -RBBIDataWrapper::~RBBIDataWrapper() { - U_ASSERT(fRefCount == 0); - ucptrie_close(fTrie); - fTrie = nullptr; - if (fUDataMem) { - udata_close(fUDataMem); - } else if (!fDontFreeData) { - uprv_free((void *)fHeader); - } -} - - - -//----------------------------------------------------------------------------- -// -// Operator == Consider two RBBIDataWrappers to be equal if they -// refer to the same underlying data. Although -// the data wrappers are normally shared between -// iterator instances, it's possible to independently -// open the same data twice, and get two instances, which -// should still be ==. -// -//----------------------------------------------------------------------------- -bool RBBIDataWrapper::operator ==(const RBBIDataWrapper &other) const { - if (fHeader == other.fHeader) { - return true; - } - if (fHeader->fLength != other.fHeader->fLength) { - return false; - } - if (uprv_memcmp(fHeader, other.fHeader, fHeader->fLength) == 0) { - return true; - } - return false; -} - -int32_t RBBIDataWrapper::hashCode() { - return fHeader->fFTableLen; -} - - - -//----------------------------------------------------------------------------- -// -// Reference Counting. A single RBBIDataWrapper object is shared among -// however many RulesBasedBreakIterator instances are -// referencing the same data. -// -//----------------------------------------------------------------------------- -void RBBIDataWrapper::removeReference() { - if (umtx_atomic_dec(&fRefCount) == 0) { - delete this; - } -} - - -RBBIDataWrapper *RBBIDataWrapper::addReference() { - umtx_atomic_inc(&fRefCount); - return this; -} - - - -//----------------------------------------------------------------------------- -// -// getRuleSourceString -// -//----------------------------------------------------------------------------- -const UnicodeString &RBBIDataWrapper::getRuleSourceString() const { - return fRuleString; -} - - -//----------------------------------------------------------------------------- -// -// print - debugging function to dump the runtime data tables. -// -//----------------------------------------------------------------------------- -#ifdef RBBI_DEBUG -void RBBIDataWrapper::printTable(const char *heading, const RBBIStateTable *table) { - uint32_t c; - uint32_t s; - - RBBIDebugPrintf("%s\n", heading); - - RBBIDebugPrintf(" fDictCategoriesStart: %d\n", table->fDictCategoriesStart); - RBBIDebugPrintf(" fLookAheadResultsSize: %d\n", table->fLookAheadResultsSize); - RBBIDebugPrintf(" Flags: %4x RBBI_LOOKAHEAD_HARD_BREAK=%s RBBI_BOF_REQUIRED=%s RBBI_8BITS_ROWS=%s\n", - table->fFlags, - table->fFlags & RBBI_LOOKAHEAD_HARD_BREAK ? "T" : "F", - table->fFlags & RBBI_BOF_REQUIRED ? "T" : "F", - table->fFlags & RBBI_8BITS_ROWS ? "T" : "F"); - RBBIDebugPrintf("\nState | Acc LA TagIx"); - for (c=0; cfCatCount; c++) {RBBIDebugPrintf("%3d ", c);} - RBBIDebugPrintf("\n------|---------------"); for (c=0;cfCatCount; c++) { - RBBIDebugPrintf("----"); - } - RBBIDebugPrintf("\n"); - - if (table == NULL) { - RBBIDebugPrintf(" N U L L T A B L E\n\n"); - return; - } - UBool use8Bits = table->fFlags & RBBI_8BITS_ROWS; - for (s=0; sfNumStates; s++) { - RBBIStateTableRow *row = (RBBIStateTableRow *) - (table->fTableData + (table->fRowLen * s)); - if (use8Bits) { - RBBIDebugPrintf("%4d | %3d %3d %3d ", s, row->r8.fAccepting, row->r8.fLookAhead, row->r8.fTagsIdx); - for (c=0; cfCatCount; c++) { - RBBIDebugPrintf("%3d ", row->r8.fNextState[c]); - } - } else { - RBBIDebugPrintf("%4d | %3d %3d %3d ", s, row->r16.fAccepting, row->r16.fLookAhead, row->r16.fTagsIdx); - for (c=0; cfCatCount; c++) { - RBBIDebugPrintf("%3d ", row->r16.fNextState[c]); - } - } - RBBIDebugPrintf("\n"); - } - RBBIDebugPrintf("\n"); -} -#endif - - -void RBBIDataWrapper::printData() { -#ifdef RBBI_DEBUG - RBBIDebugPrintf("RBBI Data at %p\n", (void *)fHeader); - RBBIDebugPrintf(" Version = {%d %d %d %d}\n", fHeader->fFormatVersion[0], fHeader->fFormatVersion[1], - fHeader->fFormatVersion[2], fHeader->fFormatVersion[3]); - RBBIDebugPrintf(" total length of data = %d\n", fHeader->fLength); - RBBIDebugPrintf(" number of character categories = %d\n\n", fHeader->fCatCount); - - printTable("Forward State Transition Table", fForwardTable); - printTable("Reverse State Transition Table", fReverseTable); - - RBBIDebugPrintf("\nOriginal Rules source:\n"); - for (int32_t c=0; fRuleSource[c] != 0; c++) { - RBBIDebugPrintf("%c", fRuleSource[c]); - } - RBBIDebugPrintf("\n\n"); -#endif -} - - -U_NAMESPACE_END -U_NAMESPACE_USE - -//----------------------------------------------------------------------------- -// -// ubrk_swap - byte swap and char encoding swap of RBBI data -// -//----------------------------------------------------------------------------- - -U_CAPI int32_t U_EXPORT2 -ubrk_swap(const UDataSwapper *ds, const void *inData, int32_t length, void *outData, - UErrorCode *status) { - - if (status == NULL || U_FAILURE(*status)) { - return 0; - } - if(ds==NULL || inData==NULL || length<-1 || (length>0 && outData==NULL)) { - *status=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - // - // Check that the data header is for for break data. - // (Header contents are defined in genbrk.cpp) - // - const UDataInfo *pInfo = (const UDataInfo *)((const char *)inData+4); - if(!( pInfo->dataFormat[0]==0x42 && /* dataFormat="Brk " */ - pInfo->dataFormat[1]==0x72 && - pInfo->dataFormat[2]==0x6b && - pInfo->dataFormat[3]==0x20 && - RBBIDataWrapper::isDataVersionAcceptable(pInfo->formatVersion) )) { - udata_printError(ds, "ubrk_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized\n", - pInfo->dataFormat[0], pInfo->dataFormat[1], - pInfo->dataFormat[2], pInfo->dataFormat[3], - pInfo->formatVersion[0]); - *status=U_UNSUPPORTED_ERROR; - return 0; - } - - // - // Swap the data header. (This is the generic ICU Data Header, not the RBBI Specific - // RBBIDataHeader). This swap also conveniently gets us - // the size of the ICU d.h., which lets us locate the start - // of the RBBI specific data. - // - int32_t headerSize=udata_swapDataHeader(ds, inData, length, outData, status); - - - // - // Get the RRBI Data Header, and check that it appears to be OK. - // - const uint8_t *inBytes =(const uint8_t *)inData+headerSize; - RBBIDataHeader *rbbiDH = (RBBIDataHeader *)inBytes; - if (ds->readUInt32(rbbiDH->fMagic) != 0xb1a0 || - !RBBIDataWrapper::isDataVersionAcceptable(rbbiDH->fFormatVersion) || - ds->readUInt32(rbbiDH->fLength) < sizeof(RBBIDataHeader)) { - udata_printError(ds, "ubrk_swap(): RBBI Data header is invalid.\n"); - *status=U_UNSUPPORTED_ERROR; - return 0; - } - - // - // Prefight operation? Just return the size - // - int32_t breakDataLength = ds->readUInt32(rbbiDH->fLength); - int32_t totalSize = headerSize + breakDataLength; - if (length < 0) { - return totalSize; - } - - // - // Check that length passed in is consistent with length from RBBI data header. - // - if (length < totalSize) { - udata_printError(ds, "ubrk_swap(): too few bytes (%d after ICU Data header) for break data.\n", - breakDataLength); - *status=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - - // - // Swap the Data. Do the data itself first, then the RBBI Data Header, because - // we need to reference the header to locate the data, and an - // inplace swap of the header leaves it unusable. - // - uint8_t *outBytes = (uint8_t *)outData + headerSize; - RBBIDataHeader *outputDH = (RBBIDataHeader *)outBytes; - - int32_t tableStartOffset; - int32_t tableLength; - - // - // If not swapping in place, zero out the output buffer before starting. - // Individual tables and other data items within are aligned to 8 byte boundaries - // when originally created. Any unused space between items needs to be zero. - // - if (inBytes != outBytes) { - uprv_memset(outBytes, 0, breakDataLength); - } - - // - // Each state table begins with several 32 bit fields. Calculate the size - // in bytes of these. - // - int32_t topSize = offsetof(RBBIStateTable, fTableData); - - // Forward state table. - tableStartOffset = ds->readUInt32(rbbiDH->fFTable); - tableLength = ds->readUInt32(rbbiDH->fFTableLen); - - if (tableLength > 0) { - RBBIStateTable *rbbiST = (RBBIStateTable *)(inBytes+tableStartOffset); - UBool use8Bits = ds->readUInt32(rbbiST->fFlags) & RBBI_8BITS_ROWS; - - ds->swapArray32(ds, inBytes+tableStartOffset, topSize, - outBytes+tableStartOffset, status); - - // Swap the state table if the table is in 16 bits. - if (use8Bits) { - if (outBytes != inBytes) { - uprv_memmove(outBytes+tableStartOffset+topSize, - inBytes+tableStartOffset+topSize, - tableLength-topSize); - } - } else { - ds->swapArray16(ds, inBytes+tableStartOffset+topSize, tableLength-topSize, - outBytes+tableStartOffset+topSize, status); - } - } - - // Reverse state table. Same layout as forward table, above. - tableStartOffset = ds->readUInt32(rbbiDH->fRTable); - tableLength = ds->readUInt32(rbbiDH->fRTableLen); - - if (tableLength > 0) { - RBBIStateTable *rbbiST = (RBBIStateTable *)(inBytes+tableStartOffset); - UBool use8Bits = ds->readUInt32(rbbiST->fFlags) & RBBI_8BITS_ROWS; - - ds->swapArray32(ds, inBytes+tableStartOffset, topSize, - outBytes+tableStartOffset, status); - - // Swap the state table if the table is in 16 bits. - if (use8Bits) { - if (outBytes != inBytes) { - uprv_memmove(outBytes+tableStartOffset+topSize, - inBytes+tableStartOffset+topSize, - tableLength-topSize); - } - } else { - ds->swapArray16(ds, inBytes+tableStartOffset+topSize, tableLength-topSize, - outBytes+tableStartOffset+topSize, status); - } - } - - // Trie table for character categories - ucptrie_swap(ds, inBytes+ds->readUInt32(rbbiDH->fTrie), ds->readUInt32(rbbiDH->fTrieLen), - outBytes+ds->readUInt32(rbbiDH->fTrie), status); - - // Source Rules Text. It's UTF8 data - if (outBytes != inBytes) { - uprv_memmove(outBytes+ds->readUInt32(rbbiDH->fRuleSource), - inBytes+ds->readUInt32(rbbiDH->fRuleSource), - ds->readUInt32(rbbiDH->fRuleSourceLen)); - } - - // Table of rule status values. It's all int_32 values - ds->swapArray32(ds, inBytes+ds->readUInt32(rbbiDH->fStatusTable), ds->readUInt32(rbbiDH->fStatusTableLen), - outBytes+ds->readUInt32(rbbiDH->fStatusTable), status); - - // And, last, the header. - // It is all int32_t values except for fFormataVersion, which is an array of four bytes. - // Swap the whole thing as int32_t, then re-swap the one field. - // - ds->swapArray32(ds, inBytes, sizeof(RBBIDataHeader), outBytes, status); - ds->swapArray32(ds, outputDH->fFormatVersion, 4, outputDH->fFormatVersion, status); - - return totalSize; -} - - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +*************************************************************************** +* Copyright (C) 1999-2014 International Business Machines Corporation * +* and others. All rights reserved. * +*************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/ucptrie.h" +#include "unicode/utypes.h" +#include "rbbidata.h" +#include "rbbirb.h" +#include "udatamem.h" +#include "cmemory.h" +#include "cstring.h" +#include "umutex.h" + +#include "uassert.h" + + +U_NAMESPACE_BEGIN + +//----------------------------------------------------------------------------- +// +// Constructors. +// +//----------------------------------------------------------------------------- +RBBIDataWrapper::RBBIDataWrapper(const RBBIDataHeader *data, UErrorCode &status) { + init0(); + init(data, status); +} + +RBBIDataWrapper::RBBIDataWrapper(const RBBIDataHeader *data, enum EDontAdopt, UErrorCode &status) { + init0(); + init(data, status); + fDontFreeData = true; +} + +RBBIDataWrapper::RBBIDataWrapper(UDataMemory* udm, UErrorCode &status) { + init0(); + if (U_FAILURE(status)) { + return; + } + const DataHeader *dh = udm->pHeader; + int32_t headerSize = dh->dataHeader.headerSize; + if ( !(headerSize >= 20 && + dh->info.isBigEndian == U_IS_BIG_ENDIAN && + dh->info.charsetFamily == U_CHARSET_FAMILY && + dh->info.dataFormat[0] == 0x42 && // dataFormat="Brk " + dh->info.dataFormat[1] == 0x72 && + dh->info.dataFormat[2] == 0x6b && + dh->info.dataFormat[3] == 0x20 && + isDataVersionAcceptable(dh->info.formatVersion)) + ) { + status = U_INVALID_FORMAT_ERROR; + return; + } + const char *dataAsBytes = reinterpret_cast(dh); + const RBBIDataHeader *rbbidh = reinterpret_cast(dataAsBytes + headerSize); + init(rbbidh, status); + fUDataMem = udm; +} + +UBool RBBIDataWrapper::isDataVersionAcceptable(const UVersionInfo version) { + return RBBI_DATA_FORMAT_VERSION[0] == version[0]; +} + + +//----------------------------------------------------------------------------- +// +// init(). Does most of the work of construction, shared between the +// constructors. +// +//----------------------------------------------------------------------------- +void RBBIDataWrapper::init0() { + fHeader = nullptr; + fForwardTable = nullptr; + fReverseTable = nullptr; + fRuleSource = nullptr; + fRuleStatusTable = nullptr; + fTrie = nullptr; + fUDataMem = nullptr; + fRefCount = 0; + fDontFreeData = true; +} + +void RBBIDataWrapper::init(const RBBIDataHeader *data, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + fHeader = data; + if (fHeader->fMagic != 0xb1a0 || !isDataVersionAcceptable(fHeader->fFormatVersion)) { + status = U_INVALID_FORMAT_ERROR; + return; + } + // Note: in ICU version 3.2 and earlier, there was a formatVersion 1 + // that is no longer supported. At that time fFormatVersion was + // an int32_t field, rather than an array of 4 bytes. + + fDontFreeData = false; + if (data->fFTableLen != 0) { + fForwardTable = (RBBIStateTable *)((char *)data + fHeader->fFTable); + } + if (data->fRTableLen != 0) { + fReverseTable = (RBBIStateTable *)((char *)data + fHeader->fRTable); + } + + fTrie = ucptrie_openFromBinary(UCPTRIE_TYPE_FAST, + UCPTRIE_VALUE_BITS_ANY, + (uint8_t *)data + fHeader->fTrie, + fHeader->fTrieLen, + nullptr, // *actual length + &status); + if (U_FAILURE(status)) { + return; + } + + UCPTrieValueWidth width = ucptrie_getValueWidth(fTrie); + if (!(width == UCPTRIE_VALUE_BITS_8 || width == UCPTRIE_VALUE_BITS_16)) { + status = U_INVALID_FORMAT_ERROR; + return; + } + + fRuleSource = ((char *)data + fHeader->fRuleSource); + fRuleString = UnicodeString::fromUTF8(StringPiece(fRuleSource, fHeader->fRuleSourceLen)); + U_ASSERT(data->fRuleSourceLen > 0); + + fRuleStatusTable = (int32_t *)((char *)data + fHeader->fStatusTable); + fStatusMaxIdx = data->fStatusTableLen / sizeof(int32_t); + + fRefCount = 1; + +#ifdef RBBI_DEBUG + char *debugEnv = getenv("U_RBBIDEBUG"); + if (debugEnv && uprv_strstr(debugEnv, "data")) {this->printData();} +#endif +} + + +//----------------------------------------------------------------------------- +// +// Destructor. Don't call this - use removeReference() instead. +// +//----------------------------------------------------------------------------- +RBBIDataWrapper::~RBBIDataWrapper() { + U_ASSERT(fRefCount == 0); + ucptrie_close(fTrie); + fTrie = nullptr; + if (fUDataMem) { + udata_close(fUDataMem); + } else if (!fDontFreeData) { + uprv_free((void *)fHeader); + } +} + + + +//----------------------------------------------------------------------------- +// +// Operator == Consider two RBBIDataWrappers to be equal if they +// refer to the same underlying data. Although +// the data wrappers are normally shared between +// iterator instances, it's possible to independently +// open the same data twice, and get two instances, which +// should still be ==. +// +//----------------------------------------------------------------------------- +bool RBBIDataWrapper::operator ==(const RBBIDataWrapper &other) const { + if (fHeader == other.fHeader) { + return true; + } + if (fHeader->fLength != other.fHeader->fLength) { + return false; + } + if (uprv_memcmp(fHeader, other.fHeader, fHeader->fLength) == 0) { + return true; + } + return false; +} + +int32_t RBBIDataWrapper::hashCode() { + return fHeader->fFTableLen; +} + + + +//----------------------------------------------------------------------------- +// +// Reference Counting. A single RBBIDataWrapper object is shared among +// however many RulesBasedBreakIterator instances are +// referencing the same data. +// +//----------------------------------------------------------------------------- +void RBBIDataWrapper::removeReference() { + if (umtx_atomic_dec(&fRefCount) == 0) { + delete this; + } +} + + +RBBIDataWrapper *RBBIDataWrapper::addReference() { + umtx_atomic_inc(&fRefCount); + return this; +} + + + +//----------------------------------------------------------------------------- +// +// getRuleSourceString +// +//----------------------------------------------------------------------------- +const UnicodeString &RBBIDataWrapper::getRuleSourceString() const { + return fRuleString; +} + + +//----------------------------------------------------------------------------- +// +// print - debugging function to dump the runtime data tables. +// +//----------------------------------------------------------------------------- +#ifdef RBBI_DEBUG +void RBBIDataWrapper::printTable(const char *heading, const RBBIStateTable *table) { + uint32_t c; + uint32_t s; + + RBBIDebugPrintf("%s\n", heading); + + RBBIDebugPrintf(" fDictCategoriesStart: %d\n", table->fDictCategoriesStart); + RBBIDebugPrintf(" fLookAheadResultsSize: %d\n", table->fLookAheadResultsSize); + RBBIDebugPrintf(" Flags: %4x RBBI_LOOKAHEAD_HARD_BREAK=%s RBBI_BOF_REQUIRED=%s RBBI_8BITS_ROWS=%s\n", + table->fFlags, + table->fFlags & RBBI_LOOKAHEAD_HARD_BREAK ? "T" : "F", + table->fFlags & RBBI_BOF_REQUIRED ? "T" : "F", + table->fFlags & RBBI_8BITS_ROWS ? "T" : "F"); + RBBIDebugPrintf("\nState | Acc LA TagIx"); + for (c=0; cfCatCount; c++) {RBBIDebugPrintf("%3d ", c);} + RBBIDebugPrintf("\n------|---------------"); for (c=0;cfCatCount; c++) { + RBBIDebugPrintf("----"); + } + RBBIDebugPrintf("\n"); + + if (table == nullptr) { + RBBIDebugPrintf(" N U L L T A B L E\n\n"); + return; + } + UBool use8Bits = table->fFlags & RBBI_8BITS_ROWS; + for (s=0; sfNumStates; s++) { + RBBIStateTableRow *row = (RBBIStateTableRow *) + (table->fTableData + (table->fRowLen * s)); + if (use8Bits) { + RBBIDebugPrintf("%4d | %3d %3d %3d ", s, row->r8.fAccepting, row->r8.fLookAhead, row->r8.fTagsIdx); + for (c=0; cfCatCount; c++) { + RBBIDebugPrintf("%3d ", row->r8.fNextState[c]); + } + } else { + RBBIDebugPrintf("%4d | %3d %3d %3d ", s, row->r16.fAccepting, row->r16.fLookAhead, row->r16.fTagsIdx); + for (c=0; cfCatCount; c++) { + RBBIDebugPrintf("%3d ", row->r16.fNextState[c]); + } + } + RBBIDebugPrintf("\n"); + } + RBBIDebugPrintf("\n"); +} +#endif + + +void RBBIDataWrapper::printData() { +#ifdef RBBI_DEBUG + RBBIDebugPrintf("RBBI Data at %p\n", (void *)fHeader); + RBBIDebugPrintf(" Version = {%d %d %d %d}\n", fHeader->fFormatVersion[0], fHeader->fFormatVersion[1], + fHeader->fFormatVersion[2], fHeader->fFormatVersion[3]); + RBBIDebugPrintf(" total length of data = %d\n", fHeader->fLength); + RBBIDebugPrintf(" number of character categories = %d\n\n", fHeader->fCatCount); + + printTable("Forward State Transition Table", fForwardTable); + printTable("Reverse State Transition Table", fReverseTable); + + RBBIDebugPrintf("\nOriginal Rules source:\n"); + for (int32_t c=0; fRuleSource[c] != 0; c++) { + RBBIDebugPrintf("%c", fRuleSource[c]); + } + RBBIDebugPrintf("\n\n"); +#endif +} + + +U_NAMESPACE_END +U_NAMESPACE_USE + +//----------------------------------------------------------------------------- +// +// ubrk_swap - byte swap and char encoding swap of RBBI data +// +//----------------------------------------------------------------------------- + +U_CAPI int32_t U_EXPORT2 +ubrk_swap(const UDataSwapper *ds, const void *inData, int32_t length, void *outData, + UErrorCode *status) { + + if (status == nullptr || U_FAILURE(*status)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<-1 || (length>0 && outData==nullptr)) { + *status=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + // + // Check that the data header is for for break data. + // (Header contents are defined in genbrk.cpp) + // + const UDataInfo *pInfo = (const UDataInfo *)((const char *)inData+4); + if(!( pInfo->dataFormat[0]==0x42 && /* dataFormat="Brk " */ + pInfo->dataFormat[1]==0x72 && + pInfo->dataFormat[2]==0x6b && + pInfo->dataFormat[3]==0x20 && + RBBIDataWrapper::isDataVersionAcceptable(pInfo->formatVersion) )) { + udata_printError(ds, "ubrk_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized\n", + pInfo->dataFormat[0], pInfo->dataFormat[1], + pInfo->dataFormat[2], pInfo->dataFormat[3], + pInfo->formatVersion[0]); + *status=U_UNSUPPORTED_ERROR; + return 0; + } + + // + // Swap the data header. (This is the generic ICU Data Header, not the RBBI Specific + // RBBIDataHeader). This swap also conveniently gets us + // the size of the ICU d.h., which lets us locate the start + // of the RBBI specific data. + // + int32_t headerSize=udata_swapDataHeader(ds, inData, length, outData, status); + + + // + // Get the RRBI Data Header, and check that it appears to be OK. + // + const uint8_t *inBytes =(const uint8_t *)inData+headerSize; + RBBIDataHeader *rbbiDH = (RBBIDataHeader *)inBytes; + if (ds->readUInt32(rbbiDH->fMagic) != 0xb1a0 || + !RBBIDataWrapper::isDataVersionAcceptable(rbbiDH->fFormatVersion) || + ds->readUInt32(rbbiDH->fLength) < sizeof(RBBIDataHeader)) { + udata_printError(ds, "ubrk_swap(): RBBI Data header is invalid.\n"); + *status=U_UNSUPPORTED_ERROR; + return 0; + } + + // + // Prefight operation? Just return the size + // + int32_t breakDataLength = ds->readUInt32(rbbiDH->fLength); + int32_t totalSize = headerSize + breakDataLength; + if (length < 0) { + return totalSize; + } + + // + // Check that length passed in is consistent with length from RBBI data header. + // + if (length < totalSize) { + udata_printError(ds, "ubrk_swap(): too few bytes (%d after ICU Data header) for break data.\n", + breakDataLength); + *status=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + + // + // Swap the Data. Do the data itself first, then the RBBI Data Header, because + // we need to reference the header to locate the data, and an + // inplace swap of the header leaves it unusable. + // + uint8_t *outBytes = (uint8_t *)outData + headerSize; + RBBIDataHeader *outputDH = (RBBIDataHeader *)outBytes; + + int32_t tableStartOffset; + int32_t tableLength; + + // + // If not swapping in place, zero out the output buffer before starting. + // Individual tables and other data items within are aligned to 8 byte boundaries + // when originally created. Any unused space between items needs to be zero. + // + if (inBytes != outBytes) { + uprv_memset(outBytes, 0, breakDataLength); + } + + // + // Each state table begins with several 32 bit fields. Calculate the size + // in bytes of these. + // + int32_t topSize = offsetof(RBBIStateTable, fTableData); + + // Forward state table. + tableStartOffset = ds->readUInt32(rbbiDH->fFTable); + tableLength = ds->readUInt32(rbbiDH->fFTableLen); + + if (tableLength > 0) { + RBBIStateTable *rbbiST = (RBBIStateTable *)(inBytes+tableStartOffset); + UBool use8Bits = ds->readUInt32(rbbiST->fFlags) & RBBI_8BITS_ROWS; + + ds->swapArray32(ds, inBytes+tableStartOffset, topSize, + outBytes+tableStartOffset, status); + + // Swap the state table if the table is in 16 bits. + if (use8Bits) { + if (outBytes != inBytes) { + uprv_memmove(outBytes+tableStartOffset+topSize, + inBytes+tableStartOffset+topSize, + tableLength-topSize); + } + } else { + ds->swapArray16(ds, inBytes+tableStartOffset+topSize, tableLength-topSize, + outBytes+tableStartOffset+topSize, status); + } + } + + // Reverse state table. Same layout as forward table, above. + tableStartOffset = ds->readUInt32(rbbiDH->fRTable); + tableLength = ds->readUInt32(rbbiDH->fRTableLen); + + if (tableLength > 0) { + RBBIStateTable *rbbiST = (RBBIStateTable *)(inBytes+tableStartOffset); + UBool use8Bits = ds->readUInt32(rbbiST->fFlags) & RBBI_8BITS_ROWS; + + ds->swapArray32(ds, inBytes+tableStartOffset, topSize, + outBytes+tableStartOffset, status); + + // Swap the state table if the table is in 16 bits. + if (use8Bits) { + if (outBytes != inBytes) { + uprv_memmove(outBytes+tableStartOffset+topSize, + inBytes+tableStartOffset+topSize, + tableLength-topSize); + } + } else { + ds->swapArray16(ds, inBytes+tableStartOffset+topSize, tableLength-topSize, + outBytes+tableStartOffset+topSize, status); + } + } + + // Trie table for character categories + ucptrie_swap(ds, inBytes+ds->readUInt32(rbbiDH->fTrie), ds->readUInt32(rbbiDH->fTrieLen), + outBytes+ds->readUInt32(rbbiDH->fTrie), status); + + // Source Rules Text. It's UTF8 data + if (outBytes != inBytes) { + uprv_memmove(outBytes+ds->readUInt32(rbbiDH->fRuleSource), + inBytes+ds->readUInt32(rbbiDH->fRuleSource), + ds->readUInt32(rbbiDH->fRuleSourceLen)); + } + + // Table of rule status values. It's all int_32 values + ds->swapArray32(ds, inBytes+ds->readUInt32(rbbiDH->fStatusTable), ds->readUInt32(rbbiDH->fStatusTableLen), + outBytes+ds->readUInt32(rbbiDH->fStatusTable), status); + + // And, last, the header. + // It is all int32_t values except for fFormataVersion, which is an array of four bytes. + // Swap the whole thing as int32_t, then re-swap the one field. + // + ds->swapArray32(ds, inBytes, sizeof(RBBIDataHeader), outBytes, status); + ds->swapArray32(ds, outputDH->fFormatVersion, 4, outputDH->fFormatVersion, status); + + return totalSize; +} + + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ diff --git a/deps/icu-small/source/common/rbbidata.h b/deps/icu-small/source/common/rbbidata.h index 1bc76d5e5dc95f..97e24c51dae7e0 100644 --- a/deps/icu-small/source/common/rbbidata.h +++ b/deps/icu-small/source/common/rbbidata.h @@ -1,212 +1,212 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1999-2014 International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: rbbidata.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* RBBI data formats Includes -* -* Structs that describes the format of the Binary RBBI data, -* as it is stored in ICU's data file. -* -* RBBIDataWrapper - Instances of this class sit between the -* raw data structs and the RulesBasedBreakIterator objects -* that are created by applications. The wrapper class -* provides reference counting for the underlying data, -* and direct pointers to data that would not otherwise -* be accessible without ugly pointer arithmetic. The -* wrapper does not attempt to provide any higher level -* abstractions for the data itself. -* -* There will be only one instance of RBBIDataWrapper for any -* set of RBBI run time data being shared by instances -* (clones) of RulesBasedBreakIterator. -*/ - -#ifndef __RBBIDATA_H__ -#define __RBBIDATA_H__ - -#include "unicode/utypes.h" -#include "unicode/udata.h" -#include "udataswp.h" - -/** - * Swap RBBI data. See udataswp.h. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -ubrk_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -#ifdef __cplusplus - -#include "unicode/ucptrie.h" -#include "unicode/uobject.h" -#include "unicode/unistr.h" -#include "unicode/uversion.h" -#include "umutex.h" - - -U_NAMESPACE_BEGIN - -// The current RBBI data format version. -static const uint8_t RBBI_DATA_FORMAT_VERSION[] = {6, 0, 0, 0}; - -/* - * The following structs map exactly onto the raw data from ICU common data file. - */ -struct RBBIDataHeader { - uint32_t fMagic; /* == 0xbla0 */ - UVersionInfo fFormatVersion; /* Data Format. Same as the value in struct UDataInfo */ - /* if there is one associated with this data. */ - /* (version originates in rbbi, is copied to UDataInfo) */ - uint32_t fLength; /* Total length in bytes of this RBBI Data, */ - /* including all sections, not just the header. */ - uint32_t fCatCount; /* Number of character categories. */ - - /* */ - /* Offsets and sizes of each of the subsections within the RBBI data. */ - /* All offsets are bytes from the start of the RBBIDataHeader. */ - /* All sizes are in bytes. */ - /* */ - uint32_t fFTable; /* forward state transition table. */ - uint32_t fFTableLen; - uint32_t fRTable; /* Offset to the reverse state transition table. */ - uint32_t fRTableLen; - uint32_t fTrie; /* Offset to Trie data for character categories */ - uint32_t fTrieLen; - uint32_t fRuleSource; /* Offset to the source for for the break */ - uint32_t fRuleSourceLen; /* rules. Stored UChar *. */ - uint32_t fStatusTable; /* Offset to the table of rule status values */ - uint32_t fStatusTableLen; - - uint32_t fReserved[6]; /* Reserved for expansion */ - -}; - - - -template -struct RBBIStateTableRowT { - T fAccepting; // Non-zero if this row is for an accepting state. - // Value 0: not an accepting state. - // 1: (ACCEPTING_UNCONDITIONAL) Unconditional Accepting state. - // >1: Look-ahead match has completed. - // Actual boundary position happened earlier. - // Value here == fLookAhead in earlier - // state, at actual boundary pos. - T fLookAhead; // Non-zero if this row is for a state that - // corresponds to a '/' in the rule source. - // Value is the same as the fAccepting - // value for the rule (which will appear - // in a different state. - T fTagsIdx; // Non-zero if this row covers a {tagged} position - // from a rule. Value is the index in the - // StatusTable of the set of matching - // tags (rule status values) - T fNextState[1]; // Next State, indexed by char category. - // Variable-length array declared with length 1 - // to disable bounds checkers. - // Array Size is actually fData->fHeader->fCatCount - // CAUTION: see RBBITableBuilder::getTableSize() - // before changing anything here. -}; - -typedef RBBIStateTableRowT RBBIStateTableRow8; -typedef RBBIStateTableRowT RBBIStateTableRow16; - -constexpr uint16_t ACCEPTING_UNCONDITIONAL = 1; // Value constant for RBBIStateTableRow::fAccepting - -union RBBIStateTableRow { - RBBIStateTableRow16 r16; - RBBIStateTableRow8 r8; -}; - -struct RBBIStateTable { - uint32_t fNumStates; // Number of states. - uint32_t fRowLen; // Length of a state table row, in bytes. - uint32_t fDictCategoriesStart; // Char category number of the first dictionary - // char class, or the the largest category number + 1 - // if there are no dictionary categories. - uint32_t fLookAheadResultsSize; // Size of run-time array required for holding - // look-ahead results. Indexed by row.fLookAhead. - uint32_t fFlags; // Option Flags for this state table. - char fTableData[1]; // First RBBIStateTableRow begins here. - // Variable-length array declared with length 1 - // to disable bounds checkers. - // (making it char[] simplifies ugly address - // arithmetic for indexing variable length rows.) -}; - -constexpr uint32_t RBBI_LOOKAHEAD_HARD_BREAK = 1; -constexpr uint32_t RBBI_BOF_REQUIRED = 2; -constexpr uint32_t RBBI_8BITS_ROWS = 4; - - -/* */ -/* The reference counting wrapper class */ -/* */ -class RBBIDataWrapper : public UMemory { -public: - enum EDontAdopt { - kDontAdopt - }; - RBBIDataWrapper(const RBBIDataHeader *data, UErrorCode &status); - RBBIDataWrapper(const RBBIDataHeader *data, enum EDontAdopt dontAdopt, UErrorCode &status); - RBBIDataWrapper(UDataMemory* udm, UErrorCode &status); - ~RBBIDataWrapper(); - - static UBool isDataVersionAcceptable(const UVersionInfo version); - - void init0(); - void init(const RBBIDataHeader *data, UErrorCode &status); - RBBIDataWrapper *addReference(); - void removeReference(); - bool operator ==(const RBBIDataWrapper &other) const; - int32_t hashCode(); - const UnicodeString &getRuleSourceString() const; - void printData(); - void printTable(const char *heading, const RBBIStateTable *table); - - /* */ - /* Pointers to items within the data */ - /* */ - const RBBIDataHeader *fHeader; - const RBBIStateTable *fForwardTable; - const RBBIStateTable *fReverseTable; - const char *fRuleSource; - const int32_t *fRuleStatusTable; - - /* number of int32_t values in the rule status table. Used to sanity check indexing */ - int32_t fStatusMaxIdx; - - UCPTrie *fTrie; - -private: - u_atomic_int32_t fRefCount; - UDataMemory *fUDataMem; - UnicodeString fRuleString; - UBool fDontFreeData; - - RBBIDataWrapper(const RBBIDataWrapper &other) = delete; /* forbid copying of this class */ - RBBIDataWrapper &operator=(const RBBIDataWrapper &other) = delete; /* forbid copying of this class */ -}; - - - -U_NAMESPACE_END - -U_CFUNC UBool rbbi_cleanup(void); - -#endif /* C++ */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1999-2014 International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: rbbidata.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* RBBI data formats Includes +* +* Structs that describes the format of the Binary RBBI data, +* as it is stored in ICU's data file. +* +* RBBIDataWrapper - Instances of this class sit between the +* raw data structs and the RulesBasedBreakIterator objects +* that are created by applications. The wrapper class +* provides reference counting for the underlying data, +* and direct pointers to data that would not otherwise +* be accessible without ugly pointer arithmetic. The +* wrapper does not attempt to provide any higher level +* abstractions for the data itself. +* +* There will be only one instance of RBBIDataWrapper for any +* set of RBBI run time data being shared by instances +* (clones) of RulesBasedBreakIterator. +*/ + +#ifndef __RBBIDATA_H__ +#define __RBBIDATA_H__ + +#include "unicode/utypes.h" +#include "unicode/udata.h" +#include "udataswp.h" + +/** + * Swap RBBI data. See udataswp.h. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +ubrk_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +#ifdef __cplusplus + +#include "unicode/ucptrie.h" +#include "unicode/uobject.h" +#include "unicode/unistr.h" +#include "unicode/uversion.h" +#include "umutex.h" + + +U_NAMESPACE_BEGIN + +// The current RBBI data format version. +static const uint8_t RBBI_DATA_FORMAT_VERSION[] = {6, 0, 0, 0}; + +/* + * The following structs map exactly onto the raw data from ICU common data file. + */ +struct RBBIDataHeader { + uint32_t fMagic; /* == 0xbla0 */ + UVersionInfo fFormatVersion; /* Data Format. Same as the value in struct UDataInfo */ + /* if there is one associated with this data. */ + /* (version originates in rbbi, is copied to UDataInfo) */ + uint32_t fLength; /* Total length in bytes of this RBBI Data, */ + /* including all sections, not just the header. */ + uint32_t fCatCount; /* Number of character categories. */ + + /* */ + /* Offsets and sizes of each of the subsections within the RBBI data. */ + /* All offsets are bytes from the start of the RBBIDataHeader. */ + /* All sizes are in bytes. */ + /* */ + uint32_t fFTable; /* forward state transition table. */ + uint32_t fFTableLen; + uint32_t fRTable; /* Offset to the reverse state transition table. */ + uint32_t fRTableLen; + uint32_t fTrie; /* Offset to Trie data for character categories */ + uint32_t fTrieLen; + uint32_t fRuleSource; /* Offset to the source for for the break */ + uint32_t fRuleSourceLen; /* rules. Stored char16_t *. */ + uint32_t fStatusTable; /* Offset to the table of rule status values */ + uint32_t fStatusTableLen; + + uint32_t fReserved[6]; /* Reserved for expansion */ + +}; + + + +template +struct RBBIStateTableRowT { + T fAccepting; // Non-zero if this row is for an accepting state. + // Value 0: not an accepting state. + // 1: (ACCEPTING_UNCONDITIONAL) Unconditional Accepting state. + // >1: Look-ahead match has completed. + // Actual boundary position happened earlier. + // Value here == fLookAhead in earlier + // state, at actual boundary pos. + T fLookAhead; // Non-zero if this row is for a state that + // corresponds to a '/' in the rule source. + // Value is the same as the fAccepting + // value for the rule (which will appear + // in a different state. + T fTagsIdx; // Non-zero if this row covers a {tagged} position + // from a rule. Value is the index in the + // StatusTable of the set of matching + // tags (rule status values) + T fNextState[1]; // Next State, indexed by char category. + // Variable-length array declared with length 1 + // to disable bounds checkers. + // Array Size is actually fData->fHeader->fCatCount + // CAUTION: see RBBITableBuilder::getTableSize() + // before changing anything here. +}; + +typedef RBBIStateTableRowT RBBIStateTableRow8; +typedef RBBIStateTableRowT RBBIStateTableRow16; + +constexpr uint16_t ACCEPTING_UNCONDITIONAL = 1; // Value constant for RBBIStateTableRow::fAccepting + +union RBBIStateTableRow { + RBBIStateTableRow16 r16; + RBBIStateTableRow8 r8; +}; + +struct RBBIStateTable { + uint32_t fNumStates; // Number of states. + uint32_t fRowLen; // Length of a state table row, in bytes. + uint32_t fDictCategoriesStart; // Char category number of the first dictionary + // char class, or the the largest category number + 1 + // if there are no dictionary categories. + uint32_t fLookAheadResultsSize; // Size of run-time array required for holding + // look-ahead results. Indexed by row.fLookAhead. + uint32_t fFlags; // Option Flags for this state table. + char fTableData[1]; // First RBBIStateTableRow begins here. + // Variable-length array declared with length 1 + // to disable bounds checkers. + // (making it char[] simplifies ugly address + // arithmetic for indexing variable length rows.) +}; + +constexpr uint32_t RBBI_LOOKAHEAD_HARD_BREAK = 1; +constexpr uint32_t RBBI_BOF_REQUIRED = 2; +constexpr uint32_t RBBI_8BITS_ROWS = 4; + + +/* */ +/* The reference counting wrapper class */ +/* */ +class RBBIDataWrapper : public UMemory { +public: + enum EDontAdopt { + kDontAdopt + }; + RBBIDataWrapper(const RBBIDataHeader *data, UErrorCode &status); + RBBIDataWrapper(const RBBIDataHeader *data, enum EDontAdopt dontAdopt, UErrorCode &status); + RBBIDataWrapper(UDataMemory* udm, UErrorCode &status); + ~RBBIDataWrapper(); + + static UBool isDataVersionAcceptable(const UVersionInfo version); + + void init0(); + void init(const RBBIDataHeader *data, UErrorCode &status); + RBBIDataWrapper *addReference(); + void removeReference(); + bool operator ==(const RBBIDataWrapper &other) const; + int32_t hashCode(); + const UnicodeString &getRuleSourceString() const; + void printData(); + void printTable(const char *heading, const RBBIStateTable *table); + + /* */ + /* Pointers to items within the data */ + /* */ + const RBBIDataHeader *fHeader; + const RBBIStateTable *fForwardTable; + const RBBIStateTable *fReverseTable; + const char *fRuleSource; + const int32_t *fRuleStatusTable; + + /* number of int32_t values in the rule status table. Used to sanity check indexing */ + int32_t fStatusMaxIdx; + + UCPTrie *fTrie; + +private: + u_atomic_int32_t fRefCount; + UDataMemory *fUDataMem; + UnicodeString fRuleString; + UBool fDontFreeData; + + RBBIDataWrapper(const RBBIDataWrapper &other) = delete; /* forbid copying of this class */ + RBBIDataWrapper &operator=(const RBBIDataWrapper &other) = delete; /* forbid copying of this class */ +}; + + + +U_NAMESPACE_END + +U_CFUNC UBool rbbi_cleanup(); + +#endif /* C++ */ + +#endif diff --git a/deps/icu-small/source/common/rbbinode.cpp b/deps/icu-small/source/common/rbbinode.cpp index da5937cafd7e9c..a9dff79f5af786 100644 --- a/deps/icu-small/source/common/rbbinode.cpp +++ b/deps/icu-small/source/common/rbbinode.cpp @@ -1,373 +1,373 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -*************************************************************************** -* Copyright (C) 2002-2016 International Business Machines Corporation * -* and others. All rights reserved. * -*************************************************************************** -*/ - -// -// File: rbbinode.cpp -// -// Implementation of class RBBINode, which represents a node in the -// tree generated when parsing the Rules Based Break Iterator rules. -// -// This "Class" is actually closer to a struct. -// Code using it is expected to directly access fields much of the time. -// - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/unistr.h" -#include "unicode/uniset.h" -#include "unicode/uchar.h" -#include "unicode/parsepos.h" - -#include "cstr.h" -#include "uvector.h" - -#include "rbbirb.h" -#include "rbbinode.h" - -#include "uassert.h" - - -U_NAMESPACE_BEGIN - -#ifdef RBBI_DEBUG -static int gLastSerial = 0; -#endif - - -//------------------------------------------------------------------------- -// -// Constructor. Just set the fields to reasonable default values. -// -//------------------------------------------------------------------------- -RBBINode::RBBINode(NodeType t) : UMemory() { -#ifdef RBBI_DEBUG - fSerialNum = ++gLastSerial; -#endif - fType = t; - fParent = NULL; - fLeftChild = NULL; - fRightChild = NULL; - fInputSet = NULL; - fFirstPos = 0; - fLastPos = 0; - fNullable = false; - fLookAheadEnd = false; - fRuleRoot = false; - fChainIn = false; - fVal = 0; - fPrecedence = precZero; - - UErrorCode status = U_ZERO_ERROR; - fFirstPosSet = new UVector(status); // TODO - get a real status from somewhere - fLastPosSet = new UVector(status); - fFollowPos = new UVector(status); - if (t==opCat) {fPrecedence = precOpCat;} - else if (t==opOr) {fPrecedence = precOpOr;} - else if (t==opStart) {fPrecedence = precStart;} - else if (t==opLParen) {fPrecedence = precLParen;} - -} - - -RBBINode::RBBINode(const RBBINode &other) : UMemory(other) { -#ifdef RBBI_DEBUG - fSerialNum = ++gLastSerial; -#endif - fType = other.fType; - fParent = NULL; - fLeftChild = NULL; - fRightChild = NULL; - fInputSet = other.fInputSet; - fPrecedence = other.fPrecedence; - fText = other.fText; - fFirstPos = other.fFirstPos; - fLastPos = other.fLastPos; - fNullable = other.fNullable; - fVal = other.fVal; - fRuleRoot = false; - fChainIn = other.fChainIn; - UErrorCode status = U_ZERO_ERROR; - fFirstPosSet = new UVector(status); // TODO - get a real status from somewhere - fLastPosSet = new UVector(status); - fFollowPos = new UVector(status); -} - - -//------------------------------------------------------------------------- -// -// Destructor. Deletes both this node AND any child nodes, -// except in the case of variable reference nodes. For -// these, the l. child points back to the definition, which -// is common for all references to the variable, meaning -// it can't be deleted here. -// -//------------------------------------------------------------------------- -RBBINode::~RBBINode() { - // printf("deleting node %8x serial %4d\n", this, this->fSerialNum); - delete fInputSet; - fInputSet = NULL; - - switch (this->fType) { - case varRef: - case setRef: - // for these node types, multiple instances point to the same "children" - // Storage ownership of children handled elsewhere. Don't delete here. - break; - - default: - delete fLeftChild; - fLeftChild = NULL; - delete fRightChild; - fRightChild = NULL; - } - - - delete fFirstPosSet; - delete fLastPosSet; - delete fFollowPos; - -} - - -//------------------------------------------------------------------------- -// -// cloneTree Make a copy of the subtree rooted at this node. -// Discard any variable references encountered along the way, -// and replace with copies of the variable's definitions. -// Used to replicate the expression underneath variable -// references in preparation for generating the DFA tables. -// -//------------------------------------------------------------------------- -RBBINode *RBBINode::cloneTree() { - RBBINode *n; - - if (fType == RBBINode::varRef) { - // If the current node is a variable reference, skip over it - // and clone the definition of the variable instead. - n = fLeftChild->cloneTree(); - } else if (fType == RBBINode::uset) { - n = this; - } else { - n = new RBBINode(*this); - // Check for null pointer. - if (n != NULL) { - if (fLeftChild != NULL) { - n->fLeftChild = fLeftChild->cloneTree(); - n->fLeftChild->fParent = n; - } - if (fRightChild != NULL) { - n->fRightChild = fRightChild->cloneTree(); - n->fRightChild->fParent = n; - } - } - } - return n; -} - - - -//------------------------------------------------------------------------- -// -// flattenVariables Walk a parse tree, replacing any variable -// references with a copy of the variable's definition. -// Aside from variables, the tree is not changed. -// -// Return the root of the tree. If the root was not a variable -// reference, it remains unchanged - the root we started with -// is the root we return. If, however, the root was a variable -// reference, the root of the newly cloned replacement tree will -// be returned, and the original tree deleted. -// -// This function works by recursively walking the tree -// without doing anything until a variable reference is -// found, then calling cloneTree() at that point. Any -// nested references are handled by cloneTree(), not here. -// -//------------------------------------------------------------------------- -RBBINode *RBBINode::flattenVariables() { - if (fType == varRef) { - RBBINode *retNode = fLeftChild->cloneTree(); - if (retNode != NULL) { - retNode->fRuleRoot = this->fRuleRoot; - retNode->fChainIn = this->fChainIn; - } - delete this; // TODO: undefined behavior. Fix. - return retNode; - } - - if (fLeftChild != NULL) { - fLeftChild = fLeftChild->flattenVariables(); - fLeftChild->fParent = this; - } - if (fRightChild != NULL) { - fRightChild = fRightChild->flattenVariables(); - fRightChild->fParent = this; - } - return this; -} - - -//------------------------------------------------------------------------- -// -// flattenSets Walk the parse tree, replacing any nodes of type setRef -// with a copy of the expression tree for the set. A set's -// equivalent expression tree is precomputed and saved as -// the left child of the uset node. -// -//------------------------------------------------------------------------- -void RBBINode::flattenSets() { - U_ASSERT(fType != setRef); - - if (fLeftChild != NULL) { - if (fLeftChild->fType==setRef) { - RBBINode *setRefNode = fLeftChild; - RBBINode *usetNode = setRefNode->fLeftChild; - RBBINode *replTree = usetNode->fLeftChild; - fLeftChild = replTree->cloneTree(); - fLeftChild->fParent = this; - delete setRefNode; - } else { - fLeftChild->flattenSets(); - } - } - - if (fRightChild != NULL) { - if (fRightChild->fType==setRef) { - RBBINode *setRefNode = fRightChild; - RBBINode *usetNode = setRefNode->fLeftChild; - RBBINode *replTree = usetNode->fLeftChild; - fRightChild = replTree->cloneTree(); - fRightChild->fParent = this; - delete setRefNode; - } else { - fRightChild->flattenSets(); - } - } -} - - - -//------------------------------------------------------------------------- -// -// findNodes() Locate all the nodes of the specified type, starting -// at the specified root. -// -//------------------------------------------------------------------------- -void RBBINode::findNodes(UVector *dest, RBBINode::NodeType kind, UErrorCode &status) { - /* test for buffer overflows */ - if (U_FAILURE(status)) { - return; - } - U_ASSERT(!dest->hasDeleter()); - if (fType == kind) { - dest->addElement(this, status); - } - if (fLeftChild != NULL) { - fLeftChild->findNodes(dest, kind, status); - } - if (fRightChild != NULL) { - fRightChild->findNodes(dest, kind, status); - } -} - - -//------------------------------------------------------------------------- -// -// print. Print out a single node, for debugging. -// -//------------------------------------------------------------------------- -#ifdef RBBI_DEBUG - -static int32_t serial(const RBBINode *node) { - return (node == NULL? -1 : node->fSerialNum); -} - - -void RBBINode::printNode(const RBBINode *node) { - static const char * const nodeTypeNames[] = { - "setRef", - "uset", - "varRef", - "leafChar", - "lookAhead", - "tag", - "endMark", - "opStart", - "opCat", - "opOr", - "opStar", - "opPlus", - "opQuestion", - "opBreak", - "opReverse", - "opLParen" - }; - - if (node==NULL) { - RBBIDebugPrintf("%10p", (void *)node); - } else { - RBBIDebugPrintf("%10p %5d %12s %c%c %5d %5d %5d %6d %d ", - (void *)node, node->fSerialNum, nodeTypeNames[node->fType], - node->fRuleRoot?'R':' ', node->fChainIn?'C':' ', - serial(node->fLeftChild), serial(node->fRightChild), serial(node->fParent), - node->fFirstPos, node->fVal); - if (node->fType == varRef) { - RBBI_DEBUG_printUnicodeString(node->fText); - } - } - RBBIDebugPrintf("\n"); -} -#endif - - -#ifdef RBBI_DEBUG -U_CFUNC void RBBI_DEBUG_printUnicodeString(const UnicodeString &s, int minWidth) { - RBBIDebugPrintf("%*s", minWidth, CStr(s)()); -} -#endif - - -//------------------------------------------------------------------------- -// -// print. Print out the tree of nodes rooted at "this" -// -//------------------------------------------------------------------------- -#ifdef RBBI_DEBUG -void RBBINode::printNodeHeader() { - RBBIDebugPrintf(" Address serial type LeftChild RightChild Parent position value\n"); -} - -void RBBINode::printTree(const RBBINode *node, UBool printHeading) { - if (printHeading) { - printNodeHeader(); - } - printNode(node); - if (node != NULL) { - // Only dump the definition under a variable reference if asked to. - // Unconditionally dump children of all other node types. - if (node->fType != varRef) { - if (node->fLeftChild != NULL) { - printTree(node->fLeftChild, false); - } - - if (node->fRightChild != NULL) { - printTree(node->fRightChild, false); - } - } - } -} -#endif - - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +*************************************************************************** +* Copyright (C) 2002-2016 International Business Machines Corporation * +* and others. All rights reserved. * +*************************************************************************** +*/ + +// +// File: rbbinode.cpp +// +// Implementation of class RBBINode, which represents a node in the +// tree generated when parsing the Rules Based Break Iterator rules. +// +// This "Class" is actually closer to a struct. +// Code using it is expected to directly access fields much of the time. +// + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/unistr.h" +#include "unicode/uniset.h" +#include "unicode/uchar.h" +#include "unicode/parsepos.h" + +#include "cstr.h" +#include "uvector.h" + +#include "rbbirb.h" +#include "rbbinode.h" + +#include "uassert.h" + + +U_NAMESPACE_BEGIN + +#ifdef RBBI_DEBUG +static int gLastSerial = 0; +#endif + + +//------------------------------------------------------------------------- +// +// Constructor. Just set the fields to reasonable default values. +// +//------------------------------------------------------------------------- +RBBINode::RBBINode(NodeType t) : UMemory() { +#ifdef RBBI_DEBUG + fSerialNum = ++gLastSerial; +#endif + fType = t; + fParent = nullptr; + fLeftChild = nullptr; + fRightChild = nullptr; + fInputSet = nullptr; + fFirstPos = 0; + fLastPos = 0; + fNullable = false; + fLookAheadEnd = false; + fRuleRoot = false; + fChainIn = false; + fVal = 0; + fPrecedence = precZero; + + UErrorCode status = U_ZERO_ERROR; + fFirstPosSet = new UVector(status); // TODO - get a real status from somewhere + fLastPosSet = new UVector(status); + fFollowPos = new UVector(status); + if (t==opCat) {fPrecedence = precOpCat;} + else if (t==opOr) {fPrecedence = precOpOr;} + else if (t==opStart) {fPrecedence = precStart;} + else if (t==opLParen) {fPrecedence = precLParen;} + +} + + +RBBINode::RBBINode(const RBBINode &other) : UMemory(other) { +#ifdef RBBI_DEBUG + fSerialNum = ++gLastSerial; +#endif + fType = other.fType; + fParent = nullptr; + fLeftChild = nullptr; + fRightChild = nullptr; + fInputSet = other.fInputSet; + fPrecedence = other.fPrecedence; + fText = other.fText; + fFirstPos = other.fFirstPos; + fLastPos = other.fLastPos; + fNullable = other.fNullable; + fVal = other.fVal; + fRuleRoot = false; + fChainIn = other.fChainIn; + UErrorCode status = U_ZERO_ERROR; + fFirstPosSet = new UVector(status); // TODO - get a real status from somewhere + fLastPosSet = new UVector(status); + fFollowPos = new UVector(status); +} + + +//------------------------------------------------------------------------- +// +// Destructor. Deletes both this node AND any child nodes, +// except in the case of variable reference nodes. For +// these, the l. child points back to the definition, which +// is common for all references to the variable, meaning +// it can't be deleted here. +// +//------------------------------------------------------------------------- +RBBINode::~RBBINode() { + // printf("deleting node %8x serial %4d\n", this, this->fSerialNum); + delete fInputSet; + fInputSet = nullptr; + + switch (this->fType) { + case varRef: + case setRef: + // for these node types, multiple instances point to the same "children" + // Storage ownership of children handled elsewhere. Don't delete here. + break; + + default: + delete fLeftChild; + fLeftChild = nullptr; + delete fRightChild; + fRightChild = nullptr; + } + + + delete fFirstPosSet; + delete fLastPosSet; + delete fFollowPos; + +} + + +//------------------------------------------------------------------------- +// +// cloneTree Make a copy of the subtree rooted at this node. +// Discard any variable references encountered along the way, +// and replace with copies of the variable's definitions. +// Used to replicate the expression underneath variable +// references in preparation for generating the DFA tables. +// +//------------------------------------------------------------------------- +RBBINode *RBBINode::cloneTree() { + RBBINode *n; + + if (fType == RBBINode::varRef) { + // If the current node is a variable reference, skip over it + // and clone the definition of the variable instead. + n = fLeftChild->cloneTree(); + } else if (fType == RBBINode::uset) { + n = this; + } else { + n = new RBBINode(*this); + // Check for null pointer. + if (n != nullptr) { + if (fLeftChild != nullptr) { + n->fLeftChild = fLeftChild->cloneTree(); + n->fLeftChild->fParent = n; + } + if (fRightChild != nullptr) { + n->fRightChild = fRightChild->cloneTree(); + n->fRightChild->fParent = n; + } + } + } + return n; +} + + + +//------------------------------------------------------------------------- +// +// flattenVariables Walk a parse tree, replacing any variable +// references with a copy of the variable's definition. +// Aside from variables, the tree is not changed. +// +// Return the root of the tree. If the root was not a variable +// reference, it remains unchanged - the root we started with +// is the root we return. If, however, the root was a variable +// reference, the root of the newly cloned replacement tree will +// be returned, and the original tree deleted. +// +// This function works by recursively walking the tree +// without doing anything until a variable reference is +// found, then calling cloneTree() at that point. Any +// nested references are handled by cloneTree(), not here. +// +//------------------------------------------------------------------------- +RBBINode *RBBINode::flattenVariables() { + if (fType == varRef) { + RBBINode *retNode = fLeftChild->cloneTree(); + if (retNode != nullptr) { + retNode->fRuleRoot = this->fRuleRoot; + retNode->fChainIn = this->fChainIn; + } + delete this; // TODO: undefined behavior. Fix. + return retNode; + } + + if (fLeftChild != nullptr) { + fLeftChild = fLeftChild->flattenVariables(); + fLeftChild->fParent = this; + } + if (fRightChild != nullptr) { + fRightChild = fRightChild->flattenVariables(); + fRightChild->fParent = this; + } + return this; +} + + +//------------------------------------------------------------------------- +// +// flattenSets Walk the parse tree, replacing any nodes of type setRef +// with a copy of the expression tree for the set. A set's +// equivalent expression tree is precomputed and saved as +// the left child of the uset node. +// +//------------------------------------------------------------------------- +void RBBINode::flattenSets() { + U_ASSERT(fType != setRef); + + if (fLeftChild != nullptr) { + if (fLeftChild->fType==setRef) { + RBBINode *setRefNode = fLeftChild; + RBBINode *usetNode = setRefNode->fLeftChild; + RBBINode *replTree = usetNode->fLeftChild; + fLeftChild = replTree->cloneTree(); + fLeftChild->fParent = this; + delete setRefNode; + } else { + fLeftChild->flattenSets(); + } + } + + if (fRightChild != nullptr) { + if (fRightChild->fType==setRef) { + RBBINode *setRefNode = fRightChild; + RBBINode *usetNode = setRefNode->fLeftChild; + RBBINode *replTree = usetNode->fLeftChild; + fRightChild = replTree->cloneTree(); + fRightChild->fParent = this; + delete setRefNode; + } else { + fRightChild->flattenSets(); + } + } +} + + + +//------------------------------------------------------------------------- +// +// findNodes() Locate all the nodes of the specified type, starting +// at the specified root. +// +//------------------------------------------------------------------------- +void RBBINode::findNodes(UVector *dest, RBBINode::NodeType kind, UErrorCode &status) { + /* test for buffer overflows */ + if (U_FAILURE(status)) { + return; + } + U_ASSERT(!dest->hasDeleter()); + if (fType == kind) { + dest->addElement(this, status); + } + if (fLeftChild != nullptr) { + fLeftChild->findNodes(dest, kind, status); + } + if (fRightChild != nullptr) { + fRightChild->findNodes(dest, kind, status); + } +} + + +//------------------------------------------------------------------------- +// +// print. Print out a single node, for debugging. +// +//------------------------------------------------------------------------- +#ifdef RBBI_DEBUG + +static int32_t serial(const RBBINode *node) { + return (node == nullptr? -1 : node->fSerialNum); +} + + +void RBBINode::printNode(const RBBINode *node) { + static const char * const nodeTypeNames[] = { + "setRef", + "uset", + "varRef", + "leafChar", + "lookAhead", + "tag", + "endMark", + "opStart", + "opCat", + "opOr", + "opStar", + "opPlus", + "opQuestion", + "opBreak", + "opReverse", + "opLParen" + }; + + if (node==nullptr) { + RBBIDebugPrintf("%10p", (void *)node); + } else { + RBBIDebugPrintf("%10p %5d %12s %c%c %5d %5d %5d %6d %d ", + (void *)node, node->fSerialNum, nodeTypeNames[node->fType], + node->fRuleRoot?'R':' ', node->fChainIn?'C':' ', + serial(node->fLeftChild), serial(node->fRightChild), serial(node->fParent), + node->fFirstPos, node->fVal); + if (node->fType == varRef) { + RBBI_DEBUG_printUnicodeString(node->fText); + } + } + RBBIDebugPrintf("\n"); +} +#endif + + +#ifdef RBBI_DEBUG +U_CFUNC void RBBI_DEBUG_printUnicodeString(const UnicodeString &s, int minWidth) { + RBBIDebugPrintf("%*s", minWidth, CStr(s)()); +} +#endif + + +//------------------------------------------------------------------------- +// +// print. Print out the tree of nodes rooted at "this" +// +//------------------------------------------------------------------------- +#ifdef RBBI_DEBUG +void RBBINode::printNodeHeader() { + RBBIDebugPrintf(" Address serial type LeftChild RightChild Parent position value\n"); +} + +void RBBINode::printTree(const RBBINode *node, UBool printHeading) { + if (printHeading) { + printNodeHeader(); + } + printNode(node); + if (node != nullptr) { + // Only dump the definition under a variable reference if asked to. + // Unconditionally dump children of all other node types. + if (node->fType != varRef) { + if (node->fLeftChild != nullptr) { + printTree(node->fLeftChild, false); + } + + if (node->fRightChild != nullptr) { + printTree(node->fRightChild, false); + } + } + } +} +#endif + + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ diff --git a/deps/icu-small/source/common/rbbinode.h b/deps/icu-small/source/common/rbbinode.h index 4ed84d4e073fad..a0106e5ad3e3a9 100644 --- a/deps/icu-small/source/common/rbbinode.h +++ b/deps/icu-small/source/common/rbbinode.h @@ -1,127 +1,127 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/******************************************************************** - * COPYRIGHT: - * Copyright (c) 2001-2016, International Business Machines Corporation and - * others. All Rights Reserved. - ********************************************************************/ - -#ifndef RBBINODE_H -#define RBBINODE_H - -#include "unicode/utypes.h" -#include "unicode/unistr.h" -#include "unicode/uobject.h" - -// -// class RBBINode -// -// Represents a node in the parse tree generated when reading -// a rule file. -// - -U_NAMESPACE_BEGIN - -class UnicodeSet; -class UVector; - -class RBBINode : public UMemory { - public: - enum NodeType { - setRef, - uset, - varRef, - leafChar, - lookAhead, - tag, - endMark, - opStart, - opCat, - opOr, - opStar, - opPlus, - opQuestion, - opBreak, - opReverse, - opLParen - }; - - enum OpPrecedence { - precZero, - precStart, - precLParen, - precOpOr, - precOpCat - }; - - NodeType fType; - RBBINode *fParent; - RBBINode *fLeftChild; - RBBINode *fRightChild; - UnicodeSet *fInputSet; // For uset nodes only. - OpPrecedence fPrecedence; // For binary ops only. - - UnicodeString fText; // Text corresponding to this node. - // May be lazily evaluated when (if) needed - // for some node types. - int fFirstPos; // Position in the rule source string of the - // first text associated with the node. - // If there's a left child, this will be the same - // as that child's left pos. - int fLastPos; // Last position in the rule source string - // of any text associated with this node. - // If there's a right child, this will be the same - // as that child's last position. - - UBool fNullable; // See Aho. - int32_t fVal; // For leafChar nodes, the value. - // Values are the character category, - // corresponds to columns in the final - // state transition table. - - UBool fLookAheadEnd; // For endMark nodes, set true if - // marking the end of a look-ahead rule. - - UBool fRuleRoot; // True if this node is the root of a rule. - UBool fChainIn; // True if chaining into this rule is allowed - // (no '^' present). - - UVector *fFirstPosSet; - UVector *fLastPosSet; // TODO: rename fFirstPos & fLastPos to avoid confusion. - UVector *fFollowPos; - - - RBBINode(NodeType t); - RBBINode(const RBBINode &other); - ~RBBINode(); - - RBBINode *cloneTree(); - RBBINode *flattenVariables(); - void flattenSets(); - void findNodes(UVector *dest, RBBINode::NodeType kind, UErrorCode &status); - -#ifdef RBBI_DEBUG - static void printNodeHeader(); - static void printNode(const RBBINode *n); - static void printTree(const RBBINode *n, UBool withHeading); -#endif - - private: - RBBINode &operator = (const RBBINode &other); // No defs. - bool operator == (const RBBINode &other); // Private, so these functions won't accidentally be used. - -#ifdef RBBI_DEBUG - public: - int fSerialNum; // Debugging aids. -#endif -}; - -#ifdef RBBI_DEBUG -U_CFUNC void -RBBI_DEBUG_printUnicodeString(const UnicodeString &s, int minWidth=0); -#endif - -U_NAMESPACE_END - -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2001-2016, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +#ifndef RBBINODE_H +#define RBBINODE_H + +#include "unicode/utypes.h" +#include "unicode/unistr.h" +#include "unicode/uobject.h" + +// +// class RBBINode +// +// Represents a node in the parse tree generated when reading +// a rule file. +// + +U_NAMESPACE_BEGIN + +class UnicodeSet; +class UVector; + +class RBBINode : public UMemory { + public: + enum NodeType { + setRef, + uset, + varRef, + leafChar, + lookAhead, + tag, + endMark, + opStart, + opCat, + opOr, + opStar, + opPlus, + opQuestion, + opBreak, + opReverse, + opLParen + }; + + enum OpPrecedence { + precZero, + precStart, + precLParen, + precOpOr, + precOpCat + }; + + NodeType fType; + RBBINode *fParent; + RBBINode *fLeftChild; + RBBINode *fRightChild; + UnicodeSet *fInputSet; // For uset nodes only. + OpPrecedence fPrecedence; // For binary ops only. + + UnicodeString fText; // Text corresponding to this node. + // May be lazily evaluated when (if) needed + // for some node types. + int fFirstPos; // Position in the rule source string of the + // first text associated with the node. + // If there's a left child, this will be the same + // as that child's left pos. + int fLastPos; // Last position in the rule source string + // of any text associated with this node. + // If there's a right child, this will be the same + // as that child's last position. + + UBool fNullable; // See Aho. + int32_t fVal; // For leafChar nodes, the value. + // Values are the character category, + // corresponds to columns in the final + // state transition table. + + UBool fLookAheadEnd; // For endMark nodes, set true if + // marking the end of a look-ahead rule. + + UBool fRuleRoot; // True if this node is the root of a rule. + UBool fChainIn; // True if chaining into this rule is allowed + // (no '^' present). + + UVector *fFirstPosSet; + UVector *fLastPosSet; // TODO: rename fFirstPos & fLastPos to avoid confusion. + UVector *fFollowPos; + + + RBBINode(NodeType t); + RBBINode(const RBBINode &other); + ~RBBINode(); + + RBBINode *cloneTree(); + RBBINode *flattenVariables(); + void flattenSets(); + void findNodes(UVector *dest, RBBINode::NodeType kind, UErrorCode &status); + +#ifdef RBBI_DEBUG + static void printNodeHeader(); + static void printNode(const RBBINode *n); + static void printTree(const RBBINode *n, UBool withHeading); +#endif + + private: + RBBINode &operator = (const RBBINode &other); // No defs. + bool operator == (const RBBINode &other); // Private, so these functions won't accidentally be used. + +#ifdef RBBI_DEBUG + public: + int fSerialNum; // Debugging aids. +#endif +}; + +#ifdef RBBI_DEBUG +U_CFUNC void +RBBI_DEBUG_printUnicodeString(const UnicodeString &s, int minWidth=0); +#endif + +U_NAMESPACE_END + +#endif + diff --git a/deps/icu-small/source/common/rbbirb.cpp b/deps/icu-small/source/common/rbbirb.cpp index a9d76f248272b6..a01242503b657c 100644 --- a/deps/icu-small/source/common/rbbirb.cpp +++ b/deps/icu-small/source/common/rbbirb.cpp @@ -1,361 +1,361 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// file: rbbirb.cpp -// -// Copyright (C) 2002-2011, International Business Machines Corporation and others. -// All Rights Reserved. -// -// This file contains the RBBIRuleBuilder class implementation. This is the main class for -// building (compiling) break rules into the tables required by the runtime -// RBBI engine. -// - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/brkiter.h" -#include "unicode/rbbi.h" -#include "unicode/ubrk.h" -#include "unicode/unistr.h" -#include "unicode/uniset.h" -#include "unicode/uchar.h" -#include "unicode/uchriter.h" -#include "unicode/ustring.h" -#include "unicode/parsepos.h" -#include "unicode/parseerr.h" - -#include "cmemory.h" -#include "cstring.h" -#include "rbbirb.h" -#include "rbbinode.h" -#include "rbbiscan.h" -#include "rbbisetb.h" -#include "rbbitblb.h" -#include "rbbidata.h" -#include "uassert.h" - - -U_NAMESPACE_BEGIN - - -//---------------------------------------------------------------------------------------- -// -// Constructor. -// -//---------------------------------------------------------------------------------------- -RBBIRuleBuilder::RBBIRuleBuilder(const UnicodeString &rules, - UParseError *parseErr, - UErrorCode &status) - : fRules(rules), fStrippedRules(rules) -{ - fStatus = &status; // status is checked below - fParseError = parseErr; - fDebugEnv = NULL; -#ifdef RBBI_DEBUG - fDebugEnv = getenv("U_RBBIDEBUG"); -#endif - - - fForwardTree = NULL; - fReverseTree = NULL; - fSafeFwdTree = NULL; - fSafeRevTree = NULL; - fDefaultTree = &fForwardTree; - fForwardTable = NULL; - fRuleStatusVals = NULL; - fChainRules = false; - fLBCMNoChain = false; - fLookAheadHardBreak = false; - fUSetNodes = NULL; - fRuleStatusVals = NULL; - fScanner = NULL; - fSetBuilder = NULL; - if (parseErr) { - uprv_memset(parseErr, 0, sizeof(UParseError)); - } - - if (U_FAILURE(status)) { - return; - } - - fUSetNodes = new UVector(status); // bcos status gets overwritten here - fRuleStatusVals = new UVector(status); - fScanner = new RBBIRuleScanner(this); - fSetBuilder = new RBBISetBuilder(this); - if (U_FAILURE(status)) { - return; - } - if(fSetBuilder == 0 || fScanner == 0 || fUSetNodes == 0 || fRuleStatusVals == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - } -} - - - -//---------------------------------------------------------------------------------------- -// -// Destructor -// -//---------------------------------------------------------------------------------------- -RBBIRuleBuilder::~RBBIRuleBuilder() { - - int i; - for (i=0; ; i++) { - RBBINode *n = (RBBINode *)fUSetNodes->elementAt(i); - if (n==NULL) { - break; - } - delete n; - } - - delete fUSetNodes; - delete fSetBuilder; - delete fForwardTable; - delete fForwardTree; - delete fReverseTree; - delete fSafeFwdTree; - delete fSafeRevTree; - delete fScanner; - delete fRuleStatusVals; -} - - - - - -//---------------------------------------------------------------------------------------- -// -// flattenData() - Collect up the compiled RBBI rule data and put it into -// the format for saving in ICU data files, -// which is also the format needed by the RBBI runtime engine. -// -//---------------------------------------------------------------------------------------- -static int32_t align8(int32_t i) {return (i+7) & 0xfffffff8;} - -RBBIDataHeader *RBBIRuleBuilder::flattenData() { - int32_t i; - - if (U_FAILURE(*fStatus)) { - return NULL; - } - - // Remove whitespace from the rules to make it smaller. - // The rule parser has already removed comments. - fStrippedRules = fScanner->stripRules(fStrippedRules); - - // Calculate the size of each section in the data. - // Sizes here are padded up to a multiple of 8 for better memory alignment. - // Sections sizes actually stored in the header are for the actual data - // without the padding. - // - int32_t headerSize = align8(sizeof(RBBIDataHeader)); - 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 rulesLengthInUTF8 = 0; - u_strToUTF8WithSub(0, 0, &rulesLengthInUTF8, - fStrippedRules.getBuffer(), fStrippedRules.length(), - 0xfffd, nullptr, fStatus); - *fStatus = U_ZERO_ERROR; - - int32_t rulesSize = align8((rulesLengthInUTF8+1)); - - int32_t totalSize = headerSize - + forwardTableSize - + reverseTableSize - + statusTableSize + trieSize + rulesSize; - -#ifdef RBBI_DEBUG - if (fDebugEnv && uprv_strstr(fDebugEnv, "size")) { - RBBIDebugPrintf("Header Size: %8d\n", headerSize); - RBBIDebugPrintf("Forward Table Size: %8d\n", forwardTableSize); - RBBIDebugPrintf("Reverse Table Size: %8d\n", reverseTableSize); - RBBIDebugPrintf("Trie Size: %8d\n", trieSize); - RBBIDebugPrintf("Status Table Size: %8d\n", statusTableSize); - RBBIDebugPrintf("Rules Size: %8d\n", rulesSize); - RBBIDebugPrintf("-----------------------------\n"); - RBBIDebugPrintf("Total Size: %8d\n", totalSize); - } -#endif - - RBBIDataHeader *data = (RBBIDataHeader *)uprv_malloc(totalSize); - if (data == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uprv_memset(data, 0, totalSize); - - - data->fMagic = 0xb1a0; - data->fFormatVersion[0] = RBBI_DATA_FORMAT_VERSION[0]; - data->fFormatVersion[1] = RBBI_DATA_FORMAT_VERSION[1]; - data->fFormatVersion[2] = RBBI_DATA_FORMAT_VERSION[2]; - data->fFormatVersion[3] = RBBI_DATA_FORMAT_VERSION[3]; - data->fLength = totalSize; - data->fCatCount = fSetBuilder->getNumCharCategories(); - - data->fFTable = headerSize; - data->fFTableLen = forwardTableSize; - - data->fRTable = data->fFTable + data->fFTableLen; - data->fRTableLen = reverseTableSize; - - data->fTrie = data->fRTable + data->fRTableLen; - data->fTrieLen = trieSize; - data->fStatusTable = data->fTrie + data->fTrieLen; - data->fStatusTableLen= statusTableSize; - data->fRuleSource = data->fStatusTable + statusTableSize; - data->fRuleSourceLen = rulesLengthInUTF8; - - uprv_memset(data->fReserved, 0, sizeof(data->fReserved)); - - 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); - for (i=0; isize(); i++) { - ruleStatusTable[i] = fRuleStatusVals->elementAti(i); - } - - u_strToUTF8WithSub((char *)data+data->fRuleSource, rulesSize, &rulesLengthInUTF8, - fStrippedRules.getBuffer(), fStrippedRules.length(), - 0xfffd, nullptr, fStatus); - if (U_FAILURE(*fStatus)) { - return NULL; - } - - return data; -} - - -//---------------------------------------------------------------------------------------- -// -// createRuleBasedBreakIterator construct from source rules that are passed in -// in a UnicodeString -// -//---------------------------------------------------------------------------------------- -BreakIterator * -RBBIRuleBuilder::createRuleBasedBreakIterator( const UnicodeString &rules, - UParseError *parseError, - UErrorCode &status) -{ - // - // Read the input rules, generate a parse tree, symbol table, - // and list of all Unicode Sets referenced by the rules. - // - RBBIRuleBuilder builder(rules, parseError, status); - if (U_FAILURE(status)) { // status checked here bcos build below doesn't - return NULL; - } - - 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. - // Munge the Unicode Sets to create an initial set of character categories. - // - fSetBuilder->buildRanges(); - - // - // Generate the DFA state transition table. - // - fForwardTable = new RBBITableBuilder(this, &fForwardTree, status); - if (fForwardTable == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - - fForwardTable->buildForwardTable(); - - // State table and character category optimization. - // Merge equivalent rows and columns. - // Note that this process alters the initial set of character categories, - // causing the representation of UnicodeSets in the parse tree to become invalid. - - optimizeTables(); - fForwardTable->buildSafeReverseTable(status); - - -#ifdef RBBI_DEBUG - if (fDebugEnv && uprv_strstr(fDebugEnv, "states")) { - fForwardTable->printStates(); - fForwardTable->printRuleStatusTable(); - fForwardTable->printReverseTable(); - } -#endif - - // Generate the mapping tables (TRIE) from input code points to - // the character categories. - // - fSetBuilder->buildTrie(); - - // - // Package up the compiled data into a memory image - // in the run-time format. - // - RBBIDataHeader *data = flattenData(); // returns NULL if error - if (U_FAILURE(status)) { - return nullptr; - } - return data; -} - -void RBBIRuleBuilder::optimizeTables() { - bool didSomething; - do { - didSomething = false; - - // 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); - didSomething = true; - } - - while (fForwardTable->removeDuplicateStates() > 0) { - didSomething = true; - } - } while (didSomething); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// file: rbbirb.cpp +// +// Copyright (C) 2002-2011, International Business Machines Corporation and others. +// All Rights Reserved. +// +// This file contains the RBBIRuleBuilder class implementation. This is the main class for +// building (compiling) break rules into the tables required by the runtime +// RBBI engine. +// + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/brkiter.h" +#include "unicode/rbbi.h" +#include "unicode/ubrk.h" +#include "unicode/unistr.h" +#include "unicode/uniset.h" +#include "unicode/uchar.h" +#include "unicode/uchriter.h" +#include "unicode/ustring.h" +#include "unicode/parsepos.h" +#include "unicode/parseerr.h" + +#include "cmemory.h" +#include "cstring.h" +#include "rbbirb.h" +#include "rbbinode.h" +#include "rbbiscan.h" +#include "rbbisetb.h" +#include "rbbitblb.h" +#include "rbbidata.h" +#include "uassert.h" + + +U_NAMESPACE_BEGIN + + +//---------------------------------------------------------------------------------------- +// +// Constructor. +// +//---------------------------------------------------------------------------------------- +RBBIRuleBuilder::RBBIRuleBuilder(const UnicodeString &rules, + UParseError *parseErr, + UErrorCode &status) + : fRules(rules), fStrippedRules(rules) +{ + fStatus = &status; // status is checked below + fParseError = parseErr; + fDebugEnv = nullptr; +#ifdef RBBI_DEBUG + fDebugEnv = getenv("U_RBBIDEBUG"); +#endif + + + fForwardTree = nullptr; + fReverseTree = nullptr; + fSafeFwdTree = nullptr; + fSafeRevTree = nullptr; + fDefaultTree = &fForwardTree; + fForwardTable = nullptr; + fRuleStatusVals = nullptr; + fChainRules = false; + fLBCMNoChain = false; + fLookAheadHardBreak = false; + fUSetNodes = nullptr; + fRuleStatusVals = nullptr; + fScanner = nullptr; + fSetBuilder = nullptr; + if (parseErr) { + uprv_memset(parseErr, 0, sizeof(UParseError)); + } + + if (U_FAILURE(status)) { + return; + } + + fUSetNodes = new UVector(status); // bcos status gets overwritten here + fRuleStatusVals = new UVector(status); + fScanner = new RBBIRuleScanner(this); + fSetBuilder = new RBBISetBuilder(this); + if (U_FAILURE(status)) { + return; + } + if(fSetBuilder == 0 || fScanner == 0 || fUSetNodes == 0 || fRuleStatusVals == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + } +} + + + +//---------------------------------------------------------------------------------------- +// +// Destructor +// +//---------------------------------------------------------------------------------------- +RBBIRuleBuilder::~RBBIRuleBuilder() { + + int i; + for (i=0; ; i++) { + RBBINode *n = (RBBINode *)fUSetNodes->elementAt(i); + if (n==nullptr) { + break; + } + delete n; + } + + delete fUSetNodes; + delete fSetBuilder; + delete fForwardTable; + delete fForwardTree; + delete fReverseTree; + delete fSafeFwdTree; + delete fSafeRevTree; + delete fScanner; + delete fRuleStatusVals; +} + + + + + +//---------------------------------------------------------------------------------------- +// +// flattenData() - Collect up the compiled RBBI rule data and put it into +// the format for saving in ICU data files, +// which is also the format needed by the RBBI runtime engine. +// +//---------------------------------------------------------------------------------------- +static int32_t align8(int32_t i) {return (i+7) & 0xfffffff8;} + +RBBIDataHeader *RBBIRuleBuilder::flattenData() { + int32_t i; + + if (U_FAILURE(*fStatus)) { + return nullptr; + } + + // Remove whitespace from the rules to make it smaller. + // The rule parser has already removed comments. + fStrippedRules = fScanner->stripRules(fStrippedRules); + + // Calculate the size of each section in the data. + // Sizes here are padded up to a multiple of 8 for better memory alignment. + // Sections sizes actually stored in the header are for the actual data + // without the padding. + // + int32_t headerSize = align8(sizeof(RBBIDataHeader)); + 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 rulesLengthInUTF8 = 0; + u_strToUTF8WithSub(0, 0, &rulesLengthInUTF8, + fStrippedRules.getBuffer(), fStrippedRules.length(), + 0xfffd, nullptr, fStatus); + *fStatus = U_ZERO_ERROR; + + int32_t rulesSize = align8((rulesLengthInUTF8+1)); + + int32_t totalSize = headerSize + + forwardTableSize + + reverseTableSize + + statusTableSize + trieSize + rulesSize; + +#ifdef RBBI_DEBUG + if (fDebugEnv && uprv_strstr(fDebugEnv, "size")) { + RBBIDebugPrintf("Header Size: %8d\n", headerSize); + RBBIDebugPrintf("Forward Table Size: %8d\n", forwardTableSize); + RBBIDebugPrintf("Reverse Table Size: %8d\n", reverseTableSize); + RBBIDebugPrintf("Trie Size: %8d\n", trieSize); + RBBIDebugPrintf("Status Table Size: %8d\n", statusTableSize); + RBBIDebugPrintf("Rules Size: %8d\n", rulesSize); + RBBIDebugPrintf("-----------------------------\n"); + RBBIDebugPrintf("Total Size: %8d\n", totalSize); + } +#endif + + RBBIDataHeader *data = (RBBIDataHeader *)uprv_malloc(totalSize); + if (data == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memset(data, 0, totalSize); + + + data->fMagic = 0xb1a0; + data->fFormatVersion[0] = RBBI_DATA_FORMAT_VERSION[0]; + data->fFormatVersion[1] = RBBI_DATA_FORMAT_VERSION[1]; + data->fFormatVersion[2] = RBBI_DATA_FORMAT_VERSION[2]; + data->fFormatVersion[3] = RBBI_DATA_FORMAT_VERSION[3]; + data->fLength = totalSize; + data->fCatCount = fSetBuilder->getNumCharCategories(); + + data->fFTable = headerSize; + data->fFTableLen = forwardTableSize; + + data->fRTable = data->fFTable + data->fFTableLen; + data->fRTableLen = reverseTableSize; + + data->fTrie = data->fRTable + data->fRTableLen; + data->fTrieLen = trieSize; + data->fStatusTable = data->fTrie + data->fTrieLen; + data->fStatusTableLen= statusTableSize; + data->fRuleSource = data->fStatusTable + statusTableSize; + data->fRuleSourceLen = rulesLengthInUTF8; + + uprv_memset(data->fReserved, 0, sizeof(data->fReserved)); + + 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); + for (i=0; isize(); i++) { + ruleStatusTable[i] = fRuleStatusVals->elementAti(i); + } + + u_strToUTF8WithSub((char *)data+data->fRuleSource, rulesSize, &rulesLengthInUTF8, + fStrippedRules.getBuffer(), fStrippedRules.length(), + 0xfffd, nullptr, fStatus); + if (U_FAILURE(*fStatus)) { + return nullptr; + } + + return data; +} + + +//---------------------------------------------------------------------------------------- +// +// createRuleBasedBreakIterator construct from source rules that are passed in +// in a UnicodeString +// +//---------------------------------------------------------------------------------------- +BreakIterator * +RBBIRuleBuilder::createRuleBasedBreakIterator( const UnicodeString &rules, + UParseError *parseError, + UErrorCode &status) +{ + // + // Read the input rules, generate a parse tree, symbol table, + // and list of all Unicode Sets referenced by the rules. + // + RBBIRuleBuilder builder(rules, parseError, status); + if (U_FAILURE(status)) { // status checked here bcos build below doesn't + return nullptr; + } + + 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 = nullptr; + } + else if(This == nullptr) { // test for nullptr + 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. + // Munge the Unicode Sets to create an initial set of character categories. + // + fSetBuilder->buildRanges(); + + // + // Generate the DFA state transition table. + // + fForwardTable = new RBBITableBuilder(this, &fForwardTree, status); + if (fForwardTable == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + fForwardTable->buildForwardTable(); + + // State table and character category optimization. + // Merge equivalent rows and columns. + // Note that this process alters the initial set of character categories, + // causing the representation of UnicodeSets in the parse tree to become invalid. + + optimizeTables(); + fForwardTable->buildSafeReverseTable(status); + + +#ifdef RBBI_DEBUG + if (fDebugEnv && uprv_strstr(fDebugEnv, "states")) { + fForwardTable->printStates(); + fForwardTable->printRuleStatusTable(); + fForwardTable->printReverseTable(); + } +#endif + + // Generate the mapping tables (TRIE) from input code points to + // the character categories. + // + fSetBuilder->buildTrie(); + + // + // Package up the compiled data into a memory image + // in the run-time format. + // + RBBIDataHeader *data = flattenData(); // returns nullptr if error + if (U_FAILURE(status)) { + return nullptr; + } + return data; +} + +void RBBIRuleBuilder::optimizeTables() { + bool didSomething; + do { + didSomething = false; + + // 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); + didSomething = true; + } + + while (fForwardTable->removeDuplicateStates() > 0) { + didSomething = true; + } + } while (didSomething); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ diff --git a/deps/icu-small/source/common/rbbirb.h b/deps/icu-small/source/common/rbbirb.h index d983a184b64cef..e1d57f05c42dd6 100644 --- a/deps/icu-small/source/common/rbbirb.h +++ b/deps/icu-small/source/common/rbbirb.h @@ -1,237 +1,237 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// rbbirb.h -// -// Copyright (C) 2002-2008, International Business Machines Corporation and others. -// All Rights Reserved. -// -// This file contains declarations for several classes from the -// Rule Based Break Iterator rule builder. -// - - -#ifndef RBBIRB_H -#define RBBIRB_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include - -#include "unicode/uobject.h" -#include "unicode/rbbi.h" -#include "unicode/uniset.h" -#include "unicode/parseerr.h" -#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. - - -U_NAMESPACE_BEGIN - -class RBBIRuleScanner; -struct RBBIRuleTableEl; -class RBBISetBuilder; -class RBBINode; -class RBBITableBuilder; - - - -//-------------------------------------------------------------------------------- -// -// RBBISymbolTable. Implements SymbolTable interface that is used by the -// UnicodeSet parser to resolve references to $variables. -// -//-------------------------------------------------------------------------------- -class RBBISymbolTableEntry : public UMemory { // The symbol table hash table contains one -public: // of these structs for each entry. - RBBISymbolTableEntry(); - UnicodeString key; - RBBINode *val; - ~RBBISymbolTableEntry(); - -private: - RBBISymbolTableEntry(const RBBISymbolTableEntry &other) = delete; // forbid copying of this class - RBBISymbolTableEntry &operator=(const RBBISymbolTableEntry &other) = delete; // forbid copying of this class -}; - - -class RBBISymbolTable : public UMemory, public SymbolTable { -private: - const UnicodeString &fRules; - UHashtable *fHashTable; - RBBIRuleScanner *fRuleScanner; - - // These next two fields are part of the mechanism for passing references to - // already-constructed UnicodeSets back to the UnicodeSet constructor - // when the pattern includes $variable references. - const UnicodeString ffffString; // = "/uffff" - UnicodeSet *fCachedSetLookup; - -public: - // API inherited from class SymbolTable - virtual const UnicodeString* lookup(const UnicodeString& s) const override; - virtual const UnicodeFunctor* lookupMatcher(UChar32 ch) const override; - virtual UnicodeString parseReference(const UnicodeString& text, - ParsePosition& pos, int32_t limit) const override; - - // Additional Functions - RBBISymbolTable(RBBIRuleScanner *, const UnicodeString &fRules, UErrorCode &status); - virtual ~RBBISymbolTable(); - - virtual RBBINode *lookupNode(const UnicodeString &key) const; - virtual void addEntry (const UnicodeString &key, RBBINode *val, UErrorCode &err); - -#ifdef RBBI_DEBUG - virtual void rbbiSymtablePrint() const; -#else - // A do-nothing inline function for non-debug builds. Member funcs can't be empty - // or the call sites won't compile. - int32_t fFakeField; - #define rbbiSymtablePrint() fFakeField=0; -#endif - -private: - RBBISymbolTable(const RBBISymbolTable &other); // forbid copying of this class - RBBISymbolTable &operator=(const RBBISymbolTable &other); // forbid copying of this class -}; - - -//-------------------------------------------------------------------------------- -// -// class RBBIRuleBuilder The top-level class handling RBBI rule compiling. -// -//-------------------------------------------------------------------------------- -class RBBIRuleBuilder : public UMemory { -public: - - // Create a rule based break iterator from a set of rules. - // This function is the main entry point into the rule builder. The - // public ICU API for creating RBBIs uses this function to do the actual work. - // - static BreakIterator * createRuleBasedBreakIterator( const UnicodeString &rules, - UParseError *parseError, - UErrorCode &status); - -public: - // The "public" functions and data members that appear below are accessed - // (and shared) by the various parts that make up the rule builder. They - // are NOT intended to be accessed by anything outside of the - // rule builder implementation. - 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, - * before serializing the result. - */ - void optimizeTables(); - - char *fDebugEnv; // controls debug trace output - UErrorCode *fStatus; // Error reporting. Keeping status - UParseError *fParseError; // here avoids passing it everywhere. - const UnicodeString &fRules; // The rule string that we are compiling - UnicodeString fStrippedRules; // The rule string, with comments stripped. - - RBBIRuleScanner *fScanner; // The scanner. - RBBINode *fForwardTree; // The parse trees, generated by the scanner, - RBBINode *fReverseTree; // then manipulated by subsequent steps. - RBBINode *fSafeFwdTree; - RBBINode *fSafeRevTree; - - RBBINode **fDefaultTree; // For rules not qualified with a ! - // the tree to which they belong to. - - UBool fChainRules; // True for chained Unicode TR style rules. - // False for traditional regexp rules. - - UBool fLBCMNoChain; // True: suppress chaining of rules on - // chars with LineBreak property == CM. - - UBool fLookAheadHardBreak; // True: Look ahead matches cause an - // immediate break, no continuing for the - // longest match. - - RBBISetBuilder *fSetBuilder; // Set and Character Category builder. - UVector *fUSetNodes; // Vector of all uset nodes. - - RBBITableBuilder *fForwardTable; // State transition table, build time form. - - UVector *fRuleStatusVals; // The values that can be returned - // from getRuleStatus(). - - RBBIDataHeader *flattenData(); // Create the flattened (runtime format) - // data tables.. -private: - RBBIRuleBuilder(const RBBIRuleBuilder &other) = delete; // forbid copying of this class - RBBIRuleBuilder &operator=(const RBBIRuleBuilder &other) = delete; // forbid copying of this class -}; - - - - -//---------------------------------------------------------------------------- -// -// RBBISetTableEl is an entry in the hash table of UnicodeSets that have -// been encountered. The val Node will be of nodetype uset -// and contain pointers to the actual UnicodeSets. -// The Key is the source string for initializing the set. -// -// The hash table is used to avoid creating duplicate -// unnamed (not $var references) UnicodeSets. -// -// Memory Management: -// The Hash Table owns these RBBISetTableEl structs and -// the key strings. It does NOT own the val nodes. -// -//---------------------------------------------------------------------------- -struct RBBISetTableEl { - UnicodeString *key; - RBBINode *val; -}; - -/** - * A pair of ints, used to bundle pairs of states or pairs of character classes. - */ -typedef std::pair IntPair; - - -//---------------------------------------------------------------------------- -// -// RBBIDebugPrintf Printf equivalent, for debugging output. -// Conditional compilation of the implementation lets us -// get rid of the stdio dependency in environments where it -// is unavailable. -// -//---------------------------------------------------------------------------- -#ifdef RBBI_DEBUG -#include -#define RBBIDebugPrintf printf -#define RBBIDebugPuts puts -#else -#undef RBBIDebugPrintf -#define RBBIDebugPuts(arg) -#endif - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ - -#endif - - - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// rbbirb.h +// +// Copyright (C) 2002-2008, International Business Machines Corporation and others. +// All Rights Reserved. +// +// This file contains declarations for several classes from the +// Rule Based Break Iterator rule builder. +// + + +#ifndef RBBIRB_H +#define RBBIRB_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include + +#include "unicode/uobject.h" +#include "unicode/rbbi.h" +#include "unicode/uniset.h" +#include "unicode/parseerr.h" +#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. + + +U_NAMESPACE_BEGIN + +class RBBIRuleScanner; +struct RBBIRuleTableEl; +class RBBISetBuilder; +class RBBINode; +class RBBITableBuilder; + + + +//-------------------------------------------------------------------------------- +// +// RBBISymbolTable. Implements SymbolTable interface that is used by the +// UnicodeSet parser to resolve references to $variables. +// +//-------------------------------------------------------------------------------- +class RBBISymbolTableEntry : public UMemory { // The symbol table hash table contains one +public: // of these structs for each entry. + RBBISymbolTableEntry(); + UnicodeString key; + RBBINode *val; + ~RBBISymbolTableEntry(); + +private: + RBBISymbolTableEntry(const RBBISymbolTableEntry &other) = delete; // forbid copying of this class + RBBISymbolTableEntry &operator=(const RBBISymbolTableEntry &other) = delete; // forbid copying of this class +}; + + +class RBBISymbolTable : public UMemory, public SymbolTable { +private: + const UnicodeString &fRules; + UHashtable *fHashTable; + RBBIRuleScanner *fRuleScanner; + + // These next two fields are part of the mechanism for passing references to + // already-constructed UnicodeSets back to the UnicodeSet constructor + // when the pattern includes $variable references. + const UnicodeString ffffString; // = "/uffff" + UnicodeSet *fCachedSetLookup; + +public: + // API inherited from class SymbolTable + virtual const UnicodeString* lookup(const UnicodeString& s) const override; + virtual const UnicodeFunctor* lookupMatcher(UChar32 ch) const override; + virtual UnicodeString parseReference(const UnicodeString& text, + ParsePosition& pos, int32_t limit) const override; + + // Additional Functions + RBBISymbolTable(RBBIRuleScanner *, const UnicodeString &fRules, UErrorCode &status); + virtual ~RBBISymbolTable(); + + virtual RBBINode *lookupNode(const UnicodeString &key) const; + virtual void addEntry (const UnicodeString &key, RBBINode *val, UErrorCode &err); + +#ifdef RBBI_DEBUG + virtual void rbbiSymtablePrint() const; +#else + // A do-nothing inline function for non-debug builds. Member funcs can't be empty + // or the call sites won't compile. + int32_t fFakeField; + #define rbbiSymtablePrint() fFakeField=0; +#endif + +private: + RBBISymbolTable(const RBBISymbolTable &other); // forbid copying of this class + RBBISymbolTable &operator=(const RBBISymbolTable &other); // forbid copying of this class +}; + + +//-------------------------------------------------------------------------------- +// +// class RBBIRuleBuilder The top-level class handling RBBI rule compiling. +// +//-------------------------------------------------------------------------------- +class RBBIRuleBuilder : public UMemory { +public: + + // Create a rule based break iterator from a set of rules. + // This function is the main entry point into the rule builder. The + // public ICU API for creating RBBIs uses this function to do the actual work. + // + static BreakIterator * createRuleBasedBreakIterator( const UnicodeString &rules, + UParseError *parseError, + UErrorCode &status); + +public: + // The "public" functions and data members that appear below are accessed + // (and shared) by the various parts that make up the rule builder. They + // are NOT intended to be accessed by anything outside of the + // rule builder implementation. + 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, + * before serializing the result. + */ + void optimizeTables(); + + char *fDebugEnv; // controls debug trace output + UErrorCode *fStatus; // Error reporting. Keeping status + UParseError *fParseError; // here avoids passing it everywhere. + const UnicodeString &fRules; // The rule string that we are compiling + UnicodeString fStrippedRules; // The rule string, with comments stripped. + + RBBIRuleScanner *fScanner; // The scanner. + RBBINode *fForwardTree; // The parse trees, generated by the scanner, + RBBINode *fReverseTree; // then manipulated by subsequent steps. + RBBINode *fSafeFwdTree; + RBBINode *fSafeRevTree; + + RBBINode **fDefaultTree; // For rules not qualified with a ! + // the tree to which they belong to. + + UBool fChainRules; // True for chained Unicode TR style rules. + // False for traditional regexp rules. + + UBool fLBCMNoChain; // True: suppress chaining of rules on + // chars with LineBreak property == CM. + + UBool fLookAheadHardBreak; // True: Look ahead matches cause an + // immediate break, no continuing for the + // longest match. + + RBBISetBuilder *fSetBuilder; // Set and Character Category builder. + UVector *fUSetNodes; // Vector of all uset nodes. + + RBBITableBuilder *fForwardTable; // State transition table, build time form. + + UVector *fRuleStatusVals; // The values that can be returned + // from getRuleStatus(). + + RBBIDataHeader *flattenData(); // Create the flattened (runtime format) + // data tables.. +private: + RBBIRuleBuilder(const RBBIRuleBuilder &other) = delete; // forbid copying of this class + RBBIRuleBuilder &operator=(const RBBIRuleBuilder &other) = delete; // forbid copying of this class +}; + + + + +//---------------------------------------------------------------------------- +// +// RBBISetTableEl is an entry in the hash table of UnicodeSets that have +// been encountered. The val Node will be of nodetype uset +// and contain pointers to the actual UnicodeSets. +// The Key is the source string for initializing the set. +// +// The hash table is used to avoid creating duplicate +// unnamed (not $var references) UnicodeSets. +// +// Memory Management: +// The Hash Table owns these RBBISetTableEl structs and +// the key strings. It does NOT own the val nodes. +// +//---------------------------------------------------------------------------- +struct RBBISetTableEl { + UnicodeString *key; + RBBINode *val; +}; + +/** + * A pair of ints, used to bundle pairs of states or pairs of character classes. + */ +typedef std::pair IntPair; + + +//---------------------------------------------------------------------------- +// +// RBBIDebugPrintf Printf equivalent, for debugging output. +// Conditional compilation of the implementation lets us +// get rid of the stdio dependency in environments where it +// is unavailable. +// +//---------------------------------------------------------------------------- +#ifdef RBBI_DEBUG +#include +#define RBBIDebugPrintf printf +#define RBBIDebugPuts puts +#else +#undef RBBIDebugPrintf +#define RBBIDebugPuts(arg) +#endif + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ + +#endif + + + diff --git a/deps/icu-small/source/common/rbbirpt.h b/deps/icu-small/source/common/rbbirpt.h index ca1bcf45dc4328..e52e0f3b25c391 100644 --- a/deps/icu-small/source/common/rbbirpt.h +++ b/deps/icu-small/source/common/rbbirpt.h @@ -1,296 +1,296 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -//--------------------------------------------------------------------------------- -// -// Generated Header File. Do not edit by hand. -// This file contains the state table for the ICU Rule Based Break Iterator -// rule parser. -// It is generated by the Perl script "rbbicst.pl" from -// the rule parser state definitions file "rbbirpt.txt". -// -// Copyright (C) 2002-2016 International Business Machines Corporation -// and others. All rights reserved. -// -//--------------------------------------------------------------------------------- -#ifndef RBBIRPT_H -#define RBBIRPT_H - -#include "unicode/utypes.h" - -U_NAMESPACE_BEGIN -// -// Character classes for RBBI rule scanning. -// - static const uint8_t kRuleSet_digit_char = 128; - static const uint8_t kRuleSet_name_char = 129; - static const uint8_t kRuleSet_name_start_char = 130; - static const uint8_t kRuleSet_rule_char = 131; - static const uint8_t kRuleSet_white_space = 132; - - -enum RBBI_RuleParseAction { - doCheckVarDef, - doDotAny, - doEndAssign, - doEndOfRule, - doEndVariableName, - doExit, - doExprCatOperator, - doExprFinished, - doExprOrOperator, - doExprRParen, - doExprStart, - doLParen, - doNOP, - doNoChain, - doOptionEnd, - doOptionStart, - doReverseDir, - doRuleChar, - doRuleError, - doRuleErrorAssignExpr, - doScanUnicodeSet, - doSlash, - doStartAssign, - doStartTagValue, - doStartVariableName, - doTagDigit, - doTagExpectedError, - doTagValue, - doUnaryOpPlus, - doUnaryOpQuestion, - doUnaryOpStar, - doVariableNameExpectedErr, - rbbiLastAction}; - -//------------------------------------------------------------------------------- -// -// RBBIRuleTableEl represents the structure of a row in the transition table -// for the rule parser state machine. -//------------------------------------------------------------------------------- -struct RBBIRuleTableEl { - RBBI_RuleParseAction fAction; - uint8_t fCharClass; // 0-127: an individual ASCII character - // 128-255: character class index - uint8_t fNextState; // 0-250: normal next-stat numbers - // 255: pop next-state from stack. - uint8_t fPushState; - UBool fNextChar; -}; - -static const struct RBBIRuleTableEl gRuleParseStateTable[] = { - {doNOP, 0, 0, 0, true} - , {doExprStart, 254, 29, 9, false} // 1 start - , {doNOP, 132, 1,0, true} // 2 - , {doNoChain, 94 /* ^ */, 12, 9, true} // 3 - , {doExprStart, 36 /* $ */, 88, 98, false} // 4 - , {doNOP, 33 /* ! */, 19,0, true} // 5 - , {doNOP, 59 /* ; */, 1,0, true} // 6 - , {doNOP, 252, 0,0, false} // 7 - , {doExprStart, 255, 29, 9, false} // 8 - , {doEndOfRule, 59 /* ; */, 1,0, true} // 9 break-rule-end - , {doNOP, 132, 9,0, true} // 10 - , {doRuleError, 255, 103,0, false} // 11 - , {doExprStart, 254, 29,0, false} // 12 start-after-caret - , {doNOP, 132, 12,0, true} // 13 - , {doRuleError, 94 /* ^ */, 103,0, false} // 14 - , {doExprStart, 36 /* $ */, 88, 37, false} // 15 - , {doRuleError, 59 /* ; */, 103,0, false} // 16 - , {doRuleError, 252, 103,0, false} // 17 - , {doExprStart, 255, 29,0, false} // 18 - , {doNOP, 33 /* ! */, 21,0, true} // 19 rev-option - , {doReverseDir, 255, 28, 9, false} // 20 - , {doOptionStart, 130, 23,0, true} // 21 option-scan1 - , {doRuleError, 255, 103,0, false} // 22 - , {doNOP, 129, 23,0, true} // 23 option-scan2 - , {doOptionEnd, 255, 25,0, false} // 24 - , {doNOP, 59 /* ; */, 1,0, true} // 25 option-scan3 - , {doNOP, 132, 25,0, true} // 26 - , {doRuleError, 255, 103,0, false} // 27 - , {doExprStart, 255, 29, 9, false} // 28 reverse-rule - , {doRuleChar, 254, 38,0, true} // 29 term - , {doNOP, 132, 29,0, true} // 30 - , {doRuleChar, 131, 38,0, true} // 31 - , {doNOP, 91 /* [ */, 94, 38, false} // 32 - , {doLParen, 40 /* ( */, 29, 38, true} // 33 - , {doNOP, 36 /* $ */, 88, 37, false} // 34 - , {doDotAny, 46 /* . */, 38,0, true} // 35 - , {doRuleError, 255, 103,0, false} // 36 - , {doCheckVarDef, 255, 38,0, false} // 37 term-var-ref - , {doNOP, 132, 38,0, true} // 38 expr-mod - , {doUnaryOpStar, 42 /* * */, 43,0, true} // 39 - , {doUnaryOpPlus, 43 /* + */, 43,0, true} // 40 - , {doUnaryOpQuestion, 63 /* ? */, 43,0, true} // 41 - , {doNOP, 255, 43,0, false} // 42 - , {doExprCatOperator, 254, 29,0, false} // 43 expr-cont - , {doNOP, 132, 43,0, true} // 44 - , {doExprCatOperator, 131, 29,0, false} // 45 - , {doExprCatOperator, 91 /* [ */, 29,0, false} // 46 - , {doExprCatOperator, 40 /* ( */, 29,0, false} // 47 - , {doExprCatOperator, 36 /* $ */, 29,0, false} // 48 - , {doExprCatOperator, 46 /* . */, 29,0, false} // 49 - , {doExprCatOperator, 47 /* / */, 55,0, false} // 50 - , {doExprCatOperator, 123 /* { */, 67,0, true} // 51 - , {doExprOrOperator, 124 /* | */, 29,0, true} // 52 - , {doExprRParen, 41 /* ) */, 255,0, true} // 53 - , {doExprFinished, 255, 255,0, false} // 54 - , {doSlash, 47 /* / */, 57,0, true} // 55 look-ahead - , {doNOP, 255, 103,0, false} // 56 - , {doExprCatOperator, 254, 29,0, false} // 57 expr-cont-no-slash - , {doNOP, 132, 43,0, true} // 58 - , {doExprCatOperator, 131, 29,0, false} // 59 - , {doExprCatOperator, 91 /* [ */, 29,0, false} // 60 - , {doExprCatOperator, 40 /* ( */, 29,0, false} // 61 - , {doExprCatOperator, 36 /* $ */, 29,0, false} // 62 - , {doExprCatOperator, 46 /* . */, 29,0, false} // 63 - , {doExprOrOperator, 124 /* | */, 29,0, true} // 64 - , {doExprRParen, 41 /* ) */, 255,0, true} // 65 - , {doExprFinished, 255, 255,0, false} // 66 - , {doNOP, 132, 67,0, true} // 67 tag-open - , {doStartTagValue, 128, 70,0, false} // 68 - , {doTagExpectedError, 255, 103,0, false} // 69 - , {doNOP, 132, 74,0, true} // 70 tag-value - , {doNOP, 125 /* } */, 74,0, false} // 71 - , {doTagDigit, 128, 70,0, true} // 72 - , {doTagExpectedError, 255, 103,0, false} // 73 - , {doNOP, 132, 74,0, true} // 74 tag-close - , {doTagValue, 125 /* } */, 77,0, true} // 75 - , {doTagExpectedError, 255, 103,0, false} // 76 - , {doExprCatOperator, 254, 29,0, false} // 77 expr-cont-no-tag - , {doNOP, 132, 77,0, true} // 78 - , {doExprCatOperator, 131, 29,0, false} // 79 - , {doExprCatOperator, 91 /* [ */, 29,0, false} // 80 - , {doExprCatOperator, 40 /* ( */, 29,0, false} // 81 - , {doExprCatOperator, 36 /* $ */, 29,0, false} // 82 - , {doExprCatOperator, 46 /* . */, 29,0, false} // 83 - , {doExprCatOperator, 47 /* / */, 55,0, false} // 84 - , {doExprOrOperator, 124 /* | */, 29,0, true} // 85 - , {doExprRParen, 41 /* ) */, 255,0, true} // 86 - , {doExprFinished, 255, 255,0, false} // 87 - , {doStartVariableName, 36 /* $ */, 90,0, true} // 88 scan-var-name - , {doNOP, 255, 103,0, false} // 89 - , {doNOP, 130, 92,0, true} // 90 scan-var-start - , {doVariableNameExpectedErr, 255, 103,0, false} // 91 - , {doNOP, 129, 92,0, true} // 92 scan-var-body - , {doEndVariableName, 255, 255,0, false} // 93 - , {doScanUnicodeSet, 91 /* [ */, 255,0, true} // 94 scan-unicode-set - , {doScanUnicodeSet, 112 /* p */, 255,0, true} // 95 - , {doScanUnicodeSet, 80 /* P */, 255,0, true} // 96 - , {doNOP, 255, 103,0, false} // 97 - , {doNOP, 132, 98,0, true} // 98 assign-or-rule - , {doStartAssign, 61 /* = */, 29, 101, true} // 99 - , {doNOP, 255, 37, 9, false} // 100 - , {doEndAssign, 59 /* ; */, 1,0, true} // 101 assign-end - , {doRuleErrorAssignExpr, 255, 103,0, false} // 102 - , {doExit, 255, 103,0, true} // 103 errorDeath - }; -#ifdef RBBI_DEBUG -static const char * const RBBIRuleStateNames[] = { 0, - "start", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "break-rule-end", - 0, - 0, - "start-after-caret", - 0, - 0, - 0, - 0, - 0, - 0, - "rev-option", - 0, - "option-scan1", - 0, - "option-scan2", - 0, - "option-scan3", - 0, - 0, - "reverse-rule", - "term", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "term-var-ref", - "expr-mod", - 0, - 0, - 0, - 0, - "expr-cont", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "look-ahead", - 0, - "expr-cont-no-slash", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "tag-open", - 0, - 0, - "tag-value", - 0, - 0, - 0, - "tag-close", - 0, - 0, - "expr-cont-no-tag", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "scan-var-name", - 0, - "scan-var-start", - 0, - "scan-var-body", - 0, - "scan-unicode-set", - 0, - 0, - 0, - "assign-or-rule", - 0, - 0, - "assign-end", - 0, - "errorDeath", - 0}; -#endif - -U_NAMESPACE_END -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +//--------------------------------------------------------------------------------- +// +// Generated Header File. Do not edit by hand. +// This file contains the state table for the ICU Rule Based Break Iterator +// rule parser. +// It is generated by the Perl script "rbbicst.pl" from +// the rule parser state definitions file "rbbirpt.txt". +// +// Copyright (C) 2002-2016 International Business Machines Corporation +// and others. All rights reserved. +// +//--------------------------------------------------------------------------------- +#ifndef RBBIRPT_H +#define RBBIRPT_H + +#include "unicode/utypes.h" + +U_NAMESPACE_BEGIN +// +// Character classes for RBBI rule scanning. +// + static const uint8_t kRuleSet_digit_char = 128; + static const uint8_t kRuleSet_name_char = 129; + static const uint8_t kRuleSet_name_start_char = 130; + static const uint8_t kRuleSet_rule_char = 131; + static const uint8_t kRuleSet_white_space = 132; + + +enum RBBI_RuleParseAction { + doCheckVarDef, + doDotAny, + doEndAssign, + doEndOfRule, + doEndVariableName, + doExit, + doExprCatOperator, + doExprFinished, + doExprOrOperator, + doExprRParen, + doExprStart, + doLParen, + doNOP, + doNoChain, + doOptionEnd, + doOptionStart, + doReverseDir, + doRuleChar, + doRuleError, + doRuleErrorAssignExpr, + doScanUnicodeSet, + doSlash, + doStartAssign, + doStartTagValue, + doStartVariableName, + doTagDigit, + doTagExpectedError, + doTagValue, + doUnaryOpPlus, + doUnaryOpQuestion, + doUnaryOpStar, + doVariableNameExpectedErr, + rbbiLastAction}; + +//------------------------------------------------------------------------------- +// +// RBBIRuleTableEl represents the structure of a row in the transition table +// for the rule parser state machine. +//------------------------------------------------------------------------------- +struct RBBIRuleTableEl { + RBBI_RuleParseAction fAction; + uint8_t fCharClass; // 0-127: an individual ASCII character + // 128-255: character class index + uint8_t fNextState; // 0-250: normal next-stat numbers + // 255: pop next-state from stack. + uint8_t fPushState; + UBool fNextChar; +}; + +static const struct RBBIRuleTableEl gRuleParseStateTable[] = { + {doNOP, 0, 0, 0, true} + , {doExprStart, 254, 29, 9, false} // 1 start + , {doNOP, 132, 1,0, true} // 2 + , {doNoChain, 94 /* ^ */, 12, 9, true} // 3 + , {doExprStart, 36 /* $ */, 88, 98, false} // 4 + , {doNOP, 33 /* ! */, 19,0, true} // 5 + , {doNOP, 59 /* ; */, 1,0, true} // 6 + , {doNOP, 252, 0,0, false} // 7 + , {doExprStart, 255, 29, 9, false} // 8 + , {doEndOfRule, 59 /* ; */, 1,0, true} // 9 break-rule-end + , {doNOP, 132, 9,0, true} // 10 + , {doRuleError, 255, 103,0, false} // 11 + , {doExprStart, 254, 29,0, false} // 12 start-after-caret + , {doNOP, 132, 12,0, true} // 13 + , {doRuleError, 94 /* ^ */, 103,0, false} // 14 + , {doExprStart, 36 /* $ */, 88, 37, false} // 15 + , {doRuleError, 59 /* ; */, 103,0, false} // 16 + , {doRuleError, 252, 103,0, false} // 17 + , {doExprStart, 255, 29,0, false} // 18 + , {doNOP, 33 /* ! */, 21,0, true} // 19 rev-option + , {doReverseDir, 255, 28, 9, false} // 20 + , {doOptionStart, 130, 23,0, true} // 21 option-scan1 + , {doRuleError, 255, 103,0, false} // 22 + , {doNOP, 129, 23,0, true} // 23 option-scan2 + , {doOptionEnd, 255, 25,0, false} // 24 + , {doNOP, 59 /* ; */, 1,0, true} // 25 option-scan3 + , {doNOP, 132, 25,0, true} // 26 + , {doRuleError, 255, 103,0, false} // 27 + , {doExprStart, 255, 29, 9, false} // 28 reverse-rule + , {doRuleChar, 254, 38,0, true} // 29 term + , {doNOP, 132, 29,0, true} // 30 + , {doRuleChar, 131, 38,0, true} // 31 + , {doNOP, 91 /* [ */, 94, 38, false} // 32 + , {doLParen, 40 /* ( */, 29, 38, true} // 33 + , {doNOP, 36 /* $ */, 88, 37, false} // 34 + , {doDotAny, 46 /* . */, 38,0, true} // 35 + , {doRuleError, 255, 103,0, false} // 36 + , {doCheckVarDef, 255, 38,0, false} // 37 term-var-ref + , {doNOP, 132, 38,0, true} // 38 expr-mod + , {doUnaryOpStar, 42 /* * */, 43,0, true} // 39 + , {doUnaryOpPlus, 43 /* + */, 43,0, true} // 40 + , {doUnaryOpQuestion, 63 /* ? */, 43,0, true} // 41 + , {doNOP, 255, 43,0, false} // 42 + , {doExprCatOperator, 254, 29,0, false} // 43 expr-cont + , {doNOP, 132, 43,0, true} // 44 + , {doExprCatOperator, 131, 29,0, false} // 45 + , {doExprCatOperator, 91 /* [ */, 29,0, false} // 46 + , {doExprCatOperator, 40 /* ( */, 29,0, false} // 47 + , {doExprCatOperator, 36 /* $ */, 29,0, false} // 48 + , {doExprCatOperator, 46 /* . */, 29,0, false} // 49 + , {doExprCatOperator, 47 /* / */, 55,0, false} // 50 + , {doExprCatOperator, 123 /* { */, 67,0, true} // 51 + , {doExprOrOperator, 124 /* | */, 29,0, true} // 52 + , {doExprRParen, 41 /* ) */, 255,0, true} // 53 + , {doExprFinished, 255, 255,0, false} // 54 + , {doSlash, 47 /* / */, 57,0, true} // 55 look-ahead + , {doNOP, 255, 103,0, false} // 56 + , {doExprCatOperator, 254, 29,0, false} // 57 expr-cont-no-slash + , {doNOP, 132, 43,0, true} // 58 + , {doExprCatOperator, 131, 29,0, false} // 59 + , {doExprCatOperator, 91 /* [ */, 29,0, false} // 60 + , {doExprCatOperator, 40 /* ( */, 29,0, false} // 61 + , {doExprCatOperator, 36 /* $ */, 29,0, false} // 62 + , {doExprCatOperator, 46 /* . */, 29,0, false} // 63 + , {doExprOrOperator, 124 /* | */, 29,0, true} // 64 + , {doExprRParen, 41 /* ) */, 255,0, true} // 65 + , {doExprFinished, 255, 255,0, false} // 66 + , {doNOP, 132, 67,0, true} // 67 tag-open + , {doStartTagValue, 128, 70,0, false} // 68 + , {doTagExpectedError, 255, 103,0, false} // 69 + , {doNOP, 132, 74,0, true} // 70 tag-value + , {doNOP, 125 /* } */, 74,0, false} // 71 + , {doTagDigit, 128, 70,0, true} // 72 + , {doTagExpectedError, 255, 103,0, false} // 73 + , {doNOP, 132, 74,0, true} // 74 tag-close + , {doTagValue, 125 /* } */, 77,0, true} // 75 + , {doTagExpectedError, 255, 103,0, false} // 76 + , {doExprCatOperator, 254, 29,0, false} // 77 expr-cont-no-tag + , {doNOP, 132, 77,0, true} // 78 + , {doExprCatOperator, 131, 29,0, false} // 79 + , {doExprCatOperator, 91 /* [ */, 29,0, false} // 80 + , {doExprCatOperator, 40 /* ( */, 29,0, false} // 81 + , {doExprCatOperator, 36 /* $ */, 29,0, false} // 82 + , {doExprCatOperator, 46 /* . */, 29,0, false} // 83 + , {doExprCatOperator, 47 /* / */, 55,0, false} // 84 + , {doExprOrOperator, 124 /* | */, 29,0, true} // 85 + , {doExprRParen, 41 /* ) */, 255,0, true} // 86 + , {doExprFinished, 255, 255,0, false} // 87 + , {doStartVariableName, 36 /* $ */, 90,0, true} // 88 scan-var-name + , {doNOP, 255, 103,0, false} // 89 + , {doNOP, 130, 92,0, true} // 90 scan-var-start + , {doVariableNameExpectedErr, 255, 103,0, false} // 91 + , {doNOP, 129, 92,0, true} // 92 scan-var-body + , {doEndVariableName, 255, 255,0, false} // 93 + , {doScanUnicodeSet, 91 /* [ */, 255,0, true} // 94 scan-unicode-set + , {doScanUnicodeSet, 112 /* p */, 255,0, true} // 95 + , {doScanUnicodeSet, 80 /* P */, 255,0, true} // 96 + , {doNOP, 255, 103,0, false} // 97 + , {doNOP, 132, 98,0, true} // 98 assign-or-rule + , {doStartAssign, 61 /* = */, 29, 101, true} // 99 + , {doNOP, 255, 37, 9, false} // 100 + , {doEndAssign, 59 /* ; */, 1,0, true} // 101 assign-end + , {doRuleErrorAssignExpr, 255, 103,0, false} // 102 + , {doExit, 255, 103,0, true} // 103 errorDeath + }; +#ifdef RBBI_DEBUG +static const char * const RBBIRuleStateNames[] = { 0, + "start", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "break-rule-end", + 0, + 0, + "start-after-caret", + 0, + 0, + 0, + 0, + 0, + 0, + "rev-option", + 0, + "option-scan1", + 0, + "option-scan2", + 0, + "option-scan3", + 0, + 0, + "reverse-rule", + "term", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "term-var-ref", + "expr-mod", + 0, + 0, + 0, + 0, + "expr-cont", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "look-ahead", + 0, + "expr-cont-no-slash", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "tag-open", + 0, + 0, + "tag-value", + 0, + 0, + 0, + "tag-close", + 0, + 0, + "expr-cont-no-tag", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "scan-var-name", + 0, + "scan-var-start", + 0, + "scan-var-body", + 0, + "scan-unicode-set", + 0, + 0, + 0, + "assign-or-rule", + 0, + 0, + "assign-end", + 0, + "errorDeath", + 0}; +#endif + +U_NAMESPACE_END +#endif diff --git a/deps/icu-small/source/common/rbbiscan.cpp b/deps/icu-small/source/common/rbbiscan.cpp index 92cf77664f67be..9af661d9ef9759 100644 --- a/deps/icu-small/source/common/rbbiscan.cpp +++ b/deps/icu-small/source/common/rbbiscan.cpp @@ -1,1285 +1,1285 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// file: rbbiscan.cpp -// -// Copyright (C) 2002-2016, International Business Machines Corporation and others. -// All Rights Reserved. -// -// This file contains the Rule Based Break Iterator Rule Builder functions for -// scanning the rules and assembling a parse tree. This is the first phase -// of compiling the rules. -// -// The overall of the rules is managed by class RBBIRuleBuilder, which will -// create and use an instance of this class as part of the process. -// - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/unistr.h" -#include "unicode/uniset.h" -#include "unicode/uchar.h" -#include "unicode/uchriter.h" -#include "unicode/parsepos.h" -#include "unicode/parseerr.h" -#include "cmemory.h" -#include "cstring.h" - -#include "rbbirpt.h" // Contains state table for the rbbi rules parser. - // generated by a Perl script. -#include "rbbirb.h" -#include "rbbinode.h" -#include "rbbiscan.h" -#include "rbbitblb.h" - -#include "uassert.h" - -//------------------------------------------------------------------------------ -// -// Unicode Set init strings for each of the character classes needed for parsing a rule file. -// (Initialized with hex values for portability to EBCDIC based machines. -// Really ugly, but there's no good way to avoid it.) -// -// The sets are referred to by name in the rbbirpt.txt, which is the -// source form of the state transition table for the RBBI rule parser. -// -//------------------------------------------------------------------------------ -static const UChar gRuleSet_rule_char_pattern[] = { - // Characters that may appear as literals in patterns without escaping or quoting. - // [ ^ [ \ p { Z } \ u 0 0 2 0 - 0x5b, 0x5e, 0x5b, 0x5c, 0x70, 0x7b, 0x5a, 0x7d, 0x5c, 0x75, 0x30, 0x30, 0x32, 0x30, - // - \ u 0 0 7 f ] - [ \ p - 0x2d, 0x5c, 0x75, 0x30, 0x30, 0x37, 0x66, 0x5d, 0x2d, 0x5b, 0x5c, 0x70, - // { L } ] - [ \ p { N } ] ] - 0x7b, 0x4c, 0x7d, 0x5d, 0x2d, 0x5b, 0x5c, 0x70, 0x7b, 0x4e, 0x7d, 0x5d, 0x5d, 0}; - -static const UChar gRuleSet_name_char_pattern[] = { -// [ _ \ p { L } \ p { N } ] - 0x5b, 0x5f, 0x5c, 0x70, 0x7b, 0x4c, 0x7d, 0x5c, 0x70, 0x7b, 0x4e, 0x7d, 0x5d, 0}; - -static const UChar gRuleSet_digit_char_pattern[] = { -// [ 0 - 9 ] - 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0}; - -static const UChar gRuleSet_name_start_char_pattern[] = { -// [ _ \ p { L } ] - 0x5b, 0x5f, 0x5c, 0x70, 0x7b, 0x4c, 0x7d, 0x5d, 0 }; - -static const UChar kAny[] = {0x61, 0x6e, 0x79, 0x00}; // "any" - - -U_CDECL_BEGIN -static void U_CALLCONV RBBISetTable_deleter(void *p) { - icu::RBBISetTableEl *px = (icu::RBBISetTableEl *)p; - delete px->key; - // Note: px->val is owned by the linked list "fSetsListHead" in scanner. - // Don't delete the value nodes here. - uprv_free(px); -} -U_CDECL_END - -U_NAMESPACE_BEGIN - -//------------------------------------------------------------------------------ -// -// Constructor. -// -//------------------------------------------------------------------------------ -RBBIRuleScanner::RBBIRuleScanner(RBBIRuleBuilder *rb) -{ - fRB = rb; - fScanIndex = 0; - fNextIndex = 0; - fQuoteMode = false; - fLineNum = 1; - fCharNum = 0; - fLastChar = 0; - - fStateTable = NULL; - fStack[0] = 0; - fStackPtr = 0; - fNodeStack[0] = NULL; - fNodeStackPtr = 0; - - fReverseRule = false; - fLookAheadRule = false; - fNoChainInRule = false; - - fSymbolTable = NULL; - fSetTable = NULL; - fRuleNum = 0; - fOptionStart = 0; - - // Do not check status until after all critical fields are sufficiently initialized - // that the destructor can run cleanly. - if (U_FAILURE(*rb->fStatus)) { - return; - } - - // - // Set up the constant Unicode Sets. - // Note: These could be made static, lazily initialized, and shared among - // all instances of RBBIRuleScanners. BUT this is quite a bit simpler, - // and the time to build these few sets should be small compared to a - // full break iterator build. - fRuleSets[kRuleSet_rule_char-128] - = UnicodeSet(UnicodeString(gRuleSet_rule_char_pattern), *rb->fStatus); - // fRuleSets[kRuleSet_white_space-128] = [:Pattern_White_Space:] - fRuleSets[kRuleSet_white_space-128]. - add(9, 0xd).add(0x20).add(0x85).add(0x200e, 0x200f).add(0x2028, 0x2029); - fRuleSets[kRuleSet_name_char-128] - = UnicodeSet(UnicodeString(gRuleSet_name_char_pattern), *rb->fStatus); - fRuleSets[kRuleSet_name_start_char-128] - = UnicodeSet(UnicodeString(gRuleSet_name_start_char_pattern), *rb->fStatus); - fRuleSets[kRuleSet_digit_char-128] - = UnicodeSet(UnicodeString(gRuleSet_digit_char_pattern), *rb->fStatus); - if (*rb->fStatus == U_ILLEGAL_ARGUMENT_ERROR) { - // This case happens if ICU's data is missing. UnicodeSet tries to look up property - // names from the init string, can't find them, and claims an illegal argument. - // Change the error so that the actual problem will be clearer to users. - *rb->fStatus = U_BRK_INIT_ERROR; - } - if (U_FAILURE(*rb->fStatus)) { - return; - } - - fSymbolTable = new RBBISymbolTable(this, rb->fRules, *rb->fStatus); - if (fSymbolTable == NULL) { - *rb->fStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - fSetTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, rb->fStatus); - if (U_FAILURE(*rb->fStatus)) { - return; - } - uhash_setValueDeleter(fSetTable, RBBISetTable_deleter); -} - - - -//------------------------------------------------------------------------------ -// -// Destructor -// -//------------------------------------------------------------------------------ -RBBIRuleScanner::~RBBIRuleScanner() { - delete fSymbolTable; - if (fSetTable != NULL) { - uhash_close(fSetTable); - fSetTable = NULL; - - } - - - // Node Stack. - // Normally has one entry, which is the entire parse tree for the rules. - // If errors occurred, there may be additional subtrees left on the stack. - while (fNodeStackPtr > 0) { - delete fNodeStack[fNodeStackPtr]; - fNodeStackPtr--; - } - -} - -//------------------------------------------------------------------------------ -// -// doParseAction Do some action during rule parsing. -// Called by the parse state machine. -// Actions build the parse tree and Unicode Sets, -// and maintain the parse stack for nested expressions. -// -// TODO: unify EParseAction and RBBI_RuleParseAction enum types. -// They represent exactly the same thing. They're separate -// only to work around enum forward declaration restrictions -// in some compilers, while at the same time avoiding multiple -// definitions problems. I'm sure that there's a better way. -// -//------------------------------------------------------------------------------ -UBool RBBIRuleScanner::doParseActions(int32_t action) -{ - RBBINode *n = NULL; - - UBool returnVal = true; - - switch (action) { - - case doExprStart: - pushNewNode(RBBINode::opStart); - fRuleNum++; - break; - - - case doNoChain: - // Scanned a '^' while on the rule start state. - fNoChainInRule = true; - break; - - - case doExprOrOperator: - { - fixOpStack(RBBINode::precOpCat); - RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; - RBBINode *orNode = pushNewNode(RBBINode::opOr); - if (U_FAILURE(*fRB->fStatus)) { - break; - } - orNode->fLeftChild = operandNode; - operandNode->fParent = orNode; - } - break; - - case doExprCatOperator: - // concatenation operator. - // For the implicit concatenation of adjacent terms in an expression that are - // not separated by any other operator. Action is invoked between the - // actions for the two terms. - { - fixOpStack(RBBINode::precOpCat); - RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; - RBBINode *catNode = pushNewNode(RBBINode::opCat); - if (U_FAILURE(*fRB->fStatus)) { - break; - } - catNode->fLeftChild = operandNode; - operandNode->fParent = catNode; - } - break; - - case doLParen: - // Open Paren. - // The openParen node is a dummy operation type with a low precedence, - // which has the affect of ensuring that any real binary op that - // follows within the parens binds more tightly to the operands than - // stuff outside of the parens. - pushNewNode(RBBINode::opLParen); - break; - - case doExprRParen: - fixOpStack(RBBINode::precLParen); - break; - - case doNOP: - break; - - case doStartAssign: - // We've just scanned "$variable = " - // The top of the node stack has the $variable ref node. - - // Save the start position of the RHS text in the StartExpression node - // that precedes the $variableReference node on the stack. - // This will eventually be used when saving the full $variable replacement - // text as a string. - n = fNodeStack[fNodeStackPtr-1]; - n->fFirstPos = fNextIndex; // move past the '=' - - // Push a new start-of-expression node; needed to keep parse of the - // RHS expression happy. - pushNewNode(RBBINode::opStart); - break; - - - - - case doEndAssign: - { - // We have reached the end of an assignment statement. - // Current scan char is the ';' that terminates the assignment. - - // Terminate expression, leaves expression parse tree rooted in TOS node. - fixOpStack(RBBINode::precStart); - - RBBINode *startExprNode = fNodeStack[fNodeStackPtr-2]; - RBBINode *varRefNode = fNodeStack[fNodeStackPtr-1]; - RBBINode *RHSExprNode = fNodeStack[fNodeStackPtr]; - - // Save original text of right side of assignment, excluding the terminating ';' - // in the root of the node for the right-hand-side expression. - RHSExprNode->fFirstPos = startExprNode->fFirstPos; - RHSExprNode->fLastPos = fScanIndex; - fRB->fRules.extractBetween(RHSExprNode->fFirstPos, RHSExprNode->fLastPos, RHSExprNode->fText); - - // Expression parse tree becomes l. child of the $variable reference node. - varRefNode->fLeftChild = RHSExprNode; - RHSExprNode->fParent = varRefNode; - - // Make a symbol table entry for the $variableRef node. - fSymbolTable->addEntry(varRefNode->fText, varRefNode, *fRB->fStatus); - if (U_FAILURE(*fRB->fStatus)) { - // This is a round-about way to get the parse position set - // so that duplicate symbols error messages include a line number. - UErrorCode t = *fRB->fStatus; - *fRB->fStatus = U_ZERO_ERROR; - error(t); - } - - // Clean up the stack. - delete startExprNode; - fNodeStackPtr-=3; - break; - } - - case doEndOfRule: - { - fixOpStack(RBBINode::precStart); // Terminate expression, leaves expression - if (U_FAILURE(*fRB->fStatus)) { // parse tree rooted in TOS node. - break; - } -#ifdef RBBI_DEBUG - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "rtree")) {printNodeStack("end of rule");} -#endif - U_ASSERT(fNodeStackPtr == 1); - RBBINode *thisRule = fNodeStack[fNodeStackPtr]; - - // If this rule includes a look-ahead '/', add a endMark node to the - // expression tree. - if (fLookAheadRule) { - RBBINode *endNode = pushNewNode(RBBINode::endMark); - RBBINode *catNode = pushNewNode(RBBINode::opCat); - if (U_FAILURE(*fRB->fStatus)) { - break; - } - fNodeStackPtr -= 2; - catNode->fLeftChild = thisRule; - catNode->fRightChild = endNode; - fNodeStack[fNodeStackPtr] = catNode; - endNode->fVal = fRuleNum; - endNode->fLookAheadEnd = true; - thisRule = catNode; - - // TODO: Disable chaining out of look-ahead (hard break) rules. - // The break on rule match is forced, so there is no point in building up - // the state table to chain into another rule for a longer match. - } - - // Mark this node as being the root of a rule. - thisRule->fRuleRoot = true; - - // Flag if chaining into this rule is wanted. - // - if (fRB->fChainRules && // If rule chaining is enabled globally via !!chain - !fNoChainInRule) { // and no '^' chain-in inhibit was on this rule - thisRule->fChainIn = true; - } - - - // All rule expressions are ORed together. - // The ';' that terminates an expression really just functions as a '|' with - // a low operator prededence. - // - // Each of the four sets of rules are collected separately. - // (forward, reverse, safe_forward, safe_reverse) - // OR this rule into the appropriate group of them. - // - RBBINode **destRules = (fReverseRule? &fRB->fSafeRevTree : fRB->fDefaultTree); - - if (*destRules != NULL) { - // This is not the first rule encountered. - // OR previous stuff (from *destRules) - // with the current rule expression (on the Node Stack) - // with the resulting OR expression going to *destRules - // - thisRule = fNodeStack[fNodeStackPtr]; - RBBINode *prevRules = *destRules; - RBBINode *orNode = pushNewNode(RBBINode::opOr); - if (U_FAILURE(*fRB->fStatus)) { - break; - } - orNode->fLeftChild = prevRules; - prevRules->fParent = orNode; - orNode->fRightChild = thisRule; - thisRule->fParent = orNode; - *destRules = orNode; - } - else - { - // This is the first rule encountered (for this direction). - // Just move its parse tree from the stack to *destRules. - *destRules = fNodeStack[fNodeStackPtr]; - } - fReverseRule = false; // in preparation for the next rule. - fLookAheadRule = false; - fNoChainInRule = false; - fNodeStackPtr = 0; - } - break; - - - case doRuleError: - error(U_BRK_RULE_SYNTAX); - returnVal = false; - break; - - - case doVariableNameExpectedErr: - error(U_BRK_RULE_SYNTAX); - break; - - - // - // Unary operands + ? * - // These all appear after the operand to which they apply. - // When we hit one, the operand (may be a whole sub expression) - // will be on the top of the stack. - // Unary Operator becomes TOS, with the old TOS as its one child. - case doUnaryOpPlus: - { - RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; - RBBINode *plusNode = pushNewNode(RBBINode::opPlus); - if (U_FAILURE(*fRB->fStatus)) { - break; - } - plusNode->fLeftChild = operandNode; - operandNode->fParent = plusNode; - } - break; - - case doUnaryOpQuestion: - { - RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; - RBBINode *qNode = pushNewNode(RBBINode::opQuestion); - if (U_FAILURE(*fRB->fStatus)) { - break; - } - qNode->fLeftChild = operandNode; - operandNode->fParent = qNode; - } - break; - - case doUnaryOpStar: - { - RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; - RBBINode *starNode = pushNewNode(RBBINode::opStar); - if (U_FAILURE(*fRB->fStatus)) { - break; - } - starNode->fLeftChild = operandNode; - operandNode->fParent = starNode; - } - break; - - case doRuleChar: - // A "Rule Character" is any single character that is a literal part - // of the regular expression. Like a, b and c in the expression "(abc*) | [:L:]" - // These are pretty uncommon in break rules; the terms are more commonly - // sets. To keep things uniform, treat these characters like as - // sets that just happen to contain only one character. - { - n = pushNewNode(RBBINode::setRef); - if (U_FAILURE(*fRB->fStatus)) { - break; - } - findSetFor(UnicodeString(fC.fChar), n); - n->fFirstPos = fScanIndex; - n->fLastPos = fNextIndex; - fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); - break; - } - - case doDotAny: - // scanned a ".", meaning match any single character. - { - n = pushNewNode(RBBINode::setRef); - if (U_FAILURE(*fRB->fStatus)) { - break; - } - findSetFor(UnicodeString(true, kAny, 3), n); - n->fFirstPos = fScanIndex; - n->fLastPos = fNextIndex; - fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); - break; - } - - case doSlash: - // Scanned a '/', which identifies a look-ahead break position in a rule. - n = pushNewNode(RBBINode::lookAhead); - if (U_FAILURE(*fRB->fStatus)) { - break; - } - n->fVal = fRuleNum; - n->fFirstPos = fScanIndex; - n->fLastPos = fNextIndex; - fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); - fLookAheadRule = true; - break; - - - case doStartTagValue: - // Scanned a '{', the opening delimiter for a tag value within a rule. - n = pushNewNode(RBBINode::tag); - if (U_FAILURE(*fRB->fStatus)) { - break; - } - n->fVal = 0; - n->fFirstPos = fScanIndex; - n->fLastPos = fNextIndex; - break; - - case doTagDigit: - // Just scanned a decimal digit that's part of a tag value - { - n = fNodeStack[fNodeStackPtr]; - uint32_t v = u_charDigitValue(fC.fChar); - U_ASSERT(v < 10); - n->fVal = n->fVal*10 + v; - break; - } - - case doTagValue: - n = fNodeStack[fNodeStackPtr]; - n->fLastPos = fNextIndex; - fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); - break; - - case doTagExpectedError: - error(U_BRK_MALFORMED_RULE_TAG); - returnVal = false; - break; - - case doOptionStart: - // Scanning a !!option. At the start of string. - fOptionStart = fScanIndex; - break; - - case doOptionEnd: - { - UnicodeString opt(fRB->fRules, fOptionStart, fScanIndex-fOptionStart); - if (opt == UNICODE_STRING("chain", 5)) { - fRB->fChainRules = true; - } else if (opt == UNICODE_STRING("LBCMNoChain", 11)) { - fRB->fLBCMNoChain = true; - } else if (opt == UNICODE_STRING("forward", 7)) { - fRB->fDefaultTree = &fRB->fForwardTree; - } else if (opt == UNICODE_STRING("reverse", 7)) { - fRB->fDefaultTree = &fRB->fReverseTree; - } else if (opt == UNICODE_STRING("safe_forward", 12)) { - fRB->fDefaultTree = &fRB->fSafeFwdTree; - } else if (opt == UNICODE_STRING("safe_reverse", 12)) { - fRB->fDefaultTree = &fRB->fSafeRevTree; - } else if (opt == UNICODE_STRING("lookAheadHardBreak", 18)) { - fRB->fLookAheadHardBreak = true; - } else if (opt == UNICODE_STRING("quoted_literals_only", 20)) { - fRuleSets[kRuleSet_rule_char-128].clear(); - } else if (opt == UNICODE_STRING("unquoted_literals", 17)) { - fRuleSets[kRuleSet_rule_char-128].applyPattern(UnicodeString(gRuleSet_rule_char_pattern), *fRB->fStatus); - } else { - error(U_BRK_UNRECOGNIZED_OPTION); - } - } - break; - - case doReverseDir: - fReverseRule = true; - break; - - case doStartVariableName: - n = pushNewNode(RBBINode::varRef); - if (U_FAILURE(*fRB->fStatus)) { - break; - } - n->fFirstPos = fScanIndex; - break; - - case doEndVariableName: - n = fNodeStack[fNodeStackPtr]; - if (n==NULL || n->fType != RBBINode::varRef) { - error(U_BRK_INTERNAL_ERROR); - break; - } - n->fLastPos = fScanIndex; - fRB->fRules.extractBetween(n->fFirstPos+1, n->fLastPos, n->fText); - // Look the newly scanned name up in the symbol table - // If there's an entry, set the l. child of the var ref to the replacement expression. - // (We also pass through here when scanning assignments, but no harm is done, other - // than a slight wasted effort that seems hard to avoid. Lookup will be null) - n->fLeftChild = fSymbolTable->lookupNode(n->fText); - break; - - case doCheckVarDef: - n = fNodeStack[fNodeStackPtr]; - if (n->fLeftChild == NULL) { - error(U_BRK_UNDEFINED_VARIABLE); - returnVal = false; - } - break; - - case doExprFinished: - break; - - case doRuleErrorAssignExpr: - error(U_BRK_ASSIGN_ERROR); - returnVal = false; - break; - - case doExit: - returnVal = false; - break; - - case doScanUnicodeSet: - scanSet(); - break; - - default: - error(U_BRK_INTERNAL_ERROR); - returnVal = false; - break; - } - return returnVal && U_SUCCESS(*fRB->fStatus); -} - - - - -//------------------------------------------------------------------------------ -// -// Error Report a rule parse error. -// Only report it if no previous error has been recorded. -// -//------------------------------------------------------------------------------ -void RBBIRuleScanner::error(UErrorCode e) { - if (U_SUCCESS(*fRB->fStatus)) { - *fRB->fStatus = e; - if (fRB->fParseError) { - fRB->fParseError->line = fLineNum; - fRB->fParseError->offset = fCharNum; - fRB->fParseError->preContext[0] = 0; - fRB->fParseError->postContext[0] = 0; - } - } -} - - - - -//------------------------------------------------------------------------------ -// -// fixOpStack The parse stack holds partially assembled chunks of the parse tree. -// An entry on the stack may be as small as a single setRef node, -// or as large as the parse tree -// for an entire expression (this will be the one item left on the stack -// when the parsing of an RBBI rule completes. -// -// This function is called when a binary operator is encountered. -// It looks back up the stack for operators that are not yet associated -// with a right operand, and if the precedence of the stacked operator >= -// the precedence of the current operator, binds the operand left, -// to the previously encountered operator. -// -//------------------------------------------------------------------------------ -void RBBIRuleScanner::fixOpStack(RBBINode::OpPrecedence p) { - RBBINode *n; - // printNodeStack("entering fixOpStack()"); - for (;;) { - n = fNodeStack[fNodeStackPtr-1]; // an operator node - if (n->fPrecedence == 0) { - RBBIDebugPuts("RBBIRuleScanner::fixOpStack, bad operator node"); - error(U_BRK_INTERNAL_ERROR); - return; - } - - if (n->fPrecedence < p || n->fPrecedence <= RBBINode::precLParen) { - // The most recent operand goes with the current operator, - // not with the previously stacked one. - break; - } - // Stack operator is a binary op ( '|' or concatenation) - // TOS operand becomes right child of this operator. - // Resulting subexpression becomes the TOS operand. - n->fRightChild = fNodeStack[fNodeStackPtr]; - fNodeStack[fNodeStackPtr]->fParent = n; - fNodeStackPtr--; - // printNodeStack("looping in fixOpStack() "); - } - - if (p <= RBBINode::precLParen) { - // Scan is at a right paren or end of expression. - // The scanned item must match the stack, or else there was an error. - // Discard the left paren (or start expr) node from the stack, - // leaving the completed (sub)expression as TOS. - if (n->fPrecedence != p) { - // Right paren encountered matched start of expression node, or - // end of expression matched with a left paren node. - error(U_BRK_MISMATCHED_PAREN); - } - fNodeStack[fNodeStackPtr-1] = fNodeStack[fNodeStackPtr]; - fNodeStackPtr--; - // Delete the now-discarded LParen or Start node. - delete n; - } - // printNodeStack("leaving fixOpStack()"); -} - - - - -//------------------------------------------------------------------------------ -// -// findSetFor given a UnicodeString, -// - find the corresponding Unicode Set (uset node) -// (create one if necessary) -// - Set fLeftChild of the caller's node (should be a setRef node) -// to the uset node -// Maintain a hash table of uset nodes, so the same one is always used -// for the same string. -// If a "to adopt" set is provided and we haven't seen this key before, -// add the provided set to the hash table. -// If the string is one (32 bit) char in length, the set contains -// just one element which is the char in question. -// If the string is "any", return a set containing all chars. -// -//------------------------------------------------------------------------------ -void RBBIRuleScanner::findSetFor(const UnicodeString &s, RBBINode *node, UnicodeSet *setToAdopt) { - - RBBISetTableEl *el; - - // First check whether we've already cached a set for this string. - // If so, just use the cached set in the new node. - // delete any set provided by the caller, since we own it. - el = (RBBISetTableEl *)uhash_get(fSetTable, &s); - if (el != NULL) { - delete setToAdopt; - node->fLeftChild = el->val; - U_ASSERT(node->fLeftChild->fType == RBBINode::uset); - return; - } - - // Haven't seen this set before. - // If the caller didn't provide us with a prebuilt set, - // create a new UnicodeSet now. - if (setToAdopt == NULL) { - if (s.compare(kAny, -1) == 0) { - setToAdopt = new UnicodeSet(0x000000, 0x10ffff); - } else { - UChar32 c; - c = s.char32At(0); - setToAdopt = new UnicodeSet(c, c); - } - } - - // - // Make a new uset node to refer to this UnicodeSet - // This new uset node becomes the child of the caller's setReference node. - // - RBBINode *usetNode = new RBBINode(RBBINode::uset); - if (usetNode == NULL) { - error(U_MEMORY_ALLOCATION_ERROR); - return; - } - usetNode->fInputSet = setToAdopt; - usetNode->fParent = node; - node->fLeftChild = usetNode; - usetNode->fText = s; - - - // - // Add the new uset node to the list of all uset nodes. - // - fRB->fUSetNodes->addElement(usetNode, *fRB->fStatus); - - - // - // Add the new set to the set hash table. - // - el = (RBBISetTableEl *)uprv_malloc(sizeof(RBBISetTableEl)); - UnicodeString *tkey = new UnicodeString(s); - if (tkey == NULL || el == NULL || setToAdopt == NULL) { - // Delete to avoid memory leak - delete tkey; - tkey = NULL; - uprv_free(el); - el = NULL; - delete setToAdopt; - setToAdopt = NULL; - - error(U_MEMORY_ALLOCATION_ERROR); - return; - } - el->key = tkey; - el->val = usetNode; - uhash_put(fSetTable, el->key, el, fRB->fStatus); - - return; -} - - - -// -// Assorted Unicode character constants. -// Numeric because there is no portable way to enter them as literals. -// (Think EBCDIC). -// -static const UChar chCR = 0x0d; // New lines, for terminating comments. -static const UChar chLF = 0x0a; -static const UChar chNEL = 0x85; // NEL newline variant -static const UChar chLS = 0x2028; // Unicode Line Separator -static const UChar chApos = 0x27; // single quote, for quoted chars. -static const UChar chPound = 0x23; // '#', introduces a comment. -static const UChar chBackSlash = 0x5c; // '\' introduces a char escape -static const UChar chLParen = 0x28; -static const UChar chRParen = 0x29; - - -//------------------------------------------------------------------------------ -// -// stripRules Return a rules string without extra spaces. -// (Comments are removed separately, during rule parsing.) -// -//------------------------------------------------------------------------------ -UnicodeString RBBIRuleScanner::stripRules(const UnicodeString &rules) { - UnicodeString strippedRules; - int32_t rulesLength = rules.length(); - - for (int32_t idx=0; idx= fRB->fRules.length()) { - return (UChar32)-1; - } - ch = fRB->fRules.char32At(fNextIndex); - if (U_IS_SURROGATE(ch)) { - error(U_ILLEGAL_CHAR_FOUND); - return U_SENTINEL; - } - fNextIndex = fRB->fRules.moveIndex32(fNextIndex, 1); - - if (ch == chCR || - ch == chNEL || - ch == chLS || - (ch == chLF && fLastChar != chCR)) { - // Character is starting a new line. Bump up the line number, and - // reset the column to 0. - fLineNum++; - fCharNum=0; - if (fQuoteMode) { - error(U_BRK_NEW_LINE_IN_QUOTED_STRING); - fQuoteMode = false; - } - } - else { - // Character is not starting a new line. Except in the case of a - // LF following a CR, increment the column position. - if (ch != chLF) { - fCharNum++; - } - } - fLastChar = ch; - return ch; -} - - -//------------------------------------------------------------------------------ -// -// nextChar for rules scanning. At this level, we handle stripping -// out comments and processing backslash character escapes. -// The rest of the rules grammar is handled at the next level up. -// -//------------------------------------------------------------------------------ -void RBBIRuleScanner::nextChar(RBBIRuleChar &c) { - - // Unicode Character constants needed for the processing done by nextChar(), - // in hex because literals wont work on EBCDIC machines. - - fScanIndex = fNextIndex; - c.fChar = nextCharLL(); - c.fEscaped = false; - - // - // check for '' sequence. - // These are recognized in all contexts, whether in quoted text or not. - // - if (c.fChar == chApos) { - if (fRB->fRules.char32At(fNextIndex) == chApos) { - c.fChar = nextCharLL(); // get nextChar officially so character counts - c.fEscaped = true; // stay correct. - } - else - { - // Single quote, by itself. - // Toggle quoting mode. - // Return either '(' or ')', because quotes cause a grouping of the quoted text. - fQuoteMode = !fQuoteMode; - if (fQuoteMode == true) { - c.fChar = chLParen; - } else { - c.fChar = chRParen; - } - c.fEscaped = false; // The paren that we return is not escaped. - return; - } - } - - if (fQuoteMode) { - c.fEscaped = true; - } - else - { - // We are not in a 'quoted region' of the source. - // - if (c.fChar == chPound) { - // Start of a comment. Consume the rest of it. - // The new-line char that terminates the comment is always returned. - // It will be treated as white-space, and serves to break up anything - // that might otherwise incorrectly clump together with a comment in - // the middle (a variable name, for example.) - int32_t commentStart = fScanIndex; - for (;;) { - c.fChar = nextCharLL(); - if (c.fChar == (UChar32)-1 || // EOF - c.fChar == chCR || - c.fChar == chLF || - c.fChar == chNEL || - c.fChar == chLS) {break;} - } - for (int32_t i=commentStart; ifStrippedRules.setCharAt(i, u' '); - } - } - if (c.fChar == (UChar32)-1) { - return; - } - - // - // check for backslash escaped characters. - // Use UnicodeString::unescapeAt() to handle them. - // - if (c.fChar == chBackSlash) { - c.fEscaped = true; - int32_t startX = fNextIndex; - c.fChar = fRB->fRules.unescapeAt(fNextIndex); - if (fNextIndex == startX) { - error(U_BRK_HEX_DIGITS_EXPECTED); - } - fCharNum += fNextIndex-startX; - } - } - // putc(c.fChar, stdout); -} - -//------------------------------------------------------------------------------ -// -// Parse RBBI rules. The state machine for rules parsing is here. -// The state tables are hand-written in the file rbbirpt.txt, -// and converted to the form used here by a perl -// script rbbicst.pl -// -//------------------------------------------------------------------------------ -void RBBIRuleScanner::parse() { - uint16_t state; - const RBBIRuleTableEl *tableEl; - - if (U_FAILURE(*fRB->fStatus)) { - return; - } - - state = 1; - nextChar(fC); - // - // Main loop for the rule parsing state machine. - // Runs once per state transition. - // Each time through optionally performs, depending on the state table, - // - an advance to the the next input char - // - an action to be performed. - // - pushing or popping a state to/from the local state return stack. - // - for (;;) { - // Bail out if anything has gone wrong. - // RBBI rule file parsing stops on the first error encountered. - if (U_FAILURE(*fRB->fStatus)) { - break; - } - - // Quit if state == 0. This is the normal way to exit the state machine. - // - if (state == 0) { - break; - } - - // Find the state table element that matches the input char from the rule, or the - // class of the input character. Start with the first table row for this - // state, then linearly scan forward until we find a row that matches the - // character. The last row for each state always matches all characters, so - // the search will stop there, if not before. - // - tableEl = &gRuleParseStateTable[state]; - #ifdef RBBI_DEBUG - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "scan")) { - RBBIDebugPrintf("char, line, col = (\'%c\', %d, %d) state=%s ", - fC.fChar, fLineNum, fCharNum, RBBIRuleStateNames[state]); - } - #endif - - for (;;) { - #ifdef RBBI_DEBUG - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "scan")) { RBBIDebugPrintf("."); fflush(stdout);} - #endif - if (tableEl->fCharClass < 127 && fC.fEscaped == false && tableEl->fCharClass == fC.fChar) { - // Table row specified an individual character, not a set, and - // the input character is not escaped, and - // the input character matched it. - break; - } - if (tableEl->fCharClass == 255) { - // Table row specified default, match anything character class. - break; - } - if (tableEl->fCharClass == 254 && fC.fEscaped) { - // Table row specified "escaped" and the char was escaped. - break; - } - if (tableEl->fCharClass == 253 && fC.fEscaped && - (fC.fChar == 0x50 || fC.fChar == 0x70 )) { - // Table row specified "escaped P" and the char is either 'p' or 'P'. - break; - } - if (tableEl->fCharClass == 252 && fC.fChar == (UChar32)-1) { - // Table row specified eof and we hit eof on the input. - break; - } - - if (tableEl->fCharClass >= 128 && tableEl->fCharClass < 240 && // Table specs a char class && - fC.fEscaped == false && // char is not escaped && - fC.fChar != (UChar32)-1) { // char is not EOF - U_ASSERT((tableEl->fCharClass-128) < UPRV_LENGTHOF(fRuleSets)); - if (fRuleSets[tableEl->fCharClass-128].contains(fC.fChar)) { - // Table row specified a character class, or set of characters, - // and the current char matches it. - break; - } - } - - // No match on this row, advance to the next row for this state, - tableEl++; - } - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "scan")) { RBBIDebugPuts("");} - - // - // We've found the row of the state table that matches the current input - // character from the rules string. - // Perform any action specified by this row in the state table. - if (doParseActions((int32_t)tableEl->fAction) == false) { - // Break out of the state machine loop if the - // the action signalled some kind of error, or - // the action was to exit, occurs on normal end-of-rules-input. - break; - } - - if (tableEl->fPushState != 0) { - fStackPtr++; - if (fStackPtr >= kStackSize) { - error(U_BRK_INTERNAL_ERROR); - RBBIDebugPuts("RBBIRuleScanner::parse() - state stack overflow."); - fStackPtr--; - } - fStack[fStackPtr] = tableEl->fPushState; - } - - if (tableEl->fNextChar) { - nextChar(fC); - } - - // Get the next state from the table entry, or from the - // state stack if the next state was specified as "pop". - if (tableEl->fNextState != 255) { - state = tableEl->fNextState; - } else { - state = fStack[fStackPtr]; - fStackPtr--; - if (fStackPtr < 0) { - error(U_BRK_INTERNAL_ERROR); - RBBIDebugPuts("RBBIRuleScanner::parse() - state stack underflow."); - fStackPtr++; - } - } - - } - - if (U_FAILURE(*fRB->fStatus)) { - return; - } - - // If there are no forward rules set an error. - // - if (fRB->fForwardTree == NULL) { - error(U_BRK_RULE_SYNTAX); - return; - } - - // - // Parsing of the input RBBI rules is complete. - // We now have a parse tree for the rule expressions - // and a list of all UnicodeSets that are referenced. - // -#ifdef RBBI_DEBUG - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "symbols")) {fSymbolTable->rbbiSymtablePrint();} - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "ptree")) { - RBBIDebugPrintf("Completed Forward Rules Parse Tree...\n"); - RBBINode::printTree(fRB->fForwardTree, true); - RBBIDebugPrintf("\nCompleted Reverse Rules Parse Tree...\n"); - RBBINode::printTree(fRB->fReverseTree, true); - RBBIDebugPrintf("\nCompleted Safe Point Forward Rules Parse Tree...\n"); - RBBINode::printTree(fRB->fSafeFwdTree, true); - RBBIDebugPrintf("\nCompleted Safe Point Reverse Rules Parse Tree...\n"); - RBBINode::printTree(fRB->fSafeRevTree, true); - } -#endif -} - - -//------------------------------------------------------------------------------ -// -// printNodeStack for debugging... -// -//------------------------------------------------------------------------------ -#ifdef RBBI_DEBUG -void RBBIRuleScanner::printNodeStack(const char *title) { - int i; - RBBIDebugPrintf("%s. Dumping node stack...\n", title); - for (i=fNodeStackPtr; i>0; i--) {RBBINode::printTree(fNodeStack[i], true);} -} -#endif - - - - -//------------------------------------------------------------------------------ -// -// pushNewNode create a new RBBINode of the specified type and push it -// onto the stack of nodes. -// -//------------------------------------------------------------------------------ -RBBINode *RBBIRuleScanner::pushNewNode(RBBINode::NodeType t) { - if (U_FAILURE(*fRB->fStatus)) { - return NULL; - } - if (fNodeStackPtr >= kStackSize - 1) { - error(U_BRK_RULE_SYNTAX); - RBBIDebugPuts("RBBIRuleScanner::pushNewNode - stack overflow."); - return NULL; - } - fNodeStackPtr++; - fNodeStack[fNodeStackPtr] = new RBBINode(t); - if (fNodeStack[fNodeStackPtr] == NULL) { - *fRB->fStatus = U_MEMORY_ALLOCATION_ERROR; - } - return fNodeStack[fNodeStackPtr]; -} - - - -//------------------------------------------------------------------------------ -// -// scanSet Construct a UnicodeSet from the text at the current scan -// position. Advance the scan position to the first character -// after the set. -// -// A new RBBI setref node referring to the set is pushed onto the node -// stack. -// -// The scan position is normally under the control of the state machine -// that controls rule parsing. UnicodeSets, however, are parsed by -// the UnicodeSet constructor, not by the RBBI rule parser. -// -//------------------------------------------------------------------------------ -void RBBIRuleScanner::scanSet() { - UnicodeSet *uset; - ParsePosition pos; - int startPos; - int i; - - if (U_FAILURE(*fRB->fStatus)) { - return; - } - - pos.setIndex(fScanIndex); - startPos = fScanIndex; - UErrorCode localStatus = U_ZERO_ERROR; - uset = new UnicodeSet(); - if (uset == NULL) { - localStatus = U_MEMORY_ALLOCATION_ERROR; - } else { - uset->applyPatternIgnoreSpace(fRB->fRules, pos, fSymbolTable, localStatus); - } - if (U_FAILURE(localStatus)) { - // TODO: Get more accurate position of the error from UnicodeSet's return info. - // UnicodeSet appears to not be reporting correctly at this time. - #ifdef RBBI_DEBUG - RBBIDebugPrintf("UnicodeSet parse position.ErrorIndex = %d\n", pos.getIndex()); - #endif - error(localStatus); - delete uset; - return; - } - - // Verify that the set contains at least one code point. - // - U_ASSERT(uset!=NULL); - if (uset->isEmpty()) { - // This set is empty. - // Make it an error, because it almost certainly is not what the user wanted. - // Also, avoids having to think about corner cases in the tree manipulation code - // that occurs later on. - error(U_BRK_RULE_EMPTY_SET); - delete uset; - return; - } - - - // Advance the RBBI parse position over the UnicodeSet pattern. - // Don't just set fScanIndex because the line/char positions maintained - // for error reporting would be thrown off. - i = pos.getIndex(); - for (;;) { - if (fNextIndex >= i) { - break; - } - nextCharLL(); - } - - if (U_SUCCESS(*fRB->fStatus)) { - RBBINode *n; - - n = pushNewNode(RBBINode::setRef); - if (U_FAILURE(*fRB->fStatus)) { - return; - } - n->fFirstPos = startPos; - n->fLastPos = fNextIndex; - fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); - // findSetFor() serves several purposes here: - // - Adopts storage for the UnicodeSet, will be responsible for deleting. - // - Maintains collection of all sets in use, needed later for establishing - // character categories for run time engine. - // - Eliminates mulitiple instances of the same set. - // - Creates a new uset node if necessary (if this isn't a duplicate.) - findSetFor(n->fText, n, uset); - } - -} - -int32_t RBBIRuleScanner::numRules() { - return fRuleNum; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// file: rbbiscan.cpp +// +// Copyright (C) 2002-2016, International Business Machines Corporation and others. +// All Rights Reserved. +// +// This file contains the Rule Based Break Iterator Rule Builder functions for +// scanning the rules and assembling a parse tree. This is the first phase +// of compiling the rules. +// +// The overall of the rules is managed by class RBBIRuleBuilder, which will +// create and use an instance of this class as part of the process. +// + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/unistr.h" +#include "unicode/uniset.h" +#include "unicode/uchar.h" +#include "unicode/uchriter.h" +#include "unicode/parsepos.h" +#include "unicode/parseerr.h" +#include "cmemory.h" +#include "cstring.h" + +#include "rbbirpt.h" // Contains state table for the rbbi rules parser. + // generated by a Perl script. +#include "rbbirb.h" +#include "rbbinode.h" +#include "rbbiscan.h" +#include "rbbitblb.h" + +#include "uassert.h" + +//------------------------------------------------------------------------------ +// +// Unicode Set init strings for each of the character classes needed for parsing a rule file. +// (Initialized with hex values for portability to EBCDIC based machines. +// Really ugly, but there's no good way to avoid it.) +// +// The sets are referred to by name in the rbbirpt.txt, which is the +// source form of the state transition table for the RBBI rule parser. +// +//------------------------------------------------------------------------------ +static const char16_t gRuleSet_rule_char_pattern[] = { + // Characters that may appear as literals in patterns without escaping or quoting. + // [ ^ [ \ p { Z } \ u 0 0 2 0 + 0x5b, 0x5e, 0x5b, 0x5c, 0x70, 0x7b, 0x5a, 0x7d, 0x5c, 0x75, 0x30, 0x30, 0x32, 0x30, + // - \ u 0 0 7 f ] - [ \ p + 0x2d, 0x5c, 0x75, 0x30, 0x30, 0x37, 0x66, 0x5d, 0x2d, 0x5b, 0x5c, 0x70, + // { L } ] - [ \ p { N } ] ] + 0x7b, 0x4c, 0x7d, 0x5d, 0x2d, 0x5b, 0x5c, 0x70, 0x7b, 0x4e, 0x7d, 0x5d, 0x5d, 0}; + +static const char16_t gRuleSet_name_char_pattern[] = { +// [ _ \ p { L } \ p { N } ] + 0x5b, 0x5f, 0x5c, 0x70, 0x7b, 0x4c, 0x7d, 0x5c, 0x70, 0x7b, 0x4e, 0x7d, 0x5d, 0}; + +static const char16_t gRuleSet_digit_char_pattern[] = { +// [ 0 - 9 ] + 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0}; + +static const char16_t gRuleSet_name_start_char_pattern[] = { +// [ _ \ p { L } ] + 0x5b, 0x5f, 0x5c, 0x70, 0x7b, 0x4c, 0x7d, 0x5d, 0 }; + +static const char16_t kAny[] = {0x61, 0x6e, 0x79, 0x00}; // "any" + + +U_CDECL_BEGIN +static void U_CALLCONV RBBISetTable_deleter(void *p) { + icu::RBBISetTableEl *px = (icu::RBBISetTableEl *)p; + delete px->key; + // Note: px->val is owned by the linked list "fSetsListHead" in scanner. + // Don't delete the value nodes here. + uprv_free(px); +} +U_CDECL_END + +U_NAMESPACE_BEGIN + +//------------------------------------------------------------------------------ +// +// Constructor. +// +//------------------------------------------------------------------------------ +RBBIRuleScanner::RBBIRuleScanner(RBBIRuleBuilder *rb) +{ + fRB = rb; + fScanIndex = 0; + fNextIndex = 0; + fQuoteMode = false; + fLineNum = 1; + fCharNum = 0; + fLastChar = 0; + + fStateTable = nullptr; + fStack[0] = 0; + fStackPtr = 0; + fNodeStack[0] = nullptr; + fNodeStackPtr = 0; + + fReverseRule = false; + fLookAheadRule = false; + fNoChainInRule = false; + + fSymbolTable = nullptr; + fSetTable = nullptr; + fRuleNum = 0; + fOptionStart = 0; + + // Do not check status until after all critical fields are sufficiently initialized + // that the destructor can run cleanly. + if (U_FAILURE(*rb->fStatus)) { + return; + } + + // + // Set up the constant Unicode Sets. + // Note: These could be made static, lazily initialized, and shared among + // all instances of RBBIRuleScanners. BUT this is quite a bit simpler, + // and the time to build these few sets should be small compared to a + // full break iterator build. + fRuleSets[kRuleSet_rule_char-128] + = UnicodeSet(UnicodeString(gRuleSet_rule_char_pattern), *rb->fStatus); + // fRuleSets[kRuleSet_white_space-128] = [:Pattern_White_Space:] + fRuleSets[kRuleSet_white_space-128]. + add(9, 0xd).add(0x20).add(0x85).add(0x200e, 0x200f).add(0x2028, 0x2029); + fRuleSets[kRuleSet_name_char-128] + = UnicodeSet(UnicodeString(gRuleSet_name_char_pattern), *rb->fStatus); + fRuleSets[kRuleSet_name_start_char-128] + = UnicodeSet(UnicodeString(gRuleSet_name_start_char_pattern), *rb->fStatus); + fRuleSets[kRuleSet_digit_char-128] + = UnicodeSet(UnicodeString(gRuleSet_digit_char_pattern), *rb->fStatus); + if (*rb->fStatus == U_ILLEGAL_ARGUMENT_ERROR) { + // This case happens if ICU's data is missing. UnicodeSet tries to look up property + // names from the init string, can't find them, and claims an illegal argument. + // Change the error so that the actual problem will be clearer to users. + *rb->fStatus = U_BRK_INIT_ERROR; + } + if (U_FAILURE(*rb->fStatus)) { + return; + } + + fSymbolTable = new RBBISymbolTable(this, rb->fRules, *rb->fStatus); + if (fSymbolTable == nullptr) { + *rb->fStatus = U_MEMORY_ALLOCATION_ERROR; + return; + } + fSetTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, nullptr, rb->fStatus); + if (U_FAILURE(*rb->fStatus)) { + return; + } + uhash_setValueDeleter(fSetTable, RBBISetTable_deleter); +} + + + +//------------------------------------------------------------------------------ +// +// Destructor +// +//------------------------------------------------------------------------------ +RBBIRuleScanner::~RBBIRuleScanner() { + delete fSymbolTable; + if (fSetTable != nullptr) { + uhash_close(fSetTable); + fSetTable = nullptr; + + } + + + // Node Stack. + // Normally has one entry, which is the entire parse tree for the rules. + // If errors occurred, there may be additional subtrees left on the stack. + while (fNodeStackPtr > 0) { + delete fNodeStack[fNodeStackPtr]; + fNodeStackPtr--; + } + +} + +//------------------------------------------------------------------------------ +// +// doParseAction Do some action during rule parsing. +// Called by the parse state machine. +// Actions build the parse tree and Unicode Sets, +// and maintain the parse stack for nested expressions. +// +// TODO: unify EParseAction and RBBI_RuleParseAction enum types. +// They represent exactly the same thing. They're separate +// only to work around enum forward declaration restrictions +// in some compilers, while at the same time avoiding multiple +// definitions problems. I'm sure that there's a better way. +// +//------------------------------------------------------------------------------ +UBool RBBIRuleScanner::doParseActions(int32_t action) +{ + RBBINode *n = nullptr; + + UBool returnVal = true; + + switch (action) { + + case doExprStart: + pushNewNode(RBBINode::opStart); + fRuleNum++; + break; + + + case doNoChain: + // Scanned a '^' while on the rule start state. + fNoChainInRule = true; + break; + + + case doExprOrOperator: + { + fixOpStack(RBBINode::precOpCat); + RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; + RBBINode *orNode = pushNewNode(RBBINode::opOr); + if (U_FAILURE(*fRB->fStatus)) { + break; + } + orNode->fLeftChild = operandNode; + operandNode->fParent = orNode; + } + break; + + case doExprCatOperator: + // concatenation operator. + // For the implicit concatenation of adjacent terms in an expression that are + // not separated by any other operator. Action is invoked between the + // actions for the two terms. + { + fixOpStack(RBBINode::precOpCat); + RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; + RBBINode *catNode = pushNewNode(RBBINode::opCat); + if (U_FAILURE(*fRB->fStatus)) { + break; + } + catNode->fLeftChild = operandNode; + operandNode->fParent = catNode; + } + break; + + case doLParen: + // Open Paren. + // The openParen node is a dummy operation type with a low precedence, + // which has the affect of ensuring that any real binary op that + // follows within the parens binds more tightly to the operands than + // stuff outside of the parens. + pushNewNode(RBBINode::opLParen); + break; + + case doExprRParen: + fixOpStack(RBBINode::precLParen); + break; + + case doNOP: + break; + + case doStartAssign: + // We've just scanned "$variable = " + // The top of the node stack has the $variable ref node. + + // Save the start position of the RHS text in the StartExpression node + // that precedes the $variableReference node on the stack. + // This will eventually be used when saving the full $variable replacement + // text as a string. + n = fNodeStack[fNodeStackPtr-1]; + n->fFirstPos = fNextIndex; // move past the '=' + + // Push a new start-of-expression node; needed to keep parse of the + // RHS expression happy. + pushNewNode(RBBINode::opStart); + break; + + + + + case doEndAssign: + { + // We have reached the end of an assignment statement. + // Current scan char is the ';' that terminates the assignment. + + // Terminate expression, leaves expression parse tree rooted in TOS node. + fixOpStack(RBBINode::precStart); + + RBBINode *startExprNode = fNodeStack[fNodeStackPtr-2]; + RBBINode *varRefNode = fNodeStack[fNodeStackPtr-1]; + RBBINode *RHSExprNode = fNodeStack[fNodeStackPtr]; + + // Save original text of right side of assignment, excluding the terminating ';' + // in the root of the node for the right-hand-side expression. + RHSExprNode->fFirstPos = startExprNode->fFirstPos; + RHSExprNode->fLastPos = fScanIndex; + fRB->fRules.extractBetween(RHSExprNode->fFirstPos, RHSExprNode->fLastPos, RHSExprNode->fText); + + // Expression parse tree becomes l. child of the $variable reference node. + varRefNode->fLeftChild = RHSExprNode; + RHSExprNode->fParent = varRefNode; + + // Make a symbol table entry for the $variableRef node. + fSymbolTable->addEntry(varRefNode->fText, varRefNode, *fRB->fStatus); + if (U_FAILURE(*fRB->fStatus)) { + // This is a round-about way to get the parse position set + // so that duplicate symbols error messages include a line number. + UErrorCode t = *fRB->fStatus; + *fRB->fStatus = U_ZERO_ERROR; + error(t); + } + + // Clean up the stack. + delete startExprNode; + fNodeStackPtr-=3; + break; + } + + case doEndOfRule: + { + fixOpStack(RBBINode::precStart); // Terminate expression, leaves expression + if (U_FAILURE(*fRB->fStatus)) { // parse tree rooted in TOS node. + break; + } +#ifdef RBBI_DEBUG + if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "rtree")) {printNodeStack("end of rule");} +#endif + U_ASSERT(fNodeStackPtr == 1); + RBBINode *thisRule = fNodeStack[fNodeStackPtr]; + + // If this rule includes a look-ahead '/', add a endMark node to the + // expression tree. + if (fLookAheadRule) { + RBBINode *endNode = pushNewNode(RBBINode::endMark); + RBBINode *catNode = pushNewNode(RBBINode::opCat); + if (U_FAILURE(*fRB->fStatus)) { + break; + } + fNodeStackPtr -= 2; + catNode->fLeftChild = thisRule; + catNode->fRightChild = endNode; + fNodeStack[fNodeStackPtr] = catNode; + endNode->fVal = fRuleNum; + endNode->fLookAheadEnd = true; + thisRule = catNode; + + // TODO: Disable chaining out of look-ahead (hard break) rules. + // The break on rule match is forced, so there is no point in building up + // the state table to chain into another rule for a longer match. + } + + // Mark this node as being the root of a rule. + thisRule->fRuleRoot = true; + + // Flag if chaining into this rule is wanted. + // + if (fRB->fChainRules && // If rule chaining is enabled globally via !!chain + !fNoChainInRule) { // and no '^' chain-in inhibit was on this rule + thisRule->fChainIn = true; + } + + + // All rule expressions are ORed together. + // The ';' that terminates an expression really just functions as a '|' with + // a low operator prededence. + // + // Each of the four sets of rules are collected separately. + // (forward, reverse, safe_forward, safe_reverse) + // OR this rule into the appropriate group of them. + // + RBBINode **destRules = (fReverseRule? &fRB->fSafeRevTree : fRB->fDefaultTree); + + if (*destRules != nullptr) { + // This is not the first rule encountered. + // OR previous stuff (from *destRules) + // with the current rule expression (on the Node Stack) + // with the resulting OR expression going to *destRules + // + thisRule = fNodeStack[fNodeStackPtr]; + RBBINode *prevRules = *destRules; + RBBINode *orNode = pushNewNode(RBBINode::opOr); + if (U_FAILURE(*fRB->fStatus)) { + break; + } + orNode->fLeftChild = prevRules; + prevRules->fParent = orNode; + orNode->fRightChild = thisRule; + thisRule->fParent = orNode; + *destRules = orNode; + } + else + { + // This is the first rule encountered (for this direction). + // Just move its parse tree from the stack to *destRules. + *destRules = fNodeStack[fNodeStackPtr]; + } + fReverseRule = false; // in preparation for the next rule. + fLookAheadRule = false; + fNoChainInRule = false; + fNodeStackPtr = 0; + } + break; + + + case doRuleError: + error(U_BRK_RULE_SYNTAX); + returnVal = false; + break; + + + case doVariableNameExpectedErr: + error(U_BRK_RULE_SYNTAX); + break; + + + // + // Unary operands + ? * + // These all appear after the operand to which they apply. + // When we hit one, the operand (may be a whole sub expression) + // will be on the top of the stack. + // Unary Operator becomes TOS, with the old TOS as its one child. + case doUnaryOpPlus: + { + RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; + RBBINode *plusNode = pushNewNode(RBBINode::opPlus); + if (U_FAILURE(*fRB->fStatus)) { + break; + } + plusNode->fLeftChild = operandNode; + operandNode->fParent = plusNode; + } + break; + + case doUnaryOpQuestion: + { + RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; + RBBINode *qNode = pushNewNode(RBBINode::opQuestion); + if (U_FAILURE(*fRB->fStatus)) { + break; + } + qNode->fLeftChild = operandNode; + operandNode->fParent = qNode; + } + break; + + case doUnaryOpStar: + { + RBBINode *operandNode = fNodeStack[fNodeStackPtr--]; + RBBINode *starNode = pushNewNode(RBBINode::opStar); + if (U_FAILURE(*fRB->fStatus)) { + break; + } + starNode->fLeftChild = operandNode; + operandNode->fParent = starNode; + } + break; + + case doRuleChar: + // A "Rule Character" is any single character that is a literal part + // of the regular expression. Like a, b and c in the expression "(abc*) | [:L:]" + // These are pretty uncommon in break rules; the terms are more commonly + // sets. To keep things uniform, treat these characters like as + // sets that just happen to contain only one character. + { + n = pushNewNode(RBBINode::setRef); + if (U_FAILURE(*fRB->fStatus)) { + break; + } + findSetFor(UnicodeString(fC.fChar), n); + n->fFirstPos = fScanIndex; + n->fLastPos = fNextIndex; + fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); + break; + } + + case doDotAny: + // scanned a ".", meaning match any single character. + { + n = pushNewNode(RBBINode::setRef); + if (U_FAILURE(*fRB->fStatus)) { + break; + } + findSetFor(UnicodeString(true, kAny, 3), n); + n->fFirstPos = fScanIndex; + n->fLastPos = fNextIndex; + fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); + break; + } + + case doSlash: + // Scanned a '/', which identifies a look-ahead break position in a rule. + n = pushNewNode(RBBINode::lookAhead); + if (U_FAILURE(*fRB->fStatus)) { + break; + } + n->fVal = fRuleNum; + n->fFirstPos = fScanIndex; + n->fLastPos = fNextIndex; + fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); + fLookAheadRule = true; + break; + + + case doStartTagValue: + // Scanned a '{', the opening delimiter for a tag value within a rule. + n = pushNewNode(RBBINode::tag); + if (U_FAILURE(*fRB->fStatus)) { + break; + } + n->fVal = 0; + n->fFirstPos = fScanIndex; + n->fLastPos = fNextIndex; + break; + + case doTagDigit: + // Just scanned a decimal digit that's part of a tag value + { + n = fNodeStack[fNodeStackPtr]; + uint32_t v = u_charDigitValue(fC.fChar); + U_ASSERT(v < 10); + n->fVal = n->fVal*10 + v; + break; + } + + case doTagValue: + n = fNodeStack[fNodeStackPtr]; + n->fLastPos = fNextIndex; + fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); + break; + + case doTagExpectedError: + error(U_BRK_MALFORMED_RULE_TAG); + returnVal = false; + break; + + case doOptionStart: + // Scanning a !!option. At the start of string. + fOptionStart = fScanIndex; + break; + + case doOptionEnd: + { + UnicodeString opt(fRB->fRules, fOptionStart, fScanIndex-fOptionStart); + if (opt == UNICODE_STRING("chain", 5)) { + fRB->fChainRules = true; + } else if (opt == UNICODE_STRING("LBCMNoChain", 11)) { + fRB->fLBCMNoChain = true; + } else if (opt == UNICODE_STRING("forward", 7)) { + fRB->fDefaultTree = &fRB->fForwardTree; + } else if (opt == UNICODE_STRING("reverse", 7)) { + fRB->fDefaultTree = &fRB->fReverseTree; + } else if (opt == UNICODE_STRING("safe_forward", 12)) { + fRB->fDefaultTree = &fRB->fSafeFwdTree; + } else if (opt == UNICODE_STRING("safe_reverse", 12)) { + fRB->fDefaultTree = &fRB->fSafeRevTree; + } else if (opt == UNICODE_STRING("lookAheadHardBreak", 18)) { + fRB->fLookAheadHardBreak = true; + } else if (opt == UNICODE_STRING("quoted_literals_only", 20)) { + fRuleSets[kRuleSet_rule_char-128].clear(); + } else if (opt == UNICODE_STRING("unquoted_literals", 17)) { + fRuleSets[kRuleSet_rule_char-128].applyPattern(UnicodeString(gRuleSet_rule_char_pattern), *fRB->fStatus); + } else { + error(U_BRK_UNRECOGNIZED_OPTION); + } + } + break; + + case doReverseDir: + fReverseRule = true; + break; + + case doStartVariableName: + n = pushNewNode(RBBINode::varRef); + if (U_FAILURE(*fRB->fStatus)) { + break; + } + n->fFirstPos = fScanIndex; + break; + + case doEndVariableName: + n = fNodeStack[fNodeStackPtr]; + if (n==nullptr || n->fType != RBBINode::varRef) { + error(U_BRK_INTERNAL_ERROR); + break; + } + n->fLastPos = fScanIndex; + fRB->fRules.extractBetween(n->fFirstPos+1, n->fLastPos, n->fText); + // Look the newly scanned name up in the symbol table + // If there's an entry, set the l. child of the var ref to the replacement expression. + // (We also pass through here when scanning assignments, but no harm is done, other + // than a slight wasted effort that seems hard to avoid. Lookup will be null) + n->fLeftChild = fSymbolTable->lookupNode(n->fText); + break; + + case doCheckVarDef: + n = fNodeStack[fNodeStackPtr]; + if (n->fLeftChild == nullptr) { + error(U_BRK_UNDEFINED_VARIABLE); + returnVal = false; + } + break; + + case doExprFinished: + break; + + case doRuleErrorAssignExpr: + error(U_BRK_ASSIGN_ERROR); + returnVal = false; + break; + + case doExit: + returnVal = false; + break; + + case doScanUnicodeSet: + scanSet(); + break; + + default: + error(U_BRK_INTERNAL_ERROR); + returnVal = false; + break; + } + return returnVal && U_SUCCESS(*fRB->fStatus); +} + + + + +//------------------------------------------------------------------------------ +// +// Error Report a rule parse error. +// Only report it if no previous error has been recorded. +// +//------------------------------------------------------------------------------ +void RBBIRuleScanner::error(UErrorCode e) { + if (U_SUCCESS(*fRB->fStatus)) { + *fRB->fStatus = e; + if (fRB->fParseError) { + fRB->fParseError->line = fLineNum; + fRB->fParseError->offset = fCharNum; + fRB->fParseError->preContext[0] = 0; + fRB->fParseError->postContext[0] = 0; + } + } +} + + + + +//------------------------------------------------------------------------------ +// +// fixOpStack The parse stack holds partially assembled chunks of the parse tree. +// An entry on the stack may be as small as a single setRef node, +// or as large as the parse tree +// for an entire expression (this will be the one item left on the stack +// when the parsing of an RBBI rule completes. +// +// This function is called when a binary operator is encountered. +// It looks back up the stack for operators that are not yet associated +// with a right operand, and if the precedence of the stacked operator >= +// the precedence of the current operator, binds the operand left, +// to the previously encountered operator. +// +//------------------------------------------------------------------------------ +void RBBIRuleScanner::fixOpStack(RBBINode::OpPrecedence p) { + RBBINode *n; + // printNodeStack("entering fixOpStack()"); + for (;;) { + n = fNodeStack[fNodeStackPtr-1]; // an operator node + if (n->fPrecedence == 0) { + RBBIDebugPuts("RBBIRuleScanner::fixOpStack, bad operator node"); + error(U_BRK_INTERNAL_ERROR); + return; + } + + if (n->fPrecedence < p || n->fPrecedence <= RBBINode::precLParen) { + // The most recent operand goes with the current operator, + // not with the previously stacked one. + break; + } + // Stack operator is a binary op ( '|' or concatenation) + // TOS operand becomes right child of this operator. + // Resulting subexpression becomes the TOS operand. + n->fRightChild = fNodeStack[fNodeStackPtr]; + fNodeStack[fNodeStackPtr]->fParent = n; + fNodeStackPtr--; + // printNodeStack("looping in fixOpStack() "); + } + + if (p <= RBBINode::precLParen) { + // Scan is at a right paren or end of expression. + // The scanned item must match the stack, or else there was an error. + // Discard the left paren (or start expr) node from the stack, + // leaving the completed (sub)expression as TOS. + if (n->fPrecedence != p) { + // Right paren encountered matched start of expression node, or + // end of expression matched with a left paren node. + error(U_BRK_MISMATCHED_PAREN); + } + fNodeStack[fNodeStackPtr-1] = fNodeStack[fNodeStackPtr]; + fNodeStackPtr--; + // Delete the now-discarded LParen or Start node. + delete n; + } + // printNodeStack("leaving fixOpStack()"); +} + + + + +//------------------------------------------------------------------------------ +// +// findSetFor given a UnicodeString, +// - find the corresponding Unicode Set (uset node) +// (create one if necessary) +// - Set fLeftChild of the caller's node (should be a setRef node) +// to the uset node +// Maintain a hash table of uset nodes, so the same one is always used +// for the same string. +// If a "to adopt" set is provided and we haven't seen this key before, +// add the provided set to the hash table. +// If the string is one (32 bit) char in length, the set contains +// just one element which is the char in question. +// If the string is "any", return a set containing all chars. +// +//------------------------------------------------------------------------------ +void RBBIRuleScanner::findSetFor(const UnicodeString &s, RBBINode *node, UnicodeSet *setToAdopt) { + + RBBISetTableEl *el; + + // First check whether we've already cached a set for this string. + // If so, just use the cached set in the new node. + // delete any set provided by the caller, since we own it. + el = (RBBISetTableEl *)uhash_get(fSetTable, &s); + if (el != nullptr) { + delete setToAdopt; + node->fLeftChild = el->val; + U_ASSERT(node->fLeftChild->fType == RBBINode::uset); + return; + } + + // Haven't seen this set before. + // If the caller didn't provide us with a prebuilt set, + // create a new UnicodeSet now. + if (setToAdopt == nullptr) { + if (s.compare(kAny, -1) == 0) { + setToAdopt = new UnicodeSet(0x000000, 0x10ffff); + } else { + UChar32 c; + c = s.char32At(0); + setToAdopt = new UnicodeSet(c, c); + } + } + + // + // Make a new uset node to refer to this UnicodeSet + // This new uset node becomes the child of the caller's setReference node. + // + RBBINode *usetNode = new RBBINode(RBBINode::uset); + if (usetNode == nullptr) { + error(U_MEMORY_ALLOCATION_ERROR); + return; + } + usetNode->fInputSet = setToAdopt; + usetNode->fParent = node; + node->fLeftChild = usetNode; + usetNode->fText = s; + + + // + // Add the new uset node to the list of all uset nodes. + // + fRB->fUSetNodes->addElement(usetNode, *fRB->fStatus); + + + // + // Add the new set to the set hash table. + // + el = (RBBISetTableEl *)uprv_malloc(sizeof(RBBISetTableEl)); + UnicodeString *tkey = new UnicodeString(s); + if (tkey == nullptr || el == nullptr || setToAdopt == nullptr) { + // Delete to avoid memory leak + delete tkey; + tkey = nullptr; + uprv_free(el); + el = nullptr; + delete setToAdopt; + setToAdopt = nullptr; + + error(U_MEMORY_ALLOCATION_ERROR); + return; + } + el->key = tkey; + el->val = usetNode; + uhash_put(fSetTable, el->key, el, fRB->fStatus); + + return; +} + + + +// +// Assorted Unicode character constants. +// Numeric because there is no portable way to enter them as literals. +// (Think EBCDIC). +// +static const char16_t chCR = 0x0d; // New lines, for terminating comments. +static const char16_t chLF = 0x0a; +static const char16_t chNEL = 0x85; // NEL newline variant +static const char16_t chLS = 0x2028; // Unicode Line Separator +static const char16_t chApos = 0x27; // single quote, for quoted chars. +static const char16_t chPound = 0x23; // '#', introduces a comment. +static const char16_t chBackSlash = 0x5c; // '\' introduces a char escape +static const char16_t chLParen = 0x28; +static const char16_t chRParen = 0x29; + + +//------------------------------------------------------------------------------ +// +// stripRules Return a rules string without extra spaces. +// (Comments are removed separately, during rule parsing.) +// +//------------------------------------------------------------------------------ +UnicodeString RBBIRuleScanner::stripRules(const UnicodeString &rules) { + UnicodeString strippedRules; + int32_t rulesLength = rules.length(); + + for (int32_t idx=0; idx= fRB->fRules.length()) { + return (UChar32)-1; + } + ch = fRB->fRules.char32At(fNextIndex); + if (U_IS_SURROGATE(ch)) { + error(U_ILLEGAL_CHAR_FOUND); + return U_SENTINEL; + } + fNextIndex = fRB->fRules.moveIndex32(fNextIndex, 1); + + if (ch == chCR || + ch == chNEL || + ch == chLS || + (ch == chLF && fLastChar != chCR)) { + // Character is starting a new line. Bump up the line number, and + // reset the column to 0. + fLineNum++; + fCharNum=0; + if (fQuoteMode) { + error(U_BRK_NEW_LINE_IN_QUOTED_STRING); + fQuoteMode = false; + } + } + else { + // Character is not starting a new line. Except in the case of a + // LF following a CR, increment the column position. + if (ch != chLF) { + fCharNum++; + } + } + fLastChar = ch; + return ch; +} + + +//------------------------------------------------------------------------------ +// +// nextChar for rules scanning. At this level, we handle stripping +// out comments and processing backslash character escapes. +// The rest of the rules grammar is handled at the next level up. +// +//------------------------------------------------------------------------------ +void RBBIRuleScanner::nextChar(RBBIRuleChar &c) { + + // Unicode Character constants needed for the processing done by nextChar(), + // in hex because literals wont work on EBCDIC machines. + + fScanIndex = fNextIndex; + c.fChar = nextCharLL(); + c.fEscaped = false; + + // + // check for '' sequence. + // These are recognized in all contexts, whether in quoted text or not. + // + if (c.fChar == chApos) { + if (fRB->fRules.char32At(fNextIndex) == chApos) { + c.fChar = nextCharLL(); // get nextChar officially so character counts + c.fEscaped = true; // stay correct. + } + else + { + // Single quote, by itself. + // Toggle quoting mode. + // Return either '(' or ')', because quotes cause a grouping of the quoted text. + fQuoteMode = !fQuoteMode; + if (fQuoteMode) { + c.fChar = chLParen; + } else { + c.fChar = chRParen; + } + c.fEscaped = false; // The paren that we return is not escaped. + return; + } + } + + if (fQuoteMode) { + c.fEscaped = true; + } + else + { + // We are not in a 'quoted region' of the source. + // + if (c.fChar == chPound) { + // Start of a comment. Consume the rest of it. + // The new-line char that terminates the comment is always returned. + // It will be treated as white-space, and serves to break up anything + // that might otherwise incorrectly clump together with a comment in + // the middle (a variable name, for example.) + int32_t commentStart = fScanIndex; + for (;;) { + c.fChar = nextCharLL(); + if (c.fChar == (UChar32)-1 || // EOF + c.fChar == chCR || + c.fChar == chLF || + c.fChar == chNEL || + c.fChar == chLS) {break;} + } + for (int32_t i=commentStart; ifStrippedRules.setCharAt(i, u' '); + } + } + if (c.fChar == (UChar32)-1) { + return; + } + + // + // check for backslash escaped characters. + // Use UnicodeString::unescapeAt() to handle them. + // + if (c.fChar == chBackSlash) { + c.fEscaped = true; + int32_t startX = fNextIndex; + c.fChar = fRB->fRules.unescapeAt(fNextIndex); + if (fNextIndex == startX) { + error(U_BRK_HEX_DIGITS_EXPECTED); + } + fCharNum += fNextIndex-startX; + } + } + // putc(c.fChar, stdout); +} + +//------------------------------------------------------------------------------ +// +// Parse RBBI rules. The state machine for rules parsing is here. +// The state tables are hand-written in the file rbbirpt.txt, +// and converted to the form used here by a perl +// script rbbicst.pl +// +//------------------------------------------------------------------------------ +void RBBIRuleScanner::parse() { + uint16_t state; + const RBBIRuleTableEl *tableEl; + + if (U_FAILURE(*fRB->fStatus)) { + return; + } + + state = 1; + nextChar(fC); + // + // Main loop for the rule parsing state machine. + // Runs once per state transition. + // Each time through optionally performs, depending on the state table, + // - an advance to the the next input char + // - an action to be performed. + // - pushing or popping a state to/from the local state return stack. + // + for (;;) { + // Bail out if anything has gone wrong. + // RBBI rule file parsing stops on the first error encountered. + if (U_FAILURE(*fRB->fStatus)) { + break; + } + + // Quit if state == 0. This is the normal way to exit the state machine. + // + if (state == 0) { + break; + } + + // Find the state table element that matches the input char from the rule, or the + // class of the input character. Start with the first table row for this + // state, then linearly scan forward until we find a row that matches the + // character. The last row for each state always matches all characters, so + // the search will stop there, if not before. + // + tableEl = &gRuleParseStateTable[state]; + #ifdef RBBI_DEBUG + if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "scan")) { + RBBIDebugPrintf("char, line, col = (\'%c\', %d, %d) state=%s ", + fC.fChar, fLineNum, fCharNum, RBBIRuleStateNames[state]); + } + #endif + + for (;;) { + #ifdef RBBI_DEBUG + if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "scan")) { RBBIDebugPrintf("."); fflush(stdout);} + #endif + if (tableEl->fCharClass < 127 && fC.fEscaped == false && tableEl->fCharClass == fC.fChar) { + // Table row specified an individual character, not a set, and + // the input character is not escaped, and + // the input character matched it. + break; + } + if (tableEl->fCharClass == 255) { + // Table row specified default, match anything character class. + break; + } + if (tableEl->fCharClass == 254 && fC.fEscaped) { + // Table row specified "escaped" and the char was escaped. + break; + } + if (tableEl->fCharClass == 253 && fC.fEscaped && + (fC.fChar == 0x50 || fC.fChar == 0x70 )) { + // Table row specified "escaped P" and the char is either 'p' or 'P'. + break; + } + if (tableEl->fCharClass == 252 && fC.fChar == (UChar32)-1) { + // Table row specified eof and we hit eof on the input. + break; + } + + if (tableEl->fCharClass >= 128 && tableEl->fCharClass < 240 && // Table specs a char class && + fC.fEscaped == false && // char is not escaped && + fC.fChar != (UChar32)-1) { // char is not EOF + U_ASSERT((tableEl->fCharClass-128) < UPRV_LENGTHOF(fRuleSets)); + if (fRuleSets[tableEl->fCharClass-128].contains(fC.fChar)) { + // Table row specified a character class, or set of characters, + // and the current char matches it. + break; + } + } + + // No match on this row, advance to the next row for this state, + tableEl++; + } + if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "scan")) { RBBIDebugPuts("");} + + // + // We've found the row of the state table that matches the current input + // character from the rules string. + // Perform any action specified by this row in the state table. + if (doParseActions((int32_t)tableEl->fAction) == false) { + // Break out of the state machine loop if the + // the action signalled some kind of error, or + // the action was to exit, occurs on normal end-of-rules-input. + break; + } + + if (tableEl->fPushState != 0) { + fStackPtr++; + if (fStackPtr >= kStackSize) { + error(U_BRK_INTERNAL_ERROR); + RBBIDebugPuts("RBBIRuleScanner::parse() - state stack overflow."); + fStackPtr--; + } + fStack[fStackPtr] = tableEl->fPushState; + } + + if (tableEl->fNextChar) { + nextChar(fC); + } + + // Get the next state from the table entry, or from the + // state stack if the next state was specified as "pop". + if (tableEl->fNextState != 255) { + state = tableEl->fNextState; + } else { + state = fStack[fStackPtr]; + fStackPtr--; + if (fStackPtr < 0) { + error(U_BRK_INTERNAL_ERROR); + RBBIDebugPuts("RBBIRuleScanner::parse() - state stack underflow."); + fStackPtr++; + } + } + + } + + if (U_FAILURE(*fRB->fStatus)) { + return; + } + + // If there are no forward rules set an error. + // + if (fRB->fForwardTree == nullptr) { + error(U_BRK_RULE_SYNTAX); + return; + } + + // + // Parsing of the input RBBI rules is complete. + // We now have a parse tree for the rule expressions + // and a list of all UnicodeSets that are referenced. + // +#ifdef RBBI_DEBUG + if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "symbols")) {fSymbolTable->rbbiSymtablePrint();} + if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "ptree")) { + RBBIDebugPrintf("Completed Forward Rules Parse Tree...\n"); + RBBINode::printTree(fRB->fForwardTree, true); + RBBIDebugPrintf("\nCompleted Reverse Rules Parse Tree...\n"); + RBBINode::printTree(fRB->fReverseTree, true); + RBBIDebugPrintf("\nCompleted Safe Point Forward Rules Parse Tree...\n"); + RBBINode::printTree(fRB->fSafeFwdTree, true); + RBBIDebugPrintf("\nCompleted Safe Point Reverse Rules Parse Tree...\n"); + RBBINode::printTree(fRB->fSafeRevTree, true); + } +#endif +} + + +//------------------------------------------------------------------------------ +// +// printNodeStack for debugging... +// +//------------------------------------------------------------------------------ +#ifdef RBBI_DEBUG +void RBBIRuleScanner::printNodeStack(const char *title) { + int i; + RBBIDebugPrintf("%s. Dumping node stack...\n", title); + for (i=fNodeStackPtr; i>0; i--) {RBBINode::printTree(fNodeStack[i], true);} +} +#endif + + + + +//------------------------------------------------------------------------------ +// +// pushNewNode create a new RBBINode of the specified type and push it +// onto the stack of nodes. +// +//------------------------------------------------------------------------------ +RBBINode *RBBIRuleScanner::pushNewNode(RBBINode::NodeType t) { + if (U_FAILURE(*fRB->fStatus)) { + return nullptr; + } + if (fNodeStackPtr >= kStackSize - 1) { + error(U_BRK_RULE_SYNTAX); + RBBIDebugPuts("RBBIRuleScanner::pushNewNode - stack overflow."); + return nullptr; + } + fNodeStackPtr++; + fNodeStack[fNodeStackPtr] = new RBBINode(t); + if (fNodeStack[fNodeStackPtr] == nullptr) { + *fRB->fStatus = U_MEMORY_ALLOCATION_ERROR; + } + return fNodeStack[fNodeStackPtr]; +} + + + +//------------------------------------------------------------------------------ +// +// scanSet Construct a UnicodeSet from the text at the current scan +// position. Advance the scan position to the first character +// after the set. +// +// A new RBBI setref node referring to the set is pushed onto the node +// stack. +// +// The scan position is normally under the control of the state machine +// that controls rule parsing. UnicodeSets, however, are parsed by +// the UnicodeSet constructor, not by the RBBI rule parser. +// +//------------------------------------------------------------------------------ +void RBBIRuleScanner::scanSet() { + UnicodeSet *uset; + ParsePosition pos; + int startPos; + int i; + + if (U_FAILURE(*fRB->fStatus)) { + return; + } + + pos.setIndex(fScanIndex); + startPos = fScanIndex; + UErrorCode localStatus = U_ZERO_ERROR; + uset = new UnicodeSet(); + if (uset == nullptr) { + localStatus = U_MEMORY_ALLOCATION_ERROR; + } else { + uset->applyPatternIgnoreSpace(fRB->fRules, pos, fSymbolTable, localStatus); + } + if (U_FAILURE(localStatus)) { + // TODO: Get more accurate position of the error from UnicodeSet's return info. + // UnicodeSet appears to not be reporting correctly at this time. + #ifdef RBBI_DEBUG + RBBIDebugPrintf("UnicodeSet parse position.ErrorIndex = %d\n", pos.getIndex()); + #endif + error(localStatus); + delete uset; + return; + } + + // Verify that the set contains at least one code point. + // + U_ASSERT(uset!=nullptr); + if (uset->isEmpty()) { + // This set is empty. + // Make it an error, because it almost certainly is not what the user wanted. + // Also, avoids having to think about corner cases in the tree manipulation code + // that occurs later on. + error(U_BRK_RULE_EMPTY_SET); + delete uset; + return; + } + + + // Advance the RBBI parse position over the UnicodeSet pattern. + // Don't just set fScanIndex because the line/char positions maintained + // for error reporting would be thrown off. + i = pos.getIndex(); + for (;;) { + if (fNextIndex >= i) { + break; + } + nextCharLL(); + } + + if (U_SUCCESS(*fRB->fStatus)) { + RBBINode *n; + + n = pushNewNode(RBBINode::setRef); + if (U_FAILURE(*fRB->fStatus)) { + return; + } + n->fFirstPos = startPos; + n->fLastPos = fNextIndex; + fRB->fRules.extractBetween(n->fFirstPos, n->fLastPos, n->fText); + // findSetFor() serves several purposes here: + // - Adopts storage for the UnicodeSet, will be responsible for deleting. + // - Maintains collection of all sets in use, needed later for establishing + // character categories for run time engine. + // - Eliminates mulitiple instances of the same set. + // - Creates a new uset node if necessary (if this isn't a duplicate.) + findSetFor(n->fText, n, uset); + } + +} + +int32_t RBBIRuleScanner::numRules() { + return fRuleNum; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ diff --git a/deps/icu-small/source/common/rbbiscan.h b/deps/icu-small/source/common/rbbiscan.h index bf3203880bc031..d503f496962d61 100644 --- a/deps/icu-small/source/common/rbbiscan.h +++ b/deps/icu-small/source/common/rbbiscan.h @@ -1,167 +1,167 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// rbbiscan.h -// -// Copyright (C) 2002-2016, International Business Machines Corporation and others. -// All Rights Reserved. -// -// This file contains declarations for class RBBIRuleScanner -// - - -#ifndef RBBISCAN_H -#define RBBISCAN_H - -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "unicode/rbbi.h" -#include "unicode/uniset.h" -#include "unicode/parseerr.h" -#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. -#include "rbbinode.h" -#include "rbbirpt.h" - -U_NAMESPACE_BEGIN - -class RBBIRuleBuilder; -class RBBISymbolTable; - - -//-------------------------------------------------------------------------------- -// -// class RBBIRuleScanner does the lowest level, character-at-a-time -// scanning of break iterator rules. -// -// The output of the scanner is parse trees for -// the rule expressions and a list of all Unicode Sets -// encountered. -// -//-------------------------------------------------------------------------------- - -class RBBIRuleScanner : public UMemory { -public: - - enum { - kStackSize = 100 // The size of the state stack for - }; // rules parsing. Corresponds roughly - // to the depth of parentheses nesting - // that is allowed in the rules. - - struct RBBIRuleChar { - UChar32 fChar; - UBool fEscaped; - RBBIRuleChar() : fChar(0), fEscaped(false) {} - }; - - RBBIRuleScanner(RBBIRuleBuilder *rb); - - - virtual ~RBBIRuleScanner(); - - void nextChar(RBBIRuleChar &c); // Get the next char from the input stream. - // Return false if at end. - - UBool push(const RBBIRuleChar &c); // Push (unget) one character. - // Only a single character may be pushed. - - void parse(); // Parse the rules, generating two parse - // trees, one each for the forward and - // reverse rules, - // and a list of UnicodeSets encountered. - - int32_t numRules(); // Return the number of rules that have been seen. - - /** - * Return a rules string without unnecessary - * characters. - */ - static UnicodeString stripRules(const UnicodeString &rules); -private: - - UBool doParseActions(int32_t a); - void error(UErrorCode e); // error reporting convenience function. - void fixOpStack(RBBINode::OpPrecedence p); - // a character. - void findSetFor(const UnicodeString &s, RBBINode *node, UnicodeSet *setToAdopt = NULL); - - UChar32 nextCharLL(); -#ifdef RBBI_DEBUG - void printNodeStack(const char *title); -#endif - RBBINode *pushNewNode(RBBINode::NodeType t); - void scanSet(); - - - RBBIRuleBuilder *fRB; // The rule builder that we are part of. - - int32_t fScanIndex; // Index of current character being processed - // in the rule input string. - int32_t fNextIndex; // Index of the next character, which - // is the first character not yet scanned. - UBool fQuoteMode; // Scan is in a 'quoted region' - int32_t fLineNum; // Line number in input file. - int32_t fCharNum; // Char position within the line. - UChar32 fLastChar; // Previous char, needed to count CR-LF - // as a single line, not two. - - RBBIRuleChar fC; // Current char for parse state machine - // processing. - UnicodeString fVarName; // $variableName, valid when we've just - // scanned one. - - RBBIRuleTableEl **fStateTable; // State Transition Table for RBBI Rule - // parsing. index by p[state][char-class] - - uint16_t fStack[kStackSize]; // State stack, holds state pushes - int32_t fStackPtr; // and pops as specified in the state - // transition rules. - - RBBINode *fNodeStack[kStackSize]; // Node stack, holds nodes created - // during the parse of a rule - int32_t fNodeStackPtr; - - - UBool fReverseRule; // True if the rule currently being scanned - // is a reverse direction rule (if it - // starts with a '!') - - UBool fLookAheadRule; // True if the rule includes a '/' - // somewhere within it. - - UBool fNoChainInRule; // True if the current rule starts with a '^'. - - RBBISymbolTable *fSymbolTable; // symbol table, holds definitions of - // $variable symbols. - - UHashtable *fSetTable; // UnicocodeSet hash table, holds indexes to - // the sets created while parsing rules. - // The key is the string used for creating - // the set. - - UnicodeSet fRuleSets[10]; // Unicode Sets that are needed during - // the scanning of RBBI rules. The - // indices for these are assigned by the - // perl script that builds the state tables. - // See rbbirpt.h. - - int32_t fRuleNum; // Counts each rule as it is scanned. - - int32_t fOptionStart; // Input index of start of a !!option - // keyword, while being scanned. - - UnicodeSet *gRuleSet_rule_char; - UnicodeSet *gRuleSet_white_space; - UnicodeSet *gRuleSet_name_char; - UnicodeSet *gRuleSet_name_start_char; - - RBBIRuleScanner(const RBBIRuleScanner &other) = delete; // forbid copying of this class - RBBIRuleScanner &operator=(const RBBIRuleScanner &other) = delete; // forbid copying of this class -}; - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// rbbiscan.h +// +// Copyright (C) 2002-2016, International Business Machines Corporation and others. +// All Rights Reserved. +// +// This file contains declarations for class RBBIRuleScanner +// + + +#ifndef RBBISCAN_H +#define RBBISCAN_H + +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "unicode/rbbi.h" +#include "unicode/uniset.h" +#include "unicode/parseerr.h" +#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. +#include "rbbinode.h" +#include "rbbirpt.h" + +U_NAMESPACE_BEGIN + +class RBBIRuleBuilder; +class RBBISymbolTable; + + +//-------------------------------------------------------------------------------- +// +// class RBBIRuleScanner does the lowest level, character-at-a-time +// scanning of break iterator rules. +// +// The output of the scanner is parse trees for +// the rule expressions and a list of all Unicode Sets +// encountered. +// +//-------------------------------------------------------------------------------- + +class RBBIRuleScanner : public UMemory { +public: + + enum { + kStackSize = 100 // The size of the state stack for + }; // rules parsing. Corresponds roughly + // to the depth of parentheses nesting + // that is allowed in the rules. + + struct RBBIRuleChar { + UChar32 fChar; + UBool fEscaped; + RBBIRuleChar() : fChar(0), fEscaped(false) {} + }; + + RBBIRuleScanner(RBBIRuleBuilder *rb); + + + virtual ~RBBIRuleScanner(); + + void nextChar(RBBIRuleChar &c); // Get the next char from the input stream. + // Return false if at end. + + UBool push(const RBBIRuleChar &c); // Push (unget) one character. + // Only a single character may be pushed. + + void parse(); // Parse the rules, generating two parse + // trees, one each for the forward and + // reverse rules, + // and a list of UnicodeSets encountered. + + int32_t numRules(); // Return the number of rules that have been seen. + + /** + * Return a rules string without unnecessary + * characters. + */ + static UnicodeString stripRules(const UnicodeString &rules); +private: + + UBool doParseActions(int32_t a); + void error(UErrorCode e); // error reporting convenience function. + void fixOpStack(RBBINode::OpPrecedence p); + // a character. + void findSetFor(const UnicodeString &s, RBBINode *node, UnicodeSet *setToAdopt = nullptr); + + UChar32 nextCharLL(); +#ifdef RBBI_DEBUG + void printNodeStack(const char *title); +#endif + RBBINode *pushNewNode(RBBINode::NodeType t); + void scanSet(); + + + RBBIRuleBuilder *fRB; // The rule builder that we are part of. + + int32_t fScanIndex; // Index of current character being processed + // in the rule input string. + int32_t fNextIndex; // Index of the next character, which + // is the first character not yet scanned. + UBool fQuoteMode; // Scan is in a 'quoted region' + int32_t fLineNum; // Line number in input file. + int32_t fCharNum; // Char position within the line. + UChar32 fLastChar; // Previous char, needed to count CR-LF + // as a single line, not two. + + RBBIRuleChar fC; // Current char for parse state machine + // processing. + UnicodeString fVarName; // $variableName, valid when we've just + // scanned one. + + RBBIRuleTableEl **fStateTable; // State Transition Table for RBBI Rule + // parsing. index by p[state][char-class] + + uint16_t fStack[kStackSize]; // State stack, holds state pushes + int32_t fStackPtr; // and pops as specified in the state + // transition rules. + + RBBINode *fNodeStack[kStackSize]; // Node stack, holds nodes created + // during the parse of a rule + int32_t fNodeStackPtr; + + + UBool fReverseRule; // True if the rule currently being scanned + // is a reverse direction rule (if it + // starts with a '!') + + UBool fLookAheadRule; // True if the rule includes a '/' + // somewhere within it. + + UBool fNoChainInRule; // True if the current rule starts with a '^'. + + RBBISymbolTable *fSymbolTable; // symbol table, holds definitions of + // $variable symbols. + + UHashtable *fSetTable; // UnicocodeSet hash table, holds indexes to + // the sets created while parsing rules. + // The key is the string used for creating + // the set. + + UnicodeSet fRuleSets[10]; // Unicode Sets that are needed during + // the scanning of RBBI rules. The + // indices for these are assigned by the + // perl script that builds the state tables. + // See rbbirpt.h. + + int32_t fRuleNum; // Counts each rule as it is scanned. + + int32_t fOptionStart; // Input index of start of a !!option + // keyword, while being scanned. + + UnicodeSet *gRuleSet_rule_char; + UnicodeSet *gRuleSet_white_space; + UnicodeSet *gRuleSet_name_char; + UnicodeSet *gRuleSet_name_start_char; + + RBBIRuleScanner(const RBBIRuleScanner &other) = delete; // forbid copying of this class + RBBIRuleScanner &operator=(const RBBIRuleScanner &other) = delete; // forbid copying of this class +}; + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/common/rbbisetb.cpp b/deps/icu-small/source/common/rbbisetb.cpp index 11c47156d64a69..aef0b5fe0af1d7 100644 --- a/deps/icu-small/source/common/rbbisetb.cpp +++ b/deps/icu-small/source/common/rbbisetb.cpp @@ -1,694 +1,694 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// rbbisetb.cpp -// -/* -*************************************************************************** -* Copyright (C) 2002-2008 International Business Machines Corporation * -* and others. All rights reserved. * -*************************************************************************** -*/ -// -// RBBISetBuilder Handles processing of Unicode Sets from RBBI rules -// (part of the rule building process.) -// -// Starting with the rules parse tree from the scanner, -// -// - Enumerate the set of UnicodeSets that are referenced -// by the RBBI rules. -// - compute a set of non-overlapping character ranges -// with all characters within a range belonging to the same -// set of input unicode sets. -// - Derive a set of non-overlapping UnicodeSet (like things) -// that will correspond to columns in the state table for -// the RBBI execution engine. All characters within one -// of these sets belong to the same set of the original -// UnicodeSets from the user's rules. -// - construct the trie table that maps input characters -// to the index of the matching non-overlapping set of set from -// the previous step. -// - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/uniset.h" -#include "uvector.h" -#include "uassert.h" -#include "cmemory.h" -#include "cstring.h" - -#include "rbbisetb.h" -#include "rbbinode.h" - -U_NAMESPACE_BEGIN - -const int32_t kMaxCharCategoriesFor8BitsTrie = 255; -//------------------------------------------------------------------------ -// -// Constructor -// -//------------------------------------------------------------------------ -RBBISetBuilder::RBBISetBuilder(RBBIRuleBuilder *rb) -{ - fRB = rb; - fStatus = rb->fStatus; - fRangeList = nullptr; - fMutableTrie = nullptr; - fTrie = nullptr; - fTrieSize = 0; - fGroupCount = 0; - fSawBOF = false; -} - - -//------------------------------------------------------------------------ -// -// Destructor -// -//------------------------------------------------------------------------ -RBBISetBuilder::~RBBISetBuilder() -{ - RangeDescriptor *nextRangeDesc; - - // Walk through & delete the linked list of RangeDescriptors - for (nextRangeDesc = fRangeList; nextRangeDesc!=NULL;) { - RangeDescriptor *r = nextRangeDesc; - nextRangeDesc = r->fNext; - delete r; - } - - ucptrie_close(fTrie); - umutablecptrie_close(fMutableTrie); -} - - - - -//------------------------------------------------------------------------ -// -// build Build the list of non-overlapping character ranges -// from the Unicode Sets. -// -//------------------------------------------------------------------------ -void RBBISetBuilder::buildRanges() { - RBBINode *usetNode; - RangeDescriptor *rlRange; - - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "usets")) {printSets();} - - // - // Initialize the process by creating a single range encompassing all characters - // that is in no sets. - // - fRangeList = new RangeDescriptor(*fStatus); // will check for status here - if (fRangeList == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - fRangeList->fStartChar = 0; - fRangeList->fEndChar = 0x10ffff; - - if (U_FAILURE(*fStatus)) { - return; - } - - // - // Find the set of non-overlapping ranges of characters - // - int ni; - for (ni=0; ; ni++) { // Loop over each of the UnicodeSets encountered in the input rules - usetNode = (RBBINode *)this->fRB->fUSetNodes->elementAt(ni); - if (usetNode==NULL) { - break; - } - - UnicodeSet *inputSet = usetNode->fInputSet; - int32_t inputSetRangeCount = inputSet->getRangeCount(); - int inputSetRangeIndex = 0; - rlRange = fRangeList; - - for (;;) { - if (inputSetRangeIndex >= inputSetRangeCount) { - break; - } - UChar32 inputSetRangeBegin = inputSet->getRangeStart(inputSetRangeIndex); - UChar32 inputSetRangeEnd = inputSet->getRangeEnd(inputSetRangeIndex); - - // skip over ranges from the range list that are completely - // below the current range from the input unicode set. - while (rlRange->fEndChar < inputSetRangeBegin) { - rlRange = rlRange->fNext; - } - - // If the start of the range from the range list is before with - // the start of the range from the unicode set, split the range list range - // in two, with one part being before (wholly outside of) the unicode set - // and the other containing the rest. - // Then continue the loop; the post-split current range will then be skipped - // over - if (rlRange->fStartChar < inputSetRangeBegin) { - rlRange->split(inputSetRangeBegin, *fStatus); - if (U_FAILURE(*fStatus)) { - return; - } - continue; - } - - // Same thing at the end of the ranges... - // If the end of the range from the range list doesn't coincide with - // the end of the range from the unicode set, split the range list - // range in two. The first part of the split range will be - // wholly inside the Unicode set. - if (rlRange->fEndChar > inputSetRangeEnd) { - rlRange->split(inputSetRangeEnd+1, *fStatus); - if (U_FAILURE(*fStatus)) { - return; - } - } - - // The current rlRange is now entirely within the UnicodeSet range. - // Add this unicode set to the list of sets for this rlRange - if (rlRange->fIncludesSets->indexOf(usetNode) == -1) { - rlRange->fIncludesSets->addElement(usetNode, *fStatus); - if (U_FAILURE(*fStatus)) { - return; - } - } - - // Advance over ranges that we are finished with. - if (inputSetRangeEnd == rlRange->fEndChar) { - inputSetRangeIndex++; - } - rlRange = rlRange->fNext; - } - } - - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "range")) { printRanges();} - - // - // Group the above ranges, with each group consisting of one or more - // ranges that are in exactly the same set of original UnicodeSets. - // The groups are numbered, and these group numbers are the set of - // input symbols recognized by the run-time state machine. - // - // Numbering: # 0 (state table column 0) is unused. - // # 1 is reserved - table column 1 is for end-of-input - // # 2 is reserved - table column 2 is for beginning-of-input - // # 3 is the first range list. - // - RangeDescriptor *rlSearchRange; - int32_t dictGroupCount = 0; - - for (rlRange = fRangeList; rlRange!=nullptr; rlRange=rlRange->fNext) { - for (rlSearchRange=fRangeList; rlSearchRange != rlRange; rlSearchRange=rlSearchRange->fNext) { - if (rlRange->fIncludesSets->equals(*rlSearchRange->fIncludesSets)) { - rlRange->fNum = rlSearchRange->fNum; - rlRange->fIncludesDict = rlSearchRange->fIncludesDict; - break; - } - } - if (rlRange->fNum == 0) { - rlRange->fFirstInGroup = true; - if (rlRange->isDictionaryRange()) { - rlRange->fNum = ++dictGroupCount; - rlRange->fIncludesDict = true; - } else { - fGroupCount++; - rlRange->fNum = fGroupCount+2; - addValToSets(rlRange->fIncludesSets, rlRange->fNum); - } - } - } - - // Move the character category numbers for any dictionary ranges up, so that they - // immediately follow the non-dictionary ranges. - - fDictCategoriesStart = fGroupCount + 3; - for (rlRange = fRangeList; rlRange!=nullptr; rlRange=rlRange->fNext) { - if (rlRange->fIncludesDict) { - rlRange->fNum += fDictCategoriesStart - 1; - if (rlRange->fFirstInGroup) { - addValToSets(rlRange->fIncludesSets, rlRange->fNum); - } - } - } - fGroupCount += dictGroupCount; - - - // Handle input sets that contain the special string {eof}. - // Column 1 of the state table is reserved for EOF on input. - // Column 2 is reserved for before-the-start-input. - // (This column can be optimized away later if there are no rule - // references to {bof}.) - // Add this column value (1 or 2) to the equivalent expression - // subtree for each UnicodeSet that contains the string {eof} - // Because {bof} and {eof} are not characters in the normal sense, - // they don't affect the computation of the ranges or TRIE. - - UnicodeString eofString(u"eof"); - UnicodeString bofString(u"bof"); - for (ni=0; ; ni++) { // Loop over each of the UnicodeSets encountered in the input rules - usetNode = (RBBINode *)this->fRB->fUSetNodes->elementAt(ni); - if (usetNode==NULL) { - break; - } - UnicodeSet *inputSet = usetNode->fInputSet; - if (inputSet->contains(eofString)) { - addValToSet(usetNode, 1); - } - if (inputSet->contains(bofString)) { - addValToSet(usetNode, 2); - fSawBOF = true; - } - } - - - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "rgroup")) {printRangeGroups();} - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "esets")) {printSets();} -} - - -// -// Build the Trie table for mapping UChar32 values to the corresponding -// range group number. -// -void RBBISetBuilder::buildTrie() { - fMutableTrie = umutablecptrie_open( - 0, // Initial value for all code points. - 0, // Error value for out-of-range input. - fStatus); - - for (RangeDescriptor *range = fRangeList; range!=nullptr && U_SUCCESS(*fStatus); range=range->fNext) { - umutablecptrie_setRange(fMutableTrie, - range->fStartChar, // Range start - range->fEndChar, // Range end (inclusive) - range->fNum, // value for range - fStatus); - } -} - - -void RBBISetBuilder::mergeCategories(IntPair categories) { - U_ASSERT(categories.first >= 1); - U_ASSERT(categories.second > categories.first); - U_ASSERT((categories.first < fDictCategoriesStart && categories.second < fDictCategoriesStart) || - (categories.first >= fDictCategoriesStart && categories.second >= fDictCategoriesStart)); - - for (RangeDescriptor *rd = fRangeList; rd != nullptr; rd = rd->fNext) { - int32_t rangeNum = rd->fNum; - if (rangeNum == categories.second) { - rd->fNum = categories.first; - } else if (rangeNum > categories.second) { - rd->fNum--; - } - } - --fGroupCount; - if (categories.second <= fDictCategoriesStart) { - --fDictCategoriesStart; - } -} - - -//----------------------------------------------------------------------------------- -// -// getTrieSize() Return the size that will be required to serialize the Trie. -// -//----------------------------------------------------------------------------------- -int32_t RBBISetBuilder::getTrieSize() { - if (U_FAILURE(*fStatus)) { - return 0; - } - if (fTrie == nullptr) { - bool use8Bits = getNumCharCategories() <= kMaxCharCategoriesFor8BitsTrie; - fTrie = umutablecptrie_buildImmutable( - fMutableTrie, - UCPTRIE_TYPE_FAST, - use8Bits ? UCPTRIE_VALUE_BITS_8 : UCPTRIE_VALUE_BITS_16, - fStatus); - fTrieSize = ucptrie_toBinary(fTrie, nullptr, 0, fStatus); - if (*fStatus == U_BUFFER_OVERFLOW_ERROR) { - *fStatus = U_ZERO_ERROR; - } - } - return fTrieSize; -} - - -//----------------------------------------------------------------------------------- -// -// serializeTrie() Put the serialized trie at the specified address. -// Trust the caller to have given us enough memory. -// getTrieSize() MUST be called first. -// -//----------------------------------------------------------------------------------- -void RBBISetBuilder::serializeTrie(uint8_t *where) { - ucptrie_toBinary(fTrie, - where, // Buffer - fTrieSize, // Capacity - fStatus); -} - -//------------------------------------------------------------------------ -// -// addValToSets Add a runtime-mapped input value to each uset from a -// list of uset nodes. (val corresponds to a state table column.) -// For each of the original Unicode sets - which correspond -// directly to uset nodes - a logically equivalent expression -// is constructed in terms of the remapped runtime input -// symbol set. This function adds one runtime input symbol to -// a list of sets. -// -// The "logically equivalent expression" is the tree for an -// or-ing together of all of the symbols that go into the set. -// -//------------------------------------------------------------------------ -void RBBISetBuilder::addValToSets(UVector *sets, uint32_t val) { - int32_t ix; - - for (ix=0; ixsize(); ix++) { - RBBINode *usetNode = (RBBINode *)sets->elementAt(ix); - addValToSet(usetNode, val); - } -} - -void RBBISetBuilder::addValToSet(RBBINode *usetNode, uint32_t val) { - RBBINode *leafNode = new RBBINode(RBBINode::leafChar); - if (leafNode == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - leafNode->fVal = (unsigned short)val; - if (usetNode->fLeftChild == NULL) { - usetNode->fLeftChild = leafNode; - leafNode->fParent = usetNode; - } else { - // There are already input symbols present for this set. - // Set up an OR node, with the previous stuff as the left child - // and the new value as the right child. - RBBINode *orNode = new RBBINode(RBBINode::opOr); - if (orNode == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - orNode->fLeftChild = usetNode->fLeftChild; - orNode->fRightChild = leafNode; - orNode->fLeftChild->fParent = orNode; - orNode->fRightChild->fParent = orNode; - usetNode->fLeftChild = orNode; - orNode->fParent = usetNode; - } -} - - -//------------------------------------------------------------------------ -// -// getNumCharCategories -// -//------------------------------------------------------------------------ -int32_t RBBISetBuilder::getNumCharCategories() const { - return fGroupCount + 3; -} - - -//------------------------------------------------------------------------ -// -// getDictCategoriesStart -// -//------------------------------------------------------------------------ -int32_t RBBISetBuilder::getDictCategoriesStart() const { - return fDictCategoriesStart; -} - - -//------------------------------------------------------------------------ -// -// sawBOF -// -//------------------------------------------------------------------------ -UBool RBBISetBuilder::sawBOF() const { - return fSawBOF; -} - - -//------------------------------------------------------------------------ -// -// getFirstChar Given a runtime RBBI character category, find -// the first UChar32 that is in the set of chars -// in the category. -//------------------------------------------------------------------------ -UChar32 RBBISetBuilder::getFirstChar(int32_t category) const { - RangeDescriptor *rlRange; - UChar32 retVal = (UChar32)-1; - for (rlRange = fRangeList; rlRange!=nullptr; rlRange=rlRange->fNext) { - if (rlRange->fNum == category) { - retVal = rlRange->fStartChar; - break; - } - } - return retVal; -} - - -//------------------------------------------------------------------------ -// -// printRanges A debugging function. -// dump out all of the range definitions. -// -//------------------------------------------------------------------------ -#ifdef RBBI_DEBUG -void RBBISetBuilder::printRanges() { - RangeDescriptor *rlRange; - int i; - - RBBIDebugPrintf("\n\n Nonoverlapping Ranges ...\n"); - for (rlRange = fRangeList; rlRange!=nullptr; rlRange=rlRange->fNext) { - RBBIDebugPrintf("%4x-%4x ", rlRange->fStartChar, rlRange->fEndChar); - - for (i=0; ifIncludesSets->size(); i++) { - RBBINode *usetNode = (RBBINode *)rlRange->fIncludesSets->elementAt(i); - UnicodeString setName {u"anon"}; - RBBINode *setRef = usetNode->fParent; - if (setRef != nullptr) { - RBBINode *varRef = setRef->fParent; - if (varRef != nullptr && varRef->fType == RBBINode::varRef) { - setName = varRef->fText; - } - } - RBBI_DEBUG_printUnicodeString(setName); RBBIDebugPrintf(" "); - } - RBBIDebugPrintf("\n"); - } -} -#endif - - -//------------------------------------------------------------------------ -// -// printRangeGroups A debugging function. -// dump out all of the range groups. -// -//------------------------------------------------------------------------ -#ifdef RBBI_DEBUG -void RBBISetBuilder::printRangeGroups() { - int i; - - RBBIDebugPrintf("\nRanges grouped by Unicode Set Membership...\n"); - for (RangeDescriptor *rlRange = fRangeList; rlRange!=nullptr; rlRange=rlRange->fNext) { - if (rlRange->fFirstInGroup) { - int groupNum = rlRange->fNum; - RBBIDebugPrintf("%2i ", groupNum); - - if (groupNum >= fDictCategoriesStart) { RBBIDebugPrintf(" ");} - - for (i=0; ifIncludesSets->size(); i++) { - RBBINode *usetNode = (RBBINode *)rlRange->fIncludesSets->elementAt(i); - UnicodeString setName = UNICODE_STRING("anon", 4); - RBBINode *setRef = usetNode->fParent; - if (setRef != NULL) { - RBBINode *varRef = setRef->fParent; - if (varRef != NULL && varRef->fType == RBBINode::varRef) { - setName = varRef->fText; - } - } - RBBI_DEBUG_printUnicodeString(setName); RBBIDebugPrintf(" "); - } - - i = 0; - for (RangeDescriptor *tRange = rlRange; tRange != nullptr; tRange = tRange->fNext) { - if (tRange->fNum == rlRange->fNum) { - if (i++ % 5 == 0) { - RBBIDebugPrintf("\n "); - } - RBBIDebugPrintf(" %05x-%05x", tRange->fStartChar, tRange->fEndChar); - } - } - RBBIDebugPrintf("\n"); - } - } - RBBIDebugPrintf("\n"); -} -#endif - - -//------------------------------------------------------------------------ -// -// printSets A debugging function. -// dump out all of the set definitions. -// -//------------------------------------------------------------------------ -#ifdef RBBI_DEBUG -void RBBISetBuilder::printSets() { - int i; - - RBBIDebugPrintf("\n\nUnicode Sets List\n------------------\n"); - for (i=0; ; i++) { - RBBINode *usetNode; - RBBINode *setRef; - RBBINode *varRef; - UnicodeString setName; - - usetNode = (RBBINode *)fRB->fUSetNodes->elementAt(i); - if (usetNode == NULL) { - break; - } - - RBBIDebugPrintf("%3d ", i); - setName = UNICODE_STRING("anonymous", 9); - setRef = usetNode->fParent; - if (setRef != NULL) { - varRef = setRef->fParent; - if (varRef != NULL && varRef->fType == RBBINode::varRef) { - setName = varRef->fText; - } - } - RBBI_DEBUG_printUnicodeString(setName); - RBBIDebugPrintf(" "); - RBBI_DEBUG_printUnicodeString(usetNode->fText); - RBBIDebugPrintf("\n"); - if (usetNode->fLeftChild != NULL) { - RBBINode::printTree(usetNode->fLeftChild, true); - } - } - RBBIDebugPrintf("\n"); -} -#endif - - - -//------------------------------------------------------------------------------------- -// -// RangeDescriptor copy constructor -// -//------------------------------------------------------------------------------------- - -RangeDescriptor::RangeDescriptor(const RangeDescriptor &other, UErrorCode &status) : - fStartChar(other.fStartChar), fEndChar {other.fEndChar}, fNum {other.fNum}, - fIncludesDict{other.fIncludesDict}, fFirstInGroup{other.fFirstInGroup} { - - if (U_FAILURE(status)) { - return; - } - fIncludesSets = new UVector(status); - if (this->fIncludesSets == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - if (U_FAILURE(status)) { - return; - } - - for (int32_t i=0; isize(); i++) { - this->fIncludesSets->addElement(other.fIncludesSets->elementAt(i), status); - } -} - - -//------------------------------------------------------------------------------------- -// -// RangeDesriptor default constructor -// -//------------------------------------------------------------------------------------- -RangeDescriptor::RangeDescriptor(UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - fIncludesSets = new UVector(status); - if (fIncludesSets == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } -} - - -//------------------------------------------------------------------------------------- -// -// RangeDesriptor Destructor -// -//------------------------------------------------------------------------------------- -RangeDescriptor::~RangeDescriptor() { - delete fIncludesSets; - fIncludesSets = nullptr; -} - -//------------------------------------------------------------------------------------- -// -// RangeDesriptor::split() -// -//------------------------------------------------------------------------------------- -void RangeDescriptor::split(UChar32 where, UErrorCode &status) { - U_ASSERT(where>fStartChar && where<=fEndChar); - RangeDescriptor *nr = new RangeDescriptor(*this, status); - if(nr == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (U_FAILURE(status)) { - delete nr; - return; - } - // RangeDescriptor copy constructor copies all fields. - // Only need to update those that are different after the split. - nr->fStartChar = where; - this->fEndChar = where-1; - nr->fNext = this->fNext; - this->fNext = nr; -} - - -//------------------------------------------------------------------------------------- -// -// RangeDescriptor::isDictionaryRange -// -// Test whether this range includes characters from -// the original Unicode Set named "dictionary". -// -// This function looks through the Unicode Sets that -// the range includes, checking for one named "dictionary" -// -// TODO: a faster way would be to find the set node for -// "dictionary" just once, rather than looking it -// up by name every time. -// -//------------------------------------------------------------------------------------- -bool RangeDescriptor::isDictionaryRange() { - static const char16_t *dictionary = u"dictionary"; - for (int32_t i=0; isize(); i++) { - RBBINode *usetNode = (RBBINode *)fIncludesSets->elementAt(i); - RBBINode *setRef = usetNode->fParent; - if (setRef != nullptr) { - RBBINode *varRef = setRef->fParent; - if (varRef && varRef->fType == RBBINode::varRef) { - const UnicodeString *setName = &varRef->fText; - if (setName->compare(dictionary, -1) == 0) { - return true; - } - } - } - } - return false; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// rbbisetb.cpp +// +/* +*************************************************************************** +* Copyright (C) 2002-2008 International Business Machines Corporation * +* and others. All rights reserved. * +*************************************************************************** +*/ +// +// RBBISetBuilder Handles processing of Unicode Sets from RBBI rules +// (part of the rule building process.) +// +// Starting with the rules parse tree from the scanner, +// +// - Enumerate the set of UnicodeSets that are referenced +// by the RBBI rules. +// - compute a set of non-overlapping character ranges +// with all characters within a range belonging to the same +// set of input unicode sets. +// - Derive a set of non-overlapping UnicodeSet (like things) +// that will correspond to columns in the state table for +// the RBBI execution engine. All characters within one +// of these sets belong to the same set of the original +// UnicodeSets from the user's rules. +// - construct the trie table that maps input characters +// to the index of the matching non-overlapping set of set from +// the previous step. +// + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/uniset.h" +#include "uvector.h" +#include "uassert.h" +#include "cmemory.h" +#include "cstring.h" + +#include "rbbisetb.h" +#include "rbbinode.h" + +U_NAMESPACE_BEGIN + +const int32_t kMaxCharCategoriesFor8BitsTrie = 255; +//------------------------------------------------------------------------ +// +// Constructor +// +//------------------------------------------------------------------------ +RBBISetBuilder::RBBISetBuilder(RBBIRuleBuilder *rb) +{ + fRB = rb; + fStatus = rb->fStatus; + fRangeList = nullptr; + fMutableTrie = nullptr; + fTrie = nullptr; + fTrieSize = 0; + fGroupCount = 0; + fSawBOF = false; +} + + +//------------------------------------------------------------------------ +// +// Destructor +// +//------------------------------------------------------------------------ +RBBISetBuilder::~RBBISetBuilder() +{ + RangeDescriptor *nextRangeDesc; + + // Walk through & delete the linked list of RangeDescriptors + for (nextRangeDesc = fRangeList; nextRangeDesc!=nullptr;) { + RangeDescriptor *r = nextRangeDesc; + nextRangeDesc = r->fNext; + delete r; + } + + ucptrie_close(fTrie); + umutablecptrie_close(fMutableTrie); +} + + + + +//------------------------------------------------------------------------ +// +// build Build the list of non-overlapping character ranges +// from the Unicode Sets. +// +//------------------------------------------------------------------------ +void RBBISetBuilder::buildRanges() { + RBBINode *usetNode; + RangeDescriptor *rlRange; + + if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "usets")) {printSets();} + + // + // Initialize the process by creating a single range encompassing all characters + // that is in no sets. + // + fRangeList = new RangeDescriptor(*fStatus); // will check for status here + if (fRangeList == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + return; + } + fRangeList->fStartChar = 0; + fRangeList->fEndChar = 0x10ffff; + + if (U_FAILURE(*fStatus)) { + return; + } + + // + // Find the set of non-overlapping ranges of characters + // + int ni; + for (ni=0; ; ni++) { // Loop over each of the UnicodeSets encountered in the input rules + usetNode = (RBBINode *)this->fRB->fUSetNodes->elementAt(ni); + if (usetNode==nullptr) { + break; + } + + UnicodeSet *inputSet = usetNode->fInputSet; + int32_t inputSetRangeCount = inputSet->getRangeCount(); + int inputSetRangeIndex = 0; + rlRange = fRangeList; + + for (;;) { + if (inputSetRangeIndex >= inputSetRangeCount) { + break; + } + UChar32 inputSetRangeBegin = inputSet->getRangeStart(inputSetRangeIndex); + UChar32 inputSetRangeEnd = inputSet->getRangeEnd(inputSetRangeIndex); + + // skip over ranges from the range list that are completely + // below the current range from the input unicode set. + while (rlRange->fEndChar < inputSetRangeBegin) { + rlRange = rlRange->fNext; + } + + // If the start of the range from the range list is before with + // the start of the range from the unicode set, split the range list range + // in two, with one part being before (wholly outside of) the unicode set + // and the other containing the rest. + // Then continue the loop; the post-split current range will then be skipped + // over + if (rlRange->fStartChar < inputSetRangeBegin) { + rlRange->split(inputSetRangeBegin, *fStatus); + if (U_FAILURE(*fStatus)) { + return; + } + continue; + } + + // Same thing at the end of the ranges... + // If the end of the range from the range list doesn't coincide with + // the end of the range from the unicode set, split the range list + // range in two. The first part of the split range will be + // wholly inside the Unicode set. + if (rlRange->fEndChar > inputSetRangeEnd) { + rlRange->split(inputSetRangeEnd+1, *fStatus); + if (U_FAILURE(*fStatus)) { + return; + } + } + + // The current rlRange is now entirely within the UnicodeSet range. + // Add this unicode set to the list of sets for this rlRange + if (rlRange->fIncludesSets->indexOf(usetNode) == -1) { + rlRange->fIncludesSets->addElement(usetNode, *fStatus); + if (U_FAILURE(*fStatus)) { + return; + } + } + + // Advance over ranges that we are finished with. + if (inputSetRangeEnd == rlRange->fEndChar) { + inputSetRangeIndex++; + } + rlRange = rlRange->fNext; + } + } + + if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "range")) { printRanges();} + + // + // Group the above ranges, with each group consisting of one or more + // ranges that are in exactly the same set of original UnicodeSets. + // The groups are numbered, and these group numbers are the set of + // input symbols recognized by the run-time state machine. + // + // Numbering: # 0 (state table column 0) is unused. + // # 1 is reserved - table column 1 is for end-of-input + // # 2 is reserved - table column 2 is for beginning-of-input + // # 3 is the first range list. + // + RangeDescriptor *rlSearchRange; + int32_t dictGroupCount = 0; + + for (rlRange = fRangeList; rlRange!=nullptr; rlRange=rlRange->fNext) { + for (rlSearchRange=fRangeList; rlSearchRange != rlRange; rlSearchRange=rlSearchRange->fNext) { + if (rlRange->fIncludesSets->equals(*rlSearchRange->fIncludesSets)) { + rlRange->fNum = rlSearchRange->fNum; + rlRange->fIncludesDict = rlSearchRange->fIncludesDict; + break; + } + } + if (rlRange->fNum == 0) { + rlRange->fFirstInGroup = true; + if (rlRange->isDictionaryRange()) { + rlRange->fNum = ++dictGroupCount; + rlRange->fIncludesDict = true; + } else { + fGroupCount++; + rlRange->fNum = fGroupCount+2; + addValToSets(rlRange->fIncludesSets, rlRange->fNum); + } + } + } + + // Move the character category numbers for any dictionary ranges up, so that they + // immediately follow the non-dictionary ranges. + + fDictCategoriesStart = fGroupCount + 3; + for (rlRange = fRangeList; rlRange!=nullptr; rlRange=rlRange->fNext) { + if (rlRange->fIncludesDict) { + rlRange->fNum += fDictCategoriesStart - 1; + if (rlRange->fFirstInGroup) { + addValToSets(rlRange->fIncludesSets, rlRange->fNum); + } + } + } + fGroupCount += dictGroupCount; + + + // Handle input sets that contain the special string {eof}. + // Column 1 of the state table is reserved for EOF on input. + // Column 2 is reserved for before-the-start-input. + // (This column can be optimized away later if there are no rule + // references to {bof}.) + // Add this column value (1 or 2) to the equivalent expression + // subtree for each UnicodeSet that contains the string {eof} + // Because {bof} and {eof} are not characters in the normal sense, + // they don't affect the computation of the ranges or TRIE. + + UnicodeString eofString(u"eof"); + UnicodeString bofString(u"bof"); + for (ni=0; ; ni++) { // Loop over each of the UnicodeSets encountered in the input rules + usetNode = (RBBINode *)this->fRB->fUSetNodes->elementAt(ni); + if (usetNode==nullptr) { + break; + } + UnicodeSet *inputSet = usetNode->fInputSet; + if (inputSet->contains(eofString)) { + addValToSet(usetNode, 1); + } + if (inputSet->contains(bofString)) { + addValToSet(usetNode, 2); + fSawBOF = true; + } + } + + + if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "rgroup")) {printRangeGroups();} + if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "esets")) {printSets();} +} + + +// +// Build the Trie table for mapping UChar32 values to the corresponding +// range group number. +// +void RBBISetBuilder::buildTrie() { + fMutableTrie = umutablecptrie_open( + 0, // Initial value for all code points. + 0, // Error value for out-of-range input. + fStatus); + + for (RangeDescriptor *range = fRangeList; range!=nullptr && U_SUCCESS(*fStatus); range=range->fNext) { + umutablecptrie_setRange(fMutableTrie, + range->fStartChar, // Range start + range->fEndChar, // Range end (inclusive) + range->fNum, // value for range + fStatus); + } +} + + +void RBBISetBuilder::mergeCategories(IntPair categories) { + U_ASSERT(categories.first >= 1); + U_ASSERT(categories.second > categories.first); + U_ASSERT((categories.first < fDictCategoriesStart && categories.second < fDictCategoriesStart) || + (categories.first >= fDictCategoriesStart && categories.second >= fDictCategoriesStart)); + + for (RangeDescriptor *rd = fRangeList; rd != nullptr; rd = rd->fNext) { + int32_t rangeNum = rd->fNum; + if (rangeNum == categories.second) { + rd->fNum = categories.first; + } else if (rangeNum > categories.second) { + rd->fNum--; + } + } + --fGroupCount; + if (categories.second <= fDictCategoriesStart) { + --fDictCategoriesStart; + } +} + + +//----------------------------------------------------------------------------------- +// +// getTrieSize() Return the size that will be required to serialize the Trie. +// +//----------------------------------------------------------------------------------- +int32_t RBBISetBuilder::getTrieSize() { + if (U_FAILURE(*fStatus)) { + return 0; + } + if (fTrie == nullptr) { + bool use8Bits = getNumCharCategories() <= kMaxCharCategoriesFor8BitsTrie; + fTrie = umutablecptrie_buildImmutable( + fMutableTrie, + UCPTRIE_TYPE_FAST, + use8Bits ? UCPTRIE_VALUE_BITS_8 : UCPTRIE_VALUE_BITS_16, + fStatus); + fTrieSize = ucptrie_toBinary(fTrie, nullptr, 0, fStatus); + if (*fStatus == U_BUFFER_OVERFLOW_ERROR) { + *fStatus = U_ZERO_ERROR; + } + } + return fTrieSize; +} + + +//----------------------------------------------------------------------------------- +// +// serializeTrie() Put the serialized trie at the specified address. +// Trust the caller to have given us enough memory. +// getTrieSize() MUST be called first. +// +//----------------------------------------------------------------------------------- +void RBBISetBuilder::serializeTrie(uint8_t *where) { + ucptrie_toBinary(fTrie, + where, // Buffer + fTrieSize, // Capacity + fStatus); +} + +//------------------------------------------------------------------------ +// +// addValToSets Add a runtime-mapped input value to each uset from a +// list of uset nodes. (val corresponds to a state table column.) +// For each of the original Unicode sets - which correspond +// directly to uset nodes - a logically equivalent expression +// is constructed in terms of the remapped runtime input +// symbol set. This function adds one runtime input symbol to +// a list of sets. +// +// The "logically equivalent expression" is the tree for an +// or-ing together of all of the symbols that go into the set. +// +//------------------------------------------------------------------------ +void RBBISetBuilder::addValToSets(UVector *sets, uint32_t val) { + int32_t ix; + + for (ix=0; ixsize(); ix++) { + RBBINode *usetNode = (RBBINode *)sets->elementAt(ix); + addValToSet(usetNode, val); + } +} + +void RBBISetBuilder::addValToSet(RBBINode *usetNode, uint32_t val) { + RBBINode *leafNode = new RBBINode(RBBINode::leafChar); + if (leafNode == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + return; + } + leafNode->fVal = (unsigned short)val; + if (usetNode->fLeftChild == nullptr) { + usetNode->fLeftChild = leafNode; + leafNode->fParent = usetNode; + } else { + // There are already input symbols present for this set. + // Set up an OR node, with the previous stuff as the left child + // and the new value as the right child. + RBBINode *orNode = new RBBINode(RBBINode::opOr); + if (orNode == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + return; + } + orNode->fLeftChild = usetNode->fLeftChild; + orNode->fRightChild = leafNode; + orNode->fLeftChild->fParent = orNode; + orNode->fRightChild->fParent = orNode; + usetNode->fLeftChild = orNode; + orNode->fParent = usetNode; + } +} + + +//------------------------------------------------------------------------ +// +// getNumCharCategories +// +//------------------------------------------------------------------------ +int32_t RBBISetBuilder::getNumCharCategories() const { + return fGroupCount + 3; +} + + +//------------------------------------------------------------------------ +// +// getDictCategoriesStart +// +//------------------------------------------------------------------------ +int32_t RBBISetBuilder::getDictCategoriesStart() const { + return fDictCategoriesStart; +} + + +//------------------------------------------------------------------------ +// +// sawBOF +// +//------------------------------------------------------------------------ +UBool RBBISetBuilder::sawBOF() const { + return fSawBOF; +} + + +//------------------------------------------------------------------------ +// +// getFirstChar Given a runtime RBBI character category, find +// the first UChar32 that is in the set of chars +// in the category. +//------------------------------------------------------------------------ +UChar32 RBBISetBuilder::getFirstChar(int32_t category) const { + RangeDescriptor *rlRange; + UChar32 retVal = (UChar32)-1; + for (rlRange = fRangeList; rlRange!=nullptr; rlRange=rlRange->fNext) { + if (rlRange->fNum == category) { + retVal = rlRange->fStartChar; + break; + } + } + return retVal; +} + + +//------------------------------------------------------------------------ +// +// printRanges A debugging function. +// dump out all of the range definitions. +// +//------------------------------------------------------------------------ +#ifdef RBBI_DEBUG +void RBBISetBuilder::printRanges() { + RangeDescriptor *rlRange; + int i; + + RBBIDebugPrintf("\n\n Nonoverlapping Ranges ...\n"); + for (rlRange = fRangeList; rlRange!=nullptr; rlRange=rlRange->fNext) { + RBBIDebugPrintf("%4x-%4x ", rlRange->fStartChar, rlRange->fEndChar); + + for (i=0; ifIncludesSets->size(); i++) { + RBBINode *usetNode = (RBBINode *)rlRange->fIncludesSets->elementAt(i); + UnicodeString setName {u"anon"}; + RBBINode *setRef = usetNode->fParent; + if (setRef != nullptr) { + RBBINode *varRef = setRef->fParent; + if (varRef != nullptr && varRef->fType == RBBINode::varRef) { + setName = varRef->fText; + } + } + RBBI_DEBUG_printUnicodeString(setName); RBBIDebugPrintf(" "); + } + RBBIDebugPrintf("\n"); + } +} +#endif + + +//------------------------------------------------------------------------ +// +// printRangeGroups A debugging function. +// dump out all of the range groups. +// +//------------------------------------------------------------------------ +#ifdef RBBI_DEBUG +void RBBISetBuilder::printRangeGroups() { + int i; + + RBBIDebugPrintf("\nRanges grouped by Unicode Set Membership...\n"); + for (RangeDescriptor *rlRange = fRangeList; rlRange!=nullptr; rlRange=rlRange->fNext) { + if (rlRange->fFirstInGroup) { + int groupNum = rlRange->fNum; + RBBIDebugPrintf("%2i ", groupNum); + + if (groupNum >= fDictCategoriesStart) { RBBIDebugPrintf(" ");} + + for (i=0; ifIncludesSets->size(); i++) { + RBBINode *usetNode = (RBBINode *)rlRange->fIncludesSets->elementAt(i); + UnicodeString setName = UNICODE_STRING("anon", 4); + RBBINode *setRef = usetNode->fParent; + if (setRef != nullptr) { + RBBINode *varRef = setRef->fParent; + if (varRef != nullptr && varRef->fType == RBBINode::varRef) { + setName = varRef->fText; + } + } + RBBI_DEBUG_printUnicodeString(setName); RBBIDebugPrintf(" "); + } + + i = 0; + for (RangeDescriptor *tRange = rlRange; tRange != nullptr; tRange = tRange->fNext) { + if (tRange->fNum == rlRange->fNum) { + if (i++ % 5 == 0) { + RBBIDebugPrintf("\n "); + } + RBBIDebugPrintf(" %05x-%05x", tRange->fStartChar, tRange->fEndChar); + } + } + RBBIDebugPrintf("\n"); + } + } + RBBIDebugPrintf("\n"); +} +#endif + + +//------------------------------------------------------------------------ +// +// printSets A debugging function. +// dump out all of the set definitions. +// +//------------------------------------------------------------------------ +#ifdef RBBI_DEBUG +void RBBISetBuilder::printSets() { + int i; + + RBBIDebugPrintf("\n\nUnicode Sets List\n------------------\n"); + for (i=0; ; i++) { + RBBINode *usetNode; + RBBINode *setRef; + RBBINode *varRef; + UnicodeString setName; + + usetNode = (RBBINode *)fRB->fUSetNodes->elementAt(i); + if (usetNode == nullptr) { + break; + } + + RBBIDebugPrintf("%3d ", i); + setName = UNICODE_STRING("anonymous", 9); + setRef = usetNode->fParent; + if (setRef != nullptr) { + varRef = setRef->fParent; + if (varRef != nullptr && varRef->fType == RBBINode::varRef) { + setName = varRef->fText; + } + } + RBBI_DEBUG_printUnicodeString(setName); + RBBIDebugPrintf(" "); + RBBI_DEBUG_printUnicodeString(usetNode->fText); + RBBIDebugPrintf("\n"); + if (usetNode->fLeftChild != nullptr) { + RBBINode::printTree(usetNode->fLeftChild, true); + } + } + RBBIDebugPrintf("\n"); +} +#endif + + + +//------------------------------------------------------------------------------------- +// +// RangeDescriptor copy constructor +// +//------------------------------------------------------------------------------------- + +RangeDescriptor::RangeDescriptor(const RangeDescriptor &other, UErrorCode &status) : + fStartChar(other.fStartChar), fEndChar {other.fEndChar}, fNum {other.fNum}, + fIncludesDict{other.fIncludesDict}, fFirstInGroup{other.fFirstInGroup} { + + if (U_FAILURE(status)) { + return; + } + fIncludesSets = new UVector(status); + if (this->fIncludesSets == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + if (U_FAILURE(status)) { + return; + } + + for (int32_t i=0; isize(); i++) { + this->fIncludesSets->addElement(other.fIncludesSets->elementAt(i), status); + } +} + + +//------------------------------------------------------------------------------------- +// +// RangeDesriptor default constructor +// +//------------------------------------------------------------------------------------- +RangeDescriptor::RangeDescriptor(UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + fIncludesSets = new UVector(status); + if (fIncludesSets == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } +} + + +//------------------------------------------------------------------------------------- +// +// RangeDesriptor Destructor +// +//------------------------------------------------------------------------------------- +RangeDescriptor::~RangeDescriptor() { + delete fIncludesSets; + fIncludesSets = nullptr; +} + +//------------------------------------------------------------------------------------- +// +// RangeDesriptor::split() +// +//------------------------------------------------------------------------------------- +void RangeDescriptor::split(UChar32 where, UErrorCode &status) { + U_ASSERT(where>fStartChar && where<=fEndChar); + RangeDescriptor *nr = new RangeDescriptor(*this, status); + if(nr == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (U_FAILURE(status)) { + delete nr; + return; + } + // RangeDescriptor copy constructor copies all fields. + // Only need to update those that are different after the split. + nr->fStartChar = where; + this->fEndChar = where-1; + nr->fNext = this->fNext; + this->fNext = nr; +} + + +//------------------------------------------------------------------------------------- +// +// RangeDescriptor::isDictionaryRange +// +// Test whether this range includes characters from +// the original Unicode Set named "dictionary". +// +// This function looks through the Unicode Sets that +// the range includes, checking for one named "dictionary" +// +// TODO: a faster way would be to find the set node for +// "dictionary" just once, rather than looking it +// up by name every time. +// +//------------------------------------------------------------------------------------- +bool RangeDescriptor::isDictionaryRange() { + static const char16_t *dictionary = u"dictionary"; + for (int32_t i=0; isize(); i++) { + RBBINode *usetNode = (RBBINode *)fIncludesSets->elementAt(i); + RBBINode *setRef = usetNode->fParent; + if (setRef != nullptr) { + RBBINode *varRef = setRef->fParent; + if (varRef && varRef->fType == RBBINode::varRef) { + const UnicodeString *setName = &varRef->fText; + if (setName->compare(dictionary, -1) == 0) { + return true; + } + } + } + } + return false; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ diff --git a/deps/icu-small/source/common/rbbisetb.h b/deps/icu-small/source/common/rbbisetb.h index cd09d3317a38d6..a5592d062b35ff 100644 --- a/deps/icu-small/source/common/rbbisetb.h +++ b/deps/icu-small/source/common/rbbisetb.h @@ -1,147 +1,147 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// rbbisetb.h -/* -********************************************************************** -* Copyright (c) 2001-2005, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#ifndef RBBISETB_H -#define RBBISETB_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/ucptrie.h" -#include "unicode/umutablecptrie.h" -#include "unicode/uobject.h" -#include "rbbirb.h" -#include "uvector.h" - -U_NAMESPACE_BEGIN - -// -// RBBISetBuilder Derives the character categories used by the runtime RBBI engine -// from the Unicode Sets appearing in the source RBBI rules, and -// creates the TRIE table used to map from Unicode to the -// character categories. -// - - -// -// RangeDescriptor -// -// Each of the non-overlapping character ranges gets one of these descriptors. -// All of them are strung together in a linked list, which is kept in order -// (by character) -// -class RangeDescriptor : public UMemory { -public: - UChar32 fStartChar {}; // Start of range, unicode 32 bit value. - UChar32 fEndChar {}; // End of range, unicode 32 bit value. - int32_t fNum {0}; // runtime-mapped input value for this range. - bool fIncludesDict {false}; // True if the range includes $dictionary. - bool fFirstInGroup {false}; // True if first range in a group with the same fNum. - UVector *fIncludesSets {nullptr}; // vector of the the original - // Unicode sets that include this range. - // (Contains ptrs to uset nodes) - RangeDescriptor *fNext {nullptr}; // Next RangeDescriptor in the linked list. - - RangeDescriptor(UErrorCode &status); - RangeDescriptor(const RangeDescriptor &other, UErrorCode &status); - ~RangeDescriptor(); - void split(UChar32 where, UErrorCode &status); // Spit this range in two at "where", with - // where appearing in the second (higher) part. - bool isDictionaryRange(); // Check whether this range appears as part of - // the Unicode set named "dictionary" - - RangeDescriptor(const RangeDescriptor &other) = delete; // forbid default copying of this class - RangeDescriptor &operator=(const RangeDescriptor &other) = delete; // forbid assigning of this class -}; - - -// -// RBBISetBuilder Handles processing of Unicode Sets from RBBI rules. -// -// Starting with the rules parse tree from the scanner, -// -// - Enumerate the set of UnicodeSets that are referenced -// by the RBBI rules. -// - compute a derived set of non-overlapping UnicodeSets -// that will correspond to columns in the state table for -// the RBBI execution engine. -// - construct the trie table that maps input characters -// to set numbers in the non-overlapping set of sets. -// - - -class RBBISetBuilder : public UMemory { -public: - RBBISetBuilder(RBBIRuleBuilder *rb); - ~RBBISetBuilder(); - - void buildRanges(); - void buildTrie(); - void addValToSets(UVector *sets, uint32_t val); - void addValToSet (RBBINode *usetNode, uint32_t val); - int32_t getNumCharCategories() const; // CharCategories are the same as input symbol set to the - // runtime state machine, which are the same as - // columns in the DFA state table - int32_t getDictCategoriesStart() const; // First char category that includes $dictionary, or - // last category + 1 if there are no dictionary categories. - int32_t getTrieSize() /*const*/; // Size in bytes of the serialized Trie. - void serializeTrie(uint8_t *where); // write out the serialized Trie. - 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 second category (table column) will be added to the first. - * @param categories the pair of categories to be merged. - */ - void mergeCategories(IntPair categories); - -#ifdef RBBI_DEBUG - void printSets(); - void printRanges(); - void printRangeGroups(); -#else - #define printSets() - #define printRanges() - #define printRangeGroups() -#endif - -private: - RBBIRuleBuilder *fRB; // The RBBI Rule Compiler that owns us. - UErrorCode *fStatus; - - RangeDescriptor *fRangeList; // Head of the linked list of RangeDescriptors - - UMutableCPTrie *fMutableTrie; // The mapping TRIE that is the end result of processing - UCPTrie *fTrie; // the Unicode Sets. - uint32_t fTrieSize; - - // Number of range groups, which are groups of ranges that are in the same original UnicodeSets. - int32_t fGroupCount; - - // The number of the first dictionary char category. - // If there are no Dictionary categories, set to the last category + 1. - int32_t fDictCategoriesStart; - - UBool fSawBOF; - - RBBISetBuilder(const RBBISetBuilder &other) = delete; // forbid copying of this class - RBBISetBuilder &operator=(const RBBISetBuilder &other) = delete; // forbid copying of this class -}; - - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// rbbisetb.h +/* +********************************************************************** +* Copyright (c) 2001-2005, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#ifndef RBBISETB_H +#define RBBISETB_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/ucptrie.h" +#include "unicode/umutablecptrie.h" +#include "unicode/uobject.h" +#include "rbbirb.h" +#include "uvector.h" + +U_NAMESPACE_BEGIN + +// +// RBBISetBuilder Derives the character categories used by the runtime RBBI engine +// from the Unicode Sets appearing in the source RBBI rules, and +// creates the TRIE table used to map from Unicode to the +// character categories. +// + + +// +// RangeDescriptor +// +// Each of the non-overlapping character ranges gets one of these descriptors. +// All of them are strung together in a linked list, which is kept in order +// (by character) +// +class RangeDescriptor : public UMemory { +public: + UChar32 fStartChar {}; // Start of range, unicode 32 bit value. + UChar32 fEndChar {}; // End of range, unicode 32 bit value. + int32_t fNum {0}; // runtime-mapped input value for this range. + bool fIncludesDict {false}; // True if the range includes $dictionary. + bool fFirstInGroup {false}; // True if first range in a group with the same fNum. + UVector *fIncludesSets {nullptr}; // vector of the the original + // Unicode sets that include this range. + // (Contains ptrs to uset nodes) + RangeDescriptor *fNext {nullptr}; // Next RangeDescriptor in the linked list. + + RangeDescriptor(UErrorCode &status); + RangeDescriptor(const RangeDescriptor &other, UErrorCode &status); + ~RangeDescriptor(); + void split(UChar32 where, UErrorCode &status); // Spit this range in two at "where", with + // where appearing in the second (higher) part. + bool isDictionaryRange(); // Check whether this range appears as part of + // the Unicode set named "dictionary" + + RangeDescriptor(const RangeDescriptor &other) = delete; // forbid default copying of this class + RangeDescriptor &operator=(const RangeDescriptor &other) = delete; // forbid assigning of this class +}; + + +// +// RBBISetBuilder Handles processing of Unicode Sets from RBBI rules. +// +// Starting with the rules parse tree from the scanner, +// +// - Enumerate the set of UnicodeSets that are referenced +// by the RBBI rules. +// - compute a derived set of non-overlapping UnicodeSets +// that will correspond to columns in the state table for +// the RBBI execution engine. +// - construct the trie table that maps input characters +// to set numbers in the non-overlapping set of sets. +// + + +class RBBISetBuilder : public UMemory { +public: + RBBISetBuilder(RBBIRuleBuilder *rb); + ~RBBISetBuilder(); + + void buildRanges(); + void buildTrie(); + void addValToSets(UVector *sets, uint32_t val); + void addValToSet (RBBINode *usetNode, uint32_t val); + int32_t getNumCharCategories() const; // CharCategories are the same as input symbol set to the + // runtime state machine, which are the same as + // columns in the DFA state table + int32_t getDictCategoriesStart() const; // First char category that includes $dictionary, or + // last category + 1 if there are no dictionary categories. + int32_t getTrieSize() /*const*/; // Size in bytes of the serialized Trie. + void serializeTrie(uint8_t *where); // write out the serialized Trie. + 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 second category (table column) will be added to the first. + * @param categories the pair of categories to be merged. + */ + void mergeCategories(IntPair categories); + +#ifdef RBBI_DEBUG + void printSets(); + void printRanges(); + void printRangeGroups(); +#else + #define printSets() + #define printRanges() + #define printRangeGroups() +#endif + +private: + RBBIRuleBuilder *fRB; // The RBBI Rule Compiler that owns us. + UErrorCode *fStatus; + + RangeDescriptor *fRangeList; // Head of the linked list of RangeDescriptors + + UMutableCPTrie *fMutableTrie; // The mapping TRIE that is the end result of processing + UCPTrie *fTrie; // the Unicode Sets. + uint32_t fTrieSize; + + // Number of range groups, which are groups of ranges that are in the same original UnicodeSets. + int32_t fGroupCount; + + // The number of the first dictionary char category. + // If there are no Dictionary categories, set to the last category + 1. + int32_t fDictCategoriesStart; + + UBool fSawBOF; + + RBBISetBuilder(const RBBISetBuilder &other) = delete; // forbid copying of this class + RBBISetBuilder &operator=(const RBBISetBuilder &other) = delete; // forbid copying of this class +}; + + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ + +#endif diff --git a/deps/icu-small/source/common/rbbistbl.cpp b/deps/icu-small/source/common/rbbistbl.cpp index 554aeb793f7467..b02791b5606831 100644 --- a/deps/icu-small/source/common/rbbistbl.cpp +++ b/deps/icu-small/source/common/rbbistbl.cpp @@ -1,270 +1,270 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// file: rbbistbl.cpp Implementation of the ICU RBBISymbolTable class -// -/* -*************************************************************************** -* Copyright (C) 2002-2014 International Business Machines Corporation -* and others. All rights reserved. -*************************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/unistr.h" -#include "unicode/uniset.h" -#include "unicode/uchar.h" -#include "unicode/parsepos.h" - -#include "cstr.h" -#include "rbbinode.h" -#include "rbbirb.h" -#include "umutex.h" - - -// -// RBBISymbolTableEntry_deleter Used by the UHashTable to delete the contents -// when the hash table is deleted. -// -U_CDECL_BEGIN -static void U_CALLCONV RBBISymbolTableEntry_deleter(void *p) { - icu::RBBISymbolTableEntry *px = (icu::RBBISymbolTableEntry *)p; - delete px; -} -U_CDECL_END - - - -U_NAMESPACE_BEGIN - -RBBISymbolTable::RBBISymbolTable(RBBIRuleScanner *rs, const UnicodeString &rules, UErrorCode &status) - :fRules(rules), fRuleScanner(rs), ffffString(UChar(0xffff)) -{ - fHashTable = NULL; - fCachedSetLookup = NULL; - - fHashTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status); - // uhash_open checks status - if (U_FAILURE(status)) { - return; - } - uhash_setValueDeleter(fHashTable, RBBISymbolTableEntry_deleter); -} - - - -RBBISymbolTable::~RBBISymbolTable() -{ - uhash_close(fHashTable); -} - - -// -// RBBISymbolTable::lookup This function from the abstract symbol table interface -// looks up a variable name and returns a UnicodeString -// containing the substitution text. -// -// The variable name does NOT include the leading $. -// -const UnicodeString *RBBISymbolTable::lookup(const UnicodeString& s) const -{ - RBBISymbolTableEntry *el; - RBBINode *varRefNode; - RBBINode *exprNode; - RBBINode *usetNode; - const UnicodeString *retString; - RBBISymbolTable *This = (RBBISymbolTable *)this; // cast off const - - el = (RBBISymbolTableEntry *)uhash_get(fHashTable, &s); - if (el == NULL) { - return NULL; - } - - varRefNode = el->val; - exprNode = varRefNode->fLeftChild; // Root node of expression for variable - if (exprNode->fType == RBBINode::setRef) { - // The $variable refers to a single UnicodeSet - // return the ffffString, which will subsequently be interpreted as a - // stand-in character for the set by RBBISymbolTable::lookupMatcher() - usetNode = exprNode->fLeftChild; - This->fCachedSetLookup = usetNode->fInputSet; - retString = &ffffString; - } - else - { - // The variable refers to something other than just a set. - // return the original source string for the expression - retString = &exprNode->fText; - This->fCachedSetLookup = NULL; - } - return retString; -} - - - -// -// RBBISymbolTable::lookupMatcher This function from the abstract symbol table -// interface maps a single stand-in character to a -// pointer to a Unicode Set. The Unicode Set code uses this -// mechanism to get all references to the same $variable -// name to refer to a single common Unicode Set instance. -// -// This implementation cheats a little, and does not maintain a map of stand-in chars -// to sets. Instead, it takes advantage of the fact that the UnicodeSet -// constructor will always call this function right after calling lookup(), -// and we just need to remember what set to return between these two calls. -const UnicodeFunctor *RBBISymbolTable::lookupMatcher(UChar32 ch) const -{ - UnicodeSet *retVal = NULL; - RBBISymbolTable *This = (RBBISymbolTable *)this; // cast off const - if (ch == 0xffff) { - retVal = fCachedSetLookup; - This->fCachedSetLookup = 0; - } - return retVal; -} - -// -// RBBISymbolTable::parseReference This function from the abstract symbol table interface -// looks for a $variable name in the source text. -// It does not look it up, only scans for it. -// It is used by the UnicodeSet parser. -// -// This implementation is lifted pretty much verbatim -// from the rules based transliterator implementation. -// I didn't see an obvious way of sharing it. -// -UnicodeString RBBISymbolTable::parseReference(const UnicodeString& text, - ParsePosition& pos, int32_t limit) const -{ - int32_t start = pos.getIndex(); - int32_t i = start; - UnicodeString result; - while (i < limit) { - UChar c = text.charAt(i); - if ((i==start && !u_isIDStart(c)) || !u_isIDPart(c)) { - break; - } - ++i; - } - if (i == start) { // No valid name chars - return result; // Indicate failure with empty string - } - pos.setIndex(i); - text.extractBetween(start, i, result); - return result; -} - - - -// -// RBBISymbolTable::lookupNode Given a key (a variable name), return the -// corresponding RBBI Node. If there is no entry -// in the table for this name, return NULL. -// -RBBINode *RBBISymbolTable::lookupNode(const UnicodeString &key) const{ - - RBBINode *retNode = NULL; - RBBISymbolTableEntry *el; - - el = (RBBISymbolTableEntry *)uhash_get(fHashTable, &key); - if (el != NULL) { - retNode = el->val; - } - return retNode; -} - - -// -// RBBISymbolTable::addEntry Add a new entry to the symbol table. -// Indicate an error if the name already exists - -// this will only occur in the case of duplicate -// variable assignments. -// -void RBBISymbolTable::addEntry (const UnicodeString &key, RBBINode *val, UErrorCode &err) { - RBBISymbolTableEntry *e; - /* test for buffer overflows */ - if (U_FAILURE(err)) { - return; - } - e = (RBBISymbolTableEntry *)uhash_get(fHashTable, &key); - if (e != NULL) { - err = U_BRK_VARIABLE_REDFINITION; - return; - } - - e = new RBBISymbolTableEntry; - if (e == NULL) { - err = U_MEMORY_ALLOCATION_ERROR; - return; - } - e->key = key; - e->val = val; - uhash_put( fHashTable, &e->key, e, &err); -} - - -RBBISymbolTableEntry::RBBISymbolTableEntry() : UMemory(), key(), val(NULL) {} - -RBBISymbolTableEntry::~RBBISymbolTableEntry() { - // The "val" of a symbol table entry is a variable reference node. - // The l. child of the val is the rhs expression from the assignment. - // Unlike other node types, children of variable reference nodes are not - // automatically recursively deleted. We do it manually here. - delete val->fLeftChild; - val->fLeftChild = NULL; - - delete val; - - // Note: the key UnicodeString is destructed by virtue of being in the object by value. -} - - -// -// RBBISymbolTable::print Debugging function, dump out the symbol table contents. -// -#ifdef RBBI_DEBUG -void RBBISymbolTable::rbbiSymtablePrint() const { - RBBIDebugPrintf("Variable Definitions Symbol Table\n" - "Name Node serial String Val\n" - "-------------------------------------------------------------------\n"); - - int32_t pos = UHASH_FIRST; - const UHashElement *e = NULL; - for (;;) { - e = uhash_nextElement(fHashTable, &pos); - if (e == NULL ) { - break; - } - RBBISymbolTableEntry *s = (RBBISymbolTableEntry *)e->value.pointer; - - RBBIDebugPrintf("%-19s %8p %7d ", CStr(s->key)(), (void *)s->val, s->val->fSerialNum); - RBBIDebugPrintf(" %s\n", CStr(s->val->fLeftChild->fText)()); - } - - RBBIDebugPrintf("\nParsed Variable Definitions\n"); - pos = -1; - for (;;) { - e = uhash_nextElement(fHashTable, &pos); - if (e == NULL ) { - break; - } - RBBISymbolTableEntry *s = (RBBISymbolTableEntry *)e->value.pointer; - RBBIDebugPrintf("%s\n", CStr(s->key)()); - RBBINode::printTree(s->val, true); - RBBINode::printTree(s->val->fLeftChild, false); - RBBIDebugPrintf("\n"); - } -} -#endif - - - - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// file: rbbistbl.cpp Implementation of the ICU RBBISymbolTable class +// +/* +*************************************************************************** +* Copyright (C) 2002-2014 International Business Machines Corporation +* and others. All rights reserved. +*************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/unistr.h" +#include "unicode/uniset.h" +#include "unicode/uchar.h" +#include "unicode/parsepos.h" + +#include "cstr.h" +#include "rbbinode.h" +#include "rbbirb.h" +#include "umutex.h" + + +// +// RBBISymbolTableEntry_deleter Used by the UHashTable to delete the contents +// when the hash table is deleted. +// +U_CDECL_BEGIN +static void U_CALLCONV RBBISymbolTableEntry_deleter(void *p) { + icu::RBBISymbolTableEntry *px = (icu::RBBISymbolTableEntry *)p; + delete px; +} +U_CDECL_END + + + +U_NAMESPACE_BEGIN + +RBBISymbolTable::RBBISymbolTable(RBBIRuleScanner *rs, const UnicodeString &rules, UErrorCode &status) + :fRules(rules), fRuleScanner(rs), ffffString(char16_t(0xffff)) +{ + fHashTable = nullptr; + fCachedSetLookup = nullptr; + + fHashTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, nullptr, &status); + // uhash_open checks status + if (U_FAILURE(status)) { + return; + } + uhash_setValueDeleter(fHashTable, RBBISymbolTableEntry_deleter); +} + + + +RBBISymbolTable::~RBBISymbolTable() +{ + uhash_close(fHashTable); +} + + +// +// RBBISymbolTable::lookup This function from the abstract symbol table interface +// looks up a variable name and returns a UnicodeString +// containing the substitution text. +// +// The variable name does NOT include the leading $. +// +const UnicodeString *RBBISymbolTable::lookup(const UnicodeString& s) const +{ + RBBISymbolTableEntry *el; + RBBINode *varRefNode; + RBBINode *exprNode; + RBBINode *usetNode; + const UnicodeString *retString; + RBBISymbolTable *This = (RBBISymbolTable *)this; // cast off const + + el = (RBBISymbolTableEntry *)uhash_get(fHashTable, &s); + if (el == nullptr) { + return nullptr; + } + + varRefNode = el->val; + exprNode = varRefNode->fLeftChild; // Root node of expression for variable + if (exprNode->fType == RBBINode::setRef) { + // The $variable refers to a single UnicodeSet + // return the ffffString, which will subsequently be interpreted as a + // stand-in character for the set by RBBISymbolTable::lookupMatcher() + usetNode = exprNode->fLeftChild; + This->fCachedSetLookup = usetNode->fInputSet; + retString = &ffffString; + } + else + { + // The variable refers to something other than just a set. + // return the original source string for the expression + retString = &exprNode->fText; + This->fCachedSetLookup = nullptr; + } + return retString; +} + + + +// +// RBBISymbolTable::lookupMatcher This function from the abstract symbol table +// interface maps a single stand-in character to a +// pointer to a Unicode Set. The Unicode Set code uses this +// mechanism to get all references to the same $variable +// name to refer to a single common Unicode Set instance. +// +// This implementation cheats a little, and does not maintain a map of stand-in chars +// to sets. Instead, it takes advantage of the fact that the UnicodeSet +// constructor will always call this function right after calling lookup(), +// and we just need to remember what set to return between these two calls. +const UnicodeFunctor *RBBISymbolTable::lookupMatcher(UChar32 ch) const +{ + UnicodeSet *retVal = nullptr; + RBBISymbolTable *This = (RBBISymbolTable *)this; // cast off const + if (ch == 0xffff) { + retVal = fCachedSetLookup; + This->fCachedSetLookup = 0; + } + return retVal; +} + +// +// RBBISymbolTable::parseReference This function from the abstract symbol table interface +// looks for a $variable name in the source text. +// It does not look it up, only scans for it. +// It is used by the UnicodeSet parser. +// +// This implementation is lifted pretty much verbatim +// from the rules based transliterator implementation. +// I didn't see an obvious way of sharing it. +// +UnicodeString RBBISymbolTable::parseReference(const UnicodeString& text, + ParsePosition& pos, int32_t limit) const +{ + int32_t start = pos.getIndex(); + int32_t i = start; + UnicodeString result; + while (i < limit) { + char16_t c = text.charAt(i); + if ((i==start && !u_isIDStart(c)) || !u_isIDPart(c)) { + break; + } + ++i; + } + if (i == start) { // No valid name chars + return result; // Indicate failure with empty string + } + pos.setIndex(i); + text.extractBetween(start, i, result); + return result; +} + + + +// +// RBBISymbolTable::lookupNode Given a key (a variable name), return the +// corresponding RBBI Node. If there is no entry +// in the table for this name, return nullptr. +// +RBBINode *RBBISymbolTable::lookupNode(const UnicodeString &key) const{ + + RBBINode *retNode = nullptr; + RBBISymbolTableEntry *el; + + el = (RBBISymbolTableEntry *)uhash_get(fHashTable, &key); + if (el != nullptr) { + retNode = el->val; + } + return retNode; +} + + +// +// RBBISymbolTable::addEntry Add a new entry to the symbol table. +// Indicate an error if the name already exists - +// this will only occur in the case of duplicate +// variable assignments. +// +void RBBISymbolTable::addEntry (const UnicodeString &key, RBBINode *val, UErrorCode &err) { + RBBISymbolTableEntry *e; + /* test for buffer overflows */ + if (U_FAILURE(err)) { + return; + } + e = (RBBISymbolTableEntry *)uhash_get(fHashTable, &key); + if (e != nullptr) { + err = U_BRK_VARIABLE_REDFINITION; + return; + } + + e = new RBBISymbolTableEntry; + if (e == nullptr) { + err = U_MEMORY_ALLOCATION_ERROR; + return; + } + e->key = key; + e->val = val; + uhash_put( fHashTable, &e->key, e, &err); +} + + +RBBISymbolTableEntry::RBBISymbolTableEntry() : UMemory(), key(), val(nullptr) {} + +RBBISymbolTableEntry::~RBBISymbolTableEntry() { + // The "val" of a symbol table entry is a variable reference node. + // The l. child of the val is the rhs expression from the assignment. + // Unlike other node types, children of variable reference nodes are not + // automatically recursively deleted. We do it manually here. + delete val->fLeftChild; + val->fLeftChild = nullptr; + + delete val; + + // Note: the key UnicodeString is destructed by virtue of being in the object by value. +} + + +// +// RBBISymbolTable::print Debugging function, dump out the symbol table contents. +// +#ifdef RBBI_DEBUG +void RBBISymbolTable::rbbiSymtablePrint() const { + RBBIDebugPrintf("Variable Definitions Symbol Table\n" + "Name Node serial String Val\n" + "-------------------------------------------------------------------\n"); + + int32_t pos = UHASH_FIRST; + const UHashElement *e = nullptr; + for (;;) { + e = uhash_nextElement(fHashTable, &pos); + if (e == nullptr ) { + break; + } + RBBISymbolTableEntry *s = (RBBISymbolTableEntry *)e->value.pointer; + + RBBIDebugPrintf("%-19s %8p %7d ", CStr(s->key)(), (void *)s->val, s->val->fSerialNum); + RBBIDebugPrintf(" %s\n", CStr(s->val->fLeftChild->fText)()); + } + + RBBIDebugPrintf("\nParsed Variable Definitions\n"); + pos = -1; + for (;;) { + e = uhash_nextElement(fHashTable, &pos); + if (e == nullptr ) { + break; + } + RBBISymbolTableEntry *s = (RBBISymbolTableEntry *)e->value.pointer; + RBBIDebugPrintf("%s\n", CStr(s->key)()); + RBBINode::printTree(s->val, true); + RBBINode::printTree(s->val->fLeftChild, false); + RBBIDebugPrintf("\n"); + } +} +#endif + + + + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ diff --git a/deps/icu-small/source/common/rbbitblb.cpp b/deps/icu-small/source/common/rbbitblb.cpp index 0e3ec7999f775e..71650349ef1b75 100644 --- a/deps/icu-small/source/common/rbbitblb.cpp +++ b/deps/icu-small/source/common/rbbitblb.cpp @@ -1,1810 +1,1810 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ -// -// rbbitblb.cpp -// - - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/unistr.h" -#include "rbbitblb.h" -#include "rbbirb.h" -#include "rbbiscan.h" -#include "rbbisetb.h" -#include "rbbidata.h" -#include "cstring.h" -#include "uassert.h" -#include "uvectr32.h" -#include "cmemory.h" - -U_NAMESPACE_BEGIN - -const int32_t kMaxStateFor8BitsTable = 255; - -RBBITableBuilder::RBBITableBuilder(RBBIRuleBuilder *rb, RBBINode **rootNode, UErrorCode &status) : - fRB(rb), - fTree(*rootNode), - fStatus(&status), - fDStates(nullptr), - fSafeTable(nullptr) { - if (U_FAILURE(status)) { - return; - } - // fDStates is UVector - fDStates = new UVector(status); - if (U_SUCCESS(status) && fDStates == nullptr ) { - status = U_MEMORY_ALLOCATION_ERROR; - } -} - - - -RBBITableBuilder::~RBBITableBuilder() { - int i; - for (i=0; isize(); i++) { - delete (RBBIStateDescriptor *)fDStates->elementAt(i); - } - delete fDStates; - delete fSafeTable; - delete fLookAheadRuleMap; -} - - -//----------------------------------------------------------------------------- -// -// RBBITableBuilder::buildForwardTable - This is the main function for building -// the DFA state transition table from the RBBI rules parse tree. -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::buildForwardTable() { - - if (U_FAILURE(*fStatus)) { - return; - } - - // If there were no rules, just return. This situation can easily arise - // for the reverse rules. - if (fTree==NULL) { - return; - } - - // - // Walk through the tree, replacing any references to $variables with a copy of the - // parse tree for the substitution expression. - // - fTree = fTree->flattenVariables(); -#ifdef RBBI_DEBUG - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "ftree")) { - RBBIDebugPuts("\nParse tree after flattening variable references."); - RBBINode::printTree(fTree, true); - } -#endif - - // - // If the rules contained any references to {bof} - // add a {bof} to the - // tree. Means that all matches must start out with the - // {bof} fake character. - // - if (fRB->fSetBuilder->sawBOF()) { - RBBINode *bofTop = new RBBINode(RBBINode::opCat); - RBBINode *bofLeaf = new RBBINode(RBBINode::leafChar); - // Delete and exit if memory allocation failed. - if (bofTop == NULL || bofLeaf == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - delete bofTop; - delete bofLeaf; - return; - } - bofTop->fLeftChild = bofLeaf; - bofTop->fRightChild = fTree; - bofLeaf->fParent = bofTop; - bofLeaf->fVal = 2; // Reserved value for {bof}. - fTree = bofTop; - } - - // - // Add a unique right-end marker to the expression. - // Appears as a cat-node, left child being the original tree, - // right child being the end marker. - // - RBBINode *cn = new RBBINode(RBBINode::opCat); - // Exit if memory allocation failed. - if (cn == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - cn->fLeftChild = fTree; - fTree->fParent = cn; - RBBINode *endMarkerNode = cn->fRightChild = new RBBINode(RBBINode::endMark); - // Delete and exit if memory allocation failed. - if (cn->fRightChild == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - delete cn; - return; - } - cn->fRightChild->fParent = cn; - fTree = cn; - - // - // Replace all references to UnicodeSets with the tree for the equivalent - // expression. - // - fTree->flattenSets(); -#ifdef RBBI_DEBUG - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "stree")) { - RBBIDebugPuts("\nParse tree after flattening Unicode Set references."); - RBBINode::printTree(fTree, true); - } -#endif - - - // - // calculate the functions nullable, firstpos, lastpos and followpos on - // nodes in the parse tree. - // See the algorithm description in Aho. - // Understanding how this works by looking at the code alone will be - // nearly impossible. - // - calcNullable(fTree); - calcFirstPos(fTree); - calcLastPos(fTree); - calcFollowPos(fTree); - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "pos")) { - RBBIDebugPuts("\n"); - printPosSets(fTree); - } - - // - // For "chained" rules, modify the followPos sets - // - if (fRB->fChainRules) { - calcChainedFollowPos(fTree, endMarkerNode); - } - - // - // BOF (start of input) test fixup. - // - if (fRB->fSetBuilder->sawBOF()) { - bofFixup(); - } - - // - // Build the DFA state transition tables. - // - buildStateTable(); - mapLookAheadRules(); - flagAcceptingStates(); - flagLookAheadStates(); - flagTaggedStates(); - - // - // Update the global table of rule status {tag} values - // The rule builder has a global vector of status values that are common - // for all tables. Merge the ones from this table into the global set. - // - mergeRuleStatusVals(); -} - - - -//----------------------------------------------------------------------------- -// -// calcNullable. Impossible to explain succinctly. See Aho, section 3.9 -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::calcNullable(RBBINode *n) { - if (n == NULL) { - return; - } - if (n->fType == RBBINode::setRef || - n->fType == RBBINode::endMark ) { - // These are non-empty leaf node types. - n->fNullable = false; - return; - } - - if (n->fType == RBBINode::lookAhead || n->fType == RBBINode::tag) { - // Lookahead marker node. It's a leaf, so no recursion on children. - // It's nullable because it does not match any literal text from the input stream. - n->fNullable = true; - return; - } - - - // The node is not a leaf. - // Calculate nullable on its children. - calcNullable(n->fLeftChild); - calcNullable(n->fRightChild); - - // Apply functions from table 3.40 in Aho - if (n->fType == RBBINode::opOr) { - n->fNullable = n->fLeftChild->fNullable || n->fRightChild->fNullable; - } - else if (n->fType == RBBINode::opCat) { - n->fNullable = n->fLeftChild->fNullable && n->fRightChild->fNullable; - } - else if (n->fType == RBBINode::opStar || n->fType == RBBINode::opQuestion) { - n->fNullable = true; - } - else { - n->fNullable = false; - } -} - - - - -//----------------------------------------------------------------------------- -// -// calcFirstPos. Impossible to explain succinctly. See Aho, section 3.9 -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::calcFirstPos(RBBINode *n) { - if (n == NULL) { - return; - } - if (n->fType == RBBINode::leafChar || - n->fType == RBBINode::endMark || - n->fType == RBBINode::lookAhead || - n->fType == RBBINode::tag) { - // These are non-empty leaf node types. - // Note: In order to maintain the sort invariant on the set, - // this function should only be called on a node whose set is - // empty to start with. - n->fFirstPosSet->addElement(n, *fStatus); - return; - } - - // The node is not a leaf. - // Calculate firstPos on its children. - calcFirstPos(n->fLeftChild); - calcFirstPos(n->fRightChild); - - // Apply functions from table 3.40 in Aho - if (n->fType == RBBINode::opOr) { - setAdd(n->fFirstPosSet, n->fLeftChild->fFirstPosSet); - setAdd(n->fFirstPosSet, n->fRightChild->fFirstPosSet); - } - else if (n->fType == RBBINode::opCat) { - setAdd(n->fFirstPosSet, n->fLeftChild->fFirstPosSet); - if (n->fLeftChild->fNullable) { - setAdd(n->fFirstPosSet, n->fRightChild->fFirstPosSet); - } - } - else if (n->fType == RBBINode::opStar || - n->fType == RBBINode::opQuestion || - n->fType == RBBINode::opPlus) { - setAdd(n->fFirstPosSet, n->fLeftChild->fFirstPosSet); - } -} - - - -//----------------------------------------------------------------------------- -// -// calcLastPos. Impossible to explain succinctly. See Aho, section 3.9 -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::calcLastPos(RBBINode *n) { - if (n == NULL) { - return; - } - if (n->fType == RBBINode::leafChar || - n->fType == RBBINode::endMark || - n->fType == RBBINode::lookAhead || - n->fType == RBBINode::tag) { - // These are non-empty leaf node types. - // Note: In order to maintain the sort invariant on the set, - // this function should only be called on a node whose set is - // empty to start with. - n->fLastPosSet->addElement(n, *fStatus); - return; - } - - // The node is not a leaf. - // Calculate lastPos on its children. - calcLastPos(n->fLeftChild); - calcLastPos(n->fRightChild); - - // Apply functions from table 3.40 in Aho - if (n->fType == RBBINode::opOr) { - setAdd(n->fLastPosSet, n->fLeftChild->fLastPosSet); - setAdd(n->fLastPosSet, n->fRightChild->fLastPosSet); - } - else if (n->fType == RBBINode::opCat) { - setAdd(n->fLastPosSet, n->fRightChild->fLastPosSet); - if (n->fRightChild->fNullable) { - setAdd(n->fLastPosSet, n->fLeftChild->fLastPosSet); - } - } - else if (n->fType == RBBINode::opStar || - n->fType == RBBINode::opQuestion || - n->fType == RBBINode::opPlus) { - setAdd(n->fLastPosSet, n->fLeftChild->fLastPosSet); - } -} - - - -//----------------------------------------------------------------------------- -// -// calcFollowPos. Impossible to explain succinctly. See Aho, section 3.9 -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::calcFollowPos(RBBINode *n) { - if (n == NULL || - n->fType == RBBINode::leafChar || - n->fType == RBBINode::endMark) { - return; - } - - calcFollowPos(n->fLeftChild); - calcFollowPos(n->fRightChild); - - // Aho rule #1 - if (n->fType == RBBINode::opCat) { - RBBINode *i; // is 'i' in Aho's description - uint32_t ix; - - UVector *LastPosOfLeftChild = n->fLeftChild->fLastPosSet; - - for (ix=0; ix<(uint32_t)LastPosOfLeftChild->size(); ix++) { - i = (RBBINode *)LastPosOfLeftChild->elementAt(ix); - setAdd(i->fFollowPos, n->fRightChild->fFirstPosSet); - } - } - - // Aho rule #2 - if (n->fType == RBBINode::opStar || - n->fType == RBBINode::opPlus) { - RBBINode *i; // again, n and i are the names from Aho's description. - uint32_t ix; - - for (ix=0; ix<(uint32_t)n->fLastPosSet->size(); ix++) { - i = (RBBINode *)n->fLastPosSet->elementAt(ix); - setAdd(i->fFollowPos, n->fFirstPosSet); - } - } - - - -} - -//----------------------------------------------------------------------------- -// -// addRuleRootNodes Recursively walk a parse tree, adding all nodes flagged -// as roots of a rule to a destination vector. -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::addRuleRootNodes(UVector *dest, RBBINode *node) { - if (node == NULL || U_FAILURE(*fStatus)) { - return; - } - U_ASSERT(!dest->hasDeleter()); - if (node->fRuleRoot) { - dest->addElement(node, *fStatus); - // Note: rules cannot nest. If we found a rule start node, - // no child node can also be a start node. - return; - } - addRuleRootNodes(dest, node->fLeftChild); - addRuleRootNodes(dest, node->fRightChild); -} - -//----------------------------------------------------------------------------- -// -// calcChainedFollowPos. Modify the previously calculated followPos sets -// to implement rule chaining. NOT described by Aho -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::calcChainedFollowPos(RBBINode *tree, RBBINode *endMarkNode) { - - UVector leafNodes(*fStatus); - if (U_FAILURE(*fStatus)) { - return; - } - - // get a list all leaf nodes - tree->findNodes(&leafNodes, RBBINode::leafChar, *fStatus); - if (U_FAILURE(*fStatus)) { - return; - } - - // Collect all leaf nodes that can start matches for rules - // with inbound chaining enabled, which is the union of the - // firstPosition sets from each of the rule root nodes. - - UVector ruleRootNodes(*fStatus); - addRuleRootNodes(&ruleRootNodes, tree); - - UVector matchStartNodes(*fStatus); - for (int j=0; j(ruleRootNodes.elementAt(j)); - if (node->fChainIn) { - setAdd(&matchStartNodes, node->fFirstPosSet); - } - } - if (U_FAILURE(*fStatus)) { - return; - } - - int32_t endNodeIx; - int32_t startNodeIx; - - for (endNodeIx=0; endNodeIxfFollowPos->contains(endMarkNode)) { - continue; - } - - // We've got a node that can end a match. - - // !!LBCMNoChain implementation: If this node's val correspond to - // the Line Break $CM char class, don't chain from it. - // TODO: Remove this. !!LBCMNoChain is deprecated, and is not used - // by any of the standard ICU rules. - if (fRB->fLBCMNoChain) { - UChar32 c = this->fRB->fSetBuilder->getFirstChar(endNode->fVal); - if (c != -1) { - // c == -1 occurs with sets containing only the {eof} marker string. - ULineBreak cLBProp = (ULineBreak)u_getIntPropertyValue(c, UCHAR_LINE_BREAK); - if (cLBProp == U_LB_COMBINING_MARK) { - continue; - } - } - } - - // Now iterate over the nodes that can start a match, looking for ones - // with the same char class as our ending node. - RBBINode *startNode; - for (startNodeIx = 0; startNodeIxfType != RBBINode::leafChar) { - continue; - } - - if (endNode->fVal == startNode->fVal) { - // The end val (character class) of one possible match is the - // same as the start of another. - - // Add all nodes from the followPos of the start node to the - // followPos set of the end node, which will have the effect of - // letting matches transition from a match state at endNode - // to the second char of a match starting with startNode. - setAdd(endNode->fFollowPos, startNode->fFollowPos); - } - } - } -} - - -//----------------------------------------------------------------------------- -// -// bofFixup. Fixup for state tables that include {bof} beginning of input testing. -// Do an swizzle similar to chaining, modifying the followPos set of -// the bofNode to include the followPos nodes from other {bot} nodes -// scattered through the tree. -// -// This function has much in common with calcChainedFollowPos(). -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::bofFixup() { - - if (U_FAILURE(*fStatus)) { - return; - } - - // The parse tree looks like this ... - // fTree root ---> - // / \ . - // <#end node> - // / \ . - // rest - // of tree - // - // We will be adding things to the followPos set of the - // - RBBINode *bofNode = fTree->fLeftChild->fLeftChild; - U_ASSERT(bofNode->fType == RBBINode::leafChar); - U_ASSERT(bofNode->fVal == 2); - - // Get all nodes that can be the start a match of the user-written rules - // (excluding the fake bofNode) - // We want the nodes that can start a match in the - // part labeled "rest of tree" - // - UVector *matchStartNodes = fTree->fLeftChild->fRightChild->fFirstPosSet; - - RBBINode *startNode; - int startNodeIx; - for (startNodeIx = 0; startNodeIxsize(); startNodeIx++) { - startNode = (RBBINode *)matchStartNodes->elementAt(startNodeIx); - if (startNode->fType != RBBINode::leafChar) { - continue; - } - - if (startNode->fVal == bofNode->fVal) { - // We found a leaf node corresponding to a {bof} that was - // explicitly written into a rule. - // Add everything from the followPos set of this node to the - // followPos set of the fake bofNode at the start of the tree. - // - setAdd(bofNode->fFollowPos, startNode->fFollowPos); - } - } -} - -//----------------------------------------------------------------------------- -// -// buildStateTable() Determine the set of runtime DFA states and the -// transition tables for these states, by the algorithm -// of fig. 3.44 in Aho. -// -// Most of the comments are quotes of Aho's psuedo-code. -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::buildStateTable() { - if (U_FAILURE(*fStatus)) { - return; - } - RBBIStateDescriptor *failState; - // Set it to NULL to avoid uninitialized warning - RBBIStateDescriptor *initialState = NULL; - // - // Add a dummy state 0 - the stop state. Not from Aho. - int lastInputSymbol = fRB->fSetBuilder->getNumCharCategories() - 1; - failState = new RBBIStateDescriptor(lastInputSymbol, fStatus); - if (failState == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - goto ExitBuildSTdeleteall; - } - failState->fPositions = new UVector(*fStatus); - if (failState->fPositions == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - } - if (failState->fPositions == NULL || U_FAILURE(*fStatus)) { - goto ExitBuildSTdeleteall; - } - fDStates->addElement(failState, *fStatus); - if (U_FAILURE(*fStatus)) { - goto ExitBuildSTdeleteall; - } - - // initially, the only unmarked state in Dstates is firstpos(root), - // where toot is the root of the syntax tree for (r)#; - initialState = new RBBIStateDescriptor(lastInputSymbol, fStatus); - if (initialState == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - } - if (U_FAILURE(*fStatus)) { - goto ExitBuildSTdeleteall; - } - initialState->fPositions = new UVector(*fStatus); - if (initialState->fPositions == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - } - if (U_FAILURE(*fStatus)) { - goto ExitBuildSTdeleteall; - } - setAdd(initialState->fPositions, fTree->fFirstPosSet); - fDStates->addElement(initialState, *fStatus); - if (U_FAILURE(*fStatus)) { - goto ExitBuildSTdeleteall; - } - - // while there is an unmarked state T in Dstates do begin - for (;;) { - RBBIStateDescriptor *T = NULL; - int32_t tx; - for (tx=1; txsize(); tx++) { - RBBIStateDescriptor *temp; - temp = (RBBIStateDescriptor *)fDStates->elementAt(tx); - if (temp->fMarked == false) { - T = temp; - break; - } - } - if (T == NULL) { - break; - } - - // mark T; - T->fMarked = true; - - // for each input symbol a do begin - int32_t a; - for (a = 1; a<=lastInputSymbol; a++) { - // let U be the set of positions that are in followpos(p) - // for some position p in T - // such that the symbol at position p is a; - UVector *U = NULL; - RBBINode *p; - int32_t px; - for (px=0; pxfPositions->size(); px++) { - p = (RBBINode *)T->fPositions->elementAt(px); - if ((p->fType == RBBINode::leafChar) && (p->fVal == a)) { - if (U == NULL) { - U = new UVector(*fStatus); - if (U == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - goto ExitBuildSTdeleteall; - } - } - setAdd(U, p->fFollowPos); - } - } - - // if U is not empty and not in DStates then - int32_t ux = 0; - UBool UinDstates = false; - if (U != NULL) { - U_ASSERT(U->size() > 0); - int ix; - for (ix=0; ixsize(); ix++) { - RBBIStateDescriptor *temp2; - temp2 = (RBBIStateDescriptor *)fDStates->elementAt(ix); - if (setEquals(U, temp2->fPositions)) { - delete U; - U = temp2->fPositions; - ux = ix; - UinDstates = true; - break; - } - } - - // Add U as an unmarked state to Dstates - if (!UinDstates) - { - RBBIStateDescriptor *newState = new RBBIStateDescriptor(lastInputSymbol, fStatus); - if (newState == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - } - if (U_FAILURE(*fStatus)) { - goto ExitBuildSTdeleteall; - } - newState->fPositions = U; - fDStates->addElement(newState, *fStatus); - if (U_FAILURE(*fStatus)) { - return; - } - ux = fDStates->size()-1; - } - - // Dtran[T, a] := U; - T->fDtran->setElementAt(ux, a); - } - } - } - return; - // delete local pointers only if error occurred. -ExitBuildSTdeleteall: - delete initialState; - delete failState; -} - - -/** - * mapLookAheadRules - * - */ -void RBBITableBuilder::mapLookAheadRules() { - fLookAheadRuleMap = new UVector32(fRB->fScanner->numRules() + 1, *fStatus); - if (fLookAheadRuleMap == nullptr) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - } - if (U_FAILURE(*fStatus)) { - return; - } - fLookAheadRuleMap->setSize(fRB->fScanner->numRules() + 1); - - for (int32_t n=0; nsize(); n++) { - RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); - int32_t laSlotForState = 0; - - // Establish the look-ahead slot for this state, if the state covers - // any look-ahead nodes - corresponding to the '/' in look-ahead rules. - - // If any of the look-ahead nodes already have a slot assigned, use it, - // otherwise assign a new one. - - bool sawLookAheadNode = false; - for (int32_t ipos=0; iposfPositions->size(); ++ipos) { - RBBINode *node = static_cast(sd->fPositions->elementAt(ipos)); - if (node->fType != RBBINode::NodeType::lookAhead) { - continue; - } - sawLookAheadNode = true; - int32_t ruleNum = node->fVal; // Set when rule was originally parsed. - U_ASSERT(ruleNum < fLookAheadRuleMap->size()); - U_ASSERT(ruleNum > 0); - int32_t laSlot = fLookAheadRuleMap->elementAti(ruleNum); - if (laSlot != 0) { - if (laSlotForState == 0) { - laSlotForState = laSlot; - } else { - // TODO: figure out if this can fail, change to setting an error code if so. - U_ASSERT(laSlot == laSlotForState); - } - } - } - if (!sawLookAheadNode) { - continue; - } - - if (laSlotForState == 0) { - laSlotForState = ++fLASlotsInUse; - } - - // For each look ahead node covered by this state, - // set the mapping from the node's rule number to the look ahead slot. - // There can be multiple nodes/rule numbers going to the same la slot. - - for (int32_t ipos=0; iposfPositions->size(); ++ipos) { - RBBINode *node = static_cast(sd->fPositions->elementAt(ipos)); - if (node->fType != RBBINode::NodeType::lookAhead) { - continue; - } - int32_t ruleNum = node->fVal; // Set when rule was originally parsed. - int32_t existingVal = fLookAheadRuleMap->elementAti(ruleNum); - (void)existingVal; - U_ASSERT(existingVal == 0 || existingVal == laSlotForState); - fLookAheadRuleMap->setElementAt(laSlotForState, ruleNum); - } - } - -} - -//----------------------------------------------------------------------------- -// -// flagAcceptingStates Identify accepting states. -// First get a list of all of the end marker nodes. -// Then, for each state s, -// if s contains one of the end marker nodes in its list of tree positions then -// s is an accepting state. -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::flagAcceptingStates() { - if (U_FAILURE(*fStatus)) { - return; - } - UVector endMarkerNodes(*fStatus); - RBBINode *endMarker; - int32_t i; - int32_t n; - - if (U_FAILURE(*fStatus)) { - return; - } - - fTree->findNodes(&endMarkerNodes, RBBINode::endMark, *fStatus); - if (U_FAILURE(*fStatus)) { - return; - } - - for (i=0; isize(); n++) { - RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); - if (sd->fPositions->indexOf(endMarker) >= 0) { - // Any non-zero value for fAccepting means this is an accepting node. - // The value is what will be returned to the user as the break status. - // If no other value was specified, force it to ACCEPTING_UNCONDITIONAL (1). - - if (sd->fAccepting==0) { - // State hasn't been marked as accepting yet. Do it now. - sd->fAccepting = fLookAheadRuleMap->elementAti(endMarker->fVal); - if (sd->fAccepting == 0) { - sd->fAccepting = ACCEPTING_UNCONDITIONAL; - } - } - if (sd->fAccepting==ACCEPTING_UNCONDITIONAL && endMarker->fVal != 0) { - // Both lookahead and non-lookahead accepting for this state. - // Favor the look-ahead, because a look-ahead match needs to - // immediately stop the run-time engine. First match, not longest. - sd->fAccepting = fLookAheadRuleMap->elementAti(endMarker->fVal); - } - // implicit else: - // if sd->fAccepting already had a value other than 0 or 1, leave it be. - } - } - } -} - - -//----------------------------------------------------------------------------- -// -// flagLookAheadStates Very similar to flagAcceptingStates, above. -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::flagLookAheadStates() { - if (U_FAILURE(*fStatus)) { - return; - } - UVector lookAheadNodes(*fStatus); - RBBINode *lookAheadNode; - int32_t i; - int32_t n; - - fTree->findNodes(&lookAheadNodes, RBBINode::lookAhead, *fStatus); - if (U_FAILURE(*fStatus)) { - return; - } - for (i=0; ifType == RBBINode::NodeType::lookAhead); - - for (n=0; nsize(); n++) { - RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); - int32_t positionsIdx = sd->fPositions->indexOf(lookAheadNode); - if (positionsIdx >= 0) { - U_ASSERT(lookAheadNode == sd->fPositions->elementAt(positionsIdx)); - uint32_t lookaheadSlot = fLookAheadRuleMap->elementAti(lookAheadNode->fVal); - U_ASSERT(sd->fLookAhead == 0 || sd->fLookAhead == lookaheadSlot); - // if (sd->fLookAhead != 0 && sd->fLookAhead != lookaheadSlot) { - // printf("%s:%d Bingo. sd->fLookAhead:%d lookaheadSlot:%d\n", - // __FILE__, __LINE__, sd->fLookAhead, lookaheadSlot); - // } - sd->fLookAhead = lookaheadSlot; - } - } - } -} - - - - -//----------------------------------------------------------------------------- -// -// flagTaggedStates -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::flagTaggedStates() { - if (U_FAILURE(*fStatus)) { - return; - } - UVector tagNodes(*fStatus); - RBBINode *tagNode; - int32_t i; - int32_t n; - - if (U_FAILURE(*fStatus)) { - return; - } - fTree->findNodes(&tagNodes, RBBINode::tag, *fStatus); - if (U_FAILURE(*fStatus)) { - return; - } - for (i=0; isize(); n++) { // For each state s (row in the state table) - RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); - if (sd->fPositions->indexOf(tagNode) >= 0) { // if s include the tag node t - sortedAdd(&sd->fTagVals, tagNode->fVal); - } - } - } -} - - - - -//----------------------------------------------------------------------------- -// -// mergeRuleStatusVals -// -// Update the global table of rule status {tag} values -// The rule builder has a global vector of status values that are common -// for all tables. Merge the ones from this table into the global set. -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::mergeRuleStatusVals() { - // - // The basic outline of what happens here is this... - // - // for each state in this state table - // if the status tag list for this state is in the global statuses list - // record where and - // continue with the next state - // else - // add the tag list for this state to the global list. - // - int i; - int n; - - // Pre-set a single tag of {0} into the table. - // We will need this as a default, for rule sets with no explicit tagging. - if (fRB->fRuleStatusVals->size() == 0) { - fRB->fRuleStatusVals->addElement(1, *fStatus); // Num of statuses in group - fRB->fRuleStatusVals->addElement((int32_t)0, *fStatus); // and our single status of zero - } - - // For each state - for (n=0; nsize(); n++) { - RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); - UVector *thisStatesTagValues = sd->fTagVals; - if (thisStatesTagValues == NULL) { - // No tag values are explicitly associated with this state. - // Set the default tag value. - sd->fTagsIdx = 0; - continue; - } - - // There are tag(s) associated with this state. - // fTagsIdx will be the index into the global tag list for this state's tag values. - // Initial value of -1 flags that we haven't got it set yet. - sd->fTagsIdx = -1; - int32_t thisTagGroupStart = 0; // indexes into the global rule status vals list - int32_t nextTagGroupStart = 0; - - // Loop runs once per group of tags in the global list - while (nextTagGroupStart < fRB->fRuleStatusVals->size()) { - thisTagGroupStart = nextTagGroupStart; - nextTagGroupStart += fRB->fRuleStatusVals->elementAti(thisTagGroupStart) + 1; - if (thisStatesTagValues->size() != fRB->fRuleStatusVals->elementAti(thisTagGroupStart)) { - // The number of tags for this state is different from - // the number of tags in this group from the global list. - // Continue with the next group from the global list. - continue; - } - // The lengths match, go ahead and compare the actual tag values - // between this state and the group from the global list. - for (i=0; isize(); i++) { - if (thisStatesTagValues->elementAti(i) != - fRB->fRuleStatusVals->elementAti(thisTagGroupStart + 1 + i) ) { - // Mismatch. - break; - } - } - - if (i == thisStatesTagValues->size()) { - // We found a set of tag values in the global list that match - // those for this state. Use them. - sd->fTagsIdx = thisTagGroupStart; - break; - } - } - - if (sd->fTagsIdx == -1) { - // No suitable entry in the global tag list already. Add one - sd->fTagsIdx = fRB->fRuleStatusVals->size(); - fRB->fRuleStatusVals->addElement(thisStatesTagValues->size(), *fStatus); - for (i=0; isize(); i++) { - fRB->fRuleStatusVals->addElement(thisStatesTagValues->elementAti(i), *fStatus); - } - } - } -} - - - - - - - -//----------------------------------------------------------------------------- -// -// sortedAdd Add a value to a vector of sorted values (ints). -// Do not replicate entries; if the value is already there, do not -// add a second one. -// Lazily create the vector if it does not already exist. -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::sortedAdd(UVector **vector, int32_t val) { - int32_t i; - - if (*vector == NULL) { - *vector = new UVector(*fStatus); - } - if (*vector == NULL || U_FAILURE(*fStatus)) { - return; - } - UVector *vec = *vector; - int32_t vSize = vec->size(); - for (i=0; ielementAti(i); - if (valAtI == val) { - // The value is already in the vector. Don't add it again. - return; - } - if (valAtI > val) { - break; - } - } - vec->insertElementAt(val, i, *fStatus); -} - - - -//----------------------------------------------------------------------------- -// -// setAdd Set operation on UVector -// dest = dest union source -// Elements may only appear once and must be sorted. -// -//----------------------------------------------------------------------------- -void RBBITableBuilder::setAdd(UVector *dest, UVector *source) { - U_ASSERT(!dest->hasDeleter()); - U_ASSERT(!source->hasDeleter()); - int32_t destOriginalSize = dest->size(); - int32_t sourceSize = source->size(); - int32_t di = 0; - MaybeStackArray destArray, sourceArray; // Handle small cases without malloc - void **destPtr, **sourcePtr; - void **destLim, **sourceLim; - - if (destOriginalSize > destArray.getCapacity()) { - if (destArray.resize(destOriginalSize) == NULL) { - return; - } - } - destPtr = destArray.getAlias(); - destLim = destPtr + destOriginalSize; // destArray.getArrayLimit()? - - if (sourceSize > sourceArray.getCapacity()) { - if (sourceArray.resize(sourceSize) == NULL) { - return; - } - } - sourcePtr = sourceArray.getAlias(); - sourceLim = sourcePtr + sourceSize; // sourceArray.getArrayLimit()? - - // Avoid multiple "get element" calls by getting the contents into arrays - (void) dest->toArray(destPtr); - (void) source->toArray(sourcePtr); - - dest->setSize(sourceSize+destOriginalSize, *fStatus); - if (U_FAILURE(*fStatus)) { - return; - } - - while (sourcePtr < sourceLim && destPtr < destLim) { - if (*destPtr == *sourcePtr) { - dest->setElementAt(*sourcePtr++, di++); - destPtr++; - } - // This check is required for machines with segmented memory, like i5/OS. - // Direct pointer comparison is not recommended. - else if (uprv_memcmp(destPtr, sourcePtr, sizeof(void *)) < 0) { - dest->setElementAt(*destPtr++, di++); - } - else { /* *sourcePtr < *destPtr */ - dest->setElementAt(*sourcePtr++, di++); - } - } - - // At most one of these two cleanup loops will execute - while (destPtr < destLim) { - dest->setElementAt(*destPtr++, di++); - } - while (sourcePtr < sourceLim) { - dest->setElementAt(*sourcePtr++, di++); - } - - dest->setSize(di, *fStatus); -} - - - -//----------------------------------------------------------------------------- -// -// setEqual Set operation on UVector. -// Compare for equality. -// Elements must be sorted. -// -//----------------------------------------------------------------------------- -UBool RBBITableBuilder::setEquals(UVector *a, UVector *b) { - return a->equals(*b); -} - - -//----------------------------------------------------------------------------- -// -// printPosSets Debug function. Dump Nullable, firstpos, lastpos and followpos -// for each node in the tree. -// -//----------------------------------------------------------------------------- -#ifdef RBBI_DEBUG -void RBBITableBuilder::printPosSets(RBBINode *n) { - if (n==NULL) { - return; - } - printf("\n"); - RBBINode::printNodeHeader(); - RBBINode::printNode(n); - RBBIDebugPrintf(" Nullable: %s\n", n->fNullable?"true":"false"); - - RBBIDebugPrintf(" firstpos: "); - printSet(n->fFirstPosSet); - - RBBIDebugPrintf(" lastpos: "); - printSet(n->fLastPosSet); - - RBBIDebugPrintf(" followpos: "); - printSet(n->fFollowPos); - - printPosSets(n->fLeftChild); - printPosSets(n->fRightChild); -} -#endif - -// -// findDuplCharClassFrom() -// -bool RBBITableBuilder::findDuplCharClassFrom(IntPair *categories) { - int32_t numStates = fDStates->size(); - int32_t numCols = fRB->fSetBuilder->getNumCharCategories(); - - for (; categories->first < numCols-1; categories->first++) { - // Note: dictionary & non-dictionary columns cannot be merged. - // The limitSecond value prevents considering mixed pairs. - // Dictionary categories are >= DictCategoriesStart. - // Non dict categories are < DictCategoriesStart. - int limitSecond = categories->first < fRB->fSetBuilder->getDictCategoriesStart() ? - fRB->fSetBuilder->getDictCategoriesStart() : numCols; - for (categories->second=categories->first+1; categories->second < limitSecond; categories->second++) { - // Initialized to different values to prevent returning true if numStates = 0 (implies no duplicates). - uint16_t table_base = 0; - uint16_t table_dupl = 1; - for (int32_t state=0; stateelementAt(state); - table_base = (uint16_t)sd->fDtran->elementAti(categories->first); - table_dupl = (uint16_t)sd->fDtran->elementAti(categories->second); - if (table_base != table_dupl) { - break; - } - } - if (table_base == table_dupl) { - return true; - } - } - } - return false; -} - - -// -// removeColumn() -// -void RBBITableBuilder::removeColumn(int32_t column) { - int32_t numStates = fDStates->size(); - for (int32_t state=0; stateelementAt(state); - U_ASSERT(column < sd->fDtran->size()); - sd->fDtran->removeElementAt(column); - } -} - -/* - * findDuplicateState - */ -bool RBBITableBuilder::findDuplicateState(IntPair *states) { - int32_t numStates = fDStates->size(); - int32_t numCols = fRB->fSetBuilder->getNumCharCategories(); - - 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) { - continue; - } - bool rowsMatch = true; - for (int32_t col=0; col < numCols; ++col) { - int32_t firstVal = firstSD->fDtran->elementAti(col); - int32_t duplVal = duplSD->fDtran->elementAti(col); - if (!((firstVal == duplVal) || - ((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; - } - } - if (rowsMatch) { - return true; - } - } - } - return false; -} - - -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()); - - RBBIStateDescriptor *duplSD = (RBBIStateDescriptor *)fDStates->elementAt(duplState); - fDStates->removeElementAt(duplState); - delete duplSD; - - int32_t numStates = fDStates->size(); - int32_t numCols = fRB->fSetBuilder->getNumCharCategories(); - for (int32_t state=0; stateelementAt(state); - for (int32_t col=0; colfDtran->elementAti(col); - int32_t newVal = existingVal; - if (existingVal == duplState) { - newVal = keepState; - } else if (existingVal > duplState) { - newVal = existingVal - 1; - } - sd->fDtran->setElementAt(newVal, col); - } - } -} - -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, static_cast(newVal)); - } - } -} - - -/* - * RemoveDuplicateStates - */ -int32_t RBBITableBuilder::removeDuplicateStates() { - IntPair dupls = {3, 0}; - int32_t numStatesRemoved = 0; - - while (findDuplicateState(&dupls)) { - // printf("Removing duplicate states (%d, %d)\n", dupls.first, dupls.second); - removeState(dupls); - ++numStatesRemoved; - } - return numStatesRemoved; -} - - -//----------------------------------------------------------------------------- -// -// getTableSize() Calculate the size of the runtime form of this -// state transition table. -// -//----------------------------------------------------------------------------- -int32_t RBBITableBuilder::getTableSize() const { - int32_t size = 0; - int32_t numRows; - int32_t numCols; - int32_t rowSize; - - if (fTree == NULL) { - return 0; - } - - size = offsetof(RBBIStateTable, fTableData); // The header, with no rows to the table. - - numRows = fDStates->size(); - numCols = fRB->fSetBuilder->getNumCharCategories(); - - if (use8BitsForTable()) { - rowSize = offsetof(RBBIStateTableRow8, fNextState) + sizeof(int8_t)*numCols; - } else { - rowSize = offsetof(RBBIStateTableRow16, fNextState) + sizeof(int16_t)*numCols; - } - size += numRows * rowSize; - return size; -} - -bool RBBITableBuilder::use8BitsForTable() const { - return fDStates->size() <= kMaxStateFor8BitsTable; -} - -//----------------------------------------------------------------------------- -// -// exportTable() 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::exportTable(void *where) { - RBBIStateTable *table = (RBBIStateTable *)where; - uint32_t state; - int col; - - if (U_FAILURE(*fStatus) || fTree == NULL) { - return; - } - - int32_t catCount = fRB->fSetBuilder->getNumCharCategories(); - if (catCount > 0x7fff || - fDStates->size() > 0x7fff) { - *fStatus = U_BRK_INTERNAL_ERROR; - return; - } - - table->fNumStates = fDStates->size(); - table->fDictCategoriesStart = fRB->fSetBuilder->getDictCategoriesStart(); - table->fLookAheadResultsSize = fLASlotsInUse == ACCEPTING_UNCONDITIONAL ? 0 : fLASlotsInUse + 1; - table->fFlags = 0; - if (use8BitsForTable()) { - table->fRowLen = offsetof(RBBIStateTableRow8, fNextState) + sizeof(uint8_t) * catCount; - table->fFlags |= RBBI_8BITS_ROWS; - } else { - table->fRowLen = offsetof(RBBIStateTableRow16, fNextState) + sizeof(int16_t) * catCount; - } - if (fRB->fLookAheadHardBreak) { - table->fFlags |= RBBI_LOOKAHEAD_HARD_BREAK; - } - if (fRB->fSetBuilder->sawBOF()) { - table->fFlags |= RBBI_BOF_REQUIRED; - } - - for (state=0; statefNumStates; state++) { - RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(state); - RBBIStateTableRow *row = (RBBIStateTableRow *)(table->fTableData + state*table->fRowLen); - if (use8BitsForTable()) { - U_ASSERT (sd->fAccepting <= 255); - U_ASSERT (sd->fLookAhead <= 255); - U_ASSERT (0 <= sd->fTagsIdx && sd->fTagsIdx <= 255); - RBBIStateTableRow8 *r8 = (RBBIStateTableRow8*)row; - r8->fAccepting = sd->fAccepting; - r8->fLookAhead = sd->fLookAhead; - r8->fTagsIdx = sd->fTagsIdx; - for (col=0; colfDtran->elementAti(col) <= kMaxStateFor8BitsTable); - r8->fNextState[col] = sd->fDtran->elementAti(col); - } - } else { - U_ASSERT (sd->fAccepting <= 0xffff); - U_ASSERT (sd->fLookAhead <= 0xffff); - U_ASSERT (0 <= sd->fTagsIdx && sd->fTagsIdx <= 0xffff); - row->r16.fAccepting = sd->fAccepting; - row->r16.fLookAhead = sd->fLookAhead; - row->r16.fTagsIdx = sd->fTagsIdx; - for (col=0; colr16.fNextState[col] = sd->fDtran->elementAti(col); - } - } - } -} - - -/** - * Synthesize a safe state table from the main state table. - */ -void RBBITableBuilder::buildSafeReverseTable(UErrorCode &status) { - // The safe table creation has three steps: - - // 1. Identify 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 character 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 state. - // 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); - LocalPointer lpSafeTable( - new UVector(uprv_deleteUObject, uhash_compareUnicodeString, numCharClasses + 2, status), status); - if (U_FAILURE(status)) { - return; - } - fSafeTable = lpSafeTable.orphan(); - for (int32_t row=0; row lpString(new UnicodeString(numCharClasses, 0, numCharClasses+4), status); - fSafeTable->adoptElement(lpString.orphan(), status); - } - if (U_FAILURE(status)) { - return; - } - - // 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, static_cast(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(); - - if (use8BitsForSafeTable()) { - rowSize = offsetof(RBBIStateTableRow8, fNextState) + sizeof(int8_t)*numCols; - } else { - rowSize = offsetof(RBBIStateTableRow16, fNextState) + sizeof(int16_t)*numCols; - } - size += numRows * rowSize; - return size; -} - -bool RBBITableBuilder::use8BitsForSafeTable() const { - return fSafeTable->size() <= kMaxStateFor8BitsTable; -} - -//----------------------------------------------------------------------------- -// -// 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->fNumStates = fSafeTable->size(); - table->fFlags = 0; - if (use8BitsForSafeTable()) { - table->fRowLen = offsetof(RBBIStateTableRow8, fNextState) + sizeof(uint8_t) * catCount; - table->fFlags |= RBBI_8BITS_ROWS; - } else { - table->fRowLen = offsetof(RBBIStateTableRow16, fNextState) + sizeof(int16_t) * catCount; - } - - for (state=0; statefNumStates; state++) { - UnicodeString *rowString = (UnicodeString *)fSafeTable->elementAt(state); - RBBIStateTableRow *row = (RBBIStateTableRow *)(table->fTableData + state*table->fRowLen); - if (use8BitsForSafeTable()) { - RBBIStateTableRow8 *r8 = (RBBIStateTableRow8*)row; - r8->fAccepting = 0; - r8->fLookAhead = 0; - r8->fTagsIdx = 0; - for (col=0; colcharAt(col) <= kMaxStateFor8BitsTable); - r8->fNextState[col] = static_cast(rowString->charAt(col)); - } - } else { - row->r16.fAccepting = 0; - row->r16.fLookAhead = 0; - row->r16.fTagsIdx = 0; - for (col=0; colr16.fNextState[col] = rowString->charAt(col); - } - } - } -} - - - - -//----------------------------------------------------------------------------- -// -// printSet Debug function. Print the contents of a UVector -// -//----------------------------------------------------------------------------- -#ifdef RBBI_DEBUG -void RBBITableBuilder::printSet(UVector *s) { - int32_t i; - for (i=0; isize(); i++) { - const RBBINode *v = static_cast(s->elementAt(i)); - RBBIDebugPrintf("%5d", v==NULL? -1 : v->fSerialNum); - } - RBBIDebugPrintf("\n"); -} -#endif - - -//----------------------------------------------------------------------------- -// -// printStates Debug Function. Dump the fully constructed state transition table. -// -//----------------------------------------------------------------------------- -#ifdef RBBI_DEBUG -void RBBITableBuilder::printStates() { - int c; // input "character" - int n; // state number - - 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(" %3d", c); - } - RBBIDebugPrintf("\n"); - RBBIDebugPrintf(" |---------------"); - for (c=0; cfSetBuilder->getNumCharCategories(); c++) { - RBBIDebugPrintf("----"); - } - RBBIDebugPrintf("\n"); - - for (n=0; nsize(); n++) { - RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); - RBBIDebugPrintf(" %3d | " , n); - RBBIDebugPrintf("%3d %3d %5d ", sd->fAccepting, sd->fLookAhead, sd->fTagsIdx); - for (c=0; cfSetBuilder->getNumCharCategories(); c++) { - RBBIDebugPrintf(" %3d", sd->fDtran->elementAti(c)); - } - RBBIDebugPrintf("\n"); - } - RBBIDebugPrintf("\n\n"); -} -#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 - - - -//----------------------------------------------------------------------------- -// -// printRuleStatusTable Debug Function. Dump the common rule status table -// -//----------------------------------------------------------------------------- -#ifdef RBBI_DEBUG -void RBBITableBuilder::printRuleStatusTable() { - int32_t thisRecord = 0; - int32_t nextRecord = 0; - int i; - UVector *tbl = fRB->fRuleStatusVals; - - RBBIDebugPrintf("index | tags \n"); - RBBIDebugPrintf("-------------------\n"); - - while (nextRecord < tbl->size()) { - thisRecord = nextRecord; - nextRecord = thisRecord + tbl->elementAti(thisRecord) + 1; - RBBIDebugPrintf("%4d ", thisRecord); - for (i=thisRecord+1; ielementAti(i)); - } - RBBIDebugPrintf("\n"); - } - RBBIDebugPrintf("\n\n"); -} -#endif - - -//----------------------------------------------------------------------------- -// -// RBBIStateDescriptor Methods. This is a very struct-like class -// Most access is directly to the fields. -// -//----------------------------------------------------------------------------- - -RBBIStateDescriptor::RBBIStateDescriptor(int lastInputSymbol, UErrorCode *fStatus) { - fMarked = false; - fAccepting = 0; - fLookAhead = 0; - fTagsIdx = 0; - fTagVals = NULL; - fPositions = NULL; - fDtran = NULL; - - fDtran = new UVector32(lastInputSymbol+1, *fStatus); - if (U_FAILURE(*fStatus)) { - return; - } - if (fDtran == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - fDtran->setSize(lastInputSymbol+1); // fDtran needs to be pre-sized. - // It is indexed by input symbols, and will - // hold the next state number for each - // symbol. -} - - -RBBIStateDescriptor::~RBBIStateDescriptor() { - delete fPositions; - delete fDtran; - delete fTagVals; - fPositions = NULL; - fDtran = NULL; - fTagVals = NULL; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ +// +// rbbitblb.cpp +// + + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/unistr.h" +#include "rbbitblb.h" +#include "rbbirb.h" +#include "rbbiscan.h" +#include "rbbisetb.h" +#include "rbbidata.h" +#include "cstring.h" +#include "uassert.h" +#include "uvectr32.h" +#include "cmemory.h" + +U_NAMESPACE_BEGIN + +const int32_t kMaxStateFor8BitsTable = 255; + +RBBITableBuilder::RBBITableBuilder(RBBIRuleBuilder *rb, RBBINode **rootNode, UErrorCode &status) : + fRB(rb), + fTree(*rootNode), + fStatus(&status), + fDStates(nullptr), + fSafeTable(nullptr) { + if (U_FAILURE(status)) { + return; + } + // fDStates is UVector + fDStates = new UVector(status); + if (U_SUCCESS(status) && fDStates == nullptr ) { + status = U_MEMORY_ALLOCATION_ERROR; + } +} + + + +RBBITableBuilder::~RBBITableBuilder() { + int i; + for (i=0; isize(); i++) { + delete (RBBIStateDescriptor *)fDStates->elementAt(i); + } + delete fDStates; + delete fSafeTable; + delete fLookAheadRuleMap; +} + + +//----------------------------------------------------------------------------- +// +// RBBITableBuilder::buildForwardTable - This is the main function for building +// the DFA state transition table from the RBBI rules parse tree. +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::buildForwardTable() { + + if (U_FAILURE(*fStatus)) { + return; + } + + // If there were no rules, just return. This situation can easily arise + // for the reverse rules. + if (fTree==nullptr) { + return; + } + + // + // Walk through the tree, replacing any references to $variables with a copy of the + // parse tree for the substitution expression. + // + fTree = fTree->flattenVariables(); +#ifdef RBBI_DEBUG + if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "ftree")) { + RBBIDebugPuts("\nParse tree after flattening variable references."); + RBBINode::printTree(fTree, true); + } +#endif + + // + // If the rules contained any references to {bof} + // add a {bof} to the + // tree. Means that all matches must start out with the + // {bof} fake character. + // + if (fRB->fSetBuilder->sawBOF()) { + RBBINode *bofTop = new RBBINode(RBBINode::opCat); + RBBINode *bofLeaf = new RBBINode(RBBINode::leafChar); + // Delete and exit if memory allocation failed. + if (bofTop == nullptr || bofLeaf == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + delete bofTop; + delete bofLeaf; + return; + } + bofTop->fLeftChild = bofLeaf; + bofTop->fRightChild = fTree; + bofLeaf->fParent = bofTop; + bofLeaf->fVal = 2; // Reserved value for {bof}. + fTree = bofTop; + } + + // + // Add a unique right-end marker to the expression. + // Appears as a cat-node, left child being the original tree, + // right child being the end marker. + // + RBBINode *cn = new RBBINode(RBBINode::opCat); + // Exit if memory allocation failed. + if (cn == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + return; + } + cn->fLeftChild = fTree; + fTree->fParent = cn; + RBBINode *endMarkerNode = cn->fRightChild = new RBBINode(RBBINode::endMark); + // Delete and exit if memory allocation failed. + if (cn->fRightChild == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + delete cn; + return; + } + cn->fRightChild->fParent = cn; + fTree = cn; + + // + // Replace all references to UnicodeSets with the tree for the equivalent + // expression. + // + fTree->flattenSets(); +#ifdef RBBI_DEBUG + if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "stree")) { + RBBIDebugPuts("\nParse tree after flattening Unicode Set references."); + RBBINode::printTree(fTree, true); + } +#endif + + + // + // calculate the functions nullable, firstpos, lastpos and followpos on + // nodes in the parse tree. + // See the algorithm description in Aho. + // Understanding how this works by looking at the code alone will be + // nearly impossible. + // + calcNullable(fTree); + calcFirstPos(fTree); + calcLastPos(fTree); + calcFollowPos(fTree); + if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "pos")) { + RBBIDebugPuts("\n"); + printPosSets(fTree); + } + + // + // For "chained" rules, modify the followPos sets + // + if (fRB->fChainRules) { + calcChainedFollowPos(fTree, endMarkerNode); + } + + // + // BOF (start of input) test fixup. + // + if (fRB->fSetBuilder->sawBOF()) { + bofFixup(); + } + + // + // Build the DFA state transition tables. + // + buildStateTable(); + mapLookAheadRules(); + flagAcceptingStates(); + flagLookAheadStates(); + flagTaggedStates(); + + // + // Update the global table of rule status {tag} values + // The rule builder has a global vector of status values that are common + // for all tables. Merge the ones from this table into the global set. + // + mergeRuleStatusVals(); +} + + + +//----------------------------------------------------------------------------- +// +// calcNullable. Impossible to explain succinctly. See Aho, section 3.9 +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::calcNullable(RBBINode *n) { + if (n == nullptr) { + return; + } + if (n->fType == RBBINode::setRef || + n->fType == RBBINode::endMark ) { + // These are non-empty leaf node types. + n->fNullable = false; + return; + } + + if (n->fType == RBBINode::lookAhead || n->fType == RBBINode::tag) { + // Lookahead marker node. It's a leaf, so no recursion on children. + // It's nullable because it does not match any literal text from the input stream. + n->fNullable = true; + return; + } + + + // The node is not a leaf. + // Calculate nullable on its children. + calcNullable(n->fLeftChild); + calcNullable(n->fRightChild); + + // Apply functions from table 3.40 in Aho + if (n->fType == RBBINode::opOr) { + n->fNullable = n->fLeftChild->fNullable || n->fRightChild->fNullable; + } + else if (n->fType == RBBINode::opCat) { + n->fNullable = n->fLeftChild->fNullable && n->fRightChild->fNullable; + } + else if (n->fType == RBBINode::opStar || n->fType == RBBINode::opQuestion) { + n->fNullable = true; + } + else { + n->fNullable = false; + } +} + + + + +//----------------------------------------------------------------------------- +// +// calcFirstPos. Impossible to explain succinctly. See Aho, section 3.9 +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::calcFirstPos(RBBINode *n) { + if (n == nullptr) { + return; + } + if (n->fType == RBBINode::leafChar || + n->fType == RBBINode::endMark || + n->fType == RBBINode::lookAhead || + n->fType == RBBINode::tag) { + // These are non-empty leaf node types. + // Note: In order to maintain the sort invariant on the set, + // this function should only be called on a node whose set is + // empty to start with. + n->fFirstPosSet->addElement(n, *fStatus); + return; + } + + // The node is not a leaf. + // Calculate firstPos on its children. + calcFirstPos(n->fLeftChild); + calcFirstPos(n->fRightChild); + + // Apply functions from table 3.40 in Aho + if (n->fType == RBBINode::opOr) { + setAdd(n->fFirstPosSet, n->fLeftChild->fFirstPosSet); + setAdd(n->fFirstPosSet, n->fRightChild->fFirstPosSet); + } + else if (n->fType == RBBINode::opCat) { + setAdd(n->fFirstPosSet, n->fLeftChild->fFirstPosSet); + if (n->fLeftChild->fNullable) { + setAdd(n->fFirstPosSet, n->fRightChild->fFirstPosSet); + } + } + else if (n->fType == RBBINode::opStar || + n->fType == RBBINode::opQuestion || + n->fType == RBBINode::opPlus) { + setAdd(n->fFirstPosSet, n->fLeftChild->fFirstPosSet); + } +} + + + +//----------------------------------------------------------------------------- +// +// calcLastPos. Impossible to explain succinctly. See Aho, section 3.9 +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::calcLastPos(RBBINode *n) { + if (n == nullptr) { + return; + } + if (n->fType == RBBINode::leafChar || + n->fType == RBBINode::endMark || + n->fType == RBBINode::lookAhead || + n->fType == RBBINode::tag) { + // These are non-empty leaf node types. + // Note: In order to maintain the sort invariant on the set, + // this function should only be called on a node whose set is + // empty to start with. + n->fLastPosSet->addElement(n, *fStatus); + return; + } + + // The node is not a leaf. + // Calculate lastPos on its children. + calcLastPos(n->fLeftChild); + calcLastPos(n->fRightChild); + + // Apply functions from table 3.40 in Aho + if (n->fType == RBBINode::opOr) { + setAdd(n->fLastPosSet, n->fLeftChild->fLastPosSet); + setAdd(n->fLastPosSet, n->fRightChild->fLastPosSet); + } + else if (n->fType == RBBINode::opCat) { + setAdd(n->fLastPosSet, n->fRightChild->fLastPosSet); + if (n->fRightChild->fNullable) { + setAdd(n->fLastPosSet, n->fLeftChild->fLastPosSet); + } + } + else if (n->fType == RBBINode::opStar || + n->fType == RBBINode::opQuestion || + n->fType == RBBINode::opPlus) { + setAdd(n->fLastPosSet, n->fLeftChild->fLastPosSet); + } +} + + + +//----------------------------------------------------------------------------- +// +// calcFollowPos. Impossible to explain succinctly. See Aho, section 3.9 +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::calcFollowPos(RBBINode *n) { + if (n == nullptr || + n->fType == RBBINode::leafChar || + n->fType == RBBINode::endMark) { + return; + } + + calcFollowPos(n->fLeftChild); + calcFollowPos(n->fRightChild); + + // Aho rule #1 + if (n->fType == RBBINode::opCat) { + RBBINode *i; // is 'i' in Aho's description + uint32_t ix; + + UVector *LastPosOfLeftChild = n->fLeftChild->fLastPosSet; + + for (ix=0; ix<(uint32_t)LastPosOfLeftChild->size(); ix++) { + i = (RBBINode *)LastPosOfLeftChild->elementAt(ix); + setAdd(i->fFollowPos, n->fRightChild->fFirstPosSet); + } + } + + // Aho rule #2 + if (n->fType == RBBINode::opStar || + n->fType == RBBINode::opPlus) { + RBBINode *i; // again, n and i are the names from Aho's description. + uint32_t ix; + + for (ix=0; ix<(uint32_t)n->fLastPosSet->size(); ix++) { + i = (RBBINode *)n->fLastPosSet->elementAt(ix); + setAdd(i->fFollowPos, n->fFirstPosSet); + } + } + + + +} + +//----------------------------------------------------------------------------- +// +// addRuleRootNodes Recursively walk a parse tree, adding all nodes flagged +// as roots of a rule to a destination vector. +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::addRuleRootNodes(UVector *dest, RBBINode *node) { + if (node == nullptr || U_FAILURE(*fStatus)) { + return; + } + U_ASSERT(!dest->hasDeleter()); + if (node->fRuleRoot) { + dest->addElement(node, *fStatus); + // Note: rules cannot nest. If we found a rule start node, + // no child node can also be a start node. + return; + } + addRuleRootNodes(dest, node->fLeftChild); + addRuleRootNodes(dest, node->fRightChild); +} + +//----------------------------------------------------------------------------- +// +// calcChainedFollowPos. Modify the previously calculated followPos sets +// to implement rule chaining. NOT described by Aho +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::calcChainedFollowPos(RBBINode *tree, RBBINode *endMarkNode) { + + UVector leafNodes(*fStatus); + if (U_FAILURE(*fStatus)) { + return; + } + + // get a list all leaf nodes + tree->findNodes(&leafNodes, RBBINode::leafChar, *fStatus); + if (U_FAILURE(*fStatus)) { + return; + } + + // Collect all leaf nodes that can start matches for rules + // with inbound chaining enabled, which is the union of the + // firstPosition sets from each of the rule root nodes. + + UVector ruleRootNodes(*fStatus); + addRuleRootNodes(&ruleRootNodes, tree); + + UVector matchStartNodes(*fStatus); + for (int j=0; j(ruleRootNodes.elementAt(j)); + if (node->fChainIn) { + setAdd(&matchStartNodes, node->fFirstPosSet); + } + } + if (U_FAILURE(*fStatus)) { + return; + } + + int32_t endNodeIx; + int32_t startNodeIx; + + for (endNodeIx=0; endNodeIxfFollowPos->contains(endMarkNode)) { + continue; + } + + // We've got a node that can end a match. + + // !!LBCMNoChain implementation: If this node's val correspond to + // the Line Break $CM char class, don't chain from it. + // TODO: Remove this. !!LBCMNoChain is deprecated, and is not used + // by any of the standard ICU rules. + if (fRB->fLBCMNoChain) { + UChar32 c = this->fRB->fSetBuilder->getFirstChar(endNode->fVal); + if (c != -1) { + // c == -1 occurs with sets containing only the {eof} marker string. + ULineBreak cLBProp = (ULineBreak)u_getIntPropertyValue(c, UCHAR_LINE_BREAK); + if (cLBProp == U_LB_COMBINING_MARK) { + continue; + } + } + } + + // Now iterate over the nodes that can start a match, looking for ones + // with the same char class as our ending node. + RBBINode *startNode; + for (startNodeIx = 0; startNodeIxfType != RBBINode::leafChar) { + continue; + } + + if (endNode->fVal == startNode->fVal) { + // The end val (character class) of one possible match is the + // same as the start of another. + + // Add all nodes from the followPos of the start node to the + // followPos set of the end node, which will have the effect of + // letting matches transition from a match state at endNode + // to the second char of a match starting with startNode. + setAdd(endNode->fFollowPos, startNode->fFollowPos); + } + } + } +} + + +//----------------------------------------------------------------------------- +// +// bofFixup. Fixup for state tables that include {bof} beginning of input testing. +// Do an swizzle similar to chaining, modifying the followPos set of +// the bofNode to include the followPos nodes from other {bot} nodes +// scattered through the tree. +// +// This function has much in common with calcChainedFollowPos(). +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::bofFixup() { + + if (U_FAILURE(*fStatus)) { + return; + } + + // The parse tree looks like this ... + // fTree root ---> + // / \ . + // <#end node> + // / \ . + // rest + // of tree + // + // We will be adding things to the followPos set of the + // + RBBINode *bofNode = fTree->fLeftChild->fLeftChild; + U_ASSERT(bofNode->fType == RBBINode::leafChar); + U_ASSERT(bofNode->fVal == 2); + + // Get all nodes that can be the start a match of the user-written rules + // (excluding the fake bofNode) + // We want the nodes that can start a match in the + // part labeled "rest of tree" + // + UVector *matchStartNodes = fTree->fLeftChild->fRightChild->fFirstPosSet; + + RBBINode *startNode; + int startNodeIx; + for (startNodeIx = 0; startNodeIxsize(); startNodeIx++) { + startNode = (RBBINode *)matchStartNodes->elementAt(startNodeIx); + if (startNode->fType != RBBINode::leafChar) { + continue; + } + + if (startNode->fVal == bofNode->fVal) { + // We found a leaf node corresponding to a {bof} that was + // explicitly written into a rule. + // Add everything from the followPos set of this node to the + // followPos set of the fake bofNode at the start of the tree. + // + setAdd(bofNode->fFollowPos, startNode->fFollowPos); + } + } +} + +//----------------------------------------------------------------------------- +// +// buildStateTable() Determine the set of runtime DFA states and the +// transition tables for these states, by the algorithm +// of fig. 3.44 in Aho. +// +// Most of the comments are quotes of Aho's psuedo-code. +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::buildStateTable() { + if (U_FAILURE(*fStatus)) { + return; + } + RBBIStateDescriptor *failState; + // Set it to nullptr to avoid uninitialized warning + RBBIStateDescriptor *initialState = nullptr; + // + // Add a dummy state 0 - the stop state. Not from Aho. + int lastInputSymbol = fRB->fSetBuilder->getNumCharCategories() - 1; + failState = new RBBIStateDescriptor(lastInputSymbol, fStatus); + if (failState == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + goto ExitBuildSTdeleteall; + } + failState->fPositions = new UVector(*fStatus); + if (failState->fPositions == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + } + if (failState->fPositions == nullptr || U_FAILURE(*fStatus)) { + goto ExitBuildSTdeleteall; + } + fDStates->addElement(failState, *fStatus); + if (U_FAILURE(*fStatus)) { + goto ExitBuildSTdeleteall; + } + + // initially, the only unmarked state in Dstates is firstpos(root), + // where toot is the root of the syntax tree for (r)#; + initialState = new RBBIStateDescriptor(lastInputSymbol, fStatus); + if (initialState == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + } + if (U_FAILURE(*fStatus)) { + goto ExitBuildSTdeleteall; + } + initialState->fPositions = new UVector(*fStatus); + if (initialState->fPositions == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + } + if (U_FAILURE(*fStatus)) { + goto ExitBuildSTdeleteall; + } + setAdd(initialState->fPositions, fTree->fFirstPosSet); + fDStates->addElement(initialState, *fStatus); + if (U_FAILURE(*fStatus)) { + goto ExitBuildSTdeleteall; + } + + // while there is an unmarked state T in Dstates do begin + for (;;) { + RBBIStateDescriptor *T = nullptr; + int32_t tx; + for (tx=1; txsize(); tx++) { + RBBIStateDescriptor *temp; + temp = (RBBIStateDescriptor *)fDStates->elementAt(tx); + if (temp->fMarked == false) { + T = temp; + break; + } + } + if (T == nullptr) { + break; + } + + // mark T; + T->fMarked = true; + + // for each input symbol a do begin + int32_t a; + for (a = 1; a<=lastInputSymbol; a++) { + // let U be the set of positions that are in followpos(p) + // for some position p in T + // such that the symbol at position p is a; + UVector *U = nullptr; + RBBINode *p; + int32_t px; + for (px=0; pxfPositions->size(); px++) { + p = (RBBINode *)T->fPositions->elementAt(px); + if ((p->fType == RBBINode::leafChar) && (p->fVal == a)) { + if (U == nullptr) { + U = new UVector(*fStatus); + if (U == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + goto ExitBuildSTdeleteall; + } + } + setAdd(U, p->fFollowPos); + } + } + + // if U is not empty and not in DStates then + int32_t ux = 0; + UBool UinDstates = false; + if (U != nullptr) { + U_ASSERT(U->size() > 0); + int ix; + for (ix=0; ixsize(); ix++) { + RBBIStateDescriptor *temp2; + temp2 = (RBBIStateDescriptor *)fDStates->elementAt(ix); + if (setEquals(U, temp2->fPositions)) { + delete U; + U = temp2->fPositions; + ux = ix; + UinDstates = true; + break; + } + } + + // Add U as an unmarked state to Dstates + if (!UinDstates) + { + RBBIStateDescriptor *newState = new RBBIStateDescriptor(lastInputSymbol, fStatus); + if (newState == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + } + if (U_FAILURE(*fStatus)) { + goto ExitBuildSTdeleteall; + } + newState->fPositions = U; + fDStates->addElement(newState, *fStatus); + if (U_FAILURE(*fStatus)) { + return; + } + ux = fDStates->size()-1; + } + + // Dtran[T, a] := U; + T->fDtran->setElementAt(ux, a); + } + } + } + return; + // delete local pointers only if error occurred. +ExitBuildSTdeleteall: + delete initialState; + delete failState; +} + + +/** + * mapLookAheadRules + * + */ +void RBBITableBuilder::mapLookAheadRules() { + fLookAheadRuleMap = new UVector32(fRB->fScanner->numRules() + 1, *fStatus); + if (fLookAheadRuleMap == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + } + if (U_FAILURE(*fStatus)) { + return; + } + fLookAheadRuleMap->setSize(fRB->fScanner->numRules() + 1); + + for (int32_t n=0; nsize(); n++) { + RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); + int32_t laSlotForState = 0; + + // Establish the look-ahead slot for this state, if the state covers + // any look-ahead nodes - corresponding to the '/' in look-ahead rules. + + // If any of the look-ahead nodes already have a slot assigned, use it, + // otherwise assign a new one. + + bool sawLookAheadNode = false; + for (int32_t ipos=0; iposfPositions->size(); ++ipos) { + RBBINode *node = static_cast(sd->fPositions->elementAt(ipos)); + if (node->fType != RBBINode::NodeType::lookAhead) { + continue; + } + sawLookAheadNode = true; + int32_t ruleNum = node->fVal; // Set when rule was originally parsed. + U_ASSERT(ruleNum < fLookAheadRuleMap->size()); + U_ASSERT(ruleNum > 0); + int32_t laSlot = fLookAheadRuleMap->elementAti(ruleNum); + if (laSlot != 0) { + if (laSlotForState == 0) { + laSlotForState = laSlot; + } else { + // TODO: figure out if this can fail, change to setting an error code if so. + U_ASSERT(laSlot == laSlotForState); + } + } + } + if (!sawLookAheadNode) { + continue; + } + + if (laSlotForState == 0) { + laSlotForState = ++fLASlotsInUse; + } + + // For each look ahead node covered by this state, + // set the mapping from the node's rule number to the look ahead slot. + // There can be multiple nodes/rule numbers going to the same la slot. + + for (int32_t ipos=0; iposfPositions->size(); ++ipos) { + RBBINode *node = static_cast(sd->fPositions->elementAt(ipos)); + if (node->fType != RBBINode::NodeType::lookAhead) { + continue; + } + int32_t ruleNum = node->fVal; // Set when rule was originally parsed. + int32_t existingVal = fLookAheadRuleMap->elementAti(ruleNum); + (void)existingVal; + U_ASSERT(existingVal == 0 || existingVal == laSlotForState); + fLookAheadRuleMap->setElementAt(laSlotForState, ruleNum); + } + } + +} + +//----------------------------------------------------------------------------- +// +// flagAcceptingStates Identify accepting states. +// First get a list of all of the end marker nodes. +// Then, for each state s, +// if s contains one of the end marker nodes in its list of tree positions then +// s is an accepting state. +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::flagAcceptingStates() { + if (U_FAILURE(*fStatus)) { + return; + } + UVector endMarkerNodes(*fStatus); + RBBINode *endMarker; + int32_t i; + int32_t n; + + if (U_FAILURE(*fStatus)) { + return; + } + + fTree->findNodes(&endMarkerNodes, RBBINode::endMark, *fStatus); + if (U_FAILURE(*fStatus)) { + return; + } + + for (i=0; isize(); n++) { + RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); + if (sd->fPositions->indexOf(endMarker) >= 0) { + // Any non-zero value for fAccepting means this is an accepting node. + // The value is what will be returned to the user as the break status. + // If no other value was specified, force it to ACCEPTING_UNCONDITIONAL (1). + + if (sd->fAccepting==0) { + // State hasn't been marked as accepting yet. Do it now. + sd->fAccepting = fLookAheadRuleMap->elementAti(endMarker->fVal); + if (sd->fAccepting == 0) { + sd->fAccepting = ACCEPTING_UNCONDITIONAL; + } + } + if (sd->fAccepting==ACCEPTING_UNCONDITIONAL && endMarker->fVal != 0) { + // Both lookahead and non-lookahead accepting for this state. + // Favor the look-ahead, because a look-ahead match needs to + // immediately stop the run-time engine. First match, not longest. + sd->fAccepting = fLookAheadRuleMap->elementAti(endMarker->fVal); + } + // implicit else: + // if sd->fAccepting already had a value other than 0 or 1, leave it be. + } + } + } +} + + +//----------------------------------------------------------------------------- +// +// flagLookAheadStates Very similar to flagAcceptingStates, above. +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::flagLookAheadStates() { + if (U_FAILURE(*fStatus)) { + return; + } + UVector lookAheadNodes(*fStatus); + RBBINode *lookAheadNode; + int32_t i; + int32_t n; + + fTree->findNodes(&lookAheadNodes, RBBINode::lookAhead, *fStatus); + if (U_FAILURE(*fStatus)) { + return; + } + for (i=0; ifType == RBBINode::NodeType::lookAhead); + + for (n=0; nsize(); n++) { + RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); + int32_t positionsIdx = sd->fPositions->indexOf(lookAheadNode); + if (positionsIdx >= 0) { + U_ASSERT(lookAheadNode == sd->fPositions->elementAt(positionsIdx)); + uint32_t lookaheadSlot = fLookAheadRuleMap->elementAti(lookAheadNode->fVal); + U_ASSERT(sd->fLookAhead == 0 || sd->fLookAhead == lookaheadSlot); + // if (sd->fLookAhead != 0 && sd->fLookAhead != lookaheadSlot) { + // printf("%s:%d Bingo. sd->fLookAhead:%d lookaheadSlot:%d\n", + // __FILE__, __LINE__, sd->fLookAhead, lookaheadSlot); + // } + sd->fLookAhead = lookaheadSlot; + } + } + } +} + + + + +//----------------------------------------------------------------------------- +// +// flagTaggedStates +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::flagTaggedStates() { + if (U_FAILURE(*fStatus)) { + return; + } + UVector tagNodes(*fStatus); + RBBINode *tagNode; + int32_t i; + int32_t n; + + if (U_FAILURE(*fStatus)) { + return; + } + fTree->findNodes(&tagNodes, RBBINode::tag, *fStatus); + if (U_FAILURE(*fStatus)) { + return; + } + for (i=0; isize(); n++) { // For each state s (row in the state table) + RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); + if (sd->fPositions->indexOf(tagNode) >= 0) { // if s include the tag node t + sortedAdd(&sd->fTagVals, tagNode->fVal); + } + } + } +} + + + + +//----------------------------------------------------------------------------- +// +// mergeRuleStatusVals +// +// Update the global table of rule status {tag} values +// The rule builder has a global vector of status values that are common +// for all tables. Merge the ones from this table into the global set. +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::mergeRuleStatusVals() { + // + // The basic outline of what happens here is this... + // + // for each state in this state table + // if the status tag list for this state is in the global statuses list + // record where and + // continue with the next state + // else + // add the tag list for this state to the global list. + // + int i; + int n; + + // Pre-set a single tag of {0} into the table. + // We will need this as a default, for rule sets with no explicit tagging. + if (fRB->fRuleStatusVals->size() == 0) { + fRB->fRuleStatusVals->addElement(1, *fStatus); // Num of statuses in group + fRB->fRuleStatusVals->addElement((int32_t)0, *fStatus); // and our single status of zero + } + + // For each state + for (n=0; nsize(); n++) { + RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); + UVector *thisStatesTagValues = sd->fTagVals; + if (thisStatesTagValues == nullptr) { + // No tag values are explicitly associated with this state. + // Set the default tag value. + sd->fTagsIdx = 0; + continue; + } + + // There are tag(s) associated with this state. + // fTagsIdx will be the index into the global tag list for this state's tag values. + // Initial value of -1 flags that we haven't got it set yet. + sd->fTagsIdx = -1; + int32_t thisTagGroupStart = 0; // indexes into the global rule status vals list + int32_t nextTagGroupStart = 0; + + // Loop runs once per group of tags in the global list + while (nextTagGroupStart < fRB->fRuleStatusVals->size()) { + thisTagGroupStart = nextTagGroupStart; + nextTagGroupStart += fRB->fRuleStatusVals->elementAti(thisTagGroupStart) + 1; + if (thisStatesTagValues->size() != fRB->fRuleStatusVals->elementAti(thisTagGroupStart)) { + // The number of tags for this state is different from + // the number of tags in this group from the global list. + // Continue with the next group from the global list. + continue; + } + // The lengths match, go ahead and compare the actual tag values + // between this state and the group from the global list. + for (i=0; isize(); i++) { + if (thisStatesTagValues->elementAti(i) != + fRB->fRuleStatusVals->elementAti(thisTagGroupStart + 1 + i) ) { + // Mismatch. + break; + } + } + + if (i == thisStatesTagValues->size()) { + // We found a set of tag values in the global list that match + // those for this state. Use them. + sd->fTagsIdx = thisTagGroupStart; + break; + } + } + + if (sd->fTagsIdx == -1) { + // No suitable entry in the global tag list already. Add one + sd->fTagsIdx = fRB->fRuleStatusVals->size(); + fRB->fRuleStatusVals->addElement(thisStatesTagValues->size(), *fStatus); + for (i=0; isize(); i++) { + fRB->fRuleStatusVals->addElement(thisStatesTagValues->elementAti(i), *fStatus); + } + } + } +} + + + + + + + +//----------------------------------------------------------------------------- +// +// sortedAdd Add a value to a vector of sorted values (ints). +// Do not replicate entries; if the value is already there, do not +// add a second one. +// Lazily create the vector if it does not already exist. +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::sortedAdd(UVector **vector, int32_t val) { + int32_t i; + + if (*vector == nullptr) { + *vector = new UVector(*fStatus); + } + if (*vector == nullptr || U_FAILURE(*fStatus)) { + return; + } + UVector *vec = *vector; + int32_t vSize = vec->size(); + for (i=0; ielementAti(i); + if (valAtI == val) { + // The value is already in the vector. Don't add it again. + return; + } + if (valAtI > val) { + break; + } + } + vec->insertElementAt(val, i, *fStatus); +} + + + +//----------------------------------------------------------------------------- +// +// setAdd Set operation on UVector +// dest = dest union source +// Elements may only appear once and must be sorted. +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::setAdd(UVector *dest, UVector *source) { + U_ASSERT(!dest->hasDeleter()); + U_ASSERT(!source->hasDeleter()); + int32_t destOriginalSize = dest->size(); + int32_t sourceSize = source->size(); + int32_t di = 0; + MaybeStackArray destArray, sourceArray; // Handle small cases without malloc + void **destPtr, **sourcePtr; + void **destLim, **sourceLim; + + if (destOriginalSize > destArray.getCapacity()) { + if (destArray.resize(destOriginalSize) == nullptr) { + return; + } + } + destPtr = destArray.getAlias(); + destLim = destPtr + destOriginalSize; // destArray.getArrayLimit()? + + if (sourceSize > sourceArray.getCapacity()) { + if (sourceArray.resize(sourceSize) == nullptr) { + return; + } + } + sourcePtr = sourceArray.getAlias(); + sourceLim = sourcePtr + sourceSize; // sourceArray.getArrayLimit()? + + // Avoid multiple "get element" calls by getting the contents into arrays + (void) dest->toArray(destPtr); + (void) source->toArray(sourcePtr); + + dest->setSize(sourceSize+destOriginalSize, *fStatus); + if (U_FAILURE(*fStatus)) { + return; + } + + while (sourcePtr < sourceLim && destPtr < destLim) { + if (*destPtr == *sourcePtr) { + dest->setElementAt(*sourcePtr++, di++); + destPtr++; + } + // This check is required for machines with segmented memory, like i5/OS. + // Direct pointer comparison is not recommended. + else if (uprv_memcmp(destPtr, sourcePtr, sizeof(void *)) < 0) { + dest->setElementAt(*destPtr++, di++); + } + else { /* *sourcePtr < *destPtr */ + dest->setElementAt(*sourcePtr++, di++); + } + } + + // At most one of these two cleanup loops will execute + while (destPtr < destLim) { + dest->setElementAt(*destPtr++, di++); + } + while (sourcePtr < sourceLim) { + dest->setElementAt(*sourcePtr++, di++); + } + + dest->setSize(di, *fStatus); +} + + + +//----------------------------------------------------------------------------- +// +// setEqual Set operation on UVector. +// Compare for equality. +// Elements must be sorted. +// +//----------------------------------------------------------------------------- +UBool RBBITableBuilder::setEquals(UVector *a, UVector *b) { + return a->equals(*b); +} + + +//----------------------------------------------------------------------------- +// +// printPosSets Debug function. Dump Nullable, firstpos, lastpos and followpos +// for each node in the tree. +// +//----------------------------------------------------------------------------- +#ifdef RBBI_DEBUG +void RBBITableBuilder::printPosSets(RBBINode *n) { + if (n==nullptr) { + return; + } + printf("\n"); + RBBINode::printNodeHeader(); + RBBINode::printNode(n); + RBBIDebugPrintf(" Nullable: %s\n", n->fNullable?"true":"false"); + + RBBIDebugPrintf(" firstpos: "); + printSet(n->fFirstPosSet); + + RBBIDebugPrintf(" lastpos: "); + printSet(n->fLastPosSet); + + RBBIDebugPrintf(" followpos: "); + printSet(n->fFollowPos); + + printPosSets(n->fLeftChild); + printPosSets(n->fRightChild); +} +#endif + +// +// findDuplCharClassFrom() +// +bool RBBITableBuilder::findDuplCharClassFrom(IntPair *categories) { + int32_t numStates = fDStates->size(); + int32_t numCols = fRB->fSetBuilder->getNumCharCategories(); + + for (; categories->first < numCols-1; categories->first++) { + // Note: dictionary & non-dictionary columns cannot be merged. + // The limitSecond value prevents considering mixed pairs. + // Dictionary categories are >= DictCategoriesStart. + // Non dict categories are < DictCategoriesStart. + int limitSecond = categories->first < fRB->fSetBuilder->getDictCategoriesStart() ? + fRB->fSetBuilder->getDictCategoriesStart() : numCols; + for (categories->second=categories->first+1; categories->second < limitSecond; categories->second++) { + // Initialized to different values to prevent returning true if numStates = 0 (implies no duplicates). + uint16_t table_base = 0; + uint16_t table_dupl = 1; + for (int32_t state=0; stateelementAt(state); + table_base = (uint16_t)sd->fDtran->elementAti(categories->first); + table_dupl = (uint16_t)sd->fDtran->elementAti(categories->second); + if (table_base != table_dupl) { + break; + } + } + if (table_base == table_dupl) { + return true; + } + } + } + return false; +} + + +// +// removeColumn() +// +void RBBITableBuilder::removeColumn(int32_t column) { + int32_t numStates = fDStates->size(); + for (int32_t state=0; stateelementAt(state); + U_ASSERT(column < sd->fDtran->size()); + sd->fDtran->removeElementAt(column); + } +} + +/* + * findDuplicateState + */ +bool RBBITableBuilder::findDuplicateState(IntPair *states) { + int32_t numStates = fDStates->size(); + int32_t numCols = fRB->fSetBuilder->getNumCharCategories(); + + 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) { + continue; + } + bool rowsMatch = true; + for (int32_t col=0; col < numCols; ++col) { + int32_t firstVal = firstSD->fDtran->elementAti(col); + int32_t duplVal = duplSD->fDtran->elementAti(col); + if (!((firstVal == duplVal) || + ((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; + } + } + if (rowsMatch) { + return true; + } + } + } + return false; +} + + +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()); + + RBBIStateDescriptor *duplSD = (RBBIStateDescriptor *)fDStates->elementAt(duplState); + fDStates->removeElementAt(duplState); + delete duplSD; + + int32_t numStates = fDStates->size(); + int32_t numCols = fRB->fSetBuilder->getNumCharCategories(); + for (int32_t state=0; stateelementAt(state); + for (int32_t col=0; colfDtran->elementAti(col); + int32_t newVal = existingVal; + if (existingVal == duplState) { + newVal = keepState; + } else if (existingVal > duplState) { + newVal = existingVal - 1; + } + sd->fDtran->setElementAt(newVal, col); + } + } +} + +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, static_cast(newVal)); + } + } +} + + +/* + * RemoveDuplicateStates + */ +int32_t RBBITableBuilder::removeDuplicateStates() { + IntPair dupls = {3, 0}; + int32_t numStatesRemoved = 0; + + while (findDuplicateState(&dupls)) { + // printf("Removing duplicate states (%d, %d)\n", dupls.first, dupls.second); + removeState(dupls); + ++numStatesRemoved; + } + return numStatesRemoved; +} + + +//----------------------------------------------------------------------------- +// +// getTableSize() Calculate the size of the runtime form of this +// state transition table. +// +//----------------------------------------------------------------------------- +int32_t RBBITableBuilder::getTableSize() const { + int32_t size = 0; + int32_t numRows; + int32_t numCols; + int32_t rowSize; + + if (fTree == nullptr) { + return 0; + } + + size = offsetof(RBBIStateTable, fTableData); // The header, with no rows to the table. + + numRows = fDStates->size(); + numCols = fRB->fSetBuilder->getNumCharCategories(); + + if (use8BitsForTable()) { + rowSize = offsetof(RBBIStateTableRow8, fNextState) + sizeof(int8_t)*numCols; + } else { + rowSize = offsetof(RBBIStateTableRow16, fNextState) + sizeof(int16_t)*numCols; + } + size += numRows * rowSize; + return size; +} + +bool RBBITableBuilder::use8BitsForTable() const { + return fDStates->size() <= kMaxStateFor8BitsTable; +} + +//----------------------------------------------------------------------------- +// +// exportTable() 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::exportTable(void *where) { + RBBIStateTable *table = (RBBIStateTable *)where; + uint32_t state; + int col; + + if (U_FAILURE(*fStatus) || fTree == nullptr) { + return; + } + + int32_t catCount = fRB->fSetBuilder->getNumCharCategories(); + if (catCount > 0x7fff || + fDStates->size() > 0x7fff) { + *fStatus = U_BRK_INTERNAL_ERROR; + return; + } + + table->fNumStates = fDStates->size(); + table->fDictCategoriesStart = fRB->fSetBuilder->getDictCategoriesStart(); + table->fLookAheadResultsSize = fLASlotsInUse == ACCEPTING_UNCONDITIONAL ? 0 : fLASlotsInUse + 1; + table->fFlags = 0; + if (use8BitsForTable()) { + table->fRowLen = offsetof(RBBIStateTableRow8, fNextState) + sizeof(uint8_t) * catCount; + table->fFlags |= RBBI_8BITS_ROWS; + } else { + table->fRowLen = offsetof(RBBIStateTableRow16, fNextState) + sizeof(int16_t) * catCount; + } + if (fRB->fLookAheadHardBreak) { + table->fFlags |= RBBI_LOOKAHEAD_HARD_BREAK; + } + if (fRB->fSetBuilder->sawBOF()) { + table->fFlags |= RBBI_BOF_REQUIRED; + } + + for (state=0; statefNumStates; state++) { + RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(state); + RBBIStateTableRow *row = (RBBIStateTableRow *)(table->fTableData + state*table->fRowLen); + if (use8BitsForTable()) { + U_ASSERT (sd->fAccepting <= 255); + U_ASSERT (sd->fLookAhead <= 255); + U_ASSERT (0 <= sd->fTagsIdx && sd->fTagsIdx <= 255); + RBBIStateTableRow8 *r8 = (RBBIStateTableRow8*)row; + r8->fAccepting = sd->fAccepting; + r8->fLookAhead = sd->fLookAhead; + r8->fTagsIdx = sd->fTagsIdx; + for (col=0; colfDtran->elementAti(col) <= kMaxStateFor8BitsTable); + r8->fNextState[col] = sd->fDtran->elementAti(col); + } + } else { + U_ASSERT (sd->fAccepting <= 0xffff); + U_ASSERT (sd->fLookAhead <= 0xffff); + U_ASSERT (0 <= sd->fTagsIdx && sd->fTagsIdx <= 0xffff); + row->r16.fAccepting = sd->fAccepting; + row->r16.fLookAhead = sd->fLookAhead; + row->r16.fTagsIdx = sd->fTagsIdx; + for (col=0; colr16.fNextState[col] = sd->fDtran->elementAti(col); + } + } + } +} + + +/** + * Synthesize a safe state table from the main state table. + */ +void RBBITableBuilder::buildSafeReverseTable(UErrorCode &status) { + // The safe table creation has three steps: + + // 1. Identify 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 character 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 state. + // 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); + LocalPointer lpSafeTable( + new UVector(uprv_deleteUObject, uhash_compareUnicodeString, numCharClasses + 2, status), status); + if (U_FAILURE(status)) { + return; + } + fSafeTable = lpSafeTable.orphan(); + for (int32_t row=0; row lpString(new UnicodeString(numCharClasses, 0, numCharClasses+4), status); + fSafeTable->adoptElement(lpString.orphan(), status); + } + if (U_FAILURE(status)) { + return; + } + + // 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, static_cast(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(); + + if (use8BitsForSafeTable()) { + rowSize = offsetof(RBBIStateTableRow8, fNextState) + sizeof(int8_t)*numCols; + } else { + rowSize = offsetof(RBBIStateTableRow16, fNextState) + sizeof(int16_t)*numCols; + } + size += numRows * rowSize; + return size; +} + +bool RBBITableBuilder::use8BitsForSafeTable() const { + return fSafeTable->size() <= kMaxStateFor8BitsTable; +} + +//----------------------------------------------------------------------------- +// +// 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->fNumStates = fSafeTable->size(); + table->fFlags = 0; + if (use8BitsForSafeTable()) { + table->fRowLen = offsetof(RBBIStateTableRow8, fNextState) + sizeof(uint8_t) * catCount; + table->fFlags |= RBBI_8BITS_ROWS; + } else { + table->fRowLen = offsetof(RBBIStateTableRow16, fNextState) + sizeof(int16_t) * catCount; + } + + for (state=0; statefNumStates; state++) { + UnicodeString *rowString = (UnicodeString *)fSafeTable->elementAt(state); + RBBIStateTableRow *row = (RBBIStateTableRow *)(table->fTableData + state*table->fRowLen); + if (use8BitsForSafeTable()) { + RBBIStateTableRow8 *r8 = (RBBIStateTableRow8*)row; + r8->fAccepting = 0; + r8->fLookAhead = 0; + r8->fTagsIdx = 0; + for (col=0; colcharAt(col) <= kMaxStateFor8BitsTable); + r8->fNextState[col] = static_cast(rowString->charAt(col)); + } + } else { + row->r16.fAccepting = 0; + row->r16.fLookAhead = 0; + row->r16.fTagsIdx = 0; + for (col=0; colr16.fNextState[col] = rowString->charAt(col); + } + } + } +} + + + + +//----------------------------------------------------------------------------- +// +// printSet Debug function. Print the contents of a UVector +// +//----------------------------------------------------------------------------- +#ifdef RBBI_DEBUG +void RBBITableBuilder::printSet(UVector *s) { + int32_t i; + for (i=0; isize(); i++) { + const RBBINode *v = static_cast(s->elementAt(i)); + RBBIDebugPrintf("%5d", v==nullptr? -1 : v->fSerialNum); + } + RBBIDebugPrintf("\n"); +} +#endif + + +//----------------------------------------------------------------------------- +// +// printStates Debug Function. Dump the fully constructed state transition table. +// +//----------------------------------------------------------------------------- +#ifdef RBBI_DEBUG +void RBBITableBuilder::printStates() { + int c; // input "character" + int n; // state number + + 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(" %3d", c); + } + RBBIDebugPrintf("\n"); + RBBIDebugPrintf(" |---------------"); + for (c=0; cfSetBuilder->getNumCharCategories(); c++) { + RBBIDebugPrintf("----"); + } + RBBIDebugPrintf("\n"); + + for (n=0; nsize(); n++) { + RBBIStateDescriptor *sd = (RBBIStateDescriptor *)fDStates->elementAt(n); + RBBIDebugPrintf(" %3d | " , n); + RBBIDebugPrintf("%3d %3d %5d ", sd->fAccepting, sd->fLookAhead, sd->fTagsIdx); + for (c=0; cfSetBuilder->getNumCharCategories(); c++) { + RBBIDebugPrintf(" %3d", sd->fDtran->elementAti(c)); + } + RBBIDebugPrintf("\n"); + } + RBBIDebugPrintf("\n\n"); +} +#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 + + + +//----------------------------------------------------------------------------- +// +// printRuleStatusTable Debug Function. Dump the common rule status table +// +//----------------------------------------------------------------------------- +#ifdef RBBI_DEBUG +void RBBITableBuilder::printRuleStatusTable() { + int32_t thisRecord = 0; + int32_t nextRecord = 0; + int i; + UVector *tbl = fRB->fRuleStatusVals; + + RBBIDebugPrintf("index | tags \n"); + RBBIDebugPrintf("-------------------\n"); + + while (nextRecord < tbl->size()) { + thisRecord = nextRecord; + nextRecord = thisRecord + tbl->elementAti(thisRecord) + 1; + RBBIDebugPrintf("%4d ", thisRecord); + for (i=thisRecord+1; ielementAti(i)); + } + RBBIDebugPrintf("\n"); + } + RBBIDebugPrintf("\n\n"); +} +#endif + + +//----------------------------------------------------------------------------- +// +// RBBIStateDescriptor Methods. This is a very struct-like class +// Most access is directly to the fields. +// +//----------------------------------------------------------------------------- + +RBBIStateDescriptor::RBBIStateDescriptor(int lastInputSymbol, UErrorCode *fStatus) { + fMarked = false; + fAccepting = 0; + fLookAhead = 0; + fTagsIdx = 0; + fTagVals = nullptr; + fPositions = nullptr; + fDtran = nullptr; + + fDtran = new UVector32(lastInputSymbol+1, *fStatus); + if (U_FAILURE(*fStatus)) { + return; + } + if (fDtran == nullptr) { + *fStatus = U_MEMORY_ALLOCATION_ERROR; + return; + } + fDtran->setSize(lastInputSymbol+1); // fDtran needs to be pre-sized. + // It is indexed by input symbols, and will + // hold the next state number for each + // symbol. +} + + +RBBIStateDescriptor::~RBBIStateDescriptor() { + delete fPositions; + delete fDtran; + delete fTagVals; + fPositions = nullptr; + fDtran = nullptr; + fTagVals = nullptr; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ diff --git a/deps/icu-small/source/common/rbbitblb.h b/deps/icu-small/source/common/rbbitblb.h index 2ac66da11f01fe..1d758b0aa60c78 100644 --- a/deps/icu-small/source/common/rbbitblb.h +++ b/deps/icu-small/source/common/rbbitblb.h @@ -1,232 +1,232 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// rbbitblb.h -// - -/* -********************************************************************** -* Copyright (c) 2002-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#ifndef RBBITBLB_H -#define RBBITBLB_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/uobject.h" -#include "unicode/rbbi.h" -#include "rbbidata.h" -#include "rbbirb.h" -#include "rbbinode.h" - - -U_NAMESPACE_BEGIN - -class RBBIRuleScanner; -class RBBIRuleBuilder; -class UVector32; - -// -// class RBBITableBuilder is part of the RBBI rule compiler. -// It builds the state transition table used by the RBBI runtime -// from the expression syntax tree generated by the rule scanner. -// -// This class is part of the RBBI implementation only. -// There is no user-visible public API here. -// - -class RBBITableBuilder : public UMemory { -public: - RBBITableBuilder(RBBIRuleBuilder *rb, RBBINode **rootNode, UErrorCode &status); - ~RBBITableBuilder(); - - 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); - - /** Use 8 bits to encode the forward table */ - bool use8BitsForTable() const; - - /** - * 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(IntPair *categories); - - /** Remove a column from the state table. Used when two character categories - * have been found equivalent, and merged together, to eliminate the unneeded table column. - */ - void removeColumn(int32_t column); - - /** - * Check for, and remove duplicate states (table rows). - * @return the number of states removed. - */ - int32_t 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); - - /** Use 8 bits to encode the safe reverse table */ - bool use8BitsForSafeTable() const; - -private: - void calcNullable(RBBINode *n); - void calcFirstPos(RBBINode *n); - void calcLastPos(RBBINode *n); - void calcFollowPos(RBBINode *n); - void calcChainedFollowPos(RBBINode *n, RBBINode *endMarkNode); - void bofFixup(); - void buildStateTable(); - void mapLookAheadRules(); - void flagAcceptingStates(); - void flagLookAheadStates(); - void flagTaggedStates(); - void mergeRuleStatusVals(); - - /** - * Merge redundant state table columns, eliminating character classes with identical behavior. - * Done after the state tables are generated, just before converting to their run-time format. - */ - int32_t mergeColumns(); - - void addRuleRootNodes(UVector *dest, RBBINode *node); - - /** - * 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. - */ - void removeState(IntPair duplStates); - - /** 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. - */ - 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 - - void setAdd(UVector *dest, UVector *source); - UBool setEquals(UVector *a, UVector *b); - - void sortedAdd(UVector **dest, int32_t val); - -public: -#ifdef RBBI_DEBUG - void printSet(UVector *s); - 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: - RBBIRuleBuilder *fRB; - RBBINode *&fTree; // The root node of the parse tree to build a - // 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; - - /** Map from rule number (fVal in look ahead nodes) to sequential lookahead index. */ - UVector32 *fLookAheadRuleMap = nullptr; - - /* Counter used when assigning lookahead rule numbers. - * Contains the last look-ahead number already in use. - * The first look-ahead number is 2; Number 1 (ACCEPTING_UNCONDITIONAL) is reserved - * for non-lookahead accepting states. See the declarations of RBBIStateTableRowT. */ - int32_t fLASlotsInUse = ACCEPTING_UNCONDITIONAL; - - - RBBITableBuilder(const RBBITableBuilder &other) = delete; // forbid copying of this class - RBBITableBuilder &operator=(const RBBITableBuilder &other) = delete; // forbid copying of this class -}; - -// -// RBBIStateDescriptor - The DFA is constructed as a set of these descriptors, -// one for each state. -class RBBIStateDescriptor : public UMemory { -public: - UBool fMarked; - uint32_t fAccepting; - uint32_t fLookAhead; - UVector *fTagVals; - int32_t fTagsIdx; - UVector *fPositions; // Set of parse tree positions associated - // with this state. Unordered (it's a set). - // UVector contents are RBBINode * - - UVector32 *fDtran; // Transitions out of this state. - // indexed by input character - // contents is int index of dest state - // in RBBITableBuilder.fDStates - - RBBIStateDescriptor(int maxInputSymbol, UErrorCode *fStatus); - ~RBBIStateDescriptor(); - -private: - RBBIStateDescriptor(const RBBIStateDescriptor &other) = delete; // forbid copying of this class - RBBIStateDescriptor &operator=(const RBBIStateDescriptor &other) = delete; // forbid copying of this class -}; - - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// rbbitblb.h +// + +/* +********************************************************************** +* Copyright (c) 2002-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#ifndef RBBITBLB_H +#define RBBITBLB_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/uobject.h" +#include "unicode/rbbi.h" +#include "rbbidata.h" +#include "rbbirb.h" +#include "rbbinode.h" + + +U_NAMESPACE_BEGIN + +class RBBIRuleScanner; +class RBBIRuleBuilder; +class UVector32; + +// +// class RBBITableBuilder is part of the RBBI rule compiler. +// It builds the state transition table used by the RBBI runtime +// from the expression syntax tree generated by the rule scanner. +// +// This class is part of the RBBI implementation only. +// There is no user-visible public API here. +// + +class RBBITableBuilder : public UMemory { +public: + RBBITableBuilder(RBBIRuleBuilder *rb, RBBINode **rootNode, UErrorCode &status); + ~RBBITableBuilder(); + + 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); + + /** Use 8 bits to encode the forward table */ + bool use8BitsForTable() const; + + /** + * 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(IntPair *categories); + + /** Remove a column from the state table. Used when two character categories + * have been found equivalent, and merged together, to eliminate the unneeded table column. + */ + void removeColumn(int32_t column); + + /** + * Check for, and remove duplicate states (table rows). + * @return the number of states removed. + */ + int32_t 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); + + /** Use 8 bits to encode the safe reverse table */ + bool use8BitsForSafeTable() const; + +private: + void calcNullable(RBBINode *n); + void calcFirstPos(RBBINode *n); + void calcLastPos(RBBINode *n); + void calcFollowPos(RBBINode *n); + void calcChainedFollowPos(RBBINode *n, RBBINode *endMarkNode); + void bofFixup(); + void buildStateTable(); + void mapLookAheadRules(); + void flagAcceptingStates(); + void flagLookAheadStates(); + void flagTaggedStates(); + void mergeRuleStatusVals(); + + /** + * Merge redundant state table columns, eliminating character classes with identical behavior. + * Done after the state tables are generated, just before converting to their run-time format. + */ + int32_t mergeColumns(); + + void addRuleRootNodes(UVector *dest, RBBINode *node); + + /** + * 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. + */ + void removeState(IntPair duplStates); + + /** 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. + */ + 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 + + void setAdd(UVector *dest, UVector *source); + UBool setEquals(UVector *a, UVector *b); + + void sortedAdd(UVector **dest, int32_t val); + +public: +#ifdef RBBI_DEBUG + void printSet(UVector *s); + void printPosSets(RBBINode *n /* = nullptr */); + void printStates(); + void printRuleStatusTable(); + void printReverseTable(); +#else + #define printSet(s) + #define printPosSets(n) + #define printStates() + #define printRuleStatusTable() + #define printReverseTable() +#endif + +private: + RBBIRuleBuilder *fRB; + RBBINode *&fTree; // The root node of the parse tree to build a + // 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; + + /** Map from rule number (fVal in look ahead nodes) to sequential lookahead index. */ + UVector32 *fLookAheadRuleMap = nullptr; + + /* Counter used when assigning lookahead rule numbers. + * Contains the last look-ahead number already in use. + * The first look-ahead number is 2; Number 1 (ACCEPTING_UNCONDITIONAL) is reserved + * for non-lookahead accepting states. See the declarations of RBBIStateTableRowT. */ + int32_t fLASlotsInUse = ACCEPTING_UNCONDITIONAL; + + + RBBITableBuilder(const RBBITableBuilder &other) = delete; // forbid copying of this class + RBBITableBuilder &operator=(const RBBITableBuilder &other) = delete; // forbid copying of this class +}; + +// +// RBBIStateDescriptor - The DFA is constructed as a set of these descriptors, +// one for each state. +class RBBIStateDescriptor : public UMemory { +public: + UBool fMarked; + uint32_t fAccepting; + uint32_t fLookAhead; + UVector *fTagVals; + int32_t fTagsIdx; + UVector *fPositions; // Set of parse tree positions associated + // with this state. Unordered (it's a set). + // UVector contents are RBBINode * + + UVector32 *fDtran; // Transitions out of this state. + // indexed by input character + // contents is int index of dest state + // in RBBITableBuilder.fDStates + + RBBIStateDescriptor(int maxInputSymbol, UErrorCode *fStatus); + ~RBBIStateDescriptor(); + +private: + RBBIStateDescriptor(const RBBIStateDescriptor &other) = delete; // forbid copying of this class + RBBIStateDescriptor &operator=(const RBBIStateDescriptor &other) = delete; // forbid copying of this class +}; + + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ + +#endif diff --git a/deps/icu-small/source/common/resbund.cpp b/deps/icu-small/source/common/resbund.cpp index 8591a625f9594d..3130aebaeb9e19 100644 --- a/deps/icu-small/source/common/resbund.cpp +++ b/deps/icu-small/source/common/resbund.cpp @@ -1,399 +1,399 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1997-2013, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* File resbund.cpp -* -* Modification History: -* -* Date Name Description -* 02/05/97 aliu Fixed bug in chopLocale. Added scanForLocaleInFile -* based on code taken from scanForLocale. Added -* constructor which attempts to read resource bundle -* from a specific file, without searching other files. -* 02/11/97 aliu Added UErrorCode return values to constructors. Fixed -* infinite loops in scanForFile and scanForLocale. -* Modified getRawResourceData to not delete storage in -* localeData and resourceData which it doesn't own. -* Added Mac compatibility #ifdefs for tellp() and -* ios::nocreate. -* 03/04/97 aliu Modified to use ExpandingDataSink objects instead of -* the highly inefficient ostrstream objects. -* 03/13/97 aliu Rewrote to load in entire resource bundle and store -* it as a Hashtable of ResourceBundleData objects. -* Added state table to govern parsing of files. -* Modified to load locale index out of new file distinct -* from default.txt. -* 03/25/97 aliu Modified to support 2-d arrays, needed for timezone data. -* Added support for custom file suffixes. Again, needed -* to support timezone data. Improved error handling to -* detect duplicate tags and subtags. -* 04/07/97 aliu Fixed bug in getHashtableForLocale(). Fixed handling -* of failing UErrorCode values on entry to API methods. -* Fixed bugs in getArrayItem() for negative indices. -* 04/29/97 aliu Update to use new Hashtable deletion protocol. -* 05/06/97 aliu Flattened kTransitionTable for HP compiler. -* Fixed usage of CharString. -* 06/11/99 stephen Removed parsing of .txt files. -* Reworked to use new binary format. -* Cleaned up. -* 06/14/99 stephen Removed methods taking a filename suffix. -* 06/22/99 stephen Added missing T_FileStream_close in parse() -* 11/09/99 weiv Added getLocale(), rewritten constructForLocale() -* March 2000 weiv complete overhaul. -****************************************************************************** -*/ - -#include "unicode/utypes.h" -#include "unicode/resbund.h" - -#include "cmemory.h" -#include "mutex.h" -#include "uassert.h" -#include "umutex.h" - -#include "uresimp.h" - -U_NAMESPACE_BEGIN - -/*----------------------------------------------------------------------------- - * Implementation Notes - * - * Resource bundles are read in once, and thereafter cached. - * ResourceBundle statically keeps track of which files have been - * read, so we are guaranteed that each file is read at most once. - * Resource bundles can be loaded from different data directories and - * will be treated as distinct, even if they are for the same locale. - * - * Resource bundles are lightweight objects, which have pointers to - * one or more shared Hashtable objects containing all the data. - * Copying would be cheap, but there is no copy constructor, since - * there wasn't one in the original API. - * - * The ResourceBundle parsing mechanism is implemented as a transition - * network, for easy maintenance and modification. The network is - * implemented as a matrix (instead of in code) to make this even - * easier. The matrix contains Transition objects. Each Transition - * object describes a destination node and an action to take before - * moving to the destination node. The source node is encoded by the - * index of the object in the array that contains it. The pieces - * needed to understand the transition network are the enums for node - * IDs and actions, the parse() method, which walks through the - * network and implements the actions, and the network itself. The - * network guarantees certain conditions, for example, that a new - * resource will not be closed until one has been opened first; or - * that data will not be stored into a TaggedList until a TaggedList - * has been created. Nonetheless, the code in parse() does some - * consistency checks as it runs the network, and fails with an - * U_INTERNAL_PROGRAM_ERROR if one of these checks fails. If the input - * data has a bad format, an U_INVALID_FORMAT_ERROR is returned. If you - * see an U_INTERNAL_PROGRAM_ERROR the transition matrix has a bug in - * it. - * - * Old functionality of multiple locales in a single file is still - * supported. For this reason, LOCALE names override FILE names. If - * data for en_US is located in the en.txt file, once it is loaded, - * the code will not care where it came from (other than remembering - * which directory it came from). However, if there is an en_US - * resource in en_US.txt, that will take precedence. There is no - * limit to the number or type of resources that can be stored in a - * file, however, files are only searched in a specific way. If - * en_US_CA is requested, then first en_US_CA.txt is searched, then - * en_US.txt, then en.txt, then default.txt. So it only makes sense - * to put certain locales in certain files. In this example, it would - * be logical to put en_US_CA, en_US, and en into the en.txt file, - * since they would be found there if asked for. The extreme example - * is to place all locale resources into default.txt, which should - * also work. - * - * Inheritance is implemented. For example, xx_YY_zz inherits as - * follows: xx_YY_zz, xx_YY, xx, default. Inheritance is implemented - * as an array of hashtables. There will be from 1 to 4 hashtables in - * the array. - * - * Fallback files are implemented. The fallback pattern is Language - * Country Variant (LCV) -> LC -> L. Fallback is first done for the - * requested locale. Then it is done for the default locale, as - * returned by Locale::getDefault(). Then the special file - * default.txt is searched for the default locale. The overall FILE - * fallback path is LCV -> LC -> L -> dLCV -> dLC -> dL -> default. - * - * Note that although file name searching includes the default locale, - * once a ResourceBundle object is constructed, the inheritance path - * no longer includes the default locale. The path is LCV -> LC -> L - * -> default. - * - * File parsing is lazy. Nothing is parsed unless it is called for by - * someone. So when a ResourceBundle for xx_YY_zz is constructed, - * only that locale is parsed (along with anything else in the same - * file). Later, if the FooBar tag is asked for, and if it isn't - * found in xx_YY_zz, then xx_YY.txt will be parsed and checked, and - * so forth, until the chain is exhausted or the tag is found. - * - * Thread-safety is implemented around caches, both the cache that - * stores all the resource data, and the cache that stores flags - * indicating whether or not a file has been visited. These caches - * delete their storage at static cleanup time, when the process - * quits. - * - * ResourceBundle supports TableCollation as a special case. This - * involves having special ResourceBundle objects which DO own their - * data, since we don't want large collation rule strings in the - * ResourceBundle cache (these are already cached in the - * TableCollation cache). TableCollation files (.ctx files) have the - * same format as normal resource data files, with a different - * interpretation, from the standpoint of ResourceBundle. .ctx files - * are loaded into otherwise ordinary ResourceBundle objects. They - * don't inherit (that's implemented by TableCollation) and they own - * their data (as mentioned above). However, they still support - * possible multiple locales in a single .ctx file. (This is in - * practice a bad idea, since you only want the one locale you're - * looking for, and only one tag will be present - * ("CollationElements"), so you don't need an inheritance chain of - * multiple locales.) Up to 4 locale resources will be loaded from a - * .ctx file; everything after the first 4 is ignored (parsed and - * deleted). (Normal .txt files have no limit.) Instead of being - * loaded into the cache, and then looked up as needed, the locale - * resources are read straight into the ResourceBundle object. - * - * The Index, which used to reside in default.txt, has been moved to a - * new file, index.txt. This file contains a slightly modified format - * with the addition of the "InstalledLocales" tag; it looks like: - * - * Index { - * InstalledLocales { - * ar - * .. - * zh_TW - * } - * } - */ -//----------------------------------------------------------------------------- - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ResourceBundle) - -ResourceBundle::ResourceBundle(UErrorCode &err) - :UObject(), fLocale(NULL) -{ - fResource = ures_open(0, Locale::getDefault().getName(), &err); -} - -ResourceBundle::ResourceBundle(const ResourceBundle &other) - :UObject(other), fLocale(NULL) -{ - UErrorCode status = U_ZERO_ERROR; - - if (other.fResource) { - fResource = ures_copyResb(0, other.fResource, &status); - } else { - /* Copying a bad resource bundle */ - fResource = NULL; - } -} - -ResourceBundle::ResourceBundle(UResourceBundle *res, UErrorCode& err) - :UObject(), fLocale(NULL) -{ - if (res) { - fResource = ures_copyResb(0, res, &err); - } else { - /* Copying a bad resource bundle */ - fResource = NULL; - } -} - -ResourceBundle::ResourceBundle(const char* path, const Locale& locale, UErrorCode& err) - :UObject(), fLocale(NULL) -{ - fResource = ures_open(path, locale.getName(), &err); -} - - -ResourceBundle& ResourceBundle::operator=(const ResourceBundle& other) -{ - if(this == &other) { - return *this; - } - if(fResource != 0) { - ures_close(fResource); - fResource = NULL; - } - if (fLocale != NULL) { - delete fLocale; - fLocale = NULL; - } - UErrorCode status = U_ZERO_ERROR; - if (other.fResource) { - fResource = ures_copyResb(0, other.fResource, &status); - } else { - /* Copying a bad resource bundle */ - fResource = NULL; - } - return *this; -} - -ResourceBundle::~ResourceBundle() -{ - if(fResource != 0) { - ures_close(fResource); - } - if(fLocale != NULL) { - delete(fLocale); - } -} - -ResourceBundle * -ResourceBundle::clone() const { - return new ResourceBundle(*this); -} - -UnicodeString ResourceBundle::getString(UErrorCode& status) const { - int32_t len = 0; - const UChar *r = ures_getString(fResource, &len, &status); - return UnicodeString(true, r, len); -} - -const uint8_t *ResourceBundle::getBinary(int32_t& len, UErrorCode& status) const { - return ures_getBinary(fResource, &len, &status); -} - -const int32_t *ResourceBundle::getIntVector(int32_t& len, UErrorCode& status) const { - return ures_getIntVector(fResource, &len, &status); -} - -uint32_t ResourceBundle::getUInt(UErrorCode& status) const { - return ures_getUInt(fResource, &status); -} - -int32_t ResourceBundle::getInt(UErrorCode& status) const { - return ures_getInt(fResource, &status); -} - -const char *ResourceBundle::getName(void) const { - return ures_getName(fResource); -} - -const char *ResourceBundle::getKey(void) const { - return ures_getKey(fResource); -} - -UResType ResourceBundle::getType(void) const { - return ures_getType(fResource); -} - -int32_t ResourceBundle::getSize(void) const { - return ures_getSize(fResource); -} - -UBool ResourceBundle::hasNext(void) const { - return ures_hasNext(fResource); -} - -void ResourceBundle::resetIterator(void) { - ures_resetIterator(fResource); -} - -ResourceBundle ResourceBundle::getNext(UErrorCode& status) { - UResourceBundle r; - - ures_initStackObject(&r); - ures_getNextResource(fResource, &r, &status); - ResourceBundle res(&r, status); - if (U_SUCCESS(status)) { - ures_close(&r); - } - return res; -} - -UnicodeString ResourceBundle::getNextString(UErrorCode& status) { - int32_t len = 0; - const UChar* r = ures_getNextString(fResource, &len, 0, &status); - return UnicodeString(true, r, len); -} - -UnicodeString ResourceBundle::getNextString(const char ** key, UErrorCode& status) { - int32_t len = 0; - const UChar* r = ures_getNextString(fResource, &len, key, &status); - return UnicodeString(true, r, len); -} - -ResourceBundle ResourceBundle::get(int32_t indexR, UErrorCode& status) const { - UResourceBundle r; - - ures_initStackObject(&r); - ures_getByIndex(fResource, indexR, &r, &status); - ResourceBundle res(&r, status); - if (U_SUCCESS(status)) { - ures_close(&r); - } - return res; -} - -UnicodeString ResourceBundle::getStringEx(int32_t indexS, UErrorCode& status) const { - int32_t len = 0; - const UChar* r = ures_getStringByIndex(fResource, indexS, &len, &status); - return UnicodeString(true, r, len); -} - -ResourceBundle ResourceBundle::get(const char* key, UErrorCode& status) const { - UResourceBundle r; - - ures_initStackObject(&r); - ures_getByKey(fResource, key, &r, &status); - ResourceBundle res(&r, status); - if (U_SUCCESS(status)) { - ures_close(&r); - } - return res; -} - -ResourceBundle ResourceBundle::getWithFallback(const char* key, UErrorCode& status){ - UResourceBundle r; - ures_initStackObject(&r); - ures_getByKeyWithFallback(fResource, key, &r, &status); - ResourceBundle res(&r, status); - if(U_SUCCESS(status)){ - ures_close(&r); - } - return res; -} -UnicodeString ResourceBundle::getStringEx(const char* key, UErrorCode& status) const { - int32_t len = 0; - const UChar* r = ures_getStringByKey(fResource, key, &len, &status); - return UnicodeString(true, r, len); -} - -const char* -ResourceBundle::getVersionNumber() const -{ - return ures_getVersionNumberInternal(fResource); -} - -void ResourceBundle::getVersion(UVersionInfo versionInfo) const { - ures_getVersion(fResource, versionInfo); -} - -const Locale &ResourceBundle::getLocale(void) const { - static UMutex gLocaleLock; - Mutex lock(&gLocaleLock); - if (fLocale != NULL) { - return *fLocale; - } - UErrorCode status = U_ZERO_ERROR; - const char *localeName = ures_getLocaleInternal(fResource, &status); - ResourceBundle *ncThis = const_cast(this); - ncThis->fLocale = new Locale(localeName); - return ncThis->fLocale != NULL ? *ncThis->fLocale : Locale::getDefault(); -} - -const Locale ResourceBundle::getLocale(ULocDataLocaleType type, UErrorCode &status) const -{ - return ures_getLocaleByType(fResource, type, &status); -} - -U_NAMESPACE_END -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1997-2013, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* File resbund.cpp +* +* Modification History: +* +* Date Name Description +* 02/05/97 aliu Fixed bug in chopLocale. Added scanForLocaleInFile +* based on code taken from scanForLocale. Added +* constructor which attempts to read resource bundle +* from a specific file, without searching other files. +* 02/11/97 aliu Added UErrorCode return values to constructors. Fixed +* infinite loops in scanForFile and scanForLocale. +* Modified getRawResourceData to not delete storage in +* localeData and resourceData which it doesn't own. +* Added Mac compatibility #ifdefs for tellp() and +* ios::nocreate. +* 03/04/97 aliu Modified to use ExpandingDataSink objects instead of +* the highly inefficient ostrstream objects. +* 03/13/97 aliu Rewrote to load in entire resource bundle and store +* it as a Hashtable of ResourceBundleData objects. +* Added state table to govern parsing of files. +* Modified to load locale index out of new file distinct +* from default.txt. +* 03/25/97 aliu Modified to support 2-d arrays, needed for timezone data. +* Added support for custom file suffixes. Again, needed +* to support timezone data. Improved error handling to +* detect duplicate tags and subtags. +* 04/07/97 aliu Fixed bug in getHashtableForLocale(). Fixed handling +* of failing UErrorCode values on entry to API methods. +* Fixed bugs in getArrayItem() for negative indices. +* 04/29/97 aliu Update to use new Hashtable deletion protocol. +* 05/06/97 aliu Flattened kTransitionTable for HP compiler. +* Fixed usage of CharString. +* 06/11/99 stephen Removed parsing of .txt files. +* Reworked to use new binary format. +* Cleaned up. +* 06/14/99 stephen Removed methods taking a filename suffix. +* 06/22/99 stephen Added missing T_FileStream_close in parse() +* 11/09/99 weiv Added getLocale(), rewritten constructForLocale() +* March 2000 weiv complete overhaul. +****************************************************************************** +*/ + +#include "unicode/utypes.h" +#include "unicode/resbund.h" + +#include "cmemory.h" +#include "mutex.h" +#include "uassert.h" +#include "umutex.h" + +#include "uresimp.h" + +U_NAMESPACE_BEGIN + +/*----------------------------------------------------------------------------- + * Implementation Notes + * + * Resource bundles are read in once, and thereafter cached. + * ResourceBundle statically keeps track of which files have been + * read, so we are guaranteed that each file is read at most once. + * Resource bundles can be loaded from different data directories and + * will be treated as distinct, even if they are for the same locale. + * + * Resource bundles are lightweight objects, which have pointers to + * one or more shared Hashtable objects containing all the data. + * Copying would be cheap, but there is no copy constructor, since + * there wasn't one in the original API. + * + * The ResourceBundle parsing mechanism is implemented as a transition + * network, for easy maintenance and modification. The network is + * implemented as a matrix (instead of in code) to make this even + * easier. The matrix contains Transition objects. Each Transition + * object describes a destination node and an action to take before + * moving to the destination node. The source node is encoded by the + * index of the object in the array that contains it. The pieces + * needed to understand the transition network are the enums for node + * IDs and actions, the parse() method, which walks through the + * network and implements the actions, and the network itself. The + * network guarantees certain conditions, for example, that a new + * resource will not be closed until one has been opened first; or + * that data will not be stored into a TaggedList until a TaggedList + * has been created. Nonetheless, the code in parse() does some + * consistency checks as it runs the network, and fails with an + * U_INTERNAL_PROGRAM_ERROR if one of these checks fails. If the input + * data has a bad format, an U_INVALID_FORMAT_ERROR is returned. If you + * see an U_INTERNAL_PROGRAM_ERROR the transition matrix has a bug in + * it. + * + * Old functionality of multiple locales in a single file is still + * supported. For this reason, LOCALE names override FILE names. If + * data for en_US is located in the en.txt file, once it is loaded, + * the code will not care where it came from (other than remembering + * which directory it came from). However, if there is an en_US + * resource in en_US.txt, that will take precedence. There is no + * limit to the number or type of resources that can be stored in a + * file, however, files are only searched in a specific way. If + * en_US_CA is requested, then first en_US_CA.txt is searched, then + * en_US.txt, then en.txt, then default.txt. So it only makes sense + * to put certain locales in certain files. In this example, it would + * be logical to put en_US_CA, en_US, and en into the en.txt file, + * since they would be found there if asked for. The extreme example + * is to place all locale resources into default.txt, which should + * also work. + * + * Inheritance is implemented. For example, xx_YY_zz inherits as + * follows: xx_YY_zz, xx_YY, xx, default. Inheritance is implemented + * as an array of hashtables. There will be from 1 to 4 hashtables in + * the array. + * + * Fallback files are implemented. The fallback pattern is Language + * Country Variant (LCV) -> LC -> L. Fallback is first done for the + * requested locale. Then it is done for the default locale, as + * returned by Locale::getDefault(). Then the special file + * default.txt is searched for the default locale. The overall FILE + * fallback path is LCV -> LC -> L -> dLCV -> dLC -> dL -> default. + * + * Note that although file name searching includes the default locale, + * once a ResourceBundle object is constructed, the inheritance path + * no longer includes the default locale. The path is LCV -> LC -> L + * -> default. + * + * File parsing is lazy. Nothing is parsed unless it is called for by + * someone. So when a ResourceBundle for xx_YY_zz is constructed, + * only that locale is parsed (along with anything else in the same + * file). Later, if the FooBar tag is asked for, and if it isn't + * found in xx_YY_zz, then xx_YY.txt will be parsed and checked, and + * so forth, until the chain is exhausted or the tag is found. + * + * Thread-safety is implemented around caches, both the cache that + * stores all the resource data, and the cache that stores flags + * indicating whether or not a file has been visited. These caches + * delete their storage at static cleanup time, when the process + * quits. + * + * ResourceBundle supports TableCollation as a special case. This + * involves having special ResourceBundle objects which DO own their + * data, since we don't want large collation rule strings in the + * ResourceBundle cache (these are already cached in the + * TableCollation cache). TableCollation files (.ctx files) have the + * same format as normal resource data files, with a different + * interpretation, from the standpoint of ResourceBundle. .ctx files + * are loaded into otherwise ordinary ResourceBundle objects. They + * don't inherit (that's implemented by TableCollation) and they own + * their data (as mentioned above). However, they still support + * possible multiple locales in a single .ctx file. (This is in + * practice a bad idea, since you only want the one locale you're + * looking for, and only one tag will be present + * ("CollationElements"), so you don't need an inheritance chain of + * multiple locales.) Up to 4 locale resources will be loaded from a + * .ctx file; everything after the first 4 is ignored (parsed and + * deleted). (Normal .txt files have no limit.) Instead of being + * loaded into the cache, and then looked up as needed, the locale + * resources are read straight into the ResourceBundle object. + * + * The Index, which used to reside in default.txt, has been moved to a + * new file, index.txt. This file contains a slightly modified format + * with the addition of the "InstalledLocales" tag; it looks like: + * + * Index { + * InstalledLocales { + * ar + * .. + * zh_TW + * } + * } + */ +//----------------------------------------------------------------------------- + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ResourceBundle) + +ResourceBundle::ResourceBundle(UErrorCode &err) + :UObject(), fLocale(nullptr) +{ + fResource = ures_open(0, Locale::getDefault().getName(), &err); +} + +ResourceBundle::ResourceBundle(const ResourceBundle &other) + :UObject(other), fLocale(nullptr) +{ + UErrorCode status = U_ZERO_ERROR; + + if (other.fResource) { + fResource = ures_copyResb(0, other.fResource, &status); + } else { + /* Copying a bad resource bundle */ + fResource = nullptr; + } +} + +ResourceBundle::ResourceBundle(UResourceBundle *res, UErrorCode& err) + :UObject(), fLocale(nullptr) +{ + if (res) { + fResource = ures_copyResb(0, res, &err); + } else { + /* Copying a bad resource bundle */ + fResource = nullptr; + } +} + +ResourceBundle::ResourceBundle(const char* path, const Locale& locale, UErrorCode& err) + :UObject(), fLocale(nullptr) +{ + fResource = ures_open(path, locale.getName(), &err); +} + + +ResourceBundle& ResourceBundle::operator=(const ResourceBundle& other) +{ + if(this == &other) { + return *this; + } + if(fResource != 0) { + ures_close(fResource); + fResource = nullptr; + } + if (fLocale != nullptr) { + delete fLocale; + fLocale = nullptr; + } + UErrorCode status = U_ZERO_ERROR; + if (other.fResource) { + fResource = ures_copyResb(0, other.fResource, &status); + } else { + /* Copying a bad resource bundle */ + fResource = nullptr; + } + return *this; +} + +ResourceBundle::~ResourceBundle() +{ + if(fResource != 0) { + ures_close(fResource); + } + if(fLocale != nullptr) { + delete(fLocale); + } +} + +ResourceBundle * +ResourceBundle::clone() const { + return new ResourceBundle(*this); +} + +UnicodeString ResourceBundle::getString(UErrorCode& status) const { + int32_t len = 0; + const char16_t *r = ures_getString(fResource, &len, &status); + return UnicodeString(true, r, len); +} + +const uint8_t *ResourceBundle::getBinary(int32_t& len, UErrorCode& status) const { + return ures_getBinary(fResource, &len, &status); +} + +const int32_t *ResourceBundle::getIntVector(int32_t& len, UErrorCode& status) const { + return ures_getIntVector(fResource, &len, &status); +} + +uint32_t ResourceBundle::getUInt(UErrorCode& status) const { + return ures_getUInt(fResource, &status); +} + +int32_t ResourceBundle::getInt(UErrorCode& status) const { + return ures_getInt(fResource, &status); +} + +const char *ResourceBundle::getName() const { + return ures_getName(fResource); +} + +const char *ResourceBundle::getKey() const { + return ures_getKey(fResource); +} + +UResType ResourceBundle::getType() const { + return ures_getType(fResource); +} + +int32_t ResourceBundle::getSize() const { + return ures_getSize(fResource); +} + +UBool ResourceBundle::hasNext() const { + return ures_hasNext(fResource); +} + +void ResourceBundle::resetIterator() { + ures_resetIterator(fResource); +} + +ResourceBundle ResourceBundle::getNext(UErrorCode& status) { + UResourceBundle r; + + ures_initStackObject(&r); + ures_getNextResource(fResource, &r, &status); + ResourceBundle res(&r, status); + if (U_SUCCESS(status)) { + ures_close(&r); + } + return res; +} + +UnicodeString ResourceBundle::getNextString(UErrorCode& status) { + int32_t len = 0; + const char16_t* r = ures_getNextString(fResource, &len, 0, &status); + return UnicodeString(true, r, len); +} + +UnicodeString ResourceBundle::getNextString(const char ** key, UErrorCode& status) { + int32_t len = 0; + const char16_t* r = ures_getNextString(fResource, &len, key, &status); + return UnicodeString(true, r, len); +} + +ResourceBundle ResourceBundle::get(int32_t indexR, UErrorCode& status) const { + UResourceBundle r; + + ures_initStackObject(&r); + ures_getByIndex(fResource, indexR, &r, &status); + ResourceBundle res(&r, status); + if (U_SUCCESS(status)) { + ures_close(&r); + } + return res; +} + +UnicodeString ResourceBundle::getStringEx(int32_t indexS, UErrorCode& status) const { + int32_t len = 0; + const char16_t* r = ures_getStringByIndex(fResource, indexS, &len, &status); + return UnicodeString(true, r, len); +} + +ResourceBundle ResourceBundle::get(const char* key, UErrorCode& status) const { + UResourceBundle r; + + ures_initStackObject(&r); + ures_getByKey(fResource, key, &r, &status); + ResourceBundle res(&r, status); + if (U_SUCCESS(status)) { + ures_close(&r); + } + return res; +} + +ResourceBundle ResourceBundle::getWithFallback(const char* key, UErrorCode& status){ + UResourceBundle r; + ures_initStackObject(&r); + ures_getByKeyWithFallback(fResource, key, &r, &status); + ResourceBundle res(&r, status); + if(U_SUCCESS(status)){ + ures_close(&r); + } + return res; +} +UnicodeString ResourceBundle::getStringEx(const char* key, UErrorCode& status) const { + int32_t len = 0; + const char16_t* r = ures_getStringByKey(fResource, key, &len, &status); + return UnicodeString(true, r, len); +} + +const char* +ResourceBundle::getVersionNumber() const +{ + return ures_getVersionNumberInternal(fResource); +} + +void ResourceBundle::getVersion(UVersionInfo versionInfo) const { + ures_getVersion(fResource, versionInfo); +} + +const Locale &ResourceBundle::getLocale() const { + static UMutex gLocaleLock; + Mutex lock(&gLocaleLock); + if (fLocale != nullptr) { + return *fLocale; + } + UErrorCode status = U_ZERO_ERROR; + const char *localeName = ures_getLocaleInternal(fResource, &status); + ResourceBundle *ncThis = const_cast(this); + ncThis->fLocale = new Locale(localeName); + return ncThis->fLocale != nullptr ? *ncThis->fLocale : Locale::getDefault(); +} + +const Locale ResourceBundle::getLocale(ULocDataLocaleType type, UErrorCode &status) const +{ + return ures_getLocaleByType(fResource, type, &status); +} + +U_NAMESPACE_END +//eof diff --git a/deps/icu-small/source/common/resbund_cnv.cpp b/deps/icu-small/source/common/resbund_cnv.cpp index 45c0b399bfff45..2f8a0ccdb7d556 100644 --- a/deps/icu-small/source/common/resbund_cnv.cpp +++ b/deps/icu-small/source/common/resbund_cnv.cpp @@ -1,57 +1,57 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1997-2006, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: resbund_cnv.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004aug25 -* created by: Markus W. Scherer -* -* Character conversion functions moved here from resbund.cpp -*/ - -#include "unicode/utypes.h" -#include "unicode/resbund.h" -#include "uinvchar.h" - -U_NAMESPACE_BEGIN - -ResourceBundle::ResourceBundle( const UnicodeString& path, - const Locale& locale, - UErrorCode& error) - :UObject(), fLocale(NULL) -{ - constructForLocale(path, locale, error); -} - -ResourceBundle::ResourceBundle( const UnicodeString& path, - UErrorCode& error) - :UObject(), fLocale(NULL) -{ - constructForLocale(path, Locale::getDefault(), error); -} - -void -ResourceBundle::constructForLocale(const UnicodeString& path, - const Locale& locale, - UErrorCode& error) -{ - if (path.isEmpty()) { - fResource = ures_open(NULL, locale.getName(), &error); - } - else { - UnicodeString nullTerminatedPath(path); - nullTerminatedPath.append((UChar)0); - fResource = ures_openU(nullTerminatedPath.getBuffer(), locale.getName(), &error); - } -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1997-2006, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: resbund_cnv.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004aug25 +* created by: Markus W. Scherer +* +* Character conversion functions moved here from resbund.cpp +*/ + +#include "unicode/utypes.h" +#include "unicode/resbund.h" +#include "uinvchar.h" + +U_NAMESPACE_BEGIN + +ResourceBundle::ResourceBundle( const UnicodeString& path, + const Locale& locale, + UErrorCode& error) + :UObject(), fLocale(nullptr) +{ + constructForLocale(path, locale, error); +} + +ResourceBundle::ResourceBundle( const UnicodeString& path, + UErrorCode& error) + :UObject(), fLocale(nullptr) +{ + constructForLocale(path, Locale::getDefault(), error); +} + +void +ResourceBundle::constructForLocale(const UnicodeString& path, + const Locale& locale, + UErrorCode& error) +{ + if (path.isEmpty()) { + fResource = ures_open(nullptr, locale.getName(), &error); + } + else { + UnicodeString nullTerminatedPath(path); + nullTerminatedPath.append((char16_t)0); + fResource = ures_openU(nullTerminatedPath.getBuffer(), locale.getName(), &error); + } +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/resource.cpp b/deps/icu-small/source/common/resource.cpp index 3d41a16029f24e..b3290850de0d6d 100644 --- a/deps/icu-small/source/common/resource.cpp +++ b/deps/icu-small/source/common/resource.cpp @@ -1,22 +1,22 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015-2016, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* resource.cpp -* -* created on: 2015nov04 -* created by: Markus W. Scherer -*/ - -#include "resource.h" - -U_NAMESPACE_BEGIN - -ResourceValue::~ResourceValue() {} - -ResourceSink::~ResourceSink() {} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2015-2016, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* resource.cpp +* +* created on: 2015nov04 +* created by: Markus W. Scherer +*/ + +#include "resource.h" + +U_NAMESPACE_BEGIN + +ResourceValue::~ResourceValue() {} + +ResourceSink::~ResourceSink() {} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/resource.h b/deps/icu-small/source/common/resource.h index 1483f7d6bcc6a5..58b4bda8d61b71 100644 --- a/deps/icu-small/source/common/resource.h +++ b/deps/icu-small/source/common/resource.h @@ -1,295 +1,295 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015-2016, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* resource.h -* -* created on: 2015nov04 -* created by: Markus W. Scherer -*/ - -#ifndef __URESOURCE_H__ -#define __URESOURCE_H__ - -/** - * \file - * \brief ICU resource bundle key and value types. - */ - -// Note: Ported from ICU4J class UResource and its nested classes, -// but the C++ classes are separate, not nested. - -// We use the Resource prefix for C++ classes, as usual. -// The UResource prefix would be used for C types. - -#include "unicode/utypes.h" -#include "unicode/unistr.h" -#include "unicode/ures.h" -#include "restrace.h" - -struct ResourceData; - -U_NAMESPACE_BEGIN - -class ResourceValue; - -// Note: In C++, we use const char * pointers for keys, -// rather than an abstraction like Java UResource.Key. - -/** - * Interface for iterating over a resource bundle array resource. - */ -class U_COMMON_API ResourceArray { -public: - /** Constructs an empty array object. */ - ResourceArray() : items16(NULL), items32(NULL), length(0) {} - - /** Only for implementation use. @internal */ - ResourceArray(const uint16_t *i16, const uint32_t *i32, int32_t len, - const ResourceTracer& traceInfo) : - items16(i16), items32(i32), length(len), - fTraceInfo(traceInfo) {} - - /** - * @return The number of items in the array resource. - */ - int32_t getSize() const { return length; } - /** - * @param i Array item index. - * @param value Output-only, receives the value of the i'th item. - * @return true if i is non-negative and less than getSize(). - */ - UBool getValue(int32_t i, ResourceValue &value) const; - - /** Only for implementation use. @internal */ - uint32_t internalGetResource(const ResourceData *pResData, int32_t i) const; - -private: - const uint16_t *items16; - const uint32_t *items32; - int32_t length; - ResourceTracer fTraceInfo; -}; - -/** - * Interface for iterating over a resource bundle table resource. - */ -class U_COMMON_API ResourceTable { -public: - /** Constructs an empty table object. */ - ResourceTable() : keys16(NULL), keys32(NULL), items16(NULL), items32(NULL), length(0) {} - - /** Only for implementation use. @internal */ - ResourceTable(const uint16_t *k16, const int32_t *k32, - const uint16_t *i16, const uint32_t *i32, int32_t len, - const ResourceTracer& traceInfo) : - keys16(k16), keys32(k32), items16(i16), items32(i32), length(len), - fTraceInfo(traceInfo) {} - - /** - * @return The number of items in the array resource. - */ - int32_t getSize() const { return length; } - /** - * @param i Table item index. - * @param key Output-only, receives the key of the i'th item. - * @param value Output-only, receives the value of the i'th item. - * @return true if i is non-negative and less than getSize(). - */ - UBool getKeyAndValue(int32_t i, const char *&key, ResourceValue &value) const; - - /** - * @param key Key string to find in the table. - * @param value Output-only, receives the value of the item with that key. - * @return true if the table contains the key. - */ - UBool findValue(const char *key, ResourceValue &value) const; - -private: - const uint16_t *keys16; - const int32_t *keys32; - const uint16_t *items16; - const uint32_t *items32; - int32_t length; - ResourceTracer fTraceInfo; -}; - -/** - * Represents a resource bundle item's value. - * Avoids object creations as much as possible. - * Mutable, not thread-safe. - */ -class U_COMMON_API ResourceValue : public UObject { -public: - virtual ~ResourceValue(); - - /** - * @return ICU resource type, for example, URES_STRING - */ - virtual UResType getType() const = 0; - - /** - * Sets U_RESOURCE_TYPE_MISMATCH if this is not a string resource. - * - * @see ures_getString() - */ - virtual const UChar *getString(int32_t &length, UErrorCode &errorCode) const = 0; - - inline UnicodeString getUnicodeString(UErrorCode &errorCode) const { - int32_t len = 0; - const UChar *r = getString(len, errorCode); - return UnicodeString(true, r, len); - } - - /** - * Sets U_RESOURCE_TYPE_MISMATCH if this is not an alias resource. - */ - virtual const UChar *getAliasString(int32_t &length, UErrorCode &errorCode) const = 0; - - inline UnicodeString getAliasUnicodeString(UErrorCode &errorCode) const { - int32_t len = 0; - const UChar *r = getAliasString(len, errorCode); - return UnicodeString(true, r, len); - } - - /** - * Sets U_RESOURCE_TYPE_MISMATCH if this is not an integer resource. - * - * @see ures_getInt() - */ - virtual int32_t getInt(UErrorCode &errorCode) const = 0; - - /** - * Sets U_RESOURCE_TYPE_MISMATCH if this is not an integer resource. - * - * @see ures_getUInt() - */ - virtual uint32_t getUInt(UErrorCode &errorCode) const = 0; - - /** - * Sets U_RESOURCE_TYPE_MISMATCH if this is not an intvector resource. - * - * @see ures_getIntVector() - */ - virtual const int32_t *getIntVector(int32_t &length, UErrorCode &errorCode) const = 0; - - /** - * Sets U_RESOURCE_TYPE_MISMATCH if this is not a binary-blob resource. - * - * @see ures_getBinary() - */ - virtual const uint8_t *getBinary(int32_t &length, UErrorCode &errorCode) const = 0; - - /** - * Sets U_RESOURCE_TYPE_MISMATCH if this is not an array resource - */ - virtual ResourceArray getArray(UErrorCode &errorCode) const = 0; - - /** - * Sets U_RESOURCE_TYPE_MISMATCH if this is not a table resource - */ - virtual ResourceTable getTable(UErrorCode &errorCode) const = 0; - - /** - * Is this a no-fallback/no-inheritance marker string? - * Such a marker is used for - * CLDR no-fallback data values of (three empty-set symbols)=={2205, 2205, 2205} - * when enumerating tables with fallback from the specific resource bundle to root. - * - * @return true if this is a no-inheritance marker string - */ - virtual UBool isNoInheritanceMarker() const = 0; - - /** - * Sets the dest strings from the string values in this array resource. - * - * @return the number of strings in this array resource. - * If greater than capacity, then an overflow error is set. - * - * Sets U_RESOURCE_TYPE_MISMATCH if this is not an array resource - * or if any of the array items is not a string - */ - virtual int32_t getStringArray(UnicodeString *dest, int32_t capacity, - UErrorCode &errorCode) const = 0; - - /** - * Same as - *

-     * if (getType() == URES_STRING) {
-     *     return new String[] { getString(); }
-     * } else {
-     *     return getStringArray();
-     * }
-     * 
- * - * Sets U_RESOURCE_TYPE_MISMATCH if this is - * neither a string resource nor an array resource containing strings - * @see getString() - * @see getStringArray() - */ - virtual int32_t getStringArrayOrStringAsArray(UnicodeString *dest, int32_t capacity, - UErrorCode &errorCode) const = 0; - - /** - * Same as - *
-     * if (getType() == URES_STRING) {
-     *     return getString();
-     * } else {
-     *     return getStringArray()[0];
-     * }
-     * 
- * - * Sets U_RESOURCE_TYPE_MISMATCH if this is - * neither a string resource nor an array resource containing strings - * @see getString() - * @see getStringArray() - */ - virtual UnicodeString getStringOrFirstOfArray(UErrorCode &errorCode) const = 0; - -protected: - ResourceValue() {} - -private: - ResourceValue(const ResourceValue &); // no copy constructor - ResourceValue &operator=(const ResourceValue &); // no assignment operator -}; - -/** - * Sink for ICU resource bundle contents. - */ -class U_COMMON_API ResourceSink : public UObject { -public: - ResourceSink() {} - virtual ~ResourceSink(); - - /** - * Called once for each bundle (child-parent-...-root). - * The value is normally an array or table resource, - * and implementations of this method normally iterate over the - * tree of resource items stored there. - * - * @param key The key string of the enumeration-start resource. - * Empty if the enumeration starts at the top level of the bundle. - * @param value Call getArray() or getTable() as appropriate. Then reuse for - * output values from Array and Table getters. Note: ResourceTable and - * ResourceArray instances must outlive the ResourceValue instance for - * ResourceTracer to be happy. - * @param noFallback true if the bundle has no parent; - * that is, its top-level table has the nofallback attribute, - * or it is the root bundle of a locale tree. - */ - virtual void put(const char *key, ResourceValue &value, UBool noFallback, - UErrorCode &errorCode) = 0; - -private: - ResourceSink(const ResourceSink &) = delete; // no copy constructor - ResourceSink &operator=(const ResourceSink &) = delete; // no assignment operator -}; - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2015-2016, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* resource.h +* +* created on: 2015nov04 +* created by: Markus W. Scherer +*/ + +#ifndef __URESOURCE_H__ +#define __URESOURCE_H__ + +/** + * \file + * \brief ICU resource bundle key and value types. + */ + +// Note: Ported from ICU4J class UResource and its nested classes, +// but the C++ classes are separate, not nested. + +// We use the Resource prefix for C++ classes, as usual. +// The UResource prefix would be used for C types. + +#include "unicode/utypes.h" +#include "unicode/unistr.h" +#include "unicode/ures.h" +#include "restrace.h" + +struct ResourceData; + +U_NAMESPACE_BEGIN + +class ResourceValue; + +// Note: In C++, we use const char * pointers for keys, +// rather than an abstraction like Java UResource.Key. + +/** + * Interface for iterating over a resource bundle array resource. + */ +class U_COMMON_API ResourceArray { +public: + /** Constructs an empty array object. */ + ResourceArray() : items16(nullptr), items32(nullptr), length(0) {} + + /** Only for implementation use. @internal */ + ResourceArray(const uint16_t *i16, const uint32_t *i32, int32_t len, + const ResourceTracer& traceInfo) : + items16(i16), items32(i32), length(len), + fTraceInfo(traceInfo) {} + + /** + * @return The number of items in the array resource. + */ + int32_t getSize() const { return length; } + /** + * @param i Array item index. + * @param value Output-only, receives the value of the i'th item. + * @return true if i is non-negative and less than getSize(). + */ + UBool getValue(int32_t i, ResourceValue &value) const; + + /** Only for implementation use. @internal */ + uint32_t internalGetResource(const ResourceData *pResData, int32_t i) const; + +private: + const uint16_t *items16; + const uint32_t *items32; + int32_t length; + ResourceTracer fTraceInfo; +}; + +/** + * Interface for iterating over a resource bundle table resource. + */ +class U_COMMON_API ResourceTable { +public: + /** Constructs an empty table object. */ + ResourceTable() : keys16(nullptr), keys32(nullptr), items16(nullptr), items32(nullptr), length(0) {} + + /** Only for implementation use. @internal */ + ResourceTable(const uint16_t *k16, const int32_t *k32, + const uint16_t *i16, const uint32_t *i32, int32_t len, + const ResourceTracer& traceInfo) : + keys16(k16), keys32(k32), items16(i16), items32(i32), length(len), + fTraceInfo(traceInfo) {} + + /** + * @return The number of items in the array resource. + */ + int32_t getSize() const { return length; } + /** + * @param i Table item index. + * @param key Output-only, receives the key of the i'th item. + * @param value Output-only, receives the value of the i'th item. + * @return true if i is non-negative and less than getSize(). + */ + UBool getKeyAndValue(int32_t i, const char *&key, ResourceValue &value) const; + + /** + * @param key Key string to find in the table. + * @param value Output-only, receives the value of the item with that key. + * @return true if the table contains the key. + */ + UBool findValue(const char *key, ResourceValue &value) const; + +private: + const uint16_t *keys16; + const int32_t *keys32; + const uint16_t *items16; + const uint32_t *items32; + int32_t length; + ResourceTracer fTraceInfo; +}; + +/** + * Represents a resource bundle item's value. + * Avoids object creations as much as possible. + * Mutable, not thread-safe. + */ +class U_COMMON_API ResourceValue : public UObject { +public: + virtual ~ResourceValue(); + + /** + * @return ICU resource type, for example, URES_STRING + */ + virtual UResType getType() const = 0; + + /** + * Sets U_RESOURCE_TYPE_MISMATCH if this is not a string resource. + * + * @see ures_getString() + */ + virtual const char16_t *getString(int32_t &length, UErrorCode &errorCode) const = 0; + + inline UnicodeString getUnicodeString(UErrorCode &errorCode) const { + int32_t len = 0; + const char16_t *r = getString(len, errorCode); + return UnicodeString(true, r, len); + } + + /** + * Sets U_RESOURCE_TYPE_MISMATCH if this is not an alias resource. + */ + virtual const char16_t *getAliasString(int32_t &length, UErrorCode &errorCode) const = 0; + + inline UnicodeString getAliasUnicodeString(UErrorCode &errorCode) const { + int32_t len = 0; + const char16_t *r = getAliasString(len, errorCode); + return UnicodeString(true, r, len); + } + + /** + * Sets U_RESOURCE_TYPE_MISMATCH if this is not an integer resource. + * + * @see ures_getInt() + */ + virtual int32_t getInt(UErrorCode &errorCode) const = 0; + + /** + * Sets U_RESOURCE_TYPE_MISMATCH if this is not an integer resource. + * + * @see ures_getUInt() + */ + virtual uint32_t getUInt(UErrorCode &errorCode) const = 0; + + /** + * Sets U_RESOURCE_TYPE_MISMATCH if this is not an intvector resource. + * + * @see ures_getIntVector() + */ + virtual const int32_t *getIntVector(int32_t &length, UErrorCode &errorCode) const = 0; + + /** + * Sets U_RESOURCE_TYPE_MISMATCH if this is not a binary-blob resource. + * + * @see ures_getBinary() + */ + virtual const uint8_t *getBinary(int32_t &length, UErrorCode &errorCode) const = 0; + + /** + * Sets U_RESOURCE_TYPE_MISMATCH if this is not an array resource + */ + virtual ResourceArray getArray(UErrorCode &errorCode) const = 0; + + /** + * Sets U_RESOURCE_TYPE_MISMATCH if this is not a table resource + */ + virtual ResourceTable getTable(UErrorCode &errorCode) const = 0; + + /** + * Is this a no-fallback/no-inheritance marker string? + * Such a marker is used for + * CLDR no-fallback data values of (three empty-set symbols)=={2205, 2205, 2205} + * when enumerating tables with fallback from the specific resource bundle to root. + * + * @return true if this is a no-inheritance marker string + */ + virtual UBool isNoInheritanceMarker() const = 0; + + /** + * Sets the dest strings from the string values in this array resource. + * + * @return the number of strings in this array resource. + * If greater than capacity, then an overflow error is set. + * + * Sets U_RESOURCE_TYPE_MISMATCH if this is not an array resource + * or if any of the array items is not a string + */ + virtual int32_t getStringArray(UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) const = 0; + + /** + * Same as + *
+     * if (getType() == URES_STRING) {
+     *     return new String[] { getString(); }
+     * } else {
+     *     return getStringArray();
+     * }
+     * 
+ * + * Sets U_RESOURCE_TYPE_MISMATCH if this is + * neither a string resource nor an array resource containing strings + * @see getString() + * @see getStringArray() + */ + virtual int32_t getStringArrayOrStringAsArray(UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) const = 0; + + /** + * Same as + *
+     * if (getType() == URES_STRING) {
+     *     return getString();
+     * } else {
+     *     return getStringArray()[0];
+     * }
+     * 
+ * + * Sets U_RESOURCE_TYPE_MISMATCH if this is + * neither a string resource nor an array resource containing strings + * @see getString() + * @see getStringArray() + */ + virtual UnicodeString getStringOrFirstOfArray(UErrorCode &errorCode) const = 0; + +protected: + ResourceValue() {} + +private: + ResourceValue(const ResourceValue &); // no copy constructor + ResourceValue &operator=(const ResourceValue &); // no assignment operator +}; + +/** + * Sink for ICU resource bundle contents. + */ +class U_COMMON_API ResourceSink : public UObject { +public: + ResourceSink() {} + virtual ~ResourceSink(); + + /** + * Called once for each bundle (child-parent-...-root). + * The value is normally an array or table resource, + * and implementations of this method normally iterate over the + * tree of resource items stored there. + * + * @param key The key string of the enumeration-start resource. + * Empty if the enumeration starts at the top level of the bundle. + * @param value Call getArray() or getTable() as appropriate. Then reuse for + * output values from Array and Table getters. Note: ResourceTable and + * ResourceArray instances must outlive the ResourceValue instance for + * ResourceTracer to be happy. + * @param noFallback true if the bundle has no parent; + * that is, its top-level table has the nofallback attribute, + * or it is the root bundle of a locale tree. + */ + virtual void put(const char *key, ResourceValue &value, UBool noFallback, + UErrorCode &errorCode) = 0; + +private: + ResourceSink(const ResourceSink &) = delete; // no copy constructor + ResourceSink &operator=(const ResourceSink &) = delete; // no assignment operator +}; + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/common/restrace.cpp b/deps/icu-small/source/common/restrace.cpp index 1f83372d682a7f..1f76167a4129f9 100644 --- a/deps/icu-small/source/common/restrace.cpp +++ b/deps/icu-small/source/common/restrace.cpp @@ -1,133 +1,133 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#include "unicode/utypes.h" - -#if U_ENABLE_TRACING - -#include "restrace.h" -#include "charstr.h" -#include "cstring.h" -#include "utracimp.h" -#include "uresimp.h" -#include "uassert.h" -#include "util.h" - -U_NAMESPACE_BEGIN - -ResourceTracer::~ResourceTracer() = default; - -void ResourceTracer::trace(const char* resType) const { - U_ASSERT(fResB || fParent); - UTRACE_ENTRY(UTRACE_UDATA_RESOURCE); - UErrorCode status = U_ZERO_ERROR; - - CharString filePath; - getFilePath(filePath, status); - - CharString resPath; - getResPath(resPath, status); - - // The longest type ("intvector") is 9 chars - const char kSpaces[] = " "; - CharString format; - format.append(kSpaces, sizeof(kSpaces) - 1 - uprv_strlen(resType), status); - format.append("(%s) %s @ %s", status); - - UTRACE_DATA3(UTRACE_VERBOSE, - format.data(), - resType, - filePath.data(), - resPath.data()); - UTRACE_EXIT_STATUS(status); -} - -void ResourceTracer::traceOpen() const { - U_ASSERT(fResB); - UTRACE_ENTRY(UTRACE_UDATA_BUNDLE); - UErrorCode status = U_ZERO_ERROR; - - CharString filePath; - UTRACE_DATA1(UTRACE_VERBOSE, "%s", getFilePath(filePath, status).data()); - UTRACE_EXIT_STATUS(status); -} - -CharString& ResourceTracer::getFilePath(CharString& output, UErrorCode& status) const { - if (fResB) { - // Note: if you get a segfault around here, check that ResourceTable and - // ResourceArray instances outlive ResourceValue instances referring to - // their contents: - output.append(fResB->fData->fPath, status); - output.append('/', status); - output.append(fResB->fData->fName, status); - output.append(".res", status); - } else { - fParent->getFilePath(output, status); - } - return output; -} - -CharString& ResourceTracer::getResPath(CharString& output, UErrorCode& status) const { - if (fResB) { - output.append('/', status); - output.append(fResB->fResPath, status); - // removing the trailing / - U_ASSERT(output[output.length()-1] == '/'); - output.truncate(output.length()-1); - } else { - fParent->getResPath(output, status); - } - if (fKey) { - output.append('/', status); - output.append(fKey, status); - } - if (fIndex != -1) { - output.append('[', status); - UnicodeString indexString; - ICU_Utility::appendNumber(indexString, fIndex); - output.appendInvariantChars(indexString, status); - output.append(']', status); - } - return output; -} - -void FileTracer::traceOpen(const char* path, const char* type, const char* name) { - if (uprv_strcmp(type, "res") == 0) { - traceOpenResFile(path, name); - } else { - traceOpenDataFile(path, type, name); - } -} - -void FileTracer::traceOpenDataFile(const char* path, const char* type, const char* name) { - UTRACE_ENTRY(UTRACE_UDATA_DATA_FILE); - UErrorCode status = U_ZERO_ERROR; - - CharString filePath; - filePath.append(path, status); - filePath.append('/', status); - filePath.append(name, status); - filePath.append('.', status); - filePath.append(type, status); - - UTRACE_DATA1(UTRACE_VERBOSE, "%s", filePath.data()); - UTRACE_EXIT_STATUS(status); -} - -void FileTracer::traceOpenResFile(const char* path, const char* name) { - UTRACE_ENTRY(UTRACE_UDATA_RES_FILE); - UErrorCode status = U_ZERO_ERROR; - - CharString filePath; - filePath.append(path, status); - filePath.append('/', status); - filePath.append(name, status); - filePath.append(".res", status); - - UTRACE_DATA1(UTRACE_VERBOSE, "%s", filePath.data()); - UTRACE_EXIT_STATUS(status); -} - -U_NAMESPACE_END - -#endif // U_ENABLE_TRACING +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if U_ENABLE_TRACING + +#include "restrace.h" +#include "charstr.h" +#include "cstring.h" +#include "utracimp.h" +#include "uresimp.h" +#include "uassert.h" +#include "util.h" + +U_NAMESPACE_BEGIN + +ResourceTracer::~ResourceTracer() = default; + +void ResourceTracer::trace(const char* resType) const { + U_ASSERT(fResB || fParent); + UTRACE_ENTRY(UTRACE_UDATA_RESOURCE); + UErrorCode status = U_ZERO_ERROR; + + CharString filePath; + getFilePath(filePath, status); + + CharString resPath; + getResPath(resPath, status); + + // The longest type ("intvector") is 9 chars + const char kSpaces[] = " "; + CharString format; + format.append(kSpaces, sizeof(kSpaces) - 1 - uprv_strlen(resType), status); + format.append("(%s) %s @ %s", status); + + UTRACE_DATA3(UTRACE_VERBOSE, + format.data(), + resType, + filePath.data(), + resPath.data()); + UTRACE_EXIT_STATUS(status); +} + +void ResourceTracer::traceOpen() const { + U_ASSERT(fResB); + UTRACE_ENTRY(UTRACE_UDATA_BUNDLE); + UErrorCode status = U_ZERO_ERROR; + + CharString filePath; + UTRACE_DATA1(UTRACE_VERBOSE, "%s", getFilePath(filePath, status).data()); + UTRACE_EXIT_STATUS(status); +} + +CharString& ResourceTracer::getFilePath(CharString& output, UErrorCode& status) const { + if (fResB) { + // Note: if you get a segfault around here, check that ResourceTable and + // ResourceArray instances outlive ResourceValue instances referring to + // their contents: + output.append(fResB->fData->fPath, status); + output.append('/', status); + output.append(fResB->fData->fName, status); + output.append(".res", status); + } else { + fParent->getFilePath(output, status); + } + return output; +} + +CharString& ResourceTracer::getResPath(CharString& output, UErrorCode& status) const { + if (fResB) { + output.append('/', status); + output.append(fResB->fResPath, status); + // removing the trailing / + U_ASSERT(output[output.length()-1] == '/'); + output.truncate(output.length()-1); + } else { + fParent->getResPath(output, status); + } + if (fKey) { + output.append('/', status); + output.append(fKey, status); + } + if (fIndex != -1) { + output.append('[', status); + UnicodeString indexString; + ICU_Utility::appendNumber(indexString, fIndex); + output.appendInvariantChars(indexString, status); + output.append(']', status); + } + return output; +} + +void FileTracer::traceOpen(const char* path, const char* type, const char* name) { + if (uprv_strcmp(type, "res") == 0) { + traceOpenResFile(path, name); + } else { + traceOpenDataFile(path, type, name); + } +} + +void FileTracer::traceOpenDataFile(const char* path, const char* type, const char* name) { + UTRACE_ENTRY(UTRACE_UDATA_DATA_FILE); + UErrorCode status = U_ZERO_ERROR; + + CharString filePath; + filePath.append(path, status); + filePath.append('/', status); + filePath.append(name, status); + filePath.append('.', status); + filePath.append(type, status); + + UTRACE_DATA1(UTRACE_VERBOSE, "%s", filePath.data()); + UTRACE_EXIT_STATUS(status); +} + +void FileTracer::traceOpenResFile(const char* path, const char* name) { + UTRACE_ENTRY(UTRACE_UDATA_RES_FILE); + UErrorCode status = U_ZERO_ERROR; + + CharString filePath; + filePath.append(path, status); + filePath.append('/', status); + filePath.append(name, status); + filePath.append(".res", status); + + UTRACE_DATA1(UTRACE_VERBOSE, "%s", filePath.data()); + UTRACE_EXIT_STATUS(status); +} + +U_NAMESPACE_END + +#endif // U_ENABLE_TRACING diff --git a/deps/icu-small/source/common/restrace.h b/deps/icu-small/source/common/restrace.h index ef29eaed578107..66e2cb750f1f70 100644 --- a/deps/icu-small/source/common/restrace.h +++ b/deps/icu-small/source/common/restrace.h @@ -1,147 +1,147 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#ifndef __RESTRACE_H__ -#define __RESTRACE_H__ - -#include "unicode/utypes.h" - -#if U_ENABLE_TRACING - -struct UResourceBundle; - -U_NAMESPACE_BEGIN - -class CharString; - -/** - * Instances of this class store information used to trace reads from resource - * bundles when ICU is built with --enable-tracing. - * - * All arguments of type const UResourceBundle*, const char*, and - * const ResourceTracer& are stored as pointers. The caller must retain - * ownership for the lifetime of this ResourceTracer. - * - * Exported as U_COMMON_API for Windows because it is a value field - * in other exported types. - */ -class U_COMMON_API ResourceTracer { -public: - ResourceTracer() : - fResB(nullptr), - fParent(nullptr), - fKey(nullptr), - fIndex(-1) {} - - ResourceTracer(const UResourceBundle* resB) : - fResB(resB), - fParent(nullptr), - fKey(nullptr), - fIndex(-1) {} - - ResourceTracer(const UResourceBundle* resB, const char* key) : - fResB(resB), - fParent(nullptr), - fKey(key), - fIndex(-1) {} - - ResourceTracer(const UResourceBundle* resB, int32_t index) : - fResB(resB), - fParent(nullptr), - fKey(nullptr), - fIndex(index) {} - - ResourceTracer(const ResourceTracer& parent, const char* key) : - fResB(nullptr), - fParent(&parent), - fKey(key), - fIndex(-1) {} - - ResourceTracer(const ResourceTracer& parent, int32_t index) : - fResB(nullptr), - fParent(&parent), - fKey(nullptr), - fIndex(index) {} - - ~ResourceTracer(); - - void trace(const char* type) const; - void traceOpen() const; - - /** - * Calls trace() if the resB or parent provided to the constructor was - * non-null; otherwise, does nothing. - */ - void maybeTrace(const char* type) const { - if (fResB || fParent) { - trace(type); - } - } - -private: - const UResourceBundle* fResB; - const ResourceTracer* fParent; - const char* fKey; - int32_t fIndex; - - CharString& getFilePath(CharString& output, UErrorCode& status) const; - - CharString& getResPath(CharString& output, UErrorCode& status) const; -}; - -/** - * This class provides methods to trace data file reads when ICU is built - * with --enable-tracing. - */ -class FileTracer { -public: - static void traceOpen(const char* path, const char* type, const char* name); - -private: - static void traceOpenDataFile(const char* path, const char* type, const char* name); - static void traceOpenResFile(const char* path, const char* name); -}; - -U_NAMESPACE_END - -#else // U_ENABLE_TRACING - -U_NAMESPACE_BEGIN - -/** - * Default trivial implementation when --enable-tracing is not used. - */ -class U_COMMON_API ResourceTracer { -public: - ResourceTracer() {} - - ResourceTracer(const void*) {} - - ResourceTracer(const void*, const char*) {} - - ResourceTracer(const void*, int32_t) {} - - ResourceTracer(const ResourceTracer&, const char*) {} - - ResourceTracer(const ResourceTracer&, int32_t) {} - - void trace(const char*) const {} - - void traceOpen() const {} - - void maybeTrace(const char*) const {} -}; - -/** - * Default trivial implementation when --enable-tracing is not used. - */ -class FileTracer { -public: - static void traceOpen(const char*, const char*, const char*) {} -}; - -U_NAMESPACE_END - -#endif // U_ENABLE_TRACING - -#endif //__RESTRACE_H__ +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#ifndef __RESTRACE_H__ +#define __RESTRACE_H__ + +#include "unicode/utypes.h" + +#if U_ENABLE_TRACING + +struct UResourceBundle; + +U_NAMESPACE_BEGIN + +class CharString; + +/** + * Instances of this class store information used to trace reads from resource + * bundles when ICU is built with --enable-tracing. + * + * All arguments of type const UResourceBundle*, const char*, and + * const ResourceTracer& are stored as pointers. The caller must retain + * ownership for the lifetime of this ResourceTracer. + * + * Exported as U_COMMON_API for Windows because it is a value field + * in other exported types. + */ +class U_COMMON_API ResourceTracer { +public: + ResourceTracer() : + fResB(nullptr), + fParent(nullptr), + fKey(nullptr), + fIndex(-1) {} + + ResourceTracer(const UResourceBundle* resB) : + fResB(resB), + fParent(nullptr), + fKey(nullptr), + fIndex(-1) {} + + ResourceTracer(const UResourceBundle* resB, const char* key) : + fResB(resB), + fParent(nullptr), + fKey(key), + fIndex(-1) {} + + ResourceTracer(const UResourceBundle* resB, int32_t index) : + fResB(resB), + fParent(nullptr), + fKey(nullptr), + fIndex(index) {} + + ResourceTracer(const ResourceTracer& parent, const char* key) : + fResB(nullptr), + fParent(&parent), + fKey(key), + fIndex(-1) {} + + ResourceTracer(const ResourceTracer& parent, int32_t index) : + fResB(nullptr), + fParent(&parent), + fKey(nullptr), + fIndex(index) {} + + ~ResourceTracer(); + + void trace(const char* type) const; + void traceOpen() const; + + /** + * Calls trace() if the resB or parent provided to the constructor was + * non-null; otherwise, does nothing. + */ + void maybeTrace(const char* type) const { + if (fResB || fParent) { + trace(type); + } + } + +private: + const UResourceBundle* fResB; + const ResourceTracer* fParent; + const char* fKey; + int32_t fIndex; + + CharString& getFilePath(CharString& output, UErrorCode& status) const; + + CharString& getResPath(CharString& output, UErrorCode& status) const; +}; + +/** + * This class provides methods to trace data file reads when ICU is built + * with --enable-tracing. + */ +class FileTracer { +public: + static void traceOpen(const char* path, const char* type, const char* name); + +private: + static void traceOpenDataFile(const char* path, const char* type, const char* name); + static void traceOpenResFile(const char* path, const char* name); +}; + +U_NAMESPACE_END + +#else // U_ENABLE_TRACING + +U_NAMESPACE_BEGIN + +/** + * Default trivial implementation when --enable-tracing is not used. + */ +class U_COMMON_API ResourceTracer { +public: + ResourceTracer() {} + + ResourceTracer(const void*) {} + + ResourceTracer(const void*, const char*) {} + + ResourceTracer(const void*, int32_t) {} + + ResourceTracer(const ResourceTracer&, const char*) {} + + ResourceTracer(const ResourceTracer&, int32_t) {} + + void trace(const char*) const {} + + void traceOpen() const {} + + void maybeTrace(const char*) const {} +}; + +/** + * Default trivial implementation when --enable-tracing is not used. + */ +class FileTracer { +public: + static void traceOpen(const char*, const char*, const char*) {} +}; + +U_NAMESPACE_END + +#endif // U_ENABLE_TRACING + +#endif //__RESTRACE_H__ diff --git a/deps/icu-small/source/common/ruleiter.cpp b/deps/icu-small/source/common/ruleiter.cpp index 33ffd3d8337205..d4e2af2c5c2bf7 100644 --- a/deps/icu-small/source/common/ruleiter.cpp +++ b/deps/icu-small/source/common/ruleiter.cpp @@ -1,162 +1,162 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2003-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: September 24 2003 -* Since: ICU 2.8 -********************************************************************** -*/ -#include "ruleiter.h" -#include "unicode/parsepos.h" -#include "unicode/symtable.h" -#include "unicode/unistr.h" -#include "unicode/utf16.h" -#include "patternprops.h" - -/* \U87654321 or \ud800\udc00 */ -#define MAX_U_NOTATION_LEN 12 - -U_NAMESPACE_BEGIN - -RuleCharacterIterator::RuleCharacterIterator(const UnicodeString& theText, const SymbolTable* theSym, - ParsePosition& thePos) : - text(theText), - pos(thePos), - sym(theSym), - buf(0), - bufPos(0) -{} - -UBool RuleCharacterIterator::atEnd() const { - return buf == 0 && pos.getIndex() == text.length(); -} - -UChar32 RuleCharacterIterator::next(int32_t options, UBool& isEscaped, UErrorCode& ec) { - if (U_FAILURE(ec)) return DONE; - - UChar32 c = DONE; - isEscaped = false; - - for (;;) { - c = _current(); - _advance(U16_LENGTH(c)); - - if (c == SymbolTable::SYMBOL_REF && buf == 0 && - (options & PARSE_VARIABLES) != 0 && sym != 0) { - UnicodeString name = sym->parseReference(text, pos, text.length()); - // If name is empty there was an isolated SYMBOL_REF; - // return it. Caller must be prepared for this. - if (name.length() == 0) { - break; - } - bufPos = 0; - buf = sym->lookup(name); - if (buf == 0) { - ec = U_UNDEFINED_VARIABLE; - return DONE; - } - // Handle empty variable value - if (buf->length() == 0) { - buf = 0; - } - continue; - } - - if ((options & SKIP_WHITESPACE) != 0 && PatternProps::isWhiteSpace(c)) { - continue; - } - - if (c == 0x5C /*'\\'*/ && (options & PARSE_ESCAPES) != 0) { - UnicodeString tempEscape; - int32_t offset = 0; - c = lookahead(tempEscape, MAX_U_NOTATION_LEN).unescapeAt(offset); - jumpahead(offset); - isEscaped = true; - if (c < 0) { - ec = U_MALFORMED_UNICODE_ESCAPE; - return DONE; - } - } - - break; - } - - return c; -} - -void RuleCharacterIterator::getPos(RuleCharacterIterator::Pos& p) const { - p.buf = buf; - p.pos = pos.getIndex(); - p.bufPos = bufPos; -} - -void RuleCharacterIterator::setPos(const RuleCharacterIterator::Pos& p) { - buf = p.buf; - pos.setIndex(p.pos); - bufPos = p.bufPos; -} - -void RuleCharacterIterator::skipIgnored(int32_t options) { - if ((options & SKIP_WHITESPACE) != 0) { - for (;;) { - UChar32 a = _current(); - if (!PatternProps::isWhiteSpace(a)) break; - _advance(U16_LENGTH(a)); - } - } -} - -UnicodeString& RuleCharacterIterator::lookahead(UnicodeString& result, int32_t maxLookAhead) const { - if (maxLookAhead < 0) { - maxLookAhead = 0x7FFFFFFF; - } - if (buf != 0) { - buf->extract(bufPos, maxLookAhead, result); - } else { - text.extract(pos.getIndex(), maxLookAhead, result); - } - return result; -} - -void RuleCharacterIterator::jumpahead(int32_t count) { - _advance(count); -} - -/* -UnicodeString& RuleCharacterIterator::toString(UnicodeString& result) const { - int32_t b = pos.getIndex(); - text.extract(0, b, result); - return result.append((UChar) 0x7C).append(text, b, 0x7FFFFFFF); // Insert '|' at index -} -*/ - -UChar32 RuleCharacterIterator::_current() const { - if (buf != 0) { - return buf->char32At(bufPos); - } else { - int i = pos.getIndex(); - return (i < text.length()) ? text.char32At(i) : (UChar32)DONE; - } -} - -void RuleCharacterIterator::_advance(int32_t count) { - if (buf != 0) { - bufPos += count; - if (bufPos == buf->length()) { - buf = 0; - } - } else { - pos.setIndex(pos.getIndex() + count); - if (pos.getIndex() > text.length()) { - pos.setIndex(text.length()); - } - } -} - -U_NAMESPACE_END - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2003-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: September 24 2003 +* Since: ICU 2.8 +********************************************************************** +*/ +#include "ruleiter.h" +#include "unicode/parsepos.h" +#include "unicode/symtable.h" +#include "unicode/unistr.h" +#include "unicode/utf16.h" +#include "patternprops.h" + +/* \U87654321 or \ud800\udc00 */ +#define MAX_U_NOTATION_LEN 12 + +U_NAMESPACE_BEGIN + +RuleCharacterIterator::RuleCharacterIterator(const UnicodeString& theText, const SymbolTable* theSym, + ParsePosition& thePos) : + text(theText), + pos(thePos), + sym(theSym), + buf(0), + bufPos(0) +{} + +UBool RuleCharacterIterator::atEnd() const { + return buf == 0 && pos.getIndex() == text.length(); +} + +UChar32 RuleCharacterIterator::next(int32_t options, UBool& isEscaped, UErrorCode& ec) { + if (U_FAILURE(ec)) return DONE; + + UChar32 c = DONE; + isEscaped = false; + + for (;;) { + c = _current(); + _advance(U16_LENGTH(c)); + + if (c == SymbolTable::SYMBOL_REF && buf == 0 && + (options & PARSE_VARIABLES) != 0 && sym != 0) { + UnicodeString name = sym->parseReference(text, pos, text.length()); + // If name is empty there was an isolated SYMBOL_REF; + // return it. Caller must be prepared for this. + if (name.length() == 0) { + break; + } + bufPos = 0; + buf = sym->lookup(name); + if (buf == 0) { + ec = U_UNDEFINED_VARIABLE; + return DONE; + } + // Handle empty variable value + if (buf->length() == 0) { + buf = 0; + } + continue; + } + + if ((options & SKIP_WHITESPACE) != 0 && PatternProps::isWhiteSpace(c)) { + continue; + } + + if (c == 0x5C /*'\\'*/ && (options & PARSE_ESCAPES) != 0) { + UnicodeString tempEscape; + int32_t offset = 0; + c = lookahead(tempEscape, MAX_U_NOTATION_LEN).unescapeAt(offset); + jumpahead(offset); + isEscaped = true; + if (c < 0) { + ec = U_MALFORMED_UNICODE_ESCAPE; + return DONE; + } + } + + break; + } + + return c; +} + +void RuleCharacterIterator::getPos(RuleCharacterIterator::Pos& p) const { + p.buf = buf; + p.pos = pos.getIndex(); + p.bufPos = bufPos; +} + +void RuleCharacterIterator::setPos(const RuleCharacterIterator::Pos& p) { + buf = p.buf; + pos.setIndex(p.pos); + bufPos = p.bufPos; +} + +void RuleCharacterIterator::skipIgnored(int32_t options) { + if ((options & SKIP_WHITESPACE) != 0) { + for (;;) { + UChar32 a = _current(); + if (!PatternProps::isWhiteSpace(a)) break; + _advance(U16_LENGTH(a)); + } + } +} + +UnicodeString& RuleCharacterIterator::lookahead(UnicodeString& result, int32_t maxLookAhead) const { + if (maxLookAhead < 0) { + maxLookAhead = 0x7FFFFFFF; + } + if (buf != 0) { + buf->extract(bufPos, maxLookAhead, result); + } else { + text.extract(pos.getIndex(), maxLookAhead, result); + } + return result; +} + +void RuleCharacterIterator::jumpahead(int32_t count) { + _advance(count); +} + +/* +UnicodeString& RuleCharacterIterator::toString(UnicodeString& result) const { + int32_t b = pos.getIndex(); + text.extract(0, b, result); + return result.append((char16_t) 0x7C).append(text, b, 0x7FFFFFFF); // Insert '|' at index +} +*/ + +UChar32 RuleCharacterIterator::_current() const { + if (buf != 0) { + return buf->char32At(bufPos); + } else { + int i = pos.getIndex(); + return (i < text.length()) ? text.char32At(i) : (UChar32)DONE; + } +} + +void RuleCharacterIterator::_advance(int32_t count) { + if (buf != 0) { + bufPos += count; + if (bufPos == buf->length()) { + buf = 0; + } + } else { + pos.setIndex(pos.getIndex() + count); + if (pos.getIndex() > text.length()) { + pos.setIndex(text.length()); + } + } +} + +U_NAMESPACE_END + +//eof diff --git a/deps/icu-small/source/common/ruleiter.h b/deps/icu-small/source/common/ruleiter.h index 41731407da25d7..e1b8b6ff9813cc 100644 --- a/deps/icu-small/source/common/ruleiter.h +++ b/deps/icu-small/source/common/ruleiter.h @@ -1,233 +1,233 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2003-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: September 24 2003 -* Since: ICU 2.8 -********************************************************************** -*/ -#ifndef _RULEITER_H_ -#define _RULEITER_H_ - -#include "unicode/uobject.h" - -U_NAMESPACE_BEGIN - -class UnicodeString; -class ParsePosition; -class SymbolTable; - -/** - * An iterator that returns 32-bit code points. This class is deliberately - * not related to any of the ICU character iterator classes - * in order to minimize complexity. - * @author Alan Liu - * @since ICU 2.8 - */ -class RuleCharacterIterator : public UMemory { - - // TODO: Ideas for later. (Do not implement if not needed, lest the - // code coverage numbers go down due to unused methods.) - // 1. Add a copy constructor, operator==() method. - // 2. Rather than return DONE, throw an exception if the end - // is reached -- this is an alternate usage model, probably not useful. - -private: - /** - * Text being iterated. - */ - const UnicodeString& text; - - /** - * Position of iterator. - */ - ParsePosition& pos; - - /** - * Symbol table used to parse and dereference variables. May be 0. - */ - const SymbolTable* sym; - - /** - * Current variable expansion, or 0 if none. - */ - const UnicodeString* buf; - - /** - * Position within buf. Meaningless if buf == 0. - */ - int32_t bufPos; - -public: - /** - * Value returned when there are no more characters to iterate. - */ - static constexpr int32_t DONE = -1; - - /** - * Bitmask option to enable parsing of variable names. If (options & - * PARSE_VARIABLES) != 0, then an embedded variable will be expanded to - * its value. Variables are parsed using the SymbolTable API. - */ - static constexpr int32_t PARSE_VARIABLES = 1; - - /** - * Bitmask option to enable parsing of escape sequences. If (options & - * PARSE_ESCAPES) != 0, then an embedded escape sequence will be expanded - * to its value. Escapes are parsed using Utility.unescapeAt(). - */ - static constexpr int32_t PARSE_ESCAPES = 2; - - /** - * Bitmask option to enable skipping of whitespace. If (options & - * SKIP_WHITESPACE) != 0, then Pattern_White_Space characters will be silently - * skipped, as if they were not present in the input. - */ - static constexpr int32_t SKIP_WHITESPACE = 4; - - /** - * Constructs an iterator over the given text, starting at the given - * position. - * @param text the text to be iterated - * @param sym the symbol table, or null if there is none. If sym is null, - * then variables will not be dereferenced, even if the PARSE_VARIABLES - * option is set. - * @param pos upon input, the index of the next character to return. If a - * variable has been dereferenced, then pos will not increment as - * characters of the variable value are iterated. - */ - RuleCharacterIterator(const UnicodeString& text, const SymbolTable* sym, - ParsePosition& pos); - - /** - * Returns true if this iterator has no more characters to return. - */ - UBool atEnd() const; - - /** - * Returns the next character using the given options, or DONE if there - * are no more characters, and advance the position to the next - * character. - * @param options one or more of the following options, bitwise-OR-ed - * together: PARSE_VARIABLES, PARSE_ESCAPES, SKIP_WHITESPACE. - * @param isEscaped output parameter set to true if the character - * was escaped - * @param ec input-output error code. An error will only be set by - * this routing if options includes PARSE_VARIABLES and an unknown - * variable name is seen, or if options includes PARSE_ESCAPES and - * an invalid escape sequence is seen. - * @return the current 32-bit code point, or DONE - */ - UChar32 next(int32_t options, UBool& isEscaped, UErrorCode& ec); - - /** - * Returns true if this iterator is currently within a variable expansion. - */ - inline UBool inVariable() const; - - /** - * An opaque object representing the position of a RuleCharacterIterator. - */ - struct Pos : public UMemory { - private: - const UnicodeString* buf; - int32_t pos; - int32_t bufPos; - friend class RuleCharacterIterator; - }; - - /** - * Sets an object which, when later passed to setPos(), will - * restore this iterator's position. Usage idiom: - * - * RuleCharacterIterator iterator = ...; - * RuleCharacterIterator::Pos pos; - * iterator.getPos(pos); - * for (;;) { - * iterator.getPos(pos); - * int c = iterator.next(...); - * ... - * } - * iterator.setPos(pos); - * - * @param p a position object to be set to this iterator's - * current position. - */ - void getPos(Pos& p) const; - - /** - * Restores this iterator to the position it had when getPos() - * set the given object. - * @param p a position object previously set by getPos() - */ - void setPos(const Pos& p); - - /** - * Skips ahead past any ignored characters, as indicated by the given - * options. This is useful in conjunction with the lookahead() method. - * - * Currently, this only has an effect for SKIP_WHITESPACE. - * @param options one or more of the following options, bitwise-OR-ed - * together: PARSE_VARIABLES, PARSE_ESCAPES, SKIP_WHITESPACE. - */ - void skipIgnored(int32_t options); - - /** - * Returns a string containing the remainder of the characters to be - * returned by this iterator, without any option processing. If the - * iterator is currently within a variable expansion, this will only - * extend to the end of the variable expansion. This method is provided - * so that iterators may interoperate with string-based APIs. The typical - * sequence of calls is to call skipIgnored(), then call lookahead(), then - * parse the string returned by lookahead(), then call jumpahead() to - * resynchronize the iterator. - * @param result a string to receive the characters to be returned - * by future calls to next() - * @param maxLookAhead The maximum to copy into the result. - * @return a reference to result - */ - UnicodeString& lookahead(UnicodeString& result, int32_t maxLookAhead = -1) const; - - /** - * Advances the position by the given number of 16-bit code units. - * This is useful in conjunction with the lookahead() method. - * @param count the number of 16-bit code units to jump over - */ - void jumpahead(int32_t count); - - /** - * Returns a string representation of this object, consisting of the - * characters being iterated, with a '|' marking the current position. - * Position within an expanded variable is not indicated. - * @param result output parameter to receive a string - * representation of this object - */ -// UnicodeString& toString(UnicodeString& result) const; - -private: - /** - * Returns the current 32-bit code point without parsing escapes, parsing - * variables, or skipping whitespace. - * @return the current 32-bit code point - */ - UChar32 _current() const; - - /** - * Advances the position by the given amount. - * @param count the number of 16-bit code units to advance past - */ - void _advance(int32_t count); -}; - -inline UBool RuleCharacterIterator::inVariable() const { - return buf != 0; -} - -U_NAMESPACE_END - -#endif // _RULEITER_H_ -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2003-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: September 24 2003 +* Since: ICU 2.8 +********************************************************************** +*/ +#ifndef _RULEITER_H_ +#define _RULEITER_H_ + +#include "unicode/uobject.h" + +U_NAMESPACE_BEGIN + +class UnicodeString; +class ParsePosition; +class SymbolTable; + +/** + * An iterator that returns 32-bit code points. This class is deliberately + * not related to any of the ICU character iterator classes + * in order to minimize complexity. + * @author Alan Liu + * @since ICU 2.8 + */ +class RuleCharacterIterator : public UMemory { + + // TODO: Ideas for later. (Do not implement if not needed, lest the + // code coverage numbers go down due to unused methods.) + // 1. Add a copy constructor, operator==() method. + // 2. Rather than return DONE, throw an exception if the end + // is reached -- this is an alternate usage model, probably not useful. + +private: + /** + * Text being iterated. + */ + const UnicodeString& text; + + /** + * Position of iterator. + */ + ParsePosition& pos; + + /** + * Symbol table used to parse and dereference variables. May be 0. + */ + const SymbolTable* sym; + + /** + * Current variable expansion, or 0 if none. + */ + const UnicodeString* buf; + + /** + * Position within buf. Meaningless if buf == 0. + */ + int32_t bufPos; + +public: + /** + * Value returned when there are no more characters to iterate. + */ + static constexpr int32_t DONE = -1; + + /** + * Bitmask option to enable parsing of variable names. If (options & + * PARSE_VARIABLES) != 0, then an embedded variable will be expanded to + * its value. Variables are parsed using the SymbolTable API. + */ + static constexpr int32_t PARSE_VARIABLES = 1; + + /** + * Bitmask option to enable parsing of escape sequences. If (options & + * PARSE_ESCAPES) != 0, then an embedded escape sequence will be expanded + * to its value. Escapes are parsed using Utility.unescapeAt(). + */ + static constexpr int32_t PARSE_ESCAPES = 2; + + /** + * Bitmask option to enable skipping of whitespace. If (options & + * SKIP_WHITESPACE) != 0, then Pattern_White_Space characters will be silently + * skipped, as if they were not present in the input. + */ + static constexpr int32_t SKIP_WHITESPACE = 4; + + /** + * Constructs an iterator over the given text, starting at the given + * position. + * @param text the text to be iterated + * @param sym the symbol table, or null if there is none. If sym is null, + * then variables will not be dereferenced, even if the PARSE_VARIABLES + * option is set. + * @param pos upon input, the index of the next character to return. If a + * variable has been dereferenced, then pos will not increment as + * characters of the variable value are iterated. + */ + RuleCharacterIterator(const UnicodeString& text, const SymbolTable* sym, + ParsePosition& pos); + + /** + * Returns true if this iterator has no more characters to return. + */ + UBool atEnd() const; + + /** + * Returns the next character using the given options, or DONE if there + * are no more characters, and advance the position to the next + * character. + * @param options one or more of the following options, bitwise-OR-ed + * together: PARSE_VARIABLES, PARSE_ESCAPES, SKIP_WHITESPACE. + * @param isEscaped output parameter set to true if the character + * was escaped + * @param ec input-output error code. An error will only be set by + * this routing if options includes PARSE_VARIABLES and an unknown + * variable name is seen, or if options includes PARSE_ESCAPES and + * an invalid escape sequence is seen. + * @return the current 32-bit code point, or DONE + */ + UChar32 next(int32_t options, UBool& isEscaped, UErrorCode& ec); + + /** + * Returns true if this iterator is currently within a variable expansion. + */ + inline UBool inVariable() const; + + /** + * An opaque object representing the position of a RuleCharacterIterator. + */ + struct Pos : public UMemory { + private: + const UnicodeString* buf; + int32_t pos; + int32_t bufPos; + friend class RuleCharacterIterator; + }; + + /** + * Sets an object which, when later passed to setPos(), will + * restore this iterator's position. Usage idiom: + * + * RuleCharacterIterator iterator = ...; + * RuleCharacterIterator::Pos pos; + * iterator.getPos(pos); + * for (;;) { + * iterator.getPos(pos); + * int c = iterator.next(...); + * ... + * } + * iterator.setPos(pos); + * + * @param p a position object to be set to this iterator's + * current position. + */ + void getPos(Pos& p) const; + + /** + * Restores this iterator to the position it had when getPos() + * set the given object. + * @param p a position object previously set by getPos() + */ + void setPos(const Pos& p); + + /** + * Skips ahead past any ignored characters, as indicated by the given + * options. This is useful in conjunction with the lookahead() method. + * + * Currently, this only has an effect for SKIP_WHITESPACE. + * @param options one or more of the following options, bitwise-OR-ed + * together: PARSE_VARIABLES, PARSE_ESCAPES, SKIP_WHITESPACE. + */ + void skipIgnored(int32_t options); + + /** + * Returns a string containing the remainder of the characters to be + * returned by this iterator, without any option processing. If the + * iterator is currently within a variable expansion, this will only + * extend to the end of the variable expansion. This method is provided + * so that iterators may interoperate with string-based APIs. The typical + * sequence of calls is to call skipIgnored(), then call lookahead(), then + * parse the string returned by lookahead(), then call jumpahead() to + * resynchronize the iterator. + * @param result a string to receive the characters to be returned + * by future calls to next() + * @param maxLookAhead The maximum to copy into the result. + * @return a reference to result + */ + UnicodeString& lookahead(UnicodeString& result, int32_t maxLookAhead = -1) const; + + /** + * Advances the position by the given number of 16-bit code units. + * This is useful in conjunction with the lookahead() method. + * @param count the number of 16-bit code units to jump over + */ + void jumpahead(int32_t count); + + /** + * Returns a string representation of this object, consisting of the + * characters being iterated, with a '|' marking the current position. + * Position within an expanded variable is not indicated. + * @param result output parameter to receive a string + * representation of this object + */ +// UnicodeString& toString(UnicodeString& result) const; + +private: + /** + * Returns the current 32-bit code point without parsing escapes, parsing + * variables, or skipping whitespace. + * @return the current 32-bit code point + */ + UChar32 _current() const; + + /** + * Advances the position by the given amount. + * @param count the number of 16-bit code units to advance past + */ + void _advance(int32_t count); +}; + +inline UBool RuleCharacterIterator::inVariable() const { + return buf != 0; +} + +U_NAMESPACE_END + +#endif // _RULEITER_H_ +//eof diff --git a/deps/icu-small/source/common/schriter.cpp b/deps/icu-small/source/common/schriter.cpp index 83b3db4ab0a945..966b7ed0078735 100644 --- a/deps/icu-small/source/common/schriter.cpp +++ b/deps/icu-small/source/common/schriter.cpp @@ -1,119 +1,119 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 1998-2012, International Business Machines Corporation and -* others. All Rights Reserved. -****************************************************************************** -* -* File schriter.cpp -* -* Modification History: -* -* Date Name Description -* 05/05/99 stephen Cleaned up. -****************************************************************************** -*/ - -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/chariter.h" -#include "unicode/schriter.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringCharacterIterator) - -StringCharacterIterator::StringCharacterIterator() - : UCharCharacterIterator(), - text() -{ - // NEVER DEFAULT CONSTRUCT! -} - -StringCharacterIterator::StringCharacterIterator(const UnicodeString& textStr) - : UCharCharacterIterator(textStr.getBuffer(), textStr.length()), - text(textStr) -{ - // we had set the input parameter's array, now we need to set our copy's array - UCharCharacterIterator::text = this->text.getBuffer(); -} - -StringCharacterIterator::StringCharacterIterator(const UnicodeString& textStr, - int32_t textPos) - : UCharCharacterIterator(textStr.getBuffer(), textStr.length(), textPos), - text(textStr) -{ - // we had set the input parameter's array, now we need to set our copy's array - UCharCharacterIterator::text = this->text.getBuffer(); -} - -StringCharacterIterator::StringCharacterIterator(const UnicodeString& textStr, - int32_t textBegin, - int32_t textEnd, - int32_t textPos) - : UCharCharacterIterator(textStr.getBuffer(), textStr.length(), textBegin, textEnd, textPos), - text(textStr) -{ - // we had set the input parameter's array, now we need to set our copy's array - UCharCharacterIterator::text = this->text.getBuffer(); -} - -StringCharacterIterator::StringCharacterIterator(const StringCharacterIterator& that) - : UCharCharacterIterator(that), - text(that.text) -{ - // we had set the input parameter's array, now we need to set our copy's array - UCharCharacterIterator::text = this->text.getBuffer(); -} - -StringCharacterIterator::~StringCharacterIterator() { -} - -StringCharacterIterator& -StringCharacterIterator::operator=(const StringCharacterIterator& that) { - UCharCharacterIterator::operator=(that); - text = that.text; - // we had set the input parameter's array, now we need to set our copy's array - UCharCharacterIterator::text = this->text.getBuffer(); - return *this; -} - -bool -StringCharacterIterator::operator==(const ForwardCharacterIterator& that) const { - if (this == &that) { - return true; - } - - // do not call UCharCharacterIterator::operator==() - // because that checks for array pointer equality - // while we compare UnicodeString objects - - if (typeid(*this) != typeid(that)) { - return false; - } - - StringCharacterIterator& realThat = (StringCharacterIterator&)that; - - return text == realThat.text - && pos == realThat.pos - && begin == realThat.begin - && end == realThat.end; -} - -StringCharacterIterator* -StringCharacterIterator::clone() const { - return new StringCharacterIterator(*this); -} - -void -StringCharacterIterator::setText(const UnicodeString& newText) { - text = newText; - UCharCharacterIterator::setText(text.getBuffer(), text.length()); -} - -void -StringCharacterIterator::getText(UnicodeString& result) { - result = text; -} -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 1998-2012, International Business Machines Corporation and +* others. All Rights Reserved. +****************************************************************************** +* +* File schriter.cpp +* +* Modification History: +* +* Date Name Description +* 05/05/99 stephen Cleaned up. +****************************************************************************** +*/ + +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/chariter.h" +#include "unicode/schriter.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringCharacterIterator) + +StringCharacterIterator::StringCharacterIterator() + : UCharCharacterIterator(), + text() +{ + // NEVER DEFAULT CONSTRUCT! +} + +StringCharacterIterator::StringCharacterIterator(const UnicodeString& textStr) + : UCharCharacterIterator(textStr.getBuffer(), textStr.length()), + text(textStr) +{ + // we had set the input parameter's array, now we need to set our copy's array + UCharCharacterIterator::text = this->text.getBuffer(); +} + +StringCharacterIterator::StringCharacterIterator(const UnicodeString& textStr, + int32_t textPos) + : UCharCharacterIterator(textStr.getBuffer(), textStr.length(), textPos), + text(textStr) +{ + // we had set the input parameter's array, now we need to set our copy's array + UCharCharacterIterator::text = this->text.getBuffer(); +} + +StringCharacterIterator::StringCharacterIterator(const UnicodeString& textStr, + int32_t textBegin, + int32_t textEnd, + int32_t textPos) + : UCharCharacterIterator(textStr.getBuffer(), textStr.length(), textBegin, textEnd, textPos), + text(textStr) +{ + // we had set the input parameter's array, now we need to set our copy's array + UCharCharacterIterator::text = this->text.getBuffer(); +} + +StringCharacterIterator::StringCharacterIterator(const StringCharacterIterator& that) + : UCharCharacterIterator(that), + text(that.text) +{ + // we had set the input parameter's array, now we need to set our copy's array + UCharCharacterIterator::text = this->text.getBuffer(); +} + +StringCharacterIterator::~StringCharacterIterator() { +} + +StringCharacterIterator& +StringCharacterIterator::operator=(const StringCharacterIterator& that) { + UCharCharacterIterator::operator=(that); + text = that.text; + // we had set the input parameter's array, now we need to set our copy's array + UCharCharacterIterator::text = this->text.getBuffer(); + return *this; +} + +bool +StringCharacterIterator::operator==(const ForwardCharacterIterator& that) const { + if (this == &that) { + return true; + } + + // do not call UCharCharacterIterator::operator==() + // because that checks for array pointer equality + // while we compare UnicodeString objects + + if (typeid(*this) != typeid(that)) { + return false; + } + + const StringCharacterIterator& realThat = static_cast(that); + + return text == realThat.text + && pos == realThat.pos + && begin == realThat.begin + && end == realThat.end; +} + +StringCharacterIterator* +StringCharacterIterator::clone() const { + return new StringCharacterIterator(*this); +} + +void +StringCharacterIterator::setText(const UnicodeString& newText) { + text = newText; + UCharCharacterIterator::setText(text.getBuffer(), text.length()); +} + +void +StringCharacterIterator::getText(UnicodeString& result) { + result = text; +} +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/serv.cpp b/deps/icu-small/source/common/serv.cpp index 9d8c04149ce159..daf06b026f7704 100644 --- a/deps/icu-small/source/common/serv.cpp +++ b/deps/icu-small/source/common/serv.cpp @@ -1,968 +1,968 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** -******************************************************************************* -* Copyright (C) 2001-2014, International Business Machines Corporation. -* All Rights Reserved. -******************************************************************************* -*/ - -#include "unicode/utypes.h" -#include "unicode/localpointer.h" - -#if !UCONFIG_NO_SERVICE - -#include "serv.h" -#include "umutex.h" - -#undef SERVICE_REFCOUNT - -// in case we use the refcount stuff - -U_NAMESPACE_BEGIN - -/* -****************************************************************** -*/ - -const UChar ICUServiceKey::PREFIX_DELIMITER = 0x002F; /* '/' */ - -ICUServiceKey::ICUServiceKey(const UnicodeString& id) -: _id(id) { -} - -ICUServiceKey::~ICUServiceKey() -{ -} - -const UnicodeString& -ICUServiceKey::getID() const -{ - return _id; -} - -UnicodeString& -ICUServiceKey::canonicalID(UnicodeString& result) const -{ - return result.append(_id); -} - -UnicodeString& -ICUServiceKey::currentID(UnicodeString& result) const -{ - return canonicalID(result); -} - -UnicodeString& -ICUServiceKey::currentDescriptor(UnicodeString& result) const -{ - prefix(result); - result.append(PREFIX_DELIMITER); - return currentID(result); -} - -UBool -ICUServiceKey::fallback() -{ - return false; -} - -UBool -ICUServiceKey::isFallbackOf(const UnicodeString& id) const -{ - return id == _id; -} - -UnicodeString& -ICUServiceKey::prefix(UnicodeString& result) const -{ - return result; -} - -UnicodeString& -ICUServiceKey::parsePrefix(UnicodeString& result) -{ - int32_t n = result.indexOf(PREFIX_DELIMITER); - if (n < 0) { - n = 0; - } - result.remove(n); - return result; -} - -UnicodeString& -ICUServiceKey::parseSuffix(UnicodeString& result) -{ - int32_t n = result.indexOf(PREFIX_DELIMITER); - if (n >= 0) { - result.remove(0, n+1); - } - return result; -} - -#ifdef SERVICE_DEBUG -UnicodeString& -ICUServiceKey::debug(UnicodeString& result) const -{ - debugClass(result); - result.append((UnicodeString)" id: "); - result.append(_id); - return result; -} - -UnicodeString& -ICUServiceKey::debugClass(UnicodeString& result) const -{ - return result.append((UnicodeString)"ICUServiceKey"); -} -#endif - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey) - -/* -****************************************************************** -*/ - -ICUServiceFactory::~ICUServiceFactory() {} - -SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible) -: _instance(instanceToAdopt), _id(id), _visible(visible) -{ -} - -SimpleFactory::~SimpleFactory() -{ - delete _instance; -} - -UObject* -SimpleFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const -{ - if (U_SUCCESS(status)) { - UnicodeString temp; - if (_id == key.currentID(temp)) { - return service->cloneInstance(_instance); - } - } - return NULL; -} - -void -SimpleFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const -{ - if (_visible) { - result.put(_id, (void*)this, status); // cast away const - } else { - result.remove(_id); - } -} - -UnicodeString& -SimpleFactory::getDisplayName(const UnicodeString& id, const Locale& /* locale */, UnicodeString& result) const -{ - if (_visible && _id == id) { - result = _id; - } else { - result.setToBogus(); - } - return result; -} - -#ifdef SERVICE_DEBUG -UnicodeString& -SimpleFactory::debug(UnicodeString& toAppendTo) const -{ - debugClass(toAppendTo); - toAppendTo.append((UnicodeString)" id: "); - toAppendTo.append(_id); - toAppendTo.append((UnicodeString)", visible: "); - toAppendTo.append(_visible ? (UnicodeString)"T" : (UnicodeString)"F"); - return toAppendTo; -} - -UnicodeString& -SimpleFactory::debugClass(UnicodeString& toAppendTo) const -{ - return toAppendTo.append((UnicodeString)"SimpleFactory"); -} -#endif - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory) - -/* -****************************************************************** -*/ - -ServiceListener::~ServiceListener() {} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceListener) - -/* -****************************************************************** -*/ - -// Record the actual id for this service in the cache, so we can return it -// even if we succeed later with a different id. -class CacheEntry : public UMemory { -private: - int32_t refcount; - -public: - UnicodeString actualDescriptor; - UObject* service; - - /** - * Releases a reference to the shared resource. - */ - ~CacheEntry() { - delete service; - } - - CacheEntry(const UnicodeString& _actualDescriptor, UObject* _service) - : refcount(1), actualDescriptor(_actualDescriptor), service(_service) { - } - - /** - * Instantiation creates an initial reference, so don't call this - * unless you're creating a new pointer to this. Management of - * that pointer will have to know how to deal with refcounts. - * Return true if the resource has not already been released. - */ - CacheEntry* ref() { - ++refcount; - return this; - } - - /** - * Destructions removes a reference, so don't call this unless - * you're removing pointer to this somewhere. Management of that - * pointer will have to know how to deal with refcounts. Once - * the refcount drops to zero, the resource is released. Return - * false if the resource has been released. - */ - CacheEntry* unref() { - if ((--refcount) == 0) { - delete this; - return NULL; - } - return this; - } - - /** - * Return true if there is at least one reference to this and the - * resource has not been released. - */ - UBool isShared() const { - return refcount > 1; - } -}; - -// Deleter for serviceCache -U_CDECL_BEGIN -static void U_CALLCONV -cacheDeleter(void* obj) { - U_NAMESPACE_USE ((CacheEntry*)obj)->unref(); -} - -U_CDECL_END - -/* -****************************************************************** -*/ - -class DNCache : public UMemory { -public: - Hashtable cache; - const Locale locale; - - DNCache(const Locale& _locale) - : cache(), locale(_locale) - { - // cache.setKeyDeleter(uprv_deleteUObject); - } -}; - - -/* -****************************************************************** -*/ - -StringPair* -StringPair::create(const UnicodeString& displayName, - const UnicodeString& id, - UErrorCode& status) -{ - if (U_SUCCESS(status)) { - StringPair* sp = new StringPair(displayName, id); - if (sp == NULL || sp->isBogus()) { - status = U_MEMORY_ALLOCATION_ERROR; - delete sp; - return NULL; - } - return sp; - } - return NULL; -} - -UBool -StringPair::isBogus() const { - return displayName.isBogus() || id.isBogus(); -} - -StringPair::StringPair(const UnicodeString& _displayName, - const UnicodeString& _id) -: displayName(_displayName) -, id(_id) -{ -} - -U_CDECL_BEGIN -static void U_CALLCONV -userv_deleteStringPair(void *obj) { - U_NAMESPACE_USE delete (StringPair*) obj; -} -U_CDECL_END - -/* -****************************************************************** -*/ - -static UMutex lock; - -ICUService::ICUService() -: name() -, timestamp(0) -, factories(NULL) -, serviceCache(NULL) -, idCache(NULL) -, dnCache(NULL) -{ -} - -ICUService::ICUService(const UnicodeString& newName) -: name(newName) -, timestamp(0) -, factories(NULL) -, serviceCache(NULL) -, idCache(NULL) -, dnCache(NULL) -{ -} - -ICUService::~ICUService() -{ - { - Mutex mutex(&lock); - clearCaches(); - delete factories; - factories = NULL; - } -} - -UObject* -ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const -{ - return get(descriptor, NULL, status); -} - -UObject* -ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const -{ - UObject* result = NULL; - ICUServiceKey* key = createKey(&descriptor, status); - if (key) { - result = getKey(*key, actualReturn, status); - delete key; - } - return result; -} - -UObject* -ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const -{ - return getKey(key, NULL, status); -} - -// this is a vector that subclasses of ICUService can override to further customize the result object -// before returning it. All other public get functions should call this one. - -UObject* -ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const -{ - return getKey(key, actualReturn, NULL, status); -} - -// make it possible to call reentrantly on systems that don't have reentrant mutexes. -// we can use this simple approach since we know the situation where we're calling -// reentrantly even without knowing the thread. -class XMutex : public UMemory { -public: - inline XMutex(UMutex *mutex, UBool reentering) - : fMutex(mutex) - , fActive(!reentering) - { - if (fActive) umtx_lock(fMutex); - } - inline ~XMutex() { - if (fActive) umtx_unlock(fMutex); - } - -private: - UMutex *fMutex; - UBool fActive; -}; - -// called only by factories, treat as private -UObject* -ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const -{ - if (U_FAILURE(status)) { - return NULL; - } - - if (isDefault()) { - return handleDefault(key, actualReturn, status); - } - - ICUService* ncthis = (ICUService*)this; // cast away semantic const - - CacheEntry* result = NULL; - { - // The factory list can't be modified until we're done, - // otherwise we might update the cache with an invalid result. - // The cache has to stay in synch with the factory list. - // ICU doesn't have monitors so we can't use rw locks, so - // we single-thread everything using this service, for now. - - // if factory is not null, we're calling from within the mutex, - // and since some unix machines don't have reentrant mutexes we - // need to make sure not to try to lock it again. - XMutex mutex(&lock, factory != NULL); - - if (serviceCache == NULL) { - ncthis->serviceCache = new Hashtable(status); - if (ncthis->serviceCache == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - if (U_FAILURE(status)) { - delete serviceCache; - return NULL; - } - serviceCache->setValueDeleter(cacheDeleter); - } - - UnicodeString currentDescriptor; - LocalPointer cacheDescriptorList; - UBool putInCache = false; - - int32_t startIndex = 0; - int32_t limit = factories->size(); - UBool cacheResult = true; - - if (factory != NULL) { - for (int32_t i = 0; i < limit; ++i) { - if (factory == (const ICUServiceFactory*)factories->elementAt(i)) { - startIndex = i + 1; - break; - } - } - if (startIndex == 0) { - // throw new InternalError("Factory " + factory + "not registered with service: " + this); - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - cacheResult = false; - } - - do { - currentDescriptor.remove(); - key.currentDescriptor(currentDescriptor); - result = (CacheEntry*)serviceCache->get(currentDescriptor); - if (result != NULL) { - break; - } - - // first test of cache failed, so we'll have to update - // the cache if we eventually succeed-- that is, if we're - // going to update the cache at all. - putInCache = true; - - int32_t index = startIndex; - while (index < limit) { - ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(index++); - LocalPointer service(f->create(key, this, status)); - if (U_FAILURE(status)) { - return NULL; - } - if (service.isValid()) { - result = new CacheEntry(currentDescriptor, service.getAlias()); - if (result == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - service.orphan(); // result now owns service. - - goto outerEnd; - } - } - - // prepare to load the cache with all additional ids that - // will resolve to result, assuming we'll succeed. We - // don't want to keep querying on an id that's going to - // fallback to the one that succeeded, we want to hit the - // cache the first time next goaround. - if (cacheDescriptorList.isNull()) { - cacheDescriptorList.adoptInsteadAndCheckErrorCode(new UVector(uprv_deleteUObject, NULL, 5, status), status); - if (U_FAILURE(status)) { - return NULL; - } - } - - LocalPointer idToCache(new UnicodeString(currentDescriptor), status); - if (U_FAILURE(status)) { - return NULL; - } - if (idToCache->isBogus()) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - cacheDescriptorList->adoptElement(idToCache.orphan(), status); - if (U_FAILURE(status)) { - return NULL; - } - } while (key.fallback()); -outerEnd: - - if (result != NULL) { - if (putInCache && cacheResult) { - serviceCache->put(result->actualDescriptor, result, status); - if (U_FAILURE(status)) { - return NULL; - } - - if (cacheDescriptorList.isValid()) { - for (int32_t i = cacheDescriptorList->size(); --i >= 0;) { - UnicodeString* desc = (UnicodeString*)cacheDescriptorList->elementAt(i); - - serviceCache->put(*desc, result, status); - if (U_FAILURE(status)) { - return NULL; - } - - result->ref(); - cacheDescriptorList->removeElementAt(i); - } - } - } - - if (actualReturn != NULL) { - // strip null prefix - if (result->actualDescriptor.indexOf((UChar)0x2f) == 0) { // U+002f=slash (/) - actualReturn->remove(); - actualReturn->append(result->actualDescriptor, - 1, - result->actualDescriptor.length() - 1); - } else { - *actualReturn = result->actualDescriptor; - } - - if (actualReturn->isBogus()) { - status = U_MEMORY_ALLOCATION_ERROR; - delete result; - return NULL; - } - } - - UObject* service = cloneInstance(result->service); - if (putInCache && !cacheResult) { - delete result; - } - return service; - } - } - - return handleDefault(key, actualReturn, status); -} - -UObject* -ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const -{ - return NULL; -} - -UVector& -ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const { - return getVisibleIDs(result, NULL, status); -} - -UVector& -ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const -{ - result.removeAllElements(); - - if (U_FAILURE(status)) { - return result; - } - UObjectDeleter *savedDeleter = result.setDeleter(uprv_deleteUObject); - - { - Mutex mutex(&lock); - const Hashtable* map = getVisibleIDMap(status); - if (map != NULL) { - ICUServiceKey* fallbackKey = createKey(matchID, status); - - for (int32_t pos = UHASH_FIRST; U_SUCCESS(status); ) { - const UHashElement* e = map->nextElement(pos); - if (e == NULL) { - break; - } - - const UnicodeString* id = (const UnicodeString*)e->key.pointer; - if (fallbackKey != NULL) { - if (!fallbackKey->isFallbackOf(*id)) { - continue; - } - } - - LocalPointer idClone(id->clone(), status); - result.adoptElement(idClone.orphan(), status); - } - delete fallbackKey; - } - } - if (U_FAILURE(status)) { - result.removeAllElements(); - } - result.setDeleter(savedDeleter); - return result; -} - -const Hashtable* -ICUService::getVisibleIDMap(UErrorCode& status) const { - if (U_FAILURE(status)) return NULL; - - // must only be called when lock is already held - - ICUService* ncthis = (ICUService*)this; // cast away semantic const - if (idCache == NULL) { - ncthis->idCache = new Hashtable(status); - if (idCache == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } else if (factories != NULL) { - for (int32_t pos = factories->size(); --pos >= 0;) { - ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(pos); - f->updateVisibleIDs(*idCache, status); - } - if (U_FAILURE(status)) { - delete idCache; - ncthis->idCache = NULL; - } - } - } - - return idCache; -} - - -UnicodeString& -ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const -{ - return getDisplayName(id, result, Locale::getDefault()); -} - -UnicodeString& -ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const -{ - { - UErrorCode status = U_ZERO_ERROR; - Mutex mutex(&lock); - const Hashtable* map = getVisibleIDMap(status); - if (map != NULL) { - ICUServiceFactory* f = (ICUServiceFactory*)map->get(id); - if (f != NULL) { - f->getDisplayName(id, locale, result); - return result; - } - - // fallback - status = U_ZERO_ERROR; - ICUServiceKey* fallbackKey = createKey(&id, status); - while (fallbackKey != NULL && fallbackKey->fallback()) { - UnicodeString us; - fallbackKey->currentID(us); - f = (ICUServiceFactory*)map->get(us); - if (f != NULL) { - f->getDisplayName(id, locale, result); - delete fallbackKey; - return result; - } - } - delete fallbackKey; - } - } - result.setToBogus(); - return result; -} - -UVector& -ICUService::getDisplayNames(UVector& result, UErrorCode& status) const -{ - return getDisplayNames(result, Locale::getDefault(), NULL, status); -} - - -UVector& -ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const -{ - return getDisplayNames(result, locale, NULL, status); -} - -UVector& -ICUService::getDisplayNames(UVector& result, - const Locale& locale, - const UnicodeString* matchID, - UErrorCode& status) const -{ - result.removeAllElements(); - result.setDeleter(userv_deleteStringPair); - if (U_SUCCESS(status)) { - ICUService* ncthis = (ICUService*)this; // cast away semantic const - Mutex mutex(&lock); - - if (dnCache != NULL && dnCache->locale != locale) { - delete dnCache; - ncthis->dnCache = NULL; - } - - if (dnCache == NULL) { - const Hashtable* m = getVisibleIDMap(status); - if (U_FAILURE(status)) { - return result; - } - ncthis->dnCache = new DNCache(locale); - if (dnCache == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return result; - } - - int32_t pos = UHASH_FIRST; - const UHashElement* entry = NULL; - while ((entry = m->nextElement(pos)) != NULL) { - const UnicodeString* id = (const UnicodeString*)entry->key.pointer; - ICUServiceFactory* f = (ICUServiceFactory*)entry->value.pointer; - UnicodeString dname; - f->getDisplayName(*id, locale, dname); - if (dname.isBogus()) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap - if (U_SUCCESS(status)) { - continue; - } - } - delete dnCache; - ncthis->dnCache = NULL; - return result; - } - } - } - - ICUServiceKey* matchKey = createKey(matchID, status); - /* To ensure that all elements in the hashtable are iterated, set pos to -1. - * nextElement(pos) will skip the position at pos and begin the iteration - * at the next position, which in this case will be 0. - */ - int32_t pos = UHASH_FIRST; - const UHashElement *entry = NULL; - while ((entry = dnCache->cache.nextElement(pos)) != NULL) { - const UnicodeString* id = (const UnicodeString*)entry->value.pointer; - if (matchKey != NULL && !matchKey->isFallbackOf(*id)) { - continue; - } - const UnicodeString* dn = (const UnicodeString*)entry->key.pointer; - StringPair* sp = StringPair::create(*id, *dn, status); - result.adoptElement(sp, status); - if (U_FAILURE(status)) { - result.removeAllElements(); - break; - } - } - delete matchKey; - - return result; -} - -URegistryKey -ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status) -{ - return registerInstance(objToAdopt, id, true, status); -} - -URegistryKey -ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) -{ - ICUServiceKey* key = createKey(&id, status); - if (key != NULL) { - UnicodeString canonicalID; - key->canonicalID(canonicalID); - delete key; - - ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status); - if (f != NULL) { - return registerFactory(f, status); - } - } - delete objToAdopt; - return NULL; -} - -ICUServiceFactory* -ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) -{ - if (U_SUCCESS(status)) { - if ((objToAdopt != NULL) && (!id.isBogus())) { - return new SimpleFactory(objToAdopt, id, visible); - } - status = U_ILLEGAL_ARGUMENT_ERROR; - } - return NULL; -} - -URegistryKey -ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status) -{ - LocalPointerlpFactoryToAdopt(factoryToAdopt); - if (U_FAILURE(status) || factoryToAdopt == nullptr) { - return nullptr; - } - { - Mutex mutex(&lock); - - if (factories == nullptr) { - LocalPointer lpFactories(new UVector(uprv_deleteUObject, nullptr, status), status); - if (U_FAILURE(status)) { - return nullptr; - } - factories = lpFactories.orphan(); - } - factories->insertElementAt(lpFactoryToAdopt.orphan(), 0, status); - if (U_SUCCESS(status)) { - clearCaches(); - } - } // Close of mutex lock block. - - if (U_SUCCESS(status)) { - notifyChanged(); - return (URegistryKey)factoryToAdopt; - } else { - return nullptr; - } -} - -UBool -ICUService::unregister(URegistryKey rkey, UErrorCode& status) -{ - ICUServiceFactory *factory = (ICUServiceFactory*)rkey; - UBool result = false; - if (factory != NULL && factories != NULL) { - Mutex mutex(&lock); - - if (factories->removeElement(factory)) { - clearCaches(); - result = true; - } else { - status = U_ILLEGAL_ARGUMENT_ERROR; - delete factory; - } - } - if (result) { - notifyChanged(); - } - return result; -} - -void -ICUService::reset() -{ - { - Mutex mutex(&lock); - reInitializeFactories(); - clearCaches(); - } - notifyChanged(); -} - -void -ICUService::reInitializeFactories() -{ - if (factories != NULL) { - factories->removeAllElements(); - } -} - -UBool -ICUService::isDefault() const -{ - return countFactories() == 0; -} - -ICUServiceKey* -ICUService::createKey(const UnicodeString* id, UErrorCode& status) const -{ - return (U_FAILURE(status) || id == NULL) ? NULL : new ICUServiceKey(*id); -} - -void -ICUService::clearCaches() -{ - // callers synchronize before use - ++timestamp; - delete dnCache; - dnCache = NULL; - delete idCache; - idCache = NULL; - delete serviceCache; serviceCache = NULL; -} - -void -ICUService::clearServiceCache() -{ - // callers synchronize before use - delete serviceCache; serviceCache = NULL; -} - -UBool -ICUService::acceptsListener(const EventListener& l) const -{ - return dynamic_cast(&l) != NULL; -} - -void -ICUService::notifyListener(EventListener& l) const -{ - ((ServiceListener&)l).serviceChanged(*this); -} - -UnicodeString& -ICUService::getName(UnicodeString& result) const -{ - return result.append(name); -} - -int32_t -ICUService::countFactories() const -{ - return factories == NULL ? 0 : factories->size(); -} - -int32_t -ICUService::getTimestamp() const -{ - return timestamp; -} - -U_NAMESPACE_END - -/* UCONFIG_NO_SERVICE */ -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** +******************************************************************************* +* Copyright (C) 2001-2014, International Business Machines Corporation. +* All Rights Reserved. +******************************************************************************* +*/ + +#include "unicode/utypes.h" +#include "unicode/localpointer.h" + +#if !UCONFIG_NO_SERVICE + +#include "serv.h" +#include "umutex.h" + +#undef SERVICE_REFCOUNT + +// in case we use the refcount stuff + +U_NAMESPACE_BEGIN + +/* +****************************************************************** +*/ + +const char16_t ICUServiceKey::PREFIX_DELIMITER = 0x002F; /* '/' */ + +ICUServiceKey::ICUServiceKey(const UnicodeString& id) +: _id(id) { +} + +ICUServiceKey::~ICUServiceKey() +{ +} + +const UnicodeString& +ICUServiceKey::getID() const +{ + return _id; +} + +UnicodeString& +ICUServiceKey::canonicalID(UnicodeString& result) const +{ + return result.append(_id); +} + +UnicodeString& +ICUServiceKey::currentID(UnicodeString& result) const +{ + return canonicalID(result); +} + +UnicodeString& +ICUServiceKey::currentDescriptor(UnicodeString& result) const +{ + prefix(result); + result.append(PREFIX_DELIMITER); + return currentID(result); +} + +UBool +ICUServiceKey::fallback() +{ + return false; +} + +UBool +ICUServiceKey::isFallbackOf(const UnicodeString& id) const +{ + return id == _id; +} + +UnicodeString& +ICUServiceKey::prefix(UnicodeString& result) const +{ + return result; +} + +UnicodeString& +ICUServiceKey::parsePrefix(UnicodeString& result) +{ + int32_t n = result.indexOf(PREFIX_DELIMITER); + if (n < 0) { + n = 0; + } + result.remove(n); + return result; +} + +UnicodeString& +ICUServiceKey::parseSuffix(UnicodeString& result) +{ + int32_t n = result.indexOf(PREFIX_DELIMITER); + if (n >= 0) { + result.remove(0, n+1); + } + return result; +} + +#ifdef SERVICE_DEBUG +UnicodeString& +ICUServiceKey::debug(UnicodeString& result) const +{ + debugClass(result); + result.append((UnicodeString)" id: "); + result.append(_id); + return result; +} + +UnicodeString& +ICUServiceKey::debugClass(UnicodeString& result) const +{ + return result.append((UnicodeString)"ICUServiceKey"); +} +#endif + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey) + +/* +****************************************************************** +*/ + +ICUServiceFactory::~ICUServiceFactory() {} + +SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible) +: _instance(instanceToAdopt), _id(id), _visible(visible) +{ +} + +SimpleFactory::~SimpleFactory() +{ + delete _instance; +} + +UObject* +SimpleFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const +{ + if (U_SUCCESS(status)) { + UnicodeString temp; + if (_id == key.currentID(temp)) { + return service->cloneInstance(_instance); + } + } + return nullptr; +} + +void +SimpleFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const +{ + if (_visible) { + result.put(_id, (void*)this, status); // cast away const + } else { + result.remove(_id); + } +} + +UnicodeString& +SimpleFactory::getDisplayName(const UnicodeString& id, const Locale& /* locale */, UnicodeString& result) const +{ + if (_visible && _id == id) { + result = _id; + } else { + result.setToBogus(); + } + return result; +} + +#ifdef SERVICE_DEBUG +UnicodeString& +SimpleFactory::debug(UnicodeString& toAppendTo) const +{ + debugClass(toAppendTo); + toAppendTo.append((UnicodeString)" id: "); + toAppendTo.append(_id); + toAppendTo.append((UnicodeString)", visible: "); + toAppendTo.append(_visible ? (UnicodeString)"T" : (UnicodeString)"F"); + return toAppendTo; +} + +UnicodeString& +SimpleFactory::debugClass(UnicodeString& toAppendTo) const +{ + return toAppendTo.append((UnicodeString)"SimpleFactory"); +} +#endif + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory) + +/* +****************************************************************** +*/ + +ServiceListener::~ServiceListener() {} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceListener) + +/* +****************************************************************** +*/ + +// Record the actual id for this service in the cache, so we can return it +// even if we succeed later with a different id. +class CacheEntry : public UMemory { +private: + int32_t refcount; + +public: + UnicodeString actualDescriptor; + UObject* service; + + /** + * Releases a reference to the shared resource. + */ + ~CacheEntry() { + delete service; + } + + CacheEntry(const UnicodeString& _actualDescriptor, UObject* _service) + : refcount(1), actualDescriptor(_actualDescriptor), service(_service) { + } + + /** + * Instantiation creates an initial reference, so don't call this + * unless you're creating a new pointer to this. Management of + * that pointer will have to know how to deal with refcounts. + * Return true if the resource has not already been released. + */ + CacheEntry* ref() { + ++refcount; + return this; + } + + /** + * Destructions removes a reference, so don't call this unless + * you're removing pointer to this somewhere. Management of that + * pointer will have to know how to deal with refcounts. Once + * the refcount drops to zero, the resource is released. Return + * false if the resource has been released. + */ + CacheEntry* unref() { + if ((--refcount) == 0) { + delete this; + return nullptr; + } + return this; + } + + /** + * Return true if there is at least one reference to this and the + * resource has not been released. + */ + UBool isShared() const { + return refcount > 1; + } +}; + +// Deleter for serviceCache +U_CDECL_BEGIN +static void U_CALLCONV +cacheDeleter(void* obj) { + U_NAMESPACE_USE ((CacheEntry*)obj)->unref(); +} + +U_CDECL_END + +/* +****************************************************************** +*/ + +class DNCache : public UMemory { +public: + Hashtable cache; + const Locale locale; + + DNCache(const Locale& _locale) + : cache(), locale(_locale) + { + // cache.setKeyDeleter(uprv_deleteUObject); + } +}; + + +/* +****************************************************************** +*/ + +StringPair* +StringPair::create(const UnicodeString& displayName, + const UnicodeString& id, + UErrorCode& status) +{ + if (U_SUCCESS(status)) { + StringPair* sp = new StringPair(displayName, id); + if (sp == nullptr || sp->isBogus()) { + status = U_MEMORY_ALLOCATION_ERROR; + delete sp; + return nullptr; + } + return sp; + } + return nullptr; +} + +UBool +StringPair::isBogus() const { + return displayName.isBogus() || id.isBogus(); +} + +StringPair::StringPair(const UnicodeString& _displayName, + const UnicodeString& _id) +: displayName(_displayName) +, id(_id) +{ +} + +U_CDECL_BEGIN +static void U_CALLCONV +userv_deleteStringPair(void *obj) { + U_NAMESPACE_USE delete (StringPair*) obj; +} +U_CDECL_END + +/* +****************************************************************** +*/ + +static UMutex lock; + +ICUService::ICUService() +: name() +, timestamp(0) +, factories(nullptr) +, serviceCache(nullptr) +, idCache(nullptr) +, dnCache(nullptr) +{ +} + +ICUService::ICUService(const UnicodeString& newName) +: name(newName) +, timestamp(0) +, factories(nullptr) +, serviceCache(nullptr) +, idCache(nullptr) +, dnCache(nullptr) +{ +} + +ICUService::~ICUService() +{ + { + Mutex mutex(&lock); + clearCaches(); + delete factories; + factories = nullptr; + } +} + +UObject* +ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const +{ + return get(descriptor, nullptr, status); +} + +UObject* +ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const +{ + UObject* result = nullptr; + ICUServiceKey* key = createKey(&descriptor, status); + if (key) { + result = getKey(*key, actualReturn, status); + delete key; + } + return result; +} + +UObject* +ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const +{ + return getKey(key, nullptr, status); +} + +// this is a vector that subclasses of ICUService can override to further customize the result object +// before returning it. All other public get functions should call this one. + +UObject* +ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const +{ + return getKey(key, actualReturn, nullptr, status); +} + +// make it possible to call reentrantly on systems that don't have reentrant mutexes. +// we can use this simple approach since we know the situation where we're calling +// reentrantly even without knowing the thread. +class XMutex : public UMemory { +public: + inline XMutex(UMutex *mutex, UBool reentering) + : fMutex(mutex) + , fActive(!reentering) + { + if (fActive) umtx_lock(fMutex); + } + inline ~XMutex() { + if (fActive) umtx_unlock(fMutex); + } + +private: + UMutex *fMutex; + UBool fActive; +}; + +// called only by factories, treat as private +UObject* +ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const +{ + if (U_FAILURE(status)) { + return nullptr; + } + + if (isDefault()) { + return handleDefault(key, actualReturn, status); + } + + ICUService* ncthis = (ICUService*)this; // cast away semantic const + + CacheEntry* result = nullptr; + { + // The factory list can't be modified until we're done, + // otherwise we might update the cache with an invalid result. + // The cache has to stay in synch with the factory list. + // ICU doesn't have monitors so we can't use rw locks, so + // we single-thread everything using this service, for now. + + // if factory is not null, we're calling from within the mutex, + // and since some unix machines don't have reentrant mutexes we + // need to make sure not to try to lock it again. + XMutex mutex(&lock, factory != nullptr); + + if (serviceCache == nullptr) { + ncthis->serviceCache = new Hashtable(status); + if (ncthis->serviceCache == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + if (U_FAILURE(status)) { + delete serviceCache; + return nullptr; + } + serviceCache->setValueDeleter(cacheDeleter); + } + + UnicodeString currentDescriptor; + LocalPointer cacheDescriptorList; + UBool putInCache = false; + + int32_t startIndex = 0; + int32_t limit = factories->size(); + UBool cacheResult = true; + + if (factory != nullptr) { + for (int32_t i = 0; i < limit; ++i) { + if (factory == (const ICUServiceFactory*)factories->elementAt(i)) { + startIndex = i + 1; + break; + } + } + if (startIndex == 0) { + // throw new InternalError("Factory " + factory + "not registered with service: " + this); + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + cacheResult = false; + } + + do { + currentDescriptor.remove(); + key.currentDescriptor(currentDescriptor); + result = (CacheEntry*)serviceCache->get(currentDescriptor); + if (result != nullptr) { + break; + } + + // first test of cache failed, so we'll have to update + // the cache if we eventually succeed-- that is, if we're + // going to update the cache at all. + putInCache = true; + + int32_t index = startIndex; + while (index < limit) { + ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(index++); + LocalPointer service(f->create(key, this, status)); + if (U_FAILURE(status)) { + return nullptr; + } + if (service.isValid()) { + result = new CacheEntry(currentDescriptor, service.getAlias()); + if (result == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + service.orphan(); // result now owns service. + + goto outerEnd; + } + } + + // prepare to load the cache with all additional ids that + // will resolve to result, assuming we'll succeed. We + // don't want to keep querying on an id that's going to + // fallback to the one that succeeded, we want to hit the + // cache the first time next goaround. + if (cacheDescriptorList.isNull()) { + cacheDescriptorList.adoptInsteadAndCheckErrorCode(new UVector(uprv_deleteUObject, nullptr, 5, status), status); + if (U_FAILURE(status)) { + return nullptr; + } + } + + LocalPointer idToCache(new UnicodeString(currentDescriptor), status); + if (U_FAILURE(status)) { + return nullptr; + } + if (idToCache->isBogus()) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + cacheDescriptorList->adoptElement(idToCache.orphan(), status); + if (U_FAILURE(status)) { + return nullptr; + } + } while (key.fallback()); +outerEnd: + + if (result != nullptr) { + if (putInCache && cacheResult) { + serviceCache->put(result->actualDescriptor, result, status); + if (U_FAILURE(status)) { + return nullptr; + } + + if (cacheDescriptorList.isValid()) { + for (int32_t i = cacheDescriptorList->size(); --i >= 0;) { + UnicodeString* desc = (UnicodeString*)cacheDescriptorList->elementAt(i); + + serviceCache->put(*desc, result, status); + if (U_FAILURE(status)) { + return nullptr; + } + + result->ref(); + cacheDescriptorList->removeElementAt(i); + } + } + } + + if (actualReturn != nullptr) { + // strip null prefix + if (result->actualDescriptor.indexOf((char16_t)0x2f) == 0) { // U+002f=slash (/) + actualReturn->remove(); + actualReturn->append(result->actualDescriptor, + 1, + result->actualDescriptor.length() - 1); + } else { + *actualReturn = result->actualDescriptor; + } + + if (actualReturn->isBogus()) { + status = U_MEMORY_ALLOCATION_ERROR; + delete result; + return nullptr; + } + } + + UObject* service = cloneInstance(result->service); + if (putInCache && !cacheResult) { + delete result; + } + return service; + } + } + + return handleDefault(key, actualReturn, status); +} + +UObject* +ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const +{ + return nullptr; +} + +UVector& +ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const { + return getVisibleIDs(result, nullptr, status); +} + +UVector& +ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const +{ + result.removeAllElements(); + + if (U_FAILURE(status)) { + return result; + } + UObjectDeleter *savedDeleter = result.setDeleter(uprv_deleteUObject); + + { + Mutex mutex(&lock); + const Hashtable* map = getVisibleIDMap(status); + if (map != nullptr) { + ICUServiceKey* fallbackKey = createKey(matchID, status); + + for (int32_t pos = UHASH_FIRST; U_SUCCESS(status); ) { + const UHashElement* e = map->nextElement(pos); + if (e == nullptr) { + break; + } + + const UnicodeString* id = (const UnicodeString*)e->key.pointer; + if (fallbackKey != nullptr) { + if (!fallbackKey->isFallbackOf(*id)) { + continue; + } + } + + LocalPointer idClone(id->clone(), status); + result.adoptElement(idClone.orphan(), status); + } + delete fallbackKey; + } + } + if (U_FAILURE(status)) { + result.removeAllElements(); + } + result.setDeleter(savedDeleter); + return result; +} + +const Hashtable* +ICUService::getVisibleIDMap(UErrorCode& status) const { + if (U_FAILURE(status)) return nullptr; + + // must only be called when lock is already held + + ICUService* ncthis = (ICUService*)this; // cast away semantic const + if (idCache == nullptr) { + ncthis->idCache = new Hashtable(status); + if (idCache == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } else if (factories != nullptr) { + for (int32_t pos = factories->size(); --pos >= 0;) { + ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(pos); + f->updateVisibleIDs(*idCache, status); + } + if (U_FAILURE(status)) { + delete idCache; + ncthis->idCache = nullptr; + } + } + } + + return idCache; +} + + +UnicodeString& +ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const +{ + return getDisplayName(id, result, Locale::getDefault()); +} + +UnicodeString& +ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const +{ + { + UErrorCode status = U_ZERO_ERROR; + Mutex mutex(&lock); + const Hashtable* map = getVisibleIDMap(status); + if (map != nullptr) { + ICUServiceFactory* f = (ICUServiceFactory*)map->get(id); + if (f != nullptr) { + f->getDisplayName(id, locale, result); + return result; + } + + // fallback + status = U_ZERO_ERROR; + ICUServiceKey* fallbackKey = createKey(&id, status); + while (fallbackKey != nullptr && fallbackKey->fallback()) { + UnicodeString us; + fallbackKey->currentID(us); + f = (ICUServiceFactory*)map->get(us); + if (f != nullptr) { + f->getDisplayName(id, locale, result); + delete fallbackKey; + return result; + } + } + delete fallbackKey; + } + } + result.setToBogus(); + return result; +} + +UVector& +ICUService::getDisplayNames(UVector& result, UErrorCode& status) const +{ + return getDisplayNames(result, Locale::getDefault(), nullptr, status); +} + + +UVector& +ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const +{ + return getDisplayNames(result, locale, nullptr, status); +} + +UVector& +ICUService::getDisplayNames(UVector& result, + const Locale& locale, + const UnicodeString* matchID, + UErrorCode& status) const +{ + result.removeAllElements(); + result.setDeleter(userv_deleteStringPair); + if (U_SUCCESS(status)) { + ICUService* ncthis = (ICUService*)this; // cast away semantic const + Mutex mutex(&lock); + + if (dnCache != nullptr && dnCache->locale != locale) { + delete dnCache; + ncthis->dnCache = nullptr; + } + + if (dnCache == nullptr) { + const Hashtable* m = getVisibleIDMap(status); + if (U_FAILURE(status)) { + return result; + } + ncthis->dnCache = new DNCache(locale); + if (dnCache == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return result; + } + + int32_t pos = UHASH_FIRST; + const UHashElement* entry = nullptr; + while ((entry = m->nextElement(pos)) != nullptr) { + const UnicodeString* id = (const UnicodeString*)entry->key.pointer; + ICUServiceFactory* f = (ICUServiceFactory*)entry->value.pointer; + UnicodeString dname; + f->getDisplayName(*id, locale, dname); + if (dname.isBogus()) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap + if (U_SUCCESS(status)) { + continue; + } + } + delete dnCache; + ncthis->dnCache = nullptr; + return result; + } + } + } + + ICUServiceKey* matchKey = createKey(matchID, status); + /* To ensure that all elements in the hashtable are iterated, set pos to -1. + * nextElement(pos) will skip the position at pos and begin the iteration + * at the next position, which in this case will be 0. + */ + int32_t pos = UHASH_FIRST; + const UHashElement *entry = nullptr; + while ((entry = dnCache->cache.nextElement(pos)) != nullptr) { + const UnicodeString* id = (const UnicodeString*)entry->value.pointer; + if (matchKey != nullptr && !matchKey->isFallbackOf(*id)) { + continue; + } + const UnicodeString* dn = (const UnicodeString*)entry->key.pointer; + StringPair* sp = StringPair::create(*id, *dn, status); + result.adoptElement(sp, status); + if (U_FAILURE(status)) { + result.removeAllElements(); + break; + } + } + delete matchKey; + + return result; +} + +URegistryKey +ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status) +{ + return registerInstance(objToAdopt, id, true, status); +} + +URegistryKey +ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) +{ + ICUServiceKey* key = createKey(&id, status); + if (key != nullptr) { + UnicodeString canonicalID; + key->canonicalID(canonicalID); + delete key; + + ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status); + if (f != nullptr) { + return registerFactory(f, status); + } + } + delete objToAdopt; + return nullptr; +} + +ICUServiceFactory* +ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) +{ + if (U_SUCCESS(status)) { + if ((objToAdopt != nullptr) && (!id.isBogus())) { + return new SimpleFactory(objToAdopt, id, visible); + } + status = U_ILLEGAL_ARGUMENT_ERROR; + } + return nullptr; +} + +URegistryKey +ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status) +{ + LocalPointerlpFactoryToAdopt(factoryToAdopt); + if (U_FAILURE(status) || factoryToAdopt == nullptr) { + return nullptr; + } + { + Mutex mutex(&lock); + + if (factories == nullptr) { + LocalPointer lpFactories(new UVector(uprv_deleteUObject, nullptr, status), status); + if (U_FAILURE(status)) { + return nullptr; + } + factories = lpFactories.orphan(); + } + factories->insertElementAt(lpFactoryToAdopt.orphan(), 0, status); + if (U_SUCCESS(status)) { + clearCaches(); + } + } // Close of mutex lock block. + + if (U_SUCCESS(status)) { + notifyChanged(); + return (URegistryKey)factoryToAdopt; + } else { + return nullptr; + } +} + +UBool +ICUService::unregister(URegistryKey rkey, UErrorCode& status) +{ + ICUServiceFactory *factory = (ICUServiceFactory*)rkey; + UBool result = false; + if (factory != nullptr && factories != nullptr) { + Mutex mutex(&lock); + + if (factories->removeElement(factory)) { + clearCaches(); + result = true; + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + delete factory; + } + } + if (result) { + notifyChanged(); + } + return result; +} + +void +ICUService::reset() +{ + { + Mutex mutex(&lock); + reInitializeFactories(); + clearCaches(); + } + notifyChanged(); +} + +void +ICUService::reInitializeFactories() +{ + if (factories != nullptr) { + factories->removeAllElements(); + } +} + +UBool +ICUService::isDefault() const +{ + return countFactories() == 0; +} + +ICUServiceKey* +ICUService::createKey(const UnicodeString* id, UErrorCode& status) const +{ + return (U_FAILURE(status) || id == nullptr) ? nullptr : new ICUServiceKey(*id); +} + +void +ICUService::clearCaches() +{ + // callers synchronize before use + ++timestamp; + delete dnCache; + dnCache = nullptr; + delete idCache; + idCache = nullptr; + delete serviceCache; serviceCache = nullptr; +} + +void +ICUService::clearServiceCache() +{ + // callers synchronize before use + delete serviceCache; serviceCache = nullptr; +} + +UBool +ICUService::acceptsListener(const EventListener& l) const +{ + return dynamic_cast(&l) != nullptr; +} + +void +ICUService::notifyListener(EventListener& l) const +{ + (static_cast(l)).serviceChanged(*this); +} + +UnicodeString& +ICUService::getName(UnicodeString& result) const +{ + return result.append(name); +} + +int32_t +ICUService::countFactories() const +{ + return factories == nullptr ? 0 : factories->size(); +} + +int32_t +ICUService::getTimestamp() const +{ + return timestamp; +} + +U_NAMESPACE_END + +/* UCONFIG_NO_SERVICE */ +#endif diff --git a/deps/icu-small/source/common/serv.h b/deps/icu-small/source/common/serv.h index 3bd3d9a9b9ea61..9289937acdd92a 100644 --- a/deps/icu-small/source/common/serv.h +++ b/deps/icu-small/source/common/serv.h @@ -1,996 +1,996 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** - ******************************************************************************* - * Copyright (C) 2001-2011, International Business Machines Corporation. * - * All Rights Reserved. * - ******************************************************************************* - */ - -#ifndef ICUSERV_H -#define ICUSERV_H - -#include "unicode/utypes.h" - -#if UCONFIG_NO_SERVICE - -U_NAMESPACE_BEGIN - -/* - * Allow the declaration of APIs with pointers to ICUService - * even when service is removed from the build. - */ -class ICUService; - -U_NAMESPACE_END - -#else - -#include "unicode/unistr.h" -#include "unicode/locid.h" -#include "unicode/umisc.h" - -#include "hash.h" -#include "uvector.h" -#include "servnotf.h" - -class ICUServiceTest; - -U_NAMESPACE_BEGIN - -class ICUServiceKey; -class ICUServiceFactory; -class SimpleFactory; -class ServiceListener; -class ICUService; - -class DNCache; - -/******************************************************************* - * ICUServiceKey - */ - -/** - *

ICUServiceKeys are used to communicate with factories to - * generate an instance of the service. ICUServiceKeys define how - * ids are canonicalized, provide both a current id and a current - * descriptor to use in querying the cache and factories, and - * determine the fallback strategy.

- * - *

ICUServiceKeys provide both a currentDescriptor and a currentID. - * The descriptor contains an optional prefix, followed by '/' - * and the currentID. Factories that handle complex keys, - * for example number format factories that generate multiple - * kinds of formatters for the same locale, use the descriptor - * to provide a fully unique identifier for the service object, - * while using the currentID (in this case, the locale string), - * as the visible IDs that can be localized.

- * - *

The default implementation of ICUServiceKey has no fallbacks and - * has no custom descriptors.

- */ -class U_COMMON_API ICUServiceKey : public UObject { - private: - const UnicodeString _id; - - protected: - static const UChar PREFIX_DELIMITER; - - public: - - /** - *

Construct a key from an id.

- * - * @param id the ID from which to construct the key. - */ - ICUServiceKey(const UnicodeString& id); - - /** - *

Virtual destructor.

- */ - virtual ~ICUServiceKey(); - - /** - *

Return the original ID used to construct this key.

- * - * @return the ID used to construct this key. - */ - virtual const UnicodeString& getID() const; - - /** - *

Return the canonical version of the original ID. This implementation - * appends the original ID to result. Result is returned as a convenience.

- * - * @param result the output parameter to which the id will be appended. - * @return the modified result. - */ - virtual UnicodeString& canonicalID(UnicodeString& result) const; - - /** - *

Return the (canonical) current ID. This implementation appends - * the canonical ID to result. Result is returned as a convenience.

- * - * @param result the output parameter to which the current id will be appended. - * @return the modified result. - */ - virtual UnicodeString& currentID(UnicodeString& result) const; - - /** - *

Return the current descriptor. This implementation appends - * the current descriptor to result. Result is returned as a convenience.

- * - *

The current descriptor is used to fully - * identify an instance of the service in the cache. A - * factory may handle all descriptors for an ID, or just a - * particular descriptor. The factory can either parse the - * descriptor or use custom API on the key in order to - * instantiate the service.

- * - * @param result the output parameter to which the current id will be appended. - * @return the modified result. - */ - virtual UnicodeString& currentDescriptor(UnicodeString& result) const; - - /** - *

If the key has a fallback, modify the key and return true, - * otherwise return false. The current ID will change if there - * is a fallback. No currentIDs should be repeated, and fallback - * must eventually return false. This implementation has no fallbacks - * and always returns false.

- * - * @return true if the ICUServiceKey changed to a valid fallback value. - */ - virtual UBool fallback(); - - /** - *

Return true if a key created from id matches, or would eventually - * fallback to match, the canonical ID of this ICUServiceKey.

- * - * @param id the id to test. - * @return true if this ICUServiceKey's canonical ID is a fallback of id. - */ - virtual UBool isFallbackOf(const UnicodeString& id) const; - - /** - *

Return the prefix. This implementation leaves result unchanged. - * Result is returned as a convenience.

- * - * @param result the output parameter to which the prefix will be appended. - * @return the modified result. - */ - virtual UnicodeString& prefix(UnicodeString& result) const; - - /** - *

A utility to parse the prefix out of a descriptor string. Only - * the (undelimited) prefix, if any, remains in result. Result is returned as a - * convenience.

- * - * @param result an input/output parameter that on entry is a descriptor, and - * on exit is the prefix of that descriptor. - * @return the modified result. - */ - static UnicodeString& parsePrefix(UnicodeString& result); - - /** - *

A utility to parse the suffix out of a descriptor string. Only - * the (undelimited) suffix, if any, remains in result. Result is returned as a - * convenience.

- * - * @param result an input/output parameter that on entry is a descriptor, and - * on exit is the suffix of that descriptor. - * @return the modified result. - */ - static UnicodeString& parseSuffix(UnicodeString& result); - -public: - /** - * UObject RTTI boilerplate. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /** - * UObject RTTI boilerplate. - */ - virtual UClassID getDynamicClassID() const override; - -#ifdef SERVICE_DEBUG - public: - virtual UnicodeString& debug(UnicodeString& result) const; - virtual UnicodeString& debugClass(UnicodeString& result) const; -#endif - -}; - - /******************************************************************* - * ICUServiceFactory - */ - - /** - *

An implementing ICUServiceFactory generates the service objects maintained by the - * service. A factory generates a service object from a key, - * updates id->factory mappings, and returns the display name for - * a supported id.

- */ -class U_COMMON_API ICUServiceFactory : public UObject { - public: - virtual ~ICUServiceFactory(); - - /** - *

Create a service object from the key, if this factory - * supports the key. Otherwise, return NULL.

- * - *

If the factory supports the key, then it can call - * the service's getKey(ICUServiceKey, String[], ICUServiceFactory) method - * passing itself as the factory to get the object that - * the service would have created prior to the factory's - * registration with the service. This can change the - * key, so any information required from the key should - * be extracted before making such a callback.

- * - * @param key the service key. - * @param service the service with which this factory is registered. - * @param status the error code status. - * @return the service object, or NULL if the factory does not support the key. - */ - virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const = 0; - - /** - *

Update result to reflect the IDs (not descriptors) that this - * factory publicly handles. Result contains mappings from ID to - * factory. On entry it will contain all (visible) mappings from - * previously-registered factories.

- * - *

This function, together with getDisplayName, are used to - * support ICUService::getDisplayNames. The factory determines - * which IDs (of those it supports) it will make visible, and of - * those, which it will provide localized display names for. In - * most cases it will register mappings from all IDs it supports - * to itself.

- * - * @param result the mapping table to update. - * @param status the error code status. - */ - virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const = 0; - - /** - *

Return, in result, the display name of the id in the provided locale. - * This is an id, not a descriptor. If the id is - * not visible, sets result to bogus. If the - * incoming result is bogus, it remains bogus. Result is returned as a - * convenience. Results are not defined if id is not one supported by this - * factory.

- * - * @param id a visible id supported by this factory. - * @param locale the locale for which to generate the corresponding localized display name. - * @param result output parameter to hold the display name. - * @return result. - */ - virtual UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const = 0; -}; - -/* - ****************************************************************** - */ - - /** - *

A default implementation of factory. This provides default - * implementations for subclasses, and implements a singleton - * factory that matches a single ID and returns a single - * (possibly deferred-initialized) instance. This implements - * updateVisibleIDs to add a mapping from its ID to itself - * if visible is true, or to remove any existing mapping - * for its ID if visible is false. No localization of display - * names is performed.

- */ -class U_COMMON_API SimpleFactory : public ICUServiceFactory { - protected: - UObject* _instance; - const UnicodeString _id; - const UBool _visible; - - public: - /** - *

Construct a SimpleFactory that maps a single ID to a single - * service instance. If visible is true, the ID will be visible. - * The instance must not be NULL. The SimpleFactory will adopt - * the instance, which must not be changed subsequent to this call.

- * - * @param instanceToAdopt the service instance to adopt. - * @param id the ID to assign to this service instance. - * @param visible if true, the ID will be visible. - */ - SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible = true); - - /** - *

Destructor.

- */ - virtual ~SimpleFactory(); - - /** - *

This implementation returns a clone of the service instance if the factory's ID is equal to - * the key's currentID. Service and prefix are ignored.

- * - * @param key the service key. - * @param service the service with which this factory is registered. - * @param status the error code status. - * @return the service object, or NULL if the factory does not support the key. - */ - virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const override; - - /** - *

This implementation adds a mapping from ID -> this to result if visible is true, - * otherwise it removes ID from result.

- * - * @param result the mapping table to update. - * @param status the error code status. - */ - virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const override; - - /** - *

This implementation returns the factory ID if it equals id and visible is true, - * otherwise it returns the empty string. (This implementation provides - * no localized id information.)

- * - * @param id a visible id supported by this factory. - * @param locale the locale for which to generate the corresponding localized display name. - * @param result output parameter to hold the display name. - * @return result. - */ - virtual UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const override; - -public: - /** - * UObject RTTI boilerplate. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /** - * UObject RTTI boilerplate. - */ - virtual UClassID getDynamicClassID() const override; - -#ifdef SERVICE_DEBUG - public: - virtual UnicodeString& debug(UnicodeString& toAppendTo) const; - virtual UnicodeString& debugClass(UnicodeString& toAppendTo) const; -#endif - -}; - -/* - ****************************************************************** - */ - -/** - *

ServiceListener is the listener that ICUService provides by default. - * ICUService will notify this listener when factories are added to - * or removed from the service. Subclasses can provide - * different listener interfaces that extend EventListener, and modify - * acceptsListener and notifyListener as appropriate.

- */ -class U_COMMON_API ServiceListener : public EventListener { -public: - virtual ~ServiceListener(); - - /** - *

This method is called when the service changes. At the time of the - * call this listener is registered with the service. It must - * not modify the notifier in the context of this call.

- * - * @param service the service that changed. - */ - virtual void serviceChanged(const ICUService& service) const = 0; - -public: - /** - * UObject RTTI boilerplate. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /** - * UObject RTTI boilerplate. - */ - virtual UClassID getDynamicClassID() const override; - -}; - -/* - ****************************************************************** - */ - -/** - *

A StringPair holds a displayName/ID pair. ICUService uses it - * as the array elements returned by getDisplayNames. - */ -class U_COMMON_API StringPair : public UMemory { -public: - /** - *

The display name of the pair.

- */ - const UnicodeString displayName; - - /** - *

The ID of the pair.

- */ - const UnicodeString id; - - /** - *

Creates a string pair from a displayName and an ID.

- * - * @param displayName the displayName. - * @param id the ID. - * @param status the error code status. - * @return a StringPair if the creation was successful, otherwise NULL. - */ - static StringPair* create(const UnicodeString& displayName, - const UnicodeString& id, - UErrorCode& status); - - /** - *

Return true if either string of the pair is bogus.

- * @return true if either string of the pair is bogus. - */ - UBool isBogus() const; - -private: - StringPair(const UnicodeString& displayName, const UnicodeString& id); -}; - -/******************************************************************* - * ICUService - */ - - /** - *

A Service provides access to service objects that implement a - * particular service, e.g. transliterators. Users provide a String - * id (for example, a locale string) to the service, and get back an - * object for that id. Service objects can be any kind of object. A - * new service object is returned for each query. The caller is - * responsible for deleting it.

- * - *

Services 'canonicalize' the query ID and use the canonical ID to - * query for the service. The service also defines a mechanism to - * 'fallback' the ID multiple times. Clients can optionally request - * the actual ID that was matched by a query when they use an ID to - * retrieve a service object.

- * - *

Service objects are instantiated by ICUServiceFactory objects - * registered with the service. The service queries each - * ICUServiceFactory in turn, from most recently registered to - * earliest registered, until one returns a service object. If none - * responds with a service object, a fallback ID is generated, and the - * process repeats until a service object is returned or until the ID - * has no further fallbacks.

- * - *

In ICU 2.4, UObject (the base class of service instances) does - * not define a polymorphic clone function. ICUService uses clones to - * manage ownership. Thus, for now, ICUService defines an abstract - * method, cloneInstance, that clients must implement to create clones - * of the service instances. This may change in future releases of - * ICU.

- * - *

ICUServiceFactories can be dynamically registered and - * unregistered with the service. When registered, an - * ICUServiceFactory is installed at the head of the factory list, and - * so gets 'first crack' at any keys or fallback keys. When - * unregistered, it is removed from the service and can no longer be - * located through it. Service objects generated by this factory and - * held by the client are unaffected.

- * - *

If a service has variants (e.g., the different variants of - * BreakIterator) an ICUServiceFactory can use the prefix of the - * ICUServiceKey to determine the variant of a service to generate. - * If it does not support all variants, it can request - * previously-registered factories to handle the ones it does not - * support.

- * - *

ICUService uses ICUServiceKeys to query factories and perform - * fallback. The ICUServiceKey defines the canonical form of the ID, - * and implements the fallback strategy. Custom ICUServiceKeys can be - * defined that parse complex IDs into components that - * ICUServiceFactories can more easily use. The ICUServiceKey can - * cache the results of this parsing to save repeated effort. - * ICUService provides convenience APIs that take UnicodeStrings and - * generate default ICUServiceKeys for use in querying.

- * - *

ICUService provides API to get the list of IDs publicly - * supported by the service (although queries aren't restricted to - * this list). This list contains only 'simple' IDs, and not fully - * unique IDs. ICUServiceFactories are associated with each simple ID - * and the responsible factory can also return a human-readable - * localized version of the simple ID, for use in user interfaces. - * ICUService can also provide an array of the all the localized - * visible IDs and their corresponding internal IDs.

- * - *

ICUService implements ICUNotifier, so that clients can register - * to receive notification when factories are added or removed from - * the service. ICUService provides a default EventListener - * subinterface, ServiceListener, which can be registered with the - * service. When the service changes, the ServiceListener's - * serviceChanged method is called with the service as the - * argument.

- * - *

The ICUService API is both rich and generic, and it is expected - * that most implementations will statically 'wrap' ICUService to - * present a more appropriate API-- for example, to declare the type - * of the objects returned from get, to limit the factories that can - * be registered with the service, or to define their own listener - * interface with a custom callback method. They might also customize - * ICUService by overriding it, for example, to customize the - * ICUServiceKey and fallback strategy. ICULocaleService is a - * subclass of ICUService that uses Locale names as IDs and uses - * ICUServiceKeys that implement the standard resource bundle fallback - * strategy. Most clients will wish to subclass it instead of - * ICUService.

- */ -class U_COMMON_API ICUService : public ICUNotifier { - protected: - /** - * Name useful for debugging. - */ - const UnicodeString name; - - private: - - /** - * Timestamp so iterators can be fail-fast. - */ - uint32_t timestamp; - - /** - * All the factories registered with this service. - */ - UVector* factories; - - /** - * The service cache. - */ - Hashtable* serviceCache; - - /** - * The ID cache. - */ - Hashtable* idCache; - - /** - * The name cache. - */ - DNCache* dnCache; - - /** - * Constructor. - */ - public: - /** - *

Construct a new ICUService.

- */ - ICUService(); - - /** - *

Construct with a name (useful for debugging).

- * - * @param name a name to use in debugging. - */ - ICUService(const UnicodeString& name); - - /** - *

Destructor.

- */ - virtual ~ICUService(); - - /** - *

Return the name of this service. This will be the empty string if none was assigned. - * Returns result as a convenience.

- * - * @param result an output parameter to contain the name of this service. - * @return the name of this service. - */ - UnicodeString& getName(UnicodeString& result) const; - - /** - *

Convenience override for get(ICUServiceKey&, UnicodeString*). This uses - * createKey to create a key for the provided descriptor.

- * - * @param descriptor the descriptor. - * @param status the error code status. - * @return the service instance, or NULL. - */ - UObject* get(const UnicodeString& descriptor, UErrorCode& status) const; - - /** - *

Convenience override for get(ICUServiceKey&, UnicodeString*). This uses - * createKey to create a key from the provided descriptor.

- * - * @param descriptor the descriptor. - * @param actualReturn a pointer to a UnicodeString to hold the matched descriptor, or NULL. - * @param status the error code status. - * @return the service instance, or NULL. - */ - UObject* get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const; - - /** - *

Convenience override for get(ICUServiceKey&, UnicodeString*).

- * - * @param key the key. - * @param status the error code status. - * @return the service instance, or NULL. - */ - UObject* getKey(ICUServiceKey& key, UErrorCode& status) const; - - /** - *

Given a key, return a service object, and, if actualReturn - * is not NULL, the descriptor with which it was found in the - * first element of actualReturn. If no service object matches - * this key, returns NULL and leaves actualReturn unchanged.

- * - *

This queries the cache using the key's descriptor, and if no - * object in the cache matches, tries the key on each - * registered factory, in order. If none generates a service - * object for the key, repeats the process with each fallback of - * the key, until either a factory returns a service object, or the key - * has no fallback. If no object is found, the result of handleDefault - * is returned.

- * - *

Subclasses can override this method to further customize the - * result before returning it. - * - * @param key the key. - * @param actualReturn a pointer to a UnicodeString to hold the matched descriptor, or NULL. - * @param status the error code status. - * @return the service instance, or NULL. - */ - virtual UObject* getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const; - - /** - *

This version of getKey is only called by ICUServiceFactories within the scope - * of a previous getKey call, to determine what previously-registered factories would - * have returned. For details, see getKey(ICUServiceKey&, UErrorCode&). Subclasses - * should not call it directly, but call through one of the other get functions.

- * - * @param key the key. - * @param actualReturn a pointer to a UnicodeString to hold the matched descriptor, or NULL. - * @param factory the factory making the recursive call. - * @param status the error code status. - * @return the service instance, or NULL. - */ - UObject* getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const; - - /** - *

Convenience override for getVisibleIDs(String) that passes null - * as the fallback, thus returning all visible IDs.

- * - * @param result a vector to hold the returned IDs. - * @param status the error code status. - * @return the result vector. - */ - UVector& getVisibleIDs(UVector& result, UErrorCode& status) const; - - /** - *

Return a snapshot of the visible IDs for this service. This - * list will not change as ICUServiceFactories are added or removed, but the - * supported IDs will, so there is no guarantee that all and only - * the IDs in the returned list will be visible and supported by the - * service in subsequent calls.

- * - *

The IDs are returned as pointers to UnicodeStrings. The - * caller owns the IDs. Previous contents of result are discarded before - * new elements, if any, are added.

- * - *

matchID is passed to createKey to create a key. If the key - * is not NULL, its isFallbackOf method is used to filter out IDs - * that don't match the key or have it as a fallback.

- * - * @param result a vector to hold the returned IDs. - * @param matchID an ID used to filter the result, or NULL if all IDs are desired. - * @param status the error code status. - * @return the result vector. - */ - UVector& getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const; - - /** - *

Convenience override for getDisplayName(const UnicodeString&, const Locale&, UnicodeString&) that - * uses the current default locale.

- * - * @param id the ID for which to retrieve the localized displayName. - * @param result an output parameter to hold the display name. - * @return the modified result. - */ - UnicodeString& getDisplayName(const UnicodeString& id, UnicodeString& result) const; - - /** - *

Given a visible ID, return the display name in the requested locale. - * If there is no directly supported ID corresponding to this ID, result is - * set to bogus.

- * - * @param id the ID for which to retrieve the localized displayName. - * @param result an output parameter to hold the display name. - * @param locale the locale in which to localize the ID. - * @return the modified result. - */ - UnicodeString& getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const; - - /** - *

Convenience override of getDisplayNames(const Locale&, const UnicodeString*) that - * uses the current default Locale as the locale and NULL for - * the matchID.

- * - * @param result a vector to hold the returned displayName/id StringPairs. - * @param status the error code status. - * @return the modified result vector. - */ - UVector& getDisplayNames(UVector& result, UErrorCode& status) const; - - /** - *

Convenience override of getDisplayNames(const Locale&, const UnicodeString*) that - * uses NULL for the matchID.

- * - * @param result a vector to hold the returned displayName/id StringPairs. - * @param locale the locale in which to localize the ID. - * @param status the error code status. - * @return the modified result vector. - */ - UVector& getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const; - - /** - *

Return a snapshot of the mapping from display names to visible - * IDs for this service. This set will not change as factories - * are added or removed, but the supported IDs will, so there is - * no guarantee that all and only the IDs in the returned map will - * be visible and supported by the service in subsequent calls, - * nor is there any guarantee that the current display names match - * those in the result.

- * - *

The names are returned as pointers to StringPairs, which - * contain both the displayName and the corresponding ID. The - * caller owns the StringPairs. Previous contents of result are - * discarded before new elements, if any, are added.

- * - *

matchID is passed to createKey to create a key. If the key - * is not NULL, its isFallbackOf method is used to filter out IDs - * that don't match the key or have it as a fallback.

- * - * @param result a vector to hold the returned displayName/id StringPairs. - * @param locale the locale in which to localize the ID. - * @param matchID an ID used to filter the result, or NULL if all IDs are desired. - * @param status the error code status. - * @return the result vector. */ - UVector& getDisplayNames(UVector& result, - const Locale& locale, - const UnicodeString* matchID, - UErrorCode& status) const; - - /** - *

A convenience override of registerInstance(UObject*, const UnicodeString&, UBool) - * that defaults visible to true.

- * - * @param objToAdopt the object to register and adopt. - * @param id the ID to assign to this object. - * @param status the error code status. - * @return a registry key that can be passed to unregister to unregister - * (and discard) this instance. - */ - URegistryKey registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status); - - /** - *

Register a service instance with the provided ID. The ID will be - * canonicalized. The canonicalized ID will be returned by - * getVisibleIDs if visible is true. The service instance will be adopted and - * must not be modified subsequent to this call.

- * - *

This issues a serviceChanged notification to registered listeners.

- * - *

This implementation wraps the object using - * createSimpleFactory, and calls registerFactory.

- * - * @param objToAdopt the object to register and adopt. - * @param id the ID to assign to this object. - * @param visible true if getVisibleIDs is to return this ID. - * @param status the error code status. - * @return a registry key that can be passed to unregister() to unregister - * (and discard) this instance. - */ - virtual URegistryKey registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status); - - /** - *

Register an ICUServiceFactory. Returns a registry key that - * can be used to unregister the factory. The factory - * must not be modified subsequent to this call. The service owns - * all registered factories. In case of an error, the factory is - * deleted.

- * - *

This issues a serviceChanged notification to registered listeners.

- * - *

The default implementation accepts all factories.

- * - * @param factoryToAdopt the factory to register and adopt. - * @param status the error code status. - * @return a registry key that can be passed to unregister to unregister - * (and discard) this factory. - */ - virtual URegistryKey registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status); - - /** - *

Unregister a factory using a registry key returned by - * registerInstance or registerFactory. After a successful call, - * the factory will be removed from the service factory list and - * deleted, and the key becomes invalid.

- * - *

This issues a serviceChanged notification to registered - * listeners.

- * - * @param rkey the registry key. - * @param status the error code status. - * @return true if the call successfully unregistered the factory. - */ - virtual UBool unregister(URegistryKey rkey, UErrorCode& status); - - /** - *

Reset the service to the default factories. The factory - * lock is acquired and then reInitializeFactories is called.

- * - *

This issues a serviceChanged notification to registered listeners.

- */ - virtual void reset(void); - - /** - *

Return true if the service is in its default state.

- * - *

The default implementation returns true if there are no - * factories registered.

- */ - virtual UBool isDefault(void) const; - - /** - *

Create a key from an ID. If ID is NULL, returns NULL.

- * - *

The default implementation creates an ICUServiceKey instance. - * Subclasses can override to define more useful keys appropriate - * to the factories they accept.

- * - * @param a pointer to the ID for which to create a default ICUServiceKey. - * @param status the error code status. - * @return the ICUServiceKey corresponding to ID, or NULL. - */ - virtual ICUServiceKey* createKey(const UnicodeString* id, UErrorCode& status) const; - - /** - *

Clone object so that caller can own the copy. In ICU2.4, UObject doesn't define - * clone, so we need an instance-aware method that knows how to do this. - * This is public so factories can call it, but should really be protected.

- * - * @param instance the service instance to clone. - * @return a clone of the passed-in instance, or NULL if cloning was unsuccessful. - */ - virtual UObject* cloneInstance(UObject* instance) const = 0; - - - /************************************************************************ - * Subclassing API - */ - - protected: - - /** - *

Create a factory that wraps a single service object. Called by registerInstance.

- * - *

The default implementation returns an instance of SimpleFactory.

- * - * @param instanceToAdopt the service instance to adopt. - * @param id the ID to assign to this service instance. - * @param visible if true, the ID will be visible. - * @param status the error code status. - * @return an instance of ICUServiceFactory that maps this instance to the provided ID. - */ - virtual ICUServiceFactory* createSimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status); - - /** - *

Reinitialize the factory list to its default state. After this call, isDefault() - * must return true.

- * - *

This issues a serviceChanged notification to registered listeners.

- * - *

The default implementation clears the factory list. - * Subclasses can override to provide other default initialization - * of the factory list. Subclasses must not call this method - * directly, since it must only be called while holding write - * access to the factory list.

- */ - virtual void reInitializeFactories(void); - - /** - *

Default handler for this service if no factory in the factory list - * handled the key passed to getKey.

- * - *

The default implementation returns NULL.

- * - * @param key the key. - * @param actualReturn a pointer to a UnicodeString to hold the matched descriptor, or NULL. - * @param status the error code status. - * @return the service instance, or NULL. - */ - virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const; - - /** - *

Clear caches maintained by this service.

- * - *

Subclasses can override if they implement additional caches - * that need to be cleared when the service changes. Subclasses - * should generally not call this method directly, as it must only - * be called while synchronized on the factory lock.

- */ - virtual void clearCaches(void); - - /** - *

Return true if the listener is accepted.

- * - *

The default implementation accepts the listener if it is - * a ServiceListener. Subclasses can override this to accept - * different listeners.

- * - * @param l the listener to test. - * @return true if the service accepts the listener. - */ - virtual UBool acceptsListener(const EventListener& l) const override; - - /** - *

Notify the listener of a service change.

- * - *

The default implementation assumes a ServiceListener. - * If acceptsListener has been overridden to accept different - * listeners, this should be overridden as well.

- * - * @param l the listener to notify. - */ - virtual void notifyListener(EventListener& l) const override; - - /************************************************************************ - * Utilities for subclasses. - */ - - /** - *

Clear only the service cache.

- * - *

This can be called by subclasses when a change affects the service - * cache but not the ID caches, e.g., when the default locale changes - * the resolution of IDs also changes, requiring the cache to be - * flushed, but not the visible IDs themselves.

- */ - void clearServiceCache(void); - - /** - *

Return a map from visible IDs to factories. - * This must only be called when the mutex is held.

- * - * @param status the error code status. - * @return a Hashtable containing mappings from visible - * IDs to factories. - */ - const Hashtable* getVisibleIDMap(UErrorCode& status) const; - - /** - *

Allow subclasses to read the time stamp.

- * - * @return the timestamp. - */ - int32_t getTimestamp(void) const; - - /** - *

Return the number of registered factories.

- * - * @return the number of factories registered at the time of the call. - */ - int32_t countFactories(void) const; - -private: - - friend class ::ICUServiceTest; // give tests access to countFactories. -}; - -U_NAMESPACE_END - - /* UCONFIG_NO_SERVICE */ -#endif - - /* ICUSERV_H */ -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** + ******************************************************************************* + * Copyright (C) 2001-2011, International Business Machines Corporation. * + * All Rights Reserved. * + ******************************************************************************* + */ + +#ifndef ICUSERV_H +#define ICUSERV_H + +#include "unicode/utypes.h" + +#if UCONFIG_NO_SERVICE + +U_NAMESPACE_BEGIN + +/* + * Allow the declaration of APIs with pointers to ICUService + * even when service is removed from the build. + */ +class ICUService; + +U_NAMESPACE_END + +#else + +#include "unicode/unistr.h" +#include "unicode/locid.h" +#include "unicode/umisc.h" + +#include "hash.h" +#include "uvector.h" +#include "servnotf.h" + +class ICUServiceTest; + +U_NAMESPACE_BEGIN + +class ICUServiceKey; +class ICUServiceFactory; +class SimpleFactory; +class ServiceListener; +class ICUService; + +class DNCache; + +/******************************************************************* + * ICUServiceKey + */ + +/** + *

ICUServiceKeys are used to communicate with factories to + * generate an instance of the service. ICUServiceKeys define how + * ids are canonicalized, provide both a current id and a current + * descriptor to use in querying the cache and factories, and + * determine the fallback strategy.

+ * + *

ICUServiceKeys provide both a currentDescriptor and a currentID. + * The descriptor contains an optional prefix, followed by '/' + * and the currentID. Factories that handle complex keys, + * for example number format factories that generate multiple + * kinds of formatters for the same locale, use the descriptor + * to provide a fully unique identifier for the service object, + * while using the currentID (in this case, the locale string), + * as the visible IDs that can be localized.

+ * + *

The default implementation of ICUServiceKey has no fallbacks and + * has no custom descriptors.

+ */ +class U_COMMON_API ICUServiceKey : public UObject { + private: + const UnicodeString _id; + + protected: + static const char16_t PREFIX_DELIMITER; + + public: + + /** + *

Construct a key from an id.

+ * + * @param id the ID from which to construct the key. + */ + ICUServiceKey(const UnicodeString& id); + + /** + *

Virtual destructor.

+ */ + virtual ~ICUServiceKey(); + + /** + *

Return the original ID used to construct this key.

+ * + * @return the ID used to construct this key. + */ + virtual const UnicodeString& getID() const; + + /** + *

Return the canonical version of the original ID. This implementation + * appends the original ID to result. Result is returned as a convenience.

+ * + * @param result the output parameter to which the id will be appended. + * @return the modified result. + */ + virtual UnicodeString& canonicalID(UnicodeString& result) const; + + /** + *

Return the (canonical) current ID. This implementation appends + * the canonical ID to result. Result is returned as a convenience.

+ * + * @param result the output parameter to which the current id will be appended. + * @return the modified result. + */ + virtual UnicodeString& currentID(UnicodeString& result) const; + + /** + *

Return the current descriptor. This implementation appends + * the current descriptor to result. Result is returned as a convenience.

+ * + *

The current descriptor is used to fully + * identify an instance of the service in the cache. A + * factory may handle all descriptors for an ID, or just a + * particular descriptor. The factory can either parse the + * descriptor or use custom API on the key in order to + * instantiate the service.

+ * + * @param result the output parameter to which the current id will be appended. + * @return the modified result. + */ + virtual UnicodeString& currentDescriptor(UnicodeString& result) const; + + /** + *

If the key has a fallback, modify the key and return true, + * otherwise return false. The current ID will change if there + * is a fallback. No currentIDs should be repeated, and fallback + * must eventually return false. This implementation has no fallbacks + * and always returns false.

+ * + * @return true if the ICUServiceKey changed to a valid fallback value. + */ + virtual UBool fallback(); + + /** + *

Return true if a key created from id matches, or would eventually + * fallback to match, the canonical ID of this ICUServiceKey.

+ * + * @param id the id to test. + * @return true if this ICUServiceKey's canonical ID is a fallback of id. + */ + virtual UBool isFallbackOf(const UnicodeString& id) const; + + /** + *

Return the prefix. This implementation leaves result unchanged. + * Result is returned as a convenience.

+ * + * @param result the output parameter to which the prefix will be appended. + * @return the modified result. + */ + virtual UnicodeString& prefix(UnicodeString& result) const; + + /** + *

A utility to parse the prefix out of a descriptor string. Only + * the (undelimited) prefix, if any, remains in result. Result is returned as a + * convenience.

+ * + * @param result an input/output parameter that on entry is a descriptor, and + * on exit is the prefix of that descriptor. + * @return the modified result. + */ + static UnicodeString& parsePrefix(UnicodeString& result); + + /** + *

A utility to parse the suffix out of a descriptor string. Only + * the (undelimited) suffix, if any, remains in result. Result is returned as a + * convenience.

+ * + * @param result an input/output parameter that on entry is a descriptor, and + * on exit is the suffix of that descriptor. + * @return the modified result. + */ + static UnicodeString& parseSuffix(UnicodeString& result); + +public: + /** + * UObject RTTI boilerplate. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * UObject RTTI boilerplate. + */ + virtual UClassID getDynamicClassID() const override; + +#ifdef SERVICE_DEBUG + public: + virtual UnicodeString& debug(UnicodeString& result) const; + virtual UnicodeString& debugClass(UnicodeString& result) const; +#endif + +}; + + /******************************************************************* + * ICUServiceFactory + */ + + /** + *

An implementing ICUServiceFactory generates the service objects maintained by the + * service. A factory generates a service object from a key, + * updates id->factory mappings, and returns the display name for + * a supported id.

+ */ +class U_COMMON_API ICUServiceFactory : public UObject { + public: + virtual ~ICUServiceFactory(); + + /** + *

Create a service object from the key, if this factory + * supports the key. Otherwise, return nullptr.

+ * + *

If the factory supports the key, then it can call + * the service's getKey(ICUServiceKey, String[], ICUServiceFactory) method + * passing itself as the factory to get the object that + * the service would have created prior to the factory's + * registration with the service. This can change the + * key, so any information required from the key should + * be extracted before making such a callback.

+ * + * @param key the service key. + * @param service the service with which this factory is registered. + * @param status the error code status. + * @return the service object, or nullptr if the factory does not support the key. + */ + virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const = 0; + + /** + *

Update result to reflect the IDs (not descriptors) that this + * factory publicly handles. Result contains mappings from ID to + * factory. On entry it will contain all (visible) mappings from + * previously-registered factories.

+ * + *

This function, together with getDisplayName, are used to + * support ICUService::getDisplayNames. The factory determines + * which IDs (of those it supports) it will make visible, and of + * those, which it will provide localized display names for. In + * most cases it will register mappings from all IDs it supports + * to itself.

+ * + * @param result the mapping table to update. + * @param status the error code status. + */ + virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const = 0; + + /** + *

Return, in result, the display name of the id in the provided locale. + * This is an id, not a descriptor. If the id is + * not visible, sets result to bogus. If the + * incoming result is bogus, it remains bogus. Result is returned as a + * convenience. Results are not defined if id is not one supported by this + * factory.

+ * + * @param id a visible id supported by this factory. + * @param locale the locale for which to generate the corresponding localized display name. + * @param result output parameter to hold the display name. + * @return result. + */ + virtual UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const = 0; +}; + +/* + ****************************************************************** + */ + + /** + *

A default implementation of factory. This provides default + * implementations for subclasses, and implements a singleton + * factory that matches a single ID and returns a single + * (possibly deferred-initialized) instance. This implements + * updateVisibleIDs to add a mapping from its ID to itself + * if visible is true, or to remove any existing mapping + * for its ID if visible is false. No localization of display + * names is performed.

+ */ +class U_COMMON_API SimpleFactory : public ICUServiceFactory { + protected: + UObject* _instance; + const UnicodeString _id; + const UBool _visible; + + public: + /** + *

Construct a SimpleFactory that maps a single ID to a single + * service instance. If visible is true, the ID will be visible. + * The instance must not be nullptr. The SimpleFactory will adopt + * the instance, which must not be changed subsequent to this call.

+ * + * @param instanceToAdopt the service instance to adopt. + * @param id the ID to assign to this service instance. + * @param visible if true, the ID will be visible. + */ + SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible = true); + + /** + *

Destructor.

+ */ + virtual ~SimpleFactory(); + + /** + *

This implementation returns a clone of the service instance if the factory's ID is equal to + * the key's currentID. Service and prefix are ignored.

+ * + * @param key the service key. + * @param service the service with which this factory is registered. + * @param status the error code status. + * @return the service object, or nullptr if the factory does not support the key. + */ + virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const override; + + /** + *

This implementation adds a mapping from ID -> this to result if visible is true, + * otherwise it removes ID from result.

+ * + * @param result the mapping table to update. + * @param status the error code status. + */ + virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const override; + + /** + *

This implementation returns the factory ID if it equals id and visible is true, + * otherwise it returns the empty string. (This implementation provides + * no localized id information.)

+ * + * @param id a visible id supported by this factory. + * @param locale the locale for which to generate the corresponding localized display name. + * @param result output parameter to hold the display name. + * @return result. + */ + virtual UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const override; + +public: + /** + * UObject RTTI boilerplate. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * UObject RTTI boilerplate. + */ + virtual UClassID getDynamicClassID() const override; + +#ifdef SERVICE_DEBUG + public: + virtual UnicodeString& debug(UnicodeString& toAppendTo) const; + virtual UnicodeString& debugClass(UnicodeString& toAppendTo) const; +#endif + +}; + +/* + ****************************************************************** + */ + +/** + *

ServiceListener is the listener that ICUService provides by default. + * ICUService will notify this listener when factories are added to + * or removed from the service. Subclasses can provide + * different listener interfaces that extend EventListener, and modify + * acceptsListener and notifyListener as appropriate.

+ */ +class U_COMMON_API ServiceListener : public EventListener { +public: + virtual ~ServiceListener(); + + /** + *

This method is called when the service changes. At the time of the + * call this listener is registered with the service. It must + * not modify the notifier in the context of this call.

+ * + * @param service the service that changed. + */ + virtual void serviceChanged(const ICUService& service) const = 0; + +public: + /** + * UObject RTTI boilerplate. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * UObject RTTI boilerplate. + */ + virtual UClassID getDynamicClassID() const override; + +}; + +/* + ****************************************************************** + */ + +/** + *

A StringPair holds a displayName/ID pair. ICUService uses it + * as the array elements returned by getDisplayNames. + */ +class U_COMMON_API StringPair : public UMemory { +public: + /** + *

The display name of the pair.

+ */ + const UnicodeString displayName; + + /** + *

The ID of the pair.

+ */ + const UnicodeString id; + + /** + *

Creates a string pair from a displayName and an ID.

+ * + * @param displayName the displayName. + * @param id the ID. + * @param status the error code status. + * @return a StringPair if the creation was successful, otherwise nullptr. + */ + static StringPair* create(const UnicodeString& displayName, + const UnicodeString& id, + UErrorCode& status); + + /** + *

Return true if either string of the pair is bogus.

+ * @return true if either string of the pair is bogus. + */ + UBool isBogus() const; + +private: + StringPair(const UnicodeString& displayName, const UnicodeString& id); +}; + +/******************************************************************* + * ICUService + */ + + /** + *

A Service provides access to service objects that implement a + * particular service, e.g. transliterators. Users provide a String + * id (for example, a locale string) to the service, and get back an + * object for that id. Service objects can be any kind of object. A + * new service object is returned for each query. The caller is + * responsible for deleting it.

+ * + *

Services 'canonicalize' the query ID and use the canonical ID to + * query for the service. The service also defines a mechanism to + * 'fallback' the ID multiple times. Clients can optionally request + * the actual ID that was matched by a query when they use an ID to + * retrieve a service object.

+ * + *

Service objects are instantiated by ICUServiceFactory objects + * registered with the service. The service queries each + * ICUServiceFactory in turn, from most recently registered to + * earliest registered, until one returns a service object. If none + * responds with a service object, a fallback ID is generated, and the + * process repeats until a service object is returned or until the ID + * has no further fallbacks.

+ * + *

In ICU 2.4, UObject (the base class of service instances) does + * not define a polymorphic clone function. ICUService uses clones to + * manage ownership. Thus, for now, ICUService defines an abstract + * method, cloneInstance, that clients must implement to create clones + * of the service instances. This may change in future releases of + * ICU.

+ * + *

ICUServiceFactories can be dynamically registered and + * unregistered with the service. When registered, an + * ICUServiceFactory is installed at the head of the factory list, and + * so gets 'first crack' at any keys or fallback keys. When + * unregistered, it is removed from the service and can no longer be + * located through it. Service objects generated by this factory and + * held by the client are unaffected.

+ * + *

If a service has variants (e.g., the different variants of + * BreakIterator) an ICUServiceFactory can use the prefix of the + * ICUServiceKey to determine the variant of a service to generate. + * If it does not support all variants, it can request + * previously-registered factories to handle the ones it does not + * support.

+ * + *

ICUService uses ICUServiceKeys to query factories and perform + * fallback. The ICUServiceKey defines the canonical form of the ID, + * and implements the fallback strategy. Custom ICUServiceKeys can be + * defined that parse complex IDs into components that + * ICUServiceFactories can more easily use. The ICUServiceKey can + * cache the results of this parsing to save repeated effort. + * ICUService provides convenience APIs that take UnicodeStrings and + * generate default ICUServiceKeys for use in querying.

+ * + *

ICUService provides API to get the list of IDs publicly + * supported by the service (although queries aren't restricted to + * this list). This list contains only 'simple' IDs, and not fully + * unique IDs. ICUServiceFactories are associated with each simple ID + * and the responsible factory can also return a human-readable + * localized version of the simple ID, for use in user interfaces. + * ICUService can also provide an array of the all the localized + * visible IDs and their corresponding internal IDs.

+ * + *

ICUService implements ICUNotifier, so that clients can register + * to receive notification when factories are added or removed from + * the service. ICUService provides a default EventListener + * subinterface, ServiceListener, which can be registered with the + * service. When the service changes, the ServiceListener's + * serviceChanged method is called with the service as the + * argument.

+ * + *

The ICUService API is both rich and generic, and it is expected + * that most implementations will statically 'wrap' ICUService to + * present a more appropriate API-- for example, to declare the type + * of the objects returned from get, to limit the factories that can + * be registered with the service, or to define their own listener + * interface with a custom callback method. They might also customize + * ICUService by overriding it, for example, to customize the + * ICUServiceKey and fallback strategy. ICULocaleService is a + * subclass of ICUService that uses Locale names as IDs and uses + * ICUServiceKeys that implement the standard resource bundle fallback + * strategy. Most clients will wish to subclass it instead of + * ICUService.

+ */ +class U_COMMON_API ICUService : public ICUNotifier { + protected: + /** + * Name useful for debugging. + */ + const UnicodeString name; + + private: + + /** + * Timestamp so iterators can be fail-fast. + */ + uint32_t timestamp; + + /** + * All the factories registered with this service. + */ + UVector* factories; + + /** + * The service cache. + */ + Hashtable* serviceCache; + + /** + * The ID cache. + */ + Hashtable* idCache; + + /** + * The name cache. + */ + DNCache* dnCache; + + /** + * Constructor. + */ + public: + /** + *

Construct a new ICUService.

+ */ + ICUService(); + + /** + *

Construct with a name (useful for debugging).

+ * + * @param name a name to use in debugging. + */ + ICUService(const UnicodeString& name); + + /** + *

Destructor.

+ */ + virtual ~ICUService(); + + /** + *

Return the name of this service. This will be the empty string if none was assigned. + * Returns result as a convenience.

+ * + * @param result an output parameter to contain the name of this service. + * @return the name of this service. + */ + UnicodeString& getName(UnicodeString& result) const; + + /** + *

Convenience override for get(ICUServiceKey&, UnicodeString*). This uses + * createKey to create a key for the provided descriptor.

+ * + * @param descriptor the descriptor. + * @param status the error code status. + * @return the service instance, or nullptr. + */ + UObject* get(const UnicodeString& descriptor, UErrorCode& status) const; + + /** + *

Convenience override for get(ICUServiceKey&, UnicodeString*). This uses + * createKey to create a key from the provided descriptor.

+ * + * @param descriptor the descriptor. + * @param actualReturn a pointer to a UnicodeString to hold the matched descriptor, or nullptr. + * @param status the error code status. + * @return the service instance, or nullptr. + */ + UObject* get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const; + + /** + *

Convenience override for get(ICUServiceKey&, UnicodeString*).

+ * + * @param key the key. + * @param status the error code status. + * @return the service instance, or nullptr. + */ + UObject* getKey(ICUServiceKey& key, UErrorCode& status) const; + + /** + *

Given a key, return a service object, and, if actualReturn + * is not nullptr, the descriptor with which it was found in the + * first element of actualReturn. If no service object matches + * this key, returns nullptr and leaves actualReturn unchanged.

+ * + *

This queries the cache using the key's descriptor, and if no + * object in the cache matches, tries the key on each + * registered factory, in order. If none generates a service + * object for the key, repeats the process with each fallback of + * the key, until either a factory returns a service object, or the key + * has no fallback. If no object is found, the result of handleDefault + * is returned.

+ * + *

Subclasses can override this method to further customize the + * result before returning it. + * + * @param key the key. + * @param actualReturn a pointer to a UnicodeString to hold the matched descriptor, or nullptr. + * @param status the error code status. + * @return the service instance, or nullptr. + */ + virtual UObject* getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const; + + /** + *

This version of getKey is only called by ICUServiceFactories within the scope + * of a previous getKey call, to determine what previously-registered factories would + * have returned. For details, see getKey(ICUServiceKey&, UErrorCode&). Subclasses + * should not call it directly, but call through one of the other get functions.

+ * + * @param key the key. + * @param actualReturn a pointer to a UnicodeString to hold the matched descriptor, or nullptr. + * @param factory the factory making the recursive call. + * @param status the error code status. + * @return the service instance, or nullptr. + */ + UObject* getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const; + + /** + *

Convenience override for getVisibleIDs(String) that passes null + * as the fallback, thus returning all visible IDs.

+ * + * @param result a vector to hold the returned IDs. + * @param status the error code status. + * @return the result vector. + */ + UVector& getVisibleIDs(UVector& result, UErrorCode& status) const; + + /** + *

Return a snapshot of the visible IDs for this service. This + * list will not change as ICUServiceFactories are added or removed, but the + * supported IDs will, so there is no guarantee that all and only + * the IDs in the returned list will be visible and supported by the + * service in subsequent calls.

+ * + *

The IDs are returned as pointers to UnicodeStrings. The + * caller owns the IDs. Previous contents of result are discarded before + * new elements, if any, are added.

+ * + *

matchID is passed to createKey to create a key. If the key + * is not nullptr, its isFallbackOf method is used to filter out IDs + * that don't match the key or have it as a fallback.

+ * + * @param result a vector to hold the returned IDs. + * @param matchID an ID used to filter the result, or nullptr if all IDs are desired. + * @param status the error code status. + * @return the result vector. + */ + UVector& getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const; + + /** + *

Convenience override for getDisplayName(const UnicodeString&, const Locale&, UnicodeString&) that + * uses the current default locale.

+ * + * @param id the ID for which to retrieve the localized displayName. + * @param result an output parameter to hold the display name. + * @return the modified result. + */ + UnicodeString& getDisplayName(const UnicodeString& id, UnicodeString& result) const; + + /** + *

Given a visible ID, return the display name in the requested locale. + * If there is no directly supported ID corresponding to this ID, result is + * set to bogus.

+ * + * @param id the ID for which to retrieve the localized displayName. + * @param result an output parameter to hold the display name. + * @param locale the locale in which to localize the ID. + * @return the modified result. + */ + UnicodeString& getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const; + + /** + *

Convenience override of getDisplayNames(const Locale&, const UnicodeString*) that + * uses the current default Locale as the locale and nullptr for + * the matchID.

+ * + * @param result a vector to hold the returned displayName/id StringPairs. + * @param status the error code status. + * @return the modified result vector. + */ + UVector& getDisplayNames(UVector& result, UErrorCode& status) const; + + /** + *

Convenience override of getDisplayNames(const Locale&, const UnicodeString*) that + * uses nullptr for the matchID.

+ * + * @param result a vector to hold the returned displayName/id StringPairs. + * @param locale the locale in which to localize the ID. + * @param status the error code status. + * @return the modified result vector. + */ + UVector& getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const; + + /** + *

Return a snapshot of the mapping from display names to visible + * IDs for this service. This set will not change as factories + * are added or removed, but the supported IDs will, so there is + * no guarantee that all and only the IDs in the returned map will + * be visible and supported by the service in subsequent calls, + * nor is there any guarantee that the current display names match + * those in the result.

+ * + *

The names are returned as pointers to StringPairs, which + * contain both the displayName and the corresponding ID. The + * caller owns the StringPairs. Previous contents of result are + * discarded before new elements, if any, are added.

+ * + *

matchID is passed to createKey to create a key. If the key + * is not nullptr, its isFallbackOf method is used to filter out IDs + * that don't match the key or have it as a fallback.

+ * + * @param result a vector to hold the returned displayName/id StringPairs. + * @param locale the locale in which to localize the ID. + * @param matchID an ID used to filter the result, or nullptr if all IDs are desired. + * @param status the error code status. + * @return the result vector. */ + UVector& getDisplayNames(UVector& result, + const Locale& locale, + const UnicodeString* matchID, + UErrorCode& status) const; + + /** + *

A convenience override of registerInstance(UObject*, const UnicodeString&, UBool) + * that defaults visible to true.

+ * + * @param objToAdopt the object to register and adopt. + * @param id the ID to assign to this object. + * @param status the error code status. + * @return a registry key that can be passed to unregister to unregister + * (and discard) this instance. + */ + URegistryKey registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status); + + /** + *

Register a service instance with the provided ID. The ID will be + * canonicalized. The canonicalized ID will be returned by + * getVisibleIDs if visible is true. The service instance will be adopted and + * must not be modified subsequent to this call.

+ * + *

This issues a serviceChanged notification to registered listeners.

+ * + *

This implementation wraps the object using + * createSimpleFactory, and calls registerFactory.

+ * + * @param objToAdopt the object to register and adopt. + * @param id the ID to assign to this object. + * @param visible true if getVisibleIDs is to return this ID. + * @param status the error code status. + * @return a registry key that can be passed to unregister() to unregister + * (and discard) this instance. + */ + virtual URegistryKey registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status); + + /** + *

Register an ICUServiceFactory. Returns a registry key that + * can be used to unregister the factory. The factory + * must not be modified subsequent to this call. The service owns + * all registered factories. In case of an error, the factory is + * deleted.

+ * + *

This issues a serviceChanged notification to registered listeners.

+ * + *

The default implementation accepts all factories.

+ * + * @param factoryToAdopt the factory to register and adopt. + * @param status the error code status. + * @return a registry key that can be passed to unregister to unregister + * (and discard) this factory. + */ + virtual URegistryKey registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status); + + /** + *

Unregister a factory using a registry key returned by + * registerInstance or registerFactory. After a successful call, + * the factory will be removed from the service factory list and + * deleted, and the key becomes invalid.

+ * + *

This issues a serviceChanged notification to registered + * listeners.

+ * + * @param rkey the registry key. + * @param status the error code status. + * @return true if the call successfully unregistered the factory. + */ + virtual UBool unregister(URegistryKey rkey, UErrorCode& status); + + /** + *

Reset the service to the default factories. The factory + * lock is acquired and then reInitializeFactories is called.

+ * + *

This issues a serviceChanged notification to registered listeners.

+ */ + virtual void reset(); + + /** + *

Return true if the service is in its default state.

+ * + *

The default implementation returns true if there are no + * factories registered.

+ */ + virtual UBool isDefault() const; + + /** + *

Create a key from an ID. If ID is nullptr, returns nullptr.

+ * + *

The default implementation creates an ICUServiceKey instance. + * Subclasses can override to define more useful keys appropriate + * to the factories they accept.

+ * + * @param a pointer to the ID for which to create a default ICUServiceKey. + * @param status the error code status. + * @return the ICUServiceKey corresponding to ID, or nullptr. + */ + virtual ICUServiceKey* createKey(const UnicodeString* id, UErrorCode& status) const; + + /** + *

Clone object so that caller can own the copy. In ICU2.4, UObject doesn't define + * clone, so we need an instance-aware method that knows how to do this. + * This is public so factories can call it, but should really be protected.

+ * + * @param instance the service instance to clone. + * @return a clone of the passed-in instance, or nullptr if cloning was unsuccessful. + */ + virtual UObject* cloneInstance(UObject* instance) const = 0; + + + /************************************************************************ + * Subclassing API + */ + + protected: + + /** + *

Create a factory that wraps a single service object. Called by registerInstance.

+ * + *

The default implementation returns an instance of SimpleFactory.

+ * + * @param instanceToAdopt the service instance to adopt. + * @param id the ID to assign to this service instance. + * @param visible if true, the ID will be visible. + * @param status the error code status. + * @return an instance of ICUServiceFactory that maps this instance to the provided ID. + */ + virtual ICUServiceFactory* createSimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status); + + /** + *

Reinitialize the factory list to its default state. After this call, isDefault() + * must return true.

+ * + *

This issues a serviceChanged notification to registered listeners.

+ * + *

The default implementation clears the factory list. + * Subclasses can override to provide other default initialization + * of the factory list. Subclasses must not call this method + * directly, since it must only be called while holding write + * access to the factory list.

+ */ + virtual void reInitializeFactories(); + + /** + *

Default handler for this service if no factory in the factory list + * handled the key passed to getKey.

+ * + *

The default implementation returns nullptr.

+ * + * @param key the key. + * @param actualReturn a pointer to a UnicodeString to hold the matched descriptor, or nullptr. + * @param status the error code status. + * @return the service instance, or nullptr. + */ + virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const; + + /** + *

Clear caches maintained by this service.

+ * + *

Subclasses can override if they implement additional caches + * that need to be cleared when the service changes. Subclasses + * should generally not call this method directly, as it must only + * be called while synchronized on the factory lock.

+ */ + virtual void clearCaches(); + + /** + *

Return true if the listener is accepted.

+ * + *

The default implementation accepts the listener if it is + * a ServiceListener. Subclasses can override this to accept + * different listeners.

+ * + * @param l the listener to test. + * @return true if the service accepts the listener. + */ + virtual UBool acceptsListener(const EventListener& l) const override; + + /** + *

Notify the listener of a service change.

+ * + *

The default implementation assumes a ServiceListener. + * If acceptsListener has been overridden to accept different + * listeners, this should be overridden as well.

+ * + * @param l the listener to notify. + */ + virtual void notifyListener(EventListener& l) const override; + + /************************************************************************ + * Utilities for subclasses. + */ + + /** + *

Clear only the service cache.

+ * + *

This can be called by subclasses when a change affects the service + * cache but not the ID caches, e.g., when the default locale changes + * the resolution of IDs also changes, requiring the cache to be + * flushed, but not the visible IDs themselves.

+ */ + void clearServiceCache(); + + /** + *

Return a map from visible IDs to factories. + * This must only be called when the mutex is held.

+ * + * @param status the error code status. + * @return a Hashtable containing mappings from visible + * IDs to factories. + */ + const Hashtable* getVisibleIDMap(UErrorCode& status) const; + + /** + *

Allow subclasses to read the time stamp.

+ * + * @return the timestamp. + */ + int32_t getTimestamp() const; + + /** + *

Return the number of registered factories.

+ * + * @return the number of factories registered at the time of the call. + */ + int32_t countFactories() const; + +private: + + friend class ::ICUServiceTest; // give tests access to countFactories. +}; + +U_NAMESPACE_END + + /* UCONFIG_NO_SERVICE */ +#endif + + /* ICUSERV_H */ +#endif + diff --git a/deps/icu-small/source/common/servlk.cpp b/deps/icu-small/source/common/servlk.cpp index 702180665952b4..e358733f2e03fb 100644 --- a/deps/icu-small/source/common/servlk.cpp +++ b/deps/icu-small/source/common/servlk.cpp @@ -1,188 +1,188 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** - ******************************************************************************* - * Copyright (C) 2001-2014, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - * - ******************************************************************************* - */ -#include "unicode/utypes.h" - -#if !UCONFIG_NO_SERVICE - -#include "unicode/resbund.h" -#include "uresimp.h" -#include "cmemory.h" -#include "servloc.h" -#include "ustrfmt.h" -#include "uhash.h" -#include "charstr.h" -#include "uassert.h" - -#define UNDERSCORE_CHAR ((UChar)0x005f) -#define AT_SIGN_CHAR ((UChar)64) -#define PERIOD_CHAR ((UChar)46) - -U_NAMESPACE_BEGIN - -LocaleKey* -LocaleKey::createWithCanonicalFallback(const UnicodeString* primaryID, - const UnicodeString* canonicalFallbackID, - UErrorCode& status) -{ - return LocaleKey::createWithCanonicalFallback(primaryID, canonicalFallbackID, KIND_ANY, status); -} - -LocaleKey* -LocaleKey::createWithCanonicalFallback(const UnicodeString* primaryID, - const UnicodeString* canonicalFallbackID, - int32_t kind, - UErrorCode& status) -{ - if (primaryID == NULL || U_FAILURE(status)) { - return NULL; - } - UnicodeString canonicalPrimaryID; - LocaleUtility::canonicalLocaleString(primaryID, canonicalPrimaryID); - return new LocaleKey(*primaryID, canonicalPrimaryID, canonicalFallbackID, kind); -} - -LocaleKey::LocaleKey(const UnicodeString& primaryID, - const UnicodeString& canonicalPrimaryID, - const UnicodeString* canonicalFallbackID, - int32_t kind) - : ICUServiceKey(primaryID) - , _kind(kind) - , _primaryID(canonicalPrimaryID) - , _fallbackID() - , _currentID() -{ - _fallbackID.setToBogus(); - if (_primaryID.length() != 0) { - if (canonicalFallbackID != NULL && _primaryID != *canonicalFallbackID) { - _fallbackID = *canonicalFallbackID; - } - } - - _currentID = _primaryID; -} - -LocaleKey::~LocaleKey() {} - -UnicodeString& -LocaleKey::prefix(UnicodeString& result) const { - if (_kind != KIND_ANY) { - UChar buffer[64]; - uprv_itou(buffer, 64, _kind, 10, 0); - UnicodeString temp(buffer); - result.append(temp); - } - return result; -} - -int32_t -LocaleKey::kind() const { - return _kind; -} - -UnicodeString& -LocaleKey::canonicalID(UnicodeString& result) const { - return result.append(_primaryID); -} - -UnicodeString& -LocaleKey::currentID(UnicodeString& result) const { - if (!_currentID.isBogus()) { - result.append(_currentID); - } - return result; -} - -UnicodeString& -LocaleKey::currentDescriptor(UnicodeString& result) const { - if (!_currentID.isBogus()) { - prefix(result).append(PREFIX_DELIMITER).append(_currentID); - } else { - result.setToBogus(); - } - return result; -} - -Locale& -LocaleKey::canonicalLocale(Locale& result) const { - return LocaleUtility::initLocaleFromName(_primaryID, result); -} - -Locale& -LocaleKey::currentLocale(Locale& result) const { - return LocaleUtility::initLocaleFromName(_currentID, result); -} - -UBool -LocaleKey::fallback() { - if (!_currentID.isBogus()) { - int x = _currentID.lastIndexOf(UNDERSCORE_CHAR); - if (x != -1) { - _currentID.remove(x); // truncate current or fallback, whichever we're pointing to - return true; - } - - if (!_fallbackID.isBogus()) { - _currentID = _fallbackID; - _fallbackID.setToBogus(); - return true; - } - - if (_currentID.length() > 0) { - _currentID.remove(0); // completely truncate - return true; - } - - _currentID.setToBogus(); - } - - return false; -} - -UBool -LocaleKey::isFallbackOf(const UnicodeString& id) const { - UnicodeString temp(id); - parseSuffix(temp); - return temp.indexOf(_primaryID) == 0 && - (temp.length() == _primaryID.length() || - temp.charAt(_primaryID.length()) == UNDERSCORE_CHAR); -} - -#ifdef SERVICE_DEBUG -UnicodeString& -LocaleKey::debug(UnicodeString& result) const -{ - ICUServiceKey::debug(result); - result.append((UnicodeString)" kind: "); - result.append(_kind); - result.append((UnicodeString)" primaryID: "); - result.append(_primaryID); - result.append((UnicodeString)" fallbackID: "); - result.append(_fallbackID); - result.append((UnicodeString)" currentID: "); - result.append(_currentID); - return result; -} - -UnicodeString& -LocaleKey::debugClass(UnicodeString& result) const -{ - return result.append((UnicodeString)"LocaleKey "); -} -#endif - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(LocaleKey) - -U_NAMESPACE_END - -/* !UCONFIG_NO_SERVICE */ -#endif - - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** + ******************************************************************************* + * Copyright (C) 2001-2014, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + * + ******************************************************************************* + */ +#include "unicode/utypes.h" + +#if !UCONFIG_NO_SERVICE + +#include "unicode/resbund.h" +#include "uresimp.h" +#include "cmemory.h" +#include "servloc.h" +#include "ustrfmt.h" +#include "uhash.h" +#include "charstr.h" +#include "uassert.h" + +#define UNDERSCORE_CHAR ((char16_t)0x005f) +#define AT_SIGN_CHAR ((char16_t)64) +#define PERIOD_CHAR ((char16_t)46) + +U_NAMESPACE_BEGIN + +LocaleKey* +LocaleKey::createWithCanonicalFallback(const UnicodeString* primaryID, + const UnicodeString* canonicalFallbackID, + UErrorCode& status) +{ + return LocaleKey::createWithCanonicalFallback(primaryID, canonicalFallbackID, KIND_ANY, status); +} + +LocaleKey* +LocaleKey::createWithCanonicalFallback(const UnicodeString* primaryID, + const UnicodeString* canonicalFallbackID, + int32_t kind, + UErrorCode& status) +{ + if (primaryID == nullptr || U_FAILURE(status)) { + return nullptr; + } + UnicodeString canonicalPrimaryID; + LocaleUtility::canonicalLocaleString(primaryID, canonicalPrimaryID); + return new LocaleKey(*primaryID, canonicalPrimaryID, canonicalFallbackID, kind); +} + +LocaleKey::LocaleKey(const UnicodeString& primaryID, + const UnicodeString& canonicalPrimaryID, + const UnicodeString* canonicalFallbackID, + int32_t kind) + : ICUServiceKey(primaryID) + , _kind(kind) + , _primaryID(canonicalPrimaryID) + , _fallbackID() + , _currentID() +{ + _fallbackID.setToBogus(); + if (_primaryID.length() != 0) { + if (canonicalFallbackID != nullptr && _primaryID != *canonicalFallbackID) { + _fallbackID = *canonicalFallbackID; + } + } + + _currentID = _primaryID; +} + +LocaleKey::~LocaleKey() {} + +UnicodeString& +LocaleKey::prefix(UnicodeString& result) const { + if (_kind != KIND_ANY) { + char16_t buffer[64]; + uprv_itou(buffer, 64, _kind, 10, 0); + UnicodeString temp(buffer); + result.append(temp); + } + return result; +} + +int32_t +LocaleKey::kind() const { + return _kind; +} + +UnicodeString& +LocaleKey::canonicalID(UnicodeString& result) const { + return result.append(_primaryID); +} + +UnicodeString& +LocaleKey::currentID(UnicodeString& result) const { + if (!_currentID.isBogus()) { + result.append(_currentID); + } + return result; +} + +UnicodeString& +LocaleKey::currentDescriptor(UnicodeString& result) const { + if (!_currentID.isBogus()) { + prefix(result).append(PREFIX_DELIMITER).append(_currentID); + } else { + result.setToBogus(); + } + return result; +} + +Locale& +LocaleKey::canonicalLocale(Locale& result) const { + return LocaleUtility::initLocaleFromName(_primaryID, result); +} + +Locale& +LocaleKey::currentLocale(Locale& result) const { + return LocaleUtility::initLocaleFromName(_currentID, result); +} + +UBool +LocaleKey::fallback() { + if (!_currentID.isBogus()) { + int x = _currentID.lastIndexOf(UNDERSCORE_CHAR); + if (x != -1) { + _currentID.remove(x); // truncate current or fallback, whichever we're pointing to + return true; + } + + if (!_fallbackID.isBogus()) { + _currentID = _fallbackID; + _fallbackID.setToBogus(); + return true; + } + + if (_currentID.length() > 0) { + _currentID.remove(0); // completely truncate + return true; + } + + _currentID.setToBogus(); + } + + return false; +} + +UBool +LocaleKey::isFallbackOf(const UnicodeString& id) const { + UnicodeString temp(id); + parseSuffix(temp); + return temp.indexOf(_primaryID) == 0 && + (temp.length() == _primaryID.length() || + temp.charAt(_primaryID.length()) == UNDERSCORE_CHAR); +} + +#ifdef SERVICE_DEBUG +UnicodeString& +LocaleKey::debug(UnicodeString& result) const +{ + ICUServiceKey::debug(result); + result.append((UnicodeString)" kind: "); + result.append(_kind); + result.append((UnicodeString)" primaryID: "); + result.append(_primaryID); + result.append((UnicodeString)" fallbackID: "); + result.append(_fallbackID); + result.append((UnicodeString)" currentID: "); + result.append(_currentID); + return result; +} + +UnicodeString& +LocaleKey::debugClass(UnicodeString& result) const +{ + return result.append((UnicodeString)"LocaleKey "); +} +#endif + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(LocaleKey) + +U_NAMESPACE_END + +/* !UCONFIG_NO_SERVICE */ +#endif + + diff --git a/deps/icu-small/source/common/servlkf.cpp b/deps/icu-small/source/common/servlkf.cpp index 7ccb0c72aa67fe..bd7a30c9047b97 100644 --- a/deps/icu-small/source/common/servlkf.cpp +++ b/deps/icu-small/source/common/servlkf.cpp @@ -1,152 +1,152 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** - ******************************************************************************* - * Copyright (C) 2001-2014, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - * - ******************************************************************************* - */ -#include "unicode/utypes.h" - -#if !UCONFIG_NO_SERVICE - -#include "unicode/resbund.h" -#include "uresimp.h" -#include "cmemory.h" -#include "servloc.h" -#include "ustrfmt.h" -#include "uhash.h" -#include "charstr.h" -#include "ucln_cmn.h" -#include "uassert.h" - -#define UNDERSCORE_CHAR ((UChar)0x005f) -#define AT_SIGN_CHAR ((UChar)64) -#define PERIOD_CHAR ((UChar)46) - - -U_NAMESPACE_BEGIN - -LocaleKeyFactory::LocaleKeyFactory(int32_t coverage) - : _name() - , _coverage(coverage) -{ -} - -LocaleKeyFactory::LocaleKeyFactory(int32_t coverage, const UnicodeString& name) - : _name(name) - , _coverage(coverage) -{ -} - -LocaleKeyFactory::~LocaleKeyFactory() { -} - -UObject* -LocaleKeyFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const { - if (handlesKey(key, status)) { - const LocaleKey& lkey = (const LocaleKey&)key; - int32_t kind = lkey.kind(); - Locale loc; - lkey.currentLocale(loc); - - return handleCreate(loc, kind, service, status); - } - return NULL; -} - -UBool -LocaleKeyFactory::handlesKey(const ICUServiceKey& key, UErrorCode& status) const { - const Hashtable* supported = getSupportedIDs(status); - if (supported) { - UnicodeString id; - key.currentID(id); - return supported->get(id) != NULL; - } - return false; -} - -void -LocaleKeyFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const { - const Hashtable* supported = getSupportedIDs(status); - if (supported) { - UBool visible = (_coverage & 0x1) == 0; - const UHashElement* elem = NULL; - int32_t pos = UHASH_FIRST; - while ((elem = supported->nextElement(pos)) != NULL) { - const UnicodeString& id = *((const UnicodeString*)elem->key.pointer); - if (!visible) { - result.remove(id); - } else { - result.put(id, (void*)this, status); // this is dummy non-void marker used for set semantics - if (U_FAILURE(status)) { - break; - } - } - } - } -} - -UnicodeString& -LocaleKeyFactory::getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const { - if ((_coverage & 0x1) == 0) { - //UErrorCode status = U_ZERO_ERROR; - // assume if this is called on us, we support some fallback of this id - // if (isSupportedID(id, status)) { - Locale loc; - LocaleUtility::initLocaleFromName(id, loc); - return loc.getDisplayName(locale, result); - // } - } - result.setToBogus(); - return result; -} - -UObject* -LocaleKeyFactory::handleCreate(const Locale& /* loc */, - int32_t /* kind */, - const ICUService* /* service */, - UErrorCode& /* status */) const { - return NULL; -} - -//UBool -//LocaleKeyFactory::isSupportedID(const UnicodeString& id, UErrorCode& status) const { -// const Hashtable* ids = getSupportedIDs(status); -// return ids && ids->get(id); -//} - -const Hashtable* -LocaleKeyFactory::getSupportedIDs(UErrorCode& /* status */) const { - return NULL; -} - -#ifdef SERVICE_DEBUG -UnicodeString& -LocaleKeyFactory::debug(UnicodeString& result) const -{ - debugClass(result); - result.append((UnicodeString)", name: "); - result.append(_name); - result.append((UnicodeString)", coverage: "); - result.append(_coverage); - return result; -} - -UnicodeString& -LocaleKeyFactory::debugClass(UnicodeString& result) const -{ - return result.append((UnicodeString)"LocaleKeyFactory"); -} -#endif - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(LocaleKeyFactory) - -U_NAMESPACE_END - -/* !UCONFIG_NO_SERVICE */ -#endif - - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** + ******************************************************************************* + * Copyright (C) 2001-2014, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + * + ******************************************************************************* + */ +#include "unicode/utypes.h" + +#if !UCONFIG_NO_SERVICE + +#include "unicode/resbund.h" +#include "uresimp.h" +#include "cmemory.h" +#include "servloc.h" +#include "ustrfmt.h" +#include "uhash.h" +#include "charstr.h" +#include "ucln_cmn.h" +#include "uassert.h" + +#define UNDERSCORE_CHAR ((char16_t)0x005f) +#define AT_SIGN_CHAR ((char16_t)64) +#define PERIOD_CHAR ((char16_t)46) + + +U_NAMESPACE_BEGIN + +LocaleKeyFactory::LocaleKeyFactory(int32_t coverage) + : _name() + , _coverage(coverage) +{ +} + +LocaleKeyFactory::LocaleKeyFactory(int32_t coverage, const UnicodeString& name) + : _name(name) + , _coverage(coverage) +{ +} + +LocaleKeyFactory::~LocaleKeyFactory() { +} + +UObject* +LocaleKeyFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const { + if (handlesKey(key, status)) { + const LocaleKey& lkey = static_cast(key); + int32_t kind = lkey.kind(); + Locale loc; + lkey.currentLocale(loc); + + return handleCreate(loc, kind, service, status); + } + return nullptr; +} + +UBool +LocaleKeyFactory::handlesKey(const ICUServiceKey& key, UErrorCode& status) const { + const Hashtable* supported = getSupportedIDs(status); + if (supported) { + UnicodeString id; + key.currentID(id); + return supported->get(id) != nullptr; + } + return false; +} + +void +LocaleKeyFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const { + const Hashtable* supported = getSupportedIDs(status); + if (supported) { + UBool visible = (_coverage & 0x1) == 0; + const UHashElement* elem = nullptr; + int32_t pos = UHASH_FIRST; + while ((elem = supported->nextElement(pos)) != nullptr) { + const UnicodeString& id = *((const UnicodeString*)elem->key.pointer); + if (!visible) { + result.remove(id); + } else { + result.put(id, (void*)this, status); // this is dummy non-void marker used for set semantics + if (U_FAILURE(status)) { + break; + } + } + } + } +} + +UnicodeString& +LocaleKeyFactory::getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const { + if ((_coverage & 0x1) == 0) { + //UErrorCode status = U_ZERO_ERROR; + // assume if this is called on us, we support some fallback of this id + // if (isSupportedID(id, status)) { + Locale loc; + LocaleUtility::initLocaleFromName(id, loc); + return loc.getDisplayName(locale, result); + // } + } + result.setToBogus(); + return result; +} + +UObject* +LocaleKeyFactory::handleCreate(const Locale& /* loc */, + int32_t /* kind */, + const ICUService* /* service */, + UErrorCode& /* status */) const { + return nullptr; +} + +//UBool +//LocaleKeyFactory::isSupportedID(const UnicodeString& id, UErrorCode& status) const { +// const Hashtable* ids = getSupportedIDs(status); +// return ids && ids->get(id); +//} + +const Hashtable* +LocaleKeyFactory::getSupportedIDs(UErrorCode& /* status */) const { + return nullptr; +} + +#ifdef SERVICE_DEBUG +UnicodeString& +LocaleKeyFactory::debug(UnicodeString& result) const +{ + debugClass(result); + result.append((UnicodeString)", name: "); + result.append(_name); + result.append((UnicodeString)", coverage: "); + result.append(_coverage); + return result; +} + +UnicodeString& +LocaleKeyFactory::debugClass(UnicodeString& result) const +{ + return result.append((UnicodeString)"LocaleKeyFactory"); +} +#endif + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(LocaleKeyFactory) + +U_NAMESPACE_END + +/* !UCONFIG_NO_SERVICE */ +#endif + + diff --git a/deps/icu-small/source/common/servloc.h b/deps/icu-small/source/common/servloc.h index 29c50a27d1a245..2eedd52fde1d70 100644 --- a/deps/icu-small/source/common/servloc.h +++ b/deps/icu-small/source/common/servloc.h @@ -1,551 +1,551 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** - ******************************************************************************* - * Copyright (C) 2001-2011, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - * - ******************************************************************************* - */ -#ifndef ICULSERV_H -#define ICULSERV_H - -#include "unicode/utypes.h" - -#if UCONFIG_NO_SERVICE - -U_NAMESPACE_BEGIN - -/* - * Allow the declaration of APIs with pointers to ICUService - * even when service is removed from the build. - */ -class ICULocaleService; - -U_NAMESPACE_END - -#else - -#include "unicode/unistr.h" -#include "unicode/locid.h" -#include "unicode/strenum.h" - -#include "hash.h" -#include "uvector.h" - -#include "serv.h" -#include "locutil.h" - -U_NAMESPACE_BEGIN - -class ICULocaleService; - -class LocaleKey; -class LocaleKeyFactory; -class SimpleLocaleKeyFactory; -class ServiceListener; - -/* - ****************************************************************** - */ - -/** - * A subclass of Key that implements a locale fallback mechanism. - * The first locale to search for is the locale provided by the - * client, and the fallback locale to search for is the current - * default locale. If a prefix is present, the currentDescriptor - * includes it before the locale proper, separated by "/". This - * is the default key instantiated by ICULocaleService.

- * - *

Canonicalization adjusts the locale string so that the - * section before the first understore is in lower case, and the rest - * is in upper case, with no trailing underscores.

- */ - -class U_COMMON_API LocaleKey : public ICUServiceKey { - private: - int32_t _kind; - UnicodeString _primaryID; - UnicodeString _fallbackID; - UnicodeString _currentID; - - public: - enum { - KIND_ANY = -1 - }; - - /** - * Create a LocaleKey with canonical primary and fallback IDs. - */ - static LocaleKey* createWithCanonicalFallback(const UnicodeString* primaryID, - const UnicodeString* canonicalFallbackID, - UErrorCode& status); - - /** - * Create a LocaleKey with canonical primary and fallback IDs. - */ - static LocaleKey* createWithCanonicalFallback(const UnicodeString* primaryID, - const UnicodeString* canonicalFallbackID, - int32_t kind, - UErrorCode& status); - - protected: - /** - * PrimaryID is the user's requested locale string, - * canonicalPrimaryID is this string in canonical form, - * fallbackID is the current default locale's string in - * canonical form. - */ - LocaleKey(const UnicodeString& primaryID, - const UnicodeString& canonicalPrimaryID, - const UnicodeString* canonicalFallbackID, - int32_t kind); - - public: - /** - * Append the prefix associated with the kind, or nothing if the kind is KIND_ANY. - */ - virtual UnicodeString& prefix(UnicodeString& result) const override; - - /** - * Return the kind code associated with this key. - */ - virtual int32_t kind() const; - - /** - * Return the canonicalID. - */ - virtual UnicodeString& canonicalID(UnicodeString& result) const override; - - /** - * Return the currentID. - */ - virtual UnicodeString& currentID(UnicodeString& result) const override; - - /** - * Return the (canonical) current descriptor, or null if no current id. - */ - virtual UnicodeString& currentDescriptor(UnicodeString& result) const override; - - /** - * Convenience method to return the locale corresponding to the (canonical) original ID. - */ - virtual Locale& canonicalLocale(Locale& result) const; - - /** - * Convenience method to return the locale corresponding to the (canonical) current ID. - */ - virtual Locale& currentLocale(Locale& result) const; - - /** - * If the key has a fallback, modify the key and return true, - * otherwise return false.

- * - *

First falls back through the primary ID, then through - * the fallbackID. The final fallback is the empty string, - * unless the primary id was the empty string, in which case - * there is no fallback. - */ - virtual UBool fallback() override; - - /** - * Return true if a key created from id matches, or would eventually - * fallback to match, the canonical ID of this key. - */ - virtual UBool isFallbackOf(const UnicodeString& id) const override; - - public: - /** - * UObject boilerplate. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - virtual UClassID getDynamicClassID() const override; - - /** - * Destructor. - */ - virtual ~LocaleKey(); - -#ifdef SERVICE_DEBUG - public: - virtual UnicodeString& debug(UnicodeString& result) const; - virtual UnicodeString& debugClass(UnicodeString& result) const; -#endif - -}; - -/* - ****************************************************************** - */ - -/** - * A subclass of ICUServiceFactory that uses LocaleKeys, and is able to - * 'cover' more specific locales with more general locales that it - * supports. - * - *

Coverage may be either of the values VISIBLE or INVISIBLE. - * - *

'Visible' indicates that the specific locale(s) supported by - * the factory are registered in getSupportedIDs, 'Invisible' - * indicates that they are not. - * - *

Localization of visible ids is handled - * by the handling factory, regardless of kind. - */ -class U_COMMON_API LocaleKeyFactory : public ICUServiceFactory { -protected: - const UnicodeString _name; - const int32_t _coverage; - -public: - enum { - /** - * Coverage value indicating that the factory makes - * its locales visible, and does not cover more specific - * locales. - */ - VISIBLE = 0, - - /** - * Coverage value indicating that the factory does not make - * its locales visible, and does not cover more specific - * locales. - */ - INVISIBLE = 1 - }; - - /** - * Destructor. - */ - virtual ~LocaleKeyFactory(); - -protected: - /** - * Constructor used by subclasses. - */ - LocaleKeyFactory(int32_t coverage); - - /** - * Constructor used by subclasses. - */ - LocaleKeyFactory(int32_t coverage, const UnicodeString& name); - - /** - * Implement superclass abstract method. This checks the currentID of - * the key against the supported IDs, and passes the canonicalLocale and - * kind off to handleCreate (which subclasses must implement). - */ -public: - virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const override; - -protected: - virtual UBool handlesKey(const ICUServiceKey& key, UErrorCode& status) const; - -public: - /** - * Override of superclass method. This adjusts the result based - * on the coverage rule for this factory. - */ - virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const override; - - /** - * Return a localized name for the locale represented by id. - */ - virtual UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const override; - -protected: - /** - * Utility method used by create(ICUServiceKey, ICUService). Subclasses can implement - * this instead of create. The default returns NULL. - */ - virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* service, UErrorCode& status) const; - - /** - * Return true if this id is one the factory supports (visible or - * otherwise). - */ - // virtual UBool isSupportedID(const UnicodeString& id, UErrorCode& status) const; - - /** - * Return the set of ids that this factory supports (visible or - * otherwise). This can be called often and might need to be - * cached if it is expensive to create. - */ - virtual const Hashtable* getSupportedIDs(UErrorCode& status) const; - -public: - /** - * UObject boilerplate. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - virtual UClassID getDynamicClassID() const override; - -#ifdef SERVICE_DEBUG - public: - virtual UnicodeString& debug(UnicodeString& result) const; - virtual UnicodeString& debugClass(UnicodeString& result) const; -#endif - -}; - -/* - ****************************************************************** - */ - -/** - * A LocaleKeyFactory that just returns a single object for a kind/locale. - */ - -class U_COMMON_API SimpleLocaleKeyFactory : public LocaleKeyFactory { - private: - UObject* _obj; - UnicodeString _id; - const int32_t _kind; - - public: - SimpleLocaleKeyFactory(UObject* objToAdopt, - const UnicodeString& locale, - int32_t kind, - int32_t coverage); - - SimpleLocaleKeyFactory(UObject* objToAdopt, - const Locale& locale, - int32_t kind, - int32_t coverage); - - /** - * Destructor. - */ - virtual ~SimpleLocaleKeyFactory(); - - /** - * Override of superclass method. Returns the service object if kind/locale match. Service is not used. - */ - virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const override; - - /** - * Override of superclass method. This adjusts the result based - * on the coverage rule for this factory. - */ - virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const override; - - protected: - /** - * Return true if this id is equal to the locale name. - */ - //virtual UBool isSupportedID(const UnicodeString& id, UErrorCode& status) const; - - -public: - /** - * UObject boilerplate. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - virtual UClassID getDynamicClassID() const override; - -#ifdef SERVICE_DEBUG - public: - virtual UnicodeString& debug(UnicodeString& result) const; - virtual UnicodeString& debugClass(UnicodeString& result) const; -#endif - -}; - -/* - ****************************************************************** - */ - -/** - * A LocaleKeyFactory that creates a service based on the ICU locale data. - * This is a base class for most ICU factories. Subclasses instantiate it - * with a constructor that takes a bundle name, which determines the supported - * IDs. Subclasses then override handleCreate to create the actual service - * object. The default implementation returns a resource bundle. - */ -class U_COMMON_API ICUResourceBundleFactory : public LocaleKeyFactory -{ - protected: - UnicodeString _bundleName; - - public: - /** - * Convenience constructor that uses the main ICU bundle name. - */ - ICUResourceBundleFactory(); - - /** - * A service factory based on ICU resource data in resources with - * the given name. This should be a 'path' that can be passed to - * ures_openAvailableLocales, such as U_ICUDATA or U_ICUDATA_COLL. - * The empty string is equivalent to U_ICUDATA. - */ - ICUResourceBundleFactory(const UnicodeString& bundleName); - - /** - * Destructor - */ - virtual ~ICUResourceBundleFactory(); - -protected: - /** - * Return the supported IDs. This is the set of all locale names in ICULocaleData. - */ - virtual const Hashtable* getSupportedIDs(UErrorCode& status) const override; - - /** - * Create the service. The default implementation returns the resource bundle - * for the locale, ignoring kind, and service. - */ - virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* service, UErrorCode& status) const override; - -public: - /** - * UObject boilerplate. - */ - static UClassID U_EXPORT2 getStaticClassID(); - virtual UClassID getDynamicClassID() const override; - - -#ifdef SERVICE_DEBUG - public: - virtual UnicodeString& debug(UnicodeString& result) const; - virtual UnicodeString& debugClass(UnicodeString& result) const; -#endif - -}; - -/* - ****************************************************************** - */ - -class U_COMMON_API ICULocaleService : public ICUService -{ - private: - Locale fallbackLocale; - UnicodeString fallbackLocaleName; - - public: - /** - * Construct an ICULocaleService. - */ - ICULocaleService(); - - /** - * Construct an ICULocaleService with a name (useful for debugging). - */ - ICULocaleService(const UnicodeString& name); - - /** - * Destructor. - */ - virtual ~ICULocaleService(); - -#if 0 - // redeclare because of overload resolution rules? - // no, causes ambiguities since both UnicodeString and Locale have constructors that take a const char* - // need some compiler flag to remove warnings - UObject* get(const UnicodeString& descriptor, UErrorCode& status) const { - return ICUService::get(descriptor, status); - } - - UObject* get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const { - return ICUService::get(descriptor, actualReturn, status); - } -#endif - - /** - * Convenience override for callers using locales. This calls - * get(Locale, int, Locale[]) with KIND_ANY for kind and null for - * actualReturn. - */ - UObject* get(const Locale& locale, UErrorCode& status) const; - - /** - * Convenience override for callers using locales. This calls - * get(Locale, int, Locale[]) with a null actualReturn. - */ - UObject* get(const Locale& locale, int32_t kind, UErrorCode& status) const; - - /** - * Convenience override for callers using locales. This calls - * get(Locale, String, Locale[]) with a null kind. - */ - UObject* get(const Locale& locale, Locale* actualReturn, UErrorCode& status) const; - - /** - * Convenience override for callers using locales. This uses - * createKey(Locale.toString(), kind) to create a key, calls getKey, and then - * if actualReturn is not null, returns the actualResult from - * getKey (stripping any prefix) into a Locale. - */ - UObject* get(const Locale& locale, int32_t kind, Locale* actualReturn, UErrorCode& status) const; - - /** - * Convenience override for callers using locales. This calls - * registerObject(Object, Locale, int32_t kind, int coverage) - * passing KIND_ANY for the kind, and VISIBLE for the coverage. - */ - virtual URegistryKey registerInstance(UObject* objToAdopt, const Locale& locale, UErrorCode& status); - - /** - * Convenience function for callers using locales. This calls - * registerObject(Object, Locale, int kind, int coverage) - * passing VISIBLE for the coverage. - */ - virtual URegistryKey registerInstance(UObject* objToAdopt, const Locale& locale, int32_t kind, UErrorCode& status); - - /** - * Convenience function for callers using locales. This instantiates - * a SimpleLocaleKeyFactory, and registers the factory. - */ - virtual URegistryKey registerInstance(UObject* objToAdopt, const Locale& locale, int32_t kind, int32_t coverage, UErrorCode& status); - - - /** - * (Stop compiler from complaining about hidden overrides.) - * Since both UnicodeString and Locale have constructors that take const char*, adding a public - * method that takes UnicodeString causes ambiguity at call sites that use const char*. - * We really need a flag that is understood by all compilers that will suppress the warning about - * hidden overrides. - */ - virtual URegistryKey registerInstance(UObject* objToAdopt, const UnicodeString& locale, UBool visible, UErrorCode& status) override; - - /** - * Convenience method for callers using locales. This returns the standard - * service ID enumeration. - */ - virtual StringEnumeration* getAvailableLocales(void) const; - - protected: - - /** - * Return the name of the current fallback locale. If it has changed since this was - * last accessed, the service cache is cleared. - */ - const UnicodeString& validateFallbackLocale() const; - - /** - * Override superclass createKey method. - */ - virtual ICUServiceKey* createKey(const UnicodeString* id, UErrorCode& status) const override; - - /** - * Additional createKey that takes a kind. - */ - virtual ICUServiceKey* createKey(const UnicodeString* id, int32_t kind, UErrorCode& status) const; - - friend class ServiceEnumeration; -}; - -U_NAMESPACE_END - - /* UCONFIG_NO_SERVICE */ -#endif - - /* ICULSERV_H */ -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** + ******************************************************************************* + * Copyright (C) 2001-2011, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + * + ******************************************************************************* + */ +#ifndef ICULSERV_H +#define ICULSERV_H + +#include "unicode/utypes.h" + +#if UCONFIG_NO_SERVICE + +U_NAMESPACE_BEGIN + +/* + * Allow the declaration of APIs with pointers to ICUService + * even when service is removed from the build. + */ +class ICULocaleService; + +U_NAMESPACE_END + +#else + +#include "unicode/unistr.h" +#include "unicode/locid.h" +#include "unicode/strenum.h" + +#include "hash.h" +#include "uvector.h" + +#include "serv.h" +#include "locutil.h" + +U_NAMESPACE_BEGIN + +class ICULocaleService; + +class LocaleKey; +class LocaleKeyFactory; +class SimpleLocaleKeyFactory; +class ServiceListener; + +/* + ****************************************************************** + */ + +/** + * A subclass of Key that implements a locale fallback mechanism. + * The first locale to search for is the locale provided by the + * client, and the fallback locale to search for is the current + * default locale. If a prefix is present, the currentDescriptor + * includes it before the locale proper, separated by "/". This + * is the default key instantiated by ICULocaleService.

+ * + *

Canonicalization adjusts the locale string so that the + * section before the first understore is in lower case, and the rest + * is in upper case, with no trailing underscores.

+ */ + +class U_COMMON_API LocaleKey : public ICUServiceKey { + private: + int32_t _kind; + UnicodeString _primaryID; + UnicodeString _fallbackID; + UnicodeString _currentID; + + public: + enum { + KIND_ANY = -1 + }; + + /** + * Create a LocaleKey with canonical primary and fallback IDs. + */ + static LocaleKey* createWithCanonicalFallback(const UnicodeString* primaryID, + const UnicodeString* canonicalFallbackID, + UErrorCode& status); + + /** + * Create a LocaleKey with canonical primary and fallback IDs. + */ + static LocaleKey* createWithCanonicalFallback(const UnicodeString* primaryID, + const UnicodeString* canonicalFallbackID, + int32_t kind, + UErrorCode& status); + + protected: + /** + * PrimaryID is the user's requested locale string, + * canonicalPrimaryID is this string in canonical form, + * fallbackID is the current default locale's string in + * canonical form. + */ + LocaleKey(const UnicodeString& primaryID, + const UnicodeString& canonicalPrimaryID, + const UnicodeString* canonicalFallbackID, + int32_t kind); + + public: + /** + * Append the prefix associated with the kind, or nothing if the kind is KIND_ANY. + */ + virtual UnicodeString& prefix(UnicodeString& result) const override; + + /** + * Return the kind code associated with this key. + */ + virtual int32_t kind() const; + + /** + * Return the canonicalID. + */ + virtual UnicodeString& canonicalID(UnicodeString& result) const override; + + /** + * Return the currentID. + */ + virtual UnicodeString& currentID(UnicodeString& result) const override; + + /** + * Return the (canonical) current descriptor, or null if no current id. + */ + virtual UnicodeString& currentDescriptor(UnicodeString& result) const override; + + /** + * Convenience method to return the locale corresponding to the (canonical) original ID. + */ + virtual Locale& canonicalLocale(Locale& result) const; + + /** + * Convenience method to return the locale corresponding to the (canonical) current ID. + */ + virtual Locale& currentLocale(Locale& result) const; + + /** + * If the key has a fallback, modify the key and return true, + * otherwise return false.

+ * + *

First falls back through the primary ID, then through + * the fallbackID. The final fallback is the empty string, + * unless the primary id was the empty string, in which case + * there is no fallback. + */ + virtual UBool fallback() override; + + /** + * Return true if a key created from id matches, or would eventually + * fallback to match, the canonical ID of this key. + */ + virtual UBool isFallbackOf(const UnicodeString& id) const override; + + public: + /** + * UObject boilerplate. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + virtual UClassID getDynamicClassID() const override; + + /** + * Destructor. + */ + virtual ~LocaleKey(); + +#ifdef SERVICE_DEBUG + public: + virtual UnicodeString& debug(UnicodeString& result) const; + virtual UnicodeString& debugClass(UnicodeString& result) const; +#endif + +}; + +/* + ****************************************************************** + */ + +/** + * A subclass of ICUServiceFactory that uses LocaleKeys, and is able to + * 'cover' more specific locales with more general locales that it + * supports. + * + *

Coverage may be either of the values VISIBLE or INVISIBLE. + * + *

'Visible' indicates that the specific locale(s) supported by + * the factory are registered in getSupportedIDs, 'Invisible' + * indicates that they are not. + * + *

Localization of visible ids is handled + * by the handling factory, regardless of kind. + */ +class U_COMMON_API LocaleKeyFactory : public ICUServiceFactory { +protected: + const UnicodeString _name; + const int32_t _coverage; + +public: + enum { + /** + * Coverage value indicating that the factory makes + * its locales visible, and does not cover more specific + * locales. + */ + VISIBLE = 0, + + /** + * Coverage value indicating that the factory does not make + * its locales visible, and does not cover more specific + * locales. + */ + INVISIBLE = 1 + }; + + /** + * Destructor. + */ + virtual ~LocaleKeyFactory(); + +protected: + /** + * Constructor used by subclasses. + */ + LocaleKeyFactory(int32_t coverage); + + /** + * Constructor used by subclasses. + */ + LocaleKeyFactory(int32_t coverage, const UnicodeString& name); + + /** + * Implement superclass abstract method. This checks the currentID of + * the key against the supported IDs, and passes the canonicalLocale and + * kind off to handleCreate (which subclasses must implement). + */ +public: + virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const override; + +protected: + virtual UBool handlesKey(const ICUServiceKey& key, UErrorCode& status) const; + +public: + /** + * Override of superclass method. This adjusts the result based + * on the coverage rule for this factory. + */ + virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const override; + + /** + * Return a localized name for the locale represented by id. + */ + virtual UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const override; + +protected: + /** + * Utility method used by create(ICUServiceKey, ICUService). Subclasses can implement + * this instead of create. The default returns nullptr. + */ + virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* service, UErrorCode& status) const; + + /** + * Return true if this id is one the factory supports (visible or + * otherwise). + */ + // virtual UBool isSupportedID(const UnicodeString& id, UErrorCode& status) const; + + /** + * Return the set of ids that this factory supports (visible or + * otherwise). This can be called often and might need to be + * cached if it is expensive to create. + */ + virtual const Hashtable* getSupportedIDs(UErrorCode& status) const; + +public: + /** + * UObject boilerplate. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + virtual UClassID getDynamicClassID() const override; + +#ifdef SERVICE_DEBUG + public: + virtual UnicodeString& debug(UnicodeString& result) const; + virtual UnicodeString& debugClass(UnicodeString& result) const; +#endif + +}; + +/* + ****************************************************************** + */ + +/** + * A LocaleKeyFactory that just returns a single object for a kind/locale. + */ + +class U_COMMON_API SimpleLocaleKeyFactory : public LocaleKeyFactory { + private: + UObject* _obj; + UnicodeString _id; + const int32_t _kind; + + public: + SimpleLocaleKeyFactory(UObject* objToAdopt, + const UnicodeString& locale, + int32_t kind, + int32_t coverage); + + SimpleLocaleKeyFactory(UObject* objToAdopt, + const Locale& locale, + int32_t kind, + int32_t coverage); + + /** + * Destructor. + */ + virtual ~SimpleLocaleKeyFactory(); + + /** + * Override of superclass method. Returns the service object if kind/locale match. Service is not used. + */ + virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const override; + + /** + * Override of superclass method. This adjusts the result based + * on the coverage rule for this factory. + */ + virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const override; + + protected: + /** + * Return true if this id is equal to the locale name. + */ + //virtual UBool isSupportedID(const UnicodeString& id, UErrorCode& status) const; + + +public: + /** + * UObject boilerplate. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + virtual UClassID getDynamicClassID() const override; + +#ifdef SERVICE_DEBUG + public: + virtual UnicodeString& debug(UnicodeString& result) const; + virtual UnicodeString& debugClass(UnicodeString& result) const; +#endif + +}; + +/* + ****************************************************************** + */ + +/** + * A LocaleKeyFactory that creates a service based on the ICU locale data. + * This is a base class for most ICU factories. Subclasses instantiate it + * with a constructor that takes a bundle name, which determines the supported + * IDs. Subclasses then override handleCreate to create the actual service + * object. The default implementation returns a resource bundle. + */ +class U_COMMON_API ICUResourceBundleFactory : public LocaleKeyFactory +{ + protected: + UnicodeString _bundleName; + + public: + /** + * Convenience constructor that uses the main ICU bundle name. + */ + ICUResourceBundleFactory(); + + /** + * A service factory based on ICU resource data in resources with + * the given name. This should be a 'path' that can be passed to + * ures_openAvailableLocales, such as U_ICUDATA or U_ICUDATA_COLL. + * The empty string is equivalent to U_ICUDATA. + */ + ICUResourceBundleFactory(const UnicodeString& bundleName); + + /** + * Destructor + */ + virtual ~ICUResourceBundleFactory(); + +protected: + /** + * Return the supported IDs. This is the set of all locale names in ICULocaleData. + */ + virtual const Hashtable* getSupportedIDs(UErrorCode& status) const override; + + /** + * Create the service. The default implementation returns the resource bundle + * for the locale, ignoring kind, and service. + */ + virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* service, UErrorCode& status) const override; + +public: + /** + * UObject boilerplate. + */ + static UClassID U_EXPORT2 getStaticClassID(); + virtual UClassID getDynamicClassID() const override; + + +#ifdef SERVICE_DEBUG + public: + virtual UnicodeString& debug(UnicodeString& result) const; + virtual UnicodeString& debugClass(UnicodeString& result) const; +#endif + +}; + +/* + ****************************************************************** + */ + +class U_COMMON_API ICULocaleService : public ICUService +{ + private: + Locale fallbackLocale; + UnicodeString fallbackLocaleName; + + public: + /** + * Construct an ICULocaleService. + */ + ICULocaleService(); + + /** + * Construct an ICULocaleService with a name (useful for debugging). + */ + ICULocaleService(const UnicodeString& name); + + /** + * Destructor. + */ + virtual ~ICULocaleService(); + +#if 0 + // redeclare because of overload resolution rules? + // no, causes ambiguities since both UnicodeString and Locale have constructors that take a const char* + // need some compiler flag to remove warnings + UObject* get(const UnicodeString& descriptor, UErrorCode& status) const { + return ICUService::get(descriptor, status); + } + + UObject* get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const { + return ICUService::get(descriptor, actualReturn, status); + } +#endif + + /** + * Convenience override for callers using locales. This calls + * get(Locale, int, Locale[]) with KIND_ANY for kind and null for + * actualReturn. + */ + UObject* get(const Locale& locale, UErrorCode& status) const; + + /** + * Convenience override for callers using locales. This calls + * get(Locale, int, Locale[]) with a null actualReturn. + */ + UObject* get(const Locale& locale, int32_t kind, UErrorCode& status) const; + + /** + * Convenience override for callers using locales. This calls + * get(Locale, String, Locale[]) with a null kind. + */ + UObject* get(const Locale& locale, Locale* actualReturn, UErrorCode& status) const; + + /** + * Convenience override for callers using locales. This uses + * createKey(Locale.toString(), kind) to create a key, calls getKey, and then + * if actualReturn is not null, returns the actualResult from + * getKey (stripping any prefix) into a Locale. + */ + UObject* get(const Locale& locale, int32_t kind, Locale* actualReturn, UErrorCode& status) const; + + /** + * Convenience override for callers using locales. This calls + * registerObject(Object, Locale, int32_t kind, int coverage) + * passing KIND_ANY for the kind, and VISIBLE for the coverage. + */ + virtual URegistryKey registerInstance(UObject* objToAdopt, const Locale& locale, UErrorCode& status); + + /** + * Convenience function for callers using locales. This calls + * registerObject(Object, Locale, int kind, int coverage) + * passing VISIBLE for the coverage. + */ + virtual URegistryKey registerInstance(UObject* objToAdopt, const Locale& locale, int32_t kind, UErrorCode& status); + + /** + * Convenience function for callers using locales. This instantiates + * a SimpleLocaleKeyFactory, and registers the factory. + */ + virtual URegistryKey registerInstance(UObject* objToAdopt, const Locale& locale, int32_t kind, int32_t coverage, UErrorCode& status); + + + /** + * (Stop compiler from complaining about hidden overrides.) + * Since both UnicodeString and Locale have constructors that take const char*, adding a public + * method that takes UnicodeString causes ambiguity at call sites that use const char*. + * We really need a flag that is understood by all compilers that will suppress the warning about + * hidden overrides. + */ + virtual URegistryKey registerInstance(UObject* objToAdopt, const UnicodeString& locale, UBool visible, UErrorCode& status) override; + + /** + * Convenience method for callers using locales. This returns the standard + * service ID enumeration. + */ + virtual StringEnumeration* getAvailableLocales() const; + + protected: + + /** + * Return the name of the current fallback locale. If it has changed since this was + * last accessed, the service cache is cleared. + */ + const UnicodeString& validateFallbackLocale() const; + + /** + * Override superclass createKey method. + */ + virtual ICUServiceKey* createKey(const UnicodeString* id, UErrorCode& status) const override; + + /** + * Additional createKey that takes a kind. + */ + virtual ICUServiceKey* createKey(const UnicodeString* id, int32_t kind, UErrorCode& status) const; + + friend class ServiceEnumeration; +}; + +U_NAMESPACE_END + + /* UCONFIG_NO_SERVICE */ +#endif + + /* ICULSERV_H */ +#endif + diff --git a/deps/icu-small/source/common/servls.cpp b/deps/icu-small/source/common/servls.cpp index 19481122efa52e..a9b69291b047a7 100644 --- a/deps/icu-small/source/common/servls.cpp +++ b/deps/icu-small/source/common/servls.cpp @@ -1,296 +1,296 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** - ******************************************************************************* - * Copyright (C) 2001-2014, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - * - ******************************************************************************* - */ -#include "unicode/utypes.h" - -#if !UCONFIG_NO_SERVICE - -#include "unicode/resbund.h" -#include "uresimp.h" -#include "cmemory.h" -#include "servloc.h" -#include "ustrfmt.h" -#include "charstr.h" -#include "uassert.h" - -#define UNDERSCORE_CHAR ((UChar)0x005f) -#define AT_SIGN_CHAR ((UChar)64) -#define PERIOD_CHAR ((UChar)46) - -U_NAMESPACE_BEGIN - -ICULocaleService::ICULocaleService() - : fallbackLocale(Locale::getDefault()) -{ -} - -ICULocaleService::ICULocaleService(const UnicodeString& dname) - : ICUService(dname) - , fallbackLocale(Locale::getDefault()) -{ -} - -ICULocaleService::~ICULocaleService() -{ -} - -UObject* -ICULocaleService::get(const Locale& locale, UErrorCode& status) const -{ - return get(locale, LocaleKey::KIND_ANY, NULL, status); -} - -UObject* -ICULocaleService::get(const Locale& locale, int32_t kind, UErrorCode& status) const -{ - return get(locale, kind, NULL, status); -} - -UObject* -ICULocaleService::get(const Locale& locale, Locale* actualReturn, UErrorCode& status) const -{ - return get(locale, LocaleKey::KIND_ANY, actualReturn, status); -} - -UObject* -ICULocaleService::get(const Locale& locale, int32_t kind, Locale* actualReturn, UErrorCode& status) const -{ - UObject* result = NULL; - if (U_FAILURE(status)) { - return result; - } - - UnicodeString locName(locale.getName(), -1, US_INV); - if (locName.isBogus()) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - ICUServiceKey* key = createKey(&locName, kind, status); - if (key) { - if (actualReturn == NULL) { - result = getKey(*key, status); - } else { - UnicodeString temp; - result = getKey(*key, &temp, status); - - if (result != NULL) { - key->parseSuffix(temp); - LocaleUtility::initLocaleFromName(temp, *actualReturn); - } - } - delete key; - } - } - return result; -} - - -URegistryKey -ICULocaleService::registerInstance(UObject* objToAdopt, const UnicodeString& locale, - UBool visible, UErrorCode& status) -{ - Locale loc; - LocaleUtility::initLocaleFromName(locale, loc); - return registerInstance(objToAdopt, loc, LocaleKey::KIND_ANY, - visible ? LocaleKeyFactory::VISIBLE : LocaleKeyFactory::INVISIBLE, status); -} - -URegistryKey -ICULocaleService::registerInstance(UObject* objToAdopt, const Locale& locale, UErrorCode& status) -{ - return registerInstance(objToAdopt, locale, LocaleKey::KIND_ANY, LocaleKeyFactory::VISIBLE, status); -} - -URegistryKey -ICULocaleService::registerInstance(UObject* objToAdopt, const Locale& locale, int32_t kind, UErrorCode& status) -{ - return registerInstance(objToAdopt, locale, kind, LocaleKeyFactory::VISIBLE, status); -} - -URegistryKey -ICULocaleService::registerInstance(UObject* objToAdopt, const Locale& locale, int32_t kind, int32_t coverage, UErrorCode& status) -{ - ICUServiceFactory * factory = new SimpleLocaleKeyFactory(objToAdopt, locale, kind, coverage); - if (factory != NULL) { - return registerFactory(factory, status); - } - delete objToAdopt; - return NULL; -} - -#if 0 -URegistryKey -ICULocaleService::registerInstance(UObject* objToAdopt, const UnicodeString& locale, UErrorCode& status) -{ - return registerInstance(objToAdopt, locale, LocaleKey::KIND_ANY, LocaleKeyFactory::VISIBLE, status); -} - -URegistryKey -ICULocaleService::registerInstance(UObject* objToAdopt, const UnicodeString& locale, UBool visible, UErrorCode& status) -{ - return registerInstance(objToAdopt, locale, LocaleKey::KIND_ANY, - visible ? LocaleKeyFactory::VISIBLE : LocaleKeyFactory::INVISIBLE, - status); -} - -URegistryKey -ICULocaleService::registerInstance(UObject* objToAdopt, const UnicodeString& locale, int32_t kind, int32_t coverage, UErrorCode& status) -{ - ICUServiceFactory * factory = new SimpleLocaleKeyFactory(objToAdopt, locale, kind, coverage); - if (factory != NULL) { - return registerFactory(factory, status); - } - delete objToAdopt; - return NULL; -} -#endif - -class ServiceEnumeration : public StringEnumeration { -private: - const ICULocaleService* _service; - int32_t _timestamp; - UVector _ids; - int32_t _pos; - -private: - ServiceEnumeration(const ICULocaleService* service, UErrorCode &status) - : _service(service) - , _timestamp(service->getTimestamp()) - , _ids(uprv_deleteUObject, NULL, status) - , _pos(0) - { - _service->getVisibleIDs(_ids, status); - } - - ServiceEnumeration(const ServiceEnumeration &other, UErrorCode &status) - : _service(other._service) - , _timestamp(other._timestamp) - , _ids(uprv_deleteUObject, NULL, status) - , _pos(0) - { - if(U_SUCCESS(status)) { - int32_t i, length; - - length = other._ids.size(); - for(i = 0; i < length; ++i) { - LocalPointer clonedId(((UnicodeString *)other._ids.elementAt(i))->clone(), status); - _ids.adoptElement(clonedId.orphan(), status); - } - - if(U_SUCCESS(status)) { - _pos = other._pos; - } - } - } - -public: - static ServiceEnumeration* create(const ICULocaleService* service) { - UErrorCode status = U_ZERO_ERROR; - ServiceEnumeration* result = new ServiceEnumeration(service, status); - if (U_SUCCESS(status)) { - return result; - } - delete result; - return NULL; - } - - virtual ~ServiceEnumeration(); - - virtual StringEnumeration *clone() const override { - UErrorCode status = U_ZERO_ERROR; - ServiceEnumeration *cl = new ServiceEnumeration(*this, status); - if(U_FAILURE(status)) { - delete cl; - cl = NULL; - } - return cl; - } - - UBool upToDate(UErrorCode& status) const { - if (U_SUCCESS(status)) { - if (_timestamp == _service->getTimestamp()) { - return true; - } - status = U_ENUM_OUT_OF_SYNC_ERROR; - } - return false; - } - - virtual int32_t count(UErrorCode& status) const override { - return upToDate(status) ? _ids.size() : 0; - } - - virtual const UnicodeString* snext(UErrorCode& status) override { - if (upToDate(status) && (_pos < _ids.size())) { - return (const UnicodeString*)_ids[_pos++]; - } - return NULL; - } - - virtual void reset(UErrorCode& status) override { - if (status == U_ENUM_OUT_OF_SYNC_ERROR) { - status = U_ZERO_ERROR; - } - if (U_SUCCESS(status)) { - _timestamp = _service->getTimestamp(); - _pos = 0; - _service->getVisibleIDs(_ids, status); - } - } - -public: - static UClassID U_EXPORT2 getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; -}; - -ServiceEnumeration::~ServiceEnumeration() {} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceEnumeration) - -StringEnumeration* -ICULocaleService::getAvailableLocales(void) const -{ - return ServiceEnumeration::create(this); -} - -const UnicodeString& -ICULocaleService::validateFallbackLocale() const -{ - const Locale& loc = Locale::getDefault(); - ICULocaleService* ncThis = (ICULocaleService*)this; - static UMutex llock; - { - Mutex mutex(&llock); - if (loc != fallbackLocale) { - ncThis->fallbackLocale = loc; - LocaleUtility::initNameFromLocale(loc, ncThis->fallbackLocaleName); - ncThis->clearServiceCache(); - } - } - return fallbackLocaleName; -} - -ICUServiceKey* -ICULocaleService::createKey(const UnicodeString* id, UErrorCode& status) const -{ - return LocaleKey::createWithCanonicalFallback(id, &validateFallbackLocale(), status); -} - -ICUServiceKey* -ICULocaleService::createKey(const UnicodeString* id, int32_t kind, UErrorCode& status) const -{ - return LocaleKey::createWithCanonicalFallback(id, &validateFallbackLocale(), kind, status); -} - -U_NAMESPACE_END - -/* !UCONFIG_NO_SERVICE */ -#endif - - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** + ******************************************************************************* + * Copyright (C) 2001-2014, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + * + ******************************************************************************* + */ +#include "unicode/utypes.h" + +#if !UCONFIG_NO_SERVICE + +#include "unicode/resbund.h" +#include "uresimp.h" +#include "cmemory.h" +#include "servloc.h" +#include "ustrfmt.h" +#include "charstr.h" +#include "uassert.h" + +#define UNDERSCORE_CHAR ((char16_t)0x005f) +#define AT_SIGN_CHAR ((char16_t)64) +#define PERIOD_CHAR ((char16_t)46) + +U_NAMESPACE_BEGIN + +ICULocaleService::ICULocaleService() + : fallbackLocale(Locale::getDefault()) +{ +} + +ICULocaleService::ICULocaleService(const UnicodeString& dname) + : ICUService(dname) + , fallbackLocale(Locale::getDefault()) +{ +} + +ICULocaleService::~ICULocaleService() +{ +} + +UObject* +ICULocaleService::get(const Locale& locale, UErrorCode& status) const +{ + return get(locale, LocaleKey::KIND_ANY, nullptr, status); +} + +UObject* +ICULocaleService::get(const Locale& locale, int32_t kind, UErrorCode& status) const +{ + return get(locale, kind, nullptr, status); +} + +UObject* +ICULocaleService::get(const Locale& locale, Locale* actualReturn, UErrorCode& status) const +{ + return get(locale, LocaleKey::KIND_ANY, actualReturn, status); +} + +UObject* +ICULocaleService::get(const Locale& locale, int32_t kind, Locale* actualReturn, UErrorCode& status) const +{ + UObject* result = nullptr; + if (U_FAILURE(status)) { + return result; + } + + UnicodeString locName(locale.getName(), -1, US_INV); + if (locName.isBogus()) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + ICUServiceKey* key = createKey(&locName, kind, status); + if (key) { + if (actualReturn == nullptr) { + result = getKey(*key, status); + } else { + UnicodeString temp; + result = getKey(*key, &temp, status); + + if (result != nullptr) { + key->parseSuffix(temp); + LocaleUtility::initLocaleFromName(temp, *actualReturn); + } + } + delete key; + } + } + return result; +} + + +URegistryKey +ICULocaleService::registerInstance(UObject* objToAdopt, const UnicodeString& locale, + UBool visible, UErrorCode& status) +{ + Locale loc; + LocaleUtility::initLocaleFromName(locale, loc); + return registerInstance(objToAdopt, loc, LocaleKey::KIND_ANY, + visible ? LocaleKeyFactory::VISIBLE : LocaleKeyFactory::INVISIBLE, status); +} + +URegistryKey +ICULocaleService::registerInstance(UObject* objToAdopt, const Locale& locale, UErrorCode& status) +{ + return registerInstance(objToAdopt, locale, LocaleKey::KIND_ANY, LocaleKeyFactory::VISIBLE, status); +} + +URegistryKey +ICULocaleService::registerInstance(UObject* objToAdopt, const Locale& locale, int32_t kind, UErrorCode& status) +{ + return registerInstance(objToAdopt, locale, kind, LocaleKeyFactory::VISIBLE, status); +} + +URegistryKey +ICULocaleService::registerInstance(UObject* objToAdopt, const Locale& locale, int32_t kind, int32_t coverage, UErrorCode& status) +{ + ICUServiceFactory * factory = new SimpleLocaleKeyFactory(objToAdopt, locale, kind, coverage); + if (factory != nullptr) { + return registerFactory(factory, status); + } + delete objToAdopt; + return nullptr; +} + +#if 0 +URegistryKey +ICULocaleService::registerInstance(UObject* objToAdopt, const UnicodeString& locale, UErrorCode& status) +{ + return registerInstance(objToAdopt, locale, LocaleKey::KIND_ANY, LocaleKeyFactory::VISIBLE, status); +} + +URegistryKey +ICULocaleService::registerInstance(UObject* objToAdopt, const UnicodeString& locale, UBool visible, UErrorCode& status) +{ + return registerInstance(objToAdopt, locale, LocaleKey::KIND_ANY, + visible ? LocaleKeyFactory::VISIBLE : LocaleKeyFactory::INVISIBLE, + status); +} + +URegistryKey +ICULocaleService::registerInstance(UObject* objToAdopt, const UnicodeString& locale, int32_t kind, int32_t coverage, UErrorCode& status) +{ + ICUServiceFactory * factory = new SimpleLocaleKeyFactory(objToAdopt, locale, kind, coverage); + if (factory != nullptr) { + return registerFactory(factory, status); + } + delete objToAdopt; + return nullptr; +} +#endif + +class ServiceEnumeration : public StringEnumeration { +private: + const ICULocaleService* _service; + int32_t _timestamp; + UVector _ids; + int32_t _pos; + +private: + ServiceEnumeration(const ICULocaleService* service, UErrorCode &status) + : _service(service) + , _timestamp(service->getTimestamp()) + , _ids(uprv_deleteUObject, nullptr, status) + , _pos(0) + { + _service->getVisibleIDs(_ids, status); + } + + ServiceEnumeration(const ServiceEnumeration &other, UErrorCode &status) + : _service(other._service) + , _timestamp(other._timestamp) + , _ids(uprv_deleteUObject, nullptr, status) + , _pos(0) + { + if(U_SUCCESS(status)) { + int32_t i, length; + + length = other._ids.size(); + for(i = 0; i < length; ++i) { + LocalPointer clonedId(((UnicodeString *)other._ids.elementAt(i))->clone(), status); + _ids.adoptElement(clonedId.orphan(), status); + } + + if(U_SUCCESS(status)) { + _pos = other._pos; + } + } + } + +public: + static ServiceEnumeration* create(const ICULocaleService* service) { + UErrorCode status = U_ZERO_ERROR; + ServiceEnumeration* result = new ServiceEnumeration(service, status); + if (U_SUCCESS(status)) { + return result; + } + delete result; + return nullptr; + } + + virtual ~ServiceEnumeration(); + + virtual StringEnumeration *clone() const override { + UErrorCode status = U_ZERO_ERROR; + ServiceEnumeration *cl = new ServiceEnumeration(*this, status); + if(U_FAILURE(status)) { + delete cl; + cl = nullptr; + } + return cl; + } + + UBool upToDate(UErrorCode& status) const { + if (U_SUCCESS(status)) { + if (_timestamp == _service->getTimestamp()) { + return true; + } + status = U_ENUM_OUT_OF_SYNC_ERROR; + } + return false; + } + + virtual int32_t count(UErrorCode& status) const override { + return upToDate(status) ? _ids.size() : 0; + } + + virtual const UnicodeString* snext(UErrorCode& status) override { + if (upToDate(status) && (_pos < _ids.size())) { + return (const UnicodeString*)_ids[_pos++]; + } + return nullptr; + } + + virtual void reset(UErrorCode& status) override { + if (status == U_ENUM_OUT_OF_SYNC_ERROR) { + status = U_ZERO_ERROR; + } + if (U_SUCCESS(status)) { + _timestamp = _service->getTimestamp(); + _pos = 0; + _service->getVisibleIDs(_ids, status); + } + } + +public: + static UClassID U_EXPORT2 getStaticClassID(); + virtual UClassID getDynamicClassID() const override; +}; + +ServiceEnumeration::~ServiceEnumeration() {} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceEnumeration) + +StringEnumeration* +ICULocaleService::getAvailableLocales() const +{ + return ServiceEnumeration::create(this); +} + +const UnicodeString& +ICULocaleService::validateFallbackLocale() const +{ + const Locale& loc = Locale::getDefault(); + ICULocaleService* ncThis = (ICULocaleService*)this; + static UMutex llock; + { + Mutex mutex(&llock); + if (loc != fallbackLocale) { + ncThis->fallbackLocale = loc; + LocaleUtility::initNameFromLocale(loc, ncThis->fallbackLocaleName); + ncThis->clearServiceCache(); + } + } + return fallbackLocaleName; +} + +ICUServiceKey* +ICULocaleService::createKey(const UnicodeString* id, UErrorCode& status) const +{ + return LocaleKey::createWithCanonicalFallback(id, &validateFallbackLocale(), status); +} + +ICUServiceKey* +ICULocaleService::createKey(const UnicodeString* id, int32_t kind, UErrorCode& status) const +{ + return LocaleKey::createWithCanonicalFallback(id, &validateFallbackLocale(), kind, status); +} + +U_NAMESPACE_END + +/* !UCONFIG_NO_SERVICE */ +#endif + + diff --git a/deps/icu-small/source/common/servnotf.cpp b/deps/icu-small/source/common/servnotf.cpp index d9fb38875202df..34135b9c6bd6b3 100644 --- a/deps/icu-small/source/common/servnotf.cpp +++ b/deps/icu-small/source/common/servnotf.cpp @@ -1,122 +1,122 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** - ******************************************************************************* - * Copyright (C) 2001-2012, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_SERVICE - -#include "servnotf.h" -#ifdef NOTIFIER_DEBUG -#include -#endif - -U_NAMESPACE_BEGIN - -EventListener::~EventListener() {} -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(EventListener) - -static UMutex notifyLock; - -ICUNotifier::ICUNotifier(void) -: listeners(NULL) -{ -} - -ICUNotifier::~ICUNotifier(void) { - { - Mutex lmx(¬ifyLock); - delete listeners; - listeners = NULL; - } -} - - -void -ICUNotifier::addListener(const EventListener* l, UErrorCode& status) -{ - if (U_SUCCESS(status)) { - if (l == NULL) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - if (acceptsListener(*l)) { - Mutex lmx(¬ifyLock); - if (listeners == NULL) { - LocalPointer lpListeners(new UVector(5, status), status); - if (U_FAILURE(status)) { - return; - } - listeners = lpListeners.orphan(); - } else { - for (int i = 0, e = listeners->size(); i < e; ++i) { - const EventListener* el = (const EventListener*)(listeners->elementAt(i)); - if (l == el) { - return; - } - } - } - - listeners->addElement((void*)l, status); // cast away const - } -#ifdef NOTIFIER_DEBUG - else { - fprintf(stderr, "Listener invalid for this notifier."); - exit(1); - } -#endif - } -} - -void -ICUNotifier::removeListener(const EventListener *l, UErrorCode& status) -{ - if (U_SUCCESS(status)) { - if (l == NULL) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - { - Mutex lmx(¬ifyLock); - if (listeners != NULL) { - // identity equality check - for (int i = 0, e = listeners->size(); i < e; ++i) { - const EventListener* el = (const EventListener*)listeners->elementAt(i); - if (l == el) { - listeners->removeElementAt(i); - if (listeners->size() == 0) { - delete listeners; - listeners = NULL; - } - return; - } - } - } - } - } -} - -void -ICUNotifier::notifyChanged(void) -{ - Mutex lmx(¬ifyLock); - if (listeners != NULL) { - for (int i = 0, e = listeners->size(); i < e; ++i) { - EventListener* el = (EventListener*)listeners->elementAt(i); - notifyListener(*el); - } - } -} - -U_NAMESPACE_END - -/* UCONFIG_NO_SERVICE */ -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** + ******************************************************************************* + * Copyright (C) 2001-2012, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_SERVICE + +#include "servnotf.h" +#ifdef NOTIFIER_DEBUG +#include +#endif + +U_NAMESPACE_BEGIN + +EventListener::~EventListener() {} +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(EventListener) + +static UMutex notifyLock; + +ICUNotifier::ICUNotifier() +: listeners(nullptr) +{ +} + +ICUNotifier::~ICUNotifier() { + { + Mutex lmx(¬ifyLock); + delete listeners; + listeners = nullptr; + } +} + + +void +ICUNotifier::addListener(const EventListener* l, UErrorCode& status) +{ + if (U_SUCCESS(status)) { + if (l == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + if (acceptsListener(*l)) { + Mutex lmx(¬ifyLock); + if (listeners == nullptr) { + LocalPointer lpListeners(new UVector(5, status), status); + if (U_FAILURE(status)) { + return; + } + listeners = lpListeners.orphan(); + } else { + for (int i = 0, e = listeners->size(); i < e; ++i) { + const EventListener* el = (const EventListener*)(listeners->elementAt(i)); + if (l == el) { + return; + } + } + } + + listeners->addElement((void*)l, status); // cast away const + } +#ifdef NOTIFIER_DEBUG + else { + fprintf(stderr, "Listener invalid for this notifier."); + exit(1); + } +#endif + } +} + +void +ICUNotifier::removeListener(const EventListener *l, UErrorCode& status) +{ + if (U_SUCCESS(status)) { + if (l == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + { + Mutex lmx(¬ifyLock); + if (listeners != nullptr) { + // identity equality check + for (int i = 0, e = listeners->size(); i < e; ++i) { + const EventListener* el = (const EventListener*)listeners->elementAt(i); + if (l == el) { + listeners->removeElementAt(i); + if (listeners->size() == 0) { + delete listeners; + listeners = nullptr; + } + return; + } + } + } + } + } +} + +void +ICUNotifier::notifyChanged() +{ + Mutex lmx(¬ifyLock); + if (listeners != nullptr) { + for (int i = 0, e = listeners->size(); i < e; ++i) { + EventListener* el = (EventListener*)listeners->elementAt(i); + notifyListener(*el); + } + } +} + +U_NAMESPACE_END + +/* UCONFIG_NO_SERVICE */ +#endif + diff --git a/deps/icu-small/source/common/servnotf.h b/deps/icu-small/source/common/servnotf.h index e3b2cac32e4e6b..44373150eed2cf 100644 --- a/deps/icu-small/source/common/servnotf.h +++ b/deps/icu-small/source/common/servnotf.h @@ -1,125 +1,125 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** - ******************************************************************************* - * Copyright (C) 2001-2014, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - */ -#ifndef ICUNOTIF_H -#define ICUNOTIF_H - -#include "unicode/utypes.h" - -#if UCONFIG_NO_SERVICE - -U_NAMESPACE_BEGIN - -/* - * Allow the declaration of APIs with pointers to BreakIterator - * even when break iteration is removed from the build. - */ -class ICUNotifier; - -U_NAMESPACE_END - -#else - -#include "unicode/uobject.h" -#include "unicode/unistr.h" - -#include "mutex.h" -#include "uvector.h" - -U_NAMESPACE_BEGIN - -class U_COMMON_API EventListener : public UObject { -public: - virtual ~EventListener(); - -public: - static UClassID U_EXPORT2 getStaticClassID(); - - virtual UClassID getDynamicClassID() const override; - -public: -#ifdef SERVICE_DEBUG - virtual UnicodeString& debug(UnicodeString& result) const { - return debugClass(result); - } - - virtual UnicodeString& debugClass(UnicodeString& result) const { - return result.append((UnicodeString)"Key"); - } -#endif -}; - -/** - *

Abstract implementation of a notification facility. Clients add - * EventListeners with addListener and remove them with removeListener. - * Notifiers call notifyChanged when they wish to notify listeners. - * This queues the listener list on the notification thread, which - * eventually dequeues the list and calls notifyListener on each - * listener in the list.

- * - *

Subclasses override acceptsListener and notifyListener - * to add type-safe notification. AcceptsListener should return - * true if the listener is of the appropriate type; ICUNotifier - * itself will ensure the listener is non-null and that the - * identical listener is not already registered with the Notifier. - * NotifyListener should cast the listener to the appropriate - * type and call the appropriate method on the listener. - */ - -class U_COMMON_API ICUNotifier : public UMemory { -private: UVector* listeners; - -public: - ICUNotifier(void); - - virtual ~ICUNotifier(void); - - /** - * Add a listener to be notified when notifyChanged is called. - * The listener must not be null. AcceptsListener must return - * true for the listener. Attempts to concurrently - * register the identical listener more than once will be - * silently ignored. - */ - virtual void addListener(const EventListener* l, UErrorCode& status); - - /** - * Stop notifying this listener. The listener must - * not be null. Attempts to remove a listener that is - * not registered will be silently ignored. - */ - virtual void removeListener(const EventListener* l, UErrorCode& status); - - /** - * ICU doesn't spawn its own threads. All listeners are notified in - * the thread of the caller. Misbehaved listeners can therefore - * indefinitely block the calling thread. Callers should beware of - * deadlock situations. - */ - virtual void notifyChanged(void); - -protected: - /** - * Subclasses implement this to return true if the listener is - * of the appropriate type. - */ - virtual UBool acceptsListener(const EventListener& l) const = 0; - - /** - * Subclasses implement this to notify the listener. - */ - virtual void notifyListener(EventListener& l) const = 0; -}; - -U_NAMESPACE_END - -/* UCONFIG_NO_SERVICE */ -#endif - -/* ICUNOTIF_H */ -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** + ******************************************************************************* + * Copyright (C) 2001-2014, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ +#ifndef ICUNOTIF_H +#define ICUNOTIF_H + +#include "unicode/utypes.h" + +#if UCONFIG_NO_SERVICE + +U_NAMESPACE_BEGIN + +/* + * Allow the declaration of APIs with pointers to BreakIterator + * even when break iteration is removed from the build. + */ +class ICUNotifier; + +U_NAMESPACE_END + +#else + +#include "unicode/uobject.h" +#include "unicode/unistr.h" + +#include "mutex.h" +#include "uvector.h" + +U_NAMESPACE_BEGIN + +class U_COMMON_API EventListener : public UObject { +public: + virtual ~EventListener(); + +public: + static UClassID U_EXPORT2 getStaticClassID(); + + virtual UClassID getDynamicClassID() const override; + +public: +#ifdef SERVICE_DEBUG + virtual UnicodeString& debug(UnicodeString& result) const { + return debugClass(result); + } + + virtual UnicodeString& debugClass(UnicodeString& result) const { + return result.append((UnicodeString)"Key"); + } +#endif +}; + +/** + *

Abstract implementation of a notification facility. Clients add + * EventListeners with addListener and remove them with removeListener. + * Notifiers call notifyChanged when they wish to notify listeners. + * This queues the listener list on the notification thread, which + * eventually dequeues the list and calls notifyListener on each + * listener in the list.

+ * + *

Subclasses override acceptsListener and notifyListener + * to add type-safe notification. AcceptsListener should return + * true if the listener is of the appropriate type; ICUNotifier + * itself will ensure the listener is non-null and that the + * identical listener is not already registered with the Notifier. + * NotifyListener should cast the listener to the appropriate + * type and call the appropriate method on the listener. + */ + +class U_COMMON_API ICUNotifier : public UMemory { +private: UVector* listeners; + +public: + ICUNotifier(); + + virtual ~ICUNotifier(); + + /** + * Add a listener to be notified when notifyChanged is called. + * The listener must not be null. AcceptsListener must return + * true for the listener. Attempts to concurrently + * register the identical listener more than once will be + * silently ignored. + */ + virtual void addListener(const EventListener* l, UErrorCode& status); + + /** + * Stop notifying this listener. The listener must + * not be null. Attempts to remove a listener that is + * not registered will be silently ignored. + */ + virtual void removeListener(const EventListener* l, UErrorCode& status); + + /** + * ICU doesn't spawn its own threads. All listeners are notified in + * the thread of the caller. Misbehaved listeners can therefore + * indefinitely block the calling thread. Callers should beware of + * deadlock situations. + */ + virtual void notifyChanged(); + +protected: + /** + * Subclasses implement this to return true if the listener is + * of the appropriate type. + */ + virtual UBool acceptsListener(const EventListener& l) const = 0; + + /** + * Subclasses implement this to notify the listener. + */ + virtual void notifyListener(EventListener& l) const = 0; +}; + +U_NAMESPACE_END + +/* UCONFIG_NO_SERVICE */ +#endif + +/* ICUNOTIF_H */ +#endif diff --git a/deps/icu-small/source/common/servrbf.cpp b/deps/icu-small/source/common/servrbf.cpp index 94279ab3a1513b..aee9aefa130b46 100644 --- a/deps/icu-small/source/common/servrbf.cpp +++ b/deps/icu-small/source/common/servrbf.cpp @@ -1,96 +1,96 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** - ******************************************************************************* - * Copyright (C) 2001-2014, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - * - ******************************************************************************* - */ -#include "unicode/utypes.h" - -#if !UCONFIG_NO_SERVICE - -#include "unicode/resbund.h" -#include "uresimp.h" -#include "cmemory.h" -#include "servloc.h" -#include "ustrfmt.h" -#include "uhash.h" -#include "charstr.h" -#include "ucln_cmn.h" -#include "uassert.h" - -#define UNDERSCORE_CHAR ((UChar)0x005f) -#define AT_SIGN_CHAR ((UChar)64) -#define PERIOD_CHAR ((UChar)46) - -U_NAMESPACE_BEGIN - -ICUResourceBundleFactory::ICUResourceBundleFactory() - : LocaleKeyFactory(VISIBLE) - , _bundleName() -{ -} - -ICUResourceBundleFactory::ICUResourceBundleFactory(const UnicodeString& bundleName) - : LocaleKeyFactory(VISIBLE) - , _bundleName(bundleName) -{ -} - -ICUResourceBundleFactory::~ICUResourceBundleFactory() {} - -const Hashtable* -ICUResourceBundleFactory::getSupportedIDs(UErrorCode& status) const -{ - if (U_SUCCESS(status)) { - return LocaleUtility::getAvailableLocaleNames(_bundleName); - } - return NULL; -} - -UObject* -ICUResourceBundleFactory::handleCreate(const Locale& loc, int32_t /* kind */, const ICUService* /* service */, UErrorCode& status) const -{ - if (U_SUCCESS(status)) { - // _bundleName is a package name - // and should only contain invariant characters - // ??? is it always true that the max length of the bundle name is 19? - // who made this change? -- dlf - char pkg[20]; - int32_t length; - length=_bundleName.extract(0, INT32_MAX, pkg, (int32_t)sizeof(pkg), US_INV); - if(length>=(int32_t)sizeof(pkg)) { - return NULL; - } - return new ResourceBundle(pkg, loc, status); - } - return NULL; -} - -#ifdef SERVICE_DEBUG -UnicodeString& -ICUResourceBundleFactory::debug(UnicodeString& result) const -{ - LocaleKeyFactory::debug(result); - result.append((UnicodeString)", bundle: "); - return result.append(_bundleName); -} - -UnicodeString& -ICUResourceBundleFactory::debugClass(UnicodeString& result) const -{ - return result.append((UnicodeString)"ICUResourceBundleFactory"); -} -#endif - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUResourceBundleFactory) - -U_NAMESPACE_END - -/* !UCONFIG_NO_SERVICE */ -#endif - - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** + ******************************************************************************* + * Copyright (C) 2001-2014, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + * + ******************************************************************************* + */ +#include "unicode/utypes.h" + +#if !UCONFIG_NO_SERVICE + +#include "unicode/resbund.h" +#include "uresimp.h" +#include "cmemory.h" +#include "servloc.h" +#include "ustrfmt.h" +#include "uhash.h" +#include "charstr.h" +#include "ucln_cmn.h" +#include "uassert.h" + +#define UNDERSCORE_CHAR ((char16_t)0x005f) +#define AT_SIGN_CHAR ((char16_t)64) +#define PERIOD_CHAR ((char16_t)46) + +U_NAMESPACE_BEGIN + +ICUResourceBundleFactory::ICUResourceBundleFactory() + : LocaleKeyFactory(VISIBLE) + , _bundleName() +{ +} + +ICUResourceBundleFactory::ICUResourceBundleFactory(const UnicodeString& bundleName) + : LocaleKeyFactory(VISIBLE) + , _bundleName(bundleName) +{ +} + +ICUResourceBundleFactory::~ICUResourceBundleFactory() {} + +const Hashtable* +ICUResourceBundleFactory::getSupportedIDs(UErrorCode& status) const +{ + if (U_SUCCESS(status)) { + return LocaleUtility::getAvailableLocaleNames(_bundleName); + } + return nullptr; +} + +UObject* +ICUResourceBundleFactory::handleCreate(const Locale& loc, int32_t /* kind */, const ICUService* /* service */, UErrorCode& status) const +{ + if (U_SUCCESS(status)) { + // _bundleName is a package name + // and should only contain invariant characters + // ??? is it always true that the max length of the bundle name is 19? + // who made this change? -- dlf + char pkg[20]; + int32_t length; + length=_bundleName.extract(0, INT32_MAX, pkg, (int32_t)sizeof(pkg), US_INV); + if(length>=(int32_t)sizeof(pkg)) { + return nullptr; + } + return new ResourceBundle(pkg, loc, status); + } + return nullptr; +} + +#ifdef SERVICE_DEBUG +UnicodeString& +ICUResourceBundleFactory::debug(UnicodeString& result) const +{ + LocaleKeyFactory::debug(result); + result.append((UnicodeString)", bundle: "); + return result.append(_bundleName); +} + +UnicodeString& +ICUResourceBundleFactory::debugClass(UnicodeString& result) const +{ + return result.append((UnicodeString)"ICUResourceBundleFactory"); +} +#endif + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUResourceBundleFactory) + +U_NAMESPACE_END + +/* !UCONFIG_NO_SERVICE */ +#endif + + diff --git a/deps/icu-small/source/common/servslkf.cpp b/deps/icu-small/source/common/servslkf.cpp index 09154d1b9198e7..03735d7bf59ff3 100644 --- a/deps/icu-small/source/common/servslkf.cpp +++ b/deps/icu-small/source/common/servslkf.cpp @@ -1,123 +1,123 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/** - ******************************************************************************* - * Copyright (C) 2001-2014, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - * - ******************************************************************************* - */ -#include "unicode/utypes.h" - -#if !UCONFIG_NO_SERVICE - -#include "unicode/resbund.h" -#include "uresimp.h" -#include "cmemory.h" -#include "servloc.h" -#include "ustrfmt.h" -#include "uhash.h" -#include "charstr.h" -#include "uassert.h" - -#define UNDERSCORE_CHAR ((UChar)0x005f) -#define AT_SIGN_CHAR ((UChar)64) -#define PERIOD_CHAR ((UChar)46) - -U_NAMESPACE_BEGIN - -/* - ****************************************************************** - */ - -SimpleLocaleKeyFactory::SimpleLocaleKeyFactory(UObject* objToAdopt, - const UnicodeString& locale, - int32_t kind, - int32_t coverage) - : LocaleKeyFactory(coverage) - , _obj(objToAdopt) - , _id(locale) - , _kind(kind) -{ -} - -SimpleLocaleKeyFactory::SimpleLocaleKeyFactory(UObject* objToAdopt, - const Locale& locale, - int32_t kind, - int32_t coverage) - : LocaleKeyFactory(coverage) - , _obj(objToAdopt) - , _id() - , _kind(kind) -{ - LocaleUtility::initNameFromLocale(locale, _id); -} - -SimpleLocaleKeyFactory::~SimpleLocaleKeyFactory() -{ - delete _obj; - _obj = NULL; -} - -UObject* -SimpleLocaleKeyFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const -{ - if (U_SUCCESS(status)) { - const LocaleKey& lkey = (const LocaleKey&)key; - if (_kind == LocaleKey::KIND_ANY || _kind == lkey.kind()) { - UnicodeString keyID; - lkey.currentID(keyID); - if (_id == keyID) { - return service->cloneInstance(_obj); - } - } - } - return NULL; -} - -//UBool -//SimpleLocaleKeyFactory::isSupportedID(const UnicodeString& id, UErrorCode& /* status */) const -//{ -// return id == _id; -//} - -void -SimpleLocaleKeyFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const -{ - if (U_SUCCESS(status)) { - if (_coverage & 0x1) { - result.remove(_id); - } else { - result.put(_id, (void*)this, status); - } - } -} - -#ifdef SERVICE_DEBUG -UnicodeString& -SimpleLocaleKeyFactory::debug(UnicodeString& result) const -{ - LocaleKeyFactory::debug(result); - result.append((UnicodeString)", id: "); - result.append(_id); - result.append((UnicodeString)", kind: "); - result.append(_kind); - return result; -} - -UnicodeString& -SimpleLocaleKeyFactory::debugClass(UnicodeString& result) const -{ - return result.append((UnicodeString)"SimpleLocaleKeyFactory"); -} -#endif - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleLocaleKeyFactory) - -U_NAMESPACE_END - -/* !UCONFIG_NO_SERVICE */ -#endif - - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** + ******************************************************************************* + * Copyright (C) 2001-2014, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + * + ******************************************************************************* + */ +#include "unicode/utypes.h" + +#if !UCONFIG_NO_SERVICE + +#include "unicode/resbund.h" +#include "uresimp.h" +#include "cmemory.h" +#include "servloc.h" +#include "ustrfmt.h" +#include "uhash.h" +#include "charstr.h" +#include "uassert.h" + +#define UNDERSCORE_CHAR ((char16_t)0x005f) +#define AT_SIGN_CHAR ((char16_t)64) +#define PERIOD_CHAR ((char16_t)46) + +U_NAMESPACE_BEGIN + +/* + ****************************************************************** + */ + +SimpleLocaleKeyFactory::SimpleLocaleKeyFactory(UObject* objToAdopt, + const UnicodeString& locale, + int32_t kind, + int32_t coverage) + : LocaleKeyFactory(coverage) + , _obj(objToAdopt) + , _id(locale) + , _kind(kind) +{ +} + +SimpleLocaleKeyFactory::SimpleLocaleKeyFactory(UObject* objToAdopt, + const Locale& locale, + int32_t kind, + int32_t coverage) + : LocaleKeyFactory(coverage) + , _obj(objToAdopt) + , _id() + , _kind(kind) +{ + LocaleUtility::initNameFromLocale(locale, _id); +} + +SimpleLocaleKeyFactory::~SimpleLocaleKeyFactory() +{ + delete _obj; + _obj = nullptr; +} + +UObject* +SimpleLocaleKeyFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const +{ + if (U_SUCCESS(status)) { + const LocaleKey& lkey = static_cast(key); + if (_kind == LocaleKey::KIND_ANY || _kind == lkey.kind()) { + UnicodeString keyID; + lkey.currentID(keyID); + if (_id == keyID) { + return service->cloneInstance(_obj); + } + } + } + return nullptr; +} + +//UBool +//SimpleLocaleKeyFactory::isSupportedID(const UnicodeString& id, UErrorCode& /* status */) const +//{ +// return id == _id; +//} + +void +SimpleLocaleKeyFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const +{ + if (U_SUCCESS(status)) { + if (_coverage & 0x1) { + result.remove(_id); + } else { + result.put(_id, (void*)this, status); + } + } +} + +#ifdef SERVICE_DEBUG +UnicodeString& +SimpleLocaleKeyFactory::debug(UnicodeString& result) const +{ + LocaleKeyFactory::debug(result); + result.append((UnicodeString)", id: "); + result.append(_id); + result.append((UnicodeString)", kind: "); + result.append(_kind); + return result; +} + +UnicodeString& +SimpleLocaleKeyFactory::debugClass(UnicodeString& result) const +{ + return result.append((UnicodeString)"SimpleLocaleKeyFactory"); +} +#endif + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleLocaleKeyFactory) + +U_NAMESPACE_END + +/* !UCONFIG_NO_SERVICE */ +#endif + + diff --git a/deps/icu-small/source/common/sharedobject.cpp b/deps/icu-small/source/common/sharedobject.cpp index 6eeca8605f0002..aeac58d6e04d84 100644 --- a/deps/icu-small/source/common/sharedobject.cpp +++ b/deps/icu-small/source/common/sharedobject.cpp @@ -1,62 +1,62 @@ -// © 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. -****************************************************************************** -* sharedobject.cpp -*/ -#include "sharedobject.h" -#include "mutex.h" -#include "uassert.h" -#include "umutex.h" -#include "unifiedcache.h" - -U_NAMESPACE_BEGIN - -SharedObject::~SharedObject() {} - -UnifiedCacheBase::~UnifiedCacheBase() {} - -void -SharedObject::addRef() const { - umtx_atomic_inc(&hardRefCount); -} - -// removeRef Decrement the reference count and delete if it is zero. -// Note that SharedObjects with a non-null cachePtr are owned by the -// unified cache, and the cache will be responsible for the actual deletion. -// The deletion could be as soon as immediately following the -// update to the reference count, if another thread is running -// a cache eviction cycle concurrently. -// NO ACCESS TO *this PERMITTED AFTER REFERENCE COUNT == 0 for cached objects. -// THE OBJECT MAY ALREADY BE GONE. -void -SharedObject::removeRef() const { - const UnifiedCacheBase *cache = this->cachePtr; - int32_t updatedRefCount = umtx_atomic_dec(&hardRefCount); - U_ASSERT(updatedRefCount >= 0); - if (updatedRefCount == 0) { - if (cache) { - cache->handleUnreferencedObject(); - } else { - delete this; - } - } -} - - -int32_t -SharedObject::getRefCount() const { - return umtx_loadAcquire(hardRefCount); -} - -void -SharedObject::deleteIfZeroRefCount() const { - if (this->cachePtr == nullptr && getRefCount() == 0) { - delete this; - } -} - -U_NAMESPACE_END +// © 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. +****************************************************************************** +* sharedobject.cpp +*/ +#include "sharedobject.h" +#include "mutex.h" +#include "uassert.h" +#include "umutex.h" +#include "unifiedcache.h" + +U_NAMESPACE_BEGIN + +SharedObject::~SharedObject() {} + +UnifiedCacheBase::~UnifiedCacheBase() {} + +void +SharedObject::addRef() const { + umtx_atomic_inc(&hardRefCount); +} + +// removeRef Decrement the reference count and delete if it is zero. +// Note that SharedObjects with a non-null cachePtr are owned by the +// unified cache, and the cache will be responsible for the actual deletion. +// The deletion could be as soon as immediately following the +// update to the reference count, if another thread is running +// a cache eviction cycle concurrently. +// NO ACCESS TO *this PERMITTED AFTER REFERENCE COUNT == 0 for cached objects. +// THE OBJECT MAY ALREADY BE GONE. +void +SharedObject::removeRef() const { + const UnifiedCacheBase *cache = this->cachePtr; + int32_t updatedRefCount = umtx_atomic_dec(&hardRefCount); + U_ASSERT(updatedRefCount >= 0); + if (updatedRefCount == 0) { + if (cache) { + cache->handleUnreferencedObject(); + } else { + delete this; + } + } +} + + +int32_t +SharedObject::getRefCount() const { + return umtx_loadAcquire(hardRefCount); +} + +void +SharedObject::deleteIfZeroRefCount() const { + if (this->cachePtr == nullptr && getRefCount() == 0) { + delete this; + } +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/sharedobject.h b/deps/icu-small/source/common/sharedobject.h index 6298662bbaff0e..d01c932b26e4a9 100644 --- a/deps/icu-small/source/common/sharedobject.h +++ b/deps/icu-small/source/common/sharedobject.h @@ -1,184 +1,184 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 2015-2016, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* sharedobject.h -*/ - -#ifndef __SHAREDOBJECT_H__ -#define __SHAREDOBJECT_H__ - - -#include "unicode/uobject.h" -#include "umutex.h" - -U_NAMESPACE_BEGIN - -class SharedObject; - -/** - * Base class for unified cache exposing enough methods to SharedObject - * instances to allow their addRef() and removeRef() methods to - * update cache metrics. No other part of ICU, except for SharedObject, - * should directly call the methods of this base class. - */ -class U_COMMON_API UnifiedCacheBase : public UObject { -public: - UnifiedCacheBase() { } - - /** - * Notify the cache implementation that an object was seen transitioning to - * zero hard references. The cache may use this to keep track the number of - * unreferenced SharedObjects, and to trigger evictions. - */ - virtual void handleUnreferencedObject() const = 0; - - virtual ~UnifiedCacheBase(); -private: - UnifiedCacheBase(const UnifiedCacheBase &) = delete; - UnifiedCacheBase &operator=(const UnifiedCacheBase &) = delete; -}; - -/** - * Base class for shared, reference-counted, auto-deleted objects. - * Subclasses can be immutable. - * If they are mutable, then they must implement their copy constructor - * so that copyOnWrite() works. - * - * Either stack-allocate, use LocalPointer, or use addRef()/removeRef(). - * Sharing requires reference-counting. - */ -class U_COMMON_API SharedObject : public UObject { -public: - /** Initializes totalRefCount, softRefCount to 0. */ - SharedObject() : - softRefCount(0), - hardRefCount(0), - cachePtr(NULL) {} - - /** Initializes totalRefCount, softRefCount to 0. */ - SharedObject(const SharedObject &other) : - UObject(other), - softRefCount(0), - hardRefCount(0), - cachePtr(NULL) {} - - virtual ~SharedObject(); - - /** - * Increments the number of hard references to this object. Thread-safe. - * Not for use from within the Unified Cache implementation. - */ - void addRef() const; - - /** - * Decrements the number of hard references to this object, and - * arrange for possible cache-eviction and/or deletion if ref - * count goes to zero. Thread-safe. - * - * Not for use from within the UnifiedCache implementation. - */ - void removeRef() const; - - /** - * Returns the number of hard references for this object. - * Uses a memory barrier. - */ - int32_t getRefCount() const; - - /** - * If noHardReferences() == true then this object has no hard references. - * Must be called only from within the internals of UnifiedCache. - */ - inline UBool noHardReferences() const { return getRefCount() == 0; } - - /** - * If hasHardReferences() == true then this object has hard references. - * Must be called only from within the internals of UnifiedCache. - */ - inline UBool hasHardReferences() const { return getRefCount() != 0; } - - /** - * Deletes this object if it has no references. - * Available for non-cached SharedObjects only. Ownership of cached objects - * is with the UnifiedCache, which is solely responsible for eviction and deletion. - */ - void deleteIfZeroRefCount() const; - - - /** - * Returns a writable version of ptr. - * If there is exactly one owner, then ptr itself is returned as a - * non-const pointer. - * If there are multiple owners, then ptr is replaced with a - * copy-constructed clone, - * and that is returned. - * Returns NULL if cloning failed. - * - * T must be a subclass of SharedObject. - */ - template - static T *copyOnWrite(const T *&ptr) { - const T *p = ptr; - if(p->getRefCount() <= 1) { return const_cast(p); } - T *p2 = new T(*p); - if(p2 == NULL) { return NULL; } - p->removeRef(); - ptr = p2; - p2->addRef(); - return p2; - } - - /** - * Makes dest an owner of the object pointed to by src while adjusting - * reference counts and deleting the previous object dest pointed to - * if necessary. Before this call is made, dest must either be NULL or - * be included in the reference count of the object it points to. - * - * T must be a subclass of SharedObject. - */ - template - static void copyPtr(const T *src, const T *&dest) { - if(src != dest) { - if(dest != NULL) { dest->removeRef(); } - dest = src; - if(src != NULL) { src->addRef(); } - } - } - - /** - * Equivalent to copyPtr(NULL, dest). - */ - template - static void clearPtr(const T *&ptr) { - if (ptr != NULL) { - ptr->removeRef(); - ptr = NULL; - } - } - -private: - /** - * The number of references from the UnifiedCache, which is - * the number of times that the sharedObject is stored as a hash table value. - * For use by UnifiedCache implementation code only. - * All access is synchronized by UnifiedCache's gCacheMutex - */ - mutable int32_t softRefCount; - friend class UnifiedCache; - - /** - * Reference count, excluding references from within the UnifiedCache implementation. - */ - mutable u_atomic_int32_t hardRefCount; - - mutable const UnifiedCacheBase *cachePtr; - -}; - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 2015-2016, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* sharedobject.h +*/ + +#ifndef __SHAREDOBJECT_H__ +#define __SHAREDOBJECT_H__ + + +#include "unicode/uobject.h" +#include "umutex.h" + +U_NAMESPACE_BEGIN + +class SharedObject; + +/** + * Base class for unified cache exposing enough methods to SharedObject + * instances to allow their addRef() and removeRef() methods to + * update cache metrics. No other part of ICU, except for SharedObject, + * should directly call the methods of this base class. + */ +class U_COMMON_API UnifiedCacheBase : public UObject { +public: + UnifiedCacheBase() { } + + /** + * Notify the cache implementation that an object was seen transitioning to + * zero hard references. The cache may use this to keep track the number of + * unreferenced SharedObjects, and to trigger evictions. + */ + virtual void handleUnreferencedObject() const = 0; + + virtual ~UnifiedCacheBase(); +private: + UnifiedCacheBase(const UnifiedCacheBase &) = delete; + UnifiedCacheBase &operator=(const UnifiedCacheBase &) = delete; +}; + +/** + * Base class for shared, reference-counted, auto-deleted objects. + * Subclasses can be immutable. + * If they are mutable, then they must implement their copy constructor + * so that copyOnWrite() works. + * + * Either stack-allocate, use LocalPointer, or use addRef()/removeRef(). + * Sharing requires reference-counting. + */ +class U_COMMON_API SharedObject : public UObject { +public: + /** Initializes totalRefCount, softRefCount to 0. */ + SharedObject() : + softRefCount(0), + hardRefCount(0), + cachePtr(nullptr) {} + + /** Initializes totalRefCount, softRefCount to 0. */ + SharedObject(const SharedObject &other) : + UObject(other), + softRefCount(0), + hardRefCount(0), + cachePtr(nullptr) {} + + virtual ~SharedObject(); + + /** + * Increments the number of hard references to this object. Thread-safe. + * Not for use from within the Unified Cache implementation. + */ + void addRef() const; + + /** + * Decrements the number of hard references to this object, and + * arrange for possible cache-eviction and/or deletion if ref + * count goes to zero. Thread-safe. + * + * Not for use from within the UnifiedCache implementation. + */ + void removeRef() const; + + /** + * Returns the number of hard references for this object. + * Uses a memory barrier. + */ + int32_t getRefCount() const; + + /** + * If noHardReferences() == true then this object has no hard references. + * Must be called only from within the internals of UnifiedCache. + */ + inline UBool noHardReferences() const { return getRefCount() == 0; } + + /** + * If hasHardReferences() == true then this object has hard references. + * Must be called only from within the internals of UnifiedCache. + */ + inline UBool hasHardReferences() const { return getRefCount() != 0; } + + /** + * Deletes this object if it has no references. + * Available for non-cached SharedObjects only. Ownership of cached objects + * is with the UnifiedCache, which is solely responsible for eviction and deletion. + */ + void deleteIfZeroRefCount() const; + + + /** + * Returns a writable version of ptr. + * If there is exactly one owner, then ptr itself is returned as a + * non-const pointer. + * If there are multiple owners, then ptr is replaced with a + * copy-constructed clone, + * and that is returned. + * Returns nullptr if cloning failed. + * + * T must be a subclass of SharedObject. + */ + template + static T *copyOnWrite(const T *&ptr) { + const T *p = ptr; + if(p->getRefCount() <= 1) { return const_cast(p); } + T *p2 = new T(*p); + if(p2 == nullptr) { return nullptr; } + p->removeRef(); + ptr = p2; + p2->addRef(); + return p2; + } + + /** + * Makes dest an owner of the object pointed to by src while adjusting + * reference counts and deleting the previous object dest pointed to + * if necessary. Before this call is made, dest must either be nullptr or + * be included in the reference count of the object it points to. + * + * T must be a subclass of SharedObject. + */ + template + static void copyPtr(const T *src, const T *&dest) { + if(src != dest) { + if(dest != nullptr) { dest->removeRef(); } + dest = src; + if(src != nullptr) { src->addRef(); } + } + } + + /** + * Equivalent to copyPtr(nullptr, dest). + */ + template + static void clearPtr(const T *&ptr) { + if (ptr != nullptr) { + ptr->removeRef(); + ptr = nullptr; + } + } + +private: + /** + * The number of references from the UnifiedCache, which is + * the number of times that the sharedObject is stored as a hash table value. + * For use by UnifiedCache implementation code only. + * All access is synchronized by UnifiedCache's gCacheMutex + */ + mutable int32_t softRefCount; + friend class UnifiedCache; + + /** + * Reference count, excluding references from within the UnifiedCache implementation. + */ + mutable u_atomic_int32_t hardRefCount; + + mutable const UnifiedCacheBase *cachePtr; + +}; + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/common/simpleformatter.cpp b/deps/icu-small/source/common/simpleformatter.cpp index 01d3024cfc34dc..e0506c4e1db7c9 100644 --- a/deps/icu-small/source/common/simpleformatter.cpp +++ b/deps/icu-small/source/common/simpleformatter.cpp @@ -1,325 +1,325 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 2014-2016, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* simpleformatter.cpp -*/ - -#include "unicode/utypes.h" -#include "unicode/simpleformatter.h" -#include "unicode/unistr.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - -namespace { - -/** - * Argument numbers must be smaller than this limit. - * Text segment lengths are offset by this much. - * This is currently the only unused char value in compiled patterns, - * except it is the maximum value of the first unit (max arg +1). - */ -const int32_t ARG_NUM_LIMIT = 0x100; -/** - * Initial and maximum char/UChar value set for a text segment. - * Segment length char values are from ARG_NUM_LIMIT+1 to this value here. - * Normally 0xffff, but can be as small as ARG_NUM_LIMIT+1 for testing. - */ -const UChar SEGMENT_LENGTH_PLACEHOLDER_CHAR = 0xffff; -/** - * Maximum length of a text segment. Longer segments are split into shorter ones. - */ -const int32_t MAX_SEGMENT_LENGTH = SEGMENT_LENGTH_PLACEHOLDER_CHAR - ARG_NUM_LIMIT; - -enum { - APOS = 0x27, - DIGIT_ZERO = 0x30, - DIGIT_ONE = 0x31, - DIGIT_NINE = 0x39, - OPEN_BRACE = 0x7b, - CLOSE_BRACE = 0x7d -}; - -inline UBool isInvalidArray(const void *array, int32_t length) { - return (length < 0 || (array == NULL && length != 0)); -} - -} // namespace - -SimpleFormatter &SimpleFormatter::operator=(const SimpleFormatter& other) { - if (this == &other) { - return *this; - } - compiledPattern = other.compiledPattern; - return *this; -} - -SimpleFormatter::~SimpleFormatter() {} - -UBool SimpleFormatter::applyPatternMinMaxArguments( - const UnicodeString &pattern, - int32_t min, int32_t max, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { - return false; - } - // Parse consistent with MessagePattern, but - // - support only simple numbered arguments - // - build a simple binary structure into the result string - const UChar *patternBuffer = pattern.getBuffer(); - int32_t patternLength = pattern.length(); - // Reserve the first char for the number of arguments. - compiledPattern.setTo((UChar)0); - int32_t textLength = 0; - int32_t maxArg = -1; - UBool inQuote = false; - for (int32_t i = 0; i < patternLength;) { - UChar c = patternBuffer[i++]; - if (c == APOS) { - if (i < patternLength && (c = patternBuffer[i]) == APOS) { - // double apostrophe, skip the second one - ++i; - } else if (inQuote) { - // skip the quote-ending apostrophe - inQuote = false; - continue; - } else if (c == OPEN_BRACE || c == CLOSE_BRACE) { - // Skip the quote-starting apostrophe, find the end of the quoted literal text. - ++i; - inQuote = true; - } else { - // The apostrophe is part of literal text. - c = APOS; - } - } else if (!inQuote && c == OPEN_BRACE) { - if (textLength > 0) { - compiledPattern.setCharAt(compiledPattern.length() - textLength - 1, - (UChar)(ARG_NUM_LIMIT + textLength)); - textLength = 0; - } - int32_t argNumber; - if ((i + 1) < patternLength && - 0 <= (argNumber = patternBuffer[i] - DIGIT_ZERO) && argNumber <= 9 && - patternBuffer[i + 1] == CLOSE_BRACE) { - i += 2; - } else { - // Multi-digit argument number (no leading zero) or syntax error. - // MessagePattern permits PatternProps.skipWhiteSpace(pattern, index) - // around the number, but this class does not. - argNumber = -1; - if (i < patternLength && DIGIT_ONE <= (c = patternBuffer[i++]) && c <= DIGIT_NINE) { - argNumber = c - DIGIT_ZERO; - while (i < patternLength && - DIGIT_ZERO <= (c = patternBuffer[i++]) && c <= DIGIT_NINE) { - argNumber = argNumber * 10 + (c - DIGIT_ZERO); - if (argNumber >= ARG_NUM_LIMIT) { - break; - } - } - } - if (argNumber < 0 || c != CLOSE_BRACE) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - } - if (argNumber > maxArg) { - maxArg = argNumber; - } - compiledPattern.append((UChar)argNumber); - continue; - } // else: c is part of literal text - // Append c and track the literal-text segment length. - if (textLength == 0) { - // Reserve a char for the length of a new text segment, preset the maximum length. - compiledPattern.append(SEGMENT_LENGTH_PLACEHOLDER_CHAR); - } - compiledPattern.append(c); - if (++textLength == MAX_SEGMENT_LENGTH) { - textLength = 0; - } - } - if (textLength > 0) { - compiledPattern.setCharAt(compiledPattern.length() - textLength - 1, - (UChar)(ARG_NUM_LIMIT + textLength)); - } - int32_t argCount = maxArg + 1; - if (argCount < min || max < argCount) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - compiledPattern.setCharAt(0, (UChar)argCount); - return true; -} - -UnicodeString& SimpleFormatter::format( - const UnicodeString &value0, - UnicodeString &appendTo, UErrorCode &errorCode) const { - const UnicodeString *values[] = { &value0 }; - return formatAndAppend(values, 1, appendTo, NULL, 0, errorCode); -} - -UnicodeString& SimpleFormatter::format( - const UnicodeString &value0, - const UnicodeString &value1, - UnicodeString &appendTo, UErrorCode &errorCode) const { - const UnicodeString *values[] = { &value0, &value1 }; - return formatAndAppend(values, 2, appendTo, NULL, 0, errorCode); -} - -UnicodeString& SimpleFormatter::format( - const UnicodeString &value0, - const UnicodeString &value1, - const UnicodeString &value2, - UnicodeString &appendTo, UErrorCode &errorCode) const { - const UnicodeString *values[] = { &value0, &value1, &value2 }; - return formatAndAppend(values, 3, appendTo, NULL, 0, errorCode); -} - -UnicodeString& SimpleFormatter::formatAndAppend( - const UnicodeString *const *values, int32_t valuesLength, - UnicodeString &appendTo, - int32_t *offsets, int32_t offsetsLength, UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { - return appendTo; - } - if (isInvalidArray(values, valuesLength) || isInvalidArray(offsets, offsetsLength) || - valuesLength < getArgumentLimit()) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return appendTo; - } - return format(compiledPattern.getBuffer(), compiledPattern.length(), values, - appendTo, NULL, true, - offsets, offsetsLength, errorCode); -} - -UnicodeString &SimpleFormatter::formatAndReplace( - const UnicodeString *const *values, int32_t valuesLength, - UnicodeString &result, - int32_t *offsets, int32_t offsetsLength, UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { - return result; - } - if (isInvalidArray(values, valuesLength) || isInvalidArray(offsets, offsetsLength)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return result; - } - const UChar *cp = compiledPattern.getBuffer(); - int32_t cpLength = compiledPattern.length(); - if (valuesLength < getArgumentLimit(cp, cpLength)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return result; - } - - // If the pattern starts with an argument whose value is the same object - // as the result, then we keep the result contents and append to it. - // Otherwise we replace its contents. - int32_t firstArg = -1; - // If any non-initial argument value is the same object as the result, - // then we first copy its contents and use that instead while formatting. - UnicodeString resultCopy; - if (getArgumentLimit(cp, cpLength) > 0) { - for (int32_t i = 1; i < cpLength;) { - int32_t n = cp[i++]; - if (n < ARG_NUM_LIMIT) { - if (values[n] == &result) { - if (i == 2) { - firstArg = n; - } else if (resultCopy.isEmpty() && !result.isEmpty()) { - resultCopy = result; - } - } - } else { - i += n - ARG_NUM_LIMIT; - } - } - } - if (firstArg < 0) { - result.remove(); - } - return format(cp, cpLength, values, - result, &resultCopy, false, - offsets, offsetsLength, errorCode); -} - -UnicodeString SimpleFormatter::getTextWithNoArguments( - const UChar *compiledPattern, - int32_t compiledPatternLength, - int32_t* offsets, - int32_t offsetsLength) { - for (int32_t i = 0; i < offsetsLength; i++) { - offsets[i] = -1; - } - int32_t capacity = compiledPatternLength - 1 - - getArgumentLimit(compiledPattern, compiledPatternLength); - UnicodeString sb(capacity, 0, 0); // Java: StringBuilder - for (int32_t i = 1; i < compiledPatternLength;) { - int32_t n = compiledPattern[i++]; - if (n > ARG_NUM_LIMIT) { - n -= ARG_NUM_LIMIT; - sb.append(compiledPattern + i, n); - i += n; - } else if (n < offsetsLength) { - // TODO(ICU-20406): This does not distinguish between "{0}{1}" and "{1}{0}". - // Consider removing this function and replacing it with an iterator interface. - offsets[n] = sb.length(); - } - } - return sb; -} - -UnicodeString &SimpleFormatter::format( - const UChar *compiledPattern, int32_t compiledPatternLength, - const UnicodeString *const *values, - UnicodeString &result, const UnicodeString *resultCopy, UBool forbidResultAsValue, - int32_t *offsets, int32_t offsetsLength, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { - return result; - } - for (int32_t i = 0; i < offsetsLength; i++) { - offsets[i] = -1; - } - for (int32_t i = 1; i < compiledPatternLength;) { - int32_t n = compiledPattern[i++]; - if (n < ARG_NUM_LIMIT) { - const UnicodeString *value = values[n]; - if (value == NULL) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return result; - } - if (value == &result) { - if (forbidResultAsValue) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return result; - } - if (i == 2) { - // We are appending to result which is also the first value object. - if (n < offsetsLength) { - offsets[n] = 0; - } - } else { - if (n < offsetsLength) { - offsets[n] = result.length(); - } - result.append(*resultCopy); - } - } else { - if (n < offsetsLength) { - offsets[n] = result.length(); - } - result.append(*value); - } - } else { - int32_t length = n - ARG_NUM_LIMIT; - result.append(compiledPattern + i, length); - i += length; - } - } - return result; -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 2014-2016, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* simpleformatter.cpp +*/ + +#include "unicode/utypes.h" +#include "unicode/simpleformatter.h" +#include "unicode/unistr.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +namespace { + +/** + * Argument numbers must be smaller than this limit. + * Text segment lengths are offset by this much. + * This is currently the only unused char value in compiled patterns, + * except it is the maximum value of the first unit (max arg +1). + */ +const int32_t ARG_NUM_LIMIT = 0x100; +/** + * Initial and maximum char/char16_t value set for a text segment. + * Segment length char values are from ARG_NUM_LIMIT+1 to this value here. + * Normally 0xffff, but can be as small as ARG_NUM_LIMIT+1 for testing. + */ +const char16_t SEGMENT_LENGTH_PLACEHOLDER_CHAR = 0xffff; +/** + * Maximum length of a text segment. Longer segments are split into shorter ones. + */ +const int32_t MAX_SEGMENT_LENGTH = SEGMENT_LENGTH_PLACEHOLDER_CHAR - ARG_NUM_LIMIT; + +enum { + APOS = 0x27, + DIGIT_ZERO = 0x30, + DIGIT_ONE = 0x31, + DIGIT_NINE = 0x39, + OPEN_BRACE = 0x7b, + CLOSE_BRACE = 0x7d +}; + +inline UBool isInvalidArray(const void *array, int32_t length) { + return (length < 0 || (array == nullptr && length != 0)); +} + +} // namespace + +SimpleFormatter &SimpleFormatter::operator=(const SimpleFormatter& other) { + if (this == &other) { + return *this; + } + compiledPattern = other.compiledPattern; + return *this; +} + +SimpleFormatter::~SimpleFormatter() {} + +UBool SimpleFormatter::applyPatternMinMaxArguments( + const UnicodeString &pattern, + int32_t min, int32_t max, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { + return false; + } + // Parse consistent with MessagePattern, but + // - support only simple numbered arguments + // - build a simple binary structure into the result string + const char16_t *patternBuffer = pattern.getBuffer(); + int32_t patternLength = pattern.length(); + // Reserve the first char for the number of arguments. + compiledPattern.setTo((char16_t)0); + int32_t textLength = 0; + int32_t maxArg = -1; + UBool inQuote = false; + for (int32_t i = 0; i < patternLength;) { + char16_t c = patternBuffer[i++]; + if (c == APOS) { + if (i < patternLength && (c = patternBuffer[i]) == APOS) { + // double apostrophe, skip the second one + ++i; + } else if (inQuote) { + // skip the quote-ending apostrophe + inQuote = false; + continue; + } else if (c == OPEN_BRACE || c == CLOSE_BRACE) { + // Skip the quote-starting apostrophe, find the end of the quoted literal text. + ++i; + inQuote = true; + } else { + // The apostrophe is part of literal text. + c = APOS; + } + } else if (!inQuote && c == OPEN_BRACE) { + if (textLength > 0) { + compiledPattern.setCharAt(compiledPattern.length() - textLength - 1, + (char16_t)(ARG_NUM_LIMIT + textLength)); + textLength = 0; + } + int32_t argNumber; + if ((i + 1) < patternLength && + 0 <= (argNumber = patternBuffer[i] - DIGIT_ZERO) && argNumber <= 9 && + patternBuffer[i + 1] == CLOSE_BRACE) { + i += 2; + } else { + // Multi-digit argument number (no leading zero) or syntax error. + // MessagePattern permits PatternProps.skipWhiteSpace(pattern, index) + // around the number, but this class does not. + argNumber = -1; + if (i < patternLength && DIGIT_ONE <= (c = patternBuffer[i++]) && c <= DIGIT_NINE) { + argNumber = c - DIGIT_ZERO; + while (i < patternLength && + DIGIT_ZERO <= (c = patternBuffer[i++]) && c <= DIGIT_NINE) { + argNumber = argNumber * 10 + (c - DIGIT_ZERO); + if (argNumber >= ARG_NUM_LIMIT) { + break; + } + } + } + if (argNumber < 0 || c != CLOSE_BRACE) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + } + if (argNumber > maxArg) { + maxArg = argNumber; + } + compiledPattern.append((char16_t)argNumber); + continue; + } // else: c is part of literal text + // Append c and track the literal-text segment length. + if (textLength == 0) { + // Reserve a char for the length of a new text segment, preset the maximum length. + compiledPattern.append(SEGMENT_LENGTH_PLACEHOLDER_CHAR); + } + compiledPattern.append(c); + if (++textLength == MAX_SEGMENT_LENGTH) { + textLength = 0; + } + } + if (textLength > 0) { + compiledPattern.setCharAt(compiledPattern.length() - textLength - 1, + (char16_t)(ARG_NUM_LIMIT + textLength)); + } + int32_t argCount = maxArg + 1; + if (argCount < min || max < argCount) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + compiledPattern.setCharAt(0, (char16_t)argCount); + return true; +} + +UnicodeString& SimpleFormatter::format( + const UnicodeString &value0, + UnicodeString &appendTo, UErrorCode &errorCode) const { + const UnicodeString *values[] = { &value0 }; + return formatAndAppend(values, 1, appendTo, nullptr, 0, errorCode); +} + +UnicodeString& SimpleFormatter::format( + const UnicodeString &value0, + const UnicodeString &value1, + UnicodeString &appendTo, UErrorCode &errorCode) const { + const UnicodeString *values[] = { &value0, &value1 }; + return formatAndAppend(values, 2, appendTo, nullptr, 0, errorCode); +} + +UnicodeString& SimpleFormatter::format( + const UnicodeString &value0, + const UnicodeString &value1, + const UnicodeString &value2, + UnicodeString &appendTo, UErrorCode &errorCode) const { + const UnicodeString *values[] = { &value0, &value1, &value2 }; + return formatAndAppend(values, 3, appendTo, nullptr, 0, errorCode); +} + +UnicodeString& SimpleFormatter::formatAndAppend( + const UnicodeString *const *values, int32_t valuesLength, + UnicodeString &appendTo, + int32_t *offsets, int32_t offsetsLength, UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { + return appendTo; + } + if (isInvalidArray(values, valuesLength) || isInvalidArray(offsets, offsetsLength) || + valuesLength < getArgumentLimit()) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return appendTo; + } + return format(compiledPattern.getBuffer(), compiledPattern.length(), values, + appendTo, nullptr, true, + offsets, offsetsLength, errorCode); +} + +UnicodeString &SimpleFormatter::formatAndReplace( + const UnicodeString *const *values, int32_t valuesLength, + UnicodeString &result, + int32_t *offsets, int32_t offsetsLength, UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { + return result; + } + if (isInvalidArray(values, valuesLength) || isInvalidArray(offsets, offsetsLength)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return result; + } + const char16_t *cp = compiledPattern.getBuffer(); + int32_t cpLength = compiledPattern.length(); + if (valuesLength < getArgumentLimit(cp, cpLength)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return result; + } + + // If the pattern starts with an argument whose value is the same object + // as the result, then we keep the result contents and append to it. + // Otherwise we replace its contents. + int32_t firstArg = -1; + // If any non-initial argument value is the same object as the result, + // then we first copy its contents and use that instead while formatting. + UnicodeString resultCopy; + if (getArgumentLimit(cp, cpLength) > 0) { + for (int32_t i = 1; i < cpLength;) { + int32_t n = cp[i++]; + if (n < ARG_NUM_LIMIT) { + if (values[n] == &result) { + if (i == 2) { + firstArg = n; + } else if (resultCopy.isEmpty() && !result.isEmpty()) { + resultCopy = result; + } + } + } else { + i += n - ARG_NUM_LIMIT; + } + } + } + if (firstArg < 0) { + result.remove(); + } + return format(cp, cpLength, values, + result, &resultCopy, false, + offsets, offsetsLength, errorCode); +} + +UnicodeString SimpleFormatter::getTextWithNoArguments( + const char16_t *compiledPattern, + int32_t compiledPatternLength, + int32_t* offsets, + int32_t offsetsLength) { + for (int32_t i = 0; i < offsetsLength; i++) { + offsets[i] = -1; + } + int32_t capacity = compiledPatternLength - 1 - + getArgumentLimit(compiledPattern, compiledPatternLength); + UnicodeString sb(capacity, 0, 0); // Java: StringBuilder + for (int32_t i = 1; i < compiledPatternLength;) { + int32_t n = compiledPattern[i++]; + if (n > ARG_NUM_LIMIT) { + n -= ARG_NUM_LIMIT; + sb.append(compiledPattern + i, n); + i += n; + } else if (n < offsetsLength) { + // TODO(ICU-20406): This does not distinguish between "{0}{1}" and "{1}{0}". + // Consider removing this function and replacing it with an iterator interface. + offsets[n] = sb.length(); + } + } + return sb; +} + +UnicodeString &SimpleFormatter::format( + const char16_t *compiledPattern, int32_t compiledPatternLength, + const UnicodeString *const *values, + UnicodeString &result, const UnicodeString *resultCopy, UBool forbidResultAsValue, + int32_t *offsets, int32_t offsetsLength, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { + return result; + } + for (int32_t i = 0; i < offsetsLength; i++) { + offsets[i] = -1; + } + for (int32_t i = 1; i < compiledPatternLength;) { + int32_t n = compiledPattern[i++]; + if (n < ARG_NUM_LIMIT) { + const UnicodeString *value = values[n]; + if (value == nullptr) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return result; + } + if (value == &result) { + if (forbidResultAsValue) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return result; + } + if (i == 2) { + // We are appending to result which is also the first value object. + if (n < offsetsLength) { + offsets[n] = 0; + } + } else { + if (n < offsetsLength) { + offsets[n] = result.length(); + } + result.append(*resultCopy); + } + } else { + if (n < offsetsLength) { + offsets[n] = result.length(); + } + result.append(*value); + } + } else { + int32_t length = n - ARG_NUM_LIMIT; + result.append(compiledPattern + i, length); + i += length; + } + } + return result; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/sprpimpl.h b/deps/icu-small/source/common/sprpimpl.h index ca0bcdb51695cc..d6abd0b4e3da42 100644 --- a/deps/icu-small/source/common/sprpimpl.h +++ b/deps/icu-small/source/common/sprpimpl.h @@ -1,130 +1,130 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ******************************************************************************* - * - * Copyright (C) 2003-2006, International Business Machines - * Corporation and others. All Rights Reserved. - * - ******************************************************************************* - * file name: sprpimpl.h - * encoding: UTF-8 - * tab size: 8 (not used) - * indentation:4 - * - * created on: 2003feb1 - * created by: Ram Viswanadha - */ - -#ifndef SPRPIMPL_H -#define SPRPIMPL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_IDNA - -#include "unicode/ustring.h" -#include "unicode/parseerr.h" -#include "unicode/usprep.h" -#include "unicode/udata.h" -#include "utrie.h" -#include "udataswp.h" -#include "ubidi_props.h" - -#define _SPREP_DATA_TYPE "spp" - -enum UStringPrepType{ - USPREP_UNASSIGNED = 0x0000 , - USPREP_MAP = 0x0001 , - USPREP_PROHIBITED = 0x0002 , - USPREP_DELETE = 0x0003 , - USPREP_TYPE_LIMIT = 0x0004 -}; - -typedef enum UStringPrepType UStringPrepType; - -#ifdef USPREP_TYPE_NAMES_ARRAY -static const char* usprepTypeNames[] ={ - "UNASSIGNED" , - "MAP" , - "PROHIBITED" , - "DELETE", - "TYPE_LIMIT" -}; -#endif - -enum{ - _SPREP_NORMALIZATION_ON = 0x0001, - _SPREP_CHECK_BIDI_ON = 0x0002 -}; - -enum{ - _SPREP_TYPE_THRESHOLD = 0xFFF0, - _SPREP_MAX_INDEX_VALUE = 0x3FBF, /*16139*/ - _SPREP_MAX_INDEX_TOP_LENGTH = 0x0003 -}; - -/* indexes[] value names */ -enum { - _SPREP_INDEX_TRIE_SIZE = 0, /* number of bytes in StringPrep trie */ - _SPREP_INDEX_MAPPING_DATA_SIZE = 1, /* The array that contains the mapping */ - _SPREP_NORM_CORRECTNS_LAST_UNI_VERSION = 2, /* The index of Unicode version of last entry in NormalizationCorrections.txt */ - _SPREP_ONE_UCHAR_MAPPING_INDEX_START = 3, /* The starting index of 1 UChar mapping index in the mapping data array */ - _SPREP_TWO_UCHARS_MAPPING_INDEX_START = 4, /* The starting index of 2 UChars mapping index in the mapping data array */ - _SPREP_THREE_UCHARS_MAPPING_INDEX_START = 5, /* The starting index of 3 UChars mapping index in the mapping data array */ - _SPREP_FOUR_UCHARS_MAPPING_INDEX_START = 6, /* The starting index of 4 UChars mapping index in the mapping data array */ - _SPREP_OPTIONS = 7, /* Bit set of options to turn on in the profile */ - _SPREP_INDEX_TOP=16 /* changing this requires a new formatVersion */ -}; - -typedef struct UStringPrepKey UStringPrepKey; - - -struct UStringPrepKey{ - char* name; - char* path; -}; - -struct UStringPrepProfile{ - int32_t indexes[_SPREP_INDEX_TOP]; - UTrie sprepTrie; - const uint16_t* mappingData; - UDataMemory* sprepData; - int32_t refCount; - UBool isDataLoaded; - UBool doNFKC; - UBool checkBiDi; -}; - -/** - * Helper function for populating the UParseError struct - * @internal - */ -U_CAPI void U_EXPORT2 -uprv_syntaxError(const UChar* rules, - int32_t pos, - int32_t rulesLen, - UParseError* parseError); - - -/** - * Swap StringPrep .spp profile data. See udataswp.h. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -usprep_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -#endif /* #if !UCONFIG_NO_IDNA */ - -#endif - -/* - * Hey, Emacs, please set the following: - * - * Local Variables: - * indent-tabs-mode: nil - * End: - * - */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************************* + * + * Copyright (C) 2003-2006, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: sprpimpl.h + * encoding: UTF-8 + * tab size: 8 (not used) + * indentation:4 + * + * created on: 2003feb1 + * created by: Ram Viswanadha + */ + +#ifndef SPRPIMPL_H +#define SPRPIMPL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_IDNA + +#include "unicode/ustring.h" +#include "unicode/parseerr.h" +#include "unicode/usprep.h" +#include "unicode/udata.h" +#include "utrie.h" +#include "udataswp.h" +#include "ubidi_props.h" + +#define _SPREP_DATA_TYPE "spp" + +enum UStringPrepType{ + USPREP_UNASSIGNED = 0x0000 , + USPREP_MAP = 0x0001 , + USPREP_PROHIBITED = 0x0002 , + USPREP_DELETE = 0x0003 , + USPREP_TYPE_LIMIT = 0x0004 +}; + +typedef enum UStringPrepType UStringPrepType; + +#ifdef USPREP_TYPE_NAMES_ARRAY +static const char* usprepTypeNames[] ={ + "UNASSIGNED" , + "MAP" , + "PROHIBITED" , + "DELETE", + "TYPE_LIMIT" +}; +#endif + +enum{ + _SPREP_NORMALIZATION_ON = 0x0001, + _SPREP_CHECK_BIDI_ON = 0x0002 +}; + +enum{ + _SPREP_TYPE_THRESHOLD = 0xFFF0, + _SPREP_MAX_INDEX_VALUE = 0x3FBF, /*16139*/ + _SPREP_MAX_INDEX_TOP_LENGTH = 0x0003 +}; + +/* indexes[] value names */ +enum { + _SPREP_INDEX_TRIE_SIZE = 0, /* number of bytes in StringPrep trie */ + _SPREP_INDEX_MAPPING_DATA_SIZE = 1, /* The array that contains the mapping */ + _SPREP_NORM_CORRECTNS_LAST_UNI_VERSION = 2, /* The index of Unicode version of last entry in NormalizationCorrections.txt */ + _SPREP_ONE_UCHAR_MAPPING_INDEX_START = 3, /* The starting index of 1 UChar mapping index in the mapping data array */ + _SPREP_TWO_UCHARS_MAPPING_INDEX_START = 4, /* The starting index of 2 UChars mapping index in the mapping data array */ + _SPREP_THREE_UCHARS_MAPPING_INDEX_START = 5, /* The starting index of 3 UChars mapping index in the mapping data array */ + _SPREP_FOUR_UCHARS_MAPPING_INDEX_START = 6, /* The starting index of 4 UChars mapping index in the mapping data array */ + _SPREP_OPTIONS = 7, /* Bit set of options to turn on in the profile */ + _SPREP_INDEX_TOP=16 /* changing this requires a new formatVersion */ +}; + +typedef struct UStringPrepKey UStringPrepKey; + + +struct UStringPrepKey{ + char* name; + char* path; +}; + +struct UStringPrepProfile{ + int32_t indexes[_SPREP_INDEX_TOP]; + UTrie sprepTrie; + const uint16_t* mappingData; + UDataMemory* sprepData; + int32_t refCount; + UBool isDataLoaded; + UBool doNFKC; + UBool checkBiDi; +}; + +/** + * Helper function for populating the UParseError struct + * @internal + */ +U_CAPI void U_EXPORT2 +uprv_syntaxError(const UChar* rules, + int32_t pos, + int32_t rulesLen, + UParseError* parseError); + + +/** + * Swap StringPrep .spp profile data. See udataswp.h. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +usprep_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +#endif /* #if !UCONFIG_NO_IDNA */ + +#endif + +/* + * Hey, Emacs, please set the following: + * + * Local Variables: + * indent-tabs-mode: nil + * End: + * + */ diff --git a/deps/icu-small/source/common/static_unicode_sets.cpp b/deps/icu-small/source/common/static_unicode_sets.cpp index db9432f49a8aac..fa3fcc5d0a0d5f 100644 --- a/deps/icu-small/source/common/static_unicode_sets.cpp +++ b/deps/icu-small/source/common/static_unicode_sets.cpp @@ -1,245 +1,245 @@ -// © 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[UNISETS_KEY_COUNT] = {}; - -// Save the empty instance in static memory to have well-defined behavior if a -// regular UnicodeSet cannot be allocated. -alignas(UnicodeSet) -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); - } else if (str.indexOf(u'¥') != -1) { - saveSet(YEN_SIGN, str, status); - } else if (str.indexOf(u'₩') != -1) { - saveSet(WON_SIGN, str, status); - } else if (str.indexOf(u'%') != -1) { - saveSet(PERCENT_SIGN, str, status); - } else if (str.indexOf(u'‰') != -1) { - saveSet(PERMILLE_SIGN, str, status); - } else if (str.indexOf(u'’') != -1) { - saveSet(APOSTROPHE_SIGN, str, status); - } else { - // Unknown class of parse lenients - // TODO(ICU-20428): Make ICU automatically accept new classes? - U_ASSERT(false); - } - if (U_FAILURE(status)) { return; } - } - } - } - } - } -}; - - -icu::UInitOnce gNumberParseUniSetsInitOnce {}; - -UBool U_CALLCONV cleanupNumberParseUniSets() { - if (gEmptyUnicodeSetInitialized) { - reinterpret_cast(gEmptyUnicodeSet)->~UnicodeSet(); - gEmptyUnicodeSetInitialized = false; - } - for (int32_t i = 0; i < UNISETS_KEY_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); - U_ASSERT(gUnicodeSets[APOSTROPHE_SIGN] != nullptr); - - LocalPointer otherGrouping(new UnicodeSet( - u"[٬‘\\u0020\\u00A0\\u2000-\\u200A\\u202F\\u205F\\u3000]", - status - ), status); - if (U_FAILURE(status)) { return; } - otherGrouping->addAll(*gUnicodeSets[APOSTROPHE_SIGN]); - gUnicodeSets[OTHER_GROUPING_SEPARATORS] = otherGrouping.orphan(); - 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); - U_ASSERT(gUnicodeSets[PERCENT_SIGN] != nullptr); - U_ASSERT(gUnicodeSets[PERMILLE_SIGN] != nullptr); - - gUnicodeSets[INFINITY_SIGN] = new UnicodeSet(u"[∞]", status); - if (U_FAILURE(status)) { return; } - - U_ASSERT(gUnicodeSets[DOLLAR_SIGN] != nullptr); - U_ASSERT(gUnicodeSets[POUND_SIGN] != nullptr); - U_ASSERT(gUnicodeSets[RUPEE_SIGN] != nullptr); - U_ASSERT(gUnicodeSets[YEN_SIGN] != nullptr); - U_ASSERT(gUnicodeSets[WON_SIGN] != nullptr); - - gUnicodeSets[DIGITS] = new UnicodeSet(u"[:digit:]", status); - if (U_FAILURE(status)) { return; } - 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 */ +// © 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[UNISETS_KEY_COUNT] = {}; + +// Save the empty instance in static memory to have well-defined behavior if a +// regular UnicodeSet cannot be allocated. +alignas(UnicodeSet) +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) 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); + } else if (str.indexOf(u'¥') != -1) { + saveSet(YEN_SIGN, str, status); + } else if (str.indexOf(u'₩') != -1) { + saveSet(WON_SIGN, str, status); + } else if (str.indexOf(u'%') != -1) { + saveSet(PERCENT_SIGN, str, status); + } else if (str.indexOf(u'‰') != -1) { + saveSet(PERMILLE_SIGN, str, status); + } else if (str.indexOf(u'’') != -1) { + saveSet(APOSTROPHE_SIGN, str, status); + } else { + // Unknown class of parse lenients + // TODO(ICU-20428): Make ICU automatically accept new classes? + U_ASSERT(false); + } + if (U_FAILURE(status)) { return; } + } + } + } + } + } +}; + + +icu::UInitOnce gNumberParseUniSetsInitOnce {}; + +UBool U_CALLCONV cleanupNumberParseUniSets() { + if (gEmptyUnicodeSetInitialized) { + reinterpret_cast(gEmptyUnicodeSet)->~UnicodeSet(); + gEmptyUnicodeSetInitialized = false; + } + for (int32_t i = 0; i < UNISETS_KEY_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); + U_ASSERT(gUnicodeSets[APOSTROPHE_SIGN] != nullptr); + + LocalPointer otherGrouping(new UnicodeSet( + u"[٬‘\\u0020\\u00A0\\u2000-\\u200A\\u202F\\u205F\\u3000]", + status + ), status); + if (U_FAILURE(status)) { return; } + otherGrouping->addAll(*gUnicodeSets[APOSTROPHE_SIGN]); + gUnicodeSets[OTHER_GROUPING_SEPARATORS] = otherGrouping.orphan(); + 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); + U_ASSERT(gUnicodeSets[PERCENT_SIGN] != nullptr); + U_ASSERT(gUnicodeSets[PERMILLE_SIGN] != nullptr); + + gUnicodeSets[INFINITY_SIGN] = new UnicodeSet(u"[∞]", status); + if (U_FAILURE(status)) { return; } + + U_ASSERT(gUnicodeSets[DOLLAR_SIGN] != nullptr); + U_ASSERT(gUnicodeSets[POUND_SIGN] != nullptr); + U_ASSERT(gUnicodeSets[RUPEE_SIGN] != nullptr); + U_ASSERT(gUnicodeSets[YEN_SIGN] != nullptr); + U_ASSERT(gUnicodeSets[WON_SIGN] != nullptr); + + gUnicodeSets[DIGITS] = new UnicodeSet(u"[:digit:]", status); + if (U_FAILURE(status)) { return; } + 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 index 5d90ce5908de98..9a0d0fbd42cd5f 100644 --- a/deps/icu-small/source/common/static_unicode_sets.h +++ b/deps/icu-small/source/common/static_unicode_sets.h @@ -1,140 +1,140 @@ -// © 2018 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// This file contains utilities to deal with static-allocated UnicodeSets. -// -// Common use case: you write a "private static final" UnicodeSet in Java, and -// want something similarly easy in C++. Originally written for number -// parsing, but this header can be used for other applications. -// -// Main entrypoint: `unisets::get(unisets::MY_SET_ID_HERE)` -// -// This file is in common instead of i18n because it is needed by ucurr.cpp. -// -// Author: sffc - -#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, - APOSTROPHE_SIGN, - OTHER_GROUPING_SEPARATORS, - ALL_SEPARATORS, - STRICT_ALL_SEPARATORS, - - // Symbols - MINUS_SIGN, - PLUS_SIGN, - PERCENT_SIGN, - PERMILLE_SIGN, - INFINITY_SIGN, - - // Currency Symbols - DOLLAR_SIGN, - POUND_SIGN, - RUPEE_SIGN, - YEN_SIGN, - WON_SIGN, - - // 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. - UNISETS_KEY_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 - * - * This method is always safe and OK to chain: in the case of a memory or other - * error, it returns an empty set from static memory. - * - * Example: - * - * UBool hasIgnorables = unisets::get(unisets::DEFAULT_IGNORABLES)->contains(...); - * - * @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); - -// TODO: Load these from data: ICU-20108 -// 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'¥'}, - {WON_SIGN, u'₩'}, -}; - -} // namespace unisets -U_NAMESPACE_END - -#endif //__STATIC_UNICODE_SETS_H__ -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// This file contains utilities to deal with static-allocated UnicodeSets. +// +// Common use case: you write a "private static final" UnicodeSet in Java, and +// want something similarly easy in C++. Originally written for number +// parsing, but this header can be used for other applications. +// +// Main entrypoint: `unisets::get(unisets::MY_SET_ID_HERE)` +// +// This file is in common instead of i18n because it is needed by ucurr.cpp. +// +// Author: sffc + +#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, + APOSTROPHE_SIGN, + OTHER_GROUPING_SEPARATORS, + ALL_SEPARATORS, + STRICT_ALL_SEPARATORS, + + // Symbols + MINUS_SIGN, + PLUS_SIGN, + PERCENT_SIGN, + PERMILLE_SIGN, + INFINITY_SIGN, + + // Currency Symbols + DOLLAR_SIGN, + POUND_SIGN, + RUPEE_SIGN, + YEN_SIGN, + WON_SIGN, + + // 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. + UNISETS_KEY_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 + * + * This method is always safe and OK to chain: in the case of a memory or other + * error, it returns an empty set from static memory. + * + * Example: + * + * UBool hasIgnorables = unisets::get(unisets::DEFAULT_IGNORABLES)->contains(...); + * + * @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); + +// TODO: Load these from data: ICU-20108 +// 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'¥'}, + {WON_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/stringpiece.cpp b/deps/icu-small/source/common/stringpiece.cpp index 99089e08ef90f1..74365f8ad4c6f2 100644 --- a/deps/icu-small/source/common/stringpiece.cpp +++ b/deps/icu-small/source/common/stringpiece.cpp @@ -1,116 +1,116 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// Copyright (C) 2009-2013, International Business Machines -// Corporation and others. All Rights Reserved. -// -// Copyright 2004 and onwards Google Inc. -// -// Author: wilsonh@google.com (Wilson Hsieh) -// - -#include "unicode/utypes.h" -#include "unicode/stringpiece.h" -#include "cstring.h" -#include "cmemory.h" - -U_NAMESPACE_BEGIN - -StringPiece::StringPiece(const char* str) - : ptr_(str), length_((str == NULL) ? 0 : static_cast(uprv_strlen(str))) { } - -StringPiece::StringPiece(const StringPiece& x, int32_t pos) { - if (pos < 0) { - pos = 0; - } else if (pos > x.length_) { - pos = x.length_; - } - ptr_ = x.ptr_ + pos; - length_ = x.length_ - pos; -} - -StringPiece::StringPiece(const StringPiece& x, int32_t pos, int32_t len) { - if (pos < 0) { - pos = 0; - } else if (pos > x.length_) { - pos = x.length_; - } - if (len < 0) { - len = 0; - } else if (len > x.length_ - pos) { - len = x.length_ - pos; - } - ptr_ = x.ptr_ + pos; - length_ = len; -} - -void StringPiece::set(const char* str) { - ptr_ = str; - if (str != NULL) - length_ = static_cast(uprv_strlen(str)); - else - length_ = 0; -} - -int32_t StringPiece::find(StringPiece needle, int32_t offset) { - if (length() == 0 && needle.length() == 0) { - return 0; - } - // TODO: Improve to be better than O(N^2)? - for (int32_t i = offset; i < length(); i++) { - int32_t j = 0; - for (; j < needle.length(); i++, j++) { - if (data()[i] != needle.data()[j]) { - i -= j; - goto outer_end; - } - } - return i - j; - outer_end: void(); - } - return -1; -} - -int32_t StringPiece::compare(StringPiece other) { - int32_t i = 0; - for (; i < length(); i++) { - if (i == other.length()) { - // this is longer - return 1; - } - char a = data()[i]; - char b = other.data()[i]; - if (a < b) { - return -1; - } else if (a > b) { - return 1; - } - } - if (i < other.length()) { - // other is longer - return -1; - } - return 0; -} - -U_EXPORT UBool U_EXPORT2 -operator==(const StringPiece& x, const StringPiece& y) { - int32_t len = x.size(); - if (len != y.size()) { - return false; - } - if (len == 0) { - return true; - } - const char* p = x.data(); - const char* p2 = y.data(); - // Test last byte in case strings share large common prefix - --len; - if (p[len] != p2[len]) return false; - // At this point we can, but don't have to, ignore the last byte. - return uprv_memcmp(p, p2, len) == 0; -} - - -const int32_t StringPiece::npos = 0x7fffffff; - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// Copyright (C) 2009-2013, International Business Machines +// Corporation and others. All Rights Reserved. +// +// Copyright 2004 and onwards Google Inc. +// +// Author: wilsonh@google.com (Wilson Hsieh) +// + +#include "unicode/utypes.h" +#include "unicode/stringpiece.h" +#include "cstring.h" +#include "cmemory.h" + +U_NAMESPACE_BEGIN + +StringPiece::StringPiece(const char* str) + : ptr_(str), length_((str == nullptr) ? 0 : static_cast(uprv_strlen(str))) { } + +StringPiece::StringPiece(const StringPiece& x, int32_t pos) { + if (pos < 0) { + pos = 0; + } else if (pos > x.length_) { + pos = x.length_; + } + ptr_ = x.ptr_ + pos; + length_ = x.length_ - pos; +} + +StringPiece::StringPiece(const StringPiece& x, int32_t pos, int32_t len) { + if (pos < 0) { + pos = 0; + } else if (pos > x.length_) { + pos = x.length_; + } + if (len < 0) { + len = 0; + } else if (len > x.length_ - pos) { + len = x.length_ - pos; + } + ptr_ = x.ptr_ + pos; + length_ = len; +} + +void StringPiece::set(const char* str) { + ptr_ = str; + if (str != nullptr) + length_ = static_cast(uprv_strlen(str)); + else + length_ = 0; +} + +int32_t StringPiece::find(StringPiece needle, int32_t offset) { + if (length() == 0 && needle.length() == 0) { + return 0; + } + // TODO: Improve to be better than O(N^2)? + for (int32_t i = offset; i < length(); i++) { + int32_t j = 0; + for (; j < needle.length(); i++, j++) { + if (data()[i] != needle.data()[j]) { + i -= j; + goto outer_end; + } + } + return i - j; + outer_end: void(); + } + return -1; +} + +int32_t StringPiece::compare(StringPiece other) { + int32_t i = 0; + for (; i < length(); i++) { + if (i == other.length()) { + // this is longer + return 1; + } + char a = data()[i]; + char b = other.data()[i]; + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } + } + if (i < other.length()) { + // other is longer + return -1; + } + return 0; +} + +U_EXPORT UBool U_EXPORT2 +operator==(const StringPiece& x, const StringPiece& y) { + int32_t len = x.size(); + if (len != y.size()) { + return false; + } + if (len == 0) { + return true; + } + const char* p = x.data(); + const char* p2 = y.data(); + // Test last byte in case strings share large common prefix + --len; + if (p[len] != p2[len]) return false; + // At this point we can, but don't have to, ignore the last byte. + return uprv_memcmp(p, p2, len) == 0; +} + + +const int32_t StringPiece::npos = 0x7fffffff; + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/stringtriebuilder.cpp b/deps/icu-small/source/common/stringtriebuilder.cpp index e6670d1cb71553..2b2e9806b087c0 100644 --- a/deps/icu-small/source/common/stringtriebuilder.cpp +++ b/deps/icu-small/source/common/stringtriebuilder.cpp @@ -1,618 +1,618 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2012, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: stringtriebuilder.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010dec24 -* created by: Markus W. Scherer -*/ - -#include "utypeinfo.h" // for 'typeid' to work -#include "unicode/utypes.h" -#include "unicode/stringtriebuilder.h" -#include "uassert.h" -#include "uhash.h" - -U_CDECL_BEGIN - -static int32_t U_CALLCONV -hashStringTrieNode(const UHashTok key) { - return icu::StringTrieBuilder::hashNode(key.pointer); -} - -static UBool U_CALLCONV -equalStringTrieNodes(const UHashTok key1, const UHashTok key2) { - return icu::StringTrieBuilder::equalNodes(key1.pointer, key2.pointer); -} - -U_CDECL_END - -U_NAMESPACE_BEGIN - -StringTrieBuilder::StringTrieBuilder() : nodes(NULL) {} - -StringTrieBuilder::~StringTrieBuilder() { - deleteCompactBuilder(); -} - -void -StringTrieBuilder::createCompactBuilder(int32_t sizeGuess, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return; - } - nodes=uhash_openSize(hashStringTrieNode, equalStringTrieNodes, NULL, - sizeGuess, &errorCode); - if(U_SUCCESS(errorCode)) { - if(nodes==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } else { - uhash_setKeyDeleter(nodes, uprv_deleteUObject); - } - } -} - -void -StringTrieBuilder::deleteCompactBuilder() { - uhash_close(nodes); - nodes=NULL; -} - -void -StringTrieBuilder::build(UStringTrieBuildOption buildOption, int32_t elementsLength, - UErrorCode &errorCode) { - if(buildOption==USTRINGTRIE_BUILD_FAST) { - writeNode(0, elementsLength, 0); - } else /* USTRINGTRIE_BUILD_SMALL */ { - createCompactBuilder(2*elementsLength, errorCode); - Node *root=makeNode(0, elementsLength, 0, errorCode); - if(U_SUCCESS(errorCode)) { - root->markRightEdgesFirst(-1); - root->write(*this); - } - deleteCompactBuilder(); - } -} - -// Requires startmaxLinearMatchLength) { - lastUnitIndex-=maxLinearMatchLength; - length-=maxLinearMatchLength; - writeElementUnits(start, lastUnitIndex, maxLinearMatchLength); - write(getMinLinearMatch()+maxLinearMatchLength-1); - } - writeElementUnits(start, unitIndex, length); - type=getMinLinearMatch()+length-1; - } else { - // Branch node. - int32_t length=countElementUnits(start, limit, unitIndex); - // length>=2 because minUnit!=maxUnit. - writeBranchSubNode(start, limit, unitIndex, length); - if(--lengthgetMaxBranchLinearSubNodeLength()) { - // Branch on the middle unit. - // First, find the middle unit. - int32_t i=skipElementsBySomeUnits(start, unitIndex, length/2); - // Encode the less-than branch first. - middleUnits[ltLength]=getElementUnit(i, unitIndex); // middle unit - lessThan[ltLength]=writeBranchSubNode(start, i, unitIndex, length/2); - ++ltLength; - // Continue for the greater-or-equal branch. - start=i; - length=length-length/2; - } - // For each unit, find its elements array start and whether it has a final value. - int32_t starts[kMaxBranchLinearSubNodeLength]; - UBool isFinal[kMaxBranchLinearSubNodeLength-1]; - int32_t unitNumber=0; - do { - int32_t i=starts[unitNumber]=start; - UChar unit=getElementUnit(i++, unitIndex); - i=indexOfElementWithNextUnit(i, unitIndex, unit); - isFinal[unitNumber]= start==i-1 && unitIndex+1==getElementStringLength(start); - start=i; - } while(++unitNumber0); - // The maxUnit sub-node is written as the very last one because we do - // not jump for it at all. - unitNumber=length-1; - writeNode(start, limit, unitIndex+1); - int32_t offset=write(getElementUnit(start, unitIndex)); - // Write the rest of this node's unit-value pairs. - while(--unitNumber>=0) { - start=starts[unitNumber]; - int32_t value; - if(isFinal[unitNumber]) { - // Write the final value for the one string ending with this unit. - value=getElementValue(start); - } else { - // Write the delta to the start position of the sub-node. - value=offset-jumpTargets[unitNumber]; - } - writeValueAndFinal(value, isFinal[unitNumber]); - offset=write(getElementUnit(start, unitIndex)); - } - // Write the split-branch nodes. - while(ltLength>0) { - --ltLength; - writeDeltaTo(lessThan[ltLength]); - offset=write(middleUnits[ltLength]); - } - return offset; -} - -// Requires startmaxLinearMatchLength) { - lastUnitIndex-=maxLinearMatchLength; - length-=maxLinearMatchLength; - node=createLinearMatchNode(start, lastUnitIndex, maxLinearMatchLength, nextNode); - nextNode=registerNode(node, errorCode); - } - node=createLinearMatchNode(start, unitIndex, length, nextNode); - } else { - // Branch node. - int32_t length=countElementUnits(start, limit, unitIndex); - // length>=2 because minUnit!=maxUnit. - Node *subNode=makeBranchSubNode(start, limit, unitIndex, length, errorCode); - node=new BranchHeadNode(length, subNode); - } - if(hasValue && node!=NULL) { - if(matchNodesCanHaveValues()) { - ((ValueNode *)node)->setValue(value); - } else { - node=new IntermediateValueNode(value, registerNode(node, errorCode)); - } - } - return registerNode(node, errorCode); -} - -// startgetMaxBranchLinearSubNodeLength()) { - // Branch on the middle unit. - // First, find the middle unit. - int32_t i=skipElementsBySomeUnits(start, unitIndex, length/2); - // Create the less-than branch. - middleUnits[ltLength]=getElementUnit(i, unitIndex); // middle unit - lessThan[ltLength]=makeBranchSubNode(start, i, unitIndex, length/2, errorCode); - ++ltLength; - // Continue for the greater-or-equal branch. - start=i; - length=length-length/2; - } - if(U_FAILURE(errorCode)) { - return NULL; - } - ListBranchNode *listNode=new ListBranchNode(); - if(listNode==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - // For each unit, find its elements array start and whether it has a final value. - int32_t unitNumber=0; - do { - int32_t i=start; - UChar unit=getElementUnit(i++, unitIndex); - i=indexOfElementWithNextUnit(i, unitIndex, unit); - if(start==i-1 && unitIndex+1==getElementStringLength(start)) { - listNode->add(unit, getElementValue(start)); - } else { - listNode->add(unit, makeNode(start, i, unitIndex+1, errorCode)); - } - start=i; - } while(++unitNumberadd(unit, getElementValue(start)); - } else { - listNode->add(unit, makeNode(start, limit, unitIndex+1, errorCode)); - } - Node *node=registerNode(listNode, errorCode); - // Create the split-branch nodes. - while(ltLength>0) { - --ltLength; - node=registerNode( - new SplitBranchNode(middleUnits[ltLength], lessThan[ltLength], node), errorCode); - } - return node; -} - -StringTrieBuilder::Node * -StringTrieBuilder::registerNode(Node *newNode, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - delete newNode; - return NULL; - } - if(newNode==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - const UHashElement *old=uhash_find(nodes, newNode); - if(old!=NULL) { - delete newNode; - return (Node *)old->key.pointer; - } - // If uhash_puti() returns a non-zero value from an equivalent, previously - // registered node, then uhash_find() failed to find that and we will leak newNode. -#if U_DEBUG - int32_t oldValue= // Only in debug mode to avoid a compiler warning about unused oldValue. -#endif - uhash_puti(nodes, newNode, 1, &errorCode); - U_ASSERT(oldValue==0); - if(U_FAILURE(errorCode)) { - delete newNode; - return NULL; - } - return newNode; -} - -StringTrieBuilder::Node * -StringTrieBuilder::registerFinalValue(int32_t value, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return NULL; - } - FinalValueNode key(value); - const UHashElement *old=uhash_find(nodes, &key); - if(old!=NULL) { - return (Node *)old->key.pointer; - } - Node *newNode=new FinalValueNode(value); - if(newNode==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - // If uhash_puti() returns a non-zero value from an equivalent, previously - // registered node, then uhash_find() failed to find that and we will leak newNode. -#if U_DEBUG - int32_t oldValue= // Only in debug mode to avoid a compiler warning about unused oldValue. -#endif - uhash_puti(nodes, newNode, 1, &errorCode); - U_ASSERT(oldValue==0); - if(U_FAILURE(errorCode)) { - delete newNode; - return NULL; - } - return newNode; -} - -int32_t -StringTrieBuilder::hashNode(const void *node) { - return ((const Node *)node)->hashCode(); -} - -UBool -StringTrieBuilder::equalNodes(const void *left, const void *right) { - return *(const Node *)left==*(const Node *)right; -} - -bool -StringTrieBuilder::Node::operator==(const Node &other) const { - return this==&other || (typeid(*this)==typeid(other) && hash==other.hash); -} - -int32_t -StringTrieBuilder::Node::markRightEdgesFirst(int32_t edgeNumber) { - if(offset==0) { - offset=edgeNumber; - } - return edgeNumber; -} - -bool -StringTrieBuilder::FinalValueNode::operator==(const Node &other) const { - if(this==&other) { - return true; - } - if(!Node::operator==(other)) { - return false; - } - const FinalValueNode &o=(const FinalValueNode &)other; - return value==o.value; -} - -void -StringTrieBuilder::FinalValueNode::write(StringTrieBuilder &builder) { - offset=builder.writeValueAndFinal(value, true); -} - -bool -StringTrieBuilder::ValueNode::operator==(const Node &other) const { - if(this==&other) { - return true; - } - if(!Node::operator==(other)) { - return false; - } - const ValueNode &o=(const ValueNode &)other; - return hasValue==o.hasValue && (!hasValue || value==o.value); -} - -bool -StringTrieBuilder::IntermediateValueNode::operator==(const Node &other) const { - if(this==&other) { - return true; - } - if(!ValueNode::operator==(other)) { - return false; - } - const IntermediateValueNode &o=(const IntermediateValueNode &)other; - return next==o.next; -} - -int32_t -StringTrieBuilder::IntermediateValueNode::markRightEdgesFirst(int32_t edgeNumber) { - if(offset==0) { - offset=edgeNumber=next->markRightEdgesFirst(edgeNumber); - } - return edgeNumber; -} - -void -StringTrieBuilder::IntermediateValueNode::write(StringTrieBuilder &builder) { - next->write(builder); - offset=builder.writeValueAndFinal(value, false); -} - -bool -StringTrieBuilder::LinearMatchNode::operator==(const Node &other) const { - if(this==&other) { - return true; - } - if(!ValueNode::operator==(other)) { - return false; - } - const LinearMatchNode &o=(const LinearMatchNode &)other; - return length==o.length && next==o.next; -} - -int32_t -StringTrieBuilder::LinearMatchNode::markRightEdgesFirst(int32_t edgeNumber) { - if(offset==0) { - offset=edgeNumber=next->markRightEdgesFirst(edgeNumber); - } - return edgeNumber; -} - -bool -StringTrieBuilder::ListBranchNode::operator==(const Node &other) const { - if(this==&other) { - return true; - } - if(!Node::operator==(other)) { - return false; - } - const ListBranchNode &o=(const ListBranchNode &)other; - for(int32_t i=0; imarkRightEdgesFirst(edgeNumber-step); - } - // For all but the rightmost edge, decrement the edge number. - step=1; - } while(i>0); - offset=edgeNumber; - } - return edgeNumber; -} - -void -StringTrieBuilder::ListBranchNode::write(StringTrieBuilder &builder) { - // Write the sub-nodes in reverse order: The jump lengths are deltas from - // after their own positions, so if we wrote the minUnit sub-node first, - // then its jump delta would be larger. - // Instead we write the minUnit sub-node last, for a shorter delta. - int32_t unitNumber=length-1; - Node *rightEdge=equal[unitNumber]; - int32_t rightEdgeNumber= rightEdge==NULL ? firstEdgeNumber : rightEdge->getOffset(); - do { - --unitNumber; - if(equal[unitNumber]!=NULL) { - equal[unitNumber]->writeUnlessInsideRightEdge(firstEdgeNumber, rightEdgeNumber, builder); - } - } while(unitNumber>0); - // The maxUnit sub-node is written as the very last one because we do - // not jump for it at all. - unitNumber=length-1; - if(rightEdge==NULL) { - builder.writeValueAndFinal(values[unitNumber], true); - } else { - rightEdge->write(builder); - } - offset=builder.write(units[unitNumber]); - // Write the rest of this node's unit-value pairs. - while(--unitNumber>=0) { - int32_t value; - UBool isFinal; - if(equal[unitNumber]==NULL) { - // Write the final value for the one string ending with this unit. - value=values[unitNumber]; - isFinal=true; - } else { - // Write the delta to the start position of the sub-node. - U_ASSERT(equal[unitNumber]->getOffset()>0); - value=offset-equal[unitNumber]->getOffset(); - isFinal=false; - } - builder.writeValueAndFinal(value, isFinal); - offset=builder.write(units[unitNumber]); - } -} - -bool -StringTrieBuilder::SplitBranchNode::operator==(const Node &other) const { - if(this==&other) { - return true; - } - if(!Node::operator==(other)) { - return false; - } - const SplitBranchNode &o=(const SplitBranchNode &)other; - return unit==o.unit && lessThan==o.lessThan && greaterOrEqual==o.greaterOrEqual; -} - -int32_t -StringTrieBuilder::SplitBranchNode::markRightEdgesFirst(int32_t edgeNumber) { - if(offset==0) { - firstEdgeNumber=edgeNumber; - edgeNumber=greaterOrEqual->markRightEdgesFirst(edgeNumber); - offset=edgeNumber=lessThan->markRightEdgesFirst(edgeNumber-1); - } - return edgeNumber; -} - -void -StringTrieBuilder::SplitBranchNode::write(StringTrieBuilder &builder) { - // Encode the less-than branch first. - lessThan->writeUnlessInsideRightEdge(firstEdgeNumber, greaterOrEqual->getOffset(), builder); - // Encode the greater-or-equal branch last because we do not jump for it at all. - greaterOrEqual->write(builder); - // Write this node. - U_ASSERT(lessThan->getOffset()>0); - builder.writeDeltaTo(lessThan->getOffset()); // less-than - offset=builder.write(unit); -} - -bool -StringTrieBuilder::BranchHeadNode::operator==(const Node &other) const { - if(this==&other) { - return true; - } - if(!ValueNode::operator==(other)) { - return false; - } - const BranchHeadNode &o=(const BranchHeadNode &)other; - return length==o.length && next==o.next; -} - -int32_t -StringTrieBuilder::BranchHeadNode::markRightEdgesFirst(int32_t edgeNumber) { - if(offset==0) { - offset=edgeNumber=next->markRightEdgesFirst(edgeNumber); - } - return edgeNumber; -} - -void -StringTrieBuilder::BranchHeadNode::write(StringTrieBuilder &builder) { - next->write(builder); - if(length<=builder.getMinLinearMatch()) { - offset=builder.writeValueAndType(hasValue, value, length-1); - } else { - builder.write(length-1); - offset=builder.writeValueAndType(hasValue, value, 0); - } -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2012, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: stringtriebuilder.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010dec24 +* created by: Markus W. Scherer +*/ + +#include "utypeinfo.h" // for 'typeid' to work +#include "unicode/utypes.h" +#include "unicode/stringtriebuilder.h" +#include "uassert.h" +#include "uhash.h" + +U_CDECL_BEGIN + +static int32_t U_CALLCONV +hashStringTrieNode(const UHashTok key) { + return icu::StringTrieBuilder::hashNode(key.pointer); +} + +static UBool U_CALLCONV +equalStringTrieNodes(const UHashTok key1, const UHashTok key2) { + return icu::StringTrieBuilder::equalNodes(key1.pointer, key2.pointer); +} + +U_CDECL_END + +U_NAMESPACE_BEGIN + +StringTrieBuilder::StringTrieBuilder() : nodes(nullptr) {} + +StringTrieBuilder::~StringTrieBuilder() { + deleteCompactBuilder(); +} + +void +StringTrieBuilder::createCompactBuilder(int32_t sizeGuess, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return; + } + nodes=uhash_openSize(hashStringTrieNode, equalStringTrieNodes, nullptr, + sizeGuess, &errorCode); + if(U_SUCCESS(errorCode)) { + if(nodes==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } else { + uhash_setKeyDeleter(nodes, uprv_deleteUObject); + } + } +} + +void +StringTrieBuilder::deleteCompactBuilder() { + uhash_close(nodes); + nodes=nullptr; +} + +void +StringTrieBuilder::build(UStringTrieBuildOption buildOption, int32_t elementsLength, + UErrorCode &errorCode) { + if(buildOption==USTRINGTRIE_BUILD_FAST) { + writeNode(0, elementsLength, 0); + } else /* USTRINGTRIE_BUILD_SMALL */ { + createCompactBuilder(2*elementsLength, errorCode); + Node *root=makeNode(0, elementsLength, 0, errorCode); + if(U_SUCCESS(errorCode)) { + root->markRightEdgesFirst(-1); + root->write(*this); + } + deleteCompactBuilder(); + } +} + +// Requires startmaxLinearMatchLength) { + lastUnitIndex-=maxLinearMatchLength; + length-=maxLinearMatchLength; + writeElementUnits(start, lastUnitIndex, maxLinearMatchLength); + write(getMinLinearMatch()+maxLinearMatchLength-1); + } + writeElementUnits(start, unitIndex, length); + type=getMinLinearMatch()+length-1; + } else { + // Branch node. + int32_t length=countElementUnits(start, limit, unitIndex); + // length>=2 because minUnit!=maxUnit. + writeBranchSubNode(start, limit, unitIndex, length); + if(--lengthgetMaxBranchLinearSubNodeLength()) { + // Branch on the middle unit. + // First, find the middle unit. + int32_t i=skipElementsBySomeUnits(start, unitIndex, length/2); + // Encode the less-than branch first. + middleUnits[ltLength]=getElementUnit(i, unitIndex); // middle unit + lessThan[ltLength]=writeBranchSubNode(start, i, unitIndex, length/2); + ++ltLength; + // Continue for the greater-or-equal branch. + start=i; + length=length-length/2; + } + // For each unit, find its elements array start and whether it has a final value. + int32_t starts[kMaxBranchLinearSubNodeLength]; + UBool isFinal[kMaxBranchLinearSubNodeLength-1]; + int32_t unitNumber=0; + do { + int32_t i=starts[unitNumber]=start; + char16_t unit=getElementUnit(i++, unitIndex); + i=indexOfElementWithNextUnit(i, unitIndex, unit); + isFinal[unitNumber]= start==i-1 && unitIndex+1==getElementStringLength(start); + start=i; + } while(++unitNumber0); + // The maxUnit sub-node is written as the very last one because we do + // not jump for it at all. + unitNumber=length-1; + writeNode(start, limit, unitIndex+1); + int32_t offset=write(getElementUnit(start, unitIndex)); + // Write the rest of this node's unit-value pairs. + while(--unitNumber>=0) { + start=starts[unitNumber]; + int32_t value; + if(isFinal[unitNumber]) { + // Write the final value for the one string ending with this unit. + value=getElementValue(start); + } else { + // Write the delta to the start position of the sub-node. + value=offset-jumpTargets[unitNumber]; + } + writeValueAndFinal(value, isFinal[unitNumber]); + offset=write(getElementUnit(start, unitIndex)); + } + // Write the split-branch nodes. + while(ltLength>0) { + --ltLength; + writeDeltaTo(lessThan[ltLength]); + offset=write(middleUnits[ltLength]); + } + return offset; +} + +// Requires startmaxLinearMatchLength) { + lastUnitIndex-=maxLinearMatchLength; + length-=maxLinearMatchLength; + node=createLinearMatchNode(start, lastUnitIndex, maxLinearMatchLength, nextNode); + nextNode=registerNode(node, errorCode); + } + node=createLinearMatchNode(start, unitIndex, length, nextNode); + } else { + // Branch node. + int32_t length=countElementUnits(start, limit, unitIndex); + // length>=2 because minUnit!=maxUnit. + Node *subNode=makeBranchSubNode(start, limit, unitIndex, length, errorCode); + node=new BranchHeadNode(length, subNode); + } + if(hasValue && node!=nullptr) { + if(matchNodesCanHaveValues()) { + ((ValueNode *)node)->setValue(value); + } else { + node=new IntermediateValueNode(value, registerNode(node, errorCode)); + } + } + return registerNode(node, errorCode); +} + +// startgetMaxBranchLinearSubNodeLength()) { + // Branch on the middle unit. + // First, find the middle unit. + int32_t i=skipElementsBySomeUnits(start, unitIndex, length/2); + // Create the less-than branch. + middleUnits[ltLength]=getElementUnit(i, unitIndex); // middle unit + lessThan[ltLength]=makeBranchSubNode(start, i, unitIndex, length/2, errorCode); + ++ltLength; + // Continue for the greater-or-equal branch. + start=i; + length=length-length/2; + } + if(U_FAILURE(errorCode)) { + return nullptr; + } + ListBranchNode *listNode=new ListBranchNode(); + if(listNode==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + // For each unit, find its elements array start and whether it has a final value. + int32_t unitNumber=0; + do { + int32_t i=start; + char16_t unit=getElementUnit(i++, unitIndex); + i=indexOfElementWithNextUnit(i, unitIndex, unit); + if(start==i-1 && unitIndex+1==getElementStringLength(start)) { + listNode->add(unit, getElementValue(start)); + } else { + listNode->add(unit, makeNode(start, i, unitIndex+1, errorCode)); + } + start=i; + } while(++unitNumberadd(unit, getElementValue(start)); + } else { + listNode->add(unit, makeNode(start, limit, unitIndex+1, errorCode)); + } + Node *node=registerNode(listNode, errorCode); + // Create the split-branch nodes. + while(ltLength>0) { + --ltLength; + node=registerNode( + new SplitBranchNode(middleUnits[ltLength], lessThan[ltLength], node), errorCode); + } + return node; +} + +StringTrieBuilder::Node * +StringTrieBuilder::registerNode(Node *newNode, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + delete newNode; + return nullptr; + } + if(newNode==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + const UHashElement *old=uhash_find(nodes, newNode); + if(old!=nullptr) { + delete newNode; + return (Node *)old->key.pointer; + } + // If uhash_puti() returns a non-zero value from an equivalent, previously + // registered node, then uhash_find() failed to find that and we will leak newNode. +#if U_DEBUG + int32_t oldValue= // Only in debug mode to avoid a compiler warning about unused oldValue. +#endif + uhash_puti(nodes, newNode, 1, &errorCode); + U_ASSERT(oldValue==0); + if(U_FAILURE(errorCode)) { + delete newNode; + return nullptr; + } + return newNode; +} + +StringTrieBuilder::Node * +StringTrieBuilder::registerFinalValue(int32_t value, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return nullptr; + } + FinalValueNode key(value); + const UHashElement *old=uhash_find(nodes, &key); + if(old!=nullptr) { + return (Node *)old->key.pointer; + } + Node *newNode=new FinalValueNode(value); + if(newNode==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + // If uhash_puti() returns a non-zero value from an equivalent, previously + // registered node, then uhash_find() failed to find that and we will leak newNode. +#if U_DEBUG + int32_t oldValue= // Only in debug mode to avoid a compiler warning about unused oldValue. +#endif + uhash_puti(nodes, newNode, 1, &errorCode); + U_ASSERT(oldValue==0); + if(U_FAILURE(errorCode)) { + delete newNode; + return nullptr; + } + return newNode; +} + +int32_t +StringTrieBuilder::hashNode(const void *node) { + return ((const Node *)node)->hashCode(); +} + +UBool +StringTrieBuilder::equalNodes(const void *left, const void *right) { + return *(const Node *)left==*(const Node *)right; +} + +bool +StringTrieBuilder::Node::operator==(const Node &other) const { + return this==&other || (typeid(*this)==typeid(other) && hash==other.hash); +} + +int32_t +StringTrieBuilder::Node::markRightEdgesFirst(int32_t edgeNumber) { + if(offset==0) { + offset=edgeNumber; + } + return edgeNumber; +} + +bool +StringTrieBuilder::FinalValueNode::operator==(const Node &other) const { + if(this==&other) { + return true; + } + if(!Node::operator==(other)) { + return false; + } + const FinalValueNode &o=static_cast(other); + return value==o.value; +} + +void +StringTrieBuilder::FinalValueNode::write(StringTrieBuilder &builder) { + offset=builder.writeValueAndFinal(value, true); +} + +bool +StringTrieBuilder::ValueNode::operator==(const Node &other) const { + if(this==&other) { + return true; + } + if(!Node::operator==(other)) { + return false; + } + const ValueNode &o=static_cast(other); + return hasValue==o.hasValue && (!hasValue || value==o.value); +} + +bool +StringTrieBuilder::IntermediateValueNode::operator==(const Node &other) const { + if(this==&other) { + return true; + } + if(!ValueNode::operator==(other)) { + return false; + } + const IntermediateValueNode &o=static_cast(other); + return next==o.next; +} + +int32_t +StringTrieBuilder::IntermediateValueNode::markRightEdgesFirst(int32_t edgeNumber) { + if(offset==0) { + offset=edgeNumber=next->markRightEdgesFirst(edgeNumber); + } + return edgeNumber; +} + +void +StringTrieBuilder::IntermediateValueNode::write(StringTrieBuilder &builder) { + next->write(builder); + offset=builder.writeValueAndFinal(value, false); +} + +bool +StringTrieBuilder::LinearMatchNode::operator==(const Node &other) const { + if(this==&other) { + return true; + } + if(!ValueNode::operator==(other)) { + return false; + } + const LinearMatchNode &o=static_cast(other); + return length==o.length && next==o.next; +} + +int32_t +StringTrieBuilder::LinearMatchNode::markRightEdgesFirst(int32_t edgeNumber) { + if(offset==0) { + offset=edgeNumber=next->markRightEdgesFirst(edgeNumber); + } + return edgeNumber; +} + +bool +StringTrieBuilder::ListBranchNode::operator==(const Node &other) const { + if(this==&other) { + return true; + } + if(!Node::operator==(other)) { + return false; + } + const ListBranchNode &o=static_cast(other); + for(int32_t i=0; imarkRightEdgesFirst(edgeNumber-step); + } + // For all but the rightmost edge, decrement the edge number. + step=1; + } while(i>0); + offset=edgeNumber; + } + return edgeNumber; +} + +void +StringTrieBuilder::ListBranchNode::write(StringTrieBuilder &builder) { + // Write the sub-nodes in reverse order: The jump lengths are deltas from + // after their own positions, so if we wrote the minUnit sub-node first, + // then its jump delta would be larger. + // Instead we write the minUnit sub-node last, for a shorter delta. + int32_t unitNumber=length-1; + Node *rightEdge=equal[unitNumber]; + int32_t rightEdgeNumber= rightEdge==nullptr ? firstEdgeNumber : rightEdge->getOffset(); + do { + --unitNumber; + if(equal[unitNumber]!=nullptr) { + equal[unitNumber]->writeUnlessInsideRightEdge(firstEdgeNumber, rightEdgeNumber, builder); + } + } while(unitNumber>0); + // The maxUnit sub-node is written as the very last one because we do + // not jump for it at all. + unitNumber=length-1; + if(rightEdge==nullptr) { + builder.writeValueAndFinal(values[unitNumber], true); + } else { + rightEdge->write(builder); + } + offset=builder.write(units[unitNumber]); + // Write the rest of this node's unit-value pairs. + while(--unitNumber>=0) { + int32_t value; + UBool isFinal; + if(equal[unitNumber]==nullptr) { + // Write the final value for the one string ending with this unit. + value=values[unitNumber]; + isFinal=true; + } else { + // Write the delta to the start position of the sub-node. + U_ASSERT(equal[unitNumber]->getOffset()>0); + value=offset-equal[unitNumber]->getOffset(); + isFinal=false; + } + builder.writeValueAndFinal(value, isFinal); + offset=builder.write(units[unitNumber]); + } +} + +bool +StringTrieBuilder::SplitBranchNode::operator==(const Node &other) const { + if(this==&other) { + return true; + } + if(!Node::operator==(other)) { + return false; + } + const SplitBranchNode &o=static_cast(other); + return unit==o.unit && lessThan==o.lessThan && greaterOrEqual==o.greaterOrEqual; +} + +int32_t +StringTrieBuilder::SplitBranchNode::markRightEdgesFirst(int32_t edgeNumber) { + if(offset==0) { + firstEdgeNumber=edgeNumber; + edgeNumber=greaterOrEqual->markRightEdgesFirst(edgeNumber); + offset=edgeNumber=lessThan->markRightEdgesFirst(edgeNumber-1); + } + return edgeNumber; +} + +void +StringTrieBuilder::SplitBranchNode::write(StringTrieBuilder &builder) { + // Encode the less-than branch first. + lessThan->writeUnlessInsideRightEdge(firstEdgeNumber, greaterOrEqual->getOffset(), builder); + // Encode the greater-or-equal branch last because we do not jump for it at all. + greaterOrEqual->write(builder); + // Write this node. + U_ASSERT(lessThan->getOffset()>0); + builder.writeDeltaTo(lessThan->getOffset()); // less-than + offset=builder.write(unit); +} + +bool +StringTrieBuilder::BranchHeadNode::operator==(const Node &other) const { + if(this==&other) { + return true; + } + if(!ValueNode::operator==(other)) { + return false; + } + const BranchHeadNode &o=static_cast(other); + return length==o.length && next==o.next; +} + +int32_t +StringTrieBuilder::BranchHeadNode::markRightEdgesFirst(int32_t edgeNumber) { + if(offset==0) { + offset=edgeNumber=next->markRightEdgesFirst(edgeNumber); + } + return edgeNumber; +} + +void +StringTrieBuilder::BranchHeadNode::write(StringTrieBuilder &builder) { + next->write(builder); + if(length<=builder.getMinLinearMatch()) { + offset=builder.writeValueAndType(hasValue, value, length-1); + } else { + builder.write(length-1); + offset=builder.writeValueAndType(hasValue, value, 0); + } +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/uarrsort.cpp b/deps/icu-small/source/common/uarrsort.cpp index 17b6964ffe0316..87e55967ca7431 100644 --- a/deps/icu-small/source/common/uarrsort.cpp +++ b/deps/icu-small/source/common/uarrsort.cpp @@ -1,274 +1,274 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2003-2013, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uarrsort.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2003aug04 -* created by: Markus W. Scherer -* -* Internal function for sorting arrays. -*/ - -#include - -#include "unicode/utypes.h" -#include "cmemory.h" -#include "uarrsort.h" - -enum { - /** - * "from Knuth" - * - * A binary search over 8 items performs 4 comparisons: - * log2(8)=3 to subdivide, +1 to check for equality. - * A linear search over 8 items on average also performs 4 comparisons. - */ - MIN_QSORT=9, - STACK_ITEM_SIZE=200 -}; - -static constexpr int32_t sizeInMaxAlignTs(int32_t sizeInBytes) { - return (sizeInBytes + sizeof(std::max_align_t) - 1) / sizeof(std::max_align_t); -} - -/* UComparator convenience implementations ---------------------------------- */ - -U_CAPI int32_t U_EXPORT2 -uprv_uint16Comparator(const void *context, const void *left, const void *right) { - (void)context; - return (int32_t)*(const uint16_t *)left - (int32_t)*(const uint16_t *)right; -} - -U_CAPI int32_t U_EXPORT2 -uprv_int32Comparator(const void *context, const void *left, const void *right) { - (void)context; - return *(const int32_t *)left - *(const int32_t *)right; -} - -U_CAPI int32_t U_EXPORT2 -uprv_uint32Comparator(const void *context, const void *left, const void *right) { - (void)context; - uint32_t l=*(const uint32_t *)left, r=*(const uint32_t *)right; - - /* compare directly because (l-r) would overflow the int32_t result */ - if(lr */ { - return 1; - } -} - -/* Insertion sort using binary search --------------------------------------- */ - -U_CAPI int32_t U_EXPORT2 -uprv_stableBinarySearch(char *array, int32_t limit, void *item, int32_t itemSize, - UComparator *cmp, const void *context) { - int32_t start=0; - UBool found=false; - - /* Binary search until we get down to a tiny sub-array. */ - while((limit-start)>=MIN_QSORT) { - int32_t i=(start+limit)/2; - int32_t diff=cmp(context, item, array+i*itemSize); - if(diff==0) { - /* - * Found the item. We look for the *last* occurrence of such - * an item, for stable sorting. - * If we knew that there will be only few equal items, - * we could break now and enter the linear search. - * However, if there are many equal items, then it should be - * faster to continue with the binary search. - * It seems likely that we either have all unique items - * (where found will never become true in the insertion sort) - * or potentially many duplicates. - */ - found=true; - start=i+1; - } else if(diff<0) { - limit=i; - } else { - start=i; - } - } - - /* Linear search over the remaining tiny sub-array. */ - while(start v; - if (sizeInMaxAlignTs(itemSize) > v.getCapacity() && - v.resize(sizeInMaxAlignTs(itemSize)) == nullptr) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - - doInsertionSort(array, length, itemSize, cmp, context, v.getAlias()); -} - -/* QuickSort ---------------------------------------------------------------- */ - -/* - * This implementation is semi-recursive: - * It recurses for the smaller sub-array to shorten the recursion depth, - * and loops for the larger sub-array. - * - * Loosely after QuickSort algorithms in - * Niklaus Wirth - * Algorithmen und Datenstrukturen mit Modula-2 - * B.G. Teubner Stuttgart - * 4. Auflage 1986 - * ISBN 3-519-02260-5 - */ -static void -subQuickSort(char *array, int32_t start, int32_t limit, int32_t itemSize, - UComparator *cmp, const void *context, - void *px, void *pw) { - int32_t left, right; - - /* start and left are inclusive, limit and right are exclusive */ - do { - if((start+MIN_QSORT)>=limit) { - doInsertionSort(array+start*itemSize, limit-start, itemSize, cmp, context, px); - break; - } - - left=start; - right=limit; - - /* x=array[middle] */ - uprv_memcpy(px, array+(size_t)((start+limit)/2)*itemSize, itemSize); - - do { - while(/* array[left] xw; - if(sizeInMaxAlignTs(itemSize)*2 > xw.getCapacity() && - xw.resize(sizeInMaxAlignTs(itemSize) * 2) == nullptr) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - - subQuickSort(array, 0, length, itemSize, cmp, context, - xw.getAlias(), xw.getAlias() + sizeInMaxAlignTs(itemSize)); -} - -/* uprv_sortArray() API ----------------------------------------------------- */ - -/* - * Check arguments, select an appropriate implementation, - * cast the array to char * so that array+i*itemSize works. - */ -U_CAPI void U_EXPORT2 -uprv_sortArray(void *array, int32_t length, int32_t itemSize, - UComparator *cmp, const void *context, - UBool sortStable, UErrorCode *pErrorCode) { - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return; - } - if((length>0 && array==NULL) || length<0 || itemSize<=0 || cmp==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - if(length<=1) { - return; - } else if(length + +#include "unicode/utypes.h" +#include "cmemory.h" +#include "uarrsort.h" + +enum { + /** + * "from Knuth" + * + * A binary search over 8 items performs 4 comparisons: + * log2(8)=3 to subdivide, +1 to check for equality. + * A linear search over 8 items on average also performs 4 comparisons. + */ + MIN_QSORT=9, + STACK_ITEM_SIZE=200 +}; + +static constexpr int32_t sizeInMaxAlignTs(int32_t sizeInBytes) { + return (sizeInBytes + sizeof(std::max_align_t) - 1) / sizeof(std::max_align_t); +} + +/* UComparator convenience implementations ---------------------------------- */ + +U_CAPI int32_t U_EXPORT2 +uprv_uint16Comparator(const void *context, const void *left, const void *right) { + (void)context; + return (int32_t)*(const uint16_t *)left - (int32_t)*(const uint16_t *)right; +} + +U_CAPI int32_t U_EXPORT2 +uprv_int32Comparator(const void *context, const void *left, const void *right) { + (void)context; + return *(const int32_t *)left - *(const int32_t *)right; +} + +U_CAPI int32_t U_EXPORT2 +uprv_uint32Comparator(const void *context, const void *left, const void *right) { + (void)context; + uint32_t l=*(const uint32_t *)left, r=*(const uint32_t *)right; + + /* compare directly because (l-r) would overflow the int32_t result */ + if(lr */ { + return 1; + } +} + +/* Insertion sort using binary search --------------------------------------- */ + +U_CAPI int32_t U_EXPORT2 +uprv_stableBinarySearch(char *array, int32_t limit, void *item, int32_t itemSize, + UComparator *cmp, const void *context) { + int32_t start=0; + UBool found=false; + + /* Binary search until we get down to a tiny sub-array. */ + while((limit-start)>=MIN_QSORT) { + int32_t i=(start+limit)/2; + int32_t diff=cmp(context, item, array+i*itemSize); + if(diff==0) { + /* + * Found the item. We look for the *last* occurrence of such + * an item, for stable sorting. + * If we knew that there will be only few equal items, + * we could break now and enter the linear search. + * However, if there are many equal items, then it should be + * faster to continue with the binary search. + * It seems likely that we either have all unique items + * (where found will never become true in the insertion sort) + * or potentially many duplicates. + */ + found=true; + start=i+1; + } else if(diff<0) { + limit=i; + } else { + start=i; + } + } + + /* Linear search over the remaining tiny sub-array. */ + while(start v; + if (sizeInMaxAlignTs(itemSize) > v.getCapacity() && + v.resize(sizeInMaxAlignTs(itemSize)) == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + + doInsertionSort(array, length, itemSize, cmp, context, v.getAlias()); +} + +/* QuickSort ---------------------------------------------------------------- */ + +/* + * This implementation is semi-recursive: + * It recurses for the smaller sub-array to shorten the recursion depth, + * and loops for the larger sub-array. + * + * Loosely after QuickSort algorithms in + * Niklaus Wirth + * Algorithmen und Datenstrukturen mit Modula-2 + * B.G. Teubner Stuttgart + * 4. Auflage 1986 + * ISBN 3-519-02260-5 + */ +static void +subQuickSort(char *array, int32_t start, int32_t limit, int32_t itemSize, + UComparator *cmp, const void *context, + void *px, void *pw) { + int32_t left, right; + + /* start and left are inclusive, limit and right are exclusive */ + do { + if((start+MIN_QSORT)>=limit) { + doInsertionSort(array+start*itemSize, limit-start, itemSize, cmp, context, px); + break; + } + + left=start; + right=limit; + + /* x=array[middle] */ + uprv_memcpy(px, array+(size_t)((start+limit)/2)*itemSize, itemSize); + + do { + while(/* array[left] xw; + if(sizeInMaxAlignTs(itemSize)*2 > xw.getCapacity() && + xw.resize(sizeInMaxAlignTs(itemSize) * 2) == nullptr) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + + subQuickSort(array, 0, length, itemSize, cmp, context, + xw.getAlias(), xw.getAlias() + sizeInMaxAlignTs(itemSize)); +} + +/* uprv_sortArray() API ----------------------------------------------------- */ + +/* + * Check arguments, select an appropriate implementation, + * cast the array to char * so that array+i*itemSize works. + */ +U_CAPI void U_EXPORT2 +uprv_sortArray(void *array, int32_t length, int32_t itemSize, + UComparator *cmp, const void *context, + UBool sortStable, UErrorCode *pErrorCode) { + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return; + } + if((length>0 && array==nullptr) || length<0 || itemSize<=0 || cmp==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + if(length<=1) { + return; + } else if(length0 if left>right - * - * @internal - */ -typedef int32_t U_CALLCONV -UComparator(const void *context, const void *left, const void *right); -U_CDECL_END - -/** - * Array sorting function. - * Uses a UComparator for comparing array items to each other, and simple - * memory copying to move items. - * - * @param array The array to be sorted. - * @param length The number of items in the array. - * @param itemSize The size in bytes of each array item. - * @param cmp UComparator function used to compare two items each. - * @param context Application-specific pointer, passed through to the UComparator. - * @param sortStable If true, a stable sorting algorithm must be used. - * @param pErrorCode ICU in/out UErrorCode parameter. - * - * @internal - */ -U_CAPI void U_EXPORT2 -uprv_sortArray(void *array, int32_t length, int32_t itemSize, - UComparator *cmp, const void *context, - UBool sortStable, UErrorCode *pErrorCode); - -/** - * Convenience UComparator implementation for uint16_t arrays. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -uprv_uint16Comparator(const void *context, const void *left, const void *right); - -/** - * Convenience UComparator implementation for int32_t arrays. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -uprv_int32Comparator(const void *context, const void *left, const void *right); - -/** - * Convenience UComparator implementation for uint32_t arrays. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -uprv_uint32Comparator(const void *context, const void *left, const void *right); - -/** - * Much like Java Collections.binarySearch(list, key, comparator). - * - * Except: Java documents "If the list contains multiple elements equal to - * the specified object, there is no guarantee which one will be found." - * - * This version here will return the largest index of any equal item, - * for use in stable sorting. - * - * @return the index>=0 where the item was found: - * the largest such index, if multiple, for stable sorting; - * or the index<0 for inserting the item at ~index in sorted order - */ -U_CAPI int32_t U_EXPORT2 -uprv_stableBinarySearch(char *array, int32_t length, void *item, int32_t itemSize, - UComparator *cmp, const void *context); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2003-2013, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uarrsort.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2003aug04 +* created by: Markus W. Scherer +* +* Internal function for sorting arrays. +*/ + +#ifndef __UARRSORT_H__ +#define __UARRSORT_H__ + +#include "unicode/utypes.h" + +U_CDECL_BEGIN +/** + * Function type for comparing two items as part of sorting an array or similar. + * Callback function for uprv_sortArray(). + * + * @param context Application-specific pointer, passed through by uprv_sortArray(). + * @param left Pointer to the "left" item. + * @param right Pointer to the "right" item. + * @return 32-bit signed integer comparison result: + * <0 if left0 if left>right + * + * @internal + */ +typedef int32_t U_CALLCONV +UComparator(const void *context, const void *left, const void *right); +U_CDECL_END + +/** + * Array sorting function. + * Uses a UComparator for comparing array items to each other, and simple + * memory copying to move items. + * + * @param array The array to be sorted. + * @param length The number of items in the array. + * @param itemSize The size in bytes of each array item. + * @param cmp UComparator function used to compare two items each. + * @param context Application-specific pointer, passed through to the UComparator. + * @param sortStable If true, a stable sorting algorithm must be used. + * @param pErrorCode ICU in/out UErrorCode parameter. + * + * @internal + */ +U_CAPI void U_EXPORT2 +uprv_sortArray(void *array, int32_t length, int32_t itemSize, + UComparator *cmp, const void *context, + UBool sortStable, UErrorCode *pErrorCode); + +/** + * Convenience UComparator implementation for uint16_t arrays. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +uprv_uint16Comparator(const void *context, const void *left, const void *right); + +/** + * Convenience UComparator implementation for int32_t arrays. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +uprv_int32Comparator(const void *context, const void *left, const void *right); + +/** + * Convenience UComparator implementation for uint32_t arrays. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +uprv_uint32Comparator(const void *context, const void *left, const void *right); + +/** + * Much like Java Collections.binarySearch(list, key, comparator). + * + * Except: Java documents "If the list contains multiple elements equal to + * the specified object, there is no guarantee which one will be found." + * + * This version here will return the largest index of any equal item, + * for use in stable sorting. + * + * @return the index>=0 where the item was found: + * the largest such index, if multiple, for stable sorting; + * or the index<0 for inserting the item at ~index in sorted order + */ +U_CAPI int32_t U_EXPORT2 +uprv_stableBinarySearch(char *array, int32_t length, void *item, int32_t itemSize, + UComparator *cmp, const void *context); + +#endif diff --git a/deps/icu-small/source/common/uassert.h b/deps/icu-small/source/common/uassert.h index 52187528991c1c..d8c314cdb438f0 100644 --- a/deps/icu-small/source/common/uassert.h +++ b/deps/icu-small/source/common/uassert.h @@ -1,71 +1,71 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2002-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* File uassert.h -* -* Contains the U_ASSERT and UPRV_UNREACHABLE_* macros -* -****************************************************************************** -*/ -#ifndef U_ASSERT_H -#define U_ASSERT_H - -/* utypes.h is included to get the proper define for uint8_t */ -#include "unicode/utypes.h" -/* for abort */ -#include - -/** - * \def U_ASSERT - * By default, U_ASSERT just wraps the C library assert macro. - * By changing the definition here, the assert behavior for ICU can be changed - * without affecting other non - ICU uses of the C library assert(). -*/ -#if U_DEBUG -# include -# define U_ASSERT(exp) assert(exp) -#elif U_CPLUSPLUS_VERSION -# define U_ASSERT(exp) (void)0 -#else -# define U_ASSERT(exp) -#endif - -/** - * \def UPRV_UNREACHABLE_ASSERT - * This macro is used in places that we had believed were unreachable, but - * experience has shown otherwise (possibly due to memory corruption, etc). - * In this case we call assert() in debug versions as with U_ASSERT, instead - * of unconditionally calling abort(). However we also allow redefinition as - * with UPRV_UNREACHABLE_EXIT. - * @internal -*/ -#if defined(UPRV_UNREACHABLE_ASSERT) - // Use the predefined value. -#elif U_DEBUG -# include -# define UPRV_UNREACHABLE_ASSERT assert(false) -#elif U_CPLUSPLUS_VERSION -# define UPRV_UNREACHABLE_ASSERT (void)0 -#else -# define UPRV_UNREACHABLE_ASSERT -#endif - -/** - * \def UPRV_UNREACHABLE_EXIT - * This macro is used to unconditionally abort if unreachable code is ever executed. - * @internal -*/ -#if defined(UPRV_UNREACHABLE_EXIT) - // Use the predefined value. -#else -# define UPRV_UNREACHABLE_EXIT abort() -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2002-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* File uassert.h +* +* Contains the U_ASSERT and UPRV_UNREACHABLE_* macros +* +****************************************************************************** +*/ +#ifndef U_ASSERT_H +#define U_ASSERT_H + +/* utypes.h is included to get the proper define for uint8_t */ +#include "unicode/utypes.h" +/* for abort */ +#include + +/** + * \def U_ASSERT + * By default, U_ASSERT just wraps the C library assert macro. + * By changing the definition here, the assert behavior for ICU can be changed + * without affecting other non - ICU uses of the C library assert(). +*/ +#if U_DEBUG +# include +# define U_ASSERT(exp) assert(exp) +#elif U_CPLUSPLUS_VERSION +# define U_ASSERT(exp) (void)0 +#else +# define U_ASSERT(exp) +#endif + +/** + * \def UPRV_UNREACHABLE_ASSERT + * This macro is used in places that we had believed were unreachable, but + * experience has shown otherwise (possibly due to memory corruption, etc). + * In this case we call assert() in debug versions as with U_ASSERT, instead + * of unconditionally calling abort(). However we also allow redefinition as + * with UPRV_UNREACHABLE_EXIT. + * @internal +*/ +#if defined(UPRV_UNREACHABLE_ASSERT) + // Use the predefined value. +#elif U_DEBUG +# include +# define UPRV_UNREACHABLE_ASSERT assert(false) +#elif U_CPLUSPLUS_VERSION +# define UPRV_UNREACHABLE_ASSERT (void)0 +#else +# define UPRV_UNREACHABLE_ASSERT +#endif + +/** + * \def UPRV_UNREACHABLE_EXIT + * This macro is used to unconditionally abort if unreachable code is ever executed. + * @internal +*/ +#if defined(UPRV_UNREACHABLE_EXIT) + // Use the predefined value. +#else +# define UPRV_UNREACHABLE_EXIT abort() +#endif + +#endif diff --git a/deps/icu-small/source/common/ubidi.cpp b/deps/icu-small/source/common/ubidi.cpp index eb40a212e17e9e..99b0a6bca19ad1 100644 --- a/deps/icu-small/source/common/ubidi.cpp +++ b/deps/icu-small/source/common/ubidi.cpp @@ -1,3036 +1,3036 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: ubidi.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999jul27 -* created by: Markus W. Scherer, updated by Matitiahu Allouche -* -*/ - -#include "cmemory.h" -#include "unicode/utypes.h" -#include "unicode/ustring.h" -#include "unicode/uchar.h" -#include "unicode/ubidi.h" -#include "unicode/utf16.h" -#include "ubidi_props.h" -#include "ubidiimp.h" -#include "uassert.h" - -/* - * General implementation notes: - * - * Throughout the implementation, there are comments like (W2) that refer to - * rules of the BiDi algorithm, in this example to the second rule of the - * resolution of weak types. - * - * For handling surrogate pairs, where two UChar's form one "abstract" (or UTF-32) - * character according to UTF-16, the second UChar gets the directional property of - * the entire character assigned, while the first one gets a BN, a boundary - * neutral, type, which is ignored by most of the algorithm according to - * rule (X9) and the implementation suggestions of the BiDi algorithm. - * - * Later, adjustWSLevels() will set the level for each BN to that of the - * following character (UChar), which results in surrogate pairs getting the - * same level on each of their surrogates. - * - * In a UTF-8 implementation, the same thing could be done: the last byte of - * a multi-byte sequence would get the "real" property, while all previous - * bytes of that sequence would get BN. - * - * It is not possible to assign all those parts of a character the same real - * property because this would fail in the resolution of weak types with rules - * that look at immediately surrounding types. - * - * As a related topic, this implementation does not remove Boundary Neutral - * types from the input, but ignores them wherever this is relevant. - * For example, the loop for the resolution of the weak types reads - * types until it finds a non-BN. - * Also, explicit embedding codes are neither changed into BN nor removed. - * They are only treated the same way real BNs are. - * As stated before, adjustWSLevels() takes care of them at the end. - * For the purpose of conformance, the levels of all these codes - * do not matter. - * - * Note that this implementation modifies the dirProps - * after the initial setup, when applying X5c (replace FSI by LRI or RLI), - * X6, N0 (replace paired brackets by L or R). - * - * In this implementation, the resolution of weak types (W1 to W6), - * neutrals (N1 and N2), and the assignment of the resolved level (In) - * are all done in one single loop, in resolveImplicitLevels(). - * Changes of dirProp values are done on the fly, without writing - * them back to the dirProps array. - * - * - * This implementation contains code that allows to bypass steps of the - * algorithm that are not needed on the specific paragraph - * in order to speed up the most common cases considerably, - * like text that is entirely LTR, or RTL text without numbers. - * - * Most of this is done by setting a bit for each directional property - * in a flags variable and later checking for whether there are - * any LTR characters or any RTL characters, or both, whether - * there are any explicit embedding codes, etc. - * - * If the (Xn) steps are performed, then the flags are re-evaluated, - * because they will then not contain the embedding codes any more - * and will be adjusted for override codes, so that subsequently - * more bypassing may be possible than what the initial flags suggested. - * - * If the text is not mixed-directional, then the - * algorithm steps for the weak type resolution are not performed, - * and all levels are set to the paragraph level. - * - * If there are no explicit embedding codes, then the (Xn) steps - * are not performed. - * - * If embedding levels are supplied as a parameter, then all - * explicit embedding codes are ignored, and the (Xn) steps - * are not performed. - * - * White Space types could get the level of the run they belong to, - * and are checked with a test of (flags&MASK_EMBEDDING) to - * consider if the paragraph direction should be considered in - * the flags variable. - * - * If there are no White Space types in the paragraph, then - * (L1) is not necessary in adjustWSLevels(). - */ - -/* to avoid some conditional statements, use tiny constant arrays */ -static const Flags flagLR[2]={ DIRPROP_FLAG(L), DIRPROP_FLAG(R) }; -static const Flags flagE[2]={ DIRPROP_FLAG(LRE), DIRPROP_FLAG(RLE) }; -static const Flags flagO[2]={ DIRPROP_FLAG(LRO), DIRPROP_FLAG(RLO) }; - -#define DIRPROP_FLAG_LR(level) flagLR[(level)&1] -#define DIRPROP_FLAG_E(level) flagE[(level)&1] -#define DIRPROP_FLAG_O(level) flagO[(level)&1] - -#define DIR_FROM_STRONG(strong) ((strong)==L ? L : R) - -#define NO_OVERRIDE(level) ((level)&~UBIDI_LEVEL_OVERRIDE) - -/* UBiDi object management -------------------------------------------------- */ - -U_CAPI UBiDi * U_EXPORT2 -ubidi_open(void) -{ - UErrorCode errorCode=U_ZERO_ERROR; - return ubidi_openSized(0, 0, &errorCode); -} - -U_CAPI UBiDi * U_EXPORT2 -ubidi_openSized(int32_t maxLength, int32_t maxRunCount, UErrorCode *pErrorCode) { - UBiDi *pBiDi; - - /* check the argument values */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return NULL; - } else if(maxLength<0 || maxRunCount<0) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; /* invalid arguments */ - } - - /* allocate memory for the object */ - pBiDi=(UBiDi *)uprv_malloc(sizeof(UBiDi)); - if(pBiDi==NULL) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - /* reset the object, all pointers NULL, all flags false, all sizes 0 */ - uprv_memset(pBiDi, 0, sizeof(UBiDi)); - - /* allocate memory for arrays as requested */ - if(maxLength>0) { - if( !getInitialDirPropsMemory(pBiDi, maxLength) || - !getInitialLevelsMemory(pBiDi, maxLength) - ) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - } - } else { - pBiDi->mayAllocateText=true; - } - - if(maxRunCount>0) { - if(maxRunCount==1) { - /* use simpleRuns[] */ - pBiDi->runsSize=sizeof(Run); - } else if(!getInitialRunsMemory(pBiDi, maxRunCount)) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - } - } else { - pBiDi->mayAllocateRuns=true; - } - - if(U_SUCCESS(*pErrorCode)) { - return pBiDi; - } else { - ubidi_close(pBiDi); - return NULL; - } -} - -/* - * We are allowed to allocate memory if memory==NULL or - * mayAllocate==true for each array that we need. - * We also try to grow memory as needed if we - * allocate it. - * - * Assume sizeNeeded>0. - * If *pMemory!=NULL, then assume *pSize>0. - * - * ### this realloc() may unnecessarily copy the old data, - * which we know we don't need any more; - * is this the best way to do this?? - */ -U_CFUNC UBool -ubidi_getMemory(BidiMemoryForAllocation *bidiMem, int32_t *pSize, UBool mayAllocate, int32_t sizeNeeded) { - void **pMemory = (void **)bidiMem; - /* check for existing memory */ - if(*pMemory==NULL) { - /* we need to allocate memory */ - if(mayAllocate && (*pMemory=uprv_malloc(sizeNeeded))!=NULL) { - *pSize=sizeNeeded; - return true; - } else { - return false; - } - } else { - if(sizeNeeded<=*pSize) { - /* there is already enough memory */ - return true; - } - else if(!mayAllocate) { - /* not enough memory, and we must not allocate */ - return false; - } else { - /* we try to grow */ - void *memory; - /* in most cases, we do not need the copy-old-data part of - * realloc, but it is needed when adding runs using getRunsMemory() - * in setParaRunsOnly() - */ - if((memory=uprv_realloc(*pMemory, sizeNeeded))!=NULL) { - *pMemory=memory; - *pSize=sizeNeeded; - return true; - } else { - /* we failed to grow */ - return false; - } - } - } -} - -U_CAPI void U_EXPORT2 -ubidi_close(UBiDi *pBiDi) { - if(pBiDi!=NULL) { - pBiDi->pParaBiDi=NULL; /* in case one tries to reuse this block */ - if(pBiDi->dirPropsMemory!=NULL) { - uprv_free(pBiDi->dirPropsMemory); - } - if(pBiDi->levelsMemory!=NULL) { - uprv_free(pBiDi->levelsMemory); - } - if(pBiDi->openingsMemory!=NULL) { - uprv_free(pBiDi->openingsMemory); - } - if(pBiDi->parasMemory!=NULL) { - uprv_free(pBiDi->parasMemory); - } - if(pBiDi->runsMemory!=NULL) { - uprv_free(pBiDi->runsMemory); - } - if(pBiDi->isolatesMemory!=NULL) { - uprv_free(pBiDi->isolatesMemory); - } - if(pBiDi->insertPoints.points!=NULL) { - uprv_free(pBiDi->insertPoints.points); - } - - uprv_free(pBiDi); - } -} - -/* set to approximate "inverse BiDi" ---------------------------------------- */ - -U_CAPI void U_EXPORT2 -ubidi_setInverse(UBiDi *pBiDi, UBool isInverse) { - if(pBiDi!=NULL) { - pBiDi->isInverse=isInverse; - pBiDi->reorderingMode = isInverse ? UBIDI_REORDER_INVERSE_NUMBERS_AS_L - : UBIDI_REORDER_DEFAULT; - } -} - -U_CAPI UBool U_EXPORT2 -ubidi_isInverse(UBiDi *pBiDi) { - if(pBiDi!=NULL) { - return pBiDi->isInverse; - } else { - return false; - } -} - -/* FOOD FOR THOUGHT: currently the reordering modes are a mixture of - * algorithm for direct BiDi, algorithm for inverse BiDi and the bizarre - * concept of RUNS_ONLY which is a double operation. - * It could be advantageous to divide this into 3 concepts: - * a) Operation: direct / inverse / RUNS_ONLY - * b) Direct algorithm: default / NUMBERS_SPECIAL / GROUP_NUMBERS_WITH_R - * c) Inverse algorithm: default / INVERSE_LIKE_DIRECT / NUMBERS_SPECIAL - * This would allow combinations not possible today like RUNS_ONLY with - * NUMBERS_SPECIAL. - * Also allow to set INSERT_MARKS for the direct step of RUNS_ONLY and - * REMOVE_CONTROLS for the inverse step. - * Not all combinations would be supported, and probably not all do make sense. - * This would need to document which ones are supported and what are the - * fallbacks for unsupported combinations. - */ -U_CAPI void U_EXPORT2 -ubidi_setReorderingMode(UBiDi *pBiDi, UBiDiReorderingMode reorderingMode) { - if ((pBiDi!=NULL) && (reorderingMode >= UBIDI_REORDER_DEFAULT) - && (reorderingMode < UBIDI_REORDER_COUNT)) { - pBiDi->reorderingMode = reorderingMode; - pBiDi->isInverse = (UBool)(reorderingMode == UBIDI_REORDER_INVERSE_NUMBERS_AS_L); - } -} - -U_CAPI UBiDiReorderingMode U_EXPORT2 -ubidi_getReorderingMode(UBiDi *pBiDi) { - if (pBiDi!=NULL) { - return pBiDi->reorderingMode; - } else { - return UBIDI_REORDER_DEFAULT; - } -} - -U_CAPI void U_EXPORT2 -ubidi_setReorderingOptions(UBiDi *pBiDi, uint32_t reorderingOptions) { - if (reorderingOptions & UBIDI_OPTION_REMOVE_CONTROLS) { - reorderingOptions&=~UBIDI_OPTION_INSERT_MARKS; - } - if (pBiDi!=NULL) { - pBiDi->reorderingOptions=reorderingOptions; - } -} - -U_CAPI uint32_t U_EXPORT2 -ubidi_getReorderingOptions(UBiDi *pBiDi) { - if (pBiDi!=NULL) { - return pBiDi->reorderingOptions; - } else { - return 0; - } -} - -U_CAPI UBiDiDirection U_EXPORT2 -ubidi_getBaseDirection(const UChar *text, -int32_t length){ - - int32_t i; - UChar32 uchar; - UCharDirection dir; - - if( text==NULL || length<-1 ){ - return UBIDI_NEUTRAL; - } - - if(length==-1) { - length=u_strlen(text); - } - - for( i = 0 ; i < length; ) { - /* i is incremented by U16_NEXT */ - U16_NEXT(text, i, length, uchar); - dir = u_charDirection(uchar); - if( dir == U_LEFT_TO_RIGHT ) - return UBIDI_LTR; - if( dir == U_RIGHT_TO_LEFT || dir ==U_RIGHT_TO_LEFT_ARABIC ) - return UBIDI_RTL; - } - return UBIDI_NEUTRAL; -} - -/* perform (P2)..(P3) ------------------------------------------------------- */ - -/** - * Returns the directionality of the first strong character - * after the last B in prologue, if any. - * Requires prologue!=null. - */ -static DirProp -firstL_R_AL(UBiDi *pBiDi) { - const UChar *text=pBiDi->prologue; - int32_t length=pBiDi->proLength; - int32_t i; - UChar32 uchar; - DirProp dirProp, result=ON; - for(i=0; iparas - */ -static UBool -checkParaCount(UBiDi *pBiDi) { - int32_t count=pBiDi->paraCount; - if(pBiDi->paras==pBiDi->simpleParas) { - if(count<=SIMPLE_PARAS_COUNT) - return true; - if(!getInitialParasMemory(pBiDi, SIMPLE_PARAS_COUNT * 2)) - return false; - pBiDi->paras=pBiDi->parasMemory; - uprv_memcpy(pBiDi->parasMemory, pBiDi->simpleParas, SIMPLE_PARAS_COUNT * sizeof(Para)); - return true; - } - if(!getInitialParasMemory(pBiDi, count * 2)) - return false; - pBiDi->paras=pBiDi->parasMemory; - return true; -} - -/* - * Get the directional properties for the text, calculate the flags bit-set, and - * determine the paragraph level if necessary (in pBiDi->paras[i].level). - * FSI initiators are also resolved and their dirProp replaced with LRI or RLI. - * When encountering an FSI, it is initially replaced with an LRI, which is the - * default. Only if a strong R or AL is found within its scope will the LRI be - * replaced by an RLI. - */ -static UBool -getDirProps(UBiDi *pBiDi) { - const UChar *text=pBiDi->text; - DirProp *dirProps=pBiDi->dirPropsMemory; /* pBiDi->dirProps is const */ - - int32_t i=0, originalLength=pBiDi->originalLength; - Flags flags=0; /* collect all directionalities in the text */ - UChar32 uchar; - DirProp dirProp=0, defaultParaLevel=0; /* initialize to avoid compiler warnings */ - UBool isDefaultLevel=IS_DEFAULT_LEVEL(pBiDi->paraLevel); - /* for inverse BiDi, the default para level is set to RTL if there is a - strong R or AL character at either end of the text */ - UBool isDefaultLevelInverse=isDefaultLevel && (UBool) - (pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_LIKE_DIRECT || - pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL); - int32_t lastArabicPos=-1; - int32_t controlCount=0; - UBool removeBiDiControls = (UBool)(pBiDi->reorderingOptions & - UBIDI_OPTION_REMOVE_CONTROLS); - - enum State { - NOT_SEEKING_STRONG, /* 0: not contextual paraLevel, not after FSI */ - SEEKING_STRONG_FOR_PARA, /* 1: looking for first strong char in para */ - SEEKING_STRONG_FOR_FSI, /* 2: looking for first strong after FSI */ - LOOKING_FOR_PDI /* 3: found strong after FSI, looking for PDI */ - }; - State state; - DirProp lastStrong=ON; /* for default level & inverse BiDi */ - /* The following stacks are used to manage isolate sequences. Those - sequences may be nested, but obviously never more deeply than the - maximum explicit embedding level. - lastStack is the index of the last used entry in the stack. A value of -1 - means that there is no open isolate sequence. - lastStack is reset to -1 on paragraph boundaries. */ - /* The following stack contains the position of the initiator of - each open isolate sequence */ - int32_t isolateStartStack[UBIDI_MAX_EXPLICIT_LEVEL+1]; - /* The following stack contains the last known state before - encountering the initiator of an isolate sequence */ - State previousStateStack[UBIDI_MAX_EXPLICIT_LEVEL+1]; - int32_t stackLast=-1; - - if(pBiDi->reorderingOptions & UBIDI_OPTION_STREAMING) - pBiDi->length=0; - defaultParaLevel=pBiDi->paraLevel&1; - if(isDefaultLevel) { - pBiDi->paras[0].level=defaultParaLevel; - lastStrong=defaultParaLevel; - if(pBiDi->proLength>0 && /* there is a prologue */ - (dirProp=firstL_R_AL(pBiDi))!=ON) { /* with a strong character */ - if(dirProp==L) - pBiDi->paras[0].level=0; /* set the default para level */ - else - pBiDi->paras[0].level=1; /* set the default para level */ - state=NOT_SEEKING_STRONG; - } else { - state=SEEKING_STRONG_FOR_PARA; - } - } else { - pBiDi->paras[0].level=pBiDi->paraLevel; - state=NOT_SEEKING_STRONG; - } - /* count paragraphs and determine the paragraph level (P2..P3) */ - /* - * see comment in ubidi.h: - * the UBIDI_DEFAULT_XXX values are designed so that - * their bit 0 alone yields the intended default - */ - for( /* i=0 above */ ; i0xffff) { /* set the lead surrogate's property to BN */ - flags|=DIRPROP_FLAG(BN); - dirProps[i-2]=BN; - } - if(removeBiDiControls && IS_BIDI_CONTROL_CHAR(uchar)) - controlCount++; - if(dirProp==L) { - if(state==SEEKING_STRONG_FOR_PARA) { - pBiDi->paras[pBiDi->paraCount-1].level=0; - state=NOT_SEEKING_STRONG; - } - else if(state==SEEKING_STRONG_FOR_FSI) { - if(stackLast<=UBIDI_MAX_EXPLICIT_LEVEL) { - /* no need for next statement, already set by default */ - /* dirProps[isolateStartStack[stackLast]]=LRI; */ - flags|=DIRPROP_FLAG(LRI); - } - state=LOOKING_FOR_PDI; - } - lastStrong=L; - continue; - } - if(dirProp==R || dirProp==AL) { - if(state==SEEKING_STRONG_FOR_PARA) { - pBiDi->paras[pBiDi->paraCount-1].level=1; - state=NOT_SEEKING_STRONG; - } - else if(state==SEEKING_STRONG_FOR_FSI) { - if(stackLast<=UBIDI_MAX_EXPLICIT_LEVEL) { - dirProps[isolateStartStack[stackLast]]=RLI; - flags|=DIRPROP_FLAG(RLI); - } - state=LOOKING_FOR_PDI; - } - lastStrong=R; - if(dirProp==AL) - lastArabicPos=i-1; - continue; - } - if(dirProp>=FSI && dirProp<=RLI) { /* FSI, LRI or RLI */ - stackLast++; - if(stackLast<=UBIDI_MAX_EXPLICIT_LEVEL) { - isolateStartStack[stackLast]=i-1; - previousStateStack[stackLast]=state; - } - if(dirProp==FSI) { - dirProps[i-1]=LRI; /* default if no strong char */ - state=SEEKING_STRONG_FOR_FSI; - } - else - state=LOOKING_FOR_PDI; - continue; - } - if(dirProp==PDI) { - if(state==SEEKING_STRONG_FOR_FSI) { - if(stackLast<=UBIDI_MAX_EXPLICIT_LEVEL) { - /* no need for next statement, already set by default */ - /* dirProps[isolateStartStack[stackLast]]=LRI; */ - flags|=DIRPROP_FLAG(LRI); - } - } - if(stackLast>=0) { - if(stackLast<=UBIDI_MAX_EXPLICIT_LEVEL) - state=previousStateStack[stackLast]; - stackLast--; - } - continue; - } - if(dirProp==B) { - if(iparas[pBiDi->paraCount-1].limit=i; - if(isDefaultLevelInverse && lastStrong==R) - pBiDi->paras[pBiDi->paraCount-1].level=1; - if(pBiDi->reorderingOptions & UBIDI_OPTION_STREAMING) { - /* When streaming, we only process whole paragraphs - thus some updates are only done on paragraph boundaries */ - pBiDi->length=i; /* i is index to next character */ - pBiDi->controlCount=controlCount; - } - if(iparaCount++; - if(checkParaCount(pBiDi)==false) /* not enough memory for a new para entry */ - return false; - if(isDefaultLevel) { - pBiDi->paras[pBiDi->paraCount-1].level=defaultParaLevel; - state=SEEKING_STRONG_FOR_PARA; - lastStrong=defaultParaLevel; - } else { - pBiDi->paras[pBiDi->paraCount-1].level=pBiDi->paraLevel; - state=NOT_SEEKING_STRONG; - } - stackLast=-1; - } - continue; - } - } - /* Ignore still open isolate sequences with overflow */ - if(stackLast>UBIDI_MAX_EXPLICIT_LEVEL) { - stackLast=UBIDI_MAX_EXPLICIT_LEVEL; - state=SEEKING_STRONG_FOR_FSI; /* to be on the safe side */ - } - /* Resolve direction of still unresolved open FSI sequences */ - while(stackLast>=0) { - if(state==SEEKING_STRONG_FOR_FSI) { - /* no need for next statement, already set by default */ - /* dirProps[isolateStartStack[stackLast]]=LRI; */ - flags|=DIRPROP_FLAG(LRI); - break; - } - state=previousStateStack[stackLast]; - stackLast--; - } - /* When streaming, ignore text after the last paragraph separator */ - if(pBiDi->reorderingOptions & UBIDI_OPTION_STREAMING) { - if(pBiDi->lengthparaCount--; - } else { - pBiDi->paras[pBiDi->paraCount-1].limit=originalLength; - pBiDi->controlCount=controlCount; - } - /* For inverse bidi, default para direction is RTL if there is - a strong R or AL at either end of the paragraph */ - if(isDefaultLevelInverse && lastStrong==R) { - pBiDi->paras[pBiDi->paraCount-1].level=1; - } - if(isDefaultLevel) { - pBiDi->paraLevel=static_cast(pBiDi->paras[0].level); - } - /* The following is needed to resolve the text direction for default level - paragraphs containing no strong character */ - for(i=0; iparaCount; i++) - flags|=DIRPROP_FLAG_LR(pBiDi->paras[i].level); - - if(pBiDi->orderParagraphsLTR && (flags&DIRPROP_FLAG(B))) { - flags|=DIRPROP_FLAG(L); - } - pBiDi->flags=flags; - pBiDi->lastArabicPos=lastArabicPos; - return true; -} - -/* determine the paragraph level at position index */ -U_CFUNC UBiDiLevel -ubidi_getParaLevelAtIndex(const UBiDi *pBiDi, int32_t pindex) { - int32_t i; - for(i=0; iparaCount; i++) - if(pindexparas[i].limit) - break; - if(i>=pBiDi->paraCount) - i=pBiDi->paraCount-1; - return (UBiDiLevel)(pBiDi->paras[i].level); -} - -/* Functions for handling paired brackets ----------------------------------- */ - -/* In the isoRuns array, the first entry is used for text outside of any - isolate sequence. Higher entries are used for each more deeply nested - isolate sequence. isoRunLast is the index of the last used entry. The - openings array is used to note the data of opening brackets not yet - matched by a closing bracket, or matched but still susceptible to change - level. - Each isoRun entry contains the index of the first and - one-after-last openings entries for pending opening brackets it - contains. The next openings entry to use is the one-after-last of the - most deeply nested isoRun entry. - isoRun entries also contain their current embedding level and the last - encountered strong character, since these will be needed to resolve - the level of paired brackets. */ - -static void -bracketInit(UBiDi *pBiDi, BracketData *bd) { - bd->pBiDi=pBiDi; - bd->isoRunLast=0; - bd->isoRuns[0].start=0; - bd->isoRuns[0].limit=0; - bd->isoRuns[0].level=GET_PARALEVEL(pBiDi, 0); - UBiDiLevel t = GET_PARALEVEL(pBiDi, 0) & 1; - bd->isoRuns[0].lastStrong = bd->isoRuns[0].lastBase = t; - bd->isoRuns[0].contextDir = (UBiDiDirection)t; - bd->isoRuns[0].contextPos=0; - if(pBiDi->openingsMemory) { - bd->openings=pBiDi->openingsMemory; - bd->openingsCount=pBiDi->openingsSize / sizeof(Opening); - } else { - bd->openings=bd->simpleOpenings; - bd->openingsCount=SIMPLE_OPENINGS_COUNT; - } - bd->isNumbersSpecial=bd->pBiDi->reorderingMode==UBIDI_REORDER_NUMBERS_SPECIAL || - bd->pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL; -} - -/* paragraph boundary */ -static void -bracketProcessB(BracketData *bd, UBiDiLevel level) { - bd->isoRunLast=0; - bd->isoRuns[0].limit=0; - bd->isoRuns[0].level=level; - bd->isoRuns[0].lastStrong=bd->isoRuns[0].lastBase=level&1; - bd->isoRuns[0].contextDir=(UBiDiDirection)(level&1); - bd->isoRuns[0].contextPos=0; -} - -/* LRE, LRO, RLE, RLO, PDF */ -static void -bracketProcessBoundary(BracketData *bd, int32_t lastCcPos, - UBiDiLevel contextLevel, UBiDiLevel embeddingLevel) { - IsoRun *pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; - DirProp *dirProps=bd->pBiDi->dirProps; - if(DIRPROP_FLAG(dirProps[lastCcPos])&MASK_ISO) /* after an isolate */ - return; - if(NO_OVERRIDE(embeddingLevel)>NO_OVERRIDE(contextLevel)) /* not a PDF */ - contextLevel=embeddingLevel; - pLastIsoRun->limit=pLastIsoRun->start; - pLastIsoRun->level=embeddingLevel; - pLastIsoRun->lastStrong=pLastIsoRun->lastBase=contextLevel&1; - pLastIsoRun->contextDir=(UBiDiDirection)(contextLevel&1); - pLastIsoRun->contextPos=(UBiDiDirection)lastCcPos; -} - -/* LRI or RLI */ -static void -bracketProcessLRI_RLI(BracketData *bd, UBiDiLevel level) { - IsoRun *pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; - int16_t lastLimit; - pLastIsoRun->lastBase=ON; - lastLimit=pLastIsoRun->limit; - bd->isoRunLast++; - pLastIsoRun++; - pLastIsoRun->start=pLastIsoRun->limit=lastLimit; - pLastIsoRun->level=level; - pLastIsoRun->lastStrong=pLastIsoRun->lastBase=level&1; - pLastIsoRun->contextDir=(UBiDiDirection)(level&1); - pLastIsoRun->contextPos=0; -} - -/* PDI */ -static void -bracketProcessPDI(BracketData *bd) { - IsoRun *pLastIsoRun; - bd->isoRunLast--; - pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; - pLastIsoRun->lastBase=ON; -} - -/* newly found opening bracket: create an openings entry */ -static UBool /* return true if success */ -bracketAddOpening(BracketData *bd, UChar match, int32_t position) { - IsoRun *pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; - Opening *pOpening; - if(pLastIsoRun->limit>=bd->openingsCount) { /* no available new entry */ - UBiDi *pBiDi=bd->pBiDi; - if(!getInitialOpeningsMemory(pBiDi, pLastIsoRun->limit * 2)) - return false; - if(bd->openings==bd->simpleOpenings) - uprv_memcpy(pBiDi->openingsMemory, bd->simpleOpenings, - SIMPLE_OPENINGS_COUNT * sizeof(Opening)); - bd->openings=pBiDi->openingsMemory; /* may have changed */ - bd->openingsCount=pBiDi->openingsSize / sizeof(Opening); - } - pOpening=&bd->openings[pLastIsoRun->limit]; - pOpening->position=position; - pOpening->match=match; - pOpening->contextDir=pLastIsoRun->contextDir; - pOpening->contextPos=pLastIsoRun->contextPos; - pOpening->flags=0; - pLastIsoRun->limit++; - return true; -} - -/* change N0c1 to N0c2 when a preceding bracket is assigned the embedding level */ -static void -fixN0c(BracketData *bd, int32_t openingIndex, int32_t newPropPosition, DirProp newProp) { - /* This function calls itself recursively */ - IsoRun *pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; - Opening *qOpening; - DirProp *dirProps=bd->pBiDi->dirProps; - int32_t k, openingPosition, closingPosition; - for(k=openingIndex+1, qOpening=&bd->openings[k]; klimit; k++, qOpening++) { - if(qOpening->match>=0) /* not an N0c match */ - continue; - if(newPropPositioncontextPos) - break; - if(newPropPosition>=qOpening->position) - continue; - if(newProp==qOpening->contextDir) - break; - openingPosition=qOpening->position; - dirProps[openingPosition]=newProp; - closingPosition=-(qOpening->match); - dirProps[closingPosition]=newProp; - qOpening->match=0; /* prevent further changes */ - fixN0c(bd, k, openingPosition, newProp); - fixN0c(bd, k, closingPosition, newProp); - } -} - -/* process closing bracket */ -static DirProp /* return L or R if N0b or N0c, ON if N0d */ -bracketProcessClosing(BracketData *bd, int32_t openIdx, int32_t position) { - IsoRun *pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; - Opening *pOpening, *qOpening; - UBiDiDirection direction; - UBool stable; - DirProp newProp; - pOpening=&bd->openings[openIdx]; - direction=(UBiDiDirection)(pLastIsoRun->level&1); - stable=true; /* assume stable until proved otherwise */ - - /* The stable flag is set when brackets are paired and their - level is resolved and cannot be changed by what will be - found later in the source string. - An unstable match can occur only when applying N0c, where - the resolved level depends on the preceding context, and - this context may be affected by text occurring later. - Example: RTL paragraph containing: abc[(latin) HEBREW] - When the closing parenthesis is encountered, it appears - that N0c1 must be applied since 'abc' sets an opposite - direction context and both parentheses receive level 2. - However, when the closing square bracket is processed, - N0b applies because of 'HEBREW' being included within the - brackets, thus the square brackets are treated like R and - receive level 1. However, this changes the preceding - context of the opening parenthesis, and it now appears - that N0c2 must be applied to the parentheses rather than - N0c1. */ - - if((direction==0 && pOpening->flags&FOUND_L) || - (direction==1 && pOpening->flags&FOUND_R)) { /* N0b */ - newProp=static_cast(direction); - } - else if(pOpening->flags&(FOUND_L|FOUND_R)) { /* N0c */ - /* it is stable if there is no containing pair or in - conditions too complicated and not worth checking */ - stable=(openIdx==pLastIsoRun->start); - if(direction!=pOpening->contextDir) - newProp= static_cast(pOpening->contextDir); /* N0c1 */ - else - newProp= static_cast(direction); /* N0c2 */ - } else { - /* forget this and any brackets nested within this pair */ - pLastIsoRun->limit= static_cast(openIdx); - return ON; /* N0d */ - } - bd->pBiDi->dirProps[pOpening->position]=newProp; - bd->pBiDi->dirProps[position]=newProp; - /* Update nested N0c pairs that may be affected */ - fixN0c(bd, openIdx, pOpening->position, newProp); - if(stable) { - pLastIsoRun->limit= static_cast(openIdx); /* forget any brackets nested within this pair */ - /* remove lower located synonyms if any */ - while(pLastIsoRun->limit>pLastIsoRun->start && - bd->openings[pLastIsoRun->limit-1].position==pOpening->position) - pLastIsoRun->limit--; - } else { - int32_t k; - pOpening->match=-position; - /* neutralize lower located synonyms if any */ - k=openIdx-1; - while(k>=pLastIsoRun->start && - bd->openings[k].position==pOpening->position) - bd->openings[k--].match=0; - /* neutralize any unmatched opening between the current pair; - this will also neutralize higher located synonyms if any */ - for(k=openIdx+1; klimit; k++) { - qOpening=&bd->openings[k]; - if(qOpening->position>=position) - break; - if(qOpening->match>0) - qOpening->match=0; - } - } - return newProp; -} - -/* handle strong characters, digits and candidates for closing brackets */ -static UBool /* return true if success */ -bracketProcessChar(BracketData *bd, int32_t position) { - IsoRun *pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; - DirProp *dirProps, dirProp, newProp; - UBiDiLevel level; - dirProps=bd->pBiDi->dirProps; - dirProp=dirProps[position]; - if(dirProp==ON) { - UChar c, match; - int32_t idx; - /* First see if it is a matching closing bracket. Hopefully, this is - more efficient than checking if it is a closing bracket at all */ - c=bd->pBiDi->text[position]; - for(idx=pLastIsoRun->limit-1; idx>=pLastIsoRun->start; idx--) { - if(bd->openings[idx].match!=c) - continue; - /* We have a match */ - newProp=bracketProcessClosing(bd, idx, position); - if(newProp==ON) { /* N0d */ - c=0; /* prevent handling as an opening */ - break; - } - pLastIsoRun->lastBase=ON; - pLastIsoRun->contextDir=(UBiDiDirection)newProp; - pLastIsoRun->contextPos=position; - level=bd->pBiDi->levels[position]; - if(level&UBIDI_LEVEL_OVERRIDE) { /* X4, X5 */ - uint16_t flag; - int32_t i; - newProp=level&1; - pLastIsoRun->lastStrong=newProp; - flag=DIRPROP_FLAG(newProp); - for(i=pLastIsoRun->start; iopenings[i].flags|=flag; - /* matching brackets are not overridden by LRO/RLO */ - bd->pBiDi->levels[position]&=~UBIDI_LEVEL_OVERRIDE; - } - /* matching brackets are not overridden by LRO/RLO */ - bd->pBiDi->levels[bd->openings[idx].position]&=~UBIDI_LEVEL_OVERRIDE; - return true; - } - /* We get here only if the ON character is not a matching closing - bracket or it is a case of N0d */ - /* Now see if it is an opening bracket */ - if(c) - match= static_cast(u_getBidiPairedBracket(c)); /* get the matching char */ - else - match=0; - if(match!=c && /* has a matching char */ - ubidi_getPairedBracketType(c)==U_BPT_OPEN) { /* opening bracket */ - /* special case: process synonyms - create an opening entry for each synonym */ - if(match==0x232A) { /* RIGHT-POINTING ANGLE BRACKET */ - if(!bracketAddOpening(bd, 0x3009, position)) - return false; - } - else if(match==0x3009) { /* RIGHT ANGLE BRACKET */ - if(!bracketAddOpening(bd, 0x232A, position)) - return false; - } - if(!bracketAddOpening(bd, match, position)) - return false; - } - } - level=bd->pBiDi->levels[position]; - if(level&UBIDI_LEVEL_OVERRIDE) { /* X4, X5 */ - newProp=level&1; - if(dirProp!=S && dirProp!=WS && dirProp!=ON) - dirProps[position]=newProp; - pLastIsoRun->lastBase=newProp; - pLastIsoRun->lastStrong=newProp; - pLastIsoRun->contextDir=(UBiDiDirection)newProp; - pLastIsoRun->contextPos=position; - } - else if(dirProp<=R || dirProp==AL) { - newProp= static_cast(DIR_FROM_STRONG(dirProp)); - pLastIsoRun->lastBase=dirProp; - pLastIsoRun->lastStrong=dirProp; - pLastIsoRun->contextDir=(UBiDiDirection)newProp; - pLastIsoRun->contextPos=position; - } - else if(dirProp==EN) { - pLastIsoRun->lastBase=EN; - if(pLastIsoRun->lastStrong==L) { - newProp=L; /* W7 */ - if(!bd->isNumbersSpecial) - dirProps[position]=ENL; - pLastIsoRun->contextDir=(UBiDiDirection)L; - pLastIsoRun->contextPos=position; - } - else { - newProp=R; /* N0 */ - if(pLastIsoRun->lastStrong==AL) - dirProps[position]=AN; /* W2 */ - else - dirProps[position]=ENR; - pLastIsoRun->contextDir=(UBiDiDirection)R; - pLastIsoRun->contextPos=position; - } - } - else if(dirProp==AN) { - newProp=R; /* N0 */ - pLastIsoRun->lastBase=AN; - pLastIsoRun->contextDir=(UBiDiDirection)R; - pLastIsoRun->contextPos=position; - } - else if(dirProp==NSM) { - /* if the last real char was ON, change NSM to ON so that it - will stay ON even if the last real char is a bracket which - may be changed to L or R */ - newProp=pLastIsoRun->lastBase; - if(newProp==ON) - dirProps[position]=newProp; - } - else { - newProp=dirProp; - pLastIsoRun->lastBase=dirProp; - } - if(newProp<=R || newProp==AL) { - int32_t i; - uint16_t flag=DIRPROP_FLAG(DIR_FROM_STRONG(newProp)); - for(i=pLastIsoRun->start; ilimit; i++) - if(position>bd->openings[i].position) - bd->openings[i].flags|=flag; - } - return true; -} - -/* perform (X1)..(X9) ------------------------------------------------------- */ - -/* determine if the text is mixed-directional or single-directional */ -static UBiDiDirection -directionFromFlags(UBiDi *pBiDi) { - Flags flags=pBiDi->flags; - /* if the text contains AN and neutrals, then some neutrals may become RTL */ - if(!(flags&MASK_RTL || ((flags&DIRPROP_FLAG(AN)) && (flags&MASK_POSSIBLE_N)))) { - return UBIDI_LTR; - } else if(!(flags&MASK_LTR)) { - return UBIDI_RTL; - } else { - return UBIDI_MIXED; - } -} - -/* - * Resolve the explicit levels as specified by explicit embedding codes. - * Recalculate the flags to have them reflect the real properties - * after taking the explicit embeddings into account. - * - * The BiDi algorithm is designed to result in the same behavior whether embedding - * levels are externally specified (from "styled text", supposedly the preferred - * method) or set by explicit embedding codes (LRx, RLx, PDF, FSI, PDI) in the plain text. - * That is why (X9) instructs to remove all not-isolate explicit codes (and BN). - * However, in a real implementation, the removal of these codes and their index - * positions in the plain text is undesirable since it would result in - * reallocated, reindexed text. - * Instead, this implementation leaves the codes in there and just ignores them - * in the subsequent processing. - * In order to get the same reordering behavior, positions with a BN or a not-isolate - * explicit embedding code just get the same level assigned as the last "real" - * character. - * - * Some implementations, not this one, then overwrite some of these - * directionality properties at "real" same-level-run boundaries by - * L or R codes so that the resolution of weak types can be performed on the - * entire paragraph at once instead of having to parse it once more and - * perform that resolution on same-level-runs. - * This limits the scope of the implicit rules in effectively - * the same way as the run limits. - * - * Instead, this implementation does not modify these codes, except for - * paired brackets whose properties (ON) may be replaced by L or R. - * On one hand, the paragraph has to be scanned for same-level-runs, but - * on the other hand, this saves another loop to reset these codes, - * or saves making and modifying a copy of dirProps[]. - * - * - * Note that (Pn) and (Xn) changed significantly from version 4 of the BiDi algorithm. - * - * - * Handling the stack of explicit levels (Xn): - * - * With the BiDi stack of explicit levels, as pushed with each - * LRE, RLE, LRO, RLO, LRI, RLI and FSI and popped with each PDF and PDI, - * the explicit level must never exceed UBIDI_MAX_EXPLICIT_LEVEL. - * - * In order to have a correct push-pop semantics even in the case of overflows, - * overflow counters and a valid isolate counter are used as described in UAX#9 - * section 3.3.2 "Explicit Levels and Directions". - * - * This implementation assumes that UBIDI_MAX_EXPLICIT_LEVEL is odd. - * - * Returns normally the direction; -1 if there was a memory shortage - * - */ -static UBiDiDirection -resolveExplicitLevels(UBiDi *pBiDi, UErrorCode *pErrorCode) { - DirProp *dirProps=pBiDi->dirProps; - UBiDiLevel *levels=pBiDi->levels; - const UChar *text=pBiDi->text; - - int32_t i=0, length=pBiDi->length; - Flags flags=pBiDi->flags; /* collect all directionalities in the text */ - DirProp dirProp; - UBiDiLevel level=GET_PARALEVEL(pBiDi, 0); - UBiDiDirection direction; - pBiDi->isolateCount=0; - - if(U_FAILURE(*pErrorCode)) { return UBIDI_LTR; } - - /* determine if the text is mixed-directional or single-directional */ - direction=directionFromFlags(pBiDi); - - /* we may not need to resolve any explicit levels */ - if((direction!=UBIDI_MIXED)) { - /* not mixed directionality: levels don't matter - trailingWSStart will be 0 */ - return direction; - } - if(pBiDi->reorderingMode > UBIDI_REORDER_LAST_LOGICAL_TO_VISUAL) { - /* inverse BiDi: mixed, but all characters are at the same embedding level */ - /* set all levels to the paragraph level */ - int32_t paraIndex, start, limit; - for(paraIndex=0; paraIndexparaCount; paraIndex++) { - if(paraIndex==0) - start=0; - else - start=pBiDi->paras[paraIndex-1].limit; - limit=pBiDi->paras[paraIndex].limit; - level= static_cast(pBiDi->paras[paraIndex].level); - for(i=start; iparaCount; paraIndex++) { - if(paraIndex==0) - start=0; - else - start=pBiDi->paras[paraIndex-1].limit; - limit=pBiDi->paras[paraIndex].limit; - level= static_cast(pBiDi->paras[paraIndex].level); - for(i=start; i=UBIDI_MAX_EXPLICIT_LEVEL - but we need one more entry as base */ - uint32_t stackLast=0; - int32_t overflowIsolateCount=0; - int32_t overflowEmbeddingCount=0; - int32_t validIsolateCount=0; - BracketData bracketData; - bracketInit(pBiDi, &bracketData); - stack[0]=level; /* initialize base entry to para level, no override, no isolate */ - - /* recalculate the flags */ - flags=0; - - for(i=0; i0 && stack[stackLast]pBiDi->isolateCount) - pBiDi->isolateCount=validIsolateCount; - embeddingLevel=newLevel; - /* we can increment stackLast without checking because newLevel - will exceed UBIDI_MAX_EXPLICIT_LEVEL before stackLast overflows */ - stackLast++; - stack[stackLast]=embeddingLevel+ISOLATE; - bracketProcessLRI_RLI(&bracketData, embeddingLevel); - } else { - /* make it WS so that it is handled by adjustWSLevels() */ - dirProps[i]=WS; - overflowIsolateCount++; - } - break; - case PDI: - if(NO_OVERRIDE(embeddingLevel)!=NO_OVERRIDE(previousLevel)) { - bracketProcessBoundary(&bracketData, lastCcPos, - previousLevel, embeddingLevel); - flags|=DIRPROP_FLAG_MULTI_RUNS; - } - /* (X6a) */ - if(overflowIsolateCount) { - overflowIsolateCount--; - /* make it WS so that it is handled by adjustWSLevels() */ - dirProps[i]=WS; - } - else if(validIsolateCount) { - flags|=DIRPROP_FLAG(PDI); - lastCcPos=i; - overflowEmbeddingCount=0; - while(stack[stackLast]paraLevel); - if(pBiDi->orderParagraphsLTR && (flags&DIRPROP_FLAG(B))) - flags|=DIRPROP_FLAG(L); - /* again, determine if the text is mixed-directional or single-directional */ - pBiDi->flags=flags; - direction=directionFromFlags(pBiDi); - } - return direction; -} - -/* - * Use a pre-specified embedding levels array: - * - * Adjust the directional properties for overrides (->LEVEL_OVERRIDE), - * ignore all explicit codes (X9), - * and check all the preset levels. - * - * Recalculate the flags to have them reflect the real properties - * after taking the explicit embeddings into account. - */ -static UBiDiDirection -checkExplicitLevels(UBiDi *pBiDi, UErrorCode *pErrorCode) { - DirProp *dirProps=pBiDi->dirProps; - UBiDiLevel *levels=pBiDi->levels; - int32_t isolateCount=0; - - int32_t length=pBiDi->length; - Flags flags=0; /* collect all directionalities in the text */ - pBiDi->isolateCount=0; - - int32_t currentParaIndex = 0; - int32_t currentParaLimit = pBiDi->paras[0].limit; - int32_t currentParaLevel = pBiDi->paraLevel; - - for(int32_t i=0; ipBiDi->isolateCount) - pBiDi->isolateCount=isolateCount; - } - else if(dirProp==PDI) - isolateCount--; - else if(dirProp==B) - isolateCount=0; - - // optimized version of int32_t currentParaLevel = GET_PARALEVEL(pBiDi, i); - if (pBiDi->defaultParaLevel != 0 && - i == currentParaLimit && (currentParaIndex + 1) < pBiDi->paraCount) { - currentParaLevel = pBiDi->paras[++currentParaIndex].level; - currentParaLimit = pBiDi->paras[currentParaIndex].limit; - } - - UBiDiLevel overrideFlag = level & UBIDI_LEVEL_OVERRIDE; - level &= ~UBIDI_LEVEL_OVERRIDE; - if (level < currentParaLevel || UBIDI_MAX_EXPLICIT_LEVEL < level) { - if (level == 0) { - if (dirProp == B) { - // Paragraph separators are ok with explicit level 0. - // Prevents reordering of paragraphs. - } else { - // Treat explicit level 0 as a wildcard for the paragraph level. - // Avoid making the caller guess what the paragraph level would be. - level = (UBiDiLevel)currentParaLevel; - levels[i] = level | overrideFlag; - } - } else { - // 1 <= level < currentParaLevel or UBIDI_MAX_EXPLICIT_LEVEL < level - /* level out of bounds */ - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return UBIDI_LTR; - } - } - if (overrideFlag != 0) { - /* keep the override flag in levels[i] but adjust the flags */ - flags|=DIRPROP_FLAG_O(level); - } else { - /* set the flags */ - flags|=DIRPROP_FLAG_E(level)|DIRPROP_FLAG(dirProp); - } - } - if(flags&MASK_EMBEDDING) - flags|=DIRPROP_FLAG_LR(pBiDi->paraLevel); - /* determine if the text is mixed-directional or single-directional */ - pBiDi->flags=flags; - return directionFromFlags(pBiDi); -} - -/****************************************************************** - The Properties state machine table -******************************************************************* - - All table cells are 8 bits: - bits 0..4: next state - bits 5..7: action to perform (if > 0) - - Cells may be of format "n" where n represents the next state - (except for the rightmost column). - Cells may also be of format "s(x,y)" where x represents an action - to perform and y represents the next state. - -******************************************************************* - Definitions and type for properties state table -******************************************************************* -*/ -#define IMPTABPROPS_COLUMNS 16 -#define IMPTABPROPS_RES (IMPTABPROPS_COLUMNS - 1) -#define GET_STATEPROPS(cell) ((cell)&0x1f) -#define GET_ACTIONPROPS(cell) ((cell)>>5) -#define s(action, newState) ((uint8_t)(newState+(action<<5))) - -static const uint8_t groupProp[] = /* dirProp regrouped */ -{ -/* L R EN ES ET AN CS B S WS ON LRE LRO AL RLE RLO PDF NSM BN FSI LRI RLI PDI ENL ENR */ - 0, 1, 2, 7, 8, 3, 9, 6, 5, 4, 4, 10, 10, 12, 10, 10, 10, 11, 10, 4, 4, 4, 4, 13, 14 -}; -enum { DirProp_L=0, DirProp_R=1, DirProp_EN=2, DirProp_AN=3, DirProp_ON=4, DirProp_S=5, DirProp_B=6 }; /* reduced dirProp */ - -/****************************************************************** - - PROPERTIES STATE TABLE - - In table impTabProps, - - the ON column regroups ON and WS, FSI, RLI, LRI and PDI - - the BN column regroups BN, LRE, RLE, LRO, RLO, PDF - - the Res column is the reduced property assigned to a run - - Action 1: process current run1, init new run1 - 2: init new run2 - 3: process run1, process run2, init new run1 - 4: process run1, set run1=run2, init new run2 - - Notes: - 1) This table is used in resolveImplicitLevels(). - 2) This table triggers actions when there is a change in the Bidi - property of incoming characters (action 1). - 3) Most such property sequences are processed immediately (in - fact, passed to processPropertySeq(). - 4) However, numbers are assembled as one sequence. This means - that undefined situations (like CS following digits, until - it is known if the next char will be a digit) are held until - following chars define them. - Example: digits followed by CS, then comes another CS or ON; - the digits will be processed, then the CS assigned - as the start of an ON sequence (action 3). - 5) There are cases where more than one sequence must be - processed, for instance digits followed by CS followed by L: - the digits must be processed as one sequence, and the CS - must be processed as an ON sequence, all this before starting - assembling chars for the opening L sequence. - - -*/ -static const uint8_t impTabProps[][IMPTABPROPS_COLUMNS] = -{ -/* L , R , EN , AN , ON , S , B , ES , ET , CS , BN , NSM , AL , ENL , ENR , Res */ -/* 0 Init */ { 1 , 2 , 4 , 5 , 7 , 15 , 17 , 7 , 9 , 7 , 0 , 7 , 3 , 18 , 21 , DirProp_ON }, -/* 1 L */ { 1 , s(1,2), s(1,4), s(1,5), s(1,7),s(1,15),s(1,17), s(1,7), s(1,9), s(1,7), 1 , 1 , s(1,3),s(1,18),s(1,21), DirProp_L }, -/* 2 R */ { s(1,1), 2 , s(1,4), s(1,5), s(1,7),s(1,15),s(1,17), s(1,7), s(1,9), s(1,7), 2 , 2 , s(1,3),s(1,18),s(1,21), DirProp_R }, -/* 3 AL */ { s(1,1), s(1,2), s(1,6), s(1,6), s(1,8),s(1,16),s(1,17), s(1,8), s(1,8), s(1,8), 3 , 3 , 3 ,s(1,18),s(1,21), DirProp_R }, -/* 4 EN */ { s(1,1), s(1,2), 4 , s(1,5), s(1,7),s(1,15),s(1,17),s(2,10), 11 ,s(2,10), 4 , 4 , s(1,3), 18 , 21 , DirProp_EN }, -/* 5 AN */ { s(1,1), s(1,2), s(1,4), 5 , s(1,7),s(1,15),s(1,17), s(1,7), s(1,9),s(2,12), 5 , 5 , s(1,3),s(1,18),s(1,21), DirProp_AN }, -/* 6 AL:EN/AN */ { s(1,1), s(1,2), 6 , 6 , s(1,8),s(1,16),s(1,17), s(1,8), s(1,8),s(2,13), 6 , 6 , s(1,3), 18 , 21 , DirProp_AN }, -/* 7 ON */ { s(1,1), s(1,2), s(1,4), s(1,5), 7 ,s(1,15),s(1,17), 7 ,s(2,14), 7 , 7 , 7 , s(1,3),s(1,18),s(1,21), DirProp_ON }, -/* 8 AL:ON */ { s(1,1), s(1,2), s(1,6), s(1,6), 8 ,s(1,16),s(1,17), 8 , 8 , 8 , 8 , 8 , s(1,3),s(1,18),s(1,21), DirProp_ON }, -/* 9 ET */ { s(1,1), s(1,2), 4 , s(1,5), 7 ,s(1,15),s(1,17), 7 , 9 , 7 , 9 , 9 , s(1,3), 18 , 21 , DirProp_ON }, -/*10 EN+ES/CS */ { s(3,1), s(3,2), 4 , s(3,5), s(4,7),s(3,15),s(3,17), s(4,7),s(4,14), s(4,7), 10 , s(4,7), s(3,3), 18 , 21 , DirProp_EN }, -/*11 EN+ET */ { s(1,1), s(1,2), 4 , s(1,5), s(1,7),s(1,15),s(1,17), s(1,7), 11 , s(1,7), 11 , 11 , s(1,3), 18 , 21 , DirProp_EN }, -/*12 AN+CS */ { s(3,1), s(3,2), s(3,4), 5 , s(4,7),s(3,15),s(3,17), s(4,7),s(4,14), s(4,7), 12 , s(4,7), s(3,3),s(3,18),s(3,21), DirProp_AN }, -/*13 AL:EN/AN+CS */ { s(3,1), s(3,2), 6 , 6 , s(4,8),s(3,16),s(3,17), s(4,8), s(4,8), s(4,8), 13 , s(4,8), s(3,3), 18 , 21 , DirProp_AN }, -/*14 ON+ET */ { s(1,1), s(1,2), s(4,4), s(1,5), 7 ,s(1,15),s(1,17), 7 , 14 , 7 , 14 , 14 , s(1,3),s(4,18),s(4,21), DirProp_ON }, -/*15 S */ { s(1,1), s(1,2), s(1,4), s(1,5), s(1,7), 15 ,s(1,17), s(1,7), s(1,9), s(1,7), 15 , s(1,7), s(1,3),s(1,18),s(1,21), DirProp_S }, -/*16 AL:S */ { s(1,1), s(1,2), s(1,6), s(1,6), s(1,8), 16 ,s(1,17), s(1,8), s(1,8), s(1,8), 16 , s(1,8), s(1,3),s(1,18),s(1,21), DirProp_S }, -/*17 B */ { s(1,1), s(1,2), s(1,4), s(1,5), s(1,7),s(1,15), 17 , s(1,7), s(1,9), s(1,7), 17 , s(1,7), s(1,3),s(1,18),s(1,21), DirProp_B }, -/*18 ENL */ { s(1,1), s(1,2), 18 , s(1,5), s(1,7),s(1,15),s(1,17),s(2,19), 20 ,s(2,19), 18 , 18 , s(1,3), 18 , 21 , DirProp_L }, -/*19 ENL+ES/CS */ { s(3,1), s(3,2), 18 , s(3,5), s(4,7),s(3,15),s(3,17), s(4,7),s(4,14), s(4,7), 19 , s(4,7), s(3,3), 18 , 21 , DirProp_L }, -/*20 ENL+ET */ { s(1,1), s(1,2), 18 , s(1,5), s(1,7),s(1,15),s(1,17), s(1,7), 20 , s(1,7), 20 , 20 , s(1,3), 18 , 21 , DirProp_L }, -/*21 ENR */ { s(1,1), s(1,2), 21 , s(1,5), s(1,7),s(1,15),s(1,17),s(2,22), 23 ,s(2,22), 21 , 21 , s(1,3), 18 , 21 , DirProp_AN }, -/*22 ENR+ES/CS */ { s(3,1), s(3,2), 21 , s(3,5), s(4,7),s(3,15),s(3,17), s(4,7),s(4,14), s(4,7), 22 , s(4,7), s(3,3), 18 , 21 , DirProp_AN }, -/*23 ENR+ET */ { s(1,1), s(1,2), 21 , s(1,5), s(1,7),s(1,15),s(1,17), s(1,7), 23 , s(1,7), 23 , 23 , s(1,3), 18 , 21 , DirProp_AN } -}; - -/* we must undef macro s because the levels tables have a different - * structure (4 bits for action and 4 bits for next state. - */ -#undef s - -/****************************************************************** - The levels state machine tables -******************************************************************* - - All table cells are 8 bits: - bits 0..3: next state - bits 4..7: action to perform (if > 0) - - Cells may be of format "n" where n represents the next state - (except for the rightmost column). - Cells may also be of format "s(x,y)" where x represents an action - to perform and y represents the next state. - - This format limits each table to 16 states each and to 15 actions. - -******************************************************************* - Definitions and type for levels state tables -******************************************************************* -*/ -#define IMPTABLEVELS_COLUMNS (DirProp_B + 2) -#define IMPTABLEVELS_RES (IMPTABLEVELS_COLUMNS - 1) -#define GET_STATE(cell) ((cell)&0x0f) -#define GET_ACTION(cell) ((cell)>>4) -#define s(action, newState) ((uint8_t)(newState+(action<<4))) - -typedef uint8_t ImpTab[][IMPTABLEVELS_COLUMNS]; -typedef uint8_t ImpAct[]; - -/* FOOD FOR THOUGHT: each ImpTab should have its associated ImpAct, - * instead of having a pair of ImpTab and a pair of ImpAct. - */ -typedef struct ImpTabPair { - const void * pImpTab[2]; - const void * pImpAct[2]; -} ImpTabPair; - -/****************************************************************** - - LEVELS STATE TABLES - - In all levels state tables, - - state 0 is the initial state - - the Res column is the increment to add to the text level - for this property sequence. - - The impAct arrays for each table of a pair map the local action - numbers of the table to the total list of actions. For instance, - action 2 in a given table corresponds to the action number which - appears in entry [2] of the impAct array for that table. - The first entry of all impAct arrays must be 0. - - Action 1: init conditional sequence - 2: prepend conditional sequence to current sequence - 3: set ON sequence to new level - 1 - 4: init EN/AN/ON sequence - 5: fix EN/AN/ON sequence followed by R - 6: set previous level sequence to level 2 - - Notes: - 1) These tables are used in processPropertySeq(). The input - is property sequences as determined by resolveImplicitLevels. - 2) Most such property sequences are processed immediately - (levels are assigned). - 3) However, some sequences cannot be assigned a final level till - one or more following sequences are received. For instance, - ON following an R sequence within an even-level paragraph. - If the following sequence is R, the ON sequence will be - assigned basic run level+1, and so will the R sequence. - 4) S is generally handled like ON, since its level will be fixed - to paragraph level in adjustWSLevels(). - -*/ - -static const ImpTab impTabL_DEFAULT = /* Even paragraph level */ -/* In this table, conditional sequences receive the lower possible level - until proven otherwise. -*/ -{ -/* L , R , EN , AN , ON , S , B , Res */ -/* 0 : init */ { 0 , 1 , 0 , 2 , 0 , 0 , 0 , 0 }, -/* 1 : R */ { 0 , 1 , 3 , 3 , s(1,4), s(1,4), 0 , 1 }, -/* 2 : AN */ { 0 , 1 , 0 , 2 , s(1,5), s(1,5), 0 , 2 }, -/* 3 : R+EN/AN */ { 0 , 1 , 3 , 3 , s(1,4), s(1,4), 0 , 2 }, -/* 4 : R+ON */ { 0 , s(2,1), s(3,3), s(3,3), 4 , 4 , 0 , 0 }, -/* 5 : AN+ON */ { 0 , s(2,1), 0 , s(3,2), 5 , 5 , 0 , 0 } -}; -static const ImpTab impTabR_DEFAULT = /* Odd paragraph level */ -/* In this table, conditional sequences receive the lower possible level - until proven otherwise. -*/ -{ -/* L , R , EN , AN , ON , S , B , Res */ -/* 0 : init */ { 1 , 0 , 2 , 2 , 0 , 0 , 0 , 0 }, -/* 1 : L */ { 1 , 0 , 1 , 3 , s(1,4), s(1,4), 0 , 1 }, -/* 2 : EN/AN */ { 1 , 0 , 2 , 2 , 0 , 0 , 0 , 1 }, -/* 3 : L+AN */ { 1 , 0 , 1 , 3 , 5 , 5 , 0 , 1 }, -/* 4 : L+ON */ { s(2,1), 0 , s(2,1), 3 , 4 , 4 , 0 , 0 }, -/* 5 : L+AN+ON */ { 1 , 0 , 1 , 3 , 5 , 5 , 0 , 0 } -}; -static const ImpAct impAct0 = {0,1,2,3,4}; -static const ImpTabPair impTab_DEFAULT = {{&impTabL_DEFAULT, - &impTabR_DEFAULT}, - {&impAct0, &impAct0}}; - -static const ImpTab impTabL_NUMBERS_SPECIAL = /* Even paragraph level */ -/* In this table, conditional sequences receive the lower possible level - until proven otherwise. -*/ -{ -/* L , R , EN , AN , ON , S , B , Res */ -/* 0 : init */ { 0 , 2 , s(1,1), s(1,1), 0 , 0 , 0 , 0 }, -/* 1 : L+EN/AN */ { 0 , s(4,2), 1 , 1 , 0 , 0 , 0 , 0 }, -/* 2 : R */ { 0 , 2 , 4 , 4 , s(1,3), s(1,3), 0 , 1 }, -/* 3 : R+ON */ { 0 , s(2,2), s(3,4), s(3,4), 3 , 3 , 0 , 0 }, -/* 4 : R+EN/AN */ { 0 , 2 , 4 , 4 , s(1,3), s(1,3), 0 , 2 } -}; -static const ImpTabPair impTab_NUMBERS_SPECIAL = {{&impTabL_NUMBERS_SPECIAL, - &impTabR_DEFAULT}, - {&impAct0, &impAct0}}; - -static const ImpTab impTabL_GROUP_NUMBERS_WITH_R = -/* In this table, EN/AN+ON sequences receive levels as if associated with R - until proven that there is L or sor/eor on both sides. AN is handled like EN. -*/ -{ -/* L , R , EN , AN , ON , S , B , Res */ -/* 0 init */ { 0 , 3 , s(1,1), s(1,1), 0 , 0 , 0 , 0 }, -/* 1 EN/AN */ { s(2,0), 3 , 1 , 1 , 2 , s(2,0), s(2,0), 2 }, -/* 2 EN/AN+ON */ { s(2,0), 3 , 1 , 1 , 2 , s(2,0), s(2,0), 1 }, -/* 3 R */ { 0 , 3 , 5 , 5 , s(1,4), 0 , 0 , 1 }, -/* 4 R+ON */ { s(2,0), 3 , 5 , 5 , 4 , s(2,0), s(2,0), 1 }, -/* 5 R+EN/AN */ { 0 , 3 , 5 , 5 , s(1,4), 0 , 0 , 2 } -}; -static const ImpTab impTabR_GROUP_NUMBERS_WITH_R = -/* In this table, EN/AN+ON sequences receive levels as if associated with R - until proven that there is L on both sides. AN is handled like EN. -*/ -{ -/* L , R , EN , AN , ON , S , B , Res */ -/* 0 init */ { 2 , 0 , 1 , 1 , 0 , 0 , 0 , 0 }, -/* 1 EN/AN */ { 2 , 0 , 1 , 1 , 0 , 0 , 0 , 1 }, -/* 2 L */ { 2 , 0 , s(1,4), s(1,4), s(1,3), 0 , 0 , 1 }, -/* 3 L+ON */ { s(2,2), 0 , 4 , 4 , 3 , 0 , 0 , 0 }, -/* 4 L+EN/AN */ { s(2,2), 0 , 4 , 4 , 3 , 0 , 0 , 1 } -}; -static const ImpTabPair impTab_GROUP_NUMBERS_WITH_R = { - {&impTabL_GROUP_NUMBERS_WITH_R, - &impTabR_GROUP_NUMBERS_WITH_R}, - {&impAct0, &impAct0}}; - - -static const ImpTab impTabL_INVERSE_NUMBERS_AS_L = -/* This table is identical to the Default LTR table except that EN and AN are - handled like L. -*/ -{ -/* L , R , EN , AN , ON , S , B , Res */ -/* 0 : init */ { 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 }, -/* 1 : R */ { 0 , 1 , 0 , 0 , s(1,4), s(1,4), 0 , 1 }, -/* 2 : AN */ { 0 , 1 , 0 , 0 , s(1,5), s(1,5), 0 , 2 }, -/* 3 : R+EN/AN */ { 0 , 1 , 0 , 0 , s(1,4), s(1,4), 0 , 2 }, -/* 4 : R+ON */ { s(2,0), 1 , s(2,0), s(2,0), 4 , 4 , s(2,0), 1 }, -/* 5 : AN+ON */ { s(2,0), 1 , s(2,0), s(2,0), 5 , 5 , s(2,0), 1 } -}; -static const ImpTab impTabR_INVERSE_NUMBERS_AS_L = -/* This table is identical to the Default RTL table except that EN and AN are - handled like L. -*/ -{ -/* L , R , EN , AN , ON , S , B , Res */ -/* 0 : init */ { 1 , 0 , 1 , 1 , 0 , 0 , 0 , 0 }, -/* 1 : L */ { 1 , 0 , 1 , 1 , s(1,4), s(1,4), 0 , 1 }, -/* 2 : EN/AN */ { 1 , 0 , 1 , 1 , 0 , 0 , 0 , 1 }, -/* 3 : L+AN */ { 1 , 0 , 1 , 1 , 5 , 5 , 0 , 1 }, -/* 4 : L+ON */ { s(2,1), 0 , s(2,1), s(2,1), 4 , 4 , 0 , 0 }, -/* 5 : L+AN+ON */ { 1 , 0 , 1 , 1 , 5 , 5 , 0 , 0 } -}; -static const ImpTabPair impTab_INVERSE_NUMBERS_AS_L = { - {&impTabL_INVERSE_NUMBERS_AS_L, - &impTabR_INVERSE_NUMBERS_AS_L}, - {&impAct0, &impAct0}}; - -static const ImpTab impTabR_INVERSE_LIKE_DIRECT = /* Odd paragraph level */ -/* In this table, conditional sequences receive the lower possible level - until proven otherwise. -*/ -{ -/* L , R , EN , AN , ON , S , B , Res */ -/* 0 : init */ { 1 , 0 , 2 , 2 , 0 , 0 , 0 , 0 }, -/* 1 : L */ { 1 , 0 , 1 , 2 , s(1,3), s(1,3), 0 , 1 }, -/* 2 : EN/AN */ { 1 , 0 , 2 , 2 , 0 , 0 , 0 , 1 }, -/* 3 : L+ON */ { s(2,1), s(3,0), 6 , 4 , 3 , 3 , s(3,0), 0 }, -/* 4 : L+ON+AN */ { s(2,1), s(3,0), 6 , 4 , 5 , 5 , s(3,0), 3 }, -/* 5 : L+AN+ON */ { s(2,1), s(3,0), 6 , 4 , 5 , 5 , s(3,0), 2 }, -/* 6 : L+ON+EN */ { s(2,1), s(3,0), 6 , 4 , 3 , 3 , s(3,0), 1 } -}; -static const ImpAct impAct1 = {0,1,13,14}; -/* FOOD FOR THOUGHT: in LTR table below, check case "JKL 123abc" - */ -static const ImpTabPair impTab_INVERSE_LIKE_DIRECT = { - {&impTabL_DEFAULT, - &impTabR_INVERSE_LIKE_DIRECT}, - {&impAct0, &impAct1}}; - -static const ImpTab impTabL_INVERSE_LIKE_DIRECT_WITH_MARKS = -/* The case handled in this table is (visually): R EN L -*/ -{ -/* L , R , EN , AN , ON , S , B , Res */ -/* 0 : init */ { 0 , s(6,3), 0 , 1 , 0 , 0 , 0 , 0 }, -/* 1 : L+AN */ { 0 , s(6,3), 0 , 1 , s(1,2), s(3,0), 0 , 4 }, -/* 2 : L+AN+ON */ { s(2,0), s(6,3), s(2,0), 1 , 2 , s(3,0), s(2,0), 3 }, -/* 3 : R */ { 0 , s(6,3), s(5,5), s(5,6), s(1,4), s(3,0), 0 , 3 }, -/* 4 : R+ON */ { s(3,0), s(4,3), s(5,5), s(5,6), 4 , s(3,0), s(3,0), 3 }, -/* 5 : R+EN */ { s(3,0), s(4,3), 5 , s(5,6), s(1,4), s(3,0), s(3,0), 4 }, -/* 6 : R+AN */ { s(3,0), s(4,3), s(5,5), 6 , s(1,4), s(3,0), s(3,0), 4 } -}; -static const ImpTab impTabR_INVERSE_LIKE_DIRECT_WITH_MARKS = -/* The cases handled in this table are (visually): R EN L - R L AN L -*/ -{ -/* L , R , EN , AN , ON , S , B , Res */ -/* 0 : init */ { s(1,3), 0 , 1 , 1 , 0 , 0 , 0 , 0 }, -/* 1 : R+EN/AN */ { s(2,3), 0 , 1 , 1 , 2 , s(4,0), 0 , 1 }, -/* 2 : R+EN/AN+ON */ { s(2,3), 0 , 1 , 1 , 2 , s(4,0), 0 , 0 }, -/* 3 : L */ { 3 , 0 , 3 , s(3,6), s(1,4), s(4,0), 0 , 1 }, -/* 4 : L+ON */ { s(5,3), s(4,0), 5 , s(3,6), 4 , s(4,0), s(4,0), 0 }, -/* 5 : L+ON+EN */ { s(5,3), s(4,0), 5 , s(3,6), 4 , s(4,0), s(4,0), 1 }, -/* 6 : L+AN */ { s(5,3), s(4,0), 6 , 6 , 4 , s(4,0), s(4,0), 3 } -}; -static const ImpAct impAct2 = {0,1,2,5,6,7,8}; -static const ImpAct impAct3 = {0,1,9,10,11,12}; -static const ImpTabPair impTab_INVERSE_LIKE_DIRECT_WITH_MARKS = { - {&impTabL_INVERSE_LIKE_DIRECT_WITH_MARKS, - &impTabR_INVERSE_LIKE_DIRECT_WITH_MARKS}, - {&impAct2, &impAct3}}; - -static const ImpTabPair impTab_INVERSE_FOR_NUMBERS_SPECIAL = { - {&impTabL_NUMBERS_SPECIAL, - &impTabR_INVERSE_LIKE_DIRECT}, - {&impAct0, &impAct1}}; - -static const ImpTab impTabL_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS = -/* The case handled in this table is (visually): R EN L -*/ -{ -/* L , R , EN , AN , ON , S , B , Res */ -/* 0 : init */ { 0 , s(6,2), 1 , 1 , 0 , 0 , 0 , 0 }, -/* 1 : L+EN/AN */ { 0 , s(6,2), 1 , 1 , 0 , s(3,0), 0 , 4 }, -/* 2 : R */ { 0 , s(6,2), s(5,4), s(5,4), s(1,3), s(3,0), 0 , 3 }, -/* 3 : R+ON */ { s(3,0), s(4,2), s(5,4), s(5,4), 3 , s(3,0), s(3,0), 3 }, -/* 4 : R+EN/AN */ { s(3,0), s(4,2), 4 , 4 , s(1,3), s(3,0), s(3,0), 4 } -}; -static const ImpTabPair impTab_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS = { - {&impTabL_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS, - &impTabR_INVERSE_LIKE_DIRECT_WITH_MARKS}, - {&impAct2, &impAct3}}; - -#undef s - -typedef struct { - const ImpTab * pImpTab; /* level table pointer */ - const ImpAct * pImpAct; /* action map array */ - int32_t startON; /* start of ON sequence */ - int32_t startL2EN; /* start of level 2 sequence */ - int32_t lastStrongRTL; /* index of last found R or AL */ - int32_t state; /* current state */ - int32_t runStart; /* start position of the run */ - UBiDiLevel runLevel; /* run level before implicit solving */ -} LevState; - -/*------------------------------------------------------------------------*/ - -static void -addPoint(UBiDi *pBiDi, int32_t pos, int32_t flag) - /* param pos: position where to insert - param flag: one of LRM_BEFORE, LRM_AFTER, RLM_BEFORE, RLM_AFTER - */ -{ -#define FIRSTALLOC 10 - Point point; - InsertPoints * pInsertPoints=&(pBiDi->insertPoints); - - if (pInsertPoints->capacity == 0) - { - pInsertPoints->points=static_cast(uprv_malloc(sizeof(Point)*FIRSTALLOC)); - if (pInsertPoints->points == NULL) - { - pInsertPoints->errorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - pInsertPoints->capacity=FIRSTALLOC; - } - if (pInsertPoints->size >= pInsertPoints->capacity) /* no room for new point */ - { - Point * savePoints=pInsertPoints->points; - pInsertPoints->points=static_cast(uprv_realloc(pInsertPoints->points, - pInsertPoints->capacity*2*sizeof(Point))); - if (pInsertPoints->points == NULL) - { - pInsertPoints->points=savePoints; - pInsertPoints->errorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - else pInsertPoints->capacity*=2; - } - point.pos=pos; - point.flag=flag; - pInsertPoints->points[pInsertPoints->size]=point; - pInsertPoints->size++; -#undef FIRSTALLOC -} - -static void -setLevelsOutsideIsolates(UBiDi *pBiDi, int32_t start, int32_t limit, UBiDiLevel level) -{ - DirProp *dirProps=pBiDi->dirProps, dirProp; - UBiDiLevel *levels=pBiDi->levels; - int32_t isolateCount=0, k; - for(k=start; kpImpTab; - const ImpAct * pImpAct=pLevState->pImpAct; - UBiDiLevel * levels=pBiDi->levels; - UBiDiLevel level, addLevel; - InsertPoints * pInsertPoints; - int32_t start0, k; - - start0=start; /* save original start position */ - oldStateSeq=(uint8_t)pLevState->state; - cell=(*pImpTab)[oldStateSeq][_prop]; - pLevState->state=GET_STATE(cell); /* isolate the new state */ - actionSeq=(*pImpAct)[GET_ACTION(cell)]; /* isolate the action */ - addLevel=(*pImpTab)[pLevState->state][IMPTABLEVELS_RES]; - - if(actionSeq) { - switch(actionSeq) { - case 1: /* init ON seq */ - pLevState->startON=start0; - break; - - case 2: /* prepend ON seq to current seq */ - start=pLevState->startON; - break; - - case 3: /* EN/AN after R+ON */ - level=pLevState->runLevel+1; - setLevelsOutsideIsolates(pBiDi, pLevState->startON, start0, level); - break; - - case 4: /* EN/AN before R for NUMBERS_SPECIAL */ - level=pLevState->runLevel+2; - setLevelsOutsideIsolates(pBiDi, pLevState->startON, start0, level); - break; - - case 5: /* L or S after possible relevant EN/AN */ - /* check if we had EN after R/AL */ - if (pLevState->startL2EN >= 0) { - addPoint(pBiDi, pLevState->startL2EN, LRM_BEFORE); - } - pLevState->startL2EN=-1; /* not within previous if since could also be -2 */ - /* check if we had any relevant EN/AN after R/AL */ - pInsertPoints=&(pBiDi->insertPoints); - if ((pInsertPoints->capacity == 0) || - (pInsertPoints->size <= pInsertPoints->confirmed)) - { - /* nothing, just clean up */ - pLevState->lastStrongRTL=-1; - /* check if we have a pending conditional segment */ - level=(*pImpTab)[oldStateSeq][IMPTABLEVELS_RES]; - if ((level & 1) && (pLevState->startON > 0)) { /* after ON */ - start=pLevState->startON; /* reset to basic run level */ - } - if (_prop == DirProp_S) /* add LRM before S */ - { - addPoint(pBiDi, start0, LRM_BEFORE); - pInsertPoints->confirmed=pInsertPoints->size; - } - break; - } - /* reset previous RTL cont to level for LTR text */ - for (k=pLevState->lastStrongRTL+1; kconfirmed=pInsertPoints->size; - pLevState->lastStrongRTL=-1; - if (_prop == DirProp_S) /* add LRM before S */ - { - addPoint(pBiDi, start0, LRM_BEFORE); - pInsertPoints->confirmed=pInsertPoints->size; - } - break; - - case 6: /* R/AL after possible relevant EN/AN */ - /* just clean up */ - pInsertPoints=&(pBiDi->insertPoints); - if (pInsertPoints->capacity > 0) - /* remove all non confirmed insert points */ - pInsertPoints->size=pInsertPoints->confirmed; - pLevState->startON=-1; - pLevState->startL2EN=-1; - pLevState->lastStrongRTL=limit - 1; - break; - - case 7: /* EN/AN after R/AL + possible cont */ - /* check for real AN */ - if ((_prop == DirProp_AN) && (pBiDi->dirProps[start0] == AN) && - (pBiDi->reorderingMode!=UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL)) - { - /* real AN */ - if (pLevState->startL2EN == -1) /* if no relevant EN already found */ - { - /* just note the righmost digit as a strong RTL */ - pLevState->lastStrongRTL=limit - 1; - break; - } - if (pLevState->startL2EN >= 0) /* after EN, no AN */ - { - addPoint(pBiDi, pLevState->startL2EN, LRM_BEFORE); - pLevState->startL2EN=-2; - } - /* note AN */ - addPoint(pBiDi, start0, LRM_BEFORE); - break; - } - /* if first EN/AN after R/AL */ - if (pLevState->startL2EN == -1) { - pLevState->startL2EN=start0; - } - break; - - case 8: /* note location of latest R/AL */ - pLevState->lastStrongRTL=limit - 1; - pLevState->startON=-1; - break; - - case 9: /* L after R+ON/EN/AN */ - /* include possible adjacent number on the left */ - for (k=start0-1; k>=0 && !(levels[k]&1); k--); - if(k>=0) { - addPoint(pBiDi, k, RLM_BEFORE); /* add RLM before */ - pInsertPoints=&(pBiDi->insertPoints); - pInsertPoints->confirmed=pInsertPoints->size; /* confirm it */ - } - pLevState->startON=start0; - break; - - case 10: /* AN after L */ - /* AN numbers between L text on both sides may be trouble. */ - /* tentatively bracket with LRMs; will be confirmed if followed by L */ - addPoint(pBiDi, start0, LRM_BEFORE); /* add LRM before */ - addPoint(pBiDi, start0, LRM_AFTER); /* add LRM after */ - break; - - case 11: /* R after L+ON/EN/AN */ - /* false alert, infirm LRMs around previous AN */ - pInsertPoints=&(pBiDi->insertPoints); - pInsertPoints->size=pInsertPoints->confirmed; - if (_prop == DirProp_S) /* add RLM before S */ - { - addPoint(pBiDi, start0, RLM_BEFORE); - pInsertPoints->confirmed=pInsertPoints->size; - } - break; - - case 12: /* L after L+ON/AN */ - level=pLevState->runLevel + addLevel; - for(k=pLevState->startON; kinsertPoints); - pInsertPoints->confirmed=pInsertPoints->size; /* confirm inserts */ - pLevState->startON=start0; - break; - - case 13: /* L after L+ON+EN/AN/ON */ - level=pLevState->runLevel; - for(k=start0-1; k>=pLevState->startON; k--) { - if(levels[k]==level+3) { - while(levels[k]==level+3) { - levels[k--]-=2; - } - while(levels[k]==level) { - k--; - } - } - if(levels[k]==level+2) { - levels[k]=level; - continue; - } - levels[k]=level+1; - } - break; - - case 14: /* R after L+ON+EN/AN/ON */ - level=pLevState->runLevel+1; - for(k=start0-1; k>=pLevState->startON; k--) { - if(levels[k]>level) { - levels[k]-=2; - } - } - break; - - default: /* we should never get here */ - UPRV_UNREACHABLE_EXIT; - } - } - if((addLevel) || (start < start0)) { - level=pLevState->runLevel + addLevel; - if(start>=pLevState->runStart) { - for(k=start; kprologue; - int32_t length=pBiDi->proLength; - int32_t i; - UChar32 uchar; - DirProp dirProp; - for(i=length; i>0; ) { - /* i is decremented by U16_PREV */ - U16_PREV(text, 0, i, uchar); - dirProp=(DirProp)ubidi_getCustomizedClass(pBiDi, uchar); - if(dirProp==L) { - return DirProp_L; - } - if(dirProp==R || dirProp==AL) { - return DirProp_R; - } - if(dirProp==B) { - return DirProp_ON; - } - } - return DirProp_ON; -} - -/** - * Returns the directionality of the first strong character, or digit, in the epilogue, if any. - * Requires epilogue!=null. - */ -static DirProp -firstL_R_AL_EN_AN(UBiDi *pBiDi) { - const UChar *text=pBiDi->epilogue; - int32_t length=pBiDi->epiLength; - int32_t i; - UChar32 uchar; - DirProp dirProp; - for(i=0; idirProps; - DirProp dirProp; - LevState levState; - int32_t i, start1, start2; - uint16_t oldStateImp, stateImp, actionImp; - uint8_t gprop, resProp, cell; - UBool inverseRTL; - DirProp nextStrongProp=R; - int32_t nextStrongPos=-1; - - /* check for RTL inverse BiDi mode */ - /* FOOD FOR THOUGHT: in case of RTL inverse BiDi, it would make sense to - * loop on the text characters from end to start. - * This would need a different properties state table (at least different - * actions) and different levels state tables (maybe very similar to the - * LTR corresponding ones. - */ - inverseRTL=(UBool) - ((startlastArabicPos) && (GET_PARALEVEL(pBiDi, start) & 1) && - (pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_LIKE_DIRECT || - pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL)); - - /* initialize for property and levels state tables */ - levState.startL2EN=-1; /* used for INVERSE_LIKE_DIRECT_WITH_MARKS */ - levState.lastStrongRTL=-1; /* used for INVERSE_LIKE_DIRECT_WITH_MARKS */ - levState.runStart=start; - levState.runLevel=pBiDi->levels[start]; - levState.pImpTab=(const ImpTab*)((pBiDi->pImpTabPair)->pImpTab)[levState.runLevel&1]; - levState.pImpAct=(const ImpAct*)((pBiDi->pImpTabPair)->pImpAct)[levState.runLevel&1]; - if(start==0 && pBiDi->proLength>0) { - DirProp lastStrong=lastL_R_AL(pBiDi); - if(lastStrong!=DirProp_ON) { - sor=lastStrong; - } - } - /* The isolates[] entries contain enough information to - resume the bidi algorithm in the same state as it was - when it was interrupted by an isolate sequence. */ - if(dirProps[start]==PDI && pBiDi->isolateCount >= 0) { - levState.startON=pBiDi->isolates[pBiDi->isolateCount].startON; - start1=pBiDi->isolates[pBiDi->isolateCount].start1; - stateImp=pBiDi->isolates[pBiDi->isolateCount].stateImp; - levState.state=pBiDi->isolates[pBiDi->isolateCount].state; - pBiDi->isolateCount--; - } else { - levState.startON=-1; - start1=start; - if(dirProps[start]==NSM) - stateImp = 1 + sor; - else - stateImp=0; - levState.state=0; - processPropertySeq(pBiDi, &levState, sor, start, start); - } - start2=start; /* to make Java compiler happy */ - - for(i=start; i<=limit; i++) { - if(i>=limit) { - int32_t k; - for(k=limit-1; k>start&&(DIRPROP_FLAG(dirProps[k])&MASK_BN_EXPLICIT); k--); - dirProp=dirProps[k]; - if(dirProp==LRI || dirProp==RLI) - break; /* no forced closing for sequence ending with LRI/RLI */ - gprop=eor; - } else { - DirProp prop, prop1; - prop=dirProps[i]; - if(prop==B) { - pBiDi->isolateCount=-1; /* current isolates stack entry == none */ - } - if(inverseRTL) { - if(prop==AL) { - /* AL before EN does not make it AN */ - prop=R; - } else if(prop==EN) { - if(nextStrongPos<=i) { - /* look for next strong char (L/R/AL) */ - int32_t j; - nextStrongProp=R; /* set default */ - nextStrongPos=limit; - for(j=i+1; jlength && pBiDi->epiLength>0) { - DirProp firstStrong=firstL_R_AL_EN_AN(pBiDi); - if(firstStrong!=DirProp_ON) { - eor=firstStrong; - } - } - - /* look for the last char not a BN or LRE/RLE/LRO/RLO/PDF */ - for(i=limit-1; i>start&&(DIRPROP_FLAG(dirProps[i])&MASK_BN_EXPLICIT); i--); - dirProp=dirProps[i]; - if((dirProp==LRI || dirProp==RLI) && limitlength) { - pBiDi->isolateCount++; - pBiDi->isolates[pBiDi->isolateCount].stateImp=stateImp; - pBiDi->isolates[pBiDi->isolateCount].state=levState.state; - pBiDi->isolates[pBiDi->isolateCount].start1=start1; - pBiDi->isolates[pBiDi->isolateCount].startON=levState.startON; - } - else - processPropertySeq(pBiDi, &levState, eor, limit, limit); -} - -/* perform (L1) and (X9) ---------------------------------------------------- */ - -/* - * Reset the embedding levels for some non-graphic characters (L1). - * This function also sets appropriate levels for BN, and - * explicit embedding types that are supposed to have been removed - * from the paragraph in (X9). - */ -static void -adjustWSLevels(UBiDi *pBiDi) { - const DirProp *dirProps=pBiDi->dirProps; - UBiDiLevel *levels=pBiDi->levels; - int32_t i; - - if(pBiDi->flags&MASK_WS) { - UBool orderParagraphsLTR=pBiDi->orderParagraphsLTR; - Flags flag; - - i=pBiDi->trailingWSStart; - while(i>0) { - /* reset a sequence of WS/BN before eop and B/S to the paragraph paraLevel */ - while(i>0 && (flag=DIRPROP_FLAG(dirProps[--i]))&MASK_WS) { - if(orderParagraphsLTR&&(flag&DIRPROP_FLAG(B))) { - levels[i]=0; - } else { - levels[i]=GET_PARALEVEL(pBiDi, i); - } - } - - /* reset BN to the next character's paraLevel until B/S, which restarts above loop */ - /* here, i+1 is guaranteed to be 0) { - flag=DIRPROP_FLAG(dirProps[--i]); - if(flag&MASK_BN_EXPLICIT) { - levels[i]=levels[i+1]; - } else if(orderParagraphsLTR&&(flag&DIRPROP_FLAG(B))) { - levels[i]=0; - break; - } else if(flag&MASK_B_S) { - levels[i]=GET_PARALEVEL(pBiDi, i); - break; - } - } - } - } -} - -U_CAPI void U_EXPORT2 -ubidi_setContext(UBiDi *pBiDi, - const UChar *prologue, int32_t proLength, - const UChar *epilogue, int32_t epiLength, - UErrorCode *pErrorCode) { - /* check the argument values */ - RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); - if(pBiDi==NULL || proLength<-1 || epiLength<-1 || - (prologue==NULL && proLength!=0) || (epilogue==NULL && epiLength!=0)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - if(proLength==-1) { - pBiDi->proLength=u_strlen(prologue); - } else { - pBiDi->proLength=proLength; - } - if(epiLength==-1) { - pBiDi->epiLength=u_strlen(epilogue); - } else { - pBiDi->epiLength=epiLength; - } - pBiDi->prologue=prologue; - pBiDi->epilogue=epilogue; -} - -static void -setParaSuccess(UBiDi *pBiDi) { - pBiDi->proLength=0; /* forget the last context */ - pBiDi->epiLength=0; - pBiDi->pParaBiDi=pBiDi; /* mark successful setPara */ -} - -#define BIDI_MIN(x, y) ((x)<(y) ? (x) : (y)) -#define BIDI_ABS(x) ((x)>=0 ? (x) : (-(x))) - -static void -setParaRunsOnly(UBiDi *pBiDi, const UChar *text, int32_t length, - UBiDiLevel paraLevel, UErrorCode *pErrorCode) { - int32_t *runsOnlyMemory = NULL; - int32_t *visualMap; - UChar *visualText; - int32_t saveLength, saveTrailingWSStart; - const UBiDiLevel *levels; - UBiDiLevel *saveLevels; - UBiDiDirection saveDirection; - UBool saveMayAllocateText; - Run *runs; - int32_t visualLength, i, j, visualStart, logicalStart, - runCount, runLength, addedRuns, insertRemove, - start, limit, step, indexOddBit, logicalPos, - index0, index1; - uint32_t saveOptions; - - pBiDi->reorderingMode=UBIDI_REORDER_DEFAULT; - if(length==0) { - ubidi_setPara(pBiDi, text, length, paraLevel, NULL, pErrorCode); - goto cleanup3; - } - /* obtain memory for mapping table and visual text */ - runsOnlyMemory=static_cast(uprv_malloc(length*(sizeof(int32_t)+sizeof(UChar)+sizeof(UBiDiLevel)))); - if(runsOnlyMemory==NULL) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - goto cleanup3; - } - visualMap=runsOnlyMemory; - visualText=(UChar *)&visualMap[length]; - saveLevels=(UBiDiLevel *)&visualText[length]; - saveOptions=pBiDi->reorderingOptions; - if(saveOptions & UBIDI_OPTION_INSERT_MARKS) { - pBiDi->reorderingOptions&=~UBIDI_OPTION_INSERT_MARKS; - pBiDi->reorderingOptions|=UBIDI_OPTION_REMOVE_CONTROLS; - } - paraLevel&=1; /* accept only 0 or 1 */ - ubidi_setPara(pBiDi, text, length, paraLevel, NULL, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - goto cleanup3; - } - /* we cannot access directly pBiDi->levels since it is not yet set if - * direction is not MIXED - */ - levels=ubidi_getLevels(pBiDi, pErrorCode); - uprv_memcpy(saveLevels, levels, (size_t)pBiDi->length*sizeof(UBiDiLevel)); - saveTrailingWSStart=pBiDi->trailingWSStart; - saveLength=pBiDi->length; - saveDirection=pBiDi->direction; - - /* FOOD FOR THOUGHT: instead of writing the visual text, we could use - * the visual map and the dirProps array to drive the second call - * to ubidi_setPara (but must make provision for possible removal of - * BiDi controls. Alternatively, only use the dirProps array via - * customized classifier callback. - */ - visualLength=ubidi_writeReordered(pBiDi, visualText, length, - UBIDI_DO_MIRRORING, pErrorCode); - ubidi_getVisualMap(pBiDi, visualMap, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - goto cleanup2; - } - pBiDi->reorderingOptions=saveOptions; - - pBiDi->reorderingMode=UBIDI_REORDER_INVERSE_LIKE_DIRECT; - paraLevel^=1; - /* Because what we did with reorderingOptions, visualText may be shorter - * than the original text. But we don't want the levels memory to be - * reallocated shorter than the original length, since we need to restore - * the levels as after the first call to ubidi_setpara() before returning. - * We will force mayAllocateText to false before the second call to - * ubidi_setpara(), and will restore it afterwards. - */ - saveMayAllocateText=pBiDi->mayAllocateText; - pBiDi->mayAllocateText=false; - ubidi_setPara(pBiDi, visualText, visualLength, paraLevel, NULL, pErrorCode); - pBiDi->mayAllocateText=saveMayAllocateText; - ubidi_getRuns(pBiDi, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - goto cleanup1; - } - /* check if some runs must be split, count how many splits */ - addedRuns=0; - runCount=pBiDi->runCount; - runs=pBiDi->runs; - visualStart=0; - for(i=0; irunsMemory[0]=runs[0]; - } - runs=pBiDi->runs=pBiDi->runsMemory; - pBiDi->runCount+=addedRuns; - } else { - goto cleanup1; - } - } - /* split runs which are not consecutive in source text */ - for(i=runCount-1; i>=0; i--) { - runLength= i==0 ? runs[0].visualLimit : - runs[i].visualLimit-runs[i-1].visualLimit; - logicalStart=runs[i].logicalStart; - indexOddBit=GET_ODD_BIT(logicalStart); - logicalStart=GET_INDEX(logicalStart); - if(runLength<2) { - if(addedRuns) { - runs[i+addedRuns]=runs[i]; - } - logicalPos=visualMap[logicalStart]; - runs[i+addedRuns].logicalStart=MAKE_INDEX_ODD_PAIR(logicalPos, - saveLevels[logicalPos]^indexOddBit); - continue; - } - if(indexOddBit) { - start=logicalStart; - limit=logicalStart+runLength-1; - step=1; - } else { - start=logicalStart+runLength-1; - limit=logicalStart; - step=-1; - } - for(j=start; j!=limit; j+=step) { - index0=visualMap[j]; - index1=visualMap[j+step]; - if((BIDI_ABS(index0-index1)!=1) || (saveLevels[index0]!=saveLevels[index1])) { - logicalPos=BIDI_MIN(visualMap[start], index0); - runs[i+addedRuns].logicalStart=MAKE_INDEX_ODD_PAIR(logicalPos, - saveLevels[logicalPos]^indexOddBit); - runs[i+addedRuns].visualLimit=runs[i].visualLimit; - runs[i].visualLimit-=BIDI_ABS(j-start)+1; - insertRemove=runs[i].insertRemove&(LRM_AFTER|RLM_AFTER); - runs[i+addedRuns].insertRemove=insertRemove; - runs[i].insertRemove&=~insertRemove; - start=j+step; - addedRuns--; - } - } - if(addedRuns) { - runs[i+addedRuns]=runs[i]; - } - logicalPos=BIDI_MIN(visualMap[start], visualMap[limit]); - runs[i+addedRuns].logicalStart=MAKE_INDEX_ODD_PAIR(logicalPos, - saveLevels[logicalPos]^indexOddBit); - } - - cleanup1: - /* restore initial paraLevel */ - pBiDi->paraLevel^=1; - cleanup2: - /* restore real text */ - pBiDi->text=text; - pBiDi->length=saveLength; - pBiDi->originalLength=length; - pBiDi->direction=saveDirection; - /* the saved levels should never excess levelsSize, but we check anyway */ - if(saveLength>pBiDi->levelsSize) { - saveLength=pBiDi->levelsSize; - } - uprv_memcpy(pBiDi->levels, saveLevels, (size_t)saveLength*sizeof(UBiDiLevel)); - pBiDi->trailingWSStart=saveTrailingWSStart; - if(pBiDi->runCount>1) { - pBiDi->direction=UBIDI_MIXED; - } - cleanup3: - /* free memory for mapping table and visual text */ - uprv_free(runsOnlyMemory); - - pBiDi->reorderingMode=UBIDI_REORDER_RUNS_ONLY; -} - -/* ubidi_setPara ------------------------------------------------------------ */ - -U_CAPI void U_EXPORT2 -ubidi_setPara(UBiDi *pBiDi, const UChar *text, int32_t length, - UBiDiLevel paraLevel, UBiDiLevel *embeddingLevels, - UErrorCode *pErrorCode) { - UBiDiDirection direction; - DirProp *dirProps; - - /* check the argument values */ - RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); - if(pBiDi==NULL || text==NULL || length<-1 || - (paraLevel>UBIDI_MAX_EXPLICIT_LEVEL && paraLevelreorderingMode==UBIDI_REORDER_RUNS_ONLY) { - setParaRunsOnly(pBiDi, text, length, paraLevel, pErrorCode); - return; - } - - /* initialize the UBiDi structure */ - pBiDi->pParaBiDi=NULL; /* mark unfinished setPara */ - pBiDi->text=text; - pBiDi->length=pBiDi->originalLength=pBiDi->resultLength=length; - pBiDi->paraLevel=paraLevel; - pBiDi->direction=(UBiDiDirection)(paraLevel&1); - pBiDi->paraCount=1; - - pBiDi->dirProps=NULL; - pBiDi->levels=NULL; - pBiDi->runs=NULL; - pBiDi->insertPoints.size=0; /* clean up from last call */ - pBiDi->insertPoints.confirmed=0; /* clean up from last call */ - - /* - * Save the original paraLevel if contextual; otherwise, set to 0. - */ - pBiDi->defaultParaLevel=IS_DEFAULT_LEVEL(paraLevel); - - if(length==0) { - /* - * For an empty paragraph, create a UBiDi object with the paraLevel and - * the flags and the direction set but without allocating zero-length arrays. - * There is nothing more to do. - */ - if(IS_DEFAULT_LEVEL(paraLevel)) { - pBiDi->paraLevel&=1; - pBiDi->defaultParaLevel=0; - } - pBiDi->flags=DIRPROP_FLAG_LR(paraLevel); - pBiDi->runCount=0; - pBiDi->paraCount=0; - setParaSuccess(pBiDi); /* mark successful setPara */ - return; - } - - pBiDi->runCount=-1; - - /* allocate paras memory */ - if(pBiDi->parasMemory) - pBiDi->paras=pBiDi->parasMemory; - else - pBiDi->paras=pBiDi->simpleParas; - - /* - * Get the directional properties, - * the flags bit-set, and - * determine the paragraph level if necessary. - */ - if(getDirPropsMemory(pBiDi, length)) { - pBiDi->dirProps=pBiDi->dirPropsMemory; - if(!getDirProps(pBiDi)) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - } else { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - dirProps=pBiDi->dirProps; - /* the processed length may have changed if UBIDI_OPTION_STREAMING */ - length= pBiDi->length; - pBiDi->trailingWSStart=length; /* the levels[] will reflect the WS run */ - - /* are explicit levels specified? */ - if(embeddingLevels==NULL) { - /* no: determine explicit levels according to the (Xn) rules */\ - if(getLevelsMemory(pBiDi, length)) { - pBiDi->levels=pBiDi->levelsMemory; - direction=resolveExplicitLevels(pBiDi, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return; - } - } else { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - } else { - /* set BN for all explicit codes, check that all levels are 0 or paraLevel..UBIDI_MAX_EXPLICIT_LEVEL */ - pBiDi->levels=embeddingLevels; - direction=checkExplicitLevels(pBiDi, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return; - } - } - - /* allocate isolate memory */ - if(pBiDi->isolateCount<=SIMPLE_ISOLATES_COUNT) - pBiDi->isolates=pBiDi->simpleIsolates; - else - if((int32_t)(pBiDi->isolateCount*sizeof(Isolate))<=pBiDi->isolatesSize) - pBiDi->isolates=pBiDi->isolatesMemory; - else { - if(getInitialIsolatesMemory(pBiDi, pBiDi->isolateCount)) { - pBiDi->isolates=pBiDi->isolatesMemory; - } else { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - } - pBiDi->isolateCount=-1; /* current isolates stack entry == none */ - - /* - * The steps after (X9) in the UBiDi algorithm are performed only if - * the paragraph text has mixed directionality! - */ - pBiDi->direction=direction; - switch(direction) { - case UBIDI_LTR: - /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */ - pBiDi->trailingWSStart=0; - break; - case UBIDI_RTL: - /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */ - pBiDi->trailingWSStart=0; - break; - default: - /* - * Choose the right implicit state table - */ - switch(pBiDi->reorderingMode) { - case UBIDI_REORDER_DEFAULT: - pBiDi->pImpTabPair=&impTab_DEFAULT; - break; - case UBIDI_REORDER_NUMBERS_SPECIAL: - pBiDi->pImpTabPair=&impTab_NUMBERS_SPECIAL; - break; - case UBIDI_REORDER_GROUP_NUMBERS_WITH_R: - pBiDi->pImpTabPair=&impTab_GROUP_NUMBERS_WITH_R; - break; - case UBIDI_REORDER_INVERSE_NUMBERS_AS_L: - pBiDi->pImpTabPair=&impTab_INVERSE_NUMBERS_AS_L; - break; - case UBIDI_REORDER_INVERSE_LIKE_DIRECT: - if (pBiDi->reorderingOptions & UBIDI_OPTION_INSERT_MARKS) { - pBiDi->pImpTabPair=&impTab_INVERSE_LIKE_DIRECT_WITH_MARKS; - } else { - pBiDi->pImpTabPair=&impTab_INVERSE_LIKE_DIRECT; - } - break; - case UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL: - if (pBiDi->reorderingOptions & UBIDI_OPTION_INSERT_MARKS) { - pBiDi->pImpTabPair=&impTab_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS; - } else { - pBiDi->pImpTabPair=&impTab_INVERSE_FOR_NUMBERS_SPECIAL; - } - break; - default: - /* we should never get here */ - UPRV_UNREACHABLE_EXIT; - } - /* - * If there are no external levels specified and there - * are no significant explicit level codes in the text, - * then we can treat the entire paragraph as one run. - * Otherwise, we need to perform the following rules on runs of - * the text with the same embedding levels. (X10) - * "Significant" explicit level codes are ones that actually - * affect non-BN characters. - * Examples for "insignificant" ones are empty embeddings - * LRE-PDF, LRE-RLE-PDF-PDF, etc. - */ - if(embeddingLevels==NULL && pBiDi->paraCount<=1 && - !(pBiDi->flags&DIRPROP_FLAG_MULTI_RUNS)) { - resolveImplicitLevels(pBiDi, 0, length, - GET_LR_FROM_LEVEL(GET_PARALEVEL(pBiDi, 0)), - GET_LR_FROM_LEVEL(GET_PARALEVEL(pBiDi, length-1))); - } else { - /* sor, eor: start and end types of same-level-run */ - UBiDiLevel *levels=pBiDi->levels; - int32_t start, limit=0; - UBiDiLevel level, nextLevel; - DirProp sor, eor; - - /* determine the first sor and set eor to it because of the loop body (sor=eor there) */ - level=GET_PARALEVEL(pBiDi, 0); - nextLevel=levels[0]; - if(level0) && (dirProps[start-1]==B)) { - /* except if this is a new paragraph, then set sor = para level */ - sor=GET_LR_FROM_LEVEL(GET_PARALEVEL(pBiDi, start)); - } else { - sor=eor; - } - - /* search for the limit of this run */ - while((++limitinsertPoints.errorCode)) - { - *pErrorCode=pBiDi->insertPoints.errorCode; - return; - } - /* reset the embedding levels for some non-graphic characters (L1), (X9) */ - adjustWSLevels(pBiDi); - break; - } - /* add RLM for inverse Bidi with contextual orientation resolving - * to RTL which would not round-trip otherwise - */ - if((pBiDi->defaultParaLevel>0) && - (pBiDi->reorderingOptions & UBIDI_OPTION_INSERT_MARKS) && - ((pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_LIKE_DIRECT) || - (pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL))) { - int32_t i, j, start, last; - UBiDiLevel level; - DirProp dirProp; - for(i=0; iparaCount; i++) { - last=(pBiDi->paras[i].limit)-1; - level= static_cast(pBiDi->paras[i].level); - if(level==0) - continue; /* LTR paragraph */ - start= i==0 ? 0 : pBiDi->paras[i-1].limit; - for(j=last; j>=start; j--) { - dirProp=dirProps[j]; - if(dirProp==L) { - if(jreorderingOptions & UBIDI_OPTION_REMOVE_CONTROLS) { - pBiDi->resultLength -= pBiDi->controlCount; - } else { - pBiDi->resultLength += pBiDi->insertPoints.size; - } - setParaSuccess(pBiDi); /* mark successful setPara */ -} - -U_CAPI void U_EXPORT2 -ubidi_orderParagraphsLTR(UBiDi *pBiDi, UBool orderParagraphsLTR) { - if(pBiDi!=NULL) { - pBiDi->orderParagraphsLTR=orderParagraphsLTR; - } -} - -U_CAPI UBool U_EXPORT2 -ubidi_isOrderParagraphsLTR(UBiDi *pBiDi) { - if(pBiDi!=NULL) { - return pBiDi->orderParagraphsLTR; - } else { - return false; - } -} - -U_CAPI UBiDiDirection U_EXPORT2 -ubidi_getDirection(const UBiDi *pBiDi) { - if(IS_VALID_PARA_OR_LINE(pBiDi)) { - return pBiDi->direction; - } else { - return UBIDI_LTR; - } -} - -U_CAPI const UChar * U_EXPORT2 -ubidi_getText(const UBiDi *pBiDi) { - if(IS_VALID_PARA_OR_LINE(pBiDi)) { - return pBiDi->text; - } else { - return NULL; - } -} - -U_CAPI int32_t U_EXPORT2 -ubidi_getLength(const UBiDi *pBiDi) { - if(IS_VALID_PARA_OR_LINE(pBiDi)) { - return pBiDi->originalLength; - } else { - return 0; - } -} - -U_CAPI int32_t U_EXPORT2 -ubidi_getProcessedLength(const UBiDi *pBiDi) { - if(IS_VALID_PARA_OR_LINE(pBiDi)) { - return pBiDi->length; - } else { - return 0; - } -} - -U_CAPI int32_t U_EXPORT2 -ubidi_getResultLength(const UBiDi *pBiDi) { - if(IS_VALID_PARA_OR_LINE(pBiDi)) { - return pBiDi->resultLength; - } else { - return 0; - } -} - -/* paragraphs API functions ------------------------------------------------- */ - -U_CAPI UBiDiLevel U_EXPORT2 -ubidi_getParaLevel(const UBiDi *pBiDi) { - if(IS_VALID_PARA_OR_LINE(pBiDi)) { - return pBiDi->paraLevel; - } else { - return 0; - } -} - -U_CAPI int32_t U_EXPORT2 -ubidi_countParagraphs(UBiDi *pBiDi) { - if(!IS_VALID_PARA_OR_LINE(pBiDi)) { - return 0; - } else { - return pBiDi->paraCount; - } -} - -U_CAPI void U_EXPORT2 -ubidi_getParagraphByIndex(const UBiDi *pBiDi, int32_t paraIndex, - int32_t *pParaStart, int32_t *pParaLimit, - UBiDiLevel *pParaLevel, UErrorCode *pErrorCode) { - int32_t paraStart; - - /* check the argument values */ - RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); - RETURN_VOID_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode); - RETURN_VOID_IF_BAD_RANGE(paraIndex, 0, pBiDi->paraCount, *pErrorCode); - - pBiDi=pBiDi->pParaBiDi; /* get Para object if Line object */ - if(paraIndex) { - paraStart=pBiDi->paras[paraIndex-1].limit; - } else { - paraStart=0; - } - if(pParaStart!=NULL) { - *pParaStart=paraStart; - } - if(pParaLimit!=NULL) { - *pParaLimit=pBiDi->paras[paraIndex].limit; - } - if(pParaLevel!=NULL) { - *pParaLevel=GET_PARALEVEL(pBiDi, paraStart); - } -} - -U_CAPI int32_t U_EXPORT2 -ubidi_getParagraph(const UBiDi *pBiDi, int32_t charIndex, - int32_t *pParaStart, int32_t *pParaLimit, - UBiDiLevel *pParaLevel, UErrorCode *pErrorCode) { - int32_t paraIndex; - - /* check the argument values */ - /* pErrorCode will be checked by the call to ubidi_getParagraphByIndex */ - RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1); - RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1); - pBiDi=pBiDi->pParaBiDi; /* get Para object if Line object */ - RETURN_IF_BAD_RANGE(charIndex, 0, pBiDi->length, *pErrorCode, -1); - - for(paraIndex=0; charIndex>=pBiDi->paras[paraIndex].limit; paraIndex++); - ubidi_getParagraphByIndex(pBiDi, paraIndex, pParaStart, pParaLimit, pParaLevel, pErrorCode); - return paraIndex; -} - -U_CAPI void U_EXPORT2 -ubidi_setClassCallback(UBiDi *pBiDi, UBiDiClassCallback *newFn, - const void *newContext, UBiDiClassCallback **oldFn, - const void **oldContext, UErrorCode *pErrorCode) -{ - RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); - if(pBiDi==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if( oldFn ) - { - *oldFn = pBiDi->fnClassCallback; - } - if( oldContext ) - { - *oldContext = pBiDi->coClassCallback; - } - pBiDi->fnClassCallback = newFn; - pBiDi->coClassCallback = newContext; -} - -U_CAPI void U_EXPORT2 -ubidi_getClassCallback(UBiDi *pBiDi, UBiDiClassCallback **fn, const void **context) -{ - if(pBiDi==NULL) { - return; - } - if( fn ) - { - *fn = pBiDi->fnClassCallback; - } - if( context ) - { - *context = pBiDi->coClassCallback; - } -} - -U_CAPI UCharDirection U_EXPORT2 -ubidi_getCustomizedClass(UBiDi *pBiDi, UChar32 c) -{ - UCharDirection dir; - - if( pBiDi->fnClassCallback == NULL || - (dir = (*pBiDi->fnClassCallback)(pBiDi->coClassCallback, c)) == U_BIDI_CLASS_DEFAULT ) - { - dir = ubidi_getClass(c); - } - if(dir >= U_CHAR_DIRECTION_COUNT) { - dir = (UCharDirection)ON; - } - return dir; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ubidi.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999jul27 +* created by: Markus W. Scherer, updated by Matitiahu Allouche +* +*/ + +#include "cmemory.h" +#include "unicode/utypes.h" +#include "unicode/ustring.h" +#include "unicode/uchar.h" +#include "unicode/ubidi.h" +#include "unicode/utf16.h" +#include "ubidi_props.h" +#include "ubidiimp.h" +#include "uassert.h" + +/* + * General implementation notes: + * + * Throughout the implementation, there are comments like (W2) that refer to + * rules of the BiDi algorithm, in this example to the second rule of the + * resolution of weak types. + * + * For handling surrogate pairs, where two char16_t's form one "abstract" (or UTF-32) + * character according to UTF-16, the second char16_t gets the directional property of + * the entire character assigned, while the first one gets a BN, a boundary + * neutral, type, which is ignored by most of the algorithm according to + * rule (X9) and the implementation suggestions of the BiDi algorithm. + * + * Later, adjustWSLevels() will set the level for each BN to that of the + * following character (char16_t), which results in surrogate pairs getting the + * same level on each of their surrogates. + * + * In a UTF-8 implementation, the same thing could be done: the last byte of + * a multi-byte sequence would get the "real" property, while all previous + * bytes of that sequence would get BN. + * + * It is not possible to assign all those parts of a character the same real + * property because this would fail in the resolution of weak types with rules + * that look at immediately surrounding types. + * + * As a related topic, this implementation does not remove Boundary Neutral + * types from the input, but ignores them wherever this is relevant. + * For example, the loop for the resolution of the weak types reads + * types until it finds a non-BN. + * Also, explicit embedding codes are neither changed into BN nor removed. + * They are only treated the same way real BNs are. + * As stated before, adjustWSLevels() takes care of them at the end. + * For the purpose of conformance, the levels of all these codes + * do not matter. + * + * Note that this implementation modifies the dirProps + * after the initial setup, when applying X5c (replace FSI by LRI or RLI), + * X6, N0 (replace paired brackets by L or R). + * + * In this implementation, the resolution of weak types (W1 to W6), + * neutrals (N1 and N2), and the assignment of the resolved level (In) + * are all done in one single loop, in resolveImplicitLevels(). + * Changes of dirProp values are done on the fly, without writing + * them back to the dirProps array. + * + * + * This implementation contains code that allows to bypass steps of the + * algorithm that are not needed on the specific paragraph + * in order to speed up the most common cases considerably, + * like text that is entirely LTR, or RTL text without numbers. + * + * Most of this is done by setting a bit for each directional property + * in a flags variable and later checking for whether there are + * any LTR characters or any RTL characters, or both, whether + * there are any explicit embedding codes, etc. + * + * If the (Xn) steps are performed, then the flags are re-evaluated, + * because they will then not contain the embedding codes any more + * and will be adjusted for override codes, so that subsequently + * more bypassing may be possible than what the initial flags suggested. + * + * If the text is not mixed-directional, then the + * algorithm steps for the weak type resolution are not performed, + * and all levels are set to the paragraph level. + * + * If there are no explicit embedding codes, then the (Xn) steps + * are not performed. + * + * If embedding levels are supplied as a parameter, then all + * explicit embedding codes are ignored, and the (Xn) steps + * are not performed. + * + * White Space types could get the level of the run they belong to, + * and are checked with a test of (flags&MASK_EMBEDDING) to + * consider if the paragraph direction should be considered in + * the flags variable. + * + * If there are no White Space types in the paragraph, then + * (L1) is not necessary in adjustWSLevels(). + */ + +/* to avoid some conditional statements, use tiny constant arrays */ +static const Flags flagLR[2]={ DIRPROP_FLAG(L), DIRPROP_FLAG(R) }; +static const Flags flagE[2]={ DIRPROP_FLAG(LRE), DIRPROP_FLAG(RLE) }; +static const Flags flagO[2]={ DIRPROP_FLAG(LRO), DIRPROP_FLAG(RLO) }; + +#define DIRPROP_FLAG_LR(level) flagLR[(level)&1] +#define DIRPROP_FLAG_E(level) flagE[(level)&1] +#define DIRPROP_FLAG_O(level) flagO[(level)&1] + +#define DIR_FROM_STRONG(strong) ((strong)==L ? L : R) + +#define NO_OVERRIDE(level) ((level)&~UBIDI_LEVEL_OVERRIDE) + +/* UBiDi object management -------------------------------------------------- */ + +U_CAPI UBiDi * U_EXPORT2 +ubidi_open() +{ + UErrorCode errorCode=U_ZERO_ERROR; + return ubidi_openSized(0, 0, &errorCode); +} + +U_CAPI UBiDi * U_EXPORT2 +ubidi_openSized(int32_t maxLength, int32_t maxRunCount, UErrorCode *pErrorCode) { + UBiDi *pBiDi; + + /* check the argument values */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return nullptr; + } else if(maxLength<0 || maxRunCount<0) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; /* invalid arguments */ + } + + /* allocate memory for the object */ + pBiDi=(UBiDi *)uprv_malloc(sizeof(UBiDi)); + if(pBiDi==nullptr) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + /* reset the object, all pointers nullptr, all flags false, all sizes 0 */ + uprv_memset(pBiDi, 0, sizeof(UBiDi)); + + /* allocate memory for arrays as requested */ + if(maxLength>0) { + if( !getInitialDirPropsMemory(pBiDi, maxLength) || + !getInitialLevelsMemory(pBiDi, maxLength) + ) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + } + } else { + pBiDi->mayAllocateText=true; + } + + if(maxRunCount>0) { + if(maxRunCount==1) { + /* use simpleRuns[] */ + pBiDi->runsSize=sizeof(Run); + } else if(!getInitialRunsMemory(pBiDi, maxRunCount)) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + } + } else { + pBiDi->mayAllocateRuns=true; + } + + if(U_SUCCESS(*pErrorCode)) { + return pBiDi; + } else { + ubidi_close(pBiDi); + return nullptr; + } +} + +/* + * We are allowed to allocate memory if memory==nullptr or + * mayAllocate==true for each array that we need. + * We also try to grow memory as needed if we + * allocate it. + * + * Assume sizeNeeded>0. + * If *pMemory!=nullptr, then assume *pSize>0. + * + * ### this realloc() may unnecessarily copy the old data, + * which we know we don't need any more; + * is this the best way to do this?? + */ +U_CFUNC UBool +ubidi_getMemory(BidiMemoryForAllocation *bidiMem, int32_t *pSize, UBool mayAllocate, int32_t sizeNeeded) { + void **pMemory = (void **)bidiMem; + /* check for existing memory */ + if(*pMemory==nullptr) { + /* we need to allocate memory */ + if(mayAllocate && (*pMemory=uprv_malloc(sizeNeeded))!=nullptr) { + *pSize=sizeNeeded; + return true; + } else { + return false; + } + } else { + if(sizeNeeded<=*pSize) { + /* there is already enough memory */ + return true; + } + else if(!mayAllocate) { + /* not enough memory, and we must not allocate */ + return false; + } else { + /* we try to grow */ + void *memory; + /* in most cases, we do not need the copy-old-data part of + * realloc, but it is needed when adding runs using getRunsMemory() + * in setParaRunsOnly() + */ + if((memory=uprv_realloc(*pMemory, sizeNeeded))!=nullptr) { + *pMemory=memory; + *pSize=sizeNeeded; + return true; + } else { + /* we failed to grow */ + return false; + } + } + } +} + +U_CAPI void U_EXPORT2 +ubidi_close(UBiDi *pBiDi) { + if(pBiDi!=nullptr) { + pBiDi->pParaBiDi=nullptr; /* in case one tries to reuse this block */ + if(pBiDi->dirPropsMemory!=nullptr) { + uprv_free(pBiDi->dirPropsMemory); + } + if(pBiDi->levelsMemory!=nullptr) { + uprv_free(pBiDi->levelsMemory); + } + if(pBiDi->openingsMemory!=nullptr) { + uprv_free(pBiDi->openingsMemory); + } + if(pBiDi->parasMemory!=nullptr) { + uprv_free(pBiDi->parasMemory); + } + if(pBiDi->runsMemory!=nullptr) { + uprv_free(pBiDi->runsMemory); + } + if(pBiDi->isolatesMemory!=nullptr) { + uprv_free(pBiDi->isolatesMemory); + } + if(pBiDi->insertPoints.points!=nullptr) { + uprv_free(pBiDi->insertPoints.points); + } + + uprv_free(pBiDi); + } +} + +/* set to approximate "inverse BiDi" ---------------------------------------- */ + +U_CAPI void U_EXPORT2 +ubidi_setInverse(UBiDi *pBiDi, UBool isInverse) { + if(pBiDi!=nullptr) { + pBiDi->isInverse=isInverse; + pBiDi->reorderingMode = isInverse ? UBIDI_REORDER_INVERSE_NUMBERS_AS_L + : UBIDI_REORDER_DEFAULT; + } +} + +U_CAPI UBool U_EXPORT2 +ubidi_isInverse(UBiDi *pBiDi) { + if(pBiDi!=nullptr) { + return pBiDi->isInverse; + } else { + return false; + } +} + +/* FOOD FOR THOUGHT: currently the reordering modes are a mixture of + * algorithm for direct BiDi, algorithm for inverse BiDi and the bizarre + * concept of RUNS_ONLY which is a double operation. + * It could be advantageous to divide this into 3 concepts: + * a) Operation: direct / inverse / RUNS_ONLY + * b) Direct algorithm: default / NUMBERS_SPECIAL / GROUP_NUMBERS_WITH_R + * c) Inverse algorithm: default / INVERSE_LIKE_DIRECT / NUMBERS_SPECIAL + * This would allow combinations not possible today like RUNS_ONLY with + * NUMBERS_SPECIAL. + * Also allow to set INSERT_MARKS for the direct step of RUNS_ONLY and + * REMOVE_CONTROLS for the inverse step. + * Not all combinations would be supported, and probably not all do make sense. + * This would need to document which ones are supported and what are the + * fallbacks for unsupported combinations. + */ +U_CAPI void U_EXPORT2 +ubidi_setReorderingMode(UBiDi *pBiDi, UBiDiReorderingMode reorderingMode) UPRV_NO_SANITIZE_UNDEFINED { + if ((pBiDi!=nullptr) && (reorderingMode >= UBIDI_REORDER_DEFAULT) + && (reorderingMode < UBIDI_REORDER_COUNT)) { + pBiDi->reorderingMode = reorderingMode; + pBiDi->isInverse = (UBool)(reorderingMode == UBIDI_REORDER_INVERSE_NUMBERS_AS_L); + } +} + +U_CAPI UBiDiReorderingMode U_EXPORT2 +ubidi_getReorderingMode(UBiDi *pBiDi) { + if (pBiDi!=nullptr) { + return pBiDi->reorderingMode; + } else { + return UBIDI_REORDER_DEFAULT; + } +} + +U_CAPI void U_EXPORT2 +ubidi_setReorderingOptions(UBiDi *pBiDi, uint32_t reorderingOptions) { + if (reorderingOptions & UBIDI_OPTION_REMOVE_CONTROLS) { + reorderingOptions&=~UBIDI_OPTION_INSERT_MARKS; + } + if (pBiDi!=nullptr) { + pBiDi->reorderingOptions=reorderingOptions; + } +} + +U_CAPI uint32_t U_EXPORT2 +ubidi_getReorderingOptions(UBiDi *pBiDi) { + if (pBiDi!=nullptr) { + return pBiDi->reorderingOptions; + } else { + return 0; + } +} + +U_CAPI UBiDiDirection U_EXPORT2 +ubidi_getBaseDirection(const char16_t *text, +int32_t length){ + + int32_t i; + UChar32 uchar; + UCharDirection dir; + + if( text==nullptr || length<-1 ){ + return UBIDI_NEUTRAL; + } + + if(length==-1) { + length=u_strlen(text); + } + + for( i = 0 ; i < length; ) { + /* i is incremented by U16_NEXT */ + U16_NEXT(text, i, length, uchar); + dir = u_charDirection(uchar); + if( dir == U_LEFT_TO_RIGHT ) + return UBIDI_LTR; + if( dir == U_RIGHT_TO_LEFT || dir ==U_RIGHT_TO_LEFT_ARABIC ) + return UBIDI_RTL; + } + return UBIDI_NEUTRAL; +} + +/* perform (P2)..(P3) ------------------------------------------------------- */ + +/** + * Returns the directionality of the first strong character + * after the last B in prologue, if any. + * Requires prologue!=null. + */ +static DirProp +firstL_R_AL(UBiDi *pBiDi) { + const char16_t *text=pBiDi->prologue; + int32_t length=pBiDi->proLength; + int32_t i; + UChar32 uchar; + DirProp dirProp, result=ON; + for(i=0; iparas + */ +static UBool +checkParaCount(UBiDi *pBiDi) { + int32_t count=pBiDi->paraCount; + if(pBiDi->paras==pBiDi->simpleParas) { + if(count<=SIMPLE_PARAS_COUNT) + return true; + if(!getInitialParasMemory(pBiDi, SIMPLE_PARAS_COUNT * 2)) + return false; + pBiDi->paras=pBiDi->parasMemory; + uprv_memcpy(pBiDi->parasMemory, pBiDi->simpleParas, SIMPLE_PARAS_COUNT * sizeof(Para)); + return true; + } + if(!getInitialParasMemory(pBiDi, count * 2)) + return false; + pBiDi->paras=pBiDi->parasMemory; + return true; +} + +/* + * Get the directional properties for the text, calculate the flags bit-set, and + * determine the paragraph level if necessary (in pBiDi->paras[i].level). + * FSI initiators are also resolved and their dirProp replaced with LRI or RLI. + * When encountering an FSI, it is initially replaced with an LRI, which is the + * default. Only if a strong R or AL is found within its scope will the LRI be + * replaced by an RLI. + */ +static UBool +getDirProps(UBiDi *pBiDi) { + const char16_t *text=pBiDi->text; + DirProp *dirProps=pBiDi->dirPropsMemory; /* pBiDi->dirProps is const */ + + int32_t i=0, originalLength=pBiDi->originalLength; + Flags flags=0; /* collect all directionalities in the text */ + UChar32 uchar; + DirProp dirProp=0, defaultParaLevel=0; /* initialize to avoid compiler warnings */ + UBool isDefaultLevel=IS_DEFAULT_LEVEL(pBiDi->paraLevel); + /* for inverse BiDi, the default para level is set to RTL if there is a + strong R or AL character at either end of the text */ + UBool isDefaultLevelInverse=isDefaultLevel && (UBool) + (pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_LIKE_DIRECT || + pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL); + int32_t lastArabicPos=-1; + int32_t controlCount=0; + UBool removeBiDiControls = (UBool)(pBiDi->reorderingOptions & + UBIDI_OPTION_REMOVE_CONTROLS); + + enum State { + NOT_SEEKING_STRONG, /* 0: not contextual paraLevel, not after FSI */ + SEEKING_STRONG_FOR_PARA, /* 1: looking for first strong char in para */ + SEEKING_STRONG_FOR_FSI, /* 2: looking for first strong after FSI */ + LOOKING_FOR_PDI /* 3: found strong after FSI, looking for PDI */ + }; + State state; + DirProp lastStrong=ON; /* for default level & inverse BiDi */ + /* The following stacks are used to manage isolate sequences. Those + sequences may be nested, but obviously never more deeply than the + maximum explicit embedding level. + lastStack is the index of the last used entry in the stack. A value of -1 + means that there is no open isolate sequence. + lastStack is reset to -1 on paragraph boundaries. */ + /* The following stack contains the position of the initiator of + each open isolate sequence */ + int32_t isolateStartStack[UBIDI_MAX_EXPLICIT_LEVEL+1]; + /* The following stack contains the last known state before + encountering the initiator of an isolate sequence */ + State previousStateStack[UBIDI_MAX_EXPLICIT_LEVEL+1]; + int32_t stackLast=-1; + + if(pBiDi->reorderingOptions & UBIDI_OPTION_STREAMING) + pBiDi->length=0; + defaultParaLevel=pBiDi->paraLevel&1; + if(isDefaultLevel) { + pBiDi->paras[0].level=defaultParaLevel; + lastStrong=defaultParaLevel; + if(pBiDi->proLength>0 && /* there is a prologue */ + (dirProp=firstL_R_AL(pBiDi))!=ON) { /* with a strong character */ + if(dirProp==L) + pBiDi->paras[0].level=0; /* set the default para level */ + else + pBiDi->paras[0].level=1; /* set the default para level */ + state=NOT_SEEKING_STRONG; + } else { + state=SEEKING_STRONG_FOR_PARA; + } + } else { + pBiDi->paras[0].level=pBiDi->paraLevel; + state=NOT_SEEKING_STRONG; + } + /* count paragraphs and determine the paragraph level (P2..P3) */ + /* + * see comment in ubidi.h: + * the UBIDI_DEFAULT_XXX values are designed so that + * their bit 0 alone yields the intended default + */ + for( /* i=0 above */ ; i0xffff) { /* set the lead surrogate's property to BN */ + flags|=DIRPROP_FLAG(BN); + dirProps[i-2]=BN; + } + if(removeBiDiControls && IS_BIDI_CONTROL_CHAR(uchar)) + controlCount++; + if(dirProp==L) { + if(state==SEEKING_STRONG_FOR_PARA) { + pBiDi->paras[pBiDi->paraCount-1].level=0; + state=NOT_SEEKING_STRONG; + } + else if(state==SEEKING_STRONG_FOR_FSI) { + if(stackLast<=UBIDI_MAX_EXPLICIT_LEVEL) { + /* no need for next statement, already set by default */ + /* dirProps[isolateStartStack[stackLast]]=LRI; */ + flags|=DIRPROP_FLAG(LRI); + } + state=LOOKING_FOR_PDI; + } + lastStrong=L; + continue; + } + if(dirProp==R || dirProp==AL) { + if(state==SEEKING_STRONG_FOR_PARA) { + pBiDi->paras[pBiDi->paraCount-1].level=1; + state=NOT_SEEKING_STRONG; + } + else if(state==SEEKING_STRONG_FOR_FSI) { + if(stackLast<=UBIDI_MAX_EXPLICIT_LEVEL) { + dirProps[isolateStartStack[stackLast]]=RLI; + flags|=DIRPROP_FLAG(RLI); + } + state=LOOKING_FOR_PDI; + } + lastStrong=R; + if(dirProp==AL) + lastArabicPos=i-1; + continue; + } + if(dirProp>=FSI && dirProp<=RLI) { /* FSI, LRI or RLI */ + stackLast++; + if(stackLast<=UBIDI_MAX_EXPLICIT_LEVEL) { + isolateStartStack[stackLast]=i-1; + previousStateStack[stackLast]=state; + } + if(dirProp==FSI) { + dirProps[i-1]=LRI; /* default if no strong char */ + state=SEEKING_STRONG_FOR_FSI; + } + else + state=LOOKING_FOR_PDI; + continue; + } + if(dirProp==PDI) { + if(state==SEEKING_STRONG_FOR_FSI) { + if(stackLast<=UBIDI_MAX_EXPLICIT_LEVEL) { + /* no need for next statement, already set by default */ + /* dirProps[isolateStartStack[stackLast]]=LRI; */ + flags|=DIRPROP_FLAG(LRI); + } + } + if(stackLast>=0) { + if(stackLast<=UBIDI_MAX_EXPLICIT_LEVEL) + state=previousStateStack[stackLast]; + stackLast--; + } + continue; + } + if(dirProp==B) { + if(iparas[pBiDi->paraCount-1].limit=i; + if(isDefaultLevelInverse && lastStrong==R) + pBiDi->paras[pBiDi->paraCount-1].level=1; + if(pBiDi->reorderingOptions & UBIDI_OPTION_STREAMING) { + /* When streaming, we only process whole paragraphs + thus some updates are only done on paragraph boundaries */ + pBiDi->length=i; /* i is index to next character */ + pBiDi->controlCount=controlCount; + } + if(iparaCount++; + if(checkParaCount(pBiDi)==false) /* not enough memory for a new para entry */ + return false; + if(isDefaultLevel) { + pBiDi->paras[pBiDi->paraCount-1].level=defaultParaLevel; + state=SEEKING_STRONG_FOR_PARA; + lastStrong=defaultParaLevel; + } else { + pBiDi->paras[pBiDi->paraCount-1].level=pBiDi->paraLevel; + state=NOT_SEEKING_STRONG; + } + stackLast=-1; + } + continue; + } + } + /* Ignore still open isolate sequences with overflow */ + if(stackLast>UBIDI_MAX_EXPLICIT_LEVEL) { + stackLast=UBIDI_MAX_EXPLICIT_LEVEL; + state=SEEKING_STRONG_FOR_FSI; /* to be on the safe side */ + } + /* Resolve direction of still unresolved open FSI sequences */ + while(stackLast>=0) { + if(state==SEEKING_STRONG_FOR_FSI) { + /* no need for next statement, already set by default */ + /* dirProps[isolateStartStack[stackLast]]=LRI; */ + flags|=DIRPROP_FLAG(LRI); + break; + } + state=previousStateStack[stackLast]; + stackLast--; + } + /* When streaming, ignore text after the last paragraph separator */ + if(pBiDi->reorderingOptions & UBIDI_OPTION_STREAMING) { + if(pBiDi->lengthparaCount--; + } else { + pBiDi->paras[pBiDi->paraCount-1].limit=originalLength; + pBiDi->controlCount=controlCount; + } + /* For inverse bidi, default para direction is RTL if there is + a strong R or AL at either end of the paragraph */ + if(isDefaultLevelInverse && lastStrong==R) { + pBiDi->paras[pBiDi->paraCount-1].level=1; + } + if(isDefaultLevel) { + pBiDi->paraLevel=static_cast(pBiDi->paras[0].level); + } + /* The following is needed to resolve the text direction for default level + paragraphs containing no strong character */ + for(i=0; iparaCount; i++) + flags|=DIRPROP_FLAG_LR(pBiDi->paras[i].level); + + if(pBiDi->orderParagraphsLTR && (flags&DIRPROP_FLAG(B))) { + flags|=DIRPROP_FLAG(L); + } + pBiDi->flags=flags; + pBiDi->lastArabicPos=lastArabicPos; + return true; +} + +/* determine the paragraph level at position index */ +U_CFUNC UBiDiLevel +ubidi_getParaLevelAtIndex(const UBiDi *pBiDi, int32_t pindex) { + int32_t i; + for(i=0; iparaCount; i++) + if(pindexparas[i].limit) + break; + if(i>=pBiDi->paraCount) + i=pBiDi->paraCount-1; + return (UBiDiLevel)(pBiDi->paras[i].level); +} + +/* Functions for handling paired brackets ----------------------------------- */ + +/* In the isoRuns array, the first entry is used for text outside of any + isolate sequence. Higher entries are used for each more deeply nested + isolate sequence. isoRunLast is the index of the last used entry. The + openings array is used to note the data of opening brackets not yet + matched by a closing bracket, or matched but still susceptible to change + level. + Each isoRun entry contains the index of the first and + one-after-last openings entries for pending opening brackets it + contains. The next openings entry to use is the one-after-last of the + most deeply nested isoRun entry. + isoRun entries also contain their current embedding level and the last + encountered strong character, since these will be needed to resolve + the level of paired brackets. */ + +static void +bracketInit(UBiDi *pBiDi, BracketData *bd) { + bd->pBiDi=pBiDi; + bd->isoRunLast=0; + bd->isoRuns[0].start=0; + bd->isoRuns[0].limit=0; + bd->isoRuns[0].level=GET_PARALEVEL(pBiDi, 0); + UBiDiLevel t = GET_PARALEVEL(pBiDi, 0) & 1; + bd->isoRuns[0].lastStrong = bd->isoRuns[0].lastBase = t; + bd->isoRuns[0].contextDir = (UBiDiDirection)t; + bd->isoRuns[0].contextPos=0; + if(pBiDi->openingsMemory) { + bd->openings=pBiDi->openingsMemory; + bd->openingsCount=pBiDi->openingsSize / sizeof(Opening); + } else { + bd->openings=bd->simpleOpenings; + bd->openingsCount=SIMPLE_OPENINGS_COUNT; + } + bd->isNumbersSpecial=bd->pBiDi->reorderingMode==UBIDI_REORDER_NUMBERS_SPECIAL || + bd->pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL; +} + +/* paragraph boundary */ +static void +bracketProcessB(BracketData *bd, UBiDiLevel level) { + bd->isoRunLast=0; + bd->isoRuns[0].limit=0; + bd->isoRuns[0].level=level; + bd->isoRuns[0].lastStrong=bd->isoRuns[0].lastBase=level&1; + bd->isoRuns[0].contextDir=(UBiDiDirection)(level&1); + bd->isoRuns[0].contextPos=0; +} + +/* LRE, LRO, RLE, RLO, PDF */ +static void +bracketProcessBoundary(BracketData *bd, int32_t lastCcPos, + UBiDiLevel contextLevel, UBiDiLevel embeddingLevel) { + IsoRun *pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; + DirProp *dirProps=bd->pBiDi->dirProps; + if(DIRPROP_FLAG(dirProps[lastCcPos])&MASK_ISO) /* after an isolate */ + return; + if(NO_OVERRIDE(embeddingLevel)>NO_OVERRIDE(contextLevel)) /* not a PDF */ + contextLevel=embeddingLevel; + pLastIsoRun->limit=pLastIsoRun->start; + pLastIsoRun->level=embeddingLevel; + pLastIsoRun->lastStrong=pLastIsoRun->lastBase=contextLevel&1; + pLastIsoRun->contextDir=(UBiDiDirection)(contextLevel&1); + pLastIsoRun->contextPos=(UBiDiDirection)lastCcPos; +} + +/* LRI or RLI */ +static void +bracketProcessLRI_RLI(BracketData *bd, UBiDiLevel level) { + IsoRun *pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; + int16_t lastLimit; + pLastIsoRun->lastBase=ON; + lastLimit=pLastIsoRun->limit; + bd->isoRunLast++; + pLastIsoRun++; + pLastIsoRun->start=pLastIsoRun->limit=lastLimit; + pLastIsoRun->level=level; + pLastIsoRun->lastStrong=pLastIsoRun->lastBase=level&1; + pLastIsoRun->contextDir=(UBiDiDirection)(level&1); + pLastIsoRun->contextPos=0; +} + +/* PDI */ +static void +bracketProcessPDI(BracketData *bd) { + IsoRun *pLastIsoRun; + bd->isoRunLast--; + pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; + pLastIsoRun->lastBase=ON; +} + +/* newly found opening bracket: create an openings entry */ +static UBool /* return true if success */ +bracketAddOpening(BracketData *bd, char16_t match, int32_t position) { + IsoRun *pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; + Opening *pOpening; + if(pLastIsoRun->limit>=bd->openingsCount) { /* no available new entry */ + UBiDi *pBiDi=bd->pBiDi; + if(!getInitialOpeningsMemory(pBiDi, pLastIsoRun->limit * 2)) + return false; + if(bd->openings==bd->simpleOpenings) + uprv_memcpy(pBiDi->openingsMemory, bd->simpleOpenings, + SIMPLE_OPENINGS_COUNT * sizeof(Opening)); + bd->openings=pBiDi->openingsMemory; /* may have changed */ + bd->openingsCount=pBiDi->openingsSize / sizeof(Opening); + } + pOpening=&bd->openings[pLastIsoRun->limit]; + pOpening->position=position; + pOpening->match=match; + pOpening->contextDir=pLastIsoRun->contextDir; + pOpening->contextPos=pLastIsoRun->contextPos; + pOpening->flags=0; + pLastIsoRun->limit++; + return true; +} + +/* change N0c1 to N0c2 when a preceding bracket is assigned the embedding level */ +static void +fixN0c(BracketData *bd, int32_t openingIndex, int32_t newPropPosition, DirProp newProp) { + /* This function calls itself recursively */ + IsoRun *pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; + Opening *qOpening; + DirProp *dirProps=bd->pBiDi->dirProps; + int32_t k, openingPosition, closingPosition; + for(k=openingIndex+1, qOpening=&bd->openings[k]; klimit; k++, qOpening++) { + if(qOpening->match>=0) /* not an N0c match */ + continue; + if(newPropPositioncontextPos) + break; + if(newPropPosition>=qOpening->position) + continue; + if(newProp==qOpening->contextDir) + break; + openingPosition=qOpening->position; + dirProps[openingPosition]=newProp; + closingPosition=-(qOpening->match); + dirProps[closingPosition]=newProp; + qOpening->match=0; /* prevent further changes */ + fixN0c(bd, k, openingPosition, newProp); + fixN0c(bd, k, closingPosition, newProp); + } +} + +/* process closing bracket */ +static DirProp /* return L or R if N0b or N0c, ON if N0d */ +bracketProcessClosing(BracketData *bd, int32_t openIdx, int32_t position) { + IsoRun *pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; + Opening *pOpening, *qOpening; + UBiDiDirection direction; + UBool stable; + DirProp newProp; + pOpening=&bd->openings[openIdx]; + direction=(UBiDiDirection)(pLastIsoRun->level&1); + stable=true; /* assume stable until proved otherwise */ + + /* The stable flag is set when brackets are paired and their + level is resolved and cannot be changed by what will be + found later in the source string. + An unstable match can occur only when applying N0c, where + the resolved level depends on the preceding context, and + this context may be affected by text occurring later. + Example: RTL paragraph containing: abc[(latin) HEBREW] + When the closing parenthesis is encountered, it appears + that N0c1 must be applied since 'abc' sets an opposite + direction context and both parentheses receive level 2. + However, when the closing square bracket is processed, + N0b applies because of 'HEBREW' being included within the + brackets, thus the square brackets are treated like R and + receive level 1. However, this changes the preceding + context of the opening parenthesis, and it now appears + that N0c2 must be applied to the parentheses rather than + N0c1. */ + + if((direction==0 && pOpening->flags&FOUND_L) || + (direction==1 && pOpening->flags&FOUND_R)) { /* N0b */ + newProp=static_cast(direction); + } + else if(pOpening->flags&(FOUND_L|FOUND_R)) { /* N0c */ + /* it is stable if there is no containing pair or in + conditions too complicated and not worth checking */ + stable=(openIdx==pLastIsoRun->start); + if(direction!=pOpening->contextDir) + newProp= static_cast(pOpening->contextDir); /* N0c1 */ + else + newProp= static_cast(direction); /* N0c2 */ + } else { + /* forget this and any brackets nested within this pair */ + pLastIsoRun->limit= static_cast(openIdx); + return ON; /* N0d */ + } + bd->pBiDi->dirProps[pOpening->position]=newProp; + bd->pBiDi->dirProps[position]=newProp; + /* Update nested N0c pairs that may be affected */ + fixN0c(bd, openIdx, pOpening->position, newProp); + if(stable) { + pLastIsoRun->limit= static_cast(openIdx); /* forget any brackets nested within this pair */ + /* remove lower located synonyms if any */ + while(pLastIsoRun->limit>pLastIsoRun->start && + bd->openings[pLastIsoRun->limit-1].position==pOpening->position) + pLastIsoRun->limit--; + } else { + int32_t k; + pOpening->match=-position; + /* neutralize lower located synonyms if any */ + k=openIdx-1; + while(k>=pLastIsoRun->start && + bd->openings[k].position==pOpening->position) + bd->openings[k--].match=0; + /* neutralize any unmatched opening between the current pair; + this will also neutralize higher located synonyms if any */ + for(k=openIdx+1; klimit; k++) { + qOpening=&bd->openings[k]; + if(qOpening->position>=position) + break; + if(qOpening->match>0) + qOpening->match=0; + } + } + return newProp; +} + +/* handle strong characters, digits and candidates for closing brackets */ +static UBool /* return true if success */ +bracketProcessChar(BracketData *bd, int32_t position) { + IsoRun *pLastIsoRun=&bd->isoRuns[bd->isoRunLast]; + DirProp *dirProps, dirProp, newProp; + UBiDiLevel level; + dirProps=bd->pBiDi->dirProps; + dirProp=dirProps[position]; + if(dirProp==ON) { + char16_t c, match; + int32_t idx; + /* First see if it is a matching closing bracket. Hopefully, this is + more efficient than checking if it is a closing bracket at all */ + c=bd->pBiDi->text[position]; + for(idx=pLastIsoRun->limit-1; idx>=pLastIsoRun->start; idx--) { + if(bd->openings[idx].match!=c) + continue; + /* We have a match */ + newProp=bracketProcessClosing(bd, idx, position); + if(newProp==ON) { /* N0d */ + c=0; /* prevent handling as an opening */ + break; + } + pLastIsoRun->lastBase=ON; + pLastIsoRun->contextDir=(UBiDiDirection)newProp; + pLastIsoRun->contextPos=position; + level=bd->pBiDi->levels[position]; + if(level&UBIDI_LEVEL_OVERRIDE) { /* X4, X5 */ + uint16_t flag; + int32_t i; + newProp=level&1; + pLastIsoRun->lastStrong=newProp; + flag=DIRPROP_FLAG(newProp); + for(i=pLastIsoRun->start; iopenings[i].flags|=flag; + /* matching brackets are not overridden by LRO/RLO */ + bd->pBiDi->levels[position]&=~UBIDI_LEVEL_OVERRIDE; + } + /* matching brackets are not overridden by LRO/RLO */ + bd->pBiDi->levels[bd->openings[idx].position]&=~UBIDI_LEVEL_OVERRIDE; + return true; + } + /* We get here only if the ON character is not a matching closing + bracket or it is a case of N0d */ + /* Now see if it is an opening bracket */ + if(c) + match= static_cast(u_getBidiPairedBracket(c)); /* get the matching char */ + else + match=0; + if(match!=c && /* has a matching char */ + ubidi_getPairedBracketType(c)==U_BPT_OPEN) { /* opening bracket */ + /* special case: process synonyms + create an opening entry for each synonym */ + if(match==0x232A) { /* RIGHT-POINTING ANGLE BRACKET */ + if(!bracketAddOpening(bd, 0x3009, position)) + return false; + } + else if(match==0x3009) { /* RIGHT ANGLE BRACKET */ + if(!bracketAddOpening(bd, 0x232A, position)) + return false; + } + if(!bracketAddOpening(bd, match, position)) + return false; + } + } + level=bd->pBiDi->levels[position]; + if(level&UBIDI_LEVEL_OVERRIDE) { /* X4, X5 */ + newProp=level&1; + if(dirProp!=S && dirProp!=WS && dirProp!=ON) + dirProps[position]=newProp; + pLastIsoRun->lastBase=newProp; + pLastIsoRun->lastStrong=newProp; + pLastIsoRun->contextDir=(UBiDiDirection)newProp; + pLastIsoRun->contextPos=position; + } + else if(dirProp<=R || dirProp==AL) { + newProp= static_cast(DIR_FROM_STRONG(dirProp)); + pLastIsoRun->lastBase=dirProp; + pLastIsoRun->lastStrong=dirProp; + pLastIsoRun->contextDir=(UBiDiDirection)newProp; + pLastIsoRun->contextPos=position; + } + else if(dirProp==EN) { + pLastIsoRun->lastBase=EN; + if(pLastIsoRun->lastStrong==L) { + newProp=L; /* W7 */ + if(!bd->isNumbersSpecial) + dirProps[position]=ENL; + pLastIsoRun->contextDir=(UBiDiDirection)L; + pLastIsoRun->contextPos=position; + } + else { + newProp=R; /* N0 */ + if(pLastIsoRun->lastStrong==AL) + dirProps[position]=AN; /* W2 */ + else + dirProps[position]=ENR; + pLastIsoRun->contextDir=(UBiDiDirection)R; + pLastIsoRun->contextPos=position; + } + } + else if(dirProp==AN) { + newProp=R; /* N0 */ + pLastIsoRun->lastBase=AN; + pLastIsoRun->contextDir=(UBiDiDirection)R; + pLastIsoRun->contextPos=position; + } + else if(dirProp==NSM) { + /* if the last real char was ON, change NSM to ON so that it + will stay ON even if the last real char is a bracket which + may be changed to L or R */ + newProp=pLastIsoRun->lastBase; + if(newProp==ON) + dirProps[position]=newProp; + } + else { + newProp=dirProp; + pLastIsoRun->lastBase=dirProp; + } + if(newProp<=R || newProp==AL) { + int32_t i; + uint16_t flag=DIRPROP_FLAG(DIR_FROM_STRONG(newProp)); + for(i=pLastIsoRun->start; ilimit; i++) + if(position>bd->openings[i].position) + bd->openings[i].flags|=flag; + } + return true; +} + +/* perform (X1)..(X9) ------------------------------------------------------- */ + +/* determine if the text is mixed-directional or single-directional */ +static UBiDiDirection +directionFromFlags(UBiDi *pBiDi) { + Flags flags=pBiDi->flags; + /* if the text contains AN and neutrals, then some neutrals may become RTL */ + if(!(flags&MASK_RTL || ((flags&DIRPROP_FLAG(AN)) && (flags&MASK_POSSIBLE_N)))) { + return UBIDI_LTR; + } else if(!(flags&MASK_LTR)) { + return UBIDI_RTL; + } else { + return UBIDI_MIXED; + } +} + +/* + * Resolve the explicit levels as specified by explicit embedding codes. + * Recalculate the flags to have them reflect the real properties + * after taking the explicit embeddings into account. + * + * The BiDi algorithm is designed to result in the same behavior whether embedding + * levels are externally specified (from "styled text", supposedly the preferred + * method) or set by explicit embedding codes (LRx, RLx, PDF, FSI, PDI) in the plain text. + * That is why (X9) instructs to remove all not-isolate explicit codes (and BN). + * However, in a real implementation, the removal of these codes and their index + * positions in the plain text is undesirable since it would result in + * reallocated, reindexed text. + * Instead, this implementation leaves the codes in there and just ignores them + * in the subsequent processing. + * In order to get the same reordering behavior, positions with a BN or a not-isolate + * explicit embedding code just get the same level assigned as the last "real" + * character. + * + * Some implementations, not this one, then overwrite some of these + * directionality properties at "real" same-level-run boundaries by + * L or R codes so that the resolution of weak types can be performed on the + * entire paragraph at once instead of having to parse it once more and + * perform that resolution on same-level-runs. + * This limits the scope of the implicit rules in effectively + * the same way as the run limits. + * + * Instead, this implementation does not modify these codes, except for + * paired brackets whose properties (ON) may be replaced by L or R. + * On one hand, the paragraph has to be scanned for same-level-runs, but + * on the other hand, this saves another loop to reset these codes, + * or saves making and modifying a copy of dirProps[]. + * + * + * Note that (Pn) and (Xn) changed significantly from version 4 of the BiDi algorithm. + * + * + * Handling the stack of explicit levels (Xn): + * + * With the BiDi stack of explicit levels, as pushed with each + * LRE, RLE, LRO, RLO, LRI, RLI and FSI and popped with each PDF and PDI, + * the explicit level must never exceed UBIDI_MAX_EXPLICIT_LEVEL. + * + * In order to have a correct push-pop semantics even in the case of overflows, + * overflow counters and a valid isolate counter are used as described in UAX#9 + * section 3.3.2 "Explicit Levels and Directions". + * + * This implementation assumes that UBIDI_MAX_EXPLICIT_LEVEL is odd. + * + * Returns normally the direction; -1 if there was a memory shortage + * + */ +static UBiDiDirection +resolveExplicitLevels(UBiDi *pBiDi, UErrorCode *pErrorCode) { + DirProp *dirProps=pBiDi->dirProps; + UBiDiLevel *levels=pBiDi->levels; + const char16_t *text=pBiDi->text; + + int32_t i=0, length=pBiDi->length; + Flags flags=pBiDi->flags; /* collect all directionalities in the text */ + DirProp dirProp; + UBiDiLevel level=GET_PARALEVEL(pBiDi, 0); + UBiDiDirection direction; + pBiDi->isolateCount=0; + + if(U_FAILURE(*pErrorCode)) { return UBIDI_LTR; } + + /* determine if the text is mixed-directional or single-directional */ + direction=directionFromFlags(pBiDi); + + /* we may not need to resolve any explicit levels */ + if((direction!=UBIDI_MIXED)) { + /* not mixed directionality: levels don't matter - trailingWSStart will be 0 */ + return direction; + } + if(pBiDi->reorderingMode > UBIDI_REORDER_LAST_LOGICAL_TO_VISUAL) { + /* inverse BiDi: mixed, but all characters are at the same embedding level */ + /* set all levels to the paragraph level */ + int32_t paraIndex, start, limit; + for(paraIndex=0; paraIndexparaCount; paraIndex++) { + if(paraIndex==0) + start=0; + else + start=pBiDi->paras[paraIndex-1].limit; + limit=pBiDi->paras[paraIndex].limit; + level= static_cast(pBiDi->paras[paraIndex].level); + for(i=start; iparaCount; paraIndex++) { + if(paraIndex==0) + start=0; + else + start=pBiDi->paras[paraIndex-1].limit; + limit=pBiDi->paras[paraIndex].limit; + level= static_cast(pBiDi->paras[paraIndex].level); + for(i=start; i=UBIDI_MAX_EXPLICIT_LEVEL + but we need one more entry as base */ + uint32_t stackLast=0; + int32_t overflowIsolateCount=0; + int32_t overflowEmbeddingCount=0; + int32_t validIsolateCount=0; + BracketData bracketData; + bracketInit(pBiDi, &bracketData); + stack[0]=level; /* initialize base entry to para level, no override, no isolate */ + + /* recalculate the flags */ + flags=0; + + for(i=0; i0 && stack[stackLast]pBiDi->isolateCount) + pBiDi->isolateCount=validIsolateCount; + embeddingLevel=newLevel; + /* we can increment stackLast without checking because newLevel + will exceed UBIDI_MAX_EXPLICIT_LEVEL before stackLast overflows */ + stackLast++; + stack[stackLast]=embeddingLevel+ISOLATE; + bracketProcessLRI_RLI(&bracketData, embeddingLevel); + } else { + /* make it WS so that it is handled by adjustWSLevels() */ + dirProps[i]=WS; + overflowIsolateCount++; + } + break; + case PDI: + if(NO_OVERRIDE(embeddingLevel)!=NO_OVERRIDE(previousLevel)) { + bracketProcessBoundary(&bracketData, lastCcPos, + previousLevel, embeddingLevel); + flags|=DIRPROP_FLAG_MULTI_RUNS; + } + /* (X6a) */ + if(overflowIsolateCount) { + overflowIsolateCount--; + /* make it WS so that it is handled by adjustWSLevels() */ + dirProps[i]=WS; + } + else if(validIsolateCount) { + flags|=DIRPROP_FLAG(PDI); + lastCcPos=i; + overflowEmbeddingCount=0; + while(stack[stackLast]paraLevel); + if(pBiDi->orderParagraphsLTR && (flags&DIRPROP_FLAG(B))) + flags|=DIRPROP_FLAG(L); + /* again, determine if the text is mixed-directional or single-directional */ + pBiDi->flags=flags; + direction=directionFromFlags(pBiDi); + } + return direction; +} + +/* + * Use a pre-specified embedding levels array: + * + * Adjust the directional properties for overrides (->LEVEL_OVERRIDE), + * ignore all explicit codes (X9), + * and check all the preset levels. + * + * Recalculate the flags to have them reflect the real properties + * after taking the explicit embeddings into account. + */ +static UBiDiDirection +checkExplicitLevels(UBiDi *pBiDi, UErrorCode *pErrorCode) { + DirProp *dirProps=pBiDi->dirProps; + UBiDiLevel *levels=pBiDi->levels; + int32_t isolateCount=0; + + int32_t length=pBiDi->length; + Flags flags=0; /* collect all directionalities in the text */ + pBiDi->isolateCount=0; + + int32_t currentParaIndex = 0; + int32_t currentParaLimit = pBiDi->paras[0].limit; + int32_t currentParaLevel = pBiDi->paraLevel; + + for(int32_t i=0; ipBiDi->isolateCount) + pBiDi->isolateCount=isolateCount; + } + else if(dirProp==PDI) + isolateCount--; + else if(dirProp==B) + isolateCount=0; + + // optimized version of int32_t currentParaLevel = GET_PARALEVEL(pBiDi, i); + if (pBiDi->defaultParaLevel != 0 && + i == currentParaLimit && (currentParaIndex + 1) < pBiDi->paraCount) { + currentParaLevel = pBiDi->paras[++currentParaIndex].level; + currentParaLimit = pBiDi->paras[currentParaIndex].limit; + } + + UBiDiLevel overrideFlag = level & UBIDI_LEVEL_OVERRIDE; + level &= ~UBIDI_LEVEL_OVERRIDE; + if (level < currentParaLevel || UBIDI_MAX_EXPLICIT_LEVEL < level) { + if (level == 0) { + if (dirProp == B) { + // Paragraph separators are ok with explicit level 0. + // Prevents reordering of paragraphs. + } else { + // Treat explicit level 0 as a wildcard for the paragraph level. + // Avoid making the caller guess what the paragraph level would be. + level = (UBiDiLevel)currentParaLevel; + levels[i] = level | overrideFlag; + } + } else { + // 1 <= level < currentParaLevel or UBIDI_MAX_EXPLICIT_LEVEL < level + /* level out of bounds */ + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return UBIDI_LTR; + } + } + if (overrideFlag != 0) { + /* keep the override flag in levels[i] but adjust the flags */ + flags|=DIRPROP_FLAG_O(level); + } else { + /* set the flags */ + flags|=DIRPROP_FLAG_E(level)|DIRPROP_FLAG(dirProp); + } + } + if(flags&MASK_EMBEDDING) + flags|=DIRPROP_FLAG_LR(pBiDi->paraLevel); + /* determine if the text is mixed-directional or single-directional */ + pBiDi->flags=flags; + return directionFromFlags(pBiDi); +} + +/****************************************************************** + The Properties state machine table +******************************************************************* + + All table cells are 8 bits: + bits 0..4: next state + bits 5..7: action to perform (if > 0) + + Cells may be of format "n" where n represents the next state + (except for the rightmost column). + Cells may also be of format "s(x,y)" where x represents an action + to perform and y represents the next state. + +******************************************************************* + Definitions and type for properties state table +******************************************************************* +*/ +#define IMPTABPROPS_COLUMNS 16 +#define IMPTABPROPS_RES (IMPTABPROPS_COLUMNS - 1) +#define GET_STATEPROPS(cell) ((cell)&0x1f) +#define GET_ACTIONPROPS(cell) ((cell)>>5) +#define s(action, newState) ((uint8_t)(newState+(action<<5))) + +static const uint8_t groupProp[] = /* dirProp regrouped */ +{ +/* L R EN ES ET AN CS B S WS ON LRE LRO AL RLE RLO PDF NSM BN FSI LRI RLI PDI ENL ENR */ + 0, 1, 2, 7, 8, 3, 9, 6, 5, 4, 4, 10, 10, 12, 10, 10, 10, 11, 10, 4, 4, 4, 4, 13, 14 +}; +enum { DirProp_L=0, DirProp_R=1, DirProp_EN=2, DirProp_AN=3, DirProp_ON=4, DirProp_S=5, DirProp_B=6 }; /* reduced dirProp */ + +/****************************************************************** + + PROPERTIES STATE TABLE + + In table impTabProps, + - the ON column regroups ON and WS, FSI, RLI, LRI and PDI + - the BN column regroups BN, LRE, RLE, LRO, RLO, PDF + - the Res column is the reduced property assigned to a run + + Action 1: process current run1, init new run1 + 2: init new run2 + 3: process run1, process run2, init new run1 + 4: process run1, set run1=run2, init new run2 + + Notes: + 1) This table is used in resolveImplicitLevels(). + 2) This table triggers actions when there is a change in the Bidi + property of incoming characters (action 1). + 3) Most such property sequences are processed immediately (in + fact, passed to processPropertySeq(). + 4) However, numbers are assembled as one sequence. This means + that undefined situations (like CS following digits, until + it is known if the next char will be a digit) are held until + following chars define them. + Example: digits followed by CS, then comes another CS or ON; + the digits will be processed, then the CS assigned + as the start of an ON sequence (action 3). + 5) There are cases where more than one sequence must be + processed, for instance digits followed by CS followed by L: + the digits must be processed as one sequence, and the CS + must be processed as an ON sequence, all this before starting + assembling chars for the opening L sequence. + + +*/ +static const uint8_t impTabProps[][IMPTABPROPS_COLUMNS] = +{ +/* L , R , EN , AN , ON , S , B , ES , ET , CS , BN , NSM , AL , ENL , ENR , Res */ +/* 0 Init */ { 1 , 2 , 4 , 5 , 7 , 15 , 17 , 7 , 9 , 7 , 0 , 7 , 3 , 18 , 21 , DirProp_ON }, +/* 1 L */ { 1 , s(1,2), s(1,4), s(1,5), s(1,7),s(1,15),s(1,17), s(1,7), s(1,9), s(1,7), 1 , 1 , s(1,3),s(1,18),s(1,21), DirProp_L }, +/* 2 R */ { s(1,1), 2 , s(1,4), s(1,5), s(1,7),s(1,15),s(1,17), s(1,7), s(1,9), s(1,7), 2 , 2 , s(1,3),s(1,18),s(1,21), DirProp_R }, +/* 3 AL */ { s(1,1), s(1,2), s(1,6), s(1,6), s(1,8),s(1,16),s(1,17), s(1,8), s(1,8), s(1,8), 3 , 3 , 3 ,s(1,18),s(1,21), DirProp_R }, +/* 4 EN */ { s(1,1), s(1,2), 4 , s(1,5), s(1,7),s(1,15),s(1,17),s(2,10), 11 ,s(2,10), 4 , 4 , s(1,3), 18 , 21 , DirProp_EN }, +/* 5 AN */ { s(1,1), s(1,2), s(1,4), 5 , s(1,7),s(1,15),s(1,17), s(1,7), s(1,9),s(2,12), 5 , 5 , s(1,3),s(1,18),s(1,21), DirProp_AN }, +/* 6 AL:EN/AN */ { s(1,1), s(1,2), 6 , 6 , s(1,8),s(1,16),s(1,17), s(1,8), s(1,8),s(2,13), 6 , 6 , s(1,3), 18 , 21 , DirProp_AN }, +/* 7 ON */ { s(1,1), s(1,2), s(1,4), s(1,5), 7 ,s(1,15),s(1,17), 7 ,s(2,14), 7 , 7 , 7 , s(1,3),s(1,18),s(1,21), DirProp_ON }, +/* 8 AL:ON */ { s(1,1), s(1,2), s(1,6), s(1,6), 8 ,s(1,16),s(1,17), 8 , 8 , 8 , 8 , 8 , s(1,3),s(1,18),s(1,21), DirProp_ON }, +/* 9 ET */ { s(1,1), s(1,2), 4 , s(1,5), 7 ,s(1,15),s(1,17), 7 , 9 , 7 , 9 , 9 , s(1,3), 18 , 21 , DirProp_ON }, +/*10 EN+ES/CS */ { s(3,1), s(3,2), 4 , s(3,5), s(4,7),s(3,15),s(3,17), s(4,7),s(4,14), s(4,7), 10 , s(4,7), s(3,3), 18 , 21 , DirProp_EN }, +/*11 EN+ET */ { s(1,1), s(1,2), 4 , s(1,5), s(1,7),s(1,15),s(1,17), s(1,7), 11 , s(1,7), 11 , 11 , s(1,3), 18 , 21 , DirProp_EN }, +/*12 AN+CS */ { s(3,1), s(3,2), s(3,4), 5 , s(4,7),s(3,15),s(3,17), s(4,7),s(4,14), s(4,7), 12 , s(4,7), s(3,3),s(3,18),s(3,21), DirProp_AN }, +/*13 AL:EN/AN+CS */ { s(3,1), s(3,2), 6 , 6 , s(4,8),s(3,16),s(3,17), s(4,8), s(4,8), s(4,8), 13 , s(4,8), s(3,3), 18 , 21 , DirProp_AN }, +/*14 ON+ET */ { s(1,1), s(1,2), s(4,4), s(1,5), 7 ,s(1,15),s(1,17), 7 , 14 , 7 , 14 , 14 , s(1,3),s(4,18),s(4,21), DirProp_ON }, +/*15 S */ { s(1,1), s(1,2), s(1,4), s(1,5), s(1,7), 15 ,s(1,17), s(1,7), s(1,9), s(1,7), 15 , s(1,7), s(1,3),s(1,18),s(1,21), DirProp_S }, +/*16 AL:S */ { s(1,1), s(1,2), s(1,6), s(1,6), s(1,8), 16 ,s(1,17), s(1,8), s(1,8), s(1,8), 16 , s(1,8), s(1,3),s(1,18),s(1,21), DirProp_S }, +/*17 B */ { s(1,1), s(1,2), s(1,4), s(1,5), s(1,7),s(1,15), 17 , s(1,7), s(1,9), s(1,7), 17 , s(1,7), s(1,3),s(1,18),s(1,21), DirProp_B }, +/*18 ENL */ { s(1,1), s(1,2), 18 , s(1,5), s(1,7),s(1,15),s(1,17),s(2,19), 20 ,s(2,19), 18 , 18 , s(1,3), 18 , 21 , DirProp_L }, +/*19 ENL+ES/CS */ { s(3,1), s(3,2), 18 , s(3,5), s(4,7),s(3,15),s(3,17), s(4,7),s(4,14), s(4,7), 19 , s(4,7), s(3,3), 18 , 21 , DirProp_L }, +/*20 ENL+ET */ { s(1,1), s(1,2), 18 , s(1,5), s(1,7),s(1,15),s(1,17), s(1,7), 20 , s(1,7), 20 , 20 , s(1,3), 18 , 21 , DirProp_L }, +/*21 ENR */ { s(1,1), s(1,2), 21 , s(1,5), s(1,7),s(1,15),s(1,17),s(2,22), 23 ,s(2,22), 21 , 21 , s(1,3), 18 , 21 , DirProp_AN }, +/*22 ENR+ES/CS */ { s(3,1), s(3,2), 21 , s(3,5), s(4,7),s(3,15),s(3,17), s(4,7),s(4,14), s(4,7), 22 , s(4,7), s(3,3), 18 , 21 , DirProp_AN }, +/*23 ENR+ET */ { s(1,1), s(1,2), 21 , s(1,5), s(1,7),s(1,15),s(1,17), s(1,7), 23 , s(1,7), 23 , 23 , s(1,3), 18 , 21 , DirProp_AN } +}; + +/* we must undef macro s because the levels tables have a different + * structure (4 bits for action and 4 bits for next state. + */ +#undef s + +/****************************************************************** + The levels state machine tables +******************************************************************* + + All table cells are 8 bits: + bits 0..3: next state + bits 4..7: action to perform (if > 0) + + Cells may be of format "n" where n represents the next state + (except for the rightmost column). + Cells may also be of format "s(x,y)" where x represents an action + to perform and y represents the next state. + + This format limits each table to 16 states each and to 15 actions. + +******************************************************************* + Definitions and type for levels state tables +******************************************************************* +*/ +#define IMPTABLEVELS_COLUMNS (DirProp_B + 2) +#define IMPTABLEVELS_RES (IMPTABLEVELS_COLUMNS - 1) +#define GET_STATE(cell) ((cell)&0x0f) +#define GET_ACTION(cell) ((cell)>>4) +#define s(action, newState) ((uint8_t)(newState+(action<<4))) + +typedef uint8_t ImpTab[][IMPTABLEVELS_COLUMNS]; +typedef uint8_t ImpAct[]; + +/* FOOD FOR THOUGHT: each ImpTab should have its associated ImpAct, + * instead of having a pair of ImpTab and a pair of ImpAct. + */ +typedef struct ImpTabPair { + const void * pImpTab[2]; + const void * pImpAct[2]; +} ImpTabPair; + +/****************************************************************** + + LEVELS STATE TABLES + + In all levels state tables, + - state 0 is the initial state + - the Res column is the increment to add to the text level + for this property sequence. + + The impAct arrays for each table of a pair map the local action + numbers of the table to the total list of actions. For instance, + action 2 in a given table corresponds to the action number which + appears in entry [2] of the impAct array for that table. + The first entry of all impAct arrays must be 0. + + Action 1: init conditional sequence + 2: prepend conditional sequence to current sequence + 3: set ON sequence to new level - 1 + 4: init EN/AN/ON sequence + 5: fix EN/AN/ON sequence followed by R + 6: set previous level sequence to level 2 + + Notes: + 1) These tables are used in processPropertySeq(). The input + is property sequences as determined by resolveImplicitLevels. + 2) Most such property sequences are processed immediately + (levels are assigned). + 3) However, some sequences cannot be assigned a final level till + one or more following sequences are received. For instance, + ON following an R sequence within an even-level paragraph. + If the following sequence is R, the ON sequence will be + assigned basic run level+1, and so will the R sequence. + 4) S is generally handled like ON, since its level will be fixed + to paragraph level in adjustWSLevels(). + +*/ + +static const ImpTab impTabL_DEFAULT = /* Even paragraph level */ +/* In this table, conditional sequences receive the lower possible level + until proven otherwise. +*/ +{ +/* L , R , EN , AN , ON , S , B , Res */ +/* 0 : init */ { 0 , 1 , 0 , 2 , 0 , 0 , 0 , 0 }, +/* 1 : R */ { 0 , 1 , 3 , 3 , s(1,4), s(1,4), 0 , 1 }, +/* 2 : AN */ { 0 , 1 , 0 , 2 , s(1,5), s(1,5), 0 , 2 }, +/* 3 : R+EN/AN */ { 0 , 1 , 3 , 3 , s(1,4), s(1,4), 0 , 2 }, +/* 4 : R+ON */ { 0 , s(2,1), s(3,3), s(3,3), 4 , 4 , 0 , 0 }, +/* 5 : AN+ON */ { 0 , s(2,1), 0 , s(3,2), 5 , 5 , 0 , 0 } +}; +static const ImpTab impTabR_DEFAULT = /* Odd paragraph level */ +/* In this table, conditional sequences receive the lower possible level + until proven otherwise. +*/ +{ +/* L , R , EN , AN , ON , S , B , Res */ +/* 0 : init */ { 1 , 0 , 2 , 2 , 0 , 0 , 0 , 0 }, +/* 1 : L */ { 1 , 0 , 1 , 3 , s(1,4), s(1,4), 0 , 1 }, +/* 2 : EN/AN */ { 1 , 0 , 2 , 2 , 0 , 0 , 0 , 1 }, +/* 3 : L+AN */ { 1 , 0 , 1 , 3 , 5 , 5 , 0 , 1 }, +/* 4 : L+ON */ { s(2,1), 0 , s(2,1), 3 , 4 , 4 , 0 , 0 }, +/* 5 : L+AN+ON */ { 1 , 0 , 1 , 3 , 5 , 5 , 0 , 0 } +}; +static const ImpAct impAct0 = {0,1,2,3,4}; +static const ImpTabPair impTab_DEFAULT = {{&impTabL_DEFAULT, + &impTabR_DEFAULT}, + {&impAct0, &impAct0}}; + +static const ImpTab impTabL_NUMBERS_SPECIAL = /* Even paragraph level */ +/* In this table, conditional sequences receive the lower possible level + until proven otherwise. +*/ +{ +/* L , R , EN , AN , ON , S , B , Res */ +/* 0 : init */ { 0 , 2 , s(1,1), s(1,1), 0 , 0 , 0 , 0 }, +/* 1 : L+EN/AN */ { 0 , s(4,2), 1 , 1 , 0 , 0 , 0 , 0 }, +/* 2 : R */ { 0 , 2 , 4 , 4 , s(1,3), s(1,3), 0 , 1 }, +/* 3 : R+ON */ { 0 , s(2,2), s(3,4), s(3,4), 3 , 3 , 0 , 0 }, +/* 4 : R+EN/AN */ { 0 , 2 , 4 , 4 , s(1,3), s(1,3), 0 , 2 } +}; +static const ImpTabPair impTab_NUMBERS_SPECIAL = {{&impTabL_NUMBERS_SPECIAL, + &impTabR_DEFAULT}, + {&impAct0, &impAct0}}; + +static const ImpTab impTabL_GROUP_NUMBERS_WITH_R = +/* In this table, EN/AN+ON sequences receive levels as if associated with R + until proven that there is L or sor/eor on both sides. AN is handled like EN. +*/ +{ +/* L , R , EN , AN , ON , S , B , Res */ +/* 0 init */ { 0 , 3 , s(1,1), s(1,1), 0 , 0 , 0 , 0 }, +/* 1 EN/AN */ { s(2,0), 3 , 1 , 1 , 2 , s(2,0), s(2,0), 2 }, +/* 2 EN/AN+ON */ { s(2,0), 3 , 1 , 1 , 2 , s(2,0), s(2,0), 1 }, +/* 3 R */ { 0 , 3 , 5 , 5 , s(1,4), 0 , 0 , 1 }, +/* 4 R+ON */ { s(2,0), 3 , 5 , 5 , 4 , s(2,0), s(2,0), 1 }, +/* 5 R+EN/AN */ { 0 , 3 , 5 , 5 , s(1,4), 0 , 0 , 2 } +}; +static const ImpTab impTabR_GROUP_NUMBERS_WITH_R = +/* In this table, EN/AN+ON sequences receive levels as if associated with R + until proven that there is L on both sides. AN is handled like EN. +*/ +{ +/* L , R , EN , AN , ON , S , B , Res */ +/* 0 init */ { 2 , 0 , 1 , 1 , 0 , 0 , 0 , 0 }, +/* 1 EN/AN */ { 2 , 0 , 1 , 1 , 0 , 0 , 0 , 1 }, +/* 2 L */ { 2 , 0 , s(1,4), s(1,4), s(1,3), 0 , 0 , 1 }, +/* 3 L+ON */ { s(2,2), 0 , 4 , 4 , 3 , 0 , 0 , 0 }, +/* 4 L+EN/AN */ { s(2,2), 0 , 4 , 4 , 3 , 0 , 0 , 1 } +}; +static const ImpTabPair impTab_GROUP_NUMBERS_WITH_R = { + {&impTabL_GROUP_NUMBERS_WITH_R, + &impTabR_GROUP_NUMBERS_WITH_R}, + {&impAct0, &impAct0}}; + + +static const ImpTab impTabL_INVERSE_NUMBERS_AS_L = +/* This table is identical to the Default LTR table except that EN and AN are + handled like L. +*/ +{ +/* L , R , EN , AN , ON , S , B , Res */ +/* 0 : init */ { 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 }, +/* 1 : R */ { 0 , 1 , 0 , 0 , s(1,4), s(1,4), 0 , 1 }, +/* 2 : AN */ { 0 , 1 , 0 , 0 , s(1,5), s(1,5), 0 , 2 }, +/* 3 : R+EN/AN */ { 0 , 1 , 0 , 0 , s(1,4), s(1,4), 0 , 2 }, +/* 4 : R+ON */ { s(2,0), 1 , s(2,0), s(2,0), 4 , 4 , s(2,0), 1 }, +/* 5 : AN+ON */ { s(2,0), 1 , s(2,0), s(2,0), 5 , 5 , s(2,0), 1 } +}; +static const ImpTab impTabR_INVERSE_NUMBERS_AS_L = +/* This table is identical to the Default RTL table except that EN and AN are + handled like L. +*/ +{ +/* L , R , EN , AN , ON , S , B , Res */ +/* 0 : init */ { 1 , 0 , 1 , 1 , 0 , 0 , 0 , 0 }, +/* 1 : L */ { 1 , 0 , 1 , 1 , s(1,4), s(1,4), 0 , 1 }, +/* 2 : EN/AN */ { 1 , 0 , 1 , 1 , 0 , 0 , 0 , 1 }, +/* 3 : L+AN */ { 1 , 0 , 1 , 1 , 5 , 5 , 0 , 1 }, +/* 4 : L+ON */ { s(2,1), 0 , s(2,1), s(2,1), 4 , 4 , 0 , 0 }, +/* 5 : L+AN+ON */ { 1 , 0 , 1 , 1 , 5 , 5 , 0 , 0 } +}; +static const ImpTabPair impTab_INVERSE_NUMBERS_AS_L = { + {&impTabL_INVERSE_NUMBERS_AS_L, + &impTabR_INVERSE_NUMBERS_AS_L}, + {&impAct0, &impAct0}}; + +static const ImpTab impTabR_INVERSE_LIKE_DIRECT = /* Odd paragraph level */ +/* In this table, conditional sequences receive the lower possible level + until proven otherwise. +*/ +{ +/* L , R , EN , AN , ON , S , B , Res */ +/* 0 : init */ { 1 , 0 , 2 , 2 , 0 , 0 , 0 , 0 }, +/* 1 : L */ { 1 , 0 , 1 , 2 , s(1,3), s(1,3), 0 , 1 }, +/* 2 : EN/AN */ { 1 , 0 , 2 , 2 , 0 , 0 , 0 , 1 }, +/* 3 : L+ON */ { s(2,1), s(3,0), 6 , 4 , 3 , 3 , s(3,0), 0 }, +/* 4 : L+ON+AN */ { s(2,1), s(3,0), 6 , 4 , 5 , 5 , s(3,0), 3 }, +/* 5 : L+AN+ON */ { s(2,1), s(3,0), 6 , 4 , 5 , 5 , s(3,0), 2 }, +/* 6 : L+ON+EN */ { s(2,1), s(3,0), 6 , 4 , 3 , 3 , s(3,0), 1 } +}; +static const ImpAct impAct1 = {0,1,13,14}; +/* FOOD FOR THOUGHT: in LTR table below, check case "JKL 123abc" + */ +static const ImpTabPair impTab_INVERSE_LIKE_DIRECT = { + {&impTabL_DEFAULT, + &impTabR_INVERSE_LIKE_DIRECT}, + {&impAct0, &impAct1}}; + +static const ImpTab impTabL_INVERSE_LIKE_DIRECT_WITH_MARKS = +/* The case handled in this table is (visually): R EN L +*/ +{ +/* L , R , EN , AN , ON , S , B , Res */ +/* 0 : init */ { 0 , s(6,3), 0 , 1 , 0 , 0 , 0 , 0 }, +/* 1 : L+AN */ { 0 , s(6,3), 0 , 1 , s(1,2), s(3,0), 0 , 4 }, +/* 2 : L+AN+ON */ { s(2,0), s(6,3), s(2,0), 1 , 2 , s(3,0), s(2,0), 3 }, +/* 3 : R */ { 0 , s(6,3), s(5,5), s(5,6), s(1,4), s(3,0), 0 , 3 }, +/* 4 : R+ON */ { s(3,0), s(4,3), s(5,5), s(5,6), 4 , s(3,0), s(3,0), 3 }, +/* 5 : R+EN */ { s(3,0), s(4,3), 5 , s(5,6), s(1,4), s(3,0), s(3,0), 4 }, +/* 6 : R+AN */ { s(3,0), s(4,3), s(5,5), 6 , s(1,4), s(3,0), s(3,0), 4 } +}; +static const ImpTab impTabR_INVERSE_LIKE_DIRECT_WITH_MARKS = +/* The cases handled in this table are (visually): R EN L + R L AN L +*/ +{ +/* L , R , EN , AN , ON , S , B , Res */ +/* 0 : init */ { s(1,3), 0 , 1 , 1 , 0 , 0 , 0 , 0 }, +/* 1 : R+EN/AN */ { s(2,3), 0 , 1 , 1 , 2 , s(4,0), 0 , 1 }, +/* 2 : R+EN/AN+ON */ { s(2,3), 0 , 1 , 1 , 2 , s(4,0), 0 , 0 }, +/* 3 : L */ { 3 , 0 , 3 , s(3,6), s(1,4), s(4,0), 0 , 1 }, +/* 4 : L+ON */ { s(5,3), s(4,0), 5 , s(3,6), 4 , s(4,0), s(4,0), 0 }, +/* 5 : L+ON+EN */ { s(5,3), s(4,0), 5 , s(3,6), 4 , s(4,0), s(4,0), 1 }, +/* 6 : L+AN */ { s(5,3), s(4,0), 6 , 6 , 4 , s(4,0), s(4,0), 3 } +}; +static const ImpAct impAct2 = {0,1,2,5,6,7,8}; +static const ImpAct impAct3 = {0,1,9,10,11,12}; +static const ImpTabPair impTab_INVERSE_LIKE_DIRECT_WITH_MARKS = { + {&impTabL_INVERSE_LIKE_DIRECT_WITH_MARKS, + &impTabR_INVERSE_LIKE_DIRECT_WITH_MARKS}, + {&impAct2, &impAct3}}; + +static const ImpTabPair impTab_INVERSE_FOR_NUMBERS_SPECIAL = { + {&impTabL_NUMBERS_SPECIAL, + &impTabR_INVERSE_LIKE_DIRECT}, + {&impAct0, &impAct1}}; + +static const ImpTab impTabL_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS = +/* The case handled in this table is (visually): R EN L +*/ +{ +/* L , R , EN , AN , ON , S , B , Res */ +/* 0 : init */ { 0 , s(6,2), 1 , 1 , 0 , 0 , 0 , 0 }, +/* 1 : L+EN/AN */ { 0 , s(6,2), 1 , 1 , 0 , s(3,0), 0 , 4 }, +/* 2 : R */ { 0 , s(6,2), s(5,4), s(5,4), s(1,3), s(3,0), 0 , 3 }, +/* 3 : R+ON */ { s(3,0), s(4,2), s(5,4), s(5,4), 3 , s(3,0), s(3,0), 3 }, +/* 4 : R+EN/AN */ { s(3,0), s(4,2), 4 , 4 , s(1,3), s(3,0), s(3,0), 4 } +}; +static const ImpTabPair impTab_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS = { + {&impTabL_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS, + &impTabR_INVERSE_LIKE_DIRECT_WITH_MARKS}, + {&impAct2, &impAct3}}; + +#undef s + +typedef struct { + const ImpTab * pImpTab; /* level table pointer */ + const ImpAct * pImpAct; /* action map array */ + int32_t startON; /* start of ON sequence */ + int32_t startL2EN; /* start of level 2 sequence */ + int32_t lastStrongRTL; /* index of last found R or AL */ + int32_t state; /* current state */ + int32_t runStart; /* start position of the run */ + UBiDiLevel runLevel; /* run level before implicit solving */ +} LevState; + +/*------------------------------------------------------------------------*/ + +static void +addPoint(UBiDi *pBiDi, int32_t pos, int32_t flag) + /* param pos: position where to insert + param flag: one of LRM_BEFORE, LRM_AFTER, RLM_BEFORE, RLM_AFTER + */ +{ +#define FIRSTALLOC 10 + Point point; + InsertPoints * pInsertPoints=&(pBiDi->insertPoints); + + if (pInsertPoints->capacity == 0) + { + pInsertPoints->points=static_cast(uprv_malloc(sizeof(Point)*FIRSTALLOC)); + if (pInsertPoints->points == nullptr) + { + pInsertPoints->errorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + pInsertPoints->capacity=FIRSTALLOC; + } + if (pInsertPoints->size >= pInsertPoints->capacity) /* no room for new point */ + { + Point * savePoints=pInsertPoints->points; + pInsertPoints->points=static_cast(uprv_realloc(pInsertPoints->points, + pInsertPoints->capacity*2*sizeof(Point))); + if (pInsertPoints->points == nullptr) + { + pInsertPoints->points=savePoints; + pInsertPoints->errorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + else pInsertPoints->capacity*=2; + } + point.pos=pos; + point.flag=flag; + pInsertPoints->points[pInsertPoints->size]=point; + pInsertPoints->size++; +#undef FIRSTALLOC +} + +static void +setLevelsOutsideIsolates(UBiDi *pBiDi, int32_t start, int32_t limit, UBiDiLevel level) +{ + DirProp *dirProps=pBiDi->dirProps, dirProp; + UBiDiLevel *levels=pBiDi->levels; + int32_t isolateCount=0, k; + for(k=start; kpImpTab; + const ImpAct * pImpAct=pLevState->pImpAct; + UBiDiLevel * levels=pBiDi->levels; + UBiDiLevel level, addLevel; + InsertPoints * pInsertPoints; + int32_t start0, k; + + start0=start; /* save original start position */ + oldStateSeq=(uint8_t)pLevState->state; + cell=(*pImpTab)[oldStateSeq][_prop]; + pLevState->state=GET_STATE(cell); /* isolate the new state */ + actionSeq=(*pImpAct)[GET_ACTION(cell)]; /* isolate the action */ + addLevel=(*pImpTab)[pLevState->state][IMPTABLEVELS_RES]; + + if(actionSeq) { + switch(actionSeq) { + case 1: /* init ON seq */ + pLevState->startON=start0; + break; + + case 2: /* prepend ON seq to current seq */ + start=pLevState->startON; + break; + + case 3: /* EN/AN after R+ON */ + level=pLevState->runLevel+1; + setLevelsOutsideIsolates(pBiDi, pLevState->startON, start0, level); + break; + + case 4: /* EN/AN before R for NUMBERS_SPECIAL */ + level=pLevState->runLevel+2; + setLevelsOutsideIsolates(pBiDi, pLevState->startON, start0, level); + break; + + case 5: /* L or S after possible relevant EN/AN */ + /* check if we had EN after R/AL */ + if (pLevState->startL2EN >= 0) { + addPoint(pBiDi, pLevState->startL2EN, LRM_BEFORE); + } + pLevState->startL2EN=-1; /* not within previous if since could also be -2 */ + /* check if we had any relevant EN/AN after R/AL */ + pInsertPoints=&(pBiDi->insertPoints); + if ((pInsertPoints->capacity == 0) || + (pInsertPoints->size <= pInsertPoints->confirmed)) + { + /* nothing, just clean up */ + pLevState->lastStrongRTL=-1; + /* check if we have a pending conditional segment */ + level=(*pImpTab)[oldStateSeq][IMPTABLEVELS_RES]; + if ((level & 1) && (pLevState->startON > 0)) { /* after ON */ + start=pLevState->startON; /* reset to basic run level */ + } + if (_prop == DirProp_S) /* add LRM before S */ + { + addPoint(pBiDi, start0, LRM_BEFORE); + pInsertPoints->confirmed=pInsertPoints->size; + } + break; + } + /* reset previous RTL cont to level for LTR text */ + for (k=pLevState->lastStrongRTL+1; kconfirmed=pInsertPoints->size; + pLevState->lastStrongRTL=-1; + if (_prop == DirProp_S) /* add LRM before S */ + { + addPoint(pBiDi, start0, LRM_BEFORE); + pInsertPoints->confirmed=pInsertPoints->size; + } + break; + + case 6: /* R/AL after possible relevant EN/AN */ + /* just clean up */ + pInsertPoints=&(pBiDi->insertPoints); + if (pInsertPoints->capacity > 0) + /* remove all non confirmed insert points */ + pInsertPoints->size=pInsertPoints->confirmed; + pLevState->startON=-1; + pLevState->startL2EN=-1; + pLevState->lastStrongRTL=limit - 1; + break; + + case 7: /* EN/AN after R/AL + possible cont */ + /* check for real AN */ + if ((_prop == DirProp_AN) && (pBiDi->dirProps[start0] == AN) && + (pBiDi->reorderingMode!=UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL)) + { + /* real AN */ + if (pLevState->startL2EN == -1) /* if no relevant EN already found */ + { + /* just note the righmost digit as a strong RTL */ + pLevState->lastStrongRTL=limit - 1; + break; + } + if (pLevState->startL2EN >= 0) /* after EN, no AN */ + { + addPoint(pBiDi, pLevState->startL2EN, LRM_BEFORE); + pLevState->startL2EN=-2; + } + /* note AN */ + addPoint(pBiDi, start0, LRM_BEFORE); + break; + } + /* if first EN/AN after R/AL */ + if (pLevState->startL2EN == -1) { + pLevState->startL2EN=start0; + } + break; + + case 8: /* note location of latest R/AL */ + pLevState->lastStrongRTL=limit - 1; + pLevState->startON=-1; + break; + + case 9: /* L after R+ON/EN/AN */ + /* include possible adjacent number on the left */ + for (k=start0-1; k>=0 && !(levels[k]&1); k--); + if(k>=0) { + addPoint(pBiDi, k, RLM_BEFORE); /* add RLM before */ + pInsertPoints=&(pBiDi->insertPoints); + pInsertPoints->confirmed=pInsertPoints->size; /* confirm it */ + } + pLevState->startON=start0; + break; + + case 10: /* AN after L */ + /* AN numbers between L text on both sides may be trouble. */ + /* tentatively bracket with LRMs; will be confirmed if followed by L */ + addPoint(pBiDi, start0, LRM_BEFORE); /* add LRM before */ + addPoint(pBiDi, start0, LRM_AFTER); /* add LRM after */ + break; + + case 11: /* R after L+ON/EN/AN */ + /* false alert, infirm LRMs around previous AN */ + pInsertPoints=&(pBiDi->insertPoints); + pInsertPoints->size=pInsertPoints->confirmed; + if (_prop == DirProp_S) /* add RLM before S */ + { + addPoint(pBiDi, start0, RLM_BEFORE); + pInsertPoints->confirmed=pInsertPoints->size; + } + break; + + case 12: /* L after L+ON/AN */ + level=pLevState->runLevel + addLevel; + for(k=pLevState->startON; kinsertPoints); + pInsertPoints->confirmed=pInsertPoints->size; /* confirm inserts */ + pLevState->startON=start0; + break; + + case 13: /* L after L+ON+EN/AN/ON */ + level=pLevState->runLevel; + for(k=start0-1; k>=pLevState->startON; k--) { + if(levels[k]==level+3) { + while(levels[k]==level+3) { + levels[k--]-=2; + } + while(levels[k]==level) { + k--; + } + } + if(levels[k]==level+2) { + levels[k]=level; + continue; + } + levels[k]=level+1; + } + break; + + case 14: /* R after L+ON+EN/AN/ON */ + level=pLevState->runLevel+1; + for(k=start0-1; k>=pLevState->startON; k--) { + if(levels[k]>level) { + levels[k]-=2; + } + } + break; + + default: /* we should never get here */ + UPRV_UNREACHABLE_EXIT; + } + } + if((addLevel) || (start < start0)) { + level=pLevState->runLevel + addLevel; + if(start>=pLevState->runStart) { + for(k=start; kprologue; + int32_t length=pBiDi->proLength; + int32_t i; + UChar32 uchar; + DirProp dirProp; + for(i=length; i>0; ) { + /* i is decremented by U16_PREV */ + U16_PREV(text, 0, i, uchar); + dirProp=(DirProp)ubidi_getCustomizedClass(pBiDi, uchar); + if(dirProp==L) { + return DirProp_L; + } + if(dirProp==R || dirProp==AL) { + return DirProp_R; + } + if(dirProp==B) { + return DirProp_ON; + } + } + return DirProp_ON; +} + +/** + * Returns the directionality of the first strong character, or digit, in the epilogue, if any. + * Requires epilogue!=null. + */ +static DirProp +firstL_R_AL_EN_AN(UBiDi *pBiDi) { + const char16_t *text=pBiDi->epilogue; + int32_t length=pBiDi->epiLength; + int32_t i; + UChar32 uchar; + DirProp dirProp; + for(i=0; idirProps; + DirProp dirProp; + LevState levState; + int32_t i, start1, start2; + uint16_t oldStateImp, stateImp, actionImp; + uint8_t gprop, resProp, cell; + UBool inverseRTL; + DirProp nextStrongProp=R; + int32_t nextStrongPos=-1; + + /* check for RTL inverse BiDi mode */ + /* FOOD FOR THOUGHT: in case of RTL inverse BiDi, it would make sense to + * loop on the text characters from end to start. + * This would need a different properties state table (at least different + * actions) and different levels state tables (maybe very similar to the + * LTR corresponding ones. + */ + inverseRTL=(UBool) + ((startlastArabicPos) && (GET_PARALEVEL(pBiDi, start) & 1) && + (pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_LIKE_DIRECT || + pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL)); + + /* initialize for property and levels state tables */ + levState.startL2EN=-1; /* used for INVERSE_LIKE_DIRECT_WITH_MARKS */ + levState.lastStrongRTL=-1; /* used for INVERSE_LIKE_DIRECT_WITH_MARKS */ + levState.runStart=start; + levState.runLevel=pBiDi->levels[start]; + levState.pImpTab=(const ImpTab*)((pBiDi->pImpTabPair)->pImpTab)[levState.runLevel&1]; + levState.pImpAct=(const ImpAct*)((pBiDi->pImpTabPair)->pImpAct)[levState.runLevel&1]; + if(start==0 && pBiDi->proLength>0) { + DirProp lastStrong=lastL_R_AL(pBiDi); + if(lastStrong!=DirProp_ON) { + sor=lastStrong; + } + } + /* The isolates[] entries contain enough information to + resume the bidi algorithm in the same state as it was + when it was interrupted by an isolate sequence. */ + if(dirProps[start]==PDI && pBiDi->isolateCount >= 0) { + levState.startON=pBiDi->isolates[pBiDi->isolateCount].startON; + start1=pBiDi->isolates[pBiDi->isolateCount].start1; + stateImp=pBiDi->isolates[pBiDi->isolateCount].stateImp; + levState.state=pBiDi->isolates[pBiDi->isolateCount].state; + pBiDi->isolateCount--; + } else { + levState.startON=-1; + start1=start; + if(dirProps[start]==NSM) + stateImp = 1 + sor; + else + stateImp=0; + levState.state=0; + processPropertySeq(pBiDi, &levState, sor, start, start); + } + start2=start; /* to make Java compiler happy */ + + for(i=start; i<=limit; i++) { + if(i>=limit) { + int32_t k; + for(k=limit-1; k>start&&(DIRPROP_FLAG(dirProps[k])&MASK_BN_EXPLICIT); k--); + dirProp=dirProps[k]; + if(dirProp==LRI || dirProp==RLI) + break; /* no forced closing for sequence ending with LRI/RLI */ + gprop=eor; + } else { + DirProp prop, prop1; + prop=dirProps[i]; + if(prop==B) { + pBiDi->isolateCount=-1; /* current isolates stack entry == none */ + } + if(inverseRTL) { + if(prop==AL) { + /* AL before EN does not make it AN */ + prop=R; + } else if(prop==EN) { + if(nextStrongPos<=i) { + /* look for next strong char (L/R/AL) */ + int32_t j; + nextStrongProp=R; /* set default */ + nextStrongPos=limit; + for(j=i+1; jlength && pBiDi->epiLength>0) { + DirProp firstStrong=firstL_R_AL_EN_AN(pBiDi); + if(firstStrong!=DirProp_ON) { + eor=firstStrong; + } + } + + /* look for the last char not a BN or LRE/RLE/LRO/RLO/PDF */ + for(i=limit-1; i>start&&(DIRPROP_FLAG(dirProps[i])&MASK_BN_EXPLICIT); i--); + dirProp=dirProps[i]; + if((dirProp==LRI || dirProp==RLI) && limitlength) { + pBiDi->isolateCount++; + pBiDi->isolates[pBiDi->isolateCount].stateImp=stateImp; + pBiDi->isolates[pBiDi->isolateCount].state=levState.state; + pBiDi->isolates[pBiDi->isolateCount].start1=start1; + pBiDi->isolates[pBiDi->isolateCount].startON=levState.startON; + } + else + processPropertySeq(pBiDi, &levState, eor, limit, limit); +} + +/* perform (L1) and (X9) ---------------------------------------------------- */ + +/* + * Reset the embedding levels for some non-graphic characters (L1). + * This function also sets appropriate levels for BN, and + * explicit embedding types that are supposed to have been removed + * from the paragraph in (X9). + */ +static void +adjustWSLevels(UBiDi *pBiDi) { + const DirProp *dirProps=pBiDi->dirProps; + UBiDiLevel *levels=pBiDi->levels; + int32_t i; + + if(pBiDi->flags&MASK_WS) { + UBool orderParagraphsLTR=pBiDi->orderParagraphsLTR; + Flags flag; + + i=pBiDi->trailingWSStart; + while(i>0) { + /* reset a sequence of WS/BN before eop and B/S to the paragraph paraLevel */ + while(i>0 && (flag=DIRPROP_FLAG(dirProps[--i]))&MASK_WS) { + if(orderParagraphsLTR&&(flag&DIRPROP_FLAG(B))) { + levels[i]=0; + } else { + levels[i]=GET_PARALEVEL(pBiDi, i); + } + } + + /* reset BN to the next character's paraLevel until B/S, which restarts above loop */ + /* here, i+1 is guaranteed to be 0) { + flag=DIRPROP_FLAG(dirProps[--i]); + if(flag&MASK_BN_EXPLICIT) { + levels[i]=levels[i+1]; + } else if(orderParagraphsLTR&&(flag&DIRPROP_FLAG(B))) { + levels[i]=0; + break; + } else if(flag&MASK_B_S) { + levels[i]=GET_PARALEVEL(pBiDi, i); + break; + } + } + } + } +} + +U_CAPI void U_EXPORT2 +ubidi_setContext(UBiDi *pBiDi, + const char16_t *prologue, int32_t proLength, + const char16_t *epilogue, int32_t epiLength, + UErrorCode *pErrorCode) { + /* check the argument values */ + RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); + if(pBiDi==nullptr || proLength<-1 || epiLength<-1 || + (prologue==nullptr && proLength!=0) || (epilogue==nullptr && epiLength!=0)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + if(proLength==-1) { + pBiDi->proLength=u_strlen(prologue); + } else { + pBiDi->proLength=proLength; + } + if(epiLength==-1) { + pBiDi->epiLength=u_strlen(epilogue); + } else { + pBiDi->epiLength=epiLength; + } + pBiDi->prologue=prologue; + pBiDi->epilogue=epilogue; +} + +static void +setParaSuccess(UBiDi *pBiDi) { + pBiDi->proLength=0; /* forget the last context */ + pBiDi->epiLength=0; + pBiDi->pParaBiDi=pBiDi; /* mark successful setPara */ +} + +#define BIDI_MIN(x, y) ((x)<(y) ? (x) : (y)) +#define BIDI_ABS(x) ((x)>=0 ? (x) : (-(x))) + +static void +setParaRunsOnly(UBiDi *pBiDi, const char16_t *text, int32_t length, + UBiDiLevel paraLevel, UErrorCode *pErrorCode) { + int32_t *runsOnlyMemory = nullptr; + int32_t *visualMap; + char16_t *visualText; + int32_t saveLength, saveTrailingWSStart; + const UBiDiLevel *levels; + UBiDiLevel *saveLevels; + UBiDiDirection saveDirection; + UBool saveMayAllocateText; + Run *runs; + int32_t visualLength, i, j, visualStart, logicalStart, + runCount, runLength, addedRuns, insertRemove, + start, limit, step, indexOddBit, logicalPos, + index0, index1; + uint32_t saveOptions; + + pBiDi->reorderingMode=UBIDI_REORDER_DEFAULT; + if(length==0) { + ubidi_setPara(pBiDi, text, length, paraLevel, nullptr, pErrorCode); + goto cleanup3; + } + /* obtain memory for mapping table and visual text */ + runsOnlyMemory=static_cast(uprv_malloc(length*(sizeof(int32_t)+sizeof(char16_t)+sizeof(UBiDiLevel)))); + if(runsOnlyMemory==nullptr) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + goto cleanup3; + } + visualMap=runsOnlyMemory; + visualText=(char16_t *)&visualMap[length]; + saveLevels=(UBiDiLevel *)&visualText[length]; + saveOptions=pBiDi->reorderingOptions; + if(saveOptions & UBIDI_OPTION_INSERT_MARKS) { + pBiDi->reorderingOptions&=~UBIDI_OPTION_INSERT_MARKS; + pBiDi->reorderingOptions|=UBIDI_OPTION_REMOVE_CONTROLS; + } + paraLevel&=1; /* accept only 0 or 1 */ + ubidi_setPara(pBiDi, text, length, paraLevel, nullptr, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + goto cleanup3; + } + /* we cannot access directly pBiDi->levels since it is not yet set if + * direction is not MIXED + */ + levels=ubidi_getLevels(pBiDi, pErrorCode); + uprv_memcpy(saveLevels, levels, (size_t)pBiDi->length*sizeof(UBiDiLevel)); + saveTrailingWSStart=pBiDi->trailingWSStart; + saveLength=pBiDi->length; + saveDirection=pBiDi->direction; + + /* FOOD FOR THOUGHT: instead of writing the visual text, we could use + * the visual map and the dirProps array to drive the second call + * to ubidi_setPara (but must make provision for possible removal of + * BiDi controls. Alternatively, only use the dirProps array via + * customized classifier callback. + */ + visualLength=ubidi_writeReordered(pBiDi, visualText, length, + UBIDI_DO_MIRRORING, pErrorCode); + ubidi_getVisualMap(pBiDi, visualMap, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + goto cleanup2; + } + pBiDi->reorderingOptions=saveOptions; + + pBiDi->reorderingMode=UBIDI_REORDER_INVERSE_LIKE_DIRECT; + paraLevel^=1; + /* Because what we did with reorderingOptions, visualText may be shorter + * than the original text. But we don't want the levels memory to be + * reallocated shorter than the original length, since we need to restore + * the levels as after the first call to ubidi_setpara() before returning. + * We will force mayAllocateText to false before the second call to + * ubidi_setpara(), and will restore it afterwards. + */ + saveMayAllocateText=pBiDi->mayAllocateText; + pBiDi->mayAllocateText=false; + ubidi_setPara(pBiDi, visualText, visualLength, paraLevel, nullptr, pErrorCode); + pBiDi->mayAllocateText=saveMayAllocateText; + ubidi_getRuns(pBiDi, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + goto cleanup1; + } + /* check if some runs must be split, count how many splits */ + addedRuns=0; + runCount=pBiDi->runCount; + runs=pBiDi->runs; + visualStart=0; + for(i=0; irunsMemory[0]=runs[0]; + } + runs=pBiDi->runs=pBiDi->runsMemory; + pBiDi->runCount+=addedRuns; + } else { + goto cleanup1; + } + } + /* split runs which are not consecutive in source text */ + for(i=runCount-1; i>=0; i--) { + runLength= i==0 ? runs[0].visualLimit : + runs[i].visualLimit-runs[i-1].visualLimit; + logicalStart=runs[i].logicalStart; + indexOddBit=GET_ODD_BIT(logicalStart); + logicalStart=GET_INDEX(logicalStart); + if(runLength<2) { + if(addedRuns) { + runs[i+addedRuns]=runs[i]; + } + logicalPos=visualMap[logicalStart]; + runs[i+addedRuns].logicalStart=MAKE_INDEX_ODD_PAIR(logicalPos, + saveLevels[logicalPos]^indexOddBit); + continue; + } + if(indexOddBit) { + start=logicalStart; + limit=logicalStart+runLength-1; + step=1; + } else { + start=logicalStart+runLength-1; + limit=logicalStart; + step=-1; + } + for(j=start; j!=limit; j+=step) { + index0=visualMap[j]; + index1=visualMap[j+step]; + if((BIDI_ABS(index0-index1)!=1) || (saveLevels[index0]!=saveLevels[index1])) { + logicalPos=BIDI_MIN(visualMap[start], index0); + runs[i+addedRuns].logicalStart=MAKE_INDEX_ODD_PAIR(logicalPos, + saveLevels[logicalPos]^indexOddBit); + runs[i+addedRuns].visualLimit=runs[i].visualLimit; + runs[i].visualLimit-=BIDI_ABS(j-start)+1; + insertRemove=runs[i].insertRemove&(LRM_AFTER|RLM_AFTER); + runs[i+addedRuns].insertRemove=insertRemove; + runs[i].insertRemove&=~insertRemove; + start=j+step; + addedRuns--; + } + } + if(addedRuns) { + runs[i+addedRuns]=runs[i]; + } + logicalPos=BIDI_MIN(visualMap[start], visualMap[limit]); + runs[i+addedRuns].logicalStart=MAKE_INDEX_ODD_PAIR(logicalPos, + saveLevels[logicalPos]^indexOddBit); + } + + cleanup1: + /* restore initial paraLevel */ + pBiDi->paraLevel^=1; + cleanup2: + /* restore real text */ + pBiDi->text=text; + pBiDi->length=saveLength; + pBiDi->originalLength=length; + pBiDi->direction=saveDirection; + /* the saved levels should never excess levelsSize, but we check anyway */ + if(saveLength>pBiDi->levelsSize) { + saveLength=pBiDi->levelsSize; + } + uprv_memcpy(pBiDi->levels, saveLevels, (size_t)saveLength*sizeof(UBiDiLevel)); + pBiDi->trailingWSStart=saveTrailingWSStart; + if(pBiDi->runCount>1) { + pBiDi->direction=UBIDI_MIXED; + } + cleanup3: + /* free memory for mapping table and visual text */ + uprv_free(runsOnlyMemory); + + pBiDi->reorderingMode=UBIDI_REORDER_RUNS_ONLY; +} + +/* ubidi_setPara ------------------------------------------------------------ */ + +U_CAPI void U_EXPORT2 +ubidi_setPara(UBiDi *pBiDi, const char16_t *text, int32_t length, + UBiDiLevel paraLevel, UBiDiLevel *embeddingLevels, + UErrorCode *pErrorCode) { + UBiDiDirection direction; + DirProp *dirProps; + + /* check the argument values */ + RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); + if(pBiDi==nullptr || text==nullptr || length<-1 || + (paraLevel>UBIDI_MAX_EXPLICIT_LEVEL && paraLevelreorderingMode==UBIDI_REORDER_RUNS_ONLY) { + setParaRunsOnly(pBiDi, text, length, paraLevel, pErrorCode); + return; + } + + /* initialize the UBiDi structure */ + pBiDi->pParaBiDi=nullptr; /* mark unfinished setPara */ + pBiDi->text=text; + pBiDi->length=pBiDi->originalLength=pBiDi->resultLength=length; + pBiDi->paraLevel=paraLevel; + pBiDi->direction=(UBiDiDirection)(paraLevel&1); + pBiDi->paraCount=1; + + pBiDi->dirProps=nullptr; + pBiDi->levels=nullptr; + pBiDi->runs=nullptr; + pBiDi->insertPoints.size=0; /* clean up from last call */ + pBiDi->insertPoints.confirmed=0; /* clean up from last call */ + + /* + * Save the original paraLevel if contextual; otherwise, set to 0. + */ + pBiDi->defaultParaLevel=IS_DEFAULT_LEVEL(paraLevel); + + if(length==0) { + /* + * For an empty paragraph, create a UBiDi object with the paraLevel and + * the flags and the direction set but without allocating zero-length arrays. + * There is nothing more to do. + */ + if(IS_DEFAULT_LEVEL(paraLevel)) { + pBiDi->paraLevel&=1; + pBiDi->defaultParaLevel=0; + } + pBiDi->flags=DIRPROP_FLAG_LR(paraLevel); + pBiDi->runCount=0; + pBiDi->paraCount=0; + setParaSuccess(pBiDi); /* mark successful setPara */ + return; + } + + pBiDi->runCount=-1; + + /* allocate paras memory */ + if(pBiDi->parasMemory) + pBiDi->paras=pBiDi->parasMemory; + else + pBiDi->paras=pBiDi->simpleParas; + + /* + * Get the directional properties, + * the flags bit-set, and + * determine the paragraph level if necessary. + */ + if(getDirPropsMemory(pBiDi, length)) { + pBiDi->dirProps=pBiDi->dirPropsMemory; + if(!getDirProps(pBiDi)) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + } else { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + dirProps=pBiDi->dirProps; + /* the processed length may have changed if UBIDI_OPTION_STREAMING */ + length= pBiDi->length; + pBiDi->trailingWSStart=length; /* the levels[] will reflect the WS run */ + + /* are explicit levels specified? */ + if(embeddingLevels==nullptr) { + /* no: determine explicit levels according to the (Xn) rules */\ + if(getLevelsMemory(pBiDi, length)) { + pBiDi->levels=pBiDi->levelsMemory; + direction=resolveExplicitLevels(pBiDi, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return; + } + } else { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + } else { + /* set BN for all explicit codes, check that all levels are 0 or paraLevel..UBIDI_MAX_EXPLICIT_LEVEL */ + pBiDi->levels=embeddingLevels; + direction=checkExplicitLevels(pBiDi, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return; + } + } + + /* allocate isolate memory */ + if(pBiDi->isolateCount<=SIMPLE_ISOLATES_COUNT) + pBiDi->isolates=pBiDi->simpleIsolates; + else + if((int32_t)(pBiDi->isolateCount*sizeof(Isolate))<=pBiDi->isolatesSize) + pBiDi->isolates=pBiDi->isolatesMemory; + else { + if(getInitialIsolatesMemory(pBiDi, pBiDi->isolateCount)) { + pBiDi->isolates=pBiDi->isolatesMemory; + } else { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + } + pBiDi->isolateCount=-1; /* current isolates stack entry == none */ + + /* + * The steps after (X9) in the UBiDi algorithm are performed only if + * the paragraph text has mixed directionality! + */ + pBiDi->direction=direction; + switch(direction) { + case UBIDI_LTR: + /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */ + pBiDi->trailingWSStart=0; + break; + case UBIDI_RTL: + /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */ + pBiDi->trailingWSStart=0; + break; + default: + /* + * Choose the right implicit state table + */ + switch(pBiDi->reorderingMode) { + case UBIDI_REORDER_DEFAULT: + pBiDi->pImpTabPair=&impTab_DEFAULT; + break; + case UBIDI_REORDER_NUMBERS_SPECIAL: + pBiDi->pImpTabPair=&impTab_NUMBERS_SPECIAL; + break; + case UBIDI_REORDER_GROUP_NUMBERS_WITH_R: + pBiDi->pImpTabPair=&impTab_GROUP_NUMBERS_WITH_R; + break; + case UBIDI_REORDER_INVERSE_NUMBERS_AS_L: + pBiDi->pImpTabPair=&impTab_INVERSE_NUMBERS_AS_L; + break; + case UBIDI_REORDER_INVERSE_LIKE_DIRECT: + if (pBiDi->reorderingOptions & UBIDI_OPTION_INSERT_MARKS) { + pBiDi->pImpTabPair=&impTab_INVERSE_LIKE_DIRECT_WITH_MARKS; + } else { + pBiDi->pImpTabPair=&impTab_INVERSE_LIKE_DIRECT; + } + break; + case UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL: + if (pBiDi->reorderingOptions & UBIDI_OPTION_INSERT_MARKS) { + pBiDi->pImpTabPair=&impTab_INVERSE_FOR_NUMBERS_SPECIAL_WITH_MARKS; + } else { + pBiDi->pImpTabPair=&impTab_INVERSE_FOR_NUMBERS_SPECIAL; + } + break; + default: + /* we should never get here */ + UPRV_UNREACHABLE_EXIT; + } + /* + * If there are no external levels specified and there + * are no significant explicit level codes in the text, + * then we can treat the entire paragraph as one run. + * Otherwise, we need to perform the following rules on runs of + * the text with the same embedding levels. (X10) + * "Significant" explicit level codes are ones that actually + * affect non-BN characters. + * Examples for "insignificant" ones are empty embeddings + * LRE-PDF, LRE-RLE-PDF-PDF, etc. + */ + if(embeddingLevels==nullptr && pBiDi->paraCount<=1 && + !(pBiDi->flags&DIRPROP_FLAG_MULTI_RUNS)) { + resolveImplicitLevels(pBiDi, 0, length, + GET_LR_FROM_LEVEL(GET_PARALEVEL(pBiDi, 0)), + GET_LR_FROM_LEVEL(GET_PARALEVEL(pBiDi, length-1))); + } else { + /* sor, eor: start and end types of same-level-run */ + UBiDiLevel *levels=pBiDi->levels; + int32_t start, limit=0; + UBiDiLevel level, nextLevel; + DirProp sor, eor; + + /* determine the first sor and set eor to it because of the loop body (sor=eor there) */ + level=GET_PARALEVEL(pBiDi, 0); + nextLevel=levels[0]; + if(level0) && (dirProps[start-1]==B)) { + /* except if this is a new paragraph, then set sor = para level */ + sor=GET_LR_FROM_LEVEL(GET_PARALEVEL(pBiDi, start)); + } else { + sor=eor; + } + + /* search for the limit of this run */ + while((++limitinsertPoints.errorCode)) + { + *pErrorCode=pBiDi->insertPoints.errorCode; + return; + } + /* reset the embedding levels for some non-graphic characters (L1), (X9) */ + adjustWSLevels(pBiDi); + break; + } + /* add RLM for inverse Bidi with contextual orientation resolving + * to RTL which would not round-trip otherwise + */ + if((pBiDi->defaultParaLevel>0) && + (pBiDi->reorderingOptions & UBIDI_OPTION_INSERT_MARKS) && + ((pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_LIKE_DIRECT) || + (pBiDi->reorderingMode==UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL))) { + int32_t i, j, start, last; + UBiDiLevel level; + DirProp dirProp; + for(i=0; iparaCount; i++) { + last=(pBiDi->paras[i].limit)-1; + level= static_cast(pBiDi->paras[i].level); + if(level==0) + continue; /* LTR paragraph */ + start= i==0 ? 0 : pBiDi->paras[i-1].limit; + for(j=last; j>=start; j--) { + dirProp=dirProps[j]; + if(dirProp==L) { + if(jreorderingOptions & UBIDI_OPTION_REMOVE_CONTROLS) { + pBiDi->resultLength -= pBiDi->controlCount; + } else { + pBiDi->resultLength += pBiDi->insertPoints.size; + } + setParaSuccess(pBiDi); /* mark successful setPara */ +} + +U_CAPI void U_EXPORT2 +ubidi_orderParagraphsLTR(UBiDi *pBiDi, UBool orderParagraphsLTR) { + if(pBiDi!=nullptr) { + pBiDi->orderParagraphsLTR=orderParagraphsLTR; + } +} + +U_CAPI UBool U_EXPORT2 +ubidi_isOrderParagraphsLTR(UBiDi *pBiDi) { + if(pBiDi!=nullptr) { + return pBiDi->orderParagraphsLTR; + } else { + return false; + } +} + +U_CAPI UBiDiDirection U_EXPORT2 +ubidi_getDirection(const UBiDi *pBiDi) { + if(IS_VALID_PARA_OR_LINE(pBiDi)) { + return pBiDi->direction; + } else { + return UBIDI_LTR; + } +} + +U_CAPI const char16_t * U_EXPORT2 +ubidi_getText(const UBiDi *pBiDi) { + if(IS_VALID_PARA_OR_LINE(pBiDi)) { + return pBiDi->text; + } else { + return nullptr; + } +} + +U_CAPI int32_t U_EXPORT2 +ubidi_getLength(const UBiDi *pBiDi) { + if(IS_VALID_PARA_OR_LINE(pBiDi)) { + return pBiDi->originalLength; + } else { + return 0; + } +} + +U_CAPI int32_t U_EXPORT2 +ubidi_getProcessedLength(const UBiDi *pBiDi) { + if(IS_VALID_PARA_OR_LINE(pBiDi)) { + return pBiDi->length; + } else { + return 0; + } +} + +U_CAPI int32_t U_EXPORT2 +ubidi_getResultLength(const UBiDi *pBiDi) { + if(IS_VALID_PARA_OR_LINE(pBiDi)) { + return pBiDi->resultLength; + } else { + return 0; + } +} + +/* paragraphs API functions ------------------------------------------------- */ + +U_CAPI UBiDiLevel U_EXPORT2 +ubidi_getParaLevel(const UBiDi *pBiDi) { + if(IS_VALID_PARA_OR_LINE(pBiDi)) { + return pBiDi->paraLevel; + } else { + return 0; + } +} + +U_CAPI int32_t U_EXPORT2 +ubidi_countParagraphs(UBiDi *pBiDi) { + if(!IS_VALID_PARA_OR_LINE(pBiDi)) { + return 0; + } else { + return pBiDi->paraCount; + } +} + +U_CAPI void U_EXPORT2 +ubidi_getParagraphByIndex(const UBiDi *pBiDi, int32_t paraIndex, + int32_t *pParaStart, int32_t *pParaLimit, + UBiDiLevel *pParaLevel, UErrorCode *pErrorCode) { + int32_t paraStart; + + /* check the argument values */ + RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); + RETURN_VOID_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode); + RETURN_VOID_IF_BAD_RANGE(paraIndex, 0, pBiDi->paraCount, *pErrorCode); + + pBiDi=pBiDi->pParaBiDi; /* get Para object if Line object */ + if(paraIndex) { + paraStart=pBiDi->paras[paraIndex-1].limit; + } else { + paraStart=0; + } + if(pParaStart!=nullptr) { + *pParaStart=paraStart; + } + if(pParaLimit!=nullptr) { + *pParaLimit=pBiDi->paras[paraIndex].limit; + } + if(pParaLevel!=nullptr) { + *pParaLevel=GET_PARALEVEL(pBiDi, paraStart); + } +} + +U_CAPI int32_t U_EXPORT2 +ubidi_getParagraph(const UBiDi *pBiDi, int32_t charIndex, + int32_t *pParaStart, int32_t *pParaLimit, + UBiDiLevel *pParaLevel, UErrorCode *pErrorCode) { + int32_t paraIndex; + + /* check the argument values */ + /* pErrorCode will be checked by the call to ubidi_getParagraphByIndex */ + RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1); + RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1); + pBiDi=pBiDi->pParaBiDi; /* get Para object if Line object */ + RETURN_IF_BAD_RANGE(charIndex, 0, pBiDi->length, *pErrorCode, -1); + + for(paraIndex=0; charIndex>=pBiDi->paras[paraIndex].limit; paraIndex++); + ubidi_getParagraphByIndex(pBiDi, paraIndex, pParaStart, pParaLimit, pParaLevel, pErrorCode); + return paraIndex; +} + +U_CAPI void U_EXPORT2 +ubidi_setClassCallback(UBiDi *pBiDi, UBiDiClassCallback *newFn, + const void *newContext, UBiDiClassCallback **oldFn, + const void **oldContext, UErrorCode *pErrorCode) +{ + RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); + if(pBiDi==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if( oldFn ) + { + *oldFn = pBiDi->fnClassCallback; + } + if( oldContext ) + { + *oldContext = pBiDi->coClassCallback; + } + pBiDi->fnClassCallback = newFn; + pBiDi->coClassCallback = newContext; +} + +U_CAPI void U_EXPORT2 +ubidi_getClassCallback(UBiDi *pBiDi, UBiDiClassCallback **fn, const void **context) +{ + if(pBiDi==nullptr) { + return; + } + if( fn ) + { + *fn = pBiDi->fnClassCallback; + } + if( context ) + { + *context = pBiDi->coClassCallback; + } +} + +U_CAPI UCharDirection U_EXPORT2 +ubidi_getCustomizedClass(UBiDi *pBiDi, UChar32 c) +{ + UCharDirection dir; + + if( pBiDi->fnClassCallback == nullptr || + (dir = (*pBiDi->fnClassCallback)(pBiDi->coClassCallback, c)) == U_BIDI_CLASS_DEFAULT ) + { + dir = ubidi_getClass(c); + } + if(dir >= U_CHAR_DIRECTION_COUNT) { + dir = (UCharDirection)ON; + } + return dir; +} diff --git a/deps/icu-small/source/common/ubidi_props.cpp b/deps/icu-small/source/common/ubidi_props.cpp index 3ba58f7af99cc7..8c37cff398e530 100644 --- a/deps/icu-small/source/common/ubidi_props.cpp +++ b/deps/icu-small/source/common/ubidi_props.cpp @@ -1,254 +1,254 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2004-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: ubidi_props.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004dec30 -* created by: Markus W. Scherer -* -* Low-level Unicode bidi/shaping properties access. -*/ - -#include "unicode/utypes.h" -#include "unicode/uset.h" -#include "unicode/udata.h" /* UDataInfo */ -#include "ucmndata.h" /* DataHeader */ -#include "udatamem.h" -#include "uassert.h" -#include "cmemory.h" -#include "utrie2.h" -#include "ubidi_props.h" -#include "ucln_cmn.h" - -struct UBiDiProps { - UDataMemory *mem; - const int32_t *indexes; - const uint32_t *mirrors; - const uint8_t *jgArray; - const uint8_t *jgArray2; - - UTrie2 trie; - uint8_t formatVersion[4]; -}; - -/* ubidi_props_data.h is machine-generated by genbidi --csource */ -#define INCLUDED_FROM_UBIDI_PROPS_C -#include "ubidi_props_data.h" - -/* set of property starts for UnicodeSet ------------------------------------ */ - -static UBool U_CALLCONV -_enumPropertyStartsRange(const void *context, UChar32 start, UChar32 end, uint32_t value) { - (void)end; - (void)value; - /* add the start code point to the USet */ - const USetAdder *sa=(const USetAdder *)context; - sa->add(sa->set, start); - return true; -} - -U_CFUNC void -ubidi_addPropertyStarts(const USetAdder *sa, UErrorCode *pErrorCode) { - int32_t i, length; - UChar32 c, start, limit; - - const uint8_t *jgArray; - uint8_t prev, jg; - - if(U_FAILURE(*pErrorCode)) { - return; - } - - /* add the start code point of each same-value range of the trie */ - utrie2_enum(&ubidi_props_singleton.trie, NULL, _enumPropertyStartsRange, sa); - - /* add the code points from the bidi mirroring table */ - length=ubidi_props_singleton.indexes[UBIDI_IX_MIRROR_LENGTH]; - for(i=0; iaddRange(sa->set, c, c+1); - } - - /* add the code points from the Joining_Group array where the value changes */ - start=ubidi_props_singleton.indexes[UBIDI_IX_JG_START]; - limit=ubidi_props_singleton.indexes[UBIDI_IX_JG_LIMIT]; - jgArray=ubidi_props_singleton.jgArray; - for(;;) { - prev=0; - while(startadd(sa->set, start); - prev=jg; - } - ++start; - } - if(prev!=0) { - /* add the limit code point if the last value was not 0 (it is now start==limit) */ - sa->add(sa->set, limit); - } - if(limit==ubidi_props_singleton.indexes[UBIDI_IX_JG_LIMIT]) { - /* switch to the second Joining_Group range */ - start=ubidi_props_singleton.indexes[UBIDI_IX_JG_START2]; - limit=ubidi_props_singleton.indexes[UBIDI_IX_JG_LIMIT2]; - jgArray=ubidi_props_singleton.jgArray2; - } else { - break; - } - } - - /* add code points with hardcoded properties, plus the ones following them */ - - /* (none right now) */ -} - -/* property access functions ------------------------------------------------ */ - -U_CFUNC int32_t -ubidi_getMaxValue(UProperty which) { - int32_t max=ubidi_props_singleton.indexes[UBIDI_MAX_VALUES_INDEX]; - switch(which) { - case UCHAR_BIDI_CLASS: - return (max&UBIDI_CLASS_MASK); - case UCHAR_JOINING_GROUP: - return (max&UBIDI_MAX_JG_MASK)>>UBIDI_MAX_JG_SHIFT; - case UCHAR_JOINING_TYPE: - return (max&UBIDI_JT_MASK)>>UBIDI_JT_SHIFT; - case UCHAR_BIDI_PAIRED_BRACKET_TYPE: - return (max&UBIDI_BPT_MASK)>>UBIDI_BPT_SHIFT; - default: - return -1; /* undefined */ - } -} - -U_CAPI UCharDirection -ubidi_getClass(UChar32 c) { - uint16_t props=UTRIE2_GET16(&ubidi_props_singleton.trie, c); - return (UCharDirection)UBIDI_GET_CLASS(props); -} - -U_CFUNC UBool -ubidi_isMirrored(UChar32 c) { - uint16_t props=UTRIE2_GET16(&ubidi_props_singleton.trie, c); - return (UBool)UBIDI_GET_FLAG(props, UBIDI_IS_MIRRORED_SHIFT); -} - -static UChar32 -getMirror(UChar32 c, uint16_t props) { - int32_t delta=UBIDI_GET_MIRROR_DELTA(props); - if(delta!=UBIDI_ESC_MIRROR_DELTA) { - return c+delta; - } else { - /* look for mirror code point in the mirrors[] table */ - const uint32_t *mirrors; - uint32_t m; - int32_t i, length; - UChar32 c2; - - mirrors=ubidi_props_singleton.mirrors; - length=ubidi_props_singleton.indexes[UBIDI_IX_MIRROR_LENGTH]; - - /* linear search */ - for(i=0; i>UBIDI_JT_SHIFT); -} - -U_CFUNC UJoiningGroup -ubidi_getJoiningGroup(UChar32 c) { - UChar32 start, limit; - - start=ubidi_props_singleton.indexes[UBIDI_IX_JG_START]; - limit=ubidi_props_singleton.indexes[UBIDI_IX_JG_LIMIT]; - if(start<=c && c>UBIDI_BPT_SHIFT); -} - -U_CFUNC UChar32 -ubidi_getPairedBracket(UChar32 c) { - uint16_t props=UTRIE2_GET16(&ubidi_props_singleton.trie, c); - if((props&UBIDI_BPT_MASK)==0) { - return c; - } else { - return getMirror(c, props); - } -} - -/* public API (see uchar.h) ------------------------------------------------- */ - -U_CFUNC UCharDirection -u_charDirection(UChar32 c) { - return ubidi_getClass(c); -} - -U_CFUNC UBool -u_isMirrored(UChar32 c) { - return ubidi_isMirrored(c); -} - -U_CFUNC UChar32 -u_charMirror(UChar32 c) { - return ubidi_getMirror(c); -} - -U_CAPI UChar32 U_EXPORT2 -u_getBidiPairedBracket(UChar32 c) { - return ubidi_getPairedBracket(c); -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2004-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: ubidi_props.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004dec30 +* created by: Markus W. Scherer +* +* Low-level Unicode bidi/shaping properties access. +*/ + +#include "unicode/utypes.h" +#include "unicode/uset.h" +#include "unicode/udata.h" /* UDataInfo */ +#include "ucmndata.h" /* DataHeader */ +#include "udatamem.h" +#include "uassert.h" +#include "cmemory.h" +#include "utrie2.h" +#include "ubidi_props.h" +#include "ucln_cmn.h" + +struct UBiDiProps { + UDataMemory *mem; + const int32_t *indexes; + const uint32_t *mirrors; + const uint8_t *jgArray; + const uint8_t *jgArray2; + + UTrie2 trie; + uint8_t formatVersion[4]; +}; + +/* ubidi_props_data.h is machine-generated by genbidi --csource */ +#define INCLUDED_FROM_UBIDI_PROPS_C +#include "ubidi_props_data.h" + +/* set of property starts for UnicodeSet ------------------------------------ */ + +static UBool U_CALLCONV +_enumPropertyStartsRange(const void *context, UChar32 start, UChar32 end, uint32_t value) { + (void)end; + (void)value; + /* add the start code point to the USet */ + const USetAdder *sa=(const USetAdder *)context; + sa->add(sa->set, start); + return true; +} + +U_CFUNC void +ubidi_addPropertyStarts(const USetAdder *sa, UErrorCode *pErrorCode) { + int32_t i, length; + UChar32 c, start, limit; + + const uint8_t *jgArray; + uint8_t prev, jg; + + if(U_FAILURE(*pErrorCode)) { + return; + } + + /* add the start code point of each same-value range of the trie */ + utrie2_enum(&ubidi_props_singleton.trie, nullptr, _enumPropertyStartsRange, sa); + + /* add the code points from the bidi mirroring table */ + length=ubidi_props_singleton.indexes[UBIDI_IX_MIRROR_LENGTH]; + for(i=0; iaddRange(sa->set, c, c+1); + } + + /* add the code points from the Joining_Group array where the value changes */ + start=ubidi_props_singleton.indexes[UBIDI_IX_JG_START]; + limit=ubidi_props_singleton.indexes[UBIDI_IX_JG_LIMIT]; + jgArray=ubidi_props_singleton.jgArray; + for(;;) { + prev=0; + while(startadd(sa->set, start); + prev=jg; + } + ++start; + } + if(prev!=0) { + /* add the limit code point if the last value was not 0 (it is now start==limit) */ + sa->add(sa->set, limit); + } + if(limit==ubidi_props_singleton.indexes[UBIDI_IX_JG_LIMIT]) { + /* switch to the second Joining_Group range */ + start=ubidi_props_singleton.indexes[UBIDI_IX_JG_START2]; + limit=ubidi_props_singleton.indexes[UBIDI_IX_JG_LIMIT2]; + jgArray=ubidi_props_singleton.jgArray2; + } else { + break; + } + } + + /* add code points with hardcoded properties, plus the ones following them */ + + /* (none right now) */ +} + +/* property access functions ------------------------------------------------ */ + +U_CFUNC int32_t +ubidi_getMaxValue(UProperty which) { + int32_t max=ubidi_props_singleton.indexes[UBIDI_MAX_VALUES_INDEX]; + switch(which) { + case UCHAR_BIDI_CLASS: + return (max&UBIDI_CLASS_MASK); + case UCHAR_JOINING_GROUP: + return (max&UBIDI_MAX_JG_MASK)>>UBIDI_MAX_JG_SHIFT; + case UCHAR_JOINING_TYPE: + return (max&UBIDI_JT_MASK)>>UBIDI_JT_SHIFT; + case UCHAR_BIDI_PAIRED_BRACKET_TYPE: + return (max&UBIDI_BPT_MASK)>>UBIDI_BPT_SHIFT; + default: + return -1; /* undefined */ + } +} + +U_CAPI UCharDirection +ubidi_getClass(UChar32 c) { + uint16_t props=UTRIE2_GET16(&ubidi_props_singleton.trie, c); + return (UCharDirection)UBIDI_GET_CLASS(props); +} + +U_CFUNC UBool +ubidi_isMirrored(UChar32 c) { + uint16_t props=UTRIE2_GET16(&ubidi_props_singleton.trie, c); + return (UBool)UBIDI_GET_FLAG(props, UBIDI_IS_MIRRORED_SHIFT); +} + +static UChar32 +getMirror(UChar32 c, uint16_t props) { + int32_t delta=UBIDI_GET_MIRROR_DELTA(props); + if(delta!=UBIDI_ESC_MIRROR_DELTA) { + return c+delta; + } else { + /* look for mirror code point in the mirrors[] table */ + const uint32_t *mirrors; + uint32_t m; + int32_t i, length; + UChar32 c2; + + mirrors=ubidi_props_singleton.mirrors; + length=ubidi_props_singleton.indexes[UBIDI_IX_MIRROR_LENGTH]; + + /* linear search */ + for(i=0; i>UBIDI_JT_SHIFT); +} + +U_CFUNC UJoiningGroup +ubidi_getJoiningGroup(UChar32 c) { + UChar32 start, limit; + + start=ubidi_props_singleton.indexes[UBIDI_IX_JG_START]; + limit=ubidi_props_singleton.indexes[UBIDI_IX_JG_LIMIT]; + if(start<=c && c>UBIDI_BPT_SHIFT); +} + +U_CFUNC UChar32 +ubidi_getPairedBracket(UChar32 c) { + uint16_t props=UTRIE2_GET16(&ubidi_props_singleton.trie, c); + if((props&UBIDI_BPT_MASK)==0) { + return c; + } else { + return getMirror(c, props); + } +} + +/* public API (see uchar.h) ------------------------------------------------- */ + +U_CFUNC UCharDirection +u_charDirection(UChar32 c) { + return ubidi_getClass(c); +} + +U_CFUNC UBool +u_isMirrored(UChar32 c) { + return ubidi_isMirrored(c); +} + +U_CFUNC UChar32 +u_charMirror(UChar32 c) { + return ubidi_getMirror(c); +} + +U_CAPI UChar32 U_EXPORT2 +u_getBidiPairedBracket(UChar32 c) { + return ubidi_getPairedBracket(c); +} diff --git a/deps/icu-small/source/common/ubidi_props.h b/deps/icu-small/source/common/ubidi_props.h index 698ee9c52bd6cb..d8bb2c3388ae6b 100644 --- a/deps/icu-small/source/common/ubidi_props.h +++ b/deps/icu-small/source/common/ubidi_props.h @@ -1,148 +1,148 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2004-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: ubidi_props.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004dec30 -* created by: Markus W. Scherer -* -* Low-level Unicode bidi/shaping properties access. -*/ - -#ifndef __UBIDI_PROPS_H__ -#define __UBIDI_PROPS_H__ - -#include "unicode/utypes.h" -#include "unicode/uset.h" -#include "putilimp.h" -#include "uset_imp.h" -#include "udataswp.h" - -U_CDECL_BEGIN - -/* library API -------------------------------------------------------------- */ - -U_CFUNC void -ubidi_addPropertyStarts(const USetAdder *sa, UErrorCode *pErrorCode); - -/* property access functions */ - -U_CFUNC int32_t -ubidi_getMaxValue(UProperty which); - -U_CAPI UCharDirection -ubidi_getClass(UChar32 c); - -U_CFUNC UBool -ubidi_isMirrored(UChar32 c); - -U_CFUNC UChar32 -ubidi_getMirror(UChar32 c); - -U_CFUNC UBool -ubidi_isBidiControl(UChar32 c); - -U_CFUNC UBool -ubidi_isJoinControl(UChar32 c); - -U_CFUNC UJoiningType -ubidi_getJoiningType(UChar32 c); - -U_CFUNC UJoiningGroup -ubidi_getJoiningGroup(UChar32 c); - -U_CFUNC UBidiPairedBracketType -ubidi_getPairedBracketType(UChar32 c); - -U_CFUNC UChar32 -ubidi_getPairedBracket(UChar32 c); - -/* file definitions --------------------------------------------------------- */ - -#define UBIDI_DATA_NAME "ubidi" -#define UBIDI_DATA_TYPE "icu" - -/* format "BiDi" */ -#define UBIDI_FMT_0 0x42 -#define UBIDI_FMT_1 0x69 -#define UBIDI_FMT_2 0x44 -#define UBIDI_FMT_3 0x69 - -/* indexes into indexes[] */ -enum { - UBIDI_IX_INDEX_TOP, - UBIDI_IX_LENGTH, - UBIDI_IX_TRIE_SIZE, - UBIDI_IX_MIRROR_LENGTH, - - UBIDI_IX_JG_START, - UBIDI_IX_JG_LIMIT, - UBIDI_IX_JG_START2, /* new in format version 2.2, ICU 54 */ - UBIDI_IX_JG_LIMIT2, - - UBIDI_MAX_VALUES_INDEX=15, - UBIDI_IX_TOP=16 -}; - -/* definitions for 16-bit bidi/shaping properties word ---------------------- */ - -enum { - /* UBIDI_CLASS_SHIFT=0, */ /* bidi class: 5 bits (4..0) */ - UBIDI_JT_SHIFT=5, /* joining type: 3 bits (7..5) */ - - UBIDI_BPT_SHIFT=8, /* Bidi_Paired_Bracket_Type(bpt): 2 bits (9..8) */ - - UBIDI_JOIN_CONTROL_SHIFT=10, - UBIDI_BIDI_CONTROL_SHIFT=11, - - UBIDI_IS_MIRRORED_SHIFT=12, /* 'is mirrored' */ - UBIDI_MIRROR_DELTA_SHIFT=13, /* bidi mirroring delta: 3 bits (15..13) */ - - UBIDI_MAX_JG_SHIFT=16 /* max JG value in indexes[UBIDI_MAX_VALUES_INDEX] bits 23..16 */ -}; - -#define UBIDI_CLASS_MASK 0x0000001f -#define UBIDI_JT_MASK 0x000000e0 -#define UBIDI_BPT_MASK 0x00000300 - -#define UBIDI_MAX_JG_MASK 0x00ff0000 - -#define UBIDI_GET_CLASS(props) ((props)&UBIDI_CLASS_MASK) -#define UBIDI_GET_FLAG(props, shift) (((props)>>(shift))&1) - -#if U_SIGNED_RIGHT_SHIFT_IS_ARITHMETIC -# define UBIDI_GET_MIRROR_DELTA(props) ((int16_t)(props)>>UBIDI_MIRROR_DELTA_SHIFT) -#else -# define UBIDI_GET_MIRROR_DELTA(props) (int16_t)(((props)&0x8000) ? (((props)>>UBIDI_MIRROR_DELTA_SHIFT)|0xe000) : ((props)>>UBIDI_MIRROR_DELTA_SHIFT)) -#endif - -enum { - UBIDI_ESC_MIRROR_DELTA=-4, - UBIDI_MIN_MIRROR_DELTA=-3, - UBIDI_MAX_MIRROR_DELTA=3 -}; - -/* definitions for 32-bit mirror table entry -------------------------------- */ - -enum { - /* the source Unicode code point takes 21 bits (20..0) */ - UBIDI_MIRROR_INDEX_SHIFT=21, - UBIDI_MAX_MIRROR_INDEX=0x7ff -}; - -#define UBIDI_GET_MIRROR_CODE_POINT(m) (UChar32)((m)&0x1fffff) - -#define UBIDI_GET_MIRROR_INDEX(m) ((m)>>UBIDI_MIRROR_INDEX_SHIFT) - -U_CDECL_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2004-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: ubidi_props.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004dec30 +* created by: Markus W. Scherer +* +* Low-level Unicode bidi/shaping properties access. +*/ + +#ifndef __UBIDI_PROPS_H__ +#define __UBIDI_PROPS_H__ + +#include "unicode/utypes.h" +#include "unicode/uset.h" +#include "putilimp.h" +#include "uset_imp.h" +#include "udataswp.h" + +U_CDECL_BEGIN + +/* library API -------------------------------------------------------------- */ + +U_CFUNC void +ubidi_addPropertyStarts(const USetAdder *sa, UErrorCode *pErrorCode); + +/* property access functions */ + +U_CFUNC int32_t +ubidi_getMaxValue(UProperty which); + +U_CAPI UCharDirection +ubidi_getClass(UChar32 c); + +U_CFUNC UBool +ubidi_isMirrored(UChar32 c); + +U_CFUNC UChar32 +ubidi_getMirror(UChar32 c); + +U_CFUNC UBool +ubidi_isBidiControl(UChar32 c); + +U_CFUNC UBool +ubidi_isJoinControl(UChar32 c); + +U_CFUNC UJoiningType +ubidi_getJoiningType(UChar32 c); + +U_CFUNC UJoiningGroup +ubidi_getJoiningGroup(UChar32 c); + +U_CFUNC UBidiPairedBracketType +ubidi_getPairedBracketType(UChar32 c); + +U_CFUNC UChar32 +ubidi_getPairedBracket(UChar32 c); + +/* file definitions --------------------------------------------------------- */ + +#define UBIDI_DATA_NAME "ubidi" +#define UBIDI_DATA_TYPE "icu" + +/* format "BiDi" */ +#define UBIDI_FMT_0 0x42 +#define UBIDI_FMT_1 0x69 +#define UBIDI_FMT_2 0x44 +#define UBIDI_FMT_3 0x69 + +/* indexes into indexes[] */ +enum { + UBIDI_IX_INDEX_TOP, + UBIDI_IX_LENGTH, + UBIDI_IX_TRIE_SIZE, + UBIDI_IX_MIRROR_LENGTH, + + UBIDI_IX_JG_START, + UBIDI_IX_JG_LIMIT, + UBIDI_IX_JG_START2, /* new in format version 2.2, ICU 54 */ + UBIDI_IX_JG_LIMIT2, + + UBIDI_MAX_VALUES_INDEX=15, + UBIDI_IX_TOP=16 +}; + +/* definitions for 16-bit bidi/shaping properties word ---------------------- */ + +enum { + /* UBIDI_CLASS_SHIFT=0, */ /* bidi class: 5 bits (4..0) */ + UBIDI_JT_SHIFT=5, /* joining type: 3 bits (7..5) */ + + UBIDI_BPT_SHIFT=8, /* Bidi_Paired_Bracket_Type(bpt): 2 bits (9..8) */ + + UBIDI_JOIN_CONTROL_SHIFT=10, + UBIDI_BIDI_CONTROL_SHIFT=11, + + UBIDI_IS_MIRRORED_SHIFT=12, /* 'is mirrored' */ + UBIDI_MIRROR_DELTA_SHIFT=13, /* bidi mirroring delta: 3 bits (15..13) */ + + UBIDI_MAX_JG_SHIFT=16 /* max JG value in indexes[UBIDI_MAX_VALUES_INDEX] bits 23..16 */ +}; + +#define UBIDI_CLASS_MASK 0x0000001f +#define UBIDI_JT_MASK 0x000000e0 +#define UBIDI_BPT_MASK 0x00000300 + +#define UBIDI_MAX_JG_MASK 0x00ff0000 + +#define UBIDI_GET_CLASS(props) ((props)&UBIDI_CLASS_MASK) +#define UBIDI_GET_FLAG(props, shift) (((props)>>(shift))&1) + +#if U_SIGNED_RIGHT_SHIFT_IS_ARITHMETIC +# define UBIDI_GET_MIRROR_DELTA(props) ((int16_t)(props)>>UBIDI_MIRROR_DELTA_SHIFT) +#else +# define UBIDI_GET_MIRROR_DELTA(props) (int16_t)(((props)&0x8000) ? (((props)>>UBIDI_MIRROR_DELTA_SHIFT)|0xe000) : ((props)>>UBIDI_MIRROR_DELTA_SHIFT)) +#endif + +enum { + UBIDI_ESC_MIRROR_DELTA=-4, + UBIDI_MIN_MIRROR_DELTA=-3, + UBIDI_MAX_MIRROR_DELTA=3 +}; + +/* definitions for 32-bit mirror table entry -------------------------------- */ + +enum { + /* the source Unicode code point takes 21 bits (20..0) */ + UBIDI_MIRROR_INDEX_SHIFT=21, + UBIDI_MAX_MIRROR_INDEX=0x7ff +}; + +#define UBIDI_GET_MIRROR_CODE_POINT(m) (UChar32)((m)&0x1fffff) + +#define UBIDI_GET_MIRROR_INDEX(m) ((m)>>UBIDI_MIRROR_INDEX_SHIFT) + +U_CDECL_END + +#endif diff --git a/deps/icu-small/source/common/ubidi_props_data.h b/deps/icu-small/source/common/ubidi_props_data.h index 01fcc968cb8740..e632c927735b1b 100644 --- a/deps/icu-small/source/common/ubidi_props_data.h +++ b/deps/icu-small/source/common/ubidi_props_data.h @@ -1,950 +1,950 @@ -// Copyright (C) 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// Copyright (C) 1999-2016, International Business Machines -// Corporation and others. All Rights Reserved. -// -// file name: ubidi_props_data.h -// -// machine-generated by: icu/tools/unicode/c/genprops/bidipropsbuilder.cpp - -#ifdef INCLUDED_FROM_UBIDI_PROPS_C - -static const UVersionInfo ubidi_props_dataVersion={0xf,0,0,0}; - -static const int32_t ubidi_props_indexes[UBIDI_IX_TOP]={0x10,0x6bc0,0x65d0,0x28,0x620,0x8cc,0x10ac0,0x10d24,0,0,0,0,0,0,0,0x6702b6}; - -static const uint16_t ubidi_props_trieIndex[13024]={ -0x387,0x38f,0x397,0x39f,0x3b7,0x3bf,0x3c7,0x3cf,0x3a7,0x3af,0x3a7,0x3af,0x3a7,0x3af,0x3a7,0x3af, -0x3a7,0x3af,0x3a7,0x3af,0x3d5,0x3dd,0x3e5,0x3ed,0x3f5,0x3fd,0x3f9,0x401,0x409,0x411,0x40c,0x414, -0x3a7,0x3af,0x3a7,0x3af,0x41c,0x424,0x3a7,0x3af,0x3a7,0x3af,0x3a7,0x3af,0x42a,0x432,0x43a,0x442, -0x44a,0x452,0x45a,0x462,0x468,0x470,0x478,0x480,0x488,0x490,0x496,0x49e,0x4a6,0x4ae,0x4b6,0x4be, -0x4ca,0x4c6,0x4d2,0x4da,0x4e2,0x4f2,0x4f9,0x4ea,0x501,0x503,0x50b,0x513,0x51b,0x51c,0x524,0x52c, -0x534,0x51c,0x53c,0x541,0x534,0x51c,0x549,0x551,0x51b,0x559,0x561,0x513,0x569,0x3a7,0x571,0x575, -0x57d,0x57f,0x587,0x58f,0x51b,0x597,0x59f,0x513,0x41e,0x5a3,0x524,0x513,0x51b,0x3a7,0x5ab,0x3a7, -0x3a7,0x5b1,0x5b9,0x3a7,0x3a7,0x5bd,0x5c5,0x3a7,0x5c9,0x5d0,0x3a7,0x5d8,0x5e0,0x5e7,0x568,0x3a7, -0x3a7,0x5ef,0x5f7,0x5ff,0x607,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x60f,0x3a7,0x617,0x3a7,0x3a7,0x3a7, -0x61f,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x627,0x3a7,0x3a7,0x3a7,0x62f,0x528,0x528,0x528,0x3a7,0x635,0x63d,0x617, -0x653,0x645,0x645,0x65b,0x662,0x64b,0x3a7,0x3a7,0x3a7,0x66a,0x672,0x3a7,0x3a7,0x3a7,0x674,0x67c, -0x684,0x3a7,0x68b,0x693,0x3a7,0x69b,0x4fe,0x3a7,0x558,0x6a3,0x569,0x6ab,0x41e,0x6b3,0x3a7,0x6ba, -0x3a7,0x6bf,0x3a7,0x3a7,0x3a7,0x3a7,0x6c5,0x6cd,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3f5,0x3f5, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x6d4,0x6dc,0x6e0, -0x6f8,0x6fe,0x6e8,0x6f0,0x706,0x70e,0x712,0x5ea,0x71a,0x722,0x72a,0x3a7,0x732,0x67c,0x67c,0x67c, -0x742,0x74a,0x752,0x75a,0x75f,0x767,0x76f,0x73a,0x777,0x77f,0x3a7,0x785,0x78c,0x67c,0x67c,0x67c, -0x67c,0x595,0x792,0x67c,0x79a,0x3a7,0x3a7,0x679,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c, -0x67c,0x67c,0x67c,0x67c,0x67c,0x7a2,0x67c,0x67c,0x67c,0x67c,0x67c,0x7a8,0x67c,0x67c,0x7b0,0x7b8, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x67c,0x67c,0x67c,0x67c,0x7c8,0x7d0,0x7d8,0x7c0, -0x7e8,0x7f0,0x7f8,0x7ff,0x806,0x80e,0x812,0x7e0,0x67c,0x67c,0x67c,0x81a,0x820,0x67c,0x67c,0x826, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x82e,0x3a7,0x3a7,0x3a7,0x836,0x3a7,0x3a7,0x3a7,0x3f5, -0x83e,0x846,0x849,0x3a7,0x851,0x67c,0x67c,0x67f,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x858,0x85e, -0x86e,0x866,0x3a7,0x3a7,0x876,0x61f,0x3a7,0x3ce,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x67c,0x835, -0x3dc,0x3a7,0x87e,0x886,0x3a7,0x88e,0x896,0x3a7,0x3a7,0x3a7,0x3a7,0x89a,0x3a7,0x3a7,0x674,0x3cd, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x67c,0x67c, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x87e,0x67c,0x595,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x8a1,0x3a7,0x3a7,0x8a6,0x8ae,0x3a7,0x3a7,0x5cb,0x67c,0x673,0x3a7,0x3a7,0x8b6,0x3a7,0x3a7,0x3a7, -0x8be,0x8c5,0x645,0x8cd,0x3a7,0x3a7,0x5a1,0x8d5,0x3a7,0x8dd,0x8e4,0x3a7,0x501,0x8e9,0x3a7,0x51a, -0x3a7,0x8f1,0x8f9,0x51c,0x3a7,0x8fd,0x51b,0x905,0x3a7,0x3a7,0x3a7,0x90b,0x3a7,0x3a7,0x3a7,0x912, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x926,0x91a,0x91e,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6, -0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x92e,0x936,0x4a6,0x4a6,0x4a6,0x93b,0x93f, -0x947,0x94f,0x953,0x95b,0x4a6,0x4a6,0x4a6,0x95f,0x967,0x397,0x96f,0x977,0x3a7,0x3a7,0x3a7,0x97f, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0xe9c,0xe9c,0xedc,0xf1c,0xe9c,0xe9c,0xe9c,0xe9c,0xe9c,0xe9c,0xf54,0xf94,0xfd4,0xfe4,0x1024,0x1030, -0xe9c,0xe9c,0x1070,0xe9c,0xe9c,0xe9c,0x10a8,0x10e8,0x1128,0x1168,0x11a0,0x11e0,0x1220,0x1258,0x1298,0x12d8, -0xa40,0xa80,0xac0,0xaff,0x1a0,0x1a0,0xb3f,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xb68,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xba8,0x1a0,0xbce,0xc09,0xc49,0xc89,0xcc9,0xd09,0xd49, -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,0xd89, -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,0xd89, -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,0xd89, -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,0xd89, -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,0xd89, -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,0xd89, -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,0xd89, -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,0xd89, -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,0xd89, -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,0xd89, -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,0xd89, -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,0xd89, -0xdc9,0xdd9,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,0xd89, -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,0xd89, -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,0xd89, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x987,0x3a7,0x67c,0x67c,0x98f,0x61f,0x3a7,0x514, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x997,0x3a7,0x3a7,0x3a7,0x99e,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x9a6,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c, -0x9ae,0x9b2,0x43c,0x43c,0x43c,0x43c,0x9c2,0x9ba,0x43c,0x9ca,0x43c,0x43c,0x9d2,0x9d8,0x43c,0x43c, -0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x9e8,0x9e0,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c, -0x43c,0x43c,0x43c,0x9f0,0x43c,0x9f8,0x4a6,0xa00,0x43c,0xa08,0xa0f,0xa15,0xa1d,0xa21,0xa29,0x43c, -0x51b,0xa31,0xa38,0xa3f,0x41e,0xa47,0x569,0x3a7,0x501,0xa4e,0x3a7,0xa54,0x41e,0xa59,0xa61,0x3a7, -0x3a7,0xa66,0x51b,0x3a7,0x3a7,0x3a7,0x836,0xa6e,0x41e,0x5a3,0x57e,0xa75,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0xa31,0xa7d,0x3a7,0x3a7,0xa85,0xa8d,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xa91,0xa99,0x3a7, -0x3a7,0xaa1,0x57e,0xaa9,0x3a7,0xaaf,0x3a7,0x3a7,0x60f,0xab7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0xabc,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xac3,0xacb,0x3a7,0x3a7,0x3a7,0xace,0x57e,0xad6, -0xada,0xae2,0x3a7,0xae9,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0xaf0,0x3a7,0x3a7,0xafe,0xaf8,0x3a7,0x3a7,0x3a7,0xb06,0xb0e,0x3a7,0xb12,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x5a5,0x41e,0x99e,0xb1a,0x3a7,0x3a7,0x3a7,0xb27,0xb22,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0xb2f,0xb37,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xb3d, -0x3a7,0xb43,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0xa55,0x3a7,0xb49,0x3a7,0x3a7,0xb51,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x535,0xb59,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3f5,0xb61,0x500,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0xb69,0xb71,0xb77,0x3a7,0xb7d,0x67c,0x67c,0xb85,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x67c,0x67c,0xb8d,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xb93, -0x3a7,0xb9a,0x3a7,0xb96,0x3a7,0xb9d,0x3a7,0xba5,0xba9,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3f5,0xbb1,0x3f5,0xbb8,0xbbf,0xbc7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xbcf,0xbd7,0x3a7,0x3a7,0xa55,0x3a7,0x3a7, -0x3a7,0x3a7,0xb43,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xa81,0x3a7, -0xbdc,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0xbe4,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0xbec, -0x43c,0xbf4,0xbf4,0xbfb,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c, -0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x91e,0x4a6,0x4a6,0x43c, -0x43c,0x4a6,0x4a6,0xc03,0x43c,0x43c,0x43c,0x43c,0x43c,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6, -0xc0b,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x67c,0xc13,0x67c,0x67c,0x67f,0xc18,0xc1c, -0x858,0xc24,0x3c9,0x3a7,0xc2a,0x3a7,0xc2f,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x783,0x3a7,0x3a7,0x3a7, -0x3a7,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c, -0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0xc37, -0x98f,0x67c,0x67c,0x67c,0xc3e,0x67c,0x67c,0xc45,0xc4d,0xc13,0x67c,0xc55,0x67c,0xc5d,0xc62,0x3a7, -0x3a7,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67f,0xc6a,0xc73,0xc77,0xc7f, -0xc6f,0x67c,0x67c,0x67c,0x67c,0xc87,0x67c,0x792,0xc8f,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xc96,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, -0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xc96,0xca6,0xc9e,0xc9e,0xc9e,0xca7,0xca7,0xca7, -0xca7,0x3f5,0x3f5,0x3f5,0x3f5,0x3f5,0x3f5,0x3f5,0xcaf,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7, -0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7, -0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7, -0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7, -0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0x386,0x386,0x386,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,0,0,0,0,0,0,0,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, -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,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,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,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,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,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,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,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,0x81,0x81, -0x81,0xb1,0xb1,0xb1,1,1,1,1,0x4d,0xd,0x4d,0x4d,0x4d,0x4d,0xd,0x8d, -0x4d,0x8d,0x8d,0xd,0xd,0xd,0xd,0xd,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, -0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x2d,0x2d,0x2d,0x4d,0xd, -0xd,0x4d,0x4d,0x4d,0x4d,0x4d,0x8d,0xd,5,5,0xd,0xd,0xd,0xd,0xd,0xd, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,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,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,0x4d,0x4d,0x4d, -0x4d,0x8d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0xd,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,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, -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,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,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, -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,0xb1,0xb1,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,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,0xb1,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,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,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,0xb1,0xb1, -0xb1,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,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,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,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,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,0xb1,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,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,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,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,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,0,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,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,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,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,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,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,0x900a,0xa,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,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a, -0xf20a,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,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, -0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0,0,0,0,0xa,0,0,0,0,0,0,0, -0,0,0xb1,0xb1,0xb1,0xb1,0,0,0xa,0,0,0,0,0,0xa,0xa, -0,0,0,0,0,0xa,0xa,0xa,9,0xa,0xa,0xa,0xa,0,0,0, -0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0xa,0xa,0x310a,0xf20a,0x310a,0xf20a, -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, -0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xa,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,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,0xb1,0xb1,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,0xb1,0,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,0xb1,0,0xa,0xa,0xa,0xa, -0xb1,0,0,0,0,0,0,0,0,0,0,0,4,4,0,0, -0,0,0,0,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, -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,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,0xb1,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,0,0xa,0xa,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,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,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,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, -0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xa,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,0xa,0xa,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,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,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,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, -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,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,1, -1,1,1,1,1,1,1,1,1,1,1,0xb1,0xb1,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, -0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xb1,0xb1,0xb1, -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,0x41,0x41,0x41,0x41,0x81,0x81,0x41,0x41,0x41,0x41,0x41,0x41, -0x41,0x41,0x41,0x41,0x41,0x41,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,1,1,0x41,1,0x41,0x41,0x81,0x81,0x81,1,0x41,0x81,0x81,0x41, -0x41,0x81,0x41,0x41,1,0x41,0x81,0x81,0x41,1,1,1,1,0x81,0x41,0x61, -1,1,1,1,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,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,0xb1,0,0,0xb1, -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,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,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,0xb1,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,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,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, -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, -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,0,0,0, -0,0,0,0,0,0,0,0xb1,0xb1,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,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1, -0,0,0xb1,0xb1,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,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,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,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,0xb1,0,0xb1, -0,0,0,0,0,0,0,0,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,0,4,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,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,4,4,4,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0xa0,0xa0,0xa0,0xa0, -0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xb1,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,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,0,0,0xa,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,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,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,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,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,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,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,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,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,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,0,0,0,0,0xb1,0xb1,0xb1,0xb1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4, -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, -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,0x41,0x41,0x41,0x41, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xa1,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,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,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,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,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,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,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, -0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0,0,0,0,0xa,0,0,0,0,0,0,0,0,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,0,0,0xa,0xa,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,0,0,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,0xa,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,0,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,0,0,0,0,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,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,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,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,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,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[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[684]={ -0x2d,0,3,3,0x2c,3,0x2d,3,4,0x2a,4,4,0xd,0xd,0xd,6, -6,0x1f,0x1f,0x23,0x23,0x21,0x21,0x28,0x28,1,1,0xb,0xb,0x37,0x37,0x37, -0,9,0x1d,0x13,0x16,0x18,0x1a,0x10,0x2c,0x2d,0x2d,0,0,0,0,0, -0,0,0,0,0,0,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,0x1d, -0,3,3,3,0,3,0x2c,0x2c,0x2d,4,4,4,4,4,4,4, -4,0xd,0xd,0xd,0xd,0xd,0xd,0xd,6,6,6,6,6,6,6,6, -6,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x23,0x23,0x23,0x21,0x21,0x28, -1,9,9,9,9,9,9,0x1d,0x1d,0xb,0x26,0xb,0x13,0x13,0x13,0xb, -0xb,0xb,0xb,0xb,0xb,0x16,0x16,0x16,0x16,0x1a,0x1a,0x1a,0x1a,0x38,0x15,0xd, -0x2a,0x11,0x11,0xe,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x37,0x2f,0x37,0x2c, -0x2d,0x2d,0x2e,0x2e,0,0x2a,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0x1f, -0,0,0,0,0,0,0,0,0,0,0x23,0x21,1,0,0,0x15, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,5,0xc,0xc,7,7,0xf,0x27,0x32,0x12,0x2b,0x2b,0x30,0x31,0x14, -0x17,0x19,0x1b,0x24,0xa,8,0x1c,0x20,0x22,0x1e,7,0x25,0x29,5,0xc,7, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0x35,0x34,0x33, -4,4,4,4,4,4,4,0xd,0xd,6,6,0x1f,0x23,1,1,1, -9,9,0xb,0xb,0xb,0x18,0x18,0x1a,0x1a,0x1a,0x16,0x1f,0x1f,0x23,0xd,0xd, -0x23,0x1f,0xd,3,3,0x37,0x37,0x2d,0x2c,0x2c,0x36,0x36,0xd,0x23,0x23,0x13, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x5d,0x5a,0x60,0x63,0x5e,0x5f,0x59,0x61,0x5b,0x5c,0x62,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, -3,3,3,0,0,0,0x66,0,0,0x1a,0xd,0x28,0x28,0xb,0x67,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -4,4,0xd,0x28,9,0x1d,0x16,0x18,0x2d,0x2d,0x1f,0x2c,0x39,0,6,0x21, -0xb,0x55,0x1f,1,0x13,0x1d,4,4,4,0x1f,0x2d,0x56,0x58,0x57,4,4, -4,0xd,0xb,1,0x58,0xd,0xd,0x16,0xb,0,0,0 -}; - -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, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,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={ - NULL, - ubidi_props_indexes, - ubidi_props_mirrors, - ubidi_props_jgArray, - ubidi_props_jgArray2, - { - ubidi_props_trieIndex, - ubidi_props_trieIndex+3612, - NULL, - 3612, - 9412, - 0x1a0, - 0xe9c, - 0x0, - 0x0, - 0x110000, - 0x32dc, - NULL, 0, false, false, 0, NULL - }, - { 2,2,0,0 } -}; - -#endif // INCLUDED_FROM_UBIDI_PROPS_C +// Copyright (C) 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// Copyright (C) 1999-2016, International Business Machines +// Corporation and others. All Rights Reserved. +// +// file name: ubidi_props_data.h +// +// machine-generated by: icu/tools/unicode/c/genprops/bidipropsbuilder.cpp + +#ifdef INCLUDED_FROM_UBIDI_PROPS_C + +static const UVersionInfo ubidi_props_dataVersion={0xf,0,0,0}; + +static const int32_t ubidi_props_indexes[UBIDI_IX_TOP]={0x10,0x6bc0,0x65d0,0x28,0x620,0x8cc,0x10ac0,0x10d24,0,0,0,0,0,0,0,0x6702b6}; + +static const uint16_t ubidi_props_trieIndex[13024]={ +0x387,0x38f,0x397,0x39f,0x3b7,0x3bf,0x3c7,0x3cf,0x3a7,0x3af,0x3a7,0x3af,0x3a7,0x3af,0x3a7,0x3af, +0x3a7,0x3af,0x3a7,0x3af,0x3d5,0x3dd,0x3e5,0x3ed,0x3f5,0x3fd,0x3f9,0x401,0x409,0x411,0x40c,0x414, +0x3a7,0x3af,0x3a7,0x3af,0x41c,0x424,0x3a7,0x3af,0x3a7,0x3af,0x3a7,0x3af,0x42a,0x432,0x43a,0x442, +0x44a,0x452,0x45a,0x462,0x468,0x470,0x478,0x480,0x488,0x490,0x496,0x49e,0x4a6,0x4ae,0x4b6,0x4be, +0x4ca,0x4c6,0x4d2,0x4da,0x4e2,0x4f2,0x4f9,0x4ea,0x501,0x503,0x50b,0x513,0x51b,0x51c,0x524,0x52c, +0x534,0x51c,0x53c,0x541,0x534,0x51c,0x549,0x551,0x51b,0x559,0x561,0x513,0x569,0x3a7,0x571,0x575, +0x57d,0x57f,0x587,0x58f,0x51b,0x597,0x59f,0x513,0x41e,0x5a3,0x524,0x513,0x51b,0x3a7,0x5ab,0x3a7, +0x3a7,0x5b1,0x5b9,0x3a7,0x3a7,0x5bd,0x5c5,0x3a7,0x5c9,0x5d0,0x3a7,0x5d8,0x5e0,0x5e7,0x568,0x3a7, +0x3a7,0x5ef,0x5f7,0x5ff,0x607,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x60f,0x3a7,0x617,0x3a7,0x3a7,0x3a7, +0x61f,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x627,0x3a7,0x3a7,0x3a7,0x62f,0x528,0x528,0x528,0x3a7,0x635,0x63d,0x617, +0x653,0x645,0x645,0x65b,0x662,0x64b,0x3a7,0x3a7,0x3a7,0x66a,0x672,0x3a7,0x3a7,0x3a7,0x674,0x67c, +0x684,0x3a7,0x68b,0x693,0x3a7,0x69b,0x4fe,0x3a7,0x558,0x6a3,0x569,0x6ab,0x41e,0x6b3,0x3a7,0x6ba, +0x3a7,0x6bf,0x3a7,0x3a7,0x3a7,0x3a7,0x6c5,0x6cd,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3f5,0x3f5, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x6d4,0x6dc,0x6e0, +0x6f8,0x6fe,0x6e8,0x6f0,0x706,0x70e,0x712,0x5ea,0x71a,0x722,0x72a,0x3a7,0x732,0x67c,0x67c,0x67c, +0x742,0x74a,0x752,0x75a,0x75f,0x767,0x76f,0x73a,0x777,0x77f,0x3a7,0x785,0x78c,0x67c,0x67c,0x67c, +0x67c,0x595,0x792,0x67c,0x79a,0x3a7,0x3a7,0x679,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c, +0x67c,0x67c,0x67c,0x67c,0x67c,0x7a2,0x67c,0x67c,0x67c,0x67c,0x67c,0x7a8,0x67c,0x67c,0x7b0,0x7b8, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x67c,0x67c,0x67c,0x67c,0x7c8,0x7d0,0x7d8,0x7c0, +0x7e8,0x7f0,0x7f8,0x7ff,0x806,0x80e,0x812,0x7e0,0x67c,0x67c,0x67c,0x81a,0x820,0x67c,0x67c,0x826, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x82e,0x3a7,0x3a7,0x3a7,0x836,0x3a7,0x3a7,0x3a7,0x3f5, +0x83e,0x846,0x849,0x3a7,0x851,0x67c,0x67c,0x67f,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x858,0x85e, +0x86e,0x866,0x3a7,0x3a7,0x876,0x61f,0x3a7,0x3ce,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x67c,0x835, +0x3dc,0x3a7,0x87e,0x886,0x3a7,0x88e,0x896,0x3a7,0x3a7,0x3a7,0x3a7,0x89a,0x3a7,0x3a7,0x674,0x3cd, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x67c,0x67c, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x87e,0x67c,0x595,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x8a1,0x3a7,0x3a7,0x8a6,0x8ae,0x3a7,0x3a7,0x5cb,0x67c,0x673,0x3a7,0x3a7,0x8b6,0x3a7,0x3a7,0x3a7, +0x8be,0x8c5,0x645,0x8cd,0x3a7,0x3a7,0x5a1,0x8d5,0x3a7,0x8dd,0x8e4,0x3a7,0x501,0x8e9,0x3a7,0x51a, +0x3a7,0x8f1,0x8f9,0x51c,0x3a7,0x8fd,0x51b,0x905,0x3a7,0x3a7,0x3a7,0x90b,0x3a7,0x3a7,0x3a7,0x912, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x926,0x91a,0x91e,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6, +0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x92e,0x936,0x4a6,0x4a6,0x4a6,0x93b,0x93f, +0x947,0x94f,0x953,0x95b,0x4a6,0x4a6,0x4a6,0x95f,0x967,0x397,0x96f,0x977,0x3a7,0x3a7,0x3a7,0x97f, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0xe9c,0xe9c,0xedc,0xf1c,0xe9c,0xe9c,0xe9c,0xe9c,0xe9c,0xe9c,0xf54,0xf94,0xfd4,0xfe4,0x1024,0x1030, +0xe9c,0xe9c,0x1070,0xe9c,0xe9c,0xe9c,0x10a8,0x10e8,0x1128,0x1168,0x11a0,0x11e0,0x1220,0x1258,0x1298,0x12d8, +0xa40,0xa80,0xac0,0xaff,0x1a0,0x1a0,0xb3f,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xb68,0x1a0,0x1a0, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xba8,0x1a0,0xbce,0xc09,0xc49,0xc89,0xcc9,0xd09,0xd49, +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,0xd89, +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,0xd89, +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,0xd89, +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,0xd89, +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,0xd89, +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,0xd89, +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,0xd89, +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,0xd89, +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,0xd89, +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,0xd89, +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,0xd89, +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,0xd89, +0xdc9,0xdd9,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,0xd89, +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,0xd89, +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,0xd89, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x987,0x3a7,0x67c,0x67c,0x98f,0x61f,0x3a7,0x514, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x997,0x3a7,0x3a7,0x3a7,0x99e,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x9a6,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c, +0x9ae,0x9b2,0x43c,0x43c,0x43c,0x43c,0x9c2,0x9ba,0x43c,0x9ca,0x43c,0x43c,0x9d2,0x9d8,0x43c,0x43c, +0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x9e8,0x9e0,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c, +0x43c,0x43c,0x43c,0x9f0,0x43c,0x9f8,0x4a6,0xa00,0x43c,0xa08,0xa0f,0xa15,0xa1d,0xa21,0xa29,0x43c, +0x51b,0xa31,0xa38,0xa3f,0x41e,0xa47,0x569,0x3a7,0x501,0xa4e,0x3a7,0xa54,0x41e,0xa59,0xa61,0x3a7, +0x3a7,0xa66,0x51b,0x3a7,0x3a7,0x3a7,0x836,0xa6e,0x41e,0x5a3,0x57e,0xa75,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0xa31,0xa7d,0x3a7,0x3a7,0xa85,0xa8d,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xa91,0xa99,0x3a7, +0x3a7,0xaa1,0x57e,0xaa9,0x3a7,0xaaf,0x3a7,0x3a7,0x60f,0xab7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0xabc,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xac3,0xacb,0x3a7,0x3a7,0x3a7,0xace,0x57e,0xad6, +0xada,0xae2,0x3a7,0xae9,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0xaf0,0x3a7,0x3a7,0xafe,0xaf8,0x3a7,0x3a7,0x3a7,0xb06,0xb0e,0x3a7,0xb12,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x5a5,0x41e,0x99e,0xb1a,0x3a7,0x3a7,0x3a7,0xb27,0xb22,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0xb2f,0xb37,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xb3d, +0x3a7,0xb43,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0xa55,0x3a7,0xb49,0x3a7,0x3a7,0xb51,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x535,0xb59,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3f5,0xb61,0x500,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0xb69,0xb71,0xb77,0x3a7,0xb7d,0x67c,0x67c,0xb85,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x67c,0x67c,0xb8d,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xb93, +0x3a7,0xb9a,0x3a7,0xb96,0x3a7,0xb9d,0x3a7,0xba5,0xba9,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3f5,0xbb1,0x3f5,0xbb8,0xbbf,0xbc7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xbcf,0xbd7,0x3a7,0x3a7,0xa55,0x3a7,0x3a7, +0x3a7,0x3a7,0xb43,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xa81,0x3a7, +0xbdc,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0xbe4,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0xbec, +0x43c,0xbf4,0xbf4,0xbfb,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c, +0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x91e,0x4a6,0x4a6,0x43c, +0x43c,0x4a6,0x4a6,0xc03,0x43c,0x43c,0x43c,0x43c,0x43c,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6,0x4a6, +0xc0b,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x43c,0x67c,0xc13,0x67c,0x67c,0x67f,0xc18,0xc1c, +0x858,0xc24,0x3c9,0x3a7,0xc2a,0x3a7,0xc2f,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x783,0x3a7,0x3a7,0x3a7, +0x3a7,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c, +0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0xc37, +0x98f,0x67c,0x67c,0x67c,0xc3e,0x67c,0x67c,0xc45,0xc4d,0xc13,0x67c,0xc55,0x67c,0xc5d,0xc62,0x3a7, +0x3a7,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67c,0x67f,0xc6a,0xc73,0xc77,0xc7f, +0xc6f,0x67c,0x67c,0x67c,0x67c,0xc87,0x67c,0x792,0xc8f,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xc96,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7, +0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0x3a7,0xc96,0xca6,0xc9e,0xc9e,0xc9e,0xca7,0xca7,0xca7, +0xca7,0x3f5,0x3f5,0x3f5,0x3f5,0x3f5,0x3f5,0x3f5,0xcaf,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7, +0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7, +0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7, +0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7, +0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0xca7,0x386,0x386,0x386,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,0,0,0,0,0,0,0,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, +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,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,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,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,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,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,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,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,0x81,0x81, +0x81,0xb1,0xb1,0xb1,1,1,1,1,0x4d,0xd,0x4d,0x4d,0x4d,0x4d,0xd,0x8d, +0x4d,0x8d,0x8d,0xd,0xd,0xd,0xd,0xd,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, +0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x2d,0x2d,0x2d,0x4d,0xd, +0xd,0x4d,0x4d,0x4d,0x4d,0x4d,0x8d,0xd,5,5,0xd,0xd,0xd,0xd,0xd,0xd, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,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,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,0x4d,0x4d,0x4d, +0x4d,0x8d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0xd,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,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, +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,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,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, +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,0xb1,0xb1,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,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,0xb1,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,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,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,0xb1,0xb1, +0xb1,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,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,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,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,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,0xb1,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,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,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,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,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,0,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,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,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,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,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,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,0x900a,0xa,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,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a, +0xf20a,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,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, +0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0,0,0,0,0xa,0,0,0,0,0,0,0, +0,0,0xb1,0xb1,0xb1,0xb1,0,0,0xa,0,0,0,0,0,0xa,0xa, +0,0,0,0,0,0xa,0xa,0xa,9,0xa,0xa,0xa,0xa,0,0,0, +0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0xa,0xa,0x310a,0xf20a,0x310a,0xf20a, +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, +0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xa,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,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,0xb1,0xb1,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,0xb1,0,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,0xb1,0,0xa,0xa,0xa,0xa, +0xb1,0,0,0,0,0,0,0,0,0,0,0,4,4,0,0, +0,0,0,0,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, +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,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,0xb1,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,0,0xa,0xa,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,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,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,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, +0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xa,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,0xa,0xa,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,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,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,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, +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,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,1, +1,1,1,1,1,1,1,1,1,1,1,0xb1,0xb1,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, +0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xb1,0xb1,0xb1, +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,0x41,0x41,0x41,0x41,0x81,0x81,0x41,0x41,0x41,0x41,0x41,0x41, +0x41,0x41,0x41,0x41,0x41,0x41,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,1,1,0x41,1,0x41,0x41,0x81,0x81,0x81,1,0x41,0x81,0x81,0x41, +0x41,0x81,0x41,0x41,1,0x41,0x81,0x81,0x41,1,1,1,1,0x81,0x41,0x61, +1,1,1,1,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,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,0xb1,0,0,0xb1, +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,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,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,0xb1,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,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,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, +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, +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,0,0,0, +0,0,0,0,0,0,0,0xb1,0xb1,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,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1, +0,0,0xb1,0xb1,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,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,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,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,0xb1,0,0xb1, +0,0,0,0,0,0,0,0,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,0,4,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,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,4,4,4,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0xa0,0xa0,0xa0,0xa0, +0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xb1,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,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,0,0,0xa,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,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,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,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,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,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,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,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,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,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,0,0,0,0,0xb1,0xb1,0xb1,0xb1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4, +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, +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,0x41,0x41,0x41,0x41, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xa1,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,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,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,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,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,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,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, +0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0,0,0,0,0xa,0,0,0,0,0,0,0,0,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,0,0,0xa,0xa,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,0,0,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,0xa,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,0,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,0,0,0,0,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,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,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,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,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,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[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[684]={ +0x2d,0,3,3,0x2c,3,0x2d,3,4,0x2a,4,4,0xd,0xd,0xd,6, +6,0x1f,0x1f,0x23,0x23,0x21,0x21,0x28,0x28,1,1,0xb,0xb,0x37,0x37,0x37, +0,9,0x1d,0x13,0x16,0x18,0x1a,0x10,0x2c,0x2d,0x2d,0,0,0,0,0, +0,0,0,0,0,0,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,0x1d, +0,3,3,3,0,3,0x2c,0x2c,0x2d,4,4,4,4,4,4,4, +4,0xd,0xd,0xd,0xd,0xd,0xd,0xd,6,6,6,6,6,6,6,6, +6,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x23,0x23,0x23,0x21,0x21,0x28, +1,9,9,9,9,9,9,0x1d,0x1d,0xb,0x26,0xb,0x13,0x13,0x13,0xb, +0xb,0xb,0xb,0xb,0xb,0x16,0x16,0x16,0x16,0x1a,0x1a,0x1a,0x1a,0x38,0x15,0xd, +0x2a,0x11,0x11,0xe,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x37,0x2f,0x37,0x2c, +0x2d,0x2d,0x2e,0x2e,0,0x2a,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0x1f, +0,0,0,0,0,0,0,0,0,0,0x23,0x21,1,0,0,0x15, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,5,0xc,0xc,7,7,0xf,0x27,0x32,0x12,0x2b,0x2b,0x30,0x31,0x14, +0x17,0x19,0x1b,0x24,0xa,8,0x1c,0x20,0x22,0x1e,7,0x25,0x29,5,0xc,7, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0x35,0x34,0x33, +4,4,4,4,4,4,4,0xd,0xd,6,6,0x1f,0x23,1,1,1, +9,9,0xb,0xb,0xb,0x18,0x18,0x1a,0x1a,0x1a,0x16,0x1f,0x1f,0x23,0xd,0xd, +0x23,0x1f,0xd,3,3,0x37,0x37,0x2d,0x2c,0x2c,0x36,0x36,0xd,0x23,0x23,0x13, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x5d,0x5a,0x60,0x63,0x5e,0x5f,0x59,0x61,0x5b,0x5c,0x62,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,0,0,0,0x66,0,0,0x1a,0xd,0x28,0x28,0xb,0x67,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +4,4,0xd,0x28,9,0x1d,0x16,0x18,0x2d,0x2d,0x1f,0x2c,0x39,0,6,0x21, +0xb,0x55,0x1f,1,0x13,0x1d,4,4,4,0x1f,0x2d,0x56,0x58,0x57,4,4, +4,0xd,0xb,1,0x58,0xd,0xd,0x16,0xb,0,0,0 +}; + +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, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,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={ + nullptr, + ubidi_props_indexes, + ubidi_props_mirrors, + ubidi_props_jgArray, + ubidi_props_jgArray2, + { + ubidi_props_trieIndex, + ubidi_props_trieIndex+3612, + nullptr, + 3612, + 9412, + 0x1a0, + 0xe9c, + 0x0, + 0x0, + 0x110000, + 0x32dc, + nullptr, 0, false, false, 0, nullptr + }, + { 2,2,0,0 } +}; + +#endif // INCLUDED_FROM_UBIDI_PROPS_C diff --git a/deps/icu-small/source/common/ubidiimp.h b/deps/icu-small/source/common/ubidiimp.h index e48fc6f941621b..c575f8cecf42a3 100644 --- a/deps/icu-small/source/common/ubidiimp.h +++ b/deps/icu-small/source/common/ubidiimp.h @@ -1,484 +1,484 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: ubidiimp.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999aug06 -* created by: Markus W. Scherer, updated by Matitiahu Allouche -*/ - -#ifndef UBIDIIMP_H -#define UBIDIIMP_H - -#include "unicode/utypes.h" -#include "unicode/ubidi.h" -#include "unicode/uchar.h" -#include "ubidi_props.h" - -/* miscellaneous definitions ---------------------------------------------- */ - -// ICU-20853=ICU-20935 Solaris #defines CS and ES in sys/regset.h -#ifdef CS -# undef CS -#endif -#ifdef ES -# undef ES -#endif - -typedef uint8_t DirProp; -typedef uint32_t Flags; - -/* Comparing the description of the BiDi algorithm with this implementation - is easier with the same names for the BiDi types in the code as there. - See UCharDirection in uchar.h . -*/ -enum { - L= U_LEFT_TO_RIGHT, /* 0 */ - R= U_RIGHT_TO_LEFT, /* 1 */ - EN= U_EUROPEAN_NUMBER, /* 2 */ - ES= U_EUROPEAN_NUMBER_SEPARATOR, /* 3 */ - ET= U_EUROPEAN_NUMBER_TERMINATOR, /* 4 */ - AN= U_ARABIC_NUMBER, /* 5 */ - CS= U_COMMON_NUMBER_SEPARATOR, /* 6 */ - B= U_BLOCK_SEPARATOR, /* 7 */ - S= U_SEGMENT_SEPARATOR, /* 8 */ - WS= U_WHITE_SPACE_NEUTRAL, /* 9 */ - ON= U_OTHER_NEUTRAL, /* 10 */ - LRE=U_LEFT_TO_RIGHT_EMBEDDING, /* 11 */ - LRO=U_LEFT_TO_RIGHT_OVERRIDE, /* 12 */ - AL= U_RIGHT_TO_LEFT_ARABIC, /* 13 */ - RLE=U_RIGHT_TO_LEFT_EMBEDDING, /* 14 */ - RLO=U_RIGHT_TO_LEFT_OVERRIDE, /* 15 */ - PDF=U_POP_DIRECTIONAL_FORMAT, /* 16 */ - NSM=U_DIR_NON_SPACING_MARK, /* 17 */ - BN= U_BOUNDARY_NEUTRAL, /* 18 */ - FSI=U_FIRST_STRONG_ISOLATE, /* 19 */ - LRI=U_LEFT_TO_RIGHT_ISOLATE, /* 20 */ - RLI=U_RIGHT_TO_LEFT_ISOLATE, /* 21 */ - PDI=U_POP_DIRECTIONAL_ISOLATE, /* 22 */ - ENL, /* EN after W7 */ /* 23 */ - ENR, /* EN not subject to W7 */ /* 24 */ - dirPropCount -}; - -/* Sometimes, bit values are more appropriate - to deal with directionality properties. - Abbreviations in these macro names refer to names - used in the BiDi algorithm. -*/ -#define DIRPROP_FLAG(dir) (1UL<<(dir)) -#define PURE_DIRPROP(prop) ((prop)&~0xE0) ????????????????????????? - -/* special flag for multiple runs from explicit embedding codes */ -#define DIRPROP_FLAG_MULTI_RUNS (1UL<<31) - -/* are there any characters that are LTR or RTL? */ -#define MASK_LTR (DIRPROP_FLAG(L)|DIRPROP_FLAG(EN)|DIRPROP_FLAG(ENL)|DIRPROP_FLAG(ENR)|DIRPROP_FLAG(AN)|DIRPROP_FLAG(LRE)|DIRPROP_FLAG(LRO)|DIRPROP_FLAG(LRI)) -#define MASK_RTL (DIRPROP_FLAG(R)|DIRPROP_FLAG(AL)|DIRPROP_FLAG(RLE)|DIRPROP_FLAG(RLO)|DIRPROP_FLAG(RLI)) -#define MASK_R_AL (DIRPROP_FLAG(R)|DIRPROP_FLAG(AL)) -#define MASK_STRONG_EN_AN (DIRPROP_FLAG(L)|DIRPROP_FLAG(R)|DIRPROP_FLAG(AL)|DIRPROP_FLAG(EN)|DIRPROP_FLAG(AN)) - -/* explicit embedding codes */ -#define MASK_EXPLICIT (DIRPROP_FLAG(LRE)|DIRPROP_FLAG(LRO)|DIRPROP_FLAG(RLE)|DIRPROP_FLAG(RLO)|DIRPROP_FLAG(PDF)) - -/* explicit isolate codes */ -#define MASK_ISO (DIRPROP_FLAG(LRI)|DIRPROP_FLAG(RLI)|DIRPROP_FLAG(FSI)|DIRPROP_FLAG(PDI)) - -#define MASK_BN_EXPLICIT (DIRPROP_FLAG(BN)|MASK_EXPLICIT) - -/* paragraph and segment separators */ -#define MASK_B_S (DIRPROP_FLAG(B)|DIRPROP_FLAG(S)) - -/* all types that are counted as White Space or Neutral in some steps */ -#define MASK_WS (MASK_B_S|DIRPROP_FLAG(WS)|MASK_BN_EXPLICIT|MASK_ISO) - -/* types that are neutrals or could becomes neutrals in (Wn) */ -#define MASK_POSSIBLE_N (DIRPROP_FLAG(ON)|DIRPROP_FLAG(CS)|DIRPROP_FLAG(ES)|DIRPROP_FLAG(ET)|MASK_WS) - -/* - * These types may be changed to "e", - * the embedding type (L or R) of the run, - * in the BiDi algorithm (N2) - */ -#define MASK_EMBEDDING (DIRPROP_FLAG(NSM)|MASK_POSSIBLE_N) - -/* the dirProp's L and R are defined to 0 and 1 values in UCharDirection */ -#define GET_LR_FROM_LEVEL(level) ((DirProp)((level)&1)) - -#define IS_DEFAULT_LEVEL(level) ((level)>=0xfe) - -/* - * The following bit is used for the directional isolate status. - * Stack entries corresponding to isolate sequences are greater than ISOLATE. - */ -#define ISOLATE 0x0100 - -U_CFUNC UBiDiLevel -ubidi_getParaLevelAtIndex(const UBiDi *pBiDi, int32_t index); - -#define GET_PARALEVEL(ubidi, index) \ - ((UBiDiLevel)(!(ubidi)->defaultParaLevel || (index)<(ubidi)->paras[0].limit ? \ - (ubidi)->paraLevel : ubidi_getParaLevelAtIndex((ubidi), (index)))) - -/* number of paras entries allocated initially without malloc */ -#define SIMPLE_PARAS_COUNT 10 -/* number of isolate entries allocated initially without malloc */ -#define SIMPLE_ISOLATES_COUNT 5 -/* number of isolate run entries for paired brackets allocated initially without malloc */ -#define SIMPLE_OPENINGS_COUNT 20 - -#define CR 0x000D -#define LF 0x000A - -/* Run structure for reordering --------------------------------------------- */ -enum { - LRM_BEFORE=1, - LRM_AFTER=2, - RLM_BEFORE=4, - RLM_AFTER=8 -}; - -typedef struct Para { - int32_t limit; - int32_t level; -} Para; - -enum { /* flags for Opening.flags */ - FOUND_L=DIRPROP_FLAG(L), - FOUND_R=DIRPROP_FLAG(R) -}; - -typedef struct Opening { - int32_t position; /* position of opening bracket */ - int32_t match; /* matching char or -position of closing bracket */ - int32_t contextPos; /* position of last strong char found before opening */ - uint16_t flags; /* bits for L or R/AL found within the pair */ - UBiDiDirection contextDir; /* L or R according to last strong char before opening */ - uint8_t filler; /* to complete a nice multiple of 4 chars */ -} Opening; - -typedef struct IsoRun { - int32_t contextPos; /* position of char determining context */ - uint16_t start; /* index of first opening entry for this run */ - uint16_t limit; /* index after last opening entry for this run */ - UBiDiLevel level; /* level of this run */ - DirProp lastStrong; /* bidi class of last strong char found in this run */ - DirProp lastBase; /* bidi class of last base char found in this run */ - UBiDiDirection contextDir; /* L or R to use as context for following openings */ -} IsoRun; - -typedef struct BracketData { - UBiDi *pBiDi; - /* array of opening entries which should be enough in most cases; no malloc() */ - Opening simpleOpenings[SIMPLE_OPENINGS_COUNT]; - Opening *openings; /* pointer to current array of entries */ - int32_t openingsCount; /* number of allocated entries */ - int32_t isoRunLast; /* index of last used entry */ - /* array of nested isolated sequence entries; can never excess UBIDI_MAX_EXPLICIT_LEVEL - + 1 for index 0, + 1 for before the first isolated sequence */ - IsoRun isoRuns[UBIDI_MAX_EXPLICIT_LEVEL+2]; - UBool isNumbersSpecial; /* reordering mode for NUMBERS_SPECIAL */ -} BracketData; - -typedef struct Isolate { - int32_t startON; - int32_t start1; - int32_t state; - int16_t stateImp; -} Isolate; - -typedef struct Run { - int32_t logicalStart, /* first character of the run; b31 indicates even/odd level */ - visualLimit, /* last visual position of the run +1 */ - insertRemove; /* if >0, flags for inserting LRM/RLM before/after run, - if <0, count of bidi controls within run */ -} Run; - -/* in a Run, logicalStart will get this bit set if the run level is odd */ -#define INDEX_ODD_BIT (1UL<<31) - -#define MAKE_INDEX_ODD_PAIR(index, level) ((index)|((int32_t)((level)&1)<<31)) -#define ADD_ODD_BIT_FROM_LEVEL(x, level) ((x)|=((int32_t)((level)&1)<<31)) -#define REMOVE_ODD_BIT(x) ((x)&=~INDEX_ODD_BIT) - -#define GET_INDEX(x) ((x)&~INDEX_ODD_BIT) -#define GET_ODD_BIT(x) ((uint32_t)(x)>>31) -#define IS_ODD_RUN(x) ((UBool)(((x)&INDEX_ODD_BIT)!=0)) -#define IS_EVEN_RUN(x) ((UBool)(((x)&INDEX_ODD_BIT)==0)) - -U_CFUNC UBool -ubidi_getRuns(UBiDi *pBiDi, UErrorCode *pErrorCode); - -/** BiDi control code points */ -enum { - ZWNJ_CHAR=0x200c, - ZWJ_CHAR, - LRM_CHAR, - RLM_CHAR, - LRE_CHAR=0x202a, - RLE_CHAR, - PDF_CHAR, - LRO_CHAR, - RLO_CHAR, - LRI_CHAR=0x2066, - RLI_CHAR, - FSI_CHAR, - PDI_CHAR -}; - -#define IS_BIDI_CONTROL_CHAR(c) (((uint32_t)(c)&0xfffffffc)==ZWNJ_CHAR || (uint32_t)((c)-LRE_CHAR)<5 || (uint32_t)((c)-LRI_CHAR)<4) - -/* InsertPoints structure for noting where to put BiDi marks ---------------- */ - -typedef struct Point { - int32_t pos; /* position in text */ - int32_t flag; /* flag for LRM/RLM, before/after */ -} Point; - -typedef struct InsertPoints { - int32_t capacity; /* number of points allocated */ - int32_t size; /* number of points used */ - int32_t confirmed; /* number of points confirmed */ - UErrorCode errorCode; /* for eventual memory shortage */ - Point *points; /* pointer to array of points */ -} InsertPoints; - - -/* UBiDi structure ----------------------------------------------------------- */ - -struct UBiDi { - /* pointer to parent paragraph object (pointer to self if this object is - * a paragraph object); set to NULL in a newly opened object; set to a - * real value after a successful execution of ubidi_setPara or ubidi_setLine - */ - const UBiDi * pParaBiDi; - - /* alias pointer to the current text */ - const UChar *text; - - /* length of the current text */ - int32_t originalLength; - - /* if the UBIDI_OPTION_STREAMING option is set, this is the length - * of text actually processed by ubidi_setPara, which may be shorter than - * the original length. - * Otherwise, it is identical to the original length. - */ - int32_t length; - - /* if the UBIDI_OPTION_REMOVE_CONTROLS option is set, and/or - * marks are allowed to be inserted in one of the reordering mode, the - * length of the result string may be different from the processed length. - */ - int32_t resultLength; - - /* memory sizes in bytes */ - int32_t dirPropsSize, levelsSize, openingsSize, parasSize, runsSize, isolatesSize; - - /* allocated memory */ - DirProp *dirPropsMemory; - UBiDiLevel *levelsMemory; - Opening *openingsMemory; - Para *parasMemory; - Run *runsMemory; - Isolate *isolatesMemory; - - /* indicators for whether memory may be allocated after ubidi_open() */ - UBool mayAllocateText, mayAllocateRuns; - - /* arrays with one value per text-character */ - DirProp *dirProps; - UBiDiLevel *levels; - - /* are we performing an approximation of the "inverse BiDi" algorithm? */ - UBool isInverse; - - /* are we using the basic algorithm or its variation? */ - UBiDiReorderingMode reorderingMode; - - /* UBIDI_REORDER_xxx values must be ordered so that all the regular - * logical to visual modes come first, and all inverse BiDi modes - * come last. - */ - #define UBIDI_REORDER_LAST_LOGICAL_TO_VISUAL UBIDI_REORDER_NUMBERS_SPECIAL - - /* bitmask for reordering options */ - uint32_t reorderingOptions; - - /* must block separators receive level 0? */ - UBool orderParagraphsLTR; - - /* the paragraph level */ - UBiDiLevel paraLevel; - /* original paraLevel when contextual */ - /* must be one of UBIDI_DEFAULT_xxx or 0 if not contextual */ - UBiDiLevel defaultParaLevel; - - /* context data */ - const UChar *prologue; - int32_t proLength; - const UChar *epilogue; - int32_t epiLength; - - /* the following is set in ubidi_setPara, used in processPropertySeq */ - const struct ImpTabPair * pImpTabPair; /* pointer to levels state table pair */ - - /* the overall paragraph or line directionality - see UBiDiDirection */ - UBiDiDirection direction; - - /* flags is a bit set for which directional properties are in the text */ - Flags flags; - - /* lastArabicPos is index to the last AL in the text, -1 if none */ - int32_t lastArabicPos; - - /* characters after trailingWSStart are WS and are */ - /* implicitly at the paraLevel (rule (L1)) - levels may not reflect that */ - int32_t trailingWSStart; - - /* fields for paragraph handling */ - int32_t paraCount; /* set in getDirProps() */ - /* filled in getDirProps() */ - Para *paras; - - /* for relatively short text, we only need a tiny array of paras (no malloc()) */ - Para simpleParas[SIMPLE_PARAS_COUNT]; - - /* fields for line reordering */ - int32_t runCount; /* ==-1: runs not set up yet */ - Run *runs; - - /* for non-mixed text, we only need a tiny array of runs (no malloc()) */ - Run simpleRuns[1]; - - /* maximum or current nesting depth of isolate sequences */ - /* Within resolveExplicitLevels() and checkExplicitLevels(), this is the maximal - nesting encountered. - Within resolveImplicitLevels(), this is the index of the current isolates - stack entry. */ - int32_t isolateCount; - Isolate *isolates; - - /* for simple text, have a small stack (no malloc()) */ - Isolate simpleIsolates[SIMPLE_ISOLATES_COUNT]; - - /* for inverse Bidi with insertion of directional marks */ - InsertPoints insertPoints; - - /* for option UBIDI_OPTION_REMOVE_CONTROLS */ - int32_t controlCount; - - /* for Bidi class callback */ - UBiDiClassCallback *fnClassCallback; /* action pointer */ - const void *coClassCallback; /* context pointer */ -}; - -#define IS_VALID_PARA(x) ((x) && ((x)->pParaBiDi==(x))) -#define IS_VALID_PARA_OR_LINE(x) ((x) && ((x)->pParaBiDi==(x) || (((x)->pParaBiDi) && (x)->pParaBiDi->pParaBiDi==(x)->pParaBiDi))) - -typedef union { - DirProp *dirPropsMemory; - UBiDiLevel *levelsMemory; - Opening *openingsMemory; - Para *parasMemory; - Run *runsMemory; - Isolate *isolatesMemory; -} BidiMemoryForAllocation; - -/* Macros for initial checks at function entry */ -#define RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrcode, retvalue) UPRV_BLOCK_MACRO_BEGIN { \ - if((pErrcode)==NULL || U_FAILURE(*pErrcode)) return retvalue; \ -} UPRV_BLOCK_MACRO_END -#define RETURN_IF_NOT_VALID_PARA(bidi, errcode, retvalue) UPRV_BLOCK_MACRO_BEGIN { \ - if(!IS_VALID_PARA(bidi)) { \ - errcode=U_INVALID_STATE_ERROR; \ - return retvalue; \ - } \ -} UPRV_BLOCK_MACRO_END -#define RETURN_IF_NOT_VALID_PARA_OR_LINE(bidi, errcode, retvalue) UPRV_BLOCK_MACRO_BEGIN { \ - if(!IS_VALID_PARA_OR_LINE(bidi)) { \ - errcode=U_INVALID_STATE_ERROR; \ - return retvalue; \ - } \ -} UPRV_BLOCK_MACRO_END -#define RETURN_IF_BAD_RANGE(arg, start, limit, errcode, retvalue) UPRV_BLOCK_MACRO_BEGIN { \ - if((arg)<(start) || (arg)>=(limit)) { \ - (errcode)=U_ILLEGAL_ARGUMENT_ERROR; \ - return retvalue; \ - } \ -} UPRV_BLOCK_MACRO_END - -#define RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrcode) UPRV_BLOCK_MACRO_BEGIN { \ - if((pErrcode)==NULL || U_FAILURE(*pErrcode)) return; \ -} UPRV_BLOCK_MACRO_END -#define RETURN_VOID_IF_NOT_VALID_PARA(bidi, errcode) UPRV_BLOCK_MACRO_BEGIN { \ - if(!IS_VALID_PARA(bidi)) { \ - errcode=U_INVALID_STATE_ERROR; \ - return; \ - } \ -} UPRV_BLOCK_MACRO_END -#define RETURN_VOID_IF_NOT_VALID_PARA_OR_LINE(bidi, errcode) UPRV_BLOCK_MACRO_BEGIN { \ - if(!IS_VALID_PARA_OR_LINE(bidi)) { \ - errcode=U_INVALID_STATE_ERROR; \ - return; \ - } \ -} UPRV_BLOCK_MACRO_END -#define RETURN_VOID_IF_BAD_RANGE(arg, start, limit, errcode) UPRV_BLOCK_MACRO_BEGIN { \ - if((arg)<(start) || (arg)>=(limit)) { \ - (errcode)=U_ILLEGAL_ARGUMENT_ERROR; \ - return; \ - } \ -} UPRV_BLOCK_MACRO_END - -/* helper function to (re)allocate memory if allowed */ -U_CFUNC UBool -ubidi_getMemory(BidiMemoryForAllocation *pMemory, int32_t *pSize, UBool mayAllocate, int32_t sizeNeeded); - -/* helper macros for each allocated array in UBiDi */ -#define getDirPropsMemory(pBiDi, length) \ - ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->dirPropsMemory, &(pBiDi)->dirPropsSize, \ - (pBiDi)->mayAllocateText, (length)) - -#define getLevelsMemory(pBiDi, length) \ - ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->levelsMemory, &(pBiDi)->levelsSize, \ - (pBiDi)->mayAllocateText, (length)) - -#define getRunsMemory(pBiDi, length) \ - ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->runsMemory, &(pBiDi)->runsSize, \ - (pBiDi)->mayAllocateRuns, (length)*sizeof(Run)) - -/* additional macros used by ubidi_open() - always allow allocation */ -#define getInitialDirPropsMemory(pBiDi, length) \ - ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->dirPropsMemory, &(pBiDi)->dirPropsSize, \ - true, (length)) - -#define getInitialLevelsMemory(pBiDi, length) \ - ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->levelsMemory, &(pBiDi)->levelsSize, \ - true, (length)) - -#define getInitialOpeningsMemory(pBiDi, length) \ - ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->openingsMemory, &(pBiDi)->openingsSize, \ - true, (length)*sizeof(Opening)) - -#define getInitialParasMemory(pBiDi, length) \ - ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->parasMemory, &(pBiDi)->parasSize, \ - true, (length)*sizeof(Para)) - -#define getInitialRunsMemory(pBiDi, length) \ - ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->runsMemory, &(pBiDi)->runsSize, \ - true, (length)*sizeof(Run)) - -#define getInitialIsolatesMemory(pBiDi, length) \ - ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->isolatesMemory, &(pBiDi)->isolatesSize, \ - true, (length)*sizeof(Isolate)) - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ubidiimp.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999aug06 +* created by: Markus W. Scherer, updated by Matitiahu Allouche +*/ + +#ifndef UBIDIIMP_H +#define UBIDIIMP_H + +#include "unicode/utypes.h" +#include "unicode/ubidi.h" +#include "unicode/uchar.h" +#include "ubidi_props.h" + +/* miscellaneous definitions ---------------------------------------------- */ + +// ICU-20853=ICU-20935 Solaris #defines CS and ES in sys/regset.h +#ifdef CS +# undef CS +#endif +#ifdef ES +# undef ES +#endif + +typedef uint8_t DirProp; +typedef uint32_t Flags; + +/* Comparing the description of the BiDi algorithm with this implementation + is easier with the same names for the BiDi types in the code as there. + See UCharDirection in uchar.h . +*/ +enum { + L= U_LEFT_TO_RIGHT, /* 0 */ + R= U_RIGHT_TO_LEFT, /* 1 */ + EN= U_EUROPEAN_NUMBER, /* 2 */ + ES= U_EUROPEAN_NUMBER_SEPARATOR, /* 3 */ + ET= U_EUROPEAN_NUMBER_TERMINATOR, /* 4 */ + AN= U_ARABIC_NUMBER, /* 5 */ + CS= U_COMMON_NUMBER_SEPARATOR, /* 6 */ + B= U_BLOCK_SEPARATOR, /* 7 */ + S= U_SEGMENT_SEPARATOR, /* 8 */ + WS= U_WHITE_SPACE_NEUTRAL, /* 9 */ + ON= U_OTHER_NEUTRAL, /* 10 */ + LRE=U_LEFT_TO_RIGHT_EMBEDDING, /* 11 */ + LRO=U_LEFT_TO_RIGHT_OVERRIDE, /* 12 */ + AL= U_RIGHT_TO_LEFT_ARABIC, /* 13 */ + RLE=U_RIGHT_TO_LEFT_EMBEDDING, /* 14 */ + RLO=U_RIGHT_TO_LEFT_OVERRIDE, /* 15 */ + PDF=U_POP_DIRECTIONAL_FORMAT, /* 16 */ + NSM=U_DIR_NON_SPACING_MARK, /* 17 */ + BN= U_BOUNDARY_NEUTRAL, /* 18 */ + FSI=U_FIRST_STRONG_ISOLATE, /* 19 */ + LRI=U_LEFT_TO_RIGHT_ISOLATE, /* 20 */ + RLI=U_RIGHT_TO_LEFT_ISOLATE, /* 21 */ + PDI=U_POP_DIRECTIONAL_ISOLATE, /* 22 */ + ENL, /* EN after W7 */ /* 23 */ + ENR, /* EN not subject to W7 */ /* 24 */ + dirPropCount +}; + +/* Sometimes, bit values are more appropriate + to deal with directionality properties. + Abbreviations in these macro names refer to names + used in the BiDi algorithm. +*/ +#define DIRPROP_FLAG(dir) (1UL<<(dir)) +#define PURE_DIRPROP(prop) ((prop)&~0xE0) ????????????????????????? + +/* special flag for multiple runs from explicit embedding codes */ +#define DIRPROP_FLAG_MULTI_RUNS (1UL<<31) + +/* are there any characters that are LTR or RTL? */ +#define MASK_LTR (DIRPROP_FLAG(L)|DIRPROP_FLAG(EN)|DIRPROP_FLAG(ENL)|DIRPROP_FLAG(ENR)|DIRPROP_FLAG(AN)|DIRPROP_FLAG(LRE)|DIRPROP_FLAG(LRO)|DIRPROP_FLAG(LRI)) +#define MASK_RTL (DIRPROP_FLAG(R)|DIRPROP_FLAG(AL)|DIRPROP_FLAG(RLE)|DIRPROP_FLAG(RLO)|DIRPROP_FLAG(RLI)) +#define MASK_R_AL (DIRPROP_FLAG(R)|DIRPROP_FLAG(AL)) +#define MASK_STRONG_EN_AN (DIRPROP_FLAG(L)|DIRPROP_FLAG(R)|DIRPROP_FLAG(AL)|DIRPROP_FLAG(EN)|DIRPROP_FLAG(AN)) + +/* explicit embedding codes */ +#define MASK_EXPLICIT (DIRPROP_FLAG(LRE)|DIRPROP_FLAG(LRO)|DIRPROP_FLAG(RLE)|DIRPROP_FLAG(RLO)|DIRPROP_FLAG(PDF)) + +/* explicit isolate codes */ +#define MASK_ISO (DIRPROP_FLAG(LRI)|DIRPROP_FLAG(RLI)|DIRPROP_FLAG(FSI)|DIRPROP_FLAG(PDI)) + +#define MASK_BN_EXPLICIT (DIRPROP_FLAG(BN)|MASK_EXPLICIT) + +/* paragraph and segment separators */ +#define MASK_B_S (DIRPROP_FLAG(B)|DIRPROP_FLAG(S)) + +/* all types that are counted as White Space or Neutral in some steps */ +#define MASK_WS (MASK_B_S|DIRPROP_FLAG(WS)|MASK_BN_EXPLICIT|MASK_ISO) + +/* types that are neutrals or could becomes neutrals in (Wn) */ +#define MASK_POSSIBLE_N (DIRPROP_FLAG(ON)|DIRPROP_FLAG(CS)|DIRPROP_FLAG(ES)|DIRPROP_FLAG(ET)|MASK_WS) + +/* + * These types may be changed to "e", + * the embedding type (L or R) of the run, + * in the BiDi algorithm (N2) + */ +#define MASK_EMBEDDING (DIRPROP_FLAG(NSM)|MASK_POSSIBLE_N) + +/* the dirProp's L and R are defined to 0 and 1 values in UCharDirection */ +#define GET_LR_FROM_LEVEL(level) ((DirProp)((level)&1)) + +#define IS_DEFAULT_LEVEL(level) ((level)>=0xfe) + +/* + * The following bit is used for the directional isolate status. + * Stack entries corresponding to isolate sequences are greater than ISOLATE. + */ +#define ISOLATE 0x0100 + +U_CFUNC UBiDiLevel +ubidi_getParaLevelAtIndex(const UBiDi *pBiDi, int32_t index); + +#define GET_PARALEVEL(ubidi, index) \ + ((UBiDiLevel)(!(ubidi)->defaultParaLevel || (index)<(ubidi)->paras[0].limit ? \ + (ubidi)->paraLevel : ubidi_getParaLevelAtIndex((ubidi), (index)))) + +/* number of paras entries allocated initially without malloc */ +#define SIMPLE_PARAS_COUNT 10 +/* number of isolate entries allocated initially without malloc */ +#define SIMPLE_ISOLATES_COUNT 5 +/* number of isolate run entries for paired brackets allocated initially without malloc */ +#define SIMPLE_OPENINGS_COUNT 20 + +#define CR 0x000D +#define LF 0x000A + +/* Run structure for reordering --------------------------------------------- */ +enum { + LRM_BEFORE=1, + LRM_AFTER=2, + RLM_BEFORE=4, + RLM_AFTER=8 +}; + +typedef struct Para { + int32_t limit; + int32_t level; +} Para; + +enum { /* flags for Opening.flags */ + FOUND_L=DIRPROP_FLAG(L), + FOUND_R=DIRPROP_FLAG(R) +}; + +typedef struct Opening { + int32_t position; /* position of opening bracket */ + int32_t match; /* matching char or -position of closing bracket */ + int32_t contextPos; /* position of last strong char found before opening */ + uint16_t flags; /* bits for L or R/AL found within the pair */ + UBiDiDirection contextDir; /* L or R according to last strong char before opening */ + uint8_t filler; /* to complete a nice multiple of 4 chars */ +} Opening; + +typedef struct IsoRun { + int32_t contextPos; /* position of char determining context */ + uint16_t start; /* index of first opening entry for this run */ + uint16_t limit; /* index after last opening entry for this run */ + UBiDiLevel level; /* level of this run */ + DirProp lastStrong; /* bidi class of last strong char found in this run */ + DirProp lastBase; /* bidi class of last base char found in this run */ + UBiDiDirection contextDir; /* L or R to use as context for following openings */ +} IsoRun; + +typedef struct BracketData { + UBiDi *pBiDi; + /* array of opening entries which should be enough in most cases; no malloc() */ + Opening simpleOpenings[SIMPLE_OPENINGS_COUNT]; + Opening *openings; /* pointer to current array of entries */ + int32_t openingsCount; /* number of allocated entries */ + int32_t isoRunLast; /* index of last used entry */ + /* array of nested isolated sequence entries; can never excess UBIDI_MAX_EXPLICIT_LEVEL + + 1 for index 0, + 1 for before the first isolated sequence */ + IsoRun isoRuns[UBIDI_MAX_EXPLICIT_LEVEL+2]; + UBool isNumbersSpecial; /* reordering mode for NUMBERS_SPECIAL */ +} BracketData; + +typedef struct Isolate { + int32_t startON; + int32_t start1; + int32_t state; + int16_t stateImp; +} Isolate; + +typedef struct Run { + int32_t logicalStart, /* first character of the run; b31 indicates even/odd level */ + visualLimit, /* last visual position of the run +1 */ + insertRemove; /* if >0, flags for inserting LRM/RLM before/after run, + if <0, count of bidi controls within run */ +} Run; + +/* in a Run, logicalStart will get this bit set if the run level is odd */ +#define INDEX_ODD_BIT (1UL<<31) + +#define MAKE_INDEX_ODD_PAIR(index, level) ((index)|((int32_t)((level)&1)<<31)) +#define ADD_ODD_BIT_FROM_LEVEL(x, level) ((x)|=((int32_t)((level)&1)<<31)) +#define REMOVE_ODD_BIT(x) ((x)&=~INDEX_ODD_BIT) + +#define GET_INDEX(x) ((x)&~INDEX_ODD_BIT) +#define GET_ODD_BIT(x) ((uint32_t)(x)>>31) +#define IS_ODD_RUN(x) ((UBool)(((x)&INDEX_ODD_BIT)!=0)) +#define IS_EVEN_RUN(x) ((UBool)(((x)&INDEX_ODD_BIT)==0)) + +U_CFUNC UBool +ubidi_getRuns(UBiDi *pBiDi, UErrorCode *pErrorCode); + +/** BiDi control code points */ +enum { + ZWNJ_CHAR=0x200c, + ZWJ_CHAR, + LRM_CHAR, + RLM_CHAR, + LRE_CHAR=0x202a, + RLE_CHAR, + PDF_CHAR, + LRO_CHAR, + RLO_CHAR, + LRI_CHAR=0x2066, + RLI_CHAR, + FSI_CHAR, + PDI_CHAR +}; + +#define IS_BIDI_CONTROL_CHAR(c) (((uint32_t)(c)&0xfffffffc)==ZWNJ_CHAR || (uint32_t)((c)-LRE_CHAR)<5 || (uint32_t)((c)-LRI_CHAR)<4) + +/* InsertPoints structure for noting where to put BiDi marks ---------------- */ + +typedef struct Point { + int32_t pos; /* position in text */ + int32_t flag; /* flag for LRM/RLM, before/after */ +} Point; + +typedef struct InsertPoints { + int32_t capacity; /* number of points allocated */ + int32_t size; /* number of points used */ + int32_t confirmed; /* number of points confirmed */ + UErrorCode errorCode; /* for eventual memory shortage */ + Point *points; /* pointer to array of points */ +} InsertPoints; + + +/* UBiDi structure ----------------------------------------------------------- */ + +struct UBiDi { + /* pointer to parent paragraph object (pointer to self if this object is + * a paragraph object); set to NULL in a newly opened object; set to a + * real value after a successful execution of ubidi_setPara or ubidi_setLine + */ + const UBiDi * pParaBiDi; + + /* alias pointer to the current text */ + const UChar *text; + + /* length of the current text */ + int32_t originalLength; + + /* if the UBIDI_OPTION_STREAMING option is set, this is the length + * of text actually processed by ubidi_setPara, which may be shorter than + * the original length. + * Otherwise, it is identical to the original length. + */ + int32_t length; + + /* if the UBIDI_OPTION_REMOVE_CONTROLS option is set, and/or + * marks are allowed to be inserted in one of the reordering mode, the + * length of the result string may be different from the processed length. + */ + int32_t resultLength; + + /* memory sizes in bytes */ + int32_t dirPropsSize, levelsSize, openingsSize, parasSize, runsSize, isolatesSize; + + /* allocated memory */ + DirProp *dirPropsMemory; + UBiDiLevel *levelsMemory; + Opening *openingsMemory; + Para *parasMemory; + Run *runsMemory; + Isolate *isolatesMemory; + + /* indicators for whether memory may be allocated after ubidi_open() */ + UBool mayAllocateText, mayAllocateRuns; + + /* arrays with one value per text-character */ + DirProp *dirProps; + UBiDiLevel *levels; + + /* are we performing an approximation of the "inverse BiDi" algorithm? */ + UBool isInverse; + + /* are we using the basic algorithm or its variation? */ + UBiDiReorderingMode reorderingMode; + + /* UBIDI_REORDER_xxx values must be ordered so that all the regular + * logical to visual modes come first, and all inverse BiDi modes + * come last. + */ + #define UBIDI_REORDER_LAST_LOGICAL_TO_VISUAL UBIDI_REORDER_NUMBERS_SPECIAL + + /* bitmask for reordering options */ + uint32_t reorderingOptions; + + /* must block separators receive level 0? */ + UBool orderParagraphsLTR; + + /* the paragraph level */ + UBiDiLevel paraLevel; + /* original paraLevel when contextual */ + /* must be one of UBIDI_DEFAULT_xxx or 0 if not contextual */ + UBiDiLevel defaultParaLevel; + + /* context data */ + const UChar *prologue; + int32_t proLength; + const UChar *epilogue; + int32_t epiLength; + + /* the following is set in ubidi_setPara, used in processPropertySeq */ + const struct ImpTabPair * pImpTabPair; /* pointer to levels state table pair */ + + /* the overall paragraph or line directionality - see UBiDiDirection */ + UBiDiDirection direction; + + /* flags is a bit set for which directional properties are in the text */ + Flags flags; + + /* lastArabicPos is index to the last AL in the text, -1 if none */ + int32_t lastArabicPos; + + /* characters after trailingWSStart are WS and are */ + /* implicitly at the paraLevel (rule (L1)) - levels may not reflect that */ + int32_t trailingWSStart; + + /* fields for paragraph handling */ + int32_t paraCount; /* set in getDirProps() */ + /* filled in getDirProps() */ + Para *paras; + + /* for relatively short text, we only need a tiny array of paras (no malloc()) */ + Para simpleParas[SIMPLE_PARAS_COUNT]; + + /* fields for line reordering */ + int32_t runCount; /* ==-1: runs not set up yet */ + Run *runs; + + /* for non-mixed text, we only need a tiny array of runs (no malloc()) */ + Run simpleRuns[1]; + + /* maximum or current nesting depth of isolate sequences */ + /* Within resolveExplicitLevels() and checkExplicitLevels(), this is the maximal + nesting encountered. + Within resolveImplicitLevels(), this is the index of the current isolates + stack entry. */ + int32_t isolateCount; + Isolate *isolates; + + /* for simple text, have a small stack (no malloc()) */ + Isolate simpleIsolates[SIMPLE_ISOLATES_COUNT]; + + /* for inverse Bidi with insertion of directional marks */ + InsertPoints insertPoints; + + /* for option UBIDI_OPTION_REMOVE_CONTROLS */ + int32_t controlCount; + + /* for Bidi class callback */ + UBiDiClassCallback *fnClassCallback; /* action pointer */ + const void *coClassCallback; /* context pointer */ +}; + +#define IS_VALID_PARA(x) ((x) && ((x)->pParaBiDi==(x))) +#define IS_VALID_PARA_OR_LINE(x) ((x) && ((x)->pParaBiDi==(x) || (((x)->pParaBiDi) && (x)->pParaBiDi->pParaBiDi==(x)->pParaBiDi))) + +typedef union { + DirProp *dirPropsMemory; + UBiDiLevel *levelsMemory; + Opening *openingsMemory; + Para *parasMemory; + Run *runsMemory; + Isolate *isolatesMemory; +} BidiMemoryForAllocation; + +/* Macros for initial checks at function entry */ +#define RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrcode, retvalue) UPRV_BLOCK_MACRO_BEGIN { \ + if((pErrcode)==NULL || U_FAILURE(*pErrcode)) return retvalue; \ +} UPRV_BLOCK_MACRO_END +#define RETURN_IF_NOT_VALID_PARA(bidi, errcode, retvalue) UPRV_BLOCK_MACRO_BEGIN { \ + if(!IS_VALID_PARA(bidi)) { \ + errcode=U_INVALID_STATE_ERROR; \ + return retvalue; \ + } \ +} UPRV_BLOCK_MACRO_END +#define RETURN_IF_NOT_VALID_PARA_OR_LINE(bidi, errcode, retvalue) UPRV_BLOCK_MACRO_BEGIN { \ + if(!IS_VALID_PARA_OR_LINE(bidi)) { \ + errcode=U_INVALID_STATE_ERROR; \ + return retvalue; \ + } \ +} UPRV_BLOCK_MACRO_END +#define RETURN_IF_BAD_RANGE(arg, start, limit, errcode, retvalue) UPRV_BLOCK_MACRO_BEGIN { \ + if((arg)<(start) || (arg)>=(limit)) { \ + (errcode)=U_ILLEGAL_ARGUMENT_ERROR; \ + return retvalue; \ + } \ +} UPRV_BLOCK_MACRO_END + +#define RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrcode) UPRV_BLOCK_MACRO_BEGIN { \ + if((pErrcode)==NULL || U_FAILURE(*pErrcode)) return; \ +} UPRV_BLOCK_MACRO_END +#define RETURN_VOID_IF_NOT_VALID_PARA(bidi, errcode) UPRV_BLOCK_MACRO_BEGIN { \ + if(!IS_VALID_PARA(bidi)) { \ + errcode=U_INVALID_STATE_ERROR; \ + return; \ + } \ +} UPRV_BLOCK_MACRO_END +#define RETURN_VOID_IF_NOT_VALID_PARA_OR_LINE(bidi, errcode) UPRV_BLOCK_MACRO_BEGIN { \ + if(!IS_VALID_PARA_OR_LINE(bidi)) { \ + errcode=U_INVALID_STATE_ERROR; \ + return; \ + } \ +} UPRV_BLOCK_MACRO_END +#define RETURN_VOID_IF_BAD_RANGE(arg, start, limit, errcode) UPRV_BLOCK_MACRO_BEGIN { \ + if((arg)<(start) || (arg)>=(limit)) { \ + (errcode)=U_ILLEGAL_ARGUMENT_ERROR; \ + return; \ + } \ +} UPRV_BLOCK_MACRO_END + +/* helper function to (re)allocate memory if allowed */ +U_CFUNC UBool +ubidi_getMemory(BidiMemoryForAllocation *pMemory, int32_t *pSize, UBool mayAllocate, int32_t sizeNeeded); + +/* helper macros for each allocated array in UBiDi */ +#define getDirPropsMemory(pBiDi, length) \ + ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->dirPropsMemory, &(pBiDi)->dirPropsSize, \ + (pBiDi)->mayAllocateText, (length)) + +#define getLevelsMemory(pBiDi, length) \ + ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->levelsMemory, &(pBiDi)->levelsSize, \ + (pBiDi)->mayAllocateText, (length)) + +#define getRunsMemory(pBiDi, length) \ + ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->runsMemory, &(pBiDi)->runsSize, \ + (pBiDi)->mayAllocateRuns, (length)*sizeof(Run)) + +/* additional macros used by ubidi_open() - always allow allocation */ +#define getInitialDirPropsMemory(pBiDi, length) \ + ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->dirPropsMemory, &(pBiDi)->dirPropsSize, \ + true, (length)) + +#define getInitialLevelsMemory(pBiDi, length) \ + ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->levelsMemory, &(pBiDi)->levelsSize, \ + true, (length)) + +#define getInitialOpeningsMemory(pBiDi, length) \ + ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->openingsMemory, &(pBiDi)->openingsSize, \ + true, (length)*sizeof(Opening)) + +#define getInitialParasMemory(pBiDi, length) \ + ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->parasMemory, &(pBiDi)->parasSize, \ + true, (length)*sizeof(Para)) + +#define getInitialRunsMemory(pBiDi, length) \ + ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->runsMemory, &(pBiDi)->runsSize, \ + true, (length)*sizeof(Run)) + +#define getInitialIsolatesMemory(pBiDi, length) \ + ubidi_getMemory((BidiMemoryForAllocation *)&(pBiDi)->isolatesMemory, &(pBiDi)->isolatesSize, \ + true, (length)*sizeof(Isolate)) + +#endif diff --git a/deps/icu-small/source/common/ubidiln.cpp b/deps/icu-small/source/common/ubidiln.cpp index 430ece39d28e3a..0d747464e6b21a 100644 --- a/deps/icu-small/source/common/ubidiln.cpp +++ b/deps/icu-small/source/common/ubidiln.cpp @@ -1,1347 +1,1347 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: ubidiln.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999aug06 -* created by: Markus W. Scherer, updated by Matitiahu Allouche -*/ - -#include "cmemory.h" -#include "unicode/utypes.h" -#include "unicode/ustring.h" -#include "unicode/uchar.h" -#include "unicode/ubidi.h" -#include "ubidiimp.h" -#include "uassert.h" - -/* - * General remarks about the functions in this file: - * - * These functions deal with the aspects of potentially mixed-directional - * text in a single paragraph or in a line of a single paragraph - * which has already been processed according to - * the Unicode 6.3 BiDi algorithm as defined in - * https://www.unicode.org/reports/tr9/ , version 28, - * also described in The Unicode Standard, Version 6.3.0 . - * - * This means that there is a UBiDi object with a levels - * and a dirProps array. - * paraLevel and direction are also set. - * Only if the length of the text is zero, then levels==dirProps==NULL. - * - * The overall directionality of the paragraph - * or line is used to bypass the reordering steps if possible. - * Even purely RTL text does not need reordering there because - * the ubidi_getLogical/VisualIndex() functions can compute the - * index on the fly in such a case. - * - * The implementation of the access to same-level-runs and of the reordering - * do attempt to provide better performance and less memory usage compared to - * a direct implementation of especially rule (L2) with an array of - * one (32-bit) integer per text character. - * - * Here, the levels array is scanned as soon as necessary, and a vector of - * same-level-runs is created. Reordering then is done on this vector. - * For each run of text positions that were resolved to the same level, - * only 8 bytes are stored: the first text position of the run and the visual - * position behind the run after reordering. - * One sign bit is used to hold the directionality of the run. - * This is inefficient if there are many very short runs. If the average run - * length is <2, then this uses more memory. - * - * In a further attempt to save memory, the levels array is never changed - * after all the resolution rules (Xn, Wn, Nn, In). - * Many functions have to consider the field trailingWSStart: - * if it is less than length, then there is an implicit trailing run - * at the paraLevel, - * which is not reflected in the levels array. - * This allows a line UBiDi object to use the same levels array as - * its paragraph parent object. - * - * When a UBiDi object is created for a line of a paragraph, then the - * paragraph's levels and dirProps arrays are reused by way of setting - * a pointer into them, not by copying. This again saves memory and forbids to - * change the now shared levels for (L1). - */ - -/* handle trailing WS (L1) -------------------------------------------------- */ - -/* - * setTrailingWSStart() sets the start index for a trailing - * run of WS in the line. This is necessary because we do not modify - * the paragraph's levels array that we just point into. - * Using trailingWSStart is another form of performing (L1). - * - * To make subsequent operations easier, we also include the run - * before the WS if it is at the paraLevel - we merge the two here. - * - * This function is called only from ubidi_setLine(), so pBiDi->paraLevel is - * set correctly for the line even when contextual multiple paragraphs. - */ -static void -setTrailingWSStart(UBiDi *pBiDi) { - /* pBiDi->direction!=UBIDI_MIXED */ - - const DirProp *dirProps=pBiDi->dirProps; - UBiDiLevel *levels=pBiDi->levels; - int32_t start=pBiDi->length; - UBiDiLevel paraLevel=pBiDi->paraLevel; - - /* If the line is terminated by a block separator, all preceding WS etc... - are already set to paragraph level. - Setting trailingWSStart to pBidi->length will avoid changing the - level of B chars from 0 to paraLevel in ubidi_getLevels when - orderParagraphsLTR==true. - */ - if(dirProps[start-1]==B) { - pBiDi->trailingWSStart=start; /* currently == pBiDi->length */ - return; - } - /* go backwards across all WS, BN, explicit codes */ - while(start>0 && DIRPROP_FLAG(dirProps[start-1])&MASK_WS) { - --start; - } - - /* if the WS run can be merged with the previous run then do so here */ - while(start>0 && levels[start-1]==paraLevel) { - --start; - } - - pBiDi->trailingWSStart=start; -} - -/* ubidi_setLine ------------------------------------------------------------ */ - -U_CAPI void U_EXPORT2 -ubidi_setLine(const UBiDi *pParaBiDi, - int32_t start, int32_t limit, - UBiDi *pLineBiDi, - UErrorCode *pErrorCode) { - int32_t length; - - /* check the argument values */ - RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); - RETURN_VOID_IF_NOT_VALID_PARA(pParaBiDi, *pErrorCode); - RETURN_VOID_IF_BAD_RANGE(start, 0, limit, *pErrorCode); - RETURN_VOID_IF_BAD_RANGE(limit, 0, pParaBiDi->length+1, *pErrorCode); - if(pLineBiDi==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if(ubidi_getParagraph(pParaBiDi, start, NULL, NULL, NULL, pErrorCode) != - ubidi_getParagraph(pParaBiDi, limit-1, NULL, NULL, NULL, pErrorCode)) { - /* the line crosses a paragraph boundary */ - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - /* set the values in pLineBiDi from its pParaBiDi parent */ - pLineBiDi->pParaBiDi=NULL; /* mark unfinished setLine */ - pLineBiDi->text=pParaBiDi->text+start; - length=pLineBiDi->length=limit-start; - pLineBiDi->resultLength=pLineBiDi->originalLength=length; - pLineBiDi->paraLevel=GET_PARALEVEL(pParaBiDi, start); - pLineBiDi->paraCount=pParaBiDi->paraCount; - pLineBiDi->runs=NULL; - pLineBiDi->flags=0; - pLineBiDi->reorderingMode=pParaBiDi->reorderingMode; - pLineBiDi->reorderingOptions=pParaBiDi->reorderingOptions; - pLineBiDi->controlCount=0; - if(pParaBiDi->controlCount>0) { - int32_t j; - for(j=start; jtext[j])) { - pLineBiDi->controlCount++; - } - } - pLineBiDi->resultLength-=pLineBiDi->controlCount; - } - - pLineBiDi->dirProps=pParaBiDi->dirProps+start; - pLineBiDi->levels=pParaBiDi->levels+start; - pLineBiDi->runCount=-1; - - if(pParaBiDi->direction!=UBIDI_MIXED) { - /* the parent is already trivial */ - pLineBiDi->direction=pParaBiDi->direction; - - /* - * The parent's levels are all either - * implicitly or explicitly ==paraLevel; - * do the same here. - */ - if(pParaBiDi->trailingWSStart<=start) { - pLineBiDi->trailingWSStart=0; - } else if(pParaBiDi->trailingWSStarttrailingWSStart=pParaBiDi->trailingWSStart-start; - } else { - pLineBiDi->trailingWSStart=length; - } - } else { - const UBiDiLevel *levels=pLineBiDi->levels; - int32_t i, trailingWSStart; - UBiDiLevel level; - - setTrailingWSStart(pLineBiDi); - trailingWSStart=pLineBiDi->trailingWSStart; - - /* recalculate pLineBiDi->direction */ - if(trailingWSStart==0) { - /* all levels are at paraLevel */ - pLineBiDi->direction=(UBiDiDirection)(pLineBiDi->paraLevel&1); - } else { - /* get the level of the first character */ - level=(UBiDiLevel)(levels[0]&1); - - /* if there is anything of a different level, then the line is mixed */ - if(trailingWSStartparaLevel&1)!=level) { - /* the trailing WS is at paraLevel, which differs from levels[0] */ - pLineBiDi->direction=UBIDI_MIXED; - } else { - /* see if levels[1..trailingWSStart-1] have the same direction as levels[0] and paraLevel */ - i=1; - for(;;) { - if(i==trailingWSStart) { - /* the direction values match those in level */ - pLineBiDi->direction=(UBiDiDirection)level; - break; - } else if((levels[i]&1)!=level) { - pLineBiDi->direction=UBIDI_MIXED; - break; - } - ++i; - } - } - } - - switch(pLineBiDi->direction) { - case UBIDI_LTR: - /* make sure paraLevel is even */ - pLineBiDi->paraLevel=(UBiDiLevel)((pLineBiDi->paraLevel+1)&~1); - - /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */ - pLineBiDi->trailingWSStart=0; - break; - case UBIDI_RTL: - /* make sure paraLevel is odd */ - pLineBiDi->paraLevel|=1; - - /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */ - pLineBiDi->trailingWSStart=0; - break; - default: - break; - } - } - pLineBiDi->pParaBiDi=pParaBiDi; /* mark successful setLine */ - return; -} - -U_CAPI UBiDiLevel U_EXPORT2 -ubidi_getLevelAt(const UBiDi *pBiDi, int32_t charIndex) { - /* return paraLevel if in the trailing WS run, otherwise the real level */ - if(!IS_VALID_PARA_OR_LINE(pBiDi) || charIndex<0 || pBiDi->length<=charIndex) { - return 0; - } else if(pBiDi->direction!=UBIDI_MIXED || charIndex>=pBiDi->trailingWSStart) { - return GET_PARALEVEL(pBiDi, charIndex); - } else { - return pBiDi->levels[charIndex]; - } -} - -U_CAPI const UBiDiLevel * U_EXPORT2 -ubidi_getLevels(UBiDi *pBiDi, UErrorCode *pErrorCode) { - int32_t start, length; - - RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, NULL); - RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, NULL); - if((length=pBiDi->length)<=0) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - if((start=pBiDi->trailingWSStart)==length) { - /* the current levels array reflects the WS run */ - return pBiDi->levels; - } - - /* - * After the previous if(), we know that the levels array - * has an implicit trailing WS run and therefore does not fully - * reflect itself all the levels. - * This must be a UBiDi object for a line, and - * we need to create a new levels array. - */ - if(getLevelsMemory(pBiDi, length)) { - UBiDiLevel *levels=pBiDi->levelsMemory; - - if(start>0 && levels!=pBiDi->levels) { - uprv_memcpy(levels, pBiDi->levels, start); - } - /* pBiDi->paraLevel is ok even if contextual multiple paragraphs, - since pBidi is a line object */ - uprv_memset(levels+start, pBiDi->paraLevel, length-start); - - /* this new levels array is set for the line and reflects the WS run */ - pBiDi->trailingWSStart=length; - return pBiDi->levels=levels; - } else { - /* out of memory */ - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } -} - -U_CAPI void U_EXPORT2 -ubidi_getLogicalRun(const UBiDi *pBiDi, int32_t logicalPosition, - int32_t *pLogicalLimit, UBiDiLevel *pLevel) { - UErrorCode errorCode; - int32_t runCount, visualStart, logicalLimit, logicalFirst, i; - Run iRun; - - errorCode=U_ZERO_ERROR; - RETURN_VOID_IF_BAD_RANGE(logicalPosition, 0, pBiDi->length, errorCode); - /* ubidi_countRuns will check VALID_PARA_OR_LINE */ - runCount=ubidi_countRuns((UBiDi *)pBiDi, &errorCode); - if(U_FAILURE(errorCode)) { - return; - } - /* this is done based on runs rather than on levels since levels have - a special interpretation when UBIDI_REORDER_RUNS_ONLY - */ - visualStart=logicalLimit=0; - iRun=pBiDi->runs[0]; - - for(i=0; iruns[i]; - logicalFirst=GET_INDEX(iRun.logicalStart); - logicalLimit=logicalFirst+iRun.visualLimit-visualStart; - if((logicalPosition>=logicalFirst) && - (logicalPositionreorderingMode==UBIDI_REORDER_RUNS_ONLY) { - *pLevel=(UBiDiLevel)GET_ODD_BIT(iRun.logicalStart); - } - else if(pBiDi->direction!=UBIDI_MIXED || logicalPosition>=pBiDi->trailingWSStart) { - *pLevel=GET_PARALEVEL(pBiDi, logicalPosition); - } else { - *pLevel=pBiDi->levels[logicalPosition]; - } - } -} - -/* runs API functions ------------------------------------------------------- */ - -U_CAPI int32_t U_EXPORT2 -ubidi_countRuns(UBiDi *pBiDi, UErrorCode *pErrorCode) { - RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1); - RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1); - ubidi_getRuns(pBiDi, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return -1; - } - return pBiDi->runCount; -} - -U_CAPI UBiDiDirection U_EXPORT2 -ubidi_getVisualRun(UBiDi *pBiDi, int32_t runIndex, - int32_t *pLogicalStart, int32_t *pLength) -{ - int32_t start; - UErrorCode errorCode = U_ZERO_ERROR; - RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, errorCode, UBIDI_LTR); - ubidi_getRuns(pBiDi, &errorCode); - if(U_FAILURE(errorCode)) { - return UBIDI_LTR; - } - RETURN_IF_BAD_RANGE(runIndex, 0, pBiDi->runCount, errorCode, UBIDI_LTR); - - start=pBiDi->runs[runIndex].logicalStart; - if(pLogicalStart!=NULL) { - *pLogicalStart=GET_INDEX(start); - } - if(pLength!=NULL) { - if(runIndex>0) { - *pLength=pBiDi->runs[runIndex].visualLimit- - pBiDi->runs[runIndex-1].visualLimit; - } else { - *pLength=pBiDi->runs[0].visualLimit; - } - } - return (UBiDiDirection)GET_ODD_BIT(start); -} - -/* in trivial cases there is only one trivial run; called by ubidi_getRuns() */ -static void -getSingleRun(UBiDi *pBiDi, UBiDiLevel level) { - /* simple, single-run case */ - pBiDi->runs=pBiDi->simpleRuns; - pBiDi->runCount=1; - - /* fill and reorder the single run */ - pBiDi->runs[0].logicalStart=MAKE_INDEX_ODD_PAIR(0, level); - pBiDi->runs[0].visualLimit=pBiDi->length; - pBiDi->runs[0].insertRemove=0; -} - -/* reorder the runs array (L2) ---------------------------------------------- */ - -/* - * Reorder the same-level runs in the runs array. - * Here, runCount>1 and maxLevel>=minLevel>=paraLevel. - * All the visualStart fields=logical start before reordering. - * The "odd" bits are not set yet. - * - * Reordering with this data structure lends itself to some handy shortcuts: - * - * Since each run is moved but not modified, and since at the initial maxLevel - * each sequence of same-level runs consists of only one run each, we - * don't need to do anything there and can predecrement maxLevel. - * In many simple cases, the reordering is thus done entirely in the - * index mapping. - * Also, reordering occurs only down to the lowest odd level that occurs, - * which is minLevel|1. However, if the lowest level itself is odd, then - * in the last reordering the sequence of the runs at this level or higher - * will be all runs, and we don't need the elaborate loop to search for them. - * This is covered by ++minLevel instead of minLevel|=1 followed - * by an extra reorder-all after the reorder-some loop. - * About a trailing WS run: - * Such a run would need special treatment because its level is not - * reflected in levels[] if this is not a paragraph object. - * Instead, all characters from trailingWSStart on are implicitly at - * paraLevel. - * However, for all maxLevel>paraLevel, this run will never be reordered - * and does not need to be taken into account. maxLevel==paraLevel is only reordered - * if minLevel==paraLevel is odd, which is done in the extra segment. - * This means that for the main reordering loop we don't need to consider - * this run and can --runCount. If it is later part of the all-runs - * reordering, then runCount is adjusted accordingly. - */ -static void -reorderLine(UBiDi *pBiDi, UBiDiLevel minLevel, UBiDiLevel maxLevel) { - Run *runs, tempRun; - UBiDiLevel *levels; - int32_t firstRun, endRun, limitRun, runCount; - - /* nothing to do? */ - if(maxLevel<=(minLevel|1)) { - return; - } - - /* - * Reorder only down to the lowest odd level - * and reorder at an odd minLevel in a separate, simpler loop. - * See comments above for why minLevel is always incremented. - */ - ++minLevel; - - runs=pBiDi->runs; - levels=pBiDi->levels; - runCount=pBiDi->runCount; - - /* do not include the WS run at paraLevel<=old minLevel except in the simple loop */ - if(pBiDi->trailingWSStartlength) { - --runCount; - } - - while(--maxLevel>=minLevel) { - firstRun=0; - - /* loop for all sequences of runs */ - for(;;) { - /* look for a sequence of runs that are all at >=maxLevel */ - /* look for the first run of such a sequence */ - while(firstRun=runCount) { - break; /* no more such runs */ - } - - /* look for the limit run of such a sequence (the run behind it) */ - for(limitRun=firstRun; ++limitRun=maxLevel;) {} - - /* Swap the entire sequence of runs from firstRun to limitRun-1. */ - endRun=limitRun-1; - while(firstRuntrailingWSStart==pBiDi->length) { - --runCount; - } - - /* Swap the entire sequence of all runs. (endRun==runCount) */ - while(firstRunruns; - int32_t runCount=pBiDi->runCount, visualStart=0, i, length, logicalStart; - - for(i=0; i=logicalStart) && (logicalIndex<(logicalStart+length))) { - return i; - } - visualStart+=length; - } - /* we should never get here */ - UPRV_UNREACHABLE_EXIT; -} - -/* - * Compute the runs array from the levels array. - * After ubidi_getRuns() returns true, runCount is guaranteed to be >0 - * and the runs are reordered. - * Odd-level runs have visualStart on their visual right edge and - * they progress visually to the left. - * If option UBIDI_OPTION_INSERT_MARKS is set, insertRemove will contain the - * sum of appropriate LRM/RLM_BEFORE/AFTER flags. - * If option UBIDI_OPTION_REMOVE_CONTROLS is set, insertRemove will contain the - * negative number of BiDi control characters within this run. - */ -U_CFUNC UBool -ubidi_getRuns(UBiDi *pBiDi, UErrorCode*) { - /* - * This method returns immediately if the runs are already set. This - * includes the case of length==0 (handled in setPara).. - */ - if (pBiDi->runCount>=0) { - return true; - } - - if(pBiDi->direction!=UBIDI_MIXED) { - /* simple, single-run case - this covers length==0 */ - /* pBiDi->paraLevel is ok even for contextual multiple paragraphs */ - getSingleRun(pBiDi, pBiDi->paraLevel); - } else /* UBIDI_MIXED, length>0 */ { - /* mixed directionality */ - int32_t length=pBiDi->length, limit; - UBiDiLevel *levels=pBiDi->levels; - int32_t i, runCount; - UBiDiLevel level=UBIDI_DEFAULT_LTR; /* initialize with no valid level */ - /* - * If there are WS characters at the end of the line - * and the run preceding them has a level different from - * paraLevel, then they will form their own run at paraLevel (L1). - * Count them separately. - * We need some special treatment for this in order to not - * modify the levels array which a line UBiDi object shares - * with its paragraph parent and its other line siblings. - * In other words, for the trailing WS, it may be - * levels[]!=paraLevel but we have to treat it like it were so. - */ - limit=pBiDi->trailingWSStart; - /* count the runs, there is at least one non-WS run, and limit>0 */ - runCount=0; - for(i=0; i1 || limit1 */ - if(getRunsMemory(pBiDi, runCount)) { - runs=pBiDi->runsMemory; - } else { - return false; - } - - /* set the runs */ - /* FOOD FOR THOUGHT: this could be optimized, e.g.: - * 464->444, 484->444, 575->555, 595->555 - * However, that would take longer. Check also how it would - * interact with BiDi control removal and inserting Marks. - */ - runIndex=0; - - /* search for the run limits and initialize visualLimit values with the run lengths */ - i=0; - do { - /* prepare this run */ - start=i; - level=levels[i]; - if(levelmaxLevel) { - maxLevel=level; - } - - /* look for the run limit */ - while(++iparaLevel is ok even - if contextual multiple paragraphs. */ - if(pBiDi->paraLevelparaLevel; - } - } - - /* set the object fields */ - pBiDi->runs=runs; - pBiDi->runCount=runCount; - - reorderLine(pBiDi, minLevel, maxLevel); - - /* now add the direction flags and adjust the visualLimit's to be just that */ - /* this loop will also handle the trailing WS run */ - limit=0; - for(i=0; iparaLevel is ok even if - contextual multiple paragraphs. */ - if(runIndexparaLevel & 1) != 0)? 0 : runIndex; - - ADD_ODD_BIT_FROM_LEVEL(runs[trailingRun].logicalStart, pBiDi->paraLevel); - } - } - } - - /* handle insert LRM/RLM BEFORE/AFTER run */ - if(pBiDi->insertPoints.size>0) { - Point *point, *start=pBiDi->insertPoints.points, - *limit=start+pBiDi->insertPoints.size; - int32_t runIndex; - for(point=start; pointpos); - pBiDi->runs[runIndex].insertRemove|=point->flag; - } - } - - /* handle remove BiDi control characters */ - if(pBiDi->controlCount>0) { - int32_t runIndex; - const UChar *start=pBiDi->text, *limit=start+pBiDi->length, *pu; - for(pu=start; puruns[runIndex].insertRemove--; - } - } - } - - return true; -} - -static UBool -prepareReorder(const UBiDiLevel *levels, int32_t length, - int32_t *indexMap, - UBiDiLevel *pMinLevel, UBiDiLevel *pMaxLevel) { - int32_t start; - UBiDiLevel level, minLevel, maxLevel; - - if(levels==NULL || length<=0) { - return false; - } - - /* determine minLevel and maxLevel */ - minLevel=UBIDI_MAX_EXPLICIT_LEVEL+1; - maxLevel=0; - for(start=length; start>0;) { - level=levels[--start]; - if(level>UBIDI_MAX_EXPLICIT_LEVEL+1) { - return false; - } - if(levelmaxLevel) { - maxLevel=level; - } - } - *pMinLevel=minLevel; - *pMaxLevel=maxLevel; - - /* initialize the index map */ - for(start=length; start>0;) { - --start; - indexMap[start]=start; - } - - return true; -} - -/* reorder a line based on a levels array (L2) ------------------------------ */ - -U_CAPI void U_EXPORT2 -ubidi_reorderLogical(const UBiDiLevel *levels, int32_t length, int32_t *indexMap) { - int32_t start, limit, sumOfSosEos; - UBiDiLevel minLevel = 0, maxLevel = 0; - - if(indexMap==NULL || !prepareReorder(levels, length, indexMap, &minLevel, &maxLevel)) { - return; - } - - /* nothing to do? */ - if(minLevel==maxLevel && (minLevel&1)==0) { - return; - } - - /* reorder only down to the lowest odd level */ - minLevel|=1; - - /* loop maxLevel..minLevel */ - do { - start=0; - - /* loop for all sequences of levels to reorder at the current maxLevel */ - for(;;) { - /* look for a sequence of levels that are all at >=maxLevel */ - /* look for the first index of such a sequence */ - while(start=length) { - break; /* no more such sequences */ - } - - /* look for the limit of such a sequence (the index behind it) */ - for(limit=start; ++limit=maxLevel;) {} - - /* - * sos=start of sequence, eos=end of sequence - * - * The closed (inclusive) interval from sos to eos includes all the logical - * and visual indexes within this sequence. They are logically and - * visually contiguous and in the same range. - * - * For each run, the new visual index=sos+eos-old visual index; - * we pre-add sos+eos into sumOfSosEos -> - * new visual index=sumOfSosEos-old visual index; - */ - sumOfSosEos=start+limit-1; - - /* reorder each index in the sequence */ - do { - indexMap[start]=sumOfSosEos-indexMap[start]; - } while(++start=minLevel); -} - -U_CAPI void U_EXPORT2 -ubidi_reorderVisual(const UBiDiLevel *levels, int32_t length, int32_t *indexMap) { - int32_t start, end, limit, temp; - UBiDiLevel minLevel = 0, maxLevel = 0; - - if(indexMap==NULL || !prepareReorder(levels, length, indexMap, &minLevel, &maxLevel)) { - return; - } - - /* nothing to do? */ - if(minLevel==maxLevel && (minLevel&1)==0) { - return; - } - - /* reorder only down to the lowest odd level */ - minLevel|=1; - - /* loop maxLevel..minLevel */ - do { - start=0; - - /* loop for all sequences of levels to reorder at the current maxLevel */ - for(;;) { - /* look for a sequence of levels that are all at >=maxLevel */ - /* look for the first index of such a sequence */ - while(start=length) { - break; /* no more such runs */ - } - - /* look for the limit of such a sequence (the index behind it) */ - for(limit=start; ++limit=maxLevel;) {} - - /* - * Swap the entire interval of indexes from start to limit-1. - * We don't need to swap the levels for the purpose of this - * algorithm: the sequence of levels that we look at does not - * move anyway. - */ - end=limit-1; - while(start=minLevel); -} - -/* API functions for logical<->visual mapping ------------------------------- */ - -U_CAPI int32_t U_EXPORT2 -ubidi_getVisualIndex(UBiDi *pBiDi, int32_t logicalIndex, UErrorCode *pErrorCode) { - int32_t visualIndex=UBIDI_MAP_NOWHERE; - RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1); - RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1); - RETURN_IF_BAD_RANGE(logicalIndex, 0, pBiDi->length, *pErrorCode, -1); - - /* we can do the trivial cases without the runs array */ - switch(pBiDi->direction) { - case UBIDI_LTR: - visualIndex=logicalIndex; - break; - case UBIDI_RTL: - visualIndex=pBiDi->length-logicalIndex-1; - break; - default: - if(!ubidi_getRuns(pBiDi, pErrorCode)) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return -1; - } else { - Run *runs=pBiDi->runs; - int32_t i, visualStart=0, offset, length; - - /* linear search for the run, search on the visual runs */ - for(i=0; irunCount; ++i) { - length=runs[i].visualLimit-visualStart; - offset=logicalIndex-GET_INDEX(runs[i].logicalStart); - if(offset>=0 && offset=pBiDi->runCount) { - return UBIDI_MAP_NOWHERE; - } - } - } - - if(pBiDi->insertPoints.size>0) { - /* add the number of added marks until the calculated visual index */ - Run *runs=pBiDi->runs; - int32_t i, length, insertRemove; - int32_t visualStart=0, markFound=0; - for(i=0; ; i++, visualStart+=length) { - length=runs[i].visualLimit-visualStart; - insertRemove=runs[i].insertRemove; - if(insertRemove & (LRM_BEFORE|RLM_BEFORE)) { - markFound++; - } - /* is it the run containing the visual index? */ - if(visualIndexcontrolCount>0) { - /* subtract the number of controls until the calculated visual index */ - Run *runs=pBiDi->runs; - int32_t i, j, start, limit, length, insertRemove; - int32_t visualStart=0, controlFound=0; - UChar uchar=pBiDi->text[logicalIndex]; - /* is the logical index pointing to a control ? */ - if(IS_BIDI_CONTROL_CHAR(uchar)) { - return UBIDI_MAP_NOWHERE; - } - /* loop on runs */ - for(i=0; ; i++, visualStart+=length) { - length=runs[i].visualLimit-visualStart; - insertRemove=runs[i].insertRemove; - /* calculated visual index is beyond this run? */ - if(visualIndex>=runs[i].visualLimit) { - controlFound-=insertRemove; - continue; - } - /* calculated visual index must be within current run */ - if(insertRemove==0) { - return visualIndex-controlFound; - } - if(IS_EVEN_RUN(runs[i].logicalStart)) { - /* LTR: check from run start to logical index */ - start=runs[i].logicalStart; - limit=logicalIndex; - } else { - /* RTL: check from logical index to run end */ - start=logicalIndex+1; - limit=GET_INDEX(runs[i].logicalStart)+length; - } - for(j=start; jtext[j]; - if(IS_BIDI_CONTROL_CHAR(uchar)) { - controlFound++; - } - } - return visualIndex-controlFound; - } - } - - return visualIndex; -} - -U_CAPI int32_t U_EXPORT2 -ubidi_getLogicalIndex(UBiDi *pBiDi, int32_t visualIndex, UErrorCode *pErrorCode) { - Run *runs; - int32_t i, runCount, start; - RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1); - RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1); - RETURN_IF_BAD_RANGE(visualIndex, 0, pBiDi->resultLength, *pErrorCode, -1); - /* we can do the trivial cases without the runs array */ - if(pBiDi->insertPoints.size==0 && pBiDi->controlCount==0) { - if(pBiDi->direction==UBIDI_LTR) { - return visualIndex; - } - else if(pBiDi->direction==UBIDI_RTL) { - return pBiDi->length-visualIndex-1; - } - } - if(!ubidi_getRuns(pBiDi, pErrorCode)) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return -1; - } - - runs=pBiDi->runs; - runCount=pBiDi->runCount; - if(pBiDi->insertPoints.size>0) { - /* handle inserted LRM/RLM */ - int32_t markFound=0, insertRemove; - int32_t visualStart=0, length; - runs=pBiDi->runs; - /* subtract number of marks until visual index */ - for(i=0; ; i++, visualStart+=length) { - length=runs[i].visualLimit-visualStart; - insertRemove=runs[i].insertRemove; - if(insertRemove&(LRM_BEFORE|RLM_BEFORE)) { - if(visualIndex<=(visualStart+markFound)) { - return UBIDI_MAP_NOWHERE; - } - markFound++; - } - /* is adjusted visual index within this run? */ - if(visualIndex<(runs[i].visualLimit+markFound)) { - visualIndex-=markFound; - break; - } - if(insertRemove&(LRM_AFTER|RLM_AFTER)) { - if(visualIndex==(visualStart+length+markFound)) { - return UBIDI_MAP_NOWHERE; - } - markFound++; - } - } - } - else if(pBiDi->controlCount>0) { - /* handle removed BiDi control characters */ - int32_t controlFound=0, insertRemove, length; - int32_t logicalStart, logicalEnd, visualStart=0, j, k; - UChar uchar; - UBool evenRun; - /* add number of controls until visual index */ - for(i=0; ; i++, visualStart+=length) { - length=runs[i].visualLimit-visualStart; - insertRemove=runs[i].insertRemove; - /* is adjusted visual index beyond current run? */ - if(visualIndex>=(runs[i].visualLimit-controlFound+insertRemove)) { - controlFound-=insertRemove; - continue; - } - /* adjusted visual index is within current run */ - if(insertRemove==0) { - visualIndex+=controlFound; - break; - } - /* count non-control chars until visualIndex */ - logicalStart=runs[i].logicalStart; - evenRun=IS_EVEN_RUN(logicalStart); - REMOVE_ODD_BIT(logicalStart); - logicalEnd=logicalStart+length-1; - for(j=0; jtext[k]; - if(IS_BIDI_CONTROL_CHAR(uchar)) { - controlFound++; - } - if((visualIndex+controlFound)==(visualStart+j)) { - break; - } - } - visualIndex+=controlFound; - break; - } - } - /* handle all cases */ - if(runCount<=10) { - /* linear search for the run */ - for(i=0; visualIndex>=runs[i].visualLimit; ++i) {} - } else { - /* binary search for the run */ - int32_t begin=0, limit=runCount; - - /* the middle if() is guaranteed to find the run, we don't need a loop limit */ - for(;;) { - i=(begin+limit)/2; - if(visualIndex>=runs[i].visualLimit) { - begin=i+1; - } else if(i==0 || visualIndex>=runs[i-1].visualLimit) { - break; - } else { - limit=i; - } - } - } - - start=runs[i].logicalStart; - if(IS_EVEN_RUN(start)) { - /* LTR */ - /* the offset in runs[i] is visualIndex-runs[i-1].visualLimit */ - if(i>0) { - visualIndex-=runs[i-1].visualLimit; - } - return start+visualIndex; - } else { - /* RTL */ - return GET_INDEX(start)+runs[i].visualLimit-visualIndex-1; - } -} - -U_CAPI void U_EXPORT2 -ubidi_getLogicalMap(UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode) { - RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); - /* ubidi_countRuns() checks for VALID_PARA_OR_LINE */ - ubidi_countRuns(pBiDi, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - /* no op */ - } else if(indexMap==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - } else { - /* fill a logical-to-visual index map using the runs[] */ - int32_t visualStart, visualLimit, i, j, k; - int32_t logicalStart, logicalLimit; - Run *runs=pBiDi->runs; - if (pBiDi->length<=0) { - return; - } - if (pBiDi->length>pBiDi->resultLength) { - uprv_memset(indexMap, 0xFF, pBiDi->length*sizeof(int32_t)); - } - - visualStart=0; - for(j=0; jrunCount; ++j) { - logicalStart=GET_INDEX(runs[j].logicalStart); - visualLimit=runs[j].visualLimit; - if(IS_EVEN_RUN(runs[j].logicalStart)) { - do { /* LTR */ - indexMap[logicalStart++]=visualStart++; - } while(visualStartinsertPoints.size>0) { - int32_t markFound=0, runCount=pBiDi->runCount; - int32_t length, insertRemove; - visualStart=0; - /* add number of marks found until each index */ - for(i=0; i0) { - logicalStart=GET_INDEX(runs[i].logicalStart); - logicalLimit=logicalStart+length; - for(j=logicalStart; jcontrolCount>0) { - int32_t controlFound=0, runCount=pBiDi->runCount; - int32_t length, insertRemove; - UBool evenRun; - UChar uchar; - visualStart=0; - /* subtract number of controls found until each index */ - for(i=0; itext[k]; - if(IS_BIDI_CONTROL_CHAR(uchar)) { - controlFound++; - indexMap[k]=UBIDI_MAP_NOWHERE; - continue; - } - indexMap[k]-=controlFound; - } - } - } - } -} - -U_CAPI void U_EXPORT2 -ubidi_getVisualMap(UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode) { - RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); - if(indexMap==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - /* ubidi_countRuns() checks for VALID_PARA_OR_LINE */ - ubidi_countRuns(pBiDi, pErrorCode); - if(U_SUCCESS(*pErrorCode)) { - /* fill a visual-to-logical index map using the runs[] */ - Run *runs=pBiDi->runs, *runsLimit=runs+pBiDi->runCount; - int32_t logicalStart, visualStart, visualLimit, *pi=indexMap; - - if (pBiDi->resultLength<=0) { - return; - } - visualStart=0; - for(; runslogicalStart; - visualLimit=runs->visualLimit; - if(IS_EVEN_RUN(logicalStart)) { - do { /* LTR */ - *pi++ = logicalStart++; - } while(++visualStartinsertPoints.size>0) { - int32_t markFound=0, runCount=pBiDi->runCount; - int32_t insertRemove, i, j, k; - runs=pBiDi->runs; - /* count all inserted marks */ - for(i=0; iresultLength; - for(i=runCount-1; i>=0 && markFound>0; i--) { - insertRemove=runs[i].insertRemove; - if(insertRemove&(LRM_AFTER|RLM_AFTER)) { - indexMap[--k]= UBIDI_MAP_NOWHERE; - markFound--; - } - visualStart= i>0 ? runs[i-1].visualLimit : 0; - for(j=runs[i].visualLimit-1; j>=visualStart && markFound>0; j--) { - indexMap[--k]=indexMap[j]; - } - if(insertRemove&(LRM_BEFORE|RLM_BEFORE)) { - indexMap[--k]= UBIDI_MAP_NOWHERE; - markFound--; - } - } - } - else if(pBiDi->controlCount>0) { - int32_t runCount=pBiDi->runCount, logicalEnd; - int32_t insertRemove, length, i, j, k, m; - UChar uchar; - UBool evenRun; - runs=pBiDi->runs; - visualStart=0; - /* move forward indexes by number of preceding controls */ - k=0; - for(i=0; itext[m]; - if(!IS_BIDI_CONTROL_CHAR(uchar)) { - indexMap[k++]=m; - } - } - } - } - } -} - -U_CAPI void U_EXPORT2 -ubidi_invertMap(const int32_t *srcMap, int32_t *destMap, int32_t length) { - if(srcMap!=NULL && destMap!=NULL && length>0) { - const int32_t *pi; - int32_t destLength=-1, count=0; - /* find highest value and count positive indexes in srcMap */ - pi=srcMap+length; - while(pi>srcMap) { - if(*--pi>destLength) { - destLength=*pi; - } - if(*pi>=0) { - count++; - } - } - destLength++; /* add 1 for origin 0 */ - if(count0) { - if(*--pi>=0) { - destMap[*pi]=--length; - } else { - --length; - } - } - } -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ubidiln.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999aug06 +* created by: Markus W. Scherer, updated by Matitiahu Allouche +*/ + +#include "cmemory.h" +#include "unicode/utypes.h" +#include "unicode/ustring.h" +#include "unicode/uchar.h" +#include "unicode/ubidi.h" +#include "ubidiimp.h" +#include "uassert.h" + +/* + * General remarks about the functions in this file: + * + * These functions deal with the aspects of potentially mixed-directional + * text in a single paragraph or in a line of a single paragraph + * which has already been processed according to + * the Unicode 6.3 BiDi algorithm as defined in + * https://www.unicode.org/reports/tr9/ , version 28, + * also described in The Unicode Standard, Version 6.3.0 . + * + * This means that there is a UBiDi object with a levels + * and a dirProps array. + * paraLevel and direction are also set. + * Only if the length of the text is zero, then levels==dirProps==nullptr. + * + * The overall directionality of the paragraph + * or line is used to bypass the reordering steps if possible. + * Even purely RTL text does not need reordering there because + * the ubidi_getLogical/VisualIndex() functions can compute the + * index on the fly in such a case. + * + * The implementation of the access to same-level-runs and of the reordering + * do attempt to provide better performance and less memory usage compared to + * a direct implementation of especially rule (L2) with an array of + * one (32-bit) integer per text character. + * + * Here, the levels array is scanned as soon as necessary, and a vector of + * same-level-runs is created. Reordering then is done on this vector. + * For each run of text positions that were resolved to the same level, + * only 8 bytes are stored: the first text position of the run and the visual + * position behind the run after reordering. + * One sign bit is used to hold the directionality of the run. + * This is inefficient if there are many very short runs. If the average run + * length is <2, then this uses more memory. + * + * In a further attempt to save memory, the levels array is never changed + * after all the resolution rules (Xn, Wn, Nn, In). + * Many functions have to consider the field trailingWSStart: + * if it is less than length, then there is an implicit trailing run + * at the paraLevel, + * which is not reflected in the levels array. + * This allows a line UBiDi object to use the same levels array as + * its paragraph parent object. + * + * When a UBiDi object is created for a line of a paragraph, then the + * paragraph's levels and dirProps arrays are reused by way of setting + * a pointer into them, not by copying. This again saves memory and forbids to + * change the now shared levels for (L1). + */ + +/* handle trailing WS (L1) -------------------------------------------------- */ + +/* + * setTrailingWSStart() sets the start index for a trailing + * run of WS in the line. This is necessary because we do not modify + * the paragraph's levels array that we just point into. + * Using trailingWSStart is another form of performing (L1). + * + * To make subsequent operations easier, we also include the run + * before the WS if it is at the paraLevel - we merge the two here. + * + * This function is called only from ubidi_setLine(), so pBiDi->paraLevel is + * set correctly for the line even when contextual multiple paragraphs. + */ +static void +setTrailingWSStart(UBiDi *pBiDi) { + /* pBiDi->direction!=UBIDI_MIXED */ + + const DirProp *dirProps=pBiDi->dirProps; + UBiDiLevel *levels=pBiDi->levels; + int32_t start=pBiDi->length; + UBiDiLevel paraLevel=pBiDi->paraLevel; + + /* If the line is terminated by a block separator, all preceding WS etc... + are already set to paragraph level. + Setting trailingWSStart to pBidi->length will avoid changing the + level of B chars from 0 to paraLevel in ubidi_getLevels when + orderParagraphsLTR==true. + */ + if(dirProps[start-1]==B) { + pBiDi->trailingWSStart=start; /* currently == pBiDi->length */ + return; + } + /* go backwards across all WS, BN, explicit codes */ + while(start>0 && DIRPROP_FLAG(dirProps[start-1])&MASK_WS) { + --start; + } + + /* if the WS run can be merged with the previous run then do so here */ + while(start>0 && levels[start-1]==paraLevel) { + --start; + } + + pBiDi->trailingWSStart=start; +} + +/* ubidi_setLine ------------------------------------------------------------ */ + +U_CAPI void U_EXPORT2 +ubidi_setLine(const UBiDi *pParaBiDi, + int32_t start, int32_t limit, + UBiDi *pLineBiDi, + UErrorCode *pErrorCode) { + int32_t length; + + /* check the argument values */ + RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); + RETURN_VOID_IF_NOT_VALID_PARA(pParaBiDi, *pErrorCode); + RETURN_VOID_IF_BAD_RANGE(start, 0, limit, *pErrorCode); + RETURN_VOID_IF_BAD_RANGE(limit, 0, pParaBiDi->length+1, *pErrorCode); + if(pLineBiDi==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if(ubidi_getParagraph(pParaBiDi, start, nullptr, nullptr, nullptr, pErrorCode) != + ubidi_getParagraph(pParaBiDi, limit-1, nullptr, nullptr, nullptr, pErrorCode)) { + /* the line crosses a paragraph boundary */ + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + /* set the values in pLineBiDi from its pParaBiDi parent */ + pLineBiDi->pParaBiDi=nullptr; /* mark unfinished setLine */ + pLineBiDi->text=pParaBiDi->text+start; + length=pLineBiDi->length=limit-start; + pLineBiDi->resultLength=pLineBiDi->originalLength=length; + pLineBiDi->paraLevel=GET_PARALEVEL(pParaBiDi, start); + pLineBiDi->paraCount=pParaBiDi->paraCount; + pLineBiDi->runs=nullptr; + pLineBiDi->flags=0; + pLineBiDi->reorderingMode=pParaBiDi->reorderingMode; + pLineBiDi->reorderingOptions=pParaBiDi->reorderingOptions; + pLineBiDi->controlCount=0; + if(pParaBiDi->controlCount>0) { + int32_t j; + for(j=start; jtext[j])) { + pLineBiDi->controlCount++; + } + } + pLineBiDi->resultLength-=pLineBiDi->controlCount; + } + + pLineBiDi->dirProps=pParaBiDi->dirProps+start; + pLineBiDi->levels=pParaBiDi->levels+start; + pLineBiDi->runCount=-1; + + if(pParaBiDi->direction!=UBIDI_MIXED) { + /* the parent is already trivial */ + pLineBiDi->direction=pParaBiDi->direction; + + /* + * The parent's levels are all either + * implicitly or explicitly ==paraLevel; + * do the same here. + */ + if(pParaBiDi->trailingWSStart<=start) { + pLineBiDi->trailingWSStart=0; + } else if(pParaBiDi->trailingWSStarttrailingWSStart=pParaBiDi->trailingWSStart-start; + } else { + pLineBiDi->trailingWSStart=length; + } + } else { + const UBiDiLevel *levels=pLineBiDi->levels; + int32_t i, trailingWSStart; + UBiDiLevel level; + + setTrailingWSStart(pLineBiDi); + trailingWSStart=pLineBiDi->trailingWSStart; + + /* recalculate pLineBiDi->direction */ + if(trailingWSStart==0) { + /* all levels are at paraLevel */ + pLineBiDi->direction=(UBiDiDirection)(pLineBiDi->paraLevel&1); + } else { + /* get the level of the first character */ + level=(UBiDiLevel)(levels[0]&1); + + /* if there is anything of a different level, then the line is mixed */ + if(trailingWSStartparaLevel&1)!=level) { + /* the trailing WS is at paraLevel, which differs from levels[0] */ + pLineBiDi->direction=UBIDI_MIXED; + } else { + /* see if levels[1..trailingWSStart-1] have the same direction as levels[0] and paraLevel */ + i=1; + for(;;) { + if(i==trailingWSStart) { + /* the direction values match those in level */ + pLineBiDi->direction=(UBiDiDirection)level; + break; + } else if((levels[i]&1)!=level) { + pLineBiDi->direction=UBIDI_MIXED; + break; + } + ++i; + } + } + } + + switch(pLineBiDi->direction) { + case UBIDI_LTR: + /* make sure paraLevel is even */ + pLineBiDi->paraLevel=(UBiDiLevel)((pLineBiDi->paraLevel+1)&~1); + + /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */ + pLineBiDi->trailingWSStart=0; + break; + case UBIDI_RTL: + /* make sure paraLevel is odd */ + pLineBiDi->paraLevel|=1; + + /* all levels are implicitly at paraLevel (important for ubidi_getLevels()) */ + pLineBiDi->trailingWSStart=0; + break; + default: + break; + } + } + pLineBiDi->pParaBiDi=pParaBiDi; /* mark successful setLine */ + return; +} + +U_CAPI UBiDiLevel U_EXPORT2 +ubidi_getLevelAt(const UBiDi *pBiDi, int32_t charIndex) { + /* return paraLevel if in the trailing WS run, otherwise the real level */ + if(!IS_VALID_PARA_OR_LINE(pBiDi) || charIndex<0 || pBiDi->length<=charIndex) { + return 0; + } else if(pBiDi->direction!=UBIDI_MIXED || charIndex>=pBiDi->trailingWSStart) { + return GET_PARALEVEL(pBiDi, charIndex); + } else { + return pBiDi->levels[charIndex]; + } +} + +U_CAPI const UBiDiLevel * U_EXPORT2 +ubidi_getLevels(UBiDi *pBiDi, UErrorCode *pErrorCode) { + int32_t start, length; + + RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, nullptr); + RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, nullptr); + if((length=pBiDi->length)<=0) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + if((start=pBiDi->trailingWSStart)==length) { + /* the current levels array reflects the WS run */ + return pBiDi->levels; + } + + /* + * After the previous if(), we know that the levels array + * has an implicit trailing WS run and therefore does not fully + * reflect itself all the levels. + * This must be a UBiDi object for a line, and + * we need to create a new levels array. + */ + if(getLevelsMemory(pBiDi, length)) { + UBiDiLevel *levels=pBiDi->levelsMemory; + + if(start>0 && levels!=pBiDi->levels) { + uprv_memcpy(levels, pBiDi->levels, start); + } + /* pBiDi->paraLevel is ok even if contextual multiple paragraphs, + since pBidi is a line object */ + uprv_memset(levels+start, pBiDi->paraLevel, length-start); + + /* this new levels array is set for the line and reflects the WS run */ + pBiDi->trailingWSStart=length; + return pBiDi->levels=levels; + } else { + /* out of memory */ + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } +} + +U_CAPI void U_EXPORT2 +ubidi_getLogicalRun(const UBiDi *pBiDi, int32_t logicalPosition, + int32_t *pLogicalLimit, UBiDiLevel *pLevel) { + UErrorCode errorCode; + int32_t runCount, visualStart, logicalLimit, logicalFirst, i; + Run iRun; + + errorCode=U_ZERO_ERROR; + RETURN_VOID_IF_BAD_RANGE(logicalPosition, 0, pBiDi->length, errorCode); + /* ubidi_countRuns will check VALID_PARA_OR_LINE */ + runCount=ubidi_countRuns((UBiDi *)pBiDi, &errorCode); + if(U_FAILURE(errorCode)) { + return; + } + /* this is done based on runs rather than on levels since levels have + a special interpretation when UBIDI_REORDER_RUNS_ONLY + */ + visualStart=logicalLimit=0; + iRun=pBiDi->runs[0]; + + for(i=0; iruns[i]; + logicalFirst=GET_INDEX(iRun.logicalStart); + logicalLimit=logicalFirst+iRun.visualLimit-visualStart; + if((logicalPosition>=logicalFirst) && + (logicalPositionreorderingMode==UBIDI_REORDER_RUNS_ONLY) { + *pLevel=(UBiDiLevel)GET_ODD_BIT(iRun.logicalStart); + } + else if(pBiDi->direction!=UBIDI_MIXED || logicalPosition>=pBiDi->trailingWSStart) { + *pLevel=GET_PARALEVEL(pBiDi, logicalPosition); + } else { + *pLevel=pBiDi->levels[logicalPosition]; + } + } +} + +/* runs API functions ------------------------------------------------------- */ + +U_CAPI int32_t U_EXPORT2 +ubidi_countRuns(UBiDi *pBiDi, UErrorCode *pErrorCode) { + RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1); + RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1); + ubidi_getRuns(pBiDi, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return -1; + } + return pBiDi->runCount; +} + +U_CAPI UBiDiDirection U_EXPORT2 +ubidi_getVisualRun(UBiDi *pBiDi, int32_t runIndex, + int32_t *pLogicalStart, int32_t *pLength) +{ + int32_t start; + UErrorCode errorCode = U_ZERO_ERROR; + RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, errorCode, UBIDI_LTR); + ubidi_getRuns(pBiDi, &errorCode); + if(U_FAILURE(errorCode)) { + return UBIDI_LTR; + } + RETURN_IF_BAD_RANGE(runIndex, 0, pBiDi->runCount, errorCode, UBIDI_LTR); + + start=pBiDi->runs[runIndex].logicalStart; + if(pLogicalStart!=nullptr) { + *pLogicalStart=GET_INDEX(start); + } + if(pLength!=nullptr) { + if(runIndex>0) { + *pLength=pBiDi->runs[runIndex].visualLimit- + pBiDi->runs[runIndex-1].visualLimit; + } else { + *pLength=pBiDi->runs[0].visualLimit; + } + } + return (UBiDiDirection)GET_ODD_BIT(start); +} + +/* in trivial cases there is only one trivial run; called by ubidi_getRuns() */ +static void +getSingleRun(UBiDi *pBiDi, UBiDiLevel level) { + /* simple, single-run case */ + pBiDi->runs=pBiDi->simpleRuns; + pBiDi->runCount=1; + + /* fill and reorder the single run */ + pBiDi->runs[0].logicalStart=MAKE_INDEX_ODD_PAIR(0, level); + pBiDi->runs[0].visualLimit=pBiDi->length; + pBiDi->runs[0].insertRemove=0; +} + +/* reorder the runs array (L2) ---------------------------------------------- */ + +/* + * Reorder the same-level runs in the runs array. + * Here, runCount>1 and maxLevel>=minLevel>=paraLevel. + * All the visualStart fields=logical start before reordering. + * The "odd" bits are not set yet. + * + * Reordering with this data structure lends itself to some handy shortcuts: + * + * Since each run is moved but not modified, and since at the initial maxLevel + * each sequence of same-level runs consists of only one run each, we + * don't need to do anything there and can predecrement maxLevel. + * In many simple cases, the reordering is thus done entirely in the + * index mapping. + * Also, reordering occurs only down to the lowest odd level that occurs, + * which is minLevel|1. However, if the lowest level itself is odd, then + * in the last reordering the sequence of the runs at this level or higher + * will be all runs, and we don't need the elaborate loop to search for them. + * This is covered by ++minLevel instead of minLevel|=1 followed + * by an extra reorder-all after the reorder-some loop. + * About a trailing WS run: + * Such a run would need special treatment because its level is not + * reflected in levels[] if this is not a paragraph object. + * Instead, all characters from trailingWSStart on are implicitly at + * paraLevel. + * However, for all maxLevel>paraLevel, this run will never be reordered + * and does not need to be taken into account. maxLevel==paraLevel is only reordered + * if minLevel==paraLevel is odd, which is done in the extra segment. + * This means that for the main reordering loop we don't need to consider + * this run and can --runCount. If it is later part of the all-runs + * reordering, then runCount is adjusted accordingly. + */ +static void +reorderLine(UBiDi *pBiDi, UBiDiLevel minLevel, UBiDiLevel maxLevel) { + Run *runs, tempRun; + UBiDiLevel *levels; + int32_t firstRun, endRun, limitRun, runCount; + + /* nothing to do? */ + if(maxLevel<=(minLevel|1)) { + return; + } + + /* + * Reorder only down to the lowest odd level + * and reorder at an odd minLevel in a separate, simpler loop. + * See comments above for why minLevel is always incremented. + */ + ++minLevel; + + runs=pBiDi->runs; + levels=pBiDi->levels; + runCount=pBiDi->runCount; + + /* do not include the WS run at paraLevel<=old minLevel except in the simple loop */ + if(pBiDi->trailingWSStartlength) { + --runCount; + } + + while(--maxLevel>=minLevel) { + firstRun=0; + + /* loop for all sequences of runs */ + for(;;) { + /* look for a sequence of runs that are all at >=maxLevel */ + /* look for the first run of such a sequence */ + while(firstRun=runCount) { + break; /* no more such runs */ + } + + /* look for the limit run of such a sequence (the run behind it) */ + for(limitRun=firstRun; ++limitRun=maxLevel;) {} + + /* Swap the entire sequence of runs from firstRun to limitRun-1. */ + endRun=limitRun-1; + while(firstRuntrailingWSStart==pBiDi->length) { + --runCount; + } + + /* Swap the entire sequence of all runs. (endRun==runCount) */ + while(firstRunruns; + int32_t runCount=pBiDi->runCount, visualStart=0, i, length, logicalStart; + + for(i=0; i=logicalStart) && (logicalIndex<(logicalStart+length))) { + return i; + } + visualStart+=length; + } + /* we should never get here */ + UPRV_UNREACHABLE_EXIT; +} + +/* + * Compute the runs array from the levels array. + * After ubidi_getRuns() returns true, runCount is guaranteed to be >0 + * and the runs are reordered. + * Odd-level runs have visualStart on their visual right edge and + * they progress visually to the left. + * If option UBIDI_OPTION_INSERT_MARKS is set, insertRemove will contain the + * sum of appropriate LRM/RLM_BEFORE/AFTER flags. + * If option UBIDI_OPTION_REMOVE_CONTROLS is set, insertRemove will contain the + * negative number of BiDi control characters within this run. + */ +U_CFUNC UBool +ubidi_getRuns(UBiDi *pBiDi, UErrorCode*) { + /* + * This method returns immediately if the runs are already set. This + * includes the case of length==0 (handled in setPara).. + */ + if (pBiDi->runCount>=0) { + return true; + } + + if(pBiDi->direction!=UBIDI_MIXED) { + /* simple, single-run case - this covers length==0 */ + /* pBiDi->paraLevel is ok even for contextual multiple paragraphs */ + getSingleRun(pBiDi, pBiDi->paraLevel); + } else /* UBIDI_MIXED, length>0 */ { + /* mixed directionality */ + int32_t length=pBiDi->length, limit; + UBiDiLevel *levels=pBiDi->levels; + int32_t i, runCount; + UBiDiLevel level=UBIDI_DEFAULT_LTR; /* initialize with no valid level */ + /* + * If there are WS characters at the end of the line + * and the run preceding them has a level different from + * paraLevel, then they will form their own run at paraLevel (L1). + * Count them separately. + * We need some special treatment for this in order to not + * modify the levels array which a line UBiDi object shares + * with its paragraph parent and its other line siblings. + * In other words, for the trailing WS, it may be + * levels[]!=paraLevel but we have to treat it like it were so. + */ + limit=pBiDi->trailingWSStart; + /* count the runs, there is at least one non-WS run, and limit>0 */ + runCount=0; + for(i=0; i1 || limit1 */ + if(getRunsMemory(pBiDi, runCount)) { + runs=pBiDi->runsMemory; + } else { + return false; + } + + /* set the runs */ + /* FOOD FOR THOUGHT: this could be optimized, e.g.: + * 464->444, 484->444, 575->555, 595->555 + * However, that would take longer. Check also how it would + * interact with BiDi control removal and inserting Marks. + */ + runIndex=0; + + /* search for the run limits and initialize visualLimit values with the run lengths */ + i=0; + do { + /* prepare this run */ + start=i; + level=levels[i]; + if(levelmaxLevel) { + maxLevel=level; + } + + /* look for the run limit */ + while(++iparaLevel is ok even + if contextual multiple paragraphs. */ + if(pBiDi->paraLevelparaLevel; + } + } + + /* set the object fields */ + pBiDi->runs=runs; + pBiDi->runCount=runCount; + + reorderLine(pBiDi, minLevel, maxLevel); + + /* now add the direction flags and adjust the visualLimit's to be just that */ + /* this loop will also handle the trailing WS run */ + limit=0; + for(i=0; iparaLevel is ok even if + contextual multiple paragraphs. */ + if(runIndexparaLevel & 1) != 0)? 0 : runIndex; + + ADD_ODD_BIT_FROM_LEVEL(runs[trailingRun].logicalStart, pBiDi->paraLevel); + } + } + } + + /* handle insert LRM/RLM BEFORE/AFTER run */ + if(pBiDi->insertPoints.size>0) { + Point *point, *start=pBiDi->insertPoints.points, + *limit=start+pBiDi->insertPoints.size; + int32_t runIndex; + for(point=start; pointpos); + pBiDi->runs[runIndex].insertRemove|=point->flag; + } + } + + /* handle remove BiDi control characters */ + if(pBiDi->controlCount>0) { + int32_t runIndex; + const char16_t *start=pBiDi->text, *limit=start+pBiDi->length, *pu; + for(pu=start; puruns[runIndex].insertRemove--; + } + } + } + + return true; +} + +static UBool +prepareReorder(const UBiDiLevel *levels, int32_t length, + int32_t *indexMap, + UBiDiLevel *pMinLevel, UBiDiLevel *pMaxLevel) { + int32_t start; + UBiDiLevel level, minLevel, maxLevel; + + if(levels==nullptr || length<=0) { + return false; + } + + /* determine minLevel and maxLevel */ + minLevel=UBIDI_MAX_EXPLICIT_LEVEL+1; + maxLevel=0; + for(start=length; start>0;) { + level=levels[--start]; + if(level>UBIDI_MAX_EXPLICIT_LEVEL+1) { + return false; + } + if(levelmaxLevel) { + maxLevel=level; + } + } + *pMinLevel=minLevel; + *pMaxLevel=maxLevel; + + /* initialize the index map */ + for(start=length; start>0;) { + --start; + indexMap[start]=start; + } + + return true; +} + +/* reorder a line based on a levels array (L2) ------------------------------ */ + +U_CAPI void U_EXPORT2 +ubidi_reorderLogical(const UBiDiLevel *levels, int32_t length, int32_t *indexMap) { + int32_t start, limit, sumOfSosEos; + UBiDiLevel minLevel = 0, maxLevel = 0; + + if(indexMap==nullptr || !prepareReorder(levels, length, indexMap, &minLevel, &maxLevel)) { + return; + } + + /* nothing to do? */ + if(minLevel==maxLevel && (minLevel&1)==0) { + return; + } + + /* reorder only down to the lowest odd level */ + minLevel|=1; + + /* loop maxLevel..minLevel */ + do { + start=0; + + /* loop for all sequences of levels to reorder at the current maxLevel */ + for(;;) { + /* look for a sequence of levels that are all at >=maxLevel */ + /* look for the first index of such a sequence */ + while(start=length) { + break; /* no more such sequences */ + } + + /* look for the limit of such a sequence (the index behind it) */ + for(limit=start; ++limit=maxLevel;) {} + + /* + * sos=start of sequence, eos=end of sequence + * + * The closed (inclusive) interval from sos to eos includes all the logical + * and visual indexes within this sequence. They are logically and + * visually contiguous and in the same range. + * + * For each run, the new visual index=sos+eos-old visual index; + * we pre-add sos+eos into sumOfSosEos -> + * new visual index=sumOfSosEos-old visual index; + */ + sumOfSosEos=start+limit-1; + + /* reorder each index in the sequence */ + do { + indexMap[start]=sumOfSosEos-indexMap[start]; + } while(++start=minLevel); +} + +U_CAPI void U_EXPORT2 +ubidi_reorderVisual(const UBiDiLevel *levels, int32_t length, int32_t *indexMap) { + int32_t start, end, limit, temp; + UBiDiLevel minLevel = 0, maxLevel = 0; + + if(indexMap==nullptr || !prepareReorder(levels, length, indexMap, &minLevel, &maxLevel)) { + return; + } + + /* nothing to do? */ + if(minLevel==maxLevel && (minLevel&1)==0) { + return; + } + + /* reorder only down to the lowest odd level */ + minLevel|=1; + + /* loop maxLevel..minLevel */ + do { + start=0; + + /* loop for all sequences of levels to reorder at the current maxLevel */ + for(;;) { + /* look for a sequence of levels that are all at >=maxLevel */ + /* look for the first index of such a sequence */ + while(start=length) { + break; /* no more such runs */ + } + + /* look for the limit of such a sequence (the index behind it) */ + for(limit=start; ++limit=maxLevel;) {} + + /* + * Swap the entire interval of indexes from start to limit-1. + * We don't need to swap the levels for the purpose of this + * algorithm: the sequence of levels that we look at does not + * move anyway. + */ + end=limit-1; + while(start=minLevel); +} + +/* API functions for logical<->visual mapping ------------------------------- */ + +U_CAPI int32_t U_EXPORT2 +ubidi_getVisualIndex(UBiDi *pBiDi, int32_t logicalIndex, UErrorCode *pErrorCode) { + int32_t visualIndex=UBIDI_MAP_NOWHERE; + RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1); + RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1); + RETURN_IF_BAD_RANGE(logicalIndex, 0, pBiDi->length, *pErrorCode, -1); + + /* we can do the trivial cases without the runs array */ + switch(pBiDi->direction) { + case UBIDI_LTR: + visualIndex=logicalIndex; + break; + case UBIDI_RTL: + visualIndex=pBiDi->length-logicalIndex-1; + break; + default: + if(!ubidi_getRuns(pBiDi, pErrorCode)) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return -1; + } else { + Run *runs=pBiDi->runs; + int32_t i, visualStart=0, offset, length; + + /* linear search for the run, search on the visual runs */ + for(i=0; irunCount; ++i) { + length=runs[i].visualLimit-visualStart; + offset=logicalIndex-GET_INDEX(runs[i].logicalStart); + if(offset>=0 && offset=pBiDi->runCount) { + return UBIDI_MAP_NOWHERE; + } + } + } + + if(pBiDi->insertPoints.size>0) { + /* add the number of added marks until the calculated visual index */ + Run *runs=pBiDi->runs; + int32_t i, length, insertRemove; + int32_t visualStart=0, markFound=0; + for(i=0; ; i++, visualStart+=length) { + length=runs[i].visualLimit-visualStart; + insertRemove=runs[i].insertRemove; + if(insertRemove & (LRM_BEFORE|RLM_BEFORE)) { + markFound++; + } + /* is it the run containing the visual index? */ + if(visualIndexcontrolCount>0) { + /* subtract the number of controls until the calculated visual index */ + Run *runs=pBiDi->runs; + int32_t i, j, start, limit, length, insertRemove; + int32_t visualStart=0, controlFound=0; + char16_t uchar=pBiDi->text[logicalIndex]; + /* is the logical index pointing to a control ? */ + if(IS_BIDI_CONTROL_CHAR(uchar)) { + return UBIDI_MAP_NOWHERE; + } + /* loop on runs */ + for(i=0; ; i++, visualStart+=length) { + length=runs[i].visualLimit-visualStart; + insertRemove=runs[i].insertRemove; + /* calculated visual index is beyond this run? */ + if(visualIndex>=runs[i].visualLimit) { + controlFound-=insertRemove; + continue; + } + /* calculated visual index must be within current run */ + if(insertRemove==0) { + return visualIndex-controlFound; + } + if(IS_EVEN_RUN(runs[i].logicalStart)) { + /* LTR: check from run start to logical index */ + start=runs[i].logicalStart; + limit=logicalIndex; + } else { + /* RTL: check from logical index to run end */ + start=logicalIndex+1; + limit=GET_INDEX(runs[i].logicalStart)+length; + } + for(j=start; jtext[j]; + if(IS_BIDI_CONTROL_CHAR(uchar)) { + controlFound++; + } + } + return visualIndex-controlFound; + } + } + + return visualIndex; +} + +U_CAPI int32_t U_EXPORT2 +ubidi_getLogicalIndex(UBiDi *pBiDi, int32_t visualIndex, UErrorCode *pErrorCode) { + Run *runs; + int32_t i, runCount, start; + RETURN_IF_NULL_OR_FAILING_ERRCODE(pErrorCode, -1); + RETURN_IF_NOT_VALID_PARA_OR_LINE(pBiDi, *pErrorCode, -1); + RETURN_IF_BAD_RANGE(visualIndex, 0, pBiDi->resultLength, *pErrorCode, -1); + /* we can do the trivial cases without the runs array */ + if(pBiDi->insertPoints.size==0 && pBiDi->controlCount==0) { + if(pBiDi->direction==UBIDI_LTR) { + return visualIndex; + } + else if(pBiDi->direction==UBIDI_RTL) { + return pBiDi->length-visualIndex-1; + } + } + if(!ubidi_getRuns(pBiDi, pErrorCode)) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return -1; + } + + runs=pBiDi->runs; + runCount=pBiDi->runCount; + if(pBiDi->insertPoints.size>0) { + /* handle inserted LRM/RLM */ + int32_t markFound=0, insertRemove; + int32_t visualStart=0, length; + runs=pBiDi->runs; + /* subtract number of marks until visual index */ + for(i=0; ; i++, visualStart+=length) { + length=runs[i].visualLimit-visualStart; + insertRemove=runs[i].insertRemove; + if(insertRemove&(LRM_BEFORE|RLM_BEFORE)) { + if(visualIndex<=(visualStart+markFound)) { + return UBIDI_MAP_NOWHERE; + } + markFound++; + } + /* is adjusted visual index within this run? */ + if(visualIndex<(runs[i].visualLimit+markFound)) { + visualIndex-=markFound; + break; + } + if(insertRemove&(LRM_AFTER|RLM_AFTER)) { + if(visualIndex==(visualStart+length+markFound)) { + return UBIDI_MAP_NOWHERE; + } + markFound++; + } + } + } + else if(pBiDi->controlCount>0) { + /* handle removed BiDi control characters */ + int32_t controlFound=0, insertRemove, length; + int32_t logicalStart, logicalEnd, visualStart=0, j, k; + char16_t uchar; + UBool evenRun; + /* add number of controls until visual index */ + for(i=0; ; i++, visualStart+=length) { + length=runs[i].visualLimit-visualStart; + insertRemove=runs[i].insertRemove; + /* is adjusted visual index beyond current run? */ + if(visualIndex>=(runs[i].visualLimit-controlFound+insertRemove)) { + controlFound-=insertRemove; + continue; + } + /* adjusted visual index is within current run */ + if(insertRemove==0) { + visualIndex+=controlFound; + break; + } + /* count non-control chars until visualIndex */ + logicalStart=runs[i].logicalStart; + evenRun=IS_EVEN_RUN(logicalStart); + REMOVE_ODD_BIT(logicalStart); + logicalEnd=logicalStart+length-1; + for(j=0; jtext[k]; + if(IS_BIDI_CONTROL_CHAR(uchar)) { + controlFound++; + } + if((visualIndex+controlFound)==(visualStart+j)) { + break; + } + } + visualIndex+=controlFound; + break; + } + } + /* handle all cases */ + if(runCount<=10) { + /* linear search for the run */ + for(i=0; visualIndex>=runs[i].visualLimit; ++i) {} + } else { + /* binary search for the run */ + int32_t begin=0, limit=runCount; + + /* the middle if() is guaranteed to find the run, we don't need a loop limit */ + for(;;) { + i=(begin+limit)/2; + if(visualIndex>=runs[i].visualLimit) { + begin=i+1; + } else if(i==0 || visualIndex>=runs[i-1].visualLimit) { + break; + } else { + limit=i; + } + } + } + + start=runs[i].logicalStart; + if(IS_EVEN_RUN(start)) { + /* LTR */ + /* the offset in runs[i] is visualIndex-runs[i-1].visualLimit */ + if(i>0) { + visualIndex-=runs[i-1].visualLimit; + } + return start+visualIndex; + } else { + /* RTL */ + return GET_INDEX(start)+runs[i].visualLimit-visualIndex-1; + } +} + +U_CAPI void U_EXPORT2 +ubidi_getLogicalMap(UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode) { + RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); + /* ubidi_countRuns() checks for VALID_PARA_OR_LINE */ + ubidi_countRuns(pBiDi, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + /* no op */ + } else if(indexMap==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + } else { + /* fill a logical-to-visual index map using the runs[] */ + int32_t visualStart, visualLimit, i, j, k; + int32_t logicalStart, logicalLimit; + Run *runs=pBiDi->runs; + if (pBiDi->length<=0) { + return; + } + if (pBiDi->length>pBiDi->resultLength) { + uprv_memset(indexMap, 0xFF, pBiDi->length*sizeof(int32_t)); + } + + visualStart=0; + for(j=0; jrunCount; ++j) { + logicalStart=GET_INDEX(runs[j].logicalStart); + visualLimit=runs[j].visualLimit; + if(IS_EVEN_RUN(runs[j].logicalStart)) { + do { /* LTR */ + indexMap[logicalStart++]=visualStart++; + } while(visualStartinsertPoints.size>0) { + int32_t markFound=0, runCount=pBiDi->runCount; + int32_t length, insertRemove; + visualStart=0; + /* add number of marks found until each index */ + for(i=0; i0) { + logicalStart=GET_INDEX(runs[i].logicalStart); + logicalLimit=logicalStart+length; + for(j=logicalStart; jcontrolCount>0) { + int32_t controlFound=0, runCount=pBiDi->runCount; + int32_t length, insertRemove; + UBool evenRun; + char16_t uchar; + visualStart=0; + /* subtract number of controls found until each index */ + for(i=0; itext[k]; + if(IS_BIDI_CONTROL_CHAR(uchar)) { + controlFound++; + indexMap[k]=UBIDI_MAP_NOWHERE; + continue; + } + indexMap[k]-=controlFound; + } + } + } + } +} + +U_CAPI void U_EXPORT2 +ubidi_getVisualMap(UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode) { + RETURN_VOID_IF_NULL_OR_FAILING_ERRCODE(pErrorCode); + if(indexMap==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + /* ubidi_countRuns() checks for VALID_PARA_OR_LINE */ + ubidi_countRuns(pBiDi, pErrorCode); + if(U_SUCCESS(*pErrorCode)) { + /* fill a visual-to-logical index map using the runs[] */ + Run *runs=pBiDi->runs, *runsLimit=runs+pBiDi->runCount; + int32_t logicalStart, visualStart, visualLimit, *pi=indexMap; + + if (pBiDi->resultLength<=0) { + return; + } + visualStart=0; + for(; runslogicalStart; + visualLimit=runs->visualLimit; + if(IS_EVEN_RUN(logicalStart)) { + do { /* LTR */ + *pi++ = logicalStart++; + } while(++visualStartinsertPoints.size>0) { + int32_t markFound=0, runCount=pBiDi->runCount; + int32_t insertRemove, i, j, k; + runs=pBiDi->runs; + /* count all inserted marks */ + for(i=0; iresultLength; + for(i=runCount-1; i>=0 && markFound>0; i--) { + insertRemove=runs[i].insertRemove; + if(insertRemove&(LRM_AFTER|RLM_AFTER)) { + indexMap[--k]= UBIDI_MAP_NOWHERE; + markFound--; + } + visualStart= i>0 ? runs[i-1].visualLimit : 0; + for(j=runs[i].visualLimit-1; j>=visualStart && markFound>0; j--) { + indexMap[--k]=indexMap[j]; + } + if(insertRemove&(LRM_BEFORE|RLM_BEFORE)) { + indexMap[--k]= UBIDI_MAP_NOWHERE; + markFound--; + } + } + } + else if(pBiDi->controlCount>0) { + int32_t runCount=pBiDi->runCount, logicalEnd; + int32_t insertRemove, length, i, j, k, m; + char16_t uchar; + UBool evenRun; + runs=pBiDi->runs; + visualStart=0; + /* move forward indexes by number of preceding controls */ + k=0; + for(i=0; itext[m]; + if(!IS_BIDI_CONTROL_CHAR(uchar)) { + indexMap[k++]=m; + } + } + } + } + } +} + +U_CAPI void U_EXPORT2 +ubidi_invertMap(const int32_t *srcMap, int32_t *destMap, int32_t length) { + if(srcMap!=nullptr && destMap!=nullptr && length>0) { + const int32_t *pi; + int32_t destLength=-1, count=0; + /* find highest value and count positive indexes in srcMap */ + pi=srcMap+length; + while(pi>srcMap) { + if(*--pi>destLength) { + destLength=*pi; + } + if(*pi>=0) { + count++; + } + } + destLength++; /* add 1 for origin 0 */ + if(count0) { + if(*--pi>=0) { + destMap[*pi]=--length; + } else { + --length; + } + } + } +} diff --git a/deps/icu-small/source/common/ubiditransform.cpp b/deps/icu-small/source/common/ubiditransform.cpp index 24fffd9c460c05..2e8c2755f863fb 100644 --- a/deps/icu-small/source/common/ubiditransform.cpp +++ b/deps/icu-small/source/common/ubiditransform.cpp @@ -1,530 +1,530 @@ -/* -****************************************************************************** -* -* © 2016 and later: Unicode, Inc. and others. -* License & terms of use: http://www.unicode.org/copyright.html -* -****************************************************************************** -* file name: ubiditransform.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2016jul24 -* created by: Lina Kemmel -* -*/ - -#include "cmemory.h" -#include "unicode/ubidi.h" -#include "unicode/ustring.h" -#include "unicode/ushape.h" -#include "unicode/utf16.h" -#include "ustr_imp.h" -#include "unicode/ubiditransform.h" - -/* Some convenience defines */ -#define LTR UBIDI_LTR -#define RTL UBIDI_RTL -#define LOGICAL UBIDI_LOGICAL -#define VISUAL UBIDI_VISUAL -#define SHAPE_LOGICAL U_SHAPE_TEXT_DIRECTION_LOGICAL -#define SHAPE_VISUAL U_SHAPE_TEXT_DIRECTION_VISUAL_LTR - -#define CHECK_LEN(STR, LEN, ERROR) UPRV_BLOCK_MACRO_BEGIN { \ - if (LEN == 0) return 0; \ - if (LEN < -1) { *(ERROR) = U_ILLEGAL_ARGUMENT_ERROR; return 0; } \ - if (LEN == -1) LEN = u_strlen(STR); \ -} UPRV_BLOCK_MACRO_END - -#define MAX_ACTIONS 7 - -/** - * Typedef for a pointer to a function, which performs some operation (such as - * reordering, setting "inverse" mode, character mirroring, etc.). Return value - * indicates whether the text was changed in the course of this operation or - * not. - */ -typedef UBool (*UBiDiAction)(UBiDiTransform *, UErrorCode *); - -/** - * Structure that holds a predefined reordering scheme, including the following - * information: - *

    - *
  • an input base direction,
  • - *
  • an input order,
  • - *
  • an output base direction,
  • - *
  • an output order,
  • - *
  • a digit shaping direction,
  • - *
  • a letter shaping direction,
  • - *
  • a base direction that should be applied when the reordering engine is - * invoked (which can not always be derived from the caller-defined - * options),
  • - *
  • an array of pointers to functions that accomplish the bidi layout - * transformation.
  • - *
- */ -typedef struct { - UBiDiLevel inLevel; /* input level */ - UBiDiOrder inOrder; /* input order */ - UBiDiLevel outLevel; /* output level */ - UBiDiOrder outOrder; /* output order */ - uint32_t digitsDir; /* digit shaping direction */ - uint32_t lettersDir; /* letter shaping direction */ - UBiDiLevel baseLevel; /* paragraph level to be used with setPara */ - const UBiDiAction actions[MAX_ACTIONS]; /* array of pointers to functions carrying out the transformation */ -} ReorderingScheme; - -struct UBiDiTransform { - UBiDi *pBidi; /* pointer to a UBiDi object */ - const ReorderingScheme *pActiveScheme; /* effective reordering scheme */ - UChar *src; /* input text */ - UChar *dest; /* output text */ - uint32_t srcLength; /* input text length - not really needed as we are zero-terminated and can u_strlen */ - uint32_t srcSize; /* input text capacity excluding the trailing zero */ - uint32_t destSize; /* output text capacity */ - uint32_t *pDestLength; /* number of UChars written to dest */ - uint32_t reorderingOptions; /* reordering options - currently only suppot DO_MIRRORING */ - uint32_t digits; /* digit option for ArabicShaping */ - uint32_t letters; /* letter option for ArabicShaping */ -}; - -U_CAPI UBiDiTransform* U_EXPORT2 -ubiditransform_open(UErrorCode *pErrorCode) -{ - UBiDiTransform *pBiDiTransform = NULL; - if (U_SUCCESS(*pErrorCode)) { - pBiDiTransform = (UBiDiTransform*) uprv_calloc(1, sizeof(UBiDiTransform)); - if (pBiDiTransform == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - } - } - return pBiDiTransform; -} - -U_CAPI void U_EXPORT2 -ubiditransform_close(UBiDiTransform *pBiDiTransform) -{ - if (pBiDiTransform != NULL) { - if (pBiDiTransform->pBidi != NULL) { - ubidi_close(pBiDiTransform->pBidi); - } - if (pBiDiTransform->src != NULL) { - uprv_free(pBiDiTransform->src); - } - uprv_free(pBiDiTransform); - } -} - -/** - * Performs Bidi resolution of text. - * - * @param pTransform Pointer to the UBiDiTransform structure. - * @param pErrorCode Pointer to the error code value. - * - * @return Whether or not this function modifies the text. Besides the return - * value, the caller should also check U_SUCCESS(*pErrorCode). - */ -static UBool -action_resolve(UBiDiTransform *pTransform, UErrorCode *pErrorCode) -{ - ubidi_setPara(pTransform->pBidi, pTransform->src, pTransform->srcLength, - pTransform->pActiveScheme->baseLevel, NULL, pErrorCode); - return false; -} - -/** - * Performs basic reordering of text (Logical -> Visual LTR). - * - * @param pTransform Pointer to the UBiDiTransform structure. - * @param pErrorCode Pointer to the error code value. - * - * @return Whether or not this function modifies the text. Besides the return - * value, the caller should also check U_SUCCESS(*pErrorCode). - */ -static UBool -action_reorder(UBiDiTransform *pTransform, UErrorCode *pErrorCode) -{ - ubidi_writeReordered(pTransform->pBidi, pTransform->dest, pTransform->destSize, - static_cast(pTransform->reorderingOptions), pErrorCode); - - *pTransform->pDestLength = pTransform->srcLength; - pTransform->reorderingOptions = UBIDI_REORDER_DEFAULT; - return true; -} - -/** - * Sets "inverse" mode on the UBiDi object. - * - * @param pTransform Pointer to the UBiDiTransform structure. - * @param pErrorCode Pointer to the error code value. - * - * @return Whether or not this function modifies the text. Besides the return - * value, the caller should also check U_SUCCESS(*pErrorCode). - */ -static UBool -action_setInverse(UBiDiTransform *pTransform, UErrorCode *pErrorCode) -{ - (void)pErrorCode; - ubidi_setInverse(pTransform->pBidi, true); - ubidi_setReorderingMode(pTransform->pBidi, UBIDI_REORDER_INVERSE_LIKE_DIRECT); - return false; -} - -/** - * Sets "runs only" reordering mode indicating a Logical LTR <-> Logical RTL - * transformation. - * - * @param pTransform Pointer to the UBiDiTransform structure. - * @param pErrorCode Pointer to the error code value. - * - * @return Whether or not this function modifies the text. Besides the return - * value, the caller should also check U_SUCCESS(*pErrorCode). - */ -static UBool -action_setRunsOnly(UBiDiTransform *pTransform, UErrorCode *pErrorCode) -{ - (void)pErrorCode; - ubidi_setReorderingMode(pTransform->pBidi, UBIDI_REORDER_RUNS_ONLY); - return false; -} - -/** - * Performs string reverse. - * - * @param pTransform Pointer to the UBiDiTransform structure. - * @param pErrorCode Pointer to the error code value. - * - * @return Whether or not this function modifies the text. Besides the return - * value, the caller should also check U_SUCCESS(*pErrorCode). - */ -static UBool -action_reverse(UBiDiTransform *pTransform, UErrorCode *pErrorCode) -{ - ubidi_writeReverse(pTransform->src, pTransform->srcLength, - pTransform->dest, pTransform->destSize, - UBIDI_REORDER_DEFAULT, pErrorCode); - *pTransform->pDestLength = pTransform->srcLength; - return true; -} - -/** - * Applies a new value to the text that serves as input at the current - * processing step. This value is identical to the original one when we begin - * the processing, but usually changes as the transformation progresses. - * - * @param pTransform A pointer to the UBiDiTransform structure. - * @param newSrc A pointer whose value is to be used as input text. - * @param newLength A length of the new text in UChars. - * @param newSize A new source capacity in UChars. - * @param pErrorCode Pointer to the error code value. - */ -static void -updateSrc(UBiDiTransform *pTransform, const UChar *newSrc, uint32_t newLength, - uint32_t newSize, UErrorCode *pErrorCode) -{ - if (newSize < newLength) { - *pErrorCode = U_BUFFER_OVERFLOW_ERROR; - return; - } - if (newSize > pTransform->srcSize) { - newSize += 50; // allocate slightly more than needed right now - if (pTransform->src != NULL) { - uprv_free(pTransform->src); - pTransform->src = NULL; - } - pTransform->src = (UChar *)uprv_malloc(newSize * sizeof(UChar)); - if (pTransform->src == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - //pTransform->srcLength = pTransform->srcSize = 0; - return; - } - pTransform->srcSize = newSize; - } - u_strncpy(pTransform->src, newSrc, newLength); - pTransform->srcLength = u_terminateUChars(pTransform->src, - pTransform->srcSize, newLength, pErrorCode); -} - -/** - * Calls a lower level shaping function. - * - * @param pTransform Pointer to the UBiDiTransform structure. - * @param options Shaping options. - * @param pErrorCode Pointer to the error code value. - */ -static void -doShape(UBiDiTransform *pTransform, uint32_t options, UErrorCode *pErrorCode) -{ - *pTransform->pDestLength = u_shapeArabic(pTransform->src, - pTransform->srcLength, pTransform->dest, pTransform->destSize, - options, pErrorCode); -} - -/** - * Performs digit and letter shaping. - * - * @param pTransform Pointer to the UBiDiTransform structure. - * @param pErrorCode Pointer to the error code value. - * - * @return Whether or not this function modifies the text. Besides the return - * value, the caller should also check U_SUCCESS(*pErrorCode). - */ -static UBool -action_shapeArabic(UBiDiTransform *pTransform, UErrorCode *pErrorCode) -{ - if ((pTransform->letters | pTransform->digits) == 0) { - return false; - } - if (pTransform->pActiveScheme->lettersDir == pTransform->pActiveScheme->digitsDir) { - doShape(pTransform, pTransform->letters | pTransform->digits | pTransform->pActiveScheme->lettersDir, - pErrorCode); - } else { - doShape(pTransform, pTransform->digits | pTransform->pActiveScheme->digitsDir, pErrorCode); - if (U_SUCCESS(*pErrorCode)) { - updateSrc(pTransform, pTransform->dest, *pTransform->pDestLength, - *pTransform->pDestLength, pErrorCode); - doShape(pTransform, pTransform->letters | pTransform->pActiveScheme->lettersDir, - pErrorCode); - } - } - return true; -} - -/** - * Performs character mirroring. - * - * @param pTransform Pointer to the UBiDiTransform structure. - * @param pErrorCode Pointer to the error code value. - * - * @return Whether or not this function modifies the text. Besides the return - * value, the caller should also check U_SUCCESS(*pErrorCode). - */ -static UBool -action_mirror(UBiDiTransform *pTransform, UErrorCode *pErrorCode) -{ - UChar32 c; - uint32_t i = 0, j = 0; - if (0 == (pTransform->reorderingOptions & UBIDI_DO_MIRRORING)) { - return false; - } - if (pTransform->destSize < pTransform->srcLength) { - *pErrorCode = U_BUFFER_OVERFLOW_ERROR; - return false; - } - do { - UBool isOdd = ubidi_getLevelAt(pTransform->pBidi, i) & 1; - U16_NEXT(pTransform->src, i, pTransform->srcLength, c); - U16_APPEND_UNSAFE(pTransform->dest, j, isOdd ? u_charMirror(c) : c); - } while (i < pTransform->srcLength); - - *pTransform->pDestLength = pTransform->srcLength; - pTransform->reorderingOptions = UBIDI_REORDER_DEFAULT; - return true; -} - -/** - * All possible reordering schemes. - * - */ -static const ReorderingScheme Schemes[] = -{ - /* 0: Logical LTR => Visual LTR */ - {LTR, LOGICAL, LTR, VISUAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR, - {action_shapeArabic, action_resolve, action_reorder, NULL}}, - /* 1: Logical RTL => Visual LTR */ - {RTL, LOGICAL, LTR, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, RTL, - {action_resolve, action_reorder, action_shapeArabic, NULL}}, - /* 2: Logical LTR => Visual RTL */ - {LTR, LOGICAL, RTL, VISUAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR, - {action_shapeArabic, action_resolve, action_reorder, action_reverse, NULL}}, - /* 3: Logical RTL => Visual RTL */ - {RTL, LOGICAL, RTL, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, RTL, - {action_resolve, action_reorder, action_shapeArabic, action_reverse, NULL}}, - /* 4: Visual LTR => Logical RTL */ - {LTR, VISUAL, RTL, LOGICAL, SHAPE_LOGICAL, SHAPE_VISUAL, RTL, - {action_shapeArabic, action_setInverse, action_resolve, action_reorder, NULL}}, - /* 5: Visual RTL => Logical RTL */ - {RTL, VISUAL, RTL, LOGICAL, SHAPE_LOGICAL, SHAPE_VISUAL, RTL, - {action_reverse, action_shapeArabic, action_setInverse, action_resolve, action_reorder, NULL}}, - /* 6: Visual LTR => Logical LTR */ - {LTR, VISUAL, LTR, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR, - {action_setInverse, action_resolve, action_reorder, action_shapeArabic, NULL}}, - /* 7: Visual RTL => Logical LTR */ - {RTL, VISUAL, LTR, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR, - {action_reverse, action_setInverse, action_resolve, action_reorder, action_shapeArabic, NULL}}, - /* 8: Logical LTR => Logical RTL */ - {LTR, LOGICAL, RTL, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR, - {action_shapeArabic, action_resolve, action_mirror, action_setRunsOnly, action_resolve, action_reorder, NULL}}, - /* 9: Logical RTL => Logical LTR */ - {RTL, LOGICAL, LTR, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, RTL, - {action_resolve, action_mirror, action_setRunsOnly, action_resolve, action_reorder, action_shapeArabic, NULL}}, - /* 10: Visual LTR => Visual RTL */ - {LTR, VISUAL, RTL, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, LTR, - {action_shapeArabic, action_setInverse, action_resolve, action_mirror, action_reverse, NULL}}, - /* 11: Visual RTL => Visual LTR */ - {RTL, VISUAL, LTR, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, LTR, - {action_reverse, action_shapeArabic, action_setInverse, action_resolve, action_mirror, NULL}}, - /* 12: Logical LTR => Logical LTR */ - {LTR, LOGICAL, LTR, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR, - {action_resolve, action_mirror, action_shapeArabic, NULL}}, - /* 13: Logical RTL => Logical RTL */ - {RTL, LOGICAL, RTL, LOGICAL, SHAPE_VISUAL, SHAPE_LOGICAL, RTL, - {action_resolve, action_mirror, action_shapeArabic, NULL}}, - /* 14: Visual LTR => Visual LTR */ - {LTR, VISUAL, LTR, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, LTR, - {action_resolve, action_mirror, action_shapeArabic, NULL}}, - /* 15: Visual RTL => Visual RTL */ - {RTL, VISUAL, RTL, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, LTR, - {action_reverse, action_resolve, action_mirror, action_shapeArabic, action_reverse, NULL}} -}; - -static const uint32_t nSchemes = sizeof(Schemes) / sizeof(*Schemes); - -/** - * When the direction option is UBIDI_DEFAULT_LTR or - * UBIDI_DEFAULT_RTL, resolve the base direction according to that - * of the first strong bidi character. - */ -static void -resolveBaseDirection(const UChar *text, uint32_t length, - UBiDiLevel *pInLevel, UBiDiLevel *pOutLevel) -{ - switch (*pInLevel) { - case UBIDI_DEFAULT_LTR: - case UBIDI_DEFAULT_RTL: { - UBiDiLevel level = static_cast(ubidi_getBaseDirection(text, length)); - *pInLevel = static_cast(level != UBIDI_NEUTRAL) ? level - : *pInLevel == UBIDI_DEFAULT_RTL ? static_cast(RTL) : static_cast(LTR); - break; - } - default: - *pInLevel &= 1; - break; - } - switch (*pOutLevel) { - case UBIDI_DEFAULT_LTR: - case UBIDI_DEFAULT_RTL: - *pOutLevel = *pInLevel; - break; - default: - *pOutLevel &= 1; - break; - } -} - -/** - * Finds a valid ReorderingScheme matching the - * caller-defined scheme. - * - * @return A valid ReorderingScheme object or NULL - */ -static const ReorderingScheme* -findMatchingScheme(UBiDiLevel inLevel, UBiDiLevel outLevel, - UBiDiOrder inOrder, UBiDiOrder outOrder) -{ - uint32_t i; - for (i = 0; i < nSchemes; i++) { - const ReorderingScheme *pScheme = Schemes + i; - if (inLevel == pScheme->inLevel && outLevel == pScheme->outLevel - && inOrder == pScheme->inOrder && outOrder == pScheme->outOrder) { - return pScheme; - } - } - return NULL; -} - -U_CAPI uint32_t U_EXPORT2 -ubiditransform_transform(UBiDiTransform *pBiDiTransform, - const UChar *src, int32_t srcLength, - UChar *dest, int32_t destSize, - UBiDiLevel inParaLevel, UBiDiOrder inOrder, - UBiDiLevel outParaLevel, UBiDiOrder outOrder, - UBiDiMirroring doMirroring, uint32_t shapingOptions, - UErrorCode *pErrorCode) -{ - uint32_t destLength = 0; - UBool textChanged = false; - const UBiDiTransform *pOrigTransform = pBiDiTransform; - const UBiDiAction *action = NULL; - - if (U_FAILURE(*pErrorCode)) { - return 0; - } - if (src == NULL || dest == NULL) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - CHECK_LEN(src, srcLength, pErrorCode); - CHECK_LEN(dest, destSize, pErrorCode); - - if (pBiDiTransform == NULL) { - pBiDiTransform = ubiditransform_open(pErrorCode); - if (U_FAILURE(*pErrorCode)) { - return 0; - } - } - /* Current limitation: in multiple paragraphs will be resolved according - to the 1st paragraph */ - resolveBaseDirection(src, srcLength, &inParaLevel, &outParaLevel); - - pBiDiTransform->pActiveScheme = findMatchingScheme(inParaLevel, outParaLevel, - inOrder, outOrder); - if (pBiDiTransform->pActiveScheme == NULL) { - goto cleanup; - } - pBiDiTransform->reorderingOptions = doMirroring ? UBIDI_DO_MIRRORING - : UBIDI_REORDER_DEFAULT; - - /* Ignore TEXT_DIRECTION_* flags, as we apply our own depending on the text - scheme at the time shaping is invoked. */ - shapingOptions &= ~U_SHAPE_TEXT_DIRECTION_MASK; - pBiDiTransform->digits = shapingOptions & ~U_SHAPE_LETTERS_MASK; - pBiDiTransform->letters = shapingOptions & ~U_SHAPE_DIGITS_MASK; - - updateSrc(pBiDiTransform, src, srcLength, destSize > srcLength ? destSize : srcLength, pErrorCode); - if (U_FAILURE(*pErrorCode)) { - goto cleanup; - } - if (pBiDiTransform->pBidi == NULL) { - pBiDiTransform->pBidi = ubidi_openSized(0, 0, pErrorCode); - if (U_FAILURE(*pErrorCode)) { - goto cleanup; - } - } - pBiDiTransform->dest = dest; - pBiDiTransform->destSize = destSize; - pBiDiTransform->pDestLength = &destLength; - - /* Checking for U_SUCCESS() within the loop to bail out on first failure. */ - for (action = pBiDiTransform->pActiveScheme->actions; *action && U_SUCCESS(*pErrorCode); action++) { - if ((*action)(pBiDiTransform, pErrorCode)) { - if (action + 1) { - updateSrc(pBiDiTransform, pBiDiTransform->dest, *pBiDiTransform->pDestLength, - *pBiDiTransform->pDestLength, pErrorCode); - } - textChanged = true; - } - } - ubidi_setInverse(pBiDiTransform->pBidi, false); - - if (!textChanged && U_SUCCESS(*pErrorCode)) { - /* Text was not changed - just copy src to dest */ - if (destSize < srcLength) { - *pErrorCode = U_BUFFER_OVERFLOW_ERROR; - } else { - u_strncpy(dest, src, srcLength); - destLength = srcLength; - } - } -cleanup: - if (pOrigTransform != pBiDiTransform) { - ubiditransform_close(pBiDiTransform); - } else { - pBiDiTransform->dest = NULL; - pBiDiTransform->pDestLength = NULL; - pBiDiTransform->srcLength = 0; - pBiDiTransform->destSize = 0; - } - return U_FAILURE(*pErrorCode) ? 0 : destLength; -} +/* +****************************************************************************** +* +* © 2016 and later: Unicode, Inc. and others. +* License & terms of use: http://www.unicode.org/copyright.html +* +****************************************************************************** +* file name: ubiditransform.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2016jul24 +* created by: Lina Kemmel +* +*/ + +#include "cmemory.h" +#include "unicode/ubidi.h" +#include "unicode/ustring.h" +#include "unicode/ushape.h" +#include "unicode/utf16.h" +#include "ustr_imp.h" +#include "unicode/ubiditransform.h" + +/* Some convenience defines */ +#define LTR UBIDI_LTR +#define RTL UBIDI_RTL +#define LOGICAL UBIDI_LOGICAL +#define VISUAL UBIDI_VISUAL +#define SHAPE_LOGICAL U_SHAPE_TEXT_DIRECTION_LOGICAL +#define SHAPE_VISUAL U_SHAPE_TEXT_DIRECTION_VISUAL_LTR + +#define CHECK_LEN(STR, LEN, ERROR) UPRV_BLOCK_MACRO_BEGIN { \ + if (LEN == 0) return 0; \ + if (LEN < -1) { *(ERROR) = U_ILLEGAL_ARGUMENT_ERROR; return 0; } \ + if (LEN == -1) LEN = u_strlen(STR); \ +} UPRV_BLOCK_MACRO_END + +#define MAX_ACTIONS 7 + +/** + * Typedef for a pointer to a function, which performs some operation (such as + * reordering, setting "inverse" mode, character mirroring, etc.). Return value + * indicates whether the text was changed in the course of this operation or + * not. + */ +typedef UBool (*UBiDiAction)(UBiDiTransform *, UErrorCode *); + +/** + * Structure that holds a predefined reordering scheme, including the following + * information: + *
    + *
  • an input base direction,
  • + *
  • an input order,
  • + *
  • an output base direction,
  • + *
  • an output order,
  • + *
  • a digit shaping direction,
  • + *
  • a letter shaping direction,
  • + *
  • a base direction that should be applied when the reordering engine is + * invoked (which can not always be derived from the caller-defined + * options),
  • + *
  • an array of pointers to functions that accomplish the bidi layout + * transformation.
  • + *
+ */ +typedef struct { + UBiDiLevel inLevel; /* input level */ + UBiDiOrder inOrder; /* input order */ + UBiDiLevel outLevel; /* output level */ + UBiDiOrder outOrder; /* output order */ + uint32_t digitsDir; /* digit shaping direction */ + uint32_t lettersDir; /* letter shaping direction */ + UBiDiLevel baseLevel; /* paragraph level to be used with setPara */ + const UBiDiAction actions[MAX_ACTIONS]; /* array of pointers to functions carrying out the transformation */ +} ReorderingScheme; + +struct UBiDiTransform { + UBiDi *pBidi; /* pointer to a UBiDi object */ + const ReorderingScheme *pActiveScheme; /* effective reordering scheme */ + char16_t *src; /* input text */ + char16_t *dest; /* output text */ + uint32_t srcLength; /* input text length - not really needed as we are zero-terminated and can u_strlen */ + uint32_t srcSize; /* input text capacity excluding the trailing zero */ + uint32_t destSize; /* output text capacity */ + uint32_t *pDestLength; /* number of UChars written to dest */ + uint32_t reorderingOptions; /* reordering options - currently only suppot DO_MIRRORING */ + uint32_t digits; /* digit option for ArabicShaping */ + uint32_t letters; /* letter option for ArabicShaping */ +}; + +U_CAPI UBiDiTransform* U_EXPORT2 +ubiditransform_open(UErrorCode *pErrorCode) +{ + UBiDiTransform *pBiDiTransform = nullptr; + if (U_SUCCESS(*pErrorCode)) { + pBiDiTransform = (UBiDiTransform*) uprv_calloc(1, sizeof(UBiDiTransform)); + if (pBiDiTransform == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + } + } + return pBiDiTransform; +} + +U_CAPI void U_EXPORT2 +ubiditransform_close(UBiDiTransform *pBiDiTransform) +{ + if (pBiDiTransform != nullptr) { + if (pBiDiTransform->pBidi != nullptr) { + ubidi_close(pBiDiTransform->pBidi); + } + if (pBiDiTransform->src != nullptr) { + uprv_free(pBiDiTransform->src); + } + uprv_free(pBiDiTransform); + } +} + +/** + * Performs Bidi resolution of text. + * + * @param pTransform Pointer to the UBiDiTransform structure. + * @param pErrorCode Pointer to the error code value. + * + * @return Whether or not this function modifies the text. Besides the return + * value, the caller should also check U_SUCCESS(*pErrorCode). + */ +static UBool +action_resolve(UBiDiTransform *pTransform, UErrorCode *pErrorCode) +{ + ubidi_setPara(pTransform->pBidi, pTransform->src, pTransform->srcLength, + pTransform->pActiveScheme->baseLevel, nullptr, pErrorCode); + return false; +} + +/** + * Performs basic reordering of text (Logical -> Visual LTR). + * + * @param pTransform Pointer to the UBiDiTransform structure. + * @param pErrorCode Pointer to the error code value. + * + * @return Whether or not this function modifies the text. Besides the return + * value, the caller should also check U_SUCCESS(*pErrorCode). + */ +static UBool +action_reorder(UBiDiTransform *pTransform, UErrorCode *pErrorCode) +{ + ubidi_writeReordered(pTransform->pBidi, pTransform->dest, pTransform->destSize, + static_cast(pTransform->reorderingOptions), pErrorCode); + + *pTransform->pDestLength = pTransform->srcLength; + pTransform->reorderingOptions = UBIDI_REORDER_DEFAULT; + return true; +} + +/** + * Sets "inverse" mode on the UBiDi object. + * + * @param pTransform Pointer to the UBiDiTransform structure. + * @param pErrorCode Pointer to the error code value. + * + * @return Whether or not this function modifies the text. Besides the return + * value, the caller should also check U_SUCCESS(*pErrorCode). + */ +static UBool +action_setInverse(UBiDiTransform *pTransform, UErrorCode *pErrorCode) +{ + (void)pErrorCode; + ubidi_setInverse(pTransform->pBidi, true); + ubidi_setReorderingMode(pTransform->pBidi, UBIDI_REORDER_INVERSE_LIKE_DIRECT); + return false; +} + +/** + * Sets "runs only" reordering mode indicating a Logical LTR <-> Logical RTL + * transformation. + * + * @param pTransform Pointer to the UBiDiTransform structure. + * @param pErrorCode Pointer to the error code value. + * + * @return Whether or not this function modifies the text. Besides the return + * value, the caller should also check U_SUCCESS(*pErrorCode). + */ +static UBool +action_setRunsOnly(UBiDiTransform *pTransform, UErrorCode *pErrorCode) +{ + (void)pErrorCode; + ubidi_setReorderingMode(pTransform->pBidi, UBIDI_REORDER_RUNS_ONLY); + return false; +} + +/** + * Performs string reverse. + * + * @param pTransform Pointer to the UBiDiTransform structure. + * @param pErrorCode Pointer to the error code value. + * + * @return Whether or not this function modifies the text. Besides the return + * value, the caller should also check U_SUCCESS(*pErrorCode). + */ +static UBool +action_reverse(UBiDiTransform *pTransform, UErrorCode *pErrorCode) +{ + ubidi_writeReverse(pTransform->src, pTransform->srcLength, + pTransform->dest, pTransform->destSize, + UBIDI_REORDER_DEFAULT, pErrorCode); + *pTransform->pDestLength = pTransform->srcLength; + return true; +} + +/** + * Applies a new value to the text that serves as input at the current + * processing step. This value is identical to the original one when we begin + * the processing, but usually changes as the transformation progresses. + * + * @param pTransform A pointer to the UBiDiTransform structure. + * @param newSrc A pointer whose value is to be used as input text. + * @param newLength A length of the new text in char16_ts. + * @param newSize A new source capacity in char16_ts. + * @param pErrorCode Pointer to the error code value. + */ +static void +updateSrc(UBiDiTransform *pTransform, const char16_t *newSrc, uint32_t newLength, + uint32_t newSize, UErrorCode *pErrorCode) +{ + if (newSize < newLength) { + *pErrorCode = U_BUFFER_OVERFLOW_ERROR; + return; + } + if (newSize > pTransform->srcSize) { + newSize += 50; // allocate slightly more than needed right now + if (pTransform->src != nullptr) { + uprv_free(pTransform->src); + pTransform->src = nullptr; + } + pTransform->src = (char16_t *)uprv_malloc(newSize * sizeof(char16_t)); + if (pTransform->src == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + //pTransform->srcLength = pTransform->srcSize = 0; + return; + } + pTransform->srcSize = newSize; + } + u_strncpy(pTransform->src, newSrc, newLength); + pTransform->srcLength = u_terminateUChars(pTransform->src, + pTransform->srcSize, newLength, pErrorCode); +} + +/** + * Calls a lower level shaping function. + * + * @param pTransform Pointer to the UBiDiTransform structure. + * @param options Shaping options. + * @param pErrorCode Pointer to the error code value. + */ +static void +doShape(UBiDiTransform *pTransform, uint32_t options, UErrorCode *pErrorCode) +{ + *pTransform->pDestLength = u_shapeArabic(pTransform->src, + pTransform->srcLength, pTransform->dest, pTransform->destSize, + options, pErrorCode); +} + +/** + * Performs digit and letter shaping. + * + * @param pTransform Pointer to the UBiDiTransform structure. + * @param pErrorCode Pointer to the error code value. + * + * @return Whether or not this function modifies the text. Besides the return + * value, the caller should also check U_SUCCESS(*pErrorCode). + */ +static UBool +action_shapeArabic(UBiDiTransform *pTransform, UErrorCode *pErrorCode) +{ + if ((pTransform->letters | pTransform->digits) == 0) { + return false; + } + if (pTransform->pActiveScheme->lettersDir == pTransform->pActiveScheme->digitsDir) { + doShape(pTransform, pTransform->letters | pTransform->digits | pTransform->pActiveScheme->lettersDir, + pErrorCode); + } else { + doShape(pTransform, pTransform->digits | pTransform->pActiveScheme->digitsDir, pErrorCode); + if (U_SUCCESS(*pErrorCode)) { + updateSrc(pTransform, pTransform->dest, *pTransform->pDestLength, + *pTransform->pDestLength, pErrorCode); + doShape(pTransform, pTransform->letters | pTransform->pActiveScheme->lettersDir, + pErrorCode); + } + } + return true; +} + +/** + * Performs character mirroring. + * + * @param pTransform Pointer to the UBiDiTransform structure. + * @param pErrorCode Pointer to the error code value. + * + * @return Whether or not this function modifies the text. Besides the return + * value, the caller should also check U_SUCCESS(*pErrorCode). + */ +static UBool +action_mirror(UBiDiTransform *pTransform, UErrorCode *pErrorCode) +{ + UChar32 c; + uint32_t i = 0, j = 0; + if (0 == (pTransform->reorderingOptions & UBIDI_DO_MIRRORING)) { + return false; + } + if (pTransform->destSize < pTransform->srcLength) { + *pErrorCode = U_BUFFER_OVERFLOW_ERROR; + return false; + } + do { + UBool isOdd = ubidi_getLevelAt(pTransform->pBidi, i) & 1; + U16_NEXT(pTransform->src, i, pTransform->srcLength, c); + U16_APPEND_UNSAFE(pTransform->dest, j, isOdd ? u_charMirror(c) : c); + } while (i < pTransform->srcLength); + + *pTransform->pDestLength = pTransform->srcLength; + pTransform->reorderingOptions = UBIDI_REORDER_DEFAULT; + return true; +} + +/** + * All possible reordering schemes. + * + */ +static const ReorderingScheme Schemes[] = +{ + /* 0: Logical LTR => Visual LTR */ + {LTR, LOGICAL, LTR, VISUAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR, + {action_shapeArabic, action_resolve, action_reorder, nullptr}}, + /* 1: Logical RTL => Visual LTR */ + {RTL, LOGICAL, LTR, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, RTL, + {action_resolve, action_reorder, action_shapeArabic, nullptr}}, + /* 2: Logical LTR => Visual RTL */ + {LTR, LOGICAL, RTL, VISUAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR, + {action_shapeArabic, action_resolve, action_reorder, action_reverse, nullptr}}, + /* 3: Logical RTL => Visual RTL */ + {RTL, LOGICAL, RTL, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, RTL, + {action_resolve, action_reorder, action_shapeArabic, action_reverse, nullptr}}, + /* 4: Visual LTR => Logical RTL */ + {LTR, VISUAL, RTL, LOGICAL, SHAPE_LOGICAL, SHAPE_VISUAL, RTL, + {action_shapeArabic, action_setInverse, action_resolve, action_reorder, nullptr}}, + /* 5: Visual RTL => Logical RTL */ + {RTL, VISUAL, RTL, LOGICAL, SHAPE_LOGICAL, SHAPE_VISUAL, RTL, + {action_reverse, action_shapeArabic, action_setInverse, action_resolve, action_reorder, nullptr}}, + /* 6: Visual LTR => Logical LTR */ + {LTR, VISUAL, LTR, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR, + {action_setInverse, action_resolve, action_reorder, action_shapeArabic, nullptr}}, + /* 7: Visual RTL => Logical LTR */ + {RTL, VISUAL, LTR, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR, + {action_reverse, action_setInverse, action_resolve, action_reorder, action_shapeArabic, nullptr}}, + /* 8: Logical LTR => Logical RTL */ + {LTR, LOGICAL, RTL, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR, + {action_shapeArabic, action_resolve, action_mirror, action_setRunsOnly, action_resolve, action_reorder, nullptr}}, + /* 9: Logical RTL => Logical LTR */ + {RTL, LOGICAL, LTR, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, RTL, + {action_resolve, action_mirror, action_setRunsOnly, action_resolve, action_reorder, action_shapeArabic, nullptr}}, + /* 10: Visual LTR => Visual RTL */ + {LTR, VISUAL, RTL, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, LTR, + {action_shapeArabic, action_setInverse, action_resolve, action_mirror, action_reverse, nullptr}}, + /* 11: Visual RTL => Visual LTR */ + {RTL, VISUAL, LTR, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, LTR, + {action_reverse, action_shapeArabic, action_setInverse, action_resolve, action_mirror, nullptr}}, + /* 12: Logical LTR => Logical LTR */ + {LTR, LOGICAL, LTR, LOGICAL, SHAPE_LOGICAL, SHAPE_LOGICAL, LTR, + {action_resolve, action_mirror, action_shapeArabic, nullptr}}, + /* 13: Logical RTL => Logical RTL */ + {RTL, LOGICAL, RTL, LOGICAL, SHAPE_VISUAL, SHAPE_LOGICAL, RTL, + {action_resolve, action_mirror, action_shapeArabic, nullptr}}, + /* 14: Visual LTR => Visual LTR */ + {LTR, VISUAL, LTR, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, LTR, + {action_resolve, action_mirror, action_shapeArabic, nullptr}}, + /* 15: Visual RTL => Visual RTL */ + {RTL, VISUAL, RTL, VISUAL, SHAPE_LOGICAL, SHAPE_VISUAL, LTR, + {action_reverse, action_resolve, action_mirror, action_shapeArabic, action_reverse, nullptr}} +}; + +static const uint32_t nSchemes = sizeof(Schemes) / sizeof(*Schemes); + +/** + * When the direction option is UBIDI_DEFAULT_LTR or + * UBIDI_DEFAULT_RTL, resolve the base direction according to that + * of the first strong bidi character. + */ +static void +resolveBaseDirection(const char16_t *text, uint32_t length, + UBiDiLevel *pInLevel, UBiDiLevel *pOutLevel) +{ + switch (*pInLevel) { + case UBIDI_DEFAULT_LTR: + case UBIDI_DEFAULT_RTL: { + UBiDiLevel level = static_cast(ubidi_getBaseDirection(text, length)); + *pInLevel = static_cast(level != UBIDI_NEUTRAL) ? level + : *pInLevel == UBIDI_DEFAULT_RTL ? static_cast(RTL) : static_cast(LTR); + break; + } + default: + *pInLevel &= 1; + break; + } + switch (*pOutLevel) { + case UBIDI_DEFAULT_LTR: + case UBIDI_DEFAULT_RTL: + *pOutLevel = *pInLevel; + break; + default: + *pOutLevel &= 1; + break; + } +} + +/** + * Finds a valid ReorderingScheme matching the + * caller-defined scheme. + * + * @return A valid ReorderingScheme object or nullptr + */ +static const ReorderingScheme* +findMatchingScheme(UBiDiLevel inLevel, UBiDiLevel outLevel, + UBiDiOrder inOrder, UBiDiOrder outOrder) +{ + uint32_t i; + for (i = 0; i < nSchemes; i++) { + const ReorderingScheme *pScheme = Schemes + i; + if (inLevel == pScheme->inLevel && outLevel == pScheme->outLevel + && inOrder == pScheme->inOrder && outOrder == pScheme->outOrder) { + return pScheme; + } + } + return nullptr; +} + +U_CAPI uint32_t U_EXPORT2 +ubiditransform_transform(UBiDiTransform *pBiDiTransform, + const char16_t *src, int32_t srcLength, + char16_t *dest, int32_t destSize, + UBiDiLevel inParaLevel, UBiDiOrder inOrder, + UBiDiLevel outParaLevel, UBiDiOrder outOrder, + UBiDiMirroring doMirroring, uint32_t shapingOptions, + UErrorCode *pErrorCode) +{ + uint32_t destLength = 0; + UBool textChanged = false; + const UBiDiTransform *pOrigTransform = pBiDiTransform; + const UBiDiAction *action = nullptr; + + if (U_FAILURE(*pErrorCode)) { + return 0; + } + if (src == nullptr || dest == nullptr) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + CHECK_LEN(src, srcLength, pErrorCode); + CHECK_LEN(dest, destSize, pErrorCode); + + if (pBiDiTransform == nullptr) { + pBiDiTransform = ubiditransform_open(pErrorCode); + if (U_FAILURE(*pErrorCode)) { + return 0; + } + } + /* Current limitation: in multiple paragraphs will be resolved according + to the 1st paragraph */ + resolveBaseDirection(src, srcLength, &inParaLevel, &outParaLevel); + + pBiDiTransform->pActiveScheme = findMatchingScheme(inParaLevel, outParaLevel, + inOrder, outOrder); + if (pBiDiTransform->pActiveScheme == nullptr) { + goto cleanup; + } + pBiDiTransform->reorderingOptions = doMirroring ? UBIDI_DO_MIRRORING + : UBIDI_REORDER_DEFAULT; + + /* Ignore TEXT_DIRECTION_* flags, as we apply our own depending on the text + scheme at the time shaping is invoked. */ + shapingOptions &= ~U_SHAPE_TEXT_DIRECTION_MASK; + pBiDiTransform->digits = shapingOptions & ~U_SHAPE_LETTERS_MASK; + pBiDiTransform->letters = shapingOptions & ~U_SHAPE_DIGITS_MASK; + + updateSrc(pBiDiTransform, src, srcLength, destSize > srcLength ? destSize : srcLength, pErrorCode); + if (U_FAILURE(*pErrorCode)) { + goto cleanup; + } + if (pBiDiTransform->pBidi == nullptr) { + pBiDiTransform->pBidi = ubidi_openSized(0, 0, pErrorCode); + if (U_FAILURE(*pErrorCode)) { + goto cleanup; + } + } + pBiDiTransform->dest = dest; + pBiDiTransform->destSize = destSize; + pBiDiTransform->pDestLength = &destLength; + + /* Checking for U_SUCCESS() within the loop to bail out on first failure. */ + for (action = pBiDiTransform->pActiveScheme->actions; *action && U_SUCCESS(*pErrorCode); action++) { + if ((*action)(pBiDiTransform, pErrorCode)) { + if (action + 1) { + updateSrc(pBiDiTransform, pBiDiTransform->dest, *pBiDiTransform->pDestLength, + *pBiDiTransform->pDestLength, pErrorCode); + } + textChanged = true; + } + } + ubidi_setInverse(pBiDiTransform->pBidi, false); + + if (!textChanged && U_SUCCESS(*pErrorCode)) { + /* Text was not changed - just copy src to dest */ + if (destSize < srcLength) { + *pErrorCode = U_BUFFER_OVERFLOW_ERROR; + } else { + u_strncpy(dest, src, srcLength); + destLength = srcLength; + } + } +cleanup: + if (pOrigTransform != pBiDiTransform) { + ubiditransform_close(pBiDiTransform); + } else { + pBiDiTransform->dest = nullptr; + pBiDiTransform->pDestLength = nullptr; + pBiDiTransform->srcLength = 0; + pBiDiTransform->destSize = 0; + } + return U_FAILURE(*pErrorCode) ? 0 : destLength; +} diff --git a/deps/icu-small/source/common/ubidiwrt.cpp b/deps/icu-small/source/common/ubidiwrt.cpp index a69c0a4b8b16f8..b5a8dd11f097cc 100644 --- a/deps/icu-small/source/common/ubidiwrt.cpp +++ b/deps/icu-small/source/common/ubidiwrt.cpp @@ -1,650 +1,650 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2000-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: ubidiwrt.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999aug06 -* created by: Markus W. Scherer, updated by Matitiahu Allouche -* -* This file contains implementations for BiDi functions that use -* the core algorithm and core API to write reordered text. -*/ - -#include "unicode/utypes.h" -#include "unicode/ustring.h" -#include "unicode/uchar.h" -#include "unicode/ubidi.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "ustr_imp.h" -#include "ubidiimp.h" - -/* - * The function implementations in this file are designed - * for UTF-16 and UTF-32, not for UTF-8. - * - * Assumptions that are not true for UTF-8: - * - Any code point always needs the same number of code units - * ("minimum-length-problem" of UTF-8) - * - The BiDi control characters need only one code unit each - * - * Further assumptions for all UTFs: - * - u_charMirror(c) needs the same number of code units as c - */ -#if defined(UTF_SIZE) && UTF_SIZE==8 -# error reimplement ubidi_writeReordered() for UTF-8, see comment above -#endif - -#define IS_COMBINING(type) ((1UL<<(type))&(1UL<0); - return srcLength; - } - case UBIDI_DO_MIRRORING: { - /* do mirroring */ - int32_t i=0, j=0; - UChar32 c; - - if(destSize0) { - c=*src++; - if(!IS_BIDI_CONTROL_CHAR(c)) { - --remaining; - } - } - return destSize-remaining; - } - *dest++=c; - } - } while(--srcLength>0); - return destSize-remaining; - } - default: { - /* remove BiDi control characters and do mirroring */ - int32_t remaining=destSize; - int32_t i, j=0; - UChar32 c; - do { - i=0; - U16_NEXT(src, i, srcLength, c); - src+=i; - srcLength-=i; - if(!IS_BIDI_CONTROL_CHAR(c)) { - remaining-=i; - if(remaining<0) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - - /* preflight the length */ - while(srcLength>0) { - c=*src++; - if(!IS_BIDI_CONTROL_CHAR(c)) { - --remaining; - } - --srcLength; - } - return destSize-remaining; - } - c=u_charMirror(c); - U16_APPEND_UNSAFE(dest, j, c); - } - } while(srcLength>0); - return j; - } - } /* end of switch */ -} - -static int32_t -doWriteReverse(const UChar *src, int32_t srcLength, - UChar *dest, int32_t destSize, - uint16_t options, - UErrorCode *pErrorCode) { - /* - * RTL run - - * - * RTL runs need to be copied to the destination in reverse order - * of code points, not code units, to keep Unicode characters intact. - * - * The general strategy for this is to read the source text - * in backward order, collect all code units for a code point - * (and optionally following combining characters, see below), - * and copy all these code units in ascending order - * to the destination for this run. - * - * Several options request whether combining characters - * should be kept after their base characters, - * whether BiDi control characters should be removed, and - * whether characters should be replaced by their mirror-image - * equivalent Unicode characters. - */ - int32_t i, j; - UChar32 c; - - /* optimize for several combinations of options */ - switch(options&(UBIDI_REMOVE_BIDI_CONTROLS|UBIDI_DO_MIRRORING|UBIDI_KEEP_BASE_COMBINING)) { - case 0: - /* - * With none of the "complicated" options set, the destination - * run will have the same length as the source run, - * and there is no mirroring and no keeping combining characters - * with their base characters. - */ - if(destSize0); - break; - case UBIDI_KEEP_BASE_COMBINING: - /* - * Here, too, the destination - * run will have the same length as the source run, - * and there is no mirroring. - * We do need to keep combining characters with their base characters. - */ - if(destSize0 && IS_COMBINING(u_charType(c))); - - /* copy this "user character" */ - j=srcLength; - do { - *dest++=src[j++]; - } while(j0); - break; - default: - /* - * With several "complicated" options set, this is the most - * general and the slowest copying of an RTL run. - * We will do mirroring, remove BiDi controls, and - * keep combining characters with their base characters - * as requested. - */ - if(!(options&UBIDI_REMOVE_BIDI_CONTROLS)) { - i=srcLength; - } else { - /* we need to find out the destination length of the run, - which will not include the BiDi control characters */ - int32_t length=srcLength; - UChar ch; - - i=0; - do { - ch=*src++; - if(!IS_BIDI_CONTROL_CHAR(ch)) { - ++i; - } - } while(--length>0); - src-=srcLength; - } - - if(destSize0 && IS_COMBINING(u_charType(c))) { - U16_PREV(src, 0, srcLength, c); - } - } - - if(options&UBIDI_REMOVE_BIDI_CONTROLS && IS_BIDI_CONTROL_CHAR(c)) { - /* do not copy this BiDi control character */ - continue; - } - - /* copy this "user character" */ - j=srcLength; - if(options&UBIDI_DO_MIRRORING) { - /* mirror only the base character */ - int32_t k=0; - c=u_charMirror(c); - U16_APPEND_UNSAFE(dest, k, c); - dest+=k; - j+=k; - } - while(j0); - break; - } /* end of switch */ - - return destSize; -} - -U_CAPI int32_t U_EXPORT2 -ubidi_writeReverse(const UChar *src, int32_t srcLength, - UChar *dest, int32_t destSize, - uint16_t options, - UErrorCode *pErrorCode) { - int32_t destLength; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - /* more error checking */ - if( src==NULL || srcLength<-1 || - destSize<0 || (destSize>0 && dest==NULL)) - { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* do input and output overlap? */ - if( dest!=NULL && - ((src>=dest && src=src && dest0) { - destLength=doWriteReverse(src, srcLength, dest, destSize, options, pErrorCode); - } else { - /* nothing to do */ - destLength=0; - } - - return u_terminateUChars(dest, destSize, destLength, pErrorCode); -} - -// Ticket 20907 - The optimizer in MSVC/Visual Studio versions below 16.4 has trouble with this -// function on Windows ARM64. As a work-around, we disable optimizations for this function. -// This work-around could/should be removed once the following versions of Visual Studio are no -// longer supported: All versions of VS2017, and versions of VS2019 below 16.4. -#if (defined(_MSC_VER) && (defined(_M_ARM64)) && (_MSC_VER < 1924)) -#pragma optimize( "", off ) -#endif -U_CAPI int32_t U_EXPORT2 -ubidi_writeReordered(UBiDi *pBiDi, - UChar *dest, int32_t destSize, - uint16_t options, - UErrorCode *pErrorCode) { - const UChar *text; - UChar *saveDest; - int32_t length, destCapacity; - int32_t run, runCount, logicalStart, runLength; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - /* more error checking */ - if( pBiDi==NULL || - (text=pBiDi->text)==NULL || (length=pBiDi->length)<0 || - destSize<0 || (destSize>0 && dest==NULL)) - { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* do input and output overlap? */ - if( dest!=NULL && - ((text>=dest && text=text && destoriginalLength))) - { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - if(length==0) { - /* nothing to do */ - return u_terminateUChars(dest, destSize, 0, pErrorCode); - } - - runCount=ubidi_countRuns(pBiDi, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return 0; - } - - /* destSize shrinks, later destination length=destCapacity-destSize */ - saveDest=dest; - destCapacity=destSize; - - /* - * Option "insert marks" implies UBIDI_INSERT_LRM_FOR_NUMERIC if the - * reordering mode (checked below) is appropriate. - */ - if(pBiDi->reorderingOptions & UBIDI_OPTION_INSERT_MARKS) { - options|=UBIDI_INSERT_LRM_FOR_NUMERIC; - options&=~UBIDI_REMOVE_BIDI_CONTROLS; - } - /* - * Option "remove controls" implies UBIDI_REMOVE_BIDI_CONTROLS - * and cancels UBIDI_INSERT_LRM_FOR_NUMERIC. - */ - if(pBiDi->reorderingOptions & UBIDI_OPTION_REMOVE_CONTROLS) { - options|=UBIDI_REMOVE_BIDI_CONTROLS; - options&=~UBIDI_INSERT_LRM_FOR_NUMERIC; - } - /* - * If we do not perform the "inverse BiDi" algorithm, then we - * don't need to insert any LRMs, and don't need to test for it. - */ - if((pBiDi->reorderingMode != UBIDI_REORDER_INVERSE_NUMBERS_AS_L) && - (pBiDi->reorderingMode != UBIDI_REORDER_INVERSE_LIKE_DIRECT) && - (pBiDi->reorderingMode != UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL) && - (pBiDi->reorderingMode != UBIDI_REORDER_RUNS_ONLY)) { - options&=~UBIDI_INSERT_LRM_FOR_NUMERIC; - } - /* - * Iterate through all visual runs and copy the run text segments to - * the destination, according to the options. - * - * The tests for where to insert LRMs ignore the fact that there may be - * BN codes or non-BMP code points at the beginning and end of a run; - * they may insert LRMs unnecessarily but the tests are faster this way - * (this would have to be improved for UTF-8). - * - * Note that the only errors that are set by doWriteXY() are buffer overflow - * errors. Ignore them until the end, and continue for preflighting. - */ - if(!(options&UBIDI_OUTPUT_REVERSE)) { - /* forward output */ - if(!(options&UBIDI_INSERT_LRM_FOR_NUMERIC)) { - /* do not insert BiDi controls */ - for(run=0; rundirProps; - const UChar *src; - UChar uc; - UBiDiDirection dir; - int32_t markFlag; - - for(run=0; runruns[run].insertRemove; - if(markFlag<0) { /* BiDi controls count */ - markFlag=0; - } - - if(UBIDI_LTR==dir) { - if((pBiDi->isInverse) && - (/*run>0 &&*/ dirProps[logicalStart]!=L)) { - markFlag |= LRM_BEFORE; - } - if (markFlag & LRM_BEFORE) { - uc=LRM_CHAR; - } - else if (markFlag & RLM_BEFORE) { - uc=RLM_CHAR; - } - else uc=0; - if(uc) { - if(destSize>0) { - *dest++=uc; - } - --destSize; - } - - runLength=doWriteForward(src, runLength, - dest, destSize, - (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode); - if(dest!=NULL) { - dest+=runLength; - } - destSize-=runLength; - - if((pBiDi->isInverse) && - (/*run0) { - *dest++=uc; - } - --destSize; - } - } else { /* RTL run */ - if((pBiDi->isInverse) && - (/*run>0 &&*/ !(MASK_R_AL&DIRPROP_FLAG(dirProps[logicalStart+runLength-1])))) { - markFlag |= RLM_BEFORE; - } - if (markFlag & LRM_BEFORE) { - uc=LRM_CHAR; - } - else if (markFlag & RLM_BEFORE) { - uc=RLM_CHAR; - } - else uc=0; - if(uc) { - if(destSize>0) { - *dest++=uc; - } - --destSize; - } - - runLength=doWriteReverse(src, runLength, - dest, destSize, - options, pErrorCode); - if(dest!=NULL) { - dest+=runLength; - } - destSize-=runLength; - - if((pBiDi->isInverse) && - (/*run0) { - *dest++=uc; - } - --destSize; - } - } - } - } - } else { - /* reverse output */ - if(!(options&UBIDI_INSERT_LRM_FOR_NUMERIC)) { - /* do not insert BiDi controls */ - for(run=runCount; --run>=0;) { - if(UBIDI_LTR==ubidi_getVisualRun(pBiDi, run, &logicalStart, &runLength)) { - runLength=doWriteReverse(text+logicalStart, runLength, - dest, destSize, - (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode); - } else { - runLength=doWriteForward(text+logicalStart, runLength, - dest, destSize, - options, pErrorCode); - } - if(dest!=NULL) { - dest+=runLength; - } - destSize-=runLength; - } - } else { - /* insert BiDi controls for "inverse BiDi" */ - const DirProp *dirProps=pBiDi->dirProps; - const UChar *src; - UBiDiDirection dir; - - for(run=runCount; --run>=0;) { - /* reverse output */ - dir=ubidi_getVisualRun(pBiDi, run, &logicalStart, &runLength); - src=text+logicalStart; - - if(UBIDI_LTR==dir) { - if(/*run0) { - *dest++=LRM_CHAR; - } - --destSize; - } - - runLength=doWriteReverse(src, runLength, - dest, destSize, - (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode); - if(dest!=NULL) { - dest+=runLength; - } - destSize-=runLength; - - if(/*run>0 &&*/ dirProps[logicalStart]!=L) { - if(destSize>0) { - *dest++=LRM_CHAR; - } - --destSize; - } - } else { - if(/*run0) { - *dest++=RLM_CHAR; - } - --destSize; - } - - runLength=doWriteForward(src, runLength, - dest, destSize, - options, pErrorCode); - if(dest!=NULL) { - dest+=runLength; - } - destSize-=runLength; - - if(/*run>0 &&*/ !(MASK_R_AL&DIRPROP_FLAG(dirProps[logicalStart+runLength-1]))) { - if(destSize>0) { - *dest++=RLM_CHAR; - } - --destSize; - } - } - } - } - } - - return u_terminateUChars(saveDest, destCapacity, destCapacity-destSize, pErrorCode); -} -#if (defined(_MSC_VER) && (defined(_M_ARM64)) && (_MSC_VER < 1924)) -#pragma optimize( "", on ) -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2000-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ubidiwrt.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999aug06 +* created by: Markus W. Scherer, updated by Matitiahu Allouche +* +* This file contains implementations for BiDi functions that use +* the core algorithm and core API to write reordered text. +*/ + +#include "unicode/utypes.h" +#include "unicode/ustring.h" +#include "unicode/uchar.h" +#include "unicode/ubidi.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "ustr_imp.h" +#include "ubidiimp.h" + +/* + * The function implementations in this file are designed + * for UTF-16 and UTF-32, not for UTF-8. + * + * Assumptions that are not true for UTF-8: + * - Any code point always needs the same number of code units + * ("minimum-length-problem" of UTF-8) + * - The BiDi control characters need only one code unit each + * + * Further assumptions for all UTFs: + * - u_charMirror(c) needs the same number of code units as c + */ +#if defined(UTF_SIZE) && UTF_SIZE==8 +# error reimplement ubidi_writeReordered() for UTF-8, see comment above +#endif + +#define IS_COMBINING(type) ((1UL<<(type))&(1UL<0); + return srcLength; + } + case UBIDI_DO_MIRRORING: { + /* do mirroring */ + int32_t i=0, j=0; + UChar32 c; + + if(destSize0) { + c=*src++; + if(!IS_BIDI_CONTROL_CHAR(c)) { + --remaining; + } + } + return destSize-remaining; + } + *dest++=c; + } + } while(--srcLength>0); + return destSize-remaining; + } + default: { + /* remove BiDi control characters and do mirroring */ + int32_t remaining=destSize; + int32_t i, j=0; + UChar32 c; + do { + i=0; + U16_NEXT(src, i, srcLength, c); + src+=i; + srcLength-=i; + if(!IS_BIDI_CONTROL_CHAR(c)) { + remaining-=i; + if(remaining<0) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + + /* preflight the length */ + while(srcLength>0) { + c=*src++; + if(!IS_BIDI_CONTROL_CHAR(c)) { + --remaining; + } + --srcLength; + } + return destSize-remaining; + } + c=u_charMirror(c); + U16_APPEND_UNSAFE(dest, j, c); + } + } while(srcLength>0); + return j; + } + } /* end of switch */ +} + +static int32_t +doWriteReverse(const char16_t *src, int32_t srcLength, + char16_t *dest, int32_t destSize, + uint16_t options, + UErrorCode *pErrorCode) { + /* + * RTL run - + * + * RTL runs need to be copied to the destination in reverse order + * of code points, not code units, to keep Unicode characters intact. + * + * The general strategy for this is to read the source text + * in backward order, collect all code units for a code point + * (and optionally following combining characters, see below), + * and copy all these code units in ascending order + * to the destination for this run. + * + * Several options request whether combining characters + * should be kept after their base characters, + * whether BiDi control characters should be removed, and + * whether characters should be replaced by their mirror-image + * equivalent Unicode characters. + */ + int32_t i, j; + UChar32 c; + + /* optimize for several combinations of options */ + switch(options&(UBIDI_REMOVE_BIDI_CONTROLS|UBIDI_DO_MIRRORING|UBIDI_KEEP_BASE_COMBINING)) { + case 0: + /* + * With none of the "complicated" options set, the destination + * run will have the same length as the source run, + * and there is no mirroring and no keeping combining characters + * with their base characters. + */ + if(destSize0); + break; + case UBIDI_KEEP_BASE_COMBINING: + /* + * Here, too, the destination + * run will have the same length as the source run, + * and there is no mirroring. + * We do need to keep combining characters with their base characters. + */ + if(destSize0 && IS_COMBINING(u_charType(c))); + + /* copy this "user character" */ + j=srcLength; + do { + *dest++=src[j++]; + } while(j0); + break; + default: + /* + * With several "complicated" options set, this is the most + * general and the slowest copying of an RTL run. + * We will do mirroring, remove BiDi controls, and + * keep combining characters with their base characters + * as requested. + */ + if(!(options&UBIDI_REMOVE_BIDI_CONTROLS)) { + i=srcLength; + } else { + /* we need to find out the destination length of the run, + which will not include the BiDi control characters */ + int32_t length=srcLength; + char16_t ch; + + i=0; + do { + ch=*src++; + if(!IS_BIDI_CONTROL_CHAR(ch)) { + ++i; + } + } while(--length>0); + src-=srcLength; + } + + if(destSize0 && IS_COMBINING(u_charType(c))) { + U16_PREV(src, 0, srcLength, c); + } + } + + if(options&UBIDI_REMOVE_BIDI_CONTROLS && IS_BIDI_CONTROL_CHAR(c)) { + /* do not copy this BiDi control character */ + continue; + } + + /* copy this "user character" */ + j=srcLength; + if(options&UBIDI_DO_MIRRORING) { + /* mirror only the base character */ + int32_t k=0; + c=u_charMirror(c); + U16_APPEND_UNSAFE(dest, k, c); + dest+=k; + j+=k; + } + while(j0); + break; + } /* end of switch */ + + return destSize; +} + +U_CAPI int32_t U_EXPORT2 +ubidi_writeReverse(const char16_t *src, int32_t srcLength, + char16_t *dest, int32_t destSize, + uint16_t options, + UErrorCode *pErrorCode) { + int32_t destLength; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + /* more error checking */ + if( src==nullptr || srcLength<-1 || + destSize<0 || (destSize>0 && dest==nullptr)) + { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* do input and output overlap? */ + if( dest!=nullptr && + ((src>=dest && src=src && dest0) { + destLength=doWriteReverse(src, srcLength, dest, destSize, options, pErrorCode); + } else { + /* nothing to do */ + destLength=0; + } + + return u_terminateUChars(dest, destSize, destLength, pErrorCode); +} + +// Ticket 20907 - The optimizer in MSVC/Visual Studio versions below 16.4 has trouble with this +// function on Windows ARM64. As a work-around, we disable optimizations for this function. +// This work-around could/should be removed once the following versions of Visual Studio are no +// longer supported: All versions of VS2017, and versions of VS2019 below 16.4. +#if (defined(_MSC_VER) && (defined(_M_ARM64)) && (_MSC_VER < 1924)) +#pragma optimize( "", off ) +#endif +U_CAPI int32_t U_EXPORT2 +ubidi_writeReordered(UBiDi *pBiDi, + char16_t *dest, int32_t destSize, + uint16_t options, + UErrorCode *pErrorCode) { + const char16_t *text; + char16_t *saveDest; + int32_t length, destCapacity; + int32_t run, runCount, logicalStart, runLength; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + /* more error checking */ + if( pBiDi==nullptr || + (text=pBiDi->text)==nullptr || (length=pBiDi->length)<0 || + destSize<0 || (destSize>0 && dest==nullptr)) + { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* do input and output overlap? */ + if( dest!=nullptr && + ((text>=dest && text=text && destoriginalLength))) + { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + if(length==0) { + /* nothing to do */ + return u_terminateUChars(dest, destSize, 0, pErrorCode); + } + + runCount=ubidi_countRuns(pBiDi, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return 0; + } + + /* destSize shrinks, later destination length=destCapacity-destSize */ + saveDest=dest; + destCapacity=destSize; + + /* + * Option "insert marks" implies UBIDI_INSERT_LRM_FOR_NUMERIC if the + * reordering mode (checked below) is appropriate. + */ + if(pBiDi->reorderingOptions & UBIDI_OPTION_INSERT_MARKS) { + options|=UBIDI_INSERT_LRM_FOR_NUMERIC; + options&=~UBIDI_REMOVE_BIDI_CONTROLS; + } + /* + * Option "remove controls" implies UBIDI_REMOVE_BIDI_CONTROLS + * and cancels UBIDI_INSERT_LRM_FOR_NUMERIC. + */ + if(pBiDi->reorderingOptions & UBIDI_OPTION_REMOVE_CONTROLS) { + options|=UBIDI_REMOVE_BIDI_CONTROLS; + options&=~UBIDI_INSERT_LRM_FOR_NUMERIC; + } + /* + * If we do not perform the "inverse BiDi" algorithm, then we + * don't need to insert any LRMs, and don't need to test for it. + */ + if((pBiDi->reorderingMode != UBIDI_REORDER_INVERSE_NUMBERS_AS_L) && + (pBiDi->reorderingMode != UBIDI_REORDER_INVERSE_LIKE_DIRECT) && + (pBiDi->reorderingMode != UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL) && + (pBiDi->reorderingMode != UBIDI_REORDER_RUNS_ONLY)) { + options&=~UBIDI_INSERT_LRM_FOR_NUMERIC; + } + /* + * Iterate through all visual runs and copy the run text segments to + * the destination, according to the options. + * + * The tests for where to insert LRMs ignore the fact that there may be + * BN codes or non-BMP code points at the beginning and end of a run; + * they may insert LRMs unnecessarily but the tests are faster this way + * (this would have to be improved for UTF-8). + * + * Note that the only errors that are set by doWriteXY() are buffer overflow + * errors. Ignore them until the end, and continue for preflighting. + */ + if(!(options&UBIDI_OUTPUT_REVERSE)) { + /* forward output */ + if(!(options&UBIDI_INSERT_LRM_FOR_NUMERIC)) { + /* do not insert BiDi controls */ + for(run=0; rundirProps; + const char16_t *src; + char16_t uc; + UBiDiDirection dir; + int32_t markFlag; + + for(run=0; runruns[run].insertRemove; + if(markFlag<0) { /* BiDi controls count */ + markFlag=0; + } + + if(UBIDI_LTR==dir) { + if((pBiDi->isInverse) && + (/*run>0 &&*/ dirProps[logicalStart]!=L)) { + markFlag |= LRM_BEFORE; + } + if (markFlag & LRM_BEFORE) { + uc=LRM_CHAR; + } + else if (markFlag & RLM_BEFORE) { + uc=RLM_CHAR; + } + else uc=0; + if(uc) { + if(destSize>0) { + *dest++=uc; + } + --destSize; + } + + runLength=doWriteForward(src, runLength, + dest, destSize, + (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode); + if(dest!=nullptr) { + dest+=runLength; + } + destSize-=runLength; + + if((pBiDi->isInverse) && + (/*run0) { + *dest++=uc; + } + --destSize; + } + } else { /* RTL run */ + if((pBiDi->isInverse) && + (/*run>0 &&*/ !(MASK_R_AL&DIRPROP_FLAG(dirProps[logicalStart+runLength-1])))) { + markFlag |= RLM_BEFORE; + } + if (markFlag & LRM_BEFORE) { + uc=LRM_CHAR; + } + else if (markFlag & RLM_BEFORE) { + uc=RLM_CHAR; + } + else uc=0; + if(uc) { + if(destSize>0) { + *dest++=uc; + } + --destSize; + } + + runLength=doWriteReverse(src, runLength, + dest, destSize, + options, pErrorCode); + if(dest!=nullptr) { + dest+=runLength; + } + destSize-=runLength; + + if((pBiDi->isInverse) && + (/*run0) { + *dest++=uc; + } + --destSize; + } + } + } + } + } else { + /* reverse output */ + if(!(options&UBIDI_INSERT_LRM_FOR_NUMERIC)) { + /* do not insert BiDi controls */ + for(run=runCount; --run>=0;) { + if(UBIDI_LTR==ubidi_getVisualRun(pBiDi, run, &logicalStart, &runLength)) { + runLength=doWriteReverse(text+logicalStart, runLength, + dest, destSize, + (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode); + } else { + runLength=doWriteForward(text+logicalStart, runLength, + dest, destSize, + options, pErrorCode); + } + if(dest!=nullptr) { + dest+=runLength; + } + destSize-=runLength; + } + } else { + /* insert BiDi controls for "inverse BiDi" */ + const DirProp *dirProps=pBiDi->dirProps; + const char16_t *src; + UBiDiDirection dir; + + for(run=runCount; --run>=0;) { + /* reverse output */ + dir=ubidi_getVisualRun(pBiDi, run, &logicalStart, &runLength); + src=text+logicalStart; + + if(UBIDI_LTR==dir) { + if(/*run0) { + *dest++=LRM_CHAR; + } + --destSize; + } + + runLength=doWriteReverse(src, runLength, + dest, destSize, + (uint16_t)(options&~UBIDI_DO_MIRRORING), pErrorCode); + if(dest!=nullptr) { + dest+=runLength; + } + destSize-=runLength; + + if(/*run>0 &&*/ dirProps[logicalStart]!=L) { + if(destSize>0) { + *dest++=LRM_CHAR; + } + --destSize; + } + } else { + if(/*run0) { + *dest++=RLM_CHAR; + } + --destSize; + } + + runLength=doWriteForward(src, runLength, + dest, destSize, + options, pErrorCode); + if(dest!=nullptr) { + dest+=runLength; + } + destSize-=runLength; + + if(/*run>0 &&*/ !(MASK_R_AL&DIRPROP_FLAG(dirProps[logicalStart+runLength-1]))) { + if(destSize>0) { + *dest++=RLM_CHAR; + } + --destSize; + } + } + } + } + } + + return u_terminateUChars(saveDest, destCapacity, destCapacity-destSize, pErrorCode); +} +#if (defined(_MSC_VER) && (defined(_M_ARM64)) && (_MSC_VER < 1924)) +#pragma optimize( "", on ) +#endif diff --git a/deps/icu-small/source/common/ubrk.cpp b/deps/icu-small/source/common/ubrk.cpp index f4e064961f3968..c3e2cfc66442c8 100644 --- a/deps/icu-small/source/common/ubrk.cpp +++ b/deps/icu-small/source/common/ubrk.cpp @@ -1,361 +1,361 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************** -* Copyright (C) 1996-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/ubrk.h" - -#include "unicode/brkiter.h" -#include "unicode/uloc.h" -#include "unicode/ustring.h" -#include "unicode/uchriter.h" -#include "unicode/rbbi.h" -#include "rbbirb.h" -#include "uassert.h" -#include "cmemory.h" - -U_NAMESPACE_USE - -//------------------------------------------------------------------------------ -// -// ubrk_open Create a canned type of break iterator based on type (word, line, etc.) -// and locale. -// -//------------------------------------------------------------------------------ -U_CAPI UBreakIterator* U_EXPORT2 -ubrk_open(UBreakIteratorType type, - const char *locale, - const UChar *text, - int32_t textLength, - UErrorCode *status) -{ - - if(U_FAILURE(*status)) return 0; - - BreakIterator *result = 0; - - switch(type) { - - case UBRK_CHARACTER: - result = BreakIterator::createCharacterInstance(Locale(locale), *status); - break; - - case UBRK_WORD: - result = BreakIterator::createWordInstance(Locale(locale), *status); - break; - - case UBRK_LINE: - result = BreakIterator::createLineInstance(Locale(locale), *status); - break; - - case UBRK_SENTENCE: - result = BreakIterator::createSentenceInstance(Locale(locale), *status); - break; - - case UBRK_TITLE: - result = BreakIterator::createTitleInstance(Locale(locale), *status); - break; - - default: - *status = U_ILLEGAL_ARGUMENT_ERROR; - } - - // check for allocation error - if (U_FAILURE(*status)) { - return 0; - } - if(result == 0) { - *status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - - - UBreakIterator *uBI = (UBreakIterator *)result; - if (text != NULL) { - ubrk_setText(uBI, text, textLength, status); - } - return uBI; -} - - - -//------------------------------------------------------------------------------ -// -// ubrk_openRules open a break iterator from a set of break rules. -// Invokes the rule builder. -// -//------------------------------------------------------------------------------ -U_CAPI UBreakIterator* U_EXPORT2 -ubrk_openRules( const UChar *rules, - int32_t rulesLength, - const UChar *text, - int32_t textLength, - UParseError *parseErr, - UErrorCode *status) { - - if (status == NULL || U_FAILURE(*status)){ - return 0; - } - - BreakIterator *result = 0; - UnicodeString ruleString(rules, rulesLength); - result = RBBIRuleBuilder::createRuleBasedBreakIterator(ruleString, parseErr, *status); - if(U_FAILURE(*status)) { - return 0; - } - - UBreakIterator *uBI = (UBreakIterator *)result; - if (text != NULL) { - ubrk_setText(uBI, text, textLength, status); - } - return uBI; -} - - -U_CAPI UBreakIterator* U_EXPORT2 -ubrk_openBinaryRules(const uint8_t *binaryRules, int32_t rulesLength, - const UChar * text, int32_t textLength, - UErrorCode * status) -{ - if (U_FAILURE(*status)) { - return NULL; - } - if (rulesLength < 0) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - LocalPointer lpRBBI(new RuleBasedBreakIterator(binaryRules, rulesLength, *status), *status); - if (U_FAILURE(*status)) { - return NULL; - } - UBreakIterator *uBI = reinterpret_cast(lpRBBI.orphan()); - if (text != NULL) { - ubrk_setText(uBI, text, textLength, status); - } - return uBI; -} - - -U_CAPI UBreakIterator * U_EXPORT2 -ubrk_safeClone( - const UBreakIterator *bi, - void * /*stackBuffer*/, - int32_t *pBufferSize, - UErrorCode *status) -{ - if (status == NULL || U_FAILURE(*status)){ - return NULL; - } - if (bi == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - if (pBufferSize != NULL) { - int32_t inputSize = *pBufferSize; - *pBufferSize = 1; - if (inputSize == 0) { - return NULL; // preflighting for deprecated functionality - } - } - BreakIterator *newBI = ((BreakIterator *)bi)->clone(); - if (newBI == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - } else if (pBufferSize != NULL) { - *status = U_SAFECLONE_ALLOCATED_WARNING; - } - return (UBreakIterator *)newBI; -} - -U_CAPI UBreakIterator * U_EXPORT2 -ubrk_clone(const UBreakIterator *bi, UErrorCode *status) { - return ubrk_safeClone(bi, nullptr, nullptr, status); -} - - -U_CAPI void U_EXPORT2 -ubrk_close(UBreakIterator *bi) -{ - delete (BreakIterator *)bi; -} - -U_CAPI void U_EXPORT2 -ubrk_setText(UBreakIterator* bi, - const UChar* text, - int32_t textLength, - UErrorCode* status) -{ - UText ut = UTEXT_INITIALIZER; - utext_openUChars(&ut, text, textLength, status); - ((BreakIterator*)bi)->setText(&ut, *status); - // A stack allocated UText wrapping a UChar * string - // can be dumped without explicitly closing it. -} - - - -U_CAPI void U_EXPORT2 -ubrk_setUText(UBreakIterator *bi, - UText *text, - UErrorCode *status) -{ - ((BreakIterator*)bi)->setText(text, *status); -} - - - - - -U_CAPI int32_t U_EXPORT2 -ubrk_current(const UBreakIterator *bi) -{ - - return ((BreakIterator*)bi)->current(); -} - -U_CAPI int32_t U_EXPORT2 -ubrk_next(UBreakIterator *bi) -{ - - return ((BreakIterator*)bi)->next(); -} - -U_CAPI int32_t U_EXPORT2 -ubrk_previous(UBreakIterator *bi) -{ - - return ((BreakIterator*)bi)->previous(); -} - -U_CAPI int32_t U_EXPORT2 -ubrk_first(UBreakIterator *bi) -{ - - return ((BreakIterator*)bi)->first(); -} - -U_CAPI int32_t U_EXPORT2 -ubrk_last(UBreakIterator *bi) -{ - - return ((BreakIterator*)bi)->last(); -} - -U_CAPI int32_t U_EXPORT2 -ubrk_preceding(UBreakIterator *bi, - int32_t offset) -{ - - return ((BreakIterator*)bi)->preceding(offset); -} - -U_CAPI int32_t U_EXPORT2 -ubrk_following(UBreakIterator *bi, - int32_t offset) -{ - - return ((BreakIterator*)bi)->following(offset); -} - -U_CAPI const char* U_EXPORT2 -ubrk_getAvailable(int32_t index) -{ - - return uloc_getAvailable(index); -} - -U_CAPI int32_t U_EXPORT2 -ubrk_countAvailable() -{ - - return uloc_countAvailable(); -} - - -U_CAPI UBool U_EXPORT2 -ubrk_isBoundary(UBreakIterator *bi, int32_t offset) -{ - return ((BreakIterator*)bi)->isBoundary(offset); -} - - -U_CAPI int32_t U_EXPORT2 -ubrk_getRuleStatus(UBreakIterator *bi) -{ - return ((BreakIterator*)bi)->getRuleStatus(); -} - -U_CAPI int32_t U_EXPORT2 -ubrk_getRuleStatusVec(UBreakIterator *bi, int32_t *fillInVec, int32_t capacity, UErrorCode *status) -{ - return ((BreakIterator*)bi)->getRuleStatusVec(fillInVec, capacity, *status); -} - - -U_CAPI const char* U_EXPORT2 -ubrk_getLocaleByType(const UBreakIterator *bi, - ULocDataLocaleType type, - UErrorCode* status) -{ - if (bi == NULL) { - if (U_SUCCESS(*status)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - } - return NULL; - } - return ((BreakIterator*)bi)->getLocaleID(type, *status); -} - - -U_CAPI void U_EXPORT2 -ubrk_refreshUText(UBreakIterator *bi, - UText *text, - UErrorCode *status) -{ - BreakIterator *bii = reinterpret_cast(bi); - bii->refreshInputText(text, *status); -} - -U_CAPI int32_t U_EXPORT2 -ubrk_getBinaryRules(UBreakIterator *bi, - uint8_t * binaryRules, int32_t rulesCapacity, - UErrorCode * status) -{ - if (U_FAILURE(*status)) { - return 0; - } - if ((binaryRules == NULL && rulesCapacity > 0) || rulesCapacity < 0) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - RuleBasedBreakIterator* rbbi; - if ((rbbi = dynamic_cast(reinterpret_cast(bi))) == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - uint32_t rulesLength; - const uint8_t * returnedRules = rbbi->getBinaryRules(rulesLength); - if (rulesLength > INT32_MAX) { - *status = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - if (binaryRules != NULL) { // if not preflighting - // Here we know rulesLength <= INT32_MAX and rulesCapacity >= 0, can cast safely - if ((int32_t)rulesLength > rulesCapacity) { - *status = U_BUFFER_OVERFLOW_ERROR; - } else { - uprv_memcpy(binaryRules, returnedRules, rulesLength); - } - } - return (int32_t)rulesLength; -} - - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************** +* Copyright (C) 1996-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/ubrk.h" + +#include "unicode/brkiter.h" +#include "unicode/uloc.h" +#include "unicode/ustring.h" +#include "unicode/uchriter.h" +#include "unicode/rbbi.h" +#include "rbbirb.h" +#include "uassert.h" +#include "cmemory.h" + +U_NAMESPACE_USE + +//------------------------------------------------------------------------------ +// +// ubrk_open Create a canned type of break iterator based on type (word, line, etc.) +// and locale. +// +//------------------------------------------------------------------------------ +U_CAPI UBreakIterator* U_EXPORT2 +ubrk_open(UBreakIteratorType type, + const char *locale, + const char16_t *text, + int32_t textLength, + UErrorCode *status) +{ + + if(U_FAILURE(*status)) return 0; + + BreakIterator *result = 0; + + switch(type) { + + case UBRK_CHARACTER: + result = BreakIterator::createCharacterInstance(Locale(locale), *status); + break; + + case UBRK_WORD: + result = BreakIterator::createWordInstance(Locale(locale), *status); + break; + + case UBRK_LINE: + result = BreakIterator::createLineInstance(Locale(locale), *status); + break; + + case UBRK_SENTENCE: + result = BreakIterator::createSentenceInstance(Locale(locale), *status); + break; + + case UBRK_TITLE: + result = BreakIterator::createTitleInstance(Locale(locale), *status); + break; + + default: + *status = U_ILLEGAL_ARGUMENT_ERROR; + } + + // check for allocation error + if (U_FAILURE(*status)) { + return 0; + } + if(result == 0) { + *status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + + + UBreakIterator *uBI = (UBreakIterator *)result; + if (text != nullptr) { + ubrk_setText(uBI, text, textLength, status); + } + return uBI; +} + + + +//------------------------------------------------------------------------------ +// +// ubrk_openRules open a break iterator from a set of break rules. +// Invokes the rule builder. +// +//------------------------------------------------------------------------------ +U_CAPI UBreakIterator* U_EXPORT2 +ubrk_openRules( const char16_t *rules, + int32_t rulesLength, + const char16_t *text, + int32_t textLength, + UParseError *parseErr, + UErrorCode *status) { + + if (status == nullptr || U_FAILURE(*status)){ + return 0; + } + + BreakIterator *result = 0; + UnicodeString ruleString(rules, rulesLength); + result = RBBIRuleBuilder::createRuleBasedBreakIterator(ruleString, parseErr, *status); + if(U_FAILURE(*status)) { + return 0; + } + + UBreakIterator *uBI = (UBreakIterator *)result; + if (text != nullptr) { + ubrk_setText(uBI, text, textLength, status); + } + return uBI; +} + + +U_CAPI UBreakIterator* U_EXPORT2 +ubrk_openBinaryRules(const uint8_t *binaryRules, int32_t rulesLength, + const char16_t * text, int32_t textLength, + UErrorCode * status) +{ + if (U_FAILURE(*status)) { + return nullptr; + } + if (rulesLength < 0) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + LocalPointer lpRBBI(new RuleBasedBreakIterator(binaryRules, rulesLength, *status), *status); + if (U_FAILURE(*status)) { + return nullptr; + } + UBreakIterator *uBI = reinterpret_cast(lpRBBI.orphan()); + if (text != nullptr) { + ubrk_setText(uBI, text, textLength, status); + } + return uBI; +} + + +U_CAPI UBreakIterator * U_EXPORT2 +ubrk_safeClone( + const UBreakIterator *bi, + void * /*stackBuffer*/, + int32_t *pBufferSize, + UErrorCode *status) +{ + if (status == nullptr || U_FAILURE(*status)){ + return nullptr; + } + if (bi == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + if (pBufferSize != nullptr) { + int32_t inputSize = *pBufferSize; + *pBufferSize = 1; + if (inputSize == 0) { + return nullptr; // preflighting for deprecated functionality + } + } + BreakIterator *newBI = ((BreakIterator *)bi)->clone(); + if (newBI == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + } else if (pBufferSize != nullptr) { + *status = U_SAFECLONE_ALLOCATED_WARNING; + } + return (UBreakIterator *)newBI; +} + +U_CAPI UBreakIterator * U_EXPORT2 +ubrk_clone(const UBreakIterator *bi, UErrorCode *status) { + return ubrk_safeClone(bi, nullptr, nullptr, status); +} + + +U_CAPI void U_EXPORT2 +ubrk_close(UBreakIterator *bi) +{ + delete (BreakIterator *)bi; +} + +U_CAPI void U_EXPORT2 +ubrk_setText(UBreakIterator* bi, + const char16_t* text, + int32_t textLength, + UErrorCode* status) +{ + UText ut = UTEXT_INITIALIZER; + utext_openUChars(&ut, text, textLength, status); + ((BreakIterator*)bi)->setText(&ut, *status); + // A stack allocated UText wrapping a char16_t * string + // can be dumped without explicitly closing it. +} + + + +U_CAPI void U_EXPORT2 +ubrk_setUText(UBreakIterator *bi, + UText *text, + UErrorCode *status) +{ + ((BreakIterator*)bi)->setText(text, *status); +} + + + + + +U_CAPI int32_t U_EXPORT2 +ubrk_current(const UBreakIterator *bi) +{ + + return ((BreakIterator*)bi)->current(); +} + +U_CAPI int32_t U_EXPORT2 +ubrk_next(UBreakIterator *bi) +{ + + return ((BreakIterator*)bi)->next(); +} + +U_CAPI int32_t U_EXPORT2 +ubrk_previous(UBreakIterator *bi) +{ + + return ((BreakIterator*)bi)->previous(); +} + +U_CAPI int32_t U_EXPORT2 +ubrk_first(UBreakIterator *bi) +{ + + return ((BreakIterator*)bi)->first(); +} + +U_CAPI int32_t U_EXPORT2 +ubrk_last(UBreakIterator *bi) +{ + + return ((BreakIterator*)bi)->last(); +} + +U_CAPI int32_t U_EXPORT2 +ubrk_preceding(UBreakIterator *bi, + int32_t offset) +{ + + return ((BreakIterator*)bi)->preceding(offset); +} + +U_CAPI int32_t U_EXPORT2 +ubrk_following(UBreakIterator *bi, + int32_t offset) +{ + + return ((BreakIterator*)bi)->following(offset); +} + +U_CAPI const char* U_EXPORT2 +ubrk_getAvailable(int32_t index) +{ + + return uloc_getAvailable(index); +} + +U_CAPI int32_t U_EXPORT2 +ubrk_countAvailable() +{ + + return uloc_countAvailable(); +} + + +U_CAPI UBool U_EXPORT2 +ubrk_isBoundary(UBreakIterator *bi, int32_t offset) +{ + return ((BreakIterator*)bi)->isBoundary(offset); +} + + +U_CAPI int32_t U_EXPORT2 +ubrk_getRuleStatus(UBreakIterator *bi) +{ + return ((BreakIterator*)bi)->getRuleStatus(); +} + +U_CAPI int32_t U_EXPORT2 +ubrk_getRuleStatusVec(UBreakIterator *bi, int32_t *fillInVec, int32_t capacity, UErrorCode *status) +{ + return ((BreakIterator*)bi)->getRuleStatusVec(fillInVec, capacity, *status); +} + + +U_CAPI const char* U_EXPORT2 +ubrk_getLocaleByType(const UBreakIterator *bi, + ULocDataLocaleType type, + UErrorCode* status) +{ + if (bi == nullptr) { + if (U_SUCCESS(*status)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + } + return nullptr; + } + return ((BreakIterator*)bi)->getLocaleID(type, *status); +} + + +U_CAPI void U_EXPORT2 +ubrk_refreshUText(UBreakIterator *bi, + UText *text, + UErrorCode *status) +{ + BreakIterator *bii = reinterpret_cast(bi); + bii->refreshInputText(text, *status); +} + +U_CAPI int32_t U_EXPORT2 +ubrk_getBinaryRules(UBreakIterator *bi, + uint8_t * binaryRules, int32_t rulesCapacity, + UErrorCode * status) +{ + if (U_FAILURE(*status)) { + return 0; + } + if ((binaryRules == nullptr && rulesCapacity > 0) || rulesCapacity < 0) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + RuleBasedBreakIterator* rbbi; + if ((rbbi = dynamic_cast(reinterpret_cast(bi))) == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + uint32_t rulesLength; + const uint8_t * returnedRules = rbbi->getBinaryRules(rulesLength); + if (rulesLength > INT32_MAX) { + *status = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + if (binaryRules != nullptr) { // if not preflighting + // Here we know rulesLength <= INT32_MAX and rulesCapacity >= 0, can cast safely + if ((int32_t)rulesLength > rulesCapacity) { + *status = U_BUFFER_OVERFLOW_ERROR; + } else { + uprv_memcpy(binaryRules, returnedRules, rulesLength); + } + } + return (int32_t)rulesLength; +} + + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ diff --git a/deps/icu-small/source/common/ubrkimpl.h b/deps/icu-small/source/common/ubrkimpl.h index 8197f66339e47f..e87f3d70f36502 100644 --- a/deps/icu-small/source/common/ubrkimpl.h +++ b/deps/icu-small/source/common/ubrkimpl.h @@ -1,15 +1,15 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2006, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#ifndef UBRKIMPL_H -#define UBRKIMPL_H - -#define U_ICUDATA_BRKITR U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "brkitr" - -#endif /*UBRKIMPL_H*/ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2006, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#ifndef UBRKIMPL_H +#define UBRKIMPL_H + +#define U_ICUDATA_BRKITR U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "brkitr" + +#endif /*UBRKIMPL_H*/ diff --git a/deps/icu-small/source/common/ucase.cpp b/deps/icu-small/source/common/ucase.cpp index 3d1750265b157e..3a03316dd70770 100644 --- a/deps/icu-small/source/common/ucase.cpp +++ b/deps/icu-small/source/common/ucase.cpp @@ -1,1606 +1,1739 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2004-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: ucase.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004aug30 -* created by: Markus W. Scherer -* -* Low-level Unicode character/string case mapping code. -* Much code moved here (and modified) from uchar.c. -*/ - -#include "unicode/utypes.h" -#include "unicode/unistr.h" -#include "unicode/uset.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "uassert.h" -#include "ucase.h" -#include "umutex.h" -#include "utrie2.h" - -/* ucase_props_data.h is machine-generated by genprops/casepropsbuilder.cpp */ -#define INCLUDED_FROM_UCASE_CPP -#include "ucase_props_data.h" - -/* set of property starts for UnicodeSet ------------------------------------ */ - -static UBool U_CALLCONV -_enumPropertyStartsRange(const void *context, UChar32 start, UChar32 /*end*/, uint32_t /*value*/) { - /* add the start code point to the USet */ - const USetAdder *sa=(const USetAdder *)context; - sa->add(sa->set, start); - return true; -} - -U_CFUNC void U_EXPORT2 -ucase_addPropertyStarts(const USetAdder *sa, UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return; - } - - /* add the start code point of each same-value range of the trie */ - utrie2_enum(&ucase_props_singleton.trie, NULL, _enumPropertyStartsRange, sa); - - /* add code points with hardcoded properties, plus the ones following them */ - - /* (none right now, see comment below) */ - - /* - * Omit code points with hardcoded specialcasing properties - * because we do not build property UnicodeSets for them right now. - */ -} - -/* data access primitives --------------------------------------------------- */ - -U_CAPI const struct UCaseProps * U_EXPORT2 -ucase_getSingleton(int32_t *pExceptionsLength, int32_t *pUnfoldLength) { - *pExceptionsLength = UPRV_LENGTHOF(ucase_props_exceptions); - *pUnfoldLength = UPRV_LENGTHOF(ucase_props_unfold); - return &ucase_props_singleton; -} - -U_CFUNC const UTrie2 * U_EXPORT2 -ucase_getTrie() { - return &ucase_props_singleton.trie; -} - -#define GET_EXCEPTIONS(csp, props) ((csp)->exceptions+((props)>>UCASE_EXC_SHIFT)) - -/* number of bits in an 8-bit integer value */ -static const uint8_t flagsOffset[256]={ - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 -}; - -#define HAS_SLOT(flags, idx) ((flags)&(1<<(idx))) -#define SLOT_OFFSET(flags, idx) flagsOffset[(flags)&((1<<(idx))-1)] - -/* - * Get the value of an optional-value slot where HAS_SLOT(excWord, idx). - * - * @param excWord (in) initial exceptions word - * @param idx (in) desired slot index - * @param pExc16 (in/out) const uint16_t * after excWord=*pExc16++; - * moved to the last uint16_t of the value, use +1 for beginning of next slot - * @param value (out) int32_t or uint32_t output if hasSlot, otherwise not modified - */ -#define GET_SLOT_VALUE(excWord, idx, pExc16, value) UPRV_BLOCK_MACRO_BEGIN { \ - if(((excWord)&UCASE_EXC_DOUBLE_SLOTS)==0) { \ - (pExc16)+=SLOT_OFFSET(excWord, idx); \ - (value)=*pExc16; \ - } else { \ - (pExc16)+=2*SLOT_OFFSET(excWord, idx); \ - (value)=*pExc16++; \ - (value)=((value)<<16)|*pExc16; \ - } \ -} UPRV_BLOCK_MACRO_END - -/* simple case mappings ----------------------------------------------------- */ - -U_CAPI UChar32 U_EXPORT2 -ucase_tolower(UChar32 c) { - uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); - if(!UCASE_HAS_EXCEPTION(props)) { - if(UCASE_IS_UPPER_OR_TITLE(props)) { - c+=UCASE_GET_DELTA(props); - } - } 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); - } - } - return c; -} - -U_CAPI UChar32 U_EXPORT2 -ucase_toupper(UChar32 c) { - uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); - if(!UCASE_HAS_EXCEPTION(props)) { - if(UCASE_GET_TYPE(props)==UCASE_LOWER) { - c+=UCASE_GET_DELTA(props); - } - } 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); - } - } - return c; -} - -U_CAPI UChar32 U_EXPORT2 -ucase_totitle(UChar32 c) { - uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); - if(!UCASE_HAS_EXCEPTION(props)) { - if(UCASE_GET_TYPE(props)==UCASE_LOWER) { - c+=UCASE_GET_DELTA(props); - } - } 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; - } else if(HAS_SLOT(excWord, UCASE_EXC_UPPER)) { - idx=UCASE_EXC_UPPER; - } else { - return c; - } - GET_SLOT_VALUE(excWord, idx, pe, c); - } - return c; -} - -static const UChar iDot[2] = { 0x69, 0x307 }; -static const UChar jDot[2] = { 0x6a, 0x307 }; -static const UChar iOgonekDot[3] = { 0x12f, 0x307 }; -static const UChar iDotGrave[3] = { 0x69, 0x307, 0x300 }; -static const UChar iDotAcute[3] = { 0x69, 0x307, 0x301 }; -static const UChar iDotTilde[3] = { 0x69, 0x307, 0x303 }; - - -U_CFUNC void U_EXPORT2 -ucase_addCaseClosure(UChar32 c, const USetAdder *sa) { - uint16_t props; - - /* - * Hardcode the case closure of i and its relatives and ignore the - * data file data for these characters. - * The Turkic dotless i and dotted I with their case mapping conditions - * and case folding option make the related characters behave specially. - * This code matches their closure behavior to their case folding behavior. - */ - - switch(c) { - case 0x49: - /* regular i and I are in one equivalence class */ - sa->add(sa->set, 0x69); - return; - case 0x69: - sa->add(sa->set, 0x49); - return; - case 0x130: - /* dotted I is in a class with <0069 0307> (for canonical equivalence with <0049 0307>) */ - sa->addString(sa->set, iDot, 2); - return; - case 0x131: - /* dotless i is in a class by itself */ - return; - default: - /* otherwise use the data file data */ - break; - } - - props=UTRIE2_GET16(&ucase_props_singleton.trie, c); - if(!UCASE_HAS_EXCEPTION(props)) { - if(UCASE_GET_TYPE(props)!=UCASE_NONE) { - /* add the one simple case mapping, no matter what type it is */ - int32_t delta=UCASE_GET_DELTA(props); - if(delta!=0) { - sa->add(sa->set, c+delta); - } - } - } else { - /* - * c has exceptions, so there may be multiple simple and/or - * full case mappings. Add them all. - */ - const uint16_t *pe0, *pe=GET_EXCEPTIONS(&ucase_props_singleton, props); - const UChar *closure; - uint16_t excWord=*pe++; - int32_t idx, closureLength, fullLength, length; - - pe0=pe; - - /* add all simple case mappings */ - for(idx=UCASE_EXC_LOWER; idx<=UCASE_EXC_TITLE; ++idx) { - if(HAS_SLOT(excWord, idx)) { - pe=pe0; - GET_SLOT_VALUE(excWord, idx, pe, c); - sa->add(sa->set, c); - } - } - if(HAS_SLOT(excWord, UCASE_EXC_DELTA)) { - pe=pe0; - 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)) { - pe=pe0; - GET_SLOT_VALUE(excWord, UCASE_EXC_CLOSURE, pe, closureLength); - closureLength&=UCASE_CLOSURE_MAX_LENGTH; /* higher bits are reserved */ - closure=(const UChar *)pe+1; /* behind this slot, unless there are full case mappings */ - } else { - closureLength=0; - closure=NULL; - } - - /* add the full case folding */ - if(HAS_SLOT(excWord, UCASE_EXC_FULL_MAPPINGS)) { - pe=pe0; - GET_SLOT_VALUE(excWord, UCASE_EXC_FULL_MAPPINGS, pe, fullLength); - - /* start of full case mapping strings */ - ++pe; - - fullLength&=0xffff; /* bits 16 and higher are reserved */ - - /* skip the lowercase result string */ - pe+=fullLength&UCASE_FULL_LOWER; - fullLength>>=4; - - /* add the full case folding string */ - length=fullLength&0xf; - if(length!=0) { - sa->addString(sa->set, (const UChar *)pe, length); - pe+=length; - } - - /* skip the uppercase and titlecase strings */ - fullLength>>=4; - pe+=fullLength&0xf; - fullLength>>=4; - pe+=fullLength; - - closure=(const UChar *)pe; /* behind full case mappings */ - } - - /* add each code point in the closure string */ - for(idx=0; idxadd(sa->set, c); - } - } -} - -/* - * compare s, which has a length, with t, which has a maximum length or is NUL-terminated - * must be length>0 and max>0 and length<=max - */ -static inline int32_t -strcmpMax(const UChar *s, int32_t length, const UChar *t, int32_t max) { - int32_t c1, c2; - - max-=length; /* we require length<=max, so no need to decrement max in the loop */ - do { - c1=*s++; - c2=*t++; - if(c2==0) { - return 1; /* reached the end of t but not of s */ - } - c1-=c2; - if(c1!=0) { - return c1; /* return difference result */ - } - } while(--length>0); - /* ends with length==0 */ - - if(max==0 || *t==0) { - return 0; /* equal to length of both strings */ - } else { - return -max; /* return length difference */ - } -} - -U_CFUNC UBool U_EXPORT2 -ucase_addStringCaseClosure(const UChar *s, int32_t length, const USetAdder *sa) { - int32_t i, start, limit, result, unfoldRows, unfoldRowWidth, unfoldStringWidth; - - if(ucase_props_singleton.unfold==NULL || s==NULL) { - return false; /* no reverse case folding data, or no string */ - } - if(length<=1) { - /* the string is too short to find any match */ - /* - * more precise would be: - * if(!u_strHasMoreChar32Than(s, length, 1)) - * but this does not make much practical difference because - * a single supplementary code point would just not be found - */ - return false; - } - - const uint16_t *unfold=ucase_props_singleton.unfold; - unfoldRows=unfold[UCASE_UNFOLD_ROWS]; - unfoldRowWidth=unfold[UCASE_UNFOLD_ROW_WIDTH]; - unfoldStringWidth=unfold[UCASE_UNFOLD_STRING_WIDTH]; - unfold+=unfoldRowWidth; - - if(length>unfoldStringWidth) { - /* the string is too long to find any match */ - return false; - } - - /* do a binary search for the string */ - start=0; - limit=unfoldRows; - while(start(unfold+(i*unfoldRowWidth)); - result=strcmpMax(s, length, p, unfoldStringWidth); - - if(result==0) { - /* found the string: add each code point, and its case closure */ - UChar32 c; - - for(i=unfoldStringWidth; iadd(sa->set, c); - ucase_addCaseClosure(c, sa); - } - return true; - } else if(result<0) { - limit=i; - } else /* result>0 */ { - start=i+1; - } - } - - return false; /* string not found */ -} - -U_NAMESPACE_BEGIN - -FullCaseFoldingIterator::FullCaseFoldingIterator() - : unfold(reinterpret_cast(ucase_props_singleton.unfold)), - unfoldRows(unfold[UCASE_UNFOLD_ROWS]), - unfoldRowWidth(unfold[UCASE_UNFOLD_ROW_WIDTH]), - unfoldStringWidth(unfold[UCASE_UNFOLD_STRING_WIDTH]), - currentRow(0), - rowCpIndex(unfoldStringWidth) { - unfold+=unfoldRowWidth; -} - -UChar32 -FullCaseFoldingIterator::next(UnicodeString &full) { - // Advance past the last-delivered code point. - const UChar *p=unfold+(currentRow*unfoldRowWidth); - if(rowCpIndex>=unfoldRowWidth || p[rowCpIndex]==0) { - ++currentRow; - p+=unfoldRowWidth; - rowCpIndex=unfoldStringWidth; - } - if(currentRow>=unfoldRows) { return U_SENTINEL; } - // Set "full" to the NUL-terminated string in the first unfold column. - int32_t length=unfoldStringWidth; - while(length>0 && p[length-1]==0) { --length; } - full.setTo(false, p, length); - // Return the code point. - UChar32 c; - U16_NEXT_UNSAFE(p, rowCpIndex, c); - return c; -} - -namespace LatinCase { - -const int8_t TO_LOWER_NORMAL[LIMIT] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, EXC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, EXC, - 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - EXC, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, - - 0, 1, 0, 1, 0, 1, 0, 1, 0, EXC, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, EXC -}; - -const int8_t TO_LOWER_TR_LT[LIMIT] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 32, 32, 32, 32, 32, 32, 32, 32, EXC, EXC, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, EXC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, EXC, EXC, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, EXC, - 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, EXC, 0, 1, 0, 1, 0, EXC, 0, - EXC, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, - - 0, 1, 0, 1, 0, 1, 0, 1, 0, EXC, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, EXC -}; - -const int8_t TO_UPPER_NORMAL[LIMIT] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, EXC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EXC, - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, -32, -32, -32, -32, -32, 121, - - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, - 0, EXC, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, - - -1, 0, -1, 0, -1, 0, -1, 0, -1, EXC, 0, -1, 0, -1, 0, -1, - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, - 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, EXC -}; - -const int8_t TO_UPPER_TR[LIMIT] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, -32, -32, -32, -32, -32, -32, -32, -32, EXC, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, EXC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EXC, - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, -32, -32, -32, -32, -32, 121, - - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, - 0, EXC, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, - - -1, 0, -1, 0, -1, 0, -1, 0, -1, EXC, 0, -1, 0, -1, 0, -1, - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, - 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, EXC -}; - -} // namespace LatinCase - -U_NAMESPACE_END - -/** @return UCASE_NONE, UCASE_LOWER, UCASE_UPPER, UCASE_TITLE */ -U_CAPI int32_t U_EXPORT2 -ucase_getType(UChar32 c) { - uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); - return UCASE_GET_TYPE(props); -} - -/** @return same as ucase_getType() and set bit 2 if c is case-ignorable */ -U_CAPI int32_t U_EXPORT2 -ucase_getTypeOrIgnorable(UChar32 c) { - uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); - return UCASE_GET_TYPE_AND_IGNORABLE(props); -} - -/** @return UCASE_NO_DOT, UCASE_SOFT_DOTTED, UCASE_ABOVE, UCASE_OTHER_ACCENT */ -static inline int32_t -getDotType(UChar32 c) { - uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); - if(!UCASE_HAS_EXCEPTION(props)) { - return props&UCASE_DOT_MASK; - } else { - const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props); - return (*pe>>UCASE_EXC_DOT_SHIFT)&UCASE_DOT_MASK; - } -} - -U_CAPI UBool U_EXPORT2 -ucase_isSoftDotted(UChar32 c) { - return (UBool)(getDotType(c)==UCASE_SOFT_DOTTED); -} - -U_CAPI UBool U_EXPORT2 -ucase_isCaseSensitive(UChar32 c) { - uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); - 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 ------------------------------------------------------------ */ - -/* - * These internal functions form the core of string case mappings. - * They map single code points to result code points or strings and take - * all necessary conditions (context, locale ID, options) into account. - * - * They do not iterate over the source or write to the destination - * so that the same functions are useful for non-standard string storage, - * such as in a Replaceable (for Transliterator) or UTF-8/32 strings etc. - * For the same reason, the "surrounding text" context is passed in as a - * UCaseContextIterator which does not make any assumptions about - * the underlying storage. - * - * This section contains helper functions that check for conditions - * in the input text surrounding the current code point - * according to SpecialCasing.txt. - * - * Each helper function gets the index - * - after the current code point if it looks at following text - * - before the current code point if it looks at preceding text - * - * Unicode 3.2 UAX 21 "Case Mappings" defines the conditions as follows: - * - * Final_Sigma - * C is preceded by a sequence consisting of - * a cased letter and a case-ignorable sequence, - * and C is not followed by a sequence consisting of - * an ignorable sequence and then a cased letter. - * - * More_Above - * C is followed by one or more characters of combining class 230 (ABOVE) - * in the combining character sequence. - * - * After_Soft_Dotted - * The last preceding character with combining class of zero before C - * was Soft_Dotted, - * and there is no intervening combining character class 230 (ABOVE). - * - * Before_Dot - * C is followed by combining dot above (U+0307). - * Any sequence of characters with a combining class that is neither 0 nor 230 - * may intervene between the current character and the combining dot above. - * - * The erratum from 2002-10-31 adds the condition - * - * After_I - * The last preceding base character was an uppercase I, and there is no - * intervening combining character class 230 (ABOVE). - * - * (See Jitterbug 2344 and the comments on After_I below.) - * - * Helper definitions in Unicode 3.2 UAX 21: - * - * D1. A character C is defined to be cased - * if it meets any of the following criteria: - * - * - The general category of C is Titlecase Letter (Lt) - * - In [CoreProps], C has one of the properties Uppercase, or Lowercase - * - Given D = NFD(C), then it is not the case that: - * D = UCD_lower(D) = UCD_upper(D) = UCD_title(D) - * (This third criterion does not add any characters to the list - * for Unicode 3.2. Ignored.) - * - * D2. A character C is defined to be case-ignorable - * if it meets either of the following criteria: - * - * - The general category of C is - * Nonspacing Mark (Mn), or Enclosing Mark (Me), or Format Control (Cf), or - * Letter Modifier (Lm), or Symbol Modifier (Sk) - * - C is one of the following characters - * U+0027 APOSTROPHE - * U+00AD SOFT HYPHEN (SHY) - * U+2019 RIGHT SINGLE QUOTATION MARK - * (the preferred character for apostrophe) - * - * D3. A case-ignorable sequence is a sequence of - * zero or more case-ignorable characters. - */ - -#define is_d(c) ((c)=='d' || (c)=='D') -#define is_e(c) ((c)=='e' || (c)=='E') -#define is_i(c) ((c)=='i' || (c)=='I') -#define is_l(c) ((c)=='l' || (c)=='L') -#define is_r(c) ((c)=='r' || (c)=='R') -#define is_t(c) ((c)=='t' || (c)=='T') -#define is_u(c) ((c)=='u' || (c)=='U') -#define is_y(c) ((c)=='y' || (c)=='Y') -#define is_z(c) ((c)=='z' || (c)=='Z') - -/* separator? */ -#define is_sep(c) ((c)=='_' || (c)=='-' || (c)==0) - -/** - * Requires non-NULL locale ID but otherwise does the equivalent of - * checking for language codes as if uloc_getLanguage() were called: - * Accepts both 2- and 3-letter codes and accepts case variants. - */ -U_CFUNC int32_t -ucase_getCaseLocale(const char *locale) { - /* - * This function used to use uloc_getLanguage(), but the current code - * removes the dependency of this low-level code on uloc implementation code - * and is faster because not the whole locale ID has to be - * examined and copied/transformed. - * - * Because this code does not want to depend on uloc, the caller must - * pass in a non-NULL locale, i.e., may need to call uloc_getDefault(). - */ - char c=*locale++; - // Fastpath for English "en" which is often used for default (=root locale) case mappings, - // and for Chinese "zh": Very common but no special case mapping behavior. - // Then check lowercase vs. uppercase to reduce the number of comparisons - // for other locales without special behavior. - if(c=='e') { - /* el or ell? */ - c=*locale++; - if(is_l(c)) { - c=*locale++; - if(is_l(c)) { - c=*locale; - } - if(is_sep(c)) { - return UCASE_LOC_GREEK; - } - } - // en, es, ... -> root - } else if(c=='z') { - return UCASE_LOC_ROOT; -#if U_CHARSET_FAMILY==U_ASCII_FAMILY - } else if(c>='a') { // ASCII a-z = 0x61..0x7a, after A-Z -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY - } else if(c<='z') { // EBCDIC a-z = 0x81..0xa9 with two gaps, before A-Z -#else -# error Unknown charset family! -#endif - // lowercase c - if(c=='t') { - /* tr or tur? */ - c=*locale++; - if(is_u(c)) { - c=*locale++; - } - if(is_r(c)) { - c=*locale; - if(is_sep(c)) { - return UCASE_LOC_TURKISH; - } - } - } else if(c=='a') { - /* az or aze? */ - c=*locale++; - if(is_z(c)) { - c=*locale++; - if(is_e(c)) { - c=*locale; - } - if(is_sep(c)) { - return UCASE_LOC_TURKISH; - } - } - } else if(c=='l') { - /* lt or lit? */ - c=*locale++; - if(is_i(c)) { - c=*locale++; - } - if(is_t(c)) { - c=*locale; - if(is_sep(c)) { - return UCASE_LOC_LITHUANIAN; - } - } - } else if(c=='n') { - /* nl or nld? */ - c=*locale++; - if(is_l(c)) { - c=*locale++; - if(is_d(c)) { - c=*locale; - } - if(is_sep(c)) { - return UCASE_LOC_DUTCH; - } - } - } else if(c=='h') { - /* hy or hye? *not* hyw */ - c=*locale++; - if(is_y(c)) { - c=*locale++; - if(is_e(c)) { - c=*locale; - } - if(is_sep(c)) { - return UCASE_LOC_ARMENIAN; - } - } - } - } else { - // uppercase c - // Same code as for lowercase c but also check for 'E'. - if(c=='T') { - /* tr or tur? */ - c=*locale++; - if(is_u(c)) { - c=*locale++; - } - if(is_r(c)) { - c=*locale; - if(is_sep(c)) { - return UCASE_LOC_TURKISH; - } - } - } else if(c=='A') { - /* az or aze? */ - c=*locale++; - if(is_z(c)) { - c=*locale++; - if(is_e(c)) { - c=*locale; - } - if(is_sep(c)) { - return UCASE_LOC_TURKISH; - } - } - } else if(c=='L') { - /* lt or lit? */ - c=*locale++; - if(is_i(c)) { - c=*locale++; - } - if(is_t(c)) { - c=*locale; - if(is_sep(c)) { - return UCASE_LOC_LITHUANIAN; - } - } - } else if(c=='E') { - /* el or ell? */ - c=*locale++; - if(is_l(c)) { - c=*locale++; - if(is_l(c)) { - c=*locale; - } - if(is_sep(c)) { - return UCASE_LOC_GREEK; - } - } - } else if(c=='N') { - /* nl or nld? */ - c=*locale++; - if(is_l(c)) { - c=*locale++; - if(is_d(c)) { - c=*locale; - } - if(is_sep(c)) { - return UCASE_LOC_DUTCH; - } - } - } else if(c=='H') { - /* hy or hye? *not* hyw */ - c=*locale++; - if(is_y(c)) { - c=*locale++; - if(is_e(c)) { - c=*locale; - } - if(is_sep(c)) { - return UCASE_LOC_ARMENIAN; - } - } - } - } - return UCASE_LOC_ROOT; -} - -/* - * Is followed by - * {case-ignorable}* cased - * ? - * (dir determines looking forward/backward) - * If a character is case-ignorable, it is skipped regardless of whether - * it is also cased or not. - */ -static UBool -isFollowedByCasedLetter(UCaseContextIterator *iter, void *context, int8_t dir) { - UChar32 c; - - if(iter==NULL) { - return false; - } - - for(/* dir!=0 sets direction */; (c=iter(context, dir))>=0; dir=0) { - int32_t type=ucase_getTypeOrIgnorable(c); - if(type&4) { - /* case-ignorable, continue with the loop */ - } else if(type!=UCASE_NONE) { - return true; /* followed by cased letter */ - } else { - return false; /* uncased and not case-ignorable */ - } - } - - return false; /* not followed by cased letter */ -} - -/* Is preceded by Soft_Dotted character with no intervening cc=230 ? */ -static UBool -isPrecededBySoftDotted(UCaseContextIterator *iter, void *context) { - UChar32 c; - int32_t dotType; - int8_t dir; - - if(iter==NULL) { - return false; - } - - for(dir=-1; (c=iter(context, dir))>=0; dir=0) { - dotType=getDotType(c); - if(dotType==UCASE_SOFT_DOTTED) { - return true; /* preceded by TYPE_i */ - } else if(dotType!=UCASE_OTHER_ACCENT) { - return false; /* preceded by different base character (not TYPE_i), or intervening cc==230 */ - } - } - - return false; /* not preceded by TYPE_i */ -} - -/* - * See Jitterbug 2344: - * The condition After_I for Turkic-lowercasing of U+0307 combining dot above - * is checked in ICU 2.0, 2.1, 2.6 but was not in 2.2 & 2.4 because - * we made those releases compatible with Unicode 3.2 which had not fixed - * a related bug in SpecialCasing.txt. - * - * From the Jitterbug 2344 text: - * ... this bug is listed as a Unicode erratum - * from 2002-10-31 at http://www.unicode.org/uni2errata/UnicodeErrata.html - * - * There are two errors in SpecialCasing.txt. - * 1. Missing semicolons on two lines. ... [irrelevant for ICU] - * 2. An incorrect context definition. Correct as follows: - * < 0307; ; 0307; 0307; tr After_Soft_Dotted; # COMBINING DOT ABOVE - * < 0307; ; 0307; 0307; az After_Soft_Dotted; # COMBINING DOT ABOVE - * --- - * > 0307; ; 0307; 0307; tr After_I; # COMBINING DOT ABOVE - * > 0307; ; 0307; 0307; az After_I; # COMBINING DOT ABOVE - * where the context After_I is defined as: - * The last preceding base character was an uppercase I, and there is no - * intervening combining character class 230 (ABOVE). - * - * - * Note that SpecialCasing.txt even in Unicode 3.2 described the condition as: - * - * # When lowercasing, remove dot_above in the sequence I + dot_above, which will turn into i. - * # This matches the behavior of the canonically equivalent I-dot_above - * - * See also the description in this place in older versions of uchar.c (revision 1.100). - * - * Markus W. Scherer 2003-feb-15 - */ - -/* Is preceded by base character 'I' with no intervening cc=230 ? */ -static UBool -isPrecededBy_I(UCaseContextIterator *iter, void *context) { - UChar32 c; - int32_t dotType; - int8_t dir; - - if(iter==NULL) { - return false; - } - - for(dir=-1; (c=iter(context, dir))>=0; dir=0) { - if(c==0x49) { - return true; /* preceded by I */ - } - dotType=getDotType(c); - if(dotType!=UCASE_OTHER_ACCENT) { - return false; /* preceded by different base character (not I), or intervening cc==230 */ - } - } - - return false; /* not preceded by I */ -} - -/* Is followed by one or more cc==230 ? */ -static UBool -isFollowedByMoreAbove(UCaseContextIterator *iter, void *context) { - UChar32 c; - int32_t dotType; - int8_t dir; - - if(iter==NULL) { - return false; - } - - for(dir=1; (c=iter(context, dir))>=0; dir=0) { - dotType=getDotType(c); - if(dotType==UCASE_ABOVE) { - return true; /* at least one cc==230 following */ - } else if(dotType!=UCASE_OTHER_ACCENT) { - return false; /* next base character, no more cc==230 following */ - } - } - - return false; /* no more cc==230 following */ -} - -/* Is followed by a dot above (without cc==230 in between) ? */ -static UBool -isFollowedByDotAbove(UCaseContextIterator *iter, void *context) { - UChar32 c; - int32_t dotType; - int8_t dir; - - if(iter==NULL) { - return false; - } - - for(dir=1; (c=iter(context, dir))>=0; dir=0) { - if(c==0x307) { - return true; - } - dotType=getDotType(c); - if(dotType!=UCASE_OTHER_ACCENT) { - return false; /* next base character or cc==230 in between */ - } - } - - return false; /* no dot above following */ -} - -U_CAPI int32_t U_EXPORT2 -ucase_toFullLower(UChar32 c, - UCaseContextIterator *iter, void *context, - const UChar **pString, - int32_t loc) { - // The sign of the result has meaning, input must be non-negative so that it can be returned as is. - U_ASSERT(c >= 0); - UChar32 result=c; - // Reset the output pointer in case it was uninitialized. - *pString=nullptr; - uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); - if(!UCASE_HAS_EXCEPTION(props)) { - if(UCASE_IS_UPPER_OR_TITLE(props)) { - result=c+UCASE_GET_DELTA(props); - } - } else { - const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props), *pe2; - uint16_t excWord=*pe++; - int32_t full; - - pe2=pe; - - if(excWord&UCASE_EXC_CONDITIONAL_SPECIAL) { - /* use hardcoded conditions and mappings */ - - /* - * Test for conditional mappings first - * (otherwise the unconditional default mappings are always taken), - * then test for characters that have unconditional mappings in SpecialCasing.txt, - * then get the UnicodeData.txt mappings. - */ - if( loc==UCASE_LOC_LITHUANIAN && - /* base characters, find accents above */ - (((c==0x49 || c==0x4a || c==0x12e) && - isFollowedByMoreAbove(iter, context)) || - /* precomposed with accent above, no need to find one */ - (c==0xcc || c==0xcd || c==0x128)) - ) { - /* - # Lithuanian - - # Lithuanian retains the dot in a lowercase i when followed by accents. - - # Introduce an explicit dot above when lowercasing capital I's and J's - # whenever there are more accents above. - # (of the accents used in Lithuanian: grave, acute, tilde above, and ogonek) - - 0049; 0069 0307; 0049; 0049; lt More_Above; # LATIN CAPITAL LETTER I - 004A; 006A 0307; 004A; 004A; lt More_Above; # LATIN CAPITAL LETTER J - 012E; 012F 0307; 012E; 012E; lt More_Above; # LATIN CAPITAL LETTER I WITH OGONEK - 00CC; 0069 0307 0300; 00CC; 00CC; lt; # LATIN CAPITAL LETTER I WITH GRAVE - 00CD; 0069 0307 0301; 00CD; 00CD; lt; # LATIN CAPITAL LETTER I WITH ACUTE - 0128; 0069 0307 0303; 0128; 0128; lt; # LATIN CAPITAL LETTER I WITH TILDE - */ - switch(c) { - case 0x49: /* LATIN CAPITAL LETTER I */ - *pString=iDot; - return 2; - case 0x4a: /* LATIN CAPITAL LETTER J */ - *pString=jDot; - return 2; - case 0x12e: /* LATIN CAPITAL LETTER I WITH OGONEK */ - *pString=iOgonekDot; - return 2; - case 0xcc: /* LATIN CAPITAL LETTER I WITH GRAVE */ - *pString=iDotGrave; - return 3; - case 0xcd: /* LATIN CAPITAL LETTER I WITH ACUTE */ - *pString=iDotAcute; - return 3; - case 0x128: /* LATIN CAPITAL LETTER I WITH TILDE */ - *pString=iDotTilde; - return 3; - default: - return 0; /* will not occur */ - } - /* # Turkish and Azeri */ - } else if(loc==UCASE_LOC_TURKISH && c==0x130) { - /* - # I and i-dotless; I-dot and i are case pairs in Turkish and Azeri - # The following rules handle those cases. - - 0130; 0069; 0130; 0130; tr # LATIN CAPITAL LETTER I WITH DOT ABOVE - 0130; 0069; 0130; 0130; az # LATIN CAPITAL LETTER I WITH DOT ABOVE - */ - return 0x69; - } else if(loc==UCASE_LOC_TURKISH && c==0x307 && isPrecededBy_I(iter, context)) { - /* - # When lowercasing, remove dot_above in the sequence I + dot_above, which will turn into i. - # This matches the behavior of the canonically equivalent I-dot_above - - 0307; ; 0307; 0307; tr After_I; # COMBINING DOT ABOVE - 0307; ; 0307; 0307; az After_I; # COMBINING DOT ABOVE - */ - return 0; /* remove the dot (continue without output) */ - } else if(loc==UCASE_LOC_TURKISH && c==0x49 && !isFollowedByDotAbove(iter, context)) { - /* - # When lowercasing, unless an I is before a dot_above, it turns into a dotless i. - - 0049; 0131; 0049; 0049; tr Not_Before_Dot; # LATIN CAPITAL LETTER I - 0049; 0131; 0049; 0049; az Not_Before_Dot; # LATIN CAPITAL LETTER I - */ - return 0x131; - } else if(c==0x130) { - /* - # Preserve canonical equivalence for I with dot. Turkic is handled below. - - 0130; 0069 0307; 0130; 0130; # LATIN CAPITAL LETTER I WITH DOT ABOVE - */ - *pString=iDot; - return 2; - } else if( c==0x3a3 && - !isFollowedByCasedLetter(iter, context, 1) && - isFollowedByCasedLetter(iter, context, -1) /* -1=preceded */ - ) { - /* greek capital sigma maps depending on surrounding cased letters (see SpecialCasing.txt) */ - /* - # Special case for final form of sigma - - 03A3; 03C2; 03A3; 03A3; Final_Sigma; # GREEK CAPITAL LETTER SIGMA - */ - return 0x3c2; /* greek small final sigma */ - } else { - /* no known conditional special case mapping, use a normal mapping */ - } - } else if(HAS_SLOT(excWord, UCASE_EXC_FULL_MAPPINGS)) { - GET_SLOT_VALUE(excWord, UCASE_EXC_FULL_MAPPINGS, pe, full); - full&=UCASE_FULL_LOWER; - if(full!=0) { - /* set the output pointer to the lowercase mapping */ - *pString=reinterpret_cast(pe+1); - - /* return the string length */ - return full; - } - } - - if(HAS_SLOT(excWord, UCASE_EXC_DELTA) && UCASE_IS_UPPER_OR_TITLE(props)) { - int32_t delta; - GET_SLOT_VALUE(excWord, UCASE_EXC_DELTA, pe2, 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); - } - } - - return (result==c) ? ~result : result; -} - -/* internal */ -static int32_t -toUpperOrTitle(UChar32 c, - UCaseContextIterator *iter, void *context, - const UChar **pString, - int32_t loc, - UBool upperNotTitle) { - // The sign of the result has meaning, input must be non-negative so that it can be returned as is. - U_ASSERT(c >= 0); - UChar32 result=c; - // Reset the output pointer in case it was uninitialized. - *pString=nullptr; - uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); - if(!UCASE_HAS_EXCEPTION(props)) { - if(UCASE_GET_TYPE(props)==UCASE_LOWER) { - result=c+UCASE_GET_DELTA(props); - } - } else { - const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props), *pe2; - uint16_t excWord=*pe++; - int32_t full, idx; - - pe2=pe; - - if(excWord&UCASE_EXC_CONDITIONAL_SPECIAL) { - /* use hardcoded conditions and mappings */ - if(loc==UCASE_LOC_TURKISH && c==0x69) { - /* - # Turkish and Azeri - - # I and i-dotless; I-dot and i are case pairs in Turkish and Azeri - # The following rules handle those cases. - - # When uppercasing, i turns into a dotted capital I - - 0069; 0069; 0130; 0130; tr; # LATIN SMALL LETTER I - 0069; 0069; 0130; 0130; az; # LATIN SMALL LETTER I - */ - return 0x130; - } else if(loc==UCASE_LOC_LITHUANIAN && c==0x307 && isPrecededBySoftDotted(iter, context)) { - /* - # Lithuanian - - # Lithuanian retains the dot in a lowercase i when followed by accents. - - # Remove DOT ABOVE after "i" with upper or titlecase - - 0307; 0307; ; ; lt After_Soft_Dotted; # COMBINING DOT ABOVE - */ - return 0; /* remove the dot (continue without output) */ - } else if(c==0x0587) { - // See ICU-13416: - // և ligature ech-yiwn - // uppercases to ԵՒ=ech+yiwn by default and in Western Armenian, - // but to ԵՎ=ech+vew in Eastern Armenian. - if(loc==UCASE_LOC_ARMENIAN) { - *pString=upperNotTitle ? u"ԵՎ" : u"Եվ"; - } else { - *pString=upperNotTitle ? u"ԵՒ" : u"Եւ"; - } - return 2; - } else { - /* no known conditional special case mapping, use a normal mapping */ - } - } else if(HAS_SLOT(excWord, UCASE_EXC_FULL_MAPPINGS)) { - GET_SLOT_VALUE(excWord, UCASE_EXC_FULL_MAPPINGS, pe, full); - - /* start of full case mapping strings */ - ++pe; - - /* skip the lowercase and case-folding result strings */ - pe+=full&UCASE_FULL_LOWER; - full>>=4; - pe+=full&0xf; - full>>=4; - - if(upperNotTitle) { - full&=0xf; - } else { - /* skip the uppercase result string */ - pe+=full&0xf; - full=(full>>4)&0xf; - } - - if(full!=0) { - /* set the output pointer to the result string */ - *pString=reinterpret_cast(pe); - - /* return the string length */ - return full; - } - } - - if(HAS_SLOT(excWord, UCASE_EXC_DELTA) && UCASE_GET_TYPE(props)==UCASE_LOWER) { - int32_t delta; - GET_SLOT_VALUE(excWord, UCASE_EXC_DELTA, pe2, 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)) { - /* here, titlecase is same as uppercase */ - idx=UCASE_EXC_UPPER; - } else { - return ~c; - } - GET_SLOT_VALUE(excWord, idx, pe2, result); - } - - return (result==c) ? ~result : result; -} - -U_CAPI int32_t U_EXPORT2 -ucase_toFullUpper(UChar32 c, - UCaseContextIterator *iter, void *context, - const UChar **pString, - int32_t caseLocale) { - return toUpperOrTitle(c, iter, context, pString, caseLocale, true); -} - -U_CAPI int32_t U_EXPORT2 -ucase_toFullTitle(UChar32 c, - UCaseContextIterator *iter, void *context, - const UChar **pString, - int32_t caseLocale) { - return toUpperOrTitle(c, iter, context, pString, caseLocale, false); -} - -/* case folding ------------------------------------------------------------- */ - -/* - * Case folding is similar to lowercasing. - * The result may be a simple mapping, i.e., a single code point, or - * a full mapping, i.e., a string. - * If the case folding for a code point is the same as its simple (1:1) lowercase mapping, - * then only the lowercase mapping is stored. - * - * Some special cases are hardcoded because their conditions cannot be - * parsed and processed from CaseFolding.txt. - * - * Unicode 3.2 CaseFolding.txt specifies for its status field: - -# C: common case folding, common mappings shared by both simple and full mappings. -# F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. -# S: simple case folding, mappings to single characters where different from F. -# T: special case for uppercase I and dotted uppercase I -# - For non-Turkic languages, this mapping is normally not used. -# - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. -# -# Usage: -# A. To do a simple case folding, use the mappings with status C + S. -# B. To do a full case folding, use the mappings with status C + F. -# -# The mappings with status T can be used or omitted depending on the desired case-folding -# behavior. (The default option is to exclude them.) - - * Unicode 3.2 has 'T' mappings as follows: - -0049; T; 0131; # LATIN CAPITAL LETTER I -0130; T; 0069; # LATIN CAPITAL LETTER I WITH DOT ABOVE - - * while the default mappings for these code points are: - -0049; C; 0069; # LATIN CAPITAL LETTER I -0130; F; 0069 0307; # LATIN CAPITAL LETTER I WITH DOT ABOVE - - * U+0130 has no simple case folding (simple-case-folds to itself). - */ - -/* return the simple case folding mapping for c */ -U_CAPI UChar32 U_EXPORT2 -ucase_fold(UChar32 c, uint32_t options) { - uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); - if(!UCASE_HAS_EXCEPTION(props)) { - if(UCASE_IS_UPPER_OR_TITLE(props)) { - c+=UCASE_GET_DELTA(props); - } - } else { - const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props); - uint16_t excWord=*pe++; - int32_t idx; - if(excWord&UCASE_EXC_CONDITIONAL_FOLD) { - /* special case folding mappings, hardcoded */ - if((options&_FOLD_CASE_OPTIONS_MASK)==U_FOLD_CASE_DEFAULT) { - /* default mappings */ - if(c==0x49) { - /* 0049; C; 0069; # LATIN CAPITAL LETTER I */ - return 0x69; - } else if(c==0x130) { - /* no simple case folding for U+0130 */ - return c; - } - } else { - /* Turkic mappings */ - if(c==0x49) { - /* 0049; T; 0131; # LATIN CAPITAL LETTER I */ - return 0x131; - } else if(c==0x130) { - /* 0130; T; 0069; # LATIN CAPITAL LETTER I WITH DOT ABOVE */ - return 0x69; - } - } - } - 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)) { - idx=UCASE_EXC_LOWER; - } else { - return c; - } - GET_SLOT_VALUE(excWord, idx, pe, c); - } - return c; -} - -/* - * Issue for canonical caseless match (UAX #21): - * Turkic casefolding (using "T" mappings in CaseFolding.txt) does not preserve - * canonical equivalence, unlike default-option casefolding. - * For example, I-grave and I + grave fold to strings that are not canonically - * equivalent. - * For more details, see the comment in unorm_compare() in unorm.cpp - * and the intermediate prototype changes for Jitterbug 2021. - * (For example, revision 1.104 of uchar.c and 1.4 of CaseFolding.txt.) - * - * This did not get fixed because it appears that it is not possible to fix - * it for uppercase and lowercase characters (I-grave vs. i-grave) - * together in a way that they still fold to common result strings. - */ - -U_CAPI int32_t U_EXPORT2 -ucase_toFullFolding(UChar32 c, - const UChar **pString, - uint32_t options) { - // The sign of the result has meaning, input must be non-negative so that it can be returned as is. - U_ASSERT(c >= 0); - UChar32 result=c; - // Reset the output pointer in case it was uninitialized. - *pString=nullptr; - uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); - if(!UCASE_HAS_EXCEPTION(props)) { - if(UCASE_IS_UPPER_OR_TITLE(props)) { - result=c+UCASE_GET_DELTA(props); - } - } else { - const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props), *pe2; - uint16_t excWord=*pe++; - int32_t full, idx; - - pe2=pe; - - if(excWord&UCASE_EXC_CONDITIONAL_FOLD) { - /* use hardcoded conditions and mappings */ - if((options&_FOLD_CASE_OPTIONS_MASK)==U_FOLD_CASE_DEFAULT) { - /* default mappings */ - if(c==0x49) { - /* 0049; C; 0069; # LATIN CAPITAL LETTER I */ - return 0x69; - } else if(c==0x130) { - /* 0130; F; 0069 0307; # LATIN CAPITAL LETTER I WITH DOT ABOVE */ - *pString=iDot; - return 2; - } - } else { - /* Turkic mappings */ - if(c==0x49) { - /* 0049; T; 0131; # LATIN CAPITAL LETTER I */ - return 0x131; - } else if(c==0x130) { - /* 0130; T; 0069; # LATIN CAPITAL LETTER I WITH DOT ABOVE */ - return 0x69; - } - } - } else if(HAS_SLOT(excWord, UCASE_EXC_FULL_MAPPINGS)) { - GET_SLOT_VALUE(excWord, UCASE_EXC_FULL_MAPPINGS, pe, full); - - /* start of full case mapping strings */ - ++pe; - - /* skip the lowercase result string */ - pe+=full&UCASE_FULL_LOWER; - full=(full>>4)&0xf; - - if(full!=0) { - /* set the output pointer to the result string */ - *pString=reinterpret_cast(pe); - - /* return the string length */ - return full; - } - } - - 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, pe2, 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)) { - idx=UCASE_EXC_LOWER; - } else { - return ~c; - } - GET_SLOT_VALUE(excWord, idx, pe2, result); - } - - return (result==c) ? ~result : result; -} - -/* case mapping properties API ---------------------------------------------- */ - -/* public API (see uchar.h) */ - -U_CAPI UBool U_EXPORT2 -u_isULowercase(UChar32 c) { - return (UBool)(UCASE_LOWER==ucase_getType(c)); -} - -U_CAPI UBool U_EXPORT2 -u_isUUppercase(UChar32 c) { - return (UBool)(UCASE_UPPER==ucase_getType(c)); -} - -/* Transforms the Unicode character to its lower case equivalent.*/ -U_CAPI UChar32 U_EXPORT2 -u_tolower(UChar32 c) { - return ucase_tolower(c); -} - -/* Transforms the Unicode character to its upper case equivalent.*/ -U_CAPI UChar32 U_EXPORT2 -u_toupper(UChar32 c) { - return ucase_toupper(c); -} - -/* Transforms the Unicode character to its title case equivalent.*/ -U_CAPI UChar32 U_EXPORT2 -u_totitle(UChar32 c) { - return ucase_totitle(c); -} - -/* return the simple case folding mapping for c */ -U_CAPI UChar32 U_EXPORT2 -u_foldCase(UChar32 c, uint32_t options) { - return ucase_fold(c, options); -} - -U_CFUNC int32_t U_EXPORT2 -ucase_hasBinaryProperty(UChar32 c, UProperty which) { - /* case mapping properties */ - const UChar *resultString; - switch(which) { - case UCHAR_LOWERCASE: - return (UBool)(UCASE_LOWER==ucase_getType(c)); - case UCHAR_UPPERCASE: - return (UBool)(UCASE_UPPER==ucase_getType(c)); - case UCHAR_SOFT_DOTTED: - return ucase_isSoftDotted(c); - case UCHAR_CASE_SENSITIVE: - return ucase_isCaseSensitive(c); - case UCHAR_CASED: - return (UBool)(UCASE_NONE!=ucase_getType(c)); - case UCHAR_CASE_IGNORABLE: - return (UBool)(ucase_getTypeOrIgnorable(c)>>2); - /* - * Note: The following Changes_When_Xyz are defined as testing whether - * the NFD form of the input changes when Xyz-case-mapped. - * However, this simpler implementation of these properties, - * ignoring NFD, passes the tests. - * The implementation needs to be changed if the tests start failing. - * When that happens, optimizations should be used to work with the - * per-single-code point ucase_toFullXyz() functions unless - * the NFD form has more than one code point, - * and the property starts set needs to be the union of the - * start sets for normalization and case mappings. - */ - case UCHAR_CHANGES_WHEN_LOWERCASED: - return (UBool)(ucase_toFullLower(c, NULL, NULL, &resultString, UCASE_LOC_ROOT)>=0); - case UCHAR_CHANGES_WHEN_UPPERCASED: - return (UBool)(ucase_toFullUpper(c, NULL, NULL, &resultString, UCASE_LOC_ROOT)>=0); - case UCHAR_CHANGES_WHEN_TITLECASED: - return (UBool)(ucase_toFullTitle(c, NULL, NULL, &resultString, UCASE_LOC_ROOT)>=0); - /* case UCHAR_CHANGES_WHEN_CASEFOLDED: -- in uprops.c */ - case UCHAR_CHANGES_WHEN_CASEMAPPED: - return (UBool)( - ucase_toFullLower(c, NULL, NULL, &resultString, UCASE_LOC_ROOT)>=0 || - ucase_toFullUpper(c, NULL, NULL, &resultString, UCASE_LOC_ROOT)>=0 || - ucase_toFullTitle(c, NULL, NULL, &resultString, UCASE_LOC_ROOT)>=0); - default: - return false; - } -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2004-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: ucase.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004aug30 +* created by: Markus W. Scherer +* +* Low-level Unicode character/string case mapping code. +* Much code moved here (and modified) from uchar.c. +*/ + +#include "unicode/utypes.h" +#include "unicode/unistr.h" +#include "unicode/uset.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "uassert.h" +#include "ucase.h" +#include "umutex.h" +#include "utrie2.h" + +/* ucase_props_data.h is machine-generated by genprops/casepropsbuilder.cpp */ +#define INCLUDED_FROM_UCASE_CPP +#include "ucase_props_data.h" + +/* set of property starts for UnicodeSet ------------------------------------ */ + +static UBool U_CALLCONV +_enumPropertyStartsRange(const void *context, UChar32 start, UChar32 /*end*/, uint32_t /*value*/) { + /* add the start code point to the USet */ + const USetAdder *sa=(const USetAdder *)context; + sa->add(sa->set, start); + return true; +} + +U_CFUNC void U_EXPORT2 +ucase_addPropertyStarts(const USetAdder *sa, UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return; + } + + /* add the start code point of each same-value range of the trie */ + utrie2_enum(&ucase_props_singleton.trie, nullptr, _enumPropertyStartsRange, sa); + + /* add code points with hardcoded properties, plus the ones following them */ + + /* (none right now, see comment below) */ + + /* + * Omit code points with hardcoded specialcasing properties + * because we do not build property UnicodeSets for them right now. + */ +} + +/* data access primitives --------------------------------------------------- */ + +U_CAPI const struct UCaseProps * U_EXPORT2 +ucase_getSingleton(int32_t *pExceptionsLength, int32_t *pUnfoldLength) { + *pExceptionsLength = UPRV_LENGTHOF(ucase_props_exceptions); + *pUnfoldLength = UPRV_LENGTHOF(ucase_props_unfold); + return &ucase_props_singleton; +} + +U_CFUNC const UTrie2 * U_EXPORT2 +ucase_getTrie() { + return &ucase_props_singleton.trie; +} + +#define GET_EXCEPTIONS(csp, props) ((csp)->exceptions+((props)>>UCASE_EXC_SHIFT)) + +/* number of bits in an 8-bit integer value */ +static const uint8_t flagsOffset[256]={ + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 +}; + +#define HAS_SLOT(flags, idx) ((flags)&(1<<(idx))) +#define SLOT_OFFSET(flags, idx) flagsOffset[(flags)&((1<<(idx))-1)] + +/* + * Get the value of an optional-value slot where HAS_SLOT(excWord, idx). + * + * @param excWord (in) initial exceptions word + * @param idx (in) desired slot index + * @param pExc16 (in/out) const uint16_t * after excWord=*pExc16++; + * moved to the last uint16_t of the value, use +1 for beginning of next slot + * @param value (out) int32_t or uint32_t output if hasSlot, otherwise not modified + */ +#define GET_SLOT_VALUE(excWord, idx, pExc16, value) UPRV_BLOCK_MACRO_BEGIN { \ + if(((excWord)&UCASE_EXC_DOUBLE_SLOTS)==0) { \ + (pExc16)+=SLOT_OFFSET(excWord, idx); \ + (value)=*pExc16; \ + } else { \ + (pExc16)+=2*SLOT_OFFSET(excWord, idx); \ + (value)=*pExc16++; \ + (value)=((value)<<16)|*pExc16; \ + } \ +} UPRV_BLOCK_MACRO_END + +/* simple case mappings ----------------------------------------------------- */ + +U_CAPI UChar32 U_EXPORT2 +ucase_tolower(UChar32 c) { + uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); + if(!UCASE_HAS_EXCEPTION(props)) { + if(UCASE_IS_UPPER_OR_TITLE(props)) { + c+=UCASE_GET_DELTA(props); + } + } 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); + } + } + return c; +} + +U_CAPI UChar32 U_EXPORT2 +ucase_toupper(UChar32 c) { + uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); + if(!UCASE_HAS_EXCEPTION(props)) { + if(UCASE_GET_TYPE(props)==UCASE_LOWER) { + c+=UCASE_GET_DELTA(props); + } + } 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); + } + } + return c; +} + +U_CAPI UChar32 U_EXPORT2 +ucase_totitle(UChar32 c) { + uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); + if(!UCASE_HAS_EXCEPTION(props)) { + if(UCASE_GET_TYPE(props)==UCASE_LOWER) { + c+=UCASE_GET_DELTA(props); + } + } 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; + } else if(HAS_SLOT(excWord, UCASE_EXC_UPPER)) { + idx=UCASE_EXC_UPPER; + } else { + return c; + } + GET_SLOT_VALUE(excWord, idx, pe, c); + } + return c; +} + +static const char16_t iDot[2] = { 0x69, 0x307 }; +static const char16_t jDot[2] = { 0x6a, 0x307 }; +static const char16_t iOgonekDot[3] = { 0x12f, 0x307 }; +static const char16_t iDotGrave[3] = { 0x69, 0x307, 0x300 }; +static const char16_t iDotAcute[3] = { 0x69, 0x307, 0x301 }; +static const char16_t iDotTilde[3] = { 0x69, 0x307, 0x303 }; + + +U_CFUNC void U_EXPORT2 +ucase_addCaseClosure(UChar32 c, const USetAdder *sa) { + uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); + if(!UCASE_HAS_EXCEPTION(props)) { + if(UCASE_GET_TYPE(props)!=UCASE_NONE) { + /* add the one simple case mapping, no matter what type it is */ + int32_t delta=UCASE_GET_DELTA(props); + if(delta!=0) { + sa->add(sa->set, c+delta); + } + } + } else { + /* + * c has exceptions, so there may be multiple simple and/or + * full case mappings. Add them all. + */ + const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props); + uint16_t excWord=*pe++; + const uint16_t *pe0=pe; + + // Hardcode the case closure of i and its relatives and ignore the + // data file data for these characters. + // The Turkic dotless i and dotted I with their case mapping conditions + // and case folding option make the related characters behave specially. + // This code matches their closure behavior to their case folding behavior. + if (excWord&UCASE_EXC_CONDITIONAL_FOLD) { + // These characters have Turkic case foldings. Hardcode their closure. + if (c == 0x49) { + // Regular i and I are in one equivalence class. + sa->add(sa->set, 0x69); + return; + } else if (c == 0x130) { + // Dotted I is in a class with <0069 0307> + // (for canonical equivalence with <0049 0307>). + sa->addString(sa->set, iDot, 2); + return; + } + } else if (c == 0x69) { + sa->add(sa->set, 0x49); + return; + } else if (c == 0x131) { + // Dotless i is in a class by itself. + return; + } + + /* add all simple case mappings */ + for(int32_t idx=UCASE_EXC_LOWER; idx<=UCASE_EXC_TITLE; ++idx) { + if(HAS_SLOT(excWord, idx)) { + pe=pe0; + UChar32 mapping; + GET_SLOT_VALUE(excWord, idx, pe, mapping); + sa->add(sa->set, mapping); + } + } + if(HAS_SLOT(excWord, UCASE_EXC_DELTA)) { + pe=pe0; + 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 */ + const char16_t *closure; + int32_t closureLength; + if(HAS_SLOT(excWord, UCASE_EXC_CLOSURE)) { + pe=pe0; + GET_SLOT_VALUE(excWord, UCASE_EXC_CLOSURE, pe, closureLength); + closureLength&=UCASE_CLOSURE_MAX_LENGTH; /* higher bits are reserved */ + closure=(const char16_t *)pe+1; /* behind this slot, unless there are full case mappings */ + } else { + closureLength=0; + closure=nullptr; + } + + /* add the full case folding */ + if(HAS_SLOT(excWord, UCASE_EXC_FULL_MAPPINGS)) { + pe=pe0; + int32_t fullLength; + GET_SLOT_VALUE(excWord, UCASE_EXC_FULL_MAPPINGS, pe, fullLength); + + /* start of full case mapping strings */ + ++pe; + + fullLength&=0xffff; /* bits 16 and higher are reserved */ + + /* skip the lowercase result string */ + pe+=fullLength&UCASE_FULL_LOWER; + fullLength>>=4; + + /* add the full case folding string */ + int32_t length=fullLength&0xf; + if(length!=0) { + sa->addString(sa->set, (const char16_t *)pe, length); + pe+=length; + } + + /* skip the uppercase and titlecase strings */ + fullLength>>=4; + pe+=fullLength&0xf; + fullLength>>=4; + pe+=fullLength; + + closure=(const char16_t *)pe; /* behind full case mappings */ + } + + /* add each code point in the closure string */ + for(int32_t idx=0; idxadd(sa->set, mapping); + } + } +} + +namespace { + +/** + * Add the simple case closure mapping, + * except if there is not actually an scf relationship between the two characters. + * TODO: Unicode should probably add the corresponding scf mappings. + * See https://crbug.com/v8/13377 and Unicode-internal PAG issue #23. + * If & when those scf mappings are added, we should be able to remove all of these exceptions. + */ +void addOneSimpleCaseClosure(UChar32 c, UChar32 t, const USetAdder *sa) { + switch (c) { + case 0x0390: + if (t == 0x1FD3) { return; } + break; + case 0x03B0: + if (t == 0x1FE3) { return; } + break; + case 0x1FD3: + if (t == 0x0390) { return; } + break; + case 0x1FE3: + if (t == 0x03B0) { return; } + break; + case 0xFB05: + if (t == 0xFB06) { return; } + break; + case 0xFB06: + if (t == 0xFB05) { return; } + break; + default: + break; + } + sa->add(sa->set, t); +} + +} // namespace + +U_CFUNC void U_EXPORT2 +ucase_addSimpleCaseClosure(UChar32 c, const USetAdder *sa) { + uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); + if(!UCASE_HAS_EXCEPTION(props)) { + if(UCASE_GET_TYPE(props)!=UCASE_NONE) { + /* add the one simple case mapping, no matter what type it is */ + int32_t delta=UCASE_GET_DELTA(props); + if(delta!=0) { + sa->add(sa->set, c+delta); + } + } + } else { + // c has exceptions. Add the mappings relevant for scf=Simple_Case_Folding. + const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props); + uint16_t excWord=*pe++; + const uint16_t *pe0=pe; + + // Hardcode the case closure of i and its relatives and ignore the + // data file data for these characters, like in ucase_addCaseClosure(). + if (excWord&UCASE_EXC_CONDITIONAL_FOLD) { + // These characters have Turkic case foldings. Hardcode their closure. + if (c == 0x49) { + // Regular i and I are in one equivalence class. + sa->add(sa->set, 0x69); + return; + } else if (c == 0x130) { + // For scf=Simple_Case_Folding, dotted I is in a class by itself. + return; + } + } else if (c == 0x69) { + sa->add(sa->set, 0x49); + return; + } else if (c == 0x131) { + // Dotless i is in a class by itself. + return; + } + + // Add all simple case mappings. + for(int32_t idx=UCASE_EXC_LOWER; idx<=UCASE_EXC_TITLE; ++idx) { + if(HAS_SLOT(excWord, idx)) { + pe=pe0; + UChar32 mapping; + GET_SLOT_VALUE(excWord, idx, pe, mapping); + addOneSimpleCaseClosure(c, mapping, sa); + } + } + if(HAS_SLOT(excWord, UCASE_EXC_DELTA)) { + pe=pe0; + int32_t delta; + GET_SLOT_VALUE(excWord, UCASE_EXC_DELTA, pe, delta); + UChar32 mapping = (excWord&UCASE_EXC_DELTA_IS_NEGATIVE)==0 ? c+delta : c-delta; + addOneSimpleCaseClosure(c, mapping, sa); + } + + /* get the closure string pointer & length */ + const char16_t *closure; + int32_t closureLength; + if(HAS_SLOT(excWord, UCASE_EXC_CLOSURE)) { + pe=pe0; + GET_SLOT_VALUE(excWord, UCASE_EXC_CLOSURE, pe, closureLength); + closureLength&=UCASE_CLOSURE_MAX_LENGTH; /* higher bits are reserved */ + closure=(const char16_t *)pe+1; /* behind this slot, unless there are full case mappings */ + } else { + closureLength=0; + closure=nullptr; + } + + // Skip the full case mappings. + if(closureLength > 0 && HAS_SLOT(excWord, UCASE_EXC_FULL_MAPPINGS)) { + pe=pe0; + int32_t fullLength; + GET_SLOT_VALUE(excWord, UCASE_EXC_FULL_MAPPINGS, pe, fullLength); + + /* start of full case mapping strings */ + ++pe; + + fullLength&=0xffff; /* bits 16 and higher are reserved */ + + // Skip all 4 full case mappings. + pe+=fullLength&UCASE_FULL_LOWER; + fullLength>>=4; + pe+=fullLength&0xf; + fullLength>>=4; + pe+=fullLength&0xf; + fullLength>>=4; + pe+=fullLength; + + closure=(const char16_t *)pe; /* behind full case mappings */ + } + + // Add each code point in the closure string whose scf maps back to c. + for(int32_t idx=0; idx0 and max>0 and length<=max + */ +static inline int32_t +strcmpMax(const char16_t *s, int32_t length, const char16_t *t, int32_t max) { + int32_t c1, c2; + + max-=length; /* we require length<=max, so no need to decrement max in the loop */ + do { + c1=*s++; + c2=*t++; + if(c2==0) { + return 1; /* reached the end of t but not of s */ + } + c1-=c2; + if(c1!=0) { + return c1; /* return difference result */ + } + } while(--length>0); + /* ends with length==0 */ + + if(max==0 || *t==0) { + return 0; /* equal to length of both strings */ + } else { + return -max; /* return length difference */ + } +} + +U_CFUNC UBool U_EXPORT2 +ucase_addStringCaseClosure(const char16_t *s, int32_t length, const USetAdder *sa) { + int32_t i, start, limit, result, unfoldRows, unfoldRowWidth, unfoldStringWidth; + + if(ucase_props_singleton.unfold==nullptr || s==nullptr) { + return false; /* no reverse case folding data, or no string */ + } + if(length<=1) { + /* the string is too short to find any match */ + /* + * more precise would be: + * if(!u_strHasMoreChar32Than(s, length, 1)) + * but this does not make much practical difference because + * a single supplementary code point would just not be found + */ + return false; + } + + const uint16_t *unfold=ucase_props_singleton.unfold; + unfoldRows=unfold[UCASE_UNFOLD_ROWS]; + unfoldRowWidth=unfold[UCASE_UNFOLD_ROW_WIDTH]; + unfoldStringWidth=unfold[UCASE_UNFOLD_STRING_WIDTH]; + unfold+=unfoldRowWidth; + + if(length>unfoldStringWidth) { + /* the string is too long to find any match */ + return false; + } + + /* do a binary search for the string */ + start=0; + limit=unfoldRows; + while(start(unfold+(i*unfoldRowWidth)); + result=strcmpMax(s, length, p, unfoldStringWidth); + + if(result==0) { + /* found the string: add each code point, and its case closure */ + UChar32 c; + + for(i=unfoldStringWidth; iadd(sa->set, c); + ucase_addCaseClosure(c, sa); + } + return true; + } else if(result<0) { + limit=i; + } else /* result>0 */ { + start=i+1; + } + } + + return false; /* string not found */ +} + +U_NAMESPACE_BEGIN + +FullCaseFoldingIterator::FullCaseFoldingIterator() + : unfold(reinterpret_cast(ucase_props_singleton.unfold)), + unfoldRows(unfold[UCASE_UNFOLD_ROWS]), + unfoldRowWidth(unfold[UCASE_UNFOLD_ROW_WIDTH]), + unfoldStringWidth(unfold[UCASE_UNFOLD_STRING_WIDTH]), + currentRow(0), + rowCpIndex(unfoldStringWidth) { + unfold+=unfoldRowWidth; +} + +UChar32 +FullCaseFoldingIterator::next(UnicodeString &full) { + // Advance past the last-delivered code point. + const char16_t *p=unfold+(currentRow*unfoldRowWidth); + if(rowCpIndex>=unfoldRowWidth || p[rowCpIndex]==0) { + ++currentRow; + p+=unfoldRowWidth; + rowCpIndex=unfoldStringWidth; + } + if(currentRow>=unfoldRows) { return U_SENTINEL; } + // Set "full" to the NUL-terminated string in the first unfold column. + int32_t length=unfoldStringWidth; + while(length>0 && p[length-1]==0) { --length; } + full.setTo(false, p, length); + // Return the code point. + UChar32 c; + U16_NEXT_UNSAFE(p, rowCpIndex, c); + return c; +} + +namespace LatinCase { + +const int8_t TO_LOWER_NORMAL[LIMIT] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, EXC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, EXC, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + EXC, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, + + 0, 1, 0, 1, 0, 1, 0, 1, 0, EXC, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, EXC +}; + +const int8_t TO_LOWER_TR_LT[LIMIT] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 32, 32, 32, 32, 32, 32, 32, 32, EXC, EXC, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, EXC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, EXC, EXC, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, EXC, + 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, EXC, 0, 1, 0, 1, 0, EXC, 0, + EXC, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, + + 0, 1, 0, 1, 0, 1, 0, 1, 0, EXC, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, EXC +}; + +const int8_t TO_UPPER_NORMAL[LIMIT] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, EXC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EXC, + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, -32, -32, -32, -32, -32, 121, + + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, + 0, EXC, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, + + -1, 0, -1, 0, -1, 0, -1, 0, -1, EXC, 0, -1, 0, -1, 0, -1, + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, + 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, EXC +}; + +const int8_t TO_UPPER_TR[LIMIT] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -32, -32, -32, -32, -32, -32, -32, -32, EXC, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, EXC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EXC, + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, -32, -32, -32, -32, -32, 121, + + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, + 0, EXC, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, + + -1, 0, -1, 0, -1, 0, -1, 0, -1, EXC, 0, -1, 0, -1, 0, -1, + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, + 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, EXC +}; + +} // namespace LatinCase + +U_NAMESPACE_END + +/** @return UCASE_NONE, UCASE_LOWER, UCASE_UPPER, UCASE_TITLE */ +U_CAPI int32_t U_EXPORT2 +ucase_getType(UChar32 c) { + uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); + return UCASE_GET_TYPE(props); +} + +/** @return same as ucase_getType() and set bit 2 if c is case-ignorable */ +U_CAPI int32_t U_EXPORT2 +ucase_getTypeOrIgnorable(UChar32 c) { + uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); + return UCASE_GET_TYPE_AND_IGNORABLE(props); +} + +/** @return UCASE_NO_DOT, UCASE_SOFT_DOTTED, UCASE_ABOVE, UCASE_OTHER_ACCENT */ +static inline int32_t +getDotType(UChar32 c) { + uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); + if(!UCASE_HAS_EXCEPTION(props)) { + return props&UCASE_DOT_MASK; + } else { + const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props); + return (*pe>>UCASE_EXC_DOT_SHIFT)&UCASE_DOT_MASK; + } +} + +U_CAPI UBool U_EXPORT2 +ucase_isSoftDotted(UChar32 c) { + return (UBool)(getDotType(c)==UCASE_SOFT_DOTTED); +} + +U_CAPI UBool U_EXPORT2 +ucase_isCaseSensitive(UChar32 c) { + uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); + 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 ------------------------------------------------------------ */ + +/* + * These internal functions form the core of string case mappings. + * They map single code points to result code points or strings and take + * all necessary conditions (context, locale ID, options) into account. + * + * They do not iterate over the source or write to the destination + * so that the same functions are useful for non-standard string storage, + * such as in a Replaceable (for Transliterator) or UTF-8/32 strings etc. + * For the same reason, the "surrounding text" context is passed in as a + * UCaseContextIterator which does not make any assumptions about + * the underlying storage. + * + * This section contains helper functions that check for conditions + * in the input text surrounding the current code point + * according to SpecialCasing.txt. + * + * Each helper function gets the index + * - after the current code point if it looks at following text + * - before the current code point if it looks at preceding text + * + * Unicode 3.2 UAX 21 "Case Mappings" defines the conditions as follows: + * + * Final_Sigma + * C is preceded by a sequence consisting of + * a cased letter and a case-ignorable sequence, + * and C is not followed by a sequence consisting of + * an ignorable sequence and then a cased letter. + * + * More_Above + * C is followed by one or more characters of combining class 230 (ABOVE) + * in the combining character sequence. + * + * After_Soft_Dotted + * The last preceding character with combining class of zero before C + * was Soft_Dotted, + * and there is no intervening combining character class 230 (ABOVE). + * + * Before_Dot + * C is followed by combining dot above (U+0307). + * Any sequence of characters with a combining class that is neither 0 nor 230 + * may intervene between the current character and the combining dot above. + * + * The erratum from 2002-10-31 adds the condition + * + * After_I + * The last preceding base character was an uppercase I, and there is no + * intervening combining character class 230 (ABOVE). + * + * (See Jitterbug 2344 and the comments on After_I below.) + * + * Helper definitions in Unicode 3.2 UAX 21: + * + * D1. A character C is defined to be cased + * if it meets any of the following criteria: + * + * - The general category of C is Titlecase Letter (Lt) + * - In [CoreProps], C has one of the properties Uppercase, or Lowercase + * - Given D = NFD(C), then it is not the case that: + * D = UCD_lower(D) = UCD_upper(D) = UCD_title(D) + * (This third criterion does not add any characters to the list + * for Unicode 3.2. Ignored.) + * + * D2. A character C is defined to be case-ignorable + * if it meets either of the following criteria: + * + * - The general category of C is + * Nonspacing Mark (Mn), or Enclosing Mark (Me), or Format Control (Cf), or + * Letter Modifier (Lm), or Symbol Modifier (Sk) + * - C is one of the following characters + * U+0027 APOSTROPHE + * U+00AD SOFT HYPHEN (SHY) + * U+2019 RIGHT SINGLE QUOTATION MARK + * (the preferred character for apostrophe) + * + * D3. A case-ignorable sequence is a sequence of + * zero or more case-ignorable characters. + */ + +#define is_d(c) ((c)=='d' || (c)=='D') +#define is_e(c) ((c)=='e' || (c)=='E') +#define is_i(c) ((c)=='i' || (c)=='I') +#define is_l(c) ((c)=='l' || (c)=='L') +#define is_r(c) ((c)=='r' || (c)=='R') +#define is_t(c) ((c)=='t' || (c)=='T') +#define is_u(c) ((c)=='u' || (c)=='U') +#define is_y(c) ((c)=='y' || (c)=='Y') +#define is_z(c) ((c)=='z' || (c)=='Z') + +/* separator? */ +#define is_sep(c) ((c)=='_' || (c)=='-' || (c)==0) + +/** + * Requires non-nullptr locale ID but otherwise does the equivalent of + * checking for language codes as if uloc_getLanguage() were called: + * Accepts both 2- and 3-letter codes and accepts case variants. + */ +U_CFUNC int32_t +ucase_getCaseLocale(const char *locale) { + /* + * This function used to use uloc_getLanguage(), but the current code + * removes the dependency of this low-level code on uloc implementation code + * and is faster because not the whole locale ID has to be + * examined and copied/transformed. + * + * Because this code does not want to depend on uloc, the caller must + * pass in a non-nullptr locale, i.e., may need to call uloc_getDefault(). + */ + char c=*locale++; + // Fastpath for English "en" which is often used for default (=root locale) case mappings, + // and for Chinese "zh": Very common but no special case mapping behavior. + // Then check lowercase vs. uppercase to reduce the number of comparisons + // for other locales without special behavior. + if(c=='e') { + /* el or ell? */ + c=*locale++; + if(is_l(c)) { + c=*locale++; + if(is_l(c)) { + c=*locale; + } + if(is_sep(c)) { + return UCASE_LOC_GREEK; + } + } + // en, es, ... -> root + } else if(c=='z') { + return UCASE_LOC_ROOT; +#if U_CHARSET_FAMILY==U_ASCII_FAMILY + } else if(c>='a') { // ASCII a-z = 0x61..0x7a, after A-Z +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY + } else if(c<='z') { // EBCDIC a-z = 0x81..0xa9 with two gaps, before A-Z +#else +# error Unknown charset family! +#endif + // lowercase c + if(c=='t') { + /* tr or tur? */ + c=*locale++; + if(is_u(c)) { + c=*locale++; + } + if(is_r(c)) { + c=*locale; + if(is_sep(c)) { + return UCASE_LOC_TURKISH; + } + } + } else if(c=='a') { + /* az or aze? */ + c=*locale++; + if(is_z(c)) { + c=*locale++; + if(is_e(c)) { + c=*locale; + } + if(is_sep(c)) { + return UCASE_LOC_TURKISH; + } + } + } else if(c=='l') { + /* lt or lit? */ + c=*locale++; + if(is_i(c)) { + c=*locale++; + } + if(is_t(c)) { + c=*locale; + if(is_sep(c)) { + return UCASE_LOC_LITHUANIAN; + } + } + } else if(c=='n') { + /* nl or nld? */ + c=*locale++; + if(is_l(c)) { + c=*locale++; + if(is_d(c)) { + c=*locale; + } + if(is_sep(c)) { + return UCASE_LOC_DUTCH; + } + } + } else if(c=='h') { + /* hy or hye? *not* hyw */ + c=*locale++; + if(is_y(c)) { + c=*locale++; + if(is_e(c)) { + c=*locale; + } + if(is_sep(c)) { + return UCASE_LOC_ARMENIAN; + } + } + } + } else { + // uppercase c + // Same code as for lowercase c but also check for 'E'. + if(c=='T') { + /* tr or tur? */ + c=*locale++; + if(is_u(c)) { + c=*locale++; + } + if(is_r(c)) { + c=*locale; + if(is_sep(c)) { + return UCASE_LOC_TURKISH; + } + } + } else if(c=='A') { + /* az or aze? */ + c=*locale++; + if(is_z(c)) { + c=*locale++; + if(is_e(c)) { + c=*locale; + } + if(is_sep(c)) { + return UCASE_LOC_TURKISH; + } + } + } else if(c=='L') { + /* lt or lit? */ + c=*locale++; + if(is_i(c)) { + c=*locale++; + } + if(is_t(c)) { + c=*locale; + if(is_sep(c)) { + return UCASE_LOC_LITHUANIAN; + } + } + } else if(c=='E') { + /* el or ell? */ + c=*locale++; + if(is_l(c)) { + c=*locale++; + if(is_l(c)) { + c=*locale; + } + if(is_sep(c)) { + return UCASE_LOC_GREEK; + } + } + } else if(c=='N') { + /* nl or nld? */ + c=*locale++; + if(is_l(c)) { + c=*locale++; + if(is_d(c)) { + c=*locale; + } + if(is_sep(c)) { + return UCASE_LOC_DUTCH; + } + } + } else if(c=='H') { + /* hy or hye? *not* hyw */ + c=*locale++; + if(is_y(c)) { + c=*locale++; + if(is_e(c)) { + c=*locale; + } + if(is_sep(c)) { + return UCASE_LOC_ARMENIAN; + } + } + } + } + return UCASE_LOC_ROOT; +} + +/* + * Is followed by + * {case-ignorable}* cased + * ? + * (dir determines looking forward/backward) + * If a character is case-ignorable, it is skipped regardless of whether + * it is also cased or not. + */ +static UBool +isFollowedByCasedLetter(UCaseContextIterator *iter, void *context, int8_t dir) { + UChar32 c; + + if(iter==nullptr) { + return false; + } + + for(/* dir!=0 sets direction */; (c=iter(context, dir))>=0; dir=0) { + int32_t type=ucase_getTypeOrIgnorable(c); + if(type&4) { + /* case-ignorable, continue with the loop */ + } else if(type!=UCASE_NONE) { + return true; /* followed by cased letter */ + } else { + return false; /* uncased and not case-ignorable */ + } + } + + return false; /* not followed by cased letter */ +} + +/* Is preceded by Soft_Dotted character with no intervening cc=230 ? */ +static UBool +isPrecededBySoftDotted(UCaseContextIterator *iter, void *context) { + UChar32 c; + int32_t dotType; + int8_t dir; + + if(iter==nullptr) { + return false; + } + + for(dir=-1; (c=iter(context, dir))>=0; dir=0) { + dotType=getDotType(c); + if(dotType==UCASE_SOFT_DOTTED) { + return true; /* preceded by TYPE_i */ + } else if(dotType!=UCASE_OTHER_ACCENT) { + return false; /* preceded by different base character (not TYPE_i), or intervening cc==230 */ + } + } + + return false; /* not preceded by TYPE_i */ +} + +/* + * See Jitterbug 2344: + * The condition After_I for Turkic-lowercasing of U+0307 combining dot above + * is checked in ICU 2.0, 2.1, 2.6 but was not in 2.2 & 2.4 because + * we made those releases compatible with Unicode 3.2 which had not fixed + * a related bug in SpecialCasing.txt. + * + * From the Jitterbug 2344 text: + * ... this bug is listed as a Unicode erratum + * from 2002-10-31 at http://www.unicode.org/uni2errata/UnicodeErrata.html + * + * There are two errors in SpecialCasing.txt. + * 1. Missing semicolons on two lines. ... [irrelevant for ICU] + * 2. An incorrect context definition. Correct as follows: + * < 0307; ; 0307; 0307; tr After_Soft_Dotted; # COMBINING DOT ABOVE + * < 0307; ; 0307; 0307; az After_Soft_Dotted; # COMBINING DOT ABOVE + * --- + * > 0307; ; 0307; 0307; tr After_I; # COMBINING DOT ABOVE + * > 0307; ; 0307; 0307; az After_I; # COMBINING DOT ABOVE + * where the context After_I is defined as: + * The last preceding base character was an uppercase I, and there is no + * intervening combining character class 230 (ABOVE). + * + * + * Note that SpecialCasing.txt even in Unicode 3.2 described the condition as: + * + * # When lowercasing, remove dot_above in the sequence I + dot_above, which will turn into i. + * # This matches the behavior of the canonically equivalent I-dot_above + * + * See also the description in this place in older versions of uchar.c (revision 1.100). + * + * Markus W. Scherer 2003-feb-15 + */ + +/* Is preceded by base character 'I' with no intervening cc=230 ? */ +static UBool +isPrecededBy_I(UCaseContextIterator *iter, void *context) { + UChar32 c; + int32_t dotType; + int8_t dir; + + if(iter==nullptr) { + return false; + } + + for(dir=-1; (c=iter(context, dir))>=0; dir=0) { + if(c==0x49) { + return true; /* preceded by I */ + } + dotType=getDotType(c); + if(dotType!=UCASE_OTHER_ACCENT) { + return false; /* preceded by different base character (not I), or intervening cc==230 */ + } + } + + return false; /* not preceded by I */ +} + +/* Is followed by one or more cc==230 ? */ +static UBool +isFollowedByMoreAbove(UCaseContextIterator *iter, void *context) { + UChar32 c; + int32_t dotType; + int8_t dir; + + if(iter==nullptr) { + return false; + } + + for(dir=1; (c=iter(context, dir))>=0; dir=0) { + dotType=getDotType(c); + if(dotType==UCASE_ABOVE) { + return true; /* at least one cc==230 following */ + } else if(dotType!=UCASE_OTHER_ACCENT) { + return false; /* next base character, no more cc==230 following */ + } + } + + return false; /* no more cc==230 following */ +} + +/* Is followed by a dot above (without cc==230 in between) ? */ +static UBool +isFollowedByDotAbove(UCaseContextIterator *iter, void *context) { + UChar32 c; + int32_t dotType; + int8_t dir; + + if(iter==nullptr) { + return false; + } + + for(dir=1; (c=iter(context, dir))>=0; dir=0) { + if(c==0x307) { + return true; + } + dotType=getDotType(c); + if(dotType!=UCASE_OTHER_ACCENT) { + return false; /* next base character or cc==230 in between */ + } + } + + return false; /* no dot above following */ +} + +U_CAPI int32_t U_EXPORT2 +ucase_toFullLower(UChar32 c, + UCaseContextIterator *iter, void *context, + const char16_t **pString, + int32_t loc) { + // The sign of the result has meaning, input must be non-negative so that it can be returned as is. + U_ASSERT(c >= 0); + UChar32 result=c; + // Reset the output pointer in case it was uninitialized. + *pString=nullptr; + uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); + if(!UCASE_HAS_EXCEPTION(props)) { + if(UCASE_IS_UPPER_OR_TITLE(props)) { + result=c+UCASE_GET_DELTA(props); + } + } else { + const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props), *pe2; + uint16_t excWord=*pe++; + int32_t full; + + pe2=pe; + + if(excWord&UCASE_EXC_CONDITIONAL_SPECIAL) { + /* use hardcoded conditions and mappings */ + + /* + * Test for conditional mappings first + * (otherwise the unconditional default mappings are always taken), + * then test for characters that have unconditional mappings in SpecialCasing.txt, + * then get the UnicodeData.txt mappings. + */ + if( loc==UCASE_LOC_LITHUANIAN && + /* base characters, find accents above */ + (((c==0x49 || c==0x4a || c==0x12e) && + isFollowedByMoreAbove(iter, context)) || + /* precomposed with accent above, no need to find one */ + (c==0xcc || c==0xcd || c==0x128)) + ) { + /* + # Lithuanian + + # Lithuanian retains the dot in a lowercase i when followed by accents. + + # Introduce an explicit dot above when lowercasing capital I's and J's + # whenever there are more accents above. + # (of the accents used in Lithuanian: grave, acute, tilde above, and ogonek) + + 0049; 0069 0307; 0049; 0049; lt More_Above; # LATIN CAPITAL LETTER I + 004A; 006A 0307; 004A; 004A; lt More_Above; # LATIN CAPITAL LETTER J + 012E; 012F 0307; 012E; 012E; lt More_Above; # LATIN CAPITAL LETTER I WITH OGONEK + 00CC; 0069 0307 0300; 00CC; 00CC; lt; # LATIN CAPITAL LETTER I WITH GRAVE + 00CD; 0069 0307 0301; 00CD; 00CD; lt; # LATIN CAPITAL LETTER I WITH ACUTE + 0128; 0069 0307 0303; 0128; 0128; lt; # LATIN CAPITAL LETTER I WITH TILDE + */ + switch(c) { + case 0x49: /* LATIN CAPITAL LETTER I */ + *pString=iDot; + return 2; + case 0x4a: /* LATIN CAPITAL LETTER J */ + *pString=jDot; + return 2; + case 0x12e: /* LATIN CAPITAL LETTER I WITH OGONEK */ + *pString=iOgonekDot; + return 2; + case 0xcc: /* LATIN CAPITAL LETTER I WITH GRAVE */ + *pString=iDotGrave; + return 3; + case 0xcd: /* LATIN CAPITAL LETTER I WITH ACUTE */ + *pString=iDotAcute; + return 3; + case 0x128: /* LATIN CAPITAL LETTER I WITH TILDE */ + *pString=iDotTilde; + return 3; + default: + return 0; /* will not occur */ + } + /* # Turkish and Azeri */ + } else if(loc==UCASE_LOC_TURKISH && c==0x130) { + /* + # I and i-dotless; I-dot and i are case pairs in Turkish and Azeri + # The following rules handle those cases. + + 0130; 0069; 0130; 0130; tr # LATIN CAPITAL LETTER I WITH DOT ABOVE + 0130; 0069; 0130; 0130; az # LATIN CAPITAL LETTER I WITH DOT ABOVE + */ + return 0x69; + } else if(loc==UCASE_LOC_TURKISH && c==0x307 && isPrecededBy_I(iter, context)) { + /* + # When lowercasing, remove dot_above in the sequence I + dot_above, which will turn into i. + # This matches the behavior of the canonically equivalent I-dot_above + + 0307; ; 0307; 0307; tr After_I; # COMBINING DOT ABOVE + 0307; ; 0307; 0307; az After_I; # COMBINING DOT ABOVE + */ + return 0; /* remove the dot (continue without output) */ + } else if(loc==UCASE_LOC_TURKISH && c==0x49 && !isFollowedByDotAbove(iter, context)) { + /* + # When lowercasing, unless an I is before a dot_above, it turns into a dotless i. + + 0049; 0131; 0049; 0049; tr Not_Before_Dot; # LATIN CAPITAL LETTER I + 0049; 0131; 0049; 0049; az Not_Before_Dot; # LATIN CAPITAL LETTER I + */ + return 0x131; + } else if(c==0x130) { + /* + # Preserve canonical equivalence for I with dot. Turkic is handled below. + + 0130; 0069 0307; 0130; 0130; # LATIN CAPITAL LETTER I WITH DOT ABOVE + */ + *pString=iDot; + return 2; + } else if( c==0x3a3 && + !isFollowedByCasedLetter(iter, context, 1) && + isFollowedByCasedLetter(iter, context, -1) /* -1=preceded */ + ) { + /* greek capital sigma maps depending on surrounding cased letters (see SpecialCasing.txt) */ + /* + # Special case for final form of sigma + + 03A3; 03C2; 03A3; 03A3; Final_Sigma; # GREEK CAPITAL LETTER SIGMA + */ + return 0x3c2; /* greek small final sigma */ + } else { + /* no known conditional special case mapping, use a normal mapping */ + } + } else if(HAS_SLOT(excWord, UCASE_EXC_FULL_MAPPINGS)) { + GET_SLOT_VALUE(excWord, UCASE_EXC_FULL_MAPPINGS, pe, full); + full&=UCASE_FULL_LOWER; + if(full!=0) { + /* set the output pointer to the lowercase mapping */ + *pString=reinterpret_cast(pe+1); + + /* return the string length */ + return full; + } + } + + if(HAS_SLOT(excWord, UCASE_EXC_DELTA) && UCASE_IS_UPPER_OR_TITLE(props)) { + int32_t delta; + GET_SLOT_VALUE(excWord, UCASE_EXC_DELTA, pe2, 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); + } + } + + return (result==c) ? ~result : result; +} + +/* internal */ +static int32_t +toUpperOrTitle(UChar32 c, + UCaseContextIterator *iter, void *context, + const char16_t **pString, + int32_t loc, + UBool upperNotTitle) { + // The sign of the result has meaning, input must be non-negative so that it can be returned as is. + U_ASSERT(c >= 0); + UChar32 result=c; + // Reset the output pointer in case it was uninitialized. + *pString=nullptr; + uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); + if(!UCASE_HAS_EXCEPTION(props)) { + if(UCASE_GET_TYPE(props)==UCASE_LOWER) { + result=c+UCASE_GET_DELTA(props); + } + } else { + const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props), *pe2; + uint16_t excWord=*pe++; + int32_t full, idx; + + pe2=pe; + + if(excWord&UCASE_EXC_CONDITIONAL_SPECIAL) { + /* use hardcoded conditions and mappings */ + if(loc==UCASE_LOC_TURKISH && c==0x69) { + /* + # Turkish and Azeri + + # I and i-dotless; I-dot and i are case pairs in Turkish and Azeri + # The following rules handle those cases. + + # When uppercasing, i turns into a dotted capital I + + 0069; 0069; 0130; 0130; tr; # LATIN SMALL LETTER I + 0069; 0069; 0130; 0130; az; # LATIN SMALL LETTER I + */ + return 0x130; + } else if(loc==UCASE_LOC_LITHUANIAN && c==0x307 && isPrecededBySoftDotted(iter, context)) { + /* + # Lithuanian + + # Lithuanian retains the dot in a lowercase i when followed by accents. + + # Remove DOT ABOVE after "i" with upper or titlecase + + 0307; 0307; ; ; lt After_Soft_Dotted; # COMBINING DOT ABOVE + */ + return 0; /* remove the dot (continue without output) */ + } else if(c==0x0587) { + // See ICU-13416: + // և ligature ech-yiwn + // uppercases to ԵՒ=ech+yiwn by default and in Western Armenian, + // but to ԵՎ=ech+vew in Eastern Armenian. + if(loc==UCASE_LOC_ARMENIAN) { + *pString=upperNotTitle ? u"ԵՎ" : u"Եվ"; + } else { + *pString=upperNotTitle ? u"ԵՒ" : u"Եւ"; + } + return 2; + } else { + /* no known conditional special case mapping, use a normal mapping */ + } + } else if(HAS_SLOT(excWord, UCASE_EXC_FULL_MAPPINGS)) { + GET_SLOT_VALUE(excWord, UCASE_EXC_FULL_MAPPINGS, pe, full); + + /* start of full case mapping strings */ + ++pe; + + /* skip the lowercase and case-folding result strings */ + pe+=full&UCASE_FULL_LOWER; + full>>=4; + pe+=full&0xf; + full>>=4; + + if(upperNotTitle) { + full&=0xf; + } else { + /* skip the uppercase result string */ + pe+=full&0xf; + full=(full>>4)&0xf; + } + + if(full!=0) { + /* set the output pointer to the result string */ + *pString=reinterpret_cast(pe); + + /* return the string length */ + return full; + } + } + + if(HAS_SLOT(excWord, UCASE_EXC_DELTA) && UCASE_GET_TYPE(props)==UCASE_LOWER) { + int32_t delta; + GET_SLOT_VALUE(excWord, UCASE_EXC_DELTA, pe2, 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)) { + /* here, titlecase is same as uppercase */ + idx=UCASE_EXC_UPPER; + } else { + return ~c; + } + GET_SLOT_VALUE(excWord, idx, pe2, result); + } + + return (result==c) ? ~result : result; +} + +U_CAPI int32_t U_EXPORT2 +ucase_toFullUpper(UChar32 c, + UCaseContextIterator *iter, void *context, + const char16_t **pString, + int32_t caseLocale) { + return toUpperOrTitle(c, iter, context, pString, caseLocale, true); +} + +U_CAPI int32_t U_EXPORT2 +ucase_toFullTitle(UChar32 c, + UCaseContextIterator *iter, void *context, + const char16_t **pString, + int32_t caseLocale) { + return toUpperOrTitle(c, iter, context, pString, caseLocale, false); +} + +/* case folding ------------------------------------------------------------- */ + +/* + * Case folding is similar to lowercasing. + * The result may be a simple mapping, i.e., a single code point, or + * a full mapping, i.e., a string. + * If the case folding for a code point is the same as its simple (1:1) lowercase mapping, + * then only the lowercase mapping is stored. + * + * Some special cases are hardcoded because their conditions cannot be + * parsed and processed from CaseFolding.txt. + * + * Unicode 3.2 CaseFolding.txt specifies for its status field: + +# C: common case folding, common mappings shared by both simple and full mappings. +# F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. +# S: simple case folding, mappings to single characters where different from F. +# T: special case for uppercase I and dotted uppercase I +# - For non-Turkic languages, this mapping is normally not used. +# - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. +# +# Usage: +# A. To do a simple case folding, use the mappings with status C + S. +# B. To do a full case folding, use the mappings with status C + F. +# +# The mappings with status T can be used or omitted depending on the desired case-folding +# behavior. (The default option is to exclude them.) + + * Unicode 3.2 has 'T' mappings as follows: + +0049; T; 0131; # LATIN CAPITAL LETTER I +0130; T; 0069; # LATIN CAPITAL LETTER I WITH DOT ABOVE + + * while the default mappings for these code points are: + +0049; C; 0069; # LATIN CAPITAL LETTER I +0130; F; 0069 0307; # LATIN CAPITAL LETTER I WITH DOT ABOVE + + * U+0130 has no simple case folding (simple-case-folds to itself). + */ + +/* return the simple case folding mapping for c */ +U_CAPI UChar32 U_EXPORT2 +ucase_fold(UChar32 c, uint32_t options) { + uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); + if(!UCASE_HAS_EXCEPTION(props)) { + if(UCASE_IS_UPPER_OR_TITLE(props)) { + c+=UCASE_GET_DELTA(props); + } + } else { + const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props); + uint16_t excWord=*pe++; + int32_t idx; + if(excWord&UCASE_EXC_CONDITIONAL_FOLD) { + /* special case folding mappings, hardcoded */ + if((options&_FOLD_CASE_OPTIONS_MASK)==U_FOLD_CASE_DEFAULT) { + /* default mappings */ + if(c==0x49) { + /* 0049; C; 0069; # LATIN CAPITAL LETTER I */ + return 0x69; + } else if(c==0x130) { + /* no simple case folding for U+0130 */ + return c; + } + } else { + /* Turkic mappings */ + if(c==0x49) { + /* 0049; T; 0131; # LATIN CAPITAL LETTER I */ + return 0x131; + } else if(c==0x130) { + /* 0130; T; 0069; # LATIN CAPITAL LETTER I WITH DOT ABOVE */ + return 0x69; + } + } + } + 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)) { + idx=UCASE_EXC_LOWER; + } else { + return c; + } + GET_SLOT_VALUE(excWord, idx, pe, c); + } + return c; +} + +/* + * Issue for canonical caseless match (UAX #21): + * Turkic casefolding (using "T" mappings in CaseFolding.txt) does not preserve + * canonical equivalence, unlike default-option casefolding. + * For example, I-grave and I + grave fold to strings that are not canonically + * equivalent. + * For more details, see the comment in unorm_compare() in unorm.cpp + * and the intermediate prototype changes for Jitterbug 2021. + * (For example, revision 1.104 of uchar.c and 1.4 of CaseFolding.txt.) + * + * This did not get fixed because it appears that it is not possible to fix + * it for uppercase and lowercase characters (I-grave vs. i-grave) + * together in a way that they still fold to common result strings. + */ + +U_CAPI int32_t U_EXPORT2 +ucase_toFullFolding(UChar32 c, + const char16_t **pString, + uint32_t options) { + // The sign of the result has meaning, input must be non-negative so that it can be returned as is. + U_ASSERT(c >= 0); + UChar32 result=c; + // Reset the output pointer in case it was uninitialized. + *pString=nullptr; + uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); + if(!UCASE_HAS_EXCEPTION(props)) { + if(UCASE_IS_UPPER_OR_TITLE(props)) { + result=c+UCASE_GET_DELTA(props); + } + } else { + const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props), *pe2; + uint16_t excWord=*pe++; + int32_t full, idx; + + pe2=pe; + + if(excWord&UCASE_EXC_CONDITIONAL_FOLD) { + /* use hardcoded conditions and mappings */ + if((options&_FOLD_CASE_OPTIONS_MASK)==U_FOLD_CASE_DEFAULT) { + /* default mappings */ + if(c==0x49) { + /* 0049; C; 0069; # LATIN CAPITAL LETTER I */ + return 0x69; + } else if(c==0x130) { + /* 0130; F; 0069 0307; # LATIN CAPITAL LETTER I WITH DOT ABOVE */ + *pString=iDot; + return 2; + } + } else { + /* Turkic mappings */ + if(c==0x49) { + /* 0049; T; 0131; # LATIN CAPITAL LETTER I */ + return 0x131; + } else if(c==0x130) { + /* 0130; T; 0069; # LATIN CAPITAL LETTER I WITH DOT ABOVE */ + return 0x69; + } + } + } else if(HAS_SLOT(excWord, UCASE_EXC_FULL_MAPPINGS)) { + GET_SLOT_VALUE(excWord, UCASE_EXC_FULL_MAPPINGS, pe, full); + + /* start of full case mapping strings */ + ++pe; + + /* skip the lowercase result string */ + pe+=full&UCASE_FULL_LOWER; + full=(full>>4)&0xf; + + if(full!=0) { + /* set the output pointer to the result string */ + *pString=reinterpret_cast(pe); + + /* return the string length */ + return full; + } + } + + 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, pe2, 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)) { + idx=UCASE_EXC_LOWER; + } else { + return ~c; + } + GET_SLOT_VALUE(excWord, idx, pe2, result); + } + + return (result==c) ? ~result : result; +} + +/* case mapping properties API ---------------------------------------------- */ + +/* public API (see uchar.h) */ + +U_CAPI UBool U_EXPORT2 +u_isULowercase(UChar32 c) { + return (UBool)(UCASE_LOWER==ucase_getType(c)); +} + +U_CAPI UBool U_EXPORT2 +u_isUUppercase(UChar32 c) { + return (UBool)(UCASE_UPPER==ucase_getType(c)); +} + +/* Transforms the Unicode character to its lower case equivalent.*/ +U_CAPI UChar32 U_EXPORT2 +u_tolower(UChar32 c) { + return ucase_tolower(c); +} + +/* Transforms the Unicode character to its upper case equivalent.*/ +U_CAPI UChar32 U_EXPORT2 +u_toupper(UChar32 c) { + return ucase_toupper(c); +} + +/* Transforms the Unicode character to its title case equivalent.*/ +U_CAPI UChar32 U_EXPORT2 +u_totitle(UChar32 c) { + return ucase_totitle(c); +} + +/* return the simple case folding mapping for c */ +U_CAPI UChar32 U_EXPORT2 +u_foldCase(UChar32 c, uint32_t options) { + return ucase_fold(c, options); +} + +U_CFUNC int32_t U_EXPORT2 +ucase_hasBinaryProperty(UChar32 c, UProperty which) { + /* case mapping properties */ + const char16_t *resultString; + switch(which) { + case UCHAR_LOWERCASE: + return (UBool)(UCASE_LOWER==ucase_getType(c)); + case UCHAR_UPPERCASE: + return (UBool)(UCASE_UPPER==ucase_getType(c)); + case UCHAR_SOFT_DOTTED: + return ucase_isSoftDotted(c); + case UCHAR_CASE_SENSITIVE: + return ucase_isCaseSensitive(c); + case UCHAR_CASED: + return (UBool)(UCASE_NONE!=ucase_getType(c)); + case UCHAR_CASE_IGNORABLE: + return (UBool)(ucase_getTypeOrIgnorable(c)>>2); + /* + * Note: The following Changes_When_Xyz are defined as testing whether + * the NFD form of the input changes when Xyz-case-mapped. + * However, this simpler implementation of these properties, + * ignoring NFD, passes the tests. + * The implementation needs to be changed if the tests start failing. + * When that happens, optimizations should be used to work with the + * per-single-code point ucase_toFullXyz() functions unless + * the NFD form has more than one code point, + * and the property starts set needs to be the union of the + * start sets for normalization and case mappings. + */ + case UCHAR_CHANGES_WHEN_LOWERCASED: + return (UBool)(ucase_toFullLower(c, nullptr, nullptr, &resultString, UCASE_LOC_ROOT)>=0); + case UCHAR_CHANGES_WHEN_UPPERCASED: + return (UBool)(ucase_toFullUpper(c, nullptr, nullptr, &resultString, UCASE_LOC_ROOT)>=0); + case UCHAR_CHANGES_WHEN_TITLECASED: + return (UBool)(ucase_toFullTitle(c, nullptr, nullptr, &resultString, UCASE_LOC_ROOT)>=0); + /* case UCHAR_CHANGES_WHEN_CASEFOLDED: -- in uprops.c */ + case UCHAR_CHANGES_WHEN_CASEMAPPED: + return (UBool)( + ucase_toFullLower(c, nullptr, nullptr, &resultString, UCASE_LOC_ROOT)>=0 || + ucase_toFullUpper(c, nullptr, nullptr, &resultString, UCASE_LOC_ROOT)>=0 || + ucase_toFullTitle(c, nullptr, nullptr, &resultString, UCASE_LOC_ROOT)>=0); + default: + return false; + } +} diff --git a/deps/icu-small/source/common/ucase.h b/deps/icu-small/source/common/ucase.h index 049f0429122186..7e12f281c1ff1a 100644 --- a/deps/icu-small/source/common/ucase.h +++ b/deps/icu-small/source/common/ucase.h @@ -1,460 +1,464 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2004-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: ucase.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004aug30 -* created by: Markus W. Scherer -* -* Low-level Unicode character/string case mapping code. -*/ - -#ifndef __UCASE_H__ -#define __UCASE_H__ - -#include "unicode/utypes.h" -#include "unicode/uset.h" -#include "putilimp.h" -#include "uset_imp.h" -#include "udataswp.h" -#include "utrie2.h" - -#ifdef __cplusplus -U_NAMESPACE_BEGIN - -class UnicodeString; - -U_NAMESPACE_END -#endif - -/* library API -------------------------------------------------------------- */ - -U_CFUNC void U_EXPORT2 -ucase_addPropertyStarts(const USetAdder *sa, UErrorCode *pErrorCode); - -/** - * Requires non-NULL locale ID but otherwise does the equivalent of - * checking for language codes as if uloc_getLanguage() were called: - * Accepts both 2- and 3-letter codes and accepts case variants. - */ -U_CFUNC int32_t -ucase_getCaseLocale(const char *locale); - -/* Casing locale types for ucase_getCaseLocale */ -enum { - UCASE_LOC_UNKNOWN, - UCASE_LOC_ROOT, - UCASE_LOC_TURKISH, - UCASE_LOC_LITHUANIAN, - UCASE_LOC_GREEK, - UCASE_LOC_DUTCH, - UCASE_LOC_ARMENIAN -}; - -/** - * Bit mask for getting just the options from a string compare options word - * that are relevant for case-insensitive string comparison. - * See stringoptions.h. Also include _STRNCMP_STYLE and U_COMPARE_CODE_POINT_ORDER. - * @internal - */ -#define _STRCASECMP_OPTIONS_MASK 0xffff - -/** - * Bit mask for getting just the options from a string compare options word - * that are relevant for case folding (of a single string or code point). - * - * Currently only bit 0 for U_FOLD_CASE_EXCLUDE_SPECIAL_I. - * It is conceivable that at some point we might use one more bit for using uppercase sharp s. - * It is conceivable that at some point we might want the option to use only simple case foldings - * when operating on strings. - * - * See stringoptions.h. - * @internal - */ -#define _FOLD_CASE_OPTIONS_MASK 7 - -/* single-code point functions */ - -U_CAPI UChar32 U_EXPORT2 -ucase_tolower(UChar32 c); - -U_CAPI UChar32 U_EXPORT2 -ucase_toupper(UChar32 c); - -U_CAPI UChar32 U_EXPORT2 -ucase_totitle(UChar32 c); - -U_CAPI UChar32 U_EXPORT2 -ucase_fold(UChar32 c, uint32_t options); - -/** - * Adds all simple case mappings and the full case folding for c to sa, - * and also adds special case closure mappings. - * c itself is not added. - * For example, the mappings - * - for s include long s - * - for sharp s include ss - * - for k include the Kelvin sign - */ -U_CFUNC void U_EXPORT2 -ucase_addCaseClosure(UChar32 c, const USetAdder *sa); - -/** - * Maps the string to single code points and adds the associated case closure - * mappings. - * The string is mapped to code points if it is their full case folding string. - * In other words, this performs a reverse full case folding and then - * adds the case closure items of the resulting code points. - * If the string is found and its closure applied, then - * the string itself is added as well as part of its code points' closure. - * It must be length>=0. - * - * @return true if the string was found - */ -U_CFUNC UBool U_EXPORT2 -ucase_addStringCaseClosure(const UChar *s, int32_t length, const USetAdder *sa); - -#ifdef __cplusplus -U_NAMESPACE_BEGIN - -/** - * Iterator over characters with more than one code point in the full default Case_Folding. - */ -class U_COMMON_API FullCaseFoldingIterator { -public: - /** Constructor. */ - FullCaseFoldingIterator(); - /** - * Returns the next (cp, full) pair where "full" is cp's full default Case_Folding. - * Returns a negative cp value at the end of the iteration. - */ - UChar32 next(UnicodeString &full); -private: - FullCaseFoldingIterator(const FullCaseFoldingIterator &) = delete; // no copy - FullCaseFoldingIterator &operator=(const FullCaseFoldingIterator &) = delete; // no assignment - - const UChar *unfold; - int32_t unfoldRows; - int32_t unfoldRowWidth; - int32_t unfoldStringWidth; - int32_t currentRow; - int32_t rowCpIndex; -}; - -/** - * Fast case mapping data for ASCII/Latin. - * Linear arrays of delta bytes: 0=no mapping; EXC=exception. - * Deltas must not cross the ASCII boundary, or else they cannot be easily used - * in simple UTF-8 code. - */ -namespace LatinCase { - -/** Case mapping/folding data for code points up to U+017F. */ -constexpr UChar LIMIT = 0x180; -/** U+017F case-folds and uppercases crossing the ASCII boundary. */ -constexpr UChar LONG_S = 0x17f; -/** Exception: Complex mapping, or too-large delta. */ -constexpr int8_t EXC = -0x80; - -/** Deltas for lowercasing for most locales, and default case folding. */ -extern const int8_t TO_LOWER_NORMAL[LIMIT]; -/** Deltas for lowercasing for tr/az/lt, and Turkic case folding. */ -extern const int8_t TO_LOWER_TR_LT[LIMIT]; - -/** Deltas for uppercasing for most locales. */ -extern const int8_t TO_UPPER_NORMAL[LIMIT]; -/** Deltas for uppercasing for tr/az. */ -extern const int8_t TO_UPPER_TR[LIMIT]; - -} // namespace LatinCase - -U_NAMESPACE_END -#endif - -/** @return UCASE_NONE, UCASE_LOWER, UCASE_UPPER, UCASE_TITLE */ -U_CAPI int32_t U_EXPORT2 -ucase_getType(UChar32 c); - -/** @return like ucase_getType() but also sets UCASE_IGNORABLE if c is case-ignorable */ -U_CAPI int32_t U_EXPORT2 -ucase_getTypeOrIgnorable(UChar32 c); - -U_CAPI UBool U_EXPORT2 -ucase_isSoftDotted(UChar32 c); - -U_CAPI UBool U_EXPORT2 -ucase_isCaseSensitive(UChar32 c); - -/* string case mapping functions */ - -U_CDECL_BEGIN - -/** - * Iterator function for string case mappings, which need to look at the - * context (surrounding text) of a given character for conditional mappings. - * - * The iterator only needs to go backward or forward away from the - * character in question. It does not use any indexes on this interface. - * It does not support random access or an arbitrary change of - * iteration direction. - * - * The code point being case-mapped itself is never returned by - * this iterator. - * - * @param context A pointer to the iterator's working data. - * @param dir If <0 then start iterating backward from the character; - * if >0 then start iterating forward from the character; - * if 0 then continue iterating in the current direction. - * @return Next code point, or <0 when the iteration is done. - */ -typedef UChar32 U_CALLCONV -UCaseContextIterator(void *context, int8_t dir); - -/** - * Sample struct which may be used by some implementations of - * UCaseContextIterator. - */ -struct UCaseContext { - void *p; - int32_t start, index, limit; - int32_t cpStart, cpLimit; - int8_t dir; - int8_t b1, b2, b3; -}; -typedef struct UCaseContext UCaseContext; - -U_CDECL_END - -#define UCASECONTEXT_INITIALIZER { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0 } - -enum { - /** - * For string case mappings, a single character (a code point) is mapped - * either to itself (in which case in-place mapping functions do nothing), - * or to another single code point, or to a string. - * Aside from the string contents, these are indicated with a single int32_t - * value as follows: - * - * Mapping to self: Negative values (~self instead of -self to support U+0000) - * - * Mapping to another code point: Positive values >UCASE_MAX_STRING_LENGTH - * - * Mapping to a string: The string length (0..UCASE_MAX_STRING_LENGTH) is - * returned. Note that the string result may indeed have zero length. - */ - UCASE_MAX_STRING_LENGTH=0x1f -}; - -/** - * Get the full lowercase mapping for c. - * - * @param csp Case mapping properties. - * @param c Character to be mapped. - * @param iter Character iterator, used for context-sensitive mappings. - * See UCaseContextIterator for details. - * If iter==NULL then a context-independent result is returned. - * @param context Pointer to be passed into iter. - * @param pString If the mapping result is a string, then the pointer is - * written to *pString. - * @param caseLocale Case locale value from ucase_getCaseLocale(). - * @return Output code point or string length, see UCASE_MAX_STRING_LENGTH. - * - * @see UCaseContextIterator - * @see UCASE_MAX_STRING_LENGTH - * @internal - */ -U_CAPI int32_t U_EXPORT2 -ucase_toFullLower(UChar32 c, - UCaseContextIterator *iter, void *context, - const UChar **pString, - int32_t caseLocale); - -U_CAPI int32_t U_EXPORT2 -ucase_toFullUpper(UChar32 c, - UCaseContextIterator *iter, void *context, - const UChar **pString, - int32_t caseLocale); - -U_CAPI int32_t U_EXPORT2 -ucase_toFullTitle(UChar32 c, - UCaseContextIterator *iter, void *context, - const UChar **pString, - int32_t caseLocale); - -U_CAPI int32_t U_EXPORT2 -ucase_toFullFolding(UChar32 c, - const UChar **pString, - uint32_t options); - -U_CFUNC int32_t U_EXPORT2 -ucase_hasBinaryProperty(UChar32 c, UProperty which); - - -U_CDECL_BEGIN - -/** - * @internal - */ -typedef int32_t U_CALLCONV -UCaseMapFull(UChar32 c, - UCaseContextIterator *iter, void *context, - const UChar **pString, - int32_t caseLocale); - -U_CDECL_END - -/* for icuexportdata -------------------------------------------------------- */ - -struct UCaseProps { - void *mem; // TODO: was unused, and type UDataMemory -- remove - const int32_t *indexes; - const uint16_t *exceptions; - const uint16_t *unfold; - - UTrie2 trie; - uint8_t formatVersion[4]; -}; - -U_CAPI const struct UCaseProps * U_EXPORT2 -ucase_getSingleton(int32_t *pExceptionsLength, int32_t *pUnfoldLength); - -/* file definitions --------------------------------------------------------- */ - -#define UCASE_DATA_NAME "ucase" -#define UCASE_DATA_TYPE "icu" - -/* format "cAsE" */ -#define UCASE_FMT_0 0x63 -#define UCASE_FMT_1 0x41 -#define UCASE_FMT_2 0x53 -#define UCASE_FMT_3 0x45 - -/* indexes into indexes[] */ -enum { - UCASE_IX_INDEX_TOP, - UCASE_IX_LENGTH, - UCASE_IX_TRIE_SIZE, - UCASE_IX_EXC_LENGTH, - UCASE_IX_UNFOLD_LENGTH, - - UCASE_IX_MAX_FULL_LENGTH=15, - UCASE_IX_TOP=16 -}; - -/* definitions for 16-bit case properties word ------------------------------ */ - -U_CFUNC const UTrie2 * U_EXPORT2 -ucase_getTrie(); - -/* 2-bit constants for types of cased characters */ -#define UCASE_TYPE_MASK 3 -enum { - UCASE_NONE, - UCASE_LOWER, - UCASE_UPPER, - UCASE_TITLE -}; - -#define UCASE_GET_TYPE(props) ((props)&UCASE_TYPE_MASK) -#define UCASE_GET_TYPE_AND_IGNORABLE(props) ((props)&7) - -#define UCASE_IS_UPPER_OR_TITLE(props) ((props)&2) - -#define UCASE_IGNORABLE 4 -#define UCASE_EXCEPTION 8 -#define UCASE_SENSITIVE 0x10 - -#define UCASE_HAS_EXCEPTION(props) ((props)&UCASE_EXCEPTION) - -#define UCASE_DOT_MASK 0x60 -enum { - UCASE_NO_DOT=0, /* normal characters with cc=0 */ - UCASE_SOFT_DOTTED=0x20, /* soft-dotted characters with cc=0 */ - UCASE_ABOVE=0x40, /* "above" accents with cc=230 */ - UCASE_OTHER_ACCENT=0x60 /* other accent character (0>UCASE_DELTA_SHIFT) -#else -# define UCASE_GET_DELTA(props) (int16_t)(((props)&0x8000) ? (((props)>>UCASE_DELTA_SHIFT)|0xfe00) : ((uint16_t)(props)>>UCASE_DELTA_SHIFT)) -#endif - -/* 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 ------------------------------ */ - -/* first 8 bits indicate values in optional slots */ -enum { - UCASE_EXC_LOWER, - UCASE_EXC_FOLD, - UCASE_EXC_UPPER, - UCASE_EXC_TITLE, - UCASE_EXC_DELTA, - UCASE_EXC_5, /* reserved */ - UCASE_EXC_CLOSURE, - UCASE_EXC_FULL_MAPPINGS, - UCASE_EXC_ALL_SLOTS /* one past the last slot */ -}; - -/* each slot is 2 uint16_t instead of 1 */ -#define UCASE_EXC_DOUBLE_SLOTS 0x100 - -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<=0. + * + * @return true if the string was found + */ +U_CFUNC UBool U_EXPORT2 +ucase_addStringCaseClosure(const UChar *s, int32_t length, const USetAdder *sa); + +#ifdef __cplusplus +U_NAMESPACE_BEGIN + +/** + * Iterator over characters with more than one code point in the full default Case_Folding. + */ +class U_COMMON_API FullCaseFoldingIterator { +public: + /** Constructor. */ + FullCaseFoldingIterator(); + /** + * Returns the next (cp, full) pair where "full" is cp's full default Case_Folding. + * Returns a negative cp value at the end of the iteration. + */ + UChar32 next(UnicodeString &full); +private: + FullCaseFoldingIterator(const FullCaseFoldingIterator &) = delete; // no copy + FullCaseFoldingIterator &operator=(const FullCaseFoldingIterator &) = delete; // no assignment + + const char16_t *unfold; + int32_t unfoldRows; + int32_t unfoldRowWidth; + int32_t unfoldStringWidth; + int32_t currentRow; + int32_t rowCpIndex; +}; + +/** + * Fast case mapping data for ASCII/Latin. + * Linear arrays of delta bytes: 0=no mapping; EXC=exception. + * Deltas must not cross the ASCII boundary, or else they cannot be easily used + * in simple UTF-8 code. + */ +namespace LatinCase { + +/** Case mapping/folding data for code points up to U+017F. */ +constexpr char16_t LIMIT = 0x180; +/** U+017F case-folds and uppercases crossing the ASCII boundary. */ +constexpr char16_t LONG_S = 0x17f; +/** Exception: Complex mapping, or too-large delta. */ +constexpr int8_t EXC = -0x80; + +/** Deltas for lowercasing for most locales, and default case folding. */ +extern const int8_t TO_LOWER_NORMAL[LIMIT]; +/** Deltas for lowercasing for tr/az/lt, and Turkic case folding. */ +extern const int8_t TO_LOWER_TR_LT[LIMIT]; + +/** Deltas for uppercasing for most locales. */ +extern const int8_t TO_UPPER_NORMAL[LIMIT]; +/** Deltas for uppercasing for tr/az. */ +extern const int8_t TO_UPPER_TR[LIMIT]; + +} // namespace LatinCase + +U_NAMESPACE_END +#endif + +/** @return UCASE_NONE, UCASE_LOWER, UCASE_UPPER, UCASE_TITLE */ +U_CAPI int32_t U_EXPORT2 +ucase_getType(UChar32 c); + +/** @return like ucase_getType() but also sets UCASE_IGNORABLE if c is case-ignorable */ +U_CAPI int32_t U_EXPORT2 +ucase_getTypeOrIgnorable(UChar32 c); + +U_CAPI UBool U_EXPORT2 +ucase_isSoftDotted(UChar32 c); + +U_CAPI UBool U_EXPORT2 +ucase_isCaseSensitive(UChar32 c); + +/* string case mapping functions */ + +U_CDECL_BEGIN + +/** + * Iterator function for string case mappings, which need to look at the + * context (surrounding text) of a given character for conditional mappings. + * + * The iterator only needs to go backward or forward away from the + * character in question. It does not use any indexes on this interface. + * It does not support random access or an arbitrary change of + * iteration direction. + * + * The code point being case-mapped itself is never returned by + * this iterator. + * + * @param context A pointer to the iterator's working data. + * @param dir If <0 then start iterating backward from the character; + * if >0 then start iterating forward from the character; + * if 0 then continue iterating in the current direction. + * @return Next code point, or <0 when the iteration is done. + */ +typedef UChar32 U_CALLCONV +UCaseContextIterator(void *context, int8_t dir); + +/** + * Sample struct which may be used by some implementations of + * UCaseContextIterator. + */ +struct UCaseContext { + void *p; + int32_t start, index, limit; + int32_t cpStart, cpLimit; + int8_t dir; + int8_t b1, b2, b3; +}; +typedef struct UCaseContext UCaseContext; + +U_CDECL_END + +#define UCASECONTEXT_INITIALIZER { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + +enum { + /** + * For string case mappings, a single character (a code point) is mapped + * either to itself (in which case in-place mapping functions do nothing), + * or to another single code point, or to a string. + * Aside from the string contents, these are indicated with a single int32_t + * value as follows: + * + * Mapping to self: Negative values (~self instead of -self to support U+0000) + * + * Mapping to another code point: Positive values >UCASE_MAX_STRING_LENGTH + * + * Mapping to a string: The string length (0..UCASE_MAX_STRING_LENGTH) is + * returned. Note that the string result may indeed have zero length. + */ + UCASE_MAX_STRING_LENGTH=0x1f +}; + +/** + * Get the full lowercase mapping for c. + * + * @param csp Case mapping properties. + * @param c Character to be mapped. + * @param iter Character iterator, used for context-sensitive mappings. + * See UCaseContextIterator for details. + * If iter==NULL then a context-independent result is returned. + * @param context Pointer to be passed into iter. + * @param pString If the mapping result is a string, then the pointer is + * written to *pString. + * @param caseLocale Case locale value from ucase_getCaseLocale(). + * @return Output code point or string length, see UCASE_MAX_STRING_LENGTH. + * + * @see UCaseContextIterator + * @see UCASE_MAX_STRING_LENGTH + * @internal + */ +U_CAPI int32_t U_EXPORT2 +ucase_toFullLower(UChar32 c, + UCaseContextIterator *iter, void *context, + const UChar **pString, + int32_t caseLocale); + +U_CAPI int32_t U_EXPORT2 +ucase_toFullUpper(UChar32 c, + UCaseContextIterator *iter, void *context, + const UChar **pString, + int32_t caseLocale); + +U_CAPI int32_t U_EXPORT2 +ucase_toFullTitle(UChar32 c, + UCaseContextIterator *iter, void *context, + const UChar **pString, + int32_t caseLocale); + +U_CAPI int32_t U_EXPORT2 +ucase_toFullFolding(UChar32 c, + const UChar **pString, + uint32_t options); + +U_CFUNC int32_t U_EXPORT2 +ucase_hasBinaryProperty(UChar32 c, UProperty which); + + +U_CDECL_BEGIN + +/** + * @internal + */ +typedef int32_t U_CALLCONV +UCaseMapFull(UChar32 c, + UCaseContextIterator *iter, void *context, + const UChar **pString, + int32_t caseLocale); + +U_CDECL_END + +/* for icuexportdata -------------------------------------------------------- */ + +struct UCaseProps { + void *mem; // TODO: was unused, and type UDataMemory -- remove + const int32_t *indexes; + const uint16_t *exceptions; + const uint16_t *unfold; + + UTrie2 trie; + uint8_t formatVersion[4]; +}; + +U_CAPI const struct UCaseProps * U_EXPORT2 +ucase_getSingleton(int32_t *pExceptionsLength, int32_t *pUnfoldLength); + +/* file definitions --------------------------------------------------------- */ + +#define UCASE_DATA_NAME "ucase" +#define UCASE_DATA_TYPE "icu" + +/* format "cAsE" */ +#define UCASE_FMT_0 0x63 +#define UCASE_FMT_1 0x41 +#define UCASE_FMT_2 0x53 +#define UCASE_FMT_3 0x45 + +/* indexes into indexes[] */ +enum { + UCASE_IX_INDEX_TOP, + UCASE_IX_LENGTH, + UCASE_IX_TRIE_SIZE, + UCASE_IX_EXC_LENGTH, + UCASE_IX_UNFOLD_LENGTH, + + UCASE_IX_MAX_FULL_LENGTH=15, + UCASE_IX_TOP=16 +}; + +/* definitions for 16-bit case properties word ------------------------------ */ + +U_CFUNC const UTrie2 * U_EXPORT2 +ucase_getTrie(); + +/* 2-bit constants for types of cased characters */ +#define UCASE_TYPE_MASK 3 +enum { + UCASE_NONE, + UCASE_LOWER, + UCASE_UPPER, + UCASE_TITLE +}; + +#define UCASE_GET_TYPE(props) ((props)&UCASE_TYPE_MASK) +#define UCASE_GET_TYPE_AND_IGNORABLE(props) ((props)&7) + +#define UCASE_IS_UPPER_OR_TITLE(props) ((props)&2) + +#define UCASE_IGNORABLE 4 +#define UCASE_EXCEPTION 8 +#define UCASE_SENSITIVE 0x10 + +#define UCASE_HAS_EXCEPTION(props) ((props)&UCASE_EXCEPTION) + +#define UCASE_DOT_MASK 0x60 +enum { + UCASE_NO_DOT=0, /* normal characters with cc=0 */ + UCASE_SOFT_DOTTED=0x20, /* soft-dotted characters with cc=0 */ + UCASE_ABOVE=0x40, /* "above" accents with cc=230 */ + UCASE_OTHER_ACCENT=0x60 /* other accent character (0>UCASE_DELTA_SHIFT) +#else +# define UCASE_GET_DELTA(props) (int16_t)(((props)&0x8000) ? (((props)>>UCASE_DELTA_SHIFT)|0xfe00) : ((uint16_t)(props)>>UCASE_DELTA_SHIFT)) +#endif + +/* 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 ------------------------------ */ + +/* first 8 bits indicate values in optional slots */ +enum { + UCASE_EXC_LOWER, + UCASE_EXC_FOLD, + UCASE_EXC_UPPER, + UCASE_EXC_TITLE, + UCASE_EXC_DELTA, + UCASE_EXC_5, /* reserved */ + UCASE_EXC_CLOSURE, + UCASE_EXC_FULL_MAPPINGS, + UCASE_EXC_ALL_SLOTS /* one past the last slot */ +}; + +/* each slot is 2 uint16_t instead of 1 */ +#define UCASE_EXC_DOUBLE_SLOTS 0x100 + +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<locale; -} - -U_CAPI uint32_t U_EXPORT2 -ucasemap_getOptions(const UCaseMap *csm) { - return csm->options; -} - -U_CAPI void U_EXPORT2 -ucasemap_setLocale(UCaseMap *csm, const char *locale, UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return; - } - if (locale != NULL && *locale == 0) { - csm->locale[0] = 0; - csm->caseLocale = UCASE_LOC_ROOT; - return; - } - - int32_t length=uloc_getName(locale, csm->locale, (int32_t)sizeof(csm->locale), pErrorCode); - if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR || length==sizeof(csm->locale)) { - *pErrorCode=U_ZERO_ERROR; - /* we only really need the language code for case mappings */ - length=uloc_getLanguage(locale, csm->locale, (int32_t)sizeof(csm->locale), pErrorCode); - } - if(length==sizeof(csm->locale)) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - if(U_SUCCESS(*pErrorCode)) { - csm->caseLocale = ucase_getCaseLocale(csm->locale); - } else { - csm->locale[0]=0; - csm->caseLocale = UCASE_LOC_ROOT; - } -} - -U_CAPI void U_EXPORT2 -ucasemap_setOptions(UCaseMap *csm, uint32_t options, UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return; - } - csm->options=options; -} - -/* UTF-8 string case mappings ----------------------------------------------- */ - -/* TODO(markus): Move to a new, separate utf8case.cpp file. */ - -namespace { - -/* append a full case mapping result, see UCASE_MAX_STRING_LENGTH */ -inline UBool -appendResult(int32_t cpLength, int32_t result, const UChar *s, - ByteSink &sink, uint32_t options, icu::Edits *edits, UErrorCode &errorCode) { - U_ASSERT(U_SUCCESS(errorCode)); - - /* decode the result */ - if(result<0) { - /* (not) original code point */ - if(edits!=NULL) { - edits->addUnchanged(cpLength); - } - if((options & U_OMIT_UNCHANGED_TEXT) == 0) { - ByteSinkUtil::appendCodePoint(cpLength, ~result, sink); - } - } else { - if(result<=UCASE_MAX_STRING_LENGTH) { - // string: "result" is the UTF-16 length - return ByteSinkUtil::appendChange(cpLength, s, result, sink, edits, errorCode); - } else { - ByteSinkUtil::appendCodePoint(cpLength, result, sink, edits); - } - } - return true; -} - -// See unicode/utf8.h U8_APPEND_UNSAFE(). -inline uint8_t getTwoByteLead(UChar32 c) { return (uint8_t)((c >> 6) | 0xc0); } -inline uint8_t getTwoByteTrail(UChar32 c) { return (uint8_t)((c & 0x3f) | 0x80); } - -UChar32 U_CALLCONV -utf8_caseContextIterator(void *context, int8_t dir) { - UCaseContext *csc=(UCaseContext *)context; - UChar32 c; - - if(dir<0) { - /* reset for backward iteration */ - csc->index=csc->cpStart; - csc->dir=dir; - } else if(dir>0) { - /* reset for forward iteration */ - csc->index=csc->cpLimit; - csc->dir=dir; - } else { - /* continue current iteration direction */ - dir=csc->dir; - } - - if(dir<0) { - if(csc->startindex) { - U8_PREV((const uint8_t *)csc->p, csc->start, csc->index, c); - return c; - } - } else { - if(csc->indexlimit) { - U8_NEXT((const uint8_t *)csc->p, csc->index, csc->limit, c); - return c; - } - } - return U_SENTINEL; -} - -/** - * caseLocale >= 0: Lowercases [srcStart..srcLimit[ but takes context [0..srcLength[ into account. - * caseLocale < 0: Case-folds [srcStart..srcLimit[. - */ -void toLower(int32_t caseLocale, uint32_t options, - const uint8_t *src, UCaseContext *csc, int32_t srcStart, int32_t srcLimit, - icu::ByteSink &sink, icu::Edits *edits, UErrorCode &errorCode) { - const int8_t *latinToLower; - if (caseLocale == UCASE_LOC_ROOT || - (caseLocale >= 0 ? - !(caseLocale == UCASE_LOC_TURKISH || caseLocale == UCASE_LOC_LITHUANIAN) : - (options & _FOLD_CASE_OPTIONS_MASK) == U_FOLD_CASE_DEFAULT)) { - latinToLower = LatinCase::TO_LOWER_NORMAL; - } else { - latinToLower = LatinCase::TO_LOWER_TR_LT; - } - const UTrie2 *trie = ucase_getTrie(); - int32_t prev = srcStart; - int32_t srcIndex = srcStart; - for (;;) { - // fast path for simple cases - int32_t cpStart; - UChar32 c; - for (;;) { - if (U_FAILURE(errorCode) || srcIndex >= srcLimit) { - c = U_SENTINEL; - break; - } - uint8_t lead = src[srcIndex++]; - if (lead <= 0x7f) { - int8_t d = latinToLower[lead]; - if (d == LatinCase::EXC) { - cpStart = srcIndex - 1; - c = lead; - break; - } - if (d == 0) { continue; } - ByteSinkUtil::appendUnchanged(src + prev, srcIndex - 1 - prev, - sink, options, edits, errorCode); - char ascii = (char)(lead + d); - sink.Append(&ascii, 1); - if (edits != nullptr) { - edits->addReplace(1, 1); - } - prev = srcIndex; - continue; - } else if (lead < 0xe3) { - uint8_t t; - if (0xc2 <= lead && lead <= 0xc5 && srcIndex < srcLimit && - (t = src[srcIndex] - 0x80) <= 0x3f) { - // U+0080..U+017F - ++srcIndex; - c = ((lead - 0xc0) << 6) | t; - int8_t d = latinToLower[c]; - if (d == LatinCase::EXC) { - cpStart = srcIndex - 2; - break; - } - if (d == 0) { continue; } - ByteSinkUtil::appendUnchanged(src + prev, srcIndex - 2 - prev, - sink, options, edits, errorCode); - ByteSinkUtil::appendTwoBytes(c + d, sink); - if (edits != nullptr) { - edits->addReplace(2, 2); - } - prev = srcIndex; - continue; - } - } else if ((lead <= 0xe9 || lead == 0xeb || lead == 0xec) && - (srcIndex + 2) <= srcLimit && - U8_IS_TRAIL(src[srcIndex]) && U8_IS_TRAIL(src[srcIndex + 1])) { - // most of CJK: no case mappings - srcIndex += 2; - continue; - } - cpStart = --srcIndex; - U8_NEXT(src, srcIndex, srcLimit, c); - if (c < 0) { - // ill-formed UTF-8 - continue; - } - uint16_t props = UTRIE2_GET16(trie, c); - if (UCASE_HAS_EXCEPTION(props)) { break; } - int32_t delta; - if (!UCASE_IS_UPPER_OR_TITLE(props) || (delta = UCASE_GET_DELTA(props)) == 0) { - continue; - } - ByteSinkUtil::appendUnchanged(src + prev, cpStart - prev, - sink, options, edits, errorCode); - ByteSinkUtil::appendCodePoint(srcIndex - cpStart, c + delta, sink, edits); - prev = srcIndex; - } - if (c < 0) { - break; - } - // slow path - const UChar *s; - if (caseLocale >= 0) { - csc->cpStart = cpStart; - csc->cpLimit = srcIndex; - c = ucase_toFullLower(c, utf8_caseContextIterator, csc, &s, caseLocale); - } else { - c = ucase_toFullFolding(c, &s, options); - } - if (c >= 0) { - ByteSinkUtil::appendUnchanged(src + prev, cpStart - prev, - sink, options, edits, errorCode); - appendResult(srcIndex - cpStart, c, s, sink, options, edits, errorCode); - prev = srcIndex; - } - } - ByteSinkUtil::appendUnchanged(src + prev, srcIndex - prev, - sink, options, edits, errorCode); -} - -void toUpper(int32_t caseLocale, uint32_t options, - const uint8_t *src, UCaseContext *csc, int32_t srcLength, - icu::ByteSink &sink, icu::Edits *edits, UErrorCode &errorCode) { - const int8_t *latinToUpper; - if (caseLocale == UCASE_LOC_TURKISH) { - latinToUpper = LatinCase::TO_UPPER_TR; - } else { - latinToUpper = LatinCase::TO_UPPER_NORMAL; - } - const UTrie2 *trie = ucase_getTrie(); - int32_t prev = 0; - int32_t srcIndex = 0; - for (;;) { - // fast path for simple cases - int32_t cpStart; - UChar32 c; - for (;;) { - if (U_FAILURE(errorCode) || srcIndex >= srcLength) { - c = U_SENTINEL; - break; - } - uint8_t lead = src[srcIndex++]; - if (lead <= 0x7f) { - int8_t d = latinToUpper[lead]; - if (d == LatinCase::EXC) { - cpStart = srcIndex - 1; - c = lead; - break; - } - if (d == 0) { continue; } - ByteSinkUtil::appendUnchanged(src + prev, srcIndex - 1 - prev, - sink, options, edits, errorCode); - char ascii = (char)(lead + d); - sink.Append(&ascii, 1); - if (edits != nullptr) { - edits->addReplace(1, 1); - } - prev = srcIndex; - continue; - } else if (lead < 0xe3) { - uint8_t t; - if (0xc2 <= lead && lead <= 0xc5 && srcIndex < srcLength && - (t = src[srcIndex] - 0x80) <= 0x3f) { - // U+0080..U+017F - ++srcIndex; - c = ((lead - 0xc0) << 6) | t; - int8_t d = latinToUpper[c]; - if (d == LatinCase::EXC) { - cpStart = srcIndex - 2; - break; - } - if (d == 0) { continue; } - ByteSinkUtil::appendUnchanged(src + prev, srcIndex - 2 - prev, - sink, options, edits, errorCode); - ByteSinkUtil::appendTwoBytes(c + d, sink); - if (edits != nullptr) { - edits->addReplace(2, 2); - } - prev = srcIndex; - continue; - } - } else if ((lead <= 0xe9 || lead == 0xeb || lead == 0xec) && - (srcIndex + 2) <= srcLength && - U8_IS_TRAIL(src[srcIndex]) && U8_IS_TRAIL(src[srcIndex + 1])) { - // most of CJK: no case mappings - srcIndex += 2; - continue; - } - cpStart = --srcIndex; - U8_NEXT(src, srcIndex, srcLength, c); - if (c < 0) { - // ill-formed UTF-8 - continue; - } - uint16_t props = UTRIE2_GET16(trie, c); - if (UCASE_HAS_EXCEPTION(props)) { break; } - int32_t delta; - if (UCASE_GET_TYPE(props) != UCASE_LOWER || (delta = UCASE_GET_DELTA(props)) == 0) { - continue; - } - ByteSinkUtil::appendUnchanged(src + prev, cpStart - prev, - sink, options, edits, errorCode); - ByteSinkUtil::appendCodePoint(srcIndex - cpStart, c + delta, sink, edits); - prev = srcIndex; - } - if (c < 0) { - break; - } - // slow path - csc->cpStart = cpStart; - csc->cpLimit = srcIndex; - const UChar *s; - c = ucase_toFullUpper(c, utf8_caseContextIterator, csc, &s, caseLocale); - if (c >= 0) { - ByteSinkUtil::appendUnchanged(src + prev, cpStart - prev, - sink, options, edits, errorCode); - appendResult(srcIndex - cpStart, c, s, sink, options, edits, errorCode); - prev = srcIndex; - } - } - ByteSinkUtil::appendUnchanged(src + prev, srcIndex - prev, - sink, options, edits, errorCode); -} - -} // namespace - -#if !UCONFIG_NO_BREAK_ITERATION - -namespace { - -constexpr uint8_t ACUTE_BYTE0 = u8"\u0301"[0]; - -constexpr uint8_t ACUTE_BYTE1 = u8"\u0301"[1]; - -/** - * Input: c is a letter I with or without acute accent. - * start is the index in src after c, and is less than segmentLimit. - * If a plain i/I is followed by a plain j/J, - * or an i/I with acute (precomposed or decomposed) is followed by a j/J with acute, - * then we output accordingly. - * - * @return the src index after the titlecased sequence, or the start index if no Dutch IJ - */ -int32_t maybeTitleDutchIJ(const uint8_t *src, UChar32 c, int32_t start, int32_t segmentLimit, - ByteSink &sink, uint32_t options, icu::Edits *edits, UErrorCode &errorCode) { - U_ASSERT(start < segmentLimit); - - int32_t index = start; - bool withAcute = false; - - // If the conditions are met, then the following variables tell us what to output. - int32_t unchanged1 = 0; // code units before the j, or the whole sequence (0..3) - bool doTitleJ = false; // true if the j needs to be titlecased - int32_t unchanged2 = 0; // after the j (0 or 1) - - // next character after the first letter - UChar32 c2; - c2 = src[index++]; - - // Is the first letter an i/I with accent? - if (c == u'I') { - if (c2 == ACUTE_BYTE0 && index < segmentLimit && src[index++] == ACUTE_BYTE1) { - withAcute = true; - unchanged1 = 2; // ACUTE is 2 code units in UTF-8 - if (index == segmentLimit) { return start; } - c2 = src[index++]; - } - } else { // Í - withAcute = true; - } - - // Is the next character a j/J? - if (c2 == u'j') { - doTitleJ = true; - } else if (c2 == u'J') { - ++unchanged1; - } else { - return start; - } - - // A plain i/I must be followed by a plain j/J. - // An i/I with acute must be followed by a j/J with acute. - if (withAcute) { - if ((index + 1) >= segmentLimit || src[index++] != ACUTE_BYTE0 || src[index++] != ACUTE_BYTE1) { - return start; - } - if (doTitleJ) { - unchanged2 = 2; // ACUTE is 2 code units in UTF-8 - } else { - unchanged1 = unchanged1 + 2; // ACUTE is 2 code units in UTF-8 - } - } - - // There must not be another combining mark. - if (index < segmentLimit) { - int32_t cp; - int32_t i = index; - U8_NEXT(src, i, segmentLimit, cp); - uint32_t typeMask = U_GET_GC_MASK(cp); - if ((typeMask & U_GC_M_MASK) != 0) { - return start; - } - } - - // Output the rest of the Dutch IJ. - ByteSinkUtil::appendUnchanged(src + start, unchanged1, sink, options, edits, errorCode); - start += unchanged1; - if (doTitleJ) { - ByteSinkUtil::appendCodePoint(1, u'J', sink, edits); - ++start; - } - ByteSinkUtil::appendUnchanged(src + start, unchanged2, sink, options, edits, errorCode); - - U_ASSERT(start + unchanged2 == index); - return index; -} - -} // namespace - -U_CFUNC void U_CALLCONV -ucasemap_internalUTF8ToTitle( - int32_t caseLocale, uint32_t options, BreakIterator *iter, - const uint8_t *src, int32_t srcLength, - ByteSink &sink, icu::Edits *edits, - UErrorCode &errorCode) { - if (!ustrcase_checkTitleAdjustmentOptions(options, errorCode)) { - return; - } - - /* set up local variables */ - UCaseContext csc=UCASECONTEXT_INITIALIZER; - csc.p=(void *)src; - csc.limit=srcLength; - int32_t prev=0; - UBool isFirstIndex=true; - - /* titlecasing loop */ - while(prevfirst(); - } else { - index=iter->next(); - } - if(index==UBRK_DONE || index>srcLength) { - index=srcLength; - } - - /* - * Segment [prev..index[ into 3 parts: - * a) skipped characters (copy as-is) [prev..titleStart[ - * b) first letter (titlecase) [titleStart..titleLimit[ - * c) subsequent characters (lowercase) [titleLimit..index[ - */ - if(prev=0) { - csc.cpStart=titleStart; - csc.cpLimit=titleLimit; - const UChar *s; - c=ucase_toFullTitle(c, utf8_caseContextIterator, &csc, &s, caseLocale); - if (!appendResult(titleLimit-titleStart, c, s, sink, options, edits, errorCode)) { - return; - } - } else { - // Malformed UTF-8. - if (!ByteSinkUtil::appendUnchanged(src+titleStart, titleLimit-titleStart, - sink, options, edits, errorCode)) { - return; - } - } - - /* Special case Dutch IJ titlecasing */ - if (titleLimit < index && - caseLocale == UCASE_LOC_DUTCH) { - if (c < 0) { - c = ~c; - } - - if (c == u'I' || c == u'Í') { - titleLimit = maybeTitleDutchIJ(src, c, titleLimit, index, sink, options, edits, errorCode); - } - } - - /* lowercase [titleLimit..index[ */ - if(titleLimit 0) { - uint32_t upper = data & UPPER_MASK; - // Add a dialytika to this iota or ypsilon vowel - // if we removed a tonos from the previous vowel, - // and that previous vowel did not also have (or gain) a dialytika. - // Adding one only to the final vowel in a longer sequence - // (which does not occur in normal writing) would require lookahead. - // Set the same flag as for preserving an existing dialytika. - if ((data & HAS_VOWEL) != 0 && (state & AFTER_VOWEL_WITH_ACCENT) != 0 && - (upper == 0x399 || upper == 0x3A5)) { - data |= HAS_DIALYTIKA; - } - int32_t numYpogegrammeni = 0; // Map each one to a trailing, spacing, capital iota. - if ((data & HAS_YPOGEGRAMMENI) != 0) { - numYpogegrammeni = 1; - } - // Skip combining diacritics after this Greek letter. - int32_t nextNextIndex = nextIndex; - while (nextIndex < srcLength) { - UChar32 c2; - U8_NEXT(src, nextNextIndex, srcLength, c2); - uint32_t diacriticData = getDiacriticData(c2); - if (diacriticData != 0) { - data |= diacriticData; - if ((diacriticData & HAS_YPOGEGRAMMENI) != 0) { - ++numYpogegrammeni; - } - nextIndex = nextNextIndex; - } else { - break; // not a Greek diacritic - } - } - if ((data & HAS_VOWEL_AND_ACCENT_AND_DIALYTIKA) == HAS_VOWEL_AND_ACCENT) { - nextState |= AFTER_VOWEL_WITH_ACCENT; - } - // Map according to Greek rules. - UBool addTonos = false; - if (upper == 0x397 && - (data & HAS_ACCENT) != 0 && - numYpogegrammeni == 0 && - (state & AFTER_CASED) == 0 && - !isFollowedByCasedLetter(src, nextIndex, srcLength)) { - // Keep disjunctive "or" with (only) a tonos. - // We use the same "word boundary" conditions as for the Final_Sigma test. - if (i == nextIndex) { - upper = 0x389; // Preserve the precomposed form. - } else { - addTonos = true; - } - } else if ((data & HAS_DIALYTIKA) != 0) { - // Preserve a vowel with dialytika in precomposed form if it exists. - if (upper == 0x399) { - upper = 0x3AA; - data &= ~HAS_EITHER_DIALYTIKA; - } else if (upper == 0x3A5) { - upper = 0x3AB; - data &= ~HAS_EITHER_DIALYTIKA; - } - } - - UBool change; - if (edits == nullptr && (options & U_OMIT_UNCHANGED_TEXT) == 0) { - change = true; // common, simple usage - } else { - // Find out first whether we are changing the text. - U_ASSERT(0x370 <= upper && upper <= 0x3ff); // 2-byte UTF-8, main Greek block - change = (i + 2) > nextIndex || - src[i] != getTwoByteLead(upper) || src[i + 1] != getTwoByteTrail(upper) || - numYpogegrammeni > 0; - int32_t i2 = i + 2; - if ((data & HAS_EITHER_DIALYTIKA) != 0) { - change |= (i2 + 2) > nextIndex || - src[i2] != (uint8_t)u8"\u0308"[0] || - src[i2 + 1] != (uint8_t)u8"\u0308"[1]; - i2 += 2; - } - if (addTonos) { - change |= (i2 + 2) > nextIndex || - src[i2] != (uint8_t)u8"\u0301"[0] || - src[i2 + 1] != (uint8_t)u8"\u0301"[1]; - i2 += 2; - } - int32_t oldLength = nextIndex - i; - int32_t newLength = (i2 - i) + numYpogegrammeni * 2; // 2 bytes per U+0399 - change |= oldLength != newLength; - if (change) { - if (edits != NULL) { - edits->addReplace(oldLength, newLength); - } - } else { - if (edits != NULL) { - edits->addUnchanged(oldLength); - } - // Write unchanged text? - change = (options & U_OMIT_UNCHANGED_TEXT) == 0; - } - } - - if (change) { - ByteSinkUtil::appendTwoBytes(upper, sink); - if ((data & HAS_EITHER_DIALYTIKA) != 0) { - sink.AppendU8(u8"\u0308", 2); // restore or add a dialytika - } - if (addTonos) { - sink.AppendU8(u8"\u0301", 2); - } - while (numYpogegrammeni > 0) { - sink.AppendU8(u8"\u0399", 2); - --numYpogegrammeni; - } - } - } else if(c>=0) { - const UChar *s; - c=ucase_toFullUpper(c, NULL, NULL, &s, UCASE_LOC_GREEK); - if (!appendResult(nextIndex - i, c, s, sink, options, edits, errorCode)) { - return; - } - } else { - // Malformed UTF-8. - if (!ByteSinkUtil::appendUnchanged(src+i, nextIndex-i, - sink, options, edits, errorCode)) { - return; - } - } - i = nextIndex; - state = nextState; - } -} - -} // namespace GreekUpper -U_NAMESPACE_END - -static void U_CALLCONV -ucasemap_internalUTF8ToLower(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED - const uint8_t *src, int32_t srcLength, - icu::ByteSink &sink, icu::Edits *edits, - UErrorCode &errorCode) { - UCaseContext csc=UCASECONTEXT_INITIALIZER; - csc.p=(void *)src; - csc.limit=srcLength; - toLower( - caseLocale, options, - src, &csc, 0, srcLength, - sink, edits, errorCode); -} - -static void U_CALLCONV -ucasemap_internalUTF8ToUpper(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED - const uint8_t *src, int32_t srcLength, - icu::ByteSink &sink, icu::Edits *edits, - UErrorCode &errorCode) { - if (caseLocale == UCASE_LOC_GREEK) { - GreekUpper::toUpper(options, src, srcLength, sink, edits, errorCode); - } else { - UCaseContext csc=UCASECONTEXT_INITIALIZER; - csc.p=(void *)src; - csc.limit=srcLength; - toUpper( - caseLocale, options, - src, &csc, srcLength, - sink, edits, errorCode); - } -} - -static void U_CALLCONV -ucasemap_internalUTF8Fold(int32_t /* caseLocale */, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED - const uint8_t *src, int32_t srcLength, - icu::ByteSink &sink, icu::Edits *edits, - UErrorCode &errorCode) { - toLower( - -1, options, - src, nullptr, 0, srcLength, - sink, edits, errorCode); -} - -void -ucasemap_mapUTF8(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM - const char *src, int32_t srcLength, - UTF8CaseMapper *stringCaseMapper, - icu::ByteSink &sink, icu::Edits *edits, - UErrorCode &errorCode) { - /* check argument values */ - if (U_FAILURE(errorCode)) { - return; - } - if ((src == nullptr && srcLength != 0) || srcLength < -1) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - // Get the string length. - if (srcLength == -1) { - srcLength = (int32_t)uprv_strlen((const char *)src); - } - - if (edits != nullptr && (options & U_EDITS_NO_RESET) == 0) { - edits->reset(); - } - stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR - (const uint8_t *)src, srcLength, sink, edits, errorCode); - sink.Flush(); - if (U_SUCCESS(errorCode)) { - if (edits != nullptr) { - edits->copyErrorTo(errorCode); - } - } -} - -int32_t -ucasemap_mapUTF8(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM - char *dest, int32_t destCapacity, - const char *src, int32_t srcLength, - UTF8CaseMapper *stringCaseMapper, - icu::Edits *edits, - UErrorCode &errorCode) { - /* check argument values */ - if(U_FAILURE(errorCode)) { - return 0; - } - if( destCapacity<0 || - (dest==NULL && destCapacity>0) || - (src==NULL && srcLength!=0) || srcLength<-1 - ) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* get the string length */ - if(srcLength==-1) { - srcLength=(int32_t)uprv_strlen((const char *)src); - } - - /* check for overlapping source and destination */ - if( dest!=NULL && - ((src>=dest && src<(dest+destCapacity)) || - (dest>=src && dest<(src+srcLength))) - ) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - CheckedArrayByteSink sink(dest, destCapacity); - if (edits != nullptr && (options & U_EDITS_NO_RESET) == 0) { - edits->reset(); - } - stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR - (const uint8_t *)src, srcLength, sink, edits, errorCode); - sink.Flush(); - if (U_SUCCESS(errorCode)) { - if (sink.Overflowed()) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - } else if (edits != nullptr) { - edits->copyErrorTo(errorCode); - } - } - return u_terminateChars(dest, destCapacity, sink.NumberOfBytesAppended(), &errorCode); -} - -/* public API functions */ - -U_CAPI int32_t U_EXPORT2 -ucasemap_utf8ToLower(const UCaseMap *csm, - char *dest, int32_t destCapacity, - const char *src, int32_t srcLength, - UErrorCode *pErrorCode) { - return ucasemap_mapUTF8( - csm->caseLocale, csm->options, UCASEMAP_BREAK_ITERATOR_NULL - dest, destCapacity, - src, srcLength, - ucasemap_internalUTF8ToLower, NULL, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -ucasemap_utf8ToUpper(const UCaseMap *csm, - char *dest, int32_t destCapacity, - const char *src, int32_t srcLength, - UErrorCode *pErrorCode) { - return ucasemap_mapUTF8( - csm->caseLocale, csm->options, UCASEMAP_BREAK_ITERATOR_NULL - dest, destCapacity, - src, srcLength, - ucasemap_internalUTF8ToUpper, NULL, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -ucasemap_utf8FoldCase(const UCaseMap *csm, - char *dest, int32_t destCapacity, - const char *src, int32_t srcLength, - UErrorCode *pErrorCode) { - return ucasemap_mapUTF8( - UCASE_LOC_ROOT, csm->options, UCASEMAP_BREAK_ITERATOR_NULL - dest, destCapacity, - src, srcLength, - ucasemap_internalUTF8Fold, NULL, *pErrorCode); -} - -U_NAMESPACE_BEGIN - -void CaseMap::utf8ToLower( - const char *locale, uint32_t options, - StringPiece src, ByteSink &sink, Edits *edits, - UErrorCode &errorCode) { - ucasemap_mapUTF8( - ustrcase_getCaseLocale(locale), options, UCASEMAP_BREAK_ITERATOR_NULL - src.data(), src.length(), - ucasemap_internalUTF8ToLower, sink, edits, errorCode); -} - -void CaseMap::utf8ToUpper( - const char *locale, uint32_t options, - StringPiece src, ByteSink &sink, Edits *edits, - UErrorCode &errorCode) { - ucasemap_mapUTF8( - ustrcase_getCaseLocale(locale), options, UCASEMAP_BREAK_ITERATOR_NULL - src.data(), src.length(), - ucasemap_internalUTF8ToUpper, sink, edits, errorCode); -} - -void CaseMap::utf8Fold( - uint32_t options, - StringPiece src, ByteSink &sink, Edits *edits, - UErrorCode &errorCode) { - ucasemap_mapUTF8( - UCASE_LOC_ROOT, options, UCASEMAP_BREAK_ITERATOR_NULL - src.data(), src.length(), - ucasemap_internalUTF8Fold, sink, edits, errorCode); -} - -int32_t CaseMap::utf8ToLower( - const char *locale, uint32_t options, - const char *src, int32_t srcLength, - char *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode) { - return ucasemap_mapUTF8( - ustrcase_getCaseLocale(locale), options, UCASEMAP_BREAK_ITERATOR_NULL - dest, destCapacity, - src, srcLength, - ucasemap_internalUTF8ToLower, edits, errorCode); -} - -int32_t CaseMap::utf8ToUpper( - const char *locale, uint32_t options, - const char *src, int32_t srcLength, - char *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode) { - return ucasemap_mapUTF8( - ustrcase_getCaseLocale(locale), options, UCASEMAP_BREAK_ITERATOR_NULL - dest, destCapacity, - src, srcLength, - ucasemap_internalUTF8ToUpper, edits, errorCode); -} - -int32_t CaseMap::utf8Fold( - uint32_t options, - const char *src, int32_t srcLength, - char *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode) { - return ucasemap_mapUTF8( - UCASE_LOC_ROOT, options, UCASEMAP_BREAK_ITERATOR_NULL - dest, destCapacity, - src, srcLength, - ucasemap_internalUTF8Fold, edits, errorCode); -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2005-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: ucasemap.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2005may06 +* created by: Markus W. Scherer +* +* Case mapping service object and functions using it. +*/ + +#include "unicode/utypes.h" +#include "unicode/brkiter.h" +#include "unicode/bytestream.h" +#include "unicode/casemap.h" +#include "unicode/edits.h" +#include "unicode/stringoptions.h" +#include "unicode/stringpiece.h" +#include "unicode/ubrk.h" +#include "unicode/uloc.h" +#include "unicode/ustring.h" +#include "unicode/ucasemap.h" +#if !UCONFIG_NO_BREAK_ITERATION +#include "unicode/utext.h" +#endif +#include "unicode/utf.h" +#include "unicode/utf8.h" +#include "unicode/utf16.h" +#include "bytesinkutil.h" +#include "cmemory.h" +#include "cstring.h" +#include "uassert.h" +#include "ucase.h" +#include "ucasemap_imp.h" +#include "ustr_imp.h" + +U_NAMESPACE_USE + +/* UCaseMap service object -------------------------------------------------- */ + +UCaseMap::UCaseMap(const char *localeID, uint32_t opts, UErrorCode *pErrorCode) : +#if !UCONFIG_NO_BREAK_ITERATION + iter(nullptr), +#endif + caseLocale(UCASE_LOC_UNKNOWN), options(opts) { + ucasemap_setLocale(this, localeID, pErrorCode); +} + +UCaseMap::~UCaseMap() { +#if !UCONFIG_NO_BREAK_ITERATION + delete iter; +#endif +} + +U_CAPI UCaseMap * U_EXPORT2 +ucasemap_open(const char *locale, uint32_t options, UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return nullptr; + } + UCaseMap *csm = new UCaseMap(locale, options, pErrorCode); + if(csm==nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } else if (U_FAILURE(*pErrorCode)) { + delete csm; + return nullptr; + } + return csm; +} + +U_CAPI void U_EXPORT2 +ucasemap_close(UCaseMap *csm) { + delete csm; +} + +U_CAPI const char * U_EXPORT2 +ucasemap_getLocale(const UCaseMap *csm) { + return csm->locale; +} + +U_CAPI uint32_t U_EXPORT2 +ucasemap_getOptions(const UCaseMap *csm) { + return csm->options; +} + +U_CAPI void U_EXPORT2 +ucasemap_setLocale(UCaseMap *csm, const char *locale, UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return; + } + if (locale != nullptr && *locale == 0) { + csm->locale[0] = 0; + csm->caseLocale = UCASE_LOC_ROOT; + return; + } + + int32_t length=uloc_getName(locale, csm->locale, (int32_t)sizeof(csm->locale), pErrorCode); + if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR || length==sizeof(csm->locale)) { + *pErrorCode=U_ZERO_ERROR; + /* we only really need the language code for case mappings */ + length=uloc_getLanguage(locale, csm->locale, (int32_t)sizeof(csm->locale), pErrorCode); + } + if(length==sizeof(csm->locale)) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + if(U_SUCCESS(*pErrorCode)) { + csm->caseLocale = ucase_getCaseLocale(csm->locale); + } else { + csm->locale[0]=0; + csm->caseLocale = UCASE_LOC_ROOT; + } +} + +U_CAPI void U_EXPORT2 +ucasemap_setOptions(UCaseMap *csm, uint32_t options, UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return; + } + csm->options=options; +} + +/* UTF-8 string case mappings ----------------------------------------------- */ + +/* TODO(markus): Move to a new, separate utf8case.cpp file. */ + +namespace { + +/* append a full case mapping result, see UCASE_MAX_STRING_LENGTH */ +inline UBool +appendResult(int32_t cpLength, int32_t result, const char16_t *s, + ByteSink &sink, uint32_t options, icu::Edits *edits, UErrorCode &errorCode) { + U_ASSERT(U_SUCCESS(errorCode)); + + /* decode the result */ + if(result<0) { + /* (not) original code point */ + if(edits!=nullptr) { + edits->addUnchanged(cpLength); + } + if((options & U_OMIT_UNCHANGED_TEXT) == 0) { + ByteSinkUtil::appendCodePoint(cpLength, ~result, sink); + } + } else { + if(result<=UCASE_MAX_STRING_LENGTH) { + // string: "result" is the UTF-16 length + return ByteSinkUtil::appendChange(cpLength, s, result, sink, edits, errorCode); + } else { + ByteSinkUtil::appendCodePoint(cpLength, result, sink, edits); + } + } + return true; +} + +// See unicode/utf8.h U8_APPEND_UNSAFE(). +inline uint8_t getTwoByteLead(UChar32 c) { return (uint8_t)((c >> 6) | 0xc0); } +inline uint8_t getTwoByteTrail(UChar32 c) { return (uint8_t)((c & 0x3f) | 0x80); } + +UChar32 U_CALLCONV +utf8_caseContextIterator(void *context, int8_t dir) { + UCaseContext *csc=(UCaseContext *)context; + UChar32 c; + + if(dir<0) { + /* reset for backward iteration */ + csc->index=csc->cpStart; + csc->dir=dir; + } else if(dir>0) { + /* reset for forward iteration */ + csc->index=csc->cpLimit; + csc->dir=dir; + } else { + /* continue current iteration direction */ + dir=csc->dir; + } + + if(dir<0) { + if(csc->startindex) { + U8_PREV((const uint8_t *)csc->p, csc->start, csc->index, c); + return c; + } + } else { + if(csc->indexlimit) { + U8_NEXT((const uint8_t *)csc->p, csc->index, csc->limit, c); + return c; + } + } + return U_SENTINEL; +} + +/** + * caseLocale >= 0: Lowercases [srcStart..srcLimit[ but takes context [0..srcLength[ into account. + * caseLocale < 0: Case-folds [srcStart..srcLimit[. + */ +void toLower(int32_t caseLocale, uint32_t options, + const uint8_t *src, UCaseContext *csc, int32_t srcStart, int32_t srcLimit, + icu::ByteSink &sink, icu::Edits *edits, UErrorCode &errorCode) { + const int8_t *latinToLower; + if (caseLocale == UCASE_LOC_ROOT || + (caseLocale >= 0 ? + !(caseLocale == UCASE_LOC_TURKISH || caseLocale == UCASE_LOC_LITHUANIAN) : + (options & _FOLD_CASE_OPTIONS_MASK) == U_FOLD_CASE_DEFAULT)) { + latinToLower = LatinCase::TO_LOWER_NORMAL; + } else { + latinToLower = LatinCase::TO_LOWER_TR_LT; + } + const UTrie2 *trie = ucase_getTrie(); + int32_t prev = srcStart; + int32_t srcIndex = srcStart; + for (;;) { + // fast path for simple cases + int32_t cpStart; + UChar32 c; + for (;;) { + if (U_FAILURE(errorCode) || srcIndex >= srcLimit) { + c = U_SENTINEL; + break; + } + uint8_t lead = src[srcIndex++]; + if (lead <= 0x7f) { + int8_t d = latinToLower[lead]; + if (d == LatinCase::EXC) { + cpStart = srcIndex - 1; + c = lead; + break; + } + if (d == 0) { continue; } + ByteSinkUtil::appendUnchanged(src + prev, srcIndex - 1 - prev, + sink, options, edits, errorCode); + char ascii = (char)(lead + d); + sink.Append(&ascii, 1); + if (edits != nullptr) { + edits->addReplace(1, 1); + } + prev = srcIndex; + continue; + } else if (lead < 0xe3) { + uint8_t t; + if (0xc2 <= lead && lead <= 0xc5 && srcIndex < srcLimit && + (t = src[srcIndex] - 0x80) <= 0x3f) { + // U+0080..U+017F + ++srcIndex; + c = ((lead - 0xc0) << 6) | t; + int8_t d = latinToLower[c]; + if (d == LatinCase::EXC) { + cpStart = srcIndex - 2; + break; + } + if (d == 0) { continue; } + ByteSinkUtil::appendUnchanged(src + prev, srcIndex - 2 - prev, + sink, options, edits, errorCode); + ByteSinkUtil::appendTwoBytes(c + d, sink); + if (edits != nullptr) { + edits->addReplace(2, 2); + } + prev = srcIndex; + continue; + } + } else if ((lead <= 0xe9 || lead == 0xeb || lead == 0xec) && + (srcIndex + 2) <= srcLimit && + U8_IS_TRAIL(src[srcIndex]) && U8_IS_TRAIL(src[srcIndex + 1])) { + // most of CJK: no case mappings + srcIndex += 2; + continue; + } + cpStart = --srcIndex; + U8_NEXT(src, srcIndex, srcLimit, c); + if (c < 0) { + // ill-formed UTF-8 + continue; + } + uint16_t props = UTRIE2_GET16(trie, c); + if (UCASE_HAS_EXCEPTION(props)) { break; } + int32_t delta; + if (!UCASE_IS_UPPER_OR_TITLE(props) || (delta = UCASE_GET_DELTA(props)) == 0) { + continue; + } + ByteSinkUtil::appendUnchanged(src + prev, cpStart - prev, + sink, options, edits, errorCode); + ByteSinkUtil::appendCodePoint(srcIndex - cpStart, c + delta, sink, edits); + prev = srcIndex; + } + if (c < 0) { + break; + } + // slow path + const char16_t *s; + if (caseLocale >= 0) { + csc->cpStart = cpStart; + csc->cpLimit = srcIndex; + c = ucase_toFullLower(c, utf8_caseContextIterator, csc, &s, caseLocale); + } else { + c = ucase_toFullFolding(c, &s, options); + } + if (c >= 0) { + ByteSinkUtil::appendUnchanged(src + prev, cpStart - prev, + sink, options, edits, errorCode); + appendResult(srcIndex - cpStart, c, s, sink, options, edits, errorCode); + prev = srcIndex; + } + } + ByteSinkUtil::appendUnchanged(src + prev, srcIndex - prev, + sink, options, edits, errorCode); +} + +void toUpper(int32_t caseLocale, uint32_t options, + const uint8_t *src, UCaseContext *csc, int32_t srcLength, + icu::ByteSink &sink, icu::Edits *edits, UErrorCode &errorCode) { + const int8_t *latinToUpper; + if (caseLocale == UCASE_LOC_TURKISH) { + latinToUpper = LatinCase::TO_UPPER_TR; + } else { + latinToUpper = LatinCase::TO_UPPER_NORMAL; + } + const UTrie2 *trie = ucase_getTrie(); + int32_t prev = 0; + int32_t srcIndex = 0; + for (;;) { + // fast path for simple cases + int32_t cpStart; + UChar32 c; + for (;;) { + if (U_FAILURE(errorCode) || srcIndex >= srcLength) { + c = U_SENTINEL; + break; + } + uint8_t lead = src[srcIndex++]; + if (lead <= 0x7f) { + int8_t d = latinToUpper[lead]; + if (d == LatinCase::EXC) { + cpStart = srcIndex - 1; + c = lead; + break; + } + if (d == 0) { continue; } + ByteSinkUtil::appendUnchanged(src + prev, srcIndex - 1 - prev, + sink, options, edits, errorCode); + char ascii = (char)(lead + d); + sink.Append(&ascii, 1); + if (edits != nullptr) { + edits->addReplace(1, 1); + } + prev = srcIndex; + continue; + } else if (lead < 0xe3) { + uint8_t t; + if (0xc2 <= lead && lead <= 0xc5 && srcIndex < srcLength && + (t = src[srcIndex] - 0x80) <= 0x3f) { + // U+0080..U+017F + ++srcIndex; + c = ((lead - 0xc0) << 6) | t; + int8_t d = latinToUpper[c]; + if (d == LatinCase::EXC) { + cpStart = srcIndex - 2; + break; + } + if (d == 0) { continue; } + ByteSinkUtil::appendUnchanged(src + prev, srcIndex - 2 - prev, + sink, options, edits, errorCode); + ByteSinkUtil::appendTwoBytes(c + d, sink); + if (edits != nullptr) { + edits->addReplace(2, 2); + } + prev = srcIndex; + continue; + } + } else if ((lead <= 0xe9 || lead == 0xeb || lead == 0xec) && + (srcIndex + 2) <= srcLength && + U8_IS_TRAIL(src[srcIndex]) && U8_IS_TRAIL(src[srcIndex + 1])) { + // most of CJK: no case mappings + srcIndex += 2; + continue; + } + cpStart = --srcIndex; + U8_NEXT(src, srcIndex, srcLength, c); + if (c < 0) { + // ill-formed UTF-8 + continue; + } + uint16_t props = UTRIE2_GET16(trie, c); + if (UCASE_HAS_EXCEPTION(props)) { break; } + int32_t delta; + if (UCASE_GET_TYPE(props) != UCASE_LOWER || (delta = UCASE_GET_DELTA(props)) == 0) { + continue; + } + ByteSinkUtil::appendUnchanged(src + prev, cpStart - prev, + sink, options, edits, errorCode); + ByteSinkUtil::appendCodePoint(srcIndex - cpStart, c + delta, sink, edits); + prev = srcIndex; + } + if (c < 0) { + break; + } + // slow path + csc->cpStart = cpStart; + csc->cpLimit = srcIndex; + const char16_t *s; + c = ucase_toFullUpper(c, utf8_caseContextIterator, csc, &s, caseLocale); + if (c >= 0) { + ByteSinkUtil::appendUnchanged(src + prev, cpStart - prev, + sink, options, edits, errorCode); + appendResult(srcIndex - cpStart, c, s, sink, options, edits, errorCode); + prev = srcIndex; + } + } + ByteSinkUtil::appendUnchanged(src + prev, srcIndex - prev, + sink, options, edits, errorCode); +} + +} // namespace + +#if !UCONFIG_NO_BREAK_ITERATION + +namespace { + +constexpr uint8_t ACUTE_BYTE0 = u8"\u0301"[0]; + +constexpr uint8_t ACUTE_BYTE1 = u8"\u0301"[1]; + +/** + * Input: c is a letter I with or without acute accent. + * start is the index in src after c, and is less than segmentLimit. + * If a plain i/I is followed by a plain j/J, + * or an i/I with acute (precomposed or decomposed) is followed by a j/J with acute, + * then we output accordingly. + * + * @return the src index after the titlecased sequence, or the start index if no Dutch IJ + */ +int32_t maybeTitleDutchIJ(const uint8_t *src, UChar32 c, int32_t start, int32_t segmentLimit, + ByteSink &sink, uint32_t options, icu::Edits *edits, UErrorCode &errorCode) { + U_ASSERT(start < segmentLimit); + + int32_t index = start; + bool withAcute = false; + + // If the conditions are met, then the following variables tell us what to output. + int32_t unchanged1 = 0; // code units before the j, or the whole sequence (0..3) + bool doTitleJ = false; // true if the j needs to be titlecased + int32_t unchanged2 = 0; // after the j (0 or 1) + + // next character after the first letter + UChar32 c2; + c2 = src[index++]; + + // Is the first letter an i/I with accent? + if (c == u'I') { + if (c2 == ACUTE_BYTE0 && index < segmentLimit && src[index++] == ACUTE_BYTE1) { + withAcute = true; + unchanged1 = 2; // ACUTE is 2 code units in UTF-8 + if (index == segmentLimit) { return start; } + c2 = src[index++]; + } + } else { // Í + withAcute = true; + } + + // Is the next character a j/J? + if (c2 == u'j') { + doTitleJ = true; + } else if (c2 == u'J') { + ++unchanged1; + } else { + return start; + } + + // A plain i/I must be followed by a plain j/J. + // An i/I with acute must be followed by a j/J with acute. + if (withAcute) { + if ((index + 1) >= segmentLimit || src[index++] != ACUTE_BYTE0 || src[index++] != ACUTE_BYTE1) { + return start; + } + if (doTitleJ) { + unchanged2 = 2; // ACUTE is 2 code units in UTF-8 + } else { + unchanged1 = unchanged1 + 2; // ACUTE is 2 code units in UTF-8 + } + } + + // There must not be another combining mark. + if (index < segmentLimit) { + int32_t cp; + int32_t i = index; + U8_NEXT(src, i, segmentLimit, cp); + uint32_t typeMask = U_GET_GC_MASK(cp); + if ((typeMask & U_GC_M_MASK) != 0) { + return start; + } + } + + // Output the rest of the Dutch IJ. + ByteSinkUtil::appendUnchanged(src + start, unchanged1, sink, options, edits, errorCode); + start += unchanged1; + if (doTitleJ) { + ByteSinkUtil::appendCodePoint(1, u'J', sink, edits); + ++start; + } + ByteSinkUtil::appendUnchanged(src + start, unchanged2, sink, options, edits, errorCode); + + U_ASSERT(start + unchanged2 == index); + return index; +} + +} // namespace + +U_CFUNC void U_CALLCONV +ucasemap_internalUTF8ToTitle( + int32_t caseLocale, uint32_t options, BreakIterator *iter, + const uint8_t *src, int32_t srcLength, + ByteSink &sink, icu::Edits *edits, + UErrorCode &errorCode) { + if (!ustrcase_checkTitleAdjustmentOptions(options, errorCode)) { + return; + } + + /* set up local variables */ + UCaseContext csc=UCASECONTEXT_INITIALIZER; + csc.p=(void *)src; + csc.limit=srcLength; + int32_t prev=0; + UBool isFirstIndex=true; + + /* titlecasing loop */ + while(prevfirst(); + } else { + index=iter->next(); + } + if(index==UBRK_DONE || index>srcLength) { + index=srcLength; + } + + /* + * Segment [prev..index[ into 3 parts: + * a) skipped characters (copy as-is) [prev..titleStart[ + * b) first letter (titlecase) [titleStart..titleLimit[ + * c) subsequent characters (lowercase) [titleLimit..index[ + */ + if(prev=0) { + csc.cpStart=titleStart; + csc.cpLimit=titleLimit; + const char16_t *s; + c=ucase_toFullTitle(c, utf8_caseContextIterator, &csc, &s, caseLocale); + if (!appendResult(titleLimit-titleStart, c, s, sink, options, edits, errorCode)) { + return; + } + } else { + // Malformed UTF-8. + if (!ByteSinkUtil::appendUnchanged(src+titleStart, titleLimit-titleStart, + sink, options, edits, errorCode)) { + return; + } + } + + /* Special case Dutch IJ titlecasing */ + if (titleLimit < index && + caseLocale == UCASE_LOC_DUTCH) { + if (c < 0) { + c = ~c; + } + + if (c == u'I' || c == u'Í') { + titleLimit = maybeTitleDutchIJ(src, c, titleLimit, index, sink, options, edits, errorCode); + } + } + + /* lowercase [titleLimit..index[ */ + if(titleLimit 0) { + uint32_t upper = data & UPPER_MASK; + // Add a dialytika to this iota or ypsilon vowel + // if we removed a tonos from the previous vowel, + // and that previous vowel did not also have (or gain) a dialytika. + // Adding one only to the final vowel in a longer sequence + // (which does not occur in normal writing) would require lookahead. + // Set the same flag as for preserving an existing dialytika. + if ((data & HAS_VOWEL) != 0 && (state & AFTER_VOWEL_WITH_ACCENT) != 0 && + (upper == 0x399 || upper == 0x3A5)) { + data |= HAS_DIALYTIKA; + } + int32_t numYpogegrammeni = 0; // Map each one to a trailing, spacing, capital iota. + if ((data & HAS_YPOGEGRAMMENI) != 0) { + numYpogegrammeni = 1; + } + // Skip combining diacritics after this Greek letter. + int32_t nextNextIndex = nextIndex; + while (nextIndex < srcLength) { + UChar32 c2; + U8_NEXT(src, nextNextIndex, srcLength, c2); + uint32_t diacriticData = getDiacriticData(c2); + if (diacriticData != 0) { + data |= diacriticData; + if ((diacriticData & HAS_YPOGEGRAMMENI) != 0) { + ++numYpogegrammeni; + } + nextIndex = nextNextIndex; + } else { + break; // not a Greek diacritic + } + } + if ((data & HAS_VOWEL_AND_ACCENT_AND_DIALYTIKA) == HAS_VOWEL_AND_ACCENT) { + nextState |= AFTER_VOWEL_WITH_ACCENT; + } + // Map according to Greek rules. + UBool addTonos = false; + if (upper == 0x397 && + (data & HAS_ACCENT) != 0 && + numYpogegrammeni == 0 && + (state & AFTER_CASED) == 0 && + !isFollowedByCasedLetter(src, nextIndex, srcLength)) { + // Keep disjunctive "or" with (only) a tonos. + // We use the same "word boundary" conditions as for the Final_Sigma test. + if (i == nextIndex) { + upper = 0x389; // Preserve the precomposed form. + } else { + addTonos = true; + } + } else if ((data & HAS_DIALYTIKA) != 0) { + // Preserve a vowel with dialytika in precomposed form if it exists. + if (upper == 0x399) { + upper = 0x3AA; + data &= ~HAS_EITHER_DIALYTIKA; + } else if (upper == 0x3A5) { + upper = 0x3AB; + data &= ~HAS_EITHER_DIALYTIKA; + } + } + + UBool change; + if (edits == nullptr && (options & U_OMIT_UNCHANGED_TEXT) == 0) { + change = true; // common, simple usage + } else { + // Find out first whether we are changing the text. + U_ASSERT(0x370 <= upper && upper <= 0x3ff); // 2-byte UTF-8, main Greek block + change = (i + 2) > nextIndex || + src[i] != getTwoByteLead(upper) || src[i + 1] != getTwoByteTrail(upper) || + numYpogegrammeni > 0; + int32_t i2 = i + 2; + if ((data & HAS_EITHER_DIALYTIKA) != 0) { + change |= (i2 + 2) > nextIndex || + src[i2] != (uint8_t)u8"\u0308"[0] || + src[i2 + 1] != (uint8_t)u8"\u0308"[1]; + i2 += 2; + } + if (addTonos) { + change |= (i2 + 2) > nextIndex || + src[i2] != (uint8_t)u8"\u0301"[0] || + src[i2 + 1] != (uint8_t)u8"\u0301"[1]; + i2 += 2; + } + int32_t oldLength = nextIndex - i; + int32_t newLength = (i2 - i) + numYpogegrammeni * 2; // 2 bytes per U+0399 + change |= oldLength != newLength; + if (change) { + if (edits != nullptr) { + edits->addReplace(oldLength, newLength); + } + } else { + if (edits != nullptr) { + edits->addUnchanged(oldLength); + } + // Write unchanged text? + change = (options & U_OMIT_UNCHANGED_TEXT) == 0; + } + } + + if (change) { + ByteSinkUtil::appendTwoBytes(upper, sink); + if ((data & HAS_EITHER_DIALYTIKA) != 0) { + sink.AppendU8(u8"\u0308", 2); // restore or add a dialytika + } + if (addTonos) { + sink.AppendU8(u8"\u0301", 2); + } + while (numYpogegrammeni > 0) { + sink.AppendU8(u8"\u0399", 2); + --numYpogegrammeni; + } + } + } else if(c>=0) { + const char16_t *s; + c=ucase_toFullUpper(c, nullptr, nullptr, &s, UCASE_LOC_GREEK); + if (!appendResult(nextIndex - i, c, s, sink, options, edits, errorCode)) { + return; + } + } else { + // Malformed UTF-8. + if (!ByteSinkUtil::appendUnchanged(src+i, nextIndex-i, + sink, options, edits, errorCode)) { + return; + } + } + i = nextIndex; + state = nextState; + } +} + +} // namespace GreekUpper +U_NAMESPACE_END + +static void U_CALLCONV +ucasemap_internalUTF8ToLower(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED + const uint8_t *src, int32_t srcLength, + icu::ByteSink &sink, icu::Edits *edits, + UErrorCode &errorCode) { + UCaseContext csc=UCASECONTEXT_INITIALIZER; + csc.p=(void *)src; + csc.limit=srcLength; + toLower( + caseLocale, options, + src, &csc, 0, srcLength, + sink, edits, errorCode); +} + +static void U_CALLCONV +ucasemap_internalUTF8ToUpper(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED + const uint8_t *src, int32_t srcLength, + icu::ByteSink &sink, icu::Edits *edits, + UErrorCode &errorCode) { + if (caseLocale == UCASE_LOC_GREEK) { + GreekUpper::toUpper(options, src, srcLength, sink, edits, errorCode); + } else { + UCaseContext csc=UCASECONTEXT_INITIALIZER; + csc.p=(void *)src; + csc.limit=srcLength; + toUpper( + caseLocale, options, + src, &csc, srcLength, + sink, edits, errorCode); + } +} + +static void U_CALLCONV +ucasemap_internalUTF8Fold(int32_t /* caseLocale */, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED + const uint8_t *src, int32_t srcLength, + icu::ByteSink &sink, icu::Edits *edits, + UErrorCode &errorCode) { + toLower( + -1, options, + src, nullptr, 0, srcLength, + sink, edits, errorCode); +} + +void +ucasemap_mapUTF8(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM + const char *src, int32_t srcLength, + UTF8CaseMapper *stringCaseMapper, + icu::ByteSink &sink, icu::Edits *edits, + UErrorCode &errorCode) { + /* check argument values */ + if (U_FAILURE(errorCode)) { + return; + } + if ((src == nullptr && srcLength != 0) || srcLength < -1) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + // Get the string length. + if (srcLength == -1) { + srcLength = (int32_t)uprv_strlen((const char *)src); + } + + if (edits != nullptr && (options & U_EDITS_NO_RESET) == 0) { + edits->reset(); + } + stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR + (const uint8_t *)src, srcLength, sink, edits, errorCode); + sink.Flush(); + if (U_SUCCESS(errorCode)) { + if (edits != nullptr) { + edits->copyErrorTo(errorCode); + } + } +} + +int32_t +ucasemap_mapUTF8(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM + char *dest, int32_t destCapacity, + const char *src, int32_t srcLength, + UTF8CaseMapper *stringCaseMapper, + icu::Edits *edits, + UErrorCode &errorCode) { + /* check argument values */ + if(U_FAILURE(errorCode)) { + return 0; + } + if( destCapacity<0 || + (dest==nullptr && destCapacity>0) || + (src==nullptr && srcLength!=0) || srcLength<-1 + ) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* get the string length */ + if(srcLength==-1) { + srcLength=(int32_t)uprv_strlen((const char *)src); + } + + /* check for overlapping source and destination */ + if( dest!=nullptr && + ((src>=dest && src<(dest+destCapacity)) || + (dest>=src && dest<(src+srcLength))) + ) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + CheckedArrayByteSink sink(dest, destCapacity); + if (edits != nullptr && (options & U_EDITS_NO_RESET) == 0) { + edits->reset(); + } + stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR + (const uint8_t *)src, srcLength, sink, edits, errorCode); + sink.Flush(); + if (U_SUCCESS(errorCode)) { + if (sink.Overflowed()) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + } else if (edits != nullptr) { + edits->copyErrorTo(errorCode); + } + } + return u_terminateChars(dest, destCapacity, sink.NumberOfBytesAppended(), &errorCode); +} + +/* public API functions */ + +U_CAPI int32_t U_EXPORT2 +ucasemap_utf8ToLower(const UCaseMap *csm, + char *dest, int32_t destCapacity, + const char *src, int32_t srcLength, + UErrorCode *pErrorCode) { + return ucasemap_mapUTF8( + csm->caseLocale, csm->options, UCASEMAP_BREAK_ITERATOR_NULL + dest, destCapacity, + src, srcLength, + ucasemap_internalUTF8ToLower, nullptr, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +ucasemap_utf8ToUpper(const UCaseMap *csm, + char *dest, int32_t destCapacity, + const char *src, int32_t srcLength, + UErrorCode *pErrorCode) { + return ucasemap_mapUTF8( + csm->caseLocale, csm->options, UCASEMAP_BREAK_ITERATOR_NULL + dest, destCapacity, + src, srcLength, + ucasemap_internalUTF8ToUpper, nullptr, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +ucasemap_utf8FoldCase(const UCaseMap *csm, + char *dest, int32_t destCapacity, + const char *src, int32_t srcLength, + UErrorCode *pErrorCode) { + return ucasemap_mapUTF8( + UCASE_LOC_ROOT, csm->options, UCASEMAP_BREAK_ITERATOR_NULL + dest, destCapacity, + src, srcLength, + ucasemap_internalUTF8Fold, nullptr, *pErrorCode); +} + +U_NAMESPACE_BEGIN + +void CaseMap::utf8ToLower( + const char *locale, uint32_t options, + StringPiece src, ByteSink &sink, Edits *edits, + UErrorCode &errorCode) { + ucasemap_mapUTF8( + ustrcase_getCaseLocale(locale), options, UCASEMAP_BREAK_ITERATOR_NULL + src.data(), src.length(), + ucasemap_internalUTF8ToLower, sink, edits, errorCode); +} + +void CaseMap::utf8ToUpper( + const char *locale, uint32_t options, + StringPiece src, ByteSink &sink, Edits *edits, + UErrorCode &errorCode) { + ucasemap_mapUTF8( + ustrcase_getCaseLocale(locale), options, UCASEMAP_BREAK_ITERATOR_NULL + src.data(), src.length(), + ucasemap_internalUTF8ToUpper, sink, edits, errorCode); +} + +void CaseMap::utf8Fold( + uint32_t options, + StringPiece src, ByteSink &sink, Edits *edits, + UErrorCode &errorCode) { + ucasemap_mapUTF8( + UCASE_LOC_ROOT, options, UCASEMAP_BREAK_ITERATOR_NULL + src.data(), src.length(), + ucasemap_internalUTF8Fold, sink, edits, errorCode); +} + +int32_t CaseMap::utf8ToLower( + const char *locale, uint32_t options, + const char *src, int32_t srcLength, + char *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode) { + return ucasemap_mapUTF8( + ustrcase_getCaseLocale(locale), options, UCASEMAP_BREAK_ITERATOR_NULL + dest, destCapacity, + src, srcLength, + ucasemap_internalUTF8ToLower, edits, errorCode); +} + +int32_t CaseMap::utf8ToUpper( + const char *locale, uint32_t options, + const char *src, int32_t srcLength, + char *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode) { + return ucasemap_mapUTF8( + ustrcase_getCaseLocale(locale), options, UCASEMAP_BREAK_ITERATOR_NULL + dest, destCapacity, + src, srcLength, + ucasemap_internalUTF8ToUpper, edits, errorCode); +} + +int32_t CaseMap::utf8Fold( + uint32_t options, + const char *src, int32_t srcLength, + char *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode) { + return ucasemap_mapUTF8( + UCASE_LOC_ROOT, options, UCASEMAP_BREAK_ITERATOR_NULL + dest, destCapacity, + src, srcLength, + ucasemap_internalUTF8Fold, edits, errorCode); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/ucasemap_imp.h b/deps/icu-small/source/common/ucasemap_imp.h index e17a0ae5a36b6d..6977fdf1f0e17c 100644 --- a/deps/icu-small/source/common/ucasemap_imp.h +++ b/deps/icu-small/source/common/ucasemap_imp.h @@ -1,282 +1,282 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// ucasemap_imp.h -// created: 2017feb08 Markus W. Scherer - -#ifndef __UCASEMAP_IMP_H__ -#define __UCASEMAP_IMP_H__ - -#include "unicode/utypes.h" -#include "unicode/ucasemap.h" -#include "unicode/uchar.h" -#include "ucase.h" - -/** - * Bit mask for the titlecasing iterator options bit field. - * Currently only 3 out of 8 values are used: - * 0 (words), U_TITLECASE_WHOLE_STRING, U_TITLECASE_SENTENCES. - * See stringoptions.h. - * @internal - */ -#define U_TITLECASE_ITERATOR_MASK 0xe0 - -/** - * Bit mask for the titlecasing index adjustment options bit set. - * Currently two bits are defined: - * U_TITLECASE_NO_BREAK_ADJUSTMENT, U_TITLECASE_ADJUST_TO_CASED. - * See stringoptions.h. - * @internal - */ -#define U_TITLECASE_ADJUSTMENT_MASK 0x600 - -/** - * Internal API, used by u_strcasecmp() etc. - * Compare strings case-insensitively, - * in code point order or code unit order. - */ -U_CFUNC int32_t -u_strcmpFold(const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - uint32_t options, - UErrorCode *pErrorCode); - -/** - * Internal API, used for detecting length of - * shared prefix case-insensitively. - * @param s1 input string 1 - * @param length1 length of string 1, or -1 (NULL terminated) - * @param s2 input string 2 - * @param length2 length of string 2, or -1 (NULL terminated) - * @param options compare options - * @param matchLen1 (output) length of partial prefix match in s1 - * @param matchLen2 (output) length of partial prefix match in s2 - * @param pErrorCode receives error status - */ -U_CAPI void -u_caseInsensitivePrefixMatch(const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - uint32_t options, - int32_t *matchLen1, int32_t *matchLen2, - UErrorCode *pErrorCode); - -#ifdef __cplusplus - -U_NAMESPACE_BEGIN - -class BreakIterator; // unicode/brkiter.h -class ByteSink; -class Locale; // unicode/locid.h - -/** Returns true if the options are valid. Otherwise false, and sets an error. */ -inline UBool ustrcase_checkTitleAdjustmentOptions(uint32_t options, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return false; } - if ((options & U_TITLECASE_ADJUSTMENT_MASK) == U_TITLECASE_ADJUSTMENT_MASK) { - // Both options together. - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - return true; -} - -inline UBool ustrcase_isLNS(UChar32 c) { - // Letter, number, symbol, - // or a private use code point because those are typically used as letters or numbers. - // Consider modifier letters only if they are cased. - const uint32_t LNS = (U_GC_L_MASK|U_GC_N_MASK|U_GC_S_MASK|U_GC_CO_MASK) & ~U_GC_LM_MASK; - int gc = u_charType(c); - return (U_MASK(gc) & LNS) != 0 || (gc == U_MODIFIER_LETTER && ucase_getType(c) != UCASE_NONE); -} - -#if !UCONFIG_NO_BREAK_ITERATION - -/** Returns nullptr if error. Pass in either locale or locID, not both. */ -U_CFUNC -BreakIterator *ustrcase_getTitleBreakIterator( - const Locale *locale, const char *locID, uint32_t options, BreakIterator *iter, - LocalPointer &ownedIter, UErrorCode &errorCode); - -#endif - -U_NAMESPACE_END - -#include "unicode/unistr.h" // for UStringCaseMapper - -/* - * Internal string casing functions implementing - * ustring.h/ustrcase.cpp and UnicodeString case mapping functions. - */ - -struct UCaseMap : public icu::UMemory { - /** Implements most of ucasemap_open(). */ - UCaseMap(const char *localeID, uint32_t opts, UErrorCode *pErrorCode); - ~UCaseMap(); - -#if !UCONFIG_NO_BREAK_ITERATION - icu::BreakIterator *iter; /* We adopt the iterator, so we own it. */ -#endif - char locale[32]; - int32_t caseLocale; - uint32_t options; -}; - -#if UCONFIG_NO_BREAK_ITERATION -# define UCASEMAP_BREAK_ITERATOR_PARAM -# define UCASEMAP_BREAK_ITERATOR_UNUSED -# define UCASEMAP_BREAK_ITERATOR -# define UCASEMAP_BREAK_ITERATOR_NULL -#else -# define UCASEMAP_BREAK_ITERATOR_PARAM icu::BreakIterator *iter, -# define UCASEMAP_BREAK_ITERATOR_UNUSED icu::BreakIterator *, -# define UCASEMAP_BREAK_ITERATOR iter, -# define UCASEMAP_BREAK_ITERATOR_NULL NULL, -#endif - -U_CFUNC int32_t -ustrcase_getCaseLocale(const char *locale); - -// TODO: swap src / dest if approved for new public api -/** Implements UStringCaseMapper. */ -U_CFUNC int32_t U_CALLCONV -ustrcase_internalToLower(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - icu::Edits *edits, - UErrorCode &errorCode); - -/** Implements UStringCaseMapper. */ -U_CFUNC int32_t U_CALLCONV -ustrcase_internalToUpper(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - icu::Edits *edits, - UErrorCode &errorCode); - -#if !UCONFIG_NO_BREAK_ITERATION - -/** Implements UStringCaseMapper. */ -U_CFUNC int32_t U_CALLCONV -ustrcase_internalToTitle(int32_t caseLocale, uint32_t options, - icu::BreakIterator *iter, - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - icu::Edits *edits, - UErrorCode &errorCode); - -#endif - -/** Implements UStringCaseMapper. */ -U_CFUNC int32_t U_CALLCONV -ustrcase_internalFold(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - icu::Edits *edits, - UErrorCode &errorCode); - -/** - * Common string case mapping implementation for ucasemap_toXyz() and UnicodeString::toXyz(). - * Implements argument checking. - */ -U_CFUNC int32_t -ustrcase_map(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - UStringCaseMapper *stringCaseMapper, - icu::Edits *edits, - UErrorCode &errorCode); - -/** - * Common string case mapping implementation for old-fashioned u_strToXyz() functions - * that allow the source string to overlap the destination buffer. - * Implements argument checking and internally works with an intermediate buffer if necessary. - */ -U_CFUNC int32_t -ustrcase_mapWithOverlap(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - UStringCaseMapper *stringCaseMapper, - UErrorCode &errorCode); - -/** - * UTF-8 string case mapping function type, used by ucasemap_mapUTF8(). - * UTF-8 version of UStringCaseMapper. - * All error checking must be done. - * The UCaseMap must be fully initialized, with locale and/or iter set as needed. - */ -typedef void U_CALLCONV -UTF8CaseMapper(int32_t caseLocale, uint32_t options, -#if !UCONFIG_NO_BREAK_ITERATION - icu::BreakIterator *iter, -#endif - const uint8_t *src, int32_t srcLength, - icu::ByteSink &sink, icu::Edits *edits, - UErrorCode &errorCode); - -#if !UCONFIG_NO_BREAK_ITERATION - -/** Implements UTF8CaseMapper. */ -U_CFUNC void U_CALLCONV -ucasemap_internalUTF8ToTitle(int32_t caseLocale, uint32_t options, - icu::BreakIterator *iter, - const uint8_t *src, int32_t srcLength, - icu::ByteSink &sink, icu::Edits *edits, - UErrorCode &errorCode); - -#endif - -void -ucasemap_mapUTF8(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM - const char *src, int32_t srcLength, - UTF8CaseMapper *stringCaseMapper, - icu::ByteSink &sink, icu::Edits *edits, - UErrorCode &errorCode); - -/** - * Implements argument checking and buffer handling - * for UTF-8 string case mapping as a common function. - */ -int32_t -ucasemap_mapUTF8(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM - char *dest, int32_t destCapacity, - const char *src, int32_t srcLength, - UTF8CaseMapper *stringCaseMapper, - icu::Edits *edits, - UErrorCode &errorCode); - -U_NAMESPACE_BEGIN -namespace GreekUpper { - -// Data bits. -static const uint32_t UPPER_MASK = 0x3ff; -static const uint32_t HAS_VOWEL = 0x1000; -static const uint32_t HAS_YPOGEGRAMMENI = 0x2000; -static const uint32_t HAS_ACCENT = 0x4000; -static const uint32_t HAS_DIALYTIKA = 0x8000; -// Further bits during data building and processing, not stored in the data map. -static const uint32_t HAS_COMBINING_DIALYTIKA = 0x10000; -static const uint32_t HAS_OTHER_GREEK_DIACRITIC = 0x20000; - -static const uint32_t HAS_VOWEL_AND_ACCENT = HAS_VOWEL | HAS_ACCENT; -static const uint32_t HAS_VOWEL_AND_ACCENT_AND_DIALYTIKA = - HAS_VOWEL_AND_ACCENT | HAS_DIALYTIKA; -static const uint32_t HAS_EITHER_DIALYTIKA = HAS_DIALYTIKA | HAS_COMBINING_DIALYTIKA; - -// State bits. -static const uint32_t AFTER_CASED = 1; -static const uint32_t AFTER_VOWEL_WITH_ACCENT = 2; - -uint32_t getLetterData(UChar32 c); - -/** - * Returns a non-zero value for each of the Greek combining diacritics - * listed in The Unicode Standard, version 8, chapter 7.2 Greek, - * plus some perispomeni look-alikes. - */ -uint32_t getDiacriticData(UChar32 c); - -} // namespace GreekUpper -U_NAMESPACE_END - -#endif // __cplusplus - -#endif // __UCASEMAP_IMP_H__ +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// ucasemap_imp.h +// created: 2017feb08 Markus W. Scherer + +#ifndef __UCASEMAP_IMP_H__ +#define __UCASEMAP_IMP_H__ + +#include "unicode/utypes.h" +#include "unicode/ucasemap.h" +#include "unicode/uchar.h" +#include "ucase.h" + +/** + * Bit mask for the titlecasing iterator options bit field. + * Currently only 3 out of 8 values are used: + * 0 (words), U_TITLECASE_WHOLE_STRING, U_TITLECASE_SENTENCES. + * See stringoptions.h. + * @internal + */ +#define U_TITLECASE_ITERATOR_MASK 0xe0 + +/** + * Bit mask for the titlecasing index adjustment options bit set. + * Currently two bits are defined: + * U_TITLECASE_NO_BREAK_ADJUSTMENT, U_TITLECASE_ADJUST_TO_CASED. + * See stringoptions.h. + * @internal + */ +#define U_TITLECASE_ADJUSTMENT_MASK 0x600 + +/** + * Internal API, used by u_strcasecmp() etc. + * Compare strings case-insensitively, + * in code point order or code unit order. + */ +U_CFUNC int32_t +u_strcmpFold(const UChar *s1, int32_t length1, + const UChar *s2, int32_t length2, + uint32_t options, + UErrorCode *pErrorCode); + +/** + * Internal API, used for detecting length of + * shared prefix case-insensitively. + * @param s1 input string 1 + * @param length1 length of string 1, or -1 (NULL terminated) + * @param s2 input string 2 + * @param length2 length of string 2, or -1 (NULL terminated) + * @param options compare options + * @param matchLen1 (output) length of partial prefix match in s1 + * @param matchLen2 (output) length of partial prefix match in s2 + * @param pErrorCode receives error status + */ +U_CAPI void +u_caseInsensitivePrefixMatch(const UChar *s1, int32_t length1, + const UChar *s2, int32_t length2, + uint32_t options, + int32_t *matchLen1, int32_t *matchLen2, + UErrorCode *pErrorCode); + +#ifdef __cplusplus + +U_NAMESPACE_BEGIN + +class BreakIterator; // unicode/brkiter.h +class ByteSink; +class Locale; // unicode/locid.h + +/** Returns true if the options are valid. Otherwise false, and sets an error. */ +inline UBool ustrcase_checkTitleAdjustmentOptions(uint32_t options, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return false; } + if ((options & U_TITLECASE_ADJUSTMENT_MASK) == U_TITLECASE_ADJUSTMENT_MASK) { + // Both options together. + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + return true; +} + +inline UBool ustrcase_isLNS(UChar32 c) { + // Letter, number, symbol, + // or a private use code point because those are typically used as letters or numbers. + // Consider modifier letters only if they are cased. + const uint32_t LNS = (U_GC_L_MASK|U_GC_N_MASK|U_GC_S_MASK|U_GC_CO_MASK) & ~U_GC_LM_MASK; + int gc = u_charType(c); + return (U_MASK(gc) & LNS) != 0 || (gc == U_MODIFIER_LETTER && ucase_getType(c) != UCASE_NONE); +} + +#if !UCONFIG_NO_BREAK_ITERATION + +/** Returns nullptr if error. Pass in either locale or locID, not both. */ +U_CFUNC +BreakIterator *ustrcase_getTitleBreakIterator( + const Locale *locale, const char *locID, uint32_t options, BreakIterator *iter, + LocalPointer &ownedIter, UErrorCode &errorCode); + +#endif + +U_NAMESPACE_END + +#include "unicode/unistr.h" // for UStringCaseMapper + +/* + * Internal string casing functions implementing + * ustring.h/ustrcase.cpp and UnicodeString case mapping functions. + */ + +struct UCaseMap : public icu::UMemory { + /** Implements most of ucasemap_open(). */ + UCaseMap(const char *localeID, uint32_t opts, UErrorCode *pErrorCode); + ~UCaseMap(); + +#if !UCONFIG_NO_BREAK_ITERATION + icu::BreakIterator *iter; /* We adopt the iterator, so we own it. */ +#endif + char locale[32]; + int32_t caseLocale; + uint32_t options; +}; + +#if UCONFIG_NO_BREAK_ITERATION +# define UCASEMAP_BREAK_ITERATOR_PARAM +# define UCASEMAP_BREAK_ITERATOR_UNUSED +# define UCASEMAP_BREAK_ITERATOR +# define UCASEMAP_BREAK_ITERATOR_NULL +#else +# define UCASEMAP_BREAK_ITERATOR_PARAM icu::BreakIterator *iter, +# define UCASEMAP_BREAK_ITERATOR_UNUSED icu::BreakIterator *, +# define UCASEMAP_BREAK_ITERATOR iter, +# define UCASEMAP_BREAK_ITERATOR_NULL NULL, +#endif + +U_CFUNC int32_t +ustrcase_getCaseLocale(const char *locale); + +// TODO: swap src / dest if approved for new public api +/** Implements UStringCaseMapper. */ +U_CFUNC int32_t U_CALLCONV +ustrcase_internalToLower(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + icu::Edits *edits, + UErrorCode &errorCode); + +/** Implements UStringCaseMapper. */ +U_CFUNC int32_t U_CALLCONV +ustrcase_internalToUpper(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + icu::Edits *edits, + UErrorCode &errorCode); + +#if !UCONFIG_NO_BREAK_ITERATION + +/** Implements UStringCaseMapper. */ +U_CFUNC int32_t U_CALLCONV +ustrcase_internalToTitle(int32_t caseLocale, uint32_t options, + icu::BreakIterator *iter, + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + icu::Edits *edits, + UErrorCode &errorCode); + +#endif + +/** Implements UStringCaseMapper. */ +U_CFUNC int32_t U_CALLCONV +ustrcase_internalFold(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + icu::Edits *edits, + UErrorCode &errorCode); + +/** + * Common string case mapping implementation for ucasemap_toXyz() and UnicodeString::toXyz(). + * Implements argument checking. + */ +U_CFUNC int32_t +ustrcase_map(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + UStringCaseMapper *stringCaseMapper, + icu::Edits *edits, + UErrorCode &errorCode); + +/** + * Common string case mapping implementation for old-fashioned u_strToXyz() functions + * that allow the source string to overlap the destination buffer. + * Implements argument checking and internally works with an intermediate buffer if necessary. + */ +U_CFUNC int32_t +ustrcase_mapWithOverlap(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + UStringCaseMapper *stringCaseMapper, + UErrorCode &errorCode); + +/** + * UTF-8 string case mapping function type, used by ucasemap_mapUTF8(). + * UTF-8 version of UStringCaseMapper. + * All error checking must be done. + * The UCaseMap must be fully initialized, with locale and/or iter set as needed. + */ +typedef void U_CALLCONV +UTF8CaseMapper(int32_t caseLocale, uint32_t options, +#if !UCONFIG_NO_BREAK_ITERATION + icu::BreakIterator *iter, +#endif + const uint8_t *src, int32_t srcLength, + icu::ByteSink &sink, icu::Edits *edits, + UErrorCode &errorCode); + +#if !UCONFIG_NO_BREAK_ITERATION + +/** Implements UTF8CaseMapper. */ +U_CFUNC void U_CALLCONV +ucasemap_internalUTF8ToTitle(int32_t caseLocale, uint32_t options, + icu::BreakIterator *iter, + const uint8_t *src, int32_t srcLength, + icu::ByteSink &sink, icu::Edits *edits, + UErrorCode &errorCode); + +#endif + +void +ucasemap_mapUTF8(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM + const char *src, int32_t srcLength, + UTF8CaseMapper *stringCaseMapper, + icu::ByteSink &sink, icu::Edits *edits, + UErrorCode &errorCode); + +/** + * Implements argument checking and buffer handling + * for UTF-8 string case mapping as a common function. + */ +int32_t +ucasemap_mapUTF8(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM + char *dest, int32_t destCapacity, + const char *src, int32_t srcLength, + UTF8CaseMapper *stringCaseMapper, + icu::Edits *edits, + UErrorCode &errorCode); + +U_NAMESPACE_BEGIN +namespace GreekUpper { + +// Data bits. +static const uint32_t UPPER_MASK = 0x3ff; +static const uint32_t HAS_VOWEL = 0x1000; +static const uint32_t HAS_YPOGEGRAMMENI = 0x2000; +static const uint32_t HAS_ACCENT = 0x4000; +static const uint32_t HAS_DIALYTIKA = 0x8000; +// Further bits during data building and processing, not stored in the data map. +static const uint32_t HAS_COMBINING_DIALYTIKA = 0x10000; +static const uint32_t HAS_OTHER_GREEK_DIACRITIC = 0x20000; + +static const uint32_t HAS_VOWEL_AND_ACCENT = HAS_VOWEL | HAS_ACCENT; +static const uint32_t HAS_VOWEL_AND_ACCENT_AND_DIALYTIKA = + HAS_VOWEL_AND_ACCENT | HAS_DIALYTIKA; +static const uint32_t HAS_EITHER_DIALYTIKA = HAS_DIALYTIKA | HAS_COMBINING_DIALYTIKA; + +// State bits. +static const uint32_t AFTER_CASED = 1; +static const uint32_t AFTER_VOWEL_WITH_ACCENT = 2; + +uint32_t getLetterData(UChar32 c); + +/** + * Returns a non-zero value for each of the Greek combining diacritics + * listed in The Unicode Standard, version 8, chapter 7.2 Greek, + * plus some perispomeni look-alikes. + */ +uint32_t getDiacriticData(UChar32 c); + +} // namespace GreekUpper +U_NAMESPACE_END + +#endif // __cplusplus + +#endif // __UCASEMAP_IMP_H__ diff --git a/deps/icu-small/source/common/ucasemap_titlecase_brkiter.cpp b/deps/icu-small/source/common/ucasemap_titlecase_brkiter.cpp index c21dfb7698a8ad..ee77745afb4ac7 100644 --- a/deps/icu-small/source/common/ucasemap_titlecase_brkiter.cpp +++ b/deps/icu-small/source/common/ucasemap_titlecase_brkiter.cpp @@ -1,134 +1,134 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: ucasemap_titlecase_brkiter.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2011jun02 -* created by: Markus W. Scherer -* -* Titlecasing functions that are based on BreakIterator -* were moved here to break dependency cycles among parts of the common library. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/brkiter.h" -#include "unicode/ubrk.h" -#include "unicode/casemap.h" -#include "unicode/ucasemap.h" -#include "cmemory.h" -#include "ucase.h" -#include "ucasemap_imp.h" - -U_NAMESPACE_BEGIN - -void CaseMap::utf8ToTitle( - const char *locale, uint32_t options, BreakIterator *iter, - StringPiece src, ByteSink &sink, Edits *edits, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { - return; - } - UText utext = UTEXT_INITIALIZER; - utext_openUTF8(&utext, src.data(), src.length(), &errorCode); - LocalPointer ownedIter; - iter = ustrcase_getTitleBreakIterator(nullptr, locale, options, iter, ownedIter, errorCode); - if (iter == nullptr) { - utext_close(&utext); - return; - } - iter->setText(&utext, errorCode); - ucasemap_mapUTF8( - ustrcase_getCaseLocale(locale), options, iter, - src.data(), src.length(), - ucasemap_internalUTF8ToTitle, sink, edits, errorCode); - utext_close(&utext); -} - -int32_t CaseMap::utf8ToTitle( - const char *locale, uint32_t options, BreakIterator *iter, - const char *src, int32_t srcLength, - char *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { - return 0; - } - UText utext=UTEXT_INITIALIZER; - utext_openUTF8(&utext, src, srcLength, &errorCode); - LocalPointer ownedIter; - iter = ustrcase_getTitleBreakIterator(nullptr, locale, options, iter, ownedIter, errorCode); - if(iter==NULL) { - utext_close(&utext); - return 0; - } - iter->setText(&utext, errorCode); - int32_t length=ucasemap_mapUTF8( - ustrcase_getCaseLocale(locale), options, iter, - dest, destCapacity, - src, srcLength, - ucasemap_internalUTF8ToTitle, edits, errorCode); - utext_close(&utext); - return length; -} - -U_NAMESPACE_END - -U_NAMESPACE_USE - -U_CAPI const UBreakIterator * U_EXPORT2 -ucasemap_getBreakIterator(const UCaseMap *csm) { - return reinterpret_cast(csm->iter); -} - -U_CAPI void U_EXPORT2 -ucasemap_setBreakIterator(UCaseMap *csm, UBreakIterator *iterToAdopt, UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return; - } - delete csm->iter; - csm->iter=reinterpret_cast(iterToAdopt); -} - -U_CAPI int32_t U_EXPORT2 -ucasemap_utf8ToTitle(UCaseMap *csm, - char *dest, int32_t destCapacity, - const char *src, int32_t srcLength, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return 0; - } - UText utext=UTEXT_INITIALIZER; - utext_openUTF8(&utext, (const char *)src, srcLength, pErrorCode); - if (U_FAILURE(*pErrorCode)) { - return 0; - } - if(csm->iter==NULL) { - LocalPointer ownedIter; - BreakIterator *iter = ustrcase_getTitleBreakIterator( - nullptr, csm->locale, csm->options, nullptr, ownedIter, *pErrorCode); - if (iter == nullptr) { - utext_close(&utext); - return 0; - } - csm->iter = ownedIter.orphan(); - } - csm->iter->setText(&utext, *pErrorCode); - int32_t length=ucasemap_mapUTF8( - csm->caseLocale, csm->options, csm->iter, - dest, destCapacity, - src, srcLength, - ucasemap_internalUTF8ToTitle, NULL, *pErrorCode); - utext_close(&utext); - return length; -} - -#endif // !UCONFIG_NO_BREAK_ITERATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: ucasemap_titlecase_brkiter.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2011jun02 +* created by: Markus W. Scherer +* +* Titlecasing functions that are based on BreakIterator +* were moved here to break dependency cycles among parts of the common library. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/brkiter.h" +#include "unicode/ubrk.h" +#include "unicode/casemap.h" +#include "unicode/ucasemap.h" +#include "cmemory.h" +#include "ucase.h" +#include "ucasemap_imp.h" + +U_NAMESPACE_BEGIN + +void CaseMap::utf8ToTitle( + const char *locale, uint32_t options, BreakIterator *iter, + StringPiece src, ByteSink &sink, Edits *edits, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { + return; + } + UText utext = UTEXT_INITIALIZER; + utext_openUTF8(&utext, src.data(), src.length(), &errorCode); + LocalPointer ownedIter; + iter = ustrcase_getTitleBreakIterator(nullptr, locale, options, iter, ownedIter, errorCode); + if (iter == nullptr) { + utext_close(&utext); + return; + } + iter->setText(&utext, errorCode); + ucasemap_mapUTF8( + ustrcase_getCaseLocale(locale), options, iter, + src.data(), src.length(), + ucasemap_internalUTF8ToTitle, sink, edits, errorCode); + utext_close(&utext); +} + +int32_t CaseMap::utf8ToTitle( + const char *locale, uint32_t options, BreakIterator *iter, + const char *src, int32_t srcLength, + char *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { + return 0; + } + UText utext=UTEXT_INITIALIZER; + utext_openUTF8(&utext, src, srcLength, &errorCode); + LocalPointer ownedIter; + iter = ustrcase_getTitleBreakIterator(nullptr, locale, options, iter, ownedIter, errorCode); + if(iter==nullptr) { + utext_close(&utext); + return 0; + } + iter->setText(&utext, errorCode); + int32_t length=ucasemap_mapUTF8( + ustrcase_getCaseLocale(locale), options, iter, + dest, destCapacity, + src, srcLength, + ucasemap_internalUTF8ToTitle, edits, errorCode); + utext_close(&utext); + return length; +} + +U_NAMESPACE_END + +U_NAMESPACE_USE + +U_CAPI const UBreakIterator * U_EXPORT2 +ucasemap_getBreakIterator(const UCaseMap *csm) { + return reinterpret_cast(csm->iter); +} + +U_CAPI void U_EXPORT2 +ucasemap_setBreakIterator(UCaseMap *csm, UBreakIterator *iterToAdopt, UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return; + } + delete csm->iter; + csm->iter=reinterpret_cast(iterToAdopt); +} + +U_CAPI int32_t U_EXPORT2 +ucasemap_utf8ToTitle(UCaseMap *csm, + char *dest, int32_t destCapacity, + const char *src, int32_t srcLength, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return 0; + } + UText utext=UTEXT_INITIALIZER; + utext_openUTF8(&utext, (const char *)src, srcLength, pErrorCode); + if (U_FAILURE(*pErrorCode)) { + return 0; + } + if(csm->iter==nullptr) { + LocalPointer ownedIter; + BreakIterator *iter = ustrcase_getTitleBreakIterator( + nullptr, csm->locale, csm->options, nullptr, ownedIter, *pErrorCode); + if (iter == nullptr) { + utext_close(&utext); + return 0; + } + csm->iter = ownedIter.orphan(); + } + csm->iter->setText(&utext, *pErrorCode); + int32_t length=ucasemap_mapUTF8( + csm->caseLocale, csm->options, csm->iter, + dest, destCapacity, + src, srcLength, + ucasemap_internalUTF8ToTitle, nullptr, *pErrorCode); + utext_close(&utext); + return length; +} + +#endif // !UCONFIG_NO_BREAK_ITERATION diff --git a/deps/icu-small/source/common/ucat.cpp b/deps/icu-small/source/common/ucat.cpp index dac56eeb5ce482..37c6e184eea6e8 100644 --- a/deps/icu-small/source/common/ucat.cpp +++ b/deps/icu-small/source/common/ucat.cpp @@ -1,78 +1,78 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2003, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: March 19 2003 -* Since: ICU 2.6 -********************************************************************** -*/ -#include "unicode/ucat.h" -#include "unicode/ustring.h" -#include "cstring.h" -#include "uassert.h" - -/* Separator between set_num and msg_num */ -static const char SEPARATOR = '%'; - -/* Maximum length of a set_num/msg_num key, incl. terminating zero. - * Longest possible key is "-2147483648%-2147483648" */ -#define MAX_KEY_LEN (24) - -/** - * Fill in buffer with a set_num/msg_num key string, given the numeric - * values. Numeric values must be >= 0. Buffer must be of length - * MAX_KEY_LEN or more. - */ -static char* -_catkey(char* buffer, int32_t set_num, int32_t msg_num) { - int32_t i = 0; - i = T_CString_integerToString(buffer, set_num, 10); - buffer[i++] = SEPARATOR; - T_CString_integerToString(buffer+i, msg_num, 10); - return buffer; -} - -U_CAPI u_nl_catd U_EXPORT2 -u_catopen(const char* name, const char* locale, UErrorCode* ec) { - return (u_nl_catd) ures_open(name, locale, ec); -} - -U_CAPI void U_EXPORT2 -u_catclose(u_nl_catd catd) { - ures_close((UResourceBundle*) catd); /* may be NULL */ -} - -U_CAPI const UChar* U_EXPORT2 -u_catgets(u_nl_catd catd, int32_t set_num, int32_t msg_num, - const UChar* s, - int32_t* len, UErrorCode* ec) { - - char key[MAX_KEY_LEN]; - const UChar* result; - - if (ec == NULL || U_FAILURE(*ec)) { - goto ERROR; - } - - result = ures_getStringByKey((const UResourceBundle*) catd, - _catkey(key, set_num, msg_num), - len, ec); - if (U_FAILURE(*ec)) { - goto ERROR; - } - - return result; - - ERROR: - /* In case of any failure, return s */ - if (len != NULL) { - *len = u_strlen(s); - } - return s; -} - -/*eof*/ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2003, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: March 19 2003 +* Since: ICU 2.6 +********************************************************************** +*/ +#include "unicode/ucat.h" +#include "unicode/ustring.h" +#include "cstring.h" +#include "uassert.h" + +/* Separator between set_num and msg_num */ +static const char SEPARATOR = '%'; + +/* Maximum length of a set_num/msg_num key, incl. terminating zero. + * Longest possible key is "-2147483648%-2147483648" */ +#define MAX_KEY_LEN (24) + +/** + * Fill in buffer with a set_num/msg_num key string, given the numeric + * values. Numeric values must be >= 0. Buffer must be of length + * MAX_KEY_LEN or more. + */ +static char* +_catkey(char* buffer, int32_t set_num, int32_t msg_num) { + int32_t i = 0; + i = T_CString_integerToString(buffer, set_num, 10); + buffer[i++] = SEPARATOR; + T_CString_integerToString(buffer+i, msg_num, 10); + return buffer; +} + +U_CAPI u_nl_catd U_EXPORT2 +u_catopen(const char* name, const char* locale, UErrorCode* ec) { + return (u_nl_catd) ures_open(name, locale, ec); +} + +U_CAPI void U_EXPORT2 +u_catclose(u_nl_catd catd) { + ures_close((UResourceBundle*) catd); /* may be nullptr */ +} + +U_CAPI const char16_t* U_EXPORT2 +u_catgets(u_nl_catd catd, int32_t set_num, int32_t msg_num, + const char16_t* s, + int32_t* len, UErrorCode* ec) { + + char key[MAX_KEY_LEN]; + const char16_t* result; + + if (ec == nullptr || U_FAILURE(*ec)) { + goto ERROR; + } + + result = ures_getStringByKey((const UResourceBundle*) catd, + _catkey(key, set_num, msg_num), + len, ec); + if (U_FAILURE(*ec)) { + goto ERROR; + } + + return result; + + ERROR: + /* In case of any failure, return s */ + if (len != nullptr) { + *len = u_strlen(s); + } + return s; +} + +/*eof*/ diff --git a/deps/icu-small/source/common/uchar.cpp b/deps/icu-small/source/common/uchar.cpp index 7789a3b88a6c59..9dfb8dd01ab65f 100644 --- a/deps/icu-small/source/common/uchar.cpp +++ b/deps/icu-small/source/common/uchar.cpp @@ -1,732 +1,708 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************** -* Copyright (C) 1996-2016, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************** -* -* File UCHAR.C -* -* Modification History: -* -* Date Name Description -* 04/02/97 aliu Creation. -* 4/15/99 Madhu Updated all the function definitions for C Implementation -* 5/20/99 Madhu Added the function u_getVersion() -* 8/19/1999 srl Upgraded scripts to Unicode3.0 -* 11/11/1999 weiv added u_isalnum(), cleaned comments -* 01/11/2000 helena Renamed u_getVersion to u_getUnicodeVersion. -* 06/20/2000 helena OS/400 port changes; mostly typecast. -****************************************************************************** -*/ - -#include "unicode/utypes.h" -#include "unicode/uchar.h" -#include "unicode/uscript.h" -#include "unicode/udata.h" -#include "uassert.h" -#include "cmemory.h" -#include "ucln_cmn.h" -#include "utrie2.h" -#include "udataswp.h" -#include "uprops.h" -#include "ustr_imp.h" - -/* uchar_props_data.h is machine-generated by genprops --csource */ -#define INCLUDED_FROM_UCHAR_C -#include "uchar_props_data.h" - -/* constants and macros for access to the data ------------------------------ */ - -/* getting a uint32_t properties word from the data */ -#define GET_PROPS(c, result) ((result)=UTRIE2_GET16(&propsTrie, c)) - -/* API functions ------------------------------------------------------------ */ - -/* Gets the Unicode character's general category.*/ -U_CAPI int8_t U_EXPORT2 -u_charType(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (int8_t)GET_CATEGORY(props); -} - -/* Enumerate all code points with their general categories. */ -struct _EnumTypeCallback { - UCharEnumTypeRange *enumRange; - const void *context; -}; - -static uint32_t U_CALLCONV -_enumTypeValue(const void *context, uint32_t value) { - (void)context; - return GET_CATEGORY(value); -} - -static UBool U_CALLCONV -_enumTypeRange(const void *context, UChar32 start, UChar32 end, uint32_t value) { - /* just cast the value to UCharCategory */ - return ((struct _EnumTypeCallback *)context)-> - enumRange(((struct _EnumTypeCallback *)context)->context, - start, end+1, (UCharCategory)value); -} - -U_CAPI void U_EXPORT2 -u_enumCharTypes(UCharEnumTypeRange *enumRange, const void *context) { - struct _EnumTypeCallback callback; - - if(enumRange==NULL) { - return; - } - - callback.enumRange=enumRange; - callback.context=context; - utrie2_enum(&propsTrie, _enumTypeValue, _enumTypeRange, &callback); -} - -/* Checks if ch is a lower case letter.*/ -U_CAPI UBool U_EXPORT2 -u_islower(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)(GET_CATEGORY(props)==U_LOWERCASE_LETTER); -} - -/* Checks if ch is an upper case letter.*/ -U_CAPI UBool U_EXPORT2 -u_isupper(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)(GET_CATEGORY(props)==U_UPPERCASE_LETTER); -} - -/* Checks if ch is a title case letter; usually upper case letters.*/ -U_CAPI UBool U_EXPORT2 -u_istitle(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)(GET_CATEGORY(props)==U_TITLECASE_LETTER); -} - -/* Checks if ch is a decimal digit. */ -U_CAPI UBool U_EXPORT2 -u_isdigit(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)(GET_CATEGORY(props)==U_DECIMAL_DIGIT_NUMBER); -} - -U_CAPI UBool U_EXPORT2 -u_isxdigit(UChar32 c) { - uint32_t props; - - /* check ASCII and Fullwidth ASCII a-fA-F */ - if( - (c<=0x66 && c>=0x41 && (c<=0x46 || c>=0x61)) || - (c>=0xff21 && c<=0xff46 && (c<=0xff26 || c>=0xff41)) - ) { - return true; - } - - GET_PROPS(c, props); - return (UBool)(GET_CATEGORY(props)==U_DECIMAL_DIGIT_NUMBER); -} - -/* Checks if the Unicode character is a letter.*/ -U_CAPI UBool U_EXPORT2 -u_isalpha(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)((CAT_MASK(props)&U_GC_L_MASK)!=0); -} - -U_CAPI UBool U_EXPORT2 -u_isUAlphabetic(UChar32 c) { - return (u_getUnicodeProperties(c, 1)&U_MASK(UPROPS_ALPHABETIC))!=0; -} - -/* Checks if c is a letter or a decimal digit */ -U_CAPI UBool U_EXPORT2 -u_isalnum(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)((CAT_MASK(props)&(U_GC_L_MASK|U_GC_ND_MASK))!=0); -} - -/** - * Checks if c is alphabetic, or a decimal digit; implements UCHAR_POSIX_ALNUM. - * @internal - */ -U_CFUNC UBool -u_isalnumPOSIX(UChar32 c) { - return (UBool)(u_isUAlphabetic(c) || u_isdigit(c)); -} - -/* Checks if ch is a unicode character with assigned character type.*/ -U_CAPI UBool U_EXPORT2 -u_isdefined(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)(GET_CATEGORY(props)!=0); -} - -/* Checks if the Unicode character is a base form character that can take a diacritic.*/ -U_CAPI UBool U_EXPORT2 -u_isbase(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)((CAT_MASK(props)&(U_GC_L_MASK|U_GC_N_MASK|U_GC_MC_MASK|U_GC_ME_MASK))!=0); -} - -/* Checks if the Unicode character is a control character.*/ -U_CAPI UBool U_EXPORT2 -u_iscntrl(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)((CAT_MASK(props)&(U_GC_CC_MASK|U_GC_CF_MASK|U_GC_ZL_MASK|U_GC_ZP_MASK))!=0); -} - -U_CAPI UBool U_EXPORT2 -u_isISOControl(UChar32 c) { - return (uint32_t)c<=0x9f && (c<=0x1f || c>=0x7f); -} - -/* Some control characters that are used as space. */ -#define IS_THAT_CONTROL_SPACE(c) \ - (c<=0x9f && ((c>=TAB && c<=CR) || (c>=0x1c && c <=0x1f) || c==0x85)) - -/* Java has decided that U+0085 New Line is not whitespace any more. */ -#define IS_THAT_ASCII_CONTROL_SPACE(c) \ - (c<=0x1f && c>=TAB && (c<=CR || c>=0x1c)) - -/* Checks if the Unicode character is a space character.*/ -U_CAPI UBool U_EXPORT2 -u_isspace(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)((CAT_MASK(props)&U_GC_Z_MASK)!=0 || IS_THAT_CONTROL_SPACE(c)); -} - -U_CAPI UBool U_EXPORT2 -u_isJavaSpaceChar(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)((CAT_MASK(props)&U_GC_Z_MASK)!=0); -} - -/* Checks if the Unicode character is a whitespace character.*/ -U_CAPI UBool U_EXPORT2 -u_isWhitespace(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)( - ((CAT_MASK(props)&U_GC_Z_MASK)!=0 && - c!=NBSP && c!=FIGURESP && c!=NNBSP) || /* exclude no-break spaces */ - IS_THAT_ASCII_CONTROL_SPACE(c) - ); -} - -U_CAPI UBool U_EXPORT2 -u_isblank(UChar32 c) { - if((uint32_t)c<=0x9f) { - return c==9 || c==0x20; /* TAB or SPACE */ - } else { - /* Zs */ - uint32_t props; - GET_PROPS(c, props); - return (UBool)(GET_CATEGORY(props)==U_SPACE_SEPARATOR); - } -} - -U_CAPI UBool U_EXPORT2 -u_isUWhiteSpace(UChar32 c) { - return (u_getUnicodeProperties(c, 1)&U_MASK(UPROPS_WHITE_SPACE))!=0; -} - -/* Checks if the Unicode character is printable.*/ -U_CAPI UBool U_EXPORT2 -u_isprint(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - /* comparing ==0 returns false for the categories mentioned */ - return (UBool)((CAT_MASK(props)&U_GC_C_MASK)==0); -} - -/** - * Checks if c is in \p{graph}\p{blank} - \p{cntrl}. - * Implements UCHAR_POSIX_PRINT. - * @internal - */ -U_CFUNC UBool -u_isprintPOSIX(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - /* - * The only cntrl character in graph+blank is TAB (in blank). - * Here we implement (blank-TAB)=Zs instead of calling u_isblank(). - */ - return (UBool)((GET_CATEGORY(props)==U_SPACE_SEPARATOR) || u_isgraphPOSIX(c)); -} - -U_CAPI UBool U_EXPORT2 -u_isgraph(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - /* comparing ==0 returns false for the categories mentioned */ - return (UBool)((CAT_MASK(props)& - (U_GC_CC_MASK|U_GC_CF_MASK|U_GC_CS_MASK|U_GC_CN_MASK|U_GC_Z_MASK)) - ==0); -} - -/** - * Checks if c is in - * [^\p{space}\p{gc=Control}\p{gc=Surrogate}\p{gc=Unassigned}] - * with space=\p{Whitespace} and Control=Cc. - * Implements UCHAR_POSIX_GRAPH. - * @internal - */ -U_CFUNC UBool -u_isgraphPOSIX(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - /* \p{space}\p{gc=Control} == \p{gc=Z}\p{Control} */ - /* comparing ==0 returns false for the categories mentioned */ - return (UBool)((CAT_MASK(props)& - (U_GC_CC_MASK|U_GC_CS_MASK|U_GC_CN_MASK|U_GC_Z_MASK)) - ==0); -} - -U_CAPI UBool U_EXPORT2 -u_ispunct(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)((CAT_MASK(props)&U_GC_P_MASK)!=0); -} - -/* Checks if the Unicode character can start a Unicode identifier.*/ -U_CAPI UBool U_EXPORT2 -u_isIDStart(UChar32 c) { - /* same as u_isalpha() */ - uint32_t props; - GET_PROPS(c, props); - return (UBool)((CAT_MASK(props)&(U_GC_L_MASK|U_GC_NL_MASK))!=0); -} - -/* Checks if the Unicode character can be a Unicode identifier part other than starting the - identifier.*/ -U_CAPI UBool U_EXPORT2 -u_isIDPart(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)( - (CAT_MASK(props)& - (U_GC_ND_MASK|U_GC_NL_MASK| - U_GC_L_MASK| - U_GC_PC_MASK|U_GC_MC_MASK|U_GC_MN_MASK) - )!=0 || - u_isIDIgnorable(c)); -} - -/*Checks if the Unicode character can be ignorable in a Java or Unicode identifier.*/ -U_CAPI UBool U_EXPORT2 -u_isIDIgnorable(UChar32 c) { - if(c<=0x9f) { - return u_isISOControl(c) && !IS_THAT_ASCII_CONTROL_SPACE(c); - } else { - uint32_t props; - GET_PROPS(c, props); - return (UBool)(GET_CATEGORY(props)==U_FORMAT_CHAR); - } -} - -/*Checks if the Unicode character can start a Java identifier.*/ -U_CAPI UBool U_EXPORT2 -u_isJavaIDStart(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)((CAT_MASK(props)&(U_GC_L_MASK|U_GC_SC_MASK|U_GC_PC_MASK))!=0); -} - -/*Checks if the Unicode character can be a Java identifier part other than starting the - * identifier. - */ -U_CAPI UBool U_EXPORT2 -u_isJavaIDPart(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return (UBool)( - (CAT_MASK(props)& - (U_GC_ND_MASK|U_GC_NL_MASK| - U_GC_L_MASK| - U_GC_SC_MASK|U_GC_PC_MASK| - U_GC_MC_MASK|U_GC_MN_MASK) - )!=0 || - u_isIDIgnorable(c)); -} - -U_CAPI int32_t U_EXPORT2 -u_charDigitValue(UChar32 c) { - uint32_t props; - int32_t value; - GET_PROPS(c, props); - value=(int32_t)GET_NUMERIC_TYPE_VALUE(props)-UPROPS_NTV_DECIMAL_START; - if(value<=9) { - return value; - } else { - return -1; - } -} - -U_CAPI double U_EXPORT2 -u_getNumericValue(UChar32 c) { - uint32_t props; - int32_t ntv; - GET_PROPS(c, props); - ntv=(int32_t)GET_NUMERIC_TYPE_VALUE(props); - - if(ntv==UPROPS_NTV_NONE) { - return U_NO_NUMERIC_VALUE; - } else if(ntv>4)-12; - int32_t denominator=(ntv&0xf)+1; - return (double)numerator/denominator; - } else if(ntv>5)-14; - int32_t exp=(ntv&0x1f)+2; - numValue=mant; - - /* multiply by 10^exp without math.h */ - while(exp>=4) { - numValue*=10000.; - exp-=4; - } - switch(exp) { - case 3: - numValue*=1000.; - break; - case 2: - numValue*=100.; - break; - case 1: - numValue*=10.; - break; - case 0: - default: - break; - } - - return numValue; - } else if(ntv>2)-0xbf; - int32_t exp=(ntv&3)+1; - - switch(exp) { - case 4: - numValue*=60*60*60*60; - break; - case 3: - numValue*=60*60*60; - break; - case 2: - numValue*=60*60; - break; - case 1: - numValue*=60; - break; - case 0: - default: - break; - } - - return numValue; - } else if(ntv>2); - return (double)numerator/denominator; - } else if(ntv>2); - return (double)numerator/denominator; - } else { - /* reserved */ - return U_NO_NUMERIC_VALUE; - } -} - -U_CAPI int32_t U_EXPORT2 -u_digit(UChar32 ch, int8_t radix) { - int8_t value; - if((uint8_t)(radix-2)<=(36-2)) { - value=(int8_t)u_charDigitValue(ch); - if(value<0) { - /* ch is not a decimal digit, try latin letters */ - if(ch>=0x61 && ch<=0x7A) { - value=(int8_t)(ch-0x57); /* ch - 'a' + 10 */ - } else if(ch>=0x41 && ch<=0x5A) { - value=(int8_t)(ch-0x37); /* ch - 'A' + 10 */ - } else if(ch>=0xFF41 && ch<=0xFF5A) { - value=(int8_t)(ch-0xFF37); /* fullwidth ASCII a-z */ - } else if(ch>=0xFF21 && ch<=0xFF3A) { - value=(int8_t)(ch-0xFF17); /* fullwidth ASCII A-Z */ - } - } - } else { - value=-1; /* invalid radix */ - } - return (int8_t)((value(36-2) || (uint32_t)digit>=(uint32_t)radix) { - return 0; - } else if(digit<10) { - return (UChar32)(0x30+digit); - } else { - return (UChar32)((0x61-10)+digit); - } -} - -/* miscellaneous, and support for uprops.cpp -------------------------------- */ - -U_CAPI void U_EXPORT2 -u_getUnicodeVersion(UVersionInfo versionArray) { - if(versionArray!=NULL) { - uprv_memcpy(versionArray, dataVersion, U_MAX_VERSION_LENGTH); - } -} - -U_CFUNC uint32_t -u_getMainProperties(UChar32 c) { - uint32_t props; - GET_PROPS(c, props); - return props; -} - -U_CFUNC uint32_t -u_getUnicodeProperties(UChar32 c, int32_t column) { - U_ASSERT(column>=0); - if(column>=propsVectorsColumns) { - return 0; - } else { - uint16_t vecIndex=UTRIE2_GET16(&propsVectorsTrie, c); - return propsVectors[vecIndex+column]; - } -} - -U_CFUNC int32_t -uprv_getMaxValues(int32_t column) { - switch(column) { - case 0: - return indexes[UPROPS_MAX_VALUES_INDEX]; - case 2: - return indexes[UPROPS_MAX_VALUES_2_INDEX]; - default: - return 0; - } -} - -U_CAPI void U_EXPORT2 -u_charAge(UChar32 c, UVersionInfo versionArray) { - if(versionArray!=NULL) { - uint32_t version=u_getUnicodeProperties(c, 0)>>UPROPS_AGE_SHIFT; - versionArray[0]=(uint8_t)(version>>4); - versionArray[1]=(uint8_t)(version&0xf); - versionArray[2]=versionArray[3]=0; - } -} - -U_CAPI UScriptCode U_EXPORT2 -uscript_getScript(UChar32 c, UErrorCode *pErrorCode) { - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return USCRIPT_INVALID_CODE; - } - if((uint32_t)c>0x10ffff) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return USCRIPT_INVALID_CODE; - } - uint32_t scriptX=u_getUnicodeProperties(c, 0)&UPROPS_SCRIPT_X_MASK; - uint32_t codeOrIndex=uprops_mergeScriptCodeOrIndex(scriptX); - if(scriptX=UPROPS_SCRIPT_X_WITH_OTHER) { - scx=scriptExtensions+scx[1]; - } - uint32_t sc32=sc; - if(sc32>0x7fff) { - /* Guard against bogus input that would make us go past the Script_Extensions terminator. */ - return false; - } - while(sc32>*scx) { - ++scx; - } - return sc32==(*scx&0x7fff); -} - -U_CAPI int32_t U_EXPORT2 -uscript_getScriptExtensions(UChar32 c, - UScriptCode *scripts, int32_t capacity, - UErrorCode *pErrorCode) { - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(capacity<0 || (capacity>0 && scripts==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - uint32_t scriptX=u_getUnicodeProperties(c, 0)&UPROPS_SCRIPT_X_MASK; - uint32_t codeOrIndex=uprops_mergeScriptCodeOrIndex(scriptX); - if(scriptX=UPROPS_SCRIPT_X_WITH_OTHER) { - scx=scriptExtensions+scx[1]; - } - int32_t length=0; - uint16_t sx; - do { - sx=*scx++; - if(lengthcapacity) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - return length; -} - -U_CAPI UBlockCode U_EXPORT2 -ublock_getCode(UChar32 c) { - return (UBlockCode)((u_getUnicodeProperties(c, 0)&UPROPS_BLOCK_MASK)>>UPROPS_BLOCK_SHIFT); -} - -/* property starts for UnicodeSet ------------------------------------------- */ - -static UBool U_CALLCONV -_enumPropertyStartsRange(const void *context, UChar32 start, UChar32 end, uint32_t value) { - /* add the start code point to the USet */ - const USetAdder *sa=(const USetAdder *)context; - sa->add(sa->set, start); - (void)end; - (void)value; - return true; -} - -#define USET_ADD_CP_AND_NEXT(sa, cp) sa->add(sa->set, cp); sa->add(sa->set, cp+1) - -U_CFUNC void U_EXPORT2 -uchar_addPropertyStarts(const USetAdder *sa, UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return; - } - - /* add the start code point of each same-value range of the main trie */ - utrie2_enum(&propsTrie, NULL, _enumPropertyStartsRange, sa); - - /* add code points with hardcoded properties, plus the ones following them */ - - /* add for u_isblank() */ - USET_ADD_CP_AND_NEXT(sa, TAB); - - /* add for IS_THAT_CONTROL_SPACE() */ - sa->add(sa->set, CR+1); /* range TAB..CR */ - sa->add(sa->set, 0x1c); - sa->add(sa->set, 0x1f+1); - USET_ADD_CP_AND_NEXT(sa, 0x85); // NEXT LINE (NEL) - - /* add for u_isIDIgnorable() what was not added above */ - sa->add(sa->set, 0x7f); /* range DEL..NBSP-1, NBSP added below */ - sa->add(sa->set, HAIRSP); - sa->add(sa->set, RLM+1); - sa->add(sa->set, 0x206a); // INHIBIT SYMMETRIC SWAPPING - sa->add(sa->set, 0x206f+1); // NOMINAL DIGIT SHAPES - USET_ADD_CP_AND_NEXT(sa, ZWNBSP); - - /* add no-break spaces for u_isWhitespace() what was not added above */ - USET_ADD_CP_AND_NEXT(sa, NBSP); - USET_ADD_CP_AND_NEXT(sa, FIGURESP); - USET_ADD_CP_AND_NEXT(sa, NNBSP); - - /* add for u_digit() */ - sa->add(sa->set, u'a'); - sa->add(sa->set, u'z'+1); - sa->add(sa->set, u'A'); - sa->add(sa->set, u'Z'+1); - // fullwidth - sa->add(sa->set, u'a'); - sa->add(sa->set, u'z'+1); - sa->add(sa->set, u'A'); - sa->add(sa->set, u'Z'+1); - - /* add for u_isxdigit() */ - sa->add(sa->set, u'f'+1); - sa->add(sa->set, u'F'+1); - // fullwidth - sa->add(sa->set, u'f'+1); - sa->add(sa->set, u'F'+1); - - /* add for UCHAR_DEFAULT_IGNORABLE_CODE_POINT what was not added above */ - sa->add(sa->set, 0x2060); /* range 2060..206f */ - sa->add(sa->set, 0xfff0); - sa->add(sa->set, 0xfffb+1); - sa->add(sa->set, 0xe0000); - sa->add(sa->set, 0xe0fff+1); - - /* add for UCHAR_GRAPHEME_BASE and others */ - USET_ADD_CP_AND_NEXT(sa, CGJ); -} - -U_CFUNC void U_EXPORT2 -upropsvec_addPropertyStarts(const USetAdder *sa, UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return; - } - - /* add the start code point of each same-value range of the properties vectors trie */ - utrie2_enum(&propsVectorsTrie, NULL, _enumPropertyStartsRange, sa); -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************** +* Copyright (C) 1996-2016, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************** +* +* File UCHAR.C +* +* Modification History: +* +* Date Name Description +* 04/02/97 aliu Creation. +* 4/15/99 Madhu Updated all the function definitions for C Implementation +* 5/20/99 Madhu Added the function u_getVersion() +* 8/19/1999 srl Upgraded scripts to Unicode3.0 +* 11/11/1999 weiv added u_isalnum(), cleaned comments +* 01/11/2000 helena Renamed u_getVersion to u_getUnicodeVersion. +* 06/20/2000 helena OS/400 port changes; mostly typecast. +****************************************************************************** +*/ + +#include "unicode/utypes.h" +#include "unicode/uchar.h" +#include "unicode/uscript.h" +#include "unicode/udata.h" +#include "uassert.h" +#include "cmemory.h" +#include "ucln_cmn.h" +#include "utrie2.h" +#include "udataswp.h" +#include "uprops.h" +#include "ustr_imp.h" + +/* uchar_props_data.h is machine-generated by genprops --csource */ +#define INCLUDED_FROM_UCHAR_C +#include "uchar_props_data.h" + +/* constants and macros for access to the data ------------------------------ */ + +/* getting a uint32_t properties word from the data */ +#define GET_PROPS(c, result) ((result)=UTRIE2_GET16(&propsTrie, c)) + +/* API functions ------------------------------------------------------------ */ + +/* Gets the Unicode character's general category.*/ +U_CAPI int8_t U_EXPORT2 +u_charType(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (int8_t)GET_CATEGORY(props); +} + +/* Enumerate all code points with their general categories. */ +struct _EnumTypeCallback { + UCharEnumTypeRange *enumRange; + const void *context; +}; + +static uint32_t U_CALLCONV +_enumTypeValue(const void *context, uint32_t value) { + (void)context; + return GET_CATEGORY(value); +} + +static UBool U_CALLCONV +_enumTypeRange(const void *context, UChar32 start, UChar32 end, uint32_t value) { + /* just cast the value to UCharCategory */ + return ((struct _EnumTypeCallback *)context)-> + enumRange(((struct _EnumTypeCallback *)context)->context, + start, end+1, (UCharCategory)value); +} + +U_CAPI void U_EXPORT2 +u_enumCharTypes(UCharEnumTypeRange *enumRange, const void *context) { + struct _EnumTypeCallback callback; + + if(enumRange==nullptr) { + return; + } + + callback.enumRange=enumRange; + callback.context=context; + utrie2_enum(&propsTrie, _enumTypeValue, _enumTypeRange, &callback); +} + +/* Checks if ch is a lower case letter.*/ +U_CAPI UBool U_EXPORT2 +u_islower(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)(GET_CATEGORY(props)==U_LOWERCASE_LETTER); +} + +/* Checks if ch is an upper case letter.*/ +U_CAPI UBool U_EXPORT2 +u_isupper(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)(GET_CATEGORY(props)==U_UPPERCASE_LETTER); +} + +/* Checks if ch is a title case letter; usually upper case letters.*/ +U_CAPI UBool U_EXPORT2 +u_istitle(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)(GET_CATEGORY(props)==U_TITLECASE_LETTER); +} + +/* Checks if ch is a decimal digit. */ +U_CAPI UBool U_EXPORT2 +u_isdigit(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)(GET_CATEGORY(props)==U_DECIMAL_DIGIT_NUMBER); +} + +U_CAPI UBool U_EXPORT2 +u_isxdigit(UChar32 c) { + uint32_t props; + + /* check ASCII and Fullwidth ASCII a-fA-F */ + if( + (c<=0x66 && c>=0x41 && (c<=0x46 || c>=0x61)) || + (c>=0xff21 && c<=0xff46 && (c<=0xff26 || c>=0xff41)) + ) { + return true; + } + + GET_PROPS(c, props); + return (UBool)(GET_CATEGORY(props)==U_DECIMAL_DIGIT_NUMBER); +} + +/* Checks if the Unicode character is a letter.*/ +U_CAPI UBool U_EXPORT2 +u_isalpha(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)((CAT_MASK(props)&U_GC_L_MASK)!=0); +} + +U_CAPI UBool U_EXPORT2 +u_isUAlphabetic(UChar32 c) { + return (u_getUnicodeProperties(c, 1)&U_MASK(UPROPS_ALPHABETIC))!=0; +} + +/* Checks if c is a letter or a decimal digit */ +U_CAPI UBool U_EXPORT2 +u_isalnum(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)((CAT_MASK(props)&(U_GC_L_MASK|U_GC_ND_MASK))!=0); +} + +/** + * Checks if c is alphabetic, or a decimal digit; implements UCHAR_POSIX_ALNUM. + * @internal + */ +U_CFUNC UBool +u_isalnumPOSIX(UChar32 c) { + return (UBool)(u_isUAlphabetic(c) || u_isdigit(c)); +} + +/* Checks if ch is a unicode character with assigned character type.*/ +U_CAPI UBool U_EXPORT2 +u_isdefined(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)(GET_CATEGORY(props)!=0); +} + +/* Checks if the Unicode character is a base form character that can take a diacritic.*/ +U_CAPI UBool U_EXPORT2 +u_isbase(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)((CAT_MASK(props)&(U_GC_L_MASK|U_GC_N_MASK|U_GC_MC_MASK|U_GC_ME_MASK))!=0); +} + +/* Checks if the Unicode character is a control character.*/ +U_CAPI UBool U_EXPORT2 +u_iscntrl(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)((CAT_MASK(props)&(U_GC_CC_MASK|U_GC_CF_MASK|U_GC_ZL_MASK|U_GC_ZP_MASK))!=0); +} + +U_CAPI UBool U_EXPORT2 +u_isISOControl(UChar32 c) { + return (uint32_t)c<=0x9f && (c<=0x1f || c>=0x7f); +} + +/* Some control characters that are used as space. */ +#define IS_THAT_CONTROL_SPACE(c) \ + (c<=0x9f && ((c>=TAB && c<=CR) || (c>=0x1c && c <=0x1f) || c==0x85)) + +/* Java has decided that U+0085 New Line is not whitespace any more. */ +#define IS_THAT_ASCII_CONTROL_SPACE(c) \ + (c<=0x1f && c>=TAB && (c<=CR || c>=0x1c)) + +/* Checks if the Unicode character is a space character.*/ +U_CAPI UBool U_EXPORT2 +u_isspace(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)((CAT_MASK(props)&U_GC_Z_MASK)!=0 || IS_THAT_CONTROL_SPACE(c)); +} + +U_CAPI UBool U_EXPORT2 +u_isJavaSpaceChar(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)((CAT_MASK(props)&U_GC_Z_MASK)!=0); +} + +/* Checks if the Unicode character is a whitespace character.*/ +U_CAPI UBool U_EXPORT2 +u_isWhitespace(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)( + ((CAT_MASK(props)&U_GC_Z_MASK)!=0 && + c!=NBSP && c!=FIGURESP && c!=NNBSP) || /* exclude no-break spaces */ + IS_THAT_ASCII_CONTROL_SPACE(c) + ); +} + +U_CAPI UBool U_EXPORT2 +u_isblank(UChar32 c) { + if((uint32_t)c<=0x9f) { + return c==9 || c==0x20; /* TAB or SPACE */ + } else { + /* Zs */ + uint32_t props; + GET_PROPS(c, props); + return (UBool)(GET_CATEGORY(props)==U_SPACE_SEPARATOR); + } +} + +U_CAPI UBool U_EXPORT2 +u_isUWhiteSpace(UChar32 c) { + return (u_getUnicodeProperties(c, 1)&U_MASK(UPROPS_WHITE_SPACE))!=0; +} + +/* Checks if the Unicode character is printable.*/ +U_CAPI UBool U_EXPORT2 +u_isprint(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + /* comparing ==0 returns false for the categories mentioned */ + return (UBool)((CAT_MASK(props)&U_GC_C_MASK)==0); +} + +/** + * Checks if c is in \p{graph}\p{blank} - \p{cntrl}. + * Implements UCHAR_POSIX_PRINT. + * @internal + */ +U_CFUNC UBool +u_isprintPOSIX(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + /* + * The only cntrl character in graph+blank is TAB (in blank). + * Here we implement (blank-TAB)=Zs instead of calling u_isblank(). + */ + return (UBool)((GET_CATEGORY(props)==U_SPACE_SEPARATOR) || u_isgraphPOSIX(c)); +} + +U_CAPI UBool U_EXPORT2 +u_isgraph(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + /* comparing ==0 returns false for the categories mentioned */ + return (UBool)((CAT_MASK(props)& + (U_GC_CC_MASK|U_GC_CF_MASK|U_GC_CS_MASK|U_GC_CN_MASK|U_GC_Z_MASK)) + ==0); +} + +/** + * Checks if c is in + * [^\p{space}\p{gc=Control}\p{gc=Surrogate}\p{gc=Unassigned}] + * with space=\p{Whitespace} and Control=Cc. + * Implements UCHAR_POSIX_GRAPH. + * @internal + */ +U_CFUNC UBool +u_isgraphPOSIX(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + /* \p{space}\p{gc=Control} == \p{gc=Z}\p{Control} */ + /* comparing ==0 returns false for the categories mentioned */ + return (UBool)((CAT_MASK(props)& + (U_GC_CC_MASK|U_GC_CS_MASK|U_GC_CN_MASK|U_GC_Z_MASK)) + ==0); +} + +U_CAPI UBool U_EXPORT2 +u_ispunct(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)((CAT_MASK(props)&U_GC_P_MASK)!=0); +} + +/*Checks if the Unicode character can be ignorable in a Java or Unicode identifier.*/ +U_CAPI UBool U_EXPORT2 +u_isIDIgnorable(UChar32 c) { + if(c<=0x9f) { + return u_isISOControl(c) && !IS_THAT_ASCII_CONTROL_SPACE(c); + } else { + uint32_t props; + GET_PROPS(c, props); + return (UBool)(GET_CATEGORY(props)==U_FORMAT_CHAR); + } +} + +/*Checks if the Unicode character can start a Java identifier.*/ +U_CAPI UBool U_EXPORT2 +u_isJavaIDStart(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)((CAT_MASK(props)&(U_GC_L_MASK|U_GC_SC_MASK|U_GC_PC_MASK))!=0); +} + +/*Checks if the Unicode character can be a Java identifier part other than starting the + * identifier. + */ +U_CAPI UBool U_EXPORT2 +u_isJavaIDPart(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return (UBool)( + (CAT_MASK(props)& + (U_GC_ND_MASK|U_GC_NL_MASK| + U_GC_L_MASK| + U_GC_SC_MASK|U_GC_PC_MASK| + U_GC_MC_MASK|U_GC_MN_MASK) + )!=0 || + u_isIDIgnorable(c)); +} + +U_CAPI int32_t U_EXPORT2 +u_charDigitValue(UChar32 c) { + uint32_t props; + int32_t value; + GET_PROPS(c, props); + value=(int32_t)GET_NUMERIC_TYPE_VALUE(props)-UPROPS_NTV_DECIMAL_START; + if(value<=9) { + return value; + } else { + return -1; + } +} + +U_CAPI double U_EXPORT2 +u_getNumericValue(UChar32 c) { + uint32_t props; + int32_t ntv; + GET_PROPS(c, props); + ntv=(int32_t)GET_NUMERIC_TYPE_VALUE(props); + + if(ntv==UPROPS_NTV_NONE) { + return U_NO_NUMERIC_VALUE; + } else if(ntv>4)-12; + int32_t denominator=(ntv&0xf)+1; + return (double)numerator/denominator; + } else if(ntv>5)-14; + int32_t exp=(ntv&0x1f)+2; + numValue=mant; + + /* multiply by 10^exp without math.h */ + while(exp>=4) { + numValue*=10000.; + exp-=4; + } + switch(exp) { + case 3: + numValue*=1000.; + break; + case 2: + numValue*=100.; + break; + case 1: + numValue*=10.; + break; + case 0: + default: + break; + } + + return numValue; + } else if(ntv>2)-0xbf; + int32_t exp=(ntv&3)+1; + + switch(exp) { + case 4: + numValue*=60*60*60*60; + break; + case 3: + numValue*=60*60*60; + break; + case 2: + numValue*=60*60; + break; + case 1: + numValue*=60; + break; + case 0: + default: + break; + } + + return numValue; + } else if(ntv>2); + return (double)numerator/denominator; + } else if(ntv>2); + return (double)numerator/denominator; + } else { + /* reserved */ + return U_NO_NUMERIC_VALUE; + } +} + +U_CAPI int32_t U_EXPORT2 +u_digit(UChar32 ch, int8_t radix) { + int8_t value; + if((uint8_t)(radix-2)<=(36-2)) { + value=(int8_t)u_charDigitValue(ch); + if(value<0) { + /* ch is not a decimal digit, try latin letters */ + if(ch>=0x61 && ch<=0x7A) { + value=(int8_t)(ch-0x57); /* ch - 'a' + 10 */ + } else if(ch>=0x41 && ch<=0x5A) { + value=(int8_t)(ch-0x37); /* ch - 'A' + 10 */ + } else if(ch>=0xFF41 && ch<=0xFF5A) { + value=(int8_t)(ch-0xFF37); /* fullwidth ASCII a-z */ + } else if(ch>=0xFF21 && ch<=0xFF3A) { + value=(int8_t)(ch-0xFF17); /* fullwidth ASCII A-Z */ + } + } + } else { + value=-1; /* invalid radix */ + } + return (int8_t)((value(36-2) || (uint32_t)digit>=(uint32_t)radix) { + return 0; + } else if(digit<10) { + return (UChar32)(0x30+digit); + } else { + return (UChar32)((0x61-10)+digit); + } +} + +/* miscellaneous, and support for uprops.cpp -------------------------------- */ + +U_CAPI void U_EXPORT2 +u_getUnicodeVersion(UVersionInfo versionArray) { + if(versionArray!=nullptr) { + uprv_memcpy(versionArray, dataVersion, U_MAX_VERSION_LENGTH); + } +} + +U_CFUNC uint32_t +u_getMainProperties(UChar32 c) { + uint32_t props; + GET_PROPS(c, props); + return props; +} + +U_CFUNC uint32_t +u_getUnicodeProperties(UChar32 c, int32_t column) { + U_ASSERT(column>=0); + if(column>=propsVectorsColumns) { + return 0; + } else { + uint16_t vecIndex=UTRIE2_GET16(&propsVectorsTrie, c); + return propsVectors[vecIndex+column]; + } +} + +U_CFUNC int32_t +uprv_getMaxValues(int32_t column) { + switch(column) { + case 0: + return indexes[UPROPS_MAX_VALUES_INDEX]; + case 2: + return indexes[UPROPS_MAX_VALUES_2_INDEX]; + default: + return 0; + } +} + +U_CAPI void U_EXPORT2 +u_charAge(UChar32 c, UVersionInfo versionArray) { + if(versionArray!=nullptr) { + uint32_t version=u_getUnicodeProperties(c, 0)>>UPROPS_AGE_SHIFT; + versionArray[0]=(uint8_t)(version>>4); + versionArray[1]=(uint8_t)(version&0xf); + versionArray[2]=versionArray[3]=0; + } +} + +U_CAPI UScriptCode U_EXPORT2 +uscript_getScript(UChar32 c, UErrorCode *pErrorCode) { + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return USCRIPT_INVALID_CODE; + } + if((uint32_t)c>0x10ffff) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return USCRIPT_INVALID_CODE; + } + uint32_t scriptX=u_getUnicodeProperties(c, 0)&UPROPS_SCRIPT_X_MASK; + uint32_t codeOrIndex=uprops_mergeScriptCodeOrIndex(scriptX); + if(scriptX=UPROPS_SCRIPT_X_WITH_OTHER) { + scx=scriptExtensions+scx[1]; + } + uint32_t sc32=sc; + if(sc32>0x7fff) { + /* Guard against bogus input that would make us go past the Script_Extensions terminator. */ + return false; + } + while(sc32>*scx) { + ++scx; + } + return sc32==(*scx&0x7fff); +} + +U_CAPI int32_t U_EXPORT2 +uscript_getScriptExtensions(UChar32 c, + UScriptCode *scripts, int32_t capacity, + UErrorCode *pErrorCode) { + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(capacity<0 || (capacity>0 && scripts==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + uint32_t scriptX=u_getUnicodeProperties(c, 0)&UPROPS_SCRIPT_X_MASK; + uint32_t codeOrIndex=uprops_mergeScriptCodeOrIndex(scriptX); + if(scriptX=UPROPS_SCRIPT_X_WITH_OTHER) { + scx=scriptExtensions+scx[1]; + } + int32_t length=0; + uint16_t sx; + do { + sx=*scx++; + if(lengthcapacity) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + return length; +} + +U_CAPI UBlockCode U_EXPORT2 +ublock_getCode(UChar32 c) { + return (UBlockCode)((u_getUnicodeProperties(c, 0)&UPROPS_BLOCK_MASK)>>UPROPS_BLOCK_SHIFT); +} + +/* property starts for UnicodeSet ------------------------------------------- */ + +static UBool U_CALLCONV +_enumPropertyStartsRange(const void *context, UChar32 start, UChar32 end, uint32_t value) { + /* add the start code point to the USet */ + const USetAdder *sa=(const USetAdder *)context; + sa->add(sa->set, start); + (void)end; + (void)value; + return true; +} + +#define USET_ADD_CP_AND_NEXT(sa, cp) sa->add(sa->set, cp); sa->add(sa->set, cp+1) + +U_CFUNC void U_EXPORT2 +uchar_addPropertyStarts(const USetAdder *sa, UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return; + } + + /* add the start code point of each same-value range of the main trie */ + utrie2_enum(&propsTrie, nullptr, _enumPropertyStartsRange, sa); + + /* add code points with hardcoded properties, plus the ones following them */ + + /* add for u_isblank() */ + USET_ADD_CP_AND_NEXT(sa, TAB); + + /* add for IS_THAT_CONTROL_SPACE() */ + sa->add(sa->set, CR+1); /* range TAB..CR */ + sa->add(sa->set, 0x1c); + sa->add(sa->set, 0x1f+1); + USET_ADD_CP_AND_NEXT(sa, 0x85); // NEXT LINE (NEL) + + /* add for u_isIDIgnorable() what was not added above */ + sa->add(sa->set, 0x7f); /* range DEL..NBSP-1, NBSP added below */ + sa->add(sa->set, HAIRSP); + sa->add(sa->set, RLM+1); + sa->add(sa->set, 0x206a); // INHIBIT SYMMETRIC SWAPPING + sa->add(sa->set, 0x206f+1); // NOMINAL DIGIT SHAPES + USET_ADD_CP_AND_NEXT(sa, ZWNBSP); + + /* add no-break spaces for u_isWhitespace() what was not added above */ + USET_ADD_CP_AND_NEXT(sa, NBSP); + USET_ADD_CP_AND_NEXT(sa, FIGURESP); + USET_ADD_CP_AND_NEXT(sa, NNBSP); + + /* add for u_digit() */ + sa->add(sa->set, u'a'); + sa->add(sa->set, u'z'+1); + sa->add(sa->set, u'A'); + sa->add(sa->set, u'Z'+1); + // fullwidth + sa->add(sa->set, u'a'); + sa->add(sa->set, u'z'+1); + sa->add(sa->set, u'A'); + sa->add(sa->set, u'Z'+1); + + /* add for u_isxdigit() */ + sa->add(sa->set, u'f'+1); + sa->add(sa->set, u'F'+1); + // fullwidth + sa->add(sa->set, u'f'+1); + sa->add(sa->set, u'F'+1); + + /* add for UCHAR_DEFAULT_IGNORABLE_CODE_POINT what was not added above */ + sa->add(sa->set, 0x2060); /* range 2060..206f */ + sa->add(sa->set, 0xfff0); + sa->add(sa->set, 0xfffb+1); + sa->add(sa->set, 0xe0000); + sa->add(sa->set, 0xe0fff+1); + + /* add for UCHAR_GRAPHEME_BASE and others */ + USET_ADD_CP_AND_NEXT(sa, CGJ); +} + +U_CFUNC void U_EXPORT2 +upropsvec_addPropertyStarts(const USetAdder *sa, UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return; + } + + /* add the start code point of each same-value range of the properties vectors trie */ + utrie2_enum(&propsVectorsTrie, nullptr, _enumPropertyStartsRange, sa); +} diff --git a/deps/icu-small/source/common/uchar_props_data.h b/deps/icu-small/source/common/uchar_props_data.h index acbeadd249b1b6..c64ff978028785 100644 --- a/deps/icu-small/source/common/uchar_props_data.h +++ b/deps/icu-small/source/common/uchar_props_data.h @@ -1,4011 +1,4011 @@ -// Copyright (C) 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// Copyright (C) 1999-2016, International Business Machines -// Corporation and others. All Rights Reserved. -// -// file name: uchar_props_data.h -// -// machine-generated by: icu/tools/unicode/c/genprops/corepropsbuilder.cpp - -#ifdef INCLUDED_FROM_UCHAR_C - -static const UVersionInfo dataVersion={0xf,0,0,0}; - -static const uint16_t propsTrie_index[23016]={ -0x495,0x49d,0x4a5,0x4ad,0x4c5,0x4cd,0x4d5,0x4dd,0x4e5,0x4ed,0x4f3,0x4fb,0x503,0x50b,0x513,0x51b, -0x521,0x529,0x531,0x539,0x53c,0x544,0x54c,0x554,0x55c,0x564,0x560,0x568,0x570,0x578,0x57d,0x585, -0x58d,0x595,0x599,0x5a1,0x5a9,0x5b1,0x5b9,0x5c1,0x5bd,0x5c5,0x5ca,0x5d2,0x5d8,0x5e0,0x5e8,0x5f0, -0x5f8,0x600,0x608,0x610,0x615,0x61d,0x620,0x628,0x630,0x638,0x63e,0x646,0x645,0x64d,0x655,0x65d, -0x66d,0x665,0x675,0x67d,0x683,0x600,0x693,0x68b,0x6a3,0x6a5,0x6ad,0x69b,0x6bd,0x6c3,0x6cb,0x6b5, -0x6db,0x6e1,0x6e9,0x6d3,0x6f9,0x6ff,0x707,0x6f1,0x717,0x71d,0x725,0x70f,0x735,0x73d,0x745,0x72d, -0x755,0x75b,0x763,0x74d,0x773,0x779,0x781,0x76b,0x791,0x796,0x79e,0x789,0x7ae,0x7b5,0x7bd,0x7a6, -0x641,0x7c5,0x7cd,0x4b5,0x7d5,0x7dc,0x7e4,0x4b5,0x7ec,0x7f4,0x7fc,0x801,0x809,0x810,0x818,0x4b5, -0x600,0x820,0x828,0x830,0x838,0x58d,0x848,0x840,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x850,0x600,0x858,0x85c,0x864,0x600,0x86a,0x600,0x870,0x878,0x880,0x58d,0x58d,0x888, -0x890,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x895,0x89d,0x600,0x600,0x8a5,0x8ad,0x8b5,0x8bd,0x8c5,0x600,0x8cd,0x8d5,0x8dd, -0x8ed,0x600,0x8f5,0x8f7,0x8ff,0x8e5,0x600,0x902,0x916,0x90a,0x912,0x91e,0x600,0x926,0x92c,0x934, -0x93c,0x600,0x94c,0x954,0x95c,0x944,0x96c,0x4b5,0x974,0x977,0x97f,0x964,0x98f,0x987,0x600,0x996, -0x600,0x9a5,0x99e,0x9ad,0x9b5,0x9b9,0x9c1,0x9c9,0x535,0x9d1,0x9d4,0x9da,0x9e1,0x9d4,0x55c,0x55c, -0x4e5,0x4e5,0x4e5,0x4e5,0x9e9,0x4e5,0x4e5,0x4e5,0x9f9,0xa01,0xa09,0xa11,0xa19,0xa1d,0xa25,0x9f1, -0xa3d,0xa45,0xa2d,0xa35,0xa4d,0xa55,0xa5d,0xa65,0xa7d,0xa6d,0xa75,0xa85,0xa8d,0xa9c,0xaa1,0xa94, -0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xab1,0xab9,0x934,0xabc,0xac4,0xacb,0xad0,0xad8, -0x934,0xadf,0xade,0xaef,0xaf2,0x934,0x934,0xae7,0x934,0x934,0x934,0x934,0x934,0xb01,0xb09,0xaf9, -0x934,0x934,0x934,0xb0e,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0xb14,0xb1c,0x934,0xb24,0xb2b, -0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0xaa9,0xaa9,0xaa9,0xaa9,0xb33,0xaa9,0xb3a,0xb41, -0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0x934,0xb49,0xb50,0xb54,0xb5a,0x934,0x934,0x934, -0x58d,0x595,0x535,0xb62,0x4e5,0x4e5,0x4e5,0xb6a,0x535,0xb72,0x600,0xb78,0xb88,0xb80,0xb80,0x55c, -0xb90,0xb98,0xba0,0x4b5,0xba8,0x934,0x934,0xbaf,0x934,0x934,0x934,0x934,0x934,0x934,0xbb7,0xbbd, -0xbcd,0xbc5,0x641,0x600,0xbd5,0x890,0x600,0xbdd,0xbe5,0xbe9,0x600,0x600,0xbee,0x600,0x934,0xbf5, -0xad9,0xbfd,0xc03,0x934,0xbfd,0xc0b,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934, -0xc13,0x600,0x600,0x600,0xc1b,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0xc21,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xc26,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x934,0x934, -0xc2e,0x600,0xc31,0x600,0xc39,0xc3f,0xc47,0xc4f,0xc54,0x600,0x600,0xc58,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xc5f,0x600,0xc66,0xc6c,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xc74,0x600,0x600,0x600,0xc7c,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0xc7e,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xc85,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0xc8c,0x600,0x600,0x600,0xc93,0xc9b,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xca0,0x600,0x600,0xca8,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xcac,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xcaf,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xcb2,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0xcb8,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0xcc0,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0xcc5,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0xcca,0x600,0x600,0x600,0xccf,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0xcd7,0xcde,0xce2,0x600,0x600,0x600,0xce9,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0xcf7,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0xcef,0x934,0xcff,0x9ad,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0xd04,0xd0c,0x4e5,0xd1c,0xd14,0x600,0x600,0xd24,0xd2c,0xd3c,0x4e5,0xd41,0xd49,0xd4f,0xd56,0xd34, -0xd5e,0xd66,0x600,0xd6e,0xd7e,0xd81,0xd76,0xd89,0x655,0xd91,0xd98,0x8f6,0x6a3,0xda8,0xda0,0xdb0, -0x600,0xdb8,0xdc0,0xdc8,0x600,0xdd0,0xdd8,0xde0,0xde8,0xdf0,0xdf4,0xdfc,0x535,0x535,0x600,0xe04, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xe0c,0xe18,0xe10, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20, -0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0x600,0x600,0x600,0xe30,0x600,0xcea,0xe37,0xe3c, -0x600,0x600,0x600,0xe44,0x600,0x600,0x901,0x4b5,0xe5a,0xe4a,0xe52,0x600,0x600,0xe62,0xe6a,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xe6f,0x938,0x600,0xe77,0x600,0xe7d,0xe81, -0xe89,0xe91,0xe98,0xea0,0x600,0x600,0x600,0xea6,0xebe,0x4a5,0xec6,0xece,0xed3,0x916,0xeae,0xeb6, -0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20, -0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20, -0x12d4,0x12d4,0x1314,0x1354,0x1394,0x13cc,0x140c,0x144c,0x1484,0x14c4,0x14f0,0x1530,0x1570,0x1580,0x15c0,0x15f4, -0x1634,0x1664,0x16a4,0x16e4,0x16f4,0x1728,0x1760,0x17a0,0x17e0,0x1820,0x1854,0x1880,0x18c0,0x18f8,0x1914,0x1954, -0xa80,0xac0,0xb00,0xb40,0xb80,0xbab,0xbeb,0xa40,0xc0e,0xa40,0xa40,0xa40,0xa40,0xc4e,0x1db,0x1db, -0xc8e,0xcce,0xa40,0xa40,0xa40,0xcf7,0xd37,0xd57,0xa40,0xd7d,0xdbd,0xdfd,0xe3d,0xe7d,0xebd,0xefd, -0xf3d,0xf74,0x1db,0x1db,0xf98,0xfcc,0x1db,0xff4,0x1db,0x1db,0x1db,0x1db,0x1021,0x1db,0x1db,0x1db, -0x1db,0x1db,0x1db,0x1db,0x1035,0x1db,0x106d,0x10ad,0x1db,0x10b8,0x1db,0x1db,0x1db,0x10ee,0xa40,0x112e, -0x1db,0x1db,0x116e,0x1db,0x1191,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0x11d1,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700, -0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x1211, -0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700, -0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x1211, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0xedb,0xee2,0xeea,0x4b5,0x600,0x600,0x600,0xef2,0xf02,0xefa,0xf19,0xf0a,0xf11,0xf21,0xf25,0xf29, -0x4b5,0x4b5,0x4b5,0x4b5,0x8f6,0x600,0xf31,0xf39,0x600,0xf41,0xf49,0xf4d,0xf55,0x600,0xf5d,0x4b5, -0x58d,0x597,0xf65,0x600,0xf69,0xf71,0xf81,0xf79,0x600,0xf89,0x600,0xf90,0xfa0,0xf98,0x4b5,0x4b5, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xb88,0x902,0xfa8,0xfb8,0xfb0,0x4b5,0x4b5, -0xfc8,0xfc0,0xfcb,0xfd3,0x916,0xfdb,0x4b5,0xfe3,0xfeb,0xff3,0x4b5,0x4b5,0x600,0x1003,0x100b,0xffb, -0x101b,0x1022,0x1013,0x102a,0x1032,0x4b5,0x1042,0x103a,0x600,0x1045,0x104d,0x1055,0x105d,0x1065,0x4b5,0x4b5, -0x600,0x600,0x106d,0x4b5,0x58d,0x1075,0x535,0x107d,0x600,0x1085,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x108d,0x600,0x1095,0x4b5,0x109a,0x10a2,0x10aa,0x10b1,0xfdf,0x10b9,0xfdf,0x10c1,0xb88, -0x10d1,0x636,0x10d9,0x10c9,0x98f,0x10e1,0x10e9,0x10ef,0x1107,0x10f7,0x10ff,0x110b,0x98f,0x111b,0x1113,0x1123, -0x113b,0x112b,0x1133,0x4b5,0x1142,0x114a,0x658,0x1152,0x1162,0x1168,0x1170,0x115a,0x4b5,0x4b5,0x4b5,0x4b5, -0x600,0x1178,0x1180,0x1099,0x600,0x1188,0x1190,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x600,0x1198,0x11a0,0x4b5, -0x600,0x11a8,0x11b0,0x11b8,0x600,0x11c8,0x11c0,0x4b5,0x870,0x11d0,0x11d8,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x600,0x11e0,0x4b5,0x4b5,0x4b5,0x58d,0x535,0x11e8,0x11f8,0x11fe,0x11f0,0x4b5,0x4b5,0x120e,0x1212,0x1206, -0x122a,0x121a,0x1222,0x600,0x1238,0x1232,0x600,0x8f7,0x1248,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x1256,0x125b,0x1240,0x1250,0x126b,0x1263,0x4b5,0x4b5,0x127a,0x127e,0x1272,0x128e,0x1286,0x11c0,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x1292,0x12a2,0x12a7,0x129a,0x4b5,0x4b5,0x12af,0x12bf,0x12b7, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x901,0x4b5,0x4b5,0x4b5, -0x12cf,0x12d7,0x12df,0x12c7,0x600,0x600,0x600,0x600,0x600,0x600,0x12e7,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0xfdf,0x600,0x600,0x12ef,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x12f7,0x12ff,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x11d8,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x8f7, -0x916,0xda4,0x600,0x916,0x1307,0x130c,0x600,0x131c,0x1324,0x132c,0x1314,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x58d,0x535,0x1334,0x4b5,0x4b5,0x4b5,0x600,0x600,0x133c,0x1341,0x1347,0x4b5,0x4b5,0x134f,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x1357,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x902,0x4b5,0x106d,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x135d,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x1365,0x136a,0x1371,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xe10,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x600,0x600,0x600,0x1377,0x137c,0x1384,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x55c,0x1394,0x139b,0x934,0x934,0x934,0x138c,0x4b5,0x934,0x934,0x934, -0x934,0x934,0x934,0x934,0xbb7,0x934,0x13a2,0x934,0x13a9,0x13b1,0x13b7,0x934,0xade,0x934,0x934,0x13bf, -0x4b5,0x4b5,0x4b5,0x13c7,0x13c7,0x934,0x934,0xadb,0x13cf,0x4b5,0x4b5,0x4b5,0x4b5,0x13df,0x13e6,0x13eb, -0x13f1,0x13f9,0x1401,0x1409,0x13e3,0x1411,0x1419,0x1421,0x1426,0x13f8,0x13df,0x13e6,0x13e2,0x13f1,0x142e,0x13e0, -0x1431,0x13e3,0x1439,0x1441,0x1449,0x1450,0x143c,0x1444,0x144c,0x1453,0x143f,0x145b,0x13d7,0x934,0x934,0x934, -0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x55c,0x146b,0x55c, -0x1472,0x1479,0x1463,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x1488,0x1490,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x1480,0x1498,0x9d4, -0x14a8,0x14a0,0x4b5,0x4b5,0x4b5,0x600,0x14b8,0x14b0,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0xfdf,0x14c0,0x600,0x14c8,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0xfdf,0x14d0,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x14d8,0x600,0x600,0x600, -0x600,0x600,0x600,0x14e0,0x4b5,0x58d,0x14f0,0x14e8,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x14f8,0x1508,0x1500,0x4b5,0x4b5,0x1518,0x1510,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x1528,0x1530,0x1538, -0x1540,0x1548,0x1550,0x4b5,0x1520,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x934,0x1558,0x934, -0x934,0xbaf,0x13a0,0x1560,0xbb7,0x1568,0x934,0x934,0x934,0x934,0xbb9,0x4b5,0x1570,0x1578,0x157c,0x1584, -0x158c,0x4b5,0x4b5,0x4b5,0x4b5,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x1594,0x934,0x934,0x934, -0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934, -0x934,0x934,0x934,0x157d,0x159c,0x934,0x934,0x934,0x15a4,0x934,0x934,0x15ab,0x15b3,0x1558,0x934,0x15bb, -0x934,0x15c3,0x15c8,0x4b5,0x4b5,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0xbaf, -0x15d0,0x15d9,0x15dd,0x15e5,0x15d5,0x934,0x934,0x934,0x934,0x15ed,0x934,0xade,0x11bc,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x15f5,0x600,0x600, -0x15fc,0x600,0x600,0x600,0x1604,0x600,0x160c,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xc90,0x600,0x600, -0x1614,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x161c,0x1624,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0xccf,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x162b,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x1632,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x1639,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x4b5,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x901,0x600,0x600,0x600,0x600,0x600,0x600,0xf69,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x1641,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x1649,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x600,0x600, -0x600,0x600,0x1651,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xf69,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x67d,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x1314,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x1661,0x1659,0x1659,0x1659,0x4b5,0x4b5,0x4b5,0x4b5,0x55c,0x55c,0x55c,0x55c,0x55c,0x55c,0x55c, -0x1669,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, -0x4b5,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0x1671,0x494,0x494,0x494,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf, -0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf, -0xf,0xf,0xf,0xf,0xc,0x17,0x17,0x17,0x19,0x17,0x17,0x17,0x14,0x15,0x17,0x18, -0x17,0x13,0x17,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17, -0x18,0x18,0x18,0x17,0x17,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x14, -0x17,0x15,0x1a,0x16,0x1a,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,0x14, -0x18,0x15,0x18,0xf,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf, -0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf, -0xf,0xf,0xf,0xf,0xc,0x17,0x19,0x19,0x19,0x19,0x1b,0x17,0x1a,0x1b,5,0x1c, -0x18,0x10,0x1b,0x1a,0x1b,0x18,0x34b,0x38b,0x1a,2,0x17,0x17,0x1a,0x30b,5,0x1d, -0x34cb,0x344b,0x3ccb,0x17,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,0x18,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,0x18,2,2,2,2, -2,2,2,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,2,1,2,1, -2,1,2,1,2,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,1,2,1,2,1,2,2,2,1,1,2, -1,2,1,1,2,1,1,1,2,2,1,1,1,1,2,1, -1,2,1,1,1,2,2,2,1,1,2,1,1,2,1,2, -1,2,1,1,2,1,2,2,1,2,1,1,2,1,1,1, -2,1,2,1,1,2,2,5,1,2,2,2,5,5,5,5, -1,3,2,1,3,2,1,3,2,1,2,1,2,1,2,1, -2,1,2,1,2,1,2,1,2,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,2,1,3,2, -1,2,1,1,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,2,2,2,2,2,2,1,1, -2,1,1,2,2,1,2,1,1,1,1,2,1,2,1,2, -1,2,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,5,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, -4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, -4,4,0x1a,0x1a,0x1a,0x1a,4,4,4,4,4,4,4,4,4,4, -4,4,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, -4,4,4,4,4,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,4,0x1a,4,0x1a, -0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, -1,2,1,2,4,0x1a,1,2,0,0,4,2,2,2,0x17,1, -0,0,0,0,0x1a,0x1a,1,0x17,1,1,1,0,1,0,1,1, -2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,0,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,1,2,2,1,1,1,2,2,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,2,2,2,2,1,2,0x18,1,2,1,1,2, -2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,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,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,0x1b,6,6,6,6,6,7,7,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,1,2,1,2,1,2,1,2,1,2,1, -2,1,2,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,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,0, -0,4,0x17,0x17,0x17,0x17,0x17,0x17,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,0x17,0x13,0,0,0x1b,0x1b,0x19, -0,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,0x13,6, -0x17,6,6,0x17,6,6,0x17,6,0,0,0,0,0,0,0,0, -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,0,0,0,0,5, -5,5,5,0x17,0x17,0,0,0,0,0,0,0,0,0,0,0, -0x10,0x10,0x10,0x10,0x10,0x10,0x18,0x18,0x18,0x17,0x17,0x19,0x17,0x17,0x1b,0x1b, -6,6,6,6,6,6,6,6,6,6,6,0x17,0x10,0x17,0x17,0x17, -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, -4,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17,0x17,0x17,5,5, -6,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,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,0x17,5,6,6,6,6,6,6,6,0x10,0x1b,6, -6,6,6,6,6,4,4,6,6,0x1b,6,6,6,6,5,5, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,5,5,5,0x1b,0x1b,5, -0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0,0x10, -5,6,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, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,0,0,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, -5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6, -6,6,6,6,6,5,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,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,6, -6,6,6,6,6,6,6,6,4,4,0x1b,0x17,0x17,0x17,4,0, -0,6,0x19,0x19,6,6,6,6,4,6,6,6,4,6,6,6, -6,6,0,0,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, -0x17,0x17,0x17,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,6,6,6,6,4,6, -6,6,6,6,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,6,6,6, -0,0,0x17,0,5,5,5,5,5,5,5,5,5,5,5,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,0x1a,5,5,5,5,5,5,0,0x10,0x10,0,0, -0,0,0,0,6,6,6,6,6,6,6,6,6,6,0x10,6, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5, -5,5,5,5,5,4,6,6,6,6,6,6,6,6,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,5,5,6,6, -0x17,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,4,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,8, -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,6,8, -6,5,8,8,8,6,6,6,6,6,6,6,6,8,8,8, -8,6,8,8,5,6,6,6,6,6,6,6,5,5,5,5, -5,5,5,5,5,5,6,6,0,0,0x49,0x89,0xc9,0x109,0x149,0x189, -0x1c9,0x209,0x249,0x289,5,5,0x19,0x19,0x37cb,0x35cb,0x3fcb,0x34cb,0x3ccb,0x94b,0x1b,0x19, -5,0x17,6,0,5,6,8,8,0,5,5,5,5,5,5,5, -5,0,0,5,5,0,0,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,0,5,5,5,5,5,5,5,0,5,0, -0,0,5,5,5,5,0,0,6,5,8,8,8,6,6,6, -6,0,0,8,8,0,0,8,8,6,5,0,0,0,0,0, -0,0,0,8,0,0,0,0,5,5,0,5,0,0,0,0, -0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,6,6,5,5, -5,6,0x17,0,0,0,0,0,0,0,0,0,0,6,6,8, -0,5,5,5,5,5,5,0,0,0,0,5,5,0,0,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,0,5,5, -5,5,5,5,5,0,5,5,0,5,5,0,5,5,0,0, -6,0,8,8,8,6,6,0,0,0,0,6,6,0,0,6, -6,6,0,0,0,6,0,0,0,0,0,0,0,5,5,5, -5,0,5,0,5,5,6,6,0,0,0x49,0x89,0xc9,0x109,0x149,0x189, -0x1c9,0x209,0x249,0x289,0x17,0x19,0,0,0,0,0,0,0,5,6,6, -6,6,6,6,0,6,6,8,0,5,5,5,5,5,5,5, -5,5,0,5,5,5,0,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,0,5,5,5,5,5,5,5,0,5,5, -0,5,5,5,5,5,0,0,6,5,8,8,8,6,6,6, -6,6,0,6,6,8,0,8,8,6,0,0,5,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,5,5,6,6, -0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x1b,5,0x34cb,0x344b, -0x3ccb,0x37cb,0x35cb,0x3fcb,0,0,0,0,0,0,0,0,0,6,8,8, -0,5,5,5,5,5,5,5,5,0,0,5,5,0,0,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,0,5,5, -5,5,5,5,5,0,5,5,0,5,5,5,5,5,0,0, -6,5,8,6,8,6,6,6,6,0,0,8,8,0,0,8, -8,6,0,0,0,0,0,0,0,6,6,8,0,0,0,0, -5,5,0,5,0,0,0,0,0,0,0x49,0x89,0xc9,0x109,0x149,0x189, -0x1c9,0x209,0x249,0x289,0x7cb,0x1e4b,0x784b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x19,0x1b,0, -0,0,0,0,0,0,6,5,0,5,5,5,5,5,5,0, -0,0,5,5,5,0,5,5,5,5,0,0,0,5,5,0, -5,0,5,5,0,0,0,5,5,0,0,0,5,5,5,0, -0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0, -0,0,8,8,6,8,8,0,0,0,8,8,8,0,8,8, -8,6,0,0,5,0,0,0,0,0,0,8,0,0,0,0, -0,0,0,0,5,5,6,6,0,0,0x49,0x89,0xc9,0x109,0x149,0x189, -0x1c9,0x209,0x249,0x289,0,0,0,0,0,0,0,0x17,0x54b,0x58b,0x5cb,0x60b, -0x58b,0x5cb,0x60b,0x1b,6,8,8,8,6,5,5,5,5,5,5,5, -5,0,5,5,5,0,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,0,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,0,0,6,5,6,6,6,8,8,8, -8,0,6,6,6,0,6,6,6,6,0,0,0,0,0,0, -0,6,6,0,5,5,5,0,0,5,0,0,5,5,6,6, -0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,5,5,8, -0,0,0,0,0,0,0,0,0,0,0,0,5,6,8,8, -0x17,5,5,5,5,5,5,5,5,0,5,5,5,0,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,0,5,5, -5,5,5,5,5,5,5,5,0,5,5,5,5,5,0,0, -6,5,8,6,8,8,8,8,8,0,6,8,8,0,8,8, -6,6,0,0,0,0,0,0,0,8,8,0,0,0,0,0, -0,5,5,0,5,5,6,6,0,0,0x49,0x89,0xc9,0x109,0x149,0x189, -0x1c9,0x209,0x249,0x289,0x7cb,0x1e4b,0x784b,0x34cb,0x344b,0x3ccb,0x37cb,0x35cb,0x3fcb,0x1b,5,5, -5,5,5,5,6,6,8,8,5,5,5,5,5,5,5,5, -5,0,5,5,5,0,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,6,6,5,8,8,8,6,6,6,6,0,8,8, -8,0,8,8,8,6,5,0x1b,0,0,0,0,5,5,5,8, -0xcc0b,0xca0b,0xcb4b,0xc90b,0x364b,0xc94b,0x350b,5,0,0,0,0,0,0,0x49,0x89, -0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,8,8,0x17,0,0,0, -0,0,0,0,0,0,0,0,0,6,8,8,0,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0, -0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,0,5,5,5,5,5,5,5,5,5, -0,5,0,0,5,5,5,5,5,5,5,0,0,0,6,0, -0,0,0,8,8,8,6,6,6,0,6,0,8,8,8,8, -8,8,8,8,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,6,5,5,6,6,6,6,6,6,6,0, -0,0,0,0x19,5,5,5,5,5,5,4,6,6,6,6,6, -6,6,6,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17, -0,0,0,0,0,5,5,0,5,0,5,5,5,5,5,0, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,0,5,0,5,5,5,5,5,5,5,5,5, -5,6,5,5,6,6,6,6,6,6,6,6,6,5,0,0, -5,5,5,5,5,0,4,0,6,6,6,6,6,6,6,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,5,5,5,5, -5,0x1b,0x1b,0x1b,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, -0x17,0x17,0x17,0x1b,0x17,0x1b,0x1b,0x1b,6,6,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x344b,0x3c4b,0x444b,0x4c4b,0x544b,0x5c4b, -0x644b,0x6c4b,0x744b,0x2c4b,0x1b,6,0x1b,6,0x1b,6,0x14,0x15,0x14,0x15,8,8, -5,5,5,5,5,5,5,5,0,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,8,6,6,6,6,6,0x17,6,6,5,5,5,5, -5,6,6,6,6,6,6,6,6,6,6,6,0,6,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,6,0,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,6,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0x1b,0x1b, -0x17,0x17,0x17,0x17,0x17,0x1b,0x1b,0x1b,0x1b,0x17,0x17,0,0,0,0,0, -5,5,5,5,5,5,5,5,5,5,5,8,8,6,6,6, -6,8,6,6,6,6,6,6,8,6,6,8,8,6,6,5, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17,0x17,0x17,0x17,0x17, -5,5,5,5,5,5,8,8,6,6,5,5,5,5,6,6, -6,5,8,8,8,5,5,8,8,8,8,8,8,8,5,5, -5,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5, -5,5,6,8,8,6,6,8,8,8,8,8,8,6,5,8, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,8,8,8,6,0x1b,0x1b, -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,0x17,4,2,2,2, -1,1,1,1,1,1,0,1,0,0,0,0,0,1,0,0, -2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, -5,5,5,5,5,5,5,5,5,0,5,5,5,5,0,0, -5,5,5,5,5,5,5,0,5,0,5,5,5,5,0,0, -5,5,5,5,5,5,5,5,5,0,5,5,5,5,0,0, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,0,5,5,5,5,0,0,5,5,5,5,5,5,5,0, -5,0,5,5,5,5,0,0,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,0,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,0,5,5,5,5,0,0, -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,0,0,6,6,6, -0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b, -0x4cb,0x50b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1bcb,0x1e4b,0x788b,0,0,0, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,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,0,0,2,2,2,2,2,2,0,0, -0x13,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,0x1b,0x17,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,0xc,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,0x14, -0x15,0,0,0,5,5,5,5,5,5,5,5,5,5,5,0x17, -0x17,0x17,0x98a,0x9ca,0xa0a,5,5,5,5,5,5,5,5,0,0,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,6,6,6,8,0,0,0,0,0,0, -0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,6,6,8,0x17,0x17,0,0,0,0,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,6,6,0,0,0,0,0,0,0,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,0,5,5,5,0,6,6,0,0,0,0,0,0,0,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,6,6,8,6,6,6,6,6, -6,6,8,8,8,8,8,8,8,8,6,8,8,6,6,6, -6,6,6,6,6,6,6,6,0x17,0x17,0x17,4,0x17,0x17,0x17,0x19, -5,6,0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0, -0,0,0,0,0x54b,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,6,5,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,0x17,0x17,0x17,0x17,0x17,0x17,0x13,0x17,0x17,0x17,0x17,6, -6,6,0x10,6,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0, -0,0,0,0,5,5,5,4,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,0,0,0,0,0,0,0,5,5,5,5, -5,6,6,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,0,0, -0,0,0,0,0,0,0,0,6,6,6,8,8,8,8,6, -6,8,8,8,0,0,0,0,8,8,6,8,8,8,8,8, -8,6,6,6,0,0,0,0,0x1b,0,0,0,0x17,0x17,0x49,0x89, -0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,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,0,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,0,0,5,5,5,5,5,0,0,0, -0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5, -5,5,5,5,0,0,0,0,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x30b,0,0,0,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,6,6,8,8,6,0,0,0x17,0x17, -0x17,0x17,0x17,0x17,0x17,0x17,0x17,4,0x17,0x17,0x17,0x17,0x17,0x17,0,0, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,6, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,8,6,8,6,6,6,6,6,6,6,0, -6,8,6,8,8,6,6,6,6,6,6,6,6,8,8,8, -8,8,8,6,6,6,6,6,6,6,6,6,6,0,0,6, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, -0x17,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,6,6,6,6,6, -6,6,6,6,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x17,0x17,0, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -6,6,6,6,8,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, -6,8,6,6,6,6,6,8,6,8,8,8,8,8,6,8, -8,5,5,5,5,5,5,5,5,0,0,0,0x49,0x89,0xc9,0x109, -0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17,0x17,0x17,0x17,0x17,5,8,6,6, -6,6,8,8,6,6,8,6,6,6,5,5,0x49,0x89,0xc9,0x109, -0x149,0x189,0x1c9,0x209,0x249,0x289,5,5,5,5,5,5,6,6,8,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,6,8, -6,6,8,8,8,6,8,6,6,6,8,8,0,0,0,0, -0,0,0,0,0x17,0x17,0x17,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, -0x249,0x289,0,0,0,5,5,5,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, -0x249,0x289,5,5,5,5,5,5,8,8,8,8,8,8,8,8, -6,6,6,6,6,6,6,6,8,8,6,6,0,0,0,0x17, -0x17,0x17,0x17,0x17,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4, -4,4,0x17,0x17,2,2,2,2,2,2,2,2,2,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,0, -0,1,1,1,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0,0,0,0, -0,0,0,0,6,6,6,0x17,6,6,6,6,6,6,6,6, -6,6,6,6,6,8,6,6,6,6,6,6,6,5,5,5, -5,6,5,5,5,5,5,5,6,5,5,8,6,6,5,0, -0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2, -4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, -4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, -4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2, -4,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,4, -4,4,4,4,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,2,2,2,2,2,2, -2,2,1,2,2,2,2,2,2,2,2,2,1,1,1,1, -1,0x1a,0x1a,0x1a,0,0,2,2,2,0,2,2,1,1,1,1, -3,0x1a,0x1a,0,2,2,2,2,2,2,2,2,1,1,1,1, -1,1,1,1,2,2,2,2,2,2,0,0,1,1,1,1, -1,1,0,0,2,2,2,2,2,2,2,2,1,1,1,1, -1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1, -1,1,1,1,2,2,2,2,2,2,0,0,1,1,1,1, -1,1,0,0,2,2,2,2,2,2,2,2,0,1,0,1, -0,1,0,1,2,2,2,2,2,2,2,2,1,1,1,1, -1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2, -2,2,0,0,2,2,2,2,2,2,2,2,3,3,3,3, -3,3,3,3,2,2,2,2,2,2,2,2,3,3,3,3, -3,3,3,3,2,2,2,2,2,0,2,2,1,1,1,1, -3,0x1a,2,0x1a,0x1a,0x1a,2,2,2,0,2,2,1,1,1,1, -3,0x1a,0x1a,0x1a,2,2,2,2,0,0,2,2,1,1,1,1, -0,0x1a,0x1a,0x1a,0x16,0x17,0x17,0x17,0x18,0x14,0x15,0x17,0x17,0x17,0x17,0x17, -0x17,0x17,0x17,0x17,0x17,0x17,0x18,0x17,0x16,0x17,0x17,0x17,0x17,0x17,0x17,0x17, -0x17,0x17,0x17,0xc,0x10,0x10,0x10,0x10,0x10,0,0x10,0x10,0x10,0x10,0x10,0x10, -0x10,0x10,0x10,0x10,0x2cb,4,0,0,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x18,0x18, -0x18,0x14,0x15,4,0xc,0xc,0xc,0xc,0xc,0xc,0xc,0xc,0xc,0xc,0xc,0x10, -0x10,0x10,0x10,0x10,0x13,0x13,0x13,0x13,0x13,0x13,0x17,0x17,0x1c,0x1d,0x14,0x1c, -0x1c,0x1d,0x14,0x1c,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0xd,0xe,0x10,0x10, -0x10,0x10,0x10,0xc,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x1c,0x1d,0x17, -0x17,0x17,0x17,0x16,0x2cb,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x18,0x18, -0x18,0x14,0x15,0,4,4,4,4,4,4,4,4,4,4,4,4, -4,0,0,0,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, -0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, -0x19,0x19,0x19,0x19,0x19,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6, -6,7,7,7,7,6,7,7,7,6,6,6,6,6,6,6, -6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1b,0x1b,0x1b,0x1b,1,0x1b,1,0x1b,1,0x1b,1,1, -1,1,0x1b,2,1,1,1,1,2,5,5,5,5,2,0x1b,0x1b, -2,2,1,1,0x18,0x18,0x18,0x18,0x18,1,2,2,2,2,0x1b,0x18, -0x1b,0x1b,2,0x1b,0x358b,0x360b,0x364b,0x348b,0x388b,0x350b,0x390b,0x3d0b,0x410b,0x354b,0x454b,0x35cb, -0x3dcb,0x45cb,0x4dcb,0x58b,0x1b,0x1b,1,0x1b,0x1b,0x1b,0x1b,1,0x1b,0x1b,2,1, -1,1,2,2,1,1,1,2,0x1b,1,0x1b,0x1b,0x18,1,1,1, -1,1,0x1b,0x1b,0x58a,0x5ca,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0x7ca,0x80a,0x84a, -0x11ca,0x1e4a,0x980a,0x784a,0x58a,0x5ca,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0x7ca,0x80a,0x84a, -0x11ca,0x1e4a,0x980a,0x784a,0x784a,0x984a,0x788a,1,2,0x6ca,0x11ca,0x988a,0x78ca,0x54b,0x1b,0x1b, -0,0,0,0,0x18,0x18,0x18,0x18,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x18, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -0x18,0x1b,0x1b,0x18,0x1b,0x1b,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x18,0x18,0x1b,0x1b,0x18,0x1b,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -0x18,0x18,0x18,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x14,0x15,0x14,0x15, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x14,0x15,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18, -0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18, -0x18,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x2cb,0x80b,0x84b,0x88b,0x8cb,0x90b,0x94b,0x98b,0x9cb,0xa0b, -0xa4b,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb,0x2cb,0x30b,0x34b,0x38b,0x3cb, -0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb,0x80b,0x84b,0x88b,0x8cb,0x90b,0x94b,0x98b,0x9cb,0xa0b,0xa4b, -0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb,0x80b,0x84b,0x88b,0x8cb,0x90b,0x94b, -0x98b,0x9cb,0xa0b,0xa4b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18, -0x18,0x18,0x18,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15, -0x14,0x15,0x14,0x15,0x14,0x15,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb, -0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b, -0x48b,0x4cb,0x50b,0x7cb,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x18,0x18,0x18,0x18,0x18,0x14,0x15,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -0x18,0x18,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x18,0x18,0x18,0x18, -0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x14, -0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x14, -0x15,0x14,0x15,0x14,0x15,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -0x14,0x15,0x14,0x15,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -0x14,0x15,0x18,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -0x18,0x18,0x18,0x18,0x18,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,1,2,1,1,1,2,2,1, -2,1,2,1,2,1,1,1,1,2,1,2,2,1,2,2, -2,2,2,2,4,4,1,1,1,2,1,2,2,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,1,2,1,2,6,6,6,1,2,0,0,0,0, -0,0x17,0x17,0x17,0x17,0x344b,0x17,0x17,2,2,2,2,2,2,0,2, -0,0,0,0,0,2,0,0,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,4, -0x17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, -5,5,5,5,5,5,5,0,5,5,5,5,5,5,5,0, -5,5,5,5,5,5,5,0,5,5,5,5,5,5,5,0, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0, -0x17,0x17,0x1c,0x1d,0x1c,0x1d,0x17,0x17,0x17,0x1c,0x1d,0x17,0x1c,0x1d,0x17,0x17, -0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x13,0x17,0x17,0x13,0x17,0x1c,0x1d,0x17,0x17, -0x1c,0x1d,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x17,0x17,0x17,0x17,0x17,4, -0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x13,0x13,0x17,0x17,0x17,0x17, -0x13,0x17,0x14,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, -0x1b,0x1b,0x17,0x17,0x17,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x13,0,0, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0,0,0,0,0,0,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0,0,0,0,0x1b,0x58a,0x5ca,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,6,6, -6,6,8,8,0x13,4,4,4,4,4,0x1b,0x1b,0x7ca,0xa4a,0xcca,4, -5,0x17,0x1b,0x1b,0xc,0x17,0x17,0x17,0x1b,4,5,0x54a,0x14,0x15,0x14,0x15, -0x14,0x15,0x14,0x15,0x14,0x15,0x1b,0x1b,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15, -0x13,0x14,0x15,0x15,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,0,0,6,6,0x1a, -0x1a,4,4,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,0x17, -4,4,4,5,0,0,0,0,0,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,0,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,0,0x1b,0x1b,0x58b,0x5cb,0x60b,0x64b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1b,0xa8b,0xacb,0xb0b, -0xb4b,0xb8b,0xbcb,0xc0b,0xc4b,0xc8b,0xccb,0xd0b,0xd4b,0xd8b,0xdcb,0xe0b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0xe4b,0xe8b,0xecb, -0xf0b,0xf4b,0xf8b,0xfcb,0x100b,0x104b,0x108b,0x10cb,0x110b,0x114b,0x118b,0x11cb,5,5,5,5, -5,0x685,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,0x5c5, -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,0x685,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,0x705,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,0x585,5,5,0x705,5,5,5,0x7885, -5,0x605,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,0x785,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -0x5c5,5,5,5,5,5,5,5,0x685,5,0x645,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,0x7985,0x7c5,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,0x7845,5,5,5,5, -5,5,5,5,0x605,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,0x685,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -0x1e45,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -0x7985,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,0x7a85,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,0x5c5,5,0x745,5,0x6c5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,0x7c5,5,0x7845,0xa45,0xcc5,5,5,5,5,5,5,0xf45,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,0x605,0x605,0x605,0x605,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,0x645,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,0x585,5,5,5,5,5,5,5,0x585,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,0x585,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,0x785,0xa45,5,5,5,5, -5,5,5,5,5,5,5,5,0x585,0x5c5,0x605,5,0x5c5,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,0x7c5,5, -5,5,5,5,5,5,5,5,5,5,5,5,0x745,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,0x705,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,0x785,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,0x1e45,5, -5,5,5,5,5,5,0x645,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, -0x7885,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,0x5c5,5,5,5,5,0x5c5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,0x5c5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,0x7845,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,0x6c5,5, -5,5,5,5,0x1e45,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, -0x6c5,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,0x545,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,0,0,0,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,4,5,5,5,5,5,5,5,5,5,5,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,4,0x17,0x17,0x17, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,5,5,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,2,1,2,1,2,4,4,6,6, -1,2,1,2,1,2,1,2,1,2,1,2,1,2,5,6, -7,7,7,0x17,6,6,6,6,6,6,6,6,6,6,0x17,4, -5,5,5,5,5,5,0x58a,0x5ca,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0x54a, -6,6,0x17,0x17,0x17,0x17,0x17,0x17,0,0,0,0,0,0,0,0, -0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, -0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,4,4,4,4,4,4,4,4,4, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,4,4,4,1,2,5,4,4,2,5,5,5,5,5, -0x1a,0x1a,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -2,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, -1,2,1,2,4,2,2,2,2,2,2,2,2,1,2,1, -2,1,1,2,1,2,1,2,1,2,1,2,4,0x1a,0x1a,1, -2,1,2,5,1,2,1,2,2,2,1,2,1,2,1,2, -1,2,1,2,1,2,1,1,1,1,1,2,1,1,1,1, -1,2,1,2,1,2,1,2,1,2,1,2,1,1,1,1, -2,1,2,0,0,0,0,0,1,2,0,2,0,2,1,2, -1,2,0,0,0,0,0,0,5,5,6,5,5,5,6,5, -5,5,5,6,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,8,8,6,6,8, -0x1b,0x1b,0x1b,0x1b,6,0,0,0,0x34cb,0x344b,0x3ccb,0x37cb,0x35cb,0x3fcb,0x1b,0x1b, -0x19,0x1b,0,0,0,0,0,0,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,0x17,0x17,0x17,0x17, -0,0,0,0,0,0,0,0,8,8,8,8,6,6,0,0, -0,0,0,0,0,0,0x17,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, -0x249,0x289,0,0,0,0,0,0,8,8,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,8,8,8,8,8,8,8,8, -8,8,8,8,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,6,6,6,5,5,5,5,5,5,0x17,0x17,0x17,5, -0x17,5,5,6,5,5,5,5,5,5,6,6,6,6,6,6, -6,6,0x17,0x17,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6, -6,6,8,8,0,0,0,0,0,0,0,0,0,0,0,0x17, -8,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0,4, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0x17,0x17, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,6,8,8,6,6,6,6,8,8,6,6,8,8, -5,5,5,5,5,6,4,5,5,5,5,5,5,5,5,5, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,5,5,5,5,5,0, -5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,8, -8,6,6,8,8,6,6,0,0,0,0,0,0,0,0,0, -5,5,5,6,5,5,5,5,5,5,5,5,6,8,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0x17,0x17,0x17,0x17, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -4,5,5,5,5,5,5,0x1b,0x1b,0x1b,5,8,6,8,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -6,5,6,6,6,5,5,6,6,5,5,5,5,5,6,6, -5,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,5,5,4,0x17,0x17, -5,5,5,5,5,5,5,5,5,5,5,8,6,6,8,8, -0x17,0x17,5,4,4,8,6,0,0,0,0,0,0,0,0,0, -0,5,5,5,5,5,5,0,0,5,5,5,5,5,5,0, -0,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0, -5,5,5,5,5,5,5,0,5,5,5,5,5,5,5,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,0x1a,4,4,4,4, -2,2,2,2,2,2,2,2,2,4,0x1a,0x1a,0,0,0,0, -2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, -5,5,5,8,8,6,8,8,6,8,8,0x17,8,6,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, -5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0, -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,0,0,0,0, -5,5,5,5,5,5,5,0,0,0,0,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -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,0x12,0x12, -0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, -0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, -5,5,5,5,5,5,5,5,5,5,5,0x605,5,5,5,5, -5,5,5,0x7c5,5,5,5,5,0x5c5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,0x6c5,5,0x6c5, -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,0x7c5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,0x18,5,5,5,5,5,5,5,5,5,5,5,5,5,0, -5,5,5,5,5,0,5,0,5,5,0,5,5,0,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,2,2,2,2,2,2,2,0, -0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2, -0,0,0,0,0,5,6,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, -0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,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,0x15,0x14,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,0,0,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0, -0,0,0,0x1b,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -0x19,0x1b,0x1b,0x1b,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,6,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x14,0x15,0x17,0,0, -0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,6,0x17,0x13,0x13,0x16,0x16,0x14,0x15,0x14,0x15,0x14,0x15,0x14, -0x15,0x14,0x15,0x14,0x15,0x17,0x17,0x14,0x15,0x17,0x17,0x17,0x17,0x16,0x16,0x16, -0x17,0x17,0x17,0,0x17,0x17,0x17,0x17,0x13,0x14,0x15,0x14,0x15,0x14,0x15,0x17, -0x17,0x17,0x18,0x13,0x18,0x18,0x18,0,0x17,0x19,0x17,0x17,0,0,0,0, -5,5,5,5,5,0,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,0,0,0x10,0,0,5,5,5,5,5,5, -0,0,5,5,5,5,5,5,0,0,5,5,5,5,5,5, -0,0,5,5,5,0,0,0,0x19,0x19,0x18,0x1a,0x1b,0x19,0x19,0, -0x1b,0x18,0x18,0x18,0x18,0x1b,0x1b,0,0,0,0,0,0,0,0,0, -0,0x10,0x10,0x10,0x1b,0x1b,0,0,0,0x17,0x17,0x17,0x19,0x17,0x17,0x17, -0x14,0x15,0x17,0x18,0x17,0x13,0x17,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, -0x249,0x289,0x17,0x17,0x18,0x18,0x18,0x17,0x1a,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,0x14,0x18,0x15,0x18,0x14,0x15,0x17,0x14,0x15,0x17,0x17,5,5, -5,5,5,5,5,5,5,5,4,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,4,4,5,5,5,5, -5,5,5,5,5,5,5,5,0,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,0,5,5,0,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,0,0,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,0,0,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,0,0,0,0,0,0xb00b,0xb80b,0x784b,0x804b,0x884b,0x904b,0x984b,0xa04b, -0xa84b,0xb04b,0xb84b,0x788b,0x808b,0x888b,0x908b,0x988b,0xa08b,0xa88b,0xb08b,0xb88b,0,0,0,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x17,0x17,0x17,0,0,0,0,0x58b, -0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b, -0x1bcb,0x1e4b,0x800b,0x880b,0x900b,0x980b,0xa00b,0xa80b,0x7ca,0x7ca,0x7ca,0x7ca,0x7ca,0xcca,0x11ca,0x11ca, -0x11ca,0x11ca,0x1e4a,0x880a,0x980a,0x980a,0x980a,0x980a,0x980a,0x784a,0x984a,0x68a,0x11ca,0x344b,0x344b,0x388b, -0x3ccb,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x54b,0x34cb, -0x1b,0x1b,0x1b,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0,0,0,0x34ca,0x344a,0x58a,0x68a,0x11ca,0x980a,0x984a,0x988a,0x68a,0x7ca,0x11ca,0x1e4a, -0x980a,0x784a,0x984a,0x68a,0x7ca,0x11ca,0x1e4a,0x980a,0x784a,0x788a,0x988a,0x7ca,0x58a,0x58a,0x58a,0x5ca, -0x5ca,0x5ca,0x5ca,0x68a,0x1b,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,6,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,6,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b, -0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1bcb,0x1e4b,0x800b,0x880b,0x900b,0x980b,0xa00b,0xa80b,0xb00b,0xb80b, -0,0,0,0,0x58b,0x68b,0x7cb,0x11cb,0,0,0,0,0,0,0,0, -0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,0x1bca,5,5,5,5,5,5,5,5,0xb80a,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,0, -0,0,0,0,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,0,0x17,5,5,5,5,0,0,0,0,5,5,5,5, -5,5,5,5,0x17,0x58a,0x5ca,0x7ca,0xa4a,0x1e4a,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,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,0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0, -0,0,0,0,1,1,1,1,1,1,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, -0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,0,0,0,0,2,2,2,2, -2,2,2,2,5,5,5,5,5,5,5,5,0,0,0,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0x17, -1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1, -2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2, -2,2,0,2,2,2,2,2,2,2,0,2,2,0,0,0, -1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1, -1,1,1,0,1,1,0,2,2,2,2,2,2,2,2,2, -5,5,5,5,5,5,5,5,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,4,4,4,4,4,4,4,4,4,4,4,4,4,4, -4,0,4,4,4,4,4,4,4,4,4,0,0,0,0,0, -4,4,4,4,4,4,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, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,0,5,5,0,0,0,5,0,0,5, -5,5,5,5,5,5,0,0,5,0,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,0,0x17,0x58b,0x5cb,0x60b,0x7cb,0xa4b,0x1e4b,0x784b,0x788b,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,0x1b,0x1b,0x58b,0x5cb,0x60b,0x64b,0x68b,0x7cb,0xa4b,0,0,0,0, -0,0,0,0x58b,0x5cb,0x60b,0x64b,0x64b,0x68b,0x7cb,0xa4b,0x1e4b,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0, -5,5,0,0,0,0,0,0x58b,0x68b,0x7cb,0xa4b,0x1e4b,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,0x58b,0x7cb,0xa4b,0x1e4b,0x5cb,0x60b,0,0,0,0x17,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,0,0,0,0,0,0x17,0xa04b,0xa84b,0xb04b,0xb84b, -0x788b,0x808b,0x888b,0x908b,0x988b,0xa08b,0xa88b,0xb08b,0xb88b,0x78cb,0x80cb,0x88cb,0x90cb,0x98cb,0xa0cb,0xa8cb, -0xb0cb,0xb8cb,0x36cb,0x354b,0x34cb,0x348b,0x46cb,0x344b,0x4ecb,0x388b,0x3ccb,0x454b,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,0,0,0,0,0x5ecb,0x344b,5,5,0x58b,0x5cb,0x60b,0x64b, -0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0,0,0x1e4b,0x800b, -0x880b,0x900b,0x980b,0xa00b,0xa80b,0xb00b,0xb80b,0x784b,0x804b,0x884b,0x904b,0x984b,0x30b,0x34b,0x38b,0x3cb, -0x7cb,0xa4b,0x1e4b,0x784b,0x344b,0,0,0,0,0,0,0,0x17,0x17,0x17,0x17, -0x17,0x17,0x17,0x17,0x17,0,0,0,0,0,0,0,5,6,6,6, -0,6,6,0,0,0,0,0,6,6,6,6,5,5,5,5, -0,5,5,5,0,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,0,0, -6,6,6,0,0,0,0,6,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,0x58b,0x11cb,0x17,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,0x58b,0x7cb,0xa4b,5,5,5,5,5,6,6,0, -0,0,0,0x58b,0x68b,0x7cb,0xa4b,0x1e4b,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0, -0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5, -0x1b,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,0,0,0,0x17,0x17,0x17, -0x17,0x17,0x17,0x17,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,0,0,0x58b,0x5cb,0x60b,0x64b, -0x7cb,0xa4b,0x1e4b,0x784b,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,0,0,0,0,0,0x58b,0x5cb,0x60b,0x64b, -0x7cb,0xa4b,0x1e4b,0x784b,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,0,0,0,0,0,0,0,0x17,0x17,0x17, -0x17,0,0,0,0,0,0,0,0,0,0,0,0,0x58b,0x5cb,0x60b, -0x64b,0x7cb,0xa4b,0x1e4b,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,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,0,0,0,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,0,0,0,0,0,0,0,0x58b,0x68b, -0x7cb,0x11cb,0x1e4b,0x784b,5,5,5,5,6,6,6,6,0,0,0,0, -0,0,0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0, -0,0,0,0,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb,0xa4b,0xccb, -0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1bcb,0x1e4b,0x800b,0x880b,0x900b,0x980b,0xa00b,0xa80b,0xb00b,0xb80b,0x344b, -0x34cb,0x348b,0x388b,0,5,5,5,5,5,5,5,5,5,5,0,6, -6,0x13,0,0,5,5,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,6,6,6,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,0x58b,0x5cb,0x60b,0x64b,0x68b,0x7cb,0xa4b,0xccb,0x1e4b,0x344b,5, -0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6, -6,6,6,6,6,0x58b,0x7cb,0xa4b,0x1e4b,0x17,0x17,0x17,0x17,0x17,0,0, -0,0,0,0,5,5,6,6,6,6,0x17,0x17,0x17,0x17,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,5,5,5,5,5,0x58b,0x5cb,0x60b,0x64b,0x7cb,0xa4b,0x1e4b, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x144b,0x16cb,0x194b,0x1bcb,0x1e4b,0x784b,0x49,0x89,0xc9,0x109,0x149,0x189, -0x1c9,0x209,0x249,0x289,6,5,5,6,6,5,0,0,0,0,0,0, -0,0,0,6,8,6,8,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,6,6,6,6,6,6,6,0x17,0x17,0x17,0x17,0x17, -0x17,0x17,0,0,0,0,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb, -0xa4b,0xccb,0xf4b,0x11cb,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,8,8,8,6,6,6,6,8,8,6,6,0x17, -0x17,0x10,0x17,0x17,0x17,0x17,6,0,0,0,0,0,0,0,0,0, -0,0x10,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,0,0,0,0,0,0,0,0x49,0x89,0xc9,0x109, -0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0,5,5,5,5, -5,5,5,6,6,6,6,6,8,6,6,6,6,6,6,6, -6,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17,0x17,0x17, -5,8,8,5,0,0,0,0,0,0,0,0,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,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,6, -0x17,0x17,5,0,0,0,0,0,0,0,0,0,8,5,5,5, -5,0x17,0x17,0x17,0x17,6,6,6,6,0x17,8,6,0x49,0x89,0xc9,0x109, -0x149,0x189,0x1c9,0x209,0x249,0x289,5,0x17,5,0x17,0x17,0x17,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,8, -8,8,6,6,6,6,6,6,6,6,6,8,0,0x58b,0x5cb,0x60b, -0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1bcb,0x1e4b, -0x784b,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5, -5,5,5,5,5,5,5,5,8,8,8,6,6,6,8,8, -6,8,6,6,0x17,0x17,0x17,0x17,0x17,0x17,6,5,5,6,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0, -5,0,5,5,5,5,0,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,0,5,5,5,5,5,5,5,5,5, -5,0x17,0,0,0,0,0,0,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,8,8,8,6,6,6,6,6, -6,6,6,0,0,0,0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, -0x249,0x289,0,0,0,0,0,0,5,5,8,8,0,0,6,6, -6,6,6,6,6,0,0,0,6,6,6,6,6,0,0,0, -0,0,0,0,0,0,0,0,6,6,8,8,0,5,5,5, -5,5,5,5,5,0,0,5,5,0,0,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,0,5,5,5,5,5,5, -5,0,5,5,0,5,5,5,5,5,0,6,6,5,8,8, -6,8,8,8,8,0,0,8,8,0,0,8,8,8,0,0, -5,0,0,0,0,0,0,8,0,0,0,0,0,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,8,8,8,6,6,6,6,6,6,6,6, -8,8,6,6,6,8,6,5,5,5,5,0x17,0x17,0x17,0x17,0x17, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17,0,0x17,6,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -8,8,8,6,6,6,6,6,6,8,6,8,8,8,8,6, -6,8,6,6,5,5,0x17,5,0,0,0,0,0,0,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,8, -8,8,6,6,6,6,0,0,8,8,8,8,6,6,8,6, -6,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, -0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,5,5,5,5,6,6,0,0, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -8,8,8,6,6,6,6,6,6,6,6,8,8,6,8,6, -6,0x17,0x17,0x17,5,0,0,0,0,0,0,0,0,0,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, -0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -5,5,5,5,5,5,5,5,5,5,5,6,8,6,8,8, -6,6,6,6,6,6,8,6,5,0x17,0,0,0,0,0,0, -8,8,6,6,6,6,8,6,6,6,6,6,0,0,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x7cb,0xa4b,0x17,0x17,0x17,0x1b, -5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -5,5,5,5,5,5,5,5,5,5,5,5,8,8,8,6, -6,6,6,6,6,6,6,6,8,6,6,0x17,0,0,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b, -0x16cb,0x194b,0x1bcb,0,0,0,0,0,0,0,0,0,0,0,0,5, -8,5,8,6,0x17,0x17,0x17,0,0,0,0,0,0,0,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, -5,5,5,5,5,5,5,0,0,5,0,0,5,5,5,5, -5,5,5,5,0,5,5,0,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,8,8,8,8,8,8,0,8, -8,0,0,6,6,8,6,5,6,5,0x17,5,8,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5, -0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,8,8,8,6,6,6,6, -0,0,6,6,8,8,8,8,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6, -6,8,5,6,6,6,6,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,6, -0,0,0,0,0,0,0,0,5,6,6,6,6,6,6,8, -8,6,6,6,5,5,5,5,5,6,6,6,6,6,6,6, -6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,0x17,0x17,0x17,0,0,0,0,0, -0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6, -6,6,6,6,6,6,6,8,6,6,0x17,0x17,0x17,5,0x17,0x17, -5,0x17,0x17,0x17,0x17,0x17,0,0,0,0,0,0,0,0,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb, -0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1bcb,0x1e4b,0,0,0, -0x17,0x17,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,0,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,8,6,6,6,6, -6,6,6,0,6,6,6,6,6,6,8,6,6,6,6,6, -6,6,6,6,0,8,6,6,6,6,6,6,6,8,6,6, -8,6,6,0,0,0,0,0,0,0,0,0,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,0,0,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,6, -0,0,0,0,0,0,0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, -0x249,0x289,0,0,0,0,0,0,5,5,5,5,5,5,5,0, -5,5,0,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,0, -0,0,6,0,6,6,0,6,5,5,5,5,5,5,5,5, -5,5,8,8,8,8,8,0,6,6,0,8,8,6,8,6, -5,0,0,0,0,0,0,0,5,5,5,5,5,5,0,5, -5,0,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,6,6,8,8,0x17, -0x17,0,0,0,0,0,0,0,6,8,6,0x17,0x17,0x17,0x17,0x17, -0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, -0x249,0x289,0,0,0,0,0,0,6,6,5,8,5,5,5,5, -5,5,5,5,5,5,5,5,5,0,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -8,8,6,6,6,6,6,0,0,0,8,8,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0x19,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0, -0,0,0,0,0,0,0,0,0,0,0,0x17,0xcd0b,0xcc0b,0xcb0b,0xd00b, -0xca0b,0xcf0b,0xcb4b,0xd04b,0xc90b,0x37cb,0x37cb,0x364b,0x35cb,0xc94b,0x3fcb,0x350b,0x34cb,0x344b,0x344b,0x3ccb, -0xcd0b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x19,0x19,0x19,0x34ca,0x354a,0x34ca,0x34ca, -0x344a,0x348a,0x388a,0xf4a,0x11ca,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0,0x17,0x17,0x17,0x17, -0x17,0,0,0,0,0,0,0,0,0,0,0,0x5ca,0x60a,0x64a,0x68a, -0x6ca,0x70a,0x74a,0x78a,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0x64a,0x68a,0x6ca,0x70a,0x74a, -0x78a,0x58a,0x5ca,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0x58a,0x5ca,0x60a,0x64a,0x68a,0x5ca, -0x60a,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0x58a,0x5ca,0x60a,0x60a,0x64a,0x68a,0xc08a,0xc18a, -0x58a,0x5ca,0x60a,0x60a,0x64a,0x68a,0x60a,0x60a,0x64a,0x64a,0x64a,0x64a,0x6ca,0x70a,0x70a,0x70a, -0x74a,0x74a,0x78a,0x78a,0x78a,0x78a,0x5ca,0x60a,0x64a,0x68a,0x6ca,0x58a,0x5ca,0x60a,0x64a,0x64a, -0x68a,0x68a,0x5ca,0x60a,0x58a,0x5ca,0x348a,0x388a,0x454a,0x348a,0x388a,0x35ca,5,5,5,5, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,0x17,0x17,0, -0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,0x10,0x10,0x10,0x10, -0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,6,5,5,5, -5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,0,0,0,0,0,0,0,0,0,0,0x49,0x89,0xc9,0x109, -0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0, -6,6,6,6,6,0x17,0,0,0,0,0,0,0,0,0,0, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -6,6,6,6,6,6,6,0x17,0x17,0x17,0x17,0x17,0x1b,0x1b,0x1b,0x1b, -4,4,4,4,0x17,0x1b,0,0,0,0,0,0,0,0,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0x7cb,0x1e4b,0x788b,0x790b,0x798b, -0x7a0b,0x7a8b,0,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,0,0,0,0,0,5,5,5, -0x54b,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0x80b,0x84b,0x88b,0x8cb,0x90b, -0x94b,0x98b,0x9cb,0xa0b,0x58b,0x5cb,0x60b,0x17,0x17,0x17,0x17,0,0,0,0,0, -5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,6, -5,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, -8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, -8,8,8,8,0,0,0,0,0,0,0,6,6,6,6,4, -4,4,4,4,4,4,4,4,4,4,4,4,4,4,0x17,4, -6,0,0,0,0,0,0,0,0,0,0,0,8,8,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,4,4,4,4,0,4,4,4,4,4,4,4, -0,4,4,0,5,5,5,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,5,5,5,0,0,5,0,0, -0,0,0,0,0,0,0,0,5,5,5,5,0,0,0,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,0,0,0,0,0,5,5,5,5, -5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0, -5,5,5,5,5,5,5,5,5,5,0,0,0x1b,6,6,0x17, -0x10,0x10,0x10,0x10,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,0,0,0,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0, -0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,8,8,6,6,6,0x1b,0x1b, -0x1b,8,8,8,8,8,8,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,6, -6,6,6,6,6,6,6,0x1b,0x1b,6,6,6,6,6,6,6, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,6,6,6,6,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,6,6, -6,0x1b,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0x54b,0x58b,0x5cb,0x60b, -0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0x80b,0x84b,0x88b,0x8cb,0x90b,0x94b,0x98b,0x9cb,0xa0b, -0,0,0,0,0,0,0,0,0,0,0,0,0x58b,0x5cb,0x60b,0x64b, -0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1bcb,0x58b,0x5cb, -0x60b,0x64b,0x68b,0x58b,0x68b,0,0,0,0,0,0,0,0x249,0x289,0x49,0x89, -0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, -0x249,0x289,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,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,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2, -2,0,2,2,2,2,2,2,2,2,2,2,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,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, -1,0,1,1,0,0,1,0,0,1,1,0,0,1,1,1, -1,0,1,1,1,1,1,1,1,1,2,2,2,2,0,2, -0,2,2,2,2,2,2,2,0,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,1,1,2,2,2,2,1,1,0,1,1,1,1,0, -0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1, -1,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,1,1,0,1, -1,1,1,0,1,1,1,1,1,0,1,0,0,0,1,1, -1,1,1,1,1,0,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,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,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,2,2,2,2,2,2,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,0x18,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,0x18, -2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x18, -2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, -2,2,2,2,2,0x18,2,2,2,2,2,2,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,0x18, -2,2,2,2,2,2,1,2,0,0,0x49,0x89,0xc9,0x109,0x149,0x189, -0x1c9,0x209,0x249,0x289,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0,6,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,0x1b,0x1b,0x1b,0x1b,6,6,6,6,6,6,6,6,6, -6,6,6,6,6,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,6,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,6,0x1b,0x1b,0x17,0x17,0x17,0x17,0x17, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, -6,6,6,6,6,6,6,0,6,6,6,6,6,6,6,6, -6,6,6,6,6,6,6,6,6,0,0,6,6,6,6,6, -2,2,2,2,2,2,2,2,2,2,5,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,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, -6,6,0,6,6,0,6,6,6,6,6,0,0,0,0,0, -4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, -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,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,5,0x1b, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -5,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0, -6,6,6,6,6,6,6,4,4,4,4,4,4,4,0,0, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0x19, -5,5,5,5,5,5,5,5,5,5,5,4,6,6,6,6, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, -5,5,5,5,5,5,5,0,5,5,5,5,0,5,5,0, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0, -5,5,5,5,5,0,0,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b, -6,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0, -2,2,2,2,6,6,6,6,6,6,6,4,0,0,0,0, -0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0x17,0x17, -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, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b, -0x78cb,0x794b,0x814b,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x1b,0x34cb,0x344b,0x3ccb, -0x19,0x58b,0x5cb,0x788b,0x78cb,0,0,0,0,0,0,0,0,0,0,0, -0x16cb,0x194b,0x1bcb,0x1e4b,0x800b,0x880b,0x900b,0x980b,0xa00b,0xa80b,0xb00b,0xb80b,0x784b,0x804b,0x884b,0x904b, -0x984b,0xa04b,0xa84b,0xb04b,0xb84b,0x788b,0x808b,0x888b,0x908b,0x988b,0xa08b,0xa88b,0xb08b,0xb88b,0x78cb,0x80cb, -0x984b,0xa04b,0xa84b,0xb04b,0xb84b,0x788b,0x808b,0x888b,0x908b,0x988b,0xa08b,0xa88b,0xb08b,0xb88b,0x1b,0x5cb, -0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0x900b,0xa00b,0x804b,0x788b,0x344b,0x354b,0,0, -0,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b, -0x16cb,0x194b,0x1bcb,0x1e4b,0x800b,0x880b,0x900b,0x980b,0xa00b,0xa80b,0xb00b,0xb80b,0x784b,0x804b,0x884b,0x904b, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x18,0x18,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -5,5,5,5,0,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, -0,5,5,0,5,0,0,5,0,5,5,5,5,5,5,5, -5,5,5,0,5,5,5,5,0,5,0,5,0,0,0,0, -0,0,5,0,0,0,0,5,0,5,0,5,0,5,5,5, -0,5,5,0,5,0,0,5,0,5,0,5,0,5,0,5, -0,5,5,0,5,0,0,5,5,5,5,0,5,5,5,5, -5,5,5,0,5,5,5,5,0,5,5,5,5,0,5,0, -5,5,5,5,5,5,5,5,5,5,0,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0, -0,5,5,5,0,5,5,5,5,5,0,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x2cb,0x2cb,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x54b,0x54b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0, -0x1b,0x1b,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1a,0x1a,0x1a,0x1a,0x1a, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0x1b,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0, -0x1b,0x1b,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0, -0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0, -0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0, -0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x1b,0x1b,0x1b,5,0x705,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,0x645,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,0x645,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,0x685,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,0xcc5,5,5,5,5,5,5,5,5,0xf45,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,0xf45,5,5,5, -5,5,5,5,5,5,5,5,5,5,0x6c5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,0x605,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,0x605,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,0x605,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,0x605,5,5,5,5,5,5,5,5,5,5,5,5, -5,0x645,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,0x785,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, -0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, -0x10,0x10,0x10,0x10,0,0x10,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, -0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, -0x11,0x11,0,0,0,0,0,0 -}; - -static const UTrie2 propsTrie={ - propsTrie_index, - propsTrie_index+4692, - NULL, - 4692, - 18324, - 0xa40, - 0x12d4, - 0x0, - 0x0, - 0x110000, - 0x59e4, - NULL, 0, false, false, 0, NULL -}; - -static const uint16_t propsVectorsTrie_index[32692]={ -0x539,0x541,0x549,0x551,0x569,0x571,0x579,0x581,0x589,0x591,0x599,0x5a1,0x5a9,0x5b1,0x5b9,0x5c1, -0x5c8,0x5d0,0x5d8,0x5e0,0x5e3,0x5eb,0x5f3,0x5fb,0x603,0x60b,0x613,0x61b,0x623,0x62b,0x633,0x63b, -0x643,0x64b,0x652,0x65a,0x662,0x66a,0x672,0x67a,0x682,0x68a,0x68f,0x697,0x69e,0x6a6,0x6ae,0x6b6, -0x6be,0x6c6,0x6ce,0x6d6,0x6dd,0x6e5,0x6ed,0x6f5,0x6fd,0x705,0x70d,0x715,0x71d,0x725,0x72d,0x735, -0x1b39,0xd8a,0xe56,0x118d,0x12cc,0x1d01,0x1ea0,0x1cf9,0x13e6,0x13f6,0x13de,0x13ee,0x80a,0x810,0x818,0x820, -0x828,0x82e,0x836,0x83e,0x846,0x84c,0x854,0x85c,0x864,0x86a,0x872,0x87a,0x882,0x88a,0x892,0x899, -0x8a1,0x8a7,0x8af,0x8b7,0x8bf,0x8c5,0x8cd,0x8d5,0x8dd,0x13fe,0x8e5,0x8ed,0x8f5,0x8fc,0x904,0x90c, -0x914,0x918,0x920,0x927,0x92f,0x937,0x93f,0x947,0x1719,0x1721,0x94f,0x957,0x95f,0x967,0x96f,0x976, -0x177f,0x176f,0x1777,0x1a74,0x1a7c,0x140e,0x97e,0x1406,0x1662,0x1662,0x1664,0x1422,0x1423,0x1416,0x1418,0x141a, -0x1787,0x1789,0x986,0x1789,0x98e,0x993,0x99b,0x178e,0x9a1,0x1789,0x9a7,0x9af,0xc6a,0x1796,0x1796,0x9b7, -0x17a6,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7, -0x17a7,0x17a7,0x17a7,0x179e,0x9bf,0x17af,0x17af,0x9c7,0xb92,0xb9a,0xba2,0xbaa,0x17bf,0x17b7,0x9cf,0x9d7, -0x9df,0x17c9,0x17d1,0x9e7,0x17c7,0x9ef,0x1b41,0xd92,0xbb2,0xbba,0xbc2,0xbc7,0x19da,0xc91,0xc98,0x1936, -0xc42,0x1b49,0xd9a,0xda2,0xdaa,0xdb2,0xf60,0xf64,0x1a3a,0x1a3f,0xcd0,0xcd8,0x1ab0,0x1ab8,0x1c19,0xe5e, -0x1ac0,0xd1e,0xd26,0x1ac8,0x1105,0x11b5,0xf38,0xdba,0x1956,0x193e,0x194e,0x1946,0x19f2,0x19ea,0x19a6,0x1a32, -0x142b,0x142b,0x142b,0x142b,0x142e,0x142b,0x142b,0x1436,0x9f7,0x143e,0x9fb,0xa03,0x143e,0xa0b,0xa13,0xa1b, -0x144e,0x1446,0x1456,0xa23,0xa2b,0x145e,0xa33,0xa3b,0x1466,0x146e,0x1476,0x147e,0xa43,0x1486,0x148d,0x1495, -0x149d,0x14a5,0x14ad,0x14b5,0x14bd,0x14c4,0x14cc,0x14d4,0x14dc,0x14e4,0x14e7,0x14e9,0x17d9,0x18cc,0x18d2,0x1a22, -0x14f1,0xa4b,0xa53,0x1617,0x161c,0x161f,0x1625,0x14f9,0x162d,0x162d,0x1509,0x1501,0x1511,0x1519,0x1521,0x1529, -0x1531,0x1539,0x1541,0x1549,0x18da,0x192e,0x1a84,0x1be1,0x1559,0x155f,0x1567,0x156f,0x1551,0x1577,0x18e2,0x18e9, -0x17e1,0x17e1,0x17e1,0x17e1,0x17e1,0x17e1,0x17e1,0x17e1,0x18f1,0x18f1,0x18f1,0x18f1,0x18f9,0x1900,0x1902,0x1909, -0x1911,0x1915,0x1915,0x1918,0x1915,0x1915,0x191e,0x1915,0x195e,0x1a2a,0x1a8c,0xbcf,0xbd5,0x1d45,0x1d4d,0x1e2b, -0x19ca,0x19be,0x19c2,0x1a47,0x19ae,0x19ae,0x19ae,0xc52,0x19b6,0xc72,0x1a0a,0xcc0,0xc5a,0xc62,0xc62,0x1ad0, -0x19fa,0x1a94,0xca8,0xcb0,0xa5b,0x17e9,0x17e9,0xa63,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0xa6b,0x73d, -0x164a,0x166c,0xa73,0x1674,0xa7b,0x167c,0x1684,0x168c,0xa83,0xa88,0x1694,0x169b,0xa8d,0x17f9,0x1a1a,0xc4a, -0xa95,0x16f6,0x16fd,0x16a3,0x1705,0x1709,0x16ab,0x16af,0x16c8,0x16c8,0x16ca,0x16b7,0x16bf,0x16bf,0x16c0,0x1711, -0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, -0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, -0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, -0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, -0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, -0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, -0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, -0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, -0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, -0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, -0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, -0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, -0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1804,0x1966,0x1966, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, -0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d9,0x1b31,0x1f0c, -0x180c,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812, -0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812, -0x1812,0x1812,0x1812,0x1812,0xa9d,0x181a,0xaa5,0x1b51,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc, -0x1ad8,0xd2e,0x1aec,0x1ae4,0x1aee,0x1b59,0x1b59,0xdc2,0x19d2,0x1a4f,0x1aa4,0x1aa8,0x1a9c,0x1c11,0xce0,0xce7, -0x1a02,0xcb8,0x1a57,0xcef,0x1af6,0x1af9,0xd36,0x1b61,0x1b09,0x1b01,0xd3e,0xdca,0x1b69,0x1b6d,0xdd2,0x100f, -0x1b11,0xd46,0xd4e,0x1b75,0x1b85,0x1b7d,0xdda,0xf08,0xe66,0xe6e,0x1d9b,0xfbf,0x1e48,0x1e48,0x1b8d,0xde2, -0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762, -0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764, -0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766, -0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761, -0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763, -0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765, -0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767, -0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762, -0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764, -0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766, -0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761, -0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763, -0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765, -0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767, -0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762, -0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764, -0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766, -0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761, -0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763, -0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765, -0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767, -0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0xaad,0xdea,0xded, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739, -0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739, -0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, -0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, -0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, -0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, -0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, -0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, -0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, -0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, -0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, -0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, -0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, -0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, -0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x16e1,0x16e1,0x16e1,0x16e1,0x16e1,0x16e1,0x16e1,0x16e1, -0x16e6,0x16ee,0x1926,0x13a3,0x1a12,0x1a12,0x13a7,0x13ae,0xab5,0xabd,0xac5,0x1597,0x159e,0x15a6,0xacd,0x15ae, -0x15ec,0x15ec,0x157f,0x1587,0x15b6,0x15e3,0x15e4,0x15f4,0x15be,0x15c3,0x15cb,0x15d3,0xad5,0x15db,0xadd,0x158f, -0xcc8,0x15fc,0xae5,0xaed,0x1604,0x160a,0x160f,0xaf5,0xb05,0x1652,0x165a,0x163d,0x1642,0xb0d,0xb15,0xafd, -0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729, -0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1731,0x1731,0x1731,0x1731, -0x1564,0x1564,0x15a4,0x15e4,0x1624,0x1664,0x16a4,0x16e4,0x1720,0x1760,0x178c,0x17cc,0x180c,0x184c,0x188c,0x18cc, -0x190c,0x1948,0x1988,0x19c8,0x1a08,0x1a3c,0x1a78,0x1ab8,0x1af8,0x1b38,0x1b74,0x1bb4,0x1bf4,0x1c34,0x1c74,0x1cb4, -0xe59,0xa80,0xac0,0xb00,0xb40,0xb6b,0xf99,0xa40,0xed9,0xa40,0xa40,0xa40,0xa40,0xbab,0x13e2,0x13e2, -0xf19,0xfd9,0xa40,0xa40,0xa40,0xbeb,0xf59,0xc2b,0xa40,0xc51,0xc91,0xcd1,0xd11,0xd51,0xe99,0xdc9, -0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322, -0x1322,0x1322,0x1322,0x1322,0x1019,0x1362,0x1157,0x1197,0x13a2,0x11a2,0x1422,0x1422,0x1422,0x1059,0x1079,0x10b9, -0x1462,0x1462,0x11e2,0x14a2,0x10f9,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079, -0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1117, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, -0xe09,0xe19,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, -0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, -0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2, -0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x1222, -0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2, -0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x1262, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0xc27,0xc2a,0xdf5,0x1deb,0x1017,0x745,0x559,0x10b1,0xcf7,0xd76,0x559,0x559,0x1d11,0xf10,0xf18,0x1e33, -0xc7a,0xc81,0xc89,0x1b95,0x1dcb,0x559,0x1dab,0xfe7,0x1b9d,0xdfd,0xe05,0xe0d,0x103f,0x74d,0x559,0x559, -0x1ba5,0x1ba5,0x755,0x559,0x1e60,0x10c9,0x1e58,0x10d1,0x1f4c,0x11cb,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0xe15,0x1fa4,0x12c4,0x1346,0x1347,0x1f6c,0x11f3,0x11fa,0x1201,0x1303,0x1307,0x127b,0x1211, -0x1c21,0x1c23,0xe76,0xe7d,0x1bad,0x1bb5,0xe1d,0xf30,0x1d09,0xef8,0xf00,0xfdf,0x1d29,0x1d2d,0x1d35,0x105f, -0xfaf,0x1d8b,0x75d,0x559,0x10b9,0x10c1,0x1d93,0xfb7,0xf91,0xf97,0xf9f,0xfa7,0x559,0x559,0x559,0x559, -0x1ed0,0x1ec8,0x113b,0x1143,0x1e13,0x1e0b,0x1087,0x559,0x559,0x559,0x559,0x559,0x1dfb,0x1047,0x104f,0x1057, -0x1dc3,0x1dbb,0xff7,0x1133,0x1d3d,0xf40,0x765,0x559,0x1097,0x109f,0x76d,0x559,0x559,0x559,0x559,0x559, -0x1f44,0x11ad,0x775,0x559,0x559,0x1e23,0x1e1b,0x108f,0x1283,0x1289,0x1291,0x559,0x559,0x1219,0x121d,0x1225, -0x1f04,0x1efc,0x1195,0x1ef4,0x1eec,0x1185,0x1df3,0x1037,0x1357,0x135a,0x135a,0x559,0x559,0x559,0x559,0x559, -0x10e9,0x10ee,0x10f6,0x10fd,0x1125,0x112b,0x559,0x559,0x1169,0x116d,0x1175,0x11bd,0x11c3,0x77d,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x11db,0x136a,0x136f,0x1377,0x559,0x559,0x781,0x1f8c,0x126b, -0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f, -0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a64,0xcff,0xd06,0xd06,0xd06, -0x1a6c,0x1a6c,0x1a6c,0xd0e,0x1e50,0x1e50,0x1e50,0x1e50,0x1e50,0x1e50,0x789,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x78d,0x1fbc,0x1fbc,0x12d4,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b, -0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0xe85,0xfff,0x1007,0x1fc4, -0x130f,0x1317,0xf48,0x1de3,0x1ddb,0x1027,0x102f,0x795,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1f64,0x1f5c,0x11eb, -0x559,0x559,0x559,0x1d21,0x1d21,0xf20,0x1d19,0xf28,0x559,0x559,0x111d,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x799,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1d73,0x1d73,0x1d73,0xf6c,0xf71, -0x7a1,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1fd4,0x1337,0x133e,0x1fcc,0x1fcc,0x1fcc,0x7a9, -0x559,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0xb2b,0x184f,0xb33,0x1850,0x1847,0x1858,0x185e,0x1866, -0xb3b,0x198e,0x198e,0x7b1,0x559,0x559,0x559,0x1362,0x11e3,0x197e,0x197e,0xc32,0xd16,0x559,0x559,0x559, -0x559,0x1897,0x189e,0xb43,0x18a1,0xb4b,0xb53,0xb5b,0x189b,0xb63,0xb6b,0xb73,0x18a0,0x18a8,0x1897,0x189e, -0x189a,0x18a1,0x18a9,0x1898,0x189f,0x189b,0xb7a,0x186e,0x1876,0x187d,0x1884,0x1871,0x1879,0x1880,0x1887,0xb82, -0x188f,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78, -0x1e78,0x1e68,0x1e6b,0x1e68,0x1e72,0x10d9,0x7b9,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x12f0,0x12f8,0x12fb,0x12fb,0x12fb,0x12fb,0x12fb, -0x12fb,0x110d,0x1115,0x1fdc,0x134f,0x7c1,0x559,0x559,0x559,0x1f84,0x122d,0x7c9,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x7cd,0x131f,0x1f94,0x1273,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x7d5,0x137f,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x12dc,0x1db3,0x1db3,0x1db3,0x1db3,0x1db3,0x1db3,0xfef,0x559,0x1ec0,0x1eb8,0x10e1,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x7dd,0x1f54,0x11d3,0x559,0x559,0x1235,0x1236,0x7e5,0x559,0x559,0x559,0x559, -0x559,0xebd,0xec5,0xecd,0xed5,0xedd,0xee5,0xeec,0xef0,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x7e9,0x1067,0x1e03,0x106d,0x1e03,0x1075,0x107a,0x107f, -0x107f,0x1e88,0x1ea8,0x1eb0,0x1f1c,0x1e90,0x1f74,0x1e98,0x1f24,0x1f7c,0x1f7c,0x119d,0x11a5,0x124d,0x1253,0x125b, -0x1263,0x1f9c,0x1f9c,0x1f9c,0x1f9c,0x12a7,0x1f9c,0x12ad,0x12b1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1, -0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1, -0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f2,0xb8a,0x18b1,0x18b1,0x18b1,0x7fa,0x7fa,0x7fa, -0x7fa,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x802,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa, -0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa, -0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa, -0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa, -0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0xbdd,0xbe4,0xbec,0xbf4,0x196e,0x196e,0x196e, -0xbfc,0xc04,0xc07,0x199e,0x1996,0xc3a,0xd56,0xd5a,0xd5e,0x559,0x559,0x559,0x559,0xd66,0x1b19,0xd6e, -0xf58,0x1822,0xb1d,0xb23,0x101f,0xc0f,0x19e2,0xca0,0x559,0x1837,0x182a,0x182f,0x1976,0xc17,0xc1f,0x114b, -0x1151,0x1d7b,0xf79,0x1d6b,0xf50,0x1327,0x132f,0x559,0x559,0x1da3,0x1da3,0x1da3,0x1da3,0x1da3,0x1da3,0x1da3, -0x1da3,0x1da3,0xfc7,0xfcf,0xfd7,0x12e4,0x12e8,0x559,0x559,0x1b21,0xd7e,0x1b29,0x1b29,0xd82,0xe8d,0xe95, -0xe9d,0x1bf1,0x1bd9,0x1bf9,0x1c01,0x1be9,0xe25,0xe29,0xe30,0xe38,0xe3c,0xe44,0xe4c,0xe4e,0xe4e,0xe4e, -0xe4e,0x1c62,0x1c6a,0x1c62,0x1c70,0x1c78,0x1c43,0x1c80,0x1c88,0x1c62,0x1c90,0x1c98,0x1c9f,0x1ca7,0x1c4b,0x1c62, -0x1cac,0x1c53,0x1c5a,0x1cb4,0x1cba,0x1d5c,0x1d63,0x1d55,0x1cc1,0x1cc9,0x1cd1,0x1cd9,0x1dd3,0x1ce1,0x1ce9,0xea5, -0xead,0x1c33,0x1c33,0x1c33,0xeb5,0x1d83,0x1d83,0xf81,0xf89,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e3b, -0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e40,0x1e3b,0x1e3b,0x1e3b,0x10a7,0x10a9,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, -0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, -0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, -0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, -0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1159,0x1c3b,0x1f14,0x1f14,0x1f14,0x1f14,0x1f14,0x1f14, -0x1f14,0x1f34,0x1161,0x123e,0x1245,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c, -0x117d,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd, -0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbf,0x1bbd,0x1bc7,0x1bbd,0x1bbd, -0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bca,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bd1,0x1209,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0, -0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0, -0x1ee4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1299, -0x129f,0x12b9,0x12bc,0x12bc,0x12bc,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, -0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, -0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, -0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, -0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18bc, -0x1387,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, -0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, -0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x138f,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, -0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, -0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, -0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, -0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4, -0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x13b6,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, -0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, -0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, -0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1393,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, -0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, -0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x139b,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, -0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, -0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, -0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, -0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1393,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, -0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, -0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, -0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, -0x13be,0x1cf1,0x1cf1,0x1cf1,0x1cf1,0x1cf1,0x1cf1,0x13c6,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, -0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, -0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, -0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, -0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x13ce,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, -0x1f2c,0x1f2c,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac, -0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x13d6,0x1fe4,0x1fe4,0x1fe4, -0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, -0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, -0x1fe4,0x1fe4,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, -0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, -0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, -0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, -0x1751,0x1741,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, -0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, -0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, -0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, -0x1759,0x1749,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, -0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, -0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, -0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, -0x1751,0x1751,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, -0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, -0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, -0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, -0x1759,0x1759,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, -0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, -0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, -0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, -0x18b9,0x18b9,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, -0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, -0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, -0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, -0x1c09,0x1c09,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, -0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, -0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, -0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, -0x1e80,0x1e80,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, -0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, -0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, -0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, -0x1ed8,0x1ed8,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, -0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, -0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, -0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, -0x1f2c,0x1f2c,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac, -0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac, -0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac, -0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac, -0x1fac,0x1fac,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, -0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, -0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, -0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, -0x1fe4,0x1fe4,0x538,0x538,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e5,0x2ee,0x2e8, -0x2e8,0x2eb,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2, -0x2e2,0x2e2,0x2e2,0x2e2,0x7da,0x7d4,0x7b9,0x79e,0x7aa,0x7a7,0x79e,0x7b6,0x7a4,0x7b0,0x79e,0x7cb, -0x7c2,0x7b3,0x7d7,0x7ad,0x79b,0x79b,0x79b,0x79b,0x79b,0x79b,0x79b,0x79b,0x79b,0x79b,0x7bf,0x7bc, -0x7c5,0x7c5,0x7c5,0x7d4,0x79e,0x7e6,0x7e6,0x7e6,0x7e6,0x7e6,0x7e6,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0, -0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7a4, -0x7aa,0x7b0,0x7d1,0x798,0x7ce,0x7e3,0x7e3,0x7e3,0x7e3,0x7e3,0x7e3,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd, -0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7a4, -0x7c8,0x7a1,0x7c5,0x2e2,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x300,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1, -0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1, -0x2f1,0x2f1,0x2f1,0x2f1,0x2f4,0x64b,0x7ef,0x7f2,0x651,0x7f2,0x7ec,0x645,0x63c,0x2fa,0x65a,0x2fd, -0x7f5,0x633,0x648,0x7e9,0x64e,0x657,0x639,0x639,0x63f,0x2f7,0x645,0x642,0x63c,0x639,0x65a,0x2fd, -0x636,0x636,0x636,0x64b,0x306,0x306,0x306,0x306,0x306,0x306,0x663,0x306,0x306,0x306,0x306,0x306, -0x306,0x306,0x306,0x306,0x663,0x306,0x306,0x306,0x306,0x306,0x306,0x654,0x663,0x306,0x306,0x306, -0x306,0x306,0x663,0x65d,0x660,0x660,0x303,0x303,0x303,0x303,0x65d,0x303,0x660,0x660,0x660,0x303, -0x660,0x660,0x303,0x303,0x65d,0x303,0x660,0x660,0x303,0x303,0x303,0x654,0x65d,0x660,0x660,0x303, -0x660,0x303,0x65d,0x303,0x312,0x669,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309, -0x312,0x309,0x312,0x309,0x30f,0x666,0x312,0x669,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x669, -0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x66f,0x666,0x312,0x309,0x312,0x669, -0x312,0x309,0x312,0x309,0x312,0x666,0x672,0x66c,0x312,0x309,0x312,0x309,0x666,0x312,0x309,0x312, -0x309,0x312,0x309,0x672,0x66c,0x66f,0x666,0x312,0x669,0x312,0x309,0x312,0x669,0x675,0x66f,0x666, -0x312,0x669,0x312,0x309,0x312,0x309,0x66f,0x666,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309, -0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x66f,0x666,0x312,0x309,0x312,0x669, -0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x312,0x309,0x312, -0x309,0x312,0x309,0x30c,0x315,0x321,0x321,0x315,0x321,0x315,0x321,0x321,0x315,0x321,0x321,0x321, -0x315,0x315,0x321,0x321,0x321,0x321,0x315,0x321,0x321,0x315,0x321,0x321,0x321,0x315,0x315,0x315, -0x321,0x321,0x315,0x321,0x324,0x318,0x321,0x315,0x321,0x315,0x321,0x321,0x315,0x321,0x315,0x315, -0x321,0x315,0x321,0x324,0x318,0x321,0x321,0x321,0x315,0x321,0x315,0x321,0x321,0x315,0x315,0x31e, -0x321,0x315,0x315,0x315,0x31e,0x31e,0x31e,0x31e,0x327,0x327,0x31b,0x327,0x327,0x31b,0x327,0x327, -0x31b,0x324,0x678,0x324,0x678,0x324,0x678,0x324,0x678,0x324,0x678,0x324,0x678,0x324,0x678,0x324, -0x678,0x315,0x324,0x318,0x324,0x318,0x324,0x318,0x321,0x315,0x324,0x318,0x324,0x318,0x324,0x318, -0x324,0x318,0x324,0x318,0x318,0x327,0x327,0x31b,0x324,0x318,0x9cf,0x9cf,0x9d2,0x9cc,0x324,0x318, -0x324,0x318,0x324,0x318,0x324,0x318,0x324,0x318,0x324,0x318,0x324,0x318,0x324,0x318,0x324,0x318, -0x324,0x318,0x324,0x318,0x324,0x318,0x324,0x318,0x9d2,0x9cc,0x9d2,0x9cc,0x9cf,0x9c9,0x9d2,0x9cc, -0xb8b,0xc84,0x9cf,0x9c9,0x9cf,0x9c9,0x9d2,0x9cc,0x9d2,0x9cc,0x9d2,0x9cc,0x9d2,0x9cc,0x9d2,0x9cc, -0x9d2,0x9cc,0x9d2,0x9cc,0xc84,0xc84,0xc84,0xd7d,0xd7d,0xd7d,0xd80,0xd80,0xd7d,0xd80,0xd80,0xd7d, -0xd7d,0xd80,0xebe,0xec1,0xec1,0xec1,0xec1,0xebe,0xec1,0xebe,0xec1,0xebe,0xec1,0xebe,0xec1,0xebe, -0x32a,0x67b,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a, -0x32a,0x67b,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a, -0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a, -0x32d,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a, -0x32a,0x32a,0x32a,0x32a,0x32a,0x9d5,0x9d5,0x9d5,0x9d5,0x9d5,0xc87,0xc87,0x342,0x342,0x342,0x342, -0x342,0x342,0x342,0x342,0x342,0x339,0x339,0x339,0x339,0x339,0x339,0x339,0x336,0x336,0x333,0x333, -0x681,0x333,0x339,0x684,0x33c,0x684,0x684,0x684,0x33c,0x684,0x339,0x339,0x687,0x33f,0x333,0x333, -0x333,0x333,0x333,0x333,0x67e,0x67e,0x67e,0x67e,0x330,0x67e,0x333,0xb01,0x342,0x342,0x342,0x342, -0x342,0x333,0x333,0x333,0x333,0x333,0x9de,0x9de,0x9db,0x9d8,0x9db,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a, -0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0x68a,0x68a,0x68a,0x68a, -0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a, -0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a, -0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a, -0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68d,0x68d,0x92d,0x68d, -0x68d,0x930,0xb04,0xb04,0xb04,0xb04,0xb04,0xb04,0xb04,0xb04,0xb04,0xc3c,0xd47,0xd47,0xd47,0xd47, -0xd47,0xd47,0xd47,0xd47,0xe82,0xe82,0xe82,0xe82,0xe85,0xd4a,0xd4a,0xd4a,0x690,0x690,0xb07,0xc81, -0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xf6c,0xf69,0xf6c,0xf69, -0x34e,0x357,0xf6c,0xf69,9,9,0x35d,0xec4,0xec4,0xec4,0x345,0x14af,9,9,9,9, -0x35a,0x348,0x36c,0x34b,0x36c,0x36c,0x36c,9,0x36c,9,0x36c,0x36c,0x363,0x696,0x696,0x696, -0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,9,0x696, -0x696,0x696,0x696,0x696,0x696,0x696,0x36c,0x36c,0x363,0x363,0x363,0x363,0x363,0x693,0x693,0x693, -0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x360,0x693, -0x693,0x693,0x693,0x693,0x693,0x693,0x363,0x363,0x363,0x363,0x363,0xf6c,0x36f,0x36f,0x372,0x36c, -0x36c,0x36f,0x366,0x9e1,0xb94,0xb91,0x369,0x9e1,0x369,0x9e1,0x369,0x9e1,0x369,0x9e1,0x354,0x351, -0x354,0x351,0x354,0x351,0x354,0x351,0x354,0x351,0x354,0x351,0x354,0x351,0x36f,0x36f,0x366,0x360, -0xb43,0xb40,0xb8e,0xc90,0xc8d,0xc93,0xc90,0xc8d,0xd83,0xd86,0xd86,0xd86,0x9f0,0x6a2,0x37e,0x381, -0x37e,0x37e,0x37e,0x381,0x37e,0x37e,0x37e,0x37e,0x381,0x9f0,0x381,0x37e,0x69f,0x69f,0x69f,0x69f, -0x69f,0x69f,0x69f,0x69f,0x69f,0x6a2,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f, -0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x699,0x699,0x699,0x699, -0x699,0x699,0x699,0x699,0x699,0x69c,0x699,0x699,0x699,0x699,0x699,0x699,0x699,0x699,0x699,0x699, -0x699,0x699,0x699,0x699,0x699,0x699,0x699,0x699,0x9ea,0x69c,0x378,0x37b,0x378,0x378,0x378,0x37b, -0x378,0x378,0x378,0x378,0x37b,0x9ea,0x37b,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378, -0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x381,0x37b, -0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x375,0x939,0x93c,0x91e,0x91e,0x1116, -0x9e4,0x9e4,0xb9a,0xb97,0x9ed,0x9e7,0x9ed,0x9e7,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378, -0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378, -0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378, -0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x381,0x37b,0x37e,0x378,0xb9a,0xb97,0x37e, -0x378,0xb9a,0xb97,0x37e,0x378,0xb9a,0xb97,0xec7,0x381,0x37b,0x381,0x37b,0x37e,0x378,0x381,0x37b, -0x37e,0x378,0x381,0x37b,0x381,0x37b,0x381,0x37b,0x37e,0x378,0x381,0x37b,0x381,0x37b,0x381,0x37b, -0x37e,0x378,0x381,0x37b,0x9f0,0x9ea,0x381,0x37b,0x381,0x37b,0x381,0x37b,0x381,0x37b,0xd8c,0xd89, -0x381,0x37b,0xeca,0xec7,0xeca,0xec7,0xeca,0xec7,0xc00,0xbfd,0xc00,0xbfd,0xc00,0xbfd,0xc00,0xbfd, -0xc00,0xbfd,0xc00,0xbfd,0xc00,0xbfd,0xc00,0xbfd,0xef7,0xef4,0xef7,0xef4,0xfe7,0xfe4,0xfe7,0xfe4, -0xfe7,0xfe4,0xfe7,0xfe4,0xfe7,0xfe4,0xfe7,0xfe4,0xfe7,0xfe4,0xfe7,0xfe4,0x114f,0x114c,0x1329,0x1326, -0x14e5,0x14e2,0x14e5,0x14e2,0x14e5,0x14e2,0x14e5,0x14e2,0xc,0x393,0x393,0x393,0x393,0x393,0x393,0x393, -0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393, -0x393,0x393,0x393,0xc,0xc,0x396,0x384,0x384,0x384,0x38a,0x384,0x387,0x18ea,0x38d,0x38d,0x38d, -0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d, -0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x390, -0x18ea,0x399,0x9f3,0xc,0xc,0x14b2,0x14b2,0x13ce,0xf,0x960,0x960,0x960,0x960,0x960,0x960,0x960, -0x960,0x960,0x960,0x960,0x960,0x960,0x960,0x960,0x960,0x960,0xd8f,0x960,0x960,0x960,0x960,0x960, -0x960,0x960,0x960,0x960,0x960,0x960,0x960,0x960,0x39c,0x39c,0x39c,0x39c,0x39c,0x39c,0x39c,0x39c, -0x39c,0x39c,0xecd,0x39c,0x39c,0x39c,0x3a8,0x39c,0x39f,0x39c,0x39c,0x3ab,0x963,0xd92,0xd95,0xd92, -0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae, -0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae, -0x3ae,0x3ae,0x3ae,0xf,0xf,0xf,0xf,0x18ed,0x3ae,0x3ae,0x3ae,0x3a5,0x3a2,0xf,0xf,0xf, -0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xca8,0xca8,0xca8,0xca8,0x13d1,0x14b5,0xf75,0xf75, -0xf75,0xf72,0xf72,0xd9b,0x8a6,0xca2,0xc9f,0xc9f,0xc96,0xc96,0xc96,0xc96,0xc96,0xc96,0xf6f,0xf6f, -0xf6f,0xf6f,0xf6f,0x8a3,0x14ac,0x1afd,0xd9e,0x8a9,0x12f0,0x3c9,0x3cc,0x3cc,0x3cc,0x3cc,0x3cc,0x3c9, -0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9, -0x3c9,0x3c9,0x3c9,0xf78,0xf78,0xf78,0xf78,0xf78,0x8ac,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9, -0x3c9,0x3c9,0x3c9,0x924,0x924,0x924,0x924,0x924,0x924,0x924,0x924,0xb3a,0xb3a,0xb3a,0xc96,0xc9c, -0xc99,0xd98,0xd98,0xd98,0xd98,0xd98,0xd98,0x12ed,0x93f,0x93f,0x93f,0x93f,0x93f,0x93f,0x93f,0x93f, -0x93f,0x93f,0x3c3,0x3c0,0x3bd,0x3ba,0xb9d,0xb9d,0x921,0x3c9,0x3c9,0x3d5,0x3c9,0x3cf,0x3cf,0x3cf, -0x3cf,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9, -0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9, -0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9, -0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x9f9,0x9f9,0x3c9,0x3c9, -0x3c9,0x3c9,0x3c9,0x9f9,0x3cc,0x3c9,0x3cc,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9, -0x3c9,0x3c9,0x3c9,0x9f9,0x3c9,0x3c9,0x3c9,0x3cc,0x942,0x3c9,0x3b4,0x3b4,0x3b4,0x3b4,0x3b4,0x3b4, -0x3b4,0x3b1,0x3ba,0x3b7,0x3b7,0x3b4,0x3b4,0x3b4,0x3b4,0x3d2,0x3d2,0x3b4,0x3b4,0x3ba,0x3b7,0x3b7, -0x3b7,0x3b4,0xca5,0xca5,0x3c6,0x3c6,0x3c6,0x3c6,0x3c6,0x3c6,0x3c6,0x3c6,0x3c6,0x3c6,0x9f9,0x9f9, -0x9f9,0x9f6,0x9f6,0xca5,0xa0e,0xa0e,0xa0e,0xa08,0xa08,0xa08,0xa08,0xa08,0xa08,0xa08,0xa08,0xa05, -0xa08,0xa05,0x12,0xa11,0xa0b,0x9fc,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b, -0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b, -0xa0b,0xcab,0xcab,0xcab,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02, -0xa02,0xa02,0xa02,0xa02,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x12, -0x12,0xcab,0xcab,0xcab,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb, -0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb, -0xdfb,0xdfb,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9, -0xff9,0xff9,0xff9,0xff9,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17, -0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17, -0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa14,0xa14,0xa14,0xa14,0xa14,0xa14, -0xa14,0xa14,0xa14,0xa14,0xa14,0xba0,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, -0x15,0x15,0x15,0x15,0xf0f,0xf0f,0xf0f,0xf0f,0xf0f,0xf0f,0xf0f,0xf0f,0xf0f,0xf0f,0xf12,0xf12, -0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12, -0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf06, -0xf06,0xf06,0xf06,0xf06,0xf06,0xf06,0xf06,0xf06,0xf15,0xf15,0xf09,0xf09,0xf0c,0xf1b,0xf18,0x102, -0x102,0x1911,0x1914,0x1914,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0xb13,0xb13,0xb16,0xb16,0xb13,0xb13,0xb13,0xb13,0xb13,0xb13,0xb13,0xb13, -0x6f,0x6f,0x6f,0x6f,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1617,0x1617,0x1617,0x1617,0x1617, -0x1617,0x1617,0x1617,0x1617,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1ec,0x1ec,0x1ec,0x1ec,0x1ec,0x1ec,0x1ec,0x1ec,0x1ec,0x1650,0x1650,0x1650, -0x1650,0x1650,0x1650,0x1650,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x16b,0x16b,0x16b, -0x16b,0x16b,0x16b,0x16b,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1bdb,0x1bd8,0x1c2,0x1c2,0x1c2,0x1c2,0x1c2,0x1c2,0x1c2,0x1c2,0x1c2,0x1c2, -0x1c2,0x1c2,0x1c2,0x1c2,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1488,0x1488,0x1488,0x1488,0x1488,0x1488,0x1488,0x1488,0x1488,0x1488,0x1a7,0x1a7, -0x1a7,0x1a7,0x1a7,0x1a7,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1b5a,0x1b5a,0x1b5a,0x1b5a,0x1b5a,0x1b5a,0x1b5a,0x201,0x201,0x201,0x201,0x201, -0x201,0x201,0x201,0x201,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x249,0x249,0x249,0x249,0x249,0x249,0x249,0x249,0x249,0x249,0x249,0x249, -0x249,0x249,0x249,0x249,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x195f,0x195f,0x195f,0x195f,0x195f,0x195f,0x195f,0x195f,0x195f,0x195f,0x24f,0x24f, -0x24f,0x24f,0x24f,0x24f,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1ac1,0x28b,0x28b,0x28b,0x28b,0x28b,0x28b,0x28b,0x28b,0x28b,0x28b,0x28b, -0x28b,0x28b,0x28b,0x28b,0x1752,0x1752,0x1752,0x1752,0x207,0x207,0x207,0x207,0x207,0x207,0x207,0x207, -0x207,0x207,0x207,0x207,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c, -0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e, -0x163e,0x163e,0x163e,0x163e,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1bc3,0x1bc3,0x1bc3,0x1bc3,0x2a0,0x1bc3,0x1bc3,0x1bc3,0x1bc3,0x1bc3,0x1bc3,0x1bc3, -0x2a0,0x1bc3,0x1bc3,0x2a0,0x16b6,0x16b6,0x16b6,0x16b6,0x1ef,0x1ef,0x1ef,0x1ef,0x1ef,0x1ef,0x1ef,0x1ef, -0x1ef,0x1ef,0x1ef,0x1ef,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5, -0x2b5,0x2b5,0x2b5,0x2b5,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0xdf2,0xdf2,0xdef,0xdef,0xdef,0xdf2,0xd5,0xd5,0xd5,0xd5,0xd5,0xd5, -0xd5,0xd5,0xd5,0xd5,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x213,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a, -0x176a,0x176a,0x176a,0x176a,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb, -0x2bb,0x2bb,0x2bb,0x1bf6,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1a13,0x1a13,0x1a13,0x1a13,0x1a13,0x1a13,0x1a13,0x1a13,0x1a13,0x1a13,0x270,0x270, -0x270,0x270,0x1a16,0x1a10,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93, -0x1b93,0x1b93,0x1b93,0x1b93,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26, -0x1c26,0x1c26,0x1c26,0x1c26,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0x255,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974, -0x1974,0x1974,0x1974,0x1974,0x273,0x273,0x273,0x273,0x273,0x273,0x273,0x273,0x273,0x273,0x273,0x273, -0x273,0x273,0x273,0x273,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0x95d,0x95d,3,3,3,3,3,3,3,3,3,3,3,3, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, -3,3,3,3,3,3,0x95d,0x95d,6,6,6,6,6,6,6,6, -6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, -6,6,6,6,6,6,6,6,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50, -0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,6,6,6,6,6,6,6,6, -6,6,6,6,6,6,6,6,0x14bb,0x3f0,0x3ff,0x3ff,0x18,0x405,0x405,0x405, -0x405,0x405,0x405,0x405,0x405,0x18,0x18,0x405,0x405,0x18,0x18,0x405,0x405,0x405,0x405,0x405, -0x405,0x405,0x405,0x405,0x405,0x405,0x405,0x405,0x405,0x18,0x405,0x405,0x405,0x405,0x405,0x405, -0x405,0x18,0x405,0x18,0x18,0x18,0x405,0x405,0x405,0x405,0x18,0x18,0x3f3,0xcb1,0x3f0,0x3ff, -0x3ff,0x3f0,0x3f0,0x3f0,0x3f0,0x18,0x18,0x3ff,0x3ff,0x18,0x18,0x402,0x402,0x3f6,0xda4,0x18, -0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3f0,0x18,0x18,0x18,0x18,0x408,0x408,0x18,0x408, -0x405,0x405,0x3f0,0x3f0,0x18,0x18,0x948,0x948,0x948,0x948,0x948,0x948,0x948,0x948,0x948,0x948, -0x405,0x405,0x3fc,0x3fc,0x3f9,0x3f9,0x3f9,0x3f9,0x3f9,0x3fc,0x3f9,0x1125,0x184b,0x1848,0x18f0,0x18, -0x1b,0xcb4,0x40b,0xcb7,0x1b,0x417,0x417,0x417,0x417,0x417,0x417,0x1b,0x1b,0x1b,0x1b,0x417, -0x417,0x1b,0x1b,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x417, -0x417,0x1b,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x1b,0x417,0x41a,0x1b,0x417,0x41a,0x1b, -0x417,0x417,0x1b,0x1b,0x40e,0x1b,0x414,0x414,0x414,0x40b,0x40b,0x1b,0x1b,0x1b,0x1b,0x40b, -0x40b,0x1b,0x1b,0x40b,0x40b,0x411,0x1b,0x1b,0x1b,0xf81,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, -0x1b,0x41a,0x41a,0x41a,0x417,0x1b,0x41a,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x94b,0x94b, -0x94b,0x94b,0x94b,0x94b,0x94b,0x94b,0x94b,0x94b,0x40b,0x40b,0x417,0x417,0x417,0xf81,0x18f3,0x1b, -0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1e,0x41d,0x41d,0x426,0x1e,0x429,0x429,0x429, -0x429,0x429,0x429,0x429,0xcc0,0x429,0x1e,0x429,0x429,0x429,0x1e,0x429,0x429,0x429,0x429,0x429, -0x429,0x429,0x429,0x429,0x429,0x429,0x429,0x429,0x429,0x1e,0x429,0x429,0x429,0x429,0x429,0x429, -0x429,0x1e,0x429,0x429,0x1e,0x429,0x429,0x429,0x429,0x429,0x1e,0x1e,0x420,0x429,0x426,0x426, -0x426,0x41d,0x41d,0x41d,0x41d,0x41d,0x1e,0x41d,0x41d,0x426,0x1e,0x426,0x426,0x423,0x1e,0x1e, -0x429,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, -0x429,0xcc0,0xcba,0xcba,0x1e,0x1e,0x94e,0x94e,0x94e,0x94e,0x94e,0x94e,0x94e,0x94e,0x94e,0x94e, -0x13d4,0xcbd,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x16cb,0x184e,0x184e,0x184e,0x1851,0x1851,0x1851, -0x21,0x42c,0x43b,0x43b,0x21,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x21,0x21,0x441, -0x441,0x21,0x21,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441, -0x441,0x21,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x21,0x441,0x441,0x21,0xcc3,0x441,0x441, -0x441,0x441,0x21,0x21,0x42f,0x441,0x42c,0x42c,0x43b,0x42c,0x42c,0x42c,0xf84,0x21,0x21,0x43b, -0x43e,0x21,0x21,0x43e,0x43e,0x432,0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x1a5b,0x42c,0x42c, -0x21,0x21,0x21,0x21,0x444,0x444,0x21,0x441,0x441,0x441,0xf84,0xf84,0x21,0x21,0x438,0x438, -0x438,0x438,0x438,0x438,0x438,0x438,0x438,0x438,0x435,0xcc3,0x12fc,0x12fc,0x12fc,0x12fc,0x12fc,0x12fc, -0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x24,0x24,0x447,0x453,0x24,0x453,0x453,0x453, -0x453,0x453,0x453,0x24,0x24,0x24,0x453,0x453,0x453,0x24,0x453,0x453,0x456,0x453,0x24,0x24, -0x24,0x453,0x453,0x24,0x453,0x24,0x453,0x453,0x24,0x24,0x24,0x453,0x453,0x24,0x24,0x24, -0x453,0x453,0x453,0x24,0x24,0x24,0x453,0x453,0x453,0x453,0x453,0x453,0x453,0x453,0xda7,0x453, -0x453,0x453,0x24,0x24,0x24,0x24,0x447,0x44d,0x447,0x44d,0x44d,0x24,0x24,0x24,0x44d,0x44d, -0x44d,0x24,0x450,0x450,0x450,0x44a,0x24,0x24,0xf87,0x24,0x24,0x24,0x24,0x24,0x24,0x447, -0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0xebb,0x954,0x954,0x954,0x954,0x954, -0x954,0x954,0x954,0x954,0x951,0x951,0x951,0xd77,0xcc6,0xcc6,0xcc6,0xcc6,0xcc6,0xcc9,0xcc6,0x24, -0x24,0x24,0x24,0x24,0x14be,0x465,0x465,0x465,0x18f6,0x468,0x468,0x468,0x468,0x468,0x468,0x468, -0x468,0x27,0x468,0x468,0x468,0x27,0x468,0x468,0x468,0x468,0x468,0x468,0x468,0x468,0x468,0x468, -0x468,0x468,0x468,0x468,0x468,0x27,0x468,0x468,0x468,0x468,0x468,0x468,0x468,0x468,0x468,0x468, -0x14c1,0x468,0x468,0x468,0x468,0x468,0x27,0x27,0x1b00,0xf90,0x459,0x459,0x459,0x465,0x465,0x465, -0x465,0x27,0x459,0x459,0x45c,0x27,0x459,0x459,0x459,0x45f,0x27,0x27,0x27,0x27,0x27,0x27, -0x27,0x459,0x459,0x27,0xf90,0xf90,0x16ce,0x27,0x27,0x1b03,0x27,0x27,0x468,0x468,0xf8a,0xf8a, -0x27,0x27,0x462,0x462,0x462,0x462,0x462,0x462,0x462,0x462,0x462,0x462,0x27,0x27,0x27,0x27, -0x27,0x27,0x27,0x19bf,0xf8d,0xf8d,0xf8d,0xf8d,0xf8d,0xf8d,0xf8d,0xf8d,0x178e,0x14c4,0x471,0x471, -0x18f9,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x2a,0x477,0x477,0x477,0x2a,0x477,0x477, -0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x2a,0x477,0x477, -0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x2a,0x477,0x477,0x477,0x477,0x477,0x2a,0x2a, -0xccc,0xccf,0x471,0x46b,0x474,0x471,0x46b,0x471,0x471,0x2a,0x46b,0x474,0x474,0x2a,0x474,0x474, -0x46b,0x46e,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x46b,0x46b,0x2a,0x2a,0x2a,0x2a,0x2a, -0x2a,0x1b06,0x477,0x2a,0x477,0x477,0xed3,0xed3,0x2a,0x2a,0x957,0x957,0x957,0x957,0x957,0x957, -0x957,0x957,0x957,0x957,0x2a,0xed6,0xed6,0x1bc9,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, -0x2a,0x2a,0x2a,0x2a,0x1854,0x14c7,0x483,0x483,0x1a5e,0x489,0x489,0x489,0x489,0x489,0x489,0x489, -0x489,0x2d,0x489,0x489,0x489,0x2d,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489, -0x489,0x489,0x489,0x489,0x483,0x47a,0x47a,0x47a,0xf93,0x2d,0x483,0x483,0x483,0x2d,0x486,0x486, -0x486,0x47d,0x1302,0x1791,0x2d,0x2d,0x2d,0x2d,0x1794,0x1794,0x1794,0x47a,0x1791,0x1791,0x1791,0x1791, -0x1791,0x1791,0x1791,0x16d1,0x489,0x489,0xf93,0xf93,0x2d,0x2d,0x480,0x480,0x480,0x480,0x480,0x480, -0x480,0x480,0x480,0x480,0xf96,0xf96,0xf96,0xf96,0xf96,0xf96,0x1791,0x1791,0x1791,0xf99,0xf9c,0xf9c, -0xf9c,0xf9c,0xf9c,0xf9c,0x30,0x1a61,0xa23,0xa23,0x30,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29, -0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0x30,0x30,0x30,0xa29,0xa29, -0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29, -0xa29,0xa29,0x30,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0x30,0xa29,0x30,0x30, -0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0x30,0x30,0x30,0xa1d,0x30,0x30,0x30,0x30,0xa1a, -0xa23,0xa23,0xa1a,0xa1a,0xa1a,0x30,0xa1a,0x30,0xa23,0xa23,0xa26,0xa23,0xa26,0xa26,0xa26,0xa1a, -0x30,0x30,0x30,0x30,0x30,0x30,0x14ca,0x14ca,0x14ca,0x14ca,0x14ca,0x14ca,0x14ca,0x14ca,0x14ca,0x14ca, -0x30,0x30,0xa23,0xa23,0xa20,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, -0x33,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4, -0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4, -0x4a4,0x48f,0x4a4,0x4a1,0x48f,0x48f,0x48f,0x48f,0x48f,0x48f,0x495,0x33,0x33,0x33,0x33,0x48c, -0x4aa,0x4aa,0x4aa,0x4aa,0x4aa,0x4a4,0x4a7,0x492,0x492,0x492,0x492,0x492,0x492,0x48f,0x492,0x498, -0x49e,0x49e,0x49e,0x49e,0x49e,0x49e,0x49e,0x49e,0x49e,0x49e,0x49b,0x49b,0x33,0x33,0x33,0x33, -0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, -0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x36,0x4b9,0x4b9,0x36, -0x4b9,0x36,0x19c5,0x4b9,0x4b9,0x19c5,0x4b9,0x36,0x19c5,0x4b9,0x19c5,0x19c5,0x19c5,0x19c5,0x19c5,0x19c5, -0x4b9,0x4b9,0x4b9,0x4b9,0x19c5,0x4b9,0x4b9,0x4b9,0x4b9,0x4b9,0x4b9,0x4b9,0x19c5,0x4b9,0x4b9,0x4b9, -0x36,0x4b9,0x36,0x4b9,0x19c5,0x19c5,0x4b9,0x4b9,0x19c5,0x4b9,0x4b9,0x4b9,0x4b9,0x4ad,0x4b9,0x4b6, -0x4ad,0x4ad,0x4ad,0x4ad,0x4ad,0x4ad,0x19c2,0x4ad,0x4ad,0x4b9,0x36,0x36,0x4c2,0x4c2,0x4c2,0x4c2, -0x4c2,0x36,0x4bf,0x36,0x4b0,0x4b0,0x4b0,0x4b0,0x4b0,0x4ad,0x1bcc,0x36,0x4b3,0x4b3,0x4b3,0x4b3, -0x4b3,0x4b3,0x4b3,0x4b3,0x4b3,0x4b3,0x36,0x36,0x4bc,0x4bc,0x13d7,0x13d7,0x36,0x36,0x36,0x36, -0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x99c,0x99c,0x99c,0x99f, -0x99c,0x99c,0x99c,0x99c,0x39,0x99c,0x99c,0x99c,0x99c,0x99f,0x99c,0x99c,0x99c,0x99c,0x99f,0x99c, -0x99c,0x99c,0x99c,0x99f,0x99c,0x99c,0x99c,0x99c,0x99f,0x99c,0x99c,0x99c,0x99c,0x99c,0x99c,0x99c, -0x99c,0x99c,0x99c,0x99c,0x99c,0x99f,0xa38,0xfa8,0xfa8,0x39,0x39,0x39,0x39,0x966,0x966,0x969, -0x966,0x969,0x969,0x975,0x969,0x975,0x966,0x966,0x966,0x966,0x966,0x996,0x966,0x969,0x96f,0x96f, -0x972,0x97b,0x96c,0x96c,0x99c,0x99c,0x99c,0x99c,0x130b,0x1305,0x1305,0x1305,0x966,0x966,0x966,0x969, -0x966,0x966,0xa2c,0x966,0x39,0x966,0x966,0x966,0x966,0x969,0x966,0x966,0x966,0x966,0x969,0x966, -0x966,0x966,0x966,0x969,0x966,0x966,0x966,0x966,0x969,0x966,0xa2c,0xa2c,0xa2c,0x966,0x966,0x966, -0x966,0x966,0x966,0x966,0xa2c,0x969,0xa2c,0xa2c,0xa2c,0x39,0xa35,0xa35,0xa32,0xa32,0xa32,0xa32, -0xa32,0xa32,0xa2f,0xa32,0xa32,0xa32,0xa32,0xa32,0xa32,0x39,0xf9f,0xa32,0xdaa,0xdaa,0xfa2,0xfa5, -0xf9f,0x1128,0x1128,0x1128,0x1128,0x1308,0x1308,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, -0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, -0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x3c,0x13dd, -0x3c,0x3c,0x3c,0x3c,0x3c,0x13dd,0x3c,0x3c,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5, -0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xdb9, -0xa62,0x3f,0xa62,0xa62,0xa62,0xa62,0x3f,0x3f,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0x3f, -0xa62,0x3f,0xa62,0xa62,0xa62,0xa62,0x3f,0x3f,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xdb9, -0xa62,0x3f,0xa62,0xa62,0xa62,0xa62,0x3f,0x3f,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62, -0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xdb9,0xa62,0x3f,0xa62,0xa62, -0xa62,0xa62,0x3f,0x3f,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0x3f,0xa62,0x3f,0xa62,0xa62, -0xa62,0xa62,0x3f,0x3f,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xdb9,0xa62,0xa62,0xa62,0xa62, -0xa62,0xa62,0xa62,0x3f,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62, -0xa62,0xa62,0xa62,0xdb9,0xa62,0x3f,0xa62,0xa62,0xa62,0xa62,0x3f,0x3f,0xa62,0xa62,0xa62,0xa62, -0xa62,0xa62,0xa62,0xdb9,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62, -0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0x3f,0x3f,0x130e,0x130e,0xdb3,0xdb6,0xa5c,0xa65,0xa59, -0xa59,0xa59,0xa59,0xa65,0xa65,0xa5f,0xa5f,0xa5f,0xa5f,0xa5f,0xa5f,0xa5f,0xa5f,0xa5f,0xa56,0xa56, -0xa56,0xa56,0xa56,0xa56,0xa56,0xa56,0xa56,0xa56,0xa56,0x3f,0x3f,0x3f,0xa68,0xa68,0xa68,0xa68, -0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68, -0xa68,0x16d7,0x42,0x42,0x16d4,0x16d4,0x16d4,0x16d4,0x16d4,0x16d4,0x42,0x42,0xa7a,0xa7d,0xa7d,0xa7d, -0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d, -0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa77,0xa74,0x45,0x45,0x45,0xa83,0xa83,0xa83,0xa83, -0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa80,0xa80,0xa80,0xa83,0xa83,0xa83,0x14cd,0x14cd,0x14cd, -0x14cd,0x14cd,0x14cd,0x14cd,0x14cd,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0xaa4,0xaa4,0xaa4,0xaa4, -0xaa4,0xaa4,0xa86,0xaa4,0xaa4,0xa89,0xa89,0xa89,0xa89,0xa89,0xa89,0xa89,0xa89,0xa89,0xa8c,0xa89, -0xa9b,0xa9b,0xa9e,0xaa7,0xa95,0xa92,0xa9b,0xa98,0xaa7,0xcd2,0x4b,0x4b,0xaa1,0xaa1,0xaa1,0xaa1, -0xaa1,0xaa1,0xaa1,0xaa1,0xaa1,0xaa1,0x4b,0x4b,0x4b,0x4b,0x4b,0x4b,0xcd5,0xcd5,0xcd5,0xcd5, -0xcd5,0xcd5,0xcd5,0xcd5,0xcd5,0xcd5,0x4b,0x4b,0x4b,0x4b,0x4b,0x4b,0xab6,0xab6,0xb2e,0xb31, -0xabc,0xb2b,0xab9,0xab6,0xabf,0xace,0xac2,0xad1,0xad1,0xad1,0xaad,0x1b09,0xac5,0xac5,0xac5,0xac5, -0xac5,0xac5,0xac5,0xac5,0xac5,0xac5,0x4e,0x4e,0x4e,0x4e,0x4e,0x4e,0xac8,0xac8,0xac8,0xac8, -0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8, -0xac8,0xac8,0xac8,0xac8,0x18fc,0x4e,0x4e,0x4e,0x4e,0x4e,0x4e,0x4e,0xac8,0xac8,0xac8,0xac8, -0xac8,0xac8,0xac8,0xac8,0xac8,0xab0,0xfc6,0x4e,0x4e,0x4e,0x4e,0x4e,0x117f,0x117f,0x117f,0x117f, -0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x4e6,0x4e6,0x4e6,0x4e6, -0x4e6,0x4e6,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e6,0x4e6,0x4e6,0x4e6, -0x4e6,0x4e6,0x51,0x51,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x51,0x51,0x4e6,0x4e6,0x4e6,0x4e6, -0x4e6,0x4e6,0x4e6,0x4e6,0x51,0x4e9,0x51,0x4e9,0x51,0x4e9,0x51,0x4e9,0x4e6,0x4e6,0x4e6,0x4e6, -0x4e6,0x4e6,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e6,0x4e6,0x4e6,0x4e6, -0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x51,0x51,0x4e6,0x4e6,0x4e6,0x4e6, -0x4e6,0x4e6,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e6,0x4e6,0x4e6,0x4e6, -0x4e6,0x51,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e0,0x4e6,0x4e0,0x4e0,0x4dd,0x4e6,0x4e6, -0x4e6,0x51,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4dd,0x4dd,0x4dd,0x4e6,0x4e6,0x4e6,0x4e6, -0x51,0x51,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x51,0x4dd,0x4dd,0x4dd,0x4e6,0x4e6,0x4e6,0x4e6, -0x4e6,0x4e6,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4dd,0x4dd,0x4dd,0x51,0x51,0x4e6,0x4e6, -0x4e6,0x51,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e3,0x4e0,0x51,0xba6,0xba9,0xba9,0xba9, -0xfcf,0x54,0x14a9,0x14a9,0x14a9,0x14a9,0x4f2,0x4f2,0x4f2,0x4f2,0x4f2,0x4f2,0x53d,0xbbb,0x57,0x57, -0x6d8,0x53d,0x53d,0x53d,0x53d,0x53d,0x543,0x555,0x543,0x54f,0x549,0x6db,0x53a,0x6d5,0x6d5,0x6d5, -0x6d5,0x53a,0x53a,0x53a,0x53a,0x53a,0x540,0x552,0x540,0x54c,0x546,0x57,0xdc2,0xdc2,0xdc2,0xdc2, -0xdc2,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x57,0x57,0x57,0x1b0c,0x5a,0x5a,0x5a, -0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x564,0x564,0x564,0x564, -0x564,0x564,0x564,0x564,0x564,0x564,0x564,0x564,0x564,0x561,0x561,0x561,0x561,0x564,0xadd,0xadd, -0xbc1,0xbc7,0xbc7,0xbc4,0xbc4,0xbc4,0xbc4,0xdc8,0xed9,0xed9,0xed9,0xed9,0x1113,0x5d,0x5d,0x5d, -0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x594,0x594,0x594,0xae6, -0xee2,0xfd5,0xfd5,0xfd5,0xfd5,0x126f,0x16dd,0x16dd,0x60,0x60,0x60,0x60,0x702,0x702,0x702,0x702, -0x702,0x702,0x702,0x702,0x702,0x702,0x5a0,0x5a0,0x59d,0x59d,0x59d,0x59d,0x5c1,0x5c1,0x5c1,0x5c1, -0x5c1,0xaef,0xaef,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63, -0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x5c4,0x5c4,0x5c4,0x5c4, -0x5c4,0x5c4,0x5c4,0x5c4,0x5c4,0x5c4,0x5c4,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66, -0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0xb0a,0xb0a,0xb0a,0xb0a, -0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a, -0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0x69,0xb0a,0xb0a,0xb0a,0xb0a,0xb0d,0xb0a,0xb0a,0xb0a,0xb0a, -0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0d, -0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0xb10,0xb10,0xb10,0xb10, -0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10, -0xb10,0xb10,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x72,0x822,0x81c,0x822, -0x81c,0x822,0x81c,0x822,0x81c,0x822,0x81c,0x81c,0x81f,0x81c,0x81f,0x81c,0x81f,0x81c,0x81f,0x81c, -0x81f,0x81c,0x81f,0x81c,0x81f,0x81c,0x81f,0x81c,0x81f,0x81c,0x81f,0x81c,0x81c,0x81c,0x81c,0x822, -0x81c,0x822,0x81c,0x822,0x81c,0x81c,0x81c,0x81c,0x81c,0x81c,0x822,0x81c,0x81c,0x81c,0x81c,0x81c, -0x81f,0xc60,0xc60,0x72,0x72,0x936,0x936,0x8fd,0x8fd,0x825,0x828,0xc5d,0x75,0x75,0x75,0x75, -0x75,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a, -0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x1101,0x18c3,0x19aa, -0x78,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d, -0x83d,0x83d,0x83d,0x78,0x906,0x906,0x909,0x909,0x909,0x909,0x909,0x909,0x909,0x909,0x909,0x909, -0x909,0x909,0x909,0x909,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846, -0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846, -0x846,0xd5c,0xd5c,0x7b,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22, -0xb22,0x7e,0x7e,0x7e,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28, -0xb28,0xb28,0xb28,0xb28,0xb28,0xc69,0xb28,0xb28,0xb28,0xc69,0xb28,0x81,0x81,0x81,0x81,0x81, -0x81,0x81,0x81,0x81,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6, -0x11a6,0x11a6,0x11a6,0x11a6,0x9c0,0x9c0,0x9c0,0x9c0,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, -0x84,0x84,0x84,0x84,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b, -0x121b,0x121b,0x121b,0x121b,0x609,0x609,0x609,0x609,0x609,0x609,0x609,0x87,0x87,0x87,0x87,0x87, -0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x5f7,0x5f7,0x5f7,0x5f7,0x5f7,0x87,0x87,0x87,0x87, -0x87,0xafb,0x5fa,0x600,0x606,0x606,0x606,0x606,0x606,0x606,0x606,0x606,0x606,0x5fd,0x600,0x600, -0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x87,0x600,0x600,0x600,0x600, -0x600,0x87,0x600,0x87,0x600,0x600,0x87,0x600,0x600,0x87,0x600,0x600,0x600,0x600,0x600,0x600, -0x600,0x600,0x600,0x603,0x615,0x60f,0x615,0x60f,0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f, -0x612,0x618,0x615,0x60f,0x1323,0x1323,0x1b0f,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, -0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x615,0x60f,0x612,0x618,0x615,0x60f,0x615,0x60f,0x615, -0x60f,0x615,0x615,0x60f,0x60f,0x60f,0x60f,0x612,0x60f,0x60f,0x612,0x60f,0x612,0x612,0x612,0x60f, -0x612,0x612,0x612,0x612,0x8a,0x8a,0x612,0x612,0x612,0x612,0x60f,0x60f,0x612,0x60f,0x60f,0x60f, -0x60f,0x612,0x60f,0x60f,0x60f,0x60f,0x60f,0x612,0x612,0x612,0x60f,0x60f,0x8a,0x8a,0x8a,0x8a, -0x8a,0x8a,0x8a,0x1b0f,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46, -0xb46,0xb46,0xb46,0xb46,0x85e,0x870,0x86d,0x870,0x86d,0xc7e,0xc7e,0xd68,0xd65,0x861,0x861,0x861, -0x861,0x873,0x873,0x873,0x88b,0x88e,0x89d,0x8d,0x891,0x894,0x8a0,0x8a0,0x888,0x87f,0x879,0x87f, -0x879,0x87f,0x879,0x87c,0x87c,0x897,0x897,0x89a,0x897,0x897,0x897,0x8d,0x897,0x885,0x882,0x87c, -0x8d,0x8d,0x8d,0x8d,0x621,0x62d,0x621,0xbfa,0x621,0x90,0x621,0x62d,0x621,0x62d,0x621,0x62d, -0x621,0x62d,0x621,0x62d,0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627, -0x62d,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x62a, -0x624,0x90,0x90,0x61e,0x75f,0x762,0x777,0x77a,0x759,0x762,0x762,0x96,0x741,0x744,0x744,0x744, -0x744,0x741,0x741,0x96,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0xafe,0xafe,0xafe, -0x9c3,0x73b,0x630,0x630,0x96,0x789,0x768,0x759,0x762,0x75f,0x759,0x76b,0x75c,0x756,0x759,0x777, -0x76e,0x765,0x786,0x759,0x783,0x783,0x783,0x783,0x783,0x783,0x783,0x783,0x783,0x783,0x774,0x771, -0x777,0x777,0x777,0x789,0x74a,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747, -0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747, -0x747,0x747,0x747,0x96,0x96,0x96,0x747,0x747,0x747,0x747,0x747,0x747,0x96,0x96,0x747,0x747, -0x747,0x747,0x747,0x747,0x96,0x96,0x747,0x747,0x747,0x747,0x747,0x747,0x96,0x96,0x747,0x747, -0x747,0x96,0x96,0x96,0xb49,0xb49,0xb49,0xb49,0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, -0x99,0x1860,0x1860,0x1860,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f, -0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0x9c,0x9c,0x9c,0x9c,0x9c,0x1626,0x1626,0x1626,0x1626, -0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0xb58,0xb58,0xb58,0xb58, -0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58, -0xb58,0xb58,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0xb64,0xb64,0xb64,0xb64, -0xb64,0xb64,0xb64,0xa2,0xa2,0xfe1,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64, -0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0x16e3,0x16e3,0x16e3,0x16e3, -0x16e3,0x16e3,0x16e3,0x16e3,0x16e3,0x1b12,0x1b12,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2, -0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xb7c,0xb7c,0xb7c,0xb7c, -0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, -0xb79,0xa5,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb7c,0xb7c,0xb79,0xb79, -0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, -0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb7c,0xa5,0xb7c,0xb7c,0xa5,0xa5,0xb7c,0xa5, -0xa5,0xb7c,0xb7c,0xa5,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, -0xb7c,0xb7c,0xb79,0xb79,0xb79,0xb79,0xa5,0xb79,0xa5,0xb79,0xb79,0xb79,0xb79,0xcf0,0xb79,0xb79, -0xa5,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb7c,0xb7c,0xb7c,0xb7c, -0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb79,0xb79,0xb79,0xb79, -0xb7c,0xb7c,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xa5,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, -0xb7c,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xa5,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, -0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, -0xb79,0xb79,0xb79,0xb79,0xb7c,0xb7c,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xa5,0xb7c,0xb7c,0xb7c,0xb7c, -0xb7c,0xa5,0xb7c,0xa5,0xa5,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xa5,0xb79,0xb79, -0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xdda,0xdda,0xa5,0xa5, -0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, -0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb79,0xb79,0xb79,0xb73,0xb79,0xb79,0xb79,0xb79, -0xb79,0xb79,0xef1,0xeee,0xa5,0xa5,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76, -0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xa8,0xb82,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8, -0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8, -0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xc09,0xc09,0xc09,0xc09,0xc09,0xc09,0xc09,0xc09, -0xc09,0xc09,0xc09,0xc09,0xc09,0x1b18,0xc09,0xc09,0xc09,0xc09,0xc03,0xc03,0xc06,0x1b15,0xab,0xab, -0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x1b18,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12, -0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc0c,0xc0c,0xc0f,0xc72,0xc72,0xae, -0xae,0xae,0xae,0xae,0xae,0xae,0xae,0xae,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18, -0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc15,0xc15,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xc1e,0xc1e,0xc1e,0xc1e,0xc1e,0xc1e,0xc1e,0xc1e, -0xc1e,0xc1e,0xc1e,0xc1e,0xc1e,0xb4,0xc1e,0xc1e,0xc1e,0xb4,0xc1b,0xc1b,0xb4,0xb4,0xb4,0xb4, -0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02, -0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02, -0xd02,0xd02,0xd02,0xd02,0xd02,0x14e8,0x14e8,0xb7,0xcf3,0xcf3,0xcf3,0xcff,0xcff,0xcff,0xcff,0xcf3, -0xcf3,0xcff,0xcff,0xcff,0xb7,0xb7,0xb7,0xb7,0xcff,0xcff,0xcf3,0xcff,0xcff,0xcff,0xcff,0xcff, -0xcff,0xcf6,0xcf6,0xcf6,0xb7,0xb7,0xb7,0xb7,0xcf9,0xb7,0xb7,0xb7,0xd05,0xd05,0xcfc,0xcfc, -0xcfc,0xcfc,0xcfc,0xcfc,0xcfc,0xcfc,0xcfc,0xcfc,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08, -0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xba,0xba,0xd08,0xd08,0xd08,0xd08, -0xd08,0xba,0xba,0xba,0xba,0xba,0xba,0xba,0xba,0xba,0xba,0xba,0x14eb,0x14eb,0x14eb,0x14eb, -0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb, -0xbd,0xbd,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb, -0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0xbd,0x1a64,0x14eb,0x14eb,0x14eb,0x14eb, -0x14eb,0x14eb,0x14eb,0x14eb,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c, -0xc0,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c, -0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xc0,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c, -0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xc0,0xd2c,0xd2c,0xc0,0xd2c, -0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xc0,0xc0, -0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xc0,0xc0, -0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0, -0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0, -0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f, -0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xc3,0xc3,0xc3,0xc3,0xc3, -0xd6e,0xd6e,0xd74,0xc6,0xc6,0xc6,0xc6,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b, -0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b, -0xc6,0xc6,0xc6,0xd71,0xd71,0xd71,0xd71,0xd71,0xd71,0xd71,0xd71,0xd71,0xd35,0xd35,0xd35,0xd35, -0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35, -0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xc9,0xd32,0xd3e,0xd3e,0xd3e,0xd3e, -0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e, -0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xcc,0xcc,0xd3b,0xd3b,0xd3b,0xd3b, -0xd3b,0xd3b,0xd3b,0xd3b,0xd3b,0xd3b,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x1824,0x1824,0x1824,0x1824, -0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0xd41,0xd41,0xd41,0xd41, -0xd41,0xd41,0xcf,0xcf,0xd41,0xcf,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41, -0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xcf,0xd41, -0xd41,0xcf,0xcf,0xcf,0xd41,0xcf,0xcf,0xd41,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44, -0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd2, -0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5, -0xdf5,0xdf5,0xdf5,0x14ee,0x14ee,0x179a,0x179a,0xd8,0x10e0,0x10e0,0x10e0,0x10e0,0x10e0,0x10e0,0x10e0,0x10e0, -0x10e0,0x10e0,0x10e0,0x10e0,0x1a73,0x129,0x129,0x129,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07, -0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xdfe, -0xdfe,0xe04,0xe04,0xdfe,0xdb,0xdb,0xe01,0xe01,0x1110,0x1110,0x1110,0x1110,0xde,0xde,0xde,0xde, -0xde,0xde,0xde,0xde,0xde,0xde,0xde,0xde,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f, -0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xe19,0xe16,0xe19,0xe16,0xe16,0xe0d,0xe0d,0xe0d, -0xe0d,0xe0d,0xe0d,0x115b,0x1158,0x115b,0x1158,0x1155,0x1155,0x1155,0x13e6,0x13e3,0xe1,0xe1,0xe1,0xe1, -0xe1,0xe13,0xe10,0xe10,0xe10,0xe0d,0xe13,0xe10,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c, -0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe4, -0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe4, -0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe4,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe4, -0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe4,0xe22,0xe22,0xe22,0xe22,0xe22,0xe22,0xe22,0xe22, -0xe22,0xe22,0xe22,0xe22,0xe22,0xe22,0xe22,0xe22,0xe1f,0xe1f,0xe1f,0xe1f,0xe1f,0xe1f,0xe1f,0xe1f, -0xe1f,0xe1f,0xe7,0xe7,0xe7,0xe7,0xe7,0xe7,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xea,0x13e9, -0xea,0xea,0xea,0xea,0xea,0x13e9,0xea,0xea,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c, -0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe3d,0xe31,0xe31,0xe31,0xed,0xe31,0xe31,0xed, -0xed,0xed,0xed,0xed,0xe31,0xe31,0xe31,0xe31,0xe3d,0xe3d,0xe3d,0xe3d,0xed,0xe3d,0xe3d,0xe3d, -0xed,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d, -0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0x1905,0x1905,0xed,0xed,0xe2e,0xe2e,0xe2e,0xed, -0xed,0xed,0xed,0xe34,0xe37,0xe37,0xe37,0xe37,0xe37,0xe37,0xe37,0xe37,0x1902,0xed,0xed,0xed, -0xed,0xed,0xed,0xed,0xe3a,0xe3a,0xe3a,0xe3a,0xe3a,0xe3a,0xe40,0xe40,0xe37,0xed,0xed,0xed, -0xed,0xed,0xed,0xed,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0x1161,0x1161, -0xf0,0xf0,0xf0,0xf0,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4f,0xe4f,0xe4f,0xe4c,0xe4c,0xe4f,0xe4c, -0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0, -0xe49,0xe49,0xe49,0xe49,0xe49,0xe49,0xe49,0xe49,0xe49,0xe49,0x115e,0xf0,0xf0,0xf0,0xe46,0xe46, -0xe55,0xe55,0xe55,0xe55,0xf3,0xf3,0xf3,0xf3,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55, -0xe52,0xe55,0xe55,0xe55,0xe55,0xe55,0xf3,0xf3,0xf3,0xf3,0xf3,0xf3,0xf3,0xf3,0xf3,0xf3, -0x14fd,0x1503,0x1500,0x1845,0x17a0,0x1869,0x1869,0x1869,0x1869,0x1869,0x190b,0x1908,0x190e,0x1908,0x190e,0x19cb, -0x1a67,0x1a67,0x1a67,0x1b2a,0x1b2a,0x1b24,0x1b21,0x1b24,0x1b21,0x1b24,0x1b21,0x1b24,0x1b21,0x1b27,0xf6,0xf6, -0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6, -0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6, -0xe79,0xe79,0xe79,0xe76,0xe76,0xe6d,0xe6d,0xe76,0xe73,0xe73,0xe73,0xe73,0x1a6a,0xf9,0xf9,0xf9, -0x12cc,0x12cc,0x12cc,0x12cf,0x12cf,0x12cf,0x12c6,0x12c6,0x12c9,0x12c6,0x14d,0x14d,0x14d,0x14d,0x14d,0x14d, -0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0x13f5,0x13f5,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xe7f, -0x1335,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0x1332, -0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42, -0xeac,0xe9d,0xe97,0xea9,0xea6,0xea0,0xea0,0xeaf,0xe9a,0xea3,0xff,0xff,0xff,0xff,0xff,0xff, -0xf33,0xf33,0xf1e,0xf33,0xf36,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0x1b2d,0x105,0x105,0x105, -0xf2d,0xf2d,0xf2d,0xf2d,0xf2d,0xf2d,0xf2d,0xf2d,0xf2d,0xf2d,0xf3f,0xf3f,0xf24,0xf2a,0xf3f,0xf3f, -0xf27,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf21,0xf21,0xf21,0xf21,0xf21, -0xf21,0xf21,0xf21,0xf21,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0x1b30,0x1b30,0x105, -0x1b39,0x1b33,0x19d1,0x19ce,0x19d1,0x19d1,0x19d1,0x1a70,0x1a6d,0x1a70,0x1a6d,0x108,0x108,0x108,0x108,0x108, -0x1b39,0x1b33,0x108,0x1b33,0x108,0x1b33,0x1b39,0x1b33,0x1b39,0x1b33,0x108,0x108,0x108,0x108,0x108,0x108, -0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x1b36,0x1b36, -0x1b36,0x1a70,0x1a6d,0x150c,0x13fe,0x13fe,0x1338,0x103b,0x103b,0x103b,0x103b,0x103b,0xf4e,0xf4e,0xf4e,0xf4e, -0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e, -0xf4b,0xf4b,0xf51,0xf51,0x10b,0x10b,0x10b,0x10b,0x10b,0x10b,0x10b,0x10b,0xf5a,0xf5a,0xf5a,0xf5a, -0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a, -0xf5a,0xf5a,0xf54,0xf54,0xf54,0xf54,0x116a,0x116a,0x10e,0x10e,0x10e,0xf57,0x1512,0x1512,0x1512,0x1512, -0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512, -0x1512,0x1512,0x1512,0x1512,0x1512,0x16f2,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111, -0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111, -0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0xf63,0xf63,0xf63,0x1518,0x1518,0x1518,0x1518,0x1518, -0x1518,0x1518,0x1518,0x1518,0x1518,0x1518,0x1518,0x114,0xf60,0xf60,0xf60,0xf60,0x1515,0x114,0x114,0x114, -0x114,0x114,0x114,0x114,0x114,0x114,0x114,0x114,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66, -0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0x191d,0x191d,0x191d,0x191d,0x191d,0x191d, -0x191d,0x117,0x117,0x117,0x117,0x117,0x117,0x117,0x1062,0x1062,0x1062,0x1062,0x105f,0x105f,0x105f,0x105f, -0x105f,0x105f,0x105f,0x105f,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x105f,0x105f,0x1056,0x1053, -0x11a,0x11a,0x11a,0x1065,0x1065,0x1059,0x1059,0x1059,0x105c,0x105c,0x105c,0x105c,0x105c,0x105c,0x105c,0x105c, -0x105c,0x105c,0x11a,0x11a,0x11a,0x1062,0x1062,0x1062,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068, -0x1068,0x1068,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x107d,0x107d,0x107d,0x107d,0x107d,0x107d,0x107d,0x107d, -0x107d,0x107d,0x1080,0x1080,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d, -0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x10a7,0x10a7,0x10a7,0x10a7,0x10a1,0x17a6,0x120,0x120, -0x120,0x120,0x120,0x120,0x120,0x120,0x10ad,0x10ad,0x10a4,0x10a4,0x10a4,0x10a4,0x10a4,0x10a4,0x10a4,0x10a4, -0x10a4,0x10a4,0x120,0x120,0x120,0x120,0x120,0x120,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10bf, -0x10bf,0x10bf,0x10bf,0x10bf,0x10bf,0x10bf,0x10bf,0x10bf,0x10bf,0x10bf,0x10c5,0x10c8,0x123,0x123,0x123,0x123, -0x123,0x123,0x123,0x123,0x123,0x123,0x123,0x10c2,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da, -0x10da,0x10ce,0x10ce,0x10ce,0x10ce,0x10ce,0x10ce,0x10d7,0x10d7,0x10ce,0x10ce,0x10d7,0x10d7,0x10ce,0x10ce,0x126, -0x126,0x126,0x126,0x126,0x126,0x126,0x126,0x126,0x10da,0x10da,0x10da,0x10ce,0x10da,0x10da,0x10da,0x10da, -0x10da,0x10da,0x10da,0x10da,0x10ce,0x10d7,0x126,0x126,0x10d4,0x10d4,0x10d4,0x10d4,0x10d4,0x10d4,0x10d4,0x10d4, -0x10d4,0x10d4,0x126,0x126,0x10d1,0x10dd,0x10dd,0x10dd,0x1524,0x129,0x129,0x129,0x129,0x129,0x129,0x129, -0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129, -0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3, -0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3, -0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e6,0x12c,0x12c,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9, -0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9, -0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x12f,0x12f,0x12f,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec, -0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x132,0x132,0x132,0x132,0x132,0x132,0x132, -0x132,0x132,0x132,0x132,0x132,0x132,0x132,0x132,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2, -0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2, -0x10f2,0x10f2,0x135,0x135,0x135,0x135,0x135,0x10ef,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5, -0x10f5,0x10f5,0x10f5,0x10f5,0x138,0x138,0x138,0x138,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8, -0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x13b,0x13b,0x13b,0x13b, -0x13b,0x13b,0x13b,0x13b,0x13b,0x13b,0x13b,0x13b,0x1170,0x1170,0x1170,0x1170,0x1179,0x1170,0x1170,0x1170, -0x1179,0x1170,0x1170,0x1170,0x1170,0x116d,0x13e,0x13e,0x1176,0x1176,0x1176,0x1176,0x1176,0x1176,0x1176,0x117c, -0x1176,0x117c,0x1176,0x1176,0x1176,0x117c,0x117c,0x13e,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f, -0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x141,0x141, -0x141,0x141,0x141,0x141,0x141,0x141,0x141,0x141,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a, -0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x1197,0x1182,0x1197, -0x1182,0x1182,0x1182,0x1182,0x1182,0x1182,0x1182,0x144,0x118b,0x1194,0x1182,0x1194,0x1194,0x1182,0x1182,0x1182, -0x1182,0x1182,0x1182,0x1182,0x1182,0x1197,0x1197,0x1197,0x1197,0x1197,0x1197,0x1182,0x1182,0x1188,0x1188,0x1188, -0x1188,0x1188,0x1188,0x1188,0x1188,0x144,0x144,0x1185,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191, -0x1191,0x1191,0x144,0x144,0x144,0x144,0x144,0x144,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191, -0x1191,0x1191,0x144,0x144,0x144,0x144,0x144,0x144,0x118e,0x118e,0x118e,0x118e,0x118e,0x118e,0x118e,0x119d, -0x11a0,0x11a0,0x11a0,0x11a0,0x118e,0x118e,0x144,0x144,0x1563,0x1563,0x1563,0x1563,0x1563,0x1563,0x1563,0x1563, -0x1563,0x1563,0x1563,0x1563,0x1563,0x1563,0x1560,0x1a85,0x12e1,0x12ba,0x12d8,0x12d8,0x12d8,0x12d8,0x12d8,0x12d8, -0x12d8,0x12c0,0x12bd,0x12b4,0x12b4,0x12de,0x12b4,0x12b4,0x12b4,0x12b4,0x12c3,0x149d,0x14a3,0x14a0,0x14a0,0x18e4, -0x16b9,0x16b9,0x1a52,0x147,0x147,0x147,0x147,0x147,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5, -0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11ac,0x11ac,0x11af,0x11b8,0x11b2,0x11b2,0x11b2,0x11b8, -0x14a,0x14a,0x14a,0x14a,0x14a,0x14a,0x14a,0x14a,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5, -0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5, -0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x150,0x150,0x150,0x11d6,0x11ca,0x11ca,0x11ca,0x11ca,0x11ca,0x11ca,0x11cd, -0x11dc,0x11dc,0x11ca,0x11ca,0x11ca,0x11ca,0x153,0x12d5,0x11d0,0x11d0,0x11d0,0x11d0,0x11d0,0x11d0,0x11d0,0x11d0, -0x11d0,0x11d0,0x153,0x153,0x153,0x153,0x11ca,0x11ca,0x11fa,0x11ee,0x11fa,0x156,0x156,0x156,0x156,0x156, -0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156, -0x156,0x156,0x156,0x11f7,0x11f7,0x11fd,0x11f1,0x11f4,0x1212,0x1212,0x1212,0x120c,0x120c,0x1203,0x120c,0x120c, -0x1203,0x120c,0x120c,0x1215,0x120f,0x1206,0x159,0x159,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209, -0x1209,0x1209,0x159,0x159,0x159,0x159,0x159,0x159,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x15c, -0x15c,0x15c,0x15c,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218, -0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218, -0x15c,0x15c,0x15c,0x15c,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224, -0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x15f,0x1221,0x121e,0x121e,0x121e,0x121e, -0x121e,0x121e,0x121e,0x121e,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233, -0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x162,0x162,0x162,0x122d,0x1230,0x1230, -0x1230,0x1230,0x1230,0x1230,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239, -0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x165,0x165,0x1236,0x1236,0x1236,0x1236, -0x1236,0x1236,0x1236,0x1236,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f, -0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x168,0x168,0x168,0x168,0x168,0x123c,0x123c,0x123c,0x123c, -0x123c,0x123c,0x123c,0x123c,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245, -0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245, -0x1245,0x1245,0x1245,0x16e,0x125d,0x125d,0x1b3c,0x171,0x171,0x171,0x171,0x171,0x171,0x171,0x171,0x171, -0x171,0x1926,0x171,0x171,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c, -0x147c,0x147c,0x147c,0x147c,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827, -0x1827,0x1a76,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174, -0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174, -0x174,0x174,0x174,0x174,0x174,0x174,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344, -0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344, -0x12ae,0x13a7,0x13a7,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177, -0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab, -0x12ab,0x12ab,0x13a7,0x13a7,0x13a7,0x13a7,0x13a7,0x13a7,0x13a7,0x13a7,0x13a7,0x182a,0x177,0x177,0x177,0x177, -0x12a8,0x12a8,0x12a8,0x12a8,0x12a8,0x12a8,0x12a8,0x12a8,0x12a8,0x177,0x177,0x177,0x177,0x177,0x177,0x177, -0x13cb,0x13cb,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177, -0x18c9,0x18c9,0x18c9,0x18c9,0x18c9,0x18c9,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177, -0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177, -0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d, -0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d, -0x134d,0x1347,0x1347,0x1347,0x17a,0x17a,0x134a,0x17a,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x1350,0x1359, -0x1353,0x1353,0x1359,0x1359,0x1359,0x1353,0x1359,0x1353,0x1353,0x1353,0x135c,0x135c,0x17d,0x17d,0x17d,0x17d, -0x17d,0x17d,0x17d,0x17d,0x1356,0x1356,0x1356,0x1356,0x180,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x180, -0x180,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x180,0x180,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x180, -0x180,0x180,0x180,0x180,0x180,0x180,0x180,0x180,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x180, -0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x180,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0, -0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1368,0x137a, -0x137a,0x136e,0x136e,0x136e,0x136e,0x136e,0x183,0x183,0x183,0x183,0x136b,0x136b,0x136b,0x136b,0x136b,0x136b, -0x136b,0x136b,0x136b,0x136b,0x136b,0x136b,0x136b,0x136b,0x136b,0x136b,0x1371,0x1371,0x1371,0x1371,0x1371,0x1371, -0x1371,0x1371,0x1371,0x1371,0x1b42,0x1b45,0x1b45,0x1b3f,0x1b3f,0x1b45,0x183,0x183,0x183,0x183,0x183,0x183, -0x183,0x183,0x183,0x1533,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d, -0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x186,0x186,0x186, -0x186,0x186,0x186,0x186,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380, -0x1380,0x1380,0x1380,0x189,0x189,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380, -0x1380,0x1380,0x1380,0x1536,0x189,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380, -0x1380,0x1380,0x1380,0x13b0,0x189,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380, -0x1380,0x1380,0x1380,0x1380,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536, -0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x189,0x189,0x189,0x189,0x189,0x189, -0x189,0x189,0x189,0x189,0x13c5,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542, -0x16b0,0x1542,0x1542,0x1542,0x1782,0x1833,0x1833,0x186c,0x186c,0x1a34,0x1adf,0x1adf,0x18c,0x18c,0x18c,0x18c, -0x1c2c,0x1bae,0x1bae,0x1bae,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542,0x16ad, -0x16ad,0x18c,0x18c,0x18c,0x1542,0x1542,0x1542,0x1542,0x1833,0x1833,0x1833,0x18cf,0x18cf,0x19b0,0x1a34,0x1adf, -0x1adf,0x18c,0x18c,0x18c,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383, -0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1bd2,0x1bd2,0x1bd2,0x18f,0x18f,0x18f,0x18f,0x1bd2, -0x1bd2,0x1bd2,0x1bd2,0x1bd2,0x141f,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f, -0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f, -0x141f,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x192,0x141f,0x192,0x192,0x141f,0x192,0x141f,0x141f,0x141f, -0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x141f,0x141f,0x192,0x141f,0x192,0x141f, -0x192,0x192,0x192,0x192,0x192,0x192,0x141f,0x192,0x192,0x192,0x192,0x141f,0x192,0x141f,0x192,0x141f, -0x192,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x192,0x141f,0x192,0x192,0x141f,0x192,0x141f,0x192,0x141f, -0x192,0x141f,0x192,0x141f,0x192,0x141f,0x141f,0x192,0x141f,0x192,0x192,0x141f,0x141f,0x141f,0x141f,0x192, -0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x141f, -0x141f,0x192,0x141f,0x192,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x192,0x141f, -0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f, -0x192,0x192,0x192,0x192,0x192,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x141f,0x141f,0x141f,0x192,0x141f, -0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f, -0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192, -0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192, -0x141c,0x141c,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192, -0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1422,0x1422,0x1422,0x1422,0x1422,0x1431,0x1422,0x1425,0x1425, -0x1422,0x1422,0x1422,0x1428,0x1428,0x195,0x142e,0x142e,0x142e,0x142e,0x142e,0x142e,0x142e,0x142e,0x142e,0x142e, -0x142b,0x1437,0x1437,0x1437,0x1932,0x192f,0x192f,0x1a7c,0x195,0x195,0x195,0x195,0x195,0x195,0x195,0x195, -0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2, -0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1440,0x143a,0x143a,0x1440,0x1440, -0x1449,0x1449,0x1443,0x1446,0x1446,0x1440,0x143d,0x198,0x198,0x198,0x198,0x198,0x198,0x198,0x198,0x198, -0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c, -0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x19b,0x19b,0x19b,0x19b,0x1707,0x1707,0x144c,0x144c, -0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707, -0x19b,0x19b,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707, -0x1458,0x1458,0x1458,0x1458,0x1458,0x19dd,0x19dd,0x19dd,0x19dd,0x19dd,0x19dd,0x19e,0x19e,0x19e,0x19e,0x19d7, -0x1458,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455, -0x19da,0x19da,0x19da,0x19da,0x19da,0x19da,0x19da,0x19da,0x19e,0x19e,0x19e,0x19e,0x19e,0x19e,0x19e,0x1452, -0x1452,0x1452,0x1452,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b, -0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x1a1,0x1a1,0x1a1,0x1a1,0x1a1,0x1a1,0x1a1, -0x1479,0x1479,0x1479,0x1479,0x1479,0x1479,0x1479,0x1479,0x1479,0x1479,0x1a1,0x1a1,0x1a1,0x1a1,0x1a1,0x1a1, -0x147f,0x147f,0x147f,0x147f,0x147f,0x147f,0x147f,0x147f,0x1a4,0x1a4,0x1a4,0x1a4,0x1a4,0x1a4,0x1a4,0x1a4, -0x12db,0x12d8,0x12db,0x12b7,0x12d8,0x12de,0x12de,0x12e1,0x12de,0x12e1,0x12e4,0x12d8,0x12e1,0x12e1,0x12d8,0x12d8, -0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1482,0x148b,0x1482,0x148b,0x148b, -0x1482,0x1482,0x1482,0x1482,0x1482,0x1482,0x148e,0x1485,0x19e0,0x1b51,0x1a7,0x1a7,0x1a7,0x1a7,0x1a7,0x1a7, -0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1aa,0x1aa, -0x1551,0x1551,0x1551,0x1551,0x1551,0x1557,0x1aa,0x1aa,0x1aa,0x1aa,0x1aa,0x1aa,0x1aa,0x1aa,0x1aa,0x1aa, -0x155d,0x155d,0x155d,0x155d,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x155a, -0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x2b2,0x1b9c,0x1b9c,0x1b9c,0x1b9c, -0x16bc,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3, -0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x1b0,0x1b0,0x1b0,0x1b0, -0x1a85,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b54,0x1b54,0x1b54,0x1b3, -0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3, -0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3, -0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x1b6,0x1b6,0x1b6,0x1b6,0x1b6, -0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x1b6,0x1b6,0x1b6, -0x1b6,0x1b6,0x1b6,0x1b6,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x1b6,0x1b6, -0x156c,0x1566,0x1569,0x1572,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1b9,0x1b9,0x1b9,0x1b9, -0x1b9,0x1b9,0x1b9,0x1b9,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d, -0x155d,0x155d,0x155d,0x155d,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578, -0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1935,0x1935,0x1935,0x1935,0x1bd5,0x1bc,0x1bc, -0x1bc,0x1bc,0x1bc,0x1bc,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37, -0x1bc,0x1bc,0x1bc,0x1bc,0x1bb1,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc, -0x1bc,0x1bc,0x1bc,0x1bc,0x171c,0x16bf,0x1581,0x16c5,0x1bf,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a, -0x158a,0x1bf,0x1bf,0x158a,0x158a,0x1bf,0x1bf,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a, -0x158a,0x158a,0x158a,0x158a,0x158a,0x1bf,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a,0x1bf,0x158a,0x158a, -0x1bf,0x158a,0x158a,0x158a,0x158a,0x158a,0x1bf,0x19bc,0x16c2,0x158a,0x157b,0x1581,0x157b,0x1581,0x1581,0x1581, -0x1581,0x1bf,0x1bf,0x1581,0x1581,0x1bf,0x1bf,0x1584,0x1584,0x1587,0x1bf,0x1bf,0x171f,0x1bf,0x1bf,0x1bf, -0x1bf,0x1bf,0x1bf,0x157b,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x158d,0x158a,0x158a,0x158a,0x158a,0x1581,0x1581, -0x1bf,0x1bf,0x157e,0x157e,0x157e,0x157e,0x157e,0x157e,0x157e,0x1bf,0x1bf,0x1bf,0x157e,0x157e,0x157e,0x157e, -0x157e,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x15a2,0x15a2,0x15a2,0x15a2, -0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x1c2,0x15a2, -0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15b4,0x15b4,0x15b4,0x15a8, -0x15a8,0x15a8,0x15a8,0x15a8,0x15a8,0x15ab,0x15ae,0x1c5,0x1c5,0x1c5,0x1c5,0x1c5,0x15b1,0x15b1,0x15b1,0x15b1, -0x15b1,0x15b1,0x15b1,0x15b1,0x15b1,0x15b1,0x1c5,0x1c5,0x1c5,0x1c5,0x1c5,0x1c5,0x1722,0x1722,0x1722,0x1722, -0x15c0,0x15bd,0x19e3,0x19e3,0x1a8b,0x1a8e,0x1a88,0x1a88,0x1c8,0x1c8,0x1c8,0x1c8,0x174f,0x174f,0x174f,0x174f, -0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x15c6,0x15c6,0x15c6,0x15c6, -0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6, -0x15c6,0x15c6,0x15c6,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x15c6,0x15c6,0x15c6,0x15c6, -0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6, -0x15c6,0x15c6,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x15c6,0x15c6,0x15c6,0x15c6, -0x15c6,0x15c6,0x15c6,0x15c6,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb, -0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x15d2,0x15d2,0x15d2,0x15d2, -0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15c9, -0x15cc,0x15cf,0x15d2,0x1ce,0x1ce,0x1ce,0x1ce,0x1ce,0x1ce,0x1ce,0x1ce,0x1ce,0x15e1,0x15e1,0x15e1,0x15e1, -0x15e1,0x15d5,0x15d5,0x1d1,0x1d1,0x1d1,0x1d1,0x15d8,0x15d8,0x15d8,0x15d8,0x15d8,0x15de,0x15de,0x16c8,0x15de, -0x15de,0x15de,0x15db,0x1d1,0x1d1,0x1d1,0x1d1,0x1d1,0x1d1,0x1d1,0x1d1,0x1d1,0x15ea,0x15ea,0x15ea,0x15ea, -0x15ea,0x1d4,0x1d4,0x15e7,0x15e7,0x15e7,0x15e7,0x15e7,0x15e7,0x15e7,0x15e7,0x15e7,0x15e4,0x15e4,0x15e4,0x15e4, -0x15e4,0x15e4,0x15e4,0x1d4,0x1d4,0x1d4,0x1d4,0x1d4,0x1d4,0x1d4,0x1d4,0x1d4,0x15ed,0x15ff,0x15ff,0x15f3, -0x15fc,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x15f6,0x15f6,0x15f6,0x15f6, -0x15f6,0x15f6,0x15f6,0x15f6,0x15f6,0x15f6,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1605,0x1605,0x1605,0x1605, -0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605, -0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1da,0x1602,0x1602,0x1602,0x1602, -0x1602,0x1602,0x1602,0x1602,0x1602,0x1602,0x1da,0x1da,0x1da,0x1da,0x1608,0x1608,0x1b8d,0x1b8d,0x1b8d,0x1b8d, -0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1611,0x1611,0x1611,0x1611, -0x1611,0x160b,0x1614,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x160e,0x160e,0x160e,0x160e, -0x160e,0x160e,0x160e,0x160e,0x160e,0x160e,0x1611,0x1611,0x1611,0x1611,0x1611,0x1dd,0x161a,0x161a,0x161a,0x161a, -0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a, -0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x1e0,0x1626,0x1626,0x1626,0x1626, -0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626, -0x1626,0x1626,0x1623,0x1623,0x1623,0x1623,0x1623,0x1e3,0x1e3,0x1e3,0x1e3,0x1e3,0x163e,0x163e,0x1641,0x1641, -0x1644,0x1635,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x163b,0x163b,0x163b,0x163b, -0x163b,0x163b,0x163b,0x163b,0x163b,0x163b,0x1e6,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1e6,0x163e, -0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e, -0x163e,0x163e,0x163e,0x163e,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x163e,0x163e,0x163e,0x164d,0x164d,0x164d,0x164d, -0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d, -0x164d,0x164d,0x164d,0x164d,0x164d,0x1e9,0x1e9,0x1e9,0x1e9,0x1e9,0x1e9,0x1e9,0x1656,0x1656,0x1656,0x1656, -0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1ec,0x1ec, -0x1ec,0x1ec,0x1ec,0x1ec,0x1ec,0x1653,0x1653,0x1653,0x1653,0x1ec,0x1ec,0x1ec,0x1671,0x1671,0x1671,0x1671, -0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1659,0x166b,0x166b,0x1659,0x1659, -0x1659,0x1659,0x1f2,0x1f2,0x166b,0x166b,0x166e,0x166e,0x1659,0x1659,0x166b,0x165f,0x165c,0x1662,0x1674,0x1674, -0x1665,0x1665,0x1668,0x1668,0x1668,0x1674,0x172b,0x172b,0x172b,0x172b,0x172b,0x172b,0x172b,0x172b,0x172b,0x172b, -0x172b,0x172b,0x172b,0x172b,0x1728,0x1728,0x1728,0x1728,0x1725,0x1725,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2, -0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2, -0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f5,0x1677,0x1677,0x1677, -0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677, -0x1677,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x167a,0x167a,0x167a,0x167a, -0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x1f8,0x1f8,0x1f8,0x1f8,0x167a,0x167a,0x167a,0x167a, -0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x1f8,0x1f8,0x1f8,0x1f8, -0x1f8,0x1f8,0x1f8,0x1f8,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x1f8,0x1f8, -0x1f8,0x1f8,0x1f8,0x1f8,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x1f8,0x1f8,0x1f8,0x1f8, -0x1f8,0x1f8,0x1f8,0x1f8,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a, -0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x1f8,0x1f8,0x1a91,0x1a91,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8, -0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8, -0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x167d,0x168c,0x1683,0x1680, -0x1692,0x1692,0x1686,0x1692,0x1fb,0x1fb,0x1fb,0x1fb,0x1fb,0x1fb,0x1fb,0x1fb,0x1689,0x1689,0x1689,0x1689, -0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x1fb,0x1fb,0x1fb,0x1fb,0x1fb,0x1fb,0x1698,0x1698,0x1698,0x1698, -0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1fe, -0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x169e,0x1740,0x1740,0x1740,0x1740, -0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740, -0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1938,0x201,0x201,0x172e,0x172e,0x172e,0x173a,0x173a,0x172e,0x172e, -0x172e,0x172e,0x173d,0x172e,0x172e,0x172e,0x172e,0x1731,0x201,0x201,0x201,0x201,0x1737,0x1737,0x1737,0x1737, -0x1737,0x1737,0x1737,0x1737,0x1737,0x1737,0x1734,0x1734,0x1743,0x1743,0x1743,0x1734,0x1746,0x1746,0x1746,0x1746, -0x1746,0x1746,0x1746,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204, -0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204, -0x204,0x204,0x204,0x204,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758, -0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x20a,0x1758,0x1758,0x20a,0x20a,0x20a,0x20a,0x20a,0x1755, -0x1755,0x1755,0x1755,0x1755,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x20d,0x175b,0x20d,0x175b,0x175b, -0x175b,0x175b,0x20d,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b, -0x175b,0x175b,0x20d,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175e,0x20d,0x20d, -0x20d,0x20d,0x20d,0x20d,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7, -0x15b7,0x15b7,0x15b7,0x15b7,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767, -0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x210,0x210,0x210,0x210,0x210,0x210,0x210,0x210,0x210, -0x210,0x210,0x210,0x210,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764, -0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x210,0x210,0x210,0x210,0x210,0x210,0x210,0x1761,0x1761, -0x1761,0x1761,0x1761,0x1761,0x176d,0x176d,0x176d,0x176d,0x176a,0x176d,0x176d,0x1770,0x1773,0x1770,0x1770,0x176d, -0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x176a, -0x176a,0x176a,0x176a,0x176a,0x17ca,0x17ca,0x17ca,0x17ca,0x17c1,0x17c1,0x17c1,0x17bb,0x17be,0x17be,0x17be,0x19e6, -0x216,0x216,0x216,0x216,0x17c7,0x17c7,0x17c7,0x17c7,0x17c7,0x17c7,0x17c7,0x17c7,0x17c7,0x17c7,0x216,0x216, -0x216,0x216,0x17c4,0x17c4,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x219,0x17e5,0x17e5, -0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5, -0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e2,0x17d0,0x17d0,0x17d0,0x17d0,0x17d0,0x17d0,0x17d0,0x219, -0x17d0,0x17d0,0x17d0,0x17d0,0x17d0,0x17d0,0x17e2,0x17d3,0x17e5,0x17e8,0x17e8,0x17dc,0x17d9,0x17d9,0x219,0x219, -0x219,0x219,0x219,0x219,0x219,0x219,0x219,0x219,0x17df,0x17df,0x17df,0x17df,0x17df,0x17df,0x17df,0x17df, -0x17df,0x17df,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6, -0x17d6,0x219,0x219,0x219,0x17f4,0x17f7,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd, -0x17fd,0x17fd,0x17fd,0x17fd,0x17eb,0x17eb,0x17eb,0x17eb,0x17eb,0x17eb,0x17eb,0x17eb,0x17eb,0x21c,0x21c,0x21c, -0x21c,0x21c,0x21c,0x21c,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956, -0x1956,0x1956,0x1956,0x1956,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x21f,0x17ee,0x17ee,0x17ee,0x17ee, -0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x21f,0x21f,0x17ee, -0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x21f,0x17ee,0x17ee,0x21f,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x21f, -0x21f,0x21f,0x21f,0x21f,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc, -0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x183c,0x18d8,0x1a40,0x1a43,0x1aeb,0x222,0x222,0x222,0x222,0x222,0x222,0x222, -0x222,0x222,0x222,0x222,0x1ae8,0x1ae8,0x222,0x222,0x222,0x222,0x222,0x222,0x222,0x222,0x222,0x222, -0x222,0x222,0x222,0x222,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd, -0x17fd,0x17fd,0x17fd,0x17fd,0x225,0x225,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1, -0x17f1,0x17f1,0x17f1,0x17f1,0x225,0x17fa,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17fa,0x17f1,0x17f1, -0x17fa,0x17f1,0x17f1,0x225,0x225,0x225,0x225,0x225,0x225,0x225,0x225,0x225,0x1800,0x1800,0x1800,0x1800, -0x1800,0x1800,0x1800,0x1800,0x1800,0x1800,0x1800,0x1800,0x1800,0x228,0x228,0x228,0x228,0x228,0x228,0x228, -0x228,0x228,0x228,0x228,0x228,0x228,0x228,0x228,0x228,0x228,0x228,0x228,0x1818,0x1818,0x1809,0x1803, -0x1803,0x1818,0x1806,0x181b,0x181b,0x181b,0x181b,0x181e,0x181e,0x1812,0x180f,0x180c,0x1815,0x1815,0x1815,0x1815, -0x1815,0x1815,0x1815,0x1815,0x1815,0x1815,0x1a94,0x1812,0x22b,0x180c,0x193b,0x19e9,0x1a97,0x1a97,0x22b,0x22b, -0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b, -0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x1824,0x1824,0x1824,0x1824, -0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824, -0x22e,0x22e,0x22e,0x22e,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821, -0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821, -0x22e,0x22e,0x22e,0x22e,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f, -0x183f,0x19b9,0x19b9,0x19b9,0x19b9,0x19b9,0x1a46,0x1a46,0x1a46,0x1a46,0x1a46,0x1a46,0x231,0x231,0x231,0x231, -0x231,0x231,0x231,0x231,0x1bba,0x1bba,0x1bba,0x234,0x234,0x234,0x234,0x234,0x234,0x234,0x234,0x234, -0x234,0x234,0x234,0x234,0x276,0x276,0x1c2f,0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x276, -0x276,0x276,0x276,0x276,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x237,0x187e,0x187e,0x237,0x187e, -0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e, -0x187e,0x187e,0x187e,0x187e,0x187e,0x1872,0x1872,0x1872,0x1872,0x1872,0x1872,0x237,0x237,0x237,0x1872,0x237, -0x1872,0x1872,0x237,0x1872,0x1872,0x1872,0x1875,0x1872,0x1878,0x1878,0x1881,0x1872,0x237,0x237,0x237,0x237, -0x237,0x237,0x237,0x237,0x187b,0x187b,0x187b,0x187b,0x187b,0x187b,0x187b,0x187b,0x187b,0x187b,0x237,0x237, -0x237,0x237,0x237,0x237,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1, -0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1, -0x23a,0x23a,0x23a,0x23a,0x1890,0x1893,0x1893,0x23d,0x23d,0x23d,0x23d,0x23d,0x23d,0x23d,0x23d,0x23d, -0x23d,0x23d,0x23d,0x23d,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96, -0x1b96,0x1b96,0x1b96,0x1b96,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x240, -0x240,0x240,0x240,0x240,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63, -0x1b63,0x1b63,0x1b63,0x1b63,0x18ae,0x18b1,0x18c0,0x18c0,0x18b1,0x18b4,0x18ae,0x18ab,0x243,0x243,0x243,0x243, -0x243,0x243,0x243,0x243,0x1899,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1896,0x1896,0x1884,0x1884,0x1884, -0x1899,0x1899,0x1899,0x1899,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef, -0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x246,0x246,0x246,0x246,0x246,0x246,0x246,0x246, -0x246,0x246,0x246,0x246,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941, -0x1941,0x1941,0x246,0x246,0x1a4f,0x1a4f,0x1a4f,0x1a4f,0x1af1,0x1c35,0x1c35,0x1c35,0x1a4f,0x1a4f,0x1a4f,0x1bbd, -0x1bbd,0x279,0x279,0x279,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953, -0x1950,0x1950,0x1950,0x1944,0x1944,0x1944,0x1944,0x1944,0x1944,0x1944,0x1944,0x1944,0x1950,0x194a,0x1947,0x194d, -0x249,0x249,0x249,0x249,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956, -0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x24c, -0x24c,0x1956,0x1956,0x1956,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x24f,0x1965,0x1965,0x24f,0x1965,0x1965, -0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965, -0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1962,0x1962,0x1962,0x1962,0x1962,0x24f,0x1959,0x1959,0x24f,0x1962, -0x1962,0x1959,0x1962,0x195c,0x1965,0x24f,0x24f,0x24f,0x24f,0x24f,0x24f,0x24f,0x196e,0x196e,0x1971,0x1971, -0x1968,0x1968,0x1968,0x1968,0x252,0x252,0x252,0x252,0x252,0x252,0x252,0x252,0x196b,0x196b,0x196b,0x196b, -0x196b,0x196b,0x196b,0x196b,0x196b,0x196b,0x252,0x252,0x252,0x252,0x252,0x252,0x1974,0x1974,0x1974,0x1974, -0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1977,0x1974,0x1974,0x1974,0x1977,0x1974,0x1974,0x1974, -0x1974,0x255,0x255,0x255,0x255,0x255,0x255,0x255,0x255,0x255,0x255,0x255,0x1980,0x1980,0x1980,0x1980, -0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x197a, -0x197a,0x197d,0x197d,0x1983,0x1983,0x258,0x258,0x258,0x258,0x258,0x258,0x258,0x1986,0x1986,0x1986,0x1986, -0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986, -0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x1989,0x1989,0x1989,0x1989, -0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989, -0x1989,0x1989,0x1989,0x198c,0x1995,0x1989,0x1989,0x25e,0x25e,0x25e,0x25e,0x25e,0x1998,0x1998,0x1998,0x1998, -0x1998,0x1998,0x1998,0x199b,0x261,0x261,0x261,0x261,0x261,0x261,0x261,0x261,0x19a4,0x19a4,0x19a4,0x19a4, -0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x199e,0x199e, -0x199e,0x199e,0x199e,0x199e,0x199e,0x199e,0x199e,0x199e,0x199e,0x19a1,0x19a1,0x19a1,0x19a1,0x19a7,0x19a7,0x19a7, -0x19a7,0x19a7,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264, -0x264,0x264,0x264,0x264,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84, -0x1b84,0x1b84,0x1b84,0x1b84,0x1be7,0x1bed,0x1bed,0x1bed,0x1bed,0x1bed,0x1bed,0x1bea,0x1bea,0x1bea,0x1bea,0x1bea, -0x1bea,0x1bea,0x1bea,0x1bea,0x1bea,0x1bea,0x1bea,0x1bea,0x1bea,0x1bea,0x267,0x267,0x267,0x267,0x267,0x267, -0x267,0x267,0x267,0x267,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb, -0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x26a,0x26a,0x26a,0x26a,0x26a, -0x26a,0x26a,0x26a,0x26a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x26d,0x26d,0x1a0a,0x1a0a, -0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a, -0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a07,0x1a07,0x1a07,0x19fe,0x19fe,0x19fe,0x19fe,0x26d,0x26d,0x19fe,0x19fe, -0x1a07,0x1a07,0x1a07,0x1a07,0x1a01,0x1a0a,0x1a04,0x1a0a,0x1a07,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d, -0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d, -0x26d,0x26d,0x26d,0x26d,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16, -0x1a16,0x270,0x270,0x270,0x1a0d,0x1a0d,0x1a0d,0x1a0d,0x1a0d,0x1a0d,0x1a0d,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16, -0x1a19,0x1a19,0x270,0x270,0x273,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c, -0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c, -0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x273,0x273,0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x276, -0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x1a49,0x1a49,0x1a49,0x276,0x276,0x1c32,0x276,0x276, -0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x1a4c,0x1a4c,0x1a4c,0x1a4c,0x276,0x276,0x276,0x276, -0x276,0x276,0x276,0x276,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1, -0x18e1,0x18e1,0x18e1,0x18e1,0x1a4f,0x1a4f,0x1a4f,0x1af1,0x1af1,0x1af1,0x1af1,0x1c35,0x1c35,0x279,0x279,0x279, -0x279,0x279,0x279,0x279,0x1a4f,0x1a4f,0x1a4f,0x1a4f,0x1a4f,0x1a4f,0x1af1,0x1af1,0x1af1,0x1af1,0x1af1,0x1af1, -0x1af1,0x1af1,0x1af1,0x1af1,0x1af1,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1c35,0x1c35,0x1c35,0x1af1,0x1af1,0x1af1,0x1af1, -0x1af1,0x1af1,0x1af1,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1c35,0x1c35,0x1c35,0x279,0x1c35,0x1af1,0x1af1,0x1af1,0x1bc0, -0x1bc0,0x1bc0,0x279,0x279,0x279,0x279,0x279,0x279,0x279,0x279,0x1c35,0x1c35,0x1af1,0x1af1,0x1af1,0x1af1, -0x1af1,0x1af1,0x1af1,0x1bbd,0x1bbd,0x1bbd,0x1c35,0x1c35,0x279,0x279,0x279,0x279,0x1bbd,0x1bbd,0x1bbd,0x1bbd, -0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1c35,0x279,0x279,0x279,0x279,0x279,0x279,0x279,0x1bc0,0x1bc0,0x1bc0,0x1bc0, -0x1bc0,0x1bc0,0x1bc0,0x1c38,0x1c38,0x279,0x279,0x279,0x279,0x279,0x279,0x279,0x1a25,0x1a1f,0x1a1f,0x1a1f, -0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x27c,0x27c, -0x27c,0x27c,0x27c,0x27c,0x27c,0x27c,0x27c,0x27c,0x27c,0x27c,0x27c,0x1a22,0x1a31,0x1a31,0x1a31,0x1a31, -0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a28,0x1a28,0x1a28,0x1a28,0x1a2e,0x1a2e,0x1a2e,0x1a2e, -0x1a2e,0x1a2e,0x1a2e,0x1a2e,0x1a2e,0x1a2e,0x27f,0x27f,0x27f,0x27f,0x27f,0x1a2b,0x1a9d,0x1a9d,0x1a9d,0x1a9d, -0x1a9d,0x1a9a,0x1a9a,0x1a9a,0x1a9a,0x1a9a,0x1a9a,0x1a9a,0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x282, -0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x1ab8,0x1ab8,0x1ab8,0x1ab8, -0x1ab8,0x1ab8,0x1ab8,0x285,0x285,0x1ab8,0x285,0x285,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8, -0x285,0x1ab8,0x1ab8,0x285,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8, -0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1aa0,0x1aaf,0x1aaf,0x1aaf,0x1aaf,0x1aaf,0x285,0x1aaf,0x1ab2,0x285,0x285,0x1aa0, -0x1aa0,0x1ab5,0x1aa6,0x1abb,0x1aaf,0x1abb,0x1aaf,0x1aa3,0x1abe,0x1aa9,0x1abe,0x285,0x285,0x285,0x285,0x285, -0x285,0x285,0x285,0x285,0x1aac,0x1aac,0x1aac,0x1aac,0x1aac,0x1aac,0x1aac,0x1aac,0x1aac,0x1aac,0x285,0x285, -0x285,0x285,0x285,0x285,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7, -0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x288,0x288,0x288,0x288,0x288,0x288, -0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288, -0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x1ac4,0x1ac4,0x1ac4,0x1ac4, -0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x28e, -0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x28e, -0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e, -0x28e,0x28e,0x28e,0x28e,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x28e,0x28e, -0x28e,0x28e,0x28e,0x28e,0x1afa,0x1afa,0x1afa,0x1afa,0x1afa,0x1afa,0x1afa,0x1afa,0x1afa,0x291,0x291,0x291, -0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291, -0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291, -0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x294,0x1aca,0x1aca,0x1acd,0x294,0x294, -0x1ad0,0x1ad0,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294, -0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b60,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x297, -0x1b66,0x1b66,0x297,0x297,0x297,0x297,0x297,0x297,0x1b5d,0x1b5d,0x1b5d,0x1b5d,0x1b5d,0x1b5d,0x1b5d,0x1b5d, -0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c, -0x1b6c,0x1b69,0x1b69,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a, -0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x29d,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x29d,0x1b6f,0x1b6f,0x29d, -0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x29d, -0x1b72,0x1b78,0x1b78,0x1b75,0x1b75,0x1b75,0x2a3,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75, -0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75, -0x1b75,0x2a3,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x2a3,0x2a3,0x2a3,0x2a3,0x2a3, -0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7e,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b, -0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x2a6, -0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x1bf0,0x1bf0,0x1bf0,0x1bf0,0x1bf0,0x1bf0,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6, -0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6, -0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x1b84,0x1b84,0x1b81,0x1b81, -0x1b81,0x1b81,0x1b87,0x1b87,0x1b87,0x1b87,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9, -0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x1a9d,0x1a9d,0x1a9d,0x1a9d, -0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1b8d,0x1b8d,0x1b8d,0x1b8d, -0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d, -0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x2ac,0x1b8a,0x1b8a,0x1b8a,0x1b8a, -0x1b8a,0x1b8a,0x1b8a,0x1b8a,0x1b8a,0x1b8a,0x2ac,0x2ac,0x2ac,0x2ac,0x2ac,0x2ac,0x1554,0x1554,0x1554,0x1554, -0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1b93,0x1b93,0x1b93,0x1b93, -0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b90,0x2af,0x2af,0x2af,0x2af,0x2af, -0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x1b9c,0x1b9c,0x1b9c,0x1b9c, -0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x2b2,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x2b2, -0x1b9c,0x1b9c,0x2b2,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x2b2,0x1b99, -0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x2b2,0x1b99, -0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x2b2,0x1b99,0x1b99,0x2b2,0x2b2,0x2b2,0x1ba2,0x1ba2,0x1ba2,0x1ba2, -0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x2b5,0x2b5,0x1ba2,0x1ba2,0x1ba2,0x1ba2, -0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x2b5, -0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f, -0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8, -0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8, -0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x1bf3,0x1bf3,0x1bf3,0x1bf9,0x1bf9,0x1bf9,0x1bf9, -0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bfc,0x1bfc,0x1bfc,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb, -0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x1bff,0x1bff,0x1bff,0x1bff, -0x1bff,0x1bff,0x1bff,0x1bff,0x1bff,0x1bff,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be, -0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be, -0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02, -0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x2c1,0x2c1,0x2c1,0x2c1, -0x2c1,0x2c1,0x2c1,0x2c1,0x2c1,0x2c1,0x2c1,0x2c1,0x1c05,0x1c05,0x1c1a,0x1c11,0x1c17,0x1c17,0x1c17,0x1c17, -0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x2c4,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17, -0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17, -0x1c11,0x1c11,0x1c05,0x1c05,0x1c05,0x1c05,0x1c05,0x2c4,0x2c4,0x2c4,0x1c11,0x1c11,0x1c05,0x1c14,0x1c08,0x1c1d, -0x1c1d,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0e,0x1c0e,0x1c0e,0x1c0e, -0x1c0e,0x1c0e,0x1c0e,0x1c0e,0x1c0e,0x1c0e,0x2c4,0x2c4,0x2c4,0x2c4,0x2c4,0x2c4,0x1c26,0x1c26,0x1c26,0x1c26, -0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c20,0x1c20,0x1c20,0x1c20,0x1c23,0x1c23,0x1c23,0x1c23, -0x1c23,0x1c23,0x1c23,0x1c23,0x1c23,0x1c23,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2ca,0x2ca,0x2ca,0x2ca, -0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca, -0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x18db,0x2dc,0x2dc,0x2dc, -0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2ca,0x2ca,0x2ca,0x2ca, -0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca, -0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x95d,0x95d,0x1c3b,0x1c3b,0x1c3b,0x1c3b, -0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x2ca,0x2ca,0x2ca,0x2ca, -0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0xc6c,0xc6c,0xc6c,0xc6c, -0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0x129f,0x129f,0x129f,0x2cd,0x2cd,0xe94,0xe94,0xe94,0xe94, -0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94, -0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd, -0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd, -0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88, -0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88, -0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0x2d0,0x2d0,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1, -0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x1bab,0x1bab,0x1bab, -0x1bab,0x1c29,0x2d3,0x2d3,0x2d3,0x2d3,0x2d3,0x2d3,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8, -0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8, -0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x2d6,0x2d6,0x1785,0x1785,0x2d9,0x2d9,0x2d9,0x2d9,0x2d9,0x2d9, -0x2d9,0x2d9,0x2d9,0x2d9,0x2d9,0x2d9,0x2d9,0x2d9,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db, -0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4, -0x1af4,0x1af4,0x1af4,0x2df,0x2df,0x2df,0x2df,0x2df,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b, -0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x3e7,0x3db,0x3db,0x3db,0x3db,0x3db,0x3db,0x3db, -0x3db,0x3e7,0x3e7,0x3e7,0x3e7,0x3e1,0x111f,0x12f6,0x3ea,0x927,0x92a,0x3d8,0x3d8,0x111c,0x12f3,0x12f3, -0x3ed,0x3ed,0x3ed,0x3ed,0x3ed,0x3ed,0x3ed,0x3ed,0x111c,0x3db,0x3db,0x3e7,0xcae,0x3ea,0x3ea,0x3ea, -0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea, -0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3db,0x3db,0x8af,0x8b2,0x945,0x945, -0x945,0x945,0x945,0x945,0x945,0x945,0x945,0x945,0x3e4,0xf7e,0xf7b,0x12f9,0x12f9,0x12f9,0x12f9,0x12f9, -0x14b8,0x1122,0x1122,0xed0,0xed0,0xda1,0xed0,0xed0,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea, -0x3ea,0x3ed,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ed,0x3ea,0x3ea,0x3ed,0x3ea,0x3ea,0x3ea, -0x3ea,0x3ea,0x12f3,0x12f6,0x3de,0x3ea,0x3e7,0x3e7,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489, -0x489,0x12ff,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489, -0x489,0x489,0x12ff,0x1857,0x1857,0xf9c,0x47a,0x483,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5, -0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0xba3, -0xba3,0xdb0,0xdb0,0x8b5,0xdad,0x13da,0x13da,0x13da,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8, -0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8, -0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4ce,0x4ce,0x4ce,0x1137,0x1137,0x1137,0x1137,0x1137, -0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb, -0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb, -0x4cb,0x4cb,0x1134,0x1134,0x1134,0x1134,0x1134,0x1134,0x4d1,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce, -0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce, -0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4da,0x4d4,0x4da,0x4d4, -0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4, -0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4d4,0x4d4, -0x4d4,0x4d4,0x4d7,0x9a2,0xfc9,0xfc9,0xfcc,0xfc9,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4, -0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4, -0x4da,0x4d4,0xfcc,0xfc9,0xfcc,0xfc9,0xfcc,0xfc9,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6, -0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6, -0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x6a5,0x6a5,0x6a8,0x504,0x6b4,0x6b1,0x6b1,0x6ae, -0x52e,0x52e,0x4ec,0x4ec,0x4ec,0x4ec,0x4ec,0xb34,0x6b7,0x510,0x6cf,0x6d2,0x525,0x6b7,0x513,0x513, -0x504,0x51f,0x51f,0x6a5,0x52b,0x528,0x6ab,0x4fe,0x4f5,0x4f5,0x4f8,0x4f8,0x4f8,0x4f8,0x4f8,0x4fb, -0x4f8,0x4f8,0x4f8,0x4ef,0x537,0x534,0x531,0x531,0x6c3,0x519,0x516,0x6c0,0x6bd,0x6ba,0x6cc,0x507, -0x6c9,0x6c9,0x51c,0x51f,0x6c6,0x6c6,0x51c,0x51f,0x501,0x504,0x504,0x504,0x522,0x50d,0x50a,0xbb8, -0xad7,0xad7,0xad4,0xad4,0xad4,0xad4,0xbaf,0xbaf,0xbaf,0xbaf,0xbb5,0xcdb,0xcd8,0xdbc,0xdbf,0xbb2, -0xdbf,0xdbf,0xdbf,0xdbf,0xdbc,0xdbf,0xdbf,0xbac,0x55b,0x55b,0x55b,0x55b,0x55b,0x55b,0x55b,0x558, -0x55e,0x73e,0x55b,0x9a5,0x9c6,0xada,0xada,0xada,0xbbe,0xbbe,0xdc5,0xdc5,0xdc5,0xdc5,0x1140,0x1143, -0x1143,0x1314,0x14a6,0x14d0,0x14d3,0x14d3,0x16da,0x185a,0x56a,0x56a,0x582,0x6e4,0x567,0x6de,0x56a,0x57f, -0x567,0x6e4,0x579,0x582,0x582,0x582,0x579,0x579,0x582,0x582,0x582,0x6ea,0x567,0x582,0x6e7,0x567, -0x576,0x582,0x582,0x582,0x582,0x582,0x567,0x567,0x56d,0x6de,0x6e1,0x567,0x582,0x567,0x6ed,0x567, -0x582,0x570,0x588,0x6f0,0x582,0x582,0x573,0x579,0x582,0x582,0x585,0x582,0x579,0x57c,0x57c,0x57c, -0x57c,0xae3,0xae0,0xcde,0xdce,0xbd3,0xbd6,0xbd6,0xbd0,0xbcd,0xbcd,0xbcd,0xbcd,0xbd6,0xbd3,0xbd3, -0xbd3,0xbd3,0xbca,0xbcd,0xdcb,0xedc,0xedf,0xfd2,0x1146,0x1146,0x1146,0x6f6,0x6f3,0x58b,0x58e,0x58e, -0x58e,0x58e,0x58e,0x6f3,0x6f6,0x6f6,0x6f3,0x58e,0x6fc,0x6fc,0x6fc,0x6fc,0x6fc,0x6fc,0x6fc,0x6fc, -0x6fc,0x6fc,0x6fc,0x6fc,0x597,0x597,0x597,0x597,0x6f9,0x6f9,0x6f9,0x6f9,0x6f9,0x6f9,0x6f9,0x6f9, -0x6f9,0x6f9,0x591,0x591,0x591,0x591,0x591,0x591,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d, -0x59a,0x59d,0x59d,0x59d,0x59d,0x59d,0x5a0,0x59a,0x59d,0x59d,0x59a,0x59a,0x59a,0x59a,0x59d,0x59d, -0x6ff,0x6ff,0x59a,0x59a,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d, -0x59d,0x5a0,0x5a0,0x5a0,0x59d,0x59d,0x702,0x59d,0x702,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d, -0x59a,0x59d,0x59a,0x59a,0x59a,0x59a,0x59a,0x59a,0x59d,0x59d,0x59a,0x6ff,0x59a,0x59a,0x59a,0xae9, -0xae9,0xae9,0xae9,0xae9,0xae9,0xae9,0xae9,0xae9,0xbd9,0xbd9,0xbd9,0xbd9,0xbd9,0xbd9,0xbd9,0xbd9, -0xbd9,0xbd9,0xbd9,0xbd9,0x705,0x5a3,0x705,0x705,0x5a6,0x5a3,0x5a3,0x705,0x705,0x5a6,0x5a3,0x705, -0x5a6,0x5a3,0x5a3,0x705,0x5a3,0x705,0x5b2,0x5af,0x5a3,0x705,0x5a3,0x5a3,0x5a3,0x5a3,0x705,0x5a3, -0x5a3,0x705,0x705,0x705,0x705,0x5a3,0x5a3,0x705,0x5a6,0x705,0x5a6,0x705,0x705,0x705,0x705,0x705, -0x70b,0x5a9,0x705,0x5a9,0x5a9,0x5a3,0x5a3,0x5a3,0x705,0x705,0x705,0x705,0x5a3,0x5a3,0x5a3,0x5a3, -0x705,0x705,0x5a3,0x5a3,0x5a3,0x5a6,0x5a3,0x5a3,0x5a6,0x5a3,0x5a3,0x5a6,0x705,0x5a6,0x5a3,0x5a3, -0x705,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x705,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3, -0x5a3,0x5a3,0x5a3,0x5a3,0x708,0x705,0x5a6,0x5a3,0x705,0x705,0x705,0x705,0x5a3,0x5a3,0x705,0x705, -0x5a3,0x5a6,0x708,0x708,0x5a6,0x5a6,0x5a3,0x5a3,0x5a6,0x5a6,0x5a3,0x5a3,0x5a6,0x5a6,0x5a3,0x5a3, -0x5a3,0x5a3,0x5a3,0x5a3,0x5a6,0x5a6,0x705,0x705,0x5a6,0x5a6,0x705,0x705,0x5a6,0x5a6,0x5a3,0x5a3, -0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x705,0x5a3,0x5a3,0x5a3,0x705,0x5a3,0x5a3, -0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x705,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a6,0x5a6,0x5a6,0x5a6, -0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x705, -0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3, -0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3, -0x5a6,0x5a6,0x5a6,0x5a6,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a6,0x5a6,0x5a6,0x5a6,0x5a3,0x5ac, -0x5a3,0x5a3,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc, -0x5b5,0xaec,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5be,0x5bb,0x5be,0x5bb,0x5b5,0x5b5,0x5b5,0x5b5, -0x5b5,0x5b5,0x70e,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x7fe,0x7fe,0x5b5,0x5b5,0x5b5,0x5b5, -0x5b8,0x5b8,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x804,0x801,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5, -0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5, -0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0xaec, -0xbe2,0xaec,0xaec,0xaec,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1, -0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1, -0x5c1,0x5c1,0x5c1,0x5c1,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x5c7,0xc3f, -0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f, -0xc3f,0xc3f,0xc3f,0xd4d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d, -0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x5ca,0x5cd,0x5cd,0x5cd,0x5cd,0x5cd,0x5cd,0x5cd, -0x5cd,0x5cd,0x5cd,0x5cd,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d, -0x5cd,0x5cd,0x5cd,0x5cd,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d, -0x71d,0x71d,0x71d,0x71d,0x720,0x720,0x720,0x720,0x720,0x720,0x720,0x720,0x720,0x720,0x720,0x720, -0x720,0x720,0x720,0x720,0x5d0,0x5d0,0x720,0x720,0x720,0x720,0xbe5,0xbe5,0xbe5,0xbe5,0xbe5,0xbe5, -0xbe5,0xbe5,0xbe5,0xbe5,0x726,0x726,0x5d3,0x723,0x723,0x723,0x723,0x723,0x723,0x723,0x5d3,0x5d3, -0x5d3,0x5d3,0x5d6,0x5d6,0x5d6,0x5d6,0x726,0x726,0x5d6,0x5d6,0x726,0x726,0x5d3,0x5d3,0x5d3,0x5d3, -0x726,0x726,0x5d6,0x5d6,0x726,0x726,0x5d3,0x5d3,0x5d3,0x5d3,0x726,0x726,0x723,0x5d3,0x5d6,0x726, -0x5d3,0x5d3,0x723,0x726,0x726,0x726,0x5d6,0x5d6,0x5d3,0x5d3,0x5d3,0x5d3,0x5d3,0x5d3,0x5d3,0x5d3, -0x5d3,0x5d3,0x5d3,0x5d3,0x5d3,0x5d3,0x726,0x723,0x726,0x723,0x5d3,0x5d6,0x5d6,0x5d6,0x5d6,0x5d6, -0x5d6,0x5d3,0x5d3,0x723,0xaf2,0xaf2,0xaf2,0xaf2,0xaf2,0xaf2,0xaf2,0xaf2,0xbe8,0xbe8,0xbe8,0xbe8, -0xbe8,0xc57,0xc57,0xbe8,0x5dc,0x5dc,0x5dc,0x5dc,0x5d9,0x72f,0x72f,0x5d9,0x5d9,0x729,0x5d9,0x5d9, -0x5d9,0x5d9,0x729,0x729,0x5d9,0x5d9,0x5d9,0x5d9,0xd56,0xd56,0xbeb,0xbeb,0xdd7,0xaf5,0x5dc,0x5dc, -0x72c,0x5df,0x72c,0x5dc,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9, -0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5dc,0x5dc,0x5dc, -0x5d9,0x5d9,0x5d9,0x5d9,0x72f,0x5d9,0x72f,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x807,0x807,0x807,0x807, -0x807,0x807,0x807,0x807,0x807,0x807,0x807,0x807,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9, -0x5d9,0x5d9,0x5d9,0x5d9,0x72f,0x72f,0x5e2,0x72f,0x729,0x729,0x5d9,0x729,0x72c,0x729,0x729,0x5d9, -0x729,0x72f,0x5e2,0x72f,0xaf5,0xaf5,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee, -0xbee,0xbee,0xdd4,0xe8b,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5, -0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e8,0x139b,0x139b,0x139b,0x5e8,0x5e8,0x5e8,0x5e8, -0x5e8,0x5e8,0x5e8,0x5e8,0x14d9,0x5ee,0x5ee,0x5ee,0x5ee,0x139b,0x5e8,0x5e8,0x5ee,0x5ee,0x139e,0x139e, -0x5f4,0x5f4,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8, -0x5e8,0x5e8,0x5e8,0x5e8,0x139b,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8, -0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x735,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8, -0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x139b,0x5e8,0x139b,0x5e8,0x5e8,0x5e8,0x5e8,0x139b, -0x139b,0x139b,0x5e8,0x1299,0x5e8,0x5e8,0x5e8,0x5f1,0x5f1,0x5f1,0x5f1,0x1320,0x1320,0x5e8,0x5eb,0x5eb, -0x5ee,0x5e8,0x5e8,0x5e8,0xbf4,0xbf1,0xbf4,0xbf1,0xbf4,0xbf1,0xbf4,0xbf1,0xbf4,0xbf1,0xbf4,0xbf1, -0xbf4,0xbf1,0x732,0x732,0x732,0x732,0x732,0x732,0x732,0x732,0x732,0x732,0x5e8,0x5e8,0x5e8,0x5e8, -0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x139b,0x5e8,0x5e8,0x5e8, -0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x139b,0x615,0x615,0x615,0x615, -0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615, -0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x60c,0x60c,0x60c,0x60c,0x60c,0x60c, -0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f, -0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0xb46,0xb46,0xb46,0xb46, -0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0x615,0x615,0x95a,0x615, -0x615,0x615,0x615,0x615,0x615,0x615,0x60c,0x60c,0xbf7,0xd7a,0x1b0f,0x1b0f,0x612,0x618,0x615,0x60f, -0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f, -0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f,0x615,0x60f,0x615,0x60f, -0x615,0x60f,0x615,0x60f,0x615,0x60f,0x615,0x60f,0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f, -0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f,0x615,0x60f,0x612,0x618,0x615,0x60f,0x615,0x60f, -0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f,0x615,0x60f,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323, -0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x615,0x60f,0x615,0x60f,0x615,0x60f,0x612,0x618, -0x612,0x618,0x615,0x60f,0x615,0x60f,0x615,0x60f,0x615,0x60f,0x615,0x60f,0x615,0x60f,0x615,0x60f, -0x612,0x615,0x60f,0x612,0x615,0x60f,0x612,0x618,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f, -0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x612, -0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615, -0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f, -0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x612,0x612,0x612,0x612,0x612,0x612,0x612, -0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x60f,0x615,0x918,0x91b,0x1b0f,0x1b0f,0x1b0f,0x1b0f, -0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x612,0x60f,0x612,0x612, -0x612,0x612,0x612,0x612,0x60f,0x612,0x60f,0x60f,0x612,0x612,0x60f,0x60f,0x612,0x612,0x60f,0x612, -0x60f,0x612,0x60f,0x60f,0x612,0x60f,0x60f,0x612,0x60f,0x612,0x60f,0x60f,0x612,0x60f,0x612,0x612, -0x60f,0x60f,0x60f,0x612,0x60f,0x60f,0x60f,0x60f,0x60f,0x612,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f, -0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f, -0x612,0x612,0x60f,0x60f,0x612,0x60f,0x612,0x60f,0x60f,0x60f,0x60f,0x60f,0x612,0x612,0x612,0x612, -0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612, -0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x618, -0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615, -0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615, -0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618, -0x618,0x618,0x618,0x618,0x618,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615, -0x61b,0x61b,0x61b,0x61b,0xfde,0xfde,0xfde,0x14dc,0x14dc,0x14dc,0x14dc,0x14dc,0x14dc,0x14dc,0x16e0,0x16e0, -0x864,0x86a,0x86a,0x876,0x876,0x867,0x85e,0x867,0x85e,0x867,0x85e,0x867,0x85e,0x867,0x85e,0x867, -0x62a,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x62a, -0x624,0x627,0x62d,0x62a,0x624,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627, -0x62d,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627, -0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627, -0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627,0x714,0x714,0x714,0x714, -0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714, -0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711, -0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711, -0x711,0x711,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a, -0x71a,0x71a,0x71a,0x71a,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717, -0x717,0x717,0x717,0x717,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d, -0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d, -0x71d,0x71d,0x71d,0x71d,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738, -0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738, -0x738,0x738,0x738,0x738,0xc45,0x8c7,0x8c1,0x8be,0x8c4,0x8bb,0x74d,0x750,0x750,0x750,0x750,0x750, -0x750,0x750,0x750,0x750,0x8cd,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d, -0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d, -0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x8ca,0x8ca,0x753,0x8dc,0x8df,0x8e5,0x80a,0x816,0x8fa,0x813, -0x8d3,0x8d0,0x8d3,0x8d0,0x8d9,0x8d6,0x8d9,0x8d6,0x8d3,0x8d0,0x810,0x8e5,0x8d3,0x8d0,0x8d3,0x8d0, -0x8d3,0x8d0,0x8d3,0x8d0,0x8eb,0x8f1,0x8ee,0x8ee,0x759,0x795,0x795,0x795,0x795,0x795,0x795,0x78f, -0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f, -0x78f,0x78f,0x78f,0x75c,0x777,0x756,0x77d,0x780,0x77a,0x792,0x792,0x792,0x792,0x792,0x792,0x78c, -0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c, -0x78c,0x78c,0x78c,0x75c,0x777,0x756,0x777,0xc48,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8, -0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8, -0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x1293,0x1293,0x1293,0x1293,0x1293,0x7fb, -0x810,0x813,0x813,0x813,0x813,0x813,0x813,0x813,0x813,0x813,0x933,0x933,0x933,0x933,0x819,0x819, -0x8e8,0x8f7,0x8f7,0x8f7,0x8f7,0x8f4,0x80d,0x8e2,0xb19,0xb19,0xb19,0xc5a,0xc78,0xc75,0xb37,0x8b8, -0x81f,0x81c,0x81f,0x822,0x81c,0x81f,0x81c,0x81f,0x81c,0x81f,0x81c,0x81c,0x81c,0x81c,0x81c,0x81c, -0x81f,0x81f,0x81c,0x81f,0x81f,0x81c,0x81f,0x81f,0x81c,0x81f,0x81f,0x81c,0x81f,0x81f,0x81c,0x81c, -0xc7b,0x831,0x82b,0x831,0x82b,0x831,0x82b,0x831,0x82b,0x831,0x82b,0x82b,0x82e,0x82b,0x82e,0x82b, -0x82e,0x82b,0x82e,0x82b,0x82e,0x82b,0x82e,0x82b,0x82e,0x82b,0x82e,0x82b,0x82e,0x82b,0x82e,0x82b, -0x82e,0x82b,0x82e,0x831,0x82b,0x82e,0x82b,0x82e,0x82b,0x82e,0x82b,0x82b,0x82b,0x82b,0x82b,0x82b, -0x82e,0x82e,0x82b,0x82e,0x82e,0x82b,0x82e,0x82e,0x82b,0x82e,0x82e,0x82b,0x82e,0x82e,0x82b,0x82b, -0x82b,0x82b,0x82b,0x831,0x82b,0x831,0x82b,0x831,0x82b,0x82b,0x82b,0x82b,0x82b,0x82b,0x831,0x82b, -0x82b,0x82b,0x82b,0x82b,0x82e,0x831,0x831,0x82e,0x82e,0x82e,0x82e,0x900,0x903,0x834,0x837,0xc63, -0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d, -0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d, -0x840,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d, -0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x849,0x849,0x849,0x849, -0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849, -0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0xd5f,0xd5f,0xe8e,0x843,0x90c,0x90c,0x90c,0x90c, -0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0xd59,0xd59,0xd59,0xd59,0x84c,0x84c,0x84c,0x84c, -0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c, -0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x1a58,0x912,0x912,0x912,0x912, -0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x84f,0x84f,0x84f, -0x84f,0x84f,0x84f,0xd62,0xd62,0xd62,0xd62,0x915,0x915,0x915,0x915,0x915,0x84f,0x84f,0x84f,0x84f, -0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f, -0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0xd62,0xd62, -0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852, -0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852, -0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855, -0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855, -0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91, -0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91, -0x1104,0x1104,0x1104,0x1104,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858, -0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858, -0x858,0x858,0x858,0x858,0x858,0x858,0x85b,0x85b,0x858,0x85b,0x858,0x85b,0x85b,0x858,0x858,0x858, -0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x85b,0x858,0x85b,0x858,0x85b,0x85b,0x858,0x858,0x85b, -0x85b,0x85b,0x858,0x858,0x858,0x858,0x1497,0x1497,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c, -0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c, -0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c, -0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x12d2,0x12d2,0x12d2,0x12d2,0x127b,0x127b,0x127b,0x127b, -0x127b,0x127b,0x127b,0x127b,0xd59,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66, -0xc66,0xc66,0xc66,0xc66,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f, -0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f, -0x90f,0x90f,0x90f,0x90f,0x90f,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66, -0xc66,0xc66,0xc66,0xc66,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912, -0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912, -0x912,0x912,0x912,0xd62,0x99c,0x97e,0x97e,0x97e,0x97e,0x978,0x97e,0x97e,0x990,0x97e,0x97e,0x97b, -0x987,0x98d,0x98d,0x98d,0x98d,0x98d,0x990,0x978,0x984,0x978,0x978,0x978,0x96c,0x96c,0x978,0x978, -0x978,0x978,0x978,0x978,0x993,0x993,0x993,0x993,0x993,0x993,0x993,0x993,0x993,0x993,0x978,0x978, -0x978,0x978,0x978,0x978,0x978,0x978,0x978,0x978,0x97b,0x96c,0x978,0x96c,0x978,0x96c,0x98a,0x981, -0x98a,0x981,0x999,0x999,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8, -0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8, -0x9a8,0x9a8,0x9a8,0x9a8,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab, -0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab, -0x9ab,0x9ab,0x9ab,0x9ab,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae, -0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae, -0x9ae,0x9ae,0x9ae,0x9ae,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7, -0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7, -0x9b7,0x9b7,0x9b1,0x9b1,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba, -0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba, -0x9ba,0x9ba,0x9b4,0x9b4,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7, -0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7, -0x9b7,0x9b7,0x9b7,0x9b7,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba, -0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba, -0x9ba,0x9ba,0x9ba,0x9ba,0x9bd,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0, -0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0, -0x9bd,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0, -0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0xa4d,0xa4d,0xfc3,0xa4d, -0xa4d,0xa4d,0xa50,0xa4d,0xfc3,0xa4d,0xa4d,0xfba,0xa47,0xa3b,0xa3b,0xa3b,0xa3b,0xa4a,0xa3b,0xfab, -0xfab,0xfab,0xa3b,0xa3e,0xa47,0xa41,0xfb1,0xfbd,0xfbd,0xfab,0xfab,0xfc3,0xb3d,0xb3d,0xb3d,0xb3d, -0xb3d,0xb3d,0xb3d,0xb3d,0xb3d,0xb3d,0xa53,0xa53,0xa44,0xa44,0xa44,0xa44,0xa4d,0xa4d,0xa4d,0xa4d, -0xa4d,0xa4d,0xa4a,0xa4a,0xa3b,0xa3b,0xfc3,0xfc3,0xfc3,0xfc3,0xfab,0xfab,0xa4d,0xa4d,0xa4d,0xa4d, -0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d, -0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa62,0xa62,0xa62,0xa62, -0xa62,0xa62,0xa62,0xdb9,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62, -0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62, -0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xdb9,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62, -0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68, -0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68, -0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e, -0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6b,0xa71,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0x113d, -0x113d,0x113d,0x113d,0x113d,0x113d,0x113d,0x113d,0x113d,0x113a,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e, -0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e, -0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa83,0xa83,0xa83,0xa83, -0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83, -0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xaa7,0xaa7,0xaa7,0xaaa, -0xaaa,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7, -0xa8f,0xa8f,0xaa4,0xa86,0xa86,0xa86,0xa86,0xa86,0xa86,0xa86,0xaa4,0xaa4,0xaa7,0xaa7,0xaa7,0xaa7, -0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7, -0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xac8,0xac8,0xac8,0xac8, -0xac8,0xab3,0xab3,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8, -0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8, -0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xacb,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8, -0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8, -0xac8,0xac8,0xac8,0xac8,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec, -0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xbe2, -0xbe2,0xbe2,0xbe2,0xbe2,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8, -0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8, -0xaf8,0xaf8,0xaf8,0xaf8,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a, -0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a, -0xb0a,0xb0a,0xb0a,0xb0a,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10, -0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10, -0xb10,0xb10,0xb10,0xb10,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c, -0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0x13a1,0x13a1,0x13a1,0x1ad3, -0x1ad3,0x1ad3,0x1ad3,0x1ad3,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f, -0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f, -0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0x1ad6,0x1ad6,0x1ad6,0x1ad6,0x1ad6,0x1ad6,0x1ad6,0x1ad6,0x1ad6,0x1ad6, -0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22, -0xb22,0xb22,0xb22,0xb22,0xb22,0xb25,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22, -0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22, -0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb28,0xb28,0xc69,0xc69,0xb28,0xb28,0xb28,0xb28, -0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xc69,0xb28,0xb28,0xb28, -0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c, -0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c, -0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0x14df,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xced,0xced, -0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52, -0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xcea,0xcea,0xd38,0xd38,0xd38,0xd38, -0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xb55,0xb55,0xb55,0xb55, -0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55, -0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb58,0xb58,0xb58,0xb58, -0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58, -0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb67,0xb67,0xb67,0xb67, -0xb67,0xb5e,0xb6a,0xb70,0xb70,0xb70,0xb64,0xb64,0xb64,0xb6d,0xb61,0xb61,0xb61,0xb61,0xb61,0xb5b, -0xb5b,0xb5b,0xb5b,0xb5b,0xb5b,0xb5b,0xb5b,0xb70,0xb70,0xb70,0xb70,0xb70,0xb64,0xb64,0xb64,0xb64, -0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64, -0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb67,0xb67, -0xb70,0xb70,0xb70,0xb64,0xb64,0xb70,0xb70,0xb70,0xb70,0xb70,0xb70,0xb70,0xb64,0xb64,0xb64,0xb64, -0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64, -0xb64,0xb64,0xb70,0xb70,0xb70,0xb70,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64, -0xb64,0xb64,0xb64,0xb67,0xb67,0xb67,0xb67,0xb67,0xb67,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64, -0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64, -0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0x16e3,0x16e3,0xb7c,0xb73,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, -0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, -0xb79,0xb79,0xb79,0xb73,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, -0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, -0xb7c,0xb7c,0xb7c,0xb73,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, -0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb73,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, -0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, -0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb73,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, -0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb76,0xb76,0xb76,0xb76, -0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76, -0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb7c,0xb7c,0xb7c,0xb7c, -0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, -0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, -0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb7c,0xb7c,0xb7c,0xb7c, -0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, -0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, -0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, -0xb7c,0xb7c,0xb7c,0xb7c,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f, -0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f, -0xb7f,0xb7f,0xb7f,0xb7f,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85, -0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85, -0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc,0x1ba8,0x1ba8, -0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88, -0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88, -0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2, -0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbdf,0xbe2,0xbdf,0xbdf,0xbdf,0xbdf,0xbdf,0xbdf,0xbdf,0xbdf, -0xbdf,0xbdf,0xbdf,0xbdf,0xbdf,0xbdf,0xbdf,0xce1,0xce4,0xdd1,0xdd1,0xdd1,0xdd1,0xdd1,0xdd1,0xdd1, -0xdd1,0xdd1,0xdd1,0xdd1,0xee8,0xee8,0xee8,0xee8,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee, -0xbee,0xbee,0xce7,0xce7,0xce7,0xce7,0xce7,0xce7,0xce7,0xce7,0xdd4,0xe88,0xdd4,0xdd4,0xdd4,0xdd4, -0xdd4,0xdd4,0xdd4,0xdd4,0xdd4,0xfd8,0x1272,0x1272,0xddd,0xddd,0xddd,0xddd,0xddd,0xde3,0xde0,0xefa, -0xefa,0xefa,0xefa,0x13e0,0xfea,0x13e0,0x132c,0x132c,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21, -0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc4e,0xc4b,0xc4e,0xc4b,0xc4e,0xc4b, -0x10fe,0x10fb,0xff0,0xfed,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24, -0xc24,0xc24,0xc24,0xc24,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27, -0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27, -0xc27,0xc27,0xc27,0xc27,0xc2a,0xc2a,0xc2a,0xc30,0xc2d,0xc54,0xc51,0xc30,0xc2d,0xc30,0xc2d,0xc30, -0xc2d,0xc30,0xc2d,0xc30,0xc2d,0xc30,0xc2d,0xc30,0xc2d,0xc30,0xc2d,0xc30,0xc2d,0xc2a,0xc2a,0xc2a, -0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a, -0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a, -0xc30,0xc2d,0xc30,0xc2d,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a, -0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a, -0xc30,0xc2d,0xc2a,0xc2a,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33, -0xc39,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33, -0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33, -0xc33,0xc33,0xc33,0xc33,0xc39,0xc39,0xc39,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33, -0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33, -0xc33,0xc33,0xc33,0xc33,0xc36,0xc33,0xc33,0xc33,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c, -0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c, -0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xce7,0xd53,0xdd4,0xdd4,0xdd4,0xdd4,0xdd4,0xdd4, -0xdd4,0xdd4,0xe88,0xe88,0xdd4,0xdd4,0xdd4,0xdd4,0xdd4,0xdd4,0xeeb,0xfd8,0xfd8,0xfd8,0xfd8,0xfd8, -0xfd8,0xfd8,0xfd8,0xfd8,0xfd8,0x1296,0x1296,0x1275,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b, -0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b, -0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd11,0xd11, -0xd11,0xd11,0xd11,0xd0e,0xd23,0xd23,0xd23,0xd1d,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23, -0xd23,0xd23,0xd23,0xd1d,0xd23,0xd23,0xd23,0xd23,0xd17,0xd17,0xd20,0xd20,0xd20,0xd20,0xd14,0xd14, -0xd14,0xd14,0xd14,0xd1a,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9, -0xde6,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23, -0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd1d,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23, -0xd23,0xd23,0xd23,0xd23,0xd23,0xd17,0xd17,0xd17,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a, -0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a, -0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd26,0xd26,0xd26,0xd26,0xd26,0xd26,0xd26,0xd26, -0xd26,0xd26,0xd26,0xd26,0xd26,0xd26,0xdec,0xdec,0xdec,0xdec,0xdec,0xdec,0xefd,0xefd,0xefd,0xefd, -0xefd,0xefd,0xefd,0x1107,0x1107,0xff3,0xff3,0xff3,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29, -0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29, -0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f, -0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f, -0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38, -0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38, -0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44, -0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44, -0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50, -0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50, -0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2, -0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2, -0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8, -0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf5,0xdf5,0xdf5, -0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8, -0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8, -0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xeb8,0xeb8,0xe0a,0xe0a,0xf00,0xf00,0xf00,0xf00, -0xf00,0xf00,0xf00,0xfff,0xfff,0x1002,0xfff,0xfff,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc, -0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16, -0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16, -0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25, -0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25, -0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b, -0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0x1b1e,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, -0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0x1b1b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b, -0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b, -0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xeb2,0xeb2,0xeb2,0xeb2,0xeb2,0xeb2,0xeb2,0xeb2, -0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xf03, -0xf03,0xf03,0xf03,0x1005,0x1005,0x1005,0x1005,0x1005,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c, -0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c, -0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55, -0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55, -0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e, -0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e, -0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe58,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b, -0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b, -0xe5b,0xe5b,0xe5b,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe67,0xe67,0xe67,0xe67,0xe67,0xe67,0xe67,0xe67, -0xe67,0xe67,0xe67,0xe67,0xe67,0xe67,0xe64,0xe64,0xe64,0xe64,0xe64,0xe64,0xe64,0xe64,0xe61,0xe6a, -0x1011,0x100b,0x101a,0x1008,0xe67,0xe67,0x1008,0x1008,0xe79,0xe79,0xe6d,0xe79,0xe79,0xe79,0xe70,0xe79, -0xe79,0xe79,0xe79,0xe6d,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79, -0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c, -0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c, -0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94, -0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94, -0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5, -0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110, -0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0xee8,0xee8,0xee8,0xee5,0xee5,0xee5,0xee5,0xee5, -0x1149,0x1392,0x1392,0x1392,0x1392,0x1317,0x1317,0x1317,0x1395,0x131a,0x131a,0x1395,0x14d6,0x14d6,0x14d6,0x14d6, -0x14d6,0x14d6,0x14d6,0x1797,0x1797,0x1797,0x1797,0x185d,0xefd,0xefd,0xefd,0xefd,0xff3,0xff3,0xff3,0xff3, -0xff3,0xff3,0xff3,0xff3,0xff3,0xff3,0xff3,0xff3,0xff6,0xff6,0xff6,0xff6,0xff6,0xff6,0xff6,0xff6, -0xff6,0xff6,0xff6,0xff6,0xff6,0xff6,0xff6,0xff6,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0x14f1, -0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f4,0x1866,0x1866, -0x18e7,0x1866,0x1bc6,0x179d,0x132f,0x1152,0xf00,0xf00,0xf1e,0xf1e,0xf1e,0xf1e,0xf30,0xf39,0xf3c,0xf39, -0xf3c,0xf39,0xf3c,0xf39,0xf3c,0xf39,0xf3c,0xf39,0xf39,0xf39,0xf3c,0xf39,0xf39,0xf39,0xf39,0xf39, -0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39, -0xf21,0xf1e,0xf1e,0xf1e,0xf1e,0xf1e,0xf1e,0xf33,0xf1e,0xf33,0xf30,0xf30,0xf45,0xf42,0xf45,0xf45, -0xf45,0xf42,0xf42,0xf45,0xf42,0xf45,0xf42,0xf45,0xf42,0x102c,0x102c,0x102c,0x1167,0x1023,0x102c,0x1023, -0xf42,0xf45,0xf42,0xf42,0x1023,0x1023,0x1023,0x1023,0x1026,0x1029,0x1167,0x1167,0xf48,0xf48,0x103e,0x1035, -0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x1035,0x1035,0x103e,0x1035, -0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0xf4e,0xf4e,0xf4e,0xf4e, -0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e, -0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf5d,0xf5d,0xf5d,0xf5d, -0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d, -0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0x1512, -0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512, -0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63, -0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63, -0xfab,0xfc3,0xfba,0xfc0,0xfc0,0xfc3,0xfc3,0xfba,0xfba,0xfc0,0xfc0,0xfc0,0xfc0,0xfc0,0xfc3,0xfc3, -0xfc3,0xfab,0xfab,0xfab,0xfab,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3, -0xfc3,0xfc3,0xfab,0xfba,0xfbd,0xfab,0xfab,0xfc0,0xfc0,0xfc0,0xfc0,0xfc0,0xfc0,0xfae,0xfc3,0xfc0, -0xfb7,0xfb7,0xfb7,0xfb7,0xfb7,0xfb7,0xfb7,0xfb7,0xfb7,0xfb7,0x1131,0x1131,0x112e,0x112b,0xfb4,0xfb4, -0xfdb,0xfdb,0xfdb,0xfdb,0x1296,0x1296,0x1275,0x1275,0x1275,0x1272,0x1272,0x1272,0x1272,0x1275,0x1398,0x1275, -0x1275,0x1275,0x1272,0x1275,0x1296,0x1272,0x1272,0x1272,0x1275,0x1275,0x1272,0x1272,0x1275,0x1272,0x1272,0x1275, -0xff6,0xff6,0xff6,0xff6,0xff6,0xff3,0xff3,0xff6,0xff6,0xff6,0xff6,0xff6,0xff6,0x14eb,0x14eb,0x14eb, -0x1107,0xff3,0xff3,0xff3,0xff3,0x12a2,0x127e,0x127e,0x127e,0x127e,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb, -0x1017,0x1017,0x1014,0x100e,0x1014,0x100e,0x1014,0x100e,0x1014,0x100e,0x100b,0x100b,0x100b,0x100b,0x1020,0x101d, -0x100b,0x1164,0x13ec,0x13ef,0x13ef,0x13ec,0x13ec,0x13ec,0x13ec,0x13ec,0x13f2,0x13f2,0x1506,0x14fa,0x14fa,0x14f7, -0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x1032,0x102f,0x102f,0x103e,0x1035,0x133b,0x1338,0x16ec, -0x133b,0x1338,0x13fb,0x13f8,0x1509,0x1509,0x150f,0x1509,0x150f,0x1509,0x150f,0x1509,0x150f,0x1509,0x150f,0x1509, -0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035, -0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035, -0x1038,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x103e,0x1035, -0x1041,0x1041,0x1047,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d, -0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d, -0x104d,0x1047,0x1041,0x1041,0x1041,0x1041,0x1047,0x1047,0x1041,0x1041,0x104a,0x1404,0x1401,0x1401,0x104d,0x104d, -0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407, -0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062, -0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062, -0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b, -0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106e,0x106e,0x106e,0x1071,0x106e,0x106e,0x1074,0x1074, -0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077, -0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077, -0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1083,0x107a,0x1089,0x1086, -0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080, -0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080, -0x1341,0x133e,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x1098,0x1119, -0x108c,0x108c,0x108c,0x1092,0x140a,0x140a,0x140a,0x140a,0x140a,0x140a,0x140a,0x140a,0x108f,0x108f,0x1092,0x109e, -0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095, -0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095, -0x151e,0x151b,0x151e,0x151b,0x1521,0x1521,0x16f5,0x140a,0x10a7,0x10a7,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa, -0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa, -0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10a7,0x10a7,0x10a7,0x10a7,0x10a7,0x10a7,0x10a7,0x10a7, -0x10a7,0x10a7,0x10a7,0x10a7,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b0,0x10b0,0x10b0,0x10b0,0x10b0,0x10b3, -0x10b3,0x10b3,0x110d,0x10bc,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb, -0x10cb,0x10cb,0x10cb,0x10cb,0x10b6,0x10b6,0x10b6,0x10b6,0x10b6,0x10b6,0x10b6,0x10b6,0x10b6,0x10b6,0x10b9,0x10b9, -0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9, -0x10b9,0x10b9,0x10b9,0x10b9,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da, -0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da, -0x10da,0x10da,0x10da,0x10da,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec, -0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec, -0x10ec,0x10ec,0x10ec,0x10ec,0x10f5,0x10f5,0x10f5,0x10f5,0x110a,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5, -0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5, -0x10f5,0x10f5,0x10f5,0x10f5,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8, -0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8, -0x10f8,0x10f8,0x10f8,0x10f8,0x1104,0x1104,0x1104,0x1104,0x129c,0x129c,0x129c,0x129c,0x129c,0x129c,0x129c,0x129c, -0x1494,0x1776,0x1776,0x1776,0x1776,0x1776,0x1776,0x1776,0x1776,0x1776,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6, -0x18c6,0x18c6,0x18c6,0x18c6,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179, -0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1170,0x1170,0x1173,0x1173,0x1179,0x1170, -0x1170,0x1170,0x1170,0x1170,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f, -0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f, -0x117f,0x117f,0x117f,0x117f,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a, -0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a, -0x119a,0x119a,0x119a,0x119a,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6, -0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6, -0x11a6,0x11a6,0x11a3,0x11a9,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5, -0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5, -0x11b5,0x11b5,0x11b5,0x11b5,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb, -0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x12e7,0x11c1,0x12ea,0x11c1,0x11c1,0x11c1,0x11c1,0x11be,0x11be,0x11be,0x11c1, -0x16f8,0x16fb,0x1923,0x1920,0x11c4,0x11c4,0x11c4,0x11d3,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9, -0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9, -0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11c7,0x11d3,0x11d3,0x11c4,0x11c4,0x11c4,0x11c4,0x11d3,0x11d3, -0x11c4,0x11c4,0x11d3,0x11d3,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5, -0x11e5,0x11e5,0x11e5,0x11e5,0x11e8,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11df,0x11df,0x11df,0x11e5,0x11e2, -0x1527,0x152a,0x152d,0x152d,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7, -0x11f7,0x11f7,0x11f7,0x11f7,0x11eb,0x11f7,0x11eb,0x11eb,0x11eb,0x1200,0x1200,0x11eb,0x11eb,0x1200,0x11f7,0x1200, -0x1200,0x11f7,0x11eb,0x11ee,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7, -0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7, -0x11f7,0x11f7,0x11f7,0x11f7,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212, -0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212, -0x1212,0x1212,0x1212,0x1212,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a, -0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a, -0x122a,0x1227,0x1227,0x1227,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233, -0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233, -0x1233,0x1233,0x1233,0x1233,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242, -0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242, -0x1242,0x1242,0x1242,0x1242,0x1248,0x1248,0x1254,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257, -0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x125a,0x1257, -0x125a,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x125a, -0x1257,0x1257,0x1257,0x1257,0x1254,0x1254,0x1254,0x1248,0x1248,0x1248,0x1248,0x1254,0x1254,0x124e,0x124b,0x1251, -0x1251,0x1260,0x125d,0x125d,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263, -0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263, -0x1263,0x1263,0x1263,0x1263,0x1269,0x1269,0x1269,0x1266,0x1266,0x1266,0x1263,0x1263,0x1263,0x1263,0x1266,0x1263, -0x1263,0x1263,0x1269,0x1266,0x1269,0x1266,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263, -0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263, -0x1263,0x1269,0x1266,0x1266,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263, -0x1263,0x1263,0x1263,0x1bcf,0x19f5,0x19f5,0x19f5,0x19f5,0x19f5,0x19f5,0x19f5,0x19f8,0x19f2,0x1be1,0x1be1,0x1be1, -0x1be4,0x1bde,0x1be4,0x1bde,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1287, -0x1287,0x1287,0x126c,0x1929,0x138f,0x1290,0x138f,0x138f,0x138f,0x138f,0x138f,0x138f,0x138f,0x138f,0x138f,0x138f, -0x138f,0x1290,0x138f,0x1290,0x1275,0x1275,0x131d,0x1272,0x131d,0x131d,0x131d,0x131d,0x1272,0x1272,0x1296,0x1272, -0x1272,0x1272,0x1272,0x1272,0x1272,0x1275,0x1296,0x1296,0x1275,0x1296,0x1272,0x1275,0x1275,0x1278,0x1296,0x1272, -0x1272,0x1296,0x1275,0x1275,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x1281,0x1281, -0x1281,0x1281,0x13a4,0x1386,0x128a,0x13a4,0x13a4,0x13a4,0x13a4,0x13a4,0x13a4,0x13a4,0x13a4,0x13a4,0x13a4,0x1827, -0x1827,0x1827,0x1827,0x1827,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1530, -0x1530,0x1a76,0x1a76,0x1a76,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284, -0x1284,0x1284,0x1284,0x1284,0x138f,0x138f,0x1290,0x138f,0x138f,0x138f,0x1290,0x138f,0x138f,0x138f,0x128a,0x128a, -0x128a,0x128a,0x128a,0x1389,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x128d,0x138c,0x138c,0x138c,0x138c, -0x138c,0x138c,0x138c,0x128d,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x140d,0x140d, -0x19d4,0x1a76,0x1a76,0x1a76,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x128d,0x138c,0x128d, -0x128d,0x138c,0x138c,0x128d,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1, -0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1, -0x12b1,0x12b1,0x12b1,0x12b1,0x133b,0x1338,0x133b,0x1338,0x133b,0x1338,0x133b,0x1338,0x133b,0x1338,0x13fb,0x150f, -0x150f,0x150f,0x17a3,0x1917,0x150f,0x150f,0x16ef,0x16ef,0x16ef,0x16e9,0x16ef,0x16e9,0x191a,0x1917,0x19d1,0x19ce, -0x19d1,0x19ce,0x19d1,0x19ce,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f, -0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f, -0x135f,0x135f,0x135f,0x135f,0x1374,0x1365,0x1374,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377, -0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377, -0x1377,0x1377,0x1377,0x1377,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x137d,0x137d,0x137d,0x137d, -0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d, -0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x1383,0x1383,0x1383,0x1383, -0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383, -0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x13ad,0x13aa,0x18cc,0x18cc, -0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc, -0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x13b6,0x13b6,0x13b6,0x13b6, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, -0x13b6,0x13b3,0x13b3,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b3,0x13b6,0x13b6,0x13b6,0x13b3,0x13b6,0x13b3,0x13b6, -0x13b3,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b9,0x13b6,0x13b6,0x13b6,0x13b6,0x13b3,0x13b6,0x13b3,0x13b3,0x13b6, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b3,0x13b3,0x13b3,0x13b3, -0x13b3,0x13b3,0x13b3,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, -0x13b6,0x13b6,0x13b6,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b6,0x13b6,0x13b6, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3, -0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x1539,0x1539,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, -0x153c,0x153c,0x153c,0x153c,0x153c,0x1779,0x1779,0x1779,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x153c,0x13b6, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x153c,0x1779,0x1779, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b9,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, -0x13b6,0x13b6,0x13b6,0x13b6,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x1539,0x1539,0x153c,0x153c, -0x13b6,0x13b6,0x13b9,0x13b9,0x13b9,0x16a4,0x13b6,0x13b9,0x13b6,0x13b6,0x13b9,0x153f,0x153f,0x153c,0x153c,0x1779, -0x1779,0x1779,0x1779,0x1779,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, -0x13b6,0x153c,0x153c,0x153c,0x16a4,0x153c,0x153c,0x153c,0x1779,0x1779,0x1779,0x177c,0x177c,0x177c,0x177c,0x177c, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x153c, -0x13b6,0x153c,0x13b9,0x13b9,0x13b6,0x13b6,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9, -0x13b9,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, -0x13b6,0x13b6,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9, -0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b6,0x13b6,0x13b6,0x13b9,0x13b6,0x13b6,0x13b6,0x13b6,0x13b9,0x13b9,0x13b9, -0x13b6,0x13b9,0x13b9,0x13b9,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b9,0x13b6,0x13b9,0x13b6,0x13b6, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x16a4,0x13b6,0x13b6,0x13b6,0x13b6,0x153c,0x153c,0x1779, -0x1410,0x1410,0x1410,0x1410,0x1539,0x1539,0x1539,0x1539,0x1539,0x1539,0x153c,0x1779,0x1779,0x1779,0x1779,0x16fe, -0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, -0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153f,0x153f,0x153c,0x153c, -0x153c,0x153c,0x1830,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, -0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x1539,0x1539,0x1539,0x1539,0x1539,0x1539,0x153c,0x13b6, -0x13b6,0x13b6,0x13b6,0x13b6,0x149a,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc, -0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x149a,0x13bc,0x13bc,0x13bc,0x149a,0x13bc,0x149a,0x13bc,0x149a,0x13bc,0x149a, -0x13bc,0x13bc,0x13bc,0x149a,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x149a,0x149a,0x13bc,0x13bc,0x13bc,0x13bc, -0x149a,0x13bc,0x149a,0x149a,0x13bc,0x13bc,0x13bc,0x13bc,0x149a,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc, -0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x16aa,0x16aa,0x177f,0x177f,0x13bf,0x13bf,0x13bf,0x13bc,0x13bc,0x13bc,0x13bf, -0x13bf,0x13bf,0x13bf,0x13bf,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629, -0x1629,0x1629,0x1629,0x1629,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2, -0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2, -0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c5,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2, -0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c5,0x13c5,0x13c5,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2, -0x13c2,0x13c2,0x13c2,0x13c2,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8, -0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8, -0x13c8,0x13c8,0x13c8,0x13c8,0x17ac,0x17ac,0x17a9,0x1701,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1413,0x1413, -0x1413,0x1413,0x1413,0x1413,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416, -0x1416,0x1416,0x1416,0x1545,0x1419,0x1548,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419, -0x1419,0x1548,0x1548,0x1548,0x1548,0x1548,0x1548,0x1704,0x1704,0x1b4b,0x17b2,0x17b2,0x17b2,0x17b2,0x17b2,0x17b2, -0x17b2,0x17b2,0x1a79,0x1a79,0x1422,0x1422,0x1422,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434, -0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434, -0x1434,0x1434,0x1434,0x1434,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f, -0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f, -0x144f,0x144f,0x144f,0x144f,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455, -0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455, -0x1455,0x1455,0x1455,0x19da,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458, -0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458, -0x1458,0x1458,0x1458,0x1458,0x145e,0x145e,0x146a,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470, -0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470, -0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x146a,0x146a,0x146a,0x145e,0x145e,0x145e,0x145e,0x145e,0x145e, -0x145e,0x145e,0x145e,0x146a,0x146d,0x1470,0x1473,0x1473,0x1470,0x1476,0x1476,0x1461,0x1464,0x170a,0x170d,0x170d, -0x170d,0x154e,0x1a82,0x1a7f,0x1467,0x1467,0x1467,0x1467,0x1467,0x1467,0x1467,0x1467,0x1467,0x1467,0x154b,0x1713, -0x1716,0x1710,0x1719,0x1719,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491, -0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491, -0x1491,0x1491,0x1491,0x1491,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb, -0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x18ff,0x18ff, -0x18ff,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x19c8,0x14eb,0x14eb, -0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x1863,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff, -0x18ff,0x18ff,0x18ff,0x18ff,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, -0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x1539,0x1539,0x1539,0x1539,0x1539,0x1539,0x1539,0x1539, -0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, -0x153f,0x153c,0x153c,0x153c,0x153c,0x16a7,0x16a7,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, -0x182d,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, -0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x155d,0x155d,0x155d,0x155d, -0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d, -0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x156f,0x156f,0x156f,0x156f, -0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f, -0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x1575,0x1575,0x1575,0x1575, -0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575, -0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1578,0x1578,0x1578,0x1578, -0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578, -0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x15a2,0x15a2,0x15a2,0x15a2, -0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x159c,0x159c,0x159c,0x1590,0x1590,0x1590,0x159c,0x159c, -0x1590,0x159f,0x1593,0x1590,0x15a5,0x15a5,0x1599,0x15a5,0x15a5,0x1596,0x17b5,0x1bdb,0x15b7,0x15b7,0x15b7,0x15b7, -0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7, -0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15a8,0x15c0,0x15c0,0x15c0,0x15c0, -0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0, -0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15ba,0x15c3,0x15c3,0x15c3,0x15c3,0x15c6,0x15c6,0x15c6,0x15c6, -0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6, -0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15e1,0x15e1,0x15e1,0x15e1, -0x15e1,0x15e1,0x15e1,0x15e1,0x15d8,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1, -0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15ea,0x15ea,0x15ea,0x15ea, -0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea, -0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15fc,0x15fc,0x15fc,0x15fc, -0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15f9,0x15f9,0x15f9,0x15ed, -0x15ed,0x15ed,0x15ed,0x15ed,0x15ed,0x15ed,0x15ed,0x15f9,0x15f9,0x15ed,0x15f9,0x15f0,0x15fc,0x15fc,0x15fc,0x15fc, -0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc, -0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x1620,0x1620,0x1620,0x1620, -0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620, -0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x161d,0x161d,0x161d,0x1629,0x1629,0x1629,0x1629, -0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629, -0x1629,0x1629,0x162f,0x162f,0x162f,0x162c,0x162c,0x162c,0x1629,0x1629,0x1629,0x1629,0x163e,0x163e,0x163e,0x163e, -0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x1632,0x1632,0x1632,0x1632, -0x1632,0x1632,0x1632,0x1644,0x1644,0x1638,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x163e,0x163e,0x163e,0x163e, -0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e, -0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x164a,0x164a,0x164a,0x164a, -0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a, -0x164a,0x164a,0x164a,0x1647,0x1647,0x1647,0x1647,0x1647,0x1647,0x1647,0x1647,0x1647,0x164d,0x164d,0x164d,0x164d, -0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d, -0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x1671,0x1671,0x1671,0x1671, -0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671, -0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x167a,0x167a,0x167a,0x167a, -0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a, -0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x1692,0x1692,0x1692,0x1692, -0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x167d,0x168c,0x168c,0x167d, -0x167d,0x167d,0x167d,0x167d,0x167d,0x168c,0x167d,0x168f,0x168f,0x167d,0x168f,0x167d,0x1692,0x1692,0x1692,0x1692, -0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692, -0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x169b,0x169b,0x169b,0x169b, -0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b, -0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x16a1,0x16a1,0x16a1,0x16a1, -0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1, -0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x18ff,0x18ff,0x18ff,0x18ff, -0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x16e6,0x16e6,0x16e6,0x16e6,0x18ff,0x18ff,0x18ff,0x18ff, -0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x19c8,0x1707,0x1707,0x1707,0x1707, -0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707, -0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1746,0x1746,0x1746,0x1746, -0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746, -0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x174c,0x1749, -0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746, -0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f, -0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f, -0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752, -0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752, -0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764, -0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764, -0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767, -0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767, -0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a, -0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a, -0x176a,0x176a,0x176a,0x176d,0x176d,0x176d,0x176d,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a, -0x176a,0x176a,0x176a,0x176a,0x176a,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176a,0x176d,0x176d, -0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d, -0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d, -0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785, -0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785, -0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x1ae5,0x1a3a,0x1a3a,0x1a3d, -0x1788,0x1788,0x1788,0x1788,0x1788,0x1788,0x1788,0x1788,0x178b,0x1839,0x1839,0x1839,0x1839,0x1839,0x1839,0x18d5, -0x1788,0x1788,0x1788,0x1788,0x1788,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836, -0x1836,0x1836,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3, -0x1788,0x19b3,0x19b3,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1ae2,0x1bb4,0x1a3d,0x1a3d,0x1a3d, -0x18d2,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d2,0x18d2, -0x1a79,0x1a79,0x1a79,0x1a79,0x1a79,0x1a79,0x1a79,0x1a79,0x1b4b,0x1b4e,0x1b48,0x1b48,0x1b48,0x1b48,0x1b48,0x1b48, -0x1b48,0x1b48,0x1b48,0x192c,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af, -0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1839,0x1836,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2, -0x1839,0x18d5,0x18d5,0x1839,0x1839,0x1839,0x1839,0x1839,0x1839,0x1839,0x1836,0x17b8,0x1839,0x1839,0x1839,0x1a3a, -0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x17b8,0x1836,0x1836,0x1836,0x1836,0x1836,0x18d2,0x19b3,0x19b3,0x19b3, -0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x18d2, -0x17cd,0x17cd,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca, -0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca, -0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd, -0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd, -0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b, -0x181b,0x181b,0x181b,0x181b,0x181b,0x1818,0x1818,0x1818,0x1803,0x1803,0x1803,0x1803,0x1803,0x1803,0x1803,0x1803, -0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b, -0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b, -0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f, -0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f, -0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842, -0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842, -0x1842,0x1842,0x1842,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee, -0x1899,0x1899,0x1899,0x1899,0x19ec,0x19ec,0x189c,0x189c,0x189c,0x189c,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884, -0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1896,0x1887,0x188a,0x188d,0x189f,0x189f,0x193e,0x1890,0x1890, -0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899, -0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899, -0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba, -0x18ba,0x18ba,0x18ba,0x18a5,0x18ab,0x18a8,0x18a8,0x18a8,0x18a8,0x18b7,0x18bd,0x18a8,0x18a8,0x18a8,0x18a8,0x18b4, -0x18ba,0x18a8,0x18a8,0x18a8,0x18a8,0x18a8,0x18a8,0x18a8,0x18a8,0x18a8,0x18a8,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba, -0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba, -0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x19ad,0x19ad,0x19ad,0x19ad,0x19ad, -0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ba5,0x1ba5,0x1ba5, -0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc, -0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc, -0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x19b3,0x19b3,0x19b3,0x19b3, -0x19b3,0x1a3a,0x1ae2,0x19b3,0x19b3,0x19b3,0x19b3,0x1ae5,0x1ae2,0x1bb4,0x19b3,0x1a3a,0x19b3,0x19b3,0x19b3,0x19b3, -0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3, -0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3, -0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db, -0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db, -0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de, -0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x1bb7, -0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1, -0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1, -0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953, -0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953, -0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e, -0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e, -0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974, -0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974, -0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f, -0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f, -0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992, -0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992, -0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b, -0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x1998,0x1998,0x1998, -0x19b3,0x19b3,0x19b3,0x1ae2,0x1ae2,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1ae2,0x1ae2,0x1ae2,0x1a3a,0x1a3a, -0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b6,0x19b6,0x19b3,0x19b6,0x19b6,0x1a3a,0x1a3d,0x1a3a,0x1a3a,0x1a3a,0x1a3a, -0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef, -0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef, -0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16, -0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16, -0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f, -0x1a55,0x1a55,0x1a1f,0x1a55,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a25,0x1a25,0x1a25, -0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31, -0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31, -0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4, -0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4, -0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0, -0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0, -0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4, -0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4, -0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7, -0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7, -0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c, -0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c, -0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d, -0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d, -0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f, -0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f, -0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2, -0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2, -0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc, -0x1bfc,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9, -0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b, -0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b, -0,0,0,0 -}; - -static const UTrie2 propsVectorsTrie={ - propsVectorsTrie_index, - propsVectorsTrie_index+5348, - NULL, - 5348, - 27344, - 0xa40, - 0x1564, - 0x0, - 0x0, - 0x110000, - 0x7fb0, - NULL, 0, false, false, 0, NULL -}; - -static const uint32_t propsVectors[7230]={ -0x67,0,0,0x67,0,0xe00000,0x67,0x80000,0x20,0x867,0,0,0xa67,0,0,0xb67, -0,0,0xd67,0,0,0xe67,0,0,0x1067,0,0,0x1167,0,0,0x1267,0, -0,0x1367,0,0,0x1467,0,0,0x1567,0,0,0x1667,0,0,0x1767,0,0, -0x1867,0,0,0x1967,0,0,0x1a67,0,0,0x1b67,0,0,0x1d67,0,0,0x1f67, -0,0,0x2067,0,0,0x2267,0,0,0x2367,0,0,0x2467,0,0,0x2567,0, -0,0x2767,0,0,0x2867,0x80000,0x20,0x2967,0,0,0x2a67,0,0x1600000,0x2b67,0,0, -0x2d67,0,0,0x3167,0x20000000,0,0x3267,0x20000000,0,0x3a67,0,0,0x3b67,0,0,0x3c67, -0,0,0x3e67,0,0,0x4067,0,0,0x4167,0,0,0x4467,0,0,0x4867,0, -0,0x4967,0,0,0x4a67,0,0,0x5067,0,0,0x5167,0,0,0x5467,0,0, -0x5567,0,0,0x5667,0x80000,0x20,0x5767,0,0,0x5867,0,0,0x5967,0,0,0x5b67, -0,0,0x5c67,0,0,0x5d67,0,0,0x6067,0x80000,0x20,0x6267,0,0,0x6367,0, -0,0x6467,0,0,0x6567,0,0,0x6f67,0,0,0x7067,0,0,0x7367,0x20000000,0, -0x7567,0,0,0x7667,0,0,0x7767,0,0,0x7867,0,0,0x7a67,0,0,0x7b67, -0,0,0x7c67,0,0,0x7e67,0,0,0x7f67,0,0,0x8167,0,0,0x8267,0, -0,0x8467,0,0,0x8567,0,0,0x8667,0,0,0x8767,0,0,0x8967,0,0, -0x8b67,0,0,0x8c67,0,0,0x8e67,0x20000000,0,0x8f67,0,0,0x9067,0,0,0x9167, -0,0,0x9267,0,0,0x9367,0,0,0x9567,0,0,0x9667,0,0,0x9767,0, -0,0x9867,0,0,0x9967,0,0,0x9a67,0,0,0x9c67,0,0,0x9f67,0,0, -0xa167,0,0,0xa367,0,0,0xa467,0,0,0xa567,0,0,0xa667,0,0,0xa767, -0,0,0xa867,0,0,0xa967,0,0,0xaa67,0,0xe00000,0xab67,0,0xe00000,0xac67,0, -0,0xad67,0,0,0xae67,0,0,0xaf67,0,0,0xb167,0,0,0xb267,0,0, -0xb467,0,0,0xb567,0,0,0xb767,0,0,0xb867,0,0,0xb967,0,0,0xba67, -0,0,0xbc67,0,0,0xbd67,0,0,0xbe67,0,0,0xbf67,0,0,0xc067,0, -0,0xc167,0,0,0xc367,0,0xe00000,0xc467,0,0xe00000,0xc667,0,0,0xc767,0,0, -0xc867,0,0,0xc967,0,0,0xca67,0,0,0xcc67,0,0xe00000,0xcf67,0,0xe00000,0xd067, -0,0xe00000,0xd367,0,0,0xd467,0,0,0xd567,0,0,0xd667,0,0,0xd867,0, -0,0xda67,0,0,0xdb67,0,0,0xdc67,0,0,0xdd67,0,0,0xde67,0,0, -0xdf67,0,0,0xe067,0,0,0xe167,0,0,0xe267,0,0,0xe367,0,0xe00000,0xe467, -0,0,0xe567,0,0,0xe667,0,0,0xe767,0,0,0xe867,0,0,0xe967,0, -0,0xea67,0,0,0xeb67,0,0,0xec67,0,0,0xed67,0,0,0xee67,0,0, -0xef67,0,0,0xf167,0,0,0xf367,0,0,0xf567,0,0,0xf667,0,0,0xf767, -0,0,0xf867,0,0,0xf967,0,0,0xfa67,0,0xe00000,0xfb67,0,0,0xfc67,0, -0,0xfd67,0,0,0xfe67,0,0,0x10167,0,0,0x10267,0,0,0x10367,0,0, -0x10467,0,0,0x10667,0,0,0x10767,0,0,0x10867,0,0,0x10967,0,0,0x10a67, -0,0,0x10b67,0,0,0x10c67,0,0,0x10d67,0,0,0x10e67,0,0,0x10f67,0, -0,0x11067,0,0,0x11367,0,0,0x11467,0,0,0x11567,0,0,0x11667,0,0, -0x11767,0,0,0x11867,0,0,0x11967,0,0xe00000,0x11a67,0,0,0x11b67,0,0,0x11c67, -0,0,0x11d67,0,0,0x11e67,0,0,0x11f67,0,0,0x12067,0,0,0x12167,0, -0,0x12267,0,0,0x12367,0,0,0x12467,0,0,0x12567,0,0,0x12667,0,0, -0x12767,0,0,0x12867,0,0,0x12967,0,0,0x12a67,0,0xe00000,0x12b67,0,0,0x12c67, -0,0,0x12d67,0,0,0x12f67,0,0,0x13067,0,0,0x13167,0,0,0x13267,0, -0,0x13367,0,0,0x13467,0,0,0x13567,0,0,0x13667,0,0,0x13767,0,0, -0x13867,0,0,0x13967,0,0,0x13a67,0,0,0x13b67,0,0,0x13c67,0,0,0x13d67, -0,0,0x13f67,0,0,0x14067,0,0,0x14167,0,0,0x14367,0,0,0x14467,0, -0,0x14567,0,0,0x14667,0,0,0x14767,0,0,0xa0067,0,0xe00000,0xa4f67,0,0xe00000, -0xa5f67,0,0xe00000,0xac567,0,0xe00000,0xad167,0,0xe00000,0xb0067,0,0xe00000,0xb1267,0,0xe00000,0xb2e67, -0,0xe00000,0x11000100,0,0x900020,0x11000100,0x40000001,0x440020,0x11000100,0x40000001,0x643020,0x11000100,0x40000001,0xa5a040,0x11000100,0x40000001, -0x116a8a0,0x11000200,0,0x900020,0x11000200,0x4000001,0xc4000b,0x11000200,0x7c00100,0x220402,0x11000200,0x24000000,0x200000,0x11000200,0x24000008,0x1710000, -0x11000200,0x40000001,0x1d3b020,0x11000219,0x7c00100,0x220401,0x11000219,0x7c00100,0x250401,0x11000319,0x7c00100,0x220401,0x11000319,0x7c00100,0x220402,0x11000319, -0x7c00100,0x250400,0x11000319,0x7c00100,0x250401,0x11000419,0x7c00100,0x220400,0x11000419,0x7c00100,0x220401,0x11000419,0x7c00100,0x220402,0x11000419,0x7c00100, -0x230400,0x11000419,0x7c00100,0x250400,0x11000419,0x7c00100,0x250401,0x11000419,0x7c00100,0x250402,0x11000519,0x7c00100,0x220400,0x11000519,0x7c00100,0x230400, -0x11000600,0x4000400,0x200002,0x11000600,0x4000400,0x200400,0x11000600,0x7c00500,0x220400,0x11000600,0x7c00500,0x230400,0x11000600,0x7c00500,0x530400,0x11000600, -0x7c00d00,0x230400,0x11000619,0x7c00500,0x22040f,0x11000800,0x4000010,0x1001401,0x11000800,0x4000400,0x200001,0x11000800,0x6800010,0x201001,0x11000800,0x7c00500, -0x230401,0x11000807,0x7c00100,0x220400,0x11000807,0x7c00100,0x250400,0x1100080e,0x4000400,0x200000,0x1100080e,0x4000400,0x200002,0x1100080e,0x7000500,0x220402, -0x1100080e,0x7c00100,0x220400,0x1100080e,0x7c00100,0x220401,0x1100080e,0x7c00100,0x220402,0x1100080e,0x7c00100,0x250400,0x1100080e,0x7c00100,0x250401,0x1100080e, -0x7c00120,0x220402,0x1100080e,0x7c00120,0x250402,0x11000908,0x4000000,0x200000,0x11000908,0x7c00100,0x220400,0x11000908,0x7c00100,0x220401,0x11000908,0x7c00100, -0x250400,0x11000908,0x7c00100,0x250401,0x11000a03,0x4000000,0x200400,0x11000a03,0x4000000,0x201000,0x11000a03,0x4000000,0x270000,0x11000a03,0x7c00100,0x220400, -0x11000a03,0x7c00100,0x220402,0x11000a03,0x7c00100,0x250400,0x11000a03,0x7c00500,0x230400,0x11000a03,0xc000010,0x1049400,0x11000b13,0x2802500,0x962460,0x11000b13, -0x4000000,0x200000,0x11000b13,0x4000000,0x201000,0x11000b13,0x4000000,0x230400,0x11000b13,0x4000002,0x400000,0x11000b13,0x4000010,0x200000,0x11000b13,0x7c00100, -0x2633800,0x11000c00,0x80000000,0x218960,0x11000c02,0x2802100,0x962460,0x11000c02,0x2802400,0x962460,0x11000c02,0x4000000,0x200000,0x11000c02,0x4000000,0x1329400, -0x11000c02,0x4000000,0x1329800,0x11000c02,0x4000000,0x1500000,0x11000c02,0x6800000,0x1329800,0x11000c02,0x7c00100,0x230400,0x11000c02,0x7c00100,0x230401,0x11000c02, -0x7c00100,0x230402,0x11000c02,0x7c00500,0x230400,0x11000c02,0x7d00100,0x230400,0x11000f01,0x2802400,0x962460,0x11000f0a,0x2802100,0x962460,0x11000f0a,0x2802400, -0x962460,0x11000f0a,0x2806400,0x962460,0x11000f0a,0x4000000,0x200000,0x11000f0a,0x6800100,0x962540,0x11000f0a,0x7c00100,0x230400,0x11000f0a,0x7c00100,0x230401, -0x11001004,0x2802100,0x962460,0x11001004,0x2802400,0x962460,0x11001004,0x2806400,0x962460,0x11001004,0x4000000,0x200000,0x11001004,0x4000000,0x1500000,0x11001004, -0x6800100,0x962540,0x11001004,0x6800100,0x962541,0x11001004,0x7c00100,0x230400,0x11001004,0x7c00100,0x230401,0x11001110,0x2802100,0x962460,0x11001110,0x2802400, -0x962460,0x11001110,0x2806400,0x962460,0x11001110,0x6800100,0x962540,0x11001110,0x7c00100,0x230400,0x11001110,0x7c00100,0x230401,0x1100120f,0x2802100,0x962460, -0x1100120f,0x2802400,0x962460,0x1100120f,0x2806400,0x962460,0x1100120f,0x6800100,0x962540,0x1100120f,0x7c00100,0x230400,0x1100131f,0x2802100,0x962460,0x1100131f, -0x2802400,0x962460,0x1100131f,0x2806400,0x962460,0x1100131f,0x4000000,0x200000,0x1100131f,0x6800000,0x1329800,0x1100131f,0x6800100,0x962540,0x1100131f,0x6800100, -0x962541,0x1100131f,0x7c00100,0x230400,0x1100131f,0x7c00100,0x230401,0x11001423,0x2802100,0x962460,0x11001423,0x2806400,0x962460,0x11001423,0x6800100,0x962540, -0x11001423,0x6800100,0x962541,0x11001423,0x7c00100,0x230400,0x11001423,0x7c00100,0x230401,0x11001524,0x2802100,0x962460,0x11001524,0x2802100,0x962461,0x11001524, -0x2806400,0x962460,0x11001524,0x6800000,0x1329800,0x11001524,0x6800100,0x962540,0x11001524,0x7c00100,0x230400,0x11001615,0x2802100,0x962460,0x11001615,0x2806400, -0x962460,0x11001615,0x6800100,0x962540,0x11001615,0x6800100,0x962541,0x11001615,0x7c00100,0x230400,0x1100171a,0x2802100,0x962460,0x1100171a,0x2806400,0x962460, -0x1100171a,0x6800000,0x1329800,0x1100171a,0x6800100,0x962540,0x1100171a,0x6800100,0x962541,0x1100171a,0x7c00100,0x230400,0x11001900,0x4000000,0x1600000,0x11001926, -0x2802100,0x1862460,0x11001926,0x2802400,0x1862460,0x11001926,0x2806100,0x1862460,0x11001926,0x4000000,0x200000,0x11001926,0x4000010,0x400000,0x11001926,0x6800000, -0x1329800,0x11001926,0x7800100,0x1830142,0x11001926,0x7c00100,0x1830000,0x11001926,0x7c00900,0x1830000,0x11001926,0x7e00100,0x1830000,0x11001a18,0x2802100,0x1862460, -0x11001a18,0x2802400,0x1862460,0x11001a18,0x6800000,0x1329800,0x11001a18,0x7800100,0x1830142,0x11001a18,0x7c00100,0x1830000,0x11001a18,0x7c00100,0x1830002,0x11001a18, -0x7c00900,0x1830000,0x11001a18,0x7e00100,0x1830000,0x11001d0c,0x7c00100,0x230400,0x11001d0c,0x7c00100,0x250400,0x11001e12,0x7c00100,0x2230500,0x11001e12,0x7c00100, -0x2330520,0x11001e12,0x7c80100,0x2330520,0x11002619,0x7c00100,0x220401,0x11002619,0x7c00100,0x220402,0x11002619,0x7c00100,0x250401,0x1100270e,0x4000400,0x200001, -0x1100270e,0x4000400,0x200002,0x1100270e,0x4000400,0x500001,0x1100270e,0x7c00100,0x220401,0x1100270e,0x7c00100,0x250401,0x11002800,0x80000,0x918820,0x11002800, -0x80000,0x1c18020,0x11002800,0x180000,0x918820,0x11002800,0x4000001,0x445801,0x11002800,0x4000001,0x445802,0x11002800,0x4000001,0xc4000b,0x11002800,0x6800000, -0x201c00,0x11002800,0x6800020,0x201c00,0x11002800,0x24000000,0x200000,0x11002800,0x24000000,0x200002,0x11002800,0x24000000,0x810000,0x11002800,0x24000000,0x1410000, -0x11002800,0x24000000,0x1500000,0x11002800,0x24000000,0x1500002,0x11002800,0x24000002,0x400000,0x11002800,0x24000006,0xc0000b,0x11002800,0x24000008,0x1410000,0x11002800, -0x24000008,0x1710000,0x11002800,0x24000020,0x1001400,0x11002800,0x24000020,0x1500002,0x11002800,0x2c000010,0x1248000,0x11002800,0x2c000010,0x1248002,0x11002800,0x40000001, -0x63b020,0x11002800,0x40080000,0x918820,0x11002801,0x80000,0x2a65620,0x11002801,0x82000,0x962460,0x11002900,0x4000000,0x20000e,0x11002900,0x4000000,0x20000f, -0x11002900,0x4000020,0x20000e,0x11002900,0x4000020,0x20000f,0x11002900,0x4000020,0x81000e,0x11002900,0x4000020,0x81000f,0x11002900,0x4000020,0x141000e,0x11002900, -0x4000020,0x141000f,0x11002900,0x4000022,0x20000e,0x11002900,0x4000022,0x20000f,0x11002a00,0x4000000,0x1500000,0x11002a00,0x4000000,0x1600000,0x11002a00,0x4000000, -0x1600002,0x11002b01,0x2000,0x962460,0x11002b01,0x2802020,0x962460,0x11002c00,0x4000000,0x200000,0x11002c00,0x4000000,0x200002,0x11002c00,0x4000000,0x20000f, -0x11002c00,0x4000020,0x200000,0x11002c00,0x7c00000,0x200000,0x11002c00,0x7c00020,0x200000,0x11002c00,0x7c00120,0x220405,0x11002c00,0x7c00120,0x230402,0x11002c00, -0x7c00120,0x250402,0x11002c00,0x7c00120,0x250405,0x11002c19,0x7c00100,0x250400,0x11002c19,0x7c00100,0x250401,0x11002d00,0x4000000,0x100006,0x11002d00,0x4000000, -0x200006,0x11002d19,0x7c00100,0x220402,0x11002d19,0x7c00100,0x230400,0x11002d19,0x7c00100,0x250402,0x11002e00,0x24000000,0x200000,0x11002e00,0x24000020,0x200000, -0x11002e00,0x24000020,0x200001,0x11002f00,0x24000020,0x200000,0x11002f00,0x24000020,0x200001,0x11002f00,0x24000020,0x200002,0x11002f00,0x24000020,0xf00000,0x11002f00, -0x24000020,0x1600000,0x11002f00,0x24000022,0x1600000,0x11003000,0x24000000,0x200000,0x11003000,0x24000020,0x200000,0x11003000,0x24000020,0x810000,0x11003000,0x24000020, -0x1410000,0x11003100,0x24000000,0x200000,0x11003200,0x24000000,0x200000,0x11003300,0x4000000,0x100003,0x11003400,0x24000000,0x100000,0x11003400,0x24000000,0x200000, -0x11003500,0x24000000,0x200000,0x11003600,0x24000000,0x200000,0x11003600,0x24000020,0x200000,0x11003700,0x24000000,0x200000,0x11003700,0x24000000,0xe00000,0x11003700, -0x24000000,0x2800000,0x11003700,0x24000020,0x200000,0x11003800,0x4000000,0x100000,0x11003800,0x24000000,0x200000,0x11003800,0x24000000,0xb00000,0x11003800,0x24000000, -0xe00000,0x11003800,0x24000000,0x1710000,0x11003800,0x24000000,0x2800000,0x11005003,0x7c00100,0x220402,0x11005013,0x2802500,0x962460,0x11005013,0x4000020,0x200005, -0x11005013,0x7c00100,0x2633801,0x11005013,0x7c00100,0x2633802,0x11005013,0x7c00100,0x2633805,0x11005019,0x7c00100,0x220402,0x11005102,0x7000100,0x230408,0x11005102, -0x7c00100,0x230404,0x11005102,0x7c00100,0x230407,0x11005102,0x7c00100,0x230408,0x11005102,0x7c00100,0x230409,0x11005201,0x2802400,0x962460,0x11005500,0x80000, -0x1e18820,0x11005502,0x7000100,0x230408,0x11005502,0x7c00100,0x230404,0x11005502,0x7c00100,0x230407,0x11005502,0x7c00100,0x230408,0x11005502,0x7c00100,0x230409, -0x11005667,0x1000,0,0x11020200,0x80004,0x418820,0x11020200,0x4000000,0x100006,0x11020200,0x4000000,0x10000f,0x11020200,0x4000400,0x100002,0x11020200, -0x4000400,0x500002,0x11020200,0x6800c00,0x101000,0x11020200,0x24000000,0x100000,0x11020200,0x24000000,0x200000,0x11020200,0x24000000,0x1400000,0x11020200,0x24000000, -0x1500000,0x11020200,0x24000000,0x1600000,0x11020200,0x24000020,0x100000,0x11020200,0x24000020,0x1600000,0x11020219,0x7c00100,0x12040f,0x11020219,0x7c00100,0x220400, -0x11020219,0x7c00100,0x220401,0x11020219,0x7c00100,0x250400,0x11020319,0x7c00100,0x220400,0x11020319,0x7c00100,0x220401,0x11020319,0x7c00100,0x220402,0x11020319, -0x7c00100,0x250400,0x11020319,0x7c00100,0x250402,0x11020319,0x7d00100,0x220402,0x11020419,0x7c00100,0x220401,0x11020519,0x7c00100,0x220400,0x11020600,0x4000400, -0x100002,0x11020600,0x4000400,0x200400,0x11020600,0x7c00500,0x130400,0x11020600,0x7c00d00,0x130400,0x11020701,0x2802400,0x962460,0x11020701,0x2802400,0x962461, -0x11020701,0x2802400,0xc62460,0x1102080e,0x7c00100,0x220400,0x1102080e,0x7c00100,0x250400,0x11020908,0x7c00100,0x220400,0x11020908,0x7c00100,0x220401,0x11020908, -0x7c00100,0x250400,0x11020908,0x7c00100,0x250401,0x11022800,0x24000000,0x100000,0x11022800,0x24000000,0x200000,0x11022800,0x24000000,0x200002,0x11022800,0x24000000, -0x401000,0x11022800,0x24000000,0xf00002,0x11022800,0x24000000,0xf0ac02,0x11022800,0x24000000,0x1500000,0x11022800,0x24000002,0x100000,0x11022800,0x24000002,0x370000, -0x11022800,0x24000002,0x470000,0x11022800,0x24000006,0x400000,0x11022800,0x24000008,0x1710000,0x11022800,0x24000008,0x1712c00,0x11022800,0x24000020,0x100000,0x11022800, -0x24000020,0x1500000,0x11022800,0x24000020,0x1500002,0x11022900,0x4000000,0x10000e,0x11022900,0x4000000,0x10000f,0x11022919,0x7c00100,0x12040f,0x11022c00,0x4000000, -0x100002,0x11022c00,0x4000000,0x10000f,0x11022c00,0x4000000,0x1500002,0x11022c00,0x4000000,0x1600002,0x11022c00,0x7c00120,0x120405,0x11022c0e,0x7c00100,0x250401, -0x11022c19,0x7c00100,0x150401,0x11022d00,0x4000000,0x100006,0x11022d00,0x4000000,0x200006,0x11022d19,0x7c00100,0x120402,0x11022d19,0x7c00100,0x150402,0x11022e00, -0x24000000,0x200000,0x11022e00,0x24000020,0x100000,0x11022f00,0x24000020,0x100000,0x11022f00,0x24000020,0x100001,0x11022f00,0x24000020,0x100002,0x11023000,0x24000000, -0x100000,0x11023300,0x4000000,0x100002,0x11023300,0x4000000,0x100003,0x11023300,0x4000100,0x120403,0x11023300,0x4000100,0x150403,0x11023400,0x24000000,0x100000, -0x11023500,0x24000000,0x100000,0x11023600,0x24000000,0x100000,0x11023600,0x24000020,0x100000,0x11023700,0x24000000,0x100000,0x11023700,0x24000000,0xe00000,0x11023700, -0x24000020,0x100000,0x11023800,0x4000000,0x100000,0x11023800,0x24000000,0x200000,0x11024e67,0,0,0x11025600,0x4000000,0x100000,0x11042a00,0x4000000, -0x1600000,0x11045700,0x4000000,0x20000a,0x11045700,0x4000020,0x20000a,0x11045712,0x7c00100,0xe3040a,0x11045712,0x7c80100,0xe3040a,0x11045716,0x7c00100,0xe30c0a, -0x11045716,0x7c00100,0x2530c0a,0x11063d00,0x4000001,0x445811,0x11065700,0x4000000,0x810011,0x11065700,0x4000000,0xe00011,0x11065700,0x4000000,0x1410011,0x11065700, -0x4000000,0x1500011,0x11065700,0x4000000,0x1600011,0x11065700,0x4000006,0xe70011,0x11065700,0x4000008,0xe00011,0x11065700,0x4000008,0xe02c11,0x11065700,0x4000010, -0x871411,0x11065700,0x4000010,0x1201411,0x11065700,0x4000010,0x1271011,0x11065700,0x4000020,0xe00011,0x11065700,0x4000400,0xe00011,0x11065700,0x4000420,0xe00011, -0x11065700,0x6800000,0xe01c11,0x11065700,0x6800040,0xe29811,0x11065700,0xc000010,0x80ac11,0x11065700,0xc000010,0xb48011,0x11065719,0x7c00100,0xe20411,0x11065719, -0x7c00100,0xe50411,0x11065719,0x7c00140,0xe20411,0x11065719,0x7c00140,0xe50411,0x11080100,0x6800000,0x201c00,0x11080100,0x68000c0,0x1329800,0x11080100,0x24000000, -0x200000,0x11080100,0x24000000,0x810000,0x11080100,0x24000000,0x1410000,0x11080100,0x24000000,0x1500000,0x11080100,0x24000000,0x1600000,0x11080100,0x24000000,0x1b00000, -0x11080100,0x24000000,0x2410000,0x11080100,0x24000006,0xd70000,0x11080100,0x24000008,0x1713c00,0x11080100,0x24000008,0x1714000,0x11080100,0x24000010,0x1001400,0x11080100, -0x24000010,0x1071000,0x11080100,0x24000010,0x1071400,0x11080100,0x24000020,0x200000,0x11080100,0x24000020,0x400000,0x11080100,0x24000020,0x1600000,0x11080100,0x24000400, -0x200000,0x11080100,0x24000420,0x200000,0x11080100,0x2c000010,0xb48000,0x11080100,0x2c000010,0x100ac00,0x11080100,0x44000001,0x1a45800,0x11080119,0x7c00100,0x220400, -0x11080119,0x7c00100,0x250400,0x11080119,0x7c001c0,0x220400,0x11080119,0x7c001c0,0x250400,0x11080200,0x4000400,0x200002,0x11080200,0x24000000,0x200000,0x11080200, -0x24000000,0x1500000,0x11080200,0x24000000,0x1600000,0x11080200,0x24000020,0x200000,0x110a1e12,0x7c00100,0x2130480,0x110a1e12,0x7c80100,0x2130480,0x110a3000,0x24000000, -0xe00000,0x110a3000,0x24100000,0x810001,0x110a3000,0x24100000,0x1410001,0x110a3700,0x24000000,0x200000,0x110a3d00,0x4000000,0xe00000,0x110a3d00,0x4000000,0xe00002, -0x110a3d00,0x24000000,0xe00000,0x110a3d11,0x7c00300,0xe30000,0x110a3d11,0x7c00900,0x1230400,0x110a3d12,0x2802400,0x962460,0x110a3e14,0x7c00100,0xe30000,0x110a3e14, -0x7c00100,0xe30001,0x110a3e14,0x7c00100,0x2530000,0x110a3e14,0x7c00900,0x1230000,0x110a3e14,0x7c00900,0x1230001,0x110a3f16,0x7c00100,0xe30c00,0x110a3f16,0x7c00100, -0xe30c01,0x110a3f16,0x7c00100,0x2530c00,0x110a3f16,0x7c00900,0x1230c00,0x110a3f16,0x7c00900,0x1230c01,0x110a4005,0x7c00100,0xe30400,0x110a4112,0x7c00100,0xe30402, -0x110a4112,0x7c80100,0xe30402,0x110a4400,0x4000000,0xe00000,0x110a4412,0x4000000,0xe00002,0x110a4412,0x4000000,0xe00003,0x110a4416,0x4000000,0xe00c03,0x110a4500, -0x4000000,0xe0000d,0x110a4516,0x4000000,0xe00c0d,0x110a4711,0x7c40300,0xe30000,0x110a4f11,0x7c00300,0xe30001,0x110a4f11,0x7c40300,0xe30000,0x110a5300,0x4000000, -0x810010,0x110a5300,0x4000000,0xe00002,0x110a5300,0x4000000,0xe00010,0x110a5300,0x4000000,0x1410010,0x110a5300,0x4000002,0xe70010,0x110a5300,0x4000008,0x810010, -0x110a5300,0x4000008,0x1410010,0x110a5300,0x6800000,0xe01c02,0x110a5300,0x6800000,0xe01c10,0x110a5400,0x4000000,0x81000c,0x110a5400,0x4000000,0xe0000c,0x110a5400, -0x4000000,0x141000c,0x110a5400,0x4000000,0x150000c,0x110a5400,0x4000000,0x160000c,0x110a5400,0x4000002,0xe7000c,0x110a5400,0x4000010,0x87140c,0x110a5400,0x4000010, -0xe7000c,0x110a5400,0x4000010,0x120140c,0x110a5400,0x4000010,0x127100c,0x110a5400,0x4000020,0xe0000c,0x110a5400,0x4000026,0xe7000c,0x110a5400,0xc000010,0x80ac0c, -0x110a5400,0xc000010,0xb4800c,0x11400c0c,0x4000010,0xb00000,0x11400c0c,0x4000010,0x1071400,0x11400c17,0xc000010,0xb48000,0x11400c1e,0x7c00900,0x230400,0x11400f4b, -0xc000010,0x448000,0x11400f5f,0xc000010,0x448000,0x11401d94,0x4000000,0x200000,0x11403dca,0x4000000,0xe00000,0x114457bf,0x4000004,0x120000a,0x114457bf,0x4000008, -0x81000a,0x114457bf,0x4000008,0x141000a,0x114457bf,0x4000010,0x87000a,0x114457bf,0xc000010,0x84800a,0x114457c8,0x3802500,0x126246a,0x114457c8,0x7c00d00,0x2530c0a, -0x114a3dbf,0x24000000,0x810000,0x114a3dbf,0x24000000,0x1410000,0x114a3dbf,0x24000008,0x810000,0x114a3dbf,0x24000008,0x1410000,0x114a3dbf,0x24000010,0x870000,0x114a3dbf, -0x2c000010,0x848000,0x114a3dc5,0x4000000,0xe00000,0x114a3dc5,0x24000000,0xe00000,0x114a3dc5,0x24000002,0xe00000,0x114a3dc5,0x24000002,0x1200000,0x114a3dc5,0x24000008, -0x810000,0x114a3dc5,0x24000008,0x1410000,0x114a3dc8,0x7c00900,0x930c00,0x114a3dc8,0x7c00900,0xe30c00,0x114a3dca,0x7c00300,0xe30000,0x114a3ec8,0x7000400,0x1200c02, -0x114a3fbf,0x4000004,0x1200000,0x114a3fc8,0x7c00d00,0x2530c00,0x114a42ca,0x4000000,0xe00000,0x114a42ca,0x4000000,0xe0000f,0x114a44ca,0x4000000,0xe00002,0x114a44ca, -0x4000000,0xe00003,0x114a45ca,0x4000000,0xe00002,0x114a45ca,0x4000000,0xe0000d,0x11505103,0x24000000,0x810000,0x11505103,0x24000000,0x1410000,0x1180090a,0x2802400, -0x962460,0x11800c27,0x2802100,0x962460,0x11800c27,0x2802500,0x962460,0x11800f32,0x2802400,0x962460,0x11800f3f,0x2802400,0x962460,0x11820700,0x2802400,0x962460, -0x11820700,0x2802500,0x962460,0x118a3dcb,0x2802400,0x962460,0x118a3ec8,0x2802400,0x962460,0x11c00904,0x2802400,0x962460,0x11c00908,0x2802400,0x962460,0x11c00c2c, -0x6800000,0x1329800,0x11c00c30,0xc000010,0xb48000,0x11c00f78,0x6800000,0x1329800,0x11c0107d,0x6800000,0x1329800,0x11c01181,0x6800000,0x1329800,0x11c01285,0x6800000, -0x1329800,0x11c01489,0x4000000,0x200000,0x11c01489,0x6800000,0x1329800,0x11c0168d,0x6800000,0x1329800,0x11d05107,0x7c00100,0x230408,0x20000067,0x1000,0, -0x20000b13,0x2802400,0x962460,0x20000b13,0x2802500,0x962460,0x20001b27,0x2802100,0x962460,0x20001b27,0x2802100,0x962461,0x20001b27,0x2802400,0x962460,0x20001b27, -0x2802500,0x962460,0x20001b27,0x2806400,0x962460,0x20001b27,0x2902100,0x962462,0x20001b27,0x4000000,0x200000,0x20001b27,0x4000000,0x400000,0x20001b27,0x4000000, -0x500000,0x20001b27,0x4000000,0x810000,0x20001b27,0x4000000,0xb00000,0x20001b27,0x4000000,0xc0000b,0x20001b27,0x4000000,0x1410000,0x20001b27,0x4000010,0xb00000, -0x20001b27,0x4000010,0xc00000,0x20001b27,0x6800000,0x1329800,0x20001b27,0x6800100,0x462540,0x20001b27,0x6800400,0x962540,0x20001b27,0x7c00100,0x230400,0x20001b27, -0x7c00100,0x230401,0x20002619,0x7c00100,0x220401,0x20002a00,0x4000000,0x1600000,0x20004b67,0,0x1900000,0x20004c67,0,0x1900000,0x20004d67,0, -0x1900000,0x20006d67,0x1000,0,0x20006e67,0x1000,0,0x20026d67,0,0,0x20026e67,0,0,0x200a4a12,0x7c00100,0x1f304c1, -0x200a4a12,0x7c00100,0x20304e1,0x21005600,0x4000000,0x700000,0x21022a00,0x4000000,0x1600000,0x30000419,0x7c00100,0x220400,0x30000419,0x7c00100,0x220401,0x30000419, -0x7c00100,0x250400,0x30000419,0x7c00100,0x250401,0x30000519,0x7c00100,0x220400,0x30000600,0x4000400,0x200400,0x30000600,0x7c00500,0x230400,0x30000605,0x4000400, -0x200400,0x3000080e,0x7c00100,0x220400,0x30000908,0x2000,0x962460,0x30000908,0x7c00100,0x220400,0x30000908,0x7c00100,0x220401,0x30000908,0x7c00100,0x250400, -0x30000908,0x7c00100,0x250401,0x30000a03,0x4000006,0x400400,0x30000c02,0x4000000,0x200000,0x30000c02,0x7c00100,0x230400,0x30000d22,0x2802100,0x962460,0x30000d22, -0x2802400,0x962460,0x30000d22,0x2802500,0x962460,0x30000d22,0x4000000,0x200000,0x30000d22,0x4000010,0x200000,0x30000d22,0x7c00100,0x230400,0x30000d22,0xc000010, -0x248000,0x30000d22,0x80000000,0x218960,0x30000e25,0x2802500,0x962460,0x30000e25,0x7c00100,0x230400,0x30001821,0x2802100,0x962460,0x30001821,0x2806400,0x962460, -0x30001821,0x4000000,0x200000,0x30001821,0x6800100,0x962540,0x30001821,0x6800100,0x962541,0x30001821,0x7c00100,0x230400,0x30001b27,0x2802100,0x962460,0x30001b27, -0x2802400,0x962460,0x30001b27,0x4000000,0x200000,0x30001b27,0x4000000,0x400000,0x30001b27,0x7c00100,0x230400,0x30001c1c,0x2802100,0x1862460,0x30001c1c,0x2802400, -0x1862460,0x30001c1c,0x2806400,0x1862460,0x30001c1c,0x4000000,0x200000,0x30001c1c,0x6800100,0x1862400,0x30001c1c,0x6800100,0x1862540,0x30001c1c,0x7c00100,0x1830000, -0x30001c1c,0x7c00100,0x1830001,0x30001c1c,0xc000010,0x448000,0x30001f0b,0x4000000,0x200000,0x30001f0b,0x4000010,0x200000,0x30001f0b,0x4000010,0x400000,0x30001f0b, -0x6800000,0x200000,0x30001f0b,0x7c00100,0x230400,0x30001f0b,0xc000010,0x248000,0x30002006,0x7c00100,0x250400,0x30002128,0x4000000,0x200000,0x30002128,0x7c00100, -0x230400,0x30002128,0xc000010,0x248000,0x3000221d,0x4000000,0x810000,0x3000221d,0x4000000,0x1410000,0x3000221d,0x4000001,0x445800,0x3000221d,0x7c00100,0x230400, -0x30002300,0x4000010,0x400000,0x30002320,0x7c00100,0x230400,0x30002417,0x2802100,0x1862460,0x30002417,0x2802400,0x1862460,0x30002417,0x2806400,0x1862460,0x30002417, -0x2882000,0x1862460,0x30002417,0x4000000,0x200000,0x30002417,0x4000000,0x400000,0x30002417,0x4000000,0x1600000,0x30002417,0x4000010,0x400000,0x30002417,0x4000010, -0x1200000,0x30002417,0x6800000,0x1329800,0x30002417,0x6800100,0x1862540,0x30002417,0x7c00100,0x1830000,0x30002417,0x7d00100,0x1830000,0x3000251b,0x80000,0xc18820, -0x3000251b,0x2802100,0x962460,0x3000251b,0x3c02100,0x962460,0x3000251b,0x4000000,0x200000,0x3000251b,0x4000006,0x500000,0x3000251b,0x4000010,0x400000,0x3000251b, -0x4000010,0xb70000,0x3000251b,0x4000800,0x200000,0x3000251b,0x6800000,0x1329800,0x3000251b,0x7c00100,0x230400,0x3000251b,0x7c00900,0x230400,0x3000251b,0xc000010, -0xb48000,0x3000251b,0x12882000,0x962460,0x30002800,0x24000000,0x200000,0x30002800,0x2c000010,0x1248002,0x30002a00,0x4000000,0x1600000,0x30002b01,0x2000,0x962460, -0x30002c00,0x4000000,0x200000,0x30002c00,0x7c00100,0x220405,0x30002d19,0x7c00100,0x250400,0x30002e00,0x24000000,0x200000,0x30003000,0x24000000,0x200000,0x30003100, -0x24000000,0x200000,0x30003600,0x24000000,0x200000,0x30003700,0x24000000,0x200000,0x3000392e,0x24000000,0x200000,0x30005013,0x7c00100,0x2633801,0x30005600,0, -0x918820,0x30020600,0x4000400,0x500400,0x30020701,0x2802400,0x962460,0x30020701,0x2802400,0xc62460,0x300a3a11,0x4020000,0xe00000,0x300a3a11,0x4020000,0xe00002, -0x300a3b11,0x4020000,0xe00002,0x300a3c00,0x4008000,0xe00000,0x300a3c00,0x4010000,0xe00000,0x300a3d11,0x7c00300,0xe30002,0x300a4305,0x7c00100,0xe30400,0x300a4611, -0x7c40300,0xe30000,0x300a4829,0x7c00100,0xe30400,0x300a4829,0x7c00900,0x1230400,0x300a4929,0x4000000,0xe00000,0x3040259a,0x4000010,0x400000,0x3040259a,0x4000010, -0xb70000,0x3040259a,0xc000010,0xb48000,0x304028ba,0x4000001,0xc41c0b,0x304a3dca,0x4000000,0xe00000,0x30800c27,0x2802100,0x962460,0x30c01c92,0x6800000,0x1329800, -0x3100080e,0x7c00120,0x220402,0x3100080e,0x7c00120,0x250402,0x31005167,0x1000,0,0x3100581e,0x4000000,0x200000,0x3100581e,0x7c00100,0x230400,0x3100590d, -0x7c00100,0x230400,0x31005a09,0x7c00100,0x220400,0x31005a09,0x7c00100,0x250400,0x31005b00,0x4000000,0x200000,0x31005c00,0x80000,0x918820,0x31005c00,0x2802000, -0x962460,0x31005c00,0x2802400,0x962460,0x31005c00,0x4000000,0x200000,0x31005c00,0x4000000,0x200001,0x31005c00,0x6800000,0x962540,0x31005c00,0x6800400,0x962540, -0x31005c01,0x2802400,0x962460,0x31005d00,0x4000020,0x200005,0x31005d00,0x6800020,0x1329805,0x31005d00,0x7c00120,0x220405,0x31005d00,0x7c00120,0x250405,0x31006000, -0x82000,0x962460,0x31006000,0x180000,0x918820,0x310a5e11,0x7c40300,0xe30000,0x310a5f11,0x7c00300,0xe30001,0x32000419,0x7c00100,0x250400,0x3200080e,0x4000020, -0x200000,0x3200080e,0x7c00100,0x220400,0x3200080e,0x7c00100,0x250400,0x32000908,0x7c00100,0x220400,0x32000908,0x7c00100,0x250400,0x32000c02,0x7c00100,0x230400, -0x32000e25,0x7c00100,0x230400,0x32001d0c,0x7c00100,0x230400,0x32002800,0x80000,0x1e18820,0x32002800,0x80020,0x218820,0x32002800,0x4000001,0x445802,0x32002800, -0x24000000,0x200000,0x32002800,0x24000000,0x1500002,0x32002800,0x24000020,0x200000,0x32002800,0x2c000010,0x1248002,0x32002919,0x7c00100,0x22040f,0x32002a00,0x4000000, -0x1600000,0x32002b01,0x2000,0x962460,0x32002b01,0x2802000,0x962460,0x32002b01,0x2802020,0x962460,0x32002c00,0x4000000,0x200000,0x32002c00,0x4000020,0x200000, -0x32002c00,0x4000020,0x200005,0x32002c00,0x7c00120,0x220405,0x32002c00,0x7c00120,0x250405,0x32002e00,0x24000020,0x200000,0x32002f00,0x24000020,0x200000,0x32003000, -0x24000000,0x200000,0x32003000,0x24000020,0x200000,0x32003500,0x24000000,0x200000,0x32003600,0x24000020,0x200000,0x32003700,0x24000000,0x100000,0x32003700,0x24000000, -0x200000,0x32003800,0x24000000,0x810000,0x32003800,0x24000000,0x1410000,0x32005102,0x4000000,0x1500008,0x32005502,0x7c00100,0x230400,0x32006108,0x7c00100,0x220400, -0x32006108,0x7c00100,0x250400,0x3200622a,0x2802100,0x962460,0x3200622a,0x2806400,0x962460,0x3200622a,0x7c00100,0x230400,0x3200632b,0x2802100,0x962460,0x3200632b, -0x6804000,0x962540,0x3200632b,0x7c00100,0x230400,0x3200642c,0x2802100,0x962460,0x3200642c,0x7c00100,0x230400,0x3200652d,0x2802100,0x962460,0x3200652d,0x7c00100, -0x230400,0x32006600,0x24000020,0x200000,0x32006700,0x24000020,0x200000,0x32006800,0x24000020,0x200000,0x32006900,0x24000020,0x200000,0x32006900,0x24000020,0x810000, -0x32006900,0x24000020,0x1410000,0x32006a00,0x24000020,0x200000,0x32006a00,0x24000020,0x200001,0x32006a00,0x24000020,0x200002,0x32020701,0x2882000,0xc62460,0x32023300, -0x4000000,0x100000,0x32026c01,0x12882000,0x962460,0x32065700,0x4000000,0x810011,0x32065700,0x4000000,0x1410011,0x32086600,0x24000020,0x810000,0x32086600,0x24000020, -0x1410000,0x32086900,0x24000020,0x810000,0x32086900,0x24000020,0x1410000,0x320a3600,0x24000020,0x200000,0x320a3d11,0x7c00100,0x1230400,0x320a3e14,0x7c00100,0xe30010, -0x320a3e14,0x7c00100,0x2530000,0x320a3f16,0x7c00100,0xe30c10,0x320a4400,0x4000000,0xe00003,0x320a4929,0x4000000,0xe00000,0x320a4f11,0x7c00300,0xe30001,0x320a6b16, -0x7c00100,0x2530c00,0x32406396,0xc000010,0x448000,0x324a3dcd,0x4000000,0xe00000,0x324a3dcd,0x7c00100,0x1230400,0x324a3fc8,0x4000002,0x1200c00,0x324a53c5,0x24000000, -0xe00000,0x32820701,0x2802000,0x962460,0x40000419,0x7c00100,0x220400,0x40000519,0x7c00100,0x220400,0x40000600,0x4000400,0x200400,0x4000080e,0x7c00100,0x220400, -0x4000080e,0x7c00100,0x250400,0x4000080e,0x7c00100,0x250402,0x40000c02,0x2802100,0x962460,0x40000c02,0x2802400,0x962460,0x40000c02,0x2802500,0x962460,0x40000c02, -0x4000000,0x200000,0x40000c02,0x4000000,0x1071400,0x40000c02,0x7c00100,0x230400,0x40000c02,0x80000000,0x218960,0x40000d22,0x7c00100,0x230400,0x40000f0a,0x7c00100, -0x230400,0x40001004,0x7c00100,0x230400,0x40001110,0x2802100,0x962460,0x40001110,0x6800100,0x962540,0x4000120f,0x2802100,0x962460,0x4000120f,0x4000000,0x1600000, -0x4000120f,0x7c00100,0x230400,0x4000131f,0x7c00100,0x230400,0x40001423,0x4000000,0x200000,0x40001423,0x4000000,0x1600000,0x40001615,0x2802400,0x962460,0x40001615, -0x7c00100,0x230400,0x40002417,0x2802400,0x1862460,0x40002417,0x4000000,0x200000,0x40002800,0x6800000,0x201c00,0x40002800,0x24000002,0x200000,0x40002c00,0x4000000, -0x200002,0x40003000,0x24000000,0x200000,0x40003000,0x24000020,0x200000,0x40003700,0x24000000,0x200000,0x40005a09,0x7c00100,0x220400,0x40005a09,0x7c00100,0x250400, -0x40005d00,0x7c00120,0x220405,0x40006f30,0x2802100,0x962460,0x40006f30,0x2802400,0x962460,0x40006f30,0x4000000,0x200000,0x40006f30,0x6800000,0x1329800,0x40006f30, -0x6800100,0x962540,0x40006f30,0x7c00100,0x230400,0x40006f30,0xc000010,0xb48000,0x40007034,0x7c00100,0x1830000,0x40007117,0x4000000,0x200000,0x40007208,0x7c00100, -0x220400,0x4000720e,0x7c00100,0x220400,0x4000720e,0x7c00500,0x22040e,0x4000720e,0x7c00500,0x22040f,0x40007219,0x7c00100,0x220400,0x40007219,0x7c00500,0x220400, -0x40007219,0x7c00500,0x22040e,0x40007219,0x7c00500,0x22040f,0x40007300,0x24000000,0x200000,0x40007400,0x4000000,0x200000,0x40007531,0x7c00100,0x230400,0x40007631, -0x7c00100,0x230400,0x40007835,0x4000010,0x400000,0x40007835,0x7c00100,0x230400,0x40007933,0x7c00100,0x230400,0x40007a32,0x6800000,0x1329800,0x40007a32,0x7c00100, -0x230400,0x40007b2f,0x7c00100,0x230400,0x40007c00,0x4000000,0x200000,0x40020701,0x2802400,0x962460,0x40020701,0x2802400,0xc62460,0x40023300,0x4000000,0x200000, -0x40027d01,0x12882000,0x962460,0x400a3700,0x24000000,0x200000,0x400a3700,0x24000000,0xe00000,0x400a4400,0x4000000,0xe0000d,0x400a4412,0x4000000,0xe00002,0x400a4412, -0x4000000,0xe00003,0x400a4500,0x4000000,0xe0000d,0x400a5300,0x4000000,0x810010,0x400a5300,0x4000000,0x1410010,0x40507709,0x4000000,0x200000,0x4050770c,0x4000000, -0x400000,0x4050770f,0x4000000,0x200000,0x4050770f,0x4000000,0x400000,0x40c01489,0x4000000,0x200000,0x40d05107,0x4000000,0x200000,0x41000419,0x7c00100,0x220400, -0x41000419,0x7c00100,0x250400,0x4100080e,0x7c00100,0x220400,0x4100080e,0x7c00100,0x250400,0x41000908,0x7c00100,0x220400,0x41000908,0x7c00100,0x250400,0x41000b13, -0x2802000,0x962460,0x41000b13,0x2802100,0x962460,0x41000b13,0x4000000,0xb00000,0x41000c02,0x2802100,0x962460,0x41000c02,0x4000000,0x1500000,0x41000c02,0xc000010, -0xb48000,0x41000f0a,0x7c00100,0x230400,0x41001004,0x7c00100,0x230400,0x41001423,0x7c00100,0x230400,0x41001b27,0x4000000,0x500000,0x41001d0c,0x7c00100,0x22040f, -0x41001d0c,0x7c00100,0x230400,0x41001f0b,0x2802400,0x962460,0x41001f0b,0x4000000,0x200000,0x41001f0b,0x7c00100,0x230400,0x41002800,0x24000000,0x200000,0x41002800, -0x24000000,0x400000,0x41002919,0x7c00100,0x22040e,0x41002a00,0x4000000,0x1600000,0x41002b01,0x2802020,0x962460,0x41002c00,0x4000000,0x200000,0x41002c00,0x7c00120, -0x220405,0x41003000,0x24000000,0x200000,0x41003700,0x24000000,0x200000,0x41003700,0x24000000,0xe00000,0x41005d00,0x7c00120,0x220405,0x41006600,0x24000020,0x200000, -0x41006600,0x24000020,0x810000,0x41006600,0x24000020,0x1410000,0x41007208,0x7c00100,0x22040f,0x41007219,0x7c00100,0x220400,0x41007300,0x24000000,0x200000,0x41007e0e, -0x2802000,0x962460,0x41007e0e,0x4000000,0x200000,0x41007f0e,0x4000000,0x200000,0x41007f0e,0x7c00100,0x230400,0x41008002,0x7c00100,0x230400,0x41008137,0x2802100, -0x962460,0x41008137,0x4000000,0x200000,0x41008137,0x6800100,0x962540,0x41008137,0x7c00100,0x230400,0x41008301,0x2802000,0x962460,0x41008407,0x4000000,0x200000, -0x41008407,0x4000000,0x400000,0x41008407,0x4000000,0xb00000,0x41008407,0x7c00100,0x220400,0x41008407,0x7c00100,0x250400,0x4100850b,0x7c00100,0x230400,0x4100860b, -0x4000000,0x200000,0x4100860b,0x7c00100,0x230400,0x4100870c,0x7c00100,0x220400,0x41008838,0x7c00100,0x220400,0x41008838,0x7c00100,0x250400,0x41008939,0x2802000, -0x962460,0x41008939,0x2802100,0x962460,0x41008939,0x2806000,0x962460,0x41008939,0x4000000,0x200000,0x41008939,0x4000000,0x400000,0x41008939,0x7c00100,0x230400, -0x41008939,0xc000010,0x448000,0x41008a00,0x4000400,0x200400,0x41008b3b,0x4000000,0x1800000,0x41008b3b,0x6800000,0x1329800,0x41008b3b,0x7c00100,0x1830000,0x41008b3b, -0x7e00100,0x1830000,0x41008c3d,0x4000010,0x400000,0x41008c3d,0x7c00100,0x230400,0x41008d0e,0x7c00100,0x22040f,0x41008d19,0x7c00100,0x220400,0x41008d19,0x7c00100, -0x22040f,0x41008e00,0x24000000,0x200000,0x41008e00,0x24000000,0x400000,0x41008e00,0x24000000,0x1710000,0x41008e00,0x24000006,0x400000,0x41008f3a,0x2802100,0x962460, -0x41008f3a,0x2806000,0x962460,0x41008f3a,0x4000000,0x200000,0x41008f3a,0x6800100,0x962540,0x41008f3a,0x7c00100,0x230400,0x4100903c,0x7c00100,0x230400,0x4100903c, -0x7c00100,0x23040f,0x41020701,0x2802000,0x962460,0x41020701,0x2802000,0xc62460,0x410a3700,0x24000000,0x200000,0x410a3700,0x24000000,0xe00000,0x410a4412,0x4000000, -0xe00003,0x410a4711,0x7c40300,0xe30000,0x410a4f11,0x7c00300,0xe30001,0x410a9100,0x4000000,0x800010,0x410a9100,0x4000000,0x810010,0x410a9100,0x4000000,0x870010, -0x410a9100,0x4000000,0xb00010,0x410a9100,0x4000000,0xf00010,0x410a9100,0x4000000,0x1001410,0x410a9100,0x4000000,0x1071010,0x410a9100,0x4000000,0x1071410,0x410a9100, -0x4000000,0x1410010,0x41408ad0,0x4000400,0x200000,0x414a82ca,0x4000000,0xe00000,0x41808300,0x2802000,0x962460,0x41c01489,0x6800000,0x1329800,0x50000419,0x7c00100, -0x220400,0x50000419,0x7c00100,0x250400,0x5000080e,0x7c00100,0x220400,0x50000908,0x7c00100,0x220400,0x50000908,0x7c00100,0x250400,0x50000b13,0x2802500,0x962460, -0x50000f0a,0x7c00100,0x230400,0x50001615,0x2802100,0x962460,0x50001615,0x7c00100,0x230400,0x50002b01,0x2802020,0x962460,0x50002c00,0x4000000,0x200000,0x50002c19, -0x7c00100,0x220400,0x50002d19,0x7c00100,0x220400,0x50003000,0x24000000,0x200000,0x50003000,0x24000020,0x200000,0x50003700,0x24000000,0x200000,0x50005d00,0x7c00120, -0x220405,0x50005d00,0x7c00120,0x250405,0x50006108,0x7c00100,0x220400,0x50006108,0x7c00100,0x250400,0x50006600,0x24000020,0x200000,0x50007300,0x24000000,0x200000, -0x50008301,0x2802400,0x962460,0x50008a00,0x7c00500,0x230400,0x50009257,0x2802400,0x962460,0x50009257,0x4000000,0x200000,0x50009257,0x4000010,0x1071400,0x50009257, -0x6800000,0x1329800,0x50009257,0x7c00100,0x230400,0x50009257,0x7c00500,0x230400,0x50009257,0x7c00900,0x230400,0x50009257,0xc000010,0xb48000,0x5000933e,0x2802100, -0x962460,0x5000933e,0x2802400,0x962460,0x5000933e,0x4000000,0x200000,0x5000933e,0x4000000,0x400000,0x5000933e,0x4000010,0x400000,0x5000933e,0x6800000,0x1329800, -0x5000933e,0x6800100,0x962540,0x5000933e,0x6800100,0x962541,0x5000933e,0x6804400,0x962540,0x5000933e,0x7c00100,0x230400,0x5000933e,0x7c00100,0x230401,0x5000933e, -0xc000010,0x448000,0x50009419,0x7c00100,0x220400,0x50009419,0x7c00100,0x250400,0x50009500,0x4000400,0x200400,0x5000965a,0x4000000,0x500000,0x5000965a,0x7c00100, -0x230400,0x5000965a,0xc000010,0xb48000,0x5000975b,0x4000000,0x200000,0x5000975b,0x4000010,0x400000,0x5000975b,0x7c00100,0x230400,0x50009865,0x7c00100,0x230400, -0x50009965,0x4000010,0x400000,0x50009965,0x7c00100,0x230400,0x50409aca,0x4000000,0x200000,0x5100080e,0x7c00100,0x220400,0x5100080e,0x7c00100,0x250400,0x51000c02, -0x2802100,0x962460,0x51000c02,0x4000000,0x1500000,0x51000c02,0x4000020,0x200000,0x51000c02,0x7c00100,0x230400,0x51000f0a,0x7c00100,0x230400,0x51000f0a,0x7c00500, -0x230400,0x51001110,0x2802100,0x962460,0x5100131f,0x2802100,0x962460,0x51001423,0x7c00100,0x230400,0x51001524,0x2802100,0x962460,0x51001524,0x4000000,0x200000, -0x51001524,0x7c00100,0x230400,0x5100171a,0x2802100,0x962460,0x5100171a,0x4000000,0x200000,0x5100171a,0x4000000,0x1500000,0x5100171a,0x7c00100,0x230400,0x51001b27, -0x4000000,0x200000,0x51001b27,0x4000000,0x400000,0x51001b27,0x4000000,0x500000,0x51001b27,0x7c00100,0x230400,0x51001c1c,0x2802100,0x1862460,0x51001c1c,0x2802500, -0x1862460,0x51001c1c,0x2806400,0x1862460,0x51001c1c,0x4000000,0x1800000,0x51001c1c,0x6800000,0x1329800,0x51001c1c,0x6800100,0x1862400,0x51001c1c,0x6800100,0x1862540, -0x51001c1c,0x6800500,0x1862400,0x51001c1c,0x7c00100,0x1830000,0x5100251b,0x7c00100,0x230400,0x51002619,0x7c00100,0x220400,0x51002619,0x7c00100,0x250400,0x51002800, -0x80020,0x218820,0x51002c00,0x4000000,0x200000,0x51002d19,0x7c00100,0x230400,0x51003700,0x24000000,0x200000,0x51003700,0x24000000,0xe00000,0x51005201,0x2802400, -0x962460,0x51005c00,0x4000000,0x200000,0x51006108,0x7c00100,0x220400,0x51006108,0x7c00100,0x250400,0x51006600,0x24000020,0x200000,0x51006600,0x24000020,0x810000, -0x51006600,0x24000020,0x1410000,0x51007300,0x24000000,0x200000,0x51007300,0x24000020,0x200000,0x51008002,0x7c00100,0x230400,0x51008301,0x2802000,0x962460,0x51008301, -0x2802400,0x962460,0x51008301,0x2802400,0xc62460,0x51008a00,0x7c00500,0x230400,0x51008e00,0x24000000,0x200000,0x51008e00,0x24000000,0x400000,0x51008e00,0x24000000, -0x810000,0x51008e00,0x24000000,0x1400000,0x51008e00,0x24000000,0x1410000,0x51008e00,0x24000000,0x1710000,0x51008e00,0x24000002,0x200000,0x51008e00,0x24000500,0x230400, -0x51008e00,0x2c000010,0xb48000,0x51009419,0x7c00100,0x220400,0x51009419,0x7c00100,0x22040e,0x51009419,0x7c00100,0x22040f,0x51009419,0x7c00100,0x250400,0x51009500, -0x4000400,0x200400,0x51009500,0x7c00500,0x230400,0x51009519,0x7c00100,0x220400,0x51009519,0x7c00100,0x22040f,0x51009519,0x7c00100,0x230400,0x51009519,0x7c00100, -0x250400,0x51009b71,0x2802100,0x962460,0x51009b71,0x6800000,0x1329800,0x51009b71,0x6800100,0x962540,0x51009b71,0x6804400,0x962540,0x51009b71,0x7c00100,0x230400, -0x51009c52,0x2802100,0x962460,0x51009c52,0x2802400,0x962460,0x51009c52,0x2802d00,0x962460,0x51009c52,0x4000010,0x400000,0x51009c52,0x6800000,0x1329800,0x51009c52, -0x6800100,0x962540,0x51009c52,0x7c00100,0x230400,0x51009c52,0xc000010,0x448000,0x51009d6d,0x6800000,0x1329800,0x51009d6d,0x7c00100,0x230400,0x51009d6d,0x7c00500, -0x230400,0x51009d6d,0x7c00d00,0x230400,0x51009d6d,0xc000010,0x448000,0x51009e08,0x2802100,0x962460,0x51009f63,0x4000010,0x400000,0x51009f63,0x6800000,0x1329800, -0x51009f63,0x7c00100,0x230400,0x51009f63,0x7c00900,0x230400,0x51009f63,0xc000010,0x448000,0x51009f63,0xc000010,0xb48000,0x5100a008,0x2000,0x962460,0x5100a008, -0x2802400,0x962460,0x5100a008,0x4000000,0x200000,0x5100a008,0x7c00100,0x220400,0x5100a008,0x7c00100,0x230400,0x5100a008,0x7c00100,0x250400,0x5100a008,0x7c00500, -0x230400,0x5100a16f,0x2806400,0x962460,0x5100a16f,0x6800000,0x1329800,0x5100a16f,0x6800100,0x962540,0x5100a16f,0x7c00100,0x230400,0x5100a16f,0xc000010,0x448000, -0x5100a24f,0x2802100,0x962460,0x5100a24f,0x2802400,0x962460,0x5100a24f,0x6800000,0x1329800,0x5100a24f,0x7c00100,0x230400,0x5100a24f,0xc000010,0x448000,0x5100a36e, -0x2802100,0x962460,0x5100a36e,0x4000000,0x200000,0x5100a36e,0x6800100,0x962540,0x5100a36e,0x6804400,0x962540,0x5100a36e,0x7c00100,0x230400,0x5100a442,0x2802100, -0x962460,0x5100a442,0x4000000,0x200000,0x5100a442,0x6800000,0x1329800,0x5100a442,0x6800100,0x962540,0x5100a442,0x7c00100,0x230400,0x5100a442,0xc000010,0x448000, -0x5100a500,0x4000000,0x200000,0x5100a600,0x4000000,0x200000,0x5100a601,0x2802000,0x962460,0x5100a76b,0x7c00100,0x230400,0x5100a868,0x7c00100,0x230400,0x5100a96c, -0x4000000,0x200000,0x5100a96c,0x7c00100,0x230400,0x5100aa00,0x4000000,0xe00000,0x5100ab00,0x4000000,0xe00000,0x51086600,0x24000020,0x810000,0x51086600,0x24000020, -0x1410000,0x510a4005,0x7c00100,0xe30400,0x510a4711,0x7c40300,0xe30000,0x510a7300,0x24000000,0x200000,0x510aaa00,0x4000000,0xe00000,0x5140a2fe,0x4000400,0x400000, -0x514a82ca,0x4000000,0xe00000,0x51802bbc,0x2802000,0x962460,0x51c00908,0x2802400,0x962460,0x51c0a008,0x2802400,0x962460,0x52000f0a,0x2802100,0x962460,0x52000f0a, -0x6800100,0x962540,0x52000f0a,0x7c00100,0x230400,0x52001004,0x4000000,0x1600000,0x52001b00,0x4000000,0x200000,0x52001c1c,0x2802100,0x1862460,0x52001c1c,0x6800100, -0x1862400,0x52001c1c,0x6800500,0x1862400,0x52001e12,0x7c00100,0x2230500,0x52001e12,0x7c00100,0x2330520,0x52002128,0x4000002,0x400000,0x52002128,0x7c00100,0x230400, -0x52002a00,0x4000000,0x1500000,0x52002a00,0x4000000,0x1600000,0x52002d00,0x4000000,0x200006,0x52003000,0x24000000,0x200000,0x52006108,0x7c00100,0x220400,0x52006108, -0x7c00100,0x250400,0x52008301,0x2802400,0x962460,0x52008407,0x2802400,0x962460,0x52008407,0x7c00100,0x220400,0x52008407,0x7c00100,0x250400,0x52008b3b,0x6800000, -0x1800000,0x52008b3b,0x7c00100,0x1830000,0x52008e00,0x24000000,0x400000,0x52009419,0x7c00100,0x250400,0x5200975b,0x4000000,0x200000,0x5200ac7e,0x2802000,0x962460, -0x5200ac7e,0x2802100,0x962460,0x5200ac7e,0x2802400,0x962460,0x5200ac7e,0x4000010,0x200000,0x5200ac7e,0x7c00100,0x230400,0x5200ac7e,0xc000010,0x248000,0x5200ad28, -0x7c00100,0x230400,0x5200ae6a,0x2802100,0x1862460,0x5200ae6a,0x2802400,0x962460,0x5200ae6a,0x2802400,0x1862460,0x5200ae6a,0x2806000,0x1862460,0x5200ae6a,0x4000000, -0x1800000,0x5200ae6a,0x6800000,0x1329800,0x5200ae6a,0x6800100,0x1862400,0x5200ae6a,0x6800100,0x1862540,0x5200ae6a,0x7c00100,0x1830000,0x5200ae6a,0x7c00900,0x1830000, -0x5200ae6a,0xc000010,0x1848000,0x5200b083,0x4000010,0x400000,0x5200b083,0x7c00100,0x230400,0x5200b083,0xc000010,0x448000,0x5200b182,0x2802400,0x962460,0x5200b182, -0x4000000,0x200000,0x5200b182,0x4000010,0x400000,0x5200b182,0x7c00100,0x230400,0x5200b182,0xc000010,0x448000,0x5200b30a,0x2802400,0x962460,0x5200b30a,0x4000000, -0x200000,0x5200b30a,0x7c00100,0x230400,0x5200b54e,0x2802100,0x962460,0x5200b54e,0x2802400,0x962460,0x5200b54e,0x4000000,0x200000,0x5200b54e,0x4000010,0x400000, -0x5200b54e,0x6800000,0x1329800,0x5200b54e,0x6800100,0x962540,0x5200b54e,0x6804400,0x962540,0x5200b54e,0x7c00100,0x230400,0x5200b54e,0xc000010,0x448000,0x5200b61c, -0x4000000,0x1800000,0x5200b61c,0x6800500,0x1862400,0x5200b61c,0x7c00100,0x1830000,0x5200b61c,0x7c00900,0x1830000,0x5200b77f,0x2802100,0x1862460,0x5200b77f,0x2802400, -0x1862460,0x5200b77f,0x4000000,0x1800000,0x5200b77f,0x4000010,0x1800000,0x5200b77f,0x7c00100,0x1830000,0x5200b77f,0x7c00500,0x1830000,0x5200b77f,0x7c00900,0x1830000, -0x5200b77f,0x7e00100,0x1830000,0x5200b873,0x2802100,0x962460,0x5200b873,0x2806400,0x962460,0x5200b873,0x6800000,0x1329800,0x5200b873,0x6800100,0x962540,0x5200b873, -0x6800400,0x962540,0x5200b873,0x7c00100,0x230400,0x5200b873,0xc000010,0x448000,0x5200b912,0x7c00100,0x2230500,0x5200b912,0x7c00100,0x2330520,0x5200ba74,0x4000000, -0x200000,0x5200ba74,0x4000010,0x400000,0x5200ba74,0x7c00100,0x230400,0x5200bb85,0x4000000,0x200000,0x5200bb85,0x7c00100,0x230400,0x5200bc75,0x4000000,0x400000, -0x5200bc75,0x4000010,0x400000,0x5200bc75,0x7c00100,0x230400,0x5200bd7d,0x4000000,0x200000,0x5200bd7d,0x7c00100,0x230400,0x5200be7a,0x4000000,0x200000,0x5200be7a, -0x7c00100,0x230400,0x5200bf58,0x7c00100,0x230400,0x5200c002,0x4000000,0x200000,0x5200c178,0x2802100,0x962460,0x5200c178,0x2802400,0x962460,0x5200c178,0x2806400, -0x962460,0x5200c178,0x4000000,0x200000,0x5200c178,0x6800100,0x962540,0x5200c178,0x7c00100,0x230400,0x5200c178,0x7c00100,0x230401,0x5200c178,0xc000010,0x448000, -0x5200c178,0x80000000,0x218960,0x5200c247,0x7c00100,0x230400,0x5200c247,0x7c00100,0x830400,0x5200c247,0x7c00100,0x1430400,0x5200c300,0x4000000,0x200003,0x52022d00, -0x4000000,0x100006,0x52023700,0x24000000,0x100000,0x52023700,0x24000000,0xe00000,0x52023700,0x24000000,0x2800000,0x52024400,0x4000000,0x100000,0x52027300,0x24000000, -0x100000,0x5202c300,0x4000000,0x100000,0x5202c300,0x4000000,0x100002,0x5202c300,0x4000000,0x100003,0x5202c300,0x4000000,0x10000d,0x5202c300,0x4000100,0x150400, -0x5202c300,0x4000100,0x15040d,0x520a1e12,0x7c00100,0x2130480,0x520a3700,0x24000000,0xe00000,0x520a3800,0x24000000,0x100000,0x520a4711,0x7c40300,0xe30000,0x520a4f11, -0x7c00300,0xe30001,0x520a7300,0x24000000,0x100000,0x520ab412,0x7c00100,0x2130480,0x520ac400,0x4000000,0xe00002,0x520ac400,0x4000000,0xe0000d,0x520ac414,0x4000000, -0xe0000d,0x520ac511,0x7c40300,0xe30000,0x5240af9c,0x7c00100,0x230400,0x5240afa1,0x4000400,0x200000,0x5240afa3,0x6800400,0x962540,0x5240afa3,0x7c00100,0x230400, -0x5240afad,0x7c00100,0x230400,0x5240afaf,0x7c00100,0x230400,0x5240b2d2,0x4000000,0x200000,0x5240b2d2,0x4000000,0x1500000,0x5240b2dd,0x4000000,0x200000,0x5240b2eb, -0x4000000,0x200000,0x524a44ca,0x4000000,0xe00003,0x5250b501,0x7c00900,0x230400,0x5280af9c,0x2802400,0x962460,0x5280af9d,0x2802400,0x962460,0x5280afa3,0x2802400, -0x962460,0x5280afa5,0x2802400,0x962460,0x5280afa7,0x2802400,0x962460,0x52c0b3f8,0x2802400,0x962460,0x52c0b3fc,0x7c00100,0x230400,0x60000c02,0x2802100,0x962460, -0x60000c02,0x7c00100,0x230400,0x60000f0a,0x2802100,0x962460,0x60000f0a,0x6800100,0x962540,0x60000f0a,0x7c00100,0x230400,0x6000131f,0x4000000,0x200000,0x6000171a, -0x7c00100,0x230400,0x6000171a,0x7c00100,0x230560,0x60001b27,0x2802100,0x962460,0x60001b27,0x4000000,0xc00000,0x60001b27,0x7c00100,0x230400,0x60001f0b,0x2802400, -0x962460,0x60002919,0x7c00100,0x22040e,0x60002a00,0x4000000,0x1600000,0x60003000,0x24000000,0x200000,0x60003000,0x24000000,0xe00000,0x60003700,0x24000000,0x200000, -0x60003800,0x24000000,0x1710000,0x60005102,0x4000000,0x200000,0x60006108,0x7c00100,0x220400,0x60006108,0x7c00100,0x250400,0x60006600,0x24000020,0x200000,0x60008301, -0x2802400,0xc62460,0x6000903c,0x2806000,0x962460,0x6000903c,0x4000000,0x400000,0x60009519,0x7c00100,0x220400,0x60009519,0x7c00100,0x250400,0x6000a008,0x7c00100, -0x220400,0x6000a008,0x7c00100,0x250400,0x6000c300,0x4000000,0x2703580,0x6000c654,0x2802000,0x962460,0x6000c654,0x4000010,0x200000,0x6000c654,0x7c00100,0x230400, -0x6000c73f,0x2802000,0x962460,0x6000c73f,0x2802100,0x962460,0x6000c73f,0x4000000,0x200000,0x6000c73f,0x6800100,0x962540,0x6000c73f,0x6804000,0x962540,0x6000c73f, -0x7c00100,0x230400,0x6000c80b,0x7c00100,0x230400,0x6000c941,0x2802100,0x962460,0x6000c941,0x2806400,0x962460,0x6000c941,0x4000000,0x200000,0x6000c941,0x4000010, -0x200000,0x6000c941,0x6800000,0x1329800,0x6000c941,0x6800100,0x962540,0x6000c941,0x7c00100,0x230400,0x6000c941,0xc000010,0x448000,0x6000ca82,0x7c00100,0x230400, -0x6000cc00,0x4000000,0xe00000,0x6000d000,0x4000000,0x200000,0x6002c300,0x4000000,0x100000,0x6002c300,0x4000000,0x10000d,0x6002c300,0x4000100,0x150400,0x6002c300, -0x4000100,0x15040d,0x600a3000,0x24000000,0x200000,0x600a3000,0x24000000,0xe00000,0x600a3700,0x24000000,0x200000,0x600a3800,0x24000000,0x200000,0x600a3800,0x24000000, -0x2800000,0x600a4305,0x7c00100,0xe30400,0x600ac300,0x4000000,0x100000,0x600ac400,0x4000000,0xe0000d,0x600acb14,0x7c00100,0xe30000,0x600acb16,0x7c00100,0xe30c00, -0x600acc00,0x4000000,0xe00000,0x600acd00,0x4000000,0x200000,0x600acd00,0x4000000,0xe00000,0x600acd00,0x4000000,0x2800000,0x600ace00,0x4000000,0xe00000,0x600ace00, -0x4000000,0x2800000,0x600acf00,0x4000000,0xe00000,0x600acf00,0x4000000,0x2800000,0x600ad111,0x7c40300,0xe30000,0x604ac4ca,0x4000000,0xe00003,0x61000a03,0x4000000, -0x1600000,0x61000c02,0x80000000,0x218960,0x6100120f,0x4000000,0x200000,0x61001a18,0x7c00100,0x1830000,0x61001d0c,0x7c00100,0x230400,0x61001d0c,0x7c00100,0x250400, -0x61006600,0x24000020,0x200000,0x61008407,0x7c00100,0x220400,0x61008407,0x7c00100,0x250400,0x6100870c,0x7c00100,0x220400,0x61008e00,0x24000000,0x200000,0x61008e00, -0x24000000,0x400000,0x61008e00,0x24000002,0x300000,0x6100903c,0x7c00100,0x230400,0x61009519,0x7c00100,0x220400,0x61009519,0x7c00100,0x250400,0x61009519,0x7c00500, -0x22040f,0x61009b71,0x2802100,0x962460,0x61009b71,0x2806400,0x962460,0x61009b71,0x7c00100,0x230400,0x6100a008,0x2802100,0x962460,0x6100c300,0x4000000,0x20000f, -0x6100cd00,0x4000000,0x200000,0x6100d202,0x2802400,0x962460,0x6100d202,0x2802500,0x962460,0x6100d202,0x7c00100,0x230400,0x6100d302,0x4000020,0x200000,0x6100d302, -0x7c00120,0x230405,0x6100d476,0x2802100,0x962460,0x6100d476,0x2802100,0x962461,0x6100d476,0x2806400,0x962460,0x6100d476,0x4000000,0x400000,0x6100d476,0x6800000, -0x1329800,0x6100d476,0x6800100,0x962540,0x6100d476,0x7c00100,0x230400,0x6100d476,0xc000010,0x448000,0x6100d573,0x2802100,0x962460,0x6100d573,0x2806400,0x962460, -0x6100d573,0x6800100,0x962540,0x6100d573,0x7c00100,0x230400,0x6100d573,0x7c00900,0x230400,0x6100d573,0xc000010,0x448000,0x6100d68d,0x7c00100,0x230400,0x6100d756, -0x7c00100,0x230400,0x6100d85c,0x2802500,0x962460,0x6100d85c,0x6800100,0x962540,0x6100d85c,0x7c00100,0x230400,0x6100d85c,0x7c00500,0x230400,0x6100d997,0x2802100, -0x962460,0x6100d997,0x4000000,0x200000,0x6100d997,0x4000000,0x400000,0x6100d997,0x6800000,0x1329800,0x6100d997,0x6800100,0x962540,0x6100d997,0x6804400,0x962540, -0x6100d997,0x7c00100,0x230400,0x6100d997,0x7c00100,0x230560,0x6100d997,0xc000010,0x448000,0x6100da98,0x6800000,0x1329800,0x6100da98,0x7c00100,0x230400,0x6100db71, -0x4000000,0x200000,0x6100dc99,0x2802100,0x962460,0x6100dc99,0x2802400,0x962460,0x6100dc99,0x6800000,0x1329800,0x6100dc99,0x6800100,0x962540,0x6100dc99,0x6804400, -0x962540,0x6100dc99,0x7c00100,0x230400,0x610a4711,0x7c40300,0xe30000,0x610a4f11,0x7c00300,0xe30001,0x610ace00,0x4000000,0xe00000,0x6140afa1,0x7c00100,0x230400, -0x6140afa3,0x7c00100,0x230400,0x6180af9e,0x2802400,0x962460,0x62002a00,0x4000000,0x1600000,0x63002800,0x80000,0x918820,0x63c00c15,0x80000,0x918820,0x7000080e, -0x7c00100,0x250400,0x70000a03,0x4000000,0x200000,0x70000c00,0x80000000,0x218960,0x70000f0a,0x7c00100,0x230400,0x70001004,0x7c00100,0x230400,0x70001524,0x2802100, -0x962460,0x70001524,0x7c00100,0x230400,0x70001615,0x2802100,0x962460,0x7000171a,0x2802100,0x962460,0x70001821,0x6800000,0x1329800,0x70002320,0x7c00100,0x230400, -0x70002a00,0x4000000,0x1500000,0x70002a00,0x4000000,0x1600000,0x70003000,0x24000000,0x200000,0x70003800,0x24000000,0xe00000,0x70005201,0x2802400,0x962460,0x7000581e, -0x7c00100,0x230400,0x70006108,0x7c00100,0x220400,0x70006108,0x7c00100,0x250400,0x70006f30,0x7c00100,0x230400,0x70007300,0x24000000,0x200000,0x70007f0e,0x4000000, -0x200000,0x70008301,0x2802100,0x962460,0x70008301,0x2802400,0x962460,0x70008e00,0x24000000,0x200000,0x70008e00,0x24000000,0x400000,0x70008e00,0x24000002,0x400000, -0x70008e00,0x24000008,0x1410000,0x70008e00,0x24000010,0x400000,0x70008e00,0x2c000010,0x448000,0x70009519,0x7c00100,0x220400,0x70009519,0x7c00100,0x230400,0x70009519, -0x7c00100,0x250400,0x70009865,0x7c00100,0x230400,0x70009965,0x4000010,0x400000,0x70009965,0x7c00100,0x230400,0x7000a008,0x7c00100,0x220400,0x7000a008,0x7c00100, -0x250400,0x7000a008,0x7c00500,0x22040f,0x7000a50e,0x4000000,0x200000,0x7000b61c,0x2802500,0x1862460,0x7000b61c,0x6800500,0x1862400,0x7000b61c,0x7c00100,0x1830000, -0x7000c300,0x4000000,0x100000,0x7000c941,0x2806000,0x962460,0x7000cc00,0x4000000,0xe00000,0x7000cd00,0x4000000,0x200000,0x7000cd00,0x4000000,0xe00000,0x7000cd00, -0x4000000,0x2800000,0x7000cf00,0x4000000,0xe00000,0x7000d202,0x2802100,0x962460,0x7000d202,0x7c00100,0x230400,0x7000d997,0x7c00100,0x230400,0x7000d997,0xc000010, -0x248000,0x7000dd86,0x2802400,0x962460,0x7000dd86,0x7c00100,0x230400,0x7000dd86,0xc000010,0x448000,0x7000de9f,0x4000000,0x200000,0x7000de9f,0x7c00100,0x230400, -0x7000e001,0x2400,0x962460,0x7000e001,0x2802400,0x962460,0x7000e187,0x2802000,0x962460,0x7000e187,0x2802100,0x962460,0x7000e187,0x4000000,0x200000,0x7000e187, -0x7c00100,0x230400,0x7000e187,0xc000010,0x448000,0x7000e288,0x7c00100,0x230400,0x7000e300,0x4000000,0x200000,0x7000e489,0x2802100,0x962460,0x7000e489,0x2802400, -0x962460,0x7000e489,0x6800100,0x962540,0x7000e489,0x6800100,0x962541,0x7000e489,0x6804400,0x962540,0x7000e489,0x7c00100,0x230400,0x7000e489,0x7c00900,0x230400, -0x7000e59d,0x2802100,0x962460,0x7000e59d,0x2802400,0x962460,0x7000e59d,0x4000000,0x200000,0x7000e59d,0x4000010,0x200000,0x7000e59d,0x6800100,0x962540,0x7000e59d, -0x6804400,0x962540,0x7000e59d,0x7c00100,0x230400,0x7000e59d,0xc000010,0x448000,0x7000e691,0x2802100,0x962460,0x7000e691,0x2802400,0x962460,0x7000e691,0x2806400, -0x962460,0x7000e691,0x6800000,0x1329800,0x7000e691,0x6800100,0x962540,0x7000e691,0x7c00100,0x230400,0x7000e700,0x4000400,0x200400,0x7000e70e,0x7c00100,0x220400, -0x7000e719,0x7c00100,0x220400,0x7000e719,0x7c00500,0x22040f,0x7000e853,0x7c00100,0x230400,0x7000e9a0,0x2802400,0x962460,0x7000e9a0,0x4000000,0x200000,0x7000e9a0, -0x4000000,0x500000,0x7000e9a0,0x7c00100,0x230400,0x7000ea79,0x2802400,0x962460,0x7000ea79,0x4000000,0x200000,0x7000ea79,0x4000000,0xf00000,0x7000ea79,0x4000010, -0x400000,0x7000ea79,0x7c00100,0x230400,0x7000eb8c,0x2802400,0x962460,0x7000eb8c,0x4000000,0x200000,0x7000eb8c,0x7c00100,0x230400,0x7000eca3,0x2802100,0x962460, -0x7000eca3,0x2806400,0x962460,0x7000eca3,0x4000000,0x200000,0x7000eca3,0x6800000,0x1329800,0x7000eca3,0x6800100,0x962540,0x7000eca3,0x7c00100,0x230400,0x7000eca3, -0xc000010,0x448000,0x7000ed95,0x6800000,0x1329800,0x7000ed95,0x7c00100,0x230400,0x7000ed95,0xc000010,0x448000,0x7000ee1c,0x2802500,0x1862460,0x7000ee1c,0x6800000, -0x1329800,0x7000ee1c,0x7c00100,0x1830000,0x7000ee1c,0x7c00900,0x1830000,0x7000ef8f,0x4000000,0x200000,0x7000ef8f,0x7c00100,0x230400,0x7000f08e,0x4000000,0x200000, -0x7000f08e,0x7c00100,0x230400,0x7000f159,0x2802100,0x962460,0x7000f159,0x7c00100,0x230400,0x7000f200,0x4000000,0x200000,0x7000f200,0x4000000,0x1200000,0x7000f200, -0x4000000,0x1710000,0x7000f34b,0x2802400,0x962460,0x7000f34b,0x4000000,0x200000,0x7000f34b,0x4000010,0x400000,0x7000f34b,0x6800000,0x1329800,0x7000f34b,0x7c00100, -0x230400,0x7000f34b,0x7c00900,0x230400,0x7000f34b,0xc000010,0x448000,0x7000f490,0x4000000,0x200000,0x7000f490,0x7c00100,0x230400,0x7000f5a5,0x7c00100,0x230400, -0x7000f67b,0x4000000,0x200000,0x7000f67b,0x4000010,0x200000,0x7000f67b,0x7c00100,0x230400,0x7000f8a6,0x2802100,0x962460,0x7000f8a6,0x2802400,0x962460,0x7000f8a6, -0x2806400,0x962460,0x7000f8a6,0x4000000,0x500000,0x7000f8a6,0x4000010,0xb00000,0x7000f8a6,0x4000800,0x200000,0x7000f8a6,0x6800100,0x962540,0x7000f8a6,0x6800100, -0x962541,0x7000f8a6,0x7c00100,0x230400,0x7000f8a6,0xc000010,0x448000,0x7000f921,0x4000000,0x200000,0x7000fa00,0x4000000,0x200000,0x7000fb9e,0x2802100,0x962460, -0x7000fb9e,0x2802400,0x962460,0x7000fb9e,0x2806400,0x962460,0x7000fb9e,0x4000000,0x200000,0x7000fb9e,0x6800000,0x1329800,0x7000fb9e,0x6800100,0x962540,0x7000fb9e, -0x6800100,0x962541,0x7000fb9e,0x7c00100,0x230400,0x7000fc92,0x4000000,0x200000,0x7000fc92,0x6800000,0x1329800,0x7000fc92,0x7c00100,0x220400,0x7000fc92,0x7c00100, -0x230400,0x7000fc92,0x7c00100,0x250400,0x700acd00,0x4000000,0xe00000,0x700acd00,0x4000000,0x2800000,0x700ace00,0x4000000,0xe00000,0x700acf00,0x4000000,0xe00000, -0x700acf00,0x4000000,0x2800000,0x7050df11,0x4000000,0x200000,0x7050f719,0x80000,0x918820,0x7080afa1,0x2802400,0x962460,0x7090df11,0x2802400,0x962460,0x70d0e417, -0x2802100,0x962460,0x70d0e417,0x2802400,0x962460,0x70d0e417,0x6800100,0x962540,0x70d0ea15,0x4000010,0x400000,0x8000120f,0x7c00100,0x230400,0x80001524,0x7c00100, -0x230400,0x8000171a,0x7c00100,0x230400,0x80002006,0x7c00100,0x220400,0x80002006,0x7c00100,0x250400,0x80002a00,0x4000000,0x1500000,0x80002d00,0x4000000,0x200000, -0x80005208,0x2802400,0x962460,0x80005c00,0x4000000,0x200000,0x80007300,0x24000000,0x200000,0x80009519,0x7c00100,0x220400,0x80009519,0x7c00100,0x230400,0x80009519, -0x7c00100,0x250400,0x80009865,0x7c00100,0x230400,0x8000a008,0x2802100,0x962460,0x8000b30a,0x4000000,0x500000,0x8000b30a,0x7c00100,0x230400,0x8000cd00,0x4000000, -0xe00000,0x8000d202,0x2802500,0x962460,0x8000d202,0x7c00100,0x230400,0x8000d68d,0x4000000,0x200000,0x8000d997,0x2802000,0x962460,0x8000d997,0x2802400,0x962460, -0x8000d997,0x4000000,0x400000,0x8000d997,0x4000000,0x500000,0x8000d997,0x7c00100,0x230400,0x8000d997,0xc000010,0x448000,0x8000e489,0x2802100,0x962460,0x8000e489, -0x7c00100,0x230400,0x8000e719,0x7c00100,0x220400,0x8000f8a6,0x2802100,0x962460,0x8000f8a6,0x7c00100,0x230400,0x8000f8a6,0xc000010,0x448000,0x8000fda1,0x2802100, -0x1862460,0x8000fda1,0x2806400,0x1862460,0x8000fda1,0x4000000,0x1800000,0x8000fda1,0x6800000,0x1329800,0x8000fda1,0x6800100,0x1862400,0x8000fda1,0x6800100,0x1862540, -0x8000fda1,0x7c00100,0x1830000,0x8000fda1,0xc000010,0x448000,0x8000fe9c,0x7c00100,0x230400,0x8000fe9c,0x7c00100,0x830400,0x8000fe9c,0x7c00100,0x1430400,0x8000ff06, -0x7c00100,0x220400,0x80010165,0x7c00100,0x230400,0x800102a2,0x4000000,0x200000,0x800102a2,0x7c00100,0x230400,0x800103a4,0x7c00100,0x230400,0x800103a4,0xc000010, -0x448000,0x8001044c,0x4000000,0x200000,0x8001044c,0x7c00100,0x220400,0x8001044c,0x7c00100,0x250400,0x80010670,0x2802000,0x962460,0x80010670,0x4000000,0x200000, -0x80010670,0x4000010,0x400000,0x80010670,0xc000010,0x448000,0x800a4711,0x7c40300,0xe30000,0x800acd00,0x4000000,0xe00000,0x800acd00,0x4000000,0x2902460,0x800ace00, -0x4000000,0xe00000,0x800acf00,0x4000000,0xe00000,0x800b0011,0x7c40300,0xe30000,0x800b0500,0x4000000,0xe00000,0x800b0500,0x4000000,0x2800000,0x90001615,0x7c00100, -0x230400,0x9000171a,0x4000000,0x200000,0x9000171a,0x7c00100,0x230400,0x90003000,0x24000000,0x200000,0x90007f0e,0x4000000,0x200000,0x90008301,0x2802400,0x962460, -0x90008e00,0x24000000,0x400000,0x90009519,0x7c00100,0x250400,0x9000a16f,0x2802100,0x962460,0x9000d200,0x80000000,0x218960,0x9000d202,0x2802000,0x962460,0x9000d202, -0x2802100,0x962460,0x9000d202,0x7c00100,0x230400,0x9000e59d,0x2802100,0x962460,0x90010500,0x4000000,0xe00000,0x900107a7,0x2802100,0x962460,0x900107a7,0x2802400, -0x962460,0x900107a7,0x2802c00,0x962460,0x900107a7,0x4000000,0x1400000,0x900107a7,0x6800000,0x1329800,0x900107a7,0x7c00100,0x220400,0x900107a7,0x7c00100,0x250400, -0x900108a8,0x2802100,0x962460,0x900108a8,0x2806400,0x962460,0x900108a8,0x4000000,0x200000,0x900108a8,0x4000000,0x400000,0x900108a8,0x4000010,0x400000,0x900108a8, -0x6800000,0x1329800,0x900108a8,0x6800100,0x962540,0x900108a8,0x7c00100,0x230400,0x900108a8,0xc000010,0x448000,0x90010908,0x7c00100,0x220400,0x90010a38,0x2802100, -0x962460,0x90010ca9,0x2802100,0x962460,0x90010ca9,0x4000000,0x500000,0x90010ca9,0x4000010,0xb00000,0x90010ca9,0x6800100,0x962540,0x90010ca9,0x7c00100,0x230400, -0x90010d1b,0x4000000,0x500000,0x90010eaa,0x2802100,0x962460,0x90010eaa,0x2802400,0x962460,0x90010eaa,0x2806400,0x962460,0x90010eaa,0x4000000,0x200000,0x90010eaa, -0x4000000,0x400000,0x90010eaa,0x4000010,0x400000,0x90010eaa,0x6800000,0x1329800,0x90010eaa,0x6800100,0x962540,0x90010eaa,0x7c00100,0x230400,0x90010eaa,0xc000010, -0x448000,0x90010fab,0x7c00100,0x220400,0x90010fab,0x7c00100,0x250400,0x9002c300,0x4000000,0x100000,0x900ac400,0x4000000,0xe0000d,0x900acd00,0x4000000,0xe00000, -0x900acd00,0x4000000,0x2800000,0x900acf00,0x4000000,0xe00000,0x900b0500,0x4000000,0xe00000,0x900b0500,0x4000000,0x2800000,0x900b0b9a,0x7c00900,0x1230400,0x900b109a, -0x7c00300,0xe30000,0x900b119a,0x7c00300,0xe30000,0x90408e06,0x24000000,0x400000,0xa0001004,0x4000000,0x200000,0xa0001004,0x7c00100,0x230400,0xa000120f,0x2802100, -0x962460,0xa000120f,0x2802400,0x962460,0xa000171a,0x2802100,0x962460,0xa000171a,0x2806400,0x962460,0xa0002a00,0x4000000,0x1600000,0xa0003000,0x24000000,0x200000, -0xa000581e,0x7c00100,0x230400,0xa0007300,0x24000000,0x200000,0xa0008301,0x2802400,0x962460,0xa0008e00,0x24000000,0x400000,0xa000cf00,0x4000000,0xe00000,0xa0010500, -0x4000000,0x200000,0xa00114af,0x2802100,0x962460,0xa00114af,0x2802400,0x962460,0xa00114af,0x2806400,0x962460,0xa00114af,0x6800000,0x1329800,0xa00114af,0x7c00100, -0x230400,0xa00114af,0x7c00100,0x230560,0xa00116b0,0x2802100,0x962460,0xa00116b0,0x2802800,0x962460,0xa00116b0,0x2806400,0x962460,0xa00116b0,0x4000000,0x400000, -0xa00116b0,0x4000000,0x500000,0xa00116b0,0x4000010,0x400000,0xa00116b0,0x6800100,0x962540,0xa00116b0,0x7c00100,0x230400,0xa00116b0,0x7c00100,0x230560,0xa00116b0, -0xc000010,0x448000,0xa0011722,0x7c00100,0x230400,0xa00118b1,0x2802000,0x962460,0xa00118b1,0x2802100,0x962460,0xa00118b1,0x2806400,0x962460,0xa00118b1,0x4000000, -0x200000,0xa00118b1,0x4000000,0x400000,0xa00118b1,0x4000000,0x500000,0xa00118b1,0x6800100,0x962540,0xa00118b1,0x7c00100,0x230400,0xa00118b1,0x7c00100,0x230560, -0xa00118b1,0xc000010,0x448000,0xa00a4005,0x7c00100,0xe30400,0xa00a4711,0x7c40300,0xe30000,0xa00ac400,0x4000000,0xe00000,0xa00acb14,0x7c00100,0xe30000,0xa00acf00, -0x4000000,0xe00000,0xa00b0500,0x4000000,0xe00000,0xa00b0500,0x4000000,0x2800000,0xa00b0b96,0x7c00900,0x1230400,0xa00b1211,0x7c40300,0xe30000,0xa00b1314,0x7c00100, -0xe30000,0xa00b1596,0x7c00300,0xe30000,0xa040afb7,0x6800400,0x962540,0xa08083b8,0x2802400,0x962460,0xb0000a03,0x7c00100,0x220400,0xb0000b13,0x7c00100,0x2633800, -0xb0001004,0x2802000,0x962460,0xb0001110,0x4000000,0x200000,0xb0001524,0x2802100,0x962460,0xb0001615,0x4000000,0x500000,0xb000251b,0x7c00100,0x230400,0xb0007300, -0x24000000,0x200000,0xb0008939,0x4000000,0x200000,0xb0008939,0x7c00100,0x230400,0xb0008e00,0x24000000,0x200000,0xb0008e00,0x24000000,0x400000,0xb0008e00,0x24000010, -0x400000,0xb0009257,0x2802000,0x962460,0xb0009257,0x4000000,0x1600000,0xb0009519,0x7c00100,0x220400,0xb0009519,0x7c00100,0x250400,0xb0009a00,0x4000000,0x200000, -0xb000b30a,0x2802100,0x962460,0xb000b30a,0x7c00100,0x230400,0xb000c178,0x80000000,0x218960,0xb000c300,0x4000000,0x200000,0xb000d202,0x2802000,0x962460,0xb000d476, -0x6800100,0x962540,0xb000d476,0x7c00100,0x230400,0xb000e300,0x4000000,0xe00000,0xb000fda1,0x7c00100,0x1830000,0xb0010eaa,0x2802000,0x962460,0xb00116b0,0x7c00100, -0x230400,0xb0011900,0x4000000,0xe00000,0xb0011ab2,0x2802100,0x962460,0xb0011ab2,0x2802400,0x962460,0xb0011ab2,0x2806400,0x962460,0xb0011ab2,0x4000000,0x200000, -0xb0011ab2,0x6800100,0x962540,0xb0011ab2,0x7c00100,0x230400,0xb0011b0c,0x7c00100,0x230400,0xb0011cb3,0x2802100,0x962460,0xb0011cb3,0x2806400,0x962460,0xb0011cb3, -0x6800000,0x1329800,0xb0011cb3,0x6800100,0x962540,0xb0011cb3,0x7c00100,0x230400,0xb0011db6,0x2802500,0x962460,0xb0011db6,0x6800000,0x1329800,0xb0011db6,0x7c00100, -0x230400,0xb0011db6,0x7c00500,0x230400,0xb0011e00,0x4000000,0x200000,0xb0011e00,0x4000000,0x1500000,0xb0011fb4,0x2802100,0x962460,0xb0011fb4,0x6800100,0x962540, -0xb0011fb4,0x7c00100,0x230400,0xb0011fb4,0xc000010,0x248000,0xb0012000,0x4000000,0x200000,0xb00121b5,0x4000000,0x200000,0xb00121b5,0x4000010,0x400000,0xb00121b5, -0x7c00100,0x220400,0xb00121b5,0x7c00100,0x250400,0xb00121b5,0xc000010,0x448000,0xb00122b8,0x4000000,0x200000,0xb00122b8,0x7c00100,0x230400,0xb00123b7,0x2802400, -0x962460,0xb00123b7,0x4000000,0x200000,0xb00123b7,0x7c00100,0x230400,0xb00123b7,0xc000010,0x248000,0xb00a4005,0x7c00100,0xe30400,0xb00a4711,0x7c40300,0xe30000, -0xb00acf00,0x4000000,0xe00000,0xb00b0500,0x4000000,0xe00000,0xb00b0500,0x4000000,0x2800000,0xb00b109a,0x7c00300,0xe30000,0xb080e487,0x2802000,0x962460,0xc0001524, -0x4000000,0x500000,0xc0001a18,0x2806400,0x1862460,0xc0001a18,0x7c00100,0x1830000,0xc0007300,0x24000000,0x200000,0xc0008e00,0x24000010,0x400000,0xc0009519,0x7c00100, -0x220400,0xc0009519,0x7c00100,0x250400,0xc000c300,0x4000000,0x20000f,0xc000d85c,0x2802100,0x962460,0xc000d85c,0x6800100,0x962540,0xc000d85c,0x7c00100,0x230400, -0xc000dc99,0x7c00100,0x230400,0xc000e719,0x7c00100,0x220400,0xc00107a7,0x7c00100,0x230400,0xc0010eaa,0x7c00100,0x230400,0xc00116b0,0x7c00100,0x230560,0xc0011900, -0x4000000,0x200000,0xc0012447,0,0x818820,0xc0012447,0,0xc18820,0xc0012447,0,0x1418820,0xc00125b9,0x7c00100,0x230400,0xc00126bb,0x2802100, -0x962460,0xc00126bb,0x2806400,0x962460,0xc00126bb,0x4000000,0x500000,0xc00126bb,0x6800100,0x962540,0xc00126bb,0x7c00100,0x230400,0xc00127ba,0x2802400,0x962460, -0xc00127ba,0x4000000,0x200000,0xc00127ba,0x6800000,0x1329800,0xc00127ba,0x7c00100,0x230400,0xc00127ba,0x7c00900,0x230400,0xc0012800,0x4000000,0x200000,0xc0012b23, -0x4000000,0x200000,0xc0012b23,0x4000000,0x400000,0xc0012b23,0x4000000,0x1500000,0xc0012cbc,0x2802400,0x962460,0xc0012cbc,0x4000000,0x1600000,0xc0012cbc,0x6800000, -0x1329800,0xc0012cbc,0x7c00100,0x230400,0xc00acf00,0x4000000,0xe00000,0xc00ae300,0x4000000,0xe00000,0xc00b0500,0x4000000,0xe00000,0xc00b0500,0x4000000,0x2800000, -0xc00b0b11,0x4000000,0x1200000,0xc00b0b11,0x7c00900,0x1230400,0xc00b109a,0x7c00300,0xe30000,0xc00b2914,0x7c00100,0x2530000,0xc00b2916,0x7c00100,0x2530c00,0xc00b2a00, -0x4000000,0xe00000,0xc040af5e,0x7c00100,0x230400,0xc0c12b89,0x4000000,0x200000,0xc14a44ca,0x4000000,0xe0000d,0xd000131f,0x2802c00,0x962460,0xd000171a,0x7c00100, -0x230400,0xd0001821,0x2802100,0x962460,0xd0007300,0x24000000,0x200000,0xd0008e00,0x24000000,0x200000,0xd0008f3a,0x2806000,0x962460,0xd0009519,0x7c00100,0x220400, -0xd0009519,0x7c00100,0x250400,0xd000a500,0x4000000,0x200000,0xd000c300,0x4000000,0xe00000,0xd000d202,0x7c00100,0x230400,0xd000d476,0x7c00100,0x230400,0xd000d997, -0x2802100,0x962460,0xd000d997,0x6800100,0x962540,0xd000e001,0x2802100,0x962460,0xd000e700,0x4000400,0x200000,0xd000e719,0x7c00100,0x220400,0xd000e719,0x7c00500, -0x22040f,0xd000fa00,0x4000000,0xe00000,0xd0010eaa,0x4000010,0x400000,0xd0010eaa,0x7c00100,0x230400,0xd0012dbd,0x4000000,0x200000,0xd0012dbd,0x7c00100,0x230400, -0xd0012fbe,0x2802100,0x962460,0xd0012fbe,0x2802400,0x962460,0xd0012fbe,0x2806400,0x962460,0xd0012fbe,0x4000000,0x400000,0xd0012fbe,0x6800000,0x1329800,0xd0012fbe, -0x6800100,0x962540,0xd0012fbe,0x6800100,0x962541,0xd0012fbe,0x6804400,0x962540,0xd0012fbe,0x7c00100,0x230400,0xd0012fbe,0x7c00100,0x230560,0xd0012fbe,0xc000010, -0x448000,0xd0013183,0x7c00100,0x230400,0xd0013200,0x4000000,0x200000,0xd0013200,0x6800000,0x1329805,0xd00134c0,0x2802100,0x962460,0xd00134c0,0x4000002,0x400000, -0xd00134c0,0x7c00100,0x230400,0xd00a4305,0x7c00100,0xe30400,0xd00a4611,0x7c40300,0xe30000,0xd00a4711,0x7c40300,0xe30000,0xd00a5e11,0x7c40300,0xe30000,0xd00acf00, -0x4000000,0xe00000,0xd00b0500,0x4000000,0xe00000,0xd00b0500,0x4000000,0x2800000,0xd00b0b11,0x6800500,0x962540,0xd00b0bbf,0x2802200,0xc62460,0xd00b119a,0x7c00300, -0xe30000,0xd00b2a00,0x4000000,0xe00000,0xd00b2e11,0x7c40300,0xe30000,0xd00b30bf,0x7c00300,0x230000,0xd00b339a,0x7c00300,0xe30000,0xe0000c02,0xc000010,0xb48000, -0xe0001524,0x2802400,0x962460,0xe0001524,0x7c00100,0x230400,0xe0001615,0x7c00100,0x230400,0xe000251b,0x12882000,0x962460,0xe0002a00,0x4000000,0x1500000,0xe0005102, -0x4000000,0x200000,0xe0005c00,0x4000000,0x200000,0xe000622a,0x6804400,0x962540,0xe000622a,0x7c00100,0x230400,0xe0008838,0x7c00100,0x220400,0xe0008838,0x7c00100, -0x250400,0xe0008e00,0x24000000,0x810000,0xe0008e00,0x24000000,0x1410000,0xe0008e00,0x24000002,0x400000,0xe0008e00,0x2c000010,0xb48000,0xe000933e,0x7c00100,0x230400, -0xe000933e,0xc000010,0x448000,0xe0009519,0x7c00100,0x220400,0xe0009519,0x7c00100,0x22040f,0xe0009519,0x7c00100,0x250400,0xe000c178,0x2802100,0x962460,0xe000c941, -0x2802100,0x962460,0xe000c941,0x2806400,0x962460,0xe000c941,0x7c00100,0x230400,0xe000d202,0x2802400,0x962460,0xe000d202,0x7c00100,0x230400,0xe000d202,0x7c00500, -0x230400,0xe000dc99,0x4000000,0x200000,0xe000e001,0x2802100,0x962460,0xe000e001,0x2802400,0x962460,0xe000fda1,0x7c00100,0x1830000,0xe0013502,0x2802400,0x962460, -0xe0013502,0x4000000,0x200000,0xe0013502,0x7c00100,0x230400,0xe0013502,0x80000000,0x218960,0xe00136c1,0x4000000,0x200000,0xe00136c1,0x7c00100,0x230400,0xe001370b, -0x7c00100,0x230400,0xe0013919,0x7c00500,0x220400,0xe0013919,0x7c00500,0x22040f,0xe0013919,0x7c00d00,0x23040f,0xe0013a19,0x7c00100,0x220400,0xe0013a19,0x7c00100, -0x230400,0xe0013bc2,0x2802400,0x962460,0xe0013bc2,0x7c00100,0x230400,0xe0013bc2,0xc000010,0x248000,0xe0013cc3,0x6800000,0x1329800,0xe0013cc3,0x7c00100,0x230400, -0xe0013dc4,0x2802400,0x962460,0xe0013dc4,0x7c00100,0x230400,0xe0013e28,0x7c00100,0x230400,0xe0013fc5,0x7c00100,0x220400,0xe0013fc5,0x7c00100,0x250400,0xe0014000, -0x4000000,0x200000,0xe0014001,0x2802400,0x962460,0xe00a4711,0x7c40300,0xe30000,0xe00a5e11,0x7c40300,0xe30000,0xe00ac511,0x7c40300,0xe30000,0xe00acf00,0x4000000, -0xe00000,0xe00ae300,0x4000000,0xe00000,0xe00b0500,0x4000000,0xe00000,0xe00b1314,0x7c00100,0xe30000,0xe00b1316,0x7c00100,0xe30c00,0xe00b2a00,0x4000000,0xe00000, -0xe00b2a00,0x4000000,0x2800000,0xe00b3816,0x7c00500,0x230c00,0xe0808328,0x2802400,0x962460,0xf0001615,0x6800100,0x962540,0xf0001a18,0x2802000,0x1862460,0xf000c247, -0x7c00100,0x230400,0xf000d000,0x4000000,0xe00000,0xf000e300,0x4000000,0xe00000,0xf000e59d,0x2802100,0x962460,0xf000e59d,0x7c00100,0x230400,0xf0012447,0, -0x818820,0xf0012447,0,0xc18820,0xf0012447,0,0x1418820,0xf0012447,0x2802000,0x962460,0xf0012447,0x2802400,0x962460,0xf0012447,0x7c00100,0x230400, -0xf0013a19,0x7c00100,0x220400,0xf0014102,0x2802400,0x962460,0xf0014308,0x2802100,0x962460,0xf0014308,0x7c00500,0x22040e,0xf0014308,0x7c00500,0x22040f,0xf001440a, -0x4000000,0x500000,0xf0014500,0x4000000,0x200000,0xf00146c6,0x2802100,0x962460,0xf00146c6,0x2806000,0x962460,0xf00146c6,0x4000000,0xe00000,0xf00146c6,0x6800000, -0x1329800,0xf00146c6,0x6800100,0x962540,0xf00146c6,0x6804000,0x962540,0xf00146c6,0x7c00100,0x230400,0xf00146c6,0x7c00100,0x230560,0xf00146c6,0xc000010,0x448000, -0xf00147c7,0x2802000,0x962460,0xf00147c7,0x6800000,0x1329800,0xf00147c7,0x7c00100,0x230400,0xf00ac511,0x7c40300,0xe30000,0xf00acf00,0x4000000,0xe00000,0xf00b2914, -0x7c00100,0x2530000,0xf00b2916,0x7c00100,0x2530c00,0xf00b2a00,0x4000000,0xe00000,0xf00b2a00,0x4000000,0x2800000,0xf00b4211,0x7c40300,0xe30000}; - -static const int32_t countPropsVectors=7230; -static const int32_t propsVectorsColumns=3; -static const uint16_t scriptExtensions[282]={ -0x800e,0x8019,8,0x8059,8,2,8,0x8038,8,6,8,0x8019,2,0x22,0x25,0x57, -0xb6,0x80c0,2,0x22,0x8025,2,0x12,2,0x22,0x25,0x57,0xa7,0xb6,0x80c0,2,0x22, -0x54,0x79,0x7b,0xa7,0xb6,0xb7,0x80c2,2,0x8022,2,0x25,0x80c0,2,0x29,2,0x80b6, -2,0x2e,4,0xa,0xf,0x10,0x15,0x19,0x1a,0x1f,0x23,0x24,0x89,0x97,0x809e,4, -0xa,0xf,0x10,0x15,0x19,0x1a,0x1f,0x23,0x24,0x89,0x809e,4,0xa,0xf,0x10,0x15, -0x1a,0x1f,0x21,0x23,0x24,0x3a,0x89,0x91,0x99,0x9e,0xa0,0xaf,0xb2,0xb3,0x80bb,4, -0xa,0xf,0x10,0x15,0x1a,0x1f,0x21,0x23,0x24,0x30,0x3a,0x89,0x91,0x99,0x9e,0xa0, -0xaf,0xb2,0xb3,0x80bb,0xa,0x78,0xa0,0x80b2,0xa,0x74,4,0x3a,0x8076,4,0x7a,0x10, -0x80a4,0x10,0x7f,0xf,0x809d,0xf,0x83,0x23,0x8089,0x23,0x87,0x15,0x80bb,0x15,0x8b,0x1c, -0x34,0x8076,0x1c,0x8f,0xc,0x8019,0x2a,0x2b,0x2c,0x802d,0x1b,0x805a,0x800a,4,0xa,0x15, -0x8089,0xa,0x8089,4,0x800a,0xa,0x8097,0xa,0x15,0x1a,0x1f,0x23,0x8024,0xa,0x80bb,4, -0xa,0x15,0x1f,0x24,0x89,0x9e,0x80bb,0x8004,8,0x8022,0x19,0x801b,0xa,0x19,0x8089,5, -0x11,0x12,0x14,0x16,0x8029,5,0x11,0x12,0x14,0x8016,0x8011,5,0x8011,0x11,0x14,0x8016, -0x11,0x8019,0xa,0xf,0x10,0x78,0x91,0x99,0x9d,0x9e,0xa0,0xa3,0x80b2,0xa,0xf,0x10, -0x15,0x1a,0x78,0x91,0x99,0x9d,0x9e,0xa0,0xa3,0xb2,0x80bb,0xa,0xf,0x10,0x15,0x78, -0x91,0x99,0x9d,0x9e,0xa0,0xa3,0xb2,0x80bb,0xa,0xa3,0xa,0x8023,0xa,0xfa,0x19,0x1c, -0x804f,0x37,0x804e,2,0x8057,2,0x8025,2,0x105,0x2f,0x31,0x8053,0x2f,0x31,0x80c1,0x2f, -0x8031,2,0x8007,0x79,0x80c2,0x79,0x113,0x89,0x87,0x8087}; - -static const int32_t indexes[UPROPS_INDEX_COUNT]={0x2d08,0x2d08,0x2d08,0x2d08,0x6ce6,3,0x8924,0x89b1,0x89b1,0x89b1,0xb47c7,0x2a75a31,0,0,0,0}; - -#endif // INCLUDED_FROM_UCHAR_C +// Copyright (C) 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// Copyright (C) 1999-2016, International Business Machines +// Corporation and others. All Rights Reserved. +// +// file name: uchar_props_data.h +// +// machine-generated by: icu/tools/unicode/c/genprops/corepropsbuilder.cpp + +#ifdef INCLUDED_FROM_UCHAR_C + +static const UVersionInfo dataVersion={0xf,0,0,0}; + +static const uint16_t propsTrie_index[23016]={ +0x495,0x49d,0x4a5,0x4ad,0x4c5,0x4cd,0x4d5,0x4dd,0x4e5,0x4ed,0x4f3,0x4fb,0x503,0x50b,0x513,0x51b, +0x521,0x529,0x531,0x539,0x53c,0x544,0x54c,0x554,0x55c,0x564,0x560,0x568,0x570,0x578,0x57d,0x585, +0x58d,0x595,0x599,0x5a1,0x5a9,0x5b1,0x5b9,0x5c1,0x5bd,0x5c5,0x5ca,0x5d2,0x5d8,0x5e0,0x5e8,0x5f0, +0x5f8,0x600,0x608,0x610,0x615,0x61d,0x620,0x628,0x630,0x638,0x63e,0x646,0x645,0x64d,0x655,0x65d, +0x66d,0x665,0x675,0x67d,0x683,0x600,0x693,0x68b,0x6a3,0x6a5,0x6ad,0x69b,0x6bd,0x6c3,0x6cb,0x6b5, +0x6db,0x6e1,0x6e9,0x6d3,0x6f9,0x6ff,0x707,0x6f1,0x717,0x71d,0x725,0x70f,0x735,0x73d,0x745,0x72d, +0x755,0x75b,0x763,0x74d,0x773,0x779,0x781,0x76b,0x791,0x796,0x79e,0x789,0x7ae,0x7b5,0x7bd,0x7a6, +0x641,0x7c5,0x7cd,0x4b5,0x7d5,0x7dc,0x7e4,0x4b5,0x7ec,0x7f4,0x7fc,0x801,0x809,0x810,0x818,0x4b5, +0x600,0x820,0x828,0x830,0x838,0x58d,0x848,0x840,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x850,0x600,0x858,0x85c,0x864,0x600,0x86a,0x600,0x870,0x878,0x880,0x58d,0x58d,0x888, +0x890,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x895,0x89d,0x600,0x600,0x8a5,0x8ad,0x8b5,0x8bd,0x8c5,0x600,0x8cd,0x8d5,0x8dd, +0x8ed,0x600,0x8f5,0x8f7,0x8ff,0x8e5,0x600,0x902,0x916,0x90a,0x912,0x91e,0x600,0x926,0x92c,0x934, +0x93c,0x600,0x94c,0x954,0x95c,0x944,0x96c,0x4b5,0x974,0x977,0x97f,0x964,0x98f,0x987,0x600,0x996, +0x600,0x9a5,0x99e,0x9ad,0x9b5,0x9b9,0x9c1,0x9c9,0x535,0x9d1,0x9d4,0x9da,0x9e1,0x9d4,0x55c,0x55c, +0x4e5,0x4e5,0x4e5,0x4e5,0x9e9,0x4e5,0x4e5,0x4e5,0x9f9,0xa01,0xa09,0xa11,0xa19,0xa1d,0xa25,0x9f1, +0xa3d,0xa45,0xa2d,0xa35,0xa4d,0xa55,0xa5d,0xa65,0xa7d,0xa6d,0xa75,0xa85,0xa8d,0xa9c,0xaa1,0xa94, +0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xab1,0xab9,0x934,0xabc,0xac4,0xacb,0xad0,0xad8, +0x934,0xadf,0xade,0xaef,0xaf2,0x934,0x934,0xae7,0x934,0x934,0x934,0x934,0x934,0xb01,0xb09,0xaf9, +0x934,0x934,0x934,0xb0e,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0xb14,0xb1c,0x934,0xb24,0xb2b, +0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0xaa9,0xaa9,0xaa9,0xaa9,0xb33,0xaa9,0xb3a,0xb41, +0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0xaa9,0x934,0xb49,0xb50,0xb54,0xb5a,0x934,0x934,0x934, +0x58d,0x595,0x535,0xb62,0x4e5,0x4e5,0x4e5,0xb6a,0x535,0xb72,0x600,0xb78,0xb88,0xb80,0xb80,0x55c, +0xb90,0xb98,0xba0,0x4b5,0xba8,0x934,0x934,0xbaf,0x934,0x934,0x934,0x934,0x934,0x934,0xbb7,0xbbd, +0xbcd,0xbc5,0x641,0x600,0xbd5,0x890,0x600,0xbdd,0xbe5,0xbe9,0x600,0x600,0xbee,0x600,0x934,0xbf5, +0xad9,0xbfd,0xc03,0x934,0xbfd,0xc0b,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934, +0xc13,0x600,0x600,0x600,0xc1b,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0xc21,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xc26,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x934,0x934, +0xc2e,0x600,0xc31,0x600,0xc39,0xc3f,0xc47,0xc4f,0xc54,0x600,0x600,0xc58,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xc5f,0x600,0xc66,0xc6c,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xc74,0x600,0x600,0x600,0xc7c,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0xc7e,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xc85,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0xc8c,0x600,0x600,0x600,0xc93,0xc9b,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xca0,0x600,0x600,0xca8,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xcac,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xcaf,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xcb2,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0xcb8,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0xcc0,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0xcc5,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0xcca,0x600,0x600,0x600,0xccf,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0xcd7,0xcde,0xce2,0x600,0x600,0x600,0xce9,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0xcf7,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0xcef,0x934,0xcff,0x9ad,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0xd04,0xd0c,0x4e5,0xd1c,0xd14,0x600,0x600,0xd24,0xd2c,0xd3c,0x4e5,0xd41,0xd49,0xd4f,0xd56,0xd34, +0xd5e,0xd66,0x600,0xd6e,0xd7e,0xd81,0xd76,0xd89,0x655,0xd91,0xd98,0x8f6,0x6a3,0xda8,0xda0,0xdb0, +0x600,0xdb8,0xdc0,0xdc8,0x600,0xdd0,0xdd8,0xde0,0xde8,0xdf0,0xdf4,0xdfc,0x535,0x535,0x600,0xe04, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xe0c,0xe18,0xe10, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20, +0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0x600,0x600,0x600,0xe30,0x600,0xcea,0xe37,0xe3c, +0x600,0x600,0x600,0xe44,0x600,0x600,0x901,0x4b5,0xe5a,0xe4a,0xe52,0x600,0x600,0xe62,0xe6a,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xe6f,0x938,0x600,0xe77,0x600,0xe7d,0xe81, +0xe89,0xe91,0xe98,0xea0,0x600,0x600,0x600,0xea6,0xebe,0x4a5,0xec6,0xece,0xed3,0x916,0xeae,0xeb6, +0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20, +0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20,0xe20, +0x12d4,0x12d4,0x1314,0x1354,0x1394,0x13cc,0x140c,0x144c,0x1484,0x14c4,0x14f0,0x1530,0x1570,0x1580,0x15c0,0x15f4, +0x1634,0x1664,0x16a4,0x16e4,0x16f4,0x1728,0x1760,0x17a0,0x17e0,0x1820,0x1854,0x1880,0x18c0,0x18f8,0x1914,0x1954, +0xa80,0xac0,0xb00,0xb40,0xb80,0xbab,0xbeb,0xa40,0xc0e,0xa40,0xa40,0xa40,0xa40,0xc4e,0x1db,0x1db, +0xc8e,0xcce,0xa40,0xa40,0xa40,0xcf7,0xd37,0xd57,0xa40,0xd7d,0xdbd,0xdfd,0xe3d,0xe7d,0xebd,0xefd, +0xf3d,0xf74,0x1db,0x1db,0xf98,0xfcc,0x1db,0xff4,0x1db,0x1db,0x1db,0x1db,0x1021,0x1db,0x1db,0x1db, +0x1db,0x1db,0x1db,0x1db,0x1035,0x1db,0x106d,0x10ad,0x1db,0x10b8,0x1db,0x1db,0x1db,0x10ee,0xa40,0x112e, +0x1db,0x1db,0x116e,0x1db,0x1191,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0x11d1,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700, +0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x1211, +0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700, +0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x700,0x1211, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0xedb,0xee2,0xeea,0x4b5,0x600,0x600,0x600,0xef2,0xf02,0xefa,0xf19,0xf0a,0xf11,0xf21,0xf25,0xf29, +0x4b5,0x4b5,0x4b5,0x4b5,0x8f6,0x600,0xf31,0xf39,0x600,0xf41,0xf49,0xf4d,0xf55,0x600,0xf5d,0x4b5, +0x58d,0x597,0xf65,0x600,0xf69,0xf71,0xf81,0xf79,0x600,0xf89,0x600,0xf90,0xfa0,0xf98,0x4b5,0x4b5, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xb88,0x902,0xfa8,0xfb8,0xfb0,0x4b5,0x4b5, +0xfc8,0xfc0,0xfcb,0xfd3,0x916,0xfdb,0x4b5,0xfe3,0xfeb,0xff3,0x4b5,0x4b5,0x600,0x1003,0x100b,0xffb, +0x101b,0x1022,0x1013,0x102a,0x1032,0x4b5,0x1042,0x103a,0x600,0x1045,0x104d,0x1055,0x105d,0x1065,0x4b5,0x4b5, +0x600,0x600,0x106d,0x4b5,0x58d,0x1075,0x535,0x107d,0x600,0x1085,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x108d,0x600,0x1095,0x4b5,0x109a,0x10a2,0x10aa,0x10b1,0xfdf,0x10b9,0xfdf,0x10c1,0xb88, +0x10d1,0x636,0x10d9,0x10c9,0x98f,0x10e1,0x10e9,0x10ef,0x1107,0x10f7,0x10ff,0x110b,0x98f,0x111b,0x1113,0x1123, +0x113b,0x112b,0x1133,0x4b5,0x1142,0x114a,0x658,0x1152,0x1162,0x1168,0x1170,0x115a,0x4b5,0x4b5,0x4b5,0x4b5, +0x600,0x1178,0x1180,0x1099,0x600,0x1188,0x1190,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x600,0x1198,0x11a0,0x4b5, +0x600,0x11a8,0x11b0,0x11b8,0x600,0x11c8,0x11c0,0x4b5,0x870,0x11d0,0x11d8,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x600,0x11e0,0x4b5,0x4b5,0x4b5,0x58d,0x535,0x11e8,0x11f8,0x11fe,0x11f0,0x4b5,0x4b5,0x120e,0x1212,0x1206, +0x122a,0x121a,0x1222,0x600,0x1238,0x1232,0x600,0x8f7,0x1248,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x1256,0x125b,0x1240,0x1250,0x126b,0x1263,0x4b5,0x4b5,0x127a,0x127e,0x1272,0x128e,0x1286,0x11c0,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x1292,0x12a2,0x12a7,0x129a,0x4b5,0x4b5,0x12af,0x12bf,0x12b7, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x901,0x4b5,0x4b5,0x4b5, +0x12cf,0x12d7,0x12df,0x12c7,0x600,0x600,0x600,0x600,0x600,0x600,0x12e7,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0xfdf,0x600,0x600,0x12ef,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x12f7,0x12ff,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x11d8,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x8f7, +0x916,0xda4,0x600,0x916,0x1307,0x130c,0x600,0x131c,0x1324,0x132c,0x1314,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x58d,0x535,0x1334,0x4b5,0x4b5,0x4b5,0x600,0x600,0x133c,0x1341,0x1347,0x4b5,0x4b5,0x134f,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x1357,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x902,0x4b5,0x106d,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x135d,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x1365,0x136a,0x1371,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xe10,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x600,0x600,0x600,0x1377,0x137c,0x1384,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x55c,0x1394,0x139b,0x934,0x934,0x934,0x138c,0x4b5,0x934,0x934,0x934, +0x934,0x934,0x934,0x934,0xbb7,0x934,0x13a2,0x934,0x13a9,0x13b1,0x13b7,0x934,0xade,0x934,0x934,0x13bf, +0x4b5,0x4b5,0x4b5,0x13c7,0x13c7,0x934,0x934,0xadb,0x13cf,0x4b5,0x4b5,0x4b5,0x4b5,0x13df,0x13e6,0x13eb, +0x13f1,0x13f9,0x1401,0x1409,0x13e3,0x1411,0x1419,0x1421,0x1426,0x13f8,0x13df,0x13e6,0x13e2,0x13f1,0x142e,0x13e0, +0x1431,0x13e3,0x1439,0x1441,0x1449,0x1450,0x143c,0x1444,0x144c,0x1453,0x143f,0x145b,0x13d7,0x934,0x934,0x934, +0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x55c,0x146b,0x55c, +0x1472,0x1479,0x1463,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x1488,0x1490,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x1480,0x1498,0x9d4, +0x14a8,0x14a0,0x4b5,0x4b5,0x4b5,0x600,0x14b8,0x14b0,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0xfdf,0x14c0,0x600,0x14c8,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0xfdf,0x14d0,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x14d8,0x600,0x600,0x600, +0x600,0x600,0x600,0x14e0,0x4b5,0x58d,0x14f0,0x14e8,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x14f8,0x1508,0x1500,0x4b5,0x4b5,0x1518,0x1510,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x1528,0x1530,0x1538, +0x1540,0x1548,0x1550,0x4b5,0x1520,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x934,0x1558,0x934, +0x934,0xbaf,0x13a0,0x1560,0xbb7,0x1568,0x934,0x934,0x934,0x934,0xbb9,0x4b5,0x1570,0x1578,0x157c,0x1584, +0x158c,0x4b5,0x4b5,0x4b5,0x4b5,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x1594,0x934,0x934,0x934, +0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934, +0x934,0x934,0x934,0x157d,0x159c,0x934,0x934,0x934,0x15a4,0x934,0x934,0x15ab,0x15b3,0x1558,0x934,0x15bb, +0x934,0x15c3,0x15c8,0x4b5,0x4b5,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0x934,0xbaf, +0x15d0,0x15d9,0x15dd,0x15e5,0x15d5,0x934,0x934,0x934,0x934,0x15ed,0x934,0xade,0x11bc,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x15f5,0x600,0x600, +0x15fc,0x600,0x600,0x600,0x1604,0x600,0x160c,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xc90,0x600,0x600, +0x1614,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x161c,0x1624,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0xccf,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x162b,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x1632,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x1639,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x4b5,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x901,0x600,0x600,0x600,0x600,0x600,0x600,0xf69,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x1641,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x1649,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x600,0x600, +0x600,0x600,0x1651,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0xf69,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x67d,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x1314,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x1661,0x1659,0x1659,0x1659,0x4b5,0x4b5,0x4b5,0x4b5,0x55c,0x55c,0x55c,0x55c,0x55c,0x55c,0x55c, +0x1669,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5,0x4b5, +0x4b5,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0x1671,0x494,0x494,0x494,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf, +0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf, +0xf,0xf,0xf,0xf,0xc,0x17,0x17,0x17,0x19,0x17,0x17,0x17,0x14,0x15,0x17,0x18, +0x17,0x13,0x17,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17, +0x18,0x18,0x18,0x17,0x17,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x14, +0x17,0x15,0x1a,0x16,0x1a,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,0x14, +0x18,0x15,0x18,0xf,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf, +0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf, +0xf,0xf,0xf,0xf,0xc,0x17,0x19,0x19,0x19,0x19,0x1b,0x17,0x1a,0x1b,5,0x1c, +0x18,0x10,0x1b,0x1a,0x1b,0x18,0x34b,0x38b,0x1a,2,0x17,0x17,0x1a,0x30b,5,0x1d, +0x34cb,0x344b,0x3ccb,0x17,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0x18,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,0x18,2,2,2,2, +2,2,2,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,2,1,2,1, +2,1,2,1,2,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,1,2,1,2,1,2,2,2,1,1,2, +1,2,1,1,2,1,1,1,2,2,1,1,1,1,2,1, +1,2,1,1,1,2,2,2,1,1,2,1,1,2,1,2, +1,2,1,1,2,1,2,2,1,2,1,1,2,1,1,1, +2,1,2,1,1,2,2,5,1,2,2,2,5,5,5,5, +1,3,2,1,3,2,1,3,2,1,2,1,2,1,2,1, +2,1,2,1,2,1,2,1,2,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,2,1,3,2, +1,2,1,1,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,2,2,2,2,2,2,1,1, +2,1,1,2,2,1,2,1,1,1,1,2,1,2,1,2, +1,2,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,5,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, +4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +4,4,0x1a,0x1a,0x1a,0x1a,4,4,4,4,4,4,4,4,4,4, +4,4,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, +4,4,4,4,4,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,4,0x1a,4,0x1a, +0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +1,2,1,2,4,0x1a,1,2,0,0,4,2,2,2,0x17,1, +0,0,0,0,0x1a,0x1a,1,0x17,1,1,1,0,1,0,1,1, +2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,0,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,1,2,2,1,1,1,2,2,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,2,2,2,2,1,2,0x18,1,2,1,1,2, +2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,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,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,0x1b,6,6,6,6,6,7,7,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,1,2,1,2,1,2,1,2,1,2,1, +2,1,2,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,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,0, +0,4,0x17,0x17,0x17,0x17,0x17,0x17,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,0x17,0x13,0,0,0x1b,0x1b,0x19, +0,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,0x13,6, +0x17,6,6,0x17,6,6,0x17,6,0,0,0,0,0,0,0,0, +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,0,0,0,0,5, +5,5,5,0x17,0x17,0,0,0,0,0,0,0,0,0,0,0, +0x10,0x10,0x10,0x10,0x10,0x10,0x18,0x18,0x18,0x17,0x17,0x19,0x17,0x17,0x1b,0x1b, +6,6,6,6,6,6,6,6,6,6,6,0x17,0x10,0x17,0x17,0x17, +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, +4,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17,0x17,0x17,5,5, +6,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,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,0x17,5,6,6,6,6,6,6,6,0x10,0x1b,6, +6,6,6,6,6,4,4,6,6,0x1b,6,6,6,6,5,5, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,5,5,5,0x1b,0x1b,5, +0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0,0x10, +5,6,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, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,0,0,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, +5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6, +6,6,6,6,6,5,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,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,6, +6,6,6,6,6,6,6,6,4,4,0x1b,0x17,0x17,0x17,4,0, +0,6,0x19,0x19,6,6,6,6,4,6,6,6,4,6,6,6, +6,6,0,0,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, +0x17,0x17,0x17,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,6,6,6,6,4,6, +6,6,6,6,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,6,6,6, +0,0,0x17,0,5,5,5,5,5,5,5,5,5,5,5,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,0x1a,5,5,5,5,5,5,0,0x10,0x10,0,0, +0,0,0,0,6,6,6,6,6,6,6,6,6,6,0x10,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5, +5,5,5,5,5,4,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,5,5,6,6, +0x17,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,4,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,8, +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,6,8, +6,5,8,8,8,6,6,6,6,6,6,6,6,8,8,8, +8,6,8,8,5,6,6,6,6,6,6,6,5,5,5,5, +5,5,5,5,5,5,6,6,0,0,0x49,0x89,0xc9,0x109,0x149,0x189, +0x1c9,0x209,0x249,0x289,5,5,0x19,0x19,0x37cb,0x35cb,0x3fcb,0x34cb,0x3ccb,0x94b,0x1b,0x19, +5,0x17,6,0,5,6,8,8,0,5,5,5,5,5,5,5, +5,0,0,5,5,0,0,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,0,5,5,5,5,5,5,5,0,5,0, +0,0,5,5,5,5,0,0,6,5,8,8,8,6,6,6, +6,0,0,8,8,0,0,8,8,6,5,0,0,0,0,0, +0,0,0,8,0,0,0,0,5,5,0,5,0,0,0,0, +0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,6,6,5,5, +5,6,0x17,0,0,0,0,0,0,0,0,0,0,6,6,8, +0,5,5,5,5,5,5,0,0,0,0,5,5,0,0,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,0,5,5, +5,5,5,5,5,0,5,5,0,5,5,0,5,5,0,0, +6,0,8,8,8,6,6,0,0,0,0,6,6,0,0,6, +6,6,0,0,0,6,0,0,0,0,0,0,0,5,5,5, +5,0,5,0,5,5,6,6,0,0,0x49,0x89,0xc9,0x109,0x149,0x189, +0x1c9,0x209,0x249,0x289,0x17,0x19,0,0,0,0,0,0,0,5,6,6, +6,6,6,6,0,6,6,8,0,5,5,5,5,5,5,5, +5,5,0,5,5,5,0,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,0,5,5,5,5,5,5,5,0,5,5, +0,5,5,5,5,5,0,0,6,5,8,8,8,6,6,6, +6,6,0,6,6,8,0,8,8,6,0,0,5,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5,5,6,6, +0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x1b,5,0x34cb,0x344b, +0x3ccb,0x37cb,0x35cb,0x3fcb,0,0,0,0,0,0,0,0,0,6,8,8, +0,5,5,5,5,5,5,5,5,0,0,5,5,0,0,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,0,5,5, +5,5,5,5,5,0,5,5,0,5,5,5,5,5,0,0, +6,5,8,6,8,6,6,6,6,0,0,8,8,0,0,8, +8,6,0,0,0,0,0,0,0,6,6,8,0,0,0,0, +5,5,0,5,0,0,0,0,0,0,0x49,0x89,0xc9,0x109,0x149,0x189, +0x1c9,0x209,0x249,0x289,0x7cb,0x1e4b,0x784b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x19,0x1b,0, +0,0,0,0,0,0,6,5,0,5,5,5,5,5,5,0, +0,0,5,5,5,0,5,5,5,5,0,0,0,5,5,0, +5,0,5,5,0,0,0,5,5,0,0,0,5,5,5,0, +0,0,5,5,5,5,5,5,5,5,5,5,5,5,0,0, +0,0,8,8,6,8,8,0,0,0,8,8,8,0,8,8, +8,6,0,0,5,0,0,0,0,0,0,8,0,0,0,0, +0,0,0,0,5,5,6,6,0,0,0x49,0x89,0xc9,0x109,0x149,0x189, +0x1c9,0x209,0x249,0x289,0,0,0,0,0,0,0,0x17,0x54b,0x58b,0x5cb,0x60b, +0x58b,0x5cb,0x60b,0x1b,6,8,8,8,6,5,5,5,5,5,5,5, +5,0,5,5,5,0,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,0,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,0,0,6,5,6,6,6,8,8,8, +8,0,6,6,6,0,6,6,6,6,0,0,0,0,0,0, +0,6,6,0,5,5,5,0,0,5,0,0,5,5,6,6, +0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,5,5,8, +0,0,0,0,0,0,0,0,0,0,0,0,5,6,8,8, +0x17,5,5,5,5,5,5,5,5,0,5,5,5,0,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,0,5,5, +5,5,5,5,5,5,5,5,0,5,5,5,5,5,0,0, +6,5,8,6,8,8,8,8,8,0,6,8,8,0,8,8, +6,6,0,0,0,0,0,0,0,8,8,0,0,0,0,0, +0,5,5,0,5,5,6,6,0,0,0x49,0x89,0xc9,0x109,0x149,0x189, +0x1c9,0x209,0x249,0x289,0x7cb,0x1e4b,0x784b,0x34cb,0x344b,0x3ccb,0x37cb,0x35cb,0x3fcb,0x1b,5,5, +5,5,5,5,6,6,8,8,5,5,5,5,5,5,5,5, +5,0,5,5,5,0,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,6,6,5,8,8,8,6,6,6,6,0,8,8, +8,0,8,8,8,6,5,0x1b,0,0,0,0,5,5,5,8, +0xcc0b,0xca0b,0xcb4b,0xc90b,0x364b,0xc94b,0x350b,5,0,0,0,0,0,0,0x49,0x89, +0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,8,8,0x17,0,0,0, +0,0,0,0,0,0,0,0,0,6,8,8,0,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0, +0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,0,5,5,5,5,5,5,5,5,5, +0,5,0,0,5,5,5,5,5,5,5,0,0,0,6,0, +0,0,0,8,8,8,6,6,6,0,6,0,8,8,8,8, +8,8,8,8,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,6,5,5,6,6,6,6,6,6,6,0, +0,0,0,0x19,5,5,5,5,5,5,4,6,6,6,6,6, +6,6,6,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17, +0,0,0,0,0,5,5,0,5,0,5,5,5,5,5,0, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,0,5,0,5,5,5,5,5,5,5,5,5, +5,6,5,5,6,6,6,6,6,6,6,6,6,5,0,0, +5,5,5,5,5,0,4,0,6,6,6,6,6,6,6,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,5,5,5,5, +5,0x1b,0x1b,0x1b,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, +0x17,0x17,0x17,0x1b,0x17,0x1b,0x1b,0x1b,6,6,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x344b,0x3c4b,0x444b,0x4c4b,0x544b,0x5c4b, +0x644b,0x6c4b,0x744b,0x2c4b,0x1b,6,0x1b,6,0x1b,6,0x14,0x15,0x14,0x15,8,8, +5,5,5,5,5,5,5,5,0,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,8,6,6,6,6,6,0x17,6,6,5,5,5,5, +5,6,6,6,6,6,6,6,6,6,6,6,0,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,0,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,6,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0x1b,0x1b, +0x17,0x17,0x17,0x17,0x17,0x1b,0x1b,0x1b,0x1b,0x17,0x17,0,0,0,0,0, +5,5,5,5,5,5,5,5,5,5,5,8,8,6,6,6, +6,8,6,6,6,6,6,6,8,6,6,8,8,6,6,5, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17,0x17,0x17,0x17,0x17, +5,5,5,5,5,5,8,8,6,6,5,5,5,5,6,6, +6,5,8,8,8,5,5,8,8,8,8,8,8,8,5,5, +5,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5, +5,5,6,8,8,6,6,8,8,8,8,8,8,6,5,8, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,8,8,8,6,0x1b,0x1b, +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,0x17,4,2,2,2, +1,1,1,1,1,1,0,1,0,0,0,0,0,1,0,0, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +5,5,5,5,5,5,5,5,5,0,5,5,5,5,0,0, +5,5,5,5,5,5,5,0,5,0,5,5,5,5,0,0, +5,5,5,5,5,5,5,5,5,0,5,5,5,5,0,0, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,0,5,5,5,5,0,0,5,5,5,5,5,5,5,0, +5,0,5,5,5,5,0,0,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,0,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,0,5,5,5,5,0,0, +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,0,0,6,6,6, +0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b, +0x4cb,0x50b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1bcb,0x1e4b,0x788b,0,0,0, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,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,0,0,2,2,2,2,2,2,0,0, +0x13,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,0x1b,0x17,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,0xc,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,0x14, +0x15,0,0,0,5,5,5,5,5,5,5,5,5,5,5,0x17, +0x17,0x17,0x98a,0x9ca,0xa0a,5,5,5,5,5,5,5,5,0,0,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,6,6,6,8,0,0,0,0,0,0, +0,0,0,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,6,6,8,0x17,0x17,0,0,0,0,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,6,6,0,0,0,0,0,0,0,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,0,5,5,5,0,6,6,0,0,0,0,0,0,0,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,6,6,8,6,6,6,6,6, +6,6,8,8,8,8,8,8,8,8,6,8,8,6,6,6, +6,6,6,6,6,6,6,6,0x17,0x17,0x17,4,0x17,0x17,0x17,0x19, +5,6,0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0, +0,0,0,0,0x54b,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,6,5,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,0x17,0x17,0x17,0x17,0x17,0x17,0x13,0x17,0x17,0x17,0x17,6, +6,6,0x10,6,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0, +0,0,0,0,5,5,5,4,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,0,0,0,0,0,0,0,5,5,5,5, +5,6,6,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,0,0, +0,0,0,0,0,0,0,0,6,6,6,8,8,8,8,6, +6,8,8,8,0,0,0,0,8,8,6,8,8,8,8,8, +8,6,6,6,0,0,0,0,0x1b,0,0,0,0x17,0x17,0x49,0x89, +0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,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,0,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,0,0,5,5,5,5,5,0,0,0, +0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5, +5,5,5,5,0,0,0,0,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x30b,0,0,0,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,6,6,8,8,6,0,0,0x17,0x17, +0x17,0x17,0x17,0x17,0x17,0x17,0x17,4,0x17,0x17,0x17,0x17,0x17,0x17,0,0, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,6, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,8,6,8,6,6,6,6,6,6,6,0, +6,8,6,8,8,6,6,6,6,6,6,6,6,8,8,8, +8,8,8,6,6,6,6,6,6,6,6,6,6,0,0,6, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, +0x17,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,6,6,6,6,6, +6,6,6,6,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x17,0x17,0, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +6,6,6,6,8,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, +6,8,6,6,6,6,6,8,6,8,8,8,8,8,6,8, +8,5,5,5,5,5,5,5,5,0,0,0,0x49,0x89,0xc9,0x109, +0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17,0x17,0x17,0x17,0x17,5,8,6,6, +6,6,8,8,6,6,8,6,6,6,5,5,0x49,0x89,0xc9,0x109, +0x149,0x189,0x1c9,0x209,0x249,0x289,5,5,5,5,5,5,6,6,8,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,6,8, +6,6,8,8,8,6,8,6,6,6,8,8,0,0,0,0, +0,0,0,0,0x17,0x17,0x17,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, +0x249,0x289,0,0,0,5,5,5,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, +0x249,0x289,5,5,5,5,5,5,8,8,8,8,8,8,8,8, +6,6,6,6,6,6,6,6,8,8,6,6,0,0,0,0x17, +0x17,0x17,0x17,0x17,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4, +4,4,0x17,0x17,2,2,2,2,2,2,2,2,2,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,0, +0,1,1,1,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0,0,0,0, +0,0,0,0,6,6,6,0x17,6,6,6,6,6,6,6,6, +6,6,6,6,6,8,6,6,6,6,6,6,6,5,5,5, +5,6,5,5,5,5,5,5,6,5,5,8,6,6,5,0, +0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2, +4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2, +4,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,4, +4,4,4,4,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,2,2,2,2,2,2, +2,2,1,2,2,2,2,2,2,2,2,2,1,1,1,1, +1,0x1a,0x1a,0x1a,0,0,2,2,2,0,2,2,1,1,1,1, +3,0x1a,0x1a,0,2,2,2,2,2,2,2,2,1,1,1,1, +1,1,1,1,2,2,2,2,2,2,0,0,1,1,1,1, +1,1,0,0,2,2,2,2,2,2,2,2,1,1,1,1, +1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1, +1,1,1,1,2,2,2,2,2,2,0,0,1,1,1,1, +1,1,0,0,2,2,2,2,2,2,2,2,0,1,0,1, +0,1,0,1,2,2,2,2,2,2,2,2,1,1,1,1, +1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,0,0,2,2,2,2,2,2,2,2,3,3,3,3, +3,3,3,3,2,2,2,2,2,2,2,2,3,3,3,3, +3,3,3,3,2,2,2,2,2,0,2,2,1,1,1,1, +3,0x1a,2,0x1a,0x1a,0x1a,2,2,2,0,2,2,1,1,1,1, +3,0x1a,0x1a,0x1a,2,2,2,2,0,0,2,2,1,1,1,1, +0,0x1a,0x1a,0x1a,0x16,0x17,0x17,0x17,0x18,0x14,0x15,0x17,0x17,0x17,0x17,0x17, +0x17,0x17,0x17,0x17,0x17,0x17,0x18,0x17,0x16,0x17,0x17,0x17,0x17,0x17,0x17,0x17, +0x17,0x17,0x17,0xc,0x10,0x10,0x10,0x10,0x10,0,0x10,0x10,0x10,0x10,0x10,0x10, +0x10,0x10,0x10,0x10,0x2cb,4,0,0,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x18,0x18, +0x18,0x14,0x15,4,0xc,0xc,0xc,0xc,0xc,0xc,0xc,0xc,0xc,0xc,0xc,0x10, +0x10,0x10,0x10,0x10,0x13,0x13,0x13,0x13,0x13,0x13,0x17,0x17,0x1c,0x1d,0x14,0x1c, +0x1c,0x1d,0x14,0x1c,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0xd,0xe,0x10,0x10, +0x10,0x10,0x10,0xc,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x1c,0x1d,0x17, +0x17,0x17,0x17,0x16,0x2cb,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x18,0x18, +0x18,0x14,0x15,0,4,4,4,4,4,4,4,4,4,4,4,4, +4,0,0,0,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, +0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, +0x19,0x19,0x19,0x19,0x19,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6, +6,7,7,7,7,6,7,7,7,6,6,6,6,6,6,6, +6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1b,0x1b,0x1b,0x1b,1,0x1b,1,0x1b,1,0x1b,1,1, +1,1,0x1b,2,1,1,1,1,2,5,5,5,5,2,0x1b,0x1b, +2,2,1,1,0x18,0x18,0x18,0x18,0x18,1,2,2,2,2,0x1b,0x18, +0x1b,0x1b,2,0x1b,0x358b,0x360b,0x364b,0x348b,0x388b,0x350b,0x390b,0x3d0b,0x410b,0x354b,0x454b,0x35cb, +0x3dcb,0x45cb,0x4dcb,0x58b,0x1b,0x1b,1,0x1b,0x1b,0x1b,0x1b,1,0x1b,0x1b,2,1, +1,1,2,2,1,1,1,2,0x1b,1,0x1b,0x1b,0x18,1,1,1, +1,1,0x1b,0x1b,0x58a,0x5ca,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0x7ca,0x80a,0x84a, +0x11ca,0x1e4a,0x980a,0x784a,0x58a,0x5ca,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0x7ca,0x80a,0x84a, +0x11ca,0x1e4a,0x980a,0x784a,0x784a,0x984a,0x788a,1,2,0x6ca,0x11ca,0x988a,0x78ca,0x54b,0x1b,0x1b, +0,0,0,0,0x18,0x18,0x18,0x18,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x18, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x18,0x1b,0x1b,0x18,0x1b,0x1b,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x18,0x18,0x1b,0x1b,0x18,0x1b,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x14,0x15,0x14,0x15, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x14,0x15,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18, +0x18,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x2cb,0x80b,0x84b,0x88b,0x8cb,0x90b,0x94b,0x98b,0x9cb,0xa0b, +0xa4b,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb,0x2cb,0x30b,0x34b,0x38b,0x3cb, +0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb,0x80b,0x84b,0x88b,0x8cb,0x90b,0x94b,0x98b,0x9cb,0xa0b,0xa4b, +0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb,0x80b,0x84b,0x88b,0x8cb,0x90b,0x94b, +0x98b,0x9cb,0xa0b,0xa4b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15, +0x14,0x15,0x14,0x15,0x14,0x15,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb, +0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b, +0x48b,0x4cb,0x50b,0x7cb,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x18,0x18,0x18,0x18,0x18,0x14,0x15,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x18,0x18,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x14, +0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x14, +0x15,0x14,0x15,0x14,0x15,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x14,0x15,0x14,0x15,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x14,0x15,0x18,0x18,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x18,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,1,2,1,1,1,2,2,1, +2,1,2,1,2,1,1,1,1,2,1,2,2,1,2,2, +2,2,2,2,4,4,1,1,1,2,1,2,2,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,1,2,1,2,6,6,6,1,2,0,0,0,0, +0,0x17,0x17,0x17,0x17,0x344b,0x17,0x17,2,2,2,2,2,2,0,2, +0,0,0,0,0,2,0,0,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,4, +0x17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, +5,5,5,5,5,5,5,0,5,5,5,5,5,5,5,0, +5,5,5,5,5,5,5,0,5,5,5,5,5,5,5,0, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0, +0x17,0x17,0x1c,0x1d,0x1c,0x1d,0x17,0x17,0x17,0x1c,0x1d,0x17,0x1c,0x1d,0x17,0x17, +0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x13,0x17,0x17,0x13,0x17,0x1c,0x1d,0x17,0x17, +0x1c,0x1d,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x17,0x17,0x17,0x17,0x17,4, +0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x13,0x13,0x17,0x17,0x17,0x17, +0x13,0x17,0x14,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, +0x1b,0x1b,0x17,0x17,0x17,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15,0x13,0,0, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0,0,0,0,0,0,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0,0,0,0,0x1b,0x58a,0x5ca,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,6,6, +6,6,8,8,0x13,4,4,4,4,4,0x1b,0x1b,0x7ca,0xa4a,0xcca,4, +5,0x17,0x1b,0x1b,0xc,0x17,0x17,0x17,0x1b,4,5,0x54a,0x14,0x15,0x14,0x15, +0x14,0x15,0x14,0x15,0x14,0x15,0x1b,0x1b,0x14,0x15,0x14,0x15,0x14,0x15,0x14,0x15, +0x13,0x14,0x15,0x15,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,0,0,6,6,0x1a, +0x1a,4,4,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,0x17, +4,4,4,5,0,0,0,0,0,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,0,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,0,0x1b,0x1b,0x58b,0x5cb,0x60b,0x64b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1b,0xa8b,0xacb,0xb0b, +0xb4b,0xb8b,0xbcb,0xc0b,0xc4b,0xc8b,0xccb,0xd0b,0xd4b,0xd8b,0xdcb,0xe0b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0xe4b,0xe8b,0xecb, +0xf0b,0xf4b,0xf8b,0xfcb,0x100b,0x104b,0x108b,0x10cb,0x110b,0x114b,0x118b,0x11cb,5,5,5,5, +5,0x685,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,0x5c5, +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,0x685,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,0x705,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,0x585,5,5,0x705,5,5,5,0x7885, +5,0x605,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,0x785,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +0x5c5,5,5,5,5,5,5,5,0x685,5,0x645,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,0x7985,0x7c5,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,0x7845,5,5,5,5, +5,5,5,5,0x605,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,0x685,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +0x1e45,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +0x7985,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,0x7a85,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,0x5c5,5,0x745,5,0x6c5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,0x7c5,5,0x7845,0xa45,0xcc5,5,5,5,5,5,5,0xf45,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,0x605,0x605,0x605,0x605,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,0x645,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,0x585,5,5,5,5,5,5,5,0x585,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,0x585,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,0x785,0xa45,5,5,5,5, +5,5,5,5,5,5,5,5,0x585,0x5c5,0x605,5,0x5c5,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,0x7c5,5, +5,5,5,5,5,5,5,5,5,5,5,5,0x745,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,0x705,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,0x785,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,0x1e45,5, +5,5,5,5,5,5,0x645,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, +0x7885,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,0x5c5,5,5,5,5,0x5c5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,0x5c5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,0x7845,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,0x6c5,5, +5,5,5,5,0x1e45,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, +0x6c5,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,0x545,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,0,0,0,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,4,5,5,5,5,5,5,5,5,5,5,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,4,0x17,0x17,0x17, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,5,5,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,2,1,2,1,2,4,4,6,6, +1,2,1,2,1,2,1,2,1,2,1,2,1,2,5,6, +7,7,7,0x17,6,6,6,6,6,6,6,6,6,6,0x17,4, +5,5,5,5,5,5,0x58a,0x5ca,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0x54a, +6,6,0x17,0x17,0x17,0x17,0x17,0x17,0,0,0,0,0,0,0,0, +0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, +0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,4,4,4,4,4,4,4,4,4, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,4,4,4,1,2,5,4,4,2,5,5,5,5,5, +0x1a,0x1a,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +2,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, +1,2,1,2,4,2,2,2,2,2,2,2,2,1,2,1, +2,1,1,2,1,2,1,2,1,2,1,2,4,0x1a,0x1a,1, +2,1,2,5,1,2,1,2,2,2,1,2,1,2,1,2, +1,2,1,2,1,2,1,1,1,1,1,2,1,1,1,1, +1,2,1,2,1,2,1,2,1,2,1,2,1,1,1,1, +2,1,2,0,0,0,0,0,1,2,0,2,0,2,1,2, +1,2,0,0,0,0,0,0,5,5,6,5,5,5,6,5, +5,5,5,6,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,8,8,6,6,8, +0x1b,0x1b,0x1b,0x1b,6,0,0,0,0x34cb,0x344b,0x3ccb,0x37cb,0x35cb,0x3fcb,0x1b,0x1b, +0x19,0x1b,0,0,0,0,0,0,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,0x17,0x17,0x17,0x17, +0,0,0,0,0,0,0,0,8,8,8,8,6,6,0,0, +0,0,0,0,0,0,0x17,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, +0x249,0x289,0,0,0,0,0,0,8,8,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,8,8,8,8,8,8,8,8, +8,8,8,8,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,5,5,5,5,5,5,0x17,0x17,0x17,5, +0x17,5,5,6,5,5,5,5,5,5,6,6,6,6,6,6, +6,6,0x17,0x17,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6, +6,6,8,8,0,0,0,0,0,0,0,0,0,0,0,0x17, +8,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0,4, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0x17,0x17, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,6,8,8,6,6,6,6,8,8,6,6,8,8, +5,5,5,5,5,6,4,5,5,5,5,5,5,5,5,5, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,5,5,5,5,5,0, +5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,8, +8,6,6,8,8,6,6,0,0,0,0,0,0,0,0,0, +5,5,5,6,5,5,5,5,5,5,5,5,6,8,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0x17,0x17,0x17,0x17, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +4,5,5,5,5,5,5,0x1b,0x1b,0x1b,5,8,6,8,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +6,5,6,6,6,5,5,6,6,5,5,5,5,5,6,6, +5,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,5,5,4,0x17,0x17, +5,5,5,5,5,5,5,5,5,5,5,8,6,6,8,8, +0x17,0x17,5,4,4,8,6,0,0,0,0,0,0,0,0,0, +0,5,5,5,5,5,5,0,0,5,5,5,5,5,5,0, +0,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0, +5,5,5,5,5,5,5,0,5,5,5,5,5,5,5,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,0x1a,4,4,4,4, +2,2,2,2,2,2,2,2,2,4,0x1a,0x1a,0,0,0,0, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +5,5,5,8,8,6,8,8,6,8,8,0x17,8,6,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, +5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0, +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,0,0,0,0, +5,5,5,5,5,5,5,0,0,0,0,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +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,0x12,0x12, +0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, +0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, +5,5,5,5,5,5,5,5,5,5,5,0x605,5,5,5,5, +5,5,5,0x7c5,5,5,5,5,0x5c5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,0x6c5,5,0x6c5, +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,0x7c5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,0x18,5,5,5,5,5,5,5,5,5,5,5,5,5,0, +5,5,5,5,5,0,5,0,5,5,0,5,5,0,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,2,2,2,2,2,2,2,0, +0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2, +0,0,0,0,0,5,6,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, +0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,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,0x15,0x14,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,0,0,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0, +0,0,0,0x1b,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +0x19,0x1b,0x1b,0x1b,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x14,0x15,0x17,0,0, +0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,0x17,0x13,0x13,0x16,0x16,0x14,0x15,0x14,0x15,0x14,0x15,0x14, +0x15,0x14,0x15,0x14,0x15,0x17,0x17,0x14,0x15,0x17,0x17,0x17,0x17,0x16,0x16,0x16, +0x17,0x17,0x17,0,0x17,0x17,0x17,0x17,0x13,0x14,0x15,0x14,0x15,0x14,0x15,0x17, +0x17,0x17,0x18,0x13,0x18,0x18,0x18,0,0x17,0x19,0x17,0x17,0,0,0,0, +5,5,5,5,5,0,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,0,0,0x10,0,0,5,5,5,5,5,5, +0,0,5,5,5,5,5,5,0,0,5,5,5,5,5,5, +0,0,5,5,5,0,0,0,0x19,0x19,0x18,0x1a,0x1b,0x19,0x19,0, +0x1b,0x18,0x18,0x18,0x18,0x1b,0x1b,0,0,0,0,0,0,0,0,0, +0,0x10,0x10,0x10,0x1b,0x1b,0,0,0,0x17,0x17,0x17,0x19,0x17,0x17,0x17, +0x14,0x15,0x17,0x18,0x17,0x13,0x17,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, +0x249,0x289,0x17,0x17,0x18,0x18,0x18,0x17,0x1a,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,0x14,0x18,0x15,0x18,0x14,0x15,0x17,0x14,0x15,0x17,0x17,5,5, +5,5,5,5,5,5,5,5,4,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,4,4,5,5,5,5, +5,5,5,5,5,5,5,5,0,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,0,5,5,0,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,0,0,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,0,0,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,0,0,0,0,0,0xb00b,0xb80b,0x784b,0x804b,0x884b,0x904b,0x984b,0xa04b, +0xa84b,0xb04b,0xb84b,0x788b,0x808b,0x888b,0x908b,0x988b,0xa08b,0xa88b,0xb08b,0xb88b,0,0,0,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x17,0x17,0x17,0,0,0,0,0x58b, +0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b, +0x1bcb,0x1e4b,0x800b,0x880b,0x900b,0x980b,0xa00b,0xa80b,0x7ca,0x7ca,0x7ca,0x7ca,0x7ca,0xcca,0x11ca,0x11ca, +0x11ca,0x11ca,0x1e4a,0x880a,0x980a,0x980a,0x980a,0x980a,0x980a,0x784a,0x984a,0x68a,0x11ca,0x344b,0x344b,0x388b, +0x3ccb,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x54b,0x34cb, +0x1b,0x1b,0x1b,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0,0,0,0x34ca,0x344a,0x58a,0x68a,0x11ca,0x980a,0x984a,0x988a,0x68a,0x7ca,0x11ca,0x1e4a, +0x980a,0x784a,0x984a,0x68a,0x7ca,0x11ca,0x1e4a,0x980a,0x784a,0x788a,0x988a,0x7ca,0x58a,0x58a,0x58a,0x5ca, +0x5ca,0x5ca,0x5ca,0x68a,0x1b,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,6,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,6,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b, +0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1bcb,0x1e4b,0x800b,0x880b,0x900b,0x980b,0xa00b,0xa80b,0xb00b,0xb80b, +0,0,0,0,0x58b,0x68b,0x7cb,0x11cb,0,0,0,0,0,0,0,0, +0,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,0x1bca,5,5,5,5,5,5,5,5,0xb80a,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,0, +0,0,0,0,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,0,0x17,5,5,5,5,0,0,0,0,5,5,5,5, +5,5,5,5,0x17,0x58a,0x5ca,0x7ca,0xa4a,0x1e4a,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,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,0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0, +0,0,0,0,1,1,1,1,1,1,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, +0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0,0,0,0,2,2,2,2, +2,2,2,2,5,5,5,5,5,5,5,5,0,0,0,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0x17, +1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1, +2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,0,2,2,2,2,2,2,2,0,2,2,0,0,0, +1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1, +1,1,1,0,1,1,0,2,2,2,2,2,2,2,2,2, +5,5,5,5,5,5,5,5,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,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +4,0,4,4,4,4,4,4,4,4,4,0,0,0,0,0, +4,4,4,4,4,4,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, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,0,5,5,0,0,0,5,0,0,5, +5,5,5,5,5,5,0,0,5,0,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,0,0x17,0x58b,0x5cb,0x60b,0x7cb,0xa4b,0x1e4b,0x784b,0x788b,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,0x1b,0x1b,0x58b,0x5cb,0x60b,0x64b,0x68b,0x7cb,0xa4b,0,0,0,0, +0,0,0,0x58b,0x5cb,0x60b,0x64b,0x64b,0x68b,0x7cb,0xa4b,0x1e4b,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0, +5,5,0,0,0,0,0,0x58b,0x68b,0x7cb,0xa4b,0x1e4b,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,0x58b,0x7cb,0xa4b,0x1e4b,0x5cb,0x60b,0,0,0,0x17,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,0,0,0,0,0,0x17,0xa04b,0xa84b,0xb04b,0xb84b, +0x788b,0x808b,0x888b,0x908b,0x988b,0xa08b,0xa88b,0xb08b,0xb88b,0x78cb,0x80cb,0x88cb,0x90cb,0x98cb,0xa0cb,0xa8cb, +0xb0cb,0xb8cb,0x36cb,0x354b,0x34cb,0x348b,0x46cb,0x344b,0x4ecb,0x388b,0x3ccb,0x454b,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,0,0,0,0,0x5ecb,0x344b,5,5,0x58b,0x5cb,0x60b,0x64b, +0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0,0,0x1e4b,0x800b, +0x880b,0x900b,0x980b,0xa00b,0xa80b,0xb00b,0xb80b,0x784b,0x804b,0x884b,0x904b,0x984b,0x30b,0x34b,0x38b,0x3cb, +0x7cb,0xa4b,0x1e4b,0x784b,0x344b,0,0,0,0,0,0,0,0x17,0x17,0x17,0x17, +0x17,0x17,0x17,0x17,0x17,0,0,0,0,0,0,0,5,6,6,6, +0,6,6,0,0,0,0,0,6,6,6,6,5,5,5,5, +0,5,5,5,0,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,0,0, +6,6,6,0,0,0,0,6,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,0x58b,0x11cb,0x17,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,0x58b,0x7cb,0xa4b,5,5,5,5,5,6,6,0, +0,0,0,0x58b,0x68b,0x7cb,0xa4b,0x1e4b,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0, +0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5, +0x1b,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,0,0,0,0x17,0x17,0x17, +0x17,0x17,0x17,0x17,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,0,0,0x58b,0x5cb,0x60b,0x64b, +0x7cb,0xa4b,0x1e4b,0x784b,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,0,0,0,0,0,0x58b,0x5cb,0x60b,0x64b, +0x7cb,0xa4b,0x1e4b,0x784b,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,0,0,0,0,0,0,0,0x17,0x17,0x17, +0x17,0,0,0,0,0,0,0,0,0,0,0,0,0x58b,0x5cb,0x60b, +0x64b,0x7cb,0xa4b,0x1e4b,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,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,0,0,0,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,0,0,0,0,0,0,0,0x58b,0x68b, +0x7cb,0x11cb,0x1e4b,0x784b,5,5,5,5,6,6,6,6,0,0,0,0, +0,0,0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0, +0,0,0,0,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb,0xa4b,0xccb, +0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1bcb,0x1e4b,0x800b,0x880b,0x900b,0x980b,0xa00b,0xa80b,0xb00b,0xb80b,0x344b, +0x34cb,0x348b,0x388b,0,5,5,5,5,5,5,5,5,5,5,0,6, +6,0x13,0,0,5,5,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,6,6,6,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,0x58b,0x5cb,0x60b,0x64b,0x68b,0x7cb,0xa4b,0xccb,0x1e4b,0x344b,5, +0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6, +6,6,6,6,6,0x58b,0x7cb,0xa4b,0x1e4b,0x17,0x17,0x17,0x17,0x17,0,0, +0,0,0,0,5,5,6,6,6,6,0x17,0x17,0x17,0x17,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,5,5,5,5,5,0x58b,0x5cb,0x60b,0x64b,0x7cb,0xa4b,0x1e4b, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x144b,0x16cb,0x194b,0x1bcb,0x1e4b,0x784b,0x49,0x89,0xc9,0x109,0x149,0x189, +0x1c9,0x209,0x249,0x289,6,5,5,6,6,5,0,0,0,0,0,0, +0,0,0,6,8,6,8,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,6,6,6,6,6,6,6,0x17,0x17,0x17,0x17,0x17, +0x17,0x17,0,0,0,0,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x7cb, +0xa4b,0xccb,0xf4b,0x11cb,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,8,8,8,6,6,6,6,8,8,6,6,0x17, +0x17,0x10,0x17,0x17,0x17,0x17,6,0,0,0,0,0,0,0,0,0, +0,0x10,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,0,0,0,0,0,0,0,0x49,0x89,0xc9,0x109, +0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0,5,5,5,5, +5,5,5,6,6,6,6,6,8,6,6,6,6,6,6,6, +6,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17,0x17,0x17, +5,8,8,5,0,0,0,0,0,0,0,0,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,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,6, +0x17,0x17,5,0,0,0,0,0,0,0,0,0,8,5,5,5, +5,0x17,0x17,0x17,0x17,6,6,6,6,0x17,8,6,0x49,0x89,0xc9,0x109, +0x149,0x189,0x1c9,0x209,0x249,0x289,5,0x17,5,0x17,0x17,0x17,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,8, +8,8,6,6,6,6,6,6,6,6,6,8,0,0x58b,0x5cb,0x60b, +0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1bcb,0x1e4b, +0x784b,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5, +5,5,5,5,5,5,5,5,8,8,8,6,6,6,8,8, +6,8,6,6,0x17,0x17,0x17,0x17,0x17,0x17,6,5,5,6,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0, +5,0,5,5,5,5,0,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,0,5,5,5,5,5,5,5,5,5, +5,0x17,0,0,0,0,0,0,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,8,8,8,6,6,6,6,6, +6,6,6,0,0,0,0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, +0x249,0x289,0,0,0,0,0,0,5,5,8,8,0,0,6,6, +6,6,6,6,6,0,0,0,6,6,6,6,6,0,0,0, +0,0,0,0,0,0,0,0,6,6,8,8,0,5,5,5, +5,5,5,5,5,0,0,5,5,0,0,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,0,5,5,5,5,5,5, +5,0,5,5,0,5,5,5,5,5,0,6,6,5,8,8, +6,8,8,8,8,0,0,8,8,0,0,8,8,8,0,0, +5,0,0,0,0,0,0,8,0,0,0,0,0,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,8,8,8,6,6,6,6,6,6,6,6, +8,8,6,6,6,8,6,5,5,5,5,0x17,0x17,0x17,0x17,0x17, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x17,0x17,0,0x17,6,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +8,8,8,6,6,6,6,6,6,8,6,8,8,8,8,6, +6,8,6,6,5,5,0x17,5,0,0,0,0,0,0,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,8, +8,8,6,6,6,6,0,0,8,8,8,8,6,6,8,6, +6,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, +0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,5,5,5,5,6,6,0,0, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +8,8,8,6,6,6,6,6,6,6,6,8,8,6,8,6, +6,0x17,0x17,0x17,5,0,0,0,0,0,0,0,0,0,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, +0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5,5,5,5,5,5,5,5,5,5,5,6,8,6,8,8, +6,6,6,6,6,6,8,6,5,0x17,0,0,0,0,0,0, +8,8,6,6,6,6,8,6,6,6,6,6,0,0,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x7cb,0xa4b,0x17,0x17,0x17,0x1b, +5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5,5,5,5,5,5,5,5,5,5,5,5,8,8,8,6, +6,6,6,6,6,6,6,6,8,6,6,0x17,0,0,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b, +0x16cb,0x194b,0x1bcb,0,0,0,0,0,0,0,0,0,0,0,0,5, +8,5,8,6,0x17,0x17,0x17,0,0,0,0,0,0,0,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, +5,5,5,5,5,5,5,0,0,5,0,0,5,5,5,5, +5,5,5,5,0,5,5,0,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,8,8,8,8,8,8,0,8, +8,0,0,6,6,8,6,5,6,5,0x17,5,8,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5, +0,0,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,8,8,8,6,6,6,6, +0,0,6,6,8,8,8,8,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6, +6,8,5,6,6,6,6,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,6, +0,0,0,0,0,0,0,0,5,6,6,6,6,6,6,8, +8,6,6,6,5,5,5,5,5,6,6,6,6,6,6,6, +6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,0x17,0x17,0x17,0,0,0,0,0, +0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6, +6,6,6,6,6,6,6,8,6,6,0x17,0x17,0x17,5,0x17,0x17, +5,0x17,0x17,0x17,0x17,0x17,0,0,0,0,0,0,0,0,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb, +0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1bcb,0x1e4b,0,0,0, +0x17,0x17,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,0,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,8,6,6,6,6, +6,6,6,0,6,6,6,6,6,6,8,6,6,6,6,6, +6,6,6,6,0,8,6,6,6,6,6,6,6,8,6,6, +8,6,6,0,0,0,0,0,0,0,0,0,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,0,0,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,6, +0,0,0,0,0,0,0,0,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, +0x249,0x289,0,0,0,0,0,0,5,5,5,5,5,5,5,0, +5,5,0,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,0, +0,0,6,0,6,6,0,6,5,5,5,5,5,5,5,5, +5,5,8,8,8,8,8,0,6,6,0,8,8,6,8,6, +5,0,0,0,0,0,0,0,5,5,5,5,5,5,0,5, +5,0,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,6,6,8,8,0x17, +0x17,0,0,0,0,0,0,0,6,8,6,0x17,0x17,0x17,0x17,0x17, +0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, +0x249,0x289,0,0,0,0,0,0,6,6,5,8,5,5,5,5, +5,5,5,5,5,5,5,5,5,0,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +8,8,6,6,6,6,6,0,0,0,8,8,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0x19,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0, +0,0,0,0,0,0,0,0,0,0,0,0x17,0xcd0b,0xcc0b,0xcb0b,0xd00b, +0xca0b,0xcf0b,0xcb4b,0xd04b,0xc90b,0x37cb,0x37cb,0x364b,0x35cb,0xc94b,0x3fcb,0x350b,0x34cb,0x344b,0x344b,0x3ccb, +0xcd0b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x19,0x19,0x19,0x34ca,0x354a,0x34ca,0x34ca, +0x344a,0x348a,0x388a,0xf4a,0x11ca,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0,0x17,0x17,0x17,0x17, +0x17,0,0,0,0,0,0,0,0,0,0,0,0x5ca,0x60a,0x64a,0x68a, +0x6ca,0x70a,0x74a,0x78a,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0x64a,0x68a,0x6ca,0x70a,0x74a, +0x78a,0x58a,0x5ca,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0x58a,0x5ca,0x60a,0x64a,0x68a,0x5ca, +0x60a,0x60a,0x64a,0x68a,0x6ca,0x70a,0x74a,0x78a,0x58a,0x5ca,0x60a,0x60a,0x64a,0x68a,0xc08a,0xc18a, +0x58a,0x5ca,0x60a,0x60a,0x64a,0x68a,0x60a,0x60a,0x64a,0x64a,0x64a,0x64a,0x6ca,0x70a,0x70a,0x70a, +0x74a,0x74a,0x78a,0x78a,0x78a,0x78a,0x5ca,0x60a,0x64a,0x68a,0x6ca,0x58a,0x5ca,0x60a,0x64a,0x64a, +0x68a,0x68a,0x5ca,0x60a,0x58a,0x5ca,0x348a,0x388a,0x454a,0x348a,0x388a,0x35ca,5,5,5,5, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,0x17,0x17,0, +0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,0x10,0x10,0x10,0x10, +0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,6,5,5,5, +5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,0,0,0,0,0,0,0,0,0,0,0x49,0x89,0xc9,0x109, +0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0, +6,6,6,6,6,0x17,0,0,0,0,0,0,0,0,0,0, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +6,6,6,6,6,6,6,0x17,0x17,0x17,0x17,0x17,0x1b,0x1b,0x1b,0x1b, +4,4,4,4,0x17,0x1b,0,0,0,0,0,0,0,0,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0x7cb,0x1e4b,0x788b,0x790b,0x798b, +0x7a0b,0x7a8b,0,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,5,0,0,0,0,0,5,5,5, +0x54b,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0x80b,0x84b,0x88b,0x8cb,0x90b, +0x94b,0x98b,0x9cb,0xa0b,0x58b,0x5cb,0x60b,0x17,0x17,0x17,0x17,0,0,0,0,0, +5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,6, +5,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,0,0,0,0,0,0,0,6,6,6,6,4, +4,4,4,4,4,4,4,4,4,4,4,4,4,4,0x17,4, +6,0,0,0,0,0,0,0,0,0,0,0,8,8,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,4,4,4,4,0,4,4,4,4,4,4,4, +0,4,4,0,5,5,5,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,5,5,5,0,0,5,0,0, +0,0,0,0,0,0,0,0,5,5,5,5,0,0,0,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,5,0,0,0,0,0,5,5,5,5, +5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0, +5,5,5,5,5,5,5,5,5,5,0,0,0x1b,6,6,0x17, +0x10,0x10,0x10,0x10,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,0,0,0,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0, +0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,8,8,6,6,6,0x1b,0x1b, +0x1b,8,8,8,8,8,8,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,6, +6,6,6,6,6,6,6,0x1b,0x1b,6,6,6,6,6,6,6, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,6,6,6,6,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,6,6, +6,0x1b,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0x54b,0x58b,0x5cb,0x60b, +0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0x80b,0x84b,0x88b,0x8cb,0x90b,0x94b,0x98b,0x9cb,0xa0b, +0,0,0,0,0,0,0,0,0,0,0,0,0x58b,0x5cb,0x60b,0x64b, +0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b,0x16cb,0x194b,0x1bcb,0x58b,0x5cb, +0x60b,0x64b,0x68b,0x58b,0x68b,0,0,0,0,0,0,0,0x249,0x289,0x49,0x89, +0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209, +0x249,0x289,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,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,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2, +2,0,2,2,2,2,2,2,2,2,2,2,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,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, +1,0,1,1,0,0,1,0,0,1,1,0,0,1,1,1, +1,0,1,1,1,1,1,1,1,1,2,2,2,2,0,2, +0,2,2,2,2,2,2,2,0,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,1,1,2,2,2,2,1,1,0,1,1,1,1,0, +0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1, +1,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,1,1,0,1, +1,1,1,0,1,1,1,1,1,0,1,0,0,0,1,1, +1,1,1,1,1,0,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,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,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,2,2,2,2,2,2,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,0x18,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,0x18, +2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x18, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,0x18,2,2,2,2,2,2,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,0x18, +2,2,2,2,2,2,1,2,0,0,0x49,0x89,0xc9,0x109,0x149,0x189, +0x1c9,0x209,0x249,0x289,0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,0x1b,0x1b,0x1b,0x1b,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,6,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,6,0x1b,0x1b,0x17,0x17,0x17,0x17,0x17, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, +6,6,6,6,6,6,6,0,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,0,0,6,6,6,6,6, +2,2,2,2,2,2,2,2,2,2,5,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,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, +6,6,0,6,6,0,6,6,6,6,6,0,0,0,0,0, +4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, +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,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,5,0x1b, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0, +6,6,6,6,6,6,6,4,4,4,4,4,4,4,0,0, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0x19, +5,5,5,5,5,5,5,5,5,5,5,4,6,6,6,6, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0,0, +5,5,5,5,5,5,5,0,5,5,5,5,0,5,5,0, +5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0, +5,5,5,5,5,0,0,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b, +6,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0, +2,2,2,2,6,6,6,6,6,6,6,4,0,0,0,0, +0x49,0x89,0xc9,0x109,0x149,0x189,0x1c9,0x209,0x249,0x289,0,0,0,0,0x17,0x17, +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, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b, +0x78cb,0x794b,0x814b,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x1b,0x34cb,0x344b,0x3ccb, +0x19,0x58b,0x5cb,0x788b,0x78cb,0,0,0,0,0,0,0,0,0,0,0, +0x16cb,0x194b,0x1bcb,0x1e4b,0x800b,0x880b,0x900b,0x980b,0xa00b,0xa80b,0xb00b,0xb80b,0x784b,0x804b,0x884b,0x904b, +0x984b,0xa04b,0xa84b,0xb04b,0xb84b,0x788b,0x808b,0x888b,0x908b,0x988b,0xa08b,0xa88b,0xb08b,0xb88b,0x78cb,0x80cb, +0x984b,0xa04b,0xa84b,0xb04b,0xb84b,0x788b,0x808b,0x888b,0x908b,0x988b,0xa08b,0xa88b,0xb08b,0xb88b,0x1b,0x5cb, +0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0x900b,0xa00b,0x804b,0x788b,0x344b,0x354b,0,0, +0,0x58b,0x5cb,0x60b,0x64b,0x68b,0x6cb,0x70b,0x74b,0x78b,0x7cb,0xa4b,0xccb,0xf4b,0x11cb,0x144b, +0x16cb,0x194b,0x1bcb,0x1e4b,0x800b,0x880b,0x900b,0x980b,0xa00b,0xa80b,0xb00b,0xb80b,0x784b,0x804b,0x884b,0x904b, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x18,0x18,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +5,5,5,5,0,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, +0,5,5,0,5,0,0,5,0,5,5,5,5,5,5,5, +5,5,5,0,5,5,5,5,0,5,0,5,0,0,0,0, +0,0,5,0,0,0,0,5,0,5,0,5,0,5,5,5, +0,5,5,0,5,0,0,5,0,5,0,5,0,5,0,5, +0,5,5,0,5,0,0,5,5,5,5,0,5,5,5,5, +5,5,5,0,5,5,5,5,0,5,5,5,5,0,5,0, +5,5,5,5,5,5,5,5,5,5,0,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0, +0,5,5,5,0,5,5,5,5,5,0,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x2cb,0x2cb,0x30b,0x34b,0x38b,0x3cb,0x40b,0x44b,0x48b,0x4cb,0x50b,0x54b,0x54b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0, +0x1b,0x1b,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1a,0x1a,0x1a,0x1a,0x1a, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0x1b,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0,0,0,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0, +0x1b,0x1b,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0, +0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0, +0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0,0,0,0,0, +0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0,0,0,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1b,5,0x705,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,0x645,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,0x645,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,0x685,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,0xcc5,5,5,5,5,5,5,5,5,0xf45,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,0xf45,5,5,5, +5,5,5,5,5,5,5,5,5,5,0x6c5,5,5,5,5,5, +5,5,5,5,5,5,5,5,5,5,5,5,5,0x605,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,0x605,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,0x605,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,0x605,5,5,5,5,5,5,5,5,5,5,5,5, +5,0x645,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,5,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,0x785,5,5,5,5,5,5,5,5,5,5,5, +5,5,5,5,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, +0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, +0x10,0x10,0x10,0x10,0,0x10,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, +0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, +0x11,0x11,0,0,0,0,0,0 +}; + +static const UTrie2 propsTrie={ + propsTrie_index, + propsTrie_index+4692, + nullptr, + 4692, + 18324, + 0xa40, + 0x12d4, + 0x0, + 0x0, + 0x110000, + 0x59e4, + nullptr, 0, false, false, 0, nullptr +}; + +static const uint16_t propsVectorsTrie_index[32692]={ +0x539,0x541,0x549,0x551,0x569,0x571,0x579,0x581,0x589,0x591,0x599,0x5a1,0x5a9,0x5b1,0x5b9,0x5c1, +0x5c8,0x5d0,0x5d8,0x5e0,0x5e3,0x5eb,0x5f3,0x5fb,0x603,0x60b,0x613,0x61b,0x623,0x62b,0x633,0x63b, +0x643,0x64b,0x652,0x65a,0x662,0x66a,0x672,0x67a,0x682,0x68a,0x68f,0x697,0x69e,0x6a6,0x6ae,0x6b6, +0x6be,0x6c6,0x6ce,0x6d6,0x6dd,0x6e5,0x6ed,0x6f5,0x6fd,0x705,0x70d,0x715,0x71d,0x725,0x72d,0x735, +0x1b39,0xd8a,0xe56,0x118d,0x12cc,0x1d01,0x1ea0,0x1cf9,0x13e6,0x13f6,0x13de,0x13ee,0x80a,0x810,0x818,0x820, +0x828,0x82e,0x836,0x83e,0x846,0x84c,0x854,0x85c,0x864,0x86a,0x872,0x87a,0x882,0x88a,0x892,0x899, +0x8a1,0x8a7,0x8af,0x8b7,0x8bf,0x8c5,0x8cd,0x8d5,0x8dd,0x13fe,0x8e5,0x8ed,0x8f5,0x8fc,0x904,0x90c, +0x914,0x918,0x920,0x927,0x92f,0x937,0x93f,0x947,0x1719,0x1721,0x94f,0x957,0x95f,0x967,0x96f,0x976, +0x177f,0x176f,0x1777,0x1a74,0x1a7c,0x140e,0x97e,0x1406,0x1662,0x1662,0x1664,0x1422,0x1423,0x1416,0x1418,0x141a, +0x1787,0x1789,0x986,0x1789,0x98e,0x993,0x99b,0x178e,0x9a1,0x1789,0x9a7,0x9af,0xc6a,0x1796,0x1796,0x9b7, +0x17a6,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7,0x17a7, +0x17a7,0x17a7,0x17a7,0x179e,0x9bf,0x17af,0x17af,0x9c7,0xb92,0xb9a,0xba2,0xbaa,0x17bf,0x17b7,0x9cf,0x9d7, +0x9df,0x17c9,0x17d1,0x9e7,0x17c7,0x9ef,0x1b41,0xd92,0xbb2,0xbba,0xbc2,0xbc7,0x19da,0xc91,0xc98,0x1936, +0xc42,0x1b49,0xd9a,0xda2,0xdaa,0xdb2,0xf60,0xf64,0x1a3a,0x1a3f,0xcd0,0xcd8,0x1ab0,0x1ab8,0x1c19,0xe5e, +0x1ac0,0xd1e,0xd26,0x1ac8,0x1105,0x11b5,0xf38,0xdba,0x1956,0x193e,0x194e,0x1946,0x19f2,0x19ea,0x19a6,0x1a32, +0x142b,0x142b,0x142b,0x142b,0x142e,0x142b,0x142b,0x1436,0x9f7,0x143e,0x9fb,0xa03,0x143e,0xa0b,0xa13,0xa1b, +0x144e,0x1446,0x1456,0xa23,0xa2b,0x145e,0xa33,0xa3b,0x1466,0x146e,0x1476,0x147e,0xa43,0x1486,0x148d,0x1495, +0x149d,0x14a5,0x14ad,0x14b5,0x14bd,0x14c4,0x14cc,0x14d4,0x14dc,0x14e4,0x14e7,0x14e9,0x17d9,0x18cc,0x18d2,0x1a22, +0x14f1,0xa4b,0xa53,0x1617,0x161c,0x161f,0x1625,0x14f9,0x162d,0x162d,0x1509,0x1501,0x1511,0x1519,0x1521,0x1529, +0x1531,0x1539,0x1541,0x1549,0x18da,0x192e,0x1a84,0x1be1,0x1559,0x155f,0x1567,0x156f,0x1551,0x1577,0x18e2,0x18e9, +0x17e1,0x17e1,0x17e1,0x17e1,0x17e1,0x17e1,0x17e1,0x17e1,0x18f1,0x18f1,0x18f1,0x18f1,0x18f9,0x1900,0x1902,0x1909, +0x1911,0x1915,0x1915,0x1918,0x1915,0x1915,0x191e,0x1915,0x195e,0x1a2a,0x1a8c,0xbcf,0xbd5,0x1d45,0x1d4d,0x1e2b, +0x19ca,0x19be,0x19c2,0x1a47,0x19ae,0x19ae,0x19ae,0xc52,0x19b6,0xc72,0x1a0a,0xcc0,0xc5a,0xc62,0xc62,0x1ad0, +0x19fa,0x1a94,0xca8,0xcb0,0xa5b,0x17e9,0x17e9,0xa63,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0xa6b,0x73d, +0x164a,0x166c,0xa73,0x1674,0xa7b,0x167c,0x1684,0x168c,0xa83,0xa88,0x1694,0x169b,0xa8d,0x17f9,0x1a1a,0xc4a, +0xa95,0x16f6,0x16fd,0x16a3,0x1705,0x1709,0x16ab,0x16af,0x16c8,0x16c8,0x16ca,0x16b7,0x16bf,0x16bf,0x16c0,0x1711, +0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, +0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, +0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, +0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, +0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, +0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, +0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, +0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, +0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, +0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, +0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, +0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801, +0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1801,0x1804,0x1966,0x1966, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2, +0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d2,0x16d9,0x1b31,0x1f0c, +0x180c,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812, +0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812,0x1812, +0x1812,0x1812,0x1812,0x1812,0xa9d,0x181a,0xaa5,0x1b51,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc, +0x1ad8,0xd2e,0x1aec,0x1ae4,0x1aee,0x1b59,0x1b59,0xdc2,0x19d2,0x1a4f,0x1aa4,0x1aa8,0x1a9c,0x1c11,0xce0,0xce7, +0x1a02,0xcb8,0x1a57,0xcef,0x1af6,0x1af9,0xd36,0x1b61,0x1b09,0x1b01,0xd3e,0xdca,0x1b69,0x1b6d,0xdd2,0x100f, +0x1b11,0xd46,0xd4e,0x1b75,0x1b85,0x1b7d,0xdda,0xf08,0xe66,0xe6e,0x1d9b,0xfbf,0x1e48,0x1e48,0x1b8d,0xde2, +0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762, +0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764, +0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766, +0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761, +0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763, +0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765, +0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767, +0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762, +0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764, +0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766, +0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761, +0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763, +0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765, +0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767, +0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762, +0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764, +0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766, +0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761, +0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763, +0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765, +0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767, +0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0xaad,0xdea,0xded, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739, +0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739,0x1739, +0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, +0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, +0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, +0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, +0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, +0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, +0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, +0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, +0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, +0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, +0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, +0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635, +0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x16e1,0x16e1,0x16e1,0x16e1,0x16e1,0x16e1,0x16e1,0x16e1, +0x16e6,0x16ee,0x1926,0x13a3,0x1a12,0x1a12,0x13a7,0x13ae,0xab5,0xabd,0xac5,0x1597,0x159e,0x15a6,0xacd,0x15ae, +0x15ec,0x15ec,0x157f,0x1587,0x15b6,0x15e3,0x15e4,0x15f4,0x15be,0x15c3,0x15cb,0x15d3,0xad5,0x15db,0xadd,0x158f, +0xcc8,0x15fc,0xae5,0xaed,0x1604,0x160a,0x160f,0xaf5,0xb05,0x1652,0x165a,0x163d,0x1642,0xb0d,0xb15,0xafd, +0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729, +0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1729,0x1731,0x1731,0x1731,0x1731, +0x1564,0x1564,0x15a4,0x15e4,0x1624,0x1664,0x16a4,0x16e4,0x1720,0x1760,0x178c,0x17cc,0x180c,0x184c,0x188c,0x18cc, +0x190c,0x1948,0x1988,0x19c8,0x1a08,0x1a3c,0x1a78,0x1ab8,0x1af8,0x1b38,0x1b74,0x1bb4,0x1bf4,0x1c34,0x1c74,0x1cb4, +0xe59,0xa80,0xac0,0xb00,0xb40,0xb6b,0xf99,0xa40,0xed9,0xa40,0xa40,0xa40,0xa40,0xbab,0x13e2,0x13e2, +0xf19,0xfd9,0xa40,0xa40,0xa40,0xbeb,0xf59,0xc2b,0xa40,0xc51,0xc91,0xcd1,0xd11,0xd51,0xe99,0xdc9, +0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322, +0x1322,0x1322,0x1322,0x1322,0x1019,0x1362,0x1157,0x1197,0x13a2,0x11a2,0x1422,0x1422,0x1422,0x1059,0x1079,0x10b9, +0x1462,0x1462,0x11e2,0x14a2,0x10f9,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079, +0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1117, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, +0xe09,0xe19,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40, +0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xa40,0xd89, +0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2, +0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x12a2,0x1222, +0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2, +0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x12e2,0x1262, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0xc27,0xc2a,0xdf5,0x1deb,0x1017,0x745,0x559,0x10b1,0xcf7,0xd76,0x559,0x559,0x1d11,0xf10,0xf18,0x1e33, +0xc7a,0xc81,0xc89,0x1b95,0x1dcb,0x559,0x1dab,0xfe7,0x1b9d,0xdfd,0xe05,0xe0d,0x103f,0x74d,0x559,0x559, +0x1ba5,0x1ba5,0x755,0x559,0x1e60,0x10c9,0x1e58,0x10d1,0x1f4c,0x11cb,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0xe15,0x1fa4,0x12c4,0x1346,0x1347,0x1f6c,0x11f3,0x11fa,0x1201,0x1303,0x1307,0x127b,0x1211, +0x1c21,0x1c23,0xe76,0xe7d,0x1bad,0x1bb5,0xe1d,0xf30,0x1d09,0xef8,0xf00,0xfdf,0x1d29,0x1d2d,0x1d35,0x105f, +0xfaf,0x1d8b,0x75d,0x559,0x10b9,0x10c1,0x1d93,0xfb7,0xf91,0xf97,0xf9f,0xfa7,0x559,0x559,0x559,0x559, +0x1ed0,0x1ec8,0x113b,0x1143,0x1e13,0x1e0b,0x1087,0x559,0x559,0x559,0x559,0x559,0x1dfb,0x1047,0x104f,0x1057, +0x1dc3,0x1dbb,0xff7,0x1133,0x1d3d,0xf40,0x765,0x559,0x1097,0x109f,0x76d,0x559,0x559,0x559,0x559,0x559, +0x1f44,0x11ad,0x775,0x559,0x559,0x1e23,0x1e1b,0x108f,0x1283,0x1289,0x1291,0x559,0x559,0x1219,0x121d,0x1225, +0x1f04,0x1efc,0x1195,0x1ef4,0x1eec,0x1185,0x1df3,0x1037,0x1357,0x135a,0x135a,0x559,0x559,0x559,0x559,0x559, +0x10e9,0x10ee,0x10f6,0x10fd,0x1125,0x112b,0x559,0x559,0x1169,0x116d,0x1175,0x11bd,0x11c3,0x77d,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x11db,0x136a,0x136f,0x1377,0x559,0x559,0x781,0x1f8c,0x126b, +0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f, +0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a5f,0x1a64,0xcff,0xd06,0xd06,0xd06, +0x1a6c,0x1a6c,0x1a6c,0xd0e,0x1e50,0x1e50,0x1e50,0x1e50,0x1e50,0x1e50,0x789,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x78d,0x1fbc,0x1fbc,0x12d4,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b, +0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0x1c2b,0xe85,0xfff,0x1007,0x1fc4, +0x130f,0x1317,0xf48,0x1de3,0x1ddb,0x1027,0x102f,0x795,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1f64,0x1f5c,0x11eb, +0x559,0x559,0x559,0x1d21,0x1d21,0xf20,0x1d19,0xf28,0x559,0x559,0x111d,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x799,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1d73,0x1d73,0x1d73,0xf6c,0xf71, +0x7a1,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1fd4,0x1337,0x133e,0x1fcc,0x1fcc,0x1fcc,0x7a9, +0x559,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0xb2b,0x184f,0xb33,0x1850,0x1847,0x1858,0x185e,0x1866, +0xb3b,0x198e,0x198e,0x7b1,0x559,0x559,0x559,0x1362,0x11e3,0x197e,0x197e,0xc32,0xd16,0x559,0x559,0x559, +0x559,0x1897,0x189e,0xb43,0x18a1,0xb4b,0xb53,0xb5b,0x189b,0xb63,0xb6b,0xb73,0x18a0,0x18a8,0x1897,0x189e, +0x189a,0x18a1,0x18a9,0x1898,0x189f,0x189b,0xb7a,0x186e,0x1876,0x187d,0x1884,0x1871,0x1879,0x1880,0x1887,0xb82, +0x188f,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78,0x1e78, +0x1e78,0x1e68,0x1e6b,0x1e68,0x1e72,0x10d9,0x7b9,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x12f0,0x12f8,0x12fb,0x12fb,0x12fb,0x12fb,0x12fb, +0x12fb,0x110d,0x1115,0x1fdc,0x134f,0x7c1,0x559,0x559,0x559,0x1f84,0x122d,0x7c9,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x7cd,0x131f,0x1f94,0x1273,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x7d5,0x137f,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x12dc,0x1db3,0x1db3,0x1db3,0x1db3,0x1db3,0x1db3,0xfef,0x559,0x1ec0,0x1eb8,0x10e1,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x7dd,0x1f54,0x11d3,0x559,0x559,0x1235,0x1236,0x7e5,0x559,0x559,0x559,0x559, +0x559,0xebd,0xec5,0xecd,0xed5,0xedd,0xee5,0xeec,0xef0,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x7e9,0x1067,0x1e03,0x106d,0x1e03,0x1075,0x107a,0x107f, +0x107f,0x1e88,0x1ea8,0x1eb0,0x1f1c,0x1e90,0x1f74,0x1e98,0x1f24,0x1f7c,0x1f7c,0x119d,0x11a5,0x124d,0x1253,0x125b, +0x1263,0x1f9c,0x1f9c,0x1f9c,0x1f9c,0x12a7,0x1f9c,0x12ad,0x12b1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1, +0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1, +0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f1,0x7f2,0xb8a,0x18b1,0x18b1,0x18b1,0x7fa,0x7fa,0x7fa, +0x7fa,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x802,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa, +0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa, +0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa, +0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa, +0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0x7fa,0xbdd,0xbe4,0xbec,0xbf4,0x196e,0x196e,0x196e, +0xbfc,0xc04,0xc07,0x199e,0x1996,0xc3a,0xd56,0xd5a,0xd5e,0x559,0x559,0x559,0x559,0xd66,0x1b19,0xd6e, +0xf58,0x1822,0xb1d,0xb23,0x101f,0xc0f,0x19e2,0xca0,0x559,0x1837,0x182a,0x182f,0x1976,0xc17,0xc1f,0x114b, +0x1151,0x1d7b,0xf79,0x1d6b,0xf50,0x1327,0x132f,0x559,0x559,0x1da3,0x1da3,0x1da3,0x1da3,0x1da3,0x1da3,0x1da3, +0x1da3,0x1da3,0xfc7,0xfcf,0xfd7,0x12e4,0x12e8,0x559,0x559,0x1b21,0xd7e,0x1b29,0x1b29,0xd82,0xe8d,0xe95, +0xe9d,0x1bf1,0x1bd9,0x1bf9,0x1c01,0x1be9,0xe25,0xe29,0xe30,0xe38,0xe3c,0xe44,0xe4c,0xe4e,0xe4e,0xe4e, +0xe4e,0x1c62,0x1c6a,0x1c62,0x1c70,0x1c78,0x1c43,0x1c80,0x1c88,0x1c62,0x1c90,0x1c98,0x1c9f,0x1ca7,0x1c4b,0x1c62, +0x1cac,0x1c53,0x1c5a,0x1cb4,0x1cba,0x1d5c,0x1d63,0x1d55,0x1cc1,0x1cc9,0x1cd1,0x1cd9,0x1dd3,0x1ce1,0x1ce9,0xea5, +0xead,0x1c33,0x1c33,0x1c33,0xeb5,0x1d83,0x1d83,0xf81,0xf89,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e3b, +0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e3b,0x1e40,0x1e3b,0x1e3b,0x1e3b,0x10a7,0x10a9,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, +0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, +0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, +0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, +0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1159,0x1c3b,0x1f14,0x1f14,0x1f14,0x1f14,0x1f14,0x1f14, +0x1f14,0x1f34,0x1161,0x123e,0x1245,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c,0x1f3c, +0x117d,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd, +0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbf,0x1bbd,0x1bc7,0x1bbd,0x1bbd, +0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bca,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1bd1,0x1209,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0, +0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0,0x1ee0, +0x1ee4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1fb4,0x1299, +0x129f,0x12b9,0x12bc,0x12bc,0x12bc,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559, +0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x559,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, +0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, +0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, +0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18bc, +0x1387,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, +0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, +0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x138f,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, +0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, +0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, +0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, +0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4, +0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x18c4,0x13b6,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, +0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, +0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, +0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1393,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, +0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, +0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x139b,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, +0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, +0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, +0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387, +0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1393,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, +0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, +0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, +0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, +0x13be,0x1cf1,0x1cf1,0x1cf1,0x1cf1,0x1cf1,0x1cf1,0x13c6,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, +0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, +0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, +0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, +0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x13ce,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, +0x1f2c,0x1f2c,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac, +0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x13d6,0x1fe4,0x1fe4,0x1fe4, +0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, +0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, +0x1fe4,0x1fe4,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, +0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, +0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, +0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, +0x1751,0x1741,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, +0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, +0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, +0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, +0x1759,0x1749,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, +0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, +0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, +0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751,0x1751, +0x1751,0x1751,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, +0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, +0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, +0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759,0x1759, +0x1759,0x1759,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, +0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, +0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, +0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9,0x18b9, +0x18b9,0x18b9,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, +0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, +0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, +0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09,0x1c09, +0x1c09,0x1c09,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, +0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, +0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, +0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80,0x1e80, +0x1e80,0x1e80,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, +0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, +0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, +0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8,0x1ed8, +0x1ed8,0x1ed8,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, +0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, +0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, +0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c,0x1f2c, +0x1f2c,0x1f2c,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac, +0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac, +0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac, +0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac,0x1fac, +0x1fac,0x1fac,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, +0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, +0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, +0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4,0x1fe4, +0x1fe4,0x1fe4,0x538,0x538,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e5,0x2ee,0x2e8, +0x2e8,0x2eb,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2,0x2e2, +0x2e2,0x2e2,0x2e2,0x2e2,0x7da,0x7d4,0x7b9,0x79e,0x7aa,0x7a7,0x79e,0x7b6,0x7a4,0x7b0,0x79e,0x7cb, +0x7c2,0x7b3,0x7d7,0x7ad,0x79b,0x79b,0x79b,0x79b,0x79b,0x79b,0x79b,0x79b,0x79b,0x79b,0x7bf,0x7bc, +0x7c5,0x7c5,0x7c5,0x7d4,0x79e,0x7e6,0x7e6,0x7e6,0x7e6,0x7e6,0x7e6,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0, +0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7e0,0x7a4, +0x7aa,0x7b0,0x7d1,0x798,0x7ce,0x7e3,0x7e3,0x7e3,0x7e3,0x7e3,0x7e3,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd, +0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7dd,0x7a4, +0x7c8,0x7a1,0x7c5,0x2e2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x300,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1, +0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1,0x2f1, +0x2f1,0x2f1,0x2f1,0x2f1,0x2f4,0x64b,0x7ef,0x7f2,0x651,0x7f2,0x7ec,0x645,0x63c,0x2fa,0x65a,0x2fd, +0x7f5,0x633,0x648,0x7e9,0x64e,0x657,0x639,0x639,0x63f,0x2f7,0x645,0x642,0x63c,0x639,0x65a,0x2fd, +0x636,0x636,0x636,0x64b,0x306,0x306,0x306,0x306,0x306,0x306,0x663,0x306,0x306,0x306,0x306,0x306, +0x306,0x306,0x306,0x306,0x663,0x306,0x306,0x306,0x306,0x306,0x306,0x654,0x663,0x306,0x306,0x306, +0x306,0x306,0x663,0x65d,0x660,0x660,0x303,0x303,0x303,0x303,0x65d,0x303,0x660,0x660,0x660,0x303, +0x660,0x660,0x303,0x303,0x65d,0x303,0x660,0x660,0x303,0x303,0x303,0x654,0x65d,0x660,0x660,0x303, +0x660,0x303,0x65d,0x303,0x312,0x669,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309, +0x312,0x309,0x312,0x309,0x30f,0x666,0x312,0x669,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x669, +0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x66f,0x666,0x312,0x309,0x312,0x669, +0x312,0x309,0x312,0x309,0x312,0x666,0x672,0x66c,0x312,0x309,0x312,0x309,0x666,0x312,0x309,0x312, +0x309,0x312,0x309,0x672,0x66c,0x66f,0x666,0x312,0x669,0x312,0x309,0x312,0x669,0x675,0x66f,0x666, +0x312,0x669,0x312,0x309,0x312,0x309,0x66f,0x666,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309, +0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x66f,0x666,0x312,0x309,0x312,0x669, +0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x309,0x312,0x312,0x309,0x312, +0x309,0x312,0x309,0x30c,0x315,0x321,0x321,0x315,0x321,0x315,0x321,0x321,0x315,0x321,0x321,0x321, +0x315,0x315,0x321,0x321,0x321,0x321,0x315,0x321,0x321,0x315,0x321,0x321,0x321,0x315,0x315,0x315, +0x321,0x321,0x315,0x321,0x324,0x318,0x321,0x315,0x321,0x315,0x321,0x321,0x315,0x321,0x315,0x315, +0x321,0x315,0x321,0x324,0x318,0x321,0x321,0x321,0x315,0x321,0x315,0x321,0x321,0x315,0x315,0x31e, +0x321,0x315,0x315,0x315,0x31e,0x31e,0x31e,0x31e,0x327,0x327,0x31b,0x327,0x327,0x31b,0x327,0x327, +0x31b,0x324,0x678,0x324,0x678,0x324,0x678,0x324,0x678,0x324,0x678,0x324,0x678,0x324,0x678,0x324, +0x678,0x315,0x324,0x318,0x324,0x318,0x324,0x318,0x321,0x315,0x324,0x318,0x324,0x318,0x324,0x318, +0x324,0x318,0x324,0x318,0x318,0x327,0x327,0x31b,0x324,0x318,0x9cf,0x9cf,0x9d2,0x9cc,0x324,0x318, +0x324,0x318,0x324,0x318,0x324,0x318,0x324,0x318,0x324,0x318,0x324,0x318,0x324,0x318,0x324,0x318, +0x324,0x318,0x324,0x318,0x324,0x318,0x324,0x318,0x9d2,0x9cc,0x9d2,0x9cc,0x9cf,0x9c9,0x9d2,0x9cc, +0xb8b,0xc84,0x9cf,0x9c9,0x9cf,0x9c9,0x9d2,0x9cc,0x9d2,0x9cc,0x9d2,0x9cc,0x9d2,0x9cc,0x9d2,0x9cc, +0x9d2,0x9cc,0x9d2,0x9cc,0xc84,0xc84,0xc84,0xd7d,0xd7d,0xd7d,0xd80,0xd80,0xd7d,0xd80,0xd80,0xd7d, +0xd7d,0xd80,0xebe,0xec1,0xec1,0xec1,0xec1,0xebe,0xec1,0xebe,0xec1,0xebe,0xec1,0xebe,0xec1,0xebe, +0x32a,0x67b,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a, +0x32a,0x67b,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a, +0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a, +0x32d,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a,0x32a, +0x32a,0x32a,0x32a,0x32a,0x32a,0x9d5,0x9d5,0x9d5,0x9d5,0x9d5,0xc87,0xc87,0x342,0x342,0x342,0x342, +0x342,0x342,0x342,0x342,0x342,0x339,0x339,0x339,0x339,0x339,0x339,0x339,0x336,0x336,0x333,0x333, +0x681,0x333,0x339,0x684,0x33c,0x684,0x684,0x684,0x33c,0x684,0x339,0x339,0x687,0x33f,0x333,0x333, +0x333,0x333,0x333,0x333,0x67e,0x67e,0x67e,0x67e,0x330,0x67e,0x333,0xb01,0x342,0x342,0x342,0x342, +0x342,0x333,0x333,0x333,0x333,0x333,0x9de,0x9de,0x9db,0x9d8,0x9db,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a, +0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0xc8a,0x68a,0x68a,0x68a,0x68a, +0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a, +0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a, +0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a, +0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68a,0x68d,0x68d,0x92d,0x68d, +0x68d,0x930,0xb04,0xb04,0xb04,0xb04,0xb04,0xb04,0xb04,0xb04,0xb04,0xc3c,0xd47,0xd47,0xd47,0xd47, +0xd47,0xd47,0xd47,0xd47,0xe82,0xe82,0xe82,0xe82,0xe85,0xd4a,0xd4a,0xd4a,0x690,0x690,0xb07,0xc81, +0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xc81,0xf6c,0xf69,0xf6c,0xf69, +0x34e,0x357,0xf6c,0xf69,9,9,0x35d,0xec4,0xec4,0xec4,0x345,0x14af,9,9,9,9, +0x35a,0x348,0x36c,0x34b,0x36c,0x36c,0x36c,9,0x36c,9,0x36c,0x36c,0x363,0x696,0x696,0x696, +0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,0x696,9,0x696, +0x696,0x696,0x696,0x696,0x696,0x696,0x36c,0x36c,0x363,0x363,0x363,0x363,0x363,0x693,0x693,0x693, +0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x693,0x360,0x693, +0x693,0x693,0x693,0x693,0x693,0x693,0x363,0x363,0x363,0x363,0x363,0xf6c,0x36f,0x36f,0x372,0x36c, +0x36c,0x36f,0x366,0x9e1,0xb94,0xb91,0x369,0x9e1,0x369,0x9e1,0x369,0x9e1,0x369,0x9e1,0x354,0x351, +0x354,0x351,0x354,0x351,0x354,0x351,0x354,0x351,0x354,0x351,0x354,0x351,0x36f,0x36f,0x366,0x360, +0xb43,0xb40,0xb8e,0xc90,0xc8d,0xc93,0xc90,0xc8d,0xd83,0xd86,0xd86,0xd86,0x9f0,0x6a2,0x37e,0x381, +0x37e,0x37e,0x37e,0x381,0x37e,0x37e,0x37e,0x37e,0x381,0x9f0,0x381,0x37e,0x69f,0x69f,0x69f,0x69f, +0x69f,0x69f,0x69f,0x69f,0x69f,0x6a2,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f, +0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x69f,0x699,0x699,0x699,0x699, +0x699,0x699,0x699,0x699,0x699,0x69c,0x699,0x699,0x699,0x699,0x699,0x699,0x699,0x699,0x699,0x699, +0x699,0x699,0x699,0x699,0x699,0x699,0x699,0x699,0x9ea,0x69c,0x378,0x37b,0x378,0x378,0x378,0x37b, +0x378,0x378,0x378,0x378,0x37b,0x9ea,0x37b,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378, +0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x381,0x37b, +0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x375,0x939,0x93c,0x91e,0x91e,0x1116, +0x9e4,0x9e4,0xb9a,0xb97,0x9ed,0x9e7,0x9ed,0x9e7,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378, +0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378, +0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378, +0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x378,0x37e,0x381,0x37b,0x37e,0x378,0xb9a,0xb97,0x37e, +0x378,0xb9a,0xb97,0x37e,0x378,0xb9a,0xb97,0xec7,0x381,0x37b,0x381,0x37b,0x37e,0x378,0x381,0x37b, +0x37e,0x378,0x381,0x37b,0x381,0x37b,0x381,0x37b,0x37e,0x378,0x381,0x37b,0x381,0x37b,0x381,0x37b, +0x37e,0x378,0x381,0x37b,0x9f0,0x9ea,0x381,0x37b,0x381,0x37b,0x381,0x37b,0x381,0x37b,0xd8c,0xd89, +0x381,0x37b,0xeca,0xec7,0xeca,0xec7,0xeca,0xec7,0xc00,0xbfd,0xc00,0xbfd,0xc00,0xbfd,0xc00,0xbfd, +0xc00,0xbfd,0xc00,0xbfd,0xc00,0xbfd,0xc00,0xbfd,0xef7,0xef4,0xef7,0xef4,0xfe7,0xfe4,0xfe7,0xfe4, +0xfe7,0xfe4,0xfe7,0xfe4,0xfe7,0xfe4,0xfe7,0xfe4,0xfe7,0xfe4,0xfe7,0xfe4,0x114f,0x114c,0x1329,0x1326, +0x14e5,0x14e2,0x14e5,0x14e2,0x14e5,0x14e2,0x14e5,0x14e2,0xc,0x393,0x393,0x393,0x393,0x393,0x393,0x393, +0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393,0x393, +0x393,0x393,0x393,0xc,0xc,0x396,0x384,0x384,0x384,0x38a,0x384,0x387,0x18ea,0x38d,0x38d,0x38d, +0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d, +0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x38d,0x390, +0x18ea,0x399,0x9f3,0xc,0xc,0x14b2,0x14b2,0x13ce,0xf,0x960,0x960,0x960,0x960,0x960,0x960,0x960, +0x960,0x960,0x960,0x960,0x960,0x960,0x960,0x960,0x960,0x960,0xd8f,0x960,0x960,0x960,0x960,0x960, +0x960,0x960,0x960,0x960,0x960,0x960,0x960,0x960,0x39c,0x39c,0x39c,0x39c,0x39c,0x39c,0x39c,0x39c, +0x39c,0x39c,0xecd,0x39c,0x39c,0x39c,0x3a8,0x39c,0x39f,0x39c,0x39c,0x3ab,0x963,0xd92,0xd95,0xd92, +0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae, +0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae,0x3ae, +0x3ae,0x3ae,0x3ae,0xf,0xf,0xf,0xf,0x18ed,0x3ae,0x3ae,0x3ae,0x3a5,0x3a2,0xf,0xf,0xf, +0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xca8,0xca8,0xca8,0xca8,0x13d1,0x14b5,0xf75,0xf75, +0xf75,0xf72,0xf72,0xd9b,0x8a6,0xca2,0xc9f,0xc9f,0xc96,0xc96,0xc96,0xc96,0xc96,0xc96,0xf6f,0xf6f, +0xf6f,0xf6f,0xf6f,0x8a3,0x14ac,0x1afd,0xd9e,0x8a9,0x12f0,0x3c9,0x3cc,0x3cc,0x3cc,0x3cc,0x3cc,0x3c9, +0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9, +0x3c9,0x3c9,0x3c9,0xf78,0xf78,0xf78,0xf78,0xf78,0x8ac,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9, +0x3c9,0x3c9,0x3c9,0x924,0x924,0x924,0x924,0x924,0x924,0x924,0x924,0xb3a,0xb3a,0xb3a,0xc96,0xc9c, +0xc99,0xd98,0xd98,0xd98,0xd98,0xd98,0xd98,0x12ed,0x93f,0x93f,0x93f,0x93f,0x93f,0x93f,0x93f,0x93f, +0x93f,0x93f,0x3c3,0x3c0,0x3bd,0x3ba,0xb9d,0xb9d,0x921,0x3c9,0x3c9,0x3d5,0x3c9,0x3cf,0x3cf,0x3cf, +0x3cf,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9, +0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9, +0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9, +0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x9f9,0x9f9,0x3c9,0x3c9, +0x3c9,0x3c9,0x3c9,0x9f9,0x3cc,0x3c9,0x3cc,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9,0x3c9, +0x3c9,0x3c9,0x3c9,0x9f9,0x3c9,0x3c9,0x3c9,0x3cc,0x942,0x3c9,0x3b4,0x3b4,0x3b4,0x3b4,0x3b4,0x3b4, +0x3b4,0x3b1,0x3ba,0x3b7,0x3b7,0x3b4,0x3b4,0x3b4,0x3b4,0x3d2,0x3d2,0x3b4,0x3b4,0x3ba,0x3b7,0x3b7, +0x3b7,0x3b4,0xca5,0xca5,0x3c6,0x3c6,0x3c6,0x3c6,0x3c6,0x3c6,0x3c6,0x3c6,0x3c6,0x3c6,0x9f9,0x9f9, +0x9f9,0x9f6,0x9f6,0xca5,0xa0e,0xa0e,0xa0e,0xa08,0xa08,0xa08,0xa08,0xa08,0xa08,0xa08,0xa08,0xa05, +0xa08,0xa05,0x12,0xa11,0xa0b,0x9fc,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b, +0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b,0xa0b, +0xa0b,0xcab,0xcab,0xcab,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02,0xa02, +0xa02,0xa02,0xa02,0xa02,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x9ff,0x12, +0x12,0xcab,0xcab,0xcab,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb, +0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb,0xdfb, +0xdfb,0xdfb,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9,0xff9, +0xff9,0xff9,0xff9,0xff9,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17, +0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17, +0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa17,0xa14,0xa14,0xa14,0xa14,0xa14,0xa14, +0xa14,0xa14,0xa14,0xa14,0xa14,0xba0,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, +0x15,0x15,0x15,0x15,0xf0f,0xf0f,0xf0f,0xf0f,0xf0f,0xf0f,0xf0f,0xf0f,0xf0f,0xf0f,0xf12,0xf12, +0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12, +0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf12,0xf06, +0xf06,0xf06,0xf06,0xf06,0xf06,0xf06,0xf06,0xf06,0xf15,0xf15,0xf09,0xf09,0xf0c,0xf1b,0xf18,0x102, +0x102,0x1911,0x1914,0x1914,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0xb13,0xb13,0xb16,0xb16,0xb13,0xb13,0xb13,0xb13,0xb13,0xb13,0xb13,0xb13, +0x6f,0x6f,0x6f,0x6f,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1617,0x1617,0x1617,0x1617,0x1617, +0x1617,0x1617,0x1617,0x1617,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1ec,0x1ec,0x1ec,0x1ec,0x1ec,0x1ec,0x1ec,0x1ec,0x1ec,0x1650,0x1650,0x1650, +0x1650,0x1650,0x1650,0x1650,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x16b,0x16b,0x16b, +0x16b,0x16b,0x16b,0x16b,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1bdb,0x1bd8,0x1c2,0x1c2,0x1c2,0x1c2,0x1c2,0x1c2,0x1c2,0x1c2,0x1c2,0x1c2, +0x1c2,0x1c2,0x1c2,0x1c2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1488,0x1488,0x1488,0x1488,0x1488,0x1488,0x1488,0x1488,0x1488,0x1488,0x1a7,0x1a7, +0x1a7,0x1a7,0x1a7,0x1a7,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1b5a,0x1b5a,0x1b5a,0x1b5a,0x1b5a,0x1b5a,0x1b5a,0x201,0x201,0x201,0x201,0x201, +0x201,0x201,0x201,0x201,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x249,0x249,0x249,0x249,0x249,0x249,0x249,0x249,0x249,0x249,0x249,0x249, +0x249,0x249,0x249,0x249,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x195f,0x195f,0x195f,0x195f,0x195f,0x195f,0x195f,0x195f,0x195f,0x195f,0x24f,0x24f, +0x24f,0x24f,0x24f,0x24f,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1ac1,0x28b,0x28b,0x28b,0x28b,0x28b,0x28b,0x28b,0x28b,0x28b,0x28b,0x28b, +0x28b,0x28b,0x28b,0x28b,0x1752,0x1752,0x1752,0x1752,0x207,0x207,0x207,0x207,0x207,0x207,0x207,0x207, +0x207,0x207,0x207,0x207,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c, +0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e, +0x163e,0x163e,0x163e,0x163e,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1bc3,0x1bc3,0x1bc3,0x1bc3,0x2a0,0x1bc3,0x1bc3,0x1bc3,0x1bc3,0x1bc3,0x1bc3,0x1bc3, +0x2a0,0x1bc3,0x1bc3,0x2a0,0x16b6,0x16b6,0x16b6,0x16b6,0x1ef,0x1ef,0x1ef,0x1ef,0x1ef,0x1ef,0x1ef,0x1ef, +0x1ef,0x1ef,0x1ef,0x1ef,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5, +0x2b5,0x2b5,0x2b5,0x2b5,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0xdf2,0xdf2,0xdef,0xdef,0xdef,0xdf2,0xd5,0xd5,0xd5,0xd5,0xd5,0xd5, +0xd5,0xd5,0xd5,0xd5,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x213,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a, +0x176a,0x176a,0x176a,0x176a,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb, +0x2bb,0x2bb,0x2bb,0x1bf6,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1a13,0x1a13,0x1a13,0x1a13,0x1a13,0x1a13,0x1a13,0x1a13,0x1a13,0x1a13,0x270,0x270, +0x270,0x270,0x1a16,0x1a10,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93, +0x1b93,0x1b93,0x1b93,0x1b93,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26, +0x1c26,0x1c26,0x1c26,0x1c26,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x255,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974, +0x1974,0x1974,0x1974,0x1974,0x273,0x273,0x273,0x273,0x273,0x273,0x273,0x273,0x273,0x273,0x273,0x273, +0x273,0x273,0x273,0x273,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0x95d,0x95d,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,0x95d,0x95d,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50, +0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,0x14bb,0x3f0,0x3ff,0x3ff,0x18,0x405,0x405,0x405, +0x405,0x405,0x405,0x405,0x405,0x18,0x18,0x405,0x405,0x18,0x18,0x405,0x405,0x405,0x405,0x405, +0x405,0x405,0x405,0x405,0x405,0x405,0x405,0x405,0x405,0x18,0x405,0x405,0x405,0x405,0x405,0x405, +0x405,0x18,0x405,0x18,0x18,0x18,0x405,0x405,0x405,0x405,0x18,0x18,0x3f3,0xcb1,0x3f0,0x3ff, +0x3ff,0x3f0,0x3f0,0x3f0,0x3f0,0x18,0x18,0x3ff,0x3ff,0x18,0x18,0x402,0x402,0x3f6,0xda4,0x18, +0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3f0,0x18,0x18,0x18,0x18,0x408,0x408,0x18,0x408, +0x405,0x405,0x3f0,0x3f0,0x18,0x18,0x948,0x948,0x948,0x948,0x948,0x948,0x948,0x948,0x948,0x948, +0x405,0x405,0x3fc,0x3fc,0x3f9,0x3f9,0x3f9,0x3f9,0x3f9,0x3fc,0x3f9,0x1125,0x184b,0x1848,0x18f0,0x18, +0x1b,0xcb4,0x40b,0xcb7,0x1b,0x417,0x417,0x417,0x417,0x417,0x417,0x1b,0x1b,0x1b,0x1b,0x417, +0x417,0x1b,0x1b,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x417, +0x417,0x1b,0x417,0x417,0x417,0x417,0x417,0x417,0x417,0x1b,0x417,0x41a,0x1b,0x417,0x41a,0x1b, +0x417,0x417,0x1b,0x1b,0x40e,0x1b,0x414,0x414,0x414,0x40b,0x40b,0x1b,0x1b,0x1b,0x1b,0x40b, +0x40b,0x1b,0x1b,0x40b,0x40b,0x411,0x1b,0x1b,0x1b,0xf81,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, +0x1b,0x41a,0x41a,0x41a,0x417,0x1b,0x41a,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x94b,0x94b, +0x94b,0x94b,0x94b,0x94b,0x94b,0x94b,0x94b,0x94b,0x40b,0x40b,0x417,0x417,0x417,0xf81,0x18f3,0x1b, +0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1e,0x41d,0x41d,0x426,0x1e,0x429,0x429,0x429, +0x429,0x429,0x429,0x429,0xcc0,0x429,0x1e,0x429,0x429,0x429,0x1e,0x429,0x429,0x429,0x429,0x429, +0x429,0x429,0x429,0x429,0x429,0x429,0x429,0x429,0x429,0x1e,0x429,0x429,0x429,0x429,0x429,0x429, +0x429,0x1e,0x429,0x429,0x1e,0x429,0x429,0x429,0x429,0x429,0x1e,0x1e,0x420,0x429,0x426,0x426, +0x426,0x41d,0x41d,0x41d,0x41d,0x41d,0x1e,0x41d,0x41d,0x426,0x1e,0x426,0x426,0x423,0x1e,0x1e, +0x429,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, +0x429,0xcc0,0xcba,0xcba,0x1e,0x1e,0x94e,0x94e,0x94e,0x94e,0x94e,0x94e,0x94e,0x94e,0x94e,0x94e, +0x13d4,0xcbd,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x16cb,0x184e,0x184e,0x184e,0x1851,0x1851,0x1851, +0x21,0x42c,0x43b,0x43b,0x21,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x21,0x21,0x441, +0x441,0x21,0x21,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x441, +0x441,0x21,0x441,0x441,0x441,0x441,0x441,0x441,0x441,0x21,0x441,0x441,0x21,0xcc3,0x441,0x441, +0x441,0x441,0x21,0x21,0x42f,0x441,0x42c,0x42c,0x43b,0x42c,0x42c,0x42c,0xf84,0x21,0x21,0x43b, +0x43e,0x21,0x21,0x43e,0x43e,0x432,0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x1a5b,0x42c,0x42c, +0x21,0x21,0x21,0x21,0x444,0x444,0x21,0x441,0x441,0x441,0xf84,0xf84,0x21,0x21,0x438,0x438, +0x438,0x438,0x438,0x438,0x438,0x438,0x438,0x438,0x435,0xcc3,0x12fc,0x12fc,0x12fc,0x12fc,0x12fc,0x12fc, +0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x24,0x24,0x447,0x453,0x24,0x453,0x453,0x453, +0x453,0x453,0x453,0x24,0x24,0x24,0x453,0x453,0x453,0x24,0x453,0x453,0x456,0x453,0x24,0x24, +0x24,0x453,0x453,0x24,0x453,0x24,0x453,0x453,0x24,0x24,0x24,0x453,0x453,0x24,0x24,0x24, +0x453,0x453,0x453,0x24,0x24,0x24,0x453,0x453,0x453,0x453,0x453,0x453,0x453,0x453,0xda7,0x453, +0x453,0x453,0x24,0x24,0x24,0x24,0x447,0x44d,0x447,0x44d,0x44d,0x24,0x24,0x24,0x44d,0x44d, +0x44d,0x24,0x450,0x450,0x450,0x44a,0x24,0x24,0xf87,0x24,0x24,0x24,0x24,0x24,0x24,0x447, +0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0xebb,0x954,0x954,0x954,0x954,0x954, +0x954,0x954,0x954,0x954,0x951,0x951,0x951,0xd77,0xcc6,0xcc6,0xcc6,0xcc6,0xcc6,0xcc9,0xcc6,0x24, +0x24,0x24,0x24,0x24,0x14be,0x465,0x465,0x465,0x18f6,0x468,0x468,0x468,0x468,0x468,0x468,0x468, +0x468,0x27,0x468,0x468,0x468,0x27,0x468,0x468,0x468,0x468,0x468,0x468,0x468,0x468,0x468,0x468, +0x468,0x468,0x468,0x468,0x468,0x27,0x468,0x468,0x468,0x468,0x468,0x468,0x468,0x468,0x468,0x468, +0x14c1,0x468,0x468,0x468,0x468,0x468,0x27,0x27,0x1b00,0xf90,0x459,0x459,0x459,0x465,0x465,0x465, +0x465,0x27,0x459,0x459,0x45c,0x27,0x459,0x459,0x459,0x45f,0x27,0x27,0x27,0x27,0x27,0x27, +0x27,0x459,0x459,0x27,0xf90,0xf90,0x16ce,0x27,0x27,0x1b03,0x27,0x27,0x468,0x468,0xf8a,0xf8a, +0x27,0x27,0x462,0x462,0x462,0x462,0x462,0x462,0x462,0x462,0x462,0x462,0x27,0x27,0x27,0x27, +0x27,0x27,0x27,0x19bf,0xf8d,0xf8d,0xf8d,0xf8d,0xf8d,0xf8d,0xf8d,0xf8d,0x178e,0x14c4,0x471,0x471, +0x18f9,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x2a,0x477,0x477,0x477,0x2a,0x477,0x477, +0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x2a,0x477,0x477, +0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x477,0x2a,0x477,0x477,0x477,0x477,0x477,0x2a,0x2a, +0xccc,0xccf,0x471,0x46b,0x474,0x471,0x46b,0x471,0x471,0x2a,0x46b,0x474,0x474,0x2a,0x474,0x474, +0x46b,0x46e,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x46b,0x46b,0x2a,0x2a,0x2a,0x2a,0x2a, +0x2a,0x1b06,0x477,0x2a,0x477,0x477,0xed3,0xed3,0x2a,0x2a,0x957,0x957,0x957,0x957,0x957,0x957, +0x957,0x957,0x957,0x957,0x2a,0xed6,0xed6,0x1bc9,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, +0x2a,0x2a,0x2a,0x2a,0x1854,0x14c7,0x483,0x483,0x1a5e,0x489,0x489,0x489,0x489,0x489,0x489,0x489, +0x489,0x2d,0x489,0x489,0x489,0x2d,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489, +0x489,0x489,0x489,0x489,0x483,0x47a,0x47a,0x47a,0xf93,0x2d,0x483,0x483,0x483,0x2d,0x486,0x486, +0x486,0x47d,0x1302,0x1791,0x2d,0x2d,0x2d,0x2d,0x1794,0x1794,0x1794,0x47a,0x1791,0x1791,0x1791,0x1791, +0x1791,0x1791,0x1791,0x16d1,0x489,0x489,0xf93,0xf93,0x2d,0x2d,0x480,0x480,0x480,0x480,0x480,0x480, +0x480,0x480,0x480,0x480,0xf96,0xf96,0xf96,0xf96,0xf96,0xf96,0x1791,0x1791,0x1791,0xf99,0xf9c,0xf9c, +0xf9c,0xf9c,0xf9c,0xf9c,0x30,0x1a61,0xa23,0xa23,0x30,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29, +0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0x30,0x30,0x30,0xa29,0xa29, +0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29, +0xa29,0xa29,0x30,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0x30,0xa29,0x30,0x30, +0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0xa29,0x30,0x30,0x30,0xa1d,0x30,0x30,0x30,0x30,0xa1a, +0xa23,0xa23,0xa1a,0xa1a,0xa1a,0x30,0xa1a,0x30,0xa23,0xa23,0xa26,0xa23,0xa26,0xa26,0xa26,0xa1a, +0x30,0x30,0x30,0x30,0x30,0x30,0x14ca,0x14ca,0x14ca,0x14ca,0x14ca,0x14ca,0x14ca,0x14ca,0x14ca,0x14ca, +0x30,0x30,0xa23,0xa23,0xa20,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, +0x33,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4, +0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4,0x4a4, +0x4a4,0x48f,0x4a4,0x4a1,0x48f,0x48f,0x48f,0x48f,0x48f,0x48f,0x495,0x33,0x33,0x33,0x33,0x48c, +0x4aa,0x4aa,0x4aa,0x4aa,0x4aa,0x4a4,0x4a7,0x492,0x492,0x492,0x492,0x492,0x492,0x48f,0x492,0x498, +0x49e,0x49e,0x49e,0x49e,0x49e,0x49e,0x49e,0x49e,0x49e,0x49e,0x49b,0x49b,0x33,0x33,0x33,0x33, +0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, +0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x36,0x4b9,0x4b9,0x36, +0x4b9,0x36,0x19c5,0x4b9,0x4b9,0x19c5,0x4b9,0x36,0x19c5,0x4b9,0x19c5,0x19c5,0x19c5,0x19c5,0x19c5,0x19c5, +0x4b9,0x4b9,0x4b9,0x4b9,0x19c5,0x4b9,0x4b9,0x4b9,0x4b9,0x4b9,0x4b9,0x4b9,0x19c5,0x4b9,0x4b9,0x4b9, +0x36,0x4b9,0x36,0x4b9,0x19c5,0x19c5,0x4b9,0x4b9,0x19c5,0x4b9,0x4b9,0x4b9,0x4b9,0x4ad,0x4b9,0x4b6, +0x4ad,0x4ad,0x4ad,0x4ad,0x4ad,0x4ad,0x19c2,0x4ad,0x4ad,0x4b9,0x36,0x36,0x4c2,0x4c2,0x4c2,0x4c2, +0x4c2,0x36,0x4bf,0x36,0x4b0,0x4b0,0x4b0,0x4b0,0x4b0,0x4ad,0x1bcc,0x36,0x4b3,0x4b3,0x4b3,0x4b3, +0x4b3,0x4b3,0x4b3,0x4b3,0x4b3,0x4b3,0x36,0x36,0x4bc,0x4bc,0x13d7,0x13d7,0x36,0x36,0x36,0x36, +0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x99c,0x99c,0x99c,0x99f, +0x99c,0x99c,0x99c,0x99c,0x39,0x99c,0x99c,0x99c,0x99c,0x99f,0x99c,0x99c,0x99c,0x99c,0x99f,0x99c, +0x99c,0x99c,0x99c,0x99f,0x99c,0x99c,0x99c,0x99c,0x99f,0x99c,0x99c,0x99c,0x99c,0x99c,0x99c,0x99c, +0x99c,0x99c,0x99c,0x99c,0x99c,0x99f,0xa38,0xfa8,0xfa8,0x39,0x39,0x39,0x39,0x966,0x966,0x969, +0x966,0x969,0x969,0x975,0x969,0x975,0x966,0x966,0x966,0x966,0x966,0x996,0x966,0x969,0x96f,0x96f, +0x972,0x97b,0x96c,0x96c,0x99c,0x99c,0x99c,0x99c,0x130b,0x1305,0x1305,0x1305,0x966,0x966,0x966,0x969, +0x966,0x966,0xa2c,0x966,0x39,0x966,0x966,0x966,0x966,0x969,0x966,0x966,0x966,0x966,0x969,0x966, +0x966,0x966,0x966,0x969,0x966,0x966,0x966,0x966,0x969,0x966,0xa2c,0xa2c,0xa2c,0x966,0x966,0x966, +0x966,0x966,0x966,0x966,0xa2c,0x969,0xa2c,0xa2c,0xa2c,0x39,0xa35,0xa35,0xa32,0xa32,0xa32,0xa32, +0xa32,0xa32,0xa2f,0xa32,0xa32,0xa32,0xa32,0xa32,0xa32,0x39,0xf9f,0xa32,0xdaa,0xdaa,0xfa2,0xfa5, +0xf9f,0x1128,0x1128,0x1128,0x1128,0x1308,0x1308,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, +0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, +0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x3c,0x13dd, +0x3c,0x3c,0x3c,0x3c,0x3c,0x13dd,0x3c,0x3c,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5, +0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xdb9, +0xa62,0x3f,0xa62,0xa62,0xa62,0xa62,0x3f,0x3f,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0x3f, +0xa62,0x3f,0xa62,0xa62,0xa62,0xa62,0x3f,0x3f,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xdb9, +0xa62,0x3f,0xa62,0xa62,0xa62,0xa62,0x3f,0x3f,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62, +0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xdb9,0xa62,0x3f,0xa62,0xa62, +0xa62,0xa62,0x3f,0x3f,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0x3f,0xa62,0x3f,0xa62,0xa62, +0xa62,0xa62,0x3f,0x3f,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xdb9,0xa62,0xa62,0xa62,0xa62, +0xa62,0xa62,0xa62,0x3f,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62, +0xa62,0xa62,0xa62,0xdb9,0xa62,0x3f,0xa62,0xa62,0xa62,0xa62,0x3f,0x3f,0xa62,0xa62,0xa62,0xa62, +0xa62,0xa62,0xa62,0xdb9,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62, +0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0x3f,0x3f,0x130e,0x130e,0xdb3,0xdb6,0xa5c,0xa65,0xa59, +0xa59,0xa59,0xa59,0xa65,0xa65,0xa5f,0xa5f,0xa5f,0xa5f,0xa5f,0xa5f,0xa5f,0xa5f,0xa5f,0xa56,0xa56, +0xa56,0xa56,0xa56,0xa56,0xa56,0xa56,0xa56,0xa56,0xa56,0x3f,0x3f,0x3f,0xa68,0xa68,0xa68,0xa68, +0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68, +0xa68,0x16d7,0x42,0x42,0x16d4,0x16d4,0x16d4,0x16d4,0x16d4,0x16d4,0x42,0x42,0xa7a,0xa7d,0xa7d,0xa7d, +0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d, +0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa7d,0xa77,0xa74,0x45,0x45,0x45,0xa83,0xa83,0xa83,0xa83, +0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa80,0xa80,0xa80,0xa83,0xa83,0xa83,0x14cd,0x14cd,0x14cd, +0x14cd,0x14cd,0x14cd,0x14cd,0x14cd,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0xaa4,0xaa4,0xaa4,0xaa4, +0xaa4,0xaa4,0xa86,0xaa4,0xaa4,0xa89,0xa89,0xa89,0xa89,0xa89,0xa89,0xa89,0xa89,0xa89,0xa8c,0xa89, +0xa9b,0xa9b,0xa9e,0xaa7,0xa95,0xa92,0xa9b,0xa98,0xaa7,0xcd2,0x4b,0x4b,0xaa1,0xaa1,0xaa1,0xaa1, +0xaa1,0xaa1,0xaa1,0xaa1,0xaa1,0xaa1,0x4b,0x4b,0x4b,0x4b,0x4b,0x4b,0xcd5,0xcd5,0xcd5,0xcd5, +0xcd5,0xcd5,0xcd5,0xcd5,0xcd5,0xcd5,0x4b,0x4b,0x4b,0x4b,0x4b,0x4b,0xab6,0xab6,0xb2e,0xb31, +0xabc,0xb2b,0xab9,0xab6,0xabf,0xace,0xac2,0xad1,0xad1,0xad1,0xaad,0x1b09,0xac5,0xac5,0xac5,0xac5, +0xac5,0xac5,0xac5,0xac5,0xac5,0xac5,0x4e,0x4e,0x4e,0x4e,0x4e,0x4e,0xac8,0xac8,0xac8,0xac8, +0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8, +0xac8,0xac8,0xac8,0xac8,0x18fc,0x4e,0x4e,0x4e,0x4e,0x4e,0x4e,0x4e,0xac8,0xac8,0xac8,0xac8, +0xac8,0xac8,0xac8,0xac8,0xac8,0xab0,0xfc6,0x4e,0x4e,0x4e,0x4e,0x4e,0x117f,0x117f,0x117f,0x117f, +0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x4e6,0x4e6,0x4e6,0x4e6, +0x4e6,0x4e6,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e6,0x4e6,0x4e6,0x4e6, +0x4e6,0x4e6,0x51,0x51,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x51,0x51,0x4e6,0x4e6,0x4e6,0x4e6, +0x4e6,0x4e6,0x4e6,0x4e6,0x51,0x4e9,0x51,0x4e9,0x51,0x4e9,0x51,0x4e9,0x4e6,0x4e6,0x4e6,0x4e6, +0x4e6,0x4e6,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e6,0x4e6,0x4e6,0x4e6, +0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x51,0x51,0x4e6,0x4e6,0x4e6,0x4e6, +0x4e6,0x4e6,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e6,0x4e6,0x4e6,0x4e6, +0x4e6,0x51,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e0,0x4e6,0x4e0,0x4e0,0x4dd,0x4e6,0x4e6, +0x4e6,0x51,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4dd,0x4dd,0x4dd,0x4e6,0x4e6,0x4e6,0x4e6, +0x51,0x51,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x51,0x4dd,0x4dd,0x4dd,0x4e6,0x4e6,0x4e6,0x4e6, +0x4e6,0x4e6,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4dd,0x4dd,0x4dd,0x51,0x51,0x4e6,0x4e6, +0x4e6,0x51,0x4e6,0x4e6,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e3,0x4e0,0x51,0xba6,0xba9,0xba9,0xba9, +0xfcf,0x54,0x14a9,0x14a9,0x14a9,0x14a9,0x4f2,0x4f2,0x4f2,0x4f2,0x4f2,0x4f2,0x53d,0xbbb,0x57,0x57, +0x6d8,0x53d,0x53d,0x53d,0x53d,0x53d,0x543,0x555,0x543,0x54f,0x549,0x6db,0x53a,0x6d5,0x6d5,0x6d5, +0x6d5,0x53a,0x53a,0x53a,0x53a,0x53a,0x540,0x552,0x540,0x54c,0x546,0x57,0xdc2,0xdc2,0xdc2,0xdc2, +0xdc2,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x57,0x57,0x57,0x1b0c,0x5a,0x5a,0x5a, +0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x564,0x564,0x564,0x564, +0x564,0x564,0x564,0x564,0x564,0x564,0x564,0x564,0x564,0x561,0x561,0x561,0x561,0x564,0xadd,0xadd, +0xbc1,0xbc7,0xbc7,0xbc4,0xbc4,0xbc4,0xbc4,0xdc8,0xed9,0xed9,0xed9,0xed9,0x1113,0x5d,0x5d,0x5d, +0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x5d,0x594,0x594,0x594,0xae6, +0xee2,0xfd5,0xfd5,0xfd5,0xfd5,0x126f,0x16dd,0x16dd,0x60,0x60,0x60,0x60,0x702,0x702,0x702,0x702, +0x702,0x702,0x702,0x702,0x702,0x702,0x5a0,0x5a0,0x59d,0x59d,0x59d,0x59d,0x5c1,0x5c1,0x5c1,0x5c1, +0x5c1,0xaef,0xaef,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63, +0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x5c4,0x5c4,0x5c4,0x5c4, +0x5c4,0x5c4,0x5c4,0x5c4,0x5c4,0x5c4,0x5c4,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66, +0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0xb0a,0xb0a,0xb0a,0xb0a, +0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a, +0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0x69,0xb0a,0xb0a,0xb0a,0xb0a,0xb0d,0xb0a,0xb0a,0xb0a,0xb0a, +0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0d, +0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69,0xb10,0xb10,0xb10,0xb10, +0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10, +0xb10,0xb10,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x72,0x822,0x81c,0x822, +0x81c,0x822,0x81c,0x822,0x81c,0x822,0x81c,0x81c,0x81f,0x81c,0x81f,0x81c,0x81f,0x81c,0x81f,0x81c, +0x81f,0x81c,0x81f,0x81c,0x81f,0x81c,0x81f,0x81c,0x81f,0x81c,0x81f,0x81c,0x81c,0x81c,0x81c,0x822, +0x81c,0x822,0x81c,0x822,0x81c,0x81c,0x81c,0x81c,0x81c,0x81c,0x822,0x81c,0x81c,0x81c,0x81c,0x81c, +0x81f,0xc60,0xc60,0x72,0x72,0x936,0x936,0x8fd,0x8fd,0x825,0x828,0xc5d,0x75,0x75,0x75,0x75, +0x75,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a, +0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x83a,0x1101,0x18c3,0x19aa, +0x78,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d, +0x83d,0x83d,0x83d,0x78,0x906,0x906,0x909,0x909,0x909,0x909,0x909,0x909,0x909,0x909,0x909,0x909, +0x909,0x909,0x909,0x909,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846, +0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846,0x846, +0x846,0xd5c,0xd5c,0x7b,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22, +0xb22,0x7e,0x7e,0x7e,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28, +0xb28,0xb28,0xb28,0xb28,0xb28,0xc69,0xb28,0xb28,0xb28,0xc69,0xb28,0x81,0x81,0x81,0x81,0x81, +0x81,0x81,0x81,0x81,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6, +0x11a6,0x11a6,0x11a6,0x11a6,0x9c0,0x9c0,0x9c0,0x9c0,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, +0x84,0x84,0x84,0x84,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b, +0x121b,0x121b,0x121b,0x121b,0x609,0x609,0x609,0x609,0x609,0x609,0x609,0x87,0x87,0x87,0x87,0x87, +0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x5f7,0x5f7,0x5f7,0x5f7,0x5f7,0x87,0x87,0x87,0x87, +0x87,0xafb,0x5fa,0x600,0x606,0x606,0x606,0x606,0x606,0x606,0x606,0x606,0x606,0x5fd,0x600,0x600, +0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x600,0x87,0x600,0x600,0x600,0x600, +0x600,0x87,0x600,0x87,0x600,0x600,0x87,0x600,0x600,0x87,0x600,0x600,0x600,0x600,0x600,0x600, +0x600,0x600,0x600,0x603,0x615,0x60f,0x615,0x60f,0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f, +0x612,0x618,0x615,0x60f,0x1323,0x1323,0x1b0f,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, +0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x615,0x60f,0x612,0x618,0x615,0x60f,0x615,0x60f,0x615, +0x60f,0x615,0x615,0x60f,0x60f,0x60f,0x60f,0x612,0x60f,0x60f,0x612,0x60f,0x612,0x612,0x612,0x60f, +0x612,0x612,0x612,0x612,0x8a,0x8a,0x612,0x612,0x612,0x612,0x60f,0x60f,0x612,0x60f,0x60f,0x60f, +0x60f,0x612,0x60f,0x60f,0x60f,0x60f,0x60f,0x612,0x612,0x612,0x60f,0x60f,0x8a,0x8a,0x8a,0x8a, +0x8a,0x8a,0x8a,0x1b0f,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46, +0xb46,0xb46,0xb46,0xb46,0x85e,0x870,0x86d,0x870,0x86d,0xc7e,0xc7e,0xd68,0xd65,0x861,0x861,0x861, +0x861,0x873,0x873,0x873,0x88b,0x88e,0x89d,0x8d,0x891,0x894,0x8a0,0x8a0,0x888,0x87f,0x879,0x87f, +0x879,0x87f,0x879,0x87c,0x87c,0x897,0x897,0x89a,0x897,0x897,0x897,0x8d,0x897,0x885,0x882,0x87c, +0x8d,0x8d,0x8d,0x8d,0x621,0x62d,0x621,0xbfa,0x621,0x90,0x621,0x62d,0x621,0x62d,0x621,0x62d, +0x621,0x62d,0x621,0x62d,0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627, +0x62d,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x62a, +0x624,0x90,0x90,0x61e,0x75f,0x762,0x777,0x77a,0x759,0x762,0x762,0x96,0x741,0x744,0x744,0x744, +0x744,0x741,0x741,0x96,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93,0xafe,0xafe,0xafe, +0x9c3,0x73b,0x630,0x630,0x96,0x789,0x768,0x759,0x762,0x75f,0x759,0x76b,0x75c,0x756,0x759,0x777, +0x76e,0x765,0x786,0x759,0x783,0x783,0x783,0x783,0x783,0x783,0x783,0x783,0x783,0x783,0x774,0x771, +0x777,0x777,0x777,0x789,0x74a,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747, +0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747,0x747, +0x747,0x747,0x747,0x96,0x96,0x96,0x747,0x747,0x747,0x747,0x747,0x747,0x96,0x96,0x747,0x747, +0x747,0x747,0x747,0x747,0x96,0x96,0x747,0x747,0x747,0x747,0x747,0x747,0x96,0x96,0x747,0x747, +0x747,0x96,0x96,0x96,0xb49,0xb49,0xb49,0xb49,0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, +0x99,0x1860,0x1860,0x1860,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f, +0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0xb4f,0x9c,0x9c,0x9c,0x9c,0x9c,0x1626,0x1626,0x1626,0x1626, +0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0xb58,0xb58,0xb58,0xb58, +0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58, +0xb58,0xb58,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0xb64,0xb64,0xb64,0xb64, +0xb64,0xb64,0xb64,0xa2,0xa2,0xfe1,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64, +0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0x16e3,0x16e3,0x16e3,0x16e3, +0x16e3,0x16e3,0x16e3,0x16e3,0x16e3,0x1b12,0x1b12,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2, +0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xb7c,0xb7c,0xb7c,0xb7c, +0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, +0xb79,0xa5,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb7c,0xb7c,0xb79,0xb79, +0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, +0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb7c,0xa5,0xb7c,0xb7c,0xa5,0xa5,0xb7c,0xa5, +0xa5,0xb7c,0xb7c,0xa5,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, +0xb7c,0xb7c,0xb79,0xb79,0xb79,0xb79,0xa5,0xb79,0xa5,0xb79,0xb79,0xb79,0xb79,0xcf0,0xb79,0xb79, +0xa5,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb7c,0xb7c,0xb7c,0xb7c, +0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb79,0xb79,0xb79,0xb79, +0xb7c,0xb7c,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xa5,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, +0xb7c,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xa5,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, +0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, +0xb79,0xb79,0xb79,0xb79,0xb7c,0xb7c,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xa5,0xb7c,0xb7c,0xb7c,0xb7c, +0xb7c,0xa5,0xb7c,0xa5,0xa5,0xa5,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xa5,0xb79,0xb79, +0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xdda,0xdda,0xa5,0xa5, +0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, +0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb79,0xb79,0xb79,0xb73,0xb79,0xb79,0xb79,0xb79, +0xb79,0xb79,0xef1,0xeee,0xa5,0xa5,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76, +0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xa8,0xb82,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8, +0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8, +0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xc09,0xc09,0xc09,0xc09,0xc09,0xc09,0xc09,0xc09, +0xc09,0xc09,0xc09,0xc09,0xc09,0x1b18,0xc09,0xc09,0xc09,0xc09,0xc03,0xc03,0xc06,0x1b15,0xab,0xab, +0xab,0xab,0xab,0xab,0xab,0xab,0xab,0x1b18,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12, +0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc12,0xc0c,0xc0c,0xc0f,0xc72,0xc72,0xae, +0xae,0xae,0xae,0xae,0xae,0xae,0xae,0xae,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18, +0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc18,0xc15,0xc15,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xc1e,0xc1e,0xc1e,0xc1e,0xc1e,0xc1e,0xc1e,0xc1e, +0xc1e,0xc1e,0xc1e,0xc1e,0xc1e,0xb4,0xc1e,0xc1e,0xc1e,0xb4,0xc1b,0xc1b,0xb4,0xb4,0xb4,0xb4, +0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02, +0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02,0xd02, +0xd02,0xd02,0xd02,0xd02,0xd02,0x14e8,0x14e8,0xb7,0xcf3,0xcf3,0xcf3,0xcff,0xcff,0xcff,0xcff,0xcf3, +0xcf3,0xcff,0xcff,0xcff,0xb7,0xb7,0xb7,0xb7,0xcff,0xcff,0xcf3,0xcff,0xcff,0xcff,0xcff,0xcff, +0xcff,0xcf6,0xcf6,0xcf6,0xb7,0xb7,0xb7,0xb7,0xcf9,0xb7,0xb7,0xb7,0xd05,0xd05,0xcfc,0xcfc, +0xcfc,0xcfc,0xcfc,0xcfc,0xcfc,0xcfc,0xcfc,0xcfc,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08, +0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xd08,0xba,0xba,0xd08,0xd08,0xd08,0xd08, +0xd08,0xba,0xba,0xba,0xba,0xba,0xba,0xba,0xba,0xba,0xba,0xba,0x14eb,0x14eb,0x14eb,0x14eb, +0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb, +0xbd,0xbd,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb, +0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0xbd,0x1a64,0x14eb,0x14eb,0x14eb,0x14eb, +0x14eb,0x14eb,0x14eb,0x14eb,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c, +0xc0,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c, +0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xc0,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c, +0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xc0,0xd2c,0xd2c,0xc0,0xd2c, +0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xc0,0xc0, +0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xd2c,0xc0,0xc0, +0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0, +0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0, +0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f, +0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xc3,0xc3,0xc3,0xc3,0xc3, +0xd6e,0xd6e,0xd74,0xc6,0xc6,0xc6,0xc6,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b, +0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b,0xd6b, +0xc6,0xc6,0xc6,0xd71,0xd71,0xd71,0xd71,0xd71,0xd71,0xd71,0xd71,0xd71,0xd35,0xd35,0xd35,0xd35, +0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35, +0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xd35,0xc9,0xd32,0xd3e,0xd3e,0xd3e,0xd3e, +0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e, +0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xd3e,0xcc,0xcc,0xd3b,0xd3b,0xd3b,0xd3b, +0xd3b,0xd3b,0xd3b,0xd3b,0xd3b,0xd3b,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x1824,0x1824,0x1824,0x1824, +0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0xd41,0xd41,0xd41,0xd41, +0xd41,0xd41,0xcf,0xcf,0xd41,0xcf,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41, +0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xd41,0xcf,0xd41, +0xd41,0xcf,0xcf,0xcf,0xd41,0xcf,0xcf,0xd41,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44, +0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd2, +0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5, +0xdf5,0xdf5,0xdf5,0x14ee,0x14ee,0x179a,0x179a,0xd8,0x10e0,0x10e0,0x10e0,0x10e0,0x10e0,0x10e0,0x10e0,0x10e0, +0x10e0,0x10e0,0x10e0,0x10e0,0x1a73,0x129,0x129,0x129,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07, +0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xe07,0xdfe, +0xdfe,0xe04,0xe04,0xdfe,0xdb,0xdb,0xe01,0xe01,0x1110,0x1110,0x1110,0x1110,0xde,0xde,0xde,0xde, +0xde,0xde,0xde,0xde,0xde,0xde,0xde,0xde,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f, +0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xc6f,0xe19,0xe16,0xe19,0xe16,0xe16,0xe0d,0xe0d,0xe0d, +0xe0d,0xe0d,0xe0d,0x115b,0x1158,0x115b,0x1158,0x1155,0x1155,0x1155,0x13e6,0x13e3,0xe1,0xe1,0xe1,0xe1, +0xe1,0xe13,0xe10,0xe10,0xe10,0xe0d,0xe13,0xe10,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c, +0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe4, +0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe4, +0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe4,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe4, +0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe1c,0xe4,0xe22,0xe22,0xe22,0xe22,0xe22,0xe22,0xe22,0xe22, +0xe22,0xe22,0xe22,0xe22,0xe22,0xe22,0xe22,0xe22,0xe1f,0xe1f,0xe1f,0xe1f,0xe1f,0xe1f,0xe1f,0xe1f, +0xe1f,0xe1f,0xe7,0xe7,0xe7,0xe7,0xe7,0xe7,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xea,0x13e9, +0xea,0xea,0xea,0xea,0xea,0x13e9,0xea,0xea,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c, +0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe3d,0xe31,0xe31,0xe31,0xed,0xe31,0xe31,0xed, +0xed,0xed,0xed,0xed,0xe31,0xe31,0xe31,0xe31,0xe3d,0xe3d,0xe3d,0xe3d,0xed,0xe3d,0xe3d,0xe3d, +0xed,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d, +0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0xe3d,0x1905,0x1905,0xed,0xed,0xe2e,0xe2e,0xe2e,0xed, +0xed,0xed,0xed,0xe34,0xe37,0xe37,0xe37,0xe37,0xe37,0xe37,0xe37,0xe37,0x1902,0xed,0xed,0xed, +0xed,0xed,0xed,0xed,0xe3a,0xe3a,0xe3a,0xe3a,0xe3a,0xe3a,0xe40,0xe40,0xe37,0xed,0xed,0xed, +0xed,0xed,0xed,0xed,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0x1161,0x1161, +0xf0,0xf0,0xf0,0xf0,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4f,0xe4f,0xe4f,0xe4c,0xe4c,0xe4f,0xe4c, +0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0, +0xe49,0xe49,0xe49,0xe49,0xe49,0xe49,0xe49,0xe49,0xe49,0xe49,0x115e,0xf0,0xf0,0xf0,0xe46,0xe46, +0xe55,0xe55,0xe55,0xe55,0xf3,0xf3,0xf3,0xf3,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55, +0xe52,0xe55,0xe55,0xe55,0xe55,0xe55,0xf3,0xf3,0xf3,0xf3,0xf3,0xf3,0xf3,0xf3,0xf3,0xf3, +0x14fd,0x1503,0x1500,0x1845,0x17a0,0x1869,0x1869,0x1869,0x1869,0x1869,0x190b,0x1908,0x190e,0x1908,0x190e,0x19cb, +0x1a67,0x1a67,0x1a67,0x1b2a,0x1b2a,0x1b24,0x1b21,0x1b24,0x1b21,0x1b24,0x1b21,0x1b24,0x1b21,0x1b27,0xf6,0xf6, +0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6, +0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6, +0xe79,0xe79,0xe79,0xe76,0xe76,0xe6d,0xe6d,0xe76,0xe73,0xe73,0xe73,0xe73,0x1a6a,0xf9,0xf9,0xf9, +0x12cc,0x12cc,0x12cc,0x12cf,0x12cf,0x12cf,0x12c6,0x12c6,0x12c9,0x12c6,0x14d,0x14d,0x14d,0x14d,0x14d,0x14d, +0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0x13f5,0x13f5,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xe7f, +0x1335,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0x1332, +0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42,0xc42, +0xeac,0xe9d,0xe97,0xea9,0xea6,0xea0,0xea0,0xeaf,0xe9a,0xea3,0xff,0xff,0xff,0xff,0xff,0xff, +0xf33,0xf33,0xf1e,0xf33,0xf36,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0x1b2d,0x105,0x105,0x105, +0xf2d,0xf2d,0xf2d,0xf2d,0xf2d,0xf2d,0xf2d,0xf2d,0xf2d,0xf2d,0xf3f,0xf3f,0xf24,0xf2a,0xf3f,0xf3f, +0xf27,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf21,0xf21,0xf21,0xf21,0xf21, +0xf21,0xf21,0xf21,0xf21,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0xf24,0x1b30,0x1b30,0x105, +0x1b39,0x1b33,0x19d1,0x19ce,0x19d1,0x19d1,0x19d1,0x1a70,0x1a6d,0x1a70,0x1a6d,0x108,0x108,0x108,0x108,0x108, +0x1b39,0x1b33,0x108,0x1b33,0x108,0x1b33,0x1b39,0x1b33,0x1b39,0x1b33,0x108,0x108,0x108,0x108,0x108,0x108, +0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x108,0x1b36,0x1b36, +0x1b36,0x1a70,0x1a6d,0x150c,0x13fe,0x13fe,0x1338,0x103b,0x103b,0x103b,0x103b,0x103b,0xf4e,0xf4e,0xf4e,0xf4e, +0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e, +0xf4b,0xf4b,0xf51,0xf51,0x10b,0x10b,0x10b,0x10b,0x10b,0x10b,0x10b,0x10b,0xf5a,0xf5a,0xf5a,0xf5a, +0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a,0xf5a, +0xf5a,0xf5a,0xf54,0xf54,0xf54,0xf54,0x116a,0x116a,0x10e,0x10e,0x10e,0xf57,0x1512,0x1512,0x1512,0x1512, +0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512, +0x1512,0x1512,0x1512,0x1512,0x1512,0x16f2,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111, +0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111, +0x111,0x111,0x111,0x111,0x111,0x111,0x111,0x111,0xf63,0xf63,0xf63,0x1518,0x1518,0x1518,0x1518,0x1518, +0x1518,0x1518,0x1518,0x1518,0x1518,0x1518,0x1518,0x114,0xf60,0xf60,0xf60,0xf60,0x1515,0x114,0x114,0x114, +0x114,0x114,0x114,0x114,0x114,0x114,0x114,0x114,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66, +0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0xf66,0x191d,0x191d,0x191d,0x191d,0x191d,0x191d, +0x191d,0x117,0x117,0x117,0x117,0x117,0x117,0x117,0x1062,0x1062,0x1062,0x1062,0x105f,0x105f,0x105f,0x105f, +0x105f,0x105f,0x105f,0x105f,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x105f,0x105f,0x1056,0x1053, +0x11a,0x11a,0x11a,0x1065,0x1065,0x1059,0x1059,0x1059,0x105c,0x105c,0x105c,0x105c,0x105c,0x105c,0x105c,0x105c, +0x105c,0x105c,0x11a,0x11a,0x11a,0x1062,0x1062,0x1062,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068, +0x1068,0x1068,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x107d,0x107d,0x107d,0x107d,0x107d,0x107d,0x107d,0x107d, +0x107d,0x107d,0x1080,0x1080,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d, +0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x11d,0x10a7,0x10a7,0x10a7,0x10a7,0x10a1,0x17a6,0x120,0x120, +0x120,0x120,0x120,0x120,0x120,0x120,0x10ad,0x10ad,0x10a4,0x10a4,0x10a4,0x10a4,0x10a4,0x10a4,0x10a4,0x10a4, +0x10a4,0x10a4,0x120,0x120,0x120,0x120,0x120,0x120,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10bf, +0x10bf,0x10bf,0x10bf,0x10bf,0x10bf,0x10bf,0x10bf,0x10bf,0x10bf,0x10bf,0x10c5,0x10c8,0x123,0x123,0x123,0x123, +0x123,0x123,0x123,0x123,0x123,0x123,0x123,0x10c2,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da, +0x10da,0x10ce,0x10ce,0x10ce,0x10ce,0x10ce,0x10ce,0x10d7,0x10d7,0x10ce,0x10ce,0x10d7,0x10d7,0x10ce,0x10ce,0x126, +0x126,0x126,0x126,0x126,0x126,0x126,0x126,0x126,0x10da,0x10da,0x10da,0x10ce,0x10da,0x10da,0x10da,0x10da, +0x10da,0x10da,0x10da,0x10da,0x10ce,0x10d7,0x126,0x126,0x10d4,0x10d4,0x10d4,0x10d4,0x10d4,0x10d4,0x10d4,0x10d4, +0x10d4,0x10d4,0x126,0x126,0x10d1,0x10dd,0x10dd,0x10dd,0x1524,0x129,0x129,0x129,0x129,0x129,0x129,0x129, +0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129, +0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x129,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3, +0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e3, +0x10e3,0x10e3,0x10e3,0x10e3,0x10e3,0x10e6,0x12c,0x12c,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9, +0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x10e9, +0x10e9,0x10e9,0x10e9,0x10e9,0x10e9,0x12f,0x12f,0x12f,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec, +0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x132,0x132,0x132,0x132,0x132,0x132,0x132, +0x132,0x132,0x132,0x132,0x132,0x132,0x132,0x132,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2, +0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2,0x10f2, +0x10f2,0x10f2,0x135,0x135,0x135,0x135,0x135,0x10ef,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5, +0x10f5,0x10f5,0x10f5,0x10f5,0x138,0x138,0x138,0x138,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8, +0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x13b,0x13b,0x13b,0x13b, +0x13b,0x13b,0x13b,0x13b,0x13b,0x13b,0x13b,0x13b,0x1170,0x1170,0x1170,0x1170,0x1179,0x1170,0x1170,0x1170, +0x1179,0x1170,0x1170,0x1170,0x1170,0x116d,0x13e,0x13e,0x1176,0x1176,0x1176,0x1176,0x1176,0x1176,0x1176,0x117c, +0x1176,0x117c,0x1176,0x1176,0x1176,0x117c,0x117c,0x13e,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f, +0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x141,0x141, +0x141,0x141,0x141,0x141,0x141,0x141,0x141,0x141,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a, +0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x1197,0x1182,0x1197, +0x1182,0x1182,0x1182,0x1182,0x1182,0x1182,0x1182,0x144,0x118b,0x1194,0x1182,0x1194,0x1194,0x1182,0x1182,0x1182, +0x1182,0x1182,0x1182,0x1182,0x1182,0x1197,0x1197,0x1197,0x1197,0x1197,0x1197,0x1182,0x1182,0x1188,0x1188,0x1188, +0x1188,0x1188,0x1188,0x1188,0x1188,0x144,0x144,0x1185,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191, +0x1191,0x1191,0x144,0x144,0x144,0x144,0x144,0x144,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191, +0x1191,0x1191,0x144,0x144,0x144,0x144,0x144,0x144,0x118e,0x118e,0x118e,0x118e,0x118e,0x118e,0x118e,0x119d, +0x11a0,0x11a0,0x11a0,0x11a0,0x118e,0x118e,0x144,0x144,0x1563,0x1563,0x1563,0x1563,0x1563,0x1563,0x1563,0x1563, +0x1563,0x1563,0x1563,0x1563,0x1563,0x1563,0x1560,0x1a85,0x12e1,0x12ba,0x12d8,0x12d8,0x12d8,0x12d8,0x12d8,0x12d8, +0x12d8,0x12c0,0x12bd,0x12b4,0x12b4,0x12de,0x12b4,0x12b4,0x12b4,0x12b4,0x12c3,0x149d,0x14a3,0x14a0,0x14a0,0x18e4, +0x16b9,0x16b9,0x1a52,0x147,0x147,0x147,0x147,0x147,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5, +0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11ac,0x11ac,0x11af,0x11b8,0x11b2,0x11b2,0x11b2,0x11b8, +0x14a,0x14a,0x14a,0x14a,0x14a,0x14a,0x14a,0x14a,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5, +0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x12a5, +0x12a5,0x12a5,0x12a5,0x12a5,0x12a5,0x150,0x150,0x150,0x11d6,0x11ca,0x11ca,0x11ca,0x11ca,0x11ca,0x11ca,0x11cd, +0x11dc,0x11dc,0x11ca,0x11ca,0x11ca,0x11ca,0x153,0x12d5,0x11d0,0x11d0,0x11d0,0x11d0,0x11d0,0x11d0,0x11d0,0x11d0, +0x11d0,0x11d0,0x153,0x153,0x153,0x153,0x11ca,0x11ca,0x11fa,0x11ee,0x11fa,0x156,0x156,0x156,0x156,0x156, +0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156,0x156, +0x156,0x156,0x156,0x11f7,0x11f7,0x11fd,0x11f1,0x11f4,0x1212,0x1212,0x1212,0x120c,0x120c,0x1203,0x120c,0x120c, +0x1203,0x120c,0x120c,0x1215,0x120f,0x1206,0x159,0x159,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209, +0x1209,0x1209,0x159,0x159,0x159,0x159,0x159,0x159,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x121b,0x15c, +0x15c,0x15c,0x15c,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218, +0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218, +0x15c,0x15c,0x15c,0x15c,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224, +0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x15f,0x1221,0x121e,0x121e,0x121e,0x121e, +0x121e,0x121e,0x121e,0x121e,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233, +0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x162,0x162,0x162,0x122d,0x1230,0x1230, +0x1230,0x1230,0x1230,0x1230,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239, +0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x165,0x165,0x1236,0x1236,0x1236,0x1236, +0x1236,0x1236,0x1236,0x1236,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f, +0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x123f,0x168,0x168,0x168,0x168,0x168,0x123c,0x123c,0x123c,0x123c, +0x123c,0x123c,0x123c,0x123c,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245, +0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245, +0x1245,0x1245,0x1245,0x16e,0x125d,0x125d,0x1b3c,0x171,0x171,0x171,0x171,0x171,0x171,0x171,0x171,0x171, +0x171,0x1926,0x171,0x171,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c, +0x147c,0x147c,0x147c,0x147c,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827,0x1827, +0x1827,0x1a76,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174, +0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174,0x174, +0x174,0x174,0x174,0x174,0x174,0x174,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344, +0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344, +0x12ae,0x13a7,0x13a7,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177, +0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab,0x12ab, +0x12ab,0x12ab,0x13a7,0x13a7,0x13a7,0x13a7,0x13a7,0x13a7,0x13a7,0x13a7,0x13a7,0x182a,0x177,0x177,0x177,0x177, +0x12a8,0x12a8,0x12a8,0x12a8,0x12a8,0x12a8,0x12a8,0x12a8,0x12a8,0x177,0x177,0x177,0x177,0x177,0x177,0x177, +0x13cb,0x13cb,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177, +0x18c9,0x18c9,0x18c9,0x18c9,0x18c9,0x18c9,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177, +0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177, +0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x177,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d, +0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d,0x134d, +0x134d,0x1347,0x1347,0x1347,0x17a,0x17a,0x134a,0x17a,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x1350,0x1359, +0x1353,0x1353,0x1359,0x1359,0x1359,0x1353,0x1359,0x1353,0x1353,0x1353,0x135c,0x135c,0x17d,0x17d,0x17d,0x17d, +0x17d,0x17d,0x17d,0x17d,0x1356,0x1356,0x1356,0x1356,0x180,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x180, +0x180,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x180,0x180,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x180, +0x180,0x180,0x180,0x180,0x180,0x180,0x180,0x180,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x180, +0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x180,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0, +0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1368,0x137a, +0x137a,0x136e,0x136e,0x136e,0x136e,0x136e,0x183,0x183,0x183,0x183,0x136b,0x136b,0x136b,0x136b,0x136b,0x136b, +0x136b,0x136b,0x136b,0x136b,0x136b,0x136b,0x136b,0x136b,0x136b,0x136b,0x1371,0x1371,0x1371,0x1371,0x1371,0x1371, +0x1371,0x1371,0x1371,0x1371,0x1b42,0x1b45,0x1b45,0x1b3f,0x1b3f,0x1b45,0x183,0x183,0x183,0x183,0x183,0x183, +0x183,0x183,0x183,0x1533,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d, +0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x186,0x186,0x186, +0x186,0x186,0x186,0x186,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380, +0x1380,0x1380,0x1380,0x189,0x189,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380, +0x1380,0x1380,0x1380,0x1536,0x189,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380, +0x1380,0x1380,0x1380,0x13b0,0x189,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380, +0x1380,0x1380,0x1380,0x1380,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536, +0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x1536,0x189,0x189,0x189,0x189,0x189,0x189, +0x189,0x189,0x189,0x189,0x13c5,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542, +0x16b0,0x1542,0x1542,0x1542,0x1782,0x1833,0x1833,0x186c,0x186c,0x1a34,0x1adf,0x1adf,0x18c,0x18c,0x18c,0x18c, +0x1c2c,0x1bae,0x1bae,0x1bae,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542,0x1542,0x16ad, +0x16ad,0x18c,0x18c,0x18c,0x1542,0x1542,0x1542,0x1542,0x1833,0x1833,0x1833,0x18cf,0x18cf,0x19b0,0x1a34,0x1adf, +0x1adf,0x18c,0x18c,0x18c,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383, +0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1bd2,0x1bd2,0x1bd2,0x18f,0x18f,0x18f,0x18f,0x1bd2, +0x1bd2,0x1bd2,0x1bd2,0x1bd2,0x141f,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f, +0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f, +0x141f,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x192,0x141f,0x192,0x192,0x141f,0x192,0x141f,0x141f,0x141f, +0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x141f,0x141f,0x192,0x141f,0x192,0x141f, +0x192,0x192,0x192,0x192,0x192,0x192,0x141f,0x192,0x192,0x192,0x192,0x141f,0x192,0x141f,0x192,0x141f, +0x192,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x192,0x141f,0x192,0x192,0x141f,0x192,0x141f,0x192,0x141f, +0x192,0x141f,0x192,0x141f,0x192,0x141f,0x141f,0x192,0x141f,0x192,0x192,0x141f,0x141f,0x141f,0x141f,0x192, +0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x141f, +0x141f,0x192,0x141f,0x192,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x192,0x141f, +0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f, +0x192,0x192,0x192,0x192,0x192,0x141f,0x141f,0x141f,0x192,0x141f,0x141f,0x141f,0x141f,0x141f,0x192,0x141f, +0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f,0x141f, +0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192, +0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192, +0x141c,0x141c,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192,0x192, +0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1422,0x1422,0x1422,0x1422,0x1422,0x1431,0x1422,0x1425,0x1425, +0x1422,0x1422,0x1422,0x1428,0x1428,0x195,0x142e,0x142e,0x142e,0x142e,0x142e,0x142e,0x142e,0x142e,0x142e,0x142e, +0x142b,0x1437,0x1437,0x1437,0x1932,0x192f,0x192f,0x1a7c,0x195,0x195,0x195,0x195,0x195,0x195,0x195,0x195, +0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2, +0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1440,0x143a,0x143a,0x1440,0x1440, +0x1449,0x1449,0x1443,0x1446,0x1446,0x1440,0x143d,0x198,0x198,0x198,0x198,0x198,0x198,0x198,0x198,0x198, +0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c, +0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x144c,0x19b,0x19b,0x19b,0x19b,0x1707,0x1707,0x144c,0x144c, +0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707, +0x19b,0x19b,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707, +0x1458,0x1458,0x1458,0x1458,0x1458,0x19dd,0x19dd,0x19dd,0x19dd,0x19dd,0x19dd,0x19e,0x19e,0x19e,0x19e,0x19d7, +0x1458,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455, +0x19da,0x19da,0x19da,0x19da,0x19da,0x19da,0x19da,0x19da,0x19e,0x19e,0x19e,0x19e,0x19e,0x19e,0x19e,0x1452, +0x1452,0x1452,0x1452,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b,0x145b, +0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x147c,0x1a1,0x1a1,0x1a1,0x1a1,0x1a1,0x1a1,0x1a1, +0x1479,0x1479,0x1479,0x1479,0x1479,0x1479,0x1479,0x1479,0x1479,0x1479,0x1a1,0x1a1,0x1a1,0x1a1,0x1a1,0x1a1, +0x147f,0x147f,0x147f,0x147f,0x147f,0x147f,0x147f,0x147f,0x1a4,0x1a4,0x1a4,0x1a4,0x1a4,0x1a4,0x1a4,0x1a4, +0x12db,0x12d8,0x12db,0x12b7,0x12d8,0x12de,0x12de,0x12e1,0x12de,0x12e1,0x12e4,0x12d8,0x12e1,0x12e1,0x12d8,0x12d8, +0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1482,0x148b,0x1482,0x148b,0x148b, +0x1482,0x1482,0x1482,0x1482,0x1482,0x1482,0x148e,0x1485,0x19e0,0x1b51,0x1a7,0x1a7,0x1a7,0x1a7,0x1a7,0x1a7, +0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1aa,0x1aa, +0x1551,0x1551,0x1551,0x1551,0x1551,0x1557,0x1aa,0x1aa,0x1aa,0x1aa,0x1aa,0x1aa,0x1aa,0x1aa,0x1aa,0x1aa, +0x155d,0x155d,0x155d,0x155d,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x1ad,0x155a, +0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x2b2,0x1b9c,0x1b9c,0x1b9c,0x1b9c, +0x16bc,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3, +0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x16b3,0x1b0,0x1b0,0x1b0,0x1b0, +0x1a85,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b57,0x1b54,0x1b54,0x1b54,0x1b3, +0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3, +0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3,0x1b3, +0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x1b6,0x1b6,0x1b6,0x1b6,0x1b6, +0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x1b6,0x1b6,0x1b6, +0x1b6,0x1b6,0x1b6,0x1b6,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x1b6,0x1b6, +0x156c,0x1566,0x1569,0x1572,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1b9,0x1b9,0x1b9,0x1b9, +0x1b9,0x1b9,0x1b9,0x1b9,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d, +0x155d,0x155d,0x155d,0x155d,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578, +0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1935,0x1935,0x1935,0x1935,0x1bd5,0x1bc,0x1bc, +0x1bc,0x1bc,0x1bc,0x1bc,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37,0x1a37, +0x1bc,0x1bc,0x1bc,0x1bc,0x1bb1,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc,0x1bc, +0x1bc,0x1bc,0x1bc,0x1bc,0x171c,0x16bf,0x1581,0x16c5,0x1bf,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a, +0x158a,0x1bf,0x1bf,0x158a,0x158a,0x1bf,0x1bf,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a, +0x158a,0x158a,0x158a,0x158a,0x158a,0x1bf,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a,0x158a,0x1bf,0x158a,0x158a, +0x1bf,0x158a,0x158a,0x158a,0x158a,0x158a,0x1bf,0x19bc,0x16c2,0x158a,0x157b,0x1581,0x157b,0x1581,0x1581,0x1581, +0x1581,0x1bf,0x1bf,0x1581,0x1581,0x1bf,0x1bf,0x1584,0x1584,0x1587,0x1bf,0x1bf,0x171f,0x1bf,0x1bf,0x1bf, +0x1bf,0x1bf,0x1bf,0x157b,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x158d,0x158a,0x158a,0x158a,0x158a,0x1581,0x1581, +0x1bf,0x1bf,0x157e,0x157e,0x157e,0x157e,0x157e,0x157e,0x157e,0x1bf,0x1bf,0x1bf,0x157e,0x157e,0x157e,0x157e, +0x157e,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x1bf,0x15a2,0x15a2,0x15a2,0x15a2, +0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x1c2,0x15a2, +0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15b4,0x15b4,0x15b4,0x15a8, +0x15a8,0x15a8,0x15a8,0x15a8,0x15a8,0x15ab,0x15ae,0x1c5,0x1c5,0x1c5,0x1c5,0x1c5,0x15b1,0x15b1,0x15b1,0x15b1, +0x15b1,0x15b1,0x15b1,0x15b1,0x15b1,0x15b1,0x1c5,0x1c5,0x1c5,0x1c5,0x1c5,0x1c5,0x1722,0x1722,0x1722,0x1722, +0x15c0,0x15bd,0x19e3,0x19e3,0x1a8b,0x1a8e,0x1a88,0x1a88,0x1c8,0x1c8,0x1c8,0x1c8,0x174f,0x174f,0x174f,0x174f, +0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x15c6,0x15c6,0x15c6,0x15c6, +0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6, +0x15c6,0x15c6,0x15c6,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x15c6,0x15c6,0x15c6,0x15c6, +0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6, +0x15c6,0x15c6,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x15c6,0x15c6,0x15c6,0x15c6, +0x15c6,0x15c6,0x15c6,0x15c6,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb, +0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x1cb,0x15d2,0x15d2,0x15d2,0x15d2, +0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15d2,0x15c9, +0x15cc,0x15cf,0x15d2,0x1ce,0x1ce,0x1ce,0x1ce,0x1ce,0x1ce,0x1ce,0x1ce,0x1ce,0x15e1,0x15e1,0x15e1,0x15e1, +0x15e1,0x15d5,0x15d5,0x1d1,0x1d1,0x1d1,0x1d1,0x15d8,0x15d8,0x15d8,0x15d8,0x15d8,0x15de,0x15de,0x16c8,0x15de, +0x15de,0x15de,0x15db,0x1d1,0x1d1,0x1d1,0x1d1,0x1d1,0x1d1,0x1d1,0x1d1,0x1d1,0x15ea,0x15ea,0x15ea,0x15ea, +0x15ea,0x1d4,0x1d4,0x15e7,0x15e7,0x15e7,0x15e7,0x15e7,0x15e7,0x15e7,0x15e7,0x15e7,0x15e4,0x15e4,0x15e4,0x15e4, +0x15e4,0x15e4,0x15e4,0x1d4,0x1d4,0x1d4,0x1d4,0x1d4,0x1d4,0x1d4,0x1d4,0x1d4,0x15ed,0x15ff,0x15ff,0x15f3, +0x15fc,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x15f6,0x15f6,0x15f6,0x15f6, +0x15f6,0x15f6,0x15f6,0x15f6,0x15f6,0x15f6,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1d7,0x1605,0x1605,0x1605,0x1605, +0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605, +0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1605,0x1da,0x1602,0x1602,0x1602,0x1602, +0x1602,0x1602,0x1602,0x1602,0x1602,0x1602,0x1da,0x1da,0x1da,0x1da,0x1608,0x1608,0x1b8d,0x1b8d,0x1b8d,0x1b8d, +0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1611,0x1611,0x1611,0x1611, +0x1611,0x160b,0x1614,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x160e,0x160e,0x160e,0x160e, +0x160e,0x160e,0x160e,0x160e,0x160e,0x160e,0x1611,0x1611,0x1611,0x1611,0x1611,0x1dd,0x161a,0x161a,0x161a,0x161a, +0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a, +0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x161a,0x1e0,0x1626,0x1626,0x1626,0x1626, +0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626,0x1626, +0x1626,0x1626,0x1623,0x1623,0x1623,0x1623,0x1623,0x1e3,0x1e3,0x1e3,0x1e3,0x1e3,0x163e,0x163e,0x1641,0x1641, +0x1644,0x1635,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x163b,0x163b,0x163b,0x163b, +0x163b,0x163b,0x163b,0x163b,0x163b,0x163b,0x1e6,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x1e6,0x163e, +0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e, +0x163e,0x163e,0x163e,0x163e,0x1e6,0x1e6,0x1e6,0x1e6,0x1e6,0x163e,0x163e,0x163e,0x164d,0x164d,0x164d,0x164d, +0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d, +0x164d,0x164d,0x164d,0x164d,0x164d,0x1e9,0x1e9,0x1e9,0x1e9,0x1e9,0x1e9,0x1e9,0x1656,0x1656,0x1656,0x1656, +0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1656,0x1ec,0x1ec, +0x1ec,0x1ec,0x1ec,0x1ec,0x1ec,0x1653,0x1653,0x1653,0x1653,0x1ec,0x1ec,0x1ec,0x1671,0x1671,0x1671,0x1671, +0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1659,0x166b,0x166b,0x1659,0x1659, +0x1659,0x1659,0x1f2,0x1f2,0x166b,0x166b,0x166e,0x166e,0x1659,0x1659,0x166b,0x165f,0x165c,0x1662,0x1674,0x1674, +0x1665,0x1665,0x1668,0x1668,0x1668,0x1674,0x172b,0x172b,0x172b,0x172b,0x172b,0x172b,0x172b,0x172b,0x172b,0x172b, +0x172b,0x172b,0x172b,0x172b,0x1728,0x1728,0x1728,0x1728,0x1725,0x1725,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2, +0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2, +0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f2,0x1f5,0x1677,0x1677,0x1677, +0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677,0x1677, +0x1677,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x1f5,0x167a,0x167a,0x167a,0x167a, +0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x1f8,0x1f8,0x1f8,0x1f8,0x167a,0x167a,0x167a,0x167a, +0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x1f8,0x1f8,0x1f8,0x1f8, +0x1f8,0x1f8,0x1f8,0x1f8,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x1f8,0x1f8, +0x1f8,0x1f8,0x1f8,0x1f8,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x1f8,0x1f8,0x1f8,0x1f8, +0x1f8,0x1f8,0x1f8,0x1f8,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a, +0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x1f8,0x1f8,0x1a91,0x1a91,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8, +0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8, +0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x1f8,0x167d,0x168c,0x1683,0x1680, +0x1692,0x1692,0x1686,0x1692,0x1fb,0x1fb,0x1fb,0x1fb,0x1fb,0x1fb,0x1fb,0x1fb,0x1689,0x1689,0x1689,0x1689, +0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x1fb,0x1fb,0x1fb,0x1fb,0x1fb,0x1fb,0x1698,0x1698,0x1698,0x1698, +0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1fe, +0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x169e,0x1740,0x1740,0x1740,0x1740, +0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1740, +0x1740,0x1740,0x1740,0x1740,0x1740,0x1740,0x1938,0x201,0x201,0x172e,0x172e,0x172e,0x173a,0x173a,0x172e,0x172e, +0x172e,0x172e,0x173d,0x172e,0x172e,0x172e,0x172e,0x1731,0x201,0x201,0x201,0x201,0x1737,0x1737,0x1737,0x1737, +0x1737,0x1737,0x1737,0x1737,0x1737,0x1737,0x1734,0x1734,0x1743,0x1743,0x1743,0x1734,0x1746,0x1746,0x1746,0x1746, +0x1746,0x1746,0x1746,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204, +0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204,0x204, +0x204,0x204,0x204,0x204,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758, +0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x1758,0x20a,0x1758,0x1758,0x20a,0x20a,0x20a,0x20a,0x20a,0x1755, +0x1755,0x1755,0x1755,0x1755,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x20d,0x175b,0x20d,0x175b,0x175b, +0x175b,0x175b,0x20d,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b, +0x175b,0x175b,0x20d,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175b,0x175e,0x20d,0x20d, +0x20d,0x20d,0x20d,0x20d,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7, +0x15b7,0x15b7,0x15b7,0x15b7,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767, +0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x210,0x210,0x210,0x210,0x210,0x210,0x210,0x210,0x210, +0x210,0x210,0x210,0x210,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764, +0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x210,0x210,0x210,0x210,0x210,0x210,0x210,0x1761,0x1761, +0x1761,0x1761,0x1761,0x1761,0x176d,0x176d,0x176d,0x176d,0x176a,0x176d,0x176d,0x1770,0x1773,0x1770,0x1770,0x176d, +0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x213,0x176a, +0x176a,0x176a,0x176a,0x176a,0x17ca,0x17ca,0x17ca,0x17ca,0x17c1,0x17c1,0x17c1,0x17bb,0x17be,0x17be,0x17be,0x19e6, +0x216,0x216,0x216,0x216,0x17c7,0x17c7,0x17c7,0x17c7,0x17c7,0x17c7,0x17c7,0x17c7,0x17c7,0x17c7,0x216,0x216, +0x216,0x216,0x17c4,0x17c4,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x219,0x17e5,0x17e5, +0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5, +0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e5,0x17e2,0x17d0,0x17d0,0x17d0,0x17d0,0x17d0,0x17d0,0x17d0,0x219, +0x17d0,0x17d0,0x17d0,0x17d0,0x17d0,0x17d0,0x17e2,0x17d3,0x17e5,0x17e8,0x17e8,0x17dc,0x17d9,0x17d9,0x219,0x219, +0x219,0x219,0x219,0x219,0x219,0x219,0x219,0x219,0x17df,0x17df,0x17df,0x17df,0x17df,0x17df,0x17df,0x17df, +0x17df,0x17df,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6,0x17d6, +0x17d6,0x219,0x219,0x219,0x17f4,0x17f7,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd, +0x17fd,0x17fd,0x17fd,0x17fd,0x17eb,0x17eb,0x17eb,0x17eb,0x17eb,0x17eb,0x17eb,0x17eb,0x17eb,0x21c,0x21c,0x21c, +0x21c,0x21c,0x21c,0x21c,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956, +0x1956,0x1956,0x1956,0x1956,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x21f,0x17ee,0x17ee,0x17ee,0x17ee, +0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x21f,0x21f,0x17ee, +0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x21f,0x17ee,0x17ee,0x21f,0x17ee,0x17ee,0x17ee,0x17ee,0x17ee,0x21f, +0x21f,0x21f,0x21f,0x21f,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc, +0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x183c,0x18d8,0x1a40,0x1a43,0x1aeb,0x222,0x222,0x222,0x222,0x222,0x222,0x222, +0x222,0x222,0x222,0x222,0x1ae8,0x1ae8,0x222,0x222,0x222,0x222,0x222,0x222,0x222,0x222,0x222,0x222, +0x222,0x222,0x222,0x222,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd,0x17fd, +0x17fd,0x17fd,0x17fd,0x17fd,0x225,0x225,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1, +0x17f1,0x17f1,0x17f1,0x17f1,0x225,0x17fa,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17f1,0x17fa,0x17f1,0x17f1, +0x17fa,0x17f1,0x17f1,0x225,0x225,0x225,0x225,0x225,0x225,0x225,0x225,0x225,0x1800,0x1800,0x1800,0x1800, +0x1800,0x1800,0x1800,0x1800,0x1800,0x1800,0x1800,0x1800,0x1800,0x228,0x228,0x228,0x228,0x228,0x228,0x228, +0x228,0x228,0x228,0x228,0x228,0x228,0x228,0x228,0x228,0x228,0x228,0x228,0x1818,0x1818,0x1809,0x1803, +0x1803,0x1818,0x1806,0x181b,0x181b,0x181b,0x181b,0x181e,0x181e,0x1812,0x180f,0x180c,0x1815,0x1815,0x1815,0x1815, +0x1815,0x1815,0x1815,0x1815,0x1815,0x1815,0x1a94,0x1812,0x22b,0x180c,0x193b,0x19e9,0x1a97,0x1a97,0x22b,0x22b, +0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b, +0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x22b,0x1824,0x1824,0x1824,0x1824, +0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824,0x1824, +0x22e,0x22e,0x22e,0x22e,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821, +0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821,0x1821, +0x22e,0x22e,0x22e,0x22e,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f, +0x183f,0x19b9,0x19b9,0x19b9,0x19b9,0x19b9,0x1a46,0x1a46,0x1a46,0x1a46,0x1a46,0x1a46,0x231,0x231,0x231,0x231, +0x231,0x231,0x231,0x231,0x1bba,0x1bba,0x1bba,0x234,0x234,0x234,0x234,0x234,0x234,0x234,0x234,0x234, +0x234,0x234,0x234,0x234,0x276,0x276,0x1c2f,0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x276, +0x276,0x276,0x276,0x276,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x237,0x187e,0x187e,0x237,0x187e, +0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e,0x187e, +0x187e,0x187e,0x187e,0x187e,0x187e,0x1872,0x1872,0x1872,0x1872,0x1872,0x1872,0x237,0x237,0x237,0x1872,0x237, +0x1872,0x1872,0x237,0x1872,0x1872,0x1872,0x1875,0x1872,0x1878,0x1878,0x1881,0x1872,0x237,0x237,0x237,0x237, +0x237,0x237,0x237,0x237,0x187b,0x187b,0x187b,0x187b,0x187b,0x187b,0x187b,0x187b,0x187b,0x187b,0x237,0x237, +0x237,0x237,0x237,0x237,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1, +0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1, +0x23a,0x23a,0x23a,0x23a,0x1890,0x1893,0x1893,0x23d,0x23d,0x23d,0x23d,0x23d,0x23d,0x23d,0x23d,0x23d, +0x23d,0x23d,0x23d,0x23d,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96,0x1b96, +0x1b96,0x1b96,0x1b96,0x1b96,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x18a2,0x240, +0x240,0x240,0x240,0x240,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63, +0x1b63,0x1b63,0x1b63,0x1b63,0x18ae,0x18b1,0x18c0,0x18c0,0x18b1,0x18b4,0x18ae,0x18ab,0x243,0x243,0x243,0x243, +0x243,0x243,0x243,0x243,0x1899,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1896,0x1896,0x1884,0x1884,0x1884, +0x1899,0x1899,0x1899,0x1899,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef, +0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x246,0x246,0x246,0x246,0x246,0x246,0x246,0x246, +0x246,0x246,0x246,0x246,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941,0x1941, +0x1941,0x1941,0x246,0x246,0x1a4f,0x1a4f,0x1a4f,0x1a4f,0x1af1,0x1c35,0x1c35,0x1c35,0x1a4f,0x1a4f,0x1a4f,0x1bbd, +0x1bbd,0x279,0x279,0x279,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953, +0x1950,0x1950,0x1950,0x1944,0x1944,0x1944,0x1944,0x1944,0x1944,0x1944,0x1944,0x1944,0x1950,0x194a,0x1947,0x194d, +0x249,0x249,0x249,0x249,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956, +0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x1956,0x24c, +0x24c,0x1956,0x1956,0x1956,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x24f,0x1965,0x1965,0x24f,0x1965,0x1965, +0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1965, +0x1965,0x1965,0x1965,0x1965,0x1965,0x1965,0x1962,0x1962,0x1962,0x1962,0x1962,0x24f,0x1959,0x1959,0x24f,0x1962, +0x1962,0x1959,0x1962,0x195c,0x1965,0x24f,0x24f,0x24f,0x24f,0x24f,0x24f,0x24f,0x196e,0x196e,0x1971,0x1971, +0x1968,0x1968,0x1968,0x1968,0x252,0x252,0x252,0x252,0x252,0x252,0x252,0x252,0x196b,0x196b,0x196b,0x196b, +0x196b,0x196b,0x196b,0x196b,0x196b,0x196b,0x252,0x252,0x252,0x252,0x252,0x252,0x1974,0x1974,0x1974,0x1974, +0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1977,0x1974,0x1974,0x1974,0x1977,0x1974,0x1974,0x1974, +0x1974,0x255,0x255,0x255,0x255,0x255,0x255,0x255,0x255,0x255,0x255,0x255,0x1980,0x1980,0x1980,0x1980, +0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x1980,0x197a, +0x197a,0x197d,0x197d,0x1983,0x1983,0x258,0x258,0x258,0x258,0x258,0x258,0x258,0x1986,0x1986,0x1986,0x1986, +0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986,0x1986, +0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x25b,0x1989,0x1989,0x1989,0x1989, +0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989,0x1989, +0x1989,0x1989,0x1989,0x198c,0x1995,0x1989,0x1989,0x25e,0x25e,0x25e,0x25e,0x25e,0x1998,0x1998,0x1998,0x1998, +0x1998,0x1998,0x1998,0x199b,0x261,0x261,0x261,0x261,0x261,0x261,0x261,0x261,0x19a4,0x19a4,0x19a4,0x19a4, +0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x19a4,0x199e,0x199e, +0x199e,0x199e,0x199e,0x199e,0x199e,0x199e,0x199e,0x199e,0x199e,0x19a1,0x19a1,0x19a1,0x19a1,0x19a7,0x19a7,0x19a7, +0x19a7,0x19a7,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264,0x264, +0x264,0x264,0x264,0x264,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84,0x1b84, +0x1b84,0x1b84,0x1b84,0x1b84,0x1be7,0x1bed,0x1bed,0x1bed,0x1bed,0x1bed,0x1bed,0x1bea,0x1bea,0x1bea,0x1bea,0x1bea, +0x1bea,0x1bea,0x1bea,0x1bea,0x1bea,0x1bea,0x1bea,0x1bea,0x1bea,0x1bea,0x267,0x267,0x267,0x267,0x267,0x267, +0x267,0x267,0x267,0x267,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb, +0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x19fb,0x26a,0x26a,0x26a,0x26a,0x26a, +0x26a,0x26a,0x26a,0x26a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x26d,0x26d,0x1a0a,0x1a0a, +0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a, +0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a0a,0x1a07,0x1a07,0x1a07,0x19fe,0x19fe,0x19fe,0x19fe,0x26d,0x26d,0x19fe,0x19fe, +0x1a07,0x1a07,0x1a07,0x1a07,0x1a01,0x1a0a,0x1a04,0x1a0a,0x1a07,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d, +0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d,0x26d, +0x26d,0x26d,0x26d,0x26d,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16, +0x1a16,0x270,0x270,0x270,0x1a0d,0x1a0d,0x1a0d,0x1a0d,0x1a0d,0x1a0d,0x1a0d,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16, +0x1a19,0x1a19,0x270,0x270,0x273,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c, +0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c, +0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x1a1c,0x273,0x273,0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x276, +0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x1a49,0x1a49,0x1a49,0x276,0x276,0x1c32,0x276,0x276, +0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x276,0x1a4c,0x1a4c,0x1a4c,0x1a4c,0x276,0x276,0x276,0x276, +0x276,0x276,0x276,0x276,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1, +0x18e1,0x18e1,0x18e1,0x18e1,0x1a4f,0x1a4f,0x1a4f,0x1af1,0x1af1,0x1af1,0x1af1,0x1c35,0x1c35,0x279,0x279,0x279, +0x279,0x279,0x279,0x279,0x1a4f,0x1a4f,0x1a4f,0x1a4f,0x1a4f,0x1a4f,0x1af1,0x1af1,0x1af1,0x1af1,0x1af1,0x1af1, +0x1af1,0x1af1,0x1af1,0x1af1,0x1af1,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1c35,0x1c35,0x1c35,0x1af1,0x1af1,0x1af1,0x1af1, +0x1af1,0x1af1,0x1af1,0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1c35,0x1c35,0x1c35,0x279,0x1c35,0x1af1,0x1af1,0x1af1,0x1bc0, +0x1bc0,0x1bc0,0x279,0x279,0x279,0x279,0x279,0x279,0x279,0x279,0x1c35,0x1c35,0x1af1,0x1af1,0x1af1,0x1af1, +0x1af1,0x1af1,0x1af1,0x1bbd,0x1bbd,0x1bbd,0x1c35,0x1c35,0x279,0x279,0x279,0x279,0x1bbd,0x1bbd,0x1bbd,0x1bbd, +0x1bbd,0x1bbd,0x1bbd,0x1bbd,0x1c35,0x279,0x279,0x279,0x279,0x279,0x279,0x279,0x1bc0,0x1bc0,0x1bc0,0x1bc0, +0x1bc0,0x1bc0,0x1bc0,0x1c38,0x1c38,0x279,0x279,0x279,0x279,0x279,0x279,0x279,0x1a25,0x1a1f,0x1a1f,0x1a1f, +0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x27c,0x27c, +0x27c,0x27c,0x27c,0x27c,0x27c,0x27c,0x27c,0x27c,0x27c,0x27c,0x27c,0x1a22,0x1a31,0x1a31,0x1a31,0x1a31, +0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a28,0x1a28,0x1a28,0x1a28,0x1a2e,0x1a2e,0x1a2e,0x1a2e, +0x1a2e,0x1a2e,0x1a2e,0x1a2e,0x1a2e,0x1a2e,0x27f,0x27f,0x27f,0x27f,0x27f,0x1a2b,0x1a9d,0x1a9d,0x1a9d,0x1a9d, +0x1a9d,0x1a9a,0x1a9a,0x1a9a,0x1a9a,0x1a9a,0x1a9a,0x1a9a,0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x282, +0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x282,0x1ab8,0x1ab8,0x1ab8,0x1ab8, +0x1ab8,0x1ab8,0x1ab8,0x285,0x285,0x1ab8,0x285,0x285,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8, +0x285,0x1ab8,0x1ab8,0x285,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1ab8, +0x1ab8,0x1ab8,0x1ab8,0x1ab8,0x1aa0,0x1aaf,0x1aaf,0x1aaf,0x1aaf,0x1aaf,0x285,0x1aaf,0x1ab2,0x285,0x285,0x1aa0, +0x1aa0,0x1ab5,0x1aa6,0x1abb,0x1aaf,0x1abb,0x1aaf,0x1aa3,0x1abe,0x1aa9,0x1abe,0x285,0x285,0x285,0x285,0x285, +0x285,0x285,0x285,0x285,0x1aac,0x1aac,0x1aac,0x1aac,0x1aac,0x1aac,0x1aac,0x1aac,0x1aac,0x1aac,0x285,0x285, +0x285,0x285,0x285,0x285,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7, +0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x288,0x288,0x288,0x288,0x288,0x288, +0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288, +0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x288,0x1ac4,0x1ac4,0x1ac4,0x1ac4, +0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x28e, +0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x28e, +0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e,0x28e, +0x28e,0x28e,0x28e,0x28e,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x1ac7,0x28e,0x28e, +0x28e,0x28e,0x28e,0x28e,0x1afa,0x1afa,0x1afa,0x1afa,0x1afa,0x1afa,0x1afa,0x1afa,0x1afa,0x291,0x291,0x291, +0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291, +0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291,0x291, +0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x294,0x1aca,0x1aca,0x1acd,0x294,0x294, +0x1ad0,0x1ad0,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294,0x294, +0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b60,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x1b63,0x297, +0x1b66,0x1b66,0x297,0x297,0x297,0x297,0x297,0x297,0x1b5d,0x1b5d,0x1b5d,0x1b5d,0x1b5d,0x1b5d,0x1b5d,0x1b5d, +0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c, +0x1b6c,0x1b69,0x1b69,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a,0x29a, +0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x29d,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x29d,0x1b6f,0x1b6f,0x29d, +0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x1b6f,0x29d, +0x1b72,0x1b78,0x1b78,0x1b75,0x1b75,0x1b75,0x2a3,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75, +0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75, +0x1b75,0x2a3,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x1b75,0x2a3,0x2a3,0x2a3,0x2a3,0x2a3, +0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7e,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b, +0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x1b7b,0x2a6, +0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x1bf0,0x1bf0,0x1bf0,0x1bf0,0x1bf0,0x1bf0,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6, +0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6, +0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x2a6,0x1b84,0x1b84,0x1b81,0x1b81, +0x1b81,0x1b81,0x1b87,0x1b87,0x1b87,0x1b87,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9, +0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x2a9,0x1a9d,0x1a9d,0x1a9d,0x1a9d, +0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1a9d,0x1b8d,0x1b8d,0x1b8d,0x1b8d, +0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d, +0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x2ac,0x1b8a,0x1b8a,0x1b8a,0x1b8a, +0x1b8a,0x1b8a,0x1b8a,0x1b8a,0x1b8a,0x1b8a,0x2ac,0x2ac,0x2ac,0x2ac,0x2ac,0x2ac,0x1554,0x1554,0x1554,0x1554, +0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1554,0x1b93,0x1b93,0x1b93,0x1b93, +0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b93,0x1b90,0x2af,0x2af,0x2af,0x2af,0x2af, +0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x2af,0x1b9c,0x1b9c,0x1b9c,0x1b9c, +0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x2b2,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x1b9c,0x2b2, +0x1b9c,0x1b9c,0x2b2,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x2b2,0x1b99, +0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x2b2,0x1b99, +0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x1b99,0x2b2,0x1b99,0x1b99,0x2b2,0x2b2,0x2b2,0x1ba2,0x1ba2,0x1ba2,0x1ba2, +0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x2b5,0x2b5,0x1ba2,0x1ba2,0x1ba2,0x1ba2, +0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x2b5, +0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x2b5,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f, +0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8, +0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8, +0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x2b8,0x1bf3,0x1bf3,0x1bf3,0x1bf9,0x1bf9,0x1bf9,0x1bf9, +0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bfc,0x1bfc,0x1bfc,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb, +0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x2bb,0x1bff,0x1bff,0x1bff,0x1bff, +0x1bff,0x1bff,0x1bff,0x1bff,0x1bff,0x1bff,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be, +0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be, +0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x2be,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02, +0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x1c02,0x2c1,0x2c1,0x2c1,0x2c1, +0x2c1,0x2c1,0x2c1,0x2c1,0x2c1,0x2c1,0x2c1,0x2c1,0x1c05,0x1c05,0x1c1a,0x1c11,0x1c17,0x1c17,0x1c17,0x1c17, +0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x2c4,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17, +0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17,0x1c17, +0x1c11,0x1c11,0x1c05,0x1c05,0x1c05,0x1c05,0x1c05,0x2c4,0x2c4,0x2c4,0x1c11,0x1c11,0x1c05,0x1c14,0x1c08,0x1c1d, +0x1c1d,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0b,0x1c0e,0x1c0e,0x1c0e,0x1c0e, +0x1c0e,0x1c0e,0x1c0e,0x1c0e,0x1c0e,0x1c0e,0x2c4,0x2c4,0x2c4,0x2c4,0x2c4,0x2c4,0x1c26,0x1c26,0x1c26,0x1c26, +0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c26,0x1c20,0x1c20,0x1c20,0x1c20,0x1c23,0x1c23,0x1c23,0x1c23, +0x1c23,0x1c23,0x1c23,0x1c23,0x1c23,0x1c23,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2ca,0x2ca,0x2ca,0x2ca, +0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca, +0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x18db,0x2dc,0x2dc,0x2dc, +0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2dc,0x2ca,0x2ca,0x2ca,0x2ca, +0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca, +0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x95d,0x95d,0x1c3b,0x1c3b,0x1c3b,0x1c3b, +0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x2ca,0x2ca,0x2ca,0x2ca, +0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0x2ca,0xc6c,0xc6c,0xc6c,0xc6c, +0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0x129f,0x129f,0x129f,0x2cd,0x2cd,0xe94,0xe94,0xe94,0xe94, +0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94, +0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd, +0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd, +0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0x2cd,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88, +0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88, +0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0x2d0,0x2d0,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1, +0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x1bab,0x1bab,0x1bab, +0x1bab,0x1c29,0x2d3,0x2d3,0x2d3,0x2d3,0x2d3,0x2d3,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8, +0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8, +0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x2d6,0x2d6,0x1785,0x1785,0x2d9,0x2d9,0x2d9,0x2d9,0x2d9,0x2d9, +0x2d9,0x2d9,0x2d9,0x2d9,0x2d9,0x2d9,0x2d9,0x2d9,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db, +0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4, +0x1af4,0x1af4,0x1af4,0x2df,0x2df,0x2df,0x2df,0x2df,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b, +0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x3e7,0x3db,0x3db,0x3db,0x3db,0x3db,0x3db,0x3db, +0x3db,0x3e7,0x3e7,0x3e7,0x3e7,0x3e1,0x111f,0x12f6,0x3ea,0x927,0x92a,0x3d8,0x3d8,0x111c,0x12f3,0x12f3, +0x3ed,0x3ed,0x3ed,0x3ed,0x3ed,0x3ed,0x3ed,0x3ed,0x111c,0x3db,0x3db,0x3e7,0xcae,0x3ea,0x3ea,0x3ea, +0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea, +0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3db,0x3db,0x8af,0x8b2,0x945,0x945, +0x945,0x945,0x945,0x945,0x945,0x945,0x945,0x945,0x3e4,0xf7e,0xf7b,0x12f9,0x12f9,0x12f9,0x12f9,0x12f9, +0x14b8,0x1122,0x1122,0xed0,0xed0,0xda1,0xed0,0xed0,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea, +0x3ea,0x3ed,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ea,0x3ed,0x3ea,0x3ea,0x3ed,0x3ea,0x3ea,0x3ea, +0x3ea,0x3ea,0x12f3,0x12f6,0x3de,0x3ea,0x3e7,0x3e7,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489, +0x489,0x12ff,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489, +0x489,0x489,0x12ff,0x1857,0x1857,0xf9c,0x47a,0x483,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5, +0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0x4c5,0xba3, +0xba3,0xdb0,0xdb0,0x8b5,0xdad,0x13da,0x13da,0x13da,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8, +0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8, +0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4c8,0x4ce,0x4ce,0x4ce,0x1137,0x1137,0x1137,0x1137,0x1137, +0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb, +0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb,0x4cb, +0x4cb,0x4cb,0x1134,0x1134,0x1134,0x1134,0x1134,0x1134,0x4d1,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce, +0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce, +0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4ce,0x4da,0x4d4,0x4da,0x4d4, +0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4, +0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4d4,0x4d4, +0x4d4,0x4d4,0x4d7,0x9a2,0xfc9,0xfc9,0xfcc,0xfc9,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4, +0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4,0x4da,0x4d4, +0x4da,0x4d4,0xfcc,0xfc9,0xfcc,0xfc9,0xfcc,0xfc9,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6, +0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6,0x4e6, +0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x4e9,0x6a5,0x6a5,0x6a8,0x504,0x6b4,0x6b1,0x6b1,0x6ae, +0x52e,0x52e,0x4ec,0x4ec,0x4ec,0x4ec,0x4ec,0xb34,0x6b7,0x510,0x6cf,0x6d2,0x525,0x6b7,0x513,0x513, +0x504,0x51f,0x51f,0x6a5,0x52b,0x528,0x6ab,0x4fe,0x4f5,0x4f5,0x4f8,0x4f8,0x4f8,0x4f8,0x4f8,0x4fb, +0x4f8,0x4f8,0x4f8,0x4ef,0x537,0x534,0x531,0x531,0x6c3,0x519,0x516,0x6c0,0x6bd,0x6ba,0x6cc,0x507, +0x6c9,0x6c9,0x51c,0x51f,0x6c6,0x6c6,0x51c,0x51f,0x501,0x504,0x504,0x504,0x522,0x50d,0x50a,0xbb8, +0xad7,0xad7,0xad4,0xad4,0xad4,0xad4,0xbaf,0xbaf,0xbaf,0xbaf,0xbb5,0xcdb,0xcd8,0xdbc,0xdbf,0xbb2, +0xdbf,0xdbf,0xdbf,0xdbf,0xdbc,0xdbf,0xdbf,0xbac,0x55b,0x55b,0x55b,0x55b,0x55b,0x55b,0x55b,0x558, +0x55e,0x73e,0x55b,0x9a5,0x9c6,0xada,0xada,0xada,0xbbe,0xbbe,0xdc5,0xdc5,0xdc5,0xdc5,0x1140,0x1143, +0x1143,0x1314,0x14a6,0x14d0,0x14d3,0x14d3,0x16da,0x185a,0x56a,0x56a,0x582,0x6e4,0x567,0x6de,0x56a,0x57f, +0x567,0x6e4,0x579,0x582,0x582,0x582,0x579,0x579,0x582,0x582,0x582,0x6ea,0x567,0x582,0x6e7,0x567, +0x576,0x582,0x582,0x582,0x582,0x582,0x567,0x567,0x56d,0x6de,0x6e1,0x567,0x582,0x567,0x6ed,0x567, +0x582,0x570,0x588,0x6f0,0x582,0x582,0x573,0x579,0x582,0x582,0x585,0x582,0x579,0x57c,0x57c,0x57c, +0x57c,0xae3,0xae0,0xcde,0xdce,0xbd3,0xbd6,0xbd6,0xbd0,0xbcd,0xbcd,0xbcd,0xbcd,0xbd6,0xbd3,0xbd3, +0xbd3,0xbd3,0xbca,0xbcd,0xdcb,0xedc,0xedf,0xfd2,0x1146,0x1146,0x1146,0x6f6,0x6f3,0x58b,0x58e,0x58e, +0x58e,0x58e,0x58e,0x6f3,0x6f6,0x6f6,0x6f3,0x58e,0x6fc,0x6fc,0x6fc,0x6fc,0x6fc,0x6fc,0x6fc,0x6fc, +0x6fc,0x6fc,0x6fc,0x6fc,0x597,0x597,0x597,0x597,0x6f9,0x6f9,0x6f9,0x6f9,0x6f9,0x6f9,0x6f9,0x6f9, +0x6f9,0x6f9,0x591,0x591,0x591,0x591,0x591,0x591,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d, +0x59a,0x59d,0x59d,0x59d,0x59d,0x59d,0x5a0,0x59a,0x59d,0x59d,0x59a,0x59a,0x59a,0x59a,0x59d,0x59d, +0x6ff,0x6ff,0x59a,0x59a,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d, +0x59d,0x5a0,0x5a0,0x5a0,0x59d,0x59d,0x702,0x59d,0x702,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d,0x59d, +0x59a,0x59d,0x59a,0x59a,0x59a,0x59a,0x59a,0x59a,0x59d,0x59d,0x59a,0x6ff,0x59a,0x59a,0x59a,0xae9, +0xae9,0xae9,0xae9,0xae9,0xae9,0xae9,0xae9,0xae9,0xbd9,0xbd9,0xbd9,0xbd9,0xbd9,0xbd9,0xbd9,0xbd9, +0xbd9,0xbd9,0xbd9,0xbd9,0x705,0x5a3,0x705,0x705,0x5a6,0x5a3,0x5a3,0x705,0x705,0x5a6,0x5a3,0x705, +0x5a6,0x5a3,0x5a3,0x705,0x5a3,0x705,0x5b2,0x5af,0x5a3,0x705,0x5a3,0x5a3,0x5a3,0x5a3,0x705,0x5a3, +0x5a3,0x705,0x705,0x705,0x705,0x5a3,0x5a3,0x705,0x5a6,0x705,0x5a6,0x705,0x705,0x705,0x705,0x705, +0x70b,0x5a9,0x705,0x5a9,0x5a9,0x5a3,0x5a3,0x5a3,0x705,0x705,0x705,0x705,0x5a3,0x5a3,0x5a3,0x5a3, +0x705,0x705,0x5a3,0x5a3,0x5a3,0x5a6,0x5a3,0x5a3,0x5a6,0x5a3,0x5a3,0x5a6,0x705,0x5a6,0x5a3,0x5a3, +0x705,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x705,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3, +0x5a3,0x5a3,0x5a3,0x5a3,0x708,0x705,0x5a6,0x5a3,0x705,0x705,0x705,0x705,0x5a3,0x5a3,0x705,0x705, +0x5a3,0x5a6,0x708,0x708,0x5a6,0x5a6,0x5a3,0x5a3,0x5a6,0x5a6,0x5a3,0x5a3,0x5a6,0x5a6,0x5a3,0x5a3, +0x5a3,0x5a3,0x5a3,0x5a3,0x5a6,0x5a6,0x705,0x705,0x5a6,0x5a6,0x705,0x705,0x5a6,0x5a6,0x5a3,0x5a3, +0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x705,0x5a3,0x5a3,0x5a3,0x705,0x5a3,0x5a3, +0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x705,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a6,0x5a6,0x5a6,0x5a6, +0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x705, +0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3, +0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3, +0x5a6,0x5a6,0x5a6,0x5a6,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a3,0x5a6,0x5a6,0x5a6,0x5a6,0x5a3,0x5ac, +0x5a3,0x5a3,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc,0xbdc, +0x5b5,0xaec,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5be,0x5bb,0x5be,0x5bb,0x5b5,0x5b5,0x5b5,0x5b5, +0x5b5,0x5b5,0x70e,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x7fe,0x7fe,0x5b5,0x5b5,0x5b5,0x5b5, +0x5b8,0x5b8,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x804,0x801,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5, +0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5, +0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0x5b5,0xaec, +0xbe2,0xaec,0xaec,0xaec,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1, +0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1,0x5c1, +0x5c1,0x5c1,0x5c1,0x5c1,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x5c7,0xc3f, +0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f,0xc3f, +0xc3f,0xc3f,0xc3f,0xd4d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d, +0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x5ca,0x5cd,0x5cd,0x5cd,0x5cd,0x5cd,0x5cd,0x5cd, +0x5cd,0x5cd,0x5cd,0x5cd,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d, +0x5cd,0x5cd,0x5cd,0x5cd,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d, +0x71d,0x71d,0x71d,0x71d,0x720,0x720,0x720,0x720,0x720,0x720,0x720,0x720,0x720,0x720,0x720,0x720, +0x720,0x720,0x720,0x720,0x5d0,0x5d0,0x720,0x720,0x720,0x720,0xbe5,0xbe5,0xbe5,0xbe5,0xbe5,0xbe5, +0xbe5,0xbe5,0xbe5,0xbe5,0x726,0x726,0x5d3,0x723,0x723,0x723,0x723,0x723,0x723,0x723,0x5d3,0x5d3, +0x5d3,0x5d3,0x5d6,0x5d6,0x5d6,0x5d6,0x726,0x726,0x5d6,0x5d6,0x726,0x726,0x5d3,0x5d3,0x5d3,0x5d3, +0x726,0x726,0x5d6,0x5d6,0x726,0x726,0x5d3,0x5d3,0x5d3,0x5d3,0x726,0x726,0x723,0x5d3,0x5d6,0x726, +0x5d3,0x5d3,0x723,0x726,0x726,0x726,0x5d6,0x5d6,0x5d3,0x5d3,0x5d3,0x5d3,0x5d3,0x5d3,0x5d3,0x5d3, +0x5d3,0x5d3,0x5d3,0x5d3,0x5d3,0x5d3,0x726,0x723,0x726,0x723,0x5d3,0x5d6,0x5d6,0x5d6,0x5d6,0x5d6, +0x5d6,0x5d3,0x5d3,0x723,0xaf2,0xaf2,0xaf2,0xaf2,0xaf2,0xaf2,0xaf2,0xaf2,0xbe8,0xbe8,0xbe8,0xbe8, +0xbe8,0xc57,0xc57,0xbe8,0x5dc,0x5dc,0x5dc,0x5dc,0x5d9,0x72f,0x72f,0x5d9,0x5d9,0x729,0x5d9,0x5d9, +0x5d9,0x5d9,0x729,0x729,0x5d9,0x5d9,0x5d9,0x5d9,0xd56,0xd56,0xbeb,0xbeb,0xdd7,0xaf5,0x5dc,0x5dc, +0x72c,0x5df,0x72c,0x5dc,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9, +0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5dc,0x5dc,0x5dc, +0x5d9,0x5d9,0x5d9,0x5d9,0x72f,0x5d9,0x72f,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x807,0x807,0x807,0x807, +0x807,0x807,0x807,0x807,0x807,0x807,0x807,0x807,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9,0x5d9, +0x5d9,0x5d9,0x5d9,0x5d9,0x72f,0x72f,0x5e2,0x72f,0x729,0x729,0x5d9,0x729,0x72c,0x729,0x729,0x5d9, +0x729,0x72f,0x5e2,0x72f,0xaf5,0xaf5,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee, +0xbee,0xbee,0xdd4,0xe8b,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5, +0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e5,0x5e8,0x139b,0x139b,0x139b,0x5e8,0x5e8,0x5e8,0x5e8, +0x5e8,0x5e8,0x5e8,0x5e8,0x14d9,0x5ee,0x5ee,0x5ee,0x5ee,0x139b,0x5e8,0x5e8,0x5ee,0x5ee,0x139e,0x139e, +0x5f4,0x5f4,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8, +0x5e8,0x5e8,0x5e8,0x5e8,0x139b,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8, +0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x735,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8, +0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x139b,0x5e8,0x139b,0x5e8,0x5e8,0x5e8,0x5e8,0x139b, +0x139b,0x139b,0x5e8,0x1299,0x5e8,0x5e8,0x5e8,0x5f1,0x5f1,0x5f1,0x5f1,0x1320,0x1320,0x5e8,0x5eb,0x5eb, +0x5ee,0x5e8,0x5e8,0x5e8,0xbf4,0xbf1,0xbf4,0xbf1,0xbf4,0xbf1,0xbf4,0xbf1,0xbf4,0xbf1,0xbf4,0xbf1, +0xbf4,0xbf1,0x732,0x732,0x732,0x732,0x732,0x732,0x732,0x732,0x732,0x732,0x5e8,0x5e8,0x5e8,0x5e8, +0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x139b,0x5e8,0x5e8,0x5e8, +0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x5e8,0x139b,0x615,0x615,0x615,0x615, +0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615, +0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x60c,0x60c,0x60c,0x60c,0x60c,0x60c, +0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f, +0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0xb46,0xb46,0xb46,0xb46, +0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0xb46,0x615,0x615,0x95a,0x615, +0x615,0x615,0x615,0x615,0x615,0x615,0x60c,0x60c,0xbf7,0xd7a,0x1b0f,0x1b0f,0x612,0x618,0x615,0x60f, +0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f, +0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f,0x615,0x60f,0x615,0x60f, +0x615,0x60f,0x615,0x60f,0x615,0x60f,0x615,0x60f,0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f, +0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f,0x615,0x60f,0x612,0x618,0x615,0x60f,0x615,0x60f, +0x612,0x618,0x615,0x60f,0x612,0x618,0x615,0x60f,0x615,0x60f,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323, +0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x615,0x60f,0x615,0x60f,0x615,0x60f,0x612,0x618, +0x612,0x618,0x615,0x60f,0x615,0x60f,0x615,0x60f,0x615,0x60f,0x615,0x60f,0x615,0x60f,0x615,0x60f, +0x612,0x615,0x60f,0x612,0x615,0x60f,0x612,0x618,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f, +0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x612, +0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615, +0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f, +0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x612,0x612,0x612,0x612,0x612,0x612,0x612, +0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x60f,0x615,0x918,0x91b,0x1b0f,0x1b0f,0x1b0f,0x1b0f, +0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x1b0f,0x612,0x60f,0x612,0x612, +0x612,0x612,0x612,0x612,0x60f,0x612,0x60f,0x60f,0x612,0x612,0x60f,0x60f,0x612,0x612,0x60f,0x612, +0x60f,0x612,0x60f,0x60f,0x612,0x60f,0x60f,0x612,0x60f,0x612,0x60f,0x60f,0x612,0x60f,0x612,0x612, +0x60f,0x60f,0x60f,0x612,0x60f,0x60f,0x60f,0x60f,0x60f,0x612,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f, +0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f,0x60f, +0x612,0x612,0x60f,0x60f,0x612,0x60f,0x612,0x60f,0x60f,0x60f,0x60f,0x60f,0x612,0x612,0x612,0x612, +0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612, +0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x612,0x618, +0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615, +0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615, +0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618,0x618, +0x618,0x618,0x618,0x618,0x618,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615,0x615, +0x61b,0x61b,0x61b,0x61b,0xfde,0xfde,0xfde,0x14dc,0x14dc,0x14dc,0x14dc,0x14dc,0x14dc,0x14dc,0x16e0,0x16e0, +0x864,0x86a,0x86a,0x876,0x876,0x867,0x85e,0x867,0x85e,0x867,0x85e,0x867,0x85e,0x867,0x85e,0x867, +0x62a,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x62a, +0x624,0x627,0x62d,0x62a,0x624,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627, +0x62d,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627, +0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627, +0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627,0x62d,0x62a,0x624,0x627,0x714,0x714,0x714,0x714, +0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714,0x714, +0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711, +0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711,0x711, +0x711,0x711,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a,0x71a, +0x71a,0x71a,0x71a,0x71a,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717,0x717, +0x717,0x717,0x717,0x717,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d, +0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d,0x71d, +0x71d,0x71d,0x71d,0x71d,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738, +0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738,0x738, +0x738,0x738,0x738,0x738,0xc45,0x8c7,0x8c1,0x8be,0x8c4,0x8bb,0x74d,0x750,0x750,0x750,0x750,0x750, +0x750,0x750,0x750,0x750,0x8cd,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d, +0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x74d, +0x74d,0x74d,0x74d,0x74d,0x74d,0x74d,0x8ca,0x8ca,0x753,0x8dc,0x8df,0x8e5,0x80a,0x816,0x8fa,0x813, +0x8d3,0x8d0,0x8d3,0x8d0,0x8d9,0x8d6,0x8d9,0x8d6,0x8d3,0x8d0,0x810,0x8e5,0x8d3,0x8d0,0x8d3,0x8d0, +0x8d3,0x8d0,0x8d3,0x8d0,0x8eb,0x8f1,0x8ee,0x8ee,0x759,0x795,0x795,0x795,0x795,0x795,0x795,0x78f, +0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f,0x78f, +0x78f,0x78f,0x78f,0x75c,0x777,0x756,0x77d,0x780,0x77a,0x792,0x792,0x792,0x792,0x792,0x792,0x78c, +0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c,0x78c, +0x78c,0x78c,0x78c,0x75c,0x777,0x756,0x777,0xc48,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8, +0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8, +0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x7f8,0x1293,0x1293,0x1293,0x1293,0x1293,0x7fb, +0x810,0x813,0x813,0x813,0x813,0x813,0x813,0x813,0x813,0x813,0x933,0x933,0x933,0x933,0x819,0x819, +0x8e8,0x8f7,0x8f7,0x8f7,0x8f7,0x8f4,0x80d,0x8e2,0xb19,0xb19,0xb19,0xc5a,0xc78,0xc75,0xb37,0x8b8, +0x81f,0x81c,0x81f,0x822,0x81c,0x81f,0x81c,0x81f,0x81c,0x81f,0x81c,0x81c,0x81c,0x81c,0x81c,0x81c, +0x81f,0x81f,0x81c,0x81f,0x81f,0x81c,0x81f,0x81f,0x81c,0x81f,0x81f,0x81c,0x81f,0x81f,0x81c,0x81c, +0xc7b,0x831,0x82b,0x831,0x82b,0x831,0x82b,0x831,0x82b,0x831,0x82b,0x82b,0x82e,0x82b,0x82e,0x82b, +0x82e,0x82b,0x82e,0x82b,0x82e,0x82b,0x82e,0x82b,0x82e,0x82b,0x82e,0x82b,0x82e,0x82b,0x82e,0x82b, +0x82e,0x82b,0x82e,0x831,0x82b,0x82e,0x82b,0x82e,0x82b,0x82e,0x82b,0x82b,0x82b,0x82b,0x82b,0x82b, +0x82e,0x82e,0x82b,0x82e,0x82e,0x82b,0x82e,0x82e,0x82b,0x82e,0x82e,0x82b,0x82e,0x82e,0x82b,0x82b, +0x82b,0x82b,0x82b,0x831,0x82b,0x831,0x82b,0x831,0x82b,0x82b,0x82b,0x82b,0x82b,0x82b,0x831,0x82b, +0x82b,0x82b,0x82b,0x82b,0x82e,0x831,0x831,0x82e,0x82e,0x82e,0x82e,0x900,0x903,0x834,0x837,0xc63, +0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d, +0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d, +0x840,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d, +0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x83d,0x849,0x849,0x849,0x849, +0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849, +0x849,0x849,0x849,0x849,0x849,0x849,0x849,0x849,0xd5f,0xd5f,0xe8e,0x843,0x90c,0x90c,0x90c,0x90c, +0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0xd59,0xd59,0xd59,0xd59,0x84c,0x84c,0x84c,0x84c, +0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c, +0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x84c,0x1a58,0x912,0x912,0x912,0x912, +0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x84f,0x84f,0x84f, +0x84f,0x84f,0x84f,0xd62,0xd62,0xd62,0xd62,0x915,0x915,0x915,0x915,0x915,0x84f,0x84f,0x84f,0x84f, +0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f, +0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0x84f,0xd62,0xd62, +0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852, +0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852,0x852, +0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855, +0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855, +0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0x855,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91, +0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91,0xe91, +0x1104,0x1104,0x1104,0x1104,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858, +0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x858, +0x858,0x858,0x858,0x858,0x858,0x858,0x85b,0x85b,0x858,0x85b,0x858,0x85b,0x85b,0x858,0x858,0x858, +0x858,0x858,0x858,0x858,0x858,0x858,0x858,0x85b,0x858,0x85b,0x858,0x85b,0x85b,0x858,0x858,0x85b, +0x85b,0x85b,0x858,0x858,0x858,0x858,0x1497,0x1497,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c, +0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c, +0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c, +0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x90c,0x12d2,0x12d2,0x12d2,0x12d2,0x127b,0x127b,0x127b,0x127b, +0x127b,0x127b,0x127b,0x127b,0xd59,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66, +0xc66,0xc66,0xc66,0xc66,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f, +0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f,0x90f, +0x90f,0x90f,0x90f,0x90f,0x90f,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66,0xc66, +0xc66,0xc66,0xc66,0xc66,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912, +0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912,0x912, +0x912,0x912,0x912,0xd62,0x99c,0x97e,0x97e,0x97e,0x97e,0x978,0x97e,0x97e,0x990,0x97e,0x97e,0x97b, +0x987,0x98d,0x98d,0x98d,0x98d,0x98d,0x990,0x978,0x984,0x978,0x978,0x978,0x96c,0x96c,0x978,0x978, +0x978,0x978,0x978,0x978,0x993,0x993,0x993,0x993,0x993,0x993,0x993,0x993,0x993,0x993,0x978,0x978, +0x978,0x978,0x978,0x978,0x978,0x978,0x978,0x978,0x97b,0x96c,0x978,0x96c,0x978,0x96c,0x98a,0x981, +0x98a,0x981,0x999,0x999,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8, +0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8,0x9a8, +0x9a8,0x9a8,0x9a8,0x9a8,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab, +0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab,0x9ab, +0x9ab,0x9ab,0x9ab,0x9ab,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae, +0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae,0x9ae, +0x9ae,0x9ae,0x9ae,0x9ae,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7, +0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7, +0x9b7,0x9b7,0x9b1,0x9b1,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba, +0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba, +0x9ba,0x9ba,0x9b4,0x9b4,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7, +0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7,0x9b7, +0x9b7,0x9b7,0x9b7,0x9b7,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba, +0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba,0x9ba, +0x9ba,0x9ba,0x9ba,0x9ba,0x9bd,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0, +0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0, +0x9bd,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0, +0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0x9c0,0xa4d,0xa4d,0xfc3,0xa4d, +0xa4d,0xa4d,0xa50,0xa4d,0xfc3,0xa4d,0xa4d,0xfba,0xa47,0xa3b,0xa3b,0xa3b,0xa3b,0xa4a,0xa3b,0xfab, +0xfab,0xfab,0xa3b,0xa3e,0xa47,0xa41,0xfb1,0xfbd,0xfbd,0xfab,0xfab,0xfc3,0xb3d,0xb3d,0xb3d,0xb3d, +0xb3d,0xb3d,0xb3d,0xb3d,0xb3d,0xb3d,0xa53,0xa53,0xa44,0xa44,0xa44,0xa44,0xa4d,0xa4d,0xa4d,0xa4d, +0xa4d,0xa4d,0xa4a,0xa4a,0xa3b,0xa3b,0xfc3,0xfc3,0xfc3,0xfc3,0xfab,0xfab,0xa4d,0xa4d,0xa4d,0xa4d, +0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d, +0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa4d,0xa62,0xa62,0xa62,0xa62, +0xa62,0xa62,0xa62,0xdb9,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62, +0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62, +0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xdb9,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62, +0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa62,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68, +0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68, +0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa68,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e, +0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6b,0xa71,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0x113d, +0x113d,0x113d,0x113d,0x113d,0x113d,0x113d,0x113d,0x113d,0x113a,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e, +0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e, +0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa6e,0xa83,0xa83,0xa83,0xa83, +0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83, +0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xa83,0xaa7,0xaa7,0xaa7,0xaaa, +0xaaa,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7, +0xa8f,0xa8f,0xaa4,0xa86,0xa86,0xa86,0xa86,0xa86,0xa86,0xa86,0xaa4,0xaa4,0xaa7,0xaa7,0xaa7,0xaa7, +0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7, +0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xaa7,0xac8,0xac8,0xac8,0xac8, +0xac8,0xab3,0xab3,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8, +0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8, +0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xacb,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8, +0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8,0xac8, +0xac8,0xac8,0xac8,0xac8,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec, +0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xaec,0xbe2, +0xbe2,0xbe2,0xbe2,0xbe2,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8, +0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8,0xaf8, +0xaf8,0xaf8,0xaf8,0xaf8,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a, +0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a,0xb0a, +0xb0a,0xb0a,0xb0a,0xb0a,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10, +0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10,0xb10, +0xb10,0xb10,0xb10,0xb10,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c, +0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0xb1c,0x13a1,0x13a1,0x13a1,0x1ad3, +0x1ad3,0x1ad3,0x1ad3,0x1ad3,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f, +0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f, +0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0xb1f,0x1ad6,0x1ad6,0x1ad6,0x1ad6,0x1ad6,0x1ad6,0x1ad6,0x1ad6,0x1ad6,0x1ad6, +0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22, +0xb22,0xb22,0xb22,0xb22,0xb22,0xb25,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22, +0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22, +0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb22,0xb28,0xb28,0xc69,0xc69,0xb28,0xb28,0xb28,0xb28, +0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xc69,0xb28,0xb28,0xb28, +0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb28,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c, +0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c, +0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0xb4c,0x14df,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xced,0xced, +0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52, +0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xb52,0xcea,0xcea,0xd38,0xd38,0xd38,0xd38, +0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xb55,0xb55,0xb55,0xb55, +0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55, +0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb55,0xb58,0xb58,0xb58,0xb58, +0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58, +0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb58,0xb67,0xb67,0xb67,0xb67, +0xb67,0xb5e,0xb6a,0xb70,0xb70,0xb70,0xb64,0xb64,0xb64,0xb6d,0xb61,0xb61,0xb61,0xb61,0xb61,0xb5b, +0xb5b,0xb5b,0xb5b,0xb5b,0xb5b,0xb5b,0xb5b,0xb70,0xb70,0xb70,0xb70,0xb70,0xb64,0xb64,0xb64,0xb64, +0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64, +0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb67,0xb67, +0xb70,0xb70,0xb70,0xb64,0xb64,0xb70,0xb70,0xb70,0xb70,0xb70,0xb70,0xb70,0xb64,0xb64,0xb64,0xb64, +0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64, +0xb64,0xb64,0xb70,0xb70,0xb70,0xb70,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64, +0xb64,0xb64,0xb64,0xb67,0xb67,0xb67,0xb67,0xb67,0xb67,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64, +0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0xb64, +0xb64,0xb64,0xb64,0xb64,0xb64,0xb64,0x16e3,0x16e3,0xb7c,0xb73,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, +0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, +0xb79,0xb79,0xb79,0xb73,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, +0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, +0xb7c,0xb7c,0xb7c,0xb73,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, +0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb73,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, +0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, +0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb73,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, +0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb76,0xb76,0xb76,0xb76, +0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76, +0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb76,0xb7c,0xb7c,0xb7c,0xb7c, +0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, +0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, +0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb7c,0xb7c,0xb7c,0xb7c, +0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c, +0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb7c,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, +0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79,0xb79, +0xb7c,0xb7c,0xb7c,0xb7c,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f, +0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f,0xb7f, +0xb7f,0xb7f,0xb7f,0xb7f,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85, +0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85, +0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0xb85,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc,0x1adc,0x1ba8,0x1ba8, +0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88, +0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88,0xb88, +0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2, +0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbe2,0xbdf,0xbe2,0xbdf,0xbdf,0xbdf,0xbdf,0xbdf,0xbdf,0xbdf,0xbdf, +0xbdf,0xbdf,0xbdf,0xbdf,0xbdf,0xbdf,0xbdf,0xce1,0xce4,0xdd1,0xdd1,0xdd1,0xdd1,0xdd1,0xdd1,0xdd1, +0xdd1,0xdd1,0xdd1,0xdd1,0xee8,0xee8,0xee8,0xee8,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee,0xbee, +0xbee,0xbee,0xce7,0xce7,0xce7,0xce7,0xce7,0xce7,0xce7,0xce7,0xdd4,0xe88,0xdd4,0xdd4,0xdd4,0xdd4, +0xdd4,0xdd4,0xdd4,0xdd4,0xdd4,0xfd8,0x1272,0x1272,0xddd,0xddd,0xddd,0xddd,0xddd,0xde3,0xde0,0xefa, +0xefa,0xefa,0xefa,0x13e0,0xfea,0x13e0,0x132c,0x132c,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21, +0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc21,0xc4e,0xc4b,0xc4e,0xc4b,0xc4e,0xc4b, +0x10fe,0x10fb,0xff0,0xfed,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24,0xc24, +0xc24,0xc24,0xc24,0xc24,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27, +0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27,0xc27, +0xc27,0xc27,0xc27,0xc27,0xc2a,0xc2a,0xc2a,0xc30,0xc2d,0xc54,0xc51,0xc30,0xc2d,0xc30,0xc2d,0xc30, +0xc2d,0xc30,0xc2d,0xc30,0xc2d,0xc30,0xc2d,0xc30,0xc2d,0xc30,0xc2d,0xc30,0xc2d,0xc2a,0xc2a,0xc2a, +0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a, +0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a, +0xc30,0xc2d,0xc30,0xc2d,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a, +0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a,0xc2a, +0xc30,0xc2d,0xc2a,0xc2a,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33, +0xc39,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33, +0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33, +0xc33,0xc33,0xc33,0xc33,0xc39,0xc39,0xc39,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33, +0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33,0xc33, +0xc33,0xc33,0xc33,0xc33,0xc36,0xc33,0xc33,0xc33,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c, +0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c, +0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xc6c,0xce7,0xd53,0xdd4,0xdd4,0xdd4,0xdd4,0xdd4,0xdd4, +0xdd4,0xdd4,0xe88,0xe88,0xdd4,0xdd4,0xdd4,0xdd4,0xdd4,0xdd4,0xeeb,0xfd8,0xfd8,0xfd8,0xfd8,0xfd8, +0xfd8,0xfd8,0xfd8,0xfd8,0xfd8,0x1296,0x1296,0x1275,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b, +0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b, +0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd0b,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd11,0xd11, +0xd11,0xd11,0xd11,0xd0e,0xd23,0xd23,0xd23,0xd1d,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23, +0xd23,0xd23,0xd23,0xd1d,0xd23,0xd23,0xd23,0xd23,0xd17,0xd17,0xd20,0xd20,0xd20,0xd20,0xd14,0xd14, +0xd14,0xd14,0xd14,0xd1a,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9, +0xde6,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xde9,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23, +0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd1d,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23,0xd23, +0xd23,0xd23,0xd23,0xd23,0xd23,0xd17,0xd17,0xd17,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a, +0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a, +0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd1a,0xd26,0xd26,0xd26,0xd26,0xd26,0xd26,0xd26,0xd26, +0xd26,0xd26,0xd26,0xd26,0xd26,0xd26,0xdec,0xdec,0xdec,0xdec,0xdec,0xdec,0xefd,0xefd,0xefd,0xefd, +0xefd,0xefd,0xefd,0x1107,0x1107,0xff3,0xff3,0xff3,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29, +0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29, +0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd29,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f, +0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f, +0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd2f,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38, +0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38, +0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd38,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44, +0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44, +0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd44,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50, +0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50, +0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xd50,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2, +0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2, +0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf2,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8, +0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf5,0xdf5,0xdf5, +0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf5,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8, +0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8, +0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xdf8,0xeb8,0xeb8,0xe0a,0xe0a,0xf00,0xf00,0xf00,0xf00, +0xf00,0xf00,0xf00,0xfff,0xfff,0x1002,0xfff,0xfff,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc, +0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16, +0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16, +0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe19,0xe16,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25, +0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25, +0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe25,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b, +0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0x1b1e,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28, +0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0x1b1b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b, +0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b, +0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xe2b,0xeb2,0xeb2,0xeb2,0xeb2,0xeb2,0xeb2,0xeb2,0xeb2, +0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xe43,0xf03, +0xf03,0xf03,0xf03,0x1005,0x1005,0x1005,0x1005,0x1005,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c, +0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c, +0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe4c,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55, +0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55, +0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe55,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e, +0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e, +0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe58,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b, +0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b,0xe5b, +0xe5b,0xe5b,0xe5b,0xe5e,0xe5e,0xe5e,0xe5e,0xe5e,0xe67,0xe67,0xe67,0xe67,0xe67,0xe67,0xe67,0xe67, +0xe67,0xe67,0xe67,0xe67,0xe67,0xe67,0xe64,0xe64,0xe64,0xe64,0xe64,0xe64,0xe64,0xe64,0xe61,0xe6a, +0x1011,0x100b,0x101a,0x1008,0xe67,0xe67,0x1008,0x1008,0xe79,0xe79,0xe6d,0xe79,0xe79,0xe79,0xe70,0xe79, +0xe79,0xe79,0xe79,0xe6d,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79, +0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe79,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c, +0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c, +0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe7c,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94, +0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94, +0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xe94,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5, +0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0xeb5,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110, +0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0xee8,0xee8,0xee8,0xee5,0xee5,0xee5,0xee5,0xee5, +0x1149,0x1392,0x1392,0x1392,0x1392,0x1317,0x1317,0x1317,0x1395,0x131a,0x131a,0x1395,0x14d6,0x14d6,0x14d6,0x14d6, +0x14d6,0x14d6,0x14d6,0x1797,0x1797,0x1797,0x1797,0x185d,0xefd,0xefd,0xefd,0xefd,0xff3,0xff3,0xff3,0xff3, +0xff3,0xff3,0xff3,0xff3,0xff3,0xff3,0xff3,0xff3,0xff6,0xff6,0xff6,0xff6,0xff6,0xff6,0xff6,0xff6, +0xff6,0xff6,0xff6,0xff6,0xff6,0xff6,0xff6,0xff6,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0xffc,0x14f1, +0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f1,0x14f4,0x1866,0x1866, +0x18e7,0x1866,0x1bc6,0x179d,0x132f,0x1152,0xf00,0xf00,0xf1e,0xf1e,0xf1e,0xf1e,0xf30,0xf39,0xf3c,0xf39, +0xf3c,0xf39,0xf3c,0xf39,0xf3c,0xf39,0xf3c,0xf39,0xf39,0xf39,0xf3c,0xf39,0xf39,0xf39,0xf39,0xf39, +0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39,0xf39, +0xf21,0xf1e,0xf1e,0xf1e,0xf1e,0xf1e,0xf1e,0xf33,0xf1e,0xf33,0xf30,0xf30,0xf45,0xf42,0xf45,0xf45, +0xf45,0xf42,0xf42,0xf45,0xf42,0xf45,0xf42,0xf45,0xf42,0x102c,0x102c,0x102c,0x1167,0x1023,0x102c,0x1023, +0xf42,0xf45,0xf42,0xf42,0x1023,0x1023,0x1023,0x1023,0x1026,0x1029,0x1167,0x1167,0xf48,0xf48,0x103e,0x1035, +0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x1035,0x1035,0x103e,0x1035, +0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0xf4e,0xf4e,0xf4e,0xf4e, +0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e, +0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf4e,0xf5d,0xf5d,0xf5d,0xf5d, +0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d, +0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0xf5d,0x1512, +0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512,0x1512, +0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63, +0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63,0xf63, +0xfab,0xfc3,0xfba,0xfc0,0xfc0,0xfc3,0xfc3,0xfba,0xfba,0xfc0,0xfc0,0xfc0,0xfc0,0xfc0,0xfc3,0xfc3, +0xfc3,0xfab,0xfab,0xfab,0xfab,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3,0xfc3, +0xfc3,0xfc3,0xfab,0xfba,0xfbd,0xfab,0xfab,0xfc0,0xfc0,0xfc0,0xfc0,0xfc0,0xfc0,0xfae,0xfc3,0xfc0, +0xfb7,0xfb7,0xfb7,0xfb7,0xfb7,0xfb7,0xfb7,0xfb7,0xfb7,0xfb7,0x1131,0x1131,0x112e,0x112b,0xfb4,0xfb4, +0xfdb,0xfdb,0xfdb,0xfdb,0x1296,0x1296,0x1275,0x1275,0x1275,0x1272,0x1272,0x1272,0x1272,0x1275,0x1398,0x1275, +0x1275,0x1275,0x1272,0x1275,0x1296,0x1272,0x1272,0x1272,0x1275,0x1275,0x1272,0x1272,0x1275,0x1272,0x1272,0x1275, +0xff6,0xff6,0xff6,0xff6,0xff6,0xff3,0xff3,0xff6,0xff6,0xff6,0xff6,0xff6,0xff6,0x14eb,0x14eb,0x14eb, +0x1107,0xff3,0xff3,0xff3,0xff3,0x12a2,0x127e,0x127e,0x127e,0x127e,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb, +0x1017,0x1017,0x1014,0x100e,0x1014,0x100e,0x1014,0x100e,0x1014,0x100e,0x100b,0x100b,0x100b,0x100b,0x1020,0x101d, +0x100b,0x1164,0x13ec,0x13ef,0x13ef,0x13ec,0x13ec,0x13ec,0x13ec,0x13ec,0x13f2,0x13f2,0x1506,0x14fa,0x14fa,0x14f7, +0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x1032,0x102f,0x102f,0x103e,0x1035,0x133b,0x1338,0x16ec, +0x133b,0x1338,0x13fb,0x13f8,0x1509,0x1509,0x150f,0x1509,0x150f,0x1509,0x150f,0x1509,0x150f,0x1509,0x150f,0x1509, +0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035, +0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x1035, +0x1038,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x103e,0x1035,0x103e,0x1035,0x103e,0x103e,0x1035, +0x1041,0x1041,0x1047,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d, +0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d,0x104d, +0x104d,0x1047,0x1041,0x1041,0x1041,0x1041,0x1047,0x1047,0x1041,0x1041,0x104a,0x1404,0x1401,0x1401,0x104d,0x104d, +0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407, +0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062, +0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062, +0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b, +0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106b,0x106e,0x106e,0x106e,0x1071,0x106e,0x106e,0x1074,0x1074, +0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077, +0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077,0x1077, +0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1083,0x107a,0x1089,0x1086, +0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080, +0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080, +0x1341,0x133e,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x1098,0x1119, +0x108c,0x108c,0x108c,0x1092,0x140a,0x140a,0x140a,0x140a,0x140a,0x140a,0x140a,0x140a,0x108f,0x108f,0x1092,0x109e, +0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095, +0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095,0x109b,0x1095, +0x151e,0x151b,0x151e,0x151b,0x1521,0x1521,0x16f5,0x140a,0x10a7,0x10a7,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa, +0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa, +0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10aa,0x10a7,0x10a7,0x10a7,0x10a7,0x10a7,0x10a7,0x10a7,0x10a7, +0x10a7,0x10a7,0x10a7,0x10a7,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b0,0x10b0,0x10b0,0x10b0,0x10b0,0x10b3, +0x10b3,0x10b3,0x110d,0x10bc,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb,0x10cb, +0x10cb,0x10cb,0x10cb,0x10cb,0x10b6,0x10b6,0x10b6,0x10b6,0x10b6,0x10b6,0x10b6,0x10b6,0x10b6,0x10b6,0x10b9,0x10b9, +0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9,0x10b9, +0x10b9,0x10b9,0x10b9,0x10b9,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da, +0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da,0x10da, +0x10da,0x10da,0x10da,0x10da,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec, +0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec,0x10ec, +0x10ec,0x10ec,0x10ec,0x10ec,0x10f5,0x10f5,0x10f5,0x10f5,0x110a,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5, +0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5,0x10f5, +0x10f5,0x10f5,0x10f5,0x10f5,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8, +0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8,0x10f8, +0x10f8,0x10f8,0x10f8,0x10f8,0x1104,0x1104,0x1104,0x1104,0x129c,0x129c,0x129c,0x129c,0x129c,0x129c,0x129c,0x129c, +0x1494,0x1776,0x1776,0x1776,0x1776,0x1776,0x1776,0x1776,0x1776,0x1776,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6, +0x18c6,0x18c6,0x18c6,0x18c6,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179, +0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1179,0x1170,0x1170,0x1173,0x1173,0x1179,0x1170, +0x1170,0x1170,0x1170,0x1170,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f, +0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f,0x117f, +0x117f,0x117f,0x117f,0x117f,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a, +0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a,0x119a, +0x119a,0x119a,0x119a,0x119a,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6, +0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6,0x11a6, +0x11a6,0x11a6,0x11a3,0x11a9,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5, +0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5,0x11b5, +0x11b5,0x11b5,0x11b5,0x11b5,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x11bb, +0x11bb,0x11bb,0x11bb,0x11bb,0x11bb,0x12e7,0x11c1,0x12ea,0x11c1,0x11c1,0x11c1,0x11c1,0x11be,0x11be,0x11be,0x11c1, +0x16f8,0x16fb,0x1923,0x1920,0x11c4,0x11c4,0x11c4,0x11d3,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9, +0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9, +0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11d9,0x11c7,0x11d3,0x11d3,0x11c4,0x11c4,0x11c4,0x11c4,0x11d3,0x11d3, +0x11c4,0x11c4,0x11d3,0x11d3,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5, +0x11e5,0x11e5,0x11e5,0x11e5,0x11e8,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11e5,0x11df,0x11df,0x11df,0x11e5,0x11e2, +0x1527,0x152a,0x152d,0x152d,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7, +0x11f7,0x11f7,0x11f7,0x11f7,0x11eb,0x11f7,0x11eb,0x11eb,0x11eb,0x1200,0x1200,0x11eb,0x11eb,0x1200,0x11f7,0x1200, +0x1200,0x11f7,0x11eb,0x11ee,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7, +0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7,0x11f7, +0x11f7,0x11f7,0x11f7,0x11f7,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212, +0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212, +0x1212,0x1212,0x1212,0x1212,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a, +0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a,0x122a, +0x122a,0x1227,0x1227,0x1227,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233, +0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233, +0x1233,0x1233,0x1233,0x1233,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242, +0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242, +0x1242,0x1242,0x1242,0x1242,0x1248,0x1248,0x1254,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257, +0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x125a,0x1257, +0x125a,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x1257,0x125a, +0x1257,0x1257,0x1257,0x1257,0x1254,0x1254,0x1254,0x1248,0x1248,0x1248,0x1248,0x1254,0x1254,0x124e,0x124b,0x1251, +0x1251,0x1260,0x125d,0x125d,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263, +0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263, +0x1263,0x1263,0x1263,0x1263,0x1269,0x1269,0x1269,0x1266,0x1266,0x1266,0x1263,0x1263,0x1263,0x1263,0x1266,0x1263, +0x1263,0x1263,0x1269,0x1266,0x1269,0x1266,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263, +0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263, +0x1263,0x1269,0x1266,0x1266,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263,0x1263, +0x1263,0x1263,0x1263,0x1bcf,0x19f5,0x19f5,0x19f5,0x19f5,0x19f5,0x19f5,0x19f5,0x19f8,0x19f2,0x1be1,0x1be1,0x1be1, +0x1be4,0x1bde,0x1be4,0x1bde,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1287, +0x1287,0x1287,0x126c,0x1929,0x138f,0x1290,0x138f,0x138f,0x138f,0x138f,0x138f,0x138f,0x138f,0x138f,0x138f,0x138f, +0x138f,0x1290,0x138f,0x1290,0x1275,0x1275,0x131d,0x1272,0x131d,0x131d,0x131d,0x131d,0x1272,0x1272,0x1296,0x1272, +0x1272,0x1272,0x1272,0x1272,0x1272,0x1275,0x1296,0x1296,0x1275,0x1296,0x1272,0x1275,0x1275,0x1278,0x1296,0x1272, +0x1272,0x1296,0x1275,0x1275,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x1281,0x1281, +0x1281,0x1281,0x13a4,0x1386,0x128a,0x13a4,0x13a4,0x13a4,0x13a4,0x13a4,0x13a4,0x13a4,0x13a4,0x13a4,0x13a4,0x1827, +0x1827,0x1827,0x1827,0x1827,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1530, +0x1530,0x1a76,0x1a76,0x1a76,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284,0x1284, +0x1284,0x1284,0x1284,0x1284,0x138f,0x138f,0x1290,0x138f,0x138f,0x138f,0x1290,0x138f,0x138f,0x138f,0x128a,0x128a, +0x128a,0x128a,0x128a,0x1389,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x128d,0x138c,0x138c,0x138c,0x138c, +0x138c,0x138c,0x138c,0x128d,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x140d,0x140d, +0x19d4,0x1a76,0x1a76,0x1a76,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x138c,0x128d,0x138c,0x128d, +0x128d,0x138c,0x138c,0x128d,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1, +0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1,0x12b1, +0x12b1,0x12b1,0x12b1,0x12b1,0x133b,0x1338,0x133b,0x1338,0x133b,0x1338,0x133b,0x1338,0x133b,0x1338,0x13fb,0x150f, +0x150f,0x150f,0x17a3,0x1917,0x150f,0x150f,0x16ef,0x16ef,0x16ef,0x16e9,0x16ef,0x16e9,0x191a,0x1917,0x19d1,0x19ce, +0x19d1,0x19ce,0x19d1,0x19ce,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f, +0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f,0x135f, +0x135f,0x135f,0x135f,0x135f,0x1374,0x1365,0x1374,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377, +0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377, +0x1377,0x1377,0x1377,0x1377,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x137d,0x137d,0x137d,0x137d, +0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d, +0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x137d,0x1383,0x1383,0x1383,0x1383, +0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383, +0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x13ad,0x13aa,0x18cc,0x18cc, +0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc, +0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x13b6,0x13b6,0x13b6,0x13b6, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, +0x13b6,0x13b3,0x13b3,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b3,0x13b6,0x13b6,0x13b6,0x13b3,0x13b6,0x13b3,0x13b6, +0x13b3,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b9,0x13b6,0x13b6,0x13b6,0x13b6,0x13b3,0x13b6,0x13b3,0x13b3,0x13b6, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b3,0x13b3,0x13b3,0x13b3, +0x13b3,0x13b3,0x13b3,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, +0x13b6,0x13b6,0x13b6,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b6,0x13b6,0x13b6, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3, +0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x13b3,0x1539,0x1539,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, +0x153c,0x153c,0x153c,0x153c,0x153c,0x1779,0x1779,0x1779,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x153c,0x13b6, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x153c,0x1779,0x1779, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b9,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, +0x13b6,0x13b6,0x13b6,0x13b6,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x1539,0x1539,0x153c,0x153c, +0x13b6,0x13b6,0x13b9,0x13b9,0x13b9,0x16a4,0x13b6,0x13b9,0x13b6,0x13b6,0x13b9,0x153f,0x153f,0x153c,0x153c,0x1779, +0x1779,0x1779,0x1779,0x1779,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, +0x13b6,0x153c,0x153c,0x153c,0x16a4,0x153c,0x153c,0x153c,0x1779,0x1779,0x1779,0x177c,0x177c,0x177c,0x177c,0x177c, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x153c, +0x13b6,0x153c,0x13b9,0x13b9,0x13b6,0x13b6,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9, +0x13b9,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, +0x13b6,0x13b6,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b9, +0x13b9,0x13b9,0x13b9,0x13b9,0x13b9,0x13b6,0x13b6,0x13b6,0x13b9,0x13b6,0x13b6,0x13b6,0x13b6,0x13b9,0x13b9,0x13b9, +0x13b6,0x13b9,0x13b9,0x13b9,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b9,0x13b6,0x13b9,0x13b6,0x13b6, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x16a4,0x13b6,0x13b6,0x13b6,0x13b6,0x153c,0x153c,0x1779, +0x1410,0x1410,0x1410,0x1410,0x1539,0x1539,0x1539,0x1539,0x1539,0x1539,0x153c,0x1779,0x1779,0x1779,0x1779,0x16fe, +0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6,0x13b6, +0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153f,0x153f,0x153c,0x153c, +0x153c,0x153c,0x1830,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, +0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x1539,0x1539,0x1539,0x1539,0x1539,0x1539,0x153c,0x13b6, +0x13b6,0x13b6,0x13b6,0x13b6,0x149a,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc, +0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x149a,0x13bc,0x13bc,0x13bc,0x149a,0x13bc,0x149a,0x13bc,0x149a,0x13bc,0x149a, +0x13bc,0x13bc,0x13bc,0x149a,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x149a,0x149a,0x13bc,0x13bc,0x13bc,0x13bc, +0x149a,0x13bc,0x149a,0x149a,0x13bc,0x13bc,0x13bc,0x13bc,0x149a,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x13bc, +0x13bc,0x13bc,0x13bc,0x13bc,0x13bc,0x16aa,0x16aa,0x177f,0x177f,0x13bf,0x13bf,0x13bf,0x13bc,0x13bc,0x13bc,0x13bf, +0x13bf,0x13bf,0x13bf,0x13bf,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629, +0x1629,0x1629,0x1629,0x1629,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2, +0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2, +0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c5,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2, +0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2,0x13c5,0x13c5,0x13c5,0x13c2,0x13c2,0x13c2,0x13c2,0x13c2, +0x13c2,0x13c2,0x13c2,0x13c2,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8, +0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8,0x13c8, +0x13c8,0x13c8,0x13c8,0x13c8,0x17ac,0x17ac,0x17a9,0x1701,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1413,0x1413, +0x1413,0x1413,0x1413,0x1413,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416, +0x1416,0x1416,0x1416,0x1545,0x1419,0x1548,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419, +0x1419,0x1548,0x1548,0x1548,0x1548,0x1548,0x1548,0x1704,0x1704,0x1b4b,0x17b2,0x17b2,0x17b2,0x17b2,0x17b2,0x17b2, +0x17b2,0x17b2,0x1a79,0x1a79,0x1422,0x1422,0x1422,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434, +0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434, +0x1434,0x1434,0x1434,0x1434,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f, +0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f,0x144f, +0x144f,0x144f,0x144f,0x144f,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455, +0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455, +0x1455,0x1455,0x1455,0x19da,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458, +0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458, +0x1458,0x1458,0x1458,0x1458,0x145e,0x145e,0x146a,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470, +0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470, +0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x1470,0x146a,0x146a,0x146a,0x145e,0x145e,0x145e,0x145e,0x145e,0x145e, +0x145e,0x145e,0x145e,0x146a,0x146d,0x1470,0x1473,0x1473,0x1470,0x1476,0x1476,0x1461,0x1464,0x170a,0x170d,0x170d, +0x170d,0x154e,0x1a82,0x1a7f,0x1467,0x1467,0x1467,0x1467,0x1467,0x1467,0x1467,0x1467,0x1467,0x1467,0x154b,0x1713, +0x1716,0x1710,0x1719,0x1719,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491, +0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491,0x1491, +0x1491,0x1491,0x1491,0x1491,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb, +0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x18ff,0x18ff, +0x18ff,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x19c8,0x14eb,0x14eb, +0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x14eb,0x1863,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff, +0x18ff,0x18ff,0x18ff,0x18ff,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, +0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x1539,0x1539,0x1539,0x1539,0x1539,0x1539,0x1539,0x1539, +0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, +0x153f,0x153c,0x153c,0x153c,0x153c,0x16a7,0x16a7,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, +0x182d,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c, +0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x153c,0x155d,0x155d,0x155d,0x155d, +0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d, +0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x155d,0x156f,0x156f,0x156f,0x156f, +0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f, +0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x156f,0x1575,0x1575,0x1575,0x1575, +0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575, +0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1575,0x1578,0x1578,0x1578,0x1578, +0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578, +0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x1578,0x15a2,0x15a2,0x15a2,0x15a2, +0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x15a2,0x159c,0x159c,0x159c,0x1590,0x1590,0x1590,0x159c,0x159c, +0x1590,0x159f,0x1593,0x1590,0x15a5,0x15a5,0x1599,0x15a5,0x15a5,0x1596,0x17b5,0x1bdb,0x15b7,0x15b7,0x15b7,0x15b7, +0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7, +0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15b7,0x15a8,0x15c0,0x15c0,0x15c0,0x15c0, +0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0, +0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15c0,0x15ba,0x15c3,0x15c3,0x15c3,0x15c3,0x15c6,0x15c6,0x15c6,0x15c6, +0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6, +0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15c6,0x15e1,0x15e1,0x15e1,0x15e1, +0x15e1,0x15e1,0x15e1,0x15e1,0x15d8,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1, +0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15e1,0x15ea,0x15ea,0x15ea,0x15ea, +0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea, +0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15ea,0x15fc,0x15fc,0x15fc,0x15fc, +0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15f9,0x15f9,0x15f9,0x15ed, +0x15ed,0x15ed,0x15ed,0x15ed,0x15ed,0x15ed,0x15ed,0x15f9,0x15f9,0x15ed,0x15f9,0x15f0,0x15fc,0x15fc,0x15fc,0x15fc, +0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc, +0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x15fc,0x1620,0x1620,0x1620,0x1620, +0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620, +0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x1620,0x161d,0x161d,0x161d,0x1629,0x1629,0x1629,0x1629, +0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629,0x1629, +0x1629,0x1629,0x162f,0x162f,0x162f,0x162c,0x162c,0x162c,0x1629,0x1629,0x1629,0x1629,0x163e,0x163e,0x163e,0x163e, +0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x1632,0x1632,0x1632,0x1632, +0x1632,0x1632,0x1632,0x1644,0x1644,0x1638,0x1635,0x1635,0x1635,0x1635,0x1635,0x1635,0x163e,0x163e,0x163e,0x163e, +0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e, +0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x163e,0x164a,0x164a,0x164a,0x164a, +0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a,0x164a, +0x164a,0x164a,0x164a,0x1647,0x1647,0x1647,0x1647,0x1647,0x1647,0x1647,0x1647,0x1647,0x164d,0x164d,0x164d,0x164d, +0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d, +0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x164d,0x1671,0x1671,0x1671,0x1671, +0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671, +0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x1671,0x167a,0x167a,0x167a,0x167a, +0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a, +0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x167a,0x1692,0x1692,0x1692,0x1692, +0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x167d,0x168c,0x168c,0x167d, +0x167d,0x167d,0x167d,0x167d,0x167d,0x168c,0x167d,0x168f,0x168f,0x167d,0x168f,0x167d,0x1692,0x1692,0x1692,0x1692, +0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692, +0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x169b,0x169b,0x169b,0x169b, +0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b, +0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x169b,0x16a1,0x16a1,0x16a1,0x16a1, +0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1, +0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x16a1,0x18ff,0x18ff,0x18ff,0x18ff, +0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x16e6,0x16e6,0x16e6,0x16e6,0x18ff,0x18ff,0x18ff,0x18ff, +0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x18ff,0x19c8,0x1707,0x1707,0x1707,0x1707, +0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707, +0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1707,0x1746,0x1746,0x1746,0x1746, +0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746, +0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x174c,0x1749, +0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746,0x1746, +0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f, +0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f,0x174f, +0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752, +0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752,0x1752, +0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764, +0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764,0x1764, +0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767, +0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767,0x1767, +0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a, +0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a, +0x176a,0x176a,0x176a,0x176d,0x176d,0x176d,0x176d,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a,0x176a, +0x176a,0x176a,0x176a,0x176a,0x176a,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176a,0x176d,0x176d, +0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d, +0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d,0x176d, +0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785, +0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785,0x1785, +0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x186f,0x1ae5,0x1a3a,0x1a3a,0x1a3d, +0x1788,0x1788,0x1788,0x1788,0x1788,0x1788,0x1788,0x1788,0x178b,0x1839,0x1839,0x1839,0x1839,0x1839,0x1839,0x18d5, +0x1788,0x1788,0x1788,0x1788,0x1788,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836, +0x1836,0x1836,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3, +0x1788,0x19b3,0x19b3,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1ae2,0x1bb4,0x1a3d,0x1a3d,0x1a3d, +0x18d2,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d5,0x18d2,0x18d2, +0x1a79,0x1a79,0x1a79,0x1a79,0x1a79,0x1a79,0x1a79,0x1a79,0x1b4b,0x1b4e,0x1b48,0x1b48,0x1b48,0x1b48,0x1b48,0x1b48, +0x1b48,0x1b48,0x1b48,0x192c,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af,0x17af, +0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1839,0x1836,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2, +0x1839,0x18d5,0x18d5,0x1839,0x1839,0x1839,0x1839,0x1839,0x1839,0x1839,0x1836,0x17b8,0x1839,0x1839,0x1839,0x1a3a, +0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x17b8,0x1836,0x1836,0x1836,0x1836,0x1836,0x18d2,0x19b3,0x19b3,0x19b3, +0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x1836,0x18d2, +0x17cd,0x17cd,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca, +0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca,0x17ca, +0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd, +0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd,0x17cd, +0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b, +0x181b,0x181b,0x181b,0x181b,0x181b,0x1818,0x1818,0x1818,0x1803,0x1803,0x1803,0x1803,0x1803,0x1803,0x1803,0x1803, +0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b, +0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b,0x181b, +0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f, +0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f,0x183f, +0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842, +0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842,0x1842, +0x1842,0x1842,0x1842,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee,0x1aee, +0x1899,0x1899,0x1899,0x1899,0x19ec,0x19ec,0x189c,0x189c,0x189c,0x189c,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884, +0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1896,0x1887,0x188a,0x188d,0x189f,0x189f,0x193e,0x1890,0x1890, +0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899, +0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899, +0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba, +0x18ba,0x18ba,0x18ba,0x18a5,0x18ab,0x18a8,0x18a8,0x18a8,0x18a8,0x18b7,0x18bd,0x18a8,0x18a8,0x18a8,0x18a8,0x18b4, +0x18ba,0x18a8,0x18a8,0x18a8,0x18a8,0x18a8,0x18a8,0x18a8,0x18a8,0x18a8,0x18a8,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba, +0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba,0x18ba, +0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x18c6,0x19ad,0x19ad,0x19ad,0x19ad,0x19ad, +0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ad9,0x1ba5,0x1ba5,0x1ba5, +0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc, +0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc,0x18cc, +0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x19b3,0x19b3,0x19b3,0x19b3, +0x19b3,0x1a3a,0x1ae2,0x19b3,0x19b3,0x19b3,0x19b3,0x1ae5,0x1ae2,0x1bb4,0x19b3,0x1a3a,0x19b3,0x19b3,0x19b3,0x19b3, +0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x18d2,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3, +0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b3, +0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db, +0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db,0x18db, +0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de, +0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x18de,0x1bb7, +0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1, +0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1,0x18e1, +0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953, +0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953,0x1953, +0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e, +0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e,0x196e, +0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974, +0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974,0x1974, +0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f, +0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f,0x198f, +0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992, +0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992,0x1992, +0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b, +0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x199b,0x1998,0x1998,0x1998, +0x19b3,0x19b3,0x19b3,0x1ae2,0x1ae2,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1a3a,0x1ae2,0x1ae2,0x1ae2,0x1a3a,0x1a3a, +0x19b3,0x19b3,0x19b3,0x19b3,0x19b3,0x19b6,0x19b6,0x19b3,0x19b6,0x19b6,0x1a3a,0x1a3d,0x1a3a,0x1a3a,0x1a3a,0x1a3a, +0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef, +0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef,0x19ef, +0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16, +0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16,0x1a16, +0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f, +0x1a55,0x1a55,0x1a1f,0x1a55,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a1f,0x1a25,0x1a25,0x1a25, +0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31, +0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31,0x1a31, +0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4, +0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4,0x1ac4, +0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0, +0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0,0x1ad0, +0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4, +0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4,0x1af4, +0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7, +0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7,0x1af7, +0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c, +0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c,0x1b6c, +0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d, +0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d,0x1b8d, +0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f, +0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f,0x1b9f, +0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2, +0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2,0x1ba2, +0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc,0x1bfc, +0x1bfc,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9,0x1bf9, +0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b, +0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b,0x1c3b, +0,0,0,0 +}; + +static const UTrie2 propsVectorsTrie={ + propsVectorsTrie_index, + propsVectorsTrie_index+5348, + nullptr, + 5348, + 27344, + 0xa40, + 0x1564, + 0x0, + 0x0, + 0x110000, + 0x7fb0, + nullptr, 0, false, false, 0, nullptr +}; + +static const uint32_t propsVectors[7230]={ +0x67,0,0,0x67,0,0xe00000,0x67,0x80000,0x20,0x867,0,0,0xa67,0,0,0xb67, +0,0,0xd67,0,0,0xe67,0,0,0x1067,0,0,0x1167,0,0,0x1267,0, +0,0x1367,0,0,0x1467,0,0,0x1567,0,0,0x1667,0,0,0x1767,0,0, +0x1867,0,0,0x1967,0,0,0x1a67,0,0,0x1b67,0,0,0x1d67,0,0,0x1f67, +0,0,0x2067,0,0,0x2267,0,0,0x2367,0,0,0x2467,0,0,0x2567,0, +0,0x2767,0,0,0x2867,0x80000,0x20,0x2967,0,0,0x2a67,0,0x1600000,0x2b67,0,0, +0x2d67,0,0,0x3167,0x20000000,0,0x3267,0x20000000,0,0x3a67,0,0,0x3b67,0,0,0x3c67, +0,0,0x3e67,0,0,0x4067,0,0,0x4167,0,0,0x4467,0,0,0x4867,0, +0,0x4967,0,0,0x4a67,0,0,0x5067,0,0,0x5167,0,0,0x5467,0,0, +0x5567,0,0,0x5667,0x80000,0x20,0x5767,0,0,0x5867,0,0,0x5967,0,0,0x5b67, +0,0,0x5c67,0,0,0x5d67,0,0,0x6067,0x80000,0x20,0x6267,0,0,0x6367,0, +0,0x6467,0,0,0x6567,0,0,0x6f67,0,0,0x7067,0,0,0x7367,0x20000000,0, +0x7567,0,0,0x7667,0,0,0x7767,0,0,0x7867,0,0,0x7a67,0,0,0x7b67, +0,0,0x7c67,0,0,0x7e67,0,0,0x7f67,0,0,0x8167,0,0,0x8267,0, +0,0x8467,0,0,0x8567,0,0,0x8667,0,0,0x8767,0,0,0x8967,0,0, +0x8b67,0,0,0x8c67,0,0,0x8e67,0x20000000,0,0x8f67,0,0,0x9067,0,0,0x9167, +0,0,0x9267,0,0,0x9367,0,0,0x9567,0,0,0x9667,0,0,0x9767,0, +0,0x9867,0,0,0x9967,0,0,0x9a67,0,0,0x9c67,0,0,0x9f67,0,0, +0xa167,0,0,0xa367,0,0,0xa467,0,0,0xa567,0,0,0xa667,0,0,0xa767, +0,0,0xa867,0,0,0xa967,0,0,0xaa67,0,0xe00000,0xab67,0,0xe00000,0xac67,0, +0,0xad67,0,0,0xae67,0,0,0xaf67,0,0,0xb167,0,0,0xb267,0,0, +0xb467,0,0,0xb567,0,0,0xb767,0,0,0xb867,0,0,0xb967,0,0,0xba67, +0,0,0xbc67,0,0,0xbd67,0,0,0xbe67,0,0,0xbf67,0,0,0xc067,0, +0,0xc167,0,0,0xc367,0,0xe00000,0xc467,0,0xe00000,0xc667,0,0,0xc767,0,0, +0xc867,0,0,0xc967,0,0,0xca67,0,0,0xcc67,0,0xe00000,0xcf67,0,0xe00000,0xd067, +0,0xe00000,0xd367,0,0,0xd467,0,0,0xd567,0,0,0xd667,0,0,0xd867,0, +0,0xda67,0,0,0xdb67,0,0,0xdc67,0,0,0xdd67,0,0,0xde67,0,0, +0xdf67,0,0,0xe067,0,0,0xe167,0,0,0xe267,0,0,0xe367,0,0xe00000,0xe467, +0,0,0xe567,0,0,0xe667,0,0,0xe767,0,0,0xe867,0,0,0xe967,0, +0,0xea67,0,0,0xeb67,0,0,0xec67,0,0,0xed67,0,0,0xee67,0,0, +0xef67,0,0,0xf167,0,0,0xf367,0,0,0xf567,0,0,0xf667,0,0,0xf767, +0,0,0xf867,0,0,0xf967,0,0,0xfa67,0,0xe00000,0xfb67,0,0,0xfc67,0, +0,0xfd67,0,0,0xfe67,0,0,0x10167,0,0,0x10267,0,0,0x10367,0,0, +0x10467,0,0,0x10667,0,0,0x10767,0,0,0x10867,0,0,0x10967,0,0,0x10a67, +0,0,0x10b67,0,0,0x10c67,0,0,0x10d67,0,0,0x10e67,0,0,0x10f67,0, +0,0x11067,0,0,0x11367,0,0,0x11467,0,0,0x11567,0,0,0x11667,0,0, +0x11767,0,0,0x11867,0,0,0x11967,0,0xe00000,0x11a67,0,0,0x11b67,0,0,0x11c67, +0,0,0x11d67,0,0,0x11e67,0,0,0x11f67,0,0,0x12067,0,0,0x12167,0, +0,0x12267,0,0,0x12367,0,0,0x12467,0,0,0x12567,0,0,0x12667,0,0, +0x12767,0,0,0x12867,0,0,0x12967,0,0,0x12a67,0,0xe00000,0x12b67,0,0,0x12c67, +0,0,0x12d67,0,0,0x12f67,0,0,0x13067,0,0,0x13167,0,0,0x13267,0, +0,0x13367,0,0,0x13467,0,0,0x13567,0,0,0x13667,0,0,0x13767,0,0, +0x13867,0,0,0x13967,0,0,0x13a67,0,0,0x13b67,0,0,0x13c67,0,0,0x13d67, +0,0,0x13f67,0,0,0x14067,0,0,0x14167,0,0,0x14367,0,0,0x14467,0, +0,0x14567,0,0,0x14667,0,0,0x14767,0,0,0xa0067,0,0xe00000,0xa4f67,0,0xe00000, +0xa5f67,0,0xe00000,0xac567,0,0xe00000,0xad167,0,0xe00000,0xb0067,0,0xe00000,0xb1267,0,0xe00000,0xb2e67, +0,0xe00000,0x11000100,0,0x900020,0x11000100,0x40000001,0x440020,0x11000100,0x40000001,0x643020,0x11000100,0x40000001,0xa5a040,0x11000100,0x40000001, +0x116a8a0,0x11000200,0,0x900020,0x11000200,0x4000001,0xc4000b,0x11000200,0x7c00100,0x220402,0x11000200,0x24000000,0x200000,0x11000200,0x24000008,0x1710000, +0x11000200,0x40000001,0x1d3b020,0x11000219,0x7c00100,0x220401,0x11000219,0x7c00100,0x250401,0x11000319,0x7c00100,0x220401,0x11000319,0x7c00100,0x220402,0x11000319, +0x7c00100,0x250400,0x11000319,0x7c00100,0x250401,0x11000419,0x7c00100,0x220400,0x11000419,0x7c00100,0x220401,0x11000419,0x7c00100,0x220402,0x11000419,0x7c00100, +0x230400,0x11000419,0x7c00100,0x250400,0x11000419,0x7c00100,0x250401,0x11000419,0x7c00100,0x250402,0x11000519,0x7c00100,0x220400,0x11000519,0x7c00100,0x230400, +0x11000600,0x4000400,0x200002,0x11000600,0x4000400,0x200400,0x11000600,0x7c00500,0x220400,0x11000600,0x7c00500,0x230400,0x11000600,0x7c00500,0x530400,0x11000600, +0x7c00d00,0x230400,0x11000619,0x7c00500,0x22040f,0x11000800,0x4000010,0x1001401,0x11000800,0x4000400,0x200001,0x11000800,0x6800010,0x201001,0x11000800,0x7c00500, +0x230401,0x11000807,0x7c00100,0x220400,0x11000807,0x7c00100,0x250400,0x1100080e,0x4000400,0x200000,0x1100080e,0x4000400,0x200002,0x1100080e,0x7000500,0x220402, +0x1100080e,0x7c00100,0x220400,0x1100080e,0x7c00100,0x220401,0x1100080e,0x7c00100,0x220402,0x1100080e,0x7c00100,0x250400,0x1100080e,0x7c00100,0x250401,0x1100080e, +0x7c00120,0x220402,0x1100080e,0x7c00120,0x250402,0x11000908,0x4000000,0x200000,0x11000908,0x7c00100,0x220400,0x11000908,0x7c00100,0x220401,0x11000908,0x7c00100, +0x250400,0x11000908,0x7c00100,0x250401,0x11000a03,0x4000000,0x200400,0x11000a03,0x4000000,0x201000,0x11000a03,0x4000000,0x270000,0x11000a03,0x7c00100,0x220400, +0x11000a03,0x7c00100,0x220402,0x11000a03,0x7c00100,0x250400,0x11000a03,0x7c00500,0x230400,0x11000a03,0xc000010,0x1049400,0x11000b13,0x2802500,0x962460,0x11000b13, +0x4000000,0x200000,0x11000b13,0x4000000,0x201000,0x11000b13,0x4000000,0x230400,0x11000b13,0x4000002,0x400000,0x11000b13,0x4000010,0x200000,0x11000b13,0x7c00100, +0x2633800,0x11000c00,0x80000000,0x218960,0x11000c02,0x2802100,0x962460,0x11000c02,0x2802400,0x962460,0x11000c02,0x4000000,0x200000,0x11000c02,0x4000000,0x1329400, +0x11000c02,0x4000000,0x1329800,0x11000c02,0x4000000,0x1500000,0x11000c02,0x6800000,0x1329800,0x11000c02,0x7c00100,0x230400,0x11000c02,0x7c00100,0x230401,0x11000c02, +0x7c00100,0x230402,0x11000c02,0x7c00500,0x230400,0x11000c02,0x7d00100,0x230400,0x11000f01,0x2802400,0x962460,0x11000f0a,0x2802100,0x962460,0x11000f0a,0x2802400, +0x962460,0x11000f0a,0x2806400,0x962460,0x11000f0a,0x4000000,0x200000,0x11000f0a,0x6800100,0x962540,0x11000f0a,0x7c00100,0x230400,0x11000f0a,0x7c00100,0x230401, +0x11001004,0x2802100,0x962460,0x11001004,0x2802400,0x962460,0x11001004,0x2806400,0x962460,0x11001004,0x4000000,0x200000,0x11001004,0x4000000,0x1500000,0x11001004, +0x6800100,0x962540,0x11001004,0x6800100,0x962541,0x11001004,0x7c00100,0x230400,0x11001004,0x7c00100,0x230401,0x11001110,0x2802100,0x962460,0x11001110,0x2802400, +0x962460,0x11001110,0x2806400,0x962460,0x11001110,0x6800100,0x962540,0x11001110,0x7c00100,0x230400,0x11001110,0x7c00100,0x230401,0x1100120f,0x2802100,0x962460, +0x1100120f,0x2802400,0x962460,0x1100120f,0x2806400,0x962460,0x1100120f,0x6800100,0x962540,0x1100120f,0x7c00100,0x230400,0x1100131f,0x2802100,0x962460,0x1100131f, +0x2802400,0x962460,0x1100131f,0x2806400,0x962460,0x1100131f,0x4000000,0x200000,0x1100131f,0x6800000,0x1329800,0x1100131f,0x6800100,0x962540,0x1100131f,0x6800100, +0x962541,0x1100131f,0x7c00100,0x230400,0x1100131f,0x7c00100,0x230401,0x11001423,0x2802100,0x962460,0x11001423,0x2806400,0x962460,0x11001423,0x6800100,0x962540, +0x11001423,0x6800100,0x962541,0x11001423,0x7c00100,0x230400,0x11001423,0x7c00100,0x230401,0x11001524,0x2802100,0x962460,0x11001524,0x2802100,0x962461,0x11001524, +0x2806400,0x962460,0x11001524,0x6800000,0x1329800,0x11001524,0x6800100,0x962540,0x11001524,0x7c00100,0x230400,0x11001615,0x2802100,0x962460,0x11001615,0x2806400, +0x962460,0x11001615,0x6800100,0x962540,0x11001615,0x6800100,0x962541,0x11001615,0x7c00100,0x230400,0x1100171a,0x2802100,0x962460,0x1100171a,0x2806400,0x962460, +0x1100171a,0x6800000,0x1329800,0x1100171a,0x6800100,0x962540,0x1100171a,0x6800100,0x962541,0x1100171a,0x7c00100,0x230400,0x11001900,0x4000000,0x1600000,0x11001926, +0x2802100,0x1862460,0x11001926,0x2802400,0x1862460,0x11001926,0x2806100,0x1862460,0x11001926,0x4000000,0x200000,0x11001926,0x4000010,0x400000,0x11001926,0x6800000, +0x1329800,0x11001926,0x7800100,0x1830142,0x11001926,0x7c00100,0x1830000,0x11001926,0x7c00900,0x1830000,0x11001926,0x7e00100,0x1830000,0x11001a18,0x2802100,0x1862460, +0x11001a18,0x2802400,0x1862460,0x11001a18,0x6800000,0x1329800,0x11001a18,0x7800100,0x1830142,0x11001a18,0x7c00100,0x1830000,0x11001a18,0x7c00100,0x1830002,0x11001a18, +0x7c00900,0x1830000,0x11001a18,0x7e00100,0x1830000,0x11001d0c,0x7c00100,0x230400,0x11001d0c,0x7c00100,0x250400,0x11001e12,0x7c00100,0x2230500,0x11001e12,0x7c00100, +0x2330520,0x11001e12,0x7c80100,0x2330520,0x11002619,0x7c00100,0x220401,0x11002619,0x7c00100,0x220402,0x11002619,0x7c00100,0x250401,0x1100270e,0x4000400,0x200001, +0x1100270e,0x4000400,0x200002,0x1100270e,0x4000400,0x500001,0x1100270e,0x7c00100,0x220401,0x1100270e,0x7c00100,0x250401,0x11002800,0x80000,0x918820,0x11002800, +0x80000,0x1c18020,0x11002800,0x180000,0x918820,0x11002800,0x4000001,0x445801,0x11002800,0x4000001,0x445802,0x11002800,0x4000001,0xc4000b,0x11002800,0x6800000, +0x201c00,0x11002800,0x6800020,0x201c00,0x11002800,0x24000000,0x200000,0x11002800,0x24000000,0x200002,0x11002800,0x24000000,0x810000,0x11002800,0x24000000,0x1410000, +0x11002800,0x24000000,0x1500000,0x11002800,0x24000000,0x1500002,0x11002800,0x24000002,0x400000,0x11002800,0x24000006,0xc0000b,0x11002800,0x24000008,0x1410000,0x11002800, +0x24000008,0x1710000,0x11002800,0x24000020,0x1001400,0x11002800,0x24000020,0x1500002,0x11002800,0x2c000010,0x1248000,0x11002800,0x2c000010,0x1248002,0x11002800,0x40000001, +0x63b020,0x11002800,0x40080000,0x918820,0x11002801,0x80000,0x2a65620,0x11002801,0x82000,0x962460,0x11002900,0x4000000,0x20000e,0x11002900,0x4000000,0x20000f, +0x11002900,0x4000020,0x20000e,0x11002900,0x4000020,0x20000f,0x11002900,0x4000020,0x81000e,0x11002900,0x4000020,0x81000f,0x11002900,0x4000020,0x141000e,0x11002900, +0x4000020,0x141000f,0x11002900,0x4000022,0x20000e,0x11002900,0x4000022,0x20000f,0x11002a00,0x4000000,0x1500000,0x11002a00,0x4000000,0x1600000,0x11002a00,0x4000000, +0x1600002,0x11002b01,0x2000,0x962460,0x11002b01,0x2802020,0x962460,0x11002c00,0x4000000,0x200000,0x11002c00,0x4000000,0x200002,0x11002c00,0x4000000,0x20000f, +0x11002c00,0x4000020,0x200000,0x11002c00,0x7c00000,0x200000,0x11002c00,0x7c00020,0x200000,0x11002c00,0x7c00120,0x220405,0x11002c00,0x7c00120,0x230402,0x11002c00, +0x7c00120,0x250402,0x11002c00,0x7c00120,0x250405,0x11002c19,0x7c00100,0x250400,0x11002c19,0x7c00100,0x250401,0x11002d00,0x4000000,0x100006,0x11002d00,0x4000000, +0x200006,0x11002d19,0x7c00100,0x220402,0x11002d19,0x7c00100,0x230400,0x11002d19,0x7c00100,0x250402,0x11002e00,0x24000000,0x200000,0x11002e00,0x24000020,0x200000, +0x11002e00,0x24000020,0x200001,0x11002f00,0x24000020,0x200000,0x11002f00,0x24000020,0x200001,0x11002f00,0x24000020,0x200002,0x11002f00,0x24000020,0xf00000,0x11002f00, +0x24000020,0x1600000,0x11002f00,0x24000022,0x1600000,0x11003000,0x24000000,0x200000,0x11003000,0x24000020,0x200000,0x11003000,0x24000020,0x810000,0x11003000,0x24000020, +0x1410000,0x11003100,0x24000000,0x200000,0x11003200,0x24000000,0x200000,0x11003300,0x4000000,0x100003,0x11003400,0x24000000,0x100000,0x11003400,0x24000000,0x200000, +0x11003500,0x24000000,0x200000,0x11003600,0x24000000,0x200000,0x11003600,0x24000020,0x200000,0x11003700,0x24000000,0x200000,0x11003700,0x24000000,0xe00000,0x11003700, +0x24000000,0x2800000,0x11003700,0x24000020,0x200000,0x11003800,0x4000000,0x100000,0x11003800,0x24000000,0x200000,0x11003800,0x24000000,0xb00000,0x11003800,0x24000000, +0xe00000,0x11003800,0x24000000,0x1710000,0x11003800,0x24000000,0x2800000,0x11005003,0x7c00100,0x220402,0x11005013,0x2802500,0x962460,0x11005013,0x4000020,0x200005, +0x11005013,0x7c00100,0x2633801,0x11005013,0x7c00100,0x2633802,0x11005013,0x7c00100,0x2633805,0x11005019,0x7c00100,0x220402,0x11005102,0x7000100,0x230408,0x11005102, +0x7c00100,0x230404,0x11005102,0x7c00100,0x230407,0x11005102,0x7c00100,0x230408,0x11005102,0x7c00100,0x230409,0x11005201,0x2802400,0x962460,0x11005500,0x80000, +0x1e18820,0x11005502,0x7000100,0x230408,0x11005502,0x7c00100,0x230404,0x11005502,0x7c00100,0x230407,0x11005502,0x7c00100,0x230408,0x11005502,0x7c00100,0x230409, +0x11005667,0x1000,0,0x11020200,0x80004,0x418820,0x11020200,0x4000000,0x100006,0x11020200,0x4000000,0x10000f,0x11020200,0x4000400,0x100002,0x11020200, +0x4000400,0x500002,0x11020200,0x6800c00,0x101000,0x11020200,0x24000000,0x100000,0x11020200,0x24000000,0x200000,0x11020200,0x24000000,0x1400000,0x11020200,0x24000000, +0x1500000,0x11020200,0x24000000,0x1600000,0x11020200,0x24000020,0x100000,0x11020200,0x24000020,0x1600000,0x11020219,0x7c00100,0x12040f,0x11020219,0x7c00100,0x220400, +0x11020219,0x7c00100,0x220401,0x11020219,0x7c00100,0x250400,0x11020319,0x7c00100,0x220400,0x11020319,0x7c00100,0x220401,0x11020319,0x7c00100,0x220402,0x11020319, +0x7c00100,0x250400,0x11020319,0x7c00100,0x250402,0x11020319,0x7d00100,0x220402,0x11020419,0x7c00100,0x220401,0x11020519,0x7c00100,0x220400,0x11020600,0x4000400, +0x100002,0x11020600,0x4000400,0x200400,0x11020600,0x7c00500,0x130400,0x11020600,0x7c00d00,0x130400,0x11020701,0x2802400,0x962460,0x11020701,0x2802400,0x962461, +0x11020701,0x2802400,0xc62460,0x1102080e,0x7c00100,0x220400,0x1102080e,0x7c00100,0x250400,0x11020908,0x7c00100,0x220400,0x11020908,0x7c00100,0x220401,0x11020908, +0x7c00100,0x250400,0x11020908,0x7c00100,0x250401,0x11022800,0x24000000,0x100000,0x11022800,0x24000000,0x200000,0x11022800,0x24000000,0x200002,0x11022800,0x24000000, +0x401000,0x11022800,0x24000000,0xf00002,0x11022800,0x24000000,0xf0ac02,0x11022800,0x24000000,0x1500000,0x11022800,0x24000002,0x100000,0x11022800,0x24000002,0x370000, +0x11022800,0x24000002,0x470000,0x11022800,0x24000006,0x400000,0x11022800,0x24000008,0x1710000,0x11022800,0x24000008,0x1712c00,0x11022800,0x24000020,0x100000,0x11022800, +0x24000020,0x1500000,0x11022800,0x24000020,0x1500002,0x11022900,0x4000000,0x10000e,0x11022900,0x4000000,0x10000f,0x11022919,0x7c00100,0x12040f,0x11022c00,0x4000000, +0x100002,0x11022c00,0x4000000,0x10000f,0x11022c00,0x4000000,0x1500002,0x11022c00,0x4000000,0x1600002,0x11022c00,0x7c00120,0x120405,0x11022c0e,0x7c00100,0x250401, +0x11022c19,0x7c00100,0x150401,0x11022d00,0x4000000,0x100006,0x11022d00,0x4000000,0x200006,0x11022d19,0x7c00100,0x120402,0x11022d19,0x7c00100,0x150402,0x11022e00, +0x24000000,0x200000,0x11022e00,0x24000020,0x100000,0x11022f00,0x24000020,0x100000,0x11022f00,0x24000020,0x100001,0x11022f00,0x24000020,0x100002,0x11023000,0x24000000, +0x100000,0x11023300,0x4000000,0x100002,0x11023300,0x4000000,0x100003,0x11023300,0x4000100,0x120403,0x11023300,0x4000100,0x150403,0x11023400,0x24000000,0x100000, +0x11023500,0x24000000,0x100000,0x11023600,0x24000000,0x100000,0x11023600,0x24000020,0x100000,0x11023700,0x24000000,0x100000,0x11023700,0x24000000,0xe00000,0x11023700, +0x24000020,0x100000,0x11023800,0x4000000,0x100000,0x11023800,0x24000000,0x200000,0x11024e67,0,0,0x11025600,0x4000000,0x100000,0x11042a00,0x4000000, +0x1600000,0x11045700,0x4000000,0x20000a,0x11045700,0x4000020,0x20000a,0x11045712,0x7c00100,0xe3040a,0x11045712,0x7c80100,0xe3040a,0x11045716,0x7c00100,0xe30c0a, +0x11045716,0x7c00100,0x2530c0a,0x11063d00,0x4000001,0x445811,0x11065700,0x4000000,0x810011,0x11065700,0x4000000,0xe00011,0x11065700,0x4000000,0x1410011,0x11065700, +0x4000000,0x1500011,0x11065700,0x4000000,0x1600011,0x11065700,0x4000006,0xe70011,0x11065700,0x4000008,0xe00011,0x11065700,0x4000008,0xe02c11,0x11065700,0x4000010, +0x871411,0x11065700,0x4000010,0x1201411,0x11065700,0x4000010,0x1271011,0x11065700,0x4000020,0xe00011,0x11065700,0x4000400,0xe00011,0x11065700,0x4000420,0xe00011, +0x11065700,0x6800000,0xe01c11,0x11065700,0x6800040,0xe29811,0x11065700,0xc000010,0x80ac11,0x11065700,0xc000010,0xb48011,0x11065719,0x7c00100,0xe20411,0x11065719, +0x7c00100,0xe50411,0x11065719,0x7c00140,0xe20411,0x11065719,0x7c00140,0xe50411,0x11080100,0x6800000,0x201c00,0x11080100,0x68000c0,0x1329800,0x11080100,0x24000000, +0x200000,0x11080100,0x24000000,0x810000,0x11080100,0x24000000,0x1410000,0x11080100,0x24000000,0x1500000,0x11080100,0x24000000,0x1600000,0x11080100,0x24000000,0x1b00000, +0x11080100,0x24000000,0x2410000,0x11080100,0x24000006,0xd70000,0x11080100,0x24000008,0x1713c00,0x11080100,0x24000008,0x1714000,0x11080100,0x24000010,0x1001400,0x11080100, +0x24000010,0x1071000,0x11080100,0x24000010,0x1071400,0x11080100,0x24000020,0x200000,0x11080100,0x24000020,0x400000,0x11080100,0x24000020,0x1600000,0x11080100,0x24000400, +0x200000,0x11080100,0x24000420,0x200000,0x11080100,0x2c000010,0xb48000,0x11080100,0x2c000010,0x100ac00,0x11080100,0x44000001,0x1a45800,0x11080119,0x7c00100,0x220400, +0x11080119,0x7c00100,0x250400,0x11080119,0x7c001c0,0x220400,0x11080119,0x7c001c0,0x250400,0x11080200,0x4000400,0x200002,0x11080200,0x24000000,0x200000,0x11080200, +0x24000000,0x1500000,0x11080200,0x24000000,0x1600000,0x11080200,0x24000020,0x200000,0x110a1e12,0x7c00100,0x2130480,0x110a1e12,0x7c80100,0x2130480,0x110a3000,0x24000000, +0xe00000,0x110a3000,0x24100000,0x810001,0x110a3000,0x24100000,0x1410001,0x110a3700,0x24000000,0x200000,0x110a3d00,0x4000000,0xe00000,0x110a3d00,0x4000000,0xe00002, +0x110a3d00,0x24000000,0xe00000,0x110a3d11,0x7c00300,0xe30000,0x110a3d11,0x7c00900,0x1230400,0x110a3d12,0x2802400,0x962460,0x110a3e14,0x7c00100,0xe30000,0x110a3e14, +0x7c00100,0xe30001,0x110a3e14,0x7c00100,0x2530000,0x110a3e14,0x7c00900,0x1230000,0x110a3e14,0x7c00900,0x1230001,0x110a3f16,0x7c00100,0xe30c00,0x110a3f16,0x7c00100, +0xe30c01,0x110a3f16,0x7c00100,0x2530c00,0x110a3f16,0x7c00900,0x1230c00,0x110a3f16,0x7c00900,0x1230c01,0x110a4005,0x7c00100,0xe30400,0x110a4112,0x7c00100,0xe30402, +0x110a4112,0x7c80100,0xe30402,0x110a4400,0x4000000,0xe00000,0x110a4412,0x4000000,0xe00002,0x110a4412,0x4000000,0xe00003,0x110a4416,0x4000000,0xe00c03,0x110a4500, +0x4000000,0xe0000d,0x110a4516,0x4000000,0xe00c0d,0x110a4711,0x7c40300,0xe30000,0x110a4f11,0x7c00300,0xe30001,0x110a4f11,0x7c40300,0xe30000,0x110a5300,0x4000000, +0x810010,0x110a5300,0x4000000,0xe00002,0x110a5300,0x4000000,0xe00010,0x110a5300,0x4000000,0x1410010,0x110a5300,0x4000002,0xe70010,0x110a5300,0x4000008,0x810010, +0x110a5300,0x4000008,0x1410010,0x110a5300,0x6800000,0xe01c02,0x110a5300,0x6800000,0xe01c10,0x110a5400,0x4000000,0x81000c,0x110a5400,0x4000000,0xe0000c,0x110a5400, +0x4000000,0x141000c,0x110a5400,0x4000000,0x150000c,0x110a5400,0x4000000,0x160000c,0x110a5400,0x4000002,0xe7000c,0x110a5400,0x4000010,0x87140c,0x110a5400,0x4000010, +0xe7000c,0x110a5400,0x4000010,0x120140c,0x110a5400,0x4000010,0x127100c,0x110a5400,0x4000020,0xe0000c,0x110a5400,0x4000026,0xe7000c,0x110a5400,0xc000010,0x80ac0c, +0x110a5400,0xc000010,0xb4800c,0x11400c0c,0x4000010,0xb00000,0x11400c0c,0x4000010,0x1071400,0x11400c17,0xc000010,0xb48000,0x11400c1e,0x7c00900,0x230400,0x11400f4b, +0xc000010,0x448000,0x11400f5f,0xc000010,0x448000,0x11401d94,0x4000000,0x200000,0x11403dca,0x4000000,0xe00000,0x114457bf,0x4000004,0x120000a,0x114457bf,0x4000008, +0x81000a,0x114457bf,0x4000008,0x141000a,0x114457bf,0x4000010,0x87000a,0x114457bf,0xc000010,0x84800a,0x114457c8,0x3802500,0x126246a,0x114457c8,0x7c00d00,0x2530c0a, +0x114a3dbf,0x24000000,0x810000,0x114a3dbf,0x24000000,0x1410000,0x114a3dbf,0x24000008,0x810000,0x114a3dbf,0x24000008,0x1410000,0x114a3dbf,0x24000010,0x870000,0x114a3dbf, +0x2c000010,0x848000,0x114a3dc5,0x4000000,0xe00000,0x114a3dc5,0x24000000,0xe00000,0x114a3dc5,0x24000002,0xe00000,0x114a3dc5,0x24000002,0x1200000,0x114a3dc5,0x24000008, +0x810000,0x114a3dc5,0x24000008,0x1410000,0x114a3dc8,0x7c00900,0x930c00,0x114a3dc8,0x7c00900,0xe30c00,0x114a3dca,0x7c00300,0xe30000,0x114a3ec8,0x7000400,0x1200c02, +0x114a3fbf,0x4000004,0x1200000,0x114a3fc8,0x7c00d00,0x2530c00,0x114a42ca,0x4000000,0xe00000,0x114a42ca,0x4000000,0xe0000f,0x114a44ca,0x4000000,0xe00002,0x114a44ca, +0x4000000,0xe00003,0x114a45ca,0x4000000,0xe00002,0x114a45ca,0x4000000,0xe0000d,0x11505103,0x24000000,0x810000,0x11505103,0x24000000,0x1410000,0x1180090a,0x2802400, +0x962460,0x11800c27,0x2802100,0x962460,0x11800c27,0x2802500,0x962460,0x11800f32,0x2802400,0x962460,0x11800f3f,0x2802400,0x962460,0x11820700,0x2802400,0x962460, +0x11820700,0x2802500,0x962460,0x118a3dcb,0x2802400,0x962460,0x118a3ec8,0x2802400,0x962460,0x11c00904,0x2802400,0x962460,0x11c00908,0x2802400,0x962460,0x11c00c2c, +0x6800000,0x1329800,0x11c00c30,0xc000010,0xb48000,0x11c00f78,0x6800000,0x1329800,0x11c0107d,0x6800000,0x1329800,0x11c01181,0x6800000,0x1329800,0x11c01285,0x6800000, +0x1329800,0x11c01489,0x4000000,0x200000,0x11c01489,0x6800000,0x1329800,0x11c0168d,0x6800000,0x1329800,0x11d05107,0x7c00100,0x230408,0x20000067,0x1000,0, +0x20000b13,0x2802400,0x962460,0x20000b13,0x2802500,0x962460,0x20001b27,0x2802100,0x962460,0x20001b27,0x2802100,0x962461,0x20001b27,0x2802400,0x962460,0x20001b27, +0x2802500,0x962460,0x20001b27,0x2806400,0x962460,0x20001b27,0x2902100,0x962462,0x20001b27,0x4000000,0x200000,0x20001b27,0x4000000,0x400000,0x20001b27,0x4000000, +0x500000,0x20001b27,0x4000000,0x810000,0x20001b27,0x4000000,0xb00000,0x20001b27,0x4000000,0xc0000b,0x20001b27,0x4000000,0x1410000,0x20001b27,0x4000010,0xb00000, +0x20001b27,0x4000010,0xc00000,0x20001b27,0x6800000,0x1329800,0x20001b27,0x6800100,0x462540,0x20001b27,0x6800400,0x962540,0x20001b27,0x7c00100,0x230400,0x20001b27, +0x7c00100,0x230401,0x20002619,0x7c00100,0x220401,0x20002a00,0x4000000,0x1600000,0x20004b67,0,0x1900000,0x20004c67,0,0x1900000,0x20004d67,0, +0x1900000,0x20006d67,0x1000,0,0x20006e67,0x1000,0,0x20026d67,0,0,0x20026e67,0,0,0x200a4a12,0x7c00100,0x1f304c1, +0x200a4a12,0x7c00100,0x20304e1,0x21005600,0x4000000,0x700000,0x21022a00,0x4000000,0x1600000,0x30000419,0x7c00100,0x220400,0x30000419,0x7c00100,0x220401,0x30000419, +0x7c00100,0x250400,0x30000419,0x7c00100,0x250401,0x30000519,0x7c00100,0x220400,0x30000600,0x4000400,0x200400,0x30000600,0x7c00500,0x230400,0x30000605,0x4000400, +0x200400,0x3000080e,0x7c00100,0x220400,0x30000908,0x2000,0x962460,0x30000908,0x7c00100,0x220400,0x30000908,0x7c00100,0x220401,0x30000908,0x7c00100,0x250400, +0x30000908,0x7c00100,0x250401,0x30000a03,0x4000006,0x400400,0x30000c02,0x4000000,0x200000,0x30000c02,0x7c00100,0x230400,0x30000d22,0x2802100,0x962460,0x30000d22, +0x2802400,0x962460,0x30000d22,0x2802500,0x962460,0x30000d22,0x4000000,0x200000,0x30000d22,0x4000010,0x200000,0x30000d22,0x7c00100,0x230400,0x30000d22,0xc000010, +0x248000,0x30000d22,0x80000000,0x218960,0x30000e25,0x2802500,0x962460,0x30000e25,0x7c00100,0x230400,0x30001821,0x2802100,0x962460,0x30001821,0x2806400,0x962460, +0x30001821,0x4000000,0x200000,0x30001821,0x6800100,0x962540,0x30001821,0x6800100,0x962541,0x30001821,0x7c00100,0x230400,0x30001b27,0x2802100,0x962460,0x30001b27, +0x2802400,0x962460,0x30001b27,0x4000000,0x200000,0x30001b27,0x4000000,0x400000,0x30001b27,0x7c00100,0x230400,0x30001c1c,0x2802100,0x1862460,0x30001c1c,0x2802400, +0x1862460,0x30001c1c,0x2806400,0x1862460,0x30001c1c,0x4000000,0x200000,0x30001c1c,0x6800100,0x1862400,0x30001c1c,0x6800100,0x1862540,0x30001c1c,0x7c00100,0x1830000, +0x30001c1c,0x7c00100,0x1830001,0x30001c1c,0xc000010,0x448000,0x30001f0b,0x4000000,0x200000,0x30001f0b,0x4000010,0x200000,0x30001f0b,0x4000010,0x400000,0x30001f0b, +0x6800000,0x200000,0x30001f0b,0x7c00100,0x230400,0x30001f0b,0xc000010,0x248000,0x30002006,0x7c00100,0x250400,0x30002128,0x4000000,0x200000,0x30002128,0x7c00100, +0x230400,0x30002128,0xc000010,0x248000,0x3000221d,0x4000000,0x810000,0x3000221d,0x4000000,0x1410000,0x3000221d,0x4000001,0x445800,0x3000221d,0x7c00100,0x230400, +0x30002300,0x4000010,0x400000,0x30002320,0x7c00100,0x230400,0x30002417,0x2802100,0x1862460,0x30002417,0x2802400,0x1862460,0x30002417,0x2806400,0x1862460,0x30002417, +0x2882000,0x1862460,0x30002417,0x4000000,0x200000,0x30002417,0x4000000,0x400000,0x30002417,0x4000000,0x1600000,0x30002417,0x4000010,0x400000,0x30002417,0x4000010, +0x1200000,0x30002417,0x6800000,0x1329800,0x30002417,0x6800100,0x1862540,0x30002417,0x7c00100,0x1830000,0x30002417,0x7d00100,0x1830000,0x3000251b,0x80000,0xc18820, +0x3000251b,0x2802100,0x962460,0x3000251b,0x3c02100,0x962460,0x3000251b,0x4000000,0x200000,0x3000251b,0x4000006,0x500000,0x3000251b,0x4000010,0x400000,0x3000251b, +0x4000010,0xb70000,0x3000251b,0x4000800,0x200000,0x3000251b,0x6800000,0x1329800,0x3000251b,0x7c00100,0x230400,0x3000251b,0x7c00900,0x230400,0x3000251b,0xc000010, +0xb48000,0x3000251b,0x12882000,0x962460,0x30002800,0x24000000,0x200000,0x30002800,0x2c000010,0x1248002,0x30002a00,0x4000000,0x1600000,0x30002b01,0x2000,0x962460, +0x30002c00,0x4000000,0x200000,0x30002c00,0x7c00100,0x220405,0x30002d19,0x7c00100,0x250400,0x30002e00,0x24000000,0x200000,0x30003000,0x24000000,0x200000,0x30003100, +0x24000000,0x200000,0x30003600,0x24000000,0x200000,0x30003700,0x24000000,0x200000,0x3000392e,0x24000000,0x200000,0x30005013,0x7c00100,0x2633801,0x30005600,0, +0x918820,0x30020600,0x4000400,0x500400,0x30020701,0x2802400,0x962460,0x30020701,0x2802400,0xc62460,0x300a3a11,0x4020000,0xe00000,0x300a3a11,0x4020000,0xe00002, +0x300a3b11,0x4020000,0xe00002,0x300a3c00,0x4008000,0xe00000,0x300a3c00,0x4010000,0xe00000,0x300a3d11,0x7c00300,0xe30002,0x300a4305,0x7c00100,0xe30400,0x300a4611, +0x7c40300,0xe30000,0x300a4829,0x7c00100,0xe30400,0x300a4829,0x7c00900,0x1230400,0x300a4929,0x4000000,0xe00000,0x3040259a,0x4000010,0x400000,0x3040259a,0x4000010, +0xb70000,0x3040259a,0xc000010,0xb48000,0x304028ba,0x4000001,0xc41c0b,0x304a3dca,0x4000000,0xe00000,0x30800c27,0x2802100,0x962460,0x30c01c92,0x6800000,0x1329800, +0x3100080e,0x7c00120,0x220402,0x3100080e,0x7c00120,0x250402,0x31005167,0x1000,0,0x3100581e,0x4000000,0x200000,0x3100581e,0x7c00100,0x230400,0x3100590d, +0x7c00100,0x230400,0x31005a09,0x7c00100,0x220400,0x31005a09,0x7c00100,0x250400,0x31005b00,0x4000000,0x200000,0x31005c00,0x80000,0x918820,0x31005c00,0x2802000, +0x962460,0x31005c00,0x2802400,0x962460,0x31005c00,0x4000000,0x200000,0x31005c00,0x4000000,0x200001,0x31005c00,0x6800000,0x962540,0x31005c00,0x6800400,0x962540, +0x31005c01,0x2802400,0x962460,0x31005d00,0x4000020,0x200005,0x31005d00,0x6800020,0x1329805,0x31005d00,0x7c00120,0x220405,0x31005d00,0x7c00120,0x250405,0x31006000, +0x82000,0x962460,0x31006000,0x180000,0x918820,0x310a5e11,0x7c40300,0xe30000,0x310a5f11,0x7c00300,0xe30001,0x32000419,0x7c00100,0x250400,0x3200080e,0x4000020, +0x200000,0x3200080e,0x7c00100,0x220400,0x3200080e,0x7c00100,0x250400,0x32000908,0x7c00100,0x220400,0x32000908,0x7c00100,0x250400,0x32000c02,0x7c00100,0x230400, +0x32000e25,0x7c00100,0x230400,0x32001d0c,0x7c00100,0x230400,0x32002800,0x80000,0x1e18820,0x32002800,0x80020,0x218820,0x32002800,0x4000001,0x445802,0x32002800, +0x24000000,0x200000,0x32002800,0x24000000,0x1500002,0x32002800,0x24000020,0x200000,0x32002800,0x2c000010,0x1248002,0x32002919,0x7c00100,0x22040f,0x32002a00,0x4000000, +0x1600000,0x32002b01,0x2000,0x962460,0x32002b01,0x2802000,0x962460,0x32002b01,0x2802020,0x962460,0x32002c00,0x4000000,0x200000,0x32002c00,0x4000020,0x200000, +0x32002c00,0x4000020,0x200005,0x32002c00,0x7c00120,0x220405,0x32002c00,0x7c00120,0x250405,0x32002e00,0x24000020,0x200000,0x32002f00,0x24000020,0x200000,0x32003000, +0x24000000,0x200000,0x32003000,0x24000020,0x200000,0x32003500,0x24000000,0x200000,0x32003600,0x24000020,0x200000,0x32003700,0x24000000,0x100000,0x32003700,0x24000000, +0x200000,0x32003800,0x24000000,0x810000,0x32003800,0x24000000,0x1410000,0x32005102,0x4000000,0x1500008,0x32005502,0x7c00100,0x230400,0x32006108,0x7c00100,0x220400, +0x32006108,0x7c00100,0x250400,0x3200622a,0x2802100,0x962460,0x3200622a,0x2806400,0x962460,0x3200622a,0x7c00100,0x230400,0x3200632b,0x2802100,0x962460,0x3200632b, +0x6804000,0x962540,0x3200632b,0x7c00100,0x230400,0x3200642c,0x2802100,0x962460,0x3200642c,0x7c00100,0x230400,0x3200652d,0x2802100,0x962460,0x3200652d,0x7c00100, +0x230400,0x32006600,0x24000020,0x200000,0x32006700,0x24000020,0x200000,0x32006800,0x24000020,0x200000,0x32006900,0x24000020,0x200000,0x32006900,0x24000020,0x810000, +0x32006900,0x24000020,0x1410000,0x32006a00,0x24000020,0x200000,0x32006a00,0x24000020,0x200001,0x32006a00,0x24000020,0x200002,0x32020701,0x2882000,0xc62460,0x32023300, +0x4000000,0x100000,0x32026c01,0x12882000,0x962460,0x32065700,0x4000000,0x810011,0x32065700,0x4000000,0x1410011,0x32086600,0x24000020,0x810000,0x32086600,0x24000020, +0x1410000,0x32086900,0x24000020,0x810000,0x32086900,0x24000020,0x1410000,0x320a3600,0x24000020,0x200000,0x320a3d11,0x7c00100,0x1230400,0x320a3e14,0x7c00100,0xe30010, +0x320a3e14,0x7c00100,0x2530000,0x320a3f16,0x7c00100,0xe30c10,0x320a4400,0x4000000,0xe00003,0x320a4929,0x4000000,0xe00000,0x320a4f11,0x7c00300,0xe30001,0x320a6b16, +0x7c00100,0x2530c00,0x32406396,0xc000010,0x448000,0x324a3dcd,0x4000000,0xe00000,0x324a3dcd,0x7c00100,0x1230400,0x324a3fc8,0x4000002,0x1200c00,0x324a53c5,0x24000000, +0xe00000,0x32820701,0x2802000,0x962460,0x40000419,0x7c00100,0x220400,0x40000519,0x7c00100,0x220400,0x40000600,0x4000400,0x200400,0x4000080e,0x7c00100,0x220400, +0x4000080e,0x7c00100,0x250400,0x4000080e,0x7c00100,0x250402,0x40000c02,0x2802100,0x962460,0x40000c02,0x2802400,0x962460,0x40000c02,0x2802500,0x962460,0x40000c02, +0x4000000,0x200000,0x40000c02,0x4000000,0x1071400,0x40000c02,0x7c00100,0x230400,0x40000c02,0x80000000,0x218960,0x40000d22,0x7c00100,0x230400,0x40000f0a,0x7c00100, +0x230400,0x40001004,0x7c00100,0x230400,0x40001110,0x2802100,0x962460,0x40001110,0x6800100,0x962540,0x4000120f,0x2802100,0x962460,0x4000120f,0x4000000,0x1600000, +0x4000120f,0x7c00100,0x230400,0x4000131f,0x7c00100,0x230400,0x40001423,0x4000000,0x200000,0x40001423,0x4000000,0x1600000,0x40001615,0x2802400,0x962460,0x40001615, +0x7c00100,0x230400,0x40002417,0x2802400,0x1862460,0x40002417,0x4000000,0x200000,0x40002800,0x6800000,0x201c00,0x40002800,0x24000002,0x200000,0x40002c00,0x4000000, +0x200002,0x40003000,0x24000000,0x200000,0x40003000,0x24000020,0x200000,0x40003700,0x24000000,0x200000,0x40005a09,0x7c00100,0x220400,0x40005a09,0x7c00100,0x250400, +0x40005d00,0x7c00120,0x220405,0x40006f30,0x2802100,0x962460,0x40006f30,0x2802400,0x962460,0x40006f30,0x4000000,0x200000,0x40006f30,0x6800000,0x1329800,0x40006f30, +0x6800100,0x962540,0x40006f30,0x7c00100,0x230400,0x40006f30,0xc000010,0xb48000,0x40007034,0x7c00100,0x1830000,0x40007117,0x4000000,0x200000,0x40007208,0x7c00100, +0x220400,0x4000720e,0x7c00100,0x220400,0x4000720e,0x7c00500,0x22040e,0x4000720e,0x7c00500,0x22040f,0x40007219,0x7c00100,0x220400,0x40007219,0x7c00500,0x220400, +0x40007219,0x7c00500,0x22040e,0x40007219,0x7c00500,0x22040f,0x40007300,0x24000000,0x200000,0x40007400,0x4000000,0x200000,0x40007531,0x7c00100,0x230400,0x40007631, +0x7c00100,0x230400,0x40007835,0x4000010,0x400000,0x40007835,0x7c00100,0x230400,0x40007933,0x7c00100,0x230400,0x40007a32,0x6800000,0x1329800,0x40007a32,0x7c00100, +0x230400,0x40007b2f,0x7c00100,0x230400,0x40007c00,0x4000000,0x200000,0x40020701,0x2802400,0x962460,0x40020701,0x2802400,0xc62460,0x40023300,0x4000000,0x200000, +0x40027d01,0x12882000,0x962460,0x400a3700,0x24000000,0x200000,0x400a3700,0x24000000,0xe00000,0x400a4400,0x4000000,0xe0000d,0x400a4412,0x4000000,0xe00002,0x400a4412, +0x4000000,0xe00003,0x400a4500,0x4000000,0xe0000d,0x400a5300,0x4000000,0x810010,0x400a5300,0x4000000,0x1410010,0x40507709,0x4000000,0x200000,0x4050770c,0x4000000, +0x400000,0x4050770f,0x4000000,0x200000,0x4050770f,0x4000000,0x400000,0x40c01489,0x4000000,0x200000,0x40d05107,0x4000000,0x200000,0x41000419,0x7c00100,0x220400, +0x41000419,0x7c00100,0x250400,0x4100080e,0x7c00100,0x220400,0x4100080e,0x7c00100,0x250400,0x41000908,0x7c00100,0x220400,0x41000908,0x7c00100,0x250400,0x41000b13, +0x2802000,0x962460,0x41000b13,0x2802100,0x962460,0x41000b13,0x4000000,0xb00000,0x41000c02,0x2802100,0x962460,0x41000c02,0x4000000,0x1500000,0x41000c02,0xc000010, +0xb48000,0x41000f0a,0x7c00100,0x230400,0x41001004,0x7c00100,0x230400,0x41001423,0x7c00100,0x230400,0x41001b27,0x4000000,0x500000,0x41001d0c,0x7c00100,0x22040f, +0x41001d0c,0x7c00100,0x230400,0x41001f0b,0x2802400,0x962460,0x41001f0b,0x4000000,0x200000,0x41001f0b,0x7c00100,0x230400,0x41002800,0x24000000,0x200000,0x41002800, +0x24000000,0x400000,0x41002919,0x7c00100,0x22040e,0x41002a00,0x4000000,0x1600000,0x41002b01,0x2802020,0x962460,0x41002c00,0x4000000,0x200000,0x41002c00,0x7c00120, +0x220405,0x41003000,0x24000000,0x200000,0x41003700,0x24000000,0x200000,0x41003700,0x24000000,0xe00000,0x41005d00,0x7c00120,0x220405,0x41006600,0x24000020,0x200000, +0x41006600,0x24000020,0x810000,0x41006600,0x24000020,0x1410000,0x41007208,0x7c00100,0x22040f,0x41007219,0x7c00100,0x220400,0x41007300,0x24000000,0x200000,0x41007e0e, +0x2802000,0x962460,0x41007e0e,0x4000000,0x200000,0x41007f0e,0x4000000,0x200000,0x41007f0e,0x7c00100,0x230400,0x41008002,0x7c00100,0x230400,0x41008137,0x2802100, +0x962460,0x41008137,0x4000000,0x200000,0x41008137,0x6800100,0x962540,0x41008137,0x7c00100,0x230400,0x41008301,0x2802000,0x962460,0x41008407,0x4000000,0x200000, +0x41008407,0x4000000,0x400000,0x41008407,0x4000000,0xb00000,0x41008407,0x7c00100,0x220400,0x41008407,0x7c00100,0x250400,0x4100850b,0x7c00100,0x230400,0x4100860b, +0x4000000,0x200000,0x4100860b,0x7c00100,0x230400,0x4100870c,0x7c00100,0x220400,0x41008838,0x7c00100,0x220400,0x41008838,0x7c00100,0x250400,0x41008939,0x2802000, +0x962460,0x41008939,0x2802100,0x962460,0x41008939,0x2806000,0x962460,0x41008939,0x4000000,0x200000,0x41008939,0x4000000,0x400000,0x41008939,0x7c00100,0x230400, +0x41008939,0xc000010,0x448000,0x41008a00,0x4000400,0x200400,0x41008b3b,0x4000000,0x1800000,0x41008b3b,0x6800000,0x1329800,0x41008b3b,0x7c00100,0x1830000,0x41008b3b, +0x7e00100,0x1830000,0x41008c3d,0x4000010,0x400000,0x41008c3d,0x7c00100,0x230400,0x41008d0e,0x7c00100,0x22040f,0x41008d19,0x7c00100,0x220400,0x41008d19,0x7c00100, +0x22040f,0x41008e00,0x24000000,0x200000,0x41008e00,0x24000000,0x400000,0x41008e00,0x24000000,0x1710000,0x41008e00,0x24000006,0x400000,0x41008f3a,0x2802100,0x962460, +0x41008f3a,0x2806000,0x962460,0x41008f3a,0x4000000,0x200000,0x41008f3a,0x6800100,0x962540,0x41008f3a,0x7c00100,0x230400,0x4100903c,0x7c00100,0x230400,0x4100903c, +0x7c00100,0x23040f,0x41020701,0x2802000,0x962460,0x41020701,0x2802000,0xc62460,0x410a3700,0x24000000,0x200000,0x410a3700,0x24000000,0xe00000,0x410a4412,0x4000000, +0xe00003,0x410a4711,0x7c40300,0xe30000,0x410a4f11,0x7c00300,0xe30001,0x410a9100,0x4000000,0x800010,0x410a9100,0x4000000,0x810010,0x410a9100,0x4000000,0x870010, +0x410a9100,0x4000000,0xb00010,0x410a9100,0x4000000,0xf00010,0x410a9100,0x4000000,0x1001410,0x410a9100,0x4000000,0x1071010,0x410a9100,0x4000000,0x1071410,0x410a9100, +0x4000000,0x1410010,0x41408ad0,0x4000400,0x200000,0x414a82ca,0x4000000,0xe00000,0x41808300,0x2802000,0x962460,0x41c01489,0x6800000,0x1329800,0x50000419,0x7c00100, +0x220400,0x50000419,0x7c00100,0x250400,0x5000080e,0x7c00100,0x220400,0x50000908,0x7c00100,0x220400,0x50000908,0x7c00100,0x250400,0x50000b13,0x2802500,0x962460, +0x50000f0a,0x7c00100,0x230400,0x50001615,0x2802100,0x962460,0x50001615,0x7c00100,0x230400,0x50002b01,0x2802020,0x962460,0x50002c00,0x4000000,0x200000,0x50002c19, +0x7c00100,0x220400,0x50002d19,0x7c00100,0x220400,0x50003000,0x24000000,0x200000,0x50003000,0x24000020,0x200000,0x50003700,0x24000000,0x200000,0x50005d00,0x7c00120, +0x220405,0x50005d00,0x7c00120,0x250405,0x50006108,0x7c00100,0x220400,0x50006108,0x7c00100,0x250400,0x50006600,0x24000020,0x200000,0x50007300,0x24000000,0x200000, +0x50008301,0x2802400,0x962460,0x50008a00,0x7c00500,0x230400,0x50009257,0x2802400,0x962460,0x50009257,0x4000000,0x200000,0x50009257,0x4000010,0x1071400,0x50009257, +0x6800000,0x1329800,0x50009257,0x7c00100,0x230400,0x50009257,0x7c00500,0x230400,0x50009257,0x7c00900,0x230400,0x50009257,0xc000010,0xb48000,0x5000933e,0x2802100, +0x962460,0x5000933e,0x2802400,0x962460,0x5000933e,0x4000000,0x200000,0x5000933e,0x4000000,0x400000,0x5000933e,0x4000010,0x400000,0x5000933e,0x6800000,0x1329800, +0x5000933e,0x6800100,0x962540,0x5000933e,0x6800100,0x962541,0x5000933e,0x6804400,0x962540,0x5000933e,0x7c00100,0x230400,0x5000933e,0x7c00100,0x230401,0x5000933e, +0xc000010,0x448000,0x50009419,0x7c00100,0x220400,0x50009419,0x7c00100,0x250400,0x50009500,0x4000400,0x200400,0x5000965a,0x4000000,0x500000,0x5000965a,0x7c00100, +0x230400,0x5000965a,0xc000010,0xb48000,0x5000975b,0x4000000,0x200000,0x5000975b,0x4000010,0x400000,0x5000975b,0x7c00100,0x230400,0x50009865,0x7c00100,0x230400, +0x50009965,0x4000010,0x400000,0x50009965,0x7c00100,0x230400,0x50409aca,0x4000000,0x200000,0x5100080e,0x7c00100,0x220400,0x5100080e,0x7c00100,0x250400,0x51000c02, +0x2802100,0x962460,0x51000c02,0x4000000,0x1500000,0x51000c02,0x4000020,0x200000,0x51000c02,0x7c00100,0x230400,0x51000f0a,0x7c00100,0x230400,0x51000f0a,0x7c00500, +0x230400,0x51001110,0x2802100,0x962460,0x5100131f,0x2802100,0x962460,0x51001423,0x7c00100,0x230400,0x51001524,0x2802100,0x962460,0x51001524,0x4000000,0x200000, +0x51001524,0x7c00100,0x230400,0x5100171a,0x2802100,0x962460,0x5100171a,0x4000000,0x200000,0x5100171a,0x4000000,0x1500000,0x5100171a,0x7c00100,0x230400,0x51001b27, +0x4000000,0x200000,0x51001b27,0x4000000,0x400000,0x51001b27,0x4000000,0x500000,0x51001b27,0x7c00100,0x230400,0x51001c1c,0x2802100,0x1862460,0x51001c1c,0x2802500, +0x1862460,0x51001c1c,0x2806400,0x1862460,0x51001c1c,0x4000000,0x1800000,0x51001c1c,0x6800000,0x1329800,0x51001c1c,0x6800100,0x1862400,0x51001c1c,0x6800100,0x1862540, +0x51001c1c,0x6800500,0x1862400,0x51001c1c,0x7c00100,0x1830000,0x5100251b,0x7c00100,0x230400,0x51002619,0x7c00100,0x220400,0x51002619,0x7c00100,0x250400,0x51002800, +0x80020,0x218820,0x51002c00,0x4000000,0x200000,0x51002d19,0x7c00100,0x230400,0x51003700,0x24000000,0x200000,0x51003700,0x24000000,0xe00000,0x51005201,0x2802400, +0x962460,0x51005c00,0x4000000,0x200000,0x51006108,0x7c00100,0x220400,0x51006108,0x7c00100,0x250400,0x51006600,0x24000020,0x200000,0x51006600,0x24000020,0x810000, +0x51006600,0x24000020,0x1410000,0x51007300,0x24000000,0x200000,0x51007300,0x24000020,0x200000,0x51008002,0x7c00100,0x230400,0x51008301,0x2802000,0x962460,0x51008301, +0x2802400,0x962460,0x51008301,0x2802400,0xc62460,0x51008a00,0x7c00500,0x230400,0x51008e00,0x24000000,0x200000,0x51008e00,0x24000000,0x400000,0x51008e00,0x24000000, +0x810000,0x51008e00,0x24000000,0x1400000,0x51008e00,0x24000000,0x1410000,0x51008e00,0x24000000,0x1710000,0x51008e00,0x24000002,0x200000,0x51008e00,0x24000500,0x230400, +0x51008e00,0x2c000010,0xb48000,0x51009419,0x7c00100,0x220400,0x51009419,0x7c00100,0x22040e,0x51009419,0x7c00100,0x22040f,0x51009419,0x7c00100,0x250400,0x51009500, +0x4000400,0x200400,0x51009500,0x7c00500,0x230400,0x51009519,0x7c00100,0x220400,0x51009519,0x7c00100,0x22040f,0x51009519,0x7c00100,0x230400,0x51009519,0x7c00100, +0x250400,0x51009b71,0x2802100,0x962460,0x51009b71,0x6800000,0x1329800,0x51009b71,0x6800100,0x962540,0x51009b71,0x6804400,0x962540,0x51009b71,0x7c00100,0x230400, +0x51009c52,0x2802100,0x962460,0x51009c52,0x2802400,0x962460,0x51009c52,0x2802d00,0x962460,0x51009c52,0x4000010,0x400000,0x51009c52,0x6800000,0x1329800,0x51009c52, +0x6800100,0x962540,0x51009c52,0x7c00100,0x230400,0x51009c52,0xc000010,0x448000,0x51009d6d,0x6800000,0x1329800,0x51009d6d,0x7c00100,0x230400,0x51009d6d,0x7c00500, +0x230400,0x51009d6d,0x7c00d00,0x230400,0x51009d6d,0xc000010,0x448000,0x51009e08,0x2802100,0x962460,0x51009f63,0x4000010,0x400000,0x51009f63,0x6800000,0x1329800, +0x51009f63,0x7c00100,0x230400,0x51009f63,0x7c00900,0x230400,0x51009f63,0xc000010,0x448000,0x51009f63,0xc000010,0xb48000,0x5100a008,0x2000,0x962460,0x5100a008, +0x2802400,0x962460,0x5100a008,0x4000000,0x200000,0x5100a008,0x7c00100,0x220400,0x5100a008,0x7c00100,0x230400,0x5100a008,0x7c00100,0x250400,0x5100a008,0x7c00500, +0x230400,0x5100a16f,0x2806400,0x962460,0x5100a16f,0x6800000,0x1329800,0x5100a16f,0x6800100,0x962540,0x5100a16f,0x7c00100,0x230400,0x5100a16f,0xc000010,0x448000, +0x5100a24f,0x2802100,0x962460,0x5100a24f,0x2802400,0x962460,0x5100a24f,0x6800000,0x1329800,0x5100a24f,0x7c00100,0x230400,0x5100a24f,0xc000010,0x448000,0x5100a36e, +0x2802100,0x962460,0x5100a36e,0x4000000,0x200000,0x5100a36e,0x6800100,0x962540,0x5100a36e,0x6804400,0x962540,0x5100a36e,0x7c00100,0x230400,0x5100a442,0x2802100, +0x962460,0x5100a442,0x4000000,0x200000,0x5100a442,0x6800000,0x1329800,0x5100a442,0x6800100,0x962540,0x5100a442,0x7c00100,0x230400,0x5100a442,0xc000010,0x448000, +0x5100a500,0x4000000,0x200000,0x5100a600,0x4000000,0x200000,0x5100a601,0x2802000,0x962460,0x5100a76b,0x7c00100,0x230400,0x5100a868,0x7c00100,0x230400,0x5100a96c, +0x4000000,0x200000,0x5100a96c,0x7c00100,0x230400,0x5100aa00,0x4000000,0xe00000,0x5100ab00,0x4000000,0xe00000,0x51086600,0x24000020,0x810000,0x51086600,0x24000020, +0x1410000,0x510a4005,0x7c00100,0xe30400,0x510a4711,0x7c40300,0xe30000,0x510a7300,0x24000000,0x200000,0x510aaa00,0x4000000,0xe00000,0x5140a2fe,0x4000400,0x400000, +0x514a82ca,0x4000000,0xe00000,0x51802bbc,0x2802000,0x962460,0x51c00908,0x2802400,0x962460,0x51c0a008,0x2802400,0x962460,0x52000f0a,0x2802100,0x962460,0x52000f0a, +0x6800100,0x962540,0x52000f0a,0x7c00100,0x230400,0x52001004,0x4000000,0x1600000,0x52001b00,0x4000000,0x200000,0x52001c1c,0x2802100,0x1862460,0x52001c1c,0x6800100, +0x1862400,0x52001c1c,0x6800500,0x1862400,0x52001e12,0x7c00100,0x2230500,0x52001e12,0x7c00100,0x2330520,0x52002128,0x4000002,0x400000,0x52002128,0x7c00100,0x230400, +0x52002a00,0x4000000,0x1500000,0x52002a00,0x4000000,0x1600000,0x52002d00,0x4000000,0x200006,0x52003000,0x24000000,0x200000,0x52006108,0x7c00100,0x220400,0x52006108, +0x7c00100,0x250400,0x52008301,0x2802400,0x962460,0x52008407,0x2802400,0x962460,0x52008407,0x7c00100,0x220400,0x52008407,0x7c00100,0x250400,0x52008b3b,0x6800000, +0x1800000,0x52008b3b,0x7c00100,0x1830000,0x52008e00,0x24000000,0x400000,0x52009419,0x7c00100,0x250400,0x5200975b,0x4000000,0x200000,0x5200ac7e,0x2802000,0x962460, +0x5200ac7e,0x2802100,0x962460,0x5200ac7e,0x2802400,0x962460,0x5200ac7e,0x4000010,0x200000,0x5200ac7e,0x7c00100,0x230400,0x5200ac7e,0xc000010,0x248000,0x5200ad28, +0x7c00100,0x230400,0x5200ae6a,0x2802100,0x1862460,0x5200ae6a,0x2802400,0x962460,0x5200ae6a,0x2802400,0x1862460,0x5200ae6a,0x2806000,0x1862460,0x5200ae6a,0x4000000, +0x1800000,0x5200ae6a,0x6800000,0x1329800,0x5200ae6a,0x6800100,0x1862400,0x5200ae6a,0x6800100,0x1862540,0x5200ae6a,0x7c00100,0x1830000,0x5200ae6a,0x7c00900,0x1830000, +0x5200ae6a,0xc000010,0x1848000,0x5200b083,0x4000010,0x400000,0x5200b083,0x7c00100,0x230400,0x5200b083,0xc000010,0x448000,0x5200b182,0x2802400,0x962460,0x5200b182, +0x4000000,0x200000,0x5200b182,0x4000010,0x400000,0x5200b182,0x7c00100,0x230400,0x5200b182,0xc000010,0x448000,0x5200b30a,0x2802400,0x962460,0x5200b30a,0x4000000, +0x200000,0x5200b30a,0x7c00100,0x230400,0x5200b54e,0x2802100,0x962460,0x5200b54e,0x2802400,0x962460,0x5200b54e,0x4000000,0x200000,0x5200b54e,0x4000010,0x400000, +0x5200b54e,0x6800000,0x1329800,0x5200b54e,0x6800100,0x962540,0x5200b54e,0x6804400,0x962540,0x5200b54e,0x7c00100,0x230400,0x5200b54e,0xc000010,0x448000,0x5200b61c, +0x4000000,0x1800000,0x5200b61c,0x6800500,0x1862400,0x5200b61c,0x7c00100,0x1830000,0x5200b61c,0x7c00900,0x1830000,0x5200b77f,0x2802100,0x1862460,0x5200b77f,0x2802400, +0x1862460,0x5200b77f,0x4000000,0x1800000,0x5200b77f,0x4000010,0x1800000,0x5200b77f,0x7c00100,0x1830000,0x5200b77f,0x7c00500,0x1830000,0x5200b77f,0x7c00900,0x1830000, +0x5200b77f,0x7e00100,0x1830000,0x5200b873,0x2802100,0x962460,0x5200b873,0x2806400,0x962460,0x5200b873,0x6800000,0x1329800,0x5200b873,0x6800100,0x962540,0x5200b873, +0x6800400,0x962540,0x5200b873,0x7c00100,0x230400,0x5200b873,0xc000010,0x448000,0x5200b912,0x7c00100,0x2230500,0x5200b912,0x7c00100,0x2330520,0x5200ba74,0x4000000, +0x200000,0x5200ba74,0x4000010,0x400000,0x5200ba74,0x7c00100,0x230400,0x5200bb85,0x4000000,0x200000,0x5200bb85,0x7c00100,0x230400,0x5200bc75,0x4000000,0x400000, +0x5200bc75,0x4000010,0x400000,0x5200bc75,0x7c00100,0x230400,0x5200bd7d,0x4000000,0x200000,0x5200bd7d,0x7c00100,0x230400,0x5200be7a,0x4000000,0x200000,0x5200be7a, +0x7c00100,0x230400,0x5200bf58,0x7c00100,0x230400,0x5200c002,0x4000000,0x200000,0x5200c178,0x2802100,0x962460,0x5200c178,0x2802400,0x962460,0x5200c178,0x2806400, +0x962460,0x5200c178,0x4000000,0x200000,0x5200c178,0x6800100,0x962540,0x5200c178,0x7c00100,0x230400,0x5200c178,0x7c00100,0x230401,0x5200c178,0xc000010,0x448000, +0x5200c178,0x80000000,0x218960,0x5200c247,0x7c00100,0x230400,0x5200c247,0x7c00100,0x830400,0x5200c247,0x7c00100,0x1430400,0x5200c300,0x4000000,0x200003,0x52022d00, +0x4000000,0x100006,0x52023700,0x24000000,0x100000,0x52023700,0x24000000,0xe00000,0x52023700,0x24000000,0x2800000,0x52024400,0x4000000,0x100000,0x52027300,0x24000000, +0x100000,0x5202c300,0x4000000,0x100000,0x5202c300,0x4000000,0x100002,0x5202c300,0x4000000,0x100003,0x5202c300,0x4000000,0x10000d,0x5202c300,0x4000100,0x150400, +0x5202c300,0x4000100,0x15040d,0x520a1e12,0x7c00100,0x2130480,0x520a3700,0x24000000,0xe00000,0x520a3800,0x24000000,0x100000,0x520a4711,0x7c40300,0xe30000,0x520a4f11, +0x7c00300,0xe30001,0x520a7300,0x24000000,0x100000,0x520ab412,0x7c00100,0x2130480,0x520ac400,0x4000000,0xe00002,0x520ac400,0x4000000,0xe0000d,0x520ac414,0x4000000, +0xe0000d,0x520ac511,0x7c40300,0xe30000,0x5240af9c,0x7c00100,0x230400,0x5240afa1,0x4000400,0x200000,0x5240afa3,0x6800400,0x962540,0x5240afa3,0x7c00100,0x230400, +0x5240afad,0x7c00100,0x230400,0x5240afaf,0x7c00100,0x230400,0x5240b2d2,0x4000000,0x200000,0x5240b2d2,0x4000000,0x1500000,0x5240b2dd,0x4000000,0x200000,0x5240b2eb, +0x4000000,0x200000,0x524a44ca,0x4000000,0xe00003,0x5250b501,0x7c00900,0x230400,0x5280af9c,0x2802400,0x962460,0x5280af9d,0x2802400,0x962460,0x5280afa3,0x2802400, +0x962460,0x5280afa5,0x2802400,0x962460,0x5280afa7,0x2802400,0x962460,0x52c0b3f8,0x2802400,0x962460,0x52c0b3fc,0x7c00100,0x230400,0x60000c02,0x2802100,0x962460, +0x60000c02,0x7c00100,0x230400,0x60000f0a,0x2802100,0x962460,0x60000f0a,0x6800100,0x962540,0x60000f0a,0x7c00100,0x230400,0x6000131f,0x4000000,0x200000,0x6000171a, +0x7c00100,0x230400,0x6000171a,0x7c00100,0x230560,0x60001b27,0x2802100,0x962460,0x60001b27,0x4000000,0xc00000,0x60001b27,0x7c00100,0x230400,0x60001f0b,0x2802400, +0x962460,0x60002919,0x7c00100,0x22040e,0x60002a00,0x4000000,0x1600000,0x60003000,0x24000000,0x200000,0x60003000,0x24000000,0xe00000,0x60003700,0x24000000,0x200000, +0x60003800,0x24000000,0x1710000,0x60005102,0x4000000,0x200000,0x60006108,0x7c00100,0x220400,0x60006108,0x7c00100,0x250400,0x60006600,0x24000020,0x200000,0x60008301, +0x2802400,0xc62460,0x6000903c,0x2806000,0x962460,0x6000903c,0x4000000,0x400000,0x60009519,0x7c00100,0x220400,0x60009519,0x7c00100,0x250400,0x6000a008,0x7c00100, +0x220400,0x6000a008,0x7c00100,0x250400,0x6000c300,0x4000000,0x2703580,0x6000c654,0x2802000,0x962460,0x6000c654,0x4000010,0x200000,0x6000c654,0x7c00100,0x230400, +0x6000c73f,0x2802000,0x962460,0x6000c73f,0x2802100,0x962460,0x6000c73f,0x4000000,0x200000,0x6000c73f,0x6800100,0x962540,0x6000c73f,0x6804000,0x962540,0x6000c73f, +0x7c00100,0x230400,0x6000c80b,0x7c00100,0x230400,0x6000c941,0x2802100,0x962460,0x6000c941,0x2806400,0x962460,0x6000c941,0x4000000,0x200000,0x6000c941,0x4000010, +0x200000,0x6000c941,0x6800000,0x1329800,0x6000c941,0x6800100,0x962540,0x6000c941,0x7c00100,0x230400,0x6000c941,0xc000010,0x448000,0x6000ca82,0x7c00100,0x230400, +0x6000cc00,0x4000000,0xe00000,0x6000d000,0x4000000,0x200000,0x6002c300,0x4000000,0x100000,0x6002c300,0x4000000,0x10000d,0x6002c300,0x4000100,0x150400,0x6002c300, +0x4000100,0x15040d,0x600a3000,0x24000000,0x200000,0x600a3000,0x24000000,0xe00000,0x600a3700,0x24000000,0x200000,0x600a3800,0x24000000,0x200000,0x600a3800,0x24000000, +0x2800000,0x600a4305,0x7c00100,0xe30400,0x600ac300,0x4000000,0x100000,0x600ac400,0x4000000,0xe0000d,0x600acb14,0x7c00100,0xe30000,0x600acb16,0x7c00100,0xe30c00, +0x600acc00,0x4000000,0xe00000,0x600acd00,0x4000000,0x200000,0x600acd00,0x4000000,0xe00000,0x600acd00,0x4000000,0x2800000,0x600ace00,0x4000000,0xe00000,0x600ace00, +0x4000000,0x2800000,0x600acf00,0x4000000,0xe00000,0x600acf00,0x4000000,0x2800000,0x600ad111,0x7c40300,0xe30000,0x604ac4ca,0x4000000,0xe00003,0x61000a03,0x4000000, +0x1600000,0x61000c02,0x80000000,0x218960,0x6100120f,0x4000000,0x200000,0x61001a18,0x7c00100,0x1830000,0x61001d0c,0x7c00100,0x230400,0x61001d0c,0x7c00100,0x250400, +0x61006600,0x24000020,0x200000,0x61008407,0x7c00100,0x220400,0x61008407,0x7c00100,0x250400,0x6100870c,0x7c00100,0x220400,0x61008e00,0x24000000,0x200000,0x61008e00, +0x24000000,0x400000,0x61008e00,0x24000002,0x300000,0x6100903c,0x7c00100,0x230400,0x61009519,0x7c00100,0x220400,0x61009519,0x7c00100,0x250400,0x61009519,0x7c00500, +0x22040f,0x61009b71,0x2802100,0x962460,0x61009b71,0x2806400,0x962460,0x61009b71,0x7c00100,0x230400,0x6100a008,0x2802100,0x962460,0x6100c300,0x4000000,0x20000f, +0x6100cd00,0x4000000,0x200000,0x6100d202,0x2802400,0x962460,0x6100d202,0x2802500,0x962460,0x6100d202,0x7c00100,0x230400,0x6100d302,0x4000020,0x200000,0x6100d302, +0x7c00120,0x230405,0x6100d476,0x2802100,0x962460,0x6100d476,0x2802100,0x962461,0x6100d476,0x2806400,0x962460,0x6100d476,0x4000000,0x400000,0x6100d476,0x6800000, +0x1329800,0x6100d476,0x6800100,0x962540,0x6100d476,0x7c00100,0x230400,0x6100d476,0xc000010,0x448000,0x6100d573,0x2802100,0x962460,0x6100d573,0x2806400,0x962460, +0x6100d573,0x6800100,0x962540,0x6100d573,0x7c00100,0x230400,0x6100d573,0x7c00900,0x230400,0x6100d573,0xc000010,0x448000,0x6100d68d,0x7c00100,0x230400,0x6100d756, +0x7c00100,0x230400,0x6100d85c,0x2802500,0x962460,0x6100d85c,0x6800100,0x962540,0x6100d85c,0x7c00100,0x230400,0x6100d85c,0x7c00500,0x230400,0x6100d997,0x2802100, +0x962460,0x6100d997,0x4000000,0x200000,0x6100d997,0x4000000,0x400000,0x6100d997,0x6800000,0x1329800,0x6100d997,0x6800100,0x962540,0x6100d997,0x6804400,0x962540, +0x6100d997,0x7c00100,0x230400,0x6100d997,0x7c00100,0x230560,0x6100d997,0xc000010,0x448000,0x6100da98,0x6800000,0x1329800,0x6100da98,0x7c00100,0x230400,0x6100db71, +0x4000000,0x200000,0x6100dc99,0x2802100,0x962460,0x6100dc99,0x2802400,0x962460,0x6100dc99,0x6800000,0x1329800,0x6100dc99,0x6800100,0x962540,0x6100dc99,0x6804400, +0x962540,0x6100dc99,0x7c00100,0x230400,0x610a4711,0x7c40300,0xe30000,0x610a4f11,0x7c00300,0xe30001,0x610ace00,0x4000000,0xe00000,0x6140afa1,0x7c00100,0x230400, +0x6140afa3,0x7c00100,0x230400,0x6180af9e,0x2802400,0x962460,0x62002a00,0x4000000,0x1600000,0x63002800,0x80000,0x918820,0x63c00c15,0x80000,0x918820,0x7000080e, +0x7c00100,0x250400,0x70000a03,0x4000000,0x200000,0x70000c00,0x80000000,0x218960,0x70000f0a,0x7c00100,0x230400,0x70001004,0x7c00100,0x230400,0x70001524,0x2802100, +0x962460,0x70001524,0x7c00100,0x230400,0x70001615,0x2802100,0x962460,0x7000171a,0x2802100,0x962460,0x70001821,0x6800000,0x1329800,0x70002320,0x7c00100,0x230400, +0x70002a00,0x4000000,0x1500000,0x70002a00,0x4000000,0x1600000,0x70003000,0x24000000,0x200000,0x70003800,0x24000000,0xe00000,0x70005201,0x2802400,0x962460,0x7000581e, +0x7c00100,0x230400,0x70006108,0x7c00100,0x220400,0x70006108,0x7c00100,0x250400,0x70006f30,0x7c00100,0x230400,0x70007300,0x24000000,0x200000,0x70007f0e,0x4000000, +0x200000,0x70008301,0x2802100,0x962460,0x70008301,0x2802400,0x962460,0x70008e00,0x24000000,0x200000,0x70008e00,0x24000000,0x400000,0x70008e00,0x24000002,0x400000, +0x70008e00,0x24000008,0x1410000,0x70008e00,0x24000010,0x400000,0x70008e00,0x2c000010,0x448000,0x70009519,0x7c00100,0x220400,0x70009519,0x7c00100,0x230400,0x70009519, +0x7c00100,0x250400,0x70009865,0x7c00100,0x230400,0x70009965,0x4000010,0x400000,0x70009965,0x7c00100,0x230400,0x7000a008,0x7c00100,0x220400,0x7000a008,0x7c00100, +0x250400,0x7000a008,0x7c00500,0x22040f,0x7000a50e,0x4000000,0x200000,0x7000b61c,0x2802500,0x1862460,0x7000b61c,0x6800500,0x1862400,0x7000b61c,0x7c00100,0x1830000, +0x7000c300,0x4000000,0x100000,0x7000c941,0x2806000,0x962460,0x7000cc00,0x4000000,0xe00000,0x7000cd00,0x4000000,0x200000,0x7000cd00,0x4000000,0xe00000,0x7000cd00, +0x4000000,0x2800000,0x7000cf00,0x4000000,0xe00000,0x7000d202,0x2802100,0x962460,0x7000d202,0x7c00100,0x230400,0x7000d997,0x7c00100,0x230400,0x7000d997,0xc000010, +0x248000,0x7000dd86,0x2802400,0x962460,0x7000dd86,0x7c00100,0x230400,0x7000dd86,0xc000010,0x448000,0x7000de9f,0x4000000,0x200000,0x7000de9f,0x7c00100,0x230400, +0x7000e001,0x2400,0x962460,0x7000e001,0x2802400,0x962460,0x7000e187,0x2802000,0x962460,0x7000e187,0x2802100,0x962460,0x7000e187,0x4000000,0x200000,0x7000e187, +0x7c00100,0x230400,0x7000e187,0xc000010,0x448000,0x7000e288,0x7c00100,0x230400,0x7000e300,0x4000000,0x200000,0x7000e489,0x2802100,0x962460,0x7000e489,0x2802400, +0x962460,0x7000e489,0x6800100,0x962540,0x7000e489,0x6800100,0x962541,0x7000e489,0x6804400,0x962540,0x7000e489,0x7c00100,0x230400,0x7000e489,0x7c00900,0x230400, +0x7000e59d,0x2802100,0x962460,0x7000e59d,0x2802400,0x962460,0x7000e59d,0x4000000,0x200000,0x7000e59d,0x4000010,0x200000,0x7000e59d,0x6800100,0x962540,0x7000e59d, +0x6804400,0x962540,0x7000e59d,0x7c00100,0x230400,0x7000e59d,0xc000010,0x448000,0x7000e691,0x2802100,0x962460,0x7000e691,0x2802400,0x962460,0x7000e691,0x2806400, +0x962460,0x7000e691,0x6800000,0x1329800,0x7000e691,0x6800100,0x962540,0x7000e691,0x7c00100,0x230400,0x7000e700,0x4000400,0x200400,0x7000e70e,0x7c00100,0x220400, +0x7000e719,0x7c00100,0x220400,0x7000e719,0x7c00500,0x22040f,0x7000e853,0x7c00100,0x230400,0x7000e9a0,0x2802400,0x962460,0x7000e9a0,0x4000000,0x200000,0x7000e9a0, +0x4000000,0x500000,0x7000e9a0,0x7c00100,0x230400,0x7000ea79,0x2802400,0x962460,0x7000ea79,0x4000000,0x200000,0x7000ea79,0x4000000,0xf00000,0x7000ea79,0x4000010, +0x400000,0x7000ea79,0x7c00100,0x230400,0x7000eb8c,0x2802400,0x962460,0x7000eb8c,0x4000000,0x200000,0x7000eb8c,0x7c00100,0x230400,0x7000eca3,0x2802100,0x962460, +0x7000eca3,0x2806400,0x962460,0x7000eca3,0x4000000,0x200000,0x7000eca3,0x6800000,0x1329800,0x7000eca3,0x6800100,0x962540,0x7000eca3,0x7c00100,0x230400,0x7000eca3, +0xc000010,0x448000,0x7000ed95,0x6800000,0x1329800,0x7000ed95,0x7c00100,0x230400,0x7000ed95,0xc000010,0x448000,0x7000ee1c,0x2802500,0x1862460,0x7000ee1c,0x6800000, +0x1329800,0x7000ee1c,0x7c00100,0x1830000,0x7000ee1c,0x7c00900,0x1830000,0x7000ef8f,0x4000000,0x200000,0x7000ef8f,0x7c00100,0x230400,0x7000f08e,0x4000000,0x200000, +0x7000f08e,0x7c00100,0x230400,0x7000f159,0x2802100,0x962460,0x7000f159,0x7c00100,0x230400,0x7000f200,0x4000000,0x200000,0x7000f200,0x4000000,0x1200000,0x7000f200, +0x4000000,0x1710000,0x7000f34b,0x2802400,0x962460,0x7000f34b,0x4000000,0x200000,0x7000f34b,0x4000010,0x400000,0x7000f34b,0x6800000,0x1329800,0x7000f34b,0x7c00100, +0x230400,0x7000f34b,0x7c00900,0x230400,0x7000f34b,0xc000010,0x448000,0x7000f490,0x4000000,0x200000,0x7000f490,0x7c00100,0x230400,0x7000f5a5,0x7c00100,0x230400, +0x7000f67b,0x4000000,0x200000,0x7000f67b,0x4000010,0x200000,0x7000f67b,0x7c00100,0x230400,0x7000f8a6,0x2802100,0x962460,0x7000f8a6,0x2802400,0x962460,0x7000f8a6, +0x2806400,0x962460,0x7000f8a6,0x4000000,0x500000,0x7000f8a6,0x4000010,0xb00000,0x7000f8a6,0x4000800,0x200000,0x7000f8a6,0x6800100,0x962540,0x7000f8a6,0x6800100, +0x962541,0x7000f8a6,0x7c00100,0x230400,0x7000f8a6,0xc000010,0x448000,0x7000f921,0x4000000,0x200000,0x7000fa00,0x4000000,0x200000,0x7000fb9e,0x2802100,0x962460, +0x7000fb9e,0x2802400,0x962460,0x7000fb9e,0x2806400,0x962460,0x7000fb9e,0x4000000,0x200000,0x7000fb9e,0x6800000,0x1329800,0x7000fb9e,0x6800100,0x962540,0x7000fb9e, +0x6800100,0x962541,0x7000fb9e,0x7c00100,0x230400,0x7000fc92,0x4000000,0x200000,0x7000fc92,0x6800000,0x1329800,0x7000fc92,0x7c00100,0x220400,0x7000fc92,0x7c00100, +0x230400,0x7000fc92,0x7c00100,0x250400,0x700acd00,0x4000000,0xe00000,0x700acd00,0x4000000,0x2800000,0x700ace00,0x4000000,0xe00000,0x700acf00,0x4000000,0xe00000, +0x700acf00,0x4000000,0x2800000,0x7050df11,0x4000000,0x200000,0x7050f719,0x80000,0x918820,0x7080afa1,0x2802400,0x962460,0x7090df11,0x2802400,0x962460,0x70d0e417, +0x2802100,0x962460,0x70d0e417,0x2802400,0x962460,0x70d0e417,0x6800100,0x962540,0x70d0ea15,0x4000010,0x400000,0x8000120f,0x7c00100,0x230400,0x80001524,0x7c00100, +0x230400,0x8000171a,0x7c00100,0x230400,0x80002006,0x7c00100,0x220400,0x80002006,0x7c00100,0x250400,0x80002a00,0x4000000,0x1500000,0x80002d00,0x4000000,0x200000, +0x80005208,0x2802400,0x962460,0x80005c00,0x4000000,0x200000,0x80007300,0x24000000,0x200000,0x80009519,0x7c00100,0x220400,0x80009519,0x7c00100,0x230400,0x80009519, +0x7c00100,0x250400,0x80009865,0x7c00100,0x230400,0x8000a008,0x2802100,0x962460,0x8000b30a,0x4000000,0x500000,0x8000b30a,0x7c00100,0x230400,0x8000cd00,0x4000000, +0xe00000,0x8000d202,0x2802500,0x962460,0x8000d202,0x7c00100,0x230400,0x8000d68d,0x4000000,0x200000,0x8000d997,0x2802000,0x962460,0x8000d997,0x2802400,0x962460, +0x8000d997,0x4000000,0x400000,0x8000d997,0x4000000,0x500000,0x8000d997,0x7c00100,0x230400,0x8000d997,0xc000010,0x448000,0x8000e489,0x2802100,0x962460,0x8000e489, +0x7c00100,0x230400,0x8000e719,0x7c00100,0x220400,0x8000f8a6,0x2802100,0x962460,0x8000f8a6,0x7c00100,0x230400,0x8000f8a6,0xc000010,0x448000,0x8000fda1,0x2802100, +0x1862460,0x8000fda1,0x2806400,0x1862460,0x8000fda1,0x4000000,0x1800000,0x8000fda1,0x6800000,0x1329800,0x8000fda1,0x6800100,0x1862400,0x8000fda1,0x6800100,0x1862540, +0x8000fda1,0x7c00100,0x1830000,0x8000fda1,0xc000010,0x448000,0x8000fe9c,0x7c00100,0x230400,0x8000fe9c,0x7c00100,0x830400,0x8000fe9c,0x7c00100,0x1430400,0x8000ff06, +0x7c00100,0x220400,0x80010165,0x7c00100,0x230400,0x800102a2,0x4000000,0x200000,0x800102a2,0x7c00100,0x230400,0x800103a4,0x7c00100,0x230400,0x800103a4,0xc000010, +0x448000,0x8001044c,0x4000000,0x200000,0x8001044c,0x7c00100,0x220400,0x8001044c,0x7c00100,0x250400,0x80010670,0x2802000,0x962460,0x80010670,0x4000000,0x200000, +0x80010670,0x4000010,0x400000,0x80010670,0xc000010,0x448000,0x800a4711,0x7c40300,0xe30000,0x800acd00,0x4000000,0xe00000,0x800acd00,0x4000000,0x2902460,0x800ace00, +0x4000000,0xe00000,0x800acf00,0x4000000,0xe00000,0x800b0011,0x7c40300,0xe30000,0x800b0500,0x4000000,0xe00000,0x800b0500,0x4000000,0x2800000,0x90001615,0x7c00100, +0x230400,0x9000171a,0x4000000,0x200000,0x9000171a,0x7c00100,0x230400,0x90003000,0x24000000,0x200000,0x90007f0e,0x4000000,0x200000,0x90008301,0x2802400,0x962460, +0x90008e00,0x24000000,0x400000,0x90009519,0x7c00100,0x250400,0x9000a16f,0x2802100,0x962460,0x9000d200,0x80000000,0x218960,0x9000d202,0x2802000,0x962460,0x9000d202, +0x2802100,0x962460,0x9000d202,0x7c00100,0x230400,0x9000e59d,0x2802100,0x962460,0x90010500,0x4000000,0xe00000,0x900107a7,0x2802100,0x962460,0x900107a7,0x2802400, +0x962460,0x900107a7,0x2802c00,0x962460,0x900107a7,0x4000000,0x1400000,0x900107a7,0x6800000,0x1329800,0x900107a7,0x7c00100,0x220400,0x900107a7,0x7c00100,0x250400, +0x900108a8,0x2802100,0x962460,0x900108a8,0x2806400,0x962460,0x900108a8,0x4000000,0x200000,0x900108a8,0x4000000,0x400000,0x900108a8,0x4000010,0x400000,0x900108a8, +0x6800000,0x1329800,0x900108a8,0x6800100,0x962540,0x900108a8,0x7c00100,0x230400,0x900108a8,0xc000010,0x448000,0x90010908,0x7c00100,0x220400,0x90010a38,0x2802100, +0x962460,0x90010ca9,0x2802100,0x962460,0x90010ca9,0x4000000,0x500000,0x90010ca9,0x4000010,0xb00000,0x90010ca9,0x6800100,0x962540,0x90010ca9,0x7c00100,0x230400, +0x90010d1b,0x4000000,0x500000,0x90010eaa,0x2802100,0x962460,0x90010eaa,0x2802400,0x962460,0x90010eaa,0x2806400,0x962460,0x90010eaa,0x4000000,0x200000,0x90010eaa, +0x4000000,0x400000,0x90010eaa,0x4000010,0x400000,0x90010eaa,0x6800000,0x1329800,0x90010eaa,0x6800100,0x962540,0x90010eaa,0x7c00100,0x230400,0x90010eaa,0xc000010, +0x448000,0x90010fab,0x7c00100,0x220400,0x90010fab,0x7c00100,0x250400,0x9002c300,0x4000000,0x100000,0x900ac400,0x4000000,0xe0000d,0x900acd00,0x4000000,0xe00000, +0x900acd00,0x4000000,0x2800000,0x900acf00,0x4000000,0xe00000,0x900b0500,0x4000000,0xe00000,0x900b0500,0x4000000,0x2800000,0x900b0b9a,0x7c00900,0x1230400,0x900b109a, +0x7c00300,0xe30000,0x900b119a,0x7c00300,0xe30000,0x90408e06,0x24000000,0x400000,0xa0001004,0x4000000,0x200000,0xa0001004,0x7c00100,0x230400,0xa000120f,0x2802100, +0x962460,0xa000120f,0x2802400,0x962460,0xa000171a,0x2802100,0x962460,0xa000171a,0x2806400,0x962460,0xa0002a00,0x4000000,0x1600000,0xa0003000,0x24000000,0x200000, +0xa000581e,0x7c00100,0x230400,0xa0007300,0x24000000,0x200000,0xa0008301,0x2802400,0x962460,0xa0008e00,0x24000000,0x400000,0xa000cf00,0x4000000,0xe00000,0xa0010500, +0x4000000,0x200000,0xa00114af,0x2802100,0x962460,0xa00114af,0x2802400,0x962460,0xa00114af,0x2806400,0x962460,0xa00114af,0x6800000,0x1329800,0xa00114af,0x7c00100, +0x230400,0xa00114af,0x7c00100,0x230560,0xa00116b0,0x2802100,0x962460,0xa00116b0,0x2802800,0x962460,0xa00116b0,0x2806400,0x962460,0xa00116b0,0x4000000,0x400000, +0xa00116b0,0x4000000,0x500000,0xa00116b0,0x4000010,0x400000,0xa00116b0,0x6800100,0x962540,0xa00116b0,0x7c00100,0x230400,0xa00116b0,0x7c00100,0x230560,0xa00116b0, +0xc000010,0x448000,0xa0011722,0x7c00100,0x230400,0xa00118b1,0x2802000,0x962460,0xa00118b1,0x2802100,0x962460,0xa00118b1,0x2806400,0x962460,0xa00118b1,0x4000000, +0x200000,0xa00118b1,0x4000000,0x400000,0xa00118b1,0x4000000,0x500000,0xa00118b1,0x6800100,0x962540,0xa00118b1,0x7c00100,0x230400,0xa00118b1,0x7c00100,0x230560, +0xa00118b1,0xc000010,0x448000,0xa00a4005,0x7c00100,0xe30400,0xa00a4711,0x7c40300,0xe30000,0xa00ac400,0x4000000,0xe00000,0xa00acb14,0x7c00100,0xe30000,0xa00acf00, +0x4000000,0xe00000,0xa00b0500,0x4000000,0xe00000,0xa00b0500,0x4000000,0x2800000,0xa00b0b96,0x7c00900,0x1230400,0xa00b1211,0x7c40300,0xe30000,0xa00b1314,0x7c00100, +0xe30000,0xa00b1596,0x7c00300,0xe30000,0xa040afb7,0x6800400,0x962540,0xa08083b8,0x2802400,0x962460,0xb0000a03,0x7c00100,0x220400,0xb0000b13,0x7c00100,0x2633800, +0xb0001004,0x2802000,0x962460,0xb0001110,0x4000000,0x200000,0xb0001524,0x2802100,0x962460,0xb0001615,0x4000000,0x500000,0xb000251b,0x7c00100,0x230400,0xb0007300, +0x24000000,0x200000,0xb0008939,0x4000000,0x200000,0xb0008939,0x7c00100,0x230400,0xb0008e00,0x24000000,0x200000,0xb0008e00,0x24000000,0x400000,0xb0008e00,0x24000010, +0x400000,0xb0009257,0x2802000,0x962460,0xb0009257,0x4000000,0x1600000,0xb0009519,0x7c00100,0x220400,0xb0009519,0x7c00100,0x250400,0xb0009a00,0x4000000,0x200000, +0xb000b30a,0x2802100,0x962460,0xb000b30a,0x7c00100,0x230400,0xb000c178,0x80000000,0x218960,0xb000c300,0x4000000,0x200000,0xb000d202,0x2802000,0x962460,0xb000d476, +0x6800100,0x962540,0xb000d476,0x7c00100,0x230400,0xb000e300,0x4000000,0xe00000,0xb000fda1,0x7c00100,0x1830000,0xb0010eaa,0x2802000,0x962460,0xb00116b0,0x7c00100, +0x230400,0xb0011900,0x4000000,0xe00000,0xb0011ab2,0x2802100,0x962460,0xb0011ab2,0x2802400,0x962460,0xb0011ab2,0x2806400,0x962460,0xb0011ab2,0x4000000,0x200000, +0xb0011ab2,0x6800100,0x962540,0xb0011ab2,0x7c00100,0x230400,0xb0011b0c,0x7c00100,0x230400,0xb0011cb3,0x2802100,0x962460,0xb0011cb3,0x2806400,0x962460,0xb0011cb3, +0x6800000,0x1329800,0xb0011cb3,0x6800100,0x962540,0xb0011cb3,0x7c00100,0x230400,0xb0011db6,0x2802500,0x962460,0xb0011db6,0x6800000,0x1329800,0xb0011db6,0x7c00100, +0x230400,0xb0011db6,0x7c00500,0x230400,0xb0011e00,0x4000000,0x200000,0xb0011e00,0x4000000,0x1500000,0xb0011fb4,0x2802100,0x962460,0xb0011fb4,0x6800100,0x962540, +0xb0011fb4,0x7c00100,0x230400,0xb0011fb4,0xc000010,0x248000,0xb0012000,0x4000000,0x200000,0xb00121b5,0x4000000,0x200000,0xb00121b5,0x4000010,0x400000,0xb00121b5, +0x7c00100,0x220400,0xb00121b5,0x7c00100,0x250400,0xb00121b5,0xc000010,0x448000,0xb00122b8,0x4000000,0x200000,0xb00122b8,0x7c00100,0x230400,0xb00123b7,0x2802400, +0x962460,0xb00123b7,0x4000000,0x200000,0xb00123b7,0x7c00100,0x230400,0xb00123b7,0xc000010,0x248000,0xb00a4005,0x7c00100,0xe30400,0xb00a4711,0x7c40300,0xe30000, +0xb00acf00,0x4000000,0xe00000,0xb00b0500,0x4000000,0xe00000,0xb00b0500,0x4000000,0x2800000,0xb00b109a,0x7c00300,0xe30000,0xb080e487,0x2802000,0x962460,0xc0001524, +0x4000000,0x500000,0xc0001a18,0x2806400,0x1862460,0xc0001a18,0x7c00100,0x1830000,0xc0007300,0x24000000,0x200000,0xc0008e00,0x24000010,0x400000,0xc0009519,0x7c00100, +0x220400,0xc0009519,0x7c00100,0x250400,0xc000c300,0x4000000,0x20000f,0xc000d85c,0x2802100,0x962460,0xc000d85c,0x6800100,0x962540,0xc000d85c,0x7c00100,0x230400, +0xc000dc99,0x7c00100,0x230400,0xc000e719,0x7c00100,0x220400,0xc00107a7,0x7c00100,0x230400,0xc0010eaa,0x7c00100,0x230400,0xc00116b0,0x7c00100,0x230560,0xc0011900, +0x4000000,0x200000,0xc0012447,0,0x818820,0xc0012447,0,0xc18820,0xc0012447,0,0x1418820,0xc00125b9,0x7c00100,0x230400,0xc00126bb,0x2802100, +0x962460,0xc00126bb,0x2806400,0x962460,0xc00126bb,0x4000000,0x500000,0xc00126bb,0x6800100,0x962540,0xc00126bb,0x7c00100,0x230400,0xc00127ba,0x2802400,0x962460, +0xc00127ba,0x4000000,0x200000,0xc00127ba,0x6800000,0x1329800,0xc00127ba,0x7c00100,0x230400,0xc00127ba,0x7c00900,0x230400,0xc0012800,0x4000000,0x200000,0xc0012b23, +0x4000000,0x200000,0xc0012b23,0x4000000,0x400000,0xc0012b23,0x4000000,0x1500000,0xc0012cbc,0x2802400,0x962460,0xc0012cbc,0x4000000,0x1600000,0xc0012cbc,0x6800000, +0x1329800,0xc0012cbc,0x7c00100,0x230400,0xc00acf00,0x4000000,0xe00000,0xc00ae300,0x4000000,0xe00000,0xc00b0500,0x4000000,0xe00000,0xc00b0500,0x4000000,0x2800000, +0xc00b0b11,0x4000000,0x1200000,0xc00b0b11,0x7c00900,0x1230400,0xc00b109a,0x7c00300,0xe30000,0xc00b2914,0x7c00100,0x2530000,0xc00b2916,0x7c00100,0x2530c00,0xc00b2a00, +0x4000000,0xe00000,0xc040af5e,0x7c00100,0x230400,0xc0c12b89,0x4000000,0x200000,0xc14a44ca,0x4000000,0xe0000d,0xd000131f,0x2802c00,0x962460,0xd000171a,0x7c00100, +0x230400,0xd0001821,0x2802100,0x962460,0xd0007300,0x24000000,0x200000,0xd0008e00,0x24000000,0x200000,0xd0008f3a,0x2806000,0x962460,0xd0009519,0x7c00100,0x220400, +0xd0009519,0x7c00100,0x250400,0xd000a500,0x4000000,0x200000,0xd000c300,0x4000000,0xe00000,0xd000d202,0x7c00100,0x230400,0xd000d476,0x7c00100,0x230400,0xd000d997, +0x2802100,0x962460,0xd000d997,0x6800100,0x962540,0xd000e001,0x2802100,0x962460,0xd000e700,0x4000400,0x200000,0xd000e719,0x7c00100,0x220400,0xd000e719,0x7c00500, +0x22040f,0xd000fa00,0x4000000,0xe00000,0xd0010eaa,0x4000010,0x400000,0xd0010eaa,0x7c00100,0x230400,0xd0012dbd,0x4000000,0x200000,0xd0012dbd,0x7c00100,0x230400, +0xd0012fbe,0x2802100,0x962460,0xd0012fbe,0x2802400,0x962460,0xd0012fbe,0x2806400,0x962460,0xd0012fbe,0x4000000,0x400000,0xd0012fbe,0x6800000,0x1329800,0xd0012fbe, +0x6800100,0x962540,0xd0012fbe,0x6800100,0x962541,0xd0012fbe,0x6804400,0x962540,0xd0012fbe,0x7c00100,0x230400,0xd0012fbe,0x7c00100,0x230560,0xd0012fbe,0xc000010, +0x448000,0xd0013183,0x7c00100,0x230400,0xd0013200,0x4000000,0x200000,0xd0013200,0x6800000,0x1329805,0xd00134c0,0x2802100,0x962460,0xd00134c0,0x4000002,0x400000, +0xd00134c0,0x7c00100,0x230400,0xd00a4305,0x7c00100,0xe30400,0xd00a4611,0x7c40300,0xe30000,0xd00a4711,0x7c40300,0xe30000,0xd00a5e11,0x7c40300,0xe30000,0xd00acf00, +0x4000000,0xe00000,0xd00b0500,0x4000000,0xe00000,0xd00b0500,0x4000000,0x2800000,0xd00b0b11,0x6800500,0x962540,0xd00b0bbf,0x2802200,0xc62460,0xd00b119a,0x7c00300, +0xe30000,0xd00b2a00,0x4000000,0xe00000,0xd00b2e11,0x7c40300,0xe30000,0xd00b30bf,0x7c00300,0x230000,0xd00b339a,0x7c00300,0xe30000,0xe0000c02,0xc000010,0xb48000, +0xe0001524,0x2802400,0x962460,0xe0001524,0x7c00100,0x230400,0xe0001615,0x7c00100,0x230400,0xe000251b,0x12882000,0x962460,0xe0002a00,0x4000000,0x1500000,0xe0005102, +0x4000000,0x200000,0xe0005c00,0x4000000,0x200000,0xe000622a,0x6804400,0x962540,0xe000622a,0x7c00100,0x230400,0xe0008838,0x7c00100,0x220400,0xe0008838,0x7c00100, +0x250400,0xe0008e00,0x24000000,0x810000,0xe0008e00,0x24000000,0x1410000,0xe0008e00,0x24000002,0x400000,0xe0008e00,0x2c000010,0xb48000,0xe000933e,0x7c00100,0x230400, +0xe000933e,0xc000010,0x448000,0xe0009519,0x7c00100,0x220400,0xe0009519,0x7c00100,0x22040f,0xe0009519,0x7c00100,0x250400,0xe000c178,0x2802100,0x962460,0xe000c941, +0x2802100,0x962460,0xe000c941,0x2806400,0x962460,0xe000c941,0x7c00100,0x230400,0xe000d202,0x2802400,0x962460,0xe000d202,0x7c00100,0x230400,0xe000d202,0x7c00500, +0x230400,0xe000dc99,0x4000000,0x200000,0xe000e001,0x2802100,0x962460,0xe000e001,0x2802400,0x962460,0xe000fda1,0x7c00100,0x1830000,0xe0013502,0x2802400,0x962460, +0xe0013502,0x4000000,0x200000,0xe0013502,0x7c00100,0x230400,0xe0013502,0x80000000,0x218960,0xe00136c1,0x4000000,0x200000,0xe00136c1,0x7c00100,0x230400,0xe001370b, +0x7c00100,0x230400,0xe0013919,0x7c00500,0x220400,0xe0013919,0x7c00500,0x22040f,0xe0013919,0x7c00d00,0x23040f,0xe0013a19,0x7c00100,0x220400,0xe0013a19,0x7c00100, +0x230400,0xe0013bc2,0x2802400,0x962460,0xe0013bc2,0x7c00100,0x230400,0xe0013bc2,0xc000010,0x248000,0xe0013cc3,0x6800000,0x1329800,0xe0013cc3,0x7c00100,0x230400, +0xe0013dc4,0x2802400,0x962460,0xe0013dc4,0x7c00100,0x230400,0xe0013e28,0x7c00100,0x230400,0xe0013fc5,0x7c00100,0x220400,0xe0013fc5,0x7c00100,0x250400,0xe0014000, +0x4000000,0x200000,0xe0014001,0x2802400,0x962460,0xe00a4711,0x7c40300,0xe30000,0xe00a5e11,0x7c40300,0xe30000,0xe00ac511,0x7c40300,0xe30000,0xe00acf00,0x4000000, +0xe00000,0xe00ae300,0x4000000,0xe00000,0xe00b0500,0x4000000,0xe00000,0xe00b1314,0x7c00100,0xe30000,0xe00b1316,0x7c00100,0xe30c00,0xe00b2a00,0x4000000,0xe00000, +0xe00b2a00,0x4000000,0x2800000,0xe00b3816,0x7c00500,0x230c00,0xe0808328,0x2802400,0x962460,0xf0001615,0x6800100,0x962540,0xf0001a18,0x2802000,0x1862460,0xf000c247, +0x7c00100,0x230400,0xf000d000,0x4000000,0xe00000,0xf000e300,0x4000000,0xe00000,0xf000e59d,0x2802100,0x962460,0xf000e59d,0x7c00100,0x230400,0xf0012447,0, +0x818820,0xf0012447,0,0xc18820,0xf0012447,0,0x1418820,0xf0012447,0x2802000,0x962460,0xf0012447,0x2802400,0x962460,0xf0012447,0x7c00100,0x230400, +0xf0013a19,0x7c00100,0x220400,0xf0014102,0x2802400,0x962460,0xf0014308,0x2802100,0x962460,0xf0014308,0x7c00500,0x22040e,0xf0014308,0x7c00500,0x22040f,0xf001440a, +0x4000000,0x500000,0xf0014500,0x4000000,0x200000,0xf00146c6,0x2802100,0x962460,0xf00146c6,0x2806000,0x962460,0xf00146c6,0x4000000,0xe00000,0xf00146c6,0x6800000, +0x1329800,0xf00146c6,0x6800100,0x962540,0xf00146c6,0x6804000,0x962540,0xf00146c6,0x7c00100,0x230400,0xf00146c6,0x7c00100,0x230560,0xf00146c6,0xc000010,0x448000, +0xf00147c7,0x2802000,0x962460,0xf00147c7,0x6800000,0x1329800,0xf00147c7,0x7c00100,0x230400,0xf00ac511,0x7c40300,0xe30000,0xf00acf00,0x4000000,0xe00000,0xf00b2914, +0x7c00100,0x2530000,0xf00b2916,0x7c00100,0x2530c00,0xf00b2a00,0x4000000,0xe00000,0xf00b2a00,0x4000000,0x2800000,0xf00b4211,0x7c40300,0xe30000}; + +static const int32_t countPropsVectors=7230; +static const int32_t propsVectorsColumns=3; +static const uint16_t scriptExtensions[282]={ +0x800e,0x8019,8,0x8059,8,2,8,0x8038,8,6,8,0x8019,2,0x22,0x25,0x57, +0xb6,0x80c0,2,0x22,0x8025,2,0x12,2,0x22,0x25,0x57,0xa7,0xb6,0x80c0,2,0x22, +0x54,0x79,0x7b,0xa7,0xb6,0xb7,0x80c2,2,0x8022,2,0x25,0x80c0,2,0x29,2,0x80b6, +2,0x2e,4,0xa,0xf,0x10,0x15,0x19,0x1a,0x1f,0x23,0x24,0x89,0x97,0x809e,4, +0xa,0xf,0x10,0x15,0x19,0x1a,0x1f,0x23,0x24,0x89,0x809e,4,0xa,0xf,0x10,0x15, +0x1a,0x1f,0x21,0x23,0x24,0x3a,0x89,0x91,0x99,0x9e,0xa0,0xaf,0xb2,0xb3,0x80bb,4, +0xa,0xf,0x10,0x15,0x1a,0x1f,0x21,0x23,0x24,0x30,0x3a,0x89,0x91,0x99,0x9e,0xa0, +0xaf,0xb2,0xb3,0x80bb,0xa,0x78,0xa0,0x80b2,0xa,0x74,4,0x3a,0x8076,4,0x7a,0x10, +0x80a4,0x10,0x7f,0xf,0x809d,0xf,0x83,0x23,0x8089,0x23,0x87,0x15,0x80bb,0x15,0x8b,0x1c, +0x34,0x8076,0x1c,0x8f,0xc,0x8019,0x2a,0x2b,0x2c,0x802d,0x1b,0x805a,0x800a,4,0xa,0x15, +0x8089,0xa,0x8089,4,0x800a,0xa,0x8097,0xa,0x15,0x1a,0x1f,0x23,0x8024,0xa,0x80bb,4, +0xa,0x15,0x1f,0x24,0x89,0x9e,0x80bb,0x8004,8,0x8022,0x19,0x801b,0xa,0x19,0x8089,5, +0x11,0x12,0x14,0x16,0x8029,5,0x11,0x12,0x14,0x8016,0x8011,5,0x8011,0x11,0x14,0x8016, +0x11,0x8019,0xa,0xf,0x10,0x78,0x91,0x99,0x9d,0x9e,0xa0,0xa3,0x80b2,0xa,0xf,0x10, +0x15,0x1a,0x78,0x91,0x99,0x9d,0x9e,0xa0,0xa3,0xb2,0x80bb,0xa,0xf,0x10,0x15,0x78, +0x91,0x99,0x9d,0x9e,0xa0,0xa3,0xb2,0x80bb,0xa,0xa3,0xa,0x8023,0xa,0xfa,0x19,0x1c, +0x804f,0x37,0x804e,2,0x8057,2,0x8025,2,0x105,0x2f,0x31,0x8053,0x2f,0x31,0x80c1,0x2f, +0x8031,2,0x8007,0x79,0x80c2,0x79,0x113,0x89,0x87,0x8087}; + +static const int32_t indexes[UPROPS_INDEX_COUNT]={0x2d08,0x2d08,0x2d08,0x2d08,0x6ce6,3,0x8924,0x89b1,0x89b1,0x89b1,0xb47c7,0x2a75a31,0,0,0,0}; + +#endif // INCLUDED_FROM_UCHAR_C diff --git a/deps/icu-small/source/common/ucharstrie.cpp b/deps/icu-small/source/common/ucharstrie.cpp index 24ab42577791a7..1d2448babccb11 100644 --- a/deps/icu-small/source/common/ucharstrie.cpp +++ b/deps/icu-small/source/common/ucharstrie.cpp @@ -1,414 +1,414 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: ucharstrie.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010nov14 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" -#include "unicode/appendable.h" -#include "unicode/ucharstrie.h" -#include "unicode/uobject.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - -UCharsTrie::~UCharsTrie() { - uprv_free(ownedArray_); -} - -UStringTrieResult -UCharsTrie::current() const { - const UChar *pos=pos_; - if(pos==NULL) { - return USTRINGTRIE_NO_MATCH; - } else { - int32_t node; - return (remainingMatchLength_<0 && (node=*pos)>=kMinValueLead) ? - valueResult(node) : USTRINGTRIE_NO_VALUE; - } -} - -UStringTrieResult -UCharsTrie::firstForCodePoint(UChar32 cp) { - return cp<=0xffff ? - first(cp) : - (USTRINGTRIE_HAS_NEXT(first(U16_LEAD(cp))) ? - next(U16_TRAIL(cp)) : - USTRINGTRIE_NO_MATCH); -} - -UStringTrieResult -UCharsTrie::nextForCodePoint(UChar32 cp) { - return cp<=0xffff ? - next(cp) : - (USTRINGTRIE_HAS_NEXT(next(U16_LEAD(cp))) ? - next(U16_TRAIL(cp)) : - USTRINGTRIE_NO_MATCH); -} - -UStringTrieResult -UCharsTrie::branchNext(const UChar *pos, int32_t length, int32_t uchar) { - // Branch according to the current unit. - if(length==0) { - length=*pos++; - } - ++length; - // The length of the branch is the number of units to select from. - // The data structure encodes a binary search. - while(length>kMaxBranchLinearSubNodeLength) { - if(uchar<*pos++) { - length>>=1; - pos=jumpByDelta(pos); - } else { - length=length-(length>>1); - pos=skipDelta(pos); - } - } - // Drop down to linear search for the last few units. - // length>=2 because the loop body above sees length>kMaxBranchLinearSubNodeLength>=3 - // and divides length by 2. - do { - if(uchar==*pos++) { - UStringTrieResult result; - int32_t node=*pos; - if(node&kValueIsFinal) { - // Leave the final value for getValue() to read. - result=USTRINGTRIE_FINAL_VALUE; - } else { - // Use the non-final value as the jump delta. - ++pos; - // int32_t delta=readValue(pos, node); - int32_t delta; - if(node=kMinValueLead ? valueResult(node) : USTRINGTRIE_NO_VALUE; - } - pos_=pos; - return result; - } - --length; - pos=skipValue(pos); - } while(length>1); - if(uchar==*pos++) { - pos_=pos; - int32_t node=*pos; - return node>=kMinValueLead ? valueResult(node) : USTRINGTRIE_NO_VALUE; - } else { - stop(); - return USTRINGTRIE_NO_MATCH; - } -} - -UStringTrieResult -UCharsTrie::nextImpl(const UChar *pos, int32_t uchar) { - int32_t node=*pos++; - for(;;) { - if(node=kMinValueLead) ? - valueResult(node) : USTRINGTRIE_NO_VALUE; - } else { - // No match. - break; - } - } else if(node&kValueIsFinal) { - // No further matching units. - break; - } else { - // Skip intermediate value. - pos=skipNodeValue(pos, node); - node&=kNodeTypeMask; - } - } - stop(); - return USTRINGTRIE_NO_MATCH; -} - -UStringTrieResult -UCharsTrie::next(int32_t uchar) { - const UChar *pos=pos_; - if(pos==NULL) { - return USTRINGTRIE_NO_MATCH; - } - int32_t length=remainingMatchLength_; // Actual remaining match length minus 1. - if(length>=0) { - // Remaining part of a linear-match node. - if(uchar==*pos++) { - remainingMatchLength_=--length; - pos_=pos; - int32_t node; - return (length<0 && (node=*pos)>=kMinValueLead) ? - valueResult(node) : USTRINGTRIE_NO_VALUE; - } else { - stop(); - return USTRINGTRIE_NO_MATCH; - } - } - return nextImpl(pos, uchar); -} - -UStringTrieResult -UCharsTrie::next(ConstChar16Ptr ptr, int32_t sLength) { - const UChar *s=ptr; - if(sLength<0 ? *s==0 : sLength==0) { - // Empty input. - return current(); - } - const UChar *pos=pos_; - if(pos==NULL) { - return USTRINGTRIE_NO_MATCH; - } - int32_t length=remainingMatchLength_; // Actual remaining match length minus 1. - for(;;) { - // Fetch the next input unit, if there is one. - // Continue a linear-match node without rechecking sLength<0. - int32_t uchar; - if(sLength<0) { - for(;;) { - if((uchar=*s++)==0) { - remainingMatchLength_=length; - pos_=pos; - int32_t node; - return (length<0 && (node=*pos)>=kMinValueLead) ? - valueResult(node) : USTRINGTRIE_NO_VALUE; - } - if(length<0) { - remainingMatchLength_=length; - break; - } - if(uchar!=*pos) { - stop(); - return USTRINGTRIE_NO_MATCH; - } - ++pos; - --length; - } - } else { - for(;;) { - if(sLength==0) { - remainingMatchLength_=length; - pos_=pos; - int32_t node; - return (length<0 && (node=*pos)>=kMinValueLead) ? - valueResult(node) : USTRINGTRIE_NO_VALUE; - } - uchar=*s++; - --sLength; - if(length<0) { - remainingMatchLength_=length; - break; - } - if(uchar!=*pos) { - stop(); - return USTRINGTRIE_NO_MATCH; - } - ++pos; - --length; - } - } - int32_t node=*pos++; - for(;;) { - if(nodekMaxBranchLinearSubNodeLength) { - ++pos; // ignore the comparison unit - if(NULL==findUniqueValueFromBranch(jumpByDelta(pos), length>>1, haveUniqueValue, uniqueValue)) { - return NULL; - } - length=length-(length>>1); - pos=skipDelta(pos); - } - do { - ++pos; // ignore a comparison unit - // handle its value - int32_t node=*pos++; - UBool isFinal=(UBool)(node>>15); - node&=0x7fff; - int32_t value=readValue(pos, node); - pos=skipValue(pos, node); - if(isFinal) { - if(haveUniqueValue) { - if(value!=uniqueValue) { - return NULL; - } - } else { - uniqueValue=value; - haveUniqueValue=true; - } - } else { - if(!findUniqueValue(pos+value, haveUniqueValue, uniqueValue)) { - return NULL; - } - haveUniqueValue=true; - } - } while(--length>1); - return pos+1; // ignore the last comparison unit -} - -UBool -UCharsTrie::findUniqueValue(const UChar *pos, UBool haveUniqueValue, int32_t &uniqueValue) { - int32_t node=*pos++; - for(;;) { - if(node>15); - int32_t value; - if(isFinal) { - value=readValue(pos, node&0x7fff); - } else { - value=readNodeValue(pos, node); - } - if(haveUniqueValue) { - if(value!=uniqueValue) { - return false; - } - } else { - uniqueValue=value; - haveUniqueValue=true; - } - if(isFinal) { - return true; - } - pos=skipNodeValue(pos, node); - node&=kNodeTypeMask; - } - } -} - -int32_t -UCharsTrie::getNextUChars(Appendable &out) const { - const UChar *pos=pos_; - if(pos==NULL) { - return 0; - } - if(remainingMatchLength_>=0) { - out.appendCodeUnit(*pos); // Next unit of a pending linear-match node. - return 1; - } - int32_t node=*pos++; - if(node>=kMinValueLead) { - if(node&kValueIsFinal) { - return 0; - } else { - pos=skipNodeValue(pos, node); - node&=kNodeTypeMask; - } - } - if(nodekMaxBranchLinearSubNodeLength) { - ++pos; // ignore the comparison unit - getNextBranchUChars(jumpByDelta(pos), length>>1, out); - length=length-(length>>1); - pos=skipDelta(pos); - } - do { - out.appendCodeUnit(*pos++); - pos=skipValue(pos); - } while(--length>1); - out.appendCodeUnit(*pos); -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: ucharstrie.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010nov14 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" +#include "unicode/appendable.h" +#include "unicode/ucharstrie.h" +#include "unicode/uobject.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +UCharsTrie::~UCharsTrie() { + uprv_free(ownedArray_); +} + +UStringTrieResult +UCharsTrie::current() const { + const char16_t *pos=pos_; + if(pos==nullptr) { + return USTRINGTRIE_NO_MATCH; + } else { + int32_t node; + return (remainingMatchLength_<0 && (node=*pos)>=kMinValueLead) ? + valueResult(node) : USTRINGTRIE_NO_VALUE; + } +} + +UStringTrieResult +UCharsTrie::firstForCodePoint(UChar32 cp) { + return cp<=0xffff ? + first(cp) : + (USTRINGTRIE_HAS_NEXT(first(U16_LEAD(cp))) ? + next(U16_TRAIL(cp)) : + USTRINGTRIE_NO_MATCH); +} + +UStringTrieResult +UCharsTrie::nextForCodePoint(UChar32 cp) { + return cp<=0xffff ? + next(cp) : + (USTRINGTRIE_HAS_NEXT(next(U16_LEAD(cp))) ? + next(U16_TRAIL(cp)) : + USTRINGTRIE_NO_MATCH); +} + +UStringTrieResult +UCharsTrie::branchNext(const char16_t *pos, int32_t length, int32_t uchar) { + // Branch according to the current unit. + if(length==0) { + length=*pos++; + } + ++length; + // The length of the branch is the number of units to select from. + // The data structure encodes a binary search. + while(length>kMaxBranchLinearSubNodeLength) { + if(uchar<*pos++) { + length>>=1; + pos=jumpByDelta(pos); + } else { + length=length-(length>>1); + pos=skipDelta(pos); + } + } + // Drop down to linear search for the last few units. + // length>=2 because the loop body above sees length>kMaxBranchLinearSubNodeLength>=3 + // and divides length by 2. + do { + if(uchar==*pos++) { + UStringTrieResult result; + int32_t node=*pos; + if(node&kValueIsFinal) { + // Leave the final value for getValue() to read. + result=USTRINGTRIE_FINAL_VALUE; + } else { + // Use the non-final value as the jump delta. + ++pos; + // int32_t delta=readValue(pos, node); + int32_t delta; + if(node=kMinValueLead ? valueResult(node) : USTRINGTRIE_NO_VALUE; + } + pos_=pos; + return result; + } + --length; + pos=skipValue(pos); + } while(length>1); + if(uchar==*pos++) { + pos_=pos; + int32_t node=*pos; + return node>=kMinValueLead ? valueResult(node) : USTRINGTRIE_NO_VALUE; + } else { + stop(); + return USTRINGTRIE_NO_MATCH; + } +} + +UStringTrieResult +UCharsTrie::nextImpl(const char16_t *pos, int32_t uchar) { + int32_t node=*pos++; + for(;;) { + if(node=kMinValueLead) ? + valueResult(node) : USTRINGTRIE_NO_VALUE; + } else { + // No match. + break; + } + } else if(node&kValueIsFinal) { + // No further matching units. + break; + } else { + // Skip intermediate value. + pos=skipNodeValue(pos, node); + node&=kNodeTypeMask; + } + } + stop(); + return USTRINGTRIE_NO_MATCH; +} + +UStringTrieResult +UCharsTrie::next(int32_t uchar) { + const char16_t *pos=pos_; + if(pos==nullptr) { + return USTRINGTRIE_NO_MATCH; + } + int32_t length=remainingMatchLength_; // Actual remaining match length minus 1. + if(length>=0) { + // Remaining part of a linear-match node. + if(uchar==*pos++) { + remainingMatchLength_=--length; + pos_=pos; + int32_t node; + return (length<0 && (node=*pos)>=kMinValueLead) ? + valueResult(node) : USTRINGTRIE_NO_VALUE; + } else { + stop(); + return USTRINGTRIE_NO_MATCH; + } + } + return nextImpl(pos, uchar); +} + +UStringTrieResult +UCharsTrie::next(ConstChar16Ptr ptr, int32_t sLength) { + const char16_t *s=ptr; + if(sLength<0 ? *s==0 : sLength==0) { + // Empty input. + return current(); + } + const char16_t *pos=pos_; + if(pos==nullptr) { + return USTRINGTRIE_NO_MATCH; + } + int32_t length=remainingMatchLength_; // Actual remaining match length minus 1. + for(;;) { + // Fetch the next input unit, if there is one. + // Continue a linear-match node without rechecking sLength<0. + int32_t uchar; + if(sLength<0) { + for(;;) { + if((uchar=*s++)==0) { + remainingMatchLength_=length; + pos_=pos; + int32_t node; + return (length<0 && (node=*pos)>=kMinValueLead) ? + valueResult(node) : USTRINGTRIE_NO_VALUE; + } + if(length<0) { + remainingMatchLength_=length; + break; + } + if(uchar!=*pos) { + stop(); + return USTRINGTRIE_NO_MATCH; + } + ++pos; + --length; + } + } else { + for(;;) { + if(sLength==0) { + remainingMatchLength_=length; + pos_=pos; + int32_t node; + return (length<0 && (node=*pos)>=kMinValueLead) ? + valueResult(node) : USTRINGTRIE_NO_VALUE; + } + uchar=*s++; + --sLength; + if(length<0) { + remainingMatchLength_=length; + break; + } + if(uchar!=*pos) { + stop(); + return USTRINGTRIE_NO_MATCH; + } + ++pos; + --length; + } + } + int32_t node=*pos++; + for(;;) { + if(nodekMaxBranchLinearSubNodeLength) { + ++pos; // ignore the comparison unit + if(nullptr==findUniqueValueFromBranch(jumpByDelta(pos), length>>1, haveUniqueValue, uniqueValue)) { + return nullptr; + } + length=length-(length>>1); + pos=skipDelta(pos); + } + do { + ++pos; // ignore a comparison unit + // handle its value + int32_t node=*pos++; + UBool isFinal=(UBool)(node>>15); + node&=0x7fff; + int32_t value=readValue(pos, node); + pos=skipValue(pos, node); + if(isFinal) { + if(haveUniqueValue) { + if(value!=uniqueValue) { + return nullptr; + } + } else { + uniqueValue=value; + haveUniqueValue=true; + } + } else { + if(!findUniqueValue(pos+value, haveUniqueValue, uniqueValue)) { + return nullptr; + } + haveUniqueValue=true; + } + } while(--length>1); + return pos+1; // ignore the last comparison unit +} + +UBool +UCharsTrie::findUniqueValue(const char16_t *pos, UBool haveUniqueValue, int32_t &uniqueValue) { + int32_t node=*pos++; + for(;;) { + if(node>15); + int32_t value; + if(isFinal) { + value=readValue(pos, node&0x7fff); + } else { + value=readNodeValue(pos, node); + } + if(haveUniqueValue) { + if(value!=uniqueValue) { + return false; + } + } else { + uniqueValue=value; + haveUniqueValue=true; + } + if(isFinal) { + return true; + } + pos=skipNodeValue(pos, node); + node&=kNodeTypeMask; + } + } +} + +int32_t +UCharsTrie::getNextUChars(Appendable &out) const { + const char16_t *pos=pos_; + if(pos==nullptr) { + return 0; + } + if(remainingMatchLength_>=0) { + out.appendCodeUnit(*pos); // Next unit of a pending linear-match node. + return 1; + } + int32_t node=*pos++; + if(node>=kMinValueLead) { + if(node&kValueIsFinal) { + return 0; + } else { + pos=skipNodeValue(pos, node); + node&=kNodeTypeMask; + } + } + if(nodekMaxBranchLinearSubNodeLength) { + ++pos; // ignore the comparison unit + getNextBranchUChars(jumpByDelta(pos), length>>1, out); + length=length-(length>>1); + pos=skipDelta(pos); + } + do { + out.appendCodeUnit(*pos++); + pos=skipValue(pos); + } while(--length>1); + out.appendCodeUnit(*pos); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/ucharstriebuilder.cpp b/deps/icu-small/source/common/ucharstriebuilder.cpp index be3260941ee911..051fcc77660609 100644 --- a/deps/icu-small/source/common/ucharstriebuilder.cpp +++ b/deps/icu-small/source/common/ucharstriebuilder.cpp @@ -1,443 +1,443 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2012, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: ucharstriebuilder.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010nov14 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" -#include "unicode/ucharstrie.h" -#include "unicode/ucharstriebuilder.h" -#include "unicode/unistr.h" -#include "unicode/ustring.h" -#include "cmemory.h" -#include "uarrsort.h" -#include "uassert.h" -#include "uhash.h" -#include "ustr_imp.h" - -U_NAMESPACE_BEGIN - -/* - * Note: This builder implementation stores (string, value) pairs with full copies - * of the 16-bit-unit sequences, until the UCharsTrie is built. - * It might(!) take less memory if we collected the data in a temporary, dynamic trie. - */ - -class UCharsTrieElement : public UMemory { -public: - // Use compiler's default constructor, initializes nothing. - - void setTo(const UnicodeString &s, int32_t val, UnicodeString &strings, UErrorCode &errorCode); - - UnicodeString getString(const UnicodeString &strings) const { - int32_t length=strings[stringOffset]; - return strings.tempSubString(stringOffset+1, length); - } - int32_t getStringLength(const UnicodeString &strings) const { - return strings[stringOffset]; - } - - UChar charAt(int32_t index, const UnicodeString &strings) const { - return strings[stringOffset+1+index]; - } - - int32_t getValue() const { return value; } - - int32_t compareStringTo(const UCharsTrieElement &o, const UnicodeString &strings) const; - -private: - // The first strings unit contains the string length. - // (Compared with a stringLength field here, this saves 2 bytes per string.) - int32_t stringOffset; - int32_t value; -}; - -void -UCharsTrieElement::setTo(const UnicodeString &s, int32_t val, - UnicodeString &strings, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return; - } - int32_t length=s.length(); - if(length>0xffff) { - // Too long: We store the length in 1 unit. - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return; - } - stringOffset=strings.length(); - strings.append((UChar)length); - value=val; - strings.append(s); -} - -int32_t -UCharsTrieElement::compareStringTo(const UCharsTrieElement &other, const UnicodeString &strings) const { - return getString(strings).compare(other.getString(strings)); -} - -UCharsTrieBuilder::UCharsTrieBuilder(UErrorCode & /*errorCode*/) - : elements(NULL), elementsCapacity(0), elementsLength(0), - uchars(NULL), ucharsCapacity(0), ucharsLength(0) {} - -UCharsTrieBuilder::~UCharsTrieBuilder() { - delete[] elements; - uprv_free(uchars); -} - -UCharsTrieBuilder & -UCharsTrieBuilder::add(const UnicodeString &s, int32_t value, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return *this; - } - if(ucharsLength>0) { - // Cannot add elements after building. - errorCode=U_NO_WRITE_PERMISSION; - return *this; - } - if(elementsLength==elementsCapacity) { - int32_t newCapacity; - if(elementsCapacity==0) { - newCapacity=1024; - } else { - newCapacity=4*elementsCapacity; - } - UCharsTrieElement *newElements=new UCharsTrieElement[newCapacity]; - if(newElements==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return *this; - } - if(elementsLength>0) { - uprv_memcpy(newElements, elements, (size_t)elementsLength*sizeof(UCharsTrieElement)); - } - delete[] elements; - elements=newElements; - elementsCapacity=newCapacity; - } - elements[elementsLength++].setTo(s, value, strings, errorCode); - if(U_SUCCESS(errorCode) && strings.isBogus()) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } - return *this; -} - -U_CDECL_BEGIN - -static int32_t U_CALLCONV -compareElementStrings(const void *context, const void *left, const void *right) { - const UnicodeString *strings=static_cast(context); - const UCharsTrieElement *leftElement=static_cast(left); - const UCharsTrieElement *rightElement=static_cast(right); - return leftElement->compareStringTo(*rightElement, *strings); -} - -U_CDECL_END - -UCharsTrie * -UCharsTrieBuilder::build(UStringTrieBuildOption buildOption, UErrorCode &errorCode) { - buildUChars(buildOption, errorCode); - UCharsTrie *newTrie=NULL; - if(U_SUCCESS(errorCode)) { - newTrie=new UCharsTrie(uchars, uchars+(ucharsCapacity-ucharsLength)); - if(newTrie==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } else { - uchars=NULL; // The new trie now owns the array. - ucharsCapacity=0; - } - } - return newTrie; -} - -UnicodeString & -UCharsTrieBuilder::buildUnicodeString(UStringTrieBuildOption buildOption, UnicodeString &result, - UErrorCode &errorCode) { - buildUChars(buildOption, errorCode); - if(U_SUCCESS(errorCode)) { - result.setTo(false, uchars+(ucharsCapacity-ucharsLength), ucharsLength); - } - return result; -} - -void -UCharsTrieBuilder::buildUChars(UStringTrieBuildOption buildOption, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return; - } - if(uchars!=NULL && ucharsLength>0) { - // Already built. - return; - } - if(ucharsLength==0) { - if(elementsLength==0) { - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return; - } - if(strings.isBogus()) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_sortArray(elements, elementsLength, (int32_t)sizeof(UCharsTrieElement), - compareElementStrings, &strings, - false, // need not be a stable sort - &errorCode); - if(U_FAILURE(errorCode)) { - return; - } - // Duplicate strings are not allowed. - UnicodeString prev=elements[0].getString(strings); - for(int32_t i=1; i(uprv_malloc(capacity*2)); - if(uchars==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - ucharsCapacity=0; - return; - } - ucharsCapacity=capacity; - } - StringTrieBuilder::build(buildOption, elementsLength, errorCode); - if(uchars==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } -} - -int32_t -UCharsTrieBuilder::getElementStringLength(int32_t i) const { - return elements[i].getStringLength(strings); -} - -UChar -UCharsTrieBuilder::getElementUnit(int32_t i, int32_t unitIndex) const { - return elements[i].charAt(unitIndex, strings); -} - -int32_t -UCharsTrieBuilder::getElementValue(int32_t i) const { - return elements[i].getValue(); -} - -int32_t -UCharsTrieBuilder::getLimitOfLinearMatch(int32_t first, int32_t last, int32_t unitIndex) const { - const UCharsTrieElement &firstElement=elements[first]; - const UCharsTrieElement &lastElement=elements[last]; - int32_t minStringLength=firstElement.getStringLength(strings); - while(++unitIndex0); - return i; -} - -int32_t -UCharsTrieBuilder::indexOfElementWithNextUnit(int32_t i, int32_t unitIndex, UChar unit) const { - while(unit==elements[i].charAt(unitIndex, strings)) { - ++i; - } - return i; -} - -UCharsTrieBuilder::UCTLinearMatchNode::UCTLinearMatchNode(const UChar *units, int32_t len, Node *nextNode) - : LinearMatchNode(len, nextNode), s(units) { - hash=hash*37u+ustr_hashUCharsN(units, len); -} - -bool -UCharsTrieBuilder::UCTLinearMatchNode::operator==(const Node &other) const { - if(this==&other) { - return true; - } - if(!LinearMatchNode::operator==(other)) { - return false; - } - const UCTLinearMatchNode &o=(const UCTLinearMatchNode &)other; - return 0==u_memcmp(s, o.s, length); -} - -void -UCharsTrieBuilder::UCTLinearMatchNode::write(StringTrieBuilder &builder) { - UCharsTrieBuilder &b=(UCharsTrieBuilder &)builder; - next->write(builder); - b.write(s, length); - offset=b.writeValueAndType(hasValue, value, b.getMinLinearMatch()+length-1); -} - -StringTrieBuilder::Node * -UCharsTrieBuilder::createLinearMatchNode(int32_t i, int32_t unitIndex, int32_t length, - Node *nextNode) const { - return new UCTLinearMatchNode( - elements[i].getString(strings).getBuffer()+unitIndex, - length, - nextNode); -} - -UBool -UCharsTrieBuilder::ensureCapacity(int32_t length) { - if(uchars==NULL) { - return false; // previous memory allocation had failed - } - if(length>ucharsCapacity) { - int32_t newCapacity=ucharsCapacity; - do { - newCapacity*=2; - } while(newCapacity<=length); - UChar *newUChars=static_cast(uprv_malloc(newCapacity*2)); - if(newUChars==NULL) { - // unable to allocate memory - uprv_free(uchars); - uchars=NULL; - ucharsCapacity=0; - return false; - } - u_memcpy(newUChars+(newCapacity-ucharsLength), - uchars+(ucharsCapacity-ucharsLength), ucharsLength); - uprv_free(uchars); - uchars=newUChars; - ucharsCapacity=newCapacity; - } - return true; -} - -int32_t -UCharsTrieBuilder::write(int32_t unit) { - int32_t newLength=ucharsLength+1; - if(ensureCapacity(newLength)) { - ucharsLength=newLength; - uchars[ucharsCapacity-ucharsLength]=(UChar)unit; - } - return ucharsLength; -} - -int32_t -UCharsTrieBuilder::write(const UChar *s, int32_t length) { - int32_t newLength=ucharsLength+length; - if(ensureCapacity(newLength)) { - ucharsLength=newLength; - u_memcpy(uchars+(ucharsCapacity-ucharsLength), s, length); - } - return ucharsLength; -} - -int32_t -UCharsTrieBuilder::writeElementUnits(int32_t i, int32_t unitIndex, int32_t length) { - return write(elements[i].getString(strings).getBuffer()+unitIndex, length); -} - -int32_t -UCharsTrieBuilder::writeValueAndFinal(int32_t i, UBool isFinal) { - if(0<=i && i<=UCharsTrie::kMaxOneUnitValue) { - return write(i|(isFinal<<15)); - } - UChar intUnits[3]; - int32_t length; - if(i<0 || i>UCharsTrie::kMaxTwoUnitValue) { - intUnits[0]=(UChar)(UCharsTrie::kThreeUnitValueLead); - intUnits[1]=(UChar)((uint32_t)i>>16); - intUnits[2]=(UChar)i; - length=3; - // } else if(i<=UCharsTrie::kMaxOneUnitValue) { - // intUnits[0]=(UChar)(i); - // length=1; - } else { - intUnits[0]=(UChar)(UCharsTrie::kMinTwoUnitValueLead+(i>>16)); - intUnits[1]=(UChar)i; - length=2; - } - intUnits[0]=(UChar)(intUnits[0]|(isFinal<<15)); - return write(intUnits, length); -} - -int32_t -UCharsTrieBuilder::writeValueAndType(UBool hasValue, int32_t value, int32_t node) { - if(!hasValue) { - return write(node); - } - UChar intUnits[3]; - int32_t length; - if(value<0 || value>UCharsTrie::kMaxTwoUnitNodeValue) { - intUnits[0]=(UChar)(UCharsTrie::kThreeUnitNodeValueLead); - intUnits[1]=(UChar)((uint32_t)value>>16); - intUnits[2]=(UChar)value; - length=3; - } else if(value<=UCharsTrie::kMaxOneUnitNodeValue) { - intUnits[0]=(UChar)((value+1)<<6); - length=1; - } else { - intUnits[0]=(UChar)(UCharsTrie::kMinTwoUnitNodeValueLead+((value>>10)&0x7fc0)); - intUnits[1]=(UChar)value; - length=2; - } - intUnits[0]|=(UChar)node; - return write(intUnits, length); -} - -int32_t -UCharsTrieBuilder::writeDeltaTo(int32_t jumpTarget) { - int32_t i=ucharsLength-jumpTarget; - U_ASSERT(i>=0); - if(i<=UCharsTrie::kMaxOneUnitDelta) { - return write(i); - } - UChar intUnits[3]; - int32_t length; - if(i<=UCharsTrie::kMaxTwoUnitDelta) { - intUnits[0]=(UChar)(UCharsTrie::kMinTwoUnitDeltaLead+(i>>16)); - length=1; - } else { - intUnits[0]=(UChar)(UCharsTrie::kThreeUnitDeltaLead); - intUnits[1]=(UChar)(i>>16); - length=2; - } - intUnits[length++]=(UChar)i; - return write(intUnits, length); -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2012, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: ucharstriebuilder.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010nov14 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" +#include "unicode/ucharstrie.h" +#include "unicode/ucharstriebuilder.h" +#include "unicode/unistr.h" +#include "unicode/ustring.h" +#include "cmemory.h" +#include "uarrsort.h" +#include "uassert.h" +#include "uhash.h" +#include "ustr_imp.h" + +U_NAMESPACE_BEGIN + +/* + * Note: This builder implementation stores (string, value) pairs with full copies + * of the 16-bit-unit sequences, until the UCharsTrie is built. + * It might(!) take less memory if we collected the data in a temporary, dynamic trie. + */ + +class UCharsTrieElement : public UMemory { +public: + // Use compiler's default constructor, initializes nothing. + + void setTo(const UnicodeString &s, int32_t val, UnicodeString &strings, UErrorCode &errorCode); + + UnicodeString getString(const UnicodeString &strings) const { + int32_t length=strings[stringOffset]; + return strings.tempSubString(stringOffset+1, length); + } + int32_t getStringLength(const UnicodeString &strings) const { + return strings[stringOffset]; + } + + char16_t charAt(int32_t index, const UnicodeString &strings) const { + return strings[stringOffset+1+index]; + } + + int32_t getValue() const { return value; } + + int32_t compareStringTo(const UCharsTrieElement &o, const UnicodeString &strings) const; + +private: + // The first strings unit contains the string length. + // (Compared with a stringLength field here, this saves 2 bytes per string.) + int32_t stringOffset; + int32_t value; +}; + +void +UCharsTrieElement::setTo(const UnicodeString &s, int32_t val, + UnicodeString &strings, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return; + } + int32_t length=s.length(); + if(length>0xffff) { + // Too long: We store the length in 1 unit. + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return; + } + stringOffset=strings.length(); + strings.append((char16_t)length); + value=val; + strings.append(s); +} + +int32_t +UCharsTrieElement::compareStringTo(const UCharsTrieElement &other, const UnicodeString &strings) const { + return getString(strings).compare(other.getString(strings)); +} + +UCharsTrieBuilder::UCharsTrieBuilder(UErrorCode & /*errorCode*/) + : elements(nullptr), elementsCapacity(0), elementsLength(0), + uchars(nullptr), ucharsCapacity(0), ucharsLength(0) {} + +UCharsTrieBuilder::~UCharsTrieBuilder() { + delete[] elements; + uprv_free(uchars); +} + +UCharsTrieBuilder & +UCharsTrieBuilder::add(const UnicodeString &s, int32_t value, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return *this; + } + if(ucharsLength>0) { + // Cannot add elements after building. + errorCode=U_NO_WRITE_PERMISSION; + return *this; + } + if(elementsLength==elementsCapacity) { + int32_t newCapacity; + if(elementsCapacity==0) { + newCapacity=1024; + } else { + newCapacity=4*elementsCapacity; + } + UCharsTrieElement *newElements=new UCharsTrieElement[newCapacity]; + if(newElements==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return *this; + } + if(elementsLength>0) { + uprv_memcpy(newElements, elements, (size_t)elementsLength*sizeof(UCharsTrieElement)); + } + delete[] elements; + elements=newElements; + elementsCapacity=newCapacity; + } + elements[elementsLength++].setTo(s, value, strings, errorCode); + if(U_SUCCESS(errorCode) && strings.isBogus()) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } + return *this; +} + +U_CDECL_BEGIN + +static int32_t U_CALLCONV +compareElementStrings(const void *context, const void *left, const void *right) { + const UnicodeString *strings=static_cast(context); + const UCharsTrieElement *leftElement=static_cast(left); + const UCharsTrieElement *rightElement=static_cast(right); + return leftElement->compareStringTo(*rightElement, *strings); +} + +U_CDECL_END + +UCharsTrie * +UCharsTrieBuilder::build(UStringTrieBuildOption buildOption, UErrorCode &errorCode) { + buildUChars(buildOption, errorCode); + UCharsTrie *newTrie=nullptr; + if(U_SUCCESS(errorCode)) { + newTrie=new UCharsTrie(uchars, uchars+(ucharsCapacity-ucharsLength)); + if(newTrie==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } else { + uchars=nullptr; // The new trie now owns the array. + ucharsCapacity=0; + } + } + return newTrie; +} + +UnicodeString & +UCharsTrieBuilder::buildUnicodeString(UStringTrieBuildOption buildOption, UnicodeString &result, + UErrorCode &errorCode) { + buildUChars(buildOption, errorCode); + if(U_SUCCESS(errorCode)) { + result.setTo(false, uchars+(ucharsCapacity-ucharsLength), ucharsLength); + } + return result; +} + +void +UCharsTrieBuilder::buildUChars(UStringTrieBuildOption buildOption, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return; + } + if(uchars!=nullptr && ucharsLength>0) { + // Already built. + return; + } + if(ucharsLength==0) { + if(elementsLength==0) { + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return; + } + if(strings.isBogus()) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_sortArray(elements, elementsLength, (int32_t)sizeof(UCharsTrieElement), + compareElementStrings, &strings, + false, // need not be a stable sort + &errorCode); + if(U_FAILURE(errorCode)) { + return; + } + // Duplicate strings are not allowed. + UnicodeString prev=elements[0].getString(strings); + for(int32_t i=1; i(uprv_malloc(capacity*2)); + if(uchars==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + ucharsCapacity=0; + return; + } + ucharsCapacity=capacity; + } + StringTrieBuilder::build(buildOption, elementsLength, errorCode); + if(uchars==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } +} + +int32_t +UCharsTrieBuilder::getElementStringLength(int32_t i) const { + return elements[i].getStringLength(strings); +} + +char16_t +UCharsTrieBuilder::getElementUnit(int32_t i, int32_t unitIndex) const { + return elements[i].charAt(unitIndex, strings); +} + +int32_t +UCharsTrieBuilder::getElementValue(int32_t i) const { + return elements[i].getValue(); +} + +int32_t +UCharsTrieBuilder::getLimitOfLinearMatch(int32_t first, int32_t last, int32_t unitIndex) const { + const UCharsTrieElement &firstElement=elements[first]; + const UCharsTrieElement &lastElement=elements[last]; + int32_t minStringLength=firstElement.getStringLength(strings); + while(++unitIndex0); + return i; +} + +int32_t +UCharsTrieBuilder::indexOfElementWithNextUnit(int32_t i, int32_t unitIndex, char16_t unit) const { + while(unit==elements[i].charAt(unitIndex, strings)) { + ++i; + } + return i; +} + +UCharsTrieBuilder::UCTLinearMatchNode::UCTLinearMatchNode(const char16_t *units, int32_t len, Node *nextNode) + : LinearMatchNode(len, nextNode), s(units) { + hash=hash*37u+ustr_hashUCharsN(units, len); +} + +bool +UCharsTrieBuilder::UCTLinearMatchNode::operator==(const Node &other) const { + if(this==&other) { + return true; + } + if(!LinearMatchNode::operator==(other)) { + return false; + } + const UCTLinearMatchNode &o=static_cast(other); + return 0==u_memcmp(s, o.s, length); +} + +void +UCharsTrieBuilder::UCTLinearMatchNode::write(StringTrieBuilder &builder) { + UCharsTrieBuilder &b=(UCharsTrieBuilder &)builder; + next->write(builder); + b.write(s, length); + offset=b.writeValueAndType(hasValue, value, b.getMinLinearMatch()+length-1); +} + +StringTrieBuilder::Node * +UCharsTrieBuilder::createLinearMatchNode(int32_t i, int32_t unitIndex, int32_t length, + Node *nextNode) const { + return new UCTLinearMatchNode( + elements[i].getString(strings).getBuffer()+unitIndex, + length, + nextNode); +} + +UBool +UCharsTrieBuilder::ensureCapacity(int32_t length) { + if(uchars==nullptr) { + return false; // previous memory allocation had failed + } + if(length>ucharsCapacity) { + int32_t newCapacity=ucharsCapacity; + do { + newCapacity*=2; + } while(newCapacity<=length); + char16_t *newUChars=static_cast(uprv_malloc(newCapacity*2)); + if(newUChars==nullptr) { + // unable to allocate memory + uprv_free(uchars); + uchars=nullptr; + ucharsCapacity=0; + return false; + } + u_memcpy(newUChars+(newCapacity-ucharsLength), + uchars+(ucharsCapacity-ucharsLength), ucharsLength); + uprv_free(uchars); + uchars=newUChars; + ucharsCapacity=newCapacity; + } + return true; +} + +int32_t +UCharsTrieBuilder::write(int32_t unit) { + int32_t newLength=ucharsLength+1; + if(ensureCapacity(newLength)) { + ucharsLength=newLength; + uchars[ucharsCapacity-ucharsLength]=(char16_t)unit; + } + return ucharsLength; +} + +int32_t +UCharsTrieBuilder::write(const char16_t *s, int32_t length) { + int32_t newLength=ucharsLength+length; + if(ensureCapacity(newLength)) { + ucharsLength=newLength; + u_memcpy(uchars+(ucharsCapacity-ucharsLength), s, length); + } + return ucharsLength; +} + +int32_t +UCharsTrieBuilder::writeElementUnits(int32_t i, int32_t unitIndex, int32_t length) { + return write(elements[i].getString(strings).getBuffer()+unitIndex, length); +} + +int32_t +UCharsTrieBuilder::writeValueAndFinal(int32_t i, UBool isFinal) { + if(0<=i && i<=UCharsTrie::kMaxOneUnitValue) { + return write(i|(isFinal<<15)); + } + char16_t intUnits[3]; + int32_t length; + if(i<0 || i>UCharsTrie::kMaxTwoUnitValue) { + intUnits[0]=(char16_t)(UCharsTrie::kThreeUnitValueLead); + intUnits[1]=(char16_t)((uint32_t)i>>16); + intUnits[2]=(char16_t)i; + length=3; + // } else if(i<=UCharsTrie::kMaxOneUnitValue) { + // intUnits[0]=(char16_t)(i); + // length=1; + } else { + intUnits[0]=(char16_t)(UCharsTrie::kMinTwoUnitValueLead+(i>>16)); + intUnits[1]=(char16_t)i; + length=2; + } + intUnits[0]=(char16_t)(intUnits[0]|(isFinal<<15)); + return write(intUnits, length); +} + +int32_t +UCharsTrieBuilder::writeValueAndType(UBool hasValue, int32_t value, int32_t node) { + if(!hasValue) { + return write(node); + } + char16_t intUnits[3]; + int32_t length; + if(value<0 || value>UCharsTrie::kMaxTwoUnitNodeValue) { + intUnits[0]=(char16_t)(UCharsTrie::kThreeUnitNodeValueLead); + intUnits[1]=(char16_t)((uint32_t)value>>16); + intUnits[2]=(char16_t)value; + length=3; + } else if(value<=UCharsTrie::kMaxOneUnitNodeValue) { + intUnits[0]=(char16_t)((value+1)<<6); + length=1; + } else { + intUnits[0]=(char16_t)(UCharsTrie::kMinTwoUnitNodeValueLead+((value>>10)&0x7fc0)); + intUnits[1]=(char16_t)value; + length=2; + } + intUnits[0]|=(char16_t)node; + return write(intUnits, length); +} + +int32_t +UCharsTrieBuilder::writeDeltaTo(int32_t jumpTarget) { + int32_t i=ucharsLength-jumpTarget; + U_ASSERT(i>=0); + if(i<=UCharsTrie::kMaxOneUnitDelta) { + return write(i); + } + char16_t intUnits[3]; + int32_t length; + if(i<=UCharsTrie::kMaxTwoUnitDelta) { + intUnits[0]=(char16_t)(UCharsTrie::kMinTwoUnitDeltaLead+(i>>16)); + length=1; + } else { + intUnits[0]=(char16_t)(UCharsTrie::kThreeUnitDeltaLead); + intUnits[1]=(char16_t)(i>>16); + length=2; + } + intUnits[length++]=(char16_t)i; + return write(intUnits, length); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/ucharstrieiterator.cpp b/deps/icu-small/source/common/ucharstrieiterator.cpp index 2ba43692ddd011..c1e2014f00d722 100644 --- a/deps/icu-small/source/common/ucharstrieiterator.cpp +++ b/deps/icu-small/source/common/ucharstrieiterator.cpp @@ -1,215 +1,215 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: ucharstrieiterator.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010nov15 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" -#include "unicode/ucharstrie.h" -#include "unicode/unistr.h" -#include "uvectr32.h" - -U_NAMESPACE_BEGIN - -UCharsTrie::Iterator::Iterator(ConstChar16Ptr trieUChars, int32_t maxStringLength, - UErrorCode &errorCode) - : uchars_(trieUChars), - pos_(uchars_), initialPos_(uchars_), - remainingMatchLength_(-1), initialRemainingMatchLength_(-1), - skipValue_(false), - maxLength_(maxStringLength), value_(0), stack_(NULL) { - if(U_FAILURE(errorCode)) { - return; - } - // stack_ is a pointer so that it's easy to turn ucharstrie.h into - // a public API header for which we would want it to depend only on - // other public headers. - // Unlike UCharsTrie itself, its Iterator performs memory allocations anyway - // via the UnicodeString and UVector32 implementations, so this additional - // cost is minimal. - stack_=new UVector32(errorCode); - if(stack_==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } -} - -UCharsTrie::Iterator::Iterator(const UCharsTrie &trie, int32_t maxStringLength, - UErrorCode &errorCode) - : uchars_(trie.uchars_), pos_(trie.pos_), initialPos_(trie.pos_), - remainingMatchLength_(trie.remainingMatchLength_), - initialRemainingMatchLength_(trie.remainingMatchLength_), - skipValue_(false), - maxLength_(maxStringLength), value_(0), stack_(NULL) { - if(U_FAILURE(errorCode)) { - return; - } - stack_=new UVector32(errorCode); - if(U_FAILURE(errorCode)) { - return; - } - if(stack_==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - int32_t length=remainingMatchLength_; // Actual remaining match length minus 1. - if(length>=0) { - // Pending linear-match node, append remaining UChars to str_. - ++length; - if(maxLength_>0 && length>maxLength_) { - length=maxLength_; // This will leave remainingMatchLength>=0 as a signal. - } - str_.append(pos_, length); - pos_+=length; - remainingMatchLength_-=length; - } -} - -UCharsTrie::Iterator::~Iterator() { - delete stack_; -} - -UCharsTrie::Iterator & -UCharsTrie::Iterator::reset() { - pos_=initialPos_; - remainingMatchLength_=initialRemainingMatchLength_; - skipValue_=false; - int32_t length=remainingMatchLength_+1; // Remaining match length. - if(maxLength_>0 && length>maxLength_) { - length=maxLength_; - } - str_.truncate(length); - pos_+=length; - remainingMatchLength_-=length; - stack_->setSize(0); - return *this; -} - -UBool -UCharsTrie::Iterator::hasNext() const { return pos_!=NULL || !stack_->isEmpty(); } - -UBool -UCharsTrie::Iterator::next(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return false; - } - const UChar *pos=pos_; - if(pos==NULL) { - if(stack_->isEmpty()) { - return false; - } - // Pop the state off the stack and continue with the next outbound edge of - // the branch node. - int32_t stackSize=stack_->size(); - int32_t length=stack_->elementAti(stackSize-1); - pos=uchars_+stack_->elementAti(stackSize-2); - stack_->setSize(stackSize-2); - str_.truncate(length&0xffff); - length=(int32_t)((uint32_t)length>>16); - if(length>1) { - pos=branchNext(pos, length, errorCode); - if(pos==NULL) { - return true; // Reached a final value. - } - } else { - str_.append(*pos++); - } - } - if(remainingMatchLength_>=0) { - // We only get here if we started in a pending linear-match node - // with more than maxLength remaining units. - return truncateAndStop(); - } - for(;;) { - int32_t node=*pos++; - if(node>=kMinValueLead) { - if(skipValue_) { - pos=skipNodeValue(pos, node); - node&=kNodeTypeMask; - skipValue_=false; - } else { - // Deliver value for the string so far. - UBool isFinal=(UBool)(node>>15); - if(isFinal) { - value_=readValue(pos, node&0x7fff); - } else { - value_=readNodeValue(pos, node); - } - if(isFinal || (maxLength_>0 && str_.length()==maxLength_)) { - pos_=NULL; - } else { - // We cannot skip the value right here because it shares its - // lead unit with a match node which we have to evaluate - // next time. - // Instead, keep pos_ on the node lead unit itself. - pos_=pos-1; - skipValue_=true; - } - return true; - } - } - if(maxLength_>0 && str_.length()==maxLength_) { - return truncateAndStop(); - } - if(node0 && str_.length()+length>maxLength_) { - str_.append(pos, maxLength_-str_.length()); - return truncateAndStop(); - } - str_.append(pos, length); - pos+=length; - } - } -} - -// Branch node, needs to take the first outbound edge and push state for the rest. -const UChar * -UCharsTrie::Iterator::branchNext(const UChar *pos, int32_t length, UErrorCode &errorCode) { - while(length>kMaxBranchLinearSubNodeLength) { - ++pos; // ignore the comparison unit - // Push state for the greater-or-equal edge. - stack_->addElement((int32_t)(skipDelta(pos)-uchars_), errorCode); - stack_->addElement(((length-(length>>1))<<16)|str_.length(), errorCode); - // Follow the less-than edge. - length>>=1; - pos=jumpByDelta(pos); - } - // List of key-value pairs where values are either final values or jump deltas. - // Read the first (key, value) pair. - UChar trieUnit=*pos++; - int32_t node=*pos++; - UBool isFinal=(UBool)(node>>15); - int32_t value=readValue(pos, node&=0x7fff); - pos=skipValue(pos, node); - stack_->addElement((int32_t)(pos-uchars_), errorCode); - stack_->addElement(((length-1)<<16)|str_.length(), errorCode); - str_.append(trieUnit); - if(isFinal) { - pos_=NULL; - value_=value; - return NULL; - } else { - return pos+value; - } -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: ucharstrieiterator.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010nov15 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" +#include "unicode/ucharstrie.h" +#include "unicode/unistr.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +UCharsTrie::Iterator::Iterator(ConstChar16Ptr trieUChars, int32_t maxStringLength, + UErrorCode &errorCode) + : uchars_(trieUChars), + pos_(uchars_), initialPos_(uchars_), + remainingMatchLength_(-1), initialRemainingMatchLength_(-1), + skipValue_(false), + maxLength_(maxStringLength), value_(0), stack_(nullptr) { + if(U_FAILURE(errorCode)) { + return; + } + // stack_ is a pointer so that it's easy to turn ucharstrie.h into + // a public API header for which we would want it to depend only on + // other public headers. + // Unlike UCharsTrie itself, its Iterator performs memory allocations anyway + // via the UnicodeString and UVector32 implementations, so this additional + // cost is minimal. + stack_=new UVector32(errorCode); + if(stack_==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } +} + +UCharsTrie::Iterator::Iterator(const UCharsTrie &trie, int32_t maxStringLength, + UErrorCode &errorCode) + : uchars_(trie.uchars_), pos_(trie.pos_), initialPos_(trie.pos_), + remainingMatchLength_(trie.remainingMatchLength_), + initialRemainingMatchLength_(trie.remainingMatchLength_), + skipValue_(false), + maxLength_(maxStringLength), value_(0), stack_(nullptr) { + if(U_FAILURE(errorCode)) { + return; + } + stack_=new UVector32(errorCode); + if(U_FAILURE(errorCode)) { + return; + } + if(stack_==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + int32_t length=remainingMatchLength_; // Actual remaining match length minus 1. + if(length>=0) { + // Pending linear-match node, append remaining UChars to str_. + ++length; + if(maxLength_>0 && length>maxLength_) { + length=maxLength_; // This will leave remainingMatchLength>=0 as a signal. + } + str_.append(pos_, length); + pos_+=length; + remainingMatchLength_-=length; + } +} + +UCharsTrie::Iterator::~Iterator() { + delete stack_; +} + +UCharsTrie::Iterator & +UCharsTrie::Iterator::reset() { + pos_=initialPos_; + remainingMatchLength_=initialRemainingMatchLength_; + skipValue_=false; + int32_t length=remainingMatchLength_+1; // Remaining match length. + if(maxLength_>0 && length>maxLength_) { + length=maxLength_; + } + str_.truncate(length); + pos_+=length; + remainingMatchLength_-=length; + stack_->setSize(0); + return *this; +} + +UBool +UCharsTrie::Iterator::hasNext() const { return pos_!=nullptr || !stack_->isEmpty(); } + +UBool +UCharsTrie::Iterator::next(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return false; + } + const char16_t *pos=pos_; + if(pos==nullptr) { + if(stack_->isEmpty()) { + return false; + } + // Pop the state off the stack and continue with the next outbound edge of + // the branch node. + int32_t stackSize=stack_->size(); + int32_t length=stack_->elementAti(stackSize-1); + pos=uchars_+stack_->elementAti(stackSize-2); + stack_->setSize(stackSize-2); + str_.truncate(length&0xffff); + length=(int32_t)((uint32_t)length>>16); + if(length>1) { + pos=branchNext(pos, length, errorCode); + if(pos==nullptr) { + return true; // Reached a final value. + } + } else { + str_.append(*pos++); + } + } + if(remainingMatchLength_>=0) { + // We only get here if we started in a pending linear-match node + // with more than maxLength remaining units. + return truncateAndStop(); + } + for(;;) { + int32_t node=*pos++; + if(node>=kMinValueLead) { + if(skipValue_) { + pos=skipNodeValue(pos, node); + node&=kNodeTypeMask; + skipValue_=false; + } else { + // Deliver value for the string so far. + UBool isFinal=(UBool)(node>>15); + if(isFinal) { + value_=readValue(pos, node&0x7fff); + } else { + value_=readNodeValue(pos, node); + } + if(isFinal || (maxLength_>0 && str_.length()==maxLength_)) { + pos_=nullptr; + } else { + // We cannot skip the value right here because it shares its + // lead unit with a match node which we have to evaluate + // next time. + // Instead, keep pos_ on the node lead unit itself. + pos_=pos-1; + skipValue_=true; + } + return true; + } + } + if(maxLength_>0 && str_.length()==maxLength_) { + return truncateAndStop(); + } + if(node0 && str_.length()+length>maxLength_) { + str_.append(pos, maxLength_-str_.length()); + return truncateAndStop(); + } + str_.append(pos, length); + pos+=length; + } + } +} + +// Branch node, needs to take the first outbound edge and push state for the rest. +const char16_t * +UCharsTrie::Iterator::branchNext(const char16_t *pos, int32_t length, UErrorCode &errorCode) { + while(length>kMaxBranchLinearSubNodeLength) { + ++pos; // ignore the comparison unit + // Push state for the greater-or-equal edge. + stack_->addElement((int32_t)(skipDelta(pos)-uchars_), errorCode); + stack_->addElement(((length-(length>>1))<<16)|str_.length(), errorCode); + // Follow the less-than edge. + length>>=1; + pos=jumpByDelta(pos); + } + // List of key-value pairs where values are either final values or jump deltas. + // Read the first (key, value) pair. + char16_t trieUnit=*pos++; + int32_t node=*pos++; + UBool isFinal=(UBool)(node>>15); + int32_t value=readValue(pos, node&=0x7fff); + pos=skipValue(pos, node); + stack_->addElement((int32_t)(pos-uchars_), errorCode); + stack_->addElement(((length-1)<<16)|str_.length(), errorCode); + str_.append(trieUnit); + if(isFinal) { + pos_=nullptr; + value_=value; + return nullptr; + } else { + return pos+value; + } +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/uchriter.cpp b/deps/icu-small/source/common/uchriter.cpp index f2a99538413096..528b2506a0c56d 100644 --- a/deps/icu-small/source/common/uchriter.cpp +++ b/deps/icu-small/source/common/uchriter.cpp @@ -1,367 +1,367 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 1998-2012, International Business Machines Corporation and -* others. All Rights Reserved. -****************************************************************************** -*/ - -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/uchriter.h" -#include "unicode/ustring.h" -#include "unicode/utf16.h" -#include "ustr_imp.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UCharCharacterIterator) - -UCharCharacterIterator::UCharCharacterIterator() - : CharacterIterator(), - text(0) -{ - // never default construct! -} - -UCharCharacterIterator::UCharCharacterIterator(ConstChar16Ptr textPtr, - int32_t length) - : CharacterIterator(textPtr != 0 ? (length>=0 ? length : u_strlen(textPtr)) : 0), - text(textPtr) -{ -} - -UCharCharacterIterator::UCharCharacterIterator(ConstChar16Ptr textPtr, - int32_t length, - int32_t position) - : CharacterIterator(textPtr != 0 ? (length>=0 ? length : u_strlen(textPtr)) : 0, position), - text(textPtr) -{ -} - -UCharCharacterIterator::UCharCharacterIterator(ConstChar16Ptr textPtr, - int32_t length, - int32_t textBegin, - int32_t textEnd, - int32_t position) - : CharacterIterator(textPtr != 0 ? (length>=0 ? length : u_strlen(textPtr)) : 0, textBegin, textEnd, position), - text(textPtr) -{ -} - -UCharCharacterIterator::UCharCharacterIterator(const UCharCharacterIterator& that) -: CharacterIterator(that), - text(that.text) -{ -} - -UCharCharacterIterator& -UCharCharacterIterator::operator=(const UCharCharacterIterator& that) { - CharacterIterator::operator=(that); - text = that.text; - return *this; -} - -UCharCharacterIterator::~UCharCharacterIterator() { -} - -bool -UCharCharacterIterator::operator==(const ForwardCharacterIterator& that) const { - if (this == &that) { - return true; - } - if (typeid(*this) != typeid(that)) { - return false; - } - - UCharCharacterIterator& realThat = (UCharCharacterIterator&)that; - - return text == realThat.text - && textLength == realThat.textLength - && pos == realThat.pos - && begin == realThat.begin - && end == realThat.end; -} - -int32_t -UCharCharacterIterator::hashCode() const { - return ustr_hashUCharsN(text, textLength) ^ pos ^ begin ^ end; -} - -UCharCharacterIterator* -UCharCharacterIterator::clone() const { - return new UCharCharacterIterator(*this); -} - -UChar -UCharCharacterIterator::first() { - pos = begin; - if(pos < end) { - return text[pos]; - } else { - return DONE; - } -} - -UChar -UCharCharacterIterator::firstPostInc() { - pos = begin; - if(pos < end) { - return text[pos++]; - } else { - return DONE; - } -} - -UChar -UCharCharacterIterator::last() { - pos = end; - if(pos > begin) { - return text[--pos]; - } else { - return DONE; - } -} - -UChar -UCharCharacterIterator::setIndex(int32_t position) { - if(position < begin) { - pos = begin; - } else if(position > end) { - pos = end; - } else { - pos = position; - } - if(pos < end) { - return text[pos]; - } else { - return DONE; - } -} - -UChar -UCharCharacterIterator::current() const { - if (pos >= begin && pos < end) { - return text[pos]; - } else { - return DONE; - } -} - -UChar -UCharCharacterIterator::next() { - if (pos + 1 < end) { - return text[++pos]; - } else { - /* make current() return DONE */ - pos = end; - return DONE; - } -} - -UChar -UCharCharacterIterator::nextPostInc() { - if (pos < end) { - return text[pos++]; - } else { - return DONE; - } -} - -UBool -UCharCharacterIterator::hasNext() { - return (UBool)(pos < end ? true : false); -} - -UChar -UCharCharacterIterator::previous() { - if (pos > begin) { - return text[--pos]; - } else { - return DONE; - } -} - -UBool -UCharCharacterIterator::hasPrevious() { - return (UBool)(pos > begin ? true : false); -} - -UChar32 -UCharCharacterIterator::first32() { - pos = begin; - if(pos < end) { - int32_t i = pos; - UChar32 c; - U16_NEXT(text, i, end, c); - return c; - } else { - return DONE; - } -} - -UChar32 -UCharCharacterIterator::first32PostInc() { - pos = begin; - if(pos < end) { - UChar32 c; - U16_NEXT(text, pos, end, c); - return c; - } else { - return DONE; - } -} - -UChar32 -UCharCharacterIterator::last32() { - pos = end; - if(pos > begin) { - UChar32 c; - U16_PREV(text, begin, pos, c); - return c; - } else { - return DONE; - } -} - -UChar32 -UCharCharacterIterator::setIndex32(int32_t position) { - if(position < begin) { - position = begin; - } else if(position > end) { - position = end; - } - if(position < end) { - U16_SET_CP_START(text, begin, position); - int32_t i = this->pos = position; - UChar32 c; - U16_NEXT(text, i, end, c); - return c; - } else { - this->pos = position; - return DONE; - } -} - -UChar32 -UCharCharacterIterator::current32() const { - if (pos >= begin && pos < end) { - UChar32 c; - U16_GET(text, begin, pos, end, c); - return c; - } else { - return DONE; - } -} - -UChar32 -UCharCharacterIterator::next32() { - if (pos < end) { - U16_FWD_1(text, pos, end); - if(pos < end) { - int32_t i = pos; - UChar32 c; - U16_NEXT(text, i, end, c); - return c; - } - } - /* make current() return DONE */ - pos = end; - return DONE; -} - -UChar32 -UCharCharacterIterator::next32PostInc() { - if (pos < end) { - UChar32 c; - U16_NEXT(text, pos, end, c); - return c; - } else { - return DONE; - } -} - -UChar32 -UCharCharacterIterator::previous32() { - if (pos > begin) { - UChar32 c; - U16_PREV(text, begin, pos, c); - return c; - } else { - return DONE; - } -} - -int32_t -UCharCharacterIterator::move(int32_t delta, CharacterIterator::EOrigin origin) { - switch(origin) { - case kStart: - pos = begin + delta; - break; - case kCurrent: - pos += delta; - break; - case kEnd: - pos = end + delta; - break; - default: - break; - } - - if(pos < begin) { - pos = begin; - } else if(pos > end) { - pos = end; - } - - return pos; -} - -int32_t -UCharCharacterIterator::move32(int32_t delta, CharacterIterator::EOrigin origin) { - // this implementation relies on the "safe" version of the UTF macros - // (or the trustworthiness of the caller) - switch(origin) { - case kStart: - pos = begin; - if(delta > 0) { - U16_FWD_N(text, pos, end, delta); - } - break; - case kCurrent: - if(delta > 0) { - U16_FWD_N(text, pos, end, delta); - } else { - U16_BACK_N(text, begin, pos, -delta); - } - break; - case kEnd: - pos = end; - if(delta < 0) { - U16_BACK_N(text, begin, pos, -delta); - } - break; - default: - break; - } - - return pos; -} - -void UCharCharacterIterator::setText(ConstChar16Ptr newText, - int32_t newTextLength) { - text = newText; - if(newText == 0 || newTextLength < 0) { - newTextLength = 0; - } - end = textLength = newTextLength; - pos = begin = 0; -} - -void -UCharCharacterIterator::getText(UnicodeString& result) { - result = UnicodeString(text, textLength); -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 1998-2012, International Business Machines Corporation and +* others. All Rights Reserved. +****************************************************************************** +*/ + +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/uchriter.h" +#include "unicode/ustring.h" +#include "unicode/utf16.h" +#include "ustr_imp.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UCharCharacterIterator) + +UCharCharacterIterator::UCharCharacterIterator() + : CharacterIterator(), + text(0) +{ + // never default construct! +} + +UCharCharacterIterator::UCharCharacterIterator(ConstChar16Ptr textPtr, + int32_t length) + : CharacterIterator(textPtr != 0 ? (length>=0 ? length : u_strlen(textPtr)) : 0), + text(textPtr) +{ +} + +UCharCharacterIterator::UCharCharacterIterator(ConstChar16Ptr textPtr, + int32_t length, + int32_t position) + : CharacterIterator(textPtr != 0 ? (length>=0 ? length : u_strlen(textPtr)) : 0, position), + text(textPtr) +{ +} + +UCharCharacterIterator::UCharCharacterIterator(ConstChar16Ptr textPtr, + int32_t length, + int32_t textBegin, + int32_t textEnd, + int32_t position) + : CharacterIterator(textPtr != 0 ? (length>=0 ? length : u_strlen(textPtr)) : 0, textBegin, textEnd, position), + text(textPtr) +{ +} + +UCharCharacterIterator::UCharCharacterIterator(const UCharCharacterIterator& that) +: CharacterIterator(that), + text(that.text) +{ +} + +UCharCharacterIterator& +UCharCharacterIterator::operator=(const UCharCharacterIterator& that) { + CharacterIterator::operator=(that); + text = that.text; + return *this; +} + +UCharCharacterIterator::~UCharCharacterIterator() { +} + +bool +UCharCharacterIterator::operator==(const ForwardCharacterIterator& that) const { + if (this == &that) { + return true; + } + if (typeid(*this) != typeid(that)) { + return false; + } + + const UCharCharacterIterator& realThat = static_cast(that); + + return text == realThat.text + && textLength == realThat.textLength + && pos == realThat.pos + && begin == realThat.begin + && end == realThat.end; +} + +int32_t +UCharCharacterIterator::hashCode() const { + return ustr_hashUCharsN(text, textLength) ^ pos ^ begin ^ end; +} + +UCharCharacterIterator* +UCharCharacterIterator::clone() const { + return new UCharCharacterIterator(*this); +} + +char16_t +UCharCharacterIterator::first() { + pos = begin; + if(pos < end) { + return text[pos]; + } else { + return DONE; + } +} + +char16_t +UCharCharacterIterator::firstPostInc() { + pos = begin; + if(pos < end) { + return text[pos++]; + } else { + return DONE; + } +} + +char16_t +UCharCharacterIterator::last() { + pos = end; + if(pos > begin) { + return text[--pos]; + } else { + return DONE; + } +} + +char16_t +UCharCharacterIterator::setIndex(int32_t position) { + if(position < begin) { + pos = begin; + } else if(position > end) { + pos = end; + } else { + pos = position; + } + if(pos < end) { + return text[pos]; + } else { + return DONE; + } +} + +char16_t +UCharCharacterIterator::current() const { + if (pos >= begin && pos < end) { + return text[pos]; + } else { + return DONE; + } +} + +char16_t +UCharCharacterIterator::next() { + if (pos + 1 < end) { + return text[++pos]; + } else { + /* make current() return DONE */ + pos = end; + return DONE; + } +} + +char16_t +UCharCharacterIterator::nextPostInc() { + if (pos < end) { + return text[pos++]; + } else { + return DONE; + } +} + +UBool +UCharCharacterIterator::hasNext() { + return (UBool)(pos < end ? true : false); +} + +char16_t +UCharCharacterIterator::previous() { + if (pos > begin) { + return text[--pos]; + } else { + return DONE; + } +} + +UBool +UCharCharacterIterator::hasPrevious() { + return (UBool)(pos > begin ? true : false); +} + +UChar32 +UCharCharacterIterator::first32() { + pos = begin; + if(pos < end) { + int32_t i = pos; + UChar32 c; + U16_NEXT(text, i, end, c); + return c; + } else { + return DONE; + } +} + +UChar32 +UCharCharacterIterator::first32PostInc() { + pos = begin; + if(pos < end) { + UChar32 c; + U16_NEXT(text, pos, end, c); + return c; + } else { + return DONE; + } +} + +UChar32 +UCharCharacterIterator::last32() { + pos = end; + if(pos > begin) { + UChar32 c; + U16_PREV(text, begin, pos, c); + return c; + } else { + return DONE; + } +} + +UChar32 +UCharCharacterIterator::setIndex32(int32_t position) { + if(position < begin) { + position = begin; + } else if(position > end) { + position = end; + } + if(position < end) { + U16_SET_CP_START(text, begin, position); + int32_t i = this->pos = position; + UChar32 c; + U16_NEXT(text, i, end, c); + return c; + } else { + this->pos = position; + return DONE; + } +} + +UChar32 +UCharCharacterIterator::current32() const { + if (pos >= begin && pos < end) { + UChar32 c; + U16_GET(text, begin, pos, end, c); + return c; + } else { + return DONE; + } +} + +UChar32 +UCharCharacterIterator::next32() { + if (pos < end) { + U16_FWD_1(text, pos, end); + if(pos < end) { + int32_t i = pos; + UChar32 c; + U16_NEXT(text, i, end, c); + return c; + } + } + /* make current() return DONE */ + pos = end; + return DONE; +} + +UChar32 +UCharCharacterIterator::next32PostInc() { + if (pos < end) { + UChar32 c; + U16_NEXT(text, pos, end, c); + return c; + } else { + return DONE; + } +} + +UChar32 +UCharCharacterIterator::previous32() { + if (pos > begin) { + UChar32 c; + U16_PREV(text, begin, pos, c); + return c; + } else { + return DONE; + } +} + +int32_t +UCharCharacterIterator::move(int32_t delta, CharacterIterator::EOrigin origin) { + switch(origin) { + case kStart: + pos = begin + delta; + break; + case kCurrent: + pos += delta; + break; + case kEnd: + pos = end + delta; + break; + default: + break; + } + + if(pos < begin) { + pos = begin; + } else if(pos > end) { + pos = end; + } + + return pos; +} + +int32_t +UCharCharacterIterator::move32(int32_t delta, CharacterIterator::EOrigin origin) { + // this implementation relies on the "safe" version of the UTF macros + // (or the trustworthiness of the caller) + switch(origin) { + case kStart: + pos = begin; + if(delta > 0) { + U16_FWD_N(text, pos, end, delta); + } + break; + case kCurrent: + if(delta > 0) { + U16_FWD_N(text, pos, end, delta); + } else { + U16_BACK_N(text, begin, pos, -delta); + } + break; + case kEnd: + pos = end; + if(delta < 0) { + U16_BACK_N(text, begin, pos, -delta); + } + break; + default: + break; + } + + return pos; +} + +void UCharCharacterIterator::setText(ConstChar16Ptr newText, + int32_t newTextLength) { + text = newText; + if(newText == 0 || newTextLength < 0) { + newTextLength = 0; + } + end = textLength = newTextLength; + pos = begin = 0; +} + +void +UCharCharacterIterator::getText(UnicodeString& result) { + result = UnicodeString(text, textLength); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/ucln.h b/deps/icu-small/source/common/ucln.h index fe6666efed3f18..daded82ba84deb 100644 --- a/deps/icu-small/source/common/ucln.h +++ b/deps/icu-small/source/common/ucln.h @@ -1,91 +1,91 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2001-2013, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: ucln.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2001July05 -* created by: George Rhoten -*/ - -#ifndef __UCLN_H__ -#define __UCLN_H__ - -#include "unicode/utypes.h" - -/** These are the functions used to register a library's memory cleanup - * functions. Each library should define a single library register function - * to call this API. In the i18n library, it is ucln_i18n_registerCleanup(). - * - * None of the cleanup functions should use a mutex to clean up an API's - * allocated memory because a cleanup function is not meant to be thread safe, - * and plenty of data cannot be reference counted in order to make sure that - * no one else needs the allocated data. - * - * In order to make a cleanup function get called when u_cleanup is called, - * You should add your function to the library specific cleanup function. - * If the cleanup function is not in the common library, the code that - * allocates the memory should call the library specific cleanup function. - * For instance, in the i18n library, any memory allocated statically must - * call ucln_i18n_registerCleanup() from the ucln_in.h header. These library - * cleanup functions are needed in order to prevent a circular dependency - * between the common library and any other library. - * - * The order of the cleanup is very important. In general, an API that - * depends on a second API should be cleaned up before the second API. - * For instance, the default converter in ustring depends upon the converter - * API. So the default converter should be closed before the converter API - * has its cache flushed. This will prevent any memory leaks due to - * reference counting. - * - * Please see common/ucln_cmn.{h,c} and i18n/ucln_in.{h,c} for examples. - */ - -/** - * Data Type for cleanup function selector. These roughly correspond to libraries. - */ -typedef enum ECleanupLibraryType { - UCLN_START = -1, - UCLN_UPLUG, /* ICU plugins */ - UCLN_CUSTOM, /* Custom is for anyone else. */ - UCLN_CTESTFW, - UCLN_TOOLUTIL, - UCLN_LAYOUTEX, - UCLN_LAYOUT, - UCLN_IO, - UCLN_I18N, - UCLN_COMMON /* This must be the last one to cleanup. */ -} ECleanupLibraryType; - -/** - * Data type for cleanup function pointer - */ -U_CDECL_BEGIN -typedef UBool U_CALLCONV cleanupFunc(void); -typedef void U_CALLCONV initFunc(UErrorCode *); -U_CDECL_END - -/** - * Register a cleanup function - * @param type which library to register for. - * @param func the function pointer - */ -U_CAPI void U_EXPORT2 ucln_registerCleanup(ECleanupLibraryType type, - cleanupFunc *func); - -/** - * Request cleanup for one specific library. - * Not thread safe. - * @param type which library to cleanup - */ -U_CAPI void U_EXPORT2 ucln_cleanupOne(ECleanupLibraryType type); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2001-2013, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ucln.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2001July05 +* created by: George Rhoten +*/ + +#ifndef __UCLN_H__ +#define __UCLN_H__ + +#include "unicode/utypes.h" + +/** These are the functions used to register a library's memory cleanup + * functions. Each library should define a single library register function + * to call this API. In the i18n library, it is ucln_i18n_registerCleanup(). + * + * None of the cleanup functions should use a mutex to clean up an API's + * allocated memory because a cleanup function is not meant to be thread safe, + * and plenty of data cannot be reference counted in order to make sure that + * no one else needs the allocated data. + * + * In order to make a cleanup function get called when u_cleanup is called, + * You should add your function to the library specific cleanup function. + * If the cleanup function is not in the common library, the code that + * allocates the memory should call the library specific cleanup function. + * For instance, in the i18n library, any memory allocated statically must + * call ucln_i18n_registerCleanup() from the ucln_in.h header. These library + * cleanup functions are needed in order to prevent a circular dependency + * between the common library and any other library. + * + * The order of the cleanup is very important. In general, an API that + * depends on a second API should be cleaned up before the second API. + * For instance, the default converter in ustring depends upon the converter + * API. So the default converter should be closed before the converter API + * has its cache flushed. This will prevent any memory leaks due to + * reference counting. + * + * Please see common/ucln_cmn.{h,c} and i18n/ucln_in.{h,c} for examples. + */ + +/** + * Data Type for cleanup function selector. These roughly correspond to libraries. + */ +typedef enum ECleanupLibraryType { + UCLN_START = -1, + UCLN_UPLUG, /* ICU plugins */ + UCLN_CUSTOM, /* Custom is for anyone else. */ + UCLN_CTESTFW, + UCLN_TOOLUTIL, + UCLN_LAYOUTEX, + UCLN_LAYOUT, + UCLN_IO, + UCLN_I18N, + UCLN_COMMON /* This must be the last one to cleanup. */ +} ECleanupLibraryType; + +/** + * Data type for cleanup function pointer + */ +U_CDECL_BEGIN +typedef UBool U_CALLCONV cleanupFunc(void); +typedef void U_CALLCONV initFunc(UErrorCode *); +U_CDECL_END + +/** + * Register a cleanup function + * @param type which library to register for. + * @param func the function pointer + */ +U_CAPI void U_EXPORT2 ucln_registerCleanup(ECleanupLibraryType type, + cleanupFunc *func); + +/** + * Request cleanup for one specific library. + * Not thread safe. + * @param type which library to cleanup + */ +U_CAPI void U_EXPORT2 ucln_cleanupOne(ECleanupLibraryType type); + +#endif diff --git a/deps/icu-small/source/common/ucln_cmn.cpp b/deps/icu-small/source/common/ucln_cmn.cpp index ea797d134492c9..4366d475ab980f 100644 --- a/deps/icu-small/source/common/ucln_cmn.cpp +++ b/deps/icu-small/source/common/ucln_cmn.cpp @@ -1,124 +1,124 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 2001-2014, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* file name: ucln_cmn.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2001July05 -* created by: George Rhoten -*/ - -#include "unicode/utypes.h" -#include "unicode/uclean.h" -#include "cmemory.h" -#include "mutex.h" -#include "uassert.h" -#include "ucln.h" -#include "ucln_cmn.h" -#include "utracimp.h" -#include "umutex.h" - -/** Auto-client for UCLN_COMMON **/ -#define UCLN_TYPE_IS_COMMON -#include "ucln_imp.h" - -static cleanupFunc *gCommonCleanupFunctions[UCLN_COMMON_COUNT]; -static cleanupFunc *gLibCleanupFunctions[UCLN_COMMON]; - - -/************************************************ - The cleanup order is important in this function. - Please be sure that you have read ucln.h - ************************************************/ -U_CAPI void U_EXPORT2 -u_cleanup(void) -{ - UTRACE_ENTRY_OC(UTRACE_U_CLEANUP); - icu::umtx_lock(NULL); /* Force a memory barrier, so that we are sure to see */ - icu::umtx_unlock(NULL); /* all state left around by any other threads. */ - - ucln_lib_cleanup(); - - cmemory_cleanup(); /* undo any heap functions set by u_setMemoryFunctions(). */ - UTRACE_EXIT(); /* Must be before utrace_cleanup(), which turns off tracing. */ -/*#if U_ENABLE_TRACING*/ - utrace_cleanup(); -/*#endif*/ -} - -U_CAPI void U_EXPORT2 ucln_cleanupOne(ECleanupLibraryType libType) -{ - if (gLibCleanupFunctions[libType]) - { - gLibCleanupFunctions[libType](); - gLibCleanupFunctions[libType] = NULL; - } -} - -U_CFUNC void -ucln_common_registerCleanup(ECleanupCommonType type, - cleanupFunc *func) -{ - // Thread safety messiness: From ticket 10295, calls to registerCleanup() may occur - // concurrently. Although such cases should be storing the same value, they raise errors - // from the thread sanity checker. Doing the store within a mutex avoids those. - // BUT that can trigger a recursive entry into std::call_once() in umutex.cpp when this code, - // running from the call_once function, tries to grab the ICU global mutex, which - // re-enters the mutex init path. So, work-around by special casing UCLN_COMMON_MUTEX, not - // using the ICU global mutex for it. - // - // No other point in ICU uses std::call_once(). - - U_ASSERT(UCLN_COMMON_START < type && type < UCLN_COMMON_COUNT); - if (type == UCLN_COMMON_MUTEX) { - gCommonCleanupFunctions[type] = func; - } else if (UCLN_COMMON_START < type && type < UCLN_COMMON_COUNT) { - icu::Mutex m; // See ticket 10295 for discussion. - gCommonCleanupFunctions[type] = func; - } -#if !UCLN_NO_AUTO_CLEANUP && (defined(UCLN_AUTO_ATEXIT) || defined(UCLN_AUTO_LOCAL)) - ucln_registerAutomaticCleanup(); -#endif -} - -// Note: ucln_registerCleanup() is called with the ICU global mutex locked. -// Be aware if adding anything to the function. -// See ticket 10295 for discussion. - -U_CAPI void U_EXPORT2 -ucln_registerCleanup(ECleanupLibraryType type, - cleanupFunc *func) -{ - U_ASSERT(UCLN_START < type && type < UCLN_COMMON); - if (UCLN_START < type && type < UCLN_COMMON) - { - gLibCleanupFunctions[type] = func; - } -} - -U_CFUNC UBool ucln_lib_cleanup(void) { - int32_t libType = UCLN_START; - int32_t commonFunc = UCLN_COMMON_START; - - for (libType++; libType(libType)); - } - - for (commonFunc++; commonFunc(libType)); + } + + for (commonFunc++; commonFunc - -/** - * Auto cleanup of ICU libraries - * There are several methods in per library cleanup of icu libraries: - * 1) Compiler/Platform based cleanup: - * a) Windows MSVC uses DllMain() - * b) GCC uses destructor function attribute - * c) Sun Studio, AIX VA, and HP-UX aCC uses a linker option to set the exit function - * 2) Using atexit() - * 3) Implementing own automatic cleanup functions - * - * For option 1, ensure that UCLN_NO_AUTO_CLEANUP is set to 0 by using --enable-auto-cleanup - * configure option or by otherwise setting UCLN_NO_AUTO_CLEANUP to 0 - * For option 2, follow option 1 and also define UCLN_AUTO_ATEXIT - * For option 3, follow option 1 and also define UCLN_AUTO_LOCAL (see below for more information) - */ - -#if !UCLN_NO_AUTO_CLEANUP - -/* - * The following declarations are for when UCLN_AUTO_LOCAL or UCLN_AUTO_ATEXIT - * are defined. They are commented out because they are static and will be defined - * later. The information is still here to provide some guidance for the developer - * who chooses to use UCLN_AUTO_LOCAL. - */ -/** - * Give the library an opportunity to register an automatic cleanup. - * This may be called more than once. - */ -/*static void ucln_registerAutomaticCleanup();*/ -/** - * Unregister an automatic cleanup, if possible. Called from cleanup. - */ -/*static void ucln_unRegisterAutomaticCleanup();*/ - -#ifdef UCLN_TYPE_IS_COMMON -# define UCLN_CLEAN_ME_UP u_cleanup() -#else -# define UCLN_CLEAN_ME_UP ucln_cleanupOne(UCLN_TYPE) -#endif - -/* ------------ automatic cleanup: registration. Choose ONE ------- */ -#if defined(UCLN_AUTO_LOCAL) -/* To use: - * 1. define UCLN_AUTO_LOCAL, - * 2. create ucln_local_hook.c containing implementations of - * static void ucln_registerAutomaticCleanup() - * static void ucln_unRegisterAutomaticCleanup() - */ -#include "ucln_local_hook.c" - -#elif defined(UCLN_AUTO_ATEXIT) -/* - * Use the ANSI C 'atexit' function. Note that this mechanism does not - * guarantee the order of cleanup relative to other users of ICU! - */ -static UBool gAutoCleanRegistered = false; - -static void ucln_atexit_handler() -{ - UCLN_CLEAN_ME_UP; -} - -static void ucln_registerAutomaticCleanup() -{ - if(!gAutoCleanRegistered) { - gAutoCleanRegistered = true; - atexit(&ucln_atexit_handler); - } -} - -static void ucln_unRegisterAutomaticCleanup () { -} -/* ------------end of automatic cleanup: registration. ------- */ - -#elif defined (UCLN_FINI) -/** - * If UCLN_FINI is defined, it is the (versioned, etc) name of a cleanup - * entrypoint. Add a stub to call ucln_cleanupOne - * Used on AIX, Solaris, and HP-UX - */ -U_CAPI void U_EXPORT2 UCLN_FINI (void); - -U_CAPI void U_EXPORT2 UCLN_FINI () -{ - /* This function must be defined, if UCLN_FINI is defined, else link error. */ - UCLN_CLEAN_ME_UP; -} - -/* Windows: DllMain */ -#elif U_PLATFORM_HAS_WIN32_API -/* - * ICU's own DllMain. - */ - -/* these are from putil.c */ -/* READ READ READ READ! Are you getting compilation errors from windows.h? - Any source file which includes this (ucln_imp.h) header MUST - be defined with language extensions ON. */ -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -# define VC_EXTRALEAN -# define NOUSER -# define NOSERVICE -# define NOIME -# define NOMCX -# include -/* - * This is a stub DllMain function with icu specific process handling code. - */ -BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) -{ - BOOL status = true; - - switch(fdwReason) { - case DLL_PROCESS_ATTACH: - /* ICU does not trap process attach, but must pass these through properly. */ - /* ICU specific process attach could go here */ - break; - - case DLL_PROCESS_DETACH: - /* Here is the one we actually care about. */ - - UCLN_CLEAN_ME_UP; - - break; - - case DLL_THREAD_ATTACH: - /* ICU does not trap thread attach, but must pass these through properly. */ - /* ICU specific thread attach could go here */ - break; - - case DLL_THREAD_DETACH: - /* ICU does not trap thread detach, but must pass these through properly. */ - /* ICU specific thread detach could go here */ - break; - - } - return status; -} - -#elif defined(__GNUC__) -/* GCC - use __attribute((destructor)) */ -static void ucln_destructor() __attribute__((destructor)) ; - -static void ucln_destructor() -{ - UCLN_CLEAN_ME_UP; -} - -#endif - -#endif /* UCLN_NO_AUTO_CLEANUP */ - -#else -#error This file can only be included once. -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2009-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ucln_imp.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* This file contains the platform specific implementation of per-library cleanup. +* +*/ + + +#ifndef __UCLN_IMP_H__ +#define __UCLN_IMP_H__ + +#include "ucln.h" +#include + +/** + * Auto cleanup of ICU libraries + * There are several methods in per library cleanup of icu libraries: + * 1) Compiler/Platform based cleanup: + * a) Windows MSVC uses DllMain() + * b) GCC uses destructor function attribute + * c) Sun Studio, AIX VA, and HP-UX aCC uses a linker option to set the exit function + * 2) Using atexit() + * 3) Implementing own automatic cleanup functions + * + * For option 1, ensure that UCLN_NO_AUTO_CLEANUP is set to 0 by using --enable-auto-cleanup + * configure option or by otherwise setting UCLN_NO_AUTO_CLEANUP to 0 + * For option 2, follow option 1 and also define UCLN_AUTO_ATEXIT + * For option 3, follow option 1 and also define UCLN_AUTO_LOCAL (see below for more information) + */ + +#if !UCLN_NO_AUTO_CLEANUP + +/* + * The following declarations are for when UCLN_AUTO_LOCAL or UCLN_AUTO_ATEXIT + * are defined. They are commented out because they are static and will be defined + * later. The information is still here to provide some guidance for the developer + * who chooses to use UCLN_AUTO_LOCAL. + */ +/** + * Give the library an opportunity to register an automatic cleanup. + * This may be called more than once. + */ +/*static void ucln_registerAutomaticCleanup();*/ +/** + * Unregister an automatic cleanup, if possible. Called from cleanup. + */ +/*static void ucln_unRegisterAutomaticCleanup();*/ + +#ifdef UCLN_TYPE_IS_COMMON +# define UCLN_CLEAN_ME_UP u_cleanup() +#else +# define UCLN_CLEAN_ME_UP ucln_cleanupOne(UCLN_TYPE) +#endif + +/* ------------ automatic cleanup: registration. Choose ONE ------- */ +#if defined(UCLN_AUTO_LOCAL) +/* To use: + * 1. define UCLN_AUTO_LOCAL, + * 2. create ucln_local_hook.c containing implementations of + * static void ucln_registerAutomaticCleanup() + * static void ucln_unRegisterAutomaticCleanup() + */ +#include "ucln_local_hook.c" + +#elif defined(UCLN_AUTO_ATEXIT) +/* + * Use the ANSI C 'atexit' function. Note that this mechanism does not + * guarantee the order of cleanup relative to other users of ICU! + */ +static UBool gAutoCleanRegistered = false; + +static void ucln_atexit_handler() +{ + UCLN_CLEAN_ME_UP; +} + +static void ucln_registerAutomaticCleanup() +{ + if(!gAutoCleanRegistered) { + gAutoCleanRegistered = true; + atexit(&ucln_atexit_handler); + } +} + +static void ucln_unRegisterAutomaticCleanup () { +} +/* ------------end of automatic cleanup: registration. ------- */ + +#elif defined (UCLN_FINI) +/** + * If UCLN_FINI is defined, it is the (versioned, etc) name of a cleanup + * entrypoint. Add a stub to call ucln_cleanupOne + * Used on AIX, Solaris, and HP-UX + */ +U_CAPI void U_EXPORT2 UCLN_FINI (void); + +U_CAPI void U_EXPORT2 UCLN_FINI () +{ + /* This function must be defined, if UCLN_FINI is defined, else link error. */ + UCLN_CLEAN_ME_UP; +} + +/* Windows: DllMain */ +#elif U_PLATFORM_HAS_WIN32_API +/* + * ICU's own DllMain. + */ + +/* these are from putil.c */ +/* READ READ READ READ! Are you getting compilation errors from windows.h? + Any source file which includes this (ucln_imp.h) header MUST + be defined with language extensions ON. */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +# define VC_EXTRALEAN +# define NOUSER +# define NOSERVICE +# define NOIME +# define NOMCX +# include +/* + * This is a stub DllMain function with icu specific process handling code. + */ +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + BOOL status = true; + + switch(fdwReason) { + case DLL_PROCESS_ATTACH: + /* ICU does not trap process attach, but must pass these through properly. */ + /* ICU specific process attach could go here */ + break; + + case DLL_PROCESS_DETACH: + /* Here is the one we actually care about. */ + + UCLN_CLEAN_ME_UP; + + break; + + case DLL_THREAD_ATTACH: + /* ICU does not trap thread attach, but must pass these through properly. */ + /* ICU specific thread attach could go here */ + break; + + case DLL_THREAD_DETACH: + /* ICU does not trap thread detach, but must pass these through properly. */ + /* ICU specific thread detach could go here */ + break; + + } + return status; +} + +#elif defined(__GNUC__) +/* GCC - use __attribute((destructor)) */ +static void ucln_destructor() __attribute__((destructor)) ; + +static void ucln_destructor() +{ + UCLN_CLEAN_ME_UP; +} + +#endif + +#endif /* UCLN_NO_AUTO_CLEANUP */ + +#else +#error This file can only be included once. +#endif diff --git a/deps/icu-small/source/common/ucmndata.cpp b/deps/icu-small/source/common/ucmndata.cpp index 4215d66257d1c7..c384028dad2dc3 100644 --- a/deps/icu-small/source/common/ucmndata.cpp +++ b/deps/icu-small/source/common/ucmndata.cpp @@ -1,393 +1,393 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************/ - - -/*------------------------------------------------------------------------------ - * - * UCommonData An abstract interface for dealing with ICU Common Data Files. - * ICU Common Data Files are a grouping of a number of individual - * data items (resources, converters, tables, anything) into a - * single file or dll. The combined format includes a table of - * contents for locating the individual items by name. - * - * Two formats for the table of contents are supported, which is - * why there is an abstract interface involved. - * - */ - -#include "unicode/utypes.h" -#include "unicode/udata.h" -#include "cstring.h" -#include "ucmndata.h" -#include "udatamem.h" - -#if defined(UDATA_DEBUG) || defined(UDATA_DEBUG_DUMP) -# include -#endif - -U_CFUNC uint16_t -udata_getHeaderSize(const DataHeader *udh) { - if(udh==NULL) { - return 0; - } else if(udh->info.isBigEndian==U_IS_BIG_ENDIAN) { - /* same endianness */ - return udh->dataHeader.headerSize; - } else { - /* opposite endianness */ - uint16_t x=udh->dataHeader.headerSize; - return (uint16_t)((x<<8)|(x>>8)); - } -} - -U_CFUNC uint16_t -udata_getInfoSize(const UDataInfo *info) { - if(info==NULL) { - return 0; - } else if(info->isBigEndian==U_IS_BIG_ENDIAN) { - /* same endianness */ - return info->size; - } else { - /* opposite endianness */ - uint16_t x=info->size; - return (uint16_t)((x<<8)|(x>>8)); - } -} - -/*-----------------------------------------------------------------------------* - * * - * Pointer TOCs. TODO: This form of table-of-contents should be removed * - * because DLLs must be relocated on loading to correct the * - * pointer values and this operation makes shared memory * - * mapping of the data much less likely to work. * - * * - *-----------------------------------------------------------------------------*/ -typedef struct { - const char *entryName; - const DataHeader *pHeader; -} PointerTOCEntry; - - -typedef struct { - uint32_t count; - uint32_t reserved; - /** - * Variable-length array declared with length 1 to disable bounds checkers. - * The actual array length is in the count field. - */ - PointerTOCEntry entry[1]; -} PointerTOC; - - -/* definition of OffsetTOC struct types moved to ucmndata.h */ - -/*-----------------------------------------------------------------------------* - * * - * entry point lookup implementations * - * * - *-----------------------------------------------------------------------------*/ - -#ifndef MIN -#define MIN(a,b) (((a)<(b)) ? (a) : (b)) -#endif - -/** - * Compare strings where we know the shared prefix length, - * and advance the prefix length as we find that the strings share even more characters. - */ -static int32_t -strcmpAfterPrefix(const char *s1, const char *s2, int32_t *pPrefixLength) { - int32_t pl=*pPrefixLength; - int32_t cmp=0; - s1+=pl; - s2+=pl; - for(;;) { - int32_t c1=(uint8_t)*s1++; - int32_t c2=(uint8_t)*s2++; - cmp=c1-c2; - if(cmp!=0 || c1==0) { /* different or done */ - break; - } - ++pl; /* increment shared same-prefix length */ - } - *pPrefixLength=pl; - return cmp; -} - -static int32_t -offsetTOCPrefixBinarySearch(const char *s, const char *names, - const UDataOffsetTOCEntry *toc, int32_t count) { - int32_t start=0; - int32_t limit=count; - /* - * Remember the shared prefix between s, start and limit, - * and don't compare that shared prefix again. - * The shared prefix should get longer as we narrow the [start, limit[ range. - */ - int32_t startPrefixLength=0; - int32_t limitPrefixLength=0; - if(count==0) { - return -1; - } - /* - * Prime the prefix lengths so that we don't keep prefixLength at 0 until - * both the start and limit indexes have moved. - * At the same time, we find if s is one of the start and (limit-1) names, - * and if not, exclude them from the actual binary search. - */ - if(0==strcmpAfterPrefix(s, names+toc[0].nameOffset, &startPrefixLength)) { - return 0; - } - ++start; - --limit; - if(0==strcmpAfterPrefix(s, names+toc[limit].nameOffset, &limitPrefixLength)) { - return limit; - } - while(starttoc; - if (toc != NULL) { - retVal = toc->count; - } - return retVal; -} - -static const DataHeader * U_CALLCONV -offsetTOCLookupFn(const UDataMemory *pData, - const char *tocEntryName, - int32_t *pLength, - UErrorCode *pErrorCode) { - (void)pErrorCode; - const UDataOffsetTOC *toc = (UDataOffsetTOC *)pData->toc; - if(toc!=NULL) { - const char *base=(const char *)toc; - int32_t number, count=(int32_t)toc->count; - - /* perform a binary search for the data in the common data's table of contents */ -#if defined (UDATA_DEBUG_DUMP) - /* list the contents of the TOC each time .. not recommended */ - for(number=0; numberentry[number].nameOffset]); - } -#endif - number=offsetTOCPrefixBinarySearch(tocEntryName, base, toc->entry, count); - if(number>=0) { - /* found it */ - const UDataOffsetTOCEntry *entry=toc->entry+number; -#ifdef UDATA_DEBUG - fprintf(stderr, "%s: Found.\n", tocEntryName); -#endif - if((number+1) < count) { - *pLength = (int32_t)(entry[1].dataOffset - entry->dataOffset); - } else { - *pLength = -1; - } - return (const DataHeader *)(base+entry->dataOffset); - } else { -#ifdef UDATA_DEBUG - fprintf(stderr, "%s: Not found.\n", tocEntryName); -#endif - return NULL; - } - } else { -#ifdef UDATA_DEBUG - fprintf(stderr, "returning header\n"); -#endif - - return pData->pHeader; - } -} - - -static uint32_t U_CALLCONV pointerTOCEntryCount(const UDataMemory *pData) { - const PointerTOC *toc = (PointerTOC *)pData->toc; - return (uint32_t)((toc != NULL) ? (toc->count) : 0); -} - -static const DataHeader * U_CALLCONV pointerTOCLookupFn(const UDataMemory *pData, - const char *name, - int32_t *pLength, - UErrorCode *pErrorCode) { - (void)pErrorCode; - if(pData->toc!=NULL) { - const PointerTOC *toc = (PointerTOC *)pData->toc; - int32_t number, count=(int32_t)toc->count; - -#if defined (UDATA_DEBUG_DUMP) - /* list the contents of the TOC each time .. not recommended */ - for(number=0; numberentry[number].entryName); - } -#endif - number=pointerTOCPrefixBinarySearch(name, toc->entry, count); - if(number>=0) { - /* found it */ -#ifdef UDATA_DEBUG - fprintf(stderr, "%s: Found.\n", toc->entry[number].entryName); -#endif - *pLength=-1; - return UDataMemory_normalizeDataPointer(toc->entry[number].pHeader); - } else { -#ifdef UDATA_DEBUG - fprintf(stderr, "%s: Not found.\n", name); -#endif - return NULL; - } - } else { - return pData->pHeader; - } -} -U_CDECL_END - - -static const commonDataFuncs CmnDFuncs = {offsetTOCLookupFn, offsetTOCEntryCount}; -static const commonDataFuncs ToCPFuncs = {pointerTOCLookupFn, pointerTOCEntryCount}; - - - -/*----------------------------------------------------------------------* - * * - * checkCommonData Validate the format of a common data file. * - * Fill in the virtual function ptr based on TOC type * - * If the data is invalid, close the UDataMemory * - * and set the appropriate error code. * - * * - *----------------------------------------------------------------------*/ -U_CFUNC void udata_checkCommonData(UDataMemory *udm, UErrorCode *err) { - if (U_FAILURE(*err)) { - return; - } - - if(udm==NULL || udm->pHeader==NULL) { - *err=U_INVALID_FORMAT_ERROR; - } else if(!(udm->pHeader->dataHeader.magic1==0xda && - udm->pHeader->dataHeader.magic2==0x27 && - udm->pHeader->info.isBigEndian==U_IS_BIG_ENDIAN && - udm->pHeader->info.charsetFamily==U_CHARSET_FAMILY) - ) { - /* header not valid */ - *err=U_INVALID_FORMAT_ERROR; - } - else if (udm->pHeader->info.dataFormat[0]==0x43 && - udm->pHeader->info.dataFormat[1]==0x6d && - udm->pHeader->info.dataFormat[2]==0x6e && - udm->pHeader->info.dataFormat[3]==0x44 && - udm->pHeader->info.formatVersion[0]==1 - ) { - /* dataFormat="CmnD" */ - udm->vFuncs = &CmnDFuncs; - udm->toc=(const char *)udm->pHeader+udata_getHeaderSize(udm->pHeader); - } - else if(udm->pHeader->info.dataFormat[0]==0x54 && - udm->pHeader->info.dataFormat[1]==0x6f && - udm->pHeader->info.dataFormat[2]==0x43 && - udm->pHeader->info.dataFormat[3]==0x50 && - udm->pHeader->info.formatVersion[0]==1 - ) { - /* dataFormat="ToCP" */ - udm->vFuncs = &ToCPFuncs; - udm->toc=(const char *)udm->pHeader+udata_getHeaderSize(udm->pHeader); - } - else { - /* dataFormat not recognized */ - *err=U_INVALID_FORMAT_ERROR; - } - - if (U_FAILURE(*err)) { - /* If the data is no good and we memory-mapped it ourselves, - * close the memory mapping so it doesn't leak. Note that this has - * no effect on non-memory mapped data, other than clearing fields in udm. - */ - udata_close(udm); - } -} - -/* - * TODO: Add a udata_swapPackageHeader() function that swaps an ICU .dat package - * header but not its sub-items. - * This function will be needed for automatic runtime swapping. - * Sub-items should not be swapped to limit the swapping to the parts of the - * package that are actually used. - * - * Since lengths of items are implicit in the order and offsets of their - * ToC entries, and since offsets are relative to the start of the ToC, - * a swapped version may need to generate a different data structure - * with pointers to the original data items and with their lengths - * (-1 for the last one if it is not known), and maybe even pointers to the - * swapped versions of the items. - * These pointers to swapped versions would establish a cache; - * instead, each open data item could simply own the storage for its swapped - * data. This fits better with the current design. - * - * markus 2003sep18 Jitterbug 2235 - */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************/ + + +/*------------------------------------------------------------------------------ + * + * UCommonData An abstract interface for dealing with ICU Common Data Files. + * ICU Common Data Files are a grouping of a number of individual + * data items (resources, converters, tables, anything) into a + * single file or dll. The combined format includes a table of + * contents for locating the individual items by name. + * + * Two formats for the table of contents are supported, which is + * why there is an abstract interface involved. + * + */ + +#include "unicode/utypes.h" +#include "unicode/udata.h" +#include "cstring.h" +#include "ucmndata.h" +#include "udatamem.h" + +#if defined(UDATA_DEBUG) || defined(UDATA_DEBUG_DUMP) +# include +#endif + +U_CFUNC uint16_t +udata_getHeaderSize(const DataHeader *udh) { + if(udh==nullptr) { + return 0; + } else if(udh->info.isBigEndian==U_IS_BIG_ENDIAN) { + /* same endianness */ + return udh->dataHeader.headerSize; + } else { + /* opposite endianness */ + uint16_t x=udh->dataHeader.headerSize; + return (uint16_t)((x<<8)|(x>>8)); + } +} + +U_CFUNC uint16_t +udata_getInfoSize(const UDataInfo *info) { + if(info==nullptr) { + return 0; + } else if(info->isBigEndian==U_IS_BIG_ENDIAN) { + /* same endianness */ + return info->size; + } else { + /* opposite endianness */ + uint16_t x=info->size; + return (uint16_t)((x<<8)|(x>>8)); + } +} + +/*-----------------------------------------------------------------------------* + * * + * Pointer TOCs. TODO: This form of table-of-contents should be removed * + * because DLLs must be relocated on loading to correct the * + * pointer values and this operation makes shared memory * + * mapping of the data much less likely to work. * + * * + *-----------------------------------------------------------------------------*/ +typedef struct { + const char *entryName; + const DataHeader *pHeader; +} PointerTOCEntry; + + +typedef struct { + uint32_t count; + uint32_t reserved; + /** + * Variable-length array declared with length 1 to disable bounds checkers. + * The actual array length is in the count field. + */ + PointerTOCEntry entry[1]; +} PointerTOC; + + +/* definition of OffsetTOC struct types moved to ucmndata.h */ + +/*-----------------------------------------------------------------------------* + * * + * entry point lookup implementations * + * * + *-----------------------------------------------------------------------------*/ + +#ifndef MIN +#define MIN(a,b) (((a)<(b)) ? (a) : (b)) +#endif + +/** + * Compare strings where we know the shared prefix length, + * and advance the prefix length as we find that the strings share even more characters. + */ +static int32_t +strcmpAfterPrefix(const char *s1, const char *s2, int32_t *pPrefixLength) { + int32_t pl=*pPrefixLength; + int32_t cmp=0; + s1+=pl; + s2+=pl; + for(;;) { + int32_t c1=(uint8_t)*s1++; + int32_t c2=(uint8_t)*s2++; + cmp=c1-c2; + if(cmp!=0 || c1==0) { /* different or done */ + break; + } + ++pl; /* increment shared same-prefix length */ + } + *pPrefixLength=pl; + return cmp; +} + +static int32_t +offsetTOCPrefixBinarySearch(const char *s, const char *names, + const UDataOffsetTOCEntry *toc, int32_t count) { + int32_t start=0; + int32_t limit=count; + /* + * Remember the shared prefix between s, start and limit, + * and don't compare that shared prefix again. + * The shared prefix should get longer as we narrow the [start, limit[ range. + */ + int32_t startPrefixLength=0; + int32_t limitPrefixLength=0; + if(count==0) { + return -1; + } + /* + * Prime the prefix lengths so that we don't keep prefixLength at 0 until + * both the start and limit indexes have moved. + * At the same time, we find if s is one of the start and (limit-1) names, + * and if not, exclude them from the actual binary search. + */ + if(0==strcmpAfterPrefix(s, names+toc[0].nameOffset, &startPrefixLength)) { + return 0; + } + ++start; + --limit; + if(0==strcmpAfterPrefix(s, names+toc[limit].nameOffset, &limitPrefixLength)) { + return limit; + } + while(starttoc; + if (toc != nullptr) { + retVal = toc->count; + } + return retVal; +} + +static const DataHeader * U_CALLCONV +offsetTOCLookupFn(const UDataMemory *pData, + const char *tocEntryName, + int32_t *pLength, + UErrorCode *pErrorCode) { + (void)pErrorCode; + const UDataOffsetTOC *toc = (UDataOffsetTOC *)pData->toc; + if(toc!=nullptr) { + const char *base=(const char *)toc; + int32_t number, count=(int32_t)toc->count; + + /* perform a binary search for the data in the common data's table of contents */ +#if defined (UDATA_DEBUG_DUMP) + /* list the contents of the TOC each time .. not recommended */ + for(number=0; numberentry[number].nameOffset]); + } +#endif + number=offsetTOCPrefixBinarySearch(tocEntryName, base, toc->entry, count); + if(number>=0) { + /* found it */ + const UDataOffsetTOCEntry *entry=toc->entry+number; +#ifdef UDATA_DEBUG + fprintf(stderr, "%s: Found.\n", tocEntryName); +#endif + if((number+1) < count) { + *pLength = (int32_t)(entry[1].dataOffset - entry->dataOffset); + } else { + *pLength = -1; + } + return (const DataHeader *)(base+entry->dataOffset); + } else { +#ifdef UDATA_DEBUG + fprintf(stderr, "%s: Not found.\n", tocEntryName); +#endif + return nullptr; + } + } else { +#ifdef UDATA_DEBUG + fprintf(stderr, "returning header\n"); +#endif + + return pData->pHeader; + } +} + + +static uint32_t U_CALLCONV pointerTOCEntryCount(const UDataMemory *pData) { + const PointerTOC *toc = (PointerTOC *)pData->toc; + return (uint32_t)((toc != nullptr) ? (toc->count) : 0); +} + +static const DataHeader * U_CALLCONV pointerTOCLookupFn(const UDataMemory *pData, + const char *name, + int32_t *pLength, + UErrorCode *pErrorCode) { + (void)pErrorCode; + if(pData->toc!=nullptr) { + const PointerTOC *toc = (PointerTOC *)pData->toc; + int32_t number, count=(int32_t)toc->count; + +#if defined (UDATA_DEBUG_DUMP) + /* list the contents of the TOC each time .. not recommended */ + for(number=0; numberentry[number].entryName); + } +#endif + number=pointerTOCPrefixBinarySearch(name, toc->entry, count); + if(number>=0) { + /* found it */ +#ifdef UDATA_DEBUG + fprintf(stderr, "%s: Found.\n", toc->entry[number].entryName); +#endif + *pLength=-1; + return UDataMemory_normalizeDataPointer(toc->entry[number].pHeader); + } else { +#ifdef UDATA_DEBUG + fprintf(stderr, "%s: Not found.\n", name); +#endif + return nullptr; + } + } else { + return pData->pHeader; + } +} +U_CDECL_END + + +static const commonDataFuncs CmnDFuncs = {offsetTOCLookupFn, offsetTOCEntryCount}; +static const commonDataFuncs ToCPFuncs = {pointerTOCLookupFn, pointerTOCEntryCount}; + + + +/*----------------------------------------------------------------------* + * * + * checkCommonData Validate the format of a common data file. * + * Fill in the virtual function ptr based on TOC type * + * If the data is invalid, close the UDataMemory * + * and set the appropriate error code. * + * * + *----------------------------------------------------------------------*/ +U_CFUNC void udata_checkCommonData(UDataMemory *udm, UErrorCode *err) { + if (U_FAILURE(*err)) { + return; + } + + if(udm==nullptr || udm->pHeader==nullptr) { + *err=U_INVALID_FORMAT_ERROR; + } else if(!(udm->pHeader->dataHeader.magic1==0xda && + udm->pHeader->dataHeader.magic2==0x27 && + udm->pHeader->info.isBigEndian==U_IS_BIG_ENDIAN && + udm->pHeader->info.charsetFamily==U_CHARSET_FAMILY) + ) { + /* header not valid */ + *err=U_INVALID_FORMAT_ERROR; + } + else if (udm->pHeader->info.dataFormat[0]==0x43 && + udm->pHeader->info.dataFormat[1]==0x6d && + udm->pHeader->info.dataFormat[2]==0x6e && + udm->pHeader->info.dataFormat[3]==0x44 && + udm->pHeader->info.formatVersion[0]==1 + ) { + /* dataFormat="CmnD" */ + udm->vFuncs = &CmnDFuncs; + udm->toc=(const char *)udm->pHeader+udata_getHeaderSize(udm->pHeader); + } + else if(udm->pHeader->info.dataFormat[0]==0x54 && + udm->pHeader->info.dataFormat[1]==0x6f && + udm->pHeader->info.dataFormat[2]==0x43 && + udm->pHeader->info.dataFormat[3]==0x50 && + udm->pHeader->info.formatVersion[0]==1 + ) { + /* dataFormat="ToCP" */ + udm->vFuncs = &ToCPFuncs; + udm->toc=(const char *)udm->pHeader+udata_getHeaderSize(udm->pHeader); + } + else { + /* dataFormat not recognized */ + *err=U_INVALID_FORMAT_ERROR; + } + + if (U_FAILURE(*err)) { + /* If the data is no good and we memory-mapped it ourselves, + * close the memory mapping so it doesn't leak. Note that this has + * no effect on non-memory mapped data, other than clearing fields in udm. + */ + udata_close(udm); + } +} + +/* + * TODO: Add a udata_swapPackageHeader() function that swaps an ICU .dat package + * header but not its sub-items. + * This function will be needed for automatic runtime swapping. + * Sub-items should not be swapped to limit the swapping to the parts of the + * package that are actually used. + * + * Since lengths of items are implicit in the order and offsets of their + * ToC entries, and since offsets are relative to the start of the ToC, + * a swapped version may need to generate a different data structure + * with pointers to the original data items and with their lengths + * (-1 for the last one if it is not known), and maybe even pointers to the + * swapped versions of the items. + * These pointers to swapped versions would establish a cache; + * instead, each open data item could simply own the storage for its swapped + * data. This fits better with the current design. + * + * markus 2003sep18 Jitterbug 2235 + */ diff --git a/deps/icu-small/source/common/ucmndata.h b/deps/icu-small/source/common/ucmndata.h index 486b4fd7b5f444..ffffdce2f87bae 100644 --- a/deps/icu-small/source/common/ucmndata.h +++ b/deps/icu-small/source/common/ucmndata.h @@ -1,117 +1,117 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************/ - - -/*---------------------------------------------------------------------------------- - * - * UCommonData An abstract interface for dealing with ICU Common Data Files. - * ICU Common Data Files are a grouping of a number of individual - * data items (resources, converters, tables, anything) into a - * single file or dll. The combined format includes a table of - * contents for locating the individual items by name. - * - * Two formats for the table of contents are supported, which is - * why there is an abstract interface involved. - * - * These functions are part of the ICU internal implementation, and - * are not intended to be used directly by applications. - */ - -#ifndef __UCMNDATA_H__ -#define __UCMNDATA_H__ - -#include "unicode/udata.h" -#include "umapfile.h" - - -#define COMMON_DATA_NAME U_ICUDATA_NAME - -typedef struct { - uint16_t headerSize; - uint8_t magic1; - uint8_t magic2; -} MappedData; - - -typedef struct { - MappedData dataHeader; - UDataInfo info; -} DataHeader; - -typedef struct { - uint32_t nameOffset; - uint32_t dataOffset; -} UDataOffsetTOCEntry; - -typedef struct { - uint32_t count; - /** - * Variable-length array declared with length 1 to disable bounds checkers. - * The actual array length is in the count field. - */ - UDataOffsetTOCEntry entry[1]; -} UDataOffsetTOC; - -/** - * Get the header size from a const DataHeader *udh. - * Handles opposite-endian data. - * - * @internal - */ -U_CFUNC uint16_t -udata_getHeaderSize(const DataHeader *udh); - -/** - * Get the UDataInfo.size from a const UDataInfo *info. - * Handles opposite-endian data. - * - * @internal - */ -U_CFUNC uint16_t -udata_getInfoSize(const UDataInfo *info); - -U_CDECL_BEGIN -/* - * "Virtual" functions for data lookup. - * To call one, given a UDataMemory *p, the code looks like this: - * p->vFuncs.Lookup(p, tocEntryName, pErrorCode); - * (I sure do wish this was written in C++, not C) - */ - -typedef const DataHeader * -(U_CALLCONV * LookupFn)(const UDataMemory *pData, - const char *tocEntryName, - int32_t *pLength, - UErrorCode *pErrorCode); - -typedef uint32_t -(U_CALLCONV * NumEntriesFn)(const UDataMemory *pData); - -U_CDECL_END - -typedef struct { - LookupFn Lookup; - NumEntriesFn NumEntries; -} commonDataFuncs; - - -/* - * Functions to check whether a UDataMemory refers to memory containing - * a recognizable header and table of contents a Common Data Format - * - * If a valid header and TOC are found, - * set the CommonDataFuncs function dispatch vector in the UDataMemory - * to point to the right functions for the TOC type. - * otherwise - * set an errorcode. - */ -U_CFUNC void udata_checkCommonData(UDataMemory *pData, UErrorCode *pErrorCode); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************/ + + +/*---------------------------------------------------------------------------------- + * + * UCommonData An abstract interface for dealing with ICU Common Data Files. + * ICU Common Data Files are a grouping of a number of individual + * data items (resources, converters, tables, anything) into a + * single file or dll. The combined format includes a table of + * contents for locating the individual items by name. + * + * Two formats for the table of contents are supported, which is + * why there is an abstract interface involved. + * + * These functions are part of the ICU internal implementation, and + * are not intended to be used directly by applications. + */ + +#ifndef __UCMNDATA_H__ +#define __UCMNDATA_H__ + +#include "unicode/udata.h" +#include "umapfile.h" + + +#define COMMON_DATA_NAME U_ICUDATA_NAME + +typedef struct { + uint16_t headerSize; + uint8_t magic1; + uint8_t magic2; +} MappedData; + + +typedef struct { + MappedData dataHeader; + UDataInfo info; +} DataHeader; + +typedef struct { + uint32_t nameOffset; + uint32_t dataOffset; +} UDataOffsetTOCEntry; + +typedef struct { + uint32_t count; + /** + * Variable-length array declared with length 1 to disable bounds checkers. + * The actual array length is in the count field. + */ + UDataOffsetTOCEntry entry[1]; +} UDataOffsetTOC; + +/** + * Get the header size from a const DataHeader *udh. + * Handles opposite-endian data. + * + * @internal + */ +U_CFUNC uint16_t +udata_getHeaderSize(const DataHeader *udh); + +/** + * Get the UDataInfo.size from a const UDataInfo *info. + * Handles opposite-endian data. + * + * @internal + */ +U_CFUNC uint16_t +udata_getInfoSize(const UDataInfo *info); + +U_CDECL_BEGIN +/* + * "Virtual" functions for data lookup. + * To call one, given a UDataMemory *p, the code looks like this: + * p->vFuncs.Lookup(p, tocEntryName, pErrorCode); + * (I sure do wish this was written in C++, not C) + */ + +typedef const DataHeader * +(U_CALLCONV * LookupFn)(const UDataMemory *pData, + const char *tocEntryName, + int32_t *pLength, + UErrorCode *pErrorCode); + +typedef uint32_t +(U_CALLCONV * NumEntriesFn)(const UDataMemory *pData); + +U_CDECL_END + +typedef struct { + LookupFn Lookup; + NumEntriesFn NumEntries; +} commonDataFuncs; + + +/* + * Functions to check whether a UDataMemory refers to memory containing + * a recognizable header and table of contents a Common Data Format + * + * If a valid header and TOC are found, + * set the CommonDataFuncs function dispatch vector in the UDataMemory + * to point to the right functions for the TOC type. + * otherwise + * set an errorcode. + */ +U_CFUNC void udata_checkCommonData(UDataMemory *pData, UErrorCode *pErrorCode); + +#endif diff --git a/deps/icu-small/source/common/ucnv.cpp b/deps/icu-small/source/common/ucnv.cpp index 26baa550c35e06..0a76575cd5e5c9 100644 --- a/deps/icu-small/source/common/ucnv.cpp +++ b/deps/icu-small/source/common/ucnv.cpp @@ -1,2917 +1,2922 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1998-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* ucnv.c: -* Implements APIs for the ICU's codeset conversion library; -* mostly calls through internal functions; -* created by Bertrand A. Damiba -* -* Modification History: -* -* Date Name Description -* 04/04/99 helena Fixed internal header inclusion. -* 05/09/00 helena Added implementation to handle fallback mappings. -* 06/20/2000 helena OS/400 port changes; mostly typecast. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include - -#include "unicode/ustring.h" -#include "unicode/ucnv.h" -#include "unicode/ucnv_err.h" -#include "unicode/uset.h" -#include "unicode/utf.h" -#include "unicode/utf16.h" -#include "putilimp.h" -#include "cmemory.h" -#include "cstring.h" -#include "uassert.h" -#include "utracimp.h" -#include "ustr_imp.h" -#include "ucnv_imp.h" -#include "ucnv_cnv.h" -#include "ucnv_bld.h" - -/* size of intermediate and preflighting buffers in ucnv_convert() */ -#define CHUNK_SIZE 1024 - -typedef struct UAmbiguousConverter { - const char *name; - const UChar variant5c; -} UAmbiguousConverter; - -static const UAmbiguousConverter ambiguousConverters[]={ - { "ibm-897_P100-1995", 0xa5 }, - { "ibm-942_P120-1999", 0xa5 }, - { "ibm-943_P130-1999", 0xa5 }, - { "ibm-946_P100-1995", 0xa5 }, - { "ibm-33722_P120-1999", 0xa5 }, - { "ibm-1041_P100-1995", 0xa5 }, - /*{ "ibm-54191_P100-2006", 0xa5 },*/ - /*{ "ibm-62383_P100-2007", 0xa5 },*/ - /*{ "ibm-891_P100-1995", 0x20a9 },*/ - { "ibm-944_P100-1995", 0x20a9 }, - { "ibm-949_P110-1999", 0x20a9 }, - { "ibm-1363_P110-1997", 0x20a9 }, - { "ISO_2022,locale=ko,version=0", 0x20a9 }, - { "ibm-1088_P100-1995", 0x20a9 } -}; - -/*Calls through createConverter */ -U_CAPI UConverter* U_EXPORT2 -ucnv_open (const char *name, - UErrorCode * err) -{ - UConverter *r; - - if (err == NULL || U_FAILURE (*err)) { - return NULL; - } - - r = ucnv_createConverter(NULL, name, err); - return r; -} - -U_CAPI UConverter* U_EXPORT2 -ucnv_openPackage (const char *packageName, const char *converterName, UErrorCode * err) -{ - return ucnv_createConverterFromPackage(packageName, converterName, err); -} - -/*Extracts the UChar* to a char* and calls through createConverter */ -U_CAPI UConverter* U_EXPORT2 -ucnv_openU (const UChar * name, - UErrorCode * err) -{ - char asciiName[UCNV_MAX_CONVERTER_NAME_LENGTH]; - - if (err == NULL || U_FAILURE(*err)) - return NULL; - if (name == NULL) - return ucnv_open (NULL, err); - if (u_strlen(name) >= UCNV_MAX_CONVERTER_NAME_LENGTH) - { - *err = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - return ucnv_open(u_austrcpy(asciiName, name), err); -} - -/* Copy the string that is represented by the UConverterPlatform enum - * @param platformString An output buffer - * @param platform An enum representing a platform - * @return the length of the copied string. - */ -static int32_t -ucnv_copyPlatformString(char *platformString, UConverterPlatform pltfrm) -{ - switch (pltfrm) - { - case UCNV_IBM: - uprv_strcpy(platformString, "ibm-"); - return 4; - case UCNV_UNKNOWN: - break; - } - - /* default to empty string */ - *platformString = 0; - return 0; -} - -/*Assumes a $platform-#codepage.$CONVERTER_FILE_EXTENSION scheme and calls - *through createConverter*/ -U_CAPI UConverter* U_EXPORT2 -ucnv_openCCSID (int32_t codepage, - UConverterPlatform platform, - UErrorCode * err) -{ - char myName[UCNV_MAX_CONVERTER_NAME_LENGTH]; - int32_t myNameLen; - - if (err == NULL || U_FAILURE (*err)) - return NULL; - - /* ucnv_copyPlatformString could return "ibm-" or "cp" */ - myNameLen = ucnv_copyPlatformString(myName, platform); - T_CString_integerToString(myName + myNameLen, codepage, 10); - - return ucnv_createConverter(NULL, myName, err); -} - -/* Creating a temporary stack-based object that can be used in one thread, -and created from a converter that is shared across threads. -*/ - -U_CAPI UConverter* U_EXPORT2 -ucnv_safeClone(const UConverter* cnv, void *stackBuffer, int32_t *pBufferSize, UErrorCode *status) -{ - UConverter *localConverter, *allocatedConverter; - int32_t stackBufferSize; - int32_t bufferSizeNeeded; - UErrorCode cbErr; - UConverterToUnicodeArgs toUArgs = { - sizeof(UConverterToUnicodeArgs), - true, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - }; - UConverterFromUnicodeArgs fromUArgs = { - sizeof(UConverterFromUnicodeArgs), - true, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - }; - - UTRACE_ENTRY_OC(UTRACE_UCNV_CLONE); - - if (status == NULL || U_FAILURE(*status)){ - UTRACE_EXIT_STATUS(status? *status: U_ILLEGAL_ARGUMENT_ERROR); - return NULL; - } - - if (cnv == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - UTRACE_EXIT_STATUS(*status); - return NULL; - } - - UTRACE_DATA3(UTRACE_OPEN_CLOSE, "clone converter %s at %p into stackBuffer %p", - ucnv_getName(cnv, status), cnv, stackBuffer); - - if (cnv->sharedData->impl->safeClone != NULL) { - /* call the custom safeClone function for sizing */ - bufferSizeNeeded = 0; - cnv->sharedData->impl->safeClone(cnv, NULL, &bufferSizeNeeded, status); - if (U_FAILURE(*status)) { - UTRACE_EXIT_STATUS(*status); - return NULL; - } - } - else - { - /* inherent sizing */ - bufferSizeNeeded = sizeof(UConverter); - } - - if (pBufferSize == NULL) { - stackBufferSize = 1; - pBufferSize = &stackBufferSize; - } else { - stackBufferSize = *pBufferSize; - if (stackBufferSize <= 0){ /* 'preflighting' request - set needed size into *pBufferSize */ - *pBufferSize = bufferSizeNeeded; - UTRACE_EXIT_VALUE(bufferSizeNeeded); - return NULL; - } - } - - /* Adjust (if necessary) the stackBuffer pointer to be aligned correctly for a UConverter. - * TODO(Jira ICU-20736) Redo this using std::align() once g++4.9 compatibility is no longer needed. - */ - if (stackBuffer) { - uintptr_t p = reinterpret_cast(stackBuffer); - uintptr_t aligned_p = (p + alignof(UConverter) - 1) & ~(alignof(UConverter) - 1); - ptrdiff_t pointerAdjustment = aligned_p - p; - if (bufferSizeNeeded + pointerAdjustment <= stackBufferSize) { - stackBuffer = reinterpret_cast(aligned_p); - stackBufferSize -= static_cast(pointerAdjustment); - } else { - /* prevent using the stack buffer but keep the size > 0 so that we do not just preflight */ - stackBufferSize = 1; - } - } - - /* Now, see if we must allocate any memory */ - if (stackBufferSize < bufferSizeNeeded || stackBuffer == NULL) - { - /* allocate one here...*/ - localConverter = allocatedConverter = (UConverter *) uprv_malloc (bufferSizeNeeded); - - if(localConverter == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - UTRACE_EXIT_STATUS(*status); - return NULL; - } - // If pBufferSize was NULL as the input, pBufferSize is set to &stackBufferSize in this function. - if (pBufferSize != &stackBufferSize) { - *status = U_SAFECLONE_ALLOCATED_WARNING; - } - - /* record the fact that memory was allocated */ - *pBufferSize = bufferSizeNeeded; - } else { - /* just use the stack buffer */ - localConverter = (UConverter*) stackBuffer; - allocatedConverter = NULL; - } - - uprv_memset(localConverter, 0, bufferSizeNeeded); - - /* Copy initial state */ - uprv_memcpy(localConverter, cnv, sizeof(UConverter)); - localConverter->isCopyLocal = localConverter->isExtraLocal = false; - - /* copy the substitution string */ - if (cnv->subChars == (uint8_t *)cnv->subUChars) { - localConverter->subChars = (uint8_t *)localConverter->subUChars; - } else { - localConverter->subChars = (uint8_t *)uprv_malloc(UCNV_ERROR_BUFFER_LENGTH * U_SIZEOF_UCHAR); - if (localConverter->subChars == NULL) { - uprv_free(allocatedConverter); - UTRACE_EXIT_STATUS(*status); - return NULL; - } - uprv_memcpy(localConverter->subChars, cnv->subChars, UCNV_ERROR_BUFFER_LENGTH * U_SIZEOF_UCHAR); - } - - /* now either call the safeclone fcn or not */ - if (cnv->sharedData->impl->safeClone != NULL) { - /* call the custom safeClone function */ - localConverter = cnv->sharedData->impl->safeClone(cnv, localConverter, pBufferSize, status); - } - - if(localConverter==NULL || U_FAILURE(*status)) { - if (allocatedConverter != NULL && allocatedConverter->subChars != (uint8_t *)allocatedConverter->subUChars) { - uprv_free(allocatedConverter->subChars); - } - uprv_free(allocatedConverter); - UTRACE_EXIT_STATUS(*status); - return NULL; - } - - /* increment refcount of shared data if needed */ - if (cnv->sharedData->isReferenceCounted) { - ucnv_incrementRefCount(cnv->sharedData); - } - - if(localConverter == (UConverter*)stackBuffer) { - /* we're using user provided data - set to not destroy */ - localConverter->isCopyLocal = true; - } - - /* allow callback functions to handle any memory allocation */ - toUArgs.converter = fromUArgs.converter = localConverter; - cbErr = U_ZERO_ERROR; - cnv->fromCharErrorBehaviour(cnv->toUContext, &toUArgs, NULL, 0, UCNV_CLONE, &cbErr); - cbErr = U_ZERO_ERROR; - cnv->fromUCharErrorBehaviour(cnv->fromUContext, &fromUArgs, NULL, 0, 0, UCNV_CLONE, &cbErr); - - UTRACE_EXIT_PTR_STATUS(localConverter, *status); - return localConverter; -} - -U_CAPI UConverter* U_EXPORT2 -ucnv_clone(const UConverter* cnv, UErrorCode *status) -{ - return ucnv_safeClone(cnv, nullptr, nullptr, status); -} - -/*Decreases the reference counter in the shared immutable section of the object - *and frees the mutable part*/ - -U_CAPI void U_EXPORT2 -ucnv_close (UConverter * converter) -{ - UErrorCode errorCode = U_ZERO_ERROR; - - UTRACE_ENTRY_OC(UTRACE_UCNV_CLOSE); - - if (converter == NULL) - { - UTRACE_EXIT(); - return; - } - - UTRACE_DATA3(UTRACE_OPEN_CLOSE, "close converter %s at %p, isCopyLocal=%b", - ucnv_getName(converter, &errorCode), converter, converter->isCopyLocal); - - /* In order to speed up the close, only call the callbacks when they have been changed. - This performance check will only work when the callbacks are set within a shared library - or from user code that statically links this code. */ - /* first, notify the callback functions that the converter is closed */ - if (converter->fromCharErrorBehaviour != UCNV_TO_U_DEFAULT_CALLBACK) { - UConverterToUnicodeArgs toUArgs = { - sizeof(UConverterToUnicodeArgs), - true, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - }; - - toUArgs.converter = converter; - errorCode = U_ZERO_ERROR; - converter->fromCharErrorBehaviour(converter->toUContext, &toUArgs, NULL, 0, UCNV_CLOSE, &errorCode); - } - if (converter->fromUCharErrorBehaviour != UCNV_FROM_U_DEFAULT_CALLBACK) { - UConverterFromUnicodeArgs fromUArgs = { - sizeof(UConverterFromUnicodeArgs), - true, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - }; - fromUArgs.converter = converter; - errorCode = U_ZERO_ERROR; - converter->fromUCharErrorBehaviour(converter->fromUContext, &fromUArgs, NULL, 0, 0, UCNV_CLOSE, &errorCode); - } - - if (converter->sharedData->impl->close != NULL) { - converter->sharedData->impl->close(converter); - } - - if (converter->subChars != (uint8_t *)converter->subUChars) { - uprv_free(converter->subChars); - } - - if (converter->sharedData->isReferenceCounted) { - ucnv_unloadSharedDataIfReady(converter->sharedData); - } - - if(!converter->isCopyLocal){ - uprv_free(converter); - } - - UTRACE_EXIT(); -} - -/*returns a single Name from the list, will return NULL if out of bounds - */ -U_CAPI const char* U_EXPORT2 -ucnv_getAvailableName (int32_t n) -{ - if (0 <= n && n <= 0xffff) { - UErrorCode err = U_ZERO_ERROR; - const char *name = ucnv_bld_getAvailableConverter((uint16_t)n, &err); - if (U_SUCCESS(err)) { - return name; - } - } - return NULL; -} - -U_CAPI int32_t U_EXPORT2 -ucnv_countAvailable () -{ - UErrorCode err = U_ZERO_ERROR; - return ucnv_bld_countAvailableConverters(&err); -} - -U_CAPI void U_EXPORT2 -ucnv_getSubstChars (const UConverter * converter, - char *mySubChar, - int8_t * len, - UErrorCode * err) -{ - if (U_FAILURE (*err)) - return; - - if (converter->subCharLen <= 0) { - /* Unicode string or empty string from ucnv_setSubstString(). */ - *len = 0; - return; - } - - if (*len < converter->subCharLen) /*not enough space in subChars */ - { - *err = U_INDEX_OUTOFBOUNDS_ERROR; - return; - } - - uprv_memcpy (mySubChar, converter->subChars, converter->subCharLen); /*fills in the subchars */ - *len = converter->subCharLen; /*store # of bytes copied to buffer */ -} - -U_CAPI void U_EXPORT2 -ucnv_setSubstChars (UConverter * converter, - const char *mySubChar, - int8_t len, - UErrorCode * err) -{ - if (U_FAILURE (*err)) - return; - - /*Makes sure that the subChar is within the codepages char length boundaries */ - if ((len > converter->sharedData->staticData->maxBytesPerChar) - || (len < converter->sharedData->staticData->minBytesPerChar)) - { - *err = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - uprv_memcpy (converter->subChars, mySubChar, len); /*copies the subchars */ - converter->subCharLen = len; /*sets the new len */ - - /* - * There is currently (2001Feb) no separate API to set/get subChar1. - * In order to always have subChar written after it is explicitly set, - * we set subChar1 to 0. - */ - converter->subChar1 = 0; - - return; -} - -U_CAPI void U_EXPORT2 -ucnv_setSubstString(UConverter *cnv, - const UChar *s, - int32_t length, - UErrorCode *err) { - alignas(UConverter) char cloneBuffer[U_CNV_SAFECLONE_BUFFERSIZE]; - char chars[UCNV_ERROR_BUFFER_LENGTH]; - - UConverter *clone; - uint8_t *subChars; - int32_t cloneSize, length8; - - /* Let the following functions check all arguments. */ - cloneSize = sizeof(cloneBuffer); - clone = ucnv_safeClone(cnv, cloneBuffer, &cloneSize, err); - ucnv_setFromUCallBack(clone, UCNV_FROM_U_CALLBACK_STOP, NULL, NULL, NULL, err); - length8 = ucnv_fromUChars(clone, chars, (int32_t)sizeof(chars), s, length, err); - ucnv_close(clone); - if (U_FAILURE(*err)) { - return; - } - - if (cnv->sharedData->impl->writeSub == NULL -#if !UCONFIG_NO_LEGACY_CONVERSION - || (cnv->sharedData->staticData->conversionType == UCNV_MBCS && - ucnv_MBCSGetType(cnv) != UCNV_EBCDIC_STATEFUL) -#endif - ) { - /* The converter is not stateful. Store the charset bytes as a fixed string. */ - subChars = (uint8_t *)chars; - } else { - /* - * The converter has a non-default writeSub() function, indicating - * that it is stateful. - * Store the Unicode string for on-the-fly conversion for correct - * state handling. - */ - if (length > UCNV_ERROR_BUFFER_LENGTH) { - /* - * Should not occur. The converter should output at least one byte - * per UChar, which means that ucnv_fromUChars() should catch all - * overflows. - */ - *err = U_BUFFER_OVERFLOW_ERROR; - return; - } - subChars = (uint8_t *)s; - if (length < 0) { - length = u_strlen(s); - } - length8 = length * U_SIZEOF_UCHAR; - } - - /* - * For storing the substitution string, select either the small buffer inside - * UConverter or allocate a subChars buffer. - */ - if (length8 > UCNV_MAX_SUBCHAR_LEN) { - /* Use a separate buffer for the string. Outside UConverter to not make it too large. */ - if (cnv->subChars == (uint8_t *)cnv->subUChars) { - /* Allocate a new buffer for the string. */ - cnv->subChars = (uint8_t *)uprv_malloc(UCNV_ERROR_BUFFER_LENGTH * U_SIZEOF_UCHAR); - if (cnv->subChars == NULL) { - cnv->subChars = (uint8_t *)cnv->subUChars; - *err = U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_memset(cnv->subChars, 0, UCNV_ERROR_BUFFER_LENGTH * U_SIZEOF_UCHAR); - } - } - - /* Copy the substitution string into the UConverter or its subChars buffer. */ - if (length8 == 0) { - cnv->subCharLen = 0; - } else { - uprv_memcpy(cnv->subChars, subChars, length8); - if (subChars == (uint8_t *)chars) { - cnv->subCharLen = (int8_t)length8; - } else /* subChars == s */ { - cnv->subCharLen = (int8_t)-length; - } - } - - /* See comment in ucnv_setSubstChars(). */ - cnv->subChar1 = 0; -} - -/*resets the internal states of a converter - *goal : have the same behaviour than a freshly created converter - */ -static void _reset(UConverter *converter, UConverterResetChoice choice, - UBool callCallback) { - if(converter == NULL) { - return; - } - - if(callCallback) { - /* first, notify the callback functions that the converter is reset */ - UErrorCode errorCode; - - if(choice<=UCNV_RESET_TO_UNICODE && converter->fromCharErrorBehaviour != UCNV_TO_U_DEFAULT_CALLBACK) { - UConverterToUnicodeArgs toUArgs = { - sizeof(UConverterToUnicodeArgs), - true, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - }; - toUArgs.converter = converter; - errorCode = U_ZERO_ERROR; - converter->fromCharErrorBehaviour(converter->toUContext, &toUArgs, NULL, 0, UCNV_RESET, &errorCode); - } - if(choice!=UCNV_RESET_TO_UNICODE && converter->fromUCharErrorBehaviour != UCNV_FROM_U_DEFAULT_CALLBACK) { - UConverterFromUnicodeArgs fromUArgs = { - sizeof(UConverterFromUnicodeArgs), - true, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - }; - fromUArgs.converter = converter; - errorCode = U_ZERO_ERROR; - converter->fromUCharErrorBehaviour(converter->fromUContext, &fromUArgs, NULL, 0, 0, UCNV_RESET, &errorCode); - } - } - - /* now reset the converter itself */ - if(choice<=UCNV_RESET_TO_UNICODE) { - converter->toUnicodeStatus = converter->sharedData->toUnicodeStatus; - converter->mode = 0; - converter->toULength = 0; - converter->invalidCharLength = converter->UCharErrorBufferLength = 0; - converter->preToULength = 0; - } - if(choice!=UCNV_RESET_TO_UNICODE) { - converter->fromUnicodeStatus = 0; - converter->fromUChar32 = 0; - converter->invalidUCharLength = converter->charErrorBufferLength = 0; - converter->preFromUFirstCP = U_SENTINEL; - converter->preFromULength = 0; - } - - if (converter->sharedData->impl->reset != NULL) { - /* call the custom reset function */ - converter->sharedData->impl->reset(converter, choice); - } -} - -U_CAPI void U_EXPORT2 -ucnv_reset(UConverter *converter) -{ - _reset(converter, UCNV_RESET_BOTH, true); -} - -U_CAPI void U_EXPORT2 -ucnv_resetToUnicode(UConverter *converter) -{ - _reset(converter, UCNV_RESET_TO_UNICODE, true); -} - -U_CAPI void U_EXPORT2 -ucnv_resetFromUnicode(UConverter *converter) -{ - _reset(converter, UCNV_RESET_FROM_UNICODE, true); -} - -U_CAPI int8_t U_EXPORT2 -ucnv_getMaxCharSize (const UConverter * converter) -{ - return converter->maxBytesPerUChar; -} - - -U_CAPI int8_t U_EXPORT2 -ucnv_getMinCharSize (const UConverter * converter) -{ - return converter->sharedData->staticData->minBytesPerChar; -} - -U_CAPI const char* U_EXPORT2 -ucnv_getName (const UConverter * converter, UErrorCode * err) - -{ - if (U_FAILURE (*err)) - return NULL; - if(converter->sharedData->impl->getName){ - const char* temp= converter->sharedData->impl->getName(converter); - if(temp) - return temp; - } - return converter->sharedData->staticData->name; -} - -U_CAPI int32_t U_EXPORT2 -ucnv_getCCSID(const UConverter * converter, - UErrorCode * err) -{ - int32_t ccsid; - if (U_FAILURE (*err)) - return -1; - - ccsid = converter->sharedData->staticData->codepage; - if (ccsid == 0) { - /* Rare case. This is for cases like gb18030, - which doesn't have an IBM canonical name, but does have an IBM alias. */ - const char *standardName = ucnv_getStandardName(ucnv_getName(converter, err), "IBM", err); - if (U_SUCCESS(*err) && standardName) { - const char *ccsidStr = uprv_strchr(standardName, '-'); - if (ccsidStr) { - ccsid = (int32_t)atol(ccsidStr+1); /* +1 to skip '-' */ - } - } - } - return ccsid; -} - - -U_CAPI UConverterPlatform U_EXPORT2 -ucnv_getPlatform (const UConverter * converter, - UErrorCode * err) -{ - if (U_FAILURE (*err)) - return UCNV_UNKNOWN; - - return (UConverterPlatform)converter->sharedData->staticData->platform; -} - -U_CAPI void U_EXPORT2 - ucnv_getToUCallBack (const UConverter * converter, - UConverterToUCallback *action, - const void **context) -{ - *action = converter->fromCharErrorBehaviour; - *context = converter->toUContext; -} - -U_CAPI void U_EXPORT2 - ucnv_getFromUCallBack (const UConverter * converter, - UConverterFromUCallback *action, - const void **context) -{ - *action = converter->fromUCharErrorBehaviour; - *context = converter->fromUContext; -} - -U_CAPI void U_EXPORT2 -ucnv_setToUCallBack (UConverter * converter, - UConverterToUCallback newAction, - const void* newContext, - UConverterToUCallback *oldAction, - const void** oldContext, - UErrorCode * err) -{ - if (U_FAILURE (*err)) - return; - if (oldAction) *oldAction = converter->fromCharErrorBehaviour; - converter->fromCharErrorBehaviour = newAction; - if (oldContext) *oldContext = converter->toUContext; - converter->toUContext = newContext; -} - -U_CAPI void U_EXPORT2 -ucnv_setFromUCallBack (UConverter * converter, - UConverterFromUCallback newAction, - const void* newContext, - UConverterFromUCallback *oldAction, - const void** oldContext, - UErrorCode * err) -{ - if (U_FAILURE (*err)) - return; - if (oldAction) *oldAction = converter->fromUCharErrorBehaviour; - converter->fromUCharErrorBehaviour = newAction; - if (oldContext) *oldContext = converter->fromUContext; - converter->fromUContext = newContext; -} - -static void -_updateOffsets(int32_t *offsets, int32_t length, - int32_t sourceIndex, int32_t errorInputLength) { - int32_t *limit; - int32_t delta, offset; - - if(sourceIndex>=0) { - /* - * adjust each offset by adding the previous sourceIndex - * minus the length of the input sequence that caused an - * error, if any - */ - delta=sourceIndex-errorInputLength; - } else { - /* - * set each offset to -1 because this conversion function - * does not handle offsets - */ - delta=-1; - } - - limit=offsets+length; - if(delta==0) { - /* most common case, nothing to do */ - } else if(delta>0) { - /* add the delta to each offset (but not if the offset is <0) */ - while(offsets=0) { - *offsets=offset+delta; - } - ++offsets; - } - } else /* delta<0 */ { - /* - * set each offset to -1 because this conversion function - * does not handle offsets - * or the error input sequence started in a previous buffer - */ - while(offsetsconverter; - s=pArgs->source; - t=pArgs->target; - offsets=pArgs->offsets; - - /* get the converter implementation function */ - sourceIndex=0; - if(offsets==NULL) { - fromUnicode=cnv->sharedData->impl->fromUnicode; - } else { - fromUnicode=cnv->sharedData->impl->fromUnicodeWithOffsets; - if(fromUnicode==NULL) { - /* there is no WithOffsets implementation */ - fromUnicode=cnv->sharedData->impl->fromUnicode; - /* we will write -1 for each offset */ - sourceIndex=-1; - } - } - - if(cnv->preFromULength>=0) { - /* normal mode */ - realSource=NULL; - - /* avoid compiler warnings - not otherwise necessary, and the values do not matter */ - realSourceLimit=NULL; - realFlush=false; - realSourceIndex=0; - } else { - /* - * Previous m:n conversion stored source units from a partial match - * and failed to consume all of them. - * We need to "replay" them from a temporary buffer and convert them first. - */ - realSource=pArgs->source; - realSourceLimit=pArgs->sourceLimit; - realFlush=pArgs->flush; - realSourceIndex=sourceIndex; - - uprv_memcpy(replay, cnv->preFromU, -cnv->preFromULength*U_SIZEOF_UCHAR); - pArgs->source=replay; - pArgs->sourceLimit=replay-cnv->preFromULength; - pArgs->flush=false; - sourceIndex=-1; - - cnv->preFromULength=0; - } - - /* - * loop for conversion and error handling - * - * loop { - * convert - * loop { - * update offsets - * handle end of input - * handle errors/call callback - * } - * } - */ - for(;;) { - if(U_SUCCESS(*err)) { - /* convert */ - fromUnicode(pArgs, err); - - /* - * set a flag for whether the converter - * successfully processed the end of the input - * - * need not check cnv->preFromULength==0 because a replay (<0) will cause - * sflush && pArgs->source==pArgs->sourceLimit && - cnv->fromUChar32==0); - } else { - /* handle error from ucnv_convertEx() */ - converterSawEndOfInput=false; - } - - /* no callback called yet for this iteration */ - calledCallback=false; - - /* no sourceIndex adjustment for conversion, only for callback output */ - errorInputLength=0; - - /* - * loop for offsets and error handling - * - * iterates at most 3 times: - * 1. to clean up after the conversion function - * 2. after the callback - * 3. after the callback again if there was truncated input - */ - for(;;) { - /* update offsets if we write any */ - if(offsets!=NULL) { - int32_t length=(int32_t)(pArgs->target-t); - if(length>0) { - _updateOffsets(offsets, length, sourceIndex, errorInputLength); - - /* - * if a converter handles offsets and updates the offsets - * pointer at the end, then pArgs->offset should not change - * here; - * however, some converters do not handle offsets at all - * (sourceIndex<0) or may not update the offsets pointer - */ - pArgs->offsets=offsets+=length; - } - - if(sourceIndex>=0) { - sourceIndex+=(int32_t)(pArgs->source-s); - } - } - - if(cnv->preFromULength<0) { - /* - * switch the source to new replay units (cannot occur while replaying) - * after offset handling and before end-of-input and callback handling - */ - if(realSource==NULL) { - realSource=pArgs->source; - realSourceLimit=pArgs->sourceLimit; - realFlush=pArgs->flush; - realSourceIndex=sourceIndex; - - uprv_memcpy(replay, cnv->preFromU, -cnv->preFromULength*U_SIZEOF_UCHAR); - pArgs->source=replay; - pArgs->sourceLimit=replay-cnv->preFromULength; - pArgs->flush=false; - if((sourceIndex+=cnv->preFromULength)<0) { - sourceIndex=-1; - } - - cnv->preFromULength=0; - } else { - /* see implementation note before _fromUnicodeWithCallback() */ - U_ASSERT(realSource==NULL); - *err=U_INTERNAL_PROGRAM_ERROR; - } - } - - /* update pointers */ - s=pArgs->source; - t=pArgs->target; - - if(U_SUCCESS(*err)) { - if(ssourceLimit) { - /* - * continue with the conversion loop while there is still input left - * (continue converting by breaking out of only the inner loop) - */ - break; - } else if(realSource!=NULL) { - /* switch back from replaying to the real source and continue */ - pArgs->source=realSource; - pArgs->sourceLimit=realSourceLimit; - pArgs->flush=realFlush; - sourceIndex=realSourceIndex; - - realSource=NULL; - break; - } else if(pArgs->flush && cnv->fromUChar32!=0) { - /* - * the entire input stream is consumed - * and there is a partial, truncated input sequence left - */ - - /* inject an error and continue with callback handling */ - *err=U_TRUNCATED_CHAR_FOUND; - calledCallback=false; /* new error condition */ - } else { - /* input consumed */ - if(pArgs->flush) { - /* - * return to the conversion loop once more if the flush - * flag is set and the conversion function has not - * successfully processed the end of the input yet - * - * (continue converting by breaking out of only the inner loop) - */ - if(!converterSawEndOfInput) { - break; - } - - /* reset the converter without calling the callback function */ - _reset(cnv, UCNV_RESET_FROM_UNICODE, false); - } - - /* done successfully */ - return; - } - } - - /* U_FAILURE(*err) */ - { - UErrorCode e; - - if( calledCallback || - (e=*err)==U_BUFFER_OVERFLOW_ERROR || - (e!=U_INVALID_CHAR_FOUND && - e!=U_ILLEGAL_CHAR_FOUND && - e!=U_TRUNCATED_CHAR_FOUND) - ) { - /* - * the callback did not or cannot resolve the error: - * set output pointers and return - * - * the check for buffer overflow is redundant but it is - * a high-runner case and hopefully documents the intent - * well - * - * if we were replaying, then the replay buffer must be - * copied back into the UConverter - * and the real arguments must be restored - */ - if(realSource!=NULL) { - int32_t length; - - U_ASSERT(cnv->preFromULength==0); - - length=(int32_t)(pArgs->sourceLimit-pArgs->source); - if(length>0) { - u_memcpy(cnv->preFromU, pArgs->source, length); - cnv->preFromULength=(int8_t)-length; - } - - pArgs->source=realSource; - pArgs->sourceLimit=realSourceLimit; - pArgs->flush=realFlush; - } - - return; - } - } - - /* callback handling */ - { - UChar32 codePoint; - - /* get and write the code point */ - codePoint=cnv->fromUChar32; - errorInputLength=0; - U16_APPEND_UNSAFE(cnv->invalidUCharBuffer, errorInputLength, codePoint); - cnv->invalidUCharLength=(int8_t)errorInputLength; - - /* set the converter state to deal with the next character */ - cnv->fromUChar32=0; - - /* call the callback function */ - cnv->fromUCharErrorBehaviour(cnv->fromUContext, pArgs, - cnv->invalidUCharBuffer, errorInputLength, codePoint, - *err==U_INVALID_CHAR_FOUND ? UCNV_UNASSIGNED : UCNV_ILLEGAL, - err); - } - - /* - * loop back to the offset handling - * - * this flag will indicate after offset handling - * that a callback was called; - * if the callback did not resolve the error, then we return - */ - calledCallback=true; - } - } -} - -/* - * Output the fromUnicode overflow buffer. - * Call this function if(cnv->charErrorBufferLength>0). - * @return true if overflow - */ -static UBool -ucnv_outputOverflowFromUnicode(UConverter *cnv, - char **target, const char *targetLimit, - int32_t **pOffsets, - UErrorCode *err) { - int32_t *offsets; - char *overflow, *t; - int32_t i, length; - - t=*target; - if(pOffsets!=NULL) { - offsets=*pOffsets; - } else { - offsets=NULL; - } - - overflow=(char *)cnv->charErrorBuffer; - length=cnv->charErrorBufferLength; - i=0; - while(icharErrorBufferLength=(int8_t)j; - *target=t; - if(offsets!=NULL) { - *pOffsets=offsets; - } - *err=U_BUFFER_OVERFLOW_ERROR; - return true; - } - - /* copy the overflow contents to the target */ - *t++=overflow[i++]; - if(offsets!=NULL) { - *offsets++=-1; /* no source index available for old output */ - } - } - - /* the overflow buffer is completely copied to the target */ - cnv->charErrorBufferLength=0; - *target=t; - if(offsets!=NULL) { - *pOffsets=offsets; - } - return false; -} - -U_CAPI void U_EXPORT2 -ucnv_fromUnicode(UConverter *cnv, - char **target, const char *targetLimit, - const UChar **source, const UChar *sourceLimit, - int32_t *offsets, - UBool flush, - UErrorCode *err) { - UConverterFromUnicodeArgs args; - const UChar *s; - char *t; - - /* check parameters */ - if(err==NULL || U_FAILURE(*err)) { - return; - } - - if(cnv==NULL || target==NULL || source==NULL) { - *err=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - s=*source; - t=*target; - - if ((const void *)U_MAX_PTR(sourceLimit) == (const void *)sourceLimit) { - /* - Prevent code from going into an infinite loop in case we do hit this - limit. The limit pointer is expected to be on a UChar * boundary. - This also prevents the next argument check from failing. - */ - sourceLimit = (const UChar *)(((const char *)sourceLimit) - 1); - } - - /* - * All these conditions should never happen. - * - * 1) Make sure that the limits are >= to the address source or target - * - * 2) Make sure that the buffer sizes do not exceed the number range for - * int32_t because some functions use the size (in units or bytes) - * rather than comparing pointers, and because offsets are int32_t values. - * - * size_t is guaranteed to be unsigned and large enough for the job. - * - * Return with an error instead of adjusting the limits because we would - * not be able to maintain the semantics that either the source must be - * consumed or the target filled (unless an error occurs). - * An adjustment would be targetLimit=t+0x7fffffff; for example. - * - * 3) Make sure that the user didn't incorrectly cast a UChar * pointer - * to a char * pointer and provide an incomplete UChar code unit. - */ - if (sourceLimit(size_t)0x3fffffff && sourceLimit>s) || - ((size_t)(targetLimit-t)>(size_t)0x7fffffff && targetLimit>t) || - (((const char *)sourceLimit-(const char *)s) & 1) != 0) - { - *err=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - /* output the target overflow buffer */ - if( cnv->charErrorBufferLength>0 && - ucnv_outputOverflowFromUnicode(cnv, target, targetLimit, &offsets, err) - ) { - /* U_BUFFER_OVERFLOW_ERROR */ - return; - } - /* *target may have moved, therefore stop using t */ - - if(!flush && s==sourceLimit && cnv->preFromULength>=0) { - /* the overflow buffer is emptied and there is no new input: we are done */ - return; - } - - /* - * Do not simply return with a buffer overflow error if - * !flush && t==targetLimit - * because it is possible that the source will not generate any output. - * For example, the skip callback may be called; - * it does not output anything. - */ - - /* prepare the converter arguments */ - args.converter=cnv; - args.flush=flush; - args.offsets=offsets; - args.source=s; - args.sourceLimit=sourceLimit; - args.target=*target; - args.targetLimit=targetLimit; - args.size=sizeof(args); - - _fromUnicodeWithCallback(&args, err); - - *source=args.source; - *target=args.target; -} - -/* ucnv_toUnicode() --------------------------------------------------------- */ - -static void -_toUnicodeWithCallback(UConverterToUnicodeArgs *pArgs, UErrorCode *err) { - UConverterToUnicode toUnicode; - UConverter *cnv; - const char *s; - UChar *t; - int32_t *offsets; - int32_t sourceIndex; - int32_t errorInputLength; - UBool converterSawEndOfInput, calledCallback; - - /* variables for m:n conversion */ - char replay[UCNV_EXT_MAX_BYTES]; - const char *realSource, *realSourceLimit; - int32_t realSourceIndex; - UBool realFlush; - - cnv=pArgs->converter; - s=pArgs->source; - t=pArgs->target; - offsets=pArgs->offsets; - - /* get the converter implementation function */ - sourceIndex=0; - if(offsets==NULL) { - toUnicode=cnv->sharedData->impl->toUnicode; - } else { - toUnicode=cnv->sharedData->impl->toUnicodeWithOffsets; - if(toUnicode==NULL) { - /* there is no WithOffsets implementation */ - toUnicode=cnv->sharedData->impl->toUnicode; - /* we will write -1 for each offset */ - sourceIndex=-1; - } - } - - if(cnv->preToULength>=0) { - /* normal mode */ - realSource=NULL; - - /* avoid compiler warnings - not otherwise necessary, and the values do not matter */ - realSourceLimit=NULL; - realFlush=false; - realSourceIndex=0; - } else { - /* - * Previous m:n conversion stored source units from a partial match - * and failed to consume all of them. - * We need to "replay" them from a temporary buffer and convert them first. - */ - realSource=pArgs->source; - realSourceLimit=pArgs->sourceLimit; - realFlush=pArgs->flush; - realSourceIndex=sourceIndex; - - uprv_memcpy(replay, cnv->preToU, -cnv->preToULength); - pArgs->source=replay; - pArgs->sourceLimit=replay-cnv->preToULength; - pArgs->flush=false; - sourceIndex=-1; - - cnv->preToULength=0; - } - - /* - * loop for conversion and error handling - * - * loop { - * convert - * loop { - * update offsets - * handle end of input - * handle errors/call callback - * } - * } - */ - for(;;) { - if(U_SUCCESS(*err)) { - /* convert */ - toUnicode(pArgs, err); - - /* - * set a flag for whether the converter - * successfully processed the end of the input - * - * need not check cnv->preToULength==0 because a replay (<0) will cause - * sflush && pArgs->source==pArgs->sourceLimit && - cnv->toULength==0); - } else { - /* handle error from getNextUChar() or ucnv_convertEx() */ - converterSawEndOfInput=false; - } - - /* no callback called yet for this iteration */ - calledCallback=false; - - /* no sourceIndex adjustment for conversion, only for callback output */ - errorInputLength=0; - - /* - * loop for offsets and error handling - * - * iterates at most 3 times: - * 1. to clean up after the conversion function - * 2. after the callback - * 3. after the callback again if there was truncated input - */ - for(;;) { - /* update offsets if we write any */ - if(offsets!=NULL) { - int32_t length=(int32_t)(pArgs->target-t); - if(length>0) { - _updateOffsets(offsets, length, sourceIndex, errorInputLength); - - /* - * if a converter handles offsets and updates the offsets - * pointer at the end, then pArgs->offset should not change - * here; - * however, some converters do not handle offsets at all - * (sourceIndex<0) or may not update the offsets pointer - */ - pArgs->offsets=offsets+=length; - } - - if(sourceIndex>=0) { - sourceIndex+=(int32_t)(pArgs->source-s); - } - } - - if(cnv->preToULength<0) { - /* - * switch the source to new replay units (cannot occur while replaying) - * after offset handling and before end-of-input and callback handling - */ - if(realSource==NULL) { - realSource=pArgs->source; - realSourceLimit=pArgs->sourceLimit; - realFlush=pArgs->flush; - realSourceIndex=sourceIndex; - - uprv_memcpy(replay, cnv->preToU, -cnv->preToULength); - pArgs->source=replay; - pArgs->sourceLimit=replay-cnv->preToULength; - pArgs->flush=false; - if((sourceIndex+=cnv->preToULength)<0) { - sourceIndex=-1; - } - - cnv->preToULength=0; - } else { - /* see implementation note before _fromUnicodeWithCallback() */ - U_ASSERT(realSource==NULL); - *err=U_INTERNAL_PROGRAM_ERROR; - } - } - - /* update pointers */ - s=pArgs->source; - t=pArgs->target; - - if(U_SUCCESS(*err)) { - if(ssourceLimit) { - /* - * continue with the conversion loop while there is still input left - * (continue converting by breaking out of only the inner loop) - */ - break; - } else if(realSource!=NULL) { - /* switch back from replaying to the real source and continue */ - pArgs->source=realSource; - pArgs->sourceLimit=realSourceLimit; - pArgs->flush=realFlush; - sourceIndex=realSourceIndex; - - realSource=NULL; - break; - } else if(pArgs->flush && cnv->toULength>0) { - /* - * the entire input stream is consumed - * and there is a partial, truncated input sequence left - */ - - /* inject an error and continue with callback handling */ - *err=U_TRUNCATED_CHAR_FOUND; - calledCallback=false; /* new error condition */ - } else { - /* input consumed */ - if(pArgs->flush) { - /* - * return to the conversion loop once more if the flush - * flag is set and the conversion function has not - * successfully processed the end of the input yet - * - * (continue converting by breaking out of only the inner loop) - */ - if(!converterSawEndOfInput) { - break; - } - - /* reset the converter without calling the callback function */ - _reset(cnv, UCNV_RESET_TO_UNICODE, false); - } - - /* done successfully */ - return; - } - } - - /* U_FAILURE(*err) */ - { - UErrorCode e; - - if( calledCallback || - (e=*err)==U_BUFFER_OVERFLOW_ERROR || - (e!=U_INVALID_CHAR_FOUND && - e!=U_ILLEGAL_CHAR_FOUND && - e!=U_TRUNCATED_CHAR_FOUND && - e!=U_ILLEGAL_ESCAPE_SEQUENCE && - e!=U_UNSUPPORTED_ESCAPE_SEQUENCE) - ) { - /* - * the callback did not or cannot resolve the error: - * set output pointers and return - * - * the check for buffer overflow is redundant but it is - * a high-runner case and hopefully documents the intent - * well - * - * if we were replaying, then the replay buffer must be - * copied back into the UConverter - * and the real arguments must be restored - */ - if(realSource!=NULL) { - int32_t length; - - U_ASSERT(cnv->preToULength==0); - - length=(int32_t)(pArgs->sourceLimit-pArgs->source); - if(length>0) { - uprv_memcpy(cnv->preToU, pArgs->source, length); - cnv->preToULength=(int8_t)-length; - } - - pArgs->source=realSource; - pArgs->sourceLimit=realSourceLimit; - pArgs->flush=realFlush; - } - - return; - } - } - - /* copy toUBytes[] to invalidCharBuffer[] */ - errorInputLength=cnv->invalidCharLength=cnv->toULength; - if(errorInputLength>0) { - uprv_memcpy(cnv->invalidCharBuffer, cnv->toUBytes, errorInputLength); - } - - /* set the converter state to deal with the next character */ - cnv->toULength=0; - - /* call the callback function */ - if(cnv->toUCallbackReason==UCNV_ILLEGAL && *err==U_INVALID_CHAR_FOUND) { - cnv->toUCallbackReason = UCNV_UNASSIGNED; - } - cnv->fromCharErrorBehaviour(cnv->toUContext, pArgs, - cnv->invalidCharBuffer, errorInputLength, - cnv->toUCallbackReason, - err); - cnv->toUCallbackReason = UCNV_ILLEGAL; /* reset to default value */ - - /* - * loop back to the offset handling - * - * this flag will indicate after offset handling - * that a callback was called; - * if the callback did not resolve the error, then we return - */ - calledCallback=true; - } - } -} - -/* - * Output the toUnicode overflow buffer. - * Call this function if(cnv->UCharErrorBufferLength>0). - * @return true if overflow - */ -static UBool -ucnv_outputOverflowToUnicode(UConverter *cnv, - UChar **target, const UChar *targetLimit, - int32_t **pOffsets, - UErrorCode *err) { - int32_t *offsets; - UChar *overflow, *t; - int32_t i, length; - - t=*target; - if(pOffsets!=NULL) { - offsets=*pOffsets; - } else { - offsets=NULL; - } - - overflow=cnv->UCharErrorBuffer; - length=cnv->UCharErrorBufferLength; - i=0; - while(iUCharErrorBufferLength=(int8_t)j; - *target=t; - if(offsets!=NULL) { - *pOffsets=offsets; - } - *err=U_BUFFER_OVERFLOW_ERROR; - return true; - } - - /* copy the overflow contents to the target */ - *t++=overflow[i++]; - if(offsets!=NULL) { - *offsets++=-1; /* no source index available for old output */ - } - } - - /* the overflow buffer is completely copied to the target */ - cnv->UCharErrorBufferLength=0; - *target=t; - if(offsets!=NULL) { - *pOffsets=offsets; - } - return false; -} - -U_CAPI void U_EXPORT2 -ucnv_toUnicode(UConverter *cnv, - UChar **target, const UChar *targetLimit, - const char **source, const char *sourceLimit, - int32_t *offsets, - UBool flush, - UErrorCode *err) { - UConverterToUnicodeArgs args; - const char *s; - UChar *t; - - /* check parameters */ - if(err==NULL || U_FAILURE(*err)) { - return; - } - - if(cnv==NULL || target==NULL || source==NULL) { - *err=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - s=*source; - t=*target; - - if ((const void *)U_MAX_PTR(targetLimit) == (const void *)targetLimit) { - /* - Prevent code from going into an infinite loop in case we do hit this - limit. The limit pointer is expected to be on a UChar * boundary. - This also prevents the next argument check from failing. - */ - targetLimit = (const UChar *)(((const char *)targetLimit) - 1); - } - - /* - * All these conditions should never happen. - * - * 1) Make sure that the limits are >= to the address source or target - * - * 2) Make sure that the buffer sizes do not exceed the number range for - * int32_t because some functions use the size (in units or bytes) - * rather than comparing pointers, and because offsets are int32_t values. - * - * size_t is guaranteed to be unsigned and large enough for the job. - * - * Return with an error instead of adjusting the limits because we would - * not be able to maintain the semantics that either the source must be - * consumed or the target filled (unless an error occurs). - * An adjustment would be sourceLimit=t+0x7fffffff; for example. - * - * 3) Make sure that the user didn't incorrectly cast a UChar * pointer - * to a char * pointer and provide an incomplete UChar code unit. - */ - if (sourceLimit(size_t)0x7fffffff && sourceLimit>s) || - ((size_t)(targetLimit-t)>(size_t)0x3fffffff && targetLimit>t) || - (((const char *)targetLimit-(const char *)t) & 1) != 0 - ) { - *err=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - /* output the target overflow buffer */ - if( cnv->UCharErrorBufferLength>0 && - ucnv_outputOverflowToUnicode(cnv, target, targetLimit, &offsets, err) - ) { - /* U_BUFFER_OVERFLOW_ERROR */ - return; - } - /* *target may have moved, therefore stop using t */ - - if(!flush && s==sourceLimit && cnv->preToULength>=0) { - /* the overflow buffer is emptied and there is no new input: we are done */ - return; - } - - /* - * Do not simply return with a buffer overflow error if - * !flush && t==targetLimit - * because it is possible that the source will not generate any output. - * For example, the skip callback may be called; - * it does not output anything. - */ - - /* prepare the converter arguments */ - args.converter=cnv; - args.flush=flush; - args.offsets=offsets; - args.source=s; - args.sourceLimit=sourceLimit; - args.target=*target; - args.targetLimit=targetLimit; - args.size=sizeof(args); - - _toUnicodeWithCallback(&args, err); - - *source=args.source; - *target=args.target; -} - -/* ucnv_to/fromUChars() ----------------------------------------------------- */ - -U_CAPI int32_t U_EXPORT2 -ucnv_fromUChars(UConverter *cnv, - char *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - UErrorCode *pErrorCode) { - const UChar *srcLimit; - char *originalDest, *destLimit; - int32_t destLength; - - /* check arguments */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - if( cnv==NULL || - destCapacity<0 || (destCapacity>0 && dest==NULL) || - srcLength<-1 || (srcLength!=0 && src==NULL) - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* initialize */ - ucnv_resetFromUnicode(cnv); - originalDest=dest; - if(srcLength==-1) { - srcLength=u_strlen(src); - } - if(srcLength>0) { - srcLimit=src+srcLength; - destCapacity=pinCapacity(dest, destCapacity); - destLimit=dest+destCapacity; - - /* perform the conversion */ - ucnv_fromUnicode(cnv, &dest, destLimit, &src, srcLimit, 0, true, pErrorCode); - destLength=(int32_t)(dest-originalDest); - - /* if an overflow occurs, then get the preflighting length */ - if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) { - char buffer[1024]; - - destLimit=buffer+sizeof(buffer); - do { - dest=buffer; - *pErrorCode=U_ZERO_ERROR; - ucnv_fromUnicode(cnv, &dest, destLimit, &src, srcLimit, 0, true, pErrorCode); - destLength+=(int32_t)(dest-buffer); - } while(*pErrorCode==U_BUFFER_OVERFLOW_ERROR); - } - } else { - destLength=0; - } - - return u_terminateChars(originalDest, destCapacity, destLength, pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -ucnv_toUChars(UConverter *cnv, - UChar *dest, int32_t destCapacity, - const char *src, int32_t srcLength, - UErrorCode *pErrorCode) { - const char *srcLimit; - UChar *originalDest, *destLimit; - int32_t destLength; - - /* check arguments */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - if( cnv==NULL || - destCapacity<0 || (destCapacity>0 && dest==NULL) || - srcLength<-1 || (srcLength!=0 && src==NULL)) - { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* initialize */ - ucnv_resetToUnicode(cnv); - originalDest=dest; - if(srcLength==-1) { - srcLength=(int32_t)uprv_strlen(src); - } - if(srcLength>0) { - srcLimit=src+srcLength; - destCapacity=pinCapacity(dest, destCapacity); - destLimit=dest+destCapacity; - - /* perform the conversion */ - ucnv_toUnicode(cnv, &dest, destLimit, &src, srcLimit, 0, true, pErrorCode); - destLength=(int32_t)(dest-originalDest); - - /* if an overflow occurs, then get the preflighting length */ - if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) - { - UChar buffer[1024]; - - destLimit=buffer+UPRV_LENGTHOF(buffer); - do { - dest=buffer; - *pErrorCode=U_ZERO_ERROR; - ucnv_toUnicode(cnv, &dest, destLimit, &src, srcLimit, 0, true, pErrorCode); - destLength+=(int32_t)(dest-buffer); - } - while(*pErrorCode==U_BUFFER_OVERFLOW_ERROR); - } - } else { - destLength=0; - } - - return u_terminateUChars(originalDest, destCapacity, destLength, pErrorCode); -} - -/* ucnv_getNextUChar() ------------------------------------------------------ */ - -U_CAPI UChar32 U_EXPORT2 -ucnv_getNextUChar(UConverter *cnv, - const char **source, const char *sourceLimit, - UErrorCode *err) { - UConverterToUnicodeArgs args; - UChar buffer[U16_MAX_LENGTH]; - const char *s; - UChar32 c; - int32_t i, length; - - /* check parameters */ - if(err==NULL || U_FAILURE(*err)) { - return 0xffff; - } - - if(cnv==NULL || source==NULL) { - *err=U_ILLEGAL_ARGUMENT_ERROR; - return 0xffff; - } - - s=*source; - if(sourceLimit(size_t)0x7fffffff && sourceLimit>s)) { - *err=U_ILLEGAL_ARGUMENT_ERROR; - return 0xffff; - } - - c=U_SENTINEL; - - /* flush the target overflow buffer */ - if(cnv->UCharErrorBufferLength>0) { - UChar *overflow; - - overflow=cnv->UCharErrorBuffer; - i=0; - length=cnv->UCharErrorBufferLength; - U16_NEXT(overflow, i, length, c); - - /* move the remaining overflow contents up to the beginning */ - if((cnv->UCharErrorBufferLength=(int8_t)(length-i))>0) { - uprv_memmove(cnv->UCharErrorBuffer, cnv->UCharErrorBuffer+i, - cnv->UCharErrorBufferLength*U_SIZEOF_UCHAR); - } - - if(!U16_IS_LEAD(c) || itoULength==0 && cnv->sharedData->impl->getNextUChar!=NULL) { - c=cnv->sharedData->impl->getNextUChar(&args, err); - *source=s=args.source; - if(*err==U_INDEX_OUTOFBOUNDS_ERROR) { - /* reset the converter without calling the callback function */ - _reset(cnv, UCNV_RESET_TO_UNICODE, false); - return 0xffff; /* no output */ - } else if(U_SUCCESS(*err) && c>=0) { - return c; - /* - * else fall through to use _toUnicode() because - * UCNV_GET_NEXT_UCHAR_USE_TO_U: the native function did not want to handle it after all - * U_FAILURE: call _toUnicode() for callback handling (do not output c) - */ - } - } - - /* convert to one UChar in buffer[0], or handle getNextUChar() errors */ - _toUnicodeWithCallback(&args, err); - - if(*err==U_BUFFER_OVERFLOW_ERROR) { - *err=U_ZERO_ERROR; - } - - i=0; - length=(int32_t)(args.target-buffer); - } else { - /* write the lead surrogate from the overflow buffer */ - buffer[0]=(UChar)c; - args.target=buffer+1; - i=0; - length=1; - } - - /* buffer contents starts at i and ends before length */ - - if(U_FAILURE(*err)) { - c=0xffff; /* no output */ - } else if(length==0) { - /* no input or only state changes */ - *err=U_INDEX_OUTOFBOUNDS_ERROR; - /* no need to reset explicitly because _toUnicodeWithCallback() did it */ - c=0xffff; /* no output */ - } else { - c=buffer[0]; - i=1; - if(!U16_IS_LEAD(c)) { - /* consume c=buffer[0], done */ - } else { - /* got a lead surrogate, see if a trail surrogate follows */ - UChar c2; - - if(cnv->UCharErrorBufferLength>0) { - /* got overflow output from the conversion */ - if(U16_IS_TRAIL(c2=cnv->UCharErrorBuffer[0])) { - /* got a trail surrogate, too */ - c=U16_GET_SUPPLEMENTARY(c, c2); - - /* move the remaining overflow contents up to the beginning */ - if((--cnv->UCharErrorBufferLength)>0) { - uprv_memmove(cnv->UCharErrorBuffer, cnv->UCharErrorBuffer+1, - cnv->UCharErrorBufferLength*U_SIZEOF_UCHAR); - } - } else { - /* c is an unpaired lead surrogate, just return it */ - } - } else if(args.sourceUCharErrorBufferLength)>0) { - uprv_memmove(cnv->UCharErrorBuffer+delta, cnv->UCharErrorBuffer, - length*U_SIZEOF_UCHAR); - } - cnv->UCharErrorBufferLength=(int8_t)(length+delta); - - cnv->UCharErrorBuffer[0]=buffer[i++]; - if(delta>1) { - cnv->UCharErrorBuffer[1]=buffer[i]; - } - } - - *source=args.source; - return c; -} - -/* ucnv_convert() and siblings ---------------------------------------------- */ - -U_CAPI void U_EXPORT2 -ucnv_convertEx(UConverter *targetCnv, UConverter *sourceCnv, - char **target, const char *targetLimit, - const char **source, const char *sourceLimit, - UChar *pivotStart, UChar **pivotSource, - UChar **pivotTarget, const UChar *pivotLimit, - UBool reset, UBool flush, - UErrorCode *pErrorCode) { - UChar pivotBuffer[CHUNK_SIZE]; - const UChar *myPivotSource; - UChar *myPivotTarget; - const char *s; - char *t; - - UConverterToUnicodeArgs toUArgs; - UConverterFromUnicodeArgs fromUArgs; - UConverterConvert convert; - - /* error checking */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return; - } - - if( targetCnv==NULL || sourceCnv==NULL || - source==NULL || *source==NULL || - target==NULL || *target==NULL || targetLimit==NULL - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - s=*source; - t=*target; - if((sourceLimit!=NULL && sourceLimit(size_t)0x7fffffff && sourceLimit>s)) || - ((size_t)(targetLimit-t)>(size_t)0x7fffffff && targetLimit>t) - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - if(pivotStart==NULL) { - if(!flush) { - /* streaming conversion requires an explicit pivot buffer */ - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - /* use the stack pivot buffer */ - myPivotSource=myPivotTarget=pivotStart=pivotBuffer; - pivotSource=(UChar **)&myPivotSource; - pivotTarget=&myPivotTarget; - pivotLimit=pivotBuffer+CHUNK_SIZE; - } else if( pivotStart>=pivotLimit || - pivotSource==NULL || *pivotSource==NULL || - pivotTarget==NULL || *pivotTarget==NULL || - pivotLimit==NULL - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - if(sourceLimit==NULL) { - /* get limit of single-byte-NUL-terminated source string */ - sourceLimit=uprv_strchr(*source, 0); - } - - if(reset) { - ucnv_resetToUnicode(sourceCnv); - ucnv_resetFromUnicode(targetCnv); - *pivotSource=*pivotTarget=pivotStart; - } else if(targetCnv->charErrorBufferLength>0) { - /* output the targetCnv overflow buffer */ - if(ucnv_outputOverflowFromUnicode(targetCnv, target, targetLimit, NULL, pErrorCode)) { - /* U_BUFFER_OVERFLOW_ERROR */ - return; - } - /* *target has moved, therefore stop using t */ - - if( !flush && - targetCnv->preFromULength>=0 && *pivotSource==*pivotTarget && - sourceCnv->UCharErrorBufferLength==0 && sourceCnv->preToULength>=0 && s==sourceLimit - ) { - /* the fromUnicode overflow buffer is emptied and there is no new input: we are done */ - return; - } - } - - /* Is direct-UTF-8 conversion available? */ - if( sourceCnv->sharedData->staticData->conversionType==UCNV_UTF8 && - targetCnv->sharedData->impl->fromUTF8!=NULL - ) { - convert=targetCnv->sharedData->impl->fromUTF8; - } else if( targetCnv->sharedData->staticData->conversionType==UCNV_UTF8 && - sourceCnv->sharedData->impl->toUTF8!=NULL - ) { - convert=sourceCnv->sharedData->impl->toUTF8; - } else { - convert=NULL; - } - - /* - * If direct-UTF-8 conversion is available, then we use a smaller - * pivot buffer for error handling and partial matches - * so that we quickly return to direct conversion. - * - * 32 is large enough for UCNV_EXT_MAX_UCHARS and UCNV_ERROR_BUFFER_LENGTH. - * - * We could reduce the pivot buffer size further, at the cost of - * buffer overflows from callbacks. - * The pivot buffer should not be smaller than the maximum number of - * fromUnicode extension table input UChars - * (for m:n conversion, see - * targetCnv->sharedData->mbcs.extIndexes[UCNV_EXT_COUNT_UCHARS]) - * or 2 for surrogate pairs. - * - * Too small a buffer can cause thrashing between pivoting and direct - * conversion, with function call overhead outweighing the benefits - * of direct conversion. - */ - if(convert!=NULL && (pivotLimit-pivotStart)>32) { - pivotLimit=pivotStart+32; - } - - /* prepare the converter arguments */ - fromUArgs.converter=targetCnv; - fromUArgs.flush=false; - fromUArgs.offsets=NULL; - fromUArgs.target=*target; - fromUArgs.targetLimit=targetLimit; - fromUArgs.size=sizeof(fromUArgs); - - toUArgs.converter=sourceCnv; - toUArgs.flush=flush; - toUArgs.offsets=NULL; - toUArgs.source=s; - toUArgs.sourceLimit=sourceLimit; - toUArgs.targetLimit=pivotLimit; - toUArgs.size=sizeof(toUArgs); - - /* - * TODO: Consider separating this function into two functions, - * extracting exactly the conversion loop, - * for readability and to reduce the set of visible variables. - * - * Otherwise stop using s and t from here on. - */ - s=t=NULL; - - /* - * conversion loop - * - * The sequence of steps in the loop may appear backward, - * but the principle is simple: - * In the chain of - * source - sourceCnv overflow - pivot - targetCnv overflow - target - * empty out later buffers before refilling them from earlier ones. - * - * The targetCnv overflow buffer is flushed out only once before the loop. - */ - for(;;) { - /* - * if(pivot not empty or error or replay or flush fromUnicode) { - * fromUnicode(pivot -> target); - * } - * - * For pivoting conversion; and for direct conversion for - * error callback handling and flushing the replay buffer. - */ - if( *pivotSource<*pivotTarget || - U_FAILURE(*pErrorCode) || - targetCnv->preFromULength<0 || - fromUArgs.flush - ) { - fromUArgs.source=*pivotSource; - fromUArgs.sourceLimit=*pivotTarget; - _fromUnicodeWithCallback(&fromUArgs, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - /* target overflow, or conversion error */ - *pivotSource=(UChar *)fromUArgs.source; - break; - } - - /* - * _fromUnicodeWithCallback() must have consumed the pivot contents - * (*pivotSource==*pivotTarget) since it returned with U_SUCCESS() - */ - } - - /* The pivot buffer is empty; reset it so we start at pivotStart. */ - *pivotSource=*pivotTarget=pivotStart; - - /* - * if(sourceCnv overflow buffer not empty) { - * move(sourceCnv overflow buffer -> pivot); - * continue; - * } - */ - /* output the sourceCnv overflow buffer */ - if(sourceCnv->UCharErrorBufferLength>0) { - if(ucnv_outputOverflowToUnicode(sourceCnv, pivotTarget, pivotLimit, NULL, pErrorCode)) { - /* U_BUFFER_OVERFLOW_ERROR */ - *pErrorCode=U_ZERO_ERROR; - } - continue; - } - - /* - * check for end of input and break if done - * - * Checking both flush and fromUArgs.flush ensures that the converters - * have been called with the flush flag set if the ucnv_convertEx() - * caller set it. - */ - if( toUArgs.source==sourceLimit && - sourceCnv->preToULength>=0 && sourceCnv->toULength==0 && - (!flush || fromUArgs.flush) - ) { - /* done successfully */ - break; - } - - /* - * use direct conversion if available - * but not if continuing a partial match - * or flushing the toUnicode replay buffer - */ - if(convert!=NULL && targetCnv->preFromUFirstCP<0 && sourceCnv->preToULength==0) { - if(*pErrorCode==U_USING_DEFAULT_WARNING) { - /* remove a warning that may be set by this function */ - *pErrorCode=U_ZERO_ERROR; - } - convert(&fromUArgs, &toUArgs, pErrorCode); - if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) { - break; - } else if(U_FAILURE(*pErrorCode)) { - if(sourceCnv->toULength>0) { - /* - * Fall through to calling _toUnicodeWithCallback() - * for callback handling. - * - * The pivot buffer will be reset with - * *pivotSource=*pivotTarget=pivotStart; - * which indicates a toUnicode error to the caller - * (*pivotSource==pivotStart shows no pivot UChars consumed). - */ - } else { - /* - * Indicate a fromUnicode error to the caller - * (*pivotSource>pivotStart shows some pivot UChars consumed). - */ - *pivotSource=*pivotTarget=pivotStart+1; - /* - * Loop around to calling _fromUnicodeWithCallbacks() - * for callback handling. - */ - continue; - } - } else if(*pErrorCode==U_USING_DEFAULT_WARNING) { - /* - * No error, but the implementation requested to temporarily - * fall back to pivoting. - */ - *pErrorCode=U_ZERO_ERROR; - /* - * The following else branches are almost identical to the end-of-input - * handling in _toUnicodeWithCallback(). - * Avoid calling it just for the end of input. - */ - } else if(flush && sourceCnv->toULength>0) { /* flush==toUArgs.flush */ - /* - * the entire input stream is consumed - * and there is a partial, truncated input sequence left - */ - - /* inject an error and continue with callback handling */ - *pErrorCode=U_TRUNCATED_CHAR_FOUND; - } else { - /* input consumed */ - if(flush) { - /* reset the converters without calling the callback functions */ - _reset(sourceCnv, UCNV_RESET_TO_UNICODE, false); - _reset(targetCnv, UCNV_RESET_FROM_UNICODE, false); - } - - /* done successfully */ - break; - } - } - - /* - * toUnicode(source -> pivot); - * - * For pivoting conversion; and for direct conversion for - * error callback handling, continuing partial matches - * and flushing the replay buffer. - * - * The pivot buffer is empty and reset. - */ - toUArgs.target=pivotStart; /* ==*pivotTarget */ - /* toUArgs.targetLimit=pivotLimit; already set before the loop */ - _toUnicodeWithCallback(&toUArgs, pErrorCode); - *pivotTarget=toUArgs.target; - if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) { - /* pivot overflow: continue with the conversion loop */ - *pErrorCode=U_ZERO_ERROR; - } else if(U_FAILURE(*pErrorCode) || (!flush && *pivotTarget==pivotStart)) { - /* conversion error, or there was nothing left to convert */ - break; - } - /* - * else: - * _toUnicodeWithCallback() wrote into the pivot buffer, - * continue with fromUnicode conversion. - * - * Set the fromUnicode flush flag if we flush and if toUnicode has - * processed the end of the input. - */ - if( flush && toUArgs.source==sourceLimit && - sourceCnv->preToULength>=0 && - sourceCnv->UCharErrorBufferLength==0 - ) { - fromUArgs.flush=true; - } - } - - /* - * The conversion loop is exited when one of the following is true: - * - the entire source text has been converted successfully to the target buffer - * - a target buffer overflow occurred - * - a conversion error occurred - */ - - *source=toUArgs.source; - *target=fromUArgs.target; - - /* terminate the target buffer if possible */ - if(flush && U_SUCCESS(*pErrorCode)) { - if(*target!=targetLimit) { - **target=0; - if(*pErrorCode==U_STRING_NOT_TERMINATED_WARNING) { - *pErrorCode=U_ZERO_ERROR; - } - } else { - *pErrorCode=U_STRING_NOT_TERMINATED_WARNING; - } - } -} - -/* internal implementation of ucnv_convert() etc. with preflighting */ -static int32_t -ucnv_internalConvert(UConverter *outConverter, UConverter *inConverter, - char *target, int32_t targetCapacity, - const char *source, int32_t sourceLength, - UErrorCode *pErrorCode) { - UChar pivotBuffer[CHUNK_SIZE]; - UChar *pivot, *pivot2; - - char *myTarget; - const char *sourceLimit; - const char *targetLimit; - int32_t targetLength=0; - - /* set up */ - if(sourceLength<0) { - sourceLimit=uprv_strchr(source, 0); - } else { - sourceLimit=source+sourceLength; - } - - /* if there is no input data, we're done */ - if(source==sourceLimit) { - return u_terminateChars(target, targetCapacity, 0, pErrorCode); - } - - pivot=pivot2=pivotBuffer; - myTarget=target; - targetLength=0; - - if(targetCapacity>0) { - /* perform real conversion */ - targetLimit=target+targetCapacity; - ucnv_convertEx(outConverter, inConverter, - &myTarget, targetLimit, - &source, sourceLimit, - pivotBuffer, &pivot, &pivot2, pivotBuffer+CHUNK_SIZE, - false, - true, - pErrorCode); - targetLength=(int32_t)(myTarget-target); - } - - /* - * If the output buffer is exhausted (or we are only "preflighting"), we need to stop writing - * to it but continue the conversion in order to store in targetCapacity - * the number of bytes that was required. - */ - if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR || targetCapacity==0) - { - char targetBuffer[CHUNK_SIZE]; - - targetLimit=targetBuffer+CHUNK_SIZE; - do { - *pErrorCode=U_ZERO_ERROR; - myTarget=targetBuffer; - ucnv_convertEx(outConverter, inConverter, - &myTarget, targetLimit, - &source, sourceLimit, - pivotBuffer, &pivot, &pivot2, pivotBuffer+CHUNK_SIZE, - false, - true, - pErrorCode); - targetLength+=(int32_t)(myTarget-targetBuffer); - } while(*pErrorCode==U_BUFFER_OVERFLOW_ERROR); - - /* done with preflighting, set warnings and errors as appropriate */ - return u_terminateChars(target, targetCapacity, targetLength, pErrorCode); - } - - /* no need to call u_terminateChars() because ucnv_convertEx() took care of that */ - return targetLength; -} - -U_CAPI int32_t U_EXPORT2 -ucnv_convert(const char *toConverterName, const char *fromConverterName, - char *target, int32_t targetCapacity, - const char *source, int32_t sourceLength, - UErrorCode *pErrorCode) { - UConverter in, out; /* stack-allocated */ - UConverter *inConverter, *outConverter; - int32_t targetLength; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - if( source==NULL || sourceLength<-1 || - targetCapacity<0 || (targetCapacity>0 && target==NULL) - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* if there is no input data, we're done */ - if(sourceLength==0 || (sourceLength<0 && *source==0)) { - return u_terminateChars(target, targetCapacity, 0, pErrorCode); - } - - /* create the converters */ - inConverter=ucnv_createConverter(&in, fromConverterName, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return 0; - } - - outConverter=ucnv_createConverter(&out, toConverterName, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - ucnv_close(inConverter); - return 0; - } - - targetLength=ucnv_internalConvert(outConverter, inConverter, - target, targetCapacity, - source, sourceLength, - pErrorCode); - - ucnv_close(inConverter); - ucnv_close(outConverter); - - return targetLength; -} - -/* @internal */ -static int32_t -ucnv_convertAlgorithmic(UBool convertToAlgorithmic, - UConverterType algorithmicType, - UConverter *cnv, - char *target, int32_t targetCapacity, - const char *source, int32_t sourceLength, - UErrorCode *pErrorCode) { - UConverter algoConverterStatic; /* stack-allocated */ - UConverter *algoConverter, *to, *from; - int32_t targetLength; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - if( cnv==NULL || source==NULL || sourceLength<-1 || - targetCapacity<0 || (targetCapacity>0 && target==NULL) - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* if there is no input data, we're done */ - if(sourceLength==0 || (sourceLength<0 && *source==0)) { - return u_terminateChars(target, targetCapacity, 0, pErrorCode); - } - - /* create the algorithmic converter */ - algoConverter=ucnv_createAlgorithmicConverter(&algoConverterStatic, algorithmicType, - "", 0, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return 0; - } - - /* reset the other converter */ - if(convertToAlgorithmic) { - /* cnv->Unicode->algo */ - ucnv_resetToUnicode(cnv); - to=algoConverter; - from=cnv; - } else { - /* algo->Unicode->cnv */ - ucnv_resetFromUnicode(cnv); - from=algoConverter; - to=cnv; - } - - targetLength=ucnv_internalConvert(to, from, - target, targetCapacity, - source, sourceLength, - pErrorCode); - - ucnv_close(algoConverter); - - return targetLength; -} - -U_CAPI int32_t U_EXPORT2 -ucnv_toAlgorithmic(UConverterType algorithmicType, - UConverter *cnv, - char *target, int32_t targetCapacity, - const char *source, int32_t sourceLength, - UErrorCode *pErrorCode) { - return ucnv_convertAlgorithmic(true, algorithmicType, cnv, - target, targetCapacity, - source, sourceLength, - pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -ucnv_fromAlgorithmic(UConverter *cnv, - UConverterType algorithmicType, - char *target, int32_t targetCapacity, - const char *source, int32_t sourceLength, - UErrorCode *pErrorCode) { - return ucnv_convertAlgorithmic(false, algorithmicType, cnv, - target, targetCapacity, - source, sourceLength, - pErrorCode); -} - -U_CAPI UConverterType U_EXPORT2 -ucnv_getType(const UConverter* converter) -{ - int8_t type = converter->sharedData->staticData->conversionType; -#if !UCONFIG_NO_LEGACY_CONVERSION - if(type == UCNV_MBCS) { - return ucnv_MBCSGetType(converter); - } -#endif - return (UConverterType)type; -} - -U_CAPI void U_EXPORT2 -ucnv_getStarters(const UConverter* converter, - UBool starters[256], - UErrorCode* err) -{ - if (err == NULL || U_FAILURE(*err)) { - return; - } - - if(converter->sharedData->impl->getStarters != NULL) { - converter->sharedData->impl->getStarters(converter, starters, err); - } else { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } -} - -static const UAmbiguousConverter *ucnv_getAmbiguous(const UConverter *cnv) -{ - UErrorCode errorCode; - const char *name; - int32_t i; - - if(cnv==NULL) { - return NULL; - } - - errorCode=U_ZERO_ERROR; - name=ucnv_getName(cnv, &errorCode); - if(U_FAILURE(errorCode)) { - return NULL; - } - - for(i=0; ivariant5c; - for(i=0; iuseFallback = usesFallback; -} - -U_CAPI UBool U_EXPORT2 -ucnv_usesFallback(const UConverter *cnv) -{ - return cnv->useFallback; -} - -U_CAPI void U_EXPORT2 -ucnv_getInvalidChars (const UConverter * converter, - char *errBytes, - int8_t * len, - UErrorCode * err) -{ - if (err == NULL || U_FAILURE(*err)) - { - return; - } - if (len == NULL || errBytes == NULL || converter == NULL) - { - *err = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (*len < converter->invalidCharLength) - { - *err = U_INDEX_OUTOFBOUNDS_ERROR; - return; - } - if ((*len = converter->invalidCharLength) > 0) - { - uprv_memcpy (errBytes, converter->invalidCharBuffer, *len); - } -} - -U_CAPI void U_EXPORT2 -ucnv_getInvalidUChars (const UConverter * converter, - UChar *errChars, - int8_t * len, - UErrorCode * err) -{ - if (err == NULL || U_FAILURE(*err)) - { - return; - } - if (len == NULL || errChars == NULL || converter == NULL) - { - *err = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (*len < converter->invalidUCharLength) - { - *err = U_INDEX_OUTOFBOUNDS_ERROR; - return; - } - if ((*len = converter->invalidUCharLength) > 0) - { - u_memcpy (errChars, converter->invalidUCharBuffer, *len); - } -} - -#define SIG_MAX_LEN 5 - -U_CAPI const char* U_EXPORT2 -ucnv_detectUnicodeSignature( const char* source, - int32_t sourceLength, - int32_t* signatureLength, - UErrorCode* pErrorCode) { - int32_t dummy; - - /* initial 0xa5 bytes: make sure that if we read preFromUFirstCP >= 0){ - return U16_LENGTH(cnv->preFromUFirstCP)+cnv->preFromULength ; - }else if(cnv->preFromULength < 0){ - return -cnv->preFromULength ; - }else if(cnv->fromUChar32 > 0){ - return 1; - } - return 0; - -} - -U_CAPI int32_t U_EXPORT2 -ucnv_toUCountPending(const UConverter* cnv, UErrorCode* status){ - - if(status == NULL || U_FAILURE(*status)){ - return -1; - } - if(cnv == NULL){ - *status = U_ILLEGAL_ARGUMENT_ERROR; - return -1; - } - - if(cnv->preToULength > 0){ - return cnv->preToULength ; - }else if(cnv->preToULength < 0){ - return -cnv->preToULength; - }else if(cnv->toULength > 0){ - return cnv->toULength; - } - return 0; -} - -U_CAPI UBool U_EXPORT2 -ucnv_isFixedWidth(UConverter *cnv, UErrorCode *status){ - if (U_FAILURE(*status)) { - return false; - } - - if (cnv == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - - switch (ucnv_getType(cnv)) { - case UCNV_SBCS: - case UCNV_DBCS: - case UCNV_UTF32_BigEndian: - case UCNV_UTF32_LittleEndian: - case UCNV_UTF32: - case UCNV_US_ASCII: - return true; - default: - return false; - } -} -#endif - -/* - * Hey, Emacs, please set the following: - * - * Local Variables: - * indent-tabs-mode: nil - * End: - * - */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1998-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* ucnv.c: +* Implements APIs for the ICU's codeset conversion library; +* mostly calls through internal functions; +* created by Bertrand A. Damiba +* +* Modification History: +* +* Date Name Description +* 04/04/99 helena Fixed internal header inclusion. +* 05/09/00 helena Added implementation to handle fallback mappings. +* 06/20/2000 helena OS/400 port changes; mostly typecast. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include + +#include "unicode/ustring.h" +#include "unicode/ucnv.h" +#include "unicode/ucnv_err.h" +#include "unicode/uset.h" +#include "unicode/utf.h" +#include "unicode/utf16.h" +#include "putilimp.h" +#include "cmemory.h" +#include "cstring.h" +#include "uassert.h" +#include "utracimp.h" +#include "ustr_imp.h" +#include "ucnv_imp.h" +#include "ucnv_cnv.h" +#include "ucnv_bld.h" + +/* size of intermediate and preflighting buffers in ucnv_convert() */ +#define CHUNK_SIZE 1024 + +typedef struct UAmbiguousConverter { + const char *name; + const char16_t variant5c; +} UAmbiguousConverter; + +static const UAmbiguousConverter ambiguousConverters[]={ + { "ibm-897_P100-1995", 0xa5 }, + { "ibm-942_P120-1999", 0xa5 }, + { "ibm-943_P130-1999", 0xa5 }, + { "ibm-946_P100-1995", 0xa5 }, + { "ibm-33722_P120-1999", 0xa5 }, + { "ibm-1041_P100-1995", 0xa5 }, + /*{ "ibm-54191_P100-2006", 0xa5 },*/ + /*{ "ibm-62383_P100-2007", 0xa5 },*/ + /*{ "ibm-891_P100-1995", 0x20a9 },*/ + { "ibm-944_P100-1995", 0x20a9 }, + { "ibm-949_P110-1999", 0x20a9 }, + { "ibm-1363_P110-1997", 0x20a9 }, + { "ISO_2022,locale=ko,version=0", 0x20a9 }, + { "ibm-1088_P100-1995", 0x20a9 } +}; + +/*Calls through createConverter */ +U_CAPI UConverter* U_EXPORT2 +ucnv_open (const char *name, + UErrorCode * err) +{ + UConverter *r; + + if (err == nullptr || U_FAILURE (*err)) { + return nullptr; + } + + r = ucnv_createConverter(nullptr, name, err); + return r; +} + +U_CAPI UConverter* U_EXPORT2 +ucnv_openPackage (const char *packageName, const char *converterName, UErrorCode * err) +{ + return ucnv_createConverterFromPackage(packageName, converterName, err); +} + +/*Extracts the char16_t* to a char* and calls through createConverter */ +U_CAPI UConverter* U_EXPORT2 +ucnv_openU (const char16_t * name, + UErrorCode * err) +{ + char asciiName[UCNV_MAX_CONVERTER_NAME_LENGTH]; + + if (err == nullptr || U_FAILURE(*err)) + return nullptr; + if (name == nullptr) + return ucnv_open (nullptr, err); + if (u_strlen(name) >= UCNV_MAX_CONVERTER_NAME_LENGTH) + { + *err = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + return ucnv_open(u_austrcpy(asciiName, name), err); +} + +/* Copy the string that is represented by the UConverterPlatform enum + * @param platformString An output buffer + * @param platform An enum representing a platform + * @return the length of the copied string. + */ +static int32_t +ucnv_copyPlatformString(char *platformString, UConverterPlatform pltfrm) +{ + switch (pltfrm) + { + case UCNV_IBM: + uprv_strcpy(platformString, "ibm-"); + return 4; + case UCNV_UNKNOWN: + break; + } + + /* default to empty string */ + *platformString = 0; + return 0; +} + +/*Assumes a $platform-#codepage.$CONVERTER_FILE_EXTENSION scheme and calls + *through createConverter*/ +U_CAPI UConverter* U_EXPORT2 +ucnv_openCCSID (int32_t codepage, + UConverterPlatform platform, + UErrorCode * err) +{ + char myName[UCNV_MAX_CONVERTER_NAME_LENGTH]; + int32_t myNameLen; + + if (err == nullptr || U_FAILURE (*err)) + return nullptr; + + /* ucnv_copyPlatformString could return "ibm-" or "cp" */ + myNameLen = ucnv_copyPlatformString(myName, platform); + T_CString_integerToString(myName + myNameLen, codepage, 10); + + return ucnv_createConverter(nullptr, myName, err); +} + +/* Creating a temporary stack-based object that can be used in one thread, +and created from a converter that is shared across threads. +*/ + +U_CAPI UConverter* U_EXPORT2 +ucnv_safeClone(const UConverter* cnv, void *stackBuffer, int32_t *pBufferSize, UErrorCode *status) +{ + UConverter *localConverter, *allocatedConverter; + int32_t stackBufferSize; + int32_t bufferSizeNeeded; + UErrorCode cbErr; + UConverterToUnicodeArgs toUArgs = { + sizeof(UConverterToUnicodeArgs), + true, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }; + UConverterFromUnicodeArgs fromUArgs = { + sizeof(UConverterFromUnicodeArgs), + true, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }; + + UTRACE_ENTRY_OC(UTRACE_UCNV_CLONE); + + if (status == nullptr || U_FAILURE(*status)){ + UTRACE_EXIT_STATUS(status? *status: U_ILLEGAL_ARGUMENT_ERROR); + return nullptr; + } + + if (cnv == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + UTRACE_EXIT_STATUS(*status); + return nullptr; + } + + UTRACE_DATA3(UTRACE_OPEN_CLOSE, "clone converter %s at %p into stackBuffer %p", + ucnv_getName(cnv, status), cnv, stackBuffer); + + if (cnv->sharedData->impl->safeClone != nullptr) { + /* call the custom safeClone function for sizing */ + bufferSizeNeeded = 0; + cnv->sharedData->impl->safeClone(cnv, nullptr, &bufferSizeNeeded, status); + if (U_FAILURE(*status)) { + UTRACE_EXIT_STATUS(*status); + return nullptr; + } + } + else + { + /* inherent sizing */ + bufferSizeNeeded = sizeof(UConverter); + } + + if (pBufferSize == nullptr) { + stackBufferSize = 1; + pBufferSize = &stackBufferSize; + } else { + stackBufferSize = *pBufferSize; + if (stackBufferSize <= 0){ /* 'preflighting' request - set needed size into *pBufferSize */ + *pBufferSize = bufferSizeNeeded; + UTRACE_EXIT_VALUE(bufferSizeNeeded); + return nullptr; + } + } + + /* Adjust (if necessary) the stackBuffer pointer to be aligned correctly for a UConverter. + * TODO(Jira ICU-20736) Redo this using std::align() once g++4.9 compatibility is no longer needed. + */ + if (stackBuffer) { + uintptr_t p = reinterpret_cast(stackBuffer); + uintptr_t aligned_p = (p + alignof(UConverter) - 1) & ~(alignof(UConverter) - 1); + ptrdiff_t pointerAdjustment = aligned_p - p; + if (bufferSizeNeeded + pointerAdjustment <= stackBufferSize) { + stackBuffer = reinterpret_cast(aligned_p); + stackBufferSize -= static_cast(pointerAdjustment); + } else { + /* prevent using the stack buffer but keep the size > 0 so that we do not just preflight */ + stackBufferSize = 1; + } + } + + /* Now, see if we must allocate any memory */ + if (stackBufferSize < bufferSizeNeeded || stackBuffer == nullptr) + { + /* allocate one here...*/ + localConverter = allocatedConverter = (UConverter *) uprv_malloc (bufferSizeNeeded); + + if(localConverter == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + UTRACE_EXIT_STATUS(*status); + return nullptr; + } + // If pBufferSize was nullptr as the input, pBufferSize is set to &stackBufferSize in this function. + if (pBufferSize != &stackBufferSize) { + *status = U_SAFECLONE_ALLOCATED_WARNING; + } + + /* record the fact that memory was allocated */ + *pBufferSize = bufferSizeNeeded; + } else { + /* just use the stack buffer */ + localConverter = (UConverter*) stackBuffer; + allocatedConverter = nullptr; + } + + uprv_memset(localConverter, 0, bufferSizeNeeded); + + /* Copy initial state */ + uprv_memcpy(localConverter, cnv, sizeof(UConverter)); + localConverter->isCopyLocal = localConverter->isExtraLocal = false; + + /* copy the substitution string */ + if (cnv->subChars == (uint8_t *)cnv->subUChars) { + localConverter->subChars = (uint8_t *)localConverter->subUChars; + } else { + localConverter->subChars = (uint8_t *)uprv_malloc(UCNV_ERROR_BUFFER_LENGTH * U_SIZEOF_UCHAR); + if (localConverter->subChars == nullptr) { + uprv_free(allocatedConverter); + UTRACE_EXIT_STATUS(*status); + return nullptr; + } + uprv_memcpy(localConverter->subChars, cnv->subChars, UCNV_ERROR_BUFFER_LENGTH * U_SIZEOF_UCHAR); + } + + /* now either call the safeclone fcn or not */ + if (cnv->sharedData->impl->safeClone != nullptr) { + /* call the custom safeClone function */ + localConverter = cnv->sharedData->impl->safeClone(cnv, localConverter, pBufferSize, status); + } + + if(localConverter==nullptr || U_FAILURE(*status)) { + if (allocatedConverter != nullptr && allocatedConverter->subChars != (uint8_t *)allocatedConverter->subUChars) { + uprv_free(allocatedConverter->subChars); + } + uprv_free(allocatedConverter); + UTRACE_EXIT_STATUS(*status); + return nullptr; + } + + /* increment refcount of shared data if needed */ + if (cnv->sharedData->isReferenceCounted) { + ucnv_incrementRefCount(cnv->sharedData); + } + + if(localConverter == (UConverter*)stackBuffer) { + /* we're using user provided data - set to not destroy */ + localConverter->isCopyLocal = true; + } + + /* allow callback functions to handle any memory allocation */ + toUArgs.converter = fromUArgs.converter = localConverter; + cbErr = U_ZERO_ERROR; + cnv->fromCharErrorBehaviour(cnv->toUContext, &toUArgs, nullptr, 0, UCNV_CLONE, &cbErr); + cbErr = U_ZERO_ERROR; + cnv->fromUCharErrorBehaviour(cnv->fromUContext, &fromUArgs, nullptr, 0, 0, UCNV_CLONE, &cbErr); + + UTRACE_EXIT_PTR_STATUS(localConverter, *status); + return localConverter; +} + +U_CAPI UConverter* U_EXPORT2 +ucnv_clone(const UConverter* cnv, UErrorCode *status) +{ + return ucnv_safeClone(cnv, nullptr, nullptr, status); +} + +/*Decreases the reference counter in the shared immutable section of the object + *and frees the mutable part*/ + +U_CAPI void U_EXPORT2 +ucnv_close (UConverter * converter) +{ + UErrorCode errorCode = U_ZERO_ERROR; + + UTRACE_ENTRY_OC(UTRACE_UCNV_CLOSE); + + if (converter == nullptr) + { + UTRACE_EXIT(); + return; + } + + UTRACE_DATA3(UTRACE_OPEN_CLOSE, "close converter %s at %p, isCopyLocal=%b", + ucnv_getName(converter, &errorCode), converter, converter->isCopyLocal); + + /* In order to speed up the close, only call the callbacks when they have been changed. + This performance check will only work when the callbacks are set within a shared library + or from user code that statically links this code. */ + /* first, notify the callback functions that the converter is closed */ + if (converter->fromCharErrorBehaviour != UCNV_TO_U_DEFAULT_CALLBACK) { + UConverterToUnicodeArgs toUArgs = { + sizeof(UConverterToUnicodeArgs), + true, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }; + + toUArgs.converter = converter; + errorCode = U_ZERO_ERROR; + converter->fromCharErrorBehaviour(converter->toUContext, &toUArgs, nullptr, 0, UCNV_CLOSE, &errorCode); + } + if (converter->fromUCharErrorBehaviour != UCNV_FROM_U_DEFAULT_CALLBACK) { + UConverterFromUnicodeArgs fromUArgs = { + sizeof(UConverterFromUnicodeArgs), + true, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }; + fromUArgs.converter = converter; + errorCode = U_ZERO_ERROR; + converter->fromUCharErrorBehaviour(converter->fromUContext, &fromUArgs, nullptr, 0, 0, UCNV_CLOSE, &errorCode); + } + + if (converter->sharedData->impl->close != nullptr) { + converter->sharedData->impl->close(converter); + } + + if (converter->subChars != (uint8_t *)converter->subUChars) { + uprv_free(converter->subChars); + } + + if (converter->sharedData->isReferenceCounted) { + ucnv_unloadSharedDataIfReady(converter->sharedData); + } + + if(!converter->isCopyLocal){ + uprv_free(converter); + } + + UTRACE_EXIT(); +} + +/*returns a single Name from the list, will return nullptr if out of bounds + */ +U_CAPI const char* U_EXPORT2 +ucnv_getAvailableName (int32_t n) +{ + if (0 <= n && n <= 0xffff) { + UErrorCode err = U_ZERO_ERROR; + const char *name = ucnv_bld_getAvailableConverter((uint16_t)n, &err); + if (U_SUCCESS(err)) { + return name; + } + } + return nullptr; +} + +U_CAPI int32_t U_EXPORT2 +ucnv_countAvailable () +{ + UErrorCode err = U_ZERO_ERROR; + return ucnv_bld_countAvailableConverters(&err); +} + +U_CAPI void U_EXPORT2 +ucnv_getSubstChars (const UConverter * converter, + char *mySubChar, + int8_t * len, + UErrorCode * err) +{ + if (U_FAILURE (*err)) + return; + + if (converter->subCharLen <= 0) { + /* Unicode string or empty string from ucnv_setSubstString(). */ + *len = 0; + return; + } + + if (*len < converter->subCharLen) /*not enough space in subChars */ + { + *err = U_INDEX_OUTOFBOUNDS_ERROR; + return; + } + + uprv_memcpy (mySubChar, converter->subChars, converter->subCharLen); /*fills in the subchars */ + *len = converter->subCharLen; /*store # of bytes copied to buffer */ +} + +U_CAPI void U_EXPORT2 +ucnv_setSubstChars (UConverter * converter, + const char *mySubChar, + int8_t len, + UErrorCode * err) +{ + if (U_FAILURE (*err)) + return; + + /*Makes sure that the subChar is within the codepages char length boundaries */ + if ((len > converter->sharedData->staticData->maxBytesPerChar) + || (len < converter->sharedData->staticData->minBytesPerChar)) + { + *err = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + uprv_memcpy (converter->subChars, mySubChar, len); /*copies the subchars */ + converter->subCharLen = len; /*sets the new len */ + + /* + * There is currently (2001Feb) no separate API to set/get subChar1. + * In order to always have subChar written after it is explicitly set, + * we set subChar1 to 0. + */ + converter->subChar1 = 0; + + return; +} + +U_CAPI void U_EXPORT2 +ucnv_setSubstString(UConverter *cnv, + const char16_t *s, + int32_t length, + UErrorCode *err) { + alignas(UConverter) char cloneBuffer[U_CNV_SAFECLONE_BUFFERSIZE]; + char chars[UCNV_ERROR_BUFFER_LENGTH]; + + UConverter *clone; + uint8_t *subChars; + int32_t cloneSize, length8; + + /* Let the following functions check all arguments. */ + cloneSize = sizeof(cloneBuffer); + clone = ucnv_safeClone(cnv, cloneBuffer, &cloneSize, err); + ucnv_setFromUCallBack(clone, UCNV_FROM_U_CALLBACK_STOP, nullptr, nullptr, nullptr, err); + length8 = ucnv_fromUChars(clone, chars, (int32_t)sizeof(chars), s, length, err); + ucnv_close(clone); + if (U_FAILURE(*err)) { + return; + } + + if (cnv->sharedData->impl->writeSub == nullptr +#if !UCONFIG_NO_LEGACY_CONVERSION + || (cnv->sharedData->staticData->conversionType == UCNV_MBCS && + ucnv_MBCSGetType(cnv) != UCNV_EBCDIC_STATEFUL) +#endif + ) { + /* The converter is not stateful. Store the charset bytes as a fixed string. */ + subChars = (uint8_t *)chars; + } else { + /* + * The converter has a non-default writeSub() function, indicating + * that it is stateful. + * Store the Unicode string for on-the-fly conversion for correct + * state handling. + */ + if (length > UCNV_ERROR_BUFFER_LENGTH) { + /* + * Should not occur. The converter should output at least one byte + * per char16_t, which means that ucnv_fromUChars() should catch all + * overflows. + */ + *err = U_BUFFER_OVERFLOW_ERROR; + return; + } + subChars = (uint8_t *)s; + if (length < 0) { + length = u_strlen(s); + } + length8 = length * U_SIZEOF_UCHAR; + } + + /* + * For storing the substitution string, select either the small buffer inside + * UConverter or allocate a subChars buffer. + */ + if (length8 > UCNV_MAX_SUBCHAR_LEN) { + /* Use a separate buffer for the string. Outside UConverter to not make it too large. */ + if (cnv->subChars == (uint8_t *)cnv->subUChars) { + /* Allocate a new buffer for the string. */ + cnv->subChars = (uint8_t *)uprv_malloc(UCNV_ERROR_BUFFER_LENGTH * U_SIZEOF_UCHAR); + if (cnv->subChars == nullptr) { + cnv->subChars = (uint8_t *)cnv->subUChars; + *err = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_memset(cnv->subChars, 0, UCNV_ERROR_BUFFER_LENGTH * U_SIZEOF_UCHAR); + } + } + + /* Copy the substitution string into the UConverter or its subChars buffer. */ + if (length8 == 0) { + cnv->subCharLen = 0; + } else { + uprv_memcpy(cnv->subChars, subChars, length8); + if (subChars == (uint8_t *)chars) { + cnv->subCharLen = (int8_t)length8; + } else /* subChars == s */ { + cnv->subCharLen = (int8_t)-length; + } + } + + /* See comment in ucnv_setSubstChars(). */ + cnv->subChar1 = 0; +} + +/*resets the internal states of a converter + *goal : have the same behaviour than a freshly created converter + */ +static void _reset(UConverter *converter, UConverterResetChoice choice, + UBool callCallback) { + if(converter == nullptr) { + return; + } + + if(callCallback) { + /* first, notify the callback functions that the converter is reset */ + UErrorCode errorCode; + + if(choice<=UCNV_RESET_TO_UNICODE && converter->fromCharErrorBehaviour != UCNV_TO_U_DEFAULT_CALLBACK) { + UConverterToUnicodeArgs toUArgs = { + sizeof(UConverterToUnicodeArgs), + true, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }; + toUArgs.converter = converter; + errorCode = U_ZERO_ERROR; + converter->fromCharErrorBehaviour(converter->toUContext, &toUArgs, nullptr, 0, UCNV_RESET, &errorCode); + } + if(choice!=UCNV_RESET_TO_UNICODE && converter->fromUCharErrorBehaviour != UCNV_FROM_U_DEFAULT_CALLBACK) { + UConverterFromUnicodeArgs fromUArgs = { + sizeof(UConverterFromUnicodeArgs), + true, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }; + fromUArgs.converter = converter; + errorCode = U_ZERO_ERROR; + converter->fromUCharErrorBehaviour(converter->fromUContext, &fromUArgs, nullptr, 0, 0, UCNV_RESET, &errorCode); + } + } + + /* now reset the converter itself */ + if(choice<=UCNV_RESET_TO_UNICODE) { + converter->toUnicodeStatus = converter->sharedData->toUnicodeStatus; + converter->mode = 0; + converter->toULength = 0; + converter->invalidCharLength = converter->UCharErrorBufferLength = 0; + converter->preToULength = 0; + } + if(choice!=UCNV_RESET_TO_UNICODE) { + converter->fromUnicodeStatus = 0; + converter->fromUChar32 = 0; + converter->invalidUCharLength = converter->charErrorBufferLength = 0; + converter->preFromUFirstCP = U_SENTINEL; + converter->preFromULength = 0; + } + + if (converter->sharedData->impl->reset != nullptr) { + /* call the custom reset function */ + converter->sharedData->impl->reset(converter, choice); + } +} + +U_CAPI void U_EXPORT2 +ucnv_reset(UConverter *converter) +{ + _reset(converter, UCNV_RESET_BOTH, true); +} + +U_CAPI void U_EXPORT2 +ucnv_resetToUnicode(UConverter *converter) +{ + _reset(converter, UCNV_RESET_TO_UNICODE, true); +} + +U_CAPI void U_EXPORT2 +ucnv_resetFromUnicode(UConverter *converter) +{ + _reset(converter, UCNV_RESET_FROM_UNICODE, true); +} + +U_CAPI int8_t U_EXPORT2 +ucnv_getMaxCharSize (const UConverter * converter) +{ + return converter->maxBytesPerUChar; +} + + +U_CAPI int8_t U_EXPORT2 +ucnv_getMinCharSize (const UConverter * converter) +{ + return converter->sharedData->staticData->minBytesPerChar; +} + +U_CAPI const char* U_EXPORT2 +ucnv_getName (const UConverter * converter, UErrorCode * err) + +{ + if (U_FAILURE (*err)) + return nullptr; + if(converter->sharedData->impl->getName){ + const char* temp= converter->sharedData->impl->getName(converter); + if(temp) + return temp; + } + return converter->sharedData->staticData->name; +} + +U_CAPI int32_t U_EXPORT2 +ucnv_getCCSID(const UConverter * converter, + UErrorCode * err) +{ + int32_t ccsid; + if (U_FAILURE (*err)) + return -1; + + ccsid = converter->sharedData->staticData->codepage; + if (ccsid == 0) { + /* Rare case. This is for cases like gb18030, + which doesn't have an IBM canonical name, but does have an IBM alias. */ + const char *standardName = ucnv_getStandardName(ucnv_getName(converter, err), "IBM", err); + if (U_SUCCESS(*err) && standardName) { + const char *ccsidStr = uprv_strchr(standardName, '-'); + if (ccsidStr) { + ccsid = (int32_t)atol(ccsidStr+1); /* +1 to skip '-' */ + } + } + } + return ccsid; +} + + +U_CAPI UConverterPlatform U_EXPORT2 +ucnv_getPlatform (const UConverter * converter, + UErrorCode * err) +{ + if (U_FAILURE (*err)) + return UCNV_UNKNOWN; + + return (UConverterPlatform)converter->sharedData->staticData->platform; +} + +U_CAPI void U_EXPORT2 + ucnv_getToUCallBack (const UConverter * converter, + UConverterToUCallback *action, + const void **context) +{ + *action = converter->fromCharErrorBehaviour; + *context = converter->toUContext; +} + +U_CAPI void U_EXPORT2 + ucnv_getFromUCallBack (const UConverter * converter, + UConverterFromUCallback *action, + const void **context) +{ + *action = converter->fromUCharErrorBehaviour; + *context = converter->fromUContext; +} + +U_CAPI void U_EXPORT2 +ucnv_setToUCallBack (UConverter * converter, + UConverterToUCallback newAction, + const void* newContext, + UConverterToUCallback *oldAction, + const void** oldContext, + UErrorCode * err) +{ + if (U_FAILURE (*err)) + return; + if (oldAction) *oldAction = converter->fromCharErrorBehaviour; + converter->fromCharErrorBehaviour = newAction; + if (oldContext) *oldContext = converter->toUContext; + converter->toUContext = newContext; +} + +U_CAPI void U_EXPORT2 +ucnv_setFromUCallBack (UConverter * converter, + UConverterFromUCallback newAction, + const void* newContext, + UConverterFromUCallback *oldAction, + const void** oldContext, + UErrorCode * err) +{ + if (U_FAILURE (*err)) + return; + if (oldAction) *oldAction = converter->fromUCharErrorBehaviour; + converter->fromUCharErrorBehaviour = newAction; + if (oldContext) *oldContext = converter->fromUContext; + converter->fromUContext = newContext; +} + +static void +_updateOffsets(int32_t *offsets, int32_t length, + int32_t sourceIndex, int32_t errorInputLength) { + int32_t *limit; + int32_t delta, offset; + + if(sourceIndex>=0) { + /* + * adjust each offset by adding the previous sourceIndex + * minus the length of the input sequence that caused an + * error, if any + */ + delta=sourceIndex-errorInputLength; + } else { + /* + * set each offset to -1 because this conversion function + * does not handle offsets + */ + delta=-1; + } + + limit=offsets+length; + if(delta==0) { + /* most common case, nothing to do */ + } else if(delta>0) { + /* add the delta to each offset (but not if the offset is <0) */ + while(offsets=0) { + *offsets=offset+delta; + } + ++offsets; + } + } else /* delta<0 */ { + /* + * set each offset to -1 because this conversion function + * does not handle offsets + * or the error input sequence started in a previous buffer + */ + while(offsetsconverter; + s=pArgs->source; + t=pArgs->target; + offsets=pArgs->offsets; + + /* get the converter implementation function */ + sourceIndex=0; + if(offsets==nullptr) { + fromUnicode=cnv->sharedData->impl->fromUnicode; + } else { + fromUnicode=cnv->sharedData->impl->fromUnicodeWithOffsets; + if(fromUnicode==nullptr) { + /* there is no WithOffsets implementation */ + fromUnicode=cnv->sharedData->impl->fromUnicode; + /* we will write -1 for each offset */ + sourceIndex=-1; + } + } + + if(cnv->preFromULength>=0) { + /* normal mode */ + realSource=nullptr; + + /* avoid compiler warnings - not otherwise necessary, and the values do not matter */ + realSourceLimit=nullptr; + realFlush=false; + realSourceIndex=0; + } else { + /* + * Previous m:n conversion stored source units from a partial match + * and failed to consume all of them. + * We need to "replay" them from a temporary buffer and convert them first. + */ + realSource=pArgs->source; + realSourceLimit=pArgs->sourceLimit; + realFlush=pArgs->flush; + realSourceIndex=sourceIndex; + + uprv_memcpy(replay, cnv->preFromU, -cnv->preFromULength*U_SIZEOF_UCHAR); + pArgs->source=replay; + pArgs->sourceLimit=replay-cnv->preFromULength; + pArgs->flush=false; + sourceIndex=-1; + + cnv->preFromULength=0; + } + + /* + * loop for conversion and error handling + * + * loop { + * convert + * loop { + * update offsets + * handle end of input + * handle errors/call callback + * } + * } + */ + for(;;) { + if(U_SUCCESS(*err)) { + /* convert */ + fromUnicode(pArgs, err); + + /* + * set a flag for whether the converter + * successfully processed the end of the input + * + * need not check cnv->preFromULength==0 because a replay (<0) will cause + * sflush && pArgs->source==pArgs->sourceLimit && + cnv->fromUChar32==0); + } else { + /* handle error from ucnv_convertEx() */ + converterSawEndOfInput=false; + } + + /* no callback called yet for this iteration */ + calledCallback=false; + + /* no sourceIndex adjustment for conversion, only for callback output */ + errorInputLength=0; + + /* + * loop for offsets and error handling + * + * iterates at most 3 times: + * 1. to clean up after the conversion function + * 2. after the callback + * 3. after the callback again if there was truncated input + */ + for(;;) { + /* update offsets if we write any */ + if(offsets!=nullptr) { + int32_t length=(int32_t)(pArgs->target-t); + if(length>0) { + _updateOffsets(offsets, length, sourceIndex, errorInputLength); + + /* + * if a converter handles offsets and updates the offsets + * pointer at the end, then pArgs->offset should not change + * here; + * however, some converters do not handle offsets at all + * (sourceIndex<0) or may not update the offsets pointer + */ + pArgs->offsets=offsets+=length; + } + + if(sourceIndex>=0) { + sourceIndex+=(int32_t)(pArgs->source-s); + } + } + + if(cnv->preFromULength<0) { + /* + * switch the source to new replay units (cannot occur while replaying) + * after offset handling and before end-of-input and callback handling + */ + if(realSource==nullptr) { + realSource=pArgs->source; + realSourceLimit=pArgs->sourceLimit; + realFlush=pArgs->flush; + realSourceIndex=sourceIndex; + + uprv_memcpy(replay, cnv->preFromU, -cnv->preFromULength*U_SIZEOF_UCHAR); + pArgs->source=replay; + pArgs->sourceLimit=replay-cnv->preFromULength; + pArgs->flush=false; + if((sourceIndex+=cnv->preFromULength)<0) { + sourceIndex=-1; + } + + cnv->preFromULength=0; + } else { + /* see implementation note before _fromUnicodeWithCallback() */ + U_ASSERT(realSource==nullptr); + *err=U_INTERNAL_PROGRAM_ERROR; + } + } + + /* update pointers */ + s=pArgs->source; + t=pArgs->target; + + if(U_SUCCESS(*err)) { + if(ssourceLimit) { + /* + * continue with the conversion loop while there is still input left + * (continue converting by breaking out of only the inner loop) + */ + break; + } else if(realSource!=nullptr) { + /* switch back from replaying to the real source and continue */ + pArgs->source=realSource; + pArgs->sourceLimit=realSourceLimit; + pArgs->flush=realFlush; + sourceIndex=realSourceIndex; + + realSource=nullptr; + break; + } else if(pArgs->flush && cnv->fromUChar32!=0) { + /* + * the entire input stream is consumed + * and there is a partial, truncated input sequence left + */ + + /* inject an error and continue with callback handling */ + *err=U_TRUNCATED_CHAR_FOUND; + calledCallback=false; /* new error condition */ + } else { + /* input consumed */ + if(pArgs->flush) { + /* + * return to the conversion loop once more if the flush + * flag is set and the conversion function has not + * successfully processed the end of the input yet + * + * (continue converting by breaking out of only the inner loop) + */ + if(!converterSawEndOfInput) { + break; + } + + /* reset the converter without calling the callback function */ + _reset(cnv, UCNV_RESET_FROM_UNICODE, false); + } + + /* done successfully */ + return; + } + } + + /* U_FAILURE(*err) */ + { + UErrorCode e; + + if( calledCallback || + (e=*err)==U_BUFFER_OVERFLOW_ERROR || + (e!=U_INVALID_CHAR_FOUND && + e!=U_ILLEGAL_CHAR_FOUND && + e!=U_TRUNCATED_CHAR_FOUND) + ) { + /* + * the callback did not or cannot resolve the error: + * set output pointers and return + * + * the check for buffer overflow is redundant but it is + * a high-runner case and hopefully documents the intent + * well + * + * if we were replaying, then the replay buffer must be + * copied back into the UConverter + * and the real arguments must be restored + */ + if(realSource!=nullptr) { + int32_t length; + + U_ASSERT(cnv->preFromULength==0); + + length=(int32_t)(pArgs->sourceLimit-pArgs->source); + if(length>0) { + u_memcpy(cnv->preFromU, pArgs->source, length); + cnv->preFromULength=(int8_t)-length; + } + + pArgs->source=realSource; + pArgs->sourceLimit=realSourceLimit; + pArgs->flush=realFlush; + } + + return; + } + } + + /* callback handling */ + { + UChar32 codePoint; + + /* get and write the code point */ + codePoint=cnv->fromUChar32; + errorInputLength=0; + U16_APPEND_UNSAFE(cnv->invalidUCharBuffer, errorInputLength, codePoint); + cnv->invalidUCharLength=(int8_t)errorInputLength; + + /* set the converter state to deal with the next character */ + cnv->fromUChar32=0; + + /* call the callback function */ + cnv->fromUCharErrorBehaviour(cnv->fromUContext, pArgs, + cnv->invalidUCharBuffer, errorInputLength, codePoint, + *err==U_INVALID_CHAR_FOUND ? UCNV_UNASSIGNED : UCNV_ILLEGAL, + err); + } + + /* + * loop back to the offset handling + * + * this flag will indicate after offset handling + * that a callback was called; + * if the callback did not resolve the error, then we return + */ + calledCallback=true; + } + } +} + +/* + * Output the fromUnicode overflow buffer. + * Call this function if(cnv->charErrorBufferLength>0). + * @return true if overflow + */ +static UBool +ucnv_outputOverflowFromUnicode(UConverter *cnv, + char **target, const char *targetLimit, + int32_t **pOffsets, + UErrorCode *err) { + int32_t *offsets; + char *overflow, *t; + int32_t i, length; + + t=*target; + if(pOffsets!=nullptr) { + offsets=*pOffsets; + } else { + offsets=nullptr; + } + + overflow=(char *)cnv->charErrorBuffer; + length=cnv->charErrorBufferLength; + i=0; + while(icharErrorBufferLength=(int8_t)j; + *target=t; + if(offsets!=nullptr) { + *pOffsets=offsets; + } + *err=U_BUFFER_OVERFLOW_ERROR; + return true; + } + + /* copy the overflow contents to the target */ + *t++=overflow[i++]; + if(offsets!=nullptr) { + *offsets++=-1; /* no source index available for old output */ + } + } + + /* the overflow buffer is completely copied to the target */ + cnv->charErrorBufferLength=0; + *target=t; + if(offsets!=nullptr) { + *pOffsets=offsets; + } + return false; +} + +U_CAPI void U_EXPORT2 +ucnv_fromUnicode(UConverter *cnv, + char **target, const char *targetLimit, + const char16_t **source, const char16_t *sourceLimit, + int32_t *offsets, + UBool flush, + UErrorCode *err) { + UConverterFromUnicodeArgs args; + const char16_t *s; + char *t; + + /* check parameters */ + if(err==nullptr || U_FAILURE(*err)) { + return; + } + + if(cnv==nullptr || target==nullptr || source==nullptr) { + *err=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + s=*source; + t=*target; + + if ((const void *)U_MAX_PTR(sourceLimit) == (const void *)sourceLimit) { + /* + Prevent code from going into an infinite loop in case we do hit this + limit. The limit pointer is expected to be on a char16_t * boundary. + This also prevents the next argument check from failing. + */ + sourceLimit = (const char16_t *)(((const char *)sourceLimit) - 1); + } + + /* + * All these conditions should never happen. + * + * 1) Make sure that the limits are >= to the address source or target + * + * 2) Make sure that the buffer sizes do not exceed the number range for + * int32_t because some functions use the size (in units or bytes) + * rather than comparing pointers, and because offsets are int32_t values. + * + * size_t is guaranteed to be unsigned and large enough for the job. + * + * Return with an error instead of adjusting the limits because we would + * not be able to maintain the semantics that either the source must be + * consumed or the target filled (unless an error occurs). + * An adjustment would be targetLimit=t+0x7fffffff; for example. + * + * 3) Make sure that the user didn't incorrectly cast a char16_t * pointer + * to a char * pointer and provide an incomplete char16_t code unit. + */ + if (sourceLimit(size_t)0x3fffffff && sourceLimit>s) || + ((size_t)(targetLimit-t)>(size_t)0x7fffffff && targetLimit>t) || + (((const char *)sourceLimit-(const char *)s) & 1) != 0) + { + *err=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + /* output the target overflow buffer */ + if( cnv->charErrorBufferLength>0 && + ucnv_outputOverflowFromUnicode(cnv, target, targetLimit, &offsets, err) + ) { + /* U_BUFFER_OVERFLOW_ERROR */ + return; + } + /* *target may have moved, therefore stop using t */ + + if(!flush && s==sourceLimit && cnv->preFromULength>=0) { + /* the overflow buffer is emptied and there is no new input: we are done */ + return; + } + + /* + * Do not simply return with a buffer overflow error if + * !flush && t==targetLimit + * because it is possible that the source will not generate any output. + * For example, the skip callback may be called; + * it does not output anything. + */ + + /* prepare the converter arguments */ + args.converter=cnv; + args.flush=flush; + args.offsets=offsets; + args.source=s; + args.sourceLimit=sourceLimit; + args.target=*target; + args.targetLimit=targetLimit; + args.size=sizeof(args); + + _fromUnicodeWithCallback(&args, err); + + *source=args.source; + *target=args.target; +} + +/* ucnv_toUnicode() --------------------------------------------------------- */ + +static void +_toUnicodeWithCallback(UConverterToUnicodeArgs *pArgs, UErrorCode *err) { + UConverterToUnicode toUnicode; + UConverter *cnv; + const char *s; + char16_t *t; + int32_t *offsets; + int32_t sourceIndex; + int32_t errorInputLength; + UBool converterSawEndOfInput, calledCallback; + + /* variables for m:n conversion */ + char replay[UCNV_EXT_MAX_BYTES]; + const char *realSource, *realSourceLimit; + int32_t realSourceIndex; + UBool realFlush; + + cnv=pArgs->converter; + s=pArgs->source; + t=pArgs->target; + offsets=pArgs->offsets; + + /* get the converter implementation function */ + sourceIndex=0; + if(offsets==nullptr) { + toUnicode=cnv->sharedData->impl->toUnicode; + } else { + toUnicode=cnv->sharedData->impl->toUnicodeWithOffsets; + if(toUnicode==nullptr) { + /* there is no WithOffsets implementation */ + toUnicode=cnv->sharedData->impl->toUnicode; + /* we will write -1 for each offset */ + sourceIndex=-1; + } + } + + if(cnv->preToULength>=0) { + /* normal mode */ + realSource=nullptr; + + /* avoid compiler warnings - not otherwise necessary, and the values do not matter */ + realSourceLimit=nullptr; + realFlush=false; + realSourceIndex=0; + } else { + /* + * Previous m:n conversion stored source units from a partial match + * and failed to consume all of them. + * We need to "replay" them from a temporary buffer and convert them first. + */ + realSource=pArgs->source; + realSourceLimit=pArgs->sourceLimit; + realFlush=pArgs->flush; + realSourceIndex=sourceIndex; + + uprv_memcpy(replay, cnv->preToU, -cnv->preToULength); + pArgs->source=replay; + pArgs->sourceLimit=replay-cnv->preToULength; + pArgs->flush=false; + sourceIndex=-1; + + cnv->preToULength=0; + } + + /* + * loop for conversion and error handling + * + * loop { + * convert + * loop { + * update offsets + * handle end of input + * handle errors/call callback + * } + * } + */ + for(;;) { + if(U_SUCCESS(*err)) { + /* convert */ + toUnicode(pArgs, err); + + /* + * set a flag for whether the converter + * successfully processed the end of the input + * + * need not check cnv->preToULength==0 because a replay (<0) will cause + * sflush && pArgs->source==pArgs->sourceLimit && + cnv->toULength==0); + } else { + /* handle error from getNextUChar() or ucnv_convertEx() */ + converterSawEndOfInput=false; + } + + /* no callback called yet for this iteration */ + calledCallback=false; + + /* no sourceIndex adjustment for conversion, only for callback output */ + errorInputLength=0; + + /* + * loop for offsets and error handling + * + * iterates at most 3 times: + * 1. to clean up after the conversion function + * 2. after the callback + * 3. after the callback again if there was truncated input + */ + for(;;) { + /* update offsets if we write any */ + if(offsets!=nullptr) { + int32_t length=(int32_t)(pArgs->target-t); + if(length>0) { + _updateOffsets(offsets, length, sourceIndex, errorInputLength); + + /* + * if a converter handles offsets and updates the offsets + * pointer at the end, then pArgs->offset should not change + * here; + * however, some converters do not handle offsets at all + * (sourceIndex<0) or may not update the offsets pointer + */ + pArgs->offsets=offsets+=length; + } + + if(sourceIndex>=0) { + sourceIndex+=(int32_t)(pArgs->source-s); + } + } + + if(cnv->preToULength<0) { + /* + * switch the source to new replay units (cannot occur while replaying) + * after offset handling and before end-of-input and callback handling + */ + if(realSource==nullptr) { + realSource=pArgs->source; + realSourceLimit=pArgs->sourceLimit; + realFlush=pArgs->flush; + realSourceIndex=sourceIndex; + + uprv_memcpy(replay, cnv->preToU, -cnv->preToULength); + pArgs->source=replay; + pArgs->sourceLimit=replay-cnv->preToULength; + pArgs->flush=false; + if((sourceIndex+=cnv->preToULength)<0) { + sourceIndex=-1; + } + + cnv->preToULength=0; + } else { + /* see implementation note before _fromUnicodeWithCallback() */ + U_ASSERT(realSource==nullptr); + *err=U_INTERNAL_PROGRAM_ERROR; + } + } + + /* update pointers */ + s=pArgs->source; + t=pArgs->target; + + if(U_SUCCESS(*err)) { + if(ssourceLimit) { + /* + * continue with the conversion loop while there is still input left + * (continue converting by breaking out of only the inner loop) + */ + break; + } else if(realSource!=nullptr) { + /* switch back from replaying to the real source and continue */ + pArgs->source=realSource; + pArgs->sourceLimit=realSourceLimit; + pArgs->flush=realFlush; + sourceIndex=realSourceIndex; + + realSource=nullptr; + break; + } else if(pArgs->flush && cnv->toULength>0) { + /* + * the entire input stream is consumed + * and there is a partial, truncated input sequence left + */ + + /* inject an error and continue with callback handling */ + *err=U_TRUNCATED_CHAR_FOUND; + calledCallback=false; /* new error condition */ + } else { + /* input consumed */ + if(pArgs->flush) { + /* + * return to the conversion loop once more if the flush + * flag is set and the conversion function has not + * successfully processed the end of the input yet + * + * (continue converting by breaking out of only the inner loop) + */ + if(!converterSawEndOfInput) { + break; + } + + /* reset the converter without calling the callback function */ + _reset(cnv, UCNV_RESET_TO_UNICODE, false); + } + + /* done successfully */ + return; + } + } + + /* U_FAILURE(*err) */ + { + UErrorCode e; + + if( calledCallback || + (e=*err)==U_BUFFER_OVERFLOW_ERROR || + (e!=U_INVALID_CHAR_FOUND && + e!=U_ILLEGAL_CHAR_FOUND && + e!=U_TRUNCATED_CHAR_FOUND && + e!=U_ILLEGAL_ESCAPE_SEQUENCE && + e!=U_UNSUPPORTED_ESCAPE_SEQUENCE) + ) { + /* + * the callback did not or cannot resolve the error: + * set output pointers and return + * + * the check for buffer overflow is redundant but it is + * a high-runner case and hopefully documents the intent + * well + * + * if we were replaying, then the replay buffer must be + * copied back into the UConverter + * and the real arguments must be restored + */ + if(realSource!=nullptr) { + int32_t length; + + U_ASSERT(cnv->preToULength==0); + + length=(int32_t)(pArgs->sourceLimit-pArgs->source); + if(length>0) { + uprv_memcpy(cnv->preToU, pArgs->source, length); + cnv->preToULength=(int8_t)-length; + } + + pArgs->source=realSource; + pArgs->sourceLimit=realSourceLimit; + pArgs->flush=realFlush; + } + + return; + } + } + + /* copy toUBytes[] to invalidCharBuffer[] */ + errorInputLength=cnv->invalidCharLength=cnv->toULength; + if(errorInputLength>0) { + uprv_memcpy(cnv->invalidCharBuffer, cnv->toUBytes, errorInputLength); + } + + /* set the converter state to deal with the next character */ + cnv->toULength=0; + + /* call the callback function */ + if(cnv->toUCallbackReason==UCNV_ILLEGAL && *err==U_INVALID_CHAR_FOUND) { + cnv->toUCallbackReason = UCNV_UNASSIGNED; + } + cnv->fromCharErrorBehaviour(cnv->toUContext, pArgs, + cnv->invalidCharBuffer, errorInputLength, + cnv->toUCallbackReason, + err); + cnv->toUCallbackReason = UCNV_ILLEGAL; /* reset to default value */ + + /* + * loop back to the offset handling + * + * this flag will indicate after offset handling + * that a callback was called; + * if the callback did not resolve the error, then we return + */ + calledCallback=true; + } + } +} + +/* + * Output the toUnicode overflow buffer. + * Call this function if(cnv->UCharErrorBufferLength>0). + * @return true if overflow + */ +static UBool +ucnv_outputOverflowToUnicode(UConverter *cnv, + char16_t **target, const char16_t *targetLimit, + int32_t **pOffsets, + UErrorCode *err) { + int32_t *offsets; + char16_t *overflow, *t; + int32_t i, length; + + t=*target; + if(pOffsets!=nullptr) { + offsets=*pOffsets; + } else { + offsets=nullptr; + } + + overflow=cnv->UCharErrorBuffer; + length=cnv->UCharErrorBufferLength; + i=0; + while(iUCharErrorBufferLength=(int8_t)j; + *target=t; + if(offsets!=nullptr) { + *pOffsets=offsets; + } + *err=U_BUFFER_OVERFLOW_ERROR; + return true; + } + + /* copy the overflow contents to the target */ + *t++=overflow[i++]; + if(offsets!=nullptr) { + *offsets++=-1; /* no source index available for old output */ + } + } + + /* the overflow buffer is completely copied to the target */ + cnv->UCharErrorBufferLength=0; + *target=t; + if(offsets!=nullptr) { + *pOffsets=offsets; + } + return false; +} + +U_CAPI void U_EXPORT2 +ucnv_toUnicode(UConverter *cnv, + char16_t **target, const char16_t *targetLimit, + const char **source, const char *sourceLimit, + int32_t *offsets, + UBool flush, + UErrorCode *err) { + UConverterToUnicodeArgs args; + const char *s; + char16_t *t; + + /* check parameters */ + if(err==nullptr || U_FAILURE(*err)) { + return; + } + + if(cnv==nullptr || target==nullptr || source==nullptr) { + *err=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + s=*source; + t=*target; + + if ((const void *)U_MAX_PTR(targetLimit) == (const void *)targetLimit) { + /* + Prevent code from going into an infinite loop in case we do hit this + limit. The limit pointer is expected to be on a char16_t * boundary. + This also prevents the next argument check from failing. + */ + targetLimit = (const char16_t *)(((const char *)targetLimit) - 1); + } + + /* + * All these conditions should never happen. + * + * 1) Make sure that the limits are >= to the address source or target + * + * 2) Make sure that the buffer sizes do not exceed the number range for + * int32_t because some functions use the size (in units or bytes) + * rather than comparing pointers, and because offsets are int32_t values. + * + * size_t is guaranteed to be unsigned and large enough for the job. + * + * Return with an error instead of adjusting the limits because we would + * not be able to maintain the semantics that either the source must be + * consumed or the target filled (unless an error occurs). + * An adjustment would be sourceLimit=t+0x7fffffff; for example. + * + * 3) Make sure that the user didn't incorrectly cast a char16_t * pointer + * to a char * pointer and provide an incomplete char16_t code unit. + */ + if (sourceLimit(size_t)0x7fffffff && sourceLimit>s) || + ((size_t)(targetLimit-t)>(size_t)0x3fffffff && targetLimit>t) || + (((const char *)targetLimit-(const char *)t) & 1) != 0 + ) { + *err=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + /* output the target overflow buffer */ + if( cnv->UCharErrorBufferLength>0 && + ucnv_outputOverflowToUnicode(cnv, target, targetLimit, &offsets, err) + ) { + /* U_BUFFER_OVERFLOW_ERROR */ + return; + } + /* *target may have moved, therefore stop using t */ + + if(!flush && s==sourceLimit && cnv->preToULength>=0) { + /* the overflow buffer is emptied and there is no new input: we are done */ + return; + } + + /* + * Do not simply return with a buffer overflow error if + * !flush && t==targetLimit + * because it is possible that the source will not generate any output. + * For example, the skip callback may be called; + * it does not output anything. + */ + + /* prepare the converter arguments */ + args.converter=cnv; + args.flush=flush; + args.offsets=offsets; + args.source=s; + args.sourceLimit=sourceLimit; + args.target=*target; + args.targetLimit=targetLimit; + args.size=sizeof(args); + + _toUnicodeWithCallback(&args, err); + + *source=args.source; + *target=args.target; +} + +/* ucnv_to/fromUChars() ----------------------------------------------------- */ + +U_CAPI int32_t U_EXPORT2 +ucnv_fromUChars(UConverter *cnv, + char *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + UErrorCode *pErrorCode) { + const char16_t *srcLimit; + char *originalDest, *destLimit; + int32_t destLength; + + /* check arguments */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + if( cnv==nullptr || + destCapacity<0 || (destCapacity>0 && dest==nullptr) || + srcLength<-1 || (srcLength!=0 && src==nullptr) + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* initialize */ + ucnv_resetFromUnicode(cnv); + originalDest=dest; + if(srcLength==-1) { + srcLength=u_strlen(src); + } + if(srcLength>0) { + srcLimit=src+srcLength; + destCapacity=pinCapacity(dest, destCapacity); + destLimit=dest+destCapacity; + + /* perform the conversion */ + ucnv_fromUnicode(cnv, &dest, destLimit, &src, srcLimit, 0, true, pErrorCode); + destLength=(int32_t)(dest-originalDest); + + /* if an overflow occurs, then get the preflighting length */ + if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) { + char buffer[1024]; + + destLimit=buffer+sizeof(buffer); + do { + dest=buffer; + *pErrorCode=U_ZERO_ERROR; + ucnv_fromUnicode(cnv, &dest, destLimit, &src, srcLimit, 0, true, pErrorCode); + destLength+=(int32_t)(dest-buffer); + } while(*pErrorCode==U_BUFFER_OVERFLOW_ERROR); + } + } else { + destLength=0; + } + + return u_terminateChars(originalDest, destCapacity, destLength, pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +ucnv_toUChars(UConverter *cnv, + char16_t *dest, int32_t destCapacity, + const char *src, int32_t srcLength, + UErrorCode *pErrorCode) { + const char *srcLimit; + char16_t *originalDest, *destLimit; + int32_t destLength; + + /* check arguments */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + if( cnv==nullptr || + destCapacity<0 || (destCapacity>0 && dest==nullptr) || + srcLength<-1 || (srcLength!=0 && src==nullptr)) + { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* initialize */ + ucnv_resetToUnicode(cnv); + originalDest=dest; + if(srcLength==-1) { + srcLength=(int32_t)uprv_strlen(src); + } + if(srcLength>0) { + srcLimit=src+srcLength; + destCapacity=pinCapacity(dest, destCapacity); + destLimit=dest+destCapacity; + + /* perform the conversion */ + ucnv_toUnicode(cnv, &dest, destLimit, &src, srcLimit, 0, true, pErrorCode); + destLength=(int32_t)(dest-originalDest); + + /* if an overflow occurs, then get the preflighting length */ + if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) + { + char16_t buffer[1024]; + + destLimit=buffer+UPRV_LENGTHOF(buffer); + do { + dest=buffer; + *pErrorCode=U_ZERO_ERROR; + ucnv_toUnicode(cnv, &dest, destLimit, &src, srcLimit, 0, true, pErrorCode); + destLength+=(int32_t)(dest-buffer); + } + while(*pErrorCode==U_BUFFER_OVERFLOW_ERROR); + } + } else { + destLength=0; + } + + return u_terminateUChars(originalDest, destCapacity, destLength, pErrorCode); +} + +/* ucnv_getNextUChar() ------------------------------------------------------ */ + +U_CAPI UChar32 U_EXPORT2 +ucnv_getNextUChar(UConverter *cnv, + const char **source, const char *sourceLimit, + UErrorCode *err) { + UConverterToUnicodeArgs args; + char16_t buffer[U16_MAX_LENGTH]; + const char *s; + UChar32 c; + int32_t i, length; + + /* check parameters */ + if(err==nullptr || U_FAILURE(*err)) { + return 0xffff; + } + + if(cnv==nullptr || source==nullptr) { + *err=U_ILLEGAL_ARGUMENT_ERROR; + return 0xffff; + } + + s=*source; + if(sourceLimit(size_t)0x7fffffff && sourceLimit>s)) { + *err=U_ILLEGAL_ARGUMENT_ERROR; + return 0xffff; + } + + c=U_SENTINEL; + + /* flush the target overflow buffer */ + if(cnv->UCharErrorBufferLength>0) { + char16_t *overflow; + + overflow=cnv->UCharErrorBuffer; + i=0; + length=cnv->UCharErrorBufferLength; + U16_NEXT(overflow, i, length, c); + + /* move the remaining overflow contents up to the beginning */ + if((cnv->UCharErrorBufferLength=(int8_t)(length-i))>0) { + uprv_memmove(cnv->UCharErrorBuffer, cnv->UCharErrorBuffer+i, + cnv->UCharErrorBufferLength*U_SIZEOF_UCHAR); + } + + if(!U16_IS_LEAD(c) || itoULength==0 && cnv->sharedData->impl->getNextUChar!=nullptr) { + c=cnv->sharedData->impl->getNextUChar(&args, err); + *source=s=args.source; + if(*err==U_INDEX_OUTOFBOUNDS_ERROR) { + /* reset the converter without calling the callback function */ + _reset(cnv, UCNV_RESET_TO_UNICODE, false); + return 0xffff; /* no output */ + } else if(U_SUCCESS(*err) && c>=0) { + return c; + /* + * else fall through to use _toUnicode() because + * UCNV_GET_NEXT_UCHAR_USE_TO_U: the native function did not want to handle it after all + * U_FAILURE: call _toUnicode() for callback handling (do not output c) + */ + } + } + + /* convert to one char16_t in buffer[0], or handle getNextUChar() errors */ + _toUnicodeWithCallback(&args, err); + + if(*err==U_BUFFER_OVERFLOW_ERROR) { + *err=U_ZERO_ERROR; + } + + i=0; + length=(int32_t)(args.target-buffer); + } else { + /* write the lead surrogate from the overflow buffer */ + buffer[0]=(char16_t)c; + args.target=buffer+1; + i=0; + length=1; + } + + /* buffer contents starts at i and ends before length */ + + if(U_FAILURE(*err)) { + c=0xffff; /* no output */ + } else if(length==0) { + /* no input or only state changes */ + *err=U_INDEX_OUTOFBOUNDS_ERROR; + /* no need to reset explicitly because _toUnicodeWithCallback() did it */ + c=0xffff; /* no output */ + } else { + c=buffer[0]; + i=1; + if(!U16_IS_LEAD(c)) { + /* consume c=buffer[0], done */ + } else { + /* got a lead surrogate, see if a trail surrogate follows */ + char16_t c2; + + if(cnv->UCharErrorBufferLength>0) { + /* got overflow output from the conversion */ + if(U16_IS_TRAIL(c2=cnv->UCharErrorBuffer[0])) { + /* got a trail surrogate, too */ + c=U16_GET_SUPPLEMENTARY(c, c2); + + /* move the remaining overflow contents up to the beginning */ + if((--cnv->UCharErrorBufferLength)>0) { + uprv_memmove(cnv->UCharErrorBuffer, cnv->UCharErrorBuffer+1, + cnv->UCharErrorBufferLength*U_SIZEOF_UCHAR); + } + } else { + /* c is an unpaired lead surrogate, just return it */ + } + } else if(args.sourceUCharErrorBufferLength)>0) { + uprv_memmove(cnv->UCharErrorBuffer+delta, cnv->UCharErrorBuffer, + length*U_SIZEOF_UCHAR); + } + cnv->UCharErrorBufferLength=(int8_t)(length+delta); + + cnv->UCharErrorBuffer[0]=buffer[i++]; + if(delta>1) { + cnv->UCharErrorBuffer[1]=buffer[i]; + } + } + + *source=args.source; + return c; +} + +/* ucnv_convert() and siblings ---------------------------------------------- */ + +U_CAPI void U_EXPORT2 +ucnv_convertEx(UConverter *targetCnv, UConverter *sourceCnv, + char **target, const char *targetLimit, + const char **source, const char *sourceLimit, + char16_t *pivotStart, char16_t **pivotSource, + char16_t **pivotTarget, const char16_t *pivotLimit, + UBool reset, UBool flush, + UErrorCode *pErrorCode) { + char16_t pivotBuffer[CHUNK_SIZE]; + const char16_t *myPivotSource; + char16_t *myPivotTarget; + const char *s; + char *t; + + UConverterToUnicodeArgs toUArgs; + UConverterFromUnicodeArgs fromUArgs; + UConverterConvert convert; + + /* error checking */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return; + } + + if( targetCnv==nullptr || sourceCnv==nullptr || + source==nullptr || *source==nullptr || + target==nullptr || *target==nullptr || targetLimit==nullptr + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + s=*source; + t=*target; + if((sourceLimit!=nullptr && sourceLimit(size_t)0x7fffffff && sourceLimit>s)) || + ((size_t)(targetLimit-t)>(size_t)0x7fffffff && targetLimit>t) + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + if(pivotStart==nullptr) { + if(!flush) { + /* streaming conversion requires an explicit pivot buffer */ + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + /* use the stack pivot buffer */ + myPivotSource=myPivotTarget=pivotStart=pivotBuffer; + pivotSource=(char16_t **)&myPivotSource; + pivotTarget=&myPivotTarget; + pivotLimit=pivotBuffer+CHUNK_SIZE; + } else if( pivotStart>=pivotLimit || + pivotSource==nullptr || *pivotSource==nullptr || + pivotTarget==nullptr || *pivotTarget==nullptr || + pivotLimit==nullptr + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + if(sourceLimit==nullptr) { + /* get limit of single-byte-NUL-terminated source string */ + sourceLimit=uprv_strchr(*source, 0); + } + + if(reset) { + ucnv_resetToUnicode(sourceCnv); + ucnv_resetFromUnicode(targetCnv); + *pivotSource=*pivotTarget=pivotStart; + } else if(targetCnv->charErrorBufferLength>0) { + /* output the targetCnv overflow buffer */ + if(ucnv_outputOverflowFromUnicode(targetCnv, target, targetLimit, nullptr, pErrorCode)) { + /* U_BUFFER_OVERFLOW_ERROR */ + return; + } + /* *target has moved, therefore stop using t */ + + if( !flush && + targetCnv->preFromULength>=0 && *pivotSource==*pivotTarget && + sourceCnv->UCharErrorBufferLength==0 && sourceCnv->preToULength>=0 && s==sourceLimit + ) { + /* the fromUnicode overflow buffer is emptied and there is no new input: we are done */ + return; + } + } + + /* Is direct-UTF-8 conversion available? */ + if( sourceCnv->sharedData->staticData->conversionType==UCNV_UTF8 && + targetCnv->sharedData->impl->fromUTF8!=nullptr + ) { + convert=targetCnv->sharedData->impl->fromUTF8; + } else if( targetCnv->sharedData->staticData->conversionType==UCNV_UTF8 && + sourceCnv->sharedData->impl->toUTF8!=nullptr + ) { + convert=sourceCnv->sharedData->impl->toUTF8; + } else { + convert=nullptr; + } + + /* + * If direct-UTF-8 conversion is available, then we use a smaller + * pivot buffer for error handling and partial matches + * so that we quickly return to direct conversion. + * + * 32 is large enough for UCNV_EXT_MAX_UCHARS and UCNV_ERROR_BUFFER_LENGTH. + * + * We could reduce the pivot buffer size further, at the cost of + * buffer overflows from callbacks. + * The pivot buffer should not be smaller than the maximum number of + * fromUnicode extension table input UChars + * (for m:n conversion, see + * targetCnv->sharedData->mbcs.extIndexes[UCNV_EXT_COUNT_UCHARS]) + * or 2 for surrogate pairs. + * + * Too small a buffer can cause thrashing between pivoting and direct + * conversion, with function call overhead outweighing the benefits + * of direct conversion. + */ + if(convert!=nullptr && (pivotLimit-pivotStart)>32) { + pivotLimit=pivotStart+32; + } + + /* prepare the converter arguments */ + fromUArgs.converter=targetCnv; + fromUArgs.flush=false; + fromUArgs.offsets=nullptr; + fromUArgs.target=*target; + fromUArgs.targetLimit=targetLimit; + fromUArgs.size=sizeof(fromUArgs); + + toUArgs.converter=sourceCnv; + toUArgs.flush=flush; + toUArgs.offsets=nullptr; + toUArgs.source=s; + toUArgs.sourceLimit=sourceLimit; + toUArgs.targetLimit=pivotLimit; + toUArgs.size=sizeof(toUArgs); + + /* + * TODO: Consider separating this function into two functions, + * extracting exactly the conversion loop, + * for readability and to reduce the set of visible variables. + * + * Otherwise stop using s and t from here on. + */ + s=t=nullptr; + + /* + * conversion loop + * + * The sequence of steps in the loop may appear backward, + * but the principle is simple: + * In the chain of + * source - sourceCnv overflow - pivot - targetCnv overflow - target + * empty out later buffers before refilling them from earlier ones. + * + * The targetCnv overflow buffer is flushed out only once before the loop. + */ + for(;;) { + /* + * if(pivot not empty or error or replay or flush fromUnicode) { + * fromUnicode(pivot -> target); + * } + * + * For pivoting conversion; and for direct conversion for + * error callback handling and flushing the replay buffer. + */ + if( *pivotSource<*pivotTarget || + U_FAILURE(*pErrorCode) || + targetCnv->preFromULength<0 || + fromUArgs.flush + ) { + fromUArgs.source=*pivotSource; + fromUArgs.sourceLimit=*pivotTarget; + _fromUnicodeWithCallback(&fromUArgs, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + /* target overflow, or conversion error */ + *pivotSource=(char16_t *)fromUArgs.source; + break; + } + + /* + * _fromUnicodeWithCallback() must have consumed the pivot contents + * (*pivotSource==*pivotTarget) since it returned with U_SUCCESS() + */ + } + + /* The pivot buffer is empty; reset it so we start at pivotStart. */ + *pivotSource=*pivotTarget=pivotStart; + + /* + * if(sourceCnv overflow buffer not empty) { + * move(sourceCnv overflow buffer -> pivot); + * continue; + * } + */ + /* output the sourceCnv overflow buffer */ + if(sourceCnv->UCharErrorBufferLength>0) { + if(ucnv_outputOverflowToUnicode(sourceCnv, pivotTarget, pivotLimit, nullptr, pErrorCode)) { + /* U_BUFFER_OVERFLOW_ERROR */ + *pErrorCode=U_ZERO_ERROR; + } + continue; + } + + /* + * check for end of input and break if done + * + * Checking both flush and fromUArgs.flush ensures that the converters + * have been called with the flush flag set if the ucnv_convertEx() + * caller set it. + */ + if( toUArgs.source==sourceLimit && + sourceCnv->preToULength>=0 && sourceCnv->toULength==0 && + (!flush || fromUArgs.flush) + ) { + /* done successfully */ + break; + } + + /* + * use direct conversion if available + * but not if continuing a partial match + * or flushing the toUnicode replay buffer + */ + if(convert!=nullptr && targetCnv->preFromUFirstCP<0 && sourceCnv->preToULength==0) { + if(*pErrorCode==U_USING_DEFAULT_WARNING) { + /* remove a warning that may be set by this function */ + *pErrorCode=U_ZERO_ERROR; + } + convert(&fromUArgs, &toUArgs, pErrorCode); + if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) { + break; + } else if(U_FAILURE(*pErrorCode)) { + if(sourceCnv->toULength>0) { + /* + * Fall through to calling _toUnicodeWithCallback() + * for callback handling. + * + * The pivot buffer will be reset with + * *pivotSource=*pivotTarget=pivotStart; + * which indicates a toUnicode error to the caller + * (*pivotSource==pivotStart shows no pivot UChars consumed). + */ + } else { + /* + * Indicate a fromUnicode error to the caller + * (*pivotSource>pivotStart shows some pivot UChars consumed). + */ + *pivotSource=*pivotTarget=pivotStart+1; + /* + * Loop around to calling _fromUnicodeWithCallbacks() + * for callback handling. + */ + continue; + } + } else if(*pErrorCode==U_USING_DEFAULT_WARNING) { + /* + * No error, but the implementation requested to temporarily + * fall back to pivoting. + */ + *pErrorCode=U_ZERO_ERROR; + /* + * The following else branches are almost identical to the end-of-input + * handling in _toUnicodeWithCallback(). + * Avoid calling it just for the end of input. + */ + } else if(flush && sourceCnv->toULength>0) { /* flush==toUArgs.flush */ + /* + * the entire input stream is consumed + * and there is a partial, truncated input sequence left + */ + + /* inject an error and continue with callback handling */ + *pErrorCode=U_TRUNCATED_CHAR_FOUND; + } else { + /* input consumed */ + if(flush) { + /* reset the converters without calling the callback functions */ + _reset(sourceCnv, UCNV_RESET_TO_UNICODE, false); + _reset(targetCnv, UCNV_RESET_FROM_UNICODE, false); + } + + /* done successfully */ + break; + } + } + + /* + * toUnicode(source -> pivot); + * + * For pivoting conversion; and for direct conversion for + * error callback handling, continuing partial matches + * and flushing the replay buffer. + * + * The pivot buffer is empty and reset. + */ + toUArgs.target=pivotStart; /* ==*pivotTarget */ + /* toUArgs.targetLimit=pivotLimit; already set before the loop */ + _toUnicodeWithCallback(&toUArgs, pErrorCode); + *pivotTarget=toUArgs.target; + if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) { + /* pivot overflow: continue with the conversion loop */ + *pErrorCode=U_ZERO_ERROR; + } else if(U_FAILURE(*pErrorCode) || (!flush && *pivotTarget==pivotStart)) { + /* conversion error, or there was nothing left to convert */ + break; + } + /* + * else: + * _toUnicodeWithCallback() wrote into the pivot buffer, + * continue with fromUnicode conversion. + * + * Set the fromUnicode flush flag if we flush and if toUnicode has + * processed the end of the input. + */ + if( flush && toUArgs.source==sourceLimit && + sourceCnv->preToULength>=0 && + sourceCnv->UCharErrorBufferLength==0 + ) { + fromUArgs.flush=true; + } + } + + /* + * The conversion loop is exited when one of the following is true: + * - the entire source text has been converted successfully to the target buffer + * - a target buffer overflow occurred + * - a conversion error occurred + */ + + *source=toUArgs.source; + *target=fromUArgs.target; + + /* terminate the target buffer if possible */ + if(flush && U_SUCCESS(*pErrorCode)) { + if(*target!=targetLimit) { + **target=0; + if(*pErrorCode==U_STRING_NOT_TERMINATED_WARNING) { + *pErrorCode=U_ZERO_ERROR; + } + } else { + *pErrorCode=U_STRING_NOT_TERMINATED_WARNING; + } + } +} + +/* internal implementation of ucnv_convert() etc. with preflighting */ +static int32_t +ucnv_internalConvert(UConverter *outConverter, UConverter *inConverter, + char *target, int32_t targetCapacity, + const char *source, int32_t sourceLength, + UErrorCode *pErrorCode) { + char16_t pivotBuffer[CHUNK_SIZE]; + char16_t *pivot, *pivot2; + + char *myTarget; + const char *sourceLimit; + const char *targetLimit; + int32_t targetLength=0; + + /* set up */ + if(sourceLength<0) { + sourceLimit=uprv_strchr(source, 0); + } else { + sourceLimit=source+sourceLength; + } + + /* if there is no input data, we're done */ + if(source==sourceLimit) { + return u_terminateChars(target, targetCapacity, 0, pErrorCode); + } + + pivot=pivot2=pivotBuffer; + myTarget=target; + targetLength=0; + + if(targetCapacity>0) { + /* perform real conversion */ + targetLimit=target+targetCapacity; + ucnv_convertEx(outConverter, inConverter, + &myTarget, targetLimit, + &source, sourceLimit, + pivotBuffer, &pivot, &pivot2, pivotBuffer+CHUNK_SIZE, + false, + true, + pErrorCode); + targetLength=(int32_t)(myTarget-target); + } + + /* + * If the output buffer is exhausted (or we are only "preflighting"), we need to stop writing + * to it but continue the conversion in order to store in targetCapacity + * the number of bytes that was required. + */ + if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR || targetCapacity==0) + { + char targetBuffer[CHUNK_SIZE]; + + targetLimit=targetBuffer+CHUNK_SIZE; + do { + *pErrorCode=U_ZERO_ERROR; + myTarget=targetBuffer; + ucnv_convertEx(outConverter, inConverter, + &myTarget, targetLimit, + &source, sourceLimit, + pivotBuffer, &pivot, &pivot2, pivotBuffer+CHUNK_SIZE, + false, + true, + pErrorCode); + targetLength+=(int32_t)(myTarget-targetBuffer); + } while(*pErrorCode==U_BUFFER_OVERFLOW_ERROR); + + /* done with preflighting, set warnings and errors as appropriate */ + return u_terminateChars(target, targetCapacity, targetLength, pErrorCode); + } + + /* no need to call u_terminateChars() because ucnv_convertEx() took care of that */ + return targetLength; +} + +U_CAPI int32_t U_EXPORT2 +ucnv_convert(const char *toConverterName, const char *fromConverterName, + char *target, int32_t targetCapacity, + const char *source, int32_t sourceLength, + UErrorCode *pErrorCode) { + UConverter in, out; /* stack-allocated */ + UConverter *inConverter, *outConverter; + int32_t targetLength; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + if( source==nullptr || sourceLength<-1 || + targetCapacity<0 || (targetCapacity>0 && target==nullptr) + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* if there is no input data, we're done */ + if(sourceLength==0 || (sourceLength<0 && *source==0)) { + return u_terminateChars(target, targetCapacity, 0, pErrorCode); + } + + /* create the converters */ + inConverter=ucnv_createConverter(&in, fromConverterName, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return 0; + } + + outConverter=ucnv_createConverter(&out, toConverterName, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + ucnv_close(inConverter); + return 0; + } + + targetLength=ucnv_internalConvert(outConverter, inConverter, + target, targetCapacity, + source, sourceLength, + pErrorCode); + + ucnv_close(inConverter); + ucnv_close(outConverter); + + return targetLength; +} + +/* @internal */ +static int32_t +ucnv_convertAlgorithmic(UBool convertToAlgorithmic, + UConverterType algorithmicType, + UConverter *cnv, + char *target, int32_t targetCapacity, + const char *source, int32_t sourceLength, + UErrorCode *pErrorCode) { + UConverter algoConverterStatic; /* stack-allocated */ + UConverter *algoConverter, *to, *from; + int32_t targetLength; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + if( cnv==nullptr || source==nullptr || sourceLength<-1 || + targetCapacity<0 || (targetCapacity>0 && target==nullptr) + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* if there is no input data, we're done */ + if(sourceLength==0 || (sourceLength<0 && *source==0)) { + return u_terminateChars(target, targetCapacity, 0, pErrorCode); + } + + /* create the algorithmic converter */ + algoConverter=ucnv_createAlgorithmicConverter(&algoConverterStatic, algorithmicType, + "", 0, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return 0; + } + + /* reset the other converter */ + if(convertToAlgorithmic) { + /* cnv->Unicode->algo */ + ucnv_resetToUnicode(cnv); + to=algoConverter; + from=cnv; + } else { + /* algo->Unicode->cnv */ + ucnv_resetFromUnicode(cnv); + from=algoConverter; + to=cnv; + } + + targetLength=ucnv_internalConvert(to, from, + target, targetCapacity, + source, sourceLength, + pErrorCode); + + ucnv_close(algoConverter); + + return targetLength; +} + +U_CAPI int32_t U_EXPORT2 +ucnv_toAlgorithmic(UConverterType algorithmicType, + UConverter *cnv, + char *target, int32_t targetCapacity, + const char *source, int32_t sourceLength, + UErrorCode *pErrorCode) { + return ucnv_convertAlgorithmic(true, algorithmicType, cnv, + target, targetCapacity, + source, sourceLength, + pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +ucnv_fromAlgorithmic(UConverter *cnv, + UConverterType algorithmicType, + char *target, int32_t targetCapacity, + const char *source, int32_t sourceLength, + UErrorCode *pErrorCode) UPRV_NO_SANITIZE_UNDEFINED { + + if(algorithmicType<0 || UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES<=algorithmicType) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + return ucnv_convertAlgorithmic(false, algorithmicType, cnv, + target, targetCapacity, + source, sourceLength, + pErrorCode); +} + +U_CAPI UConverterType U_EXPORT2 +ucnv_getType(const UConverter* converter) +{ + int8_t type = converter->sharedData->staticData->conversionType; +#if !UCONFIG_NO_LEGACY_CONVERSION + if(type == UCNV_MBCS) { + return ucnv_MBCSGetType(converter); + } +#endif + return (UConverterType)type; +} + +U_CAPI void U_EXPORT2 +ucnv_getStarters(const UConverter* converter, + UBool starters[256], + UErrorCode* err) +{ + if (err == nullptr || U_FAILURE(*err)) { + return; + } + + if(converter->sharedData->impl->getStarters != nullptr) { + converter->sharedData->impl->getStarters(converter, starters, err); + } else { + *err = U_ILLEGAL_ARGUMENT_ERROR; + } +} + +static const UAmbiguousConverter *ucnv_getAmbiguous(const UConverter *cnv) +{ + UErrorCode errorCode; + const char *name; + int32_t i; + + if(cnv==nullptr) { + return nullptr; + } + + errorCode=U_ZERO_ERROR; + name=ucnv_getName(cnv, &errorCode); + if(U_FAILURE(errorCode)) { + return nullptr; + } + + for(i=0; ivariant5c; + for(i=0; iuseFallback = usesFallback; +} + +U_CAPI UBool U_EXPORT2 +ucnv_usesFallback(const UConverter *cnv) +{ + return cnv->useFallback; +} + +U_CAPI void U_EXPORT2 +ucnv_getInvalidChars (const UConverter * converter, + char *errBytes, + int8_t * len, + UErrorCode * err) +{ + if (err == nullptr || U_FAILURE(*err)) + { + return; + } + if (len == nullptr || errBytes == nullptr || converter == nullptr) + { + *err = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (*len < converter->invalidCharLength) + { + *err = U_INDEX_OUTOFBOUNDS_ERROR; + return; + } + if ((*len = converter->invalidCharLength) > 0) + { + uprv_memcpy (errBytes, converter->invalidCharBuffer, *len); + } +} + +U_CAPI void U_EXPORT2 +ucnv_getInvalidUChars (const UConverter * converter, + char16_t *errChars, + int8_t * len, + UErrorCode * err) +{ + if (err == nullptr || U_FAILURE(*err)) + { + return; + } + if (len == nullptr || errChars == nullptr || converter == nullptr) + { + *err = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (*len < converter->invalidUCharLength) + { + *err = U_INDEX_OUTOFBOUNDS_ERROR; + return; + } + if ((*len = converter->invalidUCharLength) > 0) + { + u_memcpy (errChars, converter->invalidUCharBuffer, *len); + } +} + +#define SIG_MAX_LEN 5 + +U_CAPI const char* U_EXPORT2 +ucnv_detectUnicodeSignature( const char* source, + int32_t sourceLength, + int32_t* signatureLength, + UErrorCode* pErrorCode) { + int32_t dummy; + + /* initial 0xa5 bytes: make sure that if we read preFromUFirstCP >= 0){ + return U16_LENGTH(cnv->preFromUFirstCP)+cnv->preFromULength ; + }else if(cnv->preFromULength < 0){ + return -cnv->preFromULength ; + }else if(cnv->fromUChar32 > 0){ + return 1; + } + return 0; + +} + +U_CAPI int32_t U_EXPORT2 +ucnv_toUCountPending(const UConverter* cnv, UErrorCode* status){ + + if(status == nullptr || U_FAILURE(*status)){ + return -1; + } + if(cnv == nullptr){ + *status = U_ILLEGAL_ARGUMENT_ERROR; + return -1; + } + + if(cnv->preToULength > 0){ + return cnv->preToULength ; + }else if(cnv->preToULength < 0){ + return -cnv->preToULength; + }else if(cnv->toULength > 0){ + return cnv->toULength; + } + return 0; +} + +U_CAPI UBool U_EXPORT2 +ucnv_isFixedWidth(UConverter *cnv, UErrorCode *status){ + if (U_FAILURE(*status)) { + return false; + } + + if (cnv == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + + switch (ucnv_getType(cnv)) { + case UCNV_SBCS: + case UCNV_DBCS: + case UCNV_UTF32_BigEndian: + case UCNV_UTF32_LittleEndian: + case UCNV_UTF32: + case UCNV_US_ASCII: + return true; + default: + return false; + } +} +#endif + +/* + * Hey, Emacs, please set the following: + * + * Local Variables: + * indent-tabs-mode: nil + * End: + * + */ diff --git a/deps/icu-small/source/common/ucnv2022.cpp b/deps/icu-small/source/common/ucnv2022.cpp index ec096780e97107..ac85a544213704 100644 --- a/deps/icu-small/source/common/ucnv2022.cpp +++ b/deps/icu-small/source/common/ucnv2022.cpp @@ -1,3973 +1,3973 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2000-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: ucnv2022.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2000feb03 -* created by: Markus W. Scherer -* -* Change history: -* -* 06/29/2000 helena Major rewrite of the callback APIs. -* 08/08/2000 Ram Included support for ISO-2022-JP-2 -* Changed implementation of toUnicode -* function -* 08/21/2000 Ram Added support for ISO-2022-KR -* 08/29/2000 Ram Seperated implementation of EBCDIC to -* ucnvebdc.c -* 09/20/2000 Ram Added support for ISO-2022-CN -* Added implementations for getNextUChar() -* for specific 2022 country variants. -* 10/31/2000 Ram Implemented offsets logic functions -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/uset.h" -#include "unicode/ucnv_err.h" -#include "unicode/ucnv_cb.h" -#include "unicode/utf16.h" -#include "ucnv_imp.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" -#include "ucnvmbcs.h" -#include "cstring.h" -#include "cmemory.h" -#include "uassert.h" - -#ifdef U_ENABLE_GENERIC_ISO_2022 -/* - * I am disabling the generic ISO-2022 converter after proposing to do so on - * the icu mailing list two days ago. - * - * Reasons: - * 1. It does not fully support the ISO-2022/ECMA-35 specification with all of - * its designation sequences, single shifts with return to the previous state, - * switch-with-no-return to UTF-16BE or similar, etc. - * This is unlike the language-specific variants like ISO-2022-JP which - * require a much smaller repertoire of ISO-2022 features. - * These variants continue to be supported. - * 2. I believe that no one is really using the generic ISO-2022 converter - * but rather always one of the language-specific variants. - * Note that ICU's generic ISO-2022 converter has always output one escape - * sequence followed by UTF-8 for the whole stream. - * 3. Switching between subcharsets is extremely slow, because each time - * the previous converter is closed and a new one opened, - * without any kind of caching, least-recently-used list, etc. - * 4. The code is currently buggy, and given the above it does not seem - * reasonable to spend the time on maintenance. - * 5. ISO-2022 subcharsets should normally be used with 7-bit byte encodings. - * This means, for example, that when ISO-8859-7 is designated, the following - * ISO-2022 bytes 00..7f should be interpreted as ISO-8859-7 bytes 80..ff. - * The ICU ISO-2022 converter does not handle this - and has no information - * about which subconverter would have to be shifted vs. which is designed - * for 7-bit ISO-2022. - * - * Markus Scherer 2003-dec-03 - */ -#endif - -#if !UCONFIG_ONLY_HTML_CONVERSION -static const char SHIFT_IN_STR[] = "\x0F"; -// static const char SHIFT_OUT_STR[] = "\x0E"; -#endif - -#define CR 0x0D -#define LF 0x0A -#define H_TAB 0x09 -#define V_TAB 0x0B -#define SPACE 0x20 - -enum { - HWKANA_START=0xff61, - HWKANA_END=0xff9f -}; - -/* - * 94-character sets with native byte values A1..FE are encoded in ISO 2022 - * as bytes 21..7E. (Subtract 0x80.) - * 96-character sets with native byte values A0..FF are encoded in ISO 2022 - * as bytes 20..7F. (Subtract 0x80.) - * Do not encode C1 control codes with native bytes 80..9F - * as bytes 00..1F (C0 control codes). - */ -enum { - GR94_START=0xa1, - GR94_END=0xfe, - GR96_START=0xa0, - GR96_END=0xff -}; - -/* - * ISO 2022 control codes must not be converted from Unicode - * because they would mess up the byte stream. - * The bit mask 0x0800c000 has bits set at bit positions 0xe, 0xf, 0x1b - * corresponding to SO, SI, and ESC. - */ -#define IS_2022_CONTROL(c) (((c)<0x20) && (((uint32_t)1<<(c))&0x0800c000)!=0) - -/* for ISO-2022-JP and -CN implementations */ -typedef enum { - /* shared values */ - INVALID_STATE=-1, - ASCII = 0, - - SS2_STATE=0x10, - SS3_STATE, - - /* JP */ - ISO8859_1 = 1 , - ISO8859_7 = 2 , - JISX201 = 3, - JISX208 = 4, - JISX212 = 5, - GB2312 =6, - KSC5601 =7, - HWKANA_7BIT=8, /* Halfwidth Katakana 7 bit */ - - /* CN */ - /* the first few enum constants must keep their values because they correspond to myConverterArray[] */ - GB2312_1=1, - ISO_IR_165=2, - CNS_11643=3, - - /* - * these are used in StateEnum and ISO2022State variables, - * but CNS_11643 must be used to index into myConverterArray[] - */ - CNS_11643_0=0x20, - CNS_11643_1, - CNS_11643_2, - CNS_11643_3, - CNS_11643_4, - CNS_11643_5, - CNS_11643_6, - CNS_11643_7 -} StateEnum; - -/* is the StateEnum charset value for a DBCS charset? */ -#if UCONFIG_ONLY_HTML_CONVERSION -#define IS_JP_DBCS(cs) (JISX208==(cs)) -#else -#define IS_JP_DBCS(cs) (JISX208<=(cs) && (cs)<=KSC5601) -#endif - -#define CSM(cs) ((uint16_t)1<<(cs)) - -/* - * Each of these charset masks (with index x) contains a bit for a charset in exact correspondence - * to whether that charset is used in the corresponding version x of ISO_2022,locale=ja,version=x - * - * Note: The converter uses some leniency: - * - The escape sequence ESC ( I for half-width 7-bit Katakana is recognized in - * all versions, not just JIS7 and JIS8. - * - ICU does not distinguish between different versions of JIS X 0208. - */ -#if UCONFIG_ONLY_HTML_CONVERSION -enum { MAX_JA_VERSION=0 }; -#else -enum { MAX_JA_VERSION=4 }; -#endif -static const uint16_t jpCharsetMasks[MAX_JA_VERSION+1]={ - CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT), -#if !UCONFIG_ONLY_HTML_CONVERSION - CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT)|CSM(JISX212), - CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT)|CSM(JISX212)|CSM(GB2312)|CSM(KSC5601)|CSM(ISO8859_1)|CSM(ISO8859_7), - CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT)|CSM(JISX212)|CSM(GB2312)|CSM(KSC5601)|CSM(ISO8859_1)|CSM(ISO8859_7), - CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT)|CSM(JISX212)|CSM(GB2312)|CSM(KSC5601)|CSM(ISO8859_1)|CSM(ISO8859_7) -#endif -}; - -typedef enum { - ASCII1=0, - LATIN1, - SBCS, - DBCS, - MBCS, - HWKANA -}Cnv2022Type; - -typedef struct ISO2022State { - int8_t cs[4]; /* charset number for SI (G0)/SO (G1)/SS2 (G2)/SS3 (G3) */ - int8_t g; /* 0..3 for G0..G3 (SI/SO/SS2/SS3) */ - int8_t prevG; /* g before single shift (SS2 or SS3) */ -} ISO2022State; - -#define UCNV_OPTIONS_VERSION_MASK 0xf -#define UCNV_2022_MAX_CONVERTERS 10 - -typedef struct{ - UConverterSharedData *myConverterArray[UCNV_2022_MAX_CONVERTERS]; - UConverter *currentConverter; - Cnv2022Type currentType; - ISO2022State toU2022State, fromU2022State; - uint32_t key; - uint32_t version; -#ifdef U_ENABLE_GENERIC_ISO_2022 - UBool isFirstBuffer; -#endif - UBool isEmptySegment; - char name[30]; - char locale[3]; -}UConverterDataISO2022; - -/* Protos */ -/* ISO-2022 ----------------------------------------------------------------- */ - -/*Forward declaration */ -U_CFUNC void U_CALLCONV -ucnv_fromUnicode_UTF8(UConverterFromUnicodeArgs * args, - UErrorCode * err); -U_CFUNC void U_CALLCONV -ucnv_fromUnicode_UTF8_OFFSETS_LOGIC(UConverterFromUnicodeArgs * args, - UErrorCode * err); - -#define ESC_2022 0x1B /*ESC*/ - -typedef enum -{ - INVALID_2022 = -1, /*Doesn't correspond to a valid iso 2022 escape sequence*/ - VALID_NON_TERMINAL_2022 = 0, /*so far corresponds to a valid iso 2022 escape sequence*/ - VALID_TERMINAL_2022 = 1, /*corresponds to a valid iso 2022 escape sequence*/ - VALID_MAYBE_TERMINAL_2022 = 2 /*so far matches one iso 2022 escape sequence, but by adding more characters might match another escape sequence*/ -} UCNV_TableStates_2022; - -/* -* The way these state transition arrays work is: -* ex : ESC$B is the sequence for JISX208 -* a) First Iteration: char is ESC -* i) Get the value of ESC from normalize_esq_chars_2022[] with int value of ESC as index -* int x = normalize_esq_chars_2022[27] which is equal to 1 -* ii) Search for this value in escSeqStateTable_Key_2022[] -* value of x is stored at escSeqStateTable_Key_2022[0] -* iii) Save this index as offset -* iv) Get state of this sequence from escSeqStateTable_Value_2022[] -* escSeqStateTable_Value_2022[offset], which is VALID_NON_TERMINAL_2022 -* b) Switch on this state and continue to next char -* i) Get the value of $ from normalize_esq_chars_2022[] with int value of $ as index -* which is normalize_esq_chars_2022[36] == 4 -* ii) x is currently 1(from above) -* x<<=5 -- x is now 32 -* x+=normalize_esq_chars_2022[36] -* now x is 36 -* iii) Search for this value in escSeqStateTable_Key_2022[] -* value of x is stored at escSeqStateTable_Key_2022[2], so offset is 2 -* iv) Get state of this sequence from escSeqStateTable_Value_2022[] -* escSeqStateTable_Value_2022[offset], which is VALID_NON_TERMINAL_2022 -* c) Switch on this state and continue to next char -* i) Get the value of B from normalize_esq_chars_2022[] with int value of B as index -* ii) x is currently 36 (from above) -* x<<=5 -- x is now 1152 -* x+=normalize_esq_chars_2022[66] -* now x is 1161 -* iii) Search for this value in escSeqStateTable_Key_2022[] -* value of x is stored at escSeqStateTable_Key_2022[21], so offset is 21 -* iv) Get state of this sequence from escSeqStateTable_Value_2022[21] -* escSeqStateTable_Value_2022[offset], which is VALID_TERMINAL_2022 -* v) Get the converter name form escSeqStateTable_Result_2022[21] which is JISX208 -*/ - - -/*Below are the 3 arrays depicting a state transition table*/ -static const int8_t normalize_esq_chars_2022[256] = { -/* 0 1 2 3 4 5 6 7 8 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 ,0 ,1 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,4 ,7 ,29 ,0 - ,2 ,24 ,26 ,27 ,0 ,3 ,23 ,6 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,5 ,8 ,9 ,10 ,11 ,12 - ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20 ,25 ,28 - ,0 ,0 ,21 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,22 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 - ,0 ,0 ,0 ,0 ,0 ,0 -}; - -#ifdef U_ENABLE_GENERIC_ISO_2022 -/* - * When the generic ISO-2022 converter is completely removed, not just disabled - * per #ifdef, then the following state table and the associated tables that are - * dimensioned with MAX_STATES_2022 should be trimmed. - * - * Especially, VALID_MAYBE_TERMINAL_2022 will not be used any more, and all of - * the associated escape sequences starting with ESC ( B should be removed. - * This includes the ones with key values 1097 and all of the ones above 1000000. - * - * For the latter, the tables can simply be truncated. - * For the former, since the tables must be kept parallel, it is probably best - * to simply duplicate an adjacent table cell, parallel in all tables. - * - * It may make sense to restructure the tables, especially by using small search - * tables for the variants instead of indexing them parallel to the table here. - */ -#endif - -#define MAX_STATES_2022 74 -static const int32_t escSeqStateTable_Key_2022[MAX_STATES_2022] = { -/* 0 1 2 3 4 5 6 7 8 9 */ - - 1 ,34 ,36 ,39 ,55 ,57 ,60 ,61 ,1093 ,1096 - ,1097 ,1098 ,1099 ,1100 ,1101 ,1102 ,1103 ,1104 ,1105 ,1106 - ,1109 ,1154 ,1157 ,1160 ,1161 ,1176 ,1178 ,1179 ,1254 ,1257 - ,1768 ,1773 ,1957 ,35105 ,36933 ,36936 ,36937 ,36938 ,36939 ,36940 - ,36942 ,36943 ,36944 ,36945 ,36946 ,36947 ,36948 ,37640 ,37642 ,37644 - ,37646 ,37711 ,37744 ,37745 ,37746 ,37747 ,37748 ,40133 ,40136 ,40138 - ,40139 ,40140 ,40141 ,1123363 ,35947624 ,35947625 ,35947626 ,35947627 ,35947629 ,35947630 - ,35947631 ,35947635 ,35947636 ,35947638 -}; - -#ifdef U_ENABLE_GENERIC_ISO_2022 - -static const char* const escSeqStateTable_Result_2022[MAX_STATES_2022] = { - /* 0 1 2 3 4 5 6 7 8 9 */ - - NULL ,NULL ,NULL ,NULL ,NULL ,NULL ,NULL ,NULL ,"latin1" ,"latin1" - ,"latin1" ,"ibm-865" ,"ibm-865" ,"ibm-865" ,"ibm-865" ,"ibm-865" ,"ibm-865" ,"JISX0201" ,"JISX0201" ,"latin1" - ,"latin1" ,NULL ,"JISX-208" ,"ibm-5478" ,"JISX-208" ,NULL ,NULL ,NULL ,NULL ,"UTF8" - ,"ISO-8859-1" ,"ISO-8859-7" ,"JIS-X-208" ,NULL ,"ibm-955" ,"ibm-367" ,"ibm-952" ,"ibm-949" ,"JISX-212" ,"ibm-1383" - ,"ibm-952" ,"ibm-964" ,"ibm-964" ,"ibm-964" ,"ibm-964" ,"ibm-964" ,"ibm-964" ,"ibm-5478" ,"ibm-949" ,"ISO-IR-165" - ,"CNS-11643-1992,1" ,"CNS-11643-1992,2" ,"CNS-11643-1992,3" ,"CNS-11643-1992,4" ,"CNS-11643-1992,5" ,"CNS-11643-1992,6" ,"CNS-11643-1992,7" ,"UTF16_PlatformEndian" ,"UTF16_PlatformEndian" ,"UTF16_PlatformEndian" - ,"UTF16_PlatformEndian" ,"UTF16_PlatformEndian" ,"UTF16_PlatformEndian" ,NULL ,"latin1" ,"ibm-912" ,"ibm-913" ,"ibm-914" ,"ibm-813" ,"ibm-1089" - ,"ibm-920" ,"ibm-915" ,"ibm-915" ,"latin1" -}; - -#endif - -static const int8_t escSeqStateTable_Value_2022[MAX_STATES_2022] = { -/* 0 1 2 3 4 5 6 7 8 9 */ - VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 - ,VALID_MAYBE_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 - ,VALID_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_TERMINAL_2022 - ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 - ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 - ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 - ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 - ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 -}; - -/* Type def for refactoring changeState_2022 code*/ -typedef enum{ -#ifdef U_ENABLE_GENERIC_ISO_2022 - ISO_2022=0, -#endif - ISO_2022_JP=1, -#if !UCONFIG_ONLY_HTML_CONVERSION - ISO_2022_KR=2, - ISO_2022_CN=3 -#endif -} Variant2022; - -/*********** ISO 2022 Converter Protos ***********/ -static void U_CALLCONV -_ISO2022Open(UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *errorCode); - -static void U_CALLCONV - _ISO2022Close(UConverter *converter); - -static void U_CALLCONV -_ISO2022Reset(UConverter *converter, UConverterResetChoice choice); - -U_CDECL_BEGIN -static const char * U_CALLCONV -_ISO2022getName(const UConverter* cnv); -U_CDECL_END - -static void U_CALLCONV -_ISO_2022_WriteSub(UConverterFromUnicodeArgs *args, int32_t offsetIndex, UErrorCode *err); - -U_CDECL_BEGIN -static UConverter * U_CALLCONV -_ISO_2022_SafeClone(const UConverter *cnv, void *stackBuffer, int32_t *pBufferSize, UErrorCode *status); - -U_CDECL_END - -#ifdef U_ENABLE_GENERIC_ISO_2022 -static void U_CALLCONV -T_UConverter_toUnicode_ISO_2022_OFFSETS_LOGIC(UConverterToUnicodeArgs* args, UErrorCode* err); -#endif - -namespace { - -/*const UConverterSharedData _ISO2022Data;*/ -extern const UConverterSharedData _ISO2022JPData; - -#if !UCONFIG_ONLY_HTML_CONVERSION -extern const UConverterSharedData _ISO2022KRData; -extern const UConverterSharedData _ISO2022CNData; -#endif - -} // namespace - -/*************** Converter implementations ******************/ - -/* The purpose of this function is to get around gcc compiler warnings. */ -static inline void -fromUWriteUInt8(UConverter *cnv, - const char *bytes, int32_t length, - uint8_t **target, const char *targetLimit, - int32_t **offsets, - int32_t sourceIndex, - UErrorCode *pErrorCode) -{ - char *targetChars = (char *)*target; - ucnv_fromUWriteBytes(cnv, bytes, length, &targetChars, targetLimit, - offsets, sourceIndex, pErrorCode); - *target = (uint8_t*)targetChars; - -} - -static inline void -setInitialStateToUnicodeKR(UConverter* /*converter*/, UConverterDataISO2022 *myConverterData){ - if(myConverterData->version == 1) { - UConverter *cnv = myConverterData->currentConverter; - - cnv->toUnicodeStatus=0; /* offset */ - cnv->mode=0; /* state */ - cnv->toULength=0; /* byteIndex */ - } -} - -static inline void -setInitialStateFromUnicodeKR(UConverter* converter,UConverterDataISO2022 *myConverterData){ - /* in ISO-2022-KR the designator sequence appears only once - * in a file so we append it only once - */ - if( converter->charErrorBufferLength==0){ - - converter->charErrorBufferLength = 4; - converter->charErrorBuffer[0] = 0x1b; - converter->charErrorBuffer[1] = 0x24; - converter->charErrorBuffer[2] = 0x29; - converter->charErrorBuffer[3] = 0x43; - } - if(myConverterData->version == 1) { - UConverter *cnv = myConverterData->currentConverter; - - cnv->fromUChar32=0; - cnv->fromUnicodeStatus=1; /* prevLength */ - } -} - -static void U_CALLCONV -_ISO2022Open(UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *errorCode){ - - char myLocale[7]={' ',' ',' ',' ',' ',' ', '\0'}; - - cnv->extraInfo = uprv_malloc (sizeof (UConverterDataISO2022)); - if(cnv->extraInfo != NULL) { - UConverterNamePieces stackPieces; - UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; - UConverterDataISO2022 *myConverterData=(UConverterDataISO2022 *) cnv->extraInfo; - uint32_t version; - - stackArgs.onlyTestIsLoadable = pArgs->onlyTestIsLoadable; - - uprv_memset(myConverterData, 0, sizeof(UConverterDataISO2022)); - myConverterData->currentType = ASCII1; - cnv->fromUnicodeStatus =false; - if(pArgs->locale){ - uprv_strncpy(myLocale, pArgs->locale, sizeof(myLocale)-1); - } - version = pArgs->options & UCNV_OPTIONS_VERSION_MASK; - myConverterData->version = version; - if(myLocale[0]=='j' && (myLocale[1]=='a'|| myLocale[1]=='p') && - (myLocale[2]=='_' || myLocale[2]=='\0')) - { - /* open the required converters and cache them */ - if(version>MAX_JA_VERSION) { - // ICU 55 fails to open a converter for an unsupported version. - // Previously, it fell back to version 0, but that would yield - // unexpected behavior. - *errorCode = U_MISSING_RESOURCE_ERROR; - return; - } - if(jpCharsetMasks[version]&CSM(ISO8859_7)) { - myConverterData->myConverterArray[ISO8859_7] = - ucnv_loadSharedData("ISO8859_7", &stackPieces, &stackArgs, errorCode); - } - myConverterData->myConverterArray[JISX208] = - ucnv_loadSharedData("Shift-JIS", &stackPieces, &stackArgs, errorCode); - if(jpCharsetMasks[version]&CSM(JISX212)) { - myConverterData->myConverterArray[JISX212] = - ucnv_loadSharedData("jisx-212", &stackPieces, &stackArgs, errorCode); - } - if(jpCharsetMasks[version]&CSM(GB2312)) { - myConverterData->myConverterArray[GB2312] = - ucnv_loadSharedData("ibm-5478", &stackPieces, &stackArgs, errorCode); /* gb_2312_80-1 */ - } - if(jpCharsetMasks[version]&CSM(KSC5601)) { - myConverterData->myConverterArray[KSC5601] = - ucnv_loadSharedData("ksc_5601", &stackPieces, &stackArgs, errorCode); - } - - /* set the function pointers to appropriate functions */ - cnv->sharedData=(UConverterSharedData*)(&_ISO2022JPData); - uprv_strcpy(myConverterData->locale,"ja"); - - (void)uprv_strcpy(myConverterData->name,"ISO_2022,locale=ja,version="); - size_t len = uprv_strlen(myConverterData->name); - myConverterData->name[len]=(char)(myConverterData->version+(int)'0'); - myConverterData->name[len+1]='\0'; - } -#if !UCONFIG_ONLY_HTML_CONVERSION - else if(myLocale[0]=='k' && (myLocale[1]=='o'|| myLocale[1]=='r') && - (myLocale[2]=='_' || myLocale[2]=='\0')) - { - if(version>1) { - // ICU 55 fails to open a converter for an unsupported version. - // Previously, it fell back to version 0, but that would yield - // unexpected behavior. - *errorCode = U_MISSING_RESOURCE_ERROR; - return; - } - const char *cnvName; - if(version==1) { - cnvName="icu-internal-25546"; - } else { - cnvName="ibm-949"; - myConverterData->version=version=0; - } - if(pArgs->onlyTestIsLoadable) { - ucnv_canCreateConverter(cnvName, errorCode); /* errorCode carries result */ - uprv_free(cnv->extraInfo); - cnv->extraInfo=NULL; - return; - } else { - myConverterData->currentConverter=ucnv_open(cnvName, errorCode); - if (U_FAILURE(*errorCode)) { - _ISO2022Close(cnv); - return; - } - - if(version==1) { - (void)uprv_strcpy(myConverterData->name,"ISO_2022,locale=ko,version=1"); - uprv_memcpy(cnv->subChars, myConverterData->currentConverter->subChars, 4); - cnv->subCharLen = myConverterData->currentConverter->subCharLen; - }else{ - (void)uprv_strcpy(myConverterData->name,"ISO_2022,locale=ko,version=0"); - } - - /* initialize the state variables */ - setInitialStateToUnicodeKR(cnv, myConverterData); - setInitialStateFromUnicodeKR(cnv, myConverterData); - - /* set the function pointers to appropriate functions */ - cnv->sharedData=(UConverterSharedData*)&_ISO2022KRData; - uprv_strcpy(myConverterData->locale,"ko"); - } - } - else if(((myLocale[0]=='z' && myLocale[1]=='h') || (myLocale[0]=='c'&& myLocale[1]=='n'))&& - (myLocale[2]=='_' || myLocale[2]=='\0')) - { - if(version>2) { - // ICU 55 fails to open a converter for an unsupported version. - // Previously, it fell back to version 0, but that would yield - // unexpected behavior. - *errorCode = U_MISSING_RESOURCE_ERROR; - return; - } - - /* open the required converters and cache them */ - myConverterData->myConverterArray[GB2312_1] = - ucnv_loadSharedData("ibm-5478", &stackPieces, &stackArgs, errorCode); - if(version==1) { - myConverterData->myConverterArray[ISO_IR_165] = - ucnv_loadSharedData("iso-ir-165", &stackPieces, &stackArgs, errorCode); - } - myConverterData->myConverterArray[CNS_11643] = - ucnv_loadSharedData("cns-11643-1992", &stackPieces, &stackArgs, errorCode); - - - /* set the function pointers to appropriate functions */ - cnv->sharedData=(UConverterSharedData*)&_ISO2022CNData; - uprv_strcpy(myConverterData->locale,"cn"); - - if (version==0){ - myConverterData->version = 0; - (void)uprv_strcpy(myConverterData->name,"ISO_2022,locale=zh,version=0"); - }else if (version==1){ - myConverterData->version = 1; - (void)uprv_strcpy(myConverterData->name,"ISO_2022,locale=zh,version=1"); - }else { - myConverterData->version = 2; - (void)uprv_strcpy(myConverterData->name,"ISO_2022,locale=zh,version=2"); - } - } -#endif // !UCONFIG_ONLY_HTML_CONVERSION - else{ -#ifdef U_ENABLE_GENERIC_ISO_2022 - myConverterData->isFirstBuffer = true; - - /* append the UTF-8 escape sequence */ - cnv->charErrorBufferLength = 3; - cnv->charErrorBuffer[0] = 0x1b; - cnv->charErrorBuffer[1] = 0x25; - cnv->charErrorBuffer[2] = 0x42; - - cnv->sharedData=(UConverterSharedData*)&_ISO2022Data; - /* initialize the state variables */ - uprv_strcpy(myConverterData->name,"ISO_2022"); -#else - *errorCode = U_MISSING_RESOURCE_ERROR; - // Was U_UNSUPPORTED_ERROR but changed in ICU 55 to a more standard - // data loading error code. - return; -#endif - } - - cnv->maxBytesPerUChar=cnv->sharedData->staticData->maxBytesPerChar; - - if(U_FAILURE(*errorCode) || pArgs->onlyTestIsLoadable) { - _ISO2022Close(cnv); - } - } else { - *errorCode = U_MEMORY_ALLOCATION_ERROR; - } -} - - -static void U_CALLCONV -_ISO2022Close(UConverter *converter) { - UConverterDataISO2022* myData =(UConverterDataISO2022 *) (converter->extraInfo); - UConverterSharedData **array = myData->myConverterArray; - int32_t i; - - if (converter->extraInfo != NULL) { - /*close the array of converter pointers and free the memory*/ - for (i=0; icurrentConverter); - - if(!converter->isExtraLocal){ - uprv_free (converter->extraInfo); - converter->extraInfo = NULL; - } - } -} - -static void U_CALLCONV -_ISO2022Reset(UConverter *converter, UConverterResetChoice choice) { - UConverterDataISO2022 *myConverterData=(UConverterDataISO2022 *) (converter->extraInfo); - if(choice<=UCNV_RESET_TO_UNICODE) { - uprv_memset(&myConverterData->toU2022State, 0, sizeof(ISO2022State)); - myConverterData->key = 0; - myConverterData->isEmptySegment = false; - } - if(choice!=UCNV_RESET_TO_UNICODE) { - uprv_memset(&myConverterData->fromU2022State, 0, sizeof(ISO2022State)); - } -#ifdef U_ENABLE_GENERIC_ISO_2022 - if(myConverterData->locale[0] == 0){ - if(choice<=UCNV_RESET_TO_UNICODE) { - myConverterData->isFirstBuffer = true; - myConverterData->key = 0; - if (converter->mode == UCNV_SO){ - ucnv_close (myConverterData->currentConverter); - myConverterData->currentConverter=NULL; - } - converter->mode = UCNV_SI; - } - if(choice!=UCNV_RESET_TO_UNICODE) { - /* re-append UTF-8 escape sequence */ - converter->charErrorBufferLength = 3; - converter->charErrorBuffer[0] = 0x1b; - converter->charErrorBuffer[1] = 0x28; - converter->charErrorBuffer[2] = 0x42; - } - } - else -#endif - { - /* reset the state variables */ - if(myConverterData->locale[0] == 'k'){ - if(choice<=UCNV_RESET_TO_UNICODE) { - setInitialStateToUnicodeKR(converter, myConverterData); - } - if(choice!=UCNV_RESET_TO_UNICODE) { - setInitialStateFromUnicodeKR(converter, myConverterData); - } - } - } -} - -U_CDECL_BEGIN - -static const char * U_CALLCONV -_ISO2022getName(const UConverter* cnv){ - if(cnv->extraInfo){ - UConverterDataISO2022* myData= (UConverterDataISO2022*)cnv->extraInfo; - return myData->name; - } - return NULL; -} - -U_CDECL_END - - -/*************** to unicode *******************/ -/**************************************************************************** - * Recognized escape sequences are - * (B ASCII - * .A ISO-8859-1 - * .F ISO-8859-7 - * (J JISX-201 - * (I JISX-201 - * $B JISX-208 - * $@ JISX-208 - * $(D JISX-212 - * $A GB2312 - * $(C KSC5601 - */ -static const int8_t nextStateToUnicodeJP[MAX_STATES_2022]= { -/* 0 1 2 3 4 5 6 7 8 9 */ - INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,SS2_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE - ,ASCII ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,JISX201 ,HWKANA_7BIT ,JISX201 ,INVALID_STATE - ,INVALID_STATE ,INVALID_STATE ,JISX208 ,GB2312 ,JISX208 ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE - ,ISO8859_1 ,ISO8859_7 ,JISX208 ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,KSC5601 ,JISX212 ,INVALID_STATE - ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE - ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE - ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE - ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE -}; - -#if !UCONFIG_ONLY_HTML_CONVERSION -/*************** to unicode *******************/ -static const int8_t nextStateToUnicodeCN[MAX_STATES_2022]= { -/* 0 1 2 3 4 5 6 7 8 9 */ - INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,SS2_STATE ,SS3_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE - ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE - ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE - ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE - ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,GB2312_1 ,INVALID_STATE ,ISO_IR_165 - ,CNS_11643_1 ,CNS_11643_2 ,CNS_11643_3 ,CNS_11643_4 ,CNS_11643_5 ,CNS_11643_6 ,CNS_11643_7 ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE - ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE - ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE -}; -#endif - - -static UCNV_TableStates_2022 -getKey_2022(char c,int32_t* key,int32_t* offset){ - int32_t togo; - int32_t low = 0; - int32_t hi = MAX_STATES_2022; - int32_t oldmid=0; - - togo = normalize_esq_chars_2022[(uint8_t)c]; - if(togo == 0) { - /* not a valid character anywhere in an escape sequence */ - *key = 0; - *offset = 0; - return INVALID_2022; - } - togo = (*key << 5) + togo; - - while (hi != low) /*binary search*/{ - - int32_t mid = (hi+low) >> 1; /*Finds median*/ - - if (mid == oldmid) - break; - - if (escSeqStateTable_Key_2022[mid] > togo){ - hi = mid; - } - else if (escSeqStateTable_Key_2022[mid] < togo){ - low = mid; - } - else /*we found it*/{ - *key = togo; - *offset = mid; - return (UCNV_TableStates_2022)escSeqStateTable_Value_2022[mid]; - } - oldmid = mid; - - } - - *key = 0; - *offset = 0; - return INVALID_2022; -} - -/*runs through a state machine to determine the escape sequence - codepage correspondence - */ -static void -changeState_2022(UConverter* _this, - const char** source, - const char* sourceLimit, - Variant2022 var, - UErrorCode* err){ - UCNV_TableStates_2022 value; - UConverterDataISO2022* myData2022 = ((UConverterDataISO2022*)_this->extraInfo); - uint32_t key = myData2022->key; - int32_t offset = 0; - int8_t initialToULength = _this->toULength; - char c; - - value = VALID_NON_TERMINAL_2022; - while (*source < sourceLimit) { - c = *(*source)++; - _this->toUBytes[_this->toULength++]=(uint8_t)c; - value = getKey_2022(c,(int32_t *) &key, &offset); - - switch (value){ - - case VALID_NON_TERMINAL_2022 : - /* continue with the loop */ - break; - - case VALID_TERMINAL_2022: - key = 0; - goto DONE; - - case INVALID_2022: - goto DONE; - - case VALID_MAYBE_TERMINAL_2022: -#ifdef U_ENABLE_GENERIC_ISO_2022 - /* ESC ( B is ambiguous only for ISO_2022 itself */ - if(var == ISO_2022) { - /* discard toUBytes[] for ESC ( B because this sequence is correct and complete */ - _this->toULength = 0; - - /* TODO need to indicate that ESC ( B was seen; if failure, then need to replay from source or from MBCS-style replay */ - - /* continue with the loop */ - value = VALID_NON_TERMINAL_2022; - break; - } else -#endif - { - /* not ISO_2022 itself, finish here */ - value = VALID_TERMINAL_2022; - key = 0; - goto DONE; - } - } - } - -DONE: - myData2022->key = key; - - if (value == VALID_NON_TERMINAL_2022) { - /* indicate that the escape sequence is incomplete: key!=0 */ - return; - } else if (value == INVALID_2022 ) { - *err = U_ILLEGAL_ESCAPE_SEQUENCE; - } else /* value == VALID_TERMINAL_2022 */ { - switch(var){ -#ifdef U_ENABLE_GENERIC_ISO_2022 - case ISO_2022: - { - const char *chosenConverterName = escSeqStateTable_Result_2022[offset]; - if(chosenConverterName == NULL) { - /* SS2 or SS3 */ - *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; - _this->toUCallbackReason = UCNV_UNASSIGNED; - return; - } - - _this->mode = UCNV_SI; - ucnv_close(myData2022->currentConverter); - myData2022->currentConverter = myUConverter = ucnv_open(chosenConverterName, err); - if(U_SUCCESS(*err)) { - myUConverter->fromCharErrorBehaviour = UCNV_TO_U_CALLBACK_STOP; - _this->mode = UCNV_SO; - } - break; - } -#endif - case ISO_2022_JP: - { - StateEnum tempState=(StateEnum)nextStateToUnicodeJP[offset]; - switch(tempState) { - case INVALID_STATE: - *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; - break; - case SS2_STATE: - if(myData2022->toU2022State.cs[2]!=0) { - if(myData2022->toU2022State.g<2) { - myData2022->toU2022State.prevG=myData2022->toU2022State.g; - } - myData2022->toU2022State.g=2; - } else { - /* illegal to have SS2 before a matching designator */ - *err = U_ILLEGAL_ESCAPE_SEQUENCE; - } - break; - /* case SS3_STATE: not used in ISO-2022-JP-x */ - case ISO8859_1: - case ISO8859_7: - if((jpCharsetMasks[myData2022->version] & CSM(tempState)) == 0) { - *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; - } else { - /* G2 charset for SS2 */ - myData2022->toU2022State.cs[2]=(int8_t)tempState; - } - break; - default: - if((jpCharsetMasks[myData2022->version] & CSM(tempState)) == 0) { - *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; - } else { - /* G0 charset */ - myData2022->toU2022State.cs[0]=(int8_t)tempState; - } - break; - } - } - break; -#if !UCONFIG_ONLY_HTML_CONVERSION - case ISO_2022_CN: - { - StateEnum tempState=(StateEnum)nextStateToUnicodeCN[offset]; - switch(tempState) { - case INVALID_STATE: - *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; - break; - case SS2_STATE: - if(myData2022->toU2022State.cs[2]!=0) { - if(myData2022->toU2022State.g<2) { - myData2022->toU2022State.prevG=myData2022->toU2022State.g; - } - myData2022->toU2022State.g=2; - } else { - /* illegal to have SS2 before a matching designator */ - *err = U_ILLEGAL_ESCAPE_SEQUENCE; - } - break; - case SS3_STATE: - if(myData2022->toU2022State.cs[3]!=0) { - if(myData2022->toU2022State.g<2) { - myData2022->toU2022State.prevG=myData2022->toU2022State.g; - } - myData2022->toU2022State.g=3; - } else { - /* illegal to have SS3 before a matching designator */ - *err = U_ILLEGAL_ESCAPE_SEQUENCE; - } - break; - case ISO_IR_165: - if(myData2022->version==0) { - *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; - break; - } - U_FALLTHROUGH; - case GB2312_1: - U_FALLTHROUGH; - case CNS_11643_1: - myData2022->toU2022State.cs[1]=(int8_t)tempState; - break; - case CNS_11643_2: - myData2022->toU2022State.cs[2]=(int8_t)tempState; - break; - default: - /* other CNS 11643 planes */ - if(myData2022->version==0) { - *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; - } else { - myData2022->toU2022State.cs[3]=(int8_t)tempState; - } - break; - } - } - break; - case ISO_2022_KR: - if(offset==0x30){ - /* nothing to be done, just accept this one escape sequence */ - } else { - *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; - } - break; -#endif // !UCONFIG_ONLY_HTML_CONVERSION - - default: - *err = U_ILLEGAL_ESCAPE_SEQUENCE; - break; - } - } - if(U_SUCCESS(*err)) { - _this->toULength = 0; - } else if(*err==U_ILLEGAL_ESCAPE_SEQUENCE) { - if(_this->toULength>1) { - /* - * Ticket 5691: consistent illegal sequences: - * - We include at least the first byte (ESC) in the illegal sequence. - * - If any of the non-initial bytes could be the start of a character, - * we stop the illegal sequence before the first one of those. - * In escape sequences, all following bytes are "printable", that is, - * unless they are completely illegal (>7f in SBCS, outside 21..7e in DBCS), - * they are valid single/lead bytes. - * For simplicity, we always only report the initial ESC byte as the - * illegal sequence and back out all other bytes we looked at. - */ - /* Back out some bytes. */ - int8_t backOutDistance=_this->toULength-1; - int8_t bytesFromThisBuffer=_this->toULength-initialToULength; - if(backOutDistance<=bytesFromThisBuffer) { - /* same as initialToULength<=1 */ - *source-=backOutDistance; - } else { - /* Back out bytes from the previous buffer: Need to replay them. */ - _this->preToULength=(int8_t)(bytesFromThisBuffer-backOutDistance); - /* same as -(initialToULength-1) */ - /* preToULength is negative! */ - uprv_memcpy(_this->preToU, _this->toUBytes+1, -_this->preToULength); - *source-=bytesFromThisBuffer; - } - _this->toULength=1; - } - } else if(*err==U_UNSUPPORTED_ESCAPE_SEQUENCE) { - _this->toUCallbackReason = UCNV_UNASSIGNED; - } -} - -#if !UCONFIG_ONLY_HTML_CONVERSION -/*Checks the characters of the buffer against valid 2022 escape sequences -*if the match we return a pointer to the initial start of the sequence otherwise -*we return sourceLimit -*/ -/*for 2022 looks ahead in the stream - *to determine the longest possible convertible - *data stream - */ -static inline const char* -getEndOfBuffer_2022(const char** source, - const char* sourceLimit, - UBool /*flush*/){ - - const char* mySource = *source; - -#ifdef U_ENABLE_GENERIC_ISO_2022 - if (*source >= sourceLimit) - return sourceLimit; - - do{ - - if (*mySource == ESC_2022){ - int8_t i; - int32_t key = 0; - int32_t offset; - UCNV_TableStates_2022 value = VALID_NON_TERMINAL_2022; - - /* Kludge: I could not - * figure out the reason for validating an escape sequence - * twice - once here and once in changeState_2022(). - * is it possible to have an ESC character in a ISO2022 - * byte stream which is valid in a code page? Is it legal? - */ - for (i=0; - (mySource+i < sourceLimit)&&(value == VALID_NON_TERMINAL_2022); - i++) { - value = getKey_2022(*(mySource+i), &key, &offset); - } - if (value > 0 || *mySource==ESC_2022) - return mySource; - - if ((value == VALID_NON_TERMINAL_2022)&&(!flush) ) - return sourceLimit; - } - }while (++mySource < sourceLimit); - - return sourceLimit; -#else - while(mySource < sourceLimit && *mySource != ESC_2022) { - ++mySource; - } - return mySource; -#endif -} -#endif - -/* This inline function replicates code in _MBCSFromUChar32() function in ucnvmbcs.c - * any future change in _MBCSFromUChar32() function should be reflected here. - * @return number of bytes in *value; negative number if fallback; 0 if no mapping - */ -static inline int32_t -MBCS_FROM_UCHAR32_ISO2022(UConverterSharedData* sharedData, - UChar32 c, - uint32_t* value, - UBool useFallback, - int outputType) -{ - const int32_t *cx; - const uint16_t *table; - uint32_t stage2Entry; - uint32_t myValue; - int32_t length; - const uint8_t *p; - /* - * TODO(markus): Use and require new, faster MBCS conversion table structures. - * Use internal version of ucnv_open() that verifies that the new structures are available, - * else U_INTERNAL_PROGRAM_ERROR. - */ - /* BMP-only codepages are stored without stage 1 entries for supplementary code points */ - if(c<0x10000 || (sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY)) { - table=sharedData->mbcs.fromUnicodeTable; - stage2Entry=MBCS_STAGE_2_FROM_U(table, c); - /* get the bytes and the length for the output */ - if(outputType==MBCS_OUTPUT_2){ - myValue=MBCS_VALUE_2_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); - if(myValue<=0xff) { - length=1; - } else { - length=2; - } - } else /* outputType==MBCS_OUTPUT_3 */ { - p=MBCS_POINTER_3_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); - myValue=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; - if(myValue<=0xff) { - length=1; - } else if(myValue<=0xffff) { - length=2; - } else { - length=3; - } - } - /* is this code point assigned, or do we use fallbacks? */ - if((stage2Entry&(1<<(16+(c&0xf))))!=0) { - /* assigned */ - *value=myValue; - return length; - } else if(FROM_U_USE_FALLBACK(useFallback, c) && myValue!=0) { - /* - * We allow a 0 byte output if the "assigned" bit is set for this entry. - * There is no way with this data structure for fallback output - * to be a zero byte. - */ - *value=myValue; - return -length; - } - } - - cx=sharedData->mbcs.extIndexes; - if(cx!=NULL) { - return ucnv_extSimpleMatchFromU(cx, c, value, useFallback); - } - - /* unassigned */ - return 0; -} - -/* This inline function replicates code in _MBCSSingleFromUChar32() function in ucnvmbcs.c - * any future change in _MBCSSingleFromUChar32() function should be reflected here. - * @param retval pointer to output byte - * @return 1 roundtrip byte 0 no mapping -1 fallback byte - */ -static inline int32_t -MBCS_SINGLE_FROM_UCHAR32(UConverterSharedData* sharedData, - UChar32 c, - uint32_t* retval, - UBool useFallback) -{ - const uint16_t *table; - int32_t value; - /* BMP-only codepages are stored without stage 1 entries for supplementary code points */ - if(c>=0x10000 && !(sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY)) { - return 0; - } - /* convert the Unicode code point in c into codepage bytes (same as in _MBCSFromUnicodeWithOffsets) */ - table=sharedData->mbcs.fromUnicodeTable; - /* get the byte for the output */ - value=MBCS_SINGLE_RESULT_FROM_U(table, (uint16_t *)sharedData->mbcs.fromUnicodeBytes, c); - /* is this code point assigned, or do we use fallbacks? */ - *retval=(uint32_t)(value&0xff); - if(value>=0xf00) { - return 1; /* roundtrip */ - } else if(useFallback ? value>=0x800 : value>=0xc00) { - return -1; /* fallback taken */ - } else { - return 0; /* no mapping */ - } -} - -/* - * Check that the result is a 2-byte value with each byte in the range A1..FE - * (strict EUC DBCS) before accepting it and subtracting 0x80 from each byte - * to move it to the ISO 2022 range 21..7E. - * Return 0 if out of range. - */ -static inline uint32_t -_2022FromGR94DBCS(uint32_t value) { - if( (uint16_t)(value - 0xa1a1) <= (0xfefe - 0xa1a1) && - (uint8_t)(value - 0xa1) <= (0xfe - 0xa1) - ) { - return value - 0x8080; /* shift down to 21..7e byte range */ - } else { - return 0; /* not valid for ISO 2022 */ - } -} - -#if 0 /* 5691: Call sites now check for validity. They can just += 0x8080 after that. */ -/* - * This method does the reverse of _2022FromGR94DBCS(). Given the 2022 code point, it returns the - * 2 byte value that is in the range A1..FE for each byte. Otherwise it returns the 2022 code point - * unchanged. - */ -static inline uint32_t -_2022ToGR94DBCS(uint32_t value) { - uint32_t returnValue = value + 0x8080; - if( (uint16_t)(returnValue - 0xa1a1) <= (0xfefe - 0xa1a1) && - (uint8_t)(returnValue - 0xa1) <= (0xfe - 0xa1)) { - return returnValue; - } else { - return value; - } -} -#endif - -#ifdef U_ENABLE_GENERIC_ISO_2022 - -/********************************************************************************** -* ISO-2022 Converter -* -* -*/ - -static void U_CALLCONV -T_UConverter_toUnicode_ISO_2022_OFFSETS_LOGIC(UConverterToUnicodeArgs* args, - UErrorCode* err){ - const char* mySourceLimit, *realSourceLimit; - const char* sourceStart; - const UChar* myTargetStart; - UConverter* saveThis; - UConverterDataISO2022* myData; - int8_t length; - - saveThis = args->converter; - myData=((UConverterDataISO2022*)(saveThis->extraInfo)); - - realSourceLimit = args->sourceLimit; - while (args->source < realSourceLimit) { - if(myData->key == 0) { /* are we in the middle of an escape sequence? */ - /*Find the end of the buffer e.g : Next Escape Seq | end of Buffer*/ - mySourceLimit = getEndOfBuffer_2022(&(args->source), realSourceLimit, args->flush); - - if(args->source < mySourceLimit) { - if(myData->currentConverter==NULL) { - myData->currentConverter = ucnv_open("ASCII",err); - if(U_FAILURE(*err)){ - return; - } - - myData->currentConverter->fromCharErrorBehaviour = UCNV_TO_U_CALLBACK_STOP; - saveThis->mode = UCNV_SO; - } - - /* convert to before the ESC or until the end of the buffer */ - myData->isFirstBuffer=false; - sourceStart = args->source; - myTargetStart = args->target; - args->converter = myData->currentConverter; - ucnv_toUnicode(args->converter, - &args->target, - args->targetLimit, - &args->source, - mySourceLimit, - args->offsets, - (UBool)(args->flush && mySourceLimit == realSourceLimit), - err); - args->converter = saveThis; - - if (*err == U_BUFFER_OVERFLOW_ERROR) { - /* move the overflow buffer */ - length = saveThis->UCharErrorBufferLength = myData->currentConverter->UCharErrorBufferLength; - myData->currentConverter->UCharErrorBufferLength = 0; - if(length > 0) { - uprv_memcpy(saveThis->UCharErrorBuffer, - myData->currentConverter->UCharErrorBuffer, - length*U_SIZEOF_UCHAR); - } - return; - } - - /* - * At least one of: - * -Error while converting - * -Done with entire buffer - * -Need to write offsets or update the current offset - * (leave that up to the code in ucnv.c) - * - * or else we just stopped at an ESC byte and continue with changeState_2022() - */ - if (U_FAILURE(*err) || - (args->source == realSourceLimit) || - (args->offsets != NULL && (args->target != myTargetStart || args->source != sourceStart) || - (mySourceLimit < realSourceLimit && myData->currentConverter->toULength > 0)) - ) { - /* copy partial or error input for truncated detection and error handling */ - if(U_FAILURE(*err)) { - length = saveThis->invalidCharLength = myData->currentConverter->invalidCharLength; - if(length > 0) { - uprv_memcpy(saveThis->invalidCharBuffer, myData->currentConverter->invalidCharBuffer, length); - } - } else { - length = saveThis->toULength = myData->currentConverter->toULength; - if(length > 0) { - uprv_memcpy(saveThis->toUBytes, myData->currentConverter->toUBytes, length); - if(args->source < mySourceLimit) { - *err = U_TRUNCATED_CHAR_FOUND; /* truncated input before ESC */ - } - } - } - return; - } - } - } - - sourceStart = args->source; - changeState_2022(args->converter, - &(args->source), - realSourceLimit, - ISO_2022, - err); - if (U_FAILURE(*err) || (args->source != sourceStart && args->offsets != NULL)) { - /* let the ucnv.c code update its current offset */ - return; - } - } -} - -#endif - -/* - * To Unicode Callback helper function - */ -static void -toUnicodeCallback(UConverter *cnv, - const uint32_t sourceChar, const uint32_t targetUniChar, - UErrorCode* err){ - if(sourceChar>0xff){ - cnv->toUBytes[0] = (uint8_t)(sourceChar>>8); - cnv->toUBytes[1] = (uint8_t)sourceChar; - cnv->toULength = 2; - } - else{ - cnv->toUBytes[0] =(char) sourceChar; - cnv->toULength = 1; - } - - if(targetUniChar == (missingCharMarker-1/*0xfffe*/)){ - *err = U_INVALID_CHAR_FOUND; - } - else{ - *err = U_ILLEGAL_CHAR_FOUND; - } -} - -/**************************************ISO-2022-JP*************************************************/ - -/************************************** IMPORTANT ************************************************** -* The UConverter_fromUnicode_ISO2022_JP converter does not use ucnv_fromUnicode() functions for SBCS,DBCS and -* MBCS; instead, the values are obtained directly by calling _MBCSFromUChar32(). -* The converter iterates over each Unicode codepoint -* to obtain the equivalent codepoints from the codepages supported. Since the source buffer is -* processed one char at a time it would make sense to reduce the extra processing a canned converter -* would do as far as possible. -* -* If the implementation of these macros or structure of sharedData struct change in the future, make -* sure that ISO-2022 is also changed. -*************************************************************************************************** -*/ - -/*************************************************************************************************** -* Rules for ISO-2022-jp encoding -* (i) Escape sequences must be fully contained within a line they should not -* span new lines or CRs -* (ii) If the last character on a line is represented by two bytes then an ASCII or -* JIS-Roman character escape sequence should follow before the line terminates -* (iii) If the first character on the line is represented by two bytes then a two -* byte character escape sequence should precede it -* (iv) If no escape sequence is encountered then the characters are ASCII -* (v) Latin(ISO-8859-1) and Greek(ISO-8859-7) characters must be designated to G2, -* and invoked with SS2 (ESC N). -* (vi) If there is any G0 designation in text, there must be a switch to -* ASCII or to JIS X 0201-Roman before a space character (but not -* necessarily before "ESC 4/14 2/0" or "ESC N ' '") or control -* characters such as tab or CRLF. -* (vi) Supported encodings: -* ASCII, JISX201, JISX208, JISX212, GB2312, KSC5601, ISO-8859-1,ISO-8859-7 -* -* source : RFC-1554 -* -* JISX201, JISX208,JISX212 : new .cnv data files created -* KSC5601 : alias to ibm-949 mapping table -* GB2312 : alias to ibm-1386 mapping table -* ISO-8859-1 : Algorithmic implemented as LATIN1 case -* ISO-8859-7 : alias to ibm-9409 mapping table -*/ - -/* preference order of JP charsets */ -static const StateEnum jpCharsetPref[]={ - ASCII, - JISX201, - ISO8859_1, - JISX208, - ISO8859_7, - JISX212, - GB2312, - KSC5601, - HWKANA_7BIT -}; - -/* - * The escape sequences must be in order of the enum constants like JISX201 = 3, - * not in order of jpCharsetPref[]! - */ -static const char escSeqChars[][6] ={ - "\x1B\x28\x42", /* (B ASCII */ - "\x1B\x2E\x41", /* .A ISO-8859-1 */ - "\x1B\x2E\x46", /* .F ISO-8859-7 */ - "\x1B\x28\x4A", /* (J JISX-201 */ - "\x1B\x24\x42", /* $B JISX-208 */ - "\x1B\x24\x28\x44", /* $(D JISX-212 */ - "\x1B\x24\x41", /* $A GB2312 */ - "\x1B\x24\x28\x43", /* $(C KSC5601 */ - "\x1B\x28\x49" /* (I HWKANA_7BIT */ - -}; -static const int8_t escSeqCharsLen[] ={ - 3, /* length of (B ASCII */ - 3, /* length of .A ISO-8859-1 */ - 3, /* length of .F ISO-8859-7 */ - 3, /* length of (J JISX-201 */ - 3, /* length of $B JISX-208 */ - 4, /* length of $(D JISX-212 */ - 3, /* length of $A GB2312 */ - 4, /* length of $(C KSC5601 */ - 3 /* length of (I HWKANA_7BIT */ -}; - -/* -* The iteration over various code pages works this way: -* i) Get the currentState from myConverterData->currentState -* ii) Check if the character is mapped to a valid character in the currentState -* Yes -> a) set the initIterState to currentState -* b) remain in this state until an invalid character is found -* No -> a) go to the next code page and find the character -* iii) Before changing the state increment the current state check if the current state -* is equal to the intitIteration state -* Yes -> A character that cannot be represented in any of the supported encodings -* break and return a U_INVALID_CHARACTER error -* No -> Continue and find the character in next code page -* -* -* TODO: Implement a priority technique where the users are allowed to set the priority of code pages -*/ - -/* Map 00..7F to Unicode according to JIS X 0201. */ -static inline uint32_t -jisx201ToU(uint32_t value) { - if(value < 0x5c) { - return value; - } else if(value == 0x5c) { - return 0xa5; - } else if(value == 0x7e) { - return 0x203e; - } else /* value <= 0x7f */ { - return value; - } -} - -/* Map Unicode to 00..7F according to JIS X 0201. Return U+FFFE if unmappable. */ -static inline uint32_t -jisx201FromU(uint32_t value) { - if(value<=0x7f) { - if(value!=0x5c && value!=0x7e) { - return value; - } - } else if(value==0xa5) { - return 0x5c; - } else if(value==0x203e) { - return 0x7e; - } - return 0xfffe; -} - -/* - * Take a valid Shift-JIS byte pair, check that it is in the range corresponding - * to JIS X 0208, and convert it to a pair of 21..7E bytes. - * Return 0 if the byte pair is out of range. - */ -static inline uint32_t -_2022FromSJIS(uint32_t value) { - uint8_t trail; - - if(value > 0xEFFC) { - return 0; /* beyond JIS X 0208 */ - } - - trail = (uint8_t)value; - - value &= 0xff00; /* lead byte */ - if(value <= 0x9f00) { - value -= 0x7000; - } else /* 0xe000 <= value <= 0xef00 */ { - value -= 0xb000; - } - value <<= 1; - - if(trail <= 0x9e) { - value -= 0x100; - if(trail <= 0x7e) { - value |= trail - 0x1f; - } else { - value |= trail - 0x20; - } - } else /* trail <= 0xfc */ { - value |= trail - 0x7e; - } - return value; -} - -/* - * Convert a pair of JIS X 0208 21..7E bytes to Shift-JIS. - * If either byte is outside 21..7E make sure that the result is not valid - * for Shift-JIS so that the converter catches it. - * Some invalid byte values already turn into equally invalid Shift-JIS - * byte values and need not be tested explicitly. - */ -static inline void -_2022ToSJIS(uint8_t c1, uint8_t c2, char bytes[2]) { - if(c1&1) { - ++c1; - if(c2 <= 0x5f) { - c2 += 0x1f; - } else if(c2 <= 0x7e) { - c2 += 0x20; - } else { - c2 = 0; /* invalid */ - } - } else { - if((uint8_t)(c2-0x21) <= ((0x7e)-0x21)) { - c2 += 0x7e; - } else { - c2 = 0; /* invalid */ - } - } - c1 >>= 1; - if(c1 <= 0x2f) { - c1 += 0x70; - } else if(c1 <= 0x3f) { - c1 += 0xb0; - } else { - c1 = 0; /* invalid */ - } - bytes[0] = (char)c1; - bytes[1] = (char)c2; -} - -/* - * JIS X 0208 has fallbacks from Unicode half-width Katakana to full-width (DBCS) - * Katakana. - * Now that we use a Shift-JIS table for JIS X 0208 we need to hardcode these fallbacks - * because Shift-JIS roundtrips half-width Katakana to single bytes. - * These were the only fallbacks in ICU's jisx-208.ucm file. - */ -static const uint16_t hwkana_fb[HWKANA_END - HWKANA_START + 1] = { - 0x2123, /* U+FF61 */ - 0x2156, - 0x2157, - 0x2122, - 0x2126, - 0x2572, - 0x2521, - 0x2523, - 0x2525, - 0x2527, - 0x2529, - 0x2563, - 0x2565, - 0x2567, - 0x2543, - 0x213C, /* U+FF70 */ - 0x2522, - 0x2524, - 0x2526, - 0x2528, - 0x252A, - 0x252B, - 0x252D, - 0x252F, - 0x2531, - 0x2533, - 0x2535, - 0x2537, - 0x2539, - 0x253B, - 0x253D, - 0x253F, /* U+FF80 */ - 0x2541, - 0x2544, - 0x2546, - 0x2548, - 0x254A, - 0x254B, - 0x254C, - 0x254D, - 0x254E, - 0x254F, - 0x2552, - 0x2555, - 0x2558, - 0x255B, - 0x255E, - 0x255F, /* U+FF90 */ - 0x2560, - 0x2561, - 0x2562, - 0x2564, - 0x2566, - 0x2568, - 0x2569, - 0x256A, - 0x256B, - 0x256C, - 0x256D, - 0x256F, - 0x2573, - 0x212B, - 0x212C /* U+FF9F */ -}; - -static void U_CALLCONV -UConverter_fromUnicode_ISO_2022_JP_OFFSETS_LOGIC(UConverterFromUnicodeArgs* args, UErrorCode* err) { - UConverter *cnv = args->converter; - UConverterDataISO2022 *converterData; - ISO2022State *pFromU2022State; - uint8_t *target = (uint8_t *) args->target; - const uint8_t *targetLimit = (const uint8_t *) args->targetLimit; - const UChar* source = args->source; - const UChar* sourceLimit = args->sourceLimit; - int32_t* offsets = args->offsets; - UChar32 sourceChar; - char buffer[8]; - int32_t len, outLen; - int8_t choices[10]; - int32_t choiceCount; - uint32_t targetValue = 0; - UBool useFallback; - - int32_t i; - int8_t cs, g; - - /* set up the state */ - converterData = (UConverterDataISO2022*)cnv->extraInfo; - pFromU2022State = &converterData->fromU2022State; - - choiceCount = 0; - - /* check if the last codepoint of previous buffer was a lead surrogate*/ - if((sourceChar = cnv->fromUChar32)!=0 && target< targetLimit) { - goto getTrail; - } - - while(source < sourceLimit) { - if(target < targetLimit) { - - sourceChar = *(source++); - /*check if the char is a First surrogate*/ - if(U16_IS_SURROGATE(sourceChar)) { - if(U16_IS_SURROGATE_LEAD(sourceChar)) { -getTrail: - /*look ahead to find the trail surrogate*/ - if(source < sourceLimit) { - /* test the following code unit */ - UChar trail=(UChar) *source; - if(U16_IS_TRAIL(trail)) { - source++; - sourceChar=U16_GET_SUPPLEMENTARY(sourceChar, trail); - cnv->fromUChar32=0x00; - /* convert this supplementary code point */ - /* exit this condition tree */ - } else { - /* this is an unmatched lead code unit (1st surrogate) */ - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - cnv->fromUChar32=sourceChar; - break; - } - } else { - /* no more input */ - cnv->fromUChar32=sourceChar; - break; - } - } else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - cnv->fromUChar32=sourceChar; - break; - } - } - - /* do not convert SO/SI/ESC */ - if(IS_2022_CONTROL(sourceChar)) { - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - cnv->fromUChar32=sourceChar; - break; - } - - /* do the conversion */ - - if(choiceCount == 0) { - uint16_t csm; - - /* - * The csm variable keeps track of which charsets are allowed - * and not used yet while building the choices[]. - */ - csm = jpCharsetMasks[converterData->version]; - choiceCount = 0; - - /* JIS7/8: try single-byte half-width Katakana before JISX208 */ - if(converterData->version == 3 || converterData->version == 4) { - choices[choiceCount++] = (int8_t)HWKANA_7BIT; - } - /* Do not try single-byte half-width Katakana for other versions. */ - csm &= ~CSM(HWKANA_7BIT); - - /* try the current G0 charset */ - choices[choiceCount++] = cs = pFromU2022State->cs[0]; - csm &= ~CSM(cs); - - /* try the current G2 charset */ - if((cs = pFromU2022State->cs[2]) != 0) { - choices[choiceCount++] = cs; - csm &= ~CSM(cs); - } - - /* try all the other possible charsets */ - for(i = 0; i < UPRV_LENGTHOF(jpCharsetPref); ++i) { - cs = (int8_t)jpCharsetPref[i]; - if(CSM(cs) & csm) { - choices[choiceCount++] = cs; - csm &= ~CSM(cs); - } - } - } - - cs = g = 0; - /* - * len==0: no mapping found yet - * len<0: found a fallback result: continue looking for a roundtrip but no further fallbacks - * len>0: found a roundtrip result, done - */ - len = 0; - /* - * We will turn off useFallback after finding a fallback, - * but we still get fallbacks from PUA code points as usual. - * Therefore, we will also need to check that we don't overwrite - * an early fallback with a later one. - */ - useFallback = cnv->useFallback; - - for(i = 0; i < choiceCount && len <= 0; ++i) { - uint32_t value; - int32_t len2; - int8_t cs0 = choices[i]; - switch(cs0) { - case ASCII: - if(sourceChar <= 0x7f) { - targetValue = (uint32_t)sourceChar; - len = 1; - cs = cs0; - g = 0; - } - break; - case ISO8859_1: - if(GR96_START <= sourceChar && sourceChar <= GR96_END) { - targetValue = (uint32_t)sourceChar - 0x80; - len = 1; - cs = cs0; - g = 2; - } - break; - case HWKANA_7BIT: - if((uint32_t)(sourceChar - HWKANA_START) <= (HWKANA_END - HWKANA_START)) { - if(converterData->version==3) { - /* JIS7: use G1 (SO) */ - /* Shift U+FF61..U+FF9F to bytes 21..5F. */ - targetValue = (uint32_t)(sourceChar - (HWKANA_START - 0x21)); - len = 1; - pFromU2022State->cs[1] = cs = cs0; /* do not output an escape sequence */ - g = 1; - } else if(converterData->version==4) { - /* JIS8: use 8-bit bytes with any single-byte charset, see escape sequence output below */ - /* Shift U+FF61..U+FF9F to bytes A1..DF. */ - targetValue = (uint32_t)(sourceChar - (HWKANA_START - 0xa1)); - len = 1; - - cs = pFromU2022State->cs[0]; - if(IS_JP_DBCS(cs)) { - /* switch from a DBCS charset to JISX201 */ - cs = (int8_t)JISX201; - } - /* else stay in the current G0 charset */ - g = 0; - } - /* else do not use HWKANA_7BIT with other versions */ - } - break; - case JISX201: - /* G0 SBCS */ - value = jisx201FromU(sourceChar); - if(value <= 0x7f) { - targetValue = value; - len = 1; - cs = cs0; - g = 0; - useFallback = false; - } - break; - case JISX208: - /* G0 DBCS from Shift-JIS table */ - len2 = MBCS_FROM_UCHAR32_ISO2022( - converterData->myConverterArray[cs0], - sourceChar, &value, - useFallback, MBCS_OUTPUT_2); - if(len2 == 2 || (len2 == -2 && len == 0)) { /* only accept DBCS: abs(len)==2 */ - value = _2022FromSJIS(value); - if(value != 0) { - targetValue = value; - len = len2; - cs = cs0; - g = 0; - useFallback = false; - } - } else if(len == 0 && useFallback && - (uint32_t)(sourceChar - HWKANA_START) <= (HWKANA_END - HWKANA_START)) { - targetValue = hwkana_fb[sourceChar - HWKANA_START]; - len = -2; - cs = cs0; - g = 0; - useFallback = false; - } - break; - case ISO8859_7: - /* G0 SBCS forced to 7-bit output */ - len2 = MBCS_SINGLE_FROM_UCHAR32( - converterData->myConverterArray[cs0], - sourceChar, &value, - useFallback); - if(len2 != 0 && !(len2 < 0 && len != 0) && GR96_START <= value && value <= GR96_END) { - targetValue = value - 0x80; - len = len2; - cs = cs0; - g = 2; - useFallback = false; - } - break; - default: - /* G0 DBCS */ - len2 = MBCS_FROM_UCHAR32_ISO2022( - converterData->myConverterArray[cs0], - sourceChar, &value, - useFallback, MBCS_OUTPUT_2); - if(len2 == 2 || (len2 == -2 && len == 0)) { /* only accept DBCS: abs(len)==2 */ - if(cs0 == KSC5601) { - /* - * Check for valid bytes for the encoding scheme. - * This is necessary because the sub-converter (windows-949) - * has a broader encoding scheme than is valid for 2022. - */ - value = _2022FromGR94DBCS(value); - if(value == 0) { - break; - } - } - targetValue = value; - len = len2; - cs = cs0; - g = 0; - useFallback = false; - } - break; - } - } - - if(len != 0) { - if(len < 0) { - len = -len; /* fallback */ - } - outLen = 0; /* count output bytes */ - - /* write SI if necessary (only for JIS7) */ - if(pFromU2022State->g == 1 && g == 0) { - buffer[outLen++] = UCNV_SI; - pFromU2022State->g = 0; - } - - /* write the designation sequence if necessary */ - if(cs != pFromU2022State->cs[g]) { - int32_t escLen = escSeqCharsLen[cs]; - uprv_memcpy(buffer + outLen, escSeqChars[cs], escLen); - outLen += escLen; - pFromU2022State->cs[g] = cs; - - /* invalidate the choices[] */ - choiceCount = 0; - } - - /* write the shift sequence if necessary */ - if(g != pFromU2022State->g) { - switch(g) { - /* case 0 handled before writing escapes */ - case 1: - buffer[outLen++] = UCNV_SO; - pFromU2022State->g = 1; - break; - default: /* case 2 */ - buffer[outLen++] = 0x1b; - buffer[outLen++] = 0x4e; - break; - /* no case 3: no SS3 in ISO-2022-JP-x */ - } - } - - /* write the output bytes */ - if(len == 1) { - buffer[outLen++] = (char)targetValue; - } else /* len == 2 */ { - buffer[outLen++] = (char)(targetValue >> 8); - buffer[outLen++] = (char)targetValue; - } - } else { - /* - * if we cannot find the character after checking all codepages - * then this is an error - */ - *err = U_INVALID_CHAR_FOUND; - cnv->fromUChar32=sourceChar; - break; - } - - if(sourceChar == CR || sourceChar == LF) { - /* reset the G2 state at the end of a line (conversion got us into ASCII or JISX201 already) */ - pFromU2022State->cs[2] = 0; - choiceCount = 0; - } - - /* output outLen>0 bytes in buffer[] */ - if(outLen == 1) { - *target++ = buffer[0]; - if(offsets) { - *offsets++ = (int32_t)(source - args->source - 1); /* -1: known to be ASCII */ - } - } else if(outLen == 2 && (target + 2) <= targetLimit) { - *target++ = buffer[0]; - *target++ = buffer[1]; - if(offsets) { - int32_t sourceIndex = (int32_t)(source - args->source - U16_LENGTH(sourceChar)); - *offsets++ = sourceIndex; - *offsets++ = sourceIndex; - } - } else { - fromUWriteUInt8( - cnv, - buffer, outLen, - &target, (const char *)targetLimit, - &offsets, (int32_t)(source - args->source - U16_LENGTH(sourceChar)), - err); - if(U_FAILURE(*err)) { - break; - } - } - } /* end if(myTargetIndexg!=0 || pFromU2022State->cs[0]!=ASCII) && - args->flush && source>=sourceLimit && cnv->fromUChar32==0 - ) { - int32_t sourceIndex; - - outLen = 0; - - if(pFromU2022State->g != 0) { - buffer[outLen++] = UCNV_SI; - pFromU2022State->g = 0; - } - - if(pFromU2022State->cs[0] != ASCII) { - int32_t escLen = escSeqCharsLen[ASCII]; - uprv_memcpy(buffer + outLen, escSeqChars[ASCII], escLen); - outLen += escLen; - pFromU2022State->cs[0] = (int8_t)ASCII; - } - - /* get the source index of the last input character */ - /* - * TODO this would be simpler and more reliable if we used a pair - * of sourceIndex/prevSourceIndex like in ucnvmbcs.c - * so that we could simply use the prevSourceIndex here; - * this code gives an incorrect result for the rare case of an unmatched - * trail surrogate that is alone in the last buffer of the text stream - */ - sourceIndex=(int32_t)(source-args->source); - if(sourceIndex>0) { - --sourceIndex; - if( U16_IS_TRAIL(args->source[sourceIndex]) && - (sourceIndex==0 || U16_IS_LEAD(args->source[sourceIndex-1])) - ) { - --sourceIndex; - } - } else { - sourceIndex=-1; - } - - fromUWriteUInt8( - cnv, - buffer, outLen, - &target, (const char *)targetLimit, - &offsets, sourceIndex, - err); - } - - /*save the state and return */ - args->source = source; - args->target = (char*)target; -} - -/*************** to unicode *******************/ - -static void U_CALLCONV -UConverter_toUnicode_ISO_2022_JP_OFFSETS_LOGIC(UConverterToUnicodeArgs *args, - UErrorCode* err){ - char tempBuf[2]; - const char *mySource = (char *) args->source; - UChar *myTarget = args->target; - const char *mySourceLimit = args->sourceLimit; - uint32_t targetUniChar = 0x0000; - uint32_t mySourceChar = 0x0000; - uint32_t tmpSourceChar = 0x0000; - UConverterDataISO2022* myData; - ISO2022State *pToU2022State; - StateEnum cs; - - myData=(UConverterDataISO2022*)(args->converter->extraInfo); - pToU2022State = &myData->toU2022State; - - if(myData->key != 0) { - /* continue with a partial escape sequence */ - goto escape; - } else if(args->converter->toULength == 1 && mySource < mySourceLimit && myTarget < args->targetLimit) { - /* continue with a partial double-byte character */ - mySourceChar = args->converter->toUBytes[0]; - args->converter->toULength = 0; - cs = (StateEnum)pToU2022State->cs[pToU2022State->g]; - targetUniChar = missingCharMarker; - goto getTrailByte; - } - - while(mySource < mySourceLimit){ - - targetUniChar =missingCharMarker; - - if(myTarget < args->targetLimit){ - - mySourceChar= (unsigned char) *mySource++; - - switch(mySourceChar) { - case UCNV_SI: - if(myData->version==3) { - pToU2022State->g=0; - continue; - } else { - /* only JIS7 uses SI/SO, not ISO-2022-JP-x */ - myData->isEmptySegment = false; /* reset this, we have a different error */ - break; - } - - case UCNV_SO: - if(myData->version==3) { - /* JIS7: switch to G1 half-width Katakana */ - pToU2022State->cs[1] = (int8_t)HWKANA_7BIT; - pToU2022State->g=1; - continue; - } else { - /* only JIS7 uses SI/SO, not ISO-2022-JP-x */ - myData->isEmptySegment = false; /* reset this, we have a different error */ - break; - } - - case ESC_2022: - mySource--; -escape: - { - const char * mySourceBefore = mySource; - int8_t toULengthBefore = args->converter->toULength; - - changeState_2022(args->converter,&(mySource), - mySourceLimit, ISO_2022_JP,err); - - /* If in ISO-2022-JP only and we successfully completed an escape sequence, but previous segment was empty, create an error */ - if(myData->version==0 && myData->key==0 && U_SUCCESS(*err) && myData->isEmptySegment) { - *err = U_ILLEGAL_ESCAPE_SEQUENCE; - args->converter->toUCallbackReason = UCNV_IRREGULAR; - args->converter->toULength = (int8_t)(toULengthBefore + (mySource - mySourceBefore)); - } - } - - /* invalid or illegal escape sequence */ - if(U_FAILURE(*err)){ - args->target = myTarget; - args->source = mySource; - myData->isEmptySegment = false; /* Reset to avoid future spurious errors */ - return; - } - /* If we successfully completed an escape sequence, we begin a new segment, empty so far */ - if(myData->key==0) { - myData->isEmptySegment = true; - } - continue; - - /* ISO-2022-JP does not use single-byte (C1) SS2 and SS3 */ - - case CR: - case LF: - /* automatically reset to single-byte mode */ - if((StateEnum)pToU2022State->cs[0] != ASCII && (StateEnum)pToU2022State->cs[0] != JISX201) { - pToU2022State->cs[0] = (int8_t)ASCII; - } - pToU2022State->cs[2] = 0; - pToU2022State->g = 0; - U_FALLTHROUGH; - default: - /* convert one or two bytes */ - myData->isEmptySegment = false; - cs = (StateEnum)pToU2022State->cs[pToU2022State->g]; - if( (uint8_t)(mySourceChar - 0xa1) <= (0xdf - 0xa1) && myData->version==4 && - !IS_JP_DBCS(cs) - ) { - /* 8-bit halfwidth katakana in any single-byte mode for JIS8 */ - targetUniChar = mySourceChar + (HWKANA_START - 0xa1); - - /* return from a single-shift state to the previous one */ - if(pToU2022State->g >= 2) { - pToU2022State->g=pToU2022State->prevG; - } - } else switch(cs) { - case ASCII: - if(mySourceChar <= 0x7f) { - targetUniChar = mySourceChar; - } - break; - case ISO8859_1: - if(mySourceChar <= 0x7f) { - targetUniChar = mySourceChar + 0x80; - } - /* return from a single-shift state to the previous one */ - pToU2022State->g=pToU2022State->prevG; - break; - case ISO8859_7: - if(mySourceChar <= 0x7f) { - /* convert mySourceChar+0x80 to use a normal 8-bit table */ - targetUniChar = - _MBCS_SINGLE_SIMPLE_GET_NEXT_BMP( - myData->myConverterArray[cs], - mySourceChar + 0x80); - } - /* return from a single-shift state to the previous one */ - pToU2022State->g=pToU2022State->prevG; - break; - case JISX201: - if(mySourceChar <= 0x7f) { - targetUniChar = jisx201ToU(mySourceChar); - } - break; - case HWKANA_7BIT: - if((uint8_t)(mySourceChar - 0x21) <= (0x5f - 0x21)) { - /* 7-bit halfwidth Katakana */ - targetUniChar = mySourceChar + (HWKANA_START - 0x21); - } - break; - default: - /* G0 DBCS */ - if(mySource < mySourceLimit) { - int leadIsOk, trailIsOk; - uint8_t trailByte; -getTrailByte: - trailByte = (uint8_t)*mySource; - /* - * Ticket 5691: consistent illegal sequences: - * - We include at least the first byte in the illegal sequence. - * - If any of the non-initial bytes could be the start of a character, - * we stop the illegal sequence before the first one of those. - * - * In ISO-2022 DBCS, if the second byte is in the 21..7e range or is - * an ESC/SO/SI, we report only the first byte as the illegal sequence. - * Otherwise we convert or report the pair of bytes. - */ - leadIsOk = (uint8_t)(mySourceChar - 0x21) <= (0x7e - 0x21); - trailIsOk = (uint8_t)(trailByte - 0x21) <= (0x7e - 0x21); - if (leadIsOk && trailIsOk) { - ++mySource; - tmpSourceChar = (mySourceChar << 8) | trailByte; - if(cs == JISX208) { - _2022ToSJIS((uint8_t)mySourceChar, trailByte, tempBuf); - mySourceChar = tmpSourceChar; - } else { - /* Copy before we modify tmpSourceChar so toUnicodeCallback() sees the correct bytes. */ - mySourceChar = tmpSourceChar; - if (cs == KSC5601) { - tmpSourceChar += 0x8080; /* = _2022ToGR94DBCS(tmpSourceChar) */ - } - tempBuf[0] = (char)(tmpSourceChar >> 8); - tempBuf[1] = (char)(tmpSourceChar); - } - targetUniChar = ucnv_MBCSSimpleGetNextUChar(myData->myConverterArray[cs], tempBuf, 2, false); - } else if (!(trailIsOk || IS_2022_CONTROL(trailByte))) { - /* report a pair of illegal bytes if the second byte is not a DBCS starter */ - ++mySource; - /* add another bit so that the code below writes 2 bytes in case of error */ - mySourceChar = 0x10000 | (mySourceChar << 8) | trailByte; - } - } else { - args->converter->toUBytes[0] = (uint8_t)mySourceChar; - args->converter->toULength = 1; - goto endloop; - } - } /* End of inner switch */ - break; - } /* End of outer switch */ - if(targetUniChar < (missingCharMarker-1/*0xfffe*/)){ - if(args->offsets){ - args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); - } - *(myTarget++)=(UChar)targetUniChar; - } - else if(targetUniChar > missingCharMarker){ - /* disassemble the surrogate pair and write to output*/ - targetUniChar-=0x0010000; - *myTarget = (UChar)(0xd800+(UChar)(targetUniChar>>10)); - if(args->offsets){ - args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); - } - ++myTarget; - if(myTarget< args->targetLimit){ - *myTarget = (UChar)(0xdc00+(UChar)(targetUniChar&0x3ff)); - if(args->offsets){ - args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); - } - ++myTarget; - }else{ - args->converter->UCharErrorBuffer[args->converter->UCharErrorBufferLength++]= - (UChar)(0xdc00+(UChar)(targetUniChar&0x3ff)); - } - - } - else{ - /* Call the callback function*/ - toUnicodeCallback(args->converter,mySourceChar,targetUniChar,err); - break; - } - } - else{ /* goes with "if(myTarget < args->targetLimit)" way up near top of function */ - *err =U_BUFFER_OVERFLOW_ERROR; - break; - } - } -endloop: - args->target = myTarget; - args->source = mySource; -} - - -#if !UCONFIG_ONLY_HTML_CONVERSION -/*************************************************************** -* Rules for ISO-2022-KR encoding -* i) The KSC5601 designator sequence should appear only once in a file, -* at the beginning of a line before any KSC5601 characters. This usually -* means that it appears by itself on the first line of the file -* ii) There are only 2 shifting sequences SO to shift into double byte mode -* and SI to shift into single byte mode -*/ -static void U_CALLCONV -UConverter_fromUnicode_ISO_2022_KR_OFFSETS_LOGIC_IBM(UConverterFromUnicodeArgs* args, UErrorCode* err){ - - UConverter* saveConv = args->converter; - UConverterDataISO2022 *myConverterData=(UConverterDataISO2022*)saveConv->extraInfo; - args->converter=myConverterData->currentConverter; - - myConverterData->currentConverter->fromUChar32 = saveConv->fromUChar32; - ucnv_MBCSFromUnicodeWithOffsets(args,err); - saveConv->fromUChar32 = myConverterData->currentConverter->fromUChar32; - - if(*err == U_BUFFER_OVERFLOW_ERROR) { - if(myConverterData->currentConverter->charErrorBufferLength > 0) { - uprv_memcpy( - saveConv->charErrorBuffer, - myConverterData->currentConverter->charErrorBuffer, - myConverterData->currentConverter->charErrorBufferLength); - } - saveConv->charErrorBufferLength = myConverterData->currentConverter->charErrorBufferLength; - myConverterData->currentConverter->charErrorBufferLength = 0; - } - args->converter=saveConv; -} - -static void U_CALLCONV -UConverter_fromUnicode_ISO_2022_KR_OFFSETS_LOGIC(UConverterFromUnicodeArgs* args, UErrorCode* err){ - - const UChar *source = args->source; - const UChar *sourceLimit = args->sourceLimit; - unsigned char *target = (unsigned char *) args->target; - unsigned char *targetLimit = (unsigned char *) args->targetLimit; - int32_t* offsets = args->offsets; - uint32_t targetByteUnit = 0x0000; - UChar32 sourceChar = 0x0000; - UBool isTargetByteDBCS; - UBool oldIsTargetByteDBCS; - UConverterDataISO2022 *converterData; - UConverterSharedData* sharedData; - UBool useFallback; - int32_t length =0; - - converterData=(UConverterDataISO2022*)args->converter->extraInfo; - /* if the version is 1 then the user is requesting - * conversion with ibm-25546 pass the arguments to - * MBCS converter and return - */ - if(converterData->version==1){ - UConverter_fromUnicode_ISO_2022_KR_OFFSETS_LOGIC_IBM(args,err); - return; - } - - /* initialize data */ - sharedData = converterData->currentConverter->sharedData; - useFallback = args->converter->useFallback; - isTargetByteDBCS=(UBool)args->converter->fromUnicodeStatus; - oldIsTargetByteDBCS = isTargetByteDBCS; - - isTargetByteDBCS = (UBool) args->converter->fromUnicodeStatus; - if((sourceChar = args->converter->fromUChar32)!=0 && target targetLimit){ - sourceChar = *source++; - - /* do not convert SO/SI/ESC */ - if(IS_2022_CONTROL(sourceChar)) { - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - args->converter->fromUChar32=sourceChar; - break; - } - - length = MBCS_FROM_UCHAR32_ISO2022(sharedData,sourceChar,&targetByteUnit,useFallback,MBCS_OUTPUT_2); - if(length < 0) { - length = -length; /* fallback */ - } - /* only DBCS or SBCS characters are expected*/ - /* DB characters with high bit set to 1 are expected */ - if( length > 2 || length==0 || - (length == 1 && targetByteUnit > 0x7f) || - (length == 2 && - ((uint16_t)(targetByteUnit - 0xa1a1) > (0xfefe - 0xa1a1) || - (uint8_t)(targetByteUnit - 0xa1) > (0xfe - 0xa1))) - ) { - targetByteUnit=missingCharMarker; - } - if (targetByteUnit != missingCharMarker){ - - oldIsTargetByteDBCS = isTargetByteDBCS; - isTargetByteDBCS = (UBool)(targetByteUnit>0x00FF); - /* append the shift sequence */ - if (oldIsTargetByteDBCS != isTargetByteDBCS ){ - - if (isTargetByteDBCS) - *target++ = UCNV_SO; - else - *target++ = UCNV_SI; - if(offsets) - *(offsets++) = (int32_t)(source - args->source-1); - } - /* write the targetUniChar to target */ - if(targetByteUnit <= 0x00FF){ - if( target < targetLimit){ - *(target++) = (unsigned char) targetByteUnit; - if(offsets){ - *(offsets++) = (int32_t)(source - args->source-1); - } - - }else{ - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (unsigned char) (targetByteUnit); - *err = U_BUFFER_OVERFLOW_ERROR; - } - }else{ - if(target < targetLimit){ - *(target++) =(unsigned char) ((targetByteUnit>>8) -0x80); - if(offsets){ - *(offsets++) = (int32_t)(source - args->source-1); - } - if(target < targetLimit){ - *(target++) =(unsigned char) (targetByteUnit -0x80); - if(offsets){ - *(offsets++) = (int32_t)(source - args->source-1); - } - }else{ - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (unsigned char) (targetByteUnit -0x80); - *err = U_BUFFER_OVERFLOW_ERROR; - } - }else{ - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (unsigned char) ((targetByteUnit>>8) -0x80); - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (unsigned char) (targetByteUnit-0x80); - *err = U_BUFFER_OVERFLOW_ERROR; - } - } - - } - else{ - /* oops.. the code point is unassingned - * set the error and reason - */ - - /*check if the char is a First surrogate*/ - if(U16_IS_SURROGATE(sourceChar)) { - if(U16_IS_SURROGATE_LEAD(sourceChar)) { -getTrail: - /*look ahead to find the trail surrogate*/ - if(source < sourceLimit) { - /* test the following code unit */ - UChar trail=(UChar) *source; - if(U16_IS_TRAIL(trail)) { - source++; - sourceChar=U16_GET_SUPPLEMENTARY(sourceChar, trail); - *err = U_INVALID_CHAR_FOUND; - /* convert this surrogate code point */ - /* exit this condition tree */ - } else { - /* this is an unmatched lead code unit (1st surrogate) */ - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - } - } else { - /* no more input */ - *err = U_ZERO_ERROR; - } - } else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - } - } else { - /* callback(unassigned) for a BMP code point */ - *err = U_INVALID_CHAR_FOUND; - } - - args->converter->fromUChar32=sourceChar; - break; - } - } /* end if(myTargetIndexflush && source>=sourceLimit && args->converter->fromUChar32==0 - ) { - int32_t sourceIndex; - - /* we are switching to ASCII */ - isTargetByteDBCS=false; - - /* get the source index of the last input character */ - /* - * TODO this would be simpler and more reliable if we used a pair - * of sourceIndex/prevSourceIndex like in ucnvmbcs.c - * so that we could simply use the prevSourceIndex here; - * this code gives an incorrect result for the rare case of an unmatched - * trail surrogate that is alone in the last buffer of the text stream - */ - sourceIndex=(int32_t)(source-args->source); - if(sourceIndex>0) { - --sourceIndex; - if( U16_IS_TRAIL(args->source[sourceIndex]) && - (sourceIndex==0 || U16_IS_LEAD(args->source[sourceIndex-1])) - ) { - --sourceIndex; - } - } else { - sourceIndex=-1; - } - - fromUWriteUInt8( - args->converter, - SHIFT_IN_STR, 1, - &target, (const char *)targetLimit, - &offsets, sourceIndex, - err); - } - - /*save the state and return */ - args->source = source; - args->target = (char*)target; - args->converter->fromUnicodeStatus = (uint32_t)isTargetByteDBCS; -} - -/************************ To Unicode ***************************************/ - -static void U_CALLCONV -UConverter_toUnicode_ISO_2022_KR_OFFSETS_LOGIC_IBM(UConverterToUnicodeArgs *args, - UErrorCode* err){ - char const* sourceStart; - UConverterDataISO2022* myData=(UConverterDataISO2022*)(args->converter->extraInfo); - - UConverterToUnicodeArgs subArgs; - int32_t minArgsSize; - - /* set up the subconverter arguments */ - if(args->sizesize; - } else { - minArgsSize = (int32_t)sizeof(UConverterToUnicodeArgs); - } - - uprv_memcpy(&subArgs, args, minArgsSize); - subArgs.size = (uint16_t)minArgsSize; - subArgs.converter = myData->currentConverter; - - /* remember the original start of the input for offsets */ - sourceStart = args->source; - - if(myData->key != 0) { - /* continue with a partial escape sequence */ - goto escape; - } - - while(U_SUCCESS(*err) && args->source < args->sourceLimit) { - /*Find the end of the buffer e.g : Next Escape Seq | end of Buffer*/ - subArgs.source = args->source; - subArgs.sourceLimit = getEndOfBuffer_2022(&(args->source), args->sourceLimit, args->flush); - if(subArgs.source != subArgs.sourceLimit) { - /* - * get the current partial byte sequence - * - * it needs to be moved between the public and the subconverter - * so that the conversion framework, which only sees the public - * converter, can handle truncated and illegal input etc. - */ - if(args->converter->toULength > 0) { - uprv_memcpy(subArgs.converter->toUBytes, args->converter->toUBytes, args->converter->toULength); - } - subArgs.converter->toULength = args->converter->toULength; - - /* - * Convert up to the end of the input, or to before the next escape character. - * Does not handle conversion extensions because the preToU[] state etc. - * is not copied. - */ - ucnv_MBCSToUnicodeWithOffsets(&subArgs, err); - - if(args->offsets != NULL && sourceStart != args->source) { - /* update offsets to base them on the actual start of the input */ - int32_t *offsets = args->offsets; - UChar *target = args->target; - int32_t delta = (int32_t)(args->source - sourceStart); - while(target < subArgs.target) { - if(*offsets >= 0) { - *offsets += delta; - } - ++offsets; - ++target; - } - } - args->source = subArgs.source; - args->target = subArgs.target; - args->offsets = subArgs.offsets; - - /* copy input/error/overflow buffers */ - if(subArgs.converter->toULength > 0) { - uprv_memcpy(args->converter->toUBytes, subArgs.converter->toUBytes, subArgs.converter->toULength); - } - args->converter->toULength = subArgs.converter->toULength; - - if(*err == U_BUFFER_OVERFLOW_ERROR) { - if(subArgs.converter->UCharErrorBufferLength > 0) { - uprv_memcpy(args->converter->UCharErrorBuffer, subArgs.converter->UCharErrorBuffer, - subArgs.converter->UCharErrorBufferLength); - } - args->converter->UCharErrorBufferLength=subArgs.converter->UCharErrorBufferLength; - subArgs.converter->UCharErrorBufferLength = 0; - } - } - - if (U_FAILURE(*err) || (args->source == args->sourceLimit)) { - return; - } - -escape: - changeState_2022(args->converter, - &(args->source), - args->sourceLimit, - ISO_2022_KR, - err); - } -} - -static void U_CALLCONV -UConverter_toUnicode_ISO_2022_KR_OFFSETS_LOGIC(UConverterToUnicodeArgs *args, - UErrorCode* err){ - char tempBuf[2]; - const char *mySource = ( char *) args->source; - UChar *myTarget = args->target; - const char *mySourceLimit = args->sourceLimit; - UChar32 targetUniChar = 0x0000; - UChar mySourceChar = 0x0000; - UConverterDataISO2022* myData; - UConverterSharedData* sharedData ; - UBool useFallback; - - myData=(UConverterDataISO2022*)(args->converter->extraInfo); - if(myData->version==1){ - UConverter_toUnicode_ISO_2022_KR_OFFSETS_LOGIC_IBM(args,err); - return; - } - - /* initialize state */ - sharedData = myData->currentConverter->sharedData; - useFallback = args->converter->useFallback; - - if(myData->key != 0) { - /* continue with a partial escape sequence */ - goto escape; - } else if(args->converter->toULength == 1 && mySource < mySourceLimit && myTarget < args->targetLimit) { - /* continue with a partial double-byte character */ - mySourceChar = args->converter->toUBytes[0]; - args->converter->toULength = 0; - goto getTrailByte; - } - - while(mySource< mySourceLimit){ - - if(myTarget < args->targetLimit){ - - mySourceChar= (unsigned char) *mySource++; - - if(mySourceChar==UCNV_SI){ - myData->toU2022State.g = 0; - if (myData->isEmptySegment) { - myData->isEmptySegment = false; /* we are handling it, reset to avoid future spurious errors */ - *err = U_ILLEGAL_ESCAPE_SEQUENCE; - args->converter->toUCallbackReason = UCNV_IRREGULAR; - args->converter->toUBytes[0] = (uint8_t)mySourceChar; - args->converter->toULength = 1; - args->target = myTarget; - args->source = mySource; - return; - } - /*consume the source */ - continue; - }else if(mySourceChar==UCNV_SO){ - myData->toU2022State.g = 1; - myData->isEmptySegment = true; /* Begin a new segment, empty so far */ - /*consume the source */ - continue; - }else if(mySourceChar==ESC_2022){ - mySource--; -escape: - myData->isEmptySegment = false; /* Any invalid ESC sequences will be detected separately, so just reset this */ - changeState_2022(args->converter,&(mySource), - mySourceLimit, ISO_2022_KR, err); - if(U_FAILURE(*err)){ - args->target = myTarget; - args->source = mySource; - return; - } - continue; - } - - myData->isEmptySegment = false; /* Any invalid char errors will be detected separately, so just reset this */ - if(myData->toU2022State.g == 1) { - if(mySource < mySourceLimit) { - int leadIsOk, trailIsOk; - uint8_t trailByte; -getTrailByte: - targetUniChar = missingCharMarker; - trailByte = (uint8_t)*mySource; - /* - * Ticket 5691: consistent illegal sequences: - * - We include at least the first byte in the illegal sequence. - * - If any of the non-initial bytes could be the start of a character, - * we stop the illegal sequence before the first one of those. - * - * In ISO-2022 DBCS, if the second byte is in the 21..7e range or is - * an ESC/SO/SI, we report only the first byte as the illegal sequence. - * Otherwise we convert or report the pair of bytes. - */ - leadIsOk = (uint8_t)(mySourceChar - 0x21) <= (0x7e - 0x21); - trailIsOk = (uint8_t)(trailByte - 0x21) <= (0x7e - 0x21); - if (leadIsOk && trailIsOk) { - ++mySource; - tempBuf[0] = (char)(mySourceChar + 0x80); - tempBuf[1] = (char)(trailByte + 0x80); - targetUniChar = ucnv_MBCSSimpleGetNextUChar(sharedData, tempBuf, 2, useFallback); - mySourceChar = (mySourceChar << 8) | trailByte; - } else if (!(trailIsOk || IS_2022_CONTROL(trailByte))) { - /* report a pair of illegal bytes if the second byte is not a DBCS starter */ - ++mySource; - /* add another bit so that the code below writes 2 bytes in case of error */ - mySourceChar = static_cast(0x10000 | (mySourceChar << 8) | trailByte); - } - } else { - args->converter->toUBytes[0] = (uint8_t)mySourceChar; - args->converter->toULength = 1; - break; - } - } - else if(mySourceChar <= 0x7f) { - targetUniChar = ucnv_MBCSSimpleGetNextUChar(sharedData, mySource - 1, 1, useFallback); - } else { - targetUniChar = 0xffff; - } - if(targetUniChar < 0xfffe){ - if(args->offsets) { - args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); - } - *(myTarget++)=(UChar)targetUniChar; - } - else { - /* Call the callback function*/ - toUnicodeCallback(args->converter,mySourceChar,targetUniChar,err); - break; - } - } - else{ - *err =U_BUFFER_OVERFLOW_ERROR; - break; - } - } - args->target = myTarget; - args->source = mySource; -} - -/*************************** END ISO2022-KR *********************************/ - -/*************************** ISO-2022-CN ********************************* -* -* Rules for ISO-2022-CN Encoding: -* i) The designator sequence must appear once on a line before any instance -* of character set it designates. -* ii) If two lines contain characters from the same character set, both lines -* must include the designator sequence. -* iii) Once the designator sequence is known, a shifting sequence has to be found -* to invoke the shifting -* iv) All lines start in ASCII and end in ASCII. -* v) Four shifting sequences are employed for this purpose: -* -* Sequcence ASCII Eq Charsets -* ---------- ------- --------- -* SI US-ASCII -* SO CNS-11643-1992 Plane 1, GB2312, ISO-IR-165 -* SS2 N CNS-11643-1992 Plane 2 -* SS3 O CNS-11643-1992 Planes 3-7 -* -* vi) -* SOdesignator : ESC "$" ")" finalchar_for_SO -* SS2designator : ESC "$" "*" finalchar_for_SS2 -* SS3designator : ESC "$" "+" finalchar_for_SS3 -* -* ESC $ ) A Indicates the bytes following SO are Chinese -* characters as defined in GB 2312-80, until -* another SOdesignation appears -* -* -* ESC $ ) E Indicates the bytes following SO are as defined -* in ISO-IR-165 (for details, see section 2.1), -* until another SOdesignation appears -* -* ESC $ ) G Indicates the bytes following SO are as defined -* in CNS 11643-plane-1, until another -* SOdesignation appears -* -* ESC $ * H Indicates the two bytes immediately following -* SS2 is a Chinese character as defined in CNS -* 11643-plane-2, until another SS2designation -* appears -* (Meaning N must precede every 2 byte -* sequence.) -* -* ESC $ + I Indicates the immediate two bytes following SS3 -* is a Chinese character as defined in CNS -* 11643-plane-3, until another SS3designation -* appears -* (Meaning O must precede every 2 byte -* sequence.) -* -* ESC $ + J Indicates the immediate two bytes following SS3 -* is a Chinese character as defined in CNS -* 11643-plane-4, until another SS3designation -* appears -* (In English: O must precede every 2 byte -* sequence.) -* -* ESC $ + K Indicates the immediate two bytes following SS3 -* is a Chinese character as defined in CNS -* 11643-plane-5, until another SS3designation -* appears -* -* ESC $ + L Indicates the immediate two bytes following SS3 -* is a Chinese character as defined in CNS -* 11643-plane-6, until another SS3designation -* appears -* -* ESC $ + M Indicates the immediate two bytes following SS3 -* is a Chinese character as defined in CNS -* 11643-plane-7, until another SS3designation -* appears -* -* As in ISO-2022-CN, each line starts in ASCII, and ends in ASCII, and -* has its own designation information before any Chinese characters -* appear -* -*/ - -/* The following are defined this way to make the strings truly readonly */ -static const char GB_2312_80_STR[] = "\x1B\x24\x29\x41"; -static const char ISO_IR_165_STR[] = "\x1B\x24\x29\x45"; -static const char CNS_11643_1992_Plane_1_STR[] = "\x1B\x24\x29\x47"; -static const char CNS_11643_1992_Plane_2_STR[] = "\x1B\x24\x2A\x48"; -static const char CNS_11643_1992_Plane_3_STR[] = "\x1B\x24\x2B\x49"; -static const char CNS_11643_1992_Plane_4_STR[] = "\x1B\x24\x2B\x4A"; -static const char CNS_11643_1992_Plane_5_STR[] = "\x1B\x24\x2B\x4B"; -static const char CNS_11643_1992_Plane_6_STR[] = "\x1B\x24\x2B\x4C"; -static const char CNS_11643_1992_Plane_7_STR[] = "\x1B\x24\x2B\x4D"; - -/********************** ISO2022-CN Data **************************/ -static const char* const escSeqCharsCN[10] ={ - SHIFT_IN_STR, /* 0 ASCII */ - GB_2312_80_STR, /* 1 GB2312_1 */ - ISO_IR_165_STR, /* 2 ISO_IR_165 */ - CNS_11643_1992_Plane_1_STR, - CNS_11643_1992_Plane_2_STR, - CNS_11643_1992_Plane_3_STR, - CNS_11643_1992_Plane_4_STR, - CNS_11643_1992_Plane_5_STR, - CNS_11643_1992_Plane_6_STR, - CNS_11643_1992_Plane_7_STR -}; - -static void U_CALLCONV -UConverter_fromUnicode_ISO_2022_CN_OFFSETS_LOGIC(UConverterFromUnicodeArgs* args, UErrorCode* err){ - UConverter *cnv = args->converter; - UConverterDataISO2022 *converterData; - ISO2022State *pFromU2022State; - uint8_t *target = (uint8_t *) args->target; - const uint8_t *targetLimit = (const uint8_t *) args->targetLimit; - const UChar* source = args->source; - const UChar* sourceLimit = args->sourceLimit; - int32_t* offsets = args->offsets; - UChar32 sourceChar; - char buffer[8]; - int32_t len; - int8_t choices[3]; - int32_t choiceCount; - uint32_t targetValue = 0; - UBool useFallback; - - /* set up the state */ - converterData = (UConverterDataISO2022*)cnv->extraInfo; - pFromU2022State = &converterData->fromU2022State; - - choiceCount = 0; - - /* check if the last codepoint of previous buffer was a lead surrogate*/ - if((sourceChar = cnv->fromUChar32)!=0 && target< targetLimit) { - goto getTrail; - } - - while( source < sourceLimit){ - if(target < targetLimit){ - - sourceChar = *(source++); - /*check if the char is a First surrogate*/ - if(U16_IS_SURROGATE(sourceChar)) { - if(U16_IS_SURROGATE_LEAD(sourceChar)) { -getTrail: - /*look ahead to find the trail surrogate*/ - if(source < sourceLimit) { - /* test the following code unit */ - UChar trail=(UChar) *source; - if(U16_IS_TRAIL(trail)) { - source++; - sourceChar=U16_GET_SUPPLEMENTARY(sourceChar, trail); - cnv->fromUChar32=0x00; - /* convert this supplementary code point */ - /* exit this condition tree */ - } else { - /* this is an unmatched lead code unit (1st surrogate) */ - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - cnv->fromUChar32=sourceChar; - break; - } - } else { - /* no more input */ - cnv->fromUChar32=sourceChar; - break; - } - } else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - cnv->fromUChar32=sourceChar; - break; - } - } - - /* do the conversion */ - if(sourceChar <= 0x007f ){ - /* do not convert SO/SI/ESC */ - if(IS_2022_CONTROL(sourceChar)) { - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - cnv->fromUChar32=sourceChar; - break; - } - - /* US-ASCII */ - if(pFromU2022State->g == 0) { - buffer[0] = (char)sourceChar; - len = 1; - } else { - buffer[0] = UCNV_SI; - buffer[1] = (char)sourceChar; - len = 2; - pFromU2022State->g = 0; - choiceCount = 0; - } - if(sourceChar == CR || sourceChar == LF) { - /* reset the state at the end of a line */ - uprv_memset(pFromU2022State, 0, sizeof(ISO2022State)); - choiceCount = 0; - } - } - else{ - /* convert U+0080..U+10ffff */ - int32_t i; - int8_t cs, g; - - if(choiceCount == 0) { - /* try the current SO/G1 converter first */ - choices[0] = pFromU2022State->cs[1]; - - /* default to GB2312_1 if none is designated yet */ - if(choices[0] == 0) { - choices[0] = GB2312_1; - } - - if(converterData->version == 0) { - /* ISO-2022-CN */ - - /* try the other SO/G1 converter; a CNS_11643_1 lookup may result in any plane */ - if(choices[0] == GB2312_1) { - choices[1] = (int8_t)CNS_11643_1; - } else { - choices[1] = (int8_t)GB2312_1; - } - - choiceCount = 2; - } else if (converterData->version == 1) { - /* ISO-2022-CN-EXT */ - - /* try one of the other converters */ - switch(choices[0]) { - case GB2312_1: - choices[1] = (int8_t)CNS_11643_1; - choices[2] = (int8_t)ISO_IR_165; - break; - case ISO_IR_165: - choices[1] = (int8_t)GB2312_1; - choices[2] = (int8_t)CNS_11643_1; - break; - default: /* CNS_11643_x */ - choices[1] = (int8_t)GB2312_1; - choices[2] = (int8_t)ISO_IR_165; - break; - } - - choiceCount = 3; - } else { - choices[0] = (int8_t)CNS_11643_1; - choices[1] = (int8_t)GB2312_1; - } - } - - cs = g = 0; - /* - * len==0: no mapping found yet - * len<0: found a fallback result: continue looking for a roundtrip but no further fallbacks - * len>0: found a roundtrip result, done - */ - len = 0; - /* - * We will turn off useFallback after finding a fallback, - * but we still get fallbacks from PUA code points as usual. - * Therefore, we will also need to check that we don't overwrite - * an early fallback with a later one. - */ - useFallback = cnv->useFallback; - - for(i = 0; i < choiceCount && len <= 0; ++i) { - int8_t cs0 = choices[i]; - if(cs0 > 0) { - uint32_t value; - int32_t len2; - if(cs0 >= CNS_11643_0) { - len2 = MBCS_FROM_UCHAR32_ISO2022( - converterData->myConverterArray[CNS_11643], - sourceChar, - &value, - useFallback, - MBCS_OUTPUT_3); - if(len2 == 3 || (len2 == -3 && len == 0)) { - targetValue = value; - cs = (int8_t)(CNS_11643_0 + (value >> 16) - 0x80); - if(len2 >= 0) { - len = 2; - } else { - len = -2; - useFallback = false; - } - if(cs == CNS_11643_1) { - g = 1; - } else if(cs == CNS_11643_2) { - g = 2; - } else /* plane 3..7 */ if(converterData->version == 1) { - g = 3; - } else { - /* ISO-2022-CN (without -EXT) does not support plane 3..7 */ - len = 0; - } - } - } else { - /* GB2312_1 or ISO-IR-165 */ - U_ASSERT(cs0myConverterArray[cs0], - sourceChar, - &value, - useFallback, - MBCS_OUTPUT_2); - if(len2 == 2 || (len2 == -2 && len == 0)) { - targetValue = value; - len = len2; - cs = cs0; - g = 1; - useFallback = false; - } - } - } - } - - if(len != 0) { - len = 0; /* count output bytes; it must have been abs(len) == 2 */ - - /* write the designation sequence if necessary */ - if(cs != pFromU2022State->cs[g]) { - if(cs < CNS_11643) { - uprv_memcpy(buffer, escSeqCharsCN[cs], 4); - } else { - U_ASSERT(cs >= CNS_11643_1); - uprv_memcpy(buffer, escSeqCharsCN[CNS_11643 + (cs - CNS_11643_1)], 4); - } - len = 4; - pFromU2022State->cs[g] = cs; - if(g == 1) { - /* changing the SO/G1 charset invalidates the choices[] */ - choiceCount = 0; - } - } - - /* write the shift sequence if necessary */ - if(g != pFromU2022State->g) { - switch(g) { - case 1: - buffer[len++] = UCNV_SO; - - /* set the new state only if it is the locking shift SO/G1, not for SS2 or SS3 */ - pFromU2022State->g = 1; - break; - case 2: - buffer[len++] = 0x1b; - buffer[len++] = 0x4e; - break; - default: /* case 3 */ - buffer[len++] = 0x1b; - buffer[len++] = 0x4f; - break; - } - } - - /* write the two output bytes */ - buffer[len++] = (char)(targetValue >> 8); - buffer[len++] = (char)targetValue; - } else { - /* if we cannot find the character after checking all codepages - * then this is an error - */ - *err = U_INVALID_CHAR_FOUND; - cnv->fromUChar32=sourceChar; - break; - } - } - - /* output len>0 bytes in buffer[] */ - if(len == 1) { - *target++ = buffer[0]; - if(offsets) { - *offsets++ = (int32_t)(source - args->source - 1); /* -1: known to be ASCII */ - } - } else if(len == 2 && (target + 2) <= targetLimit) { - *target++ = buffer[0]; - *target++ = buffer[1]; - if(offsets) { - int32_t sourceIndex = (int32_t)(source - args->source - U16_LENGTH(sourceChar)); - *offsets++ = sourceIndex; - *offsets++ = sourceIndex; - } - } else { - fromUWriteUInt8( - cnv, - buffer, len, - &target, (const char *)targetLimit, - &offsets, (int32_t)(source - args->source - U16_LENGTH(sourceChar)), - err); - if(U_FAILURE(*err)) { - break; - } - } - } /* end if(myTargetIndexg!=0 && - args->flush && source>=sourceLimit && cnv->fromUChar32==0 - ) { - int32_t sourceIndex; - - /* we are switching to ASCII */ - pFromU2022State->g=0; - - /* get the source index of the last input character */ - /* - * TODO this would be simpler and more reliable if we used a pair - * of sourceIndex/prevSourceIndex like in ucnvmbcs.c - * so that we could simply use the prevSourceIndex here; - * this code gives an incorrect result for the rare case of an unmatched - * trail surrogate that is alone in the last buffer of the text stream - */ - sourceIndex=(int32_t)(source-args->source); - if(sourceIndex>0) { - --sourceIndex; - if( U16_IS_TRAIL(args->source[sourceIndex]) && - (sourceIndex==0 || U16_IS_LEAD(args->source[sourceIndex-1])) - ) { - --sourceIndex; - } - } else { - sourceIndex=-1; - } - - fromUWriteUInt8( - cnv, - SHIFT_IN_STR, 1, - &target, (const char *)targetLimit, - &offsets, sourceIndex, - err); - } - - /*save the state and return */ - args->source = source; - args->target = (char*)target; -} - - -static void U_CALLCONV -UConverter_toUnicode_ISO_2022_CN_OFFSETS_LOGIC(UConverterToUnicodeArgs *args, - UErrorCode* err){ - char tempBuf[3]; - const char *mySource = (char *) args->source; - UChar *myTarget = args->target; - const char *mySourceLimit = args->sourceLimit; - uint32_t targetUniChar = 0x0000; - uint32_t mySourceChar = 0x0000; - UConverterDataISO2022* myData; - ISO2022State *pToU2022State; - - myData=(UConverterDataISO2022*)(args->converter->extraInfo); - pToU2022State = &myData->toU2022State; - - if(myData->key != 0) { - /* continue with a partial escape sequence */ - goto escape; - } else if(args->converter->toULength == 1 && mySource < mySourceLimit && myTarget < args->targetLimit) { - /* continue with a partial double-byte character */ - mySourceChar = args->converter->toUBytes[0]; - args->converter->toULength = 0; - targetUniChar = missingCharMarker; - goto getTrailByte; - } - - while(mySource < mySourceLimit){ - - targetUniChar =missingCharMarker; - - if(myTarget < args->targetLimit){ - - mySourceChar= (unsigned char) *mySource++; - - switch(mySourceChar){ - case UCNV_SI: - pToU2022State->g=0; - if (myData->isEmptySegment) { - myData->isEmptySegment = false; /* we are handling it, reset to avoid future spurious errors */ - *err = U_ILLEGAL_ESCAPE_SEQUENCE; - args->converter->toUCallbackReason = UCNV_IRREGULAR; - args->converter->toUBytes[0] = static_cast(mySourceChar); - args->converter->toULength = 1; - args->target = myTarget; - args->source = mySource; - return; - } - continue; - - case UCNV_SO: - if(pToU2022State->cs[1] != 0) { - pToU2022State->g=1; - myData->isEmptySegment = true; /* Begin a new segment, empty so far */ - continue; - } else { - /* illegal to have SO before a matching designator */ - myData->isEmptySegment = false; /* Handling a different error, reset this to avoid future spurious errs */ - break; - } - - case ESC_2022: - mySource--; -escape: - { - const char * mySourceBefore = mySource; - int8_t toULengthBefore = args->converter->toULength; - - changeState_2022(args->converter,&(mySource), - mySourceLimit, ISO_2022_CN,err); - - /* After SO there must be at least one character before a designator (designator error handled separately) */ - if(myData->key==0 && U_SUCCESS(*err) && myData->isEmptySegment) { - *err = U_ILLEGAL_ESCAPE_SEQUENCE; - args->converter->toUCallbackReason = UCNV_IRREGULAR; - args->converter->toULength = (int8_t)(toULengthBefore + (mySource - mySourceBefore)); - } - } - - /* invalid or illegal escape sequence */ - if(U_FAILURE(*err)){ - args->target = myTarget; - args->source = mySource; - myData->isEmptySegment = false; /* Reset to avoid future spurious errors */ - return; - } - continue; - - /* ISO-2022-CN does not use single-byte (C1) SS2 and SS3 */ - - case CR: - case LF: - uprv_memset(pToU2022State, 0, sizeof(ISO2022State)); - U_FALLTHROUGH; - default: - /* convert one or two bytes */ - myData->isEmptySegment = false; - if(pToU2022State->g != 0) { - if(mySource < mySourceLimit) { - UConverterSharedData *cnv; - StateEnum tempState; - int32_t tempBufLen; - int leadIsOk, trailIsOk; - uint8_t trailByte; -getTrailByte: - trailByte = (uint8_t)*mySource; - /* - * Ticket 5691: consistent illegal sequences: - * - We include at least the first byte in the illegal sequence. - * - If any of the non-initial bytes could be the start of a character, - * we stop the illegal sequence before the first one of those. - * - * In ISO-2022 DBCS, if the second byte is in the 21..7e range or is - * an ESC/SO/SI, we report only the first byte as the illegal sequence. - * Otherwise we convert or report the pair of bytes. - */ - leadIsOk = (uint8_t)(mySourceChar - 0x21) <= (0x7e - 0x21); - trailIsOk = (uint8_t)(trailByte - 0x21) <= (0x7e - 0x21); - if (leadIsOk && trailIsOk) { - ++mySource; - tempState = (StateEnum)pToU2022State->cs[pToU2022State->g]; - if(tempState >= CNS_11643_0) { - cnv = myData->myConverterArray[CNS_11643]; - tempBuf[0] = (char) (0x80+(tempState-CNS_11643_0)); - tempBuf[1] = (char) (mySourceChar); - tempBuf[2] = (char) trailByte; - tempBufLen = 3; - - }else{ - U_ASSERT(tempStatemyConverterArray[tempState]; - tempBuf[0] = (char) (mySourceChar); - tempBuf[1] = (char) trailByte; - tempBufLen = 2; - } - targetUniChar = ucnv_MBCSSimpleGetNextUChar(cnv, tempBuf, tempBufLen, false); - mySourceChar = (mySourceChar << 8) | trailByte; - } else if (!(trailIsOk || IS_2022_CONTROL(trailByte))) { - /* report a pair of illegal bytes if the second byte is not a DBCS starter */ - ++mySource; - /* add another bit so that the code below writes 2 bytes in case of error */ - mySourceChar = 0x10000 | (mySourceChar << 8) | trailByte; - } - if(pToU2022State->g>=2) { - /* return from a single-shift state to the previous one */ - pToU2022State->g=pToU2022State->prevG; - } - } else { - args->converter->toUBytes[0] = (uint8_t)mySourceChar; - args->converter->toULength = 1; - goto endloop; - } - } - else{ - if(mySourceChar <= 0x7f) { - targetUniChar = (UChar) mySourceChar; - } - } - break; - } - if(targetUniChar < (missingCharMarker-1/*0xfffe*/)){ - if(args->offsets){ - args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); - } - *(myTarget++)=(UChar)targetUniChar; - } - else if(targetUniChar > missingCharMarker){ - /* disassemble the surrogate pair and write to output*/ - targetUniChar-=0x0010000; - *myTarget = (UChar)(0xd800+(UChar)(targetUniChar>>10)); - if(args->offsets){ - args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); - } - ++myTarget; - if(myTarget< args->targetLimit){ - *myTarget = (UChar)(0xdc00+(UChar)(targetUniChar&0x3ff)); - if(args->offsets){ - args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); - } - ++myTarget; - }else{ - args->converter->UCharErrorBuffer[args->converter->UCharErrorBufferLength++]= - (UChar)(0xdc00+(UChar)(targetUniChar&0x3ff)); - } - - } - else{ - /* Call the callback function*/ - toUnicodeCallback(args->converter,mySourceChar,targetUniChar,err); - break; - } - } - else{ - *err =U_BUFFER_OVERFLOW_ERROR; - break; - } - } -endloop: - args->target = myTarget; - args->source = mySource; -} -#endif /* #if !UCONFIG_ONLY_HTML_CONVERSION */ - -static void U_CALLCONV -_ISO_2022_WriteSub(UConverterFromUnicodeArgs *args, int32_t offsetIndex, UErrorCode *err) { - UConverter *cnv = args->converter; - UConverterDataISO2022 *myConverterData=(UConverterDataISO2022 *) cnv->extraInfo; - ISO2022State *pFromU2022State=&myConverterData->fromU2022State; - char *p, *subchar; - char buffer[8]; - int32_t length; - - subchar=(char *)cnv->subChars; - length=cnv->subCharLen; /* assume length==1 for most variants */ - - p = buffer; - switch(myConverterData->locale[0]){ - case 'j': - { - int8_t cs; - - if(pFromU2022State->g == 1) { - /* JIS7: switch from G1 to G0 */ - pFromU2022State->g = 0; - *p++ = UCNV_SI; - } - - cs = pFromU2022State->cs[0]; - if(cs != ASCII && cs != JISX201) { - /* not in ASCII or JIS X 0201: switch to ASCII */ - pFromU2022State->cs[0] = (int8_t)ASCII; - *p++ = '\x1b'; - *p++ = '\x28'; - *p++ = '\x42'; - } - - *p++ = subchar[0]; - break; - } - case 'c': - if(pFromU2022State->g != 0) { - /* not in ASCII mode: switch to ASCII */ - pFromU2022State->g = 0; - *p++ = UCNV_SI; - } - *p++ = subchar[0]; - break; - case 'k': - if(myConverterData->version == 0) { - if(length == 1) { - if(args->converter->fromUnicodeStatus) { - /* in DBCS mode: switch to SBCS */ - args->converter->fromUnicodeStatus = 0; - *p++ = UCNV_SI; - } - *p++ = subchar[0]; - } else /* length == 2*/ { - if(!args->converter->fromUnicodeStatus) { - /* in SBCS mode: switch to DBCS */ - args->converter->fromUnicodeStatus = 1; - *p++ = UCNV_SO; - } - *p++ = subchar[0]; - *p++ = subchar[1]; - } - break; - } else { - /* save the subconverter's substitution string */ - uint8_t *currentSubChars = myConverterData->currentConverter->subChars; - int8_t currentSubCharLen = myConverterData->currentConverter->subCharLen; - - /* set our substitution string into the subconverter */ - myConverterData->currentConverter->subChars = (uint8_t *)subchar; - myConverterData->currentConverter->subCharLen = (int8_t)length; - - /* let the subconverter write the subchar, set/retrieve fromUChar32 state */ - args->converter = myConverterData->currentConverter; - myConverterData->currentConverter->fromUChar32 = cnv->fromUChar32; - ucnv_cbFromUWriteSub(args, 0, err); - cnv->fromUChar32 = myConverterData->currentConverter->fromUChar32; - args->converter = cnv; - - /* restore the subconverter's substitution string */ - myConverterData->currentConverter->subChars = currentSubChars; - myConverterData->currentConverter->subCharLen = currentSubCharLen; - - if(*err == U_BUFFER_OVERFLOW_ERROR) { - if(myConverterData->currentConverter->charErrorBufferLength > 0) { - uprv_memcpy( - cnv->charErrorBuffer, - myConverterData->currentConverter->charErrorBuffer, - myConverterData->currentConverter->charErrorBufferLength); - } - cnv->charErrorBufferLength = myConverterData->currentConverter->charErrorBufferLength; - myConverterData->currentConverter->charErrorBufferLength = 0; - } - return; - } - default: - /* not expected */ - break; - } - ucnv_cbFromUWriteBytes(args, - buffer, (int32_t)(p - buffer), - offsetIndex, err); -} - -/* - * Structure for cloning an ISO 2022 converter into a single memory block. - */ -struct cloneStruct -{ - UConverter cnv; - UConverter currentConverter; - UConverterDataISO2022 mydata; -}; - - -U_CDECL_BEGIN - -static UConverter * U_CALLCONV -_ISO_2022_SafeClone( - const UConverter *cnv, - void *stackBuffer, - int32_t *pBufferSize, - UErrorCode *status) -{ - struct cloneStruct * localClone; - UConverterDataISO2022 *cnvData; - int32_t i, size; - - if (U_FAILURE(*status)){ - return nullptr; - } - - if (*pBufferSize == 0) { /* 'preflighting' request - set needed size into *pBufferSize */ - *pBufferSize = (int32_t)sizeof(struct cloneStruct); - return NULL; - } - - cnvData = (UConverterDataISO2022 *)cnv->extraInfo; - localClone = (struct cloneStruct *)stackBuffer; - - /* ucnv.c/ucnv_safeClone() copied the main UConverter already */ - - uprv_memcpy(&localClone->mydata, cnvData, sizeof(UConverterDataISO2022)); - localClone->cnv.extraInfo = &localClone->mydata; /* set pointer to extra data */ - localClone->cnv.isExtraLocal = true; - - /* share the subconverters */ - - if(cnvData->currentConverter != NULL) { - size = (int32_t)sizeof(UConverter); - localClone->mydata.currentConverter = - ucnv_safeClone(cnvData->currentConverter, - &localClone->currentConverter, - &size, status); - if(U_FAILURE(*status)) { - return NULL; - } - } - - for(i=0; imyConverterArray[i] != NULL) { - ucnv_incrementRefCount(cnvData->myConverterArray[i]); - } - } - - return &localClone->cnv; -} - -U_CDECL_END - -static void U_CALLCONV -_ISO_2022_GetUnicodeSet(const UConverter *cnv, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode) -{ - int32_t i; - UConverterDataISO2022* cnvData; - - if (U_FAILURE(*pErrorCode)) { - return; - } -#ifdef U_ENABLE_GENERIC_ISO_2022 - if (cnv->sharedData == &_ISO2022Data) { - /* We use UTF-8 in this case */ - sa->addRange(sa->set, 0, 0xd7FF); - sa->addRange(sa->set, 0xE000, 0x10FFFF); - return; - } -#endif - - cnvData = (UConverterDataISO2022*)cnv->extraInfo; - - /* open a set and initialize it with code points that are algorithmically round-tripped */ - switch(cnvData->locale[0]){ - case 'j': - /* include JIS X 0201 which is hardcoded */ - sa->add(sa->set, 0xa5); - sa->add(sa->set, 0x203e); - if(jpCharsetMasks[cnvData->version]&CSM(ISO8859_1)) { - /* include Latin-1 for some variants of JP */ - sa->addRange(sa->set, 0, 0xff); - } else { - /* include ASCII for JP */ - sa->addRange(sa->set, 0, 0x7f); - } - if(cnvData->version==3 || cnvData->version==4 || which==UCNV_ROUNDTRIP_AND_FALLBACK_SET) { - /* - * Do not test (jpCharsetMasks[cnvData->version]&CSM(HWKANA_7BIT))!=0 - * because the bit is on for all JP versions although only versions 3 & 4 (JIS7 & JIS8) - * use half-width Katakana. - * This is because all ISO-2022-JP variants are lenient in that they accept (in toUnicode) - * half-width Katakana via the ESC ( I sequence. - * However, we only emit (fromUnicode) half-width Katakana according to the - * definition of each variant. - * - * When including fallbacks, - * we need to include half-width Katakana Unicode code points for all JP variants because - * JIS X 0208 has hardcoded fallbacks for them (which map to full-width Katakana). - */ - /* include half-width Katakana for JP */ - sa->addRange(sa->set, HWKANA_START, HWKANA_END); - } - break; -#if !UCONFIG_ONLY_HTML_CONVERSION - case 'c': - case 'z': - /* include ASCII for CN */ - sa->addRange(sa->set, 0, 0x7f); - break; - case 'k': - /* there is only one converter for KR, and it is not in the myConverterArray[] */ - cnvData->currentConverter->sharedData->impl->getUnicodeSet( - cnvData->currentConverter, sa, which, pErrorCode); - /* the loop over myConverterArray[] will simply not find another converter */ - break; -#endif - default: - break; - } - -#if 0 /* Replaced by ucnv_MBCSGetFilteredUnicodeSetForUnicode() until we implement ucnv_getUnicodeSet() with reverse fallbacks. */ - if( (cnvData->locale[0]=='c' || cnvData->locale[0]=='z') && - cnvData->version==0 && i==CNS_11643 - ) { - /* special handling for non-EXT ISO-2022-CN: add only code points for CNS planes 1 and 2 */ - ucnv_MBCSGetUnicodeSetForBytes( - cnvData->myConverterArray[i], - sa, UCNV_ROUNDTRIP_SET, - 0, 0x81, 0x82, - pErrorCode); - } -#endif - - for (i=0; imyConverterArray[i]!=NULL) { - if(cnvData->locale[0]=='j' && i==JISX208) { - /* - * Only add code points that map to Shift-JIS codes - * corresponding to JIS X 0208. - */ - filter=UCNV_SET_FILTER_SJIS; -#if !UCONFIG_ONLY_HTML_CONVERSION - } else if( (cnvData->locale[0]=='c' || cnvData->locale[0]=='z') && - cnvData->version==0 && i==CNS_11643) { - /* - * Version-specific for CN: - * CN version 0 does not map CNS planes 3..7 although - * they are all available in the CNS conversion table; - * CN version 1 (-EXT) does map them all. - * The two versions create different Unicode sets. - */ - filter=UCNV_SET_FILTER_2022_CN; - } else if(i==KSC5601) { - /* - * Some of the KSC 5601 tables (convrtrs.txt has this aliases on multiple tables) - * are broader than GR94. - */ - filter=UCNV_SET_FILTER_GR94DBCS; -#endif - } else { - filter=UCNV_SET_FILTER_NONE; - } - ucnv_MBCSGetFilteredUnicodeSetForUnicode(cnvData->myConverterArray[i], sa, which, filter, pErrorCode); - } - } - - /* - * ISO 2022 converters must not convert SO/SI/ESC despite what - * sub-converters do by themselves. - * Remove these characters from the set. - */ - sa->remove(sa->set, 0x0e); - sa->remove(sa->set, 0x0f); - sa->remove(sa->set, 0x1b); - - /* ISO 2022 converters do not convert C1 controls either */ - sa->removeRange(sa->set, 0x80, 0x9f); -} - -static const UConverterImpl _ISO2022Impl={ - UCNV_ISO_2022, - - NULL, - NULL, - - _ISO2022Open, - _ISO2022Close, - _ISO2022Reset, - -#ifdef U_ENABLE_GENERIC_ISO_2022 - T_UConverter_toUnicode_ISO_2022_OFFSETS_LOGIC, - T_UConverter_toUnicode_ISO_2022_OFFSETS_LOGIC, - ucnv_fromUnicode_UTF8, - ucnv_fromUnicode_UTF8_OFFSETS_LOGIC, -#else - NULL, - NULL, - NULL, - NULL, -#endif - NULL, - - NULL, - _ISO2022getName, - _ISO_2022_WriteSub, - _ISO_2022_SafeClone, - _ISO_2022_GetUnicodeSet, - - NULL, - NULL -}; -static const UConverterStaticData _ISO2022StaticData={ - sizeof(UConverterStaticData), - "ISO_2022", - 2022, - UCNV_IBM, - UCNV_ISO_2022, - 1, - 3, /* max 3 bytes per UChar from UTF-8 (4 bytes from surrogate _pair_) */ - { 0x1a, 0, 0, 0 }, - 1, - false, - false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; -const UConverterSharedData _ISO2022Data= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_ISO2022StaticData, &_ISO2022Impl); - -/*************JP****************/ -static const UConverterImpl _ISO2022JPImpl={ - UCNV_ISO_2022, - - NULL, - NULL, - - _ISO2022Open, - _ISO2022Close, - _ISO2022Reset, - - UConverter_toUnicode_ISO_2022_JP_OFFSETS_LOGIC, - UConverter_toUnicode_ISO_2022_JP_OFFSETS_LOGIC, - UConverter_fromUnicode_ISO_2022_JP_OFFSETS_LOGIC, - UConverter_fromUnicode_ISO_2022_JP_OFFSETS_LOGIC, - NULL, - - NULL, - _ISO2022getName, - _ISO_2022_WriteSub, - _ISO_2022_SafeClone, - _ISO_2022_GetUnicodeSet, - - NULL, - NULL -}; -static const UConverterStaticData _ISO2022JPStaticData={ - sizeof(UConverterStaticData), - "ISO_2022_JP", - 0, - UCNV_IBM, - UCNV_ISO_2022, - 1, - 6, /* max 6 bytes per UChar: 4-byte escape sequence + DBCS */ - { 0x1a, 0, 0, 0 }, - 1, - false, - false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - -namespace { - -const UConverterSharedData _ISO2022JPData= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_ISO2022JPStaticData, &_ISO2022JPImpl); - -} // namespace - -#if !UCONFIG_ONLY_HTML_CONVERSION -/************* KR ***************/ -static const UConverterImpl _ISO2022KRImpl={ - UCNV_ISO_2022, - - NULL, - NULL, - - _ISO2022Open, - _ISO2022Close, - _ISO2022Reset, - - UConverter_toUnicode_ISO_2022_KR_OFFSETS_LOGIC, - UConverter_toUnicode_ISO_2022_KR_OFFSETS_LOGIC, - UConverter_fromUnicode_ISO_2022_KR_OFFSETS_LOGIC, - UConverter_fromUnicode_ISO_2022_KR_OFFSETS_LOGIC, - NULL, - - NULL, - _ISO2022getName, - _ISO_2022_WriteSub, - _ISO_2022_SafeClone, - _ISO_2022_GetUnicodeSet, - - NULL, - NULL -}; -static const UConverterStaticData _ISO2022KRStaticData={ - sizeof(UConverterStaticData), - "ISO_2022_KR", - 0, - UCNV_IBM, - UCNV_ISO_2022, - 1, - 8, /* max 8 bytes per UChar */ - { 0x1a, 0, 0, 0 }, - 1, - false, - false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - -namespace { - -const UConverterSharedData _ISO2022KRData= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_ISO2022KRStaticData, &_ISO2022KRImpl); - -} // namespace - -/*************** CN ***************/ -static const UConverterImpl _ISO2022CNImpl={ - - UCNV_ISO_2022, - - NULL, - NULL, - - _ISO2022Open, - _ISO2022Close, - _ISO2022Reset, - - UConverter_toUnicode_ISO_2022_CN_OFFSETS_LOGIC, - UConverter_toUnicode_ISO_2022_CN_OFFSETS_LOGIC, - UConverter_fromUnicode_ISO_2022_CN_OFFSETS_LOGIC, - UConverter_fromUnicode_ISO_2022_CN_OFFSETS_LOGIC, - NULL, - - NULL, - _ISO2022getName, - _ISO_2022_WriteSub, - _ISO_2022_SafeClone, - _ISO_2022_GetUnicodeSet, - - NULL, - NULL -}; -static const UConverterStaticData _ISO2022CNStaticData={ - sizeof(UConverterStaticData), - "ISO_2022_CN", - 0, - UCNV_IBM, - UCNV_ISO_2022, - 1, - 8, /* max 8 bytes per UChar: 4-byte CNS designator + 2 bytes for SS2/SS3 + DBCS */ - { 0x1a, 0, 0, 0 }, - 1, - false, - false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - -namespace { - -const UConverterSharedData _ISO2022CNData= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_ISO2022CNStaticData, &_ISO2022CNImpl); - -} // namespace -#endif /* #if !UCONFIG_ONLY_HTML_CONVERSION */ - -#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2000-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: ucnv2022.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2000feb03 +* created by: Markus W. Scherer +* +* Change history: +* +* 06/29/2000 helena Major rewrite of the callback APIs. +* 08/08/2000 Ram Included support for ISO-2022-JP-2 +* Changed implementation of toUnicode +* function +* 08/21/2000 Ram Added support for ISO-2022-KR +* 08/29/2000 Ram Seperated implementation of EBCDIC to +* ucnvebdc.c +* 09/20/2000 Ram Added support for ISO-2022-CN +* Added implementations for getNextUChar() +* for specific 2022 country variants. +* 10/31/2000 Ram Implemented offsets logic functions +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/uset.h" +#include "unicode/ucnv_err.h" +#include "unicode/ucnv_cb.h" +#include "unicode/utf16.h" +#include "ucnv_imp.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" +#include "ucnvmbcs.h" +#include "cstring.h" +#include "cmemory.h" +#include "uassert.h" + +#ifdef U_ENABLE_GENERIC_ISO_2022 +/* + * I am disabling the generic ISO-2022 converter after proposing to do so on + * the icu mailing list two days ago. + * + * Reasons: + * 1. It does not fully support the ISO-2022/ECMA-35 specification with all of + * its designation sequences, single shifts with return to the previous state, + * switch-with-no-return to UTF-16BE or similar, etc. + * This is unlike the language-specific variants like ISO-2022-JP which + * require a much smaller repertoire of ISO-2022 features. + * These variants continue to be supported. + * 2. I believe that no one is really using the generic ISO-2022 converter + * but rather always one of the language-specific variants. + * Note that ICU's generic ISO-2022 converter has always output one escape + * sequence followed by UTF-8 for the whole stream. + * 3. Switching between subcharsets is extremely slow, because each time + * the previous converter is closed and a new one opened, + * without any kind of caching, least-recently-used list, etc. + * 4. The code is currently buggy, and given the above it does not seem + * reasonable to spend the time on maintenance. + * 5. ISO-2022 subcharsets should normally be used with 7-bit byte encodings. + * This means, for example, that when ISO-8859-7 is designated, the following + * ISO-2022 bytes 00..7f should be interpreted as ISO-8859-7 bytes 80..ff. + * The ICU ISO-2022 converter does not handle this - and has no information + * about which subconverter would have to be shifted vs. which is designed + * for 7-bit ISO-2022. + * + * Markus Scherer 2003-dec-03 + */ +#endif + +#if !UCONFIG_ONLY_HTML_CONVERSION +static const char SHIFT_IN_STR[] = "\x0F"; +// static const char SHIFT_OUT_STR[] = "\x0E"; +#endif + +#define CR 0x0D +#define LF 0x0A +#define H_TAB 0x09 +#define V_TAB 0x0B +#define SPACE 0x20 + +enum { + HWKANA_START=0xff61, + HWKANA_END=0xff9f +}; + +/* + * 94-character sets with native byte values A1..FE are encoded in ISO 2022 + * as bytes 21..7E. (Subtract 0x80.) + * 96-character sets with native byte values A0..FF are encoded in ISO 2022 + * as bytes 20..7F. (Subtract 0x80.) + * Do not encode C1 control codes with native bytes 80..9F + * as bytes 00..1F (C0 control codes). + */ +enum { + GR94_START=0xa1, + GR94_END=0xfe, + GR96_START=0xa0, + GR96_END=0xff +}; + +/* + * ISO 2022 control codes must not be converted from Unicode + * because they would mess up the byte stream. + * The bit mask 0x0800c000 has bits set at bit positions 0xe, 0xf, 0x1b + * corresponding to SO, SI, and ESC. + */ +#define IS_2022_CONTROL(c) (((c)<0x20) && (((uint32_t)1<<(c))&0x0800c000)!=0) + +/* for ISO-2022-JP and -CN implementations */ +typedef enum { + /* shared values */ + INVALID_STATE=-1, + ASCII = 0, + + SS2_STATE=0x10, + SS3_STATE, + + /* JP */ + ISO8859_1 = 1 , + ISO8859_7 = 2 , + JISX201 = 3, + JISX208 = 4, + JISX212 = 5, + GB2312 =6, + KSC5601 =7, + HWKANA_7BIT=8, /* Halfwidth Katakana 7 bit */ + + /* CN */ + /* the first few enum constants must keep their values because they correspond to myConverterArray[] */ + GB2312_1=1, + ISO_IR_165=2, + CNS_11643=3, + + /* + * these are used in StateEnum and ISO2022State variables, + * but CNS_11643 must be used to index into myConverterArray[] + */ + CNS_11643_0=0x20, + CNS_11643_1, + CNS_11643_2, + CNS_11643_3, + CNS_11643_4, + CNS_11643_5, + CNS_11643_6, + CNS_11643_7 +} StateEnum; + +/* is the StateEnum charset value for a DBCS charset? */ +#if UCONFIG_ONLY_HTML_CONVERSION +#define IS_JP_DBCS(cs) (JISX208==(cs)) +#else +#define IS_JP_DBCS(cs) (JISX208<=(cs) && (cs)<=KSC5601) +#endif + +#define CSM(cs) ((uint16_t)1<<(cs)) + +/* + * Each of these charset masks (with index x) contains a bit for a charset in exact correspondence + * to whether that charset is used in the corresponding version x of ISO_2022,locale=ja,version=x + * + * Note: The converter uses some leniency: + * - The escape sequence ESC ( I for half-width 7-bit Katakana is recognized in + * all versions, not just JIS7 and JIS8. + * - ICU does not distinguish between different versions of JIS X 0208. + */ +#if UCONFIG_ONLY_HTML_CONVERSION +enum { MAX_JA_VERSION=0 }; +#else +enum { MAX_JA_VERSION=4 }; +#endif +static const uint16_t jpCharsetMasks[MAX_JA_VERSION+1]={ + CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT), +#if !UCONFIG_ONLY_HTML_CONVERSION + CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT)|CSM(JISX212), + CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT)|CSM(JISX212)|CSM(GB2312)|CSM(KSC5601)|CSM(ISO8859_1)|CSM(ISO8859_7), + CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT)|CSM(JISX212)|CSM(GB2312)|CSM(KSC5601)|CSM(ISO8859_1)|CSM(ISO8859_7), + CSM(ASCII)|CSM(JISX201)|CSM(JISX208)|CSM(HWKANA_7BIT)|CSM(JISX212)|CSM(GB2312)|CSM(KSC5601)|CSM(ISO8859_1)|CSM(ISO8859_7) +#endif +}; + +typedef enum { + ASCII1=0, + LATIN1, + SBCS, + DBCS, + MBCS, + HWKANA +}Cnv2022Type; + +typedef struct ISO2022State { + int8_t cs[4]; /* charset number for SI (G0)/SO (G1)/SS2 (G2)/SS3 (G3) */ + int8_t g; /* 0..3 for G0..G3 (SI/SO/SS2/SS3) */ + int8_t prevG; /* g before single shift (SS2 or SS3) */ +} ISO2022State; + +#define UCNV_OPTIONS_VERSION_MASK 0xf +#define UCNV_2022_MAX_CONVERTERS 10 + +typedef struct{ + UConverterSharedData *myConverterArray[UCNV_2022_MAX_CONVERTERS]; + UConverter *currentConverter; + Cnv2022Type currentType; + ISO2022State toU2022State, fromU2022State; + uint32_t key; + uint32_t version; +#ifdef U_ENABLE_GENERIC_ISO_2022 + UBool isFirstBuffer; +#endif + UBool isEmptySegment; + char name[30]; + char locale[3]; +}UConverterDataISO2022; + +/* Protos */ +/* ISO-2022 ----------------------------------------------------------------- */ + +/*Forward declaration */ +U_CFUNC void U_CALLCONV +ucnv_fromUnicode_UTF8(UConverterFromUnicodeArgs * args, + UErrorCode * err); +U_CFUNC void U_CALLCONV +ucnv_fromUnicode_UTF8_OFFSETS_LOGIC(UConverterFromUnicodeArgs * args, + UErrorCode * err); + +#define ESC_2022 0x1B /*ESC*/ + +typedef enum +{ + INVALID_2022 = -1, /*Doesn't correspond to a valid iso 2022 escape sequence*/ + VALID_NON_TERMINAL_2022 = 0, /*so far corresponds to a valid iso 2022 escape sequence*/ + VALID_TERMINAL_2022 = 1, /*corresponds to a valid iso 2022 escape sequence*/ + VALID_MAYBE_TERMINAL_2022 = 2 /*so far matches one iso 2022 escape sequence, but by adding more characters might match another escape sequence*/ +} UCNV_TableStates_2022; + +/* +* The way these state transition arrays work is: +* ex : ESC$B is the sequence for JISX208 +* a) First Iteration: char is ESC +* i) Get the value of ESC from normalize_esq_chars_2022[] with int value of ESC as index +* int x = normalize_esq_chars_2022[27] which is equal to 1 +* ii) Search for this value in escSeqStateTable_Key_2022[] +* value of x is stored at escSeqStateTable_Key_2022[0] +* iii) Save this index as offset +* iv) Get state of this sequence from escSeqStateTable_Value_2022[] +* escSeqStateTable_Value_2022[offset], which is VALID_NON_TERMINAL_2022 +* b) Switch on this state and continue to next char +* i) Get the value of $ from normalize_esq_chars_2022[] with int value of $ as index +* which is normalize_esq_chars_2022[36] == 4 +* ii) x is currently 1(from above) +* x<<=5 -- x is now 32 +* x+=normalize_esq_chars_2022[36] +* now x is 36 +* iii) Search for this value in escSeqStateTable_Key_2022[] +* value of x is stored at escSeqStateTable_Key_2022[2], so offset is 2 +* iv) Get state of this sequence from escSeqStateTable_Value_2022[] +* escSeqStateTable_Value_2022[offset], which is VALID_NON_TERMINAL_2022 +* c) Switch on this state and continue to next char +* i) Get the value of B from normalize_esq_chars_2022[] with int value of B as index +* ii) x is currently 36 (from above) +* x<<=5 -- x is now 1152 +* x+=normalize_esq_chars_2022[66] +* now x is 1161 +* iii) Search for this value in escSeqStateTable_Key_2022[] +* value of x is stored at escSeqStateTable_Key_2022[21], so offset is 21 +* iv) Get state of this sequence from escSeqStateTable_Value_2022[21] +* escSeqStateTable_Value_2022[offset], which is VALID_TERMINAL_2022 +* v) Get the converter name form escSeqStateTable_Result_2022[21] which is JISX208 +*/ + + +/*Below are the 3 arrays depicting a state transition table*/ +static const int8_t normalize_esq_chars_2022[256] = { +/* 0 1 2 3 4 5 6 7 8 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 ,0 ,1 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,4 ,7 ,29 ,0 + ,2 ,24 ,26 ,27 ,0 ,3 ,23 ,6 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,5 ,8 ,9 ,10 ,11 ,12 + ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20 ,25 ,28 + ,0 ,0 ,21 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,22 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + ,0 ,0 ,0 ,0 ,0 ,0 +}; + +#ifdef U_ENABLE_GENERIC_ISO_2022 +/* + * When the generic ISO-2022 converter is completely removed, not just disabled + * per #ifdef, then the following state table and the associated tables that are + * dimensioned with MAX_STATES_2022 should be trimmed. + * + * Especially, VALID_MAYBE_TERMINAL_2022 will not be used any more, and all of + * the associated escape sequences starting with ESC ( B should be removed. + * This includes the ones with key values 1097 and all of the ones above 1000000. + * + * For the latter, the tables can simply be truncated. + * For the former, since the tables must be kept parallel, it is probably best + * to simply duplicate an adjacent table cell, parallel in all tables. + * + * It may make sense to restructure the tables, especially by using small search + * tables for the variants instead of indexing them parallel to the table here. + */ +#endif + +#define MAX_STATES_2022 74 +static const int32_t escSeqStateTable_Key_2022[MAX_STATES_2022] = { +/* 0 1 2 3 4 5 6 7 8 9 */ + + 1 ,34 ,36 ,39 ,55 ,57 ,60 ,61 ,1093 ,1096 + ,1097 ,1098 ,1099 ,1100 ,1101 ,1102 ,1103 ,1104 ,1105 ,1106 + ,1109 ,1154 ,1157 ,1160 ,1161 ,1176 ,1178 ,1179 ,1254 ,1257 + ,1768 ,1773 ,1957 ,35105 ,36933 ,36936 ,36937 ,36938 ,36939 ,36940 + ,36942 ,36943 ,36944 ,36945 ,36946 ,36947 ,36948 ,37640 ,37642 ,37644 + ,37646 ,37711 ,37744 ,37745 ,37746 ,37747 ,37748 ,40133 ,40136 ,40138 + ,40139 ,40140 ,40141 ,1123363 ,35947624 ,35947625 ,35947626 ,35947627 ,35947629 ,35947630 + ,35947631 ,35947635 ,35947636 ,35947638 +}; + +#ifdef U_ENABLE_GENERIC_ISO_2022 + +static const char* const escSeqStateTable_Result_2022[MAX_STATES_2022] = { + /* 0 1 2 3 4 5 6 7 8 9 */ + + nullptr ,nullptr ,nullptr ,nullptr ,nullptr ,nullptr ,nullptr ,nullptr ,"latin1" ,"latin1" + ,"latin1" ,"ibm-865" ,"ibm-865" ,"ibm-865" ,"ibm-865" ,"ibm-865" ,"ibm-865" ,"JISX0201" ,"JISX0201" ,"latin1" + ,"latin1" ,nullptr ,"JISX-208" ,"ibm-5478" ,"JISX-208" ,nullptr ,nullptr ,nullptr ,nullptr ,"UTF8" + ,"ISO-8859-1" ,"ISO-8859-7" ,"JIS-X-208" ,nullptr ,"ibm-955" ,"ibm-367" ,"ibm-952" ,"ibm-949" ,"JISX-212" ,"ibm-1383" + ,"ibm-952" ,"ibm-964" ,"ibm-964" ,"ibm-964" ,"ibm-964" ,"ibm-964" ,"ibm-964" ,"ibm-5478" ,"ibm-949" ,"ISO-IR-165" + ,"CNS-11643-1992,1" ,"CNS-11643-1992,2" ,"CNS-11643-1992,3" ,"CNS-11643-1992,4" ,"CNS-11643-1992,5" ,"CNS-11643-1992,6" ,"CNS-11643-1992,7" ,"UTF16_PlatformEndian" ,"UTF16_PlatformEndian" ,"UTF16_PlatformEndian" + ,"UTF16_PlatformEndian" ,"UTF16_PlatformEndian" ,"UTF16_PlatformEndian" ,nullptr ,"latin1" ,"ibm-912" ,"ibm-913" ,"ibm-914" ,"ibm-813" ,"ibm-1089" + ,"ibm-920" ,"ibm-915" ,"ibm-915" ,"latin1" +}; + +#endif + +static const int8_t escSeqStateTable_Value_2022[MAX_STATES_2022] = { +/* 0 1 2 3 4 5 6 7 8 9 */ + VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 + ,VALID_MAYBE_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 + ,VALID_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_TERMINAL_2022 + ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 + ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 + ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 + ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_NON_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 + ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 ,VALID_TERMINAL_2022 +}; + +/* Type def for refactoring changeState_2022 code*/ +typedef enum{ +#ifdef U_ENABLE_GENERIC_ISO_2022 + ISO_2022=0, +#endif + ISO_2022_JP=1, +#if !UCONFIG_ONLY_HTML_CONVERSION + ISO_2022_KR=2, + ISO_2022_CN=3 +#endif +} Variant2022; + +/*********** ISO 2022 Converter Protos ***********/ +static void U_CALLCONV +_ISO2022Open(UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *errorCode); + +static void U_CALLCONV + _ISO2022Close(UConverter *converter); + +static void U_CALLCONV +_ISO2022Reset(UConverter *converter, UConverterResetChoice choice); + +U_CDECL_BEGIN +static const char * U_CALLCONV +_ISO2022getName(const UConverter* cnv); +U_CDECL_END + +static void U_CALLCONV +_ISO_2022_WriteSub(UConverterFromUnicodeArgs *args, int32_t offsetIndex, UErrorCode *err); + +U_CDECL_BEGIN +static UConverter * U_CALLCONV +_ISO_2022_SafeClone(const UConverter *cnv, void *stackBuffer, int32_t *pBufferSize, UErrorCode *status); + +U_CDECL_END + +#ifdef U_ENABLE_GENERIC_ISO_2022 +static void U_CALLCONV +T_UConverter_toUnicode_ISO_2022_OFFSETS_LOGIC(UConverterToUnicodeArgs* args, UErrorCode* err); +#endif + +namespace { + +/*const UConverterSharedData _ISO2022Data;*/ +extern const UConverterSharedData _ISO2022JPData; + +#if !UCONFIG_ONLY_HTML_CONVERSION +extern const UConverterSharedData _ISO2022KRData; +extern const UConverterSharedData _ISO2022CNData; +#endif + +} // namespace + +/*************** Converter implementations ******************/ + +/* The purpose of this function is to get around gcc compiler warnings. */ +static inline void +fromUWriteUInt8(UConverter *cnv, + const char *bytes, int32_t length, + uint8_t **target, const char *targetLimit, + int32_t **offsets, + int32_t sourceIndex, + UErrorCode *pErrorCode) +{ + char *targetChars = (char *)*target; + ucnv_fromUWriteBytes(cnv, bytes, length, &targetChars, targetLimit, + offsets, sourceIndex, pErrorCode); + *target = (uint8_t*)targetChars; + +} + +static inline void +setInitialStateToUnicodeKR(UConverter* /*converter*/, UConverterDataISO2022 *myConverterData){ + if(myConverterData->version == 1) { + UConverter *cnv = myConverterData->currentConverter; + + cnv->toUnicodeStatus=0; /* offset */ + cnv->mode=0; /* state */ + cnv->toULength=0; /* byteIndex */ + } +} + +static inline void +setInitialStateFromUnicodeKR(UConverter* converter,UConverterDataISO2022 *myConverterData){ + /* in ISO-2022-KR the designator sequence appears only once + * in a file so we append it only once + */ + if( converter->charErrorBufferLength==0){ + + converter->charErrorBufferLength = 4; + converter->charErrorBuffer[0] = 0x1b; + converter->charErrorBuffer[1] = 0x24; + converter->charErrorBuffer[2] = 0x29; + converter->charErrorBuffer[3] = 0x43; + } + if(myConverterData->version == 1) { + UConverter *cnv = myConverterData->currentConverter; + + cnv->fromUChar32=0; + cnv->fromUnicodeStatus=1; /* prevLength */ + } +} + +static void U_CALLCONV +_ISO2022Open(UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *errorCode){ + + char myLocale[7]={' ',' ',' ',' ',' ',' ', '\0'}; + + cnv->extraInfo = uprv_malloc (sizeof (UConverterDataISO2022)); + if(cnv->extraInfo != nullptr) { + UConverterNamePieces stackPieces; + UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; + UConverterDataISO2022 *myConverterData=(UConverterDataISO2022 *) cnv->extraInfo; + uint32_t version; + + stackArgs.onlyTestIsLoadable = pArgs->onlyTestIsLoadable; + + uprv_memset(myConverterData, 0, sizeof(UConverterDataISO2022)); + myConverterData->currentType = ASCII1; + cnv->fromUnicodeStatus =false; + if(pArgs->locale){ + uprv_strncpy(myLocale, pArgs->locale, sizeof(myLocale)-1); + } + version = pArgs->options & UCNV_OPTIONS_VERSION_MASK; + myConverterData->version = version; + if(myLocale[0]=='j' && (myLocale[1]=='a'|| myLocale[1]=='p') && + (myLocale[2]=='_' || myLocale[2]=='\0')) + { + /* open the required converters and cache them */ + if(version>MAX_JA_VERSION) { + // ICU 55 fails to open a converter for an unsupported version. + // Previously, it fell back to version 0, but that would yield + // unexpected behavior. + *errorCode = U_MISSING_RESOURCE_ERROR; + return; + } + if(jpCharsetMasks[version]&CSM(ISO8859_7)) { + myConverterData->myConverterArray[ISO8859_7] = + ucnv_loadSharedData("ISO8859_7", &stackPieces, &stackArgs, errorCode); + } + myConverterData->myConverterArray[JISX208] = + ucnv_loadSharedData("Shift-JIS", &stackPieces, &stackArgs, errorCode); + if(jpCharsetMasks[version]&CSM(JISX212)) { + myConverterData->myConverterArray[JISX212] = + ucnv_loadSharedData("jisx-212", &stackPieces, &stackArgs, errorCode); + } + if(jpCharsetMasks[version]&CSM(GB2312)) { + myConverterData->myConverterArray[GB2312] = + ucnv_loadSharedData("ibm-5478", &stackPieces, &stackArgs, errorCode); /* gb_2312_80-1 */ + } + if(jpCharsetMasks[version]&CSM(KSC5601)) { + myConverterData->myConverterArray[KSC5601] = + ucnv_loadSharedData("ksc_5601", &stackPieces, &stackArgs, errorCode); + } + + /* set the function pointers to appropriate functions */ + cnv->sharedData=(UConverterSharedData*)(&_ISO2022JPData); + uprv_strcpy(myConverterData->locale,"ja"); + + (void)uprv_strcpy(myConverterData->name,"ISO_2022,locale=ja,version="); + size_t len = uprv_strlen(myConverterData->name); + myConverterData->name[len]=(char)(myConverterData->version+(int)'0'); + myConverterData->name[len+1]='\0'; + } +#if !UCONFIG_ONLY_HTML_CONVERSION + else if(myLocale[0]=='k' && (myLocale[1]=='o'|| myLocale[1]=='r') && + (myLocale[2]=='_' || myLocale[2]=='\0')) + { + if(version>1) { + // ICU 55 fails to open a converter for an unsupported version. + // Previously, it fell back to version 0, but that would yield + // unexpected behavior. + *errorCode = U_MISSING_RESOURCE_ERROR; + return; + } + const char *cnvName; + if(version==1) { + cnvName="icu-internal-25546"; + } else { + cnvName="ibm-949"; + myConverterData->version=version=0; + } + if(pArgs->onlyTestIsLoadable) { + ucnv_canCreateConverter(cnvName, errorCode); /* errorCode carries result */ + uprv_free(cnv->extraInfo); + cnv->extraInfo=nullptr; + return; + } else { + myConverterData->currentConverter=ucnv_open(cnvName, errorCode); + if (U_FAILURE(*errorCode)) { + _ISO2022Close(cnv); + return; + } + + if(version==1) { + (void)uprv_strcpy(myConverterData->name,"ISO_2022,locale=ko,version=1"); + uprv_memcpy(cnv->subChars, myConverterData->currentConverter->subChars, 4); + cnv->subCharLen = myConverterData->currentConverter->subCharLen; + }else{ + (void)uprv_strcpy(myConverterData->name,"ISO_2022,locale=ko,version=0"); + } + + /* initialize the state variables */ + setInitialStateToUnicodeKR(cnv, myConverterData); + setInitialStateFromUnicodeKR(cnv, myConverterData); + + /* set the function pointers to appropriate functions */ + cnv->sharedData=(UConverterSharedData*)&_ISO2022KRData; + uprv_strcpy(myConverterData->locale,"ko"); + } + } + else if(((myLocale[0]=='z' && myLocale[1]=='h') || (myLocale[0]=='c'&& myLocale[1]=='n'))&& + (myLocale[2]=='_' || myLocale[2]=='\0')) + { + if(version>2) { + // ICU 55 fails to open a converter for an unsupported version. + // Previously, it fell back to version 0, but that would yield + // unexpected behavior. + *errorCode = U_MISSING_RESOURCE_ERROR; + return; + } + + /* open the required converters and cache them */ + myConverterData->myConverterArray[GB2312_1] = + ucnv_loadSharedData("ibm-5478", &stackPieces, &stackArgs, errorCode); + if(version==1) { + myConverterData->myConverterArray[ISO_IR_165] = + ucnv_loadSharedData("iso-ir-165", &stackPieces, &stackArgs, errorCode); + } + myConverterData->myConverterArray[CNS_11643] = + ucnv_loadSharedData("cns-11643-1992", &stackPieces, &stackArgs, errorCode); + + + /* set the function pointers to appropriate functions */ + cnv->sharedData=(UConverterSharedData*)&_ISO2022CNData; + uprv_strcpy(myConverterData->locale,"cn"); + + if (version==0){ + myConverterData->version = 0; + (void)uprv_strcpy(myConverterData->name,"ISO_2022,locale=zh,version=0"); + }else if (version==1){ + myConverterData->version = 1; + (void)uprv_strcpy(myConverterData->name,"ISO_2022,locale=zh,version=1"); + }else { + myConverterData->version = 2; + (void)uprv_strcpy(myConverterData->name,"ISO_2022,locale=zh,version=2"); + } + } +#endif // !UCONFIG_ONLY_HTML_CONVERSION + else{ +#ifdef U_ENABLE_GENERIC_ISO_2022 + myConverterData->isFirstBuffer = true; + + /* append the UTF-8 escape sequence */ + cnv->charErrorBufferLength = 3; + cnv->charErrorBuffer[0] = 0x1b; + cnv->charErrorBuffer[1] = 0x25; + cnv->charErrorBuffer[2] = 0x42; + + cnv->sharedData=(UConverterSharedData*)&_ISO2022Data; + /* initialize the state variables */ + uprv_strcpy(myConverterData->name,"ISO_2022"); +#else + *errorCode = U_MISSING_RESOURCE_ERROR; + // Was U_UNSUPPORTED_ERROR but changed in ICU 55 to a more standard + // data loading error code. + return; +#endif + } + + cnv->maxBytesPerUChar=cnv->sharedData->staticData->maxBytesPerChar; + + if(U_FAILURE(*errorCode) || pArgs->onlyTestIsLoadable) { + _ISO2022Close(cnv); + } + } else { + *errorCode = U_MEMORY_ALLOCATION_ERROR; + } +} + + +static void U_CALLCONV +_ISO2022Close(UConverter *converter) { + UConverterDataISO2022* myData =(UConverterDataISO2022 *) (converter->extraInfo); + UConverterSharedData **array = myData->myConverterArray; + int32_t i; + + if (converter->extraInfo != nullptr) { + /*close the array of converter pointers and free the memory*/ + for (i=0; icurrentConverter); + + if(!converter->isExtraLocal){ + uprv_free (converter->extraInfo); + converter->extraInfo = nullptr; + } + } +} + +static void U_CALLCONV +_ISO2022Reset(UConverter *converter, UConverterResetChoice choice) { + UConverterDataISO2022 *myConverterData=(UConverterDataISO2022 *) (converter->extraInfo); + if(choice<=UCNV_RESET_TO_UNICODE) { + uprv_memset(&myConverterData->toU2022State, 0, sizeof(ISO2022State)); + myConverterData->key = 0; + myConverterData->isEmptySegment = false; + } + if(choice!=UCNV_RESET_TO_UNICODE) { + uprv_memset(&myConverterData->fromU2022State, 0, sizeof(ISO2022State)); + } +#ifdef U_ENABLE_GENERIC_ISO_2022 + if(myConverterData->locale[0] == 0){ + if(choice<=UCNV_RESET_TO_UNICODE) { + myConverterData->isFirstBuffer = true; + myConverterData->key = 0; + if (converter->mode == UCNV_SO){ + ucnv_close (myConverterData->currentConverter); + myConverterData->currentConverter=nullptr; + } + converter->mode = UCNV_SI; + } + if(choice!=UCNV_RESET_TO_UNICODE) { + /* re-append UTF-8 escape sequence */ + converter->charErrorBufferLength = 3; + converter->charErrorBuffer[0] = 0x1b; + converter->charErrorBuffer[1] = 0x28; + converter->charErrorBuffer[2] = 0x42; + } + } + else +#endif + { + /* reset the state variables */ + if(myConverterData->locale[0] == 'k'){ + if(choice<=UCNV_RESET_TO_UNICODE) { + setInitialStateToUnicodeKR(converter, myConverterData); + } + if(choice!=UCNV_RESET_TO_UNICODE) { + setInitialStateFromUnicodeKR(converter, myConverterData); + } + } + } +} + +U_CDECL_BEGIN + +static const char * U_CALLCONV +_ISO2022getName(const UConverter* cnv){ + if(cnv->extraInfo){ + UConverterDataISO2022* myData= (UConverterDataISO2022*)cnv->extraInfo; + return myData->name; + } + return nullptr; +} + +U_CDECL_END + + +/*************** to unicode *******************/ +/**************************************************************************** + * Recognized escape sequences are + * (B ASCII + * .A ISO-8859-1 + * .F ISO-8859-7 + * (J JISX-201 + * (I JISX-201 + * $B JISX-208 + * $@ JISX-208 + * $(D JISX-212 + * $A GB2312 + * $(C KSC5601 + */ +static const int8_t nextStateToUnicodeJP[MAX_STATES_2022]= { +/* 0 1 2 3 4 5 6 7 8 9 */ + INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,SS2_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE + ,ASCII ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,JISX201 ,HWKANA_7BIT ,JISX201 ,INVALID_STATE + ,INVALID_STATE ,INVALID_STATE ,JISX208 ,GB2312 ,JISX208 ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE + ,ISO8859_1 ,ISO8859_7 ,JISX208 ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,KSC5601 ,JISX212 ,INVALID_STATE + ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE + ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE + ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE + ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE +}; + +#if !UCONFIG_ONLY_HTML_CONVERSION +/*************** to unicode *******************/ +static const int8_t nextStateToUnicodeCN[MAX_STATES_2022]= { +/* 0 1 2 3 4 5 6 7 8 9 */ + INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,SS2_STATE ,SS3_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE + ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE + ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE + ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE + ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,GB2312_1 ,INVALID_STATE ,ISO_IR_165 + ,CNS_11643_1 ,CNS_11643_2 ,CNS_11643_3 ,CNS_11643_4 ,CNS_11643_5 ,CNS_11643_6 ,CNS_11643_7 ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE + ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE + ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE ,INVALID_STATE +}; +#endif + + +static UCNV_TableStates_2022 +getKey_2022(char c,int32_t* key,int32_t* offset){ + int32_t togo; + int32_t low = 0; + int32_t hi = MAX_STATES_2022; + int32_t oldmid=0; + + togo = normalize_esq_chars_2022[(uint8_t)c]; + if(togo == 0) { + /* not a valid character anywhere in an escape sequence */ + *key = 0; + *offset = 0; + return INVALID_2022; + } + togo = (*key << 5) + togo; + + while (hi != low) /*binary search*/{ + + int32_t mid = (hi+low) >> 1; /*Finds median*/ + + if (mid == oldmid) + break; + + if (escSeqStateTable_Key_2022[mid] > togo){ + hi = mid; + } + else if (escSeqStateTable_Key_2022[mid] < togo){ + low = mid; + } + else /*we found it*/{ + *key = togo; + *offset = mid; + return (UCNV_TableStates_2022)escSeqStateTable_Value_2022[mid]; + } + oldmid = mid; + + } + + *key = 0; + *offset = 0; + return INVALID_2022; +} + +/*runs through a state machine to determine the escape sequence - codepage correspondence + */ +static void +changeState_2022(UConverter* _this, + const char** source, + const char* sourceLimit, + Variant2022 var, + UErrorCode* err){ + UCNV_TableStates_2022 value; + UConverterDataISO2022* myData2022 = ((UConverterDataISO2022*)_this->extraInfo); + uint32_t key = myData2022->key; + int32_t offset = 0; + int8_t initialToULength = _this->toULength; + char c; + + value = VALID_NON_TERMINAL_2022; + while (*source < sourceLimit) { + c = *(*source)++; + _this->toUBytes[_this->toULength++]=(uint8_t)c; + value = getKey_2022(c,(int32_t *) &key, &offset); + + switch (value){ + + case VALID_NON_TERMINAL_2022 : + /* continue with the loop */ + break; + + case VALID_TERMINAL_2022: + key = 0; + goto DONE; + + case INVALID_2022: + goto DONE; + + case VALID_MAYBE_TERMINAL_2022: +#ifdef U_ENABLE_GENERIC_ISO_2022 + /* ESC ( B is ambiguous only for ISO_2022 itself */ + if(var == ISO_2022) { + /* discard toUBytes[] for ESC ( B because this sequence is correct and complete */ + _this->toULength = 0; + + /* TODO need to indicate that ESC ( B was seen; if failure, then need to replay from source or from MBCS-style replay */ + + /* continue with the loop */ + value = VALID_NON_TERMINAL_2022; + break; + } else +#endif + { + /* not ISO_2022 itself, finish here */ + value = VALID_TERMINAL_2022; + key = 0; + goto DONE; + } + } + } + +DONE: + myData2022->key = key; + + if (value == VALID_NON_TERMINAL_2022) { + /* indicate that the escape sequence is incomplete: key!=0 */ + return; + } else if (value == INVALID_2022 ) { + *err = U_ILLEGAL_ESCAPE_SEQUENCE; + } else /* value == VALID_TERMINAL_2022 */ { + switch(var){ +#ifdef U_ENABLE_GENERIC_ISO_2022 + case ISO_2022: + { + const char *chosenConverterName = escSeqStateTable_Result_2022[offset]; + if(chosenConverterName == nullptr) { + /* SS2 or SS3 */ + *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; + _this->toUCallbackReason = UCNV_UNASSIGNED; + return; + } + + _this->mode = UCNV_SI; + ucnv_close(myData2022->currentConverter); + myData2022->currentConverter = myUConverter = ucnv_open(chosenConverterName, err); + if(U_SUCCESS(*err)) { + myUConverter->fromCharErrorBehaviour = UCNV_TO_U_CALLBACK_STOP; + _this->mode = UCNV_SO; + } + break; + } +#endif + case ISO_2022_JP: + { + StateEnum tempState=(StateEnum)nextStateToUnicodeJP[offset]; + switch(tempState) { + case INVALID_STATE: + *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; + break; + case SS2_STATE: + if(myData2022->toU2022State.cs[2]!=0) { + if(myData2022->toU2022State.g<2) { + myData2022->toU2022State.prevG=myData2022->toU2022State.g; + } + myData2022->toU2022State.g=2; + } else { + /* illegal to have SS2 before a matching designator */ + *err = U_ILLEGAL_ESCAPE_SEQUENCE; + } + break; + /* case SS3_STATE: not used in ISO-2022-JP-x */ + case ISO8859_1: + case ISO8859_7: + if((jpCharsetMasks[myData2022->version] & CSM(tempState)) == 0) { + *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; + } else { + /* G2 charset for SS2 */ + myData2022->toU2022State.cs[2]=(int8_t)tempState; + } + break; + default: + if((jpCharsetMasks[myData2022->version] & CSM(tempState)) == 0) { + *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; + } else { + /* G0 charset */ + myData2022->toU2022State.cs[0]=(int8_t)tempState; + } + break; + } + } + break; +#if !UCONFIG_ONLY_HTML_CONVERSION + case ISO_2022_CN: + { + StateEnum tempState=(StateEnum)nextStateToUnicodeCN[offset]; + switch(tempState) { + case INVALID_STATE: + *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; + break; + case SS2_STATE: + if(myData2022->toU2022State.cs[2]!=0) { + if(myData2022->toU2022State.g<2) { + myData2022->toU2022State.prevG=myData2022->toU2022State.g; + } + myData2022->toU2022State.g=2; + } else { + /* illegal to have SS2 before a matching designator */ + *err = U_ILLEGAL_ESCAPE_SEQUENCE; + } + break; + case SS3_STATE: + if(myData2022->toU2022State.cs[3]!=0) { + if(myData2022->toU2022State.g<2) { + myData2022->toU2022State.prevG=myData2022->toU2022State.g; + } + myData2022->toU2022State.g=3; + } else { + /* illegal to have SS3 before a matching designator */ + *err = U_ILLEGAL_ESCAPE_SEQUENCE; + } + break; + case ISO_IR_165: + if(myData2022->version==0) { + *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; + break; + } + U_FALLTHROUGH; + case GB2312_1: + U_FALLTHROUGH; + case CNS_11643_1: + myData2022->toU2022State.cs[1]=(int8_t)tempState; + break; + case CNS_11643_2: + myData2022->toU2022State.cs[2]=(int8_t)tempState; + break; + default: + /* other CNS 11643 planes */ + if(myData2022->version==0) { + *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; + } else { + myData2022->toU2022State.cs[3]=(int8_t)tempState; + } + break; + } + } + break; + case ISO_2022_KR: + if(offset==0x30){ + /* nothing to be done, just accept this one escape sequence */ + } else { + *err = U_UNSUPPORTED_ESCAPE_SEQUENCE; + } + break; +#endif // !UCONFIG_ONLY_HTML_CONVERSION + + default: + *err = U_ILLEGAL_ESCAPE_SEQUENCE; + break; + } + } + if(U_SUCCESS(*err)) { + _this->toULength = 0; + } else if(*err==U_ILLEGAL_ESCAPE_SEQUENCE) { + if(_this->toULength>1) { + /* + * Ticket 5691: consistent illegal sequences: + * - We include at least the first byte (ESC) in the illegal sequence. + * - If any of the non-initial bytes could be the start of a character, + * we stop the illegal sequence before the first one of those. + * In escape sequences, all following bytes are "printable", that is, + * unless they are completely illegal (>7f in SBCS, outside 21..7e in DBCS), + * they are valid single/lead bytes. + * For simplicity, we always only report the initial ESC byte as the + * illegal sequence and back out all other bytes we looked at. + */ + /* Back out some bytes. */ + int8_t backOutDistance=_this->toULength-1; + int8_t bytesFromThisBuffer=_this->toULength-initialToULength; + if(backOutDistance<=bytesFromThisBuffer) { + /* same as initialToULength<=1 */ + *source-=backOutDistance; + } else { + /* Back out bytes from the previous buffer: Need to replay them. */ + _this->preToULength=(int8_t)(bytesFromThisBuffer-backOutDistance); + /* same as -(initialToULength-1) */ + /* preToULength is negative! */ + uprv_memcpy(_this->preToU, _this->toUBytes+1, -_this->preToULength); + *source-=bytesFromThisBuffer; + } + _this->toULength=1; + } + } else if(*err==U_UNSUPPORTED_ESCAPE_SEQUENCE) { + _this->toUCallbackReason = UCNV_UNASSIGNED; + } +} + +#if !UCONFIG_ONLY_HTML_CONVERSION +/*Checks the characters of the buffer against valid 2022 escape sequences +*if the match we return a pointer to the initial start of the sequence otherwise +*we return sourceLimit +*/ +/*for 2022 looks ahead in the stream + *to determine the longest possible convertible + *data stream + */ +static inline const char* +getEndOfBuffer_2022(const char** source, + const char* sourceLimit, + UBool /*flush*/){ + + const char* mySource = *source; + +#ifdef U_ENABLE_GENERIC_ISO_2022 + if (*source >= sourceLimit) + return sourceLimit; + + do{ + + if (*mySource == ESC_2022){ + int8_t i; + int32_t key = 0; + int32_t offset; + UCNV_TableStates_2022 value = VALID_NON_TERMINAL_2022; + + /* Kludge: I could not + * figure out the reason for validating an escape sequence + * twice - once here and once in changeState_2022(). + * is it possible to have an ESC character in a ISO2022 + * byte stream which is valid in a code page? Is it legal? + */ + for (i=0; + (mySource+i < sourceLimit)&&(value == VALID_NON_TERMINAL_2022); + i++) { + value = getKey_2022(*(mySource+i), &key, &offset); + } + if (value > 0 || *mySource==ESC_2022) + return mySource; + + if ((value == VALID_NON_TERMINAL_2022)&&(!flush) ) + return sourceLimit; + } + }while (++mySource < sourceLimit); + + return sourceLimit; +#else + while(mySource < sourceLimit && *mySource != ESC_2022) { + ++mySource; + } + return mySource; +#endif +} +#endif + +/* This inline function replicates code in _MBCSFromUChar32() function in ucnvmbcs.c + * any future change in _MBCSFromUChar32() function should be reflected here. + * @return number of bytes in *value; negative number if fallback; 0 if no mapping + */ +static inline int32_t +MBCS_FROM_UCHAR32_ISO2022(UConverterSharedData* sharedData, + UChar32 c, + uint32_t* value, + UBool useFallback, + int outputType) +{ + const int32_t *cx; + const uint16_t *table; + uint32_t stage2Entry; + uint32_t myValue; + int32_t length; + const uint8_t *p; + /* + * TODO(markus): Use and require new, faster MBCS conversion table structures. + * Use internal version of ucnv_open() that verifies that the new structures are available, + * else U_INTERNAL_PROGRAM_ERROR. + */ + /* BMP-only codepages are stored without stage 1 entries for supplementary code points */ + if(c<0x10000 || (sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY)) { + table=sharedData->mbcs.fromUnicodeTable; + stage2Entry=MBCS_STAGE_2_FROM_U(table, c); + /* get the bytes and the length for the output */ + if(outputType==MBCS_OUTPUT_2){ + myValue=MBCS_VALUE_2_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); + if(myValue<=0xff) { + length=1; + } else { + length=2; + } + } else /* outputType==MBCS_OUTPUT_3 */ { + p=MBCS_POINTER_3_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); + myValue=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; + if(myValue<=0xff) { + length=1; + } else if(myValue<=0xffff) { + length=2; + } else { + length=3; + } + } + /* is this code point assigned, or do we use fallbacks? */ + if((stage2Entry&(1<<(16+(c&0xf))))!=0) { + /* assigned */ + *value=myValue; + return length; + } else if(FROM_U_USE_FALLBACK(useFallback, c) && myValue!=0) { + /* + * We allow a 0 byte output if the "assigned" bit is set for this entry. + * There is no way with this data structure for fallback output + * to be a zero byte. + */ + *value=myValue; + return -length; + } + } + + cx=sharedData->mbcs.extIndexes; + if(cx!=nullptr) { + return ucnv_extSimpleMatchFromU(cx, c, value, useFallback); + } + + /* unassigned */ + return 0; +} + +/* This inline function replicates code in _MBCSSingleFromUChar32() function in ucnvmbcs.c + * any future change in _MBCSSingleFromUChar32() function should be reflected here. + * @param retval pointer to output byte + * @return 1 roundtrip byte 0 no mapping -1 fallback byte + */ +static inline int32_t +MBCS_SINGLE_FROM_UCHAR32(UConverterSharedData* sharedData, + UChar32 c, + uint32_t* retval, + UBool useFallback) +{ + const uint16_t *table; + int32_t value; + /* BMP-only codepages are stored without stage 1 entries for supplementary code points */ + if(c>=0x10000 && !(sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY)) { + return 0; + } + /* convert the Unicode code point in c into codepage bytes (same as in _MBCSFromUnicodeWithOffsets) */ + table=sharedData->mbcs.fromUnicodeTable; + /* get the byte for the output */ + value=MBCS_SINGLE_RESULT_FROM_U(table, (uint16_t *)sharedData->mbcs.fromUnicodeBytes, c); + /* is this code point assigned, or do we use fallbacks? */ + *retval=(uint32_t)(value&0xff); + if(value>=0xf00) { + return 1; /* roundtrip */ + } else if(useFallback ? value>=0x800 : value>=0xc00) { + return -1; /* fallback taken */ + } else { + return 0; /* no mapping */ + } +} + +/* + * Check that the result is a 2-byte value with each byte in the range A1..FE + * (strict EUC DBCS) before accepting it and subtracting 0x80 from each byte + * to move it to the ISO 2022 range 21..7E. + * Return 0 if out of range. + */ +static inline uint32_t +_2022FromGR94DBCS(uint32_t value) { + if( (uint16_t)(value - 0xa1a1) <= (0xfefe - 0xa1a1) && + (uint8_t)(value - 0xa1) <= (0xfe - 0xa1) + ) { + return value - 0x8080; /* shift down to 21..7e byte range */ + } else { + return 0; /* not valid for ISO 2022 */ + } +} + +#if 0 /* 5691: Call sites now check for validity. They can just += 0x8080 after that. */ +/* + * This method does the reverse of _2022FromGR94DBCS(). Given the 2022 code point, it returns the + * 2 byte value that is in the range A1..FE for each byte. Otherwise it returns the 2022 code point + * unchanged. + */ +static inline uint32_t +_2022ToGR94DBCS(uint32_t value) { + uint32_t returnValue = value + 0x8080; + if( (uint16_t)(returnValue - 0xa1a1) <= (0xfefe - 0xa1a1) && + (uint8_t)(returnValue - 0xa1) <= (0xfe - 0xa1)) { + return returnValue; + } else { + return value; + } +} +#endif + +#ifdef U_ENABLE_GENERIC_ISO_2022 + +/********************************************************************************** +* ISO-2022 Converter +* +* +*/ + +static void U_CALLCONV +T_UConverter_toUnicode_ISO_2022_OFFSETS_LOGIC(UConverterToUnicodeArgs* args, + UErrorCode* err){ + const char* mySourceLimit, *realSourceLimit; + const char* sourceStart; + const char16_t* myTargetStart; + UConverter* saveThis; + UConverterDataISO2022* myData; + int8_t length; + + saveThis = args->converter; + myData=((UConverterDataISO2022*)(saveThis->extraInfo)); + + realSourceLimit = args->sourceLimit; + while (args->source < realSourceLimit) { + if(myData->key == 0) { /* are we in the middle of an escape sequence? */ + /*Find the end of the buffer e.g : Next Escape Seq | end of Buffer*/ + mySourceLimit = getEndOfBuffer_2022(&(args->source), realSourceLimit, args->flush); + + if(args->source < mySourceLimit) { + if(myData->currentConverter==nullptr) { + myData->currentConverter = ucnv_open("ASCII",err); + if(U_FAILURE(*err)){ + return; + } + + myData->currentConverter->fromCharErrorBehaviour = UCNV_TO_U_CALLBACK_STOP; + saveThis->mode = UCNV_SO; + } + + /* convert to before the ESC or until the end of the buffer */ + myData->isFirstBuffer=false; + sourceStart = args->source; + myTargetStart = args->target; + args->converter = myData->currentConverter; + ucnv_toUnicode(args->converter, + &args->target, + args->targetLimit, + &args->source, + mySourceLimit, + args->offsets, + (UBool)(args->flush && mySourceLimit == realSourceLimit), + err); + args->converter = saveThis; + + if (*err == U_BUFFER_OVERFLOW_ERROR) { + /* move the overflow buffer */ + length = saveThis->UCharErrorBufferLength = myData->currentConverter->UCharErrorBufferLength; + myData->currentConverter->UCharErrorBufferLength = 0; + if(length > 0) { + uprv_memcpy(saveThis->UCharErrorBuffer, + myData->currentConverter->UCharErrorBuffer, + length*U_SIZEOF_UCHAR); + } + return; + } + + /* + * At least one of: + * -Error while converting + * -Done with entire buffer + * -Need to write offsets or update the current offset + * (leave that up to the code in ucnv.c) + * + * or else we just stopped at an ESC byte and continue with changeState_2022() + */ + if (U_FAILURE(*err) || + (args->source == realSourceLimit) || + (args->offsets != nullptr && (args->target != myTargetStart || args->source != sourceStart) || + (mySourceLimit < realSourceLimit && myData->currentConverter->toULength > 0)) + ) { + /* copy partial or error input for truncated detection and error handling */ + if(U_FAILURE(*err)) { + length = saveThis->invalidCharLength = myData->currentConverter->invalidCharLength; + if(length > 0) { + uprv_memcpy(saveThis->invalidCharBuffer, myData->currentConverter->invalidCharBuffer, length); + } + } else { + length = saveThis->toULength = myData->currentConverter->toULength; + if(length > 0) { + uprv_memcpy(saveThis->toUBytes, myData->currentConverter->toUBytes, length); + if(args->source < mySourceLimit) { + *err = U_TRUNCATED_CHAR_FOUND; /* truncated input before ESC */ + } + } + } + return; + } + } + } + + sourceStart = args->source; + changeState_2022(args->converter, + &(args->source), + realSourceLimit, + ISO_2022, + err); + if (U_FAILURE(*err) || (args->source != sourceStart && args->offsets != nullptr)) { + /* let the ucnv.c code update its current offset */ + return; + } + } +} + +#endif + +/* + * To Unicode Callback helper function + */ +static void +toUnicodeCallback(UConverter *cnv, + const uint32_t sourceChar, const uint32_t targetUniChar, + UErrorCode* err){ + if(sourceChar>0xff){ + cnv->toUBytes[0] = (uint8_t)(sourceChar>>8); + cnv->toUBytes[1] = (uint8_t)sourceChar; + cnv->toULength = 2; + } + else{ + cnv->toUBytes[0] =(char) sourceChar; + cnv->toULength = 1; + } + + if(targetUniChar == (missingCharMarker-1/*0xfffe*/)){ + *err = U_INVALID_CHAR_FOUND; + } + else{ + *err = U_ILLEGAL_CHAR_FOUND; + } +} + +/**************************************ISO-2022-JP*************************************************/ + +/************************************** IMPORTANT ************************************************** +* The UConverter_fromUnicode_ISO2022_JP converter does not use ucnv_fromUnicode() functions for SBCS,DBCS and +* MBCS; instead, the values are obtained directly by calling _MBCSFromUChar32(). +* The converter iterates over each Unicode codepoint +* to obtain the equivalent codepoints from the codepages supported. Since the source buffer is +* processed one char at a time it would make sense to reduce the extra processing a canned converter +* would do as far as possible. +* +* If the implementation of these macros or structure of sharedData struct change in the future, make +* sure that ISO-2022 is also changed. +*************************************************************************************************** +*/ + +/*************************************************************************************************** +* Rules for ISO-2022-jp encoding +* (i) Escape sequences must be fully contained within a line they should not +* span new lines or CRs +* (ii) If the last character on a line is represented by two bytes then an ASCII or +* JIS-Roman character escape sequence should follow before the line terminates +* (iii) If the first character on the line is represented by two bytes then a two +* byte character escape sequence should precede it +* (iv) If no escape sequence is encountered then the characters are ASCII +* (v) Latin(ISO-8859-1) and Greek(ISO-8859-7) characters must be designated to G2, +* and invoked with SS2 (ESC N). +* (vi) If there is any G0 designation in text, there must be a switch to +* ASCII or to JIS X 0201-Roman before a space character (but not +* necessarily before "ESC 4/14 2/0" or "ESC N ' '") or control +* characters such as tab or CRLF. +* (vi) Supported encodings: +* ASCII, JISX201, JISX208, JISX212, GB2312, KSC5601, ISO-8859-1,ISO-8859-7 +* +* source : RFC-1554 +* +* JISX201, JISX208,JISX212 : new .cnv data files created +* KSC5601 : alias to ibm-949 mapping table +* GB2312 : alias to ibm-1386 mapping table +* ISO-8859-1 : Algorithmic implemented as LATIN1 case +* ISO-8859-7 : alias to ibm-9409 mapping table +*/ + +/* preference order of JP charsets */ +static const StateEnum jpCharsetPref[]={ + ASCII, + JISX201, + ISO8859_1, + JISX208, + ISO8859_7, + JISX212, + GB2312, + KSC5601, + HWKANA_7BIT +}; + +/* + * The escape sequences must be in order of the enum constants like JISX201 = 3, + * not in order of jpCharsetPref[]! + */ +static const char escSeqChars[][6] ={ + "\x1B\x28\x42", /* (B ASCII */ + "\x1B\x2E\x41", /* .A ISO-8859-1 */ + "\x1B\x2E\x46", /* .F ISO-8859-7 */ + "\x1B\x28\x4A", /* (J JISX-201 */ + "\x1B\x24\x42", /* $B JISX-208 */ + "\x1B\x24\x28\x44", /* $(D JISX-212 */ + "\x1B\x24\x41", /* $A GB2312 */ + "\x1B\x24\x28\x43", /* $(C KSC5601 */ + "\x1B\x28\x49" /* (I HWKANA_7BIT */ + +}; +static const int8_t escSeqCharsLen[] ={ + 3, /* length of (B ASCII */ + 3, /* length of .A ISO-8859-1 */ + 3, /* length of .F ISO-8859-7 */ + 3, /* length of (J JISX-201 */ + 3, /* length of $B JISX-208 */ + 4, /* length of $(D JISX-212 */ + 3, /* length of $A GB2312 */ + 4, /* length of $(C KSC5601 */ + 3 /* length of (I HWKANA_7BIT */ +}; + +/* +* The iteration over various code pages works this way: +* i) Get the currentState from myConverterData->currentState +* ii) Check if the character is mapped to a valid character in the currentState +* Yes -> a) set the initIterState to currentState +* b) remain in this state until an invalid character is found +* No -> a) go to the next code page and find the character +* iii) Before changing the state increment the current state check if the current state +* is equal to the intitIteration state +* Yes -> A character that cannot be represented in any of the supported encodings +* break and return a U_INVALID_CHARACTER error +* No -> Continue and find the character in next code page +* +* +* TODO: Implement a priority technique where the users are allowed to set the priority of code pages +*/ + +/* Map 00..7F to Unicode according to JIS X 0201. */ +static inline uint32_t +jisx201ToU(uint32_t value) { + if(value < 0x5c) { + return value; + } else if(value == 0x5c) { + return 0xa5; + } else if(value == 0x7e) { + return 0x203e; + } else /* value <= 0x7f */ { + return value; + } +} + +/* Map Unicode to 00..7F according to JIS X 0201. Return U+FFFE if unmappable. */ +static inline uint32_t +jisx201FromU(uint32_t value) { + if(value<=0x7f) { + if(value!=0x5c && value!=0x7e) { + return value; + } + } else if(value==0xa5) { + return 0x5c; + } else if(value==0x203e) { + return 0x7e; + } + return 0xfffe; +} + +/* + * Take a valid Shift-JIS byte pair, check that it is in the range corresponding + * to JIS X 0208, and convert it to a pair of 21..7E bytes. + * Return 0 if the byte pair is out of range. + */ +static inline uint32_t +_2022FromSJIS(uint32_t value) { + uint8_t trail; + + if(value > 0xEFFC) { + return 0; /* beyond JIS X 0208 */ + } + + trail = (uint8_t)value; + + value &= 0xff00; /* lead byte */ + if(value <= 0x9f00) { + value -= 0x7000; + } else /* 0xe000 <= value <= 0xef00 */ { + value -= 0xb000; + } + value <<= 1; + + if(trail <= 0x9e) { + value -= 0x100; + if(trail <= 0x7e) { + value |= trail - 0x1f; + } else { + value |= trail - 0x20; + } + } else /* trail <= 0xfc */ { + value |= trail - 0x7e; + } + return value; +} + +/* + * Convert a pair of JIS X 0208 21..7E bytes to Shift-JIS. + * If either byte is outside 21..7E make sure that the result is not valid + * for Shift-JIS so that the converter catches it. + * Some invalid byte values already turn into equally invalid Shift-JIS + * byte values and need not be tested explicitly. + */ +static inline void +_2022ToSJIS(uint8_t c1, uint8_t c2, char bytes[2]) { + if(c1&1) { + ++c1; + if(c2 <= 0x5f) { + c2 += 0x1f; + } else if(c2 <= 0x7e) { + c2 += 0x20; + } else { + c2 = 0; /* invalid */ + } + } else { + if((uint8_t)(c2-0x21) <= ((0x7e)-0x21)) { + c2 += 0x7e; + } else { + c2 = 0; /* invalid */ + } + } + c1 >>= 1; + if(c1 <= 0x2f) { + c1 += 0x70; + } else if(c1 <= 0x3f) { + c1 += 0xb0; + } else { + c1 = 0; /* invalid */ + } + bytes[0] = (char)c1; + bytes[1] = (char)c2; +} + +/* + * JIS X 0208 has fallbacks from Unicode half-width Katakana to full-width (DBCS) + * Katakana. + * Now that we use a Shift-JIS table for JIS X 0208 we need to hardcode these fallbacks + * because Shift-JIS roundtrips half-width Katakana to single bytes. + * These were the only fallbacks in ICU's jisx-208.ucm file. + */ +static const uint16_t hwkana_fb[HWKANA_END - HWKANA_START + 1] = { + 0x2123, /* U+FF61 */ + 0x2156, + 0x2157, + 0x2122, + 0x2126, + 0x2572, + 0x2521, + 0x2523, + 0x2525, + 0x2527, + 0x2529, + 0x2563, + 0x2565, + 0x2567, + 0x2543, + 0x213C, /* U+FF70 */ + 0x2522, + 0x2524, + 0x2526, + 0x2528, + 0x252A, + 0x252B, + 0x252D, + 0x252F, + 0x2531, + 0x2533, + 0x2535, + 0x2537, + 0x2539, + 0x253B, + 0x253D, + 0x253F, /* U+FF80 */ + 0x2541, + 0x2544, + 0x2546, + 0x2548, + 0x254A, + 0x254B, + 0x254C, + 0x254D, + 0x254E, + 0x254F, + 0x2552, + 0x2555, + 0x2558, + 0x255B, + 0x255E, + 0x255F, /* U+FF90 */ + 0x2560, + 0x2561, + 0x2562, + 0x2564, + 0x2566, + 0x2568, + 0x2569, + 0x256A, + 0x256B, + 0x256C, + 0x256D, + 0x256F, + 0x2573, + 0x212B, + 0x212C /* U+FF9F */ +}; + +static void U_CALLCONV +UConverter_fromUnicode_ISO_2022_JP_OFFSETS_LOGIC(UConverterFromUnicodeArgs* args, UErrorCode* err) { + UConverter *cnv = args->converter; + UConverterDataISO2022 *converterData; + ISO2022State *pFromU2022State; + uint8_t *target = (uint8_t *) args->target; + const uint8_t *targetLimit = (const uint8_t *) args->targetLimit; + const char16_t* source = args->source; + const char16_t* sourceLimit = args->sourceLimit; + int32_t* offsets = args->offsets; + UChar32 sourceChar; + char buffer[8]; + int32_t len, outLen; + int8_t choices[10]; + int32_t choiceCount; + uint32_t targetValue = 0; + UBool useFallback; + + int32_t i; + int8_t cs, g; + + /* set up the state */ + converterData = (UConverterDataISO2022*)cnv->extraInfo; + pFromU2022State = &converterData->fromU2022State; + + choiceCount = 0; + + /* check if the last codepoint of previous buffer was a lead surrogate*/ + if((sourceChar = cnv->fromUChar32)!=0 && target< targetLimit) { + goto getTrail; + } + + while(source < sourceLimit) { + if(target < targetLimit) { + + sourceChar = *(source++); + /*check if the char is a First surrogate*/ + if(U16_IS_SURROGATE(sourceChar)) { + if(U16_IS_SURROGATE_LEAD(sourceChar)) { +getTrail: + /*look ahead to find the trail surrogate*/ + if(source < sourceLimit) { + /* test the following code unit */ + char16_t trail=(char16_t) *source; + if(U16_IS_TRAIL(trail)) { + source++; + sourceChar=U16_GET_SUPPLEMENTARY(sourceChar, trail); + cnv->fromUChar32=0x00; + /* convert this supplementary code point */ + /* exit this condition tree */ + } else { + /* this is an unmatched lead code unit (1st surrogate) */ + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + cnv->fromUChar32=sourceChar; + break; + } + } else { + /* no more input */ + cnv->fromUChar32=sourceChar; + break; + } + } else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + cnv->fromUChar32=sourceChar; + break; + } + } + + /* do not convert SO/SI/ESC */ + if(IS_2022_CONTROL(sourceChar)) { + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + cnv->fromUChar32=sourceChar; + break; + } + + /* do the conversion */ + + if(choiceCount == 0) { + uint16_t csm; + + /* + * The csm variable keeps track of which charsets are allowed + * and not used yet while building the choices[]. + */ + csm = jpCharsetMasks[converterData->version]; + choiceCount = 0; + + /* JIS7/8: try single-byte half-width Katakana before JISX208 */ + if(converterData->version == 3 || converterData->version == 4) { + choices[choiceCount++] = (int8_t)HWKANA_7BIT; + } + /* Do not try single-byte half-width Katakana for other versions. */ + csm &= ~CSM(HWKANA_7BIT); + + /* try the current G0 charset */ + choices[choiceCount++] = cs = pFromU2022State->cs[0]; + csm &= ~CSM(cs); + + /* try the current G2 charset */ + if((cs = pFromU2022State->cs[2]) != 0) { + choices[choiceCount++] = cs; + csm &= ~CSM(cs); + } + + /* try all the other possible charsets */ + for(i = 0; i < UPRV_LENGTHOF(jpCharsetPref); ++i) { + cs = (int8_t)jpCharsetPref[i]; + if(CSM(cs) & csm) { + choices[choiceCount++] = cs; + csm &= ~CSM(cs); + } + } + } + + cs = g = 0; + /* + * len==0: no mapping found yet + * len<0: found a fallback result: continue looking for a roundtrip but no further fallbacks + * len>0: found a roundtrip result, done + */ + len = 0; + /* + * We will turn off useFallback after finding a fallback, + * but we still get fallbacks from PUA code points as usual. + * Therefore, we will also need to check that we don't overwrite + * an early fallback with a later one. + */ + useFallback = cnv->useFallback; + + for(i = 0; i < choiceCount && len <= 0; ++i) { + uint32_t value; + int32_t len2; + int8_t cs0 = choices[i]; + switch(cs0) { + case ASCII: + if(sourceChar <= 0x7f) { + targetValue = (uint32_t)sourceChar; + len = 1; + cs = cs0; + g = 0; + } + break; + case ISO8859_1: + if(GR96_START <= sourceChar && sourceChar <= GR96_END) { + targetValue = (uint32_t)sourceChar - 0x80; + len = 1; + cs = cs0; + g = 2; + } + break; + case HWKANA_7BIT: + if((uint32_t)(sourceChar - HWKANA_START) <= (HWKANA_END - HWKANA_START)) { + if(converterData->version==3) { + /* JIS7: use G1 (SO) */ + /* Shift U+FF61..U+FF9F to bytes 21..5F. */ + targetValue = (uint32_t)(sourceChar - (HWKANA_START - 0x21)); + len = 1; + pFromU2022State->cs[1] = cs = cs0; /* do not output an escape sequence */ + g = 1; + } else if(converterData->version==4) { + /* JIS8: use 8-bit bytes with any single-byte charset, see escape sequence output below */ + /* Shift U+FF61..U+FF9F to bytes A1..DF. */ + targetValue = (uint32_t)(sourceChar - (HWKANA_START - 0xa1)); + len = 1; + + cs = pFromU2022State->cs[0]; + if(IS_JP_DBCS(cs)) { + /* switch from a DBCS charset to JISX201 */ + cs = (int8_t)JISX201; + } + /* else stay in the current G0 charset */ + g = 0; + } + /* else do not use HWKANA_7BIT with other versions */ + } + break; + case JISX201: + /* G0 SBCS */ + value = jisx201FromU(sourceChar); + if(value <= 0x7f) { + targetValue = value; + len = 1; + cs = cs0; + g = 0; + useFallback = false; + } + break; + case JISX208: + /* G0 DBCS from Shift-JIS table */ + len2 = MBCS_FROM_UCHAR32_ISO2022( + converterData->myConverterArray[cs0], + sourceChar, &value, + useFallback, MBCS_OUTPUT_2); + if(len2 == 2 || (len2 == -2 && len == 0)) { /* only accept DBCS: abs(len)==2 */ + value = _2022FromSJIS(value); + if(value != 0) { + targetValue = value; + len = len2; + cs = cs0; + g = 0; + useFallback = false; + } + } else if(len == 0 && useFallback && + (uint32_t)(sourceChar - HWKANA_START) <= (HWKANA_END - HWKANA_START)) { + targetValue = hwkana_fb[sourceChar - HWKANA_START]; + len = -2; + cs = cs0; + g = 0; + useFallback = false; + } + break; + case ISO8859_7: + /* G0 SBCS forced to 7-bit output */ + len2 = MBCS_SINGLE_FROM_UCHAR32( + converterData->myConverterArray[cs0], + sourceChar, &value, + useFallback); + if(len2 != 0 && !(len2 < 0 && len != 0) && GR96_START <= value && value <= GR96_END) { + targetValue = value - 0x80; + len = len2; + cs = cs0; + g = 2; + useFallback = false; + } + break; + default: + /* G0 DBCS */ + len2 = MBCS_FROM_UCHAR32_ISO2022( + converterData->myConverterArray[cs0], + sourceChar, &value, + useFallback, MBCS_OUTPUT_2); + if(len2 == 2 || (len2 == -2 && len == 0)) { /* only accept DBCS: abs(len)==2 */ + if(cs0 == KSC5601) { + /* + * Check for valid bytes for the encoding scheme. + * This is necessary because the sub-converter (windows-949) + * has a broader encoding scheme than is valid for 2022. + */ + value = _2022FromGR94DBCS(value); + if(value == 0) { + break; + } + } + targetValue = value; + len = len2; + cs = cs0; + g = 0; + useFallback = false; + } + break; + } + } + + if(len != 0) { + if(len < 0) { + len = -len; /* fallback */ + } + outLen = 0; /* count output bytes */ + + /* write SI if necessary (only for JIS7) */ + if(pFromU2022State->g == 1 && g == 0) { + buffer[outLen++] = UCNV_SI; + pFromU2022State->g = 0; + } + + /* write the designation sequence if necessary */ + if(cs != pFromU2022State->cs[g]) { + int32_t escLen = escSeqCharsLen[cs]; + uprv_memcpy(buffer + outLen, escSeqChars[cs], escLen); + outLen += escLen; + pFromU2022State->cs[g] = cs; + + /* invalidate the choices[] */ + choiceCount = 0; + } + + /* write the shift sequence if necessary */ + if(g != pFromU2022State->g) { + switch(g) { + /* case 0 handled before writing escapes */ + case 1: + buffer[outLen++] = UCNV_SO; + pFromU2022State->g = 1; + break; + default: /* case 2 */ + buffer[outLen++] = 0x1b; + buffer[outLen++] = 0x4e; + break; + /* no case 3: no SS3 in ISO-2022-JP-x */ + } + } + + /* write the output bytes */ + if(len == 1) { + buffer[outLen++] = (char)targetValue; + } else /* len == 2 */ { + buffer[outLen++] = (char)(targetValue >> 8); + buffer[outLen++] = (char)targetValue; + } + } else { + /* + * if we cannot find the character after checking all codepages + * then this is an error + */ + *err = U_INVALID_CHAR_FOUND; + cnv->fromUChar32=sourceChar; + break; + } + + if(sourceChar == CR || sourceChar == LF) { + /* reset the G2 state at the end of a line (conversion got us into ASCII or JISX201 already) */ + pFromU2022State->cs[2] = 0; + choiceCount = 0; + } + + /* output outLen>0 bytes in buffer[] */ + if(outLen == 1) { + *target++ = buffer[0]; + if(offsets) { + *offsets++ = (int32_t)(source - args->source - 1); /* -1: known to be ASCII */ + } + } else if(outLen == 2 && (target + 2) <= targetLimit) { + *target++ = buffer[0]; + *target++ = buffer[1]; + if(offsets) { + int32_t sourceIndex = (int32_t)(source - args->source - U16_LENGTH(sourceChar)); + *offsets++ = sourceIndex; + *offsets++ = sourceIndex; + } + } else { + fromUWriteUInt8( + cnv, + buffer, outLen, + &target, (const char *)targetLimit, + &offsets, (int32_t)(source - args->source - U16_LENGTH(sourceChar)), + err); + if(U_FAILURE(*err)) { + break; + } + } + } /* end if(myTargetIndexg!=0 || pFromU2022State->cs[0]!=ASCII) && + args->flush && source>=sourceLimit && cnv->fromUChar32==0 + ) { + int32_t sourceIndex; + + outLen = 0; + + if(pFromU2022State->g != 0) { + buffer[outLen++] = UCNV_SI; + pFromU2022State->g = 0; + } + + if(pFromU2022State->cs[0] != ASCII) { + int32_t escLen = escSeqCharsLen[ASCII]; + uprv_memcpy(buffer + outLen, escSeqChars[ASCII], escLen); + outLen += escLen; + pFromU2022State->cs[0] = (int8_t)ASCII; + } + + /* get the source index of the last input character */ + /* + * TODO this would be simpler and more reliable if we used a pair + * of sourceIndex/prevSourceIndex like in ucnvmbcs.c + * so that we could simply use the prevSourceIndex here; + * this code gives an incorrect result for the rare case of an unmatched + * trail surrogate that is alone in the last buffer of the text stream + */ + sourceIndex=(int32_t)(source-args->source); + if(sourceIndex>0) { + --sourceIndex; + if( U16_IS_TRAIL(args->source[sourceIndex]) && + (sourceIndex==0 || U16_IS_LEAD(args->source[sourceIndex-1])) + ) { + --sourceIndex; + } + } else { + sourceIndex=-1; + } + + fromUWriteUInt8( + cnv, + buffer, outLen, + &target, (const char *)targetLimit, + &offsets, sourceIndex, + err); + } + + /*save the state and return */ + args->source = source; + args->target = (char*)target; +} + +/*************** to unicode *******************/ + +static void U_CALLCONV +UConverter_toUnicode_ISO_2022_JP_OFFSETS_LOGIC(UConverterToUnicodeArgs *args, + UErrorCode* err){ + char tempBuf[2]; + const char *mySource = (char *) args->source; + char16_t *myTarget = args->target; + const char *mySourceLimit = args->sourceLimit; + uint32_t targetUniChar = 0x0000; + uint32_t mySourceChar = 0x0000; + uint32_t tmpSourceChar = 0x0000; + UConverterDataISO2022* myData; + ISO2022State *pToU2022State; + StateEnum cs; + + myData=(UConverterDataISO2022*)(args->converter->extraInfo); + pToU2022State = &myData->toU2022State; + + if(myData->key != 0) { + /* continue with a partial escape sequence */ + goto escape; + } else if(args->converter->toULength == 1 && mySource < mySourceLimit && myTarget < args->targetLimit) { + /* continue with a partial double-byte character */ + mySourceChar = args->converter->toUBytes[0]; + args->converter->toULength = 0; + cs = (StateEnum)pToU2022State->cs[pToU2022State->g]; + targetUniChar = missingCharMarker; + goto getTrailByte; + } + + while(mySource < mySourceLimit){ + + targetUniChar =missingCharMarker; + + if(myTarget < args->targetLimit){ + + mySourceChar= (unsigned char) *mySource++; + + switch(mySourceChar) { + case UCNV_SI: + if(myData->version==3) { + pToU2022State->g=0; + continue; + } else { + /* only JIS7 uses SI/SO, not ISO-2022-JP-x */ + myData->isEmptySegment = false; /* reset this, we have a different error */ + break; + } + + case UCNV_SO: + if(myData->version==3) { + /* JIS7: switch to G1 half-width Katakana */ + pToU2022State->cs[1] = (int8_t)HWKANA_7BIT; + pToU2022State->g=1; + continue; + } else { + /* only JIS7 uses SI/SO, not ISO-2022-JP-x */ + myData->isEmptySegment = false; /* reset this, we have a different error */ + break; + } + + case ESC_2022: + mySource--; +escape: + { + const char * mySourceBefore = mySource; + int8_t toULengthBefore = args->converter->toULength; + + changeState_2022(args->converter,&(mySource), + mySourceLimit, ISO_2022_JP,err); + + /* If in ISO-2022-JP only and we successfully completed an escape sequence, but previous segment was empty, create an error */ + if(myData->version==0 && myData->key==0 && U_SUCCESS(*err) && myData->isEmptySegment) { + *err = U_ILLEGAL_ESCAPE_SEQUENCE; + args->converter->toUCallbackReason = UCNV_IRREGULAR; + args->converter->toULength = (int8_t)(toULengthBefore + (mySource - mySourceBefore)); + } + } + + /* invalid or illegal escape sequence */ + if(U_FAILURE(*err)){ + args->target = myTarget; + args->source = mySource; + myData->isEmptySegment = false; /* Reset to avoid future spurious errors */ + return; + } + /* If we successfully completed an escape sequence, we begin a new segment, empty so far */ + if(myData->key==0) { + myData->isEmptySegment = true; + } + continue; + + /* ISO-2022-JP does not use single-byte (C1) SS2 and SS3 */ + + case CR: + case LF: + /* automatically reset to single-byte mode */ + if((StateEnum)pToU2022State->cs[0] != ASCII && (StateEnum)pToU2022State->cs[0] != JISX201) { + pToU2022State->cs[0] = (int8_t)ASCII; + } + pToU2022State->cs[2] = 0; + pToU2022State->g = 0; + U_FALLTHROUGH; + default: + /* convert one or two bytes */ + myData->isEmptySegment = false; + cs = (StateEnum)pToU2022State->cs[pToU2022State->g]; + if( (uint8_t)(mySourceChar - 0xa1) <= (0xdf - 0xa1) && myData->version==4 && + !IS_JP_DBCS(cs) + ) { + /* 8-bit halfwidth katakana in any single-byte mode for JIS8 */ + targetUniChar = mySourceChar + (HWKANA_START - 0xa1); + + /* return from a single-shift state to the previous one */ + if(pToU2022State->g >= 2) { + pToU2022State->g=pToU2022State->prevG; + } + } else switch(cs) { + case ASCII: + if(mySourceChar <= 0x7f) { + targetUniChar = mySourceChar; + } + break; + case ISO8859_1: + if(mySourceChar <= 0x7f) { + targetUniChar = mySourceChar + 0x80; + } + /* return from a single-shift state to the previous one */ + pToU2022State->g=pToU2022State->prevG; + break; + case ISO8859_7: + if(mySourceChar <= 0x7f) { + /* convert mySourceChar+0x80 to use a normal 8-bit table */ + targetUniChar = + _MBCS_SINGLE_SIMPLE_GET_NEXT_BMP( + myData->myConverterArray[cs], + mySourceChar + 0x80); + } + /* return from a single-shift state to the previous one */ + pToU2022State->g=pToU2022State->prevG; + break; + case JISX201: + if(mySourceChar <= 0x7f) { + targetUniChar = jisx201ToU(mySourceChar); + } + break; + case HWKANA_7BIT: + if((uint8_t)(mySourceChar - 0x21) <= (0x5f - 0x21)) { + /* 7-bit halfwidth Katakana */ + targetUniChar = mySourceChar + (HWKANA_START - 0x21); + } + break; + default: + /* G0 DBCS */ + if(mySource < mySourceLimit) { + int leadIsOk, trailIsOk; + uint8_t trailByte; +getTrailByte: + trailByte = (uint8_t)*mySource; + /* + * Ticket 5691: consistent illegal sequences: + * - We include at least the first byte in the illegal sequence. + * - If any of the non-initial bytes could be the start of a character, + * we stop the illegal sequence before the first one of those. + * + * In ISO-2022 DBCS, if the second byte is in the 21..7e range or is + * an ESC/SO/SI, we report only the first byte as the illegal sequence. + * Otherwise we convert or report the pair of bytes. + */ + leadIsOk = (uint8_t)(mySourceChar - 0x21) <= (0x7e - 0x21); + trailIsOk = (uint8_t)(trailByte - 0x21) <= (0x7e - 0x21); + if (leadIsOk && trailIsOk) { + ++mySource; + tmpSourceChar = (mySourceChar << 8) | trailByte; + if(cs == JISX208) { + _2022ToSJIS((uint8_t)mySourceChar, trailByte, tempBuf); + mySourceChar = tmpSourceChar; + } else { + /* Copy before we modify tmpSourceChar so toUnicodeCallback() sees the correct bytes. */ + mySourceChar = tmpSourceChar; + if (cs == KSC5601) { + tmpSourceChar += 0x8080; /* = _2022ToGR94DBCS(tmpSourceChar) */ + } + tempBuf[0] = (char)(tmpSourceChar >> 8); + tempBuf[1] = (char)(tmpSourceChar); + } + targetUniChar = ucnv_MBCSSimpleGetNextUChar(myData->myConverterArray[cs], tempBuf, 2, false); + } else if (!(trailIsOk || IS_2022_CONTROL(trailByte))) { + /* report a pair of illegal bytes if the second byte is not a DBCS starter */ + ++mySource; + /* add another bit so that the code below writes 2 bytes in case of error */ + mySourceChar = 0x10000 | (mySourceChar << 8) | trailByte; + } + } else { + args->converter->toUBytes[0] = (uint8_t)mySourceChar; + args->converter->toULength = 1; + goto endloop; + } + } /* End of inner switch */ + break; + } /* End of outer switch */ + if(targetUniChar < (missingCharMarker-1/*0xfffe*/)){ + if(args->offsets){ + args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); + } + *(myTarget++)=(char16_t)targetUniChar; + } + else if(targetUniChar > missingCharMarker){ + /* disassemble the surrogate pair and write to output*/ + targetUniChar-=0x0010000; + *myTarget = (char16_t)(0xd800+(char16_t)(targetUniChar>>10)); + if(args->offsets){ + args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); + } + ++myTarget; + if(myTarget< args->targetLimit){ + *myTarget = (char16_t)(0xdc00+(char16_t)(targetUniChar&0x3ff)); + if(args->offsets){ + args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); + } + ++myTarget; + }else{ + args->converter->UCharErrorBuffer[args->converter->UCharErrorBufferLength++]= + (char16_t)(0xdc00+(char16_t)(targetUniChar&0x3ff)); + } + + } + else{ + /* Call the callback function*/ + toUnicodeCallback(args->converter,mySourceChar,targetUniChar,err); + break; + } + } + else{ /* goes with "if(myTarget < args->targetLimit)" way up near top of function */ + *err =U_BUFFER_OVERFLOW_ERROR; + break; + } + } +endloop: + args->target = myTarget; + args->source = mySource; +} + + +#if !UCONFIG_ONLY_HTML_CONVERSION +/*************************************************************** +* Rules for ISO-2022-KR encoding +* i) The KSC5601 designator sequence should appear only once in a file, +* at the beginning of a line before any KSC5601 characters. This usually +* means that it appears by itself on the first line of the file +* ii) There are only 2 shifting sequences SO to shift into double byte mode +* and SI to shift into single byte mode +*/ +static void U_CALLCONV +UConverter_fromUnicode_ISO_2022_KR_OFFSETS_LOGIC_IBM(UConverterFromUnicodeArgs* args, UErrorCode* err){ + + UConverter* saveConv = args->converter; + UConverterDataISO2022 *myConverterData=(UConverterDataISO2022*)saveConv->extraInfo; + args->converter=myConverterData->currentConverter; + + myConverterData->currentConverter->fromUChar32 = saveConv->fromUChar32; + ucnv_MBCSFromUnicodeWithOffsets(args,err); + saveConv->fromUChar32 = myConverterData->currentConverter->fromUChar32; + + if(*err == U_BUFFER_OVERFLOW_ERROR) { + if(myConverterData->currentConverter->charErrorBufferLength > 0) { + uprv_memcpy( + saveConv->charErrorBuffer, + myConverterData->currentConverter->charErrorBuffer, + myConverterData->currentConverter->charErrorBufferLength); + } + saveConv->charErrorBufferLength = myConverterData->currentConverter->charErrorBufferLength; + myConverterData->currentConverter->charErrorBufferLength = 0; + } + args->converter=saveConv; +} + +static void U_CALLCONV +UConverter_fromUnicode_ISO_2022_KR_OFFSETS_LOGIC(UConverterFromUnicodeArgs* args, UErrorCode* err){ + + const char16_t *source = args->source; + const char16_t *sourceLimit = args->sourceLimit; + unsigned char *target = (unsigned char *) args->target; + unsigned char *targetLimit = (unsigned char *) args->targetLimit; + int32_t* offsets = args->offsets; + uint32_t targetByteUnit = 0x0000; + UChar32 sourceChar = 0x0000; + UBool isTargetByteDBCS; + UBool oldIsTargetByteDBCS; + UConverterDataISO2022 *converterData; + UConverterSharedData* sharedData; + UBool useFallback; + int32_t length =0; + + converterData=(UConverterDataISO2022*)args->converter->extraInfo; + /* if the version is 1 then the user is requesting + * conversion with ibm-25546 pass the arguments to + * MBCS converter and return + */ + if(converterData->version==1){ + UConverter_fromUnicode_ISO_2022_KR_OFFSETS_LOGIC_IBM(args,err); + return; + } + + /* initialize data */ + sharedData = converterData->currentConverter->sharedData; + useFallback = args->converter->useFallback; + isTargetByteDBCS=(UBool)args->converter->fromUnicodeStatus; + oldIsTargetByteDBCS = isTargetByteDBCS; + + isTargetByteDBCS = (UBool) args->converter->fromUnicodeStatus; + if((sourceChar = args->converter->fromUChar32)!=0 && target targetLimit){ + sourceChar = *source++; + + /* do not convert SO/SI/ESC */ + if(IS_2022_CONTROL(sourceChar)) { + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + args->converter->fromUChar32=sourceChar; + break; + } + + length = MBCS_FROM_UCHAR32_ISO2022(sharedData,sourceChar,&targetByteUnit,useFallback,MBCS_OUTPUT_2); + if(length < 0) { + length = -length; /* fallback */ + } + /* only DBCS or SBCS characters are expected*/ + /* DB characters with high bit set to 1 are expected */ + if( length > 2 || length==0 || + (length == 1 && targetByteUnit > 0x7f) || + (length == 2 && + ((uint16_t)(targetByteUnit - 0xa1a1) > (0xfefe - 0xa1a1) || + (uint8_t)(targetByteUnit - 0xa1) > (0xfe - 0xa1))) + ) { + targetByteUnit=missingCharMarker; + } + if (targetByteUnit != missingCharMarker){ + + oldIsTargetByteDBCS = isTargetByteDBCS; + isTargetByteDBCS = (UBool)(targetByteUnit>0x00FF); + /* append the shift sequence */ + if (oldIsTargetByteDBCS != isTargetByteDBCS ){ + + if (isTargetByteDBCS) + *target++ = UCNV_SO; + else + *target++ = UCNV_SI; + if(offsets) + *(offsets++) = (int32_t)(source - args->source-1); + } + /* write the targetUniChar to target */ + if(targetByteUnit <= 0x00FF){ + if( target < targetLimit){ + *(target++) = (unsigned char) targetByteUnit; + if(offsets){ + *(offsets++) = (int32_t)(source - args->source-1); + } + + }else{ + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (unsigned char) (targetByteUnit); + *err = U_BUFFER_OVERFLOW_ERROR; + } + }else{ + if(target < targetLimit){ + *(target++) =(unsigned char) ((targetByteUnit>>8) -0x80); + if(offsets){ + *(offsets++) = (int32_t)(source - args->source-1); + } + if(target < targetLimit){ + *(target++) =(unsigned char) (targetByteUnit -0x80); + if(offsets){ + *(offsets++) = (int32_t)(source - args->source-1); + } + }else{ + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (unsigned char) (targetByteUnit -0x80); + *err = U_BUFFER_OVERFLOW_ERROR; + } + }else{ + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (unsigned char) ((targetByteUnit>>8) -0x80); + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (unsigned char) (targetByteUnit-0x80); + *err = U_BUFFER_OVERFLOW_ERROR; + } + } + + } + else{ + /* oops.. the code point is unassingned + * set the error and reason + */ + + /*check if the char is a First surrogate*/ + if(U16_IS_SURROGATE(sourceChar)) { + if(U16_IS_SURROGATE_LEAD(sourceChar)) { +getTrail: + /*look ahead to find the trail surrogate*/ + if(source < sourceLimit) { + /* test the following code unit */ + char16_t trail=(char16_t) *source; + if(U16_IS_TRAIL(trail)) { + source++; + sourceChar=U16_GET_SUPPLEMENTARY(sourceChar, trail); + *err = U_INVALID_CHAR_FOUND; + /* convert this surrogate code point */ + /* exit this condition tree */ + } else { + /* this is an unmatched lead code unit (1st surrogate) */ + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + } + } else { + /* no more input */ + *err = U_ZERO_ERROR; + } + } else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + } + } else { + /* callback(unassigned) for a BMP code point */ + *err = U_INVALID_CHAR_FOUND; + } + + args->converter->fromUChar32=sourceChar; + break; + } + } /* end if(myTargetIndexflush && source>=sourceLimit && args->converter->fromUChar32==0 + ) { + int32_t sourceIndex; + + /* we are switching to ASCII */ + isTargetByteDBCS=false; + + /* get the source index of the last input character */ + /* + * TODO this would be simpler and more reliable if we used a pair + * of sourceIndex/prevSourceIndex like in ucnvmbcs.c + * so that we could simply use the prevSourceIndex here; + * this code gives an incorrect result for the rare case of an unmatched + * trail surrogate that is alone in the last buffer of the text stream + */ + sourceIndex=(int32_t)(source-args->source); + if(sourceIndex>0) { + --sourceIndex; + if( U16_IS_TRAIL(args->source[sourceIndex]) && + (sourceIndex==0 || U16_IS_LEAD(args->source[sourceIndex-1])) + ) { + --sourceIndex; + } + } else { + sourceIndex=-1; + } + + fromUWriteUInt8( + args->converter, + SHIFT_IN_STR, 1, + &target, (const char *)targetLimit, + &offsets, sourceIndex, + err); + } + + /*save the state and return */ + args->source = source; + args->target = (char*)target; + args->converter->fromUnicodeStatus = (uint32_t)isTargetByteDBCS; +} + +/************************ To Unicode ***************************************/ + +static void U_CALLCONV +UConverter_toUnicode_ISO_2022_KR_OFFSETS_LOGIC_IBM(UConverterToUnicodeArgs *args, + UErrorCode* err){ + char const* sourceStart; + UConverterDataISO2022* myData=(UConverterDataISO2022*)(args->converter->extraInfo); + + UConverterToUnicodeArgs subArgs; + int32_t minArgsSize; + + /* set up the subconverter arguments */ + if(args->sizesize; + } else { + minArgsSize = (int32_t)sizeof(UConverterToUnicodeArgs); + } + + uprv_memcpy(&subArgs, args, minArgsSize); + subArgs.size = (uint16_t)minArgsSize; + subArgs.converter = myData->currentConverter; + + /* remember the original start of the input for offsets */ + sourceStart = args->source; + + if(myData->key != 0) { + /* continue with a partial escape sequence */ + goto escape; + } + + while(U_SUCCESS(*err) && args->source < args->sourceLimit) { + /*Find the end of the buffer e.g : Next Escape Seq | end of Buffer*/ + subArgs.source = args->source; + subArgs.sourceLimit = getEndOfBuffer_2022(&(args->source), args->sourceLimit, args->flush); + if(subArgs.source != subArgs.sourceLimit) { + /* + * get the current partial byte sequence + * + * it needs to be moved between the public and the subconverter + * so that the conversion framework, which only sees the public + * converter, can handle truncated and illegal input etc. + */ + if(args->converter->toULength > 0) { + uprv_memcpy(subArgs.converter->toUBytes, args->converter->toUBytes, args->converter->toULength); + } + subArgs.converter->toULength = args->converter->toULength; + + /* + * Convert up to the end of the input, or to before the next escape character. + * Does not handle conversion extensions because the preToU[] state etc. + * is not copied. + */ + ucnv_MBCSToUnicodeWithOffsets(&subArgs, err); + + if(args->offsets != nullptr && sourceStart != args->source) { + /* update offsets to base them on the actual start of the input */ + int32_t *offsets = args->offsets; + char16_t *target = args->target; + int32_t delta = (int32_t)(args->source - sourceStart); + while(target < subArgs.target) { + if(*offsets >= 0) { + *offsets += delta; + } + ++offsets; + ++target; + } + } + args->source = subArgs.source; + args->target = subArgs.target; + args->offsets = subArgs.offsets; + + /* copy input/error/overflow buffers */ + if(subArgs.converter->toULength > 0) { + uprv_memcpy(args->converter->toUBytes, subArgs.converter->toUBytes, subArgs.converter->toULength); + } + args->converter->toULength = subArgs.converter->toULength; + + if(*err == U_BUFFER_OVERFLOW_ERROR) { + if(subArgs.converter->UCharErrorBufferLength > 0) { + uprv_memcpy(args->converter->UCharErrorBuffer, subArgs.converter->UCharErrorBuffer, + subArgs.converter->UCharErrorBufferLength); + } + args->converter->UCharErrorBufferLength=subArgs.converter->UCharErrorBufferLength; + subArgs.converter->UCharErrorBufferLength = 0; + } + } + + if (U_FAILURE(*err) || (args->source == args->sourceLimit)) { + return; + } + +escape: + changeState_2022(args->converter, + &(args->source), + args->sourceLimit, + ISO_2022_KR, + err); + } +} + +static void U_CALLCONV +UConverter_toUnicode_ISO_2022_KR_OFFSETS_LOGIC(UConverterToUnicodeArgs *args, + UErrorCode* err){ + char tempBuf[2]; + const char *mySource = ( char *) args->source; + char16_t *myTarget = args->target; + const char *mySourceLimit = args->sourceLimit; + UChar32 targetUniChar = 0x0000; + char16_t mySourceChar = 0x0000; + UConverterDataISO2022* myData; + UConverterSharedData* sharedData ; + UBool useFallback; + + myData=(UConverterDataISO2022*)(args->converter->extraInfo); + if(myData->version==1){ + UConverter_toUnicode_ISO_2022_KR_OFFSETS_LOGIC_IBM(args,err); + return; + } + + /* initialize state */ + sharedData = myData->currentConverter->sharedData; + useFallback = args->converter->useFallback; + + if(myData->key != 0) { + /* continue with a partial escape sequence */ + goto escape; + } else if(args->converter->toULength == 1 && mySource < mySourceLimit && myTarget < args->targetLimit) { + /* continue with a partial double-byte character */ + mySourceChar = args->converter->toUBytes[0]; + args->converter->toULength = 0; + goto getTrailByte; + } + + while(mySource< mySourceLimit){ + + if(myTarget < args->targetLimit){ + + mySourceChar= (unsigned char) *mySource++; + + if(mySourceChar==UCNV_SI){ + myData->toU2022State.g = 0; + if (myData->isEmptySegment) { + myData->isEmptySegment = false; /* we are handling it, reset to avoid future spurious errors */ + *err = U_ILLEGAL_ESCAPE_SEQUENCE; + args->converter->toUCallbackReason = UCNV_IRREGULAR; + args->converter->toUBytes[0] = (uint8_t)mySourceChar; + args->converter->toULength = 1; + args->target = myTarget; + args->source = mySource; + return; + } + /*consume the source */ + continue; + }else if(mySourceChar==UCNV_SO){ + myData->toU2022State.g = 1; + myData->isEmptySegment = true; /* Begin a new segment, empty so far */ + /*consume the source */ + continue; + }else if(mySourceChar==ESC_2022){ + mySource--; +escape: + myData->isEmptySegment = false; /* Any invalid ESC sequences will be detected separately, so just reset this */ + changeState_2022(args->converter,&(mySource), + mySourceLimit, ISO_2022_KR, err); + if(U_FAILURE(*err)){ + args->target = myTarget; + args->source = mySource; + return; + } + continue; + } + + myData->isEmptySegment = false; /* Any invalid char errors will be detected separately, so just reset this */ + if(myData->toU2022State.g == 1) { + if(mySource < mySourceLimit) { + int leadIsOk, trailIsOk; + uint8_t trailByte; +getTrailByte: + targetUniChar = missingCharMarker; + trailByte = (uint8_t)*mySource; + /* + * Ticket 5691: consistent illegal sequences: + * - We include at least the first byte in the illegal sequence. + * - If any of the non-initial bytes could be the start of a character, + * we stop the illegal sequence before the first one of those. + * + * In ISO-2022 DBCS, if the second byte is in the 21..7e range or is + * an ESC/SO/SI, we report only the first byte as the illegal sequence. + * Otherwise we convert or report the pair of bytes. + */ + leadIsOk = (uint8_t)(mySourceChar - 0x21) <= (0x7e - 0x21); + trailIsOk = (uint8_t)(trailByte - 0x21) <= (0x7e - 0x21); + if (leadIsOk && trailIsOk) { + ++mySource; + tempBuf[0] = (char)(mySourceChar + 0x80); + tempBuf[1] = (char)(trailByte + 0x80); + targetUniChar = ucnv_MBCSSimpleGetNextUChar(sharedData, tempBuf, 2, useFallback); + mySourceChar = (mySourceChar << 8) | trailByte; + } else if (!(trailIsOk || IS_2022_CONTROL(trailByte))) { + /* report a pair of illegal bytes if the second byte is not a DBCS starter */ + ++mySource; + /* add another bit so that the code below writes 2 bytes in case of error */ + mySourceChar = static_cast(0x10000 | (mySourceChar << 8) | trailByte); + } + } else { + args->converter->toUBytes[0] = (uint8_t)mySourceChar; + args->converter->toULength = 1; + break; + } + } + else if(mySourceChar <= 0x7f) { + targetUniChar = ucnv_MBCSSimpleGetNextUChar(sharedData, mySource - 1, 1, useFallback); + } else { + targetUniChar = 0xffff; + } + if(targetUniChar < 0xfffe){ + if(args->offsets) { + args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); + } + *(myTarget++)=(char16_t)targetUniChar; + } + else { + /* Call the callback function*/ + toUnicodeCallback(args->converter,mySourceChar,targetUniChar,err); + break; + } + } + else{ + *err =U_BUFFER_OVERFLOW_ERROR; + break; + } + } + args->target = myTarget; + args->source = mySource; +} + +/*************************** END ISO2022-KR *********************************/ + +/*************************** ISO-2022-CN ********************************* +* +* Rules for ISO-2022-CN Encoding: +* i) The designator sequence must appear once on a line before any instance +* of character set it designates. +* ii) If two lines contain characters from the same character set, both lines +* must include the designator sequence. +* iii) Once the designator sequence is known, a shifting sequence has to be found +* to invoke the shifting +* iv) All lines start in ASCII and end in ASCII. +* v) Four shifting sequences are employed for this purpose: +* +* Sequcence ASCII Eq Charsets +* ---------- ------- --------- +* SI US-ASCII +* SO CNS-11643-1992 Plane 1, GB2312, ISO-IR-165 +* SS2 N CNS-11643-1992 Plane 2 +* SS3 O CNS-11643-1992 Planes 3-7 +* +* vi) +* SOdesignator : ESC "$" ")" finalchar_for_SO +* SS2designator : ESC "$" "*" finalchar_for_SS2 +* SS3designator : ESC "$" "+" finalchar_for_SS3 +* +* ESC $ ) A Indicates the bytes following SO are Chinese +* characters as defined in GB 2312-80, until +* another SOdesignation appears +* +* +* ESC $ ) E Indicates the bytes following SO are as defined +* in ISO-IR-165 (for details, see section 2.1), +* until another SOdesignation appears +* +* ESC $ ) G Indicates the bytes following SO are as defined +* in CNS 11643-plane-1, until another +* SOdesignation appears +* +* ESC $ * H Indicates the two bytes immediately following +* SS2 is a Chinese character as defined in CNS +* 11643-plane-2, until another SS2designation +* appears +* (Meaning N must precede every 2 byte +* sequence.) +* +* ESC $ + I Indicates the immediate two bytes following SS3 +* is a Chinese character as defined in CNS +* 11643-plane-3, until another SS3designation +* appears +* (Meaning O must precede every 2 byte +* sequence.) +* +* ESC $ + J Indicates the immediate two bytes following SS3 +* is a Chinese character as defined in CNS +* 11643-plane-4, until another SS3designation +* appears +* (In English: O must precede every 2 byte +* sequence.) +* +* ESC $ + K Indicates the immediate two bytes following SS3 +* is a Chinese character as defined in CNS +* 11643-plane-5, until another SS3designation +* appears +* +* ESC $ + L Indicates the immediate two bytes following SS3 +* is a Chinese character as defined in CNS +* 11643-plane-6, until another SS3designation +* appears +* +* ESC $ + M Indicates the immediate two bytes following SS3 +* is a Chinese character as defined in CNS +* 11643-plane-7, until another SS3designation +* appears +* +* As in ISO-2022-CN, each line starts in ASCII, and ends in ASCII, and +* has its own designation information before any Chinese characters +* appear +* +*/ + +/* The following are defined this way to make the strings truly readonly */ +static const char GB_2312_80_STR[] = "\x1B\x24\x29\x41"; +static const char ISO_IR_165_STR[] = "\x1B\x24\x29\x45"; +static const char CNS_11643_1992_Plane_1_STR[] = "\x1B\x24\x29\x47"; +static const char CNS_11643_1992_Plane_2_STR[] = "\x1B\x24\x2A\x48"; +static const char CNS_11643_1992_Plane_3_STR[] = "\x1B\x24\x2B\x49"; +static const char CNS_11643_1992_Plane_4_STR[] = "\x1B\x24\x2B\x4A"; +static const char CNS_11643_1992_Plane_5_STR[] = "\x1B\x24\x2B\x4B"; +static const char CNS_11643_1992_Plane_6_STR[] = "\x1B\x24\x2B\x4C"; +static const char CNS_11643_1992_Plane_7_STR[] = "\x1B\x24\x2B\x4D"; + +/********************** ISO2022-CN Data **************************/ +static const char* const escSeqCharsCN[10] ={ + SHIFT_IN_STR, /* 0 ASCII */ + GB_2312_80_STR, /* 1 GB2312_1 */ + ISO_IR_165_STR, /* 2 ISO_IR_165 */ + CNS_11643_1992_Plane_1_STR, + CNS_11643_1992_Plane_2_STR, + CNS_11643_1992_Plane_3_STR, + CNS_11643_1992_Plane_4_STR, + CNS_11643_1992_Plane_5_STR, + CNS_11643_1992_Plane_6_STR, + CNS_11643_1992_Plane_7_STR +}; + +static void U_CALLCONV +UConverter_fromUnicode_ISO_2022_CN_OFFSETS_LOGIC(UConverterFromUnicodeArgs* args, UErrorCode* err){ + UConverter *cnv = args->converter; + UConverterDataISO2022 *converterData; + ISO2022State *pFromU2022State; + uint8_t *target = (uint8_t *) args->target; + const uint8_t *targetLimit = (const uint8_t *) args->targetLimit; + const char16_t* source = args->source; + const char16_t* sourceLimit = args->sourceLimit; + int32_t* offsets = args->offsets; + UChar32 sourceChar; + char buffer[8]; + int32_t len; + int8_t choices[3]; + int32_t choiceCount; + uint32_t targetValue = 0; + UBool useFallback; + + /* set up the state */ + converterData = (UConverterDataISO2022*)cnv->extraInfo; + pFromU2022State = &converterData->fromU2022State; + + choiceCount = 0; + + /* check if the last codepoint of previous buffer was a lead surrogate*/ + if((sourceChar = cnv->fromUChar32)!=0 && target< targetLimit) { + goto getTrail; + } + + while( source < sourceLimit){ + if(target < targetLimit){ + + sourceChar = *(source++); + /*check if the char is a First surrogate*/ + if(U16_IS_SURROGATE(sourceChar)) { + if(U16_IS_SURROGATE_LEAD(sourceChar)) { +getTrail: + /*look ahead to find the trail surrogate*/ + if(source < sourceLimit) { + /* test the following code unit */ + char16_t trail=(char16_t) *source; + if(U16_IS_TRAIL(trail)) { + source++; + sourceChar=U16_GET_SUPPLEMENTARY(sourceChar, trail); + cnv->fromUChar32=0x00; + /* convert this supplementary code point */ + /* exit this condition tree */ + } else { + /* this is an unmatched lead code unit (1st surrogate) */ + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + cnv->fromUChar32=sourceChar; + break; + } + } else { + /* no more input */ + cnv->fromUChar32=sourceChar; + break; + } + } else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + cnv->fromUChar32=sourceChar; + break; + } + } + + /* do the conversion */ + if(sourceChar <= 0x007f ){ + /* do not convert SO/SI/ESC */ + if(IS_2022_CONTROL(sourceChar)) { + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + cnv->fromUChar32=sourceChar; + break; + } + + /* US-ASCII */ + if(pFromU2022State->g == 0) { + buffer[0] = (char)sourceChar; + len = 1; + } else { + buffer[0] = UCNV_SI; + buffer[1] = (char)sourceChar; + len = 2; + pFromU2022State->g = 0; + choiceCount = 0; + } + if(sourceChar == CR || sourceChar == LF) { + /* reset the state at the end of a line */ + uprv_memset(pFromU2022State, 0, sizeof(ISO2022State)); + choiceCount = 0; + } + } + else{ + /* convert U+0080..U+10ffff */ + int32_t i; + int8_t cs, g; + + if(choiceCount == 0) { + /* try the current SO/G1 converter first */ + choices[0] = pFromU2022State->cs[1]; + + /* default to GB2312_1 if none is designated yet */ + if(choices[0] == 0) { + choices[0] = GB2312_1; + } + + if(converterData->version == 0) { + /* ISO-2022-CN */ + + /* try the other SO/G1 converter; a CNS_11643_1 lookup may result in any plane */ + if(choices[0] == GB2312_1) { + choices[1] = (int8_t)CNS_11643_1; + } else { + choices[1] = (int8_t)GB2312_1; + } + + choiceCount = 2; + } else if (converterData->version == 1) { + /* ISO-2022-CN-EXT */ + + /* try one of the other converters */ + switch(choices[0]) { + case GB2312_1: + choices[1] = (int8_t)CNS_11643_1; + choices[2] = (int8_t)ISO_IR_165; + break; + case ISO_IR_165: + choices[1] = (int8_t)GB2312_1; + choices[2] = (int8_t)CNS_11643_1; + break; + default: /* CNS_11643_x */ + choices[1] = (int8_t)GB2312_1; + choices[2] = (int8_t)ISO_IR_165; + break; + } + + choiceCount = 3; + } else { + choices[0] = (int8_t)CNS_11643_1; + choices[1] = (int8_t)GB2312_1; + } + } + + cs = g = 0; + /* + * len==0: no mapping found yet + * len<0: found a fallback result: continue looking for a roundtrip but no further fallbacks + * len>0: found a roundtrip result, done + */ + len = 0; + /* + * We will turn off useFallback after finding a fallback, + * but we still get fallbacks from PUA code points as usual. + * Therefore, we will also need to check that we don't overwrite + * an early fallback with a later one. + */ + useFallback = cnv->useFallback; + + for(i = 0; i < choiceCount && len <= 0; ++i) { + int8_t cs0 = choices[i]; + if(cs0 > 0) { + uint32_t value; + int32_t len2; + if(cs0 >= CNS_11643_0) { + len2 = MBCS_FROM_UCHAR32_ISO2022( + converterData->myConverterArray[CNS_11643], + sourceChar, + &value, + useFallback, + MBCS_OUTPUT_3); + if(len2 == 3 || (len2 == -3 && len == 0)) { + targetValue = value; + cs = (int8_t)(CNS_11643_0 + (value >> 16) - 0x80); + if(len2 >= 0) { + len = 2; + } else { + len = -2; + useFallback = false; + } + if(cs == CNS_11643_1) { + g = 1; + } else if(cs == CNS_11643_2) { + g = 2; + } else /* plane 3..7 */ if(converterData->version == 1) { + g = 3; + } else { + /* ISO-2022-CN (without -EXT) does not support plane 3..7 */ + len = 0; + } + } + } else { + /* GB2312_1 or ISO-IR-165 */ + U_ASSERT(cs0myConverterArray[cs0], + sourceChar, + &value, + useFallback, + MBCS_OUTPUT_2); + if(len2 == 2 || (len2 == -2 && len == 0)) { + targetValue = value; + len = len2; + cs = cs0; + g = 1; + useFallback = false; + } + } + } + } + + if(len != 0) { + len = 0; /* count output bytes; it must have been abs(len) == 2 */ + + /* write the designation sequence if necessary */ + if(cs != pFromU2022State->cs[g]) { + if(cs < CNS_11643) { + uprv_memcpy(buffer, escSeqCharsCN[cs], 4); + } else { + U_ASSERT(cs >= CNS_11643_1); + uprv_memcpy(buffer, escSeqCharsCN[CNS_11643 + (cs - CNS_11643_1)], 4); + } + len = 4; + pFromU2022State->cs[g] = cs; + if(g == 1) { + /* changing the SO/G1 charset invalidates the choices[] */ + choiceCount = 0; + } + } + + /* write the shift sequence if necessary */ + if(g != pFromU2022State->g) { + switch(g) { + case 1: + buffer[len++] = UCNV_SO; + + /* set the new state only if it is the locking shift SO/G1, not for SS2 or SS3 */ + pFromU2022State->g = 1; + break; + case 2: + buffer[len++] = 0x1b; + buffer[len++] = 0x4e; + break; + default: /* case 3 */ + buffer[len++] = 0x1b; + buffer[len++] = 0x4f; + break; + } + } + + /* write the two output bytes */ + buffer[len++] = (char)(targetValue >> 8); + buffer[len++] = (char)targetValue; + } else { + /* if we cannot find the character after checking all codepages + * then this is an error + */ + *err = U_INVALID_CHAR_FOUND; + cnv->fromUChar32=sourceChar; + break; + } + } + + /* output len>0 bytes in buffer[] */ + if(len == 1) { + *target++ = buffer[0]; + if(offsets) { + *offsets++ = (int32_t)(source - args->source - 1); /* -1: known to be ASCII */ + } + } else if(len == 2 && (target + 2) <= targetLimit) { + *target++ = buffer[0]; + *target++ = buffer[1]; + if(offsets) { + int32_t sourceIndex = (int32_t)(source - args->source - U16_LENGTH(sourceChar)); + *offsets++ = sourceIndex; + *offsets++ = sourceIndex; + } + } else { + fromUWriteUInt8( + cnv, + buffer, len, + &target, (const char *)targetLimit, + &offsets, (int32_t)(source - args->source - U16_LENGTH(sourceChar)), + err); + if(U_FAILURE(*err)) { + break; + } + } + } /* end if(myTargetIndexg!=0 && + args->flush && source>=sourceLimit && cnv->fromUChar32==0 + ) { + int32_t sourceIndex; + + /* we are switching to ASCII */ + pFromU2022State->g=0; + + /* get the source index of the last input character */ + /* + * TODO this would be simpler and more reliable if we used a pair + * of sourceIndex/prevSourceIndex like in ucnvmbcs.c + * so that we could simply use the prevSourceIndex here; + * this code gives an incorrect result for the rare case of an unmatched + * trail surrogate that is alone in the last buffer of the text stream + */ + sourceIndex=(int32_t)(source-args->source); + if(sourceIndex>0) { + --sourceIndex; + if( U16_IS_TRAIL(args->source[sourceIndex]) && + (sourceIndex==0 || U16_IS_LEAD(args->source[sourceIndex-1])) + ) { + --sourceIndex; + } + } else { + sourceIndex=-1; + } + + fromUWriteUInt8( + cnv, + SHIFT_IN_STR, 1, + &target, (const char *)targetLimit, + &offsets, sourceIndex, + err); + } + + /*save the state and return */ + args->source = source; + args->target = (char*)target; +} + + +static void U_CALLCONV +UConverter_toUnicode_ISO_2022_CN_OFFSETS_LOGIC(UConverterToUnicodeArgs *args, + UErrorCode* err){ + char tempBuf[3]; + const char *mySource = (char *) args->source; + char16_t *myTarget = args->target; + const char *mySourceLimit = args->sourceLimit; + uint32_t targetUniChar = 0x0000; + uint32_t mySourceChar = 0x0000; + UConverterDataISO2022* myData; + ISO2022State *pToU2022State; + + myData=(UConverterDataISO2022*)(args->converter->extraInfo); + pToU2022State = &myData->toU2022State; + + if(myData->key != 0) { + /* continue with a partial escape sequence */ + goto escape; + } else if(args->converter->toULength == 1 && mySource < mySourceLimit && myTarget < args->targetLimit) { + /* continue with a partial double-byte character */ + mySourceChar = args->converter->toUBytes[0]; + args->converter->toULength = 0; + targetUniChar = missingCharMarker; + goto getTrailByte; + } + + while(mySource < mySourceLimit){ + + targetUniChar =missingCharMarker; + + if(myTarget < args->targetLimit){ + + mySourceChar= (unsigned char) *mySource++; + + switch(mySourceChar){ + case UCNV_SI: + pToU2022State->g=0; + if (myData->isEmptySegment) { + myData->isEmptySegment = false; /* we are handling it, reset to avoid future spurious errors */ + *err = U_ILLEGAL_ESCAPE_SEQUENCE; + args->converter->toUCallbackReason = UCNV_IRREGULAR; + args->converter->toUBytes[0] = static_cast(mySourceChar); + args->converter->toULength = 1; + args->target = myTarget; + args->source = mySource; + return; + } + continue; + + case UCNV_SO: + if(pToU2022State->cs[1] != 0) { + pToU2022State->g=1; + myData->isEmptySegment = true; /* Begin a new segment, empty so far */ + continue; + } else { + /* illegal to have SO before a matching designator */ + myData->isEmptySegment = false; /* Handling a different error, reset this to avoid future spurious errs */ + break; + } + + case ESC_2022: + mySource--; +escape: + { + const char * mySourceBefore = mySource; + int8_t toULengthBefore = args->converter->toULength; + + changeState_2022(args->converter,&(mySource), + mySourceLimit, ISO_2022_CN,err); + + /* After SO there must be at least one character before a designator (designator error handled separately) */ + if(myData->key==0 && U_SUCCESS(*err) && myData->isEmptySegment) { + *err = U_ILLEGAL_ESCAPE_SEQUENCE; + args->converter->toUCallbackReason = UCNV_IRREGULAR; + args->converter->toULength = (int8_t)(toULengthBefore + (mySource - mySourceBefore)); + } + } + + /* invalid or illegal escape sequence */ + if(U_FAILURE(*err)){ + args->target = myTarget; + args->source = mySource; + myData->isEmptySegment = false; /* Reset to avoid future spurious errors */ + return; + } + continue; + + /* ISO-2022-CN does not use single-byte (C1) SS2 and SS3 */ + + case CR: + case LF: + uprv_memset(pToU2022State, 0, sizeof(ISO2022State)); + U_FALLTHROUGH; + default: + /* convert one or two bytes */ + myData->isEmptySegment = false; + if(pToU2022State->g != 0) { + if(mySource < mySourceLimit) { + UConverterSharedData *cnv; + StateEnum tempState; + int32_t tempBufLen; + int leadIsOk, trailIsOk; + uint8_t trailByte; +getTrailByte: + trailByte = (uint8_t)*mySource; + /* + * Ticket 5691: consistent illegal sequences: + * - We include at least the first byte in the illegal sequence. + * - If any of the non-initial bytes could be the start of a character, + * we stop the illegal sequence before the first one of those. + * + * In ISO-2022 DBCS, if the second byte is in the 21..7e range or is + * an ESC/SO/SI, we report only the first byte as the illegal sequence. + * Otherwise we convert or report the pair of bytes. + */ + leadIsOk = (uint8_t)(mySourceChar - 0x21) <= (0x7e - 0x21); + trailIsOk = (uint8_t)(trailByte - 0x21) <= (0x7e - 0x21); + if (leadIsOk && trailIsOk) { + ++mySource; + tempState = (StateEnum)pToU2022State->cs[pToU2022State->g]; + if(tempState >= CNS_11643_0) { + cnv = myData->myConverterArray[CNS_11643]; + tempBuf[0] = (char) (0x80+(tempState-CNS_11643_0)); + tempBuf[1] = (char) (mySourceChar); + tempBuf[2] = (char) trailByte; + tempBufLen = 3; + + }else{ + U_ASSERT(tempStatemyConverterArray[tempState]; + tempBuf[0] = (char) (mySourceChar); + tempBuf[1] = (char) trailByte; + tempBufLen = 2; + } + targetUniChar = ucnv_MBCSSimpleGetNextUChar(cnv, tempBuf, tempBufLen, false); + mySourceChar = (mySourceChar << 8) | trailByte; + } else if (!(trailIsOk || IS_2022_CONTROL(trailByte))) { + /* report a pair of illegal bytes if the second byte is not a DBCS starter */ + ++mySource; + /* add another bit so that the code below writes 2 bytes in case of error */ + mySourceChar = 0x10000 | (mySourceChar << 8) | trailByte; + } + if(pToU2022State->g>=2) { + /* return from a single-shift state to the previous one */ + pToU2022State->g=pToU2022State->prevG; + } + } else { + args->converter->toUBytes[0] = (uint8_t)mySourceChar; + args->converter->toULength = 1; + goto endloop; + } + } + else{ + if(mySourceChar <= 0x7f) { + targetUniChar = (char16_t) mySourceChar; + } + } + break; + } + if(targetUniChar < (missingCharMarker-1/*0xfffe*/)){ + if(args->offsets){ + args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); + } + *(myTarget++)=(char16_t)targetUniChar; + } + else if(targetUniChar > missingCharMarker){ + /* disassemble the surrogate pair and write to output*/ + targetUniChar-=0x0010000; + *myTarget = (char16_t)(0xd800+(char16_t)(targetUniChar>>10)); + if(args->offsets){ + args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); + } + ++myTarget; + if(myTarget< args->targetLimit){ + *myTarget = (char16_t)(0xdc00+(char16_t)(targetUniChar&0x3ff)); + if(args->offsets){ + args->offsets[myTarget - args->target] = (int32_t)(mySource - args->source - (mySourceChar <= 0xff ? 1 : 2)); + } + ++myTarget; + }else{ + args->converter->UCharErrorBuffer[args->converter->UCharErrorBufferLength++]= + (char16_t)(0xdc00+(char16_t)(targetUniChar&0x3ff)); + } + + } + else{ + /* Call the callback function*/ + toUnicodeCallback(args->converter,mySourceChar,targetUniChar,err); + break; + } + } + else{ + *err =U_BUFFER_OVERFLOW_ERROR; + break; + } + } +endloop: + args->target = myTarget; + args->source = mySource; +} +#endif /* #if !UCONFIG_ONLY_HTML_CONVERSION */ + +static void U_CALLCONV +_ISO_2022_WriteSub(UConverterFromUnicodeArgs *args, int32_t offsetIndex, UErrorCode *err) { + UConverter *cnv = args->converter; + UConverterDataISO2022 *myConverterData=(UConverterDataISO2022 *) cnv->extraInfo; + ISO2022State *pFromU2022State=&myConverterData->fromU2022State; + char *p, *subchar; + char buffer[8]; + int32_t length; + + subchar=(char *)cnv->subChars; + length=cnv->subCharLen; /* assume length==1 for most variants */ + + p = buffer; + switch(myConverterData->locale[0]){ + case 'j': + { + int8_t cs; + + if(pFromU2022State->g == 1) { + /* JIS7: switch from G1 to G0 */ + pFromU2022State->g = 0; + *p++ = UCNV_SI; + } + + cs = pFromU2022State->cs[0]; + if(cs != ASCII && cs != JISX201) { + /* not in ASCII or JIS X 0201: switch to ASCII */ + pFromU2022State->cs[0] = (int8_t)ASCII; + *p++ = '\x1b'; + *p++ = '\x28'; + *p++ = '\x42'; + } + + *p++ = subchar[0]; + break; + } + case 'c': + if(pFromU2022State->g != 0) { + /* not in ASCII mode: switch to ASCII */ + pFromU2022State->g = 0; + *p++ = UCNV_SI; + } + *p++ = subchar[0]; + break; + case 'k': + if(myConverterData->version == 0) { + if(length == 1) { + if(args->converter->fromUnicodeStatus) { + /* in DBCS mode: switch to SBCS */ + args->converter->fromUnicodeStatus = 0; + *p++ = UCNV_SI; + } + *p++ = subchar[0]; + } else /* length == 2*/ { + if(!args->converter->fromUnicodeStatus) { + /* in SBCS mode: switch to DBCS */ + args->converter->fromUnicodeStatus = 1; + *p++ = UCNV_SO; + } + *p++ = subchar[0]; + *p++ = subchar[1]; + } + break; + } else { + /* save the subconverter's substitution string */ + uint8_t *currentSubChars = myConverterData->currentConverter->subChars; + int8_t currentSubCharLen = myConverterData->currentConverter->subCharLen; + + /* set our substitution string into the subconverter */ + myConverterData->currentConverter->subChars = (uint8_t *)subchar; + myConverterData->currentConverter->subCharLen = (int8_t)length; + + /* let the subconverter write the subchar, set/retrieve fromUChar32 state */ + args->converter = myConverterData->currentConverter; + myConverterData->currentConverter->fromUChar32 = cnv->fromUChar32; + ucnv_cbFromUWriteSub(args, 0, err); + cnv->fromUChar32 = myConverterData->currentConverter->fromUChar32; + args->converter = cnv; + + /* restore the subconverter's substitution string */ + myConverterData->currentConverter->subChars = currentSubChars; + myConverterData->currentConverter->subCharLen = currentSubCharLen; + + if(*err == U_BUFFER_OVERFLOW_ERROR) { + if(myConverterData->currentConverter->charErrorBufferLength > 0) { + uprv_memcpy( + cnv->charErrorBuffer, + myConverterData->currentConverter->charErrorBuffer, + myConverterData->currentConverter->charErrorBufferLength); + } + cnv->charErrorBufferLength = myConverterData->currentConverter->charErrorBufferLength; + myConverterData->currentConverter->charErrorBufferLength = 0; + } + return; + } + default: + /* not expected */ + break; + } + ucnv_cbFromUWriteBytes(args, + buffer, (int32_t)(p - buffer), + offsetIndex, err); +} + +/* + * Structure for cloning an ISO 2022 converter into a single memory block. + */ +struct cloneStruct +{ + UConverter cnv; + UConverter currentConverter; + UConverterDataISO2022 mydata; +}; + + +U_CDECL_BEGIN + +static UConverter * U_CALLCONV +_ISO_2022_SafeClone( + const UConverter *cnv, + void *stackBuffer, + int32_t *pBufferSize, + UErrorCode *status) +{ + struct cloneStruct * localClone; + UConverterDataISO2022 *cnvData; + int32_t i, size; + + if (U_FAILURE(*status)){ + return nullptr; + } + + if (*pBufferSize == 0) { /* 'preflighting' request - set needed size into *pBufferSize */ + *pBufferSize = (int32_t)sizeof(struct cloneStruct); + return nullptr; + } + + cnvData = (UConverterDataISO2022 *)cnv->extraInfo; + localClone = (struct cloneStruct *)stackBuffer; + + /* ucnv.c/ucnv_safeClone() copied the main UConverter already */ + + uprv_memcpy(&localClone->mydata, cnvData, sizeof(UConverterDataISO2022)); + localClone->cnv.extraInfo = &localClone->mydata; /* set pointer to extra data */ + localClone->cnv.isExtraLocal = true; + + /* share the subconverters */ + + if(cnvData->currentConverter != nullptr) { + size = (int32_t)sizeof(UConverter); + localClone->mydata.currentConverter = + ucnv_safeClone(cnvData->currentConverter, + &localClone->currentConverter, + &size, status); + if(U_FAILURE(*status)) { + return nullptr; + } + } + + for(i=0; imyConverterArray[i] != nullptr) { + ucnv_incrementRefCount(cnvData->myConverterArray[i]); + } + } + + return &localClone->cnv; +} + +U_CDECL_END + +static void U_CALLCONV +_ISO_2022_GetUnicodeSet(const UConverter *cnv, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode) +{ + int32_t i; + UConverterDataISO2022* cnvData; + + if (U_FAILURE(*pErrorCode)) { + return; + } +#ifdef U_ENABLE_GENERIC_ISO_2022 + if (cnv->sharedData == &_ISO2022Data) { + /* We use UTF-8 in this case */ + sa->addRange(sa->set, 0, 0xd7FF); + sa->addRange(sa->set, 0xE000, 0x10FFFF); + return; + } +#endif + + cnvData = (UConverterDataISO2022*)cnv->extraInfo; + + /* open a set and initialize it with code points that are algorithmically round-tripped */ + switch(cnvData->locale[0]){ + case 'j': + /* include JIS X 0201 which is hardcoded */ + sa->add(sa->set, 0xa5); + sa->add(sa->set, 0x203e); + if(jpCharsetMasks[cnvData->version]&CSM(ISO8859_1)) { + /* include Latin-1 for some variants of JP */ + sa->addRange(sa->set, 0, 0xff); + } else { + /* include ASCII for JP */ + sa->addRange(sa->set, 0, 0x7f); + } + if(cnvData->version==3 || cnvData->version==4 || which==UCNV_ROUNDTRIP_AND_FALLBACK_SET) { + /* + * Do not test (jpCharsetMasks[cnvData->version]&CSM(HWKANA_7BIT))!=0 + * because the bit is on for all JP versions although only versions 3 & 4 (JIS7 & JIS8) + * use half-width Katakana. + * This is because all ISO-2022-JP variants are lenient in that they accept (in toUnicode) + * half-width Katakana via the ESC ( I sequence. + * However, we only emit (fromUnicode) half-width Katakana according to the + * definition of each variant. + * + * When including fallbacks, + * we need to include half-width Katakana Unicode code points for all JP variants because + * JIS X 0208 has hardcoded fallbacks for them (which map to full-width Katakana). + */ + /* include half-width Katakana for JP */ + sa->addRange(sa->set, HWKANA_START, HWKANA_END); + } + break; +#if !UCONFIG_ONLY_HTML_CONVERSION + case 'c': + case 'z': + /* include ASCII for CN */ + sa->addRange(sa->set, 0, 0x7f); + break; + case 'k': + /* there is only one converter for KR, and it is not in the myConverterArray[] */ + cnvData->currentConverter->sharedData->impl->getUnicodeSet( + cnvData->currentConverter, sa, which, pErrorCode); + /* the loop over myConverterArray[] will simply not find another converter */ + break; +#endif + default: + break; + } + +#if 0 /* Replaced by ucnv_MBCSGetFilteredUnicodeSetForUnicode() until we implement ucnv_getUnicodeSet() with reverse fallbacks. */ + if( (cnvData->locale[0]=='c' || cnvData->locale[0]=='z') && + cnvData->version==0 && i==CNS_11643 + ) { + /* special handling for non-EXT ISO-2022-CN: add only code points for CNS planes 1 and 2 */ + ucnv_MBCSGetUnicodeSetForBytes( + cnvData->myConverterArray[i], + sa, UCNV_ROUNDTRIP_SET, + 0, 0x81, 0x82, + pErrorCode); + } +#endif + + for (i=0; imyConverterArray[i]!=nullptr) { + if(cnvData->locale[0]=='j' && i==JISX208) { + /* + * Only add code points that map to Shift-JIS codes + * corresponding to JIS X 0208. + */ + filter=UCNV_SET_FILTER_SJIS; +#if !UCONFIG_ONLY_HTML_CONVERSION + } else if( (cnvData->locale[0]=='c' || cnvData->locale[0]=='z') && + cnvData->version==0 && i==CNS_11643) { + /* + * Version-specific for CN: + * CN version 0 does not map CNS planes 3..7 although + * they are all available in the CNS conversion table; + * CN version 1 (-EXT) does map them all. + * The two versions create different Unicode sets. + */ + filter=UCNV_SET_FILTER_2022_CN; + } else if(i==KSC5601) { + /* + * Some of the KSC 5601 tables (convrtrs.txt has this aliases on multiple tables) + * are broader than GR94. + */ + filter=UCNV_SET_FILTER_GR94DBCS; +#endif + } else { + filter=UCNV_SET_FILTER_NONE; + } + ucnv_MBCSGetFilteredUnicodeSetForUnicode(cnvData->myConverterArray[i], sa, which, filter, pErrorCode); + } + } + + /* + * ISO 2022 converters must not convert SO/SI/ESC despite what + * sub-converters do by themselves. + * Remove these characters from the set. + */ + sa->remove(sa->set, 0x0e); + sa->remove(sa->set, 0x0f); + sa->remove(sa->set, 0x1b); + + /* ISO 2022 converters do not convert C1 controls either */ + sa->removeRange(sa->set, 0x80, 0x9f); +} + +static const UConverterImpl _ISO2022Impl={ + UCNV_ISO_2022, + + nullptr, + nullptr, + + _ISO2022Open, + _ISO2022Close, + _ISO2022Reset, + +#ifdef U_ENABLE_GENERIC_ISO_2022 + T_UConverter_toUnicode_ISO_2022_OFFSETS_LOGIC, + T_UConverter_toUnicode_ISO_2022_OFFSETS_LOGIC, + ucnv_fromUnicode_UTF8, + ucnv_fromUnicode_UTF8_OFFSETS_LOGIC, +#else + nullptr, + nullptr, + nullptr, + nullptr, +#endif + nullptr, + + nullptr, + _ISO2022getName, + _ISO_2022_WriteSub, + _ISO_2022_SafeClone, + _ISO_2022_GetUnicodeSet, + + nullptr, + nullptr +}; +static const UConverterStaticData _ISO2022StaticData={ + sizeof(UConverterStaticData), + "ISO_2022", + 2022, + UCNV_IBM, + UCNV_ISO_2022, + 1, + 3, /* max 3 bytes per char16_t from UTF-8 (4 bytes from surrogate _pair_) */ + { 0x1a, 0, 0, 0 }, + 1, + false, + false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; +const UConverterSharedData _ISO2022Data= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_ISO2022StaticData, &_ISO2022Impl); + +/*************JP****************/ +static const UConverterImpl _ISO2022JPImpl={ + UCNV_ISO_2022, + + nullptr, + nullptr, + + _ISO2022Open, + _ISO2022Close, + _ISO2022Reset, + + UConverter_toUnicode_ISO_2022_JP_OFFSETS_LOGIC, + UConverter_toUnicode_ISO_2022_JP_OFFSETS_LOGIC, + UConverter_fromUnicode_ISO_2022_JP_OFFSETS_LOGIC, + UConverter_fromUnicode_ISO_2022_JP_OFFSETS_LOGIC, + nullptr, + + nullptr, + _ISO2022getName, + _ISO_2022_WriteSub, + _ISO_2022_SafeClone, + _ISO_2022_GetUnicodeSet, + + nullptr, + nullptr +}; +static const UConverterStaticData _ISO2022JPStaticData={ + sizeof(UConverterStaticData), + "ISO_2022_JP", + 0, + UCNV_IBM, + UCNV_ISO_2022, + 1, + 6, /* max 6 bytes per char16_t: 4-byte escape sequence + DBCS */ + { 0x1a, 0, 0, 0 }, + 1, + false, + false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + +namespace { + +const UConverterSharedData _ISO2022JPData= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_ISO2022JPStaticData, &_ISO2022JPImpl); + +} // namespace + +#if !UCONFIG_ONLY_HTML_CONVERSION +/************* KR ***************/ +static const UConverterImpl _ISO2022KRImpl={ + UCNV_ISO_2022, + + nullptr, + nullptr, + + _ISO2022Open, + _ISO2022Close, + _ISO2022Reset, + + UConverter_toUnicode_ISO_2022_KR_OFFSETS_LOGIC, + UConverter_toUnicode_ISO_2022_KR_OFFSETS_LOGIC, + UConverter_fromUnicode_ISO_2022_KR_OFFSETS_LOGIC, + UConverter_fromUnicode_ISO_2022_KR_OFFSETS_LOGIC, + nullptr, + + nullptr, + _ISO2022getName, + _ISO_2022_WriteSub, + _ISO_2022_SafeClone, + _ISO_2022_GetUnicodeSet, + + nullptr, + nullptr +}; +static const UConverterStaticData _ISO2022KRStaticData={ + sizeof(UConverterStaticData), + "ISO_2022_KR", + 0, + UCNV_IBM, + UCNV_ISO_2022, + 1, + 8, /* max 8 bytes per char16_t */ + { 0x1a, 0, 0, 0 }, + 1, + false, + false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + +namespace { + +const UConverterSharedData _ISO2022KRData= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_ISO2022KRStaticData, &_ISO2022KRImpl); + +} // namespace + +/*************** CN ***************/ +static const UConverterImpl _ISO2022CNImpl={ + + UCNV_ISO_2022, + + nullptr, + nullptr, + + _ISO2022Open, + _ISO2022Close, + _ISO2022Reset, + + UConverter_toUnicode_ISO_2022_CN_OFFSETS_LOGIC, + UConverter_toUnicode_ISO_2022_CN_OFFSETS_LOGIC, + UConverter_fromUnicode_ISO_2022_CN_OFFSETS_LOGIC, + UConverter_fromUnicode_ISO_2022_CN_OFFSETS_LOGIC, + nullptr, + + nullptr, + _ISO2022getName, + _ISO_2022_WriteSub, + _ISO_2022_SafeClone, + _ISO_2022_GetUnicodeSet, + + nullptr, + nullptr +}; +static const UConverterStaticData _ISO2022CNStaticData={ + sizeof(UConverterStaticData), + "ISO_2022_CN", + 0, + UCNV_IBM, + UCNV_ISO_2022, + 1, + 8, /* max 8 bytes per char16_t: 4-byte CNS designator + 2 bytes for SS2/SS3 + DBCS */ + { 0x1a, 0, 0, 0 }, + 1, + false, + false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + +namespace { + +const UConverterSharedData _ISO2022CNData= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_ISO2022CNStaticData, &_ISO2022CNImpl); + +} // namespace +#endif /* #if !UCONFIG_ONLY_HTML_CONVERSION */ + +#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ diff --git a/deps/icu-small/source/common/ucnv_bld.cpp b/deps/icu-small/source/common/ucnv_bld.cpp index a0fbfe2d7f58a1..8acbf957759df0 100644 --- a/deps/icu-small/source/common/ucnv_bld.cpp +++ b/deps/icu-small/source/common/ucnv_bld.cpp @@ -1,1689 +1,1689 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ******************************************************************** - * COPYRIGHT: - * Copyright (c) 1996-2016, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************** - * - * ucnv_bld.cpp: - * - * Defines functions that are used in the creation/initialization/deletion - * of converters and related structures. - * uses uconv_io.h routines to access disk information - * is used by ucnv.h to implement public API create/delete/flushCache routines - * Modification History: - * - * Date Name Description - * - * 06/20/2000 helena OS/400 port changes; mostly typecast. - * 06/29/2000 helena Major rewrite of the callback interface. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/putil.h" -#include "unicode/udata.h" -#include "unicode/ucnv.h" -#include "unicode/uloc.h" -#include "mutex.h" -#include "putilimp.h" -#include "uassert.h" -#include "utracimp.h" -#include "ucnv_io.h" -#include "ucnv_bld.h" -#include "ucnvmbcs.h" -#include "ucnv_ext.h" -#include "ucnv_cnv.h" -#include "ucnv_imp.h" -#include "uhash.h" -#include "umutex.h" -#include "cstring.h" -#include "cmemory.h" -#include "ucln_cmn.h" -#include "ustr_cnv.h" - - -#if 0 -#include -extern void UCNV_DEBUG_LOG(char *what, char *who, void *p, int l); -#define UCNV_DEBUG_LOG(x,y,z) UCNV_DEBUG_LOG(x,y,z,__LINE__) -#else -# define UCNV_DEBUG_LOG(x,y,z) -#endif - -static const UConverterSharedData * const -converterData[UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES]={ - NULL, NULL, - -#if UCONFIG_NO_LEGACY_CONVERSION - NULL, -#else - &_MBCSData, -#endif - - &_Latin1Data, - &_UTF8Data, &_UTF16BEData, &_UTF16LEData, -#if UCONFIG_ONLY_HTML_CONVERSION - NULL, NULL, -#else - &_UTF32BEData, &_UTF32LEData, -#endif - NULL, - -#if UCONFIG_NO_LEGACY_CONVERSION - NULL, -#else - &_ISO2022Data, -#endif - -#if UCONFIG_NO_LEGACY_CONVERSION || UCONFIG_ONLY_HTML_CONVERSION - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, -#else - &_LMBCSData1,&_LMBCSData2, &_LMBCSData3, &_LMBCSData4, &_LMBCSData5, &_LMBCSData6, - &_LMBCSData8,&_LMBCSData11,&_LMBCSData16,&_LMBCSData17,&_LMBCSData18,&_LMBCSData19, - &_HZData, -#endif - -#if UCONFIG_ONLY_HTML_CONVERSION - NULL, -#else - &_SCSUData, -#endif - - -#if UCONFIG_NO_LEGACY_CONVERSION || UCONFIG_ONLY_HTML_CONVERSION - NULL, -#else - &_ISCIIData, -#endif - - &_ASCIIData, -#if UCONFIG_ONLY_HTML_CONVERSION - NULL, NULL, &_UTF16Data, NULL, NULL, NULL, -#else - &_UTF7Data, &_Bocu1Data, &_UTF16Data, &_UTF32Data, &_CESU8Data, &_IMAPData, -#endif - -#if UCONFIG_NO_LEGACY_CONVERSION || UCONFIG_ONLY_HTML_CONVERSION - NULL, -#else - &_CompoundTextData -#endif -}; - -/* Please keep this in binary sorted order for getAlgorithmicTypeFromName. - Also the name should be in lower case and all spaces, dashes and underscores - removed -*/ -static struct { - const char *name; - const UConverterType type; -} const cnvNameType[] = { -#if !UCONFIG_ONLY_HTML_CONVERSION - { "bocu1", UCNV_BOCU1 }, - { "cesu8", UCNV_CESU8 }, -#endif -#if !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION - { "hz",UCNV_HZ }, -#endif -#if !UCONFIG_ONLY_HTML_CONVERSION - { "imapmailboxname", UCNV_IMAP_MAILBOX }, -#endif -#if !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION - { "iscii", UCNV_ISCII }, -#endif -#if !UCONFIG_NO_LEGACY_CONVERSION - { "iso2022", UCNV_ISO_2022 }, -#endif - { "iso88591", UCNV_LATIN_1 }, -#if !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION - { "lmbcs1", UCNV_LMBCS_1 }, - { "lmbcs11",UCNV_LMBCS_11 }, - { "lmbcs16",UCNV_LMBCS_16 }, - { "lmbcs17",UCNV_LMBCS_17 }, - { "lmbcs18",UCNV_LMBCS_18 }, - { "lmbcs19",UCNV_LMBCS_19 }, - { "lmbcs2", UCNV_LMBCS_2 }, - { "lmbcs3", UCNV_LMBCS_3 }, - { "lmbcs4", UCNV_LMBCS_4 }, - { "lmbcs5", UCNV_LMBCS_5 }, - { "lmbcs6", UCNV_LMBCS_6 }, - { "lmbcs8", UCNV_LMBCS_8 }, -#endif -#if !UCONFIG_ONLY_HTML_CONVERSION - { "scsu", UCNV_SCSU }, -#endif - { "usascii", UCNV_US_ASCII }, - { "utf16", UCNV_UTF16 }, - { "utf16be", UCNV_UTF16_BigEndian }, - { "utf16le", UCNV_UTF16_LittleEndian }, -#if U_IS_BIG_ENDIAN - { "utf16oppositeendian", UCNV_UTF16_LittleEndian }, - { "utf16platformendian", UCNV_UTF16_BigEndian }, -#else - { "utf16oppositeendian", UCNV_UTF16_BigEndian}, - { "utf16platformendian", UCNV_UTF16_LittleEndian }, -#endif -#if !UCONFIG_ONLY_HTML_CONVERSION - { "utf32", UCNV_UTF32 }, - { "utf32be", UCNV_UTF32_BigEndian }, - { "utf32le", UCNV_UTF32_LittleEndian }, -#if U_IS_BIG_ENDIAN - { "utf32oppositeendian", UCNV_UTF32_LittleEndian }, - { "utf32platformendian", UCNV_UTF32_BigEndian }, -#else - { "utf32oppositeendian", UCNV_UTF32_BigEndian }, - { "utf32platformendian", UCNV_UTF32_LittleEndian }, -#endif -#endif -#if !UCONFIG_ONLY_HTML_CONVERSION - { "utf7", UCNV_UTF7 }, -#endif - { "utf8", UCNV_UTF8 }, -#if !UCONFIG_ONLY_HTML_CONVERSION - { "x11compoundtext", UCNV_COMPOUND_TEXT} -#endif -}; - - -/*initializes some global variables */ -static UHashtable *SHARED_DATA_HASHTABLE = NULL; -static icu::UMutex cnvCacheMutex; -/* Note: the global mutex is used for */ -/* reference count updates. */ - -static const char **gAvailableConverters = NULL; -static uint16_t gAvailableConverterCount = 0; -static icu::UInitOnce gAvailableConvertersInitOnce {}; - -#if !U_CHARSET_IS_UTF8 - -/* This contains the resolved converter name. So no further alias lookup is needed again. */ -static char gDefaultConverterNameBuffer[UCNV_MAX_CONVERTER_NAME_LENGTH + 1]; /* +1 for NULL */ -static const char *gDefaultConverterName = NULL; - -/* -If the default converter is an algorithmic converter, this is the cached value. -We don't cache a full UConverter and clone it because ucnv_clone doesn't have -less overhead than an algorithmic open. We don't cache non-algorithmic converters -because ucnv_flushCache must be able to unload the default converter and its table. -*/ -static const UConverterSharedData *gDefaultAlgorithmicSharedData = NULL; - -/* Does gDefaultConverterName have a converter option and require extra parsing? */ -static UBool gDefaultConverterContainsOption; - -#endif /* !U_CHARSET_IS_UTF8 */ - -static const char DATA_TYPE[] = "cnv"; - -/* ucnv_flushAvailableConverterCache. This is only called from ucnv_cleanup(). - * If it is ever to be called from elsewhere, synchronization - * will need to be considered. - */ -static void -ucnv_flushAvailableConverterCache() { - gAvailableConverterCount = 0; - if (gAvailableConverters) { - uprv_free((char **)gAvailableConverters); - gAvailableConverters = NULL; - } - gAvailableConvertersInitOnce.reset(); -} - -/* ucnv_cleanup - delete all storage held by the converter cache, except any */ -/* in use by open converters. */ -/* Not thread safe. */ -/* Not supported API. */ -static UBool U_CALLCONV ucnv_cleanup(void) { - ucnv_flushCache(); - if (SHARED_DATA_HASHTABLE != NULL && uhash_count(SHARED_DATA_HASHTABLE) == 0) { - uhash_close(SHARED_DATA_HASHTABLE); - SHARED_DATA_HASHTABLE = NULL; - } - - /* Isn't called from flushCache because other threads may have preexisting references to the table. */ - ucnv_flushAvailableConverterCache(); - -#if !U_CHARSET_IS_UTF8 - gDefaultConverterName = NULL; - gDefaultConverterNameBuffer[0] = 0; - gDefaultConverterContainsOption = false; - gDefaultAlgorithmicSharedData = NULL; -#endif - - return (SHARED_DATA_HASHTABLE == NULL); -} - -U_CAPI void U_EXPORT2 -ucnv_enableCleanup(void) { - ucln_common_registerCleanup(UCLN_COMMON_UCNV, ucnv_cleanup); -} - -static UBool U_CALLCONV -isCnvAcceptable(void * /*context*/, - const char * /*type*/, const char * /*name*/, - const UDataInfo *pInfo) { - return (UBool)( - pInfo->size>=20 && - pInfo->isBigEndian==U_IS_BIG_ENDIAN && - pInfo->charsetFamily==U_CHARSET_FAMILY && - pInfo->sizeofUChar==U_SIZEOF_UCHAR && - pInfo->dataFormat[0]==0x63 && /* dataFormat="cnvt" */ - pInfo->dataFormat[1]==0x6e && - pInfo->dataFormat[2]==0x76 && - pInfo->dataFormat[3]==0x74 && - pInfo->formatVersion[0]==6); /* Everything will be version 6 */ -} - -/** - * Un flatten shared data from a UDATA.. - */ -static UConverterSharedData* -ucnv_data_unFlattenClone(UConverterLoadArgs *pArgs, UDataMemory *pData, UErrorCode *status) -{ - /* UDataInfo info; -- necessary only if some converters have different formatVersion */ - const uint8_t *raw = (const uint8_t *)udata_getMemory(pData); - const UConverterStaticData *source = (const UConverterStaticData *) raw; - UConverterSharedData *data; - UConverterType type = (UConverterType)source->conversionType; - - if(U_FAILURE(*status)) - return NULL; - - if( (uint16_t)type >= UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES || - converterData[type] == NULL || - !converterData[type]->isReferenceCounted || - converterData[type]->referenceCounter != 1 || - source->structSize != sizeof(UConverterStaticData)) - { - *status = U_INVALID_TABLE_FORMAT; - return NULL; - } - - data = (UConverterSharedData *)uprv_malloc(sizeof(UConverterSharedData)); - if(data == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - /* copy initial values from the static structure for this type */ - uprv_memcpy(data, converterData[type], sizeof(UConverterSharedData)); - - data->staticData = source; - - data->sharedDataCached = false; - - /* fill in fields from the loaded data */ - data->dataMemory = (void*)pData; /* for future use */ - - if(data->impl->load != NULL) { - data->impl->load(data, pArgs, raw + source->structSize, status); - if(U_FAILURE(*status)) { - uprv_free(data); - return NULL; - } - } - return data; -} - -/*Takes an alias name gets an actual converter file name - *goes to disk and opens it. - *allocates the memory and returns a new UConverter object - */ -static UConverterSharedData *createConverterFromFile(UConverterLoadArgs *pArgs, UErrorCode * err) -{ - UDataMemory *data; - UConverterSharedData *sharedData; - - UTRACE_ENTRY_OC(UTRACE_UCNV_LOAD); - - if (U_FAILURE (*err)) { - UTRACE_EXIT_STATUS(*err); - return NULL; - } - - UTRACE_DATA2(UTRACE_OPEN_CLOSE, "load converter %s from package %s", pArgs->name, pArgs->pkg); - - data = udata_openChoice(pArgs->pkg, DATA_TYPE, pArgs->name, isCnvAcceptable, NULL, err); - if(U_FAILURE(*err)) - { - UTRACE_EXIT_STATUS(*err); - return NULL; - } - - sharedData = ucnv_data_unFlattenClone(pArgs, data, err); - if(U_FAILURE(*err)) - { - udata_close(data); - UTRACE_EXIT_STATUS(*err); - return NULL; - } - - /* - * TODO Store pkg in a field in the shared data so that delta-only converters - * can load base converters from the same package. - * If the pkg name is longer than the field, then either do not load the converter - * in the first place, or just set the pkg field to "". - */ - - UTRACE_EXIT_PTR_STATUS(sharedData, *err); - return sharedData; -} - -/*returns a converter type from a string - */ -static const UConverterSharedData * -getAlgorithmicTypeFromName(const char *realName) -{ - uint32_t mid, start, limit; - uint32_t lastMid; - int result; - char strippedName[UCNV_MAX_CONVERTER_NAME_LENGTH]; - - /* Lower case and remove ignoreable characters. */ - ucnv_io_stripForCompare(strippedName, realName); - - /* do a binary search for the alias */ - start = 0; - limit = UPRV_LENGTHOF(cnvNameType); - mid = limit; - lastMid = UINT32_MAX; - - for (;;) { - mid = (uint32_t)((start + limit) / 2); - if (lastMid == mid) { /* Have we moved? */ - break; /* We haven't moved, and it wasn't found. */ - } - lastMid = mid; - result = uprv_strcmp(strippedName, cnvNameType[mid].name); - - if (result < 0) { - limit = mid; - } else if (result > 0) { - start = mid; - } else { - return converterData[cnvNameType[mid].type]; - } - } - - return NULL; -} - -/* -* Based on the number of known converters, this determines how many times larger -* the shared data hash table should be. When on small platforms, or just a couple -* of converters are used, this number should be 2. When memory is plentiful, or -* when ucnv_countAvailable is ever used with a lot of available converters, -* this should be 4. -* Larger numbers reduce the number of hash collisions, but use more memory. -*/ -#define UCNV_CACHE_LOAD_FACTOR 2 - -/* Puts the shared data in the static hashtable SHARED_DATA_HASHTABLE */ -/* Will always be called with the cnvCacheMutex already being held */ -/* by the calling function. */ -/* Stores the shared data in the SHARED_DATA_HASHTABLE - * @param data The shared data - */ -static void -ucnv_shareConverterData(UConverterSharedData * data) -{ - UErrorCode err = U_ZERO_ERROR; - /*Lazy evaluates the Hashtable itself */ - /*void *sanity = NULL;*/ - - if (SHARED_DATA_HASHTABLE == NULL) - { - SHARED_DATA_HASHTABLE = uhash_openSize(uhash_hashChars, uhash_compareChars, NULL, - ucnv_io_countKnownConverters(&err)*UCNV_CACHE_LOAD_FACTOR, - &err); - ucnv_enableCleanup(); - - if (U_FAILURE(err)) - return; - } - - /* ### check to see if the element is not already there! */ - - /* - sanity = ucnv_getSharedConverterData (data->staticData->name); - if(sanity != NULL) - { - UCNV_DEBUG_LOG("put:overwrite!",data->staticData->name,sanity); - } - UCNV_DEBUG_LOG("put:chk",data->staticData->name,sanity); - */ - - /* Mark it shared */ - data->sharedDataCached = true; - - uhash_put(SHARED_DATA_HASHTABLE, - (void*) data->staticData->name, /* Okay to cast away const as long as - keyDeleter == NULL */ - data, - &err); - UCNV_DEBUG_LOG("put", data->staticData->name,data); - -} - -/* Look up a converter name in the shared data cache. */ -/* cnvCacheMutex must be held by the caller to protect the hash table. */ -/* gets the shared data from the SHARED_DATA_HASHTABLE (might return NULL if it isn't there) - * @param name The name of the shared data - * @return the shared data from the SHARED_DATA_HASHTABLE - */ -static UConverterSharedData * -ucnv_getSharedConverterData(const char *name) -{ - /*special case when no Table has yet been created we return NULL */ - if (SHARED_DATA_HASHTABLE == NULL) - { - return NULL; - } - else - { - UConverterSharedData *rc; - - rc = (UConverterSharedData*)uhash_get(SHARED_DATA_HASHTABLE, name); - UCNV_DEBUG_LOG("get",name,rc); - return rc; - } -} - -/*frees the string of memory blocks associates with a sharedConverter - *if and only if the referenceCounter == 0 - */ -/* Deletes (frees) the Shared data it's passed. first it checks the referenceCounter to - * see if anyone is using it, if not it frees all the memory stemming from sharedConverterData and - * returns true, - * otherwise returns false - * @param sharedConverterData The shared data - * @return if not it frees all the memory stemming from sharedConverterData and - * returns true, otherwise returns false - */ -static UBool -ucnv_deleteSharedConverterData(UConverterSharedData * deadSharedData) -{ - UTRACE_ENTRY_OC(UTRACE_UCNV_UNLOAD); - UTRACE_DATA2(UTRACE_OPEN_CLOSE, "unload converter %s shared data %p", deadSharedData->staticData->name, deadSharedData); - - if (deadSharedData->referenceCounter > 0) { - UTRACE_EXIT_VALUE((int32_t)false); - return false; - } - - if (deadSharedData->impl->unload != NULL) { - deadSharedData->impl->unload(deadSharedData); - } - - if(deadSharedData->dataMemory != NULL) - { - UDataMemory *data = (UDataMemory*)deadSharedData->dataMemory; - udata_close(data); - } - - uprv_free(deadSharedData); - - UTRACE_EXIT_VALUE((int32_t)true); - return true; -} - -/** - * Load a non-algorithmic converter. - * If pkg==NULL, then this function must be called inside umtx_lock(&cnvCacheMutex). - */ -UConverterSharedData * -ucnv_load(UConverterLoadArgs *pArgs, UErrorCode *err) { - UConverterSharedData *mySharedConverterData; - - if(err == NULL || U_FAILURE(*err)) { - return NULL; - } - - if(pArgs->pkg != NULL && *pArgs->pkg != 0) { - /* application-provided converters are not currently cached */ - return createConverterFromFile(pArgs, err); - } - - mySharedConverterData = ucnv_getSharedConverterData(pArgs->name); - if (mySharedConverterData == NULL) - { - /*Not cached, we need to stream it in from file */ - mySharedConverterData = createConverterFromFile(pArgs, err); - if (U_FAILURE (*err) || (mySharedConverterData == NULL)) - { - return NULL; - } - else if (!pArgs->onlyTestIsLoadable) - { - /* share it with other library clients */ - ucnv_shareConverterData(mySharedConverterData); - } - } - else - { - /* The data for this converter was already in the cache. */ - /* Update the reference counter on the shared data: one more client */ - mySharedConverterData->referenceCounter++; - } - - return mySharedConverterData; -} - -/** - * Unload a non-algorithmic converter. - * It must be sharedData->isReferenceCounted - * and this function must be called inside umtx_lock(&cnvCacheMutex). - */ -U_CAPI void -ucnv_unload(UConverterSharedData *sharedData) { - if(sharedData != NULL) { - if (sharedData->referenceCounter > 0) { - sharedData->referenceCounter--; - } - - if((sharedData->referenceCounter <= 0)&&(sharedData->sharedDataCached == false)) { - ucnv_deleteSharedConverterData(sharedData); - } - } -} - -U_CFUNC void -ucnv_unloadSharedDataIfReady(UConverterSharedData *sharedData) -{ - if(sharedData != NULL && sharedData->isReferenceCounted) { - umtx_lock(&cnvCacheMutex); - ucnv_unload(sharedData); - umtx_unlock(&cnvCacheMutex); - } -} - -U_CFUNC void -ucnv_incrementRefCount(UConverterSharedData *sharedData) -{ - if(sharedData != NULL && sharedData->isReferenceCounted) { - umtx_lock(&cnvCacheMutex); - sharedData->referenceCounter++; - umtx_unlock(&cnvCacheMutex); - } -} - -/* - * *pPieces must be initialized. - * The name without options will be copied to pPieces->cnvName. - * The locale and options will be copied to pPieces only if present in inName, - * otherwise the existing values in pPieces remain. - * *pArgs will be set to the pPieces values. - */ -static void -parseConverterOptions(const char *inName, - UConverterNamePieces *pPieces, - UConverterLoadArgs *pArgs, - UErrorCode *err) -{ - char *cnvName = pPieces->cnvName; - char c; - int32_t len = 0; - - pArgs->name=inName; - pArgs->locale=pPieces->locale; - pArgs->options=pPieces->options; - - /* copy the converter name itself to cnvName */ - while((c=*inName)!=0 && c!=UCNV_OPTION_SEP_CHAR) { - if (++len>=UCNV_MAX_CONVERTER_NAME_LENGTH) { - *err = U_ILLEGAL_ARGUMENT_ERROR; /* bad name */ - pPieces->cnvName[0]=0; - return; - } - *cnvName++=c; - inName++; - } - *cnvName=0; - pArgs->name=pPieces->cnvName; - - /* parse options. No more name copying should occur. */ - while((c=*inName)!=0) { - if(c==UCNV_OPTION_SEP_CHAR) { - ++inName; - } - - /* inName is behind an option separator */ - if(uprv_strncmp(inName, "locale=", 7)==0) { - /* do not modify locale itself in case we have multiple locale options */ - char *dest=pPieces->locale; - - /* copy the locale option value */ - inName+=7; - len=0; - while((c=*inName)!=0 && c!=UCNV_OPTION_SEP_CHAR) { - ++inName; - - if(++len>=ULOC_FULLNAME_CAPACITY) { - *err=U_ILLEGAL_ARGUMENT_ERROR; /* bad name */ - pPieces->locale[0]=0; - return; - } - - *dest++=c; - } - *dest=0; - } else if(uprv_strncmp(inName, "version=", 8)==0) { - /* copy the version option value into bits 3..0 of pPieces->options */ - inName+=8; - c=*inName; - if(c==0) { - pArgs->options=(pPieces->options&=~UCNV_OPTION_VERSION); - return; - } else if((uint8_t)(c-'0')<10) { - pArgs->options=pPieces->options=(pPieces->options&~UCNV_OPTION_VERSION)|(uint32_t)(c-'0'); - ++inName; - } - } else if(uprv_strncmp(inName, "swaplfnl", 8)==0) { - inName+=8; - pArgs->options=(pPieces->options|=UCNV_OPTION_SWAP_LFNL); - /* add processing for new options here with another } else if(uprv_strncmp(inName, "option-name=", XX)==0) { */ - } else { - /* ignore any other options until we define some */ - while(((c = *inName++) != 0) && (c != UCNV_OPTION_SEP_CHAR)) { - } - if(c==0) { - return; - } - } - } -} - -/*Logic determines if the converter is Algorithmic AND/OR cached - *depending on that: - * -we either go to get data from disk and cache it (Data=true, Cached=false) - * -Get it from a Hashtable (Data=X, Cached=true) - * -Call dataConverter initializer (Data=true, Cached=true) - * -Call AlgorithmicConverter initializer (Data=false, Cached=true) - */ -U_CFUNC UConverterSharedData * -ucnv_loadSharedData(const char *converterName, - UConverterNamePieces *pPieces, - UConverterLoadArgs *pArgs, - UErrorCode * err) { - UConverterNamePieces stackPieces; - UConverterLoadArgs stackArgs; - UConverterSharedData *mySharedConverterData = NULL; - UErrorCode internalErrorCode = U_ZERO_ERROR; - UBool mayContainOption = true; - UBool checkForAlgorithmic = true; - - if (U_FAILURE (*err)) { - return NULL; - } - - if(pPieces == NULL) { - if(pArgs != NULL) { - /* - * Bad: We may set pArgs pointers to stackPieces fields - * which will be invalid after this function returns. - */ - *err = U_INTERNAL_PROGRAM_ERROR; - return NULL; - } - pPieces = &stackPieces; - } - if(pArgs == NULL) { - uprv_memset(&stackArgs, 0, sizeof(stackArgs)); - stackArgs.size = (int32_t)sizeof(stackArgs); - pArgs = &stackArgs; - } - - pPieces->cnvName[0] = 0; - pPieces->locale[0] = 0; - pPieces->options = 0; - - pArgs->name = converterName; - pArgs->locale = pPieces->locale; - pArgs->options = pPieces->options; - - /* In case "name" is NULL we want to open the default converter. */ - if (converterName == NULL) { -#if U_CHARSET_IS_UTF8 - pArgs->name = "UTF-8"; - return (UConverterSharedData *)converterData[UCNV_UTF8]; -#else - /* Call ucnv_getDefaultName first to query the name from the OS. */ - pArgs->name = ucnv_getDefaultName(); - if (pArgs->name == NULL) { - *err = U_MISSING_RESOURCE_ERROR; - return NULL; - } - mySharedConverterData = (UConverterSharedData *)gDefaultAlgorithmicSharedData; - checkForAlgorithmic = false; - mayContainOption = gDefaultConverterContainsOption; - /* the default converter name is already canonical */ -#endif - } - else if(UCNV_FAST_IS_UTF8(converterName)) { - /* fastpath for UTF-8 */ - pArgs->name = "UTF-8"; - return (UConverterSharedData *)converterData[UCNV_UTF8]; - } - else { - /* separate the converter name from the options */ - parseConverterOptions(converterName, pPieces, pArgs, err); - if (U_FAILURE(*err)) { - /* Very bad name used. */ - return NULL; - } - - /* get the canonical converter name */ - pArgs->name = ucnv_io_getConverterName(pArgs->name, &mayContainOption, &internalErrorCode); - if (U_FAILURE(internalErrorCode) || pArgs->name == NULL) { - /* - * set the input name in case the converter was added - * without updating the alias table, or when there is no alias table - */ - pArgs->name = pPieces->cnvName; - } else if (internalErrorCode == U_AMBIGUOUS_ALIAS_WARNING) { - *err = U_AMBIGUOUS_ALIAS_WARNING; - } - } - - /* separate the converter name from the options */ - if(mayContainOption && pArgs->name != pPieces->cnvName) { - parseConverterOptions(pArgs->name, pPieces, pArgs, err); - } - - /* get the shared data for an algorithmic converter, if it is one */ - if (checkForAlgorithmic) { - mySharedConverterData = (UConverterSharedData *)getAlgorithmicTypeFromName(pArgs->name); - } - if (mySharedConverterData == NULL) - { - /* it is a data-based converter, get its shared data. */ - /* Hold the cnvCacheMutex through the whole process of checking the */ - /* converter data cache, and adding new entries to the cache */ - /* to prevent other threads from modifying the cache during the */ - /* process. */ - pArgs->nestedLoads=1; - pArgs->pkg=NULL; - - umtx_lock(&cnvCacheMutex); - mySharedConverterData = ucnv_load(pArgs, err); - umtx_unlock(&cnvCacheMutex); - if (U_FAILURE (*err) || (mySharedConverterData == NULL)) - { - return NULL; - } - } - - return mySharedConverterData; -} - -U_CAPI UConverter * -ucnv_createConverter(UConverter *myUConverter, const char *converterName, UErrorCode * err) -{ - UConverterNamePieces stackPieces; - UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; - UConverterSharedData *mySharedConverterData; - - UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN); - - if(U_SUCCESS(*err)) { - UTRACE_DATA1(UTRACE_OPEN_CLOSE, "open converter %s", converterName); - - mySharedConverterData = ucnv_loadSharedData(converterName, &stackPieces, &stackArgs, err); - - myUConverter = ucnv_createConverterFromSharedData( - myUConverter, mySharedConverterData, - &stackArgs, - err); - - if(U_SUCCESS(*err)) { - UTRACE_EXIT_PTR_STATUS(myUConverter, *err); - return myUConverter; - } - } - - /* exit with error */ - UTRACE_EXIT_STATUS(*err); - return NULL; -} - -U_CFUNC UBool -ucnv_canCreateConverter(const char *converterName, UErrorCode *err) { - UConverter myUConverter; - UConverterNamePieces stackPieces; - UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; - UConverterSharedData *mySharedConverterData; - - UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN); - - if(U_SUCCESS(*err)) { - UTRACE_DATA1(UTRACE_OPEN_CLOSE, "test if can open converter %s", converterName); - - stackArgs.onlyTestIsLoadable=true; - mySharedConverterData = ucnv_loadSharedData(converterName, &stackPieces, &stackArgs, err); - ucnv_createConverterFromSharedData( - &myUConverter, mySharedConverterData, - &stackArgs, - err); - ucnv_unloadSharedDataIfReady(mySharedConverterData); - } - - UTRACE_EXIT_STATUS(*err); - return U_SUCCESS(*err); -} - -UConverter * -ucnv_createAlgorithmicConverter(UConverter *myUConverter, - UConverterType type, - const char *locale, uint32_t options, - UErrorCode *err) { - UConverter *cnv; - const UConverterSharedData *sharedData; - UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; - - UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN_ALGORITHMIC); - UTRACE_DATA1(UTRACE_OPEN_CLOSE, "open algorithmic converter type %d", (int32_t)type); - - if(type<0 || UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES<=type) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - UTRACE_EXIT_STATUS(U_ILLEGAL_ARGUMENT_ERROR); - return NULL; - } - - sharedData = converterData[type]; - if(sharedData == NULL || sharedData->isReferenceCounted) { - /* not a valid type, or not an algorithmic converter */ - *err = U_ILLEGAL_ARGUMENT_ERROR; - UTRACE_EXIT_STATUS(U_ILLEGAL_ARGUMENT_ERROR); - return NULL; - } - - stackArgs.name = ""; - stackArgs.options = options; - stackArgs.locale=locale; - cnv = ucnv_createConverterFromSharedData( - myUConverter, (UConverterSharedData *)sharedData, - &stackArgs, err); - - UTRACE_EXIT_PTR_STATUS(cnv, *err); - return cnv; -} - -U_CFUNC UConverter* -ucnv_createConverterFromPackage(const char *packageName, const char *converterName, UErrorCode * err) -{ - UConverter *myUConverter; - UConverterSharedData *mySharedConverterData; - UConverterNamePieces stackPieces; - UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; - - UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN_PACKAGE); - - if(U_FAILURE(*err)) { - UTRACE_EXIT_STATUS(*err); - return NULL; - } - - UTRACE_DATA2(UTRACE_OPEN_CLOSE, "open converter %s from package %s", converterName, packageName); - - /* first, get the options out of the converterName string */ - stackPieces.cnvName[0] = 0; - stackPieces.locale[0] = 0; - stackPieces.options = 0; - parseConverterOptions(converterName, &stackPieces, &stackArgs, err); - if (U_FAILURE(*err)) { - /* Very bad name used. */ - UTRACE_EXIT_STATUS(*err); - return NULL; - } - stackArgs.nestedLoads=1; - stackArgs.pkg=packageName; - - /* open the data, unflatten the shared structure */ - mySharedConverterData = createConverterFromFile(&stackArgs, err); - - if (U_FAILURE(*err)) { - UTRACE_EXIT_STATUS(*err); - return NULL; - } - - /* create the actual converter */ - myUConverter = ucnv_createConverterFromSharedData(NULL, mySharedConverterData, &stackArgs, err); - - if (U_FAILURE(*err)) { - ucnv_close(myUConverter); - UTRACE_EXIT_STATUS(*err); - return NULL; - } - - UTRACE_EXIT_PTR_STATUS(myUConverter, *err); - return myUConverter; -} - - -U_CFUNC UConverter* -ucnv_createConverterFromSharedData(UConverter *myUConverter, - UConverterSharedData *mySharedConverterData, - UConverterLoadArgs *pArgs, - UErrorCode *err) -{ - UBool isCopyLocal; - - if(U_FAILURE(*err)) { - ucnv_unloadSharedDataIfReady(mySharedConverterData); - return myUConverter; - } - if(myUConverter == NULL) - { - myUConverter = (UConverter *) uprv_malloc (sizeof (UConverter)); - if(myUConverter == NULL) - { - *err = U_MEMORY_ALLOCATION_ERROR; - ucnv_unloadSharedDataIfReady(mySharedConverterData); - return NULL; - } - isCopyLocal = false; - } else { - isCopyLocal = true; - } - - /* initialize the converter */ - uprv_memset(myUConverter, 0, sizeof(UConverter)); - myUConverter->isCopyLocal = isCopyLocal; - /*myUConverter->isExtraLocal = false;*/ /* Set by the memset call */ - myUConverter->sharedData = mySharedConverterData; - myUConverter->options = pArgs->options; - if(!pArgs->onlyTestIsLoadable) { - myUConverter->preFromUFirstCP = U_SENTINEL; - myUConverter->fromCharErrorBehaviour = UCNV_TO_U_DEFAULT_CALLBACK; - myUConverter->fromUCharErrorBehaviour = UCNV_FROM_U_DEFAULT_CALLBACK; - myUConverter->toUnicodeStatus = mySharedConverterData->toUnicodeStatus; - myUConverter->maxBytesPerUChar = mySharedConverterData->staticData->maxBytesPerChar; - myUConverter->subChar1 = mySharedConverterData->staticData->subChar1; - myUConverter->subCharLen = mySharedConverterData->staticData->subCharLen; - myUConverter->subChars = (uint8_t *)myUConverter->subUChars; - uprv_memcpy(myUConverter->subChars, mySharedConverterData->staticData->subChar, myUConverter->subCharLen); - myUConverter->toUCallbackReason = UCNV_ILLEGAL; /* default reason to invoke (*fromCharErrorBehaviour) */ - } - - if(mySharedConverterData->impl->open != NULL) { - mySharedConverterData->impl->open(myUConverter, pArgs, err); - if(U_FAILURE(*err) && !pArgs->onlyTestIsLoadable) { - /* don't ucnv_close() if onlyTestIsLoadable because not fully initialized */ - ucnv_close(myUConverter); - return NULL; - } - } - - return myUConverter; -} - -/*Frees all shared immutable objects that aren't referred to (reference count = 0) - */ -U_CAPI int32_t U_EXPORT2 -ucnv_flushCache () -{ - UConverterSharedData *mySharedData = NULL; - int32_t pos; - int32_t tableDeletedNum = 0; - const UHashElement *e; - /*UErrorCode status = U_ILLEGAL_ARGUMENT_ERROR;*/ - int32_t i, remaining; - - UTRACE_ENTRY_OC(UTRACE_UCNV_FLUSH_CACHE); - - /* Close the default converter without creating a new one so that everything will be flushed. */ - u_flushDefaultConverter(); - - /*if shared data hasn't even been lazy evaluated yet - * return 0 - */ - if (SHARED_DATA_HASHTABLE == NULL) { - UTRACE_EXIT_VALUE((int32_t)0); - return 0; - } - - /*creates an enumeration to iterate through every element in the - * table - * - * Synchronization: holding cnvCacheMutex will prevent any other thread from - * accessing or modifying the hash table during the iteration. - * The reference count of an entry may be decremented by - * ucnv_close while the iteration is in process, but this is - * benign. It can't be incremented (in ucnv_createConverter()) - * because the sequence of looking up in the cache + incrementing - * is protected by cnvCacheMutex. - */ - umtx_lock(&cnvCacheMutex); - /* - * double loop: A delta/extension-only converter has a pointer to its base table's - * shared data; the first iteration of the outer loop may see the delta converter - * before the base converter, and unloading the delta converter may get the base - * converter's reference counter down to 0. - */ - i = 0; - do { - remaining = 0; - pos = UHASH_FIRST; - while ((e = uhash_nextElement (SHARED_DATA_HASHTABLE, &pos)) != NULL) - { - mySharedData = (UConverterSharedData *) e->value.pointer; - /*deletes only if reference counter == 0 */ - if (mySharedData->referenceCounter == 0) - { - tableDeletedNum++; - - UCNV_DEBUG_LOG("del",mySharedData->staticData->name,mySharedData); - - uhash_removeElement(SHARED_DATA_HASHTABLE, e); - mySharedData->sharedDataCached = false; - ucnv_deleteSharedConverterData (mySharedData); - } else { - ++remaining; - } - } - } while(++i == 1 && remaining > 0); - umtx_unlock(&cnvCacheMutex); - - UTRACE_DATA1(UTRACE_INFO, "ucnv_flushCache() exits with %d converters remaining", remaining); - - UTRACE_EXIT_VALUE(tableDeletedNum); - return tableDeletedNum; -} - -/* available converters list --------------------------------------------------- */ - -static void U_CALLCONV initAvailableConvertersList(UErrorCode &errCode) { - U_ASSERT(gAvailableConverterCount == 0); - U_ASSERT(gAvailableConverters == NULL); - - ucnv_enableCleanup(); - UEnumeration *allConvEnum = ucnv_openAllNames(&errCode); - int32_t allConverterCount = uenum_count(allConvEnum, &errCode); - if (U_FAILURE(errCode)) { - return; - } - - /* We can't have more than "*converterTable" converters to open */ - gAvailableConverters = (const char **) uprv_malloc(allConverterCount * sizeof(char*)); - if (!gAvailableConverters) { - errCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - - /* Open the default converter to make sure that it has first dibs in the hash table. */ - UErrorCode localStatus = U_ZERO_ERROR; - UConverter tempConverter; - ucnv_close(ucnv_createConverter(&tempConverter, NULL, &localStatus)); - - gAvailableConverterCount = 0; - - for (int32_t idx = 0; idx < allConverterCount; idx++) { - localStatus = U_ZERO_ERROR; - const char *converterName = uenum_next(allConvEnum, NULL, &localStatus); - if (ucnv_canCreateConverter(converterName, &localStatus)) { - gAvailableConverters[gAvailableConverterCount++] = converterName; - } - } - - uenum_close(allConvEnum); -} - - -static UBool haveAvailableConverterList(UErrorCode *pErrorCode) { - umtx_initOnce(gAvailableConvertersInitOnce, &initAvailableConvertersList, *pErrorCode); - return U_SUCCESS(*pErrorCode); -} - -U_CFUNC uint16_t -ucnv_bld_countAvailableConverters(UErrorCode *pErrorCode) { - if (haveAvailableConverterList(pErrorCode)) { - return gAvailableConverterCount; - } - return 0; -} - -U_CFUNC const char * -ucnv_bld_getAvailableConverter(uint16_t n, UErrorCode *pErrorCode) { - if (haveAvailableConverterList(pErrorCode)) { - if (n < gAvailableConverterCount) { - return gAvailableConverters[n]; - } - *pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR; - } - return NULL; -} - -/* default converter name --------------------------------------------------- */ - -#if !U_CHARSET_IS_UTF8 -/* -Copy the canonical converter name. -ucnv_getDefaultName must be thread safe, which can call this function. - -ucnv_setDefaultName calls this function and it doesn't have to be -thread safe because there is no reliable/safe way to reset the -converter in use in all threads. If you did reset the converter, you -would not be sure that retrieving a default converter for one string -would be the same type of default converter for a successive string. -Since the name is a returned via ucnv_getDefaultName without copying, -you shouldn't be modifying or deleting the string from a separate thread. -*/ -static inline void -internalSetName(const char *name, UErrorCode *status) { - UConverterNamePieces stackPieces; - UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; - int32_t length=(int32_t)(uprv_strlen(name)); - UBool containsOption = (UBool)(uprv_strchr(name, UCNV_OPTION_SEP_CHAR) != NULL); - const UConverterSharedData *algorithmicSharedData; - - stackArgs.name = name; - if(containsOption) { - stackPieces.cnvName[0] = 0; - stackPieces.locale[0] = 0; - stackPieces.options = 0; - parseConverterOptions(name, &stackPieces, &stackArgs, status); - if(U_FAILURE(*status)) { - return; - } - } - algorithmicSharedData = getAlgorithmicTypeFromName(stackArgs.name); - - umtx_lock(&cnvCacheMutex); - - gDefaultAlgorithmicSharedData = algorithmicSharedData; - gDefaultConverterContainsOption = containsOption; - uprv_memcpy(gDefaultConverterNameBuffer, name, length); - gDefaultConverterNameBuffer[length]=0; - - /* gDefaultConverterName MUST be the last global var set by this function. */ - /* It is the variable checked in ucnv_getDefaultName() to see if initialization is required. */ - // But there is nothing here preventing that from being reordered, either by the compiler - // or hardware. I'm adding the mutex to ucnv_getDefaultName for now. UMTX_CHECK is not enough. - // -- Andy - gDefaultConverterName = gDefaultConverterNameBuffer; - - ucnv_enableCleanup(); - - umtx_unlock(&cnvCacheMutex); -} -#endif - -/* - * In order to be really thread-safe, the get function would have to take - * a buffer parameter and copy the current string inside a mutex block. - * This implementation only tries to be really thread-safe while - * setting the name. - * It assumes that setting a pointer is atomic. - */ - -U_CAPI const char* U_EXPORT2 -ucnv_getDefaultName() { -#if U_CHARSET_IS_UTF8 - return "UTF-8"; -#else - /* local variable to be thread-safe */ - const char *name; - - /* - Concurrent calls to ucnv_getDefaultName must be thread safe, - but ucnv_setDefaultName is not thread safe. - */ - { - icu::Mutex lock(&cnvCacheMutex); - name = gDefaultConverterName; - } - if(name==NULL) { - UErrorCode errorCode = U_ZERO_ERROR; - UConverter *cnv = NULL; - - name = uprv_getDefaultCodepage(); - - /* if the name is there, test it out and get the canonical name with options */ - if(name != NULL) { - cnv = ucnv_open(name, &errorCode); - if(U_SUCCESS(errorCode) && cnv != NULL) { - name = ucnv_getName(cnv, &errorCode); - } - } - - if(name == NULL || name[0] == 0 - || U_FAILURE(errorCode) || cnv == NULL - || uprv_strlen(name)>=sizeof(gDefaultConverterNameBuffer)) - { - /* Panic time, let's use a fallback. */ -#if (U_CHARSET_FAMILY == U_ASCII_FAMILY) - name = "US-ASCII"; - /* there is no 'algorithmic' converter for EBCDIC */ -#elif U_PLATFORM == U_PF_OS390 - name = "ibm-1047_P100-1995" UCNV_SWAP_LFNL_OPTION_STRING; -#else - name = "ibm-37_P100-1995"; -#endif - } - - internalSetName(name, &errorCode); - - /* The close may make the current name go away. */ - ucnv_close(cnv); - } - - return name; -#endif -} - -#if U_CHARSET_IS_UTF8 -U_CAPI void U_EXPORT2 ucnv_setDefaultName(const char *) {} -#else -/* -This function is not thread safe, and it can't be thread safe. -See internalSetName or the API reference for details. -*/ -U_CAPI void U_EXPORT2 -ucnv_setDefaultName(const char *converterName) { - if(converterName==NULL) { - /* reset to the default codepage */ - gDefaultConverterName=NULL; - } else { - UErrorCode errorCode = U_ZERO_ERROR; - UConverter *cnv = NULL; - const char *name = NULL; - - /* if the name is there, test it out and get the canonical name with options */ - cnv = ucnv_open(converterName, &errorCode); - if(U_SUCCESS(errorCode) && cnv != NULL) { - name = ucnv_getName(cnv, &errorCode); - } - - if(U_SUCCESS(errorCode) && name!=NULL) { - internalSetName(name, &errorCode); - } - /* else this converter is bad to use. Don't change it to a bad value. */ - - /* The close may make the current name go away. */ - ucnv_close(cnv); - - /* reset the converter cache */ - u_flushDefaultConverter(); - } -} -#endif - -/* data swapping ------------------------------------------------------------ */ - -/* most of this might belong more properly into ucnvmbcs.c, but that is so large */ - -#if !UCONFIG_NO_LEGACY_CONVERSION - -U_CAPI int32_t U_EXPORT2 -ucnv_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const UDataInfo *pInfo; - int32_t headerSize; - - const uint8_t *inBytes; - uint8_t *outBytes; - - uint32_t offset, count, staticDataSize; - int32_t size; - - const UConverterStaticData *inStaticData; - UConverterStaticData *outStaticData; - - const _MBCSHeader *inMBCSHeader; - _MBCSHeader *outMBCSHeader; - _MBCSHeader mbcsHeader; - uint32_t mbcsHeaderLength; - UBool noFromU=false; - - uint8_t outputType; - - int32_t maxFastUChar, mbcsIndexLength; - - const int32_t *inExtIndexes; - int32_t extOffset; - - /* udata_swapDataHeader checks the arguments */ - headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - /* check data format and format version */ - pInfo=(const UDataInfo *)((const char *)inData+4); - if(!( - pInfo->dataFormat[0]==0x63 && /* dataFormat="cnvt" */ - pInfo->dataFormat[1]==0x6e && - pInfo->dataFormat[2]==0x76 && - pInfo->dataFormat[3]==0x74 && - pInfo->formatVersion[0]==6 && - pInfo->formatVersion[1]>=2 - )) { - udata_printError(ds, "ucnv_swap(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not recognized as an ICU .cnv conversion table\n", - pInfo->dataFormat[0], pInfo->dataFormat[1], - pInfo->dataFormat[2], pInfo->dataFormat[3], - pInfo->formatVersion[0], pInfo->formatVersion[1]); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - inBytes=(const uint8_t *)inData+headerSize; - outBytes=(uint8_t *)outData+headerSize; - - /* read the initial UConverterStaticData structure after the UDataInfo header */ - inStaticData=(const UConverterStaticData *)inBytes; - outStaticData=(UConverterStaticData *)outBytes; - - if(length<0) { - staticDataSize=ds->readUInt32(inStaticData->structSize); - } else { - length-=headerSize; - if( length<(int32_t)sizeof(UConverterStaticData) || - (uint32_t)length<(staticDataSize=ds->readUInt32(inStaticData->structSize)) - ) { - udata_printError(ds, "ucnv_swap(): too few bytes (%d after header) for an ICU .cnv conversion table\n", - length); - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - } - - if(length>=0) { - /* swap the static data */ - if(inStaticData!=outStaticData) { - uprv_memcpy(outStaticData, inStaticData, staticDataSize); - } - - ds->swapArray32(ds, &inStaticData->structSize, 4, - &outStaticData->structSize, pErrorCode); - ds->swapArray32(ds, &inStaticData->codepage, 4, - &outStaticData->codepage, pErrorCode); - - ds->swapInvChars(ds, inStaticData->name, (int32_t)uprv_strlen(inStaticData->name), - outStaticData->name, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - udata_printError(ds, "ucnv_swap(): error swapping converter name\n"); - return 0; - } - } - - inBytes+=staticDataSize; - outBytes+=staticDataSize; - if(length>=0) { - length-=(int32_t)staticDataSize; - } - - /* check for supported conversionType values */ - if(inStaticData->conversionType==UCNV_MBCS) { - /* swap MBCS data */ - inMBCSHeader=(const _MBCSHeader *)inBytes; - outMBCSHeader=(_MBCSHeader *)outBytes; - - if(0<=length && length<(int32_t)sizeof(_MBCSHeader)) { - udata_printError(ds, "ucnv_swap(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table\n", - length); - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - if(inMBCSHeader->version[0]==4 && inMBCSHeader->version[1]>=1) { - mbcsHeaderLength=MBCS_HEADER_V4_LENGTH; - } else if(inMBCSHeader->version[0]==5 && inMBCSHeader->version[1]>=3 && - ((mbcsHeader.options=ds->readUInt32(inMBCSHeader->options))& - MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK)==0 - ) { - mbcsHeaderLength=mbcsHeader.options&MBCS_OPT_LENGTH_MASK; - noFromU=(UBool)((mbcsHeader.options&MBCS_OPT_NO_FROM_U)!=0); - } else { - udata_printError(ds, "ucnv_swap(): unsupported _MBCSHeader.version %d.%d\n", - inMBCSHeader->version[0], inMBCSHeader->version[1]); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - uprv_memcpy(mbcsHeader.version, inMBCSHeader->version, 4); - mbcsHeader.countStates= ds->readUInt32(inMBCSHeader->countStates); - mbcsHeader.countToUFallbacks= ds->readUInt32(inMBCSHeader->countToUFallbacks); - mbcsHeader.offsetToUCodeUnits= ds->readUInt32(inMBCSHeader->offsetToUCodeUnits); - mbcsHeader.offsetFromUTable= ds->readUInt32(inMBCSHeader->offsetFromUTable); - mbcsHeader.offsetFromUBytes= ds->readUInt32(inMBCSHeader->offsetFromUBytes); - mbcsHeader.flags= ds->readUInt32(inMBCSHeader->flags); - mbcsHeader.fromUBytesLength= ds->readUInt32(inMBCSHeader->fromUBytesLength); - /* mbcsHeader.options have been read above */ - - extOffset=(int32_t)(mbcsHeader.flags>>8); - outputType=(uint8_t)mbcsHeader.flags; - if(noFromU && outputType==MBCS_OUTPUT_1) { - udata_printError(ds, "ucnv_swap(): unsupported combination of makeconv --small with SBCS\n"); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - /* make sure that the output type is known */ - switch(outputType) { - case MBCS_OUTPUT_1: - case MBCS_OUTPUT_2: - case MBCS_OUTPUT_3: - case MBCS_OUTPUT_4: - case MBCS_OUTPUT_3_EUC: - case MBCS_OUTPUT_4_EUC: - case MBCS_OUTPUT_2_SISO: - case MBCS_OUTPUT_EXT_ONLY: - /* OK */ - break; - default: - udata_printError(ds, "ucnv_swap(): unsupported MBCS output type 0x%x\n", - outputType); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - /* calculate the length of the MBCS data */ - - /* - * utf8Friendly MBCS files (mbcsHeader.version 4.3) - * contain an additional mbcsIndex table: - * uint16_t[(maxFastUChar+1)>>6]; - * where maxFastUChar=((mbcsHeader.version[2]<<8)|0xff). - */ - maxFastUChar=0; - mbcsIndexLength=0; - if( outputType!=MBCS_OUTPUT_EXT_ONLY && outputType!=MBCS_OUTPUT_1 && - mbcsHeader.version[1]>=3 && (maxFastUChar=mbcsHeader.version[2])!=0 - ) { - maxFastUChar=(maxFastUChar<<8)|0xff; - mbcsIndexLength=((maxFastUChar+1)>>6)*2; /* number of bytes */ - } - - if(extOffset==0) { - size=(int32_t)(mbcsHeader.offsetFromUBytes+mbcsIndexLength); - if(!noFromU) { - size+=(int32_t)mbcsHeader.fromUBytesLength; - } - - /* avoid compiler warnings - not otherwise necessary, and the value does not matter */ - inExtIndexes=NULL; - } else { - /* there is extension data after the base data, see ucnv_ext.h */ - if(length>=0 && length<(extOffset+UCNV_EXT_INDEXES_MIN_LENGTH*4)) { - udata_printError(ds, "ucnv_swap(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table with extension data\n", - length); - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - inExtIndexes=(const int32_t *)(inBytes+extOffset); - size=extOffset+udata_readInt32(ds, inExtIndexes[UCNV_EXT_SIZE]); - } - - if(length>=0) { - if(lengthswapArray32(ds, &inMBCSHeader->countStates, count-4, - &outMBCSHeader->countStates, pErrorCode); - - if(outputType==MBCS_OUTPUT_EXT_ONLY) { - /* - * extension-only file, - * contains a base name instead of normal base table data - */ - - /* swap the base name, between the header and the extension data */ - const char *inBaseName=(const char *)inBytes+count; - char *outBaseName=(char *)outBytes+count; - ds->swapInvChars(ds, inBaseName, (int32_t)uprv_strlen(inBaseName), - outBaseName, pErrorCode); - } else { - /* normal file with base table data */ - - /* swap the state table, 1kB per state */ - offset=count; - count=mbcsHeader.countStates*1024; - ds->swapArray32(ds, inBytes+offset, (int32_t)count, - outBytes+offset, pErrorCode); - - /* swap the toUFallbacks[] */ - offset+=count; - count=mbcsHeader.countToUFallbacks*8; - ds->swapArray32(ds, inBytes+offset, (int32_t)count, - outBytes+offset, pErrorCode); - - /* swap the unicodeCodeUnits[] */ - offset=mbcsHeader.offsetToUCodeUnits; - count=mbcsHeader.offsetFromUTable-offset; - ds->swapArray16(ds, inBytes+offset, (int32_t)count, - outBytes+offset, pErrorCode); - - /* offset to the stage 1 table, independent of the outputType */ - offset=mbcsHeader.offsetFromUTable; - - if(outputType==MBCS_OUTPUT_1) { - /* SBCS: swap the fromU tables, all 16 bits wide */ - count=(mbcsHeader.offsetFromUBytes-offset)+mbcsHeader.fromUBytesLength; - ds->swapArray16(ds, inBytes+offset, (int32_t)count, - outBytes+offset, pErrorCode); - } else { - /* otherwise: swap the stage tables separately */ - - /* stage 1 table: uint16_t[0x440 or 0x40] */ - if(inStaticData->unicodeMask&UCNV_HAS_SUPPLEMENTARY) { - count=0x440*2; /* for all of Unicode */ - } else { - count=0x40*2; /* only BMP */ - } - ds->swapArray16(ds, inBytes+offset, (int32_t)count, - outBytes+offset, pErrorCode); - - /* stage 2 table: uint32_t[] */ - offset+=count; - count=mbcsHeader.offsetFromUBytes-offset; - ds->swapArray32(ds, inBytes+offset, (int32_t)count, - outBytes+offset, pErrorCode); - - /* stage 3/result bytes: sometimes uint16_t[] or uint32_t[] */ - offset=mbcsHeader.offsetFromUBytes; - count= noFromU ? 0 : mbcsHeader.fromUBytesLength; - switch(outputType) { - case MBCS_OUTPUT_2: - case MBCS_OUTPUT_3_EUC: - case MBCS_OUTPUT_2_SISO: - ds->swapArray16(ds, inBytes+offset, (int32_t)count, - outBytes+offset, pErrorCode); - break; - case MBCS_OUTPUT_4: - ds->swapArray32(ds, inBytes+offset, (int32_t)count, - outBytes+offset, pErrorCode); - break; - default: - /* just uint8_t[], nothing to swap */ - break; - } - - if(mbcsIndexLength!=0) { - offset+=count; - count=mbcsIndexLength; - ds->swapArray16(ds, inBytes+offset, (int32_t)count, - outBytes+offset, pErrorCode); - } - } - } - - if(extOffset!=0) { - /* swap the extension data */ - inBytes+=extOffset; - outBytes+=extOffset; - - /* swap toUTable[] */ - offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_INDEX]); - length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_LENGTH]); - ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); - - /* swap toUUChars[] */ - offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_UCHARS_INDEX]); - length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_UCHARS_LENGTH]); - ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); - - /* swap fromUTableUChars[] */ - offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_UCHARS_INDEX]); - length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_LENGTH]); - ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); - - /* swap fromUTableValues[] */ - offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_VALUES_INDEX]); - /* same length as for fromUTableUChars[] */ - ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); - - /* no need to swap fromUBytes[] */ - - /* swap fromUStage12[] */ - offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_12_INDEX]); - length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_12_LENGTH]); - ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); - - /* swap fromUStage3[] */ - offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3_INDEX]); - length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3_LENGTH]); - ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); - - /* swap fromUStage3b[] */ - offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3B_INDEX]); - length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3B_LENGTH]); - ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); - - /* swap indexes[] */ - length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_INDEXES_LENGTH]); - ds->swapArray32(ds, inBytes, length*4, outBytes, pErrorCode); - } - } - } else { - udata_printError(ds, "ucnv_swap(): unknown conversionType=%d!=UCNV_MBCS\n", - inStaticData->conversionType); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - return headerSize+(int32_t)staticDataSize+size; -} - -#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************** + * COPYRIGHT: + * Copyright (c) 1996-2016, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************** + * + * ucnv_bld.cpp: + * + * Defines functions that are used in the creation/initialization/deletion + * of converters and related structures. + * uses uconv_io.h routines to access disk information + * is used by ucnv.h to implement public API create/delete/flushCache routines + * Modification History: + * + * Date Name Description + * + * 06/20/2000 helena OS/400 port changes; mostly typecast. + * 06/29/2000 helena Major rewrite of the callback interface. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/putil.h" +#include "unicode/udata.h" +#include "unicode/ucnv.h" +#include "unicode/uloc.h" +#include "mutex.h" +#include "putilimp.h" +#include "uassert.h" +#include "utracimp.h" +#include "ucnv_io.h" +#include "ucnv_bld.h" +#include "ucnvmbcs.h" +#include "ucnv_ext.h" +#include "ucnv_cnv.h" +#include "ucnv_imp.h" +#include "uhash.h" +#include "umutex.h" +#include "cstring.h" +#include "cmemory.h" +#include "ucln_cmn.h" +#include "ustr_cnv.h" + + +#if 0 +#include +extern void UCNV_DEBUG_LOG(char *what, char *who, void *p, int l); +#define UCNV_DEBUG_LOG(x,y,z) UCNV_DEBUG_LOG(x,y,z,__LINE__) +#else +# define UCNV_DEBUG_LOG(x,y,z) +#endif + +static const UConverterSharedData * const +converterData[UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES]={ + nullptr, nullptr, + +#if UCONFIG_NO_LEGACY_CONVERSION + nullptr, +#else + &_MBCSData, +#endif + + &_Latin1Data, + &_UTF8Data, &_UTF16BEData, &_UTF16LEData, +#if UCONFIG_ONLY_HTML_CONVERSION + nullptr, nullptr, +#else + &_UTF32BEData, &_UTF32LEData, +#endif + nullptr, + +#if UCONFIG_NO_LEGACY_CONVERSION + nullptr, +#else + &_ISO2022Data, +#endif + +#if UCONFIG_NO_LEGACY_CONVERSION || UCONFIG_ONLY_HTML_CONVERSION + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, +#else + &_LMBCSData1,&_LMBCSData2, &_LMBCSData3, &_LMBCSData4, &_LMBCSData5, &_LMBCSData6, + &_LMBCSData8,&_LMBCSData11,&_LMBCSData16,&_LMBCSData17,&_LMBCSData18,&_LMBCSData19, + &_HZData, +#endif + +#if UCONFIG_ONLY_HTML_CONVERSION + nullptr, +#else + &_SCSUData, +#endif + + +#if UCONFIG_NO_LEGACY_CONVERSION || UCONFIG_ONLY_HTML_CONVERSION + nullptr, +#else + &_ISCIIData, +#endif + + &_ASCIIData, +#if UCONFIG_ONLY_HTML_CONVERSION + nullptr, nullptr, &_UTF16Data, nullptr, nullptr, nullptr, +#else + &_UTF7Data, &_Bocu1Data, &_UTF16Data, &_UTF32Data, &_CESU8Data, &_IMAPData, +#endif + +#if UCONFIG_NO_LEGACY_CONVERSION || UCONFIG_ONLY_HTML_CONVERSION + nullptr, +#else + &_CompoundTextData +#endif +}; + +/* Please keep this in binary sorted order for getAlgorithmicTypeFromName. + Also the name should be in lower case and all spaces, dashes and underscores + removed +*/ +static struct { + const char *name; + const UConverterType type; +} const cnvNameType[] = { +#if !UCONFIG_ONLY_HTML_CONVERSION + { "bocu1", UCNV_BOCU1 }, + { "cesu8", UCNV_CESU8 }, +#endif +#if !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION + { "hz",UCNV_HZ }, +#endif +#if !UCONFIG_ONLY_HTML_CONVERSION + { "imapmailboxname", UCNV_IMAP_MAILBOX }, +#endif +#if !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION + { "iscii", UCNV_ISCII }, +#endif +#if !UCONFIG_NO_LEGACY_CONVERSION + { "iso2022", UCNV_ISO_2022 }, +#endif + { "iso88591", UCNV_LATIN_1 }, +#if !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION + { "lmbcs1", UCNV_LMBCS_1 }, + { "lmbcs11",UCNV_LMBCS_11 }, + { "lmbcs16",UCNV_LMBCS_16 }, + { "lmbcs17",UCNV_LMBCS_17 }, + { "lmbcs18",UCNV_LMBCS_18 }, + { "lmbcs19",UCNV_LMBCS_19 }, + { "lmbcs2", UCNV_LMBCS_2 }, + { "lmbcs3", UCNV_LMBCS_3 }, + { "lmbcs4", UCNV_LMBCS_4 }, + { "lmbcs5", UCNV_LMBCS_5 }, + { "lmbcs6", UCNV_LMBCS_6 }, + { "lmbcs8", UCNV_LMBCS_8 }, +#endif +#if !UCONFIG_ONLY_HTML_CONVERSION + { "scsu", UCNV_SCSU }, +#endif + { "usascii", UCNV_US_ASCII }, + { "utf16", UCNV_UTF16 }, + { "utf16be", UCNV_UTF16_BigEndian }, + { "utf16le", UCNV_UTF16_LittleEndian }, +#if U_IS_BIG_ENDIAN + { "utf16oppositeendian", UCNV_UTF16_LittleEndian }, + { "utf16platformendian", UCNV_UTF16_BigEndian }, +#else + { "utf16oppositeendian", UCNV_UTF16_BigEndian}, + { "utf16platformendian", UCNV_UTF16_LittleEndian }, +#endif +#if !UCONFIG_ONLY_HTML_CONVERSION + { "utf32", UCNV_UTF32 }, + { "utf32be", UCNV_UTF32_BigEndian }, + { "utf32le", UCNV_UTF32_LittleEndian }, +#if U_IS_BIG_ENDIAN + { "utf32oppositeendian", UCNV_UTF32_LittleEndian }, + { "utf32platformendian", UCNV_UTF32_BigEndian }, +#else + { "utf32oppositeendian", UCNV_UTF32_BigEndian }, + { "utf32platformendian", UCNV_UTF32_LittleEndian }, +#endif +#endif +#if !UCONFIG_ONLY_HTML_CONVERSION + { "utf7", UCNV_UTF7 }, +#endif + { "utf8", UCNV_UTF8 }, +#if !UCONFIG_ONLY_HTML_CONVERSION + { "x11compoundtext", UCNV_COMPOUND_TEXT} +#endif +}; + + +/*initializes some global variables */ +static UHashtable *SHARED_DATA_HASHTABLE = nullptr; +static icu::UMutex cnvCacheMutex; +/* Note: the global mutex is used for */ +/* reference count updates. */ + +static const char **gAvailableConverters = nullptr; +static uint16_t gAvailableConverterCount = 0; +static icu::UInitOnce gAvailableConvertersInitOnce {}; + +#if !U_CHARSET_IS_UTF8 + +/* This contains the resolved converter name. So no further alias lookup is needed again. */ +static char gDefaultConverterNameBuffer[UCNV_MAX_CONVERTER_NAME_LENGTH + 1]; /* +1 for nullptr */ +static const char *gDefaultConverterName = nullptr; + +/* +If the default converter is an algorithmic converter, this is the cached value. +We don't cache a full UConverter and clone it because ucnv_clone doesn't have +less overhead than an algorithmic open. We don't cache non-algorithmic converters +because ucnv_flushCache must be able to unload the default converter and its table. +*/ +static const UConverterSharedData *gDefaultAlgorithmicSharedData = nullptr; + +/* Does gDefaultConverterName have a converter option and require extra parsing? */ +static UBool gDefaultConverterContainsOption; + +#endif /* !U_CHARSET_IS_UTF8 */ + +static const char DATA_TYPE[] = "cnv"; + +/* ucnv_flushAvailableConverterCache. This is only called from ucnv_cleanup(). + * If it is ever to be called from elsewhere, synchronization + * will need to be considered. + */ +static void +ucnv_flushAvailableConverterCache() { + gAvailableConverterCount = 0; + if (gAvailableConverters) { + uprv_free((char **)gAvailableConverters); + gAvailableConverters = nullptr; + } + gAvailableConvertersInitOnce.reset(); +} + +/* ucnv_cleanup - delete all storage held by the converter cache, except any */ +/* in use by open converters. */ +/* Not thread safe. */ +/* Not supported API. */ +static UBool U_CALLCONV ucnv_cleanup() { + ucnv_flushCache(); + if (SHARED_DATA_HASHTABLE != nullptr && uhash_count(SHARED_DATA_HASHTABLE) == 0) { + uhash_close(SHARED_DATA_HASHTABLE); + SHARED_DATA_HASHTABLE = nullptr; + } + + /* Isn't called from flushCache because other threads may have preexisting references to the table. */ + ucnv_flushAvailableConverterCache(); + +#if !U_CHARSET_IS_UTF8 + gDefaultConverterName = nullptr; + gDefaultConverterNameBuffer[0] = 0; + gDefaultConverterContainsOption = false; + gDefaultAlgorithmicSharedData = nullptr; +#endif + + return (SHARED_DATA_HASHTABLE == nullptr); +} + +U_CAPI void U_EXPORT2 +ucnv_enableCleanup() { + ucln_common_registerCleanup(UCLN_COMMON_UCNV, ucnv_cleanup); +} + +static UBool U_CALLCONV +isCnvAcceptable(void * /*context*/, + const char * /*type*/, const char * /*name*/, + const UDataInfo *pInfo) { + return (UBool)( + pInfo->size>=20 && + pInfo->isBigEndian==U_IS_BIG_ENDIAN && + pInfo->charsetFamily==U_CHARSET_FAMILY && + pInfo->sizeofUChar==U_SIZEOF_UCHAR && + pInfo->dataFormat[0]==0x63 && /* dataFormat="cnvt" */ + pInfo->dataFormat[1]==0x6e && + pInfo->dataFormat[2]==0x76 && + pInfo->dataFormat[3]==0x74 && + pInfo->formatVersion[0]==6); /* Everything will be version 6 */ +} + +/** + * Un flatten shared data from a UDATA.. + */ +static UConverterSharedData* +ucnv_data_unFlattenClone(UConverterLoadArgs *pArgs, UDataMemory *pData, UErrorCode *status) +{ + /* UDataInfo info; -- necessary only if some converters have different formatVersion */ + const uint8_t *raw = (const uint8_t *)udata_getMemory(pData); + const UConverterStaticData *source = (const UConverterStaticData *) raw; + UConverterSharedData *data; + UConverterType type = (UConverterType)source->conversionType; + + if(U_FAILURE(*status)) + return nullptr; + + if( (uint16_t)type >= UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES || + converterData[type] == nullptr || + !converterData[type]->isReferenceCounted || + converterData[type]->referenceCounter != 1 || + source->structSize != sizeof(UConverterStaticData)) + { + *status = U_INVALID_TABLE_FORMAT; + return nullptr; + } + + data = (UConverterSharedData *)uprv_malloc(sizeof(UConverterSharedData)); + if(data == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + /* copy initial values from the static structure for this type */ + uprv_memcpy(data, converterData[type], sizeof(UConverterSharedData)); + + data->staticData = source; + + data->sharedDataCached = false; + + /* fill in fields from the loaded data */ + data->dataMemory = (void*)pData; /* for future use */ + + if(data->impl->load != nullptr) { + data->impl->load(data, pArgs, raw + source->structSize, status); + if(U_FAILURE(*status)) { + uprv_free(data); + return nullptr; + } + } + return data; +} + +/*Takes an alias name gets an actual converter file name + *goes to disk and opens it. + *allocates the memory and returns a new UConverter object + */ +static UConverterSharedData *createConverterFromFile(UConverterLoadArgs *pArgs, UErrorCode * err) +{ + UDataMemory *data; + UConverterSharedData *sharedData; + + UTRACE_ENTRY_OC(UTRACE_UCNV_LOAD); + + if (U_FAILURE (*err)) { + UTRACE_EXIT_STATUS(*err); + return nullptr; + } + + UTRACE_DATA2(UTRACE_OPEN_CLOSE, "load converter %s from package %s", pArgs->name, pArgs->pkg); + + data = udata_openChoice(pArgs->pkg, DATA_TYPE, pArgs->name, isCnvAcceptable, nullptr, err); + if(U_FAILURE(*err)) + { + UTRACE_EXIT_STATUS(*err); + return nullptr; + } + + sharedData = ucnv_data_unFlattenClone(pArgs, data, err); + if(U_FAILURE(*err)) + { + udata_close(data); + UTRACE_EXIT_STATUS(*err); + return nullptr; + } + + /* + * TODO Store pkg in a field in the shared data so that delta-only converters + * can load base converters from the same package. + * If the pkg name is longer than the field, then either do not load the converter + * in the first place, or just set the pkg field to "". + */ + + UTRACE_EXIT_PTR_STATUS(sharedData, *err); + return sharedData; +} + +/*returns a converter type from a string + */ +static const UConverterSharedData * +getAlgorithmicTypeFromName(const char *realName) +{ + uint32_t mid, start, limit; + uint32_t lastMid; + int result; + char strippedName[UCNV_MAX_CONVERTER_NAME_LENGTH]; + + /* Lower case and remove ignoreable characters. */ + ucnv_io_stripForCompare(strippedName, realName); + + /* do a binary search for the alias */ + start = 0; + limit = UPRV_LENGTHOF(cnvNameType); + mid = limit; + lastMid = UINT32_MAX; + + for (;;) { + mid = (uint32_t)((start + limit) / 2); + if (lastMid == mid) { /* Have we moved? */ + break; /* We haven't moved, and it wasn't found. */ + } + lastMid = mid; + result = uprv_strcmp(strippedName, cnvNameType[mid].name); + + if (result < 0) { + limit = mid; + } else if (result > 0) { + start = mid; + } else { + return converterData[cnvNameType[mid].type]; + } + } + + return nullptr; +} + +/* +* Based on the number of known converters, this determines how many times larger +* the shared data hash table should be. When on small platforms, or just a couple +* of converters are used, this number should be 2. When memory is plentiful, or +* when ucnv_countAvailable is ever used with a lot of available converters, +* this should be 4. +* Larger numbers reduce the number of hash collisions, but use more memory. +*/ +#define UCNV_CACHE_LOAD_FACTOR 2 + +/* Puts the shared data in the static hashtable SHARED_DATA_HASHTABLE */ +/* Will always be called with the cnvCacheMutex already being held */ +/* by the calling function. */ +/* Stores the shared data in the SHARED_DATA_HASHTABLE + * @param data The shared data + */ +static void +ucnv_shareConverterData(UConverterSharedData * data) +{ + UErrorCode err = U_ZERO_ERROR; + /*Lazy evaluates the Hashtable itself */ + /*void *sanity = nullptr;*/ + + if (SHARED_DATA_HASHTABLE == nullptr) + { + SHARED_DATA_HASHTABLE = uhash_openSize(uhash_hashChars, uhash_compareChars, nullptr, + ucnv_io_countKnownConverters(&err)*UCNV_CACHE_LOAD_FACTOR, + &err); + ucnv_enableCleanup(); + + if (U_FAILURE(err)) + return; + } + + /* ### check to see if the element is not already there! */ + + /* + sanity = ucnv_getSharedConverterData (data->staticData->name); + if(sanity != nullptr) + { + UCNV_DEBUG_LOG("put:overwrite!",data->staticData->name,sanity); + } + UCNV_DEBUG_LOG("put:chk",data->staticData->name,sanity); + */ + + /* Mark it shared */ + data->sharedDataCached = true; + + uhash_put(SHARED_DATA_HASHTABLE, + (void*) data->staticData->name, /* Okay to cast away const as long as + keyDeleter == nullptr */ + data, + &err); + UCNV_DEBUG_LOG("put", data->staticData->name,data); + +} + +/* Look up a converter name in the shared data cache. */ +/* cnvCacheMutex must be held by the caller to protect the hash table. */ +/* gets the shared data from the SHARED_DATA_HASHTABLE (might return nullptr if it isn't there) + * @param name The name of the shared data + * @return the shared data from the SHARED_DATA_HASHTABLE + */ +static UConverterSharedData * +ucnv_getSharedConverterData(const char *name) +{ + /*special case when no Table has yet been created we return nullptr */ + if (SHARED_DATA_HASHTABLE == nullptr) + { + return nullptr; + } + else + { + UConverterSharedData *rc; + + rc = (UConverterSharedData*)uhash_get(SHARED_DATA_HASHTABLE, name); + UCNV_DEBUG_LOG("get",name,rc); + return rc; + } +} + +/*frees the string of memory blocks associates with a sharedConverter + *if and only if the referenceCounter == 0 + */ +/* Deletes (frees) the Shared data it's passed. first it checks the referenceCounter to + * see if anyone is using it, if not it frees all the memory stemming from sharedConverterData and + * returns true, + * otherwise returns false + * @param sharedConverterData The shared data + * @return if not it frees all the memory stemming from sharedConverterData and + * returns true, otherwise returns false + */ +static UBool +ucnv_deleteSharedConverterData(UConverterSharedData * deadSharedData) +{ + UTRACE_ENTRY_OC(UTRACE_UCNV_UNLOAD); + UTRACE_DATA2(UTRACE_OPEN_CLOSE, "unload converter %s shared data %p", deadSharedData->staticData->name, deadSharedData); + + if (deadSharedData->referenceCounter > 0) { + UTRACE_EXIT_VALUE((int32_t)false); + return false; + } + + if (deadSharedData->impl->unload != nullptr) { + deadSharedData->impl->unload(deadSharedData); + } + + if(deadSharedData->dataMemory != nullptr) + { + UDataMemory *data = (UDataMemory*)deadSharedData->dataMemory; + udata_close(data); + } + + uprv_free(deadSharedData); + + UTRACE_EXIT_VALUE((int32_t)true); + return true; +} + +/** + * Load a non-algorithmic converter. + * If pkg==nullptr, then this function must be called inside umtx_lock(&cnvCacheMutex). + */ +UConverterSharedData * +ucnv_load(UConverterLoadArgs *pArgs, UErrorCode *err) { + UConverterSharedData *mySharedConverterData; + + if(err == nullptr || U_FAILURE(*err)) { + return nullptr; + } + + if(pArgs->pkg != nullptr && *pArgs->pkg != 0) { + /* application-provided converters are not currently cached */ + return createConverterFromFile(pArgs, err); + } + + mySharedConverterData = ucnv_getSharedConverterData(pArgs->name); + if (mySharedConverterData == nullptr) + { + /*Not cached, we need to stream it in from file */ + mySharedConverterData = createConverterFromFile(pArgs, err); + if (U_FAILURE (*err) || (mySharedConverterData == nullptr)) + { + return nullptr; + } + else if (!pArgs->onlyTestIsLoadable) + { + /* share it with other library clients */ + ucnv_shareConverterData(mySharedConverterData); + } + } + else + { + /* The data for this converter was already in the cache. */ + /* Update the reference counter on the shared data: one more client */ + mySharedConverterData->referenceCounter++; + } + + return mySharedConverterData; +} + +/** + * Unload a non-algorithmic converter. + * It must be sharedData->isReferenceCounted + * and this function must be called inside umtx_lock(&cnvCacheMutex). + */ +U_CAPI void +ucnv_unload(UConverterSharedData *sharedData) { + if(sharedData != nullptr) { + if (sharedData->referenceCounter > 0) { + sharedData->referenceCounter--; + } + + if((sharedData->referenceCounter <= 0)&&(sharedData->sharedDataCached == false)) { + ucnv_deleteSharedConverterData(sharedData); + } + } +} + +U_CFUNC void +ucnv_unloadSharedDataIfReady(UConverterSharedData *sharedData) +{ + if(sharedData != nullptr && sharedData->isReferenceCounted) { + umtx_lock(&cnvCacheMutex); + ucnv_unload(sharedData); + umtx_unlock(&cnvCacheMutex); + } +} + +U_CFUNC void +ucnv_incrementRefCount(UConverterSharedData *sharedData) +{ + if(sharedData != nullptr && sharedData->isReferenceCounted) { + umtx_lock(&cnvCacheMutex); + sharedData->referenceCounter++; + umtx_unlock(&cnvCacheMutex); + } +} + +/* + * *pPieces must be initialized. + * The name without options will be copied to pPieces->cnvName. + * The locale and options will be copied to pPieces only if present in inName, + * otherwise the existing values in pPieces remain. + * *pArgs will be set to the pPieces values. + */ +static void +parseConverterOptions(const char *inName, + UConverterNamePieces *pPieces, + UConverterLoadArgs *pArgs, + UErrorCode *err) +{ + char *cnvName = pPieces->cnvName; + char c; + int32_t len = 0; + + pArgs->name=inName; + pArgs->locale=pPieces->locale; + pArgs->options=pPieces->options; + + /* copy the converter name itself to cnvName */ + while((c=*inName)!=0 && c!=UCNV_OPTION_SEP_CHAR) { + if (++len>=UCNV_MAX_CONVERTER_NAME_LENGTH) { + *err = U_ILLEGAL_ARGUMENT_ERROR; /* bad name */ + pPieces->cnvName[0]=0; + return; + } + *cnvName++=c; + inName++; + } + *cnvName=0; + pArgs->name=pPieces->cnvName; + + /* parse options. No more name copying should occur. */ + while((c=*inName)!=0) { + if(c==UCNV_OPTION_SEP_CHAR) { + ++inName; + } + + /* inName is behind an option separator */ + if(uprv_strncmp(inName, "locale=", 7)==0) { + /* do not modify locale itself in case we have multiple locale options */ + char *dest=pPieces->locale; + + /* copy the locale option value */ + inName+=7; + len=0; + while((c=*inName)!=0 && c!=UCNV_OPTION_SEP_CHAR) { + ++inName; + + if(++len>=ULOC_FULLNAME_CAPACITY) { + *err=U_ILLEGAL_ARGUMENT_ERROR; /* bad name */ + pPieces->locale[0]=0; + return; + } + + *dest++=c; + } + *dest=0; + } else if(uprv_strncmp(inName, "version=", 8)==0) { + /* copy the version option value into bits 3..0 of pPieces->options */ + inName+=8; + c=*inName; + if(c==0) { + pArgs->options=(pPieces->options&=~UCNV_OPTION_VERSION); + return; + } else if((uint8_t)(c-'0')<10) { + pArgs->options=pPieces->options=(pPieces->options&~UCNV_OPTION_VERSION)|(uint32_t)(c-'0'); + ++inName; + } + } else if(uprv_strncmp(inName, "swaplfnl", 8)==0) { + inName+=8; + pArgs->options=(pPieces->options|=UCNV_OPTION_SWAP_LFNL); + /* add processing for new options here with another } else if(uprv_strncmp(inName, "option-name=", XX)==0) { */ + } else { + /* ignore any other options until we define some */ + while(((c = *inName++) != 0) && (c != UCNV_OPTION_SEP_CHAR)) { + } + if(c==0) { + return; + } + } + } +} + +/*Logic determines if the converter is Algorithmic AND/OR cached + *depending on that: + * -we either go to get data from disk and cache it (Data=true, Cached=false) + * -Get it from a Hashtable (Data=X, Cached=true) + * -Call dataConverter initializer (Data=true, Cached=true) + * -Call AlgorithmicConverter initializer (Data=false, Cached=true) + */ +U_CFUNC UConverterSharedData * +ucnv_loadSharedData(const char *converterName, + UConverterNamePieces *pPieces, + UConverterLoadArgs *pArgs, + UErrorCode * err) { + UConverterNamePieces stackPieces; + UConverterLoadArgs stackArgs; + UConverterSharedData *mySharedConverterData = nullptr; + UErrorCode internalErrorCode = U_ZERO_ERROR; + UBool mayContainOption = true; + UBool checkForAlgorithmic = true; + + if (U_FAILURE (*err)) { + return nullptr; + } + + if(pPieces == nullptr) { + if(pArgs != nullptr) { + /* + * Bad: We may set pArgs pointers to stackPieces fields + * which will be invalid after this function returns. + */ + *err = U_INTERNAL_PROGRAM_ERROR; + return nullptr; + } + pPieces = &stackPieces; + } + if(pArgs == nullptr) { + uprv_memset(&stackArgs, 0, sizeof(stackArgs)); + stackArgs.size = (int32_t)sizeof(stackArgs); + pArgs = &stackArgs; + } + + pPieces->cnvName[0] = 0; + pPieces->locale[0] = 0; + pPieces->options = 0; + + pArgs->name = converterName; + pArgs->locale = pPieces->locale; + pArgs->options = pPieces->options; + + /* In case "name" is nullptr we want to open the default converter. */ + if (converterName == nullptr) { +#if U_CHARSET_IS_UTF8 + pArgs->name = "UTF-8"; + return (UConverterSharedData *)converterData[UCNV_UTF8]; +#else + /* Call ucnv_getDefaultName first to query the name from the OS. */ + pArgs->name = ucnv_getDefaultName(); + if (pArgs->name == nullptr) { + *err = U_MISSING_RESOURCE_ERROR; + return nullptr; + } + mySharedConverterData = (UConverterSharedData *)gDefaultAlgorithmicSharedData; + checkForAlgorithmic = false; + mayContainOption = gDefaultConverterContainsOption; + /* the default converter name is already canonical */ +#endif + } + else if(UCNV_FAST_IS_UTF8(converterName)) { + /* fastpath for UTF-8 */ + pArgs->name = "UTF-8"; + return (UConverterSharedData *)converterData[UCNV_UTF8]; + } + else { + /* separate the converter name from the options */ + parseConverterOptions(converterName, pPieces, pArgs, err); + if (U_FAILURE(*err)) { + /* Very bad name used. */ + return nullptr; + } + + /* get the canonical converter name */ + pArgs->name = ucnv_io_getConverterName(pArgs->name, &mayContainOption, &internalErrorCode); + if (U_FAILURE(internalErrorCode) || pArgs->name == nullptr) { + /* + * set the input name in case the converter was added + * without updating the alias table, or when there is no alias table + */ + pArgs->name = pPieces->cnvName; + } else if (internalErrorCode == U_AMBIGUOUS_ALIAS_WARNING) { + *err = U_AMBIGUOUS_ALIAS_WARNING; + } + } + + /* separate the converter name from the options */ + if(mayContainOption && pArgs->name != pPieces->cnvName) { + parseConverterOptions(pArgs->name, pPieces, pArgs, err); + } + + /* get the shared data for an algorithmic converter, if it is one */ + if (checkForAlgorithmic) { + mySharedConverterData = (UConverterSharedData *)getAlgorithmicTypeFromName(pArgs->name); + } + if (mySharedConverterData == nullptr) + { + /* it is a data-based converter, get its shared data. */ + /* Hold the cnvCacheMutex through the whole process of checking the */ + /* converter data cache, and adding new entries to the cache */ + /* to prevent other threads from modifying the cache during the */ + /* process. */ + pArgs->nestedLoads=1; + pArgs->pkg=nullptr; + + umtx_lock(&cnvCacheMutex); + mySharedConverterData = ucnv_load(pArgs, err); + umtx_unlock(&cnvCacheMutex); + if (U_FAILURE (*err) || (mySharedConverterData == nullptr)) + { + return nullptr; + } + } + + return mySharedConverterData; +} + +U_CAPI UConverter * +ucnv_createConverter(UConverter *myUConverter, const char *converterName, UErrorCode * err) +{ + UConverterNamePieces stackPieces; + UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; + UConverterSharedData *mySharedConverterData; + + UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN); + + if(U_SUCCESS(*err)) { + UTRACE_DATA1(UTRACE_OPEN_CLOSE, "open converter %s", converterName); + + mySharedConverterData = ucnv_loadSharedData(converterName, &stackPieces, &stackArgs, err); + + myUConverter = ucnv_createConverterFromSharedData( + myUConverter, mySharedConverterData, + &stackArgs, + err); + + if(U_SUCCESS(*err)) { + UTRACE_EXIT_PTR_STATUS(myUConverter, *err); + return myUConverter; + } + } + + /* exit with error */ + UTRACE_EXIT_STATUS(*err); + return nullptr; +} + +U_CFUNC UBool +ucnv_canCreateConverter(const char *converterName, UErrorCode *err) { + UConverter myUConverter; + UConverterNamePieces stackPieces; + UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; + UConverterSharedData *mySharedConverterData; + + UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN); + + if(U_SUCCESS(*err)) { + UTRACE_DATA1(UTRACE_OPEN_CLOSE, "test if can open converter %s", converterName); + + stackArgs.onlyTestIsLoadable=true; + mySharedConverterData = ucnv_loadSharedData(converterName, &stackPieces, &stackArgs, err); + ucnv_createConverterFromSharedData( + &myUConverter, mySharedConverterData, + &stackArgs, + err); + ucnv_unloadSharedDataIfReady(mySharedConverterData); + } + + UTRACE_EXIT_STATUS(*err); + return U_SUCCESS(*err); +} + +UConverter * +ucnv_createAlgorithmicConverter(UConverter *myUConverter, + UConverterType type, + const char *locale, uint32_t options, + UErrorCode *err) { + UConverter *cnv; + const UConverterSharedData *sharedData; + UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; + + UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN_ALGORITHMIC); + UTRACE_DATA1(UTRACE_OPEN_CLOSE, "open algorithmic converter type %d", (int32_t)type); + + if(type<0 || UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES<=type) { + *err = U_ILLEGAL_ARGUMENT_ERROR; + UTRACE_EXIT_STATUS(U_ILLEGAL_ARGUMENT_ERROR); + return nullptr; + } + + sharedData = converterData[type]; + if(sharedData == nullptr || sharedData->isReferenceCounted) { + /* not a valid type, or not an algorithmic converter */ + *err = U_ILLEGAL_ARGUMENT_ERROR; + UTRACE_EXIT_STATUS(U_ILLEGAL_ARGUMENT_ERROR); + return nullptr; + } + + stackArgs.name = ""; + stackArgs.options = options; + stackArgs.locale=locale; + cnv = ucnv_createConverterFromSharedData( + myUConverter, (UConverterSharedData *)sharedData, + &stackArgs, err); + + UTRACE_EXIT_PTR_STATUS(cnv, *err); + return cnv; +} + +U_CFUNC UConverter* +ucnv_createConverterFromPackage(const char *packageName, const char *converterName, UErrorCode * err) +{ + UConverter *myUConverter; + UConverterSharedData *mySharedConverterData; + UConverterNamePieces stackPieces; + UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; + + UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN_PACKAGE); + + if(U_FAILURE(*err)) { + UTRACE_EXIT_STATUS(*err); + return nullptr; + } + + UTRACE_DATA2(UTRACE_OPEN_CLOSE, "open converter %s from package %s", converterName, packageName); + + /* first, get the options out of the converterName string */ + stackPieces.cnvName[0] = 0; + stackPieces.locale[0] = 0; + stackPieces.options = 0; + parseConverterOptions(converterName, &stackPieces, &stackArgs, err); + if (U_FAILURE(*err)) { + /* Very bad name used. */ + UTRACE_EXIT_STATUS(*err); + return nullptr; + } + stackArgs.nestedLoads=1; + stackArgs.pkg=packageName; + + /* open the data, unflatten the shared structure */ + mySharedConverterData = createConverterFromFile(&stackArgs, err); + + if (U_FAILURE(*err)) { + UTRACE_EXIT_STATUS(*err); + return nullptr; + } + + /* create the actual converter */ + myUConverter = ucnv_createConverterFromSharedData(nullptr, mySharedConverterData, &stackArgs, err); + + if (U_FAILURE(*err)) { + ucnv_close(myUConverter); + UTRACE_EXIT_STATUS(*err); + return nullptr; + } + + UTRACE_EXIT_PTR_STATUS(myUConverter, *err); + return myUConverter; +} + + +U_CFUNC UConverter* +ucnv_createConverterFromSharedData(UConverter *myUConverter, + UConverterSharedData *mySharedConverterData, + UConverterLoadArgs *pArgs, + UErrorCode *err) +{ + UBool isCopyLocal; + + if(U_FAILURE(*err)) { + ucnv_unloadSharedDataIfReady(mySharedConverterData); + return myUConverter; + } + if(myUConverter == nullptr) + { + myUConverter = (UConverter *) uprv_malloc (sizeof (UConverter)); + if(myUConverter == nullptr) + { + *err = U_MEMORY_ALLOCATION_ERROR; + ucnv_unloadSharedDataIfReady(mySharedConverterData); + return nullptr; + } + isCopyLocal = false; + } else { + isCopyLocal = true; + } + + /* initialize the converter */ + uprv_memset(myUConverter, 0, sizeof(UConverter)); + myUConverter->isCopyLocal = isCopyLocal; + /*myUConverter->isExtraLocal = false;*/ /* Set by the memset call */ + myUConverter->sharedData = mySharedConverterData; + myUConverter->options = pArgs->options; + if(!pArgs->onlyTestIsLoadable) { + myUConverter->preFromUFirstCP = U_SENTINEL; + myUConverter->fromCharErrorBehaviour = UCNV_TO_U_DEFAULT_CALLBACK; + myUConverter->fromUCharErrorBehaviour = UCNV_FROM_U_DEFAULT_CALLBACK; + myUConverter->toUnicodeStatus = mySharedConverterData->toUnicodeStatus; + myUConverter->maxBytesPerUChar = mySharedConverterData->staticData->maxBytesPerChar; + myUConverter->subChar1 = mySharedConverterData->staticData->subChar1; + myUConverter->subCharLen = mySharedConverterData->staticData->subCharLen; + myUConverter->subChars = (uint8_t *)myUConverter->subUChars; + uprv_memcpy(myUConverter->subChars, mySharedConverterData->staticData->subChar, myUConverter->subCharLen); + myUConverter->toUCallbackReason = UCNV_ILLEGAL; /* default reason to invoke (*fromCharErrorBehaviour) */ + } + + if(mySharedConverterData->impl->open != nullptr) { + mySharedConverterData->impl->open(myUConverter, pArgs, err); + if(U_FAILURE(*err) && !pArgs->onlyTestIsLoadable) { + /* don't ucnv_close() if onlyTestIsLoadable because not fully initialized */ + ucnv_close(myUConverter); + return nullptr; + } + } + + return myUConverter; +} + +/*Frees all shared immutable objects that aren't referred to (reference count = 0) + */ +U_CAPI int32_t U_EXPORT2 +ucnv_flushCache () +{ + UConverterSharedData *mySharedData = nullptr; + int32_t pos; + int32_t tableDeletedNum = 0; + const UHashElement *e; + /*UErrorCode status = U_ILLEGAL_ARGUMENT_ERROR;*/ + int32_t i, remaining; + + UTRACE_ENTRY_OC(UTRACE_UCNV_FLUSH_CACHE); + + /* Close the default converter without creating a new one so that everything will be flushed. */ + u_flushDefaultConverter(); + + /*if shared data hasn't even been lazy evaluated yet + * return 0 + */ + if (SHARED_DATA_HASHTABLE == nullptr) { + UTRACE_EXIT_VALUE((int32_t)0); + return 0; + } + + /*creates an enumeration to iterate through every element in the + * table + * + * Synchronization: holding cnvCacheMutex will prevent any other thread from + * accessing or modifying the hash table during the iteration. + * The reference count of an entry may be decremented by + * ucnv_close while the iteration is in process, but this is + * benign. It can't be incremented (in ucnv_createConverter()) + * because the sequence of looking up in the cache + incrementing + * is protected by cnvCacheMutex. + */ + umtx_lock(&cnvCacheMutex); + /* + * double loop: A delta/extension-only converter has a pointer to its base table's + * shared data; the first iteration of the outer loop may see the delta converter + * before the base converter, and unloading the delta converter may get the base + * converter's reference counter down to 0. + */ + i = 0; + do { + remaining = 0; + pos = UHASH_FIRST; + while ((e = uhash_nextElement (SHARED_DATA_HASHTABLE, &pos)) != nullptr) + { + mySharedData = (UConverterSharedData *) e->value.pointer; + /*deletes only if reference counter == 0 */ + if (mySharedData->referenceCounter == 0) + { + tableDeletedNum++; + + UCNV_DEBUG_LOG("del",mySharedData->staticData->name,mySharedData); + + uhash_removeElement(SHARED_DATA_HASHTABLE, e); + mySharedData->sharedDataCached = false; + ucnv_deleteSharedConverterData (mySharedData); + } else { + ++remaining; + } + } + } while(++i == 1 && remaining > 0); + umtx_unlock(&cnvCacheMutex); + + UTRACE_DATA1(UTRACE_INFO, "ucnv_flushCache() exits with %d converters remaining", remaining); + + UTRACE_EXIT_VALUE(tableDeletedNum); + return tableDeletedNum; +} + +/* available converters list --------------------------------------------------- */ + +static void U_CALLCONV initAvailableConvertersList(UErrorCode &errCode) { + U_ASSERT(gAvailableConverterCount == 0); + U_ASSERT(gAvailableConverters == nullptr); + + ucnv_enableCleanup(); + UEnumeration *allConvEnum = ucnv_openAllNames(&errCode); + int32_t allConverterCount = uenum_count(allConvEnum, &errCode); + if (U_FAILURE(errCode)) { + return; + } + + /* We can't have more than "*converterTable" converters to open */ + gAvailableConverters = (const char **) uprv_malloc(allConverterCount * sizeof(char*)); + if (!gAvailableConverters) { + errCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + + /* Open the default converter to make sure that it has first dibs in the hash table. */ + UErrorCode localStatus = U_ZERO_ERROR; + UConverter tempConverter; + ucnv_close(ucnv_createConverter(&tempConverter, nullptr, &localStatus)); + + gAvailableConverterCount = 0; + + for (int32_t idx = 0; idx < allConverterCount; idx++) { + localStatus = U_ZERO_ERROR; + const char *converterName = uenum_next(allConvEnum, nullptr, &localStatus); + if (ucnv_canCreateConverter(converterName, &localStatus)) { + gAvailableConverters[gAvailableConverterCount++] = converterName; + } + } + + uenum_close(allConvEnum); +} + + +static UBool haveAvailableConverterList(UErrorCode *pErrorCode) { + umtx_initOnce(gAvailableConvertersInitOnce, &initAvailableConvertersList, *pErrorCode); + return U_SUCCESS(*pErrorCode); +} + +U_CFUNC uint16_t +ucnv_bld_countAvailableConverters(UErrorCode *pErrorCode) { + if (haveAvailableConverterList(pErrorCode)) { + return gAvailableConverterCount; + } + return 0; +} + +U_CFUNC const char * +ucnv_bld_getAvailableConverter(uint16_t n, UErrorCode *pErrorCode) { + if (haveAvailableConverterList(pErrorCode)) { + if (n < gAvailableConverterCount) { + return gAvailableConverters[n]; + } + *pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR; + } + return nullptr; +} + +/* default converter name --------------------------------------------------- */ + +#if !U_CHARSET_IS_UTF8 +/* +Copy the canonical converter name. +ucnv_getDefaultName must be thread safe, which can call this function. + +ucnv_setDefaultName calls this function and it doesn't have to be +thread safe because there is no reliable/safe way to reset the +converter in use in all threads. If you did reset the converter, you +would not be sure that retrieving a default converter for one string +would be the same type of default converter for a successive string. +Since the name is a returned via ucnv_getDefaultName without copying, +you shouldn't be modifying or deleting the string from a separate thread. +*/ +static inline void +internalSetName(const char *name, UErrorCode *status) { + UConverterNamePieces stackPieces; + UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; + int32_t length=(int32_t)(uprv_strlen(name)); + UBool containsOption = (UBool)(uprv_strchr(name, UCNV_OPTION_SEP_CHAR) != nullptr); + const UConverterSharedData *algorithmicSharedData; + + stackArgs.name = name; + if(containsOption) { + stackPieces.cnvName[0] = 0; + stackPieces.locale[0] = 0; + stackPieces.options = 0; + parseConverterOptions(name, &stackPieces, &stackArgs, status); + if(U_FAILURE(*status)) { + return; + } + } + algorithmicSharedData = getAlgorithmicTypeFromName(stackArgs.name); + + umtx_lock(&cnvCacheMutex); + + gDefaultAlgorithmicSharedData = algorithmicSharedData; + gDefaultConverterContainsOption = containsOption; + uprv_memcpy(gDefaultConverterNameBuffer, name, length); + gDefaultConverterNameBuffer[length]=0; + + /* gDefaultConverterName MUST be the last global var set by this function. */ + /* It is the variable checked in ucnv_getDefaultName() to see if initialization is required. */ + // But there is nothing here preventing that from being reordered, either by the compiler + // or hardware. I'm adding the mutex to ucnv_getDefaultName for now. UMTX_CHECK is not enough. + // -- Andy + gDefaultConverterName = gDefaultConverterNameBuffer; + + ucnv_enableCleanup(); + + umtx_unlock(&cnvCacheMutex); +} +#endif + +/* + * In order to be really thread-safe, the get function would have to take + * a buffer parameter and copy the current string inside a mutex block. + * This implementation only tries to be really thread-safe while + * setting the name. + * It assumes that setting a pointer is atomic. + */ + +U_CAPI const char* U_EXPORT2 +ucnv_getDefaultName() { +#if U_CHARSET_IS_UTF8 + return "UTF-8"; +#else + /* local variable to be thread-safe */ + const char *name; + + /* + Concurrent calls to ucnv_getDefaultName must be thread safe, + but ucnv_setDefaultName is not thread safe. + */ + { + icu::Mutex lock(&cnvCacheMutex); + name = gDefaultConverterName; + } + if(name==nullptr) { + UErrorCode errorCode = U_ZERO_ERROR; + UConverter *cnv = nullptr; + + name = uprv_getDefaultCodepage(); + + /* if the name is there, test it out and get the canonical name with options */ + if(name != nullptr) { + cnv = ucnv_open(name, &errorCode); + if(U_SUCCESS(errorCode) && cnv != nullptr) { + name = ucnv_getName(cnv, &errorCode); + } + } + + if(name == nullptr || name[0] == 0 + || U_FAILURE(errorCode) || cnv == nullptr + || uprv_strlen(name)>=sizeof(gDefaultConverterNameBuffer)) + { + /* Panic time, let's use a fallback. */ +#if (U_CHARSET_FAMILY == U_ASCII_FAMILY) + name = "US-ASCII"; + /* there is no 'algorithmic' converter for EBCDIC */ +#elif U_PLATFORM == U_PF_OS390 + name = "ibm-1047_P100-1995" UCNV_SWAP_LFNL_OPTION_STRING; +#else + name = "ibm-37_P100-1995"; +#endif + } + + internalSetName(name, &errorCode); + + /* The close may make the current name go away. */ + ucnv_close(cnv); + } + + return name; +#endif +} + +#if U_CHARSET_IS_UTF8 +U_CAPI void U_EXPORT2 ucnv_setDefaultName(const char *) {} +#else +/* +This function is not thread safe, and it can't be thread safe. +See internalSetName or the API reference for details. +*/ +U_CAPI void U_EXPORT2 +ucnv_setDefaultName(const char *converterName) { + if(converterName==nullptr) { + /* reset to the default codepage */ + gDefaultConverterName=nullptr; + } else { + UErrorCode errorCode = U_ZERO_ERROR; + UConverter *cnv = nullptr; + const char *name = nullptr; + + /* if the name is there, test it out and get the canonical name with options */ + cnv = ucnv_open(converterName, &errorCode); + if(U_SUCCESS(errorCode) && cnv != nullptr) { + name = ucnv_getName(cnv, &errorCode); + } + + if(U_SUCCESS(errorCode) && name!=nullptr) { + internalSetName(name, &errorCode); + } + /* else this converter is bad to use. Don't change it to a bad value. */ + + /* The close may make the current name go away. */ + ucnv_close(cnv); + + /* reset the converter cache */ + u_flushDefaultConverter(); + } +} +#endif + +/* data swapping ------------------------------------------------------------ */ + +/* most of this might belong more properly into ucnvmbcs.c, but that is so large */ + +#if !UCONFIG_NO_LEGACY_CONVERSION + +U_CAPI int32_t U_EXPORT2 +ucnv_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const UDataInfo *pInfo; + int32_t headerSize; + + const uint8_t *inBytes; + uint8_t *outBytes; + + uint32_t offset, count, staticDataSize; + int32_t size; + + const UConverterStaticData *inStaticData; + UConverterStaticData *outStaticData; + + const _MBCSHeader *inMBCSHeader; + _MBCSHeader *outMBCSHeader; + _MBCSHeader mbcsHeader; + uint32_t mbcsHeaderLength; + UBool noFromU=false; + + uint8_t outputType; + + int32_t maxFastUChar, mbcsIndexLength; + + const int32_t *inExtIndexes; + int32_t extOffset; + + /* udata_swapDataHeader checks the arguments */ + headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + /* check data format and format version */ + pInfo=(const UDataInfo *)((const char *)inData+4); + if(!( + pInfo->dataFormat[0]==0x63 && /* dataFormat="cnvt" */ + pInfo->dataFormat[1]==0x6e && + pInfo->dataFormat[2]==0x76 && + pInfo->dataFormat[3]==0x74 && + pInfo->formatVersion[0]==6 && + pInfo->formatVersion[1]>=2 + )) { + udata_printError(ds, "ucnv_swap(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not recognized as an ICU .cnv conversion table\n", + pInfo->dataFormat[0], pInfo->dataFormat[1], + pInfo->dataFormat[2], pInfo->dataFormat[3], + pInfo->formatVersion[0], pInfo->formatVersion[1]); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + inBytes=(const uint8_t *)inData+headerSize; + outBytes=(outData == nullptr) ? nullptr : (uint8_t *)outData+headerSize; + + /* read the initial UConverterStaticData structure after the UDataInfo header */ + inStaticData=(const UConverterStaticData *)inBytes; + outStaticData=(UConverterStaticData *)outBytes; + + if(length<0) { + staticDataSize=ds->readUInt32(inStaticData->structSize); + } else { + length-=headerSize; + if( length<(int32_t)sizeof(UConverterStaticData) || + (uint32_t)length<(staticDataSize=ds->readUInt32(inStaticData->structSize)) + ) { + udata_printError(ds, "ucnv_swap(): too few bytes (%d after header) for an ICU .cnv conversion table\n", + length); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + } + + if(length>=0) { + /* swap the static data */ + if(inStaticData!=outStaticData) { + uprv_memcpy(outStaticData, inStaticData, staticDataSize); + } + + ds->swapArray32(ds, &inStaticData->structSize, 4, + &outStaticData->structSize, pErrorCode); + ds->swapArray32(ds, &inStaticData->codepage, 4, + &outStaticData->codepage, pErrorCode); + + ds->swapInvChars(ds, inStaticData->name, (int32_t)uprv_strlen(inStaticData->name), + outStaticData->name, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ucnv_swap(): error swapping converter name\n"); + return 0; + } + } + + inBytes+=staticDataSize; + if (outBytes != nullptr) outBytes+=staticDataSize; + if(length>=0) { + length-=(int32_t)staticDataSize; + } + + /* check for supported conversionType values */ + if(inStaticData->conversionType==UCNV_MBCS) { + /* swap MBCS data */ + inMBCSHeader=(const _MBCSHeader *)inBytes; + outMBCSHeader=(_MBCSHeader *)outBytes; + + if(0<=length && length<(int32_t)sizeof(_MBCSHeader)) { + udata_printError(ds, "ucnv_swap(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table\n", + length); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + if(inMBCSHeader->version[0]==4 && inMBCSHeader->version[1]>=1) { + mbcsHeaderLength=MBCS_HEADER_V4_LENGTH; + } else if(inMBCSHeader->version[0]==5 && inMBCSHeader->version[1]>=3 && + ((mbcsHeader.options=ds->readUInt32(inMBCSHeader->options))& + MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK)==0 + ) { + mbcsHeaderLength=mbcsHeader.options&MBCS_OPT_LENGTH_MASK; + noFromU=(UBool)((mbcsHeader.options&MBCS_OPT_NO_FROM_U)!=0); + } else { + udata_printError(ds, "ucnv_swap(): unsupported _MBCSHeader.version %d.%d\n", + inMBCSHeader->version[0], inMBCSHeader->version[1]); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + uprv_memcpy(mbcsHeader.version, inMBCSHeader->version, 4); + mbcsHeader.countStates= ds->readUInt32(inMBCSHeader->countStates); + mbcsHeader.countToUFallbacks= ds->readUInt32(inMBCSHeader->countToUFallbacks); + mbcsHeader.offsetToUCodeUnits= ds->readUInt32(inMBCSHeader->offsetToUCodeUnits); + mbcsHeader.offsetFromUTable= ds->readUInt32(inMBCSHeader->offsetFromUTable); + mbcsHeader.offsetFromUBytes= ds->readUInt32(inMBCSHeader->offsetFromUBytes); + mbcsHeader.flags= ds->readUInt32(inMBCSHeader->flags); + mbcsHeader.fromUBytesLength= ds->readUInt32(inMBCSHeader->fromUBytesLength); + /* mbcsHeader.options have been read above */ + + extOffset=(int32_t)(mbcsHeader.flags>>8); + outputType=(uint8_t)mbcsHeader.flags; + if(noFromU && outputType==MBCS_OUTPUT_1) { + udata_printError(ds, "ucnv_swap(): unsupported combination of makeconv --small with SBCS\n"); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + /* make sure that the output type is known */ + switch(outputType) { + case MBCS_OUTPUT_1: + case MBCS_OUTPUT_2: + case MBCS_OUTPUT_3: + case MBCS_OUTPUT_4: + case MBCS_OUTPUT_3_EUC: + case MBCS_OUTPUT_4_EUC: + case MBCS_OUTPUT_2_SISO: + case MBCS_OUTPUT_EXT_ONLY: + /* OK */ + break; + default: + udata_printError(ds, "ucnv_swap(): unsupported MBCS output type 0x%x\n", + outputType); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + /* calculate the length of the MBCS data */ + + /* + * utf8Friendly MBCS files (mbcsHeader.version 4.3) + * contain an additional mbcsIndex table: + * uint16_t[(maxFastUChar+1)>>6]; + * where maxFastUChar=((mbcsHeader.version[2]<<8)|0xff). + */ + maxFastUChar=0; + mbcsIndexLength=0; + if( outputType!=MBCS_OUTPUT_EXT_ONLY && outputType!=MBCS_OUTPUT_1 && + mbcsHeader.version[1]>=3 && (maxFastUChar=mbcsHeader.version[2])!=0 + ) { + maxFastUChar=(maxFastUChar<<8)|0xff; + mbcsIndexLength=((maxFastUChar+1)>>6)*2; /* number of bytes */ + } + + if(extOffset==0) { + size=(int32_t)(mbcsHeader.offsetFromUBytes+mbcsIndexLength); + if(!noFromU) { + size+=(int32_t)mbcsHeader.fromUBytesLength; + } + + /* avoid compiler warnings - not otherwise necessary, and the value does not matter */ + inExtIndexes=nullptr; + } else { + /* there is extension data after the base data, see ucnv_ext.h */ + if(length>=0 && length<(extOffset+UCNV_EXT_INDEXES_MIN_LENGTH*4)) { + udata_printError(ds, "ucnv_swap(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table with extension data\n", + length); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + inExtIndexes=(const int32_t *)(inBytes+extOffset); + size=extOffset+udata_readInt32(ds, inExtIndexes[UCNV_EXT_SIZE]); + } + + if(length>=0) { + if(lengthswapArray32(ds, &inMBCSHeader->countStates, count-4, + &outMBCSHeader->countStates, pErrorCode); + + if(outputType==MBCS_OUTPUT_EXT_ONLY) { + /* + * extension-only file, + * contains a base name instead of normal base table data + */ + + /* swap the base name, between the header and the extension data */ + const char *inBaseName=(const char *)inBytes+count; + char *outBaseName=(char *)outBytes+count; + ds->swapInvChars(ds, inBaseName, (int32_t)uprv_strlen(inBaseName), + outBaseName, pErrorCode); + } else { + /* normal file with base table data */ + + /* swap the state table, 1kB per state */ + offset=count; + count=mbcsHeader.countStates*1024; + ds->swapArray32(ds, inBytes+offset, (int32_t)count, + outBytes+offset, pErrorCode); + + /* swap the toUFallbacks[] */ + offset+=count; + count=mbcsHeader.countToUFallbacks*8; + ds->swapArray32(ds, inBytes+offset, (int32_t)count, + outBytes+offset, pErrorCode); + + /* swap the unicodeCodeUnits[] */ + offset=mbcsHeader.offsetToUCodeUnits; + count=mbcsHeader.offsetFromUTable-offset; + ds->swapArray16(ds, inBytes+offset, (int32_t)count, + outBytes+offset, pErrorCode); + + /* offset to the stage 1 table, independent of the outputType */ + offset=mbcsHeader.offsetFromUTable; + + if(outputType==MBCS_OUTPUT_1) { + /* SBCS: swap the fromU tables, all 16 bits wide */ + count=(mbcsHeader.offsetFromUBytes-offset)+mbcsHeader.fromUBytesLength; + ds->swapArray16(ds, inBytes+offset, (int32_t)count, + outBytes+offset, pErrorCode); + } else { + /* otherwise: swap the stage tables separately */ + + /* stage 1 table: uint16_t[0x440 or 0x40] */ + if(inStaticData->unicodeMask&UCNV_HAS_SUPPLEMENTARY) { + count=0x440*2; /* for all of Unicode */ + } else { + count=0x40*2; /* only BMP */ + } + ds->swapArray16(ds, inBytes+offset, (int32_t)count, + outBytes+offset, pErrorCode); + + /* stage 2 table: uint32_t[] */ + offset+=count; + count=mbcsHeader.offsetFromUBytes-offset; + ds->swapArray32(ds, inBytes+offset, (int32_t)count, + outBytes+offset, pErrorCode); + + /* stage 3/result bytes: sometimes uint16_t[] or uint32_t[] */ + offset=mbcsHeader.offsetFromUBytes; + count= noFromU ? 0 : mbcsHeader.fromUBytesLength; + switch(outputType) { + case MBCS_OUTPUT_2: + case MBCS_OUTPUT_3_EUC: + case MBCS_OUTPUT_2_SISO: + ds->swapArray16(ds, inBytes+offset, (int32_t)count, + outBytes+offset, pErrorCode); + break; + case MBCS_OUTPUT_4: + ds->swapArray32(ds, inBytes+offset, (int32_t)count, + outBytes+offset, pErrorCode); + break; + default: + /* just uint8_t[], nothing to swap */ + break; + } + + if(mbcsIndexLength!=0) { + offset+=count; + count=mbcsIndexLength; + ds->swapArray16(ds, inBytes+offset, (int32_t)count, + outBytes+offset, pErrorCode); + } + } + } + + if(extOffset!=0) { + /* swap the extension data */ + inBytes+=extOffset; + outBytes+=extOffset; + + /* swap toUTable[] */ + offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_INDEX]); + length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_LENGTH]); + ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); + + /* swap toUUChars[] */ + offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_UCHARS_INDEX]); + length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_UCHARS_LENGTH]); + ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); + + /* swap fromUTableUChars[] */ + offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_UCHARS_INDEX]); + length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_LENGTH]); + ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); + + /* swap fromUTableValues[] */ + offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_VALUES_INDEX]); + /* same length as for fromUTableUChars[] */ + ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); + + /* no need to swap fromUBytes[] */ + + /* swap fromUStage12[] */ + offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_12_INDEX]); + length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_12_LENGTH]); + ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); + + /* swap fromUStage3[] */ + offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3_INDEX]); + length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3_LENGTH]); + ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); + + /* swap fromUStage3b[] */ + offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3B_INDEX]); + length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3B_LENGTH]); + ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); + + /* swap indexes[] */ + length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_INDEXES_LENGTH]); + ds->swapArray32(ds, inBytes, length*4, outBytes, pErrorCode); + } + } + } else { + udata_printError(ds, "ucnv_swap(): unknown conversionType=%d!=UCNV_MBCS\n", + inStaticData->conversionType); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + return headerSize+(int32_t)staticDataSize+size; +} + +#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ + +#endif diff --git a/deps/icu-small/source/common/ucnv_bld.h b/deps/icu-small/source/common/ucnv_bld.h index 43e6c09ac0b0f8..f175ed1b57404f 100644 --- a/deps/icu-small/source/common/ucnv_bld.h +++ b/deps/icu-small/source/common/ucnv_bld.h @@ -1,296 +1,296 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2015 International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* -* ucnv_bld.h: -* Contains internal data structure definitions -* Created by Bertrand A. Damiba -* -* Change history: -* -* 06/29/2000 helena Major rewrite of the callback APIs. -*/ - -#ifndef UCNV_BLD_H -#define UCNV_BLD_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/ucnv_err.h" -#include "unicode/utf16.h" -#include "ucnv_cnv.h" -#include "ucnvmbcs.h" -#include "ucnv_ext.h" -#include "udataswp.h" - -/* size of the overflow buffers in UConverter, enough for escaping callbacks */ -#define UCNV_ERROR_BUFFER_LENGTH 32 - -/* at most 4 bytes per substitution character (part of .cnv file format! see UConverterStaticData) */ -#define UCNV_MAX_SUBCHAR_LEN 4 - -/* at most 8 bytes per character in toUBytes[] (UTF-8 uses up to 6) */ -#define UCNV_MAX_CHAR_LEN 8 - -/* converter options bits */ -#define UCNV_OPTION_VERSION 0xf -#define UCNV_OPTION_SWAP_LFNL 0x10 - -#define UCNV_GET_VERSION(cnv) ((cnv)->options&UCNV_OPTION_VERSION) - -U_CDECL_BEGIN /* We must declare the following as 'extern "C"' so that if ucnv - itself is compiled under C++, the linkage of the funcptrs will - work. - */ - -union UConverterTable { - UConverterMBCSTable mbcs; -}; - -typedef union UConverterTable UConverterTable; - -struct UConverterImpl; -typedef struct UConverterImpl UConverterImpl; - -/** values for the unicodeMask */ -#define UCNV_HAS_SUPPLEMENTARY 1 -#define UCNV_HAS_SURROGATES 2 - -typedef struct UConverterStaticData { /* +offset: size */ - uint32_t structSize; /* +0: 4 Size of this structure */ - - char name - [UCNV_MAX_CONVERTER_NAME_LENGTH]; /* +4: 60 internal name of the converter- invariant chars */ - - int32_t codepage; /* +64: 4 codepage # (now IBM-$codepage) */ - - int8_t platform; /* +68: 1 platform of the converter (only IBM now) */ - int8_t conversionType; /* +69: 1 conversion type */ - - int8_t minBytesPerChar; /* +70: 1 Minimum # bytes per char in this codepage */ - int8_t maxBytesPerChar; /* +71: 1 Maximum # bytes output per UChar in this codepage */ - - uint8_t subChar[UCNV_MAX_SUBCHAR_LEN]; /* +72: 4 [note: 4 and 8 byte boundary] */ - int8_t subCharLen; /* +76: 1 */ - - uint8_t hasToUnicodeFallback; /* +77: 1 UBool needs to be changed to UBool to be consistent across platform */ - uint8_t hasFromUnicodeFallback; /* +78: 1 */ - uint8_t unicodeMask; /* +79: 1 bit 0: has supplementary bit 1: has single surrogates */ - uint8_t subChar1; /* +80: 1 single-byte substitution character for IBM MBCS (0 if none) */ - uint8_t reserved[19]; /* +81: 19 to round out the structure */ - /* total size: 100 */ -} UConverterStaticData; - -/* - * Defines the UConverterSharedData struct, - * the immutable, shared part of UConverter. - */ -struct UConverterSharedData { - uint32_t structSize; /* Size of this structure */ - uint32_t referenceCounter; /* used to count number of clients, unused for static/immutable SharedData */ - - const void *dataMemory; /* from udata_openChoice() - for cleanup */ - - const UConverterStaticData *staticData; /* pointer to the static (non changing) data. */ - - UBool sharedDataCached; /* true: shared data is in cache, don't destroy on ucnv_close() if 0 ref. false: shared data isn't in the cache, do attempt to clean it up if the ref is 0 */ - /** If false, then referenceCounter is not used. Must not change after initialization. */ - UBool isReferenceCounted; - - const UConverterImpl *impl; /* vtable-style struct of mostly function pointers */ - - /*initial values of some members of the mutable part of object */ - uint32_t toUnicodeStatus; - - /* - * Shared data structures currently come in two flavors: - * - readonly for built-in algorithmic converters - * - allocated for MBCS, with a pointer to an allocated UConverterTable - * which always has a UConverterMBCSTable - * - * To eliminate one allocation, I am making the UConverterMBCSTable - * a member of the shared data. - * - * markus 2003-nov-07 - */ - UConverterMBCSTable mbcs; -}; - -/** UConverterSharedData initializer for static, non-reference-counted converters. */ -#define UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(pStaticData, pImpl) \ - { \ - sizeof(UConverterSharedData), ~((uint32_t)0), \ - NULL, pStaticData, false, false, pImpl, \ - 0, UCNV_MBCS_TABLE_INITIALIZER \ - } - -/* Defines a UConverter, the lightweight mutable part the user sees */ - -struct UConverter { - /* - * Error function pointer called when conversion issues - * occur during a ucnv_fromUnicode call - */ - void (U_EXPORT2 *fromUCharErrorBehaviour) (const void *context, - UConverterFromUnicodeArgs *args, - const UChar *codeUnits, - int32_t length, - UChar32 codePoint, - UConverterCallbackReason reason, - UErrorCode *); - /* - * Error function pointer called when conversion issues - * occur during a ucnv_toUnicode call - */ - void (U_EXPORT2 *fromCharErrorBehaviour) (const void *context, - UConverterToUnicodeArgs *args, - const char *codeUnits, - int32_t length, - UConverterCallbackReason reason, - UErrorCode *); - - /* - * Pointer to additional data that depends on the converter type. - * Used by ISO 2022, SCSU, GB 18030 converters, possibly more. - */ - void *extraInfo; - - const void *fromUContext; - const void *toUContext; - - /* - * Pointer to charset bytes for substitution string if subCharLen>0, - * or pointer to Unicode string (UChar *) if subCharLen<0. - * subCharLen==0 is equivalent to using a skip callback. - * If the pointer is !=subUChars then it is allocated with - * UCNV_ERROR_BUFFER_LENGTH * U_SIZEOF_UCHAR bytes. - * The subUChars field is declared as UChar[] not uint8_t[] to - * guarantee alignment for UChars. - */ - uint8_t *subChars; - - UConverterSharedData *sharedData; /* Pointer to the shared immutable part of the converter object */ - - uint32_t options; /* options flags from UConverterOpen, may contain additional bits */ - - UBool sharedDataIsCached; /* true: shared data is in cache, don't destroy on ucnv_close() if 0 ref. false: shared data isn't in the cache, do attempt to clean it up if the ref is 0 */ - UBool isCopyLocal; /* true if UConverter is not owned and not released in ucnv_close() (stack-allocated, safeClone(), etc.) */ - UBool isExtraLocal; /* true if extraInfo is not owned and not released in ucnv_close() (stack-allocated, safeClone(), etc.) */ - - UBool useFallback; - int8_t toULength; /* number of bytes in toUBytes */ - uint8_t toUBytes[UCNV_MAX_CHAR_LEN-1];/* more "toU status"; keeps the bytes of the current character */ - uint32_t toUnicodeStatus; /* Used to internalize stream status information */ - int32_t mode; - uint32_t fromUnicodeStatus; - - /* - * More fromUnicode() status. Serves 3 purposes: - * - keeps a lead surrogate between buffers (similar to toUBytes[]) - * - keeps a lead surrogate at the end of the stream, - * which the framework handles as truncated input - * - if the fromUnicode() implementation returns to the framework - * (ucnv.c ucnv_fromUnicode()), then the framework calls the callback - * for this code point - */ - UChar32 fromUChar32; - - /* - * value for ucnv_getMaxCharSize() - * - * usually simply copied from the static data, but ucnvmbcs.c modifies - * the value depending on the converter type and options - */ - int8_t maxBytesPerUChar; - - int8_t subCharLen; /* length of the codepage specific character sequence */ - int8_t invalidCharLength; - int8_t charErrorBufferLength; /* number of valid bytes in charErrorBuffer */ - - int8_t invalidUCharLength; - int8_t UCharErrorBufferLength; /* number of valid UChars in charErrorBuffer */ - - uint8_t subChar1; /* single-byte substitution character if different from subChar */ - UBool useSubChar1; - char invalidCharBuffer[UCNV_MAX_CHAR_LEN]; /* bytes from last error/callback situation */ - uint8_t charErrorBuffer[UCNV_ERROR_BUFFER_LENGTH]; /* codepage output from Error functions */ - UChar subUChars[UCNV_MAX_SUBCHAR_LEN/U_SIZEOF_UCHAR]; /* see subChars documentation */ - - UChar invalidUCharBuffer[U16_MAX_LENGTH]; /* UChars from last error/callback situation */ - UChar UCharErrorBuffer[UCNV_ERROR_BUFFER_LENGTH]; /* unicode output from Error functions */ - - /* fields for conversion extension */ - - /* store previous UChars/chars to continue partial matches */ - UChar32 preFromUFirstCP; /* >=0: partial match */ - UChar preFromU[UCNV_EXT_MAX_UCHARS]; - char preToU[UCNV_EXT_MAX_BYTES]; - int8_t preFromULength, preToULength; /* negative: replay */ - int8_t preToUFirstLength; /* length of first character */ - - /* new fields for ICU 4.0 */ - UConverterCallbackReason toUCallbackReason; /* (*fromCharErrorBehaviour) reason, set when error is detected */ -}; - -U_CDECL_END /* end of UConverter */ - -#define CONVERTER_FILE_EXTENSION ".cnv" - - -/** - * Return the number of all converter names. - * @param pErrorCode The error code - * @return the number of all converter names - */ -U_CFUNC uint16_t -ucnv_bld_countAvailableConverters(UErrorCode *pErrorCode); - -/** - * Return the (n)th converter name in mixed case, or NULL - * if there is none (typically, if the data cannot be loaded). - * 0<=indexisReferenceCounted - * and this function must be called inside umtx_lock(&cnvCacheMutex). - */ -U_CAPI void -ucnv_unload(UConverterSharedData *sharedData); - -/** - * Swap ICU .cnv conversion tables. See udataswp.h. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -ucnv_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -U_CAPI void U_EXPORT2 -ucnv_enableCleanup(void); - -#endif - -#endif /* _UCNV_BLD */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2015 International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* +* ucnv_bld.h: +* Contains internal data structure definitions +* Created by Bertrand A. Damiba +* +* Change history: +* +* 06/29/2000 helena Major rewrite of the callback APIs. +*/ + +#ifndef UCNV_BLD_H +#define UCNV_BLD_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/ucnv_err.h" +#include "unicode/utf16.h" +#include "ucnv_cnv.h" +#include "ucnvmbcs.h" +#include "ucnv_ext.h" +#include "udataswp.h" + +/* size of the overflow buffers in UConverter, enough for escaping callbacks */ +#define UCNV_ERROR_BUFFER_LENGTH 32 + +/* at most 4 bytes per substitution character (part of .cnv file format! see UConverterStaticData) */ +#define UCNV_MAX_SUBCHAR_LEN 4 + +/* at most 8 bytes per character in toUBytes[] (UTF-8 uses up to 6) */ +#define UCNV_MAX_CHAR_LEN 8 + +/* converter options bits */ +#define UCNV_OPTION_VERSION 0xf +#define UCNV_OPTION_SWAP_LFNL 0x10 + +#define UCNV_GET_VERSION(cnv) ((cnv)->options&UCNV_OPTION_VERSION) + +U_CDECL_BEGIN /* We must declare the following as 'extern "C"' so that if ucnv + itself is compiled under C++, the linkage of the funcptrs will + work. + */ + +union UConverterTable { + UConverterMBCSTable mbcs; +}; + +typedef union UConverterTable UConverterTable; + +struct UConverterImpl; +typedef struct UConverterImpl UConverterImpl; + +/** values for the unicodeMask */ +#define UCNV_HAS_SUPPLEMENTARY 1 +#define UCNV_HAS_SURROGATES 2 + +typedef struct UConverterStaticData { /* +offset: size */ + uint32_t structSize; /* +0: 4 Size of this structure */ + + char name + [UCNV_MAX_CONVERTER_NAME_LENGTH]; /* +4: 60 internal name of the converter- invariant chars */ + + int32_t codepage; /* +64: 4 codepage # (now IBM-$codepage) */ + + int8_t platform; /* +68: 1 platform of the converter (only IBM now) */ + int8_t conversionType; /* +69: 1 conversion type */ + + int8_t minBytesPerChar; /* +70: 1 Minimum # bytes per char in this codepage */ + int8_t maxBytesPerChar; /* +71: 1 Maximum # bytes output per UChar in this codepage */ + + uint8_t subChar[UCNV_MAX_SUBCHAR_LEN]; /* +72: 4 [note: 4 and 8 byte boundary] */ + int8_t subCharLen; /* +76: 1 */ + + uint8_t hasToUnicodeFallback; /* +77: 1 UBool needs to be changed to UBool to be consistent across platform */ + uint8_t hasFromUnicodeFallback; /* +78: 1 */ + uint8_t unicodeMask; /* +79: 1 bit 0: has supplementary bit 1: has single surrogates */ + uint8_t subChar1; /* +80: 1 single-byte substitution character for IBM MBCS (0 if none) */ + uint8_t reserved[19]; /* +81: 19 to round out the structure */ + /* total size: 100 */ +} UConverterStaticData; + +/* + * Defines the UConverterSharedData struct, + * the immutable, shared part of UConverter. + */ +struct UConverterSharedData { + uint32_t structSize; /* Size of this structure */ + uint32_t referenceCounter; /* used to count number of clients, unused for static/immutable SharedData */ + + const void *dataMemory; /* from udata_openChoice() - for cleanup */ + + const UConverterStaticData *staticData; /* pointer to the static (non changing) data. */ + + UBool sharedDataCached; /* true: shared data is in cache, don't destroy on ucnv_close() if 0 ref. false: shared data isn't in the cache, do attempt to clean it up if the ref is 0 */ + /** If false, then referenceCounter is not used. Must not change after initialization. */ + UBool isReferenceCounted; + + const UConverterImpl *impl; /* vtable-style struct of mostly function pointers */ + + /*initial values of some members of the mutable part of object */ + uint32_t toUnicodeStatus; + + /* + * Shared data structures currently come in two flavors: + * - readonly for built-in algorithmic converters + * - allocated for MBCS, with a pointer to an allocated UConverterTable + * which always has a UConverterMBCSTable + * + * To eliminate one allocation, I am making the UConverterMBCSTable + * a member of the shared data. + * + * markus 2003-nov-07 + */ + UConverterMBCSTable mbcs; +}; + +/** UConverterSharedData initializer for static, non-reference-counted converters. */ +#define UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(pStaticData, pImpl) \ + { \ + sizeof(UConverterSharedData), ~((uint32_t)0), \ + NULL, pStaticData, false, false, pImpl, \ + 0, UCNV_MBCS_TABLE_INITIALIZER \ + } + +/* Defines a UConverter, the lightweight mutable part the user sees */ + +struct UConverter { + /* + * Error function pointer called when conversion issues + * occur during a ucnv_fromUnicode call + */ + void (U_EXPORT2 *fromUCharErrorBehaviour) (const void *context, + UConverterFromUnicodeArgs *args, + const UChar *codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode *); + /* + * Error function pointer called when conversion issues + * occur during a ucnv_toUnicode call + */ + void (U_EXPORT2 *fromCharErrorBehaviour) (const void *context, + UConverterToUnicodeArgs *args, + const char *codeUnits, + int32_t length, + UConverterCallbackReason reason, + UErrorCode *); + + /* + * Pointer to additional data that depends on the converter type. + * Used by ISO 2022, SCSU, GB 18030 converters, possibly more. + */ + void *extraInfo; + + const void *fromUContext; + const void *toUContext; + + /* + * Pointer to charset bytes for substitution string if subCharLen>0, + * or pointer to Unicode string (UChar *) if subCharLen<0. + * subCharLen==0 is equivalent to using a skip callback. + * If the pointer is !=subUChars then it is allocated with + * UCNV_ERROR_BUFFER_LENGTH * U_SIZEOF_UCHAR bytes. + * The subUChars field is declared as UChar[] not uint8_t[] to + * guarantee alignment for UChars. + */ + uint8_t *subChars; + + UConverterSharedData *sharedData; /* Pointer to the shared immutable part of the converter object */ + + uint32_t options; /* options flags from UConverterOpen, may contain additional bits */ + + UBool sharedDataIsCached; /* true: shared data is in cache, don't destroy on ucnv_close() if 0 ref. false: shared data isn't in the cache, do attempt to clean it up if the ref is 0 */ + UBool isCopyLocal; /* true if UConverter is not owned and not released in ucnv_close() (stack-allocated, safeClone(), etc.) */ + UBool isExtraLocal; /* true if extraInfo is not owned and not released in ucnv_close() (stack-allocated, safeClone(), etc.) */ + + UBool useFallback; + int8_t toULength; /* number of bytes in toUBytes */ + uint8_t toUBytes[UCNV_MAX_CHAR_LEN-1];/* more "toU status"; keeps the bytes of the current character */ + uint32_t toUnicodeStatus; /* Used to internalize stream status information */ + int32_t mode; + uint32_t fromUnicodeStatus; + + /* + * More fromUnicode() status. Serves 3 purposes: + * - keeps a lead surrogate between buffers (similar to toUBytes[]) + * - keeps a lead surrogate at the end of the stream, + * which the framework handles as truncated input + * - if the fromUnicode() implementation returns to the framework + * (ucnv.c ucnv_fromUnicode()), then the framework calls the callback + * for this code point + */ + UChar32 fromUChar32; + + /* + * value for ucnv_getMaxCharSize() + * + * usually simply copied from the static data, but ucnvmbcs.c modifies + * the value depending on the converter type and options + */ + int8_t maxBytesPerUChar; + + int8_t subCharLen; /* length of the codepage specific character sequence */ + int8_t invalidCharLength; + int8_t charErrorBufferLength; /* number of valid bytes in charErrorBuffer */ + + int8_t invalidUCharLength; + int8_t UCharErrorBufferLength; /* number of valid UChars in charErrorBuffer */ + + uint8_t subChar1; /* single-byte substitution character if different from subChar */ + UBool useSubChar1; + char invalidCharBuffer[UCNV_MAX_CHAR_LEN]; /* bytes from last error/callback situation */ + uint8_t charErrorBuffer[UCNV_ERROR_BUFFER_LENGTH]; /* codepage output from Error functions */ + UChar subUChars[UCNV_MAX_SUBCHAR_LEN/U_SIZEOF_UCHAR]; /* see subChars documentation */ + + UChar invalidUCharBuffer[U16_MAX_LENGTH]; /* UChars from last error/callback situation */ + UChar UCharErrorBuffer[UCNV_ERROR_BUFFER_LENGTH]; /* unicode output from Error functions */ + + /* fields for conversion extension */ + + /* store previous UChars/chars to continue partial matches */ + UChar32 preFromUFirstCP; /* >=0: partial match */ + UChar preFromU[UCNV_EXT_MAX_UCHARS]; + char preToU[UCNV_EXT_MAX_BYTES]; + int8_t preFromULength, preToULength; /* negative: replay */ + int8_t preToUFirstLength; /* length of first character */ + + /* new fields for ICU 4.0 */ + UConverterCallbackReason toUCallbackReason; /* (*fromCharErrorBehaviour) reason, set when error is detected */ +}; + +U_CDECL_END /* end of UConverter */ + +#define CONVERTER_FILE_EXTENSION ".cnv" + + +/** + * Return the number of all converter names. + * @param pErrorCode The error code + * @return the number of all converter names + */ +U_CFUNC uint16_t +ucnv_bld_countAvailableConverters(UErrorCode *pErrorCode); + +/** + * Return the (n)th converter name in mixed case, or NULL + * if there is none (typically, if the data cannot be loaded). + * 0<=indexisReferenceCounted + * and this function must be called inside umtx_lock(&cnvCacheMutex). + */ +U_CAPI void +ucnv_unload(UConverterSharedData *sharedData); + +/** + * Swap ICU .cnv conversion tables. See udataswp.h. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +ucnv_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +U_CAPI void U_EXPORT2 +ucnv_enableCleanup(void); + +#endif + +#endif /* _UCNV_BLD */ diff --git a/deps/icu-small/source/common/ucnv_cb.cpp b/deps/icu-small/source/common/ucnv_cb.cpp index 7bfde828704576..0ddc40abcd7285 100644 --- a/deps/icu-small/source/common/ucnv_cb.cpp +++ b/deps/icu-small/source/common/ucnv_cb.cpp @@ -1,261 +1,261 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2000-2006, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** - * ucnv_cb.c: - * External APIs for the ICU's codeset conversion library - * Helena Shih - * - * Modification History: - * - * Date Name Description - * 7/28/2000 srl Implementation - */ - -/** - * @name Character Conversion C API - * - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ucnv_cb.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" -#include "cmemory.h" - -/* need to update the offsets when the target moves. */ -/* Note: Recursion may occur in the cb functions, be sure to update the offsets correctly -if you don't use ucnv_cbXXX functions. Make sure you don't use the same callback within -the same call stack if the complexity arises. */ -U_CAPI void U_EXPORT2 -ucnv_cbFromUWriteBytes (UConverterFromUnicodeArgs *args, - const char* source, - int32_t length, - int32_t offsetIndex, - UErrorCode * err) -{ - if(U_FAILURE(*err)) { - return; - } - - ucnv_fromUWriteBytes( - args->converter, - source, length, - &args->target, args->targetLimit, - &args->offsets, offsetIndex, - err); -} - -U_CAPI void U_EXPORT2 -ucnv_cbFromUWriteUChars(UConverterFromUnicodeArgs *args, - const UChar** source, - const UChar* sourceLimit, - int32_t offsetIndex, - UErrorCode * err) -{ - /* - This is a fun one. Recursion can occur - we're basically going to - just retry shoving data through the same converter. Note, if you got - here through some kind of invalid sequence, you maybe should emit a - reset sequence of some kind and/or call ucnv_reset(). Since this - IS an actual conversion, take care that you've changed the callback - or the data, or you'll get an infinite loop. - - Please set the err value to something reasonable before calling - into this. - */ - - char *oldTarget; - - if(U_FAILURE(*err)) - { - return; - } - - oldTarget = args->target; - - ucnv_fromUnicode(args->converter, - &args->target, - args->targetLimit, - source, - sourceLimit, - NULL, /* no offsets */ - false, /* no flush */ - err); - - if(args->offsets) - { - while (args->target != oldTarget) /* if it moved at all.. */ - { - *(args->offsets)++ = offsetIndex; - oldTarget++; - } - } - - /* - Note, if you did something like used a Stop subcallback, things would get interesting. - In fact, here's where we want to return the partially consumed in-source! - */ - if(*err == U_BUFFER_OVERFLOW_ERROR) - /* && (*source < sourceLimit && args->target >= args->targetLimit) - -- S. Hrcek */ - { - /* Overflowed the target. Now, we'll write into the charErrorBuffer. - It's a fixed size. If we overflow it... Hmm */ - char *newTarget; - const char *newTargetLimit; - UErrorCode err2 = U_ZERO_ERROR; - - int8_t errBuffLen; - - errBuffLen = args->converter->charErrorBufferLength; - - /* start the new target at the first free slot in the errbuff.. */ - newTarget = (char *)(args->converter->charErrorBuffer + errBuffLen); - - newTargetLimit = (char *)(args->converter->charErrorBuffer + - sizeof(args->converter->charErrorBuffer)); - - if(newTarget >= newTargetLimit) - { - *err = U_INTERNAL_PROGRAM_ERROR; - return; - } - - /* We're going to tell the converter that the errbuff len is empty. - This prevents the existing errbuff from being 'flushed' out onto - itself. If the errbuff is needed by the converter this time, - we're hosed - we're out of space! */ - - args->converter->charErrorBufferLength = 0; - - ucnv_fromUnicode(args->converter, - &newTarget, - newTargetLimit, - source, - sourceLimit, - NULL, - false, - &err2); - - /* We can go ahead and overwrite the length here. We know just how - to recalculate it. */ - - args->converter->charErrorBufferLength = (int8_t)( - newTarget - (char*)args->converter->charErrorBuffer); - - if((newTarget >= newTargetLimit) || (err2 == U_BUFFER_OVERFLOW_ERROR)) - { - /* now we're REALLY in trouble. - Internal program error - callback shouldn't have written this much - data! - */ - *err = U_INTERNAL_PROGRAM_ERROR; - return; - } - /*else {*/ - /* sub errs could be invalid/truncated/illegal chars or w/e. - These might want to be passed on up.. But the problem is, we already - need to pass U_BUFFER_OVERFLOW_ERROR. That has to override these - other errs.. */ - - /* - if(U_FAILURE(err2)) - ?? - */ - /*}*/ - } -} - -U_CAPI void U_EXPORT2 -ucnv_cbFromUWriteSub (UConverterFromUnicodeArgs *args, - int32_t offsetIndex, - UErrorCode * err) -{ - UConverter *converter; - int32_t length; - - if(U_FAILURE(*err)) { - return; - } - converter = args->converter; - length = converter->subCharLen; - - if(length == 0) { - return; - } - - if(length < 0) { - /* - * Write/convert the substitution string. Its real length is -length. - * Unlike the escape callback, we need not change the converter's - * callback function because ucnv_setSubstString() verified that - * the string can be converted, so we will not get a conversion error - * and will not recurse. - * At worst we should get a U_BUFFER_OVERFLOW_ERROR. - */ - const UChar *source = (const UChar *)converter->subChars; - ucnv_cbFromUWriteUChars(args, &source, source - length, offsetIndex, err); - return; - } - - if(converter->sharedData->impl->writeSub!=NULL) { - converter->sharedData->impl->writeSub(args, offsetIndex, err); - } - else if(converter->subChar1!=0 && (uint16_t)converter->invalidUCharBuffer[0]<=(uint16_t)0xffu) { - /* - TODO: Is this untestable because the MBCS converter has a writeSub function to call - and the other converters don't use subChar1? - */ - ucnv_cbFromUWriteBytes(args, - (const char *)&converter->subChar1, 1, - offsetIndex, err); - } - else { - ucnv_cbFromUWriteBytes(args, - (const char *)converter->subChars, length, - offsetIndex, err); - } -} - -U_CAPI void U_EXPORT2 -ucnv_cbToUWriteUChars (UConverterToUnicodeArgs *args, - const UChar* source, - int32_t length, - int32_t offsetIndex, - UErrorCode * err) -{ - if(U_FAILURE(*err)) { - return; - } - - ucnv_toUWriteUChars( - args->converter, - source, length, - &args->target, args->targetLimit, - &args->offsets, offsetIndex, - err); -} - -U_CAPI void U_EXPORT2 -ucnv_cbToUWriteSub (UConverterToUnicodeArgs *args, - int32_t offsetIndex, - UErrorCode * err) -{ - static const UChar kSubstituteChar1 = 0x1A, kSubstituteChar = 0xFFFD; - - /* could optimize this case, just one uchar */ - if(args->converter->invalidCharLength == 1 && args->converter->subChar1 != 0) { - ucnv_cbToUWriteUChars(args, &kSubstituteChar1, 1, offsetIndex, err); - } else { - ucnv_cbToUWriteUChars(args, &kSubstituteChar, 1, offsetIndex, err); - } -} - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2000-2006, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** + * ucnv_cb.c: + * External APIs for the ICU's codeset conversion library + * Helena Shih + * + * Modification History: + * + * Date Name Description + * 7/28/2000 srl Implementation + */ + +/** + * @name Character Conversion C API + * + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ucnv_cb.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" +#include "cmemory.h" + +/* need to update the offsets when the target moves. */ +/* Note: Recursion may occur in the cb functions, be sure to update the offsets correctly +if you don't use ucnv_cbXXX functions. Make sure you don't use the same callback within +the same call stack if the complexity arises. */ +U_CAPI void U_EXPORT2 +ucnv_cbFromUWriteBytes (UConverterFromUnicodeArgs *args, + const char* source, + int32_t length, + int32_t offsetIndex, + UErrorCode * err) +{ + if(U_FAILURE(*err)) { + return; + } + + ucnv_fromUWriteBytes( + args->converter, + source, length, + &args->target, args->targetLimit, + &args->offsets, offsetIndex, + err); +} + +U_CAPI void U_EXPORT2 +ucnv_cbFromUWriteUChars(UConverterFromUnicodeArgs *args, + const char16_t** source, + const char16_t* sourceLimit, + int32_t offsetIndex, + UErrorCode * err) +{ + /* + This is a fun one. Recursion can occur - we're basically going to + just retry shoving data through the same converter. Note, if you got + here through some kind of invalid sequence, you maybe should emit a + reset sequence of some kind and/or call ucnv_reset(). Since this + IS an actual conversion, take care that you've changed the callback + or the data, or you'll get an infinite loop. + + Please set the err value to something reasonable before calling + into this. + */ + + char *oldTarget; + + if(U_FAILURE(*err)) + { + return; + } + + oldTarget = args->target; + + ucnv_fromUnicode(args->converter, + &args->target, + args->targetLimit, + source, + sourceLimit, + nullptr, /* no offsets */ + false, /* no flush */ + err); + + if(args->offsets) + { + while (args->target != oldTarget) /* if it moved at all.. */ + { + *(args->offsets)++ = offsetIndex; + oldTarget++; + } + } + + /* + Note, if you did something like used a Stop subcallback, things would get interesting. + In fact, here's where we want to return the partially consumed in-source! + */ + if(*err == U_BUFFER_OVERFLOW_ERROR) + /* && (*source < sourceLimit && args->target >= args->targetLimit) + -- S. Hrcek */ + { + /* Overflowed the target. Now, we'll write into the charErrorBuffer. + It's a fixed size. If we overflow it... Hmm */ + char *newTarget; + const char *newTargetLimit; + UErrorCode err2 = U_ZERO_ERROR; + + int8_t errBuffLen; + + errBuffLen = args->converter->charErrorBufferLength; + + /* start the new target at the first free slot in the errbuff.. */ + newTarget = (char *)(args->converter->charErrorBuffer + errBuffLen); + + newTargetLimit = (char *)(args->converter->charErrorBuffer + + sizeof(args->converter->charErrorBuffer)); + + if(newTarget >= newTargetLimit) + { + *err = U_INTERNAL_PROGRAM_ERROR; + return; + } + + /* We're going to tell the converter that the errbuff len is empty. + This prevents the existing errbuff from being 'flushed' out onto + itself. If the errbuff is needed by the converter this time, + we're hosed - we're out of space! */ + + args->converter->charErrorBufferLength = 0; + + ucnv_fromUnicode(args->converter, + &newTarget, + newTargetLimit, + source, + sourceLimit, + nullptr, + false, + &err2); + + /* We can go ahead and overwrite the length here. We know just how + to recalculate it. */ + + args->converter->charErrorBufferLength = (int8_t)( + newTarget - (char*)args->converter->charErrorBuffer); + + if((newTarget >= newTargetLimit) || (err2 == U_BUFFER_OVERFLOW_ERROR)) + { + /* now we're REALLY in trouble. + Internal program error - callback shouldn't have written this much + data! + */ + *err = U_INTERNAL_PROGRAM_ERROR; + return; + } + /*else {*/ + /* sub errs could be invalid/truncated/illegal chars or w/e. + These might want to be passed on up.. But the problem is, we already + need to pass U_BUFFER_OVERFLOW_ERROR. That has to override these + other errs.. */ + + /* + if(U_FAILURE(err2)) + ?? + */ + /*}*/ + } +} + +U_CAPI void U_EXPORT2 +ucnv_cbFromUWriteSub (UConverterFromUnicodeArgs *args, + int32_t offsetIndex, + UErrorCode * err) +{ + UConverter *converter; + int32_t length; + + if(U_FAILURE(*err)) { + return; + } + converter = args->converter; + length = converter->subCharLen; + + if(length == 0) { + return; + } + + if(length < 0) { + /* + * Write/convert the substitution string. Its real length is -length. + * Unlike the escape callback, we need not change the converter's + * callback function because ucnv_setSubstString() verified that + * the string can be converted, so we will not get a conversion error + * and will not recurse. + * At worst we should get a U_BUFFER_OVERFLOW_ERROR. + */ + const char16_t *source = (const char16_t *)converter->subChars; + ucnv_cbFromUWriteUChars(args, &source, source - length, offsetIndex, err); + return; + } + + if(converter->sharedData->impl->writeSub!=nullptr) { + converter->sharedData->impl->writeSub(args, offsetIndex, err); + } + else if(converter->subChar1!=0 && (uint16_t)converter->invalidUCharBuffer[0]<=(uint16_t)0xffu) { + /* + TODO: Is this untestable because the MBCS converter has a writeSub function to call + and the other converters don't use subChar1? + */ + ucnv_cbFromUWriteBytes(args, + (const char *)&converter->subChar1, 1, + offsetIndex, err); + } + else { + ucnv_cbFromUWriteBytes(args, + (const char *)converter->subChars, length, + offsetIndex, err); + } +} + +U_CAPI void U_EXPORT2 +ucnv_cbToUWriteUChars (UConverterToUnicodeArgs *args, + const char16_t* source, + int32_t length, + int32_t offsetIndex, + UErrorCode * err) +{ + if(U_FAILURE(*err)) { + return; + } + + ucnv_toUWriteUChars( + args->converter, + source, length, + &args->target, args->targetLimit, + &args->offsets, offsetIndex, + err); +} + +U_CAPI void U_EXPORT2 +ucnv_cbToUWriteSub (UConverterToUnicodeArgs *args, + int32_t offsetIndex, + UErrorCode * err) +{ + static const char16_t kSubstituteChar1 = 0x1A, kSubstituteChar = 0xFFFD; + + /* could optimize this case, just one uchar */ + if(args->converter->invalidCharLength == 1 && args->converter->subChar1 != 0) { + ucnv_cbToUWriteUChars(args, &kSubstituteChar1, 1, offsetIndex, err); + } else { + ucnv_cbToUWriteUChars(args, &kSubstituteChar, 1, offsetIndex, err); + } +} + +#endif diff --git a/deps/icu-small/source/common/ucnv_cnv.cpp b/deps/icu-small/source/common/ucnv_cnv.cpp index ea71acf92c7ae7..8e2fb13f0dc8f5 100644 --- a/deps/icu-small/source/common/ucnv_cnv.cpp +++ b/deps/icu-small/source/common/ucnv_cnv.cpp @@ -1,182 +1,182 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2004, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* uconv_cnv.c: -* Implements all the low level conversion functions -* T_UnicodeConverter_{to,from}Unicode_$ConversionType -* -* Change history: -* -* 06/29/2000 helena Major rewrite of the callback APIs. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ucnv_err.h" -#include "unicode/ucnv.h" -#include "unicode/uset.h" -#include "ucnv_cnv.h" -#include "ucnv_bld.h" -#include "cmemory.h" - -U_CFUNC void -ucnv_getCompleteUnicodeSet(const UConverter *cnv, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode) { - (void)cnv; - (void)which; - (void)pErrorCode; - sa->addRange(sa->set, 0, 0x10ffff); -} - -U_CFUNC void -ucnv_getNonSurrogateUnicodeSet(const UConverter *cnv, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode) { - (void)cnv; - (void)which; - (void)pErrorCode; - sa->addRange(sa->set, 0, 0xd7ff); - sa->addRange(sa->set, 0xe000, 0x10ffff); -} - -U_CFUNC void -ucnv_fromUWriteBytes(UConverter *cnv, - const char *bytes, int32_t length, - char **target, const char *targetLimit, - int32_t **offsets, - int32_t sourceIndex, - UErrorCode *pErrorCode) { - char *t=*target; - int32_t *o; - - /* write bytes */ - if(offsets==NULL || (o=*offsets)==NULL) { - while(length>0 && t0 && t0) { - if(cnv!=NULL) { - t=(char *)cnv->charErrorBuffer; - cnv->charErrorBufferLength=(int8_t)length; - do { - *t++=(uint8_t)*bytes++; - } while(--length>0); - } - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } -} - -U_CFUNC void -ucnv_toUWriteUChars(UConverter *cnv, - const UChar *uchars, int32_t length, - UChar **target, const UChar *targetLimit, - int32_t **offsets, - int32_t sourceIndex, - UErrorCode *pErrorCode) { - UChar *t=*target; - int32_t *o; - - /* write UChars */ - if(offsets==NULL || (o=*offsets)==NULL) { - while(length>0 && t0 && t0) { - if(cnv!=NULL) { - t=cnv->UCharErrorBuffer; - cnv->UCharErrorBufferLength=(int8_t)length; - do { - *t++=*uchars++; - } while(--length>0); - } - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } -} - -U_CFUNC void -ucnv_toUWriteCodePoint(UConverter *cnv, - UChar32 c, - UChar **target, const UChar *targetLimit, - int32_t **offsets, - int32_t sourceIndex, - UErrorCode *pErrorCode) { - UChar *t; - int32_t *o; - - t=*target; - - if(t=0) { - if(cnv!=NULL) { - int8_t i=0; - U16_APPEND_UNSAFE(cnv->UCharErrorBuffer, i, c); - cnv->UCharErrorBufferLength=i; - } - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } -} - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2004, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* uconv_cnv.c: +* Implements all the low level conversion functions +* T_UnicodeConverter_{to,from}Unicode_$ConversionType +* +* Change history: +* +* 06/29/2000 helena Major rewrite of the callback APIs. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ucnv_err.h" +#include "unicode/ucnv.h" +#include "unicode/uset.h" +#include "ucnv_cnv.h" +#include "ucnv_bld.h" +#include "cmemory.h" + +U_CFUNC void +ucnv_getCompleteUnicodeSet(const UConverter *cnv, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode) { + (void)cnv; + (void)which; + (void)pErrorCode; + sa->addRange(sa->set, 0, 0x10ffff); +} + +U_CFUNC void +ucnv_getNonSurrogateUnicodeSet(const UConverter *cnv, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode) { + (void)cnv; + (void)which; + (void)pErrorCode; + sa->addRange(sa->set, 0, 0xd7ff); + sa->addRange(sa->set, 0xe000, 0x10ffff); +} + +U_CFUNC void +ucnv_fromUWriteBytes(UConverter *cnv, + const char *bytes, int32_t length, + char **target, const char *targetLimit, + int32_t **offsets, + int32_t sourceIndex, + UErrorCode *pErrorCode) { + char *t=*target; + int32_t *o; + + /* write bytes */ + if(offsets==nullptr || (o=*offsets)==nullptr) { + while(length>0 && t0 && t0) { + if(cnv!=nullptr) { + t=(char *)cnv->charErrorBuffer; + cnv->charErrorBufferLength=(int8_t)length; + do { + *t++=(uint8_t)*bytes++; + } while(--length>0); + } + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } +} + +U_CFUNC void +ucnv_toUWriteUChars(UConverter *cnv, + const char16_t *uchars, int32_t length, + char16_t **target, const char16_t *targetLimit, + int32_t **offsets, + int32_t sourceIndex, + UErrorCode *pErrorCode) { + char16_t *t=*target; + int32_t *o; + + /* write UChars */ + if(offsets==nullptr || (o=*offsets)==nullptr) { + while(length>0 && t0 && t0) { + if(cnv!=nullptr) { + t=cnv->UCharErrorBuffer; + cnv->UCharErrorBufferLength=(int8_t)length; + do { + *t++=*uchars++; + } while(--length>0); + } + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } +} + +U_CFUNC void +ucnv_toUWriteCodePoint(UConverter *cnv, + UChar32 c, + char16_t **target, const char16_t *targetLimit, + int32_t **offsets, + int32_t sourceIndex, + UErrorCode *pErrorCode) { + char16_t *t; + int32_t *o; + + t=*target; + + if(t=0) { + if(cnv!=nullptr) { + int8_t i=0; + U16_APPEND_UNSAFE(cnv->UCharErrorBuffer, i, c); + cnv->UCharErrorBufferLength=i; + } + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } +} + +#endif diff --git a/deps/icu-small/source/common/ucnv_cnv.h b/deps/icu-small/source/common/ucnv_cnv.h index e89eebe54ed8c4..7320610297b2e8 100644 --- a/deps/icu-small/source/common/ucnv_cnv.h +++ b/deps/icu-small/source/common/ucnv_cnv.h @@ -1,323 +1,323 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* ucnv_cnv.h: -* Definitions for converter implementations. -* -* Modification History: -* -* Date Name Description -* 05/09/00 helena Added implementation to handle fallback mappings. -* 06/29/2000 helena Major rewrite of the callback APIs. -*/ - -#ifndef UCNV_CNV_H -#define UCNV_CNV_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/ucnv_err.h" -#include "unicode/uset.h" -#include "uset_imp.h" - -U_CDECL_BEGIN - -/* this is used in fromUnicode DBCS tables as an "unassigned" marker */ -#define missingCharMarker 0xFFFF - -/* - * #define missingUCharMarker 0xfffe - * - * commented out because there are actually two values used in toUnicode tables: - * U+fffe "unassigned" - * U+ffff "illegal" - */ - -/** Forward declaration, see ucnv_bld.h */ -struct UConverterSharedData; -typedef struct UConverterSharedData UConverterSharedData; - -/* function types for UConverterImpl ---------------------------------------- */ - -/* struct with arguments for UConverterLoad and ucnv_load() */ -typedef struct { - int32_t size; /* sizeof(UConverterLoadArgs) */ - int32_t nestedLoads; /* count nested ucnv_load() calls */ - UBool onlyTestIsLoadable; /* input: don't actually load */ - UBool reserved0; /* reserved - for good alignment of the pointers */ - int16_t reserved; /* reserved - for good alignment of the pointers */ - uint32_t options; - const char *pkg, *name, *locale; -} UConverterLoadArgs; - -#define UCNV_LOAD_ARGS_INITIALIZER \ - { (int32_t)sizeof(UConverterLoadArgs), 0, false, false, 0, 0, NULL, NULL, NULL } - -typedef void (*UConverterLoad) (UConverterSharedData *sharedData, - UConverterLoadArgs *pArgs, - const uint8_t *raw, UErrorCode *pErrorCode); -typedef void (*UConverterUnload) (UConverterSharedData *sharedData); - -typedef void (*UConverterOpen) (UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *pErrorCode); -typedef void (*UConverterClose) (UConverter *cnv); - -typedef enum UConverterResetChoice { - UCNV_RESET_BOTH, - UCNV_RESET_TO_UNICODE, - UCNV_RESET_FROM_UNICODE -} UConverterResetChoice; - -typedef void (*UConverterReset) (UConverter *cnv, UConverterResetChoice choice); - -/* - * Converter implementation function(s) for ucnv_toUnicode(). - * If the toUnicodeWithOffsets function pointer is NULL, - * then the toUnicode function will be used and the offsets will be set to -1. - * - * Must maintain state across buffers. Use toUBytes[toULength] for partial input - * sequences; it will be checked in ucnv.c at the end of the input stream - * to detect truncated input. - * Some converters may need additional detection and may then set U_TRUNCATED_CHAR_FOUND. - * - * The toUnicodeWithOffsets must write exactly as many offset values as target - * units. Write offset values of -1 for when the source index corresponding to - * the output unit is not known (e.g., the character started in an earlier buffer). - * The pArgs->offsets pointer need not be moved forward. - * - * At function return, either one of the following conditions must be true: - * - U_BUFFER_OVERFLOW_ERROR and the target is full: target==targetLimit - * - another error code with toUBytes[toULength] set to the offending input - * - no error, and the source is consumed: source==sourceLimit - * - * The ucnv.c code will handle the end of the input (reset) - * (reset, and truncation detection) and callbacks. - */ -typedef void (*UConverterToUnicode) (UConverterToUnicodeArgs *, UErrorCode *); - -/* - * Same rules as for UConverterToUnicode. - * A lead surrogate is kept in fromUChar32 across buffers, and if an error - * occurs, then the offending input code point must be put into fromUChar32 - * as well. - */ -typedef void (*UConverterFromUnicode) (UConverterFromUnicodeArgs *, UErrorCode *); - -/* - * Converter implementation function for ucnv_convertEx(), for direct conversion - * between two charsets without pivoting through UTF-16. - * The rules are the same as for UConverterToUnicode and UConverterFromUnicode. - * In addition, - * - The toUnicode side must behave and keep state exactly like the - * UConverterToUnicode implementation for the same source charset. - * - A U_USING_DEFAULT_WARNING can be set to request to temporarily fall back - * to pivoting. When this function is called, the conversion framework makes - * sure that this warning is not set on input. - * - Continuing a partial match and flushing the toUnicode replay buffer - * are handled by pivoting, using the toUnicode and fromUnicode functions. - */ -typedef void (*UConverterConvert) (UConverterFromUnicodeArgs *pFromUArgs, - UConverterToUnicodeArgs *pToUArgs, - UErrorCode *pErrorCode); - -/* - * Converter implementation function for ucnv_getNextUChar(). - * If the function pointer is NULL, then the toUnicode function will be used. - * - * Will be called at a character boundary (toULength==0). - * May return with - * - U_INDEX_OUTOFBOUNDS_ERROR if there was no output for the input - * (the return value will be ignored) - * - U_TRUNCATED_CHAR_FOUND or another error code (never U_BUFFER_OVERFLOW_ERROR!) - * with toUBytes[toULength] set to the offending input - * (the return value will be ignored) - * - return UCNV_GET_NEXT_UCHAR_USE_TO_U, without moving the source pointer, - * to indicate that the ucnv.c code shall call the toUnicode function instead - * - return a real code point result - * - * Unless UCNV_GET_NEXT_UCHAR_USE_TO_U is returned, the source bytes must be consumed. - * - * The ucnv.c code will handle the end of the input (reset) - * (except for truncation detection!) and callbacks. - */ -typedef UChar32 (*UConverterGetNextUChar) (UConverterToUnicodeArgs *, UErrorCode *); - -typedef void (*UConverterGetStarters)(const UConverter* converter, - UBool starters[256], - UErrorCode *pErrorCode); - -/* If this function pointer is null or if the function returns null - * the name field in static data struct should be returned by - * ucnv_getName() API function - */ -typedef const char * (*UConverterGetName) (const UConverter *cnv); - -/** - * Write the codepage substitution character. - * If this function is not set, then ucnv_cbFromUWriteSub() writes - * the substitution character from UConverter. - * For stateful converters, it is typically necessary to handle this - * specifically for the converter in order to properly maintain the state. - */ -typedef void (*UConverterWriteSub) (UConverterFromUnicodeArgs *pArgs, int32_t offsetIndex, UErrorCode *pErrorCode); - -/** - * For converter-specific safeClone processing - * If this function is not set, then ucnv_safeClone assumes that the converter has no private data that changes - * after the converter is done opening. - * If this function is set, then it is called just after a memcpy() of - * converter data to the new, empty converter, and is expected to set up - * the initial state of the converter. It is not expected to increment the - * reference counts of the standard data types such as the shared data. - */ -typedef UConverter * (*UConverterSafeClone) (const UConverter *cnv, - void *stackBuffer, - int32_t *pBufferSize, - UErrorCode *status); - -/** - * Filters for some ucnv_getUnicodeSet() implementation code. - */ -typedef enum UConverterSetFilter { - UCNV_SET_FILTER_NONE, - UCNV_SET_FILTER_DBCS_ONLY, - UCNV_SET_FILTER_2022_CN, - UCNV_SET_FILTER_SJIS, - UCNV_SET_FILTER_GR94DBCS, - UCNV_SET_FILTER_HZ, - UCNV_SET_FILTER_COUNT -} UConverterSetFilter; - -/** - * Fills the set of Unicode code points that can be converted by an ICU converter. - * The API function ucnv_getUnicodeSet() clears the USet before calling - * the converter's getUnicodeSet() implementation; the converter should only - * add the appropriate code points to allow recursive use. - * For example, the ISO-2022-JP converter will call each subconverter's - * getUnicodeSet() implementation to consecutively add code points to - * the same USet, which will result in a union of the sets of all subconverters. - * - * For more documentation, see ucnv_getUnicodeSet() in ucnv.h. - */ -typedef void (*UConverterGetUnicodeSet) (const UConverter *cnv, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode); - -UBool CONVERSION_U_SUCCESS (UErrorCode err); - -/** - * UConverterImpl contains all the data and functions for a converter type. - * Its function pointers work much like a C++ vtable. - * Many converter types need to define only a subset of the functions; - * when a function pointer is NULL, then a default action will be performed. - * - * Every converter type must implement toUnicode, fromUnicode, and getNextUChar, - * otherwise the converter may crash. - * Every converter type that has variable-length codepage sequences should - * also implement toUnicodeWithOffsets and fromUnicodeWithOffsets for - * correct offset handling. - * All other functions may or may not be implemented - it depends only on - * whether the converter type needs them. - * - * When open() fails, then close() will be called, if present. - */ -struct UConverterImpl { - UConverterType type; - - UConverterLoad load; - UConverterUnload unload; - - UConverterOpen open; - UConverterClose close; - UConverterReset reset; - - UConverterToUnicode toUnicode; - UConverterToUnicode toUnicodeWithOffsets; - UConverterFromUnicode fromUnicode; - UConverterFromUnicode fromUnicodeWithOffsets; - UConverterGetNextUChar getNextUChar; - - UConverterGetStarters getStarters; - UConverterGetName getName; - UConverterWriteSub writeSub; - UConverterSafeClone safeClone; - UConverterGetUnicodeSet getUnicodeSet; - - UConverterConvert toUTF8; - UConverterConvert fromUTF8; -}; - -extern const UConverterSharedData - _MBCSData, _Latin1Data, - _UTF8Data, _UTF16BEData, _UTF16LEData, _UTF32BEData, _UTF32LEData, - _ISO2022Data, - _LMBCSData1,_LMBCSData2, _LMBCSData3, _LMBCSData4, _LMBCSData5, _LMBCSData6, - _LMBCSData8,_LMBCSData11,_LMBCSData16,_LMBCSData17,_LMBCSData18,_LMBCSData19, - _HZData,_ISCIIData, _SCSUData, _ASCIIData, - _UTF7Data, _Bocu1Data, _UTF16Data, _UTF32Data, _CESU8Data, _IMAPData, _CompoundTextData; - -U_CDECL_END - -/** Always use fallbacks from codepage to Unicode */ -#define TO_U_USE_FALLBACK(useFallback) true -#define UCNV_TO_U_USE_FALLBACK(cnv) true - -/** Use fallbacks from Unicode to codepage when cnv->useFallback or for private-use code points */ -#define IS_PRIVATE_USE(c) ((uint32_t)((c)-0xe000)<0x1900 || (uint32_t)((c)-0xf0000)<0x20000) -#define FROM_U_USE_FALLBACK(useFallback, c) ((useFallback) || IS_PRIVATE_USE(c)) -#define UCNV_FROM_U_USE_FALLBACK(cnv, c) FROM_U_USE_FALLBACK((cnv)->useFallback, c) - -/** - * Magic number for ucnv_getNextUChar(), returned by a - * getNextUChar() implementation to indicate to use the converter's toUnicode() - * instead of the native function. - * @internal - */ -#define UCNV_GET_NEXT_UCHAR_USE_TO_U -9 - -U_CFUNC void -ucnv_getCompleteUnicodeSet(const UConverter *cnv, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode); - -U_CFUNC void -ucnv_getNonSurrogateUnicodeSet(const UConverter *cnv, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode); - -U_CFUNC void -ucnv_fromUWriteBytes(UConverter *cnv, - const char *bytes, int32_t length, - char **target, const char *targetLimit, - int32_t **offsets, - int32_t sourceIndex, - UErrorCode *pErrorCode); -U_CFUNC void -ucnv_toUWriteUChars(UConverter *cnv, - const UChar *uchars, int32_t length, - UChar **target, const UChar *targetLimit, - int32_t **offsets, - int32_t sourceIndex, - UErrorCode *pErrorCode); - -U_CFUNC void -ucnv_toUWriteCodePoint(UConverter *cnv, - UChar32 c, - UChar **target, const UChar *targetLimit, - int32_t **offsets, - int32_t sourceIndex, - UErrorCode *pErrorCode); - -#endif - -#endif /* UCNV_CNV */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* ucnv_cnv.h: +* Definitions for converter implementations. +* +* Modification History: +* +* Date Name Description +* 05/09/00 helena Added implementation to handle fallback mappings. +* 06/29/2000 helena Major rewrite of the callback APIs. +*/ + +#ifndef UCNV_CNV_H +#define UCNV_CNV_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/ucnv_err.h" +#include "unicode/uset.h" +#include "uset_imp.h" + +U_CDECL_BEGIN + +/* this is used in fromUnicode DBCS tables as an "unassigned" marker */ +#define missingCharMarker 0xFFFF + +/* + * #define missingUCharMarker 0xfffe + * + * commented out because there are actually two values used in toUnicode tables: + * U+fffe "unassigned" + * U+ffff "illegal" + */ + +/** Forward declaration, see ucnv_bld.h */ +struct UConverterSharedData; +typedef struct UConverterSharedData UConverterSharedData; + +/* function types for UConverterImpl ---------------------------------------- */ + +/* struct with arguments for UConverterLoad and ucnv_load() */ +typedef struct { + int32_t size; /* sizeof(UConverterLoadArgs) */ + int32_t nestedLoads; /* count nested ucnv_load() calls */ + UBool onlyTestIsLoadable; /* input: don't actually load */ + UBool reserved0; /* reserved - for good alignment of the pointers */ + int16_t reserved; /* reserved - for good alignment of the pointers */ + uint32_t options; + const char *pkg, *name, *locale; +} UConverterLoadArgs; + +#define UCNV_LOAD_ARGS_INITIALIZER \ + { (int32_t)sizeof(UConverterLoadArgs), 0, false, false, 0, 0, NULL, NULL, NULL } + +typedef void (*UConverterLoad) (UConverterSharedData *sharedData, + UConverterLoadArgs *pArgs, + const uint8_t *raw, UErrorCode *pErrorCode); +typedef void (*UConverterUnload) (UConverterSharedData *sharedData); + +typedef void (*UConverterOpen) (UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *pErrorCode); +typedef void (*UConverterClose) (UConverter *cnv); + +typedef enum UConverterResetChoice { + UCNV_RESET_BOTH, + UCNV_RESET_TO_UNICODE, + UCNV_RESET_FROM_UNICODE +} UConverterResetChoice; + +typedef void (*UConverterReset) (UConverter *cnv, UConverterResetChoice choice); + +/* + * Converter implementation function(s) for ucnv_toUnicode(). + * If the toUnicodeWithOffsets function pointer is NULL, + * then the toUnicode function will be used and the offsets will be set to -1. + * + * Must maintain state across buffers. Use toUBytes[toULength] for partial input + * sequences; it will be checked in ucnv.c at the end of the input stream + * to detect truncated input. + * Some converters may need additional detection and may then set U_TRUNCATED_CHAR_FOUND. + * + * The toUnicodeWithOffsets must write exactly as many offset values as target + * units. Write offset values of -1 for when the source index corresponding to + * the output unit is not known (e.g., the character started in an earlier buffer). + * The pArgs->offsets pointer need not be moved forward. + * + * At function return, either one of the following conditions must be true: + * - U_BUFFER_OVERFLOW_ERROR and the target is full: target==targetLimit + * - another error code with toUBytes[toULength] set to the offending input + * - no error, and the source is consumed: source==sourceLimit + * + * The ucnv.c code will handle the end of the input (reset) + * (reset, and truncation detection) and callbacks. + */ +typedef void (*UConverterToUnicode) (UConverterToUnicodeArgs *, UErrorCode *); + +/* + * Same rules as for UConverterToUnicode. + * A lead surrogate is kept in fromUChar32 across buffers, and if an error + * occurs, then the offending input code point must be put into fromUChar32 + * as well. + */ +typedef void (*UConverterFromUnicode) (UConverterFromUnicodeArgs *, UErrorCode *); + +/* + * Converter implementation function for ucnv_convertEx(), for direct conversion + * between two charsets without pivoting through UTF-16. + * The rules are the same as for UConverterToUnicode and UConverterFromUnicode. + * In addition, + * - The toUnicode side must behave and keep state exactly like the + * UConverterToUnicode implementation for the same source charset. + * - A U_USING_DEFAULT_WARNING can be set to request to temporarily fall back + * to pivoting. When this function is called, the conversion framework makes + * sure that this warning is not set on input. + * - Continuing a partial match and flushing the toUnicode replay buffer + * are handled by pivoting, using the toUnicode and fromUnicode functions. + */ +typedef void (*UConverterConvert) (UConverterFromUnicodeArgs *pFromUArgs, + UConverterToUnicodeArgs *pToUArgs, + UErrorCode *pErrorCode); + +/* + * Converter implementation function for ucnv_getNextUChar(). + * If the function pointer is NULL, then the toUnicode function will be used. + * + * Will be called at a character boundary (toULength==0). + * May return with + * - U_INDEX_OUTOFBOUNDS_ERROR if there was no output for the input + * (the return value will be ignored) + * - U_TRUNCATED_CHAR_FOUND or another error code (never U_BUFFER_OVERFLOW_ERROR!) + * with toUBytes[toULength] set to the offending input + * (the return value will be ignored) + * - return UCNV_GET_NEXT_UCHAR_USE_TO_U, without moving the source pointer, + * to indicate that the ucnv.c code shall call the toUnicode function instead + * - return a real code point result + * + * Unless UCNV_GET_NEXT_UCHAR_USE_TO_U is returned, the source bytes must be consumed. + * + * The ucnv.c code will handle the end of the input (reset) + * (except for truncation detection!) and callbacks. + */ +typedef UChar32 (*UConverterGetNextUChar) (UConverterToUnicodeArgs *, UErrorCode *); + +typedef void (*UConverterGetStarters)(const UConverter* converter, + UBool starters[256], + UErrorCode *pErrorCode); + +/* If this function pointer is null or if the function returns null + * the name field in static data struct should be returned by + * ucnv_getName() API function + */ +typedef const char * (*UConverterGetName) (const UConverter *cnv); + +/** + * Write the codepage substitution character. + * If this function is not set, then ucnv_cbFromUWriteSub() writes + * the substitution character from UConverter. + * For stateful converters, it is typically necessary to handle this + * specifically for the converter in order to properly maintain the state. + */ +typedef void (*UConverterWriteSub) (UConverterFromUnicodeArgs *pArgs, int32_t offsetIndex, UErrorCode *pErrorCode); + +/** + * For converter-specific safeClone processing + * If this function is not set, then ucnv_safeClone assumes that the converter has no private data that changes + * after the converter is done opening. + * If this function is set, then it is called just after a memcpy() of + * converter data to the new, empty converter, and is expected to set up + * the initial state of the converter. It is not expected to increment the + * reference counts of the standard data types such as the shared data. + */ +typedef UConverter * (*UConverterSafeClone) (const UConverter *cnv, + void *stackBuffer, + int32_t *pBufferSize, + UErrorCode *status); + +/** + * Filters for some ucnv_getUnicodeSet() implementation code. + */ +typedef enum UConverterSetFilter { + UCNV_SET_FILTER_NONE, + UCNV_SET_FILTER_DBCS_ONLY, + UCNV_SET_FILTER_2022_CN, + UCNV_SET_FILTER_SJIS, + UCNV_SET_FILTER_GR94DBCS, + UCNV_SET_FILTER_HZ, + UCNV_SET_FILTER_COUNT +} UConverterSetFilter; + +/** + * Fills the set of Unicode code points that can be converted by an ICU converter. + * The API function ucnv_getUnicodeSet() clears the USet before calling + * the converter's getUnicodeSet() implementation; the converter should only + * add the appropriate code points to allow recursive use. + * For example, the ISO-2022-JP converter will call each subconverter's + * getUnicodeSet() implementation to consecutively add code points to + * the same USet, which will result in a union of the sets of all subconverters. + * + * For more documentation, see ucnv_getUnicodeSet() in ucnv.h. + */ +typedef void (*UConverterGetUnicodeSet) (const UConverter *cnv, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode); + +UBool CONVERSION_U_SUCCESS (UErrorCode err); + +/** + * UConverterImpl contains all the data and functions for a converter type. + * Its function pointers work much like a C++ vtable. + * Many converter types need to define only a subset of the functions; + * when a function pointer is NULL, then a default action will be performed. + * + * Every converter type must implement toUnicode, fromUnicode, and getNextUChar, + * otherwise the converter may crash. + * Every converter type that has variable-length codepage sequences should + * also implement toUnicodeWithOffsets and fromUnicodeWithOffsets for + * correct offset handling. + * All other functions may or may not be implemented - it depends only on + * whether the converter type needs them. + * + * When open() fails, then close() will be called, if present. + */ +struct UConverterImpl { + UConverterType type; + + UConverterLoad load; + UConverterUnload unload; + + UConverterOpen open; + UConverterClose close; + UConverterReset reset; + + UConverterToUnicode toUnicode; + UConverterToUnicode toUnicodeWithOffsets; + UConverterFromUnicode fromUnicode; + UConverterFromUnicode fromUnicodeWithOffsets; + UConverterGetNextUChar getNextUChar; + + UConverterGetStarters getStarters; + UConverterGetName getName; + UConverterWriteSub writeSub; + UConverterSafeClone safeClone; + UConverterGetUnicodeSet getUnicodeSet; + + UConverterConvert toUTF8; + UConverterConvert fromUTF8; +}; + +extern const UConverterSharedData + _MBCSData, _Latin1Data, + _UTF8Data, _UTF16BEData, _UTF16LEData, _UTF32BEData, _UTF32LEData, + _ISO2022Data, + _LMBCSData1,_LMBCSData2, _LMBCSData3, _LMBCSData4, _LMBCSData5, _LMBCSData6, + _LMBCSData8,_LMBCSData11,_LMBCSData16,_LMBCSData17,_LMBCSData18,_LMBCSData19, + _HZData,_ISCIIData, _SCSUData, _ASCIIData, + _UTF7Data, _Bocu1Data, _UTF16Data, _UTF32Data, _CESU8Data, _IMAPData, _CompoundTextData; + +U_CDECL_END + +/** Always use fallbacks from codepage to Unicode */ +#define TO_U_USE_FALLBACK(useFallback) true +#define UCNV_TO_U_USE_FALLBACK(cnv) true + +/** Use fallbacks from Unicode to codepage when cnv->useFallback or for private-use code points */ +#define IS_PRIVATE_USE(c) ((uint32_t)((c)-0xe000)<0x1900 || (uint32_t)((c)-0xf0000)<0x20000) +#define FROM_U_USE_FALLBACK(useFallback, c) ((useFallback) || IS_PRIVATE_USE(c)) +#define UCNV_FROM_U_USE_FALLBACK(cnv, c) FROM_U_USE_FALLBACK((cnv)->useFallback, c) + +/** + * Magic number for ucnv_getNextUChar(), returned by a + * getNextUChar() implementation to indicate to use the converter's toUnicode() + * instead of the native function. + * @internal + */ +#define UCNV_GET_NEXT_UCHAR_USE_TO_U -9 + +U_CFUNC void +ucnv_getCompleteUnicodeSet(const UConverter *cnv, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode); + +U_CFUNC void +ucnv_getNonSurrogateUnicodeSet(const UConverter *cnv, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode); + +U_CFUNC void +ucnv_fromUWriteBytes(UConverter *cnv, + const char *bytes, int32_t length, + char **target, const char *targetLimit, + int32_t **offsets, + int32_t sourceIndex, + UErrorCode *pErrorCode); +U_CFUNC void +ucnv_toUWriteUChars(UConverter *cnv, + const UChar *uchars, int32_t length, + UChar **target, const UChar *targetLimit, + int32_t **offsets, + int32_t sourceIndex, + UErrorCode *pErrorCode); + +U_CFUNC void +ucnv_toUWriteCodePoint(UConverter *cnv, + UChar32 c, + UChar **target, const UChar *targetLimit, + int32_t **offsets, + int32_t sourceIndex, + UErrorCode *pErrorCode); + +#endif + +#endif /* UCNV_CNV */ diff --git a/deps/icu-small/source/common/ucnv_ct.cpp b/deps/icu-small/source/common/ucnv_ct.cpp index c12e982b88b27f..8c631f3ebf60bb 100644 --- a/deps/icu-small/source/common/ucnv_ct.cpp +++ b/deps/icu-small/source/common/ucnv_ct.cpp @@ -1,646 +1,646 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2010-2015, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: ucnv_ct.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010Dec09 -* created by: Michael Ow -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/uset.h" -#include "unicode/ucnv_err.h" -#include "unicode/ucnv_cb.h" -#include "unicode/utf16.h" -#include "ucnv_imp.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" -#include "ucnvmbcs.h" -#include "cstring.h" -#include "cmemory.h" - -typedef enum { - INVALID = -2, - DO_SEARCH = -1, - - COMPOUND_TEXT_SINGLE_0 = 0, - COMPOUND_TEXT_SINGLE_1 = 1, - COMPOUND_TEXT_SINGLE_2 = 2, - COMPOUND_TEXT_SINGLE_3 = 3, - - COMPOUND_TEXT_DOUBLE_1 = 4, - COMPOUND_TEXT_DOUBLE_2 = 5, - COMPOUND_TEXT_DOUBLE_3 = 6, - COMPOUND_TEXT_DOUBLE_4 = 7, - COMPOUND_TEXT_DOUBLE_5 = 8, - COMPOUND_TEXT_DOUBLE_6 = 9, - COMPOUND_TEXT_DOUBLE_7 = 10, - - COMPOUND_TEXT_TRIPLE_DOUBLE = 11, - - IBM_915 = 12, - IBM_916 = 13, - IBM_914 = 14, - IBM_874 = 15, - IBM_912 = 16, - IBM_913 = 17, - ISO_8859_14 = 18, - IBM_923 = 19, - NUM_OF_CONVERTERS = 20 -} COMPOUND_TEXT_CONVERTERS; - -#define SEARCH_LENGTH 12 - -static const uint8_t escSeqCompoundText[NUM_OF_CONVERTERS][5] = { - /* Single */ - { 0x1B, 0x2D, 0x41, 0, 0 }, - { 0x1B, 0x2D, 0x4D, 0, 0 }, - { 0x1B, 0x2D, 0x46, 0, 0 }, - { 0x1B, 0x2D, 0x47, 0, 0 }, - - /* Double */ - { 0x1B, 0x24, 0x29, 0x41, 0 }, - { 0x1B, 0x24, 0x29, 0x42, 0 }, - { 0x1B, 0x24, 0x29, 0x43, 0 }, - { 0x1B, 0x24, 0x29, 0x44, 0 }, - { 0x1B, 0x24, 0x29, 0x47, 0 }, - { 0x1B, 0x24, 0x29, 0x48, 0 }, - { 0x1B, 0x24, 0x29, 0x49, 0 }, - - /* Triple/Double */ - { 0x1B, 0x25, 0x47, 0, 0 }, - - /*IBM-915*/ - { 0x1B, 0x2D, 0x4C, 0, 0 }, - /*IBM-916*/ - { 0x1B, 0x2D, 0x48, 0, 0 }, - /*IBM-914*/ - { 0x1B, 0x2D, 0x44, 0, 0 }, - /*IBM-874*/ - { 0x1B, 0x2D, 0x54, 0, 0 }, - /*IBM-912*/ - { 0x1B, 0x2D, 0x42, 0, 0 }, - /* IBM-913 */ - { 0x1B, 0x2D, 0x43, 0, 0 }, - /* ISO-8859_14 */ - { 0x1B, 0x2D, 0x5F, 0, 0 }, - /* IBM-923 */ - { 0x1B, 0x2D, 0x62, 0, 0 }, -}; - -#define ESC_START 0x1B - -#define isASCIIRange(codepoint) \ - ((codepoint == 0x0000) || (codepoint == 0x0009) || (codepoint == 0x000A) || \ - (codepoint >= 0x0020 && codepoint <= 0x007f) || (codepoint >= 0x00A0 && codepoint <= 0x00FF)) - -#define isIBM915(codepoint) \ - ((codepoint >= 0x0401 && codepoint <= 0x045F) || (codepoint == 0x2116)) - -#define isIBM916(codepoint) \ - ((codepoint >= 0x05D0 && codepoint <= 0x05EA) || (codepoint == 0x2017) || (codepoint == 0x203E)) - -#define isCompoundS3(codepoint) \ - ((codepoint == 0x060C) || (codepoint == 0x061B) || (codepoint == 0x061F) || (codepoint >= 0x0621 && codepoint <= 0x063A) || \ - (codepoint >= 0x0640 && codepoint <= 0x0652) || (codepoint >= 0x0660 && codepoint <= 0x066D) || (codepoint == 0x200B) || \ - (codepoint >= 0x0FE70 && codepoint <= 0x0FE72) || (codepoint == 0x0FE74) || (codepoint >= 0x0FE76 && codepoint <= 0x0FEBE)) - -#define isCompoundS2(codepoint) \ - ((codepoint == 0x02BC) || (codepoint == 0x02BD) || (codepoint >= 0x0384 && codepoint <= 0x03CE) || (codepoint == 0x2015)) - -#define isIBM914(codepoint) \ - ((codepoint == 0x0100) || (codepoint == 0x0101) || (codepoint == 0x0112) || (codepoint == 0x0113) || (codepoint == 0x0116) || (codepoint == 0x0117) || \ - (codepoint == 0x0122) || (codepoint == 0x0123) || (codepoint >= 0x0128 && codepoint <= 0x012B) || (codepoint == 0x012E) || (codepoint == 0x012F) || \ - (codepoint >= 0x0136 && codepoint <= 0x0138) || (codepoint == 0x013B) || (codepoint == 0x013C) || (codepoint == 0x0145) || (codepoint == 0x0146) || \ - (codepoint >= 0x014A && codepoint <= 0x014D) || (codepoint == 0x0156) || (codepoint == 0x0157) || (codepoint >= 0x0166 && codepoint <= 0x016B) || \ - (codepoint == 0x0172) || (codepoint == 0x0173)) - -#define isIBM874(codepoint) \ - ((codepoint >= 0x0E01 && codepoint <= 0x0E3A) || (codepoint >= 0x0E3F && codepoint <= 0x0E5B)) - -#define isIBM912(codepoint) \ - ((codepoint >= 0x0102 && codepoint <= 0x0107) || (codepoint >= 0x010C && codepoint <= 0x0111) || (codepoint >= 0x0118 && codepoint <= 0x011B) || \ - (codepoint == 0x0139) || (codepoint == 0x013A) || (codepoint == 0x013D) || (codepoint == 0x013E) || (codepoint >= 0x0141 && codepoint <= 0x0144) || \ - (codepoint == 0x0147) || (codepoint == 0x0147) || (codepoint == 0x0150) || (codepoint == 0x0151) || (codepoint == 0x0154) || (codepoint == 0x0155) || \ - (codepoint >= 0x0158 && codepoint <= 0x015B) || (codepoint == 0x015E) || (codepoint == 0x015F) || (codepoint >= 0x0160 && codepoint <= 0x0165) || \ - (codepoint == 0x016E) || (codepoint == 0x016F) || (codepoint == 0x0170) || (codepoint == 0x0171) || (codepoint >= 0x0179 && codepoint <= 0x017E) || \ - (codepoint == 0x02C7) || (codepoint == 0x02D8) || (codepoint == 0x02D9) || (codepoint == 0x02DB) || (codepoint == 0x02DD)) - -#define isIBM913(codepoint) \ - ((codepoint >= 0x0108 && codepoint <= 0x010B) || (codepoint == 0x011C) || \ - (codepoint == 0x011D) || (codepoint == 0x0120) || (codepoint == 0x0121) || \ - (codepoint >= 0x0124 && codepoint <= 0x0127) || (codepoint == 0x0134) || (codepoint == 0x0135) || \ - (codepoint == 0x015C) || (codepoint == 0x015D) || (codepoint == 0x016C) || (codepoint == 0x016D)) - -#define isCompoundS1(codepoint) \ - ((codepoint == 0x011E) || (codepoint == 0x011F) || (codepoint == 0x0130) || \ - (codepoint == 0x0131) || (codepoint >= 0x0218 && codepoint <= 0x021B)) - -#define isISO8859_14(codepoint) \ - ((codepoint >= 0x0174 && codepoint <= 0x0177) || (codepoint == 0x1E0A) || \ - (codepoint == 0x1E0B) || (codepoint == 0x1E1E) || (codepoint == 0x1E1F) || \ - (codepoint == 0x1E40) || (codepoint == 0x1E41) || (codepoint == 0x1E56) || \ - (codepoint == 0x1E57) || (codepoint == 0x1E60) || (codepoint == 0x1E61) || \ - (codepoint == 0x1E6A) || (codepoint == 0x1E6B) || (codepoint == 0x1EF2) || \ - (codepoint == 0x1EF3) || (codepoint >= 0x1E80 && codepoint <= 0x1E85)) - -#define isIBM923(codepoint) \ - ((codepoint == 0x0152) || (codepoint == 0x0153) || (codepoint == 0x0178) || (codepoint == 0x20AC)) - - -typedef struct{ - UConverterSharedData *myConverterArray[NUM_OF_CONVERTERS]; - COMPOUND_TEXT_CONVERTERS state; -} UConverterDataCompoundText; - -/*********** Compound Text Converter Protos ***********/ -U_CDECL_BEGIN -static void U_CALLCONV -_CompoundTextOpen(UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *errorCode); - -static void U_CALLCONV - _CompoundTextClose(UConverter *converter); - -static void U_CALLCONV -_CompoundTextReset(UConverter *converter, UConverterResetChoice choice); - -static const char* U_CALLCONV -_CompoundTextgetName(const UConverter* cnv); - - -static int32_t findNextEsc(const char *source, const char *sourceLimit) { - int32_t length = static_cast(sourceLimit - source); - int32_t i; - for (i = 1; i < length; i++) { - if (*(source + i) == 0x1B) { - return i; - } - } - - return length; -} - -static COMPOUND_TEXT_CONVERTERS getState(int codepoint) { - COMPOUND_TEXT_CONVERTERS state = DO_SEARCH; - - if (isASCIIRange(codepoint)) { - state = COMPOUND_TEXT_SINGLE_0; - } else if (isIBM912(codepoint)) { - state = IBM_912; - }else if (isIBM913(codepoint)) { - state = IBM_913; - } else if (isISO8859_14(codepoint)) { - state = ISO_8859_14; - } else if (isIBM923(codepoint)) { - state = IBM_923; - } else if (isIBM874(codepoint)) { - state = IBM_874; - } else if (isIBM914(codepoint)) { - state = IBM_914; - } else if (isCompoundS2(codepoint)) { - state = COMPOUND_TEXT_SINGLE_2; - } else if (isCompoundS3(codepoint)) { - state = COMPOUND_TEXT_SINGLE_3; - } else if (isIBM916(codepoint)) { - state = IBM_916; - } else if (isIBM915(codepoint)) { - state = IBM_915; - } else if (isCompoundS1(codepoint)) { - state = COMPOUND_TEXT_SINGLE_1; - } - - return state; -} - -static COMPOUND_TEXT_CONVERTERS findStateFromEscSeq(const char* source, const char* sourceLimit, const uint8_t* toUBytesBuffer, int32_t toUBytesBufferLength, UErrorCode *err) { - COMPOUND_TEXT_CONVERTERS state = INVALID; - UBool matchFound = false; - int32_t i, n, offset = toUBytesBufferLength; - - for (i = 0; i < NUM_OF_CONVERTERS; i++) { - matchFound = true; - for (n = 0; escSeqCompoundText[i][n] != 0; n++) { - if (n < toUBytesBufferLength) { - if (toUBytesBuffer[n] != escSeqCompoundText[i][n]) { - matchFound = false; - break; - } - } else if ((source + (n - offset)) >= sourceLimit) { - *err = U_TRUNCATED_CHAR_FOUND; - matchFound = false; - break; - } else if (*(source + (n - offset)) != escSeqCompoundText[i][n]) { - matchFound = false; - break; - } - } - - if (matchFound) { - break; - } - } - - if (matchFound) { - state = (COMPOUND_TEXT_CONVERTERS)i; - } - - return state; -} - -static void U_CALLCONV -_CompoundTextOpen(UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *errorCode){ - cnv->extraInfo = uprv_malloc (sizeof (UConverterDataCompoundText)); - if (cnv->extraInfo != NULL) { - UConverterDataCompoundText *myConverterData = (UConverterDataCompoundText *) cnv->extraInfo; - - UConverterNamePieces stackPieces; - UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; - - myConverterData->myConverterArray[COMPOUND_TEXT_SINGLE_0] = NULL; - myConverterData->myConverterArray[COMPOUND_TEXT_SINGLE_1] = ucnv_loadSharedData("icu-internal-compound-s1", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[COMPOUND_TEXT_SINGLE_2] = ucnv_loadSharedData("icu-internal-compound-s2", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[COMPOUND_TEXT_SINGLE_3] = ucnv_loadSharedData("icu-internal-compound-s3", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_1] = ucnv_loadSharedData("icu-internal-compound-d1", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_2] = ucnv_loadSharedData("icu-internal-compound-d2", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_3] = ucnv_loadSharedData("icu-internal-compound-d3", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_4] = ucnv_loadSharedData("icu-internal-compound-d4", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_5] = ucnv_loadSharedData("icu-internal-compound-d5", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_6] = ucnv_loadSharedData("icu-internal-compound-d6", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_7] = ucnv_loadSharedData("icu-internal-compound-d7", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[COMPOUND_TEXT_TRIPLE_DOUBLE] = ucnv_loadSharedData("icu-internal-compound-t", &stackPieces, &stackArgs, errorCode); - - myConverterData->myConverterArray[IBM_915] = ucnv_loadSharedData("ibm-915_P100-1995", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[IBM_916] = ucnv_loadSharedData("ibm-916_P100-1995", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[IBM_914] = ucnv_loadSharedData("ibm-914_P100-1995", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[IBM_874] = ucnv_loadSharedData("ibm-874_P100-1995", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[IBM_912] = ucnv_loadSharedData("ibm-912_P100-1995", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[IBM_913] = ucnv_loadSharedData("ibm-913_P100-2000", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[ISO_8859_14] = ucnv_loadSharedData("iso-8859_14-1998", &stackPieces, &stackArgs, errorCode); - myConverterData->myConverterArray[IBM_923] = ucnv_loadSharedData("ibm-923_P100-1998", &stackPieces, &stackArgs, errorCode); - - if (U_FAILURE(*errorCode) || pArgs->onlyTestIsLoadable) { - _CompoundTextClose(cnv); - return; - } - - myConverterData->state = (COMPOUND_TEXT_CONVERTERS)0; - } else { - *errorCode = U_MEMORY_ALLOCATION_ERROR; - } -} - - -static void U_CALLCONV -_CompoundTextClose(UConverter *converter) { - UConverterDataCompoundText* myConverterData = (UConverterDataCompoundText*)(converter->extraInfo); - int32_t i; - - if (converter->extraInfo != NULL) { - /*close the array of converter pointers and free the memory*/ - for (i = 0; i < NUM_OF_CONVERTERS; i++) { - if (myConverterData->myConverterArray[i] != NULL) { - ucnv_unloadSharedDataIfReady(myConverterData->myConverterArray[i]); - } - } - - uprv_free(converter->extraInfo); - converter->extraInfo = NULL; - } -} - -static void U_CALLCONV -_CompoundTextReset(UConverter *converter, UConverterResetChoice choice) { - (void)converter; - (void)choice; -} - -static const char* U_CALLCONV -_CompoundTextgetName(const UConverter* cnv){ - (void)cnv; - return "x11-compound-text"; -} - -static void U_CALLCONV -UConverter_fromUnicode_CompoundText_OFFSETS(UConverterFromUnicodeArgs* args, UErrorCode* err){ - UConverter *cnv = args->converter; - uint8_t *target = (uint8_t *) args->target; - const uint8_t *targetLimit = (const uint8_t *) args->targetLimit; - const UChar* source = args->source; - const UChar* sourceLimit = args->sourceLimit; - /* int32_t* offsets = args->offsets; */ - UChar32 sourceChar; - UBool useFallback = cnv->useFallback; - uint8_t tmpTargetBuffer[7]; - int32_t tmpTargetBufferLength = 0; - COMPOUND_TEXT_CONVERTERS currentState, tmpState; - uint32_t pValue; - int32_t pValueLength = 0; - int32_t i, n, j; - - UConverterDataCompoundText *myConverterData = (UConverterDataCompoundText *) cnv->extraInfo; - - currentState = myConverterData->state; - - /* check if the last codepoint of previous buffer was a lead surrogate*/ - if((sourceChar = cnv->fromUChar32)!=0 && target< targetLimit) { - goto getTrail; - } - - while( source < sourceLimit){ - if(target < targetLimit){ - - sourceChar = *(source++); - /*check if the char is a First surrogate*/ - if(U16_IS_SURROGATE(sourceChar)) { - if(U16_IS_SURROGATE_LEAD(sourceChar)) { -getTrail: - /*look ahead to find the trail surrogate*/ - if(source < sourceLimit) { - /* test the following code unit */ - UChar trail=(UChar) *source; - if(U16_IS_TRAIL(trail)) { - source++; - sourceChar=U16_GET_SUPPLEMENTARY(sourceChar, trail); - cnv->fromUChar32=0x00; - /* convert this supplementary code point */ - /* exit this condition tree */ - } else { - /* this is an unmatched lead code unit (1st surrogate) */ - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - cnv->fromUChar32=sourceChar; - break; - } - } else { - /* no more input */ - cnv->fromUChar32=sourceChar; - break; - } - } else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - cnv->fromUChar32=sourceChar; - break; - } - } - - tmpTargetBufferLength = 0; - tmpState = getState(sourceChar); - - if (tmpState != DO_SEARCH && currentState != tmpState) { - /* Get escape sequence if necessary */ - currentState = tmpState; - for (i = 0; escSeqCompoundText[currentState][i] != 0; i++) { - tmpTargetBuffer[tmpTargetBufferLength++] = escSeqCompoundText[currentState][i]; - } - } - - if (tmpState == DO_SEARCH) { - /* Test all available converters */ - for (i = 1; i < SEARCH_LENGTH; i++) { - pValueLength = ucnv_MBCSFromUChar32(myConverterData->myConverterArray[i], sourceChar, &pValue, useFallback); - if (pValueLength > 0) { - tmpState = (COMPOUND_TEXT_CONVERTERS)i; - if (currentState != tmpState) { - currentState = tmpState; - for (j = 0; escSeqCompoundText[currentState][j] != 0; j++) { - tmpTargetBuffer[tmpTargetBufferLength++] = escSeqCompoundText[currentState][j]; - } - } - for (n = (pValueLength - 1); n >= 0; n--) { - tmpTargetBuffer[tmpTargetBufferLength++] = (uint8_t)(pValue >> (n * 8)); - } - break; - } - } - } else if (tmpState == COMPOUND_TEXT_SINGLE_0) { - tmpTargetBuffer[tmpTargetBufferLength++] = (uint8_t)sourceChar; - } else { - pValueLength = ucnv_MBCSFromUChar32(myConverterData->myConverterArray[currentState], sourceChar, &pValue, useFallback); - if (pValueLength > 0) { - for (n = (pValueLength - 1); n >= 0; n--) { - tmpTargetBuffer[tmpTargetBufferLength++] = (uint8_t)(pValue >> (n * 8)); - } - } - } - - for (i = 0; i < tmpTargetBufferLength; i++) { - if (target < targetLimit) { - *target++ = tmpTargetBuffer[i]; - } else { - *err = U_BUFFER_OVERFLOW_ERROR; - break; - } - } - - if (*err == U_BUFFER_OVERFLOW_ERROR) { - for (; i < tmpTargetBufferLength; i++) { - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = tmpTargetBuffer[i]; - } - } - } else { - *err = U_BUFFER_OVERFLOW_ERROR; - break; - } - } - - /*save the state and return */ - myConverterData->state = currentState; - args->source = source; - args->target = (char*)target; -} - - -static void U_CALLCONV -UConverter_toUnicode_CompoundText_OFFSETS(UConverterToUnicodeArgs *args, - UErrorCode* err){ - const char *mySource = (char *) args->source; - UChar *myTarget = args->target; - const char *mySourceLimit = args->sourceLimit; - const char *tmpSourceLimit = mySourceLimit; - uint32_t mySourceChar = 0x0000; - COMPOUND_TEXT_CONVERTERS currentState, tmpState; - int32_t sourceOffset = 0; - UConverterDataCompoundText *myConverterData = (UConverterDataCompoundText *) args->converter->extraInfo; - UConverterSharedData* savedSharedData = NULL; - - UConverterToUnicodeArgs subArgs; - int32_t minArgsSize; - - /* set up the subconverter arguments */ - if(args->sizesize; - } else { - minArgsSize = (int32_t)sizeof(UConverterToUnicodeArgs); - } - - uprv_memcpy(&subArgs, args, minArgsSize); - subArgs.size = (uint16_t)minArgsSize; - - currentState = tmpState = myConverterData->state; - - while(mySource < mySourceLimit){ - if(myTarget < args->targetLimit){ - if (args->converter->toULength > 0) { - mySourceChar = args->converter->toUBytes[0]; - } else { - mySourceChar = (uint8_t)*mySource; - } - - if (mySourceChar == ESC_START) { - tmpState = findStateFromEscSeq(mySource, mySourceLimit, args->converter->toUBytes, args->converter->toULength, err); - - if (*err == U_TRUNCATED_CHAR_FOUND) { - for (; mySource < mySourceLimit;) { - args->converter->toUBytes[args->converter->toULength++] = *mySource++; - } - *err = U_ZERO_ERROR; - break; - } else if (tmpState == INVALID) { - if (args->converter->toULength == 0) { - mySource++; /* skip over the 0x1b byte */ - } - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - - if (tmpState != currentState) { - currentState = tmpState; - } - - sourceOffset = static_cast(uprv_strlen((char*)escSeqCompoundText[currentState]) - args->converter->toULength); - - mySource += sourceOffset; - - args->converter->toULength = 0; - } - - if (currentState == COMPOUND_TEXT_SINGLE_0) { - while (mySource < mySourceLimit) { - if (*mySource == ESC_START) { - break; - } - if (myTarget < args->targetLimit) { - *myTarget++ = 0x00ff&(*mySource++); - } else { - *err = U_BUFFER_OVERFLOW_ERROR; - break; - } - } - } else if (mySource < mySourceLimit){ - sourceOffset = findNextEsc(mySource, mySourceLimit); - - tmpSourceLimit = mySource + sourceOffset; - - subArgs.source = mySource; - subArgs.sourceLimit = tmpSourceLimit; - subArgs.target = myTarget; - savedSharedData = subArgs.converter->sharedData; - subArgs.converter->sharedData = myConverterData->myConverterArray[currentState]; - - ucnv_MBCSToUnicodeWithOffsets(&subArgs, err); - - subArgs.converter->sharedData = savedSharedData; - - mySource = subArgs.source; - myTarget = subArgs.target; - - if (U_FAILURE(*err)) { - if(*err == U_BUFFER_OVERFLOW_ERROR) { - if(subArgs.converter->UCharErrorBufferLength > 0) { - uprv_memcpy(args->converter->UCharErrorBuffer, subArgs.converter->UCharErrorBuffer, - subArgs.converter->UCharErrorBufferLength); - } - args->converter->UCharErrorBufferLength=subArgs.converter->UCharErrorBufferLength; - subArgs.converter->UCharErrorBufferLength = 0; - } - break; - } - } - } else { - *err = U_BUFFER_OVERFLOW_ERROR; - break; - } - } - myConverterData->state = currentState; - args->target = myTarget; - args->source = mySource; -} - -static void U_CALLCONV -_CompoundText_GetUnicodeSet(const UConverter *cnv, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode) { - UConverterDataCompoundText *myConverterData = (UConverterDataCompoundText *)cnv->extraInfo; - int32_t i; - - for (i = 1; i < NUM_OF_CONVERTERS; i++) { - ucnv_MBCSGetUnicodeSetForUnicode(myConverterData->myConverterArray[i], sa, which, pErrorCode); - } - sa->add(sa->set, 0x0000); - sa->add(sa->set, 0x0009); - sa->add(sa->set, 0x000A); - sa->addRange(sa->set, 0x0020, 0x007F); - sa->addRange(sa->set, 0x00A0, 0x00FF); -} -U_CDECL_END - -static const UConverterImpl _CompoundTextImpl = { - - UCNV_COMPOUND_TEXT, - - NULL, - NULL, - - _CompoundTextOpen, - _CompoundTextClose, - _CompoundTextReset, - - UConverter_toUnicode_CompoundText_OFFSETS, - UConverter_toUnicode_CompoundText_OFFSETS, - UConverter_fromUnicode_CompoundText_OFFSETS, - UConverter_fromUnicode_CompoundText_OFFSETS, - NULL, - - NULL, - _CompoundTextgetName, - NULL, - NULL, - _CompoundText_GetUnicodeSet, - NULL, - NULL -}; - -static const UConverterStaticData _CompoundTextStaticData = { - sizeof(UConverterStaticData), - "COMPOUND_TEXT", - 0, - UCNV_IBM, - UCNV_COMPOUND_TEXT, - 1, - 6, - { 0xef, 0, 0, 0 }, - 1, - false, - false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; -const UConverterSharedData _CompoundTextData = - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_CompoundTextStaticData, &_CompoundTextImpl); - -#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2010-2015, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: ucnv_ct.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010Dec09 +* created by: Michael Ow +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/uset.h" +#include "unicode/ucnv_err.h" +#include "unicode/ucnv_cb.h" +#include "unicode/utf16.h" +#include "ucnv_imp.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" +#include "ucnvmbcs.h" +#include "cstring.h" +#include "cmemory.h" + +typedef enum { + INVALID = -2, + DO_SEARCH = -1, + + COMPOUND_TEXT_SINGLE_0 = 0, + COMPOUND_TEXT_SINGLE_1 = 1, + COMPOUND_TEXT_SINGLE_2 = 2, + COMPOUND_TEXT_SINGLE_3 = 3, + + COMPOUND_TEXT_DOUBLE_1 = 4, + COMPOUND_TEXT_DOUBLE_2 = 5, + COMPOUND_TEXT_DOUBLE_3 = 6, + COMPOUND_TEXT_DOUBLE_4 = 7, + COMPOUND_TEXT_DOUBLE_5 = 8, + COMPOUND_TEXT_DOUBLE_6 = 9, + COMPOUND_TEXT_DOUBLE_7 = 10, + + COMPOUND_TEXT_TRIPLE_DOUBLE = 11, + + IBM_915 = 12, + IBM_916 = 13, + IBM_914 = 14, + IBM_874 = 15, + IBM_912 = 16, + IBM_913 = 17, + ISO_8859_14 = 18, + IBM_923 = 19, + NUM_OF_CONVERTERS = 20 +} COMPOUND_TEXT_CONVERTERS; + +#define SEARCH_LENGTH 12 + +static const uint8_t escSeqCompoundText[NUM_OF_CONVERTERS][5] = { + /* Single */ + { 0x1B, 0x2D, 0x41, 0, 0 }, + { 0x1B, 0x2D, 0x4D, 0, 0 }, + { 0x1B, 0x2D, 0x46, 0, 0 }, + { 0x1B, 0x2D, 0x47, 0, 0 }, + + /* Double */ + { 0x1B, 0x24, 0x29, 0x41, 0 }, + { 0x1B, 0x24, 0x29, 0x42, 0 }, + { 0x1B, 0x24, 0x29, 0x43, 0 }, + { 0x1B, 0x24, 0x29, 0x44, 0 }, + { 0x1B, 0x24, 0x29, 0x47, 0 }, + { 0x1B, 0x24, 0x29, 0x48, 0 }, + { 0x1B, 0x24, 0x29, 0x49, 0 }, + + /* Triple/Double */ + { 0x1B, 0x25, 0x47, 0, 0 }, + + /*IBM-915*/ + { 0x1B, 0x2D, 0x4C, 0, 0 }, + /*IBM-916*/ + { 0x1B, 0x2D, 0x48, 0, 0 }, + /*IBM-914*/ + { 0x1B, 0x2D, 0x44, 0, 0 }, + /*IBM-874*/ + { 0x1B, 0x2D, 0x54, 0, 0 }, + /*IBM-912*/ + { 0x1B, 0x2D, 0x42, 0, 0 }, + /* IBM-913 */ + { 0x1B, 0x2D, 0x43, 0, 0 }, + /* ISO-8859_14 */ + { 0x1B, 0x2D, 0x5F, 0, 0 }, + /* IBM-923 */ + { 0x1B, 0x2D, 0x62, 0, 0 }, +}; + +#define ESC_START 0x1B + +#define isASCIIRange(codepoint) \ + ((codepoint == 0x0000) || (codepoint == 0x0009) || (codepoint == 0x000A) || \ + (codepoint >= 0x0020 && codepoint <= 0x007f) || (codepoint >= 0x00A0 && codepoint <= 0x00FF)) + +#define isIBM915(codepoint) \ + ((codepoint >= 0x0401 && codepoint <= 0x045F) || (codepoint == 0x2116)) + +#define isIBM916(codepoint) \ + ((codepoint >= 0x05D0 && codepoint <= 0x05EA) || (codepoint == 0x2017) || (codepoint == 0x203E)) + +#define isCompoundS3(codepoint) \ + ((codepoint == 0x060C) || (codepoint == 0x061B) || (codepoint == 0x061F) || (codepoint >= 0x0621 && codepoint <= 0x063A) || \ + (codepoint >= 0x0640 && codepoint <= 0x0652) || (codepoint >= 0x0660 && codepoint <= 0x066D) || (codepoint == 0x200B) || \ + (codepoint >= 0x0FE70 && codepoint <= 0x0FE72) || (codepoint == 0x0FE74) || (codepoint >= 0x0FE76 && codepoint <= 0x0FEBE)) + +#define isCompoundS2(codepoint) \ + ((codepoint == 0x02BC) || (codepoint == 0x02BD) || (codepoint >= 0x0384 && codepoint <= 0x03CE) || (codepoint == 0x2015)) + +#define isIBM914(codepoint) \ + ((codepoint == 0x0100) || (codepoint == 0x0101) || (codepoint == 0x0112) || (codepoint == 0x0113) || (codepoint == 0x0116) || (codepoint == 0x0117) || \ + (codepoint == 0x0122) || (codepoint == 0x0123) || (codepoint >= 0x0128 && codepoint <= 0x012B) || (codepoint == 0x012E) || (codepoint == 0x012F) || \ + (codepoint >= 0x0136 && codepoint <= 0x0138) || (codepoint == 0x013B) || (codepoint == 0x013C) || (codepoint == 0x0145) || (codepoint == 0x0146) || \ + (codepoint >= 0x014A && codepoint <= 0x014D) || (codepoint == 0x0156) || (codepoint == 0x0157) || (codepoint >= 0x0166 && codepoint <= 0x016B) || \ + (codepoint == 0x0172) || (codepoint == 0x0173)) + +#define isIBM874(codepoint) \ + ((codepoint >= 0x0E01 && codepoint <= 0x0E3A) || (codepoint >= 0x0E3F && codepoint <= 0x0E5B)) + +#define isIBM912(codepoint) \ + ((codepoint >= 0x0102 && codepoint <= 0x0107) || (codepoint >= 0x010C && codepoint <= 0x0111) || (codepoint >= 0x0118 && codepoint <= 0x011B) || \ + (codepoint == 0x0139) || (codepoint == 0x013A) || (codepoint == 0x013D) || (codepoint == 0x013E) || (codepoint >= 0x0141 && codepoint <= 0x0144) || \ + (codepoint == 0x0147) || (codepoint == 0x0147) || (codepoint == 0x0150) || (codepoint == 0x0151) || (codepoint == 0x0154) || (codepoint == 0x0155) || \ + (codepoint >= 0x0158 && codepoint <= 0x015B) || (codepoint == 0x015E) || (codepoint == 0x015F) || (codepoint >= 0x0160 && codepoint <= 0x0165) || \ + (codepoint == 0x016E) || (codepoint == 0x016F) || (codepoint == 0x0170) || (codepoint == 0x0171) || (codepoint >= 0x0179 && codepoint <= 0x017E) || \ + (codepoint == 0x02C7) || (codepoint == 0x02D8) || (codepoint == 0x02D9) || (codepoint == 0x02DB) || (codepoint == 0x02DD)) + +#define isIBM913(codepoint) \ + ((codepoint >= 0x0108 && codepoint <= 0x010B) || (codepoint == 0x011C) || \ + (codepoint == 0x011D) || (codepoint == 0x0120) || (codepoint == 0x0121) || \ + (codepoint >= 0x0124 && codepoint <= 0x0127) || (codepoint == 0x0134) || (codepoint == 0x0135) || \ + (codepoint == 0x015C) || (codepoint == 0x015D) || (codepoint == 0x016C) || (codepoint == 0x016D)) + +#define isCompoundS1(codepoint) \ + ((codepoint == 0x011E) || (codepoint == 0x011F) || (codepoint == 0x0130) || \ + (codepoint == 0x0131) || (codepoint >= 0x0218 && codepoint <= 0x021B)) + +#define isISO8859_14(codepoint) \ + ((codepoint >= 0x0174 && codepoint <= 0x0177) || (codepoint == 0x1E0A) || \ + (codepoint == 0x1E0B) || (codepoint == 0x1E1E) || (codepoint == 0x1E1F) || \ + (codepoint == 0x1E40) || (codepoint == 0x1E41) || (codepoint == 0x1E56) || \ + (codepoint == 0x1E57) || (codepoint == 0x1E60) || (codepoint == 0x1E61) || \ + (codepoint == 0x1E6A) || (codepoint == 0x1E6B) || (codepoint == 0x1EF2) || \ + (codepoint == 0x1EF3) || (codepoint >= 0x1E80 && codepoint <= 0x1E85)) + +#define isIBM923(codepoint) \ + ((codepoint == 0x0152) || (codepoint == 0x0153) || (codepoint == 0x0178) || (codepoint == 0x20AC)) + + +typedef struct{ + UConverterSharedData *myConverterArray[NUM_OF_CONVERTERS]; + COMPOUND_TEXT_CONVERTERS state; +} UConverterDataCompoundText; + +/*********** Compound Text Converter Protos ***********/ +U_CDECL_BEGIN +static void U_CALLCONV +_CompoundTextOpen(UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *errorCode); + +static void U_CALLCONV + _CompoundTextClose(UConverter *converter); + +static void U_CALLCONV +_CompoundTextReset(UConverter *converter, UConverterResetChoice choice); + +static const char* U_CALLCONV +_CompoundTextgetName(const UConverter* cnv); + + +static int32_t findNextEsc(const char *source, const char *sourceLimit) { + int32_t length = static_cast(sourceLimit - source); + int32_t i; + for (i = 1; i < length; i++) { + if (*(source + i) == 0x1B) { + return i; + } + } + + return length; +} + +static COMPOUND_TEXT_CONVERTERS getState(int codepoint) { + COMPOUND_TEXT_CONVERTERS state = DO_SEARCH; + + if (isASCIIRange(codepoint)) { + state = COMPOUND_TEXT_SINGLE_0; + } else if (isIBM912(codepoint)) { + state = IBM_912; + }else if (isIBM913(codepoint)) { + state = IBM_913; + } else if (isISO8859_14(codepoint)) { + state = ISO_8859_14; + } else if (isIBM923(codepoint)) { + state = IBM_923; + } else if (isIBM874(codepoint)) { + state = IBM_874; + } else if (isIBM914(codepoint)) { + state = IBM_914; + } else if (isCompoundS2(codepoint)) { + state = COMPOUND_TEXT_SINGLE_2; + } else if (isCompoundS3(codepoint)) { + state = COMPOUND_TEXT_SINGLE_3; + } else if (isIBM916(codepoint)) { + state = IBM_916; + } else if (isIBM915(codepoint)) { + state = IBM_915; + } else if (isCompoundS1(codepoint)) { + state = COMPOUND_TEXT_SINGLE_1; + } + + return state; +} + +static COMPOUND_TEXT_CONVERTERS findStateFromEscSeq(const char* source, const char* sourceLimit, const uint8_t* toUBytesBuffer, int32_t toUBytesBufferLength, UErrorCode *err) { + COMPOUND_TEXT_CONVERTERS state = INVALID; + UBool matchFound = false; + int32_t i, n, offset = toUBytesBufferLength; + + for (i = 0; i < NUM_OF_CONVERTERS; i++) { + matchFound = true; + for (n = 0; escSeqCompoundText[i][n] != 0; n++) { + if (n < toUBytesBufferLength) { + if (toUBytesBuffer[n] != escSeqCompoundText[i][n]) { + matchFound = false; + break; + } + } else if ((source + (n - offset)) >= sourceLimit) { + *err = U_TRUNCATED_CHAR_FOUND; + matchFound = false; + break; + } else if (*(source + (n - offset)) != escSeqCompoundText[i][n]) { + matchFound = false; + break; + } + } + + if (matchFound) { + break; + } + } + + if (matchFound) { + state = (COMPOUND_TEXT_CONVERTERS)i; + } + + return state; +} + +static void U_CALLCONV +_CompoundTextOpen(UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *errorCode){ + cnv->extraInfo = uprv_malloc (sizeof (UConverterDataCompoundText)); + if (cnv->extraInfo != nullptr) { + UConverterDataCompoundText *myConverterData = (UConverterDataCompoundText *) cnv->extraInfo; + + UConverterNamePieces stackPieces; + UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; + + myConverterData->myConverterArray[COMPOUND_TEXT_SINGLE_0] = nullptr; + myConverterData->myConverterArray[COMPOUND_TEXT_SINGLE_1] = ucnv_loadSharedData("icu-internal-compound-s1", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[COMPOUND_TEXT_SINGLE_2] = ucnv_loadSharedData("icu-internal-compound-s2", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[COMPOUND_TEXT_SINGLE_3] = ucnv_loadSharedData("icu-internal-compound-s3", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_1] = ucnv_loadSharedData("icu-internal-compound-d1", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_2] = ucnv_loadSharedData("icu-internal-compound-d2", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_3] = ucnv_loadSharedData("icu-internal-compound-d3", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_4] = ucnv_loadSharedData("icu-internal-compound-d4", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_5] = ucnv_loadSharedData("icu-internal-compound-d5", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_6] = ucnv_loadSharedData("icu-internal-compound-d6", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[COMPOUND_TEXT_DOUBLE_7] = ucnv_loadSharedData("icu-internal-compound-d7", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[COMPOUND_TEXT_TRIPLE_DOUBLE] = ucnv_loadSharedData("icu-internal-compound-t", &stackPieces, &stackArgs, errorCode); + + myConverterData->myConverterArray[IBM_915] = ucnv_loadSharedData("ibm-915_P100-1995", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[IBM_916] = ucnv_loadSharedData("ibm-916_P100-1995", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[IBM_914] = ucnv_loadSharedData("ibm-914_P100-1995", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[IBM_874] = ucnv_loadSharedData("ibm-874_P100-1995", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[IBM_912] = ucnv_loadSharedData("ibm-912_P100-1995", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[IBM_913] = ucnv_loadSharedData("ibm-913_P100-2000", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[ISO_8859_14] = ucnv_loadSharedData("iso-8859_14-1998", &stackPieces, &stackArgs, errorCode); + myConverterData->myConverterArray[IBM_923] = ucnv_loadSharedData("ibm-923_P100-1998", &stackPieces, &stackArgs, errorCode); + + if (U_FAILURE(*errorCode) || pArgs->onlyTestIsLoadable) { + _CompoundTextClose(cnv); + return; + } + + myConverterData->state = (COMPOUND_TEXT_CONVERTERS)0; + } else { + *errorCode = U_MEMORY_ALLOCATION_ERROR; + } +} + + +static void U_CALLCONV +_CompoundTextClose(UConverter *converter) { + UConverterDataCompoundText* myConverterData = (UConverterDataCompoundText*)(converter->extraInfo); + int32_t i; + + if (converter->extraInfo != nullptr) { + /*close the array of converter pointers and free the memory*/ + for (i = 0; i < NUM_OF_CONVERTERS; i++) { + if (myConverterData->myConverterArray[i] != nullptr) { + ucnv_unloadSharedDataIfReady(myConverterData->myConverterArray[i]); + } + } + + uprv_free(converter->extraInfo); + converter->extraInfo = nullptr; + } +} + +static void U_CALLCONV +_CompoundTextReset(UConverter *converter, UConverterResetChoice choice) { + (void)converter; + (void)choice; +} + +static const char* U_CALLCONV +_CompoundTextgetName(const UConverter* cnv){ + (void)cnv; + return "x11-compound-text"; +} + +static void U_CALLCONV +UConverter_fromUnicode_CompoundText_OFFSETS(UConverterFromUnicodeArgs* args, UErrorCode* err){ + UConverter *cnv = args->converter; + uint8_t *target = (uint8_t *) args->target; + const uint8_t *targetLimit = (const uint8_t *) args->targetLimit; + const char16_t* source = args->source; + const char16_t* sourceLimit = args->sourceLimit; + /* int32_t* offsets = args->offsets; */ + UChar32 sourceChar; + UBool useFallback = cnv->useFallback; + uint8_t tmpTargetBuffer[7]; + int32_t tmpTargetBufferLength = 0; + COMPOUND_TEXT_CONVERTERS currentState, tmpState; + uint32_t pValue; + int32_t pValueLength = 0; + int32_t i, n, j; + + UConverterDataCompoundText *myConverterData = (UConverterDataCompoundText *) cnv->extraInfo; + + currentState = myConverterData->state; + + /* check if the last codepoint of previous buffer was a lead surrogate*/ + if((sourceChar = cnv->fromUChar32)!=0 && target< targetLimit) { + goto getTrail; + } + + while( source < sourceLimit){ + if(target < targetLimit){ + + sourceChar = *(source++); + /*check if the char is a First surrogate*/ + if(U16_IS_SURROGATE(sourceChar)) { + if(U16_IS_SURROGATE_LEAD(sourceChar)) { +getTrail: + /*look ahead to find the trail surrogate*/ + if(source < sourceLimit) { + /* test the following code unit */ + char16_t trail=(char16_t) *source; + if(U16_IS_TRAIL(trail)) { + source++; + sourceChar=U16_GET_SUPPLEMENTARY(sourceChar, trail); + cnv->fromUChar32=0x00; + /* convert this supplementary code point */ + /* exit this condition tree */ + } else { + /* this is an unmatched lead code unit (1st surrogate) */ + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + cnv->fromUChar32=sourceChar; + break; + } + } else { + /* no more input */ + cnv->fromUChar32=sourceChar; + break; + } + } else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + cnv->fromUChar32=sourceChar; + break; + } + } + + tmpTargetBufferLength = 0; + tmpState = getState(sourceChar); + + if (tmpState != DO_SEARCH && currentState != tmpState) { + /* Get escape sequence if necessary */ + currentState = tmpState; + for (i = 0; escSeqCompoundText[currentState][i] != 0; i++) { + tmpTargetBuffer[tmpTargetBufferLength++] = escSeqCompoundText[currentState][i]; + } + } + + if (tmpState == DO_SEARCH) { + /* Test all available converters */ + for (i = 1; i < SEARCH_LENGTH; i++) { + pValueLength = ucnv_MBCSFromUChar32(myConverterData->myConverterArray[i], sourceChar, &pValue, useFallback); + if (pValueLength > 0) { + tmpState = (COMPOUND_TEXT_CONVERTERS)i; + if (currentState != tmpState) { + currentState = tmpState; + for (j = 0; escSeqCompoundText[currentState][j] != 0; j++) { + tmpTargetBuffer[tmpTargetBufferLength++] = escSeqCompoundText[currentState][j]; + } + } + for (n = (pValueLength - 1); n >= 0; n--) { + tmpTargetBuffer[tmpTargetBufferLength++] = (uint8_t)(pValue >> (n * 8)); + } + break; + } + } + } else if (tmpState == COMPOUND_TEXT_SINGLE_0) { + tmpTargetBuffer[tmpTargetBufferLength++] = (uint8_t)sourceChar; + } else { + pValueLength = ucnv_MBCSFromUChar32(myConverterData->myConverterArray[currentState], sourceChar, &pValue, useFallback); + if (pValueLength > 0) { + for (n = (pValueLength - 1); n >= 0; n--) { + tmpTargetBuffer[tmpTargetBufferLength++] = (uint8_t)(pValue >> (n * 8)); + } + } + } + + for (i = 0; i < tmpTargetBufferLength; i++) { + if (target < targetLimit) { + *target++ = tmpTargetBuffer[i]; + } else { + *err = U_BUFFER_OVERFLOW_ERROR; + break; + } + } + + if (*err == U_BUFFER_OVERFLOW_ERROR) { + for (; i < tmpTargetBufferLength; i++) { + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = tmpTargetBuffer[i]; + } + } + } else { + *err = U_BUFFER_OVERFLOW_ERROR; + break; + } + } + + /*save the state and return */ + myConverterData->state = currentState; + args->source = source; + args->target = (char*)target; +} + + +static void U_CALLCONV +UConverter_toUnicode_CompoundText_OFFSETS(UConverterToUnicodeArgs *args, + UErrorCode* err){ + const char *mySource = (char *) args->source; + char16_t *myTarget = args->target; + const char *mySourceLimit = args->sourceLimit; + const char *tmpSourceLimit = mySourceLimit; + uint32_t mySourceChar = 0x0000; + COMPOUND_TEXT_CONVERTERS currentState, tmpState; + int32_t sourceOffset = 0; + UConverterDataCompoundText *myConverterData = (UConverterDataCompoundText *) args->converter->extraInfo; + UConverterSharedData* savedSharedData = nullptr; + + UConverterToUnicodeArgs subArgs; + int32_t minArgsSize; + + /* set up the subconverter arguments */ + if(args->sizesize; + } else { + minArgsSize = (int32_t)sizeof(UConverterToUnicodeArgs); + } + + uprv_memcpy(&subArgs, args, minArgsSize); + subArgs.size = (uint16_t)minArgsSize; + + currentState = tmpState = myConverterData->state; + + while(mySource < mySourceLimit){ + if(myTarget < args->targetLimit){ + if (args->converter->toULength > 0) { + mySourceChar = args->converter->toUBytes[0]; + } else { + mySourceChar = (uint8_t)*mySource; + } + + if (mySourceChar == ESC_START) { + tmpState = findStateFromEscSeq(mySource, mySourceLimit, args->converter->toUBytes, args->converter->toULength, err); + + if (*err == U_TRUNCATED_CHAR_FOUND) { + for (; mySource < mySourceLimit;) { + args->converter->toUBytes[args->converter->toULength++] = *mySource++; + } + *err = U_ZERO_ERROR; + break; + } else if (tmpState == INVALID) { + if (args->converter->toULength == 0) { + mySource++; /* skip over the 0x1b byte */ + } + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + + if (tmpState != currentState) { + currentState = tmpState; + } + + sourceOffset = static_cast(uprv_strlen((char*)escSeqCompoundText[currentState]) - args->converter->toULength); + + mySource += sourceOffset; + + args->converter->toULength = 0; + } + + if (currentState == COMPOUND_TEXT_SINGLE_0) { + while (mySource < mySourceLimit) { + if (*mySource == ESC_START) { + break; + } + if (myTarget < args->targetLimit) { + *myTarget++ = 0x00ff&(*mySource++); + } else { + *err = U_BUFFER_OVERFLOW_ERROR; + break; + } + } + } else if (mySource < mySourceLimit){ + sourceOffset = findNextEsc(mySource, mySourceLimit); + + tmpSourceLimit = mySource + sourceOffset; + + subArgs.source = mySource; + subArgs.sourceLimit = tmpSourceLimit; + subArgs.target = myTarget; + savedSharedData = subArgs.converter->sharedData; + subArgs.converter->sharedData = myConverterData->myConverterArray[currentState]; + + ucnv_MBCSToUnicodeWithOffsets(&subArgs, err); + + subArgs.converter->sharedData = savedSharedData; + + mySource = subArgs.source; + myTarget = subArgs.target; + + if (U_FAILURE(*err)) { + if(*err == U_BUFFER_OVERFLOW_ERROR) { + if(subArgs.converter->UCharErrorBufferLength > 0) { + uprv_memcpy(args->converter->UCharErrorBuffer, subArgs.converter->UCharErrorBuffer, + subArgs.converter->UCharErrorBufferLength); + } + args->converter->UCharErrorBufferLength=subArgs.converter->UCharErrorBufferLength; + subArgs.converter->UCharErrorBufferLength = 0; + } + break; + } + } + } else { + *err = U_BUFFER_OVERFLOW_ERROR; + break; + } + } + myConverterData->state = currentState; + args->target = myTarget; + args->source = mySource; +} + +static void U_CALLCONV +_CompoundText_GetUnicodeSet(const UConverter *cnv, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode) { + UConverterDataCompoundText *myConverterData = (UConverterDataCompoundText *)cnv->extraInfo; + int32_t i; + + for (i = 1; i < NUM_OF_CONVERTERS; i++) { + ucnv_MBCSGetUnicodeSetForUnicode(myConverterData->myConverterArray[i], sa, which, pErrorCode); + } + sa->add(sa->set, 0x0000); + sa->add(sa->set, 0x0009); + sa->add(sa->set, 0x000A); + sa->addRange(sa->set, 0x0020, 0x007F); + sa->addRange(sa->set, 0x00A0, 0x00FF); +} +U_CDECL_END + +static const UConverterImpl _CompoundTextImpl = { + + UCNV_COMPOUND_TEXT, + + nullptr, + nullptr, + + _CompoundTextOpen, + _CompoundTextClose, + _CompoundTextReset, + + UConverter_toUnicode_CompoundText_OFFSETS, + UConverter_toUnicode_CompoundText_OFFSETS, + UConverter_fromUnicode_CompoundText_OFFSETS, + UConverter_fromUnicode_CompoundText_OFFSETS, + nullptr, + + nullptr, + _CompoundTextgetName, + nullptr, + nullptr, + _CompoundText_GetUnicodeSet, + nullptr, + nullptr +}; + +static const UConverterStaticData _CompoundTextStaticData = { + sizeof(UConverterStaticData), + "COMPOUND_TEXT", + 0, + UCNV_IBM, + UCNV_COMPOUND_TEXT, + 1, + 6, + { 0xef, 0, 0, 0 }, + 1, + false, + false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; +const UConverterSharedData _CompoundTextData = + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_CompoundTextStaticData, &_CompoundTextImpl); + +#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ diff --git a/deps/icu-small/source/common/ucnv_err.cpp b/deps/icu-small/source/common/ucnv_err.cpp index 4210673ef9316f..fafaf549cf4d45 100644 --- a/deps/icu-small/source/common/ucnv_err.cpp +++ b/deps/icu-small/source/common/ucnv_err.cpp @@ -1,486 +1,486 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ***************************************************************************** - * - * Copyright (C) 1998-2016, International Business Machines - * Corporation and others. All Rights Reserved. - * - ***************************************************************************** - * - * ucnv_err.c - * Implements error behaviour functions called by T_UConverter_{from,to}Unicode - * - * -* Change history: -* -* 06/29/2000 helena Major rewrite of the callback APIs. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ucnv_err.h" -#include "unicode/ucnv_cb.h" -#include "ucnv_cnv.h" -#include "cmemory.h" -#include "unicode/ucnv.h" -#include "ustrfmt.h" - -#define VALUE_STRING_LENGTH 48 -/*Magic # 32 = 4(number of char in value string) * 8(max number of bytes per char for any converter) */ -#define UNICODE_PERCENT_SIGN_CODEPOINT 0x0025 -#define UNICODE_U_CODEPOINT 0x0055 -#define UNICODE_X_CODEPOINT 0x0058 -#define UNICODE_RS_CODEPOINT 0x005C -#define UNICODE_U_LOW_CODEPOINT 0x0075 -#define UNICODE_X_LOW_CODEPOINT 0x0078 -#define UNICODE_AMP_CODEPOINT 0x0026 -#define UNICODE_HASH_CODEPOINT 0x0023 -#define UNICODE_SEMICOLON_CODEPOINT 0x003B -#define UNICODE_PLUS_CODEPOINT 0x002B -#define UNICODE_LEFT_CURLY_CODEPOINT 0x007B -#define UNICODE_RIGHT_CURLY_CODEPOINT 0x007D -#define UNICODE_SPACE_CODEPOINT 0x0020 -#define UCNV_PRV_ESCAPE_ICU 0 -#define UCNV_PRV_ESCAPE_C 'C' -#define UCNV_PRV_ESCAPE_XML_DEC 'D' -#define UCNV_PRV_ESCAPE_XML_HEX 'X' -#define UCNV_PRV_ESCAPE_JAVA 'J' -#define UCNV_PRV_ESCAPE_UNICODE 'U' -#define UCNV_PRV_ESCAPE_CSS2 'S' -#define UCNV_PRV_STOP_ON_ILLEGAL 'i' - -/* - * IS_DEFAULT_IGNORABLE_CODE_POINT - * This is to check if a code point has the default ignorable unicode property. - * As such, this list needs to be updated if the ignorable code point list ever - * changes. - * To avoid dependency on other code, this list is hard coded here. - * When an ignorable code point is found and is unmappable, the default callbacks - * will ignore them. - * For a list of the default ignorable code points, use this link: - * https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3ADI%3A%5D&abb=on&g=&i= - * - * This list should be sync with the one in CharsetCallback.java - */ -#define IS_DEFAULT_IGNORABLE_CODE_POINT(c) ( \ - (c == 0x00AD) || \ - (c == 0x034F) || \ - (c == 0x061C) || \ - (c == 0x115F) || \ - (c == 0x1160) || \ - (0x17B4 <= c && c <= 0x17B5) || \ - (0x180B <= c && c <= 0x180F) || \ - (0x200B <= c && c <= 0x200F) || \ - (0x202A <= c && c <= 0x202E) || \ - (0x2060 <= c && c <= 0x206F) || \ - (c == 0x3164) || \ - (0xFE00 <= c && c <= 0xFE0F) || \ - (c == 0xFEFF) || \ - (c == 0xFFA0) || \ - (0xFFF0 <= c && c <= 0xFFF8) || \ - (0x1BCA0 <= c && c <= 0x1BCA3) || \ - (0x1D173 <= c && c <= 0x1D17A) || \ - (0xE0000 <= c && c <= 0xE0FFF)) - - -/*Function Pointer STOPS at the ILLEGAL_SEQUENCE */ -U_CAPI void U_EXPORT2 -UCNV_FROM_U_CALLBACK_STOP ( - const void *context, - UConverterFromUnicodeArgs *fromUArgs, - const UChar* codeUnits, - int32_t length, - UChar32 codePoint, - UConverterCallbackReason reason, - UErrorCode * err) -{ - (void)context; - (void)fromUArgs; - (void)codeUnits; - (void)length; - if (reason == UCNV_UNASSIGNED && IS_DEFAULT_IGNORABLE_CODE_POINT(codePoint)) - { - /* - * Skip if the codepoint has unicode property of default ignorable. - */ - *err = U_ZERO_ERROR; - } - /* the caller must have set the error code accordingly */ - return; -} - - -/*Function Pointer STOPS at the ILLEGAL_SEQUENCE */ -U_CAPI void U_EXPORT2 -UCNV_TO_U_CALLBACK_STOP ( - const void *context, - UConverterToUnicodeArgs *toUArgs, - const char* codePoints, - int32_t length, - UConverterCallbackReason reason, - UErrorCode * err) -{ - /* the caller must have set the error code accordingly */ - (void)context; (void)toUArgs; (void)codePoints; (void)length; (void)reason; (void)err; - return; -} - -U_CAPI void U_EXPORT2 -UCNV_FROM_U_CALLBACK_SKIP ( - const void *context, - UConverterFromUnicodeArgs *fromUArgs, - const UChar* codeUnits, - int32_t length, - UChar32 codePoint, - UConverterCallbackReason reason, - UErrorCode * err) -{ - (void)fromUArgs; - (void)codeUnits; - (void)length; - if (reason <= UCNV_IRREGULAR) - { - if (reason == UCNV_UNASSIGNED && IS_DEFAULT_IGNORABLE_CODE_POINT(codePoint)) - { - /* - * Skip if the codepoint has unicode property of default ignorable. - */ - *err = U_ZERO_ERROR; - } - else if (context == NULL || (*((char*)context) == UCNV_PRV_STOP_ON_ILLEGAL && reason == UCNV_UNASSIGNED)) - { - *err = U_ZERO_ERROR; - } - /* else the caller must have set the error code accordingly. */ - } - /* else ignore the reset, close and clone calls. */ -} - -U_CAPI void U_EXPORT2 -UCNV_FROM_U_CALLBACK_SUBSTITUTE ( - const void *context, - UConverterFromUnicodeArgs *fromArgs, - const UChar* codeUnits, - int32_t length, - UChar32 codePoint, - UConverterCallbackReason reason, - UErrorCode * err) -{ - (void)codeUnits; - (void)length; - if (reason <= UCNV_IRREGULAR) - { - if (reason == UCNV_UNASSIGNED && IS_DEFAULT_IGNORABLE_CODE_POINT(codePoint)) - { - /* - * Skip if the codepoint has unicode property of default ignorable. - */ - *err = U_ZERO_ERROR; - } - else if (context == NULL || (*((char*)context) == UCNV_PRV_STOP_ON_ILLEGAL && reason == UCNV_UNASSIGNED)) - { - *err = U_ZERO_ERROR; - ucnv_cbFromUWriteSub(fromArgs, 0, err); - } - /* else the caller must have set the error code accordingly. */ - } - /* else ignore the reset, close and clone calls. */ -} - -/*uses uprv_itou to get a unicode escape sequence of the offensive sequence, - *uses a clean copy (resetted) of the converter, to convert that unicode - *escape sequence to the target codepage (if conversion failure happens then - *we revert to substituting with subchar) - */ -U_CAPI void U_EXPORT2 -UCNV_FROM_U_CALLBACK_ESCAPE ( - const void *context, - UConverterFromUnicodeArgs *fromArgs, - const UChar *codeUnits, - int32_t length, - UChar32 codePoint, - UConverterCallbackReason reason, - UErrorCode * err) -{ - - UChar valueString[VALUE_STRING_LENGTH]; - int32_t valueStringLength = 0; - int32_t i = 0; - - const UChar *myValueSource = NULL; - UErrorCode err2 = U_ZERO_ERROR; - UConverterFromUCallback original = NULL; - const void *originalContext; - - UConverterFromUCallback ignoredCallback = NULL; - const void *ignoredContext; - - if (reason > UCNV_IRREGULAR) - { - return; - } - else if (reason == UCNV_UNASSIGNED && IS_DEFAULT_IGNORABLE_CODE_POINT(codePoint)) - { - /* - * Skip if the codepoint has unicode property of default ignorable. - */ - *err = U_ZERO_ERROR; - return; - } - - ucnv_setFromUCallBack (fromArgs->converter, - (UConverterFromUCallback) UCNV_FROM_U_CALLBACK_SUBSTITUTE, - NULL, - &original, - &originalContext, - &err2); - - if (U_FAILURE (err2)) - { - *err = err2; - return; - } - if(context==NULL) - { - while (i < length) - { - valueString[valueStringLength++] = (UChar) UNICODE_PERCENT_SIGN_CODEPOINT; /* adding % */ - valueString[valueStringLength++] = (UChar) UNICODE_U_CODEPOINT; /* adding U */ - valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[i++], 16, 4); - } - } - else - { - switch(*((char*)context)) - { - case UCNV_PRV_ESCAPE_JAVA: - while (i < length) - { - valueString[valueStringLength++] = (UChar) UNICODE_RS_CODEPOINT; /* adding \ */ - valueString[valueStringLength++] = (UChar) UNICODE_U_LOW_CODEPOINT; /* adding u */ - valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[i++], 16, 4); - } - break; - - case UCNV_PRV_ESCAPE_C: - valueString[valueStringLength++] = (UChar) UNICODE_RS_CODEPOINT; /* adding \ */ - - if(length==2){ - valueString[valueStringLength++] = (UChar) UNICODE_U_CODEPOINT; /* adding U */ - valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, codePoint, 16, 8); - - } - else{ - valueString[valueStringLength++] = (UChar) UNICODE_U_LOW_CODEPOINT; /* adding u */ - valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[0], 16, 4); - } - break; - - case UCNV_PRV_ESCAPE_XML_DEC: - - valueString[valueStringLength++] = (UChar) UNICODE_AMP_CODEPOINT; /* adding & */ - valueString[valueStringLength++] = (UChar) UNICODE_HASH_CODEPOINT; /* adding # */ - if(length==2){ - valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, codePoint, 10, 0); - } - else{ - valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[0], 10, 0); - } - valueString[valueStringLength++] = (UChar) UNICODE_SEMICOLON_CODEPOINT; /* adding ; */ - break; - - case UCNV_PRV_ESCAPE_XML_HEX: - - valueString[valueStringLength++] = (UChar) UNICODE_AMP_CODEPOINT; /* adding & */ - valueString[valueStringLength++] = (UChar) UNICODE_HASH_CODEPOINT; /* adding # */ - valueString[valueStringLength++] = (UChar) UNICODE_X_LOW_CODEPOINT; /* adding x */ - if(length==2){ - valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, codePoint, 16, 0); - } - else{ - valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[0], 16, 0); - } - valueString[valueStringLength++] = (UChar) UNICODE_SEMICOLON_CODEPOINT; /* adding ; */ - break; - - case UCNV_PRV_ESCAPE_UNICODE: - valueString[valueStringLength++] = (UChar) UNICODE_LEFT_CURLY_CODEPOINT; /* adding { */ - valueString[valueStringLength++] = (UChar) UNICODE_U_CODEPOINT; /* adding U */ - valueString[valueStringLength++] = (UChar) UNICODE_PLUS_CODEPOINT; /* adding + */ - if (length == 2) { - valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, codePoint, 16, 4); - } else { - valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[0], 16, 4); - } - valueString[valueStringLength++] = (UChar) UNICODE_RIGHT_CURLY_CODEPOINT; /* adding } */ - break; - - case UCNV_PRV_ESCAPE_CSS2: - valueString[valueStringLength++] = (UChar) UNICODE_RS_CODEPOINT; /* adding \ */ - valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, codePoint, 16, 0); - /* Always add space character, because the next character might be whitespace, - which would erroneously be considered the termination of the escape sequence. */ - valueString[valueStringLength++] = (UChar) UNICODE_SPACE_CODEPOINT; - break; - - default: - while (i < length) - { - valueString[valueStringLength++] = (UChar) UNICODE_PERCENT_SIGN_CODEPOINT; /* adding % */ - valueString[valueStringLength++] = (UChar) UNICODE_U_CODEPOINT; /* adding U */ - valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[i++], 16, 4); - } - } - } - myValueSource = valueString; - - /* reset the error */ - *err = U_ZERO_ERROR; - - ucnv_cbFromUWriteUChars(fromArgs, &myValueSource, myValueSource+valueStringLength, 0, err); - - ucnv_setFromUCallBack (fromArgs->converter, - original, - originalContext, - &ignoredCallback, - &ignoredContext, - &err2); - if (U_FAILURE (err2)) - { - *err = err2; - return; - } - - return; -} - - - -U_CAPI void U_EXPORT2 -UCNV_TO_U_CALLBACK_SKIP ( - const void *context, - UConverterToUnicodeArgs *toArgs, - const char* codeUnits, - int32_t length, - UConverterCallbackReason reason, - UErrorCode * err) -{ - (void)toArgs; - (void)codeUnits; - (void)length; - if (reason <= UCNV_IRREGULAR) - { - if (context == NULL || (*((char*)context) == UCNV_PRV_STOP_ON_ILLEGAL && reason == UCNV_UNASSIGNED)) - { - *err = U_ZERO_ERROR; - } - /* else the caller must have set the error code accordingly. */ - } - /* else ignore the reset, close and clone calls. */ -} - -U_CAPI void U_EXPORT2 -UCNV_TO_U_CALLBACK_SUBSTITUTE ( - const void *context, - UConverterToUnicodeArgs *toArgs, - const char* codeUnits, - int32_t length, - UConverterCallbackReason reason, - UErrorCode * err) -{ - (void)codeUnits; - (void)length; - if (reason <= UCNV_IRREGULAR) - { - if (context == NULL || (*((char*)context) == UCNV_PRV_STOP_ON_ILLEGAL && reason == UCNV_UNASSIGNED)) - { - *err = U_ZERO_ERROR; - ucnv_cbToUWriteSub(toArgs,0,err); - } - /* else the caller must have set the error code accordingly. */ - } - /* else ignore the reset, close and clone calls. */ -} - -/*uses uprv_itou to get a unicode escape sequence of the offensive sequence, - *and uses that as the substitution sequence - */ -U_CAPI void U_EXPORT2 -UCNV_TO_U_CALLBACK_ESCAPE ( - const void *context, - UConverterToUnicodeArgs *toArgs, - const char* codeUnits, - int32_t length, - UConverterCallbackReason reason, - UErrorCode * err) -{ - UChar uniValueString[VALUE_STRING_LENGTH]; - int32_t valueStringLength = 0; - int32_t i = 0; - - if (reason > UCNV_IRREGULAR) - { - return; - } - - if(context==NULL) - { - while (i < length) - { - uniValueString[valueStringLength++] = (UChar) UNICODE_PERCENT_SIGN_CODEPOINT; /* adding % */ - uniValueString[valueStringLength++] = (UChar) UNICODE_X_CODEPOINT; /* adding X */ - valueStringLength += uprv_itou (uniValueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint8_t) codeUnits[i++], 16, 2); - } - } - else - { - switch(*((char*)context)) - { - case UCNV_PRV_ESCAPE_XML_DEC: - while (i < length) - { - uniValueString[valueStringLength++] = (UChar) UNICODE_AMP_CODEPOINT; /* adding & */ - uniValueString[valueStringLength++] = (UChar) UNICODE_HASH_CODEPOINT; /* adding # */ - valueStringLength += uprv_itou (uniValueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint8_t)codeUnits[i++], 10, 0); - uniValueString[valueStringLength++] = (UChar) UNICODE_SEMICOLON_CODEPOINT; /* adding ; */ - } - break; - - case UCNV_PRV_ESCAPE_XML_HEX: - while (i < length) - { - uniValueString[valueStringLength++] = (UChar) UNICODE_AMP_CODEPOINT; /* adding & */ - uniValueString[valueStringLength++] = (UChar) UNICODE_HASH_CODEPOINT; /* adding # */ - uniValueString[valueStringLength++] = (UChar) UNICODE_X_LOW_CODEPOINT; /* adding x */ - valueStringLength += uprv_itou (uniValueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint8_t)codeUnits[i++], 16, 0); - uniValueString[valueStringLength++] = (UChar) UNICODE_SEMICOLON_CODEPOINT; /* adding ; */ - } - break; - case UCNV_PRV_ESCAPE_C: - while (i < length) - { - uniValueString[valueStringLength++] = (UChar) UNICODE_RS_CODEPOINT; /* adding \ */ - uniValueString[valueStringLength++] = (UChar) UNICODE_X_LOW_CODEPOINT; /* adding x */ - valueStringLength += uprv_itou (uniValueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint8_t)codeUnits[i++], 16, 2); - } - break; - default: - while (i < length) - { - uniValueString[valueStringLength++] = (UChar) UNICODE_PERCENT_SIGN_CODEPOINT; /* adding % */ - uniValueString[valueStringLength++] = (UChar) UNICODE_X_CODEPOINT; /* adding X */ - uprv_itou (uniValueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint8_t) codeUnits[i++], 16, 2); - valueStringLength += 2; - } - } - } - /* reset the error */ - *err = U_ZERO_ERROR; - - ucnv_cbToUWriteUChars(toArgs, uniValueString, valueStringLength, 0, err); -} - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ***************************************************************************** + * + * Copyright (C) 1998-2016, International Business Machines + * Corporation and others. All Rights Reserved. + * + ***************************************************************************** + * + * ucnv_err.c + * Implements error behaviour functions called by T_UConverter_{from,to}Unicode + * + * +* Change history: +* +* 06/29/2000 helena Major rewrite of the callback APIs. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ucnv_err.h" +#include "unicode/ucnv_cb.h" +#include "ucnv_cnv.h" +#include "cmemory.h" +#include "unicode/ucnv.h" +#include "ustrfmt.h" + +#define VALUE_STRING_LENGTH 48 +/*Magic # 32 = 4(number of char in value string) * 8(max number of bytes per char for any converter) */ +#define UNICODE_PERCENT_SIGN_CODEPOINT 0x0025 +#define UNICODE_U_CODEPOINT 0x0055 +#define UNICODE_X_CODEPOINT 0x0058 +#define UNICODE_RS_CODEPOINT 0x005C +#define UNICODE_U_LOW_CODEPOINT 0x0075 +#define UNICODE_X_LOW_CODEPOINT 0x0078 +#define UNICODE_AMP_CODEPOINT 0x0026 +#define UNICODE_HASH_CODEPOINT 0x0023 +#define UNICODE_SEMICOLON_CODEPOINT 0x003B +#define UNICODE_PLUS_CODEPOINT 0x002B +#define UNICODE_LEFT_CURLY_CODEPOINT 0x007B +#define UNICODE_RIGHT_CURLY_CODEPOINT 0x007D +#define UNICODE_SPACE_CODEPOINT 0x0020 +#define UCNV_PRV_ESCAPE_ICU 0 +#define UCNV_PRV_ESCAPE_C 'C' +#define UCNV_PRV_ESCAPE_XML_DEC 'D' +#define UCNV_PRV_ESCAPE_XML_HEX 'X' +#define UCNV_PRV_ESCAPE_JAVA 'J' +#define UCNV_PRV_ESCAPE_UNICODE 'U' +#define UCNV_PRV_ESCAPE_CSS2 'S' +#define UCNV_PRV_STOP_ON_ILLEGAL 'i' + +/* + * IS_DEFAULT_IGNORABLE_CODE_POINT + * This is to check if a code point has the default ignorable unicode property. + * As such, this list needs to be updated if the ignorable code point list ever + * changes. + * To avoid dependency on other code, this list is hard coded here. + * When an ignorable code point is found and is unmappable, the default callbacks + * will ignore them. + * For a list of the default ignorable code points, use this link: + * https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3ADI%3A%5D&abb=on&g=&i= + * + * This list should be sync with the one in CharsetCallback.java + */ +#define IS_DEFAULT_IGNORABLE_CODE_POINT(c) ( \ + (c == 0x00AD) || \ + (c == 0x034F) || \ + (c == 0x061C) || \ + (c == 0x115F) || \ + (c == 0x1160) || \ + (0x17B4 <= c && c <= 0x17B5) || \ + (0x180B <= c && c <= 0x180F) || \ + (0x200B <= c && c <= 0x200F) || \ + (0x202A <= c && c <= 0x202E) || \ + (0x2060 <= c && c <= 0x206F) || \ + (c == 0x3164) || \ + (0xFE00 <= c && c <= 0xFE0F) || \ + (c == 0xFEFF) || \ + (c == 0xFFA0) || \ + (0xFFF0 <= c && c <= 0xFFF8) || \ + (0x1BCA0 <= c && c <= 0x1BCA3) || \ + (0x1D173 <= c && c <= 0x1D17A) || \ + (0xE0000 <= c && c <= 0xE0FFF)) + + +/*Function Pointer STOPS at the ILLEGAL_SEQUENCE */ +U_CAPI void U_EXPORT2 +UCNV_FROM_U_CALLBACK_STOP ( + const void *context, + UConverterFromUnicodeArgs *fromUArgs, + const char16_t* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode * err) +{ + (void)context; + (void)fromUArgs; + (void)codeUnits; + (void)length; + if (reason == UCNV_UNASSIGNED && IS_DEFAULT_IGNORABLE_CODE_POINT(codePoint)) + { + /* + * Skip if the codepoint has unicode property of default ignorable. + */ + *err = U_ZERO_ERROR; + } + /* the caller must have set the error code accordingly */ + return; +} + + +/*Function Pointer STOPS at the ILLEGAL_SEQUENCE */ +U_CAPI void U_EXPORT2 +UCNV_TO_U_CALLBACK_STOP ( + const void *context, + UConverterToUnicodeArgs *toUArgs, + const char* codePoints, + int32_t length, + UConverterCallbackReason reason, + UErrorCode * err) +{ + /* the caller must have set the error code accordingly */ + (void)context; (void)toUArgs; (void)codePoints; (void)length; (void)reason; (void)err; + return; +} + +U_CAPI void U_EXPORT2 +UCNV_FROM_U_CALLBACK_SKIP ( + const void *context, + UConverterFromUnicodeArgs *fromUArgs, + const char16_t* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode * err) +{ + (void)fromUArgs; + (void)codeUnits; + (void)length; + if (reason <= UCNV_IRREGULAR) + { + if (reason == UCNV_UNASSIGNED && IS_DEFAULT_IGNORABLE_CODE_POINT(codePoint)) + { + /* + * Skip if the codepoint has unicode property of default ignorable. + */ + *err = U_ZERO_ERROR; + } + else if (context == nullptr || (*((char*)context) == UCNV_PRV_STOP_ON_ILLEGAL && reason == UCNV_UNASSIGNED)) + { + *err = U_ZERO_ERROR; + } + /* else the caller must have set the error code accordingly. */ + } + /* else ignore the reset, close and clone calls. */ +} + +U_CAPI void U_EXPORT2 +UCNV_FROM_U_CALLBACK_SUBSTITUTE ( + const void *context, + UConverterFromUnicodeArgs *fromArgs, + const char16_t* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode * err) +{ + (void)codeUnits; + (void)length; + if (reason <= UCNV_IRREGULAR) + { + if (reason == UCNV_UNASSIGNED && IS_DEFAULT_IGNORABLE_CODE_POINT(codePoint)) + { + /* + * Skip if the codepoint has unicode property of default ignorable. + */ + *err = U_ZERO_ERROR; + } + else if (context == nullptr || (*((char*)context) == UCNV_PRV_STOP_ON_ILLEGAL && reason == UCNV_UNASSIGNED)) + { + *err = U_ZERO_ERROR; + ucnv_cbFromUWriteSub(fromArgs, 0, err); + } + /* else the caller must have set the error code accordingly. */ + } + /* else ignore the reset, close and clone calls. */ +} + +/*uses uprv_itou to get a unicode escape sequence of the offensive sequence, + *uses a clean copy (resetted) of the converter, to convert that unicode + *escape sequence to the target codepage (if conversion failure happens then + *we revert to substituting with subchar) + */ +U_CAPI void U_EXPORT2 +UCNV_FROM_U_CALLBACK_ESCAPE ( + const void *context, + UConverterFromUnicodeArgs *fromArgs, + const char16_t *codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode * err) +{ + + char16_t valueString[VALUE_STRING_LENGTH]; + int32_t valueStringLength = 0; + int32_t i = 0; + + const char16_t *myValueSource = nullptr; + UErrorCode err2 = U_ZERO_ERROR; + UConverterFromUCallback original = nullptr; + const void *originalContext; + + UConverterFromUCallback ignoredCallback = nullptr; + const void *ignoredContext; + + if (reason > UCNV_IRREGULAR) + { + return; + } + else if (reason == UCNV_UNASSIGNED && IS_DEFAULT_IGNORABLE_CODE_POINT(codePoint)) + { + /* + * Skip if the codepoint has unicode property of default ignorable. + */ + *err = U_ZERO_ERROR; + return; + } + + ucnv_setFromUCallBack (fromArgs->converter, + (UConverterFromUCallback) UCNV_FROM_U_CALLBACK_SUBSTITUTE, + nullptr, + &original, + &originalContext, + &err2); + + if (U_FAILURE (err2)) + { + *err = err2; + return; + } + if(context==nullptr) + { + while (i < length) + { + valueString[valueStringLength++] = (char16_t) UNICODE_PERCENT_SIGN_CODEPOINT; /* adding % */ + valueString[valueStringLength++] = (char16_t) UNICODE_U_CODEPOINT; /* adding U */ + valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[i++], 16, 4); + } + } + else + { + switch(*((char*)context)) + { + case UCNV_PRV_ESCAPE_JAVA: + while (i < length) + { + valueString[valueStringLength++] = (char16_t) UNICODE_RS_CODEPOINT; /* adding \ */ + valueString[valueStringLength++] = (char16_t) UNICODE_U_LOW_CODEPOINT; /* adding u */ + valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[i++], 16, 4); + } + break; + + case UCNV_PRV_ESCAPE_C: + valueString[valueStringLength++] = (char16_t) UNICODE_RS_CODEPOINT; /* adding \ */ + + if(length==2){ + valueString[valueStringLength++] = (char16_t) UNICODE_U_CODEPOINT; /* adding U */ + valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, codePoint, 16, 8); + + } + else{ + valueString[valueStringLength++] = (char16_t) UNICODE_U_LOW_CODEPOINT; /* adding u */ + valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[0], 16, 4); + } + break; + + case UCNV_PRV_ESCAPE_XML_DEC: + + valueString[valueStringLength++] = (char16_t) UNICODE_AMP_CODEPOINT; /* adding & */ + valueString[valueStringLength++] = (char16_t) UNICODE_HASH_CODEPOINT; /* adding # */ + if(length==2){ + valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, codePoint, 10, 0); + } + else{ + valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[0], 10, 0); + } + valueString[valueStringLength++] = (char16_t) UNICODE_SEMICOLON_CODEPOINT; /* adding ; */ + break; + + case UCNV_PRV_ESCAPE_XML_HEX: + + valueString[valueStringLength++] = (char16_t) UNICODE_AMP_CODEPOINT; /* adding & */ + valueString[valueStringLength++] = (char16_t) UNICODE_HASH_CODEPOINT; /* adding # */ + valueString[valueStringLength++] = (char16_t) UNICODE_X_LOW_CODEPOINT; /* adding x */ + if(length==2){ + valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, codePoint, 16, 0); + } + else{ + valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[0], 16, 0); + } + valueString[valueStringLength++] = (char16_t) UNICODE_SEMICOLON_CODEPOINT; /* adding ; */ + break; + + case UCNV_PRV_ESCAPE_UNICODE: + valueString[valueStringLength++] = (char16_t) UNICODE_LEFT_CURLY_CODEPOINT; /* adding { */ + valueString[valueStringLength++] = (char16_t) UNICODE_U_CODEPOINT; /* adding U */ + valueString[valueStringLength++] = (char16_t) UNICODE_PLUS_CODEPOINT; /* adding + */ + if (length == 2) { + valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, codePoint, 16, 4); + } else { + valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[0], 16, 4); + } + valueString[valueStringLength++] = (char16_t) UNICODE_RIGHT_CURLY_CODEPOINT; /* adding } */ + break; + + case UCNV_PRV_ESCAPE_CSS2: + valueString[valueStringLength++] = (char16_t) UNICODE_RS_CODEPOINT; /* adding \ */ + valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, codePoint, 16, 0); + /* Always add space character, because the next character might be whitespace, + which would erroneously be considered the termination of the escape sequence. */ + valueString[valueStringLength++] = (char16_t) UNICODE_SPACE_CODEPOINT; + break; + + default: + while (i < length) + { + valueString[valueStringLength++] = (char16_t) UNICODE_PERCENT_SIGN_CODEPOINT; /* adding % */ + valueString[valueStringLength++] = (char16_t) UNICODE_U_CODEPOINT; /* adding U */ + valueStringLength += uprv_itou (valueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint16_t)codeUnits[i++], 16, 4); + } + } + } + myValueSource = valueString; + + /* reset the error */ + *err = U_ZERO_ERROR; + + ucnv_cbFromUWriteUChars(fromArgs, &myValueSource, myValueSource+valueStringLength, 0, err); + + ucnv_setFromUCallBack (fromArgs->converter, + original, + originalContext, + &ignoredCallback, + &ignoredContext, + &err2); + if (U_FAILURE (err2)) + { + *err = err2; + return; + } + + return; +} + + + +U_CAPI void U_EXPORT2 +UCNV_TO_U_CALLBACK_SKIP ( + const void *context, + UConverterToUnicodeArgs *toArgs, + const char* codeUnits, + int32_t length, + UConverterCallbackReason reason, + UErrorCode * err) +{ + (void)toArgs; + (void)codeUnits; + (void)length; + if (reason <= UCNV_IRREGULAR) + { + if (context == nullptr || (*((char*)context) == UCNV_PRV_STOP_ON_ILLEGAL && reason == UCNV_UNASSIGNED)) + { + *err = U_ZERO_ERROR; + } + /* else the caller must have set the error code accordingly. */ + } + /* else ignore the reset, close and clone calls. */ +} + +U_CAPI void U_EXPORT2 +UCNV_TO_U_CALLBACK_SUBSTITUTE ( + const void *context, + UConverterToUnicodeArgs *toArgs, + const char* codeUnits, + int32_t length, + UConverterCallbackReason reason, + UErrorCode * err) +{ + (void)codeUnits; + (void)length; + if (reason <= UCNV_IRREGULAR) + { + if (context == nullptr || (*((char*)context) == UCNV_PRV_STOP_ON_ILLEGAL && reason == UCNV_UNASSIGNED)) + { + *err = U_ZERO_ERROR; + ucnv_cbToUWriteSub(toArgs,0,err); + } + /* else the caller must have set the error code accordingly. */ + } + /* else ignore the reset, close and clone calls. */ +} + +/*uses uprv_itou to get a unicode escape sequence of the offensive sequence, + *and uses that as the substitution sequence + */ +U_CAPI void U_EXPORT2 +UCNV_TO_U_CALLBACK_ESCAPE ( + const void *context, + UConverterToUnicodeArgs *toArgs, + const char* codeUnits, + int32_t length, + UConverterCallbackReason reason, + UErrorCode * err) +{ + char16_t uniValueString[VALUE_STRING_LENGTH]; + int32_t valueStringLength = 0; + int32_t i = 0; + + if (reason > UCNV_IRREGULAR) + { + return; + } + + if(context==nullptr) + { + while (i < length) + { + uniValueString[valueStringLength++] = (char16_t) UNICODE_PERCENT_SIGN_CODEPOINT; /* adding % */ + uniValueString[valueStringLength++] = (char16_t) UNICODE_X_CODEPOINT; /* adding X */ + valueStringLength += uprv_itou (uniValueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint8_t) codeUnits[i++], 16, 2); + } + } + else + { + switch(*((char*)context)) + { + case UCNV_PRV_ESCAPE_XML_DEC: + while (i < length) + { + uniValueString[valueStringLength++] = (char16_t) UNICODE_AMP_CODEPOINT; /* adding & */ + uniValueString[valueStringLength++] = (char16_t) UNICODE_HASH_CODEPOINT; /* adding # */ + valueStringLength += uprv_itou (uniValueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint8_t)codeUnits[i++], 10, 0); + uniValueString[valueStringLength++] = (char16_t) UNICODE_SEMICOLON_CODEPOINT; /* adding ; */ + } + break; + + case UCNV_PRV_ESCAPE_XML_HEX: + while (i < length) + { + uniValueString[valueStringLength++] = (char16_t) UNICODE_AMP_CODEPOINT; /* adding & */ + uniValueString[valueStringLength++] = (char16_t) UNICODE_HASH_CODEPOINT; /* adding # */ + uniValueString[valueStringLength++] = (char16_t) UNICODE_X_LOW_CODEPOINT; /* adding x */ + valueStringLength += uprv_itou (uniValueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint8_t)codeUnits[i++], 16, 0); + uniValueString[valueStringLength++] = (char16_t) UNICODE_SEMICOLON_CODEPOINT; /* adding ; */ + } + break; + case UCNV_PRV_ESCAPE_C: + while (i < length) + { + uniValueString[valueStringLength++] = (char16_t) UNICODE_RS_CODEPOINT; /* adding \ */ + uniValueString[valueStringLength++] = (char16_t) UNICODE_X_LOW_CODEPOINT; /* adding x */ + valueStringLength += uprv_itou (uniValueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint8_t)codeUnits[i++], 16, 2); + } + break; + default: + while (i < length) + { + uniValueString[valueStringLength++] = (char16_t) UNICODE_PERCENT_SIGN_CODEPOINT; /* adding % */ + uniValueString[valueStringLength++] = (char16_t) UNICODE_X_CODEPOINT; /* adding X */ + uprv_itou (uniValueString + valueStringLength, VALUE_STRING_LENGTH - valueStringLength, (uint8_t) codeUnits[i++], 16, 2); + valueStringLength += 2; + } + } + } + /* reset the error */ + *err = U_ZERO_ERROR; + + ucnv_cbToUWriteUChars(toArgs, uniValueString, valueStringLength, 0, err); +} + +#endif diff --git a/deps/icu-small/source/common/ucnv_ext.cpp b/deps/icu-small/source/common/ucnv_ext.cpp index ffc3c93033a29c..354dae2594d25f 100644 --- a/deps/icu-small/source/common/ucnv_ext.cpp +++ b/deps/icu-small/source/common/ucnv_ext.cpp @@ -1,1143 +1,1143 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2003-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: ucnv_ext.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2003jun13 -* created by: Markus W. Scherer -* -* Conversion extensions -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION - -#include "unicode/uset.h" -#include "unicode/ustring.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" -#include "ucnv_ext.h" -#include "cmemory.h" -#include "uassert.h" - -/* to Unicode --------------------------------------------------------------- */ - -/* - * @return lookup value for the byte, if found; else 0 - */ -static inline uint32_t -ucnv_extFindToU(const uint32_t *toUSection, int32_t length, uint8_t byte) { - uint32_t word0, word; - int32_t i, start, limit; - - /* check the input byte against the lowest and highest section bytes */ - start=(int32_t)UCNV_EXT_TO_U_GET_BYTE(toUSection[0]); - limit=(int32_t)UCNV_EXT_TO_U_GET_BYTE(toUSection[length-1]); - if(byte1) { - return 0; /* no match of a DBCS sequence in SBCS mode */ - } else if(preLength==1) { - srcLength=0; - } else /* preLength==0 */ { - if(srcLength>1) { - srcLength=1; - } - } - flush=true; - } - - /* we must not remember fallback matches when not using fallbacks */ - - /* match input units until there is a full match or the input is consumed */ - for(;;) { - /* go to the next section */ - toUSection=toUTable+idx; - - /* read first pair of the section */ - value=*toUSection++; - length=UCNV_EXT_TO_U_GET_BYTE(value); - value=UCNV_EXT_TO_U_GET_VALUE(value); - if( value!=0 && - (UCNV_EXT_TO_U_IS_ROUNDTRIP(value) || - TO_U_USE_FALLBACK(useFallback)) && - UCNV_EXT_TO_U_VERIFY_SISO_MATCH(sisoState, i+j) - ) { - /* remember longest match so far */ - matchValue=value; - matchLength=i+j; - } - - /* match pre[] then src[] */ - if(iUCNV_EXT_MAX_BYTES) { - /* - * end of the entire input stream, stop with the longest match so far - * or: partial match must not be longer than UCNV_EXT_MAX_BYTES - * because it must fit into state buffers - */ - break; - } else { - /* continue with more input next time */ - return -length; - } - } - - /* search for the current UChar */ - value=ucnv_extFindToU(toUSection, length, b); - if(value==0) { - /* no match here, stop with the longest match so far */ - break; - } else { - if(UCNV_EXT_TO_U_IS_PARTIAL(value)) { - /* partial match, continue */ - idx=(int32_t)UCNV_EXT_TO_U_GET_PARTIAL_INDEX(value); - } else { - if( (UCNV_EXT_TO_U_IS_ROUNDTRIP(value) || - TO_U_USE_FALLBACK(useFallback)) && - UCNV_EXT_TO_U_VERIFY_SISO_MATCH(sisoState, i+j) - ) { - /* full match, stop with result */ - matchValue=value; - matchLength=i+j; - } else { - /* full match on fallback not taken, stop with the longest match so far */ - } - break; - } - } - } - - if(matchLength==0) { - /* no match at all */ - return 0; - } - - /* return result */ - *pMatchValue=UCNV_EXT_TO_U_MASK_ROUNDTRIP(matchValue); - return matchLength; -} - -static inline void -ucnv_extWriteToU(UConverter *cnv, const int32_t *cx, - uint32_t value, - UChar **target, const UChar *targetLimit, - int32_t **offsets, int32_t srcIndex, - UErrorCode *pErrorCode) { - /* output the result */ - if(UCNV_EXT_TO_U_IS_CODE_POINT(value)) { - /* output a single code point */ - ucnv_toUWriteCodePoint( - cnv, UCNV_EXT_TO_U_GET_CODE_POINT(value), - target, targetLimit, - offsets, srcIndex, - pErrorCode); - } else { - /* output a string - with correct data we have resultLength>0 */ - ucnv_toUWriteUChars( - cnv, - UCNV_EXT_ARRAY(cx, UCNV_EXT_TO_U_UCHARS_INDEX, UChar)+ - UCNV_EXT_TO_U_GET_INDEX(value), - UCNV_EXT_TO_U_GET_LENGTH(value), - target, targetLimit, - offsets, srcIndex, - pErrorCode); - } -} - -/* - * get the SI/SO toU state (state 0 is for SBCS, 1 for DBCS), - * or 1 for DBCS-only, - * or -1 if the converter is not SI/SO stateful - * - * Note: For SI/SO stateful converters getting here, - * cnv->mode==0 is equivalent to firstLength==1. - */ -#define UCNV_SISO_STATE(cnv) \ - ((cnv)->sharedData->mbcs.outputType==MBCS_OUTPUT_2_SISO ? (int8_t)(cnv)->mode : \ - (cnv)->sharedData->mbcs.outputType==MBCS_OUTPUT_DBCS_ONLY ? 1 : -1) - -/* - * targettoUBytes, firstLength, - *src, (int32_t)(srcLimit-*src), - &value, - cnv->useFallback, flush); - if(match>0) { - /* advance src pointer for the consumed input */ - *src+=match-firstLength; - - /* write result to target */ - ucnv_extWriteToU(cnv, cx, - value, - target, targetLimit, - offsets, srcIndex, - pErrorCode); - return true; - } else if(match<0) { - /* save state for partial match */ - const char *s; - int32_t j; - - /* copy the first code point */ - s=(const char *)cnv->toUBytes; - cnv->preToUFirstLength=(int8_t)firstLength; - for(j=0; jpreToU[j]=*s++; - } - - /* now copy the newly consumed input */ - s=*src; - match=-match; - for(; jpreToU[j]=*s++; - } - *src=s; /* same as *src=srcLimit; because we reached the end of input */ - cnv->preToULength=(int8_t)match; - return true; - } else /* match==0 no match */ { - return false; - } -} - -U_CFUNC UChar32 -ucnv_extSimpleMatchToU(const int32_t *cx, - const char *source, int32_t length, - UBool useFallback) { - uint32_t value = 0; /* initialize output-only param to 0 to silence gcc */ - int32_t match; - - if(length<=0) { - return 0xffff; - } - - /* try to match */ - match=ucnv_extMatchToU(cx, -1, - source, length, - NULL, 0, - &value, - useFallback, true); - if(match==length) { - /* write result for simple, single-character conversion */ - if(UCNV_EXT_TO_U_IS_CODE_POINT(value)) { - return UCNV_EXT_TO_U_GET_CODE_POINT(value); - } - } - - /* - * return no match because - * - match>0 && value points to string: simple conversion cannot handle multiple code points - * - match>0 && match!=length: not all input consumed, forbidden for this function - * - match==0: no match found in the first place - * - match<0: partial match, not supported for simple conversion (and flush==true) - */ - return 0xfffe; -} - -/* - * continue partial match with new input - * never called for simple, single-character conversion - */ -U_CFUNC void -ucnv_extContinueMatchToU(UConverter *cnv, - UConverterToUnicodeArgs *pArgs, int32_t srcIndex, - UErrorCode *pErrorCode) { - uint32_t value = 0; /* initialize output-only param to 0 to silence gcc */ - int32_t match, length; - - match=ucnv_extMatchToU(cnv->sharedData->mbcs.extIndexes, (int8_t)UCNV_SISO_STATE(cnv), - cnv->preToU, cnv->preToULength, - pArgs->source, (int32_t)(pArgs->sourceLimit-pArgs->source), - &value, - cnv->useFallback, pArgs->flush); - if(match>0) { - if(match>=cnv->preToULength) { - /* advance src pointer for the consumed input */ - pArgs->source+=match-cnv->preToULength; - cnv->preToULength=0; - } else { - /* the match did not use all of preToU[] - keep the rest for replay */ - length=cnv->preToULength-match; - uprv_memmove(cnv->preToU, cnv->preToU+match, length); - cnv->preToULength=(int8_t)-length; - } - - /* write result */ - ucnv_extWriteToU(cnv, cnv->sharedData->mbcs.extIndexes, - value, - &pArgs->target, pArgs->targetLimit, - &pArgs->offsets, srcIndex, - pErrorCode); - } else if(match<0) { - /* save state for partial match */ - const char *s; - int32_t j; - - /* just _append_ the newly consumed input to preToU[] */ - s=pArgs->source; - match=-match; - for(j=cnv->preToULength; jpreToU[j]=*s++; - } - pArgs->source=s; /* same as *src=srcLimit; because we reached the end of input */ - cnv->preToULength=(int8_t)match; - } else /* match==0 */ { - /* - * no match - * - * We need to split the previous input into two parts: - * - * 1. The first codepage character is unmappable - that's how we got into - * trying the extension data in the first place. - * We need to move it from the preToU buffer - * to the error buffer, set an error code, - * and prepare the rest of the previous input for 2. - * - * 2. The rest of the previous input must be converted once we - * come back from the callback for the first character. - * At that time, we have to try again from scratch to convert - * these input characters. - * The replay will be handled by the ucnv.c conversion code. - */ - - /* move the first codepage character to the error field */ - uprv_memcpy(cnv->toUBytes, cnv->preToU, cnv->preToUFirstLength); - cnv->toULength=cnv->preToUFirstLength; - - /* move the rest up inside the buffer */ - length=cnv->preToULength-cnv->preToUFirstLength; - if(length>0) { - uprv_memmove(cnv->preToU, cnv->preToU+cnv->preToUFirstLength, length); - } - - /* mark preToU for replay */ - cnv->preToULength=(int8_t)-length; - - /* set the error code for unassigned */ - *pErrorCode=U_INVALID_CHAR_FOUND; - } -} - -/* from Unicode ------------------------------------------------------------- */ - -// Use roundtrips, "good one-way" mappings, and some normal fallbacks. -static inline UBool -extFromUUseMapping(UBool useFallback, uint32_t value, UChar32 firstCP) { - return - ((value&UCNV_EXT_FROM_U_STATUS_MASK)!=0 || - FROM_U_USE_FALLBACK(useFallback, firstCP)) && - (value&UCNV_EXT_FROM_U_RESERVED_MASK)==0; -} - -/* - * @return index of the UChar, if found; else <0 - */ -static inline int32_t -ucnv_extFindFromU(const UChar *fromUSection, int32_t length, UChar u) { - int32_t i, start, limit; - - /* binary search */ - start=0; - limit=length; - for(;;) { - i=limit-start; - if(i<=1) { - break; /* done */ - } - /* start=0 - * @param src UChars that can be used to complete a match - * @param srcLength length of src, >=0 - * @param pMatchValue [out] output result value for the match from the data structure - * @param useFallback "use fallback" flag, usually from cnv->useFallback - * @param flush true if the end of the input stream is reached - * @return >1: matched, return value=total match length (number of input units matched) - * 1: matched, no mapping but request for - * (only for the first code point) - * 0: no match - * <0: partial match, return value=negative total match length - * (partial matches are never returned for flush==true) - * (partial matches are never returned as being longer than UCNV_EXT_MAX_UCHARS) - * the matchLength is 2 if only firstCP matched, and >2 if firstCP and - * further code units matched - */ -static int32_t -ucnv_extMatchFromU(const int32_t *cx, - UChar32 firstCP, - const UChar *pre, int32_t preLength, - const UChar *src, int32_t srcLength, - uint32_t *pMatchValue, - UBool useFallback, UBool flush) { - const uint16_t *stage12, *stage3; - const uint32_t *stage3b; - - const UChar *fromUTableUChars, *fromUSectionUChars; - const uint32_t *fromUTableValues, *fromUSectionValues; - - uint32_t value, matchValue; - int32_t i, j, idx, length, matchLength; - UChar c; - - if(cx==NULL) { - return 0; /* no extension data, no match */ - } - - /* trie lookup of firstCP */ - idx=firstCP>>10; /* stage 1 index */ - if(idx>=cx[UCNV_EXT_FROM_U_STAGE_1_LENGTH]) { - return 0; /* the first code point is outside the trie */ - } - - stage12=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_STAGE_12_INDEX, uint16_t); - stage3=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_STAGE_3_INDEX, uint16_t); - idx=UCNV_EXT_FROM_U(stage12, stage3, idx, firstCP); - - stage3b=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_STAGE_3B_INDEX, uint32_t); - value=stage3b[idx]; - if(value==0) { - return 0; - } - - /* - * Tests for (value&UCNV_EXT_FROM_U_RESERVED_MASK)==0: - * Do not interpret values with reserved bits used, for forward compatibility, - * and do not even remember intermediate results with reserved bits used. - */ - - if(UCNV_EXT_TO_U_IS_PARTIAL(value)) { - /* partial match, enter the loop below */ - idx=(int32_t)UCNV_EXT_FROM_U_GET_PARTIAL_INDEX(value); - - /* initialize */ - fromUTableUChars=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_UCHARS_INDEX, UChar); - fromUTableValues=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_VALUES_INDEX, uint32_t); - - matchValue=0; - i=j=matchLength=0; - - /* we must not remember fallback matches when not using fallbacks */ - - /* match input units until there is a full match or the input is consumed */ - for(;;) { - /* go to the next section */ - fromUSectionUChars=fromUTableUChars+idx; - fromUSectionValues=fromUTableValues+idx; - - /* read first pair of the section */ - length=*fromUSectionUChars++; - value=*fromUSectionValues++; - if(value!=0 && extFromUUseMapping(useFallback, value, firstCP)) { - /* remember longest match so far */ - matchValue=value; - matchLength=2+i+j; - } - - /* match pre[] then src[] */ - if(iUCNV_EXT_MAX_UCHARS) { - /* - * end of the entire input stream, stop with the longest match so far - * or: partial match must not be longer than UCNV_EXT_MAX_UCHARS - * because it must fit into state buffers - */ - break; - } else { - /* continue with more input next time */ - return -(2+length); - } - } - - /* search for the current UChar */ - idx=ucnv_extFindFromU(fromUSectionUChars, length, c); - if(idx<0) { - /* no match here, stop with the longest match so far */ - break; - } else { - value=fromUSectionValues[idx]; - if(UCNV_EXT_FROM_U_IS_PARTIAL(value)) { - /* partial match, continue */ - idx=(int32_t)UCNV_EXT_FROM_U_GET_PARTIAL_INDEX(value); - } else { - if(extFromUUseMapping(useFallback, value, firstCP)) { - /* full match, stop with result */ - matchValue=value; - matchLength=2+i+j; - } else { - /* full match on fallback not taken, stop with the longest match so far */ - } - break; - } - } - } - - if(matchLength==0) { - /* no match at all */ - return 0; - } - } else /* result from firstCP trie lookup */ { - if(extFromUUseMapping(useFallback, value, firstCP)) { - /* full match, stop with result */ - matchValue=value; - matchLength=2; - } else { - /* fallback not taken */ - return 0; - } - } - - /* return result */ - if(matchValue==UCNV_EXT_FROM_U_SUBCHAR1) { - return 1; /* assert matchLength==2 */ - } - - *pMatchValue=matchValue; - return matchLength; -} - -/* - * @param value fromUnicode mapping table value; ignores roundtrip and reserved bits - */ -static inline void -ucnv_extWriteFromU(UConverter *cnv, const int32_t *cx, - uint32_t value, - char **target, const char *targetLimit, - int32_t **offsets, int32_t srcIndex, - UErrorCode *pErrorCode) { - uint8_t buffer[1+UCNV_EXT_MAX_BYTES]; - const uint8_t *result; - int32_t length, prevLength; - - length=UCNV_EXT_FROM_U_GET_LENGTH(value); - value=(uint32_t)UCNV_EXT_FROM_U_GET_DATA(value); - - /* output the result */ - if(length<=UCNV_EXT_FROM_U_MAX_DIRECT_LENGTH) { - /* - * Generate a byte array and then write it below. - * This is not the fastest possible way, but it should be ok for - * extension mappings, and it is much simpler. - * Offset and overflow handling are only done once this way. - */ - uint8_t *p=buffer+1; /* reserve buffer[0] for shiftByte below */ - switch(length) { - case 3: - *p++=(uint8_t)(value>>16); - U_FALLTHROUGH; - case 2: - *p++=(uint8_t)(value>>8); - U_FALLTHROUGH; - case 1: - *p++=(uint8_t)value; - U_FALLTHROUGH; - default: - break; /* will never occur */ - } - result=buffer+1; - } else { - result=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_BYTES_INDEX, uint8_t)+value; - } - - /* with correct data we have length>0 */ - - if((prevLength=cnv->fromUnicodeStatus)!=0) { - /* handle SI/SO stateful output */ - uint8_t shiftByte; - - if(prevLength>1 && length==1) { - /* change from double-byte mode to single-byte */ - shiftByte=(uint8_t)UCNV_SI; - cnv->fromUnicodeStatus=1; - } else if(prevLength==1 && length>1) { - /* change from single-byte mode to double-byte */ - shiftByte=(uint8_t)UCNV_SO; - cnv->fromUnicodeStatus=2; - } else { - shiftByte=0; - } - - if(shiftByte!=0) { - /* prepend the shift byte to the result bytes */ - buffer[0]=shiftByte; - if(result!=buffer+1) { - uprv_memcpy(buffer+1, result, length); - } - result=buffer; - ++length; - } - } - - ucnv_fromUWriteBytes(cnv, (const char *)result, length, - target, targetLimit, - offsets, srcIndex, - pErrorCode); -} - -/* - * targetuseFallback, flush); - - /* reject a match if the result is a single byte for DBCS-only */ - if( match>=2 && - !(UCNV_EXT_FROM_U_GET_LENGTH(value)==1 && - cnv->sharedData->mbcs.outputType==MBCS_OUTPUT_DBCS_ONLY) - ) { - /* advance src pointer for the consumed input */ - *src+=match-2; /* remove 2 for the initial code point */ - - /* write result to target */ - ucnv_extWriteFromU(cnv, cx, - value, - target, targetLimit, - offsets, srcIndex, - pErrorCode); - return true; - } else if(match<0) { - /* save state for partial match */ - const UChar *s; - int32_t j; - - /* copy the first code point */ - cnv->preFromUFirstCP=cp; - - /* now copy the newly consumed input */ - s=*src; - match=-match-2; /* remove 2 for the initial code point */ - for(j=0; jpreFromU[j]=*s++; - } - *src=s; /* same as *src=srcLimit; because we reached the end of input */ - cnv->preFromULength=(int8_t)match; - return true; - } else if(match==1) { - /* matched, no mapping but request for */ - cnv->useSubChar1=true; - return false; - } else /* match==0 no match */ { - return false; - } -} - -/* - * Used by ISO 2022 implementation. - * @return number of bytes in *pValue; negative number if fallback; 0 for no mapping - */ -U_CFUNC int32_t -ucnv_extSimpleMatchFromU(const int32_t *cx, - UChar32 cp, uint32_t *pValue, - UBool useFallback) { - uint32_t value; - int32_t match; - - /* try to match */ - match=ucnv_extMatchFromU(cx, - cp, - NULL, 0, - NULL, 0, - &value, - useFallback, true); - if(match>=2) { - /* write result for simple, single-character conversion */ - int32_t length; - int isRoundtrip; - - isRoundtrip=UCNV_EXT_FROM_U_IS_ROUNDTRIP(value); - length=UCNV_EXT_FROM_U_GET_LENGTH(value); - value=(uint32_t)UCNV_EXT_FROM_U_GET_DATA(value); - - if(length<=UCNV_EXT_FROM_U_MAX_DIRECT_LENGTH) { - *pValue=value; - return isRoundtrip ? length : -length; -#if 0 /* not currently used */ - } else if(length==4) { - /* de-serialize a 4-byte result */ - const uint8_t *result=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_BYTES_INDEX, uint8_t)+value; - *pValue= - ((uint32_t)result[0]<<24)| - ((uint32_t)result[1]<<16)| - ((uint32_t)result[2]<<8)| - result[3]; - return isRoundtrip ? 4 : -4; -#endif - } - } - - /* - * return no match because - * - match>1 && resultLength>4: result too long for simple conversion - * - match==1: no match found, preferred - * - match==0: no match found in the first place - * - match<0: partial match, not supported for simple conversion (and flush==true) - */ - return 0; -} - -/* - * continue partial match with new input, requires cnv->preFromUFirstCP>=0 - * never called for simple, single-character conversion - */ -U_CFUNC void -ucnv_extContinueMatchFromU(UConverter *cnv, - UConverterFromUnicodeArgs *pArgs, int32_t srcIndex, - UErrorCode *pErrorCode) { - uint32_t value = 0; /* initialize output-only param to 0 to silence gcc */ - int32_t match; - - match=ucnv_extMatchFromU(cnv->sharedData->mbcs.extIndexes, - cnv->preFromUFirstCP, - cnv->preFromU, cnv->preFromULength, - pArgs->source, (int32_t)(pArgs->sourceLimit-pArgs->source), - &value, - cnv->useFallback, pArgs->flush); - if(match>=2) { - match-=2; /* remove 2 for the initial code point */ - - if(match>=cnv->preFromULength) { - /* advance src pointer for the consumed input */ - pArgs->source+=match-cnv->preFromULength; - cnv->preFromULength=0; - } else { - /* the match did not use all of preFromU[] - keep the rest for replay */ - int32_t length=cnv->preFromULength-match; - u_memmove(cnv->preFromU, cnv->preFromU+match, length); - cnv->preFromULength=(int8_t)-length; - } - - /* finish the partial match */ - cnv->preFromUFirstCP=U_SENTINEL; - - /* write result */ - ucnv_extWriteFromU(cnv, cnv->sharedData->mbcs.extIndexes, - value, - &pArgs->target, pArgs->targetLimit, - &pArgs->offsets, srcIndex, - pErrorCode); - } else if(match<0) { - /* save state for partial match */ - const UChar *s; - int32_t j; - - /* just _append_ the newly consumed input to preFromU[] */ - s=pArgs->source; - match=-match-2; /* remove 2 for the initial code point */ - for(j=cnv->preFromULength; j=0); - cnv->preFromU[j]=*s++; - } - pArgs->source=s; /* same as *src=srcLimit; because we reached the end of input */ - cnv->preFromULength=(int8_t)match; - } else /* match==0 or 1 */ { - /* - * no match - * - * We need to split the previous input into two parts: - * - * 1. The first code point is unmappable - that's how we got into - * trying the extension data in the first place. - * We need to move it from the preFromU buffer - * to the error buffer, set an error code, - * and prepare the rest of the previous input for 2. - * - * 2. The rest of the previous input must be converted once we - * come back from the callback for the first code point. - * At that time, we have to try again from scratch to convert - * these input characters. - * The replay will be handled by the ucnv.c conversion code. - */ - - if(match==1) { - /* matched, no mapping but request for */ - cnv->useSubChar1=true; - } - - /* move the first code point to the error field */ - cnv->fromUChar32=cnv->preFromUFirstCP; - cnv->preFromUFirstCP=U_SENTINEL; - - /* mark preFromU for replay */ - cnv->preFromULength=-cnv->preFromULength; - - /* set the error code for unassigned */ - *pErrorCode=U_INVALID_CHAR_FOUND; - } -} - -static UBool -extSetUseMapping(UConverterUnicodeSet which, int32_t minLength, uint32_t value) { - if(which==UCNV_ROUNDTRIP_SET) { - // Add only code points for which the roundtrip flag is set. - // Do not add any fallbacks, even if ucnv_fromUnicode() would use them - // (fallbacks from PUA). See the API docs for ucnv_getUnicodeSet(). - // - // By analogy, also do not add "good one-way" mappings. - // - // Do not add entries with reserved bits set. - if(((value&(UCNV_EXT_FROM_U_ROUNDTRIP_FLAG|UCNV_EXT_FROM_U_RESERVED_MASK))!= - UCNV_EXT_FROM_U_ROUNDTRIP_FLAG)) { - return false; - } - } else /* UCNV_ROUNDTRIP_AND_FALLBACK_SET */ { - // Do not add entries with reserved bits set. - if((value&UCNV_EXT_FROM_U_RESERVED_MASK)!=0) { - return false; - } - } - // Do not add entries or other (future?) pseudo-entries - // with an output length of 0. - return UCNV_EXT_FROM_U_GET_LENGTH(value)>=minLength; -} - -static void -ucnv_extGetUnicodeSetString(const UConverterSharedData *sharedData, - const int32_t *cx, - const USetAdder *sa, - UConverterUnicodeSet which, - int32_t minLength, - UChar32 firstCP, - UChar s[UCNV_EXT_MAX_UCHARS], int32_t length, - int32_t sectionIndex, - UErrorCode *pErrorCode) { - const UChar *fromUSectionUChars; - const uint32_t *fromUSectionValues; - - uint32_t value; - int32_t i, count; - - fromUSectionUChars=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_UCHARS_INDEX, UChar)+sectionIndex; - fromUSectionValues=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_VALUES_INDEX, uint32_t)+sectionIndex; - - /* read first pair of the section */ - count=*fromUSectionUChars++; - value=*fromUSectionValues++; - - if(extSetUseMapping(which, minLength, value)) { - if(length==U16_LENGTH(firstCP)) { - /* add the initial code point */ - sa->add(sa->set, firstCP); - } else { - /* add the string so far */ - sa->addString(sa->set, s, length); - } - } - - for(i=0; iaddString(sa->set, s, length+1); - } - } -} - -U_CFUNC void -ucnv_extGetUnicodeSet(const UConverterSharedData *sharedData, - const USetAdder *sa, - UConverterUnicodeSet which, - UConverterSetFilter filter, - UErrorCode *pErrorCode) { - const int32_t *cx; - const uint16_t *stage12, *stage3, *ps2, *ps3; - const uint32_t *stage3b; - - uint32_t value; - int32_t st1, stage1Length, st2, st3, minLength; - - UChar s[UCNV_EXT_MAX_UCHARS]; - UChar32 c; - int32_t length; - - cx=sharedData->mbcs.extIndexes; - if(cx==NULL) { - return; - } - - stage12=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_STAGE_12_INDEX, uint16_t); - stage3=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_STAGE_3_INDEX, uint16_t); - stage3b=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_STAGE_3B_INDEX, uint32_t); - - stage1Length=cx[UCNV_EXT_FROM_U_STAGE_1_LENGTH]; - - /* enumerate the from-Unicode trie table */ - c=0; /* keep track of the current code point while enumerating */ - - if(filter==UCNV_SET_FILTER_2022_CN) { - minLength=3; - } else if( sharedData->mbcs.outputType==MBCS_OUTPUT_DBCS_ONLY || - filter!=UCNV_SET_FILTER_NONE - ) { - /* DBCS-only, ignore single-byte results */ - minLength=2; - } else { - minLength=1; - } - - /* - * the trie enumeration is almost the same as - * in MBCSGetUnicodeSet() for MBCS_OUTPUT_1 - */ - for(st1=0; st1stage1Length) { - ps2=stage12+st2; - for(st2=0; st2<64; ++st2) { - if((st3=(int32_t)ps2[st2]<=0x8140 && value<=0xeffc)) { - continue; - } - break; - case UCNV_SET_FILTER_GR94DBCS: - if(!(UCNV_EXT_FROM_U_GET_LENGTH(value)==2 && - (uint16_t)((value=UCNV_EXT_FROM_U_GET_DATA(value))-0xa1a1)<=(0xfefe - 0xa1a1) && - (uint8_t)(value-0xa1)<=(0xfe - 0xa1))) { - continue; - } - break; - case UCNV_SET_FILTER_HZ: - if(!(UCNV_EXT_FROM_U_GET_LENGTH(value)==2 && - (uint16_t)((value=UCNV_EXT_FROM_U_GET_DATA(value))-0xa1a1)<=(0xfdfe - 0xa1a1) && - (uint8_t)(value-0xa1)<=(0xfe - 0xa1))) { - continue; - } - break; - default: - /* - * UCNV_SET_FILTER_NONE, - * or UCNV_SET_FILTER_DBCS_ONLY which is handled via minLength - */ - break; - } - sa->add(sa->set, c); - } - } while((++c&0xf)!=0); - } else { - c+=16; /* empty stage 3 block */ - } - } - } else { - c+=1024; /* empty stage 2 block */ - } - } -} - -#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2003-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ucnv_ext.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2003jun13 +* created by: Markus W. Scherer +* +* Conversion extensions +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION + +#include "unicode/uset.h" +#include "unicode/ustring.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" +#include "ucnv_ext.h" +#include "cmemory.h" +#include "uassert.h" + +/* to Unicode --------------------------------------------------------------- */ + +/* + * @return lookup value for the byte, if found; else 0 + */ +static inline uint32_t +ucnv_extFindToU(const uint32_t *toUSection, int32_t length, uint8_t byte) { + uint32_t word0, word; + int32_t i, start, limit; + + /* check the input byte against the lowest and highest section bytes */ + start=(int32_t)UCNV_EXT_TO_U_GET_BYTE(toUSection[0]); + limit=(int32_t)UCNV_EXT_TO_U_GET_BYTE(toUSection[length-1]); + if(byte1) { + return 0; /* no match of a DBCS sequence in SBCS mode */ + } else if(preLength==1) { + srcLength=0; + } else /* preLength==0 */ { + if(srcLength>1) { + srcLength=1; + } + } + flush=true; + } + + /* we must not remember fallback matches when not using fallbacks */ + + /* match input units until there is a full match or the input is consumed */ + for(;;) { + /* go to the next section */ + toUSection=toUTable+idx; + + /* read first pair of the section */ + value=*toUSection++; + length=UCNV_EXT_TO_U_GET_BYTE(value); + value=UCNV_EXT_TO_U_GET_VALUE(value); + if( value!=0 && + (UCNV_EXT_TO_U_IS_ROUNDTRIP(value) || + TO_U_USE_FALLBACK(useFallback)) && + UCNV_EXT_TO_U_VERIFY_SISO_MATCH(sisoState, i+j) + ) { + /* remember longest match so far */ + matchValue=value; + matchLength=i+j; + } + + /* match pre[] then src[] */ + if(iUCNV_EXT_MAX_BYTES) { + /* + * end of the entire input stream, stop with the longest match so far + * or: partial match must not be longer than UCNV_EXT_MAX_BYTES + * because it must fit into state buffers + */ + break; + } else { + /* continue with more input next time */ + return -length; + } + } + + /* search for the current char16_t */ + value=ucnv_extFindToU(toUSection, length, b); + if(value==0) { + /* no match here, stop with the longest match so far */ + break; + } else { + if(UCNV_EXT_TO_U_IS_PARTIAL(value)) { + /* partial match, continue */ + idx=(int32_t)UCNV_EXT_TO_U_GET_PARTIAL_INDEX(value); + } else { + if( (UCNV_EXT_TO_U_IS_ROUNDTRIP(value) || + TO_U_USE_FALLBACK(useFallback)) && + UCNV_EXT_TO_U_VERIFY_SISO_MATCH(sisoState, i+j) + ) { + /* full match, stop with result */ + matchValue=value; + matchLength=i+j; + } else { + /* full match on fallback not taken, stop with the longest match so far */ + } + break; + } + } + } + + if(matchLength==0) { + /* no match at all */ + return 0; + } + + /* return result */ + *pMatchValue=UCNV_EXT_TO_U_MASK_ROUNDTRIP(matchValue); + return matchLength; +} + +static inline void +ucnv_extWriteToU(UConverter *cnv, const int32_t *cx, + uint32_t value, + char16_t **target, const char16_t *targetLimit, + int32_t **offsets, int32_t srcIndex, + UErrorCode *pErrorCode) { + /* output the result */ + if(UCNV_EXT_TO_U_IS_CODE_POINT(value)) { + /* output a single code point */ + ucnv_toUWriteCodePoint( + cnv, UCNV_EXT_TO_U_GET_CODE_POINT(value), + target, targetLimit, + offsets, srcIndex, + pErrorCode); + } else { + /* output a string - with correct data we have resultLength>0 */ + ucnv_toUWriteUChars( + cnv, + UCNV_EXT_ARRAY(cx, UCNV_EXT_TO_U_UCHARS_INDEX, char16_t)+ + UCNV_EXT_TO_U_GET_INDEX(value), + UCNV_EXT_TO_U_GET_LENGTH(value), + target, targetLimit, + offsets, srcIndex, + pErrorCode); + } +} + +/* + * get the SI/SO toU state (state 0 is for SBCS, 1 for DBCS), + * or 1 for DBCS-only, + * or -1 if the converter is not SI/SO stateful + * + * Note: For SI/SO stateful converters getting here, + * cnv->mode==0 is equivalent to firstLength==1. + */ +#define UCNV_SISO_STATE(cnv) \ + ((cnv)->sharedData->mbcs.outputType==MBCS_OUTPUT_2_SISO ? (int8_t)(cnv)->mode : \ + (cnv)->sharedData->mbcs.outputType==MBCS_OUTPUT_DBCS_ONLY ? 1 : -1) + +/* + * targettoUBytes, firstLength, + *src, (int32_t)(srcLimit-*src), + &value, + cnv->useFallback, flush); + if(match>0) { + /* advance src pointer for the consumed input */ + *src+=match-firstLength; + + /* write result to target */ + ucnv_extWriteToU(cnv, cx, + value, + target, targetLimit, + offsets, srcIndex, + pErrorCode); + return true; + } else if(match<0) { + /* save state for partial match */ + const char *s; + int32_t j; + + /* copy the first code point */ + s=(const char *)cnv->toUBytes; + cnv->preToUFirstLength=(int8_t)firstLength; + for(j=0; jpreToU[j]=*s++; + } + + /* now copy the newly consumed input */ + s=*src; + match=-match; + for(; jpreToU[j]=*s++; + } + *src=s; /* same as *src=srcLimit; because we reached the end of input */ + cnv->preToULength=(int8_t)match; + return true; + } else /* match==0 no match */ { + return false; + } +} + +U_CFUNC UChar32 +ucnv_extSimpleMatchToU(const int32_t *cx, + const char *source, int32_t length, + UBool useFallback) { + uint32_t value = 0; /* initialize output-only param to 0 to silence gcc */ + int32_t match; + + if(length<=0) { + return 0xffff; + } + + /* try to match */ + match=ucnv_extMatchToU(cx, -1, + source, length, + nullptr, 0, + &value, + useFallback, true); + if(match==length) { + /* write result for simple, single-character conversion */ + if(UCNV_EXT_TO_U_IS_CODE_POINT(value)) { + return UCNV_EXT_TO_U_GET_CODE_POINT(value); + } + } + + /* + * return no match because + * - match>0 && value points to string: simple conversion cannot handle multiple code points + * - match>0 && match!=length: not all input consumed, forbidden for this function + * - match==0: no match found in the first place + * - match<0: partial match, not supported for simple conversion (and flush==true) + */ + return 0xfffe; +} + +/* + * continue partial match with new input + * never called for simple, single-character conversion + */ +U_CFUNC void +ucnv_extContinueMatchToU(UConverter *cnv, + UConverterToUnicodeArgs *pArgs, int32_t srcIndex, + UErrorCode *pErrorCode) { + uint32_t value = 0; /* initialize output-only param to 0 to silence gcc */ + int32_t match, length; + + match=ucnv_extMatchToU(cnv->sharedData->mbcs.extIndexes, (int8_t)UCNV_SISO_STATE(cnv), + cnv->preToU, cnv->preToULength, + pArgs->source, (int32_t)(pArgs->sourceLimit-pArgs->source), + &value, + cnv->useFallback, pArgs->flush); + if(match>0) { + if(match>=cnv->preToULength) { + /* advance src pointer for the consumed input */ + pArgs->source+=match-cnv->preToULength; + cnv->preToULength=0; + } else { + /* the match did not use all of preToU[] - keep the rest for replay */ + length=cnv->preToULength-match; + uprv_memmove(cnv->preToU, cnv->preToU+match, length); + cnv->preToULength=(int8_t)-length; + } + + /* write result */ + ucnv_extWriteToU(cnv, cnv->sharedData->mbcs.extIndexes, + value, + &pArgs->target, pArgs->targetLimit, + &pArgs->offsets, srcIndex, + pErrorCode); + } else if(match<0) { + /* save state for partial match */ + const char *s; + int32_t j; + + /* just _append_ the newly consumed input to preToU[] */ + s=pArgs->source; + match=-match; + for(j=cnv->preToULength; jpreToU[j]=*s++; + } + pArgs->source=s; /* same as *src=srcLimit; because we reached the end of input */ + cnv->preToULength=(int8_t)match; + } else /* match==0 */ { + /* + * no match + * + * We need to split the previous input into two parts: + * + * 1. The first codepage character is unmappable - that's how we got into + * trying the extension data in the first place. + * We need to move it from the preToU buffer + * to the error buffer, set an error code, + * and prepare the rest of the previous input for 2. + * + * 2. The rest of the previous input must be converted once we + * come back from the callback for the first character. + * At that time, we have to try again from scratch to convert + * these input characters. + * The replay will be handled by the ucnv.c conversion code. + */ + + /* move the first codepage character to the error field */ + uprv_memcpy(cnv->toUBytes, cnv->preToU, cnv->preToUFirstLength); + cnv->toULength=cnv->preToUFirstLength; + + /* move the rest up inside the buffer */ + length=cnv->preToULength-cnv->preToUFirstLength; + if(length>0) { + uprv_memmove(cnv->preToU, cnv->preToU+cnv->preToUFirstLength, length); + } + + /* mark preToU for replay */ + cnv->preToULength=(int8_t)-length; + + /* set the error code for unassigned */ + *pErrorCode=U_INVALID_CHAR_FOUND; + } +} + +/* from Unicode ------------------------------------------------------------- */ + +// Use roundtrips, "good one-way" mappings, and some normal fallbacks. +static inline UBool +extFromUUseMapping(UBool useFallback, uint32_t value, UChar32 firstCP) { + return + ((value&UCNV_EXT_FROM_U_STATUS_MASK)!=0 || + FROM_U_USE_FALLBACK(useFallback, firstCP)) && + (value&UCNV_EXT_FROM_U_RESERVED_MASK)==0; +} + +/* + * @return index of the char16_t, if found; else <0 + */ +static inline int32_t +ucnv_extFindFromU(const char16_t *fromUSection, int32_t length, char16_t u) { + int32_t i, start, limit; + + /* binary search */ + start=0; + limit=length; + for(;;) { + i=limit-start; + if(i<=1) { + break; /* done */ + } + /* start=0 + * @param src UChars that can be used to complete a match + * @param srcLength length of src, >=0 + * @param pMatchValue [out] output result value for the match from the data structure + * @param useFallback "use fallback" flag, usually from cnv->useFallback + * @param flush true if the end of the input stream is reached + * @return >1: matched, return value=total match length (number of input units matched) + * 1: matched, no mapping but request for + * (only for the first code point) + * 0: no match + * <0: partial match, return value=negative total match length + * (partial matches are never returned for flush==true) + * (partial matches are never returned as being longer than UCNV_EXT_MAX_UCHARS) + * the matchLength is 2 if only firstCP matched, and >2 if firstCP and + * further code units matched + */ +static int32_t +ucnv_extMatchFromU(const int32_t *cx, + UChar32 firstCP, + const char16_t *pre, int32_t preLength, + const char16_t *src, int32_t srcLength, + uint32_t *pMatchValue, + UBool useFallback, UBool flush) { + const uint16_t *stage12, *stage3; + const uint32_t *stage3b; + + const char16_t *fromUTableUChars, *fromUSectionUChars; + const uint32_t *fromUTableValues, *fromUSectionValues; + + uint32_t value, matchValue; + int32_t i, j, idx, length, matchLength; + char16_t c; + + if(cx==nullptr) { + return 0; /* no extension data, no match */ + } + + /* trie lookup of firstCP */ + idx=firstCP>>10; /* stage 1 index */ + if(idx>=cx[UCNV_EXT_FROM_U_STAGE_1_LENGTH]) { + return 0; /* the first code point is outside the trie */ + } + + stage12=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_STAGE_12_INDEX, uint16_t); + stage3=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_STAGE_3_INDEX, uint16_t); + idx=UCNV_EXT_FROM_U(stage12, stage3, idx, firstCP); + + stage3b=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_STAGE_3B_INDEX, uint32_t); + value=stage3b[idx]; + if(value==0) { + return 0; + } + + /* + * Tests for (value&UCNV_EXT_FROM_U_RESERVED_MASK)==0: + * Do not interpret values with reserved bits used, for forward compatibility, + * and do not even remember intermediate results with reserved bits used. + */ + + if(UCNV_EXT_TO_U_IS_PARTIAL(value)) { + /* partial match, enter the loop below */ + idx=(int32_t)UCNV_EXT_FROM_U_GET_PARTIAL_INDEX(value); + + /* initialize */ + fromUTableUChars=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_UCHARS_INDEX, char16_t); + fromUTableValues=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_VALUES_INDEX, uint32_t); + + matchValue=0; + i=j=matchLength=0; + + /* we must not remember fallback matches when not using fallbacks */ + + /* match input units until there is a full match or the input is consumed */ + for(;;) { + /* go to the next section */ + fromUSectionUChars=fromUTableUChars+idx; + fromUSectionValues=fromUTableValues+idx; + + /* read first pair of the section */ + length=*fromUSectionUChars++; + value=*fromUSectionValues++; + if(value!=0 && extFromUUseMapping(useFallback, value, firstCP)) { + /* remember longest match so far */ + matchValue=value; + matchLength=2+i+j; + } + + /* match pre[] then src[] */ + if(iUCNV_EXT_MAX_UCHARS) { + /* + * end of the entire input stream, stop with the longest match so far + * or: partial match must not be longer than UCNV_EXT_MAX_UCHARS + * because it must fit into state buffers + */ + break; + } else { + /* continue with more input next time */ + return -(2+length); + } + } + + /* search for the current char16_t */ + idx=ucnv_extFindFromU(fromUSectionUChars, length, c); + if(idx<0) { + /* no match here, stop with the longest match so far */ + break; + } else { + value=fromUSectionValues[idx]; + if(UCNV_EXT_FROM_U_IS_PARTIAL(value)) { + /* partial match, continue */ + idx=(int32_t)UCNV_EXT_FROM_U_GET_PARTIAL_INDEX(value); + } else { + if(extFromUUseMapping(useFallback, value, firstCP)) { + /* full match, stop with result */ + matchValue=value; + matchLength=2+i+j; + } else { + /* full match on fallback not taken, stop with the longest match so far */ + } + break; + } + } + } + + if(matchLength==0) { + /* no match at all */ + return 0; + } + } else /* result from firstCP trie lookup */ { + if(extFromUUseMapping(useFallback, value, firstCP)) { + /* full match, stop with result */ + matchValue=value; + matchLength=2; + } else { + /* fallback not taken */ + return 0; + } + } + + /* return result */ + if(matchValue==UCNV_EXT_FROM_U_SUBCHAR1) { + return 1; /* assert matchLength==2 */ + } + + *pMatchValue=matchValue; + return matchLength; +} + +/* + * @param value fromUnicode mapping table value; ignores roundtrip and reserved bits + */ +static inline void +ucnv_extWriteFromU(UConverter *cnv, const int32_t *cx, + uint32_t value, + char **target, const char *targetLimit, + int32_t **offsets, int32_t srcIndex, + UErrorCode *pErrorCode) { + uint8_t buffer[1+UCNV_EXT_MAX_BYTES]; + const uint8_t *result; + int32_t length, prevLength; + + length=UCNV_EXT_FROM_U_GET_LENGTH(value); + value=(uint32_t)UCNV_EXT_FROM_U_GET_DATA(value); + + /* output the result */ + if(length<=UCNV_EXT_FROM_U_MAX_DIRECT_LENGTH) { + /* + * Generate a byte array and then write it below. + * This is not the fastest possible way, but it should be ok for + * extension mappings, and it is much simpler. + * Offset and overflow handling are only done once this way. + */ + uint8_t *p=buffer+1; /* reserve buffer[0] for shiftByte below */ + switch(length) { + case 3: + *p++=(uint8_t)(value>>16); + U_FALLTHROUGH; + case 2: + *p++=(uint8_t)(value>>8); + U_FALLTHROUGH; + case 1: + *p++=(uint8_t)value; + U_FALLTHROUGH; + default: + break; /* will never occur */ + } + result=buffer+1; + } else { + result=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_BYTES_INDEX, uint8_t)+value; + } + + /* with correct data we have length>0 */ + + if((prevLength=cnv->fromUnicodeStatus)!=0) { + /* handle SI/SO stateful output */ + uint8_t shiftByte; + + if(prevLength>1 && length==1) { + /* change from double-byte mode to single-byte */ + shiftByte=(uint8_t)UCNV_SI; + cnv->fromUnicodeStatus=1; + } else if(prevLength==1 && length>1) { + /* change from single-byte mode to double-byte */ + shiftByte=(uint8_t)UCNV_SO; + cnv->fromUnicodeStatus=2; + } else { + shiftByte=0; + } + + if(shiftByte!=0) { + /* prepend the shift byte to the result bytes */ + buffer[0]=shiftByte; + if(result!=buffer+1) { + uprv_memcpy(buffer+1, result, length); + } + result=buffer; + ++length; + } + } + + ucnv_fromUWriteBytes(cnv, (const char *)result, length, + target, targetLimit, + offsets, srcIndex, + pErrorCode); +} + +/* + * targetuseFallback, flush); + + /* reject a match if the result is a single byte for DBCS-only */ + if( match>=2 && + !(UCNV_EXT_FROM_U_GET_LENGTH(value)==1 && + cnv->sharedData->mbcs.outputType==MBCS_OUTPUT_DBCS_ONLY) + ) { + /* advance src pointer for the consumed input */ + *src+=match-2; /* remove 2 for the initial code point */ + + /* write result to target */ + ucnv_extWriteFromU(cnv, cx, + value, + target, targetLimit, + offsets, srcIndex, + pErrorCode); + return true; + } else if(match<0) { + /* save state for partial match */ + const char16_t *s; + int32_t j; + + /* copy the first code point */ + cnv->preFromUFirstCP=cp; + + /* now copy the newly consumed input */ + s=*src; + match=-match-2; /* remove 2 for the initial code point */ + for(j=0; jpreFromU[j]=*s++; + } + *src=s; /* same as *src=srcLimit; because we reached the end of input */ + cnv->preFromULength=(int8_t)match; + return true; + } else if(match==1) { + /* matched, no mapping but request for */ + cnv->useSubChar1=true; + return false; + } else /* match==0 no match */ { + return false; + } +} + +/* + * Used by ISO 2022 implementation. + * @return number of bytes in *pValue; negative number if fallback; 0 for no mapping + */ +U_CFUNC int32_t +ucnv_extSimpleMatchFromU(const int32_t *cx, + UChar32 cp, uint32_t *pValue, + UBool useFallback) { + uint32_t value; + int32_t match; + + /* try to match */ + match=ucnv_extMatchFromU(cx, + cp, + nullptr, 0, + nullptr, 0, + &value, + useFallback, true); + if(match>=2) { + /* write result for simple, single-character conversion */ + int32_t length; + int isRoundtrip; + + isRoundtrip=UCNV_EXT_FROM_U_IS_ROUNDTRIP(value); + length=UCNV_EXT_FROM_U_GET_LENGTH(value); + value=(uint32_t)UCNV_EXT_FROM_U_GET_DATA(value); + + if(length<=UCNV_EXT_FROM_U_MAX_DIRECT_LENGTH) { + *pValue=value; + return isRoundtrip ? length : -length; +#if 0 /* not currently used */ + } else if(length==4) { + /* de-serialize a 4-byte result */ + const uint8_t *result=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_BYTES_INDEX, uint8_t)+value; + *pValue= + ((uint32_t)result[0]<<24)| + ((uint32_t)result[1]<<16)| + ((uint32_t)result[2]<<8)| + result[3]; + return isRoundtrip ? 4 : -4; +#endif + } + } + + /* + * return no match because + * - match>1 && resultLength>4: result too long for simple conversion + * - match==1: no match found, preferred + * - match==0: no match found in the first place + * - match<0: partial match, not supported for simple conversion (and flush==true) + */ + return 0; +} + +/* + * continue partial match with new input, requires cnv->preFromUFirstCP>=0 + * never called for simple, single-character conversion + */ +U_CFUNC void +ucnv_extContinueMatchFromU(UConverter *cnv, + UConverterFromUnicodeArgs *pArgs, int32_t srcIndex, + UErrorCode *pErrorCode) { + uint32_t value = 0; /* initialize output-only param to 0 to silence gcc */ + int32_t match; + + match=ucnv_extMatchFromU(cnv->sharedData->mbcs.extIndexes, + cnv->preFromUFirstCP, + cnv->preFromU, cnv->preFromULength, + pArgs->source, (int32_t)(pArgs->sourceLimit-pArgs->source), + &value, + cnv->useFallback, pArgs->flush); + if(match>=2) { + match-=2; /* remove 2 for the initial code point */ + + if(match>=cnv->preFromULength) { + /* advance src pointer for the consumed input */ + pArgs->source+=match-cnv->preFromULength; + cnv->preFromULength=0; + } else { + /* the match did not use all of preFromU[] - keep the rest for replay */ + int32_t length=cnv->preFromULength-match; + u_memmove(cnv->preFromU, cnv->preFromU+match, length); + cnv->preFromULength=(int8_t)-length; + } + + /* finish the partial match */ + cnv->preFromUFirstCP=U_SENTINEL; + + /* write result */ + ucnv_extWriteFromU(cnv, cnv->sharedData->mbcs.extIndexes, + value, + &pArgs->target, pArgs->targetLimit, + &pArgs->offsets, srcIndex, + pErrorCode); + } else if(match<0) { + /* save state for partial match */ + const char16_t *s; + int32_t j; + + /* just _append_ the newly consumed input to preFromU[] */ + s=pArgs->source; + match=-match-2; /* remove 2 for the initial code point */ + for(j=cnv->preFromULength; j=0); + cnv->preFromU[j]=*s++; + } + pArgs->source=s; /* same as *src=srcLimit; because we reached the end of input */ + cnv->preFromULength=(int8_t)match; + } else /* match==0 or 1 */ { + /* + * no match + * + * We need to split the previous input into two parts: + * + * 1. The first code point is unmappable - that's how we got into + * trying the extension data in the first place. + * We need to move it from the preFromU buffer + * to the error buffer, set an error code, + * and prepare the rest of the previous input for 2. + * + * 2. The rest of the previous input must be converted once we + * come back from the callback for the first code point. + * At that time, we have to try again from scratch to convert + * these input characters. + * The replay will be handled by the ucnv.c conversion code. + */ + + if(match==1) { + /* matched, no mapping but request for */ + cnv->useSubChar1=true; + } + + /* move the first code point to the error field */ + cnv->fromUChar32=cnv->preFromUFirstCP; + cnv->preFromUFirstCP=U_SENTINEL; + + /* mark preFromU for replay */ + cnv->preFromULength=-cnv->preFromULength; + + /* set the error code for unassigned */ + *pErrorCode=U_INVALID_CHAR_FOUND; + } +} + +static UBool +extSetUseMapping(UConverterUnicodeSet which, int32_t minLength, uint32_t value) { + if(which==UCNV_ROUNDTRIP_SET) { + // Add only code points for which the roundtrip flag is set. + // Do not add any fallbacks, even if ucnv_fromUnicode() would use them + // (fallbacks from PUA). See the API docs for ucnv_getUnicodeSet(). + // + // By analogy, also do not add "good one-way" mappings. + // + // Do not add entries with reserved bits set. + if(((value&(UCNV_EXT_FROM_U_ROUNDTRIP_FLAG|UCNV_EXT_FROM_U_RESERVED_MASK))!= + UCNV_EXT_FROM_U_ROUNDTRIP_FLAG)) { + return false; + } + } else /* UCNV_ROUNDTRIP_AND_FALLBACK_SET */ { + // Do not add entries with reserved bits set. + if((value&UCNV_EXT_FROM_U_RESERVED_MASK)!=0) { + return false; + } + } + // Do not add entries or other (future?) pseudo-entries + // with an output length of 0. + return UCNV_EXT_FROM_U_GET_LENGTH(value)>=minLength; +} + +static void +ucnv_extGetUnicodeSetString(const UConverterSharedData *sharedData, + const int32_t *cx, + const USetAdder *sa, + UConverterUnicodeSet which, + int32_t minLength, + UChar32 firstCP, + char16_t s[UCNV_EXT_MAX_UCHARS], int32_t length, + int32_t sectionIndex, + UErrorCode *pErrorCode) { + const char16_t *fromUSectionUChars; + const uint32_t *fromUSectionValues; + + uint32_t value; + int32_t i, count; + + fromUSectionUChars=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_UCHARS_INDEX, char16_t)+sectionIndex; + fromUSectionValues=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_VALUES_INDEX, uint32_t)+sectionIndex; + + /* read first pair of the section */ + count=*fromUSectionUChars++; + value=*fromUSectionValues++; + + if(extSetUseMapping(which, minLength, value)) { + if(length==U16_LENGTH(firstCP)) { + /* add the initial code point */ + sa->add(sa->set, firstCP); + } else { + /* add the string so far */ + sa->addString(sa->set, s, length); + } + } + + for(i=0; iaddString(sa->set, s, length+1); + } + } +} + +U_CFUNC void +ucnv_extGetUnicodeSet(const UConverterSharedData *sharedData, + const USetAdder *sa, + UConverterUnicodeSet which, + UConverterSetFilter filter, + UErrorCode *pErrorCode) { + const int32_t *cx; + const uint16_t *stage12, *stage3, *ps2, *ps3; + const uint32_t *stage3b; + + uint32_t value; + int32_t st1, stage1Length, st2, st3, minLength; + + char16_t s[UCNV_EXT_MAX_UCHARS]; + UChar32 c; + int32_t length; + + cx=sharedData->mbcs.extIndexes; + if(cx==nullptr) { + return; + } + + stage12=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_STAGE_12_INDEX, uint16_t); + stage3=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_STAGE_3_INDEX, uint16_t); + stage3b=UCNV_EXT_ARRAY(cx, UCNV_EXT_FROM_U_STAGE_3B_INDEX, uint32_t); + + stage1Length=cx[UCNV_EXT_FROM_U_STAGE_1_LENGTH]; + + /* enumerate the from-Unicode trie table */ + c=0; /* keep track of the current code point while enumerating */ + + if(filter==UCNV_SET_FILTER_2022_CN) { + minLength=3; + } else if( sharedData->mbcs.outputType==MBCS_OUTPUT_DBCS_ONLY || + filter!=UCNV_SET_FILTER_NONE + ) { + /* DBCS-only, ignore single-byte results */ + minLength=2; + } else { + minLength=1; + } + + /* + * the trie enumeration is almost the same as + * in MBCSGetUnicodeSet() for MBCS_OUTPUT_1 + */ + for(st1=0; st1stage1Length) { + ps2=stage12+st2; + for(st2=0; st2<64; ++st2) { + if((st3=(int32_t)ps2[st2]<=0x8140 && value<=0xeffc)) { + continue; + } + break; + case UCNV_SET_FILTER_GR94DBCS: + if(!(UCNV_EXT_FROM_U_GET_LENGTH(value)==2 && + (uint16_t)((value=UCNV_EXT_FROM_U_GET_DATA(value))-0xa1a1)<=(0xfefe - 0xa1a1) && + (uint8_t)(value-0xa1)<=(0xfe - 0xa1))) { + continue; + } + break; + case UCNV_SET_FILTER_HZ: + if(!(UCNV_EXT_FROM_U_GET_LENGTH(value)==2 && + (uint16_t)((value=UCNV_EXT_FROM_U_GET_DATA(value))-0xa1a1)<=(0xfdfe - 0xa1a1) && + (uint8_t)(value-0xa1)<=(0xfe - 0xa1))) { + continue; + } + break; + default: + /* + * UCNV_SET_FILTER_NONE, + * or UCNV_SET_FILTER_DBCS_ONLY which is handled via minLength + */ + break; + } + sa->add(sa->set, c); + } + } while((++c&0xf)!=0); + } else { + c+=16; /* empty stage 3 block */ + } + } + } else { + c+=1024; /* empty stage 2 block */ + } + } +} + +#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ diff --git a/deps/icu-small/source/common/ucnv_ext.h b/deps/icu-small/source/common/ucnv_ext.h index dceea7ef12617a..2557396615d77b 100644 --- a/deps/icu-small/source/common/ucnv_ext.h +++ b/deps/icu-small/source/common/ucnv_ext.h @@ -1,481 +1,481 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2003-2013, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: ucnv_ext.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2003jun13 -* created by: Markus W. Scherer -* -* Conversion extensions -*/ - -#ifndef __UCNV_EXT_H__ -#define __UCNV_EXT_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ucnv.h" -#include "ucnv_cnv.h" - -/* - * See icuhtml/design/conversion/conversion_extensions.html - * - * Conversion extensions serve three purposes: - * 1. They support m:n mappings. - * 2. They support extension-only conversion files that are used together - * with the regular conversion data in base files. - * 3. They support mappings with more complicated meta data, - * for example "good one-way" mappings (|4). - * - * A base file may contain an extension table (explicitly requested or - * implicitly generated for m:n mappings), but its extension table is not - * used when an extension-only file is used. - * - * It is an error if a base file contains any regular (not extension) mapping - * from the same sequence as a mapping in the extension file - * because the base mapping would hide the extension mapping. - * - * - * Data for conversion extensions: - * - * One set of data structures per conversion direction (to/from Unicode). - * The data structures are sorted by input units to allow for binary search. - * Input sequences of more than one unit are handled like contraction tables - * in collation: - * The lookup value of a unit points to another table that is to be searched - * for the next unit, recursively. - * - * For conversion from Unicode, the initial code point is looked up in - * a 3-stage trie for speed, - * with an additional table of unique results to save space. - * - * Long output strings are stored in separate arrays, with length and index - * in the lookup tables. - * Output results also include a flag distinguishing roundtrip from - * (reverse) fallback mappings. - * - * Input Unicode strings must not begin or end with unpaired surrogates - * to avoid problems with matches on parts of surrogate pairs. - * - * Mappings from multiple characters (code points or codepage state - * table sequences) must be searched preferring the longest match. - * For this to work and be efficient, the variable-width table must contain - * all mappings that contain prefixes of the multiple characters. - * If an extension table is built on top of a base table in another file - * and a base table entry is a prefix of a multi-character mapping, then - * this is an error. - * - * - * Implementation note: - * - * Currently, the parser and several checks in the code limit the number - * of UChars or bytes in a mapping to - * UCNV_EXT_MAX_UCHARS and UCNV_EXT_MAX_BYTES, respectively, - * which are output value limits in the data structure. - * - * For input, this is not strictly necessary - it is a hard limit only for the - * buffers in UConverter that are used to store partial matches. - * - * Input sequences could otherwise be arbitrarily long if partial matches - * need not be stored (i.e., if a sequence does not span several buffers with too - * many units before the last buffer), although then results would differ - * depending on whether partial matches exceed the limits or not, - * which depends on the pattern of buffer sizes. - * - * - * Data structure: - * - * int32_t indexes[>=32]; - * - * Array of indexes and lengths etc. The length of the array is at least 32. - * The actual length is stored in indexes[0] to be forward compatible. - * - * Each index to another array is the number of bytes from indexes[]. - * Each length of an array is the number of array base units in that array. - * - * Some of the structures may not be present, in which case their indexes - * and lengths are 0. - * - * Usage of indexes[i]: - * [0] length of indexes[] - * - * // to Unicode table - * [1] index of toUTable[] (array of uint32_t) - * [2] length of toUTable[] - * [3] index of toUUChars[] (array of UChar) - * [4] length of toUUChars[] - * - * // from Unicode table, not for the initial code point - * [5] index of fromUTableUChars[] (array of UChar) - * [6] index of fromUTableValues[] (array of uint32_t) - * [7] length of fromUTableUChars[] and fromUTableValues[] - * [8] index of fromUBytes[] (array of char) - * [9] length of fromUBytes[] - * - * // from Unicode trie for initial-code point lookup - * [10] index of fromUStage12[] (combined array of uint16_t for stages 1 & 2) - * [11] length of stage 1 portion of fromUStage12[] - * [12] length of fromUStage12[] - * [13] index of fromUStage3[] (array of uint16_t indexes into fromUStage3b[]) - * [14] length of fromUStage3[] - * [15] index of fromUStage3b[] (array of uint32_t like fromUTableValues[]) - * [16] length of fromUStage3b[] - * - * [17] Bit field containing numbers of bytes: - * 31..24 reserved, 0 - * 23..16 maximum input bytes - * 15.. 8 maximum output bytes - * 7.. 0 maximum bytes per UChar - * - * [18] Bit field containing numbers of UChars: - * 31..24 reserved, 0 - * 23..16 maximum input UChars - * 15.. 8 maximum output UChars - * 7.. 0 maximum UChars per byte - * - * [19] Bit field containing flags: - * (extension table unicodeMask) - * 1 UCNV_HAS_SURROGATES flag for the extension table - * 0 UCNV_HAS_SUPPLEMENTARY flag for the extension table - * - * [20]..[30] reserved, 0 - * [31] number of bytes for the entire extension structure - * [>31] reserved; there are indexes[0] indexes - * - * - * uint32_t toUTable[]; - * - * Array of byte/value pairs for lookups for toUnicode conversion. - * The array is partitioned into sections like collation contraction tables. - * Each section contains one word with the number of following words and - * a default value for when the lookup in this section yields no match. - * - * A section is sorted in ascending order of input bytes, - * allowing for fast linear or binary searches. - * The builder may store entries for a contiguous range of byte values - * (compare difference between the first and last one with count), - * which then allows for direct array access. - * The builder should always do this for the initial table section. - * - * Entries may have 0 values, see below. - * No two entries in a section have the same byte values. - * - * Each uint32_t contains an input byte value in bits 31..24 and the - * corresponding lookup value in bits 23..0. - * Interpret the value as follows: - * if(value==0) { - * no match, see below - * } else if(value<0x1f0000) { - * partial match - use value as index to the next toUTable section - * and match the next unit; (value indexes toUTable[value]) - * } else { - * if(bit 23 set) { - * roundtrip; - * } else { - * fallback; - * } - * unset value bit 23; - * if(value<=0x2fffff) { - * (value-0x1f0000) is a code point; (BMP: value<=0x1fffff) - * } else { - * bits 17..0 (value&0x3ffff) is an index to - * the result UChars in toUUChars[]; (0 indexes toUUChars[0]) - * length of the result=((value>>18)-12); (length=0..19) - * } - * } - * - * The first word in a section contains the number of following words in the - * input byte position (bits 31..24, number=1..0xff). - * The value of the initial word is used when the current byte is not found - * in this section. - * If the value is not 0, then it represents a result as above. - * If the value is 0, then the search has to return a shorter match with an - * earlier default value as the result, or result in "unmappable" even for the - * initial bytes. - * If the value is 0 for the initial toUTable entry, then the initial byte - * does not start any mapping input. - * - * - * UChar toUUChars[]; - * - * Contains toUnicode mapping results, stored as sequences of UChars. - * Indexes and lengths stored in the toUTable[]. - * - * - * UChar fromUTableUChars[]; - * uint32_t fromUTableValues[]; - * - * The fromUTable is split into two arrays, but works otherwise much like - * the toUTable. The array is partitioned into sections like collation - * contraction tables and toUTable. - * A row in the table consists of same-index entries in fromUTableUChars[] - * and fromUTableValues[]. - * - * Interpret a value as follows: - * if(value==0) { - * no match, see below - * } else if(value<=0xffffff) { (bits 31..24 are 0) - * partial match - use value as index to the next fromUTable section - * and match the next unit; (value indexes fromUTable[value]) - * } else { - * if(value==0x80000001) { - * return no mapping, but request for ; - * } - * if(bit 31 set) { - * roundtrip (|0); - * } else if(bit 30 set) { - * "good one-way" mapping (|4); -- new in ICU4C 51, _MBCSHeader.version 5.4/4.4 - * } else { - * normal fallback (|1); - * } - * // bit 29 reserved, 0 - * length=(value>>24)&0x1f; (bits 28..24) - * if(length==1..3) { - * bits 23..0 contain 1..3 bytes, padded with 00s on the left; - * } else { - * bits 23..0 (value&0xffffff) is an index to - * the result bytes in fromUBytes[]; (0 indexes fromUBytes[0]) - * } - * } - * - * The first pair in a section contains the number of following pairs in the - * UChar position (16 bits, number=1..0xffff). - * The value of the initial pair is used when the current UChar is not found - * in this section. - * If the value is not 0, then it represents a result as above. - * If the value is 0, then the search has to return a shorter match with an - * earlier default value as the result, or result in "unmappable" even for the - * initial UChars. - * - * If the from Unicode trie is present, then the from Unicode search tables - * are not used for initial code points. - * In this case, the first entries (index 0) in the tables are not used - * (reserved, set to 0) because a value of 0 is used in trie results - * to indicate no mapping. - * - * - * uint16_t fromUStage12[]; - * - * Stages 1 & 2 of a trie that maps an initial code point. - * Indexes in stage 1 are all offset by the length of stage 1 so that the - * same array pointer can be used for both stages. - * If (c>>10)>=(length of stage 1) then c does not start any mapping. - * Same bit distribution as for regular conversion tries. - * - * - * uint16_t fromUStage3[]; - * uint32_t fromUStage3b[]; - * - * Stage 3 of the trie. The first array simply contains indexes to the second, - * which contains words in the same format as fromUTableValues[]. - * Use a stage 3 granularity of 4, which allows for 256k stage 3 entries, - * and 16-bit entries in stage 3 allow for 64k stage 3b entries. - * The stage 3 granularity means that the stage 2 entry needs to be left-shifted. - * - * Two arrays are used because it is expected that more than half of the stage 3 - * entries will be zero. The 16-bit index stage 3 array saves space even - * considering storing a total of 6 bytes per non-zero entry in both arrays - * together. - * Using a stage 3 granularity of >1 diminishes the compactability in that stage - * but provides a larger effective addressing space in stage 2. - * All but the final result stage use 16-bit entries to save space. - * - * fromUStage3b[] contains a zero for "no mapping" at its index 0, - * and may contain UCNV_EXT_FROM_U_SUBCHAR1 at index 1 for " SUB mapping" - * (i.e., "no mapping" with preference for rather than ), - * and all other items are unique non-zero results. - * - * The default value of a fromUTableValues[] section that is referenced - * _directly_ from a fromUStage3b[] item may also be UCNV_EXT_FROM_U_SUBCHAR1, - * but this value must not occur anywhere else in fromUTableValues[] - * because "no mapping" is always a property of a single code point, - * never of multiple. - * - * - * char fromUBytes[]; - * - * Contains fromUnicode mapping results, stored as sequences of chars. - * Indexes and lengths stored in the fromUTableValues[]. - */ -enum { - UCNV_EXT_INDEXES_LENGTH, /* 0 */ - - UCNV_EXT_TO_U_INDEX, /* 1 */ - UCNV_EXT_TO_U_LENGTH, - UCNV_EXT_TO_U_UCHARS_INDEX, - UCNV_EXT_TO_U_UCHARS_LENGTH, - - UCNV_EXT_FROM_U_UCHARS_INDEX, /* 5 */ - UCNV_EXT_FROM_U_VALUES_INDEX, - UCNV_EXT_FROM_U_LENGTH, - UCNV_EXT_FROM_U_BYTES_INDEX, - UCNV_EXT_FROM_U_BYTES_LENGTH, - - UCNV_EXT_FROM_U_STAGE_12_INDEX, /* 10 */ - UCNV_EXT_FROM_U_STAGE_1_LENGTH, - UCNV_EXT_FROM_U_STAGE_12_LENGTH, - UCNV_EXT_FROM_U_STAGE_3_INDEX, - UCNV_EXT_FROM_U_STAGE_3_LENGTH, - UCNV_EXT_FROM_U_STAGE_3B_INDEX, - UCNV_EXT_FROM_U_STAGE_3B_LENGTH, - - UCNV_EXT_COUNT_BYTES, /* 17 */ - UCNV_EXT_COUNT_UCHARS, - UCNV_EXT_FLAGS, - - UCNV_EXT_RESERVED_INDEX, /* 20, moves with additional indexes */ - - UCNV_EXT_SIZE=31, - UCNV_EXT_INDEXES_MIN_LENGTH=32 -}; - -/* get the pointer to an extension array from indexes[index] */ -#define UCNV_EXT_ARRAY(indexes, index, itemType) \ - ((const itemType *)((const char *)(indexes)+(indexes)[index])) - -#define UCNV_GET_MAX_BYTES_PER_UCHAR(indexes) \ - ((indexes)[UCNV_EXT_COUNT_BYTES]&0xff) - -/* internal API ------------------------------------------------------------- */ - -U_CFUNC UBool -ucnv_extInitialMatchToU(UConverter *cnv, const int32_t *cx, - int32_t firstLength, - const char **src, const char *srcLimit, - UChar **target, const UChar *targetLimit, - int32_t **offsets, int32_t srcIndex, - UBool flush, - UErrorCode *pErrorCode); - -U_CFUNC UChar32 -ucnv_extSimpleMatchToU(const int32_t *cx, - const char *source, int32_t length, - UBool useFallback); - -U_CFUNC void -ucnv_extContinueMatchToU(UConverter *cnv, - UConverterToUnicodeArgs *pArgs, int32_t srcIndex, - UErrorCode *pErrorCode); - - -U_CFUNC UBool -ucnv_extInitialMatchFromU(UConverter *cnv, const int32_t *cx, - UChar32 cp, - const UChar **src, const UChar *srcLimit, - char **target, const char *targetLimit, - int32_t **offsets, int32_t srcIndex, - UBool flush, - UErrorCode *pErrorCode); - -U_CFUNC int32_t -ucnv_extSimpleMatchFromU(const int32_t *cx, - UChar32 cp, uint32_t *pValue, - UBool useFallback); - -U_CFUNC void -ucnv_extContinueMatchFromU(UConverter *cnv, - UConverterFromUnicodeArgs *pArgs, int32_t srcIndex, - UErrorCode *pErrorCode); - -/* - * Add code points and strings to the set according to the extension mappings. - * Limitation on the UConverterSetFilter: - * The filters currently assume that they are used with 1:1 mappings. - * They only apply to single input code points, and then they pass through - * only mappings with single-charset-code results. - * For example, the Shift-JIS filter only works for 2-byte results and tests - * that those 2 bytes are in the JIS X 0208 range of Shift-JIS. - */ -U_CFUNC void -ucnv_extGetUnicodeSet(const UConverterSharedData *sharedData, - const USetAdder *sa, - UConverterUnicodeSet which, - UConverterSetFilter filter, - UErrorCode *pErrorCode); - -/* toUnicode helpers -------------------------------------------------------- */ - -#define UCNV_EXT_TO_U_BYTE_SHIFT 24 -#define UCNV_EXT_TO_U_VALUE_MASK 0xffffff -#define UCNV_EXT_TO_U_MIN_CODE_POINT 0x1f0000 -#define UCNV_EXT_TO_U_MAX_CODE_POINT 0x2fffff -#define UCNV_EXT_TO_U_ROUNDTRIP_FLAG ((uint32_t)1<<23) -#define UCNV_EXT_TO_U_INDEX_MASK 0x3ffff -#define UCNV_EXT_TO_U_LENGTH_SHIFT 18 -#define UCNV_EXT_TO_U_LENGTH_OFFSET 12 - -/* maximum number of indexed UChars */ -#define UCNV_EXT_MAX_UCHARS 19 - -#define UCNV_EXT_TO_U_MAKE_WORD(byte, value) (((uint32_t)(byte)<>UCNV_EXT_TO_U_BYTE_SHIFT) -#define UCNV_EXT_TO_U_GET_VALUE(word) ((word)&UCNV_EXT_TO_U_VALUE_MASK) - -#define UCNV_EXT_TO_U_IS_PARTIAL(value) ((value)>UCNV_EXT_TO_U_LENGTH_SHIFT)-UCNV_EXT_TO_U_LENGTH_OFFSET) - -/* fromUnicode helpers ------------------------------------------------------ */ - -/* most trie constants are shared with ucnvmbcs.h */ - -/* see similar utrie.h UTRIE_INDEX_SHIFT and UTRIE_DATA_GRANULARITY */ -#define UCNV_EXT_STAGE_2_LEFT_SHIFT 2 -#define UCNV_EXT_STAGE_3_GRANULARITY 4 - -/* trie access, returns the stage 3 value=index to stage 3b; s1Index=c>>10 */ -#define UCNV_EXT_FROM_U(stage12, stage3, s1Index, c) \ - (stage3)[ ((int32_t)(stage12)[ (stage12)[s1Index] +(((c)>>4)&0x3f) ]< (impossible roundtrip to 0 bytes, value 01) */ -#define UCNV_EXT_FROM_U_SUBCHAR1 0x80000001 - -/* at most 3 bytes in the lower part of the value */ -#define UCNV_EXT_FROM_U_MAX_DIRECT_LENGTH 3 - -/* maximum number of indexed bytes */ -#define UCNV_EXT_MAX_BYTES 0x1f - -#define UCNV_EXT_FROM_U_IS_PARTIAL(value) (((value)>>UCNV_EXT_FROM_U_LENGTH_SHIFT)==0) -#define UCNV_EXT_FROM_U_GET_PARTIAL_INDEX(value) (value) - -#define UCNV_EXT_FROM_U_IS_ROUNDTRIP(value) (((value)&UCNV_EXT_FROM_U_ROUNDTRIP_FLAG)!=0) -#define UCNV_EXT_FROM_U_MASK_ROUNDTRIP(value) ((value)&~UCNV_EXT_FROM_U_ROUNDTRIP_FLAG) - -/* get length; masks away all other bits */ -#define UCNV_EXT_FROM_U_GET_LENGTH(value) (int32_t)(((value)>>UCNV_EXT_FROM_U_LENGTH_SHIFT)&UCNV_EXT_MAX_BYTES) - -/* get bytes or bytes index */ -#define UCNV_EXT_FROM_U_GET_DATA(value) ((value)&UCNV_EXT_FROM_U_DATA_MASK) - -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2003-2013, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ucnv_ext.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2003jun13 +* created by: Markus W. Scherer +* +* Conversion extensions +*/ + +#ifndef __UCNV_EXT_H__ +#define __UCNV_EXT_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ucnv.h" +#include "ucnv_cnv.h" + +/* + * See icuhtml/design/conversion/conversion_extensions.html + * + * Conversion extensions serve three purposes: + * 1. They support m:n mappings. + * 2. They support extension-only conversion files that are used together + * with the regular conversion data in base files. + * 3. They support mappings with more complicated meta data, + * for example "good one-way" mappings (|4). + * + * A base file may contain an extension table (explicitly requested or + * implicitly generated for m:n mappings), but its extension table is not + * used when an extension-only file is used. + * + * It is an error if a base file contains any regular (not extension) mapping + * from the same sequence as a mapping in the extension file + * because the base mapping would hide the extension mapping. + * + * + * Data for conversion extensions: + * + * One set of data structures per conversion direction (to/from Unicode). + * The data structures are sorted by input units to allow for binary search. + * Input sequences of more than one unit are handled like contraction tables + * in collation: + * The lookup value of a unit points to another table that is to be searched + * for the next unit, recursively. + * + * For conversion from Unicode, the initial code point is looked up in + * a 3-stage trie for speed, + * with an additional table of unique results to save space. + * + * Long output strings are stored in separate arrays, with length and index + * in the lookup tables. + * Output results also include a flag distinguishing roundtrip from + * (reverse) fallback mappings. + * + * Input Unicode strings must not begin or end with unpaired surrogates + * to avoid problems with matches on parts of surrogate pairs. + * + * Mappings from multiple characters (code points or codepage state + * table sequences) must be searched preferring the longest match. + * For this to work and be efficient, the variable-width table must contain + * all mappings that contain prefixes of the multiple characters. + * If an extension table is built on top of a base table in another file + * and a base table entry is a prefix of a multi-character mapping, then + * this is an error. + * + * + * Implementation note: + * + * Currently, the parser and several checks in the code limit the number + * of UChars or bytes in a mapping to + * UCNV_EXT_MAX_UCHARS and UCNV_EXT_MAX_BYTES, respectively, + * which are output value limits in the data structure. + * + * For input, this is not strictly necessary - it is a hard limit only for the + * buffers in UConverter that are used to store partial matches. + * + * Input sequences could otherwise be arbitrarily long if partial matches + * need not be stored (i.e., if a sequence does not span several buffers with too + * many units before the last buffer), although then results would differ + * depending on whether partial matches exceed the limits or not, + * which depends on the pattern of buffer sizes. + * + * + * Data structure: + * + * int32_t indexes[>=32]; + * + * Array of indexes and lengths etc. The length of the array is at least 32. + * The actual length is stored in indexes[0] to be forward compatible. + * + * Each index to another array is the number of bytes from indexes[]. + * Each length of an array is the number of array base units in that array. + * + * Some of the structures may not be present, in which case their indexes + * and lengths are 0. + * + * Usage of indexes[i]: + * [0] length of indexes[] + * + * // to Unicode table + * [1] index of toUTable[] (array of uint32_t) + * [2] length of toUTable[] + * [3] index of toUUChars[] (array of UChar) + * [4] length of toUUChars[] + * + * // from Unicode table, not for the initial code point + * [5] index of fromUTableUChars[] (array of UChar) + * [6] index of fromUTableValues[] (array of uint32_t) + * [7] length of fromUTableUChars[] and fromUTableValues[] + * [8] index of fromUBytes[] (array of char) + * [9] length of fromUBytes[] + * + * // from Unicode trie for initial-code point lookup + * [10] index of fromUStage12[] (combined array of uint16_t for stages 1 & 2) + * [11] length of stage 1 portion of fromUStage12[] + * [12] length of fromUStage12[] + * [13] index of fromUStage3[] (array of uint16_t indexes into fromUStage3b[]) + * [14] length of fromUStage3[] + * [15] index of fromUStage3b[] (array of uint32_t like fromUTableValues[]) + * [16] length of fromUStage3b[] + * + * [17] Bit field containing numbers of bytes: + * 31..24 reserved, 0 + * 23..16 maximum input bytes + * 15.. 8 maximum output bytes + * 7.. 0 maximum bytes per UChar + * + * [18] Bit field containing numbers of UChars: + * 31..24 reserved, 0 + * 23..16 maximum input UChars + * 15.. 8 maximum output UChars + * 7.. 0 maximum UChars per byte + * + * [19] Bit field containing flags: + * (extension table unicodeMask) + * 1 UCNV_HAS_SURROGATES flag for the extension table + * 0 UCNV_HAS_SUPPLEMENTARY flag for the extension table + * + * [20]..[30] reserved, 0 + * [31] number of bytes for the entire extension structure + * [>31] reserved; there are indexes[0] indexes + * + * + * uint32_t toUTable[]; + * + * Array of byte/value pairs for lookups for toUnicode conversion. + * The array is partitioned into sections like collation contraction tables. + * Each section contains one word with the number of following words and + * a default value for when the lookup in this section yields no match. + * + * A section is sorted in ascending order of input bytes, + * allowing for fast linear or binary searches. + * The builder may store entries for a contiguous range of byte values + * (compare difference between the first and last one with count), + * which then allows for direct array access. + * The builder should always do this for the initial table section. + * + * Entries may have 0 values, see below. + * No two entries in a section have the same byte values. + * + * Each uint32_t contains an input byte value in bits 31..24 and the + * corresponding lookup value in bits 23..0. + * Interpret the value as follows: + * if(value==0) { + * no match, see below + * } else if(value<0x1f0000) { + * partial match - use value as index to the next toUTable section + * and match the next unit; (value indexes toUTable[value]) + * } else { + * if(bit 23 set) { + * roundtrip; + * } else { + * fallback; + * } + * unset value bit 23; + * if(value<=0x2fffff) { + * (value-0x1f0000) is a code point; (BMP: value<=0x1fffff) + * } else { + * bits 17..0 (value&0x3ffff) is an index to + * the result UChars in toUUChars[]; (0 indexes toUUChars[0]) + * length of the result=((value>>18)-12); (length=0..19) + * } + * } + * + * The first word in a section contains the number of following words in the + * input byte position (bits 31..24, number=1..0xff). + * The value of the initial word is used when the current byte is not found + * in this section. + * If the value is not 0, then it represents a result as above. + * If the value is 0, then the search has to return a shorter match with an + * earlier default value as the result, or result in "unmappable" even for the + * initial bytes. + * If the value is 0 for the initial toUTable entry, then the initial byte + * does not start any mapping input. + * + * + * UChar toUUChars[]; + * + * Contains toUnicode mapping results, stored as sequences of UChars. + * Indexes and lengths stored in the toUTable[]. + * + * + * UChar fromUTableUChars[]; + * uint32_t fromUTableValues[]; + * + * The fromUTable is split into two arrays, but works otherwise much like + * the toUTable. The array is partitioned into sections like collation + * contraction tables and toUTable. + * A row in the table consists of same-index entries in fromUTableUChars[] + * and fromUTableValues[]. + * + * Interpret a value as follows: + * if(value==0) { + * no match, see below + * } else if(value<=0xffffff) { (bits 31..24 are 0) + * partial match - use value as index to the next fromUTable section + * and match the next unit; (value indexes fromUTable[value]) + * } else { + * if(value==0x80000001) { + * return no mapping, but request for ; + * } + * if(bit 31 set) { + * roundtrip (|0); + * } else if(bit 30 set) { + * "good one-way" mapping (|4); -- new in ICU4C 51, _MBCSHeader.version 5.4/4.4 + * } else { + * normal fallback (|1); + * } + * // bit 29 reserved, 0 + * length=(value>>24)&0x1f; (bits 28..24) + * if(length==1..3) { + * bits 23..0 contain 1..3 bytes, padded with 00s on the left; + * } else { + * bits 23..0 (value&0xffffff) is an index to + * the result bytes in fromUBytes[]; (0 indexes fromUBytes[0]) + * } + * } + * + * The first pair in a section contains the number of following pairs in the + * UChar position (16 bits, number=1..0xffff). + * The value of the initial pair is used when the current UChar is not found + * in this section. + * If the value is not 0, then it represents a result as above. + * If the value is 0, then the search has to return a shorter match with an + * earlier default value as the result, or result in "unmappable" even for the + * initial UChars. + * + * If the from Unicode trie is present, then the from Unicode search tables + * are not used for initial code points. + * In this case, the first entries (index 0) in the tables are not used + * (reserved, set to 0) because a value of 0 is used in trie results + * to indicate no mapping. + * + * + * uint16_t fromUStage12[]; + * + * Stages 1 & 2 of a trie that maps an initial code point. + * Indexes in stage 1 are all offset by the length of stage 1 so that the + * same array pointer can be used for both stages. + * If (c>>10)>=(length of stage 1) then c does not start any mapping. + * Same bit distribution as for regular conversion tries. + * + * + * uint16_t fromUStage3[]; + * uint32_t fromUStage3b[]; + * + * Stage 3 of the trie. The first array simply contains indexes to the second, + * which contains words in the same format as fromUTableValues[]. + * Use a stage 3 granularity of 4, which allows for 256k stage 3 entries, + * and 16-bit entries in stage 3 allow for 64k stage 3b entries. + * The stage 3 granularity means that the stage 2 entry needs to be left-shifted. + * + * Two arrays are used because it is expected that more than half of the stage 3 + * entries will be zero. The 16-bit index stage 3 array saves space even + * considering storing a total of 6 bytes per non-zero entry in both arrays + * together. + * Using a stage 3 granularity of >1 diminishes the compactability in that stage + * but provides a larger effective addressing space in stage 2. + * All but the final result stage use 16-bit entries to save space. + * + * fromUStage3b[] contains a zero for "no mapping" at its index 0, + * and may contain UCNV_EXT_FROM_U_SUBCHAR1 at index 1 for " SUB mapping" + * (i.e., "no mapping" with preference for rather than ), + * and all other items are unique non-zero results. + * + * The default value of a fromUTableValues[] section that is referenced + * _directly_ from a fromUStage3b[] item may also be UCNV_EXT_FROM_U_SUBCHAR1, + * but this value must not occur anywhere else in fromUTableValues[] + * because "no mapping" is always a property of a single code point, + * never of multiple. + * + * + * char fromUBytes[]; + * + * Contains fromUnicode mapping results, stored as sequences of chars. + * Indexes and lengths stored in the fromUTableValues[]. + */ +enum { + UCNV_EXT_INDEXES_LENGTH, /* 0 */ + + UCNV_EXT_TO_U_INDEX, /* 1 */ + UCNV_EXT_TO_U_LENGTH, + UCNV_EXT_TO_U_UCHARS_INDEX, + UCNV_EXT_TO_U_UCHARS_LENGTH, + + UCNV_EXT_FROM_U_UCHARS_INDEX, /* 5 */ + UCNV_EXT_FROM_U_VALUES_INDEX, + UCNV_EXT_FROM_U_LENGTH, + UCNV_EXT_FROM_U_BYTES_INDEX, + UCNV_EXT_FROM_U_BYTES_LENGTH, + + UCNV_EXT_FROM_U_STAGE_12_INDEX, /* 10 */ + UCNV_EXT_FROM_U_STAGE_1_LENGTH, + UCNV_EXT_FROM_U_STAGE_12_LENGTH, + UCNV_EXT_FROM_U_STAGE_3_INDEX, + UCNV_EXT_FROM_U_STAGE_3_LENGTH, + UCNV_EXT_FROM_U_STAGE_3B_INDEX, + UCNV_EXT_FROM_U_STAGE_3B_LENGTH, + + UCNV_EXT_COUNT_BYTES, /* 17 */ + UCNV_EXT_COUNT_UCHARS, + UCNV_EXT_FLAGS, + + UCNV_EXT_RESERVED_INDEX, /* 20, moves with additional indexes */ + + UCNV_EXT_SIZE=31, + UCNV_EXT_INDEXES_MIN_LENGTH=32 +}; + +/* get the pointer to an extension array from indexes[index] */ +#define UCNV_EXT_ARRAY(indexes, index, itemType) \ + ((const itemType *)((const char *)(indexes)+(indexes)[index])) + +#define UCNV_GET_MAX_BYTES_PER_UCHAR(indexes) \ + ((indexes)[UCNV_EXT_COUNT_BYTES]&0xff) + +/* internal API ------------------------------------------------------------- */ + +U_CFUNC UBool +ucnv_extInitialMatchToU(UConverter *cnv, const int32_t *cx, + int32_t firstLength, + const char **src, const char *srcLimit, + UChar **target, const UChar *targetLimit, + int32_t **offsets, int32_t srcIndex, + UBool flush, + UErrorCode *pErrorCode); + +U_CFUNC UChar32 +ucnv_extSimpleMatchToU(const int32_t *cx, + const char *source, int32_t length, + UBool useFallback); + +U_CFUNC void +ucnv_extContinueMatchToU(UConverter *cnv, + UConverterToUnicodeArgs *pArgs, int32_t srcIndex, + UErrorCode *pErrorCode); + + +U_CFUNC UBool +ucnv_extInitialMatchFromU(UConverter *cnv, const int32_t *cx, + UChar32 cp, + const UChar **src, const UChar *srcLimit, + char **target, const char *targetLimit, + int32_t **offsets, int32_t srcIndex, + UBool flush, + UErrorCode *pErrorCode); + +U_CFUNC int32_t +ucnv_extSimpleMatchFromU(const int32_t *cx, + UChar32 cp, uint32_t *pValue, + UBool useFallback); + +U_CFUNC void +ucnv_extContinueMatchFromU(UConverter *cnv, + UConverterFromUnicodeArgs *pArgs, int32_t srcIndex, + UErrorCode *pErrorCode); + +/* + * Add code points and strings to the set according to the extension mappings. + * Limitation on the UConverterSetFilter: + * The filters currently assume that they are used with 1:1 mappings. + * They only apply to single input code points, and then they pass through + * only mappings with single-charset-code results. + * For example, the Shift-JIS filter only works for 2-byte results and tests + * that those 2 bytes are in the JIS X 0208 range of Shift-JIS. + */ +U_CFUNC void +ucnv_extGetUnicodeSet(const UConverterSharedData *sharedData, + const USetAdder *sa, + UConverterUnicodeSet which, + UConverterSetFilter filter, + UErrorCode *pErrorCode); + +/* toUnicode helpers -------------------------------------------------------- */ + +#define UCNV_EXT_TO_U_BYTE_SHIFT 24 +#define UCNV_EXT_TO_U_VALUE_MASK 0xffffff +#define UCNV_EXT_TO_U_MIN_CODE_POINT 0x1f0000 +#define UCNV_EXT_TO_U_MAX_CODE_POINT 0x2fffff +#define UCNV_EXT_TO_U_ROUNDTRIP_FLAG ((uint32_t)1<<23) +#define UCNV_EXT_TO_U_INDEX_MASK 0x3ffff +#define UCNV_EXT_TO_U_LENGTH_SHIFT 18 +#define UCNV_EXT_TO_U_LENGTH_OFFSET 12 + +/* maximum number of indexed UChars */ +#define UCNV_EXT_MAX_UCHARS 19 + +#define UCNV_EXT_TO_U_MAKE_WORD(byte, value) (((uint32_t)(byte)<>UCNV_EXT_TO_U_BYTE_SHIFT) +#define UCNV_EXT_TO_U_GET_VALUE(word) ((word)&UCNV_EXT_TO_U_VALUE_MASK) + +#define UCNV_EXT_TO_U_IS_PARTIAL(value) ((value)>UCNV_EXT_TO_U_LENGTH_SHIFT)-UCNV_EXT_TO_U_LENGTH_OFFSET) + +/* fromUnicode helpers ------------------------------------------------------ */ + +/* most trie constants are shared with ucnvmbcs.h */ + +/* see similar utrie.h UTRIE_INDEX_SHIFT and UTRIE_DATA_GRANULARITY */ +#define UCNV_EXT_STAGE_2_LEFT_SHIFT 2 +#define UCNV_EXT_STAGE_3_GRANULARITY 4 + +/* trie access, returns the stage 3 value=index to stage 3b; s1Index=c>>10 */ +#define UCNV_EXT_FROM_U(stage12, stage3, s1Index, c) \ + (stage3)[ ((int32_t)(stage12)[ (stage12)[s1Index] +(((c)>>4)&0x3f) ]< (impossible roundtrip to 0 bytes, value 01) */ +#define UCNV_EXT_FROM_U_SUBCHAR1 0x80000001 + +/* at most 3 bytes in the lower part of the value */ +#define UCNV_EXT_FROM_U_MAX_DIRECT_LENGTH 3 + +/* maximum number of indexed bytes */ +#define UCNV_EXT_MAX_BYTES 0x1f + +#define UCNV_EXT_FROM_U_IS_PARTIAL(value) (((value)>>UCNV_EXT_FROM_U_LENGTH_SHIFT)==0) +#define UCNV_EXT_FROM_U_GET_PARTIAL_INDEX(value) (value) + +#define UCNV_EXT_FROM_U_IS_ROUNDTRIP(value) (((value)&UCNV_EXT_FROM_U_ROUNDTRIP_FLAG)!=0) +#define UCNV_EXT_FROM_U_MASK_ROUNDTRIP(value) ((value)&~UCNV_EXT_FROM_U_ROUNDTRIP_FLAG) + +/* get length; masks away all other bits */ +#define UCNV_EXT_FROM_U_GET_LENGTH(value) (int32_t)(((value)>>UCNV_EXT_FROM_U_LENGTH_SHIFT)&UCNV_EXT_MAX_BYTES) + +/* get bytes or bytes index */ +#define UCNV_EXT_FROM_U_GET_DATA(value) ((value)&UCNV_EXT_FROM_U_DATA_MASK) + +#endif + +#endif diff --git a/deps/icu-small/source/common/ucnv_imp.h b/deps/icu-small/source/common/ucnv_imp.h index cb939911e47e4b..bf50b6f67c1159 100644 --- a/deps/icu-small/source/common/ucnv_imp.h +++ b/deps/icu-small/source/common/ucnv_imp.h @@ -1,139 +1,139 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* -* ucnv_imp.h: -* Contains all internal and external data structure definitions -* Created & Maintained by Bertrand A. Damiba -* -* -* -* ATTENTION: -* --------- -* Although the data structures in this file are open and stack allocatable -* we reserve the right to hide them in further releases. -*/ - -#ifndef UCNV_IMP_H -#define UCNV_IMP_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/uloc.h" -#include "ucnv_bld.h" - -/* - * Fast check for whether a charset name is "UTF-8". - * This does not recognize all of the variations that ucnv_open() - * and other functions recognize, but it covers most cases. - * @param name const char * charset name - * @return - */ -#define UCNV_FAST_IS_UTF8(name) \ - (((name[0]=='U' ? \ - ( name[1]=='T' && name[2]=='F') : \ - (name[0]=='u' && name[1]=='t' && name[2]=='f'))) \ - && (name[3]=='-' ? \ - (name[4]=='8' && name[5]==0) : \ - (name[3]=='8' && name[4]==0))) - -typedef struct { - char cnvName[UCNV_MAX_CONVERTER_NAME_LENGTH]; - char locale[ULOC_FULLNAME_CAPACITY]; - uint32_t options; -} UConverterNamePieces; - -U_CFUNC UBool -ucnv_canCreateConverter(const char *converterName, UErrorCode *err); - -/* figures out if we need to go to file to read in the data tables. - * @param converterName The name of the converter - * @param err The error code - * @return the newly created converter - */ -U_CAPI UConverter * -ucnv_createConverter(UConverter *myUConverter, const char *converterName, UErrorCode * err); - -/* - * Open a purely algorithmic converter, specified by a type constant. - * @param myUConverter NULL, or pre-allocated UConverter structure to avoid - * a memory allocation - * @param type requested converter type - * @param locale locale parameter, or "" - * @param options converter options bit set (default 0) - * @param err ICU error code, not tested for U_FAILURE on input - * because this is an internal function - * @internal - */ -U_CFUNC UConverter * -ucnv_createAlgorithmicConverter(UConverter *myUConverter, - UConverterType type, - const char *locale, uint32_t options, - UErrorCode *err); - -/* - * Creates a converter from shared data. - * Adopts mySharedConverterData: No matter what happens, the caller must not - * unload mySharedConverterData, except via ucnv_close(return value) - * if this function is successful. - */ -U_CFUNC UConverter * -ucnv_createConverterFromSharedData(UConverter *myUConverter, - UConverterSharedData *mySharedConverterData, - UConverterLoadArgs *pArgs, - UErrorCode *err); - -U_CFUNC UConverter * -ucnv_createConverterFromPackage(const char *packageName, const char *converterName, UErrorCode *err); - -/** - * Load a converter but do not create a UConverter object. - * Simply return the UConverterSharedData. - * Performs alias lookup etc. - * The UConverterNamePieces need not be initialized - * before calling this function. - * The UConverterLoadArgs must be initialized - * before calling this function. - * If the args are passed in, then the pieces must be passed in too. - * In other words, the following combinations are allowed: - * - pieces==NULL && args==NULL - * - pieces!=NULL && args==NULL - * - pieces!=NULL && args!=NULL - * @internal - */ -U_CFUNC UConverterSharedData * -ucnv_loadSharedData(const char *converterName, - UConverterNamePieces *pieces, - UConverterLoadArgs *pArgs, - UErrorCode * err); - -/** - * This may unload the shared data in a thread safe manner. - * This will only unload the data if no other converters are sharing it. - */ -U_CFUNC void -ucnv_unloadSharedDataIfReady(UConverterSharedData *sharedData); - -/** - * This is a thread safe way to increment the reference count. - */ -U_CFUNC void -ucnv_incrementRefCount(UConverterSharedData *sharedData); - -/** - * These are the default error handling callbacks for the charset conversion framework. - * For performance reasons, they are only called to handle an error (not normally called for a reset or close). - */ -#define UCNV_TO_U_DEFAULT_CALLBACK ((UConverterToUCallback) UCNV_TO_U_CALLBACK_SUBSTITUTE) -#define UCNV_FROM_U_DEFAULT_CALLBACK ((UConverterFromUCallback) UCNV_FROM_U_CALLBACK_SUBSTITUTE) - -#endif - -#endif /* _UCNV_IMP */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* +* ucnv_imp.h: +* Contains all internal and external data structure definitions +* Created & Maintained by Bertrand A. Damiba +* +* +* +* ATTENTION: +* --------- +* Although the data structures in this file are open and stack allocatable +* we reserve the right to hide them in further releases. +*/ + +#ifndef UCNV_IMP_H +#define UCNV_IMP_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/uloc.h" +#include "ucnv_bld.h" + +/* + * Fast check for whether a charset name is "UTF-8". + * This does not recognize all of the variations that ucnv_open() + * and other functions recognize, but it covers most cases. + * @param name const char * charset name + * @return + */ +#define UCNV_FAST_IS_UTF8(name) \ + (((name[0]=='U' ? \ + ( name[1]=='T' && name[2]=='F') : \ + (name[0]=='u' && name[1]=='t' && name[2]=='f'))) \ + && (name[3]=='-' ? \ + (name[4]=='8' && name[5]==0) : \ + (name[3]=='8' && name[4]==0))) + +typedef struct { + char cnvName[UCNV_MAX_CONVERTER_NAME_LENGTH]; + char locale[ULOC_FULLNAME_CAPACITY]; + uint32_t options; +} UConverterNamePieces; + +U_CFUNC UBool +ucnv_canCreateConverter(const char *converterName, UErrorCode *err); + +/* figures out if we need to go to file to read in the data tables. + * @param converterName The name of the converter + * @param err The error code + * @return the newly created converter + */ +U_CAPI UConverter * +ucnv_createConverter(UConverter *myUConverter, const char *converterName, UErrorCode * err); + +/* + * Open a purely algorithmic converter, specified by a type constant. + * @param myUConverter NULL, or pre-allocated UConverter structure to avoid + * a memory allocation + * @param type requested converter type + * @param locale locale parameter, or "" + * @param options converter options bit set (default 0) + * @param err ICU error code, not tested for U_FAILURE on input + * because this is an internal function + * @internal + */ +U_CFUNC UConverter * +ucnv_createAlgorithmicConverter(UConverter *myUConverter, + UConverterType type, + const char *locale, uint32_t options, + UErrorCode *err); + +/* + * Creates a converter from shared data. + * Adopts mySharedConverterData: No matter what happens, the caller must not + * unload mySharedConverterData, except via ucnv_close(return value) + * if this function is successful. + */ +U_CFUNC UConverter * +ucnv_createConverterFromSharedData(UConverter *myUConverter, + UConverterSharedData *mySharedConverterData, + UConverterLoadArgs *pArgs, + UErrorCode *err); + +U_CFUNC UConverter * +ucnv_createConverterFromPackage(const char *packageName, const char *converterName, UErrorCode *err); + +/** + * Load a converter but do not create a UConverter object. + * Simply return the UConverterSharedData. + * Performs alias lookup etc. + * The UConverterNamePieces need not be initialized + * before calling this function. + * The UConverterLoadArgs must be initialized + * before calling this function. + * If the args are passed in, then the pieces must be passed in too. + * In other words, the following combinations are allowed: + * - pieces==NULL && args==NULL + * - pieces!=NULL && args==NULL + * - pieces!=NULL && args!=NULL + * @internal + */ +U_CFUNC UConverterSharedData * +ucnv_loadSharedData(const char *converterName, + UConverterNamePieces *pieces, + UConverterLoadArgs *pArgs, + UErrorCode * err); + +/** + * This may unload the shared data in a thread safe manner. + * This will only unload the data if no other converters are sharing it. + */ +U_CFUNC void +ucnv_unloadSharedDataIfReady(UConverterSharedData *sharedData); + +/** + * This is a thread safe way to increment the reference count. + */ +U_CFUNC void +ucnv_incrementRefCount(UConverterSharedData *sharedData); + +/** + * These are the default error handling callbacks for the charset conversion framework. + * For performance reasons, they are only called to handle an error (not normally called for a reset or close). + */ +#define UCNV_TO_U_DEFAULT_CALLBACK ((UConverterToUCallback) UCNV_TO_U_CALLBACK_SUBSTITUTE) +#define UCNV_FROM_U_DEFAULT_CALLBACK ((UConverterFromUCallback) UCNV_FROM_U_CALLBACK_SUBSTITUTE) + +#endif + +#endif /* _UCNV_IMP */ diff --git a/deps/icu-small/source/common/ucnv_io.cpp b/deps/icu-small/source/common/ucnv_io.cpp index c9d20cb941ba19..515e503bb4ce00 100644 --- a/deps/icu-small/source/common/ucnv_io.cpp +++ b/deps/icu-small/source/common/ucnv_io.cpp @@ -1,1360 +1,1360 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* -* ucnv_io.cpp: -* initializes global variables and defines functions pertaining to converter -* name resolution aspect of the conversion code. -* -* new implementation: -* -* created on: 1999nov22 -* created by: Markus W. Scherer -* -* Use the binary cnvalias.icu (created from convrtrs.txt) to work -* with aliases for converter names. -* -* Date Name Description -* 11/22/1999 markus Created -* 06/28/2002 grhoten Major overhaul of the converter alias design. -* Now an alias can map to different converters -* depending on the specified standard. -******************************************************************************* -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/udata.h" - -#include "umutex.h" -#include "uarrsort.h" -#include "uassert.h" -#include "udataswp.h" -#include "cstring.h" -#include "cmemory.h" -#include "ucnv_io.h" -#include "uenumimp.h" -#include "ucln_cmn.h" - -/* Format of cnvalias.icu ----------------------------------------------------- - * - * cnvalias.icu is a binary, memory-mappable form of convrtrs.txt. - * This binary form contains several tables. All indexes are to uint16_t - * units, and not to the bytes (uint8_t units). Addressing everything on - * 16-bit boundaries allows us to store more information with small index - * numbers, which are also 16-bit in size. The majority of the table (except - * the string table) are 16-bit numbers. - * - * First there is the size of the Table of Contents (TOC). The TOC - * entries contain the size of each section. In order to find the offset - * you just need to sum up the previous offsets. - * The TOC length and entries are an array of uint32_t values. - * The first section after the TOC starts immediately after the TOC. - * - * 1) This section contains a list of converters. This list contains indexes - * into the string table for the converter name. The index of this list is - * also used by other sections, which are mentioned later on. - * This list is not sorted. - * - * 2) This section contains a list of tags. This list contains indexes - * into the string table for the tag name. The index of this list is - * also used by other sections, which are mentioned later on. - * This list is in priority order of standards. - * - * 3) This section contains a list of sorted unique aliases. This - * list contains indexes into the string table for the alias name. The - * index of this list is also used by other sections, like the 4th section. - * The index for the 3rd and 4th section is used to get the - * alias -> converter name mapping. Section 3 and 4 form a two column table. - * Some of the most significant bits of each index may contain other - * information (see findConverter for details). - * - * 4) This section contains a list of mapped converter names. Consider this - * as a table that maps the 3rd section to the 1st section. This list contains - * indexes into the 1st section. The index of this list is the same index in - * the 3rd section. There is also some extra information in the high bits of - * each converter index in this table. Currently it's only used to say that - * an alias mapped to this converter is ambiguous. See UCNV_CONVERTER_INDEX_MASK - * and UCNV_AMBIGUOUS_ALIAS_MAP_BIT for more information. This section is - * the predigested form of the 5th section so that an alias lookup can be fast. - * - * 5) This section contains a 2D array with indexes to the 6th section. This - * section is the full form of all alias mappings. The column index is the - * index into the converter list (column header). The row index is the index - * to tag list (row header). This 2D array is the top part a 3D array. The - * third dimension is in the 6th section. - * - * 6) This is blob of variable length arrays. Each array starts with a size, - * and is followed by indexes to alias names in the string table. This is - * the third dimension to the section 5. No other section should be referencing - * this section. - * - * 7) Starting in ICU 3.6, this can be a UConverterAliasOptions struct. Its - * presence indicates that a section 9 exists. UConverterAliasOptions specifies - * what type of string normalization is used among other potential things in the - * future. - * - * 8) This is the string table. All strings are indexed on an even address. - * There are two reasons for this. First many chip architectures locate strings - * faster on even address boundaries. Second, since all indexes are 16-bit - * numbers, this string table can be 128KB in size instead of 64KB when we - * only have strings starting on an even address. - * - * 9) When present this is a set of prenormalized strings from section 8. This - * table contains normalized strings with the dashes and spaces stripped out, - * and all strings lowercased. In the future, the options in section 7 may state - * other types of normalization. - * - * Here is the concept of section 5 and 6. It's a 3D cube. Each tag - * has a unique alias among all converters. That same alias can - * be mentioned in other standards on different converters, - * but only one alias per tag can be unique. - * - * - * Converter Names (Usually in TR22 form) - * -------------------------------------------. - * T / /| - * a / / | - * g / / | - * s / / | - * / / | - * ------------------------------------------/ | - * A | | | - * l | | | - * i | | / - * a | | / - * s | | / - * e | | / - * s | |/ - * ------------------------------------------- - * - * - * - * Here is what it really looks like. It's like swiss cheese. - * There are holes. Some converters aren't recognized by - * a standard, or they are really old converters that the - * standard doesn't recognize anymore. - * - * Converter Names (Usually in TR22 form) - * -------------------------------------------. - * T /##########################################/| - * a / # # /# - * g / # ## ## ### # ### ### ### #/ - * s / # ##### #### ## ## #/# - * / ### # # ## # # # ### # # #/## - * ------------------------------------------/# # - * A |### # # ## # # # ### # # #|# # - * l |# # # # # ## # #|# # - * i |# # # # # # #|# - * a |# #|# - * s | #|# - * e - * s - * - */ - -/** - * Used by the UEnumeration API - */ -typedef struct UAliasContext { - uint32_t listOffset; - uint32_t listIdx; -} UAliasContext; - -static const char DATA_NAME[] = "cnvalias"; -static const char DATA_TYPE[] = "icu"; - -static UDataMemory *gAliasData=NULL; -static icu::UInitOnce gAliasDataInitOnce {}; - -enum { - tocLengthIndex=0, - converterListIndex=1, - tagListIndex=2, - aliasListIndex=3, - untaggedConvArrayIndex=4, - taggedAliasArrayIndex=5, - taggedAliasListsIndex=6, - tableOptionsIndex=7, - stringTableIndex=8, - normalizedStringTableIndex=9, - offsetsCount, /* length of the swapper's temporary offsets[] */ - minTocLength=8 /* min. tocLength in the file, does not count the tocLengthIndex! */ -}; - -static const UConverterAliasOptions defaultTableOptions = { - UCNV_IO_UNNORMALIZED, - 0 /* containsCnvOptionInfo */ -}; -static UConverterAlias gMainTable; - -#define GET_STRING(idx) (const char *)(gMainTable.stringTable + (idx)) -#define GET_NORMALIZED_STRING(idx) (const char *)(gMainTable.normalizedStringTable + (idx)) - -static UBool U_CALLCONV -isAcceptable(void * /*context*/, - const char * /*type*/, const char * /*name*/, - const UDataInfo *pInfo) { - return (UBool)( - pInfo->size>=20 && - pInfo->isBigEndian==U_IS_BIG_ENDIAN && - pInfo->charsetFamily==U_CHARSET_FAMILY && - pInfo->dataFormat[0]==0x43 && /* dataFormat="CvAl" */ - pInfo->dataFormat[1]==0x76 && - pInfo->dataFormat[2]==0x41 && - pInfo->dataFormat[3]==0x6c && - pInfo->formatVersion[0]==3); -} - -static UBool U_CALLCONV ucnv_io_cleanup(void) -{ - if (gAliasData) { - udata_close(gAliasData); - gAliasData = NULL; - } - gAliasDataInitOnce.reset(); - - uprv_memset(&gMainTable, 0, sizeof(gMainTable)); - - return true; /* Everything was cleaned up */ -} - -static void U_CALLCONV initAliasData(UErrorCode &errCode) { - UDataMemory *data; - const uint16_t *table; - const uint32_t *sectionSizes; - uint32_t tableStart; - uint32_t currOffset; - - ucln_common_registerCleanup(UCLN_COMMON_UCNV_IO, ucnv_io_cleanup); - - U_ASSERT(gAliasData == NULL); - data = udata_openChoice(NULL, DATA_TYPE, DATA_NAME, isAcceptable, NULL, &errCode); - if(U_FAILURE(errCode)) { - return; - } - - sectionSizes = (const uint32_t *)udata_getMemory(data); - table = (const uint16_t *)sectionSizes; - - tableStart = sectionSizes[0]; - if (tableStart < minTocLength) { - errCode = U_INVALID_FORMAT_ERROR; - udata_close(data); - return; - } - gAliasData = data; - - gMainTable.converterListSize = sectionSizes[1]; - gMainTable.tagListSize = sectionSizes[2]; - gMainTable.aliasListSize = sectionSizes[3]; - gMainTable.untaggedConvArraySize = sectionSizes[4]; - gMainTable.taggedAliasArraySize = sectionSizes[5]; - gMainTable.taggedAliasListsSize = sectionSizes[6]; - gMainTable.optionTableSize = sectionSizes[7]; - gMainTable.stringTableSize = sectionSizes[8]; - - if (tableStart > 8) { - gMainTable.normalizedStringTableSize = sectionSizes[9]; - } - - currOffset = tableStart * (sizeof(uint32_t)/sizeof(uint16_t)) + (sizeof(uint32_t)/sizeof(uint16_t)); - gMainTable.converterList = table + currOffset; - - currOffset += gMainTable.converterListSize; - gMainTable.tagList = table + currOffset; - - currOffset += gMainTable.tagListSize; - gMainTable.aliasList = table + currOffset; - - currOffset += gMainTable.aliasListSize; - gMainTable.untaggedConvArray = table + currOffset; - - currOffset += gMainTable.untaggedConvArraySize; - gMainTable.taggedAliasArray = table + currOffset; - - /* aliasLists is a 1's based array, but it has a padding character */ - currOffset += gMainTable.taggedAliasArraySize; - gMainTable.taggedAliasLists = table + currOffset; - - currOffset += gMainTable.taggedAliasListsSize; - if (gMainTable.optionTableSize > 0 - && ((const UConverterAliasOptions *)(table + currOffset))->stringNormalizationType < UCNV_IO_NORM_TYPE_COUNT) - { - /* Faster table */ - gMainTable.optionTable = (const UConverterAliasOptions *)(table + currOffset); - } - else { - /* Smaller table, or I can't handle this normalization mode! - Use the original slower table lookup. */ - gMainTable.optionTable = &defaultTableOptions; - } - - currOffset += gMainTable.optionTableSize; - gMainTable.stringTable = table + currOffset; - - currOffset += gMainTable.stringTableSize; - gMainTable.normalizedStringTable = ((gMainTable.optionTable->stringNormalizationType == UCNV_IO_UNNORMALIZED) - ? gMainTable.stringTable : (table + currOffset)); -} - - -static UBool -haveAliasData(UErrorCode *pErrorCode) { - umtx_initOnce(gAliasDataInitOnce, &initAliasData, *pErrorCode); - return U_SUCCESS(*pErrorCode); -} - -static inline UBool -isAlias(const char *alias, UErrorCode *pErrorCode) { - if(alias==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - return (UBool)(*alias!=0); -} - -static uint32_t getTagNumber(const char *tagname) { - if (gMainTable.tagList) { - uint32_t tagNum; - for (tagNum = 0; tagNum < gMainTable.tagListSize; tagNum++) { - if (!uprv_stricmp(GET_STRING(gMainTable.tagList[tagNum]), tagname)) { - return tagNum; - } - } - } - - return UINT32_MAX; -} - -/* character types relevant for ucnv_compareNames() */ -enum { - UIGNORE, - ZERO, - NONZERO, - MINLETTER /* any values from here on are lowercase letter mappings */ -}; - -/* character types for ASCII 00..7F */ -static const uint8_t asciiTypes[128] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, 0, 0, 0, 0, 0, 0, - 0, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0, 0, 0, 0, 0, - 0, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0, 0, 0, 0, 0 -}; - -#define GET_ASCII_TYPE(c) ((int8_t)(c) >= 0 ? asciiTypes[(uint8_t)c] : (uint8_t)UIGNORE) - -/* character types for EBCDIC 80..FF */ -static const uint8_t ebcdicTypes[128] = { - 0, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0, 0, 0, 0, 0, 0, - 0, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0, 0, 0, 0, 0, 0, - 0, 0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0, 0, 0, 0, 0, 0, - 0, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0, 0, 0, 0, 0, 0, - 0, 0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0, 0, 0, 0, 0, 0, - ZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, 0, 0, 0, 0, 0, 0 -}; - -#define GET_EBCDIC_TYPE(c) ((int8_t)(c) < 0 ? ebcdicTypes[(c)&0x7f] : (uint8_t)UIGNORE) - -#if U_CHARSET_FAMILY==U_ASCII_FAMILY -# define GET_CHAR_TYPE(c) GET_ASCII_TYPE(c) -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY -# define GET_CHAR_TYPE(c) GET_EBCDIC_TYPE(c) -#else -# error U_CHARSET_FAMILY is not valid -#endif - - -/* @see ucnv_compareNames */ -U_CAPI char * U_CALLCONV -ucnv_io_stripASCIIForCompare(char *dst, const char *name) { - char *dstItr = dst; - uint8_t type, nextType; - char c1; - UBool afterDigit = false; - - while ((c1 = *name++) != 0) { - type = GET_ASCII_TYPE(c1); - switch (type) { - case UIGNORE: - afterDigit = false; - continue; /* ignore all but letters and digits */ - case ZERO: - if (!afterDigit) { - nextType = GET_ASCII_TYPE(*name); - if (nextType == ZERO || nextType == NONZERO) { - continue; /* ignore leading zero before another digit */ - } - } - break; - case NONZERO: - afterDigit = true; - break; - default: - c1 = (char)type; /* lowercased letter */ - afterDigit = false; - break; - } - *dstItr++ = c1; - } - *dstItr = 0; - return dst; -} - -U_CAPI char * U_CALLCONV -ucnv_io_stripEBCDICForCompare(char *dst, const char *name) { - char *dstItr = dst; - uint8_t type, nextType; - char c1; - UBool afterDigit = false; - - while ((c1 = *name++) != 0) { - type = GET_EBCDIC_TYPE(c1); - switch (type) { - case UIGNORE: - afterDigit = false; - continue; /* ignore all but letters and digits */ - case ZERO: - if (!afterDigit) { - nextType = GET_EBCDIC_TYPE(*name); - if (nextType == ZERO || nextType == NONZERO) { - continue; /* ignore leading zero before another digit */ - } - } - break; - case NONZERO: - afterDigit = true; - break; - default: - c1 = (char)type; /* lowercased letter */ - afterDigit = false; - break; - } - *dstItr++ = c1; - } - *dstItr = 0; - return dst; -} - -/** - * Do a fuzzy compare of two converter/alias names. - * The comparison is case-insensitive, ignores leading zeroes if they are not - * followed by further digits, and ignores all but letters and digits. - * Thus the strings "UTF-8", "utf_8", "u*T@f08" and "Utf 8" are exactly equivalent. - * See section 1.4, Charset Alias Matching in Unicode Technical Standard #22 - * at http://www.unicode.org/reports/tr22/ - * - * This is a symmetrical (commutative) operation; order of arguments - * is insignificant. This is an important property for sorting the - * list (when the list is preprocessed into binary form) and for - * performing binary searches on it at run time. - * - * @param name1 a converter name or alias, zero-terminated - * @param name2 a converter name or alias, zero-terminated - * @return 0 if the names match, or a negative value if the name1 - * lexically precedes name2, or a positive value if the name1 - * lexically follows name2. - * - * @see ucnv_io_stripForCompare - */ -U_CAPI int U_EXPORT2 -ucnv_compareNames(const char *name1, const char *name2) { - int rc; - uint8_t type, nextType; - char c1, c2; - UBool afterDigit1 = false, afterDigit2 = false; - - for (;;) { - while ((c1 = *name1++) != 0) { - type = GET_CHAR_TYPE(c1); - switch (type) { - case UIGNORE: - afterDigit1 = false; - continue; /* ignore all but letters and digits */ - case ZERO: - if (!afterDigit1) { - nextType = GET_CHAR_TYPE(*name1); - if (nextType == ZERO || nextType == NONZERO) { - continue; /* ignore leading zero before another digit */ - } - } - break; - case NONZERO: - afterDigit1 = true; - break; - default: - c1 = (char)type; /* lowercased letter */ - afterDigit1 = false; - break; - } - break; /* deliver c1 */ - } - while ((c2 = *name2++) != 0) { - type = GET_CHAR_TYPE(c2); - switch (type) { - case UIGNORE: - afterDigit2 = false; - continue; /* ignore all but letters and digits */ - case ZERO: - if (!afterDigit2) { - nextType = GET_CHAR_TYPE(*name2); - if (nextType == ZERO || nextType == NONZERO) { - continue; /* ignore leading zero before another digit */ - } - } - break; - case NONZERO: - afterDigit2 = true; - break; - default: - c2 = (char)type; /* lowercased letter */ - afterDigit2 = false; - break; - } - break; /* deliver c2 */ - } - - /* If we reach the ends of both strings then they match */ - if ((c1|c2)==0) { - return 0; - } - - /* Case-insensitive comparison */ - rc = (int)(unsigned char)c1 - (int)(unsigned char)c2; - if (rc != 0) { - return rc; - } - } -} - -/* - * search for an alias - * return the converter number index for gConverterList - */ -static inline uint32_t -findConverter(const char *alias, UBool *containsOption, UErrorCode *pErrorCode) { - uint32_t mid, start, limit; - uint32_t lastMid; - int result; - int isUnnormalized = (gMainTable.optionTable->stringNormalizationType == UCNV_IO_UNNORMALIZED); - char strippedName[UCNV_MAX_CONVERTER_NAME_LENGTH]; - - if (!isUnnormalized) { - if (uprv_strlen(alias) >= UCNV_MAX_CONVERTER_NAME_LENGTH) { - *pErrorCode = U_BUFFER_OVERFLOW_ERROR; - return UINT32_MAX; - } - - /* Lower case and remove ignoreable characters. */ - ucnv_io_stripForCompare(strippedName, alias); - alias = strippedName; - } - - /* do a binary search for the alias */ - start = 0; - limit = gMainTable.untaggedConvArraySize; - mid = limit; - lastMid = UINT32_MAX; - - for (;;) { - mid = (uint32_t)((start + limit) / 2); - if (lastMid == mid) { /* Have we moved? */ - break; /* We haven't moved, and it wasn't found. */ - } - lastMid = mid; - if (isUnnormalized) { - result = ucnv_compareNames(alias, GET_STRING(gMainTable.aliasList[mid])); - } - else { - result = uprv_strcmp(alias, GET_NORMALIZED_STRING(gMainTable.aliasList[mid])); - } - - if (result < 0) { - limit = mid; - } else if (result > 0) { - start = mid; - } else { - /* Since the gencnval tool folds duplicates into one entry, - * this alias in gAliasList is unique, but different standards - * may map an alias to different converters. - */ - if (gMainTable.untaggedConvArray[mid] & UCNV_AMBIGUOUS_ALIAS_MAP_BIT) { - *pErrorCode = U_AMBIGUOUS_ALIAS_WARNING; - } - /* State whether the canonical converter name contains an option. - This information is contained in this list in order to maintain backward & forward compatibility. */ - if (containsOption) { - UBool containsCnvOptionInfo = (UBool)gMainTable.optionTable->containsCnvOptionInfo; - *containsOption = (UBool)((containsCnvOptionInfo - && ((gMainTable.untaggedConvArray[mid] & UCNV_CONTAINS_OPTION_BIT) != 0)) - || !containsCnvOptionInfo); - } - return gMainTable.untaggedConvArray[mid] & UCNV_CONVERTER_INDEX_MASK; - } - } - - return UINT32_MAX; -} - -/* - * Is this alias in this list? - * alias and listOffset should be non-NULL. - */ -static inline UBool -isAliasInList(const char *alias, uint32_t listOffset) { - if (listOffset) { - uint32_t currAlias; - uint32_t listCount = gMainTable.taggedAliasLists[listOffset]; - /* +1 to skip listCount */ - const uint16_t *currList = gMainTable.taggedAliasLists + listOffset + 1; - for (currAlias = 0; currAlias < listCount; currAlias++) { - if (currList[currAlias] - && ucnv_compareNames(alias, GET_STRING(currList[currAlias]))==0) - { - return true; - } - } - } - return false; -} - -/* - * Search for an standard name of an alias (what is the default name - * that this standard uses?) - * return the listOffset for gTaggedAliasLists. If it's 0, - * the it couldn't be found, but the parameters are valid. - */ -static uint32_t -findTaggedAliasListsOffset(const char *alias, const char *standard, UErrorCode *pErrorCode) { - uint32_t idx; - uint32_t listOffset; - uint32_t convNum; - UErrorCode myErr = U_ZERO_ERROR; - uint32_t tagNum = getTagNumber(standard); - - /* Make a quick guess. Hopefully they used a TR22 canonical alias. */ - convNum = findConverter(alias, NULL, &myErr); - if (myErr != U_ZERO_ERROR) { - *pErrorCode = myErr; - } - - if (tagNum < (gMainTable.tagListSize - UCNV_NUM_HIDDEN_TAGS) && convNum < gMainTable.converterListSize) { - listOffset = gMainTable.taggedAliasArray[tagNum*gMainTable.converterListSize + convNum]; - if (listOffset && gMainTable.taggedAliasLists[listOffset + 1]) { - return listOffset; - } - if (myErr == U_AMBIGUOUS_ALIAS_WARNING) { - /* Uh Oh! They used an ambiguous alias. - We have to search the whole swiss cheese starting - at the highest standard affinity. - This may take a while. - */ - for (idx = 0; idx < gMainTable.taggedAliasArraySize; idx++) { - listOffset = gMainTable.taggedAliasArray[idx]; - if (listOffset && isAliasInList(alias, listOffset)) { - uint32_t currTagNum = idx/gMainTable.converterListSize; - uint32_t currConvNum = (idx - currTagNum*gMainTable.converterListSize); - uint32_t tempListOffset = gMainTable.taggedAliasArray[tagNum*gMainTable.converterListSize + currConvNum]; - if (tempListOffset && gMainTable.taggedAliasLists[tempListOffset + 1]) { - return tempListOffset; - } - /* else keep on looking */ - /* We could speed this up by starting on the next row - because an alias is unique per row, right now. - This would change if alias versioning appears. */ - } - } - /* The standard doesn't know about the alias */ - } - /* else no default name */ - return 0; - } - /* else converter or tag not found */ - - return UINT32_MAX; -} - -/* Return the canonical name */ -static uint32_t -findTaggedConverterNum(const char *alias, const char *standard, UErrorCode *pErrorCode) { - uint32_t idx; - uint32_t listOffset; - uint32_t convNum; - UErrorCode myErr = U_ZERO_ERROR; - uint32_t tagNum = getTagNumber(standard); - - /* Make a quick guess. Hopefully they used a TR22 canonical alias. */ - convNum = findConverter(alias, NULL, &myErr); - if (myErr != U_ZERO_ERROR) { - *pErrorCode = myErr; - } - - if (tagNum < (gMainTable.tagListSize - UCNV_NUM_HIDDEN_TAGS) && convNum < gMainTable.converterListSize) { - listOffset = gMainTable.taggedAliasArray[tagNum*gMainTable.converterListSize + convNum]; - if (listOffset && isAliasInList(alias, listOffset)) { - return convNum; - } - if (myErr == U_AMBIGUOUS_ALIAS_WARNING) { - /* Uh Oh! They used an ambiguous alias. - We have to search one slice of the swiss cheese. - We search only in the requested tag, not the whole thing. - This may take a while. - */ - uint32_t convStart = (tagNum)*gMainTable.converterListSize; - uint32_t convLimit = (tagNum+1)*gMainTable.converterListSize; - for (idx = convStart; idx < convLimit; idx++) { - listOffset = gMainTable.taggedAliasArray[idx]; - if (listOffset && isAliasInList(alias, listOffset)) { - return idx-convStart; - } - } - /* The standard doesn't know about the alias */ - } - /* else no canonical name */ - } - /* else converter or tag not found */ - - return UINT32_MAX; -} - -U_CAPI const char * -ucnv_io_getConverterName(const char *alias, UBool *containsOption, UErrorCode *pErrorCode) { - const char *aliasTmp = alias; - int32_t i = 0; - for (i = 0; i < 2; i++) { - if (i == 1) { - /* - * After the first unsuccess converter lookup, check to see if - * the name begins with 'x-'. If it does, strip it off and try - * again. This behaviour is similar to how ICU4J does it. - */ - if (aliasTmp[0] == 'x' && aliasTmp[1] == '-') { - aliasTmp = aliasTmp+2; - } else { - break; - } - } - if(haveAliasData(pErrorCode) && isAlias(aliasTmp, pErrorCode)) { - uint32_t convNum = findConverter(aliasTmp, containsOption, pErrorCode); - if (convNum < gMainTable.converterListSize) { - return GET_STRING(gMainTable.converterList[convNum]); - } - /* else converter not found */ - } else { - break; - } - } - - return NULL; -} - -U_CDECL_BEGIN - - -static int32_t U_CALLCONV -ucnv_io_countStandardAliases(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) { - int32_t value = 0; - UAliasContext *myContext = (UAliasContext *)(enumerator->context); - uint32_t listOffset = myContext->listOffset; - - if (listOffset) { - value = gMainTable.taggedAliasLists[listOffset]; - } - return value; -} - -static const char * U_CALLCONV -ucnv_io_nextStandardAliases(UEnumeration *enumerator, - int32_t* resultLength, - UErrorCode * /*pErrorCode*/) -{ - UAliasContext *myContext = (UAliasContext *)(enumerator->context); - uint32_t listOffset = myContext->listOffset; - - if (listOffset) { - uint32_t listCount = gMainTable.taggedAliasLists[listOffset]; - const uint16_t *currList = gMainTable.taggedAliasLists + listOffset + 1; - - if (myContext->listIdx < listCount) { - const char *myStr = GET_STRING(currList[myContext->listIdx++]); - if (resultLength) { - *resultLength = (int32_t)uprv_strlen(myStr); - } - return myStr; - } - } - /* Either we accessed a zero length list, or we enumerated too far. */ - if (resultLength) { - *resultLength = 0; - } - return NULL; -} - -static void U_CALLCONV -ucnv_io_resetStandardAliases(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) { - ((UAliasContext *)(enumerator->context))->listIdx = 0; -} - -static void U_CALLCONV -ucnv_io_closeUEnumeration(UEnumeration *enumerator) { - uprv_free(enumerator->context); - uprv_free(enumerator); -} - -U_CDECL_END - -/* Enumerate the aliases for the specified converter and standard tag */ -static const UEnumeration gEnumAliases = { - NULL, - NULL, - ucnv_io_closeUEnumeration, - ucnv_io_countStandardAliases, - uenum_unextDefault, - ucnv_io_nextStandardAliases, - ucnv_io_resetStandardAliases -}; - -U_CAPI UEnumeration * U_EXPORT2 -ucnv_openStandardNames(const char *convName, - const char *standard, - UErrorCode *pErrorCode) -{ - UEnumeration *myEnum = NULL; - if (haveAliasData(pErrorCode) && isAlias(convName, pErrorCode)) { - uint32_t listOffset = findTaggedAliasListsOffset(convName, standard, pErrorCode); - - /* When listOffset == 0, we want to acknowledge that the - converter name and standard are okay, but there - is nothing to enumerate. */ - if (listOffset < gMainTable.taggedAliasListsSize) { - UAliasContext *myContext; - - myEnum = static_cast(uprv_malloc(sizeof(UEnumeration))); - if (myEnum == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uprv_memcpy(myEnum, &gEnumAliases, sizeof(UEnumeration)); - myContext = static_cast(uprv_malloc(sizeof(UAliasContext))); - if (myContext == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - uprv_free(myEnum); - return NULL; - } - myContext->listOffset = listOffset; - myContext->listIdx = 0; - myEnum->context = myContext; - } - /* else converter or tag not found */ - } - return myEnum; -} - -static uint16_t -ucnv_io_countAliases(const char *alias, UErrorCode *pErrorCode) { - if(haveAliasData(pErrorCode) && isAlias(alias, pErrorCode)) { - uint32_t convNum = findConverter(alias, NULL, pErrorCode); - if (convNum < gMainTable.converterListSize) { - /* tagListNum - 1 is the ALL tag */ - int32_t listOffset = gMainTable.taggedAliasArray[(gMainTable.tagListSize - 1)*gMainTable.converterListSize + convNum]; - - if (listOffset) { - return gMainTable.taggedAliasLists[listOffset]; - } - /* else this shouldn't happen. internal program error */ - } - /* else converter not found */ - } - return 0; -} - -static uint16_t -ucnv_io_getAliases(const char *alias, uint16_t start, const char **aliases, UErrorCode *pErrorCode) { - if(haveAliasData(pErrorCode) && isAlias(alias, pErrorCode)) { - uint32_t currAlias; - uint32_t convNum = findConverter(alias, NULL, pErrorCode); - if (convNum < gMainTable.converterListSize) { - /* tagListNum - 1 is the ALL tag */ - int32_t listOffset = gMainTable.taggedAliasArray[(gMainTable.tagListSize - 1)*gMainTable.converterListSize + convNum]; - - if (listOffset) { - uint32_t listCount = gMainTable.taggedAliasLists[listOffset]; - /* +1 to skip listCount */ - const uint16_t *currList = gMainTable.taggedAliasLists + listOffset + 1; - - for (currAlias = start; currAlias < listCount; currAlias++) { - aliases[currAlias] = GET_STRING(currList[currAlias]); - } - } - /* else this shouldn't happen. internal program error */ - } - /* else converter not found */ - } - return 0; -} - -static const char * -ucnv_io_getAlias(const char *alias, uint16_t n, UErrorCode *pErrorCode) { - if(haveAliasData(pErrorCode) && isAlias(alias, pErrorCode)) { - uint32_t convNum = findConverter(alias, NULL, pErrorCode); - if (convNum < gMainTable.converterListSize) { - /* tagListNum - 1 is the ALL tag */ - int32_t listOffset = gMainTable.taggedAliasArray[(gMainTable.tagListSize - 1)*gMainTable.converterListSize + convNum]; - - if (listOffset) { - uint32_t listCount = gMainTable.taggedAliasLists[listOffset]; - /* +1 to skip listCount */ - const uint16_t *currList = gMainTable.taggedAliasLists + listOffset + 1; - - if (n < listCount) { - return GET_STRING(currList[n]); - } - *pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR; - } - /* else this shouldn't happen. internal program error */ - } - /* else converter not found */ - } - return NULL; -} - -static uint16_t -ucnv_io_countStandards(UErrorCode *pErrorCode) { - if (haveAliasData(pErrorCode)) { - /* Don't include the empty list */ - return (uint16_t)(gMainTable.tagListSize - UCNV_NUM_HIDDEN_TAGS); - } - - return 0; -} - -U_CAPI const char * U_EXPORT2 -ucnv_getStandard(uint16_t n, UErrorCode *pErrorCode) { - if (haveAliasData(pErrorCode)) { - if (n < gMainTable.tagListSize - UCNV_NUM_HIDDEN_TAGS) { - return GET_STRING(gMainTable.tagList[n]); - } - *pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR; - } - - return NULL; -} - -U_CAPI const char * U_EXPORT2 -ucnv_getStandardName(const char *alias, const char *standard, UErrorCode *pErrorCode) { - if (haveAliasData(pErrorCode) && isAlias(alias, pErrorCode)) { - uint32_t listOffset = findTaggedAliasListsOffset(alias, standard, pErrorCode); - - if (0 < listOffset && listOffset < gMainTable.taggedAliasListsSize) { - const uint16_t *currList = gMainTable.taggedAliasLists + listOffset + 1; - - /* Get the preferred name from this list */ - if (currList[0]) { - return GET_STRING(currList[0]); - } - /* else someone screwed up the alias table. */ - /* *pErrorCode = U_INVALID_FORMAT_ERROR */ - } - } - - return NULL; -} - -U_CAPI uint16_t U_EXPORT2 -ucnv_countAliases(const char *alias, UErrorCode *pErrorCode) -{ - return ucnv_io_countAliases(alias, pErrorCode); -} - - -U_CAPI const char* U_EXPORT2 -ucnv_getAlias(const char *alias, uint16_t n, UErrorCode *pErrorCode) -{ - return ucnv_io_getAlias(alias, n, pErrorCode); -} - -U_CAPI void U_EXPORT2 -ucnv_getAliases(const char *alias, const char **aliases, UErrorCode *pErrorCode) -{ - ucnv_io_getAliases(alias, 0, aliases, pErrorCode); -} - -U_CAPI uint16_t U_EXPORT2 -ucnv_countStandards(void) -{ - UErrorCode err = U_ZERO_ERROR; - return ucnv_io_countStandards(&err); -} - -U_CAPI const char * U_EXPORT2 -ucnv_getCanonicalName(const char *alias, const char *standard, UErrorCode *pErrorCode) { - if (haveAliasData(pErrorCode) && isAlias(alias, pErrorCode)) { - uint32_t convNum = findTaggedConverterNum(alias, standard, pErrorCode); - - if (convNum < gMainTable.converterListSize) { - return GET_STRING(gMainTable.converterList[convNum]); - } - } - - return NULL; -} - -U_CDECL_BEGIN - - -static int32_t U_CALLCONV -ucnv_io_countAllConverters(UEnumeration * /*enumerator*/, UErrorCode * /*pErrorCode*/) { - return gMainTable.converterListSize; -} - -static const char * U_CALLCONV -ucnv_io_nextAllConverters(UEnumeration *enumerator, - int32_t* resultLength, - UErrorCode * /*pErrorCode*/) -{ - uint16_t *myContext = (uint16_t *)(enumerator->context); - - if (*myContext < gMainTable.converterListSize) { - const char *myStr = GET_STRING(gMainTable.converterList[(*myContext)++]); - if (resultLength) { - *resultLength = (int32_t)uprv_strlen(myStr); - } - return myStr; - } - /* Either we accessed a zero length list, or we enumerated too far. */ - if (resultLength) { - *resultLength = 0; - } - return NULL; -} - -static void U_CALLCONV -ucnv_io_resetAllConverters(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) { - *((uint16_t *)(enumerator->context)) = 0; -} -U_CDECL_END -static const UEnumeration gEnumAllConverters = { - NULL, - NULL, - ucnv_io_closeUEnumeration, - ucnv_io_countAllConverters, - uenum_unextDefault, - ucnv_io_nextAllConverters, - ucnv_io_resetAllConverters -}; - -U_CAPI UEnumeration * U_EXPORT2 -ucnv_openAllNames(UErrorCode *pErrorCode) { - UEnumeration *myEnum = NULL; - if (haveAliasData(pErrorCode)) { - uint16_t *myContext; - - myEnum = static_cast(uprv_malloc(sizeof(UEnumeration))); - if (myEnum == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uprv_memcpy(myEnum, &gEnumAllConverters, sizeof(UEnumeration)); - myContext = static_cast(uprv_malloc(sizeof(uint16_t))); - if (myContext == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - uprv_free(myEnum); - return NULL; - } - *myContext = 0; - myEnum->context = myContext; - } - return myEnum; -} - -U_CAPI uint16_t -ucnv_io_countKnownConverters(UErrorCode *pErrorCode) { - if (haveAliasData(pErrorCode)) { - return (uint16_t)gMainTable.converterListSize; - } - return 0; -} - -/* alias table swapping ----------------------------------------------------- */ - -U_CDECL_BEGIN - -typedef char * U_CALLCONV StripForCompareFn(char *dst, const char *name); -U_CDECL_END - - -/* - * row of a temporary array - * - * gets platform-endian charset string indexes and sorting indexes; - * after sorting this array by strings, the actual arrays are permutated - * according to the sorting indexes - */ -typedef struct TempRow { - uint16_t strIndex, sortIndex; -} TempRow; - -typedef struct TempAliasTable { - const char *chars; - TempRow *rows; - uint16_t *resort; - StripForCompareFn *stripForCompare; -} TempAliasTable; - -enum { - STACK_ROW_CAPACITY=500 -}; - -static int32_t U_CALLCONV -io_compareRows(const void *context, const void *left, const void *right) { - char strippedLeft[UCNV_MAX_CONVERTER_NAME_LENGTH], - strippedRight[UCNV_MAX_CONVERTER_NAME_LENGTH]; - - TempAliasTable *tempTable=(TempAliasTable *)context; - const char *chars=tempTable->chars; - - return (int32_t)uprv_strcmp(tempTable->stripForCompare(strippedLeft, chars+2*((const TempRow *)left)->strIndex), - tempTable->stripForCompare(strippedRight, chars+2*((const TempRow *)right)->strIndex)); -} - -U_CAPI int32_t U_EXPORT2 -ucnv_swapAliases(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const UDataInfo *pInfo; - int32_t headerSize; - - const uint16_t *inTable; - const uint32_t *inSectionSizes; - uint32_t toc[offsetsCount]; - uint32_t offsets[offsetsCount]; /* 16-bit-addressed offsets from inTable/outTable */ - uint32_t i, count, tocLength, topOffset; - - TempRow rows[STACK_ROW_CAPACITY]; - uint16_t resort[STACK_ROW_CAPACITY]; - TempAliasTable tempTable; - - /* udata_swapDataHeader checks the arguments */ - headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - /* check data format and format version */ - pInfo=(const UDataInfo *)((const char *)inData+4); - if(!( - pInfo->dataFormat[0]==0x43 && /* dataFormat="CvAl" */ - pInfo->dataFormat[1]==0x76 && - pInfo->dataFormat[2]==0x41 && - pInfo->dataFormat[3]==0x6c && - pInfo->formatVersion[0]==3 - )) { - udata_printError(ds, "ucnv_swapAliases(): data format %02x.%02x.%02x.%02x (format version %02x) is not an alias table\n", - pInfo->dataFormat[0], pInfo->dataFormat[1], - pInfo->dataFormat[2], pInfo->dataFormat[3], - pInfo->formatVersion[0]); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - /* an alias table must contain at least the table of contents array */ - if(length>=0 && (length-headerSize)<4*(1+minTocLength)) { - udata_printError(ds, "ucnv_swapAliases(): too few bytes (%d after header) for an alias table\n", - length-headerSize); - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - inSectionSizes=(const uint32_t *)((const char *)inData+headerSize); - inTable=(const uint16_t *)inSectionSizes; - uprv_memset(toc, 0, sizeof(toc)); - toc[tocLengthIndex]=tocLength=ds->readUInt32(inSectionSizes[tocLengthIndex]); - if(tocLengthreadUInt32(inSectionSizes[i]); - } - - /* compute offsets */ - uprv_memset(offsets, 0, sizeof(offsets)); - offsets[converterListIndex]=2*(1+tocLength); /* count two 16-bit units per toc entry */ - for(i=tagListIndex; i<=tocLength; ++i) { - offsets[i]=offsets[i-1]+toc[i-1]; - } - - /* compute the overall size of the after-header data, in numbers of 16-bit units */ - topOffset=offsets[i-1]+toc[i-1]; - - if(length>=0) { - uint16_t *outTable; - const uint16_t *p, *p2; - uint16_t *q, *q2; - uint16_t oldIndex; - - if((length-headerSize)<(2*(int32_t)topOffset)) { - udata_printError(ds, "ucnv_swapAliases(): too few bytes (%d after header) for an alias table\n", - length-headerSize); - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - outTable=(uint16_t *)((char *)outData+headerSize); - - /* swap the entire table of contents */ - ds->swapArray32(ds, inTable, 4*(1+tocLength), outTable, pErrorCode); - - /* swap unormalized strings & normalized strings */ - ds->swapInvChars(ds, inTable+offsets[stringTableIndex], 2*(int32_t)(toc[stringTableIndex]+toc[normalizedStringTableIndex]), - outTable+offsets[stringTableIndex], pErrorCode); - if(U_FAILURE(*pErrorCode)) { - udata_printError(ds, "ucnv_swapAliases().swapInvChars(charset names) failed\n"); - return 0; - } - - if(ds->inCharset==ds->outCharset) { - /* no need to sort, just swap all 16-bit values together */ - ds->swapArray16(ds, - inTable+offsets[converterListIndex], - 2*(int32_t)(offsets[stringTableIndex]-offsets[converterListIndex]), - outTable+offsets[converterListIndex], - pErrorCode); - } else { - /* allocate the temporary table for sorting */ - count=toc[aliasListIndex]; - - tempTable.chars=(const char *)(outTable+offsets[stringTableIndex]); /* sort by outCharset */ - - if(count<=STACK_ROW_CAPACITY) { - tempTable.rows=rows; - tempTable.resort=resort; - } else { - tempTable.rows=(TempRow *)uprv_malloc(count*sizeof(TempRow)+count*2); - if(tempTable.rows==NULL) { - udata_printError(ds, "ucnv_swapAliases(): unable to allocate memory for sorting tables (max length: %u)\n", - count); - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return 0; - } - tempTable.resort=(uint16_t *)(tempTable.rows+count); - } - - if(ds->outCharset==U_ASCII_FAMILY) { - tempTable.stripForCompare=ucnv_io_stripASCIIForCompare; - } else /* U_EBCDIC_FAMILY */ { - tempTable.stripForCompare=ucnv_io_stripEBCDICForCompare; - } - - /* - * Sort unique aliases+mapped names. - * - * We need to sort the list again by outCharset strings because they - * sort differently for different charset families. - * First we set up a temporary table with the string indexes and - * sorting indexes and sort that. - * Then we permutate and copy/swap the actual values. - */ - p=inTable+offsets[aliasListIndex]; - q=outTable+offsets[aliasListIndex]; - - p2=inTable+offsets[untaggedConvArrayIndex]; - q2=outTable+offsets[untaggedConvArrayIndex]; - - for(i=0; ireadUInt16(p[i]); - tempTable.rows[i].sortIndex=(uint16_t)i; - } - - uprv_sortArray(tempTable.rows, (int32_t)count, sizeof(TempRow), - io_compareRows, &tempTable, - false, pErrorCode); - - if(U_SUCCESS(*pErrorCode)) { - /* copy/swap/permutate items */ - if(p!=q) { - for(i=0; iswapArray16(ds, p+oldIndex, 2, q+i, pErrorCode); - ds->swapArray16(ds, p2+oldIndex, 2, q2+i, pErrorCode); - } - } else { - /* - * If we swap in-place, then the permutation must use another - * temporary array (tempTable.resort) - * before the results are copied to the outBundle. - */ - uint16_t *r=tempTable.resort; - - for(i=0; iswapArray16(ds, p+oldIndex, 2, r+i, pErrorCode); - } - uprv_memcpy(q, r, 2*(size_t)count); - - for(i=0; iswapArray16(ds, p2+oldIndex, 2, r+i, pErrorCode); - } - uprv_memcpy(q2, r, 2*(size_t)count); - } - } - - if(tempTable.rows!=rows) { - uprv_free(tempTable.rows); - } - - if(U_FAILURE(*pErrorCode)) { - udata_printError(ds, "ucnv_swapAliases().uprv_sortArray(%u items) failed\n", - count); - return 0; - } - - /* swap remaining 16-bit values */ - ds->swapArray16(ds, - inTable+offsets[converterListIndex], - 2*(int32_t)(offsets[aliasListIndex]-offsets[converterListIndex]), - outTable+offsets[converterListIndex], - pErrorCode); - ds->swapArray16(ds, - inTable+offsets[taggedAliasArrayIndex], - 2*(int32_t)(offsets[stringTableIndex]-offsets[taggedAliasArrayIndex]), - outTable+offsets[taggedAliasArrayIndex], - pErrorCode); - } - } - - return headerSize+2*(int32_t)topOffset; -} - -#endif - - -/* - * Hey, Emacs, please set the following: - * - * Local Variables: - * indent-tabs-mode: nil - * End: - * - */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* +* ucnv_io.cpp: +* initializes global variables and defines functions pertaining to converter +* name resolution aspect of the conversion code. +* +* new implementation: +* +* created on: 1999nov22 +* created by: Markus W. Scherer +* +* Use the binary cnvalias.icu (created from convrtrs.txt) to work +* with aliases for converter names. +* +* Date Name Description +* 11/22/1999 markus Created +* 06/28/2002 grhoten Major overhaul of the converter alias design. +* Now an alias can map to different converters +* depending on the specified standard. +******************************************************************************* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/udata.h" + +#include "umutex.h" +#include "uarrsort.h" +#include "uassert.h" +#include "udataswp.h" +#include "cstring.h" +#include "cmemory.h" +#include "ucnv_io.h" +#include "uenumimp.h" +#include "ucln_cmn.h" + +/* Format of cnvalias.icu ----------------------------------------------------- + * + * cnvalias.icu is a binary, memory-mappable form of convrtrs.txt. + * This binary form contains several tables. All indexes are to uint16_t + * units, and not to the bytes (uint8_t units). Addressing everything on + * 16-bit boundaries allows us to store more information with small index + * numbers, which are also 16-bit in size. The majority of the table (except + * the string table) are 16-bit numbers. + * + * First there is the size of the Table of Contents (TOC). The TOC + * entries contain the size of each section. In order to find the offset + * you just need to sum up the previous offsets. + * The TOC length and entries are an array of uint32_t values. + * The first section after the TOC starts immediately after the TOC. + * + * 1) This section contains a list of converters. This list contains indexes + * into the string table for the converter name. The index of this list is + * also used by other sections, which are mentioned later on. + * This list is not sorted. + * + * 2) This section contains a list of tags. This list contains indexes + * into the string table for the tag name. The index of this list is + * also used by other sections, which are mentioned later on. + * This list is in priority order of standards. + * + * 3) This section contains a list of sorted unique aliases. This + * list contains indexes into the string table for the alias name. The + * index of this list is also used by other sections, like the 4th section. + * The index for the 3rd and 4th section is used to get the + * alias -> converter name mapping. Section 3 and 4 form a two column table. + * Some of the most significant bits of each index may contain other + * information (see findConverter for details). + * + * 4) This section contains a list of mapped converter names. Consider this + * as a table that maps the 3rd section to the 1st section. This list contains + * indexes into the 1st section. The index of this list is the same index in + * the 3rd section. There is also some extra information in the high bits of + * each converter index in this table. Currently it's only used to say that + * an alias mapped to this converter is ambiguous. See UCNV_CONVERTER_INDEX_MASK + * and UCNV_AMBIGUOUS_ALIAS_MAP_BIT for more information. This section is + * the predigested form of the 5th section so that an alias lookup can be fast. + * + * 5) This section contains a 2D array with indexes to the 6th section. This + * section is the full form of all alias mappings. The column index is the + * index into the converter list (column header). The row index is the index + * to tag list (row header). This 2D array is the top part a 3D array. The + * third dimension is in the 6th section. + * + * 6) This is blob of variable length arrays. Each array starts with a size, + * and is followed by indexes to alias names in the string table. This is + * the third dimension to the section 5. No other section should be referencing + * this section. + * + * 7) Starting in ICU 3.6, this can be a UConverterAliasOptions struct. Its + * presence indicates that a section 9 exists. UConverterAliasOptions specifies + * what type of string normalization is used among other potential things in the + * future. + * + * 8) This is the string table. All strings are indexed on an even address. + * There are two reasons for this. First many chip architectures locate strings + * faster on even address boundaries. Second, since all indexes are 16-bit + * numbers, this string table can be 128KB in size instead of 64KB when we + * only have strings starting on an even address. + * + * 9) When present this is a set of prenormalized strings from section 8. This + * table contains normalized strings with the dashes and spaces stripped out, + * and all strings lowercased. In the future, the options in section 7 may state + * other types of normalization. + * + * Here is the concept of section 5 and 6. It's a 3D cube. Each tag + * has a unique alias among all converters. That same alias can + * be mentioned in other standards on different converters, + * but only one alias per tag can be unique. + * + * + * Converter Names (Usually in TR22 form) + * -------------------------------------------. + * T / /| + * a / / | + * g / / | + * s / / | + * / / | + * ------------------------------------------/ | + * A | | | + * l | | | + * i | | / + * a | | / + * s | | / + * e | | / + * s | |/ + * ------------------------------------------- + * + * + * + * Here is what it really looks like. It's like swiss cheese. + * There are holes. Some converters aren't recognized by + * a standard, or they are really old converters that the + * standard doesn't recognize anymore. + * + * Converter Names (Usually in TR22 form) + * -------------------------------------------. + * T /##########################################/| + * a / # # /# + * g / # ## ## ### # ### ### ### #/ + * s / # ##### #### ## ## #/# + * / ### # # ## # # # ### # # #/## + * ------------------------------------------/# # + * A |### # # ## # # # ### # # #|# # + * l |# # # # # ## # #|# # + * i |# # # # # # #|# + * a |# #|# + * s | #|# + * e + * s + * + */ + +/** + * Used by the UEnumeration API + */ +typedef struct UAliasContext { + uint32_t listOffset; + uint32_t listIdx; +} UAliasContext; + +static const char DATA_NAME[] = "cnvalias"; +static const char DATA_TYPE[] = "icu"; + +static UDataMemory *gAliasData=nullptr; +static icu::UInitOnce gAliasDataInitOnce {}; + +enum { + tocLengthIndex=0, + converterListIndex=1, + tagListIndex=2, + aliasListIndex=3, + untaggedConvArrayIndex=4, + taggedAliasArrayIndex=5, + taggedAliasListsIndex=6, + tableOptionsIndex=7, + stringTableIndex=8, + normalizedStringTableIndex=9, + offsetsCount, /* length of the swapper's temporary offsets[] */ + minTocLength=8 /* min. tocLength in the file, does not count the tocLengthIndex! */ +}; + +static const UConverterAliasOptions defaultTableOptions = { + UCNV_IO_UNNORMALIZED, + 0 /* containsCnvOptionInfo */ +}; +static UConverterAlias gMainTable; + +#define GET_STRING(idx) (const char *)(gMainTable.stringTable + (idx)) +#define GET_NORMALIZED_STRING(idx) (const char *)(gMainTable.normalizedStringTable + (idx)) + +static UBool U_CALLCONV +isAcceptable(void * /*context*/, + const char * /*type*/, const char * /*name*/, + const UDataInfo *pInfo) { + return (UBool)( + pInfo->size>=20 && + pInfo->isBigEndian==U_IS_BIG_ENDIAN && + pInfo->charsetFamily==U_CHARSET_FAMILY && + pInfo->dataFormat[0]==0x43 && /* dataFormat="CvAl" */ + pInfo->dataFormat[1]==0x76 && + pInfo->dataFormat[2]==0x41 && + pInfo->dataFormat[3]==0x6c && + pInfo->formatVersion[0]==3); +} + +static UBool U_CALLCONV ucnv_io_cleanup() +{ + if (gAliasData) { + udata_close(gAliasData); + gAliasData = nullptr; + } + gAliasDataInitOnce.reset(); + + uprv_memset(&gMainTable, 0, sizeof(gMainTable)); + + return true; /* Everything was cleaned up */ +} + +static void U_CALLCONV initAliasData(UErrorCode &errCode) { + UDataMemory *data; + const uint16_t *table; + const uint32_t *sectionSizes; + uint32_t tableStart; + uint32_t currOffset; + + ucln_common_registerCleanup(UCLN_COMMON_UCNV_IO, ucnv_io_cleanup); + + U_ASSERT(gAliasData == nullptr); + data = udata_openChoice(nullptr, DATA_TYPE, DATA_NAME, isAcceptable, nullptr, &errCode); + if(U_FAILURE(errCode)) { + return; + } + + sectionSizes = (const uint32_t *)udata_getMemory(data); + table = (const uint16_t *)sectionSizes; + + tableStart = sectionSizes[0]; + if (tableStart < minTocLength) { + errCode = U_INVALID_FORMAT_ERROR; + udata_close(data); + return; + } + gAliasData = data; + + gMainTable.converterListSize = sectionSizes[1]; + gMainTable.tagListSize = sectionSizes[2]; + gMainTable.aliasListSize = sectionSizes[3]; + gMainTable.untaggedConvArraySize = sectionSizes[4]; + gMainTable.taggedAliasArraySize = sectionSizes[5]; + gMainTable.taggedAliasListsSize = sectionSizes[6]; + gMainTable.optionTableSize = sectionSizes[7]; + gMainTable.stringTableSize = sectionSizes[8]; + + if (tableStart > 8) { + gMainTable.normalizedStringTableSize = sectionSizes[9]; + } + + currOffset = tableStart * (sizeof(uint32_t)/sizeof(uint16_t)) + (sizeof(uint32_t)/sizeof(uint16_t)); + gMainTable.converterList = table + currOffset; + + currOffset += gMainTable.converterListSize; + gMainTable.tagList = table + currOffset; + + currOffset += gMainTable.tagListSize; + gMainTable.aliasList = table + currOffset; + + currOffset += gMainTable.aliasListSize; + gMainTable.untaggedConvArray = table + currOffset; + + currOffset += gMainTable.untaggedConvArraySize; + gMainTable.taggedAliasArray = table + currOffset; + + /* aliasLists is a 1's based array, but it has a padding character */ + currOffset += gMainTable.taggedAliasArraySize; + gMainTable.taggedAliasLists = table + currOffset; + + currOffset += gMainTable.taggedAliasListsSize; + if (gMainTable.optionTableSize > 0 + && ((const UConverterAliasOptions *)(table + currOffset))->stringNormalizationType < UCNV_IO_NORM_TYPE_COUNT) + { + /* Faster table */ + gMainTable.optionTable = (const UConverterAliasOptions *)(table + currOffset); + } + else { + /* Smaller table, or I can't handle this normalization mode! + Use the original slower table lookup. */ + gMainTable.optionTable = &defaultTableOptions; + } + + currOffset += gMainTable.optionTableSize; + gMainTable.stringTable = table + currOffset; + + currOffset += gMainTable.stringTableSize; + gMainTable.normalizedStringTable = ((gMainTable.optionTable->stringNormalizationType == UCNV_IO_UNNORMALIZED) + ? gMainTable.stringTable : (table + currOffset)); +} + + +static UBool +haveAliasData(UErrorCode *pErrorCode) { + umtx_initOnce(gAliasDataInitOnce, &initAliasData, *pErrorCode); + return U_SUCCESS(*pErrorCode); +} + +static inline UBool +isAlias(const char *alias, UErrorCode *pErrorCode) { + if(alias==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + return (UBool)(*alias!=0); +} + +static uint32_t getTagNumber(const char *tagname) { + if (gMainTable.tagList) { + uint32_t tagNum; + for (tagNum = 0; tagNum < gMainTable.tagListSize; tagNum++) { + if (!uprv_stricmp(GET_STRING(gMainTable.tagList[tagNum]), tagname)) { + return tagNum; + } + } + } + + return UINT32_MAX; +} + +/* character types relevant for ucnv_compareNames() */ +enum { + UIGNORE, + ZERO, + NONZERO, + MINLETTER /* any values from here on are lowercase letter mappings */ +}; + +/* character types for ASCII 00..7F */ +static const uint8_t asciiTypes[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, 0, 0, 0, 0, 0, 0, + 0, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0, 0, 0, 0, 0, + 0, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0, 0, 0, 0, 0 +}; + +#define GET_ASCII_TYPE(c) ((int8_t)(c) >= 0 ? asciiTypes[(uint8_t)c] : (uint8_t)UIGNORE) + +/* character types for EBCDIC 80..FF */ +static const uint8_t ebcdicTypes[128] = { + 0, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0, 0, 0, 0, 0, 0, + 0, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0, 0, 0, 0, 0, 0, + 0, 0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0, 0, 0, 0, 0, 0, + 0, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0, 0, 0, 0, 0, 0, + 0, 0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0, 0, 0, 0, 0, 0, + ZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, NONZERO, 0, 0, 0, 0, 0, 0 +}; + +#define GET_EBCDIC_TYPE(c) ((int8_t)(c) < 0 ? ebcdicTypes[(c)&0x7f] : (uint8_t)UIGNORE) + +#if U_CHARSET_FAMILY==U_ASCII_FAMILY +# define GET_CHAR_TYPE(c) GET_ASCII_TYPE(c) +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY +# define GET_CHAR_TYPE(c) GET_EBCDIC_TYPE(c) +#else +# error U_CHARSET_FAMILY is not valid +#endif + + +/* @see ucnv_compareNames */ +U_CAPI char * U_CALLCONV +ucnv_io_stripASCIIForCompare(char *dst, const char *name) { + char *dstItr = dst; + uint8_t type, nextType; + char c1; + UBool afterDigit = false; + + while ((c1 = *name++) != 0) { + type = GET_ASCII_TYPE(c1); + switch (type) { + case UIGNORE: + afterDigit = false; + continue; /* ignore all but letters and digits */ + case ZERO: + if (!afterDigit) { + nextType = GET_ASCII_TYPE(*name); + if (nextType == ZERO || nextType == NONZERO) { + continue; /* ignore leading zero before another digit */ + } + } + break; + case NONZERO: + afterDigit = true; + break; + default: + c1 = (char)type; /* lowercased letter */ + afterDigit = false; + break; + } + *dstItr++ = c1; + } + *dstItr = 0; + return dst; +} + +U_CAPI char * U_CALLCONV +ucnv_io_stripEBCDICForCompare(char *dst, const char *name) { + char *dstItr = dst; + uint8_t type, nextType; + char c1; + UBool afterDigit = false; + + while ((c1 = *name++) != 0) { + type = GET_EBCDIC_TYPE(c1); + switch (type) { + case UIGNORE: + afterDigit = false; + continue; /* ignore all but letters and digits */ + case ZERO: + if (!afterDigit) { + nextType = GET_EBCDIC_TYPE(*name); + if (nextType == ZERO || nextType == NONZERO) { + continue; /* ignore leading zero before another digit */ + } + } + break; + case NONZERO: + afterDigit = true; + break; + default: + c1 = (char)type; /* lowercased letter */ + afterDigit = false; + break; + } + *dstItr++ = c1; + } + *dstItr = 0; + return dst; +} + +/** + * Do a fuzzy compare of two converter/alias names. + * The comparison is case-insensitive, ignores leading zeroes if they are not + * followed by further digits, and ignores all but letters and digits. + * Thus the strings "UTF-8", "utf_8", "u*T@f08" and "Utf 8" are exactly equivalent. + * See section 1.4, Charset Alias Matching in Unicode Technical Standard #22 + * at http://www.unicode.org/reports/tr22/ + * + * This is a symmetrical (commutative) operation; order of arguments + * is insignificant. This is an important property for sorting the + * list (when the list is preprocessed into binary form) and for + * performing binary searches on it at run time. + * + * @param name1 a converter name or alias, zero-terminated + * @param name2 a converter name or alias, zero-terminated + * @return 0 if the names match, or a negative value if the name1 + * lexically precedes name2, or a positive value if the name1 + * lexically follows name2. + * + * @see ucnv_io_stripForCompare + */ +U_CAPI int U_EXPORT2 +ucnv_compareNames(const char *name1, const char *name2) { + int rc; + uint8_t type, nextType; + char c1, c2; + UBool afterDigit1 = false, afterDigit2 = false; + + for (;;) { + while ((c1 = *name1++) != 0) { + type = GET_CHAR_TYPE(c1); + switch (type) { + case UIGNORE: + afterDigit1 = false; + continue; /* ignore all but letters and digits */ + case ZERO: + if (!afterDigit1) { + nextType = GET_CHAR_TYPE(*name1); + if (nextType == ZERO || nextType == NONZERO) { + continue; /* ignore leading zero before another digit */ + } + } + break; + case NONZERO: + afterDigit1 = true; + break; + default: + c1 = (char)type; /* lowercased letter */ + afterDigit1 = false; + break; + } + break; /* deliver c1 */ + } + while ((c2 = *name2++) != 0) { + type = GET_CHAR_TYPE(c2); + switch (type) { + case UIGNORE: + afterDigit2 = false; + continue; /* ignore all but letters and digits */ + case ZERO: + if (!afterDigit2) { + nextType = GET_CHAR_TYPE(*name2); + if (nextType == ZERO || nextType == NONZERO) { + continue; /* ignore leading zero before another digit */ + } + } + break; + case NONZERO: + afterDigit2 = true; + break; + default: + c2 = (char)type; /* lowercased letter */ + afterDigit2 = false; + break; + } + break; /* deliver c2 */ + } + + /* If we reach the ends of both strings then they match */ + if ((c1|c2)==0) { + return 0; + } + + /* Case-insensitive comparison */ + rc = (int)(unsigned char)c1 - (int)(unsigned char)c2; + if (rc != 0) { + return rc; + } + } +} + +/* + * search for an alias + * return the converter number index for gConverterList + */ +static inline uint32_t +findConverter(const char *alias, UBool *containsOption, UErrorCode *pErrorCode) { + uint32_t mid, start, limit; + uint32_t lastMid; + int result; + int isUnnormalized = (gMainTable.optionTable->stringNormalizationType == UCNV_IO_UNNORMALIZED); + char strippedName[UCNV_MAX_CONVERTER_NAME_LENGTH]; + + if (!isUnnormalized) { + if (uprv_strlen(alias) >= UCNV_MAX_CONVERTER_NAME_LENGTH) { + *pErrorCode = U_BUFFER_OVERFLOW_ERROR; + return UINT32_MAX; + } + + /* Lower case and remove ignoreable characters. */ + ucnv_io_stripForCompare(strippedName, alias); + alias = strippedName; + } + + /* do a binary search for the alias */ + start = 0; + limit = gMainTable.untaggedConvArraySize; + mid = limit; + lastMid = UINT32_MAX; + + for (;;) { + mid = (uint32_t)((start + limit) / 2); + if (lastMid == mid) { /* Have we moved? */ + break; /* We haven't moved, and it wasn't found. */ + } + lastMid = mid; + if (isUnnormalized) { + result = ucnv_compareNames(alias, GET_STRING(gMainTable.aliasList[mid])); + } + else { + result = uprv_strcmp(alias, GET_NORMALIZED_STRING(gMainTable.aliasList[mid])); + } + + if (result < 0) { + limit = mid; + } else if (result > 0) { + start = mid; + } else { + /* Since the gencnval tool folds duplicates into one entry, + * this alias in gAliasList is unique, but different standards + * may map an alias to different converters. + */ + if (gMainTable.untaggedConvArray[mid] & UCNV_AMBIGUOUS_ALIAS_MAP_BIT) { + *pErrorCode = U_AMBIGUOUS_ALIAS_WARNING; + } + /* State whether the canonical converter name contains an option. + This information is contained in this list in order to maintain backward & forward compatibility. */ + if (containsOption) { + UBool containsCnvOptionInfo = (UBool)gMainTable.optionTable->containsCnvOptionInfo; + *containsOption = (UBool)((containsCnvOptionInfo + && ((gMainTable.untaggedConvArray[mid] & UCNV_CONTAINS_OPTION_BIT) != 0)) + || !containsCnvOptionInfo); + } + return gMainTable.untaggedConvArray[mid] & UCNV_CONVERTER_INDEX_MASK; + } + } + + return UINT32_MAX; +} + +/* + * Is this alias in this list? + * alias and listOffset should be non-nullptr. + */ +static inline UBool +isAliasInList(const char *alias, uint32_t listOffset) { + if (listOffset) { + uint32_t currAlias; + uint32_t listCount = gMainTable.taggedAliasLists[listOffset]; + /* +1 to skip listCount */ + const uint16_t *currList = gMainTable.taggedAliasLists + listOffset + 1; + for (currAlias = 0; currAlias < listCount; currAlias++) { + if (currList[currAlias] + && ucnv_compareNames(alias, GET_STRING(currList[currAlias]))==0) + { + return true; + } + } + } + return false; +} + +/* + * Search for an standard name of an alias (what is the default name + * that this standard uses?) + * return the listOffset for gTaggedAliasLists. If it's 0, + * the it couldn't be found, but the parameters are valid. + */ +static uint32_t +findTaggedAliasListsOffset(const char *alias, const char *standard, UErrorCode *pErrorCode) { + uint32_t idx; + uint32_t listOffset; + uint32_t convNum; + UErrorCode myErr = U_ZERO_ERROR; + uint32_t tagNum = getTagNumber(standard); + + /* Make a quick guess. Hopefully they used a TR22 canonical alias. */ + convNum = findConverter(alias, nullptr, &myErr); + if (myErr != U_ZERO_ERROR) { + *pErrorCode = myErr; + } + + if (tagNum < (gMainTable.tagListSize - UCNV_NUM_HIDDEN_TAGS) && convNum < gMainTable.converterListSize) { + listOffset = gMainTable.taggedAliasArray[tagNum*gMainTable.converterListSize + convNum]; + if (listOffset && gMainTable.taggedAliasLists[listOffset + 1]) { + return listOffset; + } + if (myErr == U_AMBIGUOUS_ALIAS_WARNING) { + /* Uh Oh! They used an ambiguous alias. + We have to search the whole swiss cheese starting + at the highest standard affinity. + This may take a while. + */ + for (idx = 0; idx < gMainTable.taggedAliasArraySize; idx++) { + listOffset = gMainTable.taggedAliasArray[idx]; + if (listOffset && isAliasInList(alias, listOffset)) { + uint32_t currTagNum = idx/gMainTable.converterListSize; + uint32_t currConvNum = (idx - currTagNum*gMainTable.converterListSize); + uint32_t tempListOffset = gMainTable.taggedAliasArray[tagNum*gMainTable.converterListSize + currConvNum]; + if (tempListOffset && gMainTable.taggedAliasLists[tempListOffset + 1]) { + return tempListOffset; + } + /* else keep on looking */ + /* We could speed this up by starting on the next row + because an alias is unique per row, right now. + This would change if alias versioning appears. */ + } + } + /* The standard doesn't know about the alias */ + } + /* else no default name */ + return 0; + } + /* else converter or tag not found */ + + return UINT32_MAX; +} + +/* Return the canonical name */ +static uint32_t +findTaggedConverterNum(const char *alias, const char *standard, UErrorCode *pErrorCode) { + uint32_t idx; + uint32_t listOffset; + uint32_t convNum; + UErrorCode myErr = U_ZERO_ERROR; + uint32_t tagNum = getTagNumber(standard); + + /* Make a quick guess. Hopefully they used a TR22 canonical alias. */ + convNum = findConverter(alias, nullptr, &myErr); + if (myErr != U_ZERO_ERROR) { + *pErrorCode = myErr; + } + + if (tagNum < (gMainTable.tagListSize - UCNV_NUM_HIDDEN_TAGS) && convNum < gMainTable.converterListSize) { + listOffset = gMainTable.taggedAliasArray[tagNum*gMainTable.converterListSize + convNum]; + if (listOffset && isAliasInList(alias, listOffset)) { + return convNum; + } + if (myErr == U_AMBIGUOUS_ALIAS_WARNING) { + /* Uh Oh! They used an ambiguous alias. + We have to search one slice of the swiss cheese. + We search only in the requested tag, not the whole thing. + This may take a while. + */ + uint32_t convStart = (tagNum)*gMainTable.converterListSize; + uint32_t convLimit = (tagNum+1)*gMainTable.converterListSize; + for (idx = convStart; idx < convLimit; idx++) { + listOffset = gMainTable.taggedAliasArray[idx]; + if (listOffset && isAliasInList(alias, listOffset)) { + return idx-convStart; + } + } + /* The standard doesn't know about the alias */ + } + /* else no canonical name */ + } + /* else converter or tag not found */ + + return UINT32_MAX; +} + +U_CAPI const char * +ucnv_io_getConverterName(const char *alias, UBool *containsOption, UErrorCode *pErrorCode) { + const char *aliasTmp = alias; + int32_t i = 0; + for (i = 0; i < 2; i++) { + if (i == 1) { + /* + * After the first unsuccess converter lookup, check to see if + * the name begins with 'x-'. If it does, strip it off and try + * again. This behaviour is similar to how ICU4J does it. + */ + if (aliasTmp[0] == 'x' && aliasTmp[1] == '-') { + aliasTmp = aliasTmp+2; + } else { + break; + } + } + if(haveAliasData(pErrorCode) && isAlias(aliasTmp, pErrorCode)) { + uint32_t convNum = findConverter(aliasTmp, containsOption, pErrorCode); + if (convNum < gMainTable.converterListSize) { + return GET_STRING(gMainTable.converterList[convNum]); + } + /* else converter not found */ + } else { + break; + } + } + + return nullptr; +} + +U_CDECL_BEGIN + + +static int32_t U_CALLCONV +ucnv_io_countStandardAliases(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) { + int32_t value = 0; + UAliasContext *myContext = (UAliasContext *)(enumerator->context); + uint32_t listOffset = myContext->listOffset; + + if (listOffset) { + value = gMainTable.taggedAliasLists[listOffset]; + } + return value; +} + +static const char * U_CALLCONV +ucnv_io_nextStandardAliases(UEnumeration *enumerator, + int32_t* resultLength, + UErrorCode * /*pErrorCode*/) +{ + UAliasContext *myContext = (UAliasContext *)(enumerator->context); + uint32_t listOffset = myContext->listOffset; + + if (listOffset) { + uint32_t listCount = gMainTable.taggedAliasLists[listOffset]; + const uint16_t *currList = gMainTable.taggedAliasLists + listOffset + 1; + + if (myContext->listIdx < listCount) { + const char *myStr = GET_STRING(currList[myContext->listIdx++]); + if (resultLength) { + *resultLength = (int32_t)uprv_strlen(myStr); + } + return myStr; + } + } + /* Either we accessed a zero length list, or we enumerated too far. */ + if (resultLength) { + *resultLength = 0; + } + return nullptr; +} + +static void U_CALLCONV +ucnv_io_resetStandardAliases(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) { + ((UAliasContext *)(enumerator->context))->listIdx = 0; +} + +static void U_CALLCONV +ucnv_io_closeUEnumeration(UEnumeration *enumerator) { + uprv_free(enumerator->context); + uprv_free(enumerator); +} + +U_CDECL_END + +/* Enumerate the aliases for the specified converter and standard tag */ +static const UEnumeration gEnumAliases = { + nullptr, + nullptr, + ucnv_io_closeUEnumeration, + ucnv_io_countStandardAliases, + uenum_unextDefault, + ucnv_io_nextStandardAliases, + ucnv_io_resetStandardAliases +}; + +U_CAPI UEnumeration * U_EXPORT2 +ucnv_openStandardNames(const char *convName, + const char *standard, + UErrorCode *pErrorCode) +{ + UEnumeration *myEnum = nullptr; + if (haveAliasData(pErrorCode) && isAlias(convName, pErrorCode)) { + uint32_t listOffset = findTaggedAliasListsOffset(convName, standard, pErrorCode); + + /* When listOffset == 0, we want to acknowledge that the + converter name and standard are okay, but there + is nothing to enumerate. */ + if (listOffset < gMainTable.taggedAliasListsSize) { + UAliasContext *myContext; + + myEnum = static_cast(uprv_malloc(sizeof(UEnumeration))); + if (myEnum == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memcpy(myEnum, &gEnumAliases, sizeof(UEnumeration)); + myContext = static_cast(uprv_malloc(sizeof(UAliasContext))); + if (myContext == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + uprv_free(myEnum); + return nullptr; + } + myContext->listOffset = listOffset; + myContext->listIdx = 0; + myEnum->context = myContext; + } + /* else converter or tag not found */ + } + return myEnum; +} + +static uint16_t +ucnv_io_countAliases(const char *alias, UErrorCode *pErrorCode) { + if(haveAliasData(pErrorCode) && isAlias(alias, pErrorCode)) { + uint32_t convNum = findConverter(alias, nullptr, pErrorCode); + if (convNum < gMainTable.converterListSize) { + /* tagListNum - 1 is the ALL tag */ + int32_t listOffset = gMainTable.taggedAliasArray[(gMainTable.tagListSize - 1)*gMainTable.converterListSize + convNum]; + + if (listOffset) { + return gMainTable.taggedAliasLists[listOffset]; + } + /* else this shouldn't happen. internal program error */ + } + /* else converter not found */ + } + return 0; +} + +static uint16_t +ucnv_io_getAliases(const char *alias, uint16_t start, const char **aliases, UErrorCode *pErrorCode) { + if(haveAliasData(pErrorCode) && isAlias(alias, pErrorCode)) { + uint32_t currAlias; + uint32_t convNum = findConverter(alias, nullptr, pErrorCode); + if (convNum < gMainTable.converterListSize) { + /* tagListNum - 1 is the ALL tag */ + int32_t listOffset = gMainTable.taggedAliasArray[(gMainTable.tagListSize - 1)*gMainTable.converterListSize + convNum]; + + if (listOffset) { + uint32_t listCount = gMainTable.taggedAliasLists[listOffset]; + /* +1 to skip listCount */ + const uint16_t *currList = gMainTable.taggedAliasLists + listOffset + 1; + + for (currAlias = start; currAlias < listCount; currAlias++) { + aliases[currAlias] = GET_STRING(currList[currAlias]); + } + } + /* else this shouldn't happen. internal program error */ + } + /* else converter not found */ + } + return 0; +} + +static const char * +ucnv_io_getAlias(const char *alias, uint16_t n, UErrorCode *pErrorCode) { + if(haveAliasData(pErrorCode) && isAlias(alias, pErrorCode)) { + uint32_t convNum = findConverter(alias, nullptr, pErrorCode); + if (convNum < gMainTable.converterListSize) { + /* tagListNum - 1 is the ALL tag */ + int32_t listOffset = gMainTable.taggedAliasArray[(gMainTable.tagListSize - 1)*gMainTable.converterListSize + convNum]; + + if (listOffset) { + uint32_t listCount = gMainTable.taggedAliasLists[listOffset]; + /* +1 to skip listCount */ + const uint16_t *currList = gMainTable.taggedAliasLists + listOffset + 1; + + if (n < listCount) { + return GET_STRING(currList[n]); + } + *pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR; + } + /* else this shouldn't happen. internal program error */ + } + /* else converter not found */ + } + return nullptr; +} + +static uint16_t +ucnv_io_countStandards(UErrorCode *pErrorCode) { + if (haveAliasData(pErrorCode)) { + /* Don't include the empty list */ + return (uint16_t)(gMainTable.tagListSize - UCNV_NUM_HIDDEN_TAGS); + } + + return 0; +} + +U_CAPI const char * U_EXPORT2 +ucnv_getStandard(uint16_t n, UErrorCode *pErrorCode) { + if (haveAliasData(pErrorCode)) { + if (n < gMainTable.tagListSize - UCNV_NUM_HIDDEN_TAGS) { + return GET_STRING(gMainTable.tagList[n]); + } + *pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR; + } + + return nullptr; +} + +U_CAPI const char * U_EXPORT2 +ucnv_getStandardName(const char *alias, const char *standard, UErrorCode *pErrorCode) { + if (haveAliasData(pErrorCode) && isAlias(alias, pErrorCode)) { + uint32_t listOffset = findTaggedAliasListsOffset(alias, standard, pErrorCode); + + if (0 < listOffset && listOffset < gMainTable.taggedAliasListsSize) { + const uint16_t *currList = gMainTable.taggedAliasLists + listOffset + 1; + + /* Get the preferred name from this list */ + if (currList[0]) { + return GET_STRING(currList[0]); + } + /* else someone screwed up the alias table. */ + /* *pErrorCode = U_INVALID_FORMAT_ERROR */ + } + } + + return nullptr; +} + +U_CAPI uint16_t U_EXPORT2 +ucnv_countAliases(const char *alias, UErrorCode *pErrorCode) +{ + return ucnv_io_countAliases(alias, pErrorCode); +} + + +U_CAPI const char* U_EXPORT2 +ucnv_getAlias(const char *alias, uint16_t n, UErrorCode *pErrorCode) +{ + return ucnv_io_getAlias(alias, n, pErrorCode); +} + +U_CAPI void U_EXPORT2 +ucnv_getAliases(const char *alias, const char **aliases, UErrorCode *pErrorCode) +{ + ucnv_io_getAliases(alias, 0, aliases, pErrorCode); +} + +U_CAPI uint16_t U_EXPORT2 +ucnv_countStandards() +{ + UErrorCode err = U_ZERO_ERROR; + return ucnv_io_countStandards(&err); +} + +U_CAPI const char * U_EXPORT2 +ucnv_getCanonicalName(const char *alias, const char *standard, UErrorCode *pErrorCode) { + if (haveAliasData(pErrorCode) && isAlias(alias, pErrorCode)) { + uint32_t convNum = findTaggedConverterNum(alias, standard, pErrorCode); + + if (convNum < gMainTable.converterListSize) { + return GET_STRING(gMainTable.converterList[convNum]); + } + } + + return nullptr; +} + +U_CDECL_BEGIN + + +static int32_t U_CALLCONV +ucnv_io_countAllConverters(UEnumeration * /*enumerator*/, UErrorCode * /*pErrorCode*/) { + return gMainTable.converterListSize; +} + +static const char * U_CALLCONV +ucnv_io_nextAllConverters(UEnumeration *enumerator, + int32_t* resultLength, + UErrorCode * /*pErrorCode*/) +{ + uint16_t *myContext = (uint16_t *)(enumerator->context); + + if (*myContext < gMainTable.converterListSize) { + const char *myStr = GET_STRING(gMainTable.converterList[(*myContext)++]); + if (resultLength) { + *resultLength = (int32_t)uprv_strlen(myStr); + } + return myStr; + } + /* Either we accessed a zero length list, or we enumerated too far. */ + if (resultLength) { + *resultLength = 0; + } + return nullptr; +} + +static void U_CALLCONV +ucnv_io_resetAllConverters(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) { + *((uint16_t *)(enumerator->context)) = 0; +} +U_CDECL_END +static const UEnumeration gEnumAllConverters = { + nullptr, + nullptr, + ucnv_io_closeUEnumeration, + ucnv_io_countAllConverters, + uenum_unextDefault, + ucnv_io_nextAllConverters, + ucnv_io_resetAllConverters +}; + +U_CAPI UEnumeration * U_EXPORT2 +ucnv_openAllNames(UErrorCode *pErrorCode) { + UEnumeration *myEnum = nullptr; + if (haveAliasData(pErrorCode)) { + uint16_t *myContext; + + myEnum = static_cast(uprv_malloc(sizeof(UEnumeration))); + if (myEnum == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memcpy(myEnum, &gEnumAllConverters, sizeof(UEnumeration)); + myContext = static_cast(uprv_malloc(sizeof(uint16_t))); + if (myContext == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + uprv_free(myEnum); + return nullptr; + } + *myContext = 0; + myEnum->context = myContext; + } + return myEnum; +} + +U_CAPI uint16_t +ucnv_io_countKnownConverters(UErrorCode *pErrorCode) { + if (haveAliasData(pErrorCode)) { + return (uint16_t)gMainTable.converterListSize; + } + return 0; +} + +/* alias table swapping ----------------------------------------------------- */ + +U_CDECL_BEGIN + +typedef char * U_CALLCONV StripForCompareFn(char *dst, const char *name); +U_CDECL_END + + +/* + * row of a temporary array + * + * gets platform-endian charset string indexes and sorting indexes; + * after sorting this array by strings, the actual arrays are permutated + * according to the sorting indexes + */ +typedef struct TempRow { + uint16_t strIndex, sortIndex; +} TempRow; + +typedef struct TempAliasTable { + const char *chars; + TempRow *rows; + uint16_t *resort; + StripForCompareFn *stripForCompare; +} TempAliasTable; + +enum { + STACK_ROW_CAPACITY=500 +}; + +static int32_t U_CALLCONV +io_compareRows(const void *context, const void *left, const void *right) { + char strippedLeft[UCNV_MAX_CONVERTER_NAME_LENGTH], + strippedRight[UCNV_MAX_CONVERTER_NAME_LENGTH]; + + TempAliasTable *tempTable=(TempAliasTable *)context; + const char *chars=tempTable->chars; + + return (int32_t)uprv_strcmp(tempTable->stripForCompare(strippedLeft, chars+2*((const TempRow *)left)->strIndex), + tempTable->stripForCompare(strippedRight, chars+2*((const TempRow *)right)->strIndex)); +} + +U_CAPI int32_t U_EXPORT2 +ucnv_swapAliases(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const UDataInfo *pInfo; + int32_t headerSize; + + const uint16_t *inTable; + const uint32_t *inSectionSizes; + uint32_t toc[offsetsCount]; + uint32_t offsets[offsetsCount]; /* 16-bit-addressed offsets from inTable/outTable */ + uint32_t i, count, tocLength, topOffset; + + TempRow rows[STACK_ROW_CAPACITY]; + uint16_t resort[STACK_ROW_CAPACITY]; + TempAliasTable tempTable; + + /* udata_swapDataHeader checks the arguments */ + headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + /* check data format and format version */ + pInfo=(const UDataInfo *)((const char *)inData+4); + if(!( + pInfo->dataFormat[0]==0x43 && /* dataFormat="CvAl" */ + pInfo->dataFormat[1]==0x76 && + pInfo->dataFormat[2]==0x41 && + pInfo->dataFormat[3]==0x6c && + pInfo->formatVersion[0]==3 + )) { + udata_printError(ds, "ucnv_swapAliases(): data format %02x.%02x.%02x.%02x (format version %02x) is not an alias table\n", + pInfo->dataFormat[0], pInfo->dataFormat[1], + pInfo->dataFormat[2], pInfo->dataFormat[3], + pInfo->formatVersion[0]); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + /* an alias table must contain at least the table of contents array */ + if(length>=0 && (length-headerSize)<4*(1+minTocLength)) { + udata_printError(ds, "ucnv_swapAliases(): too few bytes (%d after header) for an alias table\n", + length-headerSize); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + inSectionSizes=(const uint32_t *)((const char *)inData+headerSize); + inTable=(const uint16_t *)inSectionSizes; + uprv_memset(toc, 0, sizeof(toc)); + toc[tocLengthIndex]=tocLength=ds->readUInt32(inSectionSizes[tocLengthIndex]); + if(tocLengthreadUInt32(inSectionSizes[i]); + } + + /* compute offsets */ + uprv_memset(offsets, 0, sizeof(offsets)); + offsets[converterListIndex]=2*(1+tocLength); /* count two 16-bit units per toc entry */ + for(i=tagListIndex; i<=tocLength; ++i) { + offsets[i]=offsets[i-1]+toc[i-1]; + } + + /* compute the overall size of the after-header data, in numbers of 16-bit units */ + topOffset=offsets[i-1]+toc[i-1]; + + if(length>=0) { + uint16_t *outTable; + const uint16_t *p, *p2; + uint16_t *q, *q2; + uint16_t oldIndex; + + if((length-headerSize)<(2*(int32_t)topOffset)) { + udata_printError(ds, "ucnv_swapAliases(): too few bytes (%d after header) for an alias table\n", + length-headerSize); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + outTable=(uint16_t *)((char *)outData+headerSize); + + /* swap the entire table of contents */ + ds->swapArray32(ds, inTable, 4*(1+tocLength), outTable, pErrorCode); + + /* swap unormalized strings & normalized strings */ + ds->swapInvChars(ds, inTable+offsets[stringTableIndex], 2*(int32_t)(toc[stringTableIndex]+toc[normalizedStringTableIndex]), + outTable+offsets[stringTableIndex], pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ucnv_swapAliases().swapInvChars(charset names) failed\n"); + return 0; + } + + if(ds->inCharset==ds->outCharset) { + /* no need to sort, just swap all 16-bit values together */ + ds->swapArray16(ds, + inTable+offsets[converterListIndex], + 2*(int32_t)(offsets[stringTableIndex]-offsets[converterListIndex]), + outTable+offsets[converterListIndex], + pErrorCode); + } else { + /* allocate the temporary table for sorting */ + count=toc[aliasListIndex]; + + tempTable.chars=(const char *)(outTable+offsets[stringTableIndex]); /* sort by outCharset */ + + if(count<=STACK_ROW_CAPACITY) { + tempTable.rows=rows; + tempTable.resort=resort; + } else { + tempTable.rows=(TempRow *)uprv_malloc(count*sizeof(TempRow)+count*2); + if(tempTable.rows==nullptr) { + udata_printError(ds, "ucnv_swapAliases(): unable to allocate memory for sorting tables (max length: %u)\n", + count); + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return 0; + } + tempTable.resort=(uint16_t *)(tempTable.rows+count); + } + + if(ds->outCharset==U_ASCII_FAMILY) { + tempTable.stripForCompare=ucnv_io_stripASCIIForCompare; + } else /* U_EBCDIC_FAMILY */ { + tempTable.stripForCompare=ucnv_io_stripEBCDICForCompare; + } + + /* + * Sort unique aliases+mapped names. + * + * We need to sort the list again by outCharset strings because they + * sort differently for different charset families. + * First we set up a temporary table with the string indexes and + * sorting indexes and sort that. + * Then we permutate and copy/swap the actual values. + */ + p=inTable+offsets[aliasListIndex]; + q=outTable+offsets[aliasListIndex]; + + p2=inTable+offsets[untaggedConvArrayIndex]; + q2=outTable+offsets[untaggedConvArrayIndex]; + + for(i=0; ireadUInt16(p[i]); + tempTable.rows[i].sortIndex=(uint16_t)i; + } + + uprv_sortArray(tempTable.rows, (int32_t)count, sizeof(TempRow), + io_compareRows, &tempTable, + false, pErrorCode); + + if(U_SUCCESS(*pErrorCode)) { + /* copy/swap/permutate items */ + if(p!=q) { + for(i=0; iswapArray16(ds, p+oldIndex, 2, q+i, pErrorCode); + ds->swapArray16(ds, p2+oldIndex, 2, q2+i, pErrorCode); + } + } else { + /* + * If we swap in-place, then the permutation must use another + * temporary array (tempTable.resort) + * before the results are copied to the outBundle. + */ + uint16_t *r=tempTable.resort; + + for(i=0; iswapArray16(ds, p+oldIndex, 2, r+i, pErrorCode); + } + uprv_memcpy(q, r, 2*(size_t)count); + + for(i=0; iswapArray16(ds, p2+oldIndex, 2, r+i, pErrorCode); + } + uprv_memcpy(q2, r, 2*(size_t)count); + } + } + + if(tempTable.rows!=rows) { + uprv_free(tempTable.rows); + } + + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ucnv_swapAliases().uprv_sortArray(%u items) failed\n", + count); + return 0; + } + + /* swap remaining 16-bit values */ + ds->swapArray16(ds, + inTable+offsets[converterListIndex], + 2*(int32_t)(offsets[aliasListIndex]-offsets[converterListIndex]), + outTable+offsets[converterListIndex], + pErrorCode); + ds->swapArray16(ds, + inTable+offsets[taggedAliasArrayIndex], + 2*(int32_t)(offsets[stringTableIndex]-offsets[taggedAliasArrayIndex]), + outTable+offsets[taggedAliasArrayIndex], + pErrorCode); + } + } + + return headerSize+2*(int32_t)topOffset; +} + +#endif + + +/* + * Hey, Emacs, please set the following: + * + * Local Variables: + * indent-tabs-mode: nil + * End: + * + */ diff --git a/deps/icu-small/source/common/ucnv_io.h b/deps/icu-small/source/common/ucnv_io.h index 8f2d7b5a02bbfa..06d9f283bf3543 100644 --- a/deps/icu-small/source/common/ucnv_io.h +++ b/deps/icu-small/source/common/ucnv_io.h @@ -1,127 +1,127 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 1999-2006, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - * - * - * ucnv_io.h: - * defines variables and functions pertaining to converter name resolution - * aspect of the conversion code - */ - -#ifndef UCNV_IO_H -#define UCNV_IO_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "udataswp.h" - -#define UCNV_AMBIGUOUS_ALIAS_MAP_BIT 0x8000 -#define UCNV_CONTAINS_OPTION_BIT 0x4000 -#define UCNV_CONVERTER_INDEX_MASK 0xFFF -#define UCNV_NUM_RESERVED_TAGS 2 -#define UCNV_NUM_HIDDEN_TAGS 1 - -enum { - UCNV_IO_UNNORMALIZED, - UCNV_IO_STD_NORMALIZED, - UCNV_IO_NORM_TYPE_COUNT -}; - -typedef struct { - uint16_t stringNormalizationType; - uint16_t containsCnvOptionInfo; -} UConverterAliasOptions; - -typedef struct UConverterAlias { - const uint16_t *converterList; - const uint16_t *tagList; - const uint16_t *aliasList; - const uint16_t *untaggedConvArray; - const uint16_t *taggedAliasArray; - const uint16_t *taggedAliasLists; - const UConverterAliasOptions *optionTable; - const uint16_t *stringTable; - const uint16_t *normalizedStringTable; - - uint32_t converterListSize; - uint32_t tagListSize; - uint32_t aliasListSize; - uint32_t untaggedConvArraySize; - uint32_t taggedAliasArraySize; - uint32_t taggedAliasListsSize; - uint32_t optionTableSize; - uint32_t stringTableSize; - uint32_t normalizedStringTableSize; -} UConverterAlias; - -/** - * \var ucnv_io_stripForCompare - * Remove the underscores, dashes and spaces from the name, and convert - * the name to lower case. - * @param dst The destination buffer, which is <= the buffer of name. - * @param dst The destination buffer, which is <= the buffer of name. - * @see ucnv_compareNames - * @return the destination buffer. - */ -#if U_CHARSET_FAMILY==U_ASCII_FAMILY -# define ucnv_io_stripForCompare ucnv_io_stripASCIIForCompare -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY -# define ucnv_io_stripForCompare ucnv_io_stripEBCDICForCompare -#else -# error U_CHARSET_FAMILY is not valid -#endif - -U_CAPI char * U_CALLCONV -ucnv_io_stripASCIIForCompare(char *dst, const char *name); - -U_CAPI char * U_CALLCONV -ucnv_io_stripEBCDICForCompare(char *dst, const char *name); - -/** - * Map a converter alias name to a canonical converter name. - * The alias is searched for case-insensitively, the converter name - * is returned in mixed-case. - * Returns NULL if the alias is not found. - * @param alias The alias name to be searched. - * @param containsOption A return value stating whether the returned converter name contains an option (a comma) - * @param pErrorCode The error code - * @return the converter name in mixed-case, return NULL if the alias is not found. - */ -U_CAPI const char * -ucnv_io_getConverterName(const char *alias, UBool *containsOption, UErrorCode *pErrorCode); - -/** - * Return the number of all known converter names (no aliases). - * @param pErrorCode The error code - * @return the number of all aliases - */ -U_CAPI uint16_t -ucnv_io_countKnownConverters(UErrorCode *pErrorCode); - -/** - * Swap an ICU converter alias table. See implementation for details. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -ucnv_swapAliases(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -#endif - -#endif /* _UCNV_IO */ - -/* - * Hey, Emacs, please set the following: - * - * Local Variables: - * indent-tabs-mode: nil - * End: - * - */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 1999-2006, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + * + * + * ucnv_io.h: + * defines variables and functions pertaining to converter name resolution + * aspect of the conversion code + */ + +#ifndef UCNV_IO_H +#define UCNV_IO_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "udataswp.h" + +#define UCNV_AMBIGUOUS_ALIAS_MAP_BIT 0x8000 +#define UCNV_CONTAINS_OPTION_BIT 0x4000 +#define UCNV_CONVERTER_INDEX_MASK 0xFFF +#define UCNV_NUM_RESERVED_TAGS 2 +#define UCNV_NUM_HIDDEN_TAGS 1 + +enum { + UCNV_IO_UNNORMALIZED, + UCNV_IO_STD_NORMALIZED, + UCNV_IO_NORM_TYPE_COUNT +}; + +typedef struct { + uint16_t stringNormalizationType; + uint16_t containsCnvOptionInfo; +} UConverterAliasOptions; + +typedef struct UConverterAlias { + const uint16_t *converterList; + const uint16_t *tagList; + const uint16_t *aliasList; + const uint16_t *untaggedConvArray; + const uint16_t *taggedAliasArray; + const uint16_t *taggedAliasLists; + const UConverterAliasOptions *optionTable; + const uint16_t *stringTable; + const uint16_t *normalizedStringTable; + + uint32_t converterListSize; + uint32_t tagListSize; + uint32_t aliasListSize; + uint32_t untaggedConvArraySize; + uint32_t taggedAliasArraySize; + uint32_t taggedAliasListsSize; + uint32_t optionTableSize; + uint32_t stringTableSize; + uint32_t normalizedStringTableSize; +} UConverterAlias; + +/** + * \var ucnv_io_stripForCompare + * Remove the underscores, dashes and spaces from the name, and convert + * the name to lower case. + * @param dst The destination buffer, which is <= the buffer of name. + * @param dst The destination buffer, which is <= the buffer of name. + * @see ucnv_compareNames + * @return the destination buffer. + */ +#if U_CHARSET_FAMILY==U_ASCII_FAMILY +# define ucnv_io_stripForCompare ucnv_io_stripASCIIForCompare +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY +# define ucnv_io_stripForCompare ucnv_io_stripEBCDICForCompare +#else +# error U_CHARSET_FAMILY is not valid +#endif + +U_CAPI char * U_CALLCONV +ucnv_io_stripASCIIForCompare(char *dst, const char *name); + +U_CAPI char * U_CALLCONV +ucnv_io_stripEBCDICForCompare(char *dst, const char *name); + +/** + * Map a converter alias name to a canonical converter name. + * The alias is searched for case-insensitively, the converter name + * is returned in mixed-case. + * Returns NULL if the alias is not found. + * @param alias The alias name to be searched. + * @param containsOption A return value stating whether the returned converter name contains an option (a comma) + * @param pErrorCode The error code + * @return the converter name in mixed-case, return NULL if the alias is not found. + */ +U_CAPI const char * +ucnv_io_getConverterName(const char *alias, UBool *containsOption, UErrorCode *pErrorCode); + +/** + * Return the number of all known converter names (no aliases). + * @param pErrorCode The error code + * @return the number of all aliases + */ +U_CAPI uint16_t +ucnv_io_countKnownConverters(UErrorCode *pErrorCode); + +/** + * Swap an ICU converter alias table. See implementation for details. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +ucnv_swapAliases(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +#endif + +#endif /* _UCNV_IO */ + +/* + * Hey, Emacs, please set the following: + * + * Local Variables: + * indent-tabs-mode: nil + * End: + * + */ diff --git a/deps/icu-small/source/common/ucnv_lmb.cpp b/deps/icu-small/source/common/ucnv_lmb.cpp index 78b8e407006a1f..727bf77088bb38 100644 --- a/deps/icu-small/source/common/ucnv_lmb.cpp +++ b/deps/icu-small/source/common/ucnv_lmb.cpp @@ -1,1388 +1,1388 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2000-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: ucnv_lmb.cpp -* encoding: UTF-8 -* tab size: 4 (not used) -* indentation:4 -* -* created on: 2000feb09 -* created by: Brendan Murray -* extensively hacked up by: Jim Snyder-Grant -* -* Modification History: -* -* Date Name Description -* -* 06/20/2000 helena OS/400 port changes; mostly typecast. -* 06/27/2000 Jim Snyder-Grant Deal with partial characters and small buffers. -* Add comments to document LMBCS format and implementation -* restructured order & breakdown of functions -* 06/28/2000 helena Major rewrite for the callback API changes. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION - -#include "unicode/ucnv_err.h" -#include "unicode/ucnv.h" -#include "unicode/uset.h" -#include "cmemory.h" -#include "cstring.h" -#include "uassert.h" -#include "ucnv_imp.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" - -#ifdef EBCDIC_RTL - #include "ascii_a.h" -#endif - -/* - LMBCS - - (Lotus Multi-Byte Character Set) - - LMBCS was invented in the late 1980's and is primarily used in Lotus Notes - databases and in Lotus 1-2-3 files. Programmers who work with the APIs - into these products will sometimes need to deal with strings in this format. - - The code in this file provides an implementation for an ICU converter of - LMBCS to and from Unicode. - - Since the LMBCS character set is only sparsely documented in existing - printed or online material, we have added extensive annotation to this - file to serve as a guide to understanding LMBCS. - - LMBCS was originally designed with these four sometimes-competing design goals: - - -Provide encodings for the characters in 12 existing national standards - (plus a few other characters) - -Minimal memory footprint - -Maximal speed of conversion into the existing national character sets - -No need to track a changing state as you interpret a string. - - - All of the national character sets LMBCS was trying to encode are 'ANSI' - based, in that the bytes from 0x20 - 0x7F are almost exactly the - same common Latin unaccented characters and symbols in all character sets. - - So, in order to help meet the speed & memory design goals, the common ANSI - bytes from 0x20-0x7F are represented by the same single-byte values in LMBCS. - - The general LMBCS code unit is from 1-3 bytes. We can describe the 3 bytes as - follows: - - [G] D1 [D2] - - That is, a sometimes-optional 'group' byte, followed by 1 and sometimes 2 - data bytes. The maximum size of a LMBCS character is 3 bytes: -*/ -#define ULMBCS_CHARSIZE_MAX 3 -/* - The single-byte values from 0x20 to 0x7F are examples of single D1 bytes. - We often have to figure out if byte values are below or above this, so we - use the ANSI nomenclature 'C0' and 'C1' to refer to the range of control - characters just above & below the common lower-ANSI range */ -#define ULMBCS_C0END 0x1F -#define ULMBCS_C1START 0x80 -/* - Since LMBCS is always dealing in byte units. we create a local type here for - dealing with these units of LMBCS code units: - -*/ -typedef uint8_t ulmbcs_byte_t; - -/* - Most of the values less than 0x20 are reserved in LMBCS to announce - which national character standard is being used for the 'D' bytes. - In the comments we show the common name and the IBM character-set ID - for these character-set announcers: -*/ - -#define ULMBCS_GRP_L1 0x01 /* Latin-1 :ibm-850 */ -#define ULMBCS_GRP_GR 0x02 /* Greek :ibm-851 */ -#define ULMBCS_GRP_HE 0x03 /* Hebrew :ibm-1255 */ -#define ULMBCS_GRP_AR 0x04 /* Arabic :ibm-1256 */ -#define ULMBCS_GRP_RU 0x05 /* Cyrillic :ibm-1251 */ -#define ULMBCS_GRP_L2 0x06 /* Latin-2 :ibm-852 */ -#define ULMBCS_GRP_TR 0x08 /* Turkish :ibm-1254 */ -#define ULMBCS_GRP_TH 0x0B /* Thai :ibm-874 */ -#define ULMBCS_GRP_JA 0x10 /* Japanese :ibm-943 */ -#define ULMBCS_GRP_KO 0x11 /* Korean :ibm-1261 */ -#define ULMBCS_GRP_TW 0x12 /* Chinese SC :ibm-950 */ -#define ULMBCS_GRP_CN 0x13 /* Chinese TC :ibm-1386 */ - -/* - So, the beginning of understanding LMBCS is that IF the first byte of a LMBCS - character is one of those 12 values, you can interpret the remaining bytes of - that character as coming from one of those character sets. Since the lower - ANSI bytes already are represented in single bytes, using one of the character - set announcers is used to announce a character that starts with a byte of - 0x80 or greater. - - The character sets are arranged so that the single byte sets all appear - before the multi-byte character sets. When we need to tell whether a - group byte is for a single byte char set or not we use this define: */ - -#define ULMBCS_DOUBLEOPTGROUP_START 0x10 - -/* -However, to fully understand LMBCS, you must also understand a series of -exceptions & optimizations made in service of the design goals. - -First, those of you who are character set mavens may have noticed that -the 'double-byte' character sets are actually multi-byte character sets -that can have 1 or two bytes, even in the upper-ascii range. To force -each group byte to introduce a fixed-width encoding (to make it faster to -count characters), we use a convention of doubling up on the group byte -to introduce any single-byte character > 0x80 in an otherwise double-byte -character set. So, for example, the LMBCS sequence x10 x10 xAE is the -same as '0xAE' in the Japanese code page 943. - -Next, you will notice that the list of group bytes has some gaps. -These are used in various ways. - -We reserve a few special single byte values for common control -characters. These are in the same place as their ANSI equivalents for speed. -*/ - -#define ULMBCS_HT 0x09 /* Fixed control char - Horizontal Tab */ -#define ULMBCS_LF 0x0A /* Fixed control char - Line Feed */ -#define ULMBCS_CR 0x0D /* Fixed control char - Carriage Return */ - -/* Then, 1-2-3 reserved a special single-byte character to put at the -beginning of internal 'system' range names: */ - -#define ULMBCS_123SYSTEMRANGE 0x19 - -/* Then we needed a place to put all the other ansi control characters -that must be moved to different values because LMBCS reserves those -values for other purposes. To represent the control characters, we start -with a first byte of 0xF & add the control character value as the -second byte */ -#define ULMBCS_GRP_CTRL 0x0F - -/* For the C0 controls (less than 0x20), we add 0x20 to preserve the -useful doctrine that any byte less than 0x20 in a LMBCS char must be -the first byte of a character:*/ -#define ULMBCS_CTRLOFFSET 0x20 - -/* -Where to put the characters that aren't part of any of the 12 national -character sets? The first thing that was done, in the earlier years of -LMBCS, was to use up the spaces of the form - - [G] D1, - - where 'G' was one of the single-byte character groups, and - D1 was less than 0x80. These sequences are gathered together - into a Lotus-invented doublebyte character set to represent a - lot of stray values. Internally, in this implementation, we track this - as group '0', as a place to tuck this exceptions list.*/ - -#define ULMBCS_GRP_EXCEPT 0x00 -/* - Finally, as the durability and usefulness of UNICODE became clear, - LOTUS added a new group 0x14 to hold Unicode values not otherwise - represented in LMBCS: */ -#define ULMBCS_GRP_UNICODE 0x14 -/* The two bytes appearing after a 0x14 are interpreted as UFT-16 BE -(Big-Endian) characters. The exception comes when the UTF16 -representation would have a zero as the second byte. In that case, -'F6' is used in its place, and the bytes are swapped. (This prevents -LMBCS from encoding any Unicode values of the form U+F6xx, but that's OK: -0xF6xx is in the middle of the Private Use Area.)*/ -#define ULMBCS_UNICOMPATZERO 0xF6 - -/* It is also useful in our code to have a constant for the size of -a LMBCS char that holds a literal Unicode value */ -#define ULMBCS_UNICODE_SIZE 3 - -/* -To squish the LMBCS representations down even further, and to make -translations even faster,sometimes the optimization group byte can be dropped -from a LMBCS character. This is decided on a process-by-process basis. The -group byte that is dropped is called the 'optimization group'. - -For Notes, the optimzation group is always 0x1.*/ -#define ULMBCS_DEFAULTOPTGROUP 0x1 -/* For 1-2-3 files, the optimzation group is stored in the header of the 1-2-3 -file. - - In any case, when using ICU, you either pass in the -optimization group as part of the name of the converter (LMBCS-1, LMBCS-2, -etc.). Using plain 'LMBCS' as the name of the converter will give you -LMBCS-1. - - -*** Implementation strategy *** - - -Because of the extensive use of other character sets, the LMBCS converter -keeps a mapping between optimization groups and IBM character sets, so that -ICU converters can be created and used as needed. */ - -/* As you can see, even though any byte below 0x20 could be an optimization -byte, only those at 0x13 or below can map to an actual converter. To limit -some loops and searches, we define a value for that last group converter:*/ - -#define ULMBCS_GRP_LAST 0x13 /* last LMBCS group that has a converter */ - -static const char * const OptGroupByteToCPName[ULMBCS_GRP_LAST + 1] = { - /* 0x0000 */ "lmb-excp", /* internal home for the LOTUS exceptions list */ - /* 0x0001 */ "ibm-850", - /* 0x0002 */ "ibm-851", - /* 0x0003 */ "windows-1255", - /* 0x0004 */ "windows-1256", - /* 0x0005 */ "windows-1251", - /* 0x0006 */ "ibm-852", - /* 0x0007 */ NULL, /* Unused */ - /* 0x0008 */ "windows-1254", - /* 0x0009 */ NULL, /* Control char HT */ - /* 0x000A */ NULL, /* Control char LF */ - /* 0x000B */ "windows-874", - /* 0x000C */ NULL, /* Unused */ - /* 0x000D */ NULL, /* Control char CR */ - /* 0x000E */ NULL, /* Unused */ - /* 0x000F */ NULL, /* Control chars: 0x0F20 + C0/C1 character: algorithmic */ - /* 0x0010 */ "windows-932", - /* 0x0011 */ "windows-949", - /* 0x0012 */ "windows-950", - /* 0x0013 */ "windows-936" - - /* The rest are null, including the 0x0014 Unicode compatibility region - and 0x0019, the 1-2-3 system range control char */ -}; - - -/* That's approximately all the data that's needed for translating - LMBCS to Unicode. - - -However, to translate Unicode to LMBCS, we need some more support. - -That's because there are often more than one possible mappings from a Unicode -code point back into LMBCS. The first thing we do is look up into a table -to figure out if there are more than one possible mappings. This table, -arranged by Unicode values (including ranges) either lists which group -to use, or says that it could go into one or more of the SBCS sets, or -into one or more of the DBCS sets. (If the character exists in both DBCS & -SBCS, the table will place it in the SBCS sets, to make the LMBCS code point -length as small as possible. Here's the two special markers we use to indicate -ambiguous mappings: */ - -#define ULMBCS_AMBIGUOUS_SBCS 0x80 /* could fit in more than one - LMBCS sbcs native encoding - (example: most accented latin) */ -#define ULMBCS_AMBIGUOUS_MBCS 0x81 /* could fit in more than one - LMBCS mbcs native encoding - (example: Unihan) */ -#define ULMBCS_AMBIGUOUS_ALL 0x82 -/* And here's a simple way to see if a group falls in an appropriate range */ -#define ULMBCS_AMBIGUOUS_MATCH(agroup, xgroup) \ - ((((agroup) == ULMBCS_AMBIGUOUS_SBCS) && \ - (xgroup) < ULMBCS_DOUBLEOPTGROUP_START) || \ - (((agroup) == ULMBCS_AMBIGUOUS_MBCS) && \ - (xgroup) >= ULMBCS_DOUBLEOPTGROUP_START)) || \ - ((agroup) == ULMBCS_AMBIGUOUS_ALL) - - -/* The table & some code to use it: */ - - -static const struct _UniLMBCSGrpMap -{ - const UChar uniStartRange; - const UChar uniEndRange; - const ulmbcs_byte_t GrpType; -} UniLMBCSGrpMap[] -= -{ - - {0x0001, 0x001F, ULMBCS_GRP_CTRL}, - {0x0080, 0x009F, ULMBCS_GRP_CTRL}, - {0x00A0, 0x00A6, ULMBCS_AMBIGUOUS_SBCS}, - {0x00A7, 0x00A8, ULMBCS_AMBIGUOUS_ALL}, - {0x00A9, 0x00AF, ULMBCS_AMBIGUOUS_SBCS}, - {0x00B0, 0x00B1, ULMBCS_AMBIGUOUS_ALL}, - {0x00B2, 0x00B3, ULMBCS_AMBIGUOUS_SBCS}, - {0x00B4, 0x00B4, ULMBCS_AMBIGUOUS_ALL}, - {0x00B5, 0x00B5, ULMBCS_AMBIGUOUS_SBCS}, - {0x00B6, 0x00B6, ULMBCS_AMBIGUOUS_ALL}, - {0x00B7, 0x00D6, ULMBCS_AMBIGUOUS_SBCS}, - {0x00D7, 0x00D7, ULMBCS_AMBIGUOUS_ALL}, - {0x00D8, 0x00F6, ULMBCS_AMBIGUOUS_SBCS}, - {0x00F7, 0x00F7, ULMBCS_AMBIGUOUS_ALL}, - {0x00F8, 0x01CD, ULMBCS_AMBIGUOUS_SBCS}, - {0x01CE, 0x01CE, ULMBCS_GRP_TW }, - {0x01CF, 0x02B9, ULMBCS_AMBIGUOUS_SBCS}, - {0x02BA, 0x02BA, ULMBCS_GRP_CN}, - {0x02BC, 0x02C8, ULMBCS_AMBIGUOUS_SBCS}, - {0x02C9, 0x02D0, ULMBCS_AMBIGUOUS_MBCS}, - {0x02D8, 0x02DD, ULMBCS_AMBIGUOUS_SBCS}, - {0x0384, 0x0390, ULMBCS_AMBIGUOUS_SBCS}, - {0x0391, 0x03A9, ULMBCS_AMBIGUOUS_ALL}, - {0x03AA, 0x03B0, ULMBCS_AMBIGUOUS_SBCS}, - {0x03B1, 0x03C9, ULMBCS_AMBIGUOUS_ALL}, - {0x03CA, 0x03CE, ULMBCS_AMBIGUOUS_SBCS}, - {0x0400, 0x0400, ULMBCS_GRP_RU}, - {0x0401, 0x0401, ULMBCS_AMBIGUOUS_ALL}, - {0x0402, 0x040F, ULMBCS_GRP_RU}, - {0x0410, 0x0431, ULMBCS_AMBIGUOUS_ALL}, - {0x0432, 0x044E, ULMBCS_GRP_RU}, - {0x044F, 0x044F, ULMBCS_AMBIGUOUS_ALL}, - {0x0450, 0x0491, ULMBCS_GRP_RU}, - {0x05B0, 0x05F2, ULMBCS_GRP_HE}, - {0x060C, 0x06AF, ULMBCS_GRP_AR}, - {0x0E01, 0x0E5B, ULMBCS_GRP_TH}, - {0x200C, 0x200F, ULMBCS_AMBIGUOUS_SBCS}, - {0x2010, 0x2010, ULMBCS_AMBIGUOUS_MBCS}, - {0x2013, 0x2014, ULMBCS_AMBIGUOUS_SBCS}, - {0x2015, 0x2015, ULMBCS_AMBIGUOUS_MBCS}, - {0x2016, 0x2016, ULMBCS_AMBIGUOUS_MBCS}, - {0x2017, 0x2017, ULMBCS_AMBIGUOUS_SBCS}, - {0x2018, 0x2019, ULMBCS_AMBIGUOUS_ALL}, - {0x201A, 0x201B, ULMBCS_AMBIGUOUS_SBCS}, - {0x201C, 0x201D, ULMBCS_AMBIGUOUS_ALL}, - {0x201E, 0x201F, ULMBCS_AMBIGUOUS_SBCS}, - {0x2020, 0x2021, ULMBCS_AMBIGUOUS_ALL}, - {0x2022, 0x2024, ULMBCS_AMBIGUOUS_SBCS}, - {0x2025, 0x2025, ULMBCS_AMBIGUOUS_MBCS}, - {0x2026, 0x2026, ULMBCS_AMBIGUOUS_ALL}, - {0x2027, 0x2027, ULMBCS_GRP_TW}, - {0x2030, 0x2030, ULMBCS_AMBIGUOUS_ALL}, - {0x2031, 0x2031, ULMBCS_AMBIGUOUS_SBCS}, - {0x2032, 0x2033, ULMBCS_AMBIGUOUS_MBCS}, - {0x2035, 0x2035, ULMBCS_AMBIGUOUS_MBCS}, - {0x2039, 0x203A, ULMBCS_AMBIGUOUS_SBCS}, - {0x203B, 0x203B, ULMBCS_AMBIGUOUS_MBCS}, - {0x203C, 0x203C, ULMBCS_GRP_EXCEPT}, - {0x2074, 0x2074, ULMBCS_GRP_KO}, - {0x207F, 0x207F, ULMBCS_GRP_EXCEPT}, - {0x2081, 0x2084, ULMBCS_GRP_KO}, - {0x20A4, 0x20AC, ULMBCS_AMBIGUOUS_SBCS}, - {0x2103, 0x2109, ULMBCS_AMBIGUOUS_MBCS}, - {0x2111, 0x2120, ULMBCS_AMBIGUOUS_SBCS}, - /*zhujin: upgrade, for regressiont test, spr HKIA4YHTSU*/ - {0x2121, 0x2121, ULMBCS_AMBIGUOUS_MBCS}, - {0x2122, 0x2126, ULMBCS_AMBIGUOUS_SBCS}, - {0x212B, 0x212B, ULMBCS_AMBIGUOUS_MBCS}, - {0x2135, 0x2135, ULMBCS_AMBIGUOUS_SBCS}, - {0x2153, 0x2154, ULMBCS_GRP_KO}, - {0x215B, 0x215E, ULMBCS_GRP_EXCEPT}, - {0x2160, 0x2179, ULMBCS_AMBIGUOUS_MBCS}, - {0x2190, 0x2193, ULMBCS_AMBIGUOUS_ALL}, - {0x2194, 0x2195, ULMBCS_GRP_EXCEPT}, - {0x2196, 0x2199, ULMBCS_AMBIGUOUS_MBCS}, - {0x21A8, 0x21A8, ULMBCS_GRP_EXCEPT}, - {0x21B8, 0x21B9, ULMBCS_GRP_CN}, - {0x21D0, 0x21D1, ULMBCS_GRP_EXCEPT}, - {0x21D2, 0x21D2, ULMBCS_AMBIGUOUS_MBCS}, - {0x21D3, 0x21D3, ULMBCS_GRP_EXCEPT}, - {0x21D4, 0x21D4, ULMBCS_AMBIGUOUS_MBCS}, - {0x21D5, 0x21D5, ULMBCS_GRP_EXCEPT}, - {0x21E7, 0x21E7, ULMBCS_GRP_CN}, - {0x2200, 0x2200, ULMBCS_AMBIGUOUS_MBCS}, - {0x2201, 0x2201, ULMBCS_GRP_EXCEPT}, - {0x2202, 0x2202, ULMBCS_AMBIGUOUS_MBCS}, - {0x2203, 0x2203, ULMBCS_AMBIGUOUS_MBCS}, - {0x2204, 0x2206, ULMBCS_GRP_EXCEPT}, - {0x2207, 0x2208, ULMBCS_AMBIGUOUS_MBCS}, - {0x2209, 0x220A, ULMBCS_GRP_EXCEPT}, - {0x220B, 0x220B, ULMBCS_AMBIGUOUS_MBCS}, - {0x220F, 0x2215, ULMBCS_AMBIGUOUS_MBCS}, - {0x2219, 0x2219, ULMBCS_GRP_EXCEPT}, - {0x221A, 0x221A, ULMBCS_AMBIGUOUS_MBCS}, - {0x221B, 0x221C, ULMBCS_GRP_EXCEPT}, - {0x221D, 0x221E, ULMBCS_AMBIGUOUS_MBCS}, - {0x221F, 0x221F, ULMBCS_GRP_EXCEPT}, - {0x2220, 0x2220, ULMBCS_AMBIGUOUS_MBCS}, - {0x2223, 0x222A, ULMBCS_AMBIGUOUS_MBCS}, - {0x222B, 0x223D, ULMBCS_AMBIGUOUS_MBCS}, - {0x2245, 0x2248, ULMBCS_GRP_EXCEPT}, - {0x224C, 0x224C, ULMBCS_GRP_TW}, - {0x2252, 0x2252, ULMBCS_AMBIGUOUS_MBCS}, - {0x2260, 0x2261, ULMBCS_AMBIGUOUS_MBCS}, - {0x2262, 0x2265, ULMBCS_GRP_EXCEPT}, - {0x2266, 0x226F, ULMBCS_AMBIGUOUS_MBCS}, - {0x2282, 0x2283, ULMBCS_AMBIGUOUS_MBCS}, - {0x2284, 0x2285, ULMBCS_GRP_EXCEPT}, - {0x2286, 0x2287, ULMBCS_AMBIGUOUS_MBCS}, - {0x2288, 0x2297, ULMBCS_GRP_EXCEPT}, - {0x2299, 0x22BF, ULMBCS_AMBIGUOUS_MBCS}, - {0x22C0, 0x22C0, ULMBCS_GRP_EXCEPT}, - {0x2310, 0x2310, ULMBCS_GRP_EXCEPT}, - {0x2312, 0x2312, ULMBCS_AMBIGUOUS_MBCS}, - {0x2318, 0x2321, ULMBCS_GRP_EXCEPT}, - {0x2318, 0x2321, ULMBCS_GRP_CN}, - {0x2460, 0x24E9, ULMBCS_AMBIGUOUS_MBCS}, - {0x2500, 0x2500, ULMBCS_AMBIGUOUS_SBCS}, - {0x2501, 0x2501, ULMBCS_AMBIGUOUS_MBCS}, - {0x2502, 0x2502, ULMBCS_AMBIGUOUS_ALL}, - {0x2503, 0x2503, ULMBCS_AMBIGUOUS_MBCS}, - {0x2504, 0x2505, ULMBCS_GRP_TW}, - {0x2506, 0x2665, ULMBCS_AMBIGUOUS_ALL}, - {0x2666, 0x2666, ULMBCS_GRP_EXCEPT}, - {0x2667, 0x2669, ULMBCS_AMBIGUOUS_SBCS}, - {0x266A, 0x266A, ULMBCS_AMBIGUOUS_ALL}, - {0x266B, 0x266C, ULMBCS_AMBIGUOUS_SBCS}, - {0x266D, 0x266D, ULMBCS_AMBIGUOUS_MBCS}, - {0x266E, 0x266E, ULMBCS_AMBIGUOUS_SBCS}, - {0x266F, 0x266F, ULMBCS_GRP_JA}, - {0x2670, 0x2E7F, ULMBCS_AMBIGUOUS_SBCS}, - {0x2E80, 0xF861, ULMBCS_AMBIGUOUS_MBCS}, - {0xF862, 0xF8FF, ULMBCS_GRP_EXCEPT}, - {0xF900, 0xFA2D, ULMBCS_AMBIGUOUS_MBCS}, - {0xFB00, 0xFEFF, ULMBCS_AMBIGUOUS_SBCS}, - {0xFF01, 0xFFEE, ULMBCS_AMBIGUOUS_MBCS}, - {0xFFFF, 0xFFFF, ULMBCS_GRP_UNICODE} -}; - -static ulmbcs_byte_t -FindLMBCSUniRange(UChar uniChar) -{ - const struct _UniLMBCSGrpMap * pTable = UniLMBCSGrpMap; - - while (uniChar > pTable->uniEndRange) - { - pTable++; - } - - if (uniChar >= pTable->uniStartRange) - { - return pTable->GrpType; - } - return ULMBCS_GRP_UNICODE; -} - -/* -We also ask the creator of a converter to send in a preferred locale -that we can use in resolving ambiguous mappings. They send the locale -in as a string, and we map it, if possible, to one of the -LMBCS groups. We use this table, and the associated code, to -do the lookup: */ - -/************************************************** - This table maps locale ID's to LMBCS opt groups. - The default return is group 0x01. Note that for - performance reasons, the table is sorted in - increasing alphabetic order, with the notable - exception of zhTW. This is to force the check - for Traditonal Chinese before dropping back to - Simplified. - - Note too that the Latin-1 groups have been - commented out because it's the default, and - this shortens the table, allowing a serial - search to go quickly. - *************************************************/ - -static const struct _LocaleLMBCSGrpMap -{ - const char *LocaleID; - const ulmbcs_byte_t OptGroup; -} LocaleLMBCSGrpMap[] = -{ - {"ar", ULMBCS_GRP_AR}, - {"be", ULMBCS_GRP_RU}, - {"bg", ULMBCS_GRP_L2}, - /* {"ca", ULMBCS_GRP_L1}, */ - {"cs", ULMBCS_GRP_L2}, - /* {"da", ULMBCS_GRP_L1}, */ - /* {"de", ULMBCS_GRP_L1}, */ - {"el", ULMBCS_GRP_GR}, - /* {"en", ULMBCS_GRP_L1}, */ - /* {"es", ULMBCS_GRP_L1}, */ - /* {"et", ULMBCS_GRP_L1}, */ - /* {"fi", ULMBCS_GRP_L1}, */ - /* {"fr", ULMBCS_GRP_L1}, */ - {"he", ULMBCS_GRP_HE}, - {"hu", ULMBCS_GRP_L2}, - /* {"is", ULMBCS_GRP_L1}, */ - /* {"it", ULMBCS_GRP_L1}, */ - {"iw", ULMBCS_GRP_HE}, - {"ja", ULMBCS_GRP_JA}, - {"ko", ULMBCS_GRP_KO}, - /* {"lt", ULMBCS_GRP_L1}, */ - /* {"lv", ULMBCS_GRP_L1}, */ - {"mk", ULMBCS_GRP_RU}, - /* {"nl", ULMBCS_GRP_L1}, */ - /* {"no", ULMBCS_GRP_L1}, */ - {"pl", ULMBCS_GRP_L2}, - /* {"pt", ULMBCS_GRP_L1}, */ - {"ro", ULMBCS_GRP_L2}, - {"ru", ULMBCS_GRP_RU}, - {"sh", ULMBCS_GRP_L2}, - {"sk", ULMBCS_GRP_L2}, - {"sl", ULMBCS_GRP_L2}, - {"sq", ULMBCS_GRP_L2}, - {"sr", ULMBCS_GRP_RU}, - /* {"sv", ULMBCS_GRP_L1}, */ - {"th", ULMBCS_GRP_TH}, - {"tr", ULMBCS_GRP_TR}, - {"uk", ULMBCS_GRP_RU}, - /* {"vi", ULMBCS_GRP_L1}, */ - {"zhTW", ULMBCS_GRP_TW}, - {"zh", ULMBCS_GRP_CN}, - {NULL, ULMBCS_GRP_L1} -}; - - -static ulmbcs_byte_t -FindLMBCSLocale(const char *LocaleID) -{ - const struct _LocaleLMBCSGrpMap *pTable = LocaleLMBCSGrpMap; - - if ((!LocaleID) || (!*LocaleID)) - { - return 0; - } - - while (pTable->LocaleID) - { - if (*pTable->LocaleID == *LocaleID) /* Check only first char for speed */ - { - /* First char matches - check whole name, for entry-length */ - if (uprv_strncmp(pTable->LocaleID, LocaleID, strlen(pTable->LocaleID)) == 0) - return pTable->OptGroup; - } - else - if (*pTable->LocaleID > *LocaleID) /* Sorted alphabetically - exit */ - break; - pTable++; - } - return ULMBCS_GRP_L1; -} - - -/* - Before we get to the main body of code, here's how we hook up to the rest - of ICU. ICU converters are required to define a structure that includes - some function pointers, and some common data, in the style of a C++ - vtable. There is also room in there for converter-specific data. LMBCS - uses that converter-specific data to keep track of the 12 subconverters - we use, the optimization group, and the group (if any) that matches the - locale. We have one structure instantiated for each of the 12 possible - optimization groups. To avoid typos & to avoid boring the reader, we - put the declarations of these structures and functions into macros. To see - the definitions of these structures, see unicode\ucnv_bld.h -*/ - -typedef struct - { - UConverterSharedData *OptGrpConverter[ULMBCS_GRP_LAST+1]; /* Converter per Opt. grp. */ - uint8_t OptGroup; /* default Opt. grp. for this LMBCS session */ - uint8_t localeConverterIndex; /* reasonable locale match for index */ - } -UConverterDataLMBCS; - -U_CDECL_BEGIN -static void U_CALLCONV _LMBCSClose(UConverter * _this); -U_CDECL_END - -#define DECLARE_LMBCS_DATA(n) \ -static const UConverterImpl _LMBCSImpl##n={\ - UCNV_LMBCS_##n,\ - NULL,NULL,\ - _LMBCSOpen##n,\ - _LMBCSClose,\ - NULL,\ - _LMBCSToUnicodeWithOffsets,\ - _LMBCSToUnicodeWithOffsets,\ - _LMBCSFromUnicode,\ - _LMBCSFromUnicode,\ - NULL,\ - NULL,\ - NULL,\ - NULL,\ - _LMBCSSafeClone,\ - ucnv_getCompleteUnicodeSet,\ - NULL,\ - NULL\ -};\ -static const UConverterStaticData _LMBCSStaticData##n={\ - sizeof(UConverterStaticData),\ - "LMBCS-" #n,\ - 0, UCNV_IBM, UCNV_LMBCS_##n, 1, 3,\ - { 0x3f, 0, 0, 0 },1,false,false,0,0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} \ -};\ -const UConverterSharedData _LMBCSData##n= \ - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_LMBCSStaticData##n, &_LMBCSImpl##n); - - /* The only function we needed to duplicate 12 times was the 'open' -function, which will do basically the same thing except set a different -optimization group. So, we put the common stuff into a worker function, -and set up another macro to stamp out the 12 open functions:*/ -#define DEFINE_LMBCS_OPEN(n) \ -static void U_CALLCONV \ - _LMBCSOpen##n(UConverter* _this, UConverterLoadArgs* pArgs, UErrorCode* err) \ -{ _LMBCSOpenWorker(_this, pArgs, err, n); } - - - -/* Here's the open worker & the common close function */ -static void -_LMBCSOpenWorker(UConverter* _this, - UConverterLoadArgs *pArgs, - UErrorCode* err, - ulmbcs_byte_t OptGroup) -{ - UConverterDataLMBCS * extraInfo = (UConverterDataLMBCS*)uprv_malloc (sizeof (UConverterDataLMBCS)); - _this->extraInfo = extraInfo; - if(extraInfo != NULL) - { - UConverterNamePieces stackPieces; - UConverterLoadArgs stackArgs= UCNV_LOAD_ARGS_INITIALIZER; - ulmbcs_byte_t i; - - uprv_memset(extraInfo, 0, sizeof(UConverterDataLMBCS)); - - stackArgs.onlyTestIsLoadable = pArgs->onlyTestIsLoadable; - - for (i=0; i <= ULMBCS_GRP_LAST && U_SUCCESS(*err); i++) - { - if(OptGroupByteToCPName[i] != NULL) { - extraInfo->OptGrpConverter[i] = ucnv_loadSharedData(OptGroupByteToCPName[i], &stackPieces, &stackArgs, err); - } - } - - if(U_FAILURE(*err) || pArgs->onlyTestIsLoadable) { - _LMBCSClose(_this); - return; - } - extraInfo->OptGroup = OptGroup; - extraInfo->localeConverterIndex = FindLMBCSLocale(pArgs->locale); - } - else - { - *err = U_MEMORY_ALLOCATION_ERROR; - } -} - -U_CDECL_BEGIN -static void U_CALLCONV -_LMBCSClose(UConverter * _this) -{ - if (_this->extraInfo != NULL) - { - ulmbcs_byte_t Ix; - UConverterDataLMBCS * extraInfo = (UConverterDataLMBCS *) _this->extraInfo; - - for (Ix=0; Ix <= ULMBCS_GRP_LAST; Ix++) - { - if (extraInfo->OptGrpConverter[Ix] != NULL) - ucnv_unloadSharedDataIfReady(extraInfo->OptGrpConverter[Ix]); - } - if (!_this->isExtraLocal) { - uprv_free (_this->extraInfo); - _this->extraInfo = NULL; - } - } -} - -typedef struct LMBCSClone { - UConverter cnv; - UConverterDataLMBCS lmbcs; -} LMBCSClone; - -static UConverter * U_CALLCONV -_LMBCSSafeClone(const UConverter *cnv, - void *stackBuffer, - int32_t *pBufferSize, - UErrorCode *status) { - (void)status; - LMBCSClone *newLMBCS; - UConverterDataLMBCS *extraInfo; - int32_t i; - - if(*pBufferSize<=0) { - *pBufferSize=(int32_t)sizeof(LMBCSClone); - return NULL; - } - - extraInfo=(UConverterDataLMBCS *)cnv->extraInfo; - newLMBCS=(LMBCSClone *)stackBuffer; - - /* ucnv.c/ucnv_safeClone() copied the main UConverter already */ - - uprv_memcpy(&newLMBCS->lmbcs, extraInfo, sizeof(UConverterDataLMBCS)); - - /* share the subconverters */ - for(i = 0; i <= ULMBCS_GRP_LAST; ++i) { - if(extraInfo->OptGrpConverter[i] != NULL) { - ucnv_incrementRefCount(extraInfo->OptGrpConverter[i]); - } - } - - newLMBCS->cnv.extraInfo = &newLMBCS->lmbcs; - newLMBCS->cnv.isExtraLocal = true; - return &newLMBCS->cnv; -} - -/* - * There used to be a _LMBCSGetUnicodeSet() function here (up to svn revision 20117) - * which added all code points except for U+F6xx - * because those cannot be represented in the Unicode group. - * However, it turns out that windows-950 has roundtrips for all of U+F6xx - * which means that LMBCS can convert all Unicode code points after all. - * We now simply use ucnv_getCompleteUnicodeSet(). - * - * This may need to be looked at again as Lotus uses _LMBCSGetUnicodeSet(). (091216) - */ - -/* - Here's the basic helper function that we use when converting from - Unicode to LMBCS, and we suspect that a Unicode character will fit into - one of the 12 groups. The return value is the number of bytes written - starting at pStartLMBCS (if any). -*/ - -static size_t -LMBCSConversionWorker ( - UConverterDataLMBCS * extraInfo, /* subconverters, opt & locale groups */ - ulmbcs_byte_t group, /* The group to try */ - ulmbcs_byte_t * pStartLMBCS, /* where to put the results */ - UChar * pUniChar, /* The input unicode character */ - ulmbcs_byte_t * lastConverterIndex, /* output: track last successful group used */ - UBool * groups_tried /* output: track any unsuccessful groups */ -) -{ - ulmbcs_byte_t * pLMBCS = pStartLMBCS; - UConverterSharedData * xcnv = extraInfo->OptGrpConverter[group]; - - int bytesConverted; - uint32_t value; - ulmbcs_byte_t firstByte; - - U_ASSERT(xcnv); - U_ASSERT(group 0) { - firstByte = (ulmbcs_byte_t)(value >> ((bytesConverted - 1) * 8)); - } else { - /* most common failure mode is an unassigned character */ - groups_tried[group] = true; - return 0; - } - - *lastConverterIndex = group; - - /* All initial byte values in lower ascii range should have been caught by now, - except with the exception group. - */ - U_ASSERT((firstByte <= ULMBCS_C0END) || (firstByte >= ULMBCS_C1START) || (group == ULMBCS_GRP_EXCEPT)); - - /* use converted data: first write 0, 1 or two group bytes */ - if (group != ULMBCS_GRP_EXCEPT && extraInfo->OptGroup != group) - { - *pLMBCS++ = group; - if (bytesConverted == 1 && group >= ULMBCS_DOUBLEOPTGROUP_START) - { - *pLMBCS++ = group; - } - } - - /* don't emit control chars */ - if ( bytesConverted == 1 && firstByte < 0x20 ) - return 0; - - - /* then move over the converted data */ - switch(bytesConverted) - { - case 4: - *pLMBCS++ = (ulmbcs_byte_t)(value >> 24); - U_FALLTHROUGH; - case 3: - *pLMBCS++ = (ulmbcs_byte_t)(value >> 16); - U_FALLTHROUGH; - case 2: - *pLMBCS++ = (ulmbcs_byte_t)(value >> 8); - U_FALLTHROUGH; - case 1: - *pLMBCS++ = (ulmbcs_byte_t)value; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - - return (pLMBCS - pStartLMBCS); -} - - -/* This is a much simpler version of above, when we -know we are writing LMBCS using the Unicode group -*/ -static size_t -LMBCSConvertUni(ulmbcs_byte_t * pLMBCS, UChar uniChar) -{ - /* encode into LMBCS Unicode range */ - uint8_t LowCh = (uint8_t)(uniChar & 0x00FF); - uint8_t HighCh = (uint8_t)(uniChar >> 8); - - *pLMBCS++ = ULMBCS_GRP_UNICODE; - - if (LowCh == 0) - { - *pLMBCS++ = ULMBCS_UNICOMPATZERO; - *pLMBCS++ = HighCh; - } - else - { - *pLMBCS++ = HighCh; - *pLMBCS++ = LowCh; - } - return ULMBCS_UNICODE_SIZE; -} - - - -/* The main Unicode to LMBCS conversion function */ -static void U_CALLCONV -_LMBCSFromUnicode(UConverterFromUnicodeArgs* args, - UErrorCode* err) -{ - ulmbcs_byte_t lastConverterIndex = 0; - UChar uniChar; - ulmbcs_byte_t LMBCS[ULMBCS_CHARSIZE_MAX]; - ulmbcs_byte_t * pLMBCS; - int32_t bytes_written; - UBool groups_tried[ULMBCS_GRP_LAST+1]; - UConverterDataLMBCS * extraInfo = (UConverterDataLMBCS *) args->converter->extraInfo; - int sourceIndex = 0; - - /* Basic strategy: attempt to fill in local LMBCS 1-char buffer.(LMBCS) - If that succeeds, see if it will all fit into the target & copy it over - if it does. - - We try conversions in the following order: - - 1. Single-byte ascii & special fixed control chars (&null) - 2. Look up group in table & try that (could be - A) Unicode group - B) control group, - C) national encoding, - or ambiguous SBCS or MBCS group (on to step 4...) - - 3. If its ambiguous, try this order: - A) The optimization group - B) The locale group - C) The last group that succeeded with this string. - D) every other group that's relevant (single or double) - E) If its single-byte ambiguous, try the exceptions group - - 4. And as a grand fallback: Unicode - */ - - /*Fix for SPR#DJOE66JFN3 (Lotus)*/ - ulmbcs_byte_t OldConverterIndex = 0; - - while (args->source < args->sourceLimit && !U_FAILURE(*err)) - { - /*Fix for SPR#DJOE66JFN3 (Lotus)*/ - OldConverterIndex = extraInfo->localeConverterIndex; - - if (args->target >= args->targetLimit) - { - *err = U_BUFFER_OVERFLOW_ERROR; - break; - } - uniChar = *(args->source); - bytes_written = 0; - pLMBCS = LMBCS; - - /* check cases in rough order of how common they are, for speed */ - - /* single byte matches: strategy 1 */ - /*Fix for SPR#DJOE66JFN3 (Lotus)*/ - if((uniChar>=0x80) && (uniChar<=0xff) - /*Fix for SPR#JUYA6XAERU and TSAO7GL5NK (Lotus)*/ &&(uniChar!=0xB1) &&(uniChar!=0xD7) &&(uniChar!=0xF7) - &&(uniChar!=0xB0) &&(uniChar!=0xB4) &&(uniChar!=0xB6) &&(uniChar!=0xA7) &&(uniChar!=0xA8)) - { - extraInfo->localeConverterIndex = ULMBCS_GRP_L1; - } - if (((uniChar > ULMBCS_C0END) && (uniChar < ULMBCS_C1START)) || - uniChar == 0 || uniChar == ULMBCS_HT || uniChar == ULMBCS_CR || - uniChar == ULMBCS_LF || uniChar == ULMBCS_123SYSTEMRANGE - ) - { - *pLMBCS++ = (ulmbcs_byte_t ) uniChar; - bytes_written = 1; - } - - - if (!bytes_written) - { - /* Check by UNICODE range (Strategy 2) */ - ulmbcs_byte_t group = FindLMBCSUniRange(uniChar); - - if (group == ULMBCS_GRP_UNICODE) /* (Strategy 2A) */ - { - pLMBCS += LMBCSConvertUni(pLMBCS,uniChar); - - bytes_written = (int32_t)(pLMBCS - LMBCS); - } - else if (group == ULMBCS_GRP_CTRL) /* (Strategy 2B) */ - { - /* Handle control characters here */ - if (uniChar <= ULMBCS_C0END) - { - *pLMBCS++ = ULMBCS_GRP_CTRL; - *pLMBCS++ = (ulmbcs_byte_t)(ULMBCS_CTRLOFFSET + uniChar); - } - else if (uniChar >= ULMBCS_C1START && uniChar <= ULMBCS_C1START + ULMBCS_CTRLOFFSET) - { - *pLMBCS++ = ULMBCS_GRP_CTRL; - *pLMBCS++ = (ulmbcs_byte_t ) (uniChar & 0x00FF); - } - bytes_written = (int32_t)(pLMBCS - LMBCS); - } - else if (group < ULMBCS_GRP_UNICODE) /* (Strategy 2C) */ - { - /* a specific converter has been identified - use it */ - bytes_written = (int32_t)LMBCSConversionWorker ( - extraInfo, group, pLMBCS, &uniChar, - &lastConverterIndex, groups_tried); - } - if (!bytes_written) /* the ambiguous group cases (Strategy 3) */ - { - uprv_memset(groups_tried, 0, sizeof(groups_tried)); - - /* check for non-default optimization group (Strategy 3A )*/ - if ((extraInfo->OptGroup != 1) && (ULMBCS_AMBIGUOUS_MATCH(group, extraInfo->OptGroup))) - { - /*zhujin: upgrade, merge #39299 here (Lotus) */ - /*To make R5 compatible translation, look for exceptional group first for non-DBCS*/ - - if(extraInfo->localeConverterIndex < ULMBCS_DOUBLEOPTGROUP_START) - { - bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, - ULMBCS_GRP_L1, pLMBCS, &uniChar, - &lastConverterIndex, groups_tried); - - if(!bytes_written) - { - bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, - ULMBCS_GRP_EXCEPT, pLMBCS, &uniChar, - &lastConverterIndex, groups_tried); - } - if(!bytes_written) - { - bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, - extraInfo->localeConverterIndex, pLMBCS, &uniChar, - &lastConverterIndex, groups_tried); - } - } - else - { - bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, - extraInfo->localeConverterIndex, pLMBCS, &uniChar, - &lastConverterIndex, groups_tried); - } - } - /* check for locale optimization group (Strategy 3B) */ - if (!bytes_written && (extraInfo->localeConverterIndex) && (ULMBCS_AMBIGUOUS_MATCH(group, extraInfo->localeConverterIndex))) - { - bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, - extraInfo->localeConverterIndex, pLMBCS, &uniChar, &lastConverterIndex, groups_tried); - } - /* check for last optimization group used for this string (Strategy 3C) */ - if (!bytes_written && (lastConverterIndex) && (ULMBCS_AMBIGUOUS_MATCH(group, lastConverterIndex))) - { - bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, - lastConverterIndex, pLMBCS, &uniChar, &lastConverterIndex, groups_tried); - } - if (!bytes_written) - { - /* just check every possible matching converter (Strategy 3D) */ - ulmbcs_byte_t grp_start; - ulmbcs_byte_t grp_end; - ulmbcs_byte_t grp_ix; - grp_start = (ulmbcs_byte_t)((group == ULMBCS_AMBIGUOUS_MBCS) - ? ULMBCS_DOUBLEOPTGROUP_START - : ULMBCS_GRP_L1); - grp_end = (ulmbcs_byte_t)((group == ULMBCS_AMBIGUOUS_MBCS) - ? ULMBCS_GRP_LAST - : ULMBCS_GRP_TH); - if(group == ULMBCS_AMBIGUOUS_ALL) - { - grp_start = ULMBCS_GRP_L1; - grp_end = ULMBCS_GRP_LAST; - } - for (grp_ix = grp_start; - grp_ix <= grp_end && !bytes_written; - grp_ix++) - { - if (extraInfo->OptGrpConverter [grp_ix] && !groups_tried [grp_ix]) - { - bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, - grp_ix, pLMBCS, &uniChar, - &lastConverterIndex, groups_tried); - } - } - /* a final conversion fallback to the exceptions group if its likely - to be single byte (Strategy 3E) */ - if (!bytes_written && grp_start == ULMBCS_GRP_L1) - { - bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, - ULMBCS_GRP_EXCEPT, pLMBCS, &uniChar, - &lastConverterIndex, groups_tried); - } - } - /* all of our other strategies failed. Fallback to Unicode. (Strategy 4)*/ - if (!bytes_written) - { - - pLMBCS += LMBCSConvertUni(pLMBCS, uniChar); - bytes_written = (int32_t)(pLMBCS - LMBCS); - } - } - } - - /* we have a translation. increment source and write as much as possible to target */ - args->source++; - pLMBCS = LMBCS; - while (args->target < args->targetLimit && bytes_written--) - { - *(args->target)++ = *pLMBCS++; - if (args->offsets) - { - *(args->offsets)++ = sourceIndex; - } - } - sourceIndex++; - if (bytes_written > 0) - { - /* write any bytes that didn't fit in target to the error buffer, - common code will move this to target if we get called back with - enough target room - */ - uint8_t * pErrorBuffer = args->converter->charErrorBuffer; - *err = U_BUFFER_OVERFLOW_ERROR; - args->converter->charErrorBufferLength = (int8_t)bytes_written; - while (bytes_written--) - { - *pErrorBuffer++ = *pLMBCS++; - } - } - /*Fix for SPR#DJOE66JFN3 (Lotus)*/ - extraInfo->localeConverterIndex = OldConverterIndex; - } -} - - -/* Now, the Unicode from LMBCS section */ - - -/* A function to call when we are looking at the Unicode group byte in LMBCS */ -static UChar -GetUniFromLMBCSUni(char const ** ppLMBCSin) /* Called with LMBCS-style Unicode byte stream */ -{ - uint8_t HighCh = *(*ppLMBCSin)++; /* Big-endian Unicode in LMBCS compatibility group*/ - uint8_t LowCh = *(*ppLMBCSin)++; - - if (HighCh == ULMBCS_UNICOMPATZERO ) - { - HighCh = LowCh; - LowCh = 0; /* zero-byte in LSB special character */ - } - return (UChar)((HighCh << 8) | LowCh); -} - - - -/* CHECK_SOURCE_LIMIT: Helper macro to verify that there are at least'index' - bytes left in source up to sourceLimit.Errors appropriately if not. - If we reach the limit, then update the source pointer to there to consume - all input as required by ICU converter semantics. -*/ - -#define CHECK_SOURCE_LIMIT(index) UPRV_BLOCK_MACRO_BEGIN { \ - if (args->source+index > args->sourceLimit) { \ - *err = U_TRUNCATED_CHAR_FOUND; \ - args->source = args->sourceLimit; \ - return 0xffff; \ - } \ -} UPRV_BLOCK_MACRO_END - -/* Return the Unicode representation for the current LMBCS character */ - -static UChar32 U_CALLCONV -_LMBCSGetNextUCharWorker(UConverterToUnicodeArgs* args, - UErrorCode* err) -{ - UChar32 uniChar = 0; /* an output UNICODE char */ - ulmbcs_byte_t CurByte; /* A byte from the input stream */ - - /* error check */ - if (args->source >= args->sourceLimit) - { - *err = U_ILLEGAL_ARGUMENT_ERROR; - return 0xffff; - } - /* Grab first byte & save address for error recovery */ - CurByte = *((ulmbcs_byte_t *) (args->source++)); - - /* - * at entry of each if clause: - * 1. 'CurByte' points at the first byte of a LMBCS character - * 2. '*source'points to the next byte of the source stream after 'CurByte' - * - * the job of each if clause is: - * 1. set '*source' to point at the beginning of next char (nop if LMBCS char is only 1 byte) - * 2. set 'uniChar' up with the right Unicode value, or set 'err' appropriately - */ - - /* First lets check the simple fixed values. */ - - if(((CurByte > ULMBCS_C0END) && (CurByte < ULMBCS_C1START)) /* ascii range */ - || (CurByte == 0) - || CurByte == ULMBCS_HT || CurByte == ULMBCS_CR - || CurByte == ULMBCS_LF || CurByte == ULMBCS_123SYSTEMRANGE) - { - uniChar = CurByte; - } - else - { - UConverterDataLMBCS * extraInfo; - ulmbcs_byte_t group; - UConverterSharedData *cnv; - - if (CurByte == ULMBCS_GRP_CTRL) /* Control character group - no opt group update */ - { - ulmbcs_byte_t C0C1byte; - CHECK_SOURCE_LIMIT(1); - C0C1byte = *(args->source)++; - uniChar = (C0C1byte < ULMBCS_C1START) ? C0C1byte - ULMBCS_CTRLOFFSET : C0C1byte; - } - else - if (CurByte == ULMBCS_GRP_UNICODE) /* Unicode compatibility group: BigEndian UTF16 */ - { - CHECK_SOURCE_LIMIT(2); - - /* don't check for error indicators fffe/ffff below */ - return GetUniFromLMBCSUni(&(args->source)); - } - else if (CurByte <= ULMBCS_CTRLOFFSET) - { - group = CurByte; /* group byte is in the source */ - extraInfo = (UConverterDataLMBCS *) args->converter->extraInfo; - if (group > ULMBCS_GRP_LAST || (cnv = extraInfo->OptGrpConverter[group]) == NULL) - { - /* this is not a valid group byte - no converter*/ - *err = U_INVALID_CHAR_FOUND; - } - else if (group >= ULMBCS_DOUBLEOPTGROUP_START) /* double byte conversion */ - { - - CHECK_SOURCE_LIMIT(2); - - /* check for LMBCS doubled-group-byte case */ - if (*args->source == group) { - /* single byte */ - ++args->source; - uniChar = ucnv_MBCSSimpleGetNextUChar(cnv, args->source, 1, false); - ++args->source; - } else { - /* double byte */ - uniChar = ucnv_MBCSSimpleGetNextUChar(cnv, args->source, 2, false); - args->source += 2; - } - } - else { /* single byte conversion */ - CHECK_SOURCE_LIMIT(1); - CurByte = *(args->source)++; - - if (CurByte >= ULMBCS_C1START) - { - uniChar = _MBCS_SINGLE_SIMPLE_GET_NEXT_BMP(cnv, CurByte); - } - else - { - /* The non-optimizable oddballs where there is an explicit byte - * AND the second byte is not in the upper ascii range - */ - char bytes[2]; - - extraInfo = (UConverterDataLMBCS *) args->converter->extraInfo; - cnv = extraInfo->OptGrpConverter [ULMBCS_GRP_EXCEPT]; - - /* Lookup value must include opt group */ - bytes[0] = group; - bytes[1] = CurByte; - uniChar = ucnv_MBCSSimpleGetNextUChar(cnv, bytes, 2, false); - } - } - } - else if (CurByte >= ULMBCS_C1START) /* group byte is implicit */ - { - extraInfo = (UConverterDataLMBCS *) args->converter->extraInfo; - group = extraInfo->OptGroup; - cnv = extraInfo->OptGrpConverter[group]; - if (group >= ULMBCS_DOUBLEOPTGROUP_START) /* double byte conversion */ - { - if (!ucnv_MBCSIsLeadByte(cnv, CurByte)) - { - CHECK_SOURCE_LIMIT(0); - - /* let the MBCS conversion consume CurByte again */ - uniChar = ucnv_MBCSSimpleGetNextUChar(cnv, args->source - 1, 1, false); - } - else - { - CHECK_SOURCE_LIMIT(1); - /* let the MBCS conversion consume CurByte again */ - uniChar = ucnv_MBCSSimpleGetNextUChar(cnv, args->source - 1, 2, false); - ++args->source; - } - } - else /* single byte conversion */ - { - uniChar = _MBCS_SINGLE_SIMPLE_GET_NEXT_BMP(cnv, CurByte); - } - } - } - return uniChar; -} - - -/* The exported function that converts lmbcs to one or more - UChars - currently UTF-16 -*/ -static void U_CALLCONV -_LMBCSToUnicodeWithOffsets(UConverterToUnicodeArgs* args, - UErrorCode* err) -{ - char LMBCS [ULMBCS_CHARSIZE_MAX]; - UChar uniChar; /* one output UNICODE char */ - const char * saveSource; /* beginning of current code point */ - const char * pStartLMBCS = args->source; /* beginning of whole string */ - const char * errSource = NULL; /* pointer to actual input in case an error occurs */ - int8_t savebytes = 0; - - /* Process from source to limit, or until error */ - while (U_SUCCESS(*err) && args->sourceLimit > args->source && args->targetLimit > args->target) - { - saveSource = args->source; /* beginning of current code point */ - - if (args->converter->toULength) /* reassemble char from previous call */ - { - const char *saveSourceLimit; - size_t size_old = args->converter->toULength; - - /* limit from source is either remainder of temp buffer, or user limit on source */ - size_t size_new_maybe_1 = sizeof(LMBCS) - size_old; - size_t size_new_maybe_2 = args->sourceLimit - args->source; - size_t size_new = (size_new_maybe_1 < size_new_maybe_2) ? size_new_maybe_1 : size_new_maybe_2; - - - uprv_memcpy(LMBCS, args->converter->toUBytes, size_old); - uprv_memcpy(LMBCS + size_old, args->source, size_new); - saveSourceLimit = args->sourceLimit; - args->source = errSource = LMBCS; - args->sourceLimit = LMBCS+size_old+size_new; - savebytes = (int8_t)(size_old+size_new); - uniChar = (UChar) _LMBCSGetNextUCharWorker(args, err); - args->source = saveSource + ((args->source - LMBCS) - size_old); - args->sourceLimit = saveSourceLimit; - - if (*err == U_TRUNCATED_CHAR_FOUND) - { - /* evil special case: source buffers so small a char spans more than 2 buffers */ - args->converter->toULength = savebytes; - uprv_memcpy(args->converter->toUBytes, LMBCS, savebytes); - args->source = args->sourceLimit; - *err = U_ZERO_ERROR; - return; - } - else - { - /* clear the partial-char marker */ - args->converter->toULength = 0; - } - } - else - { - errSource = saveSource; - uniChar = (UChar) _LMBCSGetNextUCharWorker(args, err); - savebytes = (int8_t)(args->source - saveSource); - } - if (U_SUCCESS(*err)) - { - if (uniChar < 0xfffe) - { - *(args->target)++ = uniChar; - if(args->offsets) - { - *(args->offsets)++ = (int32_t)(saveSource - pStartLMBCS); - } - } - else if (uniChar == 0xfffe) - { - *err = U_INVALID_CHAR_FOUND; - } - else /* if (uniChar == 0xffff) */ - { - *err = U_ILLEGAL_CHAR_FOUND; - } - } - } - /* if target ran out before source, return U_BUFFER_OVERFLOW_ERROR */ - if (U_SUCCESS(*err) && args->sourceLimit > args->source && args->targetLimit <= args->target) - { - *err = U_BUFFER_OVERFLOW_ERROR; - } - else if (U_FAILURE(*err)) - { - /* If character incomplete or unmappable/illegal, store it in toUBytes[] */ - args->converter->toULength = savebytes; - if (savebytes > 0) { - uprv_memcpy(args->converter->toUBytes, errSource, savebytes); - } - if (*err == U_TRUNCATED_CHAR_FOUND) { - *err = U_ZERO_ERROR; - } - } -} - -/* And now, the macroized declarations of data & functions: */ -DEFINE_LMBCS_OPEN(1) -DEFINE_LMBCS_OPEN(2) -DEFINE_LMBCS_OPEN(3) -DEFINE_LMBCS_OPEN(4) -DEFINE_LMBCS_OPEN(5) -DEFINE_LMBCS_OPEN(6) -DEFINE_LMBCS_OPEN(8) -DEFINE_LMBCS_OPEN(11) -DEFINE_LMBCS_OPEN(16) -DEFINE_LMBCS_OPEN(17) -DEFINE_LMBCS_OPEN(18) -DEFINE_LMBCS_OPEN(19) - - -DECLARE_LMBCS_DATA(1) -DECLARE_LMBCS_DATA(2) -DECLARE_LMBCS_DATA(3) -DECLARE_LMBCS_DATA(4) -DECLARE_LMBCS_DATA(5) -DECLARE_LMBCS_DATA(6) -DECLARE_LMBCS_DATA(8) -DECLARE_LMBCS_DATA(11) -DECLARE_LMBCS_DATA(16) -DECLARE_LMBCS_DATA(17) -DECLARE_LMBCS_DATA(18) -DECLARE_LMBCS_DATA(19) - -U_CDECL_END - -#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2000-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: ucnv_lmb.cpp +* encoding: UTF-8 +* tab size: 4 (not used) +* indentation:4 +* +* created on: 2000feb09 +* created by: Brendan Murray +* extensively hacked up by: Jim Snyder-Grant +* +* Modification History: +* +* Date Name Description +* +* 06/20/2000 helena OS/400 port changes; mostly typecast. +* 06/27/2000 Jim Snyder-Grant Deal with partial characters and small buffers. +* Add comments to document LMBCS format and implementation +* restructured order & breakdown of functions +* 06/28/2000 helena Major rewrite for the callback API changes. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION + +#include "unicode/ucnv_err.h" +#include "unicode/ucnv.h" +#include "unicode/uset.h" +#include "cmemory.h" +#include "cstring.h" +#include "uassert.h" +#include "ucnv_imp.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" + +#ifdef EBCDIC_RTL + #include "ascii_a.h" +#endif + +/* + LMBCS + + (Lotus Multi-Byte Character Set) + + LMBCS was invented in the late 1980's and is primarily used in Lotus Notes + databases and in Lotus 1-2-3 files. Programmers who work with the APIs + into these products will sometimes need to deal with strings in this format. + + The code in this file provides an implementation for an ICU converter of + LMBCS to and from Unicode. + + Since the LMBCS character set is only sparsely documented in existing + printed or online material, we have added extensive annotation to this + file to serve as a guide to understanding LMBCS. + + LMBCS was originally designed with these four sometimes-competing design goals: + + -Provide encodings for the characters in 12 existing national standards + (plus a few other characters) + -Minimal memory footprint + -Maximal speed of conversion into the existing national character sets + -No need to track a changing state as you interpret a string. + + + All of the national character sets LMBCS was trying to encode are 'ANSI' + based, in that the bytes from 0x20 - 0x7F are almost exactly the + same common Latin unaccented characters and symbols in all character sets. + + So, in order to help meet the speed & memory design goals, the common ANSI + bytes from 0x20-0x7F are represented by the same single-byte values in LMBCS. + + The general LMBCS code unit is from 1-3 bytes. We can describe the 3 bytes as + follows: + + [G] D1 [D2] + + That is, a sometimes-optional 'group' byte, followed by 1 and sometimes 2 + data bytes. The maximum size of a LMBCS character is 3 bytes: +*/ +#define ULMBCS_CHARSIZE_MAX 3 +/* + The single-byte values from 0x20 to 0x7F are examples of single D1 bytes. + We often have to figure out if byte values are below or above this, so we + use the ANSI nomenclature 'C0' and 'C1' to refer to the range of control + characters just above & below the common lower-ANSI range */ +#define ULMBCS_C0END 0x1F +#define ULMBCS_C1START 0x80 +/* + Since LMBCS is always dealing in byte units. we create a local type here for + dealing with these units of LMBCS code units: + +*/ +typedef uint8_t ulmbcs_byte_t; + +/* + Most of the values less than 0x20 are reserved in LMBCS to announce + which national character standard is being used for the 'D' bytes. + In the comments we show the common name and the IBM character-set ID + for these character-set announcers: +*/ + +#define ULMBCS_GRP_L1 0x01 /* Latin-1 :ibm-850 */ +#define ULMBCS_GRP_GR 0x02 /* Greek :ibm-851 */ +#define ULMBCS_GRP_HE 0x03 /* Hebrew :ibm-1255 */ +#define ULMBCS_GRP_AR 0x04 /* Arabic :ibm-1256 */ +#define ULMBCS_GRP_RU 0x05 /* Cyrillic :ibm-1251 */ +#define ULMBCS_GRP_L2 0x06 /* Latin-2 :ibm-852 */ +#define ULMBCS_GRP_TR 0x08 /* Turkish :ibm-1254 */ +#define ULMBCS_GRP_TH 0x0B /* Thai :ibm-874 */ +#define ULMBCS_GRP_JA 0x10 /* Japanese :ibm-943 */ +#define ULMBCS_GRP_KO 0x11 /* Korean :ibm-1261 */ +#define ULMBCS_GRP_TW 0x12 /* Chinese SC :ibm-950 */ +#define ULMBCS_GRP_CN 0x13 /* Chinese TC :ibm-1386 */ + +/* + So, the beginning of understanding LMBCS is that IF the first byte of a LMBCS + character is one of those 12 values, you can interpret the remaining bytes of + that character as coming from one of those character sets. Since the lower + ANSI bytes already are represented in single bytes, using one of the character + set announcers is used to announce a character that starts with a byte of + 0x80 or greater. + + The character sets are arranged so that the single byte sets all appear + before the multi-byte character sets. When we need to tell whether a + group byte is for a single byte char set or not we use this define: */ + +#define ULMBCS_DOUBLEOPTGROUP_START 0x10 + +/* +However, to fully understand LMBCS, you must also understand a series of +exceptions & optimizations made in service of the design goals. + +First, those of you who are character set mavens may have noticed that +the 'double-byte' character sets are actually multi-byte character sets +that can have 1 or two bytes, even in the upper-ascii range. To force +each group byte to introduce a fixed-width encoding (to make it faster to +count characters), we use a convention of doubling up on the group byte +to introduce any single-byte character > 0x80 in an otherwise double-byte +character set. So, for example, the LMBCS sequence x10 x10 xAE is the +same as '0xAE' in the Japanese code page 943. + +Next, you will notice that the list of group bytes has some gaps. +These are used in various ways. + +We reserve a few special single byte values for common control +characters. These are in the same place as their ANSI equivalents for speed. +*/ + +#define ULMBCS_HT 0x09 /* Fixed control char - Horizontal Tab */ +#define ULMBCS_LF 0x0A /* Fixed control char - Line Feed */ +#define ULMBCS_CR 0x0D /* Fixed control char - Carriage Return */ + +/* Then, 1-2-3 reserved a special single-byte character to put at the +beginning of internal 'system' range names: */ + +#define ULMBCS_123SYSTEMRANGE 0x19 + +/* Then we needed a place to put all the other ansi control characters +that must be moved to different values because LMBCS reserves those +values for other purposes. To represent the control characters, we start +with a first byte of 0xF & add the control character value as the +second byte */ +#define ULMBCS_GRP_CTRL 0x0F + +/* For the C0 controls (less than 0x20), we add 0x20 to preserve the +useful doctrine that any byte less than 0x20 in a LMBCS char must be +the first byte of a character:*/ +#define ULMBCS_CTRLOFFSET 0x20 + +/* +Where to put the characters that aren't part of any of the 12 national +character sets? The first thing that was done, in the earlier years of +LMBCS, was to use up the spaces of the form + + [G] D1, + + where 'G' was one of the single-byte character groups, and + D1 was less than 0x80. These sequences are gathered together + into a Lotus-invented doublebyte character set to represent a + lot of stray values. Internally, in this implementation, we track this + as group '0', as a place to tuck this exceptions list.*/ + +#define ULMBCS_GRP_EXCEPT 0x00 +/* + Finally, as the durability and usefulness of UNICODE became clear, + LOTUS added a new group 0x14 to hold Unicode values not otherwise + represented in LMBCS: */ +#define ULMBCS_GRP_UNICODE 0x14 +/* The two bytes appearing after a 0x14 are interpreted as UFT-16 BE +(Big-Endian) characters. The exception comes when the UTF16 +representation would have a zero as the second byte. In that case, +'F6' is used in its place, and the bytes are swapped. (This prevents +LMBCS from encoding any Unicode values of the form U+F6xx, but that's OK: +0xF6xx is in the middle of the Private Use Area.)*/ +#define ULMBCS_UNICOMPATZERO 0xF6 + +/* It is also useful in our code to have a constant for the size of +a LMBCS char that holds a literal Unicode value */ +#define ULMBCS_UNICODE_SIZE 3 + +/* +To squish the LMBCS representations down even further, and to make +translations even faster,sometimes the optimization group byte can be dropped +from a LMBCS character. This is decided on a process-by-process basis. The +group byte that is dropped is called the 'optimization group'. + +For Notes, the optimzation group is always 0x1.*/ +#define ULMBCS_DEFAULTOPTGROUP 0x1 +/* For 1-2-3 files, the optimzation group is stored in the header of the 1-2-3 +file. + + In any case, when using ICU, you either pass in the +optimization group as part of the name of the converter (LMBCS-1, LMBCS-2, +etc.). Using plain 'LMBCS' as the name of the converter will give you +LMBCS-1. + + +*** Implementation strategy *** + + +Because of the extensive use of other character sets, the LMBCS converter +keeps a mapping between optimization groups and IBM character sets, so that +ICU converters can be created and used as needed. */ + +/* As you can see, even though any byte below 0x20 could be an optimization +byte, only those at 0x13 or below can map to an actual converter. To limit +some loops and searches, we define a value for that last group converter:*/ + +#define ULMBCS_GRP_LAST 0x13 /* last LMBCS group that has a converter */ + +static const char * const OptGroupByteToCPName[ULMBCS_GRP_LAST + 1] = { + /* 0x0000 */ "lmb-excp", /* internal home for the LOTUS exceptions list */ + /* 0x0001 */ "ibm-850", + /* 0x0002 */ "ibm-851", + /* 0x0003 */ "windows-1255", + /* 0x0004 */ "windows-1256", + /* 0x0005 */ "windows-1251", + /* 0x0006 */ "ibm-852", + /* 0x0007 */ nullptr, /* Unused */ + /* 0x0008 */ "windows-1254", + /* 0x0009 */ nullptr, /* Control char HT */ + /* 0x000A */ nullptr, /* Control char LF */ + /* 0x000B */ "windows-874", + /* 0x000C */ nullptr, /* Unused */ + /* 0x000D */ nullptr, /* Control char CR */ + /* 0x000E */ nullptr, /* Unused */ + /* 0x000F */ nullptr, /* Control chars: 0x0F20 + C0/C1 character: algorithmic */ + /* 0x0010 */ "windows-932", + /* 0x0011 */ "windows-949", + /* 0x0012 */ "windows-950", + /* 0x0013 */ "windows-936" + + /* The rest are null, including the 0x0014 Unicode compatibility region + and 0x0019, the 1-2-3 system range control char */ +}; + + +/* That's approximately all the data that's needed for translating + LMBCS to Unicode. + + +However, to translate Unicode to LMBCS, we need some more support. + +That's because there are often more than one possible mappings from a Unicode +code point back into LMBCS. The first thing we do is look up into a table +to figure out if there are more than one possible mappings. This table, +arranged by Unicode values (including ranges) either lists which group +to use, or says that it could go into one or more of the SBCS sets, or +into one or more of the DBCS sets. (If the character exists in both DBCS & +SBCS, the table will place it in the SBCS sets, to make the LMBCS code point +length as small as possible. Here's the two special markers we use to indicate +ambiguous mappings: */ + +#define ULMBCS_AMBIGUOUS_SBCS 0x80 /* could fit in more than one + LMBCS sbcs native encoding + (example: most accented latin) */ +#define ULMBCS_AMBIGUOUS_MBCS 0x81 /* could fit in more than one + LMBCS mbcs native encoding + (example: Unihan) */ +#define ULMBCS_AMBIGUOUS_ALL 0x82 +/* And here's a simple way to see if a group falls in an appropriate range */ +#define ULMBCS_AMBIGUOUS_MATCH(agroup, xgroup) \ + ((((agroup) == ULMBCS_AMBIGUOUS_SBCS) && \ + (xgroup) < ULMBCS_DOUBLEOPTGROUP_START) || \ + (((agroup) == ULMBCS_AMBIGUOUS_MBCS) && \ + (xgroup) >= ULMBCS_DOUBLEOPTGROUP_START)) || \ + ((agroup) == ULMBCS_AMBIGUOUS_ALL) + + +/* The table & some code to use it: */ + + +static const struct _UniLMBCSGrpMap +{ + const char16_t uniStartRange; + const char16_t uniEndRange; + const ulmbcs_byte_t GrpType; +} UniLMBCSGrpMap[] += +{ + + {0x0001, 0x001F, ULMBCS_GRP_CTRL}, + {0x0080, 0x009F, ULMBCS_GRP_CTRL}, + {0x00A0, 0x00A6, ULMBCS_AMBIGUOUS_SBCS}, + {0x00A7, 0x00A8, ULMBCS_AMBIGUOUS_ALL}, + {0x00A9, 0x00AF, ULMBCS_AMBIGUOUS_SBCS}, + {0x00B0, 0x00B1, ULMBCS_AMBIGUOUS_ALL}, + {0x00B2, 0x00B3, ULMBCS_AMBIGUOUS_SBCS}, + {0x00B4, 0x00B4, ULMBCS_AMBIGUOUS_ALL}, + {0x00B5, 0x00B5, ULMBCS_AMBIGUOUS_SBCS}, + {0x00B6, 0x00B6, ULMBCS_AMBIGUOUS_ALL}, + {0x00B7, 0x00D6, ULMBCS_AMBIGUOUS_SBCS}, + {0x00D7, 0x00D7, ULMBCS_AMBIGUOUS_ALL}, + {0x00D8, 0x00F6, ULMBCS_AMBIGUOUS_SBCS}, + {0x00F7, 0x00F7, ULMBCS_AMBIGUOUS_ALL}, + {0x00F8, 0x01CD, ULMBCS_AMBIGUOUS_SBCS}, + {0x01CE, 0x01CE, ULMBCS_GRP_TW }, + {0x01CF, 0x02B9, ULMBCS_AMBIGUOUS_SBCS}, + {0x02BA, 0x02BA, ULMBCS_GRP_CN}, + {0x02BC, 0x02C8, ULMBCS_AMBIGUOUS_SBCS}, + {0x02C9, 0x02D0, ULMBCS_AMBIGUOUS_MBCS}, + {0x02D8, 0x02DD, ULMBCS_AMBIGUOUS_SBCS}, + {0x0384, 0x0390, ULMBCS_AMBIGUOUS_SBCS}, + {0x0391, 0x03A9, ULMBCS_AMBIGUOUS_ALL}, + {0x03AA, 0x03B0, ULMBCS_AMBIGUOUS_SBCS}, + {0x03B1, 0x03C9, ULMBCS_AMBIGUOUS_ALL}, + {0x03CA, 0x03CE, ULMBCS_AMBIGUOUS_SBCS}, + {0x0400, 0x0400, ULMBCS_GRP_RU}, + {0x0401, 0x0401, ULMBCS_AMBIGUOUS_ALL}, + {0x0402, 0x040F, ULMBCS_GRP_RU}, + {0x0410, 0x0431, ULMBCS_AMBIGUOUS_ALL}, + {0x0432, 0x044E, ULMBCS_GRP_RU}, + {0x044F, 0x044F, ULMBCS_AMBIGUOUS_ALL}, + {0x0450, 0x0491, ULMBCS_GRP_RU}, + {0x05B0, 0x05F2, ULMBCS_GRP_HE}, + {0x060C, 0x06AF, ULMBCS_GRP_AR}, + {0x0E01, 0x0E5B, ULMBCS_GRP_TH}, + {0x200C, 0x200F, ULMBCS_AMBIGUOUS_SBCS}, + {0x2010, 0x2010, ULMBCS_AMBIGUOUS_MBCS}, + {0x2013, 0x2014, ULMBCS_AMBIGUOUS_SBCS}, + {0x2015, 0x2015, ULMBCS_AMBIGUOUS_MBCS}, + {0x2016, 0x2016, ULMBCS_AMBIGUOUS_MBCS}, + {0x2017, 0x2017, ULMBCS_AMBIGUOUS_SBCS}, + {0x2018, 0x2019, ULMBCS_AMBIGUOUS_ALL}, + {0x201A, 0x201B, ULMBCS_AMBIGUOUS_SBCS}, + {0x201C, 0x201D, ULMBCS_AMBIGUOUS_ALL}, + {0x201E, 0x201F, ULMBCS_AMBIGUOUS_SBCS}, + {0x2020, 0x2021, ULMBCS_AMBIGUOUS_ALL}, + {0x2022, 0x2024, ULMBCS_AMBIGUOUS_SBCS}, + {0x2025, 0x2025, ULMBCS_AMBIGUOUS_MBCS}, + {0x2026, 0x2026, ULMBCS_AMBIGUOUS_ALL}, + {0x2027, 0x2027, ULMBCS_GRP_TW}, + {0x2030, 0x2030, ULMBCS_AMBIGUOUS_ALL}, + {0x2031, 0x2031, ULMBCS_AMBIGUOUS_SBCS}, + {0x2032, 0x2033, ULMBCS_AMBIGUOUS_MBCS}, + {0x2035, 0x2035, ULMBCS_AMBIGUOUS_MBCS}, + {0x2039, 0x203A, ULMBCS_AMBIGUOUS_SBCS}, + {0x203B, 0x203B, ULMBCS_AMBIGUOUS_MBCS}, + {0x203C, 0x203C, ULMBCS_GRP_EXCEPT}, + {0x2074, 0x2074, ULMBCS_GRP_KO}, + {0x207F, 0x207F, ULMBCS_GRP_EXCEPT}, + {0x2081, 0x2084, ULMBCS_GRP_KO}, + {0x20A4, 0x20AC, ULMBCS_AMBIGUOUS_SBCS}, + {0x2103, 0x2109, ULMBCS_AMBIGUOUS_MBCS}, + {0x2111, 0x2120, ULMBCS_AMBIGUOUS_SBCS}, + /*zhujin: upgrade, for regressiont test, spr HKIA4YHTSU*/ + {0x2121, 0x2121, ULMBCS_AMBIGUOUS_MBCS}, + {0x2122, 0x2126, ULMBCS_AMBIGUOUS_SBCS}, + {0x212B, 0x212B, ULMBCS_AMBIGUOUS_MBCS}, + {0x2135, 0x2135, ULMBCS_AMBIGUOUS_SBCS}, + {0x2153, 0x2154, ULMBCS_GRP_KO}, + {0x215B, 0x215E, ULMBCS_GRP_EXCEPT}, + {0x2160, 0x2179, ULMBCS_AMBIGUOUS_MBCS}, + {0x2190, 0x2193, ULMBCS_AMBIGUOUS_ALL}, + {0x2194, 0x2195, ULMBCS_GRP_EXCEPT}, + {0x2196, 0x2199, ULMBCS_AMBIGUOUS_MBCS}, + {0x21A8, 0x21A8, ULMBCS_GRP_EXCEPT}, + {0x21B8, 0x21B9, ULMBCS_GRP_CN}, + {0x21D0, 0x21D1, ULMBCS_GRP_EXCEPT}, + {0x21D2, 0x21D2, ULMBCS_AMBIGUOUS_MBCS}, + {0x21D3, 0x21D3, ULMBCS_GRP_EXCEPT}, + {0x21D4, 0x21D4, ULMBCS_AMBIGUOUS_MBCS}, + {0x21D5, 0x21D5, ULMBCS_GRP_EXCEPT}, + {0x21E7, 0x21E7, ULMBCS_GRP_CN}, + {0x2200, 0x2200, ULMBCS_AMBIGUOUS_MBCS}, + {0x2201, 0x2201, ULMBCS_GRP_EXCEPT}, + {0x2202, 0x2202, ULMBCS_AMBIGUOUS_MBCS}, + {0x2203, 0x2203, ULMBCS_AMBIGUOUS_MBCS}, + {0x2204, 0x2206, ULMBCS_GRP_EXCEPT}, + {0x2207, 0x2208, ULMBCS_AMBIGUOUS_MBCS}, + {0x2209, 0x220A, ULMBCS_GRP_EXCEPT}, + {0x220B, 0x220B, ULMBCS_AMBIGUOUS_MBCS}, + {0x220F, 0x2215, ULMBCS_AMBIGUOUS_MBCS}, + {0x2219, 0x2219, ULMBCS_GRP_EXCEPT}, + {0x221A, 0x221A, ULMBCS_AMBIGUOUS_MBCS}, + {0x221B, 0x221C, ULMBCS_GRP_EXCEPT}, + {0x221D, 0x221E, ULMBCS_AMBIGUOUS_MBCS}, + {0x221F, 0x221F, ULMBCS_GRP_EXCEPT}, + {0x2220, 0x2220, ULMBCS_AMBIGUOUS_MBCS}, + {0x2223, 0x222A, ULMBCS_AMBIGUOUS_MBCS}, + {0x222B, 0x223D, ULMBCS_AMBIGUOUS_MBCS}, + {0x2245, 0x2248, ULMBCS_GRP_EXCEPT}, + {0x224C, 0x224C, ULMBCS_GRP_TW}, + {0x2252, 0x2252, ULMBCS_AMBIGUOUS_MBCS}, + {0x2260, 0x2261, ULMBCS_AMBIGUOUS_MBCS}, + {0x2262, 0x2265, ULMBCS_GRP_EXCEPT}, + {0x2266, 0x226F, ULMBCS_AMBIGUOUS_MBCS}, + {0x2282, 0x2283, ULMBCS_AMBIGUOUS_MBCS}, + {0x2284, 0x2285, ULMBCS_GRP_EXCEPT}, + {0x2286, 0x2287, ULMBCS_AMBIGUOUS_MBCS}, + {0x2288, 0x2297, ULMBCS_GRP_EXCEPT}, + {0x2299, 0x22BF, ULMBCS_AMBIGUOUS_MBCS}, + {0x22C0, 0x22C0, ULMBCS_GRP_EXCEPT}, + {0x2310, 0x2310, ULMBCS_GRP_EXCEPT}, + {0x2312, 0x2312, ULMBCS_AMBIGUOUS_MBCS}, + {0x2318, 0x2321, ULMBCS_GRP_EXCEPT}, + {0x2318, 0x2321, ULMBCS_GRP_CN}, + {0x2460, 0x24E9, ULMBCS_AMBIGUOUS_MBCS}, + {0x2500, 0x2500, ULMBCS_AMBIGUOUS_SBCS}, + {0x2501, 0x2501, ULMBCS_AMBIGUOUS_MBCS}, + {0x2502, 0x2502, ULMBCS_AMBIGUOUS_ALL}, + {0x2503, 0x2503, ULMBCS_AMBIGUOUS_MBCS}, + {0x2504, 0x2505, ULMBCS_GRP_TW}, + {0x2506, 0x2665, ULMBCS_AMBIGUOUS_ALL}, + {0x2666, 0x2666, ULMBCS_GRP_EXCEPT}, + {0x2667, 0x2669, ULMBCS_AMBIGUOUS_SBCS}, + {0x266A, 0x266A, ULMBCS_AMBIGUOUS_ALL}, + {0x266B, 0x266C, ULMBCS_AMBIGUOUS_SBCS}, + {0x266D, 0x266D, ULMBCS_AMBIGUOUS_MBCS}, + {0x266E, 0x266E, ULMBCS_AMBIGUOUS_SBCS}, + {0x266F, 0x266F, ULMBCS_GRP_JA}, + {0x2670, 0x2E7F, ULMBCS_AMBIGUOUS_SBCS}, + {0x2E80, 0xF861, ULMBCS_AMBIGUOUS_MBCS}, + {0xF862, 0xF8FF, ULMBCS_GRP_EXCEPT}, + {0xF900, 0xFA2D, ULMBCS_AMBIGUOUS_MBCS}, + {0xFB00, 0xFEFF, ULMBCS_AMBIGUOUS_SBCS}, + {0xFF01, 0xFFEE, ULMBCS_AMBIGUOUS_MBCS}, + {0xFFFF, 0xFFFF, ULMBCS_GRP_UNICODE} +}; + +static ulmbcs_byte_t +FindLMBCSUniRange(char16_t uniChar) +{ + const struct _UniLMBCSGrpMap * pTable = UniLMBCSGrpMap; + + while (uniChar > pTable->uniEndRange) + { + pTable++; + } + + if (uniChar >= pTable->uniStartRange) + { + return pTable->GrpType; + } + return ULMBCS_GRP_UNICODE; +} + +/* +We also ask the creator of a converter to send in a preferred locale +that we can use in resolving ambiguous mappings. They send the locale +in as a string, and we map it, if possible, to one of the +LMBCS groups. We use this table, and the associated code, to +do the lookup: */ + +/************************************************** + This table maps locale ID's to LMBCS opt groups. + The default return is group 0x01. Note that for + performance reasons, the table is sorted in + increasing alphabetic order, with the notable + exception of zhTW. This is to force the check + for Traditonal Chinese before dropping back to + Simplified. + + Note too that the Latin-1 groups have been + commented out because it's the default, and + this shortens the table, allowing a serial + search to go quickly. + *************************************************/ + +static const struct _LocaleLMBCSGrpMap +{ + const char *LocaleID; + const ulmbcs_byte_t OptGroup; +} LocaleLMBCSGrpMap[] = +{ + {"ar", ULMBCS_GRP_AR}, + {"be", ULMBCS_GRP_RU}, + {"bg", ULMBCS_GRP_L2}, + /* {"ca", ULMBCS_GRP_L1}, */ + {"cs", ULMBCS_GRP_L2}, + /* {"da", ULMBCS_GRP_L1}, */ + /* {"de", ULMBCS_GRP_L1}, */ + {"el", ULMBCS_GRP_GR}, + /* {"en", ULMBCS_GRP_L1}, */ + /* {"es", ULMBCS_GRP_L1}, */ + /* {"et", ULMBCS_GRP_L1}, */ + /* {"fi", ULMBCS_GRP_L1}, */ + /* {"fr", ULMBCS_GRP_L1}, */ + {"he", ULMBCS_GRP_HE}, + {"hu", ULMBCS_GRP_L2}, + /* {"is", ULMBCS_GRP_L1}, */ + /* {"it", ULMBCS_GRP_L1}, */ + {"iw", ULMBCS_GRP_HE}, + {"ja", ULMBCS_GRP_JA}, + {"ko", ULMBCS_GRP_KO}, + /* {"lt", ULMBCS_GRP_L1}, */ + /* {"lv", ULMBCS_GRP_L1}, */ + {"mk", ULMBCS_GRP_RU}, + /* {"nl", ULMBCS_GRP_L1}, */ + /* {"no", ULMBCS_GRP_L1}, */ + {"pl", ULMBCS_GRP_L2}, + /* {"pt", ULMBCS_GRP_L1}, */ + {"ro", ULMBCS_GRP_L2}, + {"ru", ULMBCS_GRP_RU}, + {"sh", ULMBCS_GRP_L2}, + {"sk", ULMBCS_GRP_L2}, + {"sl", ULMBCS_GRP_L2}, + {"sq", ULMBCS_GRP_L2}, + {"sr", ULMBCS_GRP_RU}, + /* {"sv", ULMBCS_GRP_L1}, */ + {"th", ULMBCS_GRP_TH}, + {"tr", ULMBCS_GRP_TR}, + {"uk", ULMBCS_GRP_RU}, + /* {"vi", ULMBCS_GRP_L1}, */ + {"zhTW", ULMBCS_GRP_TW}, + {"zh", ULMBCS_GRP_CN}, + {nullptr, ULMBCS_GRP_L1} +}; + + +static ulmbcs_byte_t +FindLMBCSLocale(const char *LocaleID) +{ + const struct _LocaleLMBCSGrpMap *pTable = LocaleLMBCSGrpMap; + + if ((!LocaleID) || (!*LocaleID)) + { + return 0; + } + + while (pTable->LocaleID) + { + if (*pTable->LocaleID == *LocaleID) /* Check only first char for speed */ + { + /* First char matches - check whole name, for entry-length */ + if (uprv_strncmp(pTable->LocaleID, LocaleID, strlen(pTable->LocaleID)) == 0) + return pTable->OptGroup; + } + else + if (*pTable->LocaleID > *LocaleID) /* Sorted alphabetically - exit */ + break; + pTable++; + } + return ULMBCS_GRP_L1; +} + + +/* + Before we get to the main body of code, here's how we hook up to the rest + of ICU. ICU converters are required to define a structure that includes + some function pointers, and some common data, in the style of a C++ + vtable. There is also room in there for converter-specific data. LMBCS + uses that converter-specific data to keep track of the 12 subconverters + we use, the optimization group, and the group (if any) that matches the + locale. We have one structure instantiated for each of the 12 possible + optimization groups. To avoid typos & to avoid boring the reader, we + put the declarations of these structures and functions into macros. To see + the definitions of these structures, see unicode\ucnv_bld.h +*/ + +typedef struct + { + UConverterSharedData *OptGrpConverter[ULMBCS_GRP_LAST+1]; /* Converter per Opt. grp. */ + uint8_t OptGroup; /* default Opt. grp. for this LMBCS session */ + uint8_t localeConverterIndex; /* reasonable locale match for index */ + } +UConverterDataLMBCS; + +U_CDECL_BEGIN +static void U_CALLCONV _LMBCSClose(UConverter * _this); +U_CDECL_END + +#define DECLARE_LMBCS_DATA(n) \ +static const UConverterImpl _LMBCSImpl##n={\ + UCNV_LMBCS_##n,\ + nullptr,nullptr,\ + _LMBCSOpen##n,\ + _LMBCSClose,\ + nullptr,\ + _LMBCSToUnicodeWithOffsets,\ + _LMBCSToUnicodeWithOffsets,\ + _LMBCSFromUnicode,\ + _LMBCSFromUnicode,\ + nullptr,\ + nullptr,\ + nullptr,\ + nullptr,\ + _LMBCSSafeClone,\ + ucnv_getCompleteUnicodeSet,\ + nullptr,\ + nullptr\ +};\ +static const UConverterStaticData _LMBCSStaticData##n={\ + sizeof(UConverterStaticData),\ + "LMBCS-" #n,\ + 0, UCNV_IBM, UCNV_LMBCS_##n, 1, 3,\ + { 0x3f, 0, 0, 0 },1,false,false,0,0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} \ +};\ +const UConverterSharedData _LMBCSData##n= \ + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_LMBCSStaticData##n, &_LMBCSImpl##n); + + /* The only function we needed to duplicate 12 times was the 'open' +function, which will do basically the same thing except set a different +optimization group. So, we put the common stuff into a worker function, +and set up another macro to stamp out the 12 open functions:*/ +#define DEFINE_LMBCS_OPEN(n) \ +static void U_CALLCONV \ + _LMBCSOpen##n(UConverter* _this, UConverterLoadArgs* pArgs, UErrorCode* err) \ +{ _LMBCSOpenWorker(_this, pArgs, err, n); } + + + +/* Here's the open worker & the common close function */ +static void +_LMBCSOpenWorker(UConverter* _this, + UConverterLoadArgs *pArgs, + UErrorCode* err, + ulmbcs_byte_t OptGroup) +{ + UConverterDataLMBCS * extraInfo = (UConverterDataLMBCS*)uprv_malloc (sizeof (UConverterDataLMBCS)); + _this->extraInfo = extraInfo; + if(extraInfo != nullptr) + { + UConverterNamePieces stackPieces; + UConverterLoadArgs stackArgs= UCNV_LOAD_ARGS_INITIALIZER; + ulmbcs_byte_t i; + + uprv_memset(extraInfo, 0, sizeof(UConverterDataLMBCS)); + + stackArgs.onlyTestIsLoadable = pArgs->onlyTestIsLoadable; + + for (i=0; i <= ULMBCS_GRP_LAST && U_SUCCESS(*err); i++) + { + if(OptGroupByteToCPName[i] != nullptr) { + extraInfo->OptGrpConverter[i] = ucnv_loadSharedData(OptGroupByteToCPName[i], &stackPieces, &stackArgs, err); + } + } + + if(U_FAILURE(*err) || pArgs->onlyTestIsLoadable) { + _LMBCSClose(_this); + return; + } + extraInfo->OptGroup = OptGroup; + extraInfo->localeConverterIndex = FindLMBCSLocale(pArgs->locale); + } + else + { + *err = U_MEMORY_ALLOCATION_ERROR; + } +} + +U_CDECL_BEGIN +static void U_CALLCONV +_LMBCSClose(UConverter * _this) +{ + if (_this->extraInfo != nullptr) + { + ulmbcs_byte_t Ix; + UConverterDataLMBCS * extraInfo = (UConverterDataLMBCS *) _this->extraInfo; + + for (Ix=0; Ix <= ULMBCS_GRP_LAST; Ix++) + { + if (extraInfo->OptGrpConverter[Ix] != nullptr) + ucnv_unloadSharedDataIfReady(extraInfo->OptGrpConverter[Ix]); + } + if (!_this->isExtraLocal) { + uprv_free (_this->extraInfo); + _this->extraInfo = nullptr; + } + } +} + +typedef struct LMBCSClone { + UConverter cnv; + UConverterDataLMBCS lmbcs; +} LMBCSClone; + +static UConverter * U_CALLCONV +_LMBCSSafeClone(const UConverter *cnv, + void *stackBuffer, + int32_t *pBufferSize, + UErrorCode *status) { + (void)status; + LMBCSClone *newLMBCS; + UConverterDataLMBCS *extraInfo; + int32_t i; + + if(*pBufferSize<=0) { + *pBufferSize=(int32_t)sizeof(LMBCSClone); + return nullptr; + } + + extraInfo=(UConverterDataLMBCS *)cnv->extraInfo; + newLMBCS=(LMBCSClone *)stackBuffer; + + /* ucnv.c/ucnv_safeClone() copied the main UConverter already */ + + uprv_memcpy(&newLMBCS->lmbcs, extraInfo, sizeof(UConverterDataLMBCS)); + + /* share the subconverters */ + for(i = 0; i <= ULMBCS_GRP_LAST; ++i) { + if(extraInfo->OptGrpConverter[i] != nullptr) { + ucnv_incrementRefCount(extraInfo->OptGrpConverter[i]); + } + } + + newLMBCS->cnv.extraInfo = &newLMBCS->lmbcs; + newLMBCS->cnv.isExtraLocal = true; + return &newLMBCS->cnv; +} + +/* + * There used to be a _LMBCSGetUnicodeSet() function here (up to svn revision 20117) + * which added all code points except for U+F6xx + * because those cannot be represented in the Unicode group. + * However, it turns out that windows-950 has roundtrips for all of U+F6xx + * which means that LMBCS can convert all Unicode code points after all. + * We now simply use ucnv_getCompleteUnicodeSet(). + * + * This may need to be looked at again as Lotus uses _LMBCSGetUnicodeSet(). (091216) + */ + +/* + Here's the basic helper function that we use when converting from + Unicode to LMBCS, and we suspect that a Unicode character will fit into + one of the 12 groups. The return value is the number of bytes written + starting at pStartLMBCS (if any). +*/ + +static size_t +LMBCSConversionWorker ( + UConverterDataLMBCS * extraInfo, /* subconverters, opt & locale groups */ + ulmbcs_byte_t group, /* The group to try */ + ulmbcs_byte_t * pStartLMBCS, /* where to put the results */ + char16_t * pUniChar, /* The input unicode character */ + ulmbcs_byte_t * lastConverterIndex, /* output: track last successful group used */ + UBool * groups_tried /* output: track any unsuccessful groups */ +) +{ + ulmbcs_byte_t * pLMBCS = pStartLMBCS; + UConverterSharedData * xcnv = extraInfo->OptGrpConverter[group]; + + int bytesConverted; + uint32_t value; + ulmbcs_byte_t firstByte; + + U_ASSERT(xcnv); + U_ASSERT(group 0) { + firstByte = (ulmbcs_byte_t)(value >> ((bytesConverted - 1) * 8)); + } else { + /* most common failure mode is an unassigned character */ + groups_tried[group] = true; + return 0; + } + + *lastConverterIndex = group; + + /* All initial byte values in lower ascii range should have been caught by now, + except with the exception group. + */ + U_ASSERT((firstByte <= ULMBCS_C0END) || (firstByte >= ULMBCS_C1START) || (group == ULMBCS_GRP_EXCEPT)); + + /* use converted data: first write 0, 1 or two group bytes */ + if (group != ULMBCS_GRP_EXCEPT && extraInfo->OptGroup != group) + { + *pLMBCS++ = group; + if (bytesConverted == 1 && group >= ULMBCS_DOUBLEOPTGROUP_START) + { + *pLMBCS++ = group; + } + } + + /* don't emit control chars */ + if ( bytesConverted == 1 && firstByte < 0x20 ) + return 0; + + + /* then move over the converted data */ + switch(bytesConverted) + { + case 4: + *pLMBCS++ = (ulmbcs_byte_t)(value >> 24); + U_FALLTHROUGH; + case 3: + *pLMBCS++ = (ulmbcs_byte_t)(value >> 16); + U_FALLTHROUGH; + case 2: + *pLMBCS++ = (ulmbcs_byte_t)(value >> 8); + U_FALLTHROUGH; + case 1: + *pLMBCS++ = (ulmbcs_byte_t)value; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + + return (pLMBCS - pStartLMBCS); +} + + +/* This is a much simpler version of above, when we +know we are writing LMBCS using the Unicode group +*/ +static size_t +LMBCSConvertUni(ulmbcs_byte_t * pLMBCS, char16_t uniChar) +{ + /* encode into LMBCS Unicode range */ + uint8_t LowCh = (uint8_t)(uniChar & 0x00FF); + uint8_t HighCh = (uint8_t)(uniChar >> 8); + + *pLMBCS++ = ULMBCS_GRP_UNICODE; + + if (LowCh == 0) + { + *pLMBCS++ = ULMBCS_UNICOMPATZERO; + *pLMBCS++ = HighCh; + } + else + { + *pLMBCS++ = HighCh; + *pLMBCS++ = LowCh; + } + return ULMBCS_UNICODE_SIZE; +} + + + +/* The main Unicode to LMBCS conversion function */ +static void U_CALLCONV +_LMBCSFromUnicode(UConverterFromUnicodeArgs* args, + UErrorCode* err) +{ + ulmbcs_byte_t lastConverterIndex = 0; + char16_t uniChar; + ulmbcs_byte_t LMBCS[ULMBCS_CHARSIZE_MAX]; + ulmbcs_byte_t * pLMBCS; + int32_t bytes_written; + UBool groups_tried[ULMBCS_GRP_LAST+1]; + UConverterDataLMBCS * extraInfo = (UConverterDataLMBCS *) args->converter->extraInfo; + int sourceIndex = 0; + + /* Basic strategy: attempt to fill in local LMBCS 1-char buffer.(LMBCS) + If that succeeds, see if it will all fit into the target & copy it over + if it does. + + We try conversions in the following order: + + 1. Single-byte ascii & special fixed control chars (&null) + 2. Look up group in table & try that (could be + A) Unicode group + B) control group, + C) national encoding, + or ambiguous SBCS or MBCS group (on to step 4...) + + 3. If its ambiguous, try this order: + A) The optimization group + B) The locale group + C) The last group that succeeded with this string. + D) every other group that's relevant (single or double) + E) If its single-byte ambiguous, try the exceptions group + + 4. And as a grand fallback: Unicode + */ + + /*Fix for SPR#DJOE66JFN3 (Lotus)*/ + ulmbcs_byte_t OldConverterIndex = 0; + + while (args->source < args->sourceLimit && !U_FAILURE(*err)) + { + /*Fix for SPR#DJOE66JFN3 (Lotus)*/ + OldConverterIndex = extraInfo->localeConverterIndex; + + if (args->target >= args->targetLimit) + { + *err = U_BUFFER_OVERFLOW_ERROR; + break; + } + uniChar = *(args->source); + bytes_written = 0; + pLMBCS = LMBCS; + + /* check cases in rough order of how common they are, for speed */ + + /* single byte matches: strategy 1 */ + /*Fix for SPR#DJOE66JFN3 (Lotus)*/ + if((uniChar>=0x80) && (uniChar<=0xff) + /*Fix for SPR#JUYA6XAERU and TSAO7GL5NK (Lotus)*/ &&(uniChar!=0xB1) &&(uniChar!=0xD7) &&(uniChar!=0xF7) + &&(uniChar!=0xB0) &&(uniChar!=0xB4) &&(uniChar!=0xB6) &&(uniChar!=0xA7) &&(uniChar!=0xA8)) + { + extraInfo->localeConverterIndex = ULMBCS_GRP_L1; + } + if (((uniChar > ULMBCS_C0END) && (uniChar < ULMBCS_C1START)) || + uniChar == 0 || uniChar == ULMBCS_HT || uniChar == ULMBCS_CR || + uniChar == ULMBCS_LF || uniChar == ULMBCS_123SYSTEMRANGE + ) + { + *pLMBCS++ = (ulmbcs_byte_t ) uniChar; + bytes_written = 1; + } + + + if (!bytes_written) + { + /* Check by UNICODE range (Strategy 2) */ + ulmbcs_byte_t group = FindLMBCSUniRange(uniChar); + + if (group == ULMBCS_GRP_UNICODE) /* (Strategy 2A) */ + { + pLMBCS += LMBCSConvertUni(pLMBCS,uniChar); + + bytes_written = (int32_t)(pLMBCS - LMBCS); + } + else if (group == ULMBCS_GRP_CTRL) /* (Strategy 2B) */ + { + /* Handle control characters here */ + if (uniChar <= ULMBCS_C0END) + { + *pLMBCS++ = ULMBCS_GRP_CTRL; + *pLMBCS++ = (ulmbcs_byte_t)(ULMBCS_CTRLOFFSET + uniChar); + } + else if (uniChar >= ULMBCS_C1START && uniChar <= ULMBCS_C1START + ULMBCS_CTRLOFFSET) + { + *pLMBCS++ = ULMBCS_GRP_CTRL; + *pLMBCS++ = (ulmbcs_byte_t ) (uniChar & 0x00FF); + } + bytes_written = (int32_t)(pLMBCS - LMBCS); + } + else if (group < ULMBCS_GRP_UNICODE) /* (Strategy 2C) */ + { + /* a specific converter has been identified - use it */ + bytes_written = (int32_t)LMBCSConversionWorker ( + extraInfo, group, pLMBCS, &uniChar, + &lastConverterIndex, groups_tried); + } + if (!bytes_written) /* the ambiguous group cases (Strategy 3) */ + { + uprv_memset(groups_tried, 0, sizeof(groups_tried)); + + /* check for non-default optimization group (Strategy 3A )*/ + if ((extraInfo->OptGroup != 1) && (ULMBCS_AMBIGUOUS_MATCH(group, extraInfo->OptGroup))) + { + /*zhujin: upgrade, merge #39299 here (Lotus) */ + /*To make R5 compatible translation, look for exceptional group first for non-DBCS*/ + + if(extraInfo->localeConverterIndex < ULMBCS_DOUBLEOPTGROUP_START) + { + bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, + ULMBCS_GRP_L1, pLMBCS, &uniChar, + &lastConverterIndex, groups_tried); + + if(!bytes_written) + { + bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, + ULMBCS_GRP_EXCEPT, pLMBCS, &uniChar, + &lastConverterIndex, groups_tried); + } + if(!bytes_written) + { + bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, + extraInfo->localeConverterIndex, pLMBCS, &uniChar, + &lastConverterIndex, groups_tried); + } + } + else + { + bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, + extraInfo->localeConverterIndex, pLMBCS, &uniChar, + &lastConverterIndex, groups_tried); + } + } + /* check for locale optimization group (Strategy 3B) */ + if (!bytes_written && (extraInfo->localeConverterIndex) && (ULMBCS_AMBIGUOUS_MATCH(group, extraInfo->localeConverterIndex))) + { + bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, + extraInfo->localeConverterIndex, pLMBCS, &uniChar, &lastConverterIndex, groups_tried); + } + /* check for last optimization group used for this string (Strategy 3C) */ + if (!bytes_written && (lastConverterIndex) && (ULMBCS_AMBIGUOUS_MATCH(group, lastConverterIndex))) + { + bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, + lastConverterIndex, pLMBCS, &uniChar, &lastConverterIndex, groups_tried); + } + if (!bytes_written) + { + /* just check every possible matching converter (Strategy 3D) */ + ulmbcs_byte_t grp_start; + ulmbcs_byte_t grp_end; + ulmbcs_byte_t grp_ix; + grp_start = (ulmbcs_byte_t)((group == ULMBCS_AMBIGUOUS_MBCS) + ? ULMBCS_DOUBLEOPTGROUP_START + : ULMBCS_GRP_L1); + grp_end = (ulmbcs_byte_t)((group == ULMBCS_AMBIGUOUS_MBCS) + ? ULMBCS_GRP_LAST + : ULMBCS_GRP_TH); + if(group == ULMBCS_AMBIGUOUS_ALL) + { + grp_start = ULMBCS_GRP_L1; + grp_end = ULMBCS_GRP_LAST; + } + for (grp_ix = grp_start; + grp_ix <= grp_end && !bytes_written; + grp_ix++) + { + if (extraInfo->OptGrpConverter [grp_ix] && !groups_tried [grp_ix]) + { + bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, + grp_ix, pLMBCS, &uniChar, + &lastConverterIndex, groups_tried); + } + } + /* a final conversion fallback to the exceptions group if its likely + to be single byte (Strategy 3E) */ + if (!bytes_written && grp_start == ULMBCS_GRP_L1) + { + bytes_written = (int32_t)LMBCSConversionWorker (extraInfo, + ULMBCS_GRP_EXCEPT, pLMBCS, &uniChar, + &lastConverterIndex, groups_tried); + } + } + /* all of our other strategies failed. Fallback to Unicode. (Strategy 4)*/ + if (!bytes_written) + { + + pLMBCS += LMBCSConvertUni(pLMBCS, uniChar); + bytes_written = (int32_t)(pLMBCS - LMBCS); + } + } + } + + /* we have a translation. increment source and write as much as possible to target */ + args->source++; + pLMBCS = LMBCS; + while (args->target < args->targetLimit && bytes_written--) + { + *(args->target)++ = *pLMBCS++; + if (args->offsets) + { + *(args->offsets)++ = sourceIndex; + } + } + sourceIndex++; + if (bytes_written > 0) + { + /* write any bytes that didn't fit in target to the error buffer, + common code will move this to target if we get called back with + enough target room + */ + uint8_t * pErrorBuffer = args->converter->charErrorBuffer; + *err = U_BUFFER_OVERFLOW_ERROR; + args->converter->charErrorBufferLength = (int8_t)bytes_written; + while (bytes_written--) + { + *pErrorBuffer++ = *pLMBCS++; + } + } + /*Fix for SPR#DJOE66JFN3 (Lotus)*/ + extraInfo->localeConverterIndex = OldConverterIndex; + } +} + + +/* Now, the Unicode from LMBCS section */ + + +/* A function to call when we are looking at the Unicode group byte in LMBCS */ +static char16_t +GetUniFromLMBCSUni(char const ** ppLMBCSin) /* Called with LMBCS-style Unicode byte stream */ +{ + uint8_t HighCh = *(*ppLMBCSin)++; /* Big-endian Unicode in LMBCS compatibility group*/ + uint8_t LowCh = *(*ppLMBCSin)++; + + if (HighCh == ULMBCS_UNICOMPATZERO ) + { + HighCh = LowCh; + LowCh = 0; /* zero-byte in LSB special character */ + } + return (char16_t)((HighCh << 8) | LowCh); +} + + + +/* CHECK_SOURCE_LIMIT: Helper macro to verify that there are at least'index' + bytes left in source up to sourceLimit.Errors appropriately if not. + If we reach the limit, then update the source pointer to there to consume + all input as required by ICU converter semantics. +*/ + +#define CHECK_SOURCE_LIMIT(index) UPRV_BLOCK_MACRO_BEGIN { \ + if (args->source+index > args->sourceLimit) { \ + *err = U_TRUNCATED_CHAR_FOUND; \ + args->source = args->sourceLimit; \ + return 0xffff; \ + } \ +} UPRV_BLOCK_MACRO_END + +/* Return the Unicode representation for the current LMBCS character */ + +static UChar32 U_CALLCONV +_LMBCSGetNextUCharWorker(UConverterToUnicodeArgs* args, + UErrorCode* err) +{ + UChar32 uniChar = 0; /* an output UNICODE char */ + ulmbcs_byte_t CurByte; /* A byte from the input stream */ + + /* error check */ + if (args->source >= args->sourceLimit) + { + *err = U_ILLEGAL_ARGUMENT_ERROR; + return 0xffff; + } + /* Grab first byte & save address for error recovery */ + CurByte = *((ulmbcs_byte_t *) (args->source++)); + + /* + * at entry of each if clause: + * 1. 'CurByte' points at the first byte of a LMBCS character + * 2. '*source'points to the next byte of the source stream after 'CurByte' + * + * the job of each if clause is: + * 1. set '*source' to point at the beginning of next char (nop if LMBCS char is only 1 byte) + * 2. set 'uniChar' up with the right Unicode value, or set 'err' appropriately + */ + + /* First lets check the simple fixed values. */ + + if(((CurByte > ULMBCS_C0END) && (CurByte < ULMBCS_C1START)) /* ascii range */ + || (CurByte == 0) + || CurByte == ULMBCS_HT || CurByte == ULMBCS_CR + || CurByte == ULMBCS_LF || CurByte == ULMBCS_123SYSTEMRANGE) + { + uniChar = CurByte; + } + else + { + UConverterDataLMBCS * extraInfo; + ulmbcs_byte_t group; + UConverterSharedData *cnv; + + if (CurByte == ULMBCS_GRP_CTRL) /* Control character group - no opt group update */ + { + ulmbcs_byte_t C0C1byte; + CHECK_SOURCE_LIMIT(1); + C0C1byte = *(args->source)++; + uniChar = (C0C1byte < ULMBCS_C1START) ? C0C1byte - ULMBCS_CTRLOFFSET : C0C1byte; + } + else + if (CurByte == ULMBCS_GRP_UNICODE) /* Unicode compatibility group: BigEndian UTF16 */ + { + CHECK_SOURCE_LIMIT(2); + + /* don't check for error indicators fffe/ffff below */ + return GetUniFromLMBCSUni(&(args->source)); + } + else if (CurByte <= ULMBCS_CTRLOFFSET) + { + group = CurByte; /* group byte is in the source */ + extraInfo = (UConverterDataLMBCS *) args->converter->extraInfo; + if (group > ULMBCS_GRP_LAST || (cnv = extraInfo->OptGrpConverter[group]) == nullptr) + { + /* this is not a valid group byte - no converter*/ + *err = U_INVALID_CHAR_FOUND; + } + else if (group >= ULMBCS_DOUBLEOPTGROUP_START) /* double byte conversion */ + { + + CHECK_SOURCE_LIMIT(2); + + /* check for LMBCS doubled-group-byte case */ + if (*args->source == group) { + /* single byte */ + ++args->source; + uniChar = ucnv_MBCSSimpleGetNextUChar(cnv, args->source, 1, false); + ++args->source; + } else { + /* double byte */ + uniChar = ucnv_MBCSSimpleGetNextUChar(cnv, args->source, 2, false); + args->source += 2; + } + } + else { /* single byte conversion */ + CHECK_SOURCE_LIMIT(1); + CurByte = *(args->source)++; + + if (CurByte >= ULMBCS_C1START) + { + uniChar = _MBCS_SINGLE_SIMPLE_GET_NEXT_BMP(cnv, CurByte); + } + else + { + /* The non-optimizable oddballs where there is an explicit byte + * AND the second byte is not in the upper ascii range + */ + char bytes[2]; + + extraInfo = (UConverterDataLMBCS *) args->converter->extraInfo; + cnv = extraInfo->OptGrpConverter [ULMBCS_GRP_EXCEPT]; + + /* Lookup value must include opt group */ + bytes[0] = group; + bytes[1] = CurByte; + uniChar = ucnv_MBCSSimpleGetNextUChar(cnv, bytes, 2, false); + } + } + } + else if (CurByte >= ULMBCS_C1START) /* group byte is implicit */ + { + extraInfo = (UConverterDataLMBCS *) args->converter->extraInfo; + group = extraInfo->OptGroup; + cnv = extraInfo->OptGrpConverter[group]; + if (group >= ULMBCS_DOUBLEOPTGROUP_START) /* double byte conversion */ + { + if (!ucnv_MBCSIsLeadByte(cnv, CurByte)) + { + CHECK_SOURCE_LIMIT(0); + + /* let the MBCS conversion consume CurByte again */ + uniChar = ucnv_MBCSSimpleGetNextUChar(cnv, args->source - 1, 1, false); + } + else + { + CHECK_SOURCE_LIMIT(1); + /* let the MBCS conversion consume CurByte again */ + uniChar = ucnv_MBCSSimpleGetNextUChar(cnv, args->source - 1, 2, false); + ++args->source; + } + } + else /* single byte conversion */ + { + uniChar = _MBCS_SINGLE_SIMPLE_GET_NEXT_BMP(cnv, CurByte); + } + } + } + return uniChar; +} + + +/* The exported function that converts lmbcs to one or more + UChars - currently UTF-16 +*/ +static void U_CALLCONV +_LMBCSToUnicodeWithOffsets(UConverterToUnicodeArgs* args, + UErrorCode* err) +{ + char LMBCS [ULMBCS_CHARSIZE_MAX]; + char16_t uniChar; /* one output UNICODE char */ + const char * saveSource; /* beginning of current code point */ + const char * pStartLMBCS = args->source; /* beginning of whole string */ + const char * errSource = nullptr; /* pointer to actual input in case an error occurs */ + int8_t savebytes = 0; + + /* Process from source to limit, or until error */ + while (U_SUCCESS(*err) && args->sourceLimit > args->source && args->targetLimit > args->target) + { + saveSource = args->source; /* beginning of current code point */ + + if (args->converter->toULength) /* reassemble char from previous call */ + { + const char *saveSourceLimit; + size_t size_old = args->converter->toULength; + + /* limit from source is either remainder of temp buffer, or user limit on source */ + size_t size_new_maybe_1 = sizeof(LMBCS) - size_old; + size_t size_new_maybe_2 = args->sourceLimit - args->source; + size_t size_new = (size_new_maybe_1 < size_new_maybe_2) ? size_new_maybe_1 : size_new_maybe_2; + + + uprv_memcpy(LMBCS, args->converter->toUBytes, size_old); + uprv_memcpy(LMBCS + size_old, args->source, size_new); + saveSourceLimit = args->sourceLimit; + args->source = errSource = LMBCS; + args->sourceLimit = LMBCS+size_old+size_new; + savebytes = (int8_t)(size_old+size_new); + uniChar = (char16_t) _LMBCSGetNextUCharWorker(args, err); + args->source = saveSource + ((args->source - LMBCS) - size_old); + args->sourceLimit = saveSourceLimit; + + if (*err == U_TRUNCATED_CHAR_FOUND) + { + /* evil special case: source buffers so small a char spans more than 2 buffers */ + args->converter->toULength = savebytes; + uprv_memcpy(args->converter->toUBytes, LMBCS, savebytes); + args->source = args->sourceLimit; + *err = U_ZERO_ERROR; + return; + } + else + { + /* clear the partial-char marker */ + args->converter->toULength = 0; + } + } + else + { + errSource = saveSource; + uniChar = (char16_t) _LMBCSGetNextUCharWorker(args, err); + savebytes = (int8_t)(args->source - saveSource); + } + if (U_SUCCESS(*err)) + { + if (uniChar < 0xfffe) + { + *(args->target)++ = uniChar; + if(args->offsets) + { + *(args->offsets)++ = (int32_t)(saveSource - pStartLMBCS); + } + } + else if (uniChar == 0xfffe) + { + *err = U_INVALID_CHAR_FOUND; + } + else /* if (uniChar == 0xffff) */ + { + *err = U_ILLEGAL_CHAR_FOUND; + } + } + } + /* if target ran out before source, return U_BUFFER_OVERFLOW_ERROR */ + if (U_SUCCESS(*err) && args->sourceLimit > args->source && args->targetLimit <= args->target) + { + *err = U_BUFFER_OVERFLOW_ERROR; + } + else if (U_FAILURE(*err)) + { + /* If character incomplete or unmappable/illegal, store it in toUBytes[] */ + args->converter->toULength = savebytes; + if (savebytes > 0) { + uprv_memcpy(args->converter->toUBytes, errSource, savebytes); + } + if (*err == U_TRUNCATED_CHAR_FOUND) { + *err = U_ZERO_ERROR; + } + } +} + +/* And now, the macroized declarations of data & functions: */ +DEFINE_LMBCS_OPEN(1) +DEFINE_LMBCS_OPEN(2) +DEFINE_LMBCS_OPEN(3) +DEFINE_LMBCS_OPEN(4) +DEFINE_LMBCS_OPEN(5) +DEFINE_LMBCS_OPEN(6) +DEFINE_LMBCS_OPEN(8) +DEFINE_LMBCS_OPEN(11) +DEFINE_LMBCS_OPEN(16) +DEFINE_LMBCS_OPEN(17) +DEFINE_LMBCS_OPEN(18) +DEFINE_LMBCS_OPEN(19) + + +DECLARE_LMBCS_DATA(1) +DECLARE_LMBCS_DATA(2) +DECLARE_LMBCS_DATA(3) +DECLARE_LMBCS_DATA(4) +DECLARE_LMBCS_DATA(5) +DECLARE_LMBCS_DATA(6) +DECLARE_LMBCS_DATA(8) +DECLARE_LMBCS_DATA(11) +DECLARE_LMBCS_DATA(16) +DECLARE_LMBCS_DATA(17) +DECLARE_LMBCS_DATA(18) +DECLARE_LMBCS_DATA(19) + +U_CDECL_END + +#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ diff --git a/deps/icu-small/source/common/ucnv_set.cpp b/deps/icu-small/source/common/ucnv_set.cpp index 926cee0de810ea..639d8973f4e8f8 100644 --- a/deps/icu-small/source/common/ucnv_set.cpp +++ b/deps/icu-small/source/common/ucnv_set.cpp @@ -1,70 +1,70 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2003-2007, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: ucnv_set.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004sep07 -* created by: Markus W. Scherer -* -* Conversion API functions using USet (ucnv_getUnicodeSet()) -* moved here from ucnv.c for removing the dependency of other ucnv_ -* implementation functions on the USet implementation. -*/ - -#include "unicode/utypes.h" -#include "unicode/uset.h" -#include "unicode/ucnv.h" -#include "ucnv_bld.h" -#include "uset_imp.h" - -#if !UCONFIG_NO_CONVERSION - -U_CAPI void U_EXPORT2 -ucnv_getUnicodeSet(const UConverter *cnv, - USet *setFillIn, - UConverterUnicodeSet whichSet, - UErrorCode *pErrorCode) { - /* argument checking */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return; - } - if(cnv==NULL || setFillIn==NULL || whichSetsharedData->impl->getUnicodeSet==NULL) { - *pErrorCode=U_UNSUPPORTED_ERROR; - return; - } - - { - USetAdder sa={ - NULL, - uset_add, - uset_addRange, - uset_addString, - uset_remove, - uset_removeRange - }; - sa.set=setFillIn; - - /* empty the set */ - uset_clear(setFillIn); - - /* call the converter to add the code points it supports */ - cnv->sharedData->impl->getUnicodeSet(cnv, &sa, whichSet, pErrorCode); - } -} - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2003-2007, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: ucnv_set.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004sep07 +* created by: Markus W. Scherer +* +* Conversion API functions using USet (ucnv_getUnicodeSet()) +* moved here from ucnv.c for removing the dependency of other ucnv_ +* implementation functions on the USet implementation. +*/ + +#include "unicode/utypes.h" +#include "unicode/uset.h" +#include "unicode/ucnv.h" +#include "ucnv_bld.h" +#include "uset_imp.h" + +#if !UCONFIG_NO_CONVERSION + +U_CAPI void U_EXPORT2 +ucnv_getUnicodeSet(const UConverter *cnv, + USet *setFillIn, + UConverterUnicodeSet whichSet, + UErrorCode *pErrorCode) { + /* argument checking */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return; + } + if(cnv==nullptr || setFillIn==nullptr || whichSetsharedData->impl->getUnicodeSet==nullptr) { + *pErrorCode=U_UNSUPPORTED_ERROR; + return; + } + + { + USetAdder sa={ + nullptr, + uset_add, + uset_addRange, + uset_addString, + uset_remove, + uset_removeRange + }; + sa.set=setFillIn; + + /* empty the set */ + uset_clear(setFillIn); + + /* call the converter to add the code points it supports */ + cnv->sharedData->impl->getUnicodeSet(cnv, &sa, whichSet, pErrorCode); + } +} + +#endif diff --git a/deps/icu-small/source/common/ucnv_u16.cpp b/deps/icu-small/source/common/ucnv_u16.cpp index bebdede4c440ca..32dfcaf961ccc2 100644 --- a/deps/icu-small/source/common/ucnv_u16.cpp +++ b/deps/icu-small/source/common/ucnv_u16.cpp @@ -1,1579 +1,1579 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2002-2015, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: ucnv_u16.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002jul01 -* created by: Markus W. Scherer -* -* UTF-16 converter implementation. Used to be in ucnv_utf.c. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/uversion.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" -#include "cmemory.h" - -enum { - UCNV_NEED_TO_WRITE_BOM=1 -}; - -U_CDECL_BEGIN -/* - * The UTF-16 toUnicode implementation is also used for the Java-specific - * "with BOM" variants of UTF-16BE and UTF-16LE. - */ -static void U_CALLCONV -_UTF16ToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode); - -/* UTF-16BE ----------------------------------------------------------------- */ - -#if U_IS_BIG_ENDIAN -# define _UTF16PEFromUnicodeWithOffsets _UTF16BEFromUnicodeWithOffsets -#else -# define _UTF16PEFromUnicodeWithOffsets _UTF16LEFromUnicodeWithOffsets -#endif - - -static void U_CALLCONV -_UTF16BEFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const UChar *source; - char *target; - int32_t *offsets; - - uint32_t targetCapacity, length, sourceIndex; - UChar c, trail; - char overflow[4]; - - source=pArgs->source; - length=(int32_t)(pArgs->sourceLimit-source); - if(length<=0) { - /* no input, nothing to do */ - return; - } - - cnv=pArgs->converter; - - /* write the BOM if necessary */ - if(cnv->fromUnicodeStatus==UCNV_NEED_TO_WRITE_BOM) { - static const char bom[]={ (char)0xfeu, (char)0xffu }; - ucnv_fromUWriteBytes(cnv, - bom, 2, - &pArgs->target, pArgs->targetLimit, - &pArgs->offsets, -1, - pErrorCode); - cnv->fromUnicodeStatus=0; - } - - target=pArgs->target; - if(target >= pArgs->targetLimit) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - return; - } - - targetCapacity=(uint32_t)(pArgs->targetLimit-target); - offsets=pArgs->offsets; - sourceIndex=0; - - /* c!=0 indicates in several places outside the main loops that a surrogate was found */ - - if((c=(UChar)cnv->fromUChar32)!=0 && U16_IS_TRAIL(trail=*source) && targetCapacity>=4) { - /* the last buffer ended with a lead surrogate, output the surrogate pair */ - ++source; - --length; - target[0]=(uint8_t)(c>>8); - target[1]=(uint8_t)c; - target[2]=(uint8_t)(trail>>8); - target[3]=(uint8_t)trail; - target+=4; - targetCapacity-=4; - if(offsets!=NULL) { - *offsets++=-1; - *offsets++=-1; - *offsets++=-1; - *offsets++=-1; - } - sourceIndex=1; - cnv->fromUChar32=c=0; - } - - if(c==0) { - /* copy an even number of bytes for complete UChars */ - uint32_t count=2*length; - if(count>targetCapacity) { - count=targetCapacity&~1; - } - /* count is even */ - targetCapacity-=count; - count>>=1; - length-=count; - - if(offsets==NULL) { - while(count>0) { - c=*source++; - if(U16_IS_SINGLE(c)) { - target[0]=(uint8_t)(c>>8); - target[1]=(uint8_t)c; - target+=2; - } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && U16_IS_TRAIL(trail=*source)) { - ++source; - --count; - target[0]=(uint8_t)(c>>8); - target[1]=(uint8_t)c; - target[2]=(uint8_t)(trail>>8); - target[3]=(uint8_t)trail; - target+=4; - } else { - break; - } - --count; - } - } else { - while(count>0) { - c=*source++; - if(U16_IS_SINGLE(c)) { - target[0]=(uint8_t)(c>>8); - target[1]=(uint8_t)c; - target+=2; - *offsets++=sourceIndex; - *offsets++=sourceIndex++; - } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && U16_IS_TRAIL(trail=*source)) { - ++source; - --count; - target[0]=(uint8_t)(c>>8); - target[1]=(uint8_t)c; - target[2]=(uint8_t)(trail>>8); - target[3]=(uint8_t)trail; - target+=4; - *offsets++=sourceIndex; - *offsets++=sourceIndex; - *offsets++=sourceIndex; - *offsets++=sourceIndex; - sourceIndex+=2; - } else { - break; - } - --count; - } - } - - if(count==0) { - /* done with the loop for complete UChars */ - if(length>0 && targetCapacity>0) { - /* - * there is more input and some target capacity - - * it must be targetCapacity==1 because otherwise - * the above would have copied more; - * prepare for overflow output - */ - if(U16_IS_SINGLE(c=*source++)) { - overflow[0]=(char)(c>>8); - overflow[1]=(char)c; - length=2; /* 2 bytes to output */ - c=0; - /* } else { keep c for surrogate handling, length will be set there */ - } - } else { - length=0; - c=0; - } - } else { - /* keep c for surrogate handling, length will be set there */ - targetCapacity+=2*count; - } - } else { - length=0; /* from here on, length counts the bytes in overflow[] */ - } - - if(c!=0) { - /* - * c is a surrogate, and - * - source or target too short - * - or the surrogate is unmatched - */ - length=0; - if(U16_IS_SURROGATE_LEAD(c)) { - if(sourcesourceLimit) { - if(U16_IS_TRAIL(trail=*source)) { - /* output the surrogate pair, will overflow (see conditions comment above) */ - ++source; - overflow[0]=(char)(c>>8); - overflow[1]=(char)c; - overflow[2]=(char)(trail>>8); - overflow[3]=(char)trail; - length=4; /* 4 bytes to output */ - c=0; - } else { - /* unmatched lead surrogate */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - } else { - /* see if the trail surrogate is in the next buffer */ - } - } else { - /* unmatched trail surrogate */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - cnv->fromUChar32=c; - } - - if(length>0) { - /* output length bytes with overflow (length>targetCapacity>0) */ - ucnv_fromUWriteBytes(cnv, - overflow, length, - (char **)&target, pArgs->targetLimit, - &offsets, sourceIndex, - pErrorCode); - targetCapacity=(uint32_t)(pArgs->targetLimit-(char *)target); - } - - if(U_SUCCESS(*pErrorCode) && sourcesourceLimit && targetCapacity==0) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - - /* write back the updated pointers */ - pArgs->source=source; - pArgs->target=(char *)target; - pArgs->offsets=offsets; -} - -static void U_CALLCONV -_UTF16BEToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const uint8_t *source; - UChar *target; - int32_t *offsets; - - uint32_t targetCapacity, length, count, sourceIndex; - UChar c, trail; - - if(pArgs->converter->mode<8) { - _UTF16ToUnicodeWithOffsets(pArgs, pErrorCode); - return; - } - - cnv=pArgs->converter; - source=(const uint8_t *)pArgs->source; - length=(int32_t)((const uint8_t *)pArgs->sourceLimit-source); - if(length<=0 && cnv->toUnicodeStatus==0) { - /* no input, nothing to do */ - return; - } - - target=pArgs->target; - if(target >= pArgs->targetLimit) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - return; - } - - targetCapacity=(uint32_t)(pArgs->targetLimit-target); - offsets=pArgs->offsets; - sourceIndex=0; - c=0; - - /* complete a partial UChar or pair from the last call */ - if(cnv->toUnicodeStatus!=0) { - /* - * special case: single byte from a previous buffer, - * where the byte turned out not to belong to a trail surrogate - * and the preceding, unmatched lead surrogate was put into toUBytes[] - * for error handling - */ - cnv->toUBytes[0]=(uint8_t)cnv->toUnicodeStatus; - cnv->toULength=1; - cnv->toUnicodeStatus=0; - } - if((count=cnv->toULength)!=0) { - uint8_t *p=cnv->toUBytes; - do { - p[count++]=*source++; - ++sourceIndex; - --length; - if(count==2) { - c=((UChar)p[0]<<8)|p[1]; - if(U16_IS_SINGLE(c)) { - /* output the BMP code point */ - *target++=c; - if(offsets!=NULL) { - *offsets++=-1; - } - --targetCapacity; - count=0; - c=0; - break; - } else if(U16_IS_SURROGATE_LEAD(c)) { - /* continue collecting bytes for the trail surrogate */ - c=0; /* avoid unnecessary surrogate handling below */ - } else { - /* fall through to error handling for an unmatched trail surrogate */ - break; - } - } else if(count==4) { - c=((UChar)p[0]<<8)|p[1]; - trail=((UChar)p[2]<<8)|p[3]; - if(U16_IS_TRAIL(trail)) { - /* output the surrogate pair */ - *target++=c; - if(targetCapacity>=2) { - *target++=trail; - if(offsets!=NULL) { - *offsets++=-1; - *offsets++=-1; - } - targetCapacity-=2; - } else /* targetCapacity==1 */ { - targetCapacity=0; - cnv->UCharErrorBuffer[0]=trail; - cnv->UCharErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - count=0; - c=0; - break; - } else { - /* unmatched lead surrogate, handle here for consistent toUBytes[] */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - - /* back out reading the code unit after it */ - if(((const uint8_t *)pArgs->source-source)>=2) { - source-=2; - } else { - /* - * if the trail unit's first byte was in a previous buffer, then - * we need to put it into a special place because toUBytes[] will be - * used for the lead unit's bytes - */ - cnv->toUnicodeStatus=0x100|p[2]; - --source; - } - cnv->toULength=2; - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - pArgs->offsets=offsets; - return; - } - } - } while(length>0); - cnv->toULength=(int8_t)count; - } - - /* copy an even number of bytes for complete UChars */ - count=2*targetCapacity; - if(count>length) { - count=length&~1; - } - if(c==0 && count>0) { - length-=count; - count>>=1; - targetCapacity-=count; - if(offsets==NULL) { - do { - c=((UChar)source[0]<<8)|source[1]; - source+=2; - if(U16_IS_SINGLE(c)) { - *target++=c; - } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && - U16_IS_TRAIL(trail=((UChar)source[0]<<8)|source[1]) - ) { - source+=2; - --count; - *target++=c; - *target++=trail; - } else { - break; - } - } while(--count>0); - } else { - do { - c=((UChar)source[0]<<8)|source[1]; - source+=2; - if(U16_IS_SINGLE(c)) { - *target++=c; - *offsets++=sourceIndex; - sourceIndex+=2; - } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && - U16_IS_TRAIL(trail=((UChar)source[0]<<8)|source[1]) - ) { - source+=2; - --count; - *target++=c; - *target++=trail; - *offsets++=sourceIndex; - *offsets++=sourceIndex; - sourceIndex+=4; - } else { - break; - } - } while(--count>0); - } - - if(count==0) { - /* done with the loop for complete UChars */ - c=0; - } else { - /* keep c for surrogate handling, trail will be set there */ - length+=2*(count-1); /* one more byte pair was consumed than count decremented */ - targetCapacity+=count; - } - } - - if(c!=0) { - /* - * c is a surrogate, and - * - source or target too short - * - or the surrogate is unmatched - */ - cnv->toUBytes[0]=(uint8_t)(c>>8); - cnv->toUBytes[1]=(uint8_t)c; - cnv->toULength=2; - - if(U16_IS_SURROGATE_LEAD(c)) { - if(length>=2) { - if(U16_IS_TRAIL(trail=((UChar)source[0]<<8)|source[1])) { - /* output the surrogate pair, will overflow (see conditions comment above) */ - source+=2; - length-=2; - *target++=c; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - cnv->UCharErrorBuffer[0]=trail; - cnv->UCharErrorBufferLength=1; - cnv->toULength=0; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } else { - /* unmatched lead surrogate */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - } else { - /* see if the trail surrogate is in the next buffer */ - } - } else { - /* unmatched trail surrogate */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - } - - if(U_SUCCESS(*pErrorCode)) { - /* check for a remaining source byte */ - if(length>0) { - if(targetCapacity==0) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } else { - /* it must be length==1 because otherwise the above would have copied more */ - cnv->toUBytes[cnv->toULength++]=*source++; - } - } - } - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - pArgs->offsets=offsets; -} - -static UChar32 U_CALLCONV -_UTF16BEGetNextUChar(UConverterToUnicodeArgs *pArgs, UErrorCode *err) { - const uint8_t *s, *sourceLimit; - UChar32 c; - - if(pArgs->converter->mode<8) { - return UCNV_GET_NEXT_UCHAR_USE_TO_U; - } - - s=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - - if(s>=sourceLimit) { - /* no input */ - *err=U_INDEX_OUTOFBOUNDS_ERROR; - return 0xffff; - } - - if(s+2>sourceLimit) { - /* only one byte: truncated UChar */ - pArgs->converter->toUBytes[0]=*s++; - pArgs->converter->toULength=1; - pArgs->source=(const char *)s; - *err = U_TRUNCATED_CHAR_FOUND; - return 0xffff; - } - - /* get one UChar */ - c=((UChar32)*s<<8)|s[1]; - s+=2; - - /* check for a surrogate pair */ - if(U_IS_SURROGATE(c)) { - if(U16_IS_SURROGATE_LEAD(c)) { - if(s+2<=sourceLimit) { - UChar trail; - - /* get a second UChar and see if it is a trail surrogate */ - trail=((UChar)*s<<8)|s[1]; - if(U16_IS_TRAIL(trail)) { - c=U16_GET_SUPPLEMENTARY(c, trail); - s+=2; - } else { - /* unmatched lead surrogate */ - c=-2; - } - } else { - /* too few (2 or 3) bytes for a surrogate pair: truncated code point */ - uint8_t *bytes=pArgs->converter->toUBytes; - s-=2; - pArgs->converter->toULength=(int8_t)(sourceLimit-s); - do { - *bytes++=*s++; - } while(sconverter->toUBytes; - pArgs->converter->toULength=2; - *bytes=*(s-2); - bytes[1]=*(s-1); - - c=0xffff; - *err=U_ILLEGAL_CHAR_FOUND; - } - } - - pArgs->source=(const char *)s; - return c; -} - -static void U_CALLCONV -_UTF16BEReset(UConverter *cnv, UConverterResetChoice choice) { - if(choice<=UCNV_RESET_TO_UNICODE) { - /* reset toUnicode state */ - if(UCNV_GET_VERSION(cnv)==0) { - cnv->mode=8; /* no BOM handling */ - } else { - cnv->mode=0; /* Java-specific "UnicodeBig" requires BE BOM or no BOM */ - } - } - if(choice!=UCNV_RESET_TO_UNICODE && UCNV_GET_VERSION(cnv)==1) { - /* reset fromUnicode for "UnicodeBig": prepare to output the UTF-16BE BOM */ - cnv->fromUnicodeStatus=UCNV_NEED_TO_WRITE_BOM; - } -} - -static void U_CALLCONV -_UTF16BEOpen(UConverter *cnv, - UConverterLoadArgs *pArgs, - UErrorCode *pErrorCode) { - (void)pArgs; - if(UCNV_GET_VERSION(cnv)<=1) { - _UTF16BEReset(cnv, UCNV_RESET_BOTH); - } else { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - } -} - -static const char * U_CALLCONV -_UTF16BEGetName(const UConverter *cnv) { - if(UCNV_GET_VERSION(cnv)==0) { - return "UTF-16BE"; - } else { - return "UTF-16BE,version=1"; - } -} -U_CDECL_END - -static const UConverterImpl _UTF16BEImpl={ - UCNV_UTF16_BigEndian, - - NULL, - NULL, - - _UTF16BEOpen, - NULL, - _UTF16BEReset, - - _UTF16BEToUnicodeWithOffsets, - _UTF16BEToUnicodeWithOffsets, - _UTF16BEFromUnicodeWithOffsets, - _UTF16BEFromUnicodeWithOffsets, - _UTF16BEGetNextUChar, - - NULL, - _UTF16BEGetName, - NULL, - NULL, - ucnv_getNonSurrogateUnicodeSet, - - NULL, - NULL -}; - -static const UConverterStaticData _UTF16BEStaticData={ - sizeof(UConverterStaticData), - "UTF-16BE", - 1200, UCNV_IBM, UCNV_UTF16_BigEndian, 2, 2, - { 0xff, 0xfd, 0, 0 },2,false,false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - - -const UConverterSharedData _UTF16BEData= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF16BEStaticData, &_UTF16BEImpl); - -/* UTF-16LE ----------------------------------------------------------------- */ -U_CDECL_BEGIN -static void U_CALLCONV -_UTF16LEFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const UChar *source; - char *target; - int32_t *offsets; - - uint32_t targetCapacity, length, sourceIndex; - UChar c, trail; - char overflow[4]; - - source=pArgs->source; - length=(int32_t)(pArgs->sourceLimit-source); - if(length<=0) { - /* no input, nothing to do */ - return; - } - - cnv=pArgs->converter; - - /* write the BOM if necessary */ - if(cnv->fromUnicodeStatus==UCNV_NEED_TO_WRITE_BOM) { - static const char bom[]={ (char)0xffu, (char)0xfeu }; - ucnv_fromUWriteBytes(cnv, - bom, 2, - &pArgs->target, pArgs->targetLimit, - &pArgs->offsets, -1, - pErrorCode); - cnv->fromUnicodeStatus=0; - } - - target=pArgs->target; - if(target >= pArgs->targetLimit) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - return; - } - - targetCapacity=(uint32_t)(pArgs->targetLimit-pArgs->target); - offsets=pArgs->offsets; - sourceIndex=0; - - /* c!=0 indicates in several places outside the main loops that a surrogate was found */ - - if((c=(UChar)cnv->fromUChar32)!=0 && U16_IS_TRAIL(trail=*source) && targetCapacity>=4) { - /* the last buffer ended with a lead surrogate, output the surrogate pair */ - ++source; - --length; - target[0]=(uint8_t)c; - target[1]=(uint8_t)(c>>8); - target[2]=(uint8_t)trail; - target[3]=(uint8_t)(trail>>8); - target+=4; - targetCapacity-=4; - if(offsets!=NULL) { - *offsets++=-1; - *offsets++=-1; - *offsets++=-1; - *offsets++=-1; - } - sourceIndex=1; - cnv->fromUChar32=c=0; - } - - if(c==0) { - /* copy an even number of bytes for complete UChars */ - uint32_t count=2*length; - if(count>targetCapacity) { - count=targetCapacity&~1; - } - /* count is even */ - targetCapacity-=count; - count>>=1; - length-=count; - - if(offsets==NULL) { - while(count>0) { - c=*source++; - if(U16_IS_SINGLE(c)) { - target[0]=(uint8_t)c; - target[1]=(uint8_t)(c>>8); - target+=2; - } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && U16_IS_TRAIL(trail=*source)) { - ++source; - --count; - target[0]=(uint8_t)c; - target[1]=(uint8_t)(c>>8); - target[2]=(uint8_t)trail; - target[3]=(uint8_t)(trail>>8); - target+=4; - } else { - break; - } - --count; - } - } else { - while(count>0) { - c=*source++; - if(U16_IS_SINGLE(c)) { - target[0]=(uint8_t)c; - target[1]=(uint8_t)(c>>8); - target+=2; - *offsets++=sourceIndex; - *offsets++=sourceIndex++; - } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && U16_IS_TRAIL(trail=*source)) { - ++source; - --count; - target[0]=(uint8_t)c; - target[1]=(uint8_t)(c>>8); - target[2]=(uint8_t)trail; - target[3]=(uint8_t)(trail>>8); - target+=4; - *offsets++=sourceIndex; - *offsets++=sourceIndex; - *offsets++=sourceIndex; - *offsets++=sourceIndex; - sourceIndex+=2; - } else { - break; - } - --count; - } - } - - if(count==0) { - /* done with the loop for complete UChars */ - if(length>0 && targetCapacity>0) { - /* - * there is more input and some target capacity - - * it must be targetCapacity==1 because otherwise - * the above would have copied more; - * prepare for overflow output - */ - if(U16_IS_SINGLE(c=*source++)) { - overflow[0]=(char)c; - overflow[1]=(char)(c>>8); - length=2; /* 2 bytes to output */ - c=0; - /* } else { keep c for surrogate handling, length will be set there */ - } - } else { - length=0; - c=0; - } - } else { - /* keep c for surrogate handling, length will be set there */ - targetCapacity+=2*count; - } - } else { - length=0; /* from here on, length counts the bytes in overflow[] */ - } - - if(c!=0) { - /* - * c is a surrogate, and - * - source or target too short - * - or the surrogate is unmatched - */ - length=0; - if(U16_IS_SURROGATE_LEAD(c)) { - if(sourcesourceLimit) { - if(U16_IS_TRAIL(trail=*source)) { - /* output the surrogate pair, will overflow (see conditions comment above) */ - ++source; - overflow[0]=(char)c; - overflow[1]=(char)(c>>8); - overflow[2]=(char)trail; - overflow[3]=(char)(trail>>8); - length=4; /* 4 bytes to output */ - c=0; - } else { - /* unmatched lead surrogate */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - } else { - /* see if the trail surrogate is in the next buffer */ - } - } else { - /* unmatched trail surrogate */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - cnv->fromUChar32=c; - } - - if(length>0) { - /* output length bytes with overflow (length>targetCapacity>0) */ - ucnv_fromUWriteBytes(cnv, - overflow, length, - &target, pArgs->targetLimit, - &offsets, sourceIndex, - pErrorCode); - targetCapacity=(uint32_t)(pArgs->targetLimit-(char *)target); - } - - if(U_SUCCESS(*pErrorCode) && sourcesourceLimit && targetCapacity==0) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - - /* write back the updated pointers */ - pArgs->source=source; - pArgs->target=target; - pArgs->offsets=offsets; -} - -static void U_CALLCONV -_UTF16LEToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const uint8_t *source; - UChar *target; - int32_t *offsets; - - uint32_t targetCapacity, length, count, sourceIndex; - UChar c, trail; - - if(pArgs->converter->mode<8) { - _UTF16ToUnicodeWithOffsets(pArgs, pErrorCode); - return; - } - - cnv=pArgs->converter; - source=(const uint8_t *)pArgs->source; - length=(int32_t)((const uint8_t *)pArgs->sourceLimit-source); - if(length<=0 && cnv->toUnicodeStatus==0) { - /* no input, nothing to do */ - return; - } - - target=pArgs->target; - if(target >= pArgs->targetLimit) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - return; - } - - targetCapacity=(uint32_t)(pArgs->targetLimit-pArgs->target); - offsets=pArgs->offsets; - sourceIndex=0; - c=0; - - /* complete a partial UChar or pair from the last call */ - if(cnv->toUnicodeStatus!=0) { - /* - * special case: single byte from a previous buffer, - * where the byte turned out not to belong to a trail surrogate - * and the preceding, unmatched lead surrogate was put into toUBytes[] - * for error handling - */ - cnv->toUBytes[0]=(uint8_t)cnv->toUnicodeStatus; - cnv->toULength=1; - cnv->toUnicodeStatus=0; - } - if((count=cnv->toULength)!=0) { - uint8_t *p=cnv->toUBytes; - do { - p[count++]=*source++; - ++sourceIndex; - --length; - if(count==2) { - c=((UChar)p[1]<<8)|p[0]; - if(U16_IS_SINGLE(c)) { - /* output the BMP code point */ - *target++=c; - if(offsets!=NULL) { - *offsets++=-1; - } - --targetCapacity; - count=0; - c=0; - break; - } else if(U16_IS_SURROGATE_LEAD(c)) { - /* continue collecting bytes for the trail surrogate */ - c=0; /* avoid unnecessary surrogate handling below */ - } else { - /* fall through to error handling for an unmatched trail surrogate */ - break; - } - } else if(count==4) { - c=((UChar)p[1]<<8)|p[0]; - trail=((UChar)p[3]<<8)|p[2]; - if(U16_IS_TRAIL(trail)) { - /* output the surrogate pair */ - *target++=c; - if(targetCapacity>=2) { - *target++=trail; - if(offsets!=NULL) { - *offsets++=-1; - *offsets++=-1; - } - targetCapacity-=2; - } else /* targetCapacity==1 */ { - targetCapacity=0; - cnv->UCharErrorBuffer[0]=trail; - cnv->UCharErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - count=0; - c=0; - break; - } else { - /* unmatched lead surrogate, handle here for consistent toUBytes[] */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - - /* back out reading the code unit after it */ - if(((const uint8_t *)pArgs->source-source)>=2) { - source-=2; - } else { - /* - * if the trail unit's first byte was in a previous buffer, then - * we need to put it into a special place because toUBytes[] will be - * used for the lead unit's bytes - */ - cnv->toUnicodeStatus=0x100|p[2]; - --source; - } - cnv->toULength=2; - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - pArgs->offsets=offsets; - return; - } - } - } while(length>0); - cnv->toULength=(int8_t)count; - } - - /* copy an even number of bytes for complete UChars */ - count=2*targetCapacity; - if(count>length) { - count=length&~1; - } - if(c==0 && count>0) { - length-=count; - count>>=1; - targetCapacity-=count; - if(offsets==NULL) { - do { - c=((UChar)source[1]<<8)|source[0]; - source+=2; - if(U16_IS_SINGLE(c)) { - *target++=c; - } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && - U16_IS_TRAIL(trail=((UChar)source[1]<<8)|source[0]) - ) { - source+=2; - --count; - *target++=c; - *target++=trail; - } else { - break; - } - } while(--count>0); - } else { - do { - c=((UChar)source[1]<<8)|source[0]; - source+=2; - if(U16_IS_SINGLE(c)) { - *target++=c; - *offsets++=sourceIndex; - sourceIndex+=2; - } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && - U16_IS_TRAIL(trail=((UChar)source[1]<<8)|source[0]) - ) { - source+=2; - --count; - *target++=c; - *target++=trail; - *offsets++=sourceIndex; - *offsets++=sourceIndex; - sourceIndex+=4; - } else { - break; - } - } while(--count>0); - } - - if(count==0) { - /* done with the loop for complete UChars */ - c=0; - } else { - /* keep c for surrogate handling, trail will be set there */ - length+=2*(count-1); /* one more byte pair was consumed than count decremented */ - targetCapacity+=count; - } - } - - if(c!=0) { - /* - * c is a surrogate, and - * - source or target too short - * - or the surrogate is unmatched - */ - cnv->toUBytes[0]=(uint8_t)c; - cnv->toUBytes[1]=(uint8_t)(c>>8); - cnv->toULength=2; - - if(U16_IS_SURROGATE_LEAD(c)) { - if(length>=2) { - if(U16_IS_TRAIL(trail=((UChar)source[1]<<8)|source[0])) { - /* output the surrogate pair, will overflow (see conditions comment above) */ - source+=2; - length-=2; - *target++=c; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - cnv->UCharErrorBuffer[0]=trail; - cnv->UCharErrorBufferLength=1; - cnv->toULength=0; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } else { - /* unmatched lead surrogate */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - } else { - /* see if the trail surrogate is in the next buffer */ - } - } else { - /* unmatched trail surrogate */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - } - - if(U_SUCCESS(*pErrorCode)) { - /* check for a remaining source byte */ - if(length>0) { - if(targetCapacity==0) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } else { - /* it must be length==1 because otherwise the above would have copied more */ - cnv->toUBytes[cnv->toULength++]=*source++; - } - } - } - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - pArgs->offsets=offsets; -} - -static UChar32 U_CALLCONV -_UTF16LEGetNextUChar(UConverterToUnicodeArgs *pArgs, UErrorCode *err) { - const uint8_t *s, *sourceLimit; - UChar32 c; - - if(pArgs->converter->mode<8) { - return UCNV_GET_NEXT_UCHAR_USE_TO_U; - } - - s=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - - if(s>=sourceLimit) { - /* no input */ - *err=U_INDEX_OUTOFBOUNDS_ERROR; - return 0xffff; - } - - if(s+2>sourceLimit) { - /* only one byte: truncated UChar */ - pArgs->converter->toUBytes[0]=*s++; - pArgs->converter->toULength=1; - pArgs->source=(const char *)s; - *err = U_TRUNCATED_CHAR_FOUND; - return 0xffff; - } - - /* get one UChar */ - c=((UChar32)s[1]<<8)|*s; - s+=2; - - /* check for a surrogate pair */ - if(U_IS_SURROGATE(c)) { - if(U16_IS_SURROGATE_LEAD(c)) { - if(s+2<=sourceLimit) { - UChar trail; - - /* get a second UChar and see if it is a trail surrogate */ - trail=((UChar)s[1]<<8)|*s; - if(U16_IS_TRAIL(trail)) { - c=U16_GET_SUPPLEMENTARY(c, trail); - s+=2; - } else { - /* unmatched lead surrogate */ - c=-2; - } - } else { - /* too few (2 or 3) bytes for a surrogate pair: truncated code point */ - uint8_t *bytes=pArgs->converter->toUBytes; - s-=2; - pArgs->converter->toULength=(int8_t)(sourceLimit-s); - do { - *bytes++=*s++; - } while(sconverter->toUBytes; - pArgs->converter->toULength=2; - *bytes=*(s-2); - bytes[1]=*(s-1); - - c=0xffff; - *err=U_ILLEGAL_CHAR_FOUND; - } - } - - pArgs->source=(const char *)s; - return c; -} - -static void U_CALLCONV -_UTF16LEReset(UConverter *cnv, UConverterResetChoice choice) { - if(choice<=UCNV_RESET_TO_UNICODE) { - /* reset toUnicode state */ - if(UCNV_GET_VERSION(cnv)==0) { - cnv->mode=8; /* no BOM handling */ - } else { - cnv->mode=0; /* Java-specific "UnicodeLittle" requires LE BOM or no BOM */ - } - } - if(choice!=UCNV_RESET_TO_UNICODE && UCNV_GET_VERSION(cnv)==1) { - /* reset fromUnicode for "UnicodeLittle": prepare to output the UTF-16LE BOM */ - cnv->fromUnicodeStatus=UCNV_NEED_TO_WRITE_BOM; - } -} - -static void U_CALLCONV -_UTF16LEOpen(UConverter *cnv, - UConverterLoadArgs *pArgs, - UErrorCode *pErrorCode) { - (void)pArgs; - if(UCNV_GET_VERSION(cnv)<=1) { - _UTF16LEReset(cnv, UCNV_RESET_BOTH); - } else { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - } -} - -static const char * U_CALLCONV -_UTF16LEGetName(const UConverter *cnv) { - if(UCNV_GET_VERSION(cnv)==0) { - return "UTF-16LE"; - } else { - return "UTF-16LE,version=1"; - } -} -U_CDECL_END - -static const UConverterImpl _UTF16LEImpl={ - UCNV_UTF16_LittleEndian, - - NULL, - NULL, - - _UTF16LEOpen, - NULL, - _UTF16LEReset, - - _UTF16LEToUnicodeWithOffsets, - _UTF16LEToUnicodeWithOffsets, - _UTF16LEFromUnicodeWithOffsets, - _UTF16LEFromUnicodeWithOffsets, - _UTF16LEGetNextUChar, - - NULL, - _UTF16LEGetName, - NULL, - NULL, - ucnv_getNonSurrogateUnicodeSet, - - NULL, - NULL -}; - - -static const UConverterStaticData _UTF16LEStaticData={ - sizeof(UConverterStaticData), - "UTF-16LE", - 1202, UCNV_IBM, UCNV_UTF16_LittleEndian, 2, 2, - { 0xfd, 0xff, 0, 0 },2,false,false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - - -const UConverterSharedData _UTF16LEData= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF16LEStaticData, &_UTF16LEImpl); - -/* UTF-16 (Detect BOM) ------------------------------------------------------ */ - -/* - * Detect a BOM at the beginning of the stream and select UTF-16BE or UTF-16LE - * accordingly. - * This is a simpler version of the UTF-32 converter, with - * fewer states for shorter BOMs. - * - * State values: - * 0 initial state - * 1 saw first byte - * 2..5 - - * 6..7 see _UTF16ToUnicodeWithOffsets() comments in state 1 - * 8 UTF-16BE mode - * 9 UTF-16LE mode - * - * During detection: state==number of initial bytes seen so far. - * - * On output, emit U+FEFF as the first code point. - * - * Variants: - * - UTF-16,version=1 (Java "Unicode" encoding) treats a missing BOM as an error. - * - UTF-16BE,version=1 (Java "UnicodeBig" encoding) and - * UTF-16LE,version=1 (Java "UnicodeLittle" encoding) treat a reverse BOM as an error. - */ -U_CDECL_BEGIN -static void U_CALLCONV -_UTF16Reset(UConverter *cnv, UConverterResetChoice choice) { - if(choice<=UCNV_RESET_TO_UNICODE) { - /* reset toUnicode: state=0 */ - cnv->mode=0; - } - if(choice!=UCNV_RESET_TO_UNICODE) { - /* reset fromUnicode: prepare to output the UTF-16PE BOM */ - cnv->fromUnicodeStatus=UCNV_NEED_TO_WRITE_BOM; - } -} -U_CDECL_END -extern const UConverterSharedData _UTF16v2Data; -U_CDECL_BEGIN -static void U_CALLCONV -_UTF16Open(UConverter *cnv, - UConverterLoadArgs *pArgs, - UErrorCode *pErrorCode) { - if(UCNV_GET_VERSION(cnv)<=2) { - if(UCNV_GET_VERSION(cnv)==2 && !pArgs->onlyTestIsLoadable) { - /* - * Switch implementation, and switch the staticData that's different - * and was copied into the UConverter. - * (See ucnv_createConverterFromSharedData() in ucnv_bld.c.) - * UTF-16,version=2 fromUnicode() always writes a big-endian byte stream. - */ - cnv->sharedData=(UConverterSharedData*)&_UTF16v2Data; - uprv_memcpy(cnv->subChars, _UTF16v2Data.staticData->subChar, UCNV_MAX_SUBCHAR_LEN); - } - _UTF16Reset(cnv, UCNV_RESET_BOTH); - } else { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - } -} - -static const char * U_CALLCONV -_UTF16GetName(const UConverter *cnv) { - if(UCNV_GET_VERSION(cnv)==0) { - return "UTF-16"; - } else if(UCNV_GET_VERSION(cnv)==1) { - return "UTF-16,version=1"; - } else { - return "UTF-16,version=2"; - } -} -U_CDECL_END -extern const UConverterSharedData _UTF16Data; - -static inline bool IS_UTF16BE(const UConverter *cnv) { - return ((cnv)->sharedData == &_UTF16BEData); -} - -static inline bool IS_UTF16LE(const UConverter *cnv) { - return ((cnv)->sharedData == &_UTF16LEData); -} - -static inline bool IS_UTF16(const UConverter *cnv) { - return ((cnv)->sharedData==&_UTF16Data) || ((cnv)->sharedData == &_UTF16v2Data); -} - -U_CDECL_BEGIN -static void U_CALLCONV -_UTF16ToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv=pArgs->converter; - const char *source=pArgs->source; - const char *sourceLimit=pArgs->sourceLimit; - int32_t *offsets=pArgs->offsets; - - int32_t state, offsetDelta; - uint8_t b; - - state=cnv->mode; - - /* - * If we detect a BOM in this buffer, then we must add the BOM size to the - * offsets because the actual converter function will not see and count the BOM. - * offsetDelta will have the number of the BOM bytes that are in the current buffer. - */ - offsetDelta=0; - - while(sourcetoUBytes[0]=(uint8_t)*source++; - cnv->toULength=1; - state=1; - break; - case 1: - /* - * Only inside this switch case can the state variable - * temporarily take two additional values: - * 6: BOM error, continue with BE - * 7: BOM error, continue with LE - */ - b=*source; - if(cnv->toUBytes[0]==0xfe && b==0xff) { - if(IS_UTF16LE(cnv)) { - state=7; /* illegal reverse BOM for Java "UnicodeLittle" */ - } else { - state=8; /* detect UTF-16BE */ - } - } else if(cnv->toUBytes[0]==0xff && b==0xfe) { - if(IS_UTF16BE(cnv)) { - state=6; /* illegal reverse BOM for Java "UnicodeBig" */ - } else { - state=9; /* detect UTF-16LE */ - } - } else if((IS_UTF16(cnv) && UCNV_GET_VERSION(cnv)==1)) { - state=6; /* illegal missing BOM for Java "Unicode" */ - } - if(state>=8) { - /* BOM detected, consume it */ - ++source; - cnv->toULength=0; - offsetDelta=(int32_t)(source-pArgs->source); - } else if(state<6) { - /* ok: no BOM, and not a reverse BOM */ - if(source!=pArgs->source) { - /* reset the source for a correct first offset */ - source=pArgs->source; - cnv->toULength=0; - } - if(IS_UTF16LE(cnv)) { - /* Make Java "UnicodeLittle" default to LE. */ - state=9; - } else { - /* Make standard UTF-16 and Java "UnicodeBig" default to BE. */ - state=8; - } - } else { - /* - * error: missing BOM, or reverse BOM - * UTF-16,version=1: Java-specific "Unicode" requires a BOM. - * UTF-16BE,version=1: Java-specific "UnicodeBig" requires a BE BOM or no BOM. - * UTF-16LE,version=1: Java-specific "UnicodeLittle" requires an LE BOM or no BOM. - */ - /* report the non-BOM or reverse BOM as an illegal sequence */ - cnv->toUBytes[1]=b; - cnv->toULength=2; - pArgs->source=source+1; - /* continue with conversion if the callback resets the error */ - /* - * Make Java "Unicode" default to BE like standard UTF-16. - * Make Java "UnicodeBig" and "UnicodeLittle" default - * to their normal endiannesses. - */ - cnv->mode=state+2; - *pErrorCode=U_ILLEGAL_ESCAPE_SEQUENCE; - return; - } - /* convert the rest of the stream */ - cnv->mode=state; - continue; - case 8: - /* call UTF-16BE */ - pArgs->source=source; - _UTF16BEToUnicodeWithOffsets(pArgs, pErrorCode); - source=pArgs->source; - break; - case 9: - /* call UTF-16LE */ - pArgs->source=source; - _UTF16LEToUnicodeWithOffsets(pArgs, pErrorCode); - source=pArgs->source; - break; - default: - break; /* does not occur */ - } - } - - /* add BOM size to offsets - see comment at offsetDelta declaration */ - if(offsets!=NULL && offsetDelta!=0) { - int32_t *offsetsLimit=pArgs->offsets; - while(offsetssource=source; - - if(source==sourceLimit && pArgs->flush) { - /* handle truncated input */ - switch(state) { - case 0: - break; /* no input at all, nothing to do */ - case 8: - _UTF16BEToUnicodeWithOffsets(pArgs, pErrorCode); - break; - case 9: - _UTF16LEToUnicodeWithOffsets(pArgs, pErrorCode); - break; - default: - /* 0mode=state; -} - -static UChar32 U_CALLCONV -_UTF16GetNextUChar(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - switch(pArgs->converter->mode) { - case 8: - return _UTF16BEGetNextUChar(pArgs, pErrorCode); - case 9: - return _UTF16LEGetNextUChar(pArgs, pErrorCode); - default: - return UCNV_GET_NEXT_UCHAR_USE_TO_U; - } -} -U_CDECL_END - -static const UConverterImpl _UTF16Impl = { - UCNV_UTF16, - - NULL, - NULL, - - _UTF16Open, - NULL, - _UTF16Reset, - - _UTF16ToUnicodeWithOffsets, - _UTF16ToUnicodeWithOffsets, - _UTF16PEFromUnicodeWithOffsets, - _UTF16PEFromUnicodeWithOffsets, - _UTF16GetNextUChar, - - NULL, /* ### TODO implement getStarters for all Unicode encodings?! */ - _UTF16GetName, - NULL, - NULL, - ucnv_getNonSurrogateUnicodeSet, - - NULL, - NULL -}; - -static const UConverterStaticData _UTF16StaticData = { - sizeof(UConverterStaticData), - "UTF-16", - 1204, /* CCSID for BOM sensitive UTF-16 */ - UCNV_IBM, UCNV_UTF16, 2, 2, -#if U_IS_BIG_ENDIAN - { 0xff, 0xfd, 0, 0 }, 2, -#else - { 0xfd, 0xff, 0, 0 }, 2, -#endif - false, false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - -const UConverterSharedData _UTF16Data = - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF16StaticData, &_UTF16Impl); - -static const UConverterImpl _UTF16v2Impl = { - UCNV_UTF16, - - NULL, - NULL, - - _UTF16Open, - NULL, - _UTF16Reset, - - _UTF16ToUnicodeWithOffsets, - _UTF16ToUnicodeWithOffsets, - _UTF16BEFromUnicodeWithOffsets, - _UTF16BEFromUnicodeWithOffsets, - _UTF16GetNextUChar, - - NULL, /* ### TODO implement getStarters for all Unicode encodings?! */ - _UTF16GetName, - NULL, - NULL, - ucnv_getNonSurrogateUnicodeSet, - - NULL, - NULL -}; - -static const UConverterStaticData _UTF16v2StaticData = { - sizeof(UConverterStaticData), - "UTF-16,version=2", - 1204, /* CCSID for BOM sensitive UTF-16 */ - UCNV_IBM, UCNV_UTF16, 2, 2, - { 0xff, 0xfd, 0, 0 }, 2, - false, false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - -const UConverterSharedData _UTF16v2Data = - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF16v2StaticData, &_UTF16v2Impl); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2002-2015, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: ucnv_u16.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002jul01 +* created by: Markus W. Scherer +* +* UTF-16 converter implementation. Used to be in ucnv_utf.c. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/uversion.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" +#include "cmemory.h" + +enum { + UCNV_NEED_TO_WRITE_BOM=1 +}; + +U_CDECL_BEGIN +/* + * The UTF-16 toUnicode implementation is also used for the Java-specific + * "with BOM" variants of UTF-16BE and UTF-16LE. + */ +static void U_CALLCONV +_UTF16ToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode); + +/* UTF-16BE ----------------------------------------------------------------- */ + +#if U_IS_BIG_ENDIAN +# define _UTF16PEFromUnicodeWithOffsets _UTF16BEFromUnicodeWithOffsets +#else +# define _UTF16PEFromUnicodeWithOffsets _UTF16LEFromUnicodeWithOffsets +#endif + + +static void U_CALLCONV +_UTF16BEFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const char16_t *source; + char *target; + int32_t *offsets; + + uint32_t targetCapacity, length, sourceIndex; + char16_t c, trail; + char overflow[4]; + + source=pArgs->source; + length=(int32_t)(pArgs->sourceLimit-source); + if(length<=0) { + /* no input, nothing to do */ + return; + } + + cnv=pArgs->converter; + + /* write the BOM if necessary */ + if(cnv->fromUnicodeStatus==UCNV_NEED_TO_WRITE_BOM) { + static const char bom[]={ (char)0xfeu, (char)0xffu }; + ucnv_fromUWriteBytes(cnv, + bom, 2, + &pArgs->target, pArgs->targetLimit, + &pArgs->offsets, -1, + pErrorCode); + cnv->fromUnicodeStatus=0; + } + + target=pArgs->target; + if(target >= pArgs->targetLimit) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + return; + } + + targetCapacity=(uint32_t)(pArgs->targetLimit-target); + offsets=pArgs->offsets; + sourceIndex=0; + + /* c!=0 indicates in several places outside the main loops that a surrogate was found */ + + if((c=(char16_t)cnv->fromUChar32)!=0 && U16_IS_TRAIL(trail=*source) && targetCapacity>=4) { + /* the last buffer ended with a lead surrogate, output the surrogate pair */ + ++source; + --length; + target[0]=(uint8_t)(c>>8); + target[1]=(uint8_t)c; + target[2]=(uint8_t)(trail>>8); + target[3]=(uint8_t)trail; + target+=4; + targetCapacity-=4; + if(offsets!=nullptr) { + *offsets++=-1; + *offsets++=-1; + *offsets++=-1; + *offsets++=-1; + } + sourceIndex=1; + cnv->fromUChar32=c=0; + } + + if(c==0) { + /* copy an even number of bytes for complete UChars */ + uint32_t count=2*length; + if(count>targetCapacity) { + count=targetCapacity&~1; + } + /* count is even */ + targetCapacity-=count; + count>>=1; + length-=count; + + if(offsets==nullptr) { + while(count>0) { + c=*source++; + if(U16_IS_SINGLE(c)) { + target[0]=(uint8_t)(c>>8); + target[1]=(uint8_t)c; + target+=2; + } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && U16_IS_TRAIL(trail=*source)) { + ++source; + --count; + target[0]=(uint8_t)(c>>8); + target[1]=(uint8_t)c; + target[2]=(uint8_t)(trail>>8); + target[3]=(uint8_t)trail; + target+=4; + } else { + break; + } + --count; + } + } else { + while(count>0) { + c=*source++; + if(U16_IS_SINGLE(c)) { + target[0]=(uint8_t)(c>>8); + target[1]=(uint8_t)c; + target+=2; + *offsets++=sourceIndex; + *offsets++=sourceIndex++; + } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && U16_IS_TRAIL(trail=*source)) { + ++source; + --count; + target[0]=(uint8_t)(c>>8); + target[1]=(uint8_t)c; + target[2]=(uint8_t)(trail>>8); + target[3]=(uint8_t)trail; + target+=4; + *offsets++=sourceIndex; + *offsets++=sourceIndex; + *offsets++=sourceIndex; + *offsets++=sourceIndex; + sourceIndex+=2; + } else { + break; + } + --count; + } + } + + if(count==0) { + /* done with the loop for complete UChars */ + if(length>0 && targetCapacity>0) { + /* + * there is more input and some target capacity - + * it must be targetCapacity==1 because otherwise + * the above would have copied more; + * prepare for overflow output + */ + if(U16_IS_SINGLE(c=*source++)) { + overflow[0]=(char)(c>>8); + overflow[1]=(char)c; + length=2; /* 2 bytes to output */ + c=0; + /* } else { keep c for surrogate handling, length will be set there */ + } + } else { + length=0; + c=0; + } + } else { + /* keep c for surrogate handling, length will be set there */ + targetCapacity+=2*count; + } + } else { + length=0; /* from here on, length counts the bytes in overflow[] */ + } + + if(c!=0) { + /* + * c is a surrogate, and + * - source or target too short + * - or the surrogate is unmatched + */ + length=0; + if(U16_IS_SURROGATE_LEAD(c)) { + if(sourcesourceLimit) { + if(U16_IS_TRAIL(trail=*source)) { + /* output the surrogate pair, will overflow (see conditions comment above) */ + ++source; + overflow[0]=(char)(c>>8); + overflow[1]=(char)c; + overflow[2]=(char)(trail>>8); + overflow[3]=(char)trail; + length=4; /* 4 bytes to output */ + c=0; + } else { + /* unmatched lead surrogate */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + } else { + /* see if the trail surrogate is in the next buffer */ + } + } else { + /* unmatched trail surrogate */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + cnv->fromUChar32=c; + } + + if(length>0) { + /* output length bytes with overflow (length>targetCapacity>0) */ + ucnv_fromUWriteBytes(cnv, + overflow, length, + (char **)&target, pArgs->targetLimit, + &offsets, sourceIndex, + pErrorCode); + targetCapacity=(uint32_t)(pArgs->targetLimit-(char *)target); + } + + if(U_SUCCESS(*pErrorCode) && sourcesourceLimit && targetCapacity==0) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + + /* write back the updated pointers */ + pArgs->source=source; + pArgs->target=(char *)target; + pArgs->offsets=offsets; +} + +static void U_CALLCONV +_UTF16BEToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const uint8_t *source; + char16_t *target; + int32_t *offsets; + + uint32_t targetCapacity, length, count, sourceIndex; + char16_t c, trail; + + if(pArgs->converter->mode<8) { + _UTF16ToUnicodeWithOffsets(pArgs, pErrorCode); + return; + } + + cnv=pArgs->converter; + source=(const uint8_t *)pArgs->source; + length=(int32_t)((const uint8_t *)pArgs->sourceLimit-source); + if(length<=0 && cnv->toUnicodeStatus==0) { + /* no input, nothing to do */ + return; + } + + target=pArgs->target; + if(target >= pArgs->targetLimit) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + return; + } + + targetCapacity=(uint32_t)(pArgs->targetLimit-target); + offsets=pArgs->offsets; + sourceIndex=0; + c=0; + + /* complete a partial char16_t or pair from the last call */ + if(cnv->toUnicodeStatus!=0) { + /* + * special case: single byte from a previous buffer, + * where the byte turned out not to belong to a trail surrogate + * and the preceding, unmatched lead surrogate was put into toUBytes[] + * for error handling + */ + cnv->toUBytes[0]=(uint8_t)cnv->toUnicodeStatus; + cnv->toULength=1; + cnv->toUnicodeStatus=0; + } + if((count=cnv->toULength)!=0) { + uint8_t *p=cnv->toUBytes; + do { + p[count++]=*source++; + ++sourceIndex; + --length; + if(count==2) { + c=((char16_t)p[0]<<8)|p[1]; + if(U16_IS_SINGLE(c)) { + /* output the BMP code point */ + *target++=c; + if(offsets!=nullptr) { + *offsets++=-1; + } + --targetCapacity; + count=0; + c=0; + break; + } else if(U16_IS_SURROGATE_LEAD(c)) { + /* continue collecting bytes for the trail surrogate */ + c=0; /* avoid unnecessary surrogate handling below */ + } else { + /* fall through to error handling for an unmatched trail surrogate */ + break; + } + } else if(count==4) { + c=((char16_t)p[0]<<8)|p[1]; + trail=((char16_t)p[2]<<8)|p[3]; + if(U16_IS_TRAIL(trail)) { + /* output the surrogate pair */ + *target++=c; + if(targetCapacity>=2) { + *target++=trail; + if(offsets!=nullptr) { + *offsets++=-1; + *offsets++=-1; + } + targetCapacity-=2; + } else /* targetCapacity==1 */ { + targetCapacity=0; + cnv->UCharErrorBuffer[0]=trail; + cnv->UCharErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + count=0; + c=0; + break; + } else { + /* unmatched lead surrogate, handle here for consistent toUBytes[] */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + + /* back out reading the code unit after it */ + if(((const uint8_t *)pArgs->source-source)>=2) { + source-=2; + } else { + /* + * if the trail unit's first byte was in a previous buffer, then + * we need to put it into a special place because toUBytes[] will be + * used for the lead unit's bytes + */ + cnv->toUnicodeStatus=0x100|p[2]; + --source; + } + cnv->toULength=2; + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + pArgs->offsets=offsets; + return; + } + } + } while(length>0); + cnv->toULength=(int8_t)count; + } + + /* copy an even number of bytes for complete UChars */ + count=2*targetCapacity; + if(count>length) { + count=length&~1; + } + if(c==0 && count>0) { + length-=count; + count>>=1; + targetCapacity-=count; + if(offsets==nullptr) { + do { + c=((char16_t)source[0]<<8)|source[1]; + source+=2; + if(U16_IS_SINGLE(c)) { + *target++=c; + } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && + U16_IS_TRAIL(trail=((char16_t)source[0]<<8)|source[1]) + ) { + source+=2; + --count; + *target++=c; + *target++=trail; + } else { + break; + } + } while(--count>0); + } else { + do { + c=((char16_t)source[0]<<8)|source[1]; + source+=2; + if(U16_IS_SINGLE(c)) { + *target++=c; + *offsets++=sourceIndex; + sourceIndex+=2; + } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && + U16_IS_TRAIL(trail=((char16_t)source[0]<<8)|source[1]) + ) { + source+=2; + --count; + *target++=c; + *target++=trail; + *offsets++=sourceIndex; + *offsets++=sourceIndex; + sourceIndex+=4; + } else { + break; + } + } while(--count>0); + } + + if(count==0) { + /* done with the loop for complete UChars */ + c=0; + } else { + /* keep c for surrogate handling, trail will be set there */ + length+=2*(count-1); /* one more byte pair was consumed than count decremented */ + targetCapacity+=count; + } + } + + if(c!=0) { + /* + * c is a surrogate, and + * - source or target too short + * - or the surrogate is unmatched + */ + cnv->toUBytes[0]=(uint8_t)(c>>8); + cnv->toUBytes[1]=(uint8_t)c; + cnv->toULength=2; + + if(U16_IS_SURROGATE_LEAD(c)) { + if(length>=2) { + if(U16_IS_TRAIL(trail=((char16_t)source[0]<<8)|source[1])) { + /* output the surrogate pair, will overflow (see conditions comment above) */ + source+=2; + length-=2; + *target++=c; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + cnv->UCharErrorBuffer[0]=trail; + cnv->UCharErrorBufferLength=1; + cnv->toULength=0; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } else { + /* unmatched lead surrogate */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + } else { + /* see if the trail surrogate is in the next buffer */ + } + } else { + /* unmatched trail surrogate */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + } + + if(U_SUCCESS(*pErrorCode)) { + /* check for a remaining source byte */ + if(length>0) { + if(targetCapacity==0) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } else { + /* it must be length==1 because otherwise the above would have copied more */ + cnv->toUBytes[cnv->toULength++]=*source++; + } + } + } + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + pArgs->offsets=offsets; +} + +static UChar32 U_CALLCONV +_UTF16BEGetNextUChar(UConverterToUnicodeArgs *pArgs, UErrorCode *err) { + const uint8_t *s, *sourceLimit; + UChar32 c; + + if(pArgs->converter->mode<8) { + return UCNV_GET_NEXT_UCHAR_USE_TO_U; + } + + s=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + + if(s>=sourceLimit) { + /* no input */ + *err=U_INDEX_OUTOFBOUNDS_ERROR; + return 0xffff; + } + + if(s+2>sourceLimit) { + /* only one byte: truncated char16_t */ + pArgs->converter->toUBytes[0]=*s++; + pArgs->converter->toULength=1; + pArgs->source=(const char *)s; + *err = U_TRUNCATED_CHAR_FOUND; + return 0xffff; + } + + /* get one char16_t */ + c=((UChar32)*s<<8)|s[1]; + s+=2; + + /* check for a surrogate pair */ + if(U_IS_SURROGATE(c)) { + if(U16_IS_SURROGATE_LEAD(c)) { + if(s+2<=sourceLimit) { + char16_t trail; + + /* get a second char16_t and see if it is a trail surrogate */ + trail=((char16_t)*s<<8)|s[1]; + if(U16_IS_TRAIL(trail)) { + c=U16_GET_SUPPLEMENTARY(c, trail); + s+=2; + } else { + /* unmatched lead surrogate */ + c=-2; + } + } else { + /* too few (2 or 3) bytes for a surrogate pair: truncated code point */ + uint8_t *bytes=pArgs->converter->toUBytes; + s-=2; + pArgs->converter->toULength=(int8_t)(sourceLimit-s); + do { + *bytes++=*s++; + } while(sconverter->toUBytes; + pArgs->converter->toULength=2; + *bytes=*(s-2); + bytes[1]=*(s-1); + + c=0xffff; + *err=U_ILLEGAL_CHAR_FOUND; + } + } + + pArgs->source=(const char *)s; + return c; +} + +static void U_CALLCONV +_UTF16BEReset(UConverter *cnv, UConverterResetChoice choice) { + if(choice<=UCNV_RESET_TO_UNICODE) { + /* reset toUnicode state */ + if(UCNV_GET_VERSION(cnv)==0) { + cnv->mode=8; /* no BOM handling */ + } else { + cnv->mode=0; /* Java-specific "UnicodeBig" requires BE BOM or no BOM */ + } + } + if(choice!=UCNV_RESET_TO_UNICODE && UCNV_GET_VERSION(cnv)==1) { + /* reset fromUnicode for "UnicodeBig": prepare to output the UTF-16BE BOM */ + cnv->fromUnicodeStatus=UCNV_NEED_TO_WRITE_BOM; + } +} + +static void U_CALLCONV +_UTF16BEOpen(UConverter *cnv, + UConverterLoadArgs *pArgs, + UErrorCode *pErrorCode) { + (void)pArgs; + if(UCNV_GET_VERSION(cnv)<=1) { + _UTF16BEReset(cnv, UCNV_RESET_BOTH); + } else { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + } +} + +static const char * U_CALLCONV +_UTF16BEGetName(const UConverter *cnv) { + if(UCNV_GET_VERSION(cnv)==0) { + return "UTF-16BE"; + } else { + return "UTF-16BE,version=1"; + } +} +U_CDECL_END + +static const UConverterImpl _UTF16BEImpl={ + UCNV_UTF16_BigEndian, + + nullptr, + nullptr, + + _UTF16BEOpen, + nullptr, + _UTF16BEReset, + + _UTF16BEToUnicodeWithOffsets, + _UTF16BEToUnicodeWithOffsets, + _UTF16BEFromUnicodeWithOffsets, + _UTF16BEFromUnicodeWithOffsets, + _UTF16BEGetNextUChar, + + nullptr, + _UTF16BEGetName, + nullptr, + nullptr, + ucnv_getNonSurrogateUnicodeSet, + + nullptr, + nullptr +}; + +static const UConverterStaticData _UTF16BEStaticData={ + sizeof(UConverterStaticData), + "UTF-16BE", + 1200, UCNV_IBM, UCNV_UTF16_BigEndian, 2, 2, + { 0xff, 0xfd, 0, 0 },2,false,false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + + +const UConverterSharedData _UTF16BEData= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF16BEStaticData, &_UTF16BEImpl); + +/* UTF-16LE ----------------------------------------------------------------- */ +U_CDECL_BEGIN +static void U_CALLCONV +_UTF16LEFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const char16_t *source; + char *target; + int32_t *offsets; + + uint32_t targetCapacity, length, sourceIndex; + char16_t c, trail; + char overflow[4]; + + source=pArgs->source; + length=(int32_t)(pArgs->sourceLimit-source); + if(length<=0) { + /* no input, nothing to do */ + return; + } + + cnv=pArgs->converter; + + /* write the BOM if necessary */ + if(cnv->fromUnicodeStatus==UCNV_NEED_TO_WRITE_BOM) { + static const char bom[]={ (char)0xffu, (char)0xfeu }; + ucnv_fromUWriteBytes(cnv, + bom, 2, + &pArgs->target, pArgs->targetLimit, + &pArgs->offsets, -1, + pErrorCode); + cnv->fromUnicodeStatus=0; + } + + target=pArgs->target; + if(target >= pArgs->targetLimit) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + return; + } + + targetCapacity=(uint32_t)(pArgs->targetLimit-pArgs->target); + offsets=pArgs->offsets; + sourceIndex=0; + + /* c!=0 indicates in several places outside the main loops that a surrogate was found */ + + if((c=(char16_t)cnv->fromUChar32)!=0 && U16_IS_TRAIL(trail=*source) && targetCapacity>=4) { + /* the last buffer ended with a lead surrogate, output the surrogate pair */ + ++source; + --length; + target[0]=(uint8_t)c; + target[1]=(uint8_t)(c>>8); + target[2]=(uint8_t)trail; + target[3]=(uint8_t)(trail>>8); + target+=4; + targetCapacity-=4; + if(offsets!=nullptr) { + *offsets++=-1; + *offsets++=-1; + *offsets++=-1; + *offsets++=-1; + } + sourceIndex=1; + cnv->fromUChar32=c=0; + } + + if(c==0) { + /* copy an even number of bytes for complete UChars */ + uint32_t count=2*length; + if(count>targetCapacity) { + count=targetCapacity&~1; + } + /* count is even */ + targetCapacity-=count; + count>>=1; + length-=count; + + if(offsets==nullptr) { + while(count>0) { + c=*source++; + if(U16_IS_SINGLE(c)) { + target[0]=(uint8_t)c; + target[1]=(uint8_t)(c>>8); + target+=2; + } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && U16_IS_TRAIL(trail=*source)) { + ++source; + --count; + target[0]=(uint8_t)c; + target[1]=(uint8_t)(c>>8); + target[2]=(uint8_t)trail; + target[3]=(uint8_t)(trail>>8); + target+=4; + } else { + break; + } + --count; + } + } else { + while(count>0) { + c=*source++; + if(U16_IS_SINGLE(c)) { + target[0]=(uint8_t)c; + target[1]=(uint8_t)(c>>8); + target+=2; + *offsets++=sourceIndex; + *offsets++=sourceIndex++; + } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && U16_IS_TRAIL(trail=*source)) { + ++source; + --count; + target[0]=(uint8_t)c; + target[1]=(uint8_t)(c>>8); + target[2]=(uint8_t)trail; + target[3]=(uint8_t)(trail>>8); + target+=4; + *offsets++=sourceIndex; + *offsets++=sourceIndex; + *offsets++=sourceIndex; + *offsets++=sourceIndex; + sourceIndex+=2; + } else { + break; + } + --count; + } + } + + if(count==0) { + /* done with the loop for complete UChars */ + if(length>0 && targetCapacity>0) { + /* + * there is more input and some target capacity - + * it must be targetCapacity==1 because otherwise + * the above would have copied more; + * prepare for overflow output + */ + if(U16_IS_SINGLE(c=*source++)) { + overflow[0]=(char)c; + overflow[1]=(char)(c>>8); + length=2; /* 2 bytes to output */ + c=0; + /* } else { keep c for surrogate handling, length will be set there */ + } + } else { + length=0; + c=0; + } + } else { + /* keep c for surrogate handling, length will be set there */ + targetCapacity+=2*count; + } + } else { + length=0; /* from here on, length counts the bytes in overflow[] */ + } + + if(c!=0) { + /* + * c is a surrogate, and + * - source or target too short + * - or the surrogate is unmatched + */ + length=0; + if(U16_IS_SURROGATE_LEAD(c)) { + if(sourcesourceLimit) { + if(U16_IS_TRAIL(trail=*source)) { + /* output the surrogate pair, will overflow (see conditions comment above) */ + ++source; + overflow[0]=(char)c; + overflow[1]=(char)(c>>8); + overflow[2]=(char)trail; + overflow[3]=(char)(trail>>8); + length=4; /* 4 bytes to output */ + c=0; + } else { + /* unmatched lead surrogate */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + } else { + /* see if the trail surrogate is in the next buffer */ + } + } else { + /* unmatched trail surrogate */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + cnv->fromUChar32=c; + } + + if(length>0) { + /* output length bytes with overflow (length>targetCapacity>0) */ + ucnv_fromUWriteBytes(cnv, + overflow, length, + &target, pArgs->targetLimit, + &offsets, sourceIndex, + pErrorCode); + targetCapacity=(uint32_t)(pArgs->targetLimit-(char *)target); + } + + if(U_SUCCESS(*pErrorCode) && sourcesourceLimit && targetCapacity==0) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + + /* write back the updated pointers */ + pArgs->source=source; + pArgs->target=target; + pArgs->offsets=offsets; +} + +static void U_CALLCONV +_UTF16LEToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const uint8_t *source; + char16_t *target; + int32_t *offsets; + + uint32_t targetCapacity, length, count, sourceIndex; + char16_t c, trail; + + if(pArgs->converter->mode<8) { + _UTF16ToUnicodeWithOffsets(pArgs, pErrorCode); + return; + } + + cnv=pArgs->converter; + source=(const uint8_t *)pArgs->source; + length=(int32_t)((const uint8_t *)pArgs->sourceLimit-source); + if(length<=0 && cnv->toUnicodeStatus==0) { + /* no input, nothing to do */ + return; + } + + target=pArgs->target; + if(target >= pArgs->targetLimit) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + return; + } + + targetCapacity=(uint32_t)(pArgs->targetLimit-pArgs->target); + offsets=pArgs->offsets; + sourceIndex=0; + c=0; + + /* complete a partial char16_t or pair from the last call */ + if(cnv->toUnicodeStatus!=0) { + /* + * special case: single byte from a previous buffer, + * where the byte turned out not to belong to a trail surrogate + * and the preceding, unmatched lead surrogate was put into toUBytes[] + * for error handling + */ + cnv->toUBytes[0]=(uint8_t)cnv->toUnicodeStatus; + cnv->toULength=1; + cnv->toUnicodeStatus=0; + } + if((count=cnv->toULength)!=0) { + uint8_t *p=cnv->toUBytes; + do { + p[count++]=*source++; + ++sourceIndex; + --length; + if(count==2) { + c=((char16_t)p[1]<<8)|p[0]; + if(U16_IS_SINGLE(c)) { + /* output the BMP code point */ + *target++=c; + if(offsets!=nullptr) { + *offsets++=-1; + } + --targetCapacity; + count=0; + c=0; + break; + } else if(U16_IS_SURROGATE_LEAD(c)) { + /* continue collecting bytes for the trail surrogate */ + c=0; /* avoid unnecessary surrogate handling below */ + } else { + /* fall through to error handling for an unmatched trail surrogate */ + break; + } + } else if(count==4) { + c=((char16_t)p[1]<<8)|p[0]; + trail=((char16_t)p[3]<<8)|p[2]; + if(U16_IS_TRAIL(trail)) { + /* output the surrogate pair */ + *target++=c; + if(targetCapacity>=2) { + *target++=trail; + if(offsets!=nullptr) { + *offsets++=-1; + *offsets++=-1; + } + targetCapacity-=2; + } else /* targetCapacity==1 */ { + targetCapacity=0; + cnv->UCharErrorBuffer[0]=trail; + cnv->UCharErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + count=0; + c=0; + break; + } else { + /* unmatched lead surrogate, handle here for consistent toUBytes[] */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + + /* back out reading the code unit after it */ + if(((const uint8_t *)pArgs->source-source)>=2) { + source-=2; + } else { + /* + * if the trail unit's first byte was in a previous buffer, then + * we need to put it into a special place because toUBytes[] will be + * used for the lead unit's bytes + */ + cnv->toUnicodeStatus=0x100|p[2]; + --source; + } + cnv->toULength=2; + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + pArgs->offsets=offsets; + return; + } + } + } while(length>0); + cnv->toULength=(int8_t)count; + } + + /* copy an even number of bytes for complete UChars */ + count=2*targetCapacity; + if(count>length) { + count=length&~1; + } + if(c==0 && count>0) { + length-=count; + count>>=1; + targetCapacity-=count; + if(offsets==nullptr) { + do { + c=((char16_t)source[1]<<8)|source[0]; + source+=2; + if(U16_IS_SINGLE(c)) { + *target++=c; + } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && + U16_IS_TRAIL(trail=((char16_t)source[1]<<8)|source[0]) + ) { + source+=2; + --count; + *target++=c; + *target++=trail; + } else { + break; + } + } while(--count>0); + } else { + do { + c=((char16_t)source[1]<<8)|source[0]; + source+=2; + if(U16_IS_SINGLE(c)) { + *target++=c; + *offsets++=sourceIndex; + sourceIndex+=2; + } else if(U16_IS_SURROGATE_LEAD(c) && count>=2 && + U16_IS_TRAIL(trail=((char16_t)source[1]<<8)|source[0]) + ) { + source+=2; + --count; + *target++=c; + *target++=trail; + *offsets++=sourceIndex; + *offsets++=sourceIndex; + sourceIndex+=4; + } else { + break; + } + } while(--count>0); + } + + if(count==0) { + /* done with the loop for complete UChars */ + c=0; + } else { + /* keep c for surrogate handling, trail will be set there */ + length+=2*(count-1); /* one more byte pair was consumed than count decremented */ + targetCapacity+=count; + } + } + + if(c!=0) { + /* + * c is a surrogate, and + * - source or target too short + * - or the surrogate is unmatched + */ + cnv->toUBytes[0]=(uint8_t)c; + cnv->toUBytes[1]=(uint8_t)(c>>8); + cnv->toULength=2; + + if(U16_IS_SURROGATE_LEAD(c)) { + if(length>=2) { + if(U16_IS_TRAIL(trail=((char16_t)source[1]<<8)|source[0])) { + /* output the surrogate pair, will overflow (see conditions comment above) */ + source+=2; + length-=2; + *target++=c; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + cnv->UCharErrorBuffer[0]=trail; + cnv->UCharErrorBufferLength=1; + cnv->toULength=0; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } else { + /* unmatched lead surrogate */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + } else { + /* see if the trail surrogate is in the next buffer */ + } + } else { + /* unmatched trail surrogate */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + } + + if(U_SUCCESS(*pErrorCode)) { + /* check for a remaining source byte */ + if(length>0) { + if(targetCapacity==0) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } else { + /* it must be length==1 because otherwise the above would have copied more */ + cnv->toUBytes[cnv->toULength++]=*source++; + } + } + } + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + pArgs->offsets=offsets; +} + +static UChar32 U_CALLCONV +_UTF16LEGetNextUChar(UConverterToUnicodeArgs *pArgs, UErrorCode *err) { + const uint8_t *s, *sourceLimit; + UChar32 c; + + if(pArgs->converter->mode<8) { + return UCNV_GET_NEXT_UCHAR_USE_TO_U; + } + + s=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + + if(s>=sourceLimit) { + /* no input */ + *err=U_INDEX_OUTOFBOUNDS_ERROR; + return 0xffff; + } + + if(s+2>sourceLimit) { + /* only one byte: truncated char16_t */ + pArgs->converter->toUBytes[0]=*s++; + pArgs->converter->toULength=1; + pArgs->source=(const char *)s; + *err = U_TRUNCATED_CHAR_FOUND; + return 0xffff; + } + + /* get one char16_t */ + c=((UChar32)s[1]<<8)|*s; + s+=2; + + /* check for a surrogate pair */ + if(U_IS_SURROGATE(c)) { + if(U16_IS_SURROGATE_LEAD(c)) { + if(s+2<=sourceLimit) { + char16_t trail; + + /* get a second char16_t and see if it is a trail surrogate */ + trail=((char16_t)s[1]<<8)|*s; + if(U16_IS_TRAIL(trail)) { + c=U16_GET_SUPPLEMENTARY(c, trail); + s+=2; + } else { + /* unmatched lead surrogate */ + c=-2; + } + } else { + /* too few (2 or 3) bytes for a surrogate pair: truncated code point */ + uint8_t *bytes=pArgs->converter->toUBytes; + s-=2; + pArgs->converter->toULength=(int8_t)(sourceLimit-s); + do { + *bytes++=*s++; + } while(sconverter->toUBytes; + pArgs->converter->toULength=2; + *bytes=*(s-2); + bytes[1]=*(s-1); + + c=0xffff; + *err=U_ILLEGAL_CHAR_FOUND; + } + } + + pArgs->source=(const char *)s; + return c; +} + +static void U_CALLCONV +_UTF16LEReset(UConverter *cnv, UConverterResetChoice choice) { + if(choice<=UCNV_RESET_TO_UNICODE) { + /* reset toUnicode state */ + if(UCNV_GET_VERSION(cnv)==0) { + cnv->mode=8; /* no BOM handling */ + } else { + cnv->mode=0; /* Java-specific "UnicodeLittle" requires LE BOM or no BOM */ + } + } + if(choice!=UCNV_RESET_TO_UNICODE && UCNV_GET_VERSION(cnv)==1) { + /* reset fromUnicode for "UnicodeLittle": prepare to output the UTF-16LE BOM */ + cnv->fromUnicodeStatus=UCNV_NEED_TO_WRITE_BOM; + } +} + +static void U_CALLCONV +_UTF16LEOpen(UConverter *cnv, + UConverterLoadArgs *pArgs, + UErrorCode *pErrorCode) { + (void)pArgs; + if(UCNV_GET_VERSION(cnv)<=1) { + _UTF16LEReset(cnv, UCNV_RESET_BOTH); + } else { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + } +} + +static const char * U_CALLCONV +_UTF16LEGetName(const UConverter *cnv) { + if(UCNV_GET_VERSION(cnv)==0) { + return "UTF-16LE"; + } else { + return "UTF-16LE,version=1"; + } +} +U_CDECL_END + +static const UConverterImpl _UTF16LEImpl={ + UCNV_UTF16_LittleEndian, + + nullptr, + nullptr, + + _UTF16LEOpen, + nullptr, + _UTF16LEReset, + + _UTF16LEToUnicodeWithOffsets, + _UTF16LEToUnicodeWithOffsets, + _UTF16LEFromUnicodeWithOffsets, + _UTF16LEFromUnicodeWithOffsets, + _UTF16LEGetNextUChar, + + nullptr, + _UTF16LEGetName, + nullptr, + nullptr, + ucnv_getNonSurrogateUnicodeSet, + + nullptr, + nullptr +}; + + +static const UConverterStaticData _UTF16LEStaticData={ + sizeof(UConverterStaticData), + "UTF-16LE", + 1202, UCNV_IBM, UCNV_UTF16_LittleEndian, 2, 2, + { 0xfd, 0xff, 0, 0 },2,false,false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + + +const UConverterSharedData _UTF16LEData= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF16LEStaticData, &_UTF16LEImpl); + +/* UTF-16 (Detect BOM) ------------------------------------------------------ */ + +/* + * Detect a BOM at the beginning of the stream and select UTF-16BE or UTF-16LE + * accordingly. + * This is a simpler version of the UTF-32 converter, with + * fewer states for shorter BOMs. + * + * State values: + * 0 initial state + * 1 saw first byte + * 2..5 - + * 6..7 see _UTF16ToUnicodeWithOffsets() comments in state 1 + * 8 UTF-16BE mode + * 9 UTF-16LE mode + * + * During detection: state==number of initial bytes seen so far. + * + * On output, emit U+FEFF as the first code point. + * + * Variants: + * - UTF-16,version=1 (Java "Unicode" encoding) treats a missing BOM as an error. + * - UTF-16BE,version=1 (Java "UnicodeBig" encoding) and + * UTF-16LE,version=1 (Java "UnicodeLittle" encoding) treat a reverse BOM as an error. + */ +U_CDECL_BEGIN +static void U_CALLCONV +_UTF16Reset(UConverter *cnv, UConverterResetChoice choice) { + if(choice<=UCNV_RESET_TO_UNICODE) { + /* reset toUnicode: state=0 */ + cnv->mode=0; + } + if(choice!=UCNV_RESET_TO_UNICODE) { + /* reset fromUnicode: prepare to output the UTF-16PE BOM */ + cnv->fromUnicodeStatus=UCNV_NEED_TO_WRITE_BOM; + } +} +U_CDECL_END +extern const UConverterSharedData _UTF16v2Data; +U_CDECL_BEGIN +static void U_CALLCONV +_UTF16Open(UConverter *cnv, + UConverterLoadArgs *pArgs, + UErrorCode *pErrorCode) { + if(UCNV_GET_VERSION(cnv)<=2) { + if(UCNV_GET_VERSION(cnv)==2 && !pArgs->onlyTestIsLoadable) { + /* + * Switch implementation, and switch the staticData that's different + * and was copied into the UConverter. + * (See ucnv_createConverterFromSharedData() in ucnv_bld.c.) + * UTF-16,version=2 fromUnicode() always writes a big-endian byte stream. + */ + cnv->sharedData=(UConverterSharedData*)&_UTF16v2Data; + uprv_memcpy(cnv->subChars, _UTF16v2Data.staticData->subChar, UCNV_MAX_SUBCHAR_LEN); + } + _UTF16Reset(cnv, UCNV_RESET_BOTH); + } else { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + } +} + +static const char * U_CALLCONV +_UTF16GetName(const UConverter *cnv) { + if(UCNV_GET_VERSION(cnv)==0) { + return "UTF-16"; + } else if(UCNV_GET_VERSION(cnv)==1) { + return "UTF-16,version=1"; + } else { + return "UTF-16,version=2"; + } +} +U_CDECL_END +extern const UConverterSharedData _UTF16Data; + +static inline bool IS_UTF16BE(const UConverter *cnv) { + return ((cnv)->sharedData == &_UTF16BEData); +} + +static inline bool IS_UTF16LE(const UConverter *cnv) { + return ((cnv)->sharedData == &_UTF16LEData); +} + +static inline bool IS_UTF16(const UConverter *cnv) { + return ((cnv)->sharedData==&_UTF16Data) || ((cnv)->sharedData == &_UTF16v2Data); +} + +U_CDECL_BEGIN +static void U_CALLCONV +_UTF16ToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv=pArgs->converter; + const char *source=pArgs->source; + const char *sourceLimit=pArgs->sourceLimit; + int32_t *offsets=pArgs->offsets; + + int32_t state, offsetDelta; + uint8_t b; + + state=cnv->mode; + + /* + * If we detect a BOM in this buffer, then we must add the BOM size to the + * offsets because the actual converter function will not see and count the BOM. + * offsetDelta will have the number of the BOM bytes that are in the current buffer. + */ + offsetDelta=0; + + while(sourcetoUBytes[0]=(uint8_t)*source++; + cnv->toULength=1; + state=1; + break; + case 1: + /* + * Only inside this switch case can the state variable + * temporarily take two additional values: + * 6: BOM error, continue with BE + * 7: BOM error, continue with LE + */ + b=*source; + if(cnv->toUBytes[0]==0xfe && b==0xff) { + if(IS_UTF16LE(cnv)) { + state=7; /* illegal reverse BOM for Java "UnicodeLittle" */ + } else { + state=8; /* detect UTF-16BE */ + } + } else if(cnv->toUBytes[0]==0xff && b==0xfe) { + if(IS_UTF16BE(cnv)) { + state=6; /* illegal reverse BOM for Java "UnicodeBig" */ + } else { + state=9; /* detect UTF-16LE */ + } + } else if((IS_UTF16(cnv) && UCNV_GET_VERSION(cnv)==1)) { + state=6; /* illegal missing BOM for Java "Unicode" */ + } + if(state>=8) { + /* BOM detected, consume it */ + ++source; + cnv->toULength=0; + offsetDelta=(int32_t)(source-pArgs->source); + } else if(state<6) { + /* ok: no BOM, and not a reverse BOM */ + if(source!=pArgs->source) { + /* reset the source for a correct first offset */ + source=pArgs->source; + cnv->toULength=0; + } + if(IS_UTF16LE(cnv)) { + /* Make Java "UnicodeLittle" default to LE. */ + state=9; + } else { + /* Make standard UTF-16 and Java "UnicodeBig" default to BE. */ + state=8; + } + } else { + /* + * error: missing BOM, or reverse BOM + * UTF-16,version=1: Java-specific "Unicode" requires a BOM. + * UTF-16BE,version=1: Java-specific "UnicodeBig" requires a BE BOM or no BOM. + * UTF-16LE,version=1: Java-specific "UnicodeLittle" requires an LE BOM or no BOM. + */ + /* report the non-BOM or reverse BOM as an illegal sequence */ + cnv->toUBytes[1]=b; + cnv->toULength=2; + pArgs->source=source+1; + /* continue with conversion if the callback resets the error */ + /* + * Make Java "Unicode" default to BE like standard UTF-16. + * Make Java "UnicodeBig" and "UnicodeLittle" default + * to their normal endiannesses. + */ + cnv->mode=state+2; + *pErrorCode=U_ILLEGAL_ESCAPE_SEQUENCE; + return; + } + /* convert the rest of the stream */ + cnv->mode=state; + continue; + case 8: + /* call UTF-16BE */ + pArgs->source=source; + _UTF16BEToUnicodeWithOffsets(pArgs, pErrorCode); + source=pArgs->source; + break; + case 9: + /* call UTF-16LE */ + pArgs->source=source; + _UTF16LEToUnicodeWithOffsets(pArgs, pErrorCode); + source=pArgs->source; + break; + default: + break; /* does not occur */ + } + } + + /* add BOM size to offsets - see comment at offsetDelta declaration */ + if(offsets!=nullptr && offsetDelta!=0) { + int32_t *offsetsLimit=pArgs->offsets; + while(offsetssource=source; + + if(source==sourceLimit && pArgs->flush) { + /* handle truncated input */ + switch(state) { + case 0: + break; /* no input at all, nothing to do */ + case 8: + _UTF16BEToUnicodeWithOffsets(pArgs, pErrorCode); + break; + case 9: + _UTF16LEToUnicodeWithOffsets(pArgs, pErrorCode); + break; + default: + /* 0mode=state; +} + +static UChar32 U_CALLCONV +_UTF16GetNextUChar(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + switch(pArgs->converter->mode) { + case 8: + return _UTF16BEGetNextUChar(pArgs, pErrorCode); + case 9: + return _UTF16LEGetNextUChar(pArgs, pErrorCode); + default: + return UCNV_GET_NEXT_UCHAR_USE_TO_U; + } +} +U_CDECL_END + +static const UConverterImpl _UTF16Impl = { + UCNV_UTF16, + + nullptr, + nullptr, + + _UTF16Open, + nullptr, + _UTF16Reset, + + _UTF16ToUnicodeWithOffsets, + _UTF16ToUnicodeWithOffsets, + _UTF16PEFromUnicodeWithOffsets, + _UTF16PEFromUnicodeWithOffsets, + _UTF16GetNextUChar, + + nullptr, /* ### TODO implement getStarters for all Unicode encodings?! */ + _UTF16GetName, + nullptr, + nullptr, + ucnv_getNonSurrogateUnicodeSet, + + nullptr, + nullptr +}; + +static const UConverterStaticData _UTF16StaticData = { + sizeof(UConverterStaticData), + "UTF-16", + 1204, /* CCSID for BOM sensitive UTF-16 */ + UCNV_IBM, UCNV_UTF16, 2, 2, +#if U_IS_BIG_ENDIAN + { 0xff, 0xfd, 0, 0 }, 2, +#else + { 0xfd, 0xff, 0, 0 }, 2, +#endif + false, false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + +const UConverterSharedData _UTF16Data = + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF16StaticData, &_UTF16Impl); + +static const UConverterImpl _UTF16v2Impl = { + UCNV_UTF16, + + nullptr, + nullptr, + + _UTF16Open, + nullptr, + _UTF16Reset, + + _UTF16ToUnicodeWithOffsets, + _UTF16ToUnicodeWithOffsets, + _UTF16BEFromUnicodeWithOffsets, + _UTF16BEFromUnicodeWithOffsets, + _UTF16GetNextUChar, + + nullptr, /* ### TODO implement getStarters for all Unicode encodings?! */ + _UTF16GetName, + nullptr, + nullptr, + ucnv_getNonSurrogateUnicodeSet, + + nullptr, + nullptr +}; + +static const UConverterStaticData _UTF16v2StaticData = { + sizeof(UConverterStaticData), + "UTF-16,version=2", + 1204, /* CCSID for BOM sensitive UTF-16 */ + UCNV_IBM, UCNV_UTF16, 2, 2, + { 0xff, 0xfd, 0, 0 }, 2, + false, false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + +const UConverterSharedData _UTF16v2Data = + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF16v2StaticData, &_UTF16v2Impl); + +#endif diff --git a/deps/icu-small/source/common/ucnv_u32.cpp b/deps/icu-small/source/common/ucnv_u32.cpp index bc160b71dd6c8d..3448ecd77f6167 100644 --- a/deps/icu-small/source/common/ucnv_u32.cpp +++ b/deps/icu-small/source/common/ucnv_u32.cpp @@ -1,1253 +1,1253 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2002-2015, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: ucnv_u32.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002jul01 -* created by: Markus W. Scherer -* -* UTF-32 converter implementation. Used to be in ucnv_utf.c. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/utf.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" -#include "cmemory.h" - -#define MAXIMUM_UCS2 0x0000FFFF -#define MAXIMUM_UTF 0x0010FFFF -#define HALF_SHIFT 10 -#define HALF_BASE 0x0010000 -#define HALF_MASK 0x3FF -#define SURROGATE_HIGH_START 0xD800 -#define SURROGATE_LOW_START 0xDC00 - -/* -SURROGATE_LOW_START + HALF_BASE */ -#define SURROGATE_LOW_BASE 9216 - -enum { - UCNV_NEED_TO_WRITE_BOM=1 -}; - -/* UTF-32BE ----------------------------------------------------------------- */ -U_CDECL_BEGIN -static void U_CALLCONV -T_UConverter_toUnicode_UTF32_BE(UConverterToUnicodeArgs * args, - UErrorCode * err) -{ - const unsigned char *mySource = (unsigned char *) args->source; - UChar *myTarget = args->target; - const unsigned char *sourceLimit = (unsigned char *) args->sourceLimit; - const UChar *targetLimit = args->targetLimit; - unsigned char *toUBytes = args->converter->toUBytes; - uint32_t ch, i; - - /* Restore state of current sequence */ - if (args->converter->toULength > 0 && myTarget < targetLimit) { - i = args->converter->toULength; /* restore # of bytes consumed */ - args->converter->toULength = 0; - - ch = args->converter->toUnicodeStatus - 1;/*Stores the previously calculated ch from a previous call*/ - args->converter->toUnicodeStatus = 0; - goto morebytes; - } - - while (mySource < sourceLimit && myTarget < targetLimit) { - i = 0; - ch = 0; -morebytes: - while (i < sizeof(uint32_t)) { - if (mySource < sourceLimit) { - ch = (ch << 8) | (uint8_t)(*mySource); - toUBytes[i++] = (char) *(mySource++); - } - else { - /* stores a partially calculated target*/ - /* + 1 to make 0 a valid character */ - args->converter->toUnicodeStatus = ch + 1; - args->converter->toULength = (int8_t) i; - goto donefornow; - } - } - - if (ch <= MAXIMUM_UTF && !U_IS_SURROGATE(ch)) { - /* Normal valid byte when the loop has not prematurely terminated (i < inBytes) */ - if (ch <= MAXIMUM_UCS2) - { - /* fits in 16 bits */ - *(myTarget++) = (UChar) ch; - } - else { - /* write out the surrogates */ - *(myTarget++) = U16_LEAD(ch); - ch = U16_TRAIL(ch); - if (myTarget < targetLimit) { - *(myTarget++) = (UChar)ch; - } - else { - /* Put in overflow buffer (not handled here) */ - args->converter->UCharErrorBuffer[0] = (UChar) ch; - args->converter->UCharErrorBufferLength = 1; - *err = U_BUFFER_OVERFLOW_ERROR; - break; - } - } - } - else { - args->converter->toULength = (int8_t)i; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - -donefornow: - if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) { - /* End of target buffer */ - *err = U_BUFFER_OVERFLOW_ERROR; - } - - args->target = myTarget; - args->source = (const char *) mySource; -} - -static void U_CALLCONV -T_UConverter_toUnicode_UTF32_BE_OFFSET_LOGIC(UConverterToUnicodeArgs * args, - UErrorCode * err) -{ - const unsigned char *mySource = (unsigned char *) args->source; - UChar *myTarget = args->target; - int32_t *myOffsets = args->offsets; - const unsigned char *sourceLimit = (unsigned char *) args->sourceLimit; - const UChar *targetLimit = args->targetLimit; - unsigned char *toUBytes = args->converter->toUBytes; - uint32_t ch, i; - int32_t offsetNum = 0; - - /* Restore state of current sequence */ - if (args->converter->toULength > 0 && myTarget < targetLimit) { - i = args->converter->toULength; /* restore # of bytes consumed */ - args->converter->toULength = 0; - - ch = args->converter->toUnicodeStatus - 1;/*Stores the previously calculated ch from a previous call*/ - args->converter->toUnicodeStatus = 0; - goto morebytes; - } - - while (mySource < sourceLimit && myTarget < targetLimit) { - i = 0; - ch = 0; -morebytes: - while (i < sizeof(uint32_t)) { - if (mySource < sourceLimit) { - ch = (ch << 8) | (uint8_t)(*mySource); - toUBytes[i++] = (char) *(mySource++); - } - else { - /* stores a partially calculated target*/ - /* + 1 to make 0 a valid character */ - args->converter->toUnicodeStatus = ch + 1; - args->converter->toULength = (int8_t) i; - goto donefornow; - } - } - - if (ch <= MAXIMUM_UTF && !U_IS_SURROGATE(ch)) { - /* Normal valid byte when the loop has not prematurely terminated (i < inBytes) */ - if (ch <= MAXIMUM_UCS2) { - /* fits in 16 bits */ - *(myTarget++) = (UChar) ch; - *(myOffsets++) = offsetNum; - } - else { - /* write out the surrogates */ - *(myTarget++) = U16_LEAD(ch); - *myOffsets++ = offsetNum; - ch = U16_TRAIL(ch); - if (myTarget < targetLimit) - { - *(myTarget++) = (UChar)ch; - *(myOffsets++) = offsetNum; - } - else { - /* Put in overflow buffer (not handled here) */ - args->converter->UCharErrorBuffer[0] = (UChar) ch; - args->converter->UCharErrorBufferLength = 1; - *err = U_BUFFER_OVERFLOW_ERROR; - break; - } - } - } - else { - args->converter->toULength = (int8_t)i; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - offsetNum += i; - } - -donefornow: - if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) - { - /* End of target buffer */ - *err = U_BUFFER_OVERFLOW_ERROR; - } - - args->target = myTarget; - args->source = (const char *) mySource; - args->offsets = myOffsets; -} - -static void U_CALLCONV -T_UConverter_fromUnicode_UTF32_BE(UConverterFromUnicodeArgs * args, - UErrorCode * err) -{ - const UChar *mySource = args->source; - unsigned char *myTarget; - const UChar *sourceLimit = args->sourceLimit; - const unsigned char *targetLimit = (unsigned char *) args->targetLimit; - UChar32 ch, ch2; - unsigned int indexToWrite; - unsigned char temp[sizeof(uint32_t)]; - - if(mySource >= sourceLimit) { - /* no input, nothing to do */ - return; - } - - /* write the BOM if necessary */ - if(args->converter->fromUnicodeStatus==UCNV_NEED_TO_WRITE_BOM) { - static const char bom[]={ 0, 0, (char)0xfeu, (char)0xffu }; - ucnv_fromUWriteBytes(args->converter, - bom, 4, - &args->target, args->targetLimit, - &args->offsets, -1, - err); - args->converter->fromUnicodeStatus=0; - } - - myTarget = (unsigned char *) args->target; - temp[0] = 0; - - if (args->converter->fromUChar32) { - ch = args->converter->fromUChar32; - args->converter->fromUChar32 = 0; - goto lowsurogate; - } - - while (mySource < sourceLimit && myTarget < targetLimit) { - ch = *(mySource++); - - if (U_IS_SURROGATE(ch)) { - if (U_IS_LEAD(ch)) { -lowsurogate: - if (mySource < sourceLimit) { - ch2 = *mySource; - if (U_IS_TRAIL(ch2)) { - ch = ((ch - SURROGATE_HIGH_START) << HALF_SHIFT) + ch2 + SURROGATE_LOW_BASE; - mySource++; - } - else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - args->converter->fromUChar32 = ch; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - else { - /* ran out of source */ - args->converter->fromUChar32 = ch; - if (args->flush) { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - *err = U_ILLEGAL_CHAR_FOUND; - } - break; - } - } - else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - args->converter->fromUChar32 = ch; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - - /* We cannot get any larger than 10FFFF because we are coming from UTF-16 */ - temp[1] = (uint8_t) (ch >> 16 & 0x1F); - temp[2] = (uint8_t) (ch >> 8); /* unsigned cast implicitly does (ch & FF) */ - temp[3] = (uint8_t) (ch); /* unsigned cast implicitly does (ch & FF) */ - - for (indexToWrite = 0; indexToWrite <= sizeof(uint32_t) - 1; indexToWrite++) { - if (myTarget < targetLimit) { - *(myTarget++) = temp[indexToWrite]; - } - else { - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = temp[indexToWrite]; - *err = U_BUFFER_OVERFLOW_ERROR; - } - } - } - - if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) { - *err = U_BUFFER_OVERFLOW_ERROR; - } - - args->target = (char *) myTarget; - args->source = mySource; -} - -static void U_CALLCONV -T_UConverter_fromUnicode_UTF32_BE_OFFSET_LOGIC(UConverterFromUnicodeArgs * args, - UErrorCode * err) -{ - const UChar *mySource = args->source; - unsigned char *myTarget; - int32_t *myOffsets; - const UChar *sourceLimit = args->sourceLimit; - const unsigned char *targetLimit = (unsigned char *) args->targetLimit; - UChar32 ch, ch2; - int32_t offsetNum = 0; - unsigned int indexToWrite; - unsigned char temp[sizeof(uint32_t)]; - - if(mySource >= sourceLimit) { - /* no input, nothing to do */ - return; - } - - /* write the BOM if necessary */ - if(args->converter->fromUnicodeStatus==UCNV_NEED_TO_WRITE_BOM) { - static const char bom[]={ 0, 0, (char)0xfeu, (char)0xffu }; - ucnv_fromUWriteBytes(args->converter, - bom, 4, - &args->target, args->targetLimit, - &args->offsets, -1, - err); - args->converter->fromUnicodeStatus=0; - } - - myTarget = (unsigned char *) args->target; - myOffsets = args->offsets; - temp[0] = 0; - - if (args->converter->fromUChar32) { - ch = args->converter->fromUChar32; - args->converter->fromUChar32 = 0; - goto lowsurogate; - } - - while (mySource < sourceLimit && myTarget < targetLimit) { - ch = *(mySource++); - - if (U_IS_SURROGATE(ch)) { - if (U_IS_LEAD(ch)) { -lowsurogate: - if (mySource < sourceLimit) { - ch2 = *mySource; - if (U_IS_TRAIL(ch2)) { - ch = ((ch - SURROGATE_HIGH_START) << HALF_SHIFT) + ch2 + SURROGATE_LOW_BASE; - mySource++; - } - else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - args->converter->fromUChar32 = ch; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - else { - /* ran out of source */ - args->converter->fromUChar32 = ch; - if (args->flush) { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - *err = U_ILLEGAL_CHAR_FOUND; - } - break; - } - } - else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - args->converter->fromUChar32 = ch; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - - /* We cannot get any larger than 10FFFF because we are coming from UTF-16 */ - temp[1] = (uint8_t) (ch >> 16 & 0x1F); - temp[2] = (uint8_t) (ch >> 8); /* unsigned cast implicitly does (ch & FF) */ - temp[3] = (uint8_t) (ch); /* unsigned cast implicitly does (ch & FF) */ - - for (indexToWrite = 0; indexToWrite <= sizeof(uint32_t) - 1; indexToWrite++) { - if (myTarget < targetLimit) { - *(myTarget++) = temp[indexToWrite]; - *(myOffsets++) = offsetNum; - } - else { - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = temp[indexToWrite]; - *err = U_BUFFER_OVERFLOW_ERROR; - } - } - offsetNum = offsetNum + 1 + (temp[1] != 0); - } - - if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) { - *err = U_BUFFER_OVERFLOW_ERROR; - } - - args->target = (char *) myTarget; - args->source = mySource; - args->offsets = myOffsets; -} - -static UChar32 U_CALLCONV -T_UConverter_getNextUChar_UTF32_BE(UConverterToUnicodeArgs* args, - UErrorCode* err) -{ - const uint8_t *mySource; - UChar32 myUChar; - int32_t length; - - mySource = (const uint8_t *)args->source; - if (mySource >= (const uint8_t *)args->sourceLimit) - { - /* no input */ - *err = U_INDEX_OUTOFBOUNDS_ERROR; - return 0xffff; - } - - length = (int32_t)((const uint8_t *)args->sourceLimit - mySource); - if (length < 4) - { - /* got a partial character */ - uprv_memcpy(args->converter->toUBytes, mySource, length); - args->converter->toULength = (int8_t)length; - args->source = (const char *)(mySource + length); - *err = U_TRUNCATED_CHAR_FOUND; - return 0xffff; - } - - /* Don't even try to do a direct cast because the value may be on an odd address. */ - myUChar = ((UChar32)mySource[0] << 24) - | ((UChar32)mySource[1] << 16) - | ((UChar32)mySource[2] << 8) - | ((UChar32)mySource[3]); - - args->source = (const char *)(mySource + 4); - if ((uint32_t)myUChar <= MAXIMUM_UTF && !U_IS_SURROGATE(myUChar)) { - return myUChar; - } - - uprv_memcpy(args->converter->toUBytes, mySource, 4); - args->converter->toULength = 4; - - *err = U_ILLEGAL_CHAR_FOUND; - return 0xffff; -} -U_CDECL_END -static const UConverterImpl _UTF32BEImpl = { - UCNV_UTF32_BigEndian, - - NULL, - NULL, - - NULL, - NULL, - NULL, - - T_UConverter_toUnicode_UTF32_BE, - T_UConverter_toUnicode_UTF32_BE_OFFSET_LOGIC, - T_UConverter_fromUnicode_UTF32_BE, - T_UConverter_fromUnicode_UTF32_BE_OFFSET_LOGIC, - T_UConverter_getNextUChar_UTF32_BE, - - NULL, - NULL, - NULL, - NULL, - ucnv_getNonSurrogateUnicodeSet, - - NULL, - NULL -}; - -/* The 1232 CCSID refers to any version of Unicode with any endianness of UTF-32 */ -static const UConverterStaticData _UTF32BEStaticData = { - sizeof(UConverterStaticData), - "UTF-32BE", - 1232, - UCNV_IBM, UCNV_UTF32_BigEndian, 4, 4, - { 0, 0, 0xff, 0xfd }, 4, false, false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - -const UConverterSharedData _UTF32BEData = - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF32BEStaticData, &_UTF32BEImpl); - -/* UTF-32LE ---------------------------------------------------------- */ -U_CDECL_BEGIN -static void U_CALLCONV -T_UConverter_toUnicode_UTF32_LE(UConverterToUnicodeArgs * args, - UErrorCode * err) -{ - const unsigned char *mySource = (unsigned char *) args->source; - UChar *myTarget = args->target; - const unsigned char *sourceLimit = (unsigned char *) args->sourceLimit; - const UChar *targetLimit = args->targetLimit; - unsigned char *toUBytes = args->converter->toUBytes; - uint32_t ch, i; - - /* Restore state of current sequence */ - if (args->converter->toULength > 0 && myTarget < targetLimit) - { - i = args->converter->toULength; /* restore # of bytes consumed */ - args->converter->toULength = 0; - - /* Stores the previously calculated ch from a previous call*/ - ch = args->converter->toUnicodeStatus - 1; - args->converter->toUnicodeStatus = 0; - goto morebytes; - } - - while (mySource < sourceLimit && myTarget < targetLimit) - { - i = 0; - ch = 0; -morebytes: - while (i < sizeof(uint32_t)) - { - if (mySource < sourceLimit) - { - ch |= ((uint8_t)(*mySource)) << (i * 8); - toUBytes[i++] = (char) *(mySource++); - } - else - { - /* stores a partially calculated target*/ - /* + 1 to make 0 a valid character */ - args->converter->toUnicodeStatus = ch + 1; - args->converter->toULength = (int8_t) i; - goto donefornow; - } - } - - if (ch <= MAXIMUM_UTF && !U_IS_SURROGATE(ch)) { - /* Normal valid byte when the loop has not prematurely terminated (i < inBytes) */ - if (ch <= MAXIMUM_UCS2) { - /* fits in 16 bits */ - *(myTarget++) = (UChar) ch; - } - else { - /* write out the surrogates */ - *(myTarget++) = U16_LEAD(ch); - ch = U16_TRAIL(ch); - if (myTarget < targetLimit) { - *(myTarget++) = (UChar)ch; - } - else { - /* Put in overflow buffer (not handled here) */ - args->converter->UCharErrorBuffer[0] = (UChar) ch; - args->converter->UCharErrorBufferLength = 1; - *err = U_BUFFER_OVERFLOW_ERROR; - break; - } - } - } - else { - args->converter->toULength = (int8_t)i; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - -donefornow: - if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) - { - /* End of target buffer */ - *err = U_BUFFER_OVERFLOW_ERROR; - } - - args->target = myTarget; - args->source = (const char *) mySource; -} - -static void U_CALLCONV -T_UConverter_toUnicode_UTF32_LE_OFFSET_LOGIC(UConverterToUnicodeArgs * args, - UErrorCode * err) -{ - const unsigned char *mySource = (unsigned char *) args->source; - UChar *myTarget = args->target; - int32_t *myOffsets = args->offsets; - const unsigned char *sourceLimit = (unsigned char *) args->sourceLimit; - const UChar *targetLimit = args->targetLimit; - unsigned char *toUBytes = args->converter->toUBytes; - uint32_t ch, i; - int32_t offsetNum = 0; - - /* Restore state of current sequence */ - if (args->converter->toULength > 0 && myTarget < targetLimit) - { - i = args->converter->toULength; /* restore # of bytes consumed */ - args->converter->toULength = 0; - - /* Stores the previously calculated ch from a previous call*/ - ch = args->converter->toUnicodeStatus - 1; - args->converter->toUnicodeStatus = 0; - goto morebytes; - } - - while (mySource < sourceLimit && myTarget < targetLimit) - { - i = 0; - ch = 0; -morebytes: - while (i < sizeof(uint32_t)) - { - if (mySource < sourceLimit) - { - ch |= ((uint8_t)(*mySource)) << (i * 8); - toUBytes[i++] = (char) *(mySource++); - } - else - { - /* stores a partially calculated target*/ - /* + 1 to make 0 a valid character */ - args->converter->toUnicodeStatus = ch + 1; - args->converter->toULength = (int8_t) i; - goto donefornow; - } - } - - if (ch <= MAXIMUM_UTF && !U_IS_SURROGATE(ch)) - { - /* Normal valid byte when the loop has not prematurely terminated (i < inBytes) */ - if (ch <= MAXIMUM_UCS2) - { - /* fits in 16 bits */ - *(myTarget++) = (UChar) ch; - *(myOffsets++) = offsetNum; - } - else { - /* write out the surrogates */ - *(myTarget++) = U16_LEAD(ch); - *(myOffsets++) = offsetNum; - ch = U16_TRAIL(ch); - if (myTarget < targetLimit) - { - *(myTarget++) = (UChar)ch; - *(myOffsets++) = offsetNum; - } - else - { - /* Put in overflow buffer (not handled here) */ - args->converter->UCharErrorBuffer[0] = (UChar) ch; - args->converter->UCharErrorBufferLength = 1; - *err = U_BUFFER_OVERFLOW_ERROR; - break; - } - } - } - else - { - args->converter->toULength = (int8_t)i; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - offsetNum += i; - } - -donefornow: - if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) - { - /* End of target buffer */ - *err = U_BUFFER_OVERFLOW_ERROR; - } - - args->target = myTarget; - args->source = (const char *) mySource; - args->offsets = myOffsets; -} - -static void U_CALLCONV -T_UConverter_fromUnicode_UTF32_LE(UConverterFromUnicodeArgs * args, - UErrorCode * err) -{ - const UChar *mySource = args->source; - unsigned char *myTarget; - const UChar *sourceLimit = args->sourceLimit; - const unsigned char *targetLimit = (unsigned char *) args->targetLimit; - UChar32 ch, ch2; - unsigned int indexToWrite; - unsigned char temp[sizeof(uint32_t)]; - - if(mySource >= sourceLimit) { - /* no input, nothing to do */ - return; - } - - /* write the BOM if necessary */ - if(args->converter->fromUnicodeStatus==UCNV_NEED_TO_WRITE_BOM) { - static const char bom[]={ (char)0xffu, (char)0xfeu, 0, 0 }; - ucnv_fromUWriteBytes(args->converter, - bom, 4, - &args->target, args->targetLimit, - &args->offsets, -1, - err); - args->converter->fromUnicodeStatus=0; - } - - myTarget = (unsigned char *) args->target; - temp[3] = 0; - - if (args->converter->fromUChar32) - { - ch = args->converter->fromUChar32; - args->converter->fromUChar32 = 0; - goto lowsurogate; - } - - while (mySource < sourceLimit && myTarget < targetLimit) - { - ch = *(mySource++); - - if (U16_IS_SURROGATE(ch)) { - if (U16_IS_LEAD(ch)) - { -lowsurogate: - if (mySource < sourceLimit) - { - ch2 = *mySource; - if (U16_IS_TRAIL(ch2)) { - ch = ((ch - SURROGATE_HIGH_START) << HALF_SHIFT) + ch2 + SURROGATE_LOW_BASE; - mySource++; - } - else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - args->converter->fromUChar32 = ch; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - else { - /* ran out of source */ - args->converter->fromUChar32 = ch; - if (args->flush) { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - *err = U_ILLEGAL_CHAR_FOUND; - } - break; - } - } - else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - args->converter->fromUChar32 = ch; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - - /* We cannot get any larger than 10FFFF because we are coming from UTF-16 */ - temp[2] = (uint8_t) (ch >> 16 & 0x1F); - temp[1] = (uint8_t) (ch >> 8); /* unsigned cast implicitly does (ch & FF) */ - temp[0] = (uint8_t) (ch); /* unsigned cast implicitly does (ch & FF) */ - - for (indexToWrite = 0; indexToWrite <= sizeof(uint32_t) - 1; indexToWrite++) - { - if (myTarget < targetLimit) - { - *(myTarget++) = temp[indexToWrite]; - } - else - { - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = temp[indexToWrite]; - *err = U_BUFFER_OVERFLOW_ERROR; - } - } - } - - if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) - { - *err = U_BUFFER_OVERFLOW_ERROR; - } - - args->target = (char *) myTarget; - args->source = mySource; -} - -static void U_CALLCONV -T_UConverter_fromUnicode_UTF32_LE_OFFSET_LOGIC(UConverterFromUnicodeArgs * args, - UErrorCode * err) -{ - const UChar *mySource = args->source; - unsigned char *myTarget; - int32_t *myOffsets; - const UChar *sourceLimit = args->sourceLimit; - const unsigned char *targetLimit = (unsigned char *) args->targetLimit; - UChar32 ch, ch2; - unsigned int indexToWrite; - unsigned char temp[sizeof(uint32_t)]; - int32_t offsetNum = 0; - - if(mySource >= sourceLimit) { - /* no input, nothing to do */ - return; - } - - /* write the BOM if necessary */ - if(args->converter->fromUnicodeStatus==UCNV_NEED_TO_WRITE_BOM) { - static const char bom[]={ (char)0xffu, (char)0xfeu, 0, 0 }; - ucnv_fromUWriteBytes(args->converter, - bom, 4, - &args->target, args->targetLimit, - &args->offsets, -1, - err); - args->converter->fromUnicodeStatus=0; - } - - myTarget = (unsigned char *) args->target; - myOffsets = args->offsets; - temp[3] = 0; - - if (args->converter->fromUChar32) - { - ch = args->converter->fromUChar32; - args->converter->fromUChar32 = 0; - goto lowsurogate; - } - - while (mySource < sourceLimit && myTarget < targetLimit) - { - ch = *(mySource++); - - if (U16_IS_SURROGATE(ch)) { - if (U16_IS_LEAD(ch)) - { -lowsurogate: - if (mySource < sourceLimit) - { - ch2 = *mySource; - if (U16_IS_TRAIL(ch2)) - { - ch = ((ch - SURROGATE_HIGH_START) << HALF_SHIFT) + ch2 + SURROGATE_LOW_BASE; - mySource++; - } - else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - args->converter->fromUChar32 = ch; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - else { - /* ran out of source */ - args->converter->fromUChar32 = ch; - if (args->flush) { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - *err = U_ILLEGAL_CHAR_FOUND; - } - break; - } - } - else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - args->converter->fromUChar32 = ch; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - - /* We cannot get any larger than 10FFFF because we are coming from UTF-16 */ - temp[2] = (uint8_t) (ch >> 16 & 0x1F); - temp[1] = (uint8_t) (ch >> 8); /* unsigned cast implicitly does (ch & FF) */ - temp[0] = (uint8_t) (ch); /* unsigned cast implicitly does (ch & FF) */ - - for (indexToWrite = 0; indexToWrite <= sizeof(uint32_t) - 1; indexToWrite++) - { - if (myTarget < targetLimit) - { - *(myTarget++) = temp[indexToWrite]; - *(myOffsets++) = offsetNum; - } - else - { - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = temp[indexToWrite]; - *err = U_BUFFER_OVERFLOW_ERROR; - } - } - offsetNum = offsetNum + 1 + (temp[2] != 0); - } - - if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) - { - *err = U_BUFFER_OVERFLOW_ERROR; - } - - args->target = (char *) myTarget; - args->source = mySource; - args->offsets = myOffsets; -} - -static UChar32 U_CALLCONV -T_UConverter_getNextUChar_UTF32_LE(UConverterToUnicodeArgs* args, - UErrorCode* err) -{ - const uint8_t *mySource; - UChar32 myUChar; - int32_t length; - - mySource = (const uint8_t *)args->source; - if (mySource >= (const uint8_t *)args->sourceLimit) - { - /* no input */ - *err = U_INDEX_OUTOFBOUNDS_ERROR; - return 0xffff; - } - - length = (int32_t)((const uint8_t *)args->sourceLimit - mySource); - if (length < 4) - { - /* got a partial character */ - uprv_memcpy(args->converter->toUBytes, mySource, length); - args->converter->toULength = (int8_t)length; - args->source = (const char *)(mySource + length); - *err = U_TRUNCATED_CHAR_FOUND; - return 0xffff; - } - - /* Don't even try to do a direct cast because the value may be on an odd address. */ - myUChar = ((UChar32)mySource[3] << 24) - | ((UChar32)mySource[2] << 16) - | ((UChar32)mySource[1] << 8) - | ((UChar32)mySource[0]); - - args->source = (const char *)(mySource + 4); - if ((uint32_t)myUChar <= MAXIMUM_UTF && !U_IS_SURROGATE(myUChar)) { - return myUChar; - } - - uprv_memcpy(args->converter->toUBytes, mySource, 4); - args->converter->toULength = 4; - - *err = U_ILLEGAL_CHAR_FOUND; - return 0xffff; -} -U_CDECL_END -static const UConverterImpl _UTF32LEImpl = { - UCNV_UTF32_LittleEndian, - - NULL, - NULL, - - NULL, - NULL, - NULL, - - T_UConverter_toUnicode_UTF32_LE, - T_UConverter_toUnicode_UTF32_LE_OFFSET_LOGIC, - T_UConverter_fromUnicode_UTF32_LE, - T_UConverter_fromUnicode_UTF32_LE_OFFSET_LOGIC, - T_UConverter_getNextUChar_UTF32_LE, - - NULL, - NULL, - NULL, - NULL, - ucnv_getNonSurrogateUnicodeSet, - - NULL, - NULL -}; - -/* The 1232 CCSID refers to any version of Unicode with any endianness of UTF-32 */ -static const UConverterStaticData _UTF32LEStaticData = { - sizeof(UConverterStaticData), - "UTF-32LE", - 1234, - UCNV_IBM, UCNV_UTF32_LittleEndian, 4, 4, - { 0xfd, 0xff, 0, 0 }, 4, false, false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - - -const UConverterSharedData _UTF32LEData = - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF32LEStaticData, &_UTF32LEImpl); - -/* UTF-32 (Detect BOM) ------------------------------------------------------ */ - -/* - * Detect a BOM at the beginning of the stream and select UTF-32BE or UTF-32LE - * accordingly. - * - * State values: - * 0 initial state - * 1 saw 00 - * 2 saw 00 00 - * 3 saw 00 00 FE - * 4 - - * 5 saw FF - * 6 saw FF FE - * 7 saw FF FE 00 - * 8 UTF-32BE mode - * 9 UTF-32LE mode - * - * During detection: state&3==number of matching bytes so far. - * - * On output, emit U+FEFF as the first code point. - */ -U_CDECL_BEGIN -static void U_CALLCONV -_UTF32Reset(UConverter *cnv, UConverterResetChoice choice) { - if(choice<=UCNV_RESET_TO_UNICODE) { - /* reset toUnicode: state=0 */ - cnv->mode=0; - } - if(choice!=UCNV_RESET_TO_UNICODE) { - /* reset fromUnicode: prepare to output the UTF-32PE BOM */ - cnv->fromUnicodeStatus=UCNV_NEED_TO_WRITE_BOM; - } -} - -static void U_CALLCONV -_UTF32Open(UConverter *cnv, - UConverterLoadArgs *pArgs, - UErrorCode *pErrorCode) { - (void)pArgs; - (void)pErrorCode; - _UTF32Reset(cnv, UCNV_RESET_BOTH); -} - -static const char utf32BOM[8]={ 0, 0, (char)0xfeu, (char)0xffu, (char)0xffu, (char)0xfeu, 0, 0 }; - -static void U_CALLCONV -_UTF32ToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv=pArgs->converter; - const char *source=pArgs->source; - const char *sourceLimit=pArgs->sourceLimit; - int32_t *offsets=pArgs->offsets; - - int32_t state, offsetDelta; - char b; - - state=cnv->mode; - - /* - * If we detect a BOM in this buffer, then we must add the BOM size to the - * offsets because the actual converter function will not see and count the BOM. - * offsetDelta will have the number of the BOM bytes that are in the current buffer. - */ - offsetDelta=0; - - while(sourcesource); - } else if(state==8) { - state=9; /* detect UTF-32LE */ - offsetDelta=(int32_t)(source-pArgs->source); - } - } else { - /* switch to UTF-32BE and pass the previous bytes */ - int32_t count=(int32_t)(source-pArgs->source); /* number of bytes from this buffer */ - - /* reset the source */ - source=pArgs->source; - - if(count==(state&3)) { - /* simple: all in the same buffer, just reset source */ - } else { - UBool oldFlush=pArgs->flush; - - /* some of the bytes are from a previous buffer, replay those first */ - pArgs->source=utf32BOM+(state&4); /* select the correct BOM */ - pArgs->sourceLimit=pArgs->source+((state&3)-count); /* replay previous bytes */ - pArgs->flush=false; /* this sourceLimit is not the real source stream limit */ - - /* no offsets: bytes from previous buffer, and not enough for output */ - T_UConverter_toUnicode_UTF32_BE(pArgs, pErrorCode); - - /* restore real pointers; pArgs->source will be set in case 8/9 */ - pArgs->sourceLimit=sourceLimit; - pArgs->flush=oldFlush; - } - state=8; - continue; - } - break; - case 8: - /* call UTF-32BE */ - pArgs->source=source; - if(offsets==NULL) { - T_UConverter_toUnicode_UTF32_BE(pArgs, pErrorCode); - } else { - T_UConverter_toUnicode_UTF32_BE_OFFSET_LOGIC(pArgs, pErrorCode); - } - source=pArgs->source; - break; - case 9: - /* call UTF-32LE */ - pArgs->source=source; - if(offsets==NULL) { - T_UConverter_toUnicode_UTF32_LE(pArgs, pErrorCode); - } else { - T_UConverter_toUnicode_UTF32_LE_OFFSET_LOGIC(pArgs, pErrorCode); - } - source=pArgs->source; - break; - default: - break; /* does not occur */ - } - } - - /* add BOM size to offsets - see comment at offsetDelta declaration */ - if(offsets!=NULL && offsetDelta!=0) { - int32_t *offsetsLimit=pArgs->offsets; - while(offsetssource=source; - - if(source==sourceLimit && pArgs->flush) { - /* handle truncated input */ - switch(state) { - case 0: - break; /* no input at all, nothing to do */ - case 8: - T_UConverter_toUnicode_UTF32_BE(pArgs, pErrorCode); - break; - case 9: - T_UConverter_toUnicode_UTF32_LE(pArgs, pErrorCode); - break; - default: - /* handle 0source=utf32BOM+(state&4); /* select the correct BOM */ - pArgs->sourceLimit=pArgs->source+(state&3); /* replay bytes */ - - /* no offsets: not enough for output */ - T_UConverter_toUnicode_UTF32_BE(pArgs, pErrorCode); - pArgs->source=source; - pArgs->sourceLimit=sourceLimit; - state=8; - break; - } - } - - cnv->mode=state; -} - -static UChar32 U_CALLCONV -_UTF32GetNextUChar(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - switch(pArgs->converter->mode) { - case 8: - return T_UConverter_getNextUChar_UTF32_BE(pArgs, pErrorCode); - case 9: - return T_UConverter_getNextUChar_UTF32_LE(pArgs, pErrorCode); - default: - return UCNV_GET_NEXT_UCHAR_USE_TO_U; - } -} -U_CDECL_END -static const UConverterImpl _UTF32Impl = { - UCNV_UTF32, - - NULL, - NULL, - - _UTF32Open, - NULL, - _UTF32Reset, - - _UTF32ToUnicodeWithOffsets, - _UTF32ToUnicodeWithOffsets, -#if U_IS_BIG_ENDIAN - T_UConverter_fromUnicode_UTF32_BE, - T_UConverter_fromUnicode_UTF32_BE_OFFSET_LOGIC, -#else - T_UConverter_fromUnicode_UTF32_LE, - T_UConverter_fromUnicode_UTF32_LE_OFFSET_LOGIC, -#endif - _UTF32GetNextUChar, - - NULL, /* ### TODO implement getStarters for all Unicode encodings?! */ - NULL, - NULL, - NULL, - ucnv_getNonSurrogateUnicodeSet, - - NULL, - NULL -}; - -/* The 1236 CCSID refers to any version of Unicode with a BOM sensitive endianness of UTF-32 */ -static const UConverterStaticData _UTF32StaticData = { - sizeof(UConverterStaticData), - "UTF-32", - 1236, - UCNV_IBM, UCNV_UTF32, 4, 4, -#if U_IS_BIG_ENDIAN - { 0, 0, 0xff, 0xfd }, 4, -#else - { 0xfd, 0xff, 0, 0 }, 4, -#endif - false, false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - -const UConverterSharedData _UTF32Data = - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF32StaticData, &_UTF32Impl); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2002-2015, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: ucnv_u32.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002jul01 +* created by: Markus W. Scherer +* +* UTF-32 converter implementation. Used to be in ucnv_utf.c. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/utf.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" +#include "cmemory.h" + +#define MAXIMUM_UCS2 0x0000FFFF +#define MAXIMUM_UTF 0x0010FFFF +#define HALF_SHIFT 10 +#define HALF_BASE 0x0010000 +#define HALF_MASK 0x3FF +#define SURROGATE_HIGH_START 0xD800 +#define SURROGATE_LOW_START 0xDC00 + +/* -SURROGATE_LOW_START + HALF_BASE */ +#define SURROGATE_LOW_BASE 9216 + +enum { + UCNV_NEED_TO_WRITE_BOM=1 +}; + +/* UTF-32BE ----------------------------------------------------------------- */ +U_CDECL_BEGIN +static void U_CALLCONV +T_UConverter_toUnicode_UTF32_BE(UConverterToUnicodeArgs * args, + UErrorCode * err) +{ + const unsigned char *mySource = (unsigned char *) args->source; + char16_t *myTarget = args->target; + const unsigned char *sourceLimit = (unsigned char *) args->sourceLimit; + const char16_t *targetLimit = args->targetLimit; + unsigned char *toUBytes = args->converter->toUBytes; + uint32_t ch, i; + + /* Restore state of current sequence */ + if (args->converter->toULength > 0 && myTarget < targetLimit) { + i = args->converter->toULength; /* restore # of bytes consumed */ + args->converter->toULength = 0; + + ch = args->converter->toUnicodeStatus - 1;/*Stores the previously calculated ch from a previous call*/ + args->converter->toUnicodeStatus = 0; + goto morebytes; + } + + while (mySource < sourceLimit && myTarget < targetLimit) { + i = 0; + ch = 0; +morebytes: + while (i < sizeof(uint32_t)) { + if (mySource < sourceLimit) { + ch = (ch << 8) | (uint8_t)(*mySource); + toUBytes[i++] = (char) *(mySource++); + } + else { + /* stores a partially calculated target*/ + /* + 1 to make 0 a valid character */ + args->converter->toUnicodeStatus = ch + 1; + args->converter->toULength = (int8_t) i; + goto donefornow; + } + } + + if (ch <= MAXIMUM_UTF && !U_IS_SURROGATE(ch)) { + /* Normal valid byte when the loop has not prematurely terminated (i < inBytes) */ + if (ch <= MAXIMUM_UCS2) + { + /* fits in 16 bits */ + *(myTarget++) = (char16_t) ch; + } + else { + /* write out the surrogates */ + *(myTarget++) = U16_LEAD(ch); + ch = U16_TRAIL(ch); + if (myTarget < targetLimit) { + *(myTarget++) = (char16_t)ch; + } + else { + /* Put in overflow buffer (not handled here) */ + args->converter->UCharErrorBuffer[0] = (char16_t) ch; + args->converter->UCharErrorBufferLength = 1; + *err = U_BUFFER_OVERFLOW_ERROR; + break; + } + } + } + else { + args->converter->toULength = (int8_t)i; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + +donefornow: + if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) { + /* End of target buffer */ + *err = U_BUFFER_OVERFLOW_ERROR; + } + + args->target = myTarget; + args->source = (const char *) mySource; +} + +static void U_CALLCONV +T_UConverter_toUnicode_UTF32_BE_OFFSET_LOGIC(UConverterToUnicodeArgs * args, + UErrorCode * err) +{ + const unsigned char *mySource = (unsigned char *) args->source; + char16_t *myTarget = args->target; + int32_t *myOffsets = args->offsets; + const unsigned char *sourceLimit = (unsigned char *) args->sourceLimit; + const char16_t *targetLimit = args->targetLimit; + unsigned char *toUBytes = args->converter->toUBytes; + uint32_t ch, i; + int32_t offsetNum = 0; + + /* Restore state of current sequence */ + if (args->converter->toULength > 0 && myTarget < targetLimit) { + i = args->converter->toULength; /* restore # of bytes consumed */ + args->converter->toULength = 0; + + ch = args->converter->toUnicodeStatus - 1;/*Stores the previously calculated ch from a previous call*/ + args->converter->toUnicodeStatus = 0; + goto morebytes; + } + + while (mySource < sourceLimit && myTarget < targetLimit) { + i = 0; + ch = 0; +morebytes: + while (i < sizeof(uint32_t)) { + if (mySource < sourceLimit) { + ch = (ch << 8) | (uint8_t)(*mySource); + toUBytes[i++] = (char) *(mySource++); + } + else { + /* stores a partially calculated target*/ + /* + 1 to make 0 a valid character */ + args->converter->toUnicodeStatus = ch + 1; + args->converter->toULength = (int8_t) i; + goto donefornow; + } + } + + if (ch <= MAXIMUM_UTF && !U_IS_SURROGATE(ch)) { + /* Normal valid byte when the loop has not prematurely terminated (i < inBytes) */ + if (ch <= MAXIMUM_UCS2) { + /* fits in 16 bits */ + *(myTarget++) = (char16_t) ch; + *(myOffsets++) = offsetNum; + } + else { + /* write out the surrogates */ + *(myTarget++) = U16_LEAD(ch); + *myOffsets++ = offsetNum; + ch = U16_TRAIL(ch); + if (myTarget < targetLimit) + { + *(myTarget++) = (char16_t)ch; + *(myOffsets++) = offsetNum; + } + else { + /* Put in overflow buffer (not handled here) */ + args->converter->UCharErrorBuffer[0] = (char16_t) ch; + args->converter->UCharErrorBufferLength = 1; + *err = U_BUFFER_OVERFLOW_ERROR; + break; + } + } + } + else { + args->converter->toULength = (int8_t)i; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + offsetNum += i; + } + +donefornow: + if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) + { + /* End of target buffer */ + *err = U_BUFFER_OVERFLOW_ERROR; + } + + args->target = myTarget; + args->source = (const char *) mySource; + args->offsets = myOffsets; +} + +static void U_CALLCONV +T_UConverter_fromUnicode_UTF32_BE(UConverterFromUnicodeArgs * args, + UErrorCode * err) +{ + const char16_t *mySource = args->source; + unsigned char *myTarget; + const char16_t *sourceLimit = args->sourceLimit; + const unsigned char *targetLimit = (unsigned char *) args->targetLimit; + UChar32 ch, ch2; + unsigned int indexToWrite; + unsigned char temp[sizeof(uint32_t)]; + + if(mySource >= sourceLimit) { + /* no input, nothing to do */ + return; + } + + /* write the BOM if necessary */ + if(args->converter->fromUnicodeStatus==UCNV_NEED_TO_WRITE_BOM) { + static const char bom[]={ 0, 0, (char)0xfeu, (char)0xffu }; + ucnv_fromUWriteBytes(args->converter, + bom, 4, + &args->target, args->targetLimit, + &args->offsets, -1, + err); + args->converter->fromUnicodeStatus=0; + } + + myTarget = (unsigned char *) args->target; + temp[0] = 0; + + if (args->converter->fromUChar32) { + ch = args->converter->fromUChar32; + args->converter->fromUChar32 = 0; + goto lowsurogate; + } + + while (mySource < sourceLimit && myTarget < targetLimit) { + ch = *(mySource++); + + if (U_IS_SURROGATE(ch)) { + if (U_IS_LEAD(ch)) { +lowsurogate: + if (mySource < sourceLimit) { + ch2 = *mySource; + if (U_IS_TRAIL(ch2)) { + ch = ((ch - SURROGATE_HIGH_START) << HALF_SHIFT) + ch2 + SURROGATE_LOW_BASE; + mySource++; + } + else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + args->converter->fromUChar32 = ch; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + else { + /* ran out of source */ + args->converter->fromUChar32 = ch; + if (args->flush) { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + *err = U_ILLEGAL_CHAR_FOUND; + } + break; + } + } + else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + args->converter->fromUChar32 = ch; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + + /* We cannot get any larger than 10FFFF because we are coming from UTF-16 */ + temp[1] = (uint8_t) (ch >> 16 & 0x1F); + temp[2] = (uint8_t) (ch >> 8); /* unsigned cast implicitly does (ch & FF) */ + temp[3] = (uint8_t) (ch); /* unsigned cast implicitly does (ch & FF) */ + + for (indexToWrite = 0; indexToWrite <= sizeof(uint32_t) - 1; indexToWrite++) { + if (myTarget < targetLimit) { + *(myTarget++) = temp[indexToWrite]; + } + else { + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = temp[indexToWrite]; + *err = U_BUFFER_OVERFLOW_ERROR; + } + } + } + + if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) { + *err = U_BUFFER_OVERFLOW_ERROR; + } + + args->target = (char *) myTarget; + args->source = mySource; +} + +static void U_CALLCONV +T_UConverter_fromUnicode_UTF32_BE_OFFSET_LOGIC(UConverterFromUnicodeArgs * args, + UErrorCode * err) +{ + const char16_t *mySource = args->source; + unsigned char *myTarget; + int32_t *myOffsets; + const char16_t *sourceLimit = args->sourceLimit; + const unsigned char *targetLimit = (unsigned char *) args->targetLimit; + UChar32 ch, ch2; + int32_t offsetNum = 0; + unsigned int indexToWrite; + unsigned char temp[sizeof(uint32_t)]; + + if(mySource >= sourceLimit) { + /* no input, nothing to do */ + return; + } + + /* write the BOM if necessary */ + if(args->converter->fromUnicodeStatus==UCNV_NEED_TO_WRITE_BOM) { + static const char bom[]={ 0, 0, (char)0xfeu, (char)0xffu }; + ucnv_fromUWriteBytes(args->converter, + bom, 4, + &args->target, args->targetLimit, + &args->offsets, -1, + err); + args->converter->fromUnicodeStatus=0; + } + + myTarget = (unsigned char *) args->target; + myOffsets = args->offsets; + temp[0] = 0; + + if (args->converter->fromUChar32) { + ch = args->converter->fromUChar32; + args->converter->fromUChar32 = 0; + goto lowsurogate; + } + + while (mySource < sourceLimit && myTarget < targetLimit) { + ch = *(mySource++); + + if (U_IS_SURROGATE(ch)) { + if (U_IS_LEAD(ch)) { +lowsurogate: + if (mySource < sourceLimit) { + ch2 = *mySource; + if (U_IS_TRAIL(ch2)) { + ch = ((ch - SURROGATE_HIGH_START) << HALF_SHIFT) + ch2 + SURROGATE_LOW_BASE; + mySource++; + } + else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + args->converter->fromUChar32 = ch; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + else { + /* ran out of source */ + args->converter->fromUChar32 = ch; + if (args->flush) { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + *err = U_ILLEGAL_CHAR_FOUND; + } + break; + } + } + else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + args->converter->fromUChar32 = ch; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + + /* We cannot get any larger than 10FFFF because we are coming from UTF-16 */ + temp[1] = (uint8_t) (ch >> 16 & 0x1F); + temp[2] = (uint8_t) (ch >> 8); /* unsigned cast implicitly does (ch & FF) */ + temp[3] = (uint8_t) (ch); /* unsigned cast implicitly does (ch & FF) */ + + for (indexToWrite = 0; indexToWrite <= sizeof(uint32_t) - 1; indexToWrite++) { + if (myTarget < targetLimit) { + *(myTarget++) = temp[indexToWrite]; + *(myOffsets++) = offsetNum; + } + else { + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = temp[indexToWrite]; + *err = U_BUFFER_OVERFLOW_ERROR; + } + } + offsetNum = offsetNum + 1 + (temp[1] != 0); + } + + if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) { + *err = U_BUFFER_OVERFLOW_ERROR; + } + + args->target = (char *) myTarget; + args->source = mySource; + args->offsets = myOffsets; +} + +static UChar32 U_CALLCONV +T_UConverter_getNextUChar_UTF32_BE(UConverterToUnicodeArgs* args, + UErrorCode* err) +{ + const uint8_t *mySource; + UChar32 myUChar; + int32_t length; + + mySource = (const uint8_t *)args->source; + if (mySource >= (const uint8_t *)args->sourceLimit) + { + /* no input */ + *err = U_INDEX_OUTOFBOUNDS_ERROR; + return 0xffff; + } + + length = (int32_t)((const uint8_t *)args->sourceLimit - mySource); + if (length < 4) + { + /* got a partial character */ + uprv_memcpy(args->converter->toUBytes, mySource, length); + args->converter->toULength = (int8_t)length; + args->source = (const char *)(mySource + length); + *err = U_TRUNCATED_CHAR_FOUND; + return 0xffff; + } + + /* Don't even try to do a direct cast because the value may be on an odd address. */ + myUChar = ((UChar32)mySource[0] << 24) + | ((UChar32)mySource[1] << 16) + | ((UChar32)mySource[2] << 8) + | ((UChar32)mySource[3]); + + args->source = (const char *)(mySource + 4); + if ((uint32_t)myUChar <= MAXIMUM_UTF && !U_IS_SURROGATE(myUChar)) { + return myUChar; + } + + uprv_memcpy(args->converter->toUBytes, mySource, 4); + args->converter->toULength = 4; + + *err = U_ILLEGAL_CHAR_FOUND; + return 0xffff; +} +U_CDECL_END +static const UConverterImpl _UTF32BEImpl = { + UCNV_UTF32_BigEndian, + + nullptr, + nullptr, + + nullptr, + nullptr, + nullptr, + + T_UConverter_toUnicode_UTF32_BE, + T_UConverter_toUnicode_UTF32_BE_OFFSET_LOGIC, + T_UConverter_fromUnicode_UTF32_BE, + T_UConverter_fromUnicode_UTF32_BE_OFFSET_LOGIC, + T_UConverter_getNextUChar_UTF32_BE, + + nullptr, + nullptr, + nullptr, + nullptr, + ucnv_getNonSurrogateUnicodeSet, + + nullptr, + nullptr +}; + +/* The 1232 CCSID refers to any version of Unicode with any endianness of UTF-32 */ +static const UConverterStaticData _UTF32BEStaticData = { + sizeof(UConverterStaticData), + "UTF-32BE", + 1232, + UCNV_IBM, UCNV_UTF32_BigEndian, 4, 4, + { 0, 0, 0xff, 0xfd }, 4, false, false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + +const UConverterSharedData _UTF32BEData = + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF32BEStaticData, &_UTF32BEImpl); + +/* UTF-32LE ---------------------------------------------------------- */ +U_CDECL_BEGIN +static void U_CALLCONV +T_UConverter_toUnicode_UTF32_LE(UConverterToUnicodeArgs * args, + UErrorCode * err) +{ + const unsigned char *mySource = (unsigned char *) args->source; + char16_t *myTarget = args->target; + const unsigned char *sourceLimit = (unsigned char *) args->sourceLimit; + const char16_t *targetLimit = args->targetLimit; + unsigned char *toUBytes = args->converter->toUBytes; + uint32_t ch, i; + + /* Restore state of current sequence */ + if (args->converter->toULength > 0 && myTarget < targetLimit) + { + i = args->converter->toULength; /* restore # of bytes consumed */ + args->converter->toULength = 0; + + /* Stores the previously calculated ch from a previous call*/ + ch = args->converter->toUnicodeStatus - 1; + args->converter->toUnicodeStatus = 0; + goto morebytes; + } + + while (mySource < sourceLimit && myTarget < targetLimit) + { + i = 0; + ch = 0; +morebytes: + while (i < sizeof(uint32_t)) + { + if (mySource < sourceLimit) + { + ch |= ((uint8_t)(*mySource)) << (i * 8); + toUBytes[i++] = (char) *(mySource++); + } + else + { + /* stores a partially calculated target*/ + /* + 1 to make 0 a valid character */ + args->converter->toUnicodeStatus = ch + 1; + args->converter->toULength = (int8_t) i; + goto donefornow; + } + } + + if (ch <= MAXIMUM_UTF && !U_IS_SURROGATE(ch)) { + /* Normal valid byte when the loop has not prematurely terminated (i < inBytes) */ + if (ch <= MAXIMUM_UCS2) { + /* fits in 16 bits */ + *(myTarget++) = (char16_t) ch; + } + else { + /* write out the surrogates */ + *(myTarget++) = U16_LEAD(ch); + ch = U16_TRAIL(ch); + if (myTarget < targetLimit) { + *(myTarget++) = (char16_t)ch; + } + else { + /* Put in overflow buffer (not handled here) */ + args->converter->UCharErrorBuffer[0] = (char16_t) ch; + args->converter->UCharErrorBufferLength = 1; + *err = U_BUFFER_OVERFLOW_ERROR; + break; + } + } + } + else { + args->converter->toULength = (int8_t)i; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + +donefornow: + if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) + { + /* End of target buffer */ + *err = U_BUFFER_OVERFLOW_ERROR; + } + + args->target = myTarget; + args->source = (const char *) mySource; +} + +static void U_CALLCONV +T_UConverter_toUnicode_UTF32_LE_OFFSET_LOGIC(UConverterToUnicodeArgs * args, + UErrorCode * err) +{ + const unsigned char *mySource = (unsigned char *) args->source; + char16_t *myTarget = args->target; + int32_t *myOffsets = args->offsets; + const unsigned char *sourceLimit = (unsigned char *) args->sourceLimit; + const char16_t *targetLimit = args->targetLimit; + unsigned char *toUBytes = args->converter->toUBytes; + uint32_t ch, i; + int32_t offsetNum = 0; + + /* Restore state of current sequence */ + if (args->converter->toULength > 0 && myTarget < targetLimit) + { + i = args->converter->toULength; /* restore # of bytes consumed */ + args->converter->toULength = 0; + + /* Stores the previously calculated ch from a previous call*/ + ch = args->converter->toUnicodeStatus - 1; + args->converter->toUnicodeStatus = 0; + goto morebytes; + } + + while (mySource < sourceLimit && myTarget < targetLimit) + { + i = 0; + ch = 0; +morebytes: + while (i < sizeof(uint32_t)) + { + if (mySource < sourceLimit) + { + ch |= ((uint8_t)(*mySource)) << (i * 8); + toUBytes[i++] = (char) *(mySource++); + } + else + { + /* stores a partially calculated target*/ + /* + 1 to make 0 a valid character */ + args->converter->toUnicodeStatus = ch + 1; + args->converter->toULength = (int8_t) i; + goto donefornow; + } + } + + if (ch <= MAXIMUM_UTF && !U_IS_SURROGATE(ch)) + { + /* Normal valid byte when the loop has not prematurely terminated (i < inBytes) */ + if (ch <= MAXIMUM_UCS2) + { + /* fits in 16 bits */ + *(myTarget++) = (char16_t) ch; + *(myOffsets++) = offsetNum; + } + else { + /* write out the surrogates */ + *(myTarget++) = U16_LEAD(ch); + *(myOffsets++) = offsetNum; + ch = U16_TRAIL(ch); + if (myTarget < targetLimit) + { + *(myTarget++) = (char16_t)ch; + *(myOffsets++) = offsetNum; + } + else + { + /* Put in overflow buffer (not handled here) */ + args->converter->UCharErrorBuffer[0] = (char16_t) ch; + args->converter->UCharErrorBufferLength = 1; + *err = U_BUFFER_OVERFLOW_ERROR; + break; + } + } + } + else + { + args->converter->toULength = (int8_t)i; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + offsetNum += i; + } + +donefornow: + if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) + { + /* End of target buffer */ + *err = U_BUFFER_OVERFLOW_ERROR; + } + + args->target = myTarget; + args->source = (const char *) mySource; + args->offsets = myOffsets; +} + +static void U_CALLCONV +T_UConverter_fromUnicode_UTF32_LE(UConverterFromUnicodeArgs * args, + UErrorCode * err) +{ + const char16_t *mySource = args->source; + unsigned char *myTarget; + const char16_t *sourceLimit = args->sourceLimit; + const unsigned char *targetLimit = (unsigned char *) args->targetLimit; + UChar32 ch, ch2; + unsigned int indexToWrite; + unsigned char temp[sizeof(uint32_t)]; + + if(mySource >= sourceLimit) { + /* no input, nothing to do */ + return; + } + + /* write the BOM if necessary */ + if(args->converter->fromUnicodeStatus==UCNV_NEED_TO_WRITE_BOM) { + static const char bom[]={ (char)0xffu, (char)0xfeu, 0, 0 }; + ucnv_fromUWriteBytes(args->converter, + bom, 4, + &args->target, args->targetLimit, + &args->offsets, -1, + err); + args->converter->fromUnicodeStatus=0; + } + + myTarget = (unsigned char *) args->target; + temp[3] = 0; + + if (args->converter->fromUChar32) + { + ch = args->converter->fromUChar32; + args->converter->fromUChar32 = 0; + goto lowsurogate; + } + + while (mySource < sourceLimit && myTarget < targetLimit) + { + ch = *(mySource++); + + if (U16_IS_SURROGATE(ch)) { + if (U16_IS_LEAD(ch)) + { +lowsurogate: + if (mySource < sourceLimit) + { + ch2 = *mySource; + if (U16_IS_TRAIL(ch2)) { + ch = ((ch - SURROGATE_HIGH_START) << HALF_SHIFT) + ch2 + SURROGATE_LOW_BASE; + mySource++; + } + else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + args->converter->fromUChar32 = ch; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + else { + /* ran out of source */ + args->converter->fromUChar32 = ch; + if (args->flush) { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + *err = U_ILLEGAL_CHAR_FOUND; + } + break; + } + } + else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + args->converter->fromUChar32 = ch; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + + /* We cannot get any larger than 10FFFF because we are coming from UTF-16 */ + temp[2] = (uint8_t) (ch >> 16 & 0x1F); + temp[1] = (uint8_t) (ch >> 8); /* unsigned cast implicitly does (ch & FF) */ + temp[0] = (uint8_t) (ch); /* unsigned cast implicitly does (ch & FF) */ + + for (indexToWrite = 0; indexToWrite <= sizeof(uint32_t) - 1; indexToWrite++) + { + if (myTarget < targetLimit) + { + *(myTarget++) = temp[indexToWrite]; + } + else + { + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = temp[indexToWrite]; + *err = U_BUFFER_OVERFLOW_ERROR; + } + } + } + + if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) + { + *err = U_BUFFER_OVERFLOW_ERROR; + } + + args->target = (char *) myTarget; + args->source = mySource; +} + +static void U_CALLCONV +T_UConverter_fromUnicode_UTF32_LE_OFFSET_LOGIC(UConverterFromUnicodeArgs * args, + UErrorCode * err) +{ + const char16_t *mySource = args->source; + unsigned char *myTarget; + int32_t *myOffsets; + const char16_t *sourceLimit = args->sourceLimit; + const unsigned char *targetLimit = (unsigned char *) args->targetLimit; + UChar32 ch, ch2; + unsigned int indexToWrite; + unsigned char temp[sizeof(uint32_t)]; + int32_t offsetNum = 0; + + if(mySource >= sourceLimit) { + /* no input, nothing to do */ + return; + } + + /* write the BOM if necessary */ + if(args->converter->fromUnicodeStatus==UCNV_NEED_TO_WRITE_BOM) { + static const char bom[]={ (char)0xffu, (char)0xfeu, 0, 0 }; + ucnv_fromUWriteBytes(args->converter, + bom, 4, + &args->target, args->targetLimit, + &args->offsets, -1, + err); + args->converter->fromUnicodeStatus=0; + } + + myTarget = (unsigned char *) args->target; + myOffsets = args->offsets; + temp[3] = 0; + + if (args->converter->fromUChar32) + { + ch = args->converter->fromUChar32; + args->converter->fromUChar32 = 0; + goto lowsurogate; + } + + while (mySource < sourceLimit && myTarget < targetLimit) + { + ch = *(mySource++); + + if (U16_IS_SURROGATE(ch)) { + if (U16_IS_LEAD(ch)) + { +lowsurogate: + if (mySource < sourceLimit) + { + ch2 = *mySource; + if (U16_IS_TRAIL(ch2)) + { + ch = ((ch - SURROGATE_HIGH_START) << HALF_SHIFT) + ch2 + SURROGATE_LOW_BASE; + mySource++; + } + else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + args->converter->fromUChar32 = ch; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + else { + /* ran out of source */ + args->converter->fromUChar32 = ch; + if (args->flush) { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + *err = U_ILLEGAL_CHAR_FOUND; + } + break; + } + } + else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + args->converter->fromUChar32 = ch; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + + /* We cannot get any larger than 10FFFF because we are coming from UTF-16 */ + temp[2] = (uint8_t) (ch >> 16 & 0x1F); + temp[1] = (uint8_t) (ch >> 8); /* unsigned cast implicitly does (ch & FF) */ + temp[0] = (uint8_t) (ch); /* unsigned cast implicitly does (ch & FF) */ + + for (indexToWrite = 0; indexToWrite <= sizeof(uint32_t) - 1; indexToWrite++) + { + if (myTarget < targetLimit) + { + *(myTarget++) = temp[indexToWrite]; + *(myOffsets++) = offsetNum; + } + else + { + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = temp[indexToWrite]; + *err = U_BUFFER_OVERFLOW_ERROR; + } + } + offsetNum = offsetNum + 1 + (temp[2] != 0); + } + + if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) + { + *err = U_BUFFER_OVERFLOW_ERROR; + } + + args->target = (char *) myTarget; + args->source = mySource; + args->offsets = myOffsets; +} + +static UChar32 U_CALLCONV +T_UConverter_getNextUChar_UTF32_LE(UConverterToUnicodeArgs* args, + UErrorCode* err) +{ + const uint8_t *mySource; + UChar32 myUChar; + int32_t length; + + mySource = (const uint8_t *)args->source; + if (mySource >= (const uint8_t *)args->sourceLimit) + { + /* no input */ + *err = U_INDEX_OUTOFBOUNDS_ERROR; + return 0xffff; + } + + length = (int32_t)((const uint8_t *)args->sourceLimit - mySource); + if (length < 4) + { + /* got a partial character */ + uprv_memcpy(args->converter->toUBytes, mySource, length); + args->converter->toULength = (int8_t)length; + args->source = (const char *)(mySource + length); + *err = U_TRUNCATED_CHAR_FOUND; + return 0xffff; + } + + /* Don't even try to do a direct cast because the value may be on an odd address. */ + myUChar = ((UChar32)mySource[3] << 24) + | ((UChar32)mySource[2] << 16) + | ((UChar32)mySource[1] << 8) + | ((UChar32)mySource[0]); + + args->source = (const char *)(mySource + 4); + if ((uint32_t)myUChar <= MAXIMUM_UTF && !U_IS_SURROGATE(myUChar)) { + return myUChar; + } + + uprv_memcpy(args->converter->toUBytes, mySource, 4); + args->converter->toULength = 4; + + *err = U_ILLEGAL_CHAR_FOUND; + return 0xffff; +} +U_CDECL_END +static const UConverterImpl _UTF32LEImpl = { + UCNV_UTF32_LittleEndian, + + nullptr, + nullptr, + + nullptr, + nullptr, + nullptr, + + T_UConverter_toUnicode_UTF32_LE, + T_UConverter_toUnicode_UTF32_LE_OFFSET_LOGIC, + T_UConverter_fromUnicode_UTF32_LE, + T_UConverter_fromUnicode_UTF32_LE_OFFSET_LOGIC, + T_UConverter_getNextUChar_UTF32_LE, + + nullptr, + nullptr, + nullptr, + nullptr, + ucnv_getNonSurrogateUnicodeSet, + + nullptr, + nullptr +}; + +/* The 1232 CCSID refers to any version of Unicode with any endianness of UTF-32 */ +static const UConverterStaticData _UTF32LEStaticData = { + sizeof(UConverterStaticData), + "UTF-32LE", + 1234, + UCNV_IBM, UCNV_UTF32_LittleEndian, 4, 4, + { 0xfd, 0xff, 0, 0 }, 4, false, false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + + +const UConverterSharedData _UTF32LEData = + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF32LEStaticData, &_UTF32LEImpl); + +/* UTF-32 (Detect BOM) ------------------------------------------------------ */ + +/* + * Detect a BOM at the beginning of the stream and select UTF-32BE or UTF-32LE + * accordingly. + * + * State values: + * 0 initial state + * 1 saw 00 + * 2 saw 00 00 + * 3 saw 00 00 FE + * 4 - + * 5 saw FF + * 6 saw FF FE + * 7 saw FF FE 00 + * 8 UTF-32BE mode + * 9 UTF-32LE mode + * + * During detection: state&3==number of matching bytes so far. + * + * On output, emit U+FEFF as the first code point. + */ +U_CDECL_BEGIN +static void U_CALLCONV +_UTF32Reset(UConverter *cnv, UConverterResetChoice choice) { + if(choice<=UCNV_RESET_TO_UNICODE) { + /* reset toUnicode: state=0 */ + cnv->mode=0; + } + if(choice!=UCNV_RESET_TO_UNICODE) { + /* reset fromUnicode: prepare to output the UTF-32PE BOM */ + cnv->fromUnicodeStatus=UCNV_NEED_TO_WRITE_BOM; + } +} + +static void U_CALLCONV +_UTF32Open(UConverter *cnv, + UConverterLoadArgs *pArgs, + UErrorCode *pErrorCode) { + (void)pArgs; + (void)pErrorCode; + _UTF32Reset(cnv, UCNV_RESET_BOTH); +} + +static const char utf32BOM[8]={ 0, 0, (char)0xfeu, (char)0xffu, (char)0xffu, (char)0xfeu, 0, 0 }; + +static void U_CALLCONV +_UTF32ToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv=pArgs->converter; + const char *source=pArgs->source; + const char *sourceLimit=pArgs->sourceLimit; + int32_t *offsets=pArgs->offsets; + + int32_t state, offsetDelta; + char b; + + state=cnv->mode; + + /* + * If we detect a BOM in this buffer, then we must add the BOM size to the + * offsets because the actual converter function will not see and count the BOM. + * offsetDelta will have the number of the BOM bytes that are in the current buffer. + */ + offsetDelta=0; + + while(sourcesource); + } else if(state==8) { + state=9; /* detect UTF-32LE */ + offsetDelta=(int32_t)(source-pArgs->source); + } + } else { + /* switch to UTF-32BE and pass the previous bytes */ + int32_t count=(int32_t)(source-pArgs->source); /* number of bytes from this buffer */ + + /* reset the source */ + source=pArgs->source; + + if(count==(state&3)) { + /* simple: all in the same buffer, just reset source */ + } else { + UBool oldFlush=pArgs->flush; + + /* some of the bytes are from a previous buffer, replay those first */ + pArgs->source=utf32BOM+(state&4); /* select the correct BOM */ + pArgs->sourceLimit=pArgs->source+((state&3)-count); /* replay previous bytes */ + pArgs->flush=false; /* this sourceLimit is not the real source stream limit */ + + /* no offsets: bytes from previous buffer, and not enough for output */ + T_UConverter_toUnicode_UTF32_BE(pArgs, pErrorCode); + + /* restore real pointers; pArgs->source will be set in case 8/9 */ + pArgs->sourceLimit=sourceLimit; + pArgs->flush=oldFlush; + } + state=8; + continue; + } + break; + case 8: + /* call UTF-32BE */ + pArgs->source=source; + if(offsets==nullptr) { + T_UConverter_toUnicode_UTF32_BE(pArgs, pErrorCode); + } else { + T_UConverter_toUnicode_UTF32_BE_OFFSET_LOGIC(pArgs, pErrorCode); + } + source=pArgs->source; + break; + case 9: + /* call UTF-32LE */ + pArgs->source=source; + if(offsets==nullptr) { + T_UConverter_toUnicode_UTF32_LE(pArgs, pErrorCode); + } else { + T_UConverter_toUnicode_UTF32_LE_OFFSET_LOGIC(pArgs, pErrorCode); + } + source=pArgs->source; + break; + default: + break; /* does not occur */ + } + } + + /* add BOM size to offsets - see comment at offsetDelta declaration */ + if(offsets!=nullptr && offsetDelta!=0) { + int32_t *offsetsLimit=pArgs->offsets; + while(offsetssource=source; + + if(source==sourceLimit && pArgs->flush) { + /* handle truncated input */ + switch(state) { + case 0: + break; /* no input at all, nothing to do */ + case 8: + T_UConverter_toUnicode_UTF32_BE(pArgs, pErrorCode); + break; + case 9: + T_UConverter_toUnicode_UTF32_LE(pArgs, pErrorCode); + break; + default: + /* handle 0source=utf32BOM+(state&4); /* select the correct BOM */ + pArgs->sourceLimit=pArgs->source+(state&3); /* replay bytes */ + + /* no offsets: not enough for output */ + T_UConverter_toUnicode_UTF32_BE(pArgs, pErrorCode); + pArgs->source=source; + pArgs->sourceLimit=sourceLimit; + state=8; + break; + } + } + + cnv->mode=state; +} + +static UChar32 U_CALLCONV +_UTF32GetNextUChar(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + switch(pArgs->converter->mode) { + case 8: + return T_UConverter_getNextUChar_UTF32_BE(pArgs, pErrorCode); + case 9: + return T_UConverter_getNextUChar_UTF32_LE(pArgs, pErrorCode); + default: + return UCNV_GET_NEXT_UCHAR_USE_TO_U; + } +} +U_CDECL_END +static const UConverterImpl _UTF32Impl = { + UCNV_UTF32, + + nullptr, + nullptr, + + _UTF32Open, + nullptr, + _UTF32Reset, + + _UTF32ToUnicodeWithOffsets, + _UTF32ToUnicodeWithOffsets, +#if U_IS_BIG_ENDIAN + T_UConverter_fromUnicode_UTF32_BE, + T_UConverter_fromUnicode_UTF32_BE_OFFSET_LOGIC, +#else + T_UConverter_fromUnicode_UTF32_LE, + T_UConverter_fromUnicode_UTF32_LE_OFFSET_LOGIC, +#endif + _UTF32GetNextUChar, + + nullptr, /* ### TODO implement getStarters for all Unicode encodings?! */ + nullptr, + nullptr, + nullptr, + ucnv_getNonSurrogateUnicodeSet, + + nullptr, + nullptr +}; + +/* The 1236 CCSID refers to any version of Unicode with a BOM sensitive endianness of UTF-32 */ +static const UConverterStaticData _UTF32StaticData = { + sizeof(UConverterStaticData), + "UTF-32", + 1236, + UCNV_IBM, UCNV_UTF32, 4, 4, +#if U_IS_BIG_ENDIAN + { 0, 0, 0xff, 0xfd }, 4, +#else + { 0xfd, 0xff, 0, 0 }, 4, +#endif + false, false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + +const UConverterSharedData _UTF32Data = + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF32StaticData, &_UTF32Impl); + +#endif diff --git a/deps/icu-small/source/common/ucnv_u7.cpp b/deps/icu-small/source/common/ucnv_u7.cpp index 8964ca01de0d89..4456a10fcc5451 100644 --- a/deps/icu-small/source/common/ucnv_u7.cpp +++ b/deps/icu-small/source/common/ucnv_u7.cpp @@ -1,1491 +1,1491 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2002-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: ucnv_u7.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002jul01 -* created by: Markus W. Scherer -* -* UTF-7 converter implementation. Used to be in ucnv_utf.c. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION - -#include "cmemory.h" -#include "unicode/ucnv.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" -#include "uassert.h" - -/* UTF-7 -------------------------------------------------------------------- */ - -/* - * UTF-7 is a stateful encoding of Unicode. - * It is defined in RFC 2152. (http://www.ietf.org/rfc/rfc2152.txt) - * It was intended for use in Internet email systems, using in its bytewise - * encoding only a subset of 7-bit US-ASCII. - * UTF-7 is deprecated in favor of UTF-8/16/32 and SCSU, but still - * occasionally used. - * - * For converting Unicode to UTF-7, the RFC allows to encode some US-ASCII - * characters directly or in base64. Especially, the characters in set O - * as defined in the RFC (see below) may be encoded directly but are not - * allowed in, e.g., email headers. - * By default, the ICU UTF-7 converter encodes set O directly. - * By choosing the option "version=1", set O will be escaped instead. - * For example: - * utf7Converter=ucnv_open("UTF-7,version=1"); - * - * For details about email headers see RFC 2047. - */ - -/* - * Tests for US-ASCII characters belonging to character classes - * defined in UTF-7. - * - * Set D (directly encoded characters) consists of the following - * characters: the upper and lower case letters A through Z - * and a through z, the 10 digits 0-9, and the following nine special - * characters (note that "+" and "=" are omitted): - * '(),-./:? - * - * Set O (optional direct characters) consists of the following - * characters (note that "\" and "~" are omitted): - * !"#$%&*;<=>@[]^_`{|} - * - * According to the rules in RFC 2152, the byte values for the following - * US-ASCII characters are not used in UTF-7 and are therefore illegal: - * - all C0 control codes except for CR LF TAB - * - BACKSLASH - * - TILDE - * - DEL - * - all codes beyond US-ASCII, i.e. all >127 - */ -#define inSetD(c) \ - ((uint8_t)((c)-97)<26 || (uint8_t)((c)-65)<26 || /* letters */ \ - (uint8_t)((c)-48)<10 || /* digits */ \ - (uint8_t)((c)-39)<3 || /* '() */ \ - (uint8_t)((c)-44)<4 || /* ,-./ */ \ - (c)==58 || (c)==63 /* :? */ \ - ) - -#define inSetO(c) \ - ((uint8_t)((c)-33)<6 || /* !"#$%& */ \ - (uint8_t)((c)-59)<4 || /* ;<=> */ \ - (uint8_t)((c)-93)<4 || /* ]^_` */ \ - (uint8_t)((c)-123)<3 || /* {|} */ \ - (c)==42 || (c)==64 || (c)==91 /* *@[ */ \ - ) - -#define isCRLFTAB(c) ((c)==13 || (c)==10 || (c)==9) -#define isCRLFSPTAB(c) ((c)==32 || (c)==13 || (c)==10 || (c)==9) - -#define PLUS 43 -#define MINUS 45 -#define BACKSLASH 92 -#define TILDE 126 - -/* legal byte values: all US-ASCII graphic characters from space to before tilde, and CR LF TAB */ -#define isLegalUTF7(c) (((uint8_t)((c)-32)<94 && (c)!=BACKSLASH) || isCRLFTAB(c)) - -/* encode directly sets D and O and CR LF SP TAB */ -static const UBool encodeDirectlyMaximum[128]={ - /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 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, 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, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1, 0, 0 -}; - -/* encode directly set D and CR LF SP TAB but not set O */ -static const UBool encodeDirectlyRestricted[128]={ - /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, - - 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, 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, 0, 0, 0, 0, 0 -}; - -static const uint8_t -toBase64[64]={ - /* A-Z */ - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, - 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, - /* a-z */ - 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, - 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, - /* 0-9 */ - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* +/ */ - 43, 47 -}; - -static const int8_t -fromBase64[128]={ - /* C0 controls, -1 for legal ones (CR LF TAB), -3 for illegal ones */ - -3, -3, -3, -3, -3, -3, -3, -3, -3, -1, -1, -3, -3, -1, -3, -3, - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, - - /* general punctuation with + and / and a special value (-2) for - */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -2, -1, 63, - /* digits */ - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, - - /* A-Z */ - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -3, -1, -1, -1, - - /* a-z */ - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -3, -3 -}; - -/* - * converter status values: - * - * toUnicodeStatus: - * 24 inDirectMode (boolean) - * 23..16 base64Counter (-1..7) - * 15..0 bits (up to 14 bits incoming base64) - * - * fromUnicodeStatus: - * 31..28 version (0: set O direct 1: set O escaped) - * 24 inDirectMode (boolean) - * 23..16 base64Counter (0..2) - * 7..0 bits (6 bits outgoing base64) - * - */ - -U_CDECL_BEGIN -static void U_CALLCONV -_UTF7Reset(UConverter *cnv, UConverterResetChoice choice) { - if(choice<=UCNV_RESET_TO_UNICODE) { - /* reset toUnicode */ - cnv->toUnicodeStatus=0x1000000; /* inDirectMode=true */ - cnv->toULength=0; - } - if(choice!=UCNV_RESET_TO_UNICODE) { - /* reset fromUnicode */ - cnv->fromUnicodeStatus=(cnv->fromUnicodeStatus&0xf0000000)|0x1000000; /* keep version, inDirectMode=true */ - } -} - -static void U_CALLCONV -_UTF7Open(UConverter *cnv, - UConverterLoadArgs *pArgs, - UErrorCode *pErrorCode) { - (void)pArgs; - if(UCNV_GET_VERSION(cnv)<=1) { - /* TODO(markus): Should just use cnv->options rather than copying the version number. */ - cnv->fromUnicodeStatus=UCNV_GET_VERSION(cnv)<<28; - _UTF7Reset(cnv, UCNV_RESET_BOTH); - } else { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - } -} - -static void U_CALLCONV -_UTF7ToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const uint8_t *source, *sourceLimit; - UChar *target; - const UChar *targetLimit; - int32_t *offsets; - - uint8_t *bytes; - uint8_t byteIndex; - - int32_t length, targetCapacity; - - /* UTF-7 state */ - uint16_t bits; - int8_t base64Counter; - UBool inDirectMode; - - int8_t base64Value; - - int32_t sourceIndex, nextSourceIndex; - - uint8_t b; - /* set up the local pointers */ - cnv=pArgs->converter; - - source=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - target=pArgs->target; - targetLimit=pArgs->targetLimit; - offsets=pArgs->offsets; - /* get the state machine state */ - { - uint32_t status=cnv->toUnicodeStatus; - inDirectMode=(UBool)((status>>24)&1); - base64Counter=(int8_t)(status>>16); - bits=(uint16_t)status; - } - bytes=cnv->toUBytes; - byteIndex=cnv->toULength; - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex=byteIndex==0 ? 0 : -1; - nextSourceIndex=0; - - if(inDirectMode) { -directMode: - /* - * In Direct Mode, most US-ASCII characters are encoded directly, i.e., - * with their US-ASCII byte values. - * Backslash and Tilde and most control characters are not allowed in UTF-7. - * A plus sign starts Unicode (or "escape") Mode. - * - * In Direct Mode, only the sourceIndex is used. - */ - byteIndex=0; - length=(int32_t)(sourceLimit-source); - targetCapacity=(int32_t)(targetLimit-target); - if(length>targetCapacity) { - length=targetCapacity; - } - while(length>0) { - b=*source++; - if(!isLegalUTF7(b)) { - /* illegal */ - bytes[0]=b; - byteIndex=1; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } else if(b!=PLUS) { - /* write directly encoded character */ - *target++=b; - if(offsets!=NULL) { - *offsets++=sourceIndex++; - } - } else /* PLUS */ { - /* switch to Unicode mode */ - nextSourceIndex=++sourceIndex; - inDirectMode=false; - byteIndex=0; - bits=0; - base64Counter=-1; - goto unicodeMode; - } - --length; - } - if(source=targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - } else { -unicodeMode: - /* - * In Unicode (or "escape") Mode, UTF-16BE is base64-encoded. - * The base64 sequence ends with any character that is not in the base64 alphabet. - * A terminating minus sign is consumed. - * - * In Unicode Mode, the sourceIndex has the index to the start of the current - * base64 bytes, while nextSourceIndex is precisely parallel to source, - * keeping the index to the following byte. - * Note that in 2 out of 3 cases, UChars overlap within a base64 byte. - */ - while(source=126 || (base64Value=fromBase64[b])==-3 || base64Value==-1) { - /* either - * base64Value==-1 for any legal character except base64 and minus sign, or - * base64Value==-3 for illegal characters: - * 1. In either case, leave Unicode mode. - * 2.1. If we ended with an incomplete UChar or none after the +, then - * generate an error for the preceding erroneous sequence and deal with - * the current (possibly illegal) character next time through. - * 2.2. Else the current char comes after a complete UChar, which was already - * pushed to the output buf, so: - * 2.2.1. If the current char is legal, just save it for processing next time. - * It may be for example, a plus which we need to deal with in direct mode. - * 2.2.2. Else if the current char is illegal, we might as well deal with it here. - */ - inDirectMode=true; - if(base64Counter==-1) { - /* illegal: + immediately followed by something other than base64 or minus sign */ - /* include the plus sign in the reported sequence, but not the subsequent char */ - --source; - bytes[0]=PLUS; - byteIndex=1; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } else if(bits!=0) { - /* bits are illegally left over, a UChar is incomplete */ - /* don't include current char (legal or illegal) in error seq */ - --source; - --byteIndex; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } else { - /* previous UChar was complete */ - if(base64Value==-3) { - /* current character is illegal, deal with it here */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } else { - /* un-read the current character in case it is a plus sign */ - --source; - sourceIndex=nextSourceIndex-1; - goto directMode; - } - } - } else if(base64Value>=0) { - /* collect base64 bytes into UChars */ - switch(base64Counter) { - case -1: /* -1 is immediately after the + */ - case 0: - bits=base64Value; - base64Counter=1; - break; - case 1: - case 3: - case 4: - case 6: - bits=(uint16_t)((bits<<6)|base64Value); - ++base64Counter; - break; - case 2: - *target++=(UChar)((bits<<4)|(base64Value>>2)); - if(offsets!=NULL) { - *offsets++=sourceIndex; - sourceIndex=nextSourceIndex-1; - } - bytes[0]=b; /* keep this byte in case an error occurs */ - byteIndex=1; - bits=(uint16_t)(base64Value&3); - base64Counter=3; - break; - case 5: - *target++=(UChar)((bits<<2)|(base64Value>>4)); - if(offsets!=NULL) { - *offsets++=sourceIndex; - sourceIndex=nextSourceIndex-1; - } - bytes[0]=b; /* keep this byte in case an error occurs */ - byteIndex=1; - bits=(uint16_t)(base64Value&15); - base64Counter=6; - break; - case 7: - *target++=(UChar)((bits<<6)|base64Value); - if(offsets!=NULL) { - *offsets++=sourceIndex; - sourceIndex=nextSourceIndex; - } - byteIndex=0; - bits=0; - base64Counter=0; - break; - default: - /* will never occur */ - break; - } - } else /*base64Value==-2*/ { - /* minus sign terminates the base64 sequence */ - inDirectMode=true; - if(base64Counter==-1) { - /* +- i.e. a minus immediately following a plus */ - *target++=PLUS; - if(offsets!=NULL) { - *offsets++=sourceIndex-1; - } - } else { - /* absorb the minus and leave the Unicode Mode */ - if(bits!=0) { - /* bits are illegally left over, a UChar is incomplete */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } - } - sourceIndex=nextSourceIndex; - goto directMode; - } - } else { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - } - - if(U_SUCCESS(*pErrorCode) && pArgs->flush && source==sourceLimit && bits==0) { - /* - * if we are in Unicode mode, then the byteIndex might not be 0, - * but that is ok if bits==0 - * -> we set byteIndex=0 at the end of the stream to avoid a truncated error - * (not true for IMAP-mailbox-name where we must end in direct mode) - */ - byteIndex=0; - } - - /* set the converter state back into UConverter */ - cnv->toUnicodeStatus=((uint32_t)inDirectMode<<24)|((uint32_t)((uint8_t)base64Counter)<<16)|(uint32_t)bits; - cnv->toULength=byteIndex; - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - pArgs->offsets=offsets; - return; -} - -static void U_CALLCONV -_UTF7FromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const UChar *source, *sourceLimit; - uint8_t *target, *targetLimit; - int32_t *offsets; - - int32_t length, targetCapacity, sourceIndex; - UChar c; - - /* UTF-7 state */ - const UBool *encodeDirectly; - uint8_t bits; - int8_t base64Counter; - UBool inDirectMode; - - /* set up the local pointers */ - cnv=pArgs->converter; - - /* set up the local pointers */ - source=pArgs->source; - sourceLimit=pArgs->sourceLimit; - target=(uint8_t *)pArgs->target; - targetLimit=(uint8_t *)pArgs->targetLimit; - offsets=pArgs->offsets; - - /* get the state machine state */ - { - uint32_t status=cnv->fromUnicodeStatus; - encodeDirectly= status<0x10000000 ? encodeDirectlyMaximum : encodeDirectlyRestricted; - inDirectMode=(UBool)((status>>24)&1); - base64Counter=(int8_t)(status>>16); - bits=(uint8_t)status; - U_ASSERT(bits<=UPRV_LENGTHOF(toBase64)); - } - - /* UTF-7 always encodes UTF-16 code units, therefore we need only a simple sourceIndex */ - sourceIndex=0; - - if(inDirectMode) { -directMode: - length=(int32_t)(sourceLimit-source); - targetCapacity=(int32_t)(targetLimit-target); - if(length>targetCapacity) { - length=targetCapacity; - } - while(length>0) { - c=*source++; - /* currently always encode CR LF SP TAB directly */ - if(c<=127 && encodeDirectly[c]) { - /* encode directly */ - *target++=(uint8_t)c; - if(offsets!=NULL) { - *offsets++=sourceIndex++; - } - } else if(c==PLUS) { - /* output +- for + */ - *target++=PLUS; - if(targetcharErrorBuffer[0]=MINUS; - cnv->charErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } else { - /* un-read this character and switch to Unicode Mode */ - --source; - *target++=PLUS; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - inDirectMode=false; - base64Counter=0; - goto unicodeMode; - } - --length; - } - if(source=targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - } else { -unicodeMode: - while(sourcecharErrorBuffer[0]=MINUS; - cnv->charErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - goto directMode; - } else { - /* - * base64 this character: - * Output 2 or 3 base64 bytes for the remaining bits of the previous character - * and the bits of this character, each implicitly in UTF-16BE. - * - * Here, bits is an 8-bit variable because only 6 bits need to be kept from one - * character to the next. The actual 2 or 4 bits are shifted to the left edge - * of the 6-bits field 5..0 to make the termination of the base64 sequence easier. - */ - switch(base64Counter) { - case 0: - *target++=toBase64[c>>10]; - if(target>4)&0x3f]; - if(offsets!=NULL) { - *offsets++=sourceIndex; - *offsets++=sourceIndex++; - } - } else { - if(offsets!=NULL) { - *offsets++=sourceIndex++; - } - cnv->charErrorBuffer[0]=toBase64[(c>>4)&0x3f]; - cnv->charErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - bits=(uint8_t)((c&15)<<2); - base64Counter=1; - break; - case 1: - *target++=toBase64[bits|(c>>14)]; - if(target>8)&0x3f]; - if(target>2)&0x3f]; - if(offsets!=NULL) { - *offsets++=sourceIndex; - *offsets++=sourceIndex; - *offsets++=sourceIndex++; - } - } else { - if(offsets!=NULL) { - *offsets++=sourceIndex; - *offsets++=sourceIndex++; - } - cnv->charErrorBuffer[0]=toBase64[(c>>2)&0x3f]; - cnv->charErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - } else { - if(offsets!=NULL) { - *offsets++=sourceIndex++; - } - cnv->charErrorBuffer[0]=toBase64[(c>>8)&0x3f]; - cnv->charErrorBuffer[1]=toBase64[(c>>2)&0x3f]; - cnv->charErrorBufferLength=2; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - bits=(uint8_t)((c&3)<<4); - base64Counter=2; - break; - case 2: - *target++=toBase64[bits|(c>>12)]; - if(target>6)&0x3f]; - if(targetcharErrorBuffer[0]=toBase64[c&0x3f]; - cnv->charErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - } else { - if(offsets!=NULL) { - *offsets++=sourceIndex++; - } - cnv->charErrorBuffer[0]=toBase64[(c>>6)&0x3f]; - cnv->charErrorBuffer[1]=toBase64[c&0x3f]; - cnv->charErrorBufferLength=2; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - bits=0; - base64Counter=0; - break; - default: - /* will never occur */ - break; - } - } - } else { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - } - - if(pArgs->flush && source>=sourceLimit) { - /* flush remaining bits to the target */ - if(!inDirectMode) { - if (base64Counter!=0) { - if(targetcharErrorBuffer[cnv->charErrorBufferLength++]=toBase64[bits]; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - } - /* Add final MINUS to terminate unicodeMode */ - if(targetcharErrorBuffer[cnv->charErrorBufferLength++]=MINUS; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - } - /* reset the state for the next conversion */ - cnv->fromUnicodeStatus=(cnv->fromUnicodeStatus&0xf0000000)|0x1000000; /* keep version, inDirectMode=true */ - } else { - /* set the converter state back into UConverter */ - cnv->fromUnicodeStatus= - (cnv->fromUnicodeStatus&0xf0000000)| /* keep version*/ - ((uint32_t)inDirectMode<<24)|((uint32_t)base64Counter<<16)|(uint32_t)bits; - } - - /* write back the updated pointers */ - pArgs->source=source; - pArgs->target=(char *)target; - pArgs->offsets=offsets; - return; -} - -static const char * U_CALLCONV -_UTF7GetName(const UConverter *cnv) { - switch(cnv->fromUnicodeStatus>>28) { - case 1: - return "UTF-7,version=1"; - default: - return "UTF-7"; - } -} -U_CDECL_END - -static const UConverterImpl _UTF7Impl={ - UCNV_UTF7, - - NULL, - NULL, - - _UTF7Open, - NULL, - _UTF7Reset, - - _UTF7ToUnicodeWithOffsets, - _UTF7ToUnicodeWithOffsets, - _UTF7FromUnicodeWithOffsets, - _UTF7FromUnicodeWithOffsets, - NULL, - - NULL, - _UTF7GetName, - NULL, /* we don't need writeSub() because we never call a callback at fromUnicode() */ - NULL, - ucnv_getCompleteUnicodeSet, - - NULL, - NULL -}; - -static const UConverterStaticData _UTF7StaticData={ - sizeof(UConverterStaticData), - "UTF-7", - 0, /* TODO CCSID for UTF-7 */ - UCNV_IBM, UCNV_UTF7, - 1, 4, - { 0x3f, 0, 0, 0 }, 1, /* the subchar is not used */ - false, false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - -const UConverterSharedData _UTF7Data= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF7StaticData, &_UTF7Impl); - -/* IMAP mailbox name encoding ----------------------------------------------- */ - -/* - * RFC 2060: INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1 - * http://www.ietf.org/rfc/rfc2060.txt - * - * 5.1.3. Mailbox International Naming Convention - * - * By convention, international mailbox names are specified using a - * modified version of the UTF-7 encoding described in [UTF-7]. The - * purpose of these modifications is to correct the following problems - * with UTF-7: - * - * 1) UTF-7 uses the "+" character for shifting; this conflicts with - * the common use of "+" in mailbox names, in particular USENET - * newsgroup names. - * - * 2) UTF-7's encoding is BASE64 which uses the "/" character; this - * conflicts with the use of "/" as a popular hierarchy delimiter. - * - * 3) UTF-7 prohibits the unencoded usage of "\"; this conflicts with - * the use of "\" as a popular hierarchy delimiter. - * - * 4) UTF-7 prohibits the unencoded usage of "~"; this conflicts with - * the use of "~" in some servers as a home directory indicator. - * - * 5) UTF-7 permits multiple alternate forms to represent the same - * string; in particular, printable US-ASCII characters can be - * represented in encoded form. - * - * In modified UTF-7, printable US-ASCII characters except for "&" - * represent themselves; that is, characters with octet values 0x20-0x25 - * and 0x27-0x7e. The character "&" (0x26) is represented by the two- - * octet sequence "&-". - * - * All other characters (octet values 0x00-0x1f, 0x7f-0xff, and all - * Unicode 16-bit octets) are represented in modified BASE64, with a - * further modification from [UTF-7] that "," is used instead of "/". - * Modified BASE64 MUST NOT be used to represent any printing US-ASCII - * character which can represent itself. - * - * "&" is used to shift to modified BASE64 and "-" to shift back to US- - * ASCII. All names start in US-ASCII, and MUST end in US-ASCII (that - * is, a name that ends with a Unicode 16-bit octet MUST end with a "- - * "). - * - * For example, here is a mailbox name which mixes English, Japanese, - * and Chinese text: ~peter/mail/&ZeVnLIqe-/&U,BTFw- - */ - -/* - * Tests for US-ASCII characters belonging to character classes - * defined in UTF-7. - * - * Set D (directly encoded characters) consists of the following - * characters: the upper and lower case letters A through Z - * and a through z, the 10 digits 0-9, and the following nine special - * characters (note that "+" and "=" are omitted): - * '(),-./:? - * - * Set O (optional direct characters) consists of the following - * characters (note that "\" and "~" are omitted): - * !"#$%&*;<=>@[]^_`{|} - * - * According to the rules in RFC 2152, the byte values for the following - * US-ASCII characters are not used in UTF-7 and are therefore illegal: - * - all C0 control codes except for CR LF TAB - * - BACKSLASH - * - TILDE - * - DEL - * - all codes beyond US-ASCII, i.e. all >127 - */ - -/* uses '&' not '+' to start a base64 sequence */ -#define AMPERSAND 0x26 -#define COMMA 0x2c -#define SLASH 0x2f - -/* legal byte values: all US-ASCII graphic characters 0x20..0x7e */ -#define isLegalIMAP(c) (0x20<=(c) && (c)<=0x7e) - -/* direct-encode all of printable ASCII 0x20..0x7e except '&' 0x26 */ -#define inSetDIMAP(c) (isLegalIMAP(c) && c!=AMPERSAND) - -#define TO_BASE64_IMAP(n) ((n)<63 ? toBase64[n] : COMMA) -#define FROM_BASE64_IMAP(c) ((c)==COMMA ? 63 : (c)==SLASH ? -1 : fromBase64[c]) - -/* - * converter status values: - * - * toUnicodeStatus: - * 24 inDirectMode (boolean) - * 23..16 base64Counter (-1..7) - * 15..0 bits (up to 14 bits incoming base64) - * - * fromUnicodeStatus: - * 24 inDirectMode (boolean) - * 23..16 base64Counter (0..2) - * 7..0 bits (6 bits outgoing base64) - * - * ignore bits 31..25 - */ - -U_CDECL_BEGIN -static void U_CALLCONV -_IMAPToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const uint8_t *source, *sourceLimit; - UChar *target; - const UChar *targetLimit; - int32_t *offsets; - - uint8_t *bytes; - uint8_t byteIndex; - - int32_t length, targetCapacity; - - /* UTF-7 state */ - uint16_t bits; - int8_t base64Counter; - UBool inDirectMode; - - int8_t base64Value; - - int32_t sourceIndex, nextSourceIndex; - - UChar c; - uint8_t b; - - /* set up the local pointers */ - cnv=pArgs->converter; - - source=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - target=pArgs->target; - targetLimit=pArgs->targetLimit; - offsets=pArgs->offsets; - /* get the state machine state */ - { - uint32_t status=cnv->toUnicodeStatus; - inDirectMode=(UBool)((status>>24)&1); - base64Counter=(int8_t)(status>>16); - bits=(uint16_t)status; - } - bytes=cnv->toUBytes; - byteIndex=cnv->toULength; - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex=byteIndex==0 ? 0 : -1; - nextSourceIndex=0; - - if(inDirectMode) { -directMode: - /* - * In Direct Mode, US-ASCII characters are encoded directly, i.e., - * with their US-ASCII byte values. - * An ampersand starts Unicode (or "escape") Mode. - * - * In Direct Mode, only the sourceIndex is used. - */ - byteIndex=0; - length=(int32_t)(sourceLimit-source); - targetCapacity=(int32_t)(targetLimit-target); - if(length>targetCapacity) { - length=targetCapacity; - } - while(length>0) { - b=*source++; - if(!isLegalIMAP(b)) { - /* illegal */ - bytes[0]=b; - byteIndex=1; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } else if(b!=AMPERSAND) { - /* write directly encoded character */ - *target++=b; - if(offsets!=NULL) { - *offsets++=sourceIndex++; - } - } else /* AMPERSAND */ { - /* switch to Unicode mode */ - nextSourceIndex=++sourceIndex; - inDirectMode=false; - byteIndex=0; - bits=0; - base64Counter=-1; - goto unicodeMode; - } - --length; - } - if(source=targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - } else { -unicodeMode: - /* - * In Unicode (or "escape") Mode, UTF-16BE is base64-encoded. - * The base64 sequence ends with any character that is not in the base64 alphabet. - * A terminating minus sign is consumed. - * US-ASCII must not be base64-ed. - * - * In Unicode Mode, the sourceIndex has the index to the start of the current - * base64 bytes, while nextSourceIndex is precisely parallel to source, - * keeping the index to the following byte. - * Note that in 2 out of 3 cases, UChars overlap within a base64 byte. - */ - while(source0x7e) { - /* illegal - test other illegal US-ASCII values by base64Value==-3 */ - inDirectMode=true; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } else if((base64Value=FROM_BASE64_IMAP(b))>=0) { - /* collect base64 bytes into UChars */ - switch(base64Counter) { - case -1: /* -1 is immediately after the & */ - case 0: - bits=base64Value; - base64Counter=1; - break; - case 1: - case 3: - case 4: - case 6: - bits=(uint16_t)((bits<<6)|base64Value); - ++base64Counter; - break; - case 2: - c=(UChar)((bits<<4)|(base64Value>>2)); - if(isLegalIMAP(c)) { - /* illegal */ - inDirectMode=true; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - goto endloop; - } - *target++=c; - if(offsets!=NULL) { - *offsets++=sourceIndex; - sourceIndex=nextSourceIndex-1; - } - bytes[0]=b; /* keep this byte in case an error occurs */ - byteIndex=1; - bits=(uint16_t)(base64Value&3); - base64Counter=3; - break; - case 5: - c=(UChar)((bits<<2)|(base64Value>>4)); - if(isLegalIMAP(c)) { - /* illegal */ - inDirectMode=true; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - goto endloop; - } - *target++=c; - if(offsets!=NULL) { - *offsets++=sourceIndex; - sourceIndex=nextSourceIndex-1; - } - bytes[0]=b; /* keep this byte in case an error occurs */ - byteIndex=1; - bits=(uint16_t)(base64Value&15); - base64Counter=6; - break; - case 7: - c=(UChar)((bits<<6)|base64Value); - if(isLegalIMAP(c)) { - /* illegal */ - inDirectMode=true; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - goto endloop; - } - *target++=c; - if(offsets!=NULL) { - *offsets++=sourceIndex; - sourceIndex=nextSourceIndex; - } - byteIndex=0; - bits=0; - base64Counter=0; - break; - default: - /* will never occur */ - break; - } - } else if(base64Value==-2) { - /* minus sign terminates the base64 sequence */ - inDirectMode=true; - if(base64Counter==-1) { - /* &- i.e. a minus immediately following an ampersand */ - *target++=AMPERSAND; - if(offsets!=NULL) { - *offsets++=sourceIndex-1; - } - } else { - /* absorb the minus and leave the Unicode Mode */ - if(bits!=0 || (base64Counter!=0 && base64Counter!=3 && base64Counter!=6)) { - /* bits are illegally left over, a UChar is incomplete */ - /* base64Counter other than 0, 3, 6 means non-minimal zero-padding, also illegal */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } - } - sourceIndex=nextSourceIndex; - goto directMode; - } else { - if(base64Counter==-1) { - /* illegal: & immediately followed by something other than base64 or minus sign */ - /* include the ampersand in the reported sequence */ - --sourceIndex; - bytes[0]=AMPERSAND; - bytes[1]=b; - byteIndex=2; - } - /* base64Value==-1 for characters that are illegal only in Unicode mode */ - /* base64Value==-3 for illegal characters */ - /* illegal */ - inDirectMode=true; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } - } else { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - } -endloop: - - /* - * the end of the input stream and detection of truncated input - * are handled by the framework, but here we must check if we are in Unicode - * mode and byteIndex==0 because we must end in direct mode - * - * conditions: - * successful - * in Unicode mode and byteIndex==0 - * end of input and no truncated input - */ - if( U_SUCCESS(*pErrorCode) && - !inDirectMode && byteIndex==0 && - pArgs->flush && source>=sourceLimit - ) { - if(base64Counter==-1) { - /* & at the very end of the input */ - /* make the ampersand the reported sequence */ - bytes[0]=AMPERSAND; - byteIndex=1; - } - /* else if(base64Counter!=-1) byteIndex remains 0 because there is no particular byte sequence */ - - inDirectMode=true; /* avoid looping */ - *pErrorCode=U_TRUNCATED_CHAR_FOUND; - } - - /* set the converter state back into UConverter */ - cnv->toUnicodeStatus=((uint32_t)inDirectMode<<24)|((uint32_t)((uint8_t)base64Counter)<<16)|(uint32_t)bits; - cnv->toULength=byteIndex; - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - pArgs->offsets=offsets; - return; -} - -static void U_CALLCONV -_IMAPFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const UChar *source, *sourceLimit; - uint8_t *target, *targetLimit; - int32_t *offsets; - - int32_t length, targetCapacity, sourceIndex; - UChar c; - uint8_t b; - - /* UTF-7 state */ - uint8_t bits; - int8_t base64Counter; - UBool inDirectMode; - - /* set up the local pointers */ - cnv=pArgs->converter; - - /* set up the local pointers */ - source=pArgs->source; - sourceLimit=pArgs->sourceLimit; - target=(uint8_t *)pArgs->target; - targetLimit=(uint8_t *)pArgs->targetLimit; - offsets=pArgs->offsets; - - /* get the state machine state */ - { - uint32_t status=cnv->fromUnicodeStatus; - inDirectMode=(UBool)((status>>24)&1); - base64Counter=(int8_t)(status>>16); - bits=(uint8_t)status; - } - - /* UTF-7 always encodes UTF-16 code units, therefore we need only a simple sourceIndex */ - sourceIndex=0; - - if(inDirectMode) { -directMode: - length=(int32_t)(sourceLimit-source); - targetCapacity=(int32_t)(targetLimit-target); - if(length>targetCapacity) { - length=targetCapacity; - } - while(length>0) { - c=*source++; - /* encode 0x20..0x7e except '&' directly */ - if(inSetDIMAP(c)) { - /* encode directly */ - *target++=(uint8_t)c; - if(offsets!=NULL) { - *offsets++=sourceIndex++; - } - } else if(c==AMPERSAND) { - /* output &- for & */ - *target++=AMPERSAND; - if(targetcharErrorBuffer[0]=MINUS; - cnv->charErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } else { - /* un-read this character and switch to Unicode Mode */ - --source; - *target++=AMPERSAND; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - inDirectMode=false; - base64Counter=0; - goto unicodeMode; - } - --length; - } - if(source=targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - } else { -unicodeMode: - while(sourcecharErrorBuffer[0]=MINUS; - cnv->charErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - goto directMode; - } else { - /* - * base64 this character: - * Output 2 or 3 base64 bytes for the remaining bits of the previous character - * and the bits of this character, each implicitly in UTF-16BE. - * - * Here, bits is an 8-bit variable because only 6 bits need to be kept from one - * character to the next. The actual 2 or 4 bits are shifted to the left edge - * of the 6-bits field 5..0 to make the termination of the base64 sequence easier. - */ - switch(base64Counter) { - case 0: - b=(uint8_t)(c>>10); - *target++=TO_BASE64_IMAP(b); - if(target>4)&0x3f); - *target++=TO_BASE64_IMAP(b); - if(offsets!=NULL) { - *offsets++=sourceIndex; - *offsets++=sourceIndex++; - } - } else { - if(offsets!=NULL) { - *offsets++=sourceIndex++; - } - b=(uint8_t)((c>>4)&0x3f); - cnv->charErrorBuffer[0]=TO_BASE64_IMAP(b); - cnv->charErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - bits=(uint8_t)((c&15)<<2); - base64Counter=1; - break; - case 1: - b=(uint8_t)(bits|(c>>14)); - *target++=TO_BASE64_IMAP(b); - if(target>8)&0x3f); - *target++=TO_BASE64_IMAP(b); - if(target>2)&0x3f); - *target++=TO_BASE64_IMAP(b); - if(offsets!=NULL) { - *offsets++=sourceIndex; - *offsets++=sourceIndex; - *offsets++=sourceIndex++; - } - } else { - if(offsets!=NULL) { - *offsets++=sourceIndex; - *offsets++=sourceIndex++; - } - b=(uint8_t)((c>>2)&0x3f); - cnv->charErrorBuffer[0]=TO_BASE64_IMAP(b); - cnv->charErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - } else { - if(offsets!=NULL) { - *offsets++=sourceIndex++; - } - b=(uint8_t)((c>>8)&0x3f); - cnv->charErrorBuffer[0]=TO_BASE64_IMAP(b); - b=(uint8_t)((c>>2)&0x3f); - cnv->charErrorBuffer[1]=TO_BASE64_IMAP(b); - cnv->charErrorBufferLength=2; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - bits=(uint8_t)((c&3)<<4); - base64Counter=2; - break; - case 2: - b=(uint8_t)(bits|(c>>12)); - *target++=TO_BASE64_IMAP(b); - if(target>6)&0x3f); - *target++=TO_BASE64_IMAP(b); - if(targetcharErrorBuffer[0]=TO_BASE64_IMAP(b); - cnv->charErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - } else { - if(offsets!=NULL) { - *offsets++=sourceIndex++; - } - b=(uint8_t)((c>>6)&0x3f); - cnv->charErrorBuffer[0]=TO_BASE64_IMAP(b); - b=(uint8_t)(c&0x3f); - cnv->charErrorBuffer[1]=TO_BASE64_IMAP(b); - cnv->charErrorBufferLength=2; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - bits=0; - base64Counter=0; - break; - default: - /* will never occur */ - break; - } - } - } else { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - } - - if(pArgs->flush && source>=sourceLimit) { - /* flush remaining bits to the target */ - if(!inDirectMode) { - if(base64Counter!=0) { - if(targetcharErrorBuffer[cnv->charErrorBufferLength++]=TO_BASE64_IMAP(bits); - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - } - /* need to terminate with a minus */ - if(targetcharErrorBuffer[cnv->charErrorBufferLength++]=MINUS; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - } - /* reset the state for the next conversion */ - cnv->fromUnicodeStatus=(cnv->fromUnicodeStatus&0xf0000000)|0x1000000; /* keep version, inDirectMode=true */ - } else { - /* set the converter state back into UConverter */ - cnv->fromUnicodeStatus= - (cnv->fromUnicodeStatus&0xf0000000)| /* keep version*/ - ((uint32_t)inDirectMode<<24)|((uint32_t)base64Counter<<16)|(uint32_t)bits; - } - - /* write back the updated pointers */ - pArgs->source=source; - pArgs->target=(char *)target; - pArgs->offsets=offsets; - return; -} -U_CDECL_END - -static const UConverterImpl _IMAPImpl={ - UCNV_IMAP_MAILBOX, - - NULL, - NULL, - - _UTF7Open, - NULL, - _UTF7Reset, - - _IMAPToUnicodeWithOffsets, - _IMAPToUnicodeWithOffsets, - _IMAPFromUnicodeWithOffsets, - _IMAPFromUnicodeWithOffsets, - NULL, - - NULL, - NULL, - NULL, /* we don't need writeSub() because we never call a callback at fromUnicode() */ - NULL, - ucnv_getCompleteUnicodeSet, - NULL, - NULL -}; - -static const UConverterStaticData _IMAPStaticData={ - sizeof(UConverterStaticData), - "IMAP-mailbox-name", - 0, /* TODO CCSID for IMAP-mailbox-name */ - UCNV_IBM, UCNV_IMAP_MAILBOX, - 1, 4, - { 0x3f, 0, 0, 0 }, 1, /* the subchar is not used */ - false, false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - -const UConverterSharedData _IMAPData= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_IMAPStaticData, &_IMAPImpl); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2002-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: ucnv_u7.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002jul01 +* created by: Markus W. Scherer +* +* UTF-7 converter implementation. Used to be in ucnv_utf.c. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION + +#include "cmemory.h" +#include "unicode/ucnv.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" +#include "uassert.h" + +/* UTF-7 -------------------------------------------------------------------- */ + +/* + * UTF-7 is a stateful encoding of Unicode. + * It is defined in RFC 2152. (http://www.ietf.org/rfc/rfc2152.txt) + * It was intended for use in Internet email systems, using in its bytewise + * encoding only a subset of 7-bit US-ASCII. + * UTF-7 is deprecated in favor of UTF-8/16/32 and SCSU, but still + * occasionally used. + * + * For converting Unicode to UTF-7, the RFC allows to encode some US-ASCII + * characters directly or in base64. Especially, the characters in set O + * as defined in the RFC (see below) may be encoded directly but are not + * allowed in, e.g., email headers. + * By default, the ICU UTF-7 converter encodes set O directly. + * By choosing the option "version=1", set O will be escaped instead. + * For example: + * utf7Converter=ucnv_open("UTF-7,version=1"); + * + * For details about email headers see RFC 2047. + */ + +/* + * Tests for US-ASCII characters belonging to character classes + * defined in UTF-7. + * + * Set D (directly encoded characters) consists of the following + * characters: the upper and lower case letters A through Z + * and a through z, the 10 digits 0-9, and the following nine special + * characters (note that "+" and "=" are omitted): + * '(),-./:? + * + * Set O (optional direct characters) consists of the following + * characters (note that "\" and "~" are omitted): + * !"#$%&*;<=>@[]^_`{|} + * + * According to the rules in RFC 2152, the byte values for the following + * US-ASCII characters are not used in UTF-7 and are therefore illegal: + * - all C0 control codes except for CR LF TAB + * - BACKSLASH + * - TILDE + * - DEL + * - all codes beyond US-ASCII, i.e. all >127 + */ +#define inSetD(c) \ + ((uint8_t)((c)-97)<26 || (uint8_t)((c)-65)<26 || /* letters */ \ + (uint8_t)((c)-48)<10 || /* digits */ \ + (uint8_t)((c)-39)<3 || /* '() */ \ + (uint8_t)((c)-44)<4 || /* ,-./ */ \ + (c)==58 || (c)==63 /* :? */ \ + ) + +#define inSetO(c) \ + ((uint8_t)((c)-33)<6 || /* !"#$%& */ \ + (uint8_t)((c)-59)<4 || /* ;<=> */ \ + (uint8_t)((c)-93)<4 || /* ]^_` */ \ + (uint8_t)((c)-123)<3 || /* {|} */ \ + (c)==42 || (c)==64 || (c)==91 /* *@[ */ \ + ) + +#define isCRLFTAB(c) ((c)==13 || (c)==10 || (c)==9) +#define isCRLFSPTAB(c) ((c)==32 || (c)==13 || (c)==10 || (c)==9) + +#define PLUS 43 +#define MINUS 45 +#define BACKSLASH 92 +#define TILDE 126 + +/* legal byte values: all US-ASCII graphic characters from space to before tilde, and CR LF TAB */ +#define isLegalUTF7(c) (((uint8_t)((c)-32)<94 && (c)!=BACKSLASH) || isCRLFTAB(c)) + +/* encode directly sets D and O and CR LF SP TAB */ +static const UBool encodeDirectlyMaximum[128]={ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 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, 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, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1, 0, 0 +}; + +/* encode directly set D and CR LF SP TAB but not set O */ +static const UBool encodeDirectlyRestricted[128]={ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, + + 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, 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, 0, 0, 0, 0, 0 +}; + +static const uint8_t +toBase64[64]={ + /* A-Z */ + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + /* a-z */ + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + /* 0-9 */ + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* +/ */ + 43, 47 +}; + +static const int8_t +fromBase64[128]={ + /* C0 controls, -1 for legal ones (CR LF TAB), -3 for illegal ones */ + -3, -3, -3, -3, -3, -3, -3, -3, -3, -1, -1, -3, -3, -1, -3, -3, + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, + + /* general punctuation with + and / and a special value (-2) for - */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -2, -1, 63, + /* digits */ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + + /* A-Z */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -3, -1, -1, -1, + + /* a-z */ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -3, -3 +}; + +/* + * converter status values: + * + * toUnicodeStatus: + * 24 inDirectMode (boolean) + * 23..16 base64Counter (-1..7) + * 15..0 bits (up to 14 bits incoming base64) + * + * fromUnicodeStatus: + * 31..28 version (0: set O direct 1: set O escaped) + * 24 inDirectMode (boolean) + * 23..16 base64Counter (0..2) + * 7..0 bits (6 bits outgoing base64) + * + */ + +U_CDECL_BEGIN +static void U_CALLCONV +_UTF7Reset(UConverter *cnv, UConverterResetChoice choice) { + if(choice<=UCNV_RESET_TO_UNICODE) { + /* reset toUnicode */ + cnv->toUnicodeStatus=0x1000000; /* inDirectMode=true */ + cnv->toULength=0; + } + if(choice!=UCNV_RESET_TO_UNICODE) { + /* reset fromUnicode */ + cnv->fromUnicodeStatus=(cnv->fromUnicodeStatus&0xf0000000)|0x1000000; /* keep version, inDirectMode=true */ + } +} + +static void U_CALLCONV +_UTF7Open(UConverter *cnv, + UConverterLoadArgs *pArgs, + UErrorCode *pErrorCode) { + (void)pArgs; + if(UCNV_GET_VERSION(cnv)<=1) { + /* TODO(markus): Should just use cnv->options rather than copying the version number. */ + cnv->fromUnicodeStatus=UCNV_GET_VERSION(cnv)<<28; + _UTF7Reset(cnv, UCNV_RESET_BOTH); + } else { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + } +} + +static void U_CALLCONV +_UTF7ToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const uint8_t *source, *sourceLimit; + char16_t *target; + const char16_t *targetLimit; + int32_t *offsets; + + uint8_t *bytes; + uint8_t byteIndex; + + int32_t length, targetCapacity; + + /* UTF-7 state */ + uint16_t bits; + int8_t base64Counter; + UBool inDirectMode; + + int8_t base64Value; + + int32_t sourceIndex, nextSourceIndex; + + uint8_t b; + /* set up the local pointers */ + cnv=pArgs->converter; + + source=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + target=pArgs->target; + targetLimit=pArgs->targetLimit; + offsets=pArgs->offsets; + /* get the state machine state */ + { + uint32_t status=cnv->toUnicodeStatus; + inDirectMode=(UBool)((status>>24)&1); + base64Counter=(int8_t)(status>>16); + bits=(uint16_t)status; + } + bytes=cnv->toUBytes; + byteIndex=cnv->toULength; + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex=byteIndex==0 ? 0 : -1; + nextSourceIndex=0; + + if(inDirectMode) { +directMode: + /* + * In Direct Mode, most US-ASCII characters are encoded directly, i.e., + * with their US-ASCII byte values. + * Backslash and Tilde and most control characters are not allowed in UTF-7. + * A plus sign starts Unicode (or "escape") Mode. + * + * In Direct Mode, only the sourceIndex is used. + */ + byteIndex=0; + length=(int32_t)(sourceLimit-source); + targetCapacity=(int32_t)(targetLimit-target); + if(length>targetCapacity) { + length=targetCapacity; + } + while(length>0) { + b=*source++; + if(!isLegalUTF7(b)) { + /* illegal */ + bytes[0]=b; + byteIndex=1; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } else if(b!=PLUS) { + /* write directly encoded character */ + *target++=b; + if(offsets!=nullptr) { + *offsets++=sourceIndex++; + } + } else /* PLUS */ { + /* switch to Unicode mode */ + nextSourceIndex=++sourceIndex; + inDirectMode=false; + byteIndex=0; + bits=0; + base64Counter=-1; + goto unicodeMode; + } + --length; + } + if(source=targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + } else { +unicodeMode: + /* + * In Unicode (or "escape") Mode, UTF-16BE is base64-encoded. + * The base64 sequence ends with any character that is not in the base64 alphabet. + * A terminating minus sign is consumed. + * + * In Unicode Mode, the sourceIndex has the index to the start of the current + * base64 bytes, while nextSourceIndex is precisely parallel to source, + * keeping the index to the following byte. + * Note that in 2 out of 3 cases, UChars overlap within a base64 byte. + */ + while(source=126 || (base64Value=fromBase64[b])==-3 || base64Value==-1) { + /* either + * base64Value==-1 for any legal character except base64 and minus sign, or + * base64Value==-3 for illegal characters: + * 1. In either case, leave Unicode mode. + * 2.1. If we ended with an incomplete char16_t or none after the +, then + * generate an error for the preceding erroneous sequence and deal with + * the current (possibly illegal) character next time through. + * 2.2. Else the current char comes after a complete char16_t, which was already + * pushed to the output buf, so: + * 2.2.1. If the current char is legal, just save it for processing next time. + * It may be for example, a plus which we need to deal with in direct mode. + * 2.2.2. Else if the current char is illegal, we might as well deal with it here. + */ + inDirectMode=true; + if(base64Counter==-1) { + /* illegal: + immediately followed by something other than base64 or minus sign */ + /* include the plus sign in the reported sequence, but not the subsequent char */ + --source; + bytes[0]=PLUS; + byteIndex=1; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } else if(bits!=0) { + /* bits are illegally left over, a char16_t is incomplete */ + /* don't include current char (legal or illegal) in error seq */ + --source; + --byteIndex; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } else { + /* previous char16_t was complete */ + if(base64Value==-3) { + /* current character is illegal, deal with it here */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } else { + /* un-read the current character in case it is a plus sign */ + --source; + sourceIndex=nextSourceIndex-1; + goto directMode; + } + } + } else if(base64Value>=0) { + /* collect base64 bytes into UChars */ + switch(base64Counter) { + case -1: /* -1 is immediately after the + */ + case 0: + bits=base64Value; + base64Counter=1; + break; + case 1: + case 3: + case 4: + case 6: + bits=(uint16_t)((bits<<6)|base64Value); + ++base64Counter; + break; + case 2: + *target++=(char16_t)((bits<<4)|(base64Value>>2)); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + sourceIndex=nextSourceIndex-1; + } + bytes[0]=b; /* keep this byte in case an error occurs */ + byteIndex=1; + bits=(uint16_t)(base64Value&3); + base64Counter=3; + break; + case 5: + *target++=(char16_t)((bits<<2)|(base64Value>>4)); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + sourceIndex=nextSourceIndex-1; + } + bytes[0]=b; /* keep this byte in case an error occurs */ + byteIndex=1; + bits=(uint16_t)(base64Value&15); + base64Counter=6; + break; + case 7: + *target++=(char16_t)((bits<<6)|base64Value); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + sourceIndex=nextSourceIndex; + } + byteIndex=0; + bits=0; + base64Counter=0; + break; + default: + /* will never occur */ + break; + } + } else /*base64Value==-2*/ { + /* minus sign terminates the base64 sequence */ + inDirectMode=true; + if(base64Counter==-1) { + /* +- i.e. a minus immediately following a plus */ + *target++=PLUS; + if(offsets!=nullptr) { + *offsets++=sourceIndex-1; + } + } else { + /* absorb the minus and leave the Unicode Mode */ + if(bits!=0) { + /* bits are illegally left over, a char16_t is incomplete */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } + } + sourceIndex=nextSourceIndex; + goto directMode; + } + } else { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + } + + if(U_SUCCESS(*pErrorCode) && pArgs->flush && source==sourceLimit && bits==0) { + /* + * if we are in Unicode mode, then the byteIndex might not be 0, + * but that is ok if bits==0 + * -> we set byteIndex=0 at the end of the stream to avoid a truncated error + * (not true for IMAP-mailbox-name where we must end in direct mode) + */ + byteIndex=0; + } + + /* set the converter state back into UConverter */ + cnv->toUnicodeStatus=((uint32_t)inDirectMode<<24)|((uint32_t)((uint8_t)base64Counter)<<16)|(uint32_t)bits; + cnv->toULength=byteIndex; + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + pArgs->offsets=offsets; + return; +} + +static void U_CALLCONV +_UTF7FromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const char16_t *source, *sourceLimit; + uint8_t *target, *targetLimit; + int32_t *offsets; + + int32_t length, targetCapacity, sourceIndex; + char16_t c; + + /* UTF-7 state */ + const UBool *encodeDirectly; + uint8_t bits; + int8_t base64Counter; + UBool inDirectMode; + + /* set up the local pointers */ + cnv=pArgs->converter; + + /* set up the local pointers */ + source=pArgs->source; + sourceLimit=pArgs->sourceLimit; + target=(uint8_t *)pArgs->target; + targetLimit=(uint8_t *)pArgs->targetLimit; + offsets=pArgs->offsets; + + /* get the state machine state */ + { + uint32_t status=cnv->fromUnicodeStatus; + encodeDirectly= status<0x10000000 ? encodeDirectlyMaximum : encodeDirectlyRestricted; + inDirectMode=(UBool)((status>>24)&1); + base64Counter=(int8_t)(status>>16); + bits=(uint8_t)status; + U_ASSERT(bits<=UPRV_LENGTHOF(toBase64)); + } + + /* UTF-7 always encodes UTF-16 code units, therefore we need only a simple sourceIndex */ + sourceIndex=0; + + if(inDirectMode) { +directMode: + length=(int32_t)(sourceLimit-source); + targetCapacity=(int32_t)(targetLimit-target); + if(length>targetCapacity) { + length=targetCapacity; + } + while(length>0) { + c=*source++; + /* currently always encode CR LF SP TAB directly */ + if(c<=127 && encodeDirectly[c]) { + /* encode directly */ + *target++=(uint8_t)c; + if(offsets!=nullptr) { + *offsets++=sourceIndex++; + } + } else if(c==PLUS) { + /* output +- for + */ + *target++=PLUS; + if(targetcharErrorBuffer[0]=MINUS; + cnv->charErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } else { + /* un-read this character and switch to Unicode Mode */ + --source; + *target++=PLUS; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + inDirectMode=false; + base64Counter=0; + goto unicodeMode; + } + --length; + } + if(source=targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + } else { +unicodeMode: + while(sourcecharErrorBuffer[0]=MINUS; + cnv->charErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + goto directMode; + } else { + /* + * base64 this character: + * Output 2 or 3 base64 bytes for the remaining bits of the previous character + * and the bits of this character, each implicitly in UTF-16BE. + * + * Here, bits is an 8-bit variable because only 6 bits need to be kept from one + * character to the next. The actual 2 or 4 bits are shifted to the left edge + * of the 6-bits field 5..0 to make the termination of the base64 sequence easier. + */ + switch(base64Counter) { + case 0: + *target++=toBase64[c>>10]; + if(target>4)&0x3f]; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + *offsets++=sourceIndex++; + } + } else { + if(offsets!=nullptr) { + *offsets++=sourceIndex++; + } + cnv->charErrorBuffer[0]=toBase64[(c>>4)&0x3f]; + cnv->charErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + bits=(uint8_t)((c&15)<<2); + base64Counter=1; + break; + case 1: + *target++=toBase64[bits|(c>>14)]; + if(target>8)&0x3f]; + if(target>2)&0x3f]; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + *offsets++=sourceIndex; + *offsets++=sourceIndex++; + } + } else { + if(offsets!=nullptr) { + *offsets++=sourceIndex; + *offsets++=sourceIndex++; + } + cnv->charErrorBuffer[0]=toBase64[(c>>2)&0x3f]; + cnv->charErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + } else { + if(offsets!=nullptr) { + *offsets++=sourceIndex++; + } + cnv->charErrorBuffer[0]=toBase64[(c>>8)&0x3f]; + cnv->charErrorBuffer[1]=toBase64[(c>>2)&0x3f]; + cnv->charErrorBufferLength=2; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + bits=(uint8_t)((c&3)<<4); + base64Counter=2; + break; + case 2: + *target++=toBase64[bits|(c>>12)]; + if(target>6)&0x3f]; + if(targetcharErrorBuffer[0]=toBase64[c&0x3f]; + cnv->charErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + } else { + if(offsets!=nullptr) { + *offsets++=sourceIndex++; + } + cnv->charErrorBuffer[0]=toBase64[(c>>6)&0x3f]; + cnv->charErrorBuffer[1]=toBase64[c&0x3f]; + cnv->charErrorBufferLength=2; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + bits=0; + base64Counter=0; + break; + default: + /* will never occur */ + break; + } + } + } else { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + } + + if(pArgs->flush && source>=sourceLimit) { + /* flush remaining bits to the target */ + if(!inDirectMode) { + if (base64Counter!=0) { + if(targetcharErrorBuffer[cnv->charErrorBufferLength++]=toBase64[bits]; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + } + /* Add final MINUS to terminate unicodeMode */ + if(targetcharErrorBuffer[cnv->charErrorBufferLength++]=MINUS; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + } + /* reset the state for the next conversion */ + cnv->fromUnicodeStatus=(cnv->fromUnicodeStatus&0xf0000000)|0x1000000; /* keep version, inDirectMode=true */ + } else { + /* set the converter state back into UConverter */ + cnv->fromUnicodeStatus= + (cnv->fromUnicodeStatus&0xf0000000)| /* keep version*/ + ((uint32_t)inDirectMode<<24)|((uint32_t)base64Counter<<16)|(uint32_t)bits; + } + + /* write back the updated pointers */ + pArgs->source=source; + pArgs->target=(char *)target; + pArgs->offsets=offsets; + return; +} + +static const char * U_CALLCONV +_UTF7GetName(const UConverter *cnv) { + switch(cnv->fromUnicodeStatus>>28) { + case 1: + return "UTF-7,version=1"; + default: + return "UTF-7"; + } +} +U_CDECL_END + +static const UConverterImpl _UTF7Impl={ + UCNV_UTF7, + + nullptr, + nullptr, + + _UTF7Open, + nullptr, + _UTF7Reset, + + _UTF7ToUnicodeWithOffsets, + _UTF7ToUnicodeWithOffsets, + _UTF7FromUnicodeWithOffsets, + _UTF7FromUnicodeWithOffsets, + nullptr, + + nullptr, + _UTF7GetName, + nullptr, /* we don't need writeSub() because we never call a callback at fromUnicode() */ + nullptr, + ucnv_getCompleteUnicodeSet, + + nullptr, + nullptr +}; + +static const UConverterStaticData _UTF7StaticData={ + sizeof(UConverterStaticData), + "UTF-7", + 0, /* TODO CCSID for UTF-7 */ + UCNV_IBM, UCNV_UTF7, + 1, 4, + { 0x3f, 0, 0, 0 }, 1, /* the subchar is not used */ + false, false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + +const UConverterSharedData _UTF7Data= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF7StaticData, &_UTF7Impl); + +/* IMAP mailbox name encoding ----------------------------------------------- */ + +/* + * RFC 2060: INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1 + * http://www.ietf.org/rfc/rfc2060.txt + * + * 5.1.3. Mailbox International Naming Convention + * + * By convention, international mailbox names are specified using a + * modified version of the UTF-7 encoding described in [UTF-7]. The + * purpose of these modifications is to correct the following problems + * with UTF-7: + * + * 1) UTF-7 uses the "+" character for shifting; this conflicts with + * the common use of "+" in mailbox names, in particular USENET + * newsgroup names. + * + * 2) UTF-7's encoding is BASE64 which uses the "/" character; this + * conflicts with the use of "/" as a popular hierarchy delimiter. + * + * 3) UTF-7 prohibits the unencoded usage of "\"; this conflicts with + * the use of "\" as a popular hierarchy delimiter. + * + * 4) UTF-7 prohibits the unencoded usage of "~"; this conflicts with + * the use of "~" in some servers as a home directory indicator. + * + * 5) UTF-7 permits multiple alternate forms to represent the same + * string; in particular, printable US-ASCII characters can be + * represented in encoded form. + * + * In modified UTF-7, printable US-ASCII characters except for "&" + * represent themselves; that is, characters with octet values 0x20-0x25 + * and 0x27-0x7e. The character "&" (0x26) is represented by the two- + * octet sequence "&-". + * + * All other characters (octet values 0x00-0x1f, 0x7f-0xff, and all + * Unicode 16-bit octets) are represented in modified BASE64, with a + * further modification from [UTF-7] that "," is used instead of "/". + * Modified BASE64 MUST NOT be used to represent any printing US-ASCII + * character which can represent itself. + * + * "&" is used to shift to modified BASE64 and "-" to shift back to US- + * ASCII. All names start in US-ASCII, and MUST end in US-ASCII (that + * is, a name that ends with a Unicode 16-bit octet MUST end with a "- + * "). + * + * For example, here is a mailbox name which mixes English, Japanese, + * and Chinese text: ~peter/mail/&ZeVnLIqe-/&U,BTFw- + */ + +/* + * Tests for US-ASCII characters belonging to character classes + * defined in UTF-7. + * + * Set D (directly encoded characters) consists of the following + * characters: the upper and lower case letters A through Z + * and a through z, the 10 digits 0-9, and the following nine special + * characters (note that "+" and "=" are omitted): + * '(),-./:? + * + * Set O (optional direct characters) consists of the following + * characters (note that "\" and "~" are omitted): + * !"#$%&*;<=>@[]^_`{|} + * + * According to the rules in RFC 2152, the byte values for the following + * US-ASCII characters are not used in UTF-7 and are therefore illegal: + * - all C0 control codes except for CR LF TAB + * - BACKSLASH + * - TILDE + * - DEL + * - all codes beyond US-ASCII, i.e. all >127 + */ + +/* uses '&' not '+' to start a base64 sequence */ +#define AMPERSAND 0x26 +#define COMMA 0x2c +#define SLASH 0x2f + +/* legal byte values: all US-ASCII graphic characters 0x20..0x7e */ +#define isLegalIMAP(c) (0x20<=(c) && (c)<=0x7e) + +/* direct-encode all of printable ASCII 0x20..0x7e except '&' 0x26 */ +#define inSetDIMAP(c) (isLegalIMAP(c) && c!=AMPERSAND) + +#define TO_BASE64_IMAP(n) ((n)<63 ? toBase64[n] : COMMA) +#define FROM_BASE64_IMAP(c) ((c)==COMMA ? 63 : (c)==SLASH ? -1 : fromBase64[c]) + +/* + * converter status values: + * + * toUnicodeStatus: + * 24 inDirectMode (boolean) + * 23..16 base64Counter (-1..7) + * 15..0 bits (up to 14 bits incoming base64) + * + * fromUnicodeStatus: + * 24 inDirectMode (boolean) + * 23..16 base64Counter (0..2) + * 7..0 bits (6 bits outgoing base64) + * + * ignore bits 31..25 + */ + +U_CDECL_BEGIN +static void U_CALLCONV +_IMAPToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const uint8_t *source, *sourceLimit; + char16_t *target; + const char16_t *targetLimit; + int32_t *offsets; + + uint8_t *bytes; + uint8_t byteIndex; + + int32_t length, targetCapacity; + + /* UTF-7 state */ + uint16_t bits; + int8_t base64Counter; + UBool inDirectMode; + + int8_t base64Value; + + int32_t sourceIndex, nextSourceIndex; + + char16_t c; + uint8_t b; + + /* set up the local pointers */ + cnv=pArgs->converter; + + source=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + target=pArgs->target; + targetLimit=pArgs->targetLimit; + offsets=pArgs->offsets; + /* get the state machine state */ + { + uint32_t status=cnv->toUnicodeStatus; + inDirectMode=(UBool)((status>>24)&1); + base64Counter=(int8_t)(status>>16); + bits=(uint16_t)status; + } + bytes=cnv->toUBytes; + byteIndex=cnv->toULength; + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex=byteIndex==0 ? 0 : -1; + nextSourceIndex=0; + + if(inDirectMode) { +directMode: + /* + * In Direct Mode, US-ASCII characters are encoded directly, i.e., + * with their US-ASCII byte values. + * An ampersand starts Unicode (or "escape") Mode. + * + * In Direct Mode, only the sourceIndex is used. + */ + byteIndex=0; + length=(int32_t)(sourceLimit-source); + targetCapacity=(int32_t)(targetLimit-target); + if(length>targetCapacity) { + length=targetCapacity; + } + while(length>0) { + b=*source++; + if(!isLegalIMAP(b)) { + /* illegal */ + bytes[0]=b; + byteIndex=1; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } else if(b!=AMPERSAND) { + /* write directly encoded character */ + *target++=b; + if(offsets!=nullptr) { + *offsets++=sourceIndex++; + } + } else /* AMPERSAND */ { + /* switch to Unicode mode */ + nextSourceIndex=++sourceIndex; + inDirectMode=false; + byteIndex=0; + bits=0; + base64Counter=-1; + goto unicodeMode; + } + --length; + } + if(source=targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + } else { +unicodeMode: + /* + * In Unicode (or "escape") Mode, UTF-16BE is base64-encoded. + * The base64 sequence ends with any character that is not in the base64 alphabet. + * A terminating minus sign is consumed. + * US-ASCII must not be base64-ed. + * + * In Unicode Mode, the sourceIndex has the index to the start of the current + * base64 bytes, while nextSourceIndex is precisely parallel to source, + * keeping the index to the following byte. + * Note that in 2 out of 3 cases, UChars overlap within a base64 byte. + */ + while(source0x7e) { + /* illegal - test other illegal US-ASCII values by base64Value==-3 */ + inDirectMode=true; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } else if((base64Value=FROM_BASE64_IMAP(b))>=0) { + /* collect base64 bytes into UChars */ + switch(base64Counter) { + case -1: /* -1 is immediately after the & */ + case 0: + bits=base64Value; + base64Counter=1; + break; + case 1: + case 3: + case 4: + case 6: + bits=(uint16_t)((bits<<6)|base64Value); + ++base64Counter; + break; + case 2: + c=(char16_t)((bits<<4)|(base64Value>>2)); + if(isLegalIMAP(c)) { + /* illegal */ + inDirectMode=true; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + goto endloop; + } + *target++=c; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + sourceIndex=nextSourceIndex-1; + } + bytes[0]=b; /* keep this byte in case an error occurs */ + byteIndex=1; + bits=(uint16_t)(base64Value&3); + base64Counter=3; + break; + case 5: + c=(char16_t)((bits<<2)|(base64Value>>4)); + if(isLegalIMAP(c)) { + /* illegal */ + inDirectMode=true; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + goto endloop; + } + *target++=c; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + sourceIndex=nextSourceIndex-1; + } + bytes[0]=b; /* keep this byte in case an error occurs */ + byteIndex=1; + bits=(uint16_t)(base64Value&15); + base64Counter=6; + break; + case 7: + c=(char16_t)((bits<<6)|base64Value); + if(isLegalIMAP(c)) { + /* illegal */ + inDirectMode=true; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + goto endloop; + } + *target++=c; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + sourceIndex=nextSourceIndex; + } + byteIndex=0; + bits=0; + base64Counter=0; + break; + default: + /* will never occur */ + break; + } + } else if(base64Value==-2) { + /* minus sign terminates the base64 sequence */ + inDirectMode=true; + if(base64Counter==-1) { + /* &- i.e. a minus immediately following an ampersand */ + *target++=AMPERSAND; + if(offsets!=nullptr) { + *offsets++=sourceIndex-1; + } + } else { + /* absorb the minus and leave the Unicode Mode */ + if(bits!=0 || (base64Counter!=0 && base64Counter!=3 && base64Counter!=6)) { + /* bits are illegally left over, a char16_t is incomplete */ + /* base64Counter other than 0, 3, 6 means non-minimal zero-padding, also illegal */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } + } + sourceIndex=nextSourceIndex; + goto directMode; + } else { + if(base64Counter==-1) { + /* illegal: & immediately followed by something other than base64 or minus sign */ + /* include the ampersand in the reported sequence */ + --sourceIndex; + bytes[0]=AMPERSAND; + bytes[1]=b; + byteIndex=2; + } + /* base64Value==-1 for characters that are illegal only in Unicode mode */ + /* base64Value==-3 for illegal characters */ + /* illegal */ + inDirectMode=true; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } + } else { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + } +endloop: + + /* + * the end of the input stream and detection of truncated input + * are handled by the framework, but here we must check if we are in Unicode + * mode and byteIndex==0 because we must end in direct mode + * + * conditions: + * successful + * in Unicode mode and byteIndex==0 + * end of input and no truncated input + */ + if( U_SUCCESS(*pErrorCode) && + !inDirectMode && byteIndex==0 && + pArgs->flush && source>=sourceLimit + ) { + if(base64Counter==-1) { + /* & at the very end of the input */ + /* make the ampersand the reported sequence */ + bytes[0]=AMPERSAND; + byteIndex=1; + } + /* else if(base64Counter!=-1) byteIndex remains 0 because there is no particular byte sequence */ + + inDirectMode=true; /* avoid looping */ + *pErrorCode=U_TRUNCATED_CHAR_FOUND; + } + + /* set the converter state back into UConverter */ + cnv->toUnicodeStatus=((uint32_t)inDirectMode<<24)|((uint32_t)((uint8_t)base64Counter)<<16)|(uint32_t)bits; + cnv->toULength=byteIndex; + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + pArgs->offsets=offsets; + return; +} + +static void U_CALLCONV +_IMAPFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const char16_t *source, *sourceLimit; + uint8_t *target, *targetLimit; + int32_t *offsets; + + int32_t length, targetCapacity, sourceIndex; + char16_t c; + uint8_t b; + + /* UTF-7 state */ + uint8_t bits; + int8_t base64Counter; + UBool inDirectMode; + + /* set up the local pointers */ + cnv=pArgs->converter; + + /* set up the local pointers */ + source=pArgs->source; + sourceLimit=pArgs->sourceLimit; + target=(uint8_t *)pArgs->target; + targetLimit=(uint8_t *)pArgs->targetLimit; + offsets=pArgs->offsets; + + /* get the state machine state */ + { + uint32_t status=cnv->fromUnicodeStatus; + inDirectMode=(UBool)((status>>24)&1); + base64Counter=(int8_t)(status>>16); + bits=(uint8_t)status; + } + + /* UTF-7 always encodes UTF-16 code units, therefore we need only a simple sourceIndex */ + sourceIndex=0; + + if(inDirectMode) { +directMode: + length=(int32_t)(sourceLimit-source); + targetCapacity=(int32_t)(targetLimit-target); + if(length>targetCapacity) { + length=targetCapacity; + } + while(length>0) { + c=*source++; + /* encode 0x20..0x7e except '&' directly */ + if(inSetDIMAP(c)) { + /* encode directly */ + *target++=(uint8_t)c; + if(offsets!=nullptr) { + *offsets++=sourceIndex++; + } + } else if(c==AMPERSAND) { + /* output &- for & */ + *target++=AMPERSAND; + if(targetcharErrorBuffer[0]=MINUS; + cnv->charErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } else { + /* un-read this character and switch to Unicode Mode */ + --source; + *target++=AMPERSAND; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + inDirectMode=false; + base64Counter=0; + goto unicodeMode; + } + --length; + } + if(source=targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + } else { +unicodeMode: + while(sourcecharErrorBuffer[0]=MINUS; + cnv->charErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + goto directMode; + } else { + /* + * base64 this character: + * Output 2 or 3 base64 bytes for the remaining bits of the previous character + * and the bits of this character, each implicitly in UTF-16BE. + * + * Here, bits is an 8-bit variable because only 6 bits need to be kept from one + * character to the next. The actual 2 or 4 bits are shifted to the left edge + * of the 6-bits field 5..0 to make the termination of the base64 sequence easier. + */ + switch(base64Counter) { + case 0: + b=(uint8_t)(c>>10); + *target++=TO_BASE64_IMAP(b); + if(target>4)&0x3f); + *target++=TO_BASE64_IMAP(b); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + *offsets++=sourceIndex++; + } + } else { + if(offsets!=nullptr) { + *offsets++=sourceIndex++; + } + b=(uint8_t)((c>>4)&0x3f); + cnv->charErrorBuffer[0]=TO_BASE64_IMAP(b); + cnv->charErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + bits=(uint8_t)((c&15)<<2); + base64Counter=1; + break; + case 1: + b=(uint8_t)(bits|(c>>14)); + *target++=TO_BASE64_IMAP(b); + if(target>8)&0x3f); + *target++=TO_BASE64_IMAP(b); + if(target>2)&0x3f); + *target++=TO_BASE64_IMAP(b); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + *offsets++=sourceIndex; + *offsets++=sourceIndex++; + } + } else { + if(offsets!=nullptr) { + *offsets++=sourceIndex; + *offsets++=sourceIndex++; + } + b=(uint8_t)((c>>2)&0x3f); + cnv->charErrorBuffer[0]=TO_BASE64_IMAP(b); + cnv->charErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + } else { + if(offsets!=nullptr) { + *offsets++=sourceIndex++; + } + b=(uint8_t)((c>>8)&0x3f); + cnv->charErrorBuffer[0]=TO_BASE64_IMAP(b); + b=(uint8_t)((c>>2)&0x3f); + cnv->charErrorBuffer[1]=TO_BASE64_IMAP(b); + cnv->charErrorBufferLength=2; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + bits=(uint8_t)((c&3)<<4); + base64Counter=2; + break; + case 2: + b=(uint8_t)(bits|(c>>12)); + *target++=TO_BASE64_IMAP(b); + if(target>6)&0x3f); + *target++=TO_BASE64_IMAP(b); + if(targetcharErrorBuffer[0]=TO_BASE64_IMAP(b); + cnv->charErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + } else { + if(offsets!=nullptr) { + *offsets++=sourceIndex++; + } + b=(uint8_t)((c>>6)&0x3f); + cnv->charErrorBuffer[0]=TO_BASE64_IMAP(b); + b=(uint8_t)(c&0x3f); + cnv->charErrorBuffer[1]=TO_BASE64_IMAP(b); + cnv->charErrorBufferLength=2; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + bits=0; + base64Counter=0; + break; + default: + /* will never occur */ + break; + } + } + } else { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + } + + if(pArgs->flush && source>=sourceLimit) { + /* flush remaining bits to the target */ + if(!inDirectMode) { + if(base64Counter!=0) { + if(targetcharErrorBuffer[cnv->charErrorBufferLength++]=TO_BASE64_IMAP(bits); + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + } + /* need to terminate with a minus */ + if(targetcharErrorBuffer[cnv->charErrorBufferLength++]=MINUS; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + } + /* reset the state for the next conversion */ + cnv->fromUnicodeStatus=(cnv->fromUnicodeStatus&0xf0000000)|0x1000000; /* keep version, inDirectMode=true */ + } else { + /* set the converter state back into UConverter */ + cnv->fromUnicodeStatus= + (cnv->fromUnicodeStatus&0xf0000000)| /* keep version*/ + ((uint32_t)inDirectMode<<24)|((uint32_t)base64Counter<<16)|(uint32_t)bits; + } + + /* write back the updated pointers */ + pArgs->source=source; + pArgs->target=(char *)target; + pArgs->offsets=offsets; + return; +} +U_CDECL_END + +static const UConverterImpl _IMAPImpl={ + UCNV_IMAP_MAILBOX, + + nullptr, + nullptr, + + _UTF7Open, + nullptr, + _UTF7Reset, + + _IMAPToUnicodeWithOffsets, + _IMAPToUnicodeWithOffsets, + _IMAPFromUnicodeWithOffsets, + _IMAPFromUnicodeWithOffsets, + nullptr, + + nullptr, + nullptr, + nullptr, /* we don't need writeSub() because we never call a callback at fromUnicode() */ + nullptr, + ucnv_getCompleteUnicodeSet, + nullptr, + nullptr +}; + +static const UConverterStaticData _IMAPStaticData={ + sizeof(UConverterStaticData), + "IMAP-mailbox-name", + 0, /* TODO CCSID for IMAP-mailbox-name */ + UCNV_IBM, UCNV_IMAP_MAILBOX, + 1, 4, + { 0x3f, 0, 0, 0 }, 1, /* the subchar is not used */ + false, false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + +const UConverterSharedData _IMAPData= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_IMAPStaticData, &_IMAPImpl); + +#endif diff --git a/deps/icu-small/source/common/ucnv_u8.cpp b/deps/icu-small/source/common/ucnv_u8.cpp index 3c27f2e46e8bba..b54df079ddabdd 100644 --- a/deps/icu-small/source/common/ucnv_u8.cpp +++ b/deps/icu-small/source/common/ucnv_u8.cpp @@ -1,944 +1,944 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2002-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: ucnv_u8.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002jul01 -* created by: Markus W. Scherer -* -* UTF-8 converter implementation. Used to be in ucnv_utf.c. -* -* Also, CESU-8 implementation, see UTR 26. -* The CESU-8 converter uses all the same functions as the -* UTF-8 converter, with a branch for converting supplementary code points. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/utf.h" -#include "unicode/utf8.h" -#include "unicode/utf16.h" -#include "uassert.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" -#include "cmemory.h" -#include "ustr_imp.h" - -/* Prototypes --------------------------------------------------------------- */ - -/* Keep these here to make finicky compilers happy */ - -U_CFUNC void ucnv_fromUnicode_UTF8(UConverterFromUnicodeArgs *args, - UErrorCode *err); -U_CFUNC void ucnv_fromUnicode_UTF8_OFFSETS_LOGIC(UConverterFromUnicodeArgs *args, - UErrorCode *err); - - -/* UTF-8 -------------------------------------------------------------------- */ - -#define MAXIMUM_UCS2 0x0000FFFF - -static const uint32_t offsetsFromUTF8[5] = {0, - (uint32_t) 0x00000000, (uint32_t) 0x00003080, (uint32_t) 0x000E2080, - (uint32_t) 0x03C82080 -}; - -static UBool hasCESU8Data(const UConverter *cnv) -{ -#if UCONFIG_ONLY_HTML_CONVERSION - return false; -#else - return (UBool)(cnv->sharedData == &_CESU8Data); -#endif -} -U_CDECL_BEGIN -static void U_CALLCONV ucnv_toUnicode_UTF8 (UConverterToUnicodeArgs * args, - UErrorCode * err) -{ - UConverter *cnv = args->converter; - const unsigned char *mySource = (unsigned char *) args->source; - UChar *myTarget = args->target; - const unsigned char *sourceLimit = (unsigned char *) args->sourceLimit; - const UChar *targetLimit = args->targetLimit; - unsigned char *toUBytes = cnv->toUBytes; - UBool isCESU8 = hasCESU8Data(cnv); - uint32_t ch, ch2 = 0; - int32_t i, inBytes; - - /* Restore size of current sequence */ - if (cnv->toULength > 0 && myTarget < targetLimit) - { - inBytes = cnv->mode; /* restore # of bytes to consume */ - i = cnv->toULength; /* restore # of bytes consumed */ - cnv->toULength = 0; - - ch = cnv->toUnicodeStatus;/*Stores the previously calculated ch from a previous call*/ - cnv->toUnicodeStatus = 0; - goto morebytes; - } - - - while (mySource < sourceLimit && myTarget < targetLimit) - { - ch = *(mySource++); - if (U8_IS_SINGLE(ch)) /* Simple case */ - { - *(myTarget++) = (UChar) ch; - } - else - { - /* store the first char */ - toUBytes[0] = (char)ch; - inBytes = U8_COUNT_BYTES_NON_ASCII(ch); /* lookup current sequence length */ - i = 1; - -morebytes: - while (i < inBytes) - { - if (mySource < sourceLimit) - { - toUBytes[i] = (char) (ch2 = *mySource); - if (!icu::UTF8::isValidTrail(ch, static_cast(ch2), i, inBytes) && - !(isCESU8 && i == 1 && ch == 0xed && U8_IS_TRAIL(ch2))) - { - break; /* i < inBytes */ - } - ch = (ch << 6) + ch2; - ++mySource; - i++; - } - else - { - /* stores a partially calculated target*/ - cnv->toUnicodeStatus = ch; - cnv->mode = inBytes; - cnv->toULength = (int8_t) i; - goto donefornow; - } - } - - // In CESU-8, only surrogates, not supplementary code points, are encoded directly. - if (i == inBytes && (!isCESU8 || i <= 3)) - { - /* Remove the accumulated high bits */ - ch -= offsetsFromUTF8[inBytes]; - - /* Normal valid byte when the loop has not prematurely terminated (i < inBytes) */ - if (ch <= MAXIMUM_UCS2) - { - /* fits in 16 bits */ - *(myTarget++) = (UChar) ch; - } - else - { - /* write out the surrogates */ - *(myTarget++) = U16_LEAD(ch); - ch = U16_TRAIL(ch); - if (myTarget < targetLimit) - { - *(myTarget++) = (UChar)ch; - } - else - { - /* Put in overflow buffer (not handled here) */ - cnv->UCharErrorBuffer[0] = (UChar) ch; - cnv->UCharErrorBufferLength = 1; - *err = U_BUFFER_OVERFLOW_ERROR; - break; - } - } - } - else - { - cnv->toULength = (int8_t)i; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - } - -donefornow: - if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) - { - /* End of target buffer */ - *err = U_BUFFER_OVERFLOW_ERROR; - } - - args->target = myTarget; - args->source = (const char *) mySource; -} - -static void U_CALLCONV ucnv_toUnicode_UTF8_OFFSETS_LOGIC (UConverterToUnicodeArgs * args, - UErrorCode * err) -{ - UConverter *cnv = args->converter; - const unsigned char *mySource = (unsigned char *) args->source; - UChar *myTarget = args->target; - int32_t *myOffsets = args->offsets; - int32_t offsetNum = 0; - const unsigned char *sourceLimit = (unsigned char *) args->sourceLimit; - const UChar *targetLimit = args->targetLimit; - unsigned char *toUBytes = cnv->toUBytes; - UBool isCESU8 = hasCESU8Data(cnv); - uint32_t ch, ch2 = 0; - int32_t i, inBytes; - - /* Restore size of current sequence */ - if (cnv->toULength > 0 && myTarget < targetLimit) - { - inBytes = cnv->mode; /* restore # of bytes to consume */ - i = cnv->toULength; /* restore # of bytes consumed */ - cnv->toULength = 0; - - ch = cnv->toUnicodeStatus;/*Stores the previously calculated ch from a previous call*/ - cnv->toUnicodeStatus = 0; - goto morebytes; - } - - while (mySource < sourceLimit && myTarget < targetLimit) - { - ch = *(mySource++); - if (U8_IS_SINGLE(ch)) /* Simple case */ - { - *(myTarget++) = (UChar) ch; - *(myOffsets++) = offsetNum++; - } - else - { - toUBytes[0] = (char)ch; - inBytes = U8_COUNT_BYTES_NON_ASCII(ch); - i = 1; - -morebytes: - while (i < inBytes) - { - if (mySource < sourceLimit) - { - toUBytes[i] = (char) (ch2 = *mySource); - if (!icu::UTF8::isValidTrail(ch, static_cast(ch2), i, inBytes) && - !(isCESU8 && i == 1 && ch == 0xed && U8_IS_TRAIL(ch2))) - { - break; /* i < inBytes */ - } - ch = (ch << 6) + ch2; - ++mySource; - i++; - } - else - { - cnv->toUnicodeStatus = ch; - cnv->mode = inBytes; - cnv->toULength = (int8_t)i; - goto donefornow; - } - } - - // In CESU-8, only surrogates, not supplementary code points, are encoded directly. - if (i == inBytes && (!isCESU8 || i <= 3)) - { - /* Remove the accumulated high bits */ - ch -= offsetsFromUTF8[inBytes]; - - /* Normal valid byte when the loop has not prematurely terminated (i < inBytes) */ - if (ch <= MAXIMUM_UCS2) - { - /* fits in 16 bits */ - *(myTarget++) = (UChar) ch; - *(myOffsets++) = offsetNum; - } - else - { - /* write out the surrogates */ - *(myTarget++) = U16_LEAD(ch); - *(myOffsets++) = offsetNum; - ch = U16_TRAIL(ch); - if (myTarget < targetLimit) - { - *(myTarget++) = (UChar)ch; - *(myOffsets++) = offsetNum; - } - else - { - cnv->UCharErrorBuffer[0] = (UChar) ch; - cnv->UCharErrorBufferLength = 1; - *err = U_BUFFER_OVERFLOW_ERROR; - } - } - offsetNum += i; - } - else - { - cnv->toULength = (int8_t)i; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - } - -donefornow: - if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) - { /* End of target buffer */ - *err = U_BUFFER_OVERFLOW_ERROR; - } - - args->target = myTarget; - args->source = (const char *) mySource; - args->offsets = myOffsets; -} -U_CDECL_END - -U_CFUNC void U_CALLCONV ucnv_fromUnicode_UTF8 (UConverterFromUnicodeArgs * args, - UErrorCode * err) -{ - UConverter *cnv = args->converter; - const UChar *mySource = args->source; - const UChar *sourceLimit = args->sourceLimit; - uint8_t *myTarget = (uint8_t *) args->target; - const uint8_t *targetLimit = (uint8_t *) args->targetLimit; - uint8_t *tempPtr; - UChar32 ch; - uint8_t tempBuf[4]; - int32_t indexToWrite; - UBool isNotCESU8 = !hasCESU8Data(cnv); - - if (cnv->fromUChar32 && myTarget < targetLimit) - { - ch = cnv->fromUChar32; - cnv->fromUChar32 = 0; - goto lowsurrogate; - } - - while (mySource < sourceLimit && myTarget < targetLimit) - { - ch = *(mySource++); - - if (ch < 0x80) /* Single byte */ - { - *(myTarget++) = (uint8_t) ch; - } - else if (ch < 0x800) /* Double byte */ - { - *(myTarget++) = (uint8_t) ((ch >> 6) | 0xc0); - if (myTarget < targetLimit) - { - *(myTarget++) = (uint8_t) ((ch & 0x3f) | 0x80); - } - else - { - cnv->charErrorBuffer[0] = (uint8_t) ((ch & 0x3f) | 0x80); - cnv->charErrorBufferLength = 1; - *err = U_BUFFER_OVERFLOW_ERROR; - } - } - else { - /* Check for surrogates */ - if(U16_IS_SURROGATE(ch) && isNotCESU8) { -lowsurrogate: - if (mySource < sourceLimit) { - /* test both code units */ - if(U16_IS_SURROGATE_LEAD(ch) && U16_IS_TRAIL(*mySource)) { - /* convert and consume this supplementary code point */ - ch=U16_GET_SUPPLEMENTARY(ch, *mySource); - ++mySource; - /* exit this condition tree */ - } - else { - /* this is an unpaired trail or lead code unit */ - /* callback(illegal) */ - cnv->fromUChar32 = ch; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - else { - /* no more input */ - cnv->fromUChar32 = ch; - break; - } - } - - /* Do we write the buffer directly for speed, - or do we have to be careful about target buffer space? */ - tempPtr = (((targetLimit - myTarget) >= 4) ? myTarget : tempBuf); - - if (ch <= MAXIMUM_UCS2) { - indexToWrite = 2; - tempPtr[0] = (uint8_t) ((ch >> 12) | 0xe0); - } - else { - indexToWrite = 3; - tempPtr[0] = (uint8_t) ((ch >> 18) | 0xf0); - tempPtr[1] = (uint8_t) (((ch >> 12) & 0x3f) | 0x80); - } - tempPtr[indexToWrite-1] = (uint8_t) (((ch >> 6) & 0x3f) | 0x80); - tempPtr[indexToWrite] = (uint8_t) ((ch & 0x3f) | 0x80); - - if (tempPtr == myTarget) { - /* There was enough space to write the codepoint directly. */ - myTarget += (indexToWrite + 1); - } - else { - /* We might run out of room soon. Write it slowly. */ - for (; tempPtr <= (tempBuf + indexToWrite); tempPtr++) { - if (myTarget < targetLimit) { - *(myTarget++) = *tempPtr; - } - else { - cnv->charErrorBuffer[cnv->charErrorBufferLength++] = *tempPtr; - *err = U_BUFFER_OVERFLOW_ERROR; - } - } - } - } - } - - if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) - { - *err = U_BUFFER_OVERFLOW_ERROR; - } - - args->target = (char *) myTarget; - args->source = mySource; -} - -U_CFUNC void U_CALLCONV ucnv_fromUnicode_UTF8_OFFSETS_LOGIC (UConverterFromUnicodeArgs * args, - UErrorCode * err) -{ - UConverter *cnv = args->converter; - const UChar *mySource = args->source; - int32_t *myOffsets = args->offsets; - const UChar *sourceLimit = args->sourceLimit; - uint8_t *myTarget = (uint8_t *) args->target; - const uint8_t *targetLimit = (uint8_t *) args->targetLimit; - uint8_t *tempPtr; - UChar32 ch; - int32_t offsetNum, nextSourceIndex; - int32_t indexToWrite; - uint8_t tempBuf[4]; - UBool isNotCESU8 = !hasCESU8Data(cnv); - - if (cnv->fromUChar32 && myTarget < targetLimit) - { - ch = cnv->fromUChar32; - cnv->fromUChar32 = 0; - offsetNum = -1; - nextSourceIndex = 0; - goto lowsurrogate; - } else { - offsetNum = 0; - } - - while (mySource < sourceLimit && myTarget < targetLimit) - { - ch = *(mySource++); - - if (ch < 0x80) /* Single byte */ - { - *(myOffsets++) = offsetNum++; - *(myTarget++) = (char) ch; - } - else if (ch < 0x800) /* Double byte */ - { - *(myOffsets++) = offsetNum; - *(myTarget++) = (uint8_t) ((ch >> 6) | 0xc0); - if (myTarget < targetLimit) - { - *(myOffsets++) = offsetNum++; - *(myTarget++) = (uint8_t) ((ch & 0x3f) | 0x80); - } - else - { - cnv->charErrorBuffer[0] = (uint8_t) ((ch & 0x3f) | 0x80); - cnv->charErrorBufferLength = 1; - *err = U_BUFFER_OVERFLOW_ERROR; - } - } - else - /* Check for surrogates */ - { - nextSourceIndex = offsetNum + 1; - - if(U16_IS_SURROGATE(ch) && isNotCESU8) { -lowsurrogate: - if (mySource < sourceLimit) { - /* test both code units */ - if(U16_IS_SURROGATE_LEAD(ch) && U16_IS_TRAIL(*mySource)) { - /* convert and consume this supplementary code point */ - ch=U16_GET_SUPPLEMENTARY(ch, *mySource); - ++mySource; - ++nextSourceIndex; - /* exit this condition tree */ - } - else { - /* this is an unpaired trail or lead code unit */ - /* callback(illegal) */ - cnv->fromUChar32 = ch; - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - else { - /* no more input */ - cnv->fromUChar32 = ch; - break; - } - } - - /* Do we write the buffer directly for speed, - or do we have to be careful about target buffer space? */ - tempPtr = (((targetLimit - myTarget) >= 4) ? myTarget : tempBuf); - - if (ch <= MAXIMUM_UCS2) { - indexToWrite = 2; - tempPtr[0] = (uint8_t) ((ch >> 12) | 0xe0); - } - else { - indexToWrite = 3; - tempPtr[0] = (uint8_t) ((ch >> 18) | 0xf0); - tempPtr[1] = (uint8_t) (((ch >> 12) & 0x3f) | 0x80); - } - tempPtr[indexToWrite-1] = (uint8_t) (((ch >> 6) & 0x3f) | 0x80); - tempPtr[indexToWrite] = (uint8_t) ((ch & 0x3f) | 0x80); - - if (tempPtr == myTarget) { - /* There was enough space to write the codepoint directly. */ - myTarget += (indexToWrite + 1); - myOffsets[0] = offsetNum; - myOffsets[1] = offsetNum; - myOffsets[2] = offsetNum; - if (indexToWrite >= 3) { - myOffsets[3] = offsetNum; - } - myOffsets += (indexToWrite + 1); - } - else { - /* We might run out of room soon. Write it slowly. */ - for (; tempPtr <= (tempBuf + indexToWrite); tempPtr++) { - if (myTarget < targetLimit) - { - *(myOffsets++) = offsetNum; - *(myTarget++) = *tempPtr; - } - else - { - cnv->charErrorBuffer[cnv->charErrorBufferLength++] = *tempPtr; - *err = U_BUFFER_OVERFLOW_ERROR; - } - } - } - offsetNum = nextSourceIndex; - } - } - - if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) - { - *err = U_BUFFER_OVERFLOW_ERROR; - } - - args->target = (char *) myTarget; - args->source = mySource; - args->offsets = myOffsets; -} - -U_CDECL_BEGIN -static UChar32 U_CALLCONV ucnv_getNextUChar_UTF8(UConverterToUnicodeArgs *args, - UErrorCode *err) { - UConverter *cnv; - const uint8_t *sourceInitial; - const uint8_t *source; - uint8_t myByte; - UChar32 ch; - int8_t i; - - /* UTF-8 only here, the framework handles CESU-8 to combine surrogate pairs */ - - cnv = args->converter; - sourceInitial = source = (const uint8_t *)args->source; - if (source >= (const uint8_t *)args->sourceLimit) - { - /* no input */ - *err = U_INDEX_OUTOFBOUNDS_ERROR; - return 0xffff; - } - - myByte = (uint8_t)*(source++); - if (U8_IS_SINGLE(myByte)) - { - args->source = (const char *)source; - return (UChar32)myByte; - } - - uint16_t countTrailBytes = U8_COUNT_TRAIL_BYTES(myByte); - if (countTrailBytes == 0) { - cnv->toUBytes[0] = myByte; - cnv->toULength = 1; - *err = U_ILLEGAL_CHAR_FOUND; - args->source = (const char *)source; - return 0xffff; - } - - /*The byte sequence is longer than the buffer area passed*/ - if (((const char *)source + countTrailBytes) > args->sourceLimit) - { - /* check if all of the remaining bytes are trail bytes */ - uint16_t extraBytesToWrite = countTrailBytes + 1; - cnv->toUBytes[0] = myByte; - i = 1; - *err = U_TRUNCATED_CHAR_FOUND; - while(source < (const uint8_t *)args->sourceLimit) { - uint8_t b = *source; - if(icu::UTF8::isValidTrail(myByte, b, i, extraBytesToWrite)) { - cnv->toUBytes[i++] = b; - ++source; - } else { - /* error even before we run out of input */ - *err = U_ILLEGAL_CHAR_FOUND; - break; - } - } - cnv->toULength = i; - args->source = (const char *)source; - return 0xffff; - } - - ch = myByte << 6; - if(countTrailBytes == 2) { - uint8_t t1 = *source, t2; - if(U8_IS_VALID_LEAD3_AND_T1(myByte, t1) && U8_IS_TRAIL(t2 = *++source)) { - args->source = (const char *)(source + 1); - return (((ch + t1) << 6) + t2) - offsetsFromUTF8[3]; - } - } else if(countTrailBytes == 1) { - uint8_t t1 = *source; - if(U8_IS_TRAIL(t1)) { - args->source = (const char *)(source + 1); - return (ch + t1) - offsetsFromUTF8[2]; - } - } else { // countTrailBytes == 3 - uint8_t t1 = *source, t2, t3; - if(U8_IS_VALID_LEAD4_AND_T1(myByte, t1) && U8_IS_TRAIL(t2 = *++source) && - U8_IS_TRAIL(t3 = *++source)) { - args->source = (const char *)(source + 1); - return (((((ch + t1) << 6) + t2) << 6) + t3) - offsetsFromUTF8[4]; - } - } - args->source = (const char *)source; - - for(i = 0; sourceInitial < source; ++i) { - cnv->toUBytes[i] = *sourceInitial++; - } - cnv->toULength = i; - *err = U_ILLEGAL_CHAR_FOUND; - return 0xffff; -} -U_CDECL_END - -/* UTF-8-from-UTF-8 conversion functions ------------------------------------ */ - -U_CDECL_BEGIN -/* "Convert" UTF-8 to UTF-8: Validate and copy. Modified from ucnv_DBCSFromUTF8(). */ -static void U_CALLCONV -ucnv_UTF8FromUTF8(UConverterFromUnicodeArgs *pFromUArgs, - UConverterToUnicodeArgs *pToUArgs, - UErrorCode *pErrorCode) { - UConverter *utf8; - const uint8_t *source, *sourceLimit; - uint8_t *target; - int32_t targetCapacity; - int32_t count; - - int8_t oldToULength, toULength, toULimit; - - UChar32 c; - uint8_t b, t1, t2; - - /* set up the local pointers */ - utf8=pToUArgs->converter; - source=(uint8_t *)pToUArgs->source; - sourceLimit=(uint8_t *)pToUArgs->sourceLimit; - target=(uint8_t *)pFromUArgs->target; - targetCapacity=(int32_t)(pFromUArgs->targetLimit-pFromUArgs->target); - - /* get the converter state from the UTF-8 UConverter */ - if(utf8->toULength > 0) { - toULength=oldToULength=utf8->toULength; - toULimit=(int8_t)utf8->mode; - c=(UChar32)utf8->toUnicodeStatus; - } else { - toULength=oldToULength=toULimit=0; - c = 0; - } - - count=(int32_t)(sourceLimit-source)+oldToULength; - if(counttargetCapacity) { - count=targetCapacity; - } - - // The conversion loop checks count>0 only once per character. - // If the buffer ends with a truncated sequence, - // then we reduce the count to stop before that, - // and collect the remaining bytes after the conversion loop. - - // Do not go back into the bytes that will be read for finishing a partial - // sequence from the previous buffer. - int32_t length=count-toULength; - U8_TRUNCATE_IF_INCOMPLETE(source, 0, length); - count=toULength+length; - } - - if(c!=0) { - utf8->toUnicodeStatus=0; - utf8->toULength=0; - goto moreBytes; - /* See note in ucnv_SBCSFromUTF8() about this goto. */ - } - - /* conversion loop */ - while(count>0) { - b=*source++; - if(U8_IS_SINGLE(b)) { - /* convert ASCII */ - *target++=b; - --count; - continue; - } else { - if(b>=0xe0) { - if( /* handle U+0800..U+FFFF inline */ - b<0xf0 && - U8_IS_VALID_LEAD3_AND_T1(b, t1=source[0]) && - U8_IS_TRAIL(t2=source[1]) - ) { - source+=2; - *target++=b; - *target++=t1; - *target++=t2; - count-=3; - continue; - } - } else { - if( /* handle U+0080..U+07FF inline */ - b>=0xc2 && - U8_IS_TRAIL(t1=*source) - ) { - ++source; - *target++=b; - *target++=t1; - count-=2; - continue; - } - } - - /* handle "complicated" and error cases, and continuing partial characters */ - oldToULength=0; - toULength=1; - toULimit=U8_COUNT_BYTES_NON_ASCII(b); - c=b; -moreBytes: - while(toULengthtoUBytes[oldToULength++]=*source++; - } - utf8->toUnicodeStatus=c; - utf8->toULength=toULength; - utf8->mode=toULimit; - pToUArgs->source=(char *)source; - pFromUArgs->target=(char *)target; - return; - } - } - - if(toULength!=toULimit) { - /* error handling: illegal UTF-8 byte sequence */ - source-=(toULength-oldToULength); - while(oldToULengthtoUBytes[oldToULength++]=*source++; - } - utf8->toULength=toULength; - pToUArgs->source=(char *)source; - pFromUArgs->target=(char *)target; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - return; - } - - /* copy the legal byte sequence to the target */ - { - int8_t i; - - for(i=0; itoUBytes[i]; - } - source-=(toULength-oldToULength); - for(; i=0); - - if(U_SUCCESS(*pErrorCode) && sourcetargetLimit) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } else { - b=*source; - toULimit=U8_COUNT_BYTES(b); - if(toULimit>(sourceLimit-source)) { - /* collect a truncated byte sequence */ - toULength=0; - c=b; - for(;;) { - utf8->toUBytes[toULength++]=b; - if(++source==sourceLimit) { - /* partial byte sequence at end of source */ - utf8->toUnicodeStatus=c; - utf8->toULength=toULength; - utf8->mode=toULimit; - break; - } else if(!icu::UTF8::isValidTrail(c, b=*source, toULength, toULimit)) { - utf8->toULength=toULength; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } - c=(c<<6)+b; - } - } else { - /* partial-sequence target overflow: fall back to the pivoting implementation */ - *pErrorCode=U_USING_DEFAULT_WARNING; - } - } - } - - /* write back the updated pointers */ - pToUArgs->source=(char *)source; - pFromUArgs->target=(char *)target; -} - -U_CDECL_END - -/* UTF-8 converter data ----------------------------------------------------- */ - -static const UConverterImpl _UTF8Impl={ - UCNV_UTF8, - - NULL, - NULL, - - NULL, - NULL, - NULL, - - ucnv_toUnicode_UTF8, - ucnv_toUnicode_UTF8_OFFSETS_LOGIC, - ucnv_fromUnicode_UTF8, - ucnv_fromUnicode_UTF8_OFFSETS_LOGIC, - ucnv_getNextUChar_UTF8, - - NULL, - NULL, - NULL, - NULL, - ucnv_getNonSurrogateUnicodeSet, - - ucnv_UTF8FromUTF8, - ucnv_UTF8FromUTF8 -}; - -/* The 1208 CCSID refers to any version of Unicode of UTF-8 */ -static const UConverterStaticData _UTF8StaticData={ - sizeof(UConverterStaticData), - "UTF-8", - 1208, UCNV_IBM, UCNV_UTF8, - 1, 3, /* max 3 bytes per UChar from UTF-8 (4 bytes from surrogate _pair_) */ - { 0xef, 0xbf, 0xbd, 0 },3,false,false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - - -const UConverterSharedData _UTF8Data= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF8StaticData, &_UTF8Impl); - -/* CESU-8 converter data ---------------------------------------------------- */ - -static const UConverterImpl _CESU8Impl={ - UCNV_CESU8, - - NULL, - NULL, - - NULL, - NULL, - NULL, - - ucnv_toUnicode_UTF8, - ucnv_toUnicode_UTF8_OFFSETS_LOGIC, - ucnv_fromUnicode_UTF8, - ucnv_fromUnicode_UTF8_OFFSETS_LOGIC, - NULL, - - NULL, - NULL, - NULL, - NULL, - ucnv_getCompleteUnicodeSet, - - NULL, - NULL -}; - -static const UConverterStaticData _CESU8StaticData={ - sizeof(UConverterStaticData), - "CESU-8", - 9400, /* CCSID for CESU-8 */ - UCNV_UNKNOWN, UCNV_CESU8, 1, 3, - { 0xef, 0xbf, 0xbd, 0 },3,false,false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - - -const UConverterSharedData _CESU8Data= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_CESU8StaticData, &_CESU8Impl); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2002-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: ucnv_u8.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002jul01 +* created by: Markus W. Scherer +* +* UTF-8 converter implementation. Used to be in ucnv_utf.c. +* +* Also, CESU-8 implementation, see UTR 26. +* The CESU-8 converter uses all the same functions as the +* UTF-8 converter, with a branch for converting supplementary code points. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/utf.h" +#include "unicode/utf8.h" +#include "unicode/utf16.h" +#include "uassert.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" +#include "cmemory.h" +#include "ustr_imp.h" + +/* Prototypes --------------------------------------------------------------- */ + +/* Keep these here to make finicky compilers happy */ + +U_CFUNC void ucnv_fromUnicode_UTF8(UConverterFromUnicodeArgs *args, + UErrorCode *err); +U_CFUNC void ucnv_fromUnicode_UTF8_OFFSETS_LOGIC(UConverterFromUnicodeArgs *args, + UErrorCode *err); + + +/* UTF-8 -------------------------------------------------------------------- */ + +#define MAXIMUM_UCS2 0x0000FFFF + +static const uint32_t offsetsFromUTF8[5] = {0, + (uint32_t) 0x00000000, (uint32_t) 0x00003080, (uint32_t) 0x000E2080, + (uint32_t) 0x03C82080 +}; + +static UBool hasCESU8Data(const UConverter *cnv) +{ +#if UCONFIG_ONLY_HTML_CONVERSION + return false; +#else + return (UBool)(cnv->sharedData == &_CESU8Data); +#endif +} +U_CDECL_BEGIN +static void U_CALLCONV ucnv_toUnicode_UTF8 (UConverterToUnicodeArgs * args, + UErrorCode * err) +{ + UConverter *cnv = args->converter; + const unsigned char *mySource = (unsigned char *) args->source; + char16_t *myTarget = args->target; + const unsigned char *sourceLimit = (unsigned char *) args->sourceLimit; + const char16_t *targetLimit = args->targetLimit; + unsigned char *toUBytes = cnv->toUBytes; + UBool isCESU8 = hasCESU8Data(cnv); + uint32_t ch, ch2 = 0; + int32_t i, inBytes; + + /* Restore size of current sequence */ + if (cnv->toULength > 0 && myTarget < targetLimit) + { + inBytes = cnv->mode; /* restore # of bytes to consume */ + i = cnv->toULength; /* restore # of bytes consumed */ + cnv->toULength = 0; + + ch = cnv->toUnicodeStatus;/*Stores the previously calculated ch from a previous call*/ + cnv->toUnicodeStatus = 0; + goto morebytes; + } + + + while (mySource < sourceLimit && myTarget < targetLimit) + { + ch = *(mySource++); + if (U8_IS_SINGLE(ch)) /* Simple case */ + { + *(myTarget++) = (char16_t) ch; + } + else + { + /* store the first char */ + toUBytes[0] = (char)ch; + inBytes = U8_COUNT_BYTES_NON_ASCII(ch); /* lookup current sequence length */ + i = 1; + +morebytes: + while (i < inBytes) + { + if (mySource < sourceLimit) + { + toUBytes[i] = (char) (ch2 = *mySource); + if (!icu::UTF8::isValidTrail(ch, static_cast(ch2), i, inBytes) && + !(isCESU8 && i == 1 && ch == 0xed && U8_IS_TRAIL(ch2))) + { + break; /* i < inBytes */ + } + ch = (ch << 6) + ch2; + ++mySource; + i++; + } + else + { + /* stores a partially calculated target*/ + cnv->toUnicodeStatus = ch; + cnv->mode = inBytes; + cnv->toULength = (int8_t) i; + goto donefornow; + } + } + + // In CESU-8, only surrogates, not supplementary code points, are encoded directly. + if (i == inBytes && (!isCESU8 || i <= 3)) + { + /* Remove the accumulated high bits */ + ch -= offsetsFromUTF8[inBytes]; + + /* Normal valid byte when the loop has not prematurely terminated (i < inBytes) */ + if (ch <= MAXIMUM_UCS2) + { + /* fits in 16 bits */ + *(myTarget++) = (char16_t) ch; + } + else + { + /* write out the surrogates */ + *(myTarget++) = U16_LEAD(ch); + ch = U16_TRAIL(ch); + if (myTarget < targetLimit) + { + *(myTarget++) = (char16_t)ch; + } + else + { + /* Put in overflow buffer (not handled here) */ + cnv->UCharErrorBuffer[0] = (char16_t) ch; + cnv->UCharErrorBufferLength = 1; + *err = U_BUFFER_OVERFLOW_ERROR; + break; + } + } + } + else + { + cnv->toULength = (int8_t)i; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + } + +donefornow: + if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) + { + /* End of target buffer */ + *err = U_BUFFER_OVERFLOW_ERROR; + } + + args->target = myTarget; + args->source = (const char *) mySource; +} + +static void U_CALLCONV ucnv_toUnicode_UTF8_OFFSETS_LOGIC (UConverterToUnicodeArgs * args, + UErrorCode * err) +{ + UConverter *cnv = args->converter; + const unsigned char *mySource = (unsigned char *) args->source; + char16_t *myTarget = args->target; + int32_t *myOffsets = args->offsets; + int32_t offsetNum = 0; + const unsigned char *sourceLimit = (unsigned char *) args->sourceLimit; + const char16_t *targetLimit = args->targetLimit; + unsigned char *toUBytes = cnv->toUBytes; + UBool isCESU8 = hasCESU8Data(cnv); + uint32_t ch, ch2 = 0; + int32_t i, inBytes; + + /* Restore size of current sequence */ + if (cnv->toULength > 0 && myTarget < targetLimit) + { + inBytes = cnv->mode; /* restore # of bytes to consume */ + i = cnv->toULength; /* restore # of bytes consumed */ + cnv->toULength = 0; + + ch = cnv->toUnicodeStatus;/*Stores the previously calculated ch from a previous call*/ + cnv->toUnicodeStatus = 0; + goto morebytes; + } + + while (mySource < sourceLimit && myTarget < targetLimit) + { + ch = *(mySource++); + if (U8_IS_SINGLE(ch)) /* Simple case */ + { + *(myTarget++) = (char16_t) ch; + *(myOffsets++) = offsetNum++; + } + else + { + toUBytes[0] = (char)ch; + inBytes = U8_COUNT_BYTES_NON_ASCII(ch); + i = 1; + +morebytes: + while (i < inBytes) + { + if (mySource < sourceLimit) + { + toUBytes[i] = (char) (ch2 = *mySource); + if (!icu::UTF8::isValidTrail(ch, static_cast(ch2), i, inBytes) && + !(isCESU8 && i == 1 && ch == 0xed && U8_IS_TRAIL(ch2))) + { + break; /* i < inBytes */ + } + ch = (ch << 6) + ch2; + ++mySource; + i++; + } + else + { + cnv->toUnicodeStatus = ch; + cnv->mode = inBytes; + cnv->toULength = (int8_t)i; + goto donefornow; + } + } + + // In CESU-8, only surrogates, not supplementary code points, are encoded directly. + if (i == inBytes && (!isCESU8 || i <= 3)) + { + /* Remove the accumulated high bits */ + ch -= offsetsFromUTF8[inBytes]; + + /* Normal valid byte when the loop has not prematurely terminated (i < inBytes) */ + if (ch <= MAXIMUM_UCS2) + { + /* fits in 16 bits */ + *(myTarget++) = (char16_t) ch; + *(myOffsets++) = offsetNum; + } + else + { + /* write out the surrogates */ + *(myTarget++) = U16_LEAD(ch); + *(myOffsets++) = offsetNum; + ch = U16_TRAIL(ch); + if (myTarget < targetLimit) + { + *(myTarget++) = (char16_t)ch; + *(myOffsets++) = offsetNum; + } + else + { + cnv->UCharErrorBuffer[0] = (char16_t) ch; + cnv->UCharErrorBufferLength = 1; + *err = U_BUFFER_OVERFLOW_ERROR; + } + } + offsetNum += i; + } + else + { + cnv->toULength = (int8_t)i; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + } + +donefornow: + if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) + { /* End of target buffer */ + *err = U_BUFFER_OVERFLOW_ERROR; + } + + args->target = myTarget; + args->source = (const char *) mySource; + args->offsets = myOffsets; +} +U_CDECL_END + +U_CFUNC void U_CALLCONV ucnv_fromUnicode_UTF8 (UConverterFromUnicodeArgs * args, + UErrorCode * err) +{ + UConverter *cnv = args->converter; + const char16_t *mySource = args->source; + const char16_t *sourceLimit = args->sourceLimit; + uint8_t *myTarget = (uint8_t *) args->target; + const uint8_t *targetLimit = (uint8_t *) args->targetLimit; + uint8_t *tempPtr; + UChar32 ch; + uint8_t tempBuf[4]; + int32_t indexToWrite; + UBool isNotCESU8 = !hasCESU8Data(cnv); + + if (cnv->fromUChar32 && myTarget < targetLimit) + { + ch = cnv->fromUChar32; + cnv->fromUChar32 = 0; + goto lowsurrogate; + } + + while (mySource < sourceLimit && myTarget < targetLimit) + { + ch = *(mySource++); + + if (ch < 0x80) /* Single byte */ + { + *(myTarget++) = (uint8_t) ch; + } + else if (ch < 0x800) /* Double byte */ + { + *(myTarget++) = (uint8_t) ((ch >> 6) | 0xc0); + if (myTarget < targetLimit) + { + *(myTarget++) = (uint8_t) ((ch & 0x3f) | 0x80); + } + else + { + cnv->charErrorBuffer[0] = (uint8_t) ((ch & 0x3f) | 0x80); + cnv->charErrorBufferLength = 1; + *err = U_BUFFER_OVERFLOW_ERROR; + } + } + else { + /* Check for surrogates */ + if(U16_IS_SURROGATE(ch) && isNotCESU8) { +lowsurrogate: + if (mySource < sourceLimit) { + /* test both code units */ + if(U16_IS_SURROGATE_LEAD(ch) && U16_IS_TRAIL(*mySource)) { + /* convert and consume this supplementary code point */ + ch=U16_GET_SUPPLEMENTARY(ch, *mySource); + ++mySource; + /* exit this condition tree */ + } + else { + /* this is an unpaired trail or lead code unit */ + /* callback(illegal) */ + cnv->fromUChar32 = ch; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + else { + /* no more input */ + cnv->fromUChar32 = ch; + break; + } + } + + /* Do we write the buffer directly for speed, + or do we have to be careful about target buffer space? */ + tempPtr = (((targetLimit - myTarget) >= 4) ? myTarget : tempBuf); + + if (ch <= MAXIMUM_UCS2) { + indexToWrite = 2; + tempPtr[0] = (uint8_t) ((ch >> 12) | 0xe0); + } + else { + indexToWrite = 3; + tempPtr[0] = (uint8_t) ((ch >> 18) | 0xf0); + tempPtr[1] = (uint8_t) (((ch >> 12) & 0x3f) | 0x80); + } + tempPtr[indexToWrite-1] = (uint8_t) (((ch >> 6) & 0x3f) | 0x80); + tempPtr[indexToWrite] = (uint8_t) ((ch & 0x3f) | 0x80); + + if (tempPtr == myTarget) { + /* There was enough space to write the codepoint directly. */ + myTarget += (indexToWrite + 1); + } + else { + /* We might run out of room soon. Write it slowly. */ + for (; tempPtr <= (tempBuf + indexToWrite); tempPtr++) { + if (myTarget < targetLimit) { + *(myTarget++) = *tempPtr; + } + else { + cnv->charErrorBuffer[cnv->charErrorBufferLength++] = *tempPtr; + *err = U_BUFFER_OVERFLOW_ERROR; + } + } + } + } + } + + if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) + { + *err = U_BUFFER_OVERFLOW_ERROR; + } + + args->target = (char *) myTarget; + args->source = mySource; +} + +U_CFUNC void U_CALLCONV ucnv_fromUnicode_UTF8_OFFSETS_LOGIC (UConverterFromUnicodeArgs * args, + UErrorCode * err) +{ + UConverter *cnv = args->converter; + const char16_t *mySource = args->source; + int32_t *myOffsets = args->offsets; + const char16_t *sourceLimit = args->sourceLimit; + uint8_t *myTarget = (uint8_t *) args->target; + const uint8_t *targetLimit = (uint8_t *) args->targetLimit; + uint8_t *tempPtr; + UChar32 ch; + int32_t offsetNum, nextSourceIndex; + int32_t indexToWrite; + uint8_t tempBuf[4]; + UBool isNotCESU8 = !hasCESU8Data(cnv); + + if (cnv->fromUChar32 && myTarget < targetLimit) + { + ch = cnv->fromUChar32; + cnv->fromUChar32 = 0; + offsetNum = -1; + nextSourceIndex = 0; + goto lowsurrogate; + } else { + offsetNum = 0; + } + + while (mySource < sourceLimit && myTarget < targetLimit) + { + ch = *(mySource++); + + if (ch < 0x80) /* Single byte */ + { + *(myOffsets++) = offsetNum++; + *(myTarget++) = (char) ch; + } + else if (ch < 0x800) /* Double byte */ + { + *(myOffsets++) = offsetNum; + *(myTarget++) = (uint8_t) ((ch >> 6) | 0xc0); + if (myTarget < targetLimit) + { + *(myOffsets++) = offsetNum++; + *(myTarget++) = (uint8_t) ((ch & 0x3f) | 0x80); + } + else + { + cnv->charErrorBuffer[0] = (uint8_t) ((ch & 0x3f) | 0x80); + cnv->charErrorBufferLength = 1; + *err = U_BUFFER_OVERFLOW_ERROR; + } + } + else + /* Check for surrogates */ + { + nextSourceIndex = offsetNum + 1; + + if(U16_IS_SURROGATE(ch) && isNotCESU8) { +lowsurrogate: + if (mySource < sourceLimit) { + /* test both code units */ + if(U16_IS_SURROGATE_LEAD(ch) && U16_IS_TRAIL(*mySource)) { + /* convert and consume this supplementary code point */ + ch=U16_GET_SUPPLEMENTARY(ch, *mySource); + ++mySource; + ++nextSourceIndex; + /* exit this condition tree */ + } + else { + /* this is an unpaired trail or lead code unit */ + /* callback(illegal) */ + cnv->fromUChar32 = ch; + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + else { + /* no more input */ + cnv->fromUChar32 = ch; + break; + } + } + + /* Do we write the buffer directly for speed, + or do we have to be careful about target buffer space? */ + tempPtr = (((targetLimit - myTarget) >= 4) ? myTarget : tempBuf); + + if (ch <= MAXIMUM_UCS2) { + indexToWrite = 2; + tempPtr[0] = (uint8_t) ((ch >> 12) | 0xe0); + } + else { + indexToWrite = 3; + tempPtr[0] = (uint8_t) ((ch >> 18) | 0xf0); + tempPtr[1] = (uint8_t) (((ch >> 12) & 0x3f) | 0x80); + } + tempPtr[indexToWrite-1] = (uint8_t) (((ch >> 6) & 0x3f) | 0x80); + tempPtr[indexToWrite] = (uint8_t) ((ch & 0x3f) | 0x80); + + if (tempPtr == myTarget) { + /* There was enough space to write the codepoint directly. */ + myTarget += (indexToWrite + 1); + myOffsets[0] = offsetNum; + myOffsets[1] = offsetNum; + myOffsets[2] = offsetNum; + if (indexToWrite >= 3) { + myOffsets[3] = offsetNum; + } + myOffsets += (indexToWrite + 1); + } + else { + /* We might run out of room soon. Write it slowly. */ + for (; tempPtr <= (tempBuf + indexToWrite); tempPtr++) { + if (myTarget < targetLimit) + { + *(myOffsets++) = offsetNum; + *(myTarget++) = *tempPtr; + } + else + { + cnv->charErrorBuffer[cnv->charErrorBufferLength++] = *tempPtr; + *err = U_BUFFER_OVERFLOW_ERROR; + } + } + } + offsetNum = nextSourceIndex; + } + } + + if (mySource < sourceLimit && myTarget >= targetLimit && U_SUCCESS(*err)) + { + *err = U_BUFFER_OVERFLOW_ERROR; + } + + args->target = (char *) myTarget; + args->source = mySource; + args->offsets = myOffsets; +} + +U_CDECL_BEGIN +static UChar32 U_CALLCONV ucnv_getNextUChar_UTF8(UConverterToUnicodeArgs *args, + UErrorCode *err) { + UConverter *cnv; + const uint8_t *sourceInitial; + const uint8_t *source; + uint8_t myByte; + UChar32 ch; + int8_t i; + + /* UTF-8 only here, the framework handles CESU-8 to combine surrogate pairs */ + + cnv = args->converter; + sourceInitial = source = (const uint8_t *)args->source; + if (source >= (const uint8_t *)args->sourceLimit) + { + /* no input */ + *err = U_INDEX_OUTOFBOUNDS_ERROR; + return 0xffff; + } + + myByte = (uint8_t)*(source++); + if (U8_IS_SINGLE(myByte)) + { + args->source = (const char *)source; + return (UChar32)myByte; + } + + uint16_t countTrailBytes = U8_COUNT_TRAIL_BYTES(myByte); + if (countTrailBytes == 0) { + cnv->toUBytes[0] = myByte; + cnv->toULength = 1; + *err = U_ILLEGAL_CHAR_FOUND; + args->source = (const char *)source; + return 0xffff; + } + + /*The byte sequence is longer than the buffer area passed*/ + if (((const char *)source + countTrailBytes) > args->sourceLimit) + { + /* check if all of the remaining bytes are trail bytes */ + uint16_t extraBytesToWrite = countTrailBytes + 1; + cnv->toUBytes[0] = myByte; + i = 1; + *err = U_TRUNCATED_CHAR_FOUND; + while(source < (const uint8_t *)args->sourceLimit) { + uint8_t b = *source; + if(icu::UTF8::isValidTrail(myByte, b, i, extraBytesToWrite)) { + cnv->toUBytes[i++] = b; + ++source; + } else { + /* error even before we run out of input */ + *err = U_ILLEGAL_CHAR_FOUND; + break; + } + } + cnv->toULength = i; + args->source = (const char *)source; + return 0xffff; + } + + ch = myByte << 6; + if(countTrailBytes == 2) { + uint8_t t1 = *source, t2; + if(U8_IS_VALID_LEAD3_AND_T1(myByte, t1) && U8_IS_TRAIL(t2 = *++source)) { + args->source = (const char *)(source + 1); + return (((ch + t1) << 6) + t2) - offsetsFromUTF8[3]; + } + } else if(countTrailBytes == 1) { + uint8_t t1 = *source; + if(U8_IS_TRAIL(t1)) { + args->source = (const char *)(source + 1); + return (ch + t1) - offsetsFromUTF8[2]; + } + } else { // countTrailBytes == 3 + uint8_t t1 = *source, t2, t3; + if(U8_IS_VALID_LEAD4_AND_T1(myByte, t1) && U8_IS_TRAIL(t2 = *++source) && + U8_IS_TRAIL(t3 = *++source)) { + args->source = (const char *)(source + 1); + return (((((ch + t1) << 6) + t2) << 6) + t3) - offsetsFromUTF8[4]; + } + } + args->source = (const char *)source; + + for(i = 0; sourceInitial < source; ++i) { + cnv->toUBytes[i] = *sourceInitial++; + } + cnv->toULength = i; + *err = U_ILLEGAL_CHAR_FOUND; + return 0xffff; +} +U_CDECL_END + +/* UTF-8-from-UTF-8 conversion functions ------------------------------------ */ + +U_CDECL_BEGIN +/* "Convert" UTF-8 to UTF-8: Validate and copy. Modified from ucnv_DBCSFromUTF8(). */ +static void U_CALLCONV +ucnv_UTF8FromUTF8(UConverterFromUnicodeArgs *pFromUArgs, + UConverterToUnicodeArgs *pToUArgs, + UErrorCode *pErrorCode) { + UConverter *utf8; + const uint8_t *source, *sourceLimit; + uint8_t *target; + int32_t targetCapacity; + int32_t count; + + int8_t oldToULength, toULength, toULimit; + + UChar32 c; + uint8_t b, t1, t2; + + /* set up the local pointers */ + utf8=pToUArgs->converter; + source=(uint8_t *)pToUArgs->source; + sourceLimit=(uint8_t *)pToUArgs->sourceLimit; + target=(uint8_t *)pFromUArgs->target; + targetCapacity=(int32_t)(pFromUArgs->targetLimit-pFromUArgs->target); + + /* get the converter state from the UTF-8 UConverter */ + if(utf8->toULength > 0) { + toULength=oldToULength=utf8->toULength; + toULimit=(int8_t)utf8->mode; + c=(UChar32)utf8->toUnicodeStatus; + } else { + toULength=oldToULength=toULimit=0; + c = 0; + } + + count=(int32_t)(sourceLimit-source)+oldToULength; + if(counttargetCapacity) { + count=targetCapacity; + } + + // The conversion loop checks count>0 only once per character. + // If the buffer ends with a truncated sequence, + // then we reduce the count to stop before that, + // and collect the remaining bytes after the conversion loop. + + // Do not go back into the bytes that will be read for finishing a partial + // sequence from the previous buffer. + int32_t length=count-toULength; + U8_TRUNCATE_IF_INCOMPLETE(source, 0, length); + count=toULength+length; + } + + if(c!=0) { + utf8->toUnicodeStatus=0; + utf8->toULength=0; + goto moreBytes; + /* See note in ucnv_SBCSFromUTF8() about this goto. */ + } + + /* conversion loop */ + while(count>0) { + b=*source++; + if(U8_IS_SINGLE(b)) { + /* convert ASCII */ + *target++=b; + --count; + continue; + } else { + if(b>=0xe0) { + if( /* handle U+0800..U+FFFF inline */ + b<0xf0 && + U8_IS_VALID_LEAD3_AND_T1(b, t1=source[0]) && + U8_IS_TRAIL(t2=source[1]) + ) { + source+=2; + *target++=b; + *target++=t1; + *target++=t2; + count-=3; + continue; + } + } else { + if( /* handle U+0080..U+07FF inline */ + b>=0xc2 && + U8_IS_TRAIL(t1=*source) + ) { + ++source; + *target++=b; + *target++=t1; + count-=2; + continue; + } + } + + /* handle "complicated" and error cases, and continuing partial characters */ + oldToULength=0; + toULength=1; + toULimit=U8_COUNT_BYTES_NON_ASCII(b); + c=b; +moreBytes: + while(toULengthtoUBytes[oldToULength++]=*source++; + } + utf8->toUnicodeStatus=c; + utf8->toULength=toULength; + utf8->mode=toULimit; + pToUArgs->source=(char *)source; + pFromUArgs->target=(char *)target; + return; + } + } + + if(toULength!=toULimit) { + /* error handling: illegal UTF-8 byte sequence */ + source-=(toULength-oldToULength); + while(oldToULengthtoUBytes[oldToULength++]=*source++; + } + utf8->toULength=toULength; + pToUArgs->source=(char *)source; + pFromUArgs->target=(char *)target; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + return; + } + + /* copy the legal byte sequence to the target */ + { + int8_t i; + + for(i=0; itoUBytes[i]; + } + source-=(toULength-oldToULength); + for(; i=0); + + if(U_SUCCESS(*pErrorCode) && sourcetargetLimit) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } else { + b=*source; + toULimit=U8_COUNT_BYTES(b); + if(toULimit>(sourceLimit-source)) { + /* collect a truncated byte sequence */ + toULength=0; + c=b; + for(;;) { + utf8->toUBytes[toULength++]=b; + if(++source==sourceLimit) { + /* partial byte sequence at end of source */ + utf8->toUnicodeStatus=c; + utf8->toULength=toULength; + utf8->mode=toULimit; + break; + } else if(!icu::UTF8::isValidTrail(c, b=*source, toULength, toULimit)) { + utf8->toULength=toULength; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } + c=(c<<6)+b; + } + } else { + /* partial-sequence target overflow: fall back to the pivoting implementation */ + *pErrorCode=U_USING_DEFAULT_WARNING; + } + } + } + + /* write back the updated pointers */ + pToUArgs->source=(char *)source; + pFromUArgs->target=(char *)target; +} + +U_CDECL_END + +/* UTF-8 converter data ----------------------------------------------------- */ + +static const UConverterImpl _UTF8Impl={ + UCNV_UTF8, + + nullptr, + nullptr, + + nullptr, + nullptr, + nullptr, + + ucnv_toUnicode_UTF8, + ucnv_toUnicode_UTF8_OFFSETS_LOGIC, + ucnv_fromUnicode_UTF8, + ucnv_fromUnicode_UTF8_OFFSETS_LOGIC, + ucnv_getNextUChar_UTF8, + + nullptr, + nullptr, + nullptr, + nullptr, + ucnv_getNonSurrogateUnicodeSet, + + ucnv_UTF8FromUTF8, + ucnv_UTF8FromUTF8 +}; + +/* The 1208 CCSID refers to any version of Unicode of UTF-8 */ +static const UConverterStaticData _UTF8StaticData={ + sizeof(UConverterStaticData), + "UTF-8", + 1208, UCNV_IBM, UCNV_UTF8, + 1, 3, /* max 3 bytes per char16_t from UTF-8 (4 bytes from surrogate _pair_) */ + { 0xef, 0xbf, 0xbd, 0 },3,false,false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + + +const UConverterSharedData _UTF8Data= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF8StaticData, &_UTF8Impl); + +/* CESU-8 converter data ---------------------------------------------------- */ + +static const UConverterImpl _CESU8Impl={ + UCNV_CESU8, + + nullptr, + nullptr, + + nullptr, + nullptr, + nullptr, + + ucnv_toUnicode_UTF8, + ucnv_toUnicode_UTF8_OFFSETS_LOGIC, + ucnv_fromUnicode_UTF8, + ucnv_fromUnicode_UTF8_OFFSETS_LOGIC, + nullptr, + + nullptr, + nullptr, + nullptr, + nullptr, + ucnv_getCompleteUnicodeSet, + + nullptr, + nullptr +}; + +static const UConverterStaticData _CESU8StaticData={ + sizeof(UConverterStaticData), + "CESU-8", + 9400, /* CCSID for CESU-8 */ + UCNV_UNKNOWN, UCNV_CESU8, 1, 3, + { 0xef, 0xbf, 0xbd, 0 },3,false,false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + + +const UConverterSharedData _CESU8Data= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_CESU8StaticData, &_CESU8Impl); + +#endif diff --git a/deps/icu-small/source/common/ucnvbocu.cpp b/deps/icu-small/source/common/ucnvbocu.cpp index edb49d36a9ce53..81cc8b2526c0eb 100644 --- a/deps/icu-small/source/common/ucnvbocu.cpp +++ b/deps/icu-small/source/common/ucnvbocu.cpp @@ -1,1413 +1,1413 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2002-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: ucnvbocu.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002mar27 -* created by: Markus W. Scherer -* -* This is an implementation of the Binary Ordered Compression for Unicode, -* in its MIME-friendly form as defined in http://www.unicode.org/notes/tn6/ -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/ucnv_cb.h" -#include "unicode/utf16.h" -#include "putilimp.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" -#include "uassert.h" - -/* BOCU-1 constants and macros ---------------------------------------------- */ - -/* - * BOCU-1 encodes the code points of a Unicode string as - * a sequence of byte-encoded differences (slope detection), - * preserving lexical order. - * - * Optimize the difference-taking for runs of Unicode text within - * small scripts: - * - * Most small scripts are allocated within aligned 128-blocks of Unicode - * code points. Lexical order is preserved if the "previous code point" state - * is always moved into the middle of such a block. - * - * Additionally, "prev" is moved from anywhere in the Unihan and Hangul - * areas into the middle of those areas. - * - * C0 control codes and space are encoded with their US-ASCII bytes. - * "prev" is reset for C0 controls but not for space. - */ - -/* initial value for "prev": middle of the ASCII range */ -#define BOCU1_ASCII_PREV 0x40 - -/* bounding byte values for differences */ -#define BOCU1_MIN 0x21 -#define BOCU1_MIDDLE 0x90 -#define BOCU1_MAX_LEAD 0xfe -#define BOCU1_MAX_TRAIL 0xff -#define BOCU1_RESET 0xff - -/* number of lead bytes */ -#define BOCU1_COUNT (BOCU1_MAX_LEAD-BOCU1_MIN+1) - -/* adjust trail byte counts for the use of some C0 control byte values */ -#define BOCU1_TRAIL_CONTROLS_COUNT 20 -#define BOCU1_TRAIL_BYTE_OFFSET (BOCU1_MIN-BOCU1_TRAIL_CONTROLS_COUNT) - -/* number of trail bytes */ -#define BOCU1_TRAIL_COUNT ((BOCU1_MAX_TRAIL-BOCU1_MIN+1)+BOCU1_TRAIL_CONTROLS_COUNT) - -/* - * number of positive and negative single-byte codes - * (counting 0==BOCU1_MIDDLE among the positive ones) - */ -#define BOCU1_SINGLE 64 - -/* number of lead bytes for positive and negative 2/3/4-byte sequences */ -#define BOCU1_LEAD_2 43 -#define BOCU1_LEAD_3 3 -#define BOCU1_LEAD_4 1 - -/* The difference value range for single-byters. */ -#define BOCU1_REACH_POS_1 (BOCU1_SINGLE-1) -#define BOCU1_REACH_NEG_1 (-BOCU1_SINGLE) - -/* The difference value range for double-byters. */ -#define BOCU1_REACH_POS_2 (BOCU1_REACH_POS_1+BOCU1_LEAD_2*BOCU1_TRAIL_COUNT) -#define BOCU1_REACH_NEG_2 (BOCU1_REACH_NEG_1-BOCU1_LEAD_2*BOCU1_TRAIL_COUNT) - -/* The difference value range for 3-byters. */ -#define BOCU1_REACH_POS_3 \ - (BOCU1_REACH_POS_2+BOCU1_LEAD_3*BOCU1_TRAIL_COUNT*BOCU1_TRAIL_COUNT) - -#define BOCU1_REACH_NEG_3 (BOCU1_REACH_NEG_2-BOCU1_LEAD_3*BOCU1_TRAIL_COUNT*BOCU1_TRAIL_COUNT) - -/* The lead byte start values. */ -#define BOCU1_START_POS_2 (BOCU1_MIDDLE+BOCU1_REACH_POS_1+1) -#define BOCU1_START_POS_3 (BOCU1_START_POS_2+BOCU1_LEAD_2) -#define BOCU1_START_POS_4 (BOCU1_START_POS_3+BOCU1_LEAD_3) - /* ==BOCU1_MAX_LEAD */ - -#define BOCU1_START_NEG_2 (BOCU1_MIDDLE+BOCU1_REACH_NEG_1) -#define BOCU1_START_NEG_3 (BOCU1_START_NEG_2-BOCU1_LEAD_2) -#define BOCU1_START_NEG_4 (BOCU1_START_NEG_3-BOCU1_LEAD_3) - /* ==BOCU1_MIN+1 */ - -/* The length of a byte sequence, according to the lead byte (!=BOCU1_RESET). */ -#define BOCU1_LENGTH_FROM_LEAD(lead) \ - ((BOCU1_START_NEG_2<=(lead) && (lead)>24 : 4) - -/* - * 12 commonly used C0 control codes (and space) are only used to encode - * themselves directly, - * which makes BOCU-1 MIME-usable and reasonably safe for - * ASCII-oriented software. - * - * These controls are - * 0 NUL - * - * 7 BEL - * 8 BS - * - * 9 TAB - * a LF - * b VT - * c FF - * d CR - * - * e SO - * f SI - * - * 1a SUB - * 1b ESC - * - * The other 20 C0 controls are also encoded directly (to preserve order) - * but are also used as trail bytes in difference encoding - * (for better compression). - */ -#define BOCU1_TRAIL_TO_BYTE(t) ((t)>=BOCU1_TRAIL_CONTROLS_COUNT ? (t)+BOCU1_TRAIL_BYTE_OFFSET : bocu1TrailToByte[t]) - -/* - * Byte value map for control codes, - * from external byte values 0x00..0x20 - * to trail byte values 0..19 (0..0x13) as used in the difference calculation. - * External byte values that are illegal as trail bytes are mapped to -1. - */ -static const int8_t -bocu1ByteToTrail[BOCU1_MIN]={ -/* 0 1 2 3 4 5 6 7 */ - -1, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, -1, - -/* 8 9 a b c d e f */ - -1, -1, -1, -1, -1, -1, -1, -1, - -/* 10 11 12 13 14 15 16 17 */ - 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, - -/* 18 19 1a 1b 1c 1d 1e 1f */ - 0x0e, 0x0f, -1, -1, 0x10, 0x11, 0x12, 0x13, - -/* 20 */ - -1 -}; - -/* - * Byte value map for control codes, - * from trail byte values 0..19 (0..0x13) as used in the difference calculation - * to external byte values 0x00..0x20. - */ -static const int8_t -bocu1TrailToByte[BOCU1_TRAIL_CONTROLS_COUNT]={ -/* 0 1 2 3 4 5 6 7 */ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x10, 0x11, - -/* 8 9 a b c d e f */ - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, - -/* 10 11 12 13 */ - 0x1c, 0x1d, 0x1e, 0x1f -}; - -/** - * Integer division and modulo with negative numerators - * yields negative modulo results and quotients that are one more than - * what we need here. - * This macro adjust the results so that the modulo-value m is always >=0. - * - * For positive n, the if() condition is always false. - * - * @param n Number to be split into quotient and rest. - * Will be modified to contain the quotient. - * @param d Divisor. - * @param m Output variable for the rest (modulo result). - */ -#define NEGDIVMOD(n, d, m) UPRV_BLOCK_MACRO_BEGIN { \ - (m)=(n)%(d); \ - (n)/=(d); \ - if((m)<0) { \ - --(n); \ - (m)+=(d); \ - } \ -} UPRV_BLOCK_MACRO_END - -/* Faster versions of packDiff() for single-byte-encoded diff values. */ - -/** Is a diff value encodable in a single byte? */ -#define DIFF_IS_SINGLE(diff) (BOCU1_REACH_NEG_1<=(diff) && (diff)<=BOCU1_REACH_POS_1) - -/** Encode a diff value in a single byte. */ -#define PACK_SINGLE_DIFF(diff) (BOCU1_MIDDLE+(diff)) - -/** Is a diff value encodable in two bytes? */ -#define DIFF_IS_DOUBLE(diff) (BOCU1_REACH_NEG_2<=(diff) && (diff)<=BOCU1_REACH_POS_2) - -/* BOCU-1 implementation functions ------------------------------------------ */ - -#define BOCU1_SIMPLE_PREV(c) (((c)&~0x7f)+BOCU1_ASCII_PREV) - -/** - * Compute the next "previous" value for differencing - * from the current code point. - * - * @param c current code point, 0x3040..0xd7a3 (rest handled by macro below) - * @return "previous code point" state value - */ -static inline int32_t -bocu1Prev(int32_t c) { - /* compute new prev */ - if(/* 0x3040<=c && */ c<=0x309f) { - /* Hiragana is not 128-aligned */ - return 0x3070; - } else if(0x4e00<=c && c<=0x9fa5) { - /* CJK Unihan */ - return 0x4e00-BOCU1_REACH_NEG_2; - } else if(0xac00<=c /* && c<=0xd7a3 */) { - /* Korean Hangul */ - return (0xd7a3+0xac00)/2; - } else { - /* mostly small scripts */ - return BOCU1_SIMPLE_PREV(c); - } -} - -/** Fast version of bocu1Prev() for most scripts. */ -#define BOCU1_PREV(c) ((c)<0x3040 || (c)>0xd7a3 ? BOCU1_SIMPLE_PREV(c) : bocu1Prev(c)) - -/* - * The BOCU-1 converter uses the standard setup code in ucnv.c/ucnv_bld.c. - * The UConverter fields are used as follows: - * - * fromUnicodeStatus encoder's prev (0 will be interpreted as BOCU1_ASCII_PREV) - * - * toUnicodeStatus decoder's prev (0 will be interpreted as BOCU1_ASCII_PREV) - * mode decoder's incomplete (diff<<2)|count (ignored when toULength==0) - */ - -/* BOCU-1-from-Unicode conversion functions --------------------------------- */ - -/** - * Encode a difference -0x10ffff..0x10ffff in 1..4 bytes - * and return a packed integer with them. - * - * The encoding favors small absolute differences with short encodings - * to compress runs of same-script characters. - * - * Optimized version with unrolled loops and fewer floating-point operations - * than the standard packDiff(). - * - * @param diff difference value -0x10ffff..0x10ffff - * @return - * 0x010000zz for 1-byte sequence zz - * 0x0200yyzz for 2-byte sequence yy zz - * 0x03xxyyzz for 3-byte sequence xx yy zz - * 0xwwxxyyzz for 4-byte sequence ww xx yy zz (ww>0x03) - */ -static int32_t -packDiff(int32_t diff) { - int32_t result, m; - - U_ASSERT(!DIFF_IS_SINGLE(diff)); /* assume we won't be called where diff==BOCU1_REACH_NEG_1=-64 */ - if(diff>=BOCU1_REACH_NEG_1) { - /* mostly positive differences, and single-byte negative ones */ -#if 0 /* single-byte case handled in macros, see below */ - if(diff<=BOCU1_REACH_POS_1) { - /* single byte */ - return 0x01000000|(BOCU1_MIDDLE+diff); - } else -#endif - if(diff<=BOCU1_REACH_POS_2) { - /* two bytes */ - diff-=BOCU1_REACH_POS_1+1; - result=0x02000000; - - m=diff%BOCU1_TRAIL_COUNT; - diff/=BOCU1_TRAIL_COUNT; - result|=BOCU1_TRAIL_TO_BYTE(m); - - result|=(BOCU1_START_POS_2+diff)<<8; - } else if(diff<=BOCU1_REACH_POS_3) { - /* three bytes */ - diff-=BOCU1_REACH_POS_2+1; - result=0x03000000; - - m=diff%BOCU1_TRAIL_COUNT; - diff/=BOCU1_TRAIL_COUNT; - result|=BOCU1_TRAIL_TO_BYTE(m); - - m=diff%BOCU1_TRAIL_COUNT; - diff/=BOCU1_TRAIL_COUNT; - result|=BOCU1_TRAIL_TO_BYTE(m)<<8; - - result|=(BOCU1_START_POS_3+diff)<<16; - } else { - /* four bytes */ - diff-=BOCU1_REACH_POS_3+1; - - m=diff%BOCU1_TRAIL_COUNT; - diff/=BOCU1_TRAIL_COUNT; - result=BOCU1_TRAIL_TO_BYTE(m); - - m=diff%BOCU1_TRAIL_COUNT; - diff/=BOCU1_TRAIL_COUNT; - result|=BOCU1_TRAIL_TO_BYTE(m)<<8; - - /* - * We know that / and % would deliver quotient 0 and rest=diff. - * Avoid division and modulo for performance. - */ - result|=BOCU1_TRAIL_TO_BYTE(diff)<<16; - - result|=((uint32_t)BOCU1_START_POS_4)<<24; - } - } else { - /* two- to four-byte negative differences */ - if(diff>=BOCU1_REACH_NEG_2) { - /* two bytes */ - diff-=BOCU1_REACH_NEG_1; - result=0x02000000; - - NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); - result|=BOCU1_TRAIL_TO_BYTE(m); - - result|=(BOCU1_START_NEG_2+diff)<<8; - } else if(diff>=BOCU1_REACH_NEG_3) { - /* three bytes */ - diff-=BOCU1_REACH_NEG_2; - result=0x03000000; - - NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); - result|=BOCU1_TRAIL_TO_BYTE(m); - - NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); - result|=BOCU1_TRAIL_TO_BYTE(m)<<8; - - result|=(BOCU1_START_NEG_3+diff)<<16; - } else { - /* four bytes */ - diff-=BOCU1_REACH_NEG_3; - - NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); - result=BOCU1_TRAIL_TO_BYTE(m); - - NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); - result|=BOCU1_TRAIL_TO_BYTE(m)<<8; - - /* - * We know that NEGDIVMOD would deliver - * quotient -1 and rest=diff+BOCU1_TRAIL_COUNT. - * Avoid division and modulo for performance. - */ - m=diff+BOCU1_TRAIL_COUNT; - result|=BOCU1_TRAIL_TO_BYTE(m)<<16; - - result|=BOCU1_MIN<<24; - } - } - return result; -} - - -static void U_CALLCONV -_Bocu1FromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const UChar *source, *sourceLimit; - uint8_t *target; - int32_t targetCapacity; - int32_t *offsets; - - int32_t prev, c, diff; - - int32_t sourceIndex, nextSourceIndex; - - /* set up the local pointers */ - cnv=pArgs->converter; - source=pArgs->source; - sourceLimit=pArgs->sourceLimit; - target=(uint8_t *)pArgs->target; - targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); - offsets=pArgs->offsets; - - /* get the converter state from UConverter */ - c=cnv->fromUChar32; - prev=(int32_t)cnv->fromUnicodeStatus; - if(prev==0) { - prev=BOCU1_ASCII_PREV; - } - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex= c==0 ? 0 : -1; - nextSourceIndex=0; - - /* conversion loop */ - if(c!=0 && targetCapacity>0) { - goto getTrail; - } - -fastSingle: - /* fast loop for single-byte differences */ - /* use only one loop counter variable, targetCapacity, not also source */ - diff=(int32_t)(sourceLimit-source); - if(targetCapacity>diff) { - targetCapacity=diff; - } - while(targetCapacity>0 && (c=*source)<0x3000) { - if(c<=0x20) { - if(c!=0x20) { - prev=BOCU1_ASCII_PREV; - } - *target++=(uint8_t)c; - *offsets++=nextSourceIndex++; - ++source; - --targetCapacity; - } else { - diff=c-prev; - if(DIFF_IS_SINGLE(diff)) { - prev=BOCU1_SIMPLE_PREV(c); - *target++=(uint8_t)PACK_SINGLE_DIFF(diff); - *offsets++=nextSourceIndex++; - ++source; - --targetCapacity; - } else { - break; - } - } - } - /* restore real values */ - targetCapacity=(int32_t)((const uint8_t *)pArgs->targetLimit-target); - sourceIndex=nextSourceIndex; /* wrong if offsets==NULL but does not matter */ - - /* regular loop for all cases */ - while(source0) { - c=*source++; - ++nextSourceIndex; - - if(c<=0x20) { - /* - * ISO C0 control & space: - * Encode directly for MIME compatibility, - * and reset state except for space, to not disrupt compression. - */ - if(c!=0x20) { - prev=BOCU1_ASCII_PREV; - } - *target++=(uint8_t)c; - *offsets++=sourceIndex; - --targetCapacity; - - sourceIndex=nextSourceIndex; - continue; - } - - if(U16_IS_LEAD(c)) { -getTrail: - if(source=0) { - diff-=BOCU1_REACH_POS_1+1; - m=diff%BOCU1_TRAIL_COUNT; - diff/=BOCU1_TRAIL_COUNT; - diff+=BOCU1_START_POS_2; - } else { - diff-=BOCU1_REACH_NEG_1; - NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); - diff+=BOCU1_START_NEG_2; - } - *target++=(uint8_t)diff; - *target++=(uint8_t)BOCU1_TRAIL_TO_BYTE(m); - *offsets++=sourceIndex; - *offsets++=sourceIndex; - targetCapacity-=2; - sourceIndex=nextSourceIndex; - } else { - int32_t length; /* will be 2..4 */ - - diff=packDiff(diff); - length=BOCU1_LENGTH_FROM_PACKED(diff); - - /* write the output character bytes from diff and length */ - /* from the first if in the loop we know that targetCapacity>0 */ - if(length<=targetCapacity) { - switch(length) { - /* each branch falls through to the next one */ - case 4: - *target++=(uint8_t)(diff>>24); - *offsets++=sourceIndex; - U_FALLTHROUGH; - case 3: - *target++=(uint8_t)(diff>>16); - *offsets++=sourceIndex; - U_FALLTHROUGH; - case 2: - *target++=(uint8_t)(diff>>8); - *offsets++=sourceIndex; - /* case 1: handled above */ - *target++=(uint8_t)diff; - *offsets++=sourceIndex; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - targetCapacity-=length; - sourceIndex=nextSourceIndex; - } else { - uint8_t *charErrorBuffer; - - /* - * We actually do this backwards here: - * In order to save an intermediate variable, we output - * first to the overflow buffer what does not fit into the - * regular target. - */ - /* we know that 1<=targetCapacitycharErrorBuffer; - switch(length) { - /* each branch falls through to the next one */ - case 3: - *charErrorBuffer++=(uint8_t)(diff>>16); - U_FALLTHROUGH; - case 2: - *charErrorBuffer++=(uint8_t)(diff>>8); - U_FALLTHROUGH; - case 1: - *charErrorBuffer=(uint8_t)diff; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - cnv->charErrorBufferLength=(int8_t)length; - - /* now output what fits into the regular target */ - diff>>=8*length; /* length was reduced by targetCapacity */ - switch(targetCapacity) { - /* each branch falls through to the next one */ - case 3: - *target++=(uint8_t)(diff>>16); - *offsets++=sourceIndex; - U_FALLTHROUGH; - case 2: - *target++=(uint8_t)(diff>>8); - *offsets++=sourceIndex; - U_FALLTHROUGH; - case 1: - *target++=(uint8_t)diff; - *offsets++=sourceIndex; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - - /* target overflow */ - targetCapacity=0; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - } else { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - - /* set the converter state back into UConverter */ - cnv->fromUChar32= c<0 ? -c : 0; - cnv->fromUnicodeStatus=(uint32_t)prev; - - /* write back the updated pointers */ - pArgs->source=source; - pArgs->target=(char *)target; - pArgs->offsets=offsets; -} - -/* - * Identical to _Bocu1FromUnicodeWithOffsets but without offset handling. - * If a change is made in the original function, then either - * change this function the same way or - * re-copy the original function and remove the variables - * offsets, sourceIndex, and nextSourceIndex. - */ -static void U_CALLCONV -_Bocu1FromUnicode(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const UChar *source, *sourceLimit; - uint8_t *target; - int32_t targetCapacity; - - int32_t prev, c, diff; - - /* set up the local pointers */ - cnv=pArgs->converter; - source=pArgs->source; - sourceLimit=pArgs->sourceLimit; - target=(uint8_t *)pArgs->target; - targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); - - /* get the converter state from UConverter */ - c=cnv->fromUChar32; - prev=(int32_t)cnv->fromUnicodeStatus; - if(prev==0) { - prev=BOCU1_ASCII_PREV; - } - - /* conversion loop */ - if(c!=0 && targetCapacity>0) { - goto getTrail; - } - -fastSingle: - /* fast loop for single-byte differences */ - /* use only one loop counter variable, targetCapacity, not also source */ - diff=(int32_t)(sourceLimit-source); - if(targetCapacity>diff) { - targetCapacity=diff; - } - while(targetCapacity>0 && (c=*source)<0x3000) { - if(c<=0x20) { - if(c!=0x20) { - prev=BOCU1_ASCII_PREV; - } - *target++=(uint8_t)c; - } else { - diff=c-prev; - if(DIFF_IS_SINGLE(diff)) { - prev=BOCU1_SIMPLE_PREV(c); - *target++=(uint8_t)PACK_SINGLE_DIFF(diff); - } else { - break; - } - } - ++source; - --targetCapacity; - } - /* restore real values */ - targetCapacity=(int32_t)((const uint8_t *)pArgs->targetLimit-target); - - /* regular loop for all cases */ - while(source0) { - c=*source++; - - if(c<=0x20) { - /* - * ISO C0 control & space: - * Encode directly for MIME compatibility, - * and reset state except for space, to not disrupt compression. - */ - if(c!=0x20) { - prev=BOCU1_ASCII_PREV; - } - *target++=(uint8_t)c; - --targetCapacity; - continue; - } - - if(U16_IS_LEAD(c)) { -getTrail: - if(source=0) { - diff-=BOCU1_REACH_POS_1+1; - m=diff%BOCU1_TRAIL_COUNT; - diff/=BOCU1_TRAIL_COUNT; - diff+=BOCU1_START_POS_2; - } else { - diff-=BOCU1_REACH_NEG_1; - NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); - diff+=BOCU1_START_NEG_2; - } - *target++=(uint8_t)diff; - *target++=(uint8_t)BOCU1_TRAIL_TO_BYTE(m); - targetCapacity-=2; - } else { - int32_t length; /* will be 2..4 */ - - diff=packDiff(diff); - length=BOCU1_LENGTH_FROM_PACKED(diff); - - /* write the output character bytes from diff and length */ - /* from the first if in the loop we know that targetCapacity>0 */ - if(length<=targetCapacity) { - switch(length) { - /* each branch falls through to the next one */ - case 4: - *target++=(uint8_t)(diff>>24); - U_FALLTHROUGH; - case 3: - *target++=(uint8_t)(diff>>16); - /* case 2: handled above */ - *target++=(uint8_t)(diff>>8); - /* case 1: handled above */ - *target++=(uint8_t)diff; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - targetCapacity-=length; - } else { - uint8_t *charErrorBuffer; - - /* - * We actually do this backwards here: - * In order to save an intermediate variable, we output - * first to the overflow buffer what does not fit into the - * regular target. - */ - /* we know that 1<=targetCapacitycharErrorBuffer; - switch(length) { - /* each branch falls through to the next one */ - case 3: - *charErrorBuffer++=(uint8_t)(diff>>16); - U_FALLTHROUGH; - case 2: - *charErrorBuffer++=(uint8_t)(diff>>8); - U_FALLTHROUGH; - case 1: - *charErrorBuffer=(uint8_t)diff; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - cnv->charErrorBufferLength=(int8_t)length; - - /* now output what fits into the regular target */ - diff>>=8*length; /* length was reduced by targetCapacity */ - switch(targetCapacity) { - /* each branch falls through to the next one */ - case 3: - *target++=(uint8_t)(diff>>16); - U_FALLTHROUGH; - case 2: - *target++=(uint8_t)(diff>>8); - U_FALLTHROUGH; - case 1: - *target++=(uint8_t)diff; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - - /* target overflow */ - targetCapacity=0; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - } else { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - - /* set the converter state back into UConverter */ - cnv->fromUChar32= c<0 ? -c : 0; - cnv->fromUnicodeStatus=(uint32_t)prev; - - /* write back the updated pointers */ - pArgs->source=source; - pArgs->target=(char *)target; -} - -/* BOCU-1-to-Unicode conversion functions ----------------------------------- */ - -/** - * Function for BOCU-1 decoder; handles multi-byte lead bytes. - * - * @param b lead byte; - * BOCU1_MIN<=b=BOCU1_START_NEG_2) { - /* positive difference */ - if(b=BOCU1_START_NEG_3) { - /* two bytes */ - diff=((int32_t)b-BOCU1_START_NEG_2)*BOCU1_TRAIL_COUNT+BOCU1_REACH_NEG_1; - count=1; - } else if(b>BOCU1_MIN) { - /* three bytes */ - diff=((int32_t)b-BOCU1_START_NEG_3)*BOCU1_TRAIL_COUNT*BOCU1_TRAIL_COUNT+BOCU1_REACH_NEG_2; - count=2; - } else { - /* four bytes */ - diff=-BOCU1_TRAIL_COUNT*BOCU1_TRAIL_COUNT*BOCU1_TRAIL_COUNT+BOCU1_REACH_NEG_3; - count=3; - } - } - - /* return the state for decoding the trail byte(s) */ - return (diff<<2)|count; -} - -/** - * Function for BOCU-1 decoder; handles multi-byte trail bytes. - * - * @param count number of remaining trail bytes including this one - * @param b trail byte - * @return new delta for diff including b - <0 indicates an error - * - * @see decodeBocu1 - */ -static inline int32_t -decodeBocu1TrailByte(int32_t count, int32_t b) { - if(b<=0x20) { - /* skip some C0 controls and make the trail byte range contiguous */ - b=bocu1ByteToTrail[b]; - /* b<0 for an illegal trail byte value will result in return<0 below */ -#if BOCU1_MAX_TRAIL<0xff - } else if(b>BOCU1_MAX_TRAIL) { - return -99; -#endif - } else { - b-=BOCU1_TRAIL_BYTE_OFFSET; - } - - /* add trail byte into difference and decrement count */ - if(count==1) { - return b; - } else if(count==2) { - return b*BOCU1_TRAIL_COUNT; - } else /* count==3 */ { - return b*(BOCU1_TRAIL_COUNT*BOCU1_TRAIL_COUNT); - } -} - -static void U_CALLCONV -_Bocu1ToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const uint8_t *source, *sourceLimit; - UChar *target; - const UChar *targetLimit; - int32_t *offsets; - - int32_t prev, count, diff, c; - - int8_t byteIndex; - uint8_t *bytes; - - int32_t sourceIndex, nextSourceIndex; - - /* set up the local pointers */ - cnv=pArgs->converter; - source=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - target=pArgs->target; - targetLimit=pArgs->targetLimit; - offsets=pArgs->offsets; - - /* get the converter state from UConverter */ - prev=(int32_t)cnv->toUnicodeStatus; - if(prev==0) { - prev=BOCU1_ASCII_PREV; - } - diff=cnv->mode; /* mode may be set to UCNV_SI by ucnv_bld.c but then toULength==0 */ - count=diff&3; - diff>>=2; - - byteIndex=cnv->toULength; - bytes=cnv->toUBytes; - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex=byteIndex==0 ? 0 : -1; - nextSourceIndex=0; - - /* conversion "loop" similar to _SCSUToUnicodeWithOffsets() */ - if(count>0 && byteIndex>0 && targettargetLimit-target); - if(count>diff) { - count=diff; - } - while(count>0) { - if(BOCU1_START_NEG_2<=(c=*source) && c=targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - - ++nextSourceIndex; - c=*source++; - if(BOCU1_START_NEG_2<=c && c=BOCU1_MIDDLE) { - diff=((int32_t)c-BOCU1_START_POS_2)*BOCU1_TRAIL_COUNT+BOCU1_REACH_POS_1+1; - } else { - diff=((int32_t)c-BOCU1_START_NEG_2)*BOCU1_TRAIL_COUNT+BOCU1_REACH_NEG_1; - } - - /* trail byte */ - ++nextSourceIndex; - c=decodeBocu1TrailByte(1, *source++); - if(c<0 || (uint32_t)(c=prev+diff+c)>0x10ffff) { - bytes[0]=source[-2]; - bytes[1]=source[-1]; - byteIndex=2; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } - } else if(c==BOCU1_RESET) { - /* only reset the state, no code point */ - prev=BOCU1_ASCII_PREV; - sourceIndex=nextSourceIndex; - continue; - } else { - /* - * For multi-byte difference lead bytes, set the decoder state - * with the partial difference value from the lead byte and - * with the number of trail bytes. - */ - bytes[0]=(uint8_t)c; - byteIndex=1; - - diff=decodeBocu1LeadByte(c); - count=diff&3; - diff>>=2; -getTrail: - for(;;) { - if(source>=sourceLimit) { - goto endloop; - } - ++nextSourceIndex; - c=bytes[byteIndex++]=*source++; - - /* trail byte in any position */ - c=decodeBocu1TrailByte(count, c); - if(c<0) { - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - goto endloop; - } - - diff+=c; - if(--count==0) { - /* final trail byte, deliver a code point */ - byteIndex=0; - c=prev+diff; - if((uint32_t)c>0x10ffff) { - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - goto endloop; - } - break; - } - } - } - - /* calculate the next prev and output c */ - prev=BOCU1_PREV(c); - if(c<=0xffff) { - *target++=(UChar)c; - *offsets++=sourceIndex; - } else { - /* output surrogate pair */ - *target++=U16_LEAD(c); - if(targetUCharErrorBuffer[0]=U16_TRAIL(c); - cnv->UCharErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - sourceIndex=nextSourceIndex; - } -endloop: - - if(*pErrorCode==U_ILLEGAL_CHAR_FOUND) { - /* set the converter state in UConverter to deal with the next character */ - cnv->toUnicodeStatus=BOCU1_ASCII_PREV; - cnv->mode=0; - } else { - /* set the converter state back into UConverter */ - cnv->toUnicodeStatus=(uint32_t)prev; - cnv->mode=(diff<<2)|count; - } - cnv->toULength=byteIndex; - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - pArgs->offsets=offsets; - return; -} - -/* - * Identical to _Bocu1ToUnicodeWithOffsets but without offset handling. - * If a change is made in the original function, then either - * change this function the same way or - * re-copy the original function and remove the variables - * offsets, sourceIndex, and nextSourceIndex. - */ -static void U_CALLCONV -_Bocu1ToUnicode(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const uint8_t *source, *sourceLimit; - UChar *target; - const UChar *targetLimit; - - int32_t prev, count, diff, c; - - int8_t byteIndex; - uint8_t *bytes; - - /* set up the local pointers */ - cnv=pArgs->converter; - source=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - target=pArgs->target; - targetLimit=pArgs->targetLimit; - - /* get the converter state from UConverter */ - prev=(int32_t)cnv->toUnicodeStatus; - if(prev==0) { - prev=BOCU1_ASCII_PREV; - } - diff=cnv->mode; /* mode may be set to UCNV_SI by ucnv_bld.c but then toULength==0 */ - count=diff&3; - diff>>=2; - - byteIndex=cnv->toULength; - bytes=cnv->toUBytes; - - /* conversion "loop" similar to _SCSUToUnicodeWithOffsets() */ - if(count>0 && byteIndex>0 && targettargetLimit-target); - if(count>diff) { - count=diff; - } - while(count>0) { - if(BOCU1_START_NEG_2<=(c=*source) && c=targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - - c=*source++; - if(BOCU1_START_NEG_2<=c && c=BOCU1_MIDDLE) { - diff=((int32_t)c-BOCU1_START_POS_2)*BOCU1_TRAIL_COUNT+BOCU1_REACH_POS_1+1; - } else { - diff=((int32_t)c-BOCU1_START_NEG_2)*BOCU1_TRAIL_COUNT+BOCU1_REACH_NEG_1; - } - - /* trail byte */ - c=decodeBocu1TrailByte(1, *source++); - if(c<0 || (uint32_t)(c=prev+diff+c)>0x10ffff) { - bytes[0]=source[-2]; - bytes[1]=source[-1]; - byteIndex=2; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } - } else if(c==BOCU1_RESET) { - /* only reset the state, no code point */ - prev=BOCU1_ASCII_PREV; - continue; - } else { - /* - * For multi-byte difference lead bytes, set the decoder state - * with the partial difference value from the lead byte and - * with the number of trail bytes. - */ - bytes[0]=(uint8_t)c; - byteIndex=1; - - diff=decodeBocu1LeadByte(c); - count=diff&3; - diff>>=2; -getTrail: - for(;;) { - if(source>=sourceLimit) { - goto endloop; - } - c=bytes[byteIndex++]=*source++; - - /* trail byte in any position */ - c=decodeBocu1TrailByte(count, c); - if(c<0) { - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - goto endloop; - } - - diff+=c; - if(--count==0) { - /* final trail byte, deliver a code point */ - byteIndex=0; - c=prev+diff; - if((uint32_t)c>0x10ffff) { - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - goto endloop; - } - break; - } - } - } - - /* calculate the next prev and output c */ - prev=BOCU1_PREV(c); - if(c<=0xffff) { - *target++=(UChar)c; - } else { - /* output surrogate pair */ - *target++=U16_LEAD(c); - if(targetUCharErrorBuffer[0]=U16_TRAIL(c); - cnv->UCharErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - } -endloop: - - if(*pErrorCode==U_ILLEGAL_CHAR_FOUND) { - /* set the converter state in UConverter to deal with the next character */ - cnv->toUnicodeStatus=BOCU1_ASCII_PREV; - cnv->mode=0; - } else { - /* set the converter state back into UConverter */ - cnv->toUnicodeStatus=(uint32_t)prev; - cnv->mode=(diff<<2)|count; - } - cnv->toULength=byteIndex; - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - return; -} - -/* miscellaneous ------------------------------------------------------------ */ - -static const UConverterImpl _Bocu1Impl={ - UCNV_BOCU1, - - NULL, - NULL, - - NULL, - NULL, - NULL, - - _Bocu1ToUnicode, - _Bocu1ToUnicodeWithOffsets, - _Bocu1FromUnicode, - _Bocu1FromUnicodeWithOffsets, - NULL, - - NULL, - NULL, - NULL, - NULL, - ucnv_getCompleteUnicodeSet, - - NULL, - NULL -}; - -static const UConverterStaticData _Bocu1StaticData={ - sizeof(UConverterStaticData), - "BOCU-1", - 1214, /* CCSID for BOCU-1 */ - UCNV_IBM, UCNV_BOCU1, - 1, 4, /* one UChar generates at least 1 byte and at most 4 bytes */ - { 0x1a, 0, 0, 0 }, 1, /* BOCU-1 never needs to write a subchar */ - false, false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - -const UConverterSharedData _Bocu1Data= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_Bocu1StaticData, &_Bocu1Impl); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2002-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ucnvbocu.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002mar27 +* created by: Markus W. Scherer +* +* This is an implementation of the Binary Ordered Compression for Unicode, +* in its MIME-friendly form as defined in http://www.unicode.org/notes/tn6/ +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/ucnv_cb.h" +#include "unicode/utf16.h" +#include "putilimp.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" +#include "uassert.h" + +/* BOCU-1 constants and macros ---------------------------------------------- */ + +/* + * BOCU-1 encodes the code points of a Unicode string as + * a sequence of byte-encoded differences (slope detection), + * preserving lexical order. + * + * Optimize the difference-taking for runs of Unicode text within + * small scripts: + * + * Most small scripts are allocated within aligned 128-blocks of Unicode + * code points. Lexical order is preserved if the "previous code point" state + * is always moved into the middle of such a block. + * + * Additionally, "prev" is moved from anywhere in the Unihan and Hangul + * areas into the middle of those areas. + * + * C0 control codes and space are encoded with their US-ASCII bytes. + * "prev" is reset for C0 controls but not for space. + */ + +/* initial value for "prev": middle of the ASCII range */ +#define BOCU1_ASCII_PREV 0x40 + +/* bounding byte values for differences */ +#define BOCU1_MIN 0x21 +#define BOCU1_MIDDLE 0x90 +#define BOCU1_MAX_LEAD 0xfe +#define BOCU1_MAX_TRAIL 0xff +#define BOCU1_RESET 0xff + +/* number of lead bytes */ +#define BOCU1_COUNT (BOCU1_MAX_LEAD-BOCU1_MIN+1) + +/* adjust trail byte counts for the use of some C0 control byte values */ +#define BOCU1_TRAIL_CONTROLS_COUNT 20 +#define BOCU1_TRAIL_BYTE_OFFSET (BOCU1_MIN-BOCU1_TRAIL_CONTROLS_COUNT) + +/* number of trail bytes */ +#define BOCU1_TRAIL_COUNT ((BOCU1_MAX_TRAIL-BOCU1_MIN+1)+BOCU1_TRAIL_CONTROLS_COUNT) + +/* + * number of positive and negative single-byte codes + * (counting 0==BOCU1_MIDDLE among the positive ones) + */ +#define BOCU1_SINGLE 64 + +/* number of lead bytes for positive and negative 2/3/4-byte sequences */ +#define BOCU1_LEAD_2 43 +#define BOCU1_LEAD_3 3 +#define BOCU1_LEAD_4 1 + +/* The difference value range for single-byters. */ +#define BOCU1_REACH_POS_1 (BOCU1_SINGLE-1) +#define BOCU1_REACH_NEG_1 (-BOCU1_SINGLE) + +/* The difference value range for double-byters. */ +#define BOCU1_REACH_POS_2 (BOCU1_REACH_POS_1+BOCU1_LEAD_2*BOCU1_TRAIL_COUNT) +#define BOCU1_REACH_NEG_2 (BOCU1_REACH_NEG_1-BOCU1_LEAD_2*BOCU1_TRAIL_COUNT) + +/* The difference value range for 3-byters. */ +#define BOCU1_REACH_POS_3 \ + (BOCU1_REACH_POS_2+BOCU1_LEAD_3*BOCU1_TRAIL_COUNT*BOCU1_TRAIL_COUNT) + +#define BOCU1_REACH_NEG_3 (BOCU1_REACH_NEG_2-BOCU1_LEAD_3*BOCU1_TRAIL_COUNT*BOCU1_TRAIL_COUNT) + +/* The lead byte start values. */ +#define BOCU1_START_POS_2 (BOCU1_MIDDLE+BOCU1_REACH_POS_1+1) +#define BOCU1_START_POS_3 (BOCU1_START_POS_2+BOCU1_LEAD_2) +#define BOCU1_START_POS_4 (BOCU1_START_POS_3+BOCU1_LEAD_3) + /* ==BOCU1_MAX_LEAD */ + +#define BOCU1_START_NEG_2 (BOCU1_MIDDLE+BOCU1_REACH_NEG_1) +#define BOCU1_START_NEG_3 (BOCU1_START_NEG_2-BOCU1_LEAD_2) +#define BOCU1_START_NEG_4 (BOCU1_START_NEG_3-BOCU1_LEAD_3) + /* ==BOCU1_MIN+1 */ + +/* The length of a byte sequence, according to the lead byte (!=BOCU1_RESET). */ +#define BOCU1_LENGTH_FROM_LEAD(lead) \ + ((BOCU1_START_NEG_2<=(lead) && (lead)>24 : 4) + +/* + * 12 commonly used C0 control codes (and space) are only used to encode + * themselves directly, + * which makes BOCU-1 MIME-usable and reasonably safe for + * ASCII-oriented software. + * + * These controls are + * 0 NUL + * + * 7 BEL + * 8 BS + * + * 9 TAB + * a LF + * b VT + * c FF + * d CR + * + * e SO + * f SI + * + * 1a SUB + * 1b ESC + * + * The other 20 C0 controls are also encoded directly (to preserve order) + * but are also used as trail bytes in difference encoding + * (for better compression). + */ +#define BOCU1_TRAIL_TO_BYTE(t) ((t)>=BOCU1_TRAIL_CONTROLS_COUNT ? (t)+BOCU1_TRAIL_BYTE_OFFSET : bocu1TrailToByte[t]) + +/* + * Byte value map for control codes, + * from external byte values 0x00..0x20 + * to trail byte values 0..19 (0..0x13) as used in the difference calculation. + * External byte values that are illegal as trail bytes are mapped to -1. + */ +static const int8_t +bocu1ByteToTrail[BOCU1_MIN]={ +/* 0 1 2 3 4 5 6 7 */ + -1, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, -1, + +/* 8 9 a b c d e f */ + -1, -1, -1, -1, -1, -1, -1, -1, + +/* 10 11 12 13 14 15 16 17 */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, + +/* 18 19 1a 1b 1c 1d 1e 1f */ + 0x0e, 0x0f, -1, -1, 0x10, 0x11, 0x12, 0x13, + +/* 20 */ + -1 +}; + +/* + * Byte value map for control codes, + * from trail byte values 0..19 (0..0x13) as used in the difference calculation + * to external byte values 0x00..0x20. + */ +static const int8_t +bocu1TrailToByte[BOCU1_TRAIL_CONTROLS_COUNT]={ +/* 0 1 2 3 4 5 6 7 */ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x10, 0x11, + +/* 8 9 a b c d e f */ + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + +/* 10 11 12 13 */ + 0x1c, 0x1d, 0x1e, 0x1f +}; + +/** + * Integer division and modulo with negative numerators + * yields negative modulo results and quotients that are one more than + * what we need here. + * This macro adjust the results so that the modulo-value m is always >=0. + * + * For positive n, the if() condition is always false. + * + * @param n Number to be split into quotient and rest. + * Will be modified to contain the quotient. + * @param d Divisor. + * @param m Output variable for the rest (modulo result). + */ +#define NEGDIVMOD(n, d, m) UPRV_BLOCK_MACRO_BEGIN { \ + (m)=(n)%(d); \ + (n)/=(d); \ + if((m)<0) { \ + --(n); \ + (m)+=(d); \ + } \ +} UPRV_BLOCK_MACRO_END + +/* Faster versions of packDiff() for single-byte-encoded diff values. */ + +/** Is a diff value encodable in a single byte? */ +#define DIFF_IS_SINGLE(diff) (BOCU1_REACH_NEG_1<=(diff) && (diff)<=BOCU1_REACH_POS_1) + +/** Encode a diff value in a single byte. */ +#define PACK_SINGLE_DIFF(diff) (BOCU1_MIDDLE+(diff)) + +/** Is a diff value encodable in two bytes? */ +#define DIFF_IS_DOUBLE(diff) (BOCU1_REACH_NEG_2<=(diff) && (diff)<=BOCU1_REACH_POS_2) + +/* BOCU-1 implementation functions ------------------------------------------ */ + +#define BOCU1_SIMPLE_PREV(c) (((c)&~0x7f)+BOCU1_ASCII_PREV) + +/** + * Compute the next "previous" value for differencing + * from the current code point. + * + * @param c current code point, 0x3040..0xd7a3 (rest handled by macro below) + * @return "previous code point" state value + */ +static inline int32_t +bocu1Prev(int32_t c) { + /* compute new prev */ + if(/* 0x3040<=c && */ c<=0x309f) { + /* Hiragana is not 128-aligned */ + return 0x3070; + } else if(0x4e00<=c && c<=0x9fa5) { + /* CJK Unihan */ + return 0x4e00-BOCU1_REACH_NEG_2; + } else if(0xac00<=c /* && c<=0xd7a3 */) { + /* Korean Hangul */ + return (0xd7a3+0xac00)/2; + } else { + /* mostly small scripts */ + return BOCU1_SIMPLE_PREV(c); + } +} + +/** Fast version of bocu1Prev() for most scripts. */ +#define BOCU1_PREV(c) ((c)<0x3040 || (c)>0xd7a3 ? BOCU1_SIMPLE_PREV(c) : bocu1Prev(c)) + +/* + * The BOCU-1 converter uses the standard setup code in ucnv.c/ucnv_bld.c. + * The UConverter fields are used as follows: + * + * fromUnicodeStatus encoder's prev (0 will be interpreted as BOCU1_ASCII_PREV) + * + * toUnicodeStatus decoder's prev (0 will be interpreted as BOCU1_ASCII_PREV) + * mode decoder's incomplete (diff<<2)|count (ignored when toULength==0) + */ + +/* BOCU-1-from-Unicode conversion functions --------------------------------- */ + +/** + * Encode a difference -0x10ffff..0x10ffff in 1..4 bytes + * and return a packed integer with them. + * + * The encoding favors small absolute differences with short encodings + * to compress runs of same-script characters. + * + * Optimized version with unrolled loops and fewer floating-point operations + * than the standard packDiff(). + * + * @param diff difference value -0x10ffff..0x10ffff + * @return + * 0x010000zz for 1-byte sequence zz + * 0x0200yyzz for 2-byte sequence yy zz + * 0x03xxyyzz for 3-byte sequence xx yy zz + * 0xwwxxyyzz for 4-byte sequence ww xx yy zz (ww>0x03) + */ +static int32_t +packDiff(int32_t diff) { + int32_t result, m; + + U_ASSERT(!DIFF_IS_SINGLE(diff)); /* assume we won't be called where diff==BOCU1_REACH_NEG_1=-64 */ + if(diff>=BOCU1_REACH_NEG_1) { + /* mostly positive differences, and single-byte negative ones */ +#if 0 /* single-byte case handled in macros, see below */ + if(diff<=BOCU1_REACH_POS_1) { + /* single byte */ + return 0x01000000|(BOCU1_MIDDLE+diff); + } else +#endif + if(diff<=BOCU1_REACH_POS_2) { + /* two bytes */ + diff-=BOCU1_REACH_POS_1+1; + result=0x02000000; + + m=diff%BOCU1_TRAIL_COUNT; + diff/=BOCU1_TRAIL_COUNT; + result|=BOCU1_TRAIL_TO_BYTE(m); + + result|=(BOCU1_START_POS_2+diff)<<8; + } else if(diff<=BOCU1_REACH_POS_3) { + /* three bytes */ + diff-=BOCU1_REACH_POS_2+1; + result=0x03000000; + + m=diff%BOCU1_TRAIL_COUNT; + diff/=BOCU1_TRAIL_COUNT; + result|=BOCU1_TRAIL_TO_BYTE(m); + + m=diff%BOCU1_TRAIL_COUNT; + diff/=BOCU1_TRAIL_COUNT; + result|=BOCU1_TRAIL_TO_BYTE(m)<<8; + + result|=(BOCU1_START_POS_3+diff)<<16; + } else { + /* four bytes */ + diff-=BOCU1_REACH_POS_3+1; + + m=diff%BOCU1_TRAIL_COUNT; + diff/=BOCU1_TRAIL_COUNT; + result=BOCU1_TRAIL_TO_BYTE(m); + + m=diff%BOCU1_TRAIL_COUNT; + diff/=BOCU1_TRAIL_COUNT; + result|=BOCU1_TRAIL_TO_BYTE(m)<<8; + + /* + * We know that / and % would deliver quotient 0 and rest=diff. + * Avoid division and modulo for performance. + */ + result|=BOCU1_TRAIL_TO_BYTE(diff)<<16; + + result|=((uint32_t)BOCU1_START_POS_4)<<24; + } + } else { + /* two- to four-byte negative differences */ + if(diff>=BOCU1_REACH_NEG_2) { + /* two bytes */ + diff-=BOCU1_REACH_NEG_1; + result=0x02000000; + + NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); + result|=BOCU1_TRAIL_TO_BYTE(m); + + result|=(BOCU1_START_NEG_2+diff)<<8; + } else if(diff>=BOCU1_REACH_NEG_3) { + /* three bytes */ + diff-=BOCU1_REACH_NEG_2; + result=0x03000000; + + NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); + result|=BOCU1_TRAIL_TO_BYTE(m); + + NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); + result|=BOCU1_TRAIL_TO_BYTE(m)<<8; + + result|=(BOCU1_START_NEG_3+diff)<<16; + } else { + /* four bytes */ + diff-=BOCU1_REACH_NEG_3; + + NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); + result=BOCU1_TRAIL_TO_BYTE(m); + + NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); + result|=BOCU1_TRAIL_TO_BYTE(m)<<8; + + /* + * We know that NEGDIVMOD would deliver + * quotient -1 and rest=diff+BOCU1_TRAIL_COUNT. + * Avoid division and modulo for performance. + */ + m=diff+BOCU1_TRAIL_COUNT; + result|=BOCU1_TRAIL_TO_BYTE(m)<<16; + + result|=BOCU1_MIN<<24; + } + } + return result; +} + + +static void U_CALLCONV +_Bocu1FromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const char16_t *source, *sourceLimit; + uint8_t *target; + int32_t targetCapacity; + int32_t *offsets; + + int32_t prev, c, diff; + + int32_t sourceIndex, nextSourceIndex; + + /* set up the local pointers */ + cnv=pArgs->converter; + source=pArgs->source; + sourceLimit=pArgs->sourceLimit; + target=(uint8_t *)pArgs->target; + targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); + offsets=pArgs->offsets; + + /* get the converter state from UConverter */ + c=cnv->fromUChar32; + prev=(int32_t)cnv->fromUnicodeStatus; + if(prev==0) { + prev=BOCU1_ASCII_PREV; + } + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex= c==0 ? 0 : -1; + nextSourceIndex=0; + + /* conversion loop */ + if(c!=0 && targetCapacity>0) { + goto getTrail; + } + +fastSingle: + /* fast loop for single-byte differences */ + /* use only one loop counter variable, targetCapacity, not also source */ + diff=(int32_t)(sourceLimit-source); + if(targetCapacity>diff) { + targetCapacity=diff; + } + while(targetCapacity>0 && (c=*source)<0x3000) { + if(c<=0x20) { + if(c!=0x20) { + prev=BOCU1_ASCII_PREV; + } + *target++=(uint8_t)c; + *offsets++=nextSourceIndex++; + ++source; + --targetCapacity; + } else { + diff=c-prev; + if(DIFF_IS_SINGLE(diff)) { + prev=BOCU1_SIMPLE_PREV(c); + *target++=(uint8_t)PACK_SINGLE_DIFF(diff); + *offsets++=nextSourceIndex++; + ++source; + --targetCapacity; + } else { + break; + } + } + } + /* restore real values */ + targetCapacity=(int32_t)((const uint8_t *)pArgs->targetLimit-target); + sourceIndex=nextSourceIndex; /* wrong if offsets==nullptr but does not matter */ + + /* regular loop for all cases */ + while(source0) { + c=*source++; + ++nextSourceIndex; + + if(c<=0x20) { + /* + * ISO C0 control & space: + * Encode directly for MIME compatibility, + * and reset state except for space, to not disrupt compression. + */ + if(c!=0x20) { + prev=BOCU1_ASCII_PREV; + } + *target++=(uint8_t)c; + *offsets++=sourceIndex; + --targetCapacity; + + sourceIndex=nextSourceIndex; + continue; + } + + if(U16_IS_LEAD(c)) { +getTrail: + if(source=0) { + diff-=BOCU1_REACH_POS_1+1; + m=diff%BOCU1_TRAIL_COUNT; + diff/=BOCU1_TRAIL_COUNT; + diff+=BOCU1_START_POS_2; + } else { + diff-=BOCU1_REACH_NEG_1; + NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); + diff+=BOCU1_START_NEG_2; + } + *target++=(uint8_t)diff; + *target++=(uint8_t)BOCU1_TRAIL_TO_BYTE(m); + *offsets++=sourceIndex; + *offsets++=sourceIndex; + targetCapacity-=2; + sourceIndex=nextSourceIndex; + } else { + int32_t length; /* will be 2..4 */ + + diff=packDiff(diff); + length=BOCU1_LENGTH_FROM_PACKED(diff); + + /* write the output character bytes from diff and length */ + /* from the first if in the loop we know that targetCapacity>0 */ + if(length<=targetCapacity) { + switch(length) { + /* each branch falls through to the next one */ + case 4: + *target++=(uint8_t)(diff>>24); + *offsets++=sourceIndex; + U_FALLTHROUGH; + case 3: + *target++=(uint8_t)(diff>>16); + *offsets++=sourceIndex; + U_FALLTHROUGH; + case 2: + *target++=(uint8_t)(diff>>8); + *offsets++=sourceIndex; + /* case 1: handled above */ + *target++=(uint8_t)diff; + *offsets++=sourceIndex; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + targetCapacity-=length; + sourceIndex=nextSourceIndex; + } else { + uint8_t *charErrorBuffer; + + /* + * We actually do this backwards here: + * In order to save an intermediate variable, we output + * first to the overflow buffer what does not fit into the + * regular target. + */ + /* we know that 1<=targetCapacitycharErrorBuffer; + switch(length) { + /* each branch falls through to the next one */ + case 3: + *charErrorBuffer++=(uint8_t)(diff>>16); + U_FALLTHROUGH; + case 2: + *charErrorBuffer++=(uint8_t)(diff>>8); + U_FALLTHROUGH; + case 1: + *charErrorBuffer=(uint8_t)diff; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + cnv->charErrorBufferLength=(int8_t)length; + + /* now output what fits into the regular target */ + diff>>=8*length; /* length was reduced by targetCapacity */ + switch(targetCapacity) { + /* each branch falls through to the next one */ + case 3: + *target++=(uint8_t)(diff>>16); + *offsets++=sourceIndex; + U_FALLTHROUGH; + case 2: + *target++=(uint8_t)(diff>>8); + *offsets++=sourceIndex; + U_FALLTHROUGH; + case 1: + *target++=(uint8_t)diff; + *offsets++=sourceIndex; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + + /* target overflow */ + targetCapacity=0; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + } else { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + + /* set the converter state back into UConverter */ + cnv->fromUChar32= c<0 ? -c : 0; + cnv->fromUnicodeStatus=(uint32_t)prev; + + /* write back the updated pointers */ + pArgs->source=source; + pArgs->target=(char *)target; + pArgs->offsets=offsets; +} + +/* + * Identical to _Bocu1FromUnicodeWithOffsets but without offset handling. + * If a change is made in the original function, then either + * change this function the same way or + * re-copy the original function and remove the variables + * offsets, sourceIndex, and nextSourceIndex. + */ +static void U_CALLCONV +_Bocu1FromUnicode(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const char16_t *source, *sourceLimit; + uint8_t *target; + int32_t targetCapacity; + + int32_t prev, c, diff; + + /* set up the local pointers */ + cnv=pArgs->converter; + source=pArgs->source; + sourceLimit=pArgs->sourceLimit; + target=(uint8_t *)pArgs->target; + targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); + + /* get the converter state from UConverter */ + c=cnv->fromUChar32; + prev=(int32_t)cnv->fromUnicodeStatus; + if(prev==0) { + prev=BOCU1_ASCII_PREV; + } + + /* conversion loop */ + if(c!=0 && targetCapacity>0) { + goto getTrail; + } + +fastSingle: + /* fast loop for single-byte differences */ + /* use only one loop counter variable, targetCapacity, not also source */ + diff=(int32_t)(sourceLimit-source); + if(targetCapacity>diff) { + targetCapacity=diff; + } + while(targetCapacity>0 && (c=*source)<0x3000) { + if(c<=0x20) { + if(c!=0x20) { + prev=BOCU1_ASCII_PREV; + } + *target++=(uint8_t)c; + } else { + diff=c-prev; + if(DIFF_IS_SINGLE(diff)) { + prev=BOCU1_SIMPLE_PREV(c); + *target++=(uint8_t)PACK_SINGLE_DIFF(diff); + } else { + break; + } + } + ++source; + --targetCapacity; + } + /* restore real values */ + targetCapacity=(int32_t)((const uint8_t *)pArgs->targetLimit-target); + + /* regular loop for all cases */ + while(source0) { + c=*source++; + + if(c<=0x20) { + /* + * ISO C0 control & space: + * Encode directly for MIME compatibility, + * and reset state except for space, to not disrupt compression. + */ + if(c!=0x20) { + prev=BOCU1_ASCII_PREV; + } + *target++=(uint8_t)c; + --targetCapacity; + continue; + } + + if(U16_IS_LEAD(c)) { +getTrail: + if(source=0) { + diff-=BOCU1_REACH_POS_1+1; + m=diff%BOCU1_TRAIL_COUNT; + diff/=BOCU1_TRAIL_COUNT; + diff+=BOCU1_START_POS_2; + } else { + diff-=BOCU1_REACH_NEG_1; + NEGDIVMOD(diff, BOCU1_TRAIL_COUNT, m); + diff+=BOCU1_START_NEG_2; + } + *target++=(uint8_t)diff; + *target++=(uint8_t)BOCU1_TRAIL_TO_BYTE(m); + targetCapacity-=2; + } else { + int32_t length; /* will be 2..4 */ + + diff=packDiff(diff); + length=BOCU1_LENGTH_FROM_PACKED(diff); + + /* write the output character bytes from diff and length */ + /* from the first if in the loop we know that targetCapacity>0 */ + if(length<=targetCapacity) { + switch(length) { + /* each branch falls through to the next one */ + case 4: + *target++=(uint8_t)(diff>>24); + U_FALLTHROUGH; + case 3: + *target++=(uint8_t)(diff>>16); + /* case 2: handled above */ + *target++=(uint8_t)(diff>>8); + /* case 1: handled above */ + *target++=(uint8_t)diff; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + targetCapacity-=length; + } else { + uint8_t *charErrorBuffer; + + /* + * We actually do this backwards here: + * In order to save an intermediate variable, we output + * first to the overflow buffer what does not fit into the + * regular target. + */ + /* we know that 1<=targetCapacitycharErrorBuffer; + switch(length) { + /* each branch falls through to the next one */ + case 3: + *charErrorBuffer++=(uint8_t)(diff>>16); + U_FALLTHROUGH; + case 2: + *charErrorBuffer++=(uint8_t)(diff>>8); + U_FALLTHROUGH; + case 1: + *charErrorBuffer=(uint8_t)diff; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + cnv->charErrorBufferLength=(int8_t)length; + + /* now output what fits into the regular target */ + diff>>=8*length; /* length was reduced by targetCapacity */ + switch(targetCapacity) { + /* each branch falls through to the next one */ + case 3: + *target++=(uint8_t)(diff>>16); + U_FALLTHROUGH; + case 2: + *target++=(uint8_t)(diff>>8); + U_FALLTHROUGH; + case 1: + *target++=(uint8_t)diff; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + + /* target overflow */ + targetCapacity=0; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + } else { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + + /* set the converter state back into UConverter */ + cnv->fromUChar32= c<0 ? -c : 0; + cnv->fromUnicodeStatus=(uint32_t)prev; + + /* write back the updated pointers */ + pArgs->source=source; + pArgs->target=(char *)target; +} + +/* BOCU-1-to-Unicode conversion functions ----------------------------------- */ + +/** + * Function for BOCU-1 decoder; handles multi-byte lead bytes. + * + * @param b lead byte; + * BOCU1_MIN<=b=BOCU1_START_NEG_2) { + /* positive difference */ + if(b=BOCU1_START_NEG_3) { + /* two bytes */ + diff=((int32_t)b-BOCU1_START_NEG_2)*BOCU1_TRAIL_COUNT+BOCU1_REACH_NEG_1; + count=1; + } else if(b>BOCU1_MIN) { + /* three bytes */ + diff=((int32_t)b-BOCU1_START_NEG_3)*BOCU1_TRAIL_COUNT*BOCU1_TRAIL_COUNT+BOCU1_REACH_NEG_2; + count=2; + } else { + /* four bytes */ + diff=-BOCU1_TRAIL_COUNT*BOCU1_TRAIL_COUNT*BOCU1_TRAIL_COUNT+BOCU1_REACH_NEG_3; + count=3; + } + } + + /* return the state for decoding the trail byte(s) */ + return ((uint32_t)diff<<2)|count; +} + +/** + * Function for BOCU-1 decoder; handles multi-byte trail bytes. + * + * @param count number of remaining trail bytes including this one + * @param b trail byte + * @return new delta for diff including b - <0 indicates an error + * + * @see decodeBocu1 + */ +static inline int32_t +decodeBocu1TrailByte(int32_t count, int32_t b) { + if(b<=0x20) { + /* skip some C0 controls and make the trail byte range contiguous */ + b=bocu1ByteToTrail[b]; + /* b<0 for an illegal trail byte value will result in return<0 below */ +#if BOCU1_MAX_TRAIL<0xff + } else if(b>BOCU1_MAX_TRAIL) { + return -99; +#endif + } else { + b-=BOCU1_TRAIL_BYTE_OFFSET; + } + + /* add trail byte into difference and decrement count */ + if(count==1) { + return b; + } else if(count==2) { + return b*BOCU1_TRAIL_COUNT; + } else /* count==3 */ { + return b*(BOCU1_TRAIL_COUNT*BOCU1_TRAIL_COUNT); + } +} + +static void U_CALLCONV +_Bocu1ToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const uint8_t *source, *sourceLimit; + char16_t *target; + const char16_t *targetLimit; + int32_t *offsets; + + int32_t prev, count, diff, c; + + int8_t byteIndex; + uint8_t *bytes; + + int32_t sourceIndex, nextSourceIndex; + + /* set up the local pointers */ + cnv=pArgs->converter; + source=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + target=pArgs->target; + targetLimit=pArgs->targetLimit; + offsets=pArgs->offsets; + + /* get the converter state from UConverter */ + prev=(int32_t)cnv->toUnicodeStatus; + if(prev==0) { + prev=BOCU1_ASCII_PREV; + } + diff=cnv->mode; /* mode may be set to UCNV_SI by ucnv_bld.c but then toULength==0 */ + count=diff&3; + diff>>=2; + + byteIndex=cnv->toULength; + bytes=cnv->toUBytes; + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex=byteIndex==0 ? 0 : -1; + nextSourceIndex=0; + + /* conversion "loop" similar to _SCSUToUnicodeWithOffsets() */ + if(count>0 && byteIndex>0 && targettargetLimit-target); + if(count>diff) { + count=diff; + } + while(count>0) { + if(BOCU1_START_NEG_2<=(c=*source) && c=targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + + ++nextSourceIndex; + c=*source++; + if(BOCU1_START_NEG_2<=c && c=BOCU1_MIDDLE) { + diff=((int32_t)c-BOCU1_START_POS_2)*BOCU1_TRAIL_COUNT+BOCU1_REACH_POS_1+1; + } else { + diff=((int32_t)c-BOCU1_START_NEG_2)*BOCU1_TRAIL_COUNT+BOCU1_REACH_NEG_1; + } + + /* trail byte */ + ++nextSourceIndex; + c=decodeBocu1TrailByte(1, *source++); + if(c<0 || (uint32_t)(c=prev+diff+c)>0x10ffff) { + bytes[0]=source[-2]; + bytes[1]=source[-1]; + byteIndex=2; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } + } else if(c==BOCU1_RESET) { + /* only reset the state, no code point */ + prev=BOCU1_ASCII_PREV; + sourceIndex=nextSourceIndex; + continue; + } else { + /* + * For multi-byte difference lead bytes, set the decoder state + * with the partial difference value from the lead byte and + * with the number of trail bytes. + */ + bytes[0]=(uint8_t)c; + byteIndex=1; + + diff=decodeBocu1LeadByte(c); + count=diff&3; + diff>>=2; +getTrail: + for(;;) { + if(source>=sourceLimit) { + goto endloop; + } + ++nextSourceIndex; + c=bytes[byteIndex++]=*source++; + + /* trail byte in any position */ + c=decodeBocu1TrailByte(count, c); + if(c<0) { + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + goto endloop; + } + + diff+=c; + if(--count==0) { + /* final trail byte, deliver a code point */ + byteIndex=0; + c=prev+diff; + if((uint32_t)c>0x10ffff) { + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + goto endloop; + } + break; + } + } + } + + /* calculate the next prev and output c */ + prev=BOCU1_PREV(c); + if(c<=0xffff) { + *target++=(char16_t)c; + *offsets++=sourceIndex; + } else { + /* output surrogate pair */ + *target++=U16_LEAD(c); + if(targetUCharErrorBuffer[0]=U16_TRAIL(c); + cnv->UCharErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + sourceIndex=nextSourceIndex; + } +endloop: + + if(*pErrorCode==U_ILLEGAL_CHAR_FOUND) { + /* set the converter state in UConverter to deal with the next character */ + cnv->toUnicodeStatus=BOCU1_ASCII_PREV; + cnv->mode=0; + } else { + /* set the converter state back into UConverter */ + cnv->toUnicodeStatus=(uint32_t)prev; + cnv->mode=(int32_t)((uint32_t)diff<<2)|count; + } + cnv->toULength=byteIndex; + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + pArgs->offsets=offsets; + return; +} + +/* + * Identical to _Bocu1ToUnicodeWithOffsets but without offset handling. + * If a change is made in the original function, then either + * change this function the same way or + * re-copy the original function and remove the variables + * offsets, sourceIndex, and nextSourceIndex. + */ +static void U_CALLCONV +_Bocu1ToUnicode(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const uint8_t *source, *sourceLimit; + char16_t *target; + const char16_t *targetLimit; + + int32_t prev, count, diff, c; + + int8_t byteIndex; + uint8_t *bytes; + + /* set up the local pointers */ + cnv=pArgs->converter; + source=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + target=pArgs->target; + targetLimit=pArgs->targetLimit; + + /* get the converter state from UConverter */ + prev=(int32_t)cnv->toUnicodeStatus; + if(prev==0) { + prev=BOCU1_ASCII_PREV; + } + diff=cnv->mode; /* mode may be set to UCNV_SI by ucnv_bld.c but then toULength==0 */ + count=diff&3; + diff>>=2; + + byteIndex=cnv->toULength; + bytes=cnv->toUBytes; + + /* conversion "loop" similar to _SCSUToUnicodeWithOffsets() */ + if(count>0 && byteIndex>0 && targettargetLimit-target); + if(count>diff) { + count=diff; + } + while(count>0) { + if(BOCU1_START_NEG_2<=(c=*source) && c=targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + + c=*source++; + if(BOCU1_START_NEG_2<=c && c=BOCU1_MIDDLE) { + diff=((int32_t)c-BOCU1_START_POS_2)*BOCU1_TRAIL_COUNT+BOCU1_REACH_POS_1+1; + } else { + diff=((int32_t)c-BOCU1_START_NEG_2)*BOCU1_TRAIL_COUNT+BOCU1_REACH_NEG_1; + } + + /* trail byte */ + c=decodeBocu1TrailByte(1, *source++); + if(c<0 || (uint32_t)(c=prev+diff+c)>0x10ffff) { + bytes[0]=source[-2]; + bytes[1]=source[-1]; + byteIndex=2; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } + } else if(c==BOCU1_RESET) { + /* only reset the state, no code point */ + prev=BOCU1_ASCII_PREV; + continue; + } else { + /* + * For multi-byte difference lead bytes, set the decoder state + * with the partial difference value from the lead byte and + * with the number of trail bytes. + */ + bytes[0]=(uint8_t)c; + byteIndex=1; + + diff=decodeBocu1LeadByte(c); + count=diff&3; + diff>>=2; +getTrail: + for(;;) { + if(source>=sourceLimit) { + goto endloop; + } + c=bytes[byteIndex++]=*source++; + + /* trail byte in any position */ + c=decodeBocu1TrailByte(count, c); + if(c<0) { + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + goto endloop; + } + + diff+=c; + if(--count==0) { + /* final trail byte, deliver a code point */ + byteIndex=0; + c=prev+diff; + if((uint32_t)c>0x10ffff) { + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + goto endloop; + } + break; + } + } + } + + /* calculate the next prev and output c */ + prev=BOCU1_PREV(c); + if(c<=0xffff) { + *target++=(char16_t)c; + } else { + /* output surrogate pair */ + *target++=U16_LEAD(c); + if(targetUCharErrorBuffer[0]=U16_TRAIL(c); + cnv->UCharErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + } +endloop: + + if(*pErrorCode==U_ILLEGAL_CHAR_FOUND) { + /* set the converter state in UConverter to deal with the next character */ + cnv->toUnicodeStatus=BOCU1_ASCII_PREV; + cnv->mode=0; + } else { + /* set the converter state back into UConverter */ + cnv->toUnicodeStatus=(uint32_t)prev; + cnv->mode=((uint32_t)diff<<2)|count; + } + cnv->toULength=byteIndex; + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + return; +} + +/* miscellaneous ------------------------------------------------------------ */ + +static const UConverterImpl _Bocu1Impl={ + UCNV_BOCU1, + + nullptr, + nullptr, + + nullptr, + nullptr, + nullptr, + + _Bocu1ToUnicode, + _Bocu1ToUnicodeWithOffsets, + _Bocu1FromUnicode, + _Bocu1FromUnicodeWithOffsets, + nullptr, + + nullptr, + nullptr, + nullptr, + nullptr, + ucnv_getCompleteUnicodeSet, + + nullptr, + nullptr +}; + +static const UConverterStaticData _Bocu1StaticData={ + sizeof(UConverterStaticData), + "BOCU-1", + 1214, /* CCSID for BOCU-1 */ + UCNV_IBM, UCNV_BOCU1, + 1, 4, /* one char16_t generates at least 1 byte and at most 4 bytes */ + { 0x1a, 0, 0, 0 }, 1, /* BOCU-1 never needs to write a subchar */ + false, false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + +const UConverterSharedData _Bocu1Data= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_Bocu1StaticData, &_Bocu1Impl); + +#endif diff --git a/deps/icu-small/source/common/ucnvdisp.cpp b/deps/icu-small/source/common/ucnvdisp.cpp index ac86b98597066f..4eda36aea14ec2 100644 --- a/deps/icu-small/source/common/ucnvdisp.cpp +++ b/deps/icu-small/source/common/ucnvdisp.cpp @@ -1,88 +1,88 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1998-2004, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* ucnvdisp.c: -* Implements APIs for the ICU's codeset conversion library display names. -* -* Modification History: -* -* Date Name Description -* 04/04/99 helena Fixed internal header inclusion. -* 05/09/00 helena Added implementation to handle fallback mappings. -* 06/20/2000 helena OS/400 port changes; mostly typecast. -* 09/08/2004 grhoten split from ucnv.c -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ustring.h" -#include "unicode/ures.h" -#include "unicode/ucnv.h" -#include "cstring.h" -#include "ustr_imp.h" -#include "ucnv_imp.h" -#include "putilimp.h" - -U_CAPI int32_t U_EXPORT2 -ucnv_getDisplayName(const UConverter *cnv, - const char *displayLocale, - UChar *displayName, int32_t displayNameCapacity, - UErrorCode *pErrorCode) { - UResourceBundle *rb; - const UChar *name; - int32_t length; - UErrorCode localStatus = U_ZERO_ERROR; - - /* check arguments */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - if(cnv==NULL || displayNameCapacity<0 || (displayNameCapacity>0 && displayName==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* open the resource bundle and get the display name string */ - rb=ures_open(NULL, displayLocale, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return 0; - } - - /* use the internal name as the key */ - name=ures_getStringByKey(rb, cnv->sharedData->staticData->name, &length, &localStatus); - ures_close(rb); - - if(U_SUCCESS(localStatus)) { - /* copy the string */ - if (*pErrorCode == U_ZERO_ERROR) { - *pErrorCode = localStatus; - } - u_memcpy(displayName, name, uprv_min(length, displayNameCapacity)*U_SIZEOF_UCHAR); - } else { - /* convert the internal name into a Unicode string */ - length=(int32_t)uprv_strlen(cnv->sharedData->staticData->name); - u_charsToUChars(cnv->sharedData->staticData->name, displayName, uprv_min(length, displayNameCapacity)); - } - return u_terminateUChars(displayName, displayNameCapacity, length, pErrorCode); -} - -#endif - -/* - * Hey, Emacs, please set the following: - * - * Local Variables: - * indent-tabs-mode: nil - * End: - * - */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1998-2004, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* ucnvdisp.c: +* Implements APIs for the ICU's codeset conversion library display names. +* +* Modification History: +* +* Date Name Description +* 04/04/99 helena Fixed internal header inclusion. +* 05/09/00 helena Added implementation to handle fallback mappings. +* 06/20/2000 helena OS/400 port changes; mostly typecast. +* 09/08/2004 grhoten split from ucnv.c +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ustring.h" +#include "unicode/ures.h" +#include "unicode/ucnv.h" +#include "cstring.h" +#include "ustr_imp.h" +#include "ucnv_imp.h" +#include "putilimp.h" + +U_CAPI int32_t U_EXPORT2 +ucnv_getDisplayName(const UConverter *cnv, + const char *displayLocale, + char16_t *displayName, int32_t displayNameCapacity, + UErrorCode *pErrorCode) { + UResourceBundle *rb; + const char16_t *name; + int32_t length; + UErrorCode localStatus = U_ZERO_ERROR; + + /* check arguments */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + if(cnv==nullptr || displayNameCapacity<0 || (displayNameCapacity>0 && displayName==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* open the resource bundle and get the display name string */ + rb=ures_open(nullptr, displayLocale, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return 0; + } + + /* use the internal name as the key */ + name=ures_getStringByKey(rb, cnv->sharedData->staticData->name, &length, &localStatus); + ures_close(rb); + + if(U_SUCCESS(localStatus)) { + /* copy the string */ + if (*pErrorCode == U_ZERO_ERROR) { + *pErrorCode = localStatus; + } + u_memcpy(displayName, name, uprv_min(length, displayNameCapacity)*U_SIZEOF_UCHAR); + } else { + /* convert the internal name into a Unicode string */ + length=(int32_t)uprv_strlen(cnv->sharedData->staticData->name); + u_charsToUChars(cnv->sharedData->staticData->name, displayName, uprv_min(length, displayNameCapacity)); + } + return u_terminateUChars(displayName, displayNameCapacity, length, pErrorCode); +} + +#endif + +/* + * Hey, Emacs, please set the following: + * + * Local Variables: + * indent-tabs-mode: nil + * End: + * + */ diff --git a/deps/icu-small/source/common/ucnvhz.cpp b/deps/icu-small/source/common/ucnvhz.cpp index e0d2f0775df24e..25300773f29b51 100644 --- a/deps/icu-small/source/common/ucnvhz.cpp +++ b/deps/icu-small/source/common/ucnvhz.cpp @@ -1,625 +1,625 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2000-2015, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: ucnvhz.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2000oct16 -* created by: Ram Viswanadha -* 10/31/2000 Ram Implemented offsets logic function -* -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION - -#include "cmemory.h" -#include "unicode/ucnv.h" -#include "unicode/ucnv_cb.h" -#include "unicode/uset.h" -#include "unicode/utf16.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" -#include "ucnv_imp.h" - -#define UCNV_TILDE 0x7E /* ~ */ -#define UCNV_OPEN_BRACE 0x7B /* { */ -#define UCNV_CLOSE_BRACE 0x7D /* } */ -#define SB_ESCAPE "\x7E\x7D" -#define DB_ESCAPE "\x7E\x7B" -#define TILDE_ESCAPE "\x7E\x7E" -#define ESC_LEN 2 - - -#define CONCAT_ESCAPE_MACRO(args, targetIndex,targetLength,strToAppend, err, len,sourceIndex) UPRV_BLOCK_MACRO_BEGIN { \ - while(len-->0){ \ - if(targetIndex < targetLength){ \ - args->target[targetIndex] = (unsigned char) *strToAppend; \ - if(args->offsets!=NULL){ \ - *(offsets++) = sourceIndex-1; \ - } \ - targetIndex++; \ - } \ - else{ \ - args->converter->charErrorBuffer[(int)args->converter->charErrorBufferLength++] = (unsigned char) *strToAppend; \ - *err =U_BUFFER_OVERFLOW_ERROR; \ - } \ - strToAppend++; \ - } \ -} UPRV_BLOCK_MACRO_END - - -typedef struct{ - UConverter* gbConverter; - int32_t targetIndex; - int32_t sourceIndex; - UBool isEscapeAppended; - UBool isStateDBCS; - UBool isTargetUCharDBCS; - UBool isEmptySegment; -}UConverterDataHZ; - - -U_CDECL_BEGIN -static void U_CALLCONV -_HZOpen(UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *errorCode){ - UConverter *gbConverter; - if(pArgs->onlyTestIsLoadable) { - ucnv_canCreateConverter("GBK", errorCode); /* errorCode carries result */ - return; - } - gbConverter = ucnv_open("GBK", errorCode); - if(U_FAILURE(*errorCode)) { - return; - } - cnv->toUnicodeStatus = 0; - cnv->fromUnicodeStatus= 0; - cnv->mode=0; - cnv->fromUChar32=0x0000; - cnv->extraInfo = uprv_calloc(1, sizeof(UConverterDataHZ)); - if(cnv->extraInfo != NULL){ - ((UConverterDataHZ*)cnv->extraInfo)->gbConverter = gbConverter; - } - else { - ucnv_close(gbConverter); - *errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } -} - -static void U_CALLCONV -_HZClose(UConverter *cnv){ - if(cnv->extraInfo != NULL) { - ucnv_close (((UConverterDataHZ *) (cnv->extraInfo))->gbConverter); - if(!cnv->isExtraLocal) { - uprv_free(cnv->extraInfo); - } - cnv->extraInfo = NULL; - } -} - -static void U_CALLCONV -_HZReset(UConverter *cnv, UConverterResetChoice choice){ - if(choice<=UCNV_RESET_TO_UNICODE) { - cnv->toUnicodeStatus = 0; - cnv->mode=0; - if(cnv->extraInfo != NULL){ - ((UConverterDataHZ*)cnv->extraInfo)->isStateDBCS = false; - ((UConverterDataHZ*)cnv->extraInfo)->isEmptySegment = false; - } - } - if(choice!=UCNV_RESET_TO_UNICODE) { - cnv->fromUnicodeStatus= 0; - cnv->fromUChar32=0x0000; - if(cnv->extraInfo != NULL){ - ((UConverterDataHZ*)cnv->extraInfo)->isEscapeAppended = false; - ((UConverterDataHZ*)cnv->extraInfo)->targetIndex = 0; - ((UConverterDataHZ*)cnv->extraInfo)->sourceIndex = 0; - ((UConverterDataHZ*)cnv->extraInfo)->isTargetUCharDBCS = false; - } - } -} - -/**************************************HZ Encoding************************************************* -* Rules for HZ encoding -* -* In ASCII mode, a byte is interpreted as an ASCII character, unless a -* '~' is encountered. The character '~' is an escape character. By -* convention, it must be immediately followed ONLY by '~', '{' or '\n' -* (), with the following special meaning. - -* 1. The escape sequence '~~' is interpreted as a '~'. -* 2. The escape-to-GB sequence '~{' switches the mode from ASCII to GB. -* 3. The escape sequence '~\n' is a line-continuation marker to be -* consumed with no output produced. -* In GB mode, characters are interpreted two bytes at a time as (pure) -* GB codes until the escape-from-GB code '~}' is read. This code -* switches the mode from GB back to ASCII. (Note that the escape- -* from-GB code '~}' ($7E7D) is outside the defined GB range.) -* -* Source: RFC 1842 -* -* Note that the formal syntax in RFC 1842 is invalid. I assume that the -* intended definition of single-byte-segment is as follows (pedberg): -* single-byte-segment = single-byte-seq 1*single-byte-char -*/ - - -static void U_CALLCONV -UConverter_toUnicode_HZ_OFFSETS_LOGIC(UConverterToUnicodeArgs *args, - UErrorCode* err){ - char tempBuf[2]; - const char *mySource = ( char *) args->source; - UChar *myTarget = args->target; - const char *mySourceLimit = args->sourceLimit; - UChar32 targetUniChar = 0x0000; - int32_t mySourceChar = 0x0000; - UConverterDataHZ* myData=(UConverterDataHZ*)(args->converter->extraInfo); - tempBuf[0]=0; - tempBuf[1]=0; - - /* Calling code already handles this situation. */ - /*if ((args->converter == NULL) || (args->targetLimit < args->target) || (mySourceLimit < args->source)){ - *err = U_ILLEGAL_ARGUMENT_ERROR; - return; - }*/ - - while(mySource< mySourceLimit){ - - if(myTarget < args->targetLimit){ - - mySourceChar= (unsigned char) *mySource++; - - if(args->converter->mode == UCNV_TILDE) { - /* second byte after ~ */ - args->converter->mode=0; - switch(mySourceChar) { - case 0x0A: - /* no output for ~\n (line-continuation marker) */ - continue; - case UCNV_TILDE: - if(args->offsets) { - args->offsets[myTarget - args->target]=(int32_t)(mySource - args->source - 2); - } - *(myTarget++)=(UChar)mySourceChar; - myData->isEmptySegment = false; - continue; - case UCNV_OPEN_BRACE: - case UCNV_CLOSE_BRACE: - myData->isStateDBCS = (mySourceChar == UCNV_OPEN_BRACE); - if (myData->isEmptySegment) { - myData->isEmptySegment = false; /* we are handling it, reset to avoid future spurious errors */ - *err = U_ILLEGAL_ESCAPE_SEQUENCE; - args->converter->toUCallbackReason = UCNV_IRREGULAR; - args->converter->toUBytes[0] = UCNV_TILDE; - args->converter->toUBytes[1] = static_cast(mySourceChar); - args->converter->toULength = 2; - args->target = myTarget; - args->source = mySource; - return; - } - myData->isEmptySegment = true; - continue; - default: - /* if the first byte is equal to TILDE and the trail byte - * is not a valid byte then it is an error condition - */ - /* - * Ticket 5691: consistent illegal sequences: - * - We include at least the first byte in the illegal sequence. - * - If any of the non-initial bytes could be the start of a character, - * we stop the illegal sequence before the first one of those. - */ - myData->isEmptySegment = false; /* different error here, reset this to avoid spurious future error */ - *err = U_ILLEGAL_ESCAPE_SEQUENCE; - args->converter->toUBytes[0] = UCNV_TILDE; - if( myData->isStateDBCS ? - (0x21 <= mySourceChar && mySourceChar <= 0x7e) : - mySourceChar <= 0x7f - ) { - /* The current byte could be the start of a character: Back it out. */ - args->converter->toULength = 1; - --mySource; - } else { - /* Include the current byte in the illegal sequence. */ - args->converter->toUBytes[1] = static_cast(mySourceChar); - args->converter->toULength = 2; - } - args->target = myTarget; - args->source = mySource; - return; - } - } else if(myData->isStateDBCS) { - if(args->converter->toUnicodeStatus == 0x00){ - /* lead byte */ - if(mySourceChar == UCNV_TILDE) { - args->converter->mode = UCNV_TILDE; - } else { - /* add another bit to distinguish a 0 byte from not having seen a lead byte */ - args->converter->toUnicodeStatus = (uint32_t) (mySourceChar | 0x100); - myData->isEmptySegment = false; /* the segment has something, either valid or will produce a different error, so reset this */ - } - continue; - } - else{ - /* trail byte */ - int leadIsOk, trailIsOk; - uint32_t leadByte = args->converter->toUnicodeStatus & 0xff; - targetUniChar = 0xffff; - /* - * Ticket 5691: consistent illegal sequences: - * - We include at least the first byte in the illegal sequence. - * - If any of the non-initial bytes could be the start of a character, - * we stop the illegal sequence before the first one of those. - * - * In HZ DBCS, if the second byte is in the 21..7e range, - * we report only the first byte as the illegal sequence. - * Otherwise we convert or report the pair of bytes. - */ - leadIsOk = (uint8_t)(leadByte - 0x21) <= (0x7d - 0x21); - trailIsOk = (uint8_t)(mySourceChar - 0x21) <= (0x7e - 0x21); - if (leadIsOk && trailIsOk) { - tempBuf[0] = (char) (leadByte+0x80) ; - tempBuf[1] = (char) (mySourceChar+0x80); - targetUniChar = ucnv_MBCSSimpleGetNextUChar(myData->gbConverter->sharedData, - tempBuf, 2, args->converter->useFallback); - mySourceChar= (leadByte << 8) | mySourceChar; - } else if (trailIsOk) { - /* report a single illegal byte and continue with the following DBCS starter byte */ - --mySource; - mySourceChar = (int32_t)leadByte; - } else { - /* report a pair of illegal bytes if the second byte is not a DBCS starter */ - /* add another bit so that the code below writes 2 bytes in case of error */ - mySourceChar= 0x10000 | (leadByte << 8) | mySourceChar; - } - args->converter->toUnicodeStatus =0x00; - } - } - else{ - if(mySourceChar == UCNV_TILDE) { - args->converter->mode = UCNV_TILDE; - continue; - } else if(mySourceChar <= 0x7f) { - targetUniChar = (UChar)mySourceChar; /* ASCII */ - myData->isEmptySegment = false; /* the segment has something valid */ - } else { - targetUniChar = 0xffff; - myData->isEmptySegment = false; /* different error here, reset this to avoid spurious future error */ - } - } - if(targetUniChar < 0xfffe){ - if(args->offsets) { - args->offsets[myTarget - args->target]=(int32_t)(mySource - args->source - 1-(myData->isStateDBCS)); - } - - *(myTarget++)=(UChar)targetUniChar; - } - else /* targetUniChar>=0xfffe */ { - if(targetUniChar == 0xfffe){ - *err = U_INVALID_CHAR_FOUND; - } - else{ - *err = U_ILLEGAL_CHAR_FOUND; - } - if(mySourceChar > 0xff){ - args->converter->toUBytes[0] = (uint8_t)(mySourceChar >> 8); - args->converter->toUBytes[1] = (uint8_t)mySourceChar; - args->converter->toULength=2; - } - else{ - args->converter->toUBytes[0] = (uint8_t)mySourceChar; - args->converter->toULength=1; - } - break; - } - } - else{ - *err =U_BUFFER_OVERFLOW_ERROR; - break; - } - } - - args->target = myTarget; - args->source = mySource; -} - - -static void U_CALLCONV -UConverter_fromUnicode_HZ_OFFSETS_LOGIC (UConverterFromUnicodeArgs * args, - UErrorCode * err){ - const UChar *mySource = args->source; - char *myTarget = args->target; - int32_t* offsets = args->offsets; - int32_t mySourceIndex = 0; - int32_t myTargetIndex = 0; - int32_t targetLength = (int32_t)(args->targetLimit - myTarget); - int32_t mySourceLength = (int32_t)(args->sourceLimit - args->source); - uint32_t targetUniChar = 0x0000; - UChar32 mySourceChar = 0x0000; - UConverterDataHZ *myConverterData=(UConverterDataHZ*)args->converter->extraInfo; - UBool isTargetUCharDBCS = (UBool) myConverterData->isTargetUCharDBCS; - UBool oldIsTargetUCharDBCS; - int len =0; - const char* escSeq=NULL; - - /* Calling code already handles this situation. */ - /*if ((args->converter == NULL) || (args->targetLimit < myTarget) || (args->sourceLimit < args->source)){ - *err = U_ILLEGAL_ARGUMENT_ERROR; - return; - }*/ - if(args->converter->fromUChar32!=0 && myTargetIndex < targetLength) { - goto getTrail; - } - /*writing the char to the output stream */ - while (mySourceIndex < mySourceLength){ - targetUniChar = missingCharMarker; - if (myTargetIndex < targetLength){ - - mySourceChar = (UChar) mySource[mySourceIndex++]; - - - oldIsTargetUCharDBCS = isTargetUCharDBCS; - if(mySourceChar ==UCNV_TILDE){ - /*concatEscape(args, &myTargetIndex, &targetLength,"\x7E\x7E",err,2,&mySourceIndex);*/ - len = ESC_LEN; - escSeq = TILDE_ESCAPE; - CONCAT_ESCAPE_MACRO(args, myTargetIndex, targetLength, escSeq,err,len,mySourceIndex); - continue; - } else if(mySourceChar <= 0x7f) { - targetUniChar = mySourceChar; - } else { - int32_t length= ucnv_MBCSFromUChar32(myConverterData->gbConverter->sharedData, - mySourceChar,&targetUniChar,args->converter->useFallback); - /* we can only use lead bytes 21..7D and trail bytes 21..7E */ - if( length == 2 && - (uint16_t)(targetUniChar - 0xa1a1) <= (0xfdfe - 0xa1a1) && - (uint8_t)(targetUniChar - 0xa1) <= (0xfe - 0xa1) - ) { - targetUniChar -= 0x8080; - } else { - targetUniChar = missingCharMarker; - } - } - if (targetUniChar != missingCharMarker){ - myConverterData->isTargetUCharDBCS = isTargetUCharDBCS = (UBool)(targetUniChar>0x00FF); - if(oldIsTargetUCharDBCS != isTargetUCharDBCS || !myConverterData->isEscapeAppended ){ - /*Shifting from a double byte to single byte mode*/ - if(!isTargetUCharDBCS){ - len =ESC_LEN; - escSeq = SB_ESCAPE; - CONCAT_ESCAPE_MACRO(args, myTargetIndex, targetLength, escSeq,err,len,mySourceIndex); - myConverterData->isEscapeAppended = true; - } - else{ /* Shifting from a single byte to double byte mode*/ - len =ESC_LEN; - escSeq = DB_ESCAPE; - CONCAT_ESCAPE_MACRO(args, myTargetIndex, targetLength, escSeq,err,len,mySourceIndex); - myConverterData->isEscapeAppended = true; - - } - } - - if(isTargetUCharDBCS){ - if( myTargetIndex > 8); - if(offsets){ - *(offsets++) = mySourceIndex-1; - } - if(myTargetIndex < targetLength){ - myTarget[myTargetIndex++] =(char) targetUniChar; - if(offsets){ - *(offsets++) = mySourceIndex-1; - } - }else{ - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (char) targetUniChar; - *err = U_BUFFER_OVERFLOW_ERROR; - } - }else{ - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] =(char) (targetUniChar >> 8); - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (char) targetUniChar; - *err = U_BUFFER_OVERFLOW_ERROR; - } - - }else{ - if( myTargetIndex converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (char) targetUniChar; - *err = U_BUFFER_OVERFLOW_ERROR; - } - } - - } - else{ - /* oops.. the code point is unassigned */ - /*Handle surrogates */ - /*check if the char is a First surrogate*/ - if(U16_IS_SURROGATE(mySourceChar)) { - if(U16_IS_SURROGATE_LEAD(mySourceChar)) { - args->converter->fromUChar32=mySourceChar; -getTrail: - /*look ahead to find the trail surrogate*/ - if(mySourceIndex < mySourceLength) { - /* test the following code unit */ - UChar trail=(UChar) args->source[mySourceIndex]; - if(U16_IS_TRAIL(trail)) { - ++mySourceIndex; - mySourceChar=U16_GET_SUPPLEMENTARY(args->converter->fromUChar32, trail); - args->converter->fromUChar32=0x00; - /* there are no surrogates in GB2312*/ - *err = U_INVALID_CHAR_FOUND; - /* exit this condition tree */ - } else { - /* this is an unmatched lead code unit (1st surrogate) */ - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - } - } else { - /* no more input */ - *err = U_ZERO_ERROR; - } - } else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - } - } else { - /* callback(unassigned) for a BMP code point */ - *err = U_INVALID_CHAR_FOUND; - } - - args->converter->fromUChar32=mySourceChar; - break; - } - } - else{ - *err = U_BUFFER_OVERFLOW_ERROR; - break; - } - targetUniChar=missingCharMarker; - } - - args->target += myTargetIndex; - args->source += mySourceIndex; - myConverterData->isTargetUCharDBCS = isTargetUCharDBCS; -} - -static void U_CALLCONV -_HZ_WriteSub(UConverterFromUnicodeArgs *args, int32_t offsetIndex, UErrorCode *err) { - UConverter *cnv = args->converter; - UConverterDataHZ *convData=(UConverterDataHZ *) cnv->extraInfo; - char *p; - char buffer[4]; - p = buffer; - - if( convData->isTargetUCharDBCS){ - *p++= UCNV_TILDE; - *p++= UCNV_CLOSE_BRACE; - convData->isTargetUCharDBCS=false; - } - *p++= (char)cnv->subChars[0]; - - ucnv_cbFromUWriteBytes(args, - buffer, (int32_t)(p - buffer), - offsetIndex, err); -} - -/* - * Structure for cloning an HZ converter into a single memory block. - */ -struct cloneHZStruct -{ - UConverter cnv; - UConverter subCnv; - UConverterDataHZ mydata; -}; - - -static UConverter * U_CALLCONV -_HZ_SafeClone(const UConverter *cnv, - void *stackBuffer, - int32_t *pBufferSize, - UErrorCode *status) -{ - struct cloneHZStruct * localClone; - int32_t size, bufferSizeNeeded = sizeof(struct cloneHZStruct); - - if (U_FAILURE(*status)){ - return nullptr; - } - - if (*pBufferSize == 0){ /* 'preflighting' request - set needed size into *pBufferSize */ - *pBufferSize = bufferSizeNeeded; - return nullptr; - } - - localClone = (struct cloneHZStruct *)stackBuffer; - /* ucnv.c/ucnv_safeClone() copied the main UConverter already */ - - uprv_memcpy(&localClone->mydata, cnv->extraInfo, sizeof(UConverterDataHZ)); - localClone->cnv.extraInfo = &localClone->mydata; - localClone->cnv.isExtraLocal = true; - - /* deep-clone the sub-converter */ - size = (int32_t)sizeof(UConverter); - ((UConverterDataHZ*)localClone->cnv.extraInfo)->gbConverter = - ucnv_safeClone(((UConverterDataHZ*)cnv->extraInfo)->gbConverter, &localClone->subCnv, &size, status); - - return &localClone->cnv; -} - -static void U_CALLCONV -_HZ_GetUnicodeSet(const UConverter *cnv, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode) { - /* HZ converts all of ASCII */ - sa->addRange(sa->set, 0, 0x7f); - - /* add all of the code points that the sub-converter handles */ - ucnv_MBCSGetFilteredUnicodeSetForUnicode( - ((UConverterDataHZ*)cnv->extraInfo)->gbConverter->sharedData, - sa, which, UCNV_SET_FILTER_HZ, - pErrorCode); -} -U_CDECL_END -static const UConverterImpl _HZImpl={ - - UCNV_HZ, - - NULL, - NULL, - - _HZOpen, - _HZClose, - _HZReset, - - UConverter_toUnicode_HZ_OFFSETS_LOGIC, - UConverter_toUnicode_HZ_OFFSETS_LOGIC, - UConverter_fromUnicode_HZ_OFFSETS_LOGIC, - UConverter_fromUnicode_HZ_OFFSETS_LOGIC, - NULL, - - NULL, - NULL, - _HZ_WriteSub, - _HZ_SafeClone, - _HZ_GetUnicodeSet, - NULL, - NULL -}; - -static const UConverterStaticData _HZStaticData={ - sizeof(UConverterStaticData), - "HZ", - 0, - UCNV_IBM, - UCNV_HZ, - 1, - 4, - { 0x1a, 0, 0, 0 }, - 1, - false, - false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, /* reserved */ - -}; - -const UConverterSharedData _HZData= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_HZStaticData, &_HZImpl); - -#endif /* #if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2000-2015, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: ucnvhz.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2000oct16 +* created by: Ram Viswanadha +* 10/31/2000 Ram Implemented offsets logic function +* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION + +#include "cmemory.h" +#include "unicode/ucnv.h" +#include "unicode/ucnv_cb.h" +#include "unicode/uset.h" +#include "unicode/utf16.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" +#include "ucnv_imp.h" + +#define UCNV_TILDE 0x7E /* ~ */ +#define UCNV_OPEN_BRACE 0x7B /* { */ +#define UCNV_CLOSE_BRACE 0x7D /* } */ +#define SB_ESCAPE "\x7E\x7D" +#define DB_ESCAPE "\x7E\x7B" +#define TILDE_ESCAPE "\x7E\x7E" +#define ESC_LEN 2 + + +#define CONCAT_ESCAPE_MACRO(args, targetIndex,targetLength,strToAppend, err, len,sourceIndex) UPRV_BLOCK_MACRO_BEGIN { \ + while(len-->0){ \ + if(targetIndex < targetLength){ \ + args->target[targetIndex] = (unsigned char) *strToAppend; \ + if(args->offsets!=nullptr){ \ + *(offsets++) = sourceIndex-1; \ + } \ + targetIndex++; \ + } \ + else{ \ + args->converter->charErrorBuffer[(int)args->converter->charErrorBufferLength++] = (unsigned char) *strToAppend; \ + *err =U_BUFFER_OVERFLOW_ERROR; \ + } \ + strToAppend++; \ + } \ +} UPRV_BLOCK_MACRO_END + + +typedef struct{ + UConverter* gbConverter; + int32_t targetIndex; + int32_t sourceIndex; + UBool isEscapeAppended; + UBool isStateDBCS; + UBool isTargetUCharDBCS; + UBool isEmptySegment; +}UConverterDataHZ; + + +U_CDECL_BEGIN +static void U_CALLCONV +_HZOpen(UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *errorCode){ + UConverter *gbConverter; + if(pArgs->onlyTestIsLoadable) { + ucnv_canCreateConverter("GBK", errorCode); /* errorCode carries result */ + return; + } + gbConverter = ucnv_open("GBK", errorCode); + if(U_FAILURE(*errorCode)) { + return; + } + cnv->toUnicodeStatus = 0; + cnv->fromUnicodeStatus= 0; + cnv->mode=0; + cnv->fromUChar32=0x0000; + cnv->extraInfo = uprv_calloc(1, sizeof(UConverterDataHZ)); + if(cnv->extraInfo != nullptr){ + ((UConverterDataHZ*)cnv->extraInfo)->gbConverter = gbConverter; + } + else { + ucnv_close(gbConverter); + *errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } +} + +static void U_CALLCONV +_HZClose(UConverter *cnv){ + if(cnv->extraInfo != nullptr) { + ucnv_close (((UConverterDataHZ *) (cnv->extraInfo))->gbConverter); + if(!cnv->isExtraLocal) { + uprv_free(cnv->extraInfo); + } + cnv->extraInfo = nullptr; + } +} + +static void U_CALLCONV +_HZReset(UConverter *cnv, UConverterResetChoice choice){ + if(choice<=UCNV_RESET_TO_UNICODE) { + cnv->toUnicodeStatus = 0; + cnv->mode=0; + if(cnv->extraInfo != nullptr){ + ((UConverterDataHZ*)cnv->extraInfo)->isStateDBCS = false; + ((UConverterDataHZ*)cnv->extraInfo)->isEmptySegment = false; + } + } + if(choice!=UCNV_RESET_TO_UNICODE) { + cnv->fromUnicodeStatus= 0; + cnv->fromUChar32=0x0000; + if(cnv->extraInfo != nullptr){ + ((UConverterDataHZ*)cnv->extraInfo)->isEscapeAppended = false; + ((UConverterDataHZ*)cnv->extraInfo)->targetIndex = 0; + ((UConverterDataHZ*)cnv->extraInfo)->sourceIndex = 0; + ((UConverterDataHZ*)cnv->extraInfo)->isTargetUCharDBCS = false; + } + } +} + +/**************************************HZ Encoding************************************************* +* Rules for HZ encoding +* +* In ASCII mode, a byte is interpreted as an ASCII character, unless a +* '~' is encountered. The character '~' is an escape character. By +* convention, it must be immediately followed ONLY by '~', '{' or '\n' +* (), with the following special meaning. + +* 1. The escape sequence '~~' is interpreted as a '~'. +* 2. The escape-to-GB sequence '~{' switches the mode from ASCII to GB. +* 3. The escape sequence '~\n' is a line-continuation marker to be +* consumed with no output produced. +* In GB mode, characters are interpreted two bytes at a time as (pure) +* GB codes until the escape-from-GB code '~}' is read. This code +* switches the mode from GB back to ASCII. (Note that the escape- +* from-GB code '~}' ($7E7D) is outside the defined GB range.) +* +* Source: RFC 1842 +* +* Note that the formal syntax in RFC 1842 is invalid. I assume that the +* intended definition of single-byte-segment is as follows (pedberg): +* single-byte-segment = single-byte-seq 1*single-byte-char +*/ + + +static void U_CALLCONV +UConverter_toUnicode_HZ_OFFSETS_LOGIC(UConverterToUnicodeArgs *args, + UErrorCode* err){ + char tempBuf[2]; + const char *mySource = ( char *) args->source; + char16_t *myTarget = args->target; + const char *mySourceLimit = args->sourceLimit; + UChar32 targetUniChar = 0x0000; + int32_t mySourceChar = 0x0000; + UConverterDataHZ* myData=(UConverterDataHZ*)(args->converter->extraInfo); + tempBuf[0]=0; + tempBuf[1]=0; + + /* Calling code already handles this situation. */ + /*if ((args->converter == nullptr) || (args->targetLimit < args->target) || (mySourceLimit < args->source)){ + *err = U_ILLEGAL_ARGUMENT_ERROR; + return; + }*/ + + while(mySource< mySourceLimit){ + + if(myTarget < args->targetLimit){ + + mySourceChar= (unsigned char) *mySource++; + + if(args->converter->mode == UCNV_TILDE) { + /* second byte after ~ */ + args->converter->mode=0; + switch(mySourceChar) { + case 0x0A: + /* no output for ~\n (line-continuation marker) */ + continue; + case UCNV_TILDE: + if(args->offsets) { + args->offsets[myTarget - args->target]=(int32_t)(mySource - args->source - 2); + } + *(myTarget++)=(char16_t)mySourceChar; + myData->isEmptySegment = false; + continue; + case UCNV_OPEN_BRACE: + case UCNV_CLOSE_BRACE: + myData->isStateDBCS = (mySourceChar == UCNV_OPEN_BRACE); + if (myData->isEmptySegment) { + myData->isEmptySegment = false; /* we are handling it, reset to avoid future spurious errors */ + *err = U_ILLEGAL_ESCAPE_SEQUENCE; + args->converter->toUCallbackReason = UCNV_IRREGULAR; + args->converter->toUBytes[0] = UCNV_TILDE; + args->converter->toUBytes[1] = static_cast(mySourceChar); + args->converter->toULength = 2; + args->target = myTarget; + args->source = mySource; + return; + } + myData->isEmptySegment = true; + continue; + default: + /* if the first byte is equal to TILDE and the trail byte + * is not a valid byte then it is an error condition + */ + /* + * Ticket 5691: consistent illegal sequences: + * - We include at least the first byte in the illegal sequence. + * - If any of the non-initial bytes could be the start of a character, + * we stop the illegal sequence before the first one of those. + */ + myData->isEmptySegment = false; /* different error here, reset this to avoid spurious future error */ + *err = U_ILLEGAL_ESCAPE_SEQUENCE; + args->converter->toUBytes[0] = UCNV_TILDE; + if( myData->isStateDBCS ? + (0x21 <= mySourceChar && mySourceChar <= 0x7e) : + mySourceChar <= 0x7f + ) { + /* The current byte could be the start of a character: Back it out. */ + args->converter->toULength = 1; + --mySource; + } else { + /* Include the current byte in the illegal sequence. */ + args->converter->toUBytes[1] = static_cast(mySourceChar); + args->converter->toULength = 2; + } + args->target = myTarget; + args->source = mySource; + return; + } + } else if(myData->isStateDBCS) { + if(args->converter->toUnicodeStatus == 0x00){ + /* lead byte */ + if(mySourceChar == UCNV_TILDE) { + args->converter->mode = UCNV_TILDE; + } else { + /* add another bit to distinguish a 0 byte from not having seen a lead byte */ + args->converter->toUnicodeStatus = (uint32_t) (mySourceChar | 0x100); + myData->isEmptySegment = false; /* the segment has something, either valid or will produce a different error, so reset this */ + } + continue; + } + else{ + /* trail byte */ + int leadIsOk, trailIsOk; + uint32_t leadByte = args->converter->toUnicodeStatus & 0xff; + targetUniChar = 0xffff; + /* + * Ticket 5691: consistent illegal sequences: + * - We include at least the first byte in the illegal sequence. + * - If any of the non-initial bytes could be the start of a character, + * we stop the illegal sequence before the first one of those. + * + * In HZ DBCS, if the second byte is in the 21..7e range, + * we report only the first byte as the illegal sequence. + * Otherwise we convert or report the pair of bytes. + */ + leadIsOk = (uint8_t)(leadByte - 0x21) <= (0x7d - 0x21); + trailIsOk = (uint8_t)(mySourceChar - 0x21) <= (0x7e - 0x21); + if (leadIsOk && trailIsOk) { + tempBuf[0] = (char) (leadByte+0x80) ; + tempBuf[1] = (char) (mySourceChar+0x80); + targetUniChar = ucnv_MBCSSimpleGetNextUChar(myData->gbConverter->sharedData, + tempBuf, 2, args->converter->useFallback); + mySourceChar= (leadByte << 8) | mySourceChar; + } else if (trailIsOk) { + /* report a single illegal byte and continue with the following DBCS starter byte */ + --mySource; + mySourceChar = (int32_t)leadByte; + } else { + /* report a pair of illegal bytes if the second byte is not a DBCS starter */ + /* add another bit so that the code below writes 2 bytes in case of error */ + mySourceChar= 0x10000 | (leadByte << 8) | mySourceChar; + } + args->converter->toUnicodeStatus =0x00; + } + } + else{ + if(mySourceChar == UCNV_TILDE) { + args->converter->mode = UCNV_TILDE; + continue; + } else if(mySourceChar <= 0x7f) { + targetUniChar = (char16_t)mySourceChar; /* ASCII */ + myData->isEmptySegment = false; /* the segment has something valid */ + } else { + targetUniChar = 0xffff; + myData->isEmptySegment = false; /* different error here, reset this to avoid spurious future error */ + } + } + if(targetUniChar < 0xfffe){ + if(args->offsets) { + args->offsets[myTarget - args->target]=(int32_t)(mySource - args->source - 1-(myData->isStateDBCS)); + } + + *(myTarget++)=(char16_t)targetUniChar; + } + else /* targetUniChar>=0xfffe */ { + if(targetUniChar == 0xfffe){ + *err = U_INVALID_CHAR_FOUND; + } + else{ + *err = U_ILLEGAL_CHAR_FOUND; + } + if(mySourceChar > 0xff){ + args->converter->toUBytes[0] = (uint8_t)(mySourceChar >> 8); + args->converter->toUBytes[1] = (uint8_t)mySourceChar; + args->converter->toULength=2; + } + else{ + args->converter->toUBytes[0] = (uint8_t)mySourceChar; + args->converter->toULength=1; + } + break; + } + } + else{ + *err =U_BUFFER_OVERFLOW_ERROR; + break; + } + } + + args->target = myTarget; + args->source = mySource; +} + + +static void U_CALLCONV +UConverter_fromUnicode_HZ_OFFSETS_LOGIC (UConverterFromUnicodeArgs * args, + UErrorCode * err){ + const char16_t *mySource = args->source; + char *myTarget = args->target; + int32_t* offsets = args->offsets; + int32_t mySourceIndex = 0; + int32_t myTargetIndex = 0; + int32_t targetLength = (int32_t)(args->targetLimit - myTarget); + int32_t mySourceLength = (int32_t)(args->sourceLimit - args->source); + uint32_t targetUniChar = 0x0000; + UChar32 mySourceChar = 0x0000; + UConverterDataHZ *myConverterData=(UConverterDataHZ*)args->converter->extraInfo; + UBool isTargetUCharDBCS = (UBool) myConverterData->isTargetUCharDBCS; + UBool oldIsTargetUCharDBCS; + int len =0; + const char* escSeq=nullptr; + + /* Calling code already handles this situation. */ + /*if ((args->converter == nullptr) || (args->targetLimit < myTarget) || (args->sourceLimit < args->source)){ + *err = U_ILLEGAL_ARGUMENT_ERROR; + return; + }*/ + if(args->converter->fromUChar32!=0 && myTargetIndex < targetLength) { + goto getTrail; + } + /*writing the char to the output stream */ + while (mySourceIndex < mySourceLength){ + targetUniChar = missingCharMarker; + if (myTargetIndex < targetLength){ + + mySourceChar = (char16_t) mySource[mySourceIndex++]; + + + oldIsTargetUCharDBCS = isTargetUCharDBCS; + if(mySourceChar ==UCNV_TILDE){ + /*concatEscape(args, &myTargetIndex, &targetLength,"\x7E\x7E",err,2,&mySourceIndex);*/ + len = ESC_LEN; + escSeq = TILDE_ESCAPE; + CONCAT_ESCAPE_MACRO(args, myTargetIndex, targetLength, escSeq,err,len,mySourceIndex); + continue; + } else if(mySourceChar <= 0x7f) { + targetUniChar = mySourceChar; + } else { + int32_t length= ucnv_MBCSFromUChar32(myConverterData->gbConverter->sharedData, + mySourceChar,&targetUniChar,args->converter->useFallback); + /* we can only use lead bytes 21..7D and trail bytes 21..7E */ + if( length == 2 && + (uint16_t)(targetUniChar - 0xa1a1) <= (0xfdfe - 0xa1a1) && + (uint8_t)(targetUniChar - 0xa1) <= (0xfe - 0xa1) + ) { + targetUniChar -= 0x8080; + } else { + targetUniChar = missingCharMarker; + } + } + if (targetUniChar != missingCharMarker){ + myConverterData->isTargetUCharDBCS = isTargetUCharDBCS = (UBool)(targetUniChar>0x00FF); + if(oldIsTargetUCharDBCS != isTargetUCharDBCS || !myConverterData->isEscapeAppended ){ + /*Shifting from a double byte to single byte mode*/ + if(!isTargetUCharDBCS){ + len =ESC_LEN; + escSeq = SB_ESCAPE; + CONCAT_ESCAPE_MACRO(args, myTargetIndex, targetLength, escSeq,err,len,mySourceIndex); + myConverterData->isEscapeAppended = true; + } + else{ /* Shifting from a single byte to double byte mode*/ + len =ESC_LEN; + escSeq = DB_ESCAPE; + CONCAT_ESCAPE_MACRO(args, myTargetIndex, targetLength, escSeq,err,len,mySourceIndex); + myConverterData->isEscapeAppended = true; + + } + } + + if(isTargetUCharDBCS){ + if( myTargetIndex > 8); + if(offsets){ + *(offsets++) = mySourceIndex-1; + } + if(myTargetIndex < targetLength){ + myTarget[myTargetIndex++] =(char) targetUniChar; + if(offsets){ + *(offsets++) = mySourceIndex-1; + } + }else{ + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (char) targetUniChar; + *err = U_BUFFER_OVERFLOW_ERROR; + } + }else{ + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] =(char) (targetUniChar >> 8); + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (char) targetUniChar; + *err = U_BUFFER_OVERFLOW_ERROR; + } + + }else{ + if( myTargetIndex converter->charErrorBuffer[args->converter->charErrorBufferLength++] = (char) targetUniChar; + *err = U_BUFFER_OVERFLOW_ERROR; + } + } + + } + else{ + /* oops.. the code point is unassigned */ + /*Handle surrogates */ + /*check if the char is a First surrogate*/ + if(U16_IS_SURROGATE(mySourceChar)) { + if(U16_IS_SURROGATE_LEAD(mySourceChar)) { + args->converter->fromUChar32=mySourceChar; +getTrail: + /*look ahead to find the trail surrogate*/ + if(mySourceIndex < mySourceLength) { + /* test the following code unit */ + char16_t trail=(char16_t) args->source[mySourceIndex]; + if(U16_IS_TRAIL(trail)) { + ++mySourceIndex; + mySourceChar=U16_GET_SUPPLEMENTARY(args->converter->fromUChar32, trail); + args->converter->fromUChar32=0x00; + /* there are no surrogates in GB2312*/ + *err = U_INVALID_CHAR_FOUND; + /* exit this condition tree */ + } else { + /* this is an unmatched lead code unit (1st surrogate) */ + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + } + } else { + /* no more input */ + *err = U_ZERO_ERROR; + } + } else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + } + } else { + /* callback(unassigned) for a BMP code point */ + *err = U_INVALID_CHAR_FOUND; + } + + args->converter->fromUChar32=mySourceChar; + break; + } + } + else{ + *err = U_BUFFER_OVERFLOW_ERROR; + break; + } + targetUniChar=missingCharMarker; + } + + args->target += myTargetIndex; + args->source += mySourceIndex; + myConverterData->isTargetUCharDBCS = isTargetUCharDBCS; +} + +static void U_CALLCONV +_HZ_WriteSub(UConverterFromUnicodeArgs *args, int32_t offsetIndex, UErrorCode *err) { + UConverter *cnv = args->converter; + UConverterDataHZ *convData=(UConverterDataHZ *) cnv->extraInfo; + char *p; + char buffer[4]; + p = buffer; + + if( convData->isTargetUCharDBCS){ + *p++= UCNV_TILDE; + *p++= UCNV_CLOSE_BRACE; + convData->isTargetUCharDBCS=false; + } + *p++= (char)cnv->subChars[0]; + + ucnv_cbFromUWriteBytes(args, + buffer, (int32_t)(p - buffer), + offsetIndex, err); +} + +/* + * Structure for cloning an HZ converter into a single memory block. + */ +struct cloneHZStruct +{ + UConverter cnv; + UConverter subCnv; + UConverterDataHZ mydata; +}; + + +static UConverter * U_CALLCONV +_HZ_SafeClone(const UConverter *cnv, + void *stackBuffer, + int32_t *pBufferSize, + UErrorCode *status) +{ + struct cloneHZStruct * localClone; + int32_t size, bufferSizeNeeded = sizeof(struct cloneHZStruct); + + if (U_FAILURE(*status)){ + return nullptr; + } + + if (*pBufferSize == 0){ /* 'preflighting' request - set needed size into *pBufferSize */ + *pBufferSize = bufferSizeNeeded; + return nullptr; + } + + localClone = (struct cloneHZStruct *)stackBuffer; + /* ucnv.c/ucnv_safeClone() copied the main UConverter already */ + + uprv_memcpy(&localClone->mydata, cnv->extraInfo, sizeof(UConverterDataHZ)); + localClone->cnv.extraInfo = &localClone->mydata; + localClone->cnv.isExtraLocal = true; + + /* deep-clone the sub-converter */ + size = (int32_t)sizeof(UConverter); + ((UConverterDataHZ*)localClone->cnv.extraInfo)->gbConverter = + ucnv_safeClone(((UConverterDataHZ*)cnv->extraInfo)->gbConverter, &localClone->subCnv, &size, status); + + return &localClone->cnv; +} + +static void U_CALLCONV +_HZ_GetUnicodeSet(const UConverter *cnv, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode) { + /* HZ converts all of ASCII */ + sa->addRange(sa->set, 0, 0x7f); + + /* add all of the code points that the sub-converter handles */ + ucnv_MBCSGetFilteredUnicodeSetForUnicode( + ((UConverterDataHZ*)cnv->extraInfo)->gbConverter->sharedData, + sa, which, UCNV_SET_FILTER_HZ, + pErrorCode); +} +U_CDECL_END +static const UConverterImpl _HZImpl={ + + UCNV_HZ, + + nullptr, + nullptr, + + _HZOpen, + _HZClose, + _HZReset, + + UConverter_toUnicode_HZ_OFFSETS_LOGIC, + UConverter_toUnicode_HZ_OFFSETS_LOGIC, + UConverter_fromUnicode_HZ_OFFSETS_LOGIC, + UConverter_fromUnicode_HZ_OFFSETS_LOGIC, + nullptr, + + nullptr, + nullptr, + _HZ_WriteSub, + _HZ_SafeClone, + _HZ_GetUnicodeSet, + nullptr, + nullptr +}; + +static const UConverterStaticData _HZStaticData={ + sizeof(UConverterStaticData), + "HZ", + 0, + UCNV_IBM, + UCNV_HZ, + 1, + 4, + { 0x1a, 0, 0, 0 }, + 1, + false, + false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, /* reserved */ + +}; + +const UConverterSharedData _HZData= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_HZStaticData, &_HZImpl); + +#endif /* #if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION */ diff --git a/deps/icu-small/source/common/ucnvisci.cpp b/deps/icu-small/source/common/ucnvisci.cpp index 4d747e1ff843ba..8d33be6662aefb 100644 --- a/deps/icu-small/source/common/ucnvisci.cpp +++ b/deps/icu-small/source/common/ucnvisci.cpp @@ -1,1635 +1,1635 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2000-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: ucnvisci.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2001JUN26 -* created by: Ram Viswanadha -* -* Date Name Description -* 24/7/2001 Ram Added support for EXT character handling -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/ucnv_cb.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" -#include "cstring.h" -#include "uassert.h" - -#define UCNV_OPTIONS_VERSION_MASK 0xf -#define NUKTA 0x093c -#define HALANT 0x094d -#define ZWNJ 0x200c /* Zero Width Non Joiner */ -#define ZWJ 0x200d /* Zero width Joiner */ -#define INVALID_CHAR 0xffff -#define ATR 0xEF /* Attribute code */ -#define EXT 0xF0 /* Extension code */ -#define DANDA 0x0964 -#define DOUBLE_DANDA 0x0965 -#define ISCII_NUKTA 0xE9 -#define ISCII_HALANT 0xE8 -#define ISCII_DANDA 0xEA -#define ISCII_INV 0xD9 -#define ISCII_VOWEL_SIGN_E 0xE0 -#define INDIC_BLOCK_BEGIN 0x0900 -#define INDIC_BLOCK_END 0x0D7F -#define INDIC_RANGE (INDIC_BLOCK_END - INDIC_BLOCK_BEGIN) -#define VOCALLIC_RR 0x0931 -#define LF 0x0A -#define ASCII_END 0xA0 -#define NO_CHAR_MARKER 0xFFFE -#define TELUGU_DELTA DELTA * TELUGU -#define DEV_ABBR_SIGN 0x0970 -#define DEV_ANUDATTA 0x0952 -#define EXT_RANGE_BEGIN 0xA1 -#define EXT_RANGE_END 0xEE - -#define PNJ_DELTA 0x0100 -#define PNJ_BINDI 0x0A02 -#define PNJ_TIPPI 0x0A70 -#define PNJ_SIGN_VIRAMA 0x0A4D -#define PNJ_ADHAK 0x0A71 -#define PNJ_HA 0x0A39 -#define PNJ_RRA 0x0A5C - -typedef enum { - DEVANAGARI =0, - BENGALI, - GURMUKHI, - GUJARATI, - ORIYA, - TAMIL, - TELUGU, - KANNADA, - MALAYALAM, - DELTA=0x80 -}UniLang; - -/** - * Enumeration for switching code pages if + - * is encountered - */ -typedef enum { - DEF = 0x40, - RMN = 0x41, - DEV = 0x42, - BNG = 0x43, - TML = 0x44, - TLG = 0x45, - ASM = 0x46, - ORI = 0x47, - KND = 0x48, - MLM = 0x49, - GJR = 0x4A, - PNJ = 0x4B, - ARB = 0x71, - PES = 0x72, - URD = 0x73, - SND = 0x74, - KSM = 0x75, - PST = 0x76 -}ISCIILang; - -typedef enum { - DEV_MASK =0x80, - PNJ_MASK =0x40, - GJR_MASK =0x20, - ORI_MASK =0x10, - BNG_MASK =0x08, - KND_MASK =0x04, - MLM_MASK =0x02, - TML_MASK =0x01, - ZERO =0x00 -}MaskEnum; - -#define ISCII_CNV_PREFIX "ISCII,version=" - -typedef struct { - UChar contextCharToUnicode; /* previous Unicode codepoint for contextual analysis */ - UChar contextCharFromUnicode; /* previous Unicode codepoint for contextual analysis */ - uint16_t defDeltaToUnicode; /* delta for switching to default state when DEF is encountered */ - uint16_t currentDeltaFromUnicode; /* current delta in Indic block */ - uint16_t currentDeltaToUnicode; /* current delta in Indic block */ - MaskEnum currentMaskFromUnicode; /* mask for current state in toUnicode */ - MaskEnum currentMaskToUnicode; /* mask for current state in toUnicode */ - MaskEnum defMaskToUnicode; /* mask for default state in toUnicode */ - UBool isFirstBuffer; /* boolean for fromUnicode to see if we need to announce the first script */ - UBool resetToDefaultToUnicode; /* boolean for resetting to default delta and mask when a newline is encountered*/ - char name[sizeof(ISCII_CNV_PREFIX) + 1]; - UChar32 prevToUnicodeStatus; /* Hold the previous toUnicodeStatus. This is necessary because we may need to know the last two code points. */ -} UConverterDataISCII; - -typedef struct LookupDataStruct { - UniLang uniLang; - MaskEnum maskEnum; - ISCIILang isciiLang; -} LookupDataStruct; - -static const LookupDataStruct lookupInitialData[]={ - { DEVANAGARI, DEV_MASK, DEV }, - { BENGALI, BNG_MASK, BNG }, - { GURMUKHI, PNJ_MASK, PNJ }, - { GUJARATI, GJR_MASK, GJR }, - { ORIYA, ORI_MASK, ORI }, - { TAMIL, TML_MASK, TML }, - { TELUGU, KND_MASK, TLG }, - { KANNADA, KND_MASK, KND }, - { MALAYALAM, MLM_MASK, MLM } -}; - -/* - * For special handling of certain Gurmukhi characters. - * Bit 0 (value 1): PNJ consonant - * Bit 1 (value 2): PNJ Bindi Tippi - */ -static const uint8_t pnjMap[80] = { - /* 0A00..0A0F */ - 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, - /* 0A10..0A1F */ - 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - /* 0A20..0A2F */ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, - /* 0A30..0A3F */ - 3, 0, 0, 0, 0, 3, 3, 0, 3, 3, 0, 0, 0, 0, 0, 2, - /* 0A40..0A4F */ - 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static UBool -isPNJConsonant(UChar32 c) { - if (c < 0xa00 || 0xa50 <= c) { - return false; - } else { - return (UBool)(pnjMap[c - 0xa00] & 1); - } -} - -static UBool -isPNJBindiTippi(UChar32 c) { - if (c < 0xa00 || 0xa50 <= c) { - return false; - } else { - return (UBool)(pnjMap[c - 0xa00] >> 1); - } -} -U_CDECL_BEGIN -static void U_CALLCONV -_ISCIIOpen(UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *errorCode) { - if(pArgs->onlyTestIsLoadable) { - return; - } - - cnv->extraInfo = uprv_malloc(sizeof(UConverterDataISCII)); - - if (cnv->extraInfo != NULL) { - int32_t len=0; - UConverterDataISCII *converterData= - (UConverterDataISCII *) cnv->extraInfo; - converterData->contextCharToUnicode=NO_CHAR_MARKER; - cnv->toUnicodeStatus = missingCharMarker; - converterData->contextCharFromUnicode=0x0000; - converterData->resetToDefaultToUnicode=false; - /* check if the version requested is supported */ - if ((pArgs->options & UCNV_OPTIONS_VERSION_MASK) < 9) { - /* initialize state variables */ - converterData->currentDeltaFromUnicode - = converterData->currentDeltaToUnicode - = converterData->defDeltaToUnicode = (uint16_t)(lookupInitialData[pArgs->options & UCNV_OPTIONS_VERSION_MASK].uniLang * DELTA); - - converterData->currentMaskFromUnicode - = converterData->currentMaskToUnicode - = converterData->defMaskToUnicode = lookupInitialData[pArgs->options & UCNV_OPTIONS_VERSION_MASK].maskEnum; - - converterData->isFirstBuffer=true; - (void)uprv_strcpy(converterData->name, ISCII_CNV_PREFIX); - len = (int32_t)uprv_strlen(converterData->name); - converterData->name[len]= (char)((pArgs->options & UCNV_OPTIONS_VERSION_MASK) + '0'); - converterData->name[len+1]=0; - - converterData->prevToUnicodeStatus = 0x0000; - } else { - uprv_free(cnv->extraInfo); - cnv->extraInfo = NULL; - *errorCode = U_ILLEGAL_ARGUMENT_ERROR; - } - - } else { - *errorCode =U_MEMORY_ALLOCATION_ERROR; - } -} - -static void U_CALLCONV -_ISCIIClose(UConverter *cnv) { - if (cnv->extraInfo!=NULL) { - if (!cnv->isExtraLocal) { - uprv_free(cnv->extraInfo); - } - cnv->extraInfo=NULL; - } -} - -static const char* U_CALLCONV -_ISCIIgetName(const UConverter* cnv) { - if (cnv->extraInfo) { - UConverterDataISCII* myData= (UConverterDataISCII*)cnv->extraInfo; - return myData->name; - } - return NULL; -} - -static void U_CALLCONV -_ISCIIReset(UConverter *cnv, UConverterResetChoice choice) { - UConverterDataISCII* data =(UConverterDataISCII *) (cnv->extraInfo); - if (choice<=UCNV_RESET_TO_UNICODE) { - cnv->toUnicodeStatus = missingCharMarker; - cnv->mode=0; - data->currentDeltaToUnicode=data->defDeltaToUnicode; - data->currentMaskToUnicode = data->defMaskToUnicode; - data->contextCharToUnicode=NO_CHAR_MARKER; - data->prevToUnicodeStatus = 0x0000; - } - if (choice!=UCNV_RESET_TO_UNICODE) { - cnv->fromUChar32=0x0000; - data->contextCharFromUnicode=0x00; - data->currentMaskFromUnicode=data->defMaskToUnicode; - data->currentDeltaFromUnicode=data->defDeltaToUnicode; - data->isFirstBuffer=true; - data->resetToDefaultToUnicode=false; - } -} - -/** - * The values in validity table are indexed by the lower bits of Unicode - * range 0x0900 - 0x09ff. The values have a structure like: - * --------------------------------------------------------------- - * | DEV | PNJ | GJR | ORI | BNG | TLG | MLM | TML | - * | | | | | ASM | KND | | | - * --------------------------------------------------------------- - * If a code point is valid in a particular script - * then that bit is turned on - * - * Unicode does not distinguish between Bengali and Assamese so we use 1 bit for - * to represent these languages - * - * Telugu and Kannada have same codepoints except for Vocallic_RR which we special case - * and combine and use 1 bit to represent these languages. - * - * TODO: It is probably easier to understand and maintain to change this - * to use uint16_t and give each of the 9 Unicode/script blocks its own bit. - */ - -static const uint8_t validityTable[128] = { -/* This state table is tool generated please do not edit unless you know exactly what you are doing */ -/* Note: This table was edited to mirror the Windows XP implementation */ -/*ISCII:Valid:Unicode */ -/*0xa0 : 0x00: 0x900 */ ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0xa1 : 0xb8: 0x901 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + ZERO + ZERO + ZERO , -/*0xa2 : 0xfe: 0x902 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xa3 : 0xbf: 0x903 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0x00 : 0x00: 0x904 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0xa4 : 0xff: 0x905 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xa5 : 0xff: 0x906 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xa6 : 0xff: 0x907 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xa7 : 0xff: 0x908 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xa8 : 0xff: 0x909 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xa9 : 0xff: 0x90a */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xaa : 0xfe: 0x90b */ DEV_MASK + ZERO + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0x00 : 0x00: 0x90c */ DEV_MASK + ZERO + ZERO + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xae : 0x80: 0x90d */ DEV_MASK + ZERO + GJR_MASK + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0xab : 0x87: 0x90e */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + KND_MASK + MLM_MASK + TML_MASK , -/*0xac : 0xff: 0x90f */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xad : 0xff: 0x910 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xb2 : 0x80: 0x911 */ DEV_MASK + ZERO + GJR_MASK + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0xaf : 0x87: 0x912 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + KND_MASK + MLM_MASK + TML_MASK , -/*0xb0 : 0xff: 0x913 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xb1 : 0xff: 0x914 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xb3 : 0xff: 0x915 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xb4 : 0xfe: 0x916 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xb5 : 0xfe: 0x917 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xb6 : 0xfe: 0x918 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xb7 : 0xff: 0x919 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xb8 : 0xff: 0x91a */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xb9 : 0xfe: 0x91b */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xba : 0xff: 0x91c */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xbb : 0xfe: 0x91d */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xbc : 0xff: 0x91e */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xbd : 0xff: 0x91f */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xbe : 0xfe: 0x920 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xbf : 0xfe: 0x921 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xc0 : 0xfe: 0x922 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xc1 : 0xff: 0x923 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xc2 : 0xff: 0x924 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xc3 : 0xfe: 0x925 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xc4 : 0xfe: 0x926 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xc5 : 0xfe: 0x927 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xc6 : 0xff: 0x928 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xc7 : 0x81: 0x929 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + TML_MASK , -/*0xc8 : 0xff: 0x92a */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xc9 : 0xfe: 0x92b */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xca : 0xfe: 0x92c */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xcb : 0xfe: 0x92d */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xcc : 0xfe: 0x92e */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xcd : 0xff: 0x92f */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xcf : 0xff: 0x930 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xd0 : 0x87: 0x931 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + MLM_MASK + TML_MASK , -/*0xd1 : 0xff: 0x932 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xd2 : 0xb7: 0x933 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + ZERO + KND_MASK + MLM_MASK + TML_MASK , -/*0xd3 : 0x83: 0x934 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + MLM_MASK + TML_MASK , -/*0xd4 : 0xff: 0x935 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + ZERO + KND_MASK + MLM_MASK + TML_MASK , -/*0xd5 : 0xfe: 0x936 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0xd6 : 0xbf: 0x937 */ DEV_MASK + ZERO + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xd7 : 0xff: 0x938 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xd8 : 0xff: 0x939 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0x00 : 0x00: 0x93A */ ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x93B */ ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0xe9 : 0xda: 0x93c */ DEV_MASK + PNJ_MASK + ZERO + ORI_MASK + BNG_MASK + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x93d */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0xda : 0xff: 0x93e */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xdb : 0xff: 0x93f */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xdc : 0xff: 0x940 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xdd : 0xff: 0x941 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xde : 0xff: 0x942 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xdf : 0xbe: 0x943 */ DEV_MASK + ZERO + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0x00 : 0x00: 0x944 */ DEV_MASK + ZERO + GJR_MASK + ZERO + BNG_MASK + KND_MASK + ZERO + ZERO , -/*0xe3 : 0x80: 0x945 */ DEV_MASK + ZERO + GJR_MASK + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0xe0 : 0x87: 0x946 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + KND_MASK + MLM_MASK + TML_MASK , -/*0xe1 : 0xff: 0x947 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xe2 : 0xff: 0x948 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xe7 : 0x80: 0x949 */ DEV_MASK + ZERO + GJR_MASK + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0xe4 : 0x87: 0x94a */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + KND_MASK + MLM_MASK + TML_MASK , -/*0xe5 : 0xff: 0x94b */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xe6 : 0xff: 0x94c */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xe8 : 0xff: 0x94d */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xec : 0x00: 0x94e */ ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0xed : 0x00: 0x94f */ ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x950 */ DEV_MASK + ZERO + GJR_MASK + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x951 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x952 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x953 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x954 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x955 */ ZERO + ZERO + ZERO + ZERO + ZERO + KND_MASK + ZERO + ZERO , -/*0x00 : 0x00: 0x956 */ ZERO + ZERO + ZERO + ORI_MASK + ZERO + KND_MASK + ZERO + ZERO , -/*0x00 : 0x00: 0x957 */ ZERO + ZERO + ZERO + ORI_MASK + BNG_MASK + ZERO + MLM_MASK + ZERO , -/*0x00 : 0x00: 0x958 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x959 */ DEV_MASK + PNJ_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x95a */ DEV_MASK + PNJ_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x95b */ DEV_MASK + PNJ_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x95c */ DEV_MASK + PNJ_MASK + ZERO + ZERO + BNG_MASK + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x95d */ DEV_MASK + ZERO + ZERO + ORI_MASK + BNG_MASK + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x95e */ DEV_MASK + PNJ_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0xce : 0x98: 0x95f */ DEV_MASK + ZERO + ZERO + ORI_MASK + BNG_MASK + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x960 */ DEV_MASK + ZERO + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0x00 : 0x00: 0x961 */ DEV_MASK + ZERO + ZERO + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , -/*0x00 : 0x00: 0x962 */ DEV_MASK + ZERO + ZERO + ZERO + BNG_MASK + ZERO + ZERO + ZERO , -/*0x00 : 0x00: 0x963 */ DEV_MASK + ZERO + ZERO + ZERO + BNG_MASK + ZERO + ZERO + ZERO , -/*0xea : 0xf8: 0x964 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0xeaea : 0x00: 0x965*/ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/*0xf1 : 0xff: 0x966 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xf2 : 0xff: 0x967 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xf3 : 0xff: 0x968 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xf4 : 0xff: 0x969 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xf5 : 0xff: 0x96a */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xf6 : 0xff: 0x96b */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xf7 : 0xff: 0x96c */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xf8 : 0xff: 0x96d */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xf9 : 0xff: 0x96e */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0xfa : 0xff: 0x96f */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , -/*0x00 : 0x80: 0x970 */ DEV_MASK + PNJ_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , -/* - * The length of the array is 128 to provide values for 0x900..0x97f. - * The last 15 entries for 0x971..0x97f of the validity table are all zero - * because no Indic script uses such Unicode code points. - */ -/*0x00 : 0x00: 0x9yz */ ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO -}; - -static const uint16_t fromUnicodeTable[128]={ - 0x00a0 ,/* 0x0900 */ - 0x00a1 ,/* 0x0901 */ - 0x00a2 ,/* 0x0902 */ - 0x00a3 ,/* 0x0903 */ - 0xa4e0 ,/* 0x0904 */ - 0x00a4 ,/* 0x0905 */ - 0x00a5 ,/* 0x0906 */ - 0x00a6 ,/* 0x0907 */ - 0x00a7 ,/* 0x0908 */ - 0x00a8 ,/* 0x0909 */ - 0x00a9 ,/* 0x090a */ - 0x00aa ,/* 0x090b */ - 0xA6E9 ,/* 0x090c */ - 0x00ae ,/* 0x090d */ - 0x00ab ,/* 0x090e */ - 0x00ac ,/* 0x090f */ - 0x00ad ,/* 0x0910 */ - 0x00b2 ,/* 0x0911 */ - 0x00af ,/* 0x0912 */ - 0x00b0 ,/* 0x0913 */ - 0x00b1 ,/* 0x0914 */ - 0x00b3 ,/* 0x0915 */ - 0x00b4 ,/* 0x0916 */ - 0x00b5 ,/* 0x0917 */ - 0x00b6 ,/* 0x0918 */ - 0x00b7 ,/* 0x0919 */ - 0x00b8 ,/* 0x091a */ - 0x00b9 ,/* 0x091b */ - 0x00ba ,/* 0x091c */ - 0x00bb ,/* 0x091d */ - 0x00bc ,/* 0x091e */ - 0x00bd ,/* 0x091f */ - 0x00be ,/* 0x0920 */ - 0x00bf ,/* 0x0921 */ - 0x00c0 ,/* 0x0922 */ - 0x00c1 ,/* 0x0923 */ - 0x00c2 ,/* 0x0924 */ - 0x00c3 ,/* 0x0925 */ - 0x00c4 ,/* 0x0926 */ - 0x00c5 ,/* 0x0927 */ - 0x00c6 ,/* 0x0928 */ - 0x00c7 ,/* 0x0929 */ - 0x00c8 ,/* 0x092a */ - 0x00c9 ,/* 0x092b */ - 0x00ca ,/* 0x092c */ - 0x00cb ,/* 0x092d */ - 0x00cc ,/* 0x092e */ - 0x00cd ,/* 0x092f */ - 0x00cf ,/* 0x0930 */ - 0x00d0 ,/* 0x0931 */ - 0x00d1 ,/* 0x0932 */ - 0x00d2 ,/* 0x0933 */ - 0x00d3 ,/* 0x0934 */ - 0x00d4 ,/* 0x0935 */ - 0x00d5 ,/* 0x0936 */ - 0x00d6 ,/* 0x0937 */ - 0x00d7 ,/* 0x0938 */ - 0x00d8 ,/* 0x0939 */ - 0xFFFF ,/* 0x093A */ - 0xFFFF ,/* 0x093B */ - 0x00e9 ,/* 0x093c */ - 0xEAE9 ,/* 0x093d */ - 0x00da ,/* 0x093e */ - 0x00db ,/* 0x093f */ - 0x00dc ,/* 0x0940 */ - 0x00dd ,/* 0x0941 */ - 0x00de ,/* 0x0942 */ - 0x00df ,/* 0x0943 */ - 0xDFE9 ,/* 0x0944 */ - 0x00e3 ,/* 0x0945 */ - 0x00e0 ,/* 0x0946 */ - 0x00e1 ,/* 0x0947 */ - 0x00e2 ,/* 0x0948 */ - 0x00e7 ,/* 0x0949 */ - 0x00e4 ,/* 0x094a */ - 0x00e5 ,/* 0x094b */ - 0x00e6 ,/* 0x094c */ - 0x00e8 ,/* 0x094d */ - 0x00ec ,/* 0x094e */ - 0x00ed ,/* 0x094f */ - 0xA1E9 ,/* 0x0950 */ /* OM Symbol */ - 0xFFFF ,/* 0x0951 */ - 0xF0B8 ,/* 0x0952 */ - 0xFFFF ,/* 0x0953 */ - 0xFFFF ,/* 0x0954 */ - 0xFFFF ,/* 0x0955 */ - 0xFFFF ,/* 0x0956 */ - 0xFFFF ,/* 0x0957 */ - 0xb3e9 ,/* 0x0958 */ - 0xb4e9 ,/* 0x0959 */ - 0xb5e9 ,/* 0x095a */ - 0xbae9 ,/* 0x095b */ - 0xbfe9 ,/* 0x095c */ - 0xC0E9 ,/* 0x095d */ - 0xc9e9 ,/* 0x095e */ - 0x00ce ,/* 0x095f */ - 0xAAe9 ,/* 0x0960 */ - 0xA7E9 ,/* 0x0961 */ - 0xDBE9 ,/* 0x0962 */ - 0xDCE9 ,/* 0x0963 */ - 0x00ea ,/* 0x0964 */ - 0xeaea ,/* 0x0965 */ - 0x00f1 ,/* 0x0966 */ - 0x00f2 ,/* 0x0967 */ - 0x00f3 ,/* 0x0968 */ - 0x00f4 ,/* 0x0969 */ - 0x00f5 ,/* 0x096a */ - 0x00f6 ,/* 0x096b */ - 0x00f7 ,/* 0x096c */ - 0x00f8 ,/* 0x096d */ - 0x00f9 ,/* 0x096e */ - 0x00fa ,/* 0x096f */ - 0xF0BF ,/* 0x0970 */ - 0xFFFF ,/* 0x0971 */ - 0xFFFF ,/* 0x0972 */ - 0xFFFF ,/* 0x0973 */ - 0xFFFF ,/* 0x0974 */ - 0xFFFF ,/* 0x0975 */ - 0xFFFF ,/* 0x0976 */ - 0xFFFF ,/* 0x0977 */ - 0xFFFF ,/* 0x0978 */ - 0xFFFF ,/* 0x0979 */ - 0xFFFF ,/* 0x097a */ - 0xFFFF ,/* 0x097b */ - 0xFFFF ,/* 0x097c */ - 0xFFFF ,/* 0x097d */ - 0xFFFF ,/* 0x097e */ - 0xFFFF ,/* 0x097f */ -}; -static const uint16_t toUnicodeTable[256]={ - 0x0000,/* 0x00 */ - 0x0001,/* 0x01 */ - 0x0002,/* 0x02 */ - 0x0003,/* 0x03 */ - 0x0004,/* 0x04 */ - 0x0005,/* 0x05 */ - 0x0006,/* 0x06 */ - 0x0007,/* 0x07 */ - 0x0008,/* 0x08 */ - 0x0009,/* 0x09 */ - 0x000a,/* 0x0a */ - 0x000b,/* 0x0b */ - 0x000c,/* 0x0c */ - 0x000d,/* 0x0d */ - 0x000e,/* 0x0e */ - 0x000f,/* 0x0f */ - 0x0010,/* 0x10 */ - 0x0011,/* 0x11 */ - 0x0012,/* 0x12 */ - 0x0013,/* 0x13 */ - 0x0014,/* 0x14 */ - 0x0015,/* 0x15 */ - 0x0016,/* 0x16 */ - 0x0017,/* 0x17 */ - 0x0018,/* 0x18 */ - 0x0019,/* 0x19 */ - 0x001a,/* 0x1a */ - 0x001b,/* 0x1b */ - 0x001c,/* 0x1c */ - 0x001d,/* 0x1d */ - 0x001e,/* 0x1e */ - 0x001f,/* 0x1f */ - 0x0020,/* 0x20 */ - 0x0021,/* 0x21 */ - 0x0022,/* 0x22 */ - 0x0023,/* 0x23 */ - 0x0024,/* 0x24 */ - 0x0025,/* 0x25 */ - 0x0026,/* 0x26 */ - 0x0027,/* 0x27 */ - 0x0028,/* 0x28 */ - 0x0029,/* 0x29 */ - 0x002a,/* 0x2a */ - 0x002b,/* 0x2b */ - 0x002c,/* 0x2c */ - 0x002d,/* 0x2d */ - 0x002e,/* 0x2e */ - 0x002f,/* 0x2f */ - 0x0030,/* 0x30 */ - 0x0031,/* 0x31 */ - 0x0032,/* 0x32 */ - 0x0033,/* 0x33 */ - 0x0034,/* 0x34 */ - 0x0035,/* 0x35 */ - 0x0036,/* 0x36 */ - 0x0037,/* 0x37 */ - 0x0038,/* 0x38 */ - 0x0039,/* 0x39 */ - 0x003A,/* 0x3A */ - 0x003B,/* 0x3B */ - 0x003c,/* 0x3c */ - 0x003d,/* 0x3d */ - 0x003e,/* 0x3e */ - 0x003f,/* 0x3f */ - 0x0040,/* 0x40 */ - 0x0041,/* 0x41 */ - 0x0042,/* 0x42 */ - 0x0043,/* 0x43 */ - 0x0044,/* 0x44 */ - 0x0045,/* 0x45 */ - 0x0046,/* 0x46 */ - 0x0047,/* 0x47 */ - 0x0048,/* 0x48 */ - 0x0049,/* 0x49 */ - 0x004a,/* 0x4a */ - 0x004b,/* 0x4b */ - 0x004c,/* 0x4c */ - 0x004d,/* 0x4d */ - 0x004e,/* 0x4e */ - 0x004f,/* 0x4f */ - 0x0050,/* 0x50 */ - 0x0051,/* 0x51 */ - 0x0052,/* 0x52 */ - 0x0053,/* 0x53 */ - 0x0054,/* 0x54 */ - 0x0055,/* 0x55 */ - 0x0056,/* 0x56 */ - 0x0057,/* 0x57 */ - 0x0058,/* 0x58 */ - 0x0059,/* 0x59 */ - 0x005a,/* 0x5a */ - 0x005b,/* 0x5b */ - 0x005c,/* 0x5c */ - 0x005d,/* 0x5d */ - 0x005e,/* 0x5e */ - 0x005f,/* 0x5f */ - 0x0060,/* 0x60 */ - 0x0061,/* 0x61 */ - 0x0062,/* 0x62 */ - 0x0063,/* 0x63 */ - 0x0064,/* 0x64 */ - 0x0065,/* 0x65 */ - 0x0066,/* 0x66 */ - 0x0067,/* 0x67 */ - 0x0068,/* 0x68 */ - 0x0069,/* 0x69 */ - 0x006a,/* 0x6a */ - 0x006b,/* 0x6b */ - 0x006c,/* 0x6c */ - 0x006d,/* 0x6d */ - 0x006e,/* 0x6e */ - 0x006f,/* 0x6f */ - 0x0070,/* 0x70 */ - 0x0071,/* 0x71 */ - 0x0072,/* 0x72 */ - 0x0073,/* 0x73 */ - 0x0074,/* 0x74 */ - 0x0075,/* 0x75 */ - 0x0076,/* 0x76 */ - 0x0077,/* 0x77 */ - 0x0078,/* 0x78 */ - 0x0079,/* 0x79 */ - 0x007a,/* 0x7a */ - 0x007b,/* 0x7b */ - 0x007c,/* 0x7c */ - 0x007d,/* 0x7d */ - 0x007e,/* 0x7e */ - 0x007f,/* 0x7f */ - 0x0080,/* 0x80 */ - 0x0081,/* 0x81 */ - 0x0082,/* 0x82 */ - 0x0083,/* 0x83 */ - 0x0084,/* 0x84 */ - 0x0085,/* 0x85 */ - 0x0086,/* 0x86 */ - 0x0087,/* 0x87 */ - 0x0088,/* 0x88 */ - 0x0089,/* 0x89 */ - 0x008a,/* 0x8a */ - 0x008b,/* 0x8b */ - 0x008c,/* 0x8c */ - 0x008d,/* 0x8d */ - 0x008e,/* 0x8e */ - 0x008f,/* 0x8f */ - 0x0090,/* 0x90 */ - 0x0091,/* 0x91 */ - 0x0092,/* 0x92 */ - 0x0093,/* 0x93 */ - 0x0094,/* 0x94 */ - 0x0095,/* 0x95 */ - 0x0096,/* 0x96 */ - 0x0097,/* 0x97 */ - 0x0098,/* 0x98 */ - 0x0099,/* 0x99 */ - 0x009a,/* 0x9a */ - 0x009b,/* 0x9b */ - 0x009c,/* 0x9c */ - 0x009d,/* 0x9d */ - 0x009e,/* 0x9e */ - 0x009f,/* 0x9f */ - 0x00A0,/* 0xa0 */ - 0x0901,/* 0xa1 */ - 0x0902,/* 0xa2 */ - 0x0903,/* 0xa3 */ - 0x0905,/* 0xa4 */ - 0x0906,/* 0xa5 */ - 0x0907,/* 0xa6 */ - 0x0908,/* 0xa7 */ - 0x0909,/* 0xa8 */ - 0x090a,/* 0xa9 */ - 0x090b,/* 0xaa */ - 0x090e,/* 0xab */ - 0x090f,/* 0xac */ - 0x0910,/* 0xad */ - 0x090d,/* 0xae */ - 0x0912,/* 0xaf */ - 0x0913,/* 0xb0 */ - 0x0914,/* 0xb1 */ - 0x0911,/* 0xb2 */ - 0x0915,/* 0xb3 */ - 0x0916,/* 0xb4 */ - 0x0917,/* 0xb5 */ - 0x0918,/* 0xb6 */ - 0x0919,/* 0xb7 */ - 0x091a,/* 0xb8 */ - 0x091b,/* 0xb9 */ - 0x091c,/* 0xba */ - 0x091d,/* 0xbb */ - 0x091e,/* 0xbc */ - 0x091f,/* 0xbd */ - 0x0920,/* 0xbe */ - 0x0921,/* 0xbf */ - 0x0922,/* 0xc0 */ - 0x0923,/* 0xc1 */ - 0x0924,/* 0xc2 */ - 0x0925,/* 0xc3 */ - 0x0926,/* 0xc4 */ - 0x0927,/* 0xc5 */ - 0x0928,/* 0xc6 */ - 0x0929,/* 0xc7 */ - 0x092a,/* 0xc8 */ - 0x092b,/* 0xc9 */ - 0x092c,/* 0xca */ - 0x092d,/* 0xcb */ - 0x092e,/* 0xcc */ - 0x092f,/* 0xcd */ - 0x095f,/* 0xce */ - 0x0930,/* 0xcf */ - 0x0931,/* 0xd0 */ - 0x0932,/* 0xd1 */ - 0x0933,/* 0xd2 */ - 0x0934,/* 0xd3 */ - 0x0935,/* 0xd4 */ - 0x0936,/* 0xd5 */ - 0x0937,/* 0xd6 */ - 0x0938,/* 0xd7 */ - 0x0939,/* 0xd8 */ - 0x200D,/* 0xd9 */ - 0x093e,/* 0xda */ - 0x093f,/* 0xdb */ - 0x0940,/* 0xdc */ - 0x0941,/* 0xdd */ - 0x0942,/* 0xde */ - 0x0943,/* 0xdf */ - 0x0946,/* 0xe0 */ - 0x0947,/* 0xe1 */ - 0x0948,/* 0xe2 */ - 0x0945,/* 0xe3 */ - 0x094a,/* 0xe4 */ - 0x094b,/* 0xe5 */ - 0x094c,/* 0xe6 */ - 0x0949,/* 0xe7 */ - 0x094d,/* 0xe8 */ - 0x093c,/* 0xe9 */ - 0x0964,/* 0xea */ - 0xFFFF,/* 0xeb */ - 0xFFFF,/* 0xec */ - 0xFFFF,/* 0xed */ - 0xFFFF,/* 0xee */ - 0xFFFF,/* 0xef */ - 0xFFFF,/* 0xf0 */ - 0x0966,/* 0xf1 */ - 0x0967,/* 0xf2 */ - 0x0968,/* 0xf3 */ - 0x0969,/* 0xf4 */ - 0x096a,/* 0xf5 */ - 0x096b,/* 0xf6 */ - 0x096c,/* 0xf7 */ - 0x096d,/* 0xf8 */ - 0x096e,/* 0xf9 */ - 0x096f,/* 0xfa */ - 0xFFFF,/* 0xfb */ - 0xFFFF,/* 0xfc */ - 0xFFFF,/* 0xfd */ - 0xFFFF,/* 0xfe */ - 0xFFFF /* 0xff */ -}; - -static const uint16_t vowelSignESpecialCases[][2]={ - { 2 /*length of array*/ , 0 }, - { 0xA4 , 0x0904 }, -}; - -static const uint16_t nuktaSpecialCases[][2]={ - { 16 /*length of array*/ , 0 }, - { 0xA6 , 0x090c }, - { 0xEA , 0x093D }, - { 0xDF , 0x0944 }, - { 0xA1 , 0x0950 }, - { 0xb3 , 0x0958 }, - { 0xb4 , 0x0959 }, - { 0xb5 , 0x095a }, - { 0xba , 0x095b }, - { 0xbf , 0x095c }, - { 0xC0 , 0x095d }, - { 0xc9 , 0x095e }, - { 0xAA , 0x0960 }, - { 0xA7 , 0x0961 }, - { 0xDB , 0x0962 }, - { 0xDC , 0x0963 }, -}; - - -#define WRITE_TO_TARGET_FROM_U(args,offsets,source,target,targetLimit,targetByteUnit,err) UPRV_BLOCK_MACRO_BEGIN { \ - int32_t offset = (int32_t)(source - args->source-1); \ - /* write the targetUniChar to target */ \ - if(target < targetLimit){ \ - if(targetByteUnit <= 0xFF){ \ - *(target)++ = (uint8_t)(targetByteUnit); \ - if(offsets){ \ - *(offsets++) = offset; \ - } \ - }else{ \ - if (targetByteUnit > 0xFFFF) { \ - *(target)++ = (uint8_t)(targetByteUnit>>16); \ - if (offsets) { \ - --offset; \ - *(offsets++) = offset; \ - } \ - } \ - if (!(target < targetLimit)) { \ - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = \ - (uint8_t)(targetByteUnit >> 8); \ - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = \ - (uint8_t)targetByteUnit; \ - *err = U_BUFFER_OVERFLOW_ERROR; \ - } else { \ - *(target)++ = (uint8_t)(targetByteUnit>>8); \ - if(offsets){ \ - *(offsets++) = offset; \ - } \ - if(target < targetLimit){ \ - *(target)++ = (uint8_t) targetByteUnit; \ - if(offsets){ \ - *(offsets++) = offset ; \ - } \ - }else{ \ - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] =\ - (uint8_t) (targetByteUnit); \ - *err = U_BUFFER_OVERFLOW_ERROR; \ - } \ - } \ - } \ - }else{ \ - if (targetByteUnit & 0xFF0000) { \ - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = \ - (uint8_t) (targetByteUnit >>16); \ - } \ - if(targetByteUnit & 0xFF00){ \ - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = \ - (uint8_t) (targetByteUnit >>8); \ - } \ - args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = \ - (uint8_t) (targetByteUnit); \ - *err = U_BUFFER_OVERFLOW_ERROR; \ - } \ -} UPRV_BLOCK_MACRO_END - -/* Rules: - * Explicit Halant : - * + - * Soft Halant : - * + - */ -static void U_CALLCONV -UConverter_fromUnicode_ISCII_OFFSETS_LOGIC( - UConverterFromUnicodeArgs * args, UErrorCode * err) { - const UChar *source = args->source; - const UChar *sourceLimit = args->sourceLimit; - unsigned char *target = (unsigned char *) args->target; - unsigned char *targetLimit = (unsigned char *) args->targetLimit; - int32_t* offsets = args->offsets; - uint32_t targetByteUnit = 0x0000; - UChar32 sourceChar = 0x0000; - UChar32 tempContextFromUnicode = 0x0000; /* For special handling of the Gurmukhi script. */ - UConverterDataISCII *converterData; - uint16_t newDelta=0; - uint16_t range = 0; - UBool deltaChanged = false; - - if ((args->converter == NULL) || (args->targetLimit < args->target) || (args->sourceLimit < args->source)) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - /* initialize data */ - converterData=(UConverterDataISCII*)args->converter->extraInfo; - newDelta=converterData->currentDeltaFromUnicode; - range = (uint16_t)(newDelta/DELTA); - - if ((sourceChar = args->converter->fromUChar32)!=0) { - goto getTrail; - } - - /*writing the char to the output stream */ - while (source < sourceLimit) { - /* Write the language code following LF only if LF is not the last character. */ - if (args->converter->fromUnicodeStatus == LF) { - targetByteUnit = ATR<<8; - targetByteUnit += (uint8_t) lookupInitialData[range].isciiLang; - args->converter->fromUnicodeStatus = 0x0000; - /* now append ATR and language code */ - WRITE_TO_TARGET_FROM_U(args,offsets,source,target,targetLimit,targetByteUnit,err); - if (U_FAILURE(*err)) { - break; - } - } - - sourceChar = *source++; - tempContextFromUnicode = converterData->contextCharFromUnicode; - - targetByteUnit = missingCharMarker; - - /*check if input is in ASCII and C0 control codes range*/ - if (sourceChar <= ASCII_END) { - args->converter->fromUnicodeStatus = sourceChar; - WRITE_TO_TARGET_FROM_U(args,offsets,source,target,targetLimit,sourceChar,err); - if (U_FAILURE(*err)) { - break; - } - continue; - } - switch (sourceChar) { - case ZWNJ: - /* contextChar has HALANT */ - if (converterData->contextCharFromUnicode) { - converterData->contextCharFromUnicode = 0x00; - targetByteUnit = ISCII_HALANT; - } else { - /* consume ZWNJ and continue */ - converterData->contextCharFromUnicode = 0x00; - continue; - } - break; - case ZWJ: - /* contextChar has HALANT */ - if (converterData->contextCharFromUnicode) { - targetByteUnit = ISCII_NUKTA; - } else { - targetByteUnit =ISCII_INV; - } - converterData->contextCharFromUnicode = 0x00; - break; - default: - /* is the sourceChar in the INDIC_RANGE? */ - if ((uint16_t)(INDIC_BLOCK_END-sourceChar) <= INDIC_RANGE) { - /* Danda and Double Danda are valid in Northern scripts.. since Unicode - * does not include these codepoints in all Northern scrips we need to - * filter them out - */ - if (sourceChar!= DANDA && sourceChar != DOUBLE_DANDA) { - /* find out to which block the souceChar belongs*/ - range =(uint16_t)((sourceChar-INDIC_BLOCK_BEGIN)/DELTA); - newDelta =(uint16_t)(range*DELTA); - - /* Now are we in the same block as the previous? */ - if (newDelta!= converterData->currentDeltaFromUnicode || converterData->isFirstBuffer) { - converterData->currentDeltaFromUnicode = newDelta; - converterData->currentMaskFromUnicode = lookupInitialData[range].maskEnum; - deltaChanged =true; - converterData->isFirstBuffer=false; - } - - if (converterData->currentDeltaFromUnicode == PNJ_DELTA) { - if (sourceChar == PNJ_TIPPI) { - /* Make sure Tippi is converted to Bindi. */ - sourceChar = PNJ_BINDI; - } else if (sourceChar == PNJ_ADHAK) { - /* This is for consonant cluster handling. */ - converterData->contextCharFromUnicode = PNJ_ADHAK; - } - - } - /* Normalize all Indic codepoints to Devanagari and map them to ISCII */ - /* now subtract the new delta from sourceChar*/ - sourceChar -= converterData->currentDeltaFromUnicode; - } - - /* get the target byte unit */ - targetByteUnit=fromUnicodeTable[(uint8_t)sourceChar]; - - /* is the code point valid in current script? */ - if ((validityTable[(uint8_t)sourceChar] & converterData->currentMaskFromUnicode)==0) { - /* Vocallic RR is assigned in ISCII Telugu and Unicode */ - if (converterData->currentDeltaFromUnicode!=(TELUGU_DELTA) || sourceChar!=VOCALLIC_RR) { - targetByteUnit=missingCharMarker; - } - } - - if (deltaChanged) { - /* we are in a script block which is different than - * previous sourceChar's script block write ATR and language codes - */ - uint32_t temp=0; - temp =(uint16_t)(ATR<<8); - temp += (uint16_t)((uint8_t) lookupInitialData[range].isciiLang); - /* reset */ - deltaChanged=false; - /* now append ATR and language code */ - WRITE_TO_TARGET_FROM_U(args,offsets,source,target,targetLimit,temp,err); - if (U_FAILURE(*err)) { - break; - } - } - - if (converterData->currentDeltaFromUnicode == PNJ_DELTA && (sourceChar + PNJ_DELTA) == PNJ_ADHAK) { - continue; - } - } - /* reset context char */ - converterData->contextCharFromUnicode = 0x00; - break; - } - if (converterData->currentDeltaFromUnicode == PNJ_DELTA && tempContextFromUnicode == PNJ_ADHAK && isPNJConsonant((sourceChar + PNJ_DELTA))) { - /* If the previous codepoint is Adhak and the current codepoint is a consonant, the targetByteUnit should be C + Halant + C. */ - /* reset context char */ - converterData->contextCharFromUnicode = 0x0000; - targetByteUnit = targetByteUnit << 16 | ISCII_HALANT << 8 | targetByteUnit; - /* write targetByteUnit to target */ - WRITE_TO_TARGET_FROM_U(args, offsets, source, target, targetLimit, targetByteUnit,err); - if (U_FAILURE(*err)) { - break; - } - } else if (targetByteUnit != missingCharMarker) { - if (targetByteUnit==ISCII_HALANT) { - converterData->contextCharFromUnicode = (UChar)targetByteUnit; - } - /* write targetByteUnit to target*/ - WRITE_TO_TARGET_FROM_U(args,offsets,source,target,targetLimit,targetByteUnit,err); - if (U_FAILURE(*err)) { - break; - } - } else { - /* oops.. the code point is unassigned */ - /*check if the char is a First surrogate*/ - if (U16_IS_SURROGATE(sourceChar)) { - if (U16_IS_SURROGATE_LEAD(sourceChar)) { -getTrail: - /*look ahead to find the trail surrogate*/ - if (source < sourceLimit) { - /* test the following code unit */ - UChar trail= (*source); - if (U16_IS_TRAIL(trail)) { - source++; - sourceChar=U16_GET_SUPPLEMENTARY(sourceChar, trail); - *err =U_INVALID_CHAR_FOUND; - /* convert this surrogate code point */ - /* exit this condition tree */ - } else { - /* this is an unmatched lead code unit (1st surrogate) */ - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - } - } else { - /* no more input */ - *err = U_ZERO_ERROR; - } - } else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - *err=U_ILLEGAL_CHAR_FOUND; - } - } else { - /* callback(unassigned) for a BMP code point */ - *err = U_INVALID_CHAR_FOUND; - } - - args->converter->fromUChar32=sourceChar; - break; - } - }/* end while(mySourceIndexsource = source; - args->target = (char*)target; -} - -static const uint16_t lookupTable[][2]={ - { ZERO, ZERO }, /*DEFAULT*/ - { ZERO, ZERO }, /*ROMAN*/ - { DEVANAGARI, DEV_MASK }, - { BENGALI, BNG_MASK }, - { TAMIL, TML_MASK }, - { TELUGU, KND_MASK }, - { BENGALI, BNG_MASK }, - { ORIYA, ORI_MASK }, - { KANNADA, KND_MASK }, - { MALAYALAM, MLM_MASK }, - { GUJARATI, GJR_MASK }, - { GURMUKHI, PNJ_MASK } -}; - -#define WRITE_TO_TARGET_TO_U(args,source,target,offsets,offset,targetUniChar,delta, err) UPRV_BLOCK_MACRO_BEGIN { \ - /* add offset to current Indic Block */ \ - if(targetUniChar>ASCII_END && \ - targetUniChar != ZWJ && \ - targetUniChar != ZWNJ && \ - targetUniChar != DANDA && \ - targetUniChar != DOUBLE_DANDA){ \ - \ - targetUniChar+=(uint16_t)(delta); \ - } \ - /* now write the targetUniChar */ \ - if(targettargetLimit){ \ - *(target)++ = (UChar)targetUniChar; \ - if(offsets){ \ - *(offsets)++ = (int32_t)(offset); \ - } \ - }else{ \ - args->converter->UCharErrorBuffer[args->converter->UCharErrorBufferLength++] = \ - (UChar)targetUniChar; \ - *err = U_BUFFER_OVERFLOW_ERROR; \ - } \ -} UPRV_BLOCK_MACRO_END - -#define GET_MAPPING(sourceChar,targetUniChar,data) UPRV_BLOCK_MACRO_BEGIN { \ - targetUniChar = toUnicodeTable[(sourceChar)] ; \ - /* is the code point valid in current script? */ \ - if(sourceChar> ASCII_END && \ - (validityTable[(targetUniChar & 0x7F)] & data->currentMaskToUnicode)==0){ \ - /* Vocallic RR is assigned in ISCII Telugu and Unicode */ \ - if(data->currentDeltaToUnicode!=(TELUGU_DELTA) || \ - targetUniChar!=VOCALLIC_RR){ \ - targetUniChar=missingCharMarker; \ - } \ - } \ -} UPRV_BLOCK_MACRO_END - -/*********** - * Rules for ISCII to Unicode converter - * ISCII is stateful encoding. To convert ISCII bytes to Unicode, - * which has both precomposed and decomposed forms characters - * pre-context and post-context need to be considered. - * - * Post context - * i) ATR : Attribute code is used to declare the font and script switching. - * Currently we only switch scripts and font codes consumed without generating an error - * ii) EXT : Extension code is used to declare switching to Sanskrit and for obscure, - * obsolete characters - * Pre context - * i) Halant: if preceded by a halant then it is a explicit halant - * ii) Nukta : - * a) if preceded by a halant then it is a soft halant - * b) if preceded by specific consonants and the ligatures have pre-composed - * characters in Unicode then convert to pre-composed characters - * iii) Danda: If Danda is preceded by a Danda then convert to Double Danda - * - */ - -static void U_CALLCONV -UConverter_toUnicode_ISCII_OFFSETS_LOGIC(UConverterToUnicodeArgs *args, UErrorCode* err) { - const char *source = ( char *) args->source; - UChar *target = args->target; - const char *sourceLimit = args->sourceLimit; - const UChar* targetLimit = args->targetLimit; - uint32_t targetUniChar = 0x0000; - uint8_t sourceChar = 0x0000; - UConverterDataISCII* data; - UChar32* toUnicodeStatus=NULL; - UChar32 tempTargetUniChar = 0x0000; - UChar* contextCharToUnicode= NULL; - UBool found; - int i; - int offset = 0; - - if ((args->converter == NULL) || (target < args->target) || (source < args->source)) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - data = (UConverterDataISCII*)(args->converter->extraInfo); - contextCharToUnicode = &data->contextCharToUnicode; /* contains previous ISCII codepoint visited */ - toUnicodeStatus = (UChar32*)&args->converter->toUnicodeStatus;/* contains the mapping to Unicode of the above codepoint*/ - - while (U_SUCCESS(*err) && sourcecurrentDeltaToUnicode = (uint16_t)(lookupTable[sourceChar & 0x0F][0] * DELTA); - data->currentMaskToUnicode = (MaskEnum)lookupTable[sourceChar & 0x0F][1]; - } else if (sourceChar==DEF) { - /* switch back to default */ - data->currentDeltaToUnicode = data->defDeltaToUnicode; - data->currentMaskToUnicode = data->defMaskToUnicode; - } else { - if ((sourceChar >= 0x21 && sourceChar <= 0x3F)) { - /* these are display codes consume and continue */ - } else { - *err =U_ILLEGAL_CHAR_FOUND; - /* reset */ - *contextCharToUnicode=NO_CHAR_MARKER; - goto CALLBACK; - } - } - - /* reset */ - *contextCharToUnicode=NO_CHAR_MARKER; - - continue; - - } else if (*contextCharToUnicode==EXT) { - /* check if sourceChar is in 0xA1-0xEE range */ - if ((uint8_t) (EXT_RANGE_END - sourceChar) <= (EXT_RANGE_END - EXT_RANGE_BEGIN)) { - /* We currently support only Anudatta and Devanagari abbreviation sign */ - if (sourceChar==0xBF || sourceChar == 0xB8) { - targetUniChar = (sourceChar==0xBF) ? DEV_ABBR_SIGN : DEV_ANUDATTA; - - /* find out if the mapping is valid in this state */ - if (validityTable[(uint8_t)targetUniChar] & data->currentMaskToUnicode) { - *contextCharToUnicode= NO_CHAR_MARKER; - - /* Write the previous toUnicodeStatus, this was delayed to handle consonant clustering for Gurmukhi script. */ - if (data->prevToUnicodeStatus) { - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -1),data->prevToUnicodeStatus,0,err); - data->prevToUnicodeStatus = 0x0000; - } - /* write to target */ - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -2),targetUniChar,data->currentDeltaToUnicode,err); - - continue; - } - } - /* byte unit is unassigned */ - targetUniChar = missingCharMarker; - *err= U_INVALID_CHAR_FOUND; - } else { - /* only 0xA1 - 0xEE are legal after EXT char */ - *contextCharToUnicode= NO_CHAR_MARKER; - *err = U_ILLEGAL_CHAR_FOUND; - } - goto CALLBACK; - } else if (*contextCharToUnicode==ISCII_INV) { - if (sourceChar==ISCII_HALANT) { - targetUniChar = 0x0020; /* replace with space according to Indic FAQ */ - } else { - targetUniChar = ZWJ; - } - - /* Write the previous toUnicodeStatus, this was delayed to handle consonant clustering for Gurmukhi script. */ - if (data->prevToUnicodeStatus) { - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -1),data->prevToUnicodeStatus,0,err); - data->prevToUnicodeStatus = 0x0000; - } - /* write to target */ - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -2),targetUniChar,data->currentDeltaToUnicode,err); - /* reset */ - *contextCharToUnicode=NO_CHAR_MARKER; - } - - /* look at the pre-context and perform special processing */ - switch (sourceChar) { - case ISCII_INV: - case EXT: - case ATR: - *contextCharToUnicode = (UChar)sourceChar; - - if (*toUnicodeStatus != missingCharMarker) { - /* Write the previous toUnicodeStatus, this was delayed to handle consonant clustering for Gurmukhi script. */ - if (data->prevToUnicodeStatus) { - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -1),data->prevToUnicodeStatus,0,err); - data->prevToUnicodeStatus = 0x0000; - } - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -2),*toUnicodeStatus,data->currentDeltaToUnicode,err); - *toUnicodeStatus = missingCharMarker; - } - continue; - case ISCII_DANDA: - /* handle double danda*/ - if (*contextCharToUnicode== ISCII_DANDA) { - targetUniChar = DOUBLE_DANDA; - /* clear the context */ - *contextCharToUnicode = NO_CHAR_MARKER; - *toUnicodeStatus = missingCharMarker; - } else { - GET_MAPPING(sourceChar,targetUniChar,data); - *contextCharToUnicode = sourceChar; - } - break; - case ISCII_HALANT: - /* handle explicit halant */ - if (*contextCharToUnicode == ISCII_HALANT) { - targetUniChar = ZWNJ; - /* clear the context */ - *contextCharToUnicode = NO_CHAR_MARKER; - } else { - GET_MAPPING(sourceChar,targetUniChar,data); - *contextCharToUnicode = sourceChar; - } - break; - case 0x0A: - case 0x0D: - data->resetToDefaultToUnicode = true; - GET_MAPPING(sourceChar,targetUniChar,data) - ; - *contextCharToUnicode = sourceChar; - break; - - case ISCII_VOWEL_SIGN_E: - i=1; - found=false; - for (; icurrentMaskToUnicode) { - /*targetUniChar += data->currentDeltaToUnicode ;*/ - *contextCharToUnicode= NO_CHAR_MARKER; - *toUnicodeStatus = missingCharMarker; - break; - } - } - GET_MAPPING(sourceChar,targetUniChar,data); - *contextCharToUnicode = sourceChar; - break; - - case ISCII_NUKTA: - /* handle soft halant */ - if (*contextCharToUnicode == ISCII_HALANT) { - targetUniChar = ZWJ; - /* clear the context */ - *contextCharToUnicode = NO_CHAR_MARKER; - break; - } else if (data->currentDeltaToUnicode == PNJ_DELTA && data->contextCharToUnicode == 0xc0) { - /* Write the previous toUnicodeStatus, this was delayed to handle consonant clustering for Gurmukhi script. */ - if (data->prevToUnicodeStatus) { - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -1),data->prevToUnicodeStatus,0,err); - data->prevToUnicodeStatus = 0x0000; - } - /* We got here because ISCII_NUKTA was preceded by 0xc0 and we are converting Gurmukhi. - * In that case we must convert (0xc0 0xe9) to (\u0a5c\u0a4d\u0a39). - */ - targetUniChar = PNJ_RRA; - WRITE_TO_TARGET_TO_U(args, source, target, args->offsets, (source-args->source)-2, targetUniChar, 0, err); - if (U_SUCCESS(*err)) { - targetUniChar = PNJ_SIGN_VIRAMA; - WRITE_TO_TARGET_TO_U(args, source, target, args->offsets, (source-args->source)-2, targetUniChar, 0, err); - if (U_SUCCESS(*err)) { - targetUniChar = PNJ_HA; - WRITE_TO_TARGET_TO_U(args, source, target, args->offsets, (source-args->source)-2, targetUniChar, 0, err); - } else { - args->converter->UCharErrorBuffer[args->converter->UCharErrorBufferLength++]= PNJ_HA; - } - } else { - args->converter->UCharErrorBuffer[args->converter->UCharErrorBufferLength++]= PNJ_SIGN_VIRAMA; - args->converter->UCharErrorBuffer[args->converter->UCharErrorBufferLength++]= PNJ_HA; - } - *toUnicodeStatus = missingCharMarker; - data->contextCharToUnicode = NO_CHAR_MARKER; - continue; - } else { - /* try to handle + ISCII_NUKTA special mappings */ - i=1; - found =false; - for (; icurrentMaskToUnicode) { - /*targetUniChar += data->currentDeltaToUnicode ;*/ - *contextCharToUnicode= NO_CHAR_MARKER; - *toUnicodeStatus = missingCharMarker; - if (data->currentDeltaToUnicode == PNJ_DELTA) { - /* Write the previous toUnicodeStatus, this was delayed to handle consonant clustering for Gurmukhi script. */ - if (data->prevToUnicodeStatus) { - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -1),data->prevToUnicodeStatus,0,err); - data->prevToUnicodeStatus = 0x0000; - } - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -2),targetUniChar,data->currentDeltaToUnicode,err); - continue; - } - break; - } - /* else fall through to default */ - } - /* else fall through to default */ - U_FALLTHROUGH; - } - default:GET_MAPPING(sourceChar,targetUniChar,data) - ; - *contextCharToUnicode = sourceChar; - break; - } - - if (*toUnicodeStatus != missingCharMarker) { - /* Check to make sure that consonant clusters are handled correct for Gurmukhi script. */ - if (data->currentDeltaToUnicode == PNJ_DELTA && data->prevToUnicodeStatus != 0 && isPNJConsonant(data->prevToUnicodeStatus) && - (*toUnicodeStatus + PNJ_DELTA) == PNJ_SIGN_VIRAMA && ((UChar32)(targetUniChar + PNJ_DELTA) == data->prevToUnicodeStatus)) { - /* Consonant clusters C + HALANT + C should be encoded as ADHAK + C */ - offset = (int)(source-args->source - 3); - tempTargetUniChar = PNJ_ADHAK; /* This is necessary to avoid some compiler warnings. */ - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,offset,tempTargetUniChar,0,err); - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,offset,data->prevToUnicodeStatus,0,err); - data->prevToUnicodeStatus = 0x0000; /* reset the previous unicode code point */ - *toUnicodeStatus = missingCharMarker; - continue; - } else { - /* Write the previous toUnicodeStatus, this was delayed to handle consonant clustering for Gurmukhi script. */ - if (data->prevToUnicodeStatus) { - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -1),data->prevToUnicodeStatus,0,err); - data->prevToUnicodeStatus = 0x0000; - } - /* Check to make sure that Bindi and Tippi are handled correctly for Gurmukhi script. - * If 0xA2 is preceded by a codepoint in the PNJ_BINDI_TIPPI_SET then the target codepoint should be Tippi instead of Bindi. - */ - if (data->currentDeltaToUnicode == PNJ_DELTA && (targetUniChar + PNJ_DELTA) == PNJ_BINDI && isPNJBindiTippi((*toUnicodeStatus + PNJ_DELTA))) { - targetUniChar = PNJ_TIPPI - PNJ_DELTA; - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -2),*toUnicodeStatus,PNJ_DELTA,err); - } else if (data->currentDeltaToUnicode == PNJ_DELTA && (targetUniChar + PNJ_DELTA) == PNJ_SIGN_VIRAMA && isPNJConsonant((*toUnicodeStatus + PNJ_DELTA))) { - /* Store the current toUnicodeStatus code point for later handling of consonant cluster in Gurmukhi. */ - data->prevToUnicodeStatus = *toUnicodeStatus + PNJ_DELTA; - } else { - /* write the previously mapped codepoint */ - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -2),*toUnicodeStatus,data->currentDeltaToUnicode,err); - } - } - *toUnicodeStatus = missingCharMarker; - } - - if (targetUniChar != missingCharMarker) { - /* now save the targetUniChar for delayed write */ - *toUnicodeStatus = (UChar) targetUniChar; - if (data->resetToDefaultToUnicode==true) { - data->currentDeltaToUnicode = data->defDeltaToUnicode; - data->currentMaskToUnicode = data->defMaskToUnicode; - data->resetToDefaultToUnicode=false; - } - } else { - - /* we reach here only if targetUniChar == missingCharMarker - * so assign codes to reason and err - */ - *err = U_INVALID_CHAR_FOUND; -CALLBACK: - args->converter->toUBytes[0] = (uint8_t) sourceChar; - args->converter->toULength = 1; - break; - } - - } else { - *err =U_BUFFER_OVERFLOW_ERROR; - break; - } - } - - if (U_SUCCESS(*err) && args->flush && source == sourceLimit) { - /* end of the input stream */ - UConverter *cnv = args->converter; - - if (*contextCharToUnicode==ATR || *contextCharToUnicode==EXT || *contextCharToUnicode==ISCII_INV) { - /* set toUBytes[] */ - cnv->toUBytes[0] = (uint8_t)*contextCharToUnicode; - cnv->toULength = 1; - - /* avoid looping on truncated sequences */ - *contextCharToUnicode = NO_CHAR_MARKER; - } else { - cnv->toULength = 0; - } - - if (*toUnicodeStatus != missingCharMarker) { - /* output a remaining target character */ - WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source - args->source -1),*toUnicodeStatus,data->currentDeltaToUnicode,err); - *toUnicodeStatus = missingCharMarker; - } - } - - args->target = target; - args->source = source; -} - -/* structure for SafeClone calculations */ -struct cloneISCIIStruct { - UConverter cnv; - UConverterDataISCII mydata; -}; - -static UConverter * U_CALLCONV -_ISCII_SafeClone(const UConverter *cnv, - void *stackBuffer, - int32_t *pBufferSize, - UErrorCode *status) -{ - struct cloneISCIIStruct * localClone; - int32_t bufferSizeNeeded = sizeof(struct cloneISCIIStruct); - - if (U_FAILURE(*status)) { - return 0; - } - - if (*pBufferSize == 0) { /* 'preflighting' request - set needed size into *pBufferSize */ - *pBufferSize = bufferSizeNeeded; - return 0; - } - - localClone = (struct cloneISCIIStruct *)stackBuffer; - /* ucnv.c/ucnv_safeClone() copied the main UConverter already */ - - uprv_memcpy(&localClone->mydata, cnv->extraInfo, sizeof(UConverterDataISCII)); - localClone->cnv.extraInfo = &localClone->mydata; - localClone->cnv.isExtraLocal = true; - - return &localClone->cnv; -} - -static void U_CALLCONV -_ISCIIGetUnicodeSet(const UConverter *cnv, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode) -{ - (void)cnv; - (void)which; - (void)pErrorCode; - int32_t idx, script; - uint8_t mask; - - /* Since all ISCII versions allow switching to other ISCII - scripts, we add all roundtrippable characters to this set. */ - sa->addRange(sa->set, 0, ASCII_END); - for (script = DEVANAGARI; script <= MALAYALAM; script++) { - mask = (uint8_t)(lookupInitialData[script].maskEnum); - for (idx = 0; idx < DELTA; idx++) { - /* added check for TELUGU character */ - if ((validityTable[idx] & mask) || (script==TELUGU && idx==0x31)) { - sa->add(sa->set, idx + (script * DELTA) + INDIC_BLOCK_BEGIN); - } - } - } - sa->add(sa->set, DANDA); - sa->add(sa->set, DOUBLE_DANDA); - sa->add(sa->set, ZWNJ); - sa->add(sa->set, ZWJ); -} -U_CDECL_END -static const UConverterImpl _ISCIIImpl={ - - UCNV_ISCII, - - NULL, - NULL, - - _ISCIIOpen, - _ISCIIClose, - _ISCIIReset, - - UConverter_toUnicode_ISCII_OFFSETS_LOGIC, - UConverter_toUnicode_ISCII_OFFSETS_LOGIC, - UConverter_fromUnicode_ISCII_OFFSETS_LOGIC, - UConverter_fromUnicode_ISCII_OFFSETS_LOGIC, - NULL, - - NULL, - _ISCIIgetName, - NULL, - _ISCII_SafeClone, - _ISCIIGetUnicodeSet, - NULL, - NULL -}; - -static const UConverterStaticData _ISCIIStaticData={ - sizeof(UConverterStaticData), - "ISCII", - 0, - UCNV_IBM, - UCNV_ISCII, - 1, - 4, - { 0x1a, 0, 0, 0 }, - 0x1, - false, - false, - 0x0, - 0x0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, /* reserved */ - -}; - -const UConverterSharedData _ISCIIData= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_ISCIIStaticData, &_ISCIIImpl); - -#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2000-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: ucnvisci.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2001JUN26 +* created by: Ram Viswanadha +* +* Date Name Description +* 24/7/2001 Ram Added support for EXT character handling +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/ucnv_cb.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" +#include "cstring.h" +#include "uassert.h" + +#define UCNV_OPTIONS_VERSION_MASK 0xf +#define NUKTA 0x093c +#define HALANT 0x094d +#define ZWNJ 0x200c /* Zero Width Non Joiner */ +#define ZWJ 0x200d /* Zero width Joiner */ +#define INVALID_CHAR 0xffff +#define ATR 0xEF /* Attribute code */ +#define EXT 0xF0 /* Extension code */ +#define DANDA 0x0964 +#define DOUBLE_DANDA 0x0965 +#define ISCII_NUKTA 0xE9 +#define ISCII_HALANT 0xE8 +#define ISCII_DANDA 0xEA +#define ISCII_INV 0xD9 +#define ISCII_VOWEL_SIGN_E 0xE0 +#define INDIC_BLOCK_BEGIN 0x0900 +#define INDIC_BLOCK_END 0x0D7F +#define INDIC_RANGE (INDIC_BLOCK_END - INDIC_BLOCK_BEGIN) +#define VOCALLIC_RR 0x0931 +#define LF 0x0A +#define ASCII_END 0xA0 +#define NO_CHAR_MARKER 0xFFFE +#define TELUGU_DELTA DELTA * TELUGU +#define DEV_ABBR_SIGN 0x0970 +#define DEV_ANUDATTA 0x0952 +#define EXT_RANGE_BEGIN 0xA1 +#define EXT_RANGE_END 0xEE + +#define PNJ_DELTA 0x0100 +#define PNJ_BINDI 0x0A02 +#define PNJ_TIPPI 0x0A70 +#define PNJ_SIGN_VIRAMA 0x0A4D +#define PNJ_ADHAK 0x0A71 +#define PNJ_HA 0x0A39 +#define PNJ_RRA 0x0A5C + +typedef enum { + DEVANAGARI =0, + BENGALI, + GURMUKHI, + GUJARATI, + ORIYA, + TAMIL, + TELUGU, + KANNADA, + MALAYALAM, + DELTA=0x80 +}UniLang; + +/** + * Enumeration for switching code pages if + + * is encountered + */ +typedef enum { + DEF = 0x40, + RMN = 0x41, + DEV = 0x42, + BNG = 0x43, + TML = 0x44, + TLG = 0x45, + ASM = 0x46, + ORI = 0x47, + KND = 0x48, + MLM = 0x49, + GJR = 0x4A, + PNJ = 0x4B, + ARB = 0x71, + PES = 0x72, + URD = 0x73, + SND = 0x74, + KSM = 0x75, + PST = 0x76 +}ISCIILang; + +typedef enum { + DEV_MASK =0x80, + PNJ_MASK =0x40, + GJR_MASK =0x20, + ORI_MASK =0x10, + BNG_MASK =0x08, + KND_MASK =0x04, + MLM_MASK =0x02, + TML_MASK =0x01, + ZERO =0x00 +}MaskEnum; + +#define ISCII_CNV_PREFIX "ISCII,version=" + +typedef struct { + char16_t contextCharToUnicode; /* previous Unicode codepoint for contextual analysis */ + char16_t contextCharFromUnicode; /* previous Unicode codepoint for contextual analysis */ + uint16_t defDeltaToUnicode; /* delta for switching to default state when DEF is encountered */ + uint16_t currentDeltaFromUnicode; /* current delta in Indic block */ + uint16_t currentDeltaToUnicode; /* current delta in Indic block */ + MaskEnum currentMaskFromUnicode; /* mask for current state in toUnicode */ + MaskEnum currentMaskToUnicode; /* mask for current state in toUnicode */ + MaskEnum defMaskToUnicode; /* mask for default state in toUnicode */ + UBool isFirstBuffer; /* boolean for fromUnicode to see if we need to announce the first script */ + UBool resetToDefaultToUnicode; /* boolean for resetting to default delta and mask when a newline is encountered*/ + char name[sizeof(ISCII_CNV_PREFIX) + 1]; + UChar32 prevToUnicodeStatus; /* Hold the previous toUnicodeStatus. This is necessary because we may need to know the last two code points. */ +} UConverterDataISCII; + +typedef struct LookupDataStruct { + UniLang uniLang; + MaskEnum maskEnum; + ISCIILang isciiLang; +} LookupDataStruct; + +static const LookupDataStruct lookupInitialData[]={ + { DEVANAGARI, DEV_MASK, DEV }, + { BENGALI, BNG_MASK, BNG }, + { GURMUKHI, PNJ_MASK, PNJ }, + { GUJARATI, GJR_MASK, GJR }, + { ORIYA, ORI_MASK, ORI }, + { TAMIL, TML_MASK, TML }, + { TELUGU, KND_MASK, TLG }, + { KANNADA, KND_MASK, KND }, + { MALAYALAM, MLM_MASK, MLM } +}; + +/* + * For special handling of certain Gurmukhi characters. + * Bit 0 (value 1): PNJ consonant + * Bit 1 (value 2): PNJ Bindi Tippi + */ +static const uint8_t pnjMap[80] = { + /* 0A00..0A0F */ + 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, + /* 0A10..0A1F */ + 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + /* 0A20..0A2F */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, + /* 0A30..0A3F */ + 3, 0, 0, 0, 0, 3, 3, 0, 3, 3, 0, 0, 0, 0, 0, 2, + /* 0A40..0A4F */ + 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static UBool +isPNJConsonant(UChar32 c) { + if (c < 0xa00 || 0xa50 <= c) { + return false; + } else { + return (UBool)(pnjMap[c - 0xa00] & 1); + } +} + +static UBool +isPNJBindiTippi(UChar32 c) { + if (c < 0xa00 || 0xa50 <= c) { + return false; + } else { + return (UBool)(pnjMap[c - 0xa00] >> 1); + } +} +U_CDECL_BEGIN +static void U_CALLCONV +_ISCIIOpen(UConverter *cnv, UConverterLoadArgs *pArgs, UErrorCode *errorCode) { + if(pArgs->onlyTestIsLoadable) { + return; + } + + cnv->extraInfo = uprv_malloc(sizeof(UConverterDataISCII)); + + if (cnv->extraInfo != nullptr) { + int32_t len=0; + UConverterDataISCII *converterData= + (UConverterDataISCII *) cnv->extraInfo; + converterData->contextCharToUnicode=NO_CHAR_MARKER; + cnv->toUnicodeStatus = missingCharMarker; + converterData->contextCharFromUnicode=0x0000; + converterData->resetToDefaultToUnicode=false; + /* check if the version requested is supported */ + if ((pArgs->options & UCNV_OPTIONS_VERSION_MASK) < 9) { + /* initialize state variables */ + converterData->currentDeltaFromUnicode + = converterData->currentDeltaToUnicode + = converterData->defDeltaToUnicode = (uint16_t)(lookupInitialData[pArgs->options & UCNV_OPTIONS_VERSION_MASK].uniLang * DELTA); + + converterData->currentMaskFromUnicode + = converterData->currentMaskToUnicode + = converterData->defMaskToUnicode = lookupInitialData[pArgs->options & UCNV_OPTIONS_VERSION_MASK].maskEnum; + + converterData->isFirstBuffer=true; + (void)uprv_strcpy(converterData->name, ISCII_CNV_PREFIX); + len = (int32_t)uprv_strlen(converterData->name); + converterData->name[len]= (char)((pArgs->options & UCNV_OPTIONS_VERSION_MASK) + '0'); + converterData->name[len+1]=0; + + converterData->prevToUnicodeStatus = 0x0000; + } else { + uprv_free(cnv->extraInfo); + cnv->extraInfo = nullptr; + *errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + + } else { + *errorCode =U_MEMORY_ALLOCATION_ERROR; + } +} + +static void U_CALLCONV +_ISCIIClose(UConverter *cnv) { + if (cnv->extraInfo!=nullptr) { + if (!cnv->isExtraLocal) { + uprv_free(cnv->extraInfo); + } + cnv->extraInfo=nullptr; + } +} + +static const char* U_CALLCONV +_ISCIIgetName(const UConverter* cnv) { + if (cnv->extraInfo) { + UConverterDataISCII* myData= (UConverterDataISCII*)cnv->extraInfo; + return myData->name; + } + return nullptr; +} + +static void U_CALLCONV +_ISCIIReset(UConverter *cnv, UConverterResetChoice choice) { + UConverterDataISCII* data =(UConverterDataISCII *) (cnv->extraInfo); + if (choice<=UCNV_RESET_TO_UNICODE) { + cnv->toUnicodeStatus = missingCharMarker; + cnv->mode=0; + data->currentDeltaToUnicode=data->defDeltaToUnicode; + data->currentMaskToUnicode = data->defMaskToUnicode; + data->contextCharToUnicode=NO_CHAR_MARKER; + data->prevToUnicodeStatus = 0x0000; + } + if (choice!=UCNV_RESET_TO_UNICODE) { + cnv->fromUChar32=0x0000; + data->contextCharFromUnicode=0x00; + data->currentMaskFromUnicode=data->defMaskToUnicode; + data->currentDeltaFromUnicode=data->defDeltaToUnicode; + data->isFirstBuffer=true; + data->resetToDefaultToUnicode=false; + } +} + +/** + * The values in validity table are indexed by the lower bits of Unicode + * range 0x0900 - 0x09ff. The values have a structure like: + * --------------------------------------------------------------- + * | DEV | PNJ | GJR | ORI | BNG | TLG | MLM | TML | + * | | | | | ASM | KND | | | + * --------------------------------------------------------------- + * If a code point is valid in a particular script + * then that bit is turned on + * + * Unicode does not distinguish between Bengali and Assamese so we use 1 bit for + * to represent these languages + * + * Telugu and Kannada have same codepoints except for Vocallic_RR which we special case + * and combine and use 1 bit to represent these languages. + * + * TODO: It is probably easier to understand and maintain to change this + * to use uint16_t and give each of the 9 Unicode/script blocks its own bit. + */ + +static const uint8_t validityTable[128] = { +/* This state table is tool generated please do not edit unless you know exactly what you are doing */ +/* Note: This table was edited to mirror the Windows XP implementation */ +/*ISCII:Valid:Unicode */ +/*0xa0 : 0x00: 0x900 */ ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0xa1 : 0xb8: 0x901 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + ZERO + ZERO + ZERO , +/*0xa2 : 0xfe: 0x902 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xa3 : 0xbf: 0x903 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0x00 : 0x00: 0x904 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0xa4 : 0xff: 0x905 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xa5 : 0xff: 0x906 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xa6 : 0xff: 0x907 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xa7 : 0xff: 0x908 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xa8 : 0xff: 0x909 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xa9 : 0xff: 0x90a */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xaa : 0xfe: 0x90b */ DEV_MASK + ZERO + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0x00 : 0x00: 0x90c */ DEV_MASK + ZERO + ZERO + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xae : 0x80: 0x90d */ DEV_MASK + ZERO + GJR_MASK + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0xab : 0x87: 0x90e */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + KND_MASK + MLM_MASK + TML_MASK , +/*0xac : 0xff: 0x90f */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xad : 0xff: 0x910 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xb2 : 0x80: 0x911 */ DEV_MASK + ZERO + GJR_MASK + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0xaf : 0x87: 0x912 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + KND_MASK + MLM_MASK + TML_MASK , +/*0xb0 : 0xff: 0x913 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xb1 : 0xff: 0x914 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xb3 : 0xff: 0x915 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xb4 : 0xfe: 0x916 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xb5 : 0xfe: 0x917 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xb6 : 0xfe: 0x918 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xb7 : 0xff: 0x919 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xb8 : 0xff: 0x91a */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xb9 : 0xfe: 0x91b */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xba : 0xff: 0x91c */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xbb : 0xfe: 0x91d */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xbc : 0xff: 0x91e */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xbd : 0xff: 0x91f */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xbe : 0xfe: 0x920 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xbf : 0xfe: 0x921 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xc0 : 0xfe: 0x922 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xc1 : 0xff: 0x923 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xc2 : 0xff: 0x924 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xc3 : 0xfe: 0x925 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xc4 : 0xfe: 0x926 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xc5 : 0xfe: 0x927 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xc6 : 0xff: 0x928 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xc7 : 0x81: 0x929 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + TML_MASK , +/*0xc8 : 0xff: 0x92a */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xc9 : 0xfe: 0x92b */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xca : 0xfe: 0x92c */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xcb : 0xfe: 0x92d */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xcc : 0xfe: 0x92e */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xcd : 0xff: 0x92f */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xcf : 0xff: 0x930 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xd0 : 0x87: 0x931 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + MLM_MASK + TML_MASK , +/*0xd1 : 0xff: 0x932 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xd2 : 0xb7: 0x933 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + ZERO + KND_MASK + MLM_MASK + TML_MASK , +/*0xd3 : 0x83: 0x934 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + MLM_MASK + TML_MASK , +/*0xd4 : 0xff: 0x935 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + ZERO + KND_MASK + MLM_MASK + TML_MASK , +/*0xd5 : 0xfe: 0x936 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0xd6 : 0xbf: 0x937 */ DEV_MASK + ZERO + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xd7 : 0xff: 0x938 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xd8 : 0xff: 0x939 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0x00 : 0x00: 0x93A */ ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x93B */ ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0xe9 : 0xda: 0x93c */ DEV_MASK + PNJ_MASK + ZERO + ORI_MASK + BNG_MASK + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x93d */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0xda : 0xff: 0x93e */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xdb : 0xff: 0x93f */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xdc : 0xff: 0x940 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xdd : 0xff: 0x941 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xde : 0xff: 0x942 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xdf : 0xbe: 0x943 */ DEV_MASK + ZERO + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0x00 : 0x00: 0x944 */ DEV_MASK + ZERO + GJR_MASK + ZERO + BNG_MASK + KND_MASK + ZERO + ZERO , +/*0xe3 : 0x80: 0x945 */ DEV_MASK + ZERO + GJR_MASK + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0xe0 : 0x87: 0x946 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + KND_MASK + MLM_MASK + TML_MASK , +/*0xe1 : 0xff: 0x947 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xe2 : 0xff: 0x948 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xe7 : 0x80: 0x949 */ DEV_MASK + ZERO + GJR_MASK + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0xe4 : 0x87: 0x94a */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + KND_MASK + MLM_MASK + TML_MASK , +/*0xe5 : 0xff: 0x94b */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xe6 : 0xff: 0x94c */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xe8 : 0xff: 0x94d */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xec : 0x00: 0x94e */ ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0xed : 0x00: 0x94f */ ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x950 */ DEV_MASK + ZERO + GJR_MASK + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x951 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x952 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x953 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x954 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x955 */ ZERO + ZERO + ZERO + ZERO + ZERO + KND_MASK + ZERO + ZERO , +/*0x00 : 0x00: 0x956 */ ZERO + ZERO + ZERO + ORI_MASK + ZERO + KND_MASK + ZERO + ZERO , +/*0x00 : 0x00: 0x957 */ ZERO + ZERO + ZERO + ORI_MASK + BNG_MASK + ZERO + MLM_MASK + ZERO , +/*0x00 : 0x00: 0x958 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x959 */ DEV_MASK + PNJ_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x95a */ DEV_MASK + PNJ_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x95b */ DEV_MASK + PNJ_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x95c */ DEV_MASK + PNJ_MASK + ZERO + ZERO + BNG_MASK + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x95d */ DEV_MASK + ZERO + ZERO + ORI_MASK + BNG_MASK + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x95e */ DEV_MASK + PNJ_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0xce : 0x98: 0x95f */ DEV_MASK + ZERO + ZERO + ORI_MASK + BNG_MASK + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x960 */ DEV_MASK + ZERO + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0x00 : 0x00: 0x961 */ DEV_MASK + ZERO + ZERO + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + ZERO , +/*0x00 : 0x00: 0x962 */ DEV_MASK + ZERO + ZERO + ZERO + BNG_MASK + ZERO + ZERO + ZERO , +/*0x00 : 0x00: 0x963 */ DEV_MASK + ZERO + ZERO + ZERO + BNG_MASK + ZERO + ZERO + ZERO , +/*0xea : 0xf8: 0x964 */ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0xeaea : 0x00: 0x965*/ DEV_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/*0xf1 : 0xff: 0x966 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xf2 : 0xff: 0x967 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xf3 : 0xff: 0x968 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xf4 : 0xff: 0x969 */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xf5 : 0xff: 0x96a */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xf6 : 0xff: 0x96b */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xf7 : 0xff: 0x96c */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xf8 : 0xff: 0x96d */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xf9 : 0xff: 0x96e */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0xfa : 0xff: 0x96f */ DEV_MASK + PNJ_MASK + GJR_MASK + ORI_MASK + BNG_MASK + KND_MASK + MLM_MASK + TML_MASK , +/*0x00 : 0x80: 0x970 */ DEV_MASK + PNJ_MASK + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO , +/* + * The length of the array is 128 to provide values for 0x900..0x97f. + * The last 15 entries for 0x971..0x97f of the validity table are all zero + * because no Indic script uses such Unicode code points. + */ +/*0x00 : 0x00: 0x9yz */ ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO + ZERO +}; + +static const uint16_t fromUnicodeTable[128]={ + 0x00a0 ,/* 0x0900 */ + 0x00a1 ,/* 0x0901 */ + 0x00a2 ,/* 0x0902 */ + 0x00a3 ,/* 0x0903 */ + 0xa4e0 ,/* 0x0904 */ + 0x00a4 ,/* 0x0905 */ + 0x00a5 ,/* 0x0906 */ + 0x00a6 ,/* 0x0907 */ + 0x00a7 ,/* 0x0908 */ + 0x00a8 ,/* 0x0909 */ + 0x00a9 ,/* 0x090a */ + 0x00aa ,/* 0x090b */ + 0xA6E9 ,/* 0x090c */ + 0x00ae ,/* 0x090d */ + 0x00ab ,/* 0x090e */ + 0x00ac ,/* 0x090f */ + 0x00ad ,/* 0x0910 */ + 0x00b2 ,/* 0x0911 */ + 0x00af ,/* 0x0912 */ + 0x00b0 ,/* 0x0913 */ + 0x00b1 ,/* 0x0914 */ + 0x00b3 ,/* 0x0915 */ + 0x00b4 ,/* 0x0916 */ + 0x00b5 ,/* 0x0917 */ + 0x00b6 ,/* 0x0918 */ + 0x00b7 ,/* 0x0919 */ + 0x00b8 ,/* 0x091a */ + 0x00b9 ,/* 0x091b */ + 0x00ba ,/* 0x091c */ + 0x00bb ,/* 0x091d */ + 0x00bc ,/* 0x091e */ + 0x00bd ,/* 0x091f */ + 0x00be ,/* 0x0920 */ + 0x00bf ,/* 0x0921 */ + 0x00c0 ,/* 0x0922 */ + 0x00c1 ,/* 0x0923 */ + 0x00c2 ,/* 0x0924 */ + 0x00c3 ,/* 0x0925 */ + 0x00c4 ,/* 0x0926 */ + 0x00c5 ,/* 0x0927 */ + 0x00c6 ,/* 0x0928 */ + 0x00c7 ,/* 0x0929 */ + 0x00c8 ,/* 0x092a */ + 0x00c9 ,/* 0x092b */ + 0x00ca ,/* 0x092c */ + 0x00cb ,/* 0x092d */ + 0x00cc ,/* 0x092e */ + 0x00cd ,/* 0x092f */ + 0x00cf ,/* 0x0930 */ + 0x00d0 ,/* 0x0931 */ + 0x00d1 ,/* 0x0932 */ + 0x00d2 ,/* 0x0933 */ + 0x00d3 ,/* 0x0934 */ + 0x00d4 ,/* 0x0935 */ + 0x00d5 ,/* 0x0936 */ + 0x00d6 ,/* 0x0937 */ + 0x00d7 ,/* 0x0938 */ + 0x00d8 ,/* 0x0939 */ + 0xFFFF ,/* 0x093A */ + 0xFFFF ,/* 0x093B */ + 0x00e9 ,/* 0x093c */ + 0xEAE9 ,/* 0x093d */ + 0x00da ,/* 0x093e */ + 0x00db ,/* 0x093f */ + 0x00dc ,/* 0x0940 */ + 0x00dd ,/* 0x0941 */ + 0x00de ,/* 0x0942 */ + 0x00df ,/* 0x0943 */ + 0xDFE9 ,/* 0x0944 */ + 0x00e3 ,/* 0x0945 */ + 0x00e0 ,/* 0x0946 */ + 0x00e1 ,/* 0x0947 */ + 0x00e2 ,/* 0x0948 */ + 0x00e7 ,/* 0x0949 */ + 0x00e4 ,/* 0x094a */ + 0x00e5 ,/* 0x094b */ + 0x00e6 ,/* 0x094c */ + 0x00e8 ,/* 0x094d */ + 0x00ec ,/* 0x094e */ + 0x00ed ,/* 0x094f */ + 0xA1E9 ,/* 0x0950 */ /* OM Symbol */ + 0xFFFF ,/* 0x0951 */ + 0xF0B8 ,/* 0x0952 */ + 0xFFFF ,/* 0x0953 */ + 0xFFFF ,/* 0x0954 */ + 0xFFFF ,/* 0x0955 */ + 0xFFFF ,/* 0x0956 */ + 0xFFFF ,/* 0x0957 */ + 0xb3e9 ,/* 0x0958 */ + 0xb4e9 ,/* 0x0959 */ + 0xb5e9 ,/* 0x095a */ + 0xbae9 ,/* 0x095b */ + 0xbfe9 ,/* 0x095c */ + 0xC0E9 ,/* 0x095d */ + 0xc9e9 ,/* 0x095e */ + 0x00ce ,/* 0x095f */ + 0xAAe9 ,/* 0x0960 */ + 0xA7E9 ,/* 0x0961 */ + 0xDBE9 ,/* 0x0962 */ + 0xDCE9 ,/* 0x0963 */ + 0x00ea ,/* 0x0964 */ + 0xeaea ,/* 0x0965 */ + 0x00f1 ,/* 0x0966 */ + 0x00f2 ,/* 0x0967 */ + 0x00f3 ,/* 0x0968 */ + 0x00f4 ,/* 0x0969 */ + 0x00f5 ,/* 0x096a */ + 0x00f6 ,/* 0x096b */ + 0x00f7 ,/* 0x096c */ + 0x00f8 ,/* 0x096d */ + 0x00f9 ,/* 0x096e */ + 0x00fa ,/* 0x096f */ + 0xF0BF ,/* 0x0970 */ + 0xFFFF ,/* 0x0971 */ + 0xFFFF ,/* 0x0972 */ + 0xFFFF ,/* 0x0973 */ + 0xFFFF ,/* 0x0974 */ + 0xFFFF ,/* 0x0975 */ + 0xFFFF ,/* 0x0976 */ + 0xFFFF ,/* 0x0977 */ + 0xFFFF ,/* 0x0978 */ + 0xFFFF ,/* 0x0979 */ + 0xFFFF ,/* 0x097a */ + 0xFFFF ,/* 0x097b */ + 0xFFFF ,/* 0x097c */ + 0xFFFF ,/* 0x097d */ + 0xFFFF ,/* 0x097e */ + 0xFFFF ,/* 0x097f */ +}; +static const uint16_t toUnicodeTable[256]={ + 0x0000,/* 0x00 */ + 0x0001,/* 0x01 */ + 0x0002,/* 0x02 */ + 0x0003,/* 0x03 */ + 0x0004,/* 0x04 */ + 0x0005,/* 0x05 */ + 0x0006,/* 0x06 */ + 0x0007,/* 0x07 */ + 0x0008,/* 0x08 */ + 0x0009,/* 0x09 */ + 0x000a,/* 0x0a */ + 0x000b,/* 0x0b */ + 0x000c,/* 0x0c */ + 0x000d,/* 0x0d */ + 0x000e,/* 0x0e */ + 0x000f,/* 0x0f */ + 0x0010,/* 0x10 */ + 0x0011,/* 0x11 */ + 0x0012,/* 0x12 */ + 0x0013,/* 0x13 */ + 0x0014,/* 0x14 */ + 0x0015,/* 0x15 */ + 0x0016,/* 0x16 */ + 0x0017,/* 0x17 */ + 0x0018,/* 0x18 */ + 0x0019,/* 0x19 */ + 0x001a,/* 0x1a */ + 0x001b,/* 0x1b */ + 0x001c,/* 0x1c */ + 0x001d,/* 0x1d */ + 0x001e,/* 0x1e */ + 0x001f,/* 0x1f */ + 0x0020,/* 0x20 */ + 0x0021,/* 0x21 */ + 0x0022,/* 0x22 */ + 0x0023,/* 0x23 */ + 0x0024,/* 0x24 */ + 0x0025,/* 0x25 */ + 0x0026,/* 0x26 */ + 0x0027,/* 0x27 */ + 0x0028,/* 0x28 */ + 0x0029,/* 0x29 */ + 0x002a,/* 0x2a */ + 0x002b,/* 0x2b */ + 0x002c,/* 0x2c */ + 0x002d,/* 0x2d */ + 0x002e,/* 0x2e */ + 0x002f,/* 0x2f */ + 0x0030,/* 0x30 */ + 0x0031,/* 0x31 */ + 0x0032,/* 0x32 */ + 0x0033,/* 0x33 */ + 0x0034,/* 0x34 */ + 0x0035,/* 0x35 */ + 0x0036,/* 0x36 */ + 0x0037,/* 0x37 */ + 0x0038,/* 0x38 */ + 0x0039,/* 0x39 */ + 0x003A,/* 0x3A */ + 0x003B,/* 0x3B */ + 0x003c,/* 0x3c */ + 0x003d,/* 0x3d */ + 0x003e,/* 0x3e */ + 0x003f,/* 0x3f */ + 0x0040,/* 0x40 */ + 0x0041,/* 0x41 */ + 0x0042,/* 0x42 */ + 0x0043,/* 0x43 */ + 0x0044,/* 0x44 */ + 0x0045,/* 0x45 */ + 0x0046,/* 0x46 */ + 0x0047,/* 0x47 */ + 0x0048,/* 0x48 */ + 0x0049,/* 0x49 */ + 0x004a,/* 0x4a */ + 0x004b,/* 0x4b */ + 0x004c,/* 0x4c */ + 0x004d,/* 0x4d */ + 0x004e,/* 0x4e */ + 0x004f,/* 0x4f */ + 0x0050,/* 0x50 */ + 0x0051,/* 0x51 */ + 0x0052,/* 0x52 */ + 0x0053,/* 0x53 */ + 0x0054,/* 0x54 */ + 0x0055,/* 0x55 */ + 0x0056,/* 0x56 */ + 0x0057,/* 0x57 */ + 0x0058,/* 0x58 */ + 0x0059,/* 0x59 */ + 0x005a,/* 0x5a */ + 0x005b,/* 0x5b */ + 0x005c,/* 0x5c */ + 0x005d,/* 0x5d */ + 0x005e,/* 0x5e */ + 0x005f,/* 0x5f */ + 0x0060,/* 0x60 */ + 0x0061,/* 0x61 */ + 0x0062,/* 0x62 */ + 0x0063,/* 0x63 */ + 0x0064,/* 0x64 */ + 0x0065,/* 0x65 */ + 0x0066,/* 0x66 */ + 0x0067,/* 0x67 */ + 0x0068,/* 0x68 */ + 0x0069,/* 0x69 */ + 0x006a,/* 0x6a */ + 0x006b,/* 0x6b */ + 0x006c,/* 0x6c */ + 0x006d,/* 0x6d */ + 0x006e,/* 0x6e */ + 0x006f,/* 0x6f */ + 0x0070,/* 0x70 */ + 0x0071,/* 0x71 */ + 0x0072,/* 0x72 */ + 0x0073,/* 0x73 */ + 0x0074,/* 0x74 */ + 0x0075,/* 0x75 */ + 0x0076,/* 0x76 */ + 0x0077,/* 0x77 */ + 0x0078,/* 0x78 */ + 0x0079,/* 0x79 */ + 0x007a,/* 0x7a */ + 0x007b,/* 0x7b */ + 0x007c,/* 0x7c */ + 0x007d,/* 0x7d */ + 0x007e,/* 0x7e */ + 0x007f,/* 0x7f */ + 0x0080,/* 0x80 */ + 0x0081,/* 0x81 */ + 0x0082,/* 0x82 */ + 0x0083,/* 0x83 */ + 0x0084,/* 0x84 */ + 0x0085,/* 0x85 */ + 0x0086,/* 0x86 */ + 0x0087,/* 0x87 */ + 0x0088,/* 0x88 */ + 0x0089,/* 0x89 */ + 0x008a,/* 0x8a */ + 0x008b,/* 0x8b */ + 0x008c,/* 0x8c */ + 0x008d,/* 0x8d */ + 0x008e,/* 0x8e */ + 0x008f,/* 0x8f */ + 0x0090,/* 0x90 */ + 0x0091,/* 0x91 */ + 0x0092,/* 0x92 */ + 0x0093,/* 0x93 */ + 0x0094,/* 0x94 */ + 0x0095,/* 0x95 */ + 0x0096,/* 0x96 */ + 0x0097,/* 0x97 */ + 0x0098,/* 0x98 */ + 0x0099,/* 0x99 */ + 0x009a,/* 0x9a */ + 0x009b,/* 0x9b */ + 0x009c,/* 0x9c */ + 0x009d,/* 0x9d */ + 0x009e,/* 0x9e */ + 0x009f,/* 0x9f */ + 0x00A0,/* 0xa0 */ + 0x0901,/* 0xa1 */ + 0x0902,/* 0xa2 */ + 0x0903,/* 0xa3 */ + 0x0905,/* 0xa4 */ + 0x0906,/* 0xa5 */ + 0x0907,/* 0xa6 */ + 0x0908,/* 0xa7 */ + 0x0909,/* 0xa8 */ + 0x090a,/* 0xa9 */ + 0x090b,/* 0xaa */ + 0x090e,/* 0xab */ + 0x090f,/* 0xac */ + 0x0910,/* 0xad */ + 0x090d,/* 0xae */ + 0x0912,/* 0xaf */ + 0x0913,/* 0xb0 */ + 0x0914,/* 0xb1 */ + 0x0911,/* 0xb2 */ + 0x0915,/* 0xb3 */ + 0x0916,/* 0xb4 */ + 0x0917,/* 0xb5 */ + 0x0918,/* 0xb6 */ + 0x0919,/* 0xb7 */ + 0x091a,/* 0xb8 */ + 0x091b,/* 0xb9 */ + 0x091c,/* 0xba */ + 0x091d,/* 0xbb */ + 0x091e,/* 0xbc */ + 0x091f,/* 0xbd */ + 0x0920,/* 0xbe */ + 0x0921,/* 0xbf */ + 0x0922,/* 0xc0 */ + 0x0923,/* 0xc1 */ + 0x0924,/* 0xc2 */ + 0x0925,/* 0xc3 */ + 0x0926,/* 0xc4 */ + 0x0927,/* 0xc5 */ + 0x0928,/* 0xc6 */ + 0x0929,/* 0xc7 */ + 0x092a,/* 0xc8 */ + 0x092b,/* 0xc9 */ + 0x092c,/* 0xca */ + 0x092d,/* 0xcb */ + 0x092e,/* 0xcc */ + 0x092f,/* 0xcd */ + 0x095f,/* 0xce */ + 0x0930,/* 0xcf */ + 0x0931,/* 0xd0 */ + 0x0932,/* 0xd1 */ + 0x0933,/* 0xd2 */ + 0x0934,/* 0xd3 */ + 0x0935,/* 0xd4 */ + 0x0936,/* 0xd5 */ + 0x0937,/* 0xd6 */ + 0x0938,/* 0xd7 */ + 0x0939,/* 0xd8 */ + 0x200D,/* 0xd9 */ + 0x093e,/* 0xda */ + 0x093f,/* 0xdb */ + 0x0940,/* 0xdc */ + 0x0941,/* 0xdd */ + 0x0942,/* 0xde */ + 0x0943,/* 0xdf */ + 0x0946,/* 0xe0 */ + 0x0947,/* 0xe1 */ + 0x0948,/* 0xe2 */ + 0x0945,/* 0xe3 */ + 0x094a,/* 0xe4 */ + 0x094b,/* 0xe5 */ + 0x094c,/* 0xe6 */ + 0x0949,/* 0xe7 */ + 0x094d,/* 0xe8 */ + 0x093c,/* 0xe9 */ + 0x0964,/* 0xea */ + 0xFFFF,/* 0xeb */ + 0xFFFF,/* 0xec */ + 0xFFFF,/* 0xed */ + 0xFFFF,/* 0xee */ + 0xFFFF,/* 0xef */ + 0xFFFF,/* 0xf0 */ + 0x0966,/* 0xf1 */ + 0x0967,/* 0xf2 */ + 0x0968,/* 0xf3 */ + 0x0969,/* 0xf4 */ + 0x096a,/* 0xf5 */ + 0x096b,/* 0xf6 */ + 0x096c,/* 0xf7 */ + 0x096d,/* 0xf8 */ + 0x096e,/* 0xf9 */ + 0x096f,/* 0xfa */ + 0xFFFF,/* 0xfb */ + 0xFFFF,/* 0xfc */ + 0xFFFF,/* 0xfd */ + 0xFFFF,/* 0xfe */ + 0xFFFF /* 0xff */ +}; + +static const uint16_t vowelSignESpecialCases[][2]={ + { 2 /*length of array*/ , 0 }, + { 0xA4 , 0x0904 }, +}; + +static const uint16_t nuktaSpecialCases[][2]={ + { 16 /*length of array*/ , 0 }, + { 0xA6 , 0x090c }, + { 0xEA , 0x093D }, + { 0xDF , 0x0944 }, + { 0xA1 , 0x0950 }, + { 0xb3 , 0x0958 }, + { 0xb4 , 0x0959 }, + { 0xb5 , 0x095a }, + { 0xba , 0x095b }, + { 0xbf , 0x095c }, + { 0xC0 , 0x095d }, + { 0xc9 , 0x095e }, + { 0xAA , 0x0960 }, + { 0xA7 , 0x0961 }, + { 0xDB , 0x0962 }, + { 0xDC , 0x0963 }, +}; + + +#define WRITE_TO_TARGET_FROM_U(args,offsets,source,target,targetLimit,targetByteUnit,err) UPRV_BLOCK_MACRO_BEGIN { \ + int32_t offset = (int32_t)(source - args->source-1); \ + /* write the targetUniChar to target */ \ + if(target < targetLimit){ \ + if(targetByteUnit <= 0xFF){ \ + *(target)++ = (uint8_t)(targetByteUnit); \ + if(offsets){ \ + *(offsets++) = offset; \ + } \ + }else{ \ + if (targetByteUnit > 0xFFFF) { \ + *(target)++ = (uint8_t)(targetByteUnit>>16); \ + if (offsets) { \ + --offset; \ + *(offsets++) = offset; \ + } \ + } \ + if (!(target < targetLimit)) { \ + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = \ + (uint8_t)(targetByteUnit >> 8); \ + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = \ + (uint8_t)targetByteUnit; \ + *err = U_BUFFER_OVERFLOW_ERROR; \ + } else { \ + *(target)++ = (uint8_t)(targetByteUnit>>8); \ + if(offsets){ \ + *(offsets++) = offset; \ + } \ + if(target < targetLimit){ \ + *(target)++ = (uint8_t) targetByteUnit; \ + if(offsets){ \ + *(offsets++) = offset ; \ + } \ + }else{ \ + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] =\ + (uint8_t) (targetByteUnit); \ + *err = U_BUFFER_OVERFLOW_ERROR; \ + } \ + } \ + } \ + }else{ \ + if (targetByteUnit & 0xFF0000) { \ + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = \ + (uint8_t) (targetByteUnit >>16); \ + } \ + if(targetByteUnit & 0xFF00){ \ + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = \ + (uint8_t) (targetByteUnit >>8); \ + } \ + args->converter->charErrorBuffer[args->converter->charErrorBufferLength++] = \ + (uint8_t) (targetByteUnit); \ + *err = U_BUFFER_OVERFLOW_ERROR; \ + } \ +} UPRV_BLOCK_MACRO_END + +/* Rules: + * Explicit Halant : + * + + * Soft Halant : + * + + */ +static void U_CALLCONV +UConverter_fromUnicode_ISCII_OFFSETS_LOGIC( + UConverterFromUnicodeArgs * args, UErrorCode * err) { + const char16_t *source = args->source; + const char16_t *sourceLimit = args->sourceLimit; + unsigned char *target = (unsigned char *) args->target; + unsigned char *targetLimit = (unsigned char *) args->targetLimit; + int32_t* offsets = args->offsets; + uint32_t targetByteUnit = 0x0000; + UChar32 sourceChar = 0x0000; + UChar32 tempContextFromUnicode = 0x0000; /* For special handling of the Gurmukhi script. */ + UConverterDataISCII *converterData; + uint16_t newDelta=0; + uint16_t range = 0; + UBool deltaChanged = false; + + if ((args->converter == nullptr) || (args->targetLimit < args->target) || (args->sourceLimit < args->source)) { + *err = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + /* initialize data */ + converterData=(UConverterDataISCII*)args->converter->extraInfo; + newDelta=converterData->currentDeltaFromUnicode; + range = (uint16_t)(newDelta/DELTA); + + if ((sourceChar = args->converter->fromUChar32)!=0) { + goto getTrail; + } + + /*writing the char to the output stream */ + while (source < sourceLimit) { + /* Write the language code following LF only if LF is not the last character. */ + if (args->converter->fromUnicodeStatus == LF) { + targetByteUnit = ATR<<8; + targetByteUnit += (uint8_t) lookupInitialData[range].isciiLang; + args->converter->fromUnicodeStatus = 0x0000; + /* now append ATR and language code */ + WRITE_TO_TARGET_FROM_U(args,offsets,source,target,targetLimit,targetByteUnit,err); + if (U_FAILURE(*err)) { + break; + } + } + + sourceChar = *source++; + tempContextFromUnicode = converterData->contextCharFromUnicode; + + targetByteUnit = missingCharMarker; + + /*check if input is in ASCII and C0 control codes range*/ + if (sourceChar <= ASCII_END) { + args->converter->fromUnicodeStatus = sourceChar; + WRITE_TO_TARGET_FROM_U(args,offsets,source,target,targetLimit,sourceChar,err); + if (U_FAILURE(*err)) { + break; + } + continue; + } + switch (sourceChar) { + case ZWNJ: + /* contextChar has HALANT */ + if (converterData->contextCharFromUnicode) { + converterData->contextCharFromUnicode = 0x00; + targetByteUnit = ISCII_HALANT; + } else { + /* consume ZWNJ and continue */ + converterData->contextCharFromUnicode = 0x00; + continue; + } + break; + case ZWJ: + /* contextChar has HALANT */ + if (converterData->contextCharFromUnicode) { + targetByteUnit = ISCII_NUKTA; + } else { + targetByteUnit =ISCII_INV; + } + converterData->contextCharFromUnicode = 0x00; + break; + default: + /* is the sourceChar in the INDIC_RANGE? */ + if ((uint16_t)(INDIC_BLOCK_END-sourceChar) <= INDIC_RANGE) { + /* Danda and Double Danda are valid in Northern scripts.. since Unicode + * does not include these codepoints in all Northern scrips we need to + * filter them out + */ + if (sourceChar!= DANDA && sourceChar != DOUBLE_DANDA) { + /* find out to which block the souceChar belongs*/ + range =(uint16_t)((sourceChar-INDIC_BLOCK_BEGIN)/DELTA); + newDelta =(uint16_t)(range*DELTA); + + /* Now are we in the same block as the previous? */ + if (newDelta!= converterData->currentDeltaFromUnicode || converterData->isFirstBuffer) { + converterData->currentDeltaFromUnicode = newDelta; + converterData->currentMaskFromUnicode = lookupInitialData[range].maskEnum; + deltaChanged =true; + converterData->isFirstBuffer=false; + } + + if (converterData->currentDeltaFromUnicode == PNJ_DELTA) { + if (sourceChar == PNJ_TIPPI) { + /* Make sure Tippi is converted to Bindi. */ + sourceChar = PNJ_BINDI; + } else if (sourceChar == PNJ_ADHAK) { + /* This is for consonant cluster handling. */ + converterData->contextCharFromUnicode = PNJ_ADHAK; + } + + } + /* Normalize all Indic codepoints to Devanagari and map them to ISCII */ + /* now subtract the new delta from sourceChar*/ + sourceChar -= converterData->currentDeltaFromUnicode; + } + + /* get the target byte unit */ + targetByteUnit=fromUnicodeTable[(uint8_t)sourceChar]; + + /* is the code point valid in current script? */ + if ((validityTable[(uint8_t)sourceChar] & converterData->currentMaskFromUnicode)==0) { + /* Vocallic RR is assigned in ISCII Telugu and Unicode */ + if (converterData->currentDeltaFromUnicode!=(TELUGU_DELTA) || sourceChar!=VOCALLIC_RR) { + targetByteUnit=missingCharMarker; + } + } + + if (deltaChanged) { + /* we are in a script block which is different than + * previous sourceChar's script block write ATR and language codes + */ + uint32_t temp=0; + temp =(uint16_t)(ATR<<8); + temp += (uint16_t)((uint8_t) lookupInitialData[range].isciiLang); + /* reset */ + deltaChanged=false; + /* now append ATR and language code */ + WRITE_TO_TARGET_FROM_U(args,offsets,source,target,targetLimit,temp,err); + if (U_FAILURE(*err)) { + break; + } + } + + if (converterData->currentDeltaFromUnicode == PNJ_DELTA && (sourceChar + PNJ_DELTA) == PNJ_ADHAK) { + continue; + } + } + /* reset context char */ + converterData->contextCharFromUnicode = 0x00; + break; + } + if (converterData->currentDeltaFromUnicode == PNJ_DELTA && tempContextFromUnicode == PNJ_ADHAK && isPNJConsonant((sourceChar + PNJ_DELTA))) { + /* If the previous codepoint is Adhak and the current codepoint is a consonant, the targetByteUnit should be C + Halant + C. */ + /* reset context char */ + converterData->contextCharFromUnicode = 0x0000; + targetByteUnit = targetByteUnit << 16 | ISCII_HALANT << 8 | targetByteUnit; + /* write targetByteUnit to target */ + WRITE_TO_TARGET_FROM_U(args, offsets, source, target, targetLimit, targetByteUnit,err); + if (U_FAILURE(*err)) { + break; + } + } else if (targetByteUnit != missingCharMarker) { + if (targetByteUnit==ISCII_HALANT) { + converterData->contextCharFromUnicode = (char16_t)targetByteUnit; + } + /* write targetByteUnit to target*/ + WRITE_TO_TARGET_FROM_U(args,offsets,source,target,targetLimit,targetByteUnit,err); + if (U_FAILURE(*err)) { + break; + } + } else { + /* oops.. the code point is unassigned */ + /*check if the char is a First surrogate*/ + if (U16_IS_SURROGATE(sourceChar)) { + if (U16_IS_SURROGATE_LEAD(sourceChar)) { +getTrail: + /*look ahead to find the trail surrogate*/ + if (source < sourceLimit) { + /* test the following code unit */ + char16_t trail= (*source); + if (U16_IS_TRAIL(trail)) { + source++; + sourceChar=U16_GET_SUPPLEMENTARY(sourceChar, trail); + *err =U_INVALID_CHAR_FOUND; + /* convert this surrogate code point */ + /* exit this condition tree */ + } else { + /* this is an unmatched lead code unit (1st surrogate) */ + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + } + } else { + /* no more input */ + *err = U_ZERO_ERROR; + } + } else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + *err=U_ILLEGAL_CHAR_FOUND; + } + } else { + /* callback(unassigned) for a BMP code point */ + *err = U_INVALID_CHAR_FOUND; + } + + args->converter->fromUChar32=sourceChar; + break; + } + }/* end while(mySourceIndexsource = source; + args->target = (char*)target; +} + +static const uint16_t lookupTable[][2]={ + { ZERO, ZERO }, /*DEFAULT*/ + { ZERO, ZERO }, /*ROMAN*/ + { DEVANAGARI, DEV_MASK }, + { BENGALI, BNG_MASK }, + { TAMIL, TML_MASK }, + { TELUGU, KND_MASK }, + { BENGALI, BNG_MASK }, + { ORIYA, ORI_MASK }, + { KANNADA, KND_MASK }, + { MALAYALAM, MLM_MASK }, + { GUJARATI, GJR_MASK }, + { GURMUKHI, PNJ_MASK } +}; + +#define WRITE_TO_TARGET_TO_U(args,source,target,offsets,offset,targetUniChar,delta, err) UPRV_BLOCK_MACRO_BEGIN { \ + /* add offset to current Indic Block */ \ + if(targetUniChar>ASCII_END && \ + targetUniChar != ZWJ && \ + targetUniChar != ZWNJ && \ + targetUniChar != DANDA && \ + targetUniChar != DOUBLE_DANDA){ \ + \ + targetUniChar+=(uint16_t)(delta); \ + } \ + /* now write the targetUniChar */ \ + if(targettargetLimit){ \ + *(target)++ = (char16_t)targetUniChar; \ + if(offsets){ \ + *(offsets)++ = (int32_t)(offset); \ + } \ + }else{ \ + args->converter->UCharErrorBuffer[args->converter->UCharErrorBufferLength++] = \ + (char16_t)targetUniChar; \ + *err = U_BUFFER_OVERFLOW_ERROR; \ + } \ +} UPRV_BLOCK_MACRO_END + +#define GET_MAPPING(sourceChar,targetUniChar,data) UPRV_BLOCK_MACRO_BEGIN { \ + targetUniChar = toUnicodeTable[(sourceChar)] ; \ + /* is the code point valid in current script? */ \ + if(sourceChar> ASCII_END && \ + (validityTable[(targetUniChar & 0x7F)] & data->currentMaskToUnicode)==0){ \ + /* Vocallic RR is assigned in ISCII Telugu and Unicode */ \ + if(data->currentDeltaToUnicode!=(TELUGU_DELTA) || \ + targetUniChar!=VOCALLIC_RR){ \ + targetUniChar=missingCharMarker; \ + } \ + } \ +} UPRV_BLOCK_MACRO_END + +/*********** + * Rules for ISCII to Unicode converter + * ISCII is stateful encoding. To convert ISCII bytes to Unicode, + * which has both precomposed and decomposed forms characters + * pre-context and post-context need to be considered. + * + * Post context + * i) ATR : Attribute code is used to declare the font and script switching. + * Currently we only switch scripts and font codes consumed without generating an error + * ii) EXT : Extension code is used to declare switching to Sanskrit and for obscure, + * obsolete characters + * Pre context + * i) Halant: if preceded by a halant then it is a explicit halant + * ii) Nukta : + * a) if preceded by a halant then it is a soft halant + * b) if preceded by specific consonants and the ligatures have pre-composed + * characters in Unicode then convert to pre-composed characters + * iii) Danda: If Danda is preceded by a Danda then convert to Double Danda + * + */ + +static void U_CALLCONV +UConverter_toUnicode_ISCII_OFFSETS_LOGIC(UConverterToUnicodeArgs *args, UErrorCode* err) { + const char *source = ( char *) args->source; + char16_t *target = args->target; + const char *sourceLimit = args->sourceLimit; + const char16_t* targetLimit = args->targetLimit; + uint32_t targetUniChar = 0x0000; + uint8_t sourceChar = 0x0000; + UConverterDataISCII* data; + UChar32* toUnicodeStatus=nullptr; + UChar32 tempTargetUniChar = 0x0000; + char16_t* contextCharToUnicode= nullptr; + UBool found; + int i; + int offset = 0; + + if ((args->converter == nullptr) || (target < args->target) || (source < args->source)) { + *err = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + data = (UConverterDataISCII*)(args->converter->extraInfo); + contextCharToUnicode = &data->contextCharToUnicode; /* contains previous ISCII codepoint visited */ + toUnicodeStatus = (UChar32*)&args->converter->toUnicodeStatus;/* contains the mapping to Unicode of the above codepoint*/ + + while (U_SUCCESS(*err) && sourcecurrentDeltaToUnicode = (uint16_t)(lookupTable[sourceChar & 0x0F][0] * DELTA); + data->currentMaskToUnicode = (MaskEnum)lookupTable[sourceChar & 0x0F][1]; + } else if (sourceChar==DEF) { + /* switch back to default */ + data->currentDeltaToUnicode = data->defDeltaToUnicode; + data->currentMaskToUnicode = data->defMaskToUnicode; + } else { + if ((sourceChar >= 0x21 && sourceChar <= 0x3F)) { + /* these are display codes consume and continue */ + } else { + *err =U_ILLEGAL_CHAR_FOUND; + /* reset */ + *contextCharToUnicode=NO_CHAR_MARKER; + goto CALLBACK; + } + } + + /* reset */ + *contextCharToUnicode=NO_CHAR_MARKER; + + continue; + + } else if (*contextCharToUnicode==EXT) { + /* check if sourceChar is in 0xA1-0xEE range */ + if ((uint8_t) (EXT_RANGE_END - sourceChar) <= (EXT_RANGE_END - EXT_RANGE_BEGIN)) { + /* We currently support only Anudatta and Devanagari abbreviation sign */ + if (sourceChar==0xBF || sourceChar == 0xB8) { + targetUniChar = (sourceChar==0xBF) ? DEV_ABBR_SIGN : DEV_ANUDATTA; + + /* find out if the mapping is valid in this state */ + if (validityTable[(uint8_t)targetUniChar] & data->currentMaskToUnicode) { + *contextCharToUnicode= NO_CHAR_MARKER; + + /* Write the previous toUnicodeStatus, this was delayed to handle consonant clustering for Gurmukhi script. */ + if (data->prevToUnicodeStatus) { + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -1),data->prevToUnicodeStatus,0,err); + data->prevToUnicodeStatus = 0x0000; + } + /* write to target */ + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -2),targetUniChar,data->currentDeltaToUnicode,err); + + continue; + } + } + /* byte unit is unassigned */ + targetUniChar = missingCharMarker; + *err= U_INVALID_CHAR_FOUND; + } else { + /* only 0xA1 - 0xEE are legal after EXT char */ + *contextCharToUnicode= NO_CHAR_MARKER; + *err = U_ILLEGAL_CHAR_FOUND; + } + goto CALLBACK; + } else if (*contextCharToUnicode==ISCII_INV) { + if (sourceChar==ISCII_HALANT) { + targetUniChar = 0x0020; /* replace with space according to Indic FAQ */ + } else { + targetUniChar = ZWJ; + } + + /* Write the previous toUnicodeStatus, this was delayed to handle consonant clustering for Gurmukhi script. */ + if (data->prevToUnicodeStatus) { + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -1),data->prevToUnicodeStatus,0,err); + data->prevToUnicodeStatus = 0x0000; + } + /* write to target */ + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -2),targetUniChar,data->currentDeltaToUnicode,err); + /* reset */ + *contextCharToUnicode=NO_CHAR_MARKER; + } + + /* look at the pre-context and perform special processing */ + switch (sourceChar) { + case ISCII_INV: + case EXT: + case ATR: + *contextCharToUnicode = (char16_t)sourceChar; + + if (*toUnicodeStatus != missingCharMarker) { + /* Write the previous toUnicodeStatus, this was delayed to handle consonant clustering for Gurmukhi script. */ + if (data->prevToUnicodeStatus) { + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -1),data->prevToUnicodeStatus,0,err); + data->prevToUnicodeStatus = 0x0000; + } + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -2),*toUnicodeStatus,data->currentDeltaToUnicode,err); + *toUnicodeStatus = missingCharMarker; + } + continue; + case ISCII_DANDA: + /* handle double danda*/ + if (*contextCharToUnicode== ISCII_DANDA) { + targetUniChar = DOUBLE_DANDA; + /* clear the context */ + *contextCharToUnicode = NO_CHAR_MARKER; + *toUnicodeStatus = missingCharMarker; + } else { + GET_MAPPING(sourceChar,targetUniChar,data); + *contextCharToUnicode = sourceChar; + } + break; + case ISCII_HALANT: + /* handle explicit halant */ + if (*contextCharToUnicode == ISCII_HALANT) { + targetUniChar = ZWNJ; + /* clear the context */ + *contextCharToUnicode = NO_CHAR_MARKER; + } else { + GET_MAPPING(sourceChar,targetUniChar,data); + *contextCharToUnicode = sourceChar; + } + break; + case 0x0A: + case 0x0D: + data->resetToDefaultToUnicode = true; + GET_MAPPING(sourceChar,targetUniChar,data) + ; + *contextCharToUnicode = sourceChar; + break; + + case ISCII_VOWEL_SIGN_E: + i=1; + found=false; + for (; icurrentMaskToUnicode) { + /*targetUniChar += data->currentDeltaToUnicode ;*/ + *contextCharToUnicode= NO_CHAR_MARKER; + *toUnicodeStatus = missingCharMarker; + break; + } + } + GET_MAPPING(sourceChar,targetUniChar,data); + *contextCharToUnicode = sourceChar; + break; + + case ISCII_NUKTA: + /* handle soft halant */ + if (*contextCharToUnicode == ISCII_HALANT) { + targetUniChar = ZWJ; + /* clear the context */ + *contextCharToUnicode = NO_CHAR_MARKER; + break; + } else if (data->currentDeltaToUnicode == PNJ_DELTA && data->contextCharToUnicode == 0xc0) { + /* Write the previous toUnicodeStatus, this was delayed to handle consonant clustering for Gurmukhi script. */ + if (data->prevToUnicodeStatus) { + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -1),data->prevToUnicodeStatus,0,err); + data->prevToUnicodeStatus = 0x0000; + } + /* We got here because ISCII_NUKTA was preceded by 0xc0 and we are converting Gurmukhi. + * In that case we must convert (0xc0 0xe9) to (\u0a5c\u0a4d\u0a39). + */ + targetUniChar = PNJ_RRA; + WRITE_TO_TARGET_TO_U(args, source, target, args->offsets, (source-args->source)-2, targetUniChar, 0, err); + if (U_SUCCESS(*err)) { + targetUniChar = PNJ_SIGN_VIRAMA; + WRITE_TO_TARGET_TO_U(args, source, target, args->offsets, (source-args->source)-2, targetUniChar, 0, err); + if (U_SUCCESS(*err)) { + targetUniChar = PNJ_HA; + WRITE_TO_TARGET_TO_U(args, source, target, args->offsets, (source-args->source)-2, targetUniChar, 0, err); + } else { + args->converter->UCharErrorBuffer[args->converter->UCharErrorBufferLength++]= PNJ_HA; + } + } else { + args->converter->UCharErrorBuffer[args->converter->UCharErrorBufferLength++]= PNJ_SIGN_VIRAMA; + args->converter->UCharErrorBuffer[args->converter->UCharErrorBufferLength++]= PNJ_HA; + } + *toUnicodeStatus = missingCharMarker; + data->contextCharToUnicode = NO_CHAR_MARKER; + continue; + } else { + /* try to handle + ISCII_NUKTA special mappings */ + i=1; + found =false; + for (; icurrentMaskToUnicode) { + /*targetUniChar += data->currentDeltaToUnicode ;*/ + *contextCharToUnicode= NO_CHAR_MARKER; + *toUnicodeStatus = missingCharMarker; + if (data->currentDeltaToUnicode == PNJ_DELTA) { + /* Write the previous toUnicodeStatus, this was delayed to handle consonant clustering for Gurmukhi script. */ + if (data->prevToUnicodeStatus) { + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -1),data->prevToUnicodeStatus,0,err); + data->prevToUnicodeStatus = 0x0000; + } + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -2),targetUniChar,data->currentDeltaToUnicode,err); + continue; + } + break; + } + /* else fall through to default */ + } + /* else fall through to default */ + U_FALLTHROUGH; + } + default:GET_MAPPING(sourceChar,targetUniChar,data) + ; + *contextCharToUnicode = sourceChar; + break; + } + + if (*toUnicodeStatus != missingCharMarker) { + /* Check to make sure that consonant clusters are handled correct for Gurmukhi script. */ + if (data->currentDeltaToUnicode == PNJ_DELTA && data->prevToUnicodeStatus != 0 && isPNJConsonant(data->prevToUnicodeStatus) && + (*toUnicodeStatus + PNJ_DELTA) == PNJ_SIGN_VIRAMA && ((UChar32)(targetUniChar + PNJ_DELTA) == data->prevToUnicodeStatus)) { + /* Consonant clusters C + HALANT + C should be encoded as ADHAK + C */ + offset = (int)(source-args->source - 3); + tempTargetUniChar = PNJ_ADHAK; /* This is necessary to avoid some compiler warnings. */ + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,offset,tempTargetUniChar,0,err); + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,offset,data->prevToUnicodeStatus,0,err); + data->prevToUnicodeStatus = 0x0000; /* reset the previous unicode code point */ + *toUnicodeStatus = missingCharMarker; + continue; + } else { + /* Write the previous toUnicodeStatus, this was delayed to handle consonant clustering for Gurmukhi script. */ + if (data->prevToUnicodeStatus) { + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -1),data->prevToUnicodeStatus,0,err); + data->prevToUnicodeStatus = 0x0000; + } + /* Check to make sure that Bindi and Tippi are handled correctly for Gurmukhi script. + * If 0xA2 is preceded by a codepoint in the PNJ_BINDI_TIPPI_SET then the target codepoint should be Tippi instead of Bindi. + */ + if (data->currentDeltaToUnicode == PNJ_DELTA && (targetUniChar + PNJ_DELTA) == PNJ_BINDI && isPNJBindiTippi((*toUnicodeStatus + PNJ_DELTA))) { + targetUniChar = PNJ_TIPPI - PNJ_DELTA; + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -2),*toUnicodeStatus,PNJ_DELTA,err); + } else if (data->currentDeltaToUnicode == PNJ_DELTA && (targetUniChar + PNJ_DELTA) == PNJ_SIGN_VIRAMA && isPNJConsonant((*toUnicodeStatus + PNJ_DELTA))) { + /* Store the current toUnicodeStatus code point for later handling of consonant cluster in Gurmukhi. */ + data->prevToUnicodeStatus = *toUnicodeStatus + PNJ_DELTA; + } else { + /* write the previously mapped codepoint */ + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source-args->source -2),*toUnicodeStatus,data->currentDeltaToUnicode,err); + } + } + *toUnicodeStatus = missingCharMarker; + } + + if (targetUniChar != missingCharMarker) { + /* now save the targetUniChar for delayed write */ + *toUnicodeStatus = (char16_t) targetUniChar; + if (data->resetToDefaultToUnicode) { + data->currentDeltaToUnicode = data->defDeltaToUnicode; + data->currentMaskToUnicode = data->defMaskToUnicode; + data->resetToDefaultToUnicode=false; + } + } else { + + /* we reach here only if targetUniChar == missingCharMarker + * so assign codes to reason and err + */ + *err = U_INVALID_CHAR_FOUND; +CALLBACK: + args->converter->toUBytes[0] = (uint8_t) sourceChar; + args->converter->toULength = 1; + break; + } + + } else { + *err =U_BUFFER_OVERFLOW_ERROR; + break; + } + } + + if (U_SUCCESS(*err) && args->flush && source == sourceLimit) { + /* end of the input stream */ + UConverter *cnv = args->converter; + + if (*contextCharToUnicode==ATR || *contextCharToUnicode==EXT || *contextCharToUnicode==ISCII_INV) { + /* set toUBytes[] */ + cnv->toUBytes[0] = (uint8_t)*contextCharToUnicode; + cnv->toULength = 1; + + /* avoid looping on truncated sequences */ + *contextCharToUnicode = NO_CHAR_MARKER; + } else { + cnv->toULength = 0; + } + + if (*toUnicodeStatus != missingCharMarker) { + /* output a remaining target character */ + WRITE_TO_TARGET_TO_U(args,source,target,args->offsets,(source - args->source -1),*toUnicodeStatus,data->currentDeltaToUnicode,err); + *toUnicodeStatus = missingCharMarker; + } + } + + args->target = target; + args->source = source; +} + +/* structure for SafeClone calculations */ +struct cloneISCIIStruct { + UConverter cnv; + UConverterDataISCII mydata; +}; + +static UConverter * U_CALLCONV +_ISCII_SafeClone(const UConverter *cnv, + void *stackBuffer, + int32_t *pBufferSize, + UErrorCode *status) +{ + struct cloneISCIIStruct * localClone; + int32_t bufferSizeNeeded = sizeof(struct cloneISCIIStruct); + + if (U_FAILURE(*status)) { + return 0; + } + + if (*pBufferSize == 0) { /* 'preflighting' request - set needed size into *pBufferSize */ + *pBufferSize = bufferSizeNeeded; + return 0; + } + + localClone = (struct cloneISCIIStruct *)stackBuffer; + /* ucnv.c/ucnv_safeClone() copied the main UConverter already */ + + uprv_memcpy(&localClone->mydata, cnv->extraInfo, sizeof(UConverterDataISCII)); + localClone->cnv.extraInfo = &localClone->mydata; + localClone->cnv.isExtraLocal = true; + + return &localClone->cnv; +} + +static void U_CALLCONV +_ISCIIGetUnicodeSet(const UConverter *cnv, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode) +{ + (void)cnv; + (void)which; + (void)pErrorCode; + int32_t idx, script; + uint8_t mask; + + /* Since all ISCII versions allow switching to other ISCII + scripts, we add all roundtrippable characters to this set. */ + sa->addRange(sa->set, 0, ASCII_END); + for (script = DEVANAGARI; script <= MALAYALAM; script++) { + mask = (uint8_t)(lookupInitialData[script].maskEnum); + for (idx = 0; idx < DELTA; idx++) { + /* added check for TELUGU character */ + if ((validityTable[idx] & mask) || (script==TELUGU && idx==0x31)) { + sa->add(sa->set, idx + (script * DELTA) + INDIC_BLOCK_BEGIN); + } + } + } + sa->add(sa->set, DANDA); + sa->add(sa->set, DOUBLE_DANDA); + sa->add(sa->set, ZWNJ); + sa->add(sa->set, ZWJ); +} +U_CDECL_END +static const UConverterImpl _ISCIIImpl={ + + UCNV_ISCII, + + nullptr, + nullptr, + + _ISCIIOpen, + _ISCIIClose, + _ISCIIReset, + + UConverter_toUnicode_ISCII_OFFSETS_LOGIC, + UConverter_toUnicode_ISCII_OFFSETS_LOGIC, + UConverter_fromUnicode_ISCII_OFFSETS_LOGIC, + UConverter_fromUnicode_ISCII_OFFSETS_LOGIC, + nullptr, + + nullptr, + _ISCIIgetName, + nullptr, + _ISCII_SafeClone, + _ISCIIGetUnicodeSet, + nullptr, + nullptr +}; + +static const UConverterStaticData _ISCIIStaticData={ + sizeof(UConverterStaticData), + "ISCII", + 0, + UCNV_IBM, + UCNV_ISCII, + 1, + 4, + { 0x1a, 0, 0, 0 }, + 0x1, + false, + false, + 0x0, + 0x0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, /* reserved */ + +}; + +const UConverterSharedData _ISCIIData= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_ISCIIStaticData, &_ISCIIImpl); + +#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ diff --git a/deps/icu-small/source/common/ucnvlat1.cpp b/deps/icu-small/source/common/ucnvlat1.cpp index 05aad6a0e039b8..742c17f55ef0b3 100644 --- a/deps/icu-small/source/common/ucnvlat1.cpp +++ b/deps/icu-small/source/common/ucnvlat1.cpp @@ -1,756 +1,756 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2000-2015, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: ucnvlat1.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2000feb07 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/uset.h" -#include "unicode/utf8.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" -#include "ustr_imp.h" - -/* control optimizations according to the platform */ -#define LATIN1_UNROLL_FROM_UNICODE 1 - -/* ISO 8859-1 --------------------------------------------------------------- */ - -/* This is a table-less and callback-less version of ucnv_MBCSSingleToBMPWithOffsets(). */ -U_CDECL_BEGIN -static void U_CALLCONV -_Latin1ToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - const uint8_t *source; - UChar *target; - int32_t targetCapacity, length; - int32_t *offsets; - - int32_t sourceIndex; - - /* set up the local pointers */ - source=(const uint8_t *)pArgs->source; - target=pArgs->target; - targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); - offsets=pArgs->offsets; - - sourceIndex=0; - - /* - * since the conversion here is 1:1 UChar:uint8_t, we need only one counter - * for the minimum of the sourceLength and targetCapacity - */ - length=(int32_t)((const uint8_t *)pArgs->sourceLimit-source); - if(length<=targetCapacity) { - targetCapacity=length; - } else { - /* target will be full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - length=targetCapacity; - } - - if(targetCapacity>=8) { - /* This loop is unrolled for speed and improved pipelining. */ - int32_t count, loops; - - loops=count=targetCapacity>>3; - length=targetCapacity&=0x7; - do { - target[0]=source[0]; - target[1]=source[1]; - target[2]=source[2]; - target[3]=source[3]; - target[4]=source[4]; - target[5]=source[5]; - target[6]=source[6]; - target[7]=source[7]; - target+=8; - source+=8; - } while(--count>0); - - if(offsets!=NULL) { - do { - offsets[0]=sourceIndex++; - offsets[1]=sourceIndex++; - offsets[2]=sourceIndex++; - offsets[3]=sourceIndex++; - offsets[4]=sourceIndex++; - offsets[5]=sourceIndex++; - offsets[6]=sourceIndex++; - offsets[7]=sourceIndex++; - offsets+=8; - } while(--loops>0); - } - } - - /* conversion loop */ - while(targetCapacity>0) { - *target++=*source++; - --targetCapacity; - } - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - - /* set offsets */ - if(offsets!=NULL) { - while(length>0) { - *offsets++=sourceIndex++; - --length; - } - pArgs->offsets=offsets; - } -} - -/* This is a table-less and callback-less version of ucnv_MBCSSingleGetNextUChar(). */ -static UChar32 U_CALLCONV -_Latin1GetNextUChar(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - const uint8_t *source=(const uint8_t *)pArgs->source; - if(source<(const uint8_t *)pArgs->sourceLimit) { - pArgs->source=(const char *)(source+1); - return *source; - } - - /* no output because of empty input */ - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0xffff; -} - -/* This is a table-less version of ucnv_MBCSSingleFromBMPWithOffsets(). */ -static void U_CALLCONV -_Latin1FromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const UChar *source, *sourceLimit; - uint8_t *target, *oldTarget; - int32_t targetCapacity, length; - int32_t *offsets; - - UChar32 cp; - UChar c, max; - - int32_t sourceIndex; - - /* set up the local pointers */ - cnv=pArgs->converter; - source=pArgs->source; - sourceLimit=pArgs->sourceLimit; - target=oldTarget=(uint8_t *)pArgs->target; - targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); - offsets=pArgs->offsets; - - if(cnv->sharedData==&_Latin1Data) { - max=0xff; /* Latin-1 */ - } else { - max=0x7f; /* US-ASCII */ - } - - /* get the converter state from UConverter */ - cp=cnv->fromUChar32; - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex= cp==0 ? 0 : -1; - - /* - * since the conversion here is 1:1 UChar:uint8_t, we need only one counter - * for the minimum of the sourceLength and targetCapacity - */ - length=(int32_t)(sourceLimit-source); - if(length0) { - goto getTrail; - } - -#if LATIN1_UNROLL_FROM_UNICODE - /* unroll the loop with the most common case */ - if(targetCapacity>=16) { - int32_t count, loops; - UChar u, oredChars; - - loops=count=targetCapacity>>4; - do { - oredChars=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - oredChars|=u=*source++; - *target++=(uint8_t)u; - - /* were all 16 entries really valid? */ - if(oredChars>max) { - /* no, return to the first of these 16 */ - source-=16; - target-=16; - break; - } - } while(--count>0); - count=loops-count; - targetCapacity-=16*count; - - if(offsets!=NULL) { - oldTarget+=16*count; - while(count>0) { - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - --count; - } - } - } -#endif - - /* conversion loop */ - c=0; - while(targetCapacity>0 && (c=*source++)<=max) { - /* convert the Unicode code point */ - *target++=(uint8_t)c; - --targetCapacity; - } - - if(c>max) { - cp=c; - if(!U_IS_SURROGATE(cp)) { - /* callback(unassigned) */ - } else if(U_IS_SURROGATE_LEAD(cp)) { -getTrail: - if(sourcefromUChar32=cp; - goto noMoreInput; - } - } else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - } - - *pErrorCode= U_IS_SURROGATE(cp) ? U_ILLEGAL_CHAR_FOUND : U_INVALID_CHAR_FOUND; - cnv->fromUChar32=cp; - } -noMoreInput: - - /* set offsets since the start */ - if(offsets!=NULL) { - size_t count=target-oldTarget; - while(count>0) { - *offsets++=sourceIndex++; - --count; - } - } - - if(U_SUCCESS(*pErrorCode) && source=(uint8_t *)pArgs->targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - - /* write back the updated pointers */ - pArgs->source=source; - pArgs->target=(char *)target; - pArgs->offsets=offsets; -} - -/* Convert UTF-8 to Latin-1. Adapted from ucnv_SBCSFromUTF8(). */ -static void U_CALLCONV -ucnv_Latin1FromUTF8(UConverterFromUnicodeArgs *pFromUArgs, - UConverterToUnicodeArgs *pToUArgs, - UErrorCode *pErrorCode) { - UConverter *utf8; - const uint8_t *source, *sourceLimit; - uint8_t *target; - int32_t targetCapacity; - - UChar32 c; - uint8_t b, t1; - - /* set up the local pointers */ - utf8=pToUArgs->converter; - source=(uint8_t *)pToUArgs->source; - sourceLimit=(uint8_t *)pToUArgs->sourceLimit; - target=(uint8_t *)pFromUArgs->target; - targetCapacity=(int32_t)(pFromUArgs->targetLimit-pFromUArgs->target); - - /* get the converter state from the UTF-8 UConverter */ - if (utf8->toULength > 0) { - c=(UChar32)utf8->toUnicodeStatus; - } else { - c = 0; - } - if(c!=0 && source=0xc2 && c<=0xc3 && (t1=(uint8_t)(*source-0x80)) <= 0x3f) { - ++source; - *target++=(uint8_t)(((c&3)<<6)|t1); - --targetCapacity; - - utf8->toUnicodeStatus=0; - utf8->toULength=0; - } else { - /* complicated, illegal or unmappable input: fall back to the pivoting implementation */ - *pErrorCode=U_USING_DEFAULT_WARNING; - return; - } - } - - /* - * Make sure that the last byte sequence before sourceLimit is complete - * or runs into a lead byte. - * In the conversion loop compare source with sourceLimit only once - * per multi-byte character. - * For Latin-1, adjust sourceLimit only for 1 trail byte because - * the conversion loop handles at most 2-byte sequences. - */ - if(source0) { - b=*source++; - if(U8_IS_SINGLE(b)) { - /* convert ASCII */ - *target++=(uint8_t)b; - --targetCapacity; - } else if( /* handle U+0080..U+00FF inline */ - b>=0xc2 && b<=0xc3 && - (t1=(uint8_t)(*source-0x80)) <= 0x3f - ) { - ++source; - *target++=(uint8_t)(((b&3)<<6)|t1); - --targetCapacity; - } else { - /* complicated, illegal or unmappable input: fall back to the pivoting implementation */ - pToUArgs->source=(char *)(source-1); - pFromUArgs->target=(char *)target; - *pErrorCode=U_USING_DEFAULT_WARNING; - return; - } - } else { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - - /* - * The sourceLimit may have been adjusted before the conversion loop - * to stop before a truncated sequence. - * If so, then collect the truncated sequence now. - * For Latin-1, there is at most exactly one lead byte because of the - * smaller sourceLimit adjustment logic. - */ - if(U_SUCCESS(*pErrorCode) && source<(sourceLimit=(uint8_t *)pToUArgs->sourceLimit)) { - utf8->toUnicodeStatus=utf8->toUBytes[0]=b=*source++; - utf8->toULength=1; - utf8->mode=U8_COUNT_BYTES(b); - } - - /* write back the updated pointers */ - pToUArgs->source=(char *)source; - pFromUArgs->target=(char *)target; -} - -static void U_CALLCONV -_Latin1GetUnicodeSet(const UConverter *cnv, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode) { - (void)cnv; - (void)which; - (void)pErrorCode; - sa->addRange(sa->set, 0, 0xff); -} -U_CDECL_END - - -static const UConverterImpl _Latin1Impl={ - UCNV_LATIN_1, - - NULL, - NULL, - - NULL, - NULL, - NULL, - - _Latin1ToUnicodeWithOffsets, - _Latin1ToUnicodeWithOffsets, - _Latin1FromUnicodeWithOffsets, - _Latin1FromUnicodeWithOffsets, - _Latin1GetNextUChar, - - NULL, - NULL, - NULL, - NULL, - _Latin1GetUnicodeSet, - - NULL, - ucnv_Latin1FromUTF8 -}; - -static const UConverterStaticData _Latin1StaticData={ - sizeof(UConverterStaticData), - "ISO-8859-1", - 819, UCNV_IBM, UCNV_LATIN_1, 1, 1, - { 0x1a, 0, 0, 0 }, 1, false, false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - -const UConverterSharedData _Latin1Data= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_Latin1StaticData, &_Latin1Impl); - -/* US-ASCII ----------------------------------------------------------------- */ - -U_CDECL_BEGIN -/* This is a table-less version of ucnv_MBCSSingleToBMPWithOffsets(). */ -static void U_CALLCONV -_ASCIIToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - const uint8_t *source, *sourceLimit; - UChar *target, *oldTarget; - int32_t targetCapacity, length; - int32_t *offsets; - - int32_t sourceIndex; - - uint8_t c; - - /* set up the local pointers */ - source=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - target=oldTarget=pArgs->target; - targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); - offsets=pArgs->offsets; - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex=0; - - /* - * since the conversion here is 1:1 UChar:uint8_t, we need only one counter - * for the minimum of the sourceLength and targetCapacity - */ - length=(int32_t)(sourceLimit-source); - if(length=8) { - /* This loop is unrolled for speed and improved pipelining. */ - int32_t count, loops; - UChar oredChars; - - loops=count=targetCapacity>>3; - do { - oredChars=target[0]=source[0]; - oredChars|=target[1]=source[1]; - oredChars|=target[2]=source[2]; - oredChars|=target[3]=source[3]; - oredChars|=target[4]=source[4]; - oredChars|=target[5]=source[5]; - oredChars|=target[6]=source[6]; - oredChars|=target[7]=source[7]; - - /* were all 16 entries really valid? */ - if(oredChars>0x7f) { - /* no, return to the first of these 16 */ - break; - } - source+=8; - target+=8; - } while(--count>0); - count=loops-count; - targetCapacity-=count*8; - - if(offsets!=NULL) { - oldTarget+=count*8; - while(count>0) { - offsets[0]=sourceIndex++; - offsets[1]=sourceIndex++; - offsets[2]=sourceIndex++; - offsets[3]=sourceIndex++; - offsets[4]=sourceIndex++; - offsets[5]=sourceIndex++; - offsets[6]=sourceIndex++; - offsets[7]=sourceIndex++; - offsets+=8; - --count; - } - } - } - - /* conversion loop */ - c=0; - while(targetCapacity>0 && (c=*source++)<=0x7f) { - *target++=c; - --targetCapacity; - } - - if(c>0x7f) { - /* callback(illegal); copy the current bytes to toUBytes[] */ - UConverter *cnv=pArgs->converter; - cnv->toUBytes[0]=c; - cnv->toULength=1; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } else if(source=pArgs->targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - - /* set offsets since the start */ - if(offsets!=NULL) { - size_t count=target-oldTarget; - while(count>0) { - *offsets++=sourceIndex++; - --count; - } - } - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - pArgs->offsets=offsets; -} - -/* This is a table-less version of ucnv_MBCSSingleGetNextUChar(). */ -static UChar32 U_CALLCONV -_ASCIIGetNextUChar(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - const uint8_t *source; - uint8_t b; - - source=(const uint8_t *)pArgs->source; - if(source<(const uint8_t *)pArgs->sourceLimit) { - b=*source++; - pArgs->source=(const char *)source; - if(b<=0x7f) { - return b; - } else { - UConverter *cnv=pArgs->converter; - cnv->toUBytes[0]=b; - cnv->toULength=1; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - return 0xffff; - } - } - - /* no output because of empty input */ - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0xffff; -} - -/* "Convert" UTF-8 to US-ASCII: Validate and copy. */ -static void U_CALLCONV -ucnv_ASCIIFromUTF8(UConverterFromUnicodeArgs *pFromUArgs, - UConverterToUnicodeArgs *pToUArgs, - UErrorCode *pErrorCode) { - const uint8_t *source, *sourceLimit; - uint8_t *target; - int32_t targetCapacity, length; - - uint8_t c; - - if(pToUArgs->converter->toULength > 0) { - /* no handling of partial UTF-8 characters here, fall back to pivoting */ - *pErrorCode=U_USING_DEFAULT_WARNING; - return; - } - - /* set up the local pointers */ - source=(const uint8_t *)pToUArgs->source; - sourceLimit=(const uint8_t *)pToUArgs->sourceLimit; - target=(uint8_t *)pFromUArgs->target; - targetCapacity=(int32_t)(pFromUArgs->targetLimit-pFromUArgs->target); - - /* - * since the conversion here is 1:1 uint8_t:uint8_t, we need only one counter - * for the minimum of the sourceLength and targetCapacity - */ - length=(int32_t)(sourceLimit-source); - if(length=16) { - int32_t count, loops; - uint8_t oredChars; - - loops=count=targetCapacity>>4; - do { - oredChars=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - oredChars|=*target++=*source++; - - /* were all 16 entries really valid? */ - if(oredChars>0x7f) { - /* no, return to the first of these 16 */ - source-=16; - target-=16; - break; - } - } while(--count>0); - count=loops-count; - targetCapacity-=16*count; - } - - /* conversion loop */ - c=0; - while(targetCapacity>0 && (c=*source)<=0x7f) { - ++source; - *target++=c; - --targetCapacity; - } - - if(c>0x7f) { - /* non-ASCII character, handle in standard converter */ - *pErrorCode=U_USING_DEFAULT_WARNING; - } else if(source=(const uint8_t *)pFromUArgs->targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - - /* write back the updated pointers */ - pToUArgs->source=(const char *)source; - pFromUArgs->target=(char *)target; -} - -static void U_CALLCONV -_ASCIIGetUnicodeSet(const UConverter *cnv, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode) { - (void)cnv; - (void)which; - (void)pErrorCode; - sa->addRange(sa->set, 0, 0x7f); -} -U_CDECL_END - -static const UConverterImpl _ASCIIImpl={ - UCNV_US_ASCII, - - NULL, - NULL, - - NULL, - NULL, - NULL, - - _ASCIIToUnicodeWithOffsets, - _ASCIIToUnicodeWithOffsets, - _Latin1FromUnicodeWithOffsets, - _Latin1FromUnicodeWithOffsets, - _ASCIIGetNextUChar, - - NULL, - NULL, - NULL, - NULL, - _ASCIIGetUnicodeSet, - - NULL, - ucnv_ASCIIFromUTF8 -}; - -static const UConverterStaticData _ASCIIStaticData={ - sizeof(UConverterStaticData), - "US-ASCII", - 367, UCNV_IBM, UCNV_US_ASCII, 1, 1, - { 0x1a, 0, 0, 0 }, 1, false, false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - -const UConverterSharedData _ASCIIData= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_ASCIIStaticData, &_ASCIIImpl); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2000-2015, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: ucnvlat1.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2000feb07 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/uset.h" +#include "unicode/utf8.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" +#include "ustr_imp.h" + +/* control optimizations according to the platform */ +#define LATIN1_UNROLL_FROM_UNICODE 1 + +/* ISO 8859-1 --------------------------------------------------------------- */ + +/* This is a table-less and callback-less version of ucnv_MBCSSingleToBMPWithOffsets(). */ +U_CDECL_BEGIN +static void U_CALLCONV +_Latin1ToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + const uint8_t *source; + char16_t *target; + int32_t targetCapacity, length; + int32_t *offsets; + + int32_t sourceIndex; + + /* set up the local pointers */ + source=(const uint8_t *)pArgs->source; + target=pArgs->target; + targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); + offsets=pArgs->offsets; + + sourceIndex=0; + + /* + * since the conversion here is 1:1 char16_t:uint8_t, we need only one counter + * for the minimum of the sourceLength and targetCapacity + */ + length=(int32_t)((const uint8_t *)pArgs->sourceLimit-source); + if(length<=targetCapacity) { + targetCapacity=length; + } else { + /* target will be full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + length=targetCapacity; + } + + if(targetCapacity>=8) { + /* This loop is unrolled for speed and improved pipelining. */ + int32_t count, loops; + + loops=count=targetCapacity>>3; + length=targetCapacity&=0x7; + do { + target[0]=source[0]; + target[1]=source[1]; + target[2]=source[2]; + target[3]=source[3]; + target[4]=source[4]; + target[5]=source[5]; + target[6]=source[6]; + target[7]=source[7]; + target+=8; + source+=8; + } while(--count>0); + + if(offsets!=nullptr) { + do { + offsets[0]=sourceIndex++; + offsets[1]=sourceIndex++; + offsets[2]=sourceIndex++; + offsets[3]=sourceIndex++; + offsets[4]=sourceIndex++; + offsets[5]=sourceIndex++; + offsets[6]=sourceIndex++; + offsets[7]=sourceIndex++; + offsets+=8; + } while(--loops>0); + } + } + + /* conversion loop */ + while(targetCapacity>0) { + *target++=*source++; + --targetCapacity; + } + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + + /* set offsets */ + if(offsets!=nullptr) { + while(length>0) { + *offsets++=sourceIndex++; + --length; + } + pArgs->offsets=offsets; + } +} + +/* This is a table-less and callback-less version of ucnv_MBCSSingleGetNextUChar(). */ +static UChar32 U_CALLCONV +_Latin1GetNextUChar(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + const uint8_t *source=(const uint8_t *)pArgs->source; + if(source<(const uint8_t *)pArgs->sourceLimit) { + pArgs->source=(const char *)(source+1); + return *source; + } + + /* no output because of empty input */ + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0xffff; +} + +/* This is a table-less version of ucnv_MBCSSingleFromBMPWithOffsets(). */ +static void U_CALLCONV +_Latin1FromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const char16_t *source, *sourceLimit; + uint8_t *target, *oldTarget; + int32_t targetCapacity, length; + int32_t *offsets; + + UChar32 cp; + char16_t c, max; + + int32_t sourceIndex; + + /* set up the local pointers */ + cnv=pArgs->converter; + source=pArgs->source; + sourceLimit=pArgs->sourceLimit; + target=oldTarget=(uint8_t *)pArgs->target; + targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); + offsets=pArgs->offsets; + + if(cnv->sharedData==&_Latin1Data) { + max=0xff; /* Latin-1 */ + } else { + max=0x7f; /* US-ASCII */ + } + + /* get the converter state from UConverter */ + cp=cnv->fromUChar32; + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex= cp==0 ? 0 : -1; + + /* + * since the conversion here is 1:1 char16_t:uint8_t, we need only one counter + * for the minimum of the sourceLength and targetCapacity + */ + length=(int32_t)(sourceLimit-source); + if(length0) { + goto getTrail; + } + +#if LATIN1_UNROLL_FROM_UNICODE + /* unroll the loop with the most common case */ + if(targetCapacity>=16) { + int32_t count, loops; + char16_t u, oredChars; + + loops=count=targetCapacity>>4; + do { + oredChars=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + oredChars|=u=*source++; + *target++=(uint8_t)u; + + /* were all 16 entries really valid? */ + if(oredChars>max) { + /* no, return to the first of these 16 */ + source-=16; + target-=16; + break; + } + } while(--count>0); + count=loops-count; + targetCapacity-=16*count; + + if(offsets!=nullptr) { + oldTarget+=16*count; + while(count>0) { + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + --count; + } + } + } +#endif + + /* conversion loop */ + c=0; + while(targetCapacity>0 && (c=*source++)<=max) { + /* convert the Unicode code point */ + *target++=(uint8_t)c; + --targetCapacity; + } + + if(c>max) { + cp=c; + if(!U_IS_SURROGATE(cp)) { + /* callback(unassigned) */ + } else if(U_IS_SURROGATE_LEAD(cp)) { +getTrail: + if(sourcefromUChar32=cp; + goto noMoreInput; + } + } else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + } + + *pErrorCode= U_IS_SURROGATE(cp) ? U_ILLEGAL_CHAR_FOUND : U_INVALID_CHAR_FOUND; + cnv->fromUChar32=cp; + } +noMoreInput: + + /* set offsets since the start */ + if(offsets!=nullptr) { + size_t count=target-oldTarget; + while(count>0) { + *offsets++=sourceIndex++; + --count; + } + } + + if(U_SUCCESS(*pErrorCode) && source=(uint8_t *)pArgs->targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + + /* write back the updated pointers */ + pArgs->source=source; + pArgs->target=(char *)target; + pArgs->offsets=offsets; +} + +/* Convert UTF-8 to Latin-1. Adapted from ucnv_SBCSFromUTF8(). */ +static void U_CALLCONV +ucnv_Latin1FromUTF8(UConverterFromUnicodeArgs *pFromUArgs, + UConverterToUnicodeArgs *pToUArgs, + UErrorCode *pErrorCode) { + UConverter *utf8; + const uint8_t *source, *sourceLimit; + uint8_t *target; + int32_t targetCapacity; + + UChar32 c; + uint8_t b, t1; + + /* set up the local pointers */ + utf8=pToUArgs->converter; + source=(uint8_t *)pToUArgs->source; + sourceLimit=(uint8_t *)pToUArgs->sourceLimit; + target=(uint8_t *)pFromUArgs->target; + targetCapacity=(int32_t)(pFromUArgs->targetLimit-pFromUArgs->target); + + /* get the converter state from the UTF-8 UConverter */ + if (utf8->toULength > 0) { + c=(UChar32)utf8->toUnicodeStatus; + } else { + c = 0; + } + if(c!=0 && source=0xc2 && c<=0xc3 && (t1=(uint8_t)(*source-0x80)) <= 0x3f) { + ++source; + *target++=(uint8_t)(((c&3)<<6)|t1); + --targetCapacity; + + utf8->toUnicodeStatus=0; + utf8->toULength=0; + } else { + /* complicated, illegal or unmappable input: fall back to the pivoting implementation */ + *pErrorCode=U_USING_DEFAULT_WARNING; + return; + } + } + + /* + * Make sure that the last byte sequence before sourceLimit is complete + * or runs into a lead byte. + * In the conversion loop compare source with sourceLimit only once + * per multi-byte character. + * For Latin-1, adjust sourceLimit only for 1 trail byte because + * the conversion loop handles at most 2-byte sequences. + */ + if(source0) { + b=*source++; + if(U8_IS_SINGLE(b)) { + /* convert ASCII */ + *target++=(uint8_t)b; + --targetCapacity; + } else if( /* handle U+0080..U+00FF inline */ + b>=0xc2 && b<=0xc3 && + (t1=(uint8_t)(*source-0x80)) <= 0x3f + ) { + ++source; + *target++=(uint8_t)(((b&3)<<6)|t1); + --targetCapacity; + } else { + /* complicated, illegal or unmappable input: fall back to the pivoting implementation */ + pToUArgs->source=(char *)(source-1); + pFromUArgs->target=(char *)target; + *pErrorCode=U_USING_DEFAULT_WARNING; + return; + } + } else { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + + /* + * The sourceLimit may have been adjusted before the conversion loop + * to stop before a truncated sequence. + * If so, then collect the truncated sequence now. + * For Latin-1, there is at most exactly one lead byte because of the + * smaller sourceLimit adjustment logic. + */ + if(U_SUCCESS(*pErrorCode) && source<(sourceLimit=(uint8_t *)pToUArgs->sourceLimit)) { + utf8->toUnicodeStatus=utf8->toUBytes[0]=b=*source++; + utf8->toULength=1; + utf8->mode=U8_COUNT_BYTES(b); + } + + /* write back the updated pointers */ + pToUArgs->source=(char *)source; + pFromUArgs->target=(char *)target; +} + +static void U_CALLCONV +_Latin1GetUnicodeSet(const UConverter *cnv, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode) { + (void)cnv; + (void)which; + (void)pErrorCode; + sa->addRange(sa->set, 0, 0xff); +} +U_CDECL_END + + +static const UConverterImpl _Latin1Impl={ + UCNV_LATIN_1, + + nullptr, + nullptr, + + nullptr, + nullptr, + nullptr, + + _Latin1ToUnicodeWithOffsets, + _Latin1ToUnicodeWithOffsets, + _Latin1FromUnicodeWithOffsets, + _Latin1FromUnicodeWithOffsets, + _Latin1GetNextUChar, + + nullptr, + nullptr, + nullptr, + nullptr, + _Latin1GetUnicodeSet, + + nullptr, + ucnv_Latin1FromUTF8 +}; + +static const UConverterStaticData _Latin1StaticData={ + sizeof(UConverterStaticData), + "ISO-8859-1", + 819, UCNV_IBM, UCNV_LATIN_1, 1, 1, + { 0x1a, 0, 0, 0 }, 1, false, false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + +const UConverterSharedData _Latin1Data= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_Latin1StaticData, &_Latin1Impl); + +/* US-ASCII ----------------------------------------------------------------- */ + +U_CDECL_BEGIN +/* This is a table-less version of ucnv_MBCSSingleToBMPWithOffsets(). */ +static void U_CALLCONV +_ASCIIToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + const uint8_t *source, *sourceLimit; + char16_t *target, *oldTarget; + int32_t targetCapacity, length; + int32_t *offsets; + + int32_t sourceIndex; + + uint8_t c; + + /* set up the local pointers */ + source=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + target=oldTarget=pArgs->target; + targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); + offsets=pArgs->offsets; + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex=0; + + /* + * since the conversion here is 1:1 char16_t:uint8_t, we need only one counter + * for the minimum of the sourceLength and targetCapacity + */ + length=(int32_t)(sourceLimit-source); + if(length=8) { + /* This loop is unrolled for speed and improved pipelining. */ + int32_t count, loops; + char16_t oredChars; + + loops=count=targetCapacity>>3; + do { + oredChars=target[0]=source[0]; + oredChars|=target[1]=source[1]; + oredChars|=target[2]=source[2]; + oredChars|=target[3]=source[3]; + oredChars|=target[4]=source[4]; + oredChars|=target[5]=source[5]; + oredChars|=target[6]=source[6]; + oredChars|=target[7]=source[7]; + + /* were all 16 entries really valid? */ + if(oredChars>0x7f) { + /* no, return to the first of these 16 */ + break; + } + source+=8; + target+=8; + } while(--count>0); + count=loops-count; + targetCapacity-=count*8; + + if(offsets!=nullptr) { + oldTarget+=count*8; + while(count>0) { + offsets[0]=sourceIndex++; + offsets[1]=sourceIndex++; + offsets[2]=sourceIndex++; + offsets[3]=sourceIndex++; + offsets[4]=sourceIndex++; + offsets[5]=sourceIndex++; + offsets[6]=sourceIndex++; + offsets[7]=sourceIndex++; + offsets+=8; + --count; + } + } + } + + /* conversion loop */ + c=0; + while(targetCapacity>0 && (c=*source++)<=0x7f) { + *target++=c; + --targetCapacity; + } + + if(c>0x7f) { + /* callback(illegal); copy the current bytes to toUBytes[] */ + UConverter *cnv=pArgs->converter; + cnv->toUBytes[0]=c; + cnv->toULength=1; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } else if(source=pArgs->targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + + /* set offsets since the start */ + if(offsets!=nullptr) { + size_t count=target-oldTarget; + while(count>0) { + *offsets++=sourceIndex++; + --count; + } + } + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + pArgs->offsets=offsets; +} + +/* This is a table-less version of ucnv_MBCSSingleGetNextUChar(). */ +static UChar32 U_CALLCONV +_ASCIIGetNextUChar(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + const uint8_t *source; + uint8_t b; + + source=(const uint8_t *)pArgs->source; + if(source<(const uint8_t *)pArgs->sourceLimit) { + b=*source++; + pArgs->source=(const char *)source; + if(b<=0x7f) { + return b; + } else { + UConverter *cnv=pArgs->converter; + cnv->toUBytes[0]=b; + cnv->toULength=1; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + return 0xffff; + } + } + + /* no output because of empty input */ + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0xffff; +} + +/* "Convert" UTF-8 to US-ASCII: Validate and copy. */ +static void U_CALLCONV +ucnv_ASCIIFromUTF8(UConverterFromUnicodeArgs *pFromUArgs, + UConverterToUnicodeArgs *pToUArgs, + UErrorCode *pErrorCode) { + const uint8_t *source, *sourceLimit; + uint8_t *target; + int32_t targetCapacity, length; + + uint8_t c; + + if(pToUArgs->converter->toULength > 0) { + /* no handling of partial UTF-8 characters here, fall back to pivoting */ + *pErrorCode=U_USING_DEFAULT_WARNING; + return; + } + + /* set up the local pointers */ + source=(const uint8_t *)pToUArgs->source; + sourceLimit=(const uint8_t *)pToUArgs->sourceLimit; + target=(uint8_t *)pFromUArgs->target; + targetCapacity=(int32_t)(pFromUArgs->targetLimit-pFromUArgs->target); + + /* + * since the conversion here is 1:1 uint8_t:uint8_t, we need only one counter + * for the minimum of the sourceLength and targetCapacity + */ + length=(int32_t)(sourceLimit-source); + if(length=16) { + int32_t count, loops; + uint8_t oredChars; + + loops=count=targetCapacity>>4; + do { + oredChars=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + oredChars|=*target++=*source++; + + /* were all 16 entries really valid? */ + if(oredChars>0x7f) { + /* no, return to the first of these 16 */ + source-=16; + target-=16; + break; + } + } while(--count>0); + count=loops-count; + targetCapacity-=16*count; + } + + /* conversion loop */ + c=0; + while(targetCapacity>0 && (c=*source)<=0x7f) { + ++source; + *target++=c; + --targetCapacity; + } + + if(c>0x7f) { + /* non-ASCII character, handle in standard converter */ + *pErrorCode=U_USING_DEFAULT_WARNING; + } else if(source=(const uint8_t *)pFromUArgs->targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + + /* write back the updated pointers */ + pToUArgs->source=(const char *)source; + pFromUArgs->target=(char *)target; +} + +static void U_CALLCONV +_ASCIIGetUnicodeSet(const UConverter *cnv, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode) { + (void)cnv; + (void)which; + (void)pErrorCode; + sa->addRange(sa->set, 0, 0x7f); +} +U_CDECL_END + +static const UConverterImpl _ASCIIImpl={ + UCNV_US_ASCII, + + nullptr, + nullptr, + + nullptr, + nullptr, + nullptr, + + _ASCIIToUnicodeWithOffsets, + _ASCIIToUnicodeWithOffsets, + _Latin1FromUnicodeWithOffsets, + _Latin1FromUnicodeWithOffsets, + _ASCIIGetNextUChar, + + nullptr, + nullptr, + nullptr, + nullptr, + _ASCIIGetUnicodeSet, + + nullptr, + ucnv_ASCIIFromUTF8 +}; + +static const UConverterStaticData _ASCIIStaticData={ + sizeof(UConverterStaticData), + "US-ASCII", + 367, UCNV_IBM, UCNV_US_ASCII, 1, 1, + { 0x1a, 0, 0, 0 }, 1, false, false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + +const UConverterSharedData _ASCIIData= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_ASCIIStaticData, &_ASCIIImpl); + +#endif diff --git a/deps/icu-small/source/common/ucnvmbcs.cpp b/deps/icu-small/source/common/ucnvmbcs.cpp index 0e753c8ffbfb11..79d4cbb80cb487 100644 --- a/deps/icu-small/source/common/ucnvmbcs.cpp +++ b/deps/icu-small/source/common/ucnvmbcs.cpp @@ -1,5723 +1,5723 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2000-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: ucnvmbcs.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2000jul03 -* created by: Markus W. Scherer -* -* The current code in this file replaces the previous implementation -* of conversion code from multi-byte codepages to Unicode and back. -* This implementation supports the following: -* - legacy variable-length codepages with up to 4 bytes per character -* - all Unicode code points (up to 0x10ffff) -* - efficient distinction of unassigned vs. illegal byte sequences -* - it is possible in fromUnicode() to directly deal with simple -* stateful encodings (used for EBCDIC_STATEFUL) -* - it is possible to convert Unicode code points -* to a single zero byte (but not as a fallback except for SBCS) -* -* Remaining limitations in fromUnicode: -* - byte sequences must not have leading zero bytes -* - except for SBCS codepages: no fallback mapping from Unicode to a zero byte -* - limitation to up to 4 bytes per character -* -* ICU 2.8 (late 2003) adds a secondary data structure which lifts some of these -* limitations and adds m:n character mappings and other features. -* See ucnv_ext.h for details. -* -* Change history: -* -* 5/6/2001 Ram Moved MBCS_SINGLE_RESULT_FROM_U,MBCS_STAGE_2_FROM_U, -* MBCS_VALUE_2_FROM_STAGE_2, MBCS_VALUE_4_FROM_STAGE_2 -* macros to ucnvmbcs.h file -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/ucnv_cb.h" -#include "unicode/udata.h" -#include "unicode/uset.h" -#include "unicode/utf8.h" -#include "unicode/utf16.h" -#include "ucnv_bld.h" -#include "ucnvmbcs.h" -#include "ucnv_ext.h" -#include "ucnv_cnv.h" -#include "cmemory.h" -#include "cstring.h" -#include "umutex.h" -#include "ustr_imp.h" - -/* control optimizations according to the platform */ -#define MBCS_UNROLL_SINGLE_TO_BMP 1 -#define MBCS_UNROLL_SINGLE_FROM_BMP 0 - -/* - * _MBCSHeader versions 5.3 & 4.3 - * (Note that the _MBCSHeader version is in addition to the converter formatVersion.) - * - * This version is optional. Version 5 is used for incompatible data format changes. - * makeconv will continue to generate version 4 files if possible. - * - * Changes from version 4: - * - * The main difference is an additional _MBCSHeader field with - * - the length (number of uint32_t) of the _MBCSHeader - * - flags for further incompatible data format changes - * - flags for further, backward compatible data format changes - * - * The MBCS_OPT_FROM_U flag indicates that most of the fromUnicode data is omitted from - * the file and needs to be reconstituted at load time. - * This requires a utf8Friendly format with an additional mbcsIndex table for fast - * (and UTF-8-friendly) fromUnicode conversion for Unicode code points up to maxFastUChar. - * (For details about these structures see below, and see ucnvmbcs.h.) - * - * utf8Friendly also implies that the fromUnicode mappings are stored in ascending order - * of the Unicode code points. (This requires that the .ucm file has the |0 etc. - * precision markers for all mappings.) - * - * All fallbacks have been moved to the extension table, leaving only roundtrips in the - * omitted data that can be reconstituted from the toUnicode data. - * - * Of the stage 2 table, the part corresponding to maxFastUChar and below is omitted. - * With only roundtrip mappings in the base fromUnicode data, this part is fully - * redundant with the mbcsIndex and will be reconstituted from that (also using the - * stage 1 table which contains the information about how stage 2 was compacted). - * - * The rest of the stage 2 table, the part for code points above maxFastUChar, - * is stored in the file and will be appended to the reconstituted part. - * - * The entire fromUBytes array is omitted from the file and will be reconstitued. - * This is done by enumerating all toUnicode roundtrip mappings, performing - * each mapping (using the stage 1 and reconstituted stage 2 tables) and - * writing instead of reading the byte values. - * - * _MBCSHeader version 4.3 - * - * Change from version 4.2: - * - Optional utf8Friendly data structures, with 64-entry stage 3 block - * allocation for parts of the BMP, and an additional mbcsIndex in non-SBCS - * files which can be used instead of stages 1 & 2. - * Faster lookups for roundtrips from most commonly used characters, - * and lookups from UTF-8 byte sequences with a natural bit distribution. - * See ucnvmbcs.h for more details. - * - * Change from version 4.1: - * - Added an optional extension table structure at the end of the .cnv file. - * It is present if the upper bits of the header flags field contains a non-zero - * byte offset to it. - * Files that contain only a conversion table and no base table - * use the special outputType MBCS_OUTPUT_EXT_ONLY. - * These contain the base table name between the MBCS header and the extension - * data. - * - * Change from version 4.0: - * - Replace header.reserved with header.fromUBytesLength so that all - * fields in the data have length. - * - * Changes from version 3 (for performance improvements): - * - new bit distribution for state table entries - * - reordered action codes - * - new data structure for single-byte fromUnicode - * + stage 2 only contains indexes - * + stage 3 stores 16 bits per character with classification bits 15..8 - * - no multiplier for stage 1 entries - * - stage 2 for non-single-byte codepages contains the index and the flags in - * one 32-bit value - * - 2-byte and 4-byte fromUnicode results are stored directly as 16/32-bit integers - * - * For more details about old versions of the MBCS data structure, see - * the corresponding versions of this file. - * - * Converting stateless codepage data ---------------------------------------*** - * (or codepage data with simple states) to Unicode. - * - * Data structure and algorithm for converting from complex legacy codepages - * to Unicode. (Designed before 2000-may-22.) - * - * The basic idea is that the structure of legacy codepages can be described - * with state tables. - * When reading a byte stream, each input byte causes a state transition. - * Some transitions result in the output of a code point, some result in - * "unassigned" or "illegal" output. - * This is used here for character conversion. - * - * The data structure begins with a state table consisting of a row - * per state, with 256 entries (columns) per row for each possible input - * byte value. - * Each entry is 32 bits wide, with two formats distinguished by - * the sign bit (bit 31): - * - * One format for transitional entries (bit 31 not set) for non-final bytes, and - * one format for final entries (bit 31 set). - * Both formats contain the number of the next state in the same bit - * positions. - * State 0 is the initial state. - * - * Most of the time, the offset values of subsequent states are added - * up to a scalar value. This value will eventually be the index of - * the Unicode code point in a table that follows the state table. - * The effect is that the code points for final state table rows - * are contiguous. The code points of final state rows follow each other - * in the order of the references to those final states by previous - * states, etc. - * - * For some terminal states, the offset is itself the output Unicode - * code point (16 bits for a BMP code point or 20 bits for a supplementary - * code point (stored as code point minus 0x10000 so that 20 bits are enough). - * For others, the code point in the Unicode table is stored with either - * one or two code units: one for BMP code points, two for a pair of - * surrogates. - * All code points for a final state entry take up the same number of code - * units, regardless of whether they all actually _use_ the same number - * of code units. This is necessary for simple array access. - * - * An additional feature comes in with what in ICU is called "fallback" - * mappings: - * - * In addition to round-trippable, precise, 1:1 mappings, there are often - * mappings defined between similar, though not the same, characters. - * Typically, such mappings occur only in fromUnicode mapping tables because - * Unicode has a superset repertoire of most other codepages. However, it - * is possible to provide such mappings in the toUnicode tables, too. - * In this case, the fallback mappings are partly integrated into the - * general state tables because the structure of the encoding includes their - * byte sequences. - * For final entries in an initial state, fallback mappings are stored in - * the entry itself like with roundtrip mappings. - * For other final entries, they are stored in the code units table if - * the entry is for a pair of code units. - * For single-unit results in the code units table, there is no space to - * alternatively hold a fallback mapping; in this case, the code unit - * is stored as U+fffe (unassigned), and the fallback mapping needs to - * be looked up by the scalar offset value in a separate table. - * - * "Unassigned" state entries really mean "structurally unassigned", - * i.e., such a byte sequence will never have a mapping result. - * - * The interpretation of the bits in each entry is as follows: - * - * Bit 31 not set, not a terminal entry ("transitional"): - * 30..24 next state - * 23..0 offset delta, to be added up - * - * Bit 31 set, terminal ("final") entry: - * 30..24 next state (regardless of action code) - * 23..20 action code: - * action codes 0 and 1 result in precise-mapping Unicode code points - * 0 valid byte sequence - * 19..16 not used, 0 - * 15..0 16-bit Unicode BMP code point - * never U+fffe or U+ffff - * 1 valid byte sequence - * 19..0 20-bit Unicode supplementary code point - * never U+fffe or U+ffff - * - * action codes 2 and 3 result in fallback (unidirectional-mapping) Unicode code points - * 2 valid byte sequence (fallback) - * 19..16 not used, 0 - * 15..0 16-bit Unicode BMP code point as fallback result - * 3 valid byte sequence (fallback) - * 19..0 20-bit Unicode supplementary code point as fallback result - * - * action codes 4 and 5 may result in roundtrip/fallback/unassigned/illegal results - * depending on the code units they result in - * 4 valid byte sequence - * 19..9 not used, 0 - * 8..0 final offset delta - * pointing to one 16-bit code unit which may be - * fffe unassigned -- look for a fallback for this offset - * ffff illegal - * 5 valid byte sequence - * 19..9 not used, 0 - * 8..0 final offset delta - * pointing to two 16-bit code units - * (typically UTF-16 surrogates) - * the result depends on the first code unit as follows: - * 0000..d7ff roundtrip BMP code point (1st alone) - * d800..dbff roundtrip surrogate pair (1st, 2nd) - * dc00..dfff fallback surrogate pair (1st-400, 2nd) - * e000 roundtrip BMP code point (2nd alone) - * e001 fallback BMP code point (2nd alone) - * fffe unassigned - * ffff illegal - * (the final offset deltas are at most 255 * 2, - * times 2 because of storing code unit pairs) - * - * 6 unassigned byte sequence - * 19..16 not used, 0 - * 15..0 16-bit Unicode BMP code point U+fffe (new with version 2) - * this does not contain a final offset delta because the main - * purpose of this action code is to save scalar offset values; - * therefore, fallback values cannot be assigned to byte - * sequences that result in this action code - * 7 illegal byte sequence - * 19..16 not used, 0 - * 15..0 16-bit Unicode BMP code point U+ffff (new with version 2) - * 8 state change only - * 19..0 not used, 0 - * useful for state changes in simple stateful encodings, - * at Shift-In/Shift-Out codes - * - * - * 9..15 reserved for future use - * current implementations will only perform a state change - * and ignore bits 19..0 - * - * An encoding with contiguous ranges of unassigned byte sequences, like - * Shift-JIS and especially EUC-TW, can be stored efficiently by having - * at least two states for the trail bytes: - * One trail byte state that results in code points, and one that only - * has "unassigned" and "illegal" terminal states. - * - * Note: partly by accident, this data structure supports simple stateful - * encodings without any additional logic. - * Currently, only simple Shift-In/Shift-Out schemes are handled with - * appropriate state tables (especially EBCDIC_STATEFUL!). - * - * MBCS version 2 added: - * unassigned and illegal action codes have U+fffe and U+ffff - * instead of unused bits; this is useful for _MBCS_SINGLE_SIMPLE_GET_NEXT_BMP() - * - * Converting from Unicode to codepage bytes --------------------------------*** - * - * The conversion data structure for fromUnicode is designed for the known - * structure of Unicode. It maps from 21-bit code points (0..0x10ffff) to - * a sequence of 1..4 bytes, in addition to a flag that indicates if there is - * a roundtrip mapping. - * - * The lookup is done with a 3-stage trie, using 11/6/4 bits for stage 1/2/3 - * like in the character properties table. - * The beginning of the trie is at offsetFromUTable, the beginning of stage 3 - * with the resulting bytes is at offsetFromUBytes. - * - * Beginning with version 4, single-byte codepages have a significantly different - * trie compared to other codepages. - * In all cases, the entry in stage 1 is directly the index of the block of - * 64 entries in stage 2. - * - * Single-byte lookup: - * - * Stage 2 only contains 16-bit indexes directly to the 16-blocks in stage 3. - * Stage 3 contains one 16-bit word per result: - * Bits 15..8 indicate the kind of result: - * f roundtrip result - * c fallback result from private-use code point - * 8 fallback result from other code points - * 0 unassigned - * Bits 7..0 contain the codepage byte. A zero byte is always possible. - * - * In version 4.3, the runtime code can build an sbcsIndex for a utf8Friendly - * file. For 2-byte UTF-8 byte sequences and some 3-byte sequences the lookup - * becomes a 2-stage (single-index) trie lookup with 6 bits for stage 3. - * ASCII code points can be looked up with a linear array access into stage 3. - * See maxFastUChar and other details in ucnvmbcs.h. - * - * Multi-byte lookup: - * - * Stage 2 contains a 32-bit word for each 16-block in stage 3: - * Bits 31..16 contain flags for which stage 3 entries contain roundtrip results - * test: MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c) - * If this test is false, then a non-zero result will be interpreted as - * a fallback mapping. - * Bits 15..0 contain the index to stage 3, which must be multiplied by 16*(bytes per char) - * - * Stage 3 contains 2, 3, or 4 bytes per result. - * 2 or 4 bytes are stored as uint16_t/uint32_t in platform endianness, - * while 3 bytes are stored as bytes in big-endian order. - * Leading zero bytes are ignored, and the number of bytes is counted. - * A zero byte mapping result is possible as a roundtrip result. - * For some output types, the actual result is processed from this; - * see ucnv_MBCSFromUnicodeWithOffsets(). - * - * Note that stage 1 always contains 0x440=1088 entries (0x440==0x110000>>10), - * or (version 3 and up) for BMP-only codepages, it contains 64 entries. - * - * In version 4.3, a utf8Friendly file contains an mbcsIndex table. - * For 2-byte UTF-8 byte sequences and most 3-byte sequences the lookup - * becomes a 2-stage (single-index) trie lookup with 6 bits for stage 3. - * ASCII code points can be looked up with a linear array access into stage 3. - * See maxFastUChar, mbcsIndex and other details in ucnvmbcs.h. - * - * In version 3, stage 2 blocks may overlap by multiples of the multiplier - * for compaction. - * In version 4, stage 2 blocks (and for single-byte codepages, stage 3 blocks) - * may overlap by any number of entries. - * - * MBCS version 2 added: - * the converter checks for known output types, which allows - * adding new ones without crashing an unaware converter - */ - -/** - * Callback from ucnv_MBCSEnumToUnicode(), takes 32 mappings from - * consecutive sequences of bytes, starting from the one encoded in value, - * to Unicode code points. (Multiple mappings to reduce per-function call overhead.) - * Does not currently support m:n mappings or reverse fallbacks. - * This function will not be called for sequences of bytes with leading zeros. - * - * @param context an opaque pointer, as passed into ucnv_MBCSEnumToUnicode() - * @param value contains 1..4 bytes of the first byte sequence, right-aligned - * @param codePoints resulting Unicode code points, or negative if a byte sequence does - * not map to anything - * @return true to continue enumeration, false to stop - */ -typedef UBool U_CALLCONV -UConverterEnumToUCallback(const void *context, uint32_t value, UChar32 codePoints[32]); - -static void U_CALLCONV -ucnv_MBCSLoad(UConverterSharedData *sharedData, - UConverterLoadArgs *pArgs, - const uint8_t *raw, - UErrorCode *pErrorCode); - -static void U_CALLCONV -ucnv_MBCSUnload(UConverterSharedData *sharedData); - -static void U_CALLCONV -ucnv_MBCSOpen(UConverter *cnv, - UConverterLoadArgs *pArgs, - UErrorCode *pErrorCode); - -static UChar32 U_CALLCONV -ucnv_MBCSGetNextUChar(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode); - -static void U_CALLCONV -ucnv_MBCSGetStarters(const UConverter* cnv, - UBool starters[256], - UErrorCode *pErrorCode); - -U_CDECL_BEGIN -static const char* U_CALLCONV -ucnv_MBCSGetName(const UConverter *cnv); -U_CDECL_END - -static void U_CALLCONV -ucnv_MBCSWriteSub(UConverterFromUnicodeArgs *pArgs, - int32_t offsetIndex, - UErrorCode *pErrorCode); - -static UChar32 U_CALLCONV -ucnv_MBCSGetNextUChar(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode); - -static void U_CALLCONV -ucnv_SBCSFromUTF8(UConverterFromUnicodeArgs *pFromUArgs, - UConverterToUnicodeArgs *pToUArgs, - UErrorCode *pErrorCode); - -static void U_CALLCONV -ucnv_MBCSGetUnicodeSet(const UConverter *cnv, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode); - -static void U_CALLCONV -ucnv_DBCSFromUTF8(UConverterFromUnicodeArgs *pFromUArgs, - UConverterToUnicodeArgs *pToUArgs, - UErrorCode *pErrorCode); - -static const UConverterImpl _SBCSUTF8Impl={ - UCNV_MBCS, - - ucnv_MBCSLoad, - ucnv_MBCSUnload, - - ucnv_MBCSOpen, - NULL, - NULL, - - ucnv_MBCSToUnicodeWithOffsets, - ucnv_MBCSToUnicodeWithOffsets, - ucnv_MBCSFromUnicodeWithOffsets, - ucnv_MBCSFromUnicodeWithOffsets, - ucnv_MBCSGetNextUChar, - - ucnv_MBCSGetStarters, - ucnv_MBCSGetName, - ucnv_MBCSWriteSub, - NULL, - ucnv_MBCSGetUnicodeSet, - - NULL, - ucnv_SBCSFromUTF8 -}; - -static const UConverterImpl _DBCSUTF8Impl={ - UCNV_MBCS, - - ucnv_MBCSLoad, - ucnv_MBCSUnload, - - ucnv_MBCSOpen, - NULL, - NULL, - - ucnv_MBCSToUnicodeWithOffsets, - ucnv_MBCSToUnicodeWithOffsets, - ucnv_MBCSFromUnicodeWithOffsets, - ucnv_MBCSFromUnicodeWithOffsets, - ucnv_MBCSGetNextUChar, - - ucnv_MBCSGetStarters, - ucnv_MBCSGetName, - ucnv_MBCSWriteSub, - NULL, - ucnv_MBCSGetUnicodeSet, - - NULL, - ucnv_DBCSFromUTF8 -}; - -static const UConverterImpl _MBCSImpl={ - UCNV_MBCS, - - ucnv_MBCSLoad, - ucnv_MBCSUnload, - - ucnv_MBCSOpen, - NULL, - NULL, - - ucnv_MBCSToUnicodeWithOffsets, - ucnv_MBCSToUnicodeWithOffsets, - ucnv_MBCSFromUnicodeWithOffsets, - ucnv_MBCSFromUnicodeWithOffsets, - ucnv_MBCSGetNextUChar, - - ucnv_MBCSGetStarters, - ucnv_MBCSGetName, - ucnv_MBCSWriteSub, - NULL, - ucnv_MBCSGetUnicodeSet, - NULL, - NULL -}; - -/* Static data is in tools/makeconv/ucnvstat.c for data-based - * converters. Be sure to update it as well. - */ - -const UConverterSharedData _MBCSData={ - sizeof(UConverterSharedData), 1, - NULL, NULL, false, true, &_MBCSImpl, - 0, UCNV_MBCS_TABLE_INITIALIZER -}; - - -/* GB 18030 data ------------------------------------------------------------ */ - -/* helper macros for linear values for GB 18030 four-byte sequences */ -#define LINEAR_18030(a, b, c, d) ((((a)*10+(b))*126L+(c))*10L+(d)) - -#define LINEAR_18030_BASE LINEAR_18030(0x81, 0x30, 0x81, 0x30) - -#define LINEAR(x) LINEAR_18030(x>>24, (x>>16)&0xff, (x>>8)&0xff, x&0xff) - -/* - * Some ranges of GB 18030 where both the Unicode code points and the - * GB four-byte sequences are contiguous and are handled algorithmically by - * the special callback functions below. - * The values are start & end of Unicode & GB codes. - * - * Note that single surrogates are not mapped by GB 18030 - * as of the re-released mapping tables from 2000-nov-30. - */ -static const uint32_t -gb18030Ranges[14][4]={ - {0x10000, 0x10FFFF, LINEAR(0x90308130), LINEAR(0xE3329A35)}, - {0x9FA6, 0xD7FF, LINEAR(0x82358F33), LINEAR(0x8336C738)}, - {0x0452, 0x1E3E, LINEAR(0x8130D330), LINEAR(0x8135F436)}, - {0x1E40, 0x200F, LINEAR(0x8135F438), LINEAR(0x8136A531)}, - {0xE865, 0xF92B, LINEAR(0x8336D030), LINEAR(0x84308534)}, - {0x2643, 0x2E80, LINEAR(0x8137A839), LINEAR(0x8138FD38)}, - {0xFA2A, 0xFE2F, LINEAR(0x84309C38), LINEAR(0x84318537)}, - {0x3CE1, 0x4055, LINEAR(0x8231D438), LINEAR(0x8232AF32)}, - {0x361B, 0x3917, LINEAR(0x8230A633), LINEAR(0x8230F237)}, - {0x49B8, 0x4C76, LINEAR(0x8234A131), LINEAR(0x8234E733)}, - {0x4160, 0x4336, LINEAR(0x8232C937), LINEAR(0x8232F837)}, - {0x478E, 0x4946, LINEAR(0x8233E838), LINEAR(0x82349638)}, - {0x44D7, 0x464B, LINEAR(0x8233A339), LINEAR(0x8233C931)}, - {0xFFE6, 0xFFFF, LINEAR(0x8431A234), LINEAR(0x8431A439)} -}; - -/* bit flag for UConverter.options indicating GB 18030 special handling */ -#define _MBCS_OPTION_GB18030 0x8000 - -/* bit flag for UConverter.options indicating KEIS,JEF,JIF special handling */ -#define _MBCS_OPTION_KEIS 0x01000 -#define _MBCS_OPTION_JEF 0x02000 -#define _MBCS_OPTION_JIPS 0x04000 - -#define KEIS_SO_CHAR_1 0x0A -#define KEIS_SO_CHAR_2 0x42 -#define KEIS_SI_CHAR_1 0x0A -#define KEIS_SI_CHAR_2 0x41 - -#define JEF_SO_CHAR 0x28 -#define JEF_SI_CHAR 0x29 - -#define JIPS_SO_CHAR_1 0x1A -#define JIPS_SO_CHAR_2 0x70 -#define JIPS_SI_CHAR_1 0x1A -#define JIPS_SI_CHAR_2 0x71 - -enum SISO_Option { - SI, - SO -}; -typedef enum SISO_Option SISO_Option; - -static int32_t getSISOBytes(SISO_Option option, uint32_t cnvOption, uint8_t *value) { - int32_t SISOLength = 0; - - switch (option) { - case SI: - if ((cnvOption&_MBCS_OPTION_KEIS)!=0) { - value[0] = KEIS_SI_CHAR_1; - value[1] = KEIS_SI_CHAR_2; - SISOLength = 2; - } else if ((cnvOption&_MBCS_OPTION_JEF)!=0) { - value[0] = JEF_SI_CHAR; - SISOLength = 1; - } else if ((cnvOption&_MBCS_OPTION_JIPS)!=0) { - value[0] = JIPS_SI_CHAR_1; - value[1] = JIPS_SI_CHAR_2; - SISOLength = 2; - } else { - value[0] = UCNV_SI; - SISOLength = 1; - } - break; - case SO: - if ((cnvOption&_MBCS_OPTION_KEIS)!=0) { - value[0] = KEIS_SO_CHAR_1; - value[1] = KEIS_SO_CHAR_2; - SISOLength = 2; - } else if ((cnvOption&_MBCS_OPTION_JEF)!=0) { - value[0] = JEF_SO_CHAR; - SISOLength = 1; - } else if ((cnvOption&_MBCS_OPTION_JIPS)!=0) { - value[0] = JIPS_SO_CHAR_1; - value[1] = JIPS_SO_CHAR_2; - SISOLength = 2; - } else { - value[0] = UCNV_SO; - SISOLength = 1; - } - break; - default: - /* Should never happen. */ - break; - } - - return SISOLength; -} - -/* Miscellaneous ------------------------------------------------------------ */ - -/* similar to ucnv_MBCSGetNextUChar() but recursive */ -static UBool -enumToU(UConverterMBCSTable *mbcsTable, int8_t stateProps[], - int32_t state, uint32_t offset, - uint32_t value, - UConverterEnumToUCallback *callback, const void *context, - UErrorCode *pErrorCode) { - UChar32 codePoints[32]; - const int32_t *row; - const uint16_t *unicodeCodeUnits; - UChar32 anyCodePoints; - int32_t b, limit; - - row=mbcsTable->stateTable[state]; - unicodeCodeUnits=mbcsTable->unicodeCodeUnits; - - value<<=8; - anyCodePoints=-1; /* becomes non-negative if there is a mapping */ - - b=(stateProps[state]&0x38)<<2; - if(b==0 && stateProps[state]>=0x40) { - /* skip byte sequences with leading zeros because they are not stored in the fromUnicode table */ - codePoints[0]=U_SENTINEL; - b=1; - } - limit=((stateProps[state]&7)+1)<<5; - while(b=0) { - /* recurse to a state with non-ignorable actions */ - if(!enumToU( - mbcsTable, stateProps, nextState, - offset+MBCS_ENTRY_TRANSITION_OFFSET(entry), - value|(uint32_t)b, - callback, context, - pErrorCode)) { - return false; - } - } - codePoints[b&0x1f]=U_SENTINEL; - } else { - UChar32 c; - int32_t action; - - /* - * An if-else-if chain provides more reliable performance for - * the most common cases compared to a switch. - */ - action=MBCS_ENTRY_FINAL_ACTION(entry); - if(action==MBCS_STATE_VALID_DIRECT_16) { - /* output BMP code point */ - c=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - } else if(action==MBCS_STATE_VALID_16) { - int32_t finalOffset=offset+MBCS_ENTRY_FINAL_VALUE_16(entry); - c=unicodeCodeUnits[finalOffset]; - if(c<0xfffe) { - /* output BMP code point */ - } else { - c=U_SENTINEL; - } - } else if(action==MBCS_STATE_VALID_16_PAIR) { - int32_t finalOffset=offset+MBCS_ENTRY_FINAL_VALUE_16(entry); - c=unicodeCodeUnits[finalOffset++]; - if(c<0xd800) { - /* output BMP code point below 0xd800 */ - } else if(c<=0xdbff) { - /* output roundtrip or fallback supplementary code point */ - c=((c&0x3ff)<<10)+unicodeCodeUnits[finalOffset]+(0x10000-0xdc00); - } else if(c==0xe000) { - /* output roundtrip BMP code point above 0xd800 or fallback BMP code point */ - c=unicodeCodeUnits[finalOffset]; - } else { - c=U_SENTINEL; - } - } else if(action==MBCS_STATE_VALID_DIRECT_20) { - /* output supplementary code point */ - c=(UChar32)(MBCS_ENTRY_FINAL_VALUE(entry)+0x10000); - } else { - c=U_SENTINEL; - } - - codePoints[b&0x1f]=c; - anyCodePoints&=c; - } - if(((++b)&0x1f)==0) { - if(anyCodePoints>=0) { - if(!callback(context, value|(uint32_t)(b-0x20), codePoints)) { - return false; - } - anyCodePoints=-1; - } - } - } - return true; -} - -/* - * Only called if stateProps[state]==-1. - * A recursive call may do stateProps[state]|=0x40 if this state is the target of an - * MBCS_STATE_CHANGE_ONLY. - */ -static int8_t -getStateProp(const int32_t (*stateTable)[256], int8_t stateProps[], int state) { - const int32_t *row; - int32_t min, max, entry, nextState; - - row=stateTable[state]; - stateProps[state]=0; - - /* find first non-ignorable state */ - for(min=0;; ++min) { - entry=row[min]; - nextState=MBCS_ENTRY_STATE(entry); - if(stateProps[nextState]==-1) { - getStateProp(stateTable, stateProps, nextState); - } - if(MBCS_ENTRY_IS_TRANSITION(entry)) { - if(stateProps[nextState]>=0) { - break; - } - } else if(MBCS_ENTRY_FINAL_ACTION(entry)>5)<<3); - - /* find last non-ignorable state */ - for(max=0xff; min=0) { - break; - } - } else if(MBCS_ENTRY_FINAL_ACTION(entry)>5); - - /* recurse further and collect direct-state information */ - while(min<=max) { - entry=row[min]; - nextState=MBCS_ENTRY_STATE(entry); - if(stateProps[nextState]==-1) { - getStateProp(stateTable, stateProps, nextState); - } - if(MBCS_ENTRY_IS_FINAL(entry)) { - stateProps[nextState]|=0x40; - if(MBCS_ENTRY_FINAL_ACTION(entry)<=MBCS_STATE_FALLBACK_DIRECT_20) { - stateProps[state]|=0x40; - } - } - ++min; - } - return stateProps[state]; -} - -/* - * Internal function enumerating the toUnicode data of an MBCS converter. - * Currently only used for reconstituting data for a MBCS_OPT_NO_FROM_U - * table, but could also be used for a future ucnv_getUnicodeSet() option - * that includes reverse fallbacks (after updating this function's implementation). - * Currently only handles roundtrip mappings. - * Does not currently handle extensions. - */ -static void -ucnv_MBCSEnumToUnicode(UConverterMBCSTable *mbcsTable, - UConverterEnumToUCallback *callback, const void *context, - UErrorCode *pErrorCode) { - /* - * Properties for each state, to speed up the enumeration. - * Ignorable actions are unassigned/illegal/state-change-only: - * They do not lead to mappings. - * - * Bits 7..6: - * 1 direct/initial state (stateful converters have multiple) - * 0 non-initial state with transitions or with non-ignorable result actions - * -1 final state with only ignorable actions - * - * Bits 5..3: - * The lowest byte value with non-ignorable actions is - * value<<5 (rounded down). - * - * Bits 2..0: - * The highest byte value with non-ignorable actions is - * (value<<5)&0x1f (rounded up). - */ - int8_t stateProps[MBCS_MAX_STATE_COUNT]; - int32_t state; - - uprv_memset(stateProps, -1, sizeof(stateProps)); - - /* recurse from state 0 and set all stateProps */ - getStateProp(mbcsTable->stateTable, stateProps, 0); - - for(state=0; statecountStates; ++state) { - /*if(stateProps[state]==-1) { - printf("unused/unreachable %d\n", state); - }*/ - if(stateProps[state]>=0x40) { - /* start from each direct state */ - enumToU( - mbcsTable, stateProps, state, 0, 0, - callback, context, - pErrorCode); - } - } -} - -U_CFUNC void -ucnv_MBCSGetFilteredUnicodeSetForUnicode(const UConverterSharedData *sharedData, - const USetAdder *sa, - UConverterUnicodeSet which, - UConverterSetFilter filter, - UErrorCode *pErrorCode) { - const UConverterMBCSTable *mbcsTable; - const uint16_t *table; - - uint32_t st3; - uint16_t st1, maxStage1, st2; - - UChar32 c; - - /* enumerate the from-Unicode trie table */ - mbcsTable=&sharedData->mbcs; - table=mbcsTable->fromUnicodeTable; - if(mbcsTable->unicodeMask&UCNV_HAS_SUPPLEMENTARY) { - maxStage1=0x440; - } else { - maxStage1=0x40; - } - - c=0; /* keep track of the current code point while enumerating */ - - if(mbcsTable->outputType==MBCS_OUTPUT_1) { - const uint16_t *stage2, *stage3, *results; - uint16_t minValue; - - results=(const uint16_t *)mbcsTable->fromUnicodeBytes; - - /* - * Set a threshold variable for selecting which mappings to use. - * See ucnv_MBCSSingleFromBMPWithOffsets() and - * MBCS_SINGLE_RESULT_FROM_U() for details. - */ - if(which==UCNV_ROUNDTRIP_SET) { - /* use only roundtrips */ - minValue=0xf00; - } else /* UCNV_ROUNDTRIP_AND_FALLBACK_SET */ { - /* use all roundtrip and fallback results */ - minValue=0x800; - } - - for(st1=0; st1maxStage1) { - stage2=table+st2; - for(st2=0; st2<64; ++st2) { - if((st3=stage2[st2])!=0) { - /* read the stage 3 block */ - stage3=results+st3; - - do { - if(*stage3++>=minValue) { - sa->add(sa->set, c); - } - } while((++c&0xf)!=0); - } else { - c+=16; /* empty stage 3 block */ - } - } - } else { - c+=1024; /* empty stage 2 block */ - } - } - } else { - const uint32_t *stage2; - const uint8_t *stage3, *bytes; - uint32_t st3Multiplier; - uint32_t value; - UBool useFallback; - - bytes=mbcsTable->fromUnicodeBytes; - - useFallback=(UBool)(which==UCNV_ROUNDTRIP_AND_FALLBACK_SET); - - switch(mbcsTable->outputType) { - case MBCS_OUTPUT_3: - case MBCS_OUTPUT_4_EUC: - st3Multiplier=3; - break; - case MBCS_OUTPUT_4: - st3Multiplier=4; - break; - default: - st3Multiplier=2; - break; - } - - for(st1=0; st1(maxStage1>>1)) { - stage2=(const uint32_t *)table+st2; - for(st2=0; st2<64; ++st2) { - if((st3=stage2[st2])!=0) { - /* read the stage 3 block */ - stage3=bytes+st3Multiplier*16*(uint32_t)(uint16_t)st3; - - /* get the roundtrip flags for the stage 3 block */ - st3>>=16; - - /* - * Add code points for which the roundtrip flag is set, - * or which map to non-zero bytes if we use fallbacks. - * See ucnv_MBCSFromUnicodeWithOffsets() for details. - */ - switch(filter) { - case UCNV_SET_FILTER_NONE: - do { - if(st3&1) { - sa->add(sa->set, c); - stage3+=st3Multiplier; - } else if(useFallback) { - uint8_t b=0; - switch(st3Multiplier) { - case 4: - b|=*stage3++; - U_FALLTHROUGH; - case 3: - b|=*stage3++; - U_FALLTHROUGH; - case 2: - b|=stage3[0]|stage3[1]; - stage3+=2; - U_FALLTHROUGH; - default: - break; - } - if(b!=0) { - sa->add(sa->set, c); - } - } - st3>>=1; - } while((++c&0xf)!=0); - break; - case UCNV_SET_FILTER_DBCS_ONLY: - /* Ignore single-byte results (<0x100). */ - do { - if(((st3&1)!=0 || useFallback) && *((const uint16_t *)stage3)>=0x100) { - sa->add(sa->set, c); - } - st3>>=1; - stage3+=2; /* +=st3Multiplier */ - } while((++c&0xf)!=0); - break; - case UCNV_SET_FILTER_2022_CN: - /* Only add code points that map to CNS 11643 planes 1 & 2 for non-EXT ISO-2022-CN. */ - do { - if(((st3&1)!=0 || useFallback) && ((value=*stage3)==0x81 || value==0x82)) { - sa->add(sa->set, c); - } - st3>>=1; - stage3+=3; /* +=st3Multiplier */ - } while((++c&0xf)!=0); - break; - case UCNV_SET_FILTER_SJIS: - /* Only add code points that map to Shift-JIS codes corresponding to JIS X 0208. */ - do { - if(((st3&1)!=0 || useFallback) && (value=*((const uint16_t *)stage3))>=0x8140 && value<=0xeffc) { - sa->add(sa->set, c); - } - st3>>=1; - stage3+=2; /* +=st3Multiplier */ - } while((++c&0xf)!=0); - break; - case UCNV_SET_FILTER_GR94DBCS: - /* Only add code points that map to ISO 2022 GR 94 DBCS codes (each byte A1..FE). */ - do { - if( ((st3&1)!=0 || useFallback) && - (uint16_t)((value=*((const uint16_t *)stage3)) - 0xa1a1)<=(0xfefe - 0xa1a1) && - (uint8_t)(value-0xa1)<=(0xfe - 0xa1) - ) { - sa->add(sa->set, c); - } - st3>>=1; - stage3+=2; /* +=st3Multiplier */ - } while((++c&0xf)!=0); - break; - case UCNV_SET_FILTER_HZ: - /* Only add code points that are suitable for HZ DBCS (lead byte A1..FD). */ - do { - if( ((st3&1)!=0 || useFallback) && - (uint16_t)((value=*((const uint16_t *)stage3))-0xa1a1)<=(0xfdfe - 0xa1a1) && - (uint8_t)(value-0xa1)<=(0xfe - 0xa1) - ) { - sa->add(sa->set, c); - } - st3>>=1; - stage3+=2; /* +=st3Multiplier */ - } while((++c&0xf)!=0); - break; - default: - *pErrorCode=U_INTERNAL_PROGRAM_ERROR; - return; - } - } else { - c+=16; /* empty stage 3 block */ - } - } - } else { - c+=1024; /* empty stage 2 block */ - } - } - } - - ucnv_extGetUnicodeSet(sharedData, sa, which, filter, pErrorCode); -} - -U_CFUNC void -ucnv_MBCSGetUnicodeSetForUnicode(const UConverterSharedData *sharedData, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode) { - ucnv_MBCSGetFilteredUnicodeSetForUnicode( - sharedData, sa, which, - sharedData->mbcs.outputType==MBCS_OUTPUT_DBCS_ONLY ? - UCNV_SET_FILTER_DBCS_ONLY : - UCNV_SET_FILTER_NONE, - pErrorCode); -} - -static void U_CALLCONV -ucnv_MBCSGetUnicodeSet(const UConverter *cnv, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode) { - if(cnv->options&_MBCS_OPTION_GB18030) { - sa->addRange(sa->set, 0, 0xd7ff); - sa->addRange(sa->set, 0xe000, 0x10ffff); - } else { - ucnv_MBCSGetUnicodeSetForUnicode(cnv->sharedData, sa, which, pErrorCode); - } -} - -/* conversion extensions for input not in the main table -------------------- */ - -/* - * Hardcoded extension handling for GB 18030. - * Definition of LINEAR macros and gb18030Ranges see near the beginning of the file. - * - * In the future, conversion extensions may handle m:n mappings and delta tables, - * see https://htmlpreview.github.io/?https://github.com/unicode-org/icu-docs/blob/main/design/conversion/conversion_extensions.html - * - * If an input character cannot be mapped, then these functions set an error - * code. The framework will then call the callback function. - */ - -/* - * @return if(U_FAILURE) return the code point for cnv->fromUChar32 - * else return 0 after output has been written to the target - */ -static UChar32 -_extFromU(UConverter *cnv, const UConverterSharedData *sharedData, - UChar32 cp, - const UChar **source, const UChar *sourceLimit, - uint8_t **target, const uint8_t *targetLimit, - int32_t **offsets, int32_t sourceIndex, - UBool flush, - UErrorCode *pErrorCode) { - const int32_t *cx; - - cnv->useSubChar1=false; - - if( (cx=sharedData->mbcs.extIndexes)!=NULL && - ucnv_extInitialMatchFromU( - cnv, cx, - cp, source, sourceLimit, - (char **)target, (char *)targetLimit, - offsets, sourceIndex, - flush, - pErrorCode) - ) { - return 0; /* an extension mapping handled the input */ - } - - /* GB 18030 */ - if((cnv->options&_MBCS_OPTION_GB18030)!=0) { - const uint32_t *range; - int32_t i; - - range=gb18030Ranges[0]; - for(i=0; itoUBytes[0..length[ - * @return if(U_FAILURE) return the length (toULength, byteIndex) for the input - * else return 0 after output has been written to the target - */ -static int8_t -_extToU(UConverter *cnv, const UConverterSharedData *sharedData, - int8_t length, - const uint8_t **source, const uint8_t *sourceLimit, - UChar **target, const UChar *targetLimit, - int32_t **offsets, int32_t sourceIndex, - UBool flush, - UErrorCode *pErrorCode) { - const int32_t *cx; - - if( (cx=sharedData->mbcs.extIndexes)!=NULL && - ucnv_extInitialMatchToU( - cnv, cx, - length, (const char **)source, (const char *)sourceLimit, - target, targetLimit, - offsets, sourceIndex, - flush, - pErrorCode) - ) { - return 0; /* an extension mapping handled the input */ - } - - /* GB 18030 */ - if(length==4 && (cnv->options&_MBCS_OPTION_GB18030)!=0) { - const uint32_t *range; - uint32_t linear; - int32_t i; - - linear=LINEAR_18030(cnv->toUBytes[0], cnv->toUBytes[1], cnv->toUBytes[2], cnv->toUBytes[3]); - range=gb18030Ranges[0]; - for(i=0; iNL ------------------------------------------------------ */ - -/* - * This code modifies a standard EBCDIC<->Unicode mapping table for - * OS/390 (z/OS) Unix System Services (Open Edition). - * The difference is in the mapping of Line Feed and New Line control codes: - * Standard EBCDIC maps - * - * \x25 |0 - * \x15 |0 - * - * but OS/390 USS EBCDIC swaps the control codes for LF and NL, - * mapping - * - * \x15 |0 - * \x25 |0 - * - * This code modifies a loaded standard EBCDIC<->Unicode mapping table - * by copying it into allocated memory and swapping the LF and NL values. - * It allows to support the same EBCDIC charset in both versions without - * duplicating the entire installed table. - */ - -/* standard EBCDIC codes */ -#define EBCDIC_LF 0x25 -#define EBCDIC_NL 0x15 - -/* standard EBCDIC codes with roundtrip flag as stored in Unicode-to-single-byte tables */ -#define EBCDIC_RT_LF 0xf25 -#define EBCDIC_RT_NL 0xf15 - -/* Unicode code points */ -#define U_LF 0x0a -#define U_NL 0x85 - -static UBool -_EBCDICSwapLFNL(UConverterSharedData *sharedData, UErrorCode *pErrorCode) { - UConverterMBCSTable *mbcsTable; - - const uint16_t *table, *results; - const uint8_t *bytes; - - int32_t (*newStateTable)[256]; - uint16_t *newResults; - uint8_t *p; - char *name; - - uint32_t stage2Entry; - uint32_t size, sizeofFromUBytes; - - mbcsTable=&sharedData->mbcs; - - table=mbcsTable->fromUnicodeTable; - bytes=mbcsTable->fromUnicodeBytes; - results=(const uint16_t *)bytes; - - /* - * Check that this is an EBCDIC table with SBCS portion - - * SBCS or EBCDIC_STATEFUL with standard EBCDIC LF and NL mappings. - * - * If not, ignore the option. Options are always ignored if they do not apply. - */ - if(!( - (mbcsTable->outputType==MBCS_OUTPUT_1 || mbcsTable->outputType==MBCS_OUTPUT_2_SISO) && - mbcsTable->stateTable[0][EBCDIC_LF]==MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, U_LF) && - mbcsTable->stateTable[0][EBCDIC_NL]==MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, U_NL) - )) { - return false; - } - - if(mbcsTable->outputType==MBCS_OUTPUT_1) { - if(!( - EBCDIC_RT_LF==MBCS_SINGLE_RESULT_FROM_U(table, results, U_LF) && - EBCDIC_RT_NL==MBCS_SINGLE_RESULT_FROM_U(table, results, U_NL) - )) { - return false; - } - } else /* MBCS_OUTPUT_2_SISO */ { - stage2Entry=MBCS_STAGE_2_FROM_U(table, U_LF); - if(!( - MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, U_LF)!=0 && - EBCDIC_LF==MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, U_LF) - )) { - return false; - } - - stage2Entry=MBCS_STAGE_2_FROM_U(table, U_NL); - if(!( - MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, U_NL)!=0 && - EBCDIC_NL==MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, U_NL) - )) { - return false; - } - } - - if(mbcsTable->fromUBytesLength>0) { - /* - * We _know_ the number of bytes in the fromUnicodeBytes array - * starting with header.version 4.1. - */ - sizeofFromUBytes=mbcsTable->fromUBytesLength; - } else { - /* - * Otherwise: - * There used to be code to enumerate the fromUnicode - * trie and find the highest entry, but it was removed in ICU 3.2 - * because it was not tested and caused a low code coverage number. - * See Jitterbug 3674. - * This affects only some .cnv file formats with a header.version - * below 4.1, and only when swaplfnl is requested. - * - * ucnvmbcs.c revision 1.99 is the last one with the - * ucnv_MBCSSizeofFromUBytes() function. - */ - *pErrorCode=U_INVALID_FORMAT_ERROR; - return false; - } - - /* - * The table has an appropriate format. - * Allocate and build - * - a modified to-Unicode state table - * - a modified from-Unicode output array - * - a converter name string with the swap option appended - */ - size= - mbcsTable->countStates*1024+ - sizeofFromUBytes+ - UCNV_MAX_CONVERTER_NAME_LENGTH+20; - p=(uint8_t *)uprv_malloc(size); - if(p==NULL) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return false; - } - - /* copy and modify the to-Unicode state table */ - newStateTable=(int32_t (*)[256])p; - uprv_memcpy(newStateTable, mbcsTable->stateTable, mbcsTable->countStates*1024); - - newStateTable[0][EBCDIC_LF]=MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, U_NL); - newStateTable[0][EBCDIC_NL]=MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, U_LF); - - /* copy and modify the from-Unicode result table */ - newResults=(uint16_t *)newStateTable[mbcsTable->countStates]; - uprv_memcpy(newResults, bytes, sizeofFromUBytes); - - /* conveniently, the table access macros work on the left side of expressions */ - if(mbcsTable->outputType==MBCS_OUTPUT_1) { - MBCS_SINGLE_RESULT_FROM_U(table, newResults, U_LF)=EBCDIC_RT_NL; - MBCS_SINGLE_RESULT_FROM_U(table, newResults, U_NL)=EBCDIC_RT_LF; - } else /* MBCS_OUTPUT_2_SISO */ { - stage2Entry=MBCS_STAGE_2_FROM_U(table, U_LF); - MBCS_VALUE_2_FROM_STAGE_2(newResults, stage2Entry, U_LF)=EBCDIC_NL; - - stage2Entry=MBCS_STAGE_2_FROM_U(table, U_NL); - MBCS_VALUE_2_FROM_STAGE_2(newResults, stage2Entry, U_NL)=EBCDIC_LF; - } - - /* set the canonical converter name */ - name=(char *)newResults+sizeofFromUBytes; - uprv_strcpy(name, sharedData->staticData->name); - uprv_strcat(name, UCNV_SWAP_LFNL_OPTION_STRING); - - /* set the pointers */ - icu::umtx_lock(NULL); - if(mbcsTable->swapLFNLStateTable==NULL) { - mbcsTable->swapLFNLStateTable=newStateTable; - mbcsTable->swapLFNLFromUnicodeBytes=(uint8_t *)newResults; - mbcsTable->swapLFNLName=name; - - newStateTable=NULL; - } - icu::umtx_unlock(NULL); - - /* release the allocated memory if another thread beat us to it */ - if(newStateTable!=NULL) { - uprv_free(newStateTable); - } - return true; -} - -/* reconstitute omitted fromUnicode data ------------------------------------ */ - -/* for details, compare with genmbcs.c MBCSAddFromUnicode() and transformEUC() */ -static UBool U_CALLCONV -writeStage3Roundtrip(const void *context, uint32_t value, UChar32 codePoints[32]) { - UConverterMBCSTable *mbcsTable=(UConverterMBCSTable *)context; - const uint16_t *table; - uint32_t *stage2; - uint8_t *bytes, *p; - UChar32 c; - int32_t i, st3; - - table=mbcsTable->fromUnicodeTable; - bytes=(uint8_t *)mbcsTable->fromUnicodeBytes; - - /* for EUC outputTypes, modify the value like genmbcs.c's transformEUC() */ - switch(mbcsTable->outputType) { - case MBCS_OUTPUT_3_EUC: - if(value<=0xffff) { - /* short sequences are stored directly */ - /* code set 0 or 1 */ - } else if(value<=0x8effff) { - /* code set 2 */ - value&=0x7fff; - } else /* first byte is 0x8f */ { - /* code set 3 */ - value&=0xff7f; - } - break; - case MBCS_OUTPUT_4_EUC: - if(value<=0xffffff) { - /* short sequences are stored directly */ - /* code set 0 or 1 */ - } else if(value<=0x8effffff) { - /* code set 2 */ - value&=0x7fffff; - } else /* first byte is 0x8f */ { - /* code set 3 */ - value&=0xff7fff; - } - break; - default: - break; - } - - for(i=0; i<=0x1f; ++value, ++i) { - c=codePoints[i]; - if(c<0) { - continue; - } - - /* locate the stage 2 & 3 data */ - stage2=((uint32_t *)table)+table[c>>10]+((c>>4)&0x3f); - p=bytes; - st3=(int32_t)(uint16_t)*stage2*16+(c&0xf); - - /* write the codepage bytes into stage 3 */ - switch(mbcsTable->outputType) { - case MBCS_OUTPUT_3: - case MBCS_OUTPUT_4_EUC: - p+=st3*3; - p[0]=(uint8_t)(value>>16); - p[1]=(uint8_t)(value>>8); - p[2]=(uint8_t)value; - break; - case MBCS_OUTPUT_4: - ((uint32_t *)p)[st3]=value; - break; - default: - /* 2 bytes per character */ - ((uint16_t *)p)[st3]=(uint16_t)value; - break; - } - - /* set the roundtrip flag */ - *stage2|=(1UL<<(16+(c&0xf))); - } - return true; - } - -static void -reconstituteData(UConverterMBCSTable *mbcsTable, - uint32_t stage1Length, uint32_t stage2Length, - uint32_t fullStage2Length, /* lengths are numbers of units, not bytes */ - UErrorCode *pErrorCode) { - uint16_t *stage1; - uint32_t *stage2; - uint32_t dataLength=stage1Length*2+fullStage2Length*4+mbcsTable->fromUBytesLength; - mbcsTable->reconstitutedData=(uint8_t *)uprv_malloc(dataLength); - if(mbcsTable->reconstitutedData==NULL) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_memset(mbcsTable->reconstitutedData, 0, dataLength); - - /* copy existing data and reroute the pointers */ - stage1=(uint16_t *)mbcsTable->reconstitutedData; - uprv_memcpy(stage1, mbcsTable->fromUnicodeTable, stage1Length*2); - - stage2=(uint32_t *)(stage1+stage1Length); - uprv_memcpy(stage2+(fullStage2Length-stage2Length), - mbcsTable->fromUnicodeTable+stage1Length, - stage2Length*4); - - mbcsTable->fromUnicodeTable=stage1; - mbcsTable->fromUnicodeBytes=(uint8_t *)(stage2+fullStage2Length); - - /* indexes into stage 2 count from the bottom of the fromUnicodeTable */ - stage2=(uint32_t *)stage1; - - /* reconstitute the initial part of stage 2 from the mbcsIndex */ - { - int32_t stageUTF8Length=((int32_t)mbcsTable->maxFastUChar+1)>>6; - int32_t stageUTF8Index=0; - int32_t st1, st2, st3, i; - - for(st1=0; stageUTF8IndexmbcsIndex[stageUTF8Index++]; - if(st3!=0) { - /* an stage 2 entry's index is per stage 3 16-block, not per stage 3 entry */ - st3>>=4; - /* - * 4 stage 2 entries point to 4 consecutive stage 3 16-blocks which are - * allocated together as a single 64-block for access from the mbcsIndex - */ - stage2[st2++]=st3++; - stage2[st2++]=st3++; - stage2[st2++]=st3++; - stage2[st2++]=st3; - } else { - /* no stage 3 block, skip */ - st2+=4; - } - } - } else { - /* no stage 2 block, skip */ - stageUTF8Index+=16; - } - } - } - - /* reconstitute fromUnicodeBytes with roundtrips from toUnicode data */ - ucnv_MBCSEnumToUnicode(mbcsTable, writeStage3Roundtrip, mbcsTable, pErrorCode); -} - -/* MBCS setup functions ----------------------------------------------------- */ - -static void U_CALLCONV -ucnv_MBCSLoad(UConverterSharedData *sharedData, - UConverterLoadArgs *pArgs, - const uint8_t *raw, - UErrorCode *pErrorCode) { - UDataInfo info; - UConverterMBCSTable *mbcsTable=&sharedData->mbcs; - _MBCSHeader *header=(_MBCSHeader *)raw; - uint32_t offset; - uint32_t headerLength; - UBool noFromU=false; - - if(header->version[0]==4) { - headerLength=MBCS_HEADER_V4_LENGTH; - } else if(header->version[0]==5 && header->version[1]>=3 && - (header->options&MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK)==0) { - headerLength=header->options&MBCS_OPT_LENGTH_MASK; - noFromU=(UBool)((header->options&MBCS_OPT_NO_FROM_U)!=0); - } else { - *pErrorCode=U_INVALID_TABLE_FORMAT; - return; - } - - mbcsTable->outputType=(uint8_t)header->flags; - if(noFromU && mbcsTable->outputType==MBCS_OUTPUT_1) { - *pErrorCode=U_INVALID_TABLE_FORMAT; - return; - } - - /* extension data, header version 4.2 and higher */ - offset=header->flags>>8; - if(offset!=0) { - mbcsTable->extIndexes=(const int32_t *)(raw+offset); - } - - if(mbcsTable->outputType==MBCS_OUTPUT_EXT_ONLY) { - UConverterLoadArgs args=UCNV_LOAD_ARGS_INITIALIZER; - UConverterSharedData *baseSharedData; - const int32_t *extIndexes; - const char *baseName; - - /* extension-only file, load the base table and set values appropriately */ - if((extIndexes=mbcsTable->extIndexes)==NULL) { - /* extension-only file without extension */ - *pErrorCode=U_INVALID_TABLE_FORMAT; - return; - } - - if(pArgs->nestedLoads!=1) { - /* an extension table must not be loaded as a base table */ - *pErrorCode=U_INVALID_TABLE_FILE; - return; - } - - /* load the base table */ - baseName=(const char *)header+headerLength*4; - if(0==uprv_strcmp(baseName, sharedData->staticData->name)) { - /* forbid loading this same extension-only file */ - *pErrorCode=U_INVALID_TABLE_FORMAT; - return; - } - - /* TODO parse package name out of the prefix of the base name in the extension .cnv file? */ - args.size=sizeof(UConverterLoadArgs); - args.nestedLoads=2; - args.onlyTestIsLoadable=pArgs->onlyTestIsLoadable; - args.reserved=pArgs->reserved; - args.options=pArgs->options; - args.pkg=pArgs->pkg; - args.name=baseName; - baseSharedData=ucnv_load(&args, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return; - } - if( baseSharedData->staticData->conversionType!=UCNV_MBCS || - baseSharedData->mbcs.baseSharedData!=NULL - ) { - ucnv_unload(baseSharedData); - *pErrorCode=U_INVALID_TABLE_FORMAT; - return; - } - if(pArgs->onlyTestIsLoadable) { - /* - * Exit as soon as we know that we can load the converter - * and the format is valid and supported. - * The worst that can happen in the following code is a memory - * allocation error. - */ - ucnv_unload(baseSharedData); - return; - } - - /* copy the base table data */ - uprv_memcpy(mbcsTable, &baseSharedData->mbcs, sizeof(UConverterMBCSTable)); - - /* overwrite values with relevant ones for the extension converter */ - mbcsTable->baseSharedData=baseSharedData; - mbcsTable->extIndexes=extIndexes; - - /* - * It would be possible to share the swapLFNL data with a base converter, - * but the generated name would have to be different, and the memory - * would have to be free'd only once. - * It is easier to just create the data for the extension converter - * separately when it is requested. - */ - mbcsTable->swapLFNLStateTable=NULL; - mbcsTable->swapLFNLFromUnicodeBytes=NULL; - mbcsTable->swapLFNLName=NULL; - - /* - * The reconstitutedData must be deleted only when the base converter - * is unloaded. - */ - mbcsTable->reconstitutedData=NULL; - - /* - * Set a special, runtime-only outputType if the extension converter - * is a DBCS version of a base converter that also maps single bytes. - */ - if( sharedData->staticData->conversionType==UCNV_DBCS || - (sharedData->staticData->conversionType==UCNV_MBCS && - sharedData->staticData->minBytesPerChar>=2) - ) { - if(baseSharedData->mbcs.outputType==MBCS_OUTPUT_2_SISO) { - /* the base converter is SI/SO-stateful */ - int32_t entry; - - /* get the dbcs state from the state table entry for SO=0x0e */ - entry=mbcsTable->stateTable[0][0xe]; - if( MBCS_ENTRY_IS_FINAL(entry) && - MBCS_ENTRY_FINAL_ACTION(entry)==MBCS_STATE_CHANGE_ONLY && - MBCS_ENTRY_FINAL_STATE(entry)!=0 - ) { - mbcsTable->dbcsOnlyState=(uint8_t)MBCS_ENTRY_FINAL_STATE(entry); - - mbcsTable->outputType=MBCS_OUTPUT_DBCS_ONLY; - } - } else if( - baseSharedData->staticData->conversionType==UCNV_MBCS && - baseSharedData->staticData->minBytesPerChar==1 && - baseSharedData->staticData->maxBytesPerChar==2 && - mbcsTable->countStates<=127 - ) { - /* non-stateful base converter, need to modify the state table */ - int32_t (*newStateTable)[256]; - int32_t *state; - int32_t i, count; - - /* allocate a new state table and copy the base state table contents */ - count=mbcsTable->countStates; - newStateTable=(int32_t (*)[256])uprv_malloc((count+1)*1024); - if(newStateTable==NULL) { - ucnv_unload(baseSharedData); - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - - uprv_memcpy(newStateTable, mbcsTable->stateTable, count*1024); - - /* change all final single-byte entries to go to a new all-illegal state */ - state=newStateTable[0]; - for(i=0; i<256; ++i) { - if(MBCS_ENTRY_IS_FINAL(state[i])) { - state[i]=MBCS_ENTRY_TRANSITION(count, 0); - } - } - - /* build the new all-illegal state */ - state=newStateTable[count]; - for(i=0; i<256; ++i) { - state[i]=MBCS_ENTRY_FINAL(0, MBCS_STATE_ILLEGAL, 0); - } - mbcsTable->stateTable=(const int32_t (*)[256])newStateTable; - mbcsTable->countStates=(uint8_t)(count+1); - mbcsTable->stateTableOwned=true; - - mbcsTable->outputType=MBCS_OUTPUT_DBCS_ONLY; - } - } - - /* - * unlike below for files with base tables, do not get the unicodeMask - * from the sharedData; instead, use the base table's unicodeMask, - * which we copied in the memcpy above; - * this is necessary because the static data unicodeMask, especially - * the UCNV_HAS_SUPPLEMENTARY flag, is part of the base table data - */ - } else { - /* conversion file with a base table; an additional extension table is optional */ - /* make sure that the output type is known */ - switch(mbcsTable->outputType) { - case MBCS_OUTPUT_1: - case MBCS_OUTPUT_2: - case MBCS_OUTPUT_3: - case MBCS_OUTPUT_4: - case MBCS_OUTPUT_3_EUC: - case MBCS_OUTPUT_4_EUC: - case MBCS_OUTPUT_2_SISO: - /* OK */ - break; - default: - *pErrorCode=U_INVALID_TABLE_FORMAT; - return; - } - if(pArgs->onlyTestIsLoadable) { - /* - * Exit as soon as we know that we can load the converter - * and the format is valid and supported. - * The worst that can happen in the following code is a memory - * allocation error. - */ - return; - } - - mbcsTable->countStates=(uint8_t)header->countStates; - mbcsTable->countToUFallbacks=header->countToUFallbacks; - mbcsTable->stateTable=(const int32_t (*)[256])(raw+headerLength*4); - mbcsTable->toUFallbacks=(const _MBCSToUFallback *)(mbcsTable->stateTable+header->countStates); - mbcsTable->unicodeCodeUnits=(const uint16_t *)(raw+header->offsetToUCodeUnits); - - mbcsTable->fromUnicodeTable=(const uint16_t *)(raw+header->offsetFromUTable); - mbcsTable->fromUnicodeBytes=(const uint8_t *)(raw+header->offsetFromUBytes); - mbcsTable->fromUBytesLength=header->fromUBytesLength; - - /* - * converter versions 6.1 and up contain a unicodeMask that is - * used here to select the most efficient function implementations - */ - info.size=sizeof(UDataInfo); - udata_getInfo((UDataMemory *)sharedData->dataMemory, &info); - if(info.formatVersion[0]>6 || (info.formatVersion[0]==6 && info.formatVersion[1]>=1)) { - /* mask off possible future extensions to be safe */ - mbcsTable->unicodeMask=(uint8_t)(sharedData->staticData->unicodeMask&3); - } else { - /* for older versions, assume worst case: contains anything possible (prevent over-optimizations) */ - mbcsTable->unicodeMask=UCNV_HAS_SUPPLEMENTARY|UCNV_HAS_SURROGATES; - } - - /* - * _MBCSHeader.version 4.3 adds utf8Friendly data structures. - * Check for the header version, SBCS vs. MBCS, and for whether the - * data structures are optimized for code points as high as what the - * runtime code is designed for. - * The implementation does not handle mapping tables with entries for - * unpaired surrogates. - */ - if( header->version[1]>=3 && - (mbcsTable->unicodeMask&UCNV_HAS_SURROGATES)==0 && - (mbcsTable->countStates==1 ? - (header->version[2]>=(SBCS_FAST_MAX>>8)) : - (header->version[2]>=(MBCS_FAST_MAX>>8)) - ) - ) { - mbcsTable->utf8Friendly=true; - - if(mbcsTable->countStates==1) { - /* - * SBCS: Stage 3 is allocated in 64-entry blocks for U+0000..SBCS_FAST_MAX or higher. - * Build a table with indexes to each block, to be used instead of - * the regular stage 1/2 table. - */ - int32_t i; - for(i=0; i<(SBCS_FAST_LIMIT>>6); ++i) { - mbcsTable->sbcsIndex[i]=mbcsTable->fromUnicodeTable[mbcsTable->fromUnicodeTable[i>>4]+((i<<2)&0x3c)]; - } - /* set SBCS_FAST_MAX to reflect the reach of sbcsIndex[] even if header->version[2]>(SBCS_FAST_MAX>>8) */ - mbcsTable->maxFastUChar=SBCS_FAST_MAX; - } else { - /* - * MBCS: Stage 3 is allocated in 64-entry blocks for U+0000..MBCS_FAST_MAX or higher. - * The .cnv file is prebuilt with an additional stage table with indexes - * to each block. - */ - mbcsTable->mbcsIndex=(const uint16_t *) - (mbcsTable->fromUnicodeBytes+ - (noFromU ? 0 : mbcsTable->fromUBytesLength)); - mbcsTable->maxFastUChar=(((UChar)header->version[2])<<8)|0xff; - } - } - - /* calculate a bit set of 4 ASCII characters per bit that round-trip to ASCII bytes */ - { - uint32_t asciiRoundtrips=0xffffffff; - int32_t i; - - for(i=0; i<0x80; ++i) { - if(mbcsTable->stateTable[0][i]!=MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, i)) { - asciiRoundtrips&=~((uint32_t)1<<(i>>2)); - } - } - mbcsTable->asciiRoundtrips=asciiRoundtrips; - } - - if(noFromU) { - uint32_t stage1Length= - mbcsTable->unicodeMask&UCNV_HAS_SUPPLEMENTARY ? - 0x440 : 0x40; - uint32_t stage2Length= - (header->offsetFromUBytes-header->offsetFromUTable)/4- - stage1Length/2; - reconstituteData(mbcsTable, stage1Length, stage2Length, header->fullStage2Length, pErrorCode); - } - } - - /* Set the impl pointer here so that it is set for both extension-only and base tables. */ - if(mbcsTable->utf8Friendly) { - if(mbcsTable->countStates==1) { - sharedData->impl=&_SBCSUTF8Impl; - } else { - if(mbcsTable->outputType==MBCS_OUTPUT_2) { - sharedData->impl=&_DBCSUTF8Impl; - } - } - } - - if(mbcsTable->outputType==MBCS_OUTPUT_DBCS_ONLY || mbcsTable->outputType==MBCS_OUTPUT_2_SISO) { - /* - * MBCS_OUTPUT_DBCS_ONLY: No SBCS mappings, therefore ASCII does not roundtrip. - * MBCS_OUTPUT_2_SISO: Bypass the ASCII fastpath to handle prevLength correctly. - */ - mbcsTable->asciiRoundtrips=0; - } -} - -static void U_CALLCONV -ucnv_MBCSUnload(UConverterSharedData *sharedData) { - UConverterMBCSTable *mbcsTable=&sharedData->mbcs; - - if(mbcsTable->swapLFNLStateTable!=NULL) { - uprv_free(mbcsTable->swapLFNLStateTable); - } - if(mbcsTable->stateTableOwned) { - uprv_free((void *)mbcsTable->stateTable); - } - if(mbcsTable->baseSharedData!=NULL) { - ucnv_unload(mbcsTable->baseSharedData); - } - if(mbcsTable->reconstitutedData!=NULL) { - uprv_free(mbcsTable->reconstitutedData); - } -} - -static void U_CALLCONV -ucnv_MBCSOpen(UConverter *cnv, - UConverterLoadArgs *pArgs, - UErrorCode *pErrorCode) { - UConverterMBCSTable *mbcsTable; - const int32_t *extIndexes; - uint8_t outputType; - int8_t maxBytesPerUChar; - - if(pArgs->onlyTestIsLoadable) { - return; - } - - mbcsTable=&cnv->sharedData->mbcs; - outputType=mbcsTable->outputType; - - if(outputType==MBCS_OUTPUT_DBCS_ONLY) { - /* the swaplfnl option does not apply, remove it */ - cnv->options=pArgs->options&=~UCNV_OPTION_SWAP_LFNL; - } - - if((pArgs->options&UCNV_OPTION_SWAP_LFNL)!=0) { - /* do this because double-checked locking is broken */ - UBool isCached; - - icu::umtx_lock(NULL); - isCached=mbcsTable->swapLFNLStateTable!=NULL; - icu::umtx_unlock(NULL); - - if(!isCached) { - if(!_EBCDICSwapLFNL(cnv->sharedData, pErrorCode)) { - if(U_FAILURE(*pErrorCode)) { - return; /* something went wrong */ - } - - /* the option does not apply, remove it */ - cnv->options=pArgs->options&=~UCNV_OPTION_SWAP_LFNL; - } - } - } - - if(uprv_strstr(pArgs->name, "18030")!=NULL) { - if(uprv_strstr(pArgs->name, "gb18030")!=NULL || uprv_strstr(pArgs->name, "GB18030")!=NULL) { - /* set a flag for GB 18030 mode, which changes the callback behavior */ - cnv->options|=_MBCS_OPTION_GB18030; - } - } else if((uprv_strstr(pArgs->name, "KEIS")!=NULL) || (uprv_strstr(pArgs->name, "keis")!=NULL)) { - /* set a flag for KEIS converter, which changes the SI/SO character sequence */ - cnv->options|=_MBCS_OPTION_KEIS; - } else if((uprv_strstr(pArgs->name, "JEF")!=NULL) || (uprv_strstr(pArgs->name, "jef")!=NULL)) { - /* set a flag for JEF converter, which changes the SI/SO character sequence */ - cnv->options|=_MBCS_OPTION_JEF; - } else if((uprv_strstr(pArgs->name, "JIPS")!=NULL) || (uprv_strstr(pArgs->name, "jips")!=NULL)) { - /* set a flag for JIPS converter, which changes the SI/SO character sequence */ - cnv->options|=_MBCS_OPTION_JIPS; - } - - /* fix maxBytesPerUChar depending on outputType and options etc. */ - if(outputType==MBCS_OUTPUT_2_SISO) { - cnv->maxBytesPerUChar=3; /* SO+DBCS */ - } - - extIndexes=mbcsTable->extIndexes; - if(extIndexes!=NULL) { - maxBytesPerUChar=(int8_t)UCNV_GET_MAX_BYTES_PER_UCHAR(extIndexes); - if(outputType==MBCS_OUTPUT_2_SISO) { - ++maxBytesPerUChar; /* SO + multiple DBCS */ - } - - if(maxBytesPerUChar>cnv->maxBytesPerUChar) { - cnv->maxBytesPerUChar=maxBytesPerUChar; - } - } - -#if 0 - /* - * documentation of UConverter fields used for status - * all of these fields are (re)set to 0 by ucnv_bld.c and ucnv_reset() - */ - - /* toUnicode */ - cnv->toUnicodeStatus=0; /* offset */ - cnv->mode=0; /* state */ - cnv->toULength=0; /* byteIndex */ - - /* fromUnicode */ - cnv->fromUChar32=0; - cnv->fromUnicodeStatus=1; /* prevLength */ -#endif -} - -U_CDECL_BEGIN - -static const char* U_CALLCONV -ucnv_MBCSGetName(const UConverter *cnv) { - if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0 && cnv->sharedData->mbcs.swapLFNLName!=NULL) { - return cnv->sharedData->mbcs.swapLFNLName; - } else { - return cnv->sharedData->staticData->name; - } -} -U_CDECL_END - - -/* MBCS-to-Unicode conversion functions ------------------------------------- */ - -static UChar32 U_CALLCONV -ucnv_MBCSGetFallback(UConverterMBCSTable *mbcsTable, uint32_t offset) { - const _MBCSToUFallback *toUFallbacks; - uint32_t i, start, limit; - - limit=mbcsTable->countToUFallbacks; - if(limit>0) { - /* do a binary search for the fallback mapping */ - toUFallbacks=mbcsTable->toUFallbacks; - start=0; - while(startconverter; - source=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - target=pArgs->target; - targetLimit=pArgs->targetLimit; - offsets=pArgs->offsets; - - if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { - stateTable=(const int32_t (*)[256])cnv->sharedData->mbcs.swapLFNLStateTable; - } else { - stateTable=cnv->sharedData->mbcs.stateTable; - } - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex=0; - - /* conversion loop */ - while(source=targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - - entry=stateTable[0][*source++]; - /* MBCS_ENTRY_IS_FINAL(entry) */ - - /* test the most common case first */ - if(MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(entry)) { - /* output BMP code point */ - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - - /* normal end of action codes: prepare for a new character */ - ++sourceIndex; - continue; - } - - /* - * An if-else-if chain provides more reliable performance for - * the most common cases compared to a switch. - */ - action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); - if(action==MBCS_STATE_VALID_DIRECT_20 || - (action==MBCS_STATE_FALLBACK_DIRECT_20 && UCNV_TO_U_USE_FALLBACK(cnv)) - ) { - entry=MBCS_ENTRY_FINAL_VALUE(entry); - /* output surrogate pair */ - *target++=(UChar)(0xd800|(UChar)(entry>>10)); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - c=(UChar)(0xdc00|(UChar)(entry&0x3ff)); - if(targetUCharErrorBuffer[0]=c; - cnv->UCharErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - - ++sourceIndex; - continue; - } else if(action==MBCS_STATE_FALLBACK_DIRECT_16) { - if(UCNV_TO_U_USE_FALLBACK(cnv)) { - /* output BMP code point */ - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - - ++sourceIndex; - continue; - } - } else if(action==MBCS_STATE_UNASSIGNED) { - /* just fall through */ - } else if(action==MBCS_STATE_ILLEGAL) { - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } else { - /* reserved, must never occur */ - ++sourceIndex; - continue; - } - - if(U_FAILURE(*pErrorCode)) { - /* callback(illegal) */ - break; - } else /* unassigned sequences indicated with byteIndex>0 */ { - /* try an extension mapping */ - pArgs->source=(const char *)source; - cnv->toUBytes[0]=*(source-1); - cnv->toULength=_extToU(cnv, cnv->sharedData, - 1, &source, sourceLimit, - &target, targetLimit, - &offsets, sourceIndex, - pArgs->flush, - pErrorCode); - sourceIndex+=1+(int32_t)(source-(const uint8_t *)pArgs->source); - - if(U_FAILURE(*pErrorCode)) { - /* not mappable or buffer overflow */ - break; - } - } - } - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - pArgs->offsets=offsets; -} - -/* - * This version of ucnv_MBCSSingleToUnicodeWithOffsets() is optimized for single-byte, single-state codepages - * that only map to and from the BMP. - * In addition to single-byte optimizations, the offset calculations - * become much easier. - */ -static void -ucnv_MBCSSingleToBMPWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const uint8_t *source, *sourceLimit, *lastSource; - UChar *target; - int32_t targetCapacity, length; - int32_t *offsets; - - const int32_t (*stateTable)[256]; - - int32_t sourceIndex; - - int32_t entry; - uint8_t action; - - /* set up the local pointers */ - cnv=pArgs->converter; - source=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - target=pArgs->target; - targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); - offsets=pArgs->offsets; - - if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { - stateTable=(const int32_t (*)[256])cnv->sharedData->mbcs.swapLFNLStateTable; - } else { - stateTable=cnv->sharedData->mbcs.stateTable; - } - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex=0; - lastSource=source; - - /* - * since the conversion here is 1:1 UChar:uint8_t, we need only one counter - * for the minimum of the sourceLength and targetCapacity - */ - length=(int32_t)(sourceLimit-source); - if(length=16) { - int32_t count, loops, oredEntries; - - loops=count=targetCapacity>>4; - do { - oredEntries=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - oredEntries|=entry=stateTable[0][*source++]; - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - - /* were all 16 entries really valid? */ - if(!MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(oredEntries)) { - /* no, return to the first of these 16 */ - source-=16; - target-=16; - break; - } - } while(--count>0); - count=loops-count; - targetCapacity-=16*count; - - if(offsets!=NULL) { - lastSource+=16*count; - while(count>0) { - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - --count; - } - } - } -#endif - - /* conversion loop */ - while(targetCapacity > 0 && source < sourceLimit) { - entry=stateTable[0][*source++]; - /* MBCS_ENTRY_IS_FINAL(entry) */ - - /* test the most common case first */ - if(MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(entry)) { - /* output BMP code point */ - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - --targetCapacity; - continue; - } - - /* - * An if-else-if chain provides more reliable performance for - * the most common cases compared to a switch. - */ - action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); - if(action==MBCS_STATE_FALLBACK_DIRECT_16) { - if(UCNV_TO_U_USE_FALLBACK(cnv)) { - /* output BMP code point */ - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - --targetCapacity; - continue; - } - } else if(action==MBCS_STATE_UNASSIGNED) { - /* just fall through */ - } else if(action==MBCS_STATE_ILLEGAL) { - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } else { - /* reserved, must never occur */ - continue; - } - - /* set offsets since the start or the last extension */ - if(offsets!=NULL) { - int32_t count=(int32_t)(source-lastSource); - - /* predecrement: do not set the offset for the callback-causing character */ - while(--count>0) { - *offsets++=sourceIndex++; - } - /* offset and sourceIndex are now set for the current character */ - } - - if(U_FAILURE(*pErrorCode)) { - /* callback(illegal) */ - break; - } else /* unassigned sequences indicated with byteIndex>0 */ { - /* try an extension mapping */ - lastSource=source; - cnv->toUBytes[0]=*(source-1); - cnv->toULength=_extToU(cnv, cnv->sharedData, - 1, &source, sourceLimit, - &target, pArgs->targetLimit, - &offsets, sourceIndex, - pArgs->flush, - pErrorCode); - sourceIndex+=1+(int32_t)(source-lastSource); - - if(U_FAILURE(*pErrorCode)) { - /* not mappable or buffer overflow */ - break; - } - - /* recalculate the targetCapacity after an extension mapping */ - targetCapacity=(int32_t)(pArgs->targetLimit-target); - length=(int32_t)(sourceLimit-source); - if(length=pArgs->targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - - /* set offsets since the start or the last callback */ - if(offsets!=NULL) { - size_t count=source-lastSource; - while(count>0) { - *offsets++=sourceIndex++; - --count; - } - } - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - pArgs->offsets=offsets; -} - -static UBool -hasValidTrailBytes(const int32_t (*stateTable)[256], uint8_t state) { - const int32_t *row=stateTable[state]; - int32_t b, entry; - /* First test for final entries in this state for some commonly valid byte values. */ - entry=row[0xa1]; - if( !MBCS_ENTRY_IS_TRANSITION(entry) && - MBCS_ENTRY_FINAL_ACTION(entry)!=MBCS_STATE_ILLEGAL - ) { - return true; - } - entry=row[0x41]; - if( !MBCS_ENTRY_IS_TRANSITION(entry) && - MBCS_ENTRY_FINAL_ACTION(entry)!=MBCS_STATE_ILLEGAL - ) { - return true; - } - /* Then test for final entries in this state. */ - for(b=0; b<=0xff; ++b) { - entry=row[b]; - if( !MBCS_ENTRY_IS_TRANSITION(entry) && - MBCS_ENTRY_FINAL_ACTION(entry)!=MBCS_STATE_ILLEGAL - ) { - return true; - } - } - /* Then recurse for transition entries. */ - for(b=0; b<=0xff; ++b) { - entry=row[b]; - if( MBCS_ENTRY_IS_TRANSITION(entry) && - hasValidTrailBytes(stateTable, (uint8_t)MBCS_ENTRY_TRANSITION_STATE(entry)) - ) { - return true; - } - } - return false; -} - -/* - * Is byte b a single/lead byte in this state? - * Recurse for transition states, because here we don't want to say that - * b is a lead byte if all byte sequences that start with b are illegal. - */ -static UBool -isSingleOrLead(const int32_t (*stateTable)[256], uint8_t state, UBool isDBCSOnly, uint8_t b) { - const int32_t *row=stateTable[state]; - int32_t entry=row[b]; - if(MBCS_ENTRY_IS_TRANSITION(entry)) { /* lead byte */ - return hasValidTrailBytes(stateTable, (uint8_t)MBCS_ENTRY_TRANSITION_STATE(entry)); - } else { - uint8_t action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); - if(action==MBCS_STATE_CHANGE_ONLY && isDBCSOnly) { - return false; /* SI/SO are illegal for DBCS-only conversion */ - } else { - return action!=MBCS_STATE_ILLEGAL; - } - } -} - -U_CFUNC void -ucnv_MBCSToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const uint8_t *source, *sourceLimit; - UChar *target; - const UChar *targetLimit; - int32_t *offsets; - - const int32_t (*stateTable)[256]; - const uint16_t *unicodeCodeUnits; - - uint32_t offset; - uint8_t state; - int8_t byteIndex; - uint8_t *bytes; - - int32_t sourceIndex, nextSourceIndex; - - int32_t entry; - UChar c; - uint8_t action; - - /* use optimized function if possible */ - cnv=pArgs->converter; - - if(cnv->preToULength>0) { - /* - * pass sourceIndex=-1 because we continue from an earlier buffer - * in the future, this may change with continuous offsets - */ - ucnv_extContinueMatchToU(cnv, pArgs, -1, pErrorCode); - - if(U_FAILURE(*pErrorCode) || cnv->preToULength<0) { - return; - } - } - - if(cnv->sharedData->mbcs.countStates==1) { - if(!(cnv->sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY)) { - ucnv_MBCSSingleToBMPWithOffsets(pArgs, pErrorCode); - } else { - ucnv_MBCSSingleToUnicodeWithOffsets(pArgs, pErrorCode); - } - return; - } - - /* set up the local pointers */ - source=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - target=pArgs->target; - targetLimit=pArgs->targetLimit; - offsets=pArgs->offsets; - - if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { - stateTable=(const int32_t (*)[256])cnv->sharedData->mbcs.swapLFNLStateTable; - } else { - stateTable=cnv->sharedData->mbcs.stateTable; - } - unicodeCodeUnits=cnv->sharedData->mbcs.unicodeCodeUnits; - - /* get the converter state from UConverter */ - offset=cnv->toUnicodeStatus; - byteIndex=cnv->toULength; - bytes=cnv->toUBytes; - - /* - * if we are in the SBCS state for a DBCS-only converter, - * then load the DBCS state from the MBCS data - * (dbcsOnlyState==0 if it is not a DBCS-only converter) - */ - if((state=(uint8_t)(cnv->mode))==0) { - state=cnv->sharedData->mbcs.dbcsOnlyState; - } - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex=byteIndex==0 ? 0 : -1; - nextSourceIndex=0; - - /* conversion loop */ - while(source=targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - - if(byteIndex==0) { - /* optimized loop for 1/2-byte input and BMP output */ - if(offsets==NULL) { - do { - entry=stateTable[state][*source]; - if(MBCS_ENTRY_IS_TRANSITION(entry)) { - state=(uint8_t)MBCS_ENTRY_TRANSITION_STATE(entry); - offset=MBCS_ENTRY_TRANSITION_OFFSET(entry); - - ++source; - if( source=sourceLimit) { - break; - } - if(target>=targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - - ++nextSourceIndex; - bytes[byteIndex++]=*source++; - } else /* byteIndex>0 */ { - ++nextSourceIndex; - entry=stateTable[state][bytes[byteIndex++]=*source++]; - } - - if(MBCS_ENTRY_IS_TRANSITION(entry)) { - state=(uint8_t)MBCS_ENTRY_TRANSITION_STATE(entry); - offset+=MBCS_ENTRY_TRANSITION_OFFSET(entry); - continue; - } - - /* save the previous state for proper extension mapping with SI/SO-stateful converters */ - cnv->mode=state; - - /* set the next state early so that we can reuse the entry variable */ - state=(uint8_t)MBCS_ENTRY_FINAL_STATE(entry); /* typically 0 */ - - /* - * An if-else-if chain provides more reliable performance for - * the most common cases compared to a switch. - */ - action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); - if(action==MBCS_STATE_VALID_16) { - offset+=MBCS_ENTRY_FINAL_VALUE_16(entry); - c=unicodeCodeUnits[offset]; - if(c<0xfffe) { - /* output BMP code point */ - *target++=c; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - byteIndex=0; - } else if(c==0xfffe) { - if(UCNV_TO_U_USE_FALLBACK(cnv) && (entry=(int32_t)ucnv_MBCSGetFallback(&cnv->sharedData->mbcs, offset))!=0xfffe) { - /* output fallback BMP code point */ - *target++=(UChar)entry; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - byteIndex=0; - } - } else { - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - } else if(action==MBCS_STATE_VALID_DIRECT_16) { - /* output BMP code point */ - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - byteIndex=0; - } else if(action==MBCS_STATE_VALID_16_PAIR) { - offset+=MBCS_ENTRY_FINAL_VALUE_16(entry); - c=unicodeCodeUnits[offset++]; - if(c<0xd800) { - /* output BMP code point below 0xd800 */ - *target++=c; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - byteIndex=0; - } else if(UCNV_TO_U_USE_FALLBACK(cnv) ? c<=0xdfff : c<=0xdbff) { - /* output roundtrip or fallback surrogate pair */ - *target++=(UChar)(c&0xdbff); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - byteIndex=0; - if(targetUCharErrorBuffer[0]=unicodeCodeUnits[offset]; - cnv->UCharErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - - offset=0; - break; - } - } else if(UCNV_TO_U_USE_FALLBACK(cnv) ? (c&0xfffe)==0xe000 : c==0xe000) { - /* output roundtrip BMP code point above 0xd800 or fallback BMP code point */ - *target++=unicodeCodeUnits[offset]; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - byteIndex=0; - } else if(c==0xffff) { - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - } else if(action==MBCS_STATE_VALID_DIRECT_20 || - (action==MBCS_STATE_FALLBACK_DIRECT_20 && UCNV_TO_U_USE_FALLBACK(cnv)) - ) { - entry=MBCS_ENTRY_FINAL_VALUE(entry); - /* output surrogate pair */ - *target++=(UChar)(0xd800|(UChar)(entry>>10)); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - byteIndex=0; - c=(UChar)(0xdc00|(UChar)(entry&0x3ff)); - if(targetUCharErrorBuffer[0]=c; - cnv->UCharErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - - offset=0; - break; - } - } else if(action==MBCS_STATE_CHANGE_ONLY) { - /* - * This serves as a state change without any output. - * It is useful for reading simple stateful encodings, - * for example using just Shift-In/Shift-Out codes. - * The 21 unused bits may later be used for more sophisticated - * state transitions. - */ - if(cnv->sharedData->mbcs.dbcsOnlyState==0) { - byteIndex=0; - } else { - /* SI/SO are illegal for DBCS-only conversion */ - state=(uint8_t)(cnv->mode); /* restore the previous state */ - - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - } else if(action==MBCS_STATE_FALLBACK_DIRECT_16) { - if(UCNV_TO_U_USE_FALLBACK(cnv)) { - /* output BMP code point */ - *target++=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - byteIndex=0; - } - } else if(action==MBCS_STATE_UNASSIGNED) { - /* just fall through */ - } else if(action==MBCS_STATE_ILLEGAL) { - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } else { - /* reserved, must never occur */ - byteIndex=0; - } - - /* end of action codes: prepare for a new character */ - offset=0; - - if(byteIndex==0) { - sourceIndex=nextSourceIndex; - } else if(U_FAILURE(*pErrorCode)) { - /* callback(illegal) */ - if(byteIndex>1) { - /* - * Ticket 5691: consistent illegal sequences: - * - We include at least the first byte in the illegal sequence. - * - If any of the non-initial bytes could be the start of a character, - * we stop the illegal sequence before the first one of those. - */ - UBool isDBCSOnly=(UBool)(cnv->sharedData->mbcs.dbcsOnlyState!=0); - int8_t i; - for(i=1; - isource); - byteIndex=i; /* length of reported illegal byte sequence */ - if(backOutDistance<=bytesFromThisBuffer) { - source-=backOutDistance; - } else { - /* Back out bytes from the previous buffer: Need to replay them. */ - cnv->preToULength=(int8_t)(bytesFromThisBuffer-backOutDistance); - /* preToULength is negative! */ - uprv_memcpy(cnv->preToU, bytes+i, -cnv->preToULength); - source=(const uint8_t *)pArgs->source; - } - } - } - break; - } else /* unassigned sequences indicated with byteIndex>0 */ { - /* try an extension mapping */ - pArgs->source=(const char *)source; - byteIndex=_extToU(cnv, cnv->sharedData, - byteIndex, &source, sourceLimit, - &target, targetLimit, - &offsets, sourceIndex, - pArgs->flush, - pErrorCode); - sourceIndex=nextSourceIndex+=(int32_t)(source-(const uint8_t *)pArgs->source); - - if(U_FAILURE(*pErrorCode)) { - /* not mappable or buffer overflow */ - break; - } - } - } - - /* set the converter state back into UConverter */ - cnv->toUnicodeStatus=offset; - cnv->mode=state; - cnv->toULength=byteIndex; - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - pArgs->offsets=offsets; -} - -/* - * This version of ucnv_MBCSGetNextUChar() is optimized for single-byte, single-state codepages. - * We still need a conversion loop in case we find reserved action codes, which are to be ignored. - */ -static UChar32 -ucnv_MBCSSingleGetNextUChar(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const int32_t (*stateTable)[256]; - const uint8_t *source, *sourceLimit; - - int32_t entry; - uint8_t action; - - /* set up the local pointers */ - cnv=pArgs->converter; - source=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { - stateTable=(const int32_t (*)[256])cnv->sharedData->mbcs.swapLFNLStateTable; - } else { - stateTable=cnv->sharedData->mbcs.stateTable; - } - - /* conversion loop */ - while(sourcesource=(const char *)source; - - if(MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(entry)) { - /* output BMP code point */ - return (UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - } - - /* - * An if-else-if chain provides more reliable performance for - * the most common cases compared to a switch. - */ - action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); - if( action==MBCS_STATE_VALID_DIRECT_20 || - (action==MBCS_STATE_FALLBACK_DIRECT_20 && UCNV_TO_U_USE_FALLBACK(cnv)) - ) { - /* output supplementary code point */ - return (UChar32)(MBCS_ENTRY_FINAL_VALUE(entry)+0x10000); - } else if(action==MBCS_STATE_FALLBACK_DIRECT_16) { - if(UCNV_TO_U_USE_FALLBACK(cnv)) { - /* output BMP code point */ - return (UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - } - } else if(action==MBCS_STATE_UNASSIGNED) { - /* just fall through */ - } else if(action==MBCS_STATE_ILLEGAL) { - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } else { - /* reserved, must never occur */ - continue; - } - - if(U_FAILURE(*pErrorCode)) { - /* callback(illegal) */ - break; - } else /* unassigned sequence */ { - /* defer to the generic implementation */ - pArgs->source=(const char *)source-1; - return UCNV_GET_NEXT_UCHAR_USE_TO_U; - } - } - - /* no output because of empty input or only state changes */ - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0xffff; -} - -/* - * Version of _MBCSToUnicodeWithOffsets() optimized for single-character - * conversion without offset handling. - * - * When a character does not have a mapping to Unicode, then we return to the - * generic ucnv_getNextUChar() code for extension/GB 18030 and error/callback - * handling. - * We also defer to the generic code in other complicated cases and have them - * ultimately handled by _MBCSToUnicodeWithOffsets() itself. - * - * All normal mappings and errors are handled here. - */ -static UChar32 U_CALLCONV -ucnv_MBCSGetNextUChar(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const uint8_t *source, *sourceLimit, *lastSource; - - const int32_t (*stateTable)[256]; - const uint16_t *unicodeCodeUnits; - - uint32_t offset; - uint8_t state; - - int32_t entry; - UChar32 c; - uint8_t action; - - /* use optimized function if possible */ - cnv=pArgs->converter; - - if(cnv->preToULength>0) { - /* use the generic code in ucnv_getNextUChar() to continue with a partial match */ - return UCNV_GET_NEXT_UCHAR_USE_TO_U; - } - - if(cnv->sharedData->mbcs.unicodeMask&UCNV_HAS_SURROGATES) { - /* - * Using the generic ucnv_getNextUChar() code lets us deal correctly - * with the rare case of a codepage that maps single surrogates - * without adding the complexity to this already complicated function here. - */ - return UCNV_GET_NEXT_UCHAR_USE_TO_U; - } else if(cnv->sharedData->mbcs.countStates==1) { - return ucnv_MBCSSingleGetNextUChar(pArgs, pErrorCode); - } - - /* set up the local pointers */ - source=lastSource=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - - if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { - stateTable=(const int32_t (*)[256])cnv->sharedData->mbcs.swapLFNLStateTable; - } else { - stateTable=cnv->sharedData->mbcs.stateTable; - } - unicodeCodeUnits=cnv->sharedData->mbcs.unicodeCodeUnits; - - /* get the converter state from UConverter */ - offset=cnv->toUnicodeStatus; - - /* - * if we are in the SBCS state for a DBCS-only converter, - * then load the DBCS state from the MBCS data - * (dbcsOnlyState==0 if it is not a DBCS-only converter) - */ - if((state=(uint8_t)(cnv->mode))==0) { - state=cnv->sharedData->mbcs.dbcsOnlyState; - } - - /* conversion loop */ - c=U_SENTINEL; - while(sourcemode=state; - - /* set the next state early so that we can reuse the entry variable */ - state=(uint8_t)MBCS_ENTRY_FINAL_STATE(entry); /* typically 0 */ - - /* - * An if-else-if chain provides more reliable performance for - * the most common cases compared to a switch. - */ - action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); - if(action==MBCS_STATE_VALID_DIRECT_16) { - /* output BMP code point */ - c=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - break; - } else if(action==MBCS_STATE_VALID_16) { - offset+=MBCS_ENTRY_FINAL_VALUE_16(entry); - c=unicodeCodeUnits[offset]; - if(c<0xfffe) { - /* output BMP code point */ - break; - } else if(c==0xfffe) { - if(UCNV_TO_U_USE_FALLBACK(cnv) && (c=ucnv_MBCSGetFallback(&cnv->sharedData->mbcs, offset))!=0xfffe) { - break; - } - } else { - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - } else if(action==MBCS_STATE_VALID_16_PAIR) { - offset+=MBCS_ENTRY_FINAL_VALUE_16(entry); - c=unicodeCodeUnits[offset++]; - if(c<0xd800) { - /* output BMP code point below 0xd800 */ - break; - } else if(UCNV_TO_U_USE_FALLBACK(cnv) ? c<=0xdfff : c<=0xdbff) { - /* output roundtrip or fallback supplementary code point */ - c=((c&0x3ff)<<10)+unicodeCodeUnits[offset]+(0x10000-0xdc00); - break; - } else if(UCNV_TO_U_USE_FALLBACK(cnv) ? (c&0xfffe)==0xe000 : c==0xe000) { - /* output roundtrip BMP code point above 0xd800 or fallback BMP code point */ - c=unicodeCodeUnits[offset]; - break; - } else if(c==0xffff) { - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - } else if(action==MBCS_STATE_VALID_DIRECT_20 || - (action==MBCS_STATE_FALLBACK_DIRECT_20 && UCNV_TO_U_USE_FALLBACK(cnv)) - ) { - /* output supplementary code point */ - c=(UChar32)(MBCS_ENTRY_FINAL_VALUE(entry)+0x10000); - break; - } else if(action==MBCS_STATE_CHANGE_ONLY) { - /* - * This serves as a state change without any output. - * It is useful for reading simple stateful encodings, - * for example using just Shift-In/Shift-Out codes. - * The 21 unused bits may later be used for more sophisticated - * state transitions. - */ - if(cnv->sharedData->mbcs.dbcsOnlyState!=0) { - /* SI/SO are illegal for DBCS-only conversion */ - state=(uint8_t)(cnv->mode); /* restore the previous state */ - - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } - } else if(action==MBCS_STATE_FALLBACK_DIRECT_16) { - if(UCNV_TO_U_USE_FALLBACK(cnv)) { - /* output BMP code point */ - c=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - break; - } - } else if(action==MBCS_STATE_UNASSIGNED) { - /* just fall through */ - } else if(action==MBCS_STATE_ILLEGAL) { - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - } else { - /* reserved (must never occur), or only state change */ - offset=0; - lastSource=source; - continue; - } - - /* end of action codes: prepare for a new character */ - offset=0; - - if(U_FAILURE(*pErrorCode)) { - /* callback(illegal) */ - break; - } else /* unassigned sequence */ { - /* defer to the generic implementation */ - cnv->toUnicodeStatus=0; - cnv->mode=state; - pArgs->source=(const char *)lastSource; - return UCNV_GET_NEXT_UCHAR_USE_TO_U; - } - } - } - - if(c<0) { - if(U_SUCCESS(*pErrorCode) && source==sourceLimit && lastSourcetoUBytes; - cnv->toULength=(int8_t)(source-lastSource); - do { - *bytes++=*lastSource++; - } while(lastSourcesharedData->mbcs.dbcsOnlyState!=0); - uint8_t *bytes=cnv->toUBytes; - *bytes++=*lastSource++; /* first byte */ - if(lastSource==source) { - cnv->toULength=1; - } else /* lastSourcetoULength=i; - source=lastSource; - } - } else { - /* no output because of empty input or only state changes */ - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - } - c=0xffff; - } - - /* set the converter state back into UConverter, ready for a new character */ - cnv->toUnicodeStatus=0; - cnv->mode=state; - - /* write back the updated pointer */ - pArgs->source=(const char *)source; - return c; -} - -#if 0 -/* - * Code disabled 2002dec09 (ICU 2.4) because it is not currently used in ICU. markus - * Removal improves code coverage. - */ -/** - * This version of ucnv_MBCSSimpleGetNextUChar() is optimized for single-byte, single-state codepages. - * It does not handle the EBCDIC swaplfnl option (set in UConverter). - * It does not handle conversion extensions (_extToU()). - */ -U_CFUNC UChar32 -ucnv_MBCSSingleSimpleGetNextUChar(UConverterSharedData *sharedData, - uint8_t b, UBool useFallback) { - int32_t entry; - uint8_t action; - - entry=sharedData->mbcs.stateTable[0][b]; - /* MBCS_ENTRY_IS_FINAL(entry) */ - - if(MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(entry)) { - /* output BMP code point */ - return (UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - } - - /* - * An if-else-if chain provides more reliable performance for - * the most common cases compared to a switch. - */ - action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); - if(action==MBCS_STATE_VALID_DIRECT_20) { - /* output supplementary code point */ - return 0x10000+MBCS_ENTRY_FINAL_VALUE(entry); - } else if(action==MBCS_STATE_FALLBACK_DIRECT_16) { - if(!TO_U_USE_FALLBACK(useFallback)) { - return 0xfffe; - } - /* output BMP code point */ - return (UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - } else if(action==MBCS_STATE_FALLBACK_DIRECT_20) { - if(!TO_U_USE_FALLBACK(useFallback)) { - return 0xfffe; - } - /* output supplementary code point */ - return 0x10000+MBCS_ENTRY_FINAL_VALUE(entry); - } else if(action==MBCS_STATE_UNASSIGNED) { - return 0xfffe; - } else if(action==MBCS_STATE_ILLEGAL) { - return 0xffff; - } else { - /* reserved, must never occur */ - return 0xffff; - } -} -#endif - -/* - * This is a simple version of _MBCSGetNextUChar() that is used - * by other converter implementations. - * It only returns an "assigned" result if it consumes the entire input. - * It does not use state from the converter, nor error codes. - * It does not handle the EBCDIC swaplfnl option (set in UConverter). - * It handles conversion extensions but not GB 18030. - * - * Return value: - * U+fffe unassigned - * U+ffff illegal - * otherwise the Unicode code point - */ -U_CFUNC UChar32 -ucnv_MBCSSimpleGetNextUChar(UConverterSharedData *sharedData, - const char *source, int32_t length, - UBool useFallback) { - const int32_t (*stateTable)[256]; - const uint16_t *unicodeCodeUnits; - - uint32_t offset; - uint8_t state, action; - - UChar32 c; - int32_t i, entry; - - if(length<=0) { - /* no input at all: "illegal" */ - return 0xffff; - } - -#if 0 -/* - * Code disabled 2002dec09 (ICU 2.4) because it is not currently used in ICU. markus - * TODO In future releases, verify that this function is never called for SBCS - * conversions, i.e., that sharedData->mbcs.countStates==1 is still true. - * Removal improves code coverage. - */ - /* use optimized function if possible */ - if(sharedData->mbcs.countStates==1) { - if(length==1) { - return ucnv_MBCSSingleSimpleGetNextUChar(sharedData, (uint8_t)*source, useFallback); - } else { - return 0xffff; /* illegal: more than a single byte for an SBCS converter */ - } - } -#endif - - /* set up the local pointers */ - stateTable=sharedData->mbcs.stateTable; - unicodeCodeUnits=sharedData->mbcs.unicodeCodeUnits; - - /* converter state */ - offset=0; - state=sharedData->mbcs.dbcsOnlyState; - - /* conversion loop */ - for(i=0;;) { - entry=stateTable[state][(uint8_t)source[i++]]; - if(MBCS_ENTRY_IS_TRANSITION(entry)) { - state=(uint8_t)MBCS_ENTRY_TRANSITION_STATE(entry); - offset+=MBCS_ENTRY_TRANSITION_OFFSET(entry); - - if(i==length) { - return 0xffff; /* truncated character */ - } - } else { - /* - * An if-else-if chain provides more reliable performance for - * the most common cases compared to a switch. - */ - action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); - if(action==MBCS_STATE_VALID_16) { - offset+=MBCS_ENTRY_FINAL_VALUE_16(entry); - c=unicodeCodeUnits[offset]; - if(c!=0xfffe) { - /* done */ - } else if(UCNV_TO_U_USE_FALLBACK(cnv)) { - c=ucnv_MBCSGetFallback(&sharedData->mbcs, offset); - /* else done with 0xfffe */ - } - break; - } else if(action==MBCS_STATE_VALID_DIRECT_16) { - /* output BMP code point */ - c=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - break; - } else if(action==MBCS_STATE_VALID_16_PAIR) { - offset+=MBCS_ENTRY_FINAL_VALUE_16(entry); - c=unicodeCodeUnits[offset++]; - if(c<0xd800) { - /* output BMP code point below 0xd800 */ - } else if(UCNV_TO_U_USE_FALLBACK(cnv) ? c<=0xdfff : c<=0xdbff) { - /* output roundtrip or fallback supplementary code point */ - c=(UChar32)(((c&0x3ff)<<10)+unicodeCodeUnits[offset]+(0x10000-0xdc00)); - } else if(UCNV_TO_U_USE_FALLBACK(cnv) ? (c&0xfffe)==0xe000 : c==0xe000) { - /* output roundtrip BMP code point above 0xd800 or fallback BMP code point */ - c=unicodeCodeUnits[offset]; - } else if(c==0xffff) { - return 0xffff; - } else { - c=0xfffe; - } - break; - } else if(action==MBCS_STATE_VALID_DIRECT_20) { - /* output supplementary code point */ - c=0x10000+MBCS_ENTRY_FINAL_VALUE(entry); - break; - } else if(action==MBCS_STATE_FALLBACK_DIRECT_16) { - if(!TO_U_USE_FALLBACK(useFallback)) { - c=0xfffe; - break; - } - /* output BMP code point */ - c=(UChar)MBCS_ENTRY_FINAL_VALUE_16(entry); - break; - } else if(action==MBCS_STATE_FALLBACK_DIRECT_20) { - if(!TO_U_USE_FALLBACK(useFallback)) { - c=0xfffe; - break; - } - /* output supplementary code point */ - c=0x10000+MBCS_ENTRY_FINAL_VALUE(entry); - break; - } else if(action==MBCS_STATE_UNASSIGNED) { - c=0xfffe; - break; - } - - /* - * forbid MBCS_STATE_CHANGE_ONLY for this function, - * and MBCS_STATE_ILLEGAL and reserved action codes - */ - return 0xffff; - } - } - - if(i!=length) { - /* illegal for this function: not all input consumed */ - return 0xffff; - } - - if(c==0xfffe) { - /* try an extension mapping */ - const int32_t *cx=sharedData->mbcs.extIndexes; - if(cx!=NULL) { - return ucnv_extSimpleMatchToU(cx, source, length, useFallback); - } - } - - return c; -} - -/* MBCS-from-Unicode conversion functions ----------------------------------- */ - -/* This version of ucnv_MBCSFromUnicodeWithOffsets() is optimized for double-byte codepages. */ -static void -ucnv_MBCSDoubleFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const UChar *source, *sourceLimit; - uint8_t *target; - int32_t targetCapacity; - int32_t *offsets; - - const uint16_t *table; - const uint16_t *mbcsIndex; - const uint8_t *bytes; - - UChar32 c; - - int32_t sourceIndex, nextSourceIndex; - - uint32_t stage2Entry; - uint32_t asciiRoundtrips; - uint32_t value; - uint8_t unicodeMask; - - /* use optimized function if possible */ - cnv=pArgs->converter; - unicodeMask=cnv->sharedData->mbcs.unicodeMask; - - /* set up the local pointers */ - source=pArgs->source; - sourceLimit=pArgs->sourceLimit; - target=(uint8_t *)pArgs->target; - targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); - offsets=pArgs->offsets; - - table=cnv->sharedData->mbcs.fromUnicodeTable; - mbcsIndex=cnv->sharedData->mbcs.mbcsIndex; - if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { - bytes=cnv->sharedData->mbcs.swapLFNLFromUnicodeBytes; - } else { - bytes=cnv->sharedData->mbcs.fromUnicodeBytes; - } - asciiRoundtrips=cnv->sharedData->mbcs.asciiRoundtrips; - - /* get the converter state from UConverter */ - c=cnv->fromUChar32; - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex= c==0 ? 0 : -1; - nextSourceIndex=0; - - /* conversion loop */ - if(c!=0 && targetCapacity>0) { - goto getTrail; - } - - while(source0) { - /* - * Get a correct Unicode code point: - * a single UChar for a BMP code point or - * a matched surrogate pair for a "supplementary code point". - */ - c=*source++; - ++nextSourceIndex; - if(c<=0x7f && IS_ASCII_ROUNDTRIP(c, asciiRoundtrips)) { - *target++=(uint8_t)c; - if(offsets!=NULL) { - *offsets++=sourceIndex; - sourceIndex=nextSourceIndex; - } - --targetCapacity; - c=0; - continue; - } - /* - * utf8Friendly table: Test for <=0xd7ff rather than <=MBCS_FAST_MAX - * to avoid dealing with surrogates. - * MBCS_FAST_MAX must be >=0xd7ff. - */ - if(c<=0xd7ff) { - value=DBCS_RESULT_FROM_MOST_BMP(mbcsIndex, (const uint16_t *)bytes, c); - /* There are only roundtrips (!=0) and no-mapping (==0) entries. */ - if(value==0) { - goto unassigned; - } - /* output the value */ - } else { - /* - * This also tests if the codepage maps single surrogates. - * If it does, then surrogates are not paired but mapped separately. - * Note that in this case unmatched surrogates are not detected. - */ - if(U16_IS_SURROGATE(c) && !(unicodeMask&UCNV_HAS_SURROGATES)) { - if(U16_IS_SURROGATE_LEAD(c)) { -getTrail: - if(sourcesource=source; - c=_extFromU(cnv, cnv->sharedData, - c, &source, sourceLimit, - &target, target+targetCapacity, - &offsets, sourceIndex, - pArgs->flush, - pErrorCode); - nextSourceIndex+=(int32_t)(source-pArgs->source); - - if(U_FAILURE(*pErrorCode)) { - /* not mappable or buffer overflow */ - break; - } else { - /* a mapping was written to the target, continue */ - - /* recalculate the targetCapacity after an extension mapping */ - targetCapacity=(int32_t)(pArgs->targetLimit-(char *)target); - - /* normal end of conversion: prepare for a new character */ - sourceIndex=nextSourceIndex; - continue; - } - } - } - - /* write the output character bytes from value and length */ - /* from the first if in the loop we know that targetCapacity>0 */ - if(value<=0xff) { - /* this is easy because we know that there is enough space */ - *target++=(uint8_t)value; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - --targetCapacity; - } else /* length==2 */ { - *target++=(uint8_t)(value>>8); - if(2<=targetCapacity) { - *target++=(uint8_t)value; - if(offsets!=NULL) { - *offsets++=sourceIndex; - *offsets++=sourceIndex; - } - targetCapacity-=2; - } else { - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - cnv->charErrorBuffer[0]=(char)value; - cnv->charErrorBufferLength=1; - - /* target overflow */ - targetCapacity=0; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - c=0; - break; - } - } - - /* normal end of conversion: prepare for a new character */ - c=0; - sourceIndex=nextSourceIndex; - continue; - } else { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - - /* set the converter state back into UConverter */ - cnv->fromUChar32=c; - - /* write back the updated pointers */ - pArgs->source=source; - pArgs->target=(char *)target; - pArgs->offsets=offsets; -} - -/* This version of ucnv_MBCSFromUnicodeWithOffsets() is optimized for single-byte codepages. */ -static void -ucnv_MBCSSingleFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const UChar *source, *sourceLimit; - uint8_t *target; - int32_t targetCapacity; - int32_t *offsets; - - const uint16_t *table; - const uint16_t *results; - - UChar32 c; - - int32_t sourceIndex, nextSourceIndex; - - uint16_t value, minValue; - UBool hasSupplementary; - - /* set up the local pointers */ - cnv=pArgs->converter; - source=pArgs->source; - sourceLimit=pArgs->sourceLimit; - target=(uint8_t *)pArgs->target; - targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); - offsets=pArgs->offsets; - - table=cnv->sharedData->mbcs.fromUnicodeTable; - if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { - results=(uint16_t *)cnv->sharedData->mbcs.swapLFNLFromUnicodeBytes; - } else { - results=(uint16_t *)cnv->sharedData->mbcs.fromUnicodeBytes; - } - - if(cnv->useFallback) { - /* use all roundtrip and fallback results */ - minValue=0x800; - } else { - /* use only roundtrips and fallbacks from private-use characters */ - minValue=0xc00; - } - hasSupplementary=(UBool)(cnv->sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY); - - /* get the converter state from UConverter */ - c=cnv->fromUChar32; - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex= c==0 ? 0 : -1; - nextSourceIndex=0; - - /* conversion loop */ - if(c!=0 && targetCapacity>0) { - goto getTrail; - } - - while(source0) { - /* - * Get a correct Unicode code point: - * a single UChar for a BMP code point or - * a matched surrogate pair for a "supplementary code point". - */ - c=*source++; - ++nextSourceIndex; - if(U16_IS_SURROGATE(c)) { - if(U16_IS_SURROGATE_LEAD(c)) { -getTrail: - if(source=minValue) { - /* assigned, write the output character bytes from value and length */ - /* length==1 */ - /* this is easy because we know that there is enough space */ - *target++=(uint8_t)value; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - --targetCapacity; - - /* normal end of conversion: prepare for a new character */ - c=0; - sourceIndex=nextSourceIndex; - } else { /* unassigned */ -unassigned: - /* try an extension mapping */ - pArgs->source=source; - c=_extFromU(cnv, cnv->sharedData, - c, &source, sourceLimit, - &target, target+targetCapacity, - &offsets, sourceIndex, - pArgs->flush, - pErrorCode); - nextSourceIndex+=(int32_t)(source-pArgs->source); - - if(U_FAILURE(*pErrorCode)) { - /* not mappable or buffer overflow */ - break; - } else { - /* a mapping was written to the target, continue */ - - /* recalculate the targetCapacity after an extension mapping */ - targetCapacity=(int32_t)(pArgs->targetLimit-(char *)target); - - /* normal end of conversion: prepare for a new character */ - sourceIndex=nextSourceIndex; - } - } - } else { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - - /* set the converter state back into UConverter */ - cnv->fromUChar32=c; - - /* write back the updated pointers */ - pArgs->source=source; - pArgs->target=(char *)target; - pArgs->offsets=offsets; -} - -/* - * This version of ucnv_MBCSFromUnicode() is optimized for single-byte codepages - * that map only to and from the BMP. - * In addition to single-byte/state optimizations, the offset calculations - * become much easier. - * It would be possible to use the sbcsIndex for UTF-8-friendly tables, - * but measurements have shown that this diminishes performance - * in more cases than it improves it. - * See SVN revision 21013 (2007-feb-06) for the last version with #if switches - * for various MBCS and SBCS optimizations. - */ -static void -ucnv_MBCSSingleFromBMPWithOffsets(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const UChar *source, *sourceLimit, *lastSource; - uint8_t *target; - int32_t targetCapacity, length; - int32_t *offsets; - - const uint16_t *table; - const uint16_t *results; - - UChar32 c; - - int32_t sourceIndex; - - uint32_t asciiRoundtrips; - uint16_t value, minValue; - - /* set up the local pointers */ - cnv=pArgs->converter; - source=pArgs->source; - sourceLimit=pArgs->sourceLimit; - target=(uint8_t *)pArgs->target; - targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); - offsets=pArgs->offsets; - - table=cnv->sharedData->mbcs.fromUnicodeTable; - if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { - results=(uint16_t *)cnv->sharedData->mbcs.swapLFNLFromUnicodeBytes; - } else { - results=(uint16_t *)cnv->sharedData->mbcs.fromUnicodeBytes; - } - asciiRoundtrips=cnv->sharedData->mbcs.asciiRoundtrips; - - if(cnv->useFallback) { - /* use all roundtrip and fallback results */ - minValue=0x800; - } else { - /* use only roundtrips and fallbacks from private-use characters */ - minValue=0xc00; - } - - /* get the converter state from UConverter */ - c=cnv->fromUChar32; - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex= c==0 ? 0 : -1; - lastSource=source; - - /* - * since the conversion here is 1:1 UChar:uint8_t, we need only one counter - * for the minimum of the sourceLength and targetCapacity - */ - length=(int32_t)(sourceLimit-source); - if(length0) { - goto getTrail; - } - -#if MBCS_UNROLL_SINGLE_FROM_BMP - /* unrolling makes it slower on Pentium III/Windows 2000?! */ - /* unroll the loop with the most common case */ -unrolled: - if(targetCapacity>=4) { - int32_t count, loops; - uint16_t andedValues; - - loops=count=targetCapacity>>2; - do { - c=*source++; - andedValues=value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); - *target++=(uint8_t)value; - c=*source++; - andedValues&=value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); - *target++=(uint8_t)value; - c=*source++; - andedValues&=value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); - *target++=(uint8_t)value; - c=*source++; - andedValues&=value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); - *target++=(uint8_t)value; - - /* were all 4 entries really valid? */ - if(andedValues0); - count=loops-count; - targetCapacity-=4*count; - - if(offsets!=NULL) { - lastSource+=4*count; - while(count>0) { - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - *offsets++=sourceIndex++; - --count; - } - } - - c=0; - } -#endif - - while(targetCapacity>0) { - /* - * Get a correct Unicode code point: - * a single UChar for a BMP code point or - * a matched surrogate pair for a "supplementary code point". - */ - c=*source++; - /* - * Do not immediately check for single surrogates: - * Assume that they are unassigned and check for them in that case. - * This speeds up the conversion of assigned characters. - */ - /* convert the Unicode code point in c into codepage bytes */ - if(c<=0x7f && IS_ASCII_ROUNDTRIP(c, asciiRoundtrips)) { - *target++=(uint8_t)c; - --targetCapacity; - c=0; - continue; - } - value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); - /* is this code point assigned, or do we use fallbacks? */ - if(value>=minValue) { - /* assigned, write the output character bytes from value and length */ - /* length==1 */ - /* this is easy because we know that there is enough space */ - *target++=(uint8_t)value; - --targetCapacity; - - /* normal end of conversion: prepare for a new character */ - c=0; - continue; - } else if(!U16_IS_SURROGATE(c)) { - /* normal, unassigned BMP character */ - } else if(U16_IS_SURROGATE_LEAD(c)) { -getTrail: - if(sourceflush) { - *pErrorCode=U_TRUNCATED_CHAR_FOUND; - } - break; - } - } else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } - - /* c does not have a mapping */ - - /* get the number of code units for c to correctly advance sourceIndex */ - length=U16_LENGTH(c); - - /* set offsets since the start or the last extension */ - if(offsets!=NULL) { - int32_t count=(int32_t)(source-lastSource); - - /* do not set the offset for this character */ - count-=length; - - while(count>0) { - *offsets++=sourceIndex++; - --count; - } - /* offsets and sourceIndex are now set for the current character */ - } - - /* try an extension mapping */ - lastSource=source; - c=_extFromU(cnv, cnv->sharedData, - c, &source, sourceLimit, - &target, (const uint8_t *)(pArgs->targetLimit), - &offsets, sourceIndex, - pArgs->flush, - pErrorCode); - sourceIndex+=length+(int32_t)(source-lastSource); - lastSource=source; - - if(U_FAILURE(*pErrorCode)) { - /* not mappable or buffer overflow */ - break; - } else { - /* a mapping was written to the target, continue */ - - /* recalculate the targetCapacity after an extension mapping */ - targetCapacity=(int32_t)(pArgs->targetLimit-(char *)target); - length=(int32_t)(sourceLimit-source); - if(length=(uint8_t *)pArgs->targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - - /* set offsets since the start or the last callback */ - if(offsets!=NULL) { - size_t count=source-lastSource; - if (count > 0 && *pErrorCode == U_TRUNCATED_CHAR_FOUND) { - /* - Caller gave us a partial supplementary character, - which this function couldn't convert in any case. - The callback will handle the offset. - */ - count--; - } - while(count>0) { - *offsets++=sourceIndex++; - --count; - } - } - - /* set the converter state back into UConverter */ - cnv->fromUChar32=c; - - /* write back the updated pointers */ - pArgs->source=source; - pArgs->target=(char *)target; - pArgs->offsets=offsets; -} - -U_CFUNC void -ucnv_MBCSFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - const UChar *source, *sourceLimit; - uint8_t *target; - int32_t targetCapacity; - int32_t *offsets; - - const uint16_t *table; - const uint16_t *mbcsIndex; - const uint8_t *p, *bytes; - uint8_t outputType; - - UChar32 c; - - int32_t prevSourceIndex, sourceIndex, nextSourceIndex; - - uint32_t stage2Entry; - uint32_t asciiRoundtrips; - uint32_t value; - /* Shift-In and Shift-Out byte sequences differ by encoding scheme. */ - uint8_t siBytes[2] = {0, 0}; - uint8_t soBytes[2] = {0, 0}; - uint8_t siLength, soLength; - int32_t length = 0, prevLength; - uint8_t unicodeMask; - - cnv=pArgs->converter; - - if(cnv->preFromUFirstCP>=0) { - /* - * pass sourceIndex=-1 because we continue from an earlier buffer - * in the future, this may change with continuous offsets - */ - ucnv_extContinueMatchFromU(cnv, pArgs, -1, pErrorCode); - - if(U_FAILURE(*pErrorCode) || cnv->preFromULength<0) { - return; - } - } - - /* use optimized function if possible */ - outputType=cnv->sharedData->mbcs.outputType; - unicodeMask=cnv->sharedData->mbcs.unicodeMask; - if(outputType==MBCS_OUTPUT_1 && !(unicodeMask&UCNV_HAS_SURROGATES)) { - if(!(unicodeMask&UCNV_HAS_SUPPLEMENTARY)) { - ucnv_MBCSSingleFromBMPWithOffsets(pArgs, pErrorCode); - } else { - ucnv_MBCSSingleFromUnicodeWithOffsets(pArgs, pErrorCode); - } - return; - } else if(outputType==MBCS_OUTPUT_2 && cnv->sharedData->mbcs.utf8Friendly) { - ucnv_MBCSDoubleFromUnicodeWithOffsets(pArgs, pErrorCode); - return; - } - - /* set up the local pointers */ - source=pArgs->source; - sourceLimit=pArgs->sourceLimit; - target=(uint8_t *)pArgs->target; - targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); - offsets=pArgs->offsets; - - table=cnv->sharedData->mbcs.fromUnicodeTable; - if(cnv->sharedData->mbcs.utf8Friendly) { - mbcsIndex=cnv->sharedData->mbcs.mbcsIndex; - } else { - mbcsIndex=NULL; - } - if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { - bytes=cnv->sharedData->mbcs.swapLFNLFromUnicodeBytes; - } else { - bytes=cnv->sharedData->mbcs.fromUnicodeBytes; - } - asciiRoundtrips=cnv->sharedData->mbcs.asciiRoundtrips; - - /* get the converter state from UConverter */ - c=cnv->fromUChar32; - - if(outputType==MBCS_OUTPUT_2_SISO) { - prevLength=cnv->fromUnicodeStatus; - if(prevLength==0) { - /* set the real value */ - prevLength=1; - } - } else { - /* prevent fromUnicodeStatus from being set to something non-0 */ - prevLength=0; - } - - /* sourceIndex=-1 if the current character began in the previous buffer */ - prevSourceIndex=-1; - sourceIndex= c==0 ? 0 : -1; - nextSourceIndex=0; - - /* Get the SI/SO character for the converter */ - siLength = static_cast(getSISOBytes(SI, cnv->options, siBytes)); - soLength = static_cast(getSISOBytes(SO, cnv->options, soBytes)); - - /* conversion loop */ - /* - * This is another piece of ugly code: - * A goto into the loop if the converter state contains a first surrogate - * from the previous function call. - * It saves me to check in each loop iteration a check of if(c==0) - * and duplicating the trail-surrogate-handling code in the else - * branch of that check. - * I could not find any other way to get around this other than - * using a function call for the conversion and callback, which would - * be even more inefficient. - * - * Markus Scherer 2000-jul-19 - */ - if(c!=0 && targetCapacity>0) { - goto getTrail; - } - - while(source0) { - /* - * Get a correct Unicode code point: - * a single UChar for a BMP code point or - * a matched surrogate pair for a "supplementary code point". - */ - c=*source++; - ++nextSourceIndex; - if(c<=0x7f && IS_ASCII_ROUNDTRIP(c, asciiRoundtrips)) { - *target++=(uint8_t)c; - if(offsets!=NULL) { - *offsets++=sourceIndex; - prevSourceIndex=sourceIndex; - sourceIndex=nextSourceIndex; - } - --targetCapacity; - c=0; - continue; - } - /* - * utf8Friendly table: Test for <=0xd7ff rather than <=MBCS_FAST_MAX - * to avoid dealing with surrogates. - * MBCS_FAST_MAX must be >=0xd7ff. - */ - if(c<=0xd7ff && mbcsIndex!=NULL) { - value=mbcsIndex[c>>6]; - - /* get the bytes and the length for the output (copied from below and adapted for utf8Friendly data) */ - /* There are only roundtrips (!=0) and no-mapping (==0) entries. */ - switch(outputType) { - case MBCS_OUTPUT_2: - value=((const uint16_t *)bytes)[value +(c&0x3f)]; - if(value<=0xff) { - if(value==0) { - goto unassigned; - } else { - length=1; - } - } else { - length=2; - } - break; - case MBCS_OUTPUT_2_SISO: - /* 1/2-byte stateful with Shift-In/Shift-Out */ - /* - * Save the old state in the converter object - * right here, then change the local prevLength state variable if necessary. - * Then, if this character turns out to be unassigned or a fallback that - * is not taken, the callback code must not save the new state in the converter - * because the new state is for a character that is not output. - * However, the callback must still restore the state from the converter - * in case the callback function changed it for its output. - */ - cnv->fromUnicodeStatus=prevLength; /* save the old state */ - value=((const uint16_t *)bytes)[value +(c&0x3f)]; - if(value<=0xff) { - if(value==0) { - goto unassigned; - } else if(prevLength<=1) { - length=1; - } else { - /* change from double-byte mode to single-byte */ - if (siLength == 1) { - value|=(uint32_t)siBytes[0]<<8; - length = 2; - } else if (siLength == 2) { - value|=(uint32_t)siBytes[1]<<8; - value|=(uint32_t)siBytes[0]<<16; - length = 3; - } - prevLength=1; - } - } else { - if(prevLength==2) { - length=2; - } else { - /* change from single-byte mode to double-byte */ - if (soLength == 1) { - value|=(uint32_t)soBytes[0]<<16; - length = 3; - } else if (soLength == 2) { - value|=(uint32_t)soBytes[1]<<16; - value|=(uint32_t)soBytes[0]<<24; - length = 4; - } - prevLength=2; - } - } - break; - case MBCS_OUTPUT_DBCS_ONLY: - /* table with single-byte results, but only DBCS mappings used */ - value=((const uint16_t *)bytes)[value +(c&0x3f)]; - if(value<=0xff) { - /* no mapping or SBCS result, not taken for DBCS-only */ - goto unassigned; - } else { - length=2; - } - break; - case MBCS_OUTPUT_3: - p=bytes+(value+(c&0x3f))*3; - value=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; - if(value<=0xff) { - if(value==0) { - goto unassigned; - } else { - length=1; - } - } else if(value<=0xffff) { - length=2; - } else { - length=3; - } - break; - case MBCS_OUTPUT_4: - value=((const uint32_t *)bytes)[value +(c&0x3f)]; - if(value<=0xff) { - if(value==0) { - goto unassigned; - } else { - length=1; - } - } else if(value<=0xffff) { - length=2; - } else if(value<=0xffffff) { - length=3; - } else { - length=4; - } - break; - case MBCS_OUTPUT_3_EUC: - value=((const uint16_t *)bytes)[value +(c&0x3f)]; - /* EUC 16-bit fixed-length representation */ - if(value<=0xff) { - if(value==0) { - goto unassigned; - } else { - length=1; - } - } else if((value&0x8000)==0) { - value|=0x8e8000; - length=3; - } else if((value&0x80)==0) { - value|=0x8f0080; - length=3; - } else { - length=2; - } - break; - case MBCS_OUTPUT_4_EUC: - p=bytes+(value+(c&0x3f))*3; - value=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; - /* EUC 16-bit fixed-length representation applied to the first two bytes */ - if(value<=0xff) { - if(value==0) { - goto unassigned; - } else { - length=1; - } - } else if(value<=0xffff) { - length=2; - } else if((value&0x800000)==0) { - value|=0x8e800000; - length=4; - } else if((value&0x8000)==0) { - value|=0x8f008000; - length=4; - } else { - length=3; - } - break; - default: - /* must not occur */ - /* - * To avoid compiler warnings that value & length may be - * used without having been initialized, we set them here. - * In reality, this is unreachable code. - * Not having a default branch also causes warnings with - * some compilers. - */ - value=0; - length=0; - break; - } - /* output the value */ - } else { - /* - * This also tests if the codepage maps single surrogates. - * If it does, then surrogates are not paired but mapped separately. - * Note that in this case unmatched surrogates are not detected. - */ - if(U16_IS_SURROGATE(c) && !(unicodeMask&UCNV_HAS_SURROGATES)) { - if(U16_IS_SURROGATE_LEAD(c)) { -getTrail: - if(sourcefromUnicodeStatus=prevLength; /* save the old state */ - /* callback(unassigned) */ - goto unassigned; - } - /* convert this supplementary code point */ - /* exit this condition tree */ - } else { - /* this is an unmatched lead code unit (1st surrogate) */ - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } - } else { - /* no more input */ - break; - } - } else { - /* this is an unmatched trail code unit (2nd surrogate) */ - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - break; - } - } - - /* convert the Unicode code point in c into codepage bytes */ - - /* - * The basic lookup is a triple-stage compact array (trie) lookup. - * For details see the beginning of this file. - * - * Single-byte codepages are handled with a different data structure - * by _MBCSSingle... functions. - * - * The result consists of a 32-bit value from stage 2 and - * a pointer to as many bytes as are stored per character. - * The pointer points to the character's bytes in stage 3. - * Bits 15..0 of the stage 2 entry contain the stage 3 index - * for that pointer, while bits 31..16 are flags for which of - * the 16 characters in the block are roundtrip-assigned. - * - * For 2-byte and 4-byte codepages, the bytes are stored as uint16_t - * respectively as uint32_t, in the platform encoding. - * For 3-byte codepages, the bytes are always stored in big-endian order. - * - * For EUC encodings that use only either 0x8e or 0x8f as the first - * byte of their longest byte sequences, the first two bytes in - * this third stage indicate with their 7th bits whether these bytes - * are to be written directly or actually need to be preceded by - * one of the two Single-Shift codes. With this, the third stage - * stores one byte fewer per character than the actual maximum length of - * EUC byte sequences. - * - * Other than that, leading zero bytes are removed and the other - * bytes output. A single zero byte may be output if the "assigned" - * bit in stage 2 was on. - * The data structure does not support zero byte output as a fallback, - * and also does not allow output of leading zeros. - */ - stage2Entry=MBCS_STAGE_2_FROM_U(table, c); - - /* get the bytes and the length for the output */ - switch(outputType) { - case MBCS_OUTPUT_2: - value=MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, c); - if(value<=0xff) { - length=1; - } else { - length=2; - } - break; - case MBCS_OUTPUT_2_SISO: - /* 1/2-byte stateful with Shift-In/Shift-Out */ - /* - * Save the old state in the converter object - * right here, then change the local prevLength state variable if necessary. - * Then, if this character turns out to be unassigned or a fallback that - * is not taken, the callback code must not save the new state in the converter - * because the new state is for a character that is not output. - * However, the callback must still restore the state from the converter - * in case the callback function changed it for its output. - */ - cnv->fromUnicodeStatus=prevLength; /* save the old state */ - value=MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, c); - if(value<=0xff) { - if(value==0 && MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c)==0) { - /* no mapping, leave value==0 */ - length=0; - } else if(prevLength<=1) { - length=1; - } else { - /* change from double-byte mode to single-byte */ - if (siLength == 1) { - value|=(uint32_t)siBytes[0]<<8; - length = 2; - } else if (siLength == 2) { - value|=(uint32_t)siBytes[1]<<8; - value|=(uint32_t)siBytes[0]<<16; - length = 3; - } - prevLength=1; - } - } else { - if(prevLength==2) { - length=2; - } else { - /* change from single-byte mode to double-byte */ - if (soLength == 1) { - value|=(uint32_t)soBytes[0]<<16; - length = 3; - } else if (soLength == 2) { - value|=(uint32_t)soBytes[1]<<16; - value|=(uint32_t)soBytes[0]<<24; - length = 4; - } - prevLength=2; - } - } - break; - case MBCS_OUTPUT_DBCS_ONLY: - /* table with single-byte results, but only DBCS mappings used */ - value=MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, c); - if(value<=0xff) { - /* no mapping or SBCS result, not taken for DBCS-only */ - value=stage2Entry=0; /* stage2Entry=0 to reset roundtrip flags */ - length=0; - } else { - length=2; - } - break; - case MBCS_OUTPUT_3: - p=MBCS_POINTER_3_FROM_STAGE_2(bytes, stage2Entry, c); - value=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; - if(value<=0xff) { - length=1; - } else if(value<=0xffff) { - length=2; - } else { - length=3; - } - break; - case MBCS_OUTPUT_4: - value=MBCS_VALUE_4_FROM_STAGE_2(bytes, stage2Entry, c); - if(value<=0xff) { - length=1; - } else if(value<=0xffff) { - length=2; - } else if(value<=0xffffff) { - length=3; - } else { - length=4; - } - break; - case MBCS_OUTPUT_3_EUC: - value=MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, c); - /* EUC 16-bit fixed-length representation */ - if(value<=0xff) { - length=1; - } else if((value&0x8000)==0) { - value|=0x8e8000; - length=3; - } else if((value&0x80)==0) { - value|=0x8f0080; - length=3; - } else { - length=2; - } - break; - case MBCS_OUTPUT_4_EUC: - p=MBCS_POINTER_3_FROM_STAGE_2(bytes, stage2Entry, c); - value=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; - /* EUC 16-bit fixed-length representation applied to the first two bytes */ - if(value<=0xff) { - length=1; - } else if(value<=0xffff) { - length=2; - } else if((value&0x800000)==0) { - value|=0x8e800000; - length=4; - } else if((value&0x8000)==0) { - value|=0x8f008000; - length=4; - } else { - length=3; - } - break; - default: - /* must not occur */ - /* - * To avoid compiler warnings that value & length may be - * used without having been initialized, we set them here. - * In reality, this is unreachable code. - * Not having a default branch also causes warnings with - * some compilers. - */ - value=stage2Entry=0; /* stage2Entry=0 to reset roundtrip flags */ - length=0; - break; - } - - /* is this code point assigned, or do we use fallbacks? */ - if(!(MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c)!=0 || - (UCNV_FROM_U_USE_FALLBACK(cnv, c) && value!=0)) - ) { - /* - * We allow a 0 byte output if the "assigned" bit is set for this entry. - * There is no way with this data structure for fallback output - * to be a zero byte. - */ - -unassigned: - /* try an extension mapping */ - pArgs->source=source; - c=_extFromU(cnv, cnv->sharedData, - c, &source, sourceLimit, - &target, target+targetCapacity, - &offsets, sourceIndex, - pArgs->flush, - pErrorCode); - nextSourceIndex+=(int32_t)(source-pArgs->source); - prevLength=cnv->fromUnicodeStatus; /* restore SISO state */ - - if(U_FAILURE(*pErrorCode)) { - /* not mappable or buffer overflow */ - break; - } else { - /* a mapping was written to the target, continue */ - - /* recalculate the targetCapacity after an extension mapping */ - targetCapacity=(int32_t)(pArgs->targetLimit-(char *)target); - - /* normal end of conversion: prepare for a new character */ - if(offsets!=NULL) { - prevSourceIndex=sourceIndex; - sourceIndex=nextSourceIndex; - } - continue; - } - } - } - - /* write the output character bytes from value and length */ - /* from the first if in the loop we know that targetCapacity>0 */ - if(length<=targetCapacity) { - if(offsets==NULL) { - switch(length) { - /* each branch falls through to the next one */ - case 4: - *target++=(uint8_t)(value>>24); - U_FALLTHROUGH; - case 3: - *target++=(uint8_t)(value>>16); - U_FALLTHROUGH; - case 2: - *target++=(uint8_t)(value>>8); - U_FALLTHROUGH; - case 1: - *target++=(uint8_t)value; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - } else { - switch(length) { - /* each branch falls through to the next one */ - case 4: - *target++=(uint8_t)(value>>24); - *offsets++=sourceIndex; - U_FALLTHROUGH; - case 3: - *target++=(uint8_t)(value>>16); - *offsets++=sourceIndex; - U_FALLTHROUGH; - case 2: - *target++=(uint8_t)(value>>8); - *offsets++=sourceIndex; - U_FALLTHROUGH; - case 1: - *target++=(uint8_t)value; - *offsets++=sourceIndex; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - } - targetCapacity-=length; - } else { - uint8_t *charErrorBuffer; - - /* - * We actually do this backwards here: - * In order to save an intermediate variable, we output - * first to the overflow buffer what does not fit into the - * regular target. - */ - /* we know that 1<=targetCapacitycharErrorBuffer; - switch(length) { - /* each branch falls through to the next one */ - case 3: - *charErrorBuffer++=(uint8_t)(value>>16); - U_FALLTHROUGH; - case 2: - *charErrorBuffer++=(uint8_t)(value>>8); - U_FALLTHROUGH; - case 1: - *charErrorBuffer=(uint8_t)value; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - cnv->charErrorBufferLength=(int8_t)length; - - /* now output what fits into the regular target */ - value>>=8*length; /* length was reduced by targetCapacity */ - switch(targetCapacity) { - /* each branch falls through to the next one */ - case 3: - *target++=(uint8_t)(value>>16); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - U_FALLTHROUGH; - case 2: - *target++=(uint8_t)(value>>8); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - U_FALLTHROUGH; - case 1: - *target++=(uint8_t)value; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - - /* target overflow */ - targetCapacity=0; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - c=0; - break; - } - - /* normal end of conversion: prepare for a new character */ - c=0; - if(offsets!=NULL) { - prevSourceIndex=sourceIndex; - sourceIndex=nextSourceIndex; - } - continue; - } else { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - - /* - * the end of the input stream and detection of truncated input - * are handled by the framework, but for EBCDIC_STATEFUL conversion - * we need to emit an SI at the very end - * - * conditions: - * successful - * EBCDIC_STATEFUL in DBCS mode - * end of input and no truncated input - */ - if( U_SUCCESS(*pErrorCode) && - outputType==MBCS_OUTPUT_2_SISO && prevLength==2 && - pArgs->flush && source>=sourceLimit && c==0 - ) { - /* EBCDIC_STATEFUL ending with DBCS: emit an SI to return the output stream to SBCS */ - if(targetCapacity>0) { - *target++=(uint8_t)siBytes[0]; - if (siLength == 2) { - if (targetCapacity<2) { - cnv->charErrorBuffer[0]=(uint8_t)siBytes[1]; - cnv->charErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } else { - *target++=(uint8_t)siBytes[1]; - } - } - if(offsets!=NULL) { - /* set the last source character's index (sourceIndex points at sourceLimit now) */ - *offsets++=prevSourceIndex; - } - } else { - /* target is full */ - cnv->charErrorBuffer[0]=(uint8_t)siBytes[0]; - if (siLength == 2) { - cnv->charErrorBuffer[1]=(uint8_t)siBytes[1]; - } - cnv->charErrorBufferLength=siLength; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - prevLength=1; /* we switched into SBCS */ - } - - /* set the converter state back into UConverter */ - cnv->fromUChar32=c; - cnv->fromUnicodeStatus=prevLength; - - /* write back the updated pointers */ - pArgs->source=source; - pArgs->target=(char *)target; - pArgs->offsets=offsets; -} - -/* - * This is another simple conversion function for internal use by other - * conversion implementations. - * It does not use the converter state nor call callbacks. - * It does not handle the EBCDIC swaplfnl option (set in UConverter). - * It handles conversion extensions but not GB 18030. - * - * It converts one single Unicode code point into codepage bytes, encoded - * as one 32-bit value. The function returns the number of bytes in *pValue: - * 1..4 the number of bytes in *pValue - * 0 unassigned (*pValue undefined) - * -1 illegal (currently not used, *pValue undefined) - * - * *pValue will contain the resulting bytes with the last byte in bits 7..0, - * the second to last byte in bits 15..8, etc. - * Currently, the function assumes but does not check that 0<=c<=0x10ffff. - */ -U_CFUNC int32_t -ucnv_MBCSFromUChar32(UConverterSharedData *sharedData, - UChar32 c, uint32_t *pValue, - UBool useFallback) { - const int32_t *cx; - const uint16_t *table; -#if 0 -/* #if 0 because this is not currently used in ICU - reduce code, increase code coverage */ - const uint8_t *p; -#endif - uint32_t stage2Entry; - uint32_t value; - int32_t length; - - /* BMP-only codepages are stored without stage 1 entries for supplementary code points */ - if(c<=0xffff || (sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY)) { - table=sharedData->mbcs.fromUnicodeTable; - - /* convert the Unicode code point in c into codepage bytes (same as in _MBCSFromUnicodeWithOffsets) */ - if(sharedData->mbcs.outputType==MBCS_OUTPUT_1) { - value=MBCS_SINGLE_RESULT_FROM_U(table, (uint16_t *)sharedData->mbcs.fromUnicodeBytes, c); - /* is this code point assigned, or do we use fallbacks? */ - if(useFallback ? value>=0x800 : value>=0xc00) { - *pValue=value&0xff; - return 1; - } - } else /* outputType!=MBCS_OUTPUT_1 */ { - stage2Entry=MBCS_STAGE_2_FROM_U(table, c); - - /* get the bytes and the length for the output */ - switch(sharedData->mbcs.outputType) { - case MBCS_OUTPUT_2: - value=MBCS_VALUE_2_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); - if(value<=0xff) { - length=1; - } else { - length=2; - } - break; -#if 0 -/* #if 0 because this is not currently used in ICU - reduce code, increase code coverage */ - case MBCS_OUTPUT_DBCS_ONLY: - /* table with single-byte results, but only DBCS mappings used */ - value=MBCS_VALUE_2_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); - if(value<=0xff) { - /* no mapping or SBCS result, not taken for DBCS-only */ - value=stage2Entry=0; /* stage2Entry=0 to reset roundtrip flags */ - length=0; - } else { - length=2; - } - break; - case MBCS_OUTPUT_3: - p=MBCS_POINTER_3_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); - value=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; - if(value<=0xff) { - length=1; - } else if(value<=0xffff) { - length=2; - } else { - length=3; - } - break; - case MBCS_OUTPUT_4: - value=MBCS_VALUE_4_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); - if(value<=0xff) { - length=1; - } else if(value<=0xffff) { - length=2; - } else if(value<=0xffffff) { - length=3; - } else { - length=4; - } - break; - case MBCS_OUTPUT_3_EUC: - value=MBCS_VALUE_2_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); - /* EUC 16-bit fixed-length representation */ - if(value<=0xff) { - length=1; - } else if((value&0x8000)==0) { - value|=0x8e8000; - length=3; - } else if((value&0x80)==0) { - value|=0x8f0080; - length=3; - } else { - length=2; - } - break; - case MBCS_OUTPUT_4_EUC: - p=MBCS_POINTER_3_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); - value=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; - /* EUC 16-bit fixed-length representation applied to the first two bytes */ - if(value<=0xff) { - length=1; - } else if(value<=0xffff) { - length=2; - } else if((value&0x800000)==0) { - value|=0x8e800000; - length=4; - } else if((value&0x8000)==0) { - value|=0x8f008000; - length=4; - } else { - length=3; - } - break; -#endif - default: - /* must not occur */ - return -1; - } - - /* is this code point assigned, or do we use fallbacks? */ - if( MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c) || - (FROM_U_USE_FALLBACK(useFallback, c) && value!=0) - ) { - /* - * We allow a 0 byte output if the "assigned" bit is set for this entry. - * There is no way with this data structure for fallback output - * to be a zero byte. - */ - /* assigned */ - *pValue=value; - return length; - } - } - } - - cx=sharedData->mbcs.extIndexes; - if(cx!=NULL) { - length=ucnv_extSimpleMatchFromU(cx, c, pValue, useFallback); - return length>=0 ? length : -length; /* return abs(length); */ - } - - /* unassigned */ - return 0; -} - - -#if 0 -/* - * This function has been moved to ucnv2022.c for inlining. - * This implementation is here only for documentation purposes - */ - -/** - * This version of ucnv_MBCSFromUChar32() is optimized for single-byte codepages. - * It does not handle the EBCDIC swaplfnl option (set in UConverter). - * It does not handle conversion extensions (_extFromU()). - * - * It returns the codepage byte for the code point, or -1 if it is unassigned. - */ -U_CFUNC int32_t -ucnv_MBCSSingleFromUChar32(UConverterSharedData *sharedData, - UChar32 c, - UBool useFallback) { - const uint16_t *table; - int32_t value; - - /* BMP-only codepages are stored without stage 1 entries for supplementary code points */ - if(c>=0x10000 && !(sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY)) { - return -1; - } - - /* convert the Unicode code point in c into codepage bytes (same as in _MBCSFromUnicodeWithOffsets) */ - table=sharedData->mbcs.fromUnicodeTable; - - /* get the byte for the output */ - value=MBCS_SINGLE_RESULT_FROM_U(table, (uint16_t *)sharedData->mbcs.fromUnicodeBytes, c); - /* is this code point assigned, or do we use fallbacks? */ - if(useFallback ? value>=0x800 : value>=0xc00) { - return value&0xff; - } else { - return -1; - } -} -#endif - -/* MBCS-from-UTF-8 conversion functions ------------------------------------- */ - -/* offsets for n-byte UTF-8 sequences that were calculated with ((lead<<6)+trail)<<6+trail... */ -static const UChar32 -utf8_offsets[5]={ 0, 0, 0x3080, 0xE2080, 0x3C82080 }; - -static void U_CALLCONV -ucnv_SBCSFromUTF8(UConverterFromUnicodeArgs *pFromUArgs, - UConverterToUnicodeArgs *pToUArgs, - UErrorCode *pErrorCode) { - UConverter *utf8, *cnv; - const uint8_t *source, *sourceLimit; - uint8_t *target; - int32_t targetCapacity; - - const uint16_t *table, *sbcsIndex; - const uint16_t *results; - - int8_t oldToULength, toULength, toULimit; - - UChar32 c; - uint8_t b, t1, t2; - - uint32_t asciiRoundtrips; - uint16_t value, minValue = 0; - UBool hasSupplementary; - - /* set up the local pointers */ - utf8=pToUArgs->converter; - cnv=pFromUArgs->converter; - source=(uint8_t *)pToUArgs->source; - sourceLimit=(uint8_t *)pToUArgs->sourceLimit; - target=(uint8_t *)pFromUArgs->target; - targetCapacity=(int32_t)(pFromUArgs->targetLimit-pFromUArgs->target); - - table=cnv->sharedData->mbcs.fromUnicodeTable; - sbcsIndex=cnv->sharedData->mbcs.sbcsIndex; - if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { - results=(uint16_t *)cnv->sharedData->mbcs.swapLFNLFromUnicodeBytes; - } else { - results=(uint16_t *)cnv->sharedData->mbcs.fromUnicodeBytes; - } - asciiRoundtrips=cnv->sharedData->mbcs.asciiRoundtrips; - - if(cnv->useFallback) { - /* use all roundtrip and fallback results */ - minValue=0x800; - } else { - /* use only roundtrips and fallbacks from private-use characters */ - minValue=0xc00; - } - hasSupplementary=(UBool)(cnv->sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY); - - /* get the converter state from the UTF-8 UConverter */ - if(utf8->toULength > 0) { - toULength=oldToULength=utf8->toULength; - toULimit=(int8_t)utf8->mode; - c=(UChar32)utf8->toUnicodeStatus; - } else { - toULength=oldToULength=toULimit=0; - c = 0; - } - - // The conversion loop checks source0) { - uint8_t b1=*(sourceLimit-1); - if(U8_IS_SINGLE(b1)) { - // common ASCII character - } else if(U8_IS_TRAIL(b1) && length>=2) { - uint8_t b2=*(sourceLimit-2); - if(0xe0<=b2 && b2<0xf0 && U8_IS_VALID_LEAD3_AND_T1(b2, b1)) { - // truncated 3-byte sequence - sourceLimit-=2; - } - } else if(0xc2<=b1 && b1<0xf0) { - // truncated 2- or 3-byte sequence - --sourceLimit; - } - } - } - - if(c!=0 && targetCapacity>0) { - utf8->toUnicodeStatus=0; - utf8->toULength=0; - goto moreBytes; - /* - * Note: We could avoid the goto by duplicating some of the moreBytes - * code, but only up to the point of collecting a complete UTF-8 - * sequence; then recurse for the toUBytes[toULength] - * and then continue with normal conversion. - * - * If so, move this code to just after initializing the minimum - * set of local variables for reading the UTF-8 input - * (utf8, source, target, limits but not cnv, table, minValue, etc.). - * - * Potential advantages: - * - avoid the goto - * - oldToULength could become a local variable in just those code blocks - * that deal with buffer boundaries - * - possibly faster if the goto prevents some compiler optimizations - * (this would need measuring to confirm) - * Disadvantage: - * - code duplication - */ - } - - /* conversion loop */ - while(source0) { - b=*source++; - if(U8_IS_SINGLE(b)) { - /* convert ASCII */ - if(IS_ASCII_ROUNDTRIP(b, asciiRoundtrips)) { - *target++=(uint8_t)b; - --targetCapacity; - continue; - } else { - c=b; - value=SBCS_RESULT_FROM_UTF8(sbcsIndex, results, 0, c); - } - } else { - if(b<0xe0) { - if( /* handle U+0080..U+07FF inline */ - b>=0xc2 && - (t1=(uint8_t)(*source-0x80)) <= 0x3f - ) { - c=b&0x1f; - ++source; - value=SBCS_RESULT_FROM_UTF8(sbcsIndex, results, c, t1); - if(value>=minValue) { - *target++=(uint8_t)value; - --targetCapacity; - continue; - } else { - c=(c<<6)|t1; - } - } else { - c=-1; - } - } else if(b==0xe0) { - if( /* handle U+0800..U+0FFF inline */ - (t1=(uint8_t)(source[0]-0x80)) <= 0x3f && t1 >= 0x20 && - (t2=(uint8_t)(source[1]-0x80)) <= 0x3f - ) { - c=t1; - source+=2; - value=SBCS_RESULT_FROM_UTF8(sbcsIndex, results, c, t2); - if(value>=minValue) { - *target++=(uint8_t)value; - --targetCapacity; - continue; - } else { - c=(c<<6)|t2; - } - } else { - c=-1; - } - } else { - c=-1; - } - - if(c<0) { - /* handle "complicated" and error cases, and continuing partial characters */ - oldToULength=0; - toULength=1; - toULimit=U8_COUNT_BYTES_NON_ASCII(b); - c=b; -moreBytes: - while(toULengthsourceLimit) { - b=*source; - if(icu::UTF8::isValidTrail(c, b, toULength, toULimit)) { - ++source; - ++toULength; - c=(c<<6)+b; - } else { - break; /* sequence too short, stop with toULengthtoUBytes[oldToULength++]=*source++; - } - utf8->toUnicodeStatus=c; - utf8->toULength=toULength; - utf8->mode=toULimit; - pToUArgs->source=(char *)source; - pFromUArgs->target=(char *)target; - return; - } - } - - if(toULength==toULimit) { - c-=utf8_offsets[toULength]; - if(toULength<=3) { /* BMP */ - value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); - } else { - /* supplementary code point */ - if(!hasSupplementary) { - /* BMP-only codepages are stored without stage 1 entries for supplementary code points */ - value=0; - } else { - value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); - } - } - } else { - /* error handling: illegal UTF-8 byte sequence */ - source-=(toULength-oldToULength); - while(oldToULengthtoUBytes[oldToULength++]=*source++; - } - utf8->toULength=toULength; - pToUArgs->source=(char *)source; - pFromUArgs->target=(char *)target; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - return; - } - } - } - - if(value>=minValue) { - /* output the mapping for c */ - *target++=(uint8_t)value; - --targetCapacity; - } else { - /* valueUTF-16->charset conversion. - */ - static const UChar nul=0; - const UChar *noSource=&nul; - c=_extFromU(cnv, cnv->sharedData, - c, &noSource, noSource, - &target, target+targetCapacity, - NULL, -1, - pFromUArgs->flush, - pErrorCode); - - if(U_FAILURE(*pErrorCode)) { - /* not mappable or buffer overflow */ - cnv->fromUChar32=c; - break; - } else if(cnv->preFromUFirstCP>=0) { - /* - * Partial match, return and revert to pivoting. - * In normal from-UTF-16 conversion, we would just continue - * but then exit the loop because the extension match would - * have consumed the source. - */ - *pErrorCode=U_USING_DEFAULT_WARNING; - break; - } else { - /* a mapping was written to the target, continue */ - - /* recalculate the targetCapacity after an extension mapping */ - targetCapacity=(int32_t)(pFromUArgs->targetLimit-(char *)target); - } - } - } else { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - - /* - * The sourceLimit may have been adjusted before the conversion loop - * to stop before a truncated sequence. - * If so, then collect the truncated sequence now. - */ - if(U_SUCCESS(*pErrorCode) && - cnv->preFromUFirstCP<0 && - source<(sourceLimit=(uint8_t *)pToUArgs->sourceLimit)) { - c=utf8->toUBytes[0]=b=*source++; - toULength=1; - toULimit=U8_COUNT_BYTES(b); - while(sourcetoUBytes[toULength++]=b=*source++; - c=(c<<6)+b; - } - utf8->toUnicodeStatus=c; - utf8->toULength=toULength; - utf8->mode=toULimit; - } - - /* write back the updated pointers */ - pToUArgs->source=(char *)source; - pFromUArgs->target=(char *)target; -} - -static void U_CALLCONV -ucnv_DBCSFromUTF8(UConverterFromUnicodeArgs *pFromUArgs, - UConverterToUnicodeArgs *pToUArgs, - UErrorCode *pErrorCode) { - UConverter *utf8, *cnv; - const uint8_t *source, *sourceLimit; - uint8_t *target; - int32_t targetCapacity; - - const uint16_t *table, *mbcsIndex; - const uint16_t *results; - - int8_t oldToULength, toULength, toULimit; - - UChar32 c; - uint8_t b, t1, t2; - - uint32_t stage2Entry; - uint32_t asciiRoundtrips; - uint16_t value = 0; - UBool hasSupplementary; - - /* set up the local pointers */ - utf8=pToUArgs->converter; - cnv=pFromUArgs->converter; - source=(uint8_t *)pToUArgs->source; - sourceLimit=(uint8_t *)pToUArgs->sourceLimit; - target=(uint8_t *)pFromUArgs->target; - targetCapacity=(int32_t)(pFromUArgs->targetLimit-pFromUArgs->target); - - table=cnv->sharedData->mbcs.fromUnicodeTable; - mbcsIndex=cnv->sharedData->mbcs.mbcsIndex; - if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { - results=(uint16_t *)cnv->sharedData->mbcs.swapLFNLFromUnicodeBytes; - } else { - results=(uint16_t *)cnv->sharedData->mbcs.fromUnicodeBytes; - } - asciiRoundtrips=cnv->sharedData->mbcs.asciiRoundtrips; - - hasSupplementary=(UBool)(cnv->sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY); - - /* get the converter state from the UTF-8 UConverter */ - if(utf8->toULength > 0) { - toULength=oldToULength=utf8->toULength; - toULimit=(int8_t)utf8->mode; - c=(UChar32)utf8->toUnicodeStatus; - } else { - toULength=oldToULength=toULimit=0; - c = 0; - } - - // The conversion loop checks source0) { - uint8_t b1=*(sourceLimit-1); - if(U8_IS_SINGLE(b1)) { - // common ASCII character - } else if(U8_IS_TRAIL(b1) && length>=2) { - uint8_t b2=*(sourceLimit-2); - if(0xe0<=b2 && b2<0xf0 && U8_IS_VALID_LEAD3_AND_T1(b2, b1)) { - // truncated 3-byte sequence - sourceLimit-=2; - } - } else if(0xc2<=b1 && b1<0xf0) { - // truncated 2- or 3-byte sequence - --sourceLimit; - } - } - } - - if(c!=0 && targetCapacity>0) { - utf8->toUnicodeStatus=0; - utf8->toULength=0; - goto moreBytes; - /* See note in ucnv_SBCSFromUTF8() about this goto. */ - } - - /* conversion loop */ - while(source0) { - b=*source++; - if(U8_IS_SINGLE(b)) { - /* convert ASCII */ - if(IS_ASCII_ROUNDTRIP(b, asciiRoundtrips)) { - *target++=b; - --targetCapacity; - continue; - } else { - value=DBCS_RESULT_FROM_UTF8(mbcsIndex, results, 0, b); - if(value==0) { - c=b; - goto unassigned; - } - } - } else { - if(b>=0xe0) { - if( /* handle U+0800..U+D7FF inline */ - b<=0xed && // do not assume maxFastUChar>0xd7ff - U8_IS_VALID_LEAD3_AND_T1(b, t1=source[0]) && - (t2=(uint8_t)(source[1]-0x80)) <= 0x3f - ) { - c=((b&0xf)<<6)|(t1&0x3f); - source+=2; - value=DBCS_RESULT_FROM_UTF8(mbcsIndex, results, c, t2); - if(value==0) { - c=(c<<6)|t2; - goto unassigned; - } - } else { - c=-1; - } - } else { - if( /* handle U+0080..U+07FF inline */ - b>=0xc2 && - (t1=(uint8_t)(*source-0x80)) <= 0x3f - ) { - c=b&0x1f; - ++source; - value=DBCS_RESULT_FROM_UTF8(mbcsIndex, results, c, t1); - if(value==0) { - c=(c<<6)|t1; - goto unassigned; - } - } else { - c=-1; - } - } - - if(c<0) { - /* handle "complicated" and error cases, and continuing partial characters */ - oldToULength=0; - toULength=1; - toULimit=U8_COUNT_BYTES_NON_ASCII(b); - c=b; -moreBytes: - while(toULengthsourceLimit) { - b=*source; - if(icu::UTF8::isValidTrail(c, b, toULength, toULimit)) { - ++source; - ++toULength; - c=(c<<6)+b; - } else { - break; /* sequence too short, stop with toULengthtoUBytes[oldToULength++]=*source++; - } - utf8->toUnicodeStatus=c; - utf8->toULength=toULength; - utf8->mode=toULimit; - pToUArgs->source=(char *)source; - pFromUArgs->target=(char *)target; - return; - } - } - - if(toULength==toULimit) { - c-=utf8_offsets[toULength]; - if(toULength<=3) { /* BMP */ - stage2Entry=MBCS_STAGE_2_FROM_U(table, c); - } else { - /* supplementary code point */ - if(!hasSupplementary) { - /* BMP-only codepages are stored without stage 1 entries for supplementary code points */ - stage2Entry=0; - } else { - stage2Entry=MBCS_STAGE_2_FROM_U(table, c); - } - } - } else { - /* error handling: illegal UTF-8 byte sequence */ - source-=(toULength-oldToULength); - while(oldToULengthtoUBytes[oldToULength++]=*source++; - } - utf8->toULength=toULength; - pToUArgs->source=(char *)source; - pFromUArgs->target=(char *)target; - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - return; - } - - /* get the bytes and the length for the output */ - /* MBCS_OUTPUT_2 */ - value=MBCS_VALUE_2_FROM_STAGE_2(results, stage2Entry, c); - - /* is this code point assigned, or do we use fallbacks? */ - if(!(MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c) || - (UCNV_FROM_U_USE_FALLBACK(cnv, c) && value!=0)) - ) { - goto unassigned; - } - } - } - - /* write the output character bytes from value and length */ - /* from the first if in the loop we know that targetCapacity>0 */ - if(value<=0xff) { - /* this is easy because we know that there is enough space */ - *target++=(uint8_t)value; - --targetCapacity; - } else /* length==2 */ { - *target++=(uint8_t)(value>>8); - if(2<=targetCapacity) { - *target++=(uint8_t)value; - targetCapacity-=2; - } else { - cnv->charErrorBuffer[0]=(char)value; - cnv->charErrorBufferLength=1; - - /* target overflow */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - continue; - -unassigned: - { - /* - * Try an extension mapping. - * Pass in no source because we don't have UTF-16 input. - * If we have a partial match on c, we will return and revert - * to UTF-8->UTF-16->charset conversion. - */ - static const UChar nul=0; - const UChar *noSource=&nul; - c=_extFromU(cnv, cnv->sharedData, - c, &noSource, noSource, - &target, target+targetCapacity, - NULL, -1, - pFromUArgs->flush, - pErrorCode); - - if(U_FAILURE(*pErrorCode)) { - /* not mappable or buffer overflow */ - cnv->fromUChar32=c; - break; - } else if(cnv->preFromUFirstCP>=0) { - /* - * Partial match, return and revert to pivoting. - * In normal from-UTF-16 conversion, we would just continue - * but then exit the loop because the extension match would - * have consumed the source. - */ - *pErrorCode=U_USING_DEFAULT_WARNING; - break; - } else { - /* a mapping was written to the target, continue */ - - /* recalculate the targetCapacity after an extension mapping */ - targetCapacity=(int32_t)(pFromUArgs->targetLimit-(char *)target); - continue; - } - } - } else { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - } - - /* - * The sourceLimit may have been adjusted before the conversion loop - * to stop before a truncated sequence. - * If so, then collect the truncated sequence now. - */ - if(U_SUCCESS(*pErrorCode) && - cnv->preFromUFirstCP<0 && - source<(sourceLimit=(uint8_t *)pToUArgs->sourceLimit)) { - c=utf8->toUBytes[0]=b=*source++; - toULength=1; - toULimit=U8_COUNT_BYTES(b); - while(sourcetoUBytes[toULength++]=b=*source++; - c=(c<<6)+b; - } - utf8->toUnicodeStatus=c; - utf8->toULength=toULength; - utf8->mode=toULimit; - } - - /* write back the updated pointers */ - pToUArgs->source=(char *)source; - pFromUArgs->target=(char *)target; -} - -/* miscellaneous ------------------------------------------------------------ */ - -static void U_CALLCONV -ucnv_MBCSGetStarters(const UConverter* cnv, - UBool starters[256], - UErrorCode *) { - const int32_t *state0; - int i; - - state0=cnv->sharedData->mbcs.stateTable[cnv->sharedData->mbcs.dbcsOnlyState]; - for(i=0; i<256; ++i) { - /* all bytes that cause a state transition from state 0 are lead bytes */ - starters[i]= (UBool)MBCS_ENTRY_IS_TRANSITION(state0[i]); - } -} - -/* - * This is an internal function that allows other converter implementations - * to check whether a byte is a lead byte. - */ -U_CFUNC UBool -ucnv_MBCSIsLeadByte(UConverterSharedData *sharedData, char byte) { - return (UBool)MBCS_ENTRY_IS_TRANSITION(sharedData->mbcs.stateTable[0][(uint8_t)byte]); -} - -static void U_CALLCONV -ucnv_MBCSWriteSub(UConverterFromUnicodeArgs *pArgs, - int32_t offsetIndex, - UErrorCode *pErrorCode) { - UConverter *cnv=pArgs->converter; - char *p, *subchar; - char buffer[4]; - int32_t length; - - /* first, select between subChar and subChar1 */ - if( cnv->subChar1!=0 && - (cnv->sharedData->mbcs.extIndexes!=NULL ? - cnv->useSubChar1 : - (cnv->invalidUCharBuffer[0]<=0xff)) - ) { - /* select subChar1 if it is set (not 0) and the unmappable Unicode code point is up to U+00ff (IBM MBCS behavior) */ - subchar=(char *)&cnv->subChar1; - length=1; - } else { - /* select subChar in all other cases */ - subchar=(char *)cnv->subChars; - length=cnv->subCharLen; - } - - /* reset the selector for the next code point */ - cnv->useSubChar1=false; - - if (cnv->sharedData->mbcs.outputType == MBCS_OUTPUT_2_SISO) { - p=buffer; - - /* fromUnicodeStatus contains prevLength */ - switch(length) { - case 1: - if(cnv->fromUnicodeStatus==2) { - /* DBCS mode and SBCS sub char: change to SBCS */ - cnv->fromUnicodeStatus=1; - *p++=UCNV_SI; - } - *p++=subchar[0]; - break; - case 2: - if(cnv->fromUnicodeStatus<=1) { - /* SBCS mode and DBCS sub char: change to DBCS */ - cnv->fromUnicodeStatus=2; - *p++=UCNV_SO; - } - *p++=subchar[0]; - *p++=subchar[1]; - break; - default: - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - subchar=buffer; - length=(int32_t)(p-buffer); - } - - ucnv_cbFromUWriteBytes(pArgs, subchar, length, offsetIndex, pErrorCode); -} - -U_CFUNC UConverterType -ucnv_MBCSGetType(const UConverter* converter) { - /* SBCS, DBCS, and EBCDIC_STATEFUL are replaced by MBCS, but here we cheat a little */ - if(converter->sharedData->mbcs.countStates==1) { - return (UConverterType)UCNV_SBCS; - } else if((converter->sharedData->mbcs.outputType&0xff)==MBCS_OUTPUT_2_SISO) { - return (UConverterType)UCNV_EBCDIC_STATEFUL; - } else if(converter->sharedData->staticData->minBytesPerChar==2 && converter->sharedData->staticData->maxBytesPerChar==2) { - return (UConverterType)UCNV_DBCS; - } - return (UConverterType)UCNV_MBCS; -} - -#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2000-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ucnvmbcs.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2000jul03 +* created by: Markus W. Scherer +* +* The current code in this file replaces the previous implementation +* of conversion code from multi-byte codepages to Unicode and back. +* This implementation supports the following: +* - legacy variable-length codepages with up to 4 bytes per character +* - all Unicode code points (up to 0x10ffff) +* - efficient distinction of unassigned vs. illegal byte sequences +* - it is possible in fromUnicode() to directly deal with simple +* stateful encodings (used for EBCDIC_STATEFUL) +* - it is possible to convert Unicode code points +* to a single zero byte (but not as a fallback except for SBCS) +* +* Remaining limitations in fromUnicode: +* - byte sequences must not have leading zero bytes +* - except for SBCS codepages: no fallback mapping from Unicode to a zero byte +* - limitation to up to 4 bytes per character +* +* ICU 2.8 (late 2003) adds a secondary data structure which lifts some of these +* limitations and adds m:n character mappings and other features. +* See ucnv_ext.h for details. +* +* Change history: +* +* 5/6/2001 Ram Moved MBCS_SINGLE_RESULT_FROM_U,MBCS_STAGE_2_FROM_U, +* MBCS_VALUE_2_FROM_STAGE_2, MBCS_VALUE_4_FROM_STAGE_2 +* macros to ucnvmbcs.h file +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION && !UCONFIG_NO_LEGACY_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/ucnv_cb.h" +#include "unicode/udata.h" +#include "unicode/uset.h" +#include "unicode/utf8.h" +#include "unicode/utf16.h" +#include "ucnv_bld.h" +#include "ucnvmbcs.h" +#include "ucnv_ext.h" +#include "ucnv_cnv.h" +#include "cmemory.h" +#include "cstring.h" +#include "umutex.h" +#include "ustr_imp.h" + +/* control optimizations according to the platform */ +#define MBCS_UNROLL_SINGLE_TO_BMP 1 +#define MBCS_UNROLL_SINGLE_FROM_BMP 0 + +/* + * _MBCSHeader versions 5.3 & 4.3 + * (Note that the _MBCSHeader version is in addition to the converter formatVersion.) + * + * This version is optional. Version 5 is used for incompatible data format changes. + * makeconv will continue to generate version 4 files if possible. + * + * Changes from version 4: + * + * The main difference is an additional _MBCSHeader field with + * - the length (number of uint32_t) of the _MBCSHeader + * - flags for further incompatible data format changes + * - flags for further, backward compatible data format changes + * + * The MBCS_OPT_FROM_U flag indicates that most of the fromUnicode data is omitted from + * the file and needs to be reconstituted at load time. + * This requires a utf8Friendly format with an additional mbcsIndex table for fast + * (and UTF-8-friendly) fromUnicode conversion for Unicode code points up to maxFastUChar. + * (For details about these structures see below, and see ucnvmbcs.h.) + * + * utf8Friendly also implies that the fromUnicode mappings are stored in ascending order + * of the Unicode code points. (This requires that the .ucm file has the |0 etc. + * precision markers for all mappings.) + * + * All fallbacks have been moved to the extension table, leaving only roundtrips in the + * omitted data that can be reconstituted from the toUnicode data. + * + * Of the stage 2 table, the part corresponding to maxFastUChar and below is omitted. + * With only roundtrip mappings in the base fromUnicode data, this part is fully + * redundant with the mbcsIndex and will be reconstituted from that (also using the + * stage 1 table which contains the information about how stage 2 was compacted). + * + * The rest of the stage 2 table, the part for code points above maxFastUChar, + * is stored in the file and will be appended to the reconstituted part. + * + * The entire fromUBytes array is omitted from the file and will be reconstitued. + * This is done by enumerating all toUnicode roundtrip mappings, performing + * each mapping (using the stage 1 and reconstituted stage 2 tables) and + * writing instead of reading the byte values. + * + * _MBCSHeader version 4.3 + * + * Change from version 4.2: + * - Optional utf8Friendly data structures, with 64-entry stage 3 block + * allocation for parts of the BMP, and an additional mbcsIndex in non-SBCS + * files which can be used instead of stages 1 & 2. + * Faster lookups for roundtrips from most commonly used characters, + * and lookups from UTF-8 byte sequences with a natural bit distribution. + * See ucnvmbcs.h for more details. + * + * Change from version 4.1: + * - Added an optional extension table structure at the end of the .cnv file. + * It is present if the upper bits of the header flags field contains a non-zero + * byte offset to it. + * Files that contain only a conversion table and no base table + * use the special outputType MBCS_OUTPUT_EXT_ONLY. + * These contain the base table name between the MBCS header and the extension + * data. + * + * Change from version 4.0: + * - Replace header.reserved with header.fromUBytesLength so that all + * fields in the data have length. + * + * Changes from version 3 (for performance improvements): + * - new bit distribution for state table entries + * - reordered action codes + * - new data structure for single-byte fromUnicode + * + stage 2 only contains indexes + * + stage 3 stores 16 bits per character with classification bits 15..8 + * - no multiplier for stage 1 entries + * - stage 2 for non-single-byte codepages contains the index and the flags in + * one 32-bit value + * - 2-byte and 4-byte fromUnicode results are stored directly as 16/32-bit integers + * + * For more details about old versions of the MBCS data structure, see + * the corresponding versions of this file. + * + * Converting stateless codepage data ---------------------------------------*** + * (or codepage data with simple states) to Unicode. + * + * Data structure and algorithm for converting from complex legacy codepages + * to Unicode. (Designed before 2000-may-22.) + * + * The basic idea is that the structure of legacy codepages can be described + * with state tables. + * When reading a byte stream, each input byte causes a state transition. + * Some transitions result in the output of a code point, some result in + * "unassigned" or "illegal" output. + * This is used here for character conversion. + * + * The data structure begins with a state table consisting of a row + * per state, with 256 entries (columns) per row for each possible input + * byte value. + * Each entry is 32 bits wide, with two formats distinguished by + * the sign bit (bit 31): + * + * One format for transitional entries (bit 31 not set) for non-final bytes, and + * one format for final entries (bit 31 set). + * Both formats contain the number of the next state in the same bit + * positions. + * State 0 is the initial state. + * + * Most of the time, the offset values of subsequent states are added + * up to a scalar value. This value will eventually be the index of + * the Unicode code point in a table that follows the state table. + * The effect is that the code points for final state table rows + * are contiguous. The code points of final state rows follow each other + * in the order of the references to those final states by previous + * states, etc. + * + * For some terminal states, the offset is itself the output Unicode + * code point (16 bits for a BMP code point or 20 bits for a supplementary + * code point (stored as code point minus 0x10000 so that 20 bits are enough). + * For others, the code point in the Unicode table is stored with either + * one or two code units: one for BMP code points, two for a pair of + * surrogates. + * All code points for a final state entry take up the same number of code + * units, regardless of whether they all actually _use_ the same number + * of code units. This is necessary for simple array access. + * + * An additional feature comes in with what in ICU is called "fallback" + * mappings: + * + * In addition to round-trippable, precise, 1:1 mappings, there are often + * mappings defined between similar, though not the same, characters. + * Typically, such mappings occur only in fromUnicode mapping tables because + * Unicode has a superset repertoire of most other codepages. However, it + * is possible to provide such mappings in the toUnicode tables, too. + * In this case, the fallback mappings are partly integrated into the + * general state tables because the structure of the encoding includes their + * byte sequences. + * For final entries in an initial state, fallback mappings are stored in + * the entry itself like with roundtrip mappings. + * For other final entries, they are stored in the code units table if + * the entry is for a pair of code units. + * For single-unit results in the code units table, there is no space to + * alternatively hold a fallback mapping; in this case, the code unit + * is stored as U+fffe (unassigned), and the fallback mapping needs to + * be looked up by the scalar offset value in a separate table. + * + * "Unassigned" state entries really mean "structurally unassigned", + * i.e., such a byte sequence will never have a mapping result. + * + * The interpretation of the bits in each entry is as follows: + * + * Bit 31 not set, not a terminal entry ("transitional"): + * 30..24 next state + * 23..0 offset delta, to be added up + * + * Bit 31 set, terminal ("final") entry: + * 30..24 next state (regardless of action code) + * 23..20 action code: + * action codes 0 and 1 result in precise-mapping Unicode code points + * 0 valid byte sequence + * 19..16 not used, 0 + * 15..0 16-bit Unicode BMP code point + * never U+fffe or U+ffff + * 1 valid byte sequence + * 19..0 20-bit Unicode supplementary code point + * never U+fffe or U+ffff + * + * action codes 2 and 3 result in fallback (unidirectional-mapping) Unicode code points + * 2 valid byte sequence (fallback) + * 19..16 not used, 0 + * 15..0 16-bit Unicode BMP code point as fallback result + * 3 valid byte sequence (fallback) + * 19..0 20-bit Unicode supplementary code point as fallback result + * + * action codes 4 and 5 may result in roundtrip/fallback/unassigned/illegal results + * depending on the code units they result in + * 4 valid byte sequence + * 19..9 not used, 0 + * 8..0 final offset delta + * pointing to one 16-bit code unit which may be + * fffe unassigned -- look for a fallback for this offset + * ffff illegal + * 5 valid byte sequence + * 19..9 not used, 0 + * 8..0 final offset delta + * pointing to two 16-bit code units + * (typically UTF-16 surrogates) + * the result depends on the first code unit as follows: + * 0000..d7ff roundtrip BMP code point (1st alone) + * d800..dbff roundtrip surrogate pair (1st, 2nd) + * dc00..dfff fallback surrogate pair (1st-400, 2nd) + * e000 roundtrip BMP code point (2nd alone) + * e001 fallback BMP code point (2nd alone) + * fffe unassigned + * ffff illegal + * (the final offset deltas are at most 255 * 2, + * times 2 because of storing code unit pairs) + * + * 6 unassigned byte sequence + * 19..16 not used, 0 + * 15..0 16-bit Unicode BMP code point U+fffe (new with version 2) + * this does not contain a final offset delta because the main + * purpose of this action code is to save scalar offset values; + * therefore, fallback values cannot be assigned to byte + * sequences that result in this action code + * 7 illegal byte sequence + * 19..16 not used, 0 + * 15..0 16-bit Unicode BMP code point U+ffff (new with version 2) + * 8 state change only + * 19..0 not used, 0 + * useful for state changes in simple stateful encodings, + * at Shift-In/Shift-Out codes + * + * + * 9..15 reserved for future use + * current implementations will only perform a state change + * and ignore bits 19..0 + * + * An encoding with contiguous ranges of unassigned byte sequences, like + * Shift-JIS and especially EUC-TW, can be stored efficiently by having + * at least two states for the trail bytes: + * One trail byte state that results in code points, and one that only + * has "unassigned" and "illegal" terminal states. + * + * Note: partly by accident, this data structure supports simple stateful + * encodings without any additional logic. + * Currently, only simple Shift-In/Shift-Out schemes are handled with + * appropriate state tables (especially EBCDIC_STATEFUL!). + * + * MBCS version 2 added: + * unassigned and illegal action codes have U+fffe and U+ffff + * instead of unused bits; this is useful for _MBCS_SINGLE_SIMPLE_GET_NEXT_BMP() + * + * Converting from Unicode to codepage bytes --------------------------------*** + * + * The conversion data structure for fromUnicode is designed for the known + * structure of Unicode. It maps from 21-bit code points (0..0x10ffff) to + * a sequence of 1..4 bytes, in addition to a flag that indicates if there is + * a roundtrip mapping. + * + * The lookup is done with a 3-stage trie, using 11/6/4 bits for stage 1/2/3 + * like in the character properties table. + * The beginning of the trie is at offsetFromUTable, the beginning of stage 3 + * with the resulting bytes is at offsetFromUBytes. + * + * Beginning with version 4, single-byte codepages have a significantly different + * trie compared to other codepages. + * In all cases, the entry in stage 1 is directly the index of the block of + * 64 entries in stage 2. + * + * Single-byte lookup: + * + * Stage 2 only contains 16-bit indexes directly to the 16-blocks in stage 3. + * Stage 3 contains one 16-bit word per result: + * Bits 15..8 indicate the kind of result: + * f roundtrip result + * c fallback result from private-use code point + * 8 fallback result from other code points + * 0 unassigned + * Bits 7..0 contain the codepage byte. A zero byte is always possible. + * + * In version 4.3, the runtime code can build an sbcsIndex for a utf8Friendly + * file. For 2-byte UTF-8 byte sequences and some 3-byte sequences the lookup + * becomes a 2-stage (single-index) trie lookup with 6 bits for stage 3. + * ASCII code points can be looked up with a linear array access into stage 3. + * See maxFastUChar and other details in ucnvmbcs.h. + * + * Multi-byte lookup: + * + * Stage 2 contains a 32-bit word for each 16-block in stage 3: + * Bits 31..16 contain flags for which stage 3 entries contain roundtrip results + * test: MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c) + * If this test is false, then a non-zero result will be interpreted as + * a fallback mapping. + * Bits 15..0 contain the index to stage 3, which must be multiplied by 16*(bytes per char) + * + * Stage 3 contains 2, 3, or 4 bytes per result. + * 2 or 4 bytes are stored as uint16_t/uint32_t in platform endianness, + * while 3 bytes are stored as bytes in big-endian order. + * Leading zero bytes are ignored, and the number of bytes is counted. + * A zero byte mapping result is possible as a roundtrip result. + * For some output types, the actual result is processed from this; + * see ucnv_MBCSFromUnicodeWithOffsets(). + * + * Note that stage 1 always contains 0x440=1088 entries (0x440==0x110000>>10), + * or (version 3 and up) for BMP-only codepages, it contains 64 entries. + * + * In version 4.3, a utf8Friendly file contains an mbcsIndex table. + * For 2-byte UTF-8 byte sequences and most 3-byte sequences the lookup + * becomes a 2-stage (single-index) trie lookup with 6 bits for stage 3. + * ASCII code points can be looked up with a linear array access into stage 3. + * See maxFastUChar, mbcsIndex and other details in ucnvmbcs.h. + * + * In version 3, stage 2 blocks may overlap by multiples of the multiplier + * for compaction. + * In version 4, stage 2 blocks (and for single-byte codepages, stage 3 blocks) + * may overlap by any number of entries. + * + * MBCS version 2 added: + * the converter checks for known output types, which allows + * adding new ones without crashing an unaware converter + */ + +/** + * Callback from ucnv_MBCSEnumToUnicode(), takes 32 mappings from + * consecutive sequences of bytes, starting from the one encoded in value, + * to Unicode code points. (Multiple mappings to reduce per-function call overhead.) + * Does not currently support m:n mappings or reverse fallbacks. + * This function will not be called for sequences of bytes with leading zeros. + * + * @param context an opaque pointer, as passed into ucnv_MBCSEnumToUnicode() + * @param value contains 1..4 bytes of the first byte sequence, right-aligned + * @param codePoints resulting Unicode code points, or negative if a byte sequence does + * not map to anything + * @return true to continue enumeration, false to stop + */ +typedef UBool U_CALLCONV +UConverterEnumToUCallback(const void *context, uint32_t value, UChar32 codePoints[32]); + +static void U_CALLCONV +ucnv_MBCSLoad(UConverterSharedData *sharedData, + UConverterLoadArgs *pArgs, + const uint8_t *raw, + UErrorCode *pErrorCode); + +static void U_CALLCONV +ucnv_MBCSUnload(UConverterSharedData *sharedData); + +static void U_CALLCONV +ucnv_MBCSOpen(UConverter *cnv, + UConverterLoadArgs *pArgs, + UErrorCode *pErrorCode); + +static UChar32 U_CALLCONV +ucnv_MBCSGetNextUChar(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode); + +static void U_CALLCONV +ucnv_MBCSGetStarters(const UConverter* cnv, + UBool starters[256], + UErrorCode *pErrorCode); + +U_CDECL_BEGIN +static const char* U_CALLCONV +ucnv_MBCSGetName(const UConverter *cnv); +U_CDECL_END + +static void U_CALLCONV +ucnv_MBCSWriteSub(UConverterFromUnicodeArgs *pArgs, + int32_t offsetIndex, + UErrorCode *pErrorCode); + +static UChar32 U_CALLCONV +ucnv_MBCSGetNextUChar(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode); + +static void U_CALLCONV +ucnv_SBCSFromUTF8(UConverterFromUnicodeArgs *pFromUArgs, + UConverterToUnicodeArgs *pToUArgs, + UErrorCode *pErrorCode); + +static void U_CALLCONV +ucnv_MBCSGetUnicodeSet(const UConverter *cnv, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode); + +static void U_CALLCONV +ucnv_DBCSFromUTF8(UConverterFromUnicodeArgs *pFromUArgs, + UConverterToUnicodeArgs *pToUArgs, + UErrorCode *pErrorCode); + +static const UConverterImpl _SBCSUTF8Impl={ + UCNV_MBCS, + + ucnv_MBCSLoad, + ucnv_MBCSUnload, + + ucnv_MBCSOpen, + nullptr, + nullptr, + + ucnv_MBCSToUnicodeWithOffsets, + ucnv_MBCSToUnicodeWithOffsets, + ucnv_MBCSFromUnicodeWithOffsets, + ucnv_MBCSFromUnicodeWithOffsets, + ucnv_MBCSGetNextUChar, + + ucnv_MBCSGetStarters, + ucnv_MBCSGetName, + ucnv_MBCSWriteSub, + nullptr, + ucnv_MBCSGetUnicodeSet, + + nullptr, + ucnv_SBCSFromUTF8 +}; + +static const UConverterImpl _DBCSUTF8Impl={ + UCNV_MBCS, + + ucnv_MBCSLoad, + ucnv_MBCSUnload, + + ucnv_MBCSOpen, + nullptr, + nullptr, + + ucnv_MBCSToUnicodeWithOffsets, + ucnv_MBCSToUnicodeWithOffsets, + ucnv_MBCSFromUnicodeWithOffsets, + ucnv_MBCSFromUnicodeWithOffsets, + ucnv_MBCSGetNextUChar, + + ucnv_MBCSGetStarters, + ucnv_MBCSGetName, + ucnv_MBCSWriteSub, + nullptr, + ucnv_MBCSGetUnicodeSet, + + nullptr, + ucnv_DBCSFromUTF8 +}; + +static const UConverterImpl _MBCSImpl={ + UCNV_MBCS, + + ucnv_MBCSLoad, + ucnv_MBCSUnload, + + ucnv_MBCSOpen, + nullptr, + nullptr, + + ucnv_MBCSToUnicodeWithOffsets, + ucnv_MBCSToUnicodeWithOffsets, + ucnv_MBCSFromUnicodeWithOffsets, + ucnv_MBCSFromUnicodeWithOffsets, + ucnv_MBCSGetNextUChar, + + ucnv_MBCSGetStarters, + ucnv_MBCSGetName, + ucnv_MBCSWriteSub, + nullptr, + ucnv_MBCSGetUnicodeSet, + nullptr, + nullptr +}; + +/* Static data is in tools/makeconv/ucnvstat.c for data-based + * converters. Be sure to update it as well. + */ + +const UConverterSharedData _MBCSData={ + sizeof(UConverterSharedData), 1, + nullptr, nullptr, false, true, &_MBCSImpl, + 0, UCNV_MBCS_TABLE_INITIALIZER +}; + + +/* GB 18030 data ------------------------------------------------------------ */ + +/* helper macros for linear values for GB 18030 four-byte sequences */ +#define LINEAR_18030(a, b, c, d) ((((a)*10+(b))*126L+(c))*10L+(d)) + +#define LINEAR_18030_BASE LINEAR_18030(0x81, 0x30, 0x81, 0x30) + +#define LINEAR(x) LINEAR_18030(x>>24, (x>>16)&0xff, (x>>8)&0xff, x&0xff) + +/* + * Some ranges of GB 18030 where both the Unicode code points and the + * GB four-byte sequences are contiguous and are handled algorithmically by + * the special callback functions below. + * The values are start & end of Unicode & GB codes. + * + * Note that single surrogates are not mapped by GB 18030 + * as of the re-released mapping tables from 2000-nov-30. + */ +static const uint32_t +gb18030Ranges[14][4]={ + {0x10000, 0x10FFFF, LINEAR(0x90308130), LINEAR(0xE3329A35)}, + {0x9FA6, 0xD7FF, LINEAR(0x82358F33), LINEAR(0x8336C738)}, + {0x0452, 0x1E3E, LINEAR(0x8130D330), LINEAR(0x8135F436)}, + {0x1E40, 0x200F, LINEAR(0x8135F438), LINEAR(0x8136A531)}, + {0xE865, 0xF92B, LINEAR(0x8336D030), LINEAR(0x84308534)}, + {0x2643, 0x2E80, LINEAR(0x8137A839), LINEAR(0x8138FD38)}, + {0xFA2A, 0xFE2F, LINEAR(0x84309C38), LINEAR(0x84318537)}, + {0x3CE1, 0x4055, LINEAR(0x8231D438), LINEAR(0x8232AF32)}, + {0x361B, 0x3917, LINEAR(0x8230A633), LINEAR(0x8230F237)}, + {0x49B8, 0x4C76, LINEAR(0x8234A131), LINEAR(0x8234E733)}, + {0x4160, 0x4336, LINEAR(0x8232C937), LINEAR(0x8232F837)}, + {0x478E, 0x4946, LINEAR(0x8233E838), LINEAR(0x82349638)}, + {0x44D7, 0x464B, LINEAR(0x8233A339), LINEAR(0x8233C931)}, + {0xFFE6, 0xFFFF, LINEAR(0x8431A234), LINEAR(0x8431A439)} +}; + +/* bit flag for UConverter.options indicating GB 18030 special handling */ +#define _MBCS_OPTION_GB18030 0x8000 + +/* bit flag for UConverter.options indicating KEIS,JEF,JIF special handling */ +#define _MBCS_OPTION_KEIS 0x01000 +#define _MBCS_OPTION_JEF 0x02000 +#define _MBCS_OPTION_JIPS 0x04000 + +#define KEIS_SO_CHAR_1 0x0A +#define KEIS_SO_CHAR_2 0x42 +#define KEIS_SI_CHAR_1 0x0A +#define KEIS_SI_CHAR_2 0x41 + +#define JEF_SO_CHAR 0x28 +#define JEF_SI_CHAR 0x29 + +#define JIPS_SO_CHAR_1 0x1A +#define JIPS_SO_CHAR_2 0x70 +#define JIPS_SI_CHAR_1 0x1A +#define JIPS_SI_CHAR_2 0x71 + +enum SISO_Option { + SI, + SO +}; +typedef enum SISO_Option SISO_Option; + +static int32_t getSISOBytes(SISO_Option option, uint32_t cnvOption, uint8_t *value) { + int32_t SISOLength = 0; + + switch (option) { + case SI: + if ((cnvOption&_MBCS_OPTION_KEIS)!=0) { + value[0] = KEIS_SI_CHAR_1; + value[1] = KEIS_SI_CHAR_2; + SISOLength = 2; + } else if ((cnvOption&_MBCS_OPTION_JEF)!=0) { + value[0] = JEF_SI_CHAR; + SISOLength = 1; + } else if ((cnvOption&_MBCS_OPTION_JIPS)!=0) { + value[0] = JIPS_SI_CHAR_1; + value[1] = JIPS_SI_CHAR_2; + SISOLength = 2; + } else { + value[0] = UCNV_SI; + SISOLength = 1; + } + break; + case SO: + if ((cnvOption&_MBCS_OPTION_KEIS)!=0) { + value[0] = KEIS_SO_CHAR_1; + value[1] = KEIS_SO_CHAR_2; + SISOLength = 2; + } else if ((cnvOption&_MBCS_OPTION_JEF)!=0) { + value[0] = JEF_SO_CHAR; + SISOLength = 1; + } else if ((cnvOption&_MBCS_OPTION_JIPS)!=0) { + value[0] = JIPS_SO_CHAR_1; + value[1] = JIPS_SO_CHAR_2; + SISOLength = 2; + } else { + value[0] = UCNV_SO; + SISOLength = 1; + } + break; + default: + /* Should never happen. */ + break; + } + + return SISOLength; +} + +/* Miscellaneous ------------------------------------------------------------ */ + +/* similar to ucnv_MBCSGetNextUChar() but recursive */ +static UBool +enumToU(UConverterMBCSTable *mbcsTable, int8_t stateProps[], + int32_t state, uint32_t offset, + uint32_t value, + UConverterEnumToUCallback *callback, const void *context, + UErrorCode *pErrorCode) { + UChar32 codePoints[32]; + const int32_t *row; + const uint16_t *unicodeCodeUnits; + UChar32 anyCodePoints; + int32_t b, limit; + + row=mbcsTable->stateTable[state]; + unicodeCodeUnits=mbcsTable->unicodeCodeUnits; + + value<<=8; + anyCodePoints=-1; /* becomes non-negative if there is a mapping */ + + b=(stateProps[state]&0x38)<<2; + if(b==0 && stateProps[state]>=0x40) { + /* skip byte sequences with leading zeros because they are not stored in the fromUnicode table */ + codePoints[0]=U_SENTINEL; + b=1; + } + limit=((stateProps[state]&7)+1)<<5; + while(b=0) { + /* recurse to a state with non-ignorable actions */ + if(!enumToU( + mbcsTable, stateProps, nextState, + offset+MBCS_ENTRY_TRANSITION_OFFSET(entry), + value|(uint32_t)b, + callback, context, + pErrorCode)) { + return false; + } + } + codePoints[b&0x1f]=U_SENTINEL; + } else { + UChar32 c; + int32_t action; + + /* + * An if-else-if chain provides more reliable performance for + * the most common cases compared to a switch. + */ + action=MBCS_ENTRY_FINAL_ACTION(entry); + if(action==MBCS_STATE_VALID_DIRECT_16) { + /* output BMP code point */ + c=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + } else if(action==MBCS_STATE_VALID_16) { + int32_t finalOffset=offset+MBCS_ENTRY_FINAL_VALUE_16(entry); + c=unicodeCodeUnits[finalOffset]; + if(c<0xfffe) { + /* output BMP code point */ + } else { + c=U_SENTINEL; + } + } else if(action==MBCS_STATE_VALID_16_PAIR) { + int32_t finalOffset=offset+MBCS_ENTRY_FINAL_VALUE_16(entry); + c=unicodeCodeUnits[finalOffset++]; + if(c<0xd800) { + /* output BMP code point below 0xd800 */ + } else if(c<=0xdbff) { + /* output roundtrip or fallback supplementary code point */ + c=((c&0x3ff)<<10)+unicodeCodeUnits[finalOffset]+(0x10000-0xdc00); + } else if(c==0xe000) { + /* output roundtrip BMP code point above 0xd800 or fallback BMP code point */ + c=unicodeCodeUnits[finalOffset]; + } else { + c=U_SENTINEL; + } + } else if(action==MBCS_STATE_VALID_DIRECT_20) { + /* output supplementary code point */ + c=(UChar32)(MBCS_ENTRY_FINAL_VALUE(entry)+0x10000); + } else { + c=U_SENTINEL; + } + + codePoints[b&0x1f]=c; + anyCodePoints&=c; + } + if(((++b)&0x1f)==0) { + if(anyCodePoints>=0) { + if(!callback(context, value|(uint32_t)(b-0x20), codePoints)) { + return false; + } + anyCodePoints=-1; + } + } + } + return true; +} + +/* + * Only called if stateProps[state]==-1. + * A recursive call may do stateProps[state]|=0x40 if this state is the target of an + * MBCS_STATE_CHANGE_ONLY. + */ +static int8_t +getStateProp(const int32_t (*stateTable)[256], int8_t stateProps[], int state) { + const int32_t *row; + int32_t min, max, entry, nextState; + + row=stateTable[state]; + stateProps[state]=0; + + /* find first non-ignorable state */ + for(min=0;; ++min) { + entry=row[min]; + nextState=MBCS_ENTRY_STATE(entry); + if(stateProps[nextState]==-1) { + getStateProp(stateTable, stateProps, nextState); + } + if(MBCS_ENTRY_IS_TRANSITION(entry)) { + if(stateProps[nextState]>=0) { + break; + } + } else if(MBCS_ENTRY_FINAL_ACTION(entry)>5)<<3); + + /* find last non-ignorable state */ + for(max=0xff; min=0) { + break; + } + } else if(MBCS_ENTRY_FINAL_ACTION(entry)>5); + + /* recurse further and collect direct-state information */ + while(min<=max) { + entry=row[min]; + nextState=MBCS_ENTRY_STATE(entry); + if(stateProps[nextState]==-1) { + getStateProp(stateTable, stateProps, nextState); + } + if(MBCS_ENTRY_IS_FINAL(entry)) { + stateProps[nextState]|=0x40; + if(MBCS_ENTRY_FINAL_ACTION(entry)<=MBCS_STATE_FALLBACK_DIRECT_20) { + stateProps[state]|=0x40; + } + } + ++min; + } + return stateProps[state]; +} + +/* + * Internal function enumerating the toUnicode data of an MBCS converter. + * Currently only used for reconstituting data for a MBCS_OPT_NO_FROM_U + * table, but could also be used for a future ucnv_getUnicodeSet() option + * that includes reverse fallbacks (after updating this function's implementation). + * Currently only handles roundtrip mappings. + * Does not currently handle extensions. + */ +static void +ucnv_MBCSEnumToUnicode(UConverterMBCSTable *mbcsTable, + UConverterEnumToUCallback *callback, const void *context, + UErrorCode *pErrorCode) { + /* + * Properties for each state, to speed up the enumeration. + * Ignorable actions are unassigned/illegal/state-change-only: + * They do not lead to mappings. + * + * Bits 7..6: + * 1 direct/initial state (stateful converters have multiple) + * 0 non-initial state with transitions or with non-ignorable result actions + * -1 final state with only ignorable actions + * + * Bits 5..3: + * The lowest byte value with non-ignorable actions is + * value<<5 (rounded down). + * + * Bits 2..0: + * The highest byte value with non-ignorable actions is + * (value<<5)&0x1f (rounded up). + */ + int8_t stateProps[MBCS_MAX_STATE_COUNT]; + int32_t state; + + uprv_memset(stateProps, -1, sizeof(stateProps)); + + /* recurse from state 0 and set all stateProps */ + getStateProp(mbcsTable->stateTable, stateProps, 0); + + for(state=0; statecountStates; ++state) { + /*if(stateProps[state]==-1) { + printf("unused/unreachable %d\n", state); + }*/ + if(stateProps[state]>=0x40) { + /* start from each direct state */ + enumToU( + mbcsTable, stateProps, state, 0, 0, + callback, context, + pErrorCode); + } + } +} + +U_CFUNC void +ucnv_MBCSGetFilteredUnicodeSetForUnicode(const UConverterSharedData *sharedData, + const USetAdder *sa, + UConverterUnicodeSet which, + UConverterSetFilter filter, + UErrorCode *pErrorCode) { + const UConverterMBCSTable *mbcsTable; + const uint16_t *table; + + uint32_t st3; + uint16_t st1, maxStage1, st2; + + UChar32 c; + + /* enumerate the from-Unicode trie table */ + mbcsTable=&sharedData->mbcs; + table=mbcsTable->fromUnicodeTable; + if(mbcsTable->unicodeMask&UCNV_HAS_SUPPLEMENTARY) { + maxStage1=0x440; + } else { + maxStage1=0x40; + } + + c=0; /* keep track of the current code point while enumerating */ + + if(mbcsTable->outputType==MBCS_OUTPUT_1) { + const uint16_t *stage2, *stage3, *results; + uint16_t minValue; + + results=(const uint16_t *)mbcsTable->fromUnicodeBytes; + + /* + * Set a threshold variable for selecting which mappings to use. + * See ucnv_MBCSSingleFromBMPWithOffsets() and + * MBCS_SINGLE_RESULT_FROM_U() for details. + */ + if(which==UCNV_ROUNDTRIP_SET) { + /* use only roundtrips */ + minValue=0xf00; + } else /* UCNV_ROUNDTRIP_AND_FALLBACK_SET */ { + /* use all roundtrip and fallback results */ + minValue=0x800; + } + + for(st1=0; st1maxStage1) { + stage2=table+st2; + for(st2=0; st2<64; ++st2) { + if((st3=stage2[st2])!=0) { + /* read the stage 3 block */ + stage3=results+st3; + + do { + if(*stage3++>=minValue) { + sa->add(sa->set, c); + } + } while((++c&0xf)!=0); + } else { + c+=16; /* empty stage 3 block */ + } + } + } else { + c+=1024; /* empty stage 2 block */ + } + } + } else { + const uint32_t *stage2; + const uint8_t *stage3, *bytes; + uint32_t st3Multiplier; + uint32_t value; + UBool useFallback; + + bytes=mbcsTable->fromUnicodeBytes; + + useFallback=(UBool)(which==UCNV_ROUNDTRIP_AND_FALLBACK_SET); + + switch(mbcsTable->outputType) { + case MBCS_OUTPUT_3: + case MBCS_OUTPUT_4_EUC: + st3Multiplier=3; + break; + case MBCS_OUTPUT_4: + st3Multiplier=4; + break; + default: + st3Multiplier=2; + break; + } + + for(st1=0; st1(maxStage1>>1)) { + stage2=(const uint32_t *)table+st2; + for(st2=0; st2<64; ++st2) { + if((st3=stage2[st2])!=0) { + /* read the stage 3 block */ + stage3=bytes+st3Multiplier*16*(uint32_t)(uint16_t)st3; + + /* get the roundtrip flags for the stage 3 block */ + st3>>=16; + + /* + * Add code points for which the roundtrip flag is set, + * or which map to non-zero bytes if we use fallbacks. + * See ucnv_MBCSFromUnicodeWithOffsets() for details. + */ + switch(filter) { + case UCNV_SET_FILTER_NONE: + do { + if(st3&1) { + sa->add(sa->set, c); + stage3+=st3Multiplier; + } else if(useFallback) { + uint8_t b=0; + switch(st3Multiplier) { + case 4: + b|=*stage3++; + U_FALLTHROUGH; + case 3: + b|=*stage3++; + U_FALLTHROUGH; + case 2: + b|=stage3[0]|stage3[1]; + stage3+=2; + U_FALLTHROUGH; + default: + break; + } + if(b!=0) { + sa->add(sa->set, c); + } + } + st3>>=1; + } while((++c&0xf)!=0); + break; + case UCNV_SET_FILTER_DBCS_ONLY: + /* Ignore single-byte results (<0x100). */ + do { + if(((st3&1)!=0 || useFallback) && *((const uint16_t *)stage3)>=0x100) { + sa->add(sa->set, c); + } + st3>>=1; + stage3+=2; /* +=st3Multiplier */ + } while((++c&0xf)!=0); + break; + case UCNV_SET_FILTER_2022_CN: + /* Only add code points that map to CNS 11643 planes 1 & 2 for non-EXT ISO-2022-CN. */ + do { + if(((st3&1)!=0 || useFallback) && ((value=*stage3)==0x81 || value==0x82)) { + sa->add(sa->set, c); + } + st3>>=1; + stage3+=3; /* +=st3Multiplier */ + } while((++c&0xf)!=0); + break; + case UCNV_SET_FILTER_SJIS: + /* Only add code points that map to Shift-JIS codes corresponding to JIS X 0208. */ + do { + if(((st3&1)!=0 || useFallback) && (value=*((const uint16_t *)stage3))>=0x8140 && value<=0xeffc) { + sa->add(sa->set, c); + } + st3>>=1; + stage3+=2; /* +=st3Multiplier */ + } while((++c&0xf)!=0); + break; + case UCNV_SET_FILTER_GR94DBCS: + /* Only add code points that map to ISO 2022 GR 94 DBCS codes (each byte A1..FE). */ + do { + if( ((st3&1)!=0 || useFallback) && + (uint16_t)((value=*((const uint16_t *)stage3)) - 0xa1a1)<=(0xfefe - 0xa1a1) && + (uint8_t)(value-0xa1)<=(0xfe - 0xa1) + ) { + sa->add(sa->set, c); + } + st3>>=1; + stage3+=2; /* +=st3Multiplier */ + } while((++c&0xf)!=0); + break; + case UCNV_SET_FILTER_HZ: + /* Only add code points that are suitable for HZ DBCS (lead byte A1..FD). */ + do { + if( ((st3&1)!=0 || useFallback) && + (uint16_t)((value=*((const uint16_t *)stage3))-0xa1a1)<=(0xfdfe - 0xa1a1) && + (uint8_t)(value-0xa1)<=(0xfe - 0xa1) + ) { + sa->add(sa->set, c); + } + st3>>=1; + stage3+=2; /* +=st3Multiplier */ + } while((++c&0xf)!=0); + break; + default: + *pErrorCode=U_INTERNAL_PROGRAM_ERROR; + return; + } + } else { + c+=16; /* empty stage 3 block */ + } + } + } else { + c+=1024; /* empty stage 2 block */ + } + } + } + + ucnv_extGetUnicodeSet(sharedData, sa, which, filter, pErrorCode); +} + +U_CFUNC void +ucnv_MBCSGetUnicodeSetForUnicode(const UConverterSharedData *sharedData, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode) { + ucnv_MBCSGetFilteredUnicodeSetForUnicode( + sharedData, sa, which, + sharedData->mbcs.outputType==MBCS_OUTPUT_DBCS_ONLY ? + UCNV_SET_FILTER_DBCS_ONLY : + UCNV_SET_FILTER_NONE, + pErrorCode); +} + +static void U_CALLCONV +ucnv_MBCSGetUnicodeSet(const UConverter *cnv, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode) { + if(cnv->options&_MBCS_OPTION_GB18030) { + sa->addRange(sa->set, 0, 0xd7ff); + sa->addRange(sa->set, 0xe000, 0x10ffff); + } else { + ucnv_MBCSGetUnicodeSetForUnicode(cnv->sharedData, sa, which, pErrorCode); + } +} + +/* conversion extensions for input not in the main table -------------------- */ + +/* + * Hardcoded extension handling for GB 18030. + * Definition of LINEAR macros and gb18030Ranges see near the beginning of the file. + * + * In the future, conversion extensions may handle m:n mappings and delta tables, + * see https://htmlpreview.github.io/?https://github.com/unicode-org/icu-docs/blob/main/design/conversion/conversion_extensions.html + * + * If an input character cannot be mapped, then these functions set an error + * code. The framework will then call the callback function. + */ + +/* + * @return if(U_FAILURE) return the code point for cnv->fromUChar32 + * else return 0 after output has been written to the target + */ +static UChar32 +_extFromU(UConverter *cnv, const UConverterSharedData *sharedData, + UChar32 cp, + const char16_t **source, const char16_t *sourceLimit, + uint8_t **target, const uint8_t *targetLimit, + int32_t **offsets, int32_t sourceIndex, + UBool flush, + UErrorCode *pErrorCode) { + const int32_t *cx; + + cnv->useSubChar1=false; + + if( (cx=sharedData->mbcs.extIndexes)!=nullptr && + ucnv_extInitialMatchFromU( + cnv, cx, + cp, source, sourceLimit, + (char **)target, (char *)targetLimit, + offsets, sourceIndex, + flush, + pErrorCode) + ) { + return 0; /* an extension mapping handled the input */ + } + + /* GB 18030 */ + if((cnv->options&_MBCS_OPTION_GB18030)!=0) { + const uint32_t *range; + int32_t i; + + range=gb18030Ranges[0]; + for(i=0; itoUBytes[0..length[ + * @return if(U_FAILURE) return the length (toULength, byteIndex) for the input + * else return 0 after output has been written to the target + */ +static int8_t +_extToU(UConverter *cnv, const UConverterSharedData *sharedData, + int8_t length, + const uint8_t **source, const uint8_t *sourceLimit, + char16_t **target, const char16_t *targetLimit, + int32_t **offsets, int32_t sourceIndex, + UBool flush, + UErrorCode *pErrorCode) { + const int32_t *cx; + + if( (cx=sharedData->mbcs.extIndexes)!=nullptr && + ucnv_extInitialMatchToU( + cnv, cx, + length, (const char **)source, (const char *)sourceLimit, + target, targetLimit, + offsets, sourceIndex, + flush, + pErrorCode) + ) { + return 0; /* an extension mapping handled the input */ + } + + /* GB 18030 */ + if(length==4 && (cnv->options&_MBCS_OPTION_GB18030)!=0) { + const uint32_t *range; + uint32_t linear; + int32_t i; + + linear=LINEAR_18030(cnv->toUBytes[0], cnv->toUBytes[1], cnv->toUBytes[2], cnv->toUBytes[3]); + range=gb18030Ranges[0]; + for(i=0; iNL ------------------------------------------------------ */ + +/* + * This code modifies a standard EBCDIC<->Unicode mapping table for + * OS/390 (z/OS) Unix System Services (Open Edition). + * The difference is in the mapping of Line Feed and New Line control codes: + * Standard EBCDIC maps + * + * \x25 |0 + * \x15 |0 + * + * but OS/390 USS EBCDIC swaps the control codes for LF and NL, + * mapping + * + * \x15 |0 + * \x25 |0 + * + * This code modifies a loaded standard EBCDIC<->Unicode mapping table + * by copying it into allocated memory and swapping the LF and NL values. + * It allows to support the same EBCDIC charset in both versions without + * duplicating the entire installed table. + */ + +/* standard EBCDIC codes */ +#define EBCDIC_LF 0x25 +#define EBCDIC_NL 0x15 + +/* standard EBCDIC codes with roundtrip flag as stored in Unicode-to-single-byte tables */ +#define EBCDIC_RT_LF 0xf25 +#define EBCDIC_RT_NL 0xf15 + +/* Unicode code points */ +#define U_LF 0x0a +#define U_NL 0x85 + +static UBool +_EBCDICSwapLFNL(UConverterSharedData *sharedData, UErrorCode *pErrorCode) { + UConverterMBCSTable *mbcsTable; + + const uint16_t *table, *results; + const uint8_t *bytes; + + int32_t (*newStateTable)[256]; + uint16_t *newResults; + uint8_t *p; + char *name; + + uint32_t stage2Entry; + uint32_t size, sizeofFromUBytes; + + mbcsTable=&sharedData->mbcs; + + table=mbcsTable->fromUnicodeTable; + bytes=mbcsTable->fromUnicodeBytes; + results=(const uint16_t *)bytes; + + /* + * Check that this is an EBCDIC table with SBCS portion - + * SBCS or EBCDIC_STATEFUL with standard EBCDIC LF and NL mappings. + * + * If not, ignore the option. Options are always ignored if they do not apply. + */ + if(!( + (mbcsTable->outputType==MBCS_OUTPUT_1 || mbcsTable->outputType==MBCS_OUTPUT_2_SISO) && + mbcsTable->stateTable[0][EBCDIC_LF]==MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, U_LF) && + mbcsTable->stateTable[0][EBCDIC_NL]==MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, U_NL) + )) { + return false; + } + + if(mbcsTable->outputType==MBCS_OUTPUT_1) { + if(!( + EBCDIC_RT_LF==MBCS_SINGLE_RESULT_FROM_U(table, results, U_LF) && + EBCDIC_RT_NL==MBCS_SINGLE_RESULT_FROM_U(table, results, U_NL) + )) { + return false; + } + } else /* MBCS_OUTPUT_2_SISO */ { + stage2Entry=MBCS_STAGE_2_FROM_U(table, U_LF); + if(!( + MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, U_LF)!=0 && + EBCDIC_LF==MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, U_LF) + )) { + return false; + } + + stage2Entry=MBCS_STAGE_2_FROM_U(table, U_NL); + if(!( + MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, U_NL)!=0 && + EBCDIC_NL==MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, U_NL) + )) { + return false; + } + } + + if(mbcsTable->fromUBytesLength>0) { + /* + * We _know_ the number of bytes in the fromUnicodeBytes array + * starting with header.version 4.1. + */ + sizeofFromUBytes=mbcsTable->fromUBytesLength; + } else { + /* + * Otherwise: + * There used to be code to enumerate the fromUnicode + * trie and find the highest entry, but it was removed in ICU 3.2 + * because it was not tested and caused a low code coverage number. + * See Jitterbug 3674. + * This affects only some .cnv file formats with a header.version + * below 4.1, and only when swaplfnl is requested. + * + * ucnvmbcs.c revision 1.99 is the last one with the + * ucnv_MBCSSizeofFromUBytes() function. + */ + *pErrorCode=U_INVALID_FORMAT_ERROR; + return false; + } + + /* + * The table has an appropriate format. + * Allocate and build + * - a modified to-Unicode state table + * - a modified from-Unicode output array + * - a converter name string with the swap option appended + */ + size= + mbcsTable->countStates*1024+ + sizeofFromUBytes+ + UCNV_MAX_CONVERTER_NAME_LENGTH+20; + p=(uint8_t *)uprv_malloc(size); + if(p==nullptr) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return false; + } + + /* copy and modify the to-Unicode state table */ + newStateTable=(int32_t (*)[256])p; + uprv_memcpy(newStateTable, mbcsTable->stateTable, mbcsTable->countStates*1024); + + newStateTable[0][EBCDIC_LF]=MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, U_NL); + newStateTable[0][EBCDIC_NL]=MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, U_LF); + + /* copy and modify the from-Unicode result table */ + newResults=(uint16_t *)newStateTable[mbcsTable->countStates]; + uprv_memcpy(newResults, bytes, sizeofFromUBytes); + + /* conveniently, the table access macros work on the left side of expressions */ + if(mbcsTable->outputType==MBCS_OUTPUT_1) { + MBCS_SINGLE_RESULT_FROM_U(table, newResults, U_LF)=EBCDIC_RT_NL; + MBCS_SINGLE_RESULT_FROM_U(table, newResults, U_NL)=EBCDIC_RT_LF; + } else /* MBCS_OUTPUT_2_SISO */ { + stage2Entry=MBCS_STAGE_2_FROM_U(table, U_LF); + MBCS_VALUE_2_FROM_STAGE_2(newResults, stage2Entry, U_LF)=EBCDIC_NL; + + stage2Entry=MBCS_STAGE_2_FROM_U(table, U_NL); + MBCS_VALUE_2_FROM_STAGE_2(newResults, stage2Entry, U_NL)=EBCDIC_LF; + } + + /* set the canonical converter name */ + name=(char *)newResults+sizeofFromUBytes; + uprv_strcpy(name, sharedData->staticData->name); + uprv_strcat(name, UCNV_SWAP_LFNL_OPTION_STRING); + + /* set the pointers */ + icu::umtx_lock(nullptr); + if(mbcsTable->swapLFNLStateTable==nullptr) { + mbcsTable->swapLFNLStateTable=newStateTable; + mbcsTable->swapLFNLFromUnicodeBytes=(uint8_t *)newResults; + mbcsTable->swapLFNLName=name; + + newStateTable=nullptr; + } + icu::umtx_unlock(nullptr); + + /* release the allocated memory if another thread beat us to it */ + if(newStateTable!=nullptr) { + uprv_free(newStateTable); + } + return true; +} + +/* reconstitute omitted fromUnicode data ------------------------------------ */ + +/* for details, compare with genmbcs.c MBCSAddFromUnicode() and transformEUC() */ +static UBool U_CALLCONV +writeStage3Roundtrip(const void *context, uint32_t value, UChar32 codePoints[32]) { + UConverterMBCSTable *mbcsTable=(UConverterMBCSTable *)context; + const uint16_t *table; + uint32_t *stage2; + uint8_t *bytes, *p; + UChar32 c; + int32_t i, st3; + + table=mbcsTable->fromUnicodeTable; + bytes=(uint8_t *)mbcsTable->fromUnicodeBytes; + + /* for EUC outputTypes, modify the value like genmbcs.c's transformEUC() */ + switch(mbcsTable->outputType) { + case MBCS_OUTPUT_3_EUC: + if(value<=0xffff) { + /* short sequences are stored directly */ + /* code set 0 or 1 */ + } else if(value<=0x8effff) { + /* code set 2 */ + value&=0x7fff; + } else /* first byte is 0x8f */ { + /* code set 3 */ + value&=0xff7f; + } + break; + case MBCS_OUTPUT_4_EUC: + if(value<=0xffffff) { + /* short sequences are stored directly */ + /* code set 0 or 1 */ + } else if(value<=0x8effffff) { + /* code set 2 */ + value&=0x7fffff; + } else /* first byte is 0x8f */ { + /* code set 3 */ + value&=0xff7fff; + } + break; + default: + break; + } + + for(i=0; i<=0x1f; ++value, ++i) { + c=codePoints[i]; + if(c<0) { + continue; + } + + /* locate the stage 2 & 3 data */ + stage2=((uint32_t *)table)+table[c>>10]+((c>>4)&0x3f); + p=bytes; + st3=(int32_t)(uint16_t)*stage2*16+(c&0xf); + + /* write the codepage bytes into stage 3 */ + switch(mbcsTable->outputType) { + case MBCS_OUTPUT_3: + case MBCS_OUTPUT_4_EUC: + p+=st3*3; + p[0]=(uint8_t)(value>>16); + p[1]=(uint8_t)(value>>8); + p[2]=(uint8_t)value; + break; + case MBCS_OUTPUT_4: + ((uint32_t *)p)[st3]=value; + break; + default: + /* 2 bytes per character */ + ((uint16_t *)p)[st3]=(uint16_t)value; + break; + } + + /* set the roundtrip flag */ + *stage2|=(1UL<<(16+(c&0xf))); + } + return true; + } + +static void +reconstituteData(UConverterMBCSTable *mbcsTable, + uint32_t stage1Length, uint32_t stage2Length, + uint32_t fullStage2Length, /* lengths are numbers of units, not bytes */ + UErrorCode *pErrorCode) { + uint16_t *stage1; + uint32_t *stage2; + uint32_t dataLength=stage1Length*2+fullStage2Length*4+mbcsTable->fromUBytesLength; + mbcsTable->reconstitutedData=(uint8_t *)uprv_malloc(dataLength); + if(mbcsTable->reconstitutedData==nullptr) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_memset(mbcsTable->reconstitutedData, 0, dataLength); + + /* copy existing data and reroute the pointers */ + stage1=(uint16_t *)mbcsTable->reconstitutedData; + uprv_memcpy(stage1, mbcsTable->fromUnicodeTable, stage1Length*2); + + stage2=(uint32_t *)(stage1+stage1Length); + uprv_memcpy(stage2+(fullStage2Length-stage2Length), + mbcsTable->fromUnicodeTable+stage1Length, + stage2Length*4); + + mbcsTable->fromUnicodeTable=stage1; + mbcsTable->fromUnicodeBytes=(uint8_t *)(stage2+fullStage2Length); + + /* indexes into stage 2 count from the bottom of the fromUnicodeTable */ + stage2=(uint32_t *)stage1; + + /* reconstitute the initial part of stage 2 from the mbcsIndex */ + { + int32_t stageUTF8Length=((int32_t)mbcsTable->maxFastUChar+1)>>6; + int32_t stageUTF8Index=0; + int32_t st1, st2, st3, i; + + for(st1=0; stageUTF8IndexmbcsIndex[stageUTF8Index++]; + if(st3!=0) { + /* an stage 2 entry's index is per stage 3 16-block, not per stage 3 entry */ + st3>>=4; + /* + * 4 stage 2 entries point to 4 consecutive stage 3 16-blocks which are + * allocated together as a single 64-block for access from the mbcsIndex + */ + stage2[st2++]=st3++; + stage2[st2++]=st3++; + stage2[st2++]=st3++; + stage2[st2++]=st3; + } else { + /* no stage 3 block, skip */ + st2+=4; + } + } + } else { + /* no stage 2 block, skip */ + stageUTF8Index+=16; + } + } + } + + /* reconstitute fromUnicodeBytes with roundtrips from toUnicode data */ + ucnv_MBCSEnumToUnicode(mbcsTable, writeStage3Roundtrip, mbcsTable, pErrorCode); +} + +/* MBCS setup functions ----------------------------------------------------- */ + +static void U_CALLCONV +ucnv_MBCSLoad(UConverterSharedData *sharedData, + UConverterLoadArgs *pArgs, + const uint8_t *raw, + UErrorCode *pErrorCode) { + UDataInfo info; + UConverterMBCSTable *mbcsTable=&sharedData->mbcs; + _MBCSHeader *header=(_MBCSHeader *)raw; + uint32_t offset; + uint32_t headerLength; + UBool noFromU=false; + + if(header->version[0]==4) { + headerLength=MBCS_HEADER_V4_LENGTH; + } else if(header->version[0]==5 && header->version[1]>=3 && + (header->options&MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK)==0) { + headerLength=header->options&MBCS_OPT_LENGTH_MASK; + noFromU=(UBool)((header->options&MBCS_OPT_NO_FROM_U)!=0); + } else { + *pErrorCode=U_INVALID_TABLE_FORMAT; + return; + } + + mbcsTable->outputType=(uint8_t)header->flags; + if(noFromU && mbcsTable->outputType==MBCS_OUTPUT_1) { + *pErrorCode=U_INVALID_TABLE_FORMAT; + return; + } + + /* extension data, header version 4.2 and higher */ + offset=header->flags>>8; + if(offset!=0) { + mbcsTable->extIndexes=(const int32_t *)(raw+offset); + } + + if(mbcsTable->outputType==MBCS_OUTPUT_EXT_ONLY) { + UConverterLoadArgs args=UCNV_LOAD_ARGS_INITIALIZER; + UConverterSharedData *baseSharedData; + const int32_t *extIndexes; + const char *baseName; + + /* extension-only file, load the base table and set values appropriately */ + if((extIndexes=mbcsTable->extIndexes)==nullptr) { + /* extension-only file without extension */ + *pErrorCode=U_INVALID_TABLE_FORMAT; + return; + } + + if(pArgs->nestedLoads!=1) { + /* an extension table must not be loaded as a base table */ + *pErrorCode=U_INVALID_TABLE_FILE; + return; + } + + /* load the base table */ + baseName=(const char *)header+headerLength*4; + if(0==uprv_strcmp(baseName, sharedData->staticData->name)) { + /* forbid loading this same extension-only file */ + *pErrorCode=U_INVALID_TABLE_FORMAT; + return; + } + + /* TODO parse package name out of the prefix of the base name in the extension .cnv file? */ + args.size=sizeof(UConverterLoadArgs); + args.nestedLoads=2; + args.onlyTestIsLoadable=pArgs->onlyTestIsLoadable; + args.reserved=pArgs->reserved; + args.options=pArgs->options; + args.pkg=pArgs->pkg; + args.name=baseName; + baseSharedData=ucnv_load(&args, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return; + } + if( baseSharedData->staticData->conversionType!=UCNV_MBCS || + baseSharedData->mbcs.baseSharedData!=nullptr + ) { + ucnv_unload(baseSharedData); + *pErrorCode=U_INVALID_TABLE_FORMAT; + return; + } + if(pArgs->onlyTestIsLoadable) { + /* + * Exit as soon as we know that we can load the converter + * and the format is valid and supported. + * The worst that can happen in the following code is a memory + * allocation error. + */ + ucnv_unload(baseSharedData); + return; + } + + /* copy the base table data */ + uprv_memcpy(mbcsTable, &baseSharedData->mbcs, sizeof(UConverterMBCSTable)); + + /* overwrite values with relevant ones for the extension converter */ + mbcsTable->baseSharedData=baseSharedData; + mbcsTable->extIndexes=extIndexes; + + /* + * It would be possible to share the swapLFNL data with a base converter, + * but the generated name would have to be different, and the memory + * would have to be free'd only once. + * It is easier to just create the data for the extension converter + * separately when it is requested. + */ + mbcsTable->swapLFNLStateTable=nullptr; + mbcsTable->swapLFNLFromUnicodeBytes=nullptr; + mbcsTable->swapLFNLName=nullptr; + + /* + * The reconstitutedData must be deleted only when the base converter + * is unloaded. + */ + mbcsTable->reconstitutedData=nullptr; + + /* + * Set a special, runtime-only outputType if the extension converter + * is a DBCS version of a base converter that also maps single bytes. + */ + if( sharedData->staticData->conversionType==UCNV_DBCS || + (sharedData->staticData->conversionType==UCNV_MBCS && + sharedData->staticData->minBytesPerChar>=2) + ) { + if(baseSharedData->mbcs.outputType==MBCS_OUTPUT_2_SISO) { + /* the base converter is SI/SO-stateful */ + int32_t entry; + + /* get the dbcs state from the state table entry for SO=0x0e */ + entry=mbcsTable->stateTable[0][0xe]; + if( MBCS_ENTRY_IS_FINAL(entry) && + MBCS_ENTRY_FINAL_ACTION(entry)==MBCS_STATE_CHANGE_ONLY && + MBCS_ENTRY_FINAL_STATE(entry)!=0 + ) { + mbcsTable->dbcsOnlyState=(uint8_t)MBCS_ENTRY_FINAL_STATE(entry); + + mbcsTable->outputType=MBCS_OUTPUT_DBCS_ONLY; + } + } else if( + baseSharedData->staticData->conversionType==UCNV_MBCS && + baseSharedData->staticData->minBytesPerChar==1 && + baseSharedData->staticData->maxBytesPerChar==2 && + mbcsTable->countStates<=127 + ) { + /* non-stateful base converter, need to modify the state table */ + int32_t (*newStateTable)[256]; + int32_t *state; + int32_t i, count; + + /* allocate a new state table and copy the base state table contents */ + count=mbcsTable->countStates; + newStateTable=(int32_t (*)[256])uprv_malloc((count+1)*1024); + if(newStateTable==nullptr) { + ucnv_unload(baseSharedData); + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + + uprv_memcpy(newStateTable, mbcsTable->stateTable, count*1024); + + /* change all final single-byte entries to go to a new all-illegal state */ + state=newStateTable[0]; + for(i=0; i<256; ++i) { + if(MBCS_ENTRY_IS_FINAL(state[i])) { + state[i]=MBCS_ENTRY_TRANSITION(count, 0); + } + } + + /* build the new all-illegal state */ + state=newStateTable[count]; + for(i=0; i<256; ++i) { + state[i]=MBCS_ENTRY_FINAL(0, MBCS_STATE_ILLEGAL, 0); + } + mbcsTable->stateTable=(const int32_t (*)[256])newStateTable; + mbcsTable->countStates=(uint8_t)(count+1); + mbcsTable->stateTableOwned=true; + + mbcsTable->outputType=MBCS_OUTPUT_DBCS_ONLY; + } + } + + /* + * unlike below for files with base tables, do not get the unicodeMask + * from the sharedData; instead, use the base table's unicodeMask, + * which we copied in the memcpy above; + * this is necessary because the static data unicodeMask, especially + * the UCNV_HAS_SUPPLEMENTARY flag, is part of the base table data + */ + } else { + /* conversion file with a base table; an additional extension table is optional */ + /* make sure that the output type is known */ + switch(mbcsTable->outputType) { + case MBCS_OUTPUT_1: + case MBCS_OUTPUT_2: + case MBCS_OUTPUT_3: + case MBCS_OUTPUT_4: + case MBCS_OUTPUT_3_EUC: + case MBCS_OUTPUT_4_EUC: + case MBCS_OUTPUT_2_SISO: + /* OK */ + break; + default: + *pErrorCode=U_INVALID_TABLE_FORMAT; + return; + } + if(pArgs->onlyTestIsLoadable) { + /* + * Exit as soon as we know that we can load the converter + * and the format is valid and supported. + * The worst that can happen in the following code is a memory + * allocation error. + */ + return; + } + + mbcsTable->countStates=(uint8_t)header->countStates; + mbcsTable->countToUFallbacks=header->countToUFallbacks; + mbcsTable->stateTable=(const int32_t (*)[256])(raw+headerLength*4); + mbcsTable->toUFallbacks=(const _MBCSToUFallback *)(mbcsTable->stateTable+header->countStates); + mbcsTable->unicodeCodeUnits=(const uint16_t *)(raw+header->offsetToUCodeUnits); + + mbcsTable->fromUnicodeTable=(const uint16_t *)(raw+header->offsetFromUTable); + mbcsTable->fromUnicodeBytes=(const uint8_t *)(raw+header->offsetFromUBytes); + mbcsTable->fromUBytesLength=header->fromUBytesLength; + + /* + * converter versions 6.1 and up contain a unicodeMask that is + * used here to select the most efficient function implementations + */ + info.size=sizeof(UDataInfo); + udata_getInfo((UDataMemory *)sharedData->dataMemory, &info); + if(info.formatVersion[0]>6 || (info.formatVersion[0]==6 && info.formatVersion[1]>=1)) { + /* mask off possible future extensions to be safe */ + mbcsTable->unicodeMask=(uint8_t)(sharedData->staticData->unicodeMask&3); + } else { + /* for older versions, assume worst case: contains anything possible (prevent over-optimizations) */ + mbcsTable->unicodeMask=UCNV_HAS_SUPPLEMENTARY|UCNV_HAS_SURROGATES; + } + + /* + * _MBCSHeader.version 4.3 adds utf8Friendly data structures. + * Check for the header version, SBCS vs. MBCS, and for whether the + * data structures are optimized for code points as high as what the + * runtime code is designed for. + * The implementation does not handle mapping tables with entries for + * unpaired surrogates. + */ + if( header->version[1]>=3 && + (mbcsTable->unicodeMask&UCNV_HAS_SURROGATES)==0 && + (mbcsTable->countStates==1 ? + (header->version[2]>=(SBCS_FAST_MAX>>8)) : + (header->version[2]>=(MBCS_FAST_MAX>>8)) + ) + ) { + mbcsTable->utf8Friendly=true; + + if(mbcsTable->countStates==1) { + /* + * SBCS: Stage 3 is allocated in 64-entry blocks for U+0000..SBCS_FAST_MAX or higher. + * Build a table with indexes to each block, to be used instead of + * the regular stage 1/2 table. + */ + int32_t i; + for(i=0; i<(SBCS_FAST_LIMIT>>6); ++i) { + mbcsTable->sbcsIndex[i]=mbcsTable->fromUnicodeTable[mbcsTable->fromUnicodeTable[i>>4]+((i<<2)&0x3c)]; + } + /* set SBCS_FAST_MAX to reflect the reach of sbcsIndex[] even if header->version[2]>(SBCS_FAST_MAX>>8) */ + mbcsTable->maxFastUChar=SBCS_FAST_MAX; + } else { + /* + * MBCS: Stage 3 is allocated in 64-entry blocks for U+0000..MBCS_FAST_MAX or higher. + * The .cnv file is prebuilt with an additional stage table with indexes + * to each block. + */ + mbcsTable->mbcsIndex=(const uint16_t *) + (mbcsTable->fromUnicodeBytes+ + (noFromU ? 0 : mbcsTable->fromUBytesLength)); + mbcsTable->maxFastUChar=(((char16_t)header->version[2])<<8)|0xff; + } + } + + /* calculate a bit set of 4 ASCII characters per bit that round-trip to ASCII bytes */ + { + uint32_t asciiRoundtrips=0xffffffff; + int32_t i; + + for(i=0; i<0x80; ++i) { + if(mbcsTable->stateTable[0][i]!=MBCS_ENTRY_FINAL(0, MBCS_STATE_VALID_DIRECT_16, i)) { + asciiRoundtrips&=~((uint32_t)1<<(i>>2)); + } + } + mbcsTable->asciiRoundtrips=asciiRoundtrips; + } + + if(noFromU) { + uint32_t stage1Length= + mbcsTable->unicodeMask&UCNV_HAS_SUPPLEMENTARY ? + 0x440 : 0x40; + uint32_t stage2Length= + (header->offsetFromUBytes-header->offsetFromUTable)/4- + stage1Length/2; + reconstituteData(mbcsTable, stage1Length, stage2Length, header->fullStage2Length, pErrorCode); + } + } + + /* Set the impl pointer here so that it is set for both extension-only and base tables. */ + if(mbcsTable->utf8Friendly) { + if(mbcsTable->countStates==1) { + sharedData->impl=&_SBCSUTF8Impl; + } else { + if(mbcsTable->outputType==MBCS_OUTPUT_2) { + sharedData->impl=&_DBCSUTF8Impl; + } + } + } + + if(mbcsTable->outputType==MBCS_OUTPUT_DBCS_ONLY || mbcsTable->outputType==MBCS_OUTPUT_2_SISO) { + /* + * MBCS_OUTPUT_DBCS_ONLY: No SBCS mappings, therefore ASCII does not roundtrip. + * MBCS_OUTPUT_2_SISO: Bypass the ASCII fastpath to handle prevLength correctly. + */ + mbcsTable->asciiRoundtrips=0; + } +} + +static void U_CALLCONV +ucnv_MBCSUnload(UConverterSharedData *sharedData) { + UConverterMBCSTable *mbcsTable=&sharedData->mbcs; + + if(mbcsTable->swapLFNLStateTable!=nullptr) { + uprv_free(mbcsTable->swapLFNLStateTable); + } + if(mbcsTable->stateTableOwned) { + uprv_free((void *)mbcsTable->stateTable); + } + if(mbcsTable->baseSharedData!=nullptr) { + ucnv_unload(mbcsTable->baseSharedData); + } + if(mbcsTable->reconstitutedData!=nullptr) { + uprv_free(mbcsTable->reconstitutedData); + } +} + +static void U_CALLCONV +ucnv_MBCSOpen(UConverter *cnv, + UConverterLoadArgs *pArgs, + UErrorCode *pErrorCode) { + UConverterMBCSTable *mbcsTable; + const int32_t *extIndexes; + uint8_t outputType; + int8_t maxBytesPerUChar; + + if(pArgs->onlyTestIsLoadable) { + return; + } + + mbcsTable=&cnv->sharedData->mbcs; + outputType=mbcsTable->outputType; + + if(outputType==MBCS_OUTPUT_DBCS_ONLY) { + /* the swaplfnl option does not apply, remove it */ + cnv->options=pArgs->options&=~UCNV_OPTION_SWAP_LFNL; + } + + if((pArgs->options&UCNV_OPTION_SWAP_LFNL)!=0) { + /* do this because double-checked locking is broken */ + UBool isCached; + + icu::umtx_lock(nullptr); + isCached=mbcsTable->swapLFNLStateTable!=nullptr; + icu::umtx_unlock(nullptr); + + if(!isCached) { + if(!_EBCDICSwapLFNL(cnv->sharedData, pErrorCode)) { + if(U_FAILURE(*pErrorCode)) { + return; /* something went wrong */ + } + + /* the option does not apply, remove it */ + cnv->options=pArgs->options&=~UCNV_OPTION_SWAP_LFNL; + } + } + } + + if(uprv_strstr(pArgs->name, "18030")!=nullptr) { + if(uprv_strstr(pArgs->name, "gb18030")!=nullptr || uprv_strstr(pArgs->name, "GB18030")!=nullptr) { + /* set a flag for GB 18030 mode, which changes the callback behavior */ + cnv->options|=_MBCS_OPTION_GB18030; + } + } else if((uprv_strstr(pArgs->name, "KEIS")!=nullptr) || (uprv_strstr(pArgs->name, "keis")!=nullptr)) { + /* set a flag for KEIS converter, which changes the SI/SO character sequence */ + cnv->options|=_MBCS_OPTION_KEIS; + } else if((uprv_strstr(pArgs->name, "JEF")!=nullptr) || (uprv_strstr(pArgs->name, "jef")!=nullptr)) { + /* set a flag for JEF converter, which changes the SI/SO character sequence */ + cnv->options|=_MBCS_OPTION_JEF; + } else if((uprv_strstr(pArgs->name, "JIPS")!=nullptr) || (uprv_strstr(pArgs->name, "jips")!=nullptr)) { + /* set a flag for JIPS converter, which changes the SI/SO character sequence */ + cnv->options|=_MBCS_OPTION_JIPS; + } + + /* fix maxBytesPerUChar depending on outputType and options etc. */ + if(outputType==MBCS_OUTPUT_2_SISO) { + cnv->maxBytesPerUChar=3; /* SO+DBCS */ + } + + extIndexes=mbcsTable->extIndexes; + if(extIndexes!=nullptr) { + maxBytesPerUChar=(int8_t)UCNV_GET_MAX_BYTES_PER_UCHAR(extIndexes); + if(outputType==MBCS_OUTPUT_2_SISO) { + ++maxBytesPerUChar; /* SO + multiple DBCS */ + } + + if(maxBytesPerUChar>cnv->maxBytesPerUChar) { + cnv->maxBytesPerUChar=maxBytesPerUChar; + } + } + +#if 0 + /* + * documentation of UConverter fields used for status + * all of these fields are (re)set to 0 by ucnv_bld.c and ucnv_reset() + */ + + /* toUnicode */ + cnv->toUnicodeStatus=0; /* offset */ + cnv->mode=0; /* state */ + cnv->toULength=0; /* byteIndex */ + + /* fromUnicode */ + cnv->fromUChar32=0; + cnv->fromUnicodeStatus=1; /* prevLength */ +#endif +} + +U_CDECL_BEGIN + +static const char* U_CALLCONV +ucnv_MBCSGetName(const UConverter *cnv) { + if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0 && cnv->sharedData->mbcs.swapLFNLName!=nullptr) { + return cnv->sharedData->mbcs.swapLFNLName; + } else { + return cnv->sharedData->staticData->name; + } +} +U_CDECL_END + + +/* MBCS-to-Unicode conversion functions ------------------------------------- */ + +static UChar32 U_CALLCONV +ucnv_MBCSGetFallback(UConverterMBCSTable *mbcsTable, uint32_t offset) { + const _MBCSToUFallback *toUFallbacks; + uint32_t i, start, limit; + + limit=mbcsTable->countToUFallbacks; + if(limit>0) { + /* do a binary search for the fallback mapping */ + toUFallbacks=mbcsTable->toUFallbacks; + start=0; + while(startconverter; + source=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + target=pArgs->target; + targetLimit=pArgs->targetLimit; + offsets=pArgs->offsets; + + if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { + stateTable=(const int32_t (*)[256])cnv->sharedData->mbcs.swapLFNLStateTable; + } else { + stateTable=cnv->sharedData->mbcs.stateTable; + } + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex=0; + + /* conversion loop */ + while(source=targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + + entry=stateTable[0][*source++]; + /* MBCS_ENTRY_IS_FINAL(entry) */ + + /* test the most common case first */ + if(MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(entry)) { + /* output BMP code point */ + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + + /* normal end of action codes: prepare for a new character */ + ++sourceIndex; + continue; + } + + /* + * An if-else-if chain provides more reliable performance for + * the most common cases compared to a switch. + */ + action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); + if(action==MBCS_STATE_VALID_DIRECT_20 || + (action==MBCS_STATE_FALLBACK_DIRECT_20 && UCNV_TO_U_USE_FALLBACK(cnv)) + ) { + entry=MBCS_ENTRY_FINAL_VALUE(entry); + /* output surrogate pair */ + *target++=(char16_t)(0xd800|(char16_t)(entry>>10)); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + c=(char16_t)(0xdc00|(char16_t)(entry&0x3ff)); + if(targetUCharErrorBuffer[0]=c; + cnv->UCharErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + + ++sourceIndex; + continue; + } else if(action==MBCS_STATE_FALLBACK_DIRECT_16) { + if(UCNV_TO_U_USE_FALLBACK(cnv)) { + /* output BMP code point */ + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + + ++sourceIndex; + continue; + } + } else if(action==MBCS_STATE_UNASSIGNED) { + /* just fall through */ + } else if(action==MBCS_STATE_ILLEGAL) { + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } else { + /* reserved, must never occur */ + ++sourceIndex; + continue; + } + + if(U_FAILURE(*pErrorCode)) { + /* callback(illegal) */ + break; + } else /* unassigned sequences indicated with byteIndex>0 */ { + /* try an extension mapping */ + pArgs->source=(const char *)source; + cnv->toUBytes[0]=*(source-1); + cnv->toULength=_extToU(cnv, cnv->sharedData, + 1, &source, sourceLimit, + &target, targetLimit, + &offsets, sourceIndex, + pArgs->flush, + pErrorCode); + sourceIndex+=1+(int32_t)(source-(const uint8_t *)pArgs->source); + + if(U_FAILURE(*pErrorCode)) { + /* not mappable or buffer overflow */ + break; + } + } + } + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + pArgs->offsets=offsets; +} + +/* + * This version of ucnv_MBCSSingleToUnicodeWithOffsets() is optimized for single-byte, single-state codepages + * that only map to and from the BMP. + * In addition to single-byte optimizations, the offset calculations + * become much easier. + */ +static void +ucnv_MBCSSingleToBMPWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const uint8_t *source, *sourceLimit, *lastSource; + char16_t *target; + int32_t targetCapacity, length; + int32_t *offsets; + + const int32_t (*stateTable)[256]; + + int32_t sourceIndex; + + int32_t entry; + uint8_t action; + + /* set up the local pointers */ + cnv=pArgs->converter; + source=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + target=pArgs->target; + targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); + offsets=pArgs->offsets; + + if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { + stateTable=(const int32_t (*)[256])cnv->sharedData->mbcs.swapLFNLStateTable; + } else { + stateTable=cnv->sharedData->mbcs.stateTable; + } + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex=0; + lastSource=source; + + /* + * since the conversion here is 1:1 char16_t:uint8_t, we need only one counter + * for the minimum of the sourceLength and targetCapacity + */ + length=(int32_t)(sourceLimit-source); + if(length=16) { + int32_t count, loops, oredEntries; + + loops=count=targetCapacity>>4; + do { + oredEntries=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + oredEntries|=entry=stateTable[0][*source++]; + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + + /* were all 16 entries really valid? */ + if(!MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(oredEntries)) { + /* no, return to the first of these 16 */ + source-=16; + target-=16; + break; + } + } while(--count>0); + count=loops-count; + targetCapacity-=16*count; + + if(offsets!=nullptr) { + lastSource+=16*count; + while(count>0) { + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + --count; + } + } + } +#endif + + /* conversion loop */ + while(targetCapacity > 0 && source < sourceLimit) { + entry=stateTable[0][*source++]; + /* MBCS_ENTRY_IS_FINAL(entry) */ + + /* test the most common case first */ + if(MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(entry)) { + /* output BMP code point */ + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + --targetCapacity; + continue; + } + + /* + * An if-else-if chain provides more reliable performance for + * the most common cases compared to a switch. + */ + action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); + if(action==MBCS_STATE_FALLBACK_DIRECT_16) { + if(UCNV_TO_U_USE_FALLBACK(cnv)) { + /* output BMP code point */ + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + --targetCapacity; + continue; + } + } else if(action==MBCS_STATE_UNASSIGNED) { + /* just fall through */ + } else if(action==MBCS_STATE_ILLEGAL) { + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } else { + /* reserved, must never occur */ + continue; + } + + /* set offsets since the start or the last extension */ + if(offsets!=nullptr) { + int32_t count=(int32_t)(source-lastSource); + + /* predecrement: do not set the offset for the callback-causing character */ + while(--count>0) { + *offsets++=sourceIndex++; + } + /* offset and sourceIndex are now set for the current character */ + } + + if(U_FAILURE(*pErrorCode)) { + /* callback(illegal) */ + break; + } else /* unassigned sequences indicated with byteIndex>0 */ { + /* try an extension mapping */ + lastSource=source; + cnv->toUBytes[0]=*(source-1); + cnv->toULength=_extToU(cnv, cnv->sharedData, + 1, &source, sourceLimit, + &target, pArgs->targetLimit, + &offsets, sourceIndex, + pArgs->flush, + pErrorCode); + sourceIndex+=1+(int32_t)(source-lastSource); + + if(U_FAILURE(*pErrorCode)) { + /* not mappable or buffer overflow */ + break; + } + + /* recalculate the targetCapacity after an extension mapping */ + targetCapacity=(int32_t)(pArgs->targetLimit-target); + length=(int32_t)(sourceLimit-source); + if(length=pArgs->targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + + /* set offsets since the start or the last callback */ + if(offsets!=nullptr) { + size_t count=source-lastSource; + while(count>0) { + *offsets++=sourceIndex++; + --count; + } + } + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + pArgs->offsets=offsets; +} + +static UBool +hasValidTrailBytes(const int32_t (*stateTable)[256], uint8_t state) { + const int32_t *row=stateTable[state]; + int32_t b, entry; + /* First test for final entries in this state for some commonly valid byte values. */ + entry=row[0xa1]; + if( !MBCS_ENTRY_IS_TRANSITION(entry) && + MBCS_ENTRY_FINAL_ACTION(entry)!=MBCS_STATE_ILLEGAL + ) { + return true; + } + entry=row[0x41]; + if( !MBCS_ENTRY_IS_TRANSITION(entry) && + MBCS_ENTRY_FINAL_ACTION(entry)!=MBCS_STATE_ILLEGAL + ) { + return true; + } + /* Then test for final entries in this state. */ + for(b=0; b<=0xff; ++b) { + entry=row[b]; + if( !MBCS_ENTRY_IS_TRANSITION(entry) && + MBCS_ENTRY_FINAL_ACTION(entry)!=MBCS_STATE_ILLEGAL + ) { + return true; + } + } + /* Then recurse for transition entries. */ + for(b=0; b<=0xff; ++b) { + entry=row[b]; + if( MBCS_ENTRY_IS_TRANSITION(entry) && + hasValidTrailBytes(stateTable, (uint8_t)MBCS_ENTRY_TRANSITION_STATE(entry)) + ) { + return true; + } + } + return false; +} + +/* + * Is byte b a single/lead byte in this state? + * Recurse for transition states, because here we don't want to say that + * b is a lead byte if all byte sequences that start with b are illegal. + */ +static UBool +isSingleOrLead(const int32_t (*stateTable)[256], uint8_t state, UBool isDBCSOnly, uint8_t b) { + const int32_t *row=stateTable[state]; + int32_t entry=row[b]; + if(MBCS_ENTRY_IS_TRANSITION(entry)) { /* lead byte */ + return hasValidTrailBytes(stateTable, (uint8_t)MBCS_ENTRY_TRANSITION_STATE(entry)); + } else { + uint8_t action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); + if(action==MBCS_STATE_CHANGE_ONLY && isDBCSOnly) { + return false; /* SI/SO are illegal for DBCS-only conversion */ + } else { + return action!=MBCS_STATE_ILLEGAL; + } + } +} + +U_CFUNC void +ucnv_MBCSToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const uint8_t *source, *sourceLimit; + char16_t *target; + const char16_t *targetLimit; + int32_t *offsets; + + const int32_t (*stateTable)[256]; + const uint16_t *unicodeCodeUnits; + + uint32_t offset; + uint8_t state; + int8_t byteIndex; + uint8_t *bytes; + + int32_t sourceIndex, nextSourceIndex; + + int32_t entry; + char16_t c; + uint8_t action; + + /* use optimized function if possible */ + cnv=pArgs->converter; + + if(cnv->preToULength>0) { + /* + * pass sourceIndex=-1 because we continue from an earlier buffer + * in the future, this may change with continuous offsets + */ + ucnv_extContinueMatchToU(cnv, pArgs, -1, pErrorCode); + + if(U_FAILURE(*pErrorCode) || cnv->preToULength<0) { + return; + } + } + + if(cnv->sharedData->mbcs.countStates==1) { + if(!(cnv->sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY)) { + ucnv_MBCSSingleToBMPWithOffsets(pArgs, pErrorCode); + } else { + ucnv_MBCSSingleToUnicodeWithOffsets(pArgs, pErrorCode); + } + return; + } + + /* set up the local pointers */ + source=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + target=pArgs->target; + targetLimit=pArgs->targetLimit; + offsets=pArgs->offsets; + + if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { + stateTable=(const int32_t (*)[256])cnv->sharedData->mbcs.swapLFNLStateTable; + } else { + stateTable=cnv->sharedData->mbcs.stateTable; + } + unicodeCodeUnits=cnv->sharedData->mbcs.unicodeCodeUnits; + + /* get the converter state from UConverter */ + offset=cnv->toUnicodeStatus; + byteIndex=cnv->toULength; + bytes=cnv->toUBytes; + + /* + * if we are in the SBCS state for a DBCS-only converter, + * then load the DBCS state from the MBCS data + * (dbcsOnlyState==0 if it is not a DBCS-only converter) + */ + if((state=(uint8_t)(cnv->mode))==0) { + state=cnv->sharedData->mbcs.dbcsOnlyState; + } + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex=byteIndex==0 ? 0 : -1; + nextSourceIndex=0; + + /* conversion loop */ + while(source=targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + + if(byteIndex==0) { + /* optimized loop for 1/2-byte input and BMP output */ + if(offsets==nullptr) { + do { + entry=stateTable[state][*source]; + if(MBCS_ENTRY_IS_TRANSITION(entry)) { + state=(uint8_t)MBCS_ENTRY_TRANSITION_STATE(entry); + offset=MBCS_ENTRY_TRANSITION_OFFSET(entry); + + ++source; + if( source=sourceLimit) { + break; + } + if(target>=targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + + ++nextSourceIndex; + bytes[byteIndex++]=*source++; + } else /* byteIndex>0 */ { + ++nextSourceIndex; + entry=stateTable[state][bytes[byteIndex++]=*source++]; + } + + if(MBCS_ENTRY_IS_TRANSITION(entry)) { + state=(uint8_t)MBCS_ENTRY_TRANSITION_STATE(entry); + offset+=MBCS_ENTRY_TRANSITION_OFFSET(entry); + continue; + } + + /* save the previous state for proper extension mapping with SI/SO-stateful converters */ + cnv->mode=state; + + /* set the next state early so that we can reuse the entry variable */ + state=(uint8_t)MBCS_ENTRY_FINAL_STATE(entry); /* typically 0 */ + + /* + * An if-else-if chain provides more reliable performance for + * the most common cases compared to a switch. + */ + action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); + if(action==MBCS_STATE_VALID_16) { + offset+=MBCS_ENTRY_FINAL_VALUE_16(entry); + c=unicodeCodeUnits[offset]; + if(c<0xfffe) { + /* output BMP code point */ + *target++=c; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + byteIndex=0; + } else if(c==0xfffe) { + if(UCNV_TO_U_USE_FALLBACK(cnv) && (entry=(int32_t)ucnv_MBCSGetFallback(&cnv->sharedData->mbcs, offset))!=0xfffe) { + /* output fallback BMP code point */ + *target++=(char16_t)entry; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + byteIndex=0; + } + } else { + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + } else if(action==MBCS_STATE_VALID_DIRECT_16) { + /* output BMP code point */ + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + byteIndex=0; + } else if(action==MBCS_STATE_VALID_16_PAIR) { + offset+=MBCS_ENTRY_FINAL_VALUE_16(entry); + c=unicodeCodeUnits[offset++]; + if(c<0xd800) { + /* output BMP code point below 0xd800 */ + *target++=c; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + byteIndex=0; + } else if(UCNV_TO_U_USE_FALLBACK(cnv) ? c<=0xdfff : c<=0xdbff) { + /* output roundtrip or fallback surrogate pair */ + *target++=(char16_t)(c&0xdbff); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + byteIndex=0; + if(targetUCharErrorBuffer[0]=unicodeCodeUnits[offset]; + cnv->UCharErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + + offset=0; + break; + } + } else if(UCNV_TO_U_USE_FALLBACK(cnv) ? (c&0xfffe)==0xe000 : c==0xe000) { + /* output roundtrip BMP code point above 0xd800 or fallback BMP code point */ + *target++=unicodeCodeUnits[offset]; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + byteIndex=0; + } else if(c==0xffff) { + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + } else if(action==MBCS_STATE_VALID_DIRECT_20 || + (action==MBCS_STATE_FALLBACK_DIRECT_20 && UCNV_TO_U_USE_FALLBACK(cnv)) + ) { + entry=MBCS_ENTRY_FINAL_VALUE(entry); + /* output surrogate pair */ + *target++=(char16_t)(0xd800|(char16_t)(entry>>10)); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + byteIndex=0; + c=(char16_t)(0xdc00|(char16_t)(entry&0x3ff)); + if(targetUCharErrorBuffer[0]=c; + cnv->UCharErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + + offset=0; + break; + } + } else if(action==MBCS_STATE_CHANGE_ONLY) { + /* + * This serves as a state change without any output. + * It is useful for reading simple stateful encodings, + * for example using just Shift-In/Shift-Out codes. + * The 21 unused bits may later be used for more sophisticated + * state transitions. + */ + if(cnv->sharedData->mbcs.dbcsOnlyState==0) { + byteIndex=0; + } else { + /* SI/SO are illegal for DBCS-only conversion */ + state=(uint8_t)(cnv->mode); /* restore the previous state */ + + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + } else if(action==MBCS_STATE_FALLBACK_DIRECT_16) { + if(UCNV_TO_U_USE_FALLBACK(cnv)) { + /* output BMP code point */ + *target++=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + byteIndex=0; + } + } else if(action==MBCS_STATE_UNASSIGNED) { + /* just fall through */ + } else if(action==MBCS_STATE_ILLEGAL) { + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } else { + /* reserved, must never occur */ + byteIndex=0; + } + + /* end of action codes: prepare for a new character */ + offset=0; + + if(byteIndex==0) { + sourceIndex=nextSourceIndex; + } else if(U_FAILURE(*pErrorCode)) { + /* callback(illegal) */ + if(byteIndex>1) { + /* + * Ticket 5691: consistent illegal sequences: + * - We include at least the first byte in the illegal sequence. + * - If any of the non-initial bytes could be the start of a character, + * we stop the illegal sequence before the first one of those. + */ + UBool isDBCSOnly=(UBool)(cnv->sharedData->mbcs.dbcsOnlyState!=0); + int8_t i; + for(i=1; + isource); + byteIndex=i; /* length of reported illegal byte sequence */ + if(backOutDistance<=bytesFromThisBuffer) { + source-=backOutDistance; + } else { + /* Back out bytes from the previous buffer: Need to replay them. */ + cnv->preToULength=(int8_t)(bytesFromThisBuffer-backOutDistance); + /* preToULength is negative! */ + uprv_memcpy(cnv->preToU, bytes+i, -cnv->preToULength); + source=(const uint8_t *)pArgs->source; + } + } + } + break; + } else /* unassigned sequences indicated with byteIndex>0 */ { + /* try an extension mapping */ + pArgs->source=(const char *)source; + byteIndex=_extToU(cnv, cnv->sharedData, + byteIndex, &source, sourceLimit, + &target, targetLimit, + &offsets, sourceIndex, + pArgs->flush, + pErrorCode); + sourceIndex=nextSourceIndex+=(int32_t)(source-(const uint8_t *)pArgs->source); + + if(U_FAILURE(*pErrorCode)) { + /* not mappable or buffer overflow */ + break; + } + } + } + + /* set the converter state back into UConverter */ + cnv->toUnicodeStatus=offset; + cnv->mode=state; + cnv->toULength=byteIndex; + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + pArgs->offsets=offsets; +} + +/* + * This version of ucnv_MBCSGetNextUChar() is optimized for single-byte, single-state codepages. + * We still need a conversion loop in case we find reserved action codes, which are to be ignored. + */ +static UChar32 +ucnv_MBCSSingleGetNextUChar(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const int32_t (*stateTable)[256]; + const uint8_t *source, *sourceLimit; + + int32_t entry; + uint8_t action; + + /* set up the local pointers */ + cnv=pArgs->converter; + source=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { + stateTable=(const int32_t (*)[256])cnv->sharedData->mbcs.swapLFNLStateTable; + } else { + stateTable=cnv->sharedData->mbcs.stateTable; + } + + /* conversion loop */ + while(sourcesource=(const char *)source; + + if(MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(entry)) { + /* output BMP code point */ + return (char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + } + + /* + * An if-else-if chain provides more reliable performance for + * the most common cases compared to a switch. + */ + action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); + if( action==MBCS_STATE_VALID_DIRECT_20 || + (action==MBCS_STATE_FALLBACK_DIRECT_20 && UCNV_TO_U_USE_FALLBACK(cnv)) + ) { + /* output supplementary code point */ + return (UChar32)(MBCS_ENTRY_FINAL_VALUE(entry)+0x10000); + } else if(action==MBCS_STATE_FALLBACK_DIRECT_16) { + if(UCNV_TO_U_USE_FALLBACK(cnv)) { + /* output BMP code point */ + return (char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + } + } else if(action==MBCS_STATE_UNASSIGNED) { + /* just fall through */ + } else if(action==MBCS_STATE_ILLEGAL) { + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } else { + /* reserved, must never occur */ + continue; + } + + if(U_FAILURE(*pErrorCode)) { + /* callback(illegal) */ + break; + } else /* unassigned sequence */ { + /* defer to the generic implementation */ + pArgs->source=(const char *)source-1; + return UCNV_GET_NEXT_UCHAR_USE_TO_U; + } + } + + /* no output because of empty input or only state changes */ + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0xffff; +} + +/* + * Version of _MBCSToUnicodeWithOffsets() optimized for single-character + * conversion without offset handling. + * + * When a character does not have a mapping to Unicode, then we return to the + * generic ucnv_getNextUChar() code for extension/GB 18030 and error/callback + * handling. + * We also defer to the generic code in other complicated cases and have them + * ultimately handled by _MBCSToUnicodeWithOffsets() itself. + * + * All normal mappings and errors are handled here. + */ +static UChar32 U_CALLCONV +ucnv_MBCSGetNextUChar(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const uint8_t *source, *sourceLimit, *lastSource; + + const int32_t (*stateTable)[256]; + const uint16_t *unicodeCodeUnits; + + uint32_t offset; + uint8_t state; + + int32_t entry; + UChar32 c; + uint8_t action; + + /* use optimized function if possible */ + cnv=pArgs->converter; + + if(cnv->preToULength>0) { + /* use the generic code in ucnv_getNextUChar() to continue with a partial match */ + return UCNV_GET_NEXT_UCHAR_USE_TO_U; + } + + if(cnv->sharedData->mbcs.unicodeMask&UCNV_HAS_SURROGATES) { + /* + * Using the generic ucnv_getNextUChar() code lets us deal correctly + * with the rare case of a codepage that maps single surrogates + * without adding the complexity to this already complicated function here. + */ + return UCNV_GET_NEXT_UCHAR_USE_TO_U; + } else if(cnv->sharedData->mbcs.countStates==1) { + return ucnv_MBCSSingleGetNextUChar(pArgs, pErrorCode); + } + + /* set up the local pointers */ + source=lastSource=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + + if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { + stateTable=(const int32_t (*)[256])cnv->sharedData->mbcs.swapLFNLStateTable; + } else { + stateTable=cnv->sharedData->mbcs.stateTable; + } + unicodeCodeUnits=cnv->sharedData->mbcs.unicodeCodeUnits; + + /* get the converter state from UConverter */ + offset=cnv->toUnicodeStatus; + + /* + * if we are in the SBCS state for a DBCS-only converter, + * then load the DBCS state from the MBCS data + * (dbcsOnlyState==0 if it is not a DBCS-only converter) + */ + if((state=(uint8_t)(cnv->mode))==0) { + state=cnv->sharedData->mbcs.dbcsOnlyState; + } + + /* conversion loop */ + c=U_SENTINEL; + while(sourcemode=state; + + /* set the next state early so that we can reuse the entry variable */ + state=(uint8_t)MBCS_ENTRY_FINAL_STATE(entry); /* typically 0 */ + + /* + * An if-else-if chain provides more reliable performance for + * the most common cases compared to a switch. + */ + action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); + if(action==MBCS_STATE_VALID_DIRECT_16) { + /* output BMP code point */ + c=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + break; + } else if(action==MBCS_STATE_VALID_16) { + offset+=MBCS_ENTRY_FINAL_VALUE_16(entry); + c=unicodeCodeUnits[offset]; + if(c<0xfffe) { + /* output BMP code point */ + break; + } else if(c==0xfffe) { + if(UCNV_TO_U_USE_FALLBACK(cnv) && (c=ucnv_MBCSGetFallback(&cnv->sharedData->mbcs, offset))!=0xfffe) { + break; + } + } else { + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + } else if(action==MBCS_STATE_VALID_16_PAIR) { + offset+=MBCS_ENTRY_FINAL_VALUE_16(entry); + c=unicodeCodeUnits[offset++]; + if(c<0xd800) { + /* output BMP code point below 0xd800 */ + break; + } else if(UCNV_TO_U_USE_FALLBACK(cnv) ? c<=0xdfff : c<=0xdbff) { + /* output roundtrip or fallback supplementary code point */ + c=((c&0x3ff)<<10)+unicodeCodeUnits[offset]+(0x10000-0xdc00); + break; + } else if(UCNV_TO_U_USE_FALLBACK(cnv) ? (c&0xfffe)==0xe000 : c==0xe000) { + /* output roundtrip BMP code point above 0xd800 or fallback BMP code point */ + c=unicodeCodeUnits[offset]; + break; + } else if(c==0xffff) { + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + } else if(action==MBCS_STATE_VALID_DIRECT_20 || + (action==MBCS_STATE_FALLBACK_DIRECT_20 && UCNV_TO_U_USE_FALLBACK(cnv)) + ) { + /* output supplementary code point */ + c=(UChar32)(MBCS_ENTRY_FINAL_VALUE(entry)+0x10000); + break; + } else if(action==MBCS_STATE_CHANGE_ONLY) { + /* + * This serves as a state change without any output. + * It is useful for reading simple stateful encodings, + * for example using just Shift-In/Shift-Out codes. + * The 21 unused bits may later be used for more sophisticated + * state transitions. + */ + if(cnv->sharedData->mbcs.dbcsOnlyState!=0) { + /* SI/SO are illegal for DBCS-only conversion */ + state=(uint8_t)(cnv->mode); /* restore the previous state */ + + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } + } else if(action==MBCS_STATE_FALLBACK_DIRECT_16) { + if(UCNV_TO_U_USE_FALLBACK(cnv)) { + /* output BMP code point */ + c=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + break; + } + } else if(action==MBCS_STATE_UNASSIGNED) { + /* just fall through */ + } else if(action==MBCS_STATE_ILLEGAL) { + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + } else { + /* reserved (must never occur), or only state change */ + offset=0; + lastSource=source; + continue; + } + + /* end of action codes: prepare for a new character */ + offset=0; + + if(U_FAILURE(*pErrorCode)) { + /* callback(illegal) */ + break; + } else /* unassigned sequence */ { + /* defer to the generic implementation */ + cnv->toUnicodeStatus=0; + cnv->mode=state; + pArgs->source=(const char *)lastSource; + return UCNV_GET_NEXT_UCHAR_USE_TO_U; + } + } + } + + if(c<0) { + if(U_SUCCESS(*pErrorCode) && source==sourceLimit && lastSourcetoUBytes; + cnv->toULength=(int8_t)(source-lastSource); + do { + *bytes++=*lastSource++; + } while(lastSourcesharedData->mbcs.dbcsOnlyState!=0); + uint8_t *bytes=cnv->toUBytes; + *bytes++=*lastSource++; /* first byte */ + if(lastSource==source) { + cnv->toULength=1; + } else /* lastSourcetoULength=i; + source=lastSource; + } + } else { + /* no output because of empty input or only state changes */ + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + } + c=0xffff; + } + + /* set the converter state back into UConverter, ready for a new character */ + cnv->toUnicodeStatus=0; + cnv->mode=state; + + /* write back the updated pointer */ + pArgs->source=(const char *)source; + return c; +} + +#if 0 +/* + * Code disabled 2002dec09 (ICU 2.4) because it is not currently used in ICU. markus + * Removal improves code coverage. + */ +/** + * This version of ucnv_MBCSSimpleGetNextUChar() is optimized for single-byte, single-state codepages. + * It does not handle the EBCDIC swaplfnl option (set in UConverter). + * It does not handle conversion extensions (_extToU()). + */ +U_CFUNC UChar32 +ucnv_MBCSSingleSimpleGetNextUChar(UConverterSharedData *sharedData, + uint8_t b, UBool useFallback) { + int32_t entry; + uint8_t action; + + entry=sharedData->mbcs.stateTable[0][b]; + /* MBCS_ENTRY_IS_FINAL(entry) */ + + if(MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(entry)) { + /* output BMP code point */ + return (char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + } + + /* + * An if-else-if chain provides more reliable performance for + * the most common cases compared to a switch. + */ + action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); + if(action==MBCS_STATE_VALID_DIRECT_20) { + /* output supplementary code point */ + return 0x10000+MBCS_ENTRY_FINAL_VALUE(entry); + } else if(action==MBCS_STATE_FALLBACK_DIRECT_16) { + if(!TO_U_USE_FALLBACK(useFallback)) { + return 0xfffe; + } + /* output BMP code point */ + return (char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + } else if(action==MBCS_STATE_FALLBACK_DIRECT_20) { + if(!TO_U_USE_FALLBACK(useFallback)) { + return 0xfffe; + } + /* output supplementary code point */ + return 0x10000+MBCS_ENTRY_FINAL_VALUE(entry); + } else if(action==MBCS_STATE_UNASSIGNED) { + return 0xfffe; + } else if(action==MBCS_STATE_ILLEGAL) { + return 0xffff; + } else { + /* reserved, must never occur */ + return 0xffff; + } +} +#endif + +/* + * This is a simple version of _MBCSGetNextUChar() that is used + * by other converter implementations. + * It only returns an "assigned" result if it consumes the entire input. + * It does not use state from the converter, nor error codes. + * It does not handle the EBCDIC swaplfnl option (set in UConverter). + * It handles conversion extensions but not GB 18030. + * + * Return value: + * U+fffe unassigned + * U+ffff illegal + * otherwise the Unicode code point + */ +U_CFUNC UChar32 +ucnv_MBCSSimpleGetNextUChar(UConverterSharedData *sharedData, + const char *source, int32_t length, + UBool useFallback) { + const int32_t (*stateTable)[256]; + const uint16_t *unicodeCodeUnits; + + uint32_t offset; + uint8_t state, action; + + UChar32 c; + int32_t i, entry; + + if(length<=0) { + /* no input at all: "illegal" */ + return 0xffff; + } + +#if 0 +/* + * Code disabled 2002dec09 (ICU 2.4) because it is not currently used in ICU. markus + * TODO In future releases, verify that this function is never called for SBCS + * conversions, i.e., that sharedData->mbcs.countStates==1 is still true. + * Removal improves code coverage. + */ + /* use optimized function if possible */ + if(sharedData->mbcs.countStates==1) { + if(length==1) { + return ucnv_MBCSSingleSimpleGetNextUChar(sharedData, (uint8_t)*source, useFallback); + } else { + return 0xffff; /* illegal: more than a single byte for an SBCS converter */ + } + } +#endif + + /* set up the local pointers */ + stateTable=sharedData->mbcs.stateTable; + unicodeCodeUnits=sharedData->mbcs.unicodeCodeUnits; + + /* converter state */ + offset=0; + state=sharedData->mbcs.dbcsOnlyState; + + /* conversion loop */ + for(i=0;;) { + entry=stateTable[state][(uint8_t)source[i++]]; + if(MBCS_ENTRY_IS_TRANSITION(entry)) { + state=(uint8_t)MBCS_ENTRY_TRANSITION_STATE(entry); + offset+=MBCS_ENTRY_TRANSITION_OFFSET(entry); + + if(i==length) { + return 0xffff; /* truncated character */ + } + } else { + /* + * An if-else-if chain provides more reliable performance for + * the most common cases compared to a switch. + */ + action=(uint8_t)(MBCS_ENTRY_FINAL_ACTION(entry)); + if(action==MBCS_STATE_VALID_16) { + offset+=MBCS_ENTRY_FINAL_VALUE_16(entry); + c=unicodeCodeUnits[offset]; + if(c!=0xfffe) { + /* done */ + } else if(UCNV_TO_U_USE_FALLBACK(cnv)) { + c=ucnv_MBCSGetFallback(&sharedData->mbcs, offset); + /* else done with 0xfffe */ + } + break; + } else if(action==MBCS_STATE_VALID_DIRECT_16) { + /* output BMP code point */ + c=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + break; + } else if(action==MBCS_STATE_VALID_16_PAIR) { + offset+=MBCS_ENTRY_FINAL_VALUE_16(entry); + c=unicodeCodeUnits[offset++]; + if(c<0xd800) { + /* output BMP code point below 0xd800 */ + } else if(UCNV_TO_U_USE_FALLBACK(cnv) ? c<=0xdfff : c<=0xdbff) { + /* output roundtrip or fallback supplementary code point */ + c=(UChar32)(((c&0x3ff)<<10)+unicodeCodeUnits[offset]+(0x10000-0xdc00)); + } else if(UCNV_TO_U_USE_FALLBACK(cnv) ? (c&0xfffe)==0xe000 : c==0xe000) { + /* output roundtrip BMP code point above 0xd800 or fallback BMP code point */ + c=unicodeCodeUnits[offset]; + } else if(c==0xffff) { + return 0xffff; + } else { + c=0xfffe; + } + break; + } else if(action==MBCS_STATE_VALID_DIRECT_20) { + /* output supplementary code point */ + c=0x10000+MBCS_ENTRY_FINAL_VALUE(entry); + break; + } else if(action==MBCS_STATE_FALLBACK_DIRECT_16) { + if(!TO_U_USE_FALLBACK(useFallback)) { + c=0xfffe; + break; + } + /* output BMP code point */ + c=(char16_t)MBCS_ENTRY_FINAL_VALUE_16(entry); + break; + } else if(action==MBCS_STATE_FALLBACK_DIRECT_20) { + if(!TO_U_USE_FALLBACK(useFallback)) { + c=0xfffe; + break; + } + /* output supplementary code point */ + c=0x10000+MBCS_ENTRY_FINAL_VALUE(entry); + break; + } else if(action==MBCS_STATE_UNASSIGNED) { + c=0xfffe; + break; + } + + /* + * forbid MBCS_STATE_CHANGE_ONLY for this function, + * and MBCS_STATE_ILLEGAL and reserved action codes + */ + return 0xffff; + } + } + + if(i!=length) { + /* illegal for this function: not all input consumed */ + return 0xffff; + } + + if(c==0xfffe) { + /* try an extension mapping */ + const int32_t *cx=sharedData->mbcs.extIndexes; + if(cx!=nullptr) { + return ucnv_extSimpleMatchToU(cx, source, length, useFallback); + } + } + + return c; +} + +/* MBCS-from-Unicode conversion functions ----------------------------------- */ + +/* This version of ucnv_MBCSFromUnicodeWithOffsets() is optimized for double-byte codepages. */ +static void +ucnv_MBCSDoubleFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const char16_t *source, *sourceLimit; + uint8_t *target; + int32_t targetCapacity; + int32_t *offsets; + + const uint16_t *table; + const uint16_t *mbcsIndex; + const uint8_t *bytes; + + UChar32 c; + + int32_t sourceIndex, nextSourceIndex; + + uint32_t stage2Entry; + uint32_t asciiRoundtrips; + uint32_t value; + uint8_t unicodeMask; + + /* use optimized function if possible */ + cnv=pArgs->converter; + unicodeMask=cnv->sharedData->mbcs.unicodeMask; + + /* set up the local pointers */ + source=pArgs->source; + sourceLimit=pArgs->sourceLimit; + target=(uint8_t *)pArgs->target; + targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); + offsets=pArgs->offsets; + + table=cnv->sharedData->mbcs.fromUnicodeTable; + mbcsIndex=cnv->sharedData->mbcs.mbcsIndex; + if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { + bytes=cnv->sharedData->mbcs.swapLFNLFromUnicodeBytes; + } else { + bytes=cnv->sharedData->mbcs.fromUnicodeBytes; + } + asciiRoundtrips=cnv->sharedData->mbcs.asciiRoundtrips; + + /* get the converter state from UConverter */ + c=cnv->fromUChar32; + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex= c==0 ? 0 : -1; + nextSourceIndex=0; + + /* conversion loop */ + if(c!=0 && targetCapacity>0) { + goto getTrail; + } + + while(source0) { + /* + * Get a correct Unicode code point: + * a single char16_t for a BMP code point or + * a matched surrogate pair for a "supplementary code point". + */ + c=*source++; + ++nextSourceIndex; + if(c<=0x7f && IS_ASCII_ROUNDTRIP(c, asciiRoundtrips)) { + *target++=(uint8_t)c; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + sourceIndex=nextSourceIndex; + } + --targetCapacity; + c=0; + continue; + } + /* + * utf8Friendly table: Test for <=0xd7ff rather than <=MBCS_FAST_MAX + * to avoid dealing with surrogates. + * MBCS_FAST_MAX must be >=0xd7ff. + */ + if(c<=0xd7ff) { + value=DBCS_RESULT_FROM_MOST_BMP(mbcsIndex, (const uint16_t *)bytes, c); + /* There are only roundtrips (!=0) and no-mapping (==0) entries. */ + if(value==0) { + goto unassigned; + } + /* output the value */ + } else { + /* + * This also tests if the codepage maps single surrogates. + * If it does, then surrogates are not paired but mapped separately. + * Note that in this case unmatched surrogates are not detected. + */ + if(U16_IS_SURROGATE(c) && !(unicodeMask&UCNV_HAS_SURROGATES)) { + if(U16_IS_SURROGATE_LEAD(c)) { +getTrail: + if(sourcesource=source; + c=_extFromU(cnv, cnv->sharedData, + c, &source, sourceLimit, + &target, target+targetCapacity, + &offsets, sourceIndex, + pArgs->flush, + pErrorCode); + nextSourceIndex+=(int32_t)(source-pArgs->source); + + if(U_FAILURE(*pErrorCode)) { + /* not mappable or buffer overflow */ + break; + } else { + /* a mapping was written to the target, continue */ + + /* recalculate the targetCapacity after an extension mapping */ + targetCapacity=(int32_t)(pArgs->targetLimit-(char *)target); + + /* normal end of conversion: prepare for a new character */ + sourceIndex=nextSourceIndex; + continue; + } + } + } + + /* write the output character bytes from value and length */ + /* from the first if in the loop we know that targetCapacity>0 */ + if(value<=0xff) { + /* this is easy because we know that there is enough space */ + *target++=(uint8_t)value; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + --targetCapacity; + } else /* length==2 */ { + *target++=(uint8_t)(value>>8); + if(2<=targetCapacity) { + *target++=(uint8_t)value; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + *offsets++=sourceIndex; + } + targetCapacity-=2; + } else { + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + cnv->charErrorBuffer[0]=(char)value; + cnv->charErrorBufferLength=1; + + /* target overflow */ + targetCapacity=0; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + c=0; + break; + } + } + + /* normal end of conversion: prepare for a new character */ + c=0; + sourceIndex=nextSourceIndex; + continue; + } else { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + + /* set the converter state back into UConverter */ + cnv->fromUChar32=c; + + /* write back the updated pointers */ + pArgs->source=source; + pArgs->target=(char *)target; + pArgs->offsets=offsets; +} + +/* This version of ucnv_MBCSFromUnicodeWithOffsets() is optimized for single-byte codepages. */ +static void +ucnv_MBCSSingleFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const char16_t *source, *sourceLimit; + uint8_t *target; + int32_t targetCapacity; + int32_t *offsets; + + const uint16_t *table; + const uint16_t *results; + + UChar32 c; + + int32_t sourceIndex, nextSourceIndex; + + uint16_t value, minValue; + UBool hasSupplementary; + + /* set up the local pointers */ + cnv=pArgs->converter; + source=pArgs->source; + sourceLimit=pArgs->sourceLimit; + target=(uint8_t *)pArgs->target; + targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); + offsets=pArgs->offsets; + + table=cnv->sharedData->mbcs.fromUnicodeTable; + if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { + results=(uint16_t *)cnv->sharedData->mbcs.swapLFNLFromUnicodeBytes; + } else { + results=(uint16_t *)cnv->sharedData->mbcs.fromUnicodeBytes; + } + + if(cnv->useFallback) { + /* use all roundtrip and fallback results */ + minValue=0x800; + } else { + /* use only roundtrips and fallbacks from private-use characters */ + minValue=0xc00; + } + hasSupplementary=(UBool)(cnv->sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY); + + /* get the converter state from UConverter */ + c=cnv->fromUChar32; + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex= c==0 ? 0 : -1; + nextSourceIndex=0; + + /* conversion loop */ + if(c!=0 && targetCapacity>0) { + goto getTrail; + } + + while(source0) { + /* + * Get a correct Unicode code point: + * a single char16_t for a BMP code point or + * a matched surrogate pair for a "supplementary code point". + */ + c=*source++; + ++nextSourceIndex; + if(U16_IS_SURROGATE(c)) { + if(U16_IS_SURROGATE_LEAD(c)) { +getTrail: + if(source=minValue) { + /* assigned, write the output character bytes from value and length */ + /* length==1 */ + /* this is easy because we know that there is enough space */ + *target++=(uint8_t)value; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + --targetCapacity; + + /* normal end of conversion: prepare for a new character */ + c=0; + sourceIndex=nextSourceIndex; + } else { /* unassigned */ +unassigned: + /* try an extension mapping */ + pArgs->source=source; + c=_extFromU(cnv, cnv->sharedData, + c, &source, sourceLimit, + &target, target+targetCapacity, + &offsets, sourceIndex, + pArgs->flush, + pErrorCode); + nextSourceIndex+=(int32_t)(source-pArgs->source); + + if(U_FAILURE(*pErrorCode)) { + /* not mappable or buffer overflow */ + break; + } else { + /* a mapping was written to the target, continue */ + + /* recalculate the targetCapacity after an extension mapping */ + targetCapacity=(int32_t)(pArgs->targetLimit-(char *)target); + + /* normal end of conversion: prepare for a new character */ + sourceIndex=nextSourceIndex; + } + } + } else { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + + /* set the converter state back into UConverter */ + cnv->fromUChar32=c; + + /* write back the updated pointers */ + pArgs->source=source; + pArgs->target=(char *)target; + pArgs->offsets=offsets; +} + +/* + * This version of ucnv_MBCSFromUnicode() is optimized for single-byte codepages + * that map only to and from the BMP. + * In addition to single-byte/state optimizations, the offset calculations + * become much easier. + * It would be possible to use the sbcsIndex for UTF-8-friendly tables, + * but measurements have shown that this diminishes performance + * in more cases than it improves it. + * See SVN revision 21013 (2007-feb-06) for the last version with #if switches + * for various MBCS and SBCS optimizations. + */ +static void +ucnv_MBCSSingleFromBMPWithOffsets(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const char16_t *source, *sourceLimit, *lastSource; + uint8_t *target; + int32_t targetCapacity, length; + int32_t *offsets; + + const uint16_t *table; + const uint16_t *results; + + UChar32 c; + + int32_t sourceIndex; + + uint32_t asciiRoundtrips; + uint16_t value, minValue; + + /* set up the local pointers */ + cnv=pArgs->converter; + source=pArgs->source; + sourceLimit=pArgs->sourceLimit; + target=(uint8_t *)pArgs->target; + targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); + offsets=pArgs->offsets; + + table=cnv->sharedData->mbcs.fromUnicodeTable; + if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { + results=(uint16_t *)cnv->sharedData->mbcs.swapLFNLFromUnicodeBytes; + } else { + results=(uint16_t *)cnv->sharedData->mbcs.fromUnicodeBytes; + } + asciiRoundtrips=cnv->sharedData->mbcs.asciiRoundtrips; + + if(cnv->useFallback) { + /* use all roundtrip and fallback results */ + minValue=0x800; + } else { + /* use only roundtrips and fallbacks from private-use characters */ + minValue=0xc00; + } + + /* get the converter state from UConverter */ + c=cnv->fromUChar32; + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex= c==0 ? 0 : -1; + lastSource=source; + + /* + * since the conversion here is 1:1 char16_t:uint8_t, we need only one counter + * for the minimum of the sourceLength and targetCapacity + */ + length=(int32_t)(sourceLimit-source); + if(length0) { + goto getTrail; + } + +#if MBCS_UNROLL_SINGLE_FROM_BMP + /* unrolling makes it slower on Pentium III/Windows 2000?! */ + /* unroll the loop with the most common case */ +unrolled: + if(targetCapacity>=4) { + int32_t count, loops; + uint16_t andedValues; + + loops=count=targetCapacity>>2; + do { + c=*source++; + andedValues=value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); + *target++=(uint8_t)value; + c=*source++; + andedValues&=value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); + *target++=(uint8_t)value; + c=*source++; + andedValues&=value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); + *target++=(uint8_t)value; + c=*source++; + andedValues&=value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); + *target++=(uint8_t)value; + + /* were all 4 entries really valid? */ + if(andedValues0); + count=loops-count; + targetCapacity-=4*count; + + if(offsets!=nullptr) { + lastSource+=4*count; + while(count>0) { + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + *offsets++=sourceIndex++; + --count; + } + } + + c=0; + } +#endif + + while(targetCapacity>0) { + /* + * Get a correct Unicode code point: + * a single char16_t for a BMP code point or + * a matched surrogate pair for a "supplementary code point". + */ + c=*source++; + /* + * Do not immediately check for single surrogates: + * Assume that they are unassigned and check for them in that case. + * This speeds up the conversion of assigned characters. + */ + /* convert the Unicode code point in c into codepage bytes */ + if(c<=0x7f && IS_ASCII_ROUNDTRIP(c, asciiRoundtrips)) { + *target++=(uint8_t)c; + --targetCapacity; + c=0; + continue; + } + value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); + /* is this code point assigned, or do we use fallbacks? */ + if(value>=minValue) { + /* assigned, write the output character bytes from value and length */ + /* length==1 */ + /* this is easy because we know that there is enough space */ + *target++=(uint8_t)value; + --targetCapacity; + + /* normal end of conversion: prepare for a new character */ + c=0; + continue; + } else if(!U16_IS_SURROGATE(c)) { + /* normal, unassigned BMP character */ + } else if(U16_IS_SURROGATE_LEAD(c)) { +getTrail: + if(sourceflush) { + *pErrorCode=U_TRUNCATED_CHAR_FOUND; + } + break; + } + } else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } + + /* c does not have a mapping */ + + /* get the number of code units for c to correctly advance sourceIndex */ + length=U16_LENGTH(c); + + /* set offsets since the start or the last extension */ + if(offsets!=nullptr) { + int32_t count=(int32_t)(source-lastSource); + + /* do not set the offset for this character */ + count-=length; + + while(count>0) { + *offsets++=sourceIndex++; + --count; + } + /* offsets and sourceIndex are now set for the current character */ + } + + /* try an extension mapping */ + lastSource=source; + c=_extFromU(cnv, cnv->sharedData, + c, &source, sourceLimit, + &target, (const uint8_t *)(pArgs->targetLimit), + &offsets, sourceIndex, + pArgs->flush, + pErrorCode); + sourceIndex+=length+(int32_t)(source-lastSource); + lastSource=source; + + if(U_FAILURE(*pErrorCode)) { + /* not mappable or buffer overflow */ + break; + } else { + /* a mapping was written to the target, continue */ + + /* recalculate the targetCapacity after an extension mapping */ + targetCapacity=(int32_t)(pArgs->targetLimit-(char *)target); + length=(int32_t)(sourceLimit-source); + if(length=(uint8_t *)pArgs->targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + + /* set offsets since the start or the last callback */ + if(offsets!=nullptr) { + size_t count=source-lastSource; + if (count > 0 && *pErrorCode == U_TRUNCATED_CHAR_FOUND) { + /* + Caller gave us a partial supplementary character, + which this function couldn't convert in any case. + The callback will handle the offset. + */ + count--; + } + while(count>0) { + *offsets++=sourceIndex++; + --count; + } + } + + /* set the converter state back into UConverter */ + cnv->fromUChar32=c; + + /* write back the updated pointers */ + pArgs->source=source; + pArgs->target=(char *)target; + pArgs->offsets=offsets; +} + +U_CFUNC void +ucnv_MBCSFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + const char16_t *source, *sourceLimit; + uint8_t *target; + int32_t targetCapacity; + int32_t *offsets; + + const uint16_t *table; + const uint16_t *mbcsIndex; + const uint8_t *p, *bytes; + uint8_t outputType; + + UChar32 c; + + int32_t prevSourceIndex, sourceIndex, nextSourceIndex; + + uint32_t stage2Entry; + uint32_t asciiRoundtrips; + uint32_t value; + /* Shift-In and Shift-Out byte sequences differ by encoding scheme. */ + uint8_t siBytes[2] = {0, 0}; + uint8_t soBytes[2] = {0, 0}; + uint8_t siLength, soLength; + int32_t length = 0, prevLength; + uint8_t unicodeMask; + + cnv=pArgs->converter; + + if(cnv->preFromUFirstCP>=0) { + /* + * pass sourceIndex=-1 because we continue from an earlier buffer + * in the future, this may change with continuous offsets + */ + ucnv_extContinueMatchFromU(cnv, pArgs, -1, pErrorCode); + + if(U_FAILURE(*pErrorCode) || cnv->preFromULength<0) { + return; + } + } + + /* use optimized function if possible */ + outputType=cnv->sharedData->mbcs.outputType; + unicodeMask=cnv->sharedData->mbcs.unicodeMask; + if(outputType==MBCS_OUTPUT_1 && !(unicodeMask&UCNV_HAS_SURROGATES)) { + if(!(unicodeMask&UCNV_HAS_SUPPLEMENTARY)) { + ucnv_MBCSSingleFromBMPWithOffsets(pArgs, pErrorCode); + } else { + ucnv_MBCSSingleFromUnicodeWithOffsets(pArgs, pErrorCode); + } + return; + } else if(outputType==MBCS_OUTPUT_2 && cnv->sharedData->mbcs.utf8Friendly) { + ucnv_MBCSDoubleFromUnicodeWithOffsets(pArgs, pErrorCode); + return; + } + + /* set up the local pointers */ + source=pArgs->source; + sourceLimit=pArgs->sourceLimit; + target=(uint8_t *)pArgs->target; + targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); + offsets=pArgs->offsets; + + table=cnv->sharedData->mbcs.fromUnicodeTable; + if(cnv->sharedData->mbcs.utf8Friendly) { + mbcsIndex=cnv->sharedData->mbcs.mbcsIndex; + } else { + mbcsIndex=nullptr; + } + if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { + bytes=cnv->sharedData->mbcs.swapLFNLFromUnicodeBytes; + } else { + bytes=cnv->sharedData->mbcs.fromUnicodeBytes; + } + asciiRoundtrips=cnv->sharedData->mbcs.asciiRoundtrips; + + /* get the converter state from UConverter */ + c=cnv->fromUChar32; + + if(outputType==MBCS_OUTPUT_2_SISO) { + prevLength=cnv->fromUnicodeStatus; + if(prevLength==0) { + /* set the real value */ + prevLength=1; + } + } else { + /* prevent fromUnicodeStatus from being set to something non-0 */ + prevLength=0; + } + + /* sourceIndex=-1 if the current character began in the previous buffer */ + prevSourceIndex=-1; + sourceIndex= c==0 ? 0 : -1; + nextSourceIndex=0; + + /* Get the SI/SO character for the converter */ + siLength = static_cast(getSISOBytes(SI, cnv->options, siBytes)); + soLength = static_cast(getSISOBytes(SO, cnv->options, soBytes)); + + /* conversion loop */ + /* + * This is another piece of ugly code: + * A goto into the loop if the converter state contains a first surrogate + * from the previous function call. + * It saves me to check in each loop iteration a check of if(c==0) + * and duplicating the trail-surrogate-handling code in the else + * branch of that check. + * I could not find any other way to get around this other than + * using a function call for the conversion and callback, which would + * be even more inefficient. + * + * Markus Scherer 2000-jul-19 + */ + if(c!=0 && targetCapacity>0) { + goto getTrail; + } + + while(source0) { + /* + * Get a correct Unicode code point: + * a single char16_t for a BMP code point or + * a matched surrogate pair for a "supplementary code point". + */ + c=*source++; + ++nextSourceIndex; + if(c<=0x7f && IS_ASCII_ROUNDTRIP(c, asciiRoundtrips)) { + *target++=(uint8_t)c; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + prevSourceIndex=sourceIndex; + sourceIndex=nextSourceIndex; + } + --targetCapacity; + c=0; + continue; + } + /* + * utf8Friendly table: Test for <=0xd7ff rather than <=MBCS_FAST_MAX + * to avoid dealing with surrogates. + * MBCS_FAST_MAX must be >=0xd7ff. + */ + if(c<=0xd7ff && mbcsIndex!=nullptr) { + value=mbcsIndex[c>>6]; + + /* get the bytes and the length for the output (copied from below and adapted for utf8Friendly data) */ + /* There are only roundtrips (!=0) and no-mapping (==0) entries. */ + switch(outputType) { + case MBCS_OUTPUT_2: + value=((const uint16_t *)bytes)[value +(c&0x3f)]; + if(value<=0xff) { + if(value==0) { + goto unassigned; + } else { + length=1; + } + } else { + length=2; + } + break; + case MBCS_OUTPUT_2_SISO: + /* 1/2-byte stateful with Shift-In/Shift-Out */ + /* + * Save the old state in the converter object + * right here, then change the local prevLength state variable if necessary. + * Then, if this character turns out to be unassigned or a fallback that + * is not taken, the callback code must not save the new state in the converter + * because the new state is for a character that is not output. + * However, the callback must still restore the state from the converter + * in case the callback function changed it for its output. + */ + cnv->fromUnicodeStatus=prevLength; /* save the old state */ + value=((const uint16_t *)bytes)[value +(c&0x3f)]; + if(value<=0xff) { + if(value==0) { + goto unassigned; + } else if(prevLength<=1) { + length=1; + } else { + /* change from double-byte mode to single-byte */ + if (siLength == 1) { + value|=(uint32_t)siBytes[0]<<8; + length = 2; + } else if (siLength == 2) { + value|=(uint32_t)siBytes[1]<<8; + value|=(uint32_t)siBytes[0]<<16; + length = 3; + } + prevLength=1; + } + } else { + if(prevLength==2) { + length=2; + } else { + /* change from single-byte mode to double-byte */ + if (soLength == 1) { + value|=(uint32_t)soBytes[0]<<16; + length = 3; + } else if (soLength == 2) { + value|=(uint32_t)soBytes[1]<<16; + value|=(uint32_t)soBytes[0]<<24; + length = 4; + } + prevLength=2; + } + } + break; + case MBCS_OUTPUT_DBCS_ONLY: + /* table with single-byte results, but only DBCS mappings used */ + value=((const uint16_t *)bytes)[value +(c&0x3f)]; + if(value<=0xff) { + /* no mapping or SBCS result, not taken for DBCS-only */ + goto unassigned; + } else { + length=2; + } + break; + case MBCS_OUTPUT_3: + p=bytes+(value+(c&0x3f))*3; + value=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; + if(value<=0xff) { + if(value==0) { + goto unassigned; + } else { + length=1; + } + } else if(value<=0xffff) { + length=2; + } else { + length=3; + } + break; + case MBCS_OUTPUT_4: + value=((const uint32_t *)bytes)[value +(c&0x3f)]; + if(value<=0xff) { + if(value==0) { + goto unassigned; + } else { + length=1; + } + } else if(value<=0xffff) { + length=2; + } else if(value<=0xffffff) { + length=3; + } else { + length=4; + } + break; + case MBCS_OUTPUT_3_EUC: + value=((const uint16_t *)bytes)[value +(c&0x3f)]; + /* EUC 16-bit fixed-length representation */ + if(value<=0xff) { + if(value==0) { + goto unassigned; + } else { + length=1; + } + } else if((value&0x8000)==0) { + value|=0x8e8000; + length=3; + } else if((value&0x80)==0) { + value|=0x8f0080; + length=3; + } else { + length=2; + } + break; + case MBCS_OUTPUT_4_EUC: + p=bytes+(value+(c&0x3f))*3; + value=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; + /* EUC 16-bit fixed-length representation applied to the first two bytes */ + if(value<=0xff) { + if(value==0) { + goto unassigned; + } else { + length=1; + } + } else if(value<=0xffff) { + length=2; + } else if((value&0x800000)==0) { + value|=0x8e800000; + length=4; + } else if((value&0x8000)==0) { + value|=0x8f008000; + length=4; + } else { + length=3; + } + break; + default: + /* must not occur */ + /* + * To avoid compiler warnings that value & length may be + * used without having been initialized, we set them here. + * In reality, this is unreachable code. + * Not having a default branch also causes warnings with + * some compilers. + */ + value=0; + length=0; + break; + } + /* output the value */ + } else { + /* + * This also tests if the codepage maps single surrogates. + * If it does, then surrogates are not paired but mapped separately. + * Note that in this case unmatched surrogates are not detected. + */ + if(U16_IS_SURROGATE(c) && !(unicodeMask&UCNV_HAS_SURROGATES)) { + if(U16_IS_SURROGATE_LEAD(c)) { +getTrail: + if(sourcefromUnicodeStatus=prevLength; /* save the old state */ + /* callback(unassigned) */ + goto unassigned; + } + /* convert this supplementary code point */ + /* exit this condition tree */ + } else { + /* this is an unmatched lead code unit (1st surrogate) */ + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } + } else { + /* no more input */ + break; + } + } else { + /* this is an unmatched trail code unit (2nd surrogate) */ + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + break; + } + } + + /* convert the Unicode code point in c into codepage bytes */ + + /* + * The basic lookup is a triple-stage compact array (trie) lookup. + * For details see the beginning of this file. + * + * Single-byte codepages are handled with a different data structure + * by _MBCSSingle... functions. + * + * The result consists of a 32-bit value from stage 2 and + * a pointer to as many bytes as are stored per character. + * The pointer points to the character's bytes in stage 3. + * Bits 15..0 of the stage 2 entry contain the stage 3 index + * for that pointer, while bits 31..16 are flags for which of + * the 16 characters in the block are roundtrip-assigned. + * + * For 2-byte and 4-byte codepages, the bytes are stored as uint16_t + * respectively as uint32_t, in the platform encoding. + * For 3-byte codepages, the bytes are always stored in big-endian order. + * + * For EUC encodings that use only either 0x8e or 0x8f as the first + * byte of their longest byte sequences, the first two bytes in + * this third stage indicate with their 7th bits whether these bytes + * are to be written directly or actually need to be preceded by + * one of the two Single-Shift codes. With this, the third stage + * stores one byte fewer per character than the actual maximum length of + * EUC byte sequences. + * + * Other than that, leading zero bytes are removed and the other + * bytes output. A single zero byte may be output if the "assigned" + * bit in stage 2 was on. + * The data structure does not support zero byte output as a fallback, + * and also does not allow output of leading zeros. + */ + stage2Entry=MBCS_STAGE_2_FROM_U(table, c); + + /* get the bytes and the length for the output */ + switch(outputType) { + case MBCS_OUTPUT_2: + value=MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, c); + if(value<=0xff) { + length=1; + } else { + length=2; + } + break; + case MBCS_OUTPUT_2_SISO: + /* 1/2-byte stateful with Shift-In/Shift-Out */ + /* + * Save the old state in the converter object + * right here, then change the local prevLength state variable if necessary. + * Then, if this character turns out to be unassigned or a fallback that + * is not taken, the callback code must not save the new state in the converter + * because the new state is for a character that is not output. + * However, the callback must still restore the state from the converter + * in case the callback function changed it for its output. + */ + cnv->fromUnicodeStatus=prevLength; /* save the old state */ + value=MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, c); + if(value<=0xff) { + if(value==0 && MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c)==0) { + /* no mapping, leave value==0 */ + length=0; + } else if(prevLength<=1) { + length=1; + } else { + /* change from double-byte mode to single-byte */ + if (siLength == 1) { + value|=(uint32_t)siBytes[0]<<8; + length = 2; + } else if (siLength == 2) { + value|=(uint32_t)siBytes[1]<<8; + value|=(uint32_t)siBytes[0]<<16; + length = 3; + } + prevLength=1; + } + } else { + if(prevLength==2) { + length=2; + } else { + /* change from single-byte mode to double-byte */ + if (soLength == 1) { + value|=(uint32_t)soBytes[0]<<16; + length = 3; + } else if (soLength == 2) { + value|=(uint32_t)soBytes[1]<<16; + value|=(uint32_t)soBytes[0]<<24; + length = 4; + } + prevLength=2; + } + } + break; + case MBCS_OUTPUT_DBCS_ONLY: + /* table with single-byte results, but only DBCS mappings used */ + value=MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, c); + if(value<=0xff) { + /* no mapping or SBCS result, not taken for DBCS-only */ + value=stage2Entry=0; /* stage2Entry=0 to reset roundtrip flags */ + length=0; + } else { + length=2; + } + break; + case MBCS_OUTPUT_3: + p=MBCS_POINTER_3_FROM_STAGE_2(bytes, stage2Entry, c); + value=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; + if(value<=0xff) { + length=1; + } else if(value<=0xffff) { + length=2; + } else { + length=3; + } + break; + case MBCS_OUTPUT_4: + value=MBCS_VALUE_4_FROM_STAGE_2(bytes, stage2Entry, c); + if(value<=0xff) { + length=1; + } else if(value<=0xffff) { + length=2; + } else if(value<=0xffffff) { + length=3; + } else { + length=4; + } + break; + case MBCS_OUTPUT_3_EUC: + value=MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, c); + /* EUC 16-bit fixed-length representation */ + if(value<=0xff) { + length=1; + } else if((value&0x8000)==0) { + value|=0x8e8000; + length=3; + } else if((value&0x80)==0) { + value|=0x8f0080; + length=3; + } else { + length=2; + } + break; + case MBCS_OUTPUT_4_EUC: + p=MBCS_POINTER_3_FROM_STAGE_2(bytes, stage2Entry, c); + value=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; + /* EUC 16-bit fixed-length representation applied to the first two bytes */ + if(value<=0xff) { + length=1; + } else if(value<=0xffff) { + length=2; + } else if((value&0x800000)==0) { + value|=0x8e800000; + length=4; + } else if((value&0x8000)==0) { + value|=0x8f008000; + length=4; + } else { + length=3; + } + break; + default: + /* must not occur */ + /* + * To avoid compiler warnings that value & length may be + * used without having been initialized, we set them here. + * In reality, this is unreachable code. + * Not having a default branch also causes warnings with + * some compilers. + */ + value=stage2Entry=0; /* stage2Entry=0 to reset roundtrip flags */ + length=0; + break; + } + + /* is this code point assigned, or do we use fallbacks? */ + if(!(MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c)!=0 || + (UCNV_FROM_U_USE_FALLBACK(cnv, c) && value!=0)) + ) { + /* + * We allow a 0 byte output if the "assigned" bit is set for this entry. + * There is no way with this data structure for fallback output + * to be a zero byte. + */ + +unassigned: + /* try an extension mapping */ + pArgs->source=source; + c=_extFromU(cnv, cnv->sharedData, + c, &source, sourceLimit, + &target, target+targetCapacity, + &offsets, sourceIndex, + pArgs->flush, + pErrorCode); + nextSourceIndex+=(int32_t)(source-pArgs->source); + prevLength=cnv->fromUnicodeStatus; /* restore SISO state */ + + if(U_FAILURE(*pErrorCode)) { + /* not mappable or buffer overflow */ + break; + } else { + /* a mapping was written to the target, continue */ + + /* recalculate the targetCapacity after an extension mapping */ + targetCapacity=(int32_t)(pArgs->targetLimit-(char *)target); + + /* normal end of conversion: prepare for a new character */ + if(offsets!=nullptr) { + prevSourceIndex=sourceIndex; + sourceIndex=nextSourceIndex; + } + continue; + } + } + } + + /* write the output character bytes from value and length */ + /* from the first if in the loop we know that targetCapacity>0 */ + if(length<=targetCapacity) { + if(offsets==nullptr) { + switch(length) { + /* each branch falls through to the next one */ + case 4: + *target++=(uint8_t)(value>>24); + U_FALLTHROUGH; + case 3: + *target++=(uint8_t)(value>>16); + U_FALLTHROUGH; + case 2: + *target++=(uint8_t)(value>>8); + U_FALLTHROUGH; + case 1: + *target++=(uint8_t)value; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + } else { + switch(length) { + /* each branch falls through to the next one */ + case 4: + *target++=(uint8_t)(value>>24); + *offsets++=sourceIndex; + U_FALLTHROUGH; + case 3: + *target++=(uint8_t)(value>>16); + *offsets++=sourceIndex; + U_FALLTHROUGH; + case 2: + *target++=(uint8_t)(value>>8); + *offsets++=sourceIndex; + U_FALLTHROUGH; + case 1: + *target++=(uint8_t)value; + *offsets++=sourceIndex; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + } + targetCapacity-=length; + } else { + uint8_t *charErrorBuffer; + + /* + * We actually do this backwards here: + * In order to save an intermediate variable, we output + * first to the overflow buffer what does not fit into the + * regular target. + */ + /* we know that 1<=targetCapacitycharErrorBuffer; + switch(length) { + /* each branch falls through to the next one */ + case 3: + *charErrorBuffer++=(uint8_t)(value>>16); + U_FALLTHROUGH; + case 2: + *charErrorBuffer++=(uint8_t)(value>>8); + U_FALLTHROUGH; + case 1: + *charErrorBuffer=(uint8_t)value; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + cnv->charErrorBufferLength=(int8_t)length; + + /* now output what fits into the regular target */ + value>>=8*length; /* length was reduced by targetCapacity */ + switch(targetCapacity) { + /* each branch falls through to the next one */ + case 3: + *target++=(uint8_t)(value>>16); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + U_FALLTHROUGH; + case 2: + *target++=(uint8_t)(value>>8); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + U_FALLTHROUGH; + case 1: + *target++=(uint8_t)value; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + + /* target overflow */ + targetCapacity=0; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + c=0; + break; + } + + /* normal end of conversion: prepare for a new character */ + c=0; + if(offsets!=nullptr) { + prevSourceIndex=sourceIndex; + sourceIndex=nextSourceIndex; + } + continue; + } else { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + + /* + * the end of the input stream and detection of truncated input + * are handled by the framework, but for EBCDIC_STATEFUL conversion + * we need to emit an SI at the very end + * + * conditions: + * successful + * EBCDIC_STATEFUL in DBCS mode + * end of input and no truncated input + */ + if( U_SUCCESS(*pErrorCode) && + outputType==MBCS_OUTPUT_2_SISO && prevLength==2 && + pArgs->flush && source>=sourceLimit && c==0 + ) { + /* EBCDIC_STATEFUL ending with DBCS: emit an SI to return the output stream to SBCS */ + if(targetCapacity>0) { + *target++=(uint8_t)siBytes[0]; + if (siLength == 2) { + if (targetCapacity<2) { + cnv->charErrorBuffer[0]=(uint8_t)siBytes[1]; + cnv->charErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } else { + *target++=(uint8_t)siBytes[1]; + } + } + if(offsets!=nullptr) { + /* set the last source character's index (sourceIndex points at sourceLimit now) */ + *offsets++=prevSourceIndex; + } + } else { + /* target is full */ + cnv->charErrorBuffer[0]=(uint8_t)siBytes[0]; + if (siLength == 2) { + cnv->charErrorBuffer[1]=(uint8_t)siBytes[1]; + } + cnv->charErrorBufferLength=siLength; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + prevLength=1; /* we switched into SBCS */ + } + + /* set the converter state back into UConverter */ + cnv->fromUChar32=c; + cnv->fromUnicodeStatus=prevLength; + + /* write back the updated pointers */ + pArgs->source=source; + pArgs->target=(char *)target; + pArgs->offsets=offsets; +} + +/* + * This is another simple conversion function for internal use by other + * conversion implementations. + * It does not use the converter state nor call callbacks. + * It does not handle the EBCDIC swaplfnl option (set in UConverter). + * It handles conversion extensions but not GB 18030. + * + * It converts one single Unicode code point into codepage bytes, encoded + * as one 32-bit value. The function returns the number of bytes in *pValue: + * 1..4 the number of bytes in *pValue + * 0 unassigned (*pValue undefined) + * -1 illegal (currently not used, *pValue undefined) + * + * *pValue will contain the resulting bytes with the last byte in bits 7..0, + * the second to last byte in bits 15..8, etc. + * Currently, the function assumes but does not check that 0<=c<=0x10ffff. + */ +U_CFUNC int32_t +ucnv_MBCSFromUChar32(UConverterSharedData *sharedData, + UChar32 c, uint32_t *pValue, + UBool useFallback) { + const int32_t *cx; + const uint16_t *table; +#if 0 +/* #if 0 because this is not currently used in ICU - reduce code, increase code coverage */ + const uint8_t *p; +#endif + uint32_t stage2Entry; + uint32_t value; + int32_t length; + + /* BMP-only codepages are stored without stage 1 entries for supplementary code points */ + if(c<=0xffff || (sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY)) { + table=sharedData->mbcs.fromUnicodeTable; + + /* convert the Unicode code point in c into codepage bytes (same as in _MBCSFromUnicodeWithOffsets) */ + if(sharedData->mbcs.outputType==MBCS_OUTPUT_1) { + value=MBCS_SINGLE_RESULT_FROM_U(table, (uint16_t *)sharedData->mbcs.fromUnicodeBytes, c); + /* is this code point assigned, or do we use fallbacks? */ + if(useFallback ? value>=0x800 : value>=0xc00) { + *pValue=value&0xff; + return 1; + } + } else /* outputType!=MBCS_OUTPUT_1 */ { + stage2Entry=MBCS_STAGE_2_FROM_U(table, c); + + /* get the bytes and the length for the output */ + switch(sharedData->mbcs.outputType) { + case MBCS_OUTPUT_2: + value=MBCS_VALUE_2_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); + if(value<=0xff) { + length=1; + } else { + length=2; + } + break; +#if 0 +/* #if 0 because this is not currently used in ICU - reduce code, increase code coverage */ + case MBCS_OUTPUT_DBCS_ONLY: + /* table with single-byte results, but only DBCS mappings used */ + value=MBCS_VALUE_2_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); + if(value<=0xff) { + /* no mapping or SBCS result, not taken for DBCS-only */ + value=stage2Entry=0; /* stage2Entry=0 to reset roundtrip flags */ + length=0; + } else { + length=2; + } + break; + case MBCS_OUTPUT_3: + p=MBCS_POINTER_3_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); + value=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; + if(value<=0xff) { + length=1; + } else if(value<=0xffff) { + length=2; + } else { + length=3; + } + break; + case MBCS_OUTPUT_4: + value=MBCS_VALUE_4_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); + if(value<=0xff) { + length=1; + } else if(value<=0xffff) { + length=2; + } else if(value<=0xffffff) { + length=3; + } else { + length=4; + } + break; + case MBCS_OUTPUT_3_EUC: + value=MBCS_VALUE_2_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); + /* EUC 16-bit fixed-length representation */ + if(value<=0xff) { + length=1; + } else if((value&0x8000)==0) { + value|=0x8e8000; + length=3; + } else if((value&0x80)==0) { + value|=0x8f0080; + length=3; + } else { + length=2; + } + break; + case MBCS_OUTPUT_4_EUC: + p=MBCS_POINTER_3_FROM_STAGE_2(sharedData->mbcs.fromUnicodeBytes, stage2Entry, c); + value=((uint32_t)*p<<16)|((uint32_t)p[1]<<8)|p[2]; + /* EUC 16-bit fixed-length representation applied to the first two bytes */ + if(value<=0xff) { + length=1; + } else if(value<=0xffff) { + length=2; + } else if((value&0x800000)==0) { + value|=0x8e800000; + length=4; + } else if((value&0x8000)==0) { + value|=0x8f008000; + length=4; + } else { + length=3; + } + break; +#endif + default: + /* must not occur */ + return -1; + } + + /* is this code point assigned, or do we use fallbacks? */ + if( MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c) || + (FROM_U_USE_FALLBACK(useFallback, c) && value!=0) + ) { + /* + * We allow a 0 byte output if the "assigned" bit is set for this entry. + * There is no way with this data structure for fallback output + * to be a zero byte. + */ + /* assigned */ + *pValue=value; + return length; + } + } + } + + cx=sharedData->mbcs.extIndexes; + if(cx!=nullptr) { + length=ucnv_extSimpleMatchFromU(cx, c, pValue, useFallback); + return length>=0 ? length : -length; /* return abs(length); */ + } + + /* unassigned */ + return 0; +} + + +#if 0 +/* + * This function has been moved to ucnv2022.c for inlining. + * This implementation is here only for documentation purposes + */ + +/** + * This version of ucnv_MBCSFromUChar32() is optimized for single-byte codepages. + * It does not handle the EBCDIC swaplfnl option (set in UConverter). + * It does not handle conversion extensions (_extFromU()). + * + * It returns the codepage byte for the code point, or -1 if it is unassigned. + */ +U_CFUNC int32_t +ucnv_MBCSSingleFromUChar32(UConverterSharedData *sharedData, + UChar32 c, + UBool useFallback) { + const uint16_t *table; + int32_t value; + + /* BMP-only codepages are stored without stage 1 entries for supplementary code points */ + if(c>=0x10000 && !(sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY)) { + return -1; + } + + /* convert the Unicode code point in c into codepage bytes (same as in _MBCSFromUnicodeWithOffsets) */ + table=sharedData->mbcs.fromUnicodeTable; + + /* get the byte for the output */ + value=MBCS_SINGLE_RESULT_FROM_U(table, (uint16_t *)sharedData->mbcs.fromUnicodeBytes, c); + /* is this code point assigned, or do we use fallbacks? */ + if(useFallback ? value>=0x800 : value>=0xc00) { + return value&0xff; + } else { + return -1; + } +} +#endif + +/* MBCS-from-UTF-8 conversion functions ------------------------------------- */ + +/* offsets for n-byte UTF-8 sequences that were calculated with ((lead<<6)+trail)<<6+trail... */ +static const UChar32 +utf8_offsets[5]={ 0, 0, 0x3080, 0xE2080, 0x3C82080 }; + +static void U_CALLCONV +ucnv_SBCSFromUTF8(UConverterFromUnicodeArgs *pFromUArgs, + UConverterToUnicodeArgs *pToUArgs, + UErrorCode *pErrorCode) { + UConverter *utf8, *cnv; + const uint8_t *source, *sourceLimit; + uint8_t *target; + int32_t targetCapacity; + + const uint16_t *table, *sbcsIndex; + const uint16_t *results; + + int8_t oldToULength, toULength, toULimit; + + UChar32 c; + uint8_t b, t1, t2; + + uint32_t asciiRoundtrips; + uint16_t value, minValue = 0; + UBool hasSupplementary; + + /* set up the local pointers */ + utf8=pToUArgs->converter; + cnv=pFromUArgs->converter; + source=(uint8_t *)pToUArgs->source; + sourceLimit=(uint8_t *)pToUArgs->sourceLimit; + target=(uint8_t *)pFromUArgs->target; + targetCapacity=(int32_t)(pFromUArgs->targetLimit-pFromUArgs->target); + + table=cnv->sharedData->mbcs.fromUnicodeTable; + sbcsIndex=cnv->sharedData->mbcs.sbcsIndex; + if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { + results=(uint16_t *)cnv->sharedData->mbcs.swapLFNLFromUnicodeBytes; + } else { + results=(uint16_t *)cnv->sharedData->mbcs.fromUnicodeBytes; + } + asciiRoundtrips=cnv->sharedData->mbcs.asciiRoundtrips; + + if(cnv->useFallback) { + /* use all roundtrip and fallback results */ + minValue=0x800; + } else { + /* use only roundtrips and fallbacks from private-use characters */ + minValue=0xc00; + } + hasSupplementary=(UBool)(cnv->sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY); + + /* get the converter state from the UTF-8 UConverter */ + if(utf8->toULength > 0) { + toULength=oldToULength=utf8->toULength; + toULimit=(int8_t)utf8->mode; + c=(UChar32)utf8->toUnicodeStatus; + } else { + toULength=oldToULength=toULimit=0; + c = 0; + } + + // The conversion loop checks source0) { + uint8_t b1=*(sourceLimit-1); + if(U8_IS_SINGLE(b1)) { + // common ASCII character + } else if(U8_IS_TRAIL(b1) && length>=2) { + uint8_t b2=*(sourceLimit-2); + if(0xe0<=b2 && b2<0xf0 && U8_IS_VALID_LEAD3_AND_T1(b2, b1)) { + // truncated 3-byte sequence + sourceLimit-=2; + } + } else if(0xc2<=b1 && b1<0xf0) { + // truncated 2- or 3-byte sequence + --sourceLimit; + } + } + } + + if(c!=0 && targetCapacity>0) { + utf8->toUnicodeStatus=0; + utf8->toULength=0; + goto moreBytes; + /* + * Note: We could avoid the goto by duplicating some of the moreBytes + * code, but only up to the point of collecting a complete UTF-8 + * sequence; then recurse for the toUBytes[toULength] + * and then continue with normal conversion. + * + * If so, move this code to just after initializing the minimum + * set of local variables for reading the UTF-8 input + * (utf8, source, target, limits but not cnv, table, minValue, etc.). + * + * Potential advantages: + * - avoid the goto + * - oldToULength could become a local variable in just those code blocks + * that deal with buffer boundaries + * - possibly faster if the goto prevents some compiler optimizations + * (this would need measuring to confirm) + * Disadvantage: + * - code duplication + */ + } + + /* conversion loop */ + while(source0) { + b=*source++; + if(U8_IS_SINGLE(b)) { + /* convert ASCII */ + if(IS_ASCII_ROUNDTRIP(b, asciiRoundtrips)) { + *target++=(uint8_t)b; + --targetCapacity; + continue; + } else { + c=b; + value=SBCS_RESULT_FROM_UTF8(sbcsIndex, results, 0, c); + } + } else { + if(b<0xe0) { + if( /* handle U+0080..U+07FF inline */ + b>=0xc2 && + (t1=(uint8_t)(*source-0x80)) <= 0x3f + ) { + c=b&0x1f; + ++source; + value=SBCS_RESULT_FROM_UTF8(sbcsIndex, results, c, t1); + if(value>=minValue) { + *target++=(uint8_t)value; + --targetCapacity; + continue; + } else { + c=(c<<6)|t1; + } + } else { + c=-1; + } + } else if(b==0xe0) { + if( /* handle U+0800..U+0FFF inline */ + (t1=(uint8_t)(source[0]-0x80)) <= 0x3f && t1 >= 0x20 && + (t2=(uint8_t)(source[1]-0x80)) <= 0x3f + ) { + c=t1; + source+=2; + value=SBCS_RESULT_FROM_UTF8(sbcsIndex, results, c, t2); + if(value>=minValue) { + *target++=(uint8_t)value; + --targetCapacity; + continue; + } else { + c=(c<<6)|t2; + } + } else { + c=-1; + } + } else { + c=-1; + } + + if(c<0) { + /* handle "complicated" and error cases, and continuing partial characters */ + oldToULength=0; + toULength=1; + toULimit=U8_COUNT_BYTES_NON_ASCII(b); + c=b; +moreBytes: + while(toULengthsourceLimit) { + b=*source; + if(icu::UTF8::isValidTrail(c, b, toULength, toULimit)) { + ++source; + ++toULength; + c=(c<<6)+b; + } else { + break; /* sequence too short, stop with toULengthtoUBytes[oldToULength++]=*source++; + } + utf8->toUnicodeStatus=c; + utf8->toULength=toULength; + utf8->mode=toULimit; + pToUArgs->source=(char *)source; + pFromUArgs->target=(char *)target; + return; + } + } + + if(toULength==toULimit) { + c-=utf8_offsets[toULength]; + if(toULength<=3) { /* BMP */ + value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); + } else { + /* supplementary code point */ + if(!hasSupplementary) { + /* BMP-only codepages are stored without stage 1 entries for supplementary code points */ + value=0; + } else { + value=MBCS_SINGLE_RESULT_FROM_U(table, results, c); + } + } + } else { + /* error handling: illegal UTF-8 byte sequence */ + source-=(toULength-oldToULength); + while(oldToULengthtoUBytes[oldToULength++]=*source++; + } + utf8->toULength=toULength; + pToUArgs->source=(char *)source; + pFromUArgs->target=(char *)target; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + return; + } + } + } + + if(value>=minValue) { + /* output the mapping for c */ + *target++=(uint8_t)value; + --targetCapacity; + } else { + /* valueUTF-16->charset conversion. + */ + static const char16_t nul=0; + const char16_t *noSource=&nul; + c=_extFromU(cnv, cnv->sharedData, + c, &noSource, noSource, + &target, target+targetCapacity, + nullptr, -1, + pFromUArgs->flush, + pErrorCode); + + if(U_FAILURE(*pErrorCode)) { + /* not mappable or buffer overflow */ + cnv->fromUChar32=c; + break; + } else if(cnv->preFromUFirstCP>=0) { + /* + * Partial match, return and revert to pivoting. + * In normal from-UTF-16 conversion, we would just continue + * but then exit the loop because the extension match would + * have consumed the source. + */ + *pErrorCode=U_USING_DEFAULT_WARNING; + break; + } else { + /* a mapping was written to the target, continue */ + + /* recalculate the targetCapacity after an extension mapping */ + targetCapacity=(int32_t)(pFromUArgs->targetLimit-(char *)target); + } + } + } else { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + + /* + * The sourceLimit may have been adjusted before the conversion loop + * to stop before a truncated sequence. + * If so, then collect the truncated sequence now. + */ + if(U_SUCCESS(*pErrorCode) && + cnv->preFromUFirstCP<0 && + source<(sourceLimit=(uint8_t *)pToUArgs->sourceLimit)) { + c=utf8->toUBytes[0]=b=*source++; + toULength=1; + toULimit=U8_COUNT_BYTES(b); + while(sourcetoUBytes[toULength++]=b=*source++; + c=(c<<6)+b; + } + utf8->toUnicodeStatus=c; + utf8->toULength=toULength; + utf8->mode=toULimit; + } + + /* write back the updated pointers */ + pToUArgs->source=(char *)source; + pFromUArgs->target=(char *)target; +} + +static void U_CALLCONV +ucnv_DBCSFromUTF8(UConverterFromUnicodeArgs *pFromUArgs, + UConverterToUnicodeArgs *pToUArgs, + UErrorCode *pErrorCode) { + UConverter *utf8, *cnv; + const uint8_t *source, *sourceLimit; + uint8_t *target; + int32_t targetCapacity; + + const uint16_t *table, *mbcsIndex; + const uint16_t *results; + + int8_t oldToULength, toULength, toULimit; + + UChar32 c; + uint8_t b, t1, t2; + + uint32_t stage2Entry; + uint32_t asciiRoundtrips; + uint16_t value = 0; + UBool hasSupplementary; + + /* set up the local pointers */ + utf8=pToUArgs->converter; + cnv=pFromUArgs->converter; + source=(uint8_t *)pToUArgs->source; + sourceLimit=(uint8_t *)pToUArgs->sourceLimit; + target=(uint8_t *)pFromUArgs->target; + targetCapacity=(int32_t)(pFromUArgs->targetLimit-pFromUArgs->target); + + table=cnv->sharedData->mbcs.fromUnicodeTable; + mbcsIndex=cnv->sharedData->mbcs.mbcsIndex; + if((cnv->options&UCNV_OPTION_SWAP_LFNL)!=0) { + results=(uint16_t *)cnv->sharedData->mbcs.swapLFNLFromUnicodeBytes; + } else { + results=(uint16_t *)cnv->sharedData->mbcs.fromUnicodeBytes; + } + asciiRoundtrips=cnv->sharedData->mbcs.asciiRoundtrips; + + hasSupplementary=(UBool)(cnv->sharedData->mbcs.unicodeMask&UCNV_HAS_SUPPLEMENTARY); + + /* get the converter state from the UTF-8 UConverter */ + if(utf8->toULength > 0) { + toULength=oldToULength=utf8->toULength; + toULimit=(int8_t)utf8->mode; + c=(UChar32)utf8->toUnicodeStatus; + } else { + toULength=oldToULength=toULimit=0; + c = 0; + } + + // The conversion loop checks source0) { + uint8_t b1=*(sourceLimit-1); + if(U8_IS_SINGLE(b1)) { + // common ASCII character + } else if(U8_IS_TRAIL(b1) && length>=2) { + uint8_t b2=*(sourceLimit-2); + if(0xe0<=b2 && b2<0xf0 && U8_IS_VALID_LEAD3_AND_T1(b2, b1)) { + // truncated 3-byte sequence + sourceLimit-=2; + } + } else if(0xc2<=b1 && b1<0xf0) { + // truncated 2- or 3-byte sequence + --sourceLimit; + } + } + } + + if(c!=0 && targetCapacity>0) { + utf8->toUnicodeStatus=0; + utf8->toULength=0; + goto moreBytes; + /* See note in ucnv_SBCSFromUTF8() about this goto. */ + } + + /* conversion loop */ + while(source0) { + b=*source++; + if(U8_IS_SINGLE(b)) { + /* convert ASCII */ + if(IS_ASCII_ROUNDTRIP(b, asciiRoundtrips)) { + *target++=b; + --targetCapacity; + continue; + } else { + value=DBCS_RESULT_FROM_UTF8(mbcsIndex, results, 0, b); + if(value==0) { + c=b; + goto unassigned; + } + } + } else { + if(b>=0xe0) { + if( /* handle U+0800..U+D7FF inline */ + b<=0xed && // do not assume maxFastUChar>0xd7ff + U8_IS_VALID_LEAD3_AND_T1(b, t1=source[0]) && + (t2=(uint8_t)(source[1]-0x80)) <= 0x3f + ) { + c=((b&0xf)<<6)|(t1&0x3f); + source+=2; + value=DBCS_RESULT_FROM_UTF8(mbcsIndex, results, c, t2); + if(value==0) { + c=(c<<6)|t2; + goto unassigned; + } + } else { + c=-1; + } + } else { + if( /* handle U+0080..U+07FF inline */ + b>=0xc2 && + (t1=(uint8_t)(*source-0x80)) <= 0x3f + ) { + c=b&0x1f; + ++source; + value=DBCS_RESULT_FROM_UTF8(mbcsIndex, results, c, t1); + if(value==0) { + c=(c<<6)|t1; + goto unassigned; + } + } else { + c=-1; + } + } + + if(c<0) { + /* handle "complicated" and error cases, and continuing partial characters */ + oldToULength=0; + toULength=1; + toULimit=U8_COUNT_BYTES_NON_ASCII(b); + c=b; +moreBytes: + while(toULengthsourceLimit) { + b=*source; + if(icu::UTF8::isValidTrail(c, b, toULength, toULimit)) { + ++source; + ++toULength; + c=(c<<6)+b; + } else { + break; /* sequence too short, stop with toULengthtoUBytes[oldToULength++]=*source++; + } + utf8->toUnicodeStatus=c; + utf8->toULength=toULength; + utf8->mode=toULimit; + pToUArgs->source=(char *)source; + pFromUArgs->target=(char *)target; + return; + } + } + + if(toULength==toULimit) { + c-=utf8_offsets[toULength]; + if(toULength<=3) { /* BMP */ + stage2Entry=MBCS_STAGE_2_FROM_U(table, c); + } else { + /* supplementary code point */ + if(!hasSupplementary) { + /* BMP-only codepages are stored without stage 1 entries for supplementary code points */ + stage2Entry=0; + } else { + stage2Entry=MBCS_STAGE_2_FROM_U(table, c); + } + } + } else { + /* error handling: illegal UTF-8 byte sequence */ + source-=(toULength-oldToULength); + while(oldToULengthtoUBytes[oldToULength++]=*source++; + } + utf8->toULength=toULength; + pToUArgs->source=(char *)source; + pFromUArgs->target=(char *)target; + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + return; + } + + /* get the bytes and the length for the output */ + /* MBCS_OUTPUT_2 */ + value=MBCS_VALUE_2_FROM_STAGE_2(results, stage2Entry, c); + + /* is this code point assigned, or do we use fallbacks? */ + if(!(MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c) || + (UCNV_FROM_U_USE_FALLBACK(cnv, c) && value!=0)) + ) { + goto unassigned; + } + } + } + + /* write the output character bytes from value and length */ + /* from the first if in the loop we know that targetCapacity>0 */ + if(value<=0xff) { + /* this is easy because we know that there is enough space */ + *target++=(uint8_t)value; + --targetCapacity; + } else /* length==2 */ { + *target++=(uint8_t)(value>>8); + if(2<=targetCapacity) { + *target++=(uint8_t)value; + targetCapacity-=2; + } else { + cnv->charErrorBuffer[0]=(char)value; + cnv->charErrorBufferLength=1; + + /* target overflow */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + continue; + +unassigned: + { + /* + * Try an extension mapping. + * Pass in no source because we don't have UTF-16 input. + * If we have a partial match on c, we will return and revert + * to UTF-8->UTF-16->charset conversion. + */ + static const char16_t nul=0; + const char16_t *noSource=&nul; + c=_extFromU(cnv, cnv->sharedData, + c, &noSource, noSource, + &target, target+targetCapacity, + nullptr, -1, + pFromUArgs->flush, + pErrorCode); + + if(U_FAILURE(*pErrorCode)) { + /* not mappable or buffer overflow */ + cnv->fromUChar32=c; + break; + } else if(cnv->preFromUFirstCP>=0) { + /* + * Partial match, return and revert to pivoting. + * In normal from-UTF-16 conversion, we would just continue + * but then exit the loop because the extension match would + * have consumed the source. + */ + *pErrorCode=U_USING_DEFAULT_WARNING; + break; + } else { + /* a mapping was written to the target, continue */ + + /* recalculate the targetCapacity after an extension mapping */ + targetCapacity=(int32_t)(pFromUArgs->targetLimit-(char *)target); + continue; + } + } + } else { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + } + + /* + * The sourceLimit may have been adjusted before the conversion loop + * to stop before a truncated sequence. + * If so, then collect the truncated sequence now. + */ + if(U_SUCCESS(*pErrorCode) && + cnv->preFromUFirstCP<0 && + source<(sourceLimit=(uint8_t *)pToUArgs->sourceLimit)) { + c=utf8->toUBytes[0]=b=*source++; + toULength=1; + toULimit=U8_COUNT_BYTES(b); + while(sourcetoUBytes[toULength++]=b=*source++; + c=(c<<6)+b; + } + utf8->toUnicodeStatus=c; + utf8->toULength=toULength; + utf8->mode=toULimit; + } + + /* write back the updated pointers */ + pToUArgs->source=(char *)source; + pFromUArgs->target=(char *)target; +} + +/* miscellaneous ------------------------------------------------------------ */ + +static void U_CALLCONV +ucnv_MBCSGetStarters(const UConverter* cnv, + UBool starters[256], + UErrorCode *) { + const int32_t *state0; + int i; + + state0=cnv->sharedData->mbcs.stateTable[cnv->sharedData->mbcs.dbcsOnlyState]; + for(i=0; i<256; ++i) { + /* all bytes that cause a state transition from state 0 are lead bytes */ + starters[i]= (UBool)MBCS_ENTRY_IS_TRANSITION(state0[i]); + } +} + +/* + * This is an internal function that allows other converter implementations + * to check whether a byte is a lead byte. + */ +U_CFUNC UBool +ucnv_MBCSIsLeadByte(UConverterSharedData *sharedData, char byte) { + return (UBool)MBCS_ENTRY_IS_TRANSITION(sharedData->mbcs.stateTable[0][(uint8_t)byte]); +} + +static void U_CALLCONV +ucnv_MBCSWriteSub(UConverterFromUnicodeArgs *pArgs, + int32_t offsetIndex, + UErrorCode *pErrorCode) { + UConverter *cnv=pArgs->converter; + char *p, *subchar; + char buffer[4]; + int32_t length; + + /* first, select between subChar and subChar1 */ + if( cnv->subChar1!=0 && + (cnv->sharedData->mbcs.extIndexes!=nullptr ? + cnv->useSubChar1 : + (cnv->invalidUCharBuffer[0]<=0xff)) + ) { + /* select subChar1 if it is set (not 0) and the unmappable Unicode code point is up to U+00ff (IBM MBCS behavior) */ + subchar=(char *)&cnv->subChar1; + length=1; + } else { + /* select subChar in all other cases */ + subchar=(char *)cnv->subChars; + length=cnv->subCharLen; + } + + /* reset the selector for the next code point */ + cnv->useSubChar1=false; + + if (cnv->sharedData->mbcs.outputType == MBCS_OUTPUT_2_SISO) { + p=buffer; + + /* fromUnicodeStatus contains prevLength */ + switch(length) { + case 1: + if(cnv->fromUnicodeStatus==2) { + /* DBCS mode and SBCS sub char: change to SBCS */ + cnv->fromUnicodeStatus=1; + *p++=UCNV_SI; + } + *p++=subchar[0]; + break; + case 2: + if(cnv->fromUnicodeStatus<=1) { + /* SBCS mode and DBCS sub char: change to DBCS */ + cnv->fromUnicodeStatus=2; + *p++=UCNV_SO; + } + *p++=subchar[0]; + *p++=subchar[1]; + break; + default: + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + subchar=buffer; + length=(int32_t)(p-buffer); + } + + ucnv_cbFromUWriteBytes(pArgs, subchar, length, offsetIndex, pErrorCode); +} + +U_CFUNC UConverterType +ucnv_MBCSGetType(const UConverter* converter) { + /* SBCS, DBCS, and EBCDIC_STATEFUL are replaced by MBCS, but here we cheat a little */ + if(converter->sharedData->mbcs.countStates==1) { + return (UConverterType)UCNV_SBCS; + } else if((converter->sharedData->mbcs.outputType&0xff)==MBCS_OUTPUT_2_SISO) { + return (UConverterType)UCNV_EBCDIC_STATEFUL; + } else if(converter->sharedData->staticData->minBytesPerChar==2 && converter->sharedData->staticData->maxBytesPerChar==2) { + return (UConverterType)UCNV_DBCS; + } + return (UConverterType)UCNV_MBCS; +} + +#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ diff --git a/deps/icu-small/source/common/ucnvmbcs.h b/deps/icu-small/source/common/ucnvmbcs.h index c8f3b89a5e464d..7b375ff3d6a029 100644 --- a/deps/icu-small/source/common/ucnvmbcs.h +++ b/deps/icu-small/source/common/ucnvmbcs.h @@ -1,605 +1,605 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2000-2013, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: ucnvmbcs.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2000jul07 -* created by: Markus W. Scherer -*/ - -#ifndef __UCNVMBCS_H__ -#define __UCNVMBCS_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ucnv.h" -#include "ucnv_cnv.h" -#include "ucnv_ext.h" - -/** - * ICU conversion (.cnv) data file structure, following the usual UDataInfo - * header. - * - * Format version: 6.2 - * - * struct UConverterStaticData -- struct containing the converter name, IBM CCSID, - * min/max bytes per character, etc. - * see ucnv_bld.h - * - * -------------------- - * - * The static data is followed by conversionType-specific data structures. - * At the moment, there are only variations of MBCS converters. They all have - * the same toUnicode structures, while the fromUnicode structures for SBCS - * differ from those for other MBCS-style converters. - * - * _MBCSHeader.version 5 is optional and not backward-compatible - * (as usual for changes in the major version field). - * - * Versions 5.m work like versions 4.m except: - * - The _MBCSHeader has variable length (and is always longer than in version 4). - * See the struct _MBCSHeader further description below. - * - There is a set of flags which indicate further incompatible changes. - * (Reader code must reject the file if it does not recognize them all.) - * - In particular, one of these flags indicates that most of the fromUnicode - * data is missing and must be reconstituted from the toUnicode data - * and from the utf8Friendly mbcsIndex at load time. - * (This only works with a utf8Friendly table.) - * In this case, makeconv may increase maxFastUChar automatically to U+FFFF. - * - * The first of these versions is 5.3, which is like 4.3 except for the differences above. - * - * When possible, makeconv continues to generate version 4.m files. - * - * _MBCSHeader.version 5.4/4.4 supports "good one-way" mappings (|4) - * in the extension tables (fromUTableValues bit 30). See ucnv_ext.h for details. - * - * _MBCSHeader.version 4.3 optionally modifies the fromUnicode data structures - * slightly and optionally adds a table for conversion to MBCS (non-SBCS) - * charsets. - * - * The modifications are to make the data utf8Friendly. Not every 4.3 file - * file contains utf8Friendly data. - * It is utf8Friendly if _MBCSHeader.version[2]!=0. - * In this case, the data structures are utf8Friendly up to the code point - * maxFastUChar=((_MBCSHeader.version[2]<<8)|0xff) - * - * A utf8Friendly file has fromUnicode stage 3 entries for code points up to - * maxFastUChar allocated in blocks of 64 for indexing with the 6 bits from - * a UTF-8 trail byte. ASCII is allocated linearly with 128 contiguous entries. - * - * In addition, a utf8Friendly MBCS file contains an additional - * uint16_t mbcsIndex[(maxFastUChar+1)>>6]; - * which replaces the stage 1 and 2 tables for indexing with bits from the - * UTF-8 lead byte and middle trail byte. Unlike the older MBCS stage 2 table, - * the mbcsIndex does not contain roundtrip flags. Therefore, all fallbacks - * from code points up to maxFastUChar (and roundtrips to 0x00) are moved to - * the extension data structure. This also allows for faster roundtrip - * conversion from UTF-16. - * - * SBCS files do not contain an additional sbcsIndex[] array because the - * proportional size increase would be noticeable, but the runtime - * code builds one for the code point range for which the runtime conversion - * code is optimized. - * - * For SBCS, maxFastUChar should be at least U+0FFF. The initial makeconv - * implementation sets it to U+1FFF. Because the sbcsIndex is not stored in - * the file, a larger maxFastUChar only affects stage 3 block allocation size - * and is free in empty blocks. (Larger blocks with sparse contents cause larger - * files.) U+1FFF includes almost all of the small scripts. - * U+0FFF covers UTF-8 two-byte sequences and three-byte sequences starting with - * 0xe0. This includes most scripts with legacy SBCS charsets. - * The initial runtime implementation using 4.3 files only builds an sbcsIndex - * for code points up to U+0FFF. - * - * For MBCS, maxFastUChar should be at least U+D7FF (=initial value). - * This boundary is convenient because practically all of the commonly used - * characters are below it, and because it is the boundary to surrogate - * code points, above which special handling is necessary anyway. - * (Surrogate pair assembly for UTF-16, validity checking for UTF-8.) - * - * maxFastUChar could be up to U+FFFF to cover the whole BMP, which could be - * useful especially for conversion from UTF-8 when the input can be assumed - * to be valid, because the surrogate range would then not have to be - * checked. - * (With maxFastUChar=0xffff, makeconv would have to check for mbcsIndex value - * overflow because with the all-unassigned block 0 and nearly full mappings - * from the BMP it is theoretically possible that an index into stage 3 - * exceeds 16 bits.) - * - * _MBCSHeader.version 4.2 adds an optional conversion extension data structure. - * If it is present, then an ICU version reading header versions 4.0 or 4.1 - * will be able to use the base table and ignore the extension. - * - * The unicodeMask in the static data is part of the base table data structure. - * Especially, the UCNV_HAS_SUPPLEMENTARY flag determines the length of the - * fromUnicode stage 1 array. - * The static data unicodeMask refers only to the base table's properties if - * a base table is included. - * In an extension-only file, the static data unicodeMask is 0. - * The extension data indexes have a separate field with the unicodeMask flags. - * - * MBCS-style data structure following the static data. - * Offsets are counted in bytes from the beginning of the MBCS header structure. - * Details about usage in comments in ucnvmbcs.c. - * - * struct _MBCSHeader (see the definition in this header file below) - * contains 32-bit fields as follows: - * 8 values: - * 0 uint8_t[4] MBCS version in UVersionInfo format (currently 4.3.x.0) - * 1 uint32_t countStates - * 2 uint32_t countToUFallbacks - * 3 uint32_t offsetToUCodeUnits - * 4 uint32_t offsetFromUTable - * 5 uint32_t offsetFromUBytes - * 6 uint32_t flags, bits: - * 31.. 8 offsetExtension -- _MBCSHeader.version 4.2 (ICU 2.8) and higher - * 0 for older versions and if - * there is not extension structure - * 7.. 0 outputType - * 7 uint32_t fromUBytesLength -- _MBCSHeader.version 4.1 (ICU 2.4) and higher - * counts bytes in fromUBytes[] - * - * New and required in version 5: - * 8 uint32_t options, bits: - * 31..16 reserved for flags that can be added without breaking - * backward compatibility - * 15.. 6 reserved for flags whose addition will break - * backward compatibility - * 6 MBCS_OPT_FROM_U -- if set, - * then most of the fromUnicode data is omitted; - * fullStage2Length is present and the missing - * bottom part of stage 2 must be reconstituted from - * the toUnicode data; - * stage 3 is missing completely as well; - * not used for SBCS tables - * 5.. 0 length of the _MBCSHeader (number of uint32_t) - * - * New and optional in version 5: - * 9 uint32_t fullStage2Length: used if MBCS_OPT_FROM_U is set - * specifies the full length of stage 2 - * including the omitted part - * - * if(outputType==MBCS_OUTPUT_EXT_ONLY) { - * -- base table name for extension-only table - * char baseTableName[variable]; -- with NUL plus padding for 4-alignment - * - * -- all _MBCSHeader fields except for version and flags are 0 - * } else { - * -- normal base table with optional extension - * - * int32_t stateTable[countStates][256]; - * - * struct _MBCSToUFallback { (fallbacks are sorted by offset) - * uint32_t offset; - * UChar32 codePoint; - * } toUFallbacks[countToUFallbacks]; - * - * uint16_t unicodeCodeUnits[(offsetFromUTable-offsetToUCodeUnits)/2]; - * (padded to an even number of units) - * - * -- stage 1 tables - * if(staticData.unicodeMask&UCNV_HAS_SUPPLEMENTARY) { - * -- stage 1 table for all of Unicode - * uint16_t fromUTable[0x440]; (32-bit-aligned) - * } else { - * -- BMP-only tables have a smaller stage 1 table - * uint16_t fromUTable[0x40]; (32-bit-aligned) - * } - * - * -- stage 2 tables - * length determined by top of stage 1 and bottom of stage 3 tables - * if(outputType==MBCS_OUTPUT_1) { - * -- SBCS: pure indexes - * uint16_t stage 2 indexes[?]; - * } else { - * -- DBCS, MBCS, EBCDIC_STATEFUL, ...: roundtrip flags and indexes - * uint32_t stage 2 flags and indexes[?]; - * if(options&MBCS_OPT_NO_FROM_U) { - * stage 2 really has length fullStage2Length - * and the omitted lower part must be reconstituted from - * the toUnicode data - * } - * } - * - * -- stage 3 tables with byte results - * if(outputType==MBCS_OUTPUT_1) { - * -- SBCS: each 16-bit result contains flags and the result byte, see ucnvmbcs.c - * uint16_t fromUBytes[fromUBytesLength/2]; - * } else if(!(options&MBCS_OPT_NO_FROM_U)) { - * -- DBCS, MBCS, EBCDIC_STATEFUL, ... 2/3/4 bytes result, see ucnvmbcs.c - * uint8_t fromUBytes[fromUBytesLength]; or - * uint16_t fromUBytes[fromUBytesLength/2]; or - * uint32_t fromUBytes[fromUBytesLength/4]; - * } else { - * fromUBytes[] must be reconstituted from the toUnicode data - * } - * - * -- optional utf8Friendly mbcsIndex -- _MBCSHeader.version 4.3 (ICU 3.8) and higher - * if(outputType!=MBCS_OUTPUT_1 && - * _MBCSHeader.version[1]>=3 && - * (maxFastUChar=_MBCSHeader.version[2])!=0 - * ) { - * maxFastUChar=(maxFastUChar<<8)|0xff; - * uint16_t mbcsIndex[(maxFastUChar+1)>>6]; - * } - * } - * - * -- extension table, details see ucnv_ext.h - * int32_t indexes[>=32]; ... - */ - -/* MBCS converter data and state -------------------------------------------- */ - -enum { - MBCS_MAX_STATE_COUNT=128 -}; - -/** - * MBCS action codes for conversions to Unicode. - * These values are in bits 23..20 of the state table entries. - */ -enum { - MBCS_STATE_VALID_DIRECT_16, - MBCS_STATE_VALID_DIRECT_20, - - MBCS_STATE_FALLBACK_DIRECT_16, - MBCS_STATE_FALLBACK_DIRECT_20, - - MBCS_STATE_VALID_16, - MBCS_STATE_VALID_16_PAIR, - - MBCS_STATE_UNASSIGNED, - MBCS_STATE_ILLEGAL, - - MBCS_STATE_CHANGE_ONLY -}; - -/* Macros for state table entries */ -#define MBCS_ENTRY_TRANSITION(state, offset) (int32_t)(((int32_t)(state)<<24L)|(offset)) -#define MBCS_ENTRY_TRANSITION_SET_OFFSET(entry, offset) (int32_t)(((entry)&0xff000000)|(offset)) -#define MBCS_ENTRY_TRANSITION_ADD_OFFSET(entry, offset) (int32_t)((entry)+(offset)) - -#define MBCS_ENTRY_FINAL(state, action, value) (int32_t)(0x80000000|((int32_t)(state)<<24L)|((action)<<20L)|(value)) -#define MBCS_ENTRY_SET_FINAL(entry) (int32_t)((entry)|0x80000000) -#define MBCS_ENTRY_FINAL_SET_ACTION(entry, action) (int32_t)(((entry)&0xff0fffff)|((int32_t)(action)<<20L)) -#define MBCS_ENTRY_FINAL_SET_VALUE(entry, value) (int32_t)(((entry)&0xfff00000)|(value)) -#define MBCS_ENTRY_FINAL_SET_ACTION_VALUE(entry, action, value) (int32_t)(((entry)&0xff000000)|((int32_t)(action)<<20L)|(value)) - -#define MBCS_ENTRY_SET_STATE(entry, state) (int32_t)(((entry)&0x80ffffff)|((int32_t)(state)<<24L)) - -#define MBCS_ENTRY_STATE(entry) ((((uint32_t)entry)>>24)&0x7f) - -#define MBCS_ENTRY_IS_TRANSITION(entry) ((entry)>=0) -#define MBCS_ENTRY_IS_FINAL(entry) ((entry)<0) - -#define MBCS_ENTRY_TRANSITION_STATE(entry) (((uint32_t)entry)>>24) -#define MBCS_ENTRY_TRANSITION_OFFSET(entry) ((entry)&0xffffff) - -#define MBCS_ENTRY_FINAL_STATE(entry) ((((uint32_t)entry)>>24)&0x7f) -#define MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(entry) ((entry)<(int32_t)0x80100000) -#define MBCS_ENTRY_FINAL_ACTION(entry) ((((uint32_t)entry)>>20)&0xf) -#define MBCS_ENTRY_FINAL_VALUE(entry) ((entry)&0xfffff) -#define MBCS_ENTRY_FINAL_VALUE_16(entry) (uint16_t)(entry) - -#define IS_ASCII_ROUNDTRIP(b, asciiRoundtrips) (((asciiRoundtrips) & (1<<((b)>>2)))!=0) - -/* single-byte fromUnicode: get the 16-bit result word */ -#define MBCS_SINGLE_RESULT_FROM_U(table, results, c) (results)[ (table)[ (table)[(c)>>10] +(((c)>>4)&0x3f) ] +((c)&0xf) ] - -/* single-byte fromUnicode using the sbcsIndex */ -#define SBCS_RESULT_FROM_LOW_BMP(table, results, c) (results)[ (table)[(c)>>6] +((c)&0x3f) ] - -/* single-byte fromUTF8 using the sbcsIndex; l and t must be masked externally; can be l=0 and t<=0x7f */ -#define SBCS_RESULT_FROM_UTF8(table, results, l, t) (results)[ (table)[l] +(t) ] - -/* multi-byte fromUnicode: get the 32-bit stage 2 entry */ -#define MBCS_STAGE_2_FROM_U(table, c) ((const uint32_t *)(table))[ (table)[(c)>>10] +(((c)>>4)&0x3f) ] -#define MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c) ( ((stage2Entry) & ((uint32_t)1<< (16+((c)&0xf)) )) !=0) - -#define MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, c) ((uint16_t *)(bytes))[16*(uint32_t)(uint16_t)(stage2Entry)+((c)&0xf)] -#define MBCS_VALUE_4_FROM_STAGE_2(bytes, stage2Entry, c) ((uint32_t *)(bytes))[16*(uint32_t)(uint16_t)(stage2Entry)+((c)&0xf)] - -#define MBCS_POINTER_3_FROM_STAGE_2(bytes, stage2Entry, c) ((bytes)+(16*(uint32_t)(uint16_t)(stage2Entry)+((c)&0xf))*3) - -/* double-byte fromUnicode using the mbcsIndex */ -#define DBCS_RESULT_FROM_MOST_BMP(table, results, c) (results)[ (table)[(c)>>6] +((c)&0x3f) ] - -/* double-byte fromUTF8 using the mbcsIndex; l and t1 combined into lt1; lt1 and t2 must be masked externally */ -#define DBCS_RESULT_FROM_UTF8(table, results, lt1, t2) (results)[ (table)[lt1] +(t2) ] - - -/** - * MBCS output types for conversions from Unicode. - * These per-converter types determine the storage method in stage 3 of the lookup table, - * mostly how many bytes are stored per entry. - */ -enum { - MBCS_OUTPUT_1, /* 0 */ - MBCS_OUTPUT_2, /* 1 */ - MBCS_OUTPUT_3, /* 2 */ - MBCS_OUTPUT_4, /* 3 */ - - MBCS_OUTPUT_3_EUC=8, /* 8 */ - MBCS_OUTPUT_4_EUC, /* 9 */ - - MBCS_OUTPUT_2_SISO=12, /* c */ - MBCS_OUTPUT_2_HZ, /* d */ - - MBCS_OUTPUT_EXT_ONLY, /* e */ - - MBCS_OUTPUT_COUNT, - - MBCS_OUTPUT_DBCS_ONLY=0xdb /* runtime-only type for DBCS-only handling of SISO tables */ -}; - -/** - * Fallbacks to Unicode are stored outside the normal state table and code point structures - * in a vector of items of this type. They are sorted by offset. - */ -typedef struct { - uint32_t offset; - UChar32 codePoint; -} _MBCSToUFallback; - -/** Constants for fast and UTF-8-friendly conversion. */ -enum { - SBCS_FAST_MAX=0x0fff, /* maximum code point with UTF-8-friendly SBCS runtime code, see makeconv SBCS_UTF8_MAX */ - SBCS_FAST_LIMIT=SBCS_FAST_MAX+1, /* =0x1000 */ - MBCS_FAST_MAX=0xd7ff, /* maximum code point with UTF-8-friendly MBCS runtime code, see makeconv MBCS_UTF8_MAX */ - MBCS_FAST_LIMIT=MBCS_FAST_MAX+1 /* =0xd800 */ -}; - -/** - * This is the MBCS part of the UConverterTable union (a runtime data structure). - * It keeps all the per-converter data and points into the loaded mapping tables. - * - * utf8Friendly data structures added with _MBCSHeader.version 4.3 - */ -typedef struct UConverterMBCSTable { - /* toUnicode */ - uint8_t countStates, dbcsOnlyState, stateTableOwned; - uint32_t countToUFallbacks; - - const int32_t (*stateTable)/*[countStates]*/[256]; - int32_t (*swapLFNLStateTable)/*[countStates]*/[256]; /* for swaplfnl */ - const uint16_t *unicodeCodeUnits/*[countUnicodeResults]*/; - const _MBCSToUFallback *toUFallbacks; - - /* fromUnicode */ - const uint16_t *fromUnicodeTable; - const uint16_t *mbcsIndex; /* for fast conversion from most of BMP to MBCS (utf8Friendly data) */ - uint16_t sbcsIndex[SBCS_FAST_LIMIT>>6]; /* for fast conversion from low BMP to SBCS (utf8Friendly data) */ - const uint8_t *fromUnicodeBytes; - uint8_t *swapLFNLFromUnicodeBytes; /* for swaplfnl */ - uint32_t fromUBytesLength; - uint8_t outputType, unicodeMask; - UBool utf8Friendly; /* for utf8Friendly data */ - UChar maxFastUChar; /* for utf8Friendly data */ - - /* roundtrips */ - uint32_t asciiRoundtrips; - - /* reconstituted data that was omitted from the .cnv file */ - uint8_t *reconstitutedData; - - /* converter name for swaplfnl */ - char *swapLFNLName; - - /* extension data */ - struct UConverterSharedData *baseSharedData; - const int32_t *extIndexes; -} UConverterMBCSTable; - -#define UCNV_MBCS_TABLE_INITIALIZER { \ - /* toUnicode */ \ - 0, 0, 0, \ - 0, \ - \ - NULL, \ - NULL, \ - NULL, \ - NULL, \ - \ - /* fromUnicode */ \ - NULL, \ - NULL, \ - { 0 }, \ - NULL, \ - NULL, \ - 0, \ - 0, 0, \ - false, \ - 0, \ - \ - /* roundtrips */ \ - 0, \ - \ - /* reconstituted data that was omitted from the .cnv file */ \ - NULL, \ - \ - /* converter name for swaplfnl */ \ - NULL, \ - \ - /* extension data */ \ - NULL, \ - NULL \ -} - -enum { - MBCS_OPT_LENGTH_MASK=0x3f, - MBCS_OPT_NO_FROM_U=0x40, - /* - * If any of the following options bits are set, - * then the file must be rejected. - */ - MBCS_OPT_INCOMPATIBLE_MASK=0xffc0, - /* - * Remove bits from this mask as more options are recognized - * by all implementations that use this constant. - */ - MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK=0xff80 -}; - -enum { - MBCS_HEADER_V4_LENGTH=8, - MBCS_HEADER_V5_MIN_LENGTH=9 -}; - -/** - * MBCS data header. See data format description above. - */ -typedef struct { - UVersionInfo version; - uint32_t countStates, - countToUFallbacks, - offsetToUCodeUnits, - offsetFromUTable, - offsetFromUBytes, - flags, - fromUBytesLength; - - /* new and required in version 5 */ - uint32_t options; - - /* new and optional in version 5; used if options&MBCS_OPT_NO_FROM_U */ - uint32_t fullStage2Length; /* number of 32-bit units */ -} _MBCSHeader; - -#define UCNV_MBCS_HEADER_INITIALIZER { { 0 }, 0, 0, 0, 0, 0, 0, 0, 0, 0 } - -/* - * This is a simple version of _MBCSGetNextUChar() that is used - * by other converter implementations. - * It only returns an "assigned" result if it consumes the entire input. - * It does not use state from the converter, nor error codes. - * It does not handle the EBCDIC swaplfnl option (set in UConverter). - * It handles conversion extensions but not GB 18030. - * - * Return value: - * U+fffe unassigned - * U+ffff illegal - * otherwise the Unicode code point - */ -U_CFUNC UChar32 -ucnv_MBCSSimpleGetNextUChar(UConverterSharedData *sharedData, - const char *source, int32_t length, - UBool useFallback); - -/** - * This version of _MBCSSimpleGetNextUChar() is optimized for single-byte, single-state codepages. - * It does not handle the EBCDIC swaplfnl option (set in UConverter). - * It does not handle conversion extensions (_extToU()). - */ -U_CFUNC UChar32 -ucnv_MBCSSingleSimpleGetNextUChar(UConverterSharedData *sharedData, - uint8_t b, UBool useFallback); - -/** - * This macro version of _MBCSSingleSimpleGetNextUChar() gets a code point from a byte. - * It works for single-byte, single-state codepages that only map - * to and from BMP code points, and it always - * returns fallback values. - */ -#define _MBCS_SINGLE_SIMPLE_GET_NEXT_BMP(sharedData, b) \ - (UChar)MBCS_ENTRY_FINAL_VALUE_16((sharedData)->mbcs.stateTable[0][(uint8_t)(b)]) - -/** - * This is an internal function that allows other converter implementations - * to check whether a byte is a lead byte. - */ -U_CFUNC UBool -ucnv_MBCSIsLeadByte(UConverterSharedData *sharedData, char byte); - -/** This is a macro version of _MBCSIsLeadByte(). */ -#define _MBCS_IS_LEAD_BYTE(sharedData, byte) \ - (UBool)MBCS_ENTRY_IS_TRANSITION((sharedData)->mbcs.stateTable[0][(uint8_t)(byte)]) - -/* - * This is another simple conversion function for internal use by other - * conversion implementations. - * It does not use the converter state nor call callbacks. - * It does not handle the EBCDIC swaplfnl option (set in UConverter). - * It handles conversion extensions but not GB 18030. - * - * It converts one single Unicode code point into codepage bytes, encoded - * as one 32-bit value. The function returns the number of bytes in *pValue: - * 1..4 the number of bytes in *pValue - * 0 unassigned (*pValue undefined) - * -1 illegal (currently not used, *pValue undefined) - * - * *pValue will contain the resulting bytes with the last byte in bits 7..0, - * the second to last byte in bits 15..8, etc. - * Currently, the function assumes but does not check that 0<=c<=0x10ffff. - */ -U_CFUNC int32_t -ucnv_MBCSFromUChar32(UConverterSharedData *sharedData, - UChar32 c, uint32_t *pValue, - UBool useFallback); - -/** - * This version of _MBCSFromUChar32() is optimized for single-byte codepages. - * It does not handle the EBCDIC swaplfnl option (set in UConverter). - * - * It returns the codepage byte for the code point, or -1 if it is unassigned. - */ -U_CFUNC int32_t -ucnv_MBCSSingleFromUChar32(UConverterSharedData *sharedData, - UChar32 c, - UBool useFallback); - -/** - * SBCS, DBCS, and EBCDIC_STATEFUL are replaced by MBCS, but - * we cheat a little about the type, returning the old types if appropriate. - */ -U_CFUNC UConverterType -ucnv_MBCSGetType(const UConverter* converter); - -U_CFUNC void -ucnv_MBCSFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode); -U_CFUNC void -ucnv_MBCSToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode); - -/* - * Internal function returning a UnicodeSet for toUnicode() conversion. - * Currently only used for ISO-2022-CN, and only handles roundtrip mappings. - * In the future, if we add support for fallback sets, this function - * needs to be updated. - * Handles extensions. - * Does not empty the set first. - */ -U_CFUNC void -ucnv_MBCSGetUnicodeSetForUnicode(const UConverterSharedData *sharedData, - const USetAdder *sa, - UConverterUnicodeSet which, - UErrorCode *pErrorCode); - -/* - * Same as ucnv_MBCSGetUnicodeSetForUnicode() but - * the set can be filtered by encoding scheme. - * Used by stateful converters which share regular conversion tables - * but only use a subset of their mappings. - */ -U_CFUNC void -ucnv_MBCSGetFilteredUnicodeSetForUnicode(const UConverterSharedData *sharedData, - const USetAdder *sa, - UConverterUnicodeSet which, - UConverterSetFilter filter, - UErrorCode *pErrorCode); - -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2000-2013, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ucnvmbcs.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2000jul07 +* created by: Markus W. Scherer +*/ + +#ifndef __UCNVMBCS_H__ +#define __UCNVMBCS_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ucnv.h" +#include "ucnv_cnv.h" +#include "ucnv_ext.h" + +/** + * ICU conversion (.cnv) data file structure, following the usual UDataInfo + * header. + * + * Format version: 6.2 + * + * struct UConverterStaticData -- struct containing the converter name, IBM CCSID, + * min/max bytes per character, etc. + * see ucnv_bld.h + * + * -------------------- + * + * The static data is followed by conversionType-specific data structures. + * At the moment, there are only variations of MBCS converters. They all have + * the same toUnicode structures, while the fromUnicode structures for SBCS + * differ from those for other MBCS-style converters. + * + * _MBCSHeader.version 5 is optional and not backward-compatible + * (as usual for changes in the major version field). + * + * Versions 5.m work like versions 4.m except: + * - The _MBCSHeader has variable length (and is always longer than in version 4). + * See the struct _MBCSHeader further description below. + * - There is a set of flags which indicate further incompatible changes. + * (Reader code must reject the file if it does not recognize them all.) + * - In particular, one of these flags indicates that most of the fromUnicode + * data is missing and must be reconstituted from the toUnicode data + * and from the utf8Friendly mbcsIndex at load time. + * (This only works with a utf8Friendly table.) + * In this case, makeconv may increase maxFastUChar automatically to U+FFFF. + * + * The first of these versions is 5.3, which is like 4.3 except for the differences above. + * + * When possible, makeconv continues to generate version 4.m files. + * + * _MBCSHeader.version 5.4/4.4 supports "good one-way" mappings (|4) + * in the extension tables (fromUTableValues bit 30). See ucnv_ext.h for details. + * + * _MBCSHeader.version 4.3 optionally modifies the fromUnicode data structures + * slightly and optionally adds a table for conversion to MBCS (non-SBCS) + * charsets. + * + * The modifications are to make the data utf8Friendly. Not every 4.3 file + * file contains utf8Friendly data. + * It is utf8Friendly if _MBCSHeader.version[2]!=0. + * In this case, the data structures are utf8Friendly up to the code point + * maxFastUChar=((_MBCSHeader.version[2]<<8)|0xff) + * + * A utf8Friendly file has fromUnicode stage 3 entries for code points up to + * maxFastUChar allocated in blocks of 64 for indexing with the 6 bits from + * a UTF-8 trail byte. ASCII is allocated linearly with 128 contiguous entries. + * + * In addition, a utf8Friendly MBCS file contains an additional + * uint16_t mbcsIndex[(maxFastUChar+1)>>6]; + * which replaces the stage 1 and 2 tables for indexing with bits from the + * UTF-8 lead byte and middle trail byte. Unlike the older MBCS stage 2 table, + * the mbcsIndex does not contain roundtrip flags. Therefore, all fallbacks + * from code points up to maxFastUChar (and roundtrips to 0x00) are moved to + * the extension data structure. This also allows for faster roundtrip + * conversion from UTF-16. + * + * SBCS files do not contain an additional sbcsIndex[] array because the + * proportional size increase would be noticeable, but the runtime + * code builds one for the code point range for which the runtime conversion + * code is optimized. + * + * For SBCS, maxFastUChar should be at least U+0FFF. The initial makeconv + * implementation sets it to U+1FFF. Because the sbcsIndex is not stored in + * the file, a larger maxFastUChar only affects stage 3 block allocation size + * and is free in empty blocks. (Larger blocks with sparse contents cause larger + * files.) U+1FFF includes almost all of the small scripts. + * U+0FFF covers UTF-8 two-byte sequences and three-byte sequences starting with + * 0xe0. This includes most scripts with legacy SBCS charsets. + * The initial runtime implementation using 4.3 files only builds an sbcsIndex + * for code points up to U+0FFF. + * + * For MBCS, maxFastUChar should be at least U+D7FF (=initial value). + * This boundary is convenient because practically all of the commonly used + * characters are below it, and because it is the boundary to surrogate + * code points, above which special handling is necessary anyway. + * (Surrogate pair assembly for UTF-16, validity checking for UTF-8.) + * + * maxFastUChar could be up to U+FFFF to cover the whole BMP, which could be + * useful especially for conversion from UTF-8 when the input can be assumed + * to be valid, because the surrogate range would then not have to be + * checked. + * (With maxFastUChar=0xffff, makeconv would have to check for mbcsIndex value + * overflow because with the all-unassigned block 0 and nearly full mappings + * from the BMP it is theoretically possible that an index into stage 3 + * exceeds 16 bits.) + * + * _MBCSHeader.version 4.2 adds an optional conversion extension data structure. + * If it is present, then an ICU version reading header versions 4.0 or 4.1 + * will be able to use the base table and ignore the extension. + * + * The unicodeMask in the static data is part of the base table data structure. + * Especially, the UCNV_HAS_SUPPLEMENTARY flag determines the length of the + * fromUnicode stage 1 array. + * The static data unicodeMask refers only to the base table's properties if + * a base table is included. + * In an extension-only file, the static data unicodeMask is 0. + * The extension data indexes have a separate field with the unicodeMask flags. + * + * MBCS-style data structure following the static data. + * Offsets are counted in bytes from the beginning of the MBCS header structure. + * Details about usage in comments in ucnvmbcs.c. + * + * struct _MBCSHeader (see the definition in this header file below) + * contains 32-bit fields as follows: + * 8 values: + * 0 uint8_t[4] MBCS version in UVersionInfo format (currently 4.3.x.0) + * 1 uint32_t countStates + * 2 uint32_t countToUFallbacks + * 3 uint32_t offsetToUCodeUnits + * 4 uint32_t offsetFromUTable + * 5 uint32_t offsetFromUBytes + * 6 uint32_t flags, bits: + * 31.. 8 offsetExtension -- _MBCSHeader.version 4.2 (ICU 2.8) and higher + * 0 for older versions and if + * there is not extension structure + * 7.. 0 outputType + * 7 uint32_t fromUBytesLength -- _MBCSHeader.version 4.1 (ICU 2.4) and higher + * counts bytes in fromUBytes[] + * + * New and required in version 5: + * 8 uint32_t options, bits: + * 31..16 reserved for flags that can be added without breaking + * backward compatibility + * 15.. 6 reserved for flags whose addition will break + * backward compatibility + * 6 MBCS_OPT_FROM_U -- if set, + * then most of the fromUnicode data is omitted; + * fullStage2Length is present and the missing + * bottom part of stage 2 must be reconstituted from + * the toUnicode data; + * stage 3 is missing completely as well; + * not used for SBCS tables + * 5.. 0 length of the _MBCSHeader (number of uint32_t) + * + * New and optional in version 5: + * 9 uint32_t fullStage2Length: used if MBCS_OPT_FROM_U is set + * specifies the full length of stage 2 + * including the omitted part + * + * if(outputType==MBCS_OUTPUT_EXT_ONLY) { + * -- base table name for extension-only table + * char baseTableName[variable]; -- with NUL plus padding for 4-alignment + * + * -- all _MBCSHeader fields except for version and flags are 0 + * } else { + * -- normal base table with optional extension + * + * int32_t stateTable[countStates][256]; + * + * struct _MBCSToUFallback { (fallbacks are sorted by offset) + * uint32_t offset; + * UChar32 codePoint; + * } toUFallbacks[countToUFallbacks]; + * + * uint16_t unicodeCodeUnits[(offsetFromUTable-offsetToUCodeUnits)/2]; + * (padded to an even number of units) + * + * -- stage 1 tables + * if(staticData.unicodeMask&UCNV_HAS_SUPPLEMENTARY) { + * -- stage 1 table for all of Unicode + * uint16_t fromUTable[0x440]; (32-bit-aligned) + * } else { + * -- BMP-only tables have a smaller stage 1 table + * uint16_t fromUTable[0x40]; (32-bit-aligned) + * } + * + * -- stage 2 tables + * length determined by top of stage 1 and bottom of stage 3 tables + * if(outputType==MBCS_OUTPUT_1) { + * -- SBCS: pure indexes + * uint16_t stage 2 indexes[?]; + * } else { + * -- DBCS, MBCS, EBCDIC_STATEFUL, ...: roundtrip flags and indexes + * uint32_t stage 2 flags and indexes[?]; + * if(options&MBCS_OPT_NO_FROM_U) { + * stage 2 really has length fullStage2Length + * and the omitted lower part must be reconstituted from + * the toUnicode data + * } + * } + * + * -- stage 3 tables with byte results + * if(outputType==MBCS_OUTPUT_1) { + * -- SBCS: each 16-bit result contains flags and the result byte, see ucnvmbcs.c + * uint16_t fromUBytes[fromUBytesLength/2]; + * } else if(!(options&MBCS_OPT_NO_FROM_U)) { + * -- DBCS, MBCS, EBCDIC_STATEFUL, ... 2/3/4 bytes result, see ucnvmbcs.c + * uint8_t fromUBytes[fromUBytesLength]; or + * uint16_t fromUBytes[fromUBytesLength/2]; or + * uint32_t fromUBytes[fromUBytesLength/4]; + * } else { + * fromUBytes[] must be reconstituted from the toUnicode data + * } + * + * -- optional utf8Friendly mbcsIndex -- _MBCSHeader.version 4.3 (ICU 3.8) and higher + * if(outputType!=MBCS_OUTPUT_1 && + * _MBCSHeader.version[1]>=3 && + * (maxFastUChar=_MBCSHeader.version[2])!=0 + * ) { + * maxFastUChar=(maxFastUChar<<8)|0xff; + * uint16_t mbcsIndex[(maxFastUChar+1)>>6]; + * } + * } + * + * -- extension table, details see ucnv_ext.h + * int32_t indexes[>=32]; ... + */ + +/* MBCS converter data and state -------------------------------------------- */ + +enum { + MBCS_MAX_STATE_COUNT=128 +}; + +/** + * MBCS action codes for conversions to Unicode. + * These values are in bits 23..20 of the state table entries. + */ +enum { + MBCS_STATE_VALID_DIRECT_16, + MBCS_STATE_VALID_DIRECT_20, + + MBCS_STATE_FALLBACK_DIRECT_16, + MBCS_STATE_FALLBACK_DIRECT_20, + + MBCS_STATE_VALID_16, + MBCS_STATE_VALID_16_PAIR, + + MBCS_STATE_UNASSIGNED, + MBCS_STATE_ILLEGAL, + + MBCS_STATE_CHANGE_ONLY +}; + +/* Macros for state table entries */ +#define MBCS_ENTRY_TRANSITION(state, offset) (int32_t)(((int32_t)(state)<<24L)|(offset)) +#define MBCS_ENTRY_TRANSITION_SET_OFFSET(entry, offset) (int32_t)(((entry)&0xff000000)|(offset)) +#define MBCS_ENTRY_TRANSITION_ADD_OFFSET(entry, offset) (int32_t)((entry)+(offset)) + +#define MBCS_ENTRY_FINAL(state, action, value) (int32_t)(0x80000000|((int32_t)(state)<<24L)|((action)<<20L)|(value)) +#define MBCS_ENTRY_SET_FINAL(entry) (int32_t)((entry)|0x80000000) +#define MBCS_ENTRY_FINAL_SET_ACTION(entry, action) (int32_t)(((entry)&0xff0fffff)|((int32_t)(action)<<20L)) +#define MBCS_ENTRY_FINAL_SET_VALUE(entry, value) (int32_t)(((entry)&0xfff00000)|(value)) +#define MBCS_ENTRY_FINAL_SET_ACTION_VALUE(entry, action, value) (int32_t)(((entry)&0xff000000)|((int32_t)(action)<<20L)|(value)) + +#define MBCS_ENTRY_SET_STATE(entry, state) (int32_t)(((entry)&0x80ffffff)|((int32_t)(state)<<24L)) + +#define MBCS_ENTRY_STATE(entry) ((((uint32_t)entry)>>24)&0x7f) + +#define MBCS_ENTRY_IS_TRANSITION(entry) ((entry)>=0) +#define MBCS_ENTRY_IS_FINAL(entry) ((entry)<0) + +#define MBCS_ENTRY_TRANSITION_STATE(entry) (((uint32_t)entry)>>24) +#define MBCS_ENTRY_TRANSITION_OFFSET(entry) ((entry)&0xffffff) + +#define MBCS_ENTRY_FINAL_STATE(entry) ((((uint32_t)entry)>>24)&0x7f) +#define MBCS_ENTRY_FINAL_IS_VALID_DIRECT_16(entry) ((entry)<(int32_t)0x80100000) +#define MBCS_ENTRY_FINAL_ACTION(entry) ((((uint32_t)entry)>>20)&0xf) +#define MBCS_ENTRY_FINAL_VALUE(entry) ((entry)&0xfffff) +#define MBCS_ENTRY_FINAL_VALUE_16(entry) (uint16_t)(entry) + +#define IS_ASCII_ROUNDTRIP(b, asciiRoundtrips) (((asciiRoundtrips) & (1<<((b)>>2)))!=0) + +/* single-byte fromUnicode: get the 16-bit result word */ +#define MBCS_SINGLE_RESULT_FROM_U(table, results, c) (results)[ (table)[ (table)[(c)>>10] +(((c)>>4)&0x3f) ] +((c)&0xf) ] + +/* single-byte fromUnicode using the sbcsIndex */ +#define SBCS_RESULT_FROM_LOW_BMP(table, results, c) (results)[ (table)[(c)>>6] +((c)&0x3f) ] + +/* single-byte fromUTF8 using the sbcsIndex; l and t must be masked externally; can be l=0 and t<=0x7f */ +#define SBCS_RESULT_FROM_UTF8(table, results, l, t) (results)[ (table)[l] +(t) ] + +/* multi-byte fromUnicode: get the 32-bit stage 2 entry */ +#define MBCS_STAGE_2_FROM_U(table, c) ((const uint32_t *)(table))[ (table)[(c)>>10] +(((c)>>4)&0x3f) ] +#define MBCS_FROM_U_IS_ROUNDTRIP(stage2Entry, c) ( ((stage2Entry) & ((uint32_t)1<< (16+((c)&0xf)) )) !=0) + +#define MBCS_VALUE_2_FROM_STAGE_2(bytes, stage2Entry, c) ((uint16_t *)(bytes))[16*(uint32_t)(uint16_t)(stage2Entry)+((c)&0xf)] +#define MBCS_VALUE_4_FROM_STAGE_2(bytes, stage2Entry, c) ((uint32_t *)(bytes))[16*(uint32_t)(uint16_t)(stage2Entry)+((c)&0xf)] + +#define MBCS_POINTER_3_FROM_STAGE_2(bytes, stage2Entry, c) ((bytes)+(16*(uint32_t)(uint16_t)(stage2Entry)+((c)&0xf))*3) + +/* double-byte fromUnicode using the mbcsIndex */ +#define DBCS_RESULT_FROM_MOST_BMP(table, results, c) (results)[ (table)[(c)>>6] +((c)&0x3f) ] + +/* double-byte fromUTF8 using the mbcsIndex; l and t1 combined into lt1; lt1 and t2 must be masked externally */ +#define DBCS_RESULT_FROM_UTF8(table, results, lt1, t2) (results)[ (table)[lt1] +(t2) ] + + +/** + * MBCS output types for conversions from Unicode. + * These per-converter types determine the storage method in stage 3 of the lookup table, + * mostly how many bytes are stored per entry. + */ +enum { + MBCS_OUTPUT_1, /* 0 */ + MBCS_OUTPUT_2, /* 1 */ + MBCS_OUTPUT_3, /* 2 */ + MBCS_OUTPUT_4, /* 3 */ + + MBCS_OUTPUT_3_EUC=8, /* 8 */ + MBCS_OUTPUT_4_EUC, /* 9 */ + + MBCS_OUTPUT_2_SISO=12, /* c */ + MBCS_OUTPUT_2_HZ, /* d */ + + MBCS_OUTPUT_EXT_ONLY, /* e */ + + MBCS_OUTPUT_COUNT, + + MBCS_OUTPUT_DBCS_ONLY=0xdb /* runtime-only type for DBCS-only handling of SISO tables */ +}; + +/** + * Fallbacks to Unicode are stored outside the normal state table and code point structures + * in a vector of items of this type. They are sorted by offset. + */ +typedef struct { + uint32_t offset; + UChar32 codePoint; +} _MBCSToUFallback; + +/** Constants for fast and UTF-8-friendly conversion. */ +enum { + SBCS_FAST_MAX=0x0fff, /* maximum code point with UTF-8-friendly SBCS runtime code, see makeconv SBCS_UTF8_MAX */ + SBCS_FAST_LIMIT=SBCS_FAST_MAX+1, /* =0x1000 */ + MBCS_FAST_MAX=0xd7ff, /* maximum code point with UTF-8-friendly MBCS runtime code, see makeconv MBCS_UTF8_MAX */ + MBCS_FAST_LIMIT=MBCS_FAST_MAX+1 /* =0xd800 */ +}; + +/** + * This is the MBCS part of the UConverterTable union (a runtime data structure). + * It keeps all the per-converter data and points into the loaded mapping tables. + * + * utf8Friendly data structures added with _MBCSHeader.version 4.3 + */ +typedef struct UConverterMBCSTable { + /* toUnicode */ + uint8_t countStates, dbcsOnlyState, stateTableOwned; + uint32_t countToUFallbacks; + + const int32_t (*stateTable)/*[countStates]*/[256]; + int32_t (*swapLFNLStateTable)/*[countStates]*/[256]; /* for swaplfnl */ + const uint16_t *unicodeCodeUnits/*[countUnicodeResults]*/; + const _MBCSToUFallback *toUFallbacks; + + /* fromUnicode */ + const uint16_t *fromUnicodeTable; + const uint16_t *mbcsIndex; /* for fast conversion from most of BMP to MBCS (utf8Friendly data) */ + uint16_t sbcsIndex[SBCS_FAST_LIMIT>>6]; /* for fast conversion from low BMP to SBCS (utf8Friendly data) */ + const uint8_t *fromUnicodeBytes; + uint8_t *swapLFNLFromUnicodeBytes; /* for swaplfnl */ + uint32_t fromUBytesLength; + uint8_t outputType, unicodeMask; + UBool utf8Friendly; /* for utf8Friendly data */ + UChar maxFastUChar; /* for utf8Friendly data */ + + /* roundtrips */ + uint32_t asciiRoundtrips; + + /* reconstituted data that was omitted from the .cnv file */ + uint8_t *reconstitutedData; + + /* converter name for swaplfnl */ + char *swapLFNLName; + + /* extension data */ + struct UConverterSharedData *baseSharedData; + const int32_t *extIndexes; +} UConverterMBCSTable; + +#define UCNV_MBCS_TABLE_INITIALIZER { \ + /* toUnicode */ \ + 0, 0, 0, \ + 0, \ + \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + \ + /* fromUnicode */ \ + NULL, \ + NULL, \ + { 0 }, \ + NULL, \ + NULL, \ + 0, \ + 0, 0, \ + false, \ + 0, \ + \ + /* roundtrips */ \ + 0, \ + \ + /* reconstituted data that was omitted from the .cnv file */ \ + NULL, \ + \ + /* converter name for swaplfnl */ \ + NULL, \ + \ + /* extension data */ \ + NULL, \ + NULL \ +} + +enum { + MBCS_OPT_LENGTH_MASK=0x3f, + MBCS_OPT_NO_FROM_U=0x40, + /* + * If any of the following options bits are set, + * then the file must be rejected. + */ + MBCS_OPT_INCOMPATIBLE_MASK=0xffc0, + /* + * Remove bits from this mask as more options are recognized + * by all implementations that use this constant. + */ + MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK=0xff80 +}; + +enum { + MBCS_HEADER_V4_LENGTH=8, + MBCS_HEADER_V5_MIN_LENGTH=9 +}; + +/** + * MBCS data header. See data format description above. + */ +typedef struct { + UVersionInfo version; + uint32_t countStates, + countToUFallbacks, + offsetToUCodeUnits, + offsetFromUTable, + offsetFromUBytes, + flags, + fromUBytesLength; + + /* new and required in version 5 */ + uint32_t options; + + /* new and optional in version 5; used if options&MBCS_OPT_NO_FROM_U */ + uint32_t fullStage2Length; /* number of 32-bit units */ +} _MBCSHeader; + +#define UCNV_MBCS_HEADER_INITIALIZER { { 0 }, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + +/* + * This is a simple version of _MBCSGetNextUChar() that is used + * by other converter implementations. + * It only returns an "assigned" result if it consumes the entire input. + * It does not use state from the converter, nor error codes. + * It does not handle the EBCDIC swaplfnl option (set in UConverter). + * It handles conversion extensions but not GB 18030. + * + * Return value: + * U+fffe unassigned + * U+ffff illegal + * otherwise the Unicode code point + */ +U_CFUNC UChar32 +ucnv_MBCSSimpleGetNextUChar(UConverterSharedData *sharedData, + const char *source, int32_t length, + UBool useFallback); + +/** + * This version of _MBCSSimpleGetNextUChar() is optimized for single-byte, single-state codepages. + * It does not handle the EBCDIC swaplfnl option (set in UConverter). + * It does not handle conversion extensions (_extToU()). + */ +U_CFUNC UChar32 +ucnv_MBCSSingleSimpleGetNextUChar(UConverterSharedData *sharedData, + uint8_t b, UBool useFallback); + +/** + * This macro version of _MBCSSingleSimpleGetNextUChar() gets a code point from a byte. + * It works for single-byte, single-state codepages that only map + * to and from BMP code points, and it always + * returns fallback values. + */ +#define _MBCS_SINGLE_SIMPLE_GET_NEXT_BMP(sharedData, b) \ + (UChar)MBCS_ENTRY_FINAL_VALUE_16((sharedData)->mbcs.stateTable[0][(uint8_t)(b)]) + +/** + * This is an internal function that allows other converter implementations + * to check whether a byte is a lead byte. + */ +U_CFUNC UBool +ucnv_MBCSIsLeadByte(UConverterSharedData *sharedData, char byte); + +/** This is a macro version of _MBCSIsLeadByte(). */ +#define _MBCS_IS_LEAD_BYTE(sharedData, byte) \ + (UBool)MBCS_ENTRY_IS_TRANSITION((sharedData)->mbcs.stateTable[0][(uint8_t)(byte)]) + +/* + * This is another simple conversion function for internal use by other + * conversion implementations. + * It does not use the converter state nor call callbacks. + * It does not handle the EBCDIC swaplfnl option (set in UConverter). + * It handles conversion extensions but not GB 18030. + * + * It converts one single Unicode code point into codepage bytes, encoded + * as one 32-bit value. The function returns the number of bytes in *pValue: + * 1..4 the number of bytes in *pValue + * 0 unassigned (*pValue undefined) + * -1 illegal (currently not used, *pValue undefined) + * + * *pValue will contain the resulting bytes with the last byte in bits 7..0, + * the second to last byte in bits 15..8, etc. + * Currently, the function assumes but does not check that 0<=c<=0x10ffff. + */ +U_CFUNC int32_t +ucnv_MBCSFromUChar32(UConverterSharedData *sharedData, + UChar32 c, uint32_t *pValue, + UBool useFallback); + +/** + * This version of _MBCSFromUChar32() is optimized for single-byte codepages. + * It does not handle the EBCDIC swaplfnl option (set in UConverter). + * + * It returns the codepage byte for the code point, or -1 if it is unassigned. + */ +U_CFUNC int32_t +ucnv_MBCSSingleFromUChar32(UConverterSharedData *sharedData, + UChar32 c, + UBool useFallback); + +/** + * SBCS, DBCS, and EBCDIC_STATEFUL are replaced by MBCS, but + * we cheat a little about the type, returning the old types if appropriate. + */ +U_CFUNC UConverterType +ucnv_MBCSGetType(const UConverter* converter); + +U_CFUNC void +ucnv_MBCSFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode); +U_CFUNC void +ucnv_MBCSToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode); + +/* + * Internal function returning a UnicodeSet for toUnicode() conversion. + * Currently only used for ISO-2022-CN, and only handles roundtrip mappings. + * In the future, if we add support for fallback sets, this function + * needs to be updated. + * Handles extensions. + * Does not empty the set first. + */ +U_CFUNC void +ucnv_MBCSGetUnicodeSetForUnicode(const UConverterSharedData *sharedData, + const USetAdder *sa, + UConverterUnicodeSet which, + UErrorCode *pErrorCode); + +/* + * Same as ucnv_MBCSGetUnicodeSetForUnicode() but + * the set can be filtered by encoding scheme. + * Used by stateful converters which share regular conversion tables + * but only use a subset of their mappings. + */ +U_CFUNC void +ucnv_MBCSGetFilteredUnicodeSetForUnicode(const UConverterSharedData *sharedData, + const USetAdder *sa, + UConverterUnicodeSet which, + UConverterSetFilter filter, + UErrorCode *pErrorCode); + +#endif + +#endif diff --git a/deps/icu-small/source/common/ucnvscsu.cpp b/deps/icu-small/source/common/ucnvscsu.cpp index 86e850a998a5e9..cb0ae7f799d902 100644 --- a/deps/icu-small/source/common/ucnvscsu.cpp +++ b/deps/icu-small/source/common/ucnvscsu.cpp @@ -1,2045 +1,2045 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2000-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: ucnvscsu.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2000nov18 -* created by: Markus W. Scherer -* -* This is an implementation of the Standard Compression Scheme for Unicode -* as defined in https://www.unicode.org/reports/tr6/ . -* Reserved commands and window settings are treated as illegal sequences and -* will result in callback calls. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/ucnv_cb.h" -#include "unicode/utf16.h" -#include "ucnv_bld.h" -#include "ucnv_cnv.h" -#include "cmemory.h" - -/* SCSU definitions --------------------------------------------------------- */ - -/* SCSU command byte values */ -enum { - SQ0=0x01, /* Quote from window pair 0 */ - SQ7=0x08, /* Quote from window pair 7 */ - SDX=0x0B, /* Define a window as extended */ - Srs=0x0C, /* reserved */ - SQU=0x0E, /* Quote a single Unicode character */ - SCU=0x0F, /* Change to Unicode mode */ - SC0=0x10, /* Select window 0 */ - SC7=0x17, /* Select window 7 */ - SD0=0x18, /* Define and select window 0 */ - SD7=0x1F, /* Define and select window 7 */ - - UC0=0xE0, /* Select window 0 */ - UC7=0xE7, /* Select window 7 */ - UD0=0xE8, /* Define and select window 0 */ - UD7=0xEF, /* Define and select window 7 */ - UQU=0xF0, /* Quote a single Unicode character */ - UDX=0xF1, /* Define a Window as extended */ - Urs=0xF2 /* reserved */ -}; - -enum { - /* - * Unicode code points from 3400 to E000 are not adressible by - * dynamic window, since in these areas no short run alphabets are - * found. Therefore add gapOffset to all values from gapThreshold. - */ - gapThreshold=0x68, - gapOffset=0xAC00, - - /* values between reservedStart and fixedThreshold are reserved */ - reservedStart=0xA8, - - /* use table of predefined fixed offsets for values from fixedThreshold */ - fixedThreshold=0xF9 -}; - -/* constant offsets for the 8 static windows */ -static const uint32_t staticOffsets[8]={ - 0x0000, /* ASCII for quoted tags */ - 0x0080, /* Latin - 1 Supplement (for access to punctuation) */ - 0x0100, /* Latin Extended-A */ - 0x0300, /* Combining Diacritical Marks */ - 0x2000, /* General Punctuation */ - 0x2080, /* Currency Symbols */ - 0x2100, /* Letterlike Symbols and Number Forms */ - 0x3000 /* CJK Symbols and punctuation */ -}; - -/* initial offsets for the 8 dynamic (sliding) windows */ -static const uint32_t initialDynamicOffsets[8]={ - 0x0080, /* Latin-1 */ - 0x00C0, /* Latin Extended A */ - 0x0400, /* Cyrillic */ - 0x0600, /* Arabic */ - 0x0900, /* Devanagari */ - 0x3040, /* Hiragana */ - 0x30A0, /* Katakana */ - 0xFF00 /* Fullwidth ASCII */ -}; - -/* Table of fixed predefined Offsets */ -static const uint32_t fixedOffsets[]={ - /* 0xF9 */ 0x00C0, /* Latin-1 Letters + half of Latin Extended A */ - /* 0xFA */ 0x0250, /* IPA extensions */ - /* 0xFB */ 0x0370, /* Greek */ - /* 0xFC */ 0x0530, /* Armenian */ - /* 0xFD */ 0x3040, /* Hiragana */ - /* 0xFE */ 0x30A0, /* Katakana */ - /* 0xFF */ 0xFF60 /* Halfwidth Katakana */ -}; - -/* state values */ -enum { - readCommand, - quotePairOne, - quotePairTwo, - quoteOne, - definePairOne, - definePairTwo, - defineOne -}; - -typedef struct SCSUData { - /* dynamic window offsets, initialize to default values from initialDynamicOffsets */ - uint32_t toUDynamicOffsets[8]; - uint32_t fromUDynamicOffsets[8]; - - /* state machine state - toUnicode */ - UBool toUIsSingleByteMode; - uint8_t toUState; - int8_t toUQuoteWindow, toUDynamicWindow; - uint8_t toUByteOne; - uint8_t toUPadding[3]; - - /* state machine state - fromUnicode */ - UBool fromUIsSingleByteMode; - int8_t fromUDynamicWindow; - - /* - * windowUse[] keeps track of the use of the dynamic windows: - * At nextWindowUseIndex there is the least recently used window, - * and the following windows (in a wrapping manner) are more and more - * recently used. - * At nextWindowUseIndex-1 there is the most recently used window. - */ - uint8_t locale; - int8_t nextWindowUseIndex; - int8_t windowUse[8]; -} SCSUData; - -static const int8_t initialWindowUse[8]={ 7, 0, 3, 2, 4, 5, 6, 1 }; -static const int8_t initialWindowUse_ja[8]={ 3, 2, 4, 1, 0, 7, 5, 6 }; - -enum { - lGeneric, l_ja -}; - -/* SCSU setup functions ----------------------------------------------------- */ -U_CDECL_BEGIN -static void U_CALLCONV -_SCSUReset(UConverter *cnv, UConverterResetChoice choice) { - SCSUData *scsu=(SCSUData *)cnv->extraInfo; - - if(choice<=UCNV_RESET_TO_UNICODE) { - /* reset toUnicode */ - uprv_memcpy(scsu->toUDynamicOffsets, initialDynamicOffsets, 32); - - scsu->toUIsSingleByteMode=true; - scsu->toUState=readCommand; - scsu->toUQuoteWindow=scsu->toUDynamicWindow=0; - scsu->toUByteOne=0; - - cnv->toULength=0; - } - if(choice!=UCNV_RESET_TO_UNICODE) { - /* reset fromUnicode */ - uprv_memcpy(scsu->fromUDynamicOffsets, initialDynamicOffsets, 32); - - scsu->fromUIsSingleByteMode=true; - scsu->fromUDynamicWindow=0; - - scsu->nextWindowUseIndex=0; - switch(scsu->locale) { - case l_ja: - uprv_memcpy(scsu->windowUse, initialWindowUse_ja, 8); - break; - default: - uprv_memcpy(scsu->windowUse, initialWindowUse, 8); - break; - } - - cnv->fromUChar32=0; - } -} - -static void U_CALLCONV -_SCSUOpen(UConverter *cnv, - UConverterLoadArgs *pArgs, - UErrorCode *pErrorCode) { - const char *locale=pArgs->locale; - if(pArgs->onlyTestIsLoadable) { - return; - } - cnv->extraInfo=uprv_malloc(sizeof(SCSUData)); - if(cnv->extraInfo!=NULL) { - if(locale!=NULL && locale[0]=='j' && locale[1]=='a' && (locale[2]==0 || locale[2]=='_')) { - ((SCSUData *)cnv->extraInfo)->locale=l_ja; - } else { - ((SCSUData *)cnv->extraInfo)->locale=lGeneric; - } - _SCSUReset(cnv, UCNV_RESET_BOTH); - } else { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - } - - /* Set the substitution character U+fffd as a Unicode string. */ - cnv->subUChars[0]=0xfffd; - cnv->subCharLen=-1; -} - -static void U_CALLCONV -_SCSUClose(UConverter *cnv) { - if(cnv->extraInfo!=NULL) { - if(!cnv->isExtraLocal) { - uprv_free(cnv->extraInfo); - } - cnv->extraInfo=NULL; - } -} - -/* SCSU-to-Unicode conversion functions ------------------------------------- */ - -static void U_CALLCONV -_SCSUToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - SCSUData *scsu; - const uint8_t *source, *sourceLimit; - UChar *target; - const UChar *targetLimit; - int32_t *offsets; - UBool isSingleByteMode; - uint8_t state, byteOne; - int8_t quoteWindow, dynamicWindow; - - int32_t sourceIndex, nextSourceIndex; - - uint8_t b; - - /* set up the local pointers */ - cnv=pArgs->converter; - scsu=(SCSUData *)cnv->extraInfo; - - source=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - target=pArgs->target; - targetLimit=pArgs->targetLimit; - offsets=pArgs->offsets; - - /* get the state machine state */ - isSingleByteMode=scsu->toUIsSingleByteMode; - state=scsu->toUState; - quoteWindow=scsu->toUQuoteWindow; - dynamicWindow=scsu->toUDynamicWindow; - byteOne=scsu->toUByteOne; - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex=state==readCommand ? 0 : -1; - nextSourceIndex=0; - - /* - * conversion "loop" - * - * For performance, this is not a normal C loop. - * Instead, there are two code blocks for the two SCSU modes. - * The function branches to either one, and a change of the mode is done with a goto to - * the other branch. - * - * Each branch has two conventional loops: - * - a fast-path loop for the most common codes in the mode - * - a loop for all other codes in the mode - * When the fast-path runs into a code that it cannot handle, its loop ends and it - * runs into the following loop to handle the other codes. - * The end of the input or output buffer is also handled by the slower loop. - * The slow loop jumps (goto) to the fast-path loop again as soon as possible. - * - * The callback handling is done by returning with an error code. - * The conversion framework actually calls the callback function. - */ - if(isSingleByteMode) { - /* fast path for single-byte mode */ - if(state==readCommand) { -fastSingle: - while(source=0x20) { - ++source; - ++nextSourceIndex; - if(b<=0x7f) { - /* write US-ASCII graphic character or DEL */ - *target++=(UChar)b; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - } else { - /* write from dynamic window */ - uint32_t c=scsu->toUDynamicOffsets[dynamicWindow]+(b&0x7f); - if(c<=0xffff) { - *target++=(UChar)c; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - } else { - /* output surrogate pair */ - *target++=(UChar)(0xd7c0+(c>>10)); - if(targetUCharErrorBuffer[0]=(UChar)(0xdc00|(c&0x3ff)); - cnv->UCharErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - goto endloop; - } - } - } - sourceIndex=nextSourceIndex; - } - } - - /* normal state machine for single-byte mode, minus handling for what fastSingle covers */ -singleByteMode: - while(source=targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - b=*source++; - ++nextSourceIndex; - switch(state) { - case readCommand: - /* redundant conditions are commented out */ - /* here: b<0x20 because otherwise we would be in fastSingle */ - if((1UL<toUBytes[0]=b; - cnv->toULength=1; - goto endloop; - } - - /* store the first byte of a multibyte sequence in toUBytes[] */ - cnv->toUBytes[0]=b; - cnv->toULength=1; - break; - case quotePairOne: - byteOne=b; - cnv->toUBytes[1]=b; - cnv->toULength=2; - state=quotePairTwo; - break; - case quotePairTwo: - *target++=(UChar)((byteOne<<8)|b); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - sourceIndex=nextSourceIndex; - state=readCommand; - goto fastSingle; - case quoteOne: - if(b<0x80) { - /* all static offsets are in the BMP */ - *target++=(UChar)(staticOffsets[quoteWindow]+b); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - } else { - /* write from dynamic window */ - uint32_t c=scsu->toUDynamicOffsets[quoteWindow]+(b&0x7f); - if(c<=0xffff) { - *target++=(UChar)c; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - } else { - /* output surrogate pair */ - *target++=(UChar)(0xd7c0+(c>>10)); - if(targetUCharErrorBuffer[0]=(UChar)(0xdc00|(c&0x3ff)); - cnv->UCharErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - goto endloop; - } - } - } - sourceIndex=nextSourceIndex; - state=readCommand; - goto fastSingle; - case definePairOne: - dynamicWindow=(int8_t)((b>>5)&7); - byteOne=(uint8_t)(b&0x1f); - cnv->toUBytes[1]=b; - cnv->toULength=2; - state=definePairTwo; - break; - case definePairTwo: - scsu->toUDynamicOffsets[dynamicWindow]=0x10000+(byteOne<<15UL | b<<7UL); - sourceIndex=nextSourceIndex; - state=readCommand; - goto fastSingle; - case defineOne: - if(b==0) { - /* callback(illegal): Reserved window offset value 0 */ - cnv->toUBytes[1]=b; - cnv->toULength=2; - goto endloop; - } else if(btoUDynamicOffsets[dynamicWindow]=b<<7UL; - } else if((uint8_t)(b-gapThreshold)<(reservedStart-gapThreshold)) { - scsu->toUDynamicOffsets[dynamicWindow]=(b<<7UL)+gapOffset; - } else if(b>=fixedThreshold) { - scsu->toUDynamicOffsets[dynamicWindow]=fixedOffsets[b-fixedThreshold]; - } else { - /* callback(illegal): Reserved window offset value 0xa8..0xf8 */ - cnv->toUBytes[1]=b; - cnv->toULength=2; - goto endloop; - } - sourceIndex=nextSourceIndex; - state=readCommand; - goto fastSingle; - } - } - } else { - /* fast path for Unicode mode */ - if(state==readCommand) { -fastUnicode: - while(source+1(Urs-UC0)) { - *target++=(UChar)((b<<8)|source[1]); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - sourceIndex=nextSourceIndex; - nextSourceIndex+=2; - source+=2; - } - } - - /* normal state machine for Unicode mode */ -/* unicodeByteMode: */ - while(source=targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - b=*source++; - ++nextSourceIndex; - switch(state) { - case readCommand: - if((uint8_t)(b-UC0)>(Urs-UC0)) { - byteOne=b; - cnv->toUBytes[0]=b; - cnv->toULength=1; - state=quotePairTwo; - } else if(/* UC0<=b && */ b<=UC7) { - dynamicWindow=(int8_t)(b-UC0); - sourceIndex=nextSourceIndex; - isSingleByteMode=true; - goto fastSingle; - } else if(/* UD0<=b && */ b<=UD7) { - dynamicWindow=(int8_t)(b-UD0); - isSingleByteMode=true; - cnv->toUBytes[0]=b; - cnv->toULength=1; - state=defineOne; - goto singleByteMode; - } else if(b==UDX) { - isSingleByteMode=true; - cnv->toUBytes[0]=b; - cnv->toULength=1; - state=definePairOne; - goto singleByteMode; - } else if(b==UQU) { - cnv->toUBytes[0]=b; - cnv->toULength=1; - state=quotePairOne; - } else /* Urs */ { - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - cnv->toUBytes[0]=b; - cnv->toULength=1; - goto endloop; - } - break; - case quotePairOne: - byteOne=b; - cnv->toUBytes[1]=b; - cnv->toULength=2; - state=quotePairTwo; - break; - case quotePairTwo: - *target++=(UChar)((byteOne<<8)|b); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - sourceIndex=nextSourceIndex; - state=readCommand; - goto fastUnicode; - } - } - } -endloop: - - /* set the converter state back into UConverter */ - if(U_FAILURE(*pErrorCode) && *pErrorCode!=U_BUFFER_OVERFLOW_ERROR) { - /* reset to deal with the next character */ - state=readCommand; - } else if(state==readCommand) { - /* not in a multi-byte sequence, reset toULength */ - cnv->toULength=0; - } - scsu->toUIsSingleByteMode=isSingleByteMode; - scsu->toUState=state; - scsu->toUQuoteWindow=quoteWindow; - scsu->toUDynamicWindow=dynamicWindow; - scsu->toUByteOne=byteOne; - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - pArgs->offsets=offsets; - return; -} - -/* - * Identical to _SCSUToUnicodeWithOffsets but without offset handling. - * If a change is made in the original function, then either - * change this function the same way or - * re-copy the original function and remove the variables - * offsets, sourceIndex, and nextSourceIndex. - */ -static void U_CALLCONV -_SCSUToUnicode(UConverterToUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - SCSUData *scsu; - const uint8_t *source, *sourceLimit; - UChar *target; - const UChar *targetLimit; - UBool isSingleByteMode; - uint8_t state, byteOne; - int8_t quoteWindow, dynamicWindow; - - uint8_t b; - - /* set up the local pointers */ - cnv=pArgs->converter; - scsu=(SCSUData *)cnv->extraInfo; - - source=(const uint8_t *)pArgs->source; - sourceLimit=(const uint8_t *)pArgs->sourceLimit; - target=pArgs->target; - targetLimit=pArgs->targetLimit; - - /* get the state machine state */ - isSingleByteMode=scsu->toUIsSingleByteMode; - state=scsu->toUState; - quoteWindow=scsu->toUQuoteWindow; - dynamicWindow=scsu->toUDynamicWindow; - byteOne=scsu->toUByteOne; - - /* - * conversion "loop" - * - * For performance, this is not a normal C loop. - * Instead, there are two code blocks for the two SCSU modes. - * The function branches to either one, and a change of the mode is done with a goto to - * the other branch. - * - * Each branch has two conventional loops: - * - a fast-path loop for the most common codes in the mode - * - a loop for all other codes in the mode - * When the fast-path runs into a code that it cannot handle, its loop ends and it - * runs into the following loop to handle the other codes. - * The end of the input or output buffer is also handled by the slower loop. - * The slow loop jumps (goto) to the fast-path loop again as soon as possible. - * - * The callback handling is done by returning with an error code. - * The conversion framework actually calls the callback function. - */ - if(isSingleByteMode) { - /* fast path for single-byte mode */ - if(state==readCommand) { -fastSingle: - while(source=0x20) { - ++source; - if(b<=0x7f) { - /* write US-ASCII graphic character or DEL */ - *target++=(UChar)b; - } else { - /* write from dynamic window */ - uint32_t c=scsu->toUDynamicOffsets[dynamicWindow]+(b&0x7f); - if(c<=0xffff) { - *target++=(UChar)c; - } else { - /* output surrogate pair */ - *target++=(UChar)(0xd7c0+(c>>10)); - if(targetUCharErrorBuffer[0]=(UChar)(0xdc00|(c&0x3ff)); - cnv->UCharErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - goto endloop; - } - } - } - } - } - - /* normal state machine for single-byte mode, minus handling for what fastSingle covers */ -singleByteMode: - while(source=targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - b=*source++; - switch(state) { - case readCommand: - /* redundant conditions are commented out */ - /* here: b<0x20 because otherwise we would be in fastSingle */ - if((1UL<toUBytes[0]=b; - cnv->toULength=1; - goto endloop; - } - - /* store the first byte of a multibyte sequence in toUBytes[] */ - cnv->toUBytes[0]=b; - cnv->toULength=1; - break; - case quotePairOne: - byteOne=b; - cnv->toUBytes[1]=b; - cnv->toULength=2; - state=quotePairTwo; - break; - case quotePairTwo: - *target++=(UChar)((byteOne<<8)|b); - state=readCommand; - goto fastSingle; - case quoteOne: - if(b<0x80) { - /* all static offsets are in the BMP */ - *target++=(UChar)(staticOffsets[quoteWindow]+b); - } else { - /* write from dynamic window */ - uint32_t c=scsu->toUDynamicOffsets[quoteWindow]+(b&0x7f); - if(c<=0xffff) { - *target++=(UChar)c; - } else { - /* output surrogate pair */ - *target++=(UChar)(0xd7c0+(c>>10)); - if(targetUCharErrorBuffer[0]=(UChar)(0xdc00|(c&0x3ff)); - cnv->UCharErrorBufferLength=1; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - goto endloop; - } - } - } - state=readCommand; - goto fastSingle; - case definePairOne: - dynamicWindow=(int8_t)((b>>5)&7); - byteOne=(uint8_t)(b&0x1f); - cnv->toUBytes[1]=b; - cnv->toULength=2; - state=definePairTwo; - break; - case definePairTwo: - scsu->toUDynamicOffsets[dynamicWindow]=0x10000+(byteOne<<15UL | b<<7UL); - state=readCommand; - goto fastSingle; - case defineOne: - if(b==0) { - /* callback(illegal): Reserved window offset value 0 */ - cnv->toUBytes[1]=b; - cnv->toULength=2; - goto endloop; - } else if(btoUDynamicOffsets[dynamicWindow]=b<<7UL; - } else if((uint8_t)(b-gapThreshold)<(reservedStart-gapThreshold)) { - scsu->toUDynamicOffsets[dynamicWindow]=(b<<7UL)+gapOffset; - } else if(b>=fixedThreshold) { - scsu->toUDynamicOffsets[dynamicWindow]=fixedOffsets[b-fixedThreshold]; - } else { - /* callback(illegal): Reserved window offset value 0xa8..0xf8 */ - cnv->toUBytes[1]=b; - cnv->toULength=2; - goto endloop; - } - state=readCommand; - goto fastSingle; - } - } - } else { - /* fast path for Unicode mode */ - if(state==readCommand) { -fastUnicode: - while(source+1(Urs-UC0)) { - *target++=(UChar)((b<<8)|source[1]); - source+=2; - } - } - - /* normal state machine for Unicode mode */ -/* unicodeByteMode: */ - while(source=targetLimit) { - /* target is full */ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - break; - } - b=*source++; - switch(state) { - case readCommand: - if((uint8_t)(b-UC0)>(Urs-UC0)) { - byteOne=b; - cnv->toUBytes[0]=b; - cnv->toULength=1; - state=quotePairTwo; - } else if(/* UC0<=b && */ b<=UC7) { - dynamicWindow=(int8_t)(b-UC0); - isSingleByteMode=true; - goto fastSingle; - } else if(/* UD0<=b && */ b<=UD7) { - dynamicWindow=(int8_t)(b-UD0); - isSingleByteMode=true; - cnv->toUBytes[0]=b; - cnv->toULength=1; - state=defineOne; - goto singleByteMode; - } else if(b==UDX) { - isSingleByteMode=true; - cnv->toUBytes[0]=b; - cnv->toULength=1; - state=definePairOne; - goto singleByteMode; - } else if(b==UQU) { - cnv->toUBytes[0]=b; - cnv->toULength=1; - state=quotePairOne; - } else /* Urs */ { - /* callback(illegal) */ - *pErrorCode=U_ILLEGAL_CHAR_FOUND; - cnv->toUBytes[0]=b; - cnv->toULength=1; - goto endloop; - } - break; - case quotePairOne: - byteOne=b; - cnv->toUBytes[1]=b; - cnv->toULength=2; - state=quotePairTwo; - break; - case quotePairTwo: - *target++=(UChar)((byteOne<<8)|b); - state=readCommand; - goto fastUnicode; - } - } - } -endloop: - - /* set the converter state back into UConverter */ - if(U_FAILURE(*pErrorCode) && *pErrorCode!=U_BUFFER_OVERFLOW_ERROR) { - /* reset to deal with the next character */ - state=readCommand; - } else if(state==readCommand) { - /* not in a multi-byte sequence, reset toULength */ - cnv->toULength=0; - } - scsu->toUIsSingleByteMode=isSingleByteMode; - scsu->toUState=state; - scsu->toUQuoteWindow=quoteWindow; - scsu->toUDynamicWindow=dynamicWindow; - scsu->toUByteOne=byteOne; - - /* write back the updated pointers */ - pArgs->source=(const char *)source; - pArgs->target=target; - return; -} -U_CDECL_END -/* SCSU-from-Unicode conversion functions ----------------------------------- */ - -/* - * This SCSU Encoder is fairly simple but uses all SCSU commands to achieve - * reasonable results. The lookahead is minimal. - * Many cases are simple: - * A character fits directly into the current mode, a dynamic or static window, - * or is not compressible. These cases are tested first. - * Real compression heuristics are applied to the rest, in code branches for - * single/Unicode mode and BMP/supplementary code points. - * The heuristics used here are extremely simple. - */ - -/* get the number of the window that this character is in, or -1 */ -static int8_t -getWindow(const uint32_t offsets[8], uint32_t c) { - int i; - for(i=0; i<8; ++i) { - if((uint32_t)(c-offsets[i])<=0x7f) { - return (int8_t)(i); - } - } - return -1; -} - -/* is the character in the dynamic window starting at the offset, or in the direct-encoded range? */ -static UBool -isInOffsetWindowOrDirect(uint32_t offset, uint32_t c) { - return (UBool)(c<=offset+0x7f && - (c>=offset || (c<=0x7f && - (c>=0x20 || (1UL<windowUse[scsu->nextWindowUseIndex]; - if(++scsu->nextWindowUseIndex==8) { - scsu->nextWindowUseIndex=0; - } - return window; -} - -/* - * useDynamicWindow() adjusts - * windowUse[] and nextWindowUseIndex for the algorithm to choose - * the next dynamic window to be defined; - * a subclass may override it and provide its own algorithm. - */ -static void -useDynamicWindow(SCSUData *scsu, int8_t window) { - /* - * move the existing window, which just became the most recently used one, - * up in windowUse[] to nextWindowUseIndex-1 - */ - - /* first, find the index of the window - backwards to favor the more recently used windows */ - int i, j; - - i=scsu->nextWindowUseIndex; - do { - if(--i<0) { - i=7; - } - } while(scsu->windowUse[i]!=window); - - /* now copy each windowUse[i+1] to [i] */ - j=i+1; - if(j==8) { - j=0; - } - while(j!=scsu->nextWindowUseIndex) { - scsu->windowUse[i]=scsu->windowUse[j]; - i=j; - if(++j==8) { j=0; } - } - - /* finally, set the window into the most recently used index */ - scsu->windowUse[i]=window; -} - -/* - * calculate the offset and the code for a dynamic window that contains the character - * takes fixed offsets into account - * the offset of the window is stored in the offset variable, - * the code is returned - * - * return offset code: -1 none <=0xff code for SDn/UDn else code for SDX/UDX, subtract 0x200 to get the true code - */ -static int -getDynamicOffset(uint32_t c, uint32_t *pOffset) { - int i; - - for(i=0; i<7; ++i) { - if((uint32_t)(c-fixedOffsets[i])<=0x7f) { - *pOffset=fixedOffsets[i]; - return 0xf9+i; - } - } - - if(c<0x80) { - /* No dynamic window for US-ASCII. */ - return -1; - } else if(c<0x3400 || - (uint32_t)(c-0x10000)<(0x14000-0x10000) || - (uint32_t)(c-0x1d000)<=(0x1ffff-0x1d000) - ) { - /* This character is in a code range for a "small", i.e., reasonably windowable, script. */ - *pOffset=c&0x7fffff80; - return (int)(c>>7); - } else if(0xe000<=c && c!=0xfeff && c<0xfff0) { - /* For these characters we need to take the gapOffset into account. */ - *pOffset=c&0x7fffff80; - return (int)((c-gapOffset)>>7); - } else { - return -1; - } -} -U_CDECL_BEGIN -/* - * Idea for compression: - * - save SCSUData and other state before really starting work - * - at endloop, see if compression could be better with just unicode mode - * - don't do this if a callback has been called - * - if unicode mode would be smaller, then override the results with it - may need SCU at the beginning - * - different buffer handling! - * - * Drawback or need for corrective handling: - * it is desirable to encode U+feff as SQU fe ff for the SCSU signature, and - * it is desirable to start a document in US-ASCII/Latin-1 for as long as possible - * not only for compression but also for HTML/XML documents with following charset/encoding announcers. - * - * How to achieve both? - * - Only replace the result after an SDX or SCU? - */ - -static void U_CALLCONV -_SCSUFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - SCSUData *scsu; - const UChar *source, *sourceLimit; - uint8_t *target; - int32_t targetCapacity; - int32_t *offsets; - - UBool isSingleByteMode; - uint8_t dynamicWindow; - uint32_t currentOffset; - - uint32_t c, delta; - - int32_t sourceIndex, nextSourceIndex; - - int32_t length; - - /* variables for compression heuristics */ - uint32_t offset; - UChar lead, trail; - int code; - int8_t window; - - /* set up the local pointers */ - cnv=pArgs->converter; - scsu=(SCSUData *)cnv->extraInfo; - - /* set up the local pointers */ - source=pArgs->source; - sourceLimit=pArgs->sourceLimit; - target=(uint8_t *)pArgs->target; - targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); - offsets=pArgs->offsets; - - /* get the state machine state */ - isSingleByteMode=scsu->fromUIsSingleByteMode; - dynamicWindow=scsu->fromUDynamicWindow; - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; - - c=cnv->fromUChar32; - - /* sourceIndex=-1 if the current character began in the previous buffer */ - sourceIndex= c==0 ? 0 : -1; - nextSourceIndex=0; - - /* similar conversion "loop" as in toUnicode */ -loop: - if(isSingleByteMode) { - if(c!=0 && targetCapacity>0) { - goto getTrailSingle; - } - - /* state machine for single-byte mode */ -/* singleByteMode: */ - while(sourcefromUDynamicOffsets, c))>=0) { - /* there is a dynamic window that contains this character, change to it */ - dynamicWindow=window; - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)(SC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; - length=2; - goto outputBytes; - } else if((code=getDynamicOffset(c, &offset))>=0) { - /* might check if there are more characters in this window to come */ - /* define an extended window with this character */ - code-=0x200; - dynamicWindow=getNextDynamicWindow(scsu); - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)SDX<<24)|((uint32_t)dynamicWindow<<21)|((uint32_t)code<<8)|(c-currentOffset)|0x80; - length=4; - goto outputBytes; - } else { - /* change to Unicode mode and output this (lead, trail) pair */ - isSingleByteMode=false; - *target++=(uint8_t)SCU; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - --targetCapacity; - c=((uint32_t)lead<<16)|trail; - length=4; - goto outputBytes; - } - } else if(c<0xa0) { - /* quote C1 control character */ - c=(c&0x7f)|(SQ0+1)<<8; /* SQ0+1==SQ1 */ - length=2; - goto outputBytes; - } else if(c==0xfeff || c>=0xfff0) { - /* quote signature character=byte order mark and specials */ - c|=SQU<<16; - length=3; - goto outputBytes; - } else { - /* compress all other BMP characters */ - if((window=getWindow(scsu->fromUDynamicOffsets, c))>=0) { - /* there is a window defined that contains this character - switch to it or quote from it? */ - if(source>=sourceLimit || isInOffsetWindowOrDirect(scsu->fromUDynamicOffsets[window], *source)) { - /* change to dynamic window */ - dynamicWindow=window; - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)(SC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; - length=2; - goto outputBytes; - } else { - /* quote from dynamic window */ - c=((uint32_t)(SQ0+window)<<8)|(c-scsu->fromUDynamicOffsets[window])|0x80; - length=2; - goto outputBytes; - } - } else if((window=getWindow(staticOffsets, c))>=0) { - /* quote from static window */ - c=((uint32_t)(SQ0+window)<<8)|(c-staticOffsets[window]); - length=2; - goto outputBytes; - } else if((code=getDynamicOffset(c, &offset))>=0) { - /* define a dynamic window with this character */ - dynamicWindow=getNextDynamicWindow(scsu); - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)(SD0+dynamicWindow)<<16)|((uint32_t)code<<8)|(c-currentOffset)|0x80; - length=3; - goto outputBytes; - } else if((uint32_t)(c-0x3400)<(0xd800-0x3400) && - (source>=sourceLimit || (uint32_t)(*source-0x3400)<(0xd800-0x3400)) - ) { - /* - * this character is not compressible (a BMP ideograph or similar); - * switch to Unicode mode if this is the last character in the block - * or there is at least one more ideograph following immediately - */ - isSingleByteMode=false; - c|=SCU<<16; - length=3; - goto outputBytes; - } else { - /* quote Unicode */ - c|=SQU<<16; - length=3; - goto outputBytes; - } - } - - /* normal end of conversion: prepare for a new character */ - c=0; - sourceIndex=nextSourceIndex; - } - } else { - if(c!=0 && targetCapacity>0) { - goto getTrailUnicode; - } - - /* state machine for Unicode mode */ -/* unicodeByteMode: */ - while(source=2) { - *target++=(uint8_t)(c>>8); - *target++=(uint8_t)c; - if(offsets!=NULL) { - *offsets++=sourceIndex; - *offsets++=sourceIndex; - } - targetCapacity-=2; - } else { - length=2; - goto outputBytes; - } - } else if((uint32_t)(c-0x3400)>=(0xf300-0x3400) /* c<0x3400 || c>=0xf300 */) { - /* compress BMP character if the following one is not an uncompressible ideograph */ - if(!(sourcefromUDynamicOffsets, c))>=0) { - /* there is a dynamic window that contains this character, change to it */ - isSingleByteMode=true; - dynamicWindow=window; - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)(UC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; - length=2; - goto outputBytes; - } else if((code=getDynamicOffset(c, &offset))>=0) { - /* define a dynamic window with this character */ - isSingleByteMode=true; - dynamicWindow=getNextDynamicWindow(scsu); - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)(UD0+dynamicWindow)<<16)|((uint32_t)code<<8)|(c-currentOffset)|0x80; - length=3; - goto outputBytes; - } - } - - /* don't know how to compress this character, just write it directly */ - length=2; - goto outputBytes; - } else if(c<0xe000) { - /* c is a surrogate */ - if(U16_IS_SURROGATE_LEAD(c)) { -getTrailUnicode: - lead=(UChar)c; - if(sourcefromUDynamicOffsets, c))>=0 && - !(sourcefromUDynamicOffsets[dynamicWindow]; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)(UC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; - length=2; - goto outputBytes; - } else if(source=0 - ) { - /* two supplementary characters in (probably) the same window - define an extended one */ - isSingleByteMode=true; - code-=0x200; - dynamicWindow=getNextDynamicWindow(scsu); - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)UDX<<24)|((uint32_t)dynamicWindow<<21)|((uint32_t)code<<8)|(c-currentOffset)|0x80; - length=4; - goto outputBytes; - } else { - /* don't know how to compress this character, just write it directly */ - c=((uint32_t)lead<<16)|trail; - length=4; - goto outputBytes; - } - } else /* 0xe000<=c<0xf300 */ { - /* quote to avoid SCSU tags */ - c|=UQU<<16; - length=3; - goto outputBytes; - } - - /* normal end of conversion: prepare for a new character */ - c=0; - sourceIndex=nextSourceIndex; - } - } -endloop: - - /* set the converter state back into UConverter */ - scsu->fromUIsSingleByteMode=isSingleByteMode; - scsu->fromUDynamicWindow=dynamicWindow; - - cnv->fromUChar32=c; - - /* write back the updated pointers */ - pArgs->source=source; - pArgs->target=(char *)target; - pArgs->offsets=offsets; - return; - -outputBytes: - /* write the output character bytes from c and length [code copied from ucnvmbcs.c] */ - /* from the first if in the loop we know that targetCapacity>0 */ - if(length<=targetCapacity) { - if(offsets==NULL) { - switch(length) { - /* each branch falls through to the next one */ - case 4: - *target++=(uint8_t)(c>>24); - U_FALLTHROUGH; - case 3: - *target++=(uint8_t)(c>>16); - U_FALLTHROUGH; - case 2: - *target++=(uint8_t)(c>>8); - U_FALLTHROUGH; - case 1: - *target++=(uint8_t)c; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - } else { - switch(length) { - /* each branch falls through to the next one */ - case 4: - *target++=(uint8_t)(c>>24); - *offsets++=sourceIndex; - U_FALLTHROUGH; - case 3: - *target++=(uint8_t)(c>>16); - *offsets++=sourceIndex; - U_FALLTHROUGH; - case 2: - *target++=(uint8_t)(c>>8); - *offsets++=sourceIndex; - U_FALLTHROUGH; - case 1: - *target++=(uint8_t)c; - *offsets++=sourceIndex; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - } - targetCapacity-=length; - - /* normal end of conversion: prepare for a new character */ - c=0; - sourceIndex=nextSourceIndex; - goto loop; - } else { - uint8_t *p; - - /* - * We actually do this backwards here: - * In order to save an intermediate variable, we output - * first to the overflow buffer what does not fit into the - * regular target. - */ - /* we know that 0<=targetCapacitycharErrorBuffer; - switch(length) { - /* each branch falls through to the next one */ - case 4: - *p++=(uint8_t)(c>>24); - U_FALLTHROUGH; - case 3: - *p++=(uint8_t)(c>>16); - U_FALLTHROUGH; - case 2: - *p++=(uint8_t)(c>>8); - U_FALLTHROUGH; - case 1: - *p=(uint8_t)c; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - cnv->charErrorBufferLength=(int8_t)length; - - /* now output what fits into the regular target */ - c>>=8*length; /* length was reduced by targetCapacity */ - switch(targetCapacity) { - /* each branch falls through to the next one */ - case 3: - *target++=(uint8_t)(c>>16); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - U_FALLTHROUGH; - case 2: - *target++=(uint8_t)(c>>8); - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - U_FALLTHROUGH; - case 1: - *target++=(uint8_t)c; - if(offsets!=NULL) { - *offsets++=sourceIndex; - } - U_FALLTHROUGH; - default: - break; - } - - /* target overflow */ - targetCapacity=0; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - c=0; - goto endloop; - } -} - -/* - * Identical to _SCSUFromUnicodeWithOffsets but without offset handling. - * If a change is made in the original function, then either - * change this function the same way or - * re-copy the original function and remove the variables - * offsets, sourceIndex, and nextSourceIndex. - */ -static void U_CALLCONV -_SCSUFromUnicode(UConverterFromUnicodeArgs *pArgs, - UErrorCode *pErrorCode) { - UConverter *cnv; - SCSUData *scsu; - const UChar *source, *sourceLimit; - uint8_t *target; - int32_t targetCapacity; - - UBool isSingleByteMode; - uint8_t dynamicWindow; - uint32_t currentOffset; - - uint32_t c, delta; - - int32_t length; - - /* variables for compression heuristics */ - uint32_t offset; - UChar lead, trail; - int code; - int8_t window; - - /* set up the local pointers */ - cnv=pArgs->converter; - scsu=(SCSUData *)cnv->extraInfo; - - /* set up the local pointers */ - source=pArgs->source; - sourceLimit=pArgs->sourceLimit; - target=(uint8_t *)pArgs->target; - targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); - - /* get the state machine state */ - isSingleByteMode=scsu->fromUIsSingleByteMode; - dynamicWindow=scsu->fromUDynamicWindow; - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; - - c=cnv->fromUChar32; - - /* similar conversion "loop" as in toUnicode */ -loop: - if(isSingleByteMode) { - if(c!=0 && targetCapacity>0) { - goto getTrailSingle; - } - - /* state machine for single-byte mode */ -/* singleByteMode: */ - while(sourcefromUDynamicOffsets, c))>=0) { - /* there is a dynamic window that contains this character, change to it */ - dynamicWindow=window; - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)(SC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; - length=2; - goto outputBytes; - } else if((code=getDynamicOffset(c, &offset))>=0) { - /* might check if there are more characters in this window to come */ - /* define an extended window with this character */ - code-=0x200; - dynamicWindow=getNextDynamicWindow(scsu); - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)SDX<<24)|((uint32_t)dynamicWindow<<21)|((uint32_t)code<<8)|(c-currentOffset)|0x80; - length=4; - goto outputBytes; - } else { - /* change to Unicode mode and output this (lead, trail) pair */ - isSingleByteMode=false; - *target++=(uint8_t)SCU; - --targetCapacity; - c=((uint32_t)lead<<16)|trail; - length=4; - goto outputBytes; - } - } else if(c<0xa0) { - /* quote C1 control character */ - c=(c&0x7f)|(SQ0+1)<<8; /* SQ0+1==SQ1 */ - length=2; - goto outputBytes; - } else if(c==0xfeff || c>=0xfff0) { - /* quote signature character=byte order mark and specials */ - c|=SQU<<16; - length=3; - goto outputBytes; - } else { - /* compress all other BMP characters */ - if((window=getWindow(scsu->fromUDynamicOffsets, c))>=0) { - /* there is a window defined that contains this character - switch to it or quote from it? */ - if(source>=sourceLimit || isInOffsetWindowOrDirect(scsu->fromUDynamicOffsets[window], *source)) { - /* change to dynamic window */ - dynamicWindow=window; - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)(SC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; - length=2; - goto outputBytes; - } else { - /* quote from dynamic window */ - c=((uint32_t)(SQ0+window)<<8)|(c-scsu->fromUDynamicOffsets[window])|0x80; - length=2; - goto outputBytes; - } - } else if((window=getWindow(staticOffsets, c))>=0) { - /* quote from static window */ - c=((uint32_t)(SQ0+window)<<8)|(c-staticOffsets[window]); - length=2; - goto outputBytes; - } else if((code=getDynamicOffset(c, &offset))>=0) { - /* define a dynamic window with this character */ - dynamicWindow=getNextDynamicWindow(scsu); - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)(SD0+dynamicWindow)<<16)|((uint32_t)code<<8)|(c-currentOffset)|0x80; - length=3; - goto outputBytes; - } else if((uint32_t)(c-0x3400)<(0xd800-0x3400) && - (source>=sourceLimit || (uint32_t)(*source-0x3400)<(0xd800-0x3400)) - ) { - /* - * this character is not compressible (a BMP ideograph or similar); - * switch to Unicode mode if this is the last character in the block - * or there is at least one more ideograph following immediately - */ - isSingleByteMode=false; - c|=SCU<<16; - length=3; - goto outputBytes; - } else { - /* quote Unicode */ - c|=SQU<<16; - length=3; - goto outputBytes; - } - } - - /* normal end of conversion: prepare for a new character */ - c=0; - } - } else { - if(c!=0 && targetCapacity>0) { - goto getTrailUnicode; - } - - /* state machine for Unicode mode */ -/* unicodeByteMode: */ - while(source=2) { - *target++=(uint8_t)(c>>8); - *target++=(uint8_t)c; - targetCapacity-=2; - } else { - length=2; - goto outputBytes; - } - } else if((uint32_t)(c-0x3400)>=(0xf300-0x3400) /* c<0x3400 || c>=0xf300 */) { - /* compress BMP character if the following one is not an uncompressible ideograph */ - if(!(sourcefromUDynamicOffsets, c))>=0) { - /* there is a dynamic window that contains this character, change to it */ - isSingleByteMode=true; - dynamicWindow=window; - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)(UC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; - length=2; - goto outputBytes; - } else if((code=getDynamicOffset(c, &offset))>=0) { - /* define a dynamic window with this character */ - isSingleByteMode=true; - dynamicWindow=getNextDynamicWindow(scsu); - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)(UD0+dynamicWindow)<<16)|((uint32_t)code<<8)|(c-currentOffset)|0x80; - length=3; - goto outputBytes; - } - } - - /* don't know how to compress this character, just write it directly */ - length=2; - goto outputBytes; - } else if(c<0xe000) { - /* c is a surrogate */ - if(U16_IS_SURROGATE_LEAD(c)) { -getTrailUnicode: - lead=(UChar)c; - if(sourcefromUDynamicOffsets, c))>=0 && - !(sourcefromUDynamicOffsets[dynamicWindow]; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)(UC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; - length=2; - goto outputBytes; - } else if(source=0 - ) { - /* two supplementary characters in (probably) the same window - define an extended one */ - isSingleByteMode=true; - code-=0x200; - dynamicWindow=getNextDynamicWindow(scsu); - currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; - useDynamicWindow(scsu, dynamicWindow); - c=((uint32_t)UDX<<24)|((uint32_t)dynamicWindow<<21)|((uint32_t)code<<8)|(c-currentOffset)|0x80; - length=4; - goto outputBytes; - } else { - /* don't know how to compress this character, just write it directly */ - c=((uint32_t)lead<<16)|trail; - length=4; - goto outputBytes; - } - } else /* 0xe000<=c<0xf300 */ { - /* quote to avoid SCSU tags */ - c|=UQU<<16; - length=3; - goto outputBytes; - } - - /* normal end of conversion: prepare for a new character */ - c=0; - } - } -endloop: - - /* set the converter state back into UConverter */ - scsu->fromUIsSingleByteMode=isSingleByteMode; - scsu->fromUDynamicWindow=dynamicWindow; - - cnv->fromUChar32=c; - - /* write back the updated pointers */ - pArgs->source=source; - pArgs->target=(char *)target; - return; - -outputBytes: - /* write the output character bytes from c and length [code copied from ucnvmbcs.c] */ - /* from the first if in the loop we know that targetCapacity>0 */ - if(length<=targetCapacity) { - switch(length) { - /* each branch falls through to the next one */ - case 4: - *target++=(uint8_t)(c>>24); - U_FALLTHROUGH; - case 3: - *target++=(uint8_t)(c>>16); - U_FALLTHROUGH; - case 2: - *target++=(uint8_t)(c>>8); - U_FALLTHROUGH; - case 1: - *target++=(uint8_t)c; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - targetCapacity-=length; - - /* normal end of conversion: prepare for a new character */ - c=0; - goto loop; - } else { - uint8_t *p; - - /* - * We actually do this backwards here: - * In order to save an intermediate variable, we output - * first to the overflow buffer what does not fit into the - * regular target. - */ - /* we know that 0<=targetCapacitycharErrorBuffer; - switch(length) { - /* each branch falls through to the next one */ - case 4: - *p++=(uint8_t)(c>>24); - U_FALLTHROUGH; - case 3: - *p++=(uint8_t)(c>>16); - U_FALLTHROUGH; - case 2: - *p++=(uint8_t)(c>>8); - U_FALLTHROUGH; - case 1: - *p=(uint8_t)c; - U_FALLTHROUGH; - default: - /* will never occur */ - break; - } - cnv->charErrorBufferLength=(int8_t)length; - - /* now output what fits into the regular target */ - c>>=8*length; /* length was reduced by targetCapacity */ - switch(targetCapacity) { - /* each branch falls through to the next one */ - case 3: - *target++=(uint8_t)(c>>16); - U_FALLTHROUGH; - case 2: - *target++=(uint8_t)(c>>8); - U_FALLTHROUGH; - case 1: - *target++=(uint8_t)c; - U_FALLTHROUGH; - default: - break; - } - - /* target overflow */ - targetCapacity=0; - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - c=0; - goto endloop; - } -} - -/* miscellaneous ------------------------------------------------------------ */ - -static const char * U_CALLCONV -_SCSUGetName(const UConverter *cnv) { - SCSUData *scsu=(SCSUData *)cnv->extraInfo; - - switch(scsu->locale) { - case l_ja: - return "SCSU,locale=ja"; - default: - return "SCSU"; - } -} - -/* structure for SafeClone calculations */ -struct cloneSCSUStruct -{ - UConverter cnv; - SCSUData mydata; -}; - -static UConverter * U_CALLCONV -_SCSUSafeClone(const UConverter *cnv, - void *stackBuffer, - int32_t *pBufferSize, - UErrorCode *status) -{ - struct cloneSCSUStruct * localClone; - int32_t bufferSizeNeeded = sizeof(struct cloneSCSUStruct); - - if (U_FAILURE(*status)){ - return 0; - } - - if (*pBufferSize == 0){ /* 'preflighting' request - set needed size into *pBufferSize */ - *pBufferSize = bufferSizeNeeded; - return 0; - } - - localClone = (struct cloneSCSUStruct *)stackBuffer; - /* ucnv.c/ucnv_safeClone() copied the main UConverter already */ - - uprv_memcpy(&localClone->mydata, cnv->extraInfo, sizeof(SCSUData)); - localClone->cnv.extraInfo = &localClone->mydata; - localClone->cnv.isExtraLocal = true; - - return &localClone->cnv; -} -U_CDECL_END - -static const UConverterImpl _SCSUImpl={ - UCNV_SCSU, - - NULL, - NULL, - - _SCSUOpen, - _SCSUClose, - _SCSUReset, - - _SCSUToUnicode, - _SCSUToUnicodeWithOffsets, - _SCSUFromUnicode, - _SCSUFromUnicodeWithOffsets, - NULL, - - NULL, - _SCSUGetName, - NULL, - _SCSUSafeClone, - ucnv_getCompleteUnicodeSet, - NULL, - NULL -}; - -static const UConverterStaticData _SCSUStaticData={ - sizeof(UConverterStaticData), - "SCSU", - 1212, /* CCSID for SCSU */ - UCNV_IBM, UCNV_SCSU, - 1, 3, /* one UChar generates at least 1 byte and at most 3 bytes */ - /* - * The subchar here is ignored because _SCSUOpen() sets U+fffd as a Unicode - * substitution string. - */ - { 0x0e, 0xff, 0xfd, 0 }, 3, - false, false, - 0, - 0, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ -}; - -const UConverterSharedData _SCSUData= - UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_SCSUStaticData, &_SCSUImpl); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2000-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ucnvscsu.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2000nov18 +* created by: Markus W. Scherer +* +* This is an implementation of the Standard Compression Scheme for Unicode +* as defined in https://www.unicode.org/reports/tr6/ . +* Reserved commands and window settings are treated as illegal sequences and +* will result in callback calls. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/ucnv_cb.h" +#include "unicode/utf16.h" +#include "ucnv_bld.h" +#include "ucnv_cnv.h" +#include "cmemory.h" + +/* SCSU definitions --------------------------------------------------------- */ + +/* SCSU command byte values */ +enum { + SQ0=0x01, /* Quote from window pair 0 */ + SQ7=0x08, /* Quote from window pair 7 */ + SDX=0x0B, /* Define a window as extended */ + Srs=0x0C, /* reserved */ + SQU=0x0E, /* Quote a single Unicode character */ + SCU=0x0F, /* Change to Unicode mode */ + SC0=0x10, /* Select window 0 */ + SC7=0x17, /* Select window 7 */ + SD0=0x18, /* Define and select window 0 */ + SD7=0x1F, /* Define and select window 7 */ + + UC0=0xE0, /* Select window 0 */ + UC7=0xE7, /* Select window 7 */ + UD0=0xE8, /* Define and select window 0 */ + UD7=0xEF, /* Define and select window 7 */ + UQU=0xF0, /* Quote a single Unicode character */ + UDX=0xF1, /* Define a Window as extended */ + Urs=0xF2 /* reserved */ +}; + +enum { + /* + * Unicode code points from 3400 to E000 are not adressible by + * dynamic window, since in these areas no short run alphabets are + * found. Therefore add gapOffset to all values from gapThreshold. + */ + gapThreshold=0x68, + gapOffset=0xAC00, + + /* values between reservedStart and fixedThreshold are reserved */ + reservedStart=0xA8, + + /* use table of predefined fixed offsets for values from fixedThreshold */ + fixedThreshold=0xF9 +}; + +/* constant offsets for the 8 static windows */ +static const uint32_t staticOffsets[8]={ + 0x0000, /* ASCII for quoted tags */ + 0x0080, /* Latin - 1 Supplement (for access to punctuation) */ + 0x0100, /* Latin Extended-A */ + 0x0300, /* Combining Diacritical Marks */ + 0x2000, /* General Punctuation */ + 0x2080, /* Currency Symbols */ + 0x2100, /* Letterlike Symbols and Number Forms */ + 0x3000 /* CJK Symbols and punctuation */ +}; + +/* initial offsets for the 8 dynamic (sliding) windows */ +static const uint32_t initialDynamicOffsets[8]={ + 0x0080, /* Latin-1 */ + 0x00C0, /* Latin Extended A */ + 0x0400, /* Cyrillic */ + 0x0600, /* Arabic */ + 0x0900, /* Devanagari */ + 0x3040, /* Hiragana */ + 0x30A0, /* Katakana */ + 0xFF00 /* Fullwidth ASCII */ +}; + +/* Table of fixed predefined Offsets */ +static const uint32_t fixedOffsets[]={ + /* 0xF9 */ 0x00C0, /* Latin-1 Letters + half of Latin Extended A */ + /* 0xFA */ 0x0250, /* IPA extensions */ + /* 0xFB */ 0x0370, /* Greek */ + /* 0xFC */ 0x0530, /* Armenian */ + /* 0xFD */ 0x3040, /* Hiragana */ + /* 0xFE */ 0x30A0, /* Katakana */ + /* 0xFF */ 0xFF60 /* Halfwidth Katakana */ +}; + +/* state values */ +enum { + readCommand, + quotePairOne, + quotePairTwo, + quoteOne, + definePairOne, + definePairTwo, + defineOne +}; + +typedef struct SCSUData { + /* dynamic window offsets, initialize to default values from initialDynamicOffsets */ + uint32_t toUDynamicOffsets[8]; + uint32_t fromUDynamicOffsets[8]; + + /* state machine state - toUnicode */ + UBool toUIsSingleByteMode; + uint8_t toUState; + int8_t toUQuoteWindow, toUDynamicWindow; + uint8_t toUByteOne; + uint8_t toUPadding[3]; + + /* state machine state - fromUnicode */ + UBool fromUIsSingleByteMode; + int8_t fromUDynamicWindow; + + /* + * windowUse[] keeps track of the use of the dynamic windows: + * At nextWindowUseIndex there is the least recently used window, + * and the following windows (in a wrapping manner) are more and more + * recently used. + * At nextWindowUseIndex-1 there is the most recently used window. + */ + uint8_t locale; + int8_t nextWindowUseIndex; + int8_t windowUse[8]; +} SCSUData; + +static const int8_t initialWindowUse[8]={ 7, 0, 3, 2, 4, 5, 6, 1 }; +static const int8_t initialWindowUse_ja[8]={ 3, 2, 4, 1, 0, 7, 5, 6 }; + +enum { + lGeneric, l_ja +}; + +/* SCSU setup functions ----------------------------------------------------- */ +U_CDECL_BEGIN +static void U_CALLCONV +_SCSUReset(UConverter *cnv, UConverterResetChoice choice) { + SCSUData *scsu=(SCSUData *)cnv->extraInfo; + + if(choice<=UCNV_RESET_TO_UNICODE) { + /* reset toUnicode */ + uprv_memcpy(scsu->toUDynamicOffsets, initialDynamicOffsets, 32); + + scsu->toUIsSingleByteMode=true; + scsu->toUState=readCommand; + scsu->toUQuoteWindow=scsu->toUDynamicWindow=0; + scsu->toUByteOne=0; + + cnv->toULength=0; + } + if(choice!=UCNV_RESET_TO_UNICODE) { + /* reset fromUnicode */ + uprv_memcpy(scsu->fromUDynamicOffsets, initialDynamicOffsets, 32); + + scsu->fromUIsSingleByteMode=true; + scsu->fromUDynamicWindow=0; + + scsu->nextWindowUseIndex=0; + switch(scsu->locale) { + case l_ja: + uprv_memcpy(scsu->windowUse, initialWindowUse_ja, 8); + break; + default: + uprv_memcpy(scsu->windowUse, initialWindowUse, 8); + break; + } + + cnv->fromUChar32=0; + } +} + +static void U_CALLCONV +_SCSUOpen(UConverter *cnv, + UConverterLoadArgs *pArgs, + UErrorCode *pErrorCode) { + const char *locale=pArgs->locale; + if(pArgs->onlyTestIsLoadable) { + return; + } + cnv->extraInfo=uprv_malloc(sizeof(SCSUData)); + if(cnv->extraInfo!=nullptr) { + if(locale!=nullptr && locale[0]=='j' && locale[1]=='a' && (locale[2]==0 || locale[2]=='_')) { + ((SCSUData *)cnv->extraInfo)->locale=l_ja; + } else { + ((SCSUData *)cnv->extraInfo)->locale=lGeneric; + } + _SCSUReset(cnv, UCNV_RESET_BOTH); + } else { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + } + + /* Set the substitution character U+fffd as a Unicode string. */ + cnv->subUChars[0]=0xfffd; + cnv->subCharLen=-1; +} + +static void U_CALLCONV +_SCSUClose(UConverter *cnv) { + if(cnv->extraInfo!=nullptr) { + if(!cnv->isExtraLocal) { + uprv_free(cnv->extraInfo); + } + cnv->extraInfo=nullptr; + } +} + +/* SCSU-to-Unicode conversion functions ------------------------------------- */ + +static void U_CALLCONV +_SCSUToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + SCSUData *scsu; + const uint8_t *source, *sourceLimit; + char16_t *target; + const char16_t *targetLimit; + int32_t *offsets; + UBool isSingleByteMode; + uint8_t state, byteOne; + int8_t quoteWindow, dynamicWindow; + + int32_t sourceIndex, nextSourceIndex; + + uint8_t b; + + /* set up the local pointers */ + cnv=pArgs->converter; + scsu=(SCSUData *)cnv->extraInfo; + + source=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + target=pArgs->target; + targetLimit=pArgs->targetLimit; + offsets=pArgs->offsets; + + /* get the state machine state */ + isSingleByteMode=scsu->toUIsSingleByteMode; + state=scsu->toUState; + quoteWindow=scsu->toUQuoteWindow; + dynamicWindow=scsu->toUDynamicWindow; + byteOne=scsu->toUByteOne; + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex=state==readCommand ? 0 : -1; + nextSourceIndex=0; + + /* + * conversion "loop" + * + * For performance, this is not a normal C loop. + * Instead, there are two code blocks for the two SCSU modes. + * The function branches to either one, and a change of the mode is done with a goto to + * the other branch. + * + * Each branch has two conventional loops: + * - a fast-path loop for the most common codes in the mode + * - a loop for all other codes in the mode + * When the fast-path runs into a code that it cannot handle, its loop ends and it + * runs into the following loop to handle the other codes. + * The end of the input or output buffer is also handled by the slower loop. + * The slow loop jumps (goto) to the fast-path loop again as soon as possible. + * + * The callback handling is done by returning with an error code. + * The conversion framework actually calls the callback function. + */ + if(isSingleByteMode) { + /* fast path for single-byte mode */ + if(state==readCommand) { +fastSingle: + while(source=0x20) { + ++source; + ++nextSourceIndex; + if(b<=0x7f) { + /* write US-ASCII graphic character or DEL */ + *target++=(char16_t)b; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + } else { + /* write from dynamic window */ + uint32_t c=scsu->toUDynamicOffsets[dynamicWindow]+(b&0x7f); + if(c<=0xffff) { + *target++=(char16_t)c; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + } else { + /* output surrogate pair */ + *target++=(char16_t)(0xd7c0+(c>>10)); + if(targetUCharErrorBuffer[0]=(char16_t)(0xdc00|(c&0x3ff)); + cnv->UCharErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + goto endloop; + } + } + } + sourceIndex=nextSourceIndex; + } + } + + /* normal state machine for single-byte mode, minus handling for what fastSingle covers */ +singleByteMode: + while(source=targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + b=*source++; + ++nextSourceIndex; + switch(state) { + case readCommand: + /* redundant conditions are commented out */ + /* here: b<0x20 because otherwise we would be in fastSingle */ + if((1UL<toUBytes[0]=b; + cnv->toULength=1; + goto endloop; + } + + /* store the first byte of a multibyte sequence in toUBytes[] */ + cnv->toUBytes[0]=b; + cnv->toULength=1; + break; + case quotePairOne: + byteOne=b; + cnv->toUBytes[1]=b; + cnv->toULength=2; + state=quotePairTwo; + break; + case quotePairTwo: + *target++=(char16_t)((byteOne<<8)|b); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + sourceIndex=nextSourceIndex; + state=readCommand; + goto fastSingle; + case quoteOne: + if(b<0x80) { + /* all static offsets are in the BMP */ + *target++=(char16_t)(staticOffsets[quoteWindow]+b); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + } else { + /* write from dynamic window */ + uint32_t c=scsu->toUDynamicOffsets[quoteWindow]+(b&0x7f); + if(c<=0xffff) { + *target++=(char16_t)c; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + } else { + /* output surrogate pair */ + *target++=(char16_t)(0xd7c0+(c>>10)); + if(targetUCharErrorBuffer[0]=(char16_t)(0xdc00|(c&0x3ff)); + cnv->UCharErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + goto endloop; + } + } + } + sourceIndex=nextSourceIndex; + state=readCommand; + goto fastSingle; + case definePairOne: + dynamicWindow=(int8_t)((b>>5)&7); + byteOne=(uint8_t)(b&0x1f); + cnv->toUBytes[1]=b; + cnv->toULength=2; + state=definePairTwo; + break; + case definePairTwo: + scsu->toUDynamicOffsets[dynamicWindow]=0x10000+(byteOne<<15UL | b<<7UL); + sourceIndex=nextSourceIndex; + state=readCommand; + goto fastSingle; + case defineOne: + if(b==0) { + /* callback(illegal): Reserved window offset value 0 */ + cnv->toUBytes[1]=b; + cnv->toULength=2; + goto endloop; + } else if(btoUDynamicOffsets[dynamicWindow]=b<<7UL; + } else if((uint8_t)(b-gapThreshold)<(reservedStart-gapThreshold)) { + scsu->toUDynamicOffsets[dynamicWindow]=(b<<7UL)+gapOffset; + } else if(b>=fixedThreshold) { + scsu->toUDynamicOffsets[dynamicWindow]=fixedOffsets[b-fixedThreshold]; + } else { + /* callback(illegal): Reserved window offset value 0xa8..0xf8 */ + cnv->toUBytes[1]=b; + cnv->toULength=2; + goto endloop; + } + sourceIndex=nextSourceIndex; + state=readCommand; + goto fastSingle; + } + } + } else { + /* fast path for Unicode mode */ + if(state==readCommand) { +fastUnicode: + while(source+1(Urs-UC0)) { + *target++=(char16_t)((b<<8)|source[1]); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + sourceIndex=nextSourceIndex; + nextSourceIndex+=2; + source+=2; + } + } + + /* normal state machine for Unicode mode */ +/* unicodeByteMode: */ + while(source=targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + b=*source++; + ++nextSourceIndex; + switch(state) { + case readCommand: + if((uint8_t)(b-UC0)>(Urs-UC0)) { + byteOne=b; + cnv->toUBytes[0]=b; + cnv->toULength=1; + state=quotePairTwo; + } else if(/* UC0<=b && */ b<=UC7) { + dynamicWindow=(int8_t)(b-UC0); + sourceIndex=nextSourceIndex; + isSingleByteMode=true; + goto fastSingle; + } else if(/* UD0<=b && */ b<=UD7) { + dynamicWindow=(int8_t)(b-UD0); + isSingleByteMode=true; + cnv->toUBytes[0]=b; + cnv->toULength=1; + state=defineOne; + goto singleByteMode; + } else if(b==UDX) { + isSingleByteMode=true; + cnv->toUBytes[0]=b; + cnv->toULength=1; + state=definePairOne; + goto singleByteMode; + } else if(b==UQU) { + cnv->toUBytes[0]=b; + cnv->toULength=1; + state=quotePairOne; + } else /* Urs */ { + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + cnv->toUBytes[0]=b; + cnv->toULength=1; + goto endloop; + } + break; + case quotePairOne: + byteOne=b; + cnv->toUBytes[1]=b; + cnv->toULength=2; + state=quotePairTwo; + break; + case quotePairTwo: + *target++=(char16_t)((byteOne<<8)|b); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + sourceIndex=nextSourceIndex; + state=readCommand; + goto fastUnicode; + } + } + } +endloop: + + /* set the converter state back into UConverter */ + if(U_FAILURE(*pErrorCode) && *pErrorCode!=U_BUFFER_OVERFLOW_ERROR) { + /* reset to deal with the next character */ + state=readCommand; + } else if(state==readCommand) { + /* not in a multi-byte sequence, reset toULength */ + cnv->toULength=0; + } + scsu->toUIsSingleByteMode=isSingleByteMode; + scsu->toUState=state; + scsu->toUQuoteWindow=quoteWindow; + scsu->toUDynamicWindow=dynamicWindow; + scsu->toUByteOne=byteOne; + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + pArgs->offsets=offsets; + return; +} + +/* + * Identical to _SCSUToUnicodeWithOffsets but without offset handling. + * If a change is made in the original function, then either + * change this function the same way or + * re-copy the original function and remove the variables + * offsets, sourceIndex, and nextSourceIndex. + */ +static void U_CALLCONV +_SCSUToUnicode(UConverterToUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + SCSUData *scsu; + const uint8_t *source, *sourceLimit; + char16_t *target; + const char16_t *targetLimit; + UBool isSingleByteMode; + uint8_t state, byteOne; + int8_t quoteWindow, dynamicWindow; + + uint8_t b; + + /* set up the local pointers */ + cnv=pArgs->converter; + scsu=(SCSUData *)cnv->extraInfo; + + source=(const uint8_t *)pArgs->source; + sourceLimit=(const uint8_t *)pArgs->sourceLimit; + target=pArgs->target; + targetLimit=pArgs->targetLimit; + + /* get the state machine state */ + isSingleByteMode=scsu->toUIsSingleByteMode; + state=scsu->toUState; + quoteWindow=scsu->toUQuoteWindow; + dynamicWindow=scsu->toUDynamicWindow; + byteOne=scsu->toUByteOne; + + /* + * conversion "loop" + * + * For performance, this is not a normal C loop. + * Instead, there are two code blocks for the two SCSU modes. + * The function branches to either one, and a change of the mode is done with a goto to + * the other branch. + * + * Each branch has two conventional loops: + * - a fast-path loop for the most common codes in the mode + * - a loop for all other codes in the mode + * When the fast-path runs into a code that it cannot handle, its loop ends and it + * runs into the following loop to handle the other codes. + * The end of the input or output buffer is also handled by the slower loop. + * The slow loop jumps (goto) to the fast-path loop again as soon as possible. + * + * The callback handling is done by returning with an error code. + * The conversion framework actually calls the callback function. + */ + if(isSingleByteMode) { + /* fast path for single-byte mode */ + if(state==readCommand) { +fastSingle: + while(source=0x20) { + ++source; + if(b<=0x7f) { + /* write US-ASCII graphic character or DEL */ + *target++=(char16_t)b; + } else { + /* write from dynamic window */ + uint32_t c=scsu->toUDynamicOffsets[dynamicWindow]+(b&0x7f); + if(c<=0xffff) { + *target++=(char16_t)c; + } else { + /* output surrogate pair */ + *target++=(char16_t)(0xd7c0+(c>>10)); + if(targetUCharErrorBuffer[0]=(char16_t)(0xdc00|(c&0x3ff)); + cnv->UCharErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + goto endloop; + } + } + } + } + } + + /* normal state machine for single-byte mode, minus handling for what fastSingle covers */ +singleByteMode: + while(source=targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + b=*source++; + switch(state) { + case readCommand: + /* redundant conditions are commented out */ + /* here: b<0x20 because otherwise we would be in fastSingle */ + if((1UL<toUBytes[0]=b; + cnv->toULength=1; + goto endloop; + } + + /* store the first byte of a multibyte sequence in toUBytes[] */ + cnv->toUBytes[0]=b; + cnv->toULength=1; + break; + case quotePairOne: + byteOne=b; + cnv->toUBytes[1]=b; + cnv->toULength=2; + state=quotePairTwo; + break; + case quotePairTwo: + *target++=(char16_t)((byteOne<<8)|b); + state=readCommand; + goto fastSingle; + case quoteOne: + if(b<0x80) { + /* all static offsets are in the BMP */ + *target++=(char16_t)(staticOffsets[quoteWindow]+b); + } else { + /* write from dynamic window */ + uint32_t c=scsu->toUDynamicOffsets[quoteWindow]+(b&0x7f); + if(c<=0xffff) { + *target++=(char16_t)c; + } else { + /* output surrogate pair */ + *target++=(char16_t)(0xd7c0+(c>>10)); + if(targetUCharErrorBuffer[0]=(char16_t)(0xdc00|(c&0x3ff)); + cnv->UCharErrorBufferLength=1; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + goto endloop; + } + } + } + state=readCommand; + goto fastSingle; + case definePairOne: + dynamicWindow=(int8_t)((b>>5)&7); + byteOne=(uint8_t)(b&0x1f); + cnv->toUBytes[1]=b; + cnv->toULength=2; + state=definePairTwo; + break; + case definePairTwo: + scsu->toUDynamicOffsets[dynamicWindow]=0x10000+(byteOne<<15UL | b<<7UL); + state=readCommand; + goto fastSingle; + case defineOne: + if(b==0) { + /* callback(illegal): Reserved window offset value 0 */ + cnv->toUBytes[1]=b; + cnv->toULength=2; + goto endloop; + } else if(btoUDynamicOffsets[dynamicWindow]=b<<7UL; + } else if((uint8_t)(b-gapThreshold)<(reservedStart-gapThreshold)) { + scsu->toUDynamicOffsets[dynamicWindow]=(b<<7UL)+gapOffset; + } else if(b>=fixedThreshold) { + scsu->toUDynamicOffsets[dynamicWindow]=fixedOffsets[b-fixedThreshold]; + } else { + /* callback(illegal): Reserved window offset value 0xa8..0xf8 */ + cnv->toUBytes[1]=b; + cnv->toULength=2; + goto endloop; + } + state=readCommand; + goto fastSingle; + } + } + } else { + /* fast path for Unicode mode */ + if(state==readCommand) { +fastUnicode: + while(source+1(Urs-UC0)) { + *target++=(char16_t)((b<<8)|source[1]); + source+=2; + } + } + + /* normal state machine for Unicode mode */ +/* unicodeByteMode: */ + while(source=targetLimit) { + /* target is full */ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + break; + } + b=*source++; + switch(state) { + case readCommand: + if((uint8_t)(b-UC0)>(Urs-UC0)) { + byteOne=b; + cnv->toUBytes[0]=b; + cnv->toULength=1; + state=quotePairTwo; + } else if(/* UC0<=b && */ b<=UC7) { + dynamicWindow=(int8_t)(b-UC0); + isSingleByteMode=true; + goto fastSingle; + } else if(/* UD0<=b && */ b<=UD7) { + dynamicWindow=(int8_t)(b-UD0); + isSingleByteMode=true; + cnv->toUBytes[0]=b; + cnv->toULength=1; + state=defineOne; + goto singleByteMode; + } else if(b==UDX) { + isSingleByteMode=true; + cnv->toUBytes[0]=b; + cnv->toULength=1; + state=definePairOne; + goto singleByteMode; + } else if(b==UQU) { + cnv->toUBytes[0]=b; + cnv->toULength=1; + state=quotePairOne; + } else /* Urs */ { + /* callback(illegal) */ + *pErrorCode=U_ILLEGAL_CHAR_FOUND; + cnv->toUBytes[0]=b; + cnv->toULength=1; + goto endloop; + } + break; + case quotePairOne: + byteOne=b; + cnv->toUBytes[1]=b; + cnv->toULength=2; + state=quotePairTwo; + break; + case quotePairTwo: + *target++=(char16_t)((byteOne<<8)|b); + state=readCommand; + goto fastUnicode; + } + } + } +endloop: + + /* set the converter state back into UConverter */ + if(U_FAILURE(*pErrorCode) && *pErrorCode!=U_BUFFER_OVERFLOW_ERROR) { + /* reset to deal with the next character */ + state=readCommand; + } else if(state==readCommand) { + /* not in a multi-byte sequence, reset toULength */ + cnv->toULength=0; + } + scsu->toUIsSingleByteMode=isSingleByteMode; + scsu->toUState=state; + scsu->toUQuoteWindow=quoteWindow; + scsu->toUDynamicWindow=dynamicWindow; + scsu->toUByteOne=byteOne; + + /* write back the updated pointers */ + pArgs->source=(const char *)source; + pArgs->target=target; + return; +} +U_CDECL_END +/* SCSU-from-Unicode conversion functions ----------------------------------- */ + +/* + * This SCSU Encoder is fairly simple but uses all SCSU commands to achieve + * reasonable results. The lookahead is minimal. + * Many cases are simple: + * A character fits directly into the current mode, a dynamic or static window, + * or is not compressible. These cases are tested first. + * Real compression heuristics are applied to the rest, in code branches for + * single/Unicode mode and BMP/supplementary code points. + * The heuristics used here are extremely simple. + */ + +/* get the number of the window that this character is in, or -1 */ +static int8_t +getWindow(const uint32_t offsets[8], uint32_t c) { + int i; + for(i=0; i<8; ++i) { + if((uint32_t)(c-offsets[i])<=0x7f) { + return (int8_t)(i); + } + } + return -1; +} + +/* is the character in the dynamic window starting at the offset, or in the direct-encoded range? */ +static UBool +isInOffsetWindowOrDirect(uint32_t offset, uint32_t c) { + return (UBool)(c<=offset+0x7f && + (c>=offset || (c<=0x7f && + (c>=0x20 || (1UL<windowUse[scsu->nextWindowUseIndex]; + if(++scsu->nextWindowUseIndex==8) { + scsu->nextWindowUseIndex=0; + } + return window; +} + +/* + * useDynamicWindow() adjusts + * windowUse[] and nextWindowUseIndex for the algorithm to choose + * the next dynamic window to be defined; + * a subclass may override it and provide its own algorithm. + */ +static void +useDynamicWindow(SCSUData *scsu, int8_t window) { + /* + * move the existing window, which just became the most recently used one, + * up in windowUse[] to nextWindowUseIndex-1 + */ + + /* first, find the index of the window - backwards to favor the more recently used windows */ + int i, j; + + i=scsu->nextWindowUseIndex; + do { + if(--i<0) { + i=7; + } + } while(scsu->windowUse[i]!=window); + + /* now copy each windowUse[i+1] to [i] */ + j=i+1; + if(j==8) { + j=0; + } + while(j!=scsu->nextWindowUseIndex) { + scsu->windowUse[i]=scsu->windowUse[j]; + i=j; + if(++j==8) { j=0; } + } + + /* finally, set the window into the most recently used index */ + scsu->windowUse[i]=window; +} + +/* + * calculate the offset and the code for a dynamic window that contains the character + * takes fixed offsets into account + * the offset of the window is stored in the offset variable, + * the code is returned + * + * return offset code: -1 none <=0xff code for SDn/UDn else code for SDX/UDX, subtract 0x200 to get the true code + */ +static int +getDynamicOffset(uint32_t c, uint32_t *pOffset) { + int i; + + for(i=0; i<7; ++i) { + if((uint32_t)(c-fixedOffsets[i])<=0x7f) { + *pOffset=fixedOffsets[i]; + return 0xf9+i; + } + } + + if(c<0x80) { + /* No dynamic window for US-ASCII. */ + return -1; + } else if(c<0x3400 || + (uint32_t)(c-0x10000)<(0x14000-0x10000) || + (uint32_t)(c-0x1d000)<=(0x1ffff-0x1d000) + ) { + /* This character is in a code range for a "small", i.e., reasonably windowable, script. */ + *pOffset=c&0x7fffff80; + return (int)(c>>7); + } else if(0xe000<=c && c!=0xfeff && c<0xfff0) { + /* For these characters we need to take the gapOffset into account. */ + *pOffset=c&0x7fffff80; + return (int)((c-gapOffset)>>7); + } else { + return -1; + } +} +U_CDECL_BEGIN +/* + * Idea for compression: + * - save SCSUData and other state before really starting work + * - at endloop, see if compression could be better with just unicode mode + * - don't do this if a callback has been called + * - if unicode mode would be smaller, then override the results with it - may need SCU at the beginning + * - different buffer handling! + * + * Drawback or need for corrective handling: + * it is desirable to encode U+feff as SQU fe ff for the SCSU signature, and + * it is desirable to start a document in US-ASCII/Latin-1 for as long as possible + * not only for compression but also for HTML/XML documents with following charset/encoding announcers. + * + * How to achieve both? + * - Only replace the result after an SDX or SCU? + */ + +static void U_CALLCONV +_SCSUFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + SCSUData *scsu; + const char16_t *source, *sourceLimit; + uint8_t *target; + int32_t targetCapacity; + int32_t *offsets; + + UBool isSingleByteMode; + uint8_t dynamicWindow; + uint32_t currentOffset; + + uint32_t c, delta; + + int32_t sourceIndex, nextSourceIndex; + + int32_t length; + + /* variables for compression heuristics */ + uint32_t offset; + char16_t lead, trail; + int code; + int8_t window; + + /* set up the local pointers */ + cnv=pArgs->converter; + scsu=(SCSUData *)cnv->extraInfo; + + /* set up the local pointers */ + source=pArgs->source; + sourceLimit=pArgs->sourceLimit; + target=(uint8_t *)pArgs->target; + targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); + offsets=pArgs->offsets; + + /* get the state machine state */ + isSingleByteMode=scsu->fromUIsSingleByteMode; + dynamicWindow=scsu->fromUDynamicWindow; + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; + + c=cnv->fromUChar32; + + /* sourceIndex=-1 if the current character began in the previous buffer */ + sourceIndex= c==0 ? 0 : -1; + nextSourceIndex=0; + + /* similar conversion "loop" as in toUnicode */ +loop: + if(isSingleByteMode) { + if(c!=0 && targetCapacity>0) { + goto getTrailSingle; + } + + /* state machine for single-byte mode */ +/* singleByteMode: */ + while(sourcefromUDynamicOffsets, c))>=0) { + /* there is a dynamic window that contains this character, change to it */ + dynamicWindow=window; + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)(SC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; + length=2; + goto outputBytes; + } else if((code=getDynamicOffset(c, &offset))>=0) { + /* might check if there are more characters in this window to come */ + /* define an extended window with this character */ + code-=0x200; + dynamicWindow=getNextDynamicWindow(scsu); + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)SDX<<24)|((uint32_t)dynamicWindow<<21)|((uint32_t)code<<8)|(c-currentOffset)|0x80; + length=4; + goto outputBytes; + } else { + /* change to Unicode mode and output this (lead, trail) pair */ + isSingleByteMode=false; + *target++=(uint8_t)SCU; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + --targetCapacity; + c=((uint32_t)lead<<16)|trail; + length=4; + goto outputBytes; + } + } else if(c<0xa0) { + /* quote C1 control character */ + c=(c&0x7f)|(SQ0+1)<<8; /* SQ0+1==SQ1 */ + length=2; + goto outputBytes; + } else if(c==0xfeff || c>=0xfff0) { + /* quote signature character=byte order mark and specials */ + c|=SQU<<16; + length=3; + goto outputBytes; + } else { + /* compress all other BMP characters */ + if((window=getWindow(scsu->fromUDynamicOffsets, c))>=0) { + /* there is a window defined that contains this character - switch to it or quote from it? */ + if(source>=sourceLimit || isInOffsetWindowOrDirect(scsu->fromUDynamicOffsets[window], *source)) { + /* change to dynamic window */ + dynamicWindow=window; + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)(SC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; + length=2; + goto outputBytes; + } else { + /* quote from dynamic window */ + c=((uint32_t)(SQ0+window)<<8)|(c-scsu->fromUDynamicOffsets[window])|0x80; + length=2; + goto outputBytes; + } + } else if((window=getWindow(staticOffsets, c))>=0) { + /* quote from static window */ + c=((uint32_t)(SQ0+window)<<8)|(c-staticOffsets[window]); + length=2; + goto outputBytes; + } else if((code=getDynamicOffset(c, &offset))>=0) { + /* define a dynamic window with this character */ + dynamicWindow=getNextDynamicWindow(scsu); + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)(SD0+dynamicWindow)<<16)|((uint32_t)code<<8)|(c-currentOffset)|0x80; + length=3; + goto outputBytes; + } else if((uint32_t)(c-0x3400)<(0xd800-0x3400) && + (source>=sourceLimit || (uint32_t)(*source-0x3400)<(0xd800-0x3400)) + ) { + /* + * this character is not compressible (a BMP ideograph or similar); + * switch to Unicode mode if this is the last character in the block + * or there is at least one more ideograph following immediately + */ + isSingleByteMode=false; + c|=SCU<<16; + length=3; + goto outputBytes; + } else { + /* quote Unicode */ + c|=SQU<<16; + length=3; + goto outputBytes; + } + } + + /* normal end of conversion: prepare for a new character */ + c=0; + sourceIndex=nextSourceIndex; + } + } else { + if(c!=0 && targetCapacity>0) { + goto getTrailUnicode; + } + + /* state machine for Unicode mode */ +/* unicodeByteMode: */ + while(source=2) { + *target++=(uint8_t)(c>>8); + *target++=(uint8_t)c; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + *offsets++=sourceIndex; + } + targetCapacity-=2; + } else { + length=2; + goto outputBytes; + } + } else if((uint32_t)(c-0x3400)>=(0xf300-0x3400) /* c<0x3400 || c>=0xf300 */) { + /* compress BMP character if the following one is not an uncompressible ideograph */ + if(!(sourcefromUDynamicOffsets, c))>=0) { + /* there is a dynamic window that contains this character, change to it */ + isSingleByteMode=true; + dynamicWindow=window; + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)(UC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; + length=2; + goto outputBytes; + } else if((code=getDynamicOffset(c, &offset))>=0) { + /* define a dynamic window with this character */ + isSingleByteMode=true; + dynamicWindow=getNextDynamicWindow(scsu); + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)(UD0+dynamicWindow)<<16)|((uint32_t)code<<8)|(c-currentOffset)|0x80; + length=3; + goto outputBytes; + } + } + + /* don't know how to compress this character, just write it directly */ + length=2; + goto outputBytes; + } else if(c<0xe000) { + /* c is a surrogate */ + if(U16_IS_SURROGATE_LEAD(c)) { +getTrailUnicode: + lead=(char16_t)c; + if(sourcefromUDynamicOffsets, c))>=0 && + !(sourcefromUDynamicOffsets[dynamicWindow]; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)(UC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; + length=2; + goto outputBytes; + } else if(source=0 + ) { + /* two supplementary characters in (probably) the same window - define an extended one */ + isSingleByteMode=true; + code-=0x200; + dynamicWindow=getNextDynamicWindow(scsu); + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)UDX<<24)|((uint32_t)dynamicWindow<<21)|((uint32_t)code<<8)|(c-currentOffset)|0x80; + length=4; + goto outputBytes; + } else { + /* don't know how to compress this character, just write it directly */ + c=((uint32_t)lead<<16)|trail; + length=4; + goto outputBytes; + } + } else /* 0xe000<=c<0xf300 */ { + /* quote to avoid SCSU tags */ + c|=UQU<<16; + length=3; + goto outputBytes; + } + + /* normal end of conversion: prepare for a new character */ + c=0; + sourceIndex=nextSourceIndex; + } + } +endloop: + + /* set the converter state back into UConverter */ + scsu->fromUIsSingleByteMode=isSingleByteMode; + scsu->fromUDynamicWindow=dynamicWindow; + + cnv->fromUChar32=c; + + /* write back the updated pointers */ + pArgs->source=source; + pArgs->target=(char *)target; + pArgs->offsets=offsets; + return; + +outputBytes: + /* write the output character bytes from c and length [code copied from ucnvmbcs.c] */ + /* from the first if in the loop we know that targetCapacity>0 */ + if(length<=targetCapacity) { + if(offsets==nullptr) { + switch(length) { + /* each branch falls through to the next one */ + case 4: + *target++=(uint8_t)(c>>24); + U_FALLTHROUGH; + case 3: + *target++=(uint8_t)(c>>16); + U_FALLTHROUGH; + case 2: + *target++=(uint8_t)(c>>8); + U_FALLTHROUGH; + case 1: + *target++=(uint8_t)c; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + } else { + switch(length) { + /* each branch falls through to the next one */ + case 4: + *target++=(uint8_t)(c>>24); + *offsets++=sourceIndex; + U_FALLTHROUGH; + case 3: + *target++=(uint8_t)(c>>16); + *offsets++=sourceIndex; + U_FALLTHROUGH; + case 2: + *target++=(uint8_t)(c>>8); + *offsets++=sourceIndex; + U_FALLTHROUGH; + case 1: + *target++=(uint8_t)c; + *offsets++=sourceIndex; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + } + targetCapacity-=length; + + /* normal end of conversion: prepare for a new character */ + c=0; + sourceIndex=nextSourceIndex; + goto loop; + } else { + uint8_t *p; + + /* + * We actually do this backwards here: + * In order to save an intermediate variable, we output + * first to the overflow buffer what does not fit into the + * regular target. + */ + /* we know that 0<=targetCapacitycharErrorBuffer; + switch(length) { + /* each branch falls through to the next one */ + case 4: + *p++=(uint8_t)(c>>24); + U_FALLTHROUGH; + case 3: + *p++=(uint8_t)(c>>16); + U_FALLTHROUGH; + case 2: + *p++=(uint8_t)(c>>8); + U_FALLTHROUGH; + case 1: + *p=(uint8_t)c; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + cnv->charErrorBufferLength=(int8_t)length; + + /* now output what fits into the regular target */ + c>>=8*length; /* length was reduced by targetCapacity */ + switch(targetCapacity) { + /* each branch falls through to the next one */ + case 3: + *target++=(uint8_t)(c>>16); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + U_FALLTHROUGH; + case 2: + *target++=(uint8_t)(c>>8); + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + U_FALLTHROUGH; + case 1: + *target++=(uint8_t)c; + if(offsets!=nullptr) { + *offsets++=sourceIndex; + } + U_FALLTHROUGH; + default: + break; + } + + /* target overflow */ + targetCapacity=0; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + c=0; + goto endloop; + } +} + +/* + * Identical to _SCSUFromUnicodeWithOffsets but without offset handling. + * If a change is made in the original function, then either + * change this function the same way or + * re-copy the original function and remove the variables + * offsets, sourceIndex, and nextSourceIndex. + */ +static void U_CALLCONV +_SCSUFromUnicode(UConverterFromUnicodeArgs *pArgs, + UErrorCode *pErrorCode) { + UConverter *cnv; + SCSUData *scsu; + const char16_t *source, *sourceLimit; + uint8_t *target; + int32_t targetCapacity; + + UBool isSingleByteMode; + uint8_t dynamicWindow; + uint32_t currentOffset; + + uint32_t c, delta; + + int32_t length; + + /* variables for compression heuristics */ + uint32_t offset; + char16_t lead, trail; + int code; + int8_t window; + + /* set up the local pointers */ + cnv=pArgs->converter; + scsu=(SCSUData *)cnv->extraInfo; + + /* set up the local pointers */ + source=pArgs->source; + sourceLimit=pArgs->sourceLimit; + target=(uint8_t *)pArgs->target; + targetCapacity=(int32_t)(pArgs->targetLimit-pArgs->target); + + /* get the state machine state */ + isSingleByteMode=scsu->fromUIsSingleByteMode; + dynamicWindow=scsu->fromUDynamicWindow; + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; + + c=cnv->fromUChar32; + + /* similar conversion "loop" as in toUnicode */ +loop: + if(isSingleByteMode) { + if(c!=0 && targetCapacity>0) { + goto getTrailSingle; + } + + /* state machine for single-byte mode */ +/* singleByteMode: */ + while(sourcefromUDynamicOffsets, c))>=0) { + /* there is a dynamic window that contains this character, change to it */ + dynamicWindow=window; + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)(SC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; + length=2; + goto outputBytes; + } else if((code=getDynamicOffset(c, &offset))>=0) { + /* might check if there are more characters in this window to come */ + /* define an extended window with this character */ + code-=0x200; + dynamicWindow=getNextDynamicWindow(scsu); + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)SDX<<24)|((uint32_t)dynamicWindow<<21)|((uint32_t)code<<8)|(c-currentOffset)|0x80; + length=4; + goto outputBytes; + } else { + /* change to Unicode mode and output this (lead, trail) pair */ + isSingleByteMode=false; + *target++=(uint8_t)SCU; + --targetCapacity; + c=((uint32_t)lead<<16)|trail; + length=4; + goto outputBytes; + } + } else if(c<0xa0) { + /* quote C1 control character */ + c=(c&0x7f)|(SQ0+1)<<8; /* SQ0+1==SQ1 */ + length=2; + goto outputBytes; + } else if(c==0xfeff || c>=0xfff0) { + /* quote signature character=byte order mark and specials */ + c|=SQU<<16; + length=3; + goto outputBytes; + } else { + /* compress all other BMP characters */ + if((window=getWindow(scsu->fromUDynamicOffsets, c))>=0) { + /* there is a window defined that contains this character - switch to it or quote from it? */ + if(source>=sourceLimit || isInOffsetWindowOrDirect(scsu->fromUDynamicOffsets[window], *source)) { + /* change to dynamic window */ + dynamicWindow=window; + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)(SC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; + length=2; + goto outputBytes; + } else { + /* quote from dynamic window */ + c=((uint32_t)(SQ0+window)<<8)|(c-scsu->fromUDynamicOffsets[window])|0x80; + length=2; + goto outputBytes; + } + } else if((window=getWindow(staticOffsets, c))>=0) { + /* quote from static window */ + c=((uint32_t)(SQ0+window)<<8)|(c-staticOffsets[window]); + length=2; + goto outputBytes; + } else if((code=getDynamicOffset(c, &offset))>=0) { + /* define a dynamic window with this character */ + dynamicWindow=getNextDynamicWindow(scsu); + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)(SD0+dynamicWindow)<<16)|((uint32_t)code<<8)|(c-currentOffset)|0x80; + length=3; + goto outputBytes; + } else if((uint32_t)(c-0x3400)<(0xd800-0x3400) && + (source>=sourceLimit || (uint32_t)(*source-0x3400)<(0xd800-0x3400)) + ) { + /* + * this character is not compressible (a BMP ideograph or similar); + * switch to Unicode mode if this is the last character in the block + * or there is at least one more ideograph following immediately + */ + isSingleByteMode=false; + c|=SCU<<16; + length=3; + goto outputBytes; + } else { + /* quote Unicode */ + c|=SQU<<16; + length=3; + goto outputBytes; + } + } + + /* normal end of conversion: prepare for a new character */ + c=0; + } + } else { + if(c!=0 && targetCapacity>0) { + goto getTrailUnicode; + } + + /* state machine for Unicode mode */ +/* unicodeByteMode: */ + while(source=2) { + *target++=(uint8_t)(c>>8); + *target++=(uint8_t)c; + targetCapacity-=2; + } else { + length=2; + goto outputBytes; + } + } else if((uint32_t)(c-0x3400)>=(0xf300-0x3400) /* c<0x3400 || c>=0xf300 */) { + /* compress BMP character if the following one is not an uncompressible ideograph */ + if(!(sourcefromUDynamicOffsets, c))>=0) { + /* there is a dynamic window that contains this character, change to it */ + isSingleByteMode=true; + dynamicWindow=window; + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)(UC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; + length=2; + goto outputBytes; + } else if((code=getDynamicOffset(c, &offset))>=0) { + /* define a dynamic window with this character */ + isSingleByteMode=true; + dynamicWindow=getNextDynamicWindow(scsu); + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)(UD0+dynamicWindow)<<16)|((uint32_t)code<<8)|(c-currentOffset)|0x80; + length=3; + goto outputBytes; + } + } + + /* don't know how to compress this character, just write it directly */ + length=2; + goto outputBytes; + } else if(c<0xe000) { + /* c is a surrogate */ + if(U16_IS_SURROGATE_LEAD(c)) { +getTrailUnicode: + lead=(char16_t)c; + if(sourcefromUDynamicOffsets, c))>=0 && + !(sourcefromUDynamicOffsets[dynamicWindow]; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)(UC0+dynamicWindow)<<8)|(c-currentOffset)|0x80; + length=2; + goto outputBytes; + } else if(source=0 + ) { + /* two supplementary characters in (probably) the same window - define an extended one */ + isSingleByteMode=true; + code-=0x200; + dynamicWindow=getNextDynamicWindow(scsu); + currentOffset=scsu->fromUDynamicOffsets[dynamicWindow]=offset; + useDynamicWindow(scsu, dynamicWindow); + c=((uint32_t)UDX<<24)|((uint32_t)dynamicWindow<<21)|((uint32_t)code<<8)|(c-currentOffset)|0x80; + length=4; + goto outputBytes; + } else { + /* don't know how to compress this character, just write it directly */ + c=((uint32_t)lead<<16)|trail; + length=4; + goto outputBytes; + } + } else /* 0xe000<=c<0xf300 */ { + /* quote to avoid SCSU tags */ + c|=UQU<<16; + length=3; + goto outputBytes; + } + + /* normal end of conversion: prepare for a new character */ + c=0; + } + } +endloop: + + /* set the converter state back into UConverter */ + scsu->fromUIsSingleByteMode=isSingleByteMode; + scsu->fromUDynamicWindow=dynamicWindow; + + cnv->fromUChar32=c; + + /* write back the updated pointers */ + pArgs->source=source; + pArgs->target=(char *)target; + return; + +outputBytes: + /* write the output character bytes from c and length [code copied from ucnvmbcs.c] */ + /* from the first if in the loop we know that targetCapacity>0 */ + if(length<=targetCapacity) { + switch(length) { + /* each branch falls through to the next one */ + case 4: + *target++=(uint8_t)(c>>24); + U_FALLTHROUGH; + case 3: + *target++=(uint8_t)(c>>16); + U_FALLTHROUGH; + case 2: + *target++=(uint8_t)(c>>8); + U_FALLTHROUGH; + case 1: + *target++=(uint8_t)c; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + targetCapacity-=length; + + /* normal end of conversion: prepare for a new character */ + c=0; + goto loop; + } else { + uint8_t *p; + + /* + * We actually do this backwards here: + * In order to save an intermediate variable, we output + * first to the overflow buffer what does not fit into the + * regular target. + */ + /* we know that 0<=targetCapacitycharErrorBuffer; + switch(length) { + /* each branch falls through to the next one */ + case 4: + *p++=(uint8_t)(c>>24); + U_FALLTHROUGH; + case 3: + *p++=(uint8_t)(c>>16); + U_FALLTHROUGH; + case 2: + *p++=(uint8_t)(c>>8); + U_FALLTHROUGH; + case 1: + *p=(uint8_t)c; + U_FALLTHROUGH; + default: + /* will never occur */ + break; + } + cnv->charErrorBufferLength=(int8_t)length; + + /* now output what fits into the regular target */ + c = (length == 4) ? 0 : c >> 8*length; /* length was reduced by targetCapacity */ + switch(targetCapacity) { + /* each branch falls through to the next one */ + case 3: + *target++=(uint8_t)(c>>16); + U_FALLTHROUGH; + case 2: + *target++=(uint8_t)(c>>8); + U_FALLTHROUGH; + case 1: + *target++=(uint8_t)c; + U_FALLTHROUGH; + default: + break; + } + + /* target overflow */ + targetCapacity=0; + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + c=0; + goto endloop; + } +} + +/* miscellaneous ------------------------------------------------------------ */ + +static const char * U_CALLCONV +_SCSUGetName(const UConverter *cnv) { + SCSUData *scsu=(SCSUData *)cnv->extraInfo; + + switch(scsu->locale) { + case l_ja: + return "SCSU,locale=ja"; + default: + return "SCSU"; + } +} + +/* structure for SafeClone calculations */ +struct cloneSCSUStruct +{ + UConverter cnv; + SCSUData mydata; +}; + +static UConverter * U_CALLCONV +_SCSUSafeClone(const UConverter *cnv, + void *stackBuffer, + int32_t *pBufferSize, + UErrorCode *status) +{ + struct cloneSCSUStruct * localClone; + int32_t bufferSizeNeeded = sizeof(struct cloneSCSUStruct); + + if (U_FAILURE(*status)){ + return 0; + } + + if (*pBufferSize == 0){ /* 'preflighting' request - set needed size into *pBufferSize */ + *pBufferSize = bufferSizeNeeded; + return 0; + } + + localClone = (struct cloneSCSUStruct *)stackBuffer; + /* ucnv.c/ucnv_safeClone() copied the main UConverter already */ + + uprv_memcpy(&localClone->mydata, cnv->extraInfo, sizeof(SCSUData)); + localClone->cnv.extraInfo = &localClone->mydata; + localClone->cnv.isExtraLocal = true; + + return &localClone->cnv; +} +U_CDECL_END + +static const UConverterImpl _SCSUImpl={ + UCNV_SCSU, + + nullptr, + nullptr, + + _SCSUOpen, + _SCSUClose, + _SCSUReset, + + _SCSUToUnicode, + _SCSUToUnicodeWithOffsets, + _SCSUFromUnicode, + _SCSUFromUnicodeWithOffsets, + nullptr, + + nullptr, + _SCSUGetName, + nullptr, + _SCSUSafeClone, + ucnv_getCompleteUnicodeSet, + nullptr, + nullptr +}; + +static const UConverterStaticData _SCSUStaticData={ + sizeof(UConverterStaticData), + "SCSU", + 1212, /* CCSID for SCSU */ + UCNV_IBM, UCNV_SCSU, + 1, 3, /* one char16_t generates at least 1 byte and at most 3 bytes */ + /* + * The subchar here is ignored because _SCSUOpen() sets U+fffd as a Unicode + * substitution string. + */ + { 0x0e, 0xff, 0xfd, 0 }, 3, + false, false, + 0, + 0, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */ +}; + +const UConverterSharedData _SCSUData= + UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_SCSUStaticData, &_SCSUImpl); + +#endif diff --git a/deps/icu-small/source/common/ucnvsel.cpp b/deps/icu-small/source/common/ucnvsel.cpp index 15ee596a23c863..2280f53888b442 100644 --- a/deps/icu-small/source/common/ucnvsel.cpp +++ b/deps/icu-small/source/common/ucnvsel.cpp @@ -1,823 +1,823 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2008-2011, International Business Machines -* Corporation, Google and others. All Rights Reserved. -* -******************************************************************************* -*/ -// Author : eldawy@google.com (Mohamed Eldawy) -// ucnvsel.cpp -// -// Purpose: To generate a list of encodings capable of handling -// a given Unicode text -// -// Started 09-April-2008 - -/** - * \file - * - * This is an implementation of an encoding selector. - * The goal is, given a unicode string, find the encodings - * this string can be mapped to. To make processing faster - * a trie is built when you call ucnvsel_open() that - * stores all encodings a codepoint can map to - */ - -#include "unicode/ucnvsel.h" - -#if !UCONFIG_NO_CONVERSION - -#include - -#include "unicode/uchar.h" -#include "unicode/uniset.h" -#include "unicode/ucnv.h" -#include "unicode/ustring.h" -#include "unicode/uchriter.h" -#include "utrie2.h" -#include "propsvec.h" -#include "uassert.h" -#include "ucmndata.h" -#include "udataswp.h" -#include "uenumimp.h" -#include "cmemory.h" -#include "cstring.h" - -U_NAMESPACE_USE - -struct UConverterSelector { - UTrie2 *trie; // 16 bit trie containing offsets into pv - uint32_t* pv; // table of bits! - int32_t pvCount; - char** encodings; // which encodings did user ask to use? - int32_t encodingsCount; - int32_t encodingStrLength; - uint8_t* swapped; - UBool ownPv, ownEncodingStrings; -}; - -static void generateSelectorData(UConverterSelector* result, - UPropsVectors *upvec, - const USet* excludedCodePoints, - const UConverterUnicodeSet whichSet, - UErrorCode* status) { - if (U_FAILURE(*status)) { - return; - } - - int32_t columns = (result->encodingsCount+31)/32; - - // set errorValue to all-ones - for (int32_t col = 0; col < columns; col++) { - upvec_setValue(upvec, UPVEC_ERROR_VALUE_CP, UPVEC_ERROR_VALUE_CP, - col, static_cast(~0), static_cast(~0), status); - } - - for (int32_t i = 0; i < result->encodingsCount; ++i) { - uint32_t mask; - uint32_t column; - int32_t item_count; - int32_t j; - UConverter* test_converter = ucnv_open(result->encodings[i], status); - if (U_FAILURE(*status)) { - return; - } - USet* unicode_point_set; - unicode_point_set = uset_open(1, 0); // empty set - - ucnv_getUnicodeSet(test_converter, unicode_point_set, - whichSet, status); - if (U_FAILURE(*status)) { - ucnv_close(test_converter); - return; - } - - column = i / 32; - mask = 1 << (i%32); - // now iterate over intervals on set i! - item_count = uset_getItemCount(unicode_point_set); - - for (j = 0; j < item_count; ++j) { - UChar32 start_char; - UChar32 end_char; - UErrorCode smallStatus = U_ZERO_ERROR; - uset_getItem(unicode_point_set, j, &start_char, &end_char, NULL, 0, - &smallStatus); - if (U_FAILURE(smallStatus)) { - // this will be reached for the converters that fill the set with - // strings. Those should be ignored by our system - } else { - upvec_setValue(upvec, start_char, end_char, column, static_cast(~0), mask, - status); - } - } - ucnv_close(test_converter); - uset_close(unicode_point_set); - if (U_FAILURE(*status)) { - return; - } - } - - // handle excluded encodings! Simply set their values to all 1's in the upvec - if (excludedCodePoints) { - int32_t item_count = uset_getItemCount(excludedCodePoints); - for (int32_t j = 0; j < item_count; ++j) { - UChar32 start_char; - UChar32 end_char; - - uset_getItem(excludedCodePoints, j, &start_char, &end_char, NULL, 0, - status); - for (int32_t col = 0; col < columns; col++) { - upvec_setValue(upvec, start_char, end_char, col, static_cast(~0), static_cast(~0), - status); - } - } - } - - // alright. Now, let's put things in the same exact form you'd get when you - // unserialize things. - result->trie = upvec_compactToUTrie2WithRowIndexes(upvec, status); - result->pv = upvec_cloneArray(upvec, &result->pvCount, NULL, status); - result->pvCount *= columns; // number of uint32_t = rows * columns - result->ownPv = true; -} - -/* open a selector. If converterListSize is 0, build for all converters. - If excludedCodePoints is NULL, don't exclude any codepoints */ -U_CAPI UConverterSelector* U_EXPORT2 -ucnvsel_open(const char* const* converterList, int32_t converterListSize, - const USet* excludedCodePoints, - const UConverterUnicodeSet whichSet, UErrorCode* status) { - // check if already failed - if (U_FAILURE(*status)) { - return NULL; - } - // ensure args make sense! - if (converterListSize < 0 || (converterList == NULL && converterListSize != 0)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - // allocate a new converter - LocalUConverterSelectorPointer newSelector( - (UConverterSelector*)uprv_malloc(sizeof(UConverterSelector))); - if (newSelector.isNull()) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uprv_memset(newSelector.getAlias(), 0, sizeof(UConverterSelector)); - - if (converterListSize == 0) { - converterList = NULL; - converterListSize = ucnv_countAvailable(); - } - newSelector->encodings = - (char**)uprv_malloc(converterListSize * sizeof(char*)); - if (!newSelector->encodings) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - newSelector->encodings[0] = NULL; // now we can call ucnvsel_close() - - // make a backup copy of the list of converters - int32_t totalSize = 0; - int32_t i; - for (i = 0; i < converterListSize; i++) { - totalSize += - (int32_t)uprv_strlen(converterList != NULL ? converterList[i] : ucnv_getAvailableName(i)) + 1; - } - // 4-align the totalSize to 4-align the size of the serialized form - int32_t encodingStrPadding = totalSize & 3; - if (encodingStrPadding != 0) { - encodingStrPadding = 4 - encodingStrPadding; - } - newSelector->encodingStrLength = totalSize += encodingStrPadding; - char* allStrings = (char*) uprv_malloc(totalSize); - if (!allStrings) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - for (i = 0; i < converterListSize; i++) { - newSelector->encodings[i] = allStrings; - uprv_strcpy(newSelector->encodings[i], - converterList != NULL ? converterList[i] : ucnv_getAvailableName(i)); - allStrings += uprv_strlen(newSelector->encodings[i]) + 1; - } - while (encodingStrPadding > 0) { - *allStrings++ = 0; - --encodingStrPadding; - } - - newSelector->ownEncodingStrings = true; - newSelector->encodingsCount = converterListSize; - UPropsVectors *upvec = upvec_open((converterListSize+31)/32, status); - generateSelectorData(newSelector.getAlias(), upvec, excludedCodePoints, whichSet, status); - upvec_close(upvec); - - if (U_FAILURE(*status)) { - return NULL; - } - - return newSelector.orphan(); -} - -/* close opened selector */ -U_CAPI void U_EXPORT2 -ucnvsel_close(UConverterSelector *sel) { - if (!sel) { - return; - } - if (sel->ownEncodingStrings) { - uprv_free(sel->encodings[0]); - } - uprv_free(sel->encodings); - if (sel->ownPv) { - uprv_free(sel->pv); - } - utrie2_close(sel->trie); - uprv_free(sel->swapped); - uprv_free(sel); -} - -static const UDataInfo dataInfo = { - sizeof(UDataInfo), - 0, - - U_IS_BIG_ENDIAN, - U_CHARSET_FAMILY, - U_SIZEOF_UCHAR, - 0, - - { 0x43, 0x53, 0x65, 0x6c }, /* dataFormat="CSel" */ - { 1, 0, 0, 0 }, /* formatVersion */ - { 0, 0, 0, 0 } /* dataVersion */ -}; - -enum { - UCNVSEL_INDEX_TRIE_SIZE, // trie size in bytes - UCNVSEL_INDEX_PV_COUNT, // number of uint32_t in the bit vectors - UCNVSEL_INDEX_NAMES_COUNT, // number of encoding names - UCNVSEL_INDEX_NAMES_LENGTH, // number of encoding name bytes including padding - UCNVSEL_INDEX_SIZE = 15, // bytes following the DataHeader - UCNVSEL_INDEX_COUNT = 16 -}; - -/* - * Serialized form of a UConverterSelector, formatVersion 1: - * - * The serialized form begins with a standard ICU DataHeader with a UDataInfo - * as the template above. - * This is followed by: - * int32_t indexes[UCNVSEL_INDEX_COUNT]; // see index entry constants above - * serialized UTrie2; // indexes[UCNVSEL_INDEX_TRIE_SIZE] bytes - * uint32_t pv[indexes[UCNVSEL_INDEX_PV_COUNT]]; // bit vectors - * char* encodingNames[indexes[UCNVSEL_INDEX_NAMES_LENGTH]]; // NUL-terminated strings + padding - */ - -/* serialize a selector */ -U_CAPI int32_t U_EXPORT2 -ucnvsel_serialize(const UConverterSelector* sel, - void* buffer, int32_t bufferCapacity, UErrorCode* status) { - // check if already failed - if (U_FAILURE(*status)) { - return 0; - } - // ensure args make sense! - uint8_t *p = (uint8_t *)buffer; - if (bufferCapacity < 0 || - (bufferCapacity > 0 && (p == NULL || (U_POINTER_MASK_LSB(p, 3) != 0))) - ) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - // add up the size of the serialized form - int32_t serializedTrieSize = utrie2_serialize(sel->trie, NULL, 0, status); - if (*status != U_BUFFER_OVERFLOW_ERROR && U_FAILURE(*status)) { - return 0; - } - *status = U_ZERO_ERROR; - - DataHeader header; - uprv_memset(&header, 0, sizeof(header)); - header.dataHeader.headerSize = (uint16_t)((sizeof(header) + 15) & ~15); - header.dataHeader.magic1 = 0xda; - header.dataHeader.magic2 = 0x27; - uprv_memcpy(&header.info, &dataInfo, sizeof(dataInfo)); - - int32_t indexes[UCNVSEL_INDEX_COUNT] = { - serializedTrieSize, - sel->pvCount, - sel->encodingsCount, - sel->encodingStrLength - }; - - int32_t totalSize = - header.dataHeader.headerSize + - (int32_t)sizeof(indexes) + - serializedTrieSize + - sel->pvCount * 4 + - sel->encodingStrLength; - indexes[UCNVSEL_INDEX_SIZE] = totalSize - header.dataHeader.headerSize; - if (totalSize > bufferCapacity) { - *status = U_BUFFER_OVERFLOW_ERROR; - return totalSize; - } - // ok, save! - int32_t length = header.dataHeader.headerSize; - uprv_memcpy(p, &header, sizeof(header)); - uprv_memset(p + sizeof(header), 0, length - sizeof(header)); - p += length; - - length = (int32_t)sizeof(indexes); - uprv_memcpy(p, indexes, length); - p += length; - - utrie2_serialize(sel->trie, p, serializedTrieSize, status); - p += serializedTrieSize; - - length = sel->pvCount * 4; - uprv_memcpy(p, sel->pv, length); - p += length; - - uprv_memcpy(p, sel->encodings[0], sel->encodingStrLength); - p += sel->encodingStrLength; - - return totalSize; -} - -/** - * swap a selector into the desired Endianness and Asciiness of - * the system. Just as FYI, selectors are always saved in the format - * of the system that created them. They are only converted if used - * on another system. In other words, selectors created on different - * system can be different even if the params are identical (endianness - * and Asciiness differences only) - * - * @param ds pointer to data swapper containing swapping info - * @param inData pointer to incoming data - * @param length length of inData in bytes - * @param outData pointer to output data. Capacity should - * be at least equal to capacity of inData - * @param status an in/out ICU UErrorCode - * @return 0 on failure, number of bytes swapped on success - * number of bytes swapped can be smaller than length - */ -static int32_t -ucnvsel_swap(const UDataSwapper *ds, - const void *inData, int32_t length, - void *outData, UErrorCode *status) { - /* udata_swapDataHeader checks the arguments */ - int32_t headerSize = udata_swapDataHeader(ds, inData, length, outData, status); - if(U_FAILURE(*status)) { - return 0; - } - - /* check data format and format version */ - const UDataInfo *pInfo = (const UDataInfo *)((const char *)inData + 4); - if(!( - pInfo->dataFormat[0] == 0x43 && /* dataFormat="CSel" */ - pInfo->dataFormat[1] == 0x53 && - pInfo->dataFormat[2] == 0x65 && - pInfo->dataFormat[3] == 0x6c - )) { - udata_printError(ds, "ucnvsel_swap(): data format %02x.%02x.%02x.%02x is not recognized as UConverterSelector data\n", - pInfo->dataFormat[0], pInfo->dataFormat[1], - pInfo->dataFormat[2], pInfo->dataFormat[3]); - *status = U_INVALID_FORMAT_ERROR; - return 0; - } - if(pInfo->formatVersion[0] != 1) { - udata_printError(ds, "ucnvsel_swap(): format version %02x is not supported\n", - pInfo->formatVersion[0]); - *status = U_UNSUPPORTED_ERROR; - return 0; - } - - if(length >= 0) { - length -= headerSize; - if(length < 16*4) { - udata_printError(ds, "ucnvsel_swap(): too few bytes (%d after header) for UConverterSelector data\n", - length); - *status = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - } - - const uint8_t *inBytes = (const uint8_t *)inData + headerSize; - uint8_t *outBytes = (uint8_t *)outData + headerSize; - - /* read the indexes */ - const int32_t *inIndexes = (const int32_t *)inBytes; - int32_t indexes[16]; - int32_t i; - for(i = 0; i < 16; ++i) { - indexes[i] = udata_readInt32(ds, inIndexes[i]); - } - - /* get the total length of the data */ - int32_t size = indexes[UCNVSEL_INDEX_SIZE]; - if(length >= 0) { - if(length < size) { - udata_printError(ds, "ucnvsel_swap(): too few bytes (%d after header) for all of UConverterSelector data\n", - length); - *status = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - /* copy the data for inaccessible bytes */ - if(inBytes != outBytes) { - uprv_memcpy(outBytes, inBytes, size); - } - - int32_t offset = 0, count; - - /* swap the int32_t indexes[] */ - count = UCNVSEL_INDEX_COUNT*4; - ds->swapArray32(ds, inBytes, count, outBytes, status); - offset += count; - - /* swap the UTrie2 */ - count = indexes[UCNVSEL_INDEX_TRIE_SIZE]; - utrie2_swap(ds, inBytes + offset, count, outBytes + offset, status); - offset += count; - - /* swap the uint32_t pv[] */ - count = indexes[UCNVSEL_INDEX_PV_COUNT]*4; - ds->swapArray32(ds, inBytes + offset, count, outBytes + offset, status); - offset += count; - - /* swap the encoding names */ - count = indexes[UCNVSEL_INDEX_NAMES_LENGTH]; - ds->swapInvChars(ds, inBytes + offset, count, outBytes + offset, status); - offset += count; - - U_ASSERT(offset == size); - } - - return headerSize + size; -} - -/* unserialize a selector */ -U_CAPI UConverterSelector* U_EXPORT2 -ucnvsel_openFromSerialized(const void* buffer, int32_t length, UErrorCode* status) { - // check if already failed - if (U_FAILURE(*status)) { - return NULL; - } - // ensure args make sense! - const uint8_t *p = (const uint8_t *)buffer; - if (length <= 0 || - (length > 0 && (p == NULL || (U_POINTER_MASK_LSB(p, 3) != 0))) - ) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - // header - if (length < 32) { - // not even enough space for a minimal header - *status = U_INDEX_OUTOFBOUNDS_ERROR; - return NULL; - } - const DataHeader *pHeader = (const DataHeader *)p; - if (!( - pHeader->dataHeader.magic1==0xda && - pHeader->dataHeader.magic2==0x27 && - pHeader->info.dataFormat[0] == 0x43 && - pHeader->info.dataFormat[1] == 0x53 && - pHeader->info.dataFormat[2] == 0x65 && - pHeader->info.dataFormat[3] == 0x6c - )) { - /* header not valid or dataFormat not recognized */ - *status = U_INVALID_FORMAT_ERROR; - return NULL; - } - if (pHeader->info.formatVersion[0] != 1) { - *status = U_UNSUPPORTED_ERROR; - return NULL; - } - uint8_t* swapped = NULL; - if (pHeader->info.isBigEndian != U_IS_BIG_ENDIAN || - pHeader->info.charsetFamily != U_CHARSET_FAMILY - ) { - // swap the data - UDataSwapper *ds = - udata_openSwapperForInputData(p, length, U_IS_BIG_ENDIAN, U_CHARSET_FAMILY, status); - int32_t totalSize = ucnvsel_swap(ds, p, -1, NULL, status); - if (U_FAILURE(*status)) { - udata_closeSwapper(ds); - return NULL; - } - if (length < totalSize) { - udata_closeSwapper(ds); - *status = U_INDEX_OUTOFBOUNDS_ERROR; - return NULL; - } - swapped = (uint8_t*)uprv_malloc(totalSize); - if (swapped == NULL) { - udata_closeSwapper(ds); - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - ucnvsel_swap(ds, p, length, swapped, status); - udata_closeSwapper(ds); - if (U_FAILURE(*status)) { - uprv_free(swapped); - return NULL; - } - p = swapped; - pHeader = (const DataHeader *)p; - } - if (length < (pHeader->dataHeader.headerSize + 16 * 4)) { - // not even enough space for the header and the indexes - uprv_free(swapped); - *status = U_INDEX_OUTOFBOUNDS_ERROR; - return NULL; - } - p += pHeader->dataHeader.headerSize; - length -= pHeader->dataHeader.headerSize; - // indexes - const int32_t *indexes = (const int32_t *)p; - if (length < indexes[UCNVSEL_INDEX_SIZE]) { - uprv_free(swapped); - *status = U_INDEX_OUTOFBOUNDS_ERROR; - return NULL; - } - p += UCNVSEL_INDEX_COUNT * 4; - // create and populate the selector object - UConverterSelector* sel = (UConverterSelector*)uprv_malloc(sizeof(UConverterSelector)); - char **encodings = - (char **)uprv_malloc( - indexes[UCNVSEL_INDEX_NAMES_COUNT] * sizeof(char *)); - if (sel == NULL || encodings == NULL) { - uprv_free(swapped); - uprv_free(sel); - uprv_free(encodings); - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uprv_memset(sel, 0, sizeof(UConverterSelector)); - sel->pvCount = indexes[UCNVSEL_INDEX_PV_COUNT]; - sel->encodings = encodings; - sel->encodingsCount = indexes[UCNVSEL_INDEX_NAMES_COUNT]; - sel->encodingStrLength = indexes[UCNVSEL_INDEX_NAMES_LENGTH]; - sel->swapped = swapped; - // trie - sel->trie = utrie2_openFromSerialized(UTRIE2_16_VALUE_BITS, - p, indexes[UCNVSEL_INDEX_TRIE_SIZE], NULL, - status); - p += indexes[UCNVSEL_INDEX_TRIE_SIZE]; - if (U_FAILURE(*status)) { - ucnvsel_close(sel); - return NULL; - } - // bit vectors - sel->pv = (uint32_t *)p; - p += sel->pvCount * 4; - // encoding names - char* s = (char*)p; - for (int32_t i = 0; i < sel->encodingsCount; ++i) { - sel->encodings[i] = s; - s += uprv_strlen(s) + 1; - } - p += sel->encodingStrLength; - - return sel; -} - -// a bunch of functions for the enumeration thingie! Nothing fancy here. Just -// iterate over the selected encodings -struct Enumerator { - int16_t* index; - int16_t length; - int16_t cur; - const UConverterSelector* sel; -}; - -U_CDECL_BEGIN - -static void U_CALLCONV -ucnvsel_close_selector_iterator(UEnumeration *enumerator) { - uprv_free(((Enumerator*)(enumerator->context))->index); - uprv_free(enumerator->context); - uprv_free(enumerator); -} - - -static int32_t U_CALLCONV -ucnvsel_count_encodings(UEnumeration *enumerator, UErrorCode *status) { - // check if already failed - if (U_FAILURE(*status)) { - return 0; - } - return ((Enumerator*)(enumerator->context))->length; -} - - -static const char* U_CALLCONV ucnvsel_next_encoding(UEnumeration* enumerator, - int32_t* resultLength, - UErrorCode* status) { - // check if already failed - if (U_FAILURE(*status)) { - return NULL; - } - - int16_t cur = ((Enumerator*)(enumerator->context))->cur; - const UConverterSelector* sel; - const char* result; - if (cur >= ((Enumerator*)(enumerator->context))->length) { - return NULL; - } - sel = ((Enumerator*)(enumerator->context))->sel; - result = sel->encodings[((Enumerator*)(enumerator->context))->index[cur] ]; - ((Enumerator*)(enumerator->context))->cur++; - if (resultLength) { - *resultLength = (int32_t)uprv_strlen(result); - } - return result; -} - -static void U_CALLCONV ucnvsel_reset_iterator(UEnumeration* enumerator, - UErrorCode* status) { - // check if already failed - if (U_FAILURE(*status)) { - return ; - } - ((Enumerator*)(enumerator->context))->cur = 0; -} - -U_CDECL_END - - -static const UEnumeration defaultEncodings = { - NULL, - NULL, - ucnvsel_close_selector_iterator, - ucnvsel_count_encodings, - uenum_unextDefault, - ucnvsel_next_encoding, - ucnvsel_reset_iterator -}; - - -// internal fn to intersect two sets of masks -// returns whether the mask has reduced to all zeros -static UBool intersectMasks(uint32_t* dest, const uint32_t* source1, int32_t len) { - int32_t i; - uint32_t oredDest = 0; - for (i = 0 ; i < len ; ++i) { - oredDest |= (dest[i] &= source1[i]); - } - return oredDest == 0; -} - -// internal fn to count how many 1's are there in a mask -// algorithm taken from http://graphics.stanford.edu/~seander/bithacks.html -static int16_t countOnes(uint32_t* mask, int32_t len) { - int32_t i, totalOnes = 0; - for (i = 0 ; i < len ; ++i) { - uint32_t ent = mask[i]; - for (; ent; totalOnes++) - { - ent &= ent - 1; // clear the least significant bit set - } - } - return static_cast(totalOnes); -} - - -/* internal function! */ -static UEnumeration *selectForMask(const UConverterSelector* sel, - uint32_t *theMask, UErrorCode *status) { - LocalMemory mask(theMask); - // this is the context we will use. Store a table of indices to which - // encodings are legit. - LocalMemory result(static_cast(uprv_malloc(sizeof(Enumerator)))); - if (result.isNull()) { - *status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - result->index = nullptr; // this will be allocated later! - result->length = result->cur = 0; - result->sel = sel; - - LocalMemory en(static_cast(uprv_malloc(sizeof(UEnumeration)))); - if (en.isNull()) { - // TODO(markus): Combine Enumerator and UEnumeration into one struct. - *status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - memcpy(en.getAlias(), &defaultEncodings, sizeof(UEnumeration)); - - int32_t columns = (sel->encodingsCount+31)/32; - int16_t numOnes = countOnes(mask.getAlias(), columns); - // now, we know the exact space we need for index - if (numOnes > 0) { - result->index = static_cast(uprv_malloc(numOnes * sizeof(int16_t))); - if (result->index == nullptr) { - *status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - int32_t i, j; - int16_t k = 0; - for (j = 0 ; j < columns; j++) { - uint32_t v = mask[j]; - for (i = 0 ; i < 32 && k < sel->encodingsCount; i++, k++) { - if ((v & 1) != 0) { - result->index[result->length++] = k; - } - v >>= 1; - } - } - } //otherwise, index will remain NULL (and will never be touched by - //the enumerator code anyway) - en->context = result.orphan(); - return en.orphan(); -} - -/* check a string against the selector - UTF16 version */ -U_CAPI UEnumeration * U_EXPORT2 -ucnvsel_selectForString(const UConverterSelector* sel, - const UChar *s, int32_t length, UErrorCode *status) { - // check if already failed - if (U_FAILURE(*status)) { - return NULL; - } - // ensure args make sense! - if (sel == NULL || (s == NULL && length != 0)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - int32_t columns = (sel->encodingsCount+31)/32; - uint32_t* mask = (uint32_t*) uprv_malloc(columns * 4); - if (mask == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uprv_memset(mask, ~0, columns *4); - - if(s!=NULL) { - const UChar *limit; - if (length >= 0) { - limit = s + length; - } else { - limit = NULL; - } - - while (limit == NULL ? *s != 0 : s != limit) { - UChar32 c; - uint16_t pvIndex; - UTRIE2_U16_NEXT16(sel->trie, s, limit, c, pvIndex); - if (intersectMasks(mask, sel->pv+pvIndex, columns)) { - break; - } - } - } - return selectForMask(sel, mask, status); -} - -/* check a string against the selector - UTF8 version */ -U_CAPI UEnumeration * U_EXPORT2 -ucnvsel_selectForUTF8(const UConverterSelector* sel, - const char *s, int32_t length, UErrorCode *status) { - // check if already failed - if (U_FAILURE(*status)) { - return NULL; - } - // ensure args make sense! - if (sel == NULL || (s == NULL && length != 0)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - int32_t columns = (sel->encodingsCount+31)/32; - uint32_t* mask = (uint32_t*) uprv_malloc(columns * 4); - if (mask == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uprv_memset(mask, ~0, columns *4); - - if (length < 0) { - length = (int32_t)uprv_strlen(s); - } - - if(s!=NULL) { - const char *limit = s + length; - - while (s != limit) { - uint16_t pvIndex; - UTRIE2_U8_NEXT16(sel->trie, s, limit, pvIndex); - if (intersectMasks(mask, sel->pv+pvIndex, columns)) { - break; - } - } - } - return selectForMask(sel, mask, status); -} - -#endif // !UCONFIG_NO_CONVERSION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2008-2011, International Business Machines +* Corporation, Google and others. All Rights Reserved. +* +******************************************************************************* +*/ +// Author : eldawy@google.com (Mohamed Eldawy) +// ucnvsel.cpp +// +// Purpose: To generate a list of encodings capable of handling +// a given Unicode text +// +// Started 09-April-2008 + +/** + * \file + * + * This is an implementation of an encoding selector. + * The goal is, given a unicode string, find the encodings + * this string can be mapped to. To make processing faster + * a trie is built when you call ucnvsel_open() that + * stores all encodings a codepoint can map to + */ + +#include "unicode/ucnvsel.h" + +#if !UCONFIG_NO_CONVERSION + +#include + +#include "unicode/uchar.h" +#include "unicode/uniset.h" +#include "unicode/ucnv.h" +#include "unicode/ustring.h" +#include "unicode/uchriter.h" +#include "utrie2.h" +#include "propsvec.h" +#include "uassert.h" +#include "ucmndata.h" +#include "udataswp.h" +#include "uenumimp.h" +#include "cmemory.h" +#include "cstring.h" + +U_NAMESPACE_USE + +struct UConverterSelector { + UTrie2 *trie; // 16 bit trie containing offsets into pv + uint32_t* pv; // table of bits! + int32_t pvCount; + char** encodings; // which encodings did user ask to use? + int32_t encodingsCount; + int32_t encodingStrLength; + uint8_t* swapped; + UBool ownPv, ownEncodingStrings; +}; + +static void generateSelectorData(UConverterSelector* result, + UPropsVectors *upvec, + const USet* excludedCodePoints, + const UConverterUnicodeSet whichSet, + UErrorCode* status) { + if (U_FAILURE(*status)) { + return; + } + + int32_t columns = (result->encodingsCount+31)/32; + + // set errorValue to all-ones + for (int32_t col = 0; col < columns; col++) { + upvec_setValue(upvec, UPVEC_ERROR_VALUE_CP, UPVEC_ERROR_VALUE_CP, + col, static_cast(~0), static_cast(~0), status); + } + + for (int32_t i = 0; i < result->encodingsCount; ++i) { + uint32_t mask; + uint32_t column; + int32_t item_count; + int32_t j; + UConverter* test_converter = ucnv_open(result->encodings[i], status); + if (U_FAILURE(*status)) { + return; + } + USet* unicode_point_set; + unicode_point_set = uset_open(1, 0); // empty set + + ucnv_getUnicodeSet(test_converter, unicode_point_set, + whichSet, status); + if (U_FAILURE(*status)) { + ucnv_close(test_converter); + return; + } + + column = i / 32; + mask = 1 << (i%32); + // now iterate over intervals on set i! + item_count = uset_getItemCount(unicode_point_set); + + for (j = 0; j < item_count; ++j) { + UChar32 start_char; + UChar32 end_char; + UErrorCode smallStatus = U_ZERO_ERROR; + uset_getItem(unicode_point_set, j, &start_char, &end_char, nullptr, 0, + &smallStatus); + if (U_FAILURE(smallStatus)) { + // this will be reached for the converters that fill the set with + // strings. Those should be ignored by our system + } else { + upvec_setValue(upvec, start_char, end_char, column, static_cast(~0), mask, + status); + } + } + ucnv_close(test_converter); + uset_close(unicode_point_set); + if (U_FAILURE(*status)) { + return; + } + } + + // handle excluded encodings! Simply set their values to all 1's in the upvec + if (excludedCodePoints) { + int32_t item_count = uset_getItemCount(excludedCodePoints); + for (int32_t j = 0; j < item_count; ++j) { + UChar32 start_char; + UChar32 end_char; + + uset_getItem(excludedCodePoints, j, &start_char, &end_char, nullptr, 0, + status); + for (int32_t col = 0; col < columns; col++) { + upvec_setValue(upvec, start_char, end_char, col, static_cast(~0), static_cast(~0), + status); + } + } + } + + // alright. Now, let's put things in the same exact form you'd get when you + // unserialize things. + result->trie = upvec_compactToUTrie2WithRowIndexes(upvec, status); + result->pv = upvec_cloneArray(upvec, &result->pvCount, nullptr, status); + result->pvCount *= columns; // number of uint32_t = rows * columns + result->ownPv = true; +} + +/* open a selector. If converterListSize is 0, build for all converters. + If excludedCodePoints is nullptr, don't exclude any codepoints */ +U_CAPI UConverterSelector* U_EXPORT2 +ucnvsel_open(const char* const* converterList, int32_t converterListSize, + const USet* excludedCodePoints, + const UConverterUnicodeSet whichSet, UErrorCode* status) { + // check if already failed + if (U_FAILURE(*status)) { + return nullptr; + } + // ensure args make sense! + if (converterListSize < 0 || (converterList == nullptr && converterListSize != 0)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + // allocate a new converter + LocalUConverterSelectorPointer newSelector( + (UConverterSelector*)uprv_malloc(sizeof(UConverterSelector))); + if (newSelector.isNull()) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memset(newSelector.getAlias(), 0, sizeof(UConverterSelector)); + + if (converterListSize == 0) { + converterList = nullptr; + converterListSize = ucnv_countAvailable(); + } + newSelector->encodings = + (char**)uprv_malloc(converterListSize * sizeof(char*)); + if (!newSelector->encodings) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + newSelector->encodings[0] = nullptr; // now we can call ucnvsel_close() + + // make a backup copy of the list of converters + int32_t totalSize = 0; + int32_t i; + for (i = 0; i < converterListSize; i++) { + totalSize += + (int32_t)uprv_strlen(converterList != nullptr ? converterList[i] : ucnv_getAvailableName(i)) + 1; + } + // 4-align the totalSize to 4-align the size of the serialized form + int32_t encodingStrPadding = totalSize & 3; + if (encodingStrPadding != 0) { + encodingStrPadding = 4 - encodingStrPadding; + } + newSelector->encodingStrLength = totalSize += encodingStrPadding; + char* allStrings = (char*) uprv_malloc(totalSize); + if (!allStrings) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + for (i = 0; i < converterListSize; i++) { + newSelector->encodings[i] = allStrings; + uprv_strcpy(newSelector->encodings[i], + converterList != nullptr ? converterList[i] : ucnv_getAvailableName(i)); + allStrings += uprv_strlen(newSelector->encodings[i]) + 1; + } + while (encodingStrPadding > 0) { + *allStrings++ = 0; + --encodingStrPadding; + } + + newSelector->ownEncodingStrings = true; + newSelector->encodingsCount = converterListSize; + UPropsVectors *upvec = upvec_open((converterListSize+31)/32, status); + generateSelectorData(newSelector.getAlias(), upvec, excludedCodePoints, whichSet, status); + upvec_close(upvec); + + if (U_FAILURE(*status)) { + return nullptr; + } + + return newSelector.orphan(); +} + +/* close opened selector */ +U_CAPI void U_EXPORT2 +ucnvsel_close(UConverterSelector *sel) { + if (!sel) { + return; + } + if (sel->ownEncodingStrings) { + uprv_free(sel->encodings[0]); + } + uprv_free(sel->encodings); + if (sel->ownPv) { + uprv_free(sel->pv); + } + utrie2_close(sel->trie); + uprv_free(sel->swapped); + uprv_free(sel); +} + +static const UDataInfo dataInfo = { + sizeof(UDataInfo), + 0, + + U_IS_BIG_ENDIAN, + U_CHARSET_FAMILY, + U_SIZEOF_UCHAR, + 0, + + { 0x43, 0x53, 0x65, 0x6c }, /* dataFormat="CSel" */ + { 1, 0, 0, 0 }, /* formatVersion */ + { 0, 0, 0, 0 } /* dataVersion */ +}; + +enum { + UCNVSEL_INDEX_TRIE_SIZE, // trie size in bytes + UCNVSEL_INDEX_PV_COUNT, // number of uint32_t in the bit vectors + UCNVSEL_INDEX_NAMES_COUNT, // number of encoding names + UCNVSEL_INDEX_NAMES_LENGTH, // number of encoding name bytes including padding + UCNVSEL_INDEX_SIZE = 15, // bytes following the DataHeader + UCNVSEL_INDEX_COUNT = 16 +}; + +/* + * Serialized form of a UConverterSelector, formatVersion 1: + * + * The serialized form begins with a standard ICU DataHeader with a UDataInfo + * as the template above. + * This is followed by: + * int32_t indexes[UCNVSEL_INDEX_COUNT]; // see index entry constants above + * serialized UTrie2; // indexes[UCNVSEL_INDEX_TRIE_SIZE] bytes + * uint32_t pv[indexes[UCNVSEL_INDEX_PV_COUNT]]; // bit vectors + * char* encodingNames[indexes[UCNVSEL_INDEX_NAMES_LENGTH]]; // NUL-terminated strings + padding + */ + +/* serialize a selector */ +U_CAPI int32_t U_EXPORT2 +ucnvsel_serialize(const UConverterSelector* sel, + void* buffer, int32_t bufferCapacity, UErrorCode* status) { + // check if already failed + if (U_FAILURE(*status)) { + return 0; + } + // ensure args make sense! + uint8_t *p = (uint8_t *)buffer; + if (bufferCapacity < 0 || + (bufferCapacity > 0 && (p == nullptr || (U_POINTER_MASK_LSB(p, 3) != 0))) + ) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + // add up the size of the serialized form + int32_t serializedTrieSize = utrie2_serialize(sel->trie, nullptr, 0, status); + if (*status != U_BUFFER_OVERFLOW_ERROR && U_FAILURE(*status)) { + return 0; + } + *status = U_ZERO_ERROR; + + DataHeader header; + uprv_memset(&header, 0, sizeof(header)); + header.dataHeader.headerSize = (uint16_t)((sizeof(header) + 15) & ~15); + header.dataHeader.magic1 = 0xda; + header.dataHeader.magic2 = 0x27; + uprv_memcpy(&header.info, &dataInfo, sizeof(dataInfo)); + + int32_t indexes[UCNVSEL_INDEX_COUNT] = { + serializedTrieSize, + sel->pvCount, + sel->encodingsCount, + sel->encodingStrLength + }; + + int32_t totalSize = + header.dataHeader.headerSize + + (int32_t)sizeof(indexes) + + serializedTrieSize + + sel->pvCount * 4 + + sel->encodingStrLength; + indexes[UCNVSEL_INDEX_SIZE] = totalSize - header.dataHeader.headerSize; + if (totalSize > bufferCapacity) { + *status = U_BUFFER_OVERFLOW_ERROR; + return totalSize; + } + // ok, save! + int32_t length = header.dataHeader.headerSize; + uprv_memcpy(p, &header, sizeof(header)); + uprv_memset(p + sizeof(header), 0, length - sizeof(header)); + p += length; + + length = (int32_t)sizeof(indexes); + uprv_memcpy(p, indexes, length); + p += length; + + utrie2_serialize(sel->trie, p, serializedTrieSize, status); + p += serializedTrieSize; + + length = sel->pvCount * 4; + uprv_memcpy(p, sel->pv, length); + p += length; + + uprv_memcpy(p, sel->encodings[0], sel->encodingStrLength); + p += sel->encodingStrLength; + + return totalSize; +} + +/** + * swap a selector into the desired Endianness and Asciiness of + * the system. Just as FYI, selectors are always saved in the format + * of the system that created them. They are only converted if used + * on another system. In other words, selectors created on different + * system can be different even if the params are identical (endianness + * and Asciiness differences only) + * + * @param ds pointer to data swapper containing swapping info + * @param inData pointer to incoming data + * @param length length of inData in bytes + * @param outData pointer to output data. Capacity should + * be at least equal to capacity of inData + * @param status an in/out ICU UErrorCode + * @return 0 on failure, number of bytes swapped on success + * number of bytes swapped can be smaller than length + */ +static int32_t +ucnvsel_swap(const UDataSwapper *ds, + const void *inData, int32_t length, + void *outData, UErrorCode *status) { + /* udata_swapDataHeader checks the arguments */ + int32_t headerSize = udata_swapDataHeader(ds, inData, length, outData, status); + if(U_FAILURE(*status)) { + return 0; + } + + /* check data format and format version */ + const UDataInfo *pInfo = (const UDataInfo *)((const char *)inData + 4); + if(!( + pInfo->dataFormat[0] == 0x43 && /* dataFormat="CSel" */ + pInfo->dataFormat[1] == 0x53 && + pInfo->dataFormat[2] == 0x65 && + pInfo->dataFormat[3] == 0x6c + )) { + udata_printError(ds, "ucnvsel_swap(): data format %02x.%02x.%02x.%02x is not recognized as UConverterSelector data\n", + pInfo->dataFormat[0], pInfo->dataFormat[1], + pInfo->dataFormat[2], pInfo->dataFormat[3]); + *status = U_INVALID_FORMAT_ERROR; + return 0; + } + if(pInfo->formatVersion[0] != 1) { + udata_printError(ds, "ucnvsel_swap(): format version %02x is not supported\n", + pInfo->formatVersion[0]); + *status = U_UNSUPPORTED_ERROR; + return 0; + } + + if(length >= 0) { + length -= headerSize; + if(length < 16*4) { + udata_printError(ds, "ucnvsel_swap(): too few bytes (%d after header) for UConverterSelector data\n", + length); + *status = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + } + + const uint8_t *inBytes = (const uint8_t *)inData + headerSize; + uint8_t *outBytes = (uint8_t *)outData + headerSize; + + /* read the indexes */ + const int32_t *inIndexes = (const int32_t *)inBytes; + int32_t indexes[16]; + int32_t i; + for(i = 0; i < 16; ++i) { + indexes[i] = udata_readInt32(ds, inIndexes[i]); + } + + /* get the total length of the data */ + int32_t size = indexes[UCNVSEL_INDEX_SIZE]; + if(length >= 0) { + if(length < size) { + udata_printError(ds, "ucnvsel_swap(): too few bytes (%d after header) for all of UConverterSelector data\n", + length); + *status = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + /* copy the data for inaccessible bytes */ + if(inBytes != outBytes) { + uprv_memcpy(outBytes, inBytes, size); + } + + int32_t offset = 0, count; + + /* swap the int32_t indexes[] */ + count = UCNVSEL_INDEX_COUNT*4; + ds->swapArray32(ds, inBytes, count, outBytes, status); + offset += count; + + /* swap the UTrie2 */ + count = indexes[UCNVSEL_INDEX_TRIE_SIZE]; + utrie2_swap(ds, inBytes + offset, count, outBytes + offset, status); + offset += count; + + /* swap the uint32_t pv[] */ + count = indexes[UCNVSEL_INDEX_PV_COUNT]*4; + ds->swapArray32(ds, inBytes + offset, count, outBytes + offset, status); + offset += count; + + /* swap the encoding names */ + count = indexes[UCNVSEL_INDEX_NAMES_LENGTH]; + ds->swapInvChars(ds, inBytes + offset, count, outBytes + offset, status); + offset += count; + + U_ASSERT(offset == size); + } + + return headerSize + size; +} + +/* unserialize a selector */ +U_CAPI UConverterSelector* U_EXPORT2 +ucnvsel_openFromSerialized(const void* buffer, int32_t length, UErrorCode* status) { + // check if already failed + if (U_FAILURE(*status)) { + return nullptr; + } + // ensure args make sense! + const uint8_t *p = (const uint8_t *)buffer; + if (length <= 0 || + (length > 0 && (p == nullptr || (U_POINTER_MASK_LSB(p, 3) != 0))) + ) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + // header + if (length < 32) { + // not even enough space for a minimal header + *status = U_INDEX_OUTOFBOUNDS_ERROR; + return nullptr; + } + const DataHeader *pHeader = (const DataHeader *)p; + if (!( + pHeader->dataHeader.magic1==0xda && + pHeader->dataHeader.magic2==0x27 && + pHeader->info.dataFormat[0] == 0x43 && + pHeader->info.dataFormat[1] == 0x53 && + pHeader->info.dataFormat[2] == 0x65 && + pHeader->info.dataFormat[3] == 0x6c + )) { + /* header not valid or dataFormat not recognized */ + *status = U_INVALID_FORMAT_ERROR; + return nullptr; + } + if (pHeader->info.formatVersion[0] != 1) { + *status = U_UNSUPPORTED_ERROR; + return nullptr; + } + uint8_t* swapped = nullptr; + if (pHeader->info.isBigEndian != U_IS_BIG_ENDIAN || + pHeader->info.charsetFamily != U_CHARSET_FAMILY + ) { + // swap the data + UDataSwapper *ds = + udata_openSwapperForInputData(p, length, U_IS_BIG_ENDIAN, U_CHARSET_FAMILY, status); + int32_t totalSize = ucnvsel_swap(ds, p, -1, nullptr, status); + if (U_FAILURE(*status)) { + udata_closeSwapper(ds); + return nullptr; + } + if (length < totalSize) { + udata_closeSwapper(ds); + *status = U_INDEX_OUTOFBOUNDS_ERROR; + return nullptr; + } + swapped = (uint8_t*)uprv_malloc(totalSize); + if (swapped == nullptr) { + udata_closeSwapper(ds); + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + ucnvsel_swap(ds, p, length, swapped, status); + udata_closeSwapper(ds); + if (U_FAILURE(*status)) { + uprv_free(swapped); + return nullptr; + } + p = swapped; + pHeader = (const DataHeader *)p; + } + if (length < (pHeader->dataHeader.headerSize + 16 * 4)) { + // not even enough space for the header and the indexes + uprv_free(swapped); + *status = U_INDEX_OUTOFBOUNDS_ERROR; + return nullptr; + } + p += pHeader->dataHeader.headerSize; + length -= pHeader->dataHeader.headerSize; + // indexes + const int32_t *indexes = (const int32_t *)p; + if (length < indexes[UCNVSEL_INDEX_SIZE]) { + uprv_free(swapped); + *status = U_INDEX_OUTOFBOUNDS_ERROR; + return nullptr; + } + p += UCNVSEL_INDEX_COUNT * 4; + // create and populate the selector object + UConverterSelector* sel = (UConverterSelector*)uprv_malloc(sizeof(UConverterSelector)); + char **encodings = + (char **)uprv_malloc( + indexes[UCNVSEL_INDEX_NAMES_COUNT] * sizeof(char *)); + if (sel == nullptr || encodings == nullptr) { + uprv_free(swapped); + uprv_free(sel); + uprv_free(encodings); + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memset(sel, 0, sizeof(UConverterSelector)); + sel->pvCount = indexes[UCNVSEL_INDEX_PV_COUNT]; + sel->encodings = encodings; + sel->encodingsCount = indexes[UCNVSEL_INDEX_NAMES_COUNT]; + sel->encodingStrLength = indexes[UCNVSEL_INDEX_NAMES_LENGTH]; + sel->swapped = swapped; + // trie + sel->trie = utrie2_openFromSerialized(UTRIE2_16_VALUE_BITS, + p, indexes[UCNVSEL_INDEX_TRIE_SIZE], nullptr, + status); + p += indexes[UCNVSEL_INDEX_TRIE_SIZE]; + if (U_FAILURE(*status)) { + ucnvsel_close(sel); + return nullptr; + } + // bit vectors + sel->pv = (uint32_t *)p; + p += sel->pvCount * 4; + // encoding names + char* s = (char*)p; + for (int32_t i = 0; i < sel->encodingsCount; ++i) { + sel->encodings[i] = s; + s += uprv_strlen(s) + 1; + } + p += sel->encodingStrLength; + + return sel; +} + +// a bunch of functions for the enumeration thingie! Nothing fancy here. Just +// iterate over the selected encodings +struct Enumerator { + int16_t* index; + int16_t length; + int16_t cur; + const UConverterSelector* sel; +}; + +U_CDECL_BEGIN + +static void U_CALLCONV +ucnvsel_close_selector_iterator(UEnumeration *enumerator) { + uprv_free(((Enumerator*)(enumerator->context))->index); + uprv_free(enumerator->context); + uprv_free(enumerator); +} + + +static int32_t U_CALLCONV +ucnvsel_count_encodings(UEnumeration *enumerator, UErrorCode *status) { + // check if already failed + if (U_FAILURE(*status)) { + return 0; + } + return ((Enumerator*)(enumerator->context))->length; +} + + +static const char* U_CALLCONV ucnvsel_next_encoding(UEnumeration* enumerator, + int32_t* resultLength, + UErrorCode* status) { + // check if already failed + if (U_FAILURE(*status)) { + return nullptr; + } + + int16_t cur = ((Enumerator*)(enumerator->context))->cur; + const UConverterSelector* sel; + const char* result; + if (cur >= ((Enumerator*)(enumerator->context))->length) { + return nullptr; + } + sel = ((Enumerator*)(enumerator->context))->sel; + result = sel->encodings[((Enumerator*)(enumerator->context))->index[cur] ]; + ((Enumerator*)(enumerator->context))->cur++; + if (resultLength) { + *resultLength = (int32_t)uprv_strlen(result); + } + return result; +} + +static void U_CALLCONV ucnvsel_reset_iterator(UEnumeration* enumerator, + UErrorCode* status) { + // check if already failed + if (U_FAILURE(*status)) { + return ; + } + ((Enumerator*)(enumerator->context))->cur = 0; +} + +U_CDECL_END + + +static const UEnumeration defaultEncodings = { + nullptr, + nullptr, + ucnvsel_close_selector_iterator, + ucnvsel_count_encodings, + uenum_unextDefault, + ucnvsel_next_encoding, + ucnvsel_reset_iterator +}; + + +// internal fn to intersect two sets of masks +// returns whether the mask has reduced to all zeros +static UBool intersectMasks(uint32_t* dest, const uint32_t* source1, int32_t len) { + int32_t i; + uint32_t oredDest = 0; + for (i = 0 ; i < len ; ++i) { + oredDest |= (dest[i] &= source1[i]); + } + return oredDest == 0; +} + +// internal fn to count how many 1's are there in a mask +// algorithm taken from http://graphics.stanford.edu/~seander/bithacks.html +static int16_t countOnes(uint32_t* mask, int32_t len) { + int32_t i, totalOnes = 0; + for (i = 0 ; i < len ; ++i) { + uint32_t ent = mask[i]; + for (; ent; totalOnes++) + { + ent &= ent - 1; // clear the least significant bit set + } + } + return static_cast(totalOnes); +} + + +/* internal function! */ +static UEnumeration *selectForMask(const UConverterSelector* sel, + uint32_t *theMask, UErrorCode *status) { + LocalMemory mask(theMask); + // this is the context we will use. Store a table of indices to which + // encodings are legit. + LocalMemory result(static_cast(uprv_malloc(sizeof(Enumerator)))); + if (result.isNull()) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + result->index = nullptr; // this will be allocated later! + result->length = result->cur = 0; + result->sel = sel; + + LocalMemory en(static_cast(uprv_malloc(sizeof(UEnumeration)))); + if (en.isNull()) { + // TODO(markus): Combine Enumerator and UEnumeration into one struct. + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + memcpy(en.getAlias(), &defaultEncodings, sizeof(UEnumeration)); + + int32_t columns = (sel->encodingsCount+31)/32; + int16_t numOnes = countOnes(mask.getAlias(), columns); + // now, we know the exact space we need for index + if (numOnes > 0) { + result->index = static_cast(uprv_malloc(numOnes * sizeof(int16_t))); + if (result->index == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + int32_t i, j; + int16_t k = 0; + for (j = 0 ; j < columns; j++) { + uint32_t v = mask[j]; + for (i = 0 ; i < 32 && k < sel->encodingsCount; i++, k++) { + if ((v & 1) != 0) { + result->index[result->length++] = k; + } + v >>= 1; + } + } + } //otherwise, index will remain nullptr (and will never be touched by + //the enumerator code anyway) + en->context = result.orphan(); + return en.orphan(); +} + +/* check a string against the selector - UTF16 version */ +U_CAPI UEnumeration * U_EXPORT2 +ucnvsel_selectForString(const UConverterSelector* sel, + const char16_t *s, int32_t length, UErrorCode *status) { + // check if already failed + if (U_FAILURE(*status)) { + return nullptr; + } + // ensure args make sense! + if (sel == nullptr || (s == nullptr && length != 0)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + int32_t columns = (sel->encodingsCount+31)/32; + uint32_t* mask = (uint32_t*) uprv_malloc(columns * 4); + if (mask == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memset(mask, ~0, columns *4); + + if(s!=nullptr) { + const char16_t *limit; + if (length >= 0) { + limit = s + length; + } else { + limit = nullptr; + } + + while (limit == nullptr ? *s != 0 : s != limit) { + UChar32 c; + uint16_t pvIndex; + UTRIE2_U16_NEXT16(sel->trie, s, limit, c, pvIndex); + if (intersectMasks(mask, sel->pv+pvIndex, columns)) { + break; + } + } + } + return selectForMask(sel, mask, status); +} + +/* check a string against the selector - UTF8 version */ +U_CAPI UEnumeration * U_EXPORT2 +ucnvsel_selectForUTF8(const UConverterSelector* sel, + const char *s, int32_t length, UErrorCode *status) { + // check if already failed + if (U_FAILURE(*status)) { + return nullptr; + } + // ensure args make sense! + if (sel == nullptr || (s == nullptr && length != 0)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + int32_t columns = (sel->encodingsCount+31)/32; + uint32_t* mask = (uint32_t*) uprv_malloc(columns * 4); + if (mask == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memset(mask, ~0, columns *4); + + if (length < 0) { + length = (int32_t)uprv_strlen(s); + } + + if(s!=nullptr) { + const char *limit = s + length; + + while (s != limit) { + uint16_t pvIndex; + UTRIE2_U8_NEXT16(sel->trie, s, limit, pvIndex); + if (intersectMasks(mask, sel->pv+pvIndex, columns)) { + break; + } + } + } + return selectForMask(sel, mask, status); +} + +#endif // !UCONFIG_NO_CONVERSION diff --git a/deps/icu-small/source/common/ucol_data.h b/deps/icu-small/source/common/ucol_data.h index 83f54abba133ab..822a82127a0384 100644 --- a/deps/icu-small/source/common/ucol_data.h +++ b/deps/icu-small/source/common/ucol_data.h @@ -1,89 +1,89 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2000-2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: ucol_data.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2011jul02 -* created by: Markus Scherer -* -* Private implementation header for C/C++ collation. -* Some file data structure definitions were moved here from i18n/ucol_imp.h -* so that the common library (via ucol_swp.cpp) need not depend on the i18n library at all. -* -* We do not want to move the collation swapper to the i18n library because -* a) the resource bundle swapper depends on it and would have to move too, and -* b) we might want to eventually implement runtime data swapping, -* which might (or might not) be easier if all swappers are in the common library. -*/ - -#ifndef __UCOL_DATA_H__ -#define __UCOL_DATA_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -/* let us know whether reserved fields are reset to zero or junked */ -#define UCOL_HEADER_MAGIC 0x20030618 - -typedef struct { - int32_t size; - /* all the offsets are in bytes */ - /* to get the address add to the header address and cast properly */ - uint32_t options; /* these are the default options for the collator */ - uint32_t UCAConsts; /* structure which holds values for indirect positioning and implicit ranges */ - uint32_t contractionUCACombos; /* this one is needed only for UCA, to copy the appropriate contractions */ - uint32_t magic; /* magic number - lets us know whether reserved data is reset or junked */ - uint32_t mappingPosition; /* const uint8_t *mappingPosition; */ - uint32_t expansion; /* uint32_t *expansion; */ - uint32_t contractionIndex; /* UChar *contractionIndex; */ - uint32_t contractionCEs; /* uint32_t *contractionCEs; */ - uint32_t contractionSize; /* needed for various closures */ - /*int32_t latinOneMapping;*/ /* this is now handled in the trie itself *//* fast track to latin1 chars */ - - uint32_t endExpansionCE; /* array of last collation element in - expansion */ - uint32_t expansionCESize; /* array of maximum expansion size - corresponding to the expansion - collation elements with last element - in endExpansionCE*/ - int32_t endExpansionCECount; /* size of endExpansionCE */ - uint32_t unsafeCP; /* hash table of unsafe code points */ - uint32_t contrEndCP; /* hash table of final code points */ - /* in contractions. */ - - int32_t contractionUCACombosSize; /* number of UCA contraction items. */ - /*Length is contractionUCACombosSize*contractionUCACombosWidth*sizeof(UChar) */ - UBool jamoSpecial; /* is jamoSpecial */ - UBool isBigEndian; /* is this data big endian? from the UDataInfo header*/ - uint8_t charSetFamily; /* what is the charset family of this data from the UDataInfo header*/ - uint8_t contractionUCACombosWidth; /* width of UCA combos field */ - UVersionInfo version; - UVersionInfo UCAVersion; /* version of the UCA, read from file */ - UVersionInfo UCDVersion; /* UCD version, obtained by u_getUnicodeVersion */ - UVersionInfo formatVersion; /* format version from the UDataInfo header */ - uint32_t scriptToLeadByte; /* offset to script to lead collation byte mapping data */ - uint32_t leadByteToScript; /* offset to lead collation byte to script mapping data */ - uint8_t reserved[76]; /* for future use */ -} UCATableHeader; - -typedef struct { - uint32_t byteSize; - uint32_t tableSize; - uint32_t contsSize; - uint32_t table; - uint32_t conts; - UVersionInfo UCAVersion; /* version of the UCA, read from file */ - uint8_t padding[8]; -} InverseUCATableHeader; - -#endif /* !UCONFIG_NO_COLLATION */ - -#endif /* __UCOL_DATA_H__ */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2000-2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: ucol_data.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2011jul02 +* created by: Markus Scherer +* +* Private implementation header for C/C++ collation. +* Some file data structure definitions were moved here from i18n/ucol_imp.h +* so that the common library (via ucol_swp.cpp) need not depend on the i18n library at all. +* +* We do not want to move the collation swapper to the i18n library because +* a) the resource bundle swapper depends on it and would have to move too, and +* b) we might want to eventually implement runtime data swapping, +* which might (or might not) be easier if all swappers are in the common library. +*/ + +#ifndef __UCOL_DATA_H__ +#define __UCOL_DATA_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +/* let us know whether reserved fields are reset to zero or junked */ +#define UCOL_HEADER_MAGIC 0x20030618 + +typedef struct { + int32_t size; + /* all the offsets are in bytes */ + /* to get the address add to the header address and cast properly */ + uint32_t options; /* these are the default options for the collator */ + uint32_t UCAConsts; /* structure which holds values for indirect positioning and implicit ranges */ + uint32_t contractionUCACombos; /* this one is needed only for UCA, to copy the appropriate contractions */ + uint32_t magic; /* magic number - lets us know whether reserved data is reset or junked */ + uint32_t mappingPosition; /* const uint8_t *mappingPosition; */ + uint32_t expansion; /* uint32_t *expansion; */ + uint32_t contractionIndex; /* char16_t *contractionIndex; */ + uint32_t contractionCEs; /* uint32_t *contractionCEs; */ + uint32_t contractionSize; /* needed for various closures */ + /*int32_t latinOneMapping;*/ /* this is now handled in the trie itself *//* fast track to latin1 chars */ + + uint32_t endExpansionCE; /* array of last collation element in + expansion */ + uint32_t expansionCESize; /* array of maximum expansion size + corresponding to the expansion + collation elements with last element + in endExpansionCE*/ + int32_t endExpansionCECount; /* size of endExpansionCE */ + uint32_t unsafeCP; /* hash table of unsafe code points */ + uint32_t contrEndCP; /* hash table of final code points */ + /* in contractions. */ + + int32_t contractionUCACombosSize; /* number of UCA contraction items. */ + /*Length is contractionUCACombosSize*contractionUCACombosWidth*sizeof(char16_t) */ + UBool jamoSpecial; /* is jamoSpecial */ + UBool isBigEndian; /* is this data big endian? from the UDataInfo header*/ + uint8_t charSetFamily; /* what is the charset family of this data from the UDataInfo header*/ + uint8_t contractionUCACombosWidth; /* width of UCA combos field */ + UVersionInfo version; + UVersionInfo UCAVersion; /* version of the UCA, read from file */ + UVersionInfo UCDVersion; /* UCD version, obtained by u_getUnicodeVersion */ + UVersionInfo formatVersion; /* format version from the UDataInfo header */ + uint32_t scriptToLeadByte; /* offset to script to lead collation byte mapping data */ + uint32_t leadByteToScript; /* offset to lead collation byte to script mapping data */ + uint8_t reserved[76]; /* for future use */ +} UCATableHeader; + +typedef struct { + uint32_t byteSize; + uint32_t tableSize; + uint32_t contsSize; + uint32_t table; + uint32_t conts; + UVersionInfo UCAVersion; /* version of the UCA, read from file */ + uint8_t padding[8]; +} InverseUCATableHeader; + +#endif /* !UCONFIG_NO_COLLATION */ + +#endif /* __UCOL_DATA_H__ */ diff --git a/deps/icu-small/source/common/ucol_swp.cpp b/deps/icu-small/source/common/ucol_swp.cpp index 59704ff8f67ecb..9913ae5d8b14a2 100644 --- a/deps/icu-small/source/common/ucol_swp.cpp +++ b/deps/icu-small/source/common/ucol_swp.cpp @@ -1,615 +1,615 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2003-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: ucol_swp.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2003sep10 -* created by: Markus W. Scherer -* -* Swap collation binaries. -*/ - -#include "unicode/udata.h" /* UDataInfo */ -#include "utrie.h" -#include "utrie2.h" -#include "udataswp.h" -#include "cmemory.h" -#include "ucol_data.h" -#include "ucol_swp.h" - -/* swapping ----------------------------------------------------------------- */ - -#if !UCONFIG_NO_COLLATION - -U_CAPI UBool U_EXPORT2 -ucol_looksLikeCollationBinary(const UDataSwapper *ds, - const void *inData, int32_t length) { - if(ds==NULL || inData==NULL || length<-1) { - return false; - } - - // First check for format version 4+ which has a standard data header. - UErrorCode errorCode=U_ZERO_ERROR; - (void)udata_swapDataHeader(ds, inData, -1, NULL, &errorCode); - if(U_SUCCESS(errorCode)) { - const UDataInfo &info=*(const UDataInfo *)((const char *)inData+4); - if(info.dataFormat[0]==0x55 && // dataFormat="UCol" - info.dataFormat[1]==0x43 && - info.dataFormat[2]==0x6f && - info.dataFormat[3]==0x6c) { - return true; - } - } - - // Else check for format version 3. - const UCATableHeader *inHeader=(const UCATableHeader *)inData; - - /* - * The collation binary must contain at least the UCATableHeader, - * starting with its size field. - * sizeof(UCATableHeader)==42*4 in ICU 2.8 - * check the length against the header size before reading the size field - */ - UCATableHeader header; - uprv_memset(&header, 0, sizeof(header)); - if(length<0) { - header.size=udata_readInt32(ds, inHeader->size); - } else if((length<(42*4) || length<(header.size=udata_readInt32(ds, inHeader->size)))) { - return false; - } - - header.magic=ds->readUInt32(inHeader->magic); - if(!( - header.magic==UCOL_HEADER_MAGIC && - inHeader->formatVersion[0]==3 /*&& - inHeader->formatVersion[1]>=0*/ - )) { - return false; - } - - if(inHeader->isBigEndian!=ds->inIsBigEndian || inHeader->charSetFamily!=ds->inCharset) { - return false; - } - - return true; -} - -namespace { - -/* swap a header-less collation formatVersion=3 binary, inside a resource bundle or ucadata.icu */ -int32_t -swapFormatVersion3(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const uint8_t *inBytes; - uint8_t *outBytes; - - const UCATableHeader *inHeader; - UCATableHeader *outHeader; - UCATableHeader header; - - uint32_t count; - - /* argument checking in case we were not called from ucol_swap() */ - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || length<-1 || (length>0 && outData==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - inBytes=(const uint8_t *)inData; - outBytes=(uint8_t *)outData; - - inHeader=(const UCATableHeader *)inData; - outHeader=(UCATableHeader *)outData; - - /* - * The collation binary must contain at least the UCATableHeader, - * starting with its size field. - * sizeof(UCATableHeader)==42*4 in ICU 2.8 - * check the length against the header size before reading the size field - */ - uprv_memset(&header, 0, sizeof(header)); - if(length<0) { - header.size=udata_readInt32(ds, inHeader->size); - } else if((length<(42*4) || length<(header.size=udata_readInt32(ds, inHeader->size)))) { - udata_printError(ds, "ucol_swap(formatVersion=3): too few bytes (%d after header) for collation data\n", - length); - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - header.magic=ds->readUInt32(inHeader->magic); - if(!( - header.magic==UCOL_HEADER_MAGIC && - inHeader->formatVersion[0]==3 /*&& - inHeader->formatVersion[1]>=0*/ - )) { - udata_printError(ds, "ucol_swap(formatVersion=3): magic 0x%08x or format version %02x.%02x is not a collation binary\n", - header.magic, - inHeader->formatVersion[0], inHeader->formatVersion[1]); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - if(inHeader->isBigEndian!=ds->inIsBigEndian || inHeader->charSetFamily!=ds->inCharset) { - udata_printError(ds, "ucol_swap(formatVersion=3): endianness %d or charset %d does not match the swapper\n", - inHeader->isBigEndian, inHeader->charSetFamily); - *pErrorCode=U_INVALID_FORMAT_ERROR; - return 0; - } - - if(length>=0) { - /* copy everything, takes care of data that needs no swapping */ - if(inBytes!=outBytes) { - uprv_memcpy(outBytes, inBytes, header.size); - } - - /* swap the necessary pieces in the order of their occurrence in the data */ - - /* read more of the UCATableHeader (the size field was read above) */ - header.options= ds->readUInt32(inHeader->options); - header.UCAConsts= ds->readUInt32(inHeader->UCAConsts); - header.contractionUCACombos= ds->readUInt32(inHeader->contractionUCACombos); - header.mappingPosition= ds->readUInt32(inHeader->mappingPosition); - header.expansion= ds->readUInt32(inHeader->expansion); - header.contractionIndex= ds->readUInt32(inHeader->contractionIndex); - header.contractionCEs= ds->readUInt32(inHeader->contractionCEs); - header.contractionSize= ds->readUInt32(inHeader->contractionSize); - header.endExpansionCE= ds->readUInt32(inHeader->endExpansionCE); - header.expansionCESize= ds->readUInt32(inHeader->expansionCESize); - header.endExpansionCECount= udata_readInt32(ds, inHeader->endExpansionCECount); - header.contractionUCACombosSize=udata_readInt32(ds, inHeader->contractionUCACombosSize); - header.scriptToLeadByte= ds->readUInt32(inHeader->scriptToLeadByte); - header.leadByteToScript= ds->readUInt32(inHeader->leadByteToScript); - - /* swap the 32-bit integers in the header */ - ds->swapArray32(ds, inHeader, (int32_t)((const char *)&inHeader->jamoSpecial-(const char *)inHeader), - outHeader, pErrorCode); - ds->swapArray32(ds, &(inHeader->scriptToLeadByte), sizeof(header.scriptToLeadByte) + sizeof(header.leadByteToScript), - &(outHeader->scriptToLeadByte), pErrorCode); - /* set the output platform properties */ - outHeader->isBigEndian=ds->outIsBigEndian; - outHeader->charSetFamily=ds->outCharset; - - /* swap the options */ - if(header.options!=0) { - ds->swapArray32(ds, inBytes+header.options, header.expansion-header.options, - outBytes+header.options, pErrorCode); - } - - /* swap the expansions */ - if(header.mappingPosition!=0 && header.expansion!=0) { - if(header.contractionIndex!=0) { - /* expansions bounded by contractions */ - count=header.contractionIndex-header.expansion; - } else { - /* no contractions: expansions bounded by the main trie */ - count=header.mappingPosition-header.expansion; - } - ds->swapArray32(ds, inBytes+header.expansion, (int32_t)count, - outBytes+header.expansion, pErrorCode); - } - - /* swap the contractions */ - if(header.contractionSize!=0) { - /* contractionIndex: UChar[] */ - ds->swapArray16(ds, inBytes+header.contractionIndex, header.contractionSize*2, - outBytes+header.contractionIndex, pErrorCode); - - /* contractionCEs: CEs[] */ - ds->swapArray32(ds, inBytes+header.contractionCEs, header.contractionSize*4, - outBytes+header.contractionCEs, pErrorCode); - } - - /* swap the main trie */ - if(header.mappingPosition!=0) { - count=header.endExpansionCE-header.mappingPosition; - utrie_swap(ds, inBytes+header.mappingPosition, (int32_t)count, - outBytes+header.mappingPosition, pErrorCode); - } - - /* swap the max expansion table */ - if(header.endExpansionCECount!=0) { - ds->swapArray32(ds, inBytes+header.endExpansionCE, header.endExpansionCECount*4, - outBytes+header.endExpansionCE, pErrorCode); - } - - /* expansionCESize, unsafeCP, contrEndCP: uint8_t[], no need to swap */ - - /* swap UCA constants */ - if(header.UCAConsts!=0) { - /* - * if UCAConsts!=0 then contractionUCACombos because we are swapping - * the UCA data file, and we know that the UCA contains contractions - */ - ds->swapArray32(ds, inBytes+header.UCAConsts, header.contractionUCACombos-header.UCAConsts, - outBytes+header.UCAConsts, pErrorCode); - } - - /* swap UCA contractions */ - if(header.contractionUCACombosSize!=0) { - count=header.contractionUCACombosSize*inHeader->contractionUCACombosWidth*U_SIZEOF_UCHAR; - ds->swapArray16(ds, inBytes+header.contractionUCACombos, (int32_t)count, - outBytes+header.contractionUCACombos, pErrorCode); - } - - /* swap the script to lead bytes */ - if(header.scriptToLeadByte!=0) { - int indexCount = ds->readUInt16(*((uint16_t*)(inBytes+header.scriptToLeadByte))); // each entry = 2 * uint16 - int dataCount = ds->readUInt16(*((uint16_t*)(inBytes+header.scriptToLeadByte + 2))); // each entry = uint16 - ds->swapArray16(ds, inBytes+header.scriptToLeadByte, - 4 + (4 * indexCount) + (2 * dataCount), - outBytes+header.scriptToLeadByte, pErrorCode); - } - - /* swap the lead byte to scripts */ - if(header.leadByteToScript!=0) { - int indexCount = ds->readUInt16(*((uint16_t*)(inBytes+header.leadByteToScript))); // each entry = uint16 - int dataCount = ds->readUInt16(*((uint16_t*)(inBytes+header.leadByteToScript + 2))); // each entry = uint16 - ds->swapArray16(ds, inBytes+header.leadByteToScript, - 4 + (2 * indexCount) + (2 * dataCount), - outBytes+header.leadByteToScript, pErrorCode); - } - } - - return header.size; -} - -// swap formatVersion 4 or 5 ----------------------------------------------- *** - -// The following are copied from CollationDataReader, trading an awkward copy of constants -// for an awkward relocation of the i18n collationdatareader.h file into the common library. -// Keep them in sync! - -enum { - IX_INDEXES_LENGTH, // 0 - IX_OPTIONS, - IX_RESERVED2, - IX_RESERVED3, - - IX_JAMO_CE32S_START, // 4 - IX_REORDER_CODES_OFFSET, - IX_REORDER_TABLE_OFFSET, - IX_TRIE_OFFSET, - - IX_RESERVED8_OFFSET, // 8 - IX_CES_OFFSET, - IX_RESERVED10_OFFSET, - IX_CE32S_OFFSET, - - IX_ROOT_ELEMENTS_OFFSET, // 12 - IX_CONTEXTS_OFFSET, - IX_UNSAFE_BWD_OFFSET, - IX_FAST_LATIN_TABLE_OFFSET, - - IX_SCRIPTS_OFFSET, // 16 - IX_COMPRESSIBLE_BYTES_OFFSET, - IX_RESERVED18_OFFSET, - IX_TOTAL_SIZE -}; - -int32_t -swapFormatVersion4(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - - const uint8_t *inBytes=(const uint8_t *)inData; - uint8_t *outBytes=(uint8_t *)outData; - - const int32_t *inIndexes=(const int32_t *)inBytes; - int32_t indexes[IX_TOTAL_SIZE+1]; - - // Need at least IX_INDEXES_LENGTH and IX_OPTIONS. - if(0<=length && length<8) { - udata_printError(ds, "ucol_swap(formatVersion=4): too few bytes " - "(%d after header) for collation data\n", - length); - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - int32_t indexesLength=indexes[0]=udata_readInt32(ds, inIndexes[0]); - if(0<=length && length<(indexesLength*4)) { - udata_printError(ds, "ucol_swap(formatVersion=4): too few bytes " - "(%d after header) for collation data\n", - length); - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - for(int32_t i=1; i<=IX_TOTAL_SIZE && iIX_TOTAL_SIZE) { - size=indexes[IX_TOTAL_SIZE]; - } else if(indexesLength>IX_REORDER_CODES_OFFSET) { - size=indexes[indexesLength-1]; - } else { - size=indexesLength*4; - } - if(length<0) { return size; } - - if(lengthswapArray32(ds, inBytes, indexesLength * 4, outBytes, &errorCode); - - // The following is a modified version of CollationDataReader::read(). - // Here we use indexes[] not inIndexes[] because - // the inIndexes[] may not be in this machine's endianness. - int32_t index; // one of the indexes[] slots - int32_t offset; // byte offset for the index part - // int32_t length; // number of bytes in the index part - - index = IX_REORDER_CODES_OFFSET; - offset = indexes[index]; - length = indexes[index + 1] - offset; - if(length > 0) { - ds->swapArray32(ds, inBytes + offset, length, outBytes + offset, &errorCode); - } - - // Skip the IX_REORDER_TABLE_OFFSET byte array. - - index = IX_TRIE_OFFSET; - offset = indexes[index]; - length = indexes[index + 1] - offset; - if(length > 0) { - utrie2_swap(ds, inBytes + offset, length, outBytes + offset, &errorCode); - } - - index = IX_RESERVED8_OFFSET; - offset = indexes[index]; - length = indexes[index + 1] - offset; - if(length > 0) { - udata_printError(ds, "ucol_swap(formatVersion=4): unknown data at IX_RESERVED8_OFFSET\n", length); - errorCode = U_UNSUPPORTED_ERROR; - return 0; - } - - index = IX_CES_OFFSET; - offset = indexes[index]; - length = indexes[index + 1] - offset; - if(length > 0) { - ds->swapArray64(ds, inBytes + offset, length, outBytes + offset, &errorCode); - } - - index = IX_RESERVED10_OFFSET; - offset = indexes[index]; - length = indexes[index + 1] - offset; - if(length > 0) { - udata_printError(ds, "ucol_swap(formatVersion=4): unknown data at IX_RESERVED10_OFFSET\n", length); - errorCode = U_UNSUPPORTED_ERROR; - return 0; - } - - index = IX_CE32S_OFFSET; - offset = indexes[index]; - length = indexes[index + 1] - offset; - if(length > 0) { - ds->swapArray32(ds, inBytes + offset, length, outBytes + offset, &errorCode); - } - - index = IX_ROOT_ELEMENTS_OFFSET; - offset = indexes[index]; - length = indexes[index + 1] - offset; - if(length > 0) { - ds->swapArray32(ds, inBytes + offset, length, outBytes + offset, &errorCode); - } - - index = IX_CONTEXTS_OFFSET; - offset = indexes[index]; - length = indexes[index + 1] - offset; - if(length > 0) { - ds->swapArray16(ds, inBytes + offset, length, outBytes + offset, &errorCode); - } - - index = IX_UNSAFE_BWD_OFFSET; - offset = indexes[index]; - length = indexes[index + 1] - offset; - if(length > 0) { - ds->swapArray16(ds, inBytes + offset, length, outBytes + offset, &errorCode); - } - - index = IX_FAST_LATIN_TABLE_OFFSET; - offset = indexes[index]; - length = indexes[index + 1] - offset; - if(length > 0) { - ds->swapArray16(ds, inBytes + offset, length, outBytes + offset, &errorCode); - } - - index = IX_SCRIPTS_OFFSET; - offset = indexes[index]; - length = indexes[index + 1] - offset; - if(length > 0) { - ds->swapArray16(ds, inBytes + offset, length, outBytes + offset, &errorCode); - } - - // Skip the IX_COMPRESSIBLE_BYTES_OFFSET byte array. - - index = IX_RESERVED18_OFFSET; - offset = indexes[index]; - length = indexes[index + 1] - offset; - if(length > 0) { - udata_printError(ds, "ucol_swap(formatVersion=4): unknown data at IX_RESERVED18_OFFSET\n", length); - errorCode = U_UNSUPPORTED_ERROR; - return 0; - } - - return size; -} - -} // namespace - -/* swap ICU collation data like ucadata.icu */ -U_CAPI int32_t U_EXPORT2 -ucol_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { return 0; } - - /* udata_swapDataHeader checks the arguments */ - int32_t headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - // Try to swap the old format version which did not have a standard data header. - *pErrorCode=U_ZERO_ERROR; - return swapFormatVersion3(ds, inData, length, outData, pErrorCode); - } - - /* check data format and format version */ - const UDataInfo &info=*(const UDataInfo *)((const char *)inData+4); - if(!( - info.dataFormat[0]==0x55 && // dataFormat="UCol" - info.dataFormat[1]==0x43 && - info.dataFormat[2]==0x6f && - info.dataFormat[3]==0x6c && - (3<=info.formatVersion[0] && info.formatVersion[0]<=5) - )) { - udata_printError(ds, "ucol_swap(): data format %02x.%02x.%02x.%02x " - "(format version %02x.%02x) is not recognized as collation data\n", - info.dataFormat[0], info.dataFormat[1], - info.dataFormat[2], info.dataFormat[3], - info.formatVersion[0], info.formatVersion[1]); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - inData=(const char *)inData+headerSize; - if(length>=0) { length-=headerSize; } - outData=(char *)outData+headerSize; - int32_t collationSize; - if(info.formatVersion[0]>=4) { - collationSize=swapFormatVersion4(ds, inData, length, outData, *pErrorCode); - } else { - collationSize=swapFormatVersion3(ds, inData, length, outData, pErrorCode); - } - if(U_SUCCESS(*pErrorCode)) { - return headerSize+collationSize; - } else { - return 0; - } -} - -/* swap inverse UCA collation data (invuca.icu) */ -U_CAPI int32_t U_EXPORT2 -ucol_swapInverseUCA(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const UDataInfo *pInfo; - int32_t headerSize; - - const uint8_t *inBytes; - uint8_t *outBytes; - - const InverseUCATableHeader *inHeader; - InverseUCATableHeader *outHeader; - InverseUCATableHeader header={ 0,0,0,0,0,{0,0,0,0},{0,0,0,0,0,0,0,0} }; - - /* udata_swapDataHeader checks the arguments */ - headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - /* check data format and format version */ - pInfo=(const UDataInfo *)((const char *)inData+4); - if(!( - pInfo->dataFormat[0]==0x49 && /* dataFormat="InvC" */ - pInfo->dataFormat[1]==0x6e && - pInfo->dataFormat[2]==0x76 && - pInfo->dataFormat[3]==0x43 && - pInfo->formatVersion[0]==2 && - pInfo->formatVersion[1]>=1 - )) { - udata_printError(ds, "ucol_swapInverseUCA(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not an inverse UCA collation file\n", - pInfo->dataFormat[0], pInfo->dataFormat[1], - pInfo->dataFormat[2], pInfo->dataFormat[3], - pInfo->formatVersion[0], pInfo->formatVersion[1]); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - inBytes=(const uint8_t *)inData+headerSize; - outBytes=(uint8_t *)outData+headerSize; - - inHeader=(const InverseUCATableHeader *)inBytes; - outHeader=(InverseUCATableHeader *)outBytes; - - /* - * The inverse UCA collation binary must contain at least the InverseUCATableHeader, - * starting with its size field. - * sizeof(UCATableHeader)==8*4 in ICU 2.8 - * check the length against the header size before reading the size field - */ - if(length<0) { - header.byteSize=udata_readInt32(ds, inHeader->byteSize); - } else if( - ((length-headerSize)<(8*4) || - (uint32_t)(length-headerSize)<(header.byteSize=udata_readInt32(ds, inHeader->byteSize))) - ) { - udata_printError(ds, "ucol_swapInverseUCA(): too few bytes (%d after header) for inverse UCA collation data\n", - length); - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - if(length>=0) { - /* copy everything, takes care of data that needs no swapping */ - if(inBytes!=outBytes) { - uprv_memcpy(outBytes, inBytes, header.byteSize); - } - - /* swap the necessary pieces in the order of their occurrence in the data */ - - /* read more of the InverseUCATableHeader (the byteSize field was read above) */ - header.tableSize= ds->readUInt32(inHeader->tableSize); - header.contsSize= ds->readUInt32(inHeader->contsSize); - header.table= ds->readUInt32(inHeader->table); - header.conts= ds->readUInt32(inHeader->conts); - - /* swap the 32-bit integers in the header */ - ds->swapArray32(ds, inHeader, 5*4, outHeader, pErrorCode); - - /* swap the inverse table; tableSize counts uint32_t[3] rows */ - ds->swapArray32(ds, inBytes+header.table, header.tableSize*3*4, - outBytes+header.table, pErrorCode); - - /* swap the continuation table; contsSize counts UChars */ - ds->swapArray16(ds, inBytes+header.conts, header.contsSize*U_SIZEOF_UCHAR, - outBytes+header.conts, pErrorCode); - } - - return headerSize+header.byteSize; -} - -#endif /* #if !UCONFIG_NO_COLLATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2003-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: ucol_swp.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2003sep10 +* created by: Markus W. Scherer +* +* Swap collation binaries. +*/ + +#include "unicode/udata.h" /* UDataInfo */ +#include "utrie.h" +#include "utrie2.h" +#include "udataswp.h" +#include "cmemory.h" +#include "ucol_data.h" +#include "ucol_swp.h" + +/* swapping ----------------------------------------------------------------- */ + +#if !UCONFIG_NO_COLLATION + +U_CAPI UBool U_EXPORT2 +ucol_looksLikeCollationBinary(const UDataSwapper *ds, + const void *inData, int32_t length) { + if(ds==nullptr || inData==nullptr || length<-1) { + return false; + } + + // First check for format version 4+ which has a standard data header. + UErrorCode errorCode=U_ZERO_ERROR; + (void)udata_swapDataHeader(ds, inData, -1, nullptr, &errorCode); + if(U_SUCCESS(errorCode)) { + const UDataInfo &info=*(const UDataInfo *)((const char *)inData+4); + if(info.dataFormat[0]==0x55 && // dataFormat="UCol" + info.dataFormat[1]==0x43 && + info.dataFormat[2]==0x6f && + info.dataFormat[3]==0x6c) { + return true; + } + } + + // Else check for format version 3. + const UCATableHeader *inHeader=(const UCATableHeader *)inData; + + /* + * The collation binary must contain at least the UCATableHeader, + * starting with its size field. + * sizeof(UCATableHeader)==42*4 in ICU 2.8 + * check the length against the header size before reading the size field + */ + UCATableHeader header; + uprv_memset(&header, 0, sizeof(header)); + if(length<0) { + header.size=udata_readInt32(ds, inHeader->size); + } else if((length<(42*4) || length<(header.size=udata_readInt32(ds, inHeader->size)))) { + return false; + } + + header.magic=ds->readUInt32(inHeader->magic); + if(!( + header.magic==UCOL_HEADER_MAGIC && + inHeader->formatVersion[0]==3 /*&& + inHeader->formatVersion[1]>=0*/ + )) { + return false; + } + + if(inHeader->isBigEndian!=ds->inIsBigEndian || inHeader->charSetFamily!=ds->inCharset) { + return false; + } + + return true; +} + +namespace { + +/* swap a header-less collation formatVersion=3 binary, inside a resource bundle or ucadata.icu */ +int32_t +swapFormatVersion3(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const uint8_t *inBytes; + uint8_t *outBytes; + + const UCATableHeader *inHeader; + UCATableHeader *outHeader; + UCATableHeader header; + + uint32_t count; + + /* argument checking in case we were not called from ucol_swap() */ + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<-1 || (length>0 && outData==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + inBytes=(const uint8_t *)inData; + outBytes=(uint8_t *)outData; + + inHeader=(const UCATableHeader *)inData; + outHeader=(UCATableHeader *)outData; + + /* + * The collation binary must contain at least the UCATableHeader, + * starting with its size field. + * sizeof(UCATableHeader)==42*4 in ICU 2.8 + * check the length against the header size before reading the size field + */ + uprv_memset(&header, 0, sizeof(header)); + if(length<0) { + header.size=udata_readInt32(ds, inHeader->size); + } else if((length<(42*4) || length<(header.size=udata_readInt32(ds, inHeader->size)))) { + udata_printError(ds, "ucol_swap(formatVersion=3): too few bytes (%d after header) for collation data\n", + length); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + header.magic=ds->readUInt32(inHeader->magic); + if(!( + header.magic==UCOL_HEADER_MAGIC && + inHeader->formatVersion[0]==3 /*&& + inHeader->formatVersion[1]>=0*/ + )) { + udata_printError(ds, "ucol_swap(formatVersion=3): magic 0x%08x or format version %02x.%02x is not a collation binary\n", + header.magic, + inHeader->formatVersion[0], inHeader->formatVersion[1]); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + if(inHeader->isBigEndian!=ds->inIsBigEndian || inHeader->charSetFamily!=ds->inCharset) { + udata_printError(ds, "ucol_swap(formatVersion=3): endianness %d or charset %d does not match the swapper\n", + inHeader->isBigEndian, inHeader->charSetFamily); + *pErrorCode=U_INVALID_FORMAT_ERROR; + return 0; + } + + if(length>=0) { + /* copy everything, takes care of data that needs no swapping */ + if(inBytes!=outBytes) { + uprv_memcpy(outBytes, inBytes, header.size); + } + + /* swap the necessary pieces in the order of their occurrence in the data */ + + /* read more of the UCATableHeader (the size field was read above) */ + header.options= ds->readUInt32(inHeader->options); + header.UCAConsts= ds->readUInt32(inHeader->UCAConsts); + header.contractionUCACombos= ds->readUInt32(inHeader->contractionUCACombos); + header.mappingPosition= ds->readUInt32(inHeader->mappingPosition); + header.expansion= ds->readUInt32(inHeader->expansion); + header.contractionIndex= ds->readUInt32(inHeader->contractionIndex); + header.contractionCEs= ds->readUInt32(inHeader->contractionCEs); + header.contractionSize= ds->readUInt32(inHeader->contractionSize); + header.endExpansionCE= ds->readUInt32(inHeader->endExpansionCE); + header.expansionCESize= ds->readUInt32(inHeader->expansionCESize); + header.endExpansionCECount= udata_readInt32(ds, inHeader->endExpansionCECount); + header.contractionUCACombosSize=udata_readInt32(ds, inHeader->contractionUCACombosSize); + header.scriptToLeadByte= ds->readUInt32(inHeader->scriptToLeadByte); + header.leadByteToScript= ds->readUInt32(inHeader->leadByteToScript); + + /* swap the 32-bit integers in the header */ + ds->swapArray32(ds, inHeader, (int32_t)((const char *)&inHeader->jamoSpecial-(const char *)inHeader), + outHeader, pErrorCode); + ds->swapArray32(ds, &(inHeader->scriptToLeadByte), sizeof(header.scriptToLeadByte) + sizeof(header.leadByteToScript), + &(outHeader->scriptToLeadByte), pErrorCode); + /* set the output platform properties */ + outHeader->isBigEndian=ds->outIsBigEndian; + outHeader->charSetFamily=ds->outCharset; + + /* swap the options */ + if(header.options!=0) { + ds->swapArray32(ds, inBytes+header.options, header.expansion-header.options, + outBytes+header.options, pErrorCode); + } + + /* swap the expansions */ + if(header.mappingPosition!=0 && header.expansion!=0) { + if(header.contractionIndex!=0) { + /* expansions bounded by contractions */ + count=header.contractionIndex-header.expansion; + } else { + /* no contractions: expansions bounded by the main trie */ + count=header.mappingPosition-header.expansion; + } + ds->swapArray32(ds, inBytes+header.expansion, (int32_t)count, + outBytes+header.expansion, pErrorCode); + } + + /* swap the contractions */ + if(header.contractionSize!=0) { + /* contractionIndex: char16_t[] */ + ds->swapArray16(ds, inBytes+header.contractionIndex, header.contractionSize*2, + outBytes+header.contractionIndex, pErrorCode); + + /* contractionCEs: CEs[] */ + ds->swapArray32(ds, inBytes+header.contractionCEs, header.contractionSize*4, + outBytes+header.contractionCEs, pErrorCode); + } + + /* swap the main trie */ + if(header.mappingPosition!=0) { + count=header.endExpansionCE-header.mappingPosition; + utrie_swap(ds, inBytes+header.mappingPosition, (int32_t)count, + outBytes+header.mappingPosition, pErrorCode); + } + + /* swap the max expansion table */ + if(header.endExpansionCECount!=0) { + ds->swapArray32(ds, inBytes+header.endExpansionCE, header.endExpansionCECount*4, + outBytes+header.endExpansionCE, pErrorCode); + } + + /* expansionCESize, unsafeCP, contrEndCP: uint8_t[], no need to swap */ + + /* swap UCA constants */ + if(header.UCAConsts!=0) { + /* + * if UCAConsts!=0 then contractionUCACombos because we are swapping + * the UCA data file, and we know that the UCA contains contractions + */ + ds->swapArray32(ds, inBytes+header.UCAConsts, header.contractionUCACombos-header.UCAConsts, + outBytes+header.UCAConsts, pErrorCode); + } + + /* swap UCA contractions */ + if(header.contractionUCACombosSize!=0) { + count=header.contractionUCACombosSize*inHeader->contractionUCACombosWidth*U_SIZEOF_UCHAR; + ds->swapArray16(ds, inBytes+header.contractionUCACombos, (int32_t)count, + outBytes+header.contractionUCACombos, pErrorCode); + } + + /* swap the script to lead bytes */ + if(header.scriptToLeadByte!=0) { + int indexCount = ds->readUInt16(*((uint16_t*)(inBytes+header.scriptToLeadByte))); // each entry = 2 * uint16 + int dataCount = ds->readUInt16(*((uint16_t*)(inBytes+header.scriptToLeadByte + 2))); // each entry = uint16 + ds->swapArray16(ds, inBytes+header.scriptToLeadByte, + 4 + (4 * indexCount) + (2 * dataCount), + outBytes+header.scriptToLeadByte, pErrorCode); + } + + /* swap the lead byte to scripts */ + if(header.leadByteToScript!=0) { + int indexCount = ds->readUInt16(*((uint16_t*)(inBytes+header.leadByteToScript))); // each entry = uint16 + int dataCount = ds->readUInt16(*((uint16_t*)(inBytes+header.leadByteToScript + 2))); // each entry = uint16 + ds->swapArray16(ds, inBytes+header.leadByteToScript, + 4 + (2 * indexCount) + (2 * dataCount), + outBytes+header.leadByteToScript, pErrorCode); + } + } + + return header.size; +} + +// swap formatVersion 4 or 5 ----------------------------------------------- *** + +// The following are copied from CollationDataReader, trading an awkward copy of constants +// for an awkward relocation of the i18n collationdatareader.h file into the common library. +// Keep them in sync! + +enum { + IX_INDEXES_LENGTH, // 0 + IX_OPTIONS, + IX_RESERVED2, + IX_RESERVED3, + + IX_JAMO_CE32S_START, // 4 + IX_REORDER_CODES_OFFSET, + IX_REORDER_TABLE_OFFSET, + IX_TRIE_OFFSET, + + IX_RESERVED8_OFFSET, // 8 + IX_CES_OFFSET, + IX_RESERVED10_OFFSET, + IX_CE32S_OFFSET, + + IX_ROOT_ELEMENTS_OFFSET, // 12 + IX_CONTEXTS_OFFSET, + IX_UNSAFE_BWD_OFFSET, + IX_FAST_LATIN_TABLE_OFFSET, + + IX_SCRIPTS_OFFSET, // 16 + IX_COMPRESSIBLE_BYTES_OFFSET, + IX_RESERVED18_OFFSET, + IX_TOTAL_SIZE +}; + +int32_t +swapFormatVersion4(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + + const uint8_t *inBytes=(const uint8_t *)inData; + uint8_t *outBytes=(uint8_t *)outData; + + const int32_t *inIndexes=(const int32_t *)inBytes; + int32_t indexes[IX_TOTAL_SIZE+1]; + + // Need at least IX_INDEXES_LENGTH and IX_OPTIONS. + if(0<=length && length<8) { + udata_printError(ds, "ucol_swap(formatVersion=4): too few bytes " + "(%d after header) for collation data\n", + length); + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + int32_t indexesLength=indexes[0]=udata_readInt32(ds, inIndexes[0]); + if(0<=length && length<(indexesLength*4)) { + udata_printError(ds, "ucol_swap(formatVersion=4): too few bytes " + "(%d after header) for collation data\n", + length); + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + for(int32_t i=1; i<=IX_TOTAL_SIZE && iIX_TOTAL_SIZE) { + size=indexes[IX_TOTAL_SIZE]; + } else if(indexesLength>IX_REORDER_CODES_OFFSET) { + size=indexes[indexesLength-1]; + } else { + size=indexesLength*4; + } + if(length<0) { return size; } + + if(lengthswapArray32(ds, inBytes, indexesLength * 4, outBytes, &errorCode); + + // The following is a modified version of CollationDataReader::read(). + // Here we use indexes[] not inIndexes[] because + // the inIndexes[] may not be in this machine's endianness. + int32_t index; // one of the indexes[] slots + int32_t offset; // byte offset for the index part + // int32_t length; // number of bytes in the index part + + index = IX_REORDER_CODES_OFFSET; + offset = indexes[index]; + length = indexes[index + 1] - offset; + if(length > 0) { + ds->swapArray32(ds, inBytes + offset, length, outBytes + offset, &errorCode); + } + + // Skip the IX_REORDER_TABLE_OFFSET byte array. + + index = IX_TRIE_OFFSET; + offset = indexes[index]; + length = indexes[index + 1] - offset; + if(length > 0) { + utrie2_swap(ds, inBytes + offset, length, outBytes + offset, &errorCode); + } + + index = IX_RESERVED8_OFFSET; + offset = indexes[index]; + length = indexes[index + 1] - offset; + if(length > 0) { + udata_printError(ds, "ucol_swap(formatVersion=4): unknown data at IX_RESERVED8_OFFSET\n", length); + errorCode = U_UNSUPPORTED_ERROR; + return 0; + } + + index = IX_CES_OFFSET; + offset = indexes[index]; + length = indexes[index + 1] - offset; + if(length > 0) { + ds->swapArray64(ds, inBytes + offset, length, outBytes + offset, &errorCode); + } + + index = IX_RESERVED10_OFFSET; + offset = indexes[index]; + length = indexes[index + 1] - offset; + if(length > 0) { + udata_printError(ds, "ucol_swap(formatVersion=4): unknown data at IX_RESERVED10_OFFSET\n", length); + errorCode = U_UNSUPPORTED_ERROR; + return 0; + } + + index = IX_CE32S_OFFSET; + offset = indexes[index]; + length = indexes[index + 1] - offset; + if(length > 0) { + ds->swapArray32(ds, inBytes + offset, length, outBytes + offset, &errorCode); + } + + index = IX_ROOT_ELEMENTS_OFFSET; + offset = indexes[index]; + length = indexes[index + 1] - offset; + if(length > 0) { + ds->swapArray32(ds, inBytes + offset, length, outBytes + offset, &errorCode); + } + + index = IX_CONTEXTS_OFFSET; + offset = indexes[index]; + length = indexes[index + 1] - offset; + if(length > 0) { + ds->swapArray16(ds, inBytes + offset, length, outBytes + offset, &errorCode); + } + + index = IX_UNSAFE_BWD_OFFSET; + offset = indexes[index]; + length = indexes[index + 1] - offset; + if(length > 0) { + ds->swapArray16(ds, inBytes + offset, length, outBytes + offset, &errorCode); + } + + index = IX_FAST_LATIN_TABLE_OFFSET; + offset = indexes[index]; + length = indexes[index + 1] - offset; + if(length > 0) { + ds->swapArray16(ds, inBytes + offset, length, outBytes + offset, &errorCode); + } + + index = IX_SCRIPTS_OFFSET; + offset = indexes[index]; + length = indexes[index + 1] - offset; + if(length > 0) { + ds->swapArray16(ds, inBytes + offset, length, outBytes + offset, &errorCode); + } + + // Skip the IX_COMPRESSIBLE_BYTES_OFFSET byte array. + + index = IX_RESERVED18_OFFSET; + offset = indexes[index]; + length = indexes[index + 1] - offset; + if(length > 0) { + udata_printError(ds, "ucol_swap(formatVersion=4): unknown data at IX_RESERVED18_OFFSET\n", length); + errorCode = U_UNSUPPORTED_ERROR; + return 0; + } + + return size; +} + +} // namespace + +/* swap ICU collation data like ucadata.icu */ +U_CAPI int32_t U_EXPORT2 +ucol_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { return 0; } + + /* udata_swapDataHeader checks the arguments */ + int32_t headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + // Try to swap the old format version which did not have a standard data header. + *pErrorCode=U_ZERO_ERROR; + return swapFormatVersion3(ds, inData, length, outData, pErrorCode); + } + + /* check data format and format version */ + const UDataInfo &info=*(const UDataInfo *)((const char *)inData+4); + if(!( + info.dataFormat[0]==0x55 && // dataFormat="UCol" + info.dataFormat[1]==0x43 && + info.dataFormat[2]==0x6f && + info.dataFormat[3]==0x6c && + (3<=info.formatVersion[0] && info.formatVersion[0]<=5) + )) { + udata_printError(ds, "ucol_swap(): data format %02x.%02x.%02x.%02x " + "(format version %02x.%02x) is not recognized as collation data\n", + info.dataFormat[0], info.dataFormat[1], + info.dataFormat[2], info.dataFormat[3], + info.formatVersion[0], info.formatVersion[1]); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + inData=(const char *)inData+headerSize; + if(length>=0) { length-=headerSize; } + outData=(outData == nullptr) ? nullptr : (char *)outData+headerSize; + int32_t collationSize; + if(info.formatVersion[0]>=4) { + collationSize=swapFormatVersion4(ds, inData, length, outData, *pErrorCode); + } else { + collationSize=swapFormatVersion3(ds, inData, length, outData, pErrorCode); + } + if(U_SUCCESS(*pErrorCode)) { + return headerSize+collationSize; + } else { + return 0; + } +} + +/* swap inverse UCA collation data (invuca.icu) */ +U_CAPI int32_t U_EXPORT2 +ucol_swapInverseUCA(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const UDataInfo *pInfo; + int32_t headerSize; + + const uint8_t *inBytes; + uint8_t *outBytes; + + const InverseUCATableHeader *inHeader; + InverseUCATableHeader *outHeader; + InverseUCATableHeader header={ 0,0,0,0,0,{0,0,0,0},{0,0,0,0,0,0,0,0} }; + + /* udata_swapDataHeader checks the arguments */ + headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + /* check data format and format version */ + pInfo=(const UDataInfo *)((const char *)inData+4); + if(!( + pInfo->dataFormat[0]==0x49 && /* dataFormat="InvC" */ + pInfo->dataFormat[1]==0x6e && + pInfo->dataFormat[2]==0x76 && + pInfo->dataFormat[3]==0x43 && + pInfo->formatVersion[0]==2 && + pInfo->formatVersion[1]>=1 + )) { + udata_printError(ds, "ucol_swapInverseUCA(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not an inverse UCA collation file\n", + pInfo->dataFormat[0], pInfo->dataFormat[1], + pInfo->dataFormat[2], pInfo->dataFormat[3], + pInfo->formatVersion[0], pInfo->formatVersion[1]); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + inBytes=(const uint8_t *)inData+headerSize; + outBytes=(uint8_t *)outData+headerSize; + + inHeader=(const InverseUCATableHeader *)inBytes; + outHeader=(InverseUCATableHeader *)outBytes; + + /* + * The inverse UCA collation binary must contain at least the InverseUCATableHeader, + * starting with its size field. + * sizeof(UCATableHeader)==8*4 in ICU 2.8 + * check the length against the header size before reading the size field + */ + if(length<0) { + header.byteSize=udata_readInt32(ds, inHeader->byteSize); + } else if( + ((length-headerSize)<(8*4) || + (uint32_t)(length-headerSize)<(header.byteSize=udata_readInt32(ds, inHeader->byteSize))) + ) { + udata_printError(ds, "ucol_swapInverseUCA(): too few bytes (%d after header) for inverse UCA collation data\n", + length); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + if(length>=0) { + /* copy everything, takes care of data that needs no swapping */ + if(inBytes!=outBytes) { + uprv_memcpy(outBytes, inBytes, header.byteSize); + } + + /* swap the necessary pieces in the order of their occurrence in the data */ + + /* read more of the InverseUCATableHeader (the byteSize field was read above) */ + header.tableSize= ds->readUInt32(inHeader->tableSize); + header.contsSize= ds->readUInt32(inHeader->contsSize); + header.table= ds->readUInt32(inHeader->table); + header.conts= ds->readUInt32(inHeader->conts); + + /* swap the 32-bit integers in the header */ + ds->swapArray32(ds, inHeader, 5*4, outHeader, pErrorCode); + + /* swap the inverse table; tableSize counts uint32_t[3] rows */ + ds->swapArray32(ds, inBytes+header.table, header.tableSize*3*4, + outBytes+header.table, pErrorCode); + + /* swap the continuation table; contsSize counts UChars */ + ds->swapArray16(ds, inBytes+header.conts, header.contsSize*U_SIZEOF_UCHAR, + outBytes+header.conts, pErrorCode); + } + + return headerSize+header.byteSize; +} + +#endif /* #if !UCONFIG_NO_COLLATION */ diff --git a/deps/icu-small/source/common/ucol_swp.h b/deps/icu-small/source/common/ucol_swp.h index 0c2990a85ecfc2..82207964d24f78 100644 --- a/deps/icu-small/source/common/ucol_swp.h +++ b/deps/icu-small/source/common/ucol_swp.h @@ -1,58 +1,58 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2003-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: ucol_swp.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2003sep10 -* created by: Markus W. Scherer -* -* Swap collation binaries. -*/ - -#ifndef __UCOL_SWP_H__ -#define __UCOL_SWP_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "udataswp.h" - -/* - * Does the data look like a collation binary? - * @internal - */ -U_CAPI UBool U_EXPORT2 -ucol_looksLikeCollationBinary(const UDataSwapper *ds, - const void *inData, int32_t length); - -/** - * Swap ICU collation data like ucadata.icu. See udataswp.h. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -ucol_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -/** - * Swap inverse UCA collation data (invuca.icu). See udataswp.h. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -ucol_swapInverseUCA(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -#endif /* #if !UCONFIG_NO_COLLATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2003-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: ucol_swp.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2003sep10 +* created by: Markus W. Scherer +* +* Swap collation binaries. +*/ + +#ifndef __UCOL_SWP_H__ +#define __UCOL_SWP_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "udataswp.h" + +/* + * Does the data look like a collation binary? + * @internal + */ +U_CAPI UBool U_EXPORT2 +ucol_looksLikeCollationBinary(const UDataSwapper *ds, + const void *inData, int32_t length); + +/** + * Swap ICU collation data like ucadata.icu. See udataswp.h. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +ucol_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +/** + * Swap inverse UCA collation data (invuca.icu). See udataswp.h. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +ucol_swapInverseUCA(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +#endif /* #if !UCONFIG_NO_COLLATION */ + +#endif diff --git a/deps/icu-small/source/common/ucptrie.cpp b/deps/icu-small/source/common/ucptrie.cpp index 0004160a238b0e..c3d1a357fddc51 100644 --- a/deps/icu-small/source/common/ucptrie.cpp +++ b/deps/icu-small/source/common/ucptrie.cpp @@ -1,601 +1,601 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// ucptrie.cpp (modified from utrie2.cpp) -// created: 2017dec29 Markus W. Scherer - -// #define UCPTRIE_DEBUG -#ifdef UCPTRIE_DEBUG -# include -#endif - -#include "unicode/utypes.h" -#include "unicode/ucptrie.h" -#include "unicode/utf.h" -#include "unicode/utf8.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "uassert.h" -#include "ucptrie_impl.h" - -U_CAPI UCPTrie * U_EXPORT2 -ucptrie_openFromBinary(UCPTrieType type, UCPTrieValueWidth valueWidth, - const void *data, int32_t length, int32_t *pActualLength, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return nullptr; - } - - if (length <= 0 || (U_POINTER_MASK_LSB(data, 3) != 0) || - type < UCPTRIE_TYPE_ANY || UCPTRIE_TYPE_SMALL < type || - valueWidth < UCPTRIE_VALUE_BITS_ANY || UCPTRIE_VALUE_BITS_8 < valueWidth) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - - // Enough data for a trie header? - if (length < (int32_t)sizeof(UCPTrieHeader)) { - *pErrorCode = U_INVALID_FORMAT_ERROR; - return nullptr; - } - - // Check the signature. - const UCPTrieHeader *header = (const UCPTrieHeader *)data; - if (header->signature != UCPTRIE_SIG) { - *pErrorCode = U_INVALID_FORMAT_ERROR; - return nullptr; - } - - int32_t options = header->options; - int32_t typeInt = (options >> 6) & 3; - int32_t valueWidthInt = options & UCPTRIE_OPTIONS_VALUE_BITS_MASK; - if (typeInt > UCPTRIE_TYPE_SMALL || valueWidthInt > UCPTRIE_VALUE_BITS_8 || - (options & UCPTRIE_OPTIONS_RESERVED_MASK) != 0) { - *pErrorCode = U_INVALID_FORMAT_ERROR; - return nullptr; - } - UCPTrieType actualType = (UCPTrieType)typeInt; - UCPTrieValueWidth actualValueWidth = (UCPTrieValueWidth)valueWidthInt; - if (type < 0) { - type = actualType; - } - if (valueWidth < 0) { - valueWidth = actualValueWidth; - } - if (type != actualType || valueWidth != actualValueWidth) { - *pErrorCode = U_INVALID_FORMAT_ERROR; - return nullptr; - } - - // Get the length values and offsets. - UCPTrie tempTrie; - uprv_memset(&tempTrie, 0, sizeof(tempTrie)); - tempTrie.indexLength = header->indexLength; - tempTrie.dataLength = - ((options & UCPTRIE_OPTIONS_DATA_LENGTH_MASK) << 4) | header->dataLength; - tempTrie.index3NullOffset = header->index3NullOffset; - tempTrie.dataNullOffset = - ((options & UCPTRIE_OPTIONS_DATA_NULL_OFFSET_MASK) << 8) | header->dataNullOffset; - - tempTrie.highStart = header->shiftedHighStart << UCPTRIE_SHIFT_2; - tempTrie.shifted12HighStart = (tempTrie.highStart + 0xfff) >> 12; - tempTrie.type = type; - tempTrie.valueWidth = valueWidth; - - // Calculate the actual length. - int32_t actualLength = (int32_t)sizeof(UCPTrieHeader) + tempTrie.indexLength * 2; - if (valueWidth == UCPTRIE_VALUE_BITS_16) { - actualLength += tempTrie.dataLength * 2; - } else if (valueWidth == UCPTRIE_VALUE_BITS_32) { - actualLength += tempTrie.dataLength * 4; - } else { - actualLength += tempTrie.dataLength; - } - if (length < actualLength) { - *pErrorCode = U_INVALID_FORMAT_ERROR; // Not enough bytes. - return nullptr; - } - - // Allocate the trie. - UCPTrie *trie = (UCPTrie *)uprv_malloc(sizeof(UCPTrie)); - if (trie == nullptr) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - uprv_memcpy(trie, &tempTrie, sizeof(tempTrie)); -#ifdef UCPTRIE_DEBUG - trie->name = "fromSerialized"; -#endif - - // Set the pointers to its index and data arrays. - const uint16_t *p16 = (const uint16_t *)(header + 1); - trie->index = p16; - p16 += trie->indexLength; - - // Get the data. - int32_t nullValueOffset = trie->dataNullOffset; - if (nullValueOffset >= trie->dataLength) { - nullValueOffset = trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET; - } - switch (valueWidth) { - case UCPTRIE_VALUE_BITS_16: - trie->data.ptr16 = p16; - trie->nullValue = trie->data.ptr16[nullValueOffset]; - break; - case UCPTRIE_VALUE_BITS_32: - trie->data.ptr32 = (const uint32_t *)p16; - trie->nullValue = trie->data.ptr32[nullValueOffset]; - break; - case UCPTRIE_VALUE_BITS_8: - trie->data.ptr8 = (const uint8_t *)p16; - trie->nullValue = trie->data.ptr8[nullValueOffset]; - break; - default: - // Unreachable because valueWidth was checked above. - *pErrorCode = U_INVALID_FORMAT_ERROR; - return nullptr; - } - - if (pActualLength != nullptr) { - *pActualLength = actualLength; - } - return trie; -} - -U_CAPI void U_EXPORT2 -ucptrie_close(UCPTrie *trie) { - uprv_free(trie); -} - -U_CAPI UCPTrieType U_EXPORT2 -ucptrie_getType(const UCPTrie *trie) { - return (UCPTrieType)trie->type; -} - -U_CAPI UCPTrieValueWidth U_EXPORT2 -ucptrie_getValueWidth(const UCPTrie *trie) { - return (UCPTrieValueWidth)trie->valueWidth; -} - -U_CAPI int32_t U_EXPORT2 -ucptrie_internalSmallIndex(const UCPTrie *trie, UChar32 c) { - int32_t i1 = c >> UCPTRIE_SHIFT_1; - if (trie->type == UCPTRIE_TYPE_FAST) { - U_ASSERT(0xffff < c && c < trie->highStart); - i1 += UCPTRIE_BMP_INDEX_LENGTH - UCPTRIE_OMITTED_BMP_INDEX_1_LENGTH; - } else { - U_ASSERT((uint32_t)c < (uint32_t)trie->highStart && trie->highStart > UCPTRIE_SMALL_LIMIT); - i1 += UCPTRIE_SMALL_INDEX_LENGTH; - } - int32_t i3Block = trie->index[ - (int32_t)trie->index[i1] + ((c >> UCPTRIE_SHIFT_2) & UCPTRIE_INDEX_2_MASK)]; - int32_t i3 = (c >> UCPTRIE_SHIFT_3) & UCPTRIE_INDEX_3_MASK; - int32_t dataBlock; - if ((i3Block & 0x8000) == 0) { - // 16-bit indexes - dataBlock = trie->index[i3Block + i3]; - } else { - // 18-bit indexes stored in groups of 9 entries per 8 indexes. - i3Block = (i3Block & 0x7fff) + (i3 & ~7) + (i3 >> 3); - i3 &= 7; - dataBlock = ((int32_t)trie->index[i3Block++] << (2 + (2 * i3))) & 0x30000; - dataBlock |= trie->index[i3Block + i3]; - } - return dataBlock + (c & UCPTRIE_SMALL_DATA_MASK); -} - -U_CAPI int32_t U_EXPORT2 -ucptrie_internalSmallU8Index(const UCPTrie *trie, int32_t lt1, uint8_t t2, uint8_t t3) { - UChar32 c = (lt1 << 12) | (t2 << 6) | t3; - if (c >= trie->highStart) { - // Possible because the UTF-8 macro compares with shifted12HighStart which may be higher. - return trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET; - } - return ucptrie_internalSmallIndex(trie, c); -} - -U_CAPI int32_t U_EXPORT2 -ucptrie_internalU8PrevIndex(const UCPTrie *trie, UChar32 c, - const uint8_t *start, const uint8_t *src) { - int32_t i, length; - // Support 64-bit pointers by avoiding cast of arbitrary difference. - if ((src - start) <= 7) { - i = length = (int32_t)(src - start); - } else { - i = length = 7; - start = src - 7; - } - c = utf8_prevCharSafeBody(start, 0, &i, c, -1); - i = length - i; // Number of bytes read backward from src. - int32_t idx = _UCPTRIE_CP_INDEX(trie, 0xffff, c); - return (idx << 3) | i; -} - -namespace { - -inline uint32_t getValue(UCPTrieData data, UCPTrieValueWidth valueWidth, int32_t dataIndex) { - switch (valueWidth) { - case UCPTRIE_VALUE_BITS_16: - return data.ptr16[dataIndex]; - case UCPTRIE_VALUE_BITS_32: - return data.ptr32[dataIndex]; - case UCPTRIE_VALUE_BITS_8: - return data.ptr8[dataIndex]; - default: - // Unreachable if the trie is properly initialized. - return 0xffffffff; - } -} - -} // namespace - -U_CAPI uint32_t U_EXPORT2 -ucptrie_get(const UCPTrie *trie, UChar32 c) { - int32_t dataIndex; - if ((uint32_t)c <= 0x7f) { - // linear ASCII - dataIndex = c; - } else { - UChar32 fastMax = trie->type == UCPTRIE_TYPE_FAST ? 0xffff : UCPTRIE_SMALL_MAX; - dataIndex = _UCPTRIE_CP_INDEX(trie, fastMax, c); - } - return getValue(trie->data, (UCPTrieValueWidth)trie->valueWidth, dataIndex); -} - -namespace { - -constexpr int32_t MAX_UNICODE = 0x10ffff; - -inline uint32_t maybeFilterValue(uint32_t value, uint32_t trieNullValue, uint32_t nullValue, - UCPMapValueFilter *filter, const void *context) { - if (value == trieNullValue) { - value = nullValue; - } else if (filter != nullptr) { - value = filter(context, value); - } - return value; -} - -UChar32 getRange(const void *t, UChar32 start, - UCPMapValueFilter *filter, const void *context, uint32_t *pValue) { - if ((uint32_t)start > MAX_UNICODE) { - return U_SENTINEL; - } - const UCPTrie *trie = reinterpret_cast(t); - UCPTrieValueWidth valueWidth = (UCPTrieValueWidth)trie->valueWidth; - if (start >= trie->highStart) { - if (pValue != nullptr) { - int32_t di = trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET; - uint32_t value = getValue(trie->data, valueWidth, di); - if (filter != nullptr) { value = filter(context, value); } - *pValue = value; - } - return MAX_UNICODE; - } - - uint32_t nullValue = trie->nullValue; - if (filter != nullptr) { nullValue = filter(context, nullValue); } - const uint16_t *index = trie->index; - - int32_t prevI3Block = -1; - int32_t prevBlock = -1; - UChar32 c = start; - uint32_t trieValue, value = nullValue; - bool haveValue = false; - do { - int32_t i3Block; - int32_t i3; - int32_t i3BlockLength; - int32_t dataBlockLength; - if (c <= 0xffff && (trie->type == UCPTRIE_TYPE_FAST || c <= UCPTRIE_SMALL_MAX)) { - i3Block = 0; - i3 = c >> UCPTRIE_FAST_SHIFT; - i3BlockLength = trie->type == UCPTRIE_TYPE_FAST ? - UCPTRIE_BMP_INDEX_LENGTH : UCPTRIE_SMALL_INDEX_LENGTH; - dataBlockLength = UCPTRIE_FAST_DATA_BLOCK_LENGTH; - } else { - // Use the multi-stage index. - int32_t i1 = c >> UCPTRIE_SHIFT_1; - if (trie->type == UCPTRIE_TYPE_FAST) { - U_ASSERT(0xffff < c && c < trie->highStart); - i1 += UCPTRIE_BMP_INDEX_LENGTH - UCPTRIE_OMITTED_BMP_INDEX_1_LENGTH; - } else { - U_ASSERT(c < trie->highStart && trie->highStart > UCPTRIE_SMALL_LIMIT); - i1 += UCPTRIE_SMALL_INDEX_LENGTH; - } - i3Block = trie->index[ - (int32_t)trie->index[i1] + ((c >> UCPTRIE_SHIFT_2) & UCPTRIE_INDEX_2_MASK)]; - if (i3Block == prevI3Block && (c - start) >= UCPTRIE_CP_PER_INDEX_2_ENTRY) { - // The index-3 block is the same as the previous one, and filled with value. - U_ASSERT((c & (UCPTRIE_CP_PER_INDEX_2_ENTRY - 1)) == 0); - c += UCPTRIE_CP_PER_INDEX_2_ENTRY; - continue; - } - prevI3Block = i3Block; - if (i3Block == trie->index3NullOffset) { - // This is the index-3 null block. - if (haveValue) { - if (nullValue != value) { - return c - 1; - } - } else { - trieValue = trie->nullValue; - value = nullValue; - if (pValue != nullptr) { *pValue = nullValue; } - haveValue = true; - } - prevBlock = trie->dataNullOffset; - c = (c + UCPTRIE_CP_PER_INDEX_2_ENTRY) & ~(UCPTRIE_CP_PER_INDEX_2_ENTRY - 1); - continue; - } - i3 = (c >> UCPTRIE_SHIFT_3) & UCPTRIE_INDEX_3_MASK; - i3BlockLength = UCPTRIE_INDEX_3_BLOCK_LENGTH; - dataBlockLength = UCPTRIE_SMALL_DATA_BLOCK_LENGTH; - } - // Enumerate data blocks for one index-3 block. - do { - int32_t block; - if ((i3Block & 0x8000) == 0) { - block = index[i3Block + i3]; - } else { - // 18-bit indexes stored in groups of 9 entries per 8 indexes. - int32_t group = (i3Block & 0x7fff) + (i3 & ~7) + (i3 >> 3); - int32_t gi = i3 & 7; - block = ((int32_t)index[group++] << (2 + (2 * gi))) & 0x30000; - block |= index[group + gi]; - } - if (block == prevBlock && (c - start) >= dataBlockLength) { - // The block is the same as the previous one, and filled with value. - U_ASSERT((c & (dataBlockLength - 1)) == 0); - c += dataBlockLength; - } else { - int32_t dataMask = dataBlockLength - 1; - prevBlock = block; - if (block == trie->dataNullOffset) { - // This is the data null block. - if (haveValue) { - if (nullValue != value) { - return c - 1; - } - } else { - trieValue = trie->nullValue; - value = nullValue; - if (pValue != nullptr) { *pValue = nullValue; } - haveValue = true; - } - c = (c + dataBlockLength) & ~dataMask; - } else { - int32_t di = block + (c & dataMask); - uint32_t trieValue2 = getValue(trie->data, valueWidth, di); - if (haveValue) { - if (trieValue2 != trieValue) { - if (filter == nullptr || - maybeFilterValue(trieValue2, trie->nullValue, nullValue, - filter, context) != value) { - return c - 1; - } - trieValue = trieValue2; // may or may not help - } - } else { - trieValue = trieValue2; - value = maybeFilterValue(trieValue2, trie->nullValue, nullValue, - filter, context); - if (pValue != nullptr) { *pValue = value; } - haveValue = true; - } - while ((++c & dataMask) != 0) { - trieValue2 = getValue(trie->data, valueWidth, ++di); - if (trieValue2 != trieValue) { - if (filter == nullptr || - maybeFilterValue(trieValue2, trie->nullValue, nullValue, - filter, context) != value) { - return c - 1; - } - trieValue = trieValue2; // may or may not help - } - } - } - } - } while (++i3 < i3BlockLength); - } while (c < trie->highStart); - U_ASSERT(haveValue); - int32_t di = trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET; - uint32_t highValue = getValue(trie->data, valueWidth, di); - if (maybeFilterValue(highValue, trie->nullValue, nullValue, - filter, context) != value) { - return c - 1; - } else { - return MAX_UNICODE; - } -} - -} // namespace - -U_CFUNC UChar32 -ucptrie_internalGetRange(UCPTrieGetRange *getRange, - const void *trie, UChar32 start, - UCPMapRangeOption option, uint32_t surrogateValue, - UCPMapValueFilter *filter, const void *context, uint32_t *pValue) { - if (option == UCPMAP_RANGE_NORMAL) { - return getRange(trie, start, filter, context, pValue); - } - uint32_t value; - if (pValue == nullptr) { - // We need to examine the range value even if the caller does not want it. - pValue = &value; - } - UChar32 surrEnd = option == UCPMAP_RANGE_FIXED_ALL_SURROGATES ? 0xdfff : 0xdbff; - UChar32 end = getRange(trie, start, filter, context, pValue); - if (end < 0xd7ff || start > surrEnd) { - return end; - } - // The range overlaps with surrogates, or ends just before the first one. - if (*pValue == surrogateValue) { - if (end >= surrEnd) { - // Surrogates followed by a non-surrogateValue range, - // or surrogates are part of a larger surrogateValue range. - return end; - } - } else { - if (start <= 0xd7ff) { - return 0xd7ff; // Non-surrogateValue range ends before surrogateValue surrogates. - } - // Start is a surrogate with a non-surrogateValue code *unit* value. - // Return a surrogateValue code *point* range. - *pValue = surrogateValue; - if (end > surrEnd) { - return surrEnd; // Surrogate range ends before non-surrogateValue rest of range. - } - } - // See if the surrogateValue surrogate range can be merged with - // an immediately following range. - uint32_t value2; - UChar32 end2 = getRange(trie, surrEnd + 1, filter, context, &value2); - if (value2 == surrogateValue) { - return end2; - } - return surrEnd; -} - -U_CAPI UChar32 U_EXPORT2 -ucptrie_getRange(const UCPTrie *trie, UChar32 start, - UCPMapRangeOption option, uint32_t surrogateValue, - UCPMapValueFilter *filter, const void *context, uint32_t *pValue) { - return ucptrie_internalGetRange(getRange, trie, start, - option, surrogateValue, - filter, context, pValue); -} - -U_CAPI int32_t U_EXPORT2 -ucptrie_toBinary(const UCPTrie *trie, - void *data, int32_t capacity, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return 0; - } - - UCPTrieType type = (UCPTrieType)trie->type; - UCPTrieValueWidth valueWidth = (UCPTrieValueWidth)trie->valueWidth; - if (type < UCPTRIE_TYPE_FAST || UCPTRIE_TYPE_SMALL < type || - valueWidth < UCPTRIE_VALUE_BITS_16 || UCPTRIE_VALUE_BITS_8 < valueWidth || - capacity < 0 || - (capacity > 0 && (data == nullptr || (U_POINTER_MASK_LSB(data, 3) != 0)))) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - int32_t length = (int32_t)sizeof(UCPTrieHeader) + trie->indexLength * 2; - switch (valueWidth) { - case UCPTRIE_VALUE_BITS_16: - length += trie->dataLength * 2; - break; - case UCPTRIE_VALUE_BITS_32: - length += trie->dataLength * 4; - break; - case UCPTRIE_VALUE_BITS_8: - length += trie->dataLength; - break; - default: - // unreachable - break; - } - if (capacity < length) { - *pErrorCode = U_BUFFER_OVERFLOW_ERROR; - return length; - } - - char *bytes = (char *)data; - UCPTrieHeader *header = (UCPTrieHeader *)bytes; - header->signature = UCPTRIE_SIG; // "Tri3" - header->options = (uint16_t)( - ((trie->dataLength & 0xf0000) >> 4) | - ((trie->dataNullOffset & 0xf0000) >> 8) | - (trie->type << 6) | - valueWidth); - header->indexLength = (uint16_t)trie->indexLength; - header->dataLength = (uint16_t)trie->dataLength; - header->index3NullOffset = trie->index3NullOffset; - header->dataNullOffset = (uint16_t)trie->dataNullOffset; - header->shiftedHighStart = trie->highStart >> UCPTRIE_SHIFT_2; - bytes += sizeof(UCPTrieHeader); - - uprv_memcpy(bytes, trie->index, trie->indexLength * 2); - bytes += trie->indexLength * 2; - - switch (valueWidth) { - case UCPTRIE_VALUE_BITS_16: - uprv_memcpy(bytes, trie->data.ptr16, trie->dataLength * 2); - break; - case UCPTRIE_VALUE_BITS_32: - uprv_memcpy(bytes, trie->data.ptr32, trie->dataLength * 4); - break; - case UCPTRIE_VALUE_BITS_8: - uprv_memcpy(bytes, trie->data.ptr8, trie->dataLength); - break; - default: - // unreachable - break; - } - return length; -} - -namespace { - -#ifdef UCPTRIE_DEBUG -long countNull(const UCPTrie *trie) { - uint32_t nullValue=trie->nullValue; - int32_t length=trie->dataLength; - long count=0; - switch (trie->valueWidth) { - case UCPTRIE_VALUE_BITS_16: - for(int32_t i=0; idata.ptr16[i]==nullValue) { ++count; } - } - break; - case UCPTRIE_VALUE_BITS_32: - for(int32_t i=0; idata.ptr32[i]==nullValue) { ++count; } - } - break; - case UCPTRIE_VALUE_BITS_8: - for(int32_t i=0; idata.ptr8[i]==nullValue) { ++count; } - } - break; - default: - // unreachable - break; - } - return count; -} - -U_CFUNC void -ucptrie_printLengths(const UCPTrie *trie, const char *which) { - long indexLength=trie->indexLength; - long dataLength=(long)trie->dataLength; - long totalLength=(long)sizeof(UCPTrieHeader)+indexLength*2+ - dataLength*(trie->valueWidth==UCPTRIE_VALUE_BITS_16 ? 2 : - trie->valueWidth==UCPTRIE_VALUE_BITS_32 ? 4 : 1); - printf("**UCPTrieLengths(%s %s)** index:%6ld data:%6ld countNull:%6ld serialized:%6ld\n", - which, trie->name, indexLength, dataLength, countNull(trie), totalLength); -} -#endif - -} // namespace - -// UCPMap ---- -// Initially, this is the same as UCPTrie. This may well change. - -U_CAPI uint32_t U_EXPORT2 -ucpmap_get(const UCPMap *map, UChar32 c) { - return ucptrie_get(reinterpret_cast(map), c); -} - -U_CAPI UChar32 U_EXPORT2 -ucpmap_getRange(const UCPMap *map, UChar32 start, - UCPMapRangeOption option, uint32_t surrogateValue, - UCPMapValueFilter *filter, const void *context, uint32_t *pValue) { - return ucptrie_getRange(reinterpret_cast(map), start, - option, surrogateValue, - filter, context, pValue); -} +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// ucptrie.cpp (modified from utrie2.cpp) +// created: 2017dec29 Markus W. Scherer + +// #define UCPTRIE_DEBUG +#ifdef UCPTRIE_DEBUG +# include +#endif + +#include "unicode/utypes.h" +#include "unicode/ucptrie.h" +#include "unicode/utf.h" +#include "unicode/utf8.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "uassert.h" +#include "ucptrie_impl.h" + +U_CAPI UCPTrie * U_EXPORT2 +ucptrie_openFromBinary(UCPTrieType type, UCPTrieValueWidth valueWidth, + const void *data, int32_t length, int32_t *pActualLength, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return nullptr; + } + + if (length <= 0 || (U_POINTER_MASK_LSB(data, 3) != 0) || + type < UCPTRIE_TYPE_ANY || UCPTRIE_TYPE_SMALL < type || + valueWidth < UCPTRIE_VALUE_BITS_ANY || UCPTRIE_VALUE_BITS_8 < valueWidth) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + // Enough data for a trie header? + if (length < (int32_t)sizeof(UCPTrieHeader)) { + *pErrorCode = U_INVALID_FORMAT_ERROR; + return nullptr; + } + + // Check the signature. + const UCPTrieHeader *header = (const UCPTrieHeader *)data; + if (header->signature != UCPTRIE_SIG) { + *pErrorCode = U_INVALID_FORMAT_ERROR; + return nullptr; + } + + int32_t options = header->options; + int32_t typeInt = (options >> 6) & 3; + int32_t valueWidthInt = options & UCPTRIE_OPTIONS_VALUE_BITS_MASK; + if (typeInt > UCPTRIE_TYPE_SMALL || valueWidthInt > UCPTRIE_VALUE_BITS_8 || + (options & UCPTRIE_OPTIONS_RESERVED_MASK) != 0) { + *pErrorCode = U_INVALID_FORMAT_ERROR; + return nullptr; + } + UCPTrieType actualType = (UCPTrieType)typeInt; + UCPTrieValueWidth actualValueWidth = (UCPTrieValueWidth)valueWidthInt; + if (type < 0) { + type = actualType; + } + if (valueWidth < 0) { + valueWidth = actualValueWidth; + } + if (type != actualType || valueWidth != actualValueWidth) { + *pErrorCode = U_INVALID_FORMAT_ERROR; + return nullptr; + } + + // Get the length values and offsets. + UCPTrie tempTrie; + uprv_memset(&tempTrie, 0, sizeof(tempTrie)); + tempTrie.indexLength = header->indexLength; + tempTrie.dataLength = + ((options & UCPTRIE_OPTIONS_DATA_LENGTH_MASK) << 4) | header->dataLength; + tempTrie.index3NullOffset = header->index3NullOffset; + tempTrie.dataNullOffset = + ((options & UCPTRIE_OPTIONS_DATA_NULL_OFFSET_MASK) << 8) | header->dataNullOffset; + + tempTrie.highStart = header->shiftedHighStart << UCPTRIE_SHIFT_2; + tempTrie.shifted12HighStart = (tempTrie.highStart + 0xfff) >> 12; + tempTrie.type = type; + tempTrie.valueWidth = valueWidth; + + // Calculate the actual length. + int32_t actualLength = (int32_t)sizeof(UCPTrieHeader) + tempTrie.indexLength * 2; + if (valueWidth == UCPTRIE_VALUE_BITS_16) { + actualLength += tempTrie.dataLength * 2; + } else if (valueWidth == UCPTRIE_VALUE_BITS_32) { + actualLength += tempTrie.dataLength * 4; + } else { + actualLength += tempTrie.dataLength; + } + if (length < actualLength) { + *pErrorCode = U_INVALID_FORMAT_ERROR; // Not enough bytes. + return nullptr; + } + + // Allocate the trie. + UCPTrie *trie = (UCPTrie *)uprv_malloc(sizeof(UCPTrie)); + if (trie == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memcpy(trie, &tempTrie, sizeof(tempTrie)); +#ifdef UCPTRIE_DEBUG + trie->name = "fromSerialized"; +#endif + + // Set the pointers to its index and data arrays. + const uint16_t *p16 = (const uint16_t *)(header + 1); + trie->index = p16; + p16 += trie->indexLength; + + // Get the data. + int32_t nullValueOffset = trie->dataNullOffset; + if (nullValueOffset >= trie->dataLength) { + nullValueOffset = trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET; + } + switch (valueWidth) { + case UCPTRIE_VALUE_BITS_16: + trie->data.ptr16 = p16; + trie->nullValue = trie->data.ptr16[nullValueOffset]; + break; + case UCPTRIE_VALUE_BITS_32: + trie->data.ptr32 = (const uint32_t *)p16; + trie->nullValue = trie->data.ptr32[nullValueOffset]; + break; + case UCPTRIE_VALUE_BITS_8: + trie->data.ptr8 = (const uint8_t *)p16; + trie->nullValue = trie->data.ptr8[nullValueOffset]; + break; + default: + // Unreachable because valueWidth was checked above. + *pErrorCode = U_INVALID_FORMAT_ERROR; + return nullptr; + } + + if (pActualLength != nullptr) { + *pActualLength = actualLength; + } + return trie; +} + +U_CAPI void U_EXPORT2 +ucptrie_close(UCPTrie *trie) { + uprv_free(trie); +} + +U_CAPI UCPTrieType U_EXPORT2 +ucptrie_getType(const UCPTrie *trie) { + return (UCPTrieType)trie->type; +} + +U_CAPI UCPTrieValueWidth U_EXPORT2 +ucptrie_getValueWidth(const UCPTrie *trie) { + return (UCPTrieValueWidth)trie->valueWidth; +} + +U_CAPI int32_t U_EXPORT2 +ucptrie_internalSmallIndex(const UCPTrie *trie, UChar32 c) { + int32_t i1 = c >> UCPTRIE_SHIFT_1; + if (trie->type == UCPTRIE_TYPE_FAST) { + U_ASSERT(0xffff < c && c < trie->highStart); + i1 += UCPTRIE_BMP_INDEX_LENGTH - UCPTRIE_OMITTED_BMP_INDEX_1_LENGTH; + } else { + U_ASSERT((uint32_t)c < (uint32_t)trie->highStart && trie->highStart > UCPTRIE_SMALL_LIMIT); + i1 += UCPTRIE_SMALL_INDEX_LENGTH; + } + int32_t i3Block = trie->index[ + (int32_t)trie->index[i1] + ((c >> UCPTRIE_SHIFT_2) & UCPTRIE_INDEX_2_MASK)]; + int32_t i3 = (c >> UCPTRIE_SHIFT_3) & UCPTRIE_INDEX_3_MASK; + int32_t dataBlock; + if ((i3Block & 0x8000) == 0) { + // 16-bit indexes + dataBlock = trie->index[i3Block + i3]; + } else { + // 18-bit indexes stored in groups of 9 entries per 8 indexes. + i3Block = (i3Block & 0x7fff) + (i3 & ~7) + (i3 >> 3); + i3 &= 7; + dataBlock = ((int32_t)trie->index[i3Block++] << (2 + (2 * i3))) & 0x30000; + dataBlock |= trie->index[i3Block + i3]; + } + return dataBlock + (c & UCPTRIE_SMALL_DATA_MASK); +} + +U_CAPI int32_t U_EXPORT2 +ucptrie_internalSmallU8Index(const UCPTrie *trie, int32_t lt1, uint8_t t2, uint8_t t3) { + UChar32 c = (lt1 << 12) | (t2 << 6) | t3; + if (c >= trie->highStart) { + // Possible because the UTF-8 macro compares with shifted12HighStart which may be higher. + return trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET; + } + return ucptrie_internalSmallIndex(trie, c); +} + +U_CAPI int32_t U_EXPORT2 +ucptrie_internalU8PrevIndex(const UCPTrie *trie, UChar32 c, + const uint8_t *start, const uint8_t *src) { + int32_t i, length; + // Support 64-bit pointers by avoiding cast of arbitrary difference. + if ((src - start) <= 7) { + i = length = (int32_t)(src - start); + } else { + i = length = 7; + start = src - 7; + } + c = utf8_prevCharSafeBody(start, 0, &i, c, -1); + i = length - i; // Number of bytes read backward from src. + int32_t idx = _UCPTRIE_CP_INDEX(trie, 0xffff, c); + return (idx << 3) | i; +} + +namespace { + +inline uint32_t getValue(UCPTrieData data, UCPTrieValueWidth valueWidth, int32_t dataIndex) { + switch (valueWidth) { + case UCPTRIE_VALUE_BITS_16: + return data.ptr16[dataIndex]; + case UCPTRIE_VALUE_BITS_32: + return data.ptr32[dataIndex]; + case UCPTRIE_VALUE_BITS_8: + return data.ptr8[dataIndex]; + default: + // Unreachable if the trie is properly initialized. + return 0xffffffff; + } +} + +} // namespace + +U_CAPI uint32_t U_EXPORT2 +ucptrie_get(const UCPTrie *trie, UChar32 c) { + int32_t dataIndex; + if ((uint32_t)c <= 0x7f) { + // linear ASCII + dataIndex = c; + } else { + UChar32 fastMax = trie->type == UCPTRIE_TYPE_FAST ? 0xffff : UCPTRIE_SMALL_MAX; + dataIndex = _UCPTRIE_CP_INDEX(trie, fastMax, c); + } + return getValue(trie->data, (UCPTrieValueWidth)trie->valueWidth, dataIndex); +} + +namespace { + +constexpr int32_t MAX_UNICODE = 0x10ffff; + +inline uint32_t maybeFilterValue(uint32_t value, uint32_t trieNullValue, uint32_t nullValue, + UCPMapValueFilter *filter, const void *context) { + if (value == trieNullValue) { + value = nullValue; + } else if (filter != nullptr) { + value = filter(context, value); + } + return value; +} + +UChar32 getRange(const void *t, UChar32 start, + UCPMapValueFilter *filter, const void *context, uint32_t *pValue) { + if ((uint32_t)start > MAX_UNICODE) { + return U_SENTINEL; + } + const UCPTrie *trie = reinterpret_cast(t); + UCPTrieValueWidth valueWidth = (UCPTrieValueWidth)trie->valueWidth; + if (start >= trie->highStart) { + if (pValue != nullptr) { + int32_t di = trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET; + uint32_t value = getValue(trie->data, valueWidth, di); + if (filter != nullptr) { value = filter(context, value); } + *pValue = value; + } + return MAX_UNICODE; + } + + uint32_t nullValue = trie->nullValue; + if (filter != nullptr) { nullValue = filter(context, nullValue); } + const uint16_t *index = trie->index; + + int32_t prevI3Block = -1; + int32_t prevBlock = -1; + UChar32 c = start; + uint32_t trieValue, value = nullValue; + bool haveValue = false; + do { + int32_t i3Block; + int32_t i3; + int32_t i3BlockLength; + int32_t dataBlockLength; + if (c <= 0xffff && (trie->type == UCPTRIE_TYPE_FAST || c <= UCPTRIE_SMALL_MAX)) { + i3Block = 0; + i3 = c >> UCPTRIE_FAST_SHIFT; + i3BlockLength = trie->type == UCPTRIE_TYPE_FAST ? + UCPTRIE_BMP_INDEX_LENGTH : UCPTRIE_SMALL_INDEX_LENGTH; + dataBlockLength = UCPTRIE_FAST_DATA_BLOCK_LENGTH; + } else { + // Use the multi-stage index. + int32_t i1 = c >> UCPTRIE_SHIFT_1; + if (trie->type == UCPTRIE_TYPE_FAST) { + U_ASSERT(0xffff < c && c < trie->highStart); + i1 += UCPTRIE_BMP_INDEX_LENGTH - UCPTRIE_OMITTED_BMP_INDEX_1_LENGTH; + } else { + U_ASSERT(c < trie->highStart && trie->highStart > UCPTRIE_SMALL_LIMIT); + i1 += UCPTRIE_SMALL_INDEX_LENGTH; + } + i3Block = trie->index[ + (int32_t)trie->index[i1] + ((c >> UCPTRIE_SHIFT_2) & UCPTRIE_INDEX_2_MASK)]; + if (i3Block == prevI3Block && (c - start) >= UCPTRIE_CP_PER_INDEX_2_ENTRY) { + // The index-3 block is the same as the previous one, and filled with value. + U_ASSERT((c & (UCPTRIE_CP_PER_INDEX_2_ENTRY - 1)) == 0); + c += UCPTRIE_CP_PER_INDEX_2_ENTRY; + continue; + } + prevI3Block = i3Block; + if (i3Block == trie->index3NullOffset) { + // This is the index-3 null block. + if (haveValue) { + if (nullValue != value) { + return c - 1; + } + } else { + trieValue = trie->nullValue; + value = nullValue; + if (pValue != nullptr) { *pValue = nullValue; } + haveValue = true; + } + prevBlock = trie->dataNullOffset; + c = (c + UCPTRIE_CP_PER_INDEX_2_ENTRY) & ~(UCPTRIE_CP_PER_INDEX_2_ENTRY - 1); + continue; + } + i3 = (c >> UCPTRIE_SHIFT_3) & UCPTRIE_INDEX_3_MASK; + i3BlockLength = UCPTRIE_INDEX_3_BLOCK_LENGTH; + dataBlockLength = UCPTRIE_SMALL_DATA_BLOCK_LENGTH; + } + // Enumerate data blocks for one index-3 block. + do { + int32_t block; + if ((i3Block & 0x8000) == 0) { + block = index[i3Block + i3]; + } else { + // 18-bit indexes stored in groups of 9 entries per 8 indexes. + int32_t group = (i3Block & 0x7fff) + (i3 & ~7) + (i3 >> 3); + int32_t gi = i3 & 7; + block = ((int32_t)index[group++] << (2 + (2 * gi))) & 0x30000; + block |= index[group + gi]; + } + if (block == prevBlock && (c - start) >= dataBlockLength) { + // The block is the same as the previous one, and filled with value. + U_ASSERT((c & (dataBlockLength - 1)) == 0); + c += dataBlockLength; + } else { + int32_t dataMask = dataBlockLength - 1; + prevBlock = block; + if (block == trie->dataNullOffset) { + // This is the data null block. + if (haveValue) { + if (nullValue != value) { + return c - 1; + } + } else { + trieValue = trie->nullValue; + value = nullValue; + if (pValue != nullptr) { *pValue = nullValue; } + haveValue = true; + } + c = (c + dataBlockLength) & ~dataMask; + } else { + int32_t di = block + (c & dataMask); + uint32_t trieValue2 = getValue(trie->data, valueWidth, di); + if (haveValue) { + if (trieValue2 != trieValue) { + if (filter == nullptr || + maybeFilterValue(trieValue2, trie->nullValue, nullValue, + filter, context) != value) { + return c - 1; + } + trieValue = trieValue2; // may or may not help + } + } else { + trieValue = trieValue2; + value = maybeFilterValue(trieValue2, trie->nullValue, nullValue, + filter, context); + if (pValue != nullptr) { *pValue = value; } + haveValue = true; + } + while ((++c & dataMask) != 0) { + trieValue2 = getValue(trie->data, valueWidth, ++di); + if (trieValue2 != trieValue) { + if (filter == nullptr || + maybeFilterValue(trieValue2, trie->nullValue, nullValue, + filter, context) != value) { + return c - 1; + } + trieValue = trieValue2; // may or may not help + } + } + } + } + } while (++i3 < i3BlockLength); + } while (c < trie->highStart); + U_ASSERT(haveValue); + int32_t di = trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET; + uint32_t highValue = getValue(trie->data, valueWidth, di); + if (maybeFilterValue(highValue, trie->nullValue, nullValue, + filter, context) != value) { + return c - 1; + } else { + return MAX_UNICODE; + } +} + +} // namespace + +U_CFUNC UChar32 +ucptrie_internalGetRange(UCPTrieGetRange *getRange, + const void *trie, UChar32 start, + UCPMapRangeOption option, uint32_t surrogateValue, + UCPMapValueFilter *filter, const void *context, uint32_t *pValue) { + if (option == UCPMAP_RANGE_NORMAL) { + return getRange(trie, start, filter, context, pValue); + } + uint32_t value; + if (pValue == nullptr) { + // We need to examine the range value even if the caller does not want it. + pValue = &value; + } + UChar32 surrEnd = option == UCPMAP_RANGE_FIXED_ALL_SURROGATES ? 0xdfff : 0xdbff; + UChar32 end = getRange(trie, start, filter, context, pValue); + if (end < 0xd7ff || start > surrEnd) { + return end; + } + // The range overlaps with surrogates, or ends just before the first one. + if (*pValue == surrogateValue) { + if (end >= surrEnd) { + // Surrogates followed by a non-surrogateValue range, + // or surrogates are part of a larger surrogateValue range. + return end; + } + } else { + if (start <= 0xd7ff) { + return 0xd7ff; // Non-surrogateValue range ends before surrogateValue surrogates. + } + // Start is a surrogate with a non-surrogateValue code *unit* value. + // Return a surrogateValue code *point* range. + *pValue = surrogateValue; + if (end > surrEnd) { + return surrEnd; // Surrogate range ends before non-surrogateValue rest of range. + } + } + // See if the surrogateValue surrogate range can be merged with + // an immediately following range. + uint32_t value2; + UChar32 end2 = getRange(trie, surrEnd + 1, filter, context, &value2); + if (value2 == surrogateValue) { + return end2; + } + return surrEnd; +} + +U_CAPI UChar32 U_EXPORT2 +ucptrie_getRange(const UCPTrie *trie, UChar32 start, + UCPMapRangeOption option, uint32_t surrogateValue, + UCPMapValueFilter *filter, const void *context, uint32_t *pValue) { + return ucptrie_internalGetRange(getRange, trie, start, + option, surrogateValue, + filter, context, pValue); +} + +U_CAPI int32_t U_EXPORT2 +ucptrie_toBinary(const UCPTrie *trie, + void *data, int32_t capacity, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return 0; + } + + UCPTrieType type = (UCPTrieType)trie->type; + UCPTrieValueWidth valueWidth = (UCPTrieValueWidth)trie->valueWidth; + if (type < UCPTRIE_TYPE_FAST || UCPTRIE_TYPE_SMALL < type || + valueWidth < UCPTRIE_VALUE_BITS_16 || UCPTRIE_VALUE_BITS_8 < valueWidth || + capacity < 0 || + (capacity > 0 && (data == nullptr || (U_POINTER_MASK_LSB(data, 3) != 0)))) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + int32_t length = (int32_t)sizeof(UCPTrieHeader) + trie->indexLength * 2; + switch (valueWidth) { + case UCPTRIE_VALUE_BITS_16: + length += trie->dataLength * 2; + break; + case UCPTRIE_VALUE_BITS_32: + length += trie->dataLength * 4; + break; + case UCPTRIE_VALUE_BITS_8: + length += trie->dataLength; + break; + default: + // unreachable + break; + } + if (capacity < length) { + *pErrorCode = U_BUFFER_OVERFLOW_ERROR; + return length; + } + + char *bytes = (char *)data; + UCPTrieHeader *header = (UCPTrieHeader *)bytes; + header->signature = UCPTRIE_SIG; // "Tri3" + header->options = (uint16_t)( + ((trie->dataLength & 0xf0000) >> 4) | + ((trie->dataNullOffset & 0xf0000) >> 8) | + (trie->type << 6) | + valueWidth); + header->indexLength = (uint16_t)trie->indexLength; + header->dataLength = (uint16_t)trie->dataLength; + header->index3NullOffset = trie->index3NullOffset; + header->dataNullOffset = (uint16_t)trie->dataNullOffset; + header->shiftedHighStart = trie->highStart >> UCPTRIE_SHIFT_2; + bytes += sizeof(UCPTrieHeader); + + uprv_memcpy(bytes, trie->index, trie->indexLength * 2); + bytes += trie->indexLength * 2; + + switch (valueWidth) { + case UCPTRIE_VALUE_BITS_16: + uprv_memcpy(bytes, trie->data.ptr16, trie->dataLength * 2); + break; + case UCPTRIE_VALUE_BITS_32: + uprv_memcpy(bytes, trie->data.ptr32, trie->dataLength * 4); + break; + case UCPTRIE_VALUE_BITS_8: + uprv_memcpy(bytes, trie->data.ptr8, trie->dataLength); + break; + default: + // unreachable + break; + } + return length; +} + +namespace { + +#ifdef UCPTRIE_DEBUG +long countNull(const UCPTrie *trie) { + uint32_t nullValue=trie->nullValue; + int32_t length=trie->dataLength; + long count=0; + switch (trie->valueWidth) { + case UCPTRIE_VALUE_BITS_16: + for(int32_t i=0; idata.ptr16[i]==nullValue) { ++count; } + } + break; + case UCPTRIE_VALUE_BITS_32: + for(int32_t i=0; idata.ptr32[i]==nullValue) { ++count; } + } + break; + case UCPTRIE_VALUE_BITS_8: + for(int32_t i=0; idata.ptr8[i]==nullValue) { ++count; } + } + break; + default: + // unreachable + break; + } + return count; +} + +U_CFUNC void +ucptrie_printLengths(const UCPTrie *trie, const char *which) { + long indexLength=trie->indexLength; + long dataLength=(long)trie->dataLength; + long totalLength=(long)sizeof(UCPTrieHeader)+indexLength*2+ + dataLength*(trie->valueWidth==UCPTRIE_VALUE_BITS_16 ? 2 : + trie->valueWidth==UCPTRIE_VALUE_BITS_32 ? 4 : 1); + printf("**UCPTrieLengths(%s %s)** index:%6ld data:%6ld countNull:%6ld serialized:%6ld\n", + which, trie->name, indexLength, dataLength, countNull(trie), totalLength); +} +#endif + +} // namespace + +// UCPMap ---- +// Initially, this is the same as UCPTrie. This may well change. + +U_CAPI uint32_t U_EXPORT2 +ucpmap_get(const UCPMap *map, UChar32 c) { + return ucptrie_get(reinterpret_cast(map), c); +} + +U_CAPI UChar32 U_EXPORT2 +ucpmap_getRange(const UCPMap *map, UChar32 start, + UCPMapRangeOption option, uint32_t surrogateValue, + UCPMapValueFilter *filter, const void *context, uint32_t *pValue) { + return ucptrie_getRange(reinterpret_cast(map), start, + option, surrogateValue, + filter, context, pValue); +} diff --git a/deps/icu-small/source/common/ucptrie_impl.h b/deps/icu-small/source/common/ucptrie_impl.h index a7a80a8f08a86f..3e3a5cf7586262 100644 --- a/deps/icu-small/source/common/ucptrie_impl.h +++ b/deps/icu-small/source/common/ucptrie_impl.h @@ -1,285 +1,285 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// ucptrie_impl.h (modified from utrie2_impl.h) -// created: 2017dec29 Markus W. Scherer - -#ifndef __UCPTRIE_IMPL_H__ -#define __UCPTRIE_IMPL_H__ - -#include "unicode/ucptrie.h" -#ifdef UCPTRIE_DEBUG -#include "unicode/umutablecptrie.h" -#endif - -// UCPTrie signature values, in platform endianness and opposite endianness. -// The UCPTrie signature ASCII byte values spell "Tri3". -#define UCPTRIE_SIG 0x54726933 -#define UCPTRIE_OE_SIG 0x33697254 - -/** - * Header data for the binary, memory-mappable representation of a UCPTrie/CodePointTrie. - * @internal - */ -struct UCPTrieHeader { - /** "Tri3" in big-endian US-ASCII (0x54726933) */ - uint32_t signature; - - /** - * Options bit field: - * Bits 15..12: Data length bits 19..16. - * Bits 11..8: Data null block offset bits 19..16. - * Bits 7..6: UCPTrieType - * Bits 5..3: Reserved (0). - * Bits 2..0: UCPTrieValueWidth - */ - uint16_t options; - - /** Total length of the index tables. */ - uint16_t indexLength; - - /** Data length bits 15..0. */ - uint16_t dataLength; - - /** Index-3 null block offset, 0x7fff or 0xffff if none. */ - uint16_t index3NullOffset; - - /** Data null block offset bits 15..0, 0xfffff if none. */ - uint16_t dataNullOffset; - - /** - * First code point of the single-value range ending with U+10ffff, - * rounded up and then shifted right by UCPTRIE_SHIFT_2. - */ - uint16_t shiftedHighStart; -}; - -// Constants for use with UCPTrieHeader.options. -constexpr uint16_t UCPTRIE_OPTIONS_DATA_LENGTH_MASK = 0xf000; -constexpr uint16_t UCPTRIE_OPTIONS_DATA_NULL_OFFSET_MASK = 0xf00; -constexpr uint16_t UCPTRIE_OPTIONS_RESERVED_MASK = 0x38; -constexpr uint16_t UCPTRIE_OPTIONS_VALUE_BITS_MASK = 7; - -/** - * Value for index3NullOffset which indicates that there is no index-3 null block. - * Bit 15 is unused for this value because this bit is used if the index-3 contains - * 18-bit indexes. - */ -constexpr int32_t UCPTRIE_NO_INDEX3_NULL_OFFSET = 0x7fff; -constexpr int32_t UCPTRIE_NO_DATA_NULL_OFFSET = 0xfffff; - -// Internal constants. - -/** The length of the BMP index table. 1024=0x400 */ -constexpr int32_t UCPTRIE_BMP_INDEX_LENGTH = 0x10000 >> UCPTRIE_FAST_SHIFT; - -constexpr int32_t UCPTRIE_SMALL_LIMIT = 0x1000; -constexpr int32_t UCPTRIE_SMALL_INDEX_LENGTH = UCPTRIE_SMALL_LIMIT >> UCPTRIE_FAST_SHIFT; - -/** Shift size for getting the index-3 table offset. */ -constexpr int32_t UCPTRIE_SHIFT_3 = 4; - -/** Shift size for getting the index-2 table offset. */ -constexpr int32_t UCPTRIE_SHIFT_2 = 5 + UCPTRIE_SHIFT_3; - -/** Shift size for getting the index-1 table offset. */ -constexpr int32_t UCPTRIE_SHIFT_1 = 5 + UCPTRIE_SHIFT_2; - -/** - * Difference between two shift sizes, - * for getting an index-2 offset from an index-3 offset. 5=9-4 - */ -constexpr int32_t UCPTRIE_SHIFT_2_3 = UCPTRIE_SHIFT_2 - UCPTRIE_SHIFT_3; - -/** - * Difference between two shift sizes, - * for getting an index-1 offset from an index-2 offset. 5=14-9 - */ -constexpr int32_t UCPTRIE_SHIFT_1_2 = UCPTRIE_SHIFT_1 - UCPTRIE_SHIFT_2; - -/** - * Number of index-1 entries for the BMP. (4) - * This part of the index-1 table is omitted from the serialized form. - */ -constexpr int32_t UCPTRIE_OMITTED_BMP_INDEX_1_LENGTH = 0x10000 >> UCPTRIE_SHIFT_1; - -/** Number of entries in an index-2 block. 32=0x20 */ -constexpr int32_t UCPTRIE_INDEX_2_BLOCK_LENGTH = 1 << UCPTRIE_SHIFT_1_2; - -/** Mask for getting the lower bits for the in-index-2-block offset. */ -constexpr int32_t UCPTRIE_INDEX_2_MASK = UCPTRIE_INDEX_2_BLOCK_LENGTH - 1; - -/** Number of code points per index-2 table entry. 512=0x200 */ -constexpr int32_t UCPTRIE_CP_PER_INDEX_2_ENTRY = 1 << UCPTRIE_SHIFT_2; - -/** Number of entries in an index-3 block. 32=0x20 */ -constexpr int32_t UCPTRIE_INDEX_3_BLOCK_LENGTH = 1 << UCPTRIE_SHIFT_2_3; - -/** Mask for getting the lower bits for the in-index-3-block offset. */ -constexpr int32_t UCPTRIE_INDEX_3_MASK = UCPTRIE_INDEX_3_BLOCK_LENGTH - 1; - -/** Number of entries in a small data block. 16=0x10 */ -constexpr int32_t UCPTRIE_SMALL_DATA_BLOCK_LENGTH = 1 << UCPTRIE_SHIFT_3; - -/** Mask for getting the lower bits for the in-small-data-block offset. */ -constexpr int32_t UCPTRIE_SMALL_DATA_MASK = UCPTRIE_SMALL_DATA_BLOCK_LENGTH - 1; - - -typedef UChar32 -UCPTrieGetRange(const void *trie, UChar32 start, - UCPMapValueFilter *filter, const void *context, uint32_t *pValue); - -U_CFUNC UChar32 -ucptrie_internalGetRange(UCPTrieGetRange *getRange, - const void *trie, UChar32 start, - UCPMapRangeOption option, uint32_t surrogateValue, - UCPMapValueFilter *filter, const void *context, uint32_t *pValue); - -#ifdef UCPTRIE_DEBUG -U_CFUNC void -ucptrie_printLengths(const UCPTrie *trie, const char *which); - -U_CFUNC void umutablecptrie_setName(UMutableCPTrie *builder, const char *name); -#endif - -/* - * Format of the binary, memory-mappable representation of a UCPTrie/CodePointTrie. - * For overview information see https://icu.unicode.org/design/struct/utrie - * - * The binary trie data should be 32-bit-aligned. - * The overall layout is: - * - * UCPTrieHeader header; -- 16 bytes, see struct definition above - * uint16_t index[header.indexLength]; - * uintXY_t data[header.dataLength]; - * - * The trie data array is an array of uint16_t, uint32_t, or uint8_t, - * specified via the UCPTrieValueWidth when building the trie. - * The data array is 32-bit-aligned for uint32_t, otherwise 16-bit-aligned. - * The overall length of the trie data is a multiple of 4 bytes. - * (Padding is added at the end of the index array and/or near the end of the data array as needed.) - * - * The length of the data array (dataLength) is stored as an integer split across two fields - * of the header struct (high bits in header.options). - * - * The trie type can be "fast" or "small" which determines the index structure, - * specified via the UCPTrieType when building the trie. - * - * The type and valueWidth are stored in the header.options. - * There are reserved type and valueWidth values, and reserved header.options bits. - * They could be used in future format extensions. - * Code reading the trie structure must fail with an error when unknown values or options are set. - * - * Values for ASCII character (U+0000..U+007F) can always be found at the start of the data array. - * - * Values for code points below a type-specific fast-indexing limit are found via two-stage lookup. - * For a "fast" trie, the limit is the BMP/supplementary boundary at U+10000. - * For a "small" trie, the limit is UCPTRIE_SMALL_MAX+1=U+1000. - * - * All code points in the range highStart..U+10FFFF map to a single highValue - * which is stored at the second-to-last position of the data array. - * (See UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET.) - * The highStart value is header.shiftedHighStart<>UCPTRIE_SHIFT_1. - * (For 0x100000 supplementary code points U+10000..U+10ffff.) - * - * After this index-1 table follow the variable-length index-3 and index-2 tables. - * - * The supplementary index tables are omitted completely - * if there is only BMP data (highStart<=U+10000). - * - * For a "small" trie: - * - * The index array starts with a fast-index table for lookup of code points U+0000..U+0FFF. - * - * The "supplementary" index tables are always stored. - * The index-1 table starts from U+0000, its maximum length is 68=0x44=0x110000>>UCPTRIE_SHIFT_1. - * - * For both trie types: - * - * The last index-2 block may be a partial block, storing indexes only for code points - * below highStart. - * - * Lookup for ASCII code point c: - * - * Linear access from the start of the data array. - * - * value = data[c]; - * - * Lookup for fast-range code point c: - * - * Shift the code point right by UCPTRIE_FAST_SHIFT=6 bits, - * fetch the index array value at that offset, - * add the lower code point bits, index into the data array. - * - * value = data[index[c>>6] + (c&0x3f)]; - * - * (This works for ASCII as well.) - * - * Lookup for small-range code point c below highStart: - * - * Split the code point into four bit fields using several sets of shifts & masks - * to read consecutive values from the index-1, index-2, index-3 and data tables. - * - * If all of the data block offsets in an index-3 block fit within 16 bits (up to 0xffff), - * then the data block offsets are stored directly as uint16_t. - * - * Otherwise (this is very unusual but possible), the index-2 entry for the index-3 block - * has bit 15 (0x8000) set, and each set of 8 index-3 entries is preceded by - * an additional uint16_t word. Data block offsets are 18 bits wide, with the top 2 bits stored - * in the additional word. - * - * See ucptrie_internalSmallIndex() for details. - * - * (In a "small" trie, this works for ASCII and below-fast_limit code points as well.) - * - * Compaction: - * - * Multiple code point ranges ("blocks") that are aligned on certain boundaries - * (determined by the shifting/bit fields of code points) and - * map to the same data values normally share a single subsequence of the data array. - * Data blocks can also overlap partially. - * (Depending on the builder code finding duplicate and overlapping blocks.) - * - * Iteration over same-value ranges: - * - * Range iteration (ucptrie_getRange()) walks the structure from a start code point - * until some code point is found that maps to a different value; - * the end of the returned range is just before that. - * - * The header.dataNullOffset (split across two header fields, high bits in header.options) - * is the offset of a widely shared data block filled with one single value. - * It helps quickly skip over large ranges of data with that value. - * The builder must ensure that if the start of any data block (fast or small) - * matches the dataNullOffset, then the whole block must be filled with the null value. - * Special care must be taken if there is no fast null data block - * but a small one, which is shorter, and it matches the *start* of some fast data block. - * - * Similarly, the header.index3NullOffset is the index-array offset of an index-3 block - * where all index entries point to the dataNullOffset. - * If there is no such data or index-3 block, then these offsets are set to - * values that cannot be reached (data offset out of range/reserved index offset), - * normally UCPTRIE_NO_DATA_NULL_OFFSET or UCPTRIE_NO_INDEX3_NULL_OFFSET respectively. - */ - -#endif +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// ucptrie_impl.h (modified from utrie2_impl.h) +// created: 2017dec29 Markus W. Scherer + +#ifndef __UCPTRIE_IMPL_H__ +#define __UCPTRIE_IMPL_H__ + +#include "unicode/ucptrie.h" +#ifdef UCPTRIE_DEBUG +#include "unicode/umutablecptrie.h" +#endif + +// UCPTrie signature values, in platform endianness and opposite endianness. +// The UCPTrie signature ASCII byte values spell "Tri3". +#define UCPTRIE_SIG 0x54726933 +#define UCPTRIE_OE_SIG 0x33697254 + +/** + * Header data for the binary, memory-mappable representation of a UCPTrie/CodePointTrie. + * @internal + */ +struct UCPTrieHeader { + /** "Tri3" in big-endian US-ASCII (0x54726933) */ + uint32_t signature; + + /** + * Options bit field: + * Bits 15..12: Data length bits 19..16. + * Bits 11..8: Data null block offset bits 19..16. + * Bits 7..6: UCPTrieType + * Bits 5..3: Reserved (0). + * Bits 2..0: UCPTrieValueWidth + */ + uint16_t options; + + /** Total length of the index tables. */ + uint16_t indexLength; + + /** Data length bits 15..0. */ + uint16_t dataLength; + + /** Index-3 null block offset, 0x7fff or 0xffff if none. */ + uint16_t index3NullOffset; + + /** Data null block offset bits 15..0, 0xfffff if none. */ + uint16_t dataNullOffset; + + /** + * First code point of the single-value range ending with U+10ffff, + * rounded up and then shifted right by UCPTRIE_SHIFT_2. + */ + uint16_t shiftedHighStart; +}; + +// Constants for use with UCPTrieHeader.options. +constexpr uint16_t UCPTRIE_OPTIONS_DATA_LENGTH_MASK = 0xf000; +constexpr uint16_t UCPTRIE_OPTIONS_DATA_NULL_OFFSET_MASK = 0xf00; +constexpr uint16_t UCPTRIE_OPTIONS_RESERVED_MASK = 0x38; +constexpr uint16_t UCPTRIE_OPTIONS_VALUE_BITS_MASK = 7; + +/** + * Value for index3NullOffset which indicates that there is no index-3 null block. + * Bit 15 is unused for this value because this bit is used if the index-3 contains + * 18-bit indexes. + */ +constexpr int32_t UCPTRIE_NO_INDEX3_NULL_OFFSET = 0x7fff; +constexpr int32_t UCPTRIE_NO_DATA_NULL_OFFSET = 0xfffff; + +// Internal constants. + +/** The length of the BMP index table. 1024=0x400 */ +constexpr int32_t UCPTRIE_BMP_INDEX_LENGTH = 0x10000 >> UCPTRIE_FAST_SHIFT; + +constexpr int32_t UCPTRIE_SMALL_LIMIT = 0x1000; +constexpr int32_t UCPTRIE_SMALL_INDEX_LENGTH = UCPTRIE_SMALL_LIMIT >> UCPTRIE_FAST_SHIFT; + +/** Shift size for getting the index-3 table offset. */ +constexpr int32_t UCPTRIE_SHIFT_3 = 4; + +/** Shift size for getting the index-2 table offset. */ +constexpr int32_t UCPTRIE_SHIFT_2 = 5 + UCPTRIE_SHIFT_3; + +/** Shift size for getting the index-1 table offset. */ +constexpr int32_t UCPTRIE_SHIFT_1 = 5 + UCPTRIE_SHIFT_2; + +/** + * Difference between two shift sizes, + * for getting an index-2 offset from an index-3 offset. 5=9-4 + */ +constexpr int32_t UCPTRIE_SHIFT_2_3 = UCPTRIE_SHIFT_2 - UCPTRIE_SHIFT_3; + +/** + * Difference between two shift sizes, + * for getting an index-1 offset from an index-2 offset. 5=14-9 + */ +constexpr int32_t UCPTRIE_SHIFT_1_2 = UCPTRIE_SHIFT_1 - UCPTRIE_SHIFT_2; + +/** + * Number of index-1 entries for the BMP. (4) + * This part of the index-1 table is omitted from the serialized form. + */ +constexpr int32_t UCPTRIE_OMITTED_BMP_INDEX_1_LENGTH = 0x10000 >> UCPTRIE_SHIFT_1; + +/** Number of entries in an index-2 block. 32=0x20 */ +constexpr int32_t UCPTRIE_INDEX_2_BLOCK_LENGTH = 1 << UCPTRIE_SHIFT_1_2; + +/** Mask for getting the lower bits for the in-index-2-block offset. */ +constexpr int32_t UCPTRIE_INDEX_2_MASK = UCPTRIE_INDEX_2_BLOCK_LENGTH - 1; + +/** Number of code points per index-2 table entry. 512=0x200 */ +constexpr int32_t UCPTRIE_CP_PER_INDEX_2_ENTRY = 1 << UCPTRIE_SHIFT_2; + +/** Number of entries in an index-3 block. 32=0x20 */ +constexpr int32_t UCPTRIE_INDEX_3_BLOCK_LENGTH = 1 << UCPTRIE_SHIFT_2_3; + +/** Mask for getting the lower bits for the in-index-3-block offset. */ +constexpr int32_t UCPTRIE_INDEX_3_MASK = UCPTRIE_INDEX_3_BLOCK_LENGTH - 1; + +/** Number of entries in a small data block. 16=0x10 */ +constexpr int32_t UCPTRIE_SMALL_DATA_BLOCK_LENGTH = 1 << UCPTRIE_SHIFT_3; + +/** Mask for getting the lower bits for the in-small-data-block offset. */ +constexpr int32_t UCPTRIE_SMALL_DATA_MASK = UCPTRIE_SMALL_DATA_BLOCK_LENGTH - 1; + + +typedef UChar32 +UCPTrieGetRange(const void *trie, UChar32 start, + UCPMapValueFilter *filter, const void *context, uint32_t *pValue); + +U_CFUNC UChar32 +ucptrie_internalGetRange(UCPTrieGetRange *getRange, + const void *trie, UChar32 start, + UCPMapRangeOption option, uint32_t surrogateValue, + UCPMapValueFilter *filter, const void *context, uint32_t *pValue); + +#ifdef UCPTRIE_DEBUG +U_CFUNC void +ucptrie_printLengths(const UCPTrie *trie, const char *which); + +U_CFUNC void umutablecptrie_setName(UMutableCPTrie *builder, const char *name); +#endif + +/* + * Format of the binary, memory-mappable representation of a UCPTrie/CodePointTrie. + * For overview information see https://icu.unicode.org/design/struct/utrie + * + * The binary trie data should be 32-bit-aligned. + * The overall layout is: + * + * UCPTrieHeader header; -- 16 bytes, see struct definition above + * uint16_t index[header.indexLength]; + * uintXY_t data[header.dataLength]; + * + * The trie data array is an array of uint16_t, uint32_t, or uint8_t, + * specified via the UCPTrieValueWidth when building the trie. + * The data array is 32-bit-aligned for uint32_t, otherwise 16-bit-aligned. + * The overall length of the trie data is a multiple of 4 bytes. + * (Padding is added at the end of the index array and/or near the end of the data array as needed.) + * + * The length of the data array (dataLength) is stored as an integer split across two fields + * of the header struct (high bits in header.options). + * + * The trie type can be "fast" or "small" which determines the index structure, + * specified via the UCPTrieType when building the trie. + * + * The type and valueWidth are stored in the header.options. + * There are reserved type and valueWidth values, and reserved header.options bits. + * They could be used in future format extensions. + * Code reading the trie structure must fail with an error when unknown values or options are set. + * + * Values for ASCII character (U+0000..U+007F) can always be found at the start of the data array. + * + * Values for code points below a type-specific fast-indexing limit are found via two-stage lookup. + * For a "fast" trie, the limit is the BMP/supplementary boundary at U+10000. + * For a "small" trie, the limit is UCPTRIE_SMALL_MAX+1=U+1000. + * + * All code points in the range highStart..U+10FFFF map to a single highValue + * which is stored at the second-to-last position of the data array. + * (See UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET.) + * The highStart value is header.shiftedHighStart<>UCPTRIE_SHIFT_1. + * (For 0x100000 supplementary code points U+10000..U+10ffff.) + * + * After this index-1 table follow the variable-length index-3 and index-2 tables. + * + * The supplementary index tables are omitted completely + * if there is only BMP data (highStart<=U+10000). + * + * For a "small" trie: + * + * The index array starts with a fast-index table for lookup of code points U+0000..U+0FFF. + * + * The "supplementary" index tables are always stored. + * The index-1 table starts from U+0000, its maximum length is 68=0x44=0x110000>>UCPTRIE_SHIFT_1. + * + * For both trie types: + * + * The last index-2 block may be a partial block, storing indexes only for code points + * below highStart. + * + * Lookup for ASCII code point c: + * + * Linear access from the start of the data array. + * + * value = data[c]; + * + * Lookup for fast-range code point c: + * + * Shift the code point right by UCPTRIE_FAST_SHIFT=6 bits, + * fetch the index array value at that offset, + * add the lower code point bits, index into the data array. + * + * value = data[index[c>>6] + (c&0x3f)]; + * + * (This works for ASCII as well.) + * + * Lookup for small-range code point c below highStart: + * + * Split the code point into four bit fields using several sets of shifts & masks + * to read consecutive values from the index-1, index-2, index-3 and data tables. + * + * If all of the data block offsets in an index-3 block fit within 16 bits (up to 0xffff), + * then the data block offsets are stored directly as uint16_t. + * + * Otherwise (this is very unusual but possible), the index-2 entry for the index-3 block + * has bit 15 (0x8000) set, and each set of 8 index-3 entries is preceded by + * an additional uint16_t word. Data block offsets are 18 bits wide, with the top 2 bits stored + * in the additional word. + * + * See ucptrie_internalSmallIndex() for details. + * + * (In a "small" trie, this works for ASCII and below-fast_limit code points as well.) + * + * Compaction: + * + * Multiple code point ranges ("blocks") that are aligned on certain boundaries + * (determined by the shifting/bit fields of code points) and + * map to the same data values normally share a single subsequence of the data array. + * Data blocks can also overlap partially. + * (Depending on the builder code finding duplicate and overlapping blocks.) + * + * Iteration over same-value ranges: + * + * Range iteration (ucptrie_getRange()) walks the structure from a start code point + * until some code point is found that maps to a different value; + * the end of the returned range is just before that. + * + * The header.dataNullOffset (split across two header fields, high bits in header.options) + * is the offset of a widely shared data block filled with one single value. + * It helps quickly skip over large ranges of data with that value. + * The builder must ensure that if the start of any data block (fast or small) + * matches the dataNullOffset, then the whole block must be filled with the null value. + * Special care must be taken if there is no fast null data block + * but a small one, which is shorter, and it matches the *start* of some fast data block. + * + * Similarly, the header.index3NullOffset is the index-array offset of an index-3 block + * where all index entries point to the dataNullOffset. + * If there is no such data or index-3 block, then these offsets are set to + * values that cannot be reached (data offset out of range/reserved index offset), + * normally UCPTRIE_NO_DATA_NULL_OFFSET or UCPTRIE_NO_INDEX3_NULL_OFFSET respectively. + */ + +#endif diff --git a/deps/icu-small/source/common/ucurr.cpp b/deps/icu-small/source/common/ucurr.cpp index 928d049fb55e73..61801735007b64 100644 --- a/deps/icu-small/source/common/ucurr.cpp +++ b/deps/icu-small/source/common/ucurr.cpp @@ -1,2727 +1,2727 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/ucurr.h" -#include "unicode/locid.h" -#include "unicode/ures.h" -#include "unicode/ustring.h" -#include "unicode/parsepos.h" -#include "unicode/uniset.h" -#include "unicode/usetiter.h" -#include "unicode/utf16.h" -#include "ustr_imp.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "static_unicode_sets.h" -#include "uassert.h" -#include "umutex.h" -#include "ucln_cmn.h" -#include "uenumimp.h" -#include "uhash.h" -#include "hash.h" -#include "uinvchar.h" -#include "uresimp.h" -#include "ulist.h" -#include "uresimp.h" -#include "ureslocs.h" -#include "ulocimp.h" - -using namespace icu; - -//#define UCURR_DEBUG_EQUIV 1 -#ifdef UCURR_DEBUG_EQUIV -#include "stdio.h" -#endif -//#define UCURR_DEBUG 1 -#ifdef UCURR_DEBUG -#include "stdio.h" -#endif - -typedef struct IsoCodeEntry { - const UChar *isoCode; /* const because it's a reference to a resource bundle string. */ - UDate from; - UDate to; -} IsoCodeEntry; - -//------------------------------------------------------------ -// Constants - -// Default currency meta data of last resort. We try to use the -// defaults encoded in the meta data resource bundle. If there is a -// configuration/build error and these are not available, we use these -// hard-coded defaults (which should be identical). -static const int32_t LAST_RESORT_DATA[] = { 2, 0, 2, 0 }; - -// POW10[i] = 10^i, i=0..MAX_POW10 -static const int32_t POW10[] = { 1, 10, 100, 1000, 10000, 100000, - 1000000, 10000000, 100000000, 1000000000 }; - -static const int32_t MAX_POW10 = UPRV_LENGTHOF(POW10) - 1; - -#define ISO_CURRENCY_CODE_LENGTH 3 - -//------------------------------------------------------------ -// Resource tags -// - -static const char CURRENCY_DATA[] = "supplementalData"; -// Tag for meta-data, in root. -static const char CURRENCY_META[] = "CurrencyMeta"; - -// Tag for map from countries to currencies, in root. -static const char CURRENCY_MAP[] = "CurrencyMap"; - -// Tag for default meta-data, in CURRENCY_META -static const char DEFAULT_META[] = "DEFAULT"; - -// Variant delimiter -static const char VAR_DELIM = '_'; - -// Tag for localized display names (symbols) of currencies -static const char CURRENCIES[] = "Currencies"; -static const char CURRENCIES_NARROW[] = "Currencies%narrow"; -static const char CURRENCIES_FORMAL[] = "Currencies%formal"; -static const char CURRENCIES_VARIANT[] = "Currencies%variant"; -static const char CURRENCYPLURALS[] = "CurrencyPlurals"; - -// ISO codes mapping table -static const UHashtable* gIsoCodes = NULL; -static icu::UInitOnce gIsoCodesInitOnce {}; - -// Currency symbol equivalances -static const icu::Hashtable* gCurrSymbolsEquiv = NULL; -static icu::UInitOnce gCurrSymbolsEquivInitOnce {}; - -U_NAMESPACE_BEGIN - -// EquivIterator iterates over all strings that are equivalent to a given -// string, s. Note that EquivIterator will never yield s itself. -class EquivIterator : public icu::UMemory { -public: - // Constructor. hash stores the equivalence relationships; s is the string - // for which we find equivalent strings. - inline EquivIterator(const icu::Hashtable& hash, const icu::UnicodeString& s) - : _hash(hash) { - _start = _current = &s; - } - inline ~EquivIterator() { } - - // next returns the next equivalent string or NULL if there are no more. - // If s has no equivalent strings, next returns NULL on the first call. - const icu::UnicodeString *next(); -private: - const icu::Hashtable& _hash; - const icu::UnicodeString* _start; - const icu::UnicodeString* _current; -}; - -const icu::UnicodeString * -EquivIterator::next() { - const icu::UnicodeString* _next = (const icu::UnicodeString*) _hash.get(*_current); - if (_next == NULL) { - U_ASSERT(_current == _start); - return NULL; - } - if (*_next == *_start) { - return NULL; - } - _current = _next; - return _next; -} - -U_NAMESPACE_END - -// makeEquivalent makes lhs and rhs equivalent by updating the equivalence -// relations in hash accordingly. -static void makeEquivalent( - const icu::UnicodeString &lhs, - const icu::UnicodeString &rhs, - icu::Hashtable* hash, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if (lhs == rhs) { - // already equivalent - return; - } - icu::EquivIterator leftIter(*hash, lhs); - icu::EquivIterator rightIter(*hash, rhs); - const icu::UnicodeString *firstLeft = leftIter.next(); - const icu::UnicodeString *firstRight = rightIter.next(); - const icu::UnicodeString *nextLeft = firstLeft; - const icu::UnicodeString *nextRight = firstRight; - while (nextLeft != NULL && nextRight != NULL) { - if (*nextLeft == rhs || *nextRight == lhs) { - // Already equivalent - return; - } - nextLeft = leftIter.next(); - nextRight = rightIter.next(); - } - // Not equivalent. Must join. - icu::UnicodeString *newFirstLeft; - icu::UnicodeString *newFirstRight; - if (firstRight == NULL && firstLeft == NULL) { - // Neither lhs or rhs belong to an equivalence circle, so we form - // a new equivalnce circle of just lhs and rhs. - newFirstLeft = new icu::UnicodeString(rhs); - newFirstRight = new icu::UnicodeString(lhs); - } else if (firstRight == NULL) { - // lhs belongs to an equivalence circle, but rhs does not, so we link - // rhs into lhs' circle. - newFirstLeft = new icu::UnicodeString(rhs); - newFirstRight = new icu::UnicodeString(*firstLeft); - } else if (firstLeft == NULL) { - // rhs belongs to an equivlance circle, but lhs does not, so we link - // lhs into rhs' circle. - newFirstLeft = new icu::UnicodeString(*firstRight); - newFirstRight = new icu::UnicodeString(lhs); - } else { - // Both lhs and rhs belong to different equivalnce circles. We link - // them together to form one single, larger equivalnce circle. - newFirstLeft = new icu::UnicodeString(*firstRight); - newFirstRight = new icu::UnicodeString(*firstLeft); - } - if (newFirstLeft == NULL || newFirstRight == NULL) { - delete newFirstLeft; - delete newFirstRight; - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - hash->put(lhs, (void *) newFirstLeft, status); - hash->put(rhs, (void *) newFirstRight, status); -} - -// countEquivalent counts how many strings are equivalent to s. -// hash stores all the equivalnce relations. -// countEquivalent does not include s itself in the count. -static int32_t countEquivalent(const icu::Hashtable &hash, const icu::UnicodeString &s) { - int32_t result = 0; - icu::EquivIterator iter(hash, s); - while (iter.next() != NULL) { - ++result; - } -#ifdef UCURR_DEBUG_EQUIV - { - char tmp[200]; - s.extract(0,s.length(),tmp, "UTF-8"); - printf("CountEquivalent('%s') = %d\n", tmp, result); - } -#endif - return result; -} - -static const icu::Hashtable* getCurrSymbolsEquiv(); - -//------------------------------------------------------------ -// Code - -/** - * Cleanup callback func - */ -static UBool U_CALLCONV -isoCodes_cleanup(void) -{ - if (gIsoCodes != NULL) { - uhash_close(const_cast(gIsoCodes)); - gIsoCodes = NULL; - } - gIsoCodesInitOnce.reset(); - return true; -} - -/** - * Cleanup callback func - */ -static UBool U_CALLCONV -currSymbolsEquiv_cleanup(void) -{ - delete const_cast(gCurrSymbolsEquiv); - gCurrSymbolsEquiv = NULL; - gCurrSymbolsEquivInitOnce.reset(); - return true; -} - -/** - * Deleter for IsoCodeEntry - */ -static void U_CALLCONV -deleteIsoCodeEntry(void *obj) { - IsoCodeEntry *entry = (IsoCodeEntry*)obj; - uprv_free(entry); -} - -/** - * Deleter for gCurrSymbolsEquiv. - */ -static void U_CALLCONV -deleteUnicode(void *obj) { - icu::UnicodeString *entry = (icu::UnicodeString*)obj; - delete entry; -} - -/** - * Unfortunately, we have to convert the UChar* currency code to char* - * to use it as a resource key. - */ -static inline char* -myUCharsToChars(char* resultOfLen4, const UChar* currency) { - u_UCharsToChars(currency, resultOfLen4, ISO_CURRENCY_CODE_LENGTH); - resultOfLen4[ISO_CURRENCY_CODE_LENGTH] = 0; - return resultOfLen4; -} - -/** - * Internal function to look up currency data. Result is an array of - * four integers. The first is the fraction digits. The second is the - * rounding increment, or 0 if none. The rounding increment is in - * units of 10^(-fraction_digits). The third and fourth are the same - * except that they are those used in cash transactions ( cashDigits - * and cashRounding ). - */ -static const int32_t* -_findMetaData(const UChar* currency, UErrorCode& ec) { - - if (currency == 0 || *currency == 0) { - if (U_SUCCESS(ec)) { - ec = U_ILLEGAL_ARGUMENT_ERROR; - } - return LAST_RESORT_DATA; - } - - // Get CurrencyMeta resource out of root locale file. [This may - // move out of the root locale file later; if it does, update this - // code.] - UResourceBundle* currencyData = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &ec); - UResourceBundle* currencyMeta = ures_getByKey(currencyData, CURRENCY_META, currencyData, &ec); - - if (U_FAILURE(ec)) { - ures_close(currencyMeta); - // Config/build error; return hard-coded defaults - return LAST_RESORT_DATA; - } - - // Look up our currency, or if that's not available, then DEFAULT - char buf[ISO_CURRENCY_CODE_LENGTH+1]; - UErrorCode ec2 = U_ZERO_ERROR; // local error code: soft failure - UResourceBundle* rb = ures_getByKey(currencyMeta, myUCharsToChars(buf, currency), NULL, &ec2); - if (U_FAILURE(ec2)) { - ures_close(rb); - rb = ures_getByKey(currencyMeta,DEFAULT_META, NULL, &ec); - if (U_FAILURE(ec)) { - ures_close(currencyMeta); - ures_close(rb); - // Config/build error; return hard-coded defaults - return LAST_RESORT_DATA; - } - } - - int32_t len; - const int32_t *data = ures_getIntVector(rb, &len, &ec); - if (U_FAILURE(ec) || len != 4) { - // Config/build error; return hard-coded defaults - if (U_SUCCESS(ec)) { - ec = U_INVALID_FORMAT_ERROR; - } - ures_close(currencyMeta); - ures_close(rb); - return LAST_RESORT_DATA; - } - - ures_close(currencyMeta); - ures_close(rb); - return data; -} - -// ------------------------------------- - -static void -idForLocale(const char* locale, char* countryAndVariant, int capacity, UErrorCode* ec) -{ - ulocimp_getRegionForSupplementalData(locale, false, countryAndVariant, capacity, ec); -} - -// ------------------------------------------ -// -// Registration -// -//------------------------------------------- - -// don't use ICUService since we don't need fallback - -U_CDECL_BEGIN -static UBool U_CALLCONV currency_cleanup(void); -U_CDECL_END - -#if !UCONFIG_NO_SERVICE -struct CReg; - -static UMutex gCRegLock; -static CReg* gCRegHead = 0; - -struct CReg : public icu::UMemory { - CReg *next; - UChar iso[ISO_CURRENCY_CODE_LENGTH+1]; - char id[ULOC_FULLNAME_CAPACITY]; - - CReg(const UChar* _iso, const char* _id) - : next(0) - { - int32_t len = (int32_t)uprv_strlen(_id); - if (len > (int32_t)(sizeof(id)-1)) { - len = (sizeof(id)-1); - } - uprv_strncpy(id, _id, len); - id[len] = 0; - u_memcpy(iso, _iso, ISO_CURRENCY_CODE_LENGTH); - iso[ISO_CURRENCY_CODE_LENGTH] = 0; - } - - static UCurrRegistryKey reg(const UChar* _iso, const char* _id, UErrorCode* status) - { - if (status && U_SUCCESS(*status) && _iso && _id) { - CReg* n = new CReg(_iso, _id); - if (n) { - umtx_lock(&gCRegLock); - if (!gCRegHead) { - /* register for the first time */ - ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); - } - n->next = gCRegHead; - gCRegHead = n; - umtx_unlock(&gCRegLock); - return n; - } - *status = U_MEMORY_ALLOCATION_ERROR; - } - return 0; - } - - static UBool unreg(UCurrRegistryKey key) { - UBool found = false; - umtx_lock(&gCRegLock); - - CReg** p = &gCRegHead; - while (*p) { - if (*p == key) { - *p = ((CReg*)key)->next; - delete (CReg*)key; - found = true; - break; - } - p = &((*p)->next); - } - - umtx_unlock(&gCRegLock); - return found; - } - - static const UChar* get(const char* id) { - const UChar* result = NULL; - umtx_lock(&gCRegLock); - CReg* p = gCRegHead; - - /* register cleanup of the mutex */ - ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); - while (p) { - if (uprv_strcmp(id, p->id) == 0) { - result = p->iso; - break; - } - p = p->next; - } - umtx_unlock(&gCRegLock); - return result; - } - - /* This doesn't need to be thread safe. It's for u_cleanup only. */ - static void cleanup(void) { - while (gCRegHead) { - CReg* n = gCRegHead; - gCRegHead = gCRegHead->next; - delete n; - } - } -}; - -// ------------------------------------- - -U_CAPI UCurrRegistryKey U_EXPORT2 -ucurr_register(const UChar* isoCode, const char* locale, UErrorCode *status) -{ - if (status && U_SUCCESS(*status)) { - char id[ULOC_FULLNAME_CAPACITY]; - idForLocale(locale, id, sizeof(id), status); - return CReg::reg(isoCode, id, status); - } - return NULL; -} - -// ------------------------------------- - -U_CAPI UBool U_EXPORT2 -ucurr_unregister(UCurrRegistryKey key, UErrorCode* status) -{ - if (status && U_SUCCESS(*status)) { - return CReg::unreg(key); - } - return false; -} -#endif /* UCONFIG_NO_SERVICE */ - -// ------------------------------------- - -/** - * Release all static memory held by currency. - */ -/*The declaration here is needed so currency_cleanup(void) - * can call this function. - */ -static UBool U_CALLCONV -currency_cache_cleanup(void); - -U_CDECL_BEGIN -static UBool U_CALLCONV currency_cleanup(void) { -#if !UCONFIG_NO_SERVICE - CReg::cleanup(); -#endif - /* - * There might be some cached currency data or isoCodes data. - */ - currency_cache_cleanup(); - isoCodes_cleanup(); - currSymbolsEquiv_cleanup(); - - return true; -} -U_CDECL_END - -// ------------------------------------- - -U_CAPI int32_t U_EXPORT2 -ucurr_forLocale(const char* locale, - UChar* buff, - int32_t buffCapacity, - UErrorCode* ec) { - if (U_FAILURE(*ec)) { return 0; } - if (buffCapacity < 0 || (buff == nullptr && buffCapacity > 0)) { - *ec = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - char currency[4]; // ISO currency codes are alpha3 codes. - UErrorCode localStatus = U_ZERO_ERROR; - int32_t resLen = uloc_getKeywordValue(locale, "currency", - currency, UPRV_LENGTHOF(currency), &localStatus); - if (U_SUCCESS(localStatus) && resLen == 3 && uprv_isInvariantString(currency, resLen)) { - if (resLen < buffCapacity) { - T_CString_toUpperCase(currency); - u_charsToUChars(currency, buff, resLen); - } - return u_terminateUChars(buff, buffCapacity, resLen, ec); - } - - // get country or country_variant in `id' - char id[ULOC_FULLNAME_CAPACITY]; - idForLocale(locale, id, UPRV_LENGTHOF(id), ec); - if (U_FAILURE(*ec)) { - return 0; - } - -#if !UCONFIG_NO_SERVICE - const UChar* result = CReg::get(id); - if (result) { - if(buffCapacity > u_strlen(result)) { - u_strcpy(buff, result); - } - resLen = u_strlen(result); - return u_terminateUChars(buff, buffCapacity, resLen, ec); - } -#endif - // Remove variants, which is only needed for registration. - char *idDelim = uprv_strchr(id, VAR_DELIM); - if (idDelim) { - idDelim[0] = 0; - } - - const UChar* s = NULL; // Currency code from data file. - if (id[0] == 0) { - // No point looking in the data for an empty string. - // This is what we would get. - localStatus = U_MISSING_RESOURCE_ERROR; - } else { - // Look up the CurrencyMap element in the root bundle. - localStatus = U_ZERO_ERROR; - UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); - UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); - UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus); - // https://unicode-org.atlassian.net/browse/ICU-21997 - // Prefer to use currencies that are legal tender. - if (U_SUCCESS(localStatus)) { - int32_t arrayLength = ures_getSize(countryArray); - for (int32_t i = 0; i < arrayLength; ++i) { - LocalUResourceBundlePointer currencyReq( - ures_getByIndex(countryArray, i, nullptr, &localStatus)); - // The currency is legal tender if it is *not* marked with tender{"false"}. - UErrorCode tenderStatus = localStatus; - const UChar *tender = - ures_getStringByKey(currencyReq.getAlias(), "tender", nullptr, &tenderStatus); - bool isTender = U_FAILURE(tenderStatus) || u_strcmp(tender, u"false") != 0; - if (!isTender && s != nullptr) { - // We already have a non-tender currency. Ignore all following non-tender ones. - continue; - } - // Fetch the currency code. - s = ures_getStringByKey(currencyReq.getAlias(), "id", &resLen, &localStatus); - if (isTender) { - break; - } - } - if (U_SUCCESS(localStatus) && s == nullptr) { - localStatus = U_MISSING_RESOURCE_ERROR; - } - } - ures_close(countryArray); - } - - if ((U_FAILURE(localStatus)) && strchr(id, '_') != 0) { - // We don't know about it. Check to see if we support the variant. - uloc_getParent(locale, id, UPRV_LENGTHOF(id), ec); - *ec = U_USING_FALLBACK_WARNING; - // TODO: Loop over the shortened id rather than recursing and - // looking again for a currency keyword. - return ucurr_forLocale(id, buff, buffCapacity, ec); - } - if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) { - // There is nothing to fallback to. Report the failure/warning if possible. - *ec = localStatus; - } - if (U_SUCCESS(*ec)) { - if(buffCapacity > resLen) { - u_strcpy(buff, s); - } - } - return u_terminateUChars(buff, buffCapacity, resLen, ec); -} - -// end registration - -/** - * Modify the given locale name by removing the rightmost _-delimited - * element. If there is none, empty the string ("" == root). - * NOTE: The string "root" is not recognized; do not use it. - * @return true if the fallback happened; false if locale is already - * root (""). - */ -static UBool fallback(char *loc) { - if (!*loc) { - return false; - } - UErrorCode status = U_ZERO_ERROR; - if (uprv_strcmp(loc, "en_GB") == 0) { - // HACK: See #13368. We need "en_GB" to fall back to "en_001" instead of "en" - // in order to consume the correct data strings. This hack will be removed - // when proper data sink loading is implemented here. - // NOTE: "001" adds 1 char over "GB". However, both call sites allocate - // arrays with length ULOC_FULLNAME_CAPACITY (plenty of room for en_001). - uprv_strcpy(loc + 3, "001"); - } else { - uloc_getParent(loc, loc, (int32_t)uprv_strlen(loc), &status); - } - /* - char *i = uprv_strrchr(loc, '_'); - if (i == NULL) { - i = loc; - } - *i = 0; - */ - return true; -} - - -U_CAPI const UChar* U_EXPORT2 -ucurr_getName(const UChar* currency, - const char* locale, - UCurrNameStyle nameStyle, - UBool* isChoiceFormat, // fillin - int32_t* len, // fillin - UErrorCode* ec) { - - // Look up the Currencies resource for the given locale. The - // Currencies locale data looks like this: - //|en { - //| Currencies { - //| USD { "US$", "US Dollar" } - //| CHF { "Sw F", "Swiss Franc" } - //| INR { "=0#Rs|1#Re|1 4) { - *ec = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - // In the future, resource bundles may implement multi-level - // fallback. That is, if a currency is not found in the en_US - // Currencies data, then the en Currencies data will be searched. - // Currently, if a Currencies datum exists in en_US and en, the - // en_US entry hides that in en. - - // We want multi-level fallback for this resource, so we implement - // it manually. - - // Use a separate UErrorCode here that does not propagate out of - // this function. - UErrorCode ec2 = U_ZERO_ERROR; - - char loc[ULOC_FULLNAME_CAPACITY]; - uloc_getName(locale, loc, sizeof(loc), &ec2); - if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) { - *ec = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - char buf[ISO_CURRENCY_CODE_LENGTH+1]; - myUCharsToChars(buf, currency); - - /* Normalize the keyword value to uppercase */ - T_CString_toUpperCase(buf); - - const UChar* s = NULL; - ec2 = U_ZERO_ERROR; - LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_CURR, loc, &ec2)); - - if (nameStyle == UCURR_NARROW_SYMBOL_NAME || nameStyle == UCURR_FORMAL_SYMBOL_NAME || nameStyle == UCURR_VARIANT_SYMBOL_NAME) { - CharString key; - switch (nameStyle) { - case UCURR_NARROW_SYMBOL_NAME: - key.append(CURRENCIES_NARROW, ec2); - break; - case UCURR_FORMAL_SYMBOL_NAME: - key.append(CURRENCIES_FORMAL, ec2); - break; - case UCURR_VARIANT_SYMBOL_NAME: - key.append(CURRENCIES_VARIANT, ec2); - break; - default: - *ec = U_UNSUPPORTED_ERROR; - return 0; - } - key.append("/", ec2); - key.append(buf, ec2); - s = ures_getStringByKeyWithFallback(rb.getAlias(), key.data(), len, &ec2); - if (ec2 == U_MISSING_RESOURCE_ERROR) { - *ec = U_USING_FALLBACK_WARNING; - ec2 = U_ZERO_ERROR; - choice = UCURR_SYMBOL_NAME; - } - } - if (s == NULL) { - ures_getByKey(rb.getAlias(), CURRENCIES, rb.getAlias(), &ec2); - ures_getByKeyWithFallback(rb.getAlias(), buf, rb.getAlias(), &ec2); - s = ures_getStringByIndex(rb.getAlias(), choice, len, &ec2); - } - - // If we've succeeded we're done. Otherwise, try to fallback. - // If that fails (because we are already at root) then exit. - if (U_SUCCESS(ec2)) { - if (ec2 == U_USING_DEFAULT_WARNING - || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) { - *ec = ec2; - } - } - - // We no longer support choice format data in names. Data should not contain - // choice patterns. - if (isChoiceFormat != NULL) { - *isChoiceFormat = false; - } - if (U_SUCCESS(ec2)) { - U_ASSERT(s != NULL); - return s; - } - - // If we fail to find a match, use the ISO 4217 code - *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...? - *ec = U_USING_DEFAULT_WARNING; - return currency; -} - -U_CAPI const UChar* U_EXPORT2 -ucurr_getPluralName(const UChar* currency, - const char* locale, - UBool* isChoiceFormat, - const char* pluralCount, - int32_t* len, // fillin - UErrorCode* ec) { - // Look up the Currencies resource for the given locale. The - // Currencies locale data looks like this: - //|en { - //| CurrencyPlurals { - //| USD{ - //| one{"US dollar"} - //| other{"US dollars"} - //| } - //| } - //|} - - if (U_FAILURE(*ec)) { - return 0; - } - - // Use a separate UErrorCode here that does not propagate out of - // this function. - UErrorCode ec2 = U_ZERO_ERROR; - - char loc[ULOC_FULLNAME_CAPACITY]; - uloc_getName(locale, loc, sizeof(loc), &ec2); - if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) { - *ec = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - char buf[ISO_CURRENCY_CODE_LENGTH+1]; - myUCharsToChars(buf, currency); - - const UChar* s = NULL; - ec2 = U_ZERO_ERROR; - UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2); - - rb = ures_getByKey(rb, CURRENCYPLURALS, rb, &ec2); - - // Fetch resource with multi-level resource inheritance fallback - rb = ures_getByKeyWithFallback(rb, buf, rb, &ec2); - - s = ures_getStringByKeyWithFallback(rb, pluralCount, len, &ec2); - if (U_FAILURE(ec2)) { - // fall back to "other" - ec2 = U_ZERO_ERROR; - s = ures_getStringByKeyWithFallback(rb, "other", len, &ec2); - if (U_FAILURE(ec2)) { - ures_close(rb); - // fall back to long name in Currencies - return ucurr_getName(currency, locale, UCURR_LONG_NAME, - isChoiceFormat, len, ec); - } - } - ures_close(rb); - - // If we've succeeded we're done. Otherwise, try to fallback. - // If that fails (because we are already at root) then exit. - if (U_SUCCESS(ec2)) { - if (ec2 == U_USING_DEFAULT_WARNING - || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) { - *ec = ec2; - } - U_ASSERT(s != NULL); - return s; - } - - // If we fail to find a match, use the ISO 4217 code - *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...? - *ec = U_USING_DEFAULT_WARNING; - return currency; -} - - -//======================================================================== -// Following are structure and function for parsing currency names - -#define NEED_TO_BE_DELETED 0x1 - -// TODO: a better way to define this? -#define MAX_CURRENCY_NAME_LEN 100 - -typedef struct { - const char* IsoCode; // key - UChar* currencyName; // value - int32_t currencyNameLen; // value length - int32_t flag; // flags -} CurrencyNameStruct; - - -#ifndef MIN -#define MIN(a,b) (((a)<(b)) ? (a) : (b)) -#endif - -#ifndef MAX -#define MAX(a,b) (((a)<(b)) ? (b) : (a)) -#endif - - -// Comparison function used in quick sort. -static int U_CALLCONV currencyNameComparator(const void* a, const void* b) { - const CurrencyNameStruct* currName_1 = (const CurrencyNameStruct*)a; - const CurrencyNameStruct* currName_2 = (const CurrencyNameStruct*)b; - for (int32_t i = 0; - i < MIN(currName_1->currencyNameLen, currName_2->currencyNameLen); - ++i) { - if (currName_1->currencyName[i] < currName_2->currencyName[i]) { - return -1; - } - if (currName_1->currencyName[i] > currName_2->currencyName[i]) { - return 1; - } - } - if (currName_1->currencyNameLen < currName_2->currencyNameLen) { - return -1; - } else if (currName_1->currencyNameLen > currName_2->currencyNameLen) { - return 1; - } - return 0; -} - - -// Give a locale, return the maximum number of currency names associated with -// this locale. -// It gets currency names from resource bundles using fallback. -// It is the maximum number because in the fallback chain, some of the -// currency names are duplicated. -// For example, given locale as "en_US", the currency names get from resource -// bundle in "en_US" and "en" are duplicated. The fallback mechanism will count -// all currency names in "en_US" and "en". -static void -getCurrencyNameCount(const char* loc, int32_t* total_currency_name_count, int32_t* total_currency_symbol_count) { - U_NAMESPACE_USE - *total_currency_name_count = 0; - *total_currency_symbol_count = 0; - const UChar* s = NULL; - char locale[ULOC_FULLNAME_CAPACITY] = ""; - uprv_strcpy(locale, loc); - const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv(); - for (;;) { - UErrorCode ec2 = U_ZERO_ERROR; - // TODO: ures_openDirect? - UResourceBundle* rb = ures_open(U_ICUDATA_CURR, locale, &ec2); - UResourceBundle* curr = ures_getByKey(rb, CURRENCIES, NULL, &ec2); - int32_t n = ures_getSize(curr); - for (int32_t i=0; i(symbol->getBuffer()); - (*currencySymbols)[*total_currency_symbol_count].flag = 0; - (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = symbol->length(); - } - } - - // Add currency long name. - s = ures_getStringByIndex(names, UCURR_LONG_NAME, &len, &ec2); - (*currencyNames)[*total_currency_name_count].IsoCode = iso; - UChar* upperName = toUpperCase(s, len, locale); - (*currencyNames)[*total_currency_name_count].currencyName = upperName; - (*currencyNames)[*total_currency_name_count].flag = NEED_TO_BE_DELETED; - (*currencyNames)[(*total_currency_name_count)++].currencyNameLen = len; - - // put (iso, 3, and iso) in to array - // Add currency ISO code. - (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso; - (*currencySymbols)[*total_currency_symbol_count].currencyName = (UChar*)uprv_malloc(sizeof(UChar)*3); - // Must convert iso[] into Unicode - u_charsToUChars(iso, (*currencySymbols)[*total_currency_symbol_count].currencyName, 3); - (*currencySymbols)[*total_currency_symbol_count].flag = NEED_TO_BE_DELETED; - (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = 3; - - ures_close(names); - } - - // currency plurals - UErrorCode ec5 = U_ZERO_ERROR; - UResourceBundle* curr_p = ures_getByKey(rb, CURRENCYPLURALS, NULL, &ec5); - n = ures_getSize(curr_p); - for (int32_t i=0; i= currencyNames[mid].currencyNameLen) { - first = mid + 1; - } else { - if (key > currencyNames[mid].currencyName[indexInCurrencyNames]) { - first = mid + 1; - } - else if (key < currencyNames[mid].currencyName[indexInCurrencyNames]) { - last = mid - 1; - } - else { - // Find a match, and looking for ranges - // Now do two more binary searches. First, on the left side for - // the greatest L such that CurrencyNameStruct[L] < key. - int32_t L = *begin; - int32_t R = mid; - -#ifdef UCURR_DEBUG - printf("mid = %d\n", mid); -#endif - while (L < R) { - int32_t M = (L + R) / 2; -#ifdef UCURR_DEBUG - printf("L = %d, R = %d, M = %d\n", L, R, M); -#endif - if (indexInCurrencyNames >= currencyNames[M].currencyNameLen) { - L = M + 1; - } else { - if (currencyNames[M].currencyName[indexInCurrencyNames] < key) { - L = M + 1; - } else { -#ifdef UCURR_DEBUG - U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key); -#endif - R = M; - } - } - } -#ifdef UCURR_DEBUG - U_ASSERT(L == R); -#endif - *begin = L; -#ifdef UCURR_DEBUG - printf("begin = %d\n", *begin); - U_ASSERT(currencyNames[*begin].currencyName[indexInCurrencyNames] == key); -#endif - - // Now for the second search, finding the least R such that - // key < CurrencyNameStruct[R]. - L = mid; - R = *end; - while (L < R) { - int32_t M = (L + R) / 2; -#ifdef UCURR_DEBUG - printf("L = %d, R = %d, M = %d\n", L, R, M); -#endif - if (currencyNames[M].currencyNameLen < indexInCurrencyNames) { - L = M + 1; - } else { - if (currencyNames[M].currencyName[indexInCurrencyNames] > key) { - R = M; - } else { -#ifdef UCURR_DEBUG - U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key); -#endif - L = M + 1; - } - } - } -#ifdef UCURR_DEBUG - U_ASSERT(L == R); -#endif - if (currencyNames[R].currencyName[indexInCurrencyNames] > key) { - *end = R - 1; - } else { - *end = R; - } -#ifdef UCURR_DEBUG - printf("end = %d\n", *end); -#endif - - // now, found the range. check whether there is exact match - if (currencyNames[*begin].currencyNameLen == indexInCurrencyNames + 1) { - return *begin; // find range and exact match. - } - return -1; // find range, but no exact match. - } - } - } - *begin = -1; - *end = -1; - return -1; // failed to find range. -} - - -// Linear search "text" in "currencyNames". -// @param begin, end: the begin and end index in currencyNames, within which -// range should the search be performed. -// @param textLen: the length of the text to be compared -// @param maxMatchLen(IN/OUT): passing in the computed max matching length -// pass out the new max matching length -// @param maxMatchIndex: the index in currencyName which has the longest -// match with input text. -static void -linearSearch(const CurrencyNameStruct* currencyNames, - int32_t begin, int32_t end, - const UChar* text, int32_t textLen, - int32_t *partialMatchLen, - int32_t *maxMatchLen, int32_t* maxMatchIndex) { - int32_t initialPartialMatchLen = *partialMatchLen; - for (int32_t index = begin; index <= end; ++index) { - int32_t len = currencyNames[index].currencyNameLen; - if (len > *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, entry->totalCurrencyNameCount); - deleteCurrencyNames(entry->currencySymbols, entry->totalCurrencySymbolCount); - uprv_free(entry); -} - - -// Cache clean up -static UBool U_CALLCONV -currency_cache_cleanup(void) { - for (int32_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) { - if (currCache[i]) { - deleteCacheEntry(currCache[i]); - currCache[i] = 0; - } - } - return true; -} - - -/** - * Loads the currency name data from the cache, or from resource bundles if necessary. - * The refCount is automatically incremented. It is the caller's responsibility - * to decrement it when done! - */ -static CurrencyNameCacheEntry* -getCacheEntry(const char* locale, UErrorCode& ec) { - - int32_t total_currency_name_count = 0; - CurrencyNameStruct* currencyNames = NULL; - int32_t total_currency_symbol_count = 0; - CurrencyNameStruct* currencySymbols = NULL; - CurrencyNameCacheEntry* cacheEntry = NULL; - - umtx_lock(&gCurrencyCacheMutex); - // in order to handle racing correctly, - // not putting 'search' in a separate function. - int8_t found = -1; - for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) { - if (currCache[i]!= NULL && - uprv_strcmp(locale, currCache[i]->locale) == 0) { - found = i; - break; - } - } - if (found != -1) { - cacheEntry = currCache[found]; - ++(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 NULL; - } - umtx_lock(&gCurrencyCacheMutex); - // check again. - for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) { - if (currCache[i]!= NULL && - uprv_strcmp(locale, currCache[i]->locale) == 0) { - found = i; - break; - } - } - if (found == -1) { - // insert new entry to - // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM - // and remove the existing entry - // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM - // from cache. - cacheEntry = currCache[currentCacheEntryIndex]; - if (cacheEntry) { - --(cacheEntry->refCount); - // delete if the ref count is zero - if (cacheEntry->refCount == 0) { - deleteCacheEntry(cacheEntry); - } - } - cacheEntry = (CurrencyNameCacheEntry*)uprv_malloc(sizeof(CurrencyNameCacheEntry)); - currCache[currentCacheEntryIndex] = cacheEntry; - uprv_strcpy(cacheEntry->locale, locale); - cacheEntry->currencyNames = currencyNames; - cacheEntry->totalCurrencyNameCount = total_currency_name_count; - cacheEntry->currencySymbols = currencySymbols; - 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_cleanup); - } else { - deleteCurrencyNames(currencyNames, total_currency_name_count); - deleteCurrencyNames(currencySymbols, total_currency_symbol_count); - cacheEntry = currCache[found]; - ++(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]; - UChar upperText[MAX_CURRENCY_NAME_LEN]; - int32_t textLen = MIN(MAX_CURRENCY_NAME_LEN, text.length() - start); - text.extract(start, textLen, inputText); - 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 comparison against currency names - searchCurrencyName(currencyNames, total_currency_name_count, - upperText, textLen, partialMatchLen, &max, &matchIndex); - -#ifdef UCURR_DEBUG - printf("search in names, max = %d, matchIndex = %d\n", max, matchIndex); -#endif - - int32_t maxInSymbol = 0; - int32_t matchIndexInSymbol = -1; - if (type != UCURR_LONG_NAME) { // not name only - // case sensitive comparison against currency symbols and ISO code. - searchCurrencyName(currencySymbols, total_currency_symbol_count, - inputText, textLen, - partialMatchLen, - &maxInSymbol, &matchIndexInSymbol); - } - -#ifdef UCURR_DEBUG - printf("search in symbols, maxInSymbol = %d, matchIndexInSymbol = %d\n", maxInSymbol, matchIndexInSymbol); - if(matchIndexInSymbol != -1) { - printf("== ISO=%s\n", currencySymbols[matchIndexInSymbol].IsoCode); - } -#endif - - if (max >= maxInSymbol && matchIndex != -1) { - u_charsToUChars(currencyNames[matchIndex].IsoCode, result, 4); - pos.setIndex(start + max); - } else if (maxInSymbol >= max && matchIndexInSymbol != -1) { - u_charsToUChars(currencySymbols[matchIndexInSymbol].IsoCode, result, 4); - pos.setIndex(start + maxInSymbol); - } - - // decrease reference count - releaseCacheEntry(cacheEntry); -} - -void uprv_currencyLeads(const char* locale, icu::UnicodeSet& result, UErrorCode& ec) { - U_NAMESPACE_USE - if (U_FAILURE(ec)) { - return; - } - 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); -} - - -/** - * Internal method. Given a currency ISO code and a locale, return - * the "static" currency name. This is usually the same as the - * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the - * format is applied to the number 2.0 (to yield the more common - * plural) to return a static name. - * - * This is used for backward compatibility with old currency logic in - * DecimalFormat and DecimalFormatSymbols. - */ -U_CAPI void -uprv_getStaticCurrencyName(const UChar* iso, const char* loc, - icu::UnicodeString& result, UErrorCode& ec) -{ - U_NAMESPACE_USE - - int32_t len; - const UChar* currname = ucurr_getName(iso, loc, UCURR_SYMBOL_NAME, - nullptr /* isChoiceFormat */, &len, &ec); - if (U_SUCCESS(ec)) { - result.setTo(currname, len); - } -} - -U_CAPI int32_t U_EXPORT2 -ucurr_getDefaultFractionDigits(const UChar* currency, UErrorCode* ec) { - return ucurr_getDefaultFractionDigitsForUsage(currency,UCURR_USAGE_STANDARD,ec); -} - -U_CAPI int32_t U_EXPORT2 -ucurr_getDefaultFractionDigitsForUsage(const UChar* currency, const UCurrencyUsage usage, UErrorCode* ec) { - int32_t fracDigits = 0; - if (U_SUCCESS(*ec)) { - switch (usage) { - case UCURR_USAGE_STANDARD: - fracDigits = (_findMetaData(currency, *ec))[0]; - break; - case UCURR_USAGE_CASH: - fracDigits = (_findMetaData(currency, *ec))[2]; - break; - default: - *ec = U_UNSUPPORTED_ERROR; - } - } - return fracDigits; -} - -U_CAPI double U_EXPORT2 -ucurr_getRoundingIncrement(const UChar* currency, UErrorCode* ec) { - return ucurr_getRoundingIncrementForUsage(currency, UCURR_USAGE_STANDARD, ec); -} - -U_CAPI double U_EXPORT2 -ucurr_getRoundingIncrementForUsage(const UChar* currency, const UCurrencyUsage usage, UErrorCode* ec) { - double result = 0.0; - - const int32_t *data = _findMetaData(currency, *ec); - if (U_SUCCESS(*ec)) { - int32_t fracDigits; - int32_t increment; - switch (usage) { - case UCURR_USAGE_STANDARD: - fracDigits = data[0]; - increment = data[1]; - break; - case UCURR_USAGE_CASH: - fracDigits = data[2]; - increment = data[3]; - break; - default: - *ec = U_UNSUPPORTED_ERROR; - return result; - } - - // If the meta data is invalid, return 0.0 - if (fracDigits < 0 || fracDigits > MAX_POW10) { - *ec = U_INVALID_FORMAT_ERROR; - } else { - // A rounding value of 0 or 1 indicates no rounding. - if (increment >= 2) { - // Return (increment) / 10^(fracDigits). The only actual rounding data, - // as of this writing, is CHF { 2, 5 }. - result = double(increment) / POW10[fracDigits]; - } - } - } - - return result; -} - -U_CDECL_BEGIN - -typedef struct UCurrencyContext { - uint32_t currType; /* UCurrCurrencyType */ - uint32_t listIdx; -} UCurrencyContext; - -/* -Please keep this list in alphabetical order. -You can look at the CLDR supplemental data or ISO-4217 for the meaning of some -of these items. -ISO-4217: http://www.iso.org/iso/en/prods-services/popstds/currencycodeslist.html -*/ -static const struct CurrencyList { - const char *currency; - uint32_t currType; -} gCurrencyList[] = { - {"ADP", UCURR_COMMON|UCURR_DEPRECATED}, - {"AED", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"AFA", UCURR_COMMON|UCURR_DEPRECATED}, - {"AFN", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"ALK", UCURR_COMMON|UCURR_DEPRECATED}, - {"ALL", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"AMD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"ANG", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"AOA", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"AOK", UCURR_COMMON|UCURR_DEPRECATED}, - {"AON", UCURR_COMMON|UCURR_DEPRECATED}, - {"AOR", UCURR_COMMON|UCURR_DEPRECATED}, - {"ARA", UCURR_COMMON|UCURR_DEPRECATED}, - {"ARL", UCURR_COMMON|UCURR_DEPRECATED}, - {"ARM", UCURR_COMMON|UCURR_DEPRECATED}, - {"ARP", UCURR_COMMON|UCURR_DEPRECATED}, - {"ARS", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"ATS", UCURR_COMMON|UCURR_DEPRECATED}, - {"AUD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"AWG", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"AZM", UCURR_COMMON|UCURR_DEPRECATED}, - {"AZN", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BAD", UCURR_COMMON|UCURR_DEPRECATED}, - {"BAM", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BAN", UCURR_COMMON|UCURR_DEPRECATED}, - {"BBD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BDT", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BEC", UCURR_UNCOMMON|UCURR_DEPRECATED}, - {"BEF", UCURR_COMMON|UCURR_DEPRECATED}, - {"BEL", UCURR_UNCOMMON|UCURR_DEPRECATED}, - {"BGL", UCURR_COMMON|UCURR_DEPRECATED}, - {"BGM", UCURR_COMMON|UCURR_DEPRECATED}, - {"BGN", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BGO", UCURR_COMMON|UCURR_DEPRECATED}, - {"BHD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BIF", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BMD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BND", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BOB", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BOL", UCURR_COMMON|UCURR_DEPRECATED}, - {"BOP", UCURR_COMMON|UCURR_DEPRECATED}, - {"BOV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"BRB", UCURR_COMMON|UCURR_DEPRECATED}, - {"BRC", UCURR_COMMON|UCURR_DEPRECATED}, - {"BRE", UCURR_COMMON|UCURR_DEPRECATED}, - {"BRL", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BRN", UCURR_COMMON|UCURR_DEPRECATED}, - {"BRR", UCURR_COMMON|UCURR_DEPRECATED}, - {"BRZ", UCURR_COMMON|UCURR_DEPRECATED}, - {"BSD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BTN", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BUK", UCURR_COMMON|UCURR_DEPRECATED}, - {"BWP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"BYB", UCURR_COMMON|UCURR_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}, - {"CHE", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"CHF", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"CHW", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"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}, - {"COU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"CRC", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"CSD", UCURR_COMMON|UCURR_DEPRECATED}, - {"CSK", UCURR_COMMON|UCURR_DEPRECATED}, - {"CUC", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"CUP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"CVE", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"CYP", UCURR_COMMON|UCURR_DEPRECATED}, - {"CZK", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"DDM", UCURR_COMMON|UCURR_DEPRECATED}, - {"DEM", UCURR_COMMON|UCURR_DEPRECATED}, - {"DJF", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"DKK", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"DOP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"DZD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"ECS", UCURR_COMMON|UCURR_DEPRECATED}, - {"ECV", UCURR_UNCOMMON|UCURR_DEPRECATED}, - {"EEK", UCURR_COMMON|UCURR_DEPRECATED}, - {"EGP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"ERN", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"ESA", UCURR_UNCOMMON|UCURR_DEPRECATED}, - {"ESB", UCURR_UNCOMMON|UCURR_DEPRECATED}, - {"ESP", UCURR_COMMON|UCURR_DEPRECATED}, - {"ETB", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"EUR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"FIM", UCURR_COMMON|UCURR_DEPRECATED}, - {"FJD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"FKP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"FRF", UCURR_COMMON|UCURR_DEPRECATED}, - {"GBP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"GEK", UCURR_COMMON|UCURR_DEPRECATED}, - {"GEL", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"GHC", UCURR_COMMON|UCURR_DEPRECATED}, - {"GHS", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"GIP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"GMD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"GNF", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"GNS", UCURR_COMMON|UCURR_DEPRECATED}, - {"GQE", UCURR_COMMON|UCURR_DEPRECATED}, - {"GRD", UCURR_COMMON|UCURR_DEPRECATED}, - {"GTQ", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"GWE", UCURR_COMMON|UCURR_DEPRECATED}, - {"GWP", UCURR_COMMON|UCURR_DEPRECATED}, - {"GYD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"HKD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"HNL", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"HRD", UCURR_COMMON|UCURR_DEPRECATED}, - {"HRK", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"HTG", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"HUF", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"IDR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"IEP", UCURR_COMMON|UCURR_DEPRECATED}, - {"ILP", UCURR_COMMON|UCURR_DEPRECATED}, - {"ILR", UCURR_COMMON|UCURR_DEPRECATED}, - {"ILS", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"INR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"IQD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"IRR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"ISJ", UCURR_COMMON|UCURR_DEPRECATED}, - {"ISK", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"ITL", UCURR_COMMON|UCURR_DEPRECATED}, - {"JMD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"JOD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"JPY", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"KES", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"KGS", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"KHR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"KMF", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"KPW", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"KRH", UCURR_COMMON|UCURR_DEPRECATED}, - {"KRO", UCURR_COMMON|UCURR_DEPRECATED}, - {"KRW", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"KWD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"KYD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"KZT", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"LAK", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"LBP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"LKR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"LRD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"LSL", 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_DEPRECATED}, - {"LVR", UCURR_COMMON|UCURR_DEPRECATED}, - {"LYD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"MAD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"MAF", UCURR_COMMON|UCURR_DEPRECATED}, - {"MCF", UCURR_COMMON|UCURR_DEPRECATED}, - {"MDC", UCURR_COMMON|UCURR_DEPRECATED}, - {"MDL", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"MGA", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"MGF", UCURR_COMMON|UCURR_DEPRECATED}, - {"MKD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"MKN", UCURR_COMMON|UCURR_DEPRECATED}, - {"MLF", UCURR_COMMON|UCURR_DEPRECATED}, - {"MMK", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"MNT", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"MOP", 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}, // 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_DEPRECATED}, - {"MZM", UCURR_COMMON|UCURR_DEPRECATED}, - {"MZN", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"NAD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"NGN", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"NIC", UCURR_COMMON|UCURR_DEPRECATED}, - {"NIO", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"NLG", UCURR_COMMON|UCURR_DEPRECATED}, - {"NOK", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"NPR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"NZD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"OMR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"PAB", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"PEI", UCURR_COMMON|UCURR_DEPRECATED}, - {"PEN", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"PES", UCURR_COMMON|UCURR_DEPRECATED}, - {"PGK", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"PHP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"PKR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"PLN", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"PLZ", UCURR_COMMON|UCURR_DEPRECATED}, - {"PTE", UCURR_COMMON|UCURR_DEPRECATED}, - {"PYG", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"QAR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"RHD", UCURR_COMMON|UCURR_DEPRECATED}, - {"ROL", UCURR_COMMON|UCURR_DEPRECATED}, - {"RON", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"RSD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"RUB", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"RUR", UCURR_COMMON|UCURR_DEPRECATED}, - {"RWF", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"SAR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"SBD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"SCR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"SDD", UCURR_COMMON|UCURR_DEPRECATED}, - {"SDG", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"SDP", UCURR_COMMON|UCURR_DEPRECATED}, - {"SEK", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"SGD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"SHP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"SIT", UCURR_COMMON|UCURR_DEPRECATED}, - {"SKK", UCURR_COMMON|UCURR_DEPRECATED}, - {"SLE", UCURR_COMMON|UCURR_NON_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_DEPRECATED}, - {"STN", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"SUR", UCURR_COMMON|UCURR_DEPRECATED}, - {"SVC", UCURR_COMMON|UCURR_DEPRECATED}, - {"SYP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"SZL", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"THB", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"TJR", UCURR_COMMON|UCURR_DEPRECATED}, - {"TJS", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"TMM", UCURR_COMMON|UCURR_DEPRECATED}, - {"TMT", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"TND", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"TOP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"TPE", UCURR_COMMON|UCURR_DEPRECATED}, - {"TRL", UCURR_COMMON|UCURR_DEPRECATED}, - {"TRY", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"TTD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"TWD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"TZS", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"UAH", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"UAK", UCURR_COMMON|UCURR_DEPRECATED}, - {"UGS", UCURR_COMMON|UCURR_DEPRECATED}, - {"UGX", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"USD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"USN", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"USS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"UYI", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"UYP", UCURR_COMMON|UCURR_DEPRECATED}, - {"UYU", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"UYW", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"UZS", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"VEB", UCURR_COMMON|UCURR_DEPRECATED}, - {"VED", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"VEF", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"VES", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"VND", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"VNN", UCURR_COMMON|UCURR_DEPRECATED}, - {"VUV", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"WST", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"XAF", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"XAG", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"XAU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"XBA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"XBB", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"XBC", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"XBD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"XCD", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"XDR", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"XEU", UCURR_UNCOMMON|UCURR_DEPRECATED}, - {"XFO", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"XFU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"XOF", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"XPD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"XPF", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"XPT", 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}, - {"XXX", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"YDD", UCURR_COMMON|UCURR_DEPRECATED}, - {"YER", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"YUD", UCURR_COMMON|UCURR_DEPRECATED}, - {"YUM", UCURR_COMMON|UCURR_DEPRECATED}, - {"YUN", UCURR_COMMON|UCURR_DEPRECATED}, - {"YUR", UCURR_COMMON|UCURR_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}, - { NULL, 0 } // Leave here to denote the end of the list. -}; - -#define UCURR_MATCHES_BITMASK(variable, typeToMatch) \ - ((typeToMatch) == UCURR_ALL || ((variable) & (typeToMatch)) == (typeToMatch)) - -static int32_t U_CALLCONV -ucurr_countCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) { - UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context); - uint32_t currType = myContext->currType; - int32_t count = 0; - - /* Count the number of items matching the type we are looking for. */ - for (int32_t idx = 0; gCurrencyList[idx].currency != NULL; idx++) { - if (UCURR_MATCHES_BITMASK(gCurrencyList[idx].currType, currType)) { - count++; - } - } - return count; -} - -static const char* U_CALLCONV -ucurr_nextCurrencyList(UEnumeration *enumerator, - int32_t* resultLength, - UErrorCode * /*pErrorCode*/) -{ - UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context); - - /* Find the next in the list that matches the type we are looking for. */ - while (myContext->listIdx < UPRV_LENGTHOF(gCurrencyList)-1) { - const struct CurrencyList *currItem = &gCurrencyList[myContext->listIdx++]; - if (UCURR_MATCHES_BITMASK(currItem->currType, myContext->currType)) - { - if (resultLength) { - *resultLength = 3; /* Currency codes are only 3 chars long */ - } - return currItem->currency; - } - } - /* We enumerated too far. */ - if (resultLength) { - *resultLength = 0; - } - return NULL; -} - -static void U_CALLCONV -ucurr_resetCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) { - ((UCurrencyContext *)(enumerator->context))->listIdx = 0; -} - -static void U_CALLCONV -ucurr_closeCurrencyList(UEnumeration *enumerator) { - uprv_free(enumerator->context); - uprv_free(enumerator); -} - -static void U_CALLCONV -ucurr_createCurrencyList(UHashtable *isoCodes, UErrorCode* status){ - UErrorCode localStatus = U_ZERO_ERROR; - - // Look up the CurrencyMap element in the root bundle. - UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); - UResourceBundle *currencyMapArray = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); - - if (U_SUCCESS(localStatus)) { - // process each entry in currency map - for (int32_t i=0; iisoCode = isoCode; - entry->from = fromDate; - entry->to = toDate; - - localStatus = U_ZERO_ERROR; - uhash_put(isoCodes, (UChar *)isoCode, entry, &localStatus); - } - } else { - *status = localStatus; - } - ures_close(currencyArray); - } - } else { - *status = localStatus; - } - - ures_close(currencyMapArray); -} - -static const UEnumeration gEnumCurrencyList = { - NULL, - NULL, - ucurr_closeCurrencyList, - ucurr_countCurrencyList, - uenum_unextDefault, - ucurr_nextCurrencyList, - ucurr_resetCurrencyList -}; -U_CDECL_END - - -static void U_CALLCONV initIsoCodes(UErrorCode &status) { - U_ASSERT(gIsoCodes == NULL); - ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); - - UHashtable *isoCodes = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); - if (U_FAILURE(status)) { - return; - } - uhash_setValueDeleter(isoCodes, deleteIsoCodeEntry); - - ucurr_createCurrencyList(isoCodes, &status); - if (U_FAILURE(status)) { - uhash_close(isoCodes); - return; - } - gIsoCodes = isoCodes; // Note: gIsoCodes is const. Once set up here it is never altered, - // and read only access is safe without synchronization. -} - -static void populateCurrSymbolsEquiv(icu::Hashtable *hash, UErrorCode &status) { - 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; } - } - } -} - -static void U_CALLCONV initCurrSymbolsEquiv() { - U_ASSERT(gCurrSymbolsEquiv == NULL); - UErrorCode status = U_ZERO_ERROR; - ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); - icu::Hashtable *temp = new icu::Hashtable(status); - if (temp == NULL) { - return; - } - if (U_FAILURE(status)) { - delete temp; - return; - } - temp->setValueDeleter(deleteUnicode); - populateCurrSymbolsEquiv(temp, status); - if (U_FAILURE(status)) { - delete temp; - return; - } - gCurrSymbolsEquiv = temp; -} - -U_CAPI UBool U_EXPORT2 -ucurr_isAvailable(const UChar* isoCode, UDate from, UDate to, UErrorCode* eErrorCode) { - umtx_initOnce(gIsoCodesInitOnce, &initIsoCodes, *eErrorCode); - if (U_FAILURE(*eErrorCode)) { - return false; - } - - IsoCodeEntry* result = (IsoCodeEntry *) uhash_get(gIsoCodes, isoCode); - if (result == NULL) { - return false; - } else if (from > to) { - *eErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } else if ((from > result->to) || (to < result->from)) { - return false; - } - return true; -} - -static const icu::Hashtable* getCurrSymbolsEquiv() { - umtx_initOnce(gCurrSymbolsEquivInitOnce, &initCurrSymbolsEquiv); - return gCurrSymbolsEquiv; -} - -U_CAPI UEnumeration * U_EXPORT2 -ucurr_openISOCurrencies(uint32_t currType, UErrorCode *pErrorCode) { - UEnumeration *myEnum = NULL; - UCurrencyContext *myContext; - - myEnum = (UEnumeration*)uprv_malloc(sizeof(UEnumeration)); - if (myEnum == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uprv_memcpy(myEnum, &gEnumCurrencyList, sizeof(UEnumeration)); - myContext = (UCurrencyContext*)uprv_malloc(sizeof(UCurrencyContext)); - if (myContext == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - uprv_free(myEnum); - return NULL; - } - myContext->currType = currType; - myContext->listIdx = 0; - myEnum->context = myContext; - return myEnum; -} - -U_CAPI int32_t U_EXPORT2 -ucurr_countCurrencies(const char* locale, - UDate date, - UErrorCode* ec) -{ - int32_t currCount = 0; - - if (ec != NULL && U_SUCCESS(*ec)) - { - // local variables - UErrorCode localStatus = U_ZERO_ERROR; - char id[ULOC_FULLNAME_CAPACITY]; - - // get country or country_variant in `id' - idForLocale(locale, id, sizeof(id), ec); - - if (U_FAILURE(*ec)) - { - return 0; - } - - // Remove variants, which is only needed for registration. - char *idDelim = strchr(id, VAR_DELIM); - if (idDelim) - { - idDelim[0] = 0; - } - - // Look up the CurrencyMap element in the root bundle. - UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); - UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); - - // Using the id derived from the local, get the currency data - UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus); - - // process each currency to see which one is valid for the given date - if (U_SUCCESS(localStatus)) - { - for (int32_t i=0; i 2) - { - int32_t toLength = 0; - UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus); - const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus); - - currDate64 = (int64_t)toArray[0] << 32; - currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF)); - UDate toDate = (UDate)currDate64; - - if ((fromDate <= date) && (date < toDate)) - { - currCount++; - } - - ures_close(toRes); - } - else - { - if (fromDate <= date) - { - currCount++; - } - } - - // close open resources - ures_close(currencyRes); - ures_close(fromRes); - - } // end For loop - } // end if (U_SUCCESS(localStatus)) - - ures_close(countryArray); - - // Check for errors - if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) - { - // There is nothing to fallback to. - // Report the failure/warning if possible. - *ec = localStatus; - } - - if (U_SUCCESS(*ec)) - { - // no errors - return currCount; - } - - } - - // If we got here, either error code is invalid or - // some argument passed is no good. - return 0; -} - -U_CAPI int32_t U_EXPORT2 -ucurr_forLocaleAndDate(const char* locale, - UDate date, - int32_t index, - UChar* buff, - int32_t buffCapacity, - UErrorCode* ec) -{ - int32_t resLen = 0; - int32_t currIndex = 0; - const UChar* s = NULL; - - if (ec != NULL && U_SUCCESS(*ec)) - { - // check the arguments passed - if ((buff && buffCapacity) || !buffCapacity ) - { - // local variables - UErrorCode localStatus = U_ZERO_ERROR; - char id[ULOC_FULLNAME_CAPACITY]; - - // get country or country_variant in `id' - idForLocale(locale, id, sizeof(id), ec); - if (U_FAILURE(*ec)) - { - return 0; - } - - // Remove variants, which is only needed for registration. - char *idDelim = strchr(id, VAR_DELIM); - if (idDelim) - { - idDelim[0] = 0; - } - - // Look up the CurrencyMap element in the root bundle. - UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); - UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); - - // Using the id derived from the local, get the currency data - UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus); - - // process each currency to see which one is valid for the given date - bool matchFound = false; - if (U_SUCCESS(localStatus)) - { - if ((index <= 0) || (index> ures_getSize(countryArray))) - { - // requested index is out of bounds - ures_close(countryArray); - return 0; - } - - for (int32_t i=0; i 2) - { - int32_t toLength = 0; - UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus); - const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus); - - currDate64 = (int64_t)toArray[0] << 32; - currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF)); - UDate toDate = (UDate)currDate64; - - if ((fromDate <= date) && (date < toDate)) - { - currIndex++; - if (currIndex == index) - { - matchFound = true; - } - } - - ures_close(toRes); - } - else - { - if (fromDate <= date) - { - currIndex++; - if (currIndex == index) - { - matchFound = true; - } - } - } - - // close open resources - ures_close(currencyRes); - ures_close(fromRes); - - // check for loop exit - if (matchFound) - { - break; - } - - } // end For loop - } - - ures_close(countryArray); - - // Check for errors - if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) - { - // There is nothing to fallback to. - // Report the failure/warning if possible. - *ec = localStatus; - } - - if (U_SUCCESS(*ec)) - { - // no errors - if((buffCapacity> resLen) && matchFound) - { - // write out the currency value - u_strcpy(buff, s); - } - else - { - return 0; - } - } - - // return null terminated currency string - return u_terminateUChars(buff, buffCapacity, resLen, ec); - } - else - { - // illegal argument encountered - *ec = U_ILLEGAL_ARGUMENT_ERROR; - } - - } - - // If we got here, either error code is invalid or - // some argument passed is no good. - return resLen; -} - -static const UEnumeration defaultKeywordValues = { - NULL, - NULL, - ulist_close_keyword_values_iterator, - ulist_count_keyword_values, - uenum_unextDefault, - ulist_next_keyword_value, - ulist_reset_keyword_values_iterator -}; - -U_CAPI UEnumeration *U_EXPORT2 ucurr_getKeywordValuesForLocale(const char *key, const char *locale, UBool commonlyUsed, UErrorCode* status) { - // Resolve region - char prefRegion[ULOC_COUNTRY_CAPACITY]; - ulocimp_getRegionForSupplementalData(locale, true, prefRegion, sizeof(prefRegion), status); - - // Read value from supplementalData - UList *values = ulist_createEmptyList(status); - UList *otherValues = ulist_createEmptyList(status); - UEnumeration *en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration)); - if (U_FAILURE(*status) || en == NULL) { - if (en == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - } else { - uprv_free(en); - } - ulist_deleteList(values); - ulist_deleteList(otherValues); - return NULL; - } - memcpy(en, &defaultKeywordValues, sizeof(UEnumeration)); - en->context = values; - - UResourceBundle *bundle = ures_openDirect(U_ICUDATA_CURR, "supplementalData", status); - ures_getByKey(bundle, "CurrencyMap", bundle, status); - UResourceBundle bundlekey, regbndl, curbndl, to; - ures_initStackObject(&bundlekey); - ures_initStackObject(®bndl); - ures_initStackObject(&curbndl); - ures_initStackObject(&to); - - while (U_SUCCESS(*status) && ures_hasNext(bundle)) { - ures_getNextResource(bundle, &bundlekey, status); - if (U_FAILURE(*status)) { - break; - } - const char *region = ures_getKey(&bundlekey); - UBool isPrefRegion = uprv_strcmp(region, prefRegion) == 0 ? true : false; - if (!isPrefRegion && commonlyUsed) { - // With commonlyUsed=true, we do not put - // currencies for other regions in the - // result list. - continue; - } - ures_getByKey(bundle, region, ®bndl, status); - if (U_FAILURE(*status)) { - break; - } - while (U_SUCCESS(*status) && ures_hasNext(®bndl)) { - ures_getNextResource(®bndl, &curbndl, status); - if (ures_getType(&curbndl) != URES_TABLE) { - // Currently, an empty ARRAY is mixed in. - continue; - } - char *curID = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY); - int32_t curIDLength = ULOC_KEYWORDS_CAPACITY; - if (curID == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - break; - } - -#if U_CHARSET_FAMILY==U_ASCII_FAMILY - ures_getUTF8StringByKey(&curbndl, "id", curID, &curIDLength, true, status); - /* optimize - use the utf-8 string */ -#else - { - const UChar* defString = ures_getStringByKey(&curbndl, "id", &curIDLength, status); - if(U_SUCCESS(*status)) { - if(curIDLength+1 > ULOC_KEYWORDS_CAPACITY) { - *status = U_BUFFER_OVERFLOW_ERROR; - } else { - u_UCharsToChars(defString, curID, curIDLength+1); - } - } - } -#endif - - if (U_FAILURE(*status)) { - break; - } - UBool hasTo = false; - ures_getByKey(&curbndl, "to", &to, status); - if (U_FAILURE(*status)) { - // Do nothing here... - *status = U_ZERO_ERROR; - } else { - hasTo = true; - } - if (isPrefRegion && !hasTo && !ulist_containsString(values, curID, (int32_t)uprv_strlen(curID))) { - // Currently active currency for the target country - ulist_addItemEndList(values, curID, true, status); - } else if (!ulist_containsString(otherValues, curID, (int32_t)uprv_strlen(curID)) && !commonlyUsed) { - ulist_addItemEndList(otherValues, curID, true, status); - } else { - uprv_free(curID); - } - } - - } - if (U_SUCCESS(*status)) { - if (commonlyUsed) { - if (ulist_getListSize(values) == 0) { - // This could happen if no valid region is supplied in the input - // locale. In this case, we use the CLDR's default. - uenum_close(en); - en = ucurr_getKeywordValuesForLocale(key, "und", true, status); - } - } else { - // Consolidate the list - char *value = NULL; - ulist_resetList(otherValues); - while ((value = (char *)ulist_getNext(otherValues)) != NULL) { - if (!ulist_containsString(values, value, (int32_t)uprv_strlen(value))) { - char *tmpValue = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY); - uprv_memcpy(tmpValue, value, uprv_strlen(value) + 1); - ulist_addItemEndList(values, tmpValue, true, status); - if (U_FAILURE(*status)) { - break; - } - } - } - } - - ulist_resetList((UList *)(en->context)); - } else { - ulist_deleteList(values); - uprv_free(en); - values = NULL; - en = NULL; - } - ures_close(&to); - ures_close(&curbndl); - ures_close(®bndl); - ures_close(&bundlekey); - ures_close(bundle); - - ulist_deleteList(otherValues); - - return en; -} - - -U_CAPI int32_t U_EXPORT2 -ucurr_getNumericCode(const UChar* currency) { - int32_t code = 0; - if (currency && u_strlen(currency) == ISO_CURRENCY_CODE_LENGTH) { - UErrorCode status = U_ZERO_ERROR; - - UResourceBundle *bundle = ures_openDirect(0, "currencyNumericCodes", &status); - ures_getByKey(bundle, "codeMap", bundle, &status); - if (U_SUCCESS(status)) { - char alphaCode[ISO_CURRENCY_CODE_LENGTH+1]; - myUCharsToChars(alphaCode, currency); - T_CString_toUpperCase(alphaCode); - ures_getByKey(bundle, alphaCode, bundle, &status); - int tmpCode = ures_getInt(bundle, &status); - if (U_SUCCESS(status)) { - code = tmpCode; - } - } - ures_close(bundle); - } - return code; -} -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/ucurr.h" +#include "unicode/locid.h" +#include "unicode/ures.h" +#include "unicode/ustring.h" +#include "unicode/parsepos.h" +#include "unicode/uniset.h" +#include "unicode/usetiter.h" +#include "unicode/utf16.h" +#include "ustr_imp.h" +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "static_unicode_sets.h" +#include "uassert.h" +#include "umutex.h" +#include "ucln_cmn.h" +#include "uenumimp.h" +#include "uhash.h" +#include "hash.h" +#include "uinvchar.h" +#include "uresimp.h" +#include "ulist.h" +#include "uresimp.h" +#include "ureslocs.h" +#include "ulocimp.h" + +using namespace icu; + +//#define UCURR_DEBUG_EQUIV 1 +#ifdef UCURR_DEBUG_EQUIV +#include "stdio.h" +#endif +//#define UCURR_DEBUG 1 +#ifdef UCURR_DEBUG +#include "stdio.h" +#endif + +typedef struct IsoCodeEntry { + const char16_t *isoCode; /* const because it's a reference to a resource bundle string. */ + UDate from; + UDate to; +} IsoCodeEntry; + +//------------------------------------------------------------ +// Constants + +// Default currency meta data of last resort. We try to use the +// defaults encoded in the meta data resource bundle. If there is a +// configuration/build error and these are not available, we use these +// hard-coded defaults (which should be identical). +static const int32_t LAST_RESORT_DATA[] = { 2, 0, 2, 0 }; + +// POW10[i] = 10^i, i=0..MAX_POW10 +static const int32_t POW10[] = { 1, 10, 100, 1000, 10000, 100000, + 1000000, 10000000, 100000000, 1000000000 }; + +static const int32_t MAX_POW10 = UPRV_LENGTHOF(POW10) - 1; + +#define ISO_CURRENCY_CODE_LENGTH 3 + +//------------------------------------------------------------ +// Resource tags +// + +static const char CURRENCY_DATA[] = "supplementalData"; +// Tag for meta-data, in root. +static const char CURRENCY_META[] = "CurrencyMeta"; + +// Tag for map from countries to currencies, in root. +static const char CURRENCY_MAP[] = "CurrencyMap"; + +// Tag for default meta-data, in CURRENCY_META +static const char DEFAULT_META[] = "DEFAULT"; + +// Variant delimiter +static const char VAR_DELIM = '_'; + +// Tag for localized display names (symbols) of currencies +static const char CURRENCIES[] = "Currencies"; +static const char CURRENCIES_NARROW[] = "Currencies%narrow"; +static const char CURRENCIES_FORMAL[] = "Currencies%formal"; +static const char CURRENCIES_VARIANT[] = "Currencies%variant"; +static const char CURRENCYPLURALS[] = "CurrencyPlurals"; + +// ISO codes mapping table +static const UHashtable* gIsoCodes = nullptr; +static icu::UInitOnce gIsoCodesInitOnce {}; + +// Currency symbol equivalances +static const icu::Hashtable* gCurrSymbolsEquiv = nullptr; +static icu::UInitOnce gCurrSymbolsEquivInitOnce {}; + +U_NAMESPACE_BEGIN + +// EquivIterator iterates over all strings that are equivalent to a given +// string, s. Note that EquivIterator will never yield s itself. +class EquivIterator : public icu::UMemory { +public: + // Constructor. hash stores the equivalence relationships; s is the string + // for which we find equivalent strings. + inline EquivIterator(const icu::Hashtable& hash, const icu::UnicodeString& s) + : _hash(hash) { + _start = _current = &s; + } + inline ~EquivIterator() { } + + // next returns the next equivalent string or nullptr if there are no more. + // If s has no equivalent strings, next returns nullptr on the first call. + const icu::UnicodeString *next(); +private: + const icu::Hashtable& _hash; + const icu::UnicodeString* _start; + const icu::UnicodeString* _current; +}; + +const icu::UnicodeString * +EquivIterator::next() { + const icu::UnicodeString* _next = (const icu::UnicodeString*) _hash.get(*_current); + if (_next == nullptr) { + U_ASSERT(_current == _start); + return nullptr; + } + if (*_next == *_start) { + return nullptr; + } + _current = _next; + return _next; +} + +U_NAMESPACE_END + +// makeEquivalent makes lhs and rhs equivalent by updating the equivalence +// relations in hash accordingly. +static void makeEquivalent( + const icu::UnicodeString &lhs, + const icu::UnicodeString &rhs, + icu::Hashtable* hash, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + if (lhs == rhs) { + // already equivalent + return; + } + icu::EquivIterator leftIter(*hash, lhs); + icu::EquivIterator rightIter(*hash, rhs); + const icu::UnicodeString *firstLeft = leftIter.next(); + const icu::UnicodeString *firstRight = rightIter.next(); + const icu::UnicodeString *nextLeft = firstLeft; + const icu::UnicodeString *nextRight = firstRight; + while (nextLeft != nullptr && nextRight != nullptr) { + if (*nextLeft == rhs || *nextRight == lhs) { + // Already equivalent + return; + } + nextLeft = leftIter.next(); + nextRight = rightIter.next(); + } + // Not equivalent. Must join. + icu::UnicodeString *newFirstLeft; + icu::UnicodeString *newFirstRight; + if (firstRight == nullptr && firstLeft == nullptr) { + // Neither lhs or rhs belong to an equivalence circle, so we form + // a new equivalnce circle of just lhs and rhs. + newFirstLeft = new icu::UnicodeString(rhs); + newFirstRight = new icu::UnicodeString(lhs); + } else if (firstRight == nullptr) { + // lhs belongs to an equivalence circle, but rhs does not, so we link + // rhs into lhs' circle. + newFirstLeft = new icu::UnicodeString(rhs); + newFirstRight = new icu::UnicodeString(*firstLeft); + } else if (firstLeft == nullptr) { + // rhs belongs to an equivlance circle, but lhs does not, so we link + // lhs into rhs' circle. + newFirstLeft = new icu::UnicodeString(*firstRight); + newFirstRight = new icu::UnicodeString(lhs); + } else { + // Both lhs and rhs belong to different equivalnce circles. We link + // them together to form one single, larger equivalnce circle. + newFirstLeft = new icu::UnicodeString(*firstRight); + newFirstRight = new icu::UnicodeString(*firstLeft); + } + if (newFirstLeft == nullptr || newFirstRight == nullptr) { + delete newFirstLeft; + delete newFirstRight; + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + hash->put(lhs, (void *) newFirstLeft, status); + hash->put(rhs, (void *) newFirstRight, status); +} + +// countEquivalent counts how many strings are equivalent to s. +// hash stores all the equivalnce relations. +// countEquivalent does not include s itself in the count. +static int32_t countEquivalent(const icu::Hashtable &hash, const icu::UnicodeString &s) { + int32_t result = 0; + icu::EquivIterator iter(hash, s); + while (iter.next() != nullptr) { + ++result; + } +#ifdef UCURR_DEBUG_EQUIV + { + char tmp[200]; + s.extract(0,s.length(),tmp, "UTF-8"); + printf("CountEquivalent('%s') = %d\n", tmp, result); + } +#endif + return result; +} + +static const icu::Hashtable* getCurrSymbolsEquiv(); + +//------------------------------------------------------------ +// Code + +/** + * Cleanup callback func + */ +static UBool U_CALLCONV +isoCodes_cleanup() +{ + if (gIsoCodes != nullptr) { + uhash_close(const_cast(gIsoCodes)); + gIsoCodes = nullptr; + } + gIsoCodesInitOnce.reset(); + return true; +} + +/** + * Cleanup callback func + */ +static UBool U_CALLCONV +currSymbolsEquiv_cleanup() +{ + delete const_cast(gCurrSymbolsEquiv); + gCurrSymbolsEquiv = nullptr; + gCurrSymbolsEquivInitOnce.reset(); + return true; +} + +/** + * Deleter for IsoCodeEntry + */ +static void U_CALLCONV +deleteIsoCodeEntry(void *obj) { + IsoCodeEntry *entry = (IsoCodeEntry*)obj; + uprv_free(entry); +} + +/** + * Deleter for gCurrSymbolsEquiv. + */ +static void U_CALLCONV +deleteUnicode(void *obj) { + icu::UnicodeString *entry = (icu::UnicodeString*)obj; + delete entry; +} + +/** + * Unfortunately, we have to convert the char16_t* currency code to char* + * to use it as a resource key. + */ +static inline char* +myUCharsToChars(char* resultOfLen4, const char16_t* currency) { + u_UCharsToChars(currency, resultOfLen4, ISO_CURRENCY_CODE_LENGTH); + resultOfLen4[ISO_CURRENCY_CODE_LENGTH] = 0; + return resultOfLen4; +} + +/** + * Internal function to look up currency data. Result is an array of + * four integers. The first is the fraction digits. The second is the + * rounding increment, or 0 if none. The rounding increment is in + * units of 10^(-fraction_digits). The third and fourth are the same + * except that they are those used in cash transactions ( cashDigits + * and cashRounding ). + */ +static const int32_t* +_findMetaData(const char16_t* currency, UErrorCode& ec) { + + if (currency == 0 || *currency == 0) { + if (U_SUCCESS(ec)) { + ec = U_ILLEGAL_ARGUMENT_ERROR; + } + return LAST_RESORT_DATA; + } + + // Get CurrencyMeta resource out of root locale file. [This may + // move out of the root locale file later; if it does, update this + // code.] + UResourceBundle* currencyData = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &ec); + UResourceBundle* currencyMeta = ures_getByKey(currencyData, CURRENCY_META, currencyData, &ec); + + if (U_FAILURE(ec)) { + ures_close(currencyMeta); + // Config/build error; return hard-coded defaults + return LAST_RESORT_DATA; + } + + // Look up our currency, or if that's not available, then DEFAULT + char buf[ISO_CURRENCY_CODE_LENGTH+1]; + UErrorCode ec2 = U_ZERO_ERROR; // local error code: soft failure + UResourceBundle* rb = ures_getByKey(currencyMeta, myUCharsToChars(buf, currency), nullptr, &ec2); + if (U_FAILURE(ec2)) { + ures_close(rb); + rb = ures_getByKey(currencyMeta,DEFAULT_META, nullptr, &ec); + if (U_FAILURE(ec)) { + ures_close(currencyMeta); + ures_close(rb); + // Config/build error; return hard-coded defaults + return LAST_RESORT_DATA; + } + } + + int32_t len; + const int32_t *data = ures_getIntVector(rb, &len, &ec); + if (U_FAILURE(ec) || len != 4) { + // Config/build error; return hard-coded defaults + if (U_SUCCESS(ec)) { + ec = U_INVALID_FORMAT_ERROR; + } + ures_close(currencyMeta); + ures_close(rb); + return LAST_RESORT_DATA; + } + + ures_close(currencyMeta); + ures_close(rb); + return data; +} + +// ------------------------------------- + +static void +idForLocale(const char* locale, char* countryAndVariant, int capacity, UErrorCode* ec) +{ + ulocimp_getRegionForSupplementalData(locale, false, countryAndVariant, capacity, ec); +} + +// ------------------------------------------ +// +// Registration +// +//------------------------------------------- + +// don't use ICUService since we don't need fallback + +U_CDECL_BEGIN +static UBool U_CALLCONV currency_cleanup(); +U_CDECL_END + +#if !UCONFIG_NO_SERVICE +struct CReg; + +static UMutex gCRegLock; +static CReg* gCRegHead = 0; + +struct CReg : public icu::UMemory { + CReg *next; + char16_t iso[ISO_CURRENCY_CODE_LENGTH+1]; + char id[ULOC_FULLNAME_CAPACITY]; + + CReg(const char16_t* _iso, const char* _id) + : next(0) + { + int32_t len = (int32_t)uprv_strlen(_id); + if (len > (int32_t)(sizeof(id)-1)) { + len = (sizeof(id)-1); + } + uprv_strncpy(id, _id, len); + id[len] = 0; + u_memcpy(iso, _iso, ISO_CURRENCY_CODE_LENGTH); + iso[ISO_CURRENCY_CODE_LENGTH] = 0; + } + + static UCurrRegistryKey reg(const char16_t* _iso, const char* _id, UErrorCode* status) + { + if (status && U_SUCCESS(*status) && _iso && _id) { + CReg* n = new CReg(_iso, _id); + if (n) { + umtx_lock(&gCRegLock); + if (!gCRegHead) { + /* register for the first time */ + ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); + } + n->next = gCRegHead; + gCRegHead = n; + umtx_unlock(&gCRegLock); + return n; + } + *status = U_MEMORY_ALLOCATION_ERROR; + } + return 0; + } + + static UBool unreg(UCurrRegistryKey key) { + UBool found = false; + umtx_lock(&gCRegLock); + + CReg** p = &gCRegHead; + while (*p) { + if (*p == key) { + *p = ((CReg*)key)->next; + delete (CReg*)key; + found = true; + break; + } + p = &((*p)->next); + } + + umtx_unlock(&gCRegLock); + return found; + } + + static const char16_t* get(const char* id) { + const char16_t* result = nullptr; + umtx_lock(&gCRegLock); + CReg* p = gCRegHead; + + /* register cleanup of the mutex */ + ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); + while (p) { + if (uprv_strcmp(id, p->id) == 0) { + result = p->iso; + break; + } + p = p->next; + } + umtx_unlock(&gCRegLock); + return result; + } + + /* This doesn't need to be thread safe. It's for u_cleanup only. */ + static void cleanup() { + while (gCRegHead) { + CReg* n = gCRegHead; + gCRegHead = gCRegHead->next; + delete n; + } + } +}; + +// ------------------------------------- + +U_CAPI UCurrRegistryKey U_EXPORT2 +ucurr_register(const char16_t* isoCode, const char* locale, UErrorCode *status) +{ + if (status && U_SUCCESS(*status)) { + char id[ULOC_FULLNAME_CAPACITY]; + idForLocale(locale, id, sizeof(id), status); + return CReg::reg(isoCode, id, status); + } + return nullptr; +} + +// ------------------------------------- + +U_CAPI UBool U_EXPORT2 +ucurr_unregister(UCurrRegistryKey key, UErrorCode* status) +{ + if (status && U_SUCCESS(*status)) { + return CReg::unreg(key); + } + return false; +} +#endif /* UCONFIG_NO_SERVICE */ + +// ------------------------------------- + +/** + * Release all static memory held by currency. + */ +/*The declaration here is needed so currency_cleanup() + * can call this function. + */ +static UBool U_CALLCONV +currency_cache_cleanup(); + +U_CDECL_BEGIN +static UBool U_CALLCONV currency_cleanup() { +#if !UCONFIG_NO_SERVICE + CReg::cleanup(); +#endif + /* + * There might be some cached currency data or isoCodes data. + */ + currency_cache_cleanup(); + isoCodes_cleanup(); + currSymbolsEquiv_cleanup(); + + return true; +} +U_CDECL_END + +// ------------------------------------- + +U_CAPI int32_t U_EXPORT2 +ucurr_forLocale(const char* locale, + char16_t* buff, + int32_t buffCapacity, + UErrorCode* ec) { + if (U_FAILURE(*ec)) { return 0; } + if (buffCapacity < 0 || (buff == nullptr && buffCapacity > 0)) { + *ec = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + char currency[4]; // ISO currency codes are alpha3 codes. + UErrorCode localStatus = U_ZERO_ERROR; + int32_t resLen = uloc_getKeywordValue(locale, "currency", + currency, UPRV_LENGTHOF(currency), &localStatus); + if (U_SUCCESS(localStatus) && resLen == 3 && uprv_isInvariantString(currency, resLen)) { + if (resLen < buffCapacity) { + T_CString_toUpperCase(currency); + u_charsToUChars(currency, buff, resLen); + } + return u_terminateUChars(buff, buffCapacity, resLen, ec); + } + + // get country or country_variant in `id' + char id[ULOC_FULLNAME_CAPACITY]; + idForLocale(locale, id, UPRV_LENGTHOF(id), ec); + if (U_FAILURE(*ec)) { + return 0; + } + +#if !UCONFIG_NO_SERVICE + const char16_t* result = CReg::get(id); + if (result) { + if(buffCapacity > u_strlen(result)) { + u_strcpy(buff, result); + } + resLen = u_strlen(result); + return u_terminateUChars(buff, buffCapacity, resLen, ec); + } +#endif + // Remove variants, which is only needed for registration. + char *idDelim = uprv_strchr(id, VAR_DELIM); + if (idDelim) { + idDelim[0] = 0; + } + + const char16_t* s = nullptr; // Currency code from data file. + if (id[0] == 0) { + // No point looking in the data for an empty string. + // This is what we would get. + localStatus = U_MISSING_RESOURCE_ERROR; + } else { + // Look up the CurrencyMap element in the root bundle. + localStatus = U_ZERO_ERROR; + UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); + UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); + UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus); + // https://unicode-org.atlassian.net/browse/ICU-21997 + // Prefer to use currencies that are legal tender. + if (U_SUCCESS(localStatus)) { + int32_t arrayLength = ures_getSize(countryArray); + for (int32_t i = 0; i < arrayLength; ++i) { + LocalUResourceBundlePointer currencyReq( + ures_getByIndex(countryArray, i, nullptr, &localStatus)); + // The currency is legal tender if it is *not* marked with tender{"false"}. + UErrorCode tenderStatus = localStatus; + const char16_t *tender = + ures_getStringByKey(currencyReq.getAlias(), "tender", nullptr, &tenderStatus); + bool isTender = U_FAILURE(tenderStatus) || u_strcmp(tender, u"false") != 0; + if (!isTender && s != nullptr) { + // We already have a non-tender currency. Ignore all following non-tender ones. + continue; + } + // Fetch the currency code. + s = ures_getStringByKey(currencyReq.getAlias(), "id", &resLen, &localStatus); + if (isTender) { + break; + } + } + if (U_SUCCESS(localStatus) && s == nullptr) { + localStatus = U_MISSING_RESOURCE_ERROR; + } + } + ures_close(countryArray); + } + + if ((U_FAILURE(localStatus)) && strchr(id, '_') != 0) { + // We don't know about it. Check to see if we support the variant. + uloc_getParent(locale, id, UPRV_LENGTHOF(id), ec); + *ec = U_USING_FALLBACK_WARNING; + // TODO: Loop over the shortened id rather than recursing and + // looking again for a currency keyword. + return ucurr_forLocale(id, buff, buffCapacity, ec); + } + if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) { + // There is nothing to fallback to. Report the failure/warning if possible. + *ec = localStatus; + } + if (U_SUCCESS(*ec)) { + if(buffCapacity > resLen) { + u_strcpy(buff, s); + } + } + return u_terminateUChars(buff, buffCapacity, resLen, ec); +} + +// end registration + +/** + * Modify the given locale name by removing the rightmost _-delimited + * element. If there is none, empty the string ("" == root). + * NOTE: The string "root" is not recognized; do not use it. + * @return true if the fallback happened; false if locale is already + * root (""). + */ +static UBool fallback(char *loc) { + if (!*loc) { + return false; + } + UErrorCode status = U_ZERO_ERROR; + if (uprv_strcmp(loc, "en_GB") == 0) { + // HACK: See #13368. We need "en_GB" to fall back to "en_001" instead of "en" + // in order to consume the correct data strings. This hack will be removed + // when proper data sink loading is implemented here. + // NOTE: "001" adds 1 char over "GB". However, both call sites allocate + // arrays with length ULOC_FULLNAME_CAPACITY (plenty of room for en_001). + uprv_strcpy(loc + 3, "001"); + } else { + uloc_getParent(loc, loc, (int32_t)uprv_strlen(loc), &status); + } + /* + char *i = uprv_strrchr(loc, '_'); + if (i == nullptr) { + i = loc; + } + *i = 0; + */ + return true; +} + + +U_CAPI const char16_t* U_EXPORT2 +ucurr_getName(const char16_t* currency, + const char* locale, + UCurrNameStyle nameStyle, + UBool* isChoiceFormat, // fillin + int32_t* len, // fillin + UErrorCode* ec) { + + // Look up the Currencies resource for the given locale. The + // Currencies locale data looks like this: + //|en { + //| Currencies { + //| USD { "US$", "US Dollar" } + //| CHF { "Sw F", "Swiss Franc" } + //| INR { "=0#Rs|1#Re|1 4) { + *ec = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + // In the future, resource bundles may implement multi-level + // fallback. That is, if a currency is not found in the en_US + // Currencies data, then the en Currencies data will be searched. + // Currently, if a Currencies datum exists in en_US and en, the + // en_US entry hides that in en. + + // We want multi-level fallback for this resource, so we implement + // it manually. + + // Use a separate UErrorCode here that does not propagate out of + // this function. + UErrorCode ec2 = U_ZERO_ERROR; + + char loc[ULOC_FULLNAME_CAPACITY]; + uloc_getName(locale, loc, sizeof(loc), &ec2); + if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) { + *ec = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + char buf[ISO_CURRENCY_CODE_LENGTH+1]; + myUCharsToChars(buf, currency); + + /* Normalize the keyword value to uppercase */ + T_CString_toUpperCase(buf); + + const char16_t* s = nullptr; + ec2 = U_ZERO_ERROR; + LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_CURR, loc, &ec2)); + + if (nameStyle == UCURR_NARROW_SYMBOL_NAME || nameStyle == UCURR_FORMAL_SYMBOL_NAME || nameStyle == UCURR_VARIANT_SYMBOL_NAME) { + CharString key; + switch (nameStyle) { + case UCURR_NARROW_SYMBOL_NAME: + key.append(CURRENCIES_NARROW, ec2); + break; + case UCURR_FORMAL_SYMBOL_NAME: + key.append(CURRENCIES_FORMAL, ec2); + break; + case UCURR_VARIANT_SYMBOL_NAME: + key.append(CURRENCIES_VARIANT, ec2); + break; + default: + *ec = U_UNSUPPORTED_ERROR; + return 0; + } + key.append("/", ec2); + key.append(buf, ec2); + s = ures_getStringByKeyWithFallback(rb.getAlias(), key.data(), len, &ec2); + if (ec2 == U_MISSING_RESOURCE_ERROR) { + *ec = U_USING_FALLBACK_WARNING; + ec2 = U_ZERO_ERROR; + choice = UCURR_SYMBOL_NAME; + } + } + if (s == nullptr) { + ures_getByKey(rb.getAlias(), CURRENCIES, rb.getAlias(), &ec2); + ures_getByKeyWithFallback(rb.getAlias(), buf, rb.getAlias(), &ec2); + s = ures_getStringByIndex(rb.getAlias(), choice, len, &ec2); + } + + // If we've succeeded we're done. Otherwise, try to fallback. + // If that fails (because we are already at root) then exit. + if (U_SUCCESS(ec2)) { + if (ec2 == U_USING_DEFAULT_WARNING + || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) { + *ec = ec2; + } + } + + // We no longer support choice format data in names. Data should not contain + // choice patterns. + if (isChoiceFormat != nullptr) { + *isChoiceFormat = false; + } + if (U_SUCCESS(ec2)) { + U_ASSERT(s != nullptr); + return s; + } + + // If we fail to find a match, use the ISO 4217 code + *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...? + *ec = U_USING_DEFAULT_WARNING; + return currency; +} + +U_CAPI const char16_t* U_EXPORT2 +ucurr_getPluralName(const char16_t* currency, + const char* locale, + UBool* isChoiceFormat, + const char* pluralCount, + int32_t* len, // fillin + UErrorCode* ec) { + // Look up the Currencies resource for the given locale. The + // Currencies locale data looks like this: + //|en { + //| CurrencyPlurals { + //| USD{ + //| one{"US dollar"} + //| other{"US dollars"} + //| } + //| } + //|} + + if (U_FAILURE(*ec)) { + return 0; + } + + // Use a separate UErrorCode here that does not propagate out of + // this function. + UErrorCode ec2 = U_ZERO_ERROR; + + char loc[ULOC_FULLNAME_CAPACITY]; + uloc_getName(locale, loc, sizeof(loc), &ec2); + if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) { + *ec = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + char buf[ISO_CURRENCY_CODE_LENGTH+1]; + myUCharsToChars(buf, currency); + + const char16_t* s = nullptr; + ec2 = U_ZERO_ERROR; + UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2); + + rb = ures_getByKey(rb, CURRENCYPLURALS, rb, &ec2); + + // Fetch resource with multi-level resource inheritance fallback + rb = ures_getByKeyWithFallback(rb, buf, rb, &ec2); + + s = ures_getStringByKeyWithFallback(rb, pluralCount, len, &ec2); + if (U_FAILURE(ec2)) { + // fall back to "other" + ec2 = U_ZERO_ERROR; + s = ures_getStringByKeyWithFallback(rb, "other", len, &ec2); + if (U_FAILURE(ec2)) { + ures_close(rb); + // fall back to long name in Currencies + return ucurr_getName(currency, locale, UCURR_LONG_NAME, + isChoiceFormat, len, ec); + } + } + ures_close(rb); + + // If we've succeeded we're done. Otherwise, try to fallback. + // If that fails (because we are already at root) then exit. + if (U_SUCCESS(ec2)) { + if (ec2 == U_USING_DEFAULT_WARNING + || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) { + *ec = ec2; + } + U_ASSERT(s != nullptr); + return s; + } + + // If we fail to find a match, use the ISO 4217 code + *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...? + *ec = U_USING_DEFAULT_WARNING; + return currency; +} + + +//======================================================================== +// Following are structure and function for parsing currency names + +#define NEED_TO_BE_DELETED 0x1 + +// TODO: a better way to define this? +#define MAX_CURRENCY_NAME_LEN 100 + +typedef struct { + const char* IsoCode; // key + char16_t* currencyName; // value + int32_t currencyNameLen; // value length + int32_t flag; // flags +} CurrencyNameStruct; + + +#ifndef MIN +#define MIN(a,b) (((a)<(b)) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a,b) (((a)<(b)) ? (b) : (a)) +#endif + + +// Comparison function used in quick sort. +static int U_CALLCONV currencyNameComparator(const void* a, const void* b) { + const CurrencyNameStruct* currName_1 = (const CurrencyNameStruct*)a; + const CurrencyNameStruct* currName_2 = (const CurrencyNameStruct*)b; + for (int32_t i = 0; + i < MIN(currName_1->currencyNameLen, currName_2->currencyNameLen); + ++i) { + if (currName_1->currencyName[i] < currName_2->currencyName[i]) { + return -1; + } + if (currName_1->currencyName[i] > currName_2->currencyName[i]) { + return 1; + } + } + if (currName_1->currencyNameLen < currName_2->currencyNameLen) { + return -1; + } else if (currName_1->currencyNameLen > currName_2->currencyNameLen) { + return 1; + } + return 0; +} + + +// Give a locale, return the maximum number of currency names associated with +// this locale. +// It gets currency names from resource bundles using fallback. +// It is the maximum number because in the fallback chain, some of the +// currency names are duplicated. +// For example, given locale as "en_US", the currency names get from resource +// bundle in "en_US" and "en" are duplicated. The fallback mechanism will count +// all currency names in "en_US" and "en". +static void +getCurrencyNameCount(const char* loc, int32_t* total_currency_name_count, int32_t* total_currency_symbol_count) { + U_NAMESPACE_USE + *total_currency_name_count = 0; + *total_currency_symbol_count = 0; + const char16_t* s = nullptr; + char locale[ULOC_FULLNAME_CAPACITY] = ""; + uprv_strcpy(locale, loc); + const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv(); + for (;;) { + UErrorCode ec2 = U_ZERO_ERROR; + // TODO: ures_openDirect? + UResourceBundle* rb = ures_open(U_ICUDATA_CURR, locale, &ec2); + UResourceBundle* curr = ures_getByKey(rb, CURRENCIES, nullptr, &ec2); + int32_t n = ures_getSize(curr); + for (int32_t i=0; i(symbol->getBuffer()); + (*currencySymbols)[*total_currency_symbol_count].flag = 0; + (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = symbol->length(); + } + } + + // Add currency long name. + s = ures_getStringByIndex(names, UCURR_LONG_NAME, &len, &ec2); + (*currencyNames)[*total_currency_name_count].IsoCode = iso; + char16_t* upperName = toUpperCase(s, len, locale); + (*currencyNames)[*total_currency_name_count].currencyName = upperName; + (*currencyNames)[*total_currency_name_count].flag = NEED_TO_BE_DELETED; + (*currencyNames)[(*total_currency_name_count)++].currencyNameLen = len; + + // put (iso, 3, and iso) in to array + // Add currency ISO code. + (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso; + (*currencySymbols)[*total_currency_symbol_count].currencyName = (char16_t*)uprv_malloc(sizeof(char16_t)*3); + // Must convert iso[] into Unicode + u_charsToUChars(iso, (*currencySymbols)[*total_currency_symbol_count].currencyName, 3); + (*currencySymbols)[*total_currency_symbol_count].flag = NEED_TO_BE_DELETED; + (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = 3; + + ures_close(names); + } + + // currency plurals + UErrorCode ec5 = U_ZERO_ERROR; + UResourceBundle* curr_p = ures_getByKey(rb, CURRENCYPLURALS, nullptr, &ec5); + n = ures_getSize(curr_p); + for (int32_t i=0; i= currencyNames[mid].currencyNameLen) { + first = mid + 1; + } else { + if (key > currencyNames[mid].currencyName[indexInCurrencyNames]) { + first = mid + 1; + } + else if (key < currencyNames[mid].currencyName[indexInCurrencyNames]) { + last = mid - 1; + } + else { + // Find a match, and looking for ranges + // Now do two more binary searches. First, on the left side for + // the greatest L such that CurrencyNameStruct[L] < key. + int32_t L = *begin; + int32_t R = mid; + +#ifdef UCURR_DEBUG + printf("mid = %d\n", mid); +#endif + while (L < R) { + int32_t M = (L + R) / 2; +#ifdef UCURR_DEBUG + printf("L = %d, R = %d, M = %d\n", L, R, M); +#endif + if (indexInCurrencyNames >= currencyNames[M].currencyNameLen) { + L = M + 1; + } else { + if (currencyNames[M].currencyName[indexInCurrencyNames] < key) { + L = M + 1; + } else { +#ifdef UCURR_DEBUG + U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key); +#endif + R = M; + } + } + } +#ifdef UCURR_DEBUG + U_ASSERT(L == R); +#endif + *begin = L; +#ifdef UCURR_DEBUG + printf("begin = %d\n", *begin); + U_ASSERT(currencyNames[*begin].currencyName[indexInCurrencyNames] == key); +#endif + + // Now for the second search, finding the least R such that + // key < CurrencyNameStruct[R]. + L = mid; + R = *end; + while (L < R) { + int32_t M = (L + R) / 2; +#ifdef UCURR_DEBUG + printf("L = %d, R = %d, M = %d\n", L, R, M); +#endif + if (currencyNames[M].currencyNameLen < indexInCurrencyNames) { + L = M + 1; + } else { + if (currencyNames[M].currencyName[indexInCurrencyNames] > key) { + R = M; + } else { +#ifdef UCURR_DEBUG + U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key); +#endif + L = M + 1; + } + } + } +#ifdef UCURR_DEBUG + U_ASSERT(L == R); +#endif + if (currencyNames[R].currencyName[indexInCurrencyNames] > key) { + *end = R - 1; + } else { + *end = R; + } +#ifdef UCURR_DEBUG + printf("end = %d\n", *end); +#endif + + // now, found the range. check whether there is exact match + if (currencyNames[*begin].currencyNameLen == indexInCurrencyNames + 1) { + return *begin; // find range and exact match. + } + return -1; // find range, but no exact match. + } + } + } + *begin = -1; + *end = -1; + return -1; // failed to find range. +} + + +// Linear search "text" in "currencyNames". +// @param begin, end: the begin and end index in currencyNames, within which +// range should the search be performed. +// @param textLen: the length of the text to be compared +// @param maxMatchLen(IN/OUT): passing in the computed max matching length +// pass out the new max matching length +// @param maxMatchIndex: the index in currencyName which has the longest +// match with input text. +static void +linearSearch(const CurrencyNameStruct* currencyNames, + int32_t begin, int32_t end, + const char16_t* text, int32_t textLen, + int32_t *partialMatchLen, + int32_t *maxMatchLen, int32_t* maxMatchIndex) { + int32_t initialPartialMatchLen = *partialMatchLen; + for (int32_t index = begin; index <= end; ++index) { + int32_t len = currencyNames[index].currencyNameLen; + if (len > *maxMatchLen && len <= textLen && + uprv_memcmp(currencyNames[index].currencyName, text, len * sizeof(char16_t)) == 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, entry->totalCurrencyNameCount); + deleteCurrencyNames(entry->currencySymbols, entry->totalCurrencySymbolCount); + uprv_free(entry); +} + + +// Cache clean up +static UBool U_CALLCONV +currency_cache_cleanup() { + for (int32_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) { + if (currCache[i]) { + deleteCacheEntry(currCache[i]); + currCache[i] = 0; + } + } + return true; +} + + +/** + * Loads the currency name data from the cache, or from resource bundles if necessary. + * The refCount is automatically incremented. It is the caller's responsibility + * to decrement it when done! + */ +static CurrencyNameCacheEntry* +getCacheEntry(const char* locale, UErrorCode& ec) { + + int32_t total_currency_name_count = 0; + CurrencyNameStruct* currencyNames = nullptr; + int32_t total_currency_symbol_count = 0; + CurrencyNameStruct* currencySymbols = nullptr; + CurrencyNameCacheEntry* cacheEntry = nullptr; + + umtx_lock(&gCurrencyCacheMutex); + // in order to handle racing correctly, + // not putting 'search' in a separate function. + int8_t found = -1; + for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) { + if (currCache[i]!= nullptr && + uprv_strcmp(locale, currCache[i]->locale) == 0) { + found = i; + break; + } + } + if (found != -1) { + cacheEntry = currCache[found]; + ++(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 nullptr; + } + umtx_lock(&gCurrencyCacheMutex); + // check again. + for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) { + if (currCache[i]!= nullptr && + uprv_strcmp(locale, currCache[i]->locale) == 0) { + found = i; + break; + } + } + if (found == -1) { + // insert new entry to + // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM + // and remove the existing entry + // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM + // from cache. + cacheEntry = currCache[currentCacheEntryIndex]; + if (cacheEntry) { + --(cacheEntry->refCount); + // delete if the ref count is zero + if (cacheEntry->refCount == 0) { + deleteCacheEntry(cacheEntry); + } + } + cacheEntry = (CurrencyNameCacheEntry*)uprv_malloc(sizeof(CurrencyNameCacheEntry)); + currCache[currentCacheEntryIndex] = cacheEntry; + uprv_strcpy(cacheEntry->locale, locale); + cacheEntry->currencyNames = currencyNames; + cacheEntry->totalCurrencyNameCount = total_currency_name_count; + cacheEntry->currencySymbols = currencySymbols; + 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_cleanup); + } else { + deleteCurrencyNames(currencyNames, total_currency_name_count); + deleteCurrencyNames(currencySymbols, total_currency_symbol_count); + cacheEntry = currCache[found]; + ++(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, + char16_t* 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(); + + char16_t inputText[MAX_CURRENCY_NAME_LEN]; + char16_t upperText[MAX_CURRENCY_NAME_LEN]; + int32_t textLen = MIN(MAX_CURRENCY_NAME_LEN, text.length() - start); + text.extract(start, textLen, inputText); + 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 comparison against currency names + searchCurrencyName(currencyNames, total_currency_name_count, + upperText, textLen, partialMatchLen, &max, &matchIndex); + +#ifdef UCURR_DEBUG + printf("search in names, max = %d, matchIndex = %d\n", max, matchIndex); +#endif + + int32_t maxInSymbol = 0; + int32_t matchIndexInSymbol = -1; + if (type != UCURR_LONG_NAME) { // not name only + // case sensitive comparison against currency symbols and ISO code. + searchCurrencyName(currencySymbols, total_currency_symbol_count, + inputText, textLen, + partialMatchLen, + &maxInSymbol, &matchIndexInSymbol); + } + +#ifdef UCURR_DEBUG + printf("search in symbols, maxInSymbol = %d, matchIndexInSymbol = %d\n", maxInSymbol, matchIndexInSymbol); + if(matchIndexInSymbol != -1) { + printf("== ISO=%s\n", currencySymbols[matchIndexInSymbol].IsoCode); + } +#endif + + if (max >= maxInSymbol && matchIndex != -1) { + u_charsToUChars(currencyNames[matchIndex].IsoCode, result, 4); + pos.setIndex(start + max); + } else if (maxInSymbol >= max && matchIndexInSymbol != -1) { + u_charsToUChars(currencySymbols[matchIndexInSymbol].IsoCode, result, 4); + pos.setIndex(start + maxInSymbol); + } + + // decrease reference count + releaseCacheEntry(cacheEntry); +} + +void uprv_currencyLeads(const char* locale, icu::UnicodeSet& result, UErrorCode& ec) { + U_NAMESPACE_USE + if (U_FAILURE(ec)) { + return; + } + 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); +} + + +/** + * Internal method. Given a currency ISO code and a locale, return + * the "static" currency name. This is usually the same as the + * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the + * format is applied to the number 2.0 (to yield the more common + * plural) to return a static name. + * + * This is used for backward compatibility with old currency logic in + * DecimalFormat and DecimalFormatSymbols. + */ +U_CAPI void +uprv_getStaticCurrencyName(const char16_t* iso, const char* loc, + icu::UnicodeString& result, UErrorCode& ec) +{ + U_NAMESPACE_USE + + int32_t len; + const char16_t* currname = ucurr_getName(iso, loc, UCURR_SYMBOL_NAME, + nullptr /* isChoiceFormat */, &len, &ec); + if (U_SUCCESS(ec)) { + result.setTo(currname, len); + } +} + +U_CAPI int32_t U_EXPORT2 +ucurr_getDefaultFractionDigits(const char16_t* currency, UErrorCode* ec) { + return ucurr_getDefaultFractionDigitsForUsage(currency,UCURR_USAGE_STANDARD,ec); +} + +U_CAPI int32_t U_EXPORT2 +ucurr_getDefaultFractionDigitsForUsage(const char16_t* currency, const UCurrencyUsage usage, UErrorCode* ec) { + int32_t fracDigits = 0; + if (U_SUCCESS(*ec)) { + switch (usage) { + case UCURR_USAGE_STANDARD: + fracDigits = (_findMetaData(currency, *ec))[0]; + break; + case UCURR_USAGE_CASH: + fracDigits = (_findMetaData(currency, *ec))[2]; + break; + default: + *ec = U_UNSUPPORTED_ERROR; + } + } + return fracDigits; +} + +U_CAPI double U_EXPORT2 +ucurr_getRoundingIncrement(const char16_t* currency, UErrorCode* ec) { + return ucurr_getRoundingIncrementForUsage(currency, UCURR_USAGE_STANDARD, ec); +} + +U_CAPI double U_EXPORT2 +ucurr_getRoundingIncrementForUsage(const char16_t* currency, const UCurrencyUsage usage, UErrorCode* ec) { + double result = 0.0; + + const int32_t *data = _findMetaData(currency, *ec); + if (U_SUCCESS(*ec)) { + int32_t fracDigits; + int32_t increment; + switch (usage) { + case UCURR_USAGE_STANDARD: + fracDigits = data[0]; + increment = data[1]; + break; + case UCURR_USAGE_CASH: + fracDigits = data[2]; + increment = data[3]; + break; + default: + *ec = U_UNSUPPORTED_ERROR; + return result; + } + + // If the meta data is invalid, return 0.0 + if (fracDigits < 0 || fracDigits > MAX_POW10) { + *ec = U_INVALID_FORMAT_ERROR; + } else { + // A rounding value of 0 or 1 indicates no rounding. + if (increment >= 2) { + // Return (increment) / 10^(fracDigits). The only actual rounding data, + // as of this writing, is CHF { 2, 5 }. + result = double(increment) / POW10[fracDigits]; + } + } + } + + return result; +} + +U_CDECL_BEGIN + +typedef struct UCurrencyContext { + uint32_t currType; /* UCurrCurrencyType */ + uint32_t listIdx; +} UCurrencyContext; + +/* +Please keep this list in alphabetical order. +You can look at the CLDR supplemental data or ISO-4217 for the meaning of some +of these items. +ISO-4217: http://www.iso.org/iso/en/prods-services/popstds/currencycodeslist.html +*/ +static const struct CurrencyList { + const char *currency; + uint32_t currType; +} gCurrencyList[] = { + {"ADP", UCURR_COMMON|UCURR_DEPRECATED}, + {"AED", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"AFA", UCURR_COMMON|UCURR_DEPRECATED}, + {"AFN", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"ALK", UCURR_COMMON|UCURR_DEPRECATED}, + {"ALL", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"AMD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"ANG", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"AOA", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"AOK", UCURR_COMMON|UCURR_DEPRECATED}, + {"AON", UCURR_COMMON|UCURR_DEPRECATED}, + {"AOR", UCURR_COMMON|UCURR_DEPRECATED}, + {"ARA", UCURR_COMMON|UCURR_DEPRECATED}, + {"ARL", UCURR_COMMON|UCURR_DEPRECATED}, + {"ARM", UCURR_COMMON|UCURR_DEPRECATED}, + {"ARP", UCURR_COMMON|UCURR_DEPRECATED}, + {"ARS", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"ATS", UCURR_COMMON|UCURR_DEPRECATED}, + {"AUD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"AWG", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"AZM", UCURR_COMMON|UCURR_DEPRECATED}, + {"AZN", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BAD", UCURR_COMMON|UCURR_DEPRECATED}, + {"BAM", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BAN", UCURR_COMMON|UCURR_DEPRECATED}, + {"BBD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BDT", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BEC", UCURR_UNCOMMON|UCURR_DEPRECATED}, + {"BEF", UCURR_COMMON|UCURR_DEPRECATED}, + {"BEL", UCURR_UNCOMMON|UCURR_DEPRECATED}, + {"BGL", UCURR_COMMON|UCURR_DEPRECATED}, + {"BGM", UCURR_COMMON|UCURR_DEPRECATED}, + {"BGN", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BGO", UCURR_COMMON|UCURR_DEPRECATED}, + {"BHD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BIF", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BMD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BND", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BOB", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BOL", UCURR_COMMON|UCURR_DEPRECATED}, + {"BOP", UCURR_COMMON|UCURR_DEPRECATED}, + {"BOV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"BRB", UCURR_COMMON|UCURR_DEPRECATED}, + {"BRC", UCURR_COMMON|UCURR_DEPRECATED}, + {"BRE", UCURR_COMMON|UCURR_DEPRECATED}, + {"BRL", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BRN", UCURR_COMMON|UCURR_DEPRECATED}, + {"BRR", UCURR_COMMON|UCURR_DEPRECATED}, + {"BRZ", UCURR_COMMON|UCURR_DEPRECATED}, + {"BSD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BTN", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BUK", UCURR_COMMON|UCURR_DEPRECATED}, + {"BWP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BYB", UCURR_COMMON|UCURR_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}, + {"CHE", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"CHF", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"CHW", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"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}, + {"COU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"CRC", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"CSD", UCURR_COMMON|UCURR_DEPRECATED}, + {"CSK", UCURR_COMMON|UCURR_DEPRECATED}, + {"CUC", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"CUP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"CVE", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"CYP", UCURR_COMMON|UCURR_DEPRECATED}, + {"CZK", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"DDM", UCURR_COMMON|UCURR_DEPRECATED}, + {"DEM", UCURR_COMMON|UCURR_DEPRECATED}, + {"DJF", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"DKK", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"DOP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"DZD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"ECS", UCURR_COMMON|UCURR_DEPRECATED}, + {"ECV", UCURR_UNCOMMON|UCURR_DEPRECATED}, + {"EEK", UCURR_COMMON|UCURR_DEPRECATED}, + {"EGP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"ERN", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"ESA", UCURR_UNCOMMON|UCURR_DEPRECATED}, + {"ESB", UCURR_UNCOMMON|UCURR_DEPRECATED}, + {"ESP", UCURR_COMMON|UCURR_DEPRECATED}, + {"ETB", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"EUR", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"FIM", UCURR_COMMON|UCURR_DEPRECATED}, + {"FJD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"FKP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"FRF", UCURR_COMMON|UCURR_DEPRECATED}, + {"GBP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"GEK", UCURR_COMMON|UCURR_DEPRECATED}, + {"GEL", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"GHC", UCURR_COMMON|UCURR_DEPRECATED}, + {"GHS", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"GIP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"GMD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"GNF", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"GNS", UCURR_COMMON|UCURR_DEPRECATED}, + {"GQE", UCURR_COMMON|UCURR_DEPRECATED}, + {"GRD", UCURR_COMMON|UCURR_DEPRECATED}, + {"GTQ", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"GWE", UCURR_COMMON|UCURR_DEPRECATED}, + {"GWP", UCURR_COMMON|UCURR_DEPRECATED}, + {"GYD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"HKD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"HNL", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"HRD", UCURR_COMMON|UCURR_DEPRECATED}, + {"HRK", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"HTG", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"HUF", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"IDR", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"IEP", UCURR_COMMON|UCURR_DEPRECATED}, + {"ILP", UCURR_COMMON|UCURR_DEPRECATED}, + {"ILR", UCURR_COMMON|UCURR_DEPRECATED}, + {"ILS", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"INR", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"IQD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"IRR", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"ISJ", UCURR_COMMON|UCURR_DEPRECATED}, + {"ISK", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"ITL", UCURR_COMMON|UCURR_DEPRECATED}, + {"JMD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"JOD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"JPY", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"KES", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"KGS", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"KHR", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"KMF", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"KPW", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"KRH", UCURR_COMMON|UCURR_DEPRECATED}, + {"KRO", UCURR_COMMON|UCURR_DEPRECATED}, + {"KRW", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"KWD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"KYD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"KZT", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"LAK", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"LBP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"LKR", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"LRD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"LSL", 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_DEPRECATED}, + {"LVR", UCURR_COMMON|UCURR_DEPRECATED}, + {"LYD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"MAD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"MAF", UCURR_COMMON|UCURR_DEPRECATED}, + {"MCF", UCURR_COMMON|UCURR_DEPRECATED}, + {"MDC", UCURR_COMMON|UCURR_DEPRECATED}, + {"MDL", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"MGA", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"MGF", UCURR_COMMON|UCURR_DEPRECATED}, + {"MKD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"MKN", UCURR_COMMON|UCURR_DEPRECATED}, + {"MLF", UCURR_COMMON|UCURR_DEPRECATED}, + {"MMK", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"MNT", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"MOP", 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}, // 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_DEPRECATED}, + {"MZM", UCURR_COMMON|UCURR_DEPRECATED}, + {"MZN", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"NAD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"NGN", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"NIC", UCURR_COMMON|UCURR_DEPRECATED}, + {"NIO", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"NLG", UCURR_COMMON|UCURR_DEPRECATED}, + {"NOK", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"NPR", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"NZD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"OMR", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"PAB", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"PEI", UCURR_COMMON|UCURR_DEPRECATED}, + {"PEN", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"PES", UCURR_COMMON|UCURR_DEPRECATED}, + {"PGK", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"PHP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"PKR", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"PLN", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"PLZ", UCURR_COMMON|UCURR_DEPRECATED}, + {"PTE", UCURR_COMMON|UCURR_DEPRECATED}, + {"PYG", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"QAR", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"RHD", UCURR_COMMON|UCURR_DEPRECATED}, + {"ROL", UCURR_COMMON|UCURR_DEPRECATED}, + {"RON", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"RSD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"RUB", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"RUR", UCURR_COMMON|UCURR_DEPRECATED}, + {"RWF", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"SAR", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"SBD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"SCR", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"SDD", UCURR_COMMON|UCURR_DEPRECATED}, + {"SDG", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"SDP", UCURR_COMMON|UCURR_DEPRECATED}, + {"SEK", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"SGD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"SHP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"SIT", UCURR_COMMON|UCURR_DEPRECATED}, + {"SKK", UCURR_COMMON|UCURR_DEPRECATED}, + {"SLE", UCURR_COMMON|UCURR_NON_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_DEPRECATED}, + {"STN", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"SUR", UCURR_COMMON|UCURR_DEPRECATED}, + {"SVC", UCURR_COMMON|UCURR_DEPRECATED}, + {"SYP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"SZL", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"THB", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"TJR", UCURR_COMMON|UCURR_DEPRECATED}, + {"TJS", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"TMM", UCURR_COMMON|UCURR_DEPRECATED}, + {"TMT", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"TND", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"TOP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"TPE", UCURR_COMMON|UCURR_DEPRECATED}, + {"TRL", UCURR_COMMON|UCURR_DEPRECATED}, + {"TRY", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"TTD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"TWD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"TZS", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"UAH", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"UAK", UCURR_COMMON|UCURR_DEPRECATED}, + {"UGS", UCURR_COMMON|UCURR_DEPRECATED}, + {"UGX", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"USD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"USN", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"USS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"UYI", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"UYP", UCURR_COMMON|UCURR_DEPRECATED}, + {"UYU", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"UYW", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"UZS", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"VEB", UCURR_COMMON|UCURR_DEPRECATED}, + {"VED", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"VEF", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"VES", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"VND", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"VNN", UCURR_COMMON|UCURR_DEPRECATED}, + {"VUV", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"WST", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"XAF", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"XAG", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"XAU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"XBA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"XBB", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"XBC", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"XBD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"XCD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"XDR", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"XEU", UCURR_UNCOMMON|UCURR_DEPRECATED}, + {"XFO", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"XFU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"XOF", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"XPD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"XPF", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"XPT", 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}, + {"XXX", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"YDD", UCURR_COMMON|UCURR_DEPRECATED}, + {"YER", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"YUD", UCURR_COMMON|UCURR_DEPRECATED}, + {"YUM", UCURR_COMMON|UCURR_DEPRECATED}, + {"YUN", UCURR_COMMON|UCURR_DEPRECATED}, + {"YUR", UCURR_COMMON|UCURR_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}, + { nullptr, 0 } // Leave here to denote the end of the list. +}; + +#define UCURR_MATCHES_BITMASK(variable, typeToMatch) \ + ((typeToMatch) == UCURR_ALL || ((variable) & (typeToMatch)) == (typeToMatch)) + +static int32_t U_CALLCONV +ucurr_countCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) { + UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context); + uint32_t currType = myContext->currType; + int32_t count = 0; + + /* Count the number of items matching the type we are looking for. */ + for (int32_t idx = 0; gCurrencyList[idx].currency != nullptr; idx++) { + if (UCURR_MATCHES_BITMASK(gCurrencyList[idx].currType, currType)) { + count++; + } + } + return count; +} + +static const char* U_CALLCONV +ucurr_nextCurrencyList(UEnumeration *enumerator, + int32_t* resultLength, + UErrorCode * /*pErrorCode*/) +{ + UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context); + + /* Find the next in the list that matches the type we are looking for. */ + while (myContext->listIdx < UPRV_LENGTHOF(gCurrencyList)-1) { + const struct CurrencyList *currItem = &gCurrencyList[myContext->listIdx++]; + if (UCURR_MATCHES_BITMASK(currItem->currType, myContext->currType)) + { + if (resultLength) { + *resultLength = 3; /* Currency codes are only 3 chars long */ + } + return currItem->currency; + } + } + /* We enumerated too far. */ + if (resultLength) { + *resultLength = 0; + } + return nullptr; +} + +static void U_CALLCONV +ucurr_resetCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) { + ((UCurrencyContext *)(enumerator->context))->listIdx = 0; +} + +static void U_CALLCONV +ucurr_closeCurrencyList(UEnumeration *enumerator) { + uprv_free(enumerator->context); + uprv_free(enumerator); +} + +static void U_CALLCONV +ucurr_createCurrencyList(UHashtable *isoCodes, UErrorCode* status){ + UErrorCode localStatus = U_ZERO_ERROR; + + // Look up the CurrencyMap element in the root bundle. + UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); + UResourceBundle *currencyMapArray = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); + + if (U_SUCCESS(localStatus)) { + // process each entry in currency map + for (int32_t i=0; iisoCode = isoCode; + entry->from = fromDate; + entry->to = toDate; + + localStatus = U_ZERO_ERROR; + uhash_put(isoCodes, (char16_t *)isoCode, entry, &localStatus); + } + } else { + *status = localStatus; + } + ures_close(currencyArray); + } + } else { + *status = localStatus; + } + + ures_close(currencyMapArray); +} + +static const UEnumeration gEnumCurrencyList = { + nullptr, + nullptr, + ucurr_closeCurrencyList, + ucurr_countCurrencyList, + uenum_unextDefault, + ucurr_nextCurrencyList, + ucurr_resetCurrencyList +}; +U_CDECL_END + + +static void U_CALLCONV initIsoCodes(UErrorCode &status) { + U_ASSERT(gIsoCodes == nullptr); + ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); + + UHashtable *isoCodes = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status); + if (U_FAILURE(status)) { + return; + } + uhash_setValueDeleter(isoCodes, deleteIsoCodeEntry); + + ucurr_createCurrencyList(isoCodes, &status); + if (U_FAILURE(status)) { + uhash_close(isoCodes); + return; + } + gIsoCodes = isoCodes; // Note: gIsoCodes is const. Once set up here it is never altered, + // and read only access is safe without synchronization. +} + +static void populateCurrSymbolsEquiv(icu::Hashtable *hash, UErrorCode &status) { + 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; } + } + } +} + +static void U_CALLCONV initCurrSymbolsEquiv() { + U_ASSERT(gCurrSymbolsEquiv == nullptr); + UErrorCode status = U_ZERO_ERROR; + ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); + icu::Hashtable *temp = new icu::Hashtable(status); + if (temp == nullptr) { + return; + } + if (U_FAILURE(status)) { + delete temp; + return; + } + temp->setValueDeleter(deleteUnicode); + populateCurrSymbolsEquiv(temp, status); + if (U_FAILURE(status)) { + delete temp; + return; + } + gCurrSymbolsEquiv = temp; +} + +U_CAPI UBool U_EXPORT2 +ucurr_isAvailable(const char16_t* isoCode, UDate from, UDate to, UErrorCode* eErrorCode) { + umtx_initOnce(gIsoCodesInitOnce, &initIsoCodes, *eErrorCode); + if (U_FAILURE(*eErrorCode)) { + return false; + } + + IsoCodeEntry* result = (IsoCodeEntry *) uhash_get(gIsoCodes, isoCode); + if (result == nullptr) { + return false; + } else if (from > to) { + *eErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } else if ((from > result->to) || (to < result->from)) { + return false; + } + return true; +} + +static const icu::Hashtable* getCurrSymbolsEquiv() { + umtx_initOnce(gCurrSymbolsEquivInitOnce, &initCurrSymbolsEquiv); + return gCurrSymbolsEquiv; +} + +U_CAPI UEnumeration * U_EXPORT2 +ucurr_openISOCurrencies(uint32_t currType, UErrorCode *pErrorCode) { + UEnumeration *myEnum = nullptr; + UCurrencyContext *myContext; + + myEnum = (UEnumeration*)uprv_malloc(sizeof(UEnumeration)); + if (myEnum == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memcpy(myEnum, &gEnumCurrencyList, sizeof(UEnumeration)); + myContext = (UCurrencyContext*)uprv_malloc(sizeof(UCurrencyContext)); + if (myContext == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + uprv_free(myEnum); + return nullptr; + } + myContext->currType = currType; + myContext->listIdx = 0; + myEnum->context = myContext; + return myEnum; +} + +U_CAPI int32_t U_EXPORT2 +ucurr_countCurrencies(const char* locale, + UDate date, + UErrorCode* ec) +{ + int32_t currCount = 0; + + if (ec != nullptr && U_SUCCESS(*ec)) + { + // local variables + UErrorCode localStatus = U_ZERO_ERROR; + char id[ULOC_FULLNAME_CAPACITY]; + + // get country or country_variant in `id' + idForLocale(locale, id, sizeof(id), ec); + + if (U_FAILURE(*ec)) + { + return 0; + } + + // Remove variants, which is only needed for registration. + char *idDelim = strchr(id, VAR_DELIM); + if (idDelim) + { + idDelim[0] = 0; + } + + // Look up the CurrencyMap element in the root bundle. + UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); + UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); + + // Using the id derived from the local, get the currency data + UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus); + + // process each currency to see which one is valid for the given date + if (U_SUCCESS(localStatus)) + { + for (int32_t i=0; i 2) + { + int32_t toLength = 0; + UResourceBundle *toRes = ures_getByKey(currencyRes, "to", nullptr, &localStatus); + const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus); + + currDate64 = (int64_t)toArray[0] << 32; + currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF)); + UDate toDate = (UDate)currDate64; + + if ((fromDate <= date) && (date < toDate)) + { + currCount++; + } + + ures_close(toRes); + } + else + { + if (fromDate <= date) + { + currCount++; + } + } + + // close open resources + ures_close(currencyRes); + ures_close(fromRes); + + } // end For loop + } // end if (U_SUCCESS(localStatus)) + + ures_close(countryArray); + + // Check for errors + if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) + { + // There is nothing to fallback to. + // Report the failure/warning if possible. + *ec = localStatus; + } + + if (U_SUCCESS(*ec)) + { + // no errors + return currCount; + } + + } + + // If we got here, either error code is invalid or + // some argument passed is no good. + return 0; +} + +U_CAPI int32_t U_EXPORT2 +ucurr_forLocaleAndDate(const char* locale, + UDate date, + int32_t index, + char16_t* buff, + int32_t buffCapacity, + UErrorCode* ec) +{ + int32_t resLen = 0; + int32_t currIndex = 0; + const char16_t* s = nullptr; + + if (ec != nullptr && U_SUCCESS(*ec)) + { + // check the arguments passed + if ((buff && buffCapacity) || !buffCapacity ) + { + // local variables + UErrorCode localStatus = U_ZERO_ERROR; + char id[ULOC_FULLNAME_CAPACITY]; + + // get country or country_variant in `id' + idForLocale(locale, id, sizeof(id), ec); + if (U_FAILURE(*ec)) + { + return 0; + } + + // Remove variants, which is only needed for registration. + char *idDelim = strchr(id, VAR_DELIM); + if (idDelim) + { + idDelim[0] = 0; + } + + // Look up the CurrencyMap element in the root bundle. + UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); + UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); + + // Using the id derived from the local, get the currency data + UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus); + + // process each currency to see which one is valid for the given date + bool matchFound = false; + if (U_SUCCESS(localStatus)) + { + if ((index <= 0) || (index> ures_getSize(countryArray))) + { + // requested index is out of bounds + ures_close(countryArray); + return 0; + } + + for (int32_t i=0; i 2) + { + int32_t toLength = 0; + UResourceBundle *toRes = ures_getByKey(currencyRes, "to", nullptr, &localStatus); + const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus); + + currDate64 = (int64_t)toArray[0] << 32; + currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF)); + UDate toDate = (UDate)currDate64; + + if ((fromDate <= date) && (date < toDate)) + { + currIndex++; + if (currIndex == index) + { + matchFound = true; + } + } + + ures_close(toRes); + } + else + { + if (fromDate <= date) + { + currIndex++; + if (currIndex == index) + { + matchFound = true; + } + } + } + + // close open resources + ures_close(currencyRes); + ures_close(fromRes); + + // check for loop exit + if (matchFound) + { + break; + } + + } // end For loop + } + + ures_close(countryArray); + + // Check for errors + if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) + { + // There is nothing to fallback to. + // Report the failure/warning if possible. + *ec = localStatus; + } + + if (U_SUCCESS(*ec)) + { + // no errors + if((buffCapacity> resLen) && matchFound) + { + // write out the currency value + u_strcpy(buff, s); + } + else + { + return 0; + } + } + + // return null terminated currency string + return u_terminateUChars(buff, buffCapacity, resLen, ec); + } + else + { + // illegal argument encountered + *ec = U_ILLEGAL_ARGUMENT_ERROR; + } + + } + + // If we got here, either error code is invalid or + // some argument passed is no good. + return resLen; +} + +static const UEnumeration defaultKeywordValues = { + nullptr, + nullptr, + ulist_close_keyword_values_iterator, + ulist_count_keyword_values, + uenum_unextDefault, + ulist_next_keyword_value, + ulist_reset_keyword_values_iterator +}; + +U_CAPI UEnumeration *U_EXPORT2 ucurr_getKeywordValuesForLocale(const char *key, const char *locale, UBool commonlyUsed, UErrorCode* status) { + // Resolve region + char prefRegion[ULOC_COUNTRY_CAPACITY]; + ulocimp_getRegionForSupplementalData(locale, true, prefRegion, sizeof(prefRegion), status); + + // Read value from supplementalData + UList *values = ulist_createEmptyList(status); + UList *otherValues = ulist_createEmptyList(status); + UEnumeration *en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration)); + if (U_FAILURE(*status) || en == nullptr) { + if (en == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + } else { + uprv_free(en); + } + ulist_deleteList(values); + ulist_deleteList(otherValues); + return nullptr; + } + memcpy(en, &defaultKeywordValues, sizeof(UEnumeration)); + en->context = values; + + UResourceBundle *bundle = ures_openDirect(U_ICUDATA_CURR, "supplementalData", status); + ures_getByKey(bundle, "CurrencyMap", bundle, status); + UResourceBundle bundlekey, regbndl, curbndl, to; + ures_initStackObject(&bundlekey); + ures_initStackObject(®bndl); + ures_initStackObject(&curbndl); + ures_initStackObject(&to); + + while (U_SUCCESS(*status) && ures_hasNext(bundle)) { + ures_getNextResource(bundle, &bundlekey, status); + if (U_FAILURE(*status)) { + break; + } + const char *region = ures_getKey(&bundlekey); + UBool isPrefRegion = uprv_strcmp(region, prefRegion) == 0 ? true : false; + if (!isPrefRegion && commonlyUsed) { + // With commonlyUsed=true, we do not put + // currencies for other regions in the + // result list. + continue; + } + ures_getByKey(bundle, region, ®bndl, status); + if (U_FAILURE(*status)) { + break; + } + while (U_SUCCESS(*status) && ures_hasNext(®bndl)) { + ures_getNextResource(®bndl, &curbndl, status); + if (ures_getType(&curbndl) != URES_TABLE) { + // Currently, an empty ARRAY is mixed in. + continue; + } + char *curID = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY); + int32_t curIDLength = ULOC_KEYWORDS_CAPACITY; + if (curID == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + break; + } + +#if U_CHARSET_FAMILY==U_ASCII_FAMILY + ures_getUTF8StringByKey(&curbndl, "id", curID, &curIDLength, true, status); + /* optimize - use the utf-8 string */ +#else + { + const char16_t* defString = ures_getStringByKey(&curbndl, "id", &curIDLength, status); + if(U_SUCCESS(*status)) { + if(curIDLength+1 > ULOC_KEYWORDS_CAPACITY) { + *status = U_BUFFER_OVERFLOW_ERROR; + } else { + u_UCharsToChars(defString, curID, curIDLength+1); + } + } + } +#endif + + if (U_FAILURE(*status)) { + break; + } + UBool hasTo = false; + ures_getByKey(&curbndl, "to", &to, status); + if (U_FAILURE(*status)) { + // Do nothing here... + *status = U_ZERO_ERROR; + } else { + hasTo = true; + } + if (isPrefRegion && !hasTo && !ulist_containsString(values, curID, (int32_t)uprv_strlen(curID))) { + // Currently active currency for the target country + ulist_addItemEndList(values, curID, true, status); + } else if (!ulist_containsString(otherValues, curID, (int32_t)uprv_strlen(curID)) && !commonlyUsed) { + ulist_addItemEndList(otherValues, curID, true, status); + } else { + uprv_free(curID); + } + } + + } + if (U_SUCCESS(*status)) { + if (commonlyUsed) { + if (ulist_getListSize(values) == 0) { + // This could happen if no valid region is supplied in the input + // locale. In this case, we use the CLDR's default. + uenum_close(en); + en = ucurr_getKeywordValuesForLocale(key, "und", true, status); + } + } else { + // Consolidate the list + char *value = nullptr; + ulist_resetList(otherValues); + while ((value = (char *)ulist_getNext(otherValues)) != nullptr) { + if (!ulist_containsString(values, value, (int32_t)uprv_strlen(value))) { + char *tmpValue = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY); + uprv_memcpy(tmpValue, value, uprv_strlen(value) + 1); + ulist_addItemEndList(values, tmpValue, true, status); + if (U_FAILURE(*status)) { + break; + } + } + } + } + + ulist_resetList((UList *)(en->context)); + } else { + ulist_deleteList(values); + uprv_free(en); + values = nullptr; + en = nullptr; + } + ures_close(&to); + ures_close(&curbndl); + ures_close(®bndl); + ures_close(&bundlekey); + ures_close(bundle); + + ulist_deleteList(otherValues); + + return en; +} + + +U_CAPI int32_t U_EXPORT2 +ucurr_getNumericCode(const char16_t* currency) { + int32_t code = 0; + if (currency && u_strlen(currency) == ISO_CURRENCY_CODE_LENGTH) { + UErrorCode status = U_ZERO_ERROR; + + UResourceBundle *bundle = ures_openDirect(0, "currencyNumericCodes", &status); + ures_getByKey(bundle, "codeMap", bundle, &status); + if (U_SUCCESS(status)) { + char alphaCode[ISO_CURRENCY_CODE_LENGTH+1]; + myUCharsToChars(alphaCode, currency); + T_CString_toUpperCase(alphaCode); + ures_getByKey(bundle, alphaCode, bundle, &status); + int tmpCode = ures_getInt(bundle, &status); + if (U_SUCCESS(status)) { + code = tmpCode; + } + } + ures_close(bundle); + } + return code; +} +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/common/ucurrimp.h b/deps/icu-small/source/common/ucurrimp.h index 6d9588295df7bb..4932a733fd20c4 100644 --- a/deps/icu-small/source/common/ucurrimp.h +++ b/deps/icu-small/source/common/ucurrimp.h @@ -1,78 +1,78 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#ifndef _UCURR_IMP_H_ -#define _UCURR_IMP_H_ - -#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 - * the "static" currency name. This is usually the same as the - * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the - * format is applied to the number 2.0 (to yield the more common - * plural) to return a static name. - * - * This is used for backward compatibility with old currency logic in - * DecimalFormat and DecimalFormatSymbols. - */ -U_CAPI void -uprv_getStaticCurrencyName(const UChar* iso, const char* loc, - icu::UnicodeString& result, UErrorCode& ec); - -/** - * Attempt to parse the given string as a currency, either as a - * display name in the given locale, or as a 3-letter ISO 4217 - * code. If multiple display names match, then the longest one is - * selected. If both a display name and a 3-letter ISO code - * 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 - * 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 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 - * - * @internal - */ -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); - -/** - * 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 +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#ifndef _UCURR_IMP_H_ +#define _UCURR_IMP_H_ + +#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 + * the "static" currency name. This is usually the same as the + * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the + * format is applied to the number 2.0 (to yield the more common + * plural) to return a static name. + * + * This is used for backward compatibility with old currency logic in + * DecimalFormat and DecimalFormatSymbols. + */ +U_CAPI void +uprv_getStaticCurrencyName(const UChar* iso, const char* loc, + icu::UnicodeString& result, UErrorCode& ec); + +/** + * Attempt to parse the given string as a currency, either as a + * display name in the given locale, or as a 3-letter ISO 4217 + * code. If multiple display names match, then the longest one is + * selected. If both a display name and a 3-letter ISO code + * 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 + * 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 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 + * + * @internal + */ +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); + +/** + * 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/udata.cpp b/deps/icu-small/source/common/udata.cpp index 2bc74c97898c88..1ce5388f25a5e9 100644 --- a/deps/icu-small/source/common/udata.cpp +++ b/deps/icu-small/source/common/udata.cpp @@ -1,1460 +1,1460 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: udata.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999oct25 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" /* U_PLATFORM etc. */ - -#ifdef __GNUC__ -/* if gcc -#define ATTRIBUTE_WEAK __attribute__ ((weak)) -might have to #include some other header -*/ -#endif - -#include "unicode/putil.h" -#include "unicode/udata.h" -#include "unicode/uversion.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "mutex.h" -#include "putilimp.h" -#include "restrace.h" -#include "uassert.h" -#include "ucln_cmn.h" -#include "ucmndata.h" -#include "udatamem.h" -#include "uhash.h" -#include "umapfile.h" -#include "umutex.h" - -/*********************************************************************** -* -* Notes on the organization of the ICU data implementation -* -* All of the public API is defined in udata.h -* -* The implementation is split into several files... -* -* - udata.c (this file) contains higher level code that knows about -* the search paths for locating data, caching opened data, etc. -* -* - umapfile.c contains the low level platform-specific code for actually loading -* (memory mapping, file reading, whatever) data into memory. -* -* - ucmndata.c deals with the tables of contents of ICU data items within -* an ICU common format data file. The implementation includes -* an abstract interface and support for multiple TOC formats. -* All knowledge of any specific TOC format is encapsulated here. -* -* - udatamem.c has code for managing UDataMemory structs. These are little -* descriptor objects for blocks of memory holding ICU data of -* various types. -*/ - -/* configuration ---------------------------------------------------------- */ - -/* If you are excruciatingly bored turn this on .. */ -/* #define UDATA_DEBUG 1 */ - -#if defined(UDATA_DEBUG) -# include -#endif - -U_NAMESPACE_USE - -/* - * Forward declarations - */ -static UDataMemory *udata_findCachedData(const char *path, UErrorCode &err); - -/*********************************************************************** -* -* static (Global) data -* -************************************************************************/ - -/* - * Pointers to the common ICU data. - * - * We store multiple pointers to ICU data packages and iterate through them - * when looking for a data item. - * - * It is possible to combine this with dependency inversion: - * One or more data package libraries may export - * functions that each return a pointer to their piece of the ICU data, - * and this file would import them as weak functions, without a - * strong linker dependency from the common library on the data library. - * - * Then we can have applications depend on only that part of ICU's data - * that they really need, reducing the size of binaries that take advantage - * of this. - */ -static UDataMemory *gCommonICUDataArray[10] = { NULL }; // Access protected by icu global mutex. - -static u_atomic_int32_t gHaveTriedToLoadCommonData {0}; // See extendICUData(). - -static UHashtable *gCommonDataCache = NULL; /* Global hash table of opened ICU data files. */ -static icu::UInitOnce gCommonDataCacheInitOnce {}; - -#if !defined(ICU_DATA_DIR_WINDOWS) -static UDataFileAccess gDataFileAccess = UDATA_DEFAULT_ACCESS; // Access not synchronized. - // Modifying is documented as thread-unsafe. -#else -// If we are using the Windows data directory, then look in one spot only. -static UDataFileAccess gDataFileAccess = UDATA_NO_FILES; -#endif - -static UBool U_CALLCONV -udata_cleanup(void) -{ - int32_t i; - - if (gCommonDataCache) { /* Delete the cache of user data mappings. */ - uhash_close(gCommonDataCache); /* Table owns the contents, and will delete them. */ - gCommonDataCache = NULL; /* Cleanup is not thread safe. */ - } - gCommonDataCacheInitOnce.reset(); - - for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray) && gCommonICUDataArray[i] != NULL; ++i) { - udata_close(gCommonICUDataArray[i]); - gCommonICUDataArray[i] = NULL; - } - gHaveTriedToLoadCommonData = 0; - - return true; /* Everything was cleaned up */ -} - -static UBool U_CALLCONV -findCommonICUDataByName(const char *inBasename, UErrorCode &err) -{ - UBool found = false; - int32_t i; - - UDataMemory *pData = udata_findCachedData(inBasename, err); - if (U_FAILURE(err) || pData == NULL) - return false; - - { - Mutex lock; - for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray); ++i) { - if ((gCommonICUDataArray[i] != NULL) && (gCommonICUDataArray[i]->pHeader == pData->pHeader)) { - /* The data pointer is already in the array. */ - found = true; - break; - } - } - } - return found; -} - - -/* - * setCommonICUData. Set a UDataMemory to be the global ICU Data - */ -static UBool -setCommonICUData(UDataMemory *pData, /* The new common data. Belongs to caller, we copy it. */ - UBool warn, /* If true, set USING_DEFAULT warning if ICUData was */ - /* changed by another thread before we got to it. */ - UErrorCode *pErr) -{ - UDataMemory *newCommonData = UDataMemory_createNewInstance(pErr); - int32_t i; - UBool didUpdate = false; - if (U_FAILURE(*pErr)) { - return false; - } - - /* For the assignment, other threads must cleanly see either the old */ - /* or the new, not some partially initialized new. The old can not be */ - /* deleted - someone may still have a pointer to it lying around in */ - /* their locals. */ - UDatamemory_assign(newCommonData, pData); - umtx_lock(NULL); - for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray); ++i) { - if (gCommonICUDataArray[i] == NULL) { - gCommonICUDataArray[i] = newCommonData; - didUpdate = true; - break; - } else if (gCommonICUDataArray[i]->pHeader == pData->pHeader) { - /* The same data pointer is already in the array. */ - break; - } - } - umtx_unlock(NULL); - - if (i == UPRV_LENGTHOF(gCommonICUDataArray) && warn) { - *pErr = U_USING_DEFAULT_WARNING; - } - if (didUpdate) { - ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup); - } else { - uprv_free(newCommonData); - } - return didUpdate; -} - -#if !defined(ICU_DATA_DIR_WINDOWS) - -static UBool -setCommonICUDataPointer(const void *pData, UBool /*warn*/, UErrorCode *pErrorCode) { - UDataMemory tData; - UDataMemory_init(&tData); - UDataMemory_setData(&tData, pData); - udata_checkCommonData(&tData, pErrorCode); - return setCommonICUData(&tData, false, pErrorCode); -} - -#endif - -static const char * -findBasename(const char *path) { - const char *basename=uprv_strrchr(path, U_FILE_SEP_CHAR); - if(basename==NULL) { - return path; - } else { - return basename+1; - } -} - -#ifdef UDATA_DEBUG -static const char * -packageNameFromPath(const char *path) -{ - if((path == NULL) || (*path == 0)) { - return U_ICUDATA_NAME; - } - - path = findBasename(path); - - if((path == NULL) || (*path == 0)) { - return U_ICUDATA_NAME; - } - - return path; -} -#endif - -/*----------------------------------------------------------------------* - * * - * Cache for common data * - * Functions for looking up or adding entries to a cache of * - * data that has been previously opened. Avoids a potentially * - * expensive operation of re-opening the data for subsequent * - * uses. * - * * - * Data remains cached for the duration of the process. * - * * - *----------------------------------------------------------------------*/ - -typedef struct DataCacheElement { - char *name; - UDataMemory *item; -} DataCacheElement; - - - -/* - * Deleter function for DataCacheElements. - * udata cleanup function closes the hash table; hash table in turn calls back to - * here for each entry. - */ -static void U_CALLCONV DataCacheElement_deleter(void *pDCEl) { - DataCacheElement *p = (DataCacheElement *)pDCEl; - udata_close(p->item); /* unmaps storage */ - uprv_free(p->name); /* delete the hash key string. */ - uprv_free(pDCEl); /* delete 'this' */ -} - -static void U_CALLCONV udata_initHashTable(UErrorCode &err) { - U_ASSERT(gCommonDataCache == NULL); - gCommonDataCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &err); - if (U_FAILURE(err)) { - return; - } - U_ASSERT(gCommonDataCache != NULL); - uhash_setValueDeleter(gCommonDataCache, DataCacheElement_deleter); - ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup); -} - - /* udata_getCacheHashTable() - * Get the hash table used to store the data cache entries. - * Lazy create it if it doesn't yet exist. - */ -static UHashtable *udata_getHashTable(UErrorCode &err) { - umtx_initOnce(gCommonDataCacheInitOnce, &udata_initHashTable, err); - return gCommonDataCache; -} - - - -static UDataMemory *udata_findCachedData(const char *path, UErrorCode &err) -{ - UHashtable *htable; - UDataMemory *retVal = NULL; - DataCacheElement *el; - const char *baseName; - - htable = udata_getHashTable(err); - if (U_FAILURE(err)) { - return NULL; - } - - baseName = findBasename(path); /* Cache remembers only the base name, not the full path. */ - umtx_lock(NULL); - el = (DataCacheElement *)uhash_get(htable, baseName); - umtx_unlock(NULL); - if (el != NULL) { - retVal = el->item; - } -#ifdef UDATA_DEBUG - fprintf(stderr, "Cache: [%s] -> %p\n", baseName, (void*) retVal); -#endif - return retVal; -} - - -static UDataMemory *udata_cacheDataItem(const char *path, UDataMemory *item, UErrorCode *pErr) { - DataCacheElement *newElement; - const char *baseName; - int32_t nameLen; - UHashtable *htable; - DataCacheElement *oldValue = NULL; - UErrorCode subErr = U_ZERO_ERROR; - - htable = udata_getHashTable(*pErr); - if (U_FAILURE(*pErr)) { - return NULL; - } - - /* Create a new DataCacheElement - the thingy we store in the hash table - - * and copy the supplied path and UDataMemoryItems into it. - */ - newElement = (DataCacheElement *)uprv_malloc(sizeof(DataCacheElement)); - if (newElement == NULL) { - *pErr = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - newElement->item = UDataMemory_createNewInstance(pErr); - if (U_FAILURE(*pErr)) { - uprv_free(newElement); - return NULL; - } - UDatamemory_assign(newElement->item, item); - - baseName = findBasename(path); - nameLen = (int32_t)uprv_strlen(baseName); - newElement->name = (char *)uprv_malloc(nameLen+1); - if (newElement->name == NULL) { - *pErr = U_MEMORY_ALLOCATION_ERROR; - uprv_free(newElement->item); - uprv_free(newElement); - return NULL; - } - uprv_strcpy(newElement->name, baseName); - - /* Stick the new DataCacheElement into the hash table. - */ - umtx_lock(NULL); - oldValue = (DataCacheElement *)uhash_get(htable, path); - if (oldValue != NULL) { - subErr = U_USING_DEFAULT_WARNING; - } - else { - uhash_put( - htable, - newElement->name, /* Key */ - newElement, /* Value */ - &subErr); - } - umtx_unlock(NULL); - -#ifdef UDATA_DEBUG - fprintf(stderr, "Cache: [%s] <<< %p : %s. vFunc=%p\n", newElement->name, - (void*) newElement->item, u_errorName(subErr), (void*) newElement->item->vFuncs); -#endif - - if (subErr == U_USING_DEFAULT_WARNING || U_FAILURE(subErr)) { - *pErr = subErr; /* copy sub err unto fillin ONLY if something happens. */ - uprv_free(newElement->name); - uprv_free(newElement->item); - uprv_free(newElement); - return oldValue ? oldValue->item : NULL; - } - - return newElement->item; -} - -/*----------------------------------------------------------------------*============== - * * - * Path management. Could be shared with other tools/etc if need be * - * later on. * - * * - *----------------------------------------------------------------------*/ - -U_NAMESPACE_BEGIN - -class UDataPathIterator -{ -public: - UDataPathIterator(const char *path, const char *pkg, - const char *item, const char *suffix, UBool doCheckLastFour, - UErrorCode *pErrorCode); - const char *next(UErrorCode *pErrorCode); - -private: - const char *path; /* working path (u_icudata_Dir) */ - const char *nextPath; /* path following this one */ - const char *basename; /* item's basename (icudt22e_mt.res)*/ - - StringPiece suffix; /* item suffix (can be null) */ - - uint32_t basenameLen; /* length of basename */ - - CharString itemPath; /* path passed in with item name */ - CharString pathBuffer; /* output path for this it'ion */ - CharString packageStub; /* example: "/icudt28b". Will ignore that leaf in set paths. */ - - UBool checkLastFour; /* if true then allow paths such as '/foo/myapp.dat' - * to match, checks last 4 chars of suffix with - * last 4 of path, then previous chars. */ -}; - -/** - * @param iter The iterator to be initialized. Its current state does not matter. - * @param inPath The full pathname to be iterated over. If NULL, defaults to U_ICUDATA_NAME - * @param pkg Package which is being searched for, ex "icudt28l". Will ignore leaf directories such as /icudt28l - * @param item Item to be searched for. Can include full path, such as /a/b/foo.dat - * @param inSuffix Optional item suffix, if not-null (ex. ".dat") then 'path' can contain 'item' explicitly. - * Ex: 'stuff.dat' would be found in '/a/foo:/tmp/stuff.dat:/bar/baz' as item #2. - * '/blarg/stuff.dat' would also be found. - * Note: inSuffix may also be the 'item' being searched for as well, (ex: "ibm-5348_P100-1997.cnv"), in which case - * the 'item' parameter is often the same as pkg. (Though sometimes might have a tree part as well, ex: "icudt62l-curr"). - */ -UDataPathIterator::UDataPathIterator(const char *inPath, const char *pkg, - const char *item, const char *inSuffix, UBool doCheckLastFour, - UErrorCode *pErrorCode) -{ -#ifdef UDATA_DEBUG - fprintf(stderr, "SUFFIX1=%s PATH=%s\n", inSuffix, inPath); -#endif - /** Path **/ - if(inPath == NULL) { - path = u_getDataDirectory(); - } else { - path = inPath; - } - - /** Package **/ - if(pkg != NULL) { - packageStub.append(U_FILE_SEP_CHAR, *pErrorCode).append(pkg, *pErrorCode); -#ifdef UDATA_DEBUG - fprintf(stderr, "STUB=%s [%d]\n", packageStub.data(), packageStub.length()); -#endif - } - - /** Item **/ - basename = findBasename(item); - basenameLen = (int32_t)uprv_strlen(basename); - - /** Item path **/ - if(basename == item) { - nextPath = path; - } else { - itemPath.append(item, (int32_t)(basename-item), *pErrorCode); - nextPath = itemPath.data(); - } -#ifdef UDATA_DEBUG - fprintf(stderr, "SUFFIX=%s [%p]\n", inSuffix, (void*) inSuffix); -#endif - - /** Suffix **/ - if(inSuffix != NULL) { - suffix = inSuffix; - } else { - suffix = ""; - } - - checkLastFour = doCheckLastFour; - - /* pathBuffer will hold the output path strings returned by this iterator */ - -#ifdef UDATA_DEBUG - fprintf(stderr, "0: init %s -> [path=%s], [base=%s], [suff=%s], [itempath=%s], [nextpath=%s], [checklast4=%s]\n", - item, - path, - basename, - suffix.data(), - itemPath.data(), - nextPath, - checkLastFour?"true":"false"); -#endif -} - -/** - * Get the next path on the list. - * - * @param iter The Iter to be used - * @param len If set, pointer to the length of the returned path, for convenience. - * @return Pointer to the next path segment, or NULL if there are no more. - */ -const char *UDataPathIterator::next(UErrorCode *pErrorCode) -{ - if(U_FAILURE(*pErrorCode)) { - return NULL; - } - - const char *currentPath = NULL; - int32_t pathLen = 0; - const char *pathBasename; - - do - { - if( nextPath == NULL ) { - break; - } - currentPath = nextPath; - - if(nextPath == itemPath.data()) { /* we were processing item's path. */ - nextPath = path; /* start with regular path next tm. */ - pathLen = (int32_t)uprv_strlen(currentPath); - } else { - /* fix up next for next time */ - nextPath = uprv_strchr(currentPath, U_PATH_SEP_CHAR); - if(nextPath == NULL) { - /* segment: entire path */ - pathLen = (int32_t)uprv_strlen(currentPath); - } else { - /* segment: until next segment */ - pathLen = (int32_t)(nextPath - currentPath); - /* skip divider */ - nextPath ++; - } - } - - if(pathLen == 0) { - continue; - } - -#ifdef UDATA_DEBUG - fprintf(stderr, "rest of path (IDD) = %s\n", currentPath); - fprintf(stderr, " "); - { - int32_t qqq; - for(qqq=0;qqq=4) && - uprv_strncmp(pathBuffer.data() +(pathLen-4), suffix.data(), 4)==0 && /* suffix matches */ - uprv_strncmp(findBasename(pathBuffer.data()), basename, basenameLen)==0 && /* base matches */ - uprv_strlen(pathBasename)==(basenameLen+4)) { /* base+suffix = full len */ - -#ifdef UDATA_DEBUG - fprintf(stderr, "Have %s file on the path: %s\n", suffix.data(), pathBuffer.data()); -#endif - /* do nothing */ - } - else - { /* regular dir path */ - if(pathBuffer[pathLen-1] != U_FILE_SEP_CHAR) { - if((pathLen>=4) && - uprv_strncmp(pathBuffer.data()+(pathLen-4), ".dat", 4) == 0) - { -#ifdef UDATA_DEBUG - fprintf(stderr, "skipping non-directory .dat file %s\n", pathBuffer.data()); -#endif - continue; - } - - /* Check if it is a directory with the same name as our package */ - if(!packageStub.isEmpty() && - (pathLen > packageStub.length()) && - !uprv_strcmp(pathBuffer.data() + pathLen - packageStub.length(), packageStub.data())) { -#ifdef UDATA_DEBUG - fprintf(stderr, "Found stub %s (will add package %s of len %d)\n", packageStub.data(), basename, basenameLen); -#endif - pathBuffer.truncate(pathLen - packageStub.length()); - } - pathBuffer.append(U_FILE_SEP_CHAR, *pErrorCode); - } - - /* + basename */ - pathBuffer.append(packageStub.data()+1, packageStub.length()-1, *pErrorCode); - - if (!suffix.empty()) /* tack on suffix */ - { - if (suffix.length() > 4) { - // If the suffix is actually an item ("ibm-5348_P100-1997.cnv") and not an extension (".res") - // then we need to ensure that the path ends with a separator. - pathBuffer.ensureEndsWithFileSeparator(*pErrorCode); - } - pathBuffer.append(suffix, *pErrorCode); - } - } - -#ifdef UDATA_DEBUG - fprintf(stderr, " --> %s\n", pathBuffer.data()); -#endif - - return pathBuffer.data(); - - } while(path); - - /* fell way off the end */ - return NULL; -} - -U_NAMESPACE_END - -/* ==================================================================================*/ - - -/*----------------------------------------------------------------------* - * * - * Add a static reference to the common data library * - * Unless overridden by an explicit udata_setCommonData, this will be * - * our common data. * - * * - *----------------------------------------------------------------------*/ -#if !defined(ICU_DATA_DIR_WINDOWS) -// When using the Windows system data, we expect only a single data file. -extern "C" const DataHeader U_DATA_API U_ICUDATA_ENTRY_POINT; -#endif - -/* - * This would be a good place for weak-linkage declarations of - * partial-data-library access functions where each returns a pointer - * to its data package, if it is linked in. - */ -/* -extern const void *uprv_getICUData_collation(void) ATTRIBUTE_WEAK; -extern const void *uprv_getICUData_conversion(void) ATTRIBUTE_WEAK; -*/ - -/*----------------------------------------------------------------------* - * * - * openCommonData Attempt to open a common format (.dat) file * - * Map it into memory (if it's not there already) * - * and return a UDataMemory object for it. * - * * - * If the requested data is already open and cached * - * just return the cached UDataMem object. * - * * - *----------------------------------------------------------------------*/ -static UDataMemory * -openCommonData(const char *path, /* Path from OpenChoice? */ - int32_t commonDataIndex, /* ICU Data (index >= 0) if path == NULL */ - UErrorCode *pErrorCode) -{ - UDataMemory tData; - const char *pathBuffer; - const char *inBasename; - - if (U_FAILURE(*pErrorCode)) { - return NULL; - } - - UDataMemory_init(&tData); - - /* ??????? TODO revisit this */ - if (commonDataIndex >= 0) { - /* "mini-cache" for common ICU data */ - if(commonDataIndex >= UPRV_LENGTHOF(gCommonICUDataArray)) { - return NULL; - } - { - Mutex lock; - if(gCommonICUDataArray[commonDataIndex] != NULL) { - return gCommonICUDataArray[commonDataIndex]; - } -#if !defined(ICU_DATA_DIR_WINDOWS) -// When using the Windows system data, we expect only a single data file. - int32_t i; - for(i = 0; i < commonDataIndex; ++i) { - if(gCommonICUDataArray[i]->pHeader == &U_ICUDATA_ENTRY_POINT) { - /* The linked-in data is already in the list. */ - return NULL; - } - } -#endif - } - - /* Add the linked-in data to the list. */ - /* - * This is where we would check and call weakly linked partial-data-library - * access functions. - */ - /* - if (uprv_getICUData_collation) { - setCommonICUDataPointer(uprv_getICUData_collation(), false, pErrorCode); - } - if (uprv_getICUData_conversion) { - setCommonICUDataPointer(uprv_getICUData_conversion(), false, pErrorCode); - } - */ -#if !defined(ICU_DATA_DIR_WINDOWS) -// When using the Windows system data, we expect only a single data file. - setCommonICUDataPointer(&U_ICUDATA_ENTRY_POINT, false, pErrorCode); - { - Mutex lock; - return gCommonICUDataArray[commonDataIndex]; - } -#endif - } - - - /* request is NOT for ICU Data. */ - - /* Find the base name portion of the supplied path. */ - /* inBasename will be left pointing somewhere within the original path string. */ - inBasename = findBasename(path); -#ifdef UDATA_DEBUG - fprintf(stderr, "inBasename = %s\n", inBasename); -#endif - - if(*inBasename==0) { - /* no basename. This will happen if the original path was a directory name, */ - /* like "a/b/c/". (Fallback to separate files will still work.) */ -#ifdef UDATA_DEBUG - fprintf(stderr, "ocd: no basename in %s, bailing.\n", path); -#endif - if (U_SUCCESS(*pErrorCode)) { - *pErrorCode=U_FILE_ACCESS_ERROR; - } - return NULL; - } - - /* Is the requested common data file already open and cached? */ - /* Note that the cache is keyed by the base name only. The rest of the path, */ - /* if any, is not considered. */ - UDataMemory *dataToReturn = udata_findCachedData(inBasename, *pErrorCode); - if (dataToReturn != NULL || U_FAILURE(*pErrorCode)) { - return dataToReturn; - } - - /* Requested item is not in the cache. - * Hunt it down, trying all the path locations - */ - - UDataPathIterator iter(u_getDataDirectory(), inBasename, path, ".dat", true, pErrorCode); - - while ((UDataMemory_isLoaded(&tData)==false) && (pathBuffer = iter.next(pErrorCode)) != NULL) - { -#ifdef UDATA_DEBUG - fprintf(stderr, "ocd: trying path %s - ", pathBuffer); -#endif - uprv_mapFile(&tData, pathBuffer, pErrorCode); -#ifdef UDATA_DEBUG - fprintf(stderr, "%s\n", UDataMemory_isLoaded(&tData)?"LOADED":"not loaded"); -#endif - } - if (U_FAILURE(*pErrorCode)) { - return NULL; - } - -#if defined(OS390_STUBDATA) && defined(OS390BATCH) - if (!UDataMemory_isLoaded(&tData)) { - char ourPathBuffer[1024]; - /* One more chance, for extendCommonData() */ - uprv_strncpy(ourPathBuffer, path, 1019); - ourPathBuffer[1019]=0; - uprv_strcat(ourPathBuffer, ".dat"); - uprv_mapFile(&tData, ourPathBuffer, pErrorCode); - } -#endif - - if (U_FAILURE(*pErrorCode)) { - return NULL; - } - if (!UDataMemory_isLoaded(&tData)) { - /* no common data */ - *pErrorCode=U_FILE_ACCESS_ERROR; - return NULL; - } - - /* we have mapped a file, check its header */ - udata_checkCommonData(&tData, pErrorCode); - - - /* Cache the UDataMemory struct for this .dat file, - * so we won't need to hunt it down and map it again next time - * something is needed from it. */ - return udata_cacheDataItem(inBasename, &tData, pErrorCode); -} - - -/*----------------------------------------------------------------------* - * * - * extendICUData If the full set of ICU data was not loaded at * - * program startup, load it now. This function will * - * be called when the lookup of an ICU data item in * - * the common ICU data fails. * - * * - * return true if new data is loaded, false otherwise.* - * * - *----------------------------------------------------------------------*/ -static UBool extendICUData(UErrorCode *pErr) -{ - UDataMemory *pData; - UDataMemory copyPData; - UBool didUpdate = false; - - /* - * There is a chance for a race condition here. - * Normally, ICU data is loaded from a DLL or via mmap() and - * setCommonICUData() will detect if the same address is set twice. - * If ICU is built with data loading via fread() then the address will - * be different each time the common data is loaded and we may add - * multiple copies of the data. - * In this case, use a mutex to prevent the race. - * Use a specific mutex to avoid nested locks of the global mutex. - */ -#if MAP_IMPLEMENTATION==MAP_STDIO - static UMutex extendICUDataMutex; - umtx_lock(&extendICUDataMutex); -#endif - if(!umtx_loadAcquire(gHaveTriedToLoadCommonData)) { - /* See if we can explicitly open a .dat file for the ICUData. */ - pData = openCommonData( - U_ICUDATA_NAME, /* "icudt20l" , for example. */ - -1, /* Pretend we're not opening ICUData */ - pErr); - - /* How about if there is no pData, eh... */ - - UDataMemory_init(©PData); - if(pData != NULL) { - UDatamemory_assign(©PData, pData); - copyPData.map = 0; /* The mapping for this data is owned by the hash table */ - copyPData.mapAddr = 0; /* which will unmap it when ICU is shut down. */ - /* CommonICUData is also unmapped when ICU is shut down.*/ - /* To avoid unmapping the data twice, zero out the map */ - /* fields in the UDataMemory that we're assigning */ - /* to CommonICUData. */ - - didUpdate = /* no longer using this result */ - setCommonICUData(©PData,/* The new common data. */ - false, /* No warnings if write didn't happen */ - pErr); /* setCommonICUData honors errors; NOP if error set */ - } - - umtx_storeRelease(gHaveTriedToLoadCommonData, 1); - } - - didUpdate = findCommonICUDataByName(U_ICUDATA_NAME, *pErr); /* Return 'true' when a racing writes out the extended */ - /* data after another thread has failed to see it (in openCommonData), so */ - /* extended data can be examined. */ - /* Also handles a race through here before gHaveTriedToLoadCommonData is set. */ - -#if MAP_IMPLEMENTATION==MAP_STDIO - umtx_unlock(&extendICUDataMutex); -#endif - return didUpdate; /* Return true if ICUData pointer was updated. */ - /* (Could potentially have been done by another thread racing */ - /* us through here, but that's fine, we still return true */ - /* so that current thread will also examine extended data. */ -} - -/*----------------------------------------------------------------------* - * * - * udata_setCommonData * - * * - *----------------------------------------------------------------------*/ -U_CAPI void U_EXPORT2 -udata_setCommonData(const void *data, UErrorCode *pErrorCode) { - UDataMemory dataMemory; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return; - } - - if(data==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - /* set the data pointer and test for validity */ - UDataMemory_init(&dataMemory); - UDataMemory_setData(&dataMemory, data); - udata_checkCommonData(&dataMemory, pErrorCode); - if (U_FAILURE(*pErrorCode)) {return;} - - /* we have good data */ - /* Set it up as the ICU Common Data. */ - setCommonICUData(&dataMemory, true, pErrorCode); -} - -/*--------------------------------------------------------------------------- - * - * udata_setAppData - * - *---------------------------------------------------------------------------- */ -U_CAPI void U_EXPORT2 -udata_setAppData(const char *path, const void *data, UErrorCode *err) -{ - UDataMemory udm; - - if(err==NULL || U_FAILURE(*err)) { - return; - } - if(data==NULL) { - *err=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - UDataMemory_init(&udm); - UDataMemory_setData(&udm, data); - udata_checkCommonData(&udm, err); - udata_cacheDataItem(path, &udm, err); -} - -/*----------------------------------------------------------------------------* - * * - * checkDataItem Given a freshly located/loaded data item, either * - * an entry in a common file or a separately loaded file, * - * sanity check its header, and see if the data is * - * acceptable to the app. * - * If the data is good, create and return a UDataMemory * - * object that can be returned to the application. * - * Return NULL on any sort of failure. * - * * - *----------------------------------------------------------------------------*/ -static UDataMemory * -checkDataItem -( - const DataHeader *pHeader, /* The data item to be checked. */ - UDataMemoryIsAcceptable *isAcceptable, /* App's call-back function */ - void *context, /* pass-thru param for above. */ - const char *type, /* pass-thru param for above. */ - const char *name, /* pass-thru param for above. */ - UErrorCode *nonFatalErr, /* Error code if this data was not acceptable */ - /* but openChoice should continue with */ - /* trying to get data from fallback path. */ - UErrorCode *fatalErr /* Bad error, caller should return immediately */ - ) -{ - UDataMemory *rDataMem = NULL; /* the new UDataMemory, to be returned. */ - - if (U_FAILURE(*fatalErr)) { - return NULL; - } - - if(pHeader->dataHeader.magic1==0xda && - pHeader->dataHeader.magic2==0x27 && - (isAcceptable==NULL || isAcceptable(context, type, name, &pHeader->info)) - ) { - rDataMem=UDataMemory_createNewInstance(fatalErr); - if (U_FAILURE(*fatalErr)) { - return NULL; - } - rDataMem->pHeader = pHeader; - } else { - /* the data is not acceptable, look further */ - /* If we eventually find something good, this errorcode will be */ - /* cleared out. */ - *nonFatalErr=U_INVALID_FORMAT_ERROR; - } - return rDataMem; -} - -/** - * @return 0 if not loaded, 1 if loaded or err - */ -static UDataMemory *doLoadFromIndividualFiles(const char *pkgName, - const char *dataPath, const char *tocEntryPathSuffix, - /* following arguments are the same as doOpenChoice itself */ - const char *path, const char *type, const char *name, - UDataMemoryIsAcceptable *isAcceptable, void *context, - UErrorCode *subErrorCode, - UErrorCode *pErrorCode) -{ - const char *pathBuffer; - UDataMemory dataMemory; - UDataMemory *pEntryData; - - /* look in ind. files: package\nam.typ ========================= */ - /* init path iterator for individual files */ - UDataPathIterator iter(dataPath, pkgName, path, tocEntryPathSuffix, false, pErrorCode); - - while ((pathBuffer = iter.next(pErrorCode)) != NULL) - { -#ifdef UDATA_DEBUG - fprintf(stderr, "UDATA: trying individual file %s\n", pathBuffer); -#endif - if (uprv_mapFile(&dataMemory, pathBuffer, pErrorCode)) - { - pEntryData = checkDataItem(dataMemory.pHeader, isAcceptable, context, type, name, subErrorCode, pErrorCode); - if (pEntryData != NULL) { - /* Data is good. - * Hand off ownership of the backing memory to the user's UDataMemory. - * and return it. */ - pEntryData->mapAddr = dataMemory.mapAddr; - pEntryData->map = dataMemory.map; - -#ifdef UDATA_DEBUG - fprintf(stderr, "** Mapped file: %s\n", pathBuffer); -#endif - return pEntryData; - } - - /* the data is not acceptable, or some error occurred. Either way, unmap the memory */ - udata_close(&dataMemory); - - /* If we had a nasty error, bail out completely. */ - if (U_FAILURE(*pErrorCode)) { - return NULL; - } - - /* Otherwise remember that we found data but didn't like it for some reason */ - *subErrorCode=U_INVALID_FORMAT_ERROR; - } -#ifdef UDATA_DEBUG - fprintf(stderr, "%s\n", UDataMemory_isLoaded(&dataMemory)?"LOADED":"not loaded"); -#endif - } - return NULL; -} - -/** - * @return 0 if not loaded, 1 if loaded or err - */ -static UDataMemory *doLoadFromCommonData(UBool isICUData, const char * /*pkgName*/, - const char * /*dataPath*/, const char * /*tocEntryPathSuffix*/, const char *tocEntryName, - /* following arguments are the same as doOpenChoice itself */ - const char *path, const char *type, const char *name, - UDataMemoryIsAcceptable *isAcceptable, void *context, - UErrorCode *subErrorCode, - UErrorCode *pErrorCode) -{ - UDataMemory *pEntryData; - const DataHeader *pHeader; - UDataMemory *pCommonData; - int32_t commonDataIndex; - UBool checkedExtendedICUData = false; - /* try to get common data. The loop is for platforms such as the 390 that do - * not initially load the full set of ICU data. If the lookup of an ICU data item - * fails, the full (but slower to load) set is loaded, the and the loop repeats, - * trying the lookup again. Once the full set of ICU data is loaded, the loop wont - * repeat because the full set will be checked the first time through. - * - * The loop also handles the fallback to a .dat file if the application linked - * to the stub data library rather than a real library. - */ - for (commonDataIndex = isICUData ? 0 : -1;;) { - pCommonData=openCommonData(path, commonDataIndex, subErrorCode); /** search for pkg **/ - - if(U_SUCCESS(*subErrorCode) && pCommonData!=NULL) { - int32_t length; - - /* look up the data piece in the common data */ - pHeader=pCommonData->vFuncs->Lookup(pCommonData, tocEntryName, &length, subErrorCode); -#ifdef UDATA_DEBUG - fprintf(stderr, "%s: pHeader=%p - %s\n", tocEntryName, (void*) pHeader, u_errorName(*subErrorCode)); -#endif - - if(pHeader!=NULL) { - pEntryData = checkDataItem(pHeader, isAcceptable, context, type, name, subErrorCode, pErrorCode); -#ifdef UDATA_DEBUG - fprintf(stderr, "pEntryData=%p\n", (void*) pEntryData); -#endif - if (U_FAILURE(*pErrorCode)) { - return NULL; - } - if (pEntryData != NULL) { - pEntryData->length = length; - return pEntryData; - } - } - } - // If we failed due to being out-of-memory, then stop early and report the error. - if (*subErrorCode == U_MEMORY_ALLOCATION_ERROR) { - *pErrorCode = *subErrorCode; - return NULL; - } - /* Data wasn't found. If we were looking for an ICUData item and there is - * more data available, load it and try again, - * otherwise break out of this loop. */ - if (!isICUData) { - return NULL; - } else if (pCommonData != NULL) { - ++commonDataIndex; /* try the next data package */ - } else if ((!checkedExtendedICUData) && extendICUData(subErrorCode)) { - checkedExtendedICUData = true; - /* try this data package slot again: it changed from NULL to non-NULL */ - } else { - return NULL; - } - } -} - -/* - * Identify the Time Zone resources that are subject to special override data loading. - */ -static UBool isTimeZoneFile(const char *name, const char *type) { - return ((uprv_strcmp(type, "res") == 0) && - (uprv_strcmp(name, "zoneinfo64") == 0 || - uprv_strcmp(name, "timezoneTypes") == 0 || - uprv_strcmp(name, "windowsZones") == 0 || - uprv_strcmp(name, "metaZones") == 0)); -} - -/* - * A note on the ownership of Mapped Memory - * - * For common format files, ownership resides with the UDataMemory object - * that lives in the cache of opened common data. These UDataMemorys are private - * to the udata implementation, and are never seen directly by users. - * - * The UDataMemory objects returned to users will have the address of some desired - * data within the mapped region, but they wont have the mapping info itself, and thus - * won't cause anything to be removed from memory when they are closed. - * - * For individual data files, the UDataMemory returned to the user holds the - * information necessary to unmap the data on close. If the user independently - * opens the same data file twice, two completely independent mappings will be made. - * (There is no cache of opened data items from individual files, only a cache of - * opened Common Data files, that is, files containing a collection of data items.) - * - * For common data passed in from the user via udata_setAppData() or - * udata_setCommonData(), ownership remains with the user. - * - * UDataMemory objects themselves, as opposed to the memory they describe, - * can be anywhere - heap, stack/local or global. - * They have a flag to indicate when they're heap allocated and thus - * must be deleted when closed. - */ - - -/*----------------------------------------------------------------------------* - * * - * main data loading functions * - * * - *----------------------------------------------------------------------------*/ -static UDataMemory * -doOpenChoice(const char *path, const char *type, const char *name, - UDataMemoryIsAcceptable *isAcceptable, void *context, - UErrorCode *pErrorCode) -{ - UDataMemory *retVal = NULL; - - const char *dataPath; - - int32_t tocEntrySuffixIndex; - const char *tocEntryPathSuffix; - UErrorCode subErrorCode=U_ZERO_ERROR; - const char *treeChar; - - UBool isICUData = false; - - - FileTracer::traceOpen(path, type, name); - - - /* Is this path ICU data? */ - if(path == NULL || - !strcmp(path, U_ICUDATA_ALIAS) || /* "ICUDATA" */ - !uprv_strncmp(path, U_ICUDATA_NAME U_TREE_SEPARATOR_STRING, /* "icudt26e-" */ - uprv_strlen(U_ICUDATA_NAME U_TREE_SEPARATOR_STRING)) || - !uprv_strncmp(path, U_ICUDATA_ALIAS U_TREE_SEPARATOR_STRING, /* "ICUDATA-" */ - uprv_strlen(U_ICUDATA_ALIAS U_TREE_SEPARATOR_STRING))) { - isICUData = true; - } - -#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) /* Windows: try "foo\bar" and "foo/bar" */ - /* remap from alternate path char to the main one */ - CharString altSepPath; - if(path) { - if(uprv_strchr(path,U_FILE_ALT_SEP_CHAR) != NULL) { - altSepPath.append(path, *pErrorCode); - char *p; - while ((p = uprv_strchr(altSepPath.data(), U_FILE_ALT_SEP_CHAR)) != NULL) { - *p = U_FILE_SEP_CHAR; - } -#if defined (UDATA_DEBUG) - fprintf(stderr, "Changed path from [%s] to [%s]\n", path, altSepPath.s); -#endif - path = altSepPath.data(); - } - } -#endif - - CharString tocEntryName; /* entry name in tree format. ex: 'icudt28b/coll/ar.res' */ - CharString tocEntryPath; /* entry name in path format. ex: 'icudt28b\\coll\\ar.res' */ - - CharString pkgName; - CharString treeName; - - /* ======= Set up strings */ - if(path==NULL) { - pkgName.append(U_ICUDATA_NAME, *pErrorCode); - } else { - const char *pkg; - const char *first; - pkg = uprv_strrchr(path, U_FILE_SEP_CHAR); - first = uprv_strchr(path, U_FILE_SEP_CHAR); - if(uprv_pathIsAbsolute(path) || (pkg != first)) { /* more than one slash in the path- not a tree name */ - /* see if this is an /absolute/path/to/package path */ - if(pkg) { - pkgName.append(pkg+1, *pErrorCode); - } else { - pkgName.append(path, *pErrorCode); - } - } else { - treeChar = uprv_strchr(path, U_TREE_SEPARATOR); - if(treeChar) { - treeName.append(treeChar+1, *pErrorCode); /* following '-' */ - if(isICUData) { - pkgName.append(U_ICUDATA_NAME, *pErrorCode); - } else { - pkgName.append(path, (int32_t)(treeChar-path), *pErrorCode); - if (first == NULL) { - /* - This user data has no path, but there is a tree name. - Look up the correct path from the data cache later. - */ - path = pkgName.data(); - } - } - } else { - if(isICUData) { - pkgName.append(U_ICUDATA_NAME, *pErrorCode); - } else { - pkgName.append(path, *pErrorCode); - } - } - } - } - -#ifdef UDATA_DEBUG - fprintf(stderr, " P=%s T=%s\n", pkgName.data(), treeName.data()); -#endif - - /* setting up the entry name and file name - * Make up a full name by appending the type to the supplied - * name, assuming that a type was supplied. - */ - - /* prepend the package */ - tocEntryName.append(pkgName, *pErrorCode); - tocEntryPath.append(pkgName, *pErrorCode); - tocEntrySuffixIndex = tocEntryName.length(); - - if(!treeName.isEmpty()) { - tocEntryName.append(U_TREE_ENTRY_SEP_CHAR, *pErrorCode).append(treeName, *pErrorCode); - tocEntryPath.append(U_FILE_SEP_CHAR, *pErrorCode).append(treeName, *pErrorCode); - } - - tocEntryName.append(U_TREE_ENTRY_SEP_CHAR, *pErrorCode).append(name, *pErrorCode); - tocEntryPath.append(U_FILE_SEP_CHAR, *pErrorCode).append(name, *pErrorCode); - if(type!=NULL && *type!=0) { - tocEntryName.append(".", *pErrorCode).append(type, *pErrorCode); - tocEntryPath.append(".", *pErrorCode).append(type, *pErrorCode); - } - // The +1 is for the U_FILE_SEP_CHAR that is always appended above. - tocEntryPathSuffix = tocEntryPath.data() + tocEntrySuffixIndex + 1; /* suffix starts here */ - -#ifdef UDATA_DEBUG - fprintf(stderr, " tocEntryName = %s\n", tocEntryName.data()); - fprintf(stderr, " tocEntryPath = %s\n", tocEntryName.data()); -#endif - -#if !defined(ICU_DATA_DIR_WINDOWS) - if(path == NULL) { - path = COMMON_DATA_NAME; /* "icudt26e" */ - } -#else - // When using the Windows system data, we expects only a single data file. - path = COMMON_DATA_NAME; /* "icudt26e" */ -#endif - - /************************ Begin loop looking for ind. files ***************/ -#ifdef UDATA_DEBUG - fprintf(stderr, "IND: inBasename = %s, pkg=%s\n", "(n/a)", packageNameFromPath(path)); -#endif - - /* End of dealing with a null basename */ - dataPath = u_getDataDirectory(); - - /**** Time zone individual files override */ - if (isICUData && isTimeZoneFile(name, type)) { - const char *tzFilesDir = u_getTimeZoneFilesDirectory(pErrorCode); - if (tzFilesDir[0] != 0) { -#ifdef UDATA_DEBUG - fprintf(stderr, "Trying Time Zone Files directory = %s\n", tzFilesDir); -#endif - retVal = doLoadFromIndividualFiles(/* pkgName.data() */ "", tzFilesDir, tocEntryPathSuffix, - /* path */ "", type, name, isAcceptable, context, &subErrorCode, pErrorCode); - if((retVal != NULL) || U_FAILURE(*pErrorCode)) { - return retVal; - } - } - } - - /**** COMMON PACKAGE - only if packages are first. */ - if(gDataFileAccess == UDATA_PACKAGES_FIRST) { -#ifdef UDATA_DEBUG - fprintf(stderr, "Trying packages (UDATA_PACKAGES_FIRST)\n"); -#endif - /* #2 */ - retVal = doLoadFromCommonData(isICUData, - pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(), - path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); - if((retVal != NULL) || U_FAILURE(*pErrorCode)) { - return retVal; - } - } - - /**** INDIVIDUAL FILES */ - if((gDataFileAccess==UDATA_PACKAGES_FIRST) || - (gDataFileAccess==UDATA_FILES_FIRST)) { -#ifdef UDATA_DEBUG - fprintf(stderr, "Trying individual files\n"); -#endif - /* Check to make sure that there is a dataPath to iterate over */ - if ((dataPath && *dataPath) || !isICUData) { - retVal = doLoadFromIndividualFiles(pkgName.data(), dataPath, tocEntryPathSuffix, - path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); - if((retVal != NULL) || U_FAILURE(*pErrorCode)) { - return retVal; - } - } - } - - /**** COMMON PACKAGE */ - if((gDataFileAccess==UDATA_ONLY_PACKAGES) || - (gDataFileAccess==UDATA_FILES_FIRST)) { -#ifdef UDATA_DEBUG - fprintf(stderr, "Trying packages (UDATA_ONLY_PACKAGES || UDATA_FILES_FIRST)\n"); -#endif - retVal = doLoadFromCommonData(isICUData, - pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(), - path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); - if((retVal != NULL) || U_FAILURE(*pErrorCode)) { - return retVal; - } - } - - /* Load from DLL. If we haven't attempted package load, we also haven't had any chance to - try a DLL (static or setCommonData/etc) load. - If we ever have a "UDATA_ONLY_FILES", add it to the or list here. */ - if(gDataFileAccess==UDATA_NO_FILES) { -#ifdef UDATA_DEBUG - fprintf(stderr, "Trying common data (UDATA_NO_FILES)\n"); -#endif - retVal = doLoadFromCommonData(isICUData, - pkgName.data(), "", tocEntryPathSuffix, tocEntryName.data(), - path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); - if((retVal != NULL) || U_FAILURE(*pErrorCode)) { - return retVal; - } - } - - /* data not found */ - if(U_SUCCESS(*pErrorCode)) { - if(U_SUCCESS(subErrorCode)) { - /* file not found */ - *pErrorCode=U_FILE_ACCESS_ERROR; - } else { - /* entry point not found or rejected */ - *pErrorCode=subErrorCode; - } - } - return retVal; -} - - - -/* API ---------------------------------------------------------------------- */ - -U_CAPI UDataMemory * U_EXPORT2 -udata_open(const char *path, const char *type, const char *name, - UErrorCode *pErrorCode) { -#ifdef UDATA_DEBUG - fprintf(stderr, "udata_open(): Opening: %s : %s . %s\n", (path?path:"NULL"), name, type); - fflush(stderr); -#endif - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return NULL; - } else if(name==NULL || *name==0) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } else { - return doOpenChoice(path, type, name, NULL, NULL, pErrorCode); - } -} - - - -U_CAPI UDataMemory * U_EXPORT2 -udata_openChoice(const char *path, const char *type, const char *name, - UDataMemoryIsAcceptable *isAcceptable, void *context, - UErrorCode *pErrorCode) { -#ifdef UDATA_DEBUG - fprintf(stderr, "udata_openChoice(): Opening: %s : %s . %s\n", (path?path:"NULL"), name, type); -#endif - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return NULL; - } else if(name==NULL || *name==0 || isAcceptable==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } else { - return doOpenChoice(path, type, name, isAcceptable, context, pErrorCode); - } -} - - - -U_CAPI void U_EXPORT2 -udata_getInfo(UDataMemory *pData, UDataInfo *pInfo) { - if(pInfo!=NULL) { - if(pData!=NULL && pData->pHeader!=NULL) { - const UDataInfo *info=&pData->pHeader->info; - uint16_t dataInfoSize=udata_getInfoSize(info); - if(pInfo->size>dataInfoSize) { - pInfo->size=dataInfoSize; - } - uprv_memcpy((uint16_t *)pInfo+1, (const uint16_t *)info+1, pInfo->size-2); - if(info->isBigEndian!=U_IS_BIG_ENDIAN) { - /* opposite endianness */ - uint16_t x=info->reservedWord; - pInfo->reservedWord=(uint16_t)((x<<8)|(x>>8)); - } - } else { - pInfo->size=0; - } - } -} - - -U_CAPI void U_EXPORT2 udata_setFileAccess(UDataFileAccess access, UErrorCode * /*status*/) -{ - // Note: this function is documented as not thread safe. - gDataFileAccess = access; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: udata.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999oct25 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" /* U_PLATFORM etc. */ + +#ifdef __GNUC__ +/* if gcc +#define ATTRIBUTE_WEAK __attribute__ ((weak)) +might have to #include some other header +*/ +#endif + +#include "unicode/putil.h" +#include "unicode/udata.h" +#include "unicode/uversion.h" +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "mutex.h" +#include "putilimp.h" +#include "restrace.h" +#include "uassert.h" +#include "ucln_cmn.h" +#include "ucmndata.h" +#include "udatamem.h" +#include "uhash.h" +#include "umapfile.h" +#include "umutex.h" + +/*********************************************************************** +* +* Notes on the organization of the ICU data implementation +* +* All of the public API is defined in udata.h +* +* The implementation is split into several files... +* +* - udata.c (this file) contains higher level code that knows about +* the search paths for locating data, caching opened data, etc. +* +* - umapfile.c contains the low level platform-specific code for actually loading +* (memory mapping, file reading, whatever) data into memory. +* +* - ucmndata.c deals with the tables of contents of ICU data items within +* an ICU common format data file. The implementation includes +* an abstract interface and support for multiple TOC formats. +* All knowledge of any specific TOC format is encapsulated here. +* +* - udatamem.c has code for managing UDataMemory structs. These are little +* descriptor objects for blocks of memory holding ICU data of +* various types. +*/ + +/* configuration ---------------------------------------------------------- */ + +/* If you are excruciatingly bored turn this on .. */ +/* #define UDATA_DEBUG 1 */ + +#if defined(UDATA_DEBUG) +# include +#endif + +U_NAMESPACE_USE + +/* + * Forward declarations + */ +static UDataMemory *udata_findCachedData(const char *path, UErrorCode &err); + +/*********************************************************************** +* +* static (Global) data +* +************************************************************************/ + +/* + * Pointers to the common ICU data. + * + * We store multiple pointers to ICU data packages and iterate through them + * when looking for a data item. + * + * It is possible to combine this with dependency inversion: + * One or more data package libraries may export + * functions that each return a pointer to their piece of the ICU data, + * and this file would import them as weak functions, without a + * strong linker dependency from the common library on the data library. + * + * Then we can have applications depend on only that part of ICU's data + * that they really need, reducing the size of binaries that take advantage + * of this. + */ +static UDataMemory *gCommonICUDataArray[10] = { nullptr }; // Access protected by icu global mutex. + +static u_atomic_int32_t gHaveTriedToLoadCommonData {0}; // See extendICUData(). + +static UHashtable *gCommonDataCache = nullptr; /* Global hash table of opened ICU data files. */ +static icu::UInitOnce gCommonDataCacheInitOnce {}; + +#if !defined(ICU_DATA_DIR_WINDOWS) +static UDataFileAccess gDataFileAccess = UDATA_DEFAULT_ACCESS; // Access not synchronized. + // Modifying is documented as thread-unsafe. +#else +// If we are using the Windows data directory, then look in one spot only. +static UDataFileAccess gDataFileAccess = UDATA_NO_FILES; +#endif + +static UBool U_CALLCONV +udata_cleanup() +{ + int32_t i; + + if (gCommonDataCache) { /* Delete the cache of user data mappings. */ + uhash_close(gCommonDataCache); /* Table owns the contents, and will delete them. */ + gCommonDataCache = nullptr; /* Cleanup is not thread safe. */ + } + gCommonDataCacheInitOnce.reset(); + + for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray) && gCommonICUDataArray[i] != nullptr; ++i) { + udata_close(gCommonICUDataArray[i]); + gCommonICUDataArray[i] = nullptr; + } + gHaveTriedToLoadCommonData = 0; + + return true; /* Everything was cleaned up */ +} + +static UBool U_CALLCONV +findCommonICUDataByName(const char *inBasename, UErrorCode &err) +{ + UBool found = false; + int32_t i; + + UDataMemory *pData = udata_findCachedData(inBasename, err); + if (U_FAILURE(err) || pData == nullptr) + return false; + + { + Mutex lock; + for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray); ++i) { + if ((gCommonICUDataArray[i] != nullptr) && (gCommonICUDataArray[i]->pHeader == pData->pHeader)) { + /* The data pointer is already in the array. */ + found = true; + break; + } + } + } + return found; +} + + +/* + * setCommonICUData. Set a UDataMemory to be the global ICU Data + */ +static UBool +setCommonICUData(UDataMemory *pData, /* The new common data. Belongs to caller, we copy it. */ + UBool warn, /* If true, set USING_DEFAULT warning if ICUData was */ + /* changed by another thread before we got to it. */ + UErrorCode *pErr) +{ + UDataMemory *newCommonData = UDataMemory_createNewInstance(pErr); + int32_t i; + UBool didUpdate = false; + if (U_FAILURE(*pErr)) { + return false; + } + + /* For the assignment, other threads must cleanly see either the old */ + /* or the new, not some partially initialized new. The old can not be */ + /* deleted - someone may still have a pointer to it lying around in */ + /* their locals. */ + UDatamemory_assign(newCommonData, pData); + umtx_lock(nullptr); + for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray); ++i) { + if (gCommonICUDataArray[i] == nullptr) { + gCommonICUDataArray[i] = newCommonData; + didUpdate = true; + break; + } else if (gCommonICUDataArray[i]->pHeader == pData->pHeader) { + /* The same data pointer is already in the array. */ + break; + } + } + umtx_unlock(nullptr); + + if (i == UPRV_LENGTHOF(gCommonICUDataArray) && warn) { + *pErr = U_USING_DEFAULT_WARNING; + } + if (didUpdate) { + ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup); + } else { + uprv_free(newCommonData); + } + return didUpdate; +} + +#if !defined(ICU_DATA_DIR_WINDOWS) + +static UBool +setCommonICUDataPointer(const void *pData, UBool /*warn*/, UErrorCode *pErrorCode) { + UDataMemory tData; + UDataMemory_init(&tData); + UDataMemory_setData(&tData, pData); + udata_checkCommonData(&tData, pErrorCode); + return setCommonICUData(&tData, false, pErrorCode); +} + +#endif + +static const char * +findBasename(const char *path) { + const char *basename=uprv_strrchr(path, U_FILE_SEP_CHAR); + if(basename==nullptr) { + return path; + } else { + return basename+1; + } +} + +#ifdef UDATA_DEBUG +static const char * +packageNameFromPath(const char *path) +{ + if((path == nullptr) || (*path == 0)) { + return U_ICUDATA_NAME; + } + + path = findBasename(path); + + if((path == nullptr) || (*path == 0)) { + return U_ICUDATA_NAME; + } + + return path; +} +#endif + +/*----------------------------------------------------------------------* + * * + * Cache for common data * + * Functions for looking up or adding entries to a cache of * + * data that has been previously opened. Avoids a potentially * + * expensive operation of re-opening the data for subsequent * + * uses. * + * * + * Data remains cached for the duration of the process. * + * * + *----------------------------------------------------------------------*/ + +typedef struct DataCacheElement { + char *name; + UDataMemory *item; +} DataCacheElement; + + + +/* + * Deleter function for DataCacheElements. + * udata cleanup function closes the hash table; hash table in turn calls back to + * here for each entry. + */ +static void U_CALLCONV DataCacheElement_deleter(void *pDCEl) { + DataCacheElement *p = (DataCacheElement *)pDCEl; + udata_close(p->item); /* unmaps storage */ + uprv_free(p->name); /* delete the hash key string. */ + uprv_free(pDCEl); /* delete 'this' */ +} + +static void U_CALLCONV udata_initHashTable(UErrorCode &err) { + U_ASSERT(gCommonDataCache == nullptr); + gCommonDataCache = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &err); + if (U_FAILURE(err)) { + return; + } + U_ASSERT(gCommonDataCache != nullptr); + uhash_setValueDeleter(gCommonDataCache, DataCacheElement_deleter); + ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup); +} + + /* udata_getCacheHashTable() + * Get the hash table used to store the data cache entries. + * Lazy create it if it doesn't yet exist. + */ +static UHashtable *udata_getHashTable(UErrorCode &err) { + umtx_initOnce(gCommonDataCacheInitOnce, &udata_initHashTable, err); + return gCommonDataCache; +} + + + +static UDataMemory *udata_findCachedData(const char *path, UErrorCode &err) +{ + UHashtable *htable; + UDataMemory *retVal = nullptr; + DataCacheElement *el; + const char *baseName; + + htable = udata_getHashTable(err); + if (U_FAILURE(err)) { + return nullptr; + } + + baseName = findBasename(path); /* Cache remembers only the base name, not the full path. */ + umtx_lock(nullptr); + el = (DataCacheElement *)uhash_get(htable, baseName); + umtx_unlock(nullptr); + if (el != nullptr) { + retVal = el->item; + } +#ifdef UDATA_DEBUG + fprintf(stderr, "Cache: [%s] -> %p\n", baseName, (void*) retVal); +#endif + return retVal; +} + + +static UDataMemory *udata_cacheDataItem(const char *path, UDataMemory *item, UErrorCode *pErr) { + DataCacheElement *newElement; + const char *baseName; + int32_t nameLen; + UHashtable *htable; + DataCacheElement *oldValue = nullptr; + UErrorCode subErr = U_ZERO_ERROR; + + htable = udata_getHashTable(*pErr); + if (U_FAILURE(*pErr)) { + return nullptr; + } + + /* Create a new DataCacheElement - the thingy we store in the hash table - + * and copy the supplied path and UDataMemoryItems into it. + */ + newElement = (DataCacheElement *)uprv_malloc(sizeof(DataCacheElement)); + if (newElement == nullptr) { + *pErr = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + newElement->item = UDataMemory_createNewInstance(pErr); + if (U_FAILURE(*pErr)) { + uprv_free(newElement); + return nullptr; + } + UDatamemory_assign(newElement->item, item); + + baseName = findBasename(path); + nameLen = (int32_t)uprv_strlen(baseName); + newElement->name = (char *)uprv_malloc(nameLen+1); + if (newElement->name == nullptr) { + *pErr = U_MEMORY_ALLOCATION_ERROR; + uprv_free(newElement->item); + uprv_free(newElement); + return nullptr; + } + uprv_strcpy(newElement->name, baseName); + + /* Stick the new DataCacheElement into the hash table. + */ + umtx_lock(nullptr); + oldValue = (DataCacheElement *)uhash_get(htable, path); + if (oldValue != nullptr) { + subErr = U_USING_DEFAULT_WARNING; + } + else { + uhash_put( + htable, + newElement->name, /* Key */ + newElement, /* Value */ + &subErr); + } + umtx_unlock(nullptr); + +#ifdef UDATA_DEBUG + fprintf(stderr, "Cache: [%s] <<< %p : %s. vFunc=%p\n", newElement->name, + (void*) newElement->item, u_errorName(subErr), (void*) newElement->item->vFuncs); +#endif + + if (subErr == U_USING_DEFAULT_WARNING || U_FAILURE(subErr)) { + *pErr = subErr; /* copy sub err unto fillin ONLY if something happens. */ + uprv_free(newElement->name); + uprv_free(newElement->item); + uprv_free(newElement); + return oldValue ? oldValue->item : nullptr; + } + + return newElement->item; +} + +/*----------------------------------------------------------------------*============== + * * + * Path management. Could be shared with other tools/etc if need be * + * later on. * + * * + *----------------------------------------------------------------------*/ + +U_NAMESPACE_BEGIN + +class UDataPathIterator +{ +public: + UDataPathIterator(const char *path, const char *pkg, + const char *item, const char *suffix, UBool doCheckLastFour, + UErrorCode *pErrorCode); + const char *next(UErrorCode *pErrorCode); + +private: + const char *path; /* working path (u_icudata_Dir) */ + const char *nextPath; /* path following this one */ + const char *basename; /* item's basename (icudt22e_mt.res)*/ + + StringPiece suffix; /* item suffix (can be null) */ + + uint32_t basenameLen; /* length of basename */ + + CharString itemPath; /* path passed in with item name */ + CharString pathBuffer; /* output path for this it'ion */ + CharString packageStub; /* example: "/icudt28b". Will ignore that leaf in set paths. */ + + UBool checkLastFour; /* if true then allow paths such as '/foo/myapp.dat' + * to match, checks last 4 chars of suffix with + * last 4 of path, then previous chars. */ +}; + +/** + * @param iter The iterator to be initialized. Its current state does not matter. + * @param inPath The full pathname to be iterated over. If nullptr, defaults to U_ICUDATA_NAME + * @param pkg Package which is being searched for, ex "icudt28l". Will ignore leaf directories such as /icudt28l + * @param item Item to be searched for. Can include full path, such as /a/b/foo.dat + * @param inSuffix Optional item suffix, if not-null (ex. ".dat") then 'path' can contain 'item' explicitly. + * Ex: 'stuff.dat' would be found in '/a/foo:/tmp/stuff.dat:/bar/baz' as item #2. + * '/blarg/stuff.dat' would also be found. + * Note: inSuffix may also be the 'item' being searched for as well, (ex: "ibm-5348_P100-1997.cnv"), in which case + * the 'item' parameter is often the same as pkg. (Though sometimes might have a tree part as well, ex: "icudt62l-curr"). + */ +UDataPathIterator::UDataPathIterator(const char *inPath, const char *pkg, + const char *item, const char *inSuffix, UBool doCheckLastFour, + UErrorCode *pErrorCode) +{ +#ifdef UDATA_DEBUG + fprintf(stderr, "SUFFIX1=%s PATH=%s\n", inSuffix, inPath); +#endif + /** Path **/ + if(inPath == nullptr) { + path = u_getDataDirectory(); + } else { + path = inPath; + } + + /** Package **/ + if(pkg != nullptr) { + packageStub.append(U_FILE_SEP_CHAR, *pErrorCode).append(pkg, *pErrorCode); +#ifdef UDATA_DEBUG + fprintf(stderr, "STUB=%s [%d]\n", packageStub.data(), packageStub.length()); +#endif + } + + /** Item **/ + basename = findBasename(item); + basenameLen = (int32_t)uprv_strlen(basename); + + /** Item path **/ + if(basename == item) { + nextPath = path; + } else { + itemPath.append(item, (int32_t)(basename-item), *pErrorCode); + nextPath = itemPath.data(); + } +#ifdef UDATA_DEBUG + fprintf(stderr, "SUFFIX=%s [%p]\n", inSuffix, (void*) inSuffix); +#endif + + /** Suffix **/ + if(inSuffix != nullptr) { + suffix = inSuffix; + } else { + suffix = ""; + } + + checkLastFour = doCheckLastFour; + + /* pathBuffer will hold the output path strings returned by this iterator */ + +#ifdef UDATA_DEBUG + fprintf(stderr, "0: init %s -> [path=%s], [base=%s], [suff=%s], [itempath=%s], [nextpath=%s], [checklast4=%s]\n", + item, + path, + basename, + suffix.data(), + itemPath.data(), + nextPath, + checkLastFour?"true":"false"); +#endif +} + +/** + * Get the next path on the list. + * + * @param iter The Iter to be used + * @param len If set, pointer to the length of the returned path, for convenience. + * @return Pointer to the next path segment, or nullptr if there are no more. + */ +const char *UDataPathIterator::next(UErrorCode *pErrorCode) +{ + if(U_FAILURE(*pErrorCode)) { + return nullptr; + } + + const char *currentPath = nullptr; + int32_t pathLen = 0; + const char *pathBasename; + + do + { + if( nextPath == nullptr ) { + break; + } + currentPath = nextPath; + + if(nextPath == itemPath.data()) { /* we were processing item's path. */ + nextPath = path; /* start with regular path next tm. */ + pathLen = (int32_t)uprv_strlen(currentPath); + } else { + /* fix up next for next time */ + nextPath = uprv_strchr(currentPath, U_PATH_SEP_CHAR); + if(nextPath == nullptr) { + /* segment: entire path */ + pathLen = (int32_t)uprv_strlen(currentPath); + } else { + /* segment: until next segment */ + pathLen = (int32_t)(nextPath - currentPath); + /* skip divider */ + nextPath ++; + } + } + + if(pathLen == 0) { + continue; + } + +#ifdef UDATA_DEBUG + fprintf(stderr, "rest of path (IDD) = %s\n", currentPath); + fprintf(stderr, " "); + { + int32_t qqq; + for(qqq=0;qqq=4) && + uprv_strncmp(pathBuffer.data() +(pathLen-4), suffix.data(), 4)==0 && /* suffix matches */ + uprv_strncmp(findBasename(pathBuffer.data()), basename, basenameLen)==0 && /* base matches */ + uprv_strlen(pathBasename)==(basenameLen+4)) { /* base+suffix = full len */ + +#ifdef UDATA_DEBUG + fprintf(stderr, "Have %s file on the path: %s\n", suffix.data(), pathBuffer.data()); +#endif + /* do nothing */ + } + else + { /* regular dir path */ + if(pathBuffer[pathLen-1] != U_FILE_SEP_CHAR) { + if((pathLen>=4) && + uprv_strncmp(pathBuffer.data()+(pathLen-4), ".dat", 4) == 0) + { +#ifdef UDATA_DEBUG + fprintf(stderr, "skipping non-directory .dat file %s\n", pathBuffer.data()); +#endif + continue; + } + + /* Check if it is a directory with the same name as our package */ + if(!packageStub.isEmpty() && + (pathLen > packageStub.length()) && + !uprv_strcmp(pathBuffer.data() + pathLen - packageStub.length(), packageStub.data())) { +#ifdef UDATA_DEBUG + fprintf(stderr, "Found stub %s (will add package %s of len %d)\n", packageStub.data(), basename, basenameLen); +#endif + pathBuffer.truncate(pathLen - packageStub.length()); + } + pathBuffer.append(U_FILE_SEP_CHAR, *pErrorCode); + } + + /* + basename */ + pathBuffer.append(packageStub.data()+1, packageStub.length()-1, *pErrorCode); + + if (!suffix.empty()) /* tack on suffix */ + { + if (suffix.length() > 4) { + // If the suffix is actually an item ("ibm-5348_P100-1997.cnv") and not an extension (".res") + // then we need to ensure that the path ends with a separator. + pathBuffer.ensureEndsWithFileSeparator(*pErrorCode); + } + pathBuffer.append(suffix, *pErrorCode); + } + } + +#ifdef UDATA_DEBUG + fprintf(stderr, " --> %s\n", pathBuffer.data()); +#endif + + return pathBuffer.data(); + + } while(path); + + /* fell way off the end */ + return nullptr; +} + +U_NAMESPACE_END + +/* ==================================================================================*/ + + +/*----------------------------------------------------------------------* + * * + * Add a static reference to the common data library * + * Unless overridden by an explicit udata_setCommonData, this will be * + * our common data. * + * * + *----------------------------------------------------------------------*/ +#if !defined(ICU_DATA_DIR_WINDOWS) +// When using the Windows system data, we expect only a single data file. +extern "C" const DataHeader U_DATA_API U_ICUDATA_ENTRY_POINT; +#endif + +/* + * This would be a good place for weak-linkage declarations of + * partial-data-library access functions where each returns a pointer + * to its data package, if it is linked in. + */ +/* +extern const void *uprv_getICUData_collation() ATTRIBUTE_WEAK; +extern const void *uprv_getICUData_conversion() ATTRIBUTE_WEAK; +*/ + +/*----------------------------------------------------------------------* + * * + * openCommonData Attempt to open a common format (.dat) file * + * Map it into memory (if it's not there already) * + * and return a UDataMemory object for it. * + * * + * If the requested data is already open and cached * + * just return the cached UDataMem object. * + * * + *----------------------------------------------------------------------*/ +static UDataMemory * +openCommonData(const char *path, /* Path from OpenChoice? */ + int32_t commonDataIndex, /* ICU Data (index >= 0) if path == nullptr */ + UErrorCode *pErrorCode) +{ + UDataMemory tData; + const char *pathBuffer; + const char *inBasename; + + if (U_FAILURE(*pErrorCode)) { + return nullptr; + } + + UDataMemory_init(&tData); + + /* ??????? TODO revisit this */ + if (commonDataIndex >= 0) { + /* "mini-cache" for common ICU data */ + if(commonDataIndex >= UPRV_LENGTHOF(gCommonICUDataArray)) { + return nullptr; + } + { + Mutex lock; + if(gCommonICUDataArray[commonDataIndex] != nullptr) { + return gCommonICUDataArray[commonDataIndex]; + } +#if !defined(ICU_DATA_DIR_WINDOWS) +// When using the Windows system data, we expect only a single data file. + int32_t i; + for(i = 0; i < commonDataIndex; ++i) { + if(gCommonICUDataArray[i]->pHeader == &U_ICUDATA_ENTRY_POINT) { + /* The linked-in data is already in the list. */ + return nullptr; + } + } +#endif + } + + /* Add the linked-in data to the list. */ + /* + * This is where we would check and call weakly linked partial-data-library + * access functions. + */ + /* + if (uprv_getICUData_collation) { + setCommonICUDataPointer(uprv_getICUData_collation(), false, pErrorCode); + } + if (uprv_getICUData_conversion) { + setCommonICUDataPointer(uprv_getICUData_conversion(), false, pErrorCode); + } + */ +#if !defined(ICU_DATA_DIR_WINDOWS) +// When using the Windows system data, we expect only a single data file. + setCommonICUDataPointer(&U_ICUDATA_ENTRY_POINT, false, pErrorCode); + { + Mutex lock; + return gCommonICUDataArray[commonDataIndex]; + } +#endif + } + + + /* request is NOT for ICU Data. */ + + /* Find the base name portion of the supplied path. */ + /* inBasename will be left pointing somewhere within the original path string. */ + inBasename = findBasename(path); +#ifdef UDATA_DEBUG + fprintf(stderr, "inBasename = %s\n", inBasename); +#endif + + if(*inBasename==0) { + /* no basename. This will happen if the original path was a directory name, */ + /* like "a/b/c/". (Fallback to separate files will still work.) */ +#ifdef UDATA_DEBUG + fprintf(stderr, "ocd: no basename in %s, bailing.\n", path); +#endif + if (U_SUCCESS(*pErrorCode)) { + *pErrorCode=U_FILE_ACCESS_ERROR; + } + return nullptr; + } + + /* Is the requested common data file already open and cached? */ + /* Note that the cache is keyed by the base name only. The rest of the path, */ + /* if any, is not considered. */ + UDataMemory *dataToReturn = udata_findCachedData(inBasename, *pErrorCode); + if (dataToReturn != nullptr || U_FAILURE(*pErrorCode)) { + return dataToReturn; + } + + /* Requested item is not in the cache. + * Hunt it down, trying all the path locations + */ + + UDataPathIterator iter(u_getDataDirectory(), inBasename, path, ".dat", true, pErrorCode); + + while ((UDataMemory_isLoaded(&tData)==false) && (pathBuffer = iter.next(pErrorCode)) != nullptr) + { +#ifdef UDATA_DEBUG + fprintf(stderr, "ocd: trying path %s - ", pathBuffer); +#endif + uprv_mapFile(&tData, pathBuffer, pErrorCode); +#ifdef UDATA_DEBUG + fprintf(stderr, "%s\n", UDataMemory_isLoaded(&tData)?"LOADED":"not loaded"); +#endif + } + if (U_FAILURE(*pErrorCode)) { + return nullptr; + } + +#if defined(OS390_STUBDATA) && defined(OS390BATCH) + if (!UDataMemory_isLoaded(&tData)) { + char ourPathBuffer[1024]; + /* One more chance, for extendCommonData() */ + uprv_strncpy(ourPathBuffer, path, 1019); + ourPathBuffer[1019]=0; + uprv_strcat(ourPathBuffer, ".dat"); + uprv_mapFile(&tData, ourPathBuffer, pErrorCode); + } +#endif + + if (U_FAILURE(*pErrorCode)) { + return nullptr; + } + if (!UDataMemory_isLoaded(&tData)) { + /* no common data */ + *pErrorCode=U_FILE_ACCESS_ERROR; + return nullptr; + } + + /* we have mapped a file, check its header */ + udata_checkCommonData(&tData, pErrorCode); + + + /* Cache the UDataMemory struct for this .dat file, + * so we won't need to hunt it down and map it again next time + * something is needed from it. */ + return udata_cacheDataItem(inBasename, &tData, pErrorCode); +} + + +/*----------------------------------------------------------------------* + * * + * extendICUData If the full set of ICU data was not loaded at * + * program startup, load it now. This function will * + * be called when the lookup of an ICU data item in * + * the common ICU data fails. * + * * + * return true if new data is loaded, false otherwise.* + * * + *----------------------------------------------------------------------*/ +static UBool extendICUData(UErrorCode *pErr) +{ + UDataMemory *pData; + UDataMemory copyPData; + UBool didUpdate = false; + + /* + * There is a chance for a race condition here. + * Normally, ICU data is loaded from a DLL or via mmap() and + * setCommonICUData() will detect if the same address is set twice. + * If ICU is built with data loading via fread() then the address will + * be different each time the common data is loaded and we may add + * multiple copies of the data. + * In this case, use a mutex to prevent the race. + * Use a specific mutex to avoid nested locks of the global mutex. + */ +#if MAP_IMPLEMENTATION==MAP_STDIO + static UMutex extendICUDataMutex; + umtx_lock(&extendICUDataMutex); +#endif + if(!umtx_loadAcquire(gHaveTriedToLoadCommonData)) { + /* See if we can explicitly open a .dat file for the ICUData. */ + pData = openCommonData( + U_ICUDATA_NAME, /* "icudt20l" , for example. */ + -1, /* Pretend we're not opening ICUData */ + pErr); + + /* How about if there is no pData, eh... */ + + UDataMemory_init(©PData); + if(pData != nullptr) { + UDatamemory_assign(©PData, pData); + copyPData.map = 0; /* The mapping for this data is owned by the hash table */ + copyPData.mapAddr = 0; /* which will unmap it when ICU is shut down. */ + /* CommonICUData is also unmapped when ICU is shut down.*/ + /* To avoid unmapping the data twice, zero out the map */ + /* fields in the UDataMemory that we're assigning */ + /* to CommonICUData. */ + + didUpdate = /* no longer using this result */ + setCommonICUData(©PData,/* The new common data. */ + false, /* No warnings if write didn't happen */ + pErr); /* setCommonICUData honors errors; NOP if error set */ + } + + umtx_storeRelease(gHaveTriedToLoadCommonData, 1); + } + + didUpdate = findCommonICUDataByName(U_ICUDATA_NAME, *pErr); /* Return 'true' when a racing writes out the extended */ + /* data after another thread has failed to see it (in openCommonData), so */ + /* extended data can be examined. */ + /* Also handles a race through here before gHaveTriedToLoadCommonData is set. */ + +#if MAP_IMPLEMENTATION==MAP_STDIO + umtx_unlock(&extendICUDataMutex); +#endif + return didUpdate; /* Return true if ICUData pointer was updated. */ + /* (Could potentially have been done by another thread racing */ + /* us through here, but that's fine, we still return true */ + /* so that current thread will also examine extended data. */ +} + +/*----------------------------------------------------------------------* + * * + * udata_setCommonData * + * * + *----------------------------------------------------------------------*/ +U_CAPI void U_EXPORT2 +udata_setCommonData(const void *data, UErrorCode *pErrorCode) { + UDataMemory dataMemory; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return; + } + + if(data==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + /* set the data pointer and test for validity */ + UDataMemory_init(&dataMemory); + UDataMemory_setData(&dataMemory, data); + udata_checkCommonData(&dataMemory, pErrorCode); + if (U_FAILURE(*pErrorCode)) {return;} + + /* we have good data */ + /* Set it up as the ICU Common Data. */ + setCommonICUData(&dataMemory, true, pErrorCode); +} + +/*--------------------------------------------------------------------------- + * + * udata_setAppData + * + *---------------------------------------------------------------------------- */ +U_CAPI void U_EXPORT2 +udata_setAppData(const char *path, const void *data, UErrorCode *err) +{ + UDataMemory udm; + + if(err==nullptr || U_FAILURE(*err)) { + return; + } + if(data==nullptr) { + *err=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + UDataMemory_init(&udm); + UDataMemory_setData(&udm, data); + udata_checkCommonData(&udm, err); + udata_cacheDataItem(path, &udm, err); +} + +/*----------------------------------------------------------------------------* + * * + * checkDataItem Given a freshly located/loaded data item, either * + * an entry in a common file or a separately loaded file, * + * sanity check its header, and see if the data is * + * acceptable to the app. * + * If the data is good, create and return a UDataMemory * + * object that can be returned to the application. * + * Return nullptr on any sort of failure. * + * * + *----------------------------------------------------------------------------*/ +static UDataMemory * +checkDataItem +( + const DataHeader *pHeader, /* The data item to be checked. */ + UDataMemoryIsAcceptable *isAcceptable, /* App's call-back function */ + void *context, /* pass-thru param for above. */ + const char *type, /* pass-thru param for above. */ + const char *name, /* pass-thru param for above. */ + UErrorCode *nonFatalErr, /* Error code if this data was not acceptable */ + /* but openChoice should continue with */ + /* trying to get data from fallback path. */ + UErrorCode *fatalErr /* Bad error, caller should return immediately */ + ) +{ + UDataMemory *rDataMem = nullptr; /* the new UDataMemory, to be returned. */ + + if (U_FAILURE(*fatalErr)) { + return nullptr; + } + + if(pHeader->dataHeader.magic1==0xda && + pHeader->dataHeader.magic2==0x27 && + (isAcceptable==nullptr || isAcceptable(context, type, name, &pHeader->info)) + ) { + rDataMem=UDataMemory_createNewInstance(fatalErr); + if (U_FAILURE(*fatalErr)) { + return nullptr; + } + rDataMem->pHeader = pHeader; + } else { + /* the data is not acceptable, look further */ + /* If we eventually find something good, this errorcode will be */ + /* cleared out. */ + *nonFatalErr=U_INVALID_FORMAT_ERROR; + } + return rDataMem; +} + +/** + * @return 0 if not loaded, 1 if loaded or err + */ +static UDataMemory *doLoadFromIndividualFiles(const char *pkgName, + const char *dataPath, const char *tocEntryPathSuffix, + /* following arguments are the same as doOpenChoice itself */ + const char *path, const char *type, const char *name, + UDataMemoryIsAcceptable *isAcceptable, void *context, + UErrorCode *subErrorCode, + UErrorCode *pErrorCode) +{ + const char *pathBuffer; + UDataMemory dataMemory; + UDataMemory *pEntryData; + + /* look in ind. files: package\nam.typ ========================= */ + /* init path iterator for individual files */ + UDataPathIterator iter(dataPath, pkgName, path, tocEntryPathSuffix, false, pErrorCode); + + while ((pathBuffer = iter.next(pErrorCode)) != nullptr) + { +#ifdef UDATA_DEBUG + fprintf(stderr, "UDATA: trying individual file %s\n", pathBuffer); +#endif + if (uprv_mapFile(&dataMemory, pathBuffer, pErrorCode)) + { + pEntryData = checkDataItem(dataMemory.pHeader, isAcceptable, context, type, name, subErrorCode, pErrorCode); + if (pEntryData != nullptr) { + /* Data is good. + * Hand off ownership of the backing memory to the user's UDataMemory. + * and return it. */ + pEntryData->mapAddr = dataMemory.mapAddr; + pEntryData->map = dataMemory.map; + +#ifdef UDATA_DEBUG + fprintf(stderr, "** Mapped file: %s\n", pathBuffer); +#endif + return pEntryData; + } + + /* the data is not acceptable, or some error occurred. Either way, unmap the memory */ + udata_close(&dataMemory); + + /* If we had a nasty error, bail out completely. */ + if (U_FAILURE(*pErrorCode)) { + return nullptr; + } + + /* Otherwise remember that we found data but didn't like it for some reason */ + *subErrorCode=U_INVALID_FORMAT_ERROR; + } +#ifdef UDATA_DEBUG + fprintf(stderr, "%s\n", UDataMemory_isLoaded(&dataMemory)?"LOADED":"not loaded"); +#endif + } + return nullptr; +} + +/** + * @return 0 if not loaded, 1 if loaded or err + */ +static UDataMemory *doLoadFromCommonData(UBool isICUData, const char * /*pkgName*/, + const char * /*dataPath*/, const char * /*tocEntryPathSuffix*/, const char *tocEntryName, + /* following arguments are the same as doOpenChoice itself */ + const char *path, const char *type, const char *name, + UDataMemoryIsAcceptable *isAcceptable, void *context, + UErrorCode *subErrorCode, + UErrorCode *pErrorCode) +{ + UDataMemory *pEntryData; + const DataHeader *pHeader; + UDataMemory *pCommonData; + int32_t commonDataIndex; + UBool checkedExtendedICUData = false; + /* try to get common data. The loop is for platforms such as the 390 that do + * not initially load the full set of ICU data. If the lookup of an ICU data item + * fails, the full (but slower to load) set is loaded, the and the loop repeats, + * trying the lookup again. Once the full set of ICU data is loaded, the loop wont + * repeat because the full set will be checked the first time through. + * + * The loop also handles the fallback to a .dat file if the application linked + * to the stub data library rather than a real library. + */ + for (commonDataIndex = isICUData ? 0 : -1;;) { + pCommonData=openCommonData(path, commonDataIndex, subErrorCode); /** search for pkg **/ + + if(U_SUCCESS(*subErrorCode) && pCommonData!=nullptr) { + int32_t length; + + /* look up the data piece in the common data */ + pHeader=pCommonData->vFuncs->Lookup(pCommonData, tocEntryName, &length, subErrorCode); +#ifdef UDATA_DEBUG + fprintf(stderr, "%s: pHeader=%p - %s\n", tocEntryName, (void*) pHeader, u_errorName(*subErrorCode)); +#endif + + if(pHeader!=nullptr) { + pEntryData = checkDataItem(pHeader, isAcceptable, context, type, name, subErrorCode, pErrorCode); +#ifdef UDATA_DEBUG + fprintf(stderr, "pEntryData=%p\n", (void*) pEntryData); +#endif + if (U_FAILURE(*pErrorCode)) { + return nullptr; + } + if (pEntryData != nullptr) { + pEntryData->length = length; + return pEntryData; + } + } + } + // If we failed due to being out-of-memory, then stop early and report the error. + if (*subErrorCode == U_MEMORY_ALLOCATION_ERROR) { + *pErrorCode = *subErrorCode; + return nullptr; + } + /* Data wasn't found. If we were looking for an ICUData item and there is + * more data available, load it and try again, + * otherwise break out of this loop. */ + if (!isICUData) { + return nullptr; + } else if (pCommonData != nullptr) { + ++commonDataIndex; /* try the next data package */ + } else if ((!checkedExtendedICUData) && extendICUData(subErrorCode)) { + checkedExtendedICUData = true; + /* try this data package slot again: it changed from nullptr to non-nullptr */ + } else { + return nullptr; + } + } +} + +/* + * Identify the Time Zone resources that are subject to special override data loading. + */ +static UBool isTimeZoneFile(const char *name, const char *type) { + return ((uprv_strcmp(type, "res") == 0) && + (uprv_strcmp(name, "zoneinfo64") == 0 || + uprv_strcmp(name, "timezoneTypes") == 0 || + uprv_strcmp(name, "windowsZones") == 0 || + uprv_strcmp(name, "metaZones") == 0)); +} + +/* + * A note on the ownership of Mapped Memory + * + * For common format files, ownership resides with the UDataMemory object + * that lives in the cache of opened common data. These UDataMemorys are private + * to the udata implementation, and are never seen directly by users. + * + * The UDataMemory objects returned to users will have the address of some desired + * data within the mapped region, but they wont have the mapping info itself, and thus + * won't cause anything to be removed from memory when they are closed. + * + * For individual data files, the UDataMemory returned to the user holds the + * information necessary to unmap the data on close. If the user independently + * opens the same data file twice, two completely independent mappings will be made. + * (There is no cache of opened data items from individual files, only a cache of + * opened Common Data files, that is, files containing a collection of data items.) + * + * For common data passed in from the user via udata_setAppData() or + * udata_setCommonData(), ownership remains with the user. + * + * UDataMemory objects themselves, as opposed to the memory they describe, + * can be anywhere - heap, stack/local or global. + * They have a flag to indicate when they're heap allocated and thus + * must be deleted when closed. + */ + + +/*----------------------------------------------------------------------------* + * * + * main data loading functions * + * * + *----------------------------------------------------------------------------*/ +static UDataMemory * +doOpenChoice(const char *path, const char *type, const char *name, + UDataMemoryIsAcceptable *isAcceptable, void *context, + UErrorCode *pErrorCode) +{ + UDataMemory *retVal = nullptr; + + const char *dataPath; + + int32_t tocEntrySuffixIndex; + const char *tocEntryPathSuffix; + UErrorCode subErrorCode=U_ZERO_ERROR; + const char *treeChar; + + UBool isICUData = false; + + + FileTracer::traceOpen(path, type, name); + + + /* Is this path ICU data? */ + if(path == nullptr || + !strcmp(path, U_ICUDATA_ALIAS) || /* "ICUDATA" */ + !uprv_strncmp(path, U_ICUDATA_NAME U_TREE_SEPARATOR_STRING, /* "icudt26e-" */ + uprv_strlen(U_ICUDATA_NAME U_TREE_SEPARATOR_STRING)) || + !uprv_strncmp(path, U_ICUDATA_ALIAS U_TREE_SEPARATOR_STRING, /* "ICUDATA-" */ + uprv_strlen(U_ICUDATA_ALIAS U_TREE_SEPARATOR_STRING))) { + isICUData = true; + } + +#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) /* Windows: try "foo\bar" and "foo/bar" */ + /* remap from alternate path char to the main one */ + CharString altSepPath; + if(path) { + if(uprv_strchr(path,U_FILE_ALT_SEP_CHAR) != nullptr) { + altSepPath.append(path, *pErrorCode); + char *p; + while ((p = uprv_strchr(altSepPath.data(), U_FILE_ALT_SEP_CHAR)) != nullptr) { + *p = U_FILE_SEP_CHAR; + } +#if defined (UDATA_DEBUG) + fprintf(stderr, "Changed path from [%s] to [%s]\n", path, altSepPath.s); +#endif + path = altSepPath.data(); + } + } +#endif + + CharString tocEntryName; /* entry name in tree format. ex: 'icudt28b/coll/ar.res' */ + CharString tocEntryPath; /* entry name in path format. ex: 'icudt28b\\coll\\ar.res' */ + + CharString pkgName; + CharString treeName; + + /* ======= Set up strings */ + if(path==nullptr) { + pkgName.append(U_ICUDATA_NAME, *pErrorCode); + } else { + const char *pkg; + const char *first; + pkg = uprv_strrchr(path, U_FILE_SEP_CHAR); + first = uprv_strchr(path, U_FILE_SEP_CHAR); + if(uprv_pathIsAbsolute(path) || (pkg != first)) { /* more than one slash in the path- not a tree name */ + /* see if this is an /absolute/path/to/package path */ + if(pkg) { + pkgName.append(pkg+1, *pErrorCode); + } else { + pkgName.append(path, *pErrorCode); + } + } else { + treeChar = uprv_strchr(path, U_TREE_SEPARATOR); + if(treeChar) { + treeName.append(treeChar+1, *pErrorCode); /* following '-' */ + if(isICUData) { + pkgName.append(U_ICUDATA_NAME, *pErrorCode); + } else { + pkgName.append(path, (int32_t)(treeChar-path), *pErrorCode); + if (first == nullptr) { + /* + This user data has no path, but there is a tree name. + Look up the correct path from the data cache later. + */ + path = pkgName.data(); + } + } + } else { + if(isICUData) { + pkgName.append(U_ICUDATA_NAME, *pErrorCode); + } else { + pkgName.append(path, *pErrorCode); + } + } + } + } + +#ifdef UDATA_DEBUG + fprintf(stderr, " P=%s T=%s\n", pkgName.data(), treeName.data()); +#endif + + /* setting up the entry name and file name + * Make up a full name by appending the type to the supplied + * name, assuming that a type was supplied. + */ + + /* prepend the package */ + tocEntryName.append(pkgName, *pErrorCode); + tocEntryPath.append(pkgName, *pErrorCode); + tocEntrySuffixIndex = tocEntryName.length(); + + if(!treeName.isEmpty()) { + tocEntryName.append(U_TREE_ENTRY_SEP_CHAR, *pErrorCode).append(treeName, *pErrorCode); + tocEntryPath.append(U_FILE_SEP_CHAR, *pErrorCode).append(treeName, *pErrorCode); + } + + tocEntryName.append(U_TREE_ENTRY_SEP_CHAR, *pErrorCode).append(name, *pErrorCode); + tocEntryPath.append(U_FILE_SEP_CHAR, *pErrorCode).append(name, *pErrorCode); + if(type!=nullptr && *type!=0) { + tocEntryName.append(".", *pErrorCode).append(type, *pErrorCode); + tocEntryPath.append(".", *pErrorCode).append(type, *pErrorCode); + } + // The +1 is for the U_FILE_SEP_CHAR that is always appended above. + tocEntryPathSuffix = tocEntryPath.data() + tocEntrySuffixIndex + 1; /* suffix starts here */ + +#ifdef UDATA_DEBUG + fprintf(stderr, " tocEntryName = %s\n", tocEntryName.data()); + fprintf(stderr, " tocEntryPath = %s\n", tocEntryName.data()); +#endif + +#if !defined(ICU_DATA_DIR_WINDOWS) + if(path == nullptr) { + path = COMMON_DATA_NAME; /* "icudt26e" */ + } +#else + // When using the Windows system data, we expects only a single data file. + path = COMMON_DATA_NAME; /* "icudt26e" */ +#endif + + /************************ Begin loop looking for ind. files ***************/ +#ifdef UDATA_DEBUG + fprintf(stderr, "IND: inBasename = %s, pkg=%s\n", "(n/a)", packageNameFromPath(path)); +#endif + + /* End of dealing with a null basename */ + dataPath = u_getDataDirectory(); + + /**** Time zone individual files override */ + if (isICUData && isTimeZoneFile(name, type)) { + const char *tzFilesDir = u_getTimeZoneFilesDirectory(pErrorCode); + if (tzFilesDir[0] != 0) { +#ifdef UDATA_DEBUG + fprintf(stderr, "Trying Time Zone Files directory = %s\n", tzFilesDir); +#endif + retVal = doLoadFromIndividualFiles(/* pkgName.data() */ "", tzFilesDir, tocEntryPathSuffix, + /* path */ "", type, name, isAcceptable, context, &subErrorCode, pErrorCode); + if((retVal != nullptr) || U_FAILURE(*pErrorCode)) { + return retVal; + } + } + } + + /**** COMMON PACKAGE - only if packages are first. */ + if(gDataFileAccess == UDATA_PACKAGES_FIRST) { +#ifdef UDATA_DEBUG + fprintf(stderr, "Trying packages (UDATA_PACKAGES_FIRST)\n"); +#endif + /* #2 */ + retVal = doLoadFromCommonData(isICUData, + pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(), + path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); + if((retVal != nullptr) || U_FAILURE(*pErrorCode)) { + return retVal; + } + } + + /**** INDIVIDUAL FILES */ + if((gDataFileAccess==UDATA_PACKAGES_FIRST) || + (gDataFileAccess==UDATA_FILES_FIRST)) { +#ifdef UDATA_DEBUG + fprintf(stderr, "Trying individual files\n"); +#endif + /* Check to make sure that there is a dataPath to iterate over */ + if ((dataPath && *dataPath) || !isICUData) { + retVal = doLoadFromIndividualFiles(pkgName.data(), dataPath, tocEntryPathSuffix, + path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); + if((retVal != nullptr) || U_FAILURE(*pErrorCode)) { + return retVal; + } + } + } + + /**** COMMON PACKAGE */ + if((gDataFileAccess==UDATA_ONLY_PACKAGES) || + (gDataFileAccess==UDATA_FILES_FIRST)) { +#ifdef UDATA_DEBUG + fprintf(stderr, "Trying packages (UDATA_ONLY_PACKAGES || UDATA_FILES_FIRST)\n"); +#endif + retVal = doLoadFromCommonData(isICUData, + pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(), + path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); + if((retVal != nullptr) || U_FAILURE(*pErrorCode)) { + return retVal; + } + } + + /* Load from DLL. If we haven't attempted package load, we also haven't had any chance to + try a DLL (static or setCommonData/etc) load. + If we ever have a "UDATA_ONLY_FILES", add it to the or list here. */ + if(gDataFileAccess==UDATA_NO_FILES) { +#ifdef UDATA_DEBUG + fprintf(stderr, "Trying common data (UDATA_NO_FILES)\n"); +#endif + retVal = doLoadFromCommonData(isICUData, + pkgName.data(), "", tocEntryPathSuffix, tocEntryName.data(), + path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); + if((retVal != nullptr) || U_FAILURE(*pErrorCode)) { + return retVal; + } + } + + /* data not found */ + if(U_SUCCESS(*pErrorCode)) { + if(U_SUCCESS(subErrorCode)) { + /* file not found */ + *pErrorCode=U_FILE_ACCESS_ERROR; + } else { + /* entry point not found or rejected */ + *pErrorCode=subErrorCode; + } + } + return retVal; +} + + + +/* API ---------------------------------------------------------------------- */ + +U_CAPI UDataMemory * U_EXPORT2 +udata_open(const char *path, const char *type, const char *name, + UErrorCode *pErrorCode) { +#ifdef UDATA_DEBUG + fprintf(stderr, "udata_open(): Opening: %s : %s . %s\n", (path?path:"nullptr"), name, type); + fflush(stderr); +#endif + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return nullptr; + } else if(name==nullptr || *name==0) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } else { + return doOpenChoice(path, type, name, nullptr, nullptr, pErrorCode); + } +} + + + +U_CAPI UDataMemory * U_EXPORT2 +udata_openChoice(const char *path, const char *type, const char *name, + UDataMemoryIsAcceptable *isAcceptable, void *context, + UErrorCode *pErrorCode) { +#ifdef UDATA_DEBUG + fprintf(stderr, "udata_openChoice(): Opening: %s : %s . %s\n", (path?path:"nullptr"), name, type); +#endif + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return nullptr; + } else if(name==nullptr || *name==0 || isAcceptable==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } else { + return doOpenChoice(path, type, name, isAcceptable, context, pErrorCode); + } +} + + + +U_CAPI void U_EXPORT2 +udata_getInfo(UDataMemory *pData, UDataInfo *pInfo) { + if(pInfo!=nullptr) { + if(pData!=nullptr && pData->pHeader!=nullptr) { + const UDataInfo *info=&pData->pHeader->info; + uint16_t dataInfoSize=udata_getInfoSize(info); + if(pInfo->size>dataInfoSize) { + pInfo->size=dataInfoSize; + } + uprv_memcpy((uint16_t *)pInfo+1, (const uint16_t *)info+1, pInfo->size-2); + if(info->isBigEndian!=U_IS_BIG_ENDIAN) { + /* opposite endianness */ + uint16_t x=info->reservedWord; + pInfo->reservedWord=(uint16_t)((x<<8)|(x>>8)); + } + } else { + pInfo->size=0; + } + } +} + + +U_CAPI void U_EXPORT2 udata_setFileAccess(UDataFileAccess access, UErrorCode * /*status*/) +{ + // Note: this function is documented as not thread safe. + gDataFileAccess = access; +} diff --git a/deps/icu-small/source/common/udatamem.cpp b/deps/icu-small/source/common/udatamem.cpp index 0f80de28eb8fbe..13ef61c0d32813 100644 --- a/deps/icu-small/source/common/udatamem.cpp +++ b/deps/icu-small/source/common/udatamem.cpp @@ -1,161 +1,161 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************/ - - -/*---------------------------------------------------------------------------------- - * - * UDataMemory A class-like struct that serves as a handle to a piece of memory - * that contains some ICU data (resource, converters, whatever.) - * - * When an application opens ICU data (with udata_open, for example, - * a UDataMemory * is returned. - * - *----------------------------------------------------------------------------------*/ - -#include "unicode/utypes.h" -#include "cmemory.h" -#include "unicode/udata.h" - -#include "udatamem.h" - -U_CFUNC void UDataMemory_init(UDataMemory *This) { - uprv_memset(This, 0, sizeof(UDataMemory)); - This->length=-1; -} - - -U_CFUNC void UDatamemory_assign(UDataMemory *dest, UDataMemory *source) { - /* UDataMemory Assignment. Destination UDataMemory must be initialized first. */ - UBool mallocedFlag = dest->heapAllocated; - uprv_memcpy(dest, source, sizeof(UDataMemory)); - dest->heapAllocated = mallocedFlag; -} - -U_CFUNC UDataMemory *UDataMemory_createNewInstance(UErrorCode *pErr) { - UDataMemory *This; - - if (U_FAILURE(*pErr)) { - return NULL; - } - This = (UDataMemory *)uprv_malloc(sizeof(UDataMemory)); - if (This == NULL) { - *pErr = U_MEMORY_ALLOCATION_ERROR; } - else { - UDataMemory_init(This); - This->heapAllocated = true; - } - return This; -} - - -U_CFUNC const DataHeader * -UDataMemory_normalizeDataPointer(const void *p) { - /* allow the data to be optionally prepended with an alignment-forcing double value */ - const DataHeader *pdh = (const DataHeader *)p; - if(pdh==NULL || (pdh->dataHeader.magic1==0xda && pdh->dataHeader.magic2==0x27)) { - return pdh; - } else { -#if U_PLATFORM == U_PF_OS400 - /* - TODO: Fix this once the compiler implements this feature. Keep in sync with genccode.c - - This is here because this platform can't currently put - const data into the read-only pages of an object or - shared library (service program). Only strings are allowed in read-only - pages, so we use char * strings to store the data. - - In order to prevent the beginning of the data from ever matching the - magic numbers we must skip the initial double. - [grhoten 4/24/2003] - */ - return (const DataHeader *)*((const void **)p+1); -#else - return (const DataHeader *)((const double *)p+1); -#endif - } -} - - -U_CFUNC void UDataMemory_setData (UDataMemory *This, const void *dataAddr) { - This->pHeader = UDataMemory_normalizeDataPointer(dataAddr); -} - - -U_CAPI void U_EXPORT2 -udata_close(UDataMemory *pData) { - if(pData!=NULL) { - uprv_unmapFile(pData); - if(pData->heapAllocated ) { - uprv_free(pData); - } else { - UDataMemory_init(pData); - } - } -} - -U_CAPI const void * U_EXPORT2 -udata_getMemory(UDataMemory *pData) { - if(pData!=NULL && pData->pHeader!=NULL) { - return (char *)(pData->pHeader)+udata_getHeaderSize(pData->pHeader); - } else { - return NULL; - } -} - -/** - * Get the length of the data item if possible. - * The length may be up to 15 bytes larger than the actual data. - * - * TODO Consider making this function public. - * It would have to return the actual length in more cases. - * For example, the length of the last item in a .dat package could be - * computed from the size of the whole .dat package minus the offset of the - * last item. - * The size of a file that was directly memory-mapped could be determined - * using some system API. - * - * In order to get perfect values for all data items, we may have to add a - * length field to UDataInfo, but that complicates data generation - * and may be overkill. - * - * @param pData The data item. - * @return the length of the data item, or -1 if not known - * @internal Currently used only in cintltst/udatatst.c - */ -U_CAPI int32_t U_EXPORT2 -udata_getLength(const UDataMemory *pData) { - if(pData!=NULL && pData->pHeader!=NULL && pData->length>=0) { - /* - * subtract the header size, - * return only the size of the actual data starting at udata_getMemory() - */ - return pData->length-udata_getHeaderSize(pData->pHeader); - } else { - return -1; - } -} - -/** - * Get the memory including the data header. - * Used in cintltst/udatatst.c - * @internal - */ -U_CAPI const void * U_EXPORT2 -udata_getRawMemory(const UDataMemory *pData) { - if(pData!=NULL && pData->pHeader!=NULL) { - return pData->pHeader; - } else { - return NULL; - } -} - -U_CFUNC UBool UDataMemory_isLoaded(const UDataMemory *This) { - return This->pHeader != NULL; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************/ + + +/*---------------------------------------------------------------------------------- + * + * UDataMemory A class-like struct that serves as a handle to a piece of memory + * that contains some ICU data (resource, converters, whatever.) + * + * When an application opens ICU data (with udata_open, for example, + * a UDataMemory * is returned. + * + *----------------------------------------------------------------------------------*/ + +#include "unicode/utypes.h" +#include "cmemory.h" +#include "unicode/udata.h" + +#include "udatamem.h" + +U_CFUNC void UDataMemory_init(UDataMemory *This) { + uprv_memset(This, 0, sizeof(UDataMemory)); + This->length=-1; +} + + +U_CFUNC void UDatamemory_assign(UDataMemory *dest, UDataMemory *source) { + /* UDataMemory Assignment. Destination UDataMemory must be initialized first. */ + UBool mallocedFlag = dest->heapAllocated; + uprv_memcpy(dest, source, sizeof(UDataMemory)); + dest->heapAllocated = mallocedFlag; +} + +U_CFUNC UDataMemory *UDataMemory_createNewInstance(UErrorCode *pErr) { + UDataMemory *This; + + if (U_FAILURE(*pErr)) { + return nullptr; + } + This = (UDataMemory *)uprv_malloc(sizeof(UDataMemory)); + if (This == nullptr) { + *pErr = U_MEMORY_ALLOCATION_ERROR; } + else { + UDataMemory_init(This); + This->heapAllocated = true; + } + return This; +} + + +U_CFUNC const DataHeader * +UDataMemory_normalizeDataPointer(const void *p) { + /* allow the data to be optionally prepended with an alignment-forcing double value */ + const DataHeader *pdh = (const DataHeader *)p; + if(pdh==nullptr || (pdh->dataHeader.magic1==0xda && pdh->dataHeader.magic2==0x27)) { + return pdh; + } else { +#if U_PLATFORM == U_PF_OS400 + /* + TODO: Fix this once the compiler implements this feature. Keep in sync with genccode.c + + This is here because this platform can't currently put + const data into the read-only pages of an object or + shared library (service program). Only strings are allowed in read-only + pages, so we use char * strings to store the data. + + In order to prevent the beginning of the data from ever matching the + magic numbers we must skip the initial double. + [grhoten 4/24/2003] + */ + return (const DataHeader *)*((const void **)p+1); +#else + return (const DataHeader *)((const double *)p+1); +#endif + } +} + + +U_CFUNC void UDataMemory_setData (UDataMemory *This, const void *dataAddr) { + This->pHeader = UDataMemory_normalizeDataPointer(dataAddr); +} + + +U_CAPI void U_EXPORT2 +udata_close(UDataMemory *pData) { + if(pData!=nullptr) { + uprv_unmapFile(pData); + if(pData->heapAllocated ) { + uprv_free(pData); + } else { + UDataMemory_init(pData); + } + } +} + +U_CAPI const void * U_EXPORT2 +udata_getMemory(UDataMemory *pData) { + if(pData!=nullptr && pData->pHeader!=nullptr) { + return (char *)(pData->pHeader)+udata_getHeaderSize(pData->pHeader); + } else { + return nullptr; + } +} + +/** + * Get the length of the data item if possible. + * The length may be up to 15 bytes larger than the actual data. + * + * TODO Consider making this function public. + * It would have to return the actual length in more cases. + * For example, the length of the last item in a .dat package could be + * computed from the size of the whole .dat package minus the offset of the + * last item. + * The size of a file that was directly memory-mapped could be determined + * using some system API. + * + * In order to get perfect values for all data items, we may have to add a + * length field to UDataInfo, but that complicates data generation + * and may be overkill. + * + * @param pData The data item. + * @return the length of the data item, or -1 if not known + * @internal Currently used only in cintltst/udatatst.c + */ +U_CAPI int32_t U_EXPORT2 +udata_getLength(const UDataMemory *pData) { + if(pData!=nullptr && pData->pHeader!=nullptr && pData->length>=0) { + /* + * subtract the header size, + * return only the size of the actual data starting at udata_getMemory() + */ + return pData->length-udata_getHeaderSize(pData->pHeader); + } else { + return -1; + } +} + +/** + * Get the memory including the data header. + * Used in cintltst/udatatst.c + * @internal + */ +U_CAPI const void * U_EXPORT2 +udata_getRawMemory(const UDataMemory *pData) { + if(pData!=nullptr && pData->pHeader!=nullptr) { + return pData->pHeader; + } else { + return nullptr; + } +} + +U_CFUNC UBool UDataMemory_isLoaded(const UDataMemory *This) { + return This->pHeader != nullptr; +} diff --git a/deps/icu-small/source/common/udatamem.h b/deps/icu-small/source/common/udatamem.h index 3db2af43aad2ce..297c6f24f0d253 100644 --- a/deps/icu-small/source/common/udatamem.h +++ b/deps/icu-small/source/common/udatamem.h @@ -1,61 +1,61 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2010, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************/ - - -/*---------------------------------------------------------------------------------- - * - * UDataMemory A class-like struct that serves as a handle to a piece of memory - * that contains some ICU data (resource, converters, whatever.) - * - * When an application opens ICU data (with udata_open, for example, - * a UDataMemory * is returned. - * - *----------------------------------------------------------------------------------*/ -#ifndef __UDATAMEM_H__ -#define __UDATAMEM_H__ - -#include "unicode/udata.h" -#include "ucmndata.h" - -struct UDataMemory { - const commonDataFuncs *vFuncs; /* Function Pointers for accessing TOC */ - - const DataHeader *pHeader; /* Header of the memory being described by this */ - /* UDataMemory object. */ - const void *toc; /* For common memory, table of contents for */ - /* the pieces within. */ - UBool heapAllocated; /* True if this UDataMemory Object is on the */ - /* heap and thus needs to be deleted when closed. */ - - void *mapAddr; /* For mapped or allocated memory, the start addr. */ - /* Only non-null if a close operation should unmap */ - /* the associated data. */ - void *map; /* Handle, or other data, OS dependent. */ - /* Only non-null if a close operation should unmap */ - /* the associated data, and additional info */ - /* beyond the mapAddr is needed to do that. */ - int32_t length; /* Length of the data in bytes; -1 if unknown. */ -}; - -U_CAPI UDataMemory* U_EXPORT2 UDataMemory_createNewInstance(UErrorCode *pErr); -U_CFUNC void UDatamemory_assign (UDataMemory *dest, UDataMemory *source); -U_CFUNC void UDataMemory_init (UDataMemory *This); -U_CFUNC UBool UDataMemory_isLoaded(const UDataMemory *This); -U_CFUNC void UDataMemory_setData (UDataMemory *This, const void *dataAddr); - -U_CFUNC const DataHeader *UDataMemory_normalizeDataPointer(const void *p); - -U_CAPI int32_t U_EXPORT2 -udata_getLength(const UDataMemory *pData); - -U_CAPI const void * U_EXPORT2 -udata_getRawMemory(const UDataMemory *pData); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2010, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************/ + + +/*---------------------------------------------------------------------------------- + * + * UDataMemory A class-like struct that serves as a handle to a piece of memory + * that contains some ICU data (resource, converters, whatever.) + * + * When an application opens ICU data (with udata_open, for example, + * a UDataMemory * is returned. + * + *----------------------------------------------------------------------------------*/ +#ifndef __UDATAMEM_H__ +#define __UDATAMEM_H__ + +#include "unicode/udata.h" +#include "ucmndata.h" + +struct UDataMemory { + const commonDataFuncs *vFuncs; /* Function Pointers for accessing TOC */ + + const DataHeader *pHeader; /* Header of the memory being described by this */ + /* UDataMemory object. */ + const void *toc; /* For common memory, table of contents for */ + /* the pieces within. */ + UBool heapAllocated; /* True if this UDataMemory Object is on the */ + /* heap and thus needs to be deleted when closed. */ + + void *mapAddr; /* For mapped or allocated memory, the start addr. */ + /* Only non-null if a close operation should unmap */ + /* the associated data. */ + void *map; /* Handle, or other data, OS dependent. */ + /* Only non-null if a close operation should unmap */ + /* the associated data, and additional info */ + /* beyond the mapAddr is needed to do that. */ + int32_t length; /* Length of the data in bytes; -1 if unknown. */ +}; + +U_CAPI UDataMemory* U_EXPORT2 UDataMemory_createNewInstance(UErrorCode *pErr); +U_CFUNC void UDatamemory_assign (UDataMemory *dest, UDataMemory *source); +U_CFUNC void UDataMemory_init (UDataMemory *This); +U_CFUNC UBool UDataMemory_isLoaded(const UDataMemory *This); +U_CFUNC void UDataMemory_setData (UDataMemory *This, const void *dataAddr); + +U_CFUNC const DataHeader *UDataMemory_normalizeDataPointer(const void *p); + +U_CAPI int32_t U_EXPORT2 +udata_getLength(const UDataMemory *pData); + +U_CAPI const void * U_EXPORT2 +udata_getRawMemory(const UDataMemory *pData); + +#endif diff --git a/deps/icu-small/source/common/udataswp.cpp b/deps/icu-small/source/common/udataswp.cpp index 86f302bd9c3ab9..d901ba92b44c83 100644 --- a/deps/icu-small/source/common/udataswp.cpp +++ b/deps/icu-small/source/common/udataswp.cpp @@ -1,473 +1,473 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2003-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: udataswp.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2003jun05 -* created by: Markus W. Scherer -* -* Definitions for ICU data transformations for different platforms, -* changing between big- and little-endian data and/or between -* charset families (ASCII<->EBCDIC). -*/ - -#include -#include "unicode/utypes.h" -#include "unicode/udata.h" /* UDataInfo */ -#include "ucmndata.h" /* DataHeader */ -#include "cmemory.h" -#include "udataswp.h" - -/* swapping primitives ------------------------------------------------------ */ - -static int32_t U_CALLCONV -uprv_swapArray16(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const uint16_t *p; - uint16_t *q; - int32_t count; - uint16_t x; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || length<0 || (length&1)!=0 || outData==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* setup and swapping */ - p=(const uint16_t *)inData; - q=(uint16_t *)outData; - count=length/2; - while(count>0) { - x=*p++; - *q++=(uint16_t)((x<<8)|(x>>8)); - --count; - } - - return length; -} - -static int32_t U_CALLCONV -uprv_copyArray16(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || length<0 || (length&1)!=0 || outData==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - if(length>0 && inData!=outData) { - uprv_memcpy(outData, inData, length); - } - return length; -} - -static int32_t U_CALLCONV -uprv_swapArray32(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const uint32_t *p; - uint32_t *q; - int32_t count; - uint32_t x; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || length<0 || (length&3)!=0 || outData==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* setup and swapping */ - p=(const uint32_t *)inData; - q=(uint32_t *)outData; - count=length/4; - while(count>0) { - x=*p++; - *q++=(uint32_t)((x<<24)|((x<<8)&0xff0000)|((x>>8)&0xff00)|(x>>24)); - --count; - } - - return length; -} - -static int32_t U_CALLCONV -uprv_copyArray32(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || length<0 || (length&3)!=0 || outData==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - if(length>0 && inData!=outData) { - uprv_memcpy(outData, inData, length); - } - return length; -} - -static int32_t U_CALLCONV -uprv_swapArray64(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const uint64_t *p; - uint64_t *q; - int32_t count; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || length<0 || (length&7)!=0 || outData==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* setup and swapping */ - p=(const uint64_t *)inData; - q=(uint64_t *)outData; - count=length/8; - while(count>0) { - uint64_t x=*p++; - x=(x<<56)|((x&0xff00)<<40)|((x&0xff0000)<<24)|((x&0xff000000)<<8)| - ((x>>8)&0xff000000)|((x>>24)&0xff0000)|((x>>40)&0xff00)|(x>>56); - *q++=x; - --count; - } - - return length; -} - -static int32_t U_CALLCONV -uprv_copyArray64(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || length<0 || (length&7)!=0 || outData==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - if(length>0 && inData!=outData) { - uprv_memcpy(outData, inData, length); - } - return length; -} - -static uint16_t U_CALLCONV -uprv_readSwapUInt16(uint16_t x) { - return (uint16_t)((x<<8)|(x>>8)); -} - -static uint16_t U_CALLCONV -uprv_readDirectUInt16(uint16_t x) { - return x; -} - -static uint32_t U_CALLCONV -uprv_readSwapUInt32(uint32_t x) { - return (uint32_t)((x<<24)|((x<<8)&0xff0000)|((x>>8)&0xff00)|(x>>24)); -} - -static uint32_t U_CALLCONV -uprv_readDirectUInt32(uint32_t x) { - return x; -} - -static void U_CALLCONV -uprv_writeSwapUInt16(uint16_t *p, uint16_t x) { - *p=(uint16_t)((x<<8)|(x>>8)); -} - -static void U_CALLCONV -uprv_writeDirectUInt16(uint16_t *p, uint16_t x) { - *p=x; -} - -static void U_CALLCONV -uprv_writeSwapUInt32(uint32_t *p, uint32_t x) { - *p=(uint32_t)((x<<24)|((x<<8)&0xff0000)|((x>>8)&0xff00)|(x>>24)); -} - -static void U_CALLCONV -uprv_writeDirectUInt32(uint32_t *p, uint32_t x) { - *p=x; -} - -U_CAPI int16_t U_EXPORT2 -udata_readInt16(const UDataSwapper *ds, int16_t x) { - return (int16_t)ds->readUInt16((uint16_t)x); -} - -U_CAPI int32_t U_EXPORT2 -udata_readInt32(const UDataSwapper *ds, int32_t x) { - return (int32_t)ds->readUInt32((uint32_t)x); -} - -/** - * Swap a block of invariant, NUL-terminated strings, but not padding - * bytes after the last string. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -udata_swapInvStringBlock(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const char *inChars; - int32_t stringsLength; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || length<0 || (length>0 && outData==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* reduce the strings length to not include bytes after the last NUL */ - inChars=(const char *)inData; - stringsLength=length; - while(stringsLength>0 && inChars[stringsLength-1]!=0) { - --stringsLength; - } - - /* swap up to the last NUL */ - ds->swapInvChars(ds, inData, stringsLength, outData, pErrorCode); - - /* copy the bytes after the last NUL */ - if(inData!=outData && length>stringsLength) { - uprv_memcpy((char *)outData+stringsLength, inChars+stringsLength, length-stringsLength); - } - - /* return the length including padding bytes */ - if(U_SUCCESS(*pErrorCode)) { - return length; - } else { - return 0; - } -} - -U_CAPI void U_EXPORT2 -udata_printError(const UDataSwapper *ds, - const char *fmt, - ...) { - va_list args; - - if(ds->printError!=NULL) { - va_start(args, fmt); - ds->printError(ds->printErrorContext, fmt, args); - va_end(args); - } -} - -/* swap a data header ------------------------------------------------------- */ - -U_CAPI int32_t U_EXPORT2 -udata_swapDataHeader(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const DataHeader *pHeader; - uint16_t headerSize, infoSize; - - /* argument checking */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || length<-1 || (length>0 && outData==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* check minimum length and magic bytes */ - pHeader=(const DataHeader *)inData; - if( (length>=0 && length<(int32_t)sizeof(DataHeader)) || - pHeader->dataHeader.magic1!=0xda || - pHeader->dataHeader.magic2!=0x27 || - pHeader->info.sizeofUChar!=2 - ) { - udata_printError(ds, "udata_swapDataHeader(): initial bytes do not look like ICU data\n"); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - headerSize=ds->readUInt16(pHeader->dataHeader.headerSize); - infoSize=ds->readUInt16(pHeader->info.size); - - if( headerSizedataHeader)+infoSize) || - (length>=0 && length0) { - DataHeader *outHeader; - const char *s; - int32_t maxLength; - - /* Most of the fields are just bytes and need no swapping. */ - if(inData!=outData) { - uprv_memcpy(outData, inData, headerSize); - } - outHeader=(DataHeader *)outData; - - outHeader->info.isBigEndian = ds->outIsBigEndian; - outHeader->info.charsetFamily = ds->outCharset; - - /* swap headerSize */ - ds->swapArray16(ds, &pHeader->dataHeader.headerSize, 2, &outHeader->dataHeader.headerSize, pErrorCode); - - /* swap UDataInfo size and reservedWord */ - ds->swapArray16(ds, &pHeader->info.size, 4, &outHeader->info.size, pErrorCode); - - /* swap copyright statement after the UDataInfo */ - infoSize+=sizeof(pHeader->dataHeader); - s=(const char *)inData+infoSize; - maxLength=headerSize-infoSize; - /* get the length of the string */ - for(length=0; lengthswapInvChars(ds, s, length, (char *)outData+infoSize, pErrorCode); - } - - return headerSize; -} - -/* API functions ------------------------------------------------------------ */ - -U_CAPI UDataSwapper * U_EXPORT2 -udata_openSwapper(UBool inIsBigEndian, uint8_t inCharset, - UBool outIsBigEndian, uint8_t outCharset, - UErrorCode *pErrorCode) { - UDataSwapper *swapper; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return NULL; - } - if(inCharset>U_EBCDIC_FAMILY || outCharset>U_EBCDIC_FAMILY) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - /* allocate the swapper */ - swapper=(UDataSwapper *)uprv_malloc(sizeof(UDataSwapper)); - if(swapper==NULL) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uprv_memset(swapper, 0, sizeof(UDataSwapper)); - - /* set values and functions pointers according to in/out parameters */ - swapper->inIsBigEndian=inIsBigEndian; - swapper->inCharset=inCharset; - swapper->outIsBigEndian=outIsBigEndian; - swapper->outCharset=outCharset; - - swapper->readUInt16= inIsBigEndian==U_IS_BIG_ENDIAN ? uprv_readDirectUInt16 : uprv_readSwapUInt16; - swapper->readUInt32= inIsBigEndian==U_IS_BIG_ENDIAN ? uprv_readDirectUInt32 : uprv_readSwapUInt32; - - swapper->writeUInt16= outIsBigEndian==U_IS_BIG_ENDIAN ? uprv_writeDirectUInt16 : uprv_writeSwapUInt16; - swapper->writeUInt32= outIsBigEndian==U_IS_BIG_ENDIAN ? uprv_writeDirectUInt32 : uprv_writeSwapUInt32; - - swapper->compareInvChars= outCharset==U_ASCII_FAMILY ? uprv_compareInvAscii : uprv_compareInvEbcdic; - - if(inIsBigEndian==outIsBigEndian) { - swapper->swapArray16=uprv_copyArray16; - swapper->swapArray32=uprv_copyArray32; - swapper->swapArray64=uprv_copyArray64; - } else { - swapper->swapArray16=uprv_swapArray16; - swapper->swapArray32=uprv_swapArray32; - swapper->swapArray64=uprv_swapArray64; - } - - if(inCharset==U_ASCII_FAMILY) { - swapper->swapInvChars= outCharset==U_ASCII_FAMILY ? uprv_copyAscii : uprv_ebcdicFromAscii; - } else /* U_EBCDIC_FAMILY */ { - swapper->swapInvChars= outCharset==U_EBCDIC_FAMILY ? uprv_copyEbcdic : uprv_asciiFromEbcdic; - } - - return swapper; -} - -U_CAPI UDataSwapper * U_EXPORT2 -udata_openSwapperForInputData(const void *data, int32_t length, - UBool outIsBigEndian, uint8_t outCharset, - UErrorCode *pErrorCode) { - const DataHeader *pHeader; - uint16_t headerSize, infoSize; - UBool inIsBigEndian; - int8_t inCharset; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return NULL; - } - if( data==NULL || - (length>=0 && length<(int32_t)sizeof(DataHeader)) || - outCharset>U_EBCDIC_FAMILY - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - pHeader=(const DataHeader *)data; - if( (length>=0 && length<(int32_t)sizeof(DataHeader)) || - pHeader->dataHeader.magic1!=0xda || - pHeader->dataHeader.magic2!=0x27 || - pHeader->info.sizeofUChar!=2 - ) { - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - inIsBigEndian=(UBool)pHeader->info.isBigEndian; - inCharset=pHeader->info.charsetFamily; - - if(inIsBigEndian==U_IS_BIG_ENDIAN) { - headerSize=pHeader->dataHeader.headerSize; - infoSize=pHeader->info.size; - } else { - headerSize=uprv_readSwapUInt16(pHeader->dataHeader.headerSize); - infoSize=uprv_readSwapUInt16(pHeader->info.size); - } - - if( headerSizedataHeader)+infoSize) || - (length>=0 && lengthEBCDIC). +*/ + +#include +#include "unicode/utypes.h" +#include "unicode/udata.h" /* UDataInfo */ +#include "ucmndata.h" /* DataHeader */ +#include "cmemory.h" +#include "udataswp.h" + +/* swapping primitives ------------------------------------------------------ */ + +static int32_t U_CALLCONV +uprv_swapArray16(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const uint16_t *p; + uint16_t *q; + int32_t count; + uint16_t x; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<0 || (length&1)!=0 || outData==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* setup and swapping */ + p=(const uint16_t *)inData; + q=(uint16_t *)outData; + count=length/2; + while(count>0) { + x=*p++; + *q++=(uint16_t)((x<<8)|(x>>8)); + --count; + } + + return length; +} + +static int32_t U_CALLCONV +uprv_copyArray16(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<0 || (length&1)!=0 || outData==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + if(length>0 && inData!=outData) { + uprv_memcpy(outData, inData, length); + } + return length; +} + +static int32_t U_CALLCONV +uprv_swapArray32(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const uint32_t *p; + uint32_t *q; + int32_t count; + uint32_t x; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<0 || (length&3)!=0 || outData==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* setup and swapping */ + p=(const uint32_t *)inData; + q=(uint32_t *)outData; + count=length/4; + while(count>0) { + x=*p++; + *q++=(uint32_t)((x<<24)|((x<<8)&0xff0000)|((x>>8)&0xff00)|(x>>24)); + --count; + } + + return length; +} + +static int32_t U_CALLCONV +uprv_copyArray32(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<0 || (length&3)!=0 || outData==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + if(length>0 && inData!=outData) { + uprv_memcpy(outData, inData, length); + } + return length; +} + +static int32_t U_CALLCONV +uprv_swapArray64(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const uint64_t *p; + uint64_t *q; + int32_t count; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<0 || (length&7)!=0 || outData==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* setup and swapping */ + p=(const uint64_t *)inData; + q=(uint64_t *)outData; + count=length/8; + while(count>0) { + uint64_t x=*p++; + x=(x<<56)|((x&0xff00)<<40)|((x&0xff0000)<<24)|((x&0xff000000)<<8)| + ((x>>8)&0xff000000)|((x>>24)&0xff0000)|((x>>40)&0xff00)|(x>>56); + *q++=x; + --count; + } + + return length; +} + +static int32_t U_CALLCONV +uprv_copyArray64(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<0 || (length&7)!=0 || outData==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + if(length>0 && inData!=outData) { + uprv_memcpy(outData, inData, length); + } + return length; +} + +static uint16_t U_CALLCONV +uprv_readSwapUInt16(uint16_t x) { + return (uint16_t)((x<<8)|(x>>8)); +} + +static uint16_t U_CALLCONV +uprv_readDirectUInt16(uint16_t x) { + return x; +} + +static uint32_t U_CALLCONV +uprv_readSwapUInt32(uint32_t x) { + return (uint32_t)((x<<24)|((x<<8)&0xff0000)|((x>>8)&0xff00)|(x>>24)); +} + +static uint32_t U_CALLCONV +uprv_readDirectUInt32(uint32_t x) { + return x; +} + +static void U_CALLCONV +uprv_writeSwapUInt16(uint16_t *p, uint16_t x) { + *p=(uint16_t)((x<<8)|(x>>8)); +} + +static void U_CALLCONV +uprv_writeDirectUInt16(uint16_t *p, uint16_t x) { + *p=x; +} + +static void U_CALLCONV +uprv_writeSwapUInt32(uint32_t *p, uint32_t x) { + *p=(uint32_t)((x<<24)|((x<<8)&0xff0000)|((x>>8)&0xff00)|(x>>24)); +} + +static void U_CALLCONV +uprv_writeDirectUInt32(uint32_t *p, uint32_t x) { + *p=x; +} + +U_CAPI int16_t U_EXPORT2 +udata_readInt16(const UDataSwapper *ds, int16_t x) { + return (int16_t)ds->readUInt16((uint16_t)x); +} + +U_CAPI int32_t U_EXPORT2 +udata_readInt32(const UDataSwapper *ds, int32_t x) { + return (int32_t)ds->readUInt32((uint32_t)x); +} + +/** + * Swap a block of invariant, NUL-terminated strings, but not padding + * bytes after the last string. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +udata_swapInvStringBlock(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const char *inChars; + int32_t stringsLength; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<0 || (length>0 && outData==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* reduce the strings length to not include bytes after the last NUL */ + inChars=(const char *)inData; + stringsLength=length; + while(stringsLength>0 && inChars[stringsLength-1]!=0) { + --stringsLength; + } + + /* swap up to the last NUL */ + ds->swapInvChars(ds, inData, stringsLength, outData, pErrorCode); + + /* copy the bytes after the last NUL */ + if(inData!=outData && length>stringsLength) { + uprv_memcpy((char *)outData+stringsLength, inChars+stringsLength, length-stringsLength); + } + + /* return the length including padding bytes */ + if(U_SUCCESS(*pErrorCode)) { + return length; + } else { + return 0; + } +} + +U_CAPI void U_EXPORT2 +udata_printError(const UDataSwapper *ds, + const char *fmt, + ...) { + va_list args; + + if(ds->printError!=nullptr) { + va_start(args, fmt); + ds->printError(ds->printErrorContext, fmt, args); + va_end(args); + } +} + +/* swap a data header ------------------------------------------------------- */ + +U_CAPI int32_t U_EXPORT2 +udata_swapDataHeader(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const DataHeader *pHeader; + uint16_t headerSize, infoSize; + + /* argument checking */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<-1 || (length>0 && outData==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* check minimum length and magic bytes */ + pHeader=(const DataHeader *)inData; + if( (length>=0 && length<(int32_t)sizeof(DataHeader)) || + pHeader->dataHeader.magic1!=0xda || + pHeader->dataHeader.magic2!=0x27 || + pHeader->info.sizeofUChar!=2 + ) { + udata_printError(ds, "udata_swapDataHeader(): initial bytes do not look like ICU data\n"); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + headerSize=ds->readUInt16(pHeader->dataHeader.headerSize); + infoSize=ds->readUInt16(pHeader->info.size); + + if( headerSizedataHeader)+infoSize) || + (length>=0 && length0) { + DataHeader *outHeader; + const char *s; + int32_t maxLength; + + /* Most of the fields are just bytes and need no swapping. */ + if(inData!=outData) { + uprv_memcpy(outData, inData, headerSize); + } + outHeader=(DataHeader *)outData; + + outHeader->info.isBigEndian = ds->outIsBigEndian; + outHeader->info.charsetFamily = ds->outCharset; + + /* swap headerSize */ + ds->swapArray16(ds, &pHeader->dataHeader.headerSize, 2, &outHeader->dataHeader.headerSize, pErrorCode); + + /* swap UDataInfo size and reservedWord */ + ds->swapArray16(ds, &pHeader->info.size, 4, &outHeader->info.size, pErrorCode); + + /* swap copyright statement after the UDataInfo */ + infoSize+=sizeof(pHeader->dataHeader); + s=(const char *)inData+infoSize; + maxLength=headerSize-infoSize; + /* get the length of the string */ + for(length=0; lengthswapInvChars(ds, s, length, (char *)outData+infoSize, pErrorCode); + } + + return headerSize; +} + +/* API functions ------------------------------------------------------------ */ + +U_CAPI UDataSwapper * U_EXPORT2 +udata_openSwapper(UBool inIsBigEndian, uint8_t inCharset, + UBool outIsBigEndian, uint8_t outCharset, + UErrorCode *pErrorCode) { + UDataSwapper *swapper; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return nullptr; + } + if(inCharset>U_EBCDIC_FAMILY || outCharset>U_EBCDIC_FAMILY) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + /* allocate the swapper */ + swapper=(UDataSwapper *)uprv_malloc(sizeof(UDataSwapper)); + if(swapper==nullptr) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memset(swapper, 0, sizeof(UDataSwapper)); + + /* set values and functions pointers according to in/out parameters */ + swapper->inIsBigEndian=inIsBigEndian; + swapper->inCharset=inCharset; + swapper->outIsBigEndian=outIsBigEndian; + swapper->outCharset=outCharset; + + swapper->readUInt16= inIsBigEndian==U_IS_BIG_ENDIAN ? uprv_readDirectUInt16 : uprv_readSwapUInt16; + swapper->readUInt32= inIsBigEndian==U_IS_BIG_ENDIAN ? uprv_readDirectUInt32 : uprv_readSwapUInt32; + + swapper->writeUInt16= outIsBigEndian==U_IS_BIG_ENDIAN ? uprv_writeDirectUInt16 : uprv_writeSwapUInt16; + swapper->writeUInt32= outIsBigEndian==U_IS_BIG_ENDIAN ? uprv_writeDirectUInt32 : uprv_writeSwapUInt32; + + swapper->compareInvChars= outCharset==U_ASCII_FAMILY ? uprv_compareInvAscii : uprv_compareInvEbcdic; + + if(inIsBigEndian==outIsBigEndian) { + swapper->swapArray16=uprv_copyArray16; + swapper->swapArray32=uprv_copyArray32; + swapper->swapArray64=uprv_copyArray64; + } else { + swapper->swapArray16=uprv_swapArray16; + swapper->swapArray32=uprv_swapArray32; + swapper->swapArray64=uprv_swapArray64; + } + + if(inCharset==U_ASCII_FAMILY) { + swapper->swapInvChars= outCharset==U_ASCII_FAMILY ? uprv_copyAscii : uprv_ebcdicFromAscii; + } else /* U_EBCDIC_FAMILY */ { + swapper->swapInvChars= outCharset==U_EBCDIC_FAMILY ? uprv_copyEbcdic : uprv_asciiFromEbcdic; + } + + return swapper; +} + +U_CAPI UDataSwapper * U_EXPORT2 +udata_openSwapperForInputData(const void *data, int32_t length, + UBool outIsBigEndian, uint8_t outCharset, + UErrorCode *pErrorCode) { + const DataHeader *pHeader; + uint16_t headerSize, infoSize; + UBool inIsBigEndian; + int8_t inCharset; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return nullptr; + } + if( data==nullptr || + (length>=0 && length<(int32_t)sizeof(DataHeader)) || + outCharset>U_EBCDIC_FAMILY + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + pHeader=(const DataHeader *)data; + if( (length>=0 && length<(int32_t)sizeof(DataHeader)) || + pHeader->dataHeader.magic1!=0xda || + pHeader->dataHeader.magic2!=0x27 || + pHeader->info.sizeofUChar!=2 + ) { + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + inIsBigEndian=(UBool)pHeader->info.isBigEndian; + inCharset=pHeader->info.charsetFamily; + + if(inIsBigEndian==U_IS_BIG_ENDIAN) { + headerSize=pHeader->dataHeader.headerSize; + infoSize=pHeader->info.size; + } else { + headerSize=uprv_readSwapUInt16(pHeader->dataHeader.headerSize); + infoSize=uprv_readSwapUInt16(pHeader->info.size); + } + + if( headerSizedataHeader)+infoSize) || + (length>=0 && lengthEBCDIC). -*/ - -#ifndef __UDATASWP_H__ -#define __UDATASWP_H__ - -#include -#include "unicode/utypes.h" - -/* forward declaration */ - -U_CDECL_BEGIN - -struct UDataSwapper; -typedef struct UDataSwapper UDataSwapper; - -/** - * Function type for data transformation. - * Transforms data, or just returns the length of the data if - * the input length is -1. - * Swap functions assume that their data pointers are aligned properly. - * - * Quick implementation outline: - * (best to copy and adapt and existing swapper implementation) - * check that the data looks like the expected format - * if(length<0) { - * preflight: - * never dereference outData - * read inData and determine the data size - * assume that inData is long enough for this - * } else { - * outData can be NULL if length==0 - * inData==outData (in-place swapping) possible but not required! - * verify that length>=(actual size) - * if there is a chance that not every byte up to size is reached - * due to padding etc.: - * if(inData!=outData) { - * memcpy(outData, inData, actual size); - * } - * swap contents - * } - * return actual size - * - * Further implementation notes: - * - read integers from inData before swapping them - * because in-place swapping can make them unreadable - * - compareInvChars compares a local Unicode string with already-swapped - * output charset strings - * - * @param ds Pointer to UDataSwapper containing global data about the - * transformation and function pointers for handling primitive - * types. - * @param inData Pointer to the input data to be transformed or examined. - * @param length Length of the data, counting bytes. May be -1 for preflighting. - * If length>=0, then transform the data. - * If length==-1, then only determine the length of the data. - * The length cannot be determined from the data itself for all - * types of data (e.g., not for simple arrays of integers). - * @param outData Pointer to the output data buffer. - * If length>=0 (transformation), then the output buffer must - * have a capacity of at least length. - * If length==-1, then outData will not be used and can be NULL. - * @param pErrorCode ICU UErrorCode parameter, must not be NULL and must - * fulfill U_SUCCESS on input. - * @return The actual length of the data. - * - * @see UDataSwapper - * @internal ICU 2.8 - */ -typedef int32_t U_CALLCONV -UDataSwapFn(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -/** - * Convert one uint16_t from input to platform endianness. - * @internal ICU 2.8 - */ -typedef uint16_t U_CALLCONV -UDataReadUInt16(uint16_t x); - -/** - * Convert one uint32_t from input to platform endianness. - * @internal ICU 2.8 - */ -typedef uint32_t U_CALLCONV -UDataReadUInt32(uint32_t x); - -/** - * Convert one uint16_t from platform to input endianness. - * @internal ICU 2.8 - */ -typedef void U_CALLCONV -UDataWriteUInt16(uint16_t *p, uint16_t x); - -/** - * Convert one uint32_t from platform to input endianness. - * @internal ICU 2.8 - */ -typedef void U_CALLCONV -UDataWriteUInt32(uint32_t *p, uint32_t x); - -/** - * Compare invariant-character strings, one in the output data and the - * other one caller-provided in Unicode. - * An output data string is compared because strings are usually swapped - * before the rest of the data, to allow for sorting of string tables - * according to the output charset. - * You can use -1 for the length parameters of NUL-terminated strings as usual. - * Returns Unicode code point order for invariant characters. - * @internal ICU 2.8 - */ -typedef int32_t U_CALLCONV -UDataCompareInvChars(const UDataSwapper *ds, - const char *outString, int32_t outLength, - const UChar *localString, int32_t localLength); - -/** - * Function for message output when an error occurs during data swapping. - * A format string and variable number of arguments are passed - * like for vprintf(). - * - * @param context A function-specific context pointer. - * @param fmt The format string. - * @param args The arguments for format string inserts. - * - * @internal ICU 2.8 - */ -typedef void U_CALLCONV -UDataPrintError(void *context, const char *fmt, va_list args); - -struct UDataSwapper { - /** Input endianness. @internal ICU 2.8 */ - UBool inIsBigEndian; - /** Input charset family. @see U_CHARSET_FAMILY @internal ICU 2.8 */ - uint8_t inCharset; - /** Output endianness. @internal ICU 2.8 */ - UBool outIsBigEndian; - /** Output charset family. @see U_CHARSET_FAMILY @internal ICU 2.8 */ - uint8_t outCharset; - - /* basic functions for reading data values */ - - /** Convert one uint16_t from input to platform endianness. @internal ICU 2.8 */ - UDataReadUInt16 *readUInt16; - /** Convert one uint32_t from input to platform endianness. @internal ICU 2.8 */ - UDataReadUInt32 *readUInt32; - /** Compare an invariant-character output string with a local one. @internal ICU 2.8 */ - UDataCompareInvChars *compareInvChars; - - /* basic functions for writing data values */ - - /** Convert one uint16_t from platform to input endianness. @internal ICU 2.8 */ - UDataWriteUInt16 *writeUInt16; - /** Convert one uint32_t from platform to input endianness. @internal ICU 2.8 */ - UDataWriteUInt32 *writeUInt32; - - /* basic functions for data transformations */ - - /** Transform an array of 16-bit integers. @internal ICU 2.8 */ - UDataSwapFn *swapArray16; - /** Transform an array of 32-bit integers. @internal ICU 2.8 */ - UDataSwapFn *swapArray32; - /** Transform an array of 64-bit integers. @internal ICU 53 */ - UDataSwapFn *swapArray64; - /** Transform an invariant-character string. @internal ICU 2.8 */ - UDataSwapFn *swapInvChars; - - /** - * Function for message output when an error occurs during data swapping. - * Can be NULL. - * @internal ICU 2.8 - */ - UDataPrintError *printError; - /** Context pointer for printError. @internal ICU 2.8 */ - void *printErrorContext; -}; - -U_CDECL_END - -U_CAPI UDataSwapper * U_EXPORT2 -udata_openSwapper(UBool inIsBigEndian, uint8_t inCharset, - UBool outIsBigEndian, uint8_t outCharset, - UErrorCode *pErrorCode); - -/** - * Open a UDataSwapper for the given input data and the specified output - * characteristics. - * Values of -1 for any of the characteristics mean the local platform's - * characteristics. - * - * @see udata_swap - * @internal ICU 2.8 - */ -U_CAPI UDataSwapper * U_EXPORT2 -udata_openSwapperForInputData(const void *data, int32_t length, - UBool outIsBigEndian, uint8_t outCharset, - UErrorCode *pErrorCode); - -U_CAPI void U_EXPORT2 -udata_closeSwapper(UDataSwapper *ds); - -/** - * Read the beginning of an ICU data piece, recognize magic bytes, - * swap the structure. - * Set a U_UNSUPPORTED_ERROR if it does not look like an ICU data piece. - * - * @return The size of the data header, in bytes. - * - * @internal ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -udata_swapDataHeader(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -/** - * Convert one int16_t from input to platform endianness. - * @internal ICU 2.8 - */ -U_CAPI int16_t U_EXPORT2 -udata_readInt16(const UDataSwapper *ds, int16_t x); - -/** - * Convert one int32_t from input to platform endianness. - * @internal ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -udata_readInt32(const UDataSwapper *ds, int32_t x); - -/** - * Swap a block of invariant, NUL-terminated strings, but not padding - * bytes after the last string. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -udata_swapInvStringBlock(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -U_CAPI void U_EXPORT2 -udata_printError(const UDataSwapper *ds, - const char *fmt, - ...); - -/* internal exports from putil.c -------------------------------------------- */ - -/* declared here to keep them out of the public putil.h */ - -/** - * Swap invariant char * strings ASCII->EBCDIC. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -uprv_ebcdicFromAscii(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -/** - * Copy invariant ASCII char * strings and verify they are invariant. - * @internal - */ -U_CFUNC int32_t -uprv_copyAscii(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -/** - * Swap invariant char * strings EBCDIC->ASCII. - * @internal - */ -U_CFUNC int32_t -uprv_asciiFromEbcdic(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -/** - * Copy invariant EBCDIC char * strings and verify they are invariant. - * @internal - */ -U_CFUNC int32_t -uprv_copyEbcdic(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -/** - * Compare ASCII invariant char * with Unicode invariant UChar * - * @internal - */ -U_CFUNC int32_t -uprv_compareInvAscii(const UDataSwapper *ds, - const char *outString, int32_t outLength, - const UChar *localString, int32_t localLength); - -/** - * Compare EBCDIC invariant char * with Unicode invariant UChar * - * @internal - */ -U_CFUNC int32_t -uprv_compareInvEbcdic(const UDataSwapper *ds, - const char *outString, int32_t outLength, - const UChar *localString, int32_t localLength); - -/** - * \def uprv_compareInvWithUChar - * Compare an invariant-character strings with a UChar string - * @internal - */ -#if U_CHARSET_FAMILY==U_ASCII_FAMILY -# define uprv_compareInvWithUChar uprv_compareInvAscii -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY -# define uprv_compareInvWithUChar uprv_compareInvEbcdic -#else -# error Unknown charset family! -#endif - -// utrie_swap.cpp -----------------------------------------------------------*** - -/** - * Swaps a serialized UTrie. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -utrie_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -/** - * Swaps a serialized UTrie2. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -utrie2_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -/** - * Swaps a serialized UCPTrie. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -ucptrie_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -/** - * Swaps a serialized UTrie, UTrie2, or UCPTrie. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -utrie_swapAnyVersion(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -/* material... -------------------------------------------------------------- */ - -#if 0 - -/* udata.h */ - -/** - * Public API function in udata.c - * - * Same as udata_openChoice() but automatically swaps the data. - * isAcceptable, if not NULL, may accept data with endianness and charset family - * different from the current platform's properties. - * If the data is acceptable and the platform properties do not match, then - * the swap function is called to swap an allocated version of the data. - * Preflighting may or may not be performed depending on whether the size of - * the loaded data item is known. - * - * @param isAcceptable Same as for udata_openChoice(). May be NULL. - * - * @internal ICU 2.8 - */ -U_CAPI UDataMemory * U_EXPORT2 -udata_openSwap(const char *path, const char *type, const char *name, - UDataMemoryIsAcceptable *isAcceptable, void *isAcceptableContext, - UDataSwapFn *swap, - UDataPrintError *printError, void *printErrorContext, - UErrorCode *pErrorCode); - -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2003-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: udataswp.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2003jun05 +* created by: Markus W. Scherer +* +* Definitions for ICU data transformations for different platforms, +* changing between big- and little-endian data and/or between +* charset families (ASCII<->EBCDIC). +*/ + +#ifndef __UDATASWP_H__ +#define __UDATASWP_H__ + +#include +#include "unicode/utypes.h" + +/* forward declaration */ + +U_CDECL_BEGIN + +struct UDataSwapper; +typedef struct UDataSwapper UDataSwapper; + +/** + * Function type for data transformation. + * Transforms data, or just returns the length of the data if + * the input length is -1. + * Swap functions assume that their data pointers are aligned properly. + * + * Quick implementation outline: + * (best to copy and adapt and existing swapper implementation) + * check that the data looks like the expected format + * if(length<0) { + * preflight: + * never dereference outData + * read inData and determine the data size + * assume that inData is long enough for this + * } else { + * outData can be NULL if length==0 + * inData==outData (in-place swapping) possible but not required! + * verify that length>=(actual size) + * if there is a chance that not every byte up to size is reached + * due to padding etc.: + * if(inData!=outData) { + * memcpy(outData, inData, actual size); + * } + * swap contents + * } + * return actual size + * + * Further implementation notes: + * - read integers from inData before swapping them + * because in-place swapping can make them unreadable + * - compareInvChars compares a local Unicode string with already-swapped + * output charset strings + * + * @param ds Pointer to UDataSwapper containing global data about the + * transformation and function pointers for handling primitive + * types. + * @param inData Pointer to the input data to be transformed or examined. + * @param length Length of the data, counting bytes. May be -1 for preflighting. + * If length>=0, then transform the data. + * If length==-1, then only determine the length of the data. + * The length cannot be determined from the data itself for all + * types of data (e.g., not for simple arrays of integers). + * @param outData Pointer to the output data buffer. + * If length>=0 (transformation), then the output buffer must + * have a capacity of at least length. + * If length==-1, then outData will not be used and can be NULL. + * @param pErrorCode ICU UErrorCode parameter, must not be NULL and must + * fulfill U_SUCCESS on input. + * @return The actual length of the data. + * + * @see UDataSwapper + * @internal ICU 2.8 + */ +typedef int32_t U_CALLCONV +UDataSwapFn(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +/** + * Convert one uint16_t from input to platform endianness. + * @internal ICU 2.8 + */ +typedef uint16_t U_CALLCONV +UDataReadUInt16(uint16_t x); + +/** + * Convert one uint32_t from input to platform endianness. + * @internal ICU 2.8 + */ +typedef uint32_t U_CALLCONV +UDataReadUInt32(uint32_t x); + +/** + * Convert one uint16_t from platform to input endianness. + * @internal ICU 2.8 + */ +typedef void U_CALLCONV +UDataWriteUInt16(uint16_t *p, uint16_t x); + +/** + * Convert one uint32_t from platform to input endianness. + * @internal ICU 2.8 + */ +typedef void U_CALLCONV +UDataWriteUInt32(uint32_t *p, uint32_t x); + +/** + * Compare invariant-character strings, one in the output data and the + * other one caller-provided in Unicode. + * An output data string is compared because strings are usually swapped + * before the rest of the data, to allow for sorting of string tables + * according to the output charset. + * You can use -1 for the length parameters of NUL-terminated strings as usual. + * Returns Unicode code point order for invariant characters. + * @internal ICU 2.8 + */ +typedef int32_t U_CALLCONV +UDataCompareInvChars(const UDataSwapper *ds, + const char *outString, int32_t outLength, + const UChar *localString, int32_t localLength); + +/** + * Function for message output when an error occurs during data swapping. + * A format string and variable number of arguments are passed + * like for vprintf(). + * + * @param context A function-specific context pointer. + * @param fmt The format string. + * @param args The arguments for format string inserts. + * + * @internal ICU 2.8 + */ +typedef void U_CALLCONV +UDataPrintError(void *context, const char *fmt, va_list args); + +struct UDataSwapper { + /** Input endianness. @internal ICU 2.8 */ + UBool inIsBigEndian; + /** Input charset family. @see U_CHARSET_FAMILY @internal ICU 2.8 */ + uint8_t inCharset; + /** Output endianness. @internal ICU 2.8 */ + UBool outIsBigEndian; + /** Output charset family. @see U_CHARSET_FAMILY @internal ICU 2.8 */ + uint8_t outCharset; + + /* basic functions for reading data values */ + + /** Convert one uint16_t from input to platform endianness. @internal ICU 2.8 */ + UDataReadUInt16 *readUInt16; + /** Convert one uint32_t from input to platform endianness. @internal ICU 2.8 */ + UDataReadUInt32 *readUInt32; + /** Compare an invariant-character output string with a local one. @internal ICU 2.8 */ + UDataCompareInvChars *compareInvChars; + + /* basic functions for writing data values */ + + /** Convert one uint16_t from platform to input endianness. @internal ICU 2.8 */ + UDataWriteUInt16 *writeUInt16; + /** Convert one uint32_t from platform to input endianness. @internal ICU 2.8 */ + UDataWriteUInt32 *writeUInt32; + + /* basic functions for data transformations */ + + /** Transform an array of 16-bit integers. @internal ICU 2.8 */ + UDataSwapFn *swapArray16; + /** Transform an array of 32-bit integers. @internal ICU 2.8 */ + UDataSwapFn *swapArray32; + /** Transform an array of 64-bit integers. @internal ICU 53 */ + UDataSwapFn *swapArray64; + /** Transform an invariant-character string. @internal ICU 2.8 */ + UDataSwapFn *swapInvChars; + + /** + * Function for message output when an error occurs during data swapping. + * Can be NULL. + * @internal ICU 2.8 + */ + UDataPrintError *printError; + /** Context pointer for printError. @internal ICU 2.8 */ + void *printErrorContext; +}; + +U_CDECL_END + +U_CAPI UDataSwapper * U_EXPORT2 +udata_openSwapper(UBool inIsBigEndian, uint8_t inCharset, + UBool outIsBigEndian, uint8_t outCharset, + UErrorCode *pErrorCode); + +/** + * Open a UDataSwapper for the given input data and the specified output + * characteristics. + * Values of -1 for any of the characteristics mean the local platform's + * characteristics. + * + * @see udata_swap + * @internal ICU 2.8 + */ +U_CAPI UDataSwapper * U_EXPORT2 +udata_openSwapperForInputData(const void *data, int32_t length, + UBool outIsBigEndian, uint8_t outCharset, + UErrorCode *pErrorCode); + +U_CAPI void U_EXPORT2 +udata_closeSwapper(UDataSwapper *ds); + +/** + * Read the beginning of an ICU data piece, recognize magic bytes, + * swap the structure. + * Set a U_UNSUPPORTED_ERROR if it does not look like an ICU data piece. + * + * @return The size of the data header, in bytes. + * + * @internal ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +udata_swapDataHeader(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +/** + * Convert one int16_t from input to platform endianness. + * @internal ICU 2.8 + */ +U_CAPI int16_t U_EXPORT2 +udata_readInt16(const UDataSwapper *ds, int16_t x); + +/** + * Convert one int32_t from input to platform endianness. + * @internal ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +udata_readInt32(const UDataSwapper *ds, int32_t x); + +/** + * Swap a block of invariant, NUL-terminated strings, but not padding + * bytes after the last string. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +udata_swapInvStringBlock(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +U_CAPI void U_EXPORT2 +udata_printError(const UDataSwapper *ds, + const char *fmt, + ...); + +/* internal exports from putil.c -------------------------------------------- */ + +/* declared here to keep them out of the public putil.h */ + +/** + * Swap invariant char * strings ASCII->EBCDIC. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +uprv_ebcdicFromAscii(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +/** + * Copy invariant ASCII char * strings and verify they are invariant. + * @internal + */ +U_CFUNC int32_t +uprv_copyAscii(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +/** + * Swap invariant char * strings EBCDIC->ASCII. + * @internal + */ +U_CFUNC int32_t +uprv_asciiFromEbcdic(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +/** + * Copy invariant EBCDIC char * strings and verify they are invariant. + * @internal + */ +U_CFUNC int32_t +uprv_copyEbcdic(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +/** + * Compare ASCII invariant char * with Unicode invariant UChar * + * @internal + */ +U_CFUNC int32_t +uprv_compareInvAscii(const UDataSwapper *ds, + const char *outString, int32_t outLength, + const UChar *localString, int32_t localLength); + +/** + * Compare EBCDIC invariant char * with Unicode invariant UChar * + * @internal + */ +U_CFUNC int32_t +uprv_compareInvEbcdic(const UDataSwapper *ds, + const char *outString, int32_t outLength, + const UChar *localString, int32_t localLength); + +/** + * \def uprv_compareInvWithUChar + * Compare an invariant-character strings with a UChar string + * @internal + */ +#if U_CHARSET_FAMILY==U_ASCII_FAMILY +# define uprv_compareInvWithUChar uprv_compareInvAscii +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY +# define uprv_compareInvWithUChar uprv_compareInvEbcdic +#else +# error Unknown charset family! +#endif + +// utrie_swap.cpp -----------------------------------------------------------*** + +/** + * Swaps a serialized UTrie. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +utrie_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +/** + * Swaps a serialized UTrie2. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +utrie2_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +/** + * Swaps a serialized UCPTrie. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +ucptrie_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +/** + * Swaps a serialized UTrie, UTrie2, or UCPTrie. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +utrie_swapAnyVersion(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +/* material... -------------------------------------------------------------- */ + +#if 0 + +/* udata.h */ + +/** + * Public API function in udata.c + * + * Same as udata_openChoice() but automatically swaps the data. + * isAcceptable, if not NULL, may accept data with endianness and charset family + * different from the current platform's properties. + * If the data is acceptable and the platform properties do not match, then + * the swap function is called to swap an allocated version of the data. + * Preflighting may or may not be performed depending on whether the size of + * the loaded data item is known. + * + * @param isAcceptable Same as for udata_openChoice(). May be NULL. + * + * @internal ICU 2.8 + */ +U_CAPI UDataMemory * U_EXPORT2 +udata_openSwap(const char *path, const char *type, const char *name, + UDataMemoryIsAcceptable *isAcceptable, void *isAcceptableContext, + UDataSwapFn *swap, + UDataPrintError *printError, void *printErrorContext, + UErrorCode *pErrorCode); + +#endif + +#endif diff --git a/deps/icu-small/source/common/uelement.h b/deps/icu-small/source/common/uelement.h index 2c5a2043e17d5b..630114904a6371 100644 --- a/deps/icu-small/source/common/uelement.h +++ b/deps/icu-small/source/common/uelement.h @@ -1,94 +1,94 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: uelement.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2011jul04 -* created by: Markus W. Scherer -* -* Common definitions for UHashTable and UVector. -* UHashTok moved here from uhash.h and renamed UElement. -* This allows users of UVector to avoid the confusing #include of uhash.h. -* uhash.h aliases UElement to UHashTok, -* so that we need not change all of its code and its users. -*/ - -#ifndef __UELEMENT_H__ -#define __UELEMENT_H__ - -#include "unicode/utypes.h" - -U_CDECL_BEGIN - -/** - * A UVector element, or a key or value within a UHashtable. - * It may be either a 32-bit integral value or an opaque void* pointer. - * The void* pointer may be smaller than 32 bits (e.g. 24 bits) - * or may be larger (e.g. 64 bits). - * - * Because a UElement is the size of a native pointer or a 32-bit - * integer, we pass it around by value. - */ -union UElement { - void* pointer; - int32_t integer; -}; -typedef union UElement UElement; - -/** - * An element-equality (boolean) comparison function. - * @param e1 An element (object or integer) - * @param e2 An element (object or integer) - * @return true if the two elements are equal. - */ -typedef UBool U_CALLCONV UElementsAreEqual(const UElement e1, const UElement e2); - -/** - * An element sorting (three-way) comparison function. - * @param e1 An element (object or integer) - * @param e2 An element (object or integer) - * @return 32-bit signed integer comparison result: - * ==0 if the two elements are equal, - * <0 if e1 is < e2, or - * >0 if e1 is > e2. - */ -typedef int32_t U_CALLCONV UElementComparator(UElement e1, UElement e2); - -/** - * An element assignment function. It may copy an integer, copy - * a pointer, or clone a pointer, as appropriate. - * @param dst The element to be assigned to - * @param src The element to assign from - */ -typedef void U_CALLCONV UElementAssigner(UElement *dst, UElement *src); - -U_CDECL_END - -/** - * Comparator function for UnicodeString* keys. Implements UElementsAreEqual. - * @param key1 The string for comparison - * @param key2 The string for comparison - * @return true if key1 and key2 are equal, return false otherwise. - */ -U_CAPI UBool U_EXPORT2 -uhash_compareUnicodeString(const UElement key1, const UElement key2); - -/** - * Comparator function for UnicodeString* keys (case insensitive). - * Make sure to use together with uhash_hashCaselessUnicodeString. - * Implements UElementsAreEqual. - * @param key1 The string for comparison - * @param key2 The string for comparison - * @return true if key1 and key2 are equal, return false otherwise. - */ -U_CAPI UBool U_EXPORT2 -uhash_compareCaselessUnicodeString(const UElement key1, const UElement key2); - -#endif /* __UELEMENT_H__ */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1997-2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: uelement.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2011jul04 +* created by: Markus W. Scherer +* +* Common definitions for UHashTable and UVector. +* UHashTok moved here from uhash.h and renamed UElement. +* This allows users of UVector to avoid the confusing #include of uhash.h. +* uhash.h aliases UElement to UHashTok, +* so that we need not change all of its code and its users. +*/ + +#ifndef __UELEMENT_H__ +#define __UELEMENT_H__ + +#include "unicode/utypes.h" + +U_CDECL_BEGIN + +/** + * A UVector element, or a key or value within a UHashtable. + * It may be either a 32-bit integral value or an opaque void* pointer. + * The void* pointer may be smaller than 32 bits (e.g. 24 bits) + * or may be larger (e.g. 64 bits). + * + * Because a UElement is the size of a native pointer or a 32-bit + * integer, we pass it around by value. + */ +union UElement { + void* pointer; + int32_t integer; +}; +typedef union UElement UElement; + +/** + * An element-equality (boolean) comparison function. + * @param e1 An element (object or integer) + * @param e2 An element (object or integer) + * @return true if the two elements are equal. + */ +typedef UBool U_CALLCONV UElementsAreEqual(const UElement e1, const UElement e2); + +/** + * An element sorting (three-way) comparison function. + * @param e1 An element (object or integer) + * @param e2 An element (object or integer) + * @return 32-bit signed integer comparison result: + * ==0 if the two elements are equal, + * <0 if e1 is < e2, or + * >0 if e1 is > e2. + */ +typedef int32_t U_CALLCONV UElementComparator(UElement e1, UElement e2); + +/** + * An element assignment function. It may copy an integer, copy + * a pointer, or clone a pointer, as appropriate. + * @param dst The element to be assigned to + * @param src The element to assign from + */ +typedef void U_CALLCONV UElementAssigner(UElement *dst, UElement *src); + +U_CDECL_END + +/** + * Comparator function for UnicodeString* keys. Implements UElementsAreEqual. + * @param key1 The string for comparison + * @param key2 The string for comparison + * @return true if key1 and key2 are equal, return false otherwise. + */ +U_CAPI UBool U_EXPORT2 +uhash_compareUnicodeString(const UElement key1, const UElement key2); + +/** + * Comparator function for UnicodeString* keys (case insensitive). + * Make sure to use together with uhash_hashCaselessUnicodeString. + * Implements UElementsAreEqual. + * @param key1 The string for comparison + * @param key2 The string for comparison + * @return true if key1 and key2 are equal, return false otherwise. + */ +U_CAPI UBool U_EXPORT2 +uhash_compareCaselessUnicodeString(const UElement key1, const UElement key2); + +#endif /* __UELEMENT_H__ */ diff --git a/deps/icu-small/source/common/uenum.cpp b/deps/icu-small/source/common/uenum.cpp index 11d895ebcd7463..434f258662ebd9 100644 --- a/deps/icu-small/source/common/uenum.cpp +++ b/deps/icu-small/source/common/uenum.cpp @@ -1,189 +1,189 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uenum.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:2 -* -* created on: 2002jul08 -* created by: Vladimir Weinstein -*/ - -#include "unicode/putil.h" -#include "uenumimp.h" -#include "cmemory.h" - -/* Layout of the baseContext buffer. */ -typedef struct { - int32_t len; /* number of bytes available starting at 'data' */ - char data; /* actual data starts here */ -} _UEnumBuffer; - -/* Extra bytes to allocate in the baseContext buffer. */ -static const int32_t PAD = 8; - -/* Return a pointer to the baseContext buffer, possibly allocating - or reallocating it if at least 'capacity' bytes are not available. */ -static void* _getBuffer(UEnumeration* en, int32_t capacity) { - - if (en->baseContext != NULL) { - if (((_UEnumBuffer*) en->baseContext)->len < capacity) { - capacity += PAD; - en->baseContext = uprv_realloc(en->baseContext, - sizeof(int32_t) + capacity); - if (en->baseContext == NULL) { - return NULL; - } - ((_UEnumBuffer*) en->baseContext)->len = capacity; - } - } else { - capacity += PAD; - en->baseContext = uprv_malloc(sizeof(int32_t) + capacity); - if (en->baseContext == NULL) { - return NULL; - } - ((_UEnumBuffer*) en->baseContext)->len = capacity; - } - - return (void*) & ((_UEnumBuffer*) en->baseContext)->data; -} - -U_CAPI void U_EXPORT2 -uenum_close(UEnumeration* en) -{ - if (en) { - if (en->close != NULL) { - if (en->baseContext) { - uprv_free(en->baseContext); - } - en->close(en); - } else { /* this seems dangerous, but we better kill the object */ - uprv_free(en); - } - } -} - -U_CAPI int32_t U_EXPORT2 -uenum_count(UEnumeration* en, UErrorCode* status) -{ - if (!en || U_FAILURE(*status)) { - return -1; - } - if (en->count != NULL) { - return en->count(en, status); - } else { - *status = U_UNSUPPORTED_ERROR; - return -1; - } -} - -/* Don't call this directly. Only uenum_unext should be calling this. */ -U_CAPI const UChar* U_EXPORT2 -uenum_unextDefault(UEnumeration* en, - int32_t* resultLength, - UErrorCode* status) -{ - UChar *ustr = NULL; - int32_t len = 0; - if (en->next != NULL) { - const char *cstr = en->next(en, &len, status); - if (cstr != NULL) { - ustr = (UChar*) _getBuffer(en, (len+1) * sizeof(UChar)); - if (ustr == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - } else { - u_charsToUChars(cstr, ustr, len+1); - } - } - } else { - *status = U_UNSUPPORTED_ERROR; - } - if (resultLength) { - *resultLength = len; - } - return ustr; -} - -/* Don't call this directly. Only uenum_next should be calling this. */ -U_CAPI const char* U_EXPORT2 -uenum_nextDefault(UEnumeration* en, - int32_t* resultLength, - UErrorCode* status) -{ - if (en->uNext != NULL) { - char *tempCharVal; - const UChar *tempUCharVal = en->uNext(en, resultLength, status); - if (tempUCharVal == NULL) { - return NULL; - } - tempCharVal = (char*) - _getBuffer(en, (*resultLength+1) * sizeof(char)); - if (!tempCharVal) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - u_UCharsToChars(tempUCharVal, tempCharVal, *resultLength + 1); - return tempCharVal; - } else { - *status = U_UNSUPPORTED_ERROR; - return NULL; - } -} - -U_CAPI const UChar* U_EXPORT2 -uenum_unext(UEnumeration* en, - int32_t* resultLength, - UErrorCode* status) -{ - if (!en || U_FAILURE(*status)) { - return NULL; - } - if (en->uNext != NULL) { - return en->uNext(en, resultLength, status); - } else { - *status = U_UNSUPPORTED_ERROR; - return NULL; - } -} - -U_CAPI const char* U_EXPORT2 -uenum_next(UEnumeration* en, - int32_t* resultLength, - UErrorCode* status) -{ - if (!en || U_FAILURE(*status)) { - return NULL; - } - if (en->next != NULL) { - if (resultLength != NULL) { - return en->next(en, resultLength, status); - } - else { - int32_t dummyLength=0; - return en->next(en, &dummyLength, status); - } - } else { - *status = U_UNSUPPORTED_ERROR; - return NULL; - } -} - -U_CAPI void U_EXPORT2 -uenum_reset(UEnumeration* en, UErrorCode* status) -{ - if (!en || U_FAILURE(*status)) { - return; - } - if (en->reset != NULL) { - en->reset(en, status); - } else { - *status = U_UNSUPPORTED_ERROR; - } -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uenum.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:2 +* +* created on: 2002jul08 +* created by: Vladimir Weinstein +*/ + +#include "unicode/putil.h" +#include "uenumimp.h" +#include "cmemory.h" + +/* Layout of the baseContext buffer. */ +typedef struct { + int32_t len; /* number of bytes available starting at 'data' */ + char data; /* actual data starts here */ +} _UEnumBuffer; + +/* Extra bytes to allocate in the baseContext buffer. */ +static const int32_t PAD = 8; + +/* Return a pointer to the baseContext buffer, possibly allocating + or reallocating it if at least 'capacity' bytes are not available. */ +static void* _getBuffer(UEnumeration* en, int32_t capacity) { + + if (en->baseContext != nullptr) { + if (((_UEnumBuffer*) en->baseContext)->len < capacity) { + capacity += PAD; + en->baseContext = uprv_realloc(en->baseContext, + sizeof(int32_t) + capacity); + if (en->baseContext == nullptr) { + return nullptr; + } + ((_UEnumBuffer*) en->baseContext)->len = capacity; + } + } else { + capacity += PAD; + en->baseContext = uprv_malloc(sizeof(int32_t) + capacity); + if (en->baseContext == nullptr) { + return nullptr; + } + ((_UEnumBuffer*) en->baseContext)->len = capacity; + } + + return (void*) & ((_UEnumBuffer*) en->baseContext)->data; +} + +U_CAPI void U_EXPORT2 +uenum_close(UEnumeration* en) +{ + if (en) { + if (en->close != nullptr) { + if (en->baseContext) { + uprv_free(en->baseContext); + } + en->close(en); + } else { /* this seems dangerous, but we better kill the object */ + uprv_free(en); + } + } +} + +U_CAPI int32_t U_EXPORT2 +uenum_count(UEnumeration* en, UErrorCode* status) +{ + if (!en || U_FAILURE(*status)) { + return -1; + } + if (en->count != nullptr) { + return en->count(en, status); + } else { + *status = U_UNSUPPORTED_ERROR; + return -1; + } +} + +/* Don't call this directly. Only uenum_unext should be calling this. */ +U_CAPI const char16_t* U_EXPORT2 +uenum_unextDefault(UEnumeration* en, + int32_t* resultLength, + UErrorCode* status) +{ + char16_t *ustr = nullptr; + int32_t len = 0; + if (en->next != nullptr) { + const char *cstr = en->next(en, &len, status); + if (cstr != nullptr) { + ustr = (char16_t*) _getBuffer(en, (len+1) * sizeof(char16_t)); + if (ustr == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + } else { + u_charsToUChars(cstr, ustr, len+1); + } + } + } else { + *status = U_UNSUPPORTED_ERROR; + } + if (resultLength) { + *resultLength = len; + } + return ustr; +} + +/* Don't call this directly. Only uenum_next should be calling this. */ +U_CAPI const char* U_EXPORT2 +uenum_nextDefault(UEnumeration* en, + int32_t* resultLength, + UErrorCode* status) +{ + if (en->uNext != nullptr) { + char *tempCharVal; + const char16_t *tempUCharVal = en->uNext(en, resultLength, status); + if (tempUCharVal == nullptr) { + return nullptr; + } + tempCharVal = (char*) + _getBuffer(en, (*resultLength+1) * sizeof(char)); + if (!tempCharVal) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + u_UCharsToChars(tempUCharVal, tempCharVal, *resultLength + 1); + return tempCharVal; + } else { + *status = U_UNSUPPORTED_ERROR; + return nullptr; + } +} + +U_CAPI const char16_t* U_EXPORT2 +uenum_unext(UEnumeration* en, + int32_t* resultLength, + UErrorCode* status) +{ + if (!en || U_FAILURE(*status)) { + return nullptr; + } + if (en->uNext != nullptr) { + return en->uNext(en, resultLength, status); + } else { + *status = U_UNSUPPORTED_ERROR; + return nullptr; + } +} + +U_CAPI const char* U_EXPORT2 +uenum_next(UEnumeration* en, + int32_t* resultLength, + UErrorCode* status) +{ + if (!en || U_FAILURE(*status)) { + return nullptr; + } + if (en->next != nullptr) { + if (resultLength != nullptr) { + return en->next(en, resultLength, status); + } + else { + int32_t dummyLength=0; + return en->next(en, &dummyLength, status); + } + } else { + *status = U_UNSUPPORTED_ERROR; + return nullptr; + } +} + +U_CAPI void U_EXPORT2 +uenum_reset(UEnumeration* en, UErrorCode* status) +{ + if (!en || U_FAILURE(*status)) { + return; + } + if (en->reset != nullptr) { + en->reset(en, status); + } else { + *status = U_UNSUPPORTED_ERROR; + } +} diff --git a/deps/icu-small/source/common/uenumimp.h b/deps/icu-small/source/common/uenumimp.h index 9c9df75ae0497a..67e34cbc60b702 100644 --- a/deps/icu-small/source/common/uenumimp.h +++ b/deps/icu-small/source/common/uenumimp.h @@ -1,155 +1,155 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2006, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uenumimp.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:2 -* -* created on: 2002jul08 -* created by: Vladimir Weinstein -*/ - -#ifndef __UENUMIMP_H -#define __UENUMIMP_H - -#include "unicode/uenum.h" - -U_CDECL_BEGIN - -/** - * following are the type declarations for - * implementations of APIs. If any of these - * functions are NULL, U_UNSUPPORTED_ERROR - * is returned. If close is NULL, the enumeration - * object is going to be released. - * Initial error checking is done in the body - * of API function, so the implementations - * need not to check the initial error condition. - */ - -/** - * Function type declaration for uenum_close(). - * - * This function should cleanup the enumerator object - * - * @param en enumeration to be closed - */ -typedef void U_CALLCONV -UEnumClose(UEnumeration *en); - -/** - * Function type declaration for uenum_count(). - * - * This function should count the number of elements - * in this enumeration - * - * @param en enumeration to be counted - * @param status pointer to UErrorCode variable - * @return number of elements in enumeration - */ -typedef int32_t U_CALLCONV -UEnumCount(UEnumeration *en, UErrorCode *status); - -/** - * Function type declaration for uenum_unext(). - * - * This function returns the next element as a UChar *, - * or NULL after all elements haven been enumerated. - * - * @param en enumeration - * @param resultLength pointer to result length - * @param status pointer to UErrorCode variable - * @return next element as UChar *, - * or NULL after all elements haven been enumerated - */ -typedef const UChar* U_CALLCONV -UEnumUNext(UEnumeration* en, - int32_t* resultLength, - UErrorCode* status); - -/** - * Function type declaration for uenum_next(). - * - * This function returns the next element as a char *, - * or NULL after all elements haven been enumerated. - * - * @param en enumeration - * @param resultLength pointer to result length - * @param status pointer to UErrorCode variable - * @return next element as char *, - * or NULL after all elements haven been enumerated - */ -typedef const char* U_CALLCONV -UEnumNext(UEnumeration* en, - int32_t* resultLength, - UErrorCode* status); - -/** - * Function type declaration for uenum_reset(). - * - * This function should reset the enumeration - * object - * - * @param en enumeration - * @param status pointer to UErrorCode variable - */ -typedef void U_CALLCONV -UEnumReset(UEnumeration* en, - UErrorCode* status); - - -struct UEnumeration { - /* baseContext. For the base class only. Don't touch! */ - void *baseContext; - - /* context. Use it for what you need */ - void *context; - - /** - * these are functions that will - * be used for APIs - */ - /* called from uenum_close */ - UEnumClose *close; - /* called from uenum_count */ - UEnumCount *count; - /* called from uenum_unext */ - UEnumUNext *uNext; - /* called from uenum_next */ - UEnumNext *next; - /* called from uenum_reset */ - UEnumReset *reset; -}; - -U_CDECL_END - -/* This is the default implementation for uenum_unext(). - * It automatically converts the char * string to UChar *. - * Don't call this directly. This is called internally by uenum_unext - * when a UEnumeration is defined with 'uNext' pointing to this - * function. - */ -U_CAPI const UChar* U_EXPORT2 -uenum_unextDefault(UEnumeration* en, - int32_t* resultLength, - UErrorCode* status); - -/* This is the default implementation for uenum_next(). - * It automatically converts the UChar * string to char *. - * Don't call this directly. This is called internally by uenum_next - * when a UEnumeration is defined with 'next' pointing to this - * function. - */ -U_CAPI const char* U_EXPORT2 -uenum_nextDefault(UEnumeration* en, - int32_t* resultLength, - UErrorCode* status); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2006, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uenumimp.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:2 +* +* created on: 2002jul08 +* created by: Vladimir Weinstein +*/ + +#ifndef __UENUMIMP_H +#define __UENUMIMP_H + +#include "unicode/uenum.h" + +U_CDECL_BEGIN + +/** + * following are the type declarations for + * implementations of APIs. If any of these + * functions are NULL, U_UNSUPPORTED_ERROR + * is returned. If close is NULL, the enumeration + * object is going to be released. + * Initial error checking is done in the body + * of API function, so the implementations + * need not to check the initial error condition. + */ + +/** + * Function type declaration for uenum_close(). + * + * This function should cleanup the enumerator object + * + * @param en enumeration to be closed + */ +typedef void U_CALLCONV +UEnumClose(UEnumeration *en); + +/** + * Function type declaration for uenum_count(). + * + * This function should count the number of elements + * in this enumeration + * + * @param en enumeration to be counted + * @param status pointer to UErrorCode variable + * @return number of elements in enumeration + */ +typedef int32_t U_CALLCONV +UEnumCount(UEnumeration *en, UErrorCode *status); + +/** + * Function type declaration for uenum_unext(). + * + * This function returns the next element as a UChar *, + * or NULL after all elements haven been enumerated. + * + * @param en enumeration + * @param resultLength pointer to result length + * @param status pointer to UErrorCode variable + * @return next element as UChar *, + * or NULL after all elements haven been enumerated + */ +typedef const UChar* U_CALLCONV +UEnumUNext(UEnumeration* en, + int32_t* resultLength, + UErrorCode* status); + +/** + * Function type declaration for uenum_next(). + * + * This function returns the next element as a char *, + * or NULL after all elements haven been enumerated. + * + * @param en enumeration + * @param resultLength pointer to result length + * @param status pointer to UErrorCode variable + * @return next element as char *, + * or NULL after all elements haven been enumerated + */ +typedef const char* U_CALLCONV +UEnumNext(UEnumeration* en, + int32_t* resultLength, + UErrorCode* status); + +/** + * Function type declaration for uenum_reset(). + * + * This function should reset the enumeration + * object + * + * @param en enumeration + * @param status pointer to UErrorCode variable + */ +typedef void U_CALLCONV +UEnumReset(UEnumeration* en, + UErrorCode* status); + + +struct UEnumeration { + /* baseContext. For the base class only. Don't touch! */ + void *baseContext; + + /* context. Use it for what you need */ + void *context; + + /** + * these are functions that will + * be used for APIs + */ + /* called from uenum_close */ + UEnumClose *close; + /* called from uenum_count */ + UEnumCount *count; + /* called from uenum_unext */ + UEnumUNext *uNext; + /* called from uenum_next */ + UEnumNext *next; + /* called from uenum_reset */ + UEnumReset *reset; +}; + +U_CDECL_END + +/* This is the default implementation for uenum_unext(). + * It automatically converts the char * string to UChar *. + * Don't call this directly. This is called internally by uenum_unext + * when a UEnumeration is defined with 'uNext' pointing to this + * function. + */ +U_CAPI const UChar* U_EXPORT2 +uenum_unextDefault(UEnumeration* en, + int32_t* resultLength, + UErrorCode* status); + +/* This is the default implementation for uenum_next(). + * It automatically converts the UChar * string to char *. + * Don't call this directly. This is called internally by uenum_next + * when a UEnumeration is defined with 'next' pointing to this + * function. + */ +U_CAPI const char* U_EXPORT2 +uenum_nextDefault(UEnumeration* en, + int32_t* resultLength, + UErrorCode* status); + +#endif diff --git a/deps/icu-small/source/common/uhash.cpp b/deps/icu-small/source/common/uhash.cpp index a04f9606c572ce..7ffbb705a405d2 100644 --- a/deps/icu-small/source/common/uhash.cpp +++ b/deps/icu-small/source/common/uhash.cpp @@ -1,1066 +1,1066 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 1997-2016, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* Date Name Description -* 03/22/00 aliu Adapted from original C++ ICU Hashtable. -* 07/06/01 aliu Modified to support int32_t keys on -* platforms with sizeof(void*) < 32. -****************************************************************************** -*/ - -#include "uhash.h" -#include "unicode/ustring.h" -#include "cstring.h" -#include "cmemory.h" -#include "uassert.h" -#include "ustr_imp.h" - -/* This hashtable is implemented as a double hash. All elements are - * stored in a single array with no secondary storage for collision - * resolution (no linked list, etc.). When there is a hash collision - * (when two unequal keys have the same hashcode) we resolve this by - * using a secondary hash. The secondary hash is an increment - * computed as a hash function (a different one) of the primary - * hashcode. This increment is added to the initial hash value to - * obtain further slots assigned to the same hash code. For this to - * work, the length of the array and the increment must be relatively - * prime. The easiest way to achieve this is to have the length of - * the array be prime, and the increment be any value from - * 1..length-1. - * - * Hashcodes are 32-bit integers. We make sure all hashcodes are - * non-negative by masking off the top bit. This has two effects: (1) - * modulo arithmetic is simplified. If we allowed negative hashcodes, - * then when we computed hashcode % length, we could get a negative - * result, which we would then have to adjust back into range. It's - * simpler to just make hashcodes non-negative. (2) It makes it easy - * to check for empty vs. occupied slots in the table. We just mark - * empty or deleted slots with a negative hashcode. - * - * The central function is _uhash_find(). This function looks for a - * slot matching the given key and hashcode. If one is found, it - * returns a pointer to that slot. If the table is full, and no match - * is found, it returns NULL -- in theory. This would make the code - * more complicated, since all callers of _uhash_find() would then - * have to check for a NULL result. To keep this from happening, we - * don't allow the table to fill. When there is only one - * empty/deleted slot left, uhash_put() will refuse to increase the - * count, and fail. This simplifies the code. In practice, one will - * seldom encounter this using default UHashtables. However, if a - * hashtable is set to a U_FIXED resize policy, or if memory is - * exhausted, then the table may fill. - * - * High and low water ratios control rehashing. They establish levels - * of fullness (from 0 to 1) outside of which the data array is - * reallocated and repopulated. Setting the low water ratio to zero - * means the table will never shrink. Setting the high water ratio to - * one means the table will never grow. The ratios should be - * coordinated with the ratio between successive elements of the - * PRIMES table, so that when the primeIndex is incremented or - * decremented during rehashing, it brings the ratio of count / length - * back into the desired range (between low and high water ratios). - */ - -/******************************************************************** - * PRIVATE Constants, Macros - ********************************************************************/ - -/* This is a list of non-consecutive primes chosen such that - * PRIMES[i+1] ~ 2*PRIMES[i]. (Currently, the ratio ranges from 1.81 - * to 2.18; the inverse ratio ranges from 0.459 to 0.552.) If this - * ratio is changed, the low and high water ratios should also be - * adjusted to suit. - * - * These prime numbers were also chosen so that they are the largest - * prime number while being less than a power of two. - */ -static const int32_t PRIMES[] = { - 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, - 65521, 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, - 16777213, 33554393, 67108859, 134217689, 268435399, 536870909, - 1073741789, 2147483647 /*, 4294967291 */ -}; - -#define PRIMES_LENGTH UPRV_LENGTHOF(PRIMES) -#define DEFAULT_PRIME_INDEX 4 - -/* These ratios are tuned to the PRIMES array such that a resize - * places the table back into the zone of non-resizing. That is, - * after a call to _uhash_rehash(), a subsequent call to - * _uhash_rehash() should do nothing (should not churn). This is only - * a potential problem with U_GROW_AND_SHRINK. - */ -static const float RESIZE_POLICY_RATIO_TABLE[6] = { - /* low, high water ratio */ - 0.0F, 0.5F, /* U_GROW: Grow on demand, do not shrink */ - 0.1F, 0.5F, /* U_GROW_AND_SHRINK: Grow and shrink on demand */ - 0.0F, 1.0F /* U_FIXED: Never change size */ -}; - -/* - Invariants for hashcode values: - - * DELETED < 0 - * EMPTY < 0 - * Real hashes >= 0 - - Hashcodes may not start out this way, but internally they are - adjusted so that they are always positive. We assume 32-bit - hashcodes; adjust these constants for other hashcode sizes. -*/ -#define HASH_DELETED ((int32_t) 0x80000000) -#define HASH_EMPTY ((int32_t) HASH_DELETED + 1) - -#define IS_EMPTY_OR_DELETED(x) ((x) < 0) - -/* This macro expects a UHashTok.pointer as its keypointer and - valuepointer parameters */ -#define HASH_DELETE_KEY_VALUE(hash, keypointer, valuepointer) UPRV_BLOCK_MACRO_BEGIN { \ - if (hash->keyDeleter != NULL && keypointer != NULL) { \ - (*hash->keyDeleter)(keypointer); \ - } \ - if (hash->valueDeleter != NULL && valuepointer != NULL) { \ - (*hash->valueDeleter)(valuepointer); \ - } \ -} UPRV_BLOCK_MACRO_END - -/* - * Constants for hinting whether a key or value is an integer - * or a pointer. If a hint bit is zero, then the associated - * token is assumed to be an integer. - */ -#define HINT_BOTH_INTEGERS (0) -#define HINT_KEY_POINTER (1) -#define HINT_VALUE_POINTER (2) -#define HINT_ALLOW_ZERO (4) - -/******************************************************************** - * PRIVATE Implementation - ********************************************************************/ - -static UHashTok -_uhash_setElement(UHashtable *hash, UHashElement* e, - int32_t hashcode, - UHashTok key, UHashTok value, int8_t hint) { - - UHashTok oldValue = e->value; - if (hash->keyDeleter != NULL && e->key.pointer != NULL && - e->key.pointer != key.pointer) { /* Avoid double deletion */ - (*hash->keyDeleter)(e->key.pointer); - } - if (hash->valueDeleter != NULL) { - if (oldValue.pointer != NULL && - oldValue.pointer != value.pointer) { /* Avoid double deletion */ - (*hash->valueDeleter)(oldValue.pointer); - } - oldValue.pointer = NULL; - } - /* Compilers should copy the UHashTok union correctly, but even if - * they do, memory heap tools (e.g. BoundsChecker) can get - * confused when a pointer is cloaked in a union and then copied. - * TO ALLEVIATE THIS, we use hints (based on what API the user is - * calling) to copy pointers when we know the user thinks - * something is a pointer. */ - if (hint & HINT_KEY_POINTER) { - e->key.pointer = key.pointer; - } else { - e->key = key; - } - if (hint & HINT_VALUE_POINTER) { - e->value.pointer = value.pointer; - } else { - e->value = value; - } - e->hashcode = hashcode; - return oldValue; -} - -/** - * Assumes that the given element is not empty or deleted. - */ -static UHashTok -_uhash_internalRemoveElement(UHashtable *hash, UHashElement* e) { - UHashTok empty; - U_ASSERT(!IS_EMPTY_OR_DELETED(e->hashcode)); - --hash->count; - empty.pointer = NULL; empty.integer = 0; - return _uhash_setElement(hash, e, HASH_DELETED, empty, empty, 0); -} - -static void -_uhash_internalSetResizePolicy(UHashtable *hash, enum UHashResizePolicy policy) { - U_ASSERT(hash != NULL); - U_ASSERT(((int32_t)policy) >= 0); - U_ASSERT(((int32_t)policy) < 3); - hash->lowWaterRatio = RESIZE_POLICY_RATIO_TABLE[policy * 2]; - hash->highWaterRatio = RESIZE_POLICY_RATIO_TABLE[policy * 2 + 1]; -} - -/** - * Allocate internal data array of a size determined by the given - * prime index. If the index is out of range it is pinned into range. - * If the allocation fails the status is set to - * U_MEMORY_ALLOCATION_ERROR and all array storage is freed. In - * either case the previous array pointer is overwritten. - * - * Caller must ensure primeIndex is in range 0..PRIME_LENGTH-1. - */ -static void -_uhash_allocate(UHashtable *hash, - int32_t primeIndex, - UErrorCode *status) { - - UHashElement *p, *limit; - UHashTok emptytok; - - if (U_FAILURE(*status)) return; - - U_ASSERT(primeIndex >= 0 && primeIndex < PRIMES_LENGTH); - - hash->primeIndex = static_cast(primeIndex); - hash->length = PRIMES[primeIndex]; - - p = hash->elements = (UHashElement*) - uprv_malloc(sizeof(UHashElement) * hash->length); - - if (hash->elements == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - emptytok.pointer = NULL; /* Only one of these two is needed */ - emptytok.integer = 0; /* but we don't know which one. */ - - limit = p + hash->length; - while (p < limit) { - p->key = emptytok; - p->value = emptytok; - p->hashcode = HASH_EMPTY; - ++p; - } - - hash->count = 0; - hash->lowWaterMark = (int32_t)(hash->length * hash->lowWaterRatio); - hash->highWaterMark = (int32_t)(hash->length * hash->highWaterRatio); -} - -static UHashtable* -_uhash_init(UHashtable *result, - UHashFunction *keyHash, - UKeyComparator *keyComp, - UValueComparator *valueComp, - int32_t primeIndex, - UErrorCode *status) -{ - if (U_FAILURE(*status)) return NULL; - U_ASSERT(keyHash != NULL); - U_ASSERT(keyComp != NULL); - - result->keyHasher = keyHash; - result->keyComparator = keyComp; - result->valueComparator = valueComp; - result->keyDeleter = NULL; - result->valueDeleter = NULL; - result->allocated = false; - _uhash_internalSetResizePolicy(result, U_GROW); - - _uhash_allocate(result, primeIndex, status); - - if (U_FAILURE(*status)) { - return NULL; - } - - return result; -} - -static UHashtable* -_uhash_create(UHashFunction *keyHash, - UKeyComparator *keyComp, - UValueComparator *valueComp, - int32_t primeIndex, - UErrorCode *status) { - UHashtable *result; - - if (U_FAILURE(*status)) return NULL; - - result = (UHashtable*) uprv_malloc(sizeof(UHashtable)); - if (result == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - _uhash_init(result, keyHash, keyComp, valueComp, primeIndex, status); - result->allocated = true; - - if (U_FAILURE(*status)) { - uprv_free(result); - return NULL; - } - - return result; -} - -/** - * Look for a key in the table, or if no such key exists, the first - * empty slot matching the given hashcode. Keys are compared using - * the keyComparator function. - * - * First find the start position, which is the hashcode modulo - * the length. Test it to see if it is: - * - * a. identical: First check the hash values for a quick check, - * then compare keys for equality using keyComparator. - * b. deleted - * c. empty - * - * Stop if it is identical or empty, otherwise continue by adding a - * "jump" value (moduloing by the length again to keep it within - * range) and retesting. For efficiency, there need enough empty - * values so that the searches stop within a reasonable amount of time. - * This can be changed by changing the high/low water marks. - * - * In theory, this function can return NULL, if it is full (no empty - * or deleted slots) and if no matching key is found. In practice, we - * prevent this elsewhere (in uhash_put) by making sure the last slot - * in the table is never filled. - * - * The size of the table should be prime for this algorithm to work; - * otherwise we are not guaranteed that the jump value (the secondary - * hash) is relatively prime to the table length. - */ -static UHashElement* -_uhash_find(const UHashtable *hash, UHashTok key, - int32_t hashcode) { - - int32_t firstDeleted = -1; /* assume invalid index */ - int32_t theIndex, startIndex; - int32_t jump = 0; /* lazy evaluate */ - int32_t tableHash; - UHashElement *elements = hash->elements; - - hashcode &= 0x7FFFFFFF; /* must be positive */ - startIndex = theIndex = (hashcode ^ 0x4000000) % hash->length; - - do { - tableHash = elements[theIndex].hashcode; - if (tableHash == hashcode) { /* quick check */ - if ((*hash->keyComparator)(key, elements[theIndex].key)) { - return &(elements[theIndex]); - } - } else if (!IS_EMPTY_OR_DELETED(tableHash)) { - /* We have hit a slot which contains a key-value pair, - * but for which the hash code does not match. Keep - * looking. - */ - } else if (tableHash == HASH_EMPTY) { /* empty, end o' the line */ - break; - } else if (firstDeleted < 0) { /* remember first deleted */ - firstDeleted = theIndex; - } - if (jump == 0) { /* lazy compute jump */ - /* The jump value must be relatively prime to the table - * length. As long as the length is prime, then any value - * 1..length-1 will be relatively prime to it. - */ - jump = (hashcode % (hash->length - 1)) + 1; - } - theIndex = (theIndex + jump) % hash->length; - } while (theIndex != startIndex); - - if (firstDeleted >= 0) { - theIndex = firstDeleted; /* reset if had deleted slot */ - } else if (tableHash != HASH_EMPTY) { - /* We get to this point if the hashtable is full (no empty or - * deleted slots), and we've failed to find a match. THIS - * WILL NEVER HAPPEN as long as uhash_put() makes sure that - * count is always < length. - */ - UPRV_UNREACHABLE_EXIT; - } - return &(elements[theIndex]); -} - -/** - * Attempt to grow or shrink the data arrays in order to make the - * count fit between the high and low water marks. hash_put() and - * hash_remove() call this method when the count exceeds the high or - * low water marks. This method may do nothing, if memory allocation - * fails, or if the count is already in range, or if the length is - * already at the low or high limit. In any case, upon return the - * arrays will be valid. - */ -static void -_uhash_rehash(UHashtable *hash, UErrorCode *status) { - - UHashElement *old = hash->elements; - int32_t oldLength = hash->length; - int32_t newPrimeIndex = hash->primeIndex; - int32_t i; - - if (hash->count > hash->highWaterMark) { - if (++newPrimeIndex >= PRIMES_LENGTH) { - return; - } - } else if (hash->count < hash->lowWaterMark) { - if (--newPrimeIndex < 0) { - return; - } - } else { - return; - } - - _uhash_allocate(hash, newPrimeIndex, status); - - if (U_FAILURE(*status)) { - hash->elements = old; - hash->length = oldLength; - return; - } - - for (i = oldLength - 1; i >= 0; --i) { - if (!IS_EMPTY_OR_DELETED(old[i].hashcode)) { - UHashElement *e = _uhash_find(hash, old[i].key, old[i].hashcode); - U_ASSERT(e != NULL); - U_ASSERT(e->hashcode == HASH_EMPTY); - e->key = old[i].key; - e->value = old[i].value; - e->hashcode = old[i].hashcode; - ++hash->count; - } - } - - uprv_free(old); -} - -static UHashTok -_uhash_remove(UHashtable *hash, - UHashTok key) { - /* First find the position of the key in the table. If the object - * has not been removed already, remove it. If the user wanted - * keys deleted, then delete it also. We have to put a special - * hashcode in that position that means that something has been - * deleted, since when we do a find, we have to continue PAST any - * deleted values. - */ - UHashTok result; - UHashElement* e = _uhash_find(hash, key, hash->keyHasher(key)); - U_ASSERT(e != NULL); - result.pointer = NULL; - result.integer = 0; - if (!IS_EMPTY_OR_DELETED(e->hashcode)) { - result = _uhash_internalRemoveElement(hash, e); - if (hash->count < hash->lowWaterMark) { - UErrorCode status = U_ZERO_ERROR; - _uhash_rehash(hash, &status); - } - } - return result; -} - -static UHashTok -_uhash_put(UHashtable *hash, - UHashTok key, - UHashTok value, - int8_t hint, - UErrorCode *status) { - - /* Put finds the position in the table for the new value. If the - * key is already in the table, it is deleted, if there is a - * non-NULL keyDeleter. Then the key, the hash and the value are - * all put at the position in their respective arrays. - */ - int32_t hashcode; - UHashElement* e; - UHashTok emptytok; - - if (U_FAILURE(*status)) { - goto err; - } - U_ASSERT(hash != NULL); - if ((hint & HINT_VALUE_POINTER) ? - value.pointer == NULL : - value.integer == 0 && (hint & HINT_ALLOW_ZERO) == 0) { - /* Disallow storage of NULL values, since NULL is returned by - * get() to indicate an absent key. Storing NULL == removing. - */ - return _uhash_remove(hash, key); - } - if (hash->count > hash->highWaterMark) { - _uhash_rehash(hash, status); - if (U_FAILURE(*status)) { - goto err; - } - } - - hashcode = (*hash->keyHasher)(key); - e = _uhash_find(hash, key, hashcode); - U_ASSERT(e != NULL); - - if (IS_EMPTY_OR_DELETED(e->hashcode)) { - /* Important: We must never actually fill the table up. If we - * do so, then _uhash_find() will return NULL, and we'll have - * to check for NULL after every call to _uhash_find(). To - * avoid this we make sure there is always at least one empty - * or deleted slot in the table. This only is a problem if we - * are out of memory and rehash isn't working. - */ - ++hash->count; - if (hash->count == hash->length) { - /* Don't allow count to reach length */ - --hash->count; - *status = U_MEMORY_ALLOCATION_ERROR; - goto err; - } - } - - /* We must in all cases handle storage properly. If there was an - * old key, then it must be deleted (if the deleter != NULL). - * Make hashcodes stored in table positive. - */ - return _uhash_setElement(hash, e, hashcode & 0x7FFFFFFF, key, value, hint); - - err: - /* If the deleters are non-NULL, this method adopts its key and/or - * value arguments, and we must be sure to delete the key and/or - * value in all cases, even upon failure. - */ - HASH_DELETE_KEY_VALUE(hash, key.pointer, value.pointer); - emptytok.pointer = NULL; emptytok.integer = 0; - return emptytok; -} - - -/******************************************************************** - * PUBLIC API - ********************************************************************/ - -U_CAPI UHashtable* U_EXPORT2 -uhash_open(UHashFunction *keyHash, - UKeyComparator *keyComp, - UValueComparator *valueComp, - UErrorCode *status) { - - return _uhash_create(keyHash, keyComp, valueComp, DEFAULT_PRIME_INDEX, status); -} - -U_CAPI UHashtable* U_EXPORT2 -uhash_openSize(UHashFunction *keyHash, - UKeyComparator *keyComp, - UValueComparator *valueComp, - int32_t size, - UErrorCode *status) { - - /* Find the smallest index i for which PRIMES[i] >= size. */ - int32_t i = 0; - while (i<(PRIMES_LENGTH-1) && PRIMES[i]= size. - int32_t i = 0; - while (i<(PRIMES_LENGTH-1) && PRIMES[i]elements != NULL) { - if (hash->keyDeleter != NULL || hash->valueDeleter != NULL) { - int32_t pos=UHASH_FIRST; - UHashElement *e; - while ((e = (UHashElement*) uhash_nextElement(hash, &pos)) != NULL) { - HASH_DELETE_KEY_VALUE(hash, e->key.pointer, e->value.pointer); - } - } - uprv_free(hash->elements); - hash->elements = NULL; - } - if (hash->allocated) { - uprv_free(hash); - } -} - -U_CAPI UHashFunction *U_EXPORT2 -uhash_setKeyHasher(UHashtable *hash, UHashFunction *fn) { - UHashFunction *result = hash->keyHasher; - hash->keyHasher = fn; - return result; -} - -U_CAPI UKeyComparator *U_EXPORT2 -uhash_setKeyComparator(UHashtable *hash, UKeyComparator *fn) { - UKeyComparator *result = hash->keyComparator; - hash->keyComparator = fn; - return result; -} -U_CAPI UValueComparator *U_EXPORT2 -uhash_setValueComparator(UHashtable *hash, UValueComparator *fn){ - UValueComparator *result = hash->valueComparator; - hash->valueComparator = fn; - return result; -} - -U_CAPI UObjectDeleter *U_EXPORT2 -uhash_setKeyDeleter(UHashtable *hash, UObjectDeleter *fn) { - UObjectDeleter *result = hash->keyDeleter; - hash->keyDeleter = fn; - return result; -} - -U_CAPI UObjectDeleter *U_EXPORT2 -uhash_setValueDeleter(UHashtable *hash, UObjectDeleter *fn) { - UObjectDeleter *result = hash->valueDeleter; - hash->valueDeleter = fn; - return result; -} - -U_CAPI void U_EXPORT2 -uhash_setResizePolicy(UHashtable *hash, enum UHashResizePolicy policy) { - UErrorCode status = U_ZERO_ERROR; - _uhash_internalSetResizePolicy(hash, policy); - hash->lowWaterMark = (int32_t)(hash->length * hash->lowWaterRatio); - hash->highWaterMark = (int32_t)(hash->length * hash->highWaterRatio); - _uhash_rehash(hash, &status); -} - -U_CAPI int32_t U_EXPORT2 -uhash_count(const UHashtable *hash) { - return hash->count; -} - -U_CAPI void* U_EXPORT2 -uhash_get(const UHashtable *hash, - const void* key) { - UHashTok keyholder; - keyholder.pointer = (void*) key; - return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.pointer; -} - -U_CAPI void* U_EXPORT2 -uhash_iget(const UHashtable *hash, - int32_t key) { - UHashTok keyholder; - keyholder.integer = key; - return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.pointer; -} - -U_CAPI int32_t U_EXPORT2 -uhash_geti(const UHashtable *hash, - const void* key) { - UHashTok keyholder; - keyholder.pointer = (void*) key; - return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.integer; -} - -U_CAPI int32_t U_EXPORT2 -uhash_igeti(const UHashtable *hash, - int32_t key) { - UHashTok keyholder; - keyholder.integer = key; - return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.integer; -} - -U_CAPI int32_t U_EXPORT2 -uhash_getiAndFound(const UHashtable *hash, - const void *key, - UBool *found) { - UHashTok keyholder; - keyholder.pointer = (void *)key; - const UHashElement *e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder)); - *found = !IS_EMPTY_OR_DELETED(e->hashcode); - return e->value.integer; -} - -U_CAPI int32_t U_EXPORT2 -uhash_igetiAndFound(const UHashtable *hash, - int32_t key, - UBool *found) { - UHashTok keyholder; - keyholder.integer = key; - const UHashElement *e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder)); - *found = !IS_EMPTY_OR_DELETED(e->hashcode); - return e->value.integer; -} - -U_CAPI void* U_EXPORT2 -uhash_put(UHashtable *hash, - void* key, - void* value, - UErrorCode *status) { - UHashTok keyholder, valueholder; - keyholder.pointer = key; - valueholder.pointer = value; - return _uhash_put(hash, keyholder, valueholder, - HINT_KEY_POINTER | HINT_VALUE_POINTER, - status).pointer; -} - -U_CAPI void* U_EXPORT2 -uhash_iput(UHashtable *hash, - int32_t key, - void* value, - UErrorCode *status) { - UHashTok keyholder, valueholder; - keyholder.integer = key; - valueholder.pointer = value; - return _uhash_put(hash, keyholder, valueholder, - HINT_VALUE_POINTER, - status).pointer; -} - -U_CAPI int32_t U_EXPORT2 -uhash_puti(UHashtable *hash, - void* key, - int32_t value, - UErrorCode *status) { - UHashTok keyholder, valueholder; - keyholder.pointer = key; - valueholder.integer = value; - return _uhash_put(hash, keyholder, valueholder, - HINT_KEY_POINTER, - status).integer; -} - - -U_CAPI int32_t U_EXPORT2 -uhash_iputi(UHashtable *hash, - int32_t key, - int32_t value, - UErrorCode *status) { - UHashTok keyholder, valueholder; - keyholder.integer = key; - valueholder.integer = value; - return _uhash_put(hash, keyholder, valueholder, - HINT_BOTH_INTEGERS, - status).integer; -} - -U_CAPI int32_t U_EXPORT2 -uhash_putiAllowZero(UHashtable *hash, - void *key, - int32_t value, - UErrorCode *status) { - UHashTok keyholder, valueholder; - keyholder.pointer = key; - valueholder.integer = value; - return _uhash_put(hash, keyholder, valueholder, - HINT_KEY_POINTER | HINT_ALLOW_ZERO, - status).integer; -} - - -U_CAPI int32_t U_EXPORT2 -uhash_iputiAllowZero(UHashtable *hash, - int32_t key, - int32_t value, - UErrorCode *status) { - UHashTok keyholder, valueholder; - keyholder.integer = key; - valueholder.integer = value; - return _uhash_put(hash, keyholder, valueholder, - HINT_BOTH_INTEGERS | HINT_ALLOW_ZERO, - status).integer; -} - -U_CAPI void* U_EXPORT2 -uhash_remove(UHashtable *hash, - const void* key) { - UHashTok keyholder; - keyholder.pointer = (void*) key; - return _uhash_remove(hash, keyholder).pointer; -} - -U_CAPI void* U_EXPORT2 -uhash_iremove(UHashtable *hash, - int32_t key) { - UHashTok keyholder; - keyholder.integer = key; - return _uhash_remove(hash, keyholder).pointer; -} - -U_CAPI int32_t U_EXPORT2 -uhash_removei(UHashtable *hash, - const void* key) { - UHashTok keyholder; - keyholder.pointer = (void*) key; - return _uhash_remove(hash, keyholder).integer; -} - -U_CAPI int32_t U_EXPORT2 -uhash_iremovei(UHashtable *hash, - int32_t key) { - UHashTok keyholder; - keyholder.integer = key; - return _uhash_remove(hash, keyholder).integer; -} - -U_CAPI void U_EXPORT2 -uhash_removeAll(UHashtable *hash) { - int32_t pos = UHASH_FIRST; - const UHashElement *e; - U_ASSERT(hash != NULL); - if (hash->count != 0) { - while ((e = uhash_nextElement(hash, &pos)) != NULL) { - uhash_removeElement(hash, e); - } - } - U_ASSERT(hash->count == 0); -} - -U_CAPI UBool U_EXPORT2 -uhash_containsKey(const UHashtable *hash, const void *key) { - UHashTok keyholder; - keyholder.pointer = (void *)key; - const UHashElement *e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder)); - return !IS_EMPTY_OR_DELETED(e->hashcode); -} - -/** - * Returns true if the UHashtable contains an item with this integer key. - * - * @param hash The target UHashtable. - * @param key An integer key stored in a hashtable - * @return true if the key is found. - */ -U_CAPI UBool U_EXPORT2 -uhash_icontainsKey(const UHashtable *hash, int32_t key) { - UHashTok keyholder; - keyholder.integer = key; - const UHashElement *e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder)); - return !IS_EMPTY_OR_DELETED(e->hashcode); -} - -U_CAPI const UHashElement* U_EXPORT2 -uhash_find(const UHashtable *hash, const void* key) { - UHashTok keyholder; - const UHashElement *e; - keyholder.pointer = (void*) key; - e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder)); - return IS_EMPTY_OR_DELETED(e->hashcode) ? NULL : e; -} - -U_CAPI const UHashElement* U_EXPORT2 -uhash_nextElement(const UHashtable *hash, int32_t *pos) { - /* Walk through the array until we find an element that is not - * EMPTY and not DELETED. - */ - int32_t i; - U_ASSERT(hash != NULL); - for (i = *pos + 1; i < hash->length; ++i) { - if (!IS_EMPTY_OR_DELETED(hash->elements[i].hashcode)) { - *pos = i; - return &(hash->elements[i]); - } - } - - /* No more elements */ - return NULL; -} - -U_CAPI void* U_EXPORT2 -uhash_removeElement(UHashtable *hash, const UHashElement* e) { - U_ASSERT(hash != NULL); - U_ASSERT(e != NULL); - if (!IS_EMPTY_OR_DELETED(e->hashcode)) { - UHashElement *nce = (UHashElement *)e; - return _uhash_internalRemoveElement(hash, nce).pointer; - } - return NULL; -} - -/******************************************************************** - * UHashTok convenience - ********************************************************************/ - -/** - * Return a UHashTok for an integer. - */ -/*U_CAPI UHashTok U_EXPORT2 -uhash_toki(int32_t i) { - UHashTok tok; - tok.integer = i; - return tok; -}*/ - -/** - * Return a UHashTok for a pointer. - */ -/*U_CAPI UHashTok U_EXPORT2 -uhash_tokp(void* p) { - UHashTok tok; - tok.pointer = p; - return tok; -}*/ - -/******************************************************************** - * PUBLIC Key Hash Functions - ********************************************************************/ - -U_CAPI int32_t U_EXPORT2 -uhash_hashUChars(const UHashTok key) { - const UChar *s = (const UChar *)key.pointer; - return s == NULL ? 0 : ustr_hashUCharsN(s, u_strlen(s)); -} - -U_CAPI int32_t U_EXPORT2 -uhash_hashChars(const UHashTok key) { - const char *s = (const char *)key.pointer; - return s == NULL ? 0 : static_cast(ustr_hashCharsN(s, static_cast(uprv_strlen(s)))); -} - -U_CAPI int32_t U_EXPORT2 -uhash_hashIChars(const UHashTok key) { - const char *s = (const char *)key.pointer; - return s == NULL ? 0 : ustr_hashICharsN(s, static_cast(uprv_strlen(s))); -} - -U_CAPI UBool U_EXPORT2 -uhash_equals(const UHashtable* hash1, const UHashtable* hash2){ - int32_t count1, count2, pos, i; - - if(hash1==hash2){ - return true; - } - - /* - * Make sure that we are comparing 2 valid hashes of the same type - * with valid comparison functions. - * Without valid comparison functions, a binary comparison - * of the hash values will yield random results on machines - * with 64-bit pointers and 32-bit integer hashes. - * A valueComparator is normally optional. - */ - if (hash1==NULL || hash2==NULL || - hash1->keyComparator != hash2->keyComparator || - hash1->valueComparator != hash2->valueComparator || - hash1->valueComparator == NULL) - { - /* - Normally we would return an error here about incompatible hash tables, - but we return false instead. - */ - return false; - } - - count1 = uhash_count(hash1); - count2 = uhash_count(hash2); - if(count1!=count2){ - return false; - } - - pos=UHASH_FIRST; - for(i=0; ikey; - const UHashTok val1 = elem1->value; - /* here the keys are not compared, instead the key form hash1 is used to fetch - * value from hash2. If the hashes are equal then then both hashes should - * contain equal values for the same key! - */ - const UHashElement* elem2 = _uhash_find(hash2, key1, hash2->keyHasher(key1)); - const UHashTok val2 = elem2->value; - if(hash1->valueComparator(val1, val2)==false){ - return false; - } - } - return true; -} - -/******************************************************************** - * PUBLIC Comparator Functions - ********************************************************************/ - -U_CAPI UBool U_EXPORT2 -uhash_compareUChars(const UHashTok key1, const UHashTok key2) { - const UChar *p1 = (const UChar*) key1.pointer; - const UChar *p2 = (const UChar*) key2.pointer; - if (p1 == p2) { - return true; - } - if (p1 == NULL || p2 == NULL) { - return false; - } - while (*p1 != 0 && *p1 == *p2) { - ++p1; - ++p2; - } - return (UBool)(*p1 == *p2); -} - -U_CAPI UBool U_EXPORT2 -uhash_compareChars(const UHashTok key1, const UHashTok key2) { - const char *p1 = (const char*) key1.pointer; - const char *p2 = (const char*) key2.pointer; - if (p1 == p2) { - return true; - } - if (p1 == NULL || p2 == NULL) { - return false; - } - while (*p1 != 0 && *p1 == *p2) { - ++p1; - ++p2; - } - return (UBool)(*p1 == *p2); -} - -U_CAPI UBool U_EXPORT2 -uhash_compareIChars(const UHashTok key1, const UHashTok key2) { - const char *p1 = (const char*) key1.pointer; - const char *p2 = (const char*) key2.pointer; - if (p1 == p2) { - return true; - } - if (p1 == NULL || p2 == NULL) { - return false; - } - while (*p1 != 0 && uprv_tolower(*p1) == uprv_tolower(*p2)) { - ++p1; - ++p2; - } - return (UBool)(*p1 == *p2); -} - -/******************************************************************** - * PUBLIC int32_t Support Functions - ********************************************************************/ - -U_CAPI int32_t U_EXPORT2 -uhash_hashLong(const UHashTok key) { - return key.integer; -} - -U_CAPI UBool U_EXPORT2 -uhash_compareLong(const UHashTok key1, const UHashTok key2) { - return (UBool)(key1.integer == key2.integer); -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 1997-2016, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* Date Name Description +* 03/22/00 aliu Adapted from original C++ ICU Hashtable. +* 07/06/01 aliu Modified to support int32_t keys on +* platforms with sizeof(void*) < 32. +****************************************************************************** +*/ + +#include "uhash.h" +#include "unicode/ustring.h" +#include "cstring.h" +#include "cmemory.h" +#include "uassert.h" +#include "ustr_imp.h" + +/* This hashtable is implemented as a double hash. All elements are + * stored in a single array with no secondary storage for collision + * resolution (no linked list, etc.). When there is a hash collision + * (when two unequal keys have the same hashcode) we resolve this by + * using a secondary hash. The secondary hash is an increment + * computed as a hash function (a different one) of the primary + * hashcode. This increment is added to the initial hash value to + * obtain further slots assigned to the same hash code. For this to + * work, the length of the array and the increment must be relatively + * prime. The easiest way to achieve this is to have the length of + * the array be prime, and the increment be any value from + * 1..length-1. + * + * Hashcodes are 32-bit integers. We make sure all hashcodes are + * non-negative by masking off the top bit. This has two effects: (1) + * modulo arithmetic is simplified. If we allowed negative hashcodes, + * then when we computed hashcode % length, we could get a negative + * result, which we would then have to adjust back into range. It's + * simpler to just make hashcodes non-negative. (2) It makes it easy + * to check for empty vs. occupied slots in the table. We just mark + * empty or deleted slots with a negative hashcode. + * + * The central function is _uhash_find(). This function looks for a + * slot matching the given key and hashcode. If one is found, it + * returns a pointer to that slot. If the table is full, and no match + * is found, it returns nullptr -- in theory. This would make the code + * more complicated, since all callers of _uhash_find() would then + * have to check for a nullptr result. To keep this from happening, we + * don't allow the table to fill. When there is only one + * empty/deleted slot left, uhash_put() will refuse to increase the + * count, and fail. This simplifies the code. In practice, one will + * seldom encounter this using default UHashtables. However, if a + * hashtable is set to a U_FIXED resize policy, or if memory is + * exhausted, then the table may fill. + * + * High and low water ratios control rehashing. They establish levels + * of fullness (from 0 to 1) outside of which the data array is + * reallocated and repopulated. Setting the low water ratio to zero + * means the table will never shrink. Setting the high water ratio to + * one means the table will never grow. The ratios should be + * coordinated with the ratio between successive elements of the + * PRIMES table, so that when the primeIndex is incremented or + * decremented during rehashing, it brings the ratio of count / length + * back into the desired range (between low and high water ratios). + */ + +/******************************************************************** + * PRIVATE Constants, Macros + ********************************************************************/ + +/* This is a list of non-consecutive primes chosen such that + * PRIMES[i+1] ~ 2*PRIMES[i]. (Currently, the ratio ranges from 1.81 + * to 2.18; the inverse ratio ranges from 0.459 to 0.552.) If this + * ratio is changed, the low and high water ratios should also be + * adjusted to suit. + * + * These prime numbers were also chosen so that they are the largest + * prime number while being less than a power of two. + */ +static const int32_t PRIMES[] = { + 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, + 65521, 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, + 16777213, 33554393, 67108859, 134217689, 268435399, 536870909, + 1073741789, 2147483647 /*, 4294967291 */ +}; + +#define PRIMES_LENGTH UPRV_LENGTHOF(PRIMES) +#define DEFAULT_PRIME_INDEX 4 + +/* These ratios are tuned to the PRIMES array such that a resize + * places the table back into the zone of non-resizing. That is, + * after a call to _uhash_rehash(), a subsequent call to + * _uhash_rehash() should do nothing (should not churn). This is only + * a potential problem with U_GROW_AND_SHRINK. + */ +static const float RESIZE_POLICY_RATIO_TABLE[6] = { + /* low, high water ratio */ + 0.0F, 0.5F, /* U_GROW: Grow on demand, do not shrink */ + 0.1F, 0.5F, /* U_GROW_AND_SHRINK: Grow and shrink on demand */ + 0.0F, 1.0F /* U_FIXED: Never change size */ +}; + +/* + Invariants for hashcode values: + + * DELETED < 0 + * EMPTY < 0 + * Real hashes >= 0 + + Hashcodes may not start out this way, but internally they are + adjusted so that they are always positive. We assume 32-bit + hashcodes; adjust these constants for other hashcode sizes. +*/ +#define HASH_DELETED ((int32_t) 0x80000000) +#define HASH_EMPTY ((int32_t) HASH_DELETED + 1) + +#define IS_EMPTY_OR_DELETED(x) ((x) < 0) + +/* This macro expects a UHashTok.pointer as its keypointer and + valuepointer parameters */ +#define HASH_DELETE_KEY_VALUE(hash, keypointer, valuepointer) UPRV_BLOCK_MACRO_BEGIN { \ + if (hash->keyDeleter != nullptr && keypointer != nullptr) { \ + (*hash->keyDeleter)(keypointer); \ + } \ + if (hash->valueDeleter != nullptr && valuepointer != nullptr) { \ + (*hash->valueDeleter)(valuepointer); \ + } \ +} UPRV_BLOCK_MACRO_END + +/* + * Constants for hinting whether a key or value is an integer + * or a pointer. If a hint bit is zero, then the associated + * token is assumed to be an integer. + */ +#define HINT_BOTH_INTEGERS (0) +#define HINT_KEY_POINTER (1) +#define HINT_VALUE_POINTER (2) +#define HINT_ALLOW_ZERO (4) + +/******************************************************************** + * PRIVATE Implementation + ********************************************************************/ + +static UHashTok +_uhash_setElement(UHashtable *hash, UHashElement* e, + int32_t hashcode, + UHashTok key, UHashTok value, int8_t hint) { + + UHashTok oldValue = e->value; + if (hash->keyDeleter != nullptr && e->key.pointer != nullptr && + e->key.pointer != key.pointer) { /* Avoid double deletion */ + (*hash->keyDeleter)(e->key.pointer); + } + if (hash->valueDeleter != nullptr) { + if (oldValue.pointer != nullptr && + oldValue.pointer != value.pointer) { /* Avoid double deletion */ + (*hash->valueDeleter)(oldValue.pointer); + } + oldValue.pointer = nullptr; + } + /* Compilers should copy the UHashTok union correctly, but even if + * they do, memory heap tools (e.g. BoundsChecker) can get + * confused when a pointer is cloaked in a union and then copied. + * TO ALLEVIATE THIS, we use hints (based on what API the user is + * calling) to copy pointers when we know the user thinks + * something is a pointer. */ + if (hint & HINT_KEY_POINTER) { + e->key.pointer = key.pointer; + } else { + e->key = key; + } + if (hint & HINT_VALUE_POINTER) { + e->value.pointer = value.pointer; + } else { + e->value = value; + } + e->hashcode = hashcode; + return oldValue; +} + +/** + * Assumes that the given element is not empty or deleted. + */ +static UHashTok +_uhash_internalRemoveElement(UHashtable *hash, UHashElement* e) { + UHashTok empty; + U_ASSERT(!IS_EMPTY_OR_DELETED(e->hashcode)); + --hash->count; + empty.pointer = nullptr; empty.integer = 0; + return _uhash_setElement(hash, e, HASH_DELETED, empty, empty, 0); +} + +static void +_uhash_internalSetResizePolicy(UHashtable *hash, enum UHashResizePolicy policy) { + U_ASSERT(hash != nullptr); + U_ASSERT(((int32_t)policy) >= 0); + U_ASSERT(((int32_t)policy) < 3); + hash->lowWaterRatio = RESIZE_POLICY_RATIO_TABLE[policy * 2]; + hash->highWaterRatio = RESIZE_POLICY_RATIO_TABLE[policy * 2 + 1]; +} + +/** + * Allocate internal data array of a size determined by the given + * prime index. If the index is out of range it is pinned into range. + * If the allocation fails the status is set to + * U_MEMORY_ALLOCATION_ERROR and all array storage is freed. In + * either case the previous array pointer is overwritten. + * + * Caller must ensure primeIndex is in range 0..PRIME_LENGTH-1. + */ +static void +_uhash_allocate(UHashtable *hash, + int32_t primeIndex, + UErrorCode *status) { + + UHashElement *p, *limit; + UHashTok emptytok; + + if (U_FAILURE(*status)) return; + + U_ASSERT(primeIndex >= 0 && primeIndex < PRIMES_LENGTH); + + hash->primeIndex = static_cast(primeIndex); + hash->length = PRIMES[primeIndex]; + + p = hash->elements = (UHashElement*) + uprv_malloc(sizeof(UHashElement) * hash->length); + + if (hash->elements == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + emptytok.pointer = nullptr; /* Only one of these two is needed */ + emptytok.integer = 0; /* but we don't know which one. */ + + limit = p + hash->length; + while (p < limit) { + p->key = emptytok; + p->value = emptytok; + p->hashcode = HASH_EMPTY; + ++p; + } + + hash->count = 0; + hash->lowWaterMark = (int32_t)(hash->length * hash->lowWaterRatio); + hash->highWaterMark = (int32_t)(hash->length * hash->highWaterRatio); +} + +static UHashtable* +_uhash_init(UHashtable *result, + UHashFunction *keyHash, + UKeyComparator *keyComp, + UValueComparator *valueComp, + int32_t primeIndex, + UErrorCode *status) +{ + if (U_FAILURE(*status)) return nullptr; + U_ASSERT(keyHash != nullptr); + U_ASSERT(keyComp != nullptr); + + result->keyHasher = keyHash; + result->keyComparator = keyComp; + result->valueComparator = valueComp; + result->keyDeleter = nullptr; + result->valueDeleter = nullptr; + result->allocated = false; + _uhash_internalSetResizePolicy(result, U_GROW); + + _uhash_allocate(result, primeIndex, status); + + if (U_FAILURE(*status)) { + return nullptr; + } + + return result; +} + +static UHashtable* +_uhash_create(UHashFunction *keyHash, + UKeyComparator *keyComp, + UValueComparator *valueComp, + int32_t primeIndex, + UErrorCode *status) { + UHashtable *result; + + if (U_FAILURE(*status)) return nullptr; + + result = (UHashtable*) uprv_malloc(sizeof(UHashtable)); + if (result == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + _uhash_init(result, keyHash, keyComp, valueComp, primeIndex, status); + result->allocated = true; + + if (U_FAILURE(*status)) { + uprv_free(result); + return nullptr; + } + + return result; +} + +/** + * Look for a key in the table, or if no such key exists, the first + * empty slot matching the given hashcode. Keys are compared using + * the keyComparator function. + * + * First find the start position, which is the hashcode modulo + * the length. Test it to see if it is: + * + * a. identical: First check the hash values for a quick check, + * then compare keys for equality using keyComparator. + * b. deleted + * c. empty + * + * Stop if it is identical or empty, otherwise continue by adding a + * "jump" value (moduloing by the length again to keep it within + * range) and retesting. For efficiency, there need enough empty + * values so that the searches stop within a reasonable amount of time. + * This can be changed by changing the high/low water marks. + * + * In theory, this function can return nullptr, if it is full (no empty + * or deleted slots) and if no matching key is found. In practice, we + * prevent this elsewhere (in uhash_put) by making sure the last slot + * in the table is never filled. + * + * The size of the table should be prime for this algorithm to work; + * otherwise we are not guaranteed that the jump value (the secondary + * hash) is relatively prime to the table length. + */ +static UHashElement* +_uhash_find(const UHashtable *hash, UHashTok key, + int32_t hashcode) { + + int32_t firstDeleted = -1; /* assume invalid index */ + int32_t theIndex, startIndex; + int32_t jump = 0; /* lazy evaluate */ + int32_t tableHash; + UHashElement *elements = hash->elements; + + hashcode &= 0x7FFFFFFF; /* must be positive */ + startIndex = theIndex = (hashcode ^ 0x4000000) % hash->length; + + do { + tableHash = elements[theIndex].hashcode; + if (tableHash == hashcode) { /* quick check */ + if ((*hash->keyComparator)(key, elements[theIndex].key)) { + return &(elements[theIndex]); + } + } else if (!IS_EMPTY_OR_DELETED(tableHash)) { + /* We have hit a slot which contains a key-value pair, + * but for which the hash code does not match. Keep + * looking. + */ + } else if (tableHash == HASH_EMPTY) { /* empty, end o' the line */ + break; + } else if (firstDeleted < 0) { /* remember first deleted */ + firstDeleted = theIndex; + } + if (jump == 0) { /* lazy compute jump */ + /* The jump value must be relatively prime to the table + * length. As long as the length is prime, then any value + * 1..length-1 will be relatively prime to it. + */ + jump = (hashcode % (hash->length - 1)) + 1; + } + theIndex = (theIndex + jump) % hash->length; + } while (theIndex != startIndex); + + if (firstDeleted >= 0) { + theIndex = firstDeleted; /* reset if had deleted slot */ + } else if (tableHash != HASH_EMPTY) { + /* We get to this point if the hashtable is full (no empty or + * deleted slots), and we've failed to find a match. THIS + * WILL NEVER HAPPEN as long as uhash_put() makes sure that + * count is always < length. + */ + UPRV_UNREACHABLE_EXIT; + } + return &(elements[theIndex]); +} + +/** + * Attempt to grow or shrink the data arrays in order to make the + * count fit between the high and low water marks. hash_put() and + * hash_remove() call this method when the count exceeds the high or + * low water marks. This method may do nothing, if memory allocation + * fails, or if the count is already in range, or if the length is + * already at the low or high limit. In any case, upon return the + * arrays will be valid. + */ +static void +_uhash_rehash(UHashtable *hash, UErrorCode *status) { + + UHashElement *old = hash->elements; + int32_t oldLength = hash->length; + int32_t newPrimeIndex = hash->primeIndex; + int32_t i; + + if (hash->count > hash->highWaterMark) { + if (++newPrimeIndex >= PRIMES_LENGTH) { + return; + } + } else if (hash->count < hash->lowWaterMark) { + if (--newPrimeIndex < 0) { + return; + } + } else { + return; + } + + _uhash_allocate(hash, newPrimeIndex, status); + + if (U_FAILURE(*status)) { + hash->elements = old; + hash->length = oldLength; + return; + } + + for (i = oldLength - 1; i >= 0; --i) { + if (!IS_EMPTY_OR_DELETED(old[i].hashcode)) { + UHashElement *e = _uhash_find(hash, old[i].key, old[i].hashcode); + U_ASSERT(e != nullptr); + U_ASSERT(e->hashcode == HASH_EMPTY); + e->key = old[i].key; + e->value = old[i].value; + e->hashcode = old[i].hashcode; + ++hash->count; + } + } + + uprv_free(old); +} + +static UHashTok +_uhash_remove(UHashtable *hash, + UHashTok key) { + /* First find the position of the key in the table. If the object + * has not been removed already, remove it. If the user wanted + * keys deleted, then delete it also. We have to put a special + * hashcode in that position that means that something has been + * deleted, since when we do a find, we have to continue PAST any + * deleted values. + */ + UHashTok result; + UHashElement* e = _uhash_find(hash, key, hash->keyHasher(key)); + U_ASSERT(e != nullptr); + result.pointer = nullptr; + result.integer = 0; + if (!IS_EMPTY_OR_DELETED(e->hashcode)) { + result = _uhash_internalRemoveElement(hash, e); + if (hash->count < hash->lowWaterMark) { + UErrorCode status = U_ZERO_ERROR; + _uhash_rehash(hash, &status); + } + } + return result; +} + +static UHashTok +_uhash_put(UHashtable *hash, + UHashTok key, + UHashTok value, + int8_t hint, + UErrorCode *status) { + + /* Put finds the position in the table for the new value. If the + * key is already in the table, it is deleted, if there is a + * non-nullptr keyDeleter. Then the key, the hash and the value are + * all put at the position in their respective arrays. + */ + int32_t hashcode; + UHashElement* e; + UHashTok emptytok; + + if (U_FAILURE(*status)) { + goto err; + } + U_ASSERT(hash != nullptr); + if ((hint & HINT_VALUE_POINTER) ? + value.pointer == nullptr : + value.integer == 0 && (hint & HINT_ALLOW_ZERO) == 0) { + /* Disallow storage of nullptr values, since nullptr is returned by + * get() to indicate an absent key. Storing nullptr == removing. + */ + return _uhash_remove(hash, key); + } + if (hash->count > hash->highWaterMark) { + _uhash_rehash(hash, status); + if (U_FAILURE(*status)) { + goto err; + } + } + + hashcode = (*hash->keyHasher)(key); + e = _uhash_find(hash, key, hashcode); + U_ASSERT(e != nullptr); + + if (IS_EMPTY_OR_DELETED(e->hashcode)) { + /* Important: We must never actually fill the table up. If we + * do so, then _uhash_find() will return nullptr, and we'll have + * to check for nullptr after every call to _uhash_find(). To + * avoid this we make sure there is always at least one empty + * or deleted slot in the table. This only is a problem if we + * are out of memory and rehash isn't working. + */ + ++hash->count; + if (hash->count == hash->length) { + /* Don't allow count to reach length */ + --hash->count; + *status = U_MEMORY_ALLOCATION_ERROR; + goto err; + } + } + + /* We must in all cases handle storage properly. If there was an + * old key, then it must be deleted (if the deleter != nullptr). + * Make hashcodes stored in table positive. + */ + return _uhash_setElement(hash, e, hashcode & 0x7FFFFFFF, key, value, hint); + + err: + /* If the deleters are non-nullptr, this method adopts its key and/or + * value arguments, and we must be sure to delete the key and/or + * value in all cases, even upon failure. + */ + HASH_DELETE_KEY_VALUE(hash, key.pointer, value.pointer); + emptytok.pointer = nullptr; emptytok.integer = 0; + return emptytok; +} + + +/******************************************************************** + * PUBLIC API + ********************************************************************/ + +U_CAPI UHashtable* U_EXPORT2 +uhash_open(UHashFunction *keyHash, + UKeyComparator *keyComp, + UValueComparator *valueComp, + UErrorCode *status) { + + return _uhash_create(keyHash, keyComp, valueComp, DEFAULT_PRIME_INDEX, status); +} + +U_CAPI UHashtable* U_EXPORT2 +uhash_openSize(UHashFunction *keyHash, + UKeyComparator *keyComp, + UValueComparator *valueComp, + int32_t size, + UErrorCode *status) { + + /* Find the smallest index i for which PRIMES[i] >= size. */ + int32_t i = 0; + while (i<(PRIMES_LENGTH-1) && PRIMES[i]= size. + int32_t i = 0; + while (i<(PRIMES_LENGTH-1) && PRIMES[i]elements != nullptr) { + if (hash->keyDeleter != nullptr || hash->valueDeleter != nullptr) { + int32_t pos=UHASH_FIRST; + UHashElement *e; + while ((e = (UHashElement*) uhash_nextElement(hash, &pos)) != nullptr) { + HASH_DELETE_KEY_VALUE(hash, e->key.pointer, e->value.pointer); + } + } + uprv_free(hash->elements); + hash->elements = nullptr; + } + if (hash->allocated) { + uprv_free(hash); + } +} + +U_CAPI UHashFunction *U_EXPORT2 +uhash_setKeyHasher(UHashtable *hash, UHashFunction *fn) { + UHashFunction *result = hash->keyHasher; + hash->keyHasher = fn; + return result; +} + +U_CAPI UKeyComparator *U_EXPORT2 +uhash_setKeyComparator(UHashtable *hash, UKeyComparator *fn) { + UKeyComparator *result = hash->keyComparator; + hash->keyComparator = fn; + return result; +} +U_CAPI UValueComparator *U_EXPORT2 +uhash_setValueComparator(UHashtable *hash, UValueComparator *fn){ + UValueComparator *result = hash->valueComparator; + hash->valueComparator = fn; + return result; +} + +U_CAPI UObjectDeleter *U_EXPORT2 +uhash_setKeyDeleter(UHashtable *hash, UObjectDeleter *fn) { + UObjectDeleter *result = hash->keyDeleter; + hash->keyDeleter = fn; + return result; +} + +U_CAPI UObjectDeleter *U_EXPORT2 +uhash_setValueDeleter(UHashtable *hash, UObjectDeleter *fn) { + UObjectDeleter *result = hash->valueDeleter; + hash->valueDeleter = fn; + return result; +} + +U_CAPI void U_EXPORT2 +uhash_setResizePolicy(UHashtable *hash, enum UHashResizePolicy policy) { + UErrorCode status = U_ZERO_ERROR; + _uhash_internalSetResizePolicy(hash, policy); + hash->lowWaterMark = (int32_t)(hash->length * hash->lowWaterRatio); + hash->highWaterMark = (int32_t)(hash->length * hash->highWaterRatio); + _uhash_rehash(hash, &status); +} + +U_CAPI int32_t U_EXPORT2 +uhash_count(const UHashtable *hash) { + return hash->count; +} + +U_CAPI void* U_EXPORT2 +uhash_get(const UHashtable *hash, + const void* key) { + UHashTok keyholder; + keyholder.pointer = (void*) key; + return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.pointer; +} + +U_CAPI void* U_EXPORT2 +uhash_iget(const UHashtable *hash, + int32_t key) { + UHashTok keyholder; + keyholder.integer = key; + return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.pointer; +} + +U_CAPI int32_t U_EXPORT2 +uhash_geti(const UHashtable *hash, + const void* key) { + UHashTok keyholder; + keyholder.pointer = (void*) key; + return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.integer; +} + +U_CAPI int32_t U_EXPORT2 +uhash_igeti(const UHashtable *hash, + int32_t key) { + UHashTok keyholder; + keyholder.integer = key; + return _uhash_find(hash, keyholder, hash->keyHasher(keyholder))->value.integer; +} + +U_CAPI int32_t U_EXPORT2 +uhash_getiAndFound(const UHashtable *hash, + const void *key, + UBool *found) { + UHashTok keyholder; + keyholder.pointer = (void *)key; + const UHashElement *e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder)); + *found = !IS_EMPTY_OR_DELETED(e->hashcode); + return e->value.integer; +} + +U_CAPI int32_t U_EXPORT2 +uhash_igetiAndFound(const UHashtable *hash, + int32_t key, + UBool *found) { + UHashTok keyholder; + keyholder.integer = key; + const UHashElement *e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder)); + *found = !IS_EMPTY_OR_DELETED(e->hashcode); + return e->value.integer; +} + +U_CAPI void* U_EXPORT2 +uhash_put(UHashtable *hash, + void* key, + void* value, + UErrorCode *status) { + UHashTok keyholder, valueholder; + keyholder.pointer = key; + valueholder.pointer = value; + return _uhash_put(hash, keyholder, valueholder, + HINT_KEY_POINTER | HINT_VALUE_POINTER, + status).pointer; +} + +U_CAPI void* U_EXPORT2 +uhash_iput(UHashtable *hash, + int32_t key, + void* value, + UErrorCode *status) { + UHashTok keyholder, valueholder; + keyholder.integer = key; + valueholder.pointer = value; + return _uhash_put(hash, keyholder, valueholder, + HINT_VALUE_POINTER, + status).pointer; +} + +U_CAPI int32_t U_EXPORT2 +uhash_puti(UHashtable *hash, + void* key, + int32_t value, + UErrorCode *status) { + UHashTok keyholder, valueholder; + keyholder.pointer = key; + valueholder.integer = value; + return _uhash_put(hash, keyholder, valueholder, + HINT_KEY_POINTER, + status).integer; +} + + +U_CAPI int32_t U_EXPORT2 +uhash_iputi(UHashtable *hash, + int32_t key, + int32_t value, + UErrorCode *status) { + UHashTok keyholder, valueholder; + keyholder.integer = key; + valueholder.integer = value; + return _uhash_put(hash, keyholder, valueholder, + HINT_BOTH_INTEGERS, + status).integer; +} + +U_CAPI int32_t U_EXPORT2 +uhash_putiAllowZero(UHashtable *hash, + void *key, + int32_t value, + UErrorCode *status) { + UHashTok keyholder, valueholder; + keyholder.pointer = key; + valueholder.integer = value; + return _uhash_put(hash, keyholder, valueholder, + HINT_KEY_POINTER | HINT_ALLOW_ZERO, + status).integer; +} + + +U_CAPI int32_t U_EXPORT2 +uhash_iputiAllowZero(UHashtable *hash, + int32_t key, + int32_t value, + UErrorCode *status) { + UHashTok keyholder, valueholder; + keyholder.integer = key; + valueholder.integer = value; + return _uhash_put(hash, keyholder, valueholder, + HINT_BOTH_INTEGERS | HINT_ALLOW_ZERO, + status).integer; +} + +U_CAPI void* U_EXPORT2 +uhash_remove(UHashtable *hash, + const void* key) { + UHashTok keyholder; + keyholder.pointer = (void*) key; + return _uhash_remove(hash, keyholder).pointer; +} + +U_CAPI void* U_EXPORT2 +uhash_iremove(UHashtable *hash, + int32_t key) { + UHashTok keyholder; + keyholder.integer = key; + return _uhash_remove(hash, keyholder).pointer; +} + +U_CAPI int32_t U_EXPORT2 +uhash_removei(UHashtable *hash, + const void* key) { + UHashTok keyholder; + keyholder.pointer = (void*) key; + return _uhash_remove(hash, keyholder).integer; +} + +U_CAPI int32_t U_EXPORT2 +uhash_iremovei(UHashtable *hash, + int32_t key) { + UHashTok keyholder; + keyholder.integer = key; + return _uhash_remove(hash, keyholder).integer; +} + +U_CAPI void U_EXPORT2 +uhash_removeAll(UHashtable *hash) { + int32_t pos = UHASH_FIRST; + const UHashElement *e; + U_ASSERT(hash != nullptr); + if (hash->count != 0) { + while ((e = uhash_nextElement(hash, &pos)) != nullptr) { + uhash_removeElement(hash, e); + } + } + U_ASSERT(hash->count == 0); +} + +U_CAPI UBool U_EXPORT2 +uhash_containsKey(const UHashtable *hash, const void *key) { + UHashTok keyholder; + keyholder.pointer = (void *)key; + const UHashElement *e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder)); + return !IS_EMPTY_OR_DELETED(e->hashcode); +} + +/** + * Returns true if the UHashtable contains an item with this integer key. + * + * @param hash The target UHashtable. + * @param key An integer key stored in a hashtable + * @return true if the key is found. + */ +U_CAPI UBool U_EXPORT2 +uhash_icontainsKey(const UHashtable *hash, int32_t key) { + UHashTok keyholder; + keyholder.integer = key; + const UHashElement *e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder)); + return !IS_EMPTY_OR_DELETED(e->hashcode); +} + +U_CAPI const UHashElement* U_EXPORT2 +uhash_find(const UHashtable *hash, const void* key) { + UHashTok keyholder; + const UHashElement *e; + keyholder.pointer = (void*) key; + e = _uhash_find(hash, keyholder, hash->keyHasher(keyholder)); + return IS_EMPTY_OR_DELETED(e->hashcode) ? nullptr : e; +} + +U_CAPI const UHashElement* U_EXPORT2 +uhash_nextElement(const UHashtable *hash, int32_t *pos) { + /* Walk through the array until we find an element that is not + * EMPTY and not DELETED. + */ + int32_t i; + U_ASSERT(hash != nullptr); + for (i = *pos + 1; i < hash->length; ++i) { + if (!IS_EMPTY_OR_DELETED(hash->elements[i].hashcode)) { + *pos = i; + return &(hash->elements[i]); + } + } + + /* No more elements */ + return nullptr; +} + +U_CAPI void* U_EXPORT2 +uhash_removeElement(UHashtable *hash, const UHashElement* e) { + U_ASSERT(hash != nullptr); + U_ASSERT(e != nullptr); + if (!IS_EMPTY_OR_DELETED(e->hashcode)) { + UHashElement *nce = (UHashElement *)e; + return _uhash_internalRemoveElement(hash, nce).pointer; + } + return nullptr; +} + +/******************************************************************** + * UHashTok convenience + ********************************************************************/ + +/** + * Return a UHashTok for an integer. + */ +/*U_CAPI UHashTok U_EXPORT2 +uhash_toki(int32_t i) { + UHashTok tok; + tok.integer = i; + return tok; +}*/ + +/** + * Return a UHashTok for a pointer. + */ +/*U_CAPI UHashTok U_EXPORT2 +uhash_tokp(void* p) { + UHashTok tok; + tok.pointer = p; + return tok; +}*/ + +/******************************************************************** + * PUBLIC Key Hash Functions + ********************************************************************/ + +U_CAPI int32_t U_EXPORT2 +uhash_hashUChars(const UHashTok key) { + const char16_t *s = (const char16_t *)key.pointer; + return s == nullptr ? 0 : ustr_hashUCharsN(s, u_strlen(s)); +} + +U_CAPI int32_t U_EXPORT2 +uhash_hashChars(const UHashTok key) { + const char *s = (const char *)key.pointer; + return s == nullptr ? 0 : static_cast(ustr_hashCharsN(s, static_cast(uprv_strlen(s)))); +} + +U_CAPI int32_t U_EXPORT2 +uhash_hashIChars(const UHashTok key) { + const char *s = (const char *)key.pointer; + return s == nullptr ? 0 : ustr_hashICharsN(s, static_cast(uprv_strlen(s))); +} + +U_CAPI UBool U_EXPORT2 +uhash_equals(const UHashtable* hash1, const UHashtable* hash2){ + int32_t count1, count2, pos, i; + + if(hash1==hash2){ + return true; + } + + /* + * Make sure that we are comparing 2 valid hashes of the same type + * with valid comparison functions. + * Without valid comparison functions, a binary comparison + * of the hash values will yield random results on machines + * with 64-bit pointers and 32-bit integer hashes. + * A valueComparator is normally optional. + */ + if (hash1==nullptr || hash2==nullptr || + hash1->keyComparator != hash2->keyComparator || + hash1->valueComparator != hash2->valueComparator || + hash1->valueComparator == nullptr) + { + /* + Normally we would return an error here about incompatible hash tables, + but we return false instead. + */ + return false; + } + + count1 = uhash_count(hash1); + count2 = uhash_count(hash2); + if(count1!=count2){ + return false; + } + + pos=UHASH_FIRST; + for(i=0; ikey; + const UHashTok val1 = elem1->value; + /* here the keys are not compared, instead the key form hash1 is used to fetch + * value from hash2. If the hashes are equal then then both hashes should + * contain equal values for the same key! + */ + const UHashElement* elem2 = _uhash_find(hash2, key1, hash2->keyHasher(key1)); + const UHashTok val2 = elem2->value; + if(hash1->valueComparator(val1, val2)==false){ + return false; + } + } + return true; +} + +/******************************************************************** + * PUBLIC Comparator Functions + ********************************************************************/ + +U_CAPI UBool U_EXPORT2 +uhash_compareUChars(const UHashTok key1, const UHashTok key2) { + const char16_t *p1 = (const char16_t*) key1.pointer; + const char16_t *p2 = (const char16_t*) key2.pointer; + if (p1 == p2) { + return true; + } + if (p1 == nullptr || p2 == nullptr) { + return false; + } + while (*p1 != 0 && *p1 == *p2) { + ++p1; + ++p2; + } + return (UBool)(*p1 == *p2); +} + +U_CAPI UBool U_EXPORT2 +uhash_compareChars(const UHashTok key1, const UHashTok key2) { + const char *p1 = (const char*) key1.pointer; + const char *p2 = (const char*) key2.pointer; + if (p1 == p2) { + return true; + } + if (p1 == nullptr || p2 == nullptr) { + return false; + } + while (*p1 != 0 && *p1 == *p2) { + ++p1; + ++p2; + } + return (UBool)(*p1 == *p2); +} + +U_CAPI UBool U_EXPORT2 +uhash_compareIChars(const UHashTok key1, const UHashTok key2) { + const char *p1 = (const char*) key1.pointer; + const char *p2 = (const char*) key2.pointer; + if (p1 == p2) { + return true; + } + if (p1 == nullptr || p2 == nullptr) { + return false; + } + while (*p1 != 0 && uprv_tolower(*p1) == uprv_tolower(*p2)) { + ++p1; + ++p2; + } + return (UBool)(*p1 == *p2); +} + +/******************************************************************** + * PUBLIC int32_t Support Functions + ********************************************************************/ + +U_CAPI int32_t U_EXPORT2 +uhash_hashLong(const UHashTok key) { + return key.integer; +} + +U_CAPI UBool U_EXPORT2 +uhash_compareLong(const UHashTok key1, const UHashTok key2) { + return (UBool)(key1.integer == key2.integer); +} diff --git a/deps/icu-small/source/common/uhash.h b/deps/icu-small/source/common/uhash.h index 2ce296f0ec7c36..db1443f11a47f7 100644 --- a/deps/icu-small/source/common/uhash.h +++ b/deps/icu-small/source/common/uhash.h @@ -1,811 +1,811 @@ -// © 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. -****************************************************************************** -* Date Name Description -* 03/22/00 aliu Adapted from original C++ ICU Hashtable. -* 07/06/01 aliu Modified to support int32_t keys on -* platforms with sizeof(void*) < 32. -****************************************************************************** -*/ - -#ifndef UHASH_H -#define UHASH_H - -#include "unicode/utypes.h" -#include "cmemory.h" -#include "uelement.h" -#include "unicode/localpointer.h" - -/** - * UHashtable stores key-value pairs and does moderately fast lookup - * based on keys. It provides a good tradeoff between access time and - * storage space. As elements are added to it, it grows to accommodate - * them. By default, the table never shrinks, even if all elements - * are removed from it. - * - * Keys and values are stored as void* pointers. These void* pointers - * may be actual pointers to strings, objects, or any other structure - * in memory, or they may simply be integral values cast to void*. - * UHashtable doesn't care and manipulates them via user-supplied - * functions. These functions hash keys, compare keys, delete keys, - * and delete values. Some function pointers are optional (may be - * NULL); others must be supplied. Several prebuilt functions exist - * to handle common key types. - * - * UHashtable ownership of keys and values is flexible, and controlled - * by whether or not the key deleter and value deleter functions are - * set. If a void* key is actually a pointer to a deletable object, - * then UHashtable can be made to delete that object by setting the - * key deleter function pointer to a non-NULL value. If this is done, - * then keys passed to uhash_put() are owned by the hashtable and will - * be deleted by it at some point, either as keys are replaced, or - * when uhash_close() is finally called. The same is true of values - * and the value deleter function pointer. Keys passed to methods - * other than uhash_put() are never owned by the hashtable. - * - * NULL values are not allowed. uhash_get() returns NULL to indicate - * a key that is not in the table, and having a NULL value in the - * table would generate an ambiguous result. If a key and a NULL - * value is passed to uhash_put(), this has the effect of doing a - * uhash_remove() on that key. This keeps uhash_get(), uhash_count(), - * and uhash_nextElement() consistent with one another. - * - * Keys and values can be integers. - * Functions that work with an integer key have an "i" prefix. - * Functions that work with an integer value have an "i" suffix. - * As with putting a NULL value pointer, putting a zero value integer removes the item. - * Except, there are pairs of functions that allow setting zero values - * and fetching (value, found) pairs. - * - * To see everything in a hashtable, use uhash_nextElement() to - * iterate through its contents. Each call to this function returns a - * UHashElement pointer. A hash element contains a key, value, and - * hashcode. During iteration an element may be deleted by calling - * uhash_removeElement(); iteration may safely continue thereafter. - * The uhash_remove() function may also be safely called in - * mid-iteration. If uhash_put() is called during iteration, - * the iteration is still guaranteed to terminate reasonably, but - * there is no guarantee that every element will be returned or that - * some won't be returned more than once. - * - * Under no circumstances should the UHashElement returned by - * uhash_nextElement be modified directly. - * - * By default, the hashtable grows when necessary, but never shrinks, - * even if all items are removed. For most applications this is - * optimal. However, in a highly dynamic usage where memory is at a - * premium, the table can be set to both grow and shrink by calling - * uhash_setResizePolicy() with the policy U_GROW_AND_SHRINK. In a - * situation where memory is critical and the client wants a table - * that does not grow at all, the constant U_FIXED can be used. - */ - -/******************************************************************** - * Data Structures - ********************************************************************/ - -U_CDECL_BEGIN - -/** - * A key or value within a UHashtable. - * The hashing and comparison functions take a pointer to a - * UHashTok, but the deleter receives the void* pointer within it. - */ -typedef UElement UHashTok; - -/** - * This is a single hash element. - */ -struct UHashElement { - /* Reorder these elements to pack nicely if necessary */ - int32_t hashcode; - UHashTok value; - UHashTok key; -}; -typedef struct UHashElement UHashElement; - -/** - * A hashing function. - * @param key A key stored in a hashtable - * @return A NON-NEGATIVE hash code for parm. - */ -typedef int32_t U_CALLCONV UHashFunction(const UHashTok key); - -/** - * A key equality (boolean) comparison function. - */ -typedef UElementsAreEqual UKeyComparator; - -/** - * A value equality (boolean) comparison function. - */ -typedef UElementsAreEqual UValueComparator; - -/* see cmemory.h for UObjectDeleter and uprv_deleteUObject() */ - -/** - * This specifies whether or not, and how, the hashtable resizes itself. - * See uhash_setResizePolicy(). - */ -enum UHashResizePolicy { - U_GROW, /* Grow on demand, do not shrink */ - U_GROW_AND_SHRINK, /* Grow and shrink on demand */ - U_FIXED /* Never change size */ -}; - -/** - * The UHashtable struct. Clients should treat this as an opaque data - * type and manipulate it only through the uhash_... API. - */ -struct UHashtable { - - /* Main key-value pair storage array */ - - UHashElement *elements; - - /* Function pointers */ - - UHashFunction *keyHasher; /* Computes hash from key. - * Never null. */ - UKeyComparator *keyComparator; /* Compares keys for equality. - * Never null. */ - UValueComparator *valueComparator; /* Compares the values for equality */ - - UObjectDeleter *keyDeleter; /* Deletes keys when required. - * If NULL won't do anything */ - UObjectDeleter *valueDeleter; /* Deletes values when required. - * If NULL won't do anything */ - - /* Size parameters */ - - int32_t count; /* The number of key-value pairs in this table. - * 0 <= count <= length. In practice we - * never let count == length (see code). */ - int32_t length; /* The physical size of the arrays hashes, keys - * and values. Must be prime. */ - - /* Rehashing thresholds */ - - int32_t highWaterMark; /* If count > highWaterMark, rehash */ - int32_t lowWaterMark; /* If count < lowWaterMark, rehash */ - float highWaterRatio; /* 0..1; high water as a fraction of length */ - float lowWaterRatio; /* 0..1; low water as a fraction of length */ - - int8_t primeIndex; /* Index into our prime table for length. - * length == PRIMES[primeIndex] */ - UBool allocated; /* Was this UHashtable allocated? */ -}; -typedef struct UHashtable UHashtable; - -U_CDECL_END - -/******************************************************************** - * API - ********************************************************************/ - -/** - * Initialize a new UHashtable. - * @param keyHash A pointer to the key hashing function. Must not be - * NULL. - * @param keyComp A pointer to the function that compares keys. Must - * not be NULL. - * @param status A pointer to an UErrorCode to receive any errors. - * @return A pointer to a UHashtable, or 0 if an error occurred. - * @see uhash_openSize - */ -U_CAPI UHashtable* U_EXPORT2 -uhash_open(UHashFunction *keyHash, - UKeyComparator *keyComp, - UValueComparator *valueComp, - UErrorCode *status); - -/** - * Initialize a new UHashtable with a given initial size. - * @param keyHash A pointer to the key hashing function. Must not be - * NULL. - * @param keyComp A pointer to the function that compares keys. Must - * not be NULL. - * @param size The initial capacity of this hashtable. - * @param status A pointer to an UErrorCode to receive any errors. - * @return A pointer to a UHashtable, or 0 if an error occurred. - * @see uhash_open - */ -U_CAPI UHashtable* U_EXPORT2 -uhash_openSize(UHashFunction *keyHash, - UKeyComparator *keyComp, - UValueComparator *valueComp, - int32_t size, - UErrorCode *status); - -/** - * Initialize an existing UHashtable. - * @param keyHash A pointer to the key hashing function. Must not be - * NULL. - * @param keyComp A pointer to the function that compares keys. Must - * not be NULL. - * @param status A pointer to an UErrorCode to receive any errors. - * @return A pointer to a UHashtable, or 0 if an error occurred. - * @see uhash_openSize - */ -U_CAPI UHashtable* U_EXPORT2 -uhash_init(UHashtable *hash, - UHashFunction *keyHash, - UKeyComparator *keyComp, - UValueComparator *valueComp, - UErrorCode *status); - -/** - * Initialize an existing UHashtable. - * @param keyHash A pointer to the key hashing function. Must not be - * NULL. - * @param keyComp A pointer to the function that compares keys. Must - * not be NULL. - * @param size The initial capacity of this hashtable. - * @param status A pointer to an UErrorCode to receive any errors. - * @return A pointer to a UHashtable, or 0 if an error occurred. - * @see uhash_openSize - */ -U_CAPI UHashtable* U_EXPORT2 -uhash_initSize(UHashtable *hash, - UHashFunction *keyHash, - UKeyComparator *keyComp, - UValueComparator *valueComp, - int32_t size, - UErrorCode *status); - -/** - * Close a UHashtable, releasing the memory used. - * @param hash The UHashtable to close. If hash is NULL no operation is performed. - */ -U_CAPI void U_EXPORT2 -uhash_close(UHashtable *hash); - - - -/** - * Set the function used to hash keys. - * @param hash The UHashtable to set - * @param fn the function to be used hash keys; must not be NULL - * @return the previous key hasher; non-NULL - */ -U_CAPI UHashFunction *U_EXPORT2 -uhash_setKeyHasher(UHashtable *hash, UHashFunction *fn); - -/** - * Set the function used to compare keys. The default comparison is a - * void* pointer comparison. - * @param hash The UHashtable to set - * @param fn the function to be used compare keys; must not be NULL - * @return the previous key comparator; non-NULL - */ -U_CAPI UKeyComparator *U_EXPORT2 -uhash_setKeyComparator(UHashtable *hash, UKeyComparator *fn); - -/** - * Set the function used to compare values. The default comparison is a - * void* pointer comparison. - * @param hash The UHashtable to set - * @param fn the function to be used compare keys; must not be NULL - * @return the previous key comparator; non-NULL - */ -U_CAPI UValueComparator *U_EXPORT2 -uhash_setValueComparator(UHashtable *hash, UValueComparator *fn); - -/** - * Set the function used to delete keys. If this function pointer is - * NULL, this hashtable does not delete keys. If it is non-NULL, this - * hashtable does delete keys. This function should be set once - * before any elements are added to the hashtable and should not be - * changed thereafter. - * @param hash The UHashtable to set - * @param fn the function to be used delete keys, or NULL - * @return the previous key deleter; may be NULL - */ -U_CAPI UObjectDeleter *U_EXPORT2 -uhash_setKeyDeleter(UHashtable *hash, UObjectDeleter *fn); - -/** - * Set the function used to delete values. If this function pointer - * is NULL, this hashtable does not delete values. If it is non-NULL, - * this hashtable does delete values. This function should be set - * once before any elements are added to the hashtable and should not - * be changed thereafter. - * @param hash The UHashtable to set - * @param fn the function to be used delete values, or NULL - * @return the previous value deleter; may be NULL - */ -U_CAPI UObjectDeleter *U_EXPORT2 -uhash_setValueDeleter(UHashtable *hash, UObjectDeleter *fn); - -/** - * Specify whether or not, and how, the hashtable resizes itself. - * By default, tables grow but do not shrink (policy U_GROW). - * See enum UHashResizePolicy. - * @param hash The UHashtable to set - * @param policy The way the hashtable resizes itself, {U_GROW, U_GROW_AND_SHRINK, U_FIXED} - */ -U_CAPI void U_EXPORT2 -uhash_setResizePolicy(UHashtable *hash, enum UHashResizePolicy policy); - -/** - * Get the number of key-value pairs stored in a UHashtable. - * @param hash The UHashtable to query. - * @return The number of key-value pairs stored in hash. - */ -U_CAPI int32_t U_EXPORT2 -uhash_count(const UHashtable *hash); - -/** - * Put a (key=pointer, value=pointer) item in a UHashtable. If the - * keyDeleter is non-NULL, then the hashtable owns 'key' after this - * call. If the valueDeleter is non-NULL, then the hashtable owns - * 'value' after this call. Storing a NULL value is the same as - * calling uhash_remove(). - * @param hash The target UHashtable. - * @param key The key to store. - * @param value The value to store, may be NULL (see above). - * @param status A pointer to an UErrorCode to receive any errors. - * @return The previous value, or NULL if none. - * @see uhash_get - */ -U_CAPI void* U_EXPORT2 -uhash_put(UHashtable *hash, - void *key, - void *value, - UErrorCode *status); - -/** - * Put a (key=integer, value=pointer) item in a UHashtable. - * keyDeleter must be NULL. If the valueDeleter is non-NULL, then the - * hashtable owns 'value' after this call. Storing a NULL value is - * the same as calling uhash_remove(). - * @param hash The target UHashtable. - * @param key The integer key to store. - * @param value The value to store, may be NULL (see above). - * @param status A pointer to an UErrorCode to receive any errors. - * @return The previous value, or NULL if none. - * @see uhash_get - */ -U_CAPI void* U_EXPORT2 -uhash_iput(UHashtable *hash, - int32_t key, - void* value, - UErrorCode *status); - -/** - * Put a (key=pointer, value=integer) item in a UHashtable. If the - * keyDeleter is non-NULL, then the hashtable owns 'key' after this - * call. valueDeleter must be NULL. Storing a 0 value is the same as - * calling uhash_remove(). - * @param hash The target UHashtable. - * @param key The key to store. - * @param value The integer value to store. - * @param status A pointer to an UErrorCode to receive any errors. - * @return The previous value, or 0 if none. - * @see uhash_get - */ -U_CAPI int32_t U_EXPORT2 -uhash_puti(UHashtable *hash, - void* key, - int32_t value, - UErrorCode *status); - -/** - * Put a (key=integer, value=integer) item in a UHashtable. If the - * keyDeleter is non-NULL, then the hashtable owns 'key' after this - * call. valueDeleter must be NULL. Storing a 0 value is the same as - * calling uhash_remove(). - * @param hash The target UHashtable. - * @param key The key to store. - * @param value The integer value to store. - * @param status A pointer to an UErrorCode to receive any errors. - * @return The previous value, or 0 if none. - * @see uhash_get - */ -U_CAPI int32_t U_EXPORT2 -uhash_iputi(UHashtable *hash, - int32_t key, - int32_t value, - UErrorCode *status); - -/** - * Put a (key=pointer, value=integer) item in a UHashtable. If the - * keyDeleter is non-NULL, then the hashtable owns 'key' after this - * call. valueDeleter must be NULL. - * Storing a 0 value is possible; call uhash_igetiAndFound() to retrieve values including zero. - * - * @param hash The target UHashtable. - * @param key The key to store. - * @param value The integer value to store. - * @param status A pointer to an UErrorCode to receive any errors. - * @return The previous value, or 0 if none. - * @see uhash_getiAndFound - */ -U_CAPI int32_t U_EXPORT2 -uhash_putiAllowZero(UHashtable *hash, - void *key, - int32_t value, - UErrorCode *status); - -/** - * Put a (key=integer, value=integer) item in a UHashtable. If the - * keyDeleter is non-NULL, then the hashtable owns 'key' after this - * call. valueDeleter must be NULL. - * Storing a 0 value is possible; call uhash_igetiAndFound() to retrieve values including zero. - * - * @param hash The target UHashtable. - * @param key The key to store. - * @param value The integer value to store. - * @param status A pointer to an UErrorCode to receive any errors. - * @return The previous value, or 0 if none. - * @see uhash_igetiAndFound - */ -U_CAPI int32_t U_EXPORT2 -uhash_iputiAllowZero(UHashtable *hash, - int32_t key, - int32_t value, - UErrorCode *status); - -/** - * Retrieve a pointer value from a UHashtable using a pointer key, - * as previously stored by uhash_put(). - * @param hash The target UHashtable. - * @param key A pointer key stored in a hashtable - * @return The requested item, or NULL if not found. - */ -U_CAPI void* U_EXPORT2 -uhash_get(const UHashtable *hash, - const void *key); - -/** - * Retrieve a pointer value from a UHashtable using a integer key, - * as previously stored by uhash_iput(). - * @param hash The target UHashtable. - * @param key An integer key stored in a hashtable - * @return The requested item, or NULL if not found. - */ -U_CAPI void* U_EXPORT2 -uhash_iget(const UHashtable *hash, - int32_t key); - -/** - * Retrieve an integer value from a UHashtable using a pointer key, - * as previously stored by uhash_puti(). - * @param hash The target UHashtable. - * @param key A pointer key stored in a hashtable - * @return The requested item, or 0 if not found. - */ -U_CAPI int32_t U_EXPORT2 -uhash_geti(const UHashtable *hash, - const void* key); -/** - * Retrieve an integer value from a UHashtable using an integer key, - * as previously stored by uhash_iputi(). - * @param hash The target UHashtable. - * @param key An integer key stored in a hashtable - * @return The requested item, or 0 if not found. - */ -U_CAPI int32_t U_EXPORT2 -uhash_igeti(const UHashtable *hash, - int32_t key); - -/** - * Retrieves an integer value from a UHashtable using a pointer key, - * as previously stored by uhash_putiAllowZero() or uhash_puti(). - * - * @param hash The target UHashtable. - * @param key A pointer key stored in a hashtable - * @param found A pointer to a boolean which will be set for whether the key was found. - * @return The requested item, or 0 if not found. - */ -U_CAPI int32_t U_EXPORT2 -uhash_getiAndFound(const UHashtable *hash, - const void *key, - UBool *found); - -/** - * Retrieves an integer value from a UHashtable using an integer key, - * as previously stored by uhash_iputiAllowZero() or uhash_iputi(). - * - * @param hash The target UHashtable. - * @param key An integer key stored in a hashtable - * @param found A pointer to a boolean which will be set for whether the key was found. - * @return The requested item, or 0 if not found. - */ -U_CAPI int32_t U_EXPORT2 -uhash_igetiAndFound(const UHashtable *hash, - int32_t key, - UBool *found); - -/** - * Remove an item from a UHashtable stored by uhash_put(). - * @param hash The target UHashtable. - * @param key A key stored in a hashtable - * @return The item removed, or NULL if not found. - */ -U_CAPI void* U_EXPORT2 -uhash_remove(UHashtable *hash, - const void *key); - -/** - * Remove an item from a UHashtable stored by uhash_iput(). - * @param hash The target UHashtable. - * @param key An integer key stored in a hashtable - * @return The item removed, or NULL if not found. - */ -U_CAPI void* U_EXPORT2 -uhash_iremove(UHashtable *hash, - int32_t key); - -/** - * Remove an item from a UHashtable stored by uhash_puti(). - * @param hash The target UHashtable. - * @param key An key stored in a hashtable - * @return The item removed, or 0 if not found. - */ -U_CAPI int32_t U_EXPORT2 -uhash_removei(UHashtable *hash, - const void* key); - -/** - * Remove an item from a UHashtable stored by uhash_iputi(). - * @param hash The target UHashtable. - * @param key An integer key stored in a hashtable - * @return The item removed, or 0 if not found. - */ -U_CAPI int32_t U_EXPORT2 -uhash_iremovei(UHashtable *hash, - int32_t key); - -/** - * Remove all items from a UHashtable. - * @param hash The target UHashtable. - */ -U_CAPI void U_EXPORT2 -uhash_removeAll(UHashtable *hash); - -/** - * Returns true if the UHashtable contains an item with this pointer key. - * - * @param hash The target UHashtable. - * @param key A pointer key stored in a hashtable - * @return true if the key is found. - */ -U_CAPI UBool U_EXPORT2 -uhash_containsKey(const UHashtable *hash, const void *key); - -/** - * Returns true if the UHashtable contains an item with this integer key. - * - * @param hash The target UHashtable. - * @param key An integer key stored in a hashtable - * @return true if the key is found. - */ -U_CAPI UBool U_EXPORT2 -uhash_icontainsKey(const UHashtable *hash, int32_t key); - -/** - * Locate an element of a UHashtable. The caller must not modify the - * returned object. The primary use of this function is to obtain the - * stored key when it may not be identical to the search key. For - * example, if the compare function is a case-insensitive string - * compare, then the hash key may be desired in order to obtain the - * canonical case corresponding to a search key. - * @param hash The target UHashtable. - * @param key A key stored in a hashtable - * @return a hash element, or NULL if the key is not found. - */ -U_CAPI const UHashElement* U_EXPORT2 -uhash_find(const UHashtable *hash, const void* key); - -/** - * \def UHASH_FIRST - * Constant for use with uhash_nextElement - * @see uhash_nextElement - */ -#define UHASH_FIRST (-1) - -/** - * Iterate through the elements of a UHashtable. The caller must not - * modify the returned object. However, uhash_removeElement() may be - * called during iteration to remove an element from the table. - * Iteration may safely be resumed afterwards. If uhash_put() is - * called during iteration the iteration will then be out of sync and - * should be restarted. - * @param hash The target UHashtable. - * @param pos This should be set to UHASH_FIRST initially, and left untouched - * thereafter. - * @return a hash element, or NULL if no further key-value pairs - * exist in the table. - */ -U_CAPI const UHashElement* U_EXPORT2 -uhash_nextElement(const UHashtable *hash, - int32_t *pos); - -/** - * Remove an element, returned by uhash_nextElement(), from the table. - * Iteration may be safely continued afterwards. - * @param hash The hashtable - * @param e The element, returned by uhash_nextElement(), to remove. - * Must not be NULL. Must not be an empty or deleted element (as long - * as this was returned by uhash_nextElement() it will not be empty or - * deleted). Note: Although this parameter is const, it will be - * modified. - * @return the value that was removed. - */ -U_CAPI void* U_EXPORT2 -uhash_removeElement(UHashtable *hash, const UHashElement* e); - -/******************************************************************** - * UHashTok convenience - ********************************************************************/ - -/** - * Return a UHashTok for an integer. - * @param i The given integer - * @return a UHashTok for an integer. - */ -/*U_CAPI UHashTok U_EXPORT2 -uhash_toki(int32_t i);*/ - -/** - * Return a UHashTok for a pointer. - * @param p The given pointer - * @return a UHashTok for a pointer. - */ -/*U_CAPI UHashTok U_EXPORT2 -uhash_tokp(void* p);*/ - -/******************************************************************** - * UChar* and char* Support Functions - ********************************************************************/ - -/** - * Generate a hash code for a null-terminated UChar* string. If the - * string is not null-terminated do not use this function. Use - * together with uhash_compareUChars. - * @param key The string (const UChar*) to hash. - * @return A hash code for the key. - */ -U_CAPI int32_t U_EXPORT2 -uhash_hashUChars(const UHashTok key); - -/** - * Generate a hash code for a null-terminated char* string. If the - * string is not null-terminated do not use this function. Use - * together with uhash_compareChars. - * @param key The string (const char*) to hash. - * @return A hash code for the key. - */ -U_CAPI int32_t U_EXPORT2 -uhash_hashChars(const UHashTok key); - -/** - * Generate a case-insensitive hash code for a null-terminated char* - * string. If the string is not null-terminated do not use this - * function. Use together with uhash_compareIChars. - * @param key The string (const char*) to hash. - * @return A hash code for the key. - */ -U_CAPI int32_t U_EXPORT2 -uhash_hashIChars(const UHashTok key); - -/** - * Comparator for null-terminated UChar* strings. Use together with - * uhash_hashUChars. - * @param key1 The string for comparison - * @param key2 The string for comparison - * @return true if key1 and key2 are equal, return false otherwise. - */ -U_CAPI UBool U_EXPORT2 -uhash_compareUChars(const UHashTok key1, const UHashTok key2); - -/** - * Comparator for null-terminated char* strings. Use together with - * uhash_hashChars. - * @param key1 The string for comparison - * @param key2 The string for comparison - * @return true if key1 and key2 are equal, return false otherwise. - */ -U_CAPI UBool U_EXPORT2 -uhash_compareChars(const UHashTok key1, const UHashTok key2); - -/** - * Case-insensitive comparator for null-terminated char* strings. Use - * together with uhash_hashIChars. - * @param key1 The string for comparison - * @param key2 The string for comparison - * @return true if key1 and key2 are equal, return false otherwise. - */ -U_CAPI UBool U_EXPORT2 -uhash_compareIChars(const UHashTok key1, const UHashTok key2); - -/******************************************************************** - * UnicodeString Support Functions - ********************************************************************/ - -/** - * Hash function for UnicodeString* keys. - * @param key The string (const char*) to hash. - * @return A hash code for the key. - */ -U_CAPI int32_t U_EXPORT2 -uhash_hashUnicodeString(const UElement key); - -/** - * Hash function for UnicodeString* keys (case insensitive). - * Make sure to use together with uhash_compareCaselessUnicodeString. - * @param key The string (const char*) to hash. - * @return A hash code for the key. - */ -U_CAPI int32_t U_EXPORT2 -uhash_hashCaselessUnicodeString(const UElement key); - -/******************************************************************** - * int32_t Support Functions - ********************************************************************/ - -/** - * Hash function for 32-bit integer keys. - * @param key The string (const char*) to hash. - * @return A hash code for the key. - */ -U_CAPI int32_t U_EXPORT2 -uhash_hashLong(const UHashTok key); - -/** - * Comparator function for 32-bit integer keys. - * @param key1 The integer for comparison - * @param Key2 The integer for comparison - * @return true if key1 and key2 are equal, return false otherwise - */ -U_CAPI UBool U_EXPORT2 -uhash_compareLong(const UHashTok key1, const UHashTok key2); - -/******************************************************************** - * Other Support Functions - ********************************************************************/ - -/** - * Deleter for Hashtable objects. - * @param obj The object to be deleted - */ -U_CAPI void U_EXPORT2 -uhash_deleteHashtable(void *obj); - -/* Use uprv_free() itself as a deleter for any key or value allocated using uprv_malloc. */ - -/** - * Checks if the given hashtables are equal or not. - * @param hash1 - * @param hash2 - * @return true if the hashtables are equal and false if not. - */ -U_CAPI UBool U_EXPORT2 -uhash_equals(const UHashtable* hash1, const UHashtable* hash2); - - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUHashtablePointer - * "Smart pointer" class, closes a UHashtable via uhash_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUHashtablePointer, UHashtable, uhash_close); - -U_NAMESPACE_END - -#endif - -#endif +// © 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. +****************************************************************************** +* Date Name Description +* 03/22/00 aliu Adapted from original C++ ICU Hashtable. +* 07/06/01 aliu Modified to support int32_t keys on +* platforms with sizeof(void*) < 32. +****************************************************************************** +*/ + +#ifndef UHASH_H +#define UHASH_H + +#include "unicode/utypes.h" +#include "cmemory.h" +#include "uelement.h" +#include "unicode/localpointer.h" + +/** + * UHashtable stores key-value pairs and does moderately fast lookup + * based on keys. It provides a good tradeoff between access time and + * storage space. As elements are added to it, it grows to accommodate + * them. By default, the table never shrinks, even if all elements + * are removed from it. + * + * Keys and values are stored as void* pointers. These void* pointers + * may be actual pointers to strings, objects, or any other structure + * in memory, or they may simply be integral values cast to void*. + * UHashtable doesn't care and manipulates them via user-supplied + * functions. These functions hash keys, compare keys, delete keys, + * and delete values. Some function pointers are optional (may be + * NULL); others must be supplied. Several prebuilt functions exist + * to handle common key types. + * + * UHashtable ownership of keys and values is flexible, and controlled + * by whether or not the key deleter and value deleter functions are + * set. If a void* key is actually a pointer to a deletable object, + * then UHashtable can be made to delete that object by setting the + * key deleter function pointer to a non-NULL value. If this is done, + * then keys passed to uhash_put() are owned by the hashtable and will + * be deleted by it at some point, either as keys are replaced, or + * when uhash_close() is finally called. The same is true of values + * and the value deleter function pointer. Keys passed to methods + * other than uhash_put() are never owned by the hashtable. + * + * NULL values are not allowed. uhash_get() returns NULL to indicate + * a key that is not in the table, and having a NULL value in the + * table would generate an ambiguous result. If a key and a NULL + * value is passed to uhash_put(), this has the effect of doing a + * uhash_remove() on that key. This keeps uhash_get(), uhash_count(), + * and uhash_nextElement() consistent with one another. + * + * Keys and values can be integers. + * Functions that work with an integer key have an "i" prefix. + * Functions that work with an integer value have an "i" suffix. + * As with putting a NULL value pointer, putting a zero value integer removes the item. + * Except, there are pairs of functions that allow setting zero values + * and fetching (value, found) pairs. + * + * To see everything in a hashtable, use uhash_nextElement() to + * iterate through its contents. Each call to this function returns a + * UHashElement pointer. A hash element contains a key, value, and + * hashcode. During iteration an element may be deleted by calling + * uhash_removeElement(); iteration may safely continue thereafter. + * The uhash_remove() function may also be safely called in + * mid-iteration. If uhash_put() is called during iteration, + * the iteration is still guaranteed to terminate reasonably, but + * there is no guarantee that every element will be returned or that + * some won't be returned more than once. + * + * Under no circumstances should the UHashElement returned by + * uhash_nextElement be modified directly. + * + * By default, the hashtable grows when necessary, but never shrinks, + * even if all items are removed. For most applications this is + * optimal. However, in a highly dynamic usage where memory is at a + * premium, the table can be set to both grow and shrink by calling + * uhash_setResizePolicy() with the policy U_GROW_AND_SHRINK. In a + * situation where memory is critical and the client wants a table + * that does not grow at all, the constant U_FIXED can be used. + */ + +/******************************************************************** + * Data Structures + ********************************************************************/ + +U_CDECL_BEGIN + +/** + * A key or value within a UHashtable. + * The hashing and comparison functions take a pointer to a + * UHashTok, but the deleter receives the void* pointer within it. + */ +typedef UElement UHashTok; + +/** + * This is a single hash element. + */ +struct UHashElement { + /* Reorder these elements to pack nicely if necessary */ + int32_t hashcode; + UHashTok value; + UHashTok key; +}; +typedef struct UHashElement UHashElement; + +/** + * A hashing function. + * @param key A key stored in a hashtable + * @return A NON-NEGATIVE hash code for parm. + */ +typedef int32_t U_CALLCONV UHashFunction(const UHashTok key); + +/** + * A key equality (boolean) comparison function. + */ +typedef UElementsAreEqual UKeyComparator; + +/** + * A value equality (boolean) comparison function. + */ +typedef UElementsAreEqual UValueComparator; + +/* see cmemory.h for UObjectDeleter and uprv_deleteUObject() */ + +/** + * This specifies whether or not, and how, the hashtable resizes itself. + * See uhash_setResizePolicy(). + */ +enum UHashResizePolicy { + U_GROW, /* Grow on demand, do not shrink */ + U_GROW_AND_SHRINK, /* Grow and shrink on demand */ + U_FIXED /* Never change size */ +}; + +/** + * The UHashtable struct. Clients should treat this as an opaque data + * type and manipulate it only through the uhash_... API. + */ +struct UHashtable { + + /* Main key-value pair storage array */ + + UHashElement *elements; + + /* Function pointers */ + + UHashFunction *keyHasher; /* Computes hash from key. + * Never null. */ + UKeyComparator *keyComparator; /* Compares keys for equality. + * Never null. */ + UValueComparator *valueComparator; /* Compares the values for equality */ + + UObjectDeleter *keyDeleter; /* Deletes keys when required. + * If NULL won't do anything */ + UObjectDeleter *valueDeleter; /* Deletes values when required. + * If NULL won't do anything */ + + /* Size parameters */ + + int32_t count; /* The number of key-value pairs in this table. + * 0 <= count <= length. In practice we + * never let count == length (see code). */ + int32_t length; /* The physical size of the arrays hashes, keys + * and values. Must be prime. */ + + /* Rehashing thresholds */ + + int32_t highWaterMark; /* If count > highWaterMark, rehash */ + int32_t lowWaterMark; /* If count < lowWaterMark, rehash */ + float highWaterRatio; /* 0..1; high water as a fraction of length */ + float lowWaterRatio; /* 0..1; low water as a fraction of length */ + + int8_t primeIndex; /* Index into our prime table for length. + * length == PRIMES[primeIndex] */ + UBool allocated; /* Was this UHashtable allocated? */ +}; +typedef struct UHashtable UHashtable; + +U_CDECL_END + +/******************************************************************** + * API + ********************************************************************/ + +/** + * Initialize a new UHashtable. + * @param keyHash A pointer to the key hashing function. Must not be + * NULL. + * @param keyComp A pointer to the function that compares keys. Must + * not be NULL. + * @param status A pointer to an UErrorCode to receive any errors. + * @return A pointer to a UHashtable, or 0 if an error occurred. + * @see uhash_openSize + */ +U_CAPI UHashtable* U_EXPORT2 +uhash_open(UHashFunction *keyHash, + UKeyComparator *keyComp, + UValueComparator *valueComp, + UErrorCode *status); + +/** + * Initialize a new UHashtable with a given initial size. + * @param keyHash A pointer to the key hashing function. Must not be + * NULL. + * @param keyComp A pointer to the function that compares keys. Must + * not be NULL. + * @param size The initial capacity of this hashtable. + * @param status A pointer to an UErrorCode to receive any errors. + * @return A pointer to a UHashtable, or 0 if an error occurred. + * @see uhash_open + */ +U_CAPI UHashtable* U_EXPORT2 +uhash_openSize(UHashFunction *keyHash, + UKeyComparator *keyComp, + UValueComparator *valueComp, + int32_t size, + UErrorCode *status); + +/** + * Initialize an existing UHashtable. + * @param keyHash A pointer to the key hashing function. Must not be + * NULL. + * @param keyComp A pointer to the function that compares keys. Must + * not be NULL. + * @param status A pointer to an UErrorCode to receive any errors. + * @return A pointer to a UHashtable, or 0 if an error occurred. + * @see uhash_openSize + */ +U_CAPI UHashtable* U_EXPORT2 +uhash_init(UHashtable *hash, + UHashFunction *keyHash, + UKeyComparator *keyComp, + UValueComparator *valueComp, + UErrorCode *status); + +/** + * Initialize an existing UHashtable. + * @param keyHash A pointer to the key hashing function. Must not be + * NULL. + * @param keyComp A pointer to the function that compares keys. Must + * not be NULL. + * @param size The initial capacity of this hashtable. + * @param status A pointer to an UErrorCode to receive any errors. + * @return A pointer to a UHashtable, or 0 if an error occurred. + * @see uhash_openSize + */ +U_CAPI UHashtable* U_EXPORT2 +uhash_initSize(UHashtable *hash, + UHashFunction *keyHash, + UKeyComparator *keyComp, + UValueComparator *valueComp, + int32_t size, + UErrorCode *status); + +/** + * Close a UHashtable, releasing the memory used. + * @param hash The UHashtable to close. If hash is NULL no operation is performed. + */ +U_CAPI void U_EXPORT2 +uhash_close(UHashtable *hash); + + + +/** + * Set the function used to hash keys. + * @param hash The UHashtable to set + * @param fn the function to be used hash keys; must not be NULL + * @return the previous key hasher; non-NULL + */ +U_CAPI UHashFunction *U_EXPORT2 +uhash_setKeyHasher(UHashtable *hash, UHashFunction *fn); + +/** + * Set the function used to compare keys. The default comparison is a + * void* pointer comparison. + * @param hash The UHashtable to set + * @param fn the function to be used compare keys; must not be NULL + * @return the previous key comparator; non-NULL + */ +U_CAPI UKeyComparator *U_EXPORT2 +uhash_setKeyComparator(UHashtable *hash, UKeyComparator *fn); + +/** + * Set the function used to compare values. The default comparison is a + * void* pointer comparison. + * @param hash The UHashtable to set + * @param fn the function to be used compare keys; must not be NULL + * @return the previous key comparator; non-NULL + */ +U_CAPI UValueComparator *U_EXPORT2 +uhash_setValueComparator(UHashtable *hash, UValueComparator *fn); + +/** + * Set the function used to delete keys. If this function pointer is + * NULL, this hashtable does not delete keys. If it is non-NULL, this + * hashtable does delete keys. This function should be set once + * before any elements are added to the hashtable and should not be + * changed thereafter. + * @param hash The UHashtable to set + * @param fn the function to be used delete keys, or NULL + * @return the previous key deleter; may be NULL + */ +U_CAPI UObjectDeleter *U_EXPORT2 +uhash_setKeyDeleter(UHashtable *hash, UObjectDeleter *fn); + +/** + * Set the function used to delete values. If this function pointer + * is NULL, this hashtable does not delete values. If it is non-NULL, + * this hashtable does delete values. This function should be set + * once before any elements are added to the hashtable and should not + * be changed thereafter. + * @param hash The UHashtable to set + * @param fn the function to be used delete values, or NULL + * @return the previous value deleter; may be NULL + */ +U_CAPI UObjectDeleter *U_EXPORT2 +uhash_setValueDeleter(UHashtable *hash, UObjectDeleter *fn); + +/** + * Specify whether or not, and how, the hashtable resizes itself. + * By default, tables grow but do not shrink (policy U_GROW). + * See enum UHashResizePolicy. + * @param hash The UHashtable to set + * @param policy The way the hashtable resizes itself, {U_GROW, U_GROW_AND_SHRINK, U_FIXED} + */ +U_CAPI void U_EXPORT2 +uhash_setResizePolicy(UHashtable *hash, enum UHashResizePolicy policy); + +/** + * Get the number of key-value pairs stored in a UHashtable. + * @param hash The UHashtable to query. + * @return The number of key-value pairs stored in hash. + */ +U_CAPI int32_t U_EXPORT2 +uhash_count(const UHashtable *hash); + +/** + * Put a (key=pointer, value=pointer) item in a UHashtable. If the + * keyDeleter is non-NULL, then the hashtable owns 'key' after this + * call. If the valueDeleter is non-NULL, then the hashtable owns + * 'value' after this call. Storing a NULL value is the same as + * calling uhash_remove(). + * @param hash The target UHashtable. + * @param key The key to store. + * @param value The value to store, may be NULL (see above). + * @param status A pointer to an UErrorCode to receive any errors. + * @return The previous value, or NULL if none. + * @see uhash_get + */ +U_CAPI void* U_EXPORT2 +uhash_put(UHashtable *hash, + void *key, + void *value, + UErrorCode *status); + +/** + * Put a (key=integer, value=pointer) item in a UHashtable. + * keyDeleter must be NULL. If the valueDeleter is non-NULL, then the + * hashtable owns 'value' after this call. Storing a NULL value is + * the same as calling uhash_remove(). + * @param hash The target UHashtable. + * @param key The integer key to store. + * @param value The value to store, may be NULL (see above). + * @param status A pointer to an UErrorCode to receive any errors. + * @return The previous value, or NULL if none. + * @see uhash_get + */ +U_CAPI void* U_EXPORT2 +uhash_iput(UHashtable *hash, + int32_t key, + void* value, + UErrorCode *status); + +/** + * Put a (key=pointer, value=integer) item in a UHashtable. If the + * keyDeleter is non-NULL, then the hashtable owns 'key' after this + * call. valueDeleter must be NULL. Storing a 0 value is the same as + * calling uhash_remove(). + * @param hash The target UHashtable. + * @param key The key to store. + * @param value The integer value to store. + * @param status A pointer to an UErrorCode to receive any errors. + * @return The previous value, or 0 if none. + * @see uhash_get + */ +U_CAPI int32_t U_EXPORT2 +uhash_puti(UHashtable *hash, + void* key, + int32_t value, + UErrorCode *status); + +/** + * Put a (key=integer, value=integer) item in a UHashtable. If the + * keyDeleter is non-NULL, then the hashtable owns 'key' after this + * call. valueDeleter must be NULL. Storing a 0 value is the same as + * calling uhash_remove(). + * @param hash The target UHashtable. + * @param key The key to store. + * @param value The integer value to store. + * @param status A pointer to an UErrorCode to receive any errors. + * @return The previous value, or 0 if none. + * @see uhash_get + */ +U_CAPI int32_t U_EXPORT2 +uhash_iputi(UHashtable *hash, + int32_t key, + int32_t value, + UErrorCode *status); + +/** + * Put a (key=pointer, value=integer) item in a UHashtable. If the + * keyDeleter is non-NULL, then the hashtable owns 'key' after this + * call. valueDeleter must be NULL. + * Storing a 0 value is possible; call uhash_igetiAndFound() to retrieve values including zero. + * + * @param hash The target UHashtable. + * @param key The key to store. + * @param value The integer value to store. + * @param status A pointer to an UErrorCode to receive any errors. + * @return The previous value, or 0 if none. + * @see uhash_getiAndFound + */ +U_CAPI int32_t U_EXPORT2 +uhash_putiAllowZero(UHashtable *hash, + void *key, + int32_t value, + UErrorCode *status); + +/** + * Put a (key=integer, value=integer) item in a UHashtable. If the + * keyDeleter is non-NULL, then the hashtable owns 'key' after this + * call. valueDeleter must be NULL. + * Storing a 0 value is possible; call uhash_igetiAndFound() to retrieve values including zero. + * + * @param hash The target UHashtable. + * @param key The key to store. + * @param value The integer value to store. + * @param status A pointer to an UErrorCode to receive any errors. + * @return The previous value, or 0 if none. + * @see uhash_igetiAndFound + */ +U_CAPI int32_t U_EXPORT2 +uhash_iputiAllowZero(UHashtable *hash, + int32_t key, + int32_t value, + UErrorCode *status); + +/** + * Retrieve a pointer value from a UHashtable using a pointer key, + * as previously stored by uhash_put(). + * @param hash The target UHashtable. + * @param key A pointer key stored in a hashtable + * @return The requested item, or NULL if not found. + */ +U_CAPI void* U_EXPORT2 +uhash_get(const UHashtable *hash, + const void *key); + +/** + * Retrieve a pointer value from a UHashtable using a integer key, + * as previously stored by uhash_iput(). + * @param hash The target UHashtable. + * @param key An integer key stored in a hashtable + * @return The requested item, or NULL if not found. + */ +U_CAPI void* U_EXPORT2 +uhash_iget(const UHashtable *hash, + int32_t key); + +/** + * Retrieve an integer value from a UHashtable using a pointer key, + * as previously stored by uhash_puti(). + * @param hash The target UHashtable. + * @param key A pointer key stored in a hashtable + * @return The requested item, or 0 if not found. + */ +U_CAPI int32_t U_EXPORT2 +uhash_geti(const UHashtable *hash, + const void* key); +/** + * Retrieve an integer value from a UHashtable using an integer key, + * as previously stored by uhash_iputi(). + * @param hash The target UHashtable. + * @param key An integer key stored in a hashtable + * @return The requested item, or 0 if not found. + */ +U_CAPI int32_t U_EXPORT2 +uhash_igeti(const UHashtable *hash, + int32_t key); + +/** + * Retrieves an integer value from a UHashtable using a pointer key, + * as previously stored by uhash_putiAllowZero() or uhash_puti(). + * + * @param hash The target UHashtable. + * @param key A pointer key stored in a hashtable + * @param found A pointer to a boolean which will be set for whether the key was found. + * @return The requested item, or 0 if not found. + */ +U_CAPI int32_t U_EXPORT2 +uhash_getiAndFound(const UHashtable *hash, + const void *key, + UBool *found); + +/** + * Retrieves an integer value from a UHashtable using an integer key, + * as previously stored by uhash_iputiAllowZero() or uhash_iputi(). + * + * @param hash The target UHashtable. + * @param key An integer key stored in a hashtable + * @param found A pointer to a boolean which will be set for whether the key was found. + * @return The requested item, or 0 if not found. + */ +U_CAPI int32_t U_EXPORT2 +uhash_igetiAndFound(const UHashtable *hash, + int32_t key, + UBool *found); + +/** + * Remove an item from a UHashtable stored by uhash_put(). + * @param hash The target UHashtable. + * @param key A key stored in a hashtable + * @return The item removed, or NULL if not found. + */ +U_CAPI void* U_EXPORT2 +uhash_remove(UHashtable *hash, + const void *key); + +/** + * Remove an item from a UHashtable stored by uhash_iput(). + * @param hash The target UHashtable. + * @param key An integer key stored in a hashtable + * @return The item removed, or NULL if not found. + */ +U_CAPI void* U_EXPORT2 +uhash_iremove(UHashtable *hash, + int32_t key); + +/** + * Remove an item from a UHashtable stored by uhash_puti(). + * @param hash The target UHashtable. + * @param key An key stored in a hashtable + * @return The item removed, or 0 if not found. + */ +U_CAPI int32_t U_EXPORT2 +uhash_removei(UHashtable *hash, + const void* key); + +/** + * Remove an item from a UHashtable stored by uhash_iputi(). + * @param hash The target UHashtable. + * @param key An integer key stored in a hashtable + * @return The item removed, or 0 if not found. + */ +U_CAPI int32_t U_EXPORT2 +uhash_iremovei(UHashtable *hash, + int32_t key); + +/** + * Remove all items from a UHashtable. + * @param hash The target UHashtable. + */ +U_CAPI void U_EXPORT2 +uhash_removeAll(UHashtable *hash); + +/** + * Returns true if the UHashtable contains an item with this pointer key. + * + * @param hash The target UHashtable. + * @param key A pointer key stored in a hashtable + * @return true if the key is found. + */ +U_CAPI UBool U_EXPORT2 +uhash_containsKey(const UHashtable *hash, const void *key); + +/** + * Returns true if the UHashtable contains an item with this integer key. + * + * @param hash The target UHashtable. + * @param key An integer key stored in a hashtable + * @return true if the key is found. + */ +U_CAPI UBool U_EXPORT2 +uhash_icontainsKey(const UHashtable *hash, int32_t key); + +/** + * Locate an element of a UHashtable. The caller must not modify the + * returned object. The primary use of this function is to obtain the + * stored key when it may not be identical to the search key. For + * example, if the compare function is a case-insensitive string + * compare, then the hash key may be desired in order to obtain the + * canonical case corresponding to a search key. + * @param hash The target UHashtable. + * @param key A key stored in a hashtable + * @return a hash element, or NULL if the key is not found. + */ +U_CAPI const UHashElement* U_EXPORT2 +uhash_find(const UHashtable *hash, const void* key); + +/** + * \def UHASH_FIRST + * Constant for use with uhash_nextElement + * @see uhash_nextElement + */ +#define UHASH_FIRST (-1) + +/** + * Iterate through the elements of a UHashtable. The caller must not + * modify the returned object. However, uhash_removeElement() may be + * called during iteration to remove an element from the table. + * Iteration may safely be resumed afterwards. If uhash_put() is + * called during iteration the iteration will then be out of sync and + * should be restarted. + * @param hash The target UHashtable. + * @param pos This should be set to UHASH_FIRST initially, and left untouched + * thereafter. + * @return a hash element, or NULL if no further key-value pairs + * exist in the table. + */ +U_CAPI const UHashElement* U_EXPORT2 +uhash_nextElement(const UHashtable *hash, + int32_t *pos); + +/** + * Remove an element, returned by uhash_nextElement(), from the table. + * Iteration may be safely continued afterwards. + * @param hash The hashtable + * @param e The element, returned by uhash_nextElement(), to remove. + * Must not be NULL. Must not be an empty or deleted element (as long + * as this was returned by uhash_nextElement() it will not be empty or + * deleted). Note: Although this parameter is const, it will be + * modified. + * @return the value that was removed. + */ +U_CAPI void* U_EXPORT2 +uhash_removeElement(UHashtable *hash, const UHashElement* e); + +/******************************************************************** + * UHashTok convenience + ********************************************************************/ + +/** + * Return a UHashTok for an integer. + * @param i The given integer + * @return a UHashTok for an integer. + */ +/*U_CAPI UHashTok U_EXPORT2 +uhash_toki(int32_t i);*/ + +/** + * Return a UHashTok for a pointer. + * @param p The given pointer + * @return a UHashTok for a pointer. + */ +/*U_CAPI UHashTok U_EXPORT2 +uhash_tokp(void* p);*/ + +/******************************************************************** + * UChar* and char* Support Functions + ********************************************************************/ + +/** + * Generate a hash code for a null-terminated UChar* string. If the + * string is not null-terminated do not use this function. Use + * together with uhash_compareUChars. + * @param key The string (const UChar*) to hash. + * @return A hash code for the key. + */ +U_CAPI int32_t U_EXPORT2 +uhash_hashUChars(const UHashTok key); + +/** + * Generate a hash code for a null-terminated char* string. If the + * string is not null-terminated do not use this function. Use + * together with uhash_compareChars. + * @param key The string (const char*) to hash. + * @return A hash code for the key. + */ +U_CAPI int32_t U_EXPORT2 +uhash_hashChars(const UHashTok key); + +/** + * Generate a case-insensitive hash code for a null-terminated char* + * string. If the string is not null-terminated do not use this + * function. Use together with uhash_compareIChars. + * @param key The string (const char*) to hash. + * @return A hash code for the key. + */ +U_CAPI int32_t U_EXPORT2 +uhash_hashIChars(const UHashTok key); + +/** + * Comparator for null-terminated UChar* strings. Use together with + * uhash_hashUChars. + * @param key1 The string for comparison + * @param key2 The string for comparison + * @return true if key1 and key2 are equal, return false otherwise. + */ +U_CAPI UBool U_EXPORT2 +uhash_compareUChars(const UHashTok key1, const UHashTok key2); + +/** + * Comparator for null-terminated char* strings. Use together with + * uhash_hashChars. + * @param key1 The string for comparison + * @param key2 The string for comparison + * @return true if key1 and key2 are equal, return false otherwise. + */ +U_CAPI UBool U_EXPORT2 +uhash_compareChars(const UHashTok key1, const UHashTok key2); + +/** + * Case-insensitive comparator for null-terminated char* strings. Use + * together with uhash_hashIChars. + * @param key1 The string for comparison + * @param key2 The string for comparison + * @return true if key1 and key2 are equal, return false otherwise. + */ +U_CAPI UBool U_EXPORT2 +uhash_compareIChars(const UHashTok key1, const UHashTok key2); + +/******************************************************************** + * UnicodeString Support Functions + ********************************************************************/ + +/** + * Hash function for UnicodeString* keys. + * @param key The string (const char*) to hash. + * @return A hash code for the key. + */ +U_CAPI int32_t U_EXPORT2 +uhash_hashUnicodeString(const UElement key); + +/** + * Hash function for UnicodeString* keys (case insensitive). + * Make sure to use together with uhash_compareCaselessUnicodeString. + * @param key The string (const char*) to hash. + * @return A hash code for the key. + */ +U_CAPI int32_t U_EXPORT2 +uhash_hashCaselessUnicodeString(const UElement key); + +/******************************************************************** + * int32_t Support Functions + ********************************************************************/ + +/** + * Hash function for 32-bit integer keys. + * @param key The string (const char*) to hash. + * @return A hash code for the key. + */ +U_CAPI int32_t U_EXPORT2 +uhash_hashLong(const UHashTok key); + +/** + * Comparator function for 32-bit integer keys. + * @param key1 The integer for comparison + * @param Key2 The integer for comparison + * @return true if key1 and key2 are equal, return false otherwise + */ +U_CAPI UBool U_EXPORT2 +uhash_compareLong(const UHashTok key1, const UHashTok key2); + +/******************************************************************** + * Other Support Functions + ********************************************************************/ + +/** + * Deleter for Hashtable objects. + * @param obj The object to be deleted + */ +U_CAPI void U_EXPORT2 +uhash_deleteHashtable(void *obj); + +/* Use uprv_free() itself as a deleter for any key or value allocated using uprv_malloc. */ + +/** + * Checks if the given hashtables are equal or not. + * @param hash1 + * @param hash2 + * @return true if the hashtables are equal and false if not. + */ +U_CAPI UBool U_EXPORT2 +uhash_equals(const UHashtable* hash1, const UHashtable* hash2); + + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUHashtablePointer + * "Smart pointer" class, closes a UHashtable via uhash_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUHashtablePointer, UHashtable, uhash_close); + +U_NAMESPACE_END + +#endif + +#endif diff --git a/deps/icu-small/source/common/uhash_us.cpp b/deps/icu-small/source/common/uhash_us.cpp index ef482c27463322..51ccd58d52f554 100644 --- a/deps/icu-small/source/common/uhash_us.cpp +++ b/deps/icu-small/source/common/uhash_us.cpp @@ -1,26 +1,26 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 1997-2011, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* Date Name Description -* 03/22/00 aliu Creation. -* 07/06/01 aliu Modified to support int32_t keys on -* platforms with sizeof(void*) < 32. -****************************************************************************** -*/ - -#include "hash.h" - -/** - * Deleter for Hashtable objects. - */ -U_CAPI void U_EXPORT2 -uhash_deleteHashtable(void *obj) { - U_NAMESPACE_USE - delete (Hashtable*) obj; -} - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 1997-2011, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* Date Name Description +* 03/22/00 aliu Creation. +* 07/06/01 aliu Modified to support int32_t keys on +* platforms with sizeof(void*) < 32. +****************************************************************************** +*/ + +#include "hash.h" + +/** + * Deleter for Hashtable objects. + */ +U_CAPI void U_EXPORT2 +uhash_deleteHashtable(void *obj) { + U_NAMESPACE_USE + delete (Hashtable*) obj; +} + +//eof diff --git a/deps/icu-small/source/common/uidna.cpp b/deps/icu-small/source/common/uidna.cpp index 1cbdeec3272794..08abdd09e821bb 100644 --- a/deps/icu-small/source/common/uidna.cpp +++ b/deps/icu-small/source/common/uidna.cpp @@ -1,921 +1,921 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ******************************************************************************* - * - * Copyright (C) 2003-2014, International Business Machines - * Corporation and others. All Rights Reserved. - * - ******************************************************************************* - * file name: uidna.cpp - * encoding: UTF-8 - * tab size: 8 (not used) - * indentation:4 - * - * created on: 2003feb1 - * created by: Ram Viswanadha - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_IDNA - -#include "unicode/uidna.h" -#include "unicode/ustring.h" -#include "unicode/usprep.h" -#include "punycode.h" -#include "ustr_imp.h" -#include "cmemory.h" -#include "uassert.h" -#include "sprpimpl.h" - -/* it is official IDNA ACE Prefix is "xn--" */ -static const UChar ACE_PREFIX[] ={ 0x0078,0x006E,0x002d,0x002d } ; -#define ACE_PREFIX_LENGTH 4 - -#define MAX_LABEL_LENGTH 63 -/* The Max length of the labels should not be more than MAX_LABEL_LENGTH */ -#define MAX_LABEL_BUFFER_SIZE 100 - -#define MAX_DOMAIN_NAME_LENGTH 255 -/* The Max length of the domain names should not be more than MAX_DOMAIN_NAME_LENGTH */ -#define MAX_IDN_BUFFER_SIZE MAX_DOMAIN_NAME_LENGTH+1 - -#define LOWER_CASE_DELTA 0x0020 -#define HYPHEN 0x002D -#define FULL_STOP 0x002E -#define CAPITAL_A 0x0041 -#define CAPITAL_Z 0x005A - -inline static UChar -toASCIILower(UChar ch){ - if(CAPITAL_A <= ch && ch <= CAPITAL_Z){ - return ch + LOWER_CASE_DELTA; - } - return ch; -} - -inline static UBool -startsWithPrefix(const UChar* src , int32_t srcLength){ - if(srcLength < ACE_PREFIX_LENGTH){ - return false; - } - - for(int8_t i=0; i< ACE_PREFIX_LENGTH; i++){ - if(toASCIILower(src[i]) != ACE_PREFIX[i]){ - return false; - } - } - return true; -} - - -inline static int32_t -compareCaseInsensitiveASCII(const UChar* s1, int32_t s1Len, - const UChar* s2, int32_t s2Len){ - - int32_t minLength; - int32_t lengthResult; - - // are we comparing different lengths? - if(s1Len != s2Len) { - if(s1Len < s2Len) { - minLength = s1Len; - lengthResult = -1; - } else { - minLength = s2Len; - lengthResult = 1; - } - } else { - // ok the lengths are equal - minLength = s1Len; - lengthResult = 0; - } - - UChar c1,c2; - int32_t rc; - - for(int32_t i =0;/* no condition */;i++) { - - /* If we reach the ends of both strings then they match */ - if(i == minLength) { - return lengthResult; - } - - c1 = s1[i]; - c2 = s2[i]; - - /* Case-insensitive comparison */ - if(c1!=c2) { - rc=(int32_t)toASCIILower(c1)-(int32_t)toASCIILower(c2); - if(rc!=0) { - lengthResult=rc; - break; - } - } - } - return lengthResult; -} - - -/** - * Ascertain if the given code point is a label separator as - * defined by the IDNA RFC - * - * @param ch The code point to be ascertained - * @return true if the char is a label separator - * @stable ICU 2.8 - */ -static inline UBool isLabelSeparator(UChar ch){ - switch(ch){ - case 0x002e: - case 0x3002: - case 0xFF0E: - case 0xFF61: - return true; - default: - return false; - } -} - -// returns the length of the label excluding the separator -// if *limit == separator then the length returned does not include -// the separtor. -static inline int32_t -getNextSeparator(UChar *src, int32_t srcLength, - UChar **limit, UBool *done){ - if(srcLength == -1){ - int32_t i; - for(i=0 ; ;i++){ - if(src[i] == 0){ - *limit = src + i; // point to null - *done = true; - return i; - } - if(isLabelSeparator(src[i])){ - *limit = src + (i+1); // go past the delimiter - return i; - - } - } - }else{ - int32_t i; - for(i=0;i0x007A){ - return false; - } - //[\\u002D \\u0030-\\u0039 \\u0041-\\u005A \\u0061-\\u007A] - if( (ch==0x002D) || - (0x0030 <= ch && ch <= 0x0039) || - (0x0041 <= ch && ch <= 0x005A) || - (0x0061 <= ch && ch <= 0x007A) - ){ - return true; - } - return false; -} - -static int32_t -_internal_toASCII(const UChar* src, int32_t srcLength, - UChar* dest, int32_t destCapacity, - int32_t options, - UStringPrepProfile* nameprep, - UParseError* parseError, - UErrorCode* status) -{ - - // TODO Revisit buffer handling. The label should not be over 63 ASCII characters. ICU4J may need to be updated too. - UChar b1Stack[MAX_LABEL_BUFFER_SIZE], b2Stack[MAX_LABEL_BUFFER_SIZE]; - //initialize pointers to stack buffers - UChar *b1 = b1Stack, *b2 = b2Stack; - int32_t b1Len=0, b2Len, - b1Capacity = MAX_LABEL_BUFFER_SIZE, - b2Capacity = MAX_LABEL_BUFFER_SIZE , - reqLength=0; - - int32_t namePrepOptions = ((options & UIDNA_ALLOW_UNASSIGNED) != 0) ? USPREP_ALLOW_UNASSIGNED: 0; - UBool* caseFlags = NULL; - - // the source contains all ascii codepoints - UBool srcIsASCII = true; - // assume the source contains all LDH codepoints - UBool srcIsLDH = true; - - int32_t j=0; - - //get the options - UBool useSTD3ASCIIRules = (UBool)((options & UIDNA_USE_STD3_RULES) != 0); - - int32_t failPos = -1; - - if(srcLength == -1){ - srcLength = u_strlen(src); - } - - if(srcLength > b1Capacity){ - b1 = (UChar*) uprv_malloc(srcLength * U_SIZEOF_UCHAR); - if(b1==NULL){ - *status = U_MEMORY_ALLOCATION_ERROR; - goto CLEANUP; - } - b1Capacity = srcLength; - } - - // step 1 - for( j=0;j 0x7F){ - srcIsASCII = false; - } - b1[b1Len++] = src[j]; - } - - // step 2 is performed only if the source contains non ASCII - if(srcIsASCII == false){ - - // step 2 - b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Capacity, namePrepOptions, parseError, status); - - if(*status == U_BUFFER_OVERFLOW_ERROR){ - // redo processing of string - // we do not have enough room so grow the buffer - if(b1 != b1Stack){ - uprv_free(b1); - } - b1 = (UChar*) uprv_malloc(b1Len * U_SIZEOF_UCHAR); - if(b1==NULL){ - *status = U_MEMORY_ALLOCATION_ERROR; - goto CLEANUP; - } - - *status = U_ZERO_ERROR; // reset error - - b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Len, namePrepOptions, parseError, status); - } - } - // error bail out - if(U_FAILURE(*status)){ - goto CLEANUP; - } - if(b1Len == 0){ - *status = U_IDNA_ZERO_LENGTH_LABEL_ERROR; - goto CLEANUP; - } - - // for step 3 & 4 - srcIsASCII = true; - for( j=0;j 0x7F){ - srcIsASCII = false; - }else if(isLDHChar(b1[j])==false){ // if the char is in ASCII range verify that it is an LDH character - srcIsLDH = false; - failPos = j; - } - } - if(useSTD3ASCIIRules == true){ - // verify 3a and 3b - // 3(a) Verify the absence of non-LDH ASCII code points; that is, the - // absence of 0..2C, 2E..2F, 3A..40, 5B..60, and 7B..7F. - // 3(b) Verify the absence of leading and trailing hyphen-minus; that - // is, the absence of U+002D at the beginning and end of the - // sequence. - if( srcIsLDH == false /* source at this point should not contain anyLDH characters */ - || b1[0] == HYPHEN || b1[b1Len-1] == HYPHEN){ - *status = U_IDNA_STD3_ASCII_RULES_ERROR; - - /* populate the parseError struct */ - if(srcIsLDH==false){ - // failPos is always set the index of failure - uprv_syntaxError(b1,failPos, b1Len,parseError); - }else if(b1[0] == HYPHEN){ - // fail position is 0 - uprv_syntaxError(b1,0,b1Len,parseError); - }else{ - // the last index in the source is always length-1 - uprv_syntaxError(b1, (b1Len>0) ? b1Len-1 : b1Len, b1Len,parseError); - } - - goto CLEANUP; - } - } - // Step 4: if the source is ASCII then proceed to step 8 - if(srcIsASCII){ - if(b1Len <= destCapacity){ - u_memmove(dest, b1, b1Len); - reqLength = b1Len; - }else{ - reqLength = b1Len; - goto CLEANUP; - } - }else{ - // step 5 : verify the sequence does not begin with ACE prefix - if(!startsWithPrefix(b1,b1Len)){ - - //step 6: encode the sequence with punycode - - // do not preserve the case flags for now! - // TODO: Preserve the case while implementing the RFE - // caseFlags = (UBool*) uprv_malloc(b1Len * sizeof(UBool)); - // uprv_memset(caseFlags,true,b1Len); - - b2Len = u_strToPunycode(b1,b1Len,b2,b2Capacity,caseFlags, status); - - if(*status == U_BUFFER_OVERFLOW_ERROR){ - // redo processing of string - /* we do not have enough room so grow the buffer*/ - b2 = (UChar*) uprv_malloc(b2Len * U_SIZEOF_UCHAR); - if(b2 == NULL){ - *status = U_MEMORY_ALLOCATION_ERROR; - goto CLEANUP; - } - - *status = U_ZERO_ERROR; // reset error - - b2Len = u_strToPunycode(b1,b1Len,b2,b2Len,caseFlags, status); - } - //error bail out - if(U_FAILURE(*status)){ - goto CLEANUP; - } - // TODO : Reconsider while implementing the case preserve RFE - // convert all codepoints to lower case ASCII - // toASCIILower(b2,b2Len); - reqLength = b2Len+ACE_PREFIX_LENGTH; - - if(reqLength > destCapacity){ - *status = U_BUFFER_OVERFLOW_ERROR; - goto CLEANUP; - } - //Step 7: prepend the ACE prefix - u_memcpy(dest, ACE_PREFIX, ACE_PREFIX_LENGTH); - //Step 6: copy the contents in b2 into dest - u_memcpy(dest+ACE_PREFIX_LENGTH, b2, b2Len); - - }else{ - *status = U_IDNA_ACE_PREFIX_ERROR; - //position of failure is 0 - uprv_syntaxError(b1,0,b1Len,parseError); - goto CLEANUP; - } - } - // step 8: verify the length of label - if(reqLength > MAX_LABEL_LENGTH){ - *status = U_IDNA_LABEL_TOO_LONG_ERROR; - } - -CLEANUP: - if(b1 != b1Stack){ - uprv_free(b1); - } - if(b2 != b2Stack){ - uprv_free(b2); - } - uprv_free(caseFlags); - - return u_terminateUChars(dest, destCapacity, reqLength, status); -} - -static int32_t -_internal_toUnicode(const UChar* src, int32_t srcLength, - UChar* dest, int32_t destCapacity, - int32_t options, - UStringPrepProfile* nameprep, - UParseError* parseError, - UErrorCode* status) -{ - - //get the options - //UBool useSTD3ASCIIRules = (UBool)((options & UIDNA_USE_STD3_RULES) != 0); - int32_t namePrepOptions = ((options & UIDNA_ALLOW_UNASSIGNED) != 0) ? USPREP_ALLOW_UNASSIGNED: 0; - - // TODO Revisit buffer handling. The label should not be over 63 ASCII characters. ICU4J may need to be updated too. - UChar b1Stack[MAX_LABEL_BUFFER_SIZE], b2Stack[MAX_LABEL_BUFFER_SIZE], b3Stack[MAX_LABEL_BUFFER_SIZE]; - - //initialize pointers to stack buffers - UChar *b1 = b1Stack, *b2 = b2Stack, *b1Prime=NULL, *b3=b3Stack; - int32_t b1Len = 0, b2Len, b1PrimeLen, b3Len, - b1Capacity = MAX_LABEL_BUFFER_SIZE, - b2Capacity = MAX_LABEL_BUFFER_SIZE, - b3Capacity = MAX_LABEL_BUFFER_SIZE, - reqLength=0; - - UBool* caseFlags = NULL; - - UBool srcIsASCII = true; - /*UBool srcIsLDH = true; - int32_t failPos =0;*/ - - // step 1: find out if all the codepoints in src are ASCII - if(srcLength==-1){ - srcLength = 0; - for(;src[srcLength]!=0;){ - if(src[srcLength]> 0x7f){ - srcIsASCII = false; - }/*else if(isLDHChar(src[srcLength])==false){ - // here we do not assemble surrogates - // since we know that LDH code points - // are in the ASCII range only - srcIsLDH = false; - failPos = srcLength; - }*/ - srcLength++; - } - }else if(srcLength > 0){ - for(int32_t j=0; j 0x7f){ - srcIsASCII = false; - break; - }/*else if(isLDHChar(src[j])==false){ - // here we do not assemble surrogates - // since we know that LDH code points - // are in the ASCII range only - srcIsLDH = false; - failPos = j; - }*/ - } - }else{ - return 0; - } - - if(srcIsASCII == false){ - // step 2: process the string - b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Capacity, namePrepOptions, parseError, status); - if(*status == U_BUFFER_OVERFLOW_ERROR){ - // redo processing of string - /* we do not have enough room so grow the buffer*/ - b1 = (UChar*) uprv_malloc(b1Len * U_SIZEOF_UCHAR); - if(b1==NULL){ - *status = U_MEMORY_ALLOCATION_ERROR; - goto CLEANUP; - } - - *status = U_ZERO_ERROR; // reset error - - b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Len, namePrepOptions, parseError, status); - } - //bail out on error - if(U_FAILURE(*status)){ - goto CLEANUP; - } - }else{ - - //just point src to b1 - b1 = (UChar*) src; - b1Len = srcLength; - } - - // The RFC states that - // - // ToUnicode never fails. If any step fails, then the original input - // is returned immediately in that step. - // - - //step 3: verify ACE Prefix - if(startsWithPrefix(b1,b1Len)){ - - //step 4: Remove the ACE Prefix - b1Prime = b1 + ACE_PREFIX_LENGTH; - b1PrimeLen = b1Len - ACE_PREFIX_LENGTH; - - //step 5: Decode using punycode - b2Len = u_strFromPunycode(b1Prime, b1PrimeLen, b2, b2Capacity, caseFlags,status); - - if(*status == U_BUFFER_OVERFLOW_ERROR){ - // redo processing of string - /* we do not have enough room so grow the buffer*/ - b2 = (UChar*) uprv_malloc(b2Len * U_SIZEOF_UCHAR); - if(b2==NULL){ - *status = U_MEMORY_ALLOCATION_ERROR; - goto CLEANUP; - } - - *status = U_ZERO_ERROR; // reset error - - b2Len = u_strFromPunycode(b1Prime, b1PrimeLen, b2, b2Len, caseFlags, status); - } - - - //step 6:Apply toASCII - b3Len = uidna_toASCII(b2, b2Len, b3, b3Capacity, options, parseError, status); - - if(*status == U_BUFFER_OVERFLOW_ERROR){ - // redo processing of string - /* we do not have enough room so grow the buffer*/ - b3 = (UChar*) uprv_malloc(b3Len * U_SIZEOF_UCHAR); - if(b3==NULL){ - *status = U_MEMORY_ALLOCATION_ERROR; - goto CLEANUP; - } - - *status = U_ZERO_ERROR; // reset error - - b3Len = uidna_toASCII(b2,b2Len,b3,b3Len,options,parseError, status); - - } - //bail out on error - if(U_FAILURE(*status)){ - goto CLEANUP; - } - - //step 7: verify - if(compareCaseInsensitiveASCII(b1, b1Len, b3, b3Len) !=0){ - // Cause the original to be returned. - *status = U_IDNA_VERIFICATION_ERROR; - goto CLEANUP; - } - - //step 8: return output of step 5 - reqLength = b2Len; - if(b2Len <= destCapacity) { - u_memmove(dest, b2, b2Len); - } - } - else{ - // See the start of this if statement for why this is commented out. - // verify that STD3 ASCII rules are satisfied - /*if(useSTD3ASCIIRules == true){ - if( srcIsLDH == false // source contains some non-LDH characters - || src[0] == HYPHEN || src[srcLength-1] == HYPHEN){ - *status = U_IDNA_STD3_ASCII_RULES_ERROR; - - // populate the parseError struct - if(srcIsLDH==false){ - // failPos is always set the index of failure - uprv_syntaxError(src,failPos, srcLength,parseError); - }else if(src[0] == HYPHEN){ - // fail position is 0 - uprv_syntaxError(src,0,srcLength,parseError); - }else{ - // the last index in the source is always length-1 - uprv_syntaxError(src, (srcLength>0) ? srcLength-1 : srcLength, srcLength,parseError); - } - - goto CLEANUP; - } - }*/ - // just return the source - //copy the source to destination - if(srcLength <= destCapacity){ - u_memmove(dest, src, srcLength); - } - reqLength = srcLength; - } - - -CLEANUP: - - if(b1 != b1Stack && b1!=src){ - uprv_free(b1); - } - if(b2 != b2Stack){ - uprv_free(b2); - } - uprv_free(caseFlags); - - // The RFC states that - // - // ToUnicode never fails. If any step fails, then the original input - // is returned immediately in that step. - // - // So if any step fails lets copy source to destination - if(U_FAILURE(*status)){ - //copy the source to destination - if(dest && srcLength <= destCapacity){ - // srcLength should have already been set earlier. - U_ASSERT(srcLength >= 0); - u_memmove(dest, src, srcLength); - } - reqLength = srcLength; - *status = U_ZERO_ERROR; - } - - return u_terminateUChars(dest, destCapacity, reqLength, status); -} - -U_CAPI int32_t U_EXPORT2 -uidna_toASCII(const UChar* src, int32_t srcLength, - UChar* dest, int32_t destCapacity, - int32_t options, - UParseError* parseError, - UErrorCode* status){ - - if(status == NULL || U_FAILURE(*status)){ - return 0; - } - if((src==NULL) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){ - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status); - - if(U_FAILURE(*status)){ - return -1; - } - - int32_t retLen = _internal_toASCII(src, srcLength, dest, destCapacity, options, nameprep, parseError, status); - - /* close the profile*/ - usprep_close(nameprep); - - return retLen; -} - -U_CAPI int32_t U_EXPORT2 -uidna_toUnicode(const UChar* src, int32_t srcLength, - UChar* dest, int32_t destCapacity, - int32_t options, - UParseError* parseError, - UErrorCode* status){ - - if(status == NULL || U_FAILURE(*status)){ - return 0; - } - if( (src==NULL) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){ - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status); - - if(U_FAILURE(*status)){ - return -1; - } - - int32_t retLen = _internal_toUnicode(src, srcLength, dest, destCapacity, options, nameprep, parseError, status); - - usprep_close(nameprep); - - return retLen; -} - - -U_CAPI int32_t U_EXPORT2 -uidna_IDNToASCII( const UChar *src, int32_t srcLength, - UChar* dest, int32_t destCapacity, - int32_t options, - UParseError *parseError, - UErrorCode *status){ - - if(status == NULL || U_FAILURE(*status)){ - return 0; - } - if((src==NULL) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){ - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - int32_t reqLength = 0; - - UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status); - - if(U_FAILURE(*status)){ - return 0; - } - - //initialize pointers - UChar *delimiter = (UChar*)src; - UChar *labelStart = (UChar*)src; - UChar *currentDest = (UChar*) dest; - int32_t remainingLen = srcLength; - int32_t remainingDestCapacity = destCapacity; - int32_t labelLen = 0, labelReqLength = 0; - UBool done = false; - - - for(;;){ - - labelLen = getNextSeparator(labelStart,remainingLen, &delimiter,&done); - labelReqLength = 0; - if(!(labelLen==0 && done)){// make sure this is not a root label separator. - - labelReqLength = _internal_toASCII( labelStart, labelLen, - currentDest, remainingDestCapacity, - options, nameprep, - parseError, status); - - if(*status == U_BUFFER_OVERFLOW_ERROR){ - - *status = U_ZERO_ERROR; // reset error - remainingDestCapacity = 0; - } - } - - - if(U_FAILURE(*status)){ - break; - } - - reqLength +=labelReqLength; - // adjust the destination pointer - if(labelReqLength < remainingDestCapacity){ - currentDest = currentDest + labelReqLength; - remainingDestCapacity -= labelReqLength; - }else{ - // should never occur - remainingDestCapacity = 0; - } - - if(done == true){ - break; - } - - // add the label separator - if(remainingDestCapacity > 0){ - *currentDest++ = FULL_STOP; - remainingDestCapacity--; - } - reqLength++; - - labelStart = delimiter; - if(remainingLen >0 ){ - remainingLen = (int32_t)(srcLength - (delimiter - src)); - } - - } - - if(reqLength > MAX_DOMAIN_NAME_LENGTH){ - *status = U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR; - } - - usprep_close(nameprep); - - return u_terminateUChars(dest, destCapacity, reqLength, status); -} - -U_CAPI int32_t U_EXPORT2 -uidna_IDNToUnicode( const UChar* src, int32_t srcLength, - UChar* dest, int32_t destCapacity, - int32_t options, - UParseError* parseError, - UErrorCode* status){ - - if(status == NULL || U_FAILURE(*status)){ - return 0; - } - if((src==NULL) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){ - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - int32_t reqLength = 0; - - UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status); - - if(U_FAILURE(*status)){ - return 0; - } - - //initialize pointers - UChar *delimiter = (UChar*)src; - UChar *labelStart = (UChar*)src; - UChar *currentDest = (UChar*) dest; - int32_t remainingLen = srcLength; - int32_t remainingDestCapacity = destCapacity; - int32_t labelLen = 0, labelReqLength = 0; - UBool done = false; - - for(;;){ - - labelLen = getNextSeparator(labelStart,remainingLen, &delimiter,&done); - - // The RFC states that - // - // ToUnicode never fails. If any step fails, then the original input - // is returned immediately in that step. - // - // _internal_toUnicode will copy the label. - /*if(labelLen==0 && done==false){ - *status = U_IDNA_ZERO_LENGTH_LABEL_ERROR; - break; - }*/ - - labelReqLength = _internal_toUnicode(labelStart, labelLen, - currentDest, remainingDestCapacity, - options, nameprep, - parseError, status); - - if(*status == U_BUFFER_OVERFLOW_ERROR){ - *status = U_ZERO_ERROR; // reset error - remainingDestCapacity = 0; - } - - if(U_FAILURE(*status)){ - break; - } - - reqLength +=labelReqLength; - // adjust the destination pointer - if(labelReqLength < remainingDestCapacity){ - currentDest = currentDest + labelReqLength; - remainingDestCapacity -= labelReqLength; - }else{ - // should never occur - remainingDestCapacity = 0; - } - - if(done == true){ - break; - } - - // add the label separator - // Unlike the ToASCII operation we don't normalize the label separators - if(remainingDestCapacity > 0){ - *currentDest++ = *(labelStart + labelLen); - remainingDestCapacity--; - } - reqLength++; - - labelStart = delimiter; - if(remainingLen >0 ){ - remainingLen = (int32_t)(srcLength - (delimiter - src)); - } - - } - - if(reqLength > MAX_DOMAIN_NAME_LENGTH){ - *status = U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR; - } - - usprep_close(nameprep); - - return u_terminateUChars(dest, destCapacity, reqLength, status); -} - -U_CAPI int32_t U_EXPORT2 -uidna_compare( const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - int32_t options, - UErrorCode* status){ - - if(status == NULL || U_FAILURE(*status)){ - return -1; - } - - UChar b1Stack[MAX_IDN_BUFFER_SIZE], b2Stack[MAX_IDN_BUFFER_SIZE]; - UChar *b1 = b1Stack, *b2 = b2Stack; - int32_t b1Len, b2Len, b1Capacity = MAX_IDN_BUFFER_SIZE, b2Capacity = MAX_IDN_BUFFER_SIZE; - int32_t result=-1; - - UParseError parseError; - - b1Len = uidna_IDNToASCII(s1, length1, b1, b1Capacity, options, &parseError, status); - if(*status == U_BUFFER_OVERFLOW_ERROR){ - // redo processing of string - b1 = (UChar*) uprv_malloc(b1Len * U_SIZEOF_UCHAR); - if(b1==NULL){ - *status = U_MEMORY_ALLOCATION_ERROR; - goto CLEANUP; - } - - *status = U_ZERO_ERROR; // reset error - - b1Len = uidna_IDNToASCII(s1,length1,b1,b1Len, options, &parseError, status); - - } - - b2Len = uidna_IDNToASCII(s2,length2, b2,b2Capacity, options, &parseError, status); - if(*status == U_BUFFER_OVERFLOW_ERROR){ - // redo processing of string - b2 = (UChar*) uprv_malloc(b2Len * U_SIZEOF_UCHAR); - if(b2==NULL){ - *status = U_MEMORY_ALLOCATION_ERROR; - goto CLEANUP; - } - - *status = U_ZERO_ERROR; // reset error - - b2Len = uidna_IDNToASCII(s2, length2, b2, b2Len, options, &parseError, status); - - } - // when toASCII is applied all label separators are replaced with FULL_STOP - result = compareCaseInsensitiveASCII(b1,b1Len,b2,b2Len); - -CLEANUP: - if(b1 != b1Stack){ - uprv_free(b1); - } - - if(b2 != b2Stack){ - uprv_free(b2); - } - - return result; -} - -#endif /* #if !UCONFIG_NO_IDNA */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************************* + * + * Copyright (C) 2003-2014, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: uidna.cpp + * encoding: UTF-8 + * tab size: 8 (not used) + * indentation:4 + * + * created on: 2003feb1 + * created by: Ram Viswanadha + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_IDNA + +#include "unicode/uidna.h" +#include "unicode/ustring.h" +#include "unicode/usprep.h" +#include "punycode.h" +#include "ustr_imp.h" +#include "cmemory.h" +#include "uassert.h" +#include "sprpimpl.h" + +/* it is official IDNA ACE Prefix is "xn--" */ +static const char16_t ACE_PREFIX[] ={ 0x0078,0x006E,0x002d,0x002d } ; +#define ACE_PREFIX_LENGTH 4 + +#define MAX_LABEL_LENGTH 63 +/* The Max length of the labels should not be more than MAX_LABEL_LENGTH */ +#define MAX_LABEL_BUFFER_SIZE 100 + +#define MAX_DOMAIN_NAME_LENGTH 255 +/* The Max length of the domain names should not be more than MAX_DOMAIN_NAME_LENGTH */ +#define MAX_IDN_BUFFER_SIZE MAX_DOMAIN_NAME_LENGTH+1 + +#define LOWER_CASE_DELTA 0x0020 +#define HYPHEN 0x002D +#define FULL_STOP 0x002E +#define CAPITAL_A 0x0041 +#define CAPITAL_Z 0x005A + +inline static char16_t +toASCIILower(char16_t ch){ + if(CAPITAL_A <= ch && ch <= CAPITAL_Z){ + return ch + LOWER_CASE_DELTA; + } + return ch; +} + +inline static UBool +startsWithPrefix(const char16_t* src , int32_t srcLength){ + if(srcLength < ACE_PREFIX_LENGTH){ + return false; + } + + for(int8_t i=0; i< ACE_PREFIX_LENGTH; i++){ + if(toASCIILower(src[i]) != ACE_PREFIX[i]){ + return false; + } + } + return true; +} + + +inline static int32_t +compareCaseInsensitiveASCII(const char16_t* s1, int32_t s1Len, + const char16_t* s2, int32_t s2Len){ + + int32_t minLength; + int32_t lengthResult; + + // are we comparing different lengths? + if(s1Len != s2Len) { + if(s1Len < s2Len) { + minLength = s1Len; + lengthResult = -1; + } else { + minLength = s2Len; + lengthResult = 1; + } + } else { + // ok the lengths are equal + minLength = s1Len; + lengthResult = 0; + } + + char16_t c1,c2; + int32_t rc; + + for(int32_t i =0;/* no condition */;i++) { + + /* If we reach the ends of both strings then they match */ + if(i == minLength) { + return lengthResult; + } + + c1 = s1[i]; + c2 = s2[i]; + + /* Case-insensitive comparison */ + if(c1!=c2) { + rc=(int32_t)toASCIILower(c1)-(int32_t)toASCIILower(c2); + if(rc!=0) { + lengthResult=rc; + break; + } + } + } + return lengthResult; +} + + +/** + * Ascertain if the given code point is a label separator as + * defined by the IDNA RFC + * + * @param ch The code point to be ascertained + * @return true if the char is a label separator + * @stable ICU 2.8 + */ +static inline UBool isLabelSeparator(char16_t ch){ + switch(ch){ + case 0x002e: + case 0x3002: + case 0xFF0E: + case 0xFF61: + return true; + default: + return false; + } +} + +// returns the length of the label excluding the separator +// if *limit == separator then the length returned does not include +// the separtor. +static inline int32_t +getNextSeparator(char16_t *src, int32_t srcLength, + char16_t **limit, UBool *done){ + if(srcLength == -1){ + int32_t i; + for(i=0 ; ;i++){ + if(src[i] == 0){ + *limit = src + i; // point to null + *done = true; + return i; + } + if(isLabelSeparator(src[i])){ + *limit = src + (i+1); // go past the delimiter + return i; + + } + } + }else{ + int32_t i; + for(i=0;i0x007A){ + return false; + } + //[\\u002D \\u0030-\\u0039 \\u0041-\\u005A \\u0061-\\u007A] + if( (ch==0x002D) || + (0x0030 <= ch && ch <= 0x0039) || + (0x0041 <= ch && ch <= 0x005A) || + (0x0061 <= ch && ch <= 0x007A) + ){ + return true; + } + return false; +} + +static int32_t +_internal_toASCII(const char16_t* src, int32_t srcLength, + char16_t* dest, int32_t destCapacity, + int32_t options, + UStringPrepProfile* nameprep, + UParseError* parseError, + UErrorCode* status) +{ + + // TODO Revisit buffer handling. The label should not be over 63 ASCII characters. ICU4J may need to be updated too. + char16_t b1Stack[MAX_LABEL_BUFFER_SIZE], b2Stack[MAX_LABEL_BUFFER_SIZE]; + //initialize pointers to stack buffers + char16_t *b1 = b1Stack, *b2 = b2Stack; + int32_t b1Len=0, b2Len, + b1Capacity = MAX_LABEL_BUFFER_SIZE, + b2Capacity = MAX_LABEL_BUFFER_SIZE , + reqLength=0; + + int32_t namePrepOptions = ((options & UIDNA_ALLOW_UNASSIGNED) != 0) ? USPREP_ALLOW_UNASSIGNED: 0; + UBool* caseFlags = nullptr; + + // the source contains all ascii codepoints + UBool srcIsASCII = true; + // assume the source contains all LDH codepoints + UBool srcIsLDH = true; + + int32_t j=0; + + //get the options + UBool useSTD3ASCIIRules = (UBool)((options & UIDNA_USE_STD3_RULES) != 0); + + int32_t failPos = -1; + + if(srcLength == -1){ + srcLength = u_strlen(src); + } + + if(srcLength > b1Capacity){ + b1 = (char16_t*) uprv_malloc(srcLength * U_SIZEOF_UCHAR); + if(b1==nullptr){ + *status = U_MEMORY_ALLOCATION_ERROR; + goto CLEANUP; + } + b1Capacity = srcLength; + } + + // step 1 + for( j=0;j 0x7F){ + srcIsASCII = false; + } + b1[b1Len++] = src[j]; + } + + // step 2 is performed only if the source contains non ASCII + if(srcIsASCII == false){ + + // step 2 + b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Capacity, namePrepOptions, parseError, status); + + if(*status == U_BUFFER_OVERFLOW_ERROR){ + // redo processing of string + // we do not have enough room so grow the buffer + if(b1 != b1Stack){ + uprv_free(b1); + } + b1 = (char16_t*) uprv_malloc(b1Len * U_SIZEOF_UCHAR); + if(b1==nullptr){ + *status = U_MEMORY_ALLOCATION_ERROR; + goto CLEANUP; + } + + *status = U_ZERO_ERROR; // reset error + + b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Len, namePrepOptions, parseError, status); + } + } + // error bail out + if(U_FAILURE(*status)){ + goto CLEANUP; + } + if(b1Len == 0){ + *status = U_IDNA_ZERO_LENGTH_LABEL_ERROR; + goto CLEANUP; + } + + // for step 3 & 4 + srcIsASCII = true; + for( j=0;j 0x7F){ + srcIsASCII = false; + }else if(isLDHChar(b1[j])==false){ // if the char is in ASCII range verify that it is an LDH character + srcIsLDH = false; + failPos = j; + } + } + if(useSTD3ASCIIRules){ + // verify 3a and 3b + // 3(a) Verify the absence of non-LDH ASCII code points; that is, the + // absence of 0..2C, 2E..2F, 3A..40, 5B..60, and 7B..7F. + // 3(b) Verify the absence of leading and trailing hyphen-minus; that + // is, the absence of U+002D at the beginning and end of the + // sequence. + if( srcIsLDH == false /* source at this point should not contain anyLDH characters */ + || b1[0] == HYPHEN || b1[b1Len-1] == HYPHEN){ + *status = U_IDNA_STD3_ASCII_RULES_ERROR; + + /* populate the parseError struct */ + if(srcIsLDH==false){ + // failPos is always set the index of failure + uprv_syntaxError(b1,failPos, b1Len,parseError); + }else if(b1[0] == HYPHEN){ + // fail position is 0 + uprv_syntaxError(b1,0,b1Len,parseError); + }else{ + // the last index in the source is always length-1 + uprv_syntaxError(b1, (b1Len>0) ? b1Len-1 : b1Len, b1Len,parseError); + } + + goto CLEANUP; + } + } + // Step 4: if the source is ASCII then proceed to step 8 + if(srcIsASCII){ + if(b1Len <= destCapacity){ + u_memmove(dest, b1, b1Len); + reqLength = b1Len; + }else{ + reqLength = b1Len; + goto CLEANUP; + } + }else{ + // step 5 : verify the sequence does not begin with ACE prefix + if(!startsWithPrefix(b1,b1Len)){ + + //step 6: encode the sequence with punycode + + // do not preserve the case flags for now! + // TODO: Preserve the case while implementing the RFE + // caseFlags = (UBool*) uprv_malloc(b1Len * sizeof(UBool)); + // uprv_memset(caseFlags,true,b1Len); + + b2Len = u_strToPunycode(b1,b1Len,b2,b2Capacity,caseFlags, status); + + if(*status == U_BUFFER_OVERFLOW_ERROR){ + // redo processing of string + /* we do not have enough room so grow the buffer*/ + b2 = (char16_t*) uprv_malloc(b2Len * U_SIZEOF_UCHAR); + if(b2 == nullptr){ + *status = U_MEMORY_ALLOCATION_ERROR; + goto CLEANUP; + } + + *status = U_ZERO_ERROR; // reset error + + b2Len = u_strToPunycode(b1,b1Len,b2,b2Len,caseFlags, status); + } + //error bail out + if(U_FAILURE(*status)){ + goto CLEANUP; + } + // TODO : Reconsider while implementing the case preserve RFE + // convert all codepoints to lower case ASCII + // toASCIILower(b2,b2Len); + reqLength = b2Len+ACE_PREFIX_LENGTH; + + if(reqLength > destCapacity){ + *status = U_BUFFER_OVERFLOW_ERROR; + goto CLEANUP; + } + //Step 7: prepend the ACE prefix + u_memcpy(dest, ACE_PREFIX, ACE_PREFIX_LENGTH); + //Step 6: copy the contents in b2 into dest + u_memcpy(dest+ACE_PREFIX_LENGTH, b2, b2Len); + + }else{ + *status = U_IDNA_ACE_PREFIX_ERROR; + //position of failure is 0 + uprv_syntaxError(b1,0,b1Len,parseError); + goto CLEANUP; + } + } + // step 8: verify the length of label + if(reqLength > MAX_LABEL_LENGTH){ + *status = U_IDNA_LABEL_TOO_LONG_ERROR; + } + +CLEANUP: + if(b1 != b1Stack){ + uprv_free(b1); + } + if(b2 != b2Stack){ + uprv_free(b2); + } + uprv_free(caseFlags); + + return u_terminateUChars(dest, destCapacity, reqLength, status); +} + +static int32_t +_internal_toUnicode(const char16_t* src, int32_t srcLength, + char16_t* dest, int32_t destCapacity, + int32_t options, + UStringPrepProfile* nameprep, + UParseError* parseError, + UErrorCode* status) +{ + + //get the options + //UBool useSTD3ASCIIRules = (UBool)((options & UIDNA_USE_STD3_RULES) != 0); + int32_t namePrepOptions = ((options & UIDNA_ALLOW_UNASSIGNED) != 0) ? USPREP_ALLOW_UNASSIGNED: 0; + + // TODO Revisit buffer handling. The label should not be over 63 ASCII characters. ICU4J may need to be updated too. + char16_t b1Stack[MAX_LABEL_BUFFER_SIZE], b2Stack[MAX_LABEL_BUFFER_SIZE], b3Stack[MAX_LABEL_BUFFER_SIZE]; + + //initialize pointers to stack buffers + char16_t *b1 = b1Stack, *b2 = b2Stack, *b1Prime=nullptr, *b3=b3Stack; + int32_t b1Len = 0, b2Len, b1PrimeLen, b3Len, + b1Capacity = MAX_LABEL_BUFFER_SIZE, + b2Capacity = MAX_LABEL_BUFFER_SIZE, + b3Capacity = MAX_LABEL_BUFFER_SIZE, + reqLength=0; + + UBool* caseFlags = nullptr; + + UBool srcIsASCII = true; + /*UBool srcIsLDH = true; + int32_t failPos =0;*/ + + // step 1: find out if all the codepoints in src are ASCII + if(srcLength==-1){ + srcLength = 0; + for(;src[srcLength]!=0;){ + if(src[srcLength]> 0x7f){ + srcIsASCII = false; + }/*else if(isLDHChar(src[srcLength])==false){ + // here we do not assemble surrogates + // since we know that LDH code points + // are in the ASCII range only + srcIsLDH = false; + failPos = srcLength; + }*/ + srcLength++; + } + }else if(srcLength > 0){ + for(int32_t j=0; j 0x7f){ + srcIsASCII = false; + break; + }/*else if(isLDHChar(src[j])==false){ + // here we do not assemble surrogates + // since we know that LDH code points + // are in the ASCII range only + srcIsLDH = false; + failPos = j; + }*/ + } + }else{ + return 0; + } + + if(srcIsASCII == false){ + // step 2: process the string + b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Capacity, namePrepOptions, parseError, status); + if(*status == U_BUFFER_OVERFLOW_ERROR){ + // redo processing of string + /* we do not have enough room so grow the buffer*/ + b1 = (char16_t*) uprv_malloc(b1Len * U_SIZEOF_UCHAR); + if(b1==nullptr){ + *status = U_MEMORY_ALLOCATION_ERROR; + goto CLEANUP; + } + + *status = U_ZERO_ERROR; // reset error + + b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Len, namePrepOptions, parseError, status); + } + //bail out on error + if(U_FAILURE(*status)){ + goto CLEANUP; + } + }else{ + + //just point src to b1 + b1 = (char16_t*) src; + b1Len = srcLength; + } + + // The RFC states that + // + // ToUnicode never fails. If any step fails, then the original input + // is returned immediately in that step. + // + + //step 3: verify ACE Prefix + if(startsWithPrefix(b1,b1Len)){ + + //step 4: Remove the ACE Prefix + b1Prime = b1 + ACE_PREFIX_LENGTH; + b1PrimeLen = b1Len - ACE_PREFIX_LENGTH; + + //step 5: Decode using punycode + b2Len = u_strFromPunycode(b1Prime, b1PrimeLen, b2, b2Capacity, caseFlags,status); + + if(*status == U_BUFFER_OVERFLOW_ERROR){ + // redo processing of string + /* we do not have enough room so grow the buffer*/ + b2 = (char16_t*) uprv_malloc(b2Len * U_SIZEOF_UCHAR); + if(b2==nullptr){ + *status = U_MEMORY_ALLOCATION_ERROR; + goto CLEANUP; + } + + *status = U_ZERO_ERROR; // reset error + + b2Len = u_strFromPunycode(b1Prime, b1PrimeLen, b2, b2Len, caseFlags, status); + } + + + //step 6:Apply toASCII + b3Len = uidna_toASCII(b2, b2Len, b3, b3Capacity, options, parseError, status); + + if(*status == U_BUFFER_OVERFLOW_ERROR){ + // redo processing of string + /* we do not have enough room so grow the buffer*/ + b3 = (char16_t*) uprv_malloc(b3Len * U_SIZEOF_UCHAR); + if(b3==nullptr){ + *status = U_MEMORY_ALLOCATION_ERROR; + goto CLEANUP; + } + + *status = U_ZERO_ERROR; // reset error + + b3Len = uidna_toASCII(b2,b2Len,b3,b3Len,options,parseError, status); + + } + //bail out on error + if(U_FAILURE(*status)){ + goto CLEANUP; + } + + //step 7: verify + if(compareCaseInsensitiveASCII(b1, b1Len, b3, b3Len) !=0){ + // Cause the original to be returned. + *status = U_IDNA_VERIFICATION_ERROR; + goto CLEANUP; + } + + //step 8: return output of step 5 + reqLength = b2Len; + if(b2Len <= destCapacity) { + u_memmove(dest, b2, b2Len); + } + } + else{ + // See the start of this if statement for why this is commented out. + // verify that STD3 ASCII rules are satisfied + /*if(useSTD3ASCIIRules == true){ + if( srcIsLDH == false // source contains some non-LDH characters + || src[0] == HYPHEN || src[srcLength-1] == HYPHEN){ + *status = U_IDNA_STD3_ASCII_RULES_ERROR; + + // populate the parseError struct + if(srcIsLDH==false){ + // failPos is always set the index of failure + uprv_syntaxError(src,failPos, srcLength,parseError); + }else if(src[0] == HYPHEN){ + // fail position is 0 + uprv_syntaxError(src,0,srcLength,parseError); + }else{ + // the last index in the source is always length-1 + uprv_syntaxError(src, (srcLength>0) ? srcLength-1 : srcLength, srcLength,parseError); + } + + goto CLEANUP; + } + }*/ + // just return the source + //copy the source to destination + if(srcLength <= destCapacity){ + u_memmove(dest, src, srcLength); + } + reqLength = srcLength; + } + + +CLEANUP: + + if(b1 != b1Stack && b1!=src){ + uprv_free(b1); + } + if(b2 != b2Stack){ + uprv_free(b2); + } + uprv_free(caseFlags); + + // The RFC states that + // + // ToUnicode never fails. If any step fails, then the original input + // is returned immediately in that step. + // + // So if any step fails lets copy source to destination + if(U_FAILURE(*status)){ + //copy the source to destination + if(dest && srcLength <= destCapacity){ + // srcLength should have already been set earlier. + U_ASSERT(srcLength >= 0); + u_memmove(dest, src, srcLength); + } + reqLength = srcLength; + *status = U_ZERO_ERROR; + } + + return u_terminateUChars(dest, destCapacity, reqLength, status); +} + +U_CAPI int32_t U_EXPORT2 +uidna_toASCII(const char16_t* src, int32_t srcLength, + char16_t* dest, int32_t destCapacity, + int32_t options, + UParseError* parseError, + UErrorCode* status){ + + if(status == nullptr || U_FAILURE(*status)){ + return 0; + } + if((src==nullptr) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){ + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status); + + if(U_FAILURE(*status)){ + return -1; + } + + int32_t retLen = _internal_toASCII(src, srcLength, dest, destCapacity, options, nameprep, parseError, status); + + /* close the profile*/ + usprep_close(nameprep); + + return retLen; +} + +U_CAPI int32_t U_EXPORT2 +uidna_toUnicode(const char16_t* src, int32_t srcLength, + char16_t* dest, int32_t destCapacity, + int32_t options, + UParseError* parseError, + UErrorCode* status){ + + if(status == nullptr || U_FAILURE(*status)){ + return 0; + } + if( (src==nullptr) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){ + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status); + + if(U_FAILURE(*status)){ + return -1; + } + + int32_t retLen = _internal_toUnicode(src, srcLength, dest, destCapacity, options, nameprep, parseError, status); + + usprep_close(nameprep); + + return retLen; +} + + +U_CAPI int32_t U_EXPORT2 +uidna_IDNToASCII( const char16_t *src, int32_t srcLength, + char16_t* dest, int32_t destCapacity, + int32_t options, + UParseError *parseError, + UErrorCode *status){ + + if(status == nullptr || U_FAILURE(*status)){ + return 0; + } + if((src==nullptr) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){ + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + int32_t reqLength = 0; + + UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status); + + if(U_FAILURE(*status)){ + return 0; + } + + //initialize pointers + char16_t *delimiter = (char16_t*)src; + char16_t *labelStart = (char16_t*)src; + char16_t *currentDest = (char16_t*) dest; + int32_t remainingLen = srcLength; + int32_t remainingDestCapacity = destCapacity; + int32_t labelLen = 0, labelReqLength = 0; + UBool done = false; + + + for(;;){ + + labelLen = getNextSeparator(labelStart,remainingLen, &delimiter,&done); + labelReqLength = 0; + if(!(labelLen==0 && done)){// make sure this is not a root label separator. + + labelReqLength = _internal_toASCII( labelStart, labelLen, + currentDest, remainingDestCapacity, + options, nameprep, + parseError, status); + + if(*status == U_BUFFER_OVERFLOW_ERROR){ + + *status = U_ZERO_ERROR; // reset error + remainingDestCapacity = 0; + } + } + + + if(U_FAILURE(*status)){ + break; + } + + reqLength +=labelReqLength; + // adjust the destination pointer + if(labelReqLength < remainingDestCapacity){ + currentDest = currentDest + labelReqLength; + remainingDestCapacity -= labelReqLength; + }else{ + // should never occur + remainingDestCapacity = 0; + } + + if(done){ + break; + } + + // add the label separator + if(remainingDestCapacity > 0){ + *currentDest++ = FULL_STOP; + remainingDestCapacity--; + } + reqLength++; + + labelStart = delimiter; + if(remainingLen >0 ){ + remainingLen = (int32_t)(srcLength - (delimiter - src)); + } + + } + + if(reqLength > MAX_DOMAIN_NAME_LENGTH){ + *status = U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR; + } + + usprep_close(nameprep); + + return u_terminateUChars(dest, destCapacity, reqLength, status); +} + +U_CAPI int32_t U_EXPORT2 +uidna_IDNToUnicode( const char16_t* src, int32_t srcLength, + char16_t* dest, int32_t destCapacity, + int32_t options, + UParseError* parseError, + UErrorCode* status){ + + if(status == nullptr || U_FAILURE(*status)){ + return 0; + } + if((src==nullptr) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){ + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + int32_t reqLength = 0; + + UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status); + + if(U_FAILURE(*status)){ + return 0; + } + + //initialize pointers + char16_t *delimiter = (char16_t*)src; + char16_t *labelStart = (char16_t*)src; + char16_t *currentDest = (char16_t*) dest; + int32_t remainingLen = srcLength; + int32_t remainingDestCapacity = destCapacity; + int32_t labelLen = 0, labelReqLength = 0; + UBool done = false; + + for(;;){ + + labelLen = getNextSeparator(labelStart,remainingLen, &delimiter,&done); + + // The RFC states that + // + // ToUnicode never fails. If any step fails, then the original input + // is returned immediately in that step. + // + // _internal_toUnicode will copy the label. + /*if(labelLen==0 && done==false){ + *status = U_IDNA_ZERO_LENGTH_LABEL_ERROR; + break; + }*/ + + labelReqLength = _internal_toUnicode(labelStart, labelLen, + currentDest, remainingDestCapacity, + options, nameprep, + parseError, status); + + if(*status == U_BUFFER_OVERFLOW_ERROR){ + *status = U_ZERO_ERROR; // reset error + remainingDestCapacity = 0; + } + + if(U_FAILURE(*status)){ + break; + } + + reqLength +=labelReqLength; + // adjust the destination pointer + if(labelReqLength < remainingDestCapacity){ + currentDest = currentDest + labelReqLength; + remainingDestCapacity -= labelReqLength; + }else{ + // should never occur + remainingDestCapacity = 0; + } + + if(done){ + break; + } + + // add the label separator + // Unlike the ToASCII operation we don't normalize the label separators + if(remainingDestCapacity > 0){ + *currentDest++ = *(labelStart + labelLen); + remainingDestCapacity--; + } + reqLength++; + + labelStart = delimiter; + if(remainingLen >0 ){ + remainingLen = (int32_t)(srcLength - (delimiter - src)); + } + + } + + if(reqLength > MAX_DOMAIN_NAME_LENGTH){ + *status = U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR; + } + + usprep_close(nameprep); + + return u_terminateUChars(dest, destCapacity, reqLength, status); +} + +U_CAPI int32_t U_EXPORT2 +uidna_compare( const char16_t *s1, int32_t length1, + const char16_t *s2, int32_t length2, + int32_t options, + UErrorCode* status){ + + if(status == nullptr || U_FAILURE(*status)){ + return -1; + } + + char16_t b1Stack[MAX_IDN_BUFFER_SIZE], b2Stack[MAX_IDN_BUFFER_SIZE]; + char16_t *b1 = b1Stack, *b2 = b2Stack; + int32_t b1Len, b2Len, b1Capacity = MAX_IDN_BUFFER_SIZE, b2Capacity = MAX_IDN_BUFFER_SIZE; + int32_t result=-1; + + UParseError parseError; + + b1Len = uidna_IDNToASCII(s1, length1, b1, b1Capacity, options, &parseError, status); + if(*status == U_BUFFER_OVERFLOW_ERROR){ + // redo processing of string + b1 = (char16_t*) uprv_malloc(b1Len * U_SIZEOF_UCHAR); + if(b1==nullptr){ + *status = U_MEMORY_ALLOCATION_ERROR; + goto CLEANUP; + } + + *status = U_ZERO_ERROR; // reset error + + b1Len = uidna_IDNToASCII(s1,length1,b1,b1Len, options, &parseError, status); + + } + + b2Len = uidna_IDNToASCII(s2,length2, b2,b2Capacity, options, &parseError, status); + if(*status == U_BUFFER_OVERFLOW_ERROR){ + // redo processing of string + b2 = (char16_t*) uprv_malloc(b2Len * U_SIZEOF_UCHAR); + if(b2==nullptr){ + *status = U_MEMORY_ALLOCATION_ERROR; + goto CLEANUP; + } + + *status = U_ZERO_ERROR; // reset error + + b2Len = uidna_IDNToASCII(s2, length2, b2, b2Len, options, &parseError, status); + + } + // when toASCII is applied all label separators are replaced with FULL_STOP + result = compareCaseInsensitiveASCII(b1,b1Len,b2,b2Len); + +CLEANUP: + if(b1 != b1Stack){ + uprv_free(b1); + } + + if(b2 != b2Stack){ + uprv_free(b2); + } + + return result; +} + +#endif /* #if !UCONFIG_NO_IDNA */ diff --git a/deps/icu-small/source/common/uinit.cpp b/deps/icu-small/source/common/uinit.cpp index dc3867b17e5886..2b65f32aa86d3f 100644 --- a/deps/icu-small/source/common/uinit.cpp +++ b/deps/icu-small/source/common/uinit.cpp @@ -1,74 +1,74 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 2001-2015, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* file name: uinit.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2001July05 -* created by: George Rhoten -*/ - -#include "unicode/utypes.h" -#include "unicode/icuplug.h" -#include "unicode/uclean.h" -#include "cmemory.h" -#include "icuplugimp.h" -#include "ucln_cmn.h" -#include "ucnv_io.h" -#include "umutex.h" -#include "utracimp.h" - -U_NAMESPACE_BEGIN - -static UInitOnce gICUInitOnce {}; - -static UBool U_CALLCONV uinit_cleanup() { - gICUInitOnce.reset(); - return true; -} - -static void U_CALLCONV -initData(UErrorCode &status) -{ -#if UCONFIG_ENABLE_PLUGINS - /* initialize plugins */ - uplug_init(&status); -#endif - -#if !UCONFIG_NO_CONVERSION - /* - * 2005-may-02 - * - * ICU4C 3.4 (jitterbug 4497) hardcodes the data for Unicode character - * properties for APIs that want to be fast. - * Therefore, we need not load them here nor check for errors. - * Instead, we load the converter alias table to see if any ICU data - * is available. - * Users should really open the service objects they need and check - * for errors there, to make sure that the actual items they need are - * available. - */ - ucnv_io_countKnownConverters(&status); -#endif - ucln_common_registerCleanup(UCLN_COMMON_UINIT, uinit_cleanup); -} - -U_NAMESPACE_END - -U_NAMESPACE_USE - -/* - * ICU Initialization Function. Need not be called. - */ -U_CAPI void U_EXPORT2 -u_init(UErrorCode *status) { - UTRACE_ENTRY_OC(UTRACE_U_INIT); - umtx_initOnce(gICUInitOnce, &initData, *status); - UTRACE_EXIT_STATUS(*status); -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 2001-2015, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* file name: uinit.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2001July05 +* created by: George Rhoten +*/ + +#include "unicode/utypes.h" +#include "unicode/icuplug.h" +#include "unicode/uclean.h" +#include "cmemory.h" +#include "icuplugimp.h" +#include "ucln_cmn.h" +#include "ucnv_io.h" +#include "umutex.h" +#include "utracimp.h" + +U_NAMESPACE_BEGIN + +static UInitOnce gICUInitOnce {}; + +static UBool U_CALLCONV uinit_cleanup() { + gICUInitOnce.reset(); + return true; +} + +static void U_CALLCONV +initData(UErrorCode &status) +{ +#if UCONFIG_ENABLE_PLUGINS + /* initialize plugins */ + uplug_init(&status); +#endif + +#if !UCONFIG_NO_CONVERSION + /* + * 2005-may-02 + * + * ICU4C 3.4 (jitterbug 4497) hardcodes the data for Unicode character + * properties for APIs that want to be fast. + * Therefore, we need not load them here nor check for errors. + * Instead, we load the converter alias table to see if any ICU data + * is available. + * Users should really open the service objects they need and check + * for errors there, to make sure that the actual items they need are + * available. + */ + ucnv_io_countKnownConverters(&status); +#endif + ucln_common_registerCleanup(UCLN_COMMON_UINIT, uinit_cleanup); +} + +U_NAMESPACE_END + +U_NAMESPACE_USE + +/* + * ICU Initialization Function. Need not be called. + */ +U_CAPI void U_EXPORT2 +u_init(UErrorCode *status) { + UTRACE_ENTRY_OC(UTRACE_U_INIT); + umtx_initOnce(gICUInitOnce, &initData, *status); + UTRACE_EXIT_STATUS(*status); +} diff --git a/deps/icu-small/source/common/uinvchar.cpp b/deps/icu-small/source/common/uinvchar.cpp index ffce3ec158d326..c58de25e638c6c 100644 --- a/deps/icu-small/source/common/uinvchar.cpp +++ b/deps/icu-small/source/common/uinvchar.cpp @@ -1,627 +1,627 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1999-2010, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uinvchar.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:2 -* -* created on: 2004sep14 -* created by: Markus W. Scherer -* -* Functions for handling invariant characters, moved here from putil.c -* for better modularization. -*/ - -#include "unicode/utypes.h" -#include "unicode/ustring.h" -#include "udataswp.h" -#include "cstring.h" -#include "cmemory.h" -#include "uassert.h" -#include "uinvchar.h" - -/* invariant-character handling --------------------------------------------- */ - -/* - * These maps for ASCII to/from EBCDIC map invariant characters (see utypes.h) - * appropriately for most EBCDIC codepages. - * - * They currently also map most other ASCII graphic characters, - * appropriately for codepages 37 and 1047. - * Exceptions: The characters for []^ have different codes in 37 & 1047. - * Both versions are mapped to ASCII. - * - * ASCII 37 1047 - * [ 5B BA AD - * ] 5D BB BD - * ^ 5E B0 5F - * - * There are no mappings for variant characters from Unicode to EBCDIC. - * - * Currently, C0 control codes are also included in these maps. - * Exceptions: S/390 Open Edition swaps LF and NEL codes compared with other - * EBCDIC platforms; both codes (15 and 25) are mapped to ASCII LF (0A), - * but there is no mapping for ASCII LF back to EBCDIC. - * - * ASCII EBCDIC S/390-OE - * LF 0A 25 15 - * NEL 85 15 25 - * - * The maps below explicitly exclude the variant - * control and graphical characters that are in ASCII-based - * codepages at 0x80 and above. - * "No mapping" is expressed by mapping to a 00 byte. - * - * These tables do not establish a converter or a codepage. - */ - -static const uint8_t asciiFromEbcdic[256]={ - 0x00, 0x01, 0x02, 0x03, 0x00, 0x09, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x00, 0x0a, 0x08, 0x00, 0x18, 0x19, 0x00, 0x00, 0x1c, 0x1d, 0x1e, 0x1f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x17, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x07, - 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x14, 0x15, 0x00, 0x1a, - - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, - 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, - 0x2d, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, - - 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, - 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x5d, 0x00, 0x5d, 0x00, 0x00, - - 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5c, 0x00, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static const uint8_t ebcdicFromAscii[256]={ - 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x00, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, - 0x40, 0x00, 0x7f, 0x00, 0x00, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, - - 0x00, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, - 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x6d, - 0x00, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x07, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* Same as asciiFromEbcdic[] except maps all letters to lowercase. */ -static const uint8_t lowercaseAsciiFromEbcdic[256]={ - 0x00, 0x01, 0x02, 0x03, 0x00, 0x09, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x00, 0x0a, 0x08, 0x00, 0x18, 0x19, 0x00, 0x00, 0x1c, 0x1d, 0x1e, 0x1f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x17, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x07, - 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x14, 0x15, 0x00, 0x1a, - - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, - 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, - 0x2d, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, - - 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, - 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x5d, 0x00, 0x5d, 0x00, 0x00, - - 0x7b, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7d, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7c, 0x00, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -/* - * Bit sets indicating which characters of the ASCII repertoire - * (by ASCII/Unicode code) are "invariant". - * See utypes.h for more details. - * - * As invariant are considered the characters of the ASCII repertoire except - * for the following: - * 21 '!' - * 23 '#' - * 24 '$' - * - * 40 '@' - * - * 5b '[' - * 5c '\' - * 5d ']' - * 5e '^' - * - * 60 '`' - * - * 7b '{' - * 7c '|' - * 7d '}' - * 7e '~' - */ -static const uint32_t invariantChars[4]={ - 0xfffffbff, /* 00..1f but not 0a */ - 0xffffffe5, /* 20..3f but not 21 23 24 */ - 0x87fffffe, /* 40..5f but not 40 5b..5e */ - 0x87fffffe /* 60..7f but not 60 7b..7e */ -}; - -/* - * test unsigned types (or values known to be non-negative) for invariant characters, - * tests ASCII-family character values - */ -#define UCHAR_IS_INVARIANT(c) (((c)<=0x7f) && (invariantChars[(c)>>5]&((uint32_t)1<<((c)&0x1f)))!=0) - -/* test signed types for invariant characters, adds test for positive values */ -#define SCHAR_IS_INVARIANT(c) ((0<=(c)) && UCHAR_IS_INVARIANT(c)) - -#if U_CHARSET_FAMILY==U_ASCII_FAMILY -#define CHAR_TO_UCHAR(c) c -#define UCHAR_TO_CHAR(c) c -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY -#define CHAR_TO_UCHAR(u) asciiFromEbcdic[u] -#define UCHAR_TO_CHAR(u) ebcdicFromAscii[u] -#else -# error U_CHARSET_FAMILY is not valid -#endif - - -U_CAPI void U_EXPORT2 -u_charsToUChars(const char *cs, UChar *us, int32_t length) { - UChar u; - uint8_t c; - - /* - * Allow the entire ASCII repertoire to be mapped _to_ Unicode. - * For EBCDIC systems, this works for characters with codes from - * codepages 37 and 1047 or compatible. - */ - while(length>0) { - c=(uint8_t)(*cs++); - u=(UChar)CHAR_TO_UCHAR(c); - U_ASSERT((u!=0 || c==0)); /* only invariant chars converted? */ - *us++=u; - --length; - } -} - -U_CAPI void U_EXPORT2 -u_UCharsToChars(const UChar *us, char *cs, int32_t length) { - UChar u; - - while(length>0) { - u=*us++; - if(!UCHAR_IS_INVARIANT(u)) { - U_ASSERT(false); /* Variant characters were used. These are not portable in ICU. */ - u=0; - } - *cs++=(char)UCHAR_TO_CHAR(u); - --length; - } -} - -U_CAPI UBool U_EXPORT2 -uprv_isInvariantString(const char *s, int32_t length) { - uint8_t c; - - for(;;) { - if(length<0) { - /* NUL-terminated */ - c=(uint8_t)*s++; - if(c==0) { - break; - } - } else { - /* count length */ - if(length==0) { - break; - } - --length; - c=(uint8_t)*s++; - if(c==0) { - continue; /* NUL is invariant */ - } - } - /* c!=0 now, one branch below checks c==0 for variant characters */ - - /* - * no assertions here because these functions are legitimately called - * for strings with variant characters - */ -#if U_CHARSET_FAMILY==U_ASCII_FAMILY - if(!UCHAR_IS_INVARIANT(c)) { - return false; /* found a variant char */ - } -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY - c=CHAR_TO_UCHAR(c); - if(c==0 || !UCHAR_IS_INVARIANT(c)) { - return false; /* found a variant char */ - } -#else -# error U_CHARSET_FAMILY is not valid -#endif - } - return true; -} - -U_CAPI UBool U_EXPORT2 -uprv_isInvariantUString(const UChar *s, int32_t length) { - UChar c; - - for(;;) { - if(length<0) { - /* NUL-terminated */ - c=*s++; - if(c==0) { - break; - } - } else { - /* count length */ - if(length==0) { - break; - } - --length; - c=*s++; - } - - /* - * no assertions here because these functions are legitimately called - * for strings with variant characters - */ - if(!UCHAR_IS_INVARIANT(c)) { - return false; /* found a variant char */ - } - } - return true; -} - -/* UDataSwapFn implementations used in udataswp.c ------- */ - -/* convert ASCII to EBCDIC and verify that all characters are invariant */ -U_CAPI int32_t U_EXPORT2 -uprv_ebcdicFromAscii(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const uint8_t *s; - uint8_t *t; - uint8_t c; - - int32_t count; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || length<0 || (length>0 && outData==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* setup and swapping */ - s=(const uint8_t *)inData; - t=(uint8_t *)outData; - count=length; - while(count>0) { - c=*s++; - if(!UCHAR_IS_INVARIANT(c)) { - udata_printError(ds, "uprv_ebcdicFromAscii() string[%d] contains a variant character in position %d\n", - length, length-count); - *pErrorCode=U_INVALID_CHAR_FOUND; - return 0; - } - *t++=ebcdicFromAscii[c]; - --count; - } - - return length; -} - -/* this function only checks and copies ASCII strings without conversion */ -U_CFUNC int32_t -uprv_copyAscii(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const uint8_t *s; - uint8_t c; - - int32_t count; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || length<0 || (length>0 && outData==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* setup and checking */ - s=(const uint8_t *)inData; - count=length; - while(count>0) { - c=*s++; - if(!UCHAR_IS_INVARIANT(c)) { - udata_printError(ds, "uprv_copyFromAscii() string[%d] contains a variant character in position %d\n", - length, length-count); - *pErrorCode=U_INVALID_CHAR_FOUND; - return 0; - } - --count; - } - - if(length>0 && inData!=outData) { - uprv_memcpy(outData, inData, length); - } - - return length; -} - -/* convert EBCDIC to ASCII and verify that all characters are invariant */ -U_CFUNC int32_t -uprv_asciiFromEbcdic(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const uint8_t *s; - uint8_t *t; - uint8_t c; - - int32_t count; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || length<0 || (length>0 && outData==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* setup and swapping */ - s=(const uint8_t *)inData; - t=(uint8_t *)outData; - count=length; - while(count>0) { - c=*s++; - if(c!=0 && ((c=asciiFromEbcdic[c])==0 || !UCHAR_IS_INVARIANT(c))) { - udata_printError(ds, "uprv_asciiFromEbcdic() string[%d] contains a variant character in position %d\n", - length, length-count); - *pErrorCode=U_INVALID_CHAR_FOUND; - return 0; - } - *t++=c; - --count; - } - - return length; -} - -/* this function only checks and copies EBCDIC strings without conversion */ -U_CFUNC int32_t -uprv_copyEbcdic(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const uint8_t *s; - uint8_t c; - - int32_t count; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || length<0 || (length>0 && outData==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* setup and checking */ - s=(const uint8_t *)inData; - count=length; - while(count>0) { - c=*s++; - if(c!=0 && ((c=asciiFromEbcdic[c])==0 || !UCHAR_IS_INVARIANT(c))) { - udata_printError(ds, "uprv_copyEbcdic() string[%] contains a variant character in position %d\n", - length, length-count); - *pErrorCode=U_INVALID_CHAR_FOUND; - return 0; - } - --count; - } - - if(length>0 && inData!=outData) { - uprv_memcpy(outData, inData, length); - } - - return length; -} - -U_CFUNC UBool -uprv_isEbcdicAtSign(char c) { - static const uint8_t ebcdicAtSigns[] = { - 0x7C, 0x44, 0x66, 0x80, 0xAC, 0xAE, 0xAF, 0xB5, 0xEC, 0xEF, 0x00 }; - return c != 0 && uprv_strchr((const char *)ebcdicAtSigns, c) != nullptr; -} - -/* compare invariant strings; variant characters compare less than others and unlike each other */ -U_CFUNC int32_t -uprv_compareInvAscii(const UDataSwapper *ds, - const char *outString, int32_t outLength, - const UChar *localString, int32_t localLength) { - (void)ds; - int32_t minLength; - UChar32 c1, c2; - uint8_t c; - - if(outString==NULL || outLength<-1 || localString==NULL || localLength<-1) { - return 0; - } - - if(outLength<0) { - outLength=(int32_t)uprv_strlen(outString); - } - if(localLength<0) { - localLength=u_strlen(localString); - } - - minLength= outLength0) { - c=(uint8_t)*outString++; - if(UCHAR_IS_INVARIANT(c)) { - c1=c; - } else { - c1=-1; - } - - c2=*localString++; - if(!UCHAR_IS_INVARIANT(c2)) { - c2=-2; - } - - if((c1-=c2)!=0) { - return c1; - } - - --minLength; - } - - /* strings start with same prefix, compare lengths */ - return outLength-localLength; -} - -U_CFUNC int32_t -uprv_compareInvEbcdic(const UDataSwapper *ds, - const char *outString, int32_t outLength, - const UChar *localString, int32_t localLength) { - (void)ds; - int32_t minLength; - UChar32 c1, c2; - uint8_t c; - - if(outString==NULL || outLength<-1 || localString==NULL || localLength<-1) { - return 0; - } - - if(outLength<0) { - outLength=(int32_t)uprv_strlen(outString); - } - if(localLength<0) { - localLength=u_strlen(localString); - } - - minLength= outLength0) { - c=(uint8_t)*outString++; - if(c==0) { - c1=0; - } else if((c1=asciiFromEbcdic[c])!=0 && UCHAR_IS_INVARIANT(c1)) { - /* c1 is set */ - } else { - c1=-1; - } - - c2=*localString++; - if(!UCHAR_IS_INVARIANT(c2)) { - c2=-2; - } - - if((c1-=c2)!=0) { - return c1; - } - - --minLength; - } - - /* strings start with same prefix, compare lengths */ - return outLength-localLength; -} - -U_CAPI int32_t U_EXPORT2 -uprv_compareInvEbcdicAsAscii(const char *s1, const char *s2) { - int32_t c1, c2; - - for(;; ++s1, ++s2) { - c1=(uint8_t)*s1; - c2=(uint8_t)*s2; - if(c1!=c2) { - if(c1!=0 && ((c1=asciiFromEbcdic[c1])==0 || !UCHAR_IS_INVARIANT(c1))) { - c1=-(int32_t)(uint8_t)*s1; - } - if(c2!=0 && ((c2=asciiFromEbcdic[c2])==0 || !UCHAR_IS_INVARIANT(c2))) { - c2=-(int32_t)(uint8_t)*s2; - } - return c1-c2; - } else if(c1==0) { - return 0; - } - } -} - -U_CAPI char U_EXPORT2 -uprv_ebcdicToAscii(char c) { - return (char)asciiFromEbcdic[(uint8_t)c]; -} - -U_CAPI char U_EXPORT2 -uprv_ebcdicToLowercaseAscii(char c) { - return (char)lowercaseAsciiFromEbcdic[(uint8_t)c]; -} - -U_CAPI uint8_t* U_EXPORT2 -uprv_aestrncpy(uint8_t *dst, const uint8_t *src, int32_t n) -{ - uint8_t *orig_dst = dst; - - if(n==-1) { - n = static_cast(uprv_strlen((const char*)src)+1); /* copy NUL */ - } - /* copy non-null */ - while(*src && n>0) { - *(dst++) = asciiFromEbcdic[*(src++)]; - n--; - } - /* pad */ - while(n>0) { - *(dst++) = 0; - n--; - } - return orig_dst; -} - -U_CAPI uint8_t* U_EXPORT2 -uprv_eastrncpy(uint8_t *dst, const uint8_t *src, int32_t n) -{ - uint8_t *orig_dst = dst; - - if(n==-1) { - n = static_cast(uprv_strlen((const char*)src)+1); /* copy NUL */ - } - /* copy non-null */ - while(*src && n>0) { - char ch = ebcdicFromAscii[*(src++)]; - if(ch == 0) { - ch = ebcdicFromAscii[0x3f]; /* questionmark (subchar) */ - } - *(dst++) = ch; - n--; - } - /* pad */ - while(n>0) { - *(dst++) = 0; - n--; - } - return orig_dst; -} - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1999-2010, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uinvchar.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:2 +* +* created on: 2004sep14 +* created by: Markus W. Scherer +* +* Functions for handling invariant characters, moved here from putil.c +* for better modularization. +*/ + +#include "unicode/utypes.h" +#include "unicode/ustring.h" +#include "udataswp.h" +#include "cstring.h" +#include "cmemory.h" +#include "uassert.h" +#include "uinvchar.h" + +/* invariant-character handling --------------------------------------------- */ + +/* + * These maps for ASCII to/from EBCDIC map invariant characters (see utypes.h) + * appropriately for most EBCDIC codepages. + * + * They currently also map most other ASCII graphic characters, + * appropriately for codepages 37 and 1047. + * Exceptions: The characters for []^ have different codes in 37 & 1047. + * Both versions are mapped to ASCII. + * + * ASCII 37 1047 + * [ 5B BA AD + * ] 5D BB BD + * ^ 5E B0 5F + * + * There are no mappings for variant characters from Unicode to EBCDIC. + * + * Currently, C0 control codes are also included in these maps. + * Exceptions: S/390 Open Edition swaps LF and NEL codes compared with other + * EBCDIC platforms; both codes (15 and 25) are mapped to ASCII LF (0A), + * but there is no mapping for ASCII LF back to EBCDIC. + * + * ASCII EBCDIC S/390-OE + * LF 0A 25 15 + * NEL 85 15 25 + * + * The maps below explicitly exclude the variant + * control and graphical characters that are in ASCII-based + * codepages at 0x80 and above. + * "No mapping" is expressed by mapping to a 00 byte. + * + * These tables do not establish a converter or a codepage. + */ + +static const uint8_t asciiFromEbcdic[256]={ + 0x00, 0x01, 0x02, 0x03, 0x00, 0x09, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x00, 0x0a, 0x08, 0x00, 0x18, 0x19, 0x00, 0x00, 0x1c, 0x1d, 0x1e, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x17, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x07, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x14, 0x15, 0x00, 0x1a, + + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, + 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, + 0x2d, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, + + 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, + 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x5d, 0x00, 0x5d, 0x00, 0x00, + + 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t ebcdicFromAscii[256]={ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, 0x16, 0x05, 0x00, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + 0x40, 0x00, 0x7f, 0x00, 0x00, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + + 0x00, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x6d, + 0x00, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x07, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* Same as asciiFromEbcdic[] except maps all letters to lowercase. */ +static const uint8_t lowercaseAsciiFromEbcdic[256]={ + 0x00, 0x01, 0x02, 0x03, 0x00, 0x09, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x00, 0x0a, 0x08, 0x00, 0x18, 0x19, 0x00, 0x00, 0x1c, 0x1d, 0x1e, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x17, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x07, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x14, 0x15, 0x00, 0x1a, + + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, + 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, + 0x2d, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, + + 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, + 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x5d, 0x00, 0x5d, 0x00, 0x00, + + 0x7b, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* + * Bit sets indicating which characters of the ASCII repertoire + * (by ASCII/Unicode code) are "invariant". + * See utypes.h for more details. + * + * As invariant are considered the characters of the ASCII repertoire except + * for the following: + * 21 '!' + * 23 '#' + * 24 '$' + * + * 40 '@' + * + * 5b '[' + * 5c '\' + * 5d ']' + * 5e '^' + * + * 60 '`' + * + * 7b '{' + * 7c '|' + * 7d '}' + * 7e '~' + */ +static const uint32_t invariantChars[4]={ + 0xfffffbff, /* 00..1f but not 0a */ + 0xffffffe5, /* 20..3f but not 21 23 24 */ + 0x87fffffe, /* 40..5f but not 40 5b..5e */ + 0x87fffffe /* 60..7f but not 60 7b..7e */ +}; + +/* + * test unsigned types (or values known to be non-negative) for invariant characters, + * tests ASCII-family character values + */ +#define UCHAR_IS_INVARIANT(c) (((c)<=0x7f) && (invariantChars[(c)>>5]&((uint32_t)1<<((c)&0x1f)))!=0) + +/* test signed types for invariant characters, adds test for positive values */ +#define SCHAR_IS_INVARIANT(c) ((0<=(c)) && UCHAR_IS_INVARIANT(c)) + +#if U_CHARSET_FAMILY==U_ASCII_FAMILY +#define CHAR_TO_UCHAR(c) c +#define UCHAR_TO_CHAR(c) c +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY +#define CHAR_TO_UCHAR(u) asciiFromEbcdic[u] +#define UCHAR_TO_CHAR(u) ebcdicFromAscii[u] +#else +# error U_CHARSET_FAMILY is not valid +#endif + + +U_CAPI void U_EXPORT2 +u_charsToUChars(const char *cs, char16_t *us, int32_t length) { + char16_t u; + uint8_t c; + + /* + * Allow the entire ASCII repertoire to be mapped _to_ Unicode. + * For EBCDIC systems, this works for characters with codes from + * codepages 37 and 1047 or compatible. + */ + while(length>0) { + c=(uint8_t)(*cs++); + u=(char16_t)CHAR_TO_UCHAR(c); + U_ASSERT((u!=0 || c==0)); /* only invariant chars converted? */ + *us++=u; + --length; + } +} + +U_CAPI void U_EXPORT2 +u_UCharsToChars(const char16_t *us, char *cs, int32_t length) { + char16_t u; + + while(length>0) { + u=*us++; + if(!UCHAR_IS_INVARIANT(u)) { + U_ASSERT(false); /* Variant characters were used. These are not portable in ICU. */ + u=0; + } + *cs++=(char)UCHAR_TO_CHAR(u); + --length; + } +} + +U_CAPI UBool U_EXPORT2 +uprv_isInvariantString(const char *s, int32_t length) { + uint8_t c; + + for(;;) { + if(length<0) { + /* NUL-terminated */ + c=(uint8_t)*s++; + if(c==0) { + break; + } + } else { + /* count length */ + if(length==0) { + break; + } + --length; + c=(uint8_t)*s++; + if(c==0) { + continue; /* NUL is invariant */ + } + } + /* c!=0 now, one branch below checks c==0 for variant characters */ + + /* + * no assertions here because these functions are legitimately called + * for strings with variant characters + */ +#if U_CHARSET_FAMILY==U_ASCII_FAMILY + if(!UCHAR_IS_INVARIANT(c)) { + return false; /* found a variant char */ + } +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY + c=CHAR_TO_UCHAR(c); + if(c==0 || !UCHAR_IS_INVARIANT(c)) { + return false; /* found a variant char */ + } +#else +# error U_CHARSET_FAMILY is not valid +#endif + } + return true; +} + +U_CAPI UBool U_EXPORT2 +uprv_isInvariantUString(const char16_t *s, int32_t length) { + char16_t c; + + for(;;) { + if(length<0) { + /* NUL-terminated */ + c=*s++; + if(c==0) { + break; + } + } else { + /* count length */ + if(length==0) { + break; + } + --length; + c=*s++; + } + + /* + * no assertions here because these functions are legitimately called + * for strings with variant characters + */ + if(!UCHAR_IS_INVARIANT(c)) { + return false; /* found a variant char */ + } + } + return true; +} + +/* UDataSwapFn implementations used in udataswp.c ------- */ + +/* convert ASCII to EBCDIC and verify that all characters are invariant */ +U_CAPI int32_t U_EXPORT2 +uprv_ebcdicFromAscii(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const uint8_t *s; + uint8_t *t; + uint8_t c; + + int32_t count; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<0 || (length>0 && outData==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* setup and swapping */ + s=(const uint8_t *)inData; + t=(uint8_t *)outData; + count=length; + while(count>0) { + c=*s++; + if(!UCHAR_IS_INVARIANT(c)) { + udata_printError(ds, "uprv_ebcdicFromAscii() string[%d] contains a variant character in position %d\n", + length, length-count); + *pErrorCode=U_INVALID_CHAR_FOUND; + return 0; + } + *t++=ebcdicFromAscii[c]; + --count; + } + + return length; +} + +/* this function only checks and copies ASCII strings without conversion */ +U_CFUNC int32_t +uprv_copyAscii(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const uint8_t *s; + uint8_t c; + + int32_t count; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<0 || (length>0 && outData==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* setup and checking */ + s=(const uint8_t *)inData; + count=length; + while(count>0) { + c=*s++; + if(!UCHAR_IS_INVARIANT(c)) { + udata_printError(ds, "uprv_copyFromAscii() string[%d] contains a variant character in position %d\n", + length, length-count); + *pErrorCode=U_INVALID_CHAR_FOUND; + return 0; + } + --count; + } + + if(length>0 && inData!=outData) { + uprv_memcpy(outData, inData, length); + } + + return length; +} + +/* convert EBCDIC to ASCII and verify that all characters are invariant */ +U_CFUNC int32_t +uprv_asciiFromEbcdic(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const uint8_t *s; + uint8_t *t; + uint8_t c; + + int32_t count; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<0 || (length>0 && outData==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* setup and swapping */ + s=(const uint8_t *)inData; + t=(uint8_t *)outData; + count=length; + while(count>0) { + c=*s++; + if(c!=0 && ((c=asciiFromEbcdic[c])==0 || !UCHAR_IS_INVARIANT(c))) { + udata_printError(ds, "uprv_asciiFromEbcdic() string[%d] contains a variant character in position %d\n", + length, length-count); + *pErrorCode=U_INVALID_CHAR_FOUND; + return 0; + } + *t++=c; + --count; + } + + return length; +} + +/* this function only checks and copies EBCDIC strings without conversion */ +U_CFUNC int32_t +uprv_copyEbcdic(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const uint8_t *s; + uint8_t c; + + int32_t count; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || length<0 || (length>0 && outData==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* setup and checking */ + s=(const uint8_t *)inData; + count=length; + while(count>0) { + c=*s++; + if(c!=0 && ((c=asciiFromEbcdic[c])==0 || !UCHAR_IS_INVARIANT(c))) { + udata_printError(ds, "uprv_copyEbcdic() string[%] contains a variant character in position %d\n", + length, length-count); + *pErrorCode=U_INVALID_CHAR_FOUND; + return 0; + } + --count; + } + + if(length>0 && inData!=outData) { + uprv_memcpy(outData, inData, length); + } + + return length; +} + +U_CFUNC UBool +uprv_isEbcdicAtSign(char c) { + static const uint8_t ebcdicAtSigns[] = { + 0x7C, 0x44, 0x66, 0x80, 0xAC, 0xAE, 0xAF, 0xB5, 0xEC, 0xEF, 0x00 }; + return c != 0 && uprv_strchr((const char *)ebcdicAtSigns, c) != nullptr; +} + +/* compare invariant strings; variant characters compare less than others and unlike each other */ +U_CFUNC int32_t +uprv_compareInvAscii(const UDataSwapper *ds, + const char *outString, int32_t outLength, + const char16_t *localString, int32_t localLength) { + (void)ds; + int32_t minLength; + UChar32 c1, c2; + uint8_t c; + + if(outString==nullptr || outLength<-1 || localString==nullptr || localLength<-1) { + return 0; + } + + if(outLength<0) { + outLength=(int32_t)uprv_strlen(outString); + } + if(localLength<0) { + localLength=u_strlen(localString); + } + + minLength= outLength0) { + c=(uint8_t)*outString++; + if(UCHAR_IS_INVARIANT(c)) { + c1=c; + } else { + c1=-1; + } + + c2=*localString++; + if(!UCHAR_IS_INVARIANT(c2)) { + c2=-2; + } + + if((c1-=c2)!=0) { + return c1; + } + + --minLength; + } + + /* strings start with same prefix, compare lengths */ + return outLength-localLength; +} + +U_CFUNC int32_t +uprv_compareInvEbcdic(const UDataSwapper *ds, + const char *outString, int32_t outLength, + const char16_t *localString, int32_t localLength) { + (void)ds; + int32_t minLength; + UChar32 c1, c2; + uint8_t c; + + if(outString==nullptr || outLength<-1 || localString==nullptr || localLength<-1) { + return 0; + } + + if(outLength<0) { + outLength=(int32_t)uprv_strlen(outString); + } + if(localLength<0) { + localLength=u_strlen(localString); + } + + minLength= outLength0) { + c=(uint8_t)*outString++; + if(c==0) { + c1=0; + } else if((c1=asciiFromEbcdic[c])!=0 && UCHAR_IS_INVARIANT(c1)) { + /* c1 is set */ + } else { + c1=-1; + } + + c2=*localString++; + if(!UCHAR_IS_INVARIANT(c2)) { + c2=-2; + } + + if((c1-=c2)!=0) { + return c1; + } + + --minLength; + } + + /* strings start with same prefix, compare lengths */ + return outLength-localLength; +} + +U_CAPI int32_t U_EXPORT2 +uprv_compareInvEbcdicAsAscii(const char *s1, const char *s2) { + int32_t c1, c2; + + for(;; ++s1, ++s2) { + c1=(uint8_t)*s1; + c2=(uint8_t)*s2; + if(c1!=c2) { + if(c1!=0 && ((c1=asciiFromEbcdic[c1])==0 || !UCHAR_IS_INVARIANT(c1))) { + c1=-(int32_t)(uint8_t)*s1; + } + if(c2!=0 && ((c2=asciiFromEbcdic[c2])==0 || !UCHAR_IS_INVARIANT(c2))) { + c2=-(int32_t)(uint8_t)*s2; + } + return c1-c2; + } else if(c1==0) { + return 0; + } + } +} + +U_CAPI char U_EXPORT2 +uprv_ebcdicToAscii(char c) { + return (char)asciiFromEbcdic[(uint8_t)c]; +} + +U_CAPI char U_EXPORT2 +uprv_ebcdicToLowercaseAscii(char c) { + return (char)lowercaseAsciiFromEbcdic[(uint8_t)c]; +} + +U_CAPI uint8_t* U_EXPORT2 +uprv_aestrncpy(uint8_t *dst, const uint8_t *src, int32_t n) +{ + uint8_t *orig_dst = dst; + + if(n==-1) { + n = static_cast(uprv_strlen((const char*)src)+1); /* copy NUL */ + } + /* copy non-null */ + while(*src && n>0) { + *(dst++) = asciiFromEbcdic[*(src++)]; + n--; + } + /* pad */ + while(n>0) { + *(dst++) = 0; + n--; + } + return orig_dst; +} + +U_CAPI uint8_t* U_EXPORT2 +uprv_eastrncpy(uint8_t *dst, const uint8_t *src, int32_t n) +{ + uint8_t *orig_dst = dst; + + if(n==-1) { + n = static_cast(uprv_strlen((const char*)src)+1); /* copy NUL */ + } + /* copy non-null */ + while(*src && n>0) { + char ch = ebcdicFromAscii[*(src++)]; + if(ch == 0) { + ch = ebcdicFromAscii[0x3f]; /* questionmark (subchar) */ + } + *(dst++) = ch; + n--; + } + /* pad */ + while(n>0) { + *(dst++) = 0; + n--; + } + return orig_dst; +} + diff --git a/deps/icu-small/source/common/uinvchar.h b/deps/icu-small/source/common/uinvchar.h index 9b7a9bd114172e..a986dab9f5363b 100644 --- a/deps/icu-small/source/common/uinvchar.h +++ b/deps/icu-small/source/common/uinvchar.h @@ -1,219 +1,219 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1999-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uinvchar.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:2 -* -* created on: 2004sep14 -* created by: Markus W. Scherer -* -* Definitions for handling invariant characters, moved here from putil.c -* for better modularization. -*/ - -#ifndef __UINVCHAR_H__ -#define __UINVCHAR_H__ - -#include "unicode/utypes.h" -#ifdef __cplusplus -#include "unicode/unistr.h" -#endif - -/** - * Check if a char string only contains invariant characters. - * See utypes.h for details. - * - * @param s Input string pointer. - * @param length Length of the string, can be -1 if NUL-terminated. - * @return true if s contains only invariant characters. - * - * @internal (ICU 2.8) - */ -U_CAPI UBool U_EXPORT2 -uprv_isInvariantString(const char *s, int32_t length); - -/** - * Check if a Unicode string only contains invariant characters. - * See utypes.h for details. - * - * @param s Input string pointer. - * @param length Length of the string, can be -1 if NUL-terminated. - * @return true if s contains only invariant characters. - * - * @internal (ICU 2.8) - */ -U_CAPI UBool U_EXPORT2 -uprv_isInvariantUString(const UChar *s, int32_t length); - -/** - * \def U_UPPER_ORDINAL - * Get the ordinal number of an uppercase invariant character - * @internal - */ -#if U_CHARSET_FAMILY==U_ASCII_FAMILY -# define U_UPPER_ORDINAL(x) ((x)-'A') -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY -# define U_UPPER_ORDINAL(x) (((x) < 'J') ? ((x)-'A') : \ - (((x) < 'S') ? ((x)-'J'+9) : \ - ((x)-'S'+18))) -#else -# error Unknown charset family! -#endif - -#ifdef __cplusplus - -U_NAMESPACE_BEGIN - -/** - * Like U_UPPER_ORDINAL(x) but with validation. - * Returns 0..25 for A..Z else a value outside 0..25. - */ -inline int32_t uprv_upperOrdinal(int32_t c) { -#if U_CHARSET_FAMILY==U_ASCII_FAMILY - return c - 'A'; -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY - // EBCDIC: A-Z (26 letters) is split into three ranges A-I (9 letters), J-R (9), S-Z (8). - // https://en.wikipedia.org/wiki/EBCDIC_037#Codepage_layout - if (c <= 'I') { return c - 'A'; } // A-I --> 0-8 - if (c < 'J') { return -1; } - if (c <= 'R') { return c - 'J' + 9; } // J-R --> 9..17 - if (c < 'S') { return -1; } - return c - 'S' + 18; // S-Z --> 18..25 -#else -# error Unknown charset family! -#endif -} - -// Like U_UPPER_ORDINAL(x) but for lowercase and with validation. -// Returns 0..25 for a..z else a value outside 0..25. -inline int32_t uprv_lowerOrdinal(int32_t c) { -#if U_CHARSET_FAMILY==U_ASCII_FAMILY - return c - 'a'; -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY - // EBCDIC: a-z (26 letters) is split into three ranges a-i (9 letters), j-r (9), s-z (8). - // https://en.wikipedia.org/wiki/EBCDIC_037#Codepage_layout - if (c <= 'i') { return c - 'a'; } // a-i --> 0-8 - if (c < 'j') { return -1; } - if (c <= 'r') { return c - 'j' + 9; } // j-r --> 9..17 - if (c < 's') { return -1; } - return c - 's' + 18; // s-z --> 18..25 -#else -# error Unknown charset family! -#endif -} - -U_NAMESPACE_END - -#endif - -/** - * Returns true if c == '@' is possible. - * The @ sign is variant, and the @ sign used on one - * EBCDIC machine won't be compiled the same way on other EBCDIC based machines. - * @internal - */ -U_CFUNC UBool -uprv_isEbcdicAtSign(char c); - -/** - * \def uprv_isAtSign - * Returns true if c == '@' is possible. - * For ASCII, checks for exactly '@'. For EBCDIC, calls uprv_isEbcdicAtSign(). - * @internal - */ -#if U_CHARSET_FAMILY==U_ASCII_FAMILY -# define uprv_isAtSign(c) ((c)=='@') -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY -# define uprv_isAtSign(c) uprv_isEbcdicAtSign(c) -#else -# error Unknown charset family! -#endif - -/** - * Compare two EBCDIC invariant-character strings in ASCII order. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -uprv_compareInvEbcdicAsAscii(const char *s1, const char *s2); - -/** - * \def uprv_compareInvCharsAsAscii - * Compare two invariant-character strings in ASCII order. - * @internal - */ -#if U_CHARSET_FAMILY==U_ASCII_FAMILY -# define uprv_compareInvCharsAsAscii(s1, s2) uprv_strcmp(s1, s2) -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY -# define uprv_compareInvCharsAsAscii(s1, s2) uprv_compareInvEbcdicAsAscii(s1, s2) -#else -# error Unknown charset family! -#endif - -/** - * Converts an EBCDIC invariant character to ASCII. - * @internal - */ -U_CAPI char U_EXPORT2 -uprv_ebcdicToAscii(char c); - -/** - * \def uprv_invCharToAscii - * Converts an invariant character to ASCII. - * @internal - */ -#if U_CHARSET_FAMILY==U_ASCII_FAMILY -# define uprv_invCharToAscii(c) (c) -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY -# define uprv_invCharToAscii(c) uprv_ebcdicToAscii(c) -#else -# error Unknown charset family! -#endif - -/** - * Converts an EBCDIC invariant character to lowercase ASCII. - * @internal - */ -U_CAPI char U_EXPORT2 -uprv_ebcdicToLowercaseAscii(char c); - -/** - * \def uprv_invCharToLowercaseAscii - * Converts an invariant character to lowercase ASCII. - * @internal - */ -#if U_CHARSET_FAMILY==U_ASCII_FAMILY -# define uprv_invCharToLowercaseAscii uprv_asciitolower -#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY -# define uprv_invCharToLowercaseAscii uprv_ebcdicToLowercaseAscii -#else -# error Unknown charset family! -#endif - -/** - * Copy EBCDIC to ASCII - * @internal - * @see uprv_strncpy - */ -U_CAPI uint8_t* U_EXPORT2 -uprv_aestrncpy(uint8_t *dst, const uint8_t *src, int32_t n); - - -/** - * Copy ASCII to EBCDIC - * @internal - * @see uprv_strncpy - */ -U_CAPI uint8_t* U_EXPORT2 -uprv_eastrncpy(uint8_t *dst, const uint8_t *src, int32_t n); - - - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1999-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uinvchar.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:2 +* +* created on: 2004sep14 +* created by: Markus W. Scherer +* +* Definitions for handling invariant characters, moved here from putil.c +* for better modularization. +*/ + +#ifndef __UINVCHAR_H__ +#define __UINVCHAR_H__ + +#include "unicode/utypes.h" +#ifdef __cplusplus +#include "unicode/unistr.h" +#endif + +/** + * Check if a char string only contains invariant characters. + * See utypes.h for details. + * + * @param s Input string pointer. + * @param length Length of the string, can be -1 if NUL-terminated. + * @return true if s contains only invariant characters. + * + * @internal (ICU 2.8) + */ +U_CAPI UBool U_EXPORT2 +uprv_isInvariantString(const char *s, int32_t length); + +/** + * Check if a Unicode string only contains invariant characters. + * See utypes.h for details. + * + * @param s Input string pointer. + * @param length Length of the string, can be -1 if NUL-terminated. + * @return true if s contains only invariant characters. + * + * @internal (ICU 2.8) + */ +U_CAPI UBool U_EXPORT2 +uprv_isInvariantUString(const UChar *s, int32_t length); + +/** + * \def U_UPPER_ORDINAL + * Get the ordinal number of an uppercase invariant character + * @internal + */ +#if U_CHARSET_FAMILY==U_ASCII_FAMILY +# define U_UPPER_ORDINAL(x) ((x)-'A') +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY +# define U_UPPER_ORDINAL(x) (((x) < 'J') ? ((x)-'A') : \ + (((x) < 'S') ? ((x)-'J'+9) : \ + ((x)-'S'+18))) +#else +# error Unknown charset family! +#endif + +#ifdef __cplusplus + +U_NAMESPACE_BEGIN + +/** + * Like U_UPPER_ORDINAL(x) but with validation. + * Returns 0..25 for A..Z else a value outside 0..25. + */ +inline int32_t uprv_upperOrdinal(int32_t c) { +#if U_CHARSET_FAMILY==U_ASCII_FAMILY + return c - 'A'; +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY + // EBCDIC: A-Z (26 letters) is split into three ranges A-I (9 letters), J-R (9), S-Z (8). + // https://en.wikipedia.org/wiki/EBCDIC_037#Codepage_layout + if (c <= 'I') { return c - 'A'; } // A-I --> 0-8 + if (c < 'J') { return -1; } + if (c <= 'R') { return c - 'J' + 9; } // J-R --> 9..17 + if (c < 'S') { return -1; } + return c - 'S' + 18; // S-Z --> 18..25 +#else +# error Unknown charset family! +#endif +} + +// Like U_UPPER_ORDINAL(x) but for lowercase and with validation. +// Returns 0..25 for a..z else a value outside 0..25. +inline int32_t uprv_lowerOrdinal(int32_t c) { +#if U_CHARSET_FAMILY==U_ASCII_FAMILY + return c - 'a'; +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY + // EBCDIC: a-z (26 letters) is split into three ranges a-i (9 letters), j-r (9), s-z (8). + // https://en.wikipedia.org/wiki/EBCDIC_037#Codepage_layout + if (c <= 'i') { return c - 'a'; } // a-i --> 0-8 + if (c < 'j') { return -1; } + if (c <= 'r') { return c - 'j' + 9; } // j-r --> 9..17 + if (c < 's') { return -1; } + return c - 's' + 18; // s-z --> 18..25 +#else +# error Unknown charset family! +#endif +} + +U_NAMESPACE_END + +#endif + +/** + * Returns true if c == '@' is possible. + * The @ sign is variant, and the @ sign used on one + * EBCDIC machine won't be compiled the same way on other EBCDIC based machines. + * @internal + */ +U_CFUNC UBool +uprv_isEbcdicAtSign(char c); + +/** + * \def uprv_isAtSign + * Returns true if c == '@' is possible. + * For ASCII, checks for exactly '@'. For EBCDIC, calls uprv_isEbcdicAtSign(). + * @internal + */ +#if U_CHARSET_FAMILY==U_ASCII_FAMILY +# define uprv_isAtSign(c) ((c)=='@') +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY +# define uprv_isAtSign(c) uprv_isEbcdicAtSign(c) +#else +# error Unknown charset family! +#endif + +/** + * Compare two EBCDIC invariant-character strings in ASCII order. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +uprv_compareInvEbcdicAsAscii(const char *s1, const char *s2); + +/** + * \def uprv_compareInvCharsAsAscii + * Compare two invariant-character strings in ASCII order. + * @internal + */ +#if U_CHARSET_FAMILY==U_ASCII_FAMILY +# define uprv_compareInvCharsAsAscii(s1, s2) uprv_strcmp(s1, s2) +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY +# define uprv_compareInvCharsAsAscii(s1, s2) uprv_compareInvEbcdicAsAscii(s1, s2) +#else +# error Unknown charset family! +#endif + +/** + * Converts an EBCDIC invariant character to ASCII. + * @internal + */ +U_CAPI char U_EXPORT2 +uprv_ebcdicToAscii(char c); + +/** + * \def uprv_invCharToAscii + * Converts an invariant character to ASCII. + * @internal + */ +#if U_CHARSET_FAMILY==U_ASCII_FAMILY +# define uprv_invCharToAscii(c) (c) +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY +# define uprv_invCharToAscii(c) uprv_ebcdicToAscii(c) +#else +# error Unknown charset family! +#endif + +/** + * Converts an EBCDIC invariant character to lowercase ASCII. + * @internal + */ +U_CAPI char U_EXPORT2 +uprv_ebcdicToLowercaseAscii(char c); + +/** + * \def uprv_invCharToLowercaseAscii + * Converts an invariant character to lowercase ASCII. + * @internal + */ +#if U_CHARSET_FAMILY==U_ASCII_FAMILY +# define uprv_invCharToLowercaseAscii uprv_asciitolower +#elif U_CHARSET_FAMILY==U_EBCDIC_FAMILY +# define uprv_invCharToLowercaseAscii uprv_ebcdicToLowercaseAscii +#else +# error Unknown charset family! +#endif + +/** + * Copy EBCDIC to ASCII + * @internal + * @see uprv_strncpy + */ +U_CAPI uint8_t* U_EXPORT2 +uprv_aestrncpy(uint8_t *dst, const uint8_t *src, int32_t n); + + +/** + * Copy ASCII to EBCDIC + * @internal + * @see uprv_strncpy + */ +U_CAPI uint8_t* U_EXPORT2 +uprv_eastrncpy(uint8_t *dst, const uint8_t *src, int32_t n); + + + +#endif diff --git a/deps/icu-small/source/common/uiter.cpp b/deps/icu-small/source/common/uiter.cpp index c4ab7d6d565860..a77f72b3052ce2 100644 --- a/deps/icu-small/source/common/uiter.cpp +++ b/deps/icu-small/source/common/uiter.cpp @@ -1,1108 +1,1108 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uiter.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002jan18 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" -#include "unicode/ustring.h" -#include "unicode/chariter.h" -#include "unicode/rep.h" -#include "unicode/uiter.h" -#include "unicode/utf.h" -#include "unicode/utf8.h" -#include "unicode/utf16.h" -#include "cstring.h" - -U_NAMESPACE_USE - -#define IS_EVEN(n) (((n)&1)==0) -#define IS_POINTER_EVEN(p) IS_EVEN((size_t)p) - -U_CDECL_BEGIN - -/* No-Op UCharIterator implementation for illegal input --------------------- */ - -static int32_t U_CALLCONV -noopGetIndex(UCharIterator * /*iter*/, UCharIteratorOrigin /*origin*/) { - return 0; -} - -static int32_t U_CALLCONV -noopMove(UCharIterator * /*iter*/, int32_t /*delta*/, UCharIteratorOrigin /*origin*/) { - return 0; -} - -static UBool U_CALLCONV -noopHasNext(UCharIterator * /*iter*/) { - return false; -} - -static UChar32 U_CALLCONV -noopCurrent(UCharIterator * /*iter*/) { - return U_SENTINEL; -} - -static uint32_t U_CALLCONV -noopGetState(const UCharIterator * /*iter*/) { - return UITER_NO_STATE; -} - -static void U_CALLCONV -noopSetState(UCharIterator * /*iter*/, uint32_t /*state*/, UErrorCode *pErrorCode) { - *pErrorCode=U_UNSUPPORTED_ERROR; -} - -static const UCharIterator noopIterator={ - 0, 0, 0, 0, 0, 0, - noopGetIndex, - noopMove, - noopHasNext, - noopHasNext, - noopCurrent, - noopCurrent, - noopCurrent, - NULL, - noopGetState, - noopSetState -}; - -/* UCharIterator implementation for simple strings -------------------------- */ - -/* - * This is an implementation of a code unit (UChar) iterator - * for UChar * strings. - * - * The UCharIterator.context field holds a pointer to the string. - */ - -static int32_t U_CALLCONV -stringIteratorGetIndex(UCharIterator *iter, UCharIteratorOrigin origin) { - switch(origin) { - case UITER_ZERO: - return 0; - case UITER_START: - return iter->start; - case UITER_CURRENT: - return iter->index; - case UITER_LIMIT: - return iter->limit; - case UITER_LENGTH: - return iter->length; - default: - /* not a valid origin */ - /* Should never get here! */ - return -1; - } -} - -static int32_t U_CALLCONV -stringIteratorMove(UCharIterator *iter, int32_t delta, UCharIteratorOrigin origin) { - int32_t pos; - - switch(origin) { - case UITER_ZERO: - pos=delta; - break; - case UITER_START: - pos=iter->start+delta; - break; - case UITER_CURRENT: - pos=iter->index+delta; - break; - case UITER_LIMIT: - pos=iter->limit+delta; - break; - case UITER_LENGTH: - pos=iter->length+delta; - break; - default: - return -1; /* Error */ - } - - if(posstart) { - pos=iter->start; - } else if(pos>iter->limit) { - pos=iter->limit; - } - - return iter->index=pos; -} - -static UBool U_CALLCONV -stringIteratorHasNext(UCharIterator *iter) { - return iter->indexlimit; -} - -static UBool U_CALLCONV -stringIteratorHasPrevious(UCharIterator *iter) { - return iter->index>iter->start; -} - -static UChar32 U_CALLCONV -stringIteratorCurrent(UCharIterator *iter) { - if(iter->indexlimit) { - return ((const UChar *)(iter->context))[iter->index]; - } else { - return U_SENTINEL; - } -} - -static UChar32 U_CALLCONV -stringIteratorNext(UCharIterator *iter) { - if(iter->indexlimit) { - return ((const UChar *)(iter->context))[iter->index++]; - } else { - return U_SENTINEL; - } -} - -static UChar32 U_CALLCONV -stringIteratorPrevious(UCharIterator *iter) { - if(iter->index>iter->start) { - return ((const UChar *)(iter->context))[--iter->index]; - } else { - return U_SENTINEL; - } -} - -static uint32_t U_CALLCONV -stringIteratorGetState(const UCharIterator *iter) { - return (uint32_t)iter->index; -} - -static void U_CALLCONV -stringIteratorSetState(UCharIterator *iter, uint32_t state, UErrorCode *pErrorCode) { - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - /* do nothing */ - } else if(iter==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - } else if((int32_t)statestart || iter->limit<(int32_t)state) { - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - } else { - iter->index=(int32_t)state; - } -} - -static const UCharIterator stringIterator={ - 0, 0, 0, 0, 0, 0, - stringIteratorGetIndex, - stringIteratorMove, - stringIteratorHasNext, - stringIteratorHasPrevious, - stringIteratorCurrent, - stringIteratorNext, - stringIteratorPrevious, - NULL, - stringIteratorGetState, - stringIteratorSetState -}; - -U_CAPI void U_EXPORT2 -uiter_setString(UCharIterator *iter, const UChar *s, int32_t length) { - if(iter!=0) { - if(s!=0 && length>=-1) { - *iter=stringIterator; - iter->context=s; - if(length>=0) { - iter->length=length; - } else { - iter->length=u_strlen(s); - } - iter->limit=iter->length; - } else { - *iter=noopIterator; - } - } -} - -/* UCharIterator implementation for UTF-16BE strings ------------------------ */ - -/* - * This is an implementation of a code unit (UChar) iterator - * for UTF-16BE strings, i.e., strings in byte-vectors where - * each UChar is stored as a big-endian pair of bytes. - * - * The UCharIterator.context field holds a pointer to the string. - * Everything works just like with a normal UChar iterator (uiter_setString), - * except that UChars are assembled from byte pairs. - */ - -/* internal helper function */ -static inline UChar32 -utf16BEIteratorGet(UCharIterator *iter, int32_t index) { - const uint8_t *p=(const uint8_t *)iter->context; - return ((UChar)p[2*index]<<8)|(UChar)p[2*index+1]; -} - -static UChar32 U_CALLCONV -utf16BEIteratorCurrent(UCharIterator *iter) { - int32_t index; - - if((index=iter->index)limit) { - return utf16BEIteratorGet(iter, index); - } else { - return U_SENTINEL; - } -} - -static UChar32 U_CALLCONV -utf16BEIteratorNext(UCharIterator *iter) { - int32_t index; - - if((index=iter->index)limit) { - iter->index=index+1; - return utf16BEIteratorGet(iter, index); - } else { - return U_SENTINEL; - } -} - -static UChar32 U_CALLCONV -utf16BEIteratorPrevious(UCharIterator *iter) { - int32_t index; - - if((index=iter->index)>iter->start) { - iter->index=--index; - return utf16BEIteratorGet(iter, index); - } else { - return U_SENTINEL; - } -} - -static const UCharIterator utf16BEIterator={ - 0, 0, 0, 0, 0, 0, - stringIteratorGetIndex, - stringIteratorMove, - stringIteratorHasNext, - stringIteratorHasPrevious, - utf16BEIteratorCurrent, - utf16BEIteratorNext, - utf16BEIteratorPrevious, - NULL, - stringIteratorGetState, - stringIteratorSetState -}; - -/* - * Count the number of UChars in a UTF-16BE string before a terminating UChar NUL, - * i.e., before a pair of 0 bytes where the first 0 byte is at an even - * offset from s. - */ -static int32_t -utf16BE_strlen(const char *s) { - if(IS_POINTER_EVEN(s)) { - /* - * even-aligned, call u_strlen(s) - * we are probably on a little-endian machine, but searching for UChar NUL - * does not care about endianness - */ - return u_strlen((const UChar *)s); - } else { - /* odd-aligned, search for pair of 0 bytes */ - const char *p=s; - - while(!(*p==0 && p[1]==0)) { - p+=2; - } - return (int32_t)((p-s)/2); - } -} - -U_CAPI void U_EXPORT2 -uiter_setUTF16BE(UCharIterator *iter, const char *s, int32_t length) { - if(iter!=NULL) { - /* allow only even-length strings (the input length counts bytes) */ - if(s!=NULL && (length==-1 || (length>=0 && IS_EVEN(length)))) { - /* length/=2, except that >>=1 also works for -1 (-1/2==0, -1>>1==-1) */ - length>>=1; - - if(U_IS_BIG_ENDIAN && IS_POINTER_EVEN(s)) { - /* big-endian machine and 2-aligned UTF-16BE string: use normal UChar iterator */ - uiter_setString(iter, (const UChar *)s, length); - return; - } - - *iter=utf16BEIterator; - iter->context=s; - if(length>=0) { - iter->length=length; - } else { - iter->length=utf16BE_strlen(s); - } - iter->limit=iter->length; - } else { - *iter=noopIterator; - } - } -} - -/* UCharIterator wrapper around CharacterIterator --------------------------- */ - -/* - * This is wrapper code around a C++ CharacterIterator to - * look like a C UCharIterator. - * - * The UCharIterator.context field holds a pointer to the CharacterIterator. - */ - -static int32_t U_CALLCONV -characterIteratorGetIndex(UCharIterator *iter, UCharIteratorOrigin origin) { - switch(origin) { - case UITER_ZERO: - return 0; - case UITER_START: - return ((CharacterIterator *)(iter->context))->startIndex(); - case UITER_CURRENT: - return ((CharacterIterator *)(iter->context))->getIndex(); - case UITER_LIMIT: - return ((CharacterIterator *)(iter->context))->endIndex(); - case UITER_LENGTH: - return ((CharacterIterator *)(iter->context))->getLength(); - default: - /* not a valid origin */ - /* Should never get here! */ - return -1; - } -} - -static int32_t U_CALLCONV -characterIteratorMove(UCharIterator *iter, int32_t delta, UCharIteratorOrigin origin) { - switch(origin) { - case UITER_ZERO: - ((CharacterIterator *)(iter->context))->setIndex(delta); - return ((CharacterIterator *)(iter->context))->getIndex(); - case UITER_START: - case UITER_CURRENT: - case UITER_LIMIT: - return ((CharacterIterator *)(iter->context))->move(delta, (CharacterIterator::EOrigin)origin); - case UITER_LENGTH: - ((CharacterIterator *)(iter->context))->setIndex(((CharacterIterator *)(iter->context))->getLength()+delta); - return ((CharacterIterator *)(iter->context))->getIndex(); - default: - /* not a valid origin */ - /* Should never get here! */ - return -1; - } -} - -static UBool U_CALLCONV -characterIteratorHasNext(UCharIterator *iter) { - return ((CharacterIterator *)(iter->context))->hasNext(); -} - -static UBool U_CALLCONV -characterIteratorHasPrevious(UCharIterator *iter) { - return ((CharacterIterator *)(iter->context))->hasPrevious(); -} - -static UChar32 U_CALLCONV -characterIteratorCurrent(UCharIterator *iter) { - UChar32 c; - - c=((CharacterIterator *)(iter->context))->current(); - if(c!=0xffff || ((CharacterIterator *)(iter->context))->hasNext()) { - return c; - } else { - return U_SENTINEL; - } -} - -static UChar32 U_CALLCONV -characterIteratorNext(UCharIterator *iter) { - if(((CharacterIterator *)(iter->context))->hasNext()) { - return ((CharacterIterator *)(iter->context))->nextPostInc(); - } else { - return U_SENTINEL; - } -} - -static UChar32 U_CALLCONV -characterIteratorPrevious(UCharIterator *iter) { - if(((CharacterIterator *)(iter->context))->hasPrevious()) { - return ((CharacterIterator *)(iter->context))->previous(); - } else { - return U_SENTINEL; - } -} - -static uint32_t U_CALLCONV -characterIteratorGetState(const UCharIterator *iter) { - return ((CharacterIterator *)(iter->context))->getIndex(); -} - -static void U_CALLCONV -characterIteratorSetState(UCharIterator *iter, uint32_t state, UErrorCode *pErrorCode) { - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - /* do nothing */ - } else if(iter==NULL || iter->context==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - } else if((int32_t)state<((CharacterIterator *)(iter->context))->startIndex() || ((CharacterIterator *)(iter->context))->endIndex()<(int32_t)state) { - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - } else { - ((CharacterIterator *)(iter->context))->setIndex((int32_t)state); - } -} - -static const UCharIterator characterIteratorWrapper={ - 0, 0, 0, 0, 0, 0, - characterIteratorGetIndex, - characterIteratorMove, - characterIteratorHasNext, - characterIteratorHasPrevious, - characterIteratorCurrent, - characterIteratorNext, - characterIteratorPrevious, - NULL, - characterIteratorGetState, - characterIteratorSetState -}; - -U_CAPI void U_EXPORT2 -uiter_setCharacterIterator(UCharIterator *iter, CharacterIterator *charIter) { - if(iter!=0) { - if(charIter!=0) { - *iter=characterIteratorWrapper; - iter->context=charIter; - } else { - *iter=noopIterator; - } - } -} - -/* UCharIterator wrapper around Replaceable --------------------------------- */ - -/* - * This is an implementation of a code unit (UChar) iterator - * based on a Replaceable object. - * - * The UCharIterator.context field holds a pointer to the Replaceable. - * UCharIterator.length and UCharIterator.index hold Replaceable.length() - * and the iteration index. - */ - -static UChar32 U_CALLCONV -replaceableIteratorCurrent(UCharIterator *iter) { - if(iter->indexlimit) { - return ((Replaceable *)(iter->context))->charAt(iter->index); - } else { - return U_SENTINEL; - } -} - -static UChar32 U_CALLCONV -replaceableIteratorNext(UCharIterator *iter) { - if(iter->indexlimit) { - return ((Replaceable *)(iter->context))->charAt(iter->index++); - } else { - return U_SENTINEL; - } -} - -static UChar32 U_CALLCONV -replaceableIteratorPrevious(UCharIterator *iter) { - if(iter->index>iter->start) { - return ((Replaceable *)(iter->context))->charAt(--iter->index); - } else { - return U_SENTINEL; - } -} - -static const UCharIterator replaceableIterator={ - 0, 0, 0, 0, 0, 0, - stringIteratorGetIndex, - stringIteratorMove, - stringIteratorHasNext, - stringIteratorHasPrevious, - replaceableIteratorCurrent, - replaceableIteratorNext, - replaceableIteratorPrevious, - NULL, - stringIteratorGetState, - stringIteratorSetState -}; - -U_CAPI void U_EXPORT2 -uiter_setReplaceable(UCharIterator *iter, const Replaceable *rep) { - if(iter!=0) { - if(rep!=0) { - *iter=replaceableIterator; - iter->context=rep; - iter->limit=iter->length=rep->length(); - } else { - *iter=noopIterator; - } - } -} - -/* UCharIterator implementation for UTF-8 strings --------------------------- */ - -/* - * Possible, probably necessary only for an implementation for arbitrary - * converters: - * Maintain a buffer (ring buffer?) for a piece of converted 16-bit text. - * This would require to turn reservedFn into a close function and - * to introduce a uiter_close(iter). - */ - -#define UITER_CNV_CAPACITY 16 - -/* - * Minimal implementation: - * Maintain a single-UChar buffer for an additional surrogate. - * The caller must not modify start and limit because they are used internally. - * - * Use UCharIterator fields as follows: - * context pointer to UTF-8 string - * length UTF-16 length of the string; -1 until lazy evaluation - * start current UTF-8 index - * index current UTF-16 index; may be -1="unknown" after setState() - * limit UTF-8 length of the string - * reservedField supplementary code point - * - * Since UCharIterator delivers 16-bit code units, the iteration can be - * currently in the middle of the byte sequence for a supplementary code point. - * In this case, reservedField will contain that code point and start will - * point to after the corresponding byte sequence. The UTF-16 index will be - * one less than what it would otherwise be corresponding to the UTF-8 index. - * Otherwise, reservedField will be 0. - */ - -/* - * Possible optimization for NUL-terminated UTF-8 and UTF-16 strings: - * Add implementations that do not call strlen() for iteration but check for NUL. - */ - -static int32_t U_CALLCONV -utf8IteratorGetIndex(UCharIterator *iter, UCharIteratorOrigin origin) { - switch(origin) { - case UITER_ZERO: - case UITER_START: - return 0; - case UITER_CURRENT: - if(iter->index<0) { - /* the current UTF-16 index is unknown after setState(), count from the beginning */ - const uint8_t *s; - UChar32 c; - int32_t i, limit, index; - - s=(const uint8_t *)iter->context; - i=index=0; - limit=iter->start; /* count up to the UTF-8 index */ - while(istart=i; /* just in case setState() did not get us to a code point boundary */ - if(i==iter->limit) { - iter->length=index; /* in case it was <0 or wrong */ - } - if(iter->reservedField!=0) { - --index; /* we are in the middle of a supplementary code point */ - } - iter->index=index; - } - return iter->index; - case UITER_LIMIT: - case UITER_LENGTH: - if(iter->length<0) { - const uint8_t *s; - UChar32 c; - int32_t i, limit, length; - - s=(const uint8_t *)iter->context; - if(iter->index<0) { - /* - * the current UTF-16 index is unknown after setState(), - * we must first count from the beginning to here - */ - i=length=0; - limit=iter->start; - - /* count from the beginning to the current index */ - while(istart, set the UTF-16 index */ - iter->start=i; /* just in case setState() did not get us to a code point boundary */ - iter->index= iter->reservedField!=0 ? length-1 : length; - } else { - i=iter->start; - length=iter->index; - if(iter->reservedField!=0) { - ++length; - } - } - - /* count from the current index to the end */ - limit=iter->limit; - while(ilength=length; - } - return iter->length; - default: - /* not a valid origin */ - /* Should never get here! */ - return -1; - } -} - -static int32_t U_CALLCONV -utf8IteratorMove(UCharIterator *iter, int32_t delta, UCharIteratorOrigin origin) { - const uint8_t *s; - UChar32 c; - int32_t pos; /* requested UTF-16 index */ - int32_t i; /* UTF-8 index */ - UBool havePos; - - /* calculate the requested UTF-16 index */ - switch(origin) { - case UITER_ZERO: - case UITER_START: - pos=delta; - havePos=true; - /* iter->index<0 (unknown) is possible */ - break; - case UITER_CURRENT: - if(iter->index>=0) { - pos=iter->index+delta; - havePos=true; - } else { - /* the current UTF-16 index is unknown after setState(), use only delta */ - pos=0; - havePos=false; - } - break; - case UITER_LIMIT: - case UITER_LENGTH: - if(iter->length>=0) { - pos=iter->length+delta; - havePos=true; - } else { - /* pin to the end, avoid counting the length */ - iter->index=-1; - iter->start=iter->limit; - iter->reservedField=0; - if(delta>=0) { - return UITER_UNKNOWN_INDEX; - } else { - /* the current UTF-16 index is unknown, use only delta */ - pos=0; - havePos=false; - } - } - break; - default: - return -1; /* Error */ - } - - if(havePos) { - /* shortcuts: pinning to the edges of the string */ - if(pos<=0) { - iter->index=iter->start=iter->reservedField=0; - return 0; - } else if(iter->length>=0 && pos>=iter->length) { - iter->index=iter->length; - iter->start=iter->limit; - iter->reservedField=0; - return iter->index; - } - - /* minimize the number of U8_NEXT/PREV operations */ - if(iter->index<0 || posindex/2) { - /* go forward from the start instead of backward from the current index */ - iter->index=iter->start=iter->reservedField=0; - } else if(iter->length>=0 && (iter->length-pos)<(pos-iter->index)) { - /* - * if we have the UTF-16 index and length and the new position is - * closer to the end than the current index, - * then go backward from the end instead of forward from the current index - */ - iter->index=iter->length; - iter->start=iter->limit; - iter->reservedField=0; - } - - delta=pos-iter->index; - if(delta==0) { - return iter->index; /* nothing to do */ - } - } else { - /* move relative to unknown UTF-16 index */ - if(delta==0) { - return UITER_UNKNOWN_INDEX; /* nothing to do */ - } else if(-delta>=iter->start) { - /* moving backwards by more UChars than there are UTF-8 bytes, pin to 0 */ - iter->index=iter->start=iter->reservedField=0; - return 0; - } else if(delta>=(iter->limit-iter->start)) { - /* moving forward by more UChars than the remaining UTF-8 bytes, pin to the end */ - iter->index=iter->length; /* may or may not be <0 (unknown) */ - iter->start=iter->limit; - iter->reservedField=0; - return iter->index>=0 ? iter->index : (int32_t)UITER_UNKNOWN_INDEX; - } - } - - /* delta!=0 */ - - /* move towards the requested position, pin to the edges of the string */ - s=(const uint8_t *)iter->context; - pos=iter->index; /* could be <0 (unknown) */ - i=iter->start; - if(delta>0) { - /* go forward */ - int32_t limit=iter->limit; - if(iter->reservedField!=0) { - iter->reservedField=0; - ++pos; - --delta; - } - while(delta>0 && i=2) { - pos+=2; - delta-=2; - } else /* delta==1 */ { - /* stop in the middle of a supplementary code point */ - iter->reservedField=c; - ++pos; - break; /* delta=0; */ - } - } - if(i==limit) { - if(iter->length<0 && iter->index>=0) { - iter->length= iter->reservedField==0 ? pos : pos+1; - } else if(iter->index<0 && iter->length>=0) { - iter->index= iter->reservedField==0 ? iter->length : iter->length-1; - } - } - } else /* delta<0 */ { - /* go backward */ - if(iter->reservedField!=0) { - iter->reservedField=0; - i-=4; /* we stayed behind the supplementary code point; go before it now */ - --pos; - ++delta; - } - while(delta<0 && i>0) { - U8_PREV_OR_FFFD(s, 0, i, c); - if(c<=0xffff) { - --pos; - ++delta; - } else if(delta<=-2) { - pos-=2; - delta+=2; - } else /* delta==-1 */ { - /* stop in the middle of a supplementary code point */ - i+=4; /* back to behind this supplementary code point for consistent state */ - iter->reservedField=c; - --pos; - break; /* delta=0; */ - } - } - } - - iter->start=i; - if(iter->index>=0) { - return iter->index=pos; - } else { - /* we started with index<0 (unknown) so pos is bogus */ - if(i<=1) { - return iter->index=i; /* reached the beginning */ - } else { - /* we still don't know the UTF-16 index */ - return UITER_UNKNOWN_INDEX; - } - } -} - -static UBool U_CALLCONV -utf8IteratorHasNext(UCharIterator *iter) { - return iter->startlimit || iter->reservedField!=0; -} - -static UBool U_CALLCONV -utf8IteratorHasPrevious(UCharIterator *iter) { - return iter->start>0; -} - -static UChar32 U_CALLCONV -utf8IteratorCurrent(UCharIterator *iter) { - if(iter->reservedField!=0) { - return U16_TRAIL(iter->reservedField); - } else if(iter->startlimit) { - const uint8_t *s=(const uint8_t *)iter->context; - UChar32 c; - int32_t i=iter->start; - - U8_NEXT_OR_FFFD(s, i, iter->limit, c); - if(c<=0xffff) { - return c; - } else { - return U16_LEAD(c); - } - } else { - return U_SENTINEL; - } -} - -static UChar32 U_CALLCONV -utf8IteratorNext(UCharIterator *iter) { - int32_t index; - - if(iter->reservedField!=0) { - UChar trail=U16_TRAIL(iter->reservedField); - iter->reservedField=0; - if((index=iter->index)>=0) { - iter->index=index+1; - } - return trail; - } else if(iter->startlimit) { - const uint8_t *s=(const uint8_t *)iter->context; - UChar32 c; - - U8_NEXT_OR_FFFD(s, iter->start, iter->limit, c); - if((index=iter->index)>=0) { - iter->index=++index; - if(iter->length<0 && iter->start==iter->limit) { - iter->length= c<=0xffff ? index : index+1; - } - } else if(iter->start==iter->limit && iter->length>=0) { - iter->index= c<=0xffff ? iter->length : iter->length-1; - } - if(c<=0xffff) { - return c; - } else { - iter->reservedField=c; - return U16_LEAD(c); - } - } else { - return U_SENTINEL; - } -} - -static UChar32 U_CALLCONV -utf8IteratorPrevious(UCharIterator *iter) { - int32_t index; - - if(iter->reservedField!=0) { - UChar lead=U16_LEAD(iter->reservedField); - iter->reservedField=0; - iter->start-=4; /* we stayed behind the supplementary code point; go before it now */ - if((index=iter->index)>0) { - iter->index=index-1; - } - return lead; - } else if(iter->start>0) { - const uint8_t *s=(const uint8_t *)iter->context; - UChar32 c; - - U8_PREV_OR_FFFD(s, 0, iter->start, c); - if((index=iter->index)>0) { - iter->index=index-1; - } else if(iter->start<=1) { - iter->index= c<=0xffff ? iter->start : iter->start+1; - } - if(c<=0xffff) { - return c; - } else { - iter->start+=4; /* back to behind this supplementary code point for consistent state */ - iter->reservedField=c; - return U16_TRAIL(c); - } - } else { - return U_SENTINEL; - } -} - -static uint32_t U_CALLCONV -utf8IteratorGetState(const UCharIterator *iter) { - uint32_t state=(uint32_t)(iter->start<<1); - if(iter->reservedField!=0) { - state|=1; - } - return state; -} - -static void U_CALLCONV -utf8IteratorSetState(UCharIterator *iter, - uint32_t state, - UErrorCode *pErrorCode) -{ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - /* do nothing */ - } else if(iter==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - } else if(state==utf8IteratorGetState(iter)) { - /* setting to the current state: no-op */ - } else { - int32_t index=(int32_t)(state>>1); /* UTF-8 index */ - state&=1; /* 1 if in surrogate pair, must be index>=4 */ - - if((state==0 ? index<0 : index<4) || iter->limitstart=index; /* restore UTF-8 byte index */ - if(index<=1) { - iter->index=index; - } else { - iter->index=-1; /* unknown UTF-16 index */ - } - if(state==0) { - iter->reservedField=0; - } else { - /* verified index>=4 above */ - UChar32 c; - U8_PREV_OR_FFFD((const uint8_t *)iter->context, 0, index, c); - if(c<=0xffff) { - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - } else { - iter->reservedField=c; - } - } - } - } -} - -static const UCharIterator utf8Iterator={ - 0, 0, 0, 0, 0, 0, - utf8IteratorGetIndex, - utf8IteratorMove, - utf8IteratorHasNext, - utf8IteratorHasPrevious, - utf8IteratorCurrent, - utf8IteratorNext, - utf8IteratorPrevious, - NULL, - utf8IteratorGetState, - utf8IteratorSetState -}; - -U_CAPI void U_EXPORT2 -uiter_setUTF8(UCharIterator *iter, const char *s, int32_t length) { - if(iter!=0) { - if(s!=0 && length>=-1) { - *iter=utf8Iterator; - iter->context=s; - if(length>=0) { - iter->limit=length; - } else { - iter->limit=(int32_t)uprv_strlen(s); - } - iter->length= iter->limit<=1 ? iter->limit : -1; - } else { - *iter=noopIterator; - } - } -} - -/* Helper functions --------------------------------------------------------- */ - -U_CAPI UChar32 U_EXPORT2 -uiter_current32(UCharIterator *iter) { - UChar32 c, c2; - - c=iter->current(iter); - if(U16_IS_SURROGATE(c)) { - if(U16_IS_SURROGATE_LEAD(c)) { - /* - * go to the next code unit - * we know that we are not at the limit because c!=U_SENTINEL - */ - iter->move(iter, 1, UITER_CURRENT); - if(U16_IS_TRAIL(c2=iter->current(iter))) { - c=U16_GET_SUPPLEMENTARY(c, c2); - } - - /* undo index movement */ - iter->move(iter, -1, UITER_CURRENT); - } else { - if(U16_IS_LEAD(c2=iter->previous(iter))) { - c=U16_GET_SUPPLEMENTARY(c2, c); - } - if(c2>=0) { - /* undo index movement */ - iter->move(iter, 1, UITER_CURRENT); - } - } - } - return c; -} - -U_CAPI UChar32 U_EXPORT2 -uiter_next32(UCharIterator *iter) { - UChar32 c, c2; - - c=iter->next(iter); - if(U16_IS_LEAD(c)) { - if(U16_IS_TRAIL(c2=iter->next(iter))) { - c=U16_GET_SUPPLEMENTARY(c, c2); - } else if(c2>=0) { - /* unmatched first surrogate, undo index movement */ - iter->move(iter, -1, UITER_CURRENT); - } - } - return c; -} - -U_CAPI UChar32 U_EXPORT2 -uiter_previous32(UCharIterator *iter) { - UChar32 c, c2; - - c=iter->previous(iter); - if(U16_IS_TRAIL(c)) { - if(U16_IS_LEAD(c2=iter->previous(iter))) { - c=U16_GET_SUPPLEMENTARY(c2, c); - } else if(c2>=0) { - /* unmatched second surrogate, undo index movement */ - iter->move(iter, 1, UITER_CURRENT); - } - } - return c; -} - -U_CAPI uint32_t U_EXPORT2 -uiter_getState(const UCharIterator *iter) { - if(iter==NULL || iter->getState==NULL) { - return UITER_NO_STATE; - } else { - return iter->getState(iter); - } -} - -U_CAPI void U_EXPORT2 -uiter_setState(UCharIterator *iter, uint32_t state, UErrorCode *pErrorCode) { - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - /* do nothing */ - } else if(iter==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - } else if(iter->setState==NULL) { - *pErrorCode=U_UNSUPPORTED_ERROR; - } else { - iter->setState(iter, state, pErrorCode); - } -} - -U_CDECL_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uiter.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002jan18 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" +#include "unicode/ustring.h" +#include "unicode/chariter.h" +#include "unicode/rep.h" +#include "unicode/uiter.h" +#include "unicode/utf.h" +#include "unicode/utf8.h" +#include "unicode/utf16.h" +#include "cstring.h" + +U_NAMESPACE_USE + +#define IS_EVEN(n) (((n)&1)==0) +#define IS_POINTER_EVEN(p) IS_EVEN((size_t)p) + +U_CDECL_BEGIN + +/* No-Op UCharIterator implementation for illegal input --------------------- */ + +static int32_t U_CALLCONV +noopGetIndex(UCharIterator * /*iter*/, UCharIteratorOrigin /*origin*/) { + return 0; +} + +static int32_t U_CALLCONV +noopMove(UCharIterator * /*iter*/, int32_t /*delta*/, UCharIteratorOrigin /*origin*/) { + return 0; +} + +static UBool U_CALLCONV +noopHasNext(UCharIterator * /*iter*/) { + return false; +} + +static UChar32 U_CALLCONV +noopCurrent(UCharIterator * /*iter*/) { + return U_SENTINEL; +} + +static uint32_t U_CALLCONV +noopGetState(const UCharIterator * /*iter*/) { + return UITER_NO_STATE; +} + +static void U_CALLCONV +noopSetState(UCharIterator * /*iter*/, uint32_t /*state*/, UErrorCode *pErrorCode) { + *pErrorCode=U_UNSUPPORTED_ERROR; +} + +static const UCharIterator noopIterator={ + 0, 0, 0, 0, 0, 0, + noopGetIndex, + noopMove, + noopHasNext, + noopHasNext, + noopCurrent, + noopCurrent, + noopCurrent, + nullptr, + noopGetState, + noopSetState +}; + +/* UCharIterator implementation for simple strings -------------------------- */ + +/* + * This is an implementation of a code unit (char16_t) iterator + * for char16_t * strings. + * + * The UCharIterator.context field holds a pointer to the string. + */ + +static int32_t U_CALLCONV +stringIteratorGetIndex(UCharIterator *iter, UCharIteratorOrigin origin) UPRV_NO_SANITIZE_UNDEFINED { + switch(origin) { + case UITER_ZERO: + return 0; + case UITER_START: + return iter->start; + case UITER_CURRENT: + return iter->index; + case UITER_LIMIT: + return iter->limit; + case UITER_LENGTH: + return iter->length; + default: + /* not a valid origin */ + /* Should never get here! */ + return -1; + } +} + +static int32_t U_CALLCONV +stringIteratorMove(UCharIterator *iter, int32_t delta, UCharIteratorOrigin origin) UPRV_NO_SANITIZE_UNDEFINED { + int32_t pos; + + switch(origin) { + case UITER_ZERO: + pos=delta; + break; + case UITER_START: + pos=iter->start+delta; + break; + case UITER_CURRENT: + pos=iter->index+delta; + break; + case UITER_LIMIT: + pos=iter->limit+delta; + break; + case UITER_LENGTH: + pos=iter->length+delta; + break; + default: + return -1; /* Error */ + } + + if(posstart) { + pos=iter->start; + } else if(pos>iter->limit) { + pos=iter->limit; + } + + return iter->index=pos; +} + +static UBool U_CALLCONV +stringIteratorHasNext(UCharIterator *iter) { + return iter->indexlimit; +} + +static UBool U_CALLCONV +stringIteratorHasPrevious(UCharIterator *iter) { + return iter->index>iter->start; +} + +static UChar32 U_CALLCONV +stringIteratorCurrent(UCharIterator *iter) { + if(iter->indexlimit) { + return ((const char16_t *)(iter->context))[iter->index]; + } else { + return U_SENTINEL; + } +} + +static UChar32 U_CALLCONV +stringIteratorNext(UCharIterator *iter) { + if(iter->indexlimit) { + return ((const char16_t *)(iter->context))[iter->index++]; + } else { + return U_SENTINEL; + } +} + +static UChar32 U_CALLCONV +stringIteratorPrevious(UCharIterator *iter) { + if(iter->index>iter->start) { + return ((const char16_t *)(iter->context))[--iter->index]; + } else { + return U_SENTINEL; + } +} + +static uint32_t U_CALLCONV +stringIteratorGetState(const UCharIterator *iter) { + return (uint32_t)iter->index; +} + +static void U_CALLCONV +stringIteratorSetState(UCharIterator *iter, uint32_t state, UErrorCode *pErrorCode) { + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + /* do nothing */ + } else if(iter==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + } else if((int32_t)statestart || iter->limit<(int32_t)state) { + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + } else { + iter->index=(int32_t)state; + } +} + +static const UCharIterator stringIterator={ + 0, 0, 0, 0, 0, 0, + stringIteratorGetIndex, + stringIteratorMove, + stringIteratorHasNext, + stringIteratorHasPrevious, + stringIteratorCurrent, + stringIteratorNext, + stringIteratorPrevious, + nullptr, + stringIteratorGetState, + stringIteratorSetState +}; + +U_CAPI void U_EXPORT2 +uiter_setString(UCharIterator *iter, const char16_t *s, int32_t length) { + if(iter!=0) { + if(s!=0 && length>=-1) { + *iter=stringIterator; + iter->context=s; + if(length>=0) { + iter->length=length; + } else { + iter->length=u_strlen(s); + } + iter->limit=iter->length; + } else { + *iter=noopIterator; + } + } +} + +/* UCharIterator implementation for UTF-16BE strings ------------------------ */ + +/* + * This is an implementation of a code unit (char16_t) iterator + * for UTF-16BE strings, i.e., strings in byte-vectors where + * each char16_t is stored as a big-endian pair of bytes. + * + * The UCharIterator.context field holds a pointer to the string. + * Everything works just like with a normal char16_t iterator (uiter_setString), + * except that UChars are assembled from byte pairs. + */ + +/* internal helper function */ +static inline UChar32 +utf16BEIteratorGet(UCharIterator *iter, int32_t index) { + const uint8_t *p=(const uint8_t *)iter->context; + return ((char16_t)p[2*index]<<8)|(char16_t)p[2*index+1]; +} + +static UChar32 U_CALLCONV +utf16BEIteratorCurrent(UCharIterator *iter) { + int32_t index; + + if((index=iter->index)limit) { + return utf16BEIteratorGet(iter, index); + } else { + return U_SENTINEL; + } +} + +static UChar32 U_CALLCONV +utf16BEIteratorNext(UCharIterator *iter) { + int32_t index; + + if((index=iter->index)limit) { + iter->index=index+1; + return utf16BEIteratorGet(iter, index); + } else { + return U_SENTINEL; + } +} + +static UChar32 U_CALLCONV +utf16BEIteratorPrevious(UCharIterator *iter) { + int32_t index; + + if((index=iter->index)>iter->start) { + iter->index=--index; + return utf16BEIteratorGet(iter, index); + } else { + return U_SENTINEL; + } +} + +static const UCharIterator utf16BEIterator={ + 0, 0, 0, 0, 0, 0, + stringIteratorGetIndex, + stringIteratorMove, + stringIteratorHasNext, + stringIteratorHasPrevious, + utf16BEIteratorCurrent, + utf16BEIteratorNext, + utf16BEIteratorPrevious, + nullptr, + stringIteratorGetState, + stringIteratorSetState +}; + +/* + * Count the number of UChars in a UTF-16BE string before a terminating char16_t NUL, + * i.e., before a pair of 0 bytes where the first 0 byte is at an even + * offset from s. + */ +static int32_t +utf16BE_strlen(const char *s) { + if(IS_POINTER_EVEN(s)) { + /* + * even-aligned, call u_strlen(s) + * we are probably on a little-endian machine, but searching for char16_t NUL + * does not care about endianness + */ + return u_strlen((const char16_t *)s); + } else { + /* odd-aligned, search for pair of 0 bytes */ + const char *p=s; + + while(!(*p==0 && p[1]==0)) { + p+=2; + } + return (int32_t)((p-s)/2); + } +} + +U_CAPI void U_EXPORT2 +uiter_setUTF16BE(UCharIterator *iter, const char *s, int32_t length) { + if(iter!=nullptr) { + /* allow only even-length strings (the input length counts bytes) */ + if(s!=nullptr && (length==-1 || (length>=0 && IS_EVEN(length)))) { + /* length/=2, except that >>=1 also works for -1 (-1/2==0, -1>>1==-1) */ + length>>=1; + + if(U_IS_BIG_ENDIAN && IS_POINTER_EVEN(s)) { + /* big-endian machine and 2-aligned UTF-16BE string: use normal char16_t iterator */ + uiter_setString(iter, (const char16_t *)s, length); + return; + } + + *iter=utf16BEIterator; + iter->context=s; + if(length>=0) { + iter->length=length; + } else { + iter->length=utf16BE_strlen(s); + } + iter->limit=iter->length; + } else { + *iter=noopIterator; + } + } +} + +/* UCharIterator wrapper around CharacterIterator --------------------------- */ + +/* + * This is wrapper code around a C++ CharacterIterator to + * look like a C UCharIterator. + * + * The UCharIterator.context field holds a pointer to the CharacterIterator. + */ + +static int32_t U_CALLCONV +characterIteratorGetIndex(UCharIterator *iter, UCharIteratorOrigin origin) UPRV_NO_SANITIZE_UNDEFINED { + switch(origin) { + case UITER_ZERO: + return 0; + case UITER_START: + return ((CharacterIterator *)(iter->context))->startIndex(); + case UITER_CURRENT: + return ((CharacterIterator *)(iter->context))->getIndex(); + case UITER_LIMIT: + return ((CharacterIterator *)(iter->context))->endIndex(); + case UITER_LENGTH: + return ((CharacterIterator *)(iter->context))->getLength(); + default: + /* not a valid origin */ + /* Should never get here! */ + return -1; + } +} + +static int32_t U_CALLCONV +characterIteratorMove(UCharIterator *iter, int32_t delta, UCharIteratorOrigin origin) UPRV_NO_SANITIZE_UNDEFINED { + switch(origin) { + case UITER_ZERO: + ((CharacterIterator *)(iter->context))->setIndex(delta); + return ((CharacterIterator *)(iter->context))->getIndex(); + case UITER_START: + case UITER_CURRENT: + case UITER_LIMIT: + return ((CharacterIterator *)(iter->context))->move(delta, (CharacterIterator::EOrigin)origin); + case UITER_LENGTH: + ((CharacterIterator *)(iter->context))->setIndex(((CharacterIterator *)(iter->context))->getLength()+delta); + return ((CharacterIterator *)(iter->context))->getIndex(); + default: + /* not a valid origin */ + /* Should never get here! */ + return -1; + } +} + +static UBool U_CALLCONV +characterIteratorHasNext(UCharIterator *iter) { + return ((CharacterIterator *)(iter->context))->hasNext(); +} + +static UBool U_CALLCONV +characterIteratorHasPrevious(UCharIterator *iter) { + return ((CharacterIterator *)(iter->context))->hasPrevious(); +} + +static UChar32 U_CALLCONV +characterIteratorCurrent(UCharIterator *iter) { + UChar32 c; + + c=((CharacterIterator *)(iter->context))->current(); + if(c!=0xffff || ((CharacterIterator *)(iter->context))->hasNext()) { + return c; + } else { + return U_SENTINEL; + } +} + +static UChar32 U_CALLCONV +characterIteratorNext(UCharIterator *iter) { + if(((CharacterIterator *)(iter->context))->hasNext()) { + return ((CharacterIterator *)(iter->context))->nextPostInc(); + } else { + return U_SENTINEL; + } +} + +static UChar32 U_CALLCONV +characterIteratorPrevious(UCharIterator *iter) { + if(((CharacterIterator *)(iter->context))->hasPrevious()) { + return ((CharacterIterator *)(iter->context))->previous(); + } else { + return U_SENTINEL; + } +} + +static uint32_t U_CALLCONV +characterIteratorGetState(const UCharIterator *iter) { + return ((CharacterIterator *)(iter->context))->getIndex(); +} + +static void U_CALLCONV +characterIteratorSetState(UCharIterator *iter, uint32_t state, UErrorCode *pErrorCode) { + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + /* do nothing */ + } else if(iter==nullptr || iter->context==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + } else if((int32_t)state<((CharacterIterator *)(iter->context))->startIndex() || ((CharacterIterator *)(iter->context))->endIndex()<(int32_t)state) { + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + } else { + ((CharacterIterator *)(iter->context))->setIndex((int32_t)state); + } +} + +static const UCharIterator characterIteratorWrapper={ + 0, 0, 0, 0, 0, 0, + characterIteratorGetIndex, + characterIteratorMove, + characterIteratorHasNext, + characterIteratorHasPrevious, + characterIteratorCurrent, + characterIteratorNext, + characterIteratorPrevious, + nullptr, + characterIteratorGetState, + characterIteratorSetState +}; + +U_CAPI void U_EXPORT2 +uiter_setCharacterIterator(UCharIterator *iter, CharacterIterator *charIter) { + if(iter!=0) { + if(charIter!=0) { + *iter=characterIteratorWrapper; + iter->context=charIter; + } else { + *iter=noopIterator; + } + } +} + +/* UCharIterator wrapper around Replaceable --------------------------------- */ + +/* + * This is an implementation of a code unit (char16_t) iterator + * based on a Replaceable object. + * + * The UCharIterator.context field holds a pointer to the Replaceable. + * UCharIterator.length and UCharIterator.index hold Replaceable.length() + * and the iteration index. + */ + +static UChar32 U_CALLCONV +replaceableIteratorCurrent(UCharIterator *iter) { + if(iter->indexlimit) { + return ((Replaceable *)(iter->context))->charAt(iter->index); + } else { + return U_SENTINEL; + } +} + +static UChar32 U_CALLCONV +replaceableIteratorNext(UCharIterator *iter) { + if(iter->indexlimit) { + return ((Replaceable *)(iter->context))->charAt(iter->index++); + } else { + return U_SENTINEL; + } +} + +static UChar32 U_CALLCONV +replaceableIteratorPrevious(UCharIterator *iter) { + if(iter->index>iter->start) { + return ((Replaceable *)(iter->context))->charAt(--iter->index); + } else { + return U_SENTINEL; + } +} + +static const UCharIterator replaceableIterator={ + 0, 0, 0, 0, 0, 0, + stringIteratorGetIndex, + stringIteratorMove, + stringIteratorHasNext, + stringIteratorHasPrevious, + replaceableIteratorCurrent, + replaceableIteratorNext, + replaceableIteratorPrevious, + nullptr, + stringIteratorGetState, + stringIteratorSetState +}; + +U_CAPI void U_EXPORT2 +uiter_setReplaceable(UCharIterator *iter, const Replaceable *rep) { + if(iter!=0) { + if(rep!=0) { + *iter=replaceableIterator; + iter->context=rep; + iter->limit=iter->length=rep->length(); + } else { + *iter=noopIterator; + } + } +} + +/* UCharIterator implementation for UTF-8 strings --------------------------- */ + +/* + * Possible, probably necessary only for an implementation for arbitrary + * converters: + * Maintain a buffer (ring buffer?) for a piece of converted 16-bit text. + * This would require to turn reservedFn into a close function and + * to introduce a uiter_close(iter). + */ + +#define UITER_CNV_CAPACITY 16 + +/* + * Minimal implementation: + * Maintain a single-char16_t buffer for an additional surrogate. + * The caller must not modify start and limit because they are used internally. + * + * Use UCharIterator fields as follows: + * context pointer to UTF-8 string + * length UTF-16 length of the string; -1 until lazy evaluation + * start current UTF-8 index + * index current UTF-16 index; may be -1="unknown" after setState() + * limit UTF-8 length of the string + * reservedField supplementary code point + * + * Since UCharIterator delivers 16-bit code units, the iteration can be + * currently in the middle of the byte sequence for a supplementary code point. + * In this case, reservedField will contain that code point and start will + * point to after the corresponding byte sequence. The UTF-16 index will be + * one less than what it would otherwise be corresponding to the UTF-8 index. + * Otherwise, reservedField will be 0. + */ + +/* + * Possible optimization for NUL-terminated UTF-8 and UTF-16 strings: + * Add implementations that do not call strlen() for iteration but check for NUL. + */ + +static int32_t U_CALLCONV +utf8IteratorGetIndex(UCharIterator *iter, UCharIteratorOrigin origin) UPRV_NO_SANITIZE_UNDEFINED { + switch(origin) { + case UITER_ZERO: + case UITER_START: + return 0; + case UITER_CURRENT: + if(iter->index<0) { + /* the current UTF-16 index is unknown after setState(), count from the beginning */ + const uint8_t *s; + UChar32 c; + int32_t i, limit, index; + + s=(const uint8_t *)iter->context; + i=index=0; + limit=iter->start; /* count up to the UTF-8 index */ + while(istart=i; /* just in case setState() did not get us to a code point boundary */ + if(i==iter->limit) { + iter->length=index; /* in case it was <0 or wrong */ + } + if(iter->reservedField!=0) { + --index; /* we are in the middle of a supplementary code point */ + } + iter->index=index; + } + return iter->index; + case UITER_LIMIT: + case UITER_LENGTH: + if(iter->length<0) { + const uint8_t *s; + UChar32 c; + int32_t i, limit, length; + + s=(const uint8_t *)iter->context; + if(iter->index<0) { + /* + * the current UTF-16 index is unknown after setState(), + * we must first count from the beginning to here + */ + i=length=0; + limit=iter->start; + + /* count from the beginning to the current index */ + while(istart, set the UTF-16 index */ + iter->start=i; /* just in case setState() did not get us to a code point boundary */ + iter->index= iter->reservedField!=0 ? length-1 : length; + } else { + i=iter->start; + length=iter->index; + if(iter->reservedField!=0) { + ++length; + } + } + + /* count from the current index to the end */ + limit=iter->limit; + while(ilength=length; + } + return iter->length; + default: + /* not a valid origin */ + /* Should never get here! */ + return -1; + } +} + +static int32_t U_CALLCONV +utf8IteratorMove(UCharIterator *iter, int32_t delta, UCharIteratorOrigin origin) UPRV_NO_SANITIZE_UNDEFINED { + const uint8_t *s; + UChar32 c; + int32_t pos; /* requested UTF-16 index */ + int32_t i; /* UTF-8 index */ + UBool havePos; + + /* calculate the requested UTF-16 index */ + switch(origin) { + case UITER_ZERO: + case UITER_START: + pos=delta; + havePos=true; + /* iter->index<0 (unknown) is possible */ + break; + case UITER_CURRENT: + if(iter->index>=0) { + pos=iter->index+delta; + havePos=true; + } else { + /* the current UTF-16 index is unknown after setState(), use only delta */ + pos=0; + havePos=false; + } + break; + case UITER_LIMIT: + case UITER_LENGTH: + if(iter->length>=0) { + pos=iter->length+delta; + havePos=true; + } else { + /* pin to the end, avoid counting the length */ + iter->index=-1; + iter->start=iter->limit; + iter->reservedField=0; + if(delta>=0) { + return UITER_UNKNOWN_INDEX; + } else { + /* the current UTF-16 index is unknown, use only delta */ + pos=0; + havePos=false; + } + } + break; + default: + return -1; /* Error */ + } + + if(havePos) { + /* shortcuts: pinning to the edges of the string */ + if(pos<=0) { + iter->index=iter->start=iter->reservedField=0; + return 0; + } else if(iter->length>=0 && pos>=iter->length) { + iter->index=iter->length; + iter->start=iter->limit; + iter->reservedField=0; + return iter->index; + } + + /* minimize the number of U8_NEXT/PREV operations */ + if(iter->index<0 || posindex/2) { + /* go forward from the start instead of backward from the current index */ + iter->index=iter->start=iter->reservedField=0; + } else if(iter->length>=0 && (iter->length-pos)<(pos-iter->index)) { + /* + * if we have the UTF-16 index and length and the new position is + * closer to the end than the current index, + * then go backward from the end instead of forward from the current index + */ + iter->index=iter->length; + iter->start=iter->limit; + iter->reservedField=0; + } + + delta=pos-iter->index; + if(delta==0) { + return iter->index; /* nothing to do */ + } + } else { + /* move relative to unknown UTF-16 index */ + if(delta==0) { + return UITER_UNKNOWN_INDEX; /* nothing to do */ + } else if(-delta>=iter->start) { + /* moving backwards by more UChars than there are UTF-8 bytes, pin to 0 */ + iter->index=iter->start=iter->reservedField=0; + return 0; + } else if(delta>=(iter->limit-iter->start)) { + /* moving forward by more UChars than the remaining UTF-8 bytes, pin to the end */ + iter->index=iter->length; /* may or may not be <0 (unknown) */ + iter->start=iter->limit; + iter->reservedField=0; + return iter->index>=0 ? iter->index : (int32_t)UITER_UNKNOWN_INDEX; + } + } + + /* delta!=0 */ + + /* move towards the requested position, pin to the edges of the string */ + s=(const uint8_t *)iter->context; + pos=iter->index; /* could be <0 (unknown) */ + i=iter->start; + if(delta>0) { + /* go forward */ + int32_t limit=iter->limit; + if(iter->reservedField!=0) { + iter->reservedField=0; + ++pos; + --delta; + } + while(delta>0 && i=2) { + pos+=2; + delta-=2; + } else /* delta==1 */ { + /* stop in the middle of a supplementary code point */ + iter->reservedField=c; + ++pos; + break; /* delta=0; */ + } + } + if(i==limit) { + if(iter->length<0 && iter->index>=0) { + iter->length= iter->reservedField==0 ? pos : pos+1; + } else if(iter->index<0 && iter->length>=0) { + iter->index= iter->reservedField==0 ? iter->length : iter->length-1; + } + } + } else /* delta<0 */ { + /* go backward */ + if(iter->reservedField!=0) { + iter->reservedField=0; + i-=4; /* we stayed behind the supplementary code point; go before it now */ + --pos; + ++delta; + } + while(delta<0 && i>0) { + U8_PREV_OR_FFFD(s, 0, i, c); + if(c<=0xffff) { + --pos; + ++delta; + } else if(delta<=-2) { + pos-=2; + delta+=2; + } else /* delta==-1 */ { + /* stop in the middle of a supplementary code point */ + i+=4; /* back to behind this supplementary code point for consistent state */ + iter->reservedField=c; + --pos; + break; /* delta=0; */ + } + } + } + + iter->start=i; + if(iter->index>=0) { + return iter->index=pos; + } else { + /* we started with index<0 (unknown) so pos is bogus */ + if(i<=1) { + return iter->index=i; /* reached the beginning */ + } else { + /* we still don't know the UTF-16 index */ + return UITER_UNKNOWN_INDEX; + } + } +} + +static UBool U_CALLCONV +utf8IteratorHasNext(UCharIterator *iter) { + return iter->startlimit || iter->reservedField!=0; +} + +static UBool U_CALLCONV +utf8IteratorHasPrevious(UCharIterator *iter) { + return iter->start>0; +} + +static UChar32 U_CALLCONV +utf8IteratorCurrent(UCharIterator *iter) { + if(iter->reservedField!=0) { + return U16_TRAIL(iter->reservedField); + } else if(iter->startlimit) { + const uint8_t *s=(const uint8_t *)iter->context; + UChar32 c; + int32_t i=iter->start; + + U8_NEXT_OR_FFFD(s, i, iter->limit, c); + if(c<=0xffff) { + return c; + } else { + return U16_LEAD(c); + } + } else { + return U_SENTINEL; + } +} + +static UChar32 U_CALLCONV +utf8IteratorNext(UCharIterator *iter) { + int32_t index; + + if(iter->reservedField!=0) { + char16_t trail=U16_TRAIL(iter->reservedField); + iter->reservedField=0; + if((index=iter->index)>=0) { + iter->index=index+1; + } + return trail; + } else if(iter->startlimit) { + const uint8_t *s=(const uint8_t *)iter->context; + UChar32 c; + + U8_NEXT_OR_FFFD(s, iter->start, iter->limit, c); + if((index=iter->index)>=0) { + iter->index=++index; + if(iter->length<0 && iter->start==iter->limit) { + iter->length= c<=0xffff ? index : index+1; + } + } else if(iter->start==iter->limit && iter->length>=0) { + iter->index= c<=0xffff ? iter->length : iter->length-1; + } + if(c<=0xffff) { + return c; + } else { + iter->reservedField=c; + return U16_LEAD(c); + } + } else { + return U_SENTINEL; + } +} + +static UChar32 U_CALLCONV +utf8IteratorPrevious(UCharIterator *iter) { + int32_t index; + + if(iter->reservedField!=0) { + char16_t lead=U16_LEAD(iter->reservedField); + iter->reservedField=0; + iter->start-=4; /* we stayed behind the supplementary code point; go before it now */ + if((index=iter->index)>0) { + iter->index=index-1; + } + return lead; + } else if(iter->start>0) { + const uint8_t *s=(const uint8_t *)iter->context; + UChar32 c; + + U8_PREV_OR_FFFD(s, 0, iter->start, c); + if((index=iter->index)>0) { + iter->index=index-1; + } else if(iter->start<=1) { + iter->index= c<=0xffff ? iter->start : iter->start+1; + } + if(c<=0xffff) { + return c; + } else { + iter->start+=4; /* back to behind this supplementary code point for consistent state */ + iter->reservedField=c; + return U16_TRAIL(c); + } + } else { + return U_SENTINEL; + } +} + +static uint32_t U_CALLCONV +utf8IteratorGetState(const UCharIterator *iter) { + uint32_t state=(uint32_t)(iter->start<<1); + if(iter->reservedField!=0) { + state|=1; + } + return state; +} + +static void U_CALLCONV +utf8IteratorSetState(UCharIterator *iter, + uint32_t state, + UErrorCode *pErrorCode) +{ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + /* do nothing */ + } else if(iter==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + } else if(state==utf8IteratorGetState(iter)) { + /* setting to the current state: no-op */ + } else { + int32_t index=(int32_t)(state>>1); /* UTF-8 index */ + state&=1; /* 1 if in surrogate pair, must be index>=4 */ + + if((state==0 ? index<0 : index<4) || iter->limitstart=index; /* restore UTF-8 byte index */ + if(index<=1) { + iter->index=index; + } else { + iter->index=-1; /* unknown UTF-16 index */ + } + if(state==0) { + iter->reservedField=0; + } else { + /* verified index>=4 above */ + UChar32 c; + U8_PREV_OR_FFFD((const uint8_t *)iter->context, 0, index, c); + if(c<=0xffff) { + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + } else { + iter->reservedField=c; + } + } + } + } +} + +static const UCharIterator utf8Iterator={ + 0, 0, 0, 0, 0, 0, + utf8IteratorGetIndex, + utf8IteratorMove, + utf8IteratorHasNext, + utf8IteratorHasPrevious, + utf8IteratorCurrent, + utf8IteratorNext, + utf8IteratorPrevious, + nullptr, + utf8IteratorGetState, + utf8IteratorSetState +}; + +U_CAPI void U_EXPORT2 +uiter_setUTF8(UCharIterator *iter, const char *s, int32_t length) { + if(iter!=0) { + if(s!=0 && length>=-1) { + *iter=utf8Iterator; + iter->context=s; + if(length>=0) { + iter->limit=length; + } else { + iter->limit=(int32_t)uprv_strlen(s); + } + iter->length= iter->limit<=1 ? iter->limit : -1; + } else { + *iter=noopIterator; + } + } +} + +/* Helper functions --------------------------------------------------------- */ + +U_CAPI UChar32 U_EXPORT2 +uiter_current32(UCharIterator *iter) { + UChar32 c, c2; + + c=iter->current(iter); + if(U16_IS_SURROGATE(c)) { + if(U16_IS_SURROGATE_LEAD(c)) { + /* + * go to the next code unit + * we know that we are not at the limit because c!=U_SENTINEL + */ + iter->move(iter, 1, UITER_CURRENT); + if(U16_IS_TRAIL(c2=iter->current(iter))) { + c=U16_GET_SUPPLEMENTARY(c, c2); + } + + /* undo index movement */ + iter->move(iter, -1, UITER_CURRENT); + } else { + if(U16_IS_LEAD(c2=iter->previous(iter))) { + c=U16_GET_SUPPLEMENTARY(c2, c); + } + if(c2>=0) { + /* undo index movement */ + iter->move(iter, 1, UITER_CURRENT); + } + } + } + return c; +} + +U_CAPI UChar32 U_EXPORT2 +uiter_next32(UCharIterator *iter) { + UChar32 c, c2; + + c=iter->next(iter); + if(U16_IS_LEAD(c)) { + if(U16_IS_TRAIL(c2=iter->next(iter))) { + c=U16_GET_SUPPLEMENTARY(c, c2); + } else if(c2>=0) { + /* unmatched first surrogate, undo index movement */ + iter->move(iter, -1, UITER_CURRENT); + } + } + return c; +} + +U_CAPI UChar32 U_EXPORT2 +uiter_previous32(UCharIterator *iter) { + UChar32 c, c2; + + c=iter->previous(iter); + if(U16_IS_TRAIL(c)) { + if(U16_IS_LEAD(c2=iter->previous(iter))) { + c=U16_GET_SUPPLEMENTARY(c2, c); + } else if(c2>=0) { + /* unmatched second surrogate, undo index movement */ + iter->move(iter, 1, UITER_CURRENT); + } + } + return c; +} + +U_CAPI uint32_t U_EXPORT2 +uiter_getState(const UCharIterator *iter) { + if(iter==nullptr || iter->getState==nullptr) { + return UITER_NO_STATE; + } else { + return iter->getState(iter); + } +} + +U_CAPI void U_EXPORT2 +uiter_setState(UCharIterator *iter, uint32_t state, UErrorCode *pErrorCode) { + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + /* do nothing */ + } else if(iter==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + } else if(iter->setState==nullptr) { + *pErrorCode=U_UNSUPPORTED_ERROR; + } else { + iter->setState(iter, state, pErrorCode); + } +} + +U_CDECL_END diff --git a/deps/icu-small/source/common/ulayout_props.h b/deps/icu-small/source/common/ulayout_props.h index c0f028c7132609..a1d224de9f1fdd 100644 --- a/deps/icu-small/source/common/ulayout_props.h +++ b/deps/icu-small/source/common/ulayout_props.h @@ -1,46 +1,46 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// ulayout_props.h -// created: 2019feb12 Markus W. Scherer - -#ifndef __ULAYOUT_PROPS_H__ -#define __ULAYOUT_PROPS_H__ - -#include "unicode/utypes.h" - -// file definitions ------------------------------------------------------------ - -#define ULAYOUT_DATA_NAME "ulayout" -#define ULAYOUT_DATA_TYPE "icu" - -// data format "Layo" -#define ULAYOUT_FMT_0 0x4c -#define ULAYOUT_FMT_1 0x61 -#define ULAYOUT_FMT_2 0x79 -#define ULAYOUT_FMT_3 0x6f - -// indexes into indexes[] -enum { - // Element 0 stores the length of the indexes[] array. - ULAYOUT_IX_INDEXES_LENGTH, - // Elements 1..7 store the tops of consecutive code point tries. - // No trie is stored if the difference between two of these is less than 16. - ULAYOUT_IX_INPC_TRIE_TOP, - ULAYOUT_IX_INSC_TRIE_TOP, - ULAYOUT_IX_VO_TRIE_TOP, - ULAYOUT_IX_RESERVED_TOP, - - ULAYOUT_IX_TRIES_TOP = 7, - - ULAYOUT_IX_MAX_VALUES = 9, - - // Length of indexes[]. Multiple of 4 to 16-align the tries. - ULAYOUT_IX_COUNT = 12 -}; - -constexpr int32_t ULAYOUT_MAX_INPC_SHIFT = 24; -constexpr int32_t ULAYOUT_MAX_INSC_SHIFT = 16; -constexpr int32_t ULAYOUT_MAX_VO_SHIFT = 8; - -#endif // __ULAYOUT_PROPS_H__ +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// ulayout_props.h +// created: 2019feb12 Markus W. Scherer + +#ifndef __ULAYOUT_PROPS_H__ +#define __ULAYOUT_PROPS_H__ + +#include "unicode/utypes.h" + +// file definitions ------------------------------------------------------------ + +#define ULAYOUT_DATA_NAME "ulayout" +#define ULAYOUT_DATA_TYPE "icu" + +// data format "Layo" +#define ULAYOUT_FMT_0 0x4c +#define ULAYOUT_FMT_1 0x61 +#define ULAYOUT_FMT_2 0x79 +#define ULAYOUT_FMT_3 0x6f + +// indexes into indexes[] +enum { + // Element 0 stores the length of the indexes[] array. + ULAYOUT_IX_INDEXES_LENGTH, + // Elements 1..7 store the tops of consecutive code point tries. + // No trie is stored if the difference between two of these is less than 16. + ULAYOUT_IX_INPC_TRIE_TOP, + ULAYOUT_IX_INSC_TRIE_TOP, + ULAYOUT_IX_VO_TRIE_TOP, + ULAYOUT_IX_RESERVED_TOP, + + ULAYOUT_IX_TRIES_TOP = 7, + + ULAYOUT_IX_MAX_VALUES = 9, + + // Length of indexes[]. Multiple of 4 to 16-align the tries. + ULAYOUT_IX_COUNT = 12 +}; + +constexpr int32_t ULAYOUT_MAX_INPC_SHIFT = 24; +constexpr int32_t ULAYOUT_MAX_INSC_SHIFT = 16; +constexpr int32_t ULAYOUT_MAX_VO_SHIFT = 8; + +#endif // __ULAYOUT_PROPS_H__ diff --git a/deps/icu-small/source/common/ulist.cpp b/deps/icu-small/source/common/ulist.cpp index 57344715de5fe5..26152c74e77c0b 100644 --- a/deps/icu-small/source/common/ulist.cpp +++ b/deps/icu-small/source/common/ulist.cpp @@ -1,270 +1,270 @@ -// © 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. -****************************************************************************** -*/ - -#include "ulist.h" -#include "cmemory.h" -#include "cstring.h" -#include "uenumimp.h" - -typedef struct UListNode UListNode; -struct UListNode { - void *data; - - UListNode *next; - UListNode *previous; - - /* When data is created with uprv_malloc, needs to be freed during deleteList function. */ - UBool forceDelete; -}; - -struct UList { - UListNode *curr; - UListNode *head; - UListNode *tail; - - int32_t size; -}; - -static void ulist_addFirstItem(UList *list, UListNode *newItem); - -U_CAPI UList *U_EXPORT2 ulist_createEmptyList(UErrorCode *status) { - UList *newList = NULL; - - if (U_FAILURE(*status)) { - return NULL; - } - - newList = (UList *)uprv_malloc(sizeof(UList)); - if (newList == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - newList->curr = NULL; - newList->head = NULL; - newList->tail = NULL; - newList->size = 0; - - return newList; -} - -/* - * Function called by addItemEndList or addItemBeginList when the first item is added to the list. - * This function properly sets the pointers for the first item added. - */ -static void ulist_addFirstItem(UList *list, UListNode *newItem) { - newItem->next = NULL; - newItem->previous = NULL; - list->head = newItem; - list->tail = newItem; -} - -static void ulist_removeItem(UList *list, UListNode *p) { - if (p->previous == NULL) { - // p is the list head. - list->head = p->next; - } else { - p->previous->next = p->next; - } - if (p->next == NULL) { - // p is the list tail. - list->tail = p->previous; - } else { - p->next->previous = p->previous; - } - if (p == list->curr) { - list->curr = p->next; - } - --list->size; - if (p->forceDelete) { - uprv_free(p->data); - } - uprv_free(p); -} - -U_CAPI void U_EXPORT2 ulist_addItemEndList(UList *list, const void *data, UBool forceDelete, UErrorCode *status) { - UListNode *newItem = NULL; - - if (U_FAILURE(*status) || list == NULL || data == NULL) { - if (forceDelete) { - uprv_free((void *)data); - } - return; - } - - newItem = (UListNode *)uprv_malloc(sizeof(UListNode)); - if (newItem == NULL) { - if (forceDelete) { - uprv_free((void *)data); - } - *status = U_MEMORY_ALLOCATION_ERROR; - return; - } - newItem->data = (void *)(data); - newItem->forceDelete = forceDelete; - - if (list->size == 0) { - ulist_addFirstItem(list, newItem); - } else { - newItem->next = NULL; - newItem->previous = list->tail; - list->tail->next = newItem; - list->tail = newItem; - } - - list->size++; -} - -U_CAPI void U_EXPORT2 ulist_addItemBeginList(UList *list, const void *data, UBool forceDelete, UErrorCode *status) { - UListNode *newItem = NULL; - - if (U_FAILURE(*status) || list == NULL || data == NULL) { - if (forceDelete) { - uprv_free((void *)data); - } - return; - } - - newItem = (UListNode *)uprv_malloc(sizeof(UListNode)); - if (newItem == NULL) { - if (forceDelete) { - uprv_free((void *)data); - } - *status = U_MEMORY_ALLOCATION_ERROR; - return; - } - newItem->data = (void *)(data); - newItem->forceDelete = forceDelete; - - if (list->size == 0) { - ulist_addFirstItem(list, newItem); - } else { - newItem->previous = NULL; - newItem->next = list->head; - list->head->previous = newItem; - list->head = newItem; - } - - list->size++; -} - -U_CAPI UBool U_EXPORT2 ulist_containsString(const UList *list, const char *data, int32_t length) { - if (list != NULL) { - const UListNode *pointer; - for (pointer = list->head; pointer != NULL; pointer = pointer->next) { - if (length == (int32_t)uprv_strlen((const char *)pointer->data)) { - if (uprv_memcmp(data, pointer->data, length) == 0) { - return true; - } - } - } - } - return false; -} - -U_CAPI UBool U_EXPORT2 ulist_removeString(UList *list, const char *data) { - if (list != NULL) { - UListNode *pointer; - for (pointer = list->head; pointer != NULL; pointer = pointer->next) { - if (uprv_strcmp(data, (const char *)pointer->data) == 0) { - ulist_removeItem(list, pointer); - // Remove only the first occurrence, like Java LinkedList.remove(Object). - return true; - } - } - } - return false; -} - -U_CAPI void *U_EXPORT2 ulist_getNext(UList *list) { - UListNode *curr = NULL; - - if (list == NULL || list->curr == NULL) { - return NULL; - } - - curr = list->curr; - list->curr = curr->next; - - return curr->data; -} - -U_CAPI int32_t U_EXPORT2 ulist_getListSize(const UList *list) { - if (list != NULL) { - return list->size; - } - - return -1; -} - -U_CAPI void U_EXPORT2 ulist_resetList(UList *list) { - if (list != NULL) { - list->curr = list->head; - } -} - -U_CAPI void U_EXPORT2 ulist_deleteList(UList *list) { - UListNode *listHead = NULL; - - if (list != NULL) { - listHead = list->head; - while (listHead != NULL) { - UListNode *listPointer = listHead->next; - - if (listHead->forceDelete) { - uprv_free(listHead->data); - } - - uprv_free(listHead); - listHead = listPointer; - } - uprv_free(list); - list = NULL; - } -} - -U_CAPI void U_EXPORT2 ulist_close_keyword_values_iterator(UEnumeration *en) { - if (en != NULL) { - ulist_deleteList((UList *)(en->context)); - uprv_free(en); - } -} - -U_CAPI int32_t U_EXPORT2 ulist_count_keyword_values(UEnumeration *en, UErrorCode *status) { - if (U_FAILURE(*status)) { - return -1; - } - - return ulist_getListSize((UList *)(en->context)); -} - -U_CAPI const char * U_EXPORT2 ulist_next_keyword_value(UEnumeration *en, int32_t *resultLength, UErrorCode *status) { - const char *s; - if (U_FAILURE(*status)) { - return NULL; - } - - s = (const char *)ulist_getNext((UList *)(en->context)); - if (s != NULL && resultLength != NULL) { - *resultLength = static_cast(uprv_strlen(s)); - } - return s; -} - -U_CAPI void U_EXPORT2 ulist_reset_keyword_values_iterator(UEnumeration *en, UErrorCode *status) { - if (U_FAILURE(*status)) { - return ; - } - - ulist_resetList((UList *)(en->context)); -} - -U_CAPI UList * U_EXPORT2 ulist_getListFromEnum(UEnumeration *en) { - return (UList *)(en->context); -} +// © 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. +****************************************************************************** +*/ + +#include "ulist.h" +#include "cmemory.h" +#include "cstring.h" +#include "uenumimp.h" + +typedef struct UListNode UListNode; +struct UListNode { + void *data; + + UListNode *next; + UListNode *previous; + + /* When data is created with uprv_malloc, needs to be freed during deleteList function. */ + UBool forceDelete; +}; + +struct UList { + UListNode *curr; + UListNode *head; + UListNode *tail; + + int32_t size; +}; + +static void ulist_addFirstItem(UList *list, UListNode *newItem); + +U_CAPI UList *U_EXPORT2 ulist_createEmptyList(UErrorCode *status) { + UList *newList = nullptr; + + if (U_FAILURE(*status)) { + return nullptr; + } + + newList = (UList *)uprv_malloc(sizeof(UList)); + if (newList == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + newList->curr = nullptr; + newList->head = nullptr; + newList->tail = nullptr; + newList->size = 0; + + return newList; +} + +/* + * Function called by addItemEndList or addItemBeginList when the first item is added to the list. + * This function properly sets the pointers for the first item added. + */ +static void ulist_addFirstItem(UList *list, UListNode *newItem) { + newItem->next = nullptr; + newItem->previous = nullptr; + list->head = newItem; + list->tail = newItem; +} + +static void ulist_removeItem(UList *list, UListNode *p) { + if (p->previous == nullptr) { + // p is the list head. + list->head = p->next; + } else { + p->previous->next = p->next; + } + if (p->next == nullptr) { + // p is the list tail. + list->tail = p->previous; + } else { + p->next->previous = p->previous; + } + if (p == list->curr) { + list->curr = p->next; + } + --list->size; + if (p->forceDelete) { + uprv_free(p->data); + } + uprv_free(p); +} + +U_CAPI void U_EXPORT2 ulist_addItemEndList(UList *list, const void *data, UBool forceDelete, UErrorCode *status) { + UListNode *newItem = nullptr; + + if (U_FAILURE(*status) || list == nullptr || data == nullptr) { + if (forceDelete) { + uprv_free((void *)data); + } + return; + } + + newItem = (UListNode *)uprv_malloc(sizeof(UListNode)); + if (newItem == nullptr) { + if (forceDelete) { + uprv_free((void *)data); + } + *status = U_MEMORY_ALLOCATION_ERROR; + return; + } + newItem->data = (void *)(data); + newItem->forceDelete = forceDelete; + + if (list->size == 0) { + ulist_addFirstItem(list, newItem); + } else { + newItem->next = nullptr; + newItem->previous = list->tail; + list->tail->next = newItem; + list->tail = newItem; + } + + list->size++; +} + +U_CAPI void U_EXPORT2 ulist_addItemBeginList(UList *list, const void *data, UBool forceDelete, UErrorCode *status) { + UListNode *newItem = nullptr; + + if (U_FAILURE(*status) || list == nullptr || data == nullptr) { + if (forceDelete) { + uprv_free((void *)data); + } + return; + } + + newItem = (UListNode *)uprv_malloc(sizeof(UListNode)); + if (newItem == nullptr) { + if (forceDelete) { + uprv_free((void *)data); + } + *status = U_MEMORY_ALLOCATION_ERROR; + return; + } + newItem->data = (void *)(data); + newItem->forceDelete = forceDelete; + + if (list->size == 0) { + ulist_addFirstItem(list, newItem); + } else { + newItem->previous = nullptr; + newItem->next = list->head; + list->head->previous = newItem; + list->head = newItem; + } + + list->size++; +} + +U_CAPI UBool U_EXPORT2 ulist_containsString(const UList *list, const char *data, int32_t length) { + if (list != nullptr) { + const UListNode *pointer; + for (pointer = list->head; pointer != nullptr; pointer = pointer->next) { + if (length == (int32_t)uprv_strlen((const char *)pointer->data)) { + if (uprv_memcmp(data, pointer->data, length) == 0) { + return true; + } + } + } + } + return false; +} + +U_CAPI UBool U_EXPORT2 ulist_removeString(UList *list, const char *data) { + if (list != nullptr) { + UListNode *pointer; + for (pointer = list->head; pointer != nullptr; pointer = pointer->next) { + if (uprv_strcmp(data, (const char *)pointer->data) == 0) { + ulist_removeItem(list, pointer); + // Remove only the first occurrence, like Java LinkedList.remove(Object). + return true; + } + } + } + return false; +} + +U_CAPI void *U_EXPORT2 ulist_getNext(UList *list) { + UListNode *curr = nullptr; + + if (list == nullptr || list->curr == nullptr) { + return nullptr; + } + + curr = list->curr; + list->curr = curr->next; + + return curr->data; +} + +U_CAPI int32_t U_EXPORT2 ulist_getListSize(const UList *list) { + if (list != nullptr) { + return list->size; + } + + return -1; +} + +U_CAPI void U_EXPORT2 ulist_resetList(UList *list) { + if (list != nullptr) { + list->curr = list->head; + } +} + +U_CAPI void U_EXPORT2 ulist_deleteList(UList *list) { + UListNode *listHead = nullptr; + + if (list != nullptr) { + listHead = list->head; + while (listHead != nullptr) { + UListNode *listPointer = listHead->next; + + if (listHead->forceDelete) { + uprv_free(listHead->data); + } + + uprv_free(listHead); + listHead = listPointer; + } + uprv_free(list); + list = nullptr; + } +} + +U_CAPI void U_EXPORT2 ulist_close_keyword_values_iterator(UEnumeration *en) { + if (en != nullptr) { + ulist_deleteList((UList *)(en->context)); + uprv_free(en); + } +} + +U_CAPI int32_t U_EXPORT2 ulist_count_keyword_values(UEnumeration *en, UErrorCode *status) { + if (U_FAILURE(*status)) { + return -1; + } + + return ulist_getListSize((UList *)(en->context)); +} + +U_CAPI const char * U_EXPORT2 ulist_next_keyword_value(UEnumeration *en, int32_t *resultLength, UErrorCode *status) { + const char *s; + if (U_FAILURE(*status)) { + return nullptr; + } + + s = (const char *)ulist_getNext((UList *)(en->context)); + if (s != nullptr && resultLength != nullptr) { + *resultLength = static_cast(uprv_strlen(s)); + } + return s; +} + +U_CAPI void U_EXPORT2 ulist_reset_keyword_values_iterator(UEnumeration *en, UErrorCode *status) { + if (U_FAILURE(*status)) { + return ; + } + + ulist_resetList((UList *)(en->context)); +} + +U_CAPI UList * U_EXPORT2 ulist_getListFromEnum(UEnumeration *en) { + return (UList *)(en->context); +} diff --git a/deps/icu-small/source/common/ulist.h b/deps/icu-small/source/common/ulist.h index de58a4ad02c178..3a97b4259a14a2 100644 --- a/deps/icu-small/source/common/ulist.h +++ b/deps/icu-small/source/common/ulist.h @@ -1,50 +1,50 @@ -// © 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. -****************************************************************************** -*/ - -#ifndef ULIST_H -#define ULIST_H - -#include "unicode/utypes.h" -#include "unicode/uenum.h" - -struct UList; -typedef struct UList UList; - -U_CAPI UList * U_EXPORT2 ulist_createEmptyList(UErrorCode *status); - -U_CAPI void U_EXPORT2 ulist_addItemEndList(UList *list, const void *data, UBool forceDelete, UErrorCode *status); - -U_CAPI void U_EXPORT2 ulist_addItemBeginList(UList *list, const void *data, UBool forceDelete, UErrorCode *status); - -U_CAPI UBool U_EXPORT2 ulist_containsString(const UList *list, const char *data, int32_t length); - -U_CAPI UBool U_EXPORT2 ulist_removeString(UList *list, const char *data); - -U_CAPI void *U_EXPORT2 ulist_getNext(UList *list); - -U_CAPI int32_t U_EXPORT2 ulist_getListSize(const UList *list); - -U_CAPI void U_EXPORT2 ulist_resetList(UList *list); - -U_CAPI void U_EXPORT2 ulist_deleteList(UList *list); - -/* - * The following are for use when creating UEnumeration object backed by UList. - */ -U_CAPI void U_EXPORT2 ulist_close_keyword_values_iterator(UEnumeration *en); - -U_CAPI int32_t U_EXPORT2 ulist_count_keyword_values(UEnumeration *en, UErrorCode *status); - -U_CAPI const char * U_EXPORT2 ulist_next_keyword_value(UEnumeration* en, int32_t *resultLength, UErrorCode* status); - -U_CAPI void U_EXPORT2 ulist_reset_keyword_values_iterator(UEnumeration* en, UErrorCode* status); - -U_CAPI UList * U_EXPORT2 ulist_getListFromEnum(UEnumeration *en); - -#endif +// © 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. +****************************************************************************** +*/ + +#ifndef ULIST_H +#define ULIST_H + +#include "unicode/utypes.h" +#include "unicode/uenum.h" + +struct UList; +typedef struct UList UList; + +U_CAPI UList * U_EXPORT2 ulist_createEmptyList(UErrorCode *status); + +U_CAPI void U_EXPORT2 ulist_addItemEndList(UList *list, const void *data, UBool forceDelete, UErrorCode *status); + +U_CAPI void U_EXPORT2 ulist_addItemBeginList(UList *list, const void *data, UBool forceDelete, UErrorCode *status); + +U_CAPI UBool U_EXPORT2 ulist_containsString(const UList *list, const char *data, int32_t length); + +U_CAPI UBool U_EXPORT2 ulist_removeString(UList *list, const char *data); + +U_CAPI void *U_EXPORT2 ulist_getNext(UList *list); + +U_CAPI int32_t U_EXPORT2 ulist_getListSize(const UList *list); + +U_CAPI void U_EXPORT2 ulist_resetList(UList *list); + +U_CAPI void U_EXPORT2 ulist_deleteList(UList *list); + +/* + * The following are for use when creating UEnumeration object backed by UList. + */ +U_CAPI void U_EXPORT2 ulist_close_keyword_values_iterator(UEnumeration *en); + +U_CAPI int32_t U_EXPORT2 ulist_count_keyword_values(UEnumeration *en, UErrorCode *status); + +U_CAPI const char * U_EXPORT2 ulist_next_keyword_value(UEnumeration* en, int32_t *resultLength, UErrorCode* status); + +U_CAPI void U_EXPORT2 ulist_reset_keyword_values_iterator(UEnumeration* en, UErrorCode* status); + +U_CAPI UList * U_EXPORT2 ulist_getListFromEnum(UEnumeration *en); + +#endif diff --git a/deps/icu-small/source/common/uloc.cpp b/deps/icu-small/source/common/uloc.cpp index 1da2abc361daf8..1fa738a038dddd 100644 --- a/deps/icu-small/source/common/uloc.cpp +++ b/deps/icu-small/source/common/uloc.cpp @@ -1,2228 +1,2228 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1997-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* File ULOC.CPP -* -* Modification History: -* -* Date Name Description -* 04/01/97 aliu Creation. -* 08/21/98 stephen JDK 1.2 sync -* 12/08/98 rtg New Locale implementation and C API -* 03/15/99 damiba overhaul. -* 04/06/99 stephen changed setDefault() to realloc and copy -* 06/14/99 stephen Changed calls to ures_open for new params -* 07/21/99 stephen Modified setDefault() to propagate to C++ -* 05/14/04 alan 7 years later: refactored, cleaned up, fixed bugs, -* brought canonicalization code into line with spec -*****************************************************************************/ - -/* - POSIX's locale format, from putil.c: [no spaces] - - ll [ _CC ] [ . MM ] [ @ VV] - - l = lang, C = ctry, M = charmap, V = variant -*/ - -#include "unicode/bytestream.h" -#include "unicode/errorcode.h" -#include "unicode/stringpiece.h" -#include "unicode/utypes.h" -#include "unicode/ustring.h" -#include "unicode/uloc.h" - -#include "bytesinkutil.h" -#include "putilimp.h" -#include "ustr_imp.h" -#include "ulocimp.h" -#include "umutex.h" -#include "cstring.h" -#include "cmemory.h" -#include "locmap.h" -#include "uarrsort.h" -#include "uenumimp.h" -#include "uassert.h" -#include "charstr.h" - -U_NAMESPACE_USE - -/* ### Declarations **************************************************/ - -/* Locale stuff from locid.cpp */ -U_CFUNC void locale_set_default(const char *id); -U_CFUNC const char *locale_get_default(void); - -/* ### Data tables **************************************************/ - -/** - * Table of language codes, both 2- and 3-letter, with preference - * given to 2-letter codes where possible. Includes 3-letter codes - * that lack a 2-letter equivalent. - * - * This list must be in sorted order. This list is returned directly - * to the user by some API. - * - * This list must be kept in sync with LANGUAGES_3, with corresponding - * entries matched. - * - * This table should be terminated with a NULL entry, followed by a - * second list, and another NULL entry. The first list is visible to - * user code when this array is returned by API. The second list - * contains codes we support, but do not expose through user API. - * - * Notes - * - * Tables updated per http://lcweb.loc.gov/standards/iso639-2/ to - * include the revisions up to 2001/7/27 *CWB* - * - * The 3 character codes are the terminology codes like RFC 3066. This - * is compatible with prior ICU codes - * - * "in" "iw" "ji" "jw" & "sh" have been withdrawn but are still in the - * table but now at the end of the table because 3 character codes are - * duplicates. This avoids bad searches going from 3 to 2 character - * codes. - * - * The range qaa-qtz is reserved for local use - */ -/* Generated using org.unicode.cldr.icu.GenerateISO639LanguageTables */ -/* ISO639 table version is 20150505 */ -/* Subsequent hand addition of selected languages */ -static const char * const LANGUAGES[] = { - "aa", "ab", "ace", "ach", "ada", "ady", "ae", "aeb", - "af", "afh", "agq", "ain", "ak", "akk", "akz", "ale", - "aln", "alt", "am", "an", "ang", "anp", "ar", "arc", - "arn", "aro", "arp", "arq", "ars", "arw", "ary", "arz", "as", - "asa", "ase", "ast", "av", "avk", "awa", "ay", "az", - "ba", "bal", "ban", "bar", "bas", "bax", "bbc", "bbj", - "be", "bej", "bem", "bew", "bez", "bfd", "bfq", "bg", - "bgc", "bgn", "bho", "bi", "bik", "bin", "bjn", "bkm", "bla", - "bm", "bn", "bo", "bpy", "bqi", "br", "bra", "brh", - "brx", "bs", "bss", "bua", "bug", "bum", "byn", "byv", - "ca", "cad", "car", "cay", "cch", "ccp", "ce", "ceb", "cgg", - "ch", "chb", "chg", "chk", "chm", "chn", "cho", "chp", - "chr", "chy", "ckb", "co", "cop", "cps", "cr", "crh", - "cs", "csb", "cu", "cv", "cy", - "da", "dak", "dar", "dav", "de", "del", "den", "dgr", - "din", "dje", "doi", "dsb", "dtp", "dua", "dum", "dv", - "dyo", "dyu", "dz", "dzg", - "ebu", "ee", "efi", "egl", "egy", "eka", "el", "elx", - "en", "enm", "eo", "es", "esu", "et", "eu", "ewo", - "ext", - "fa", "fan", "fat", "ff", "fi", "fil", "fit", "fj", - "fo", "fon", "fr", "frc", "frm", "fro", "frp", "frr", - "frs", "fur", "fy", - "ga", "gaa", "gag", "gan", "gay", "gba", "gbz", "gd", - "gez", "gil", "gl", "glk", "gmh", "gn", "goh", "gom", - "gon", "gor", "got", "grb", "grc", "gsw", "gu", "guc", - "gur", "guz", "gv", "gwi", - "ha", "hai", "hak", "haw", "he", "hi", "hif", "hil", - "hit", "hmn", "ho", "hr", "hsb", "hsn", "ht", "hu", - "hup", "hy", "hz", - "ia", "iba", "ibb", "id", "ie", "ig", "ii", "ik", - "ilo", "inh", "io", "is", "it", "iu", "izh", - "ja", "jam", "jbo", "jgo", "jmc", "jpr", "jrb", "jut", - "jv", - "ka", "kaa", "kab", "kac", "kaj", "kam", "kaw", "kbd", - "kbl", "kcg", "kde", "kea", "ken", "kfo", "kg", "kgp", - "kha", "kho", "khq", "khw", "ki", "kiu", "kj", "kk", - "kkj", "kl", "kln", "km", "kmb", "kn", "ko", "koi", - "kok", "kos", "kpe", "kr", "krc", "kri", "krj", "krl", - "kru", "ks", "ksb", "ksf", "ksh", "ku", "kum", "kut", - "kv", "kw", "ky", - "la", "lad", "lag", "lah", "lam", "lb", "lez", "lfn", - "lg", "li", "lij", "liv", "lkt", "lmo", "ln", "lo", - "lol", "loz", "lrc", "lt", "ltg", "lu", "lua", "lui", - "lun", "luo", "lus", "luy", "lv", "lzh", "lzz", - "mad", "maf", "mag", "mai", "mak", "man", "mas", "mde", - "mdf", "mdh", "mdr", "men", "mer", "mfe", "mg", "mga", - "mgh", "mgo", "mh", "mi", "mic", "min", "mis", "mk", - "ml", "mn", "mnc", "mni", - "moh", "mos", "mr", "mrj", - "ms", "mt", "mua", "mul", "mus", "mwl", "mwr", "mwv", - "my", "mye", "myv", "mzn", - "na", "nan", "nap", "naq", "nb", "nd", "nds", "ne", - "new", "ng", "nia", "niu", "njo", "nl", "nmg", "nn", - "nnh", "no", "nog", "non", "nov", "nqo", "nr", "nso", - "nus", "nv", "nwc", "ny", "nym", "nyn", "nyo", "nzi", - "oc", "oj", "om", "or", "os", "osa", "ota", - "pa", "pag", "pal", "pam", "pap", "pau", "pcd", "pcm", "pdc", - "pdt", "peo", "pfl", "phn", "pi", "pl", "pms", "pnt", - "pon", "prg", "pro", "ps", "pt", - "qu", "quc", "qug", - "raj", "rap", "rar", "rgn", "rif", "rm", "rn", "ro", - "rof", "rom", "rtm", "ru", "rue", "rug", "rup", - "rw", "rwk", - "sa", "sad", "sah", "sam", "saq", "sas", "sat", "saz", - "sba", "sbp", "sc", "scn", "sco", "sd", "sdc", "sdh", - "se", "see", "seh", "sei", "sel", "ses", "sg", "sga", - "sgs", "shi", "shn", "shu", "si", "sid", "sk", - "sl", "sli", "sly", "sm", "sma", "smj", "smn", "sms", - "sn", "snk", "so", "sog", "sq", "sr", "srn", "srr", - "ss", "ssy", "st", "stq", "su", "suk", "sus", "sux", - "sv", "sw", "swb", "syc", "syr", "szl", - "ta", "tcy", "te", "tem", "teo", "ter", "tet", "tg", - "th", "ti", "tig", "tiv", "tk", "tkl", "tkr", - "tlh", "tli", "tly", "tmh", "tn", "to", "tog", "tpi", - "tr", "tru", "trv", "ts", "tsd", "tsi", "tt", "ttt", - "tum", "tvl", "tw", "twq", "ty", "tyv", "tzm", - "udm", "ug", "uga", "uk", "umb", "und", "ur", "uz", - "vai", "ve", "vec", "vep", "vi", "vls", "vmf", "vo", - "vot", "vro", "vun", - "wa", "wae", "wal", "war", "was", "wbp", "wo", "wuu", - "xal", "xh", "xmf", "xog", - "yao", "yap", "yav", "ybb", "yi", "yo", "yrl", "yue", - "za", "zap", "zbl", "zea", "zen", "zgh", "zh", "zu", - "zun", "zxx", "zza", -NULL, - "in", "iw", "ji", "jw", "mo", "sh", "swc", "tl", /* obsolete language codes */ -NULL -}; - -static const char* const DEPRECATED_LANGUAGES[]={ - "in", "iw", "ji", "jw", "mo", NULL, NULL -}; -static const char* const REPLACEMENT_LANGUAGES[]={ - "id", "he", "yi", "jv", "ro", NULL, NULL -}; - -/** - * Table of 3-letter language codes. - * - * This is a lookup table used to convert 3-letter language codes to - * their 2-letter equivalent, where possible. It must be kept in sync - * with LANGUAGES. For all valid i, LANGUAGES[i] must refer to the - * same language as LANGUAGES_3[i]. The commented-out lines are - * copied from LANGUAGES to make eyeballing this baby easier. - * - * Where a 3-letter language code has no 2-letter equivalent, the - * 3-letter code occupies both LANGUAGES[i] and LANGUAGES_3[i]. - * - * This table should be terminated with a NULL entry, followed by a - * second list, and another NULL entry. The two lists correspond to - * the two lists in LANGUAGES. - */ -/* Generated using org.unicode.cldr.icu.GenerateISO639LanguageTables */ -/* ISO639 table version is 20150505 */ -/* Subsequent hand addition of selected languages */ -static const char * const LANGUAGES_3[] = { - "aar", "abk", "ace", "ach", "ada", "ady", "ave", "aeb", - "afr", "afh", "agq", "ain", "aka", "akk", "akz", "ale", - "aln", "alt", "amh", "arg", "ang", "anp", "ara", "arc", - "arn", "aro", "arp", "arq", "ars", "arw", "ary", "arz", "asm", - "asa", "ase", "ast", "ava", "avk", "awa", "aym", "aze", - "bak", "bal", "ban", "bar", "bas", "bax", "bbc", "bbj", - "bel", "bej", "bem", "bew", "bez", "bfd", "bfq", "bul", - "bgc", "bgn", "bho", "bis", "bik", "bin", "bjn", "bkm", "bla", - "bam", "ben", "bod", "bpy", "bqi", "bre", "bra", "brh", - "brx", "bos", "bss", "bua", "bug", "bum", "byn", "byv", - "cat", "cad", "car", "cay", "cch", "ccp", "che", "ceb", "cgg", - "cha", "chb", "chg", "chk", "chm", "chn", "cho", "chp", - "chr", "chy", "ckb", "cos", "cop", "cps", "cre", "crh", - "ces", "csb", "chu", "chv", "cym", - "dan", "dak", "dar", "dav", "deu", "del", "den", "dgr", - "din", "dje", "doi", "dsb", "dtp", "dua", "dum", "div", - "dyo", "dyu", "dzo", "dzg", - "ebu", "ewe", "efi", "egl", "egy", "eka", "ell", "elx", - "eng", "enm", "epo", "spa", "esu", "est", "eus", "ewo", - "ext", - "fas", "fan", "fat", "ful", "fin", "fil", "fit", "fij", - "fao", "fon", "fra", "frc", "frm", "fro", "frp", "frr", - "frs", "fur", "fry", - "gle", "gaa", "gag", "gan", "gay", "gba", "gbz", "gla", - "gez", "gil", "glg", "glk", "gmh", "grn", "goh", "gom", - "gon", "gor", "got", "grb", "grc", "gsw", "guj", "guc", - "gur", "guz", "glv", "gwi", - "hau", "hai", "hak", "haw", "heb", "hin", "hif", "hil", - "hit", "hmn", "hmo", "hrv", "hsb", "hsn", "hat", "hun", - "hup", "hye", "her", - "ina", "iba", "ibb", "ind", "ile", "ibo", "iii", "ipk", - "ilo", "inh", "ido", "isl", "ita", "iku", "izh", - "jpn", "jam", "jbo", "jgo", "jmc", "jpr", "jrb", "jut", - "jav", - "kat", "kaa", "kab", "kac", "kaj", "kam", "kaw", "kbd", - "kbl", "kcg", "kde", "kea", "ken", "kfo", "kon", "kgp", - "kha", "kho", "khq", "khw", "kik", "kiu", "kua", "kaz", - "kkj", "kal", "kln", "khm", "kmb", "kan", "kor", "koi", - "kok", "kos", "kpe", "kau", "krc", "kri", "krj", "krl", - "kru", "kas", "ksb", "ksf", "ksh", "kur", "kum", "kut", - "kom", "cor", "kir", - "lat", "lad", "lag", "lah", "lam", "ltz", "lez", "lfn", - "lug", "lim", "lij", "liv", "lkt", "lmo", "lin", "lao", - "lol", "loz", "lrc", "lit", "ltg", "lub", "lua", "lui", - "lun", "luo", "lus", "luy", "lav", "lzh", "lzz", - "mad", "maf", "mag", "mai", "mak", "man", "mas", "mde", - "mdf", "mdh", "mdr", "men", "mer", "mfe", "mlg", "mga", - "mgh", "mgo", "mah", "mri", "mic", "min", "mis", "mkd", - "mal", "mon", "mnc", "mni", - "moh", "mos", "mar", "mrj", - "msa", "mlt", "mua", "mul", "mus", "mwl", "mwr", "mwv", - "mya", "mye", "myv", "mzn", - "nau", "nan", "nap", "naq", "nob", "nde", "nds", "nep", - "new", "ndo", "nia", "niu", "njo", "nld", "nmg", "nno", - "nnh", "nor", "nog", "non", "nov", "nqo", "nbl", "nso", - "nus", "nav", "nwc", "nya", "nym", "nyn", "nyo", "nzi", - "oci", "oji", "orm", "ori", "oss", "osa", "ota", - "pan", "pag", "pal", "pam", "pap", "pau", "pcd", "pcm", "pdc", - "pdt", "peo", "pfl", "phn", "pli", "pol", "pms", "pnt", - "pon", "prg", "pro", "pus", "por", - "que", "quc", "qug", - "raj", "rap", "rar", "rgn", "rif", "roh", "run", "ron", - "rof", "rom", "rtm", "rus", "rue", "rug", "rup", - "kin", "rwk", - "san", "sad", "sah", "sam", "saq", "sas", "sat", "saz", - "sba", "sbp", "srd", "scn", "sco", "snd", "sdc", "sdh", - "sme", "see", "seh", "sei", "sel", "ses", "sag", "sga", - "sgs", "shi", "shn", "shu", "sin", "sid", "slk", - "slv", "sli", "sly", "smo", "sma", "smj", "smn", "sms", - "sna", "snk", "som", "sog", "sqi", "srp", "srn", "srr", - "ssw", "ssy", "sot", "stq", "sun", "suk", "sus", "sux", - "swe", "swa", "swb", "syc", "syr", "szl", - "tam", "tcy", "tel", "tem", "teo", "ter", "tet", "tgk", - "tha", "tir", "tig", "tiv", "tuk", "tkl", "tkr", - "tlh", "tli", "tly", "tmh", "tsn", "ton", "tog", "tpi", - "tur", "tru", "trv", "tso", "tsd", "tsi", "tat", "ttt", - "tum", "tvl", "twi", "twq", "tah", "tyv", "tzm", - "udm", "uig", "uga", "ukr", "umb", "und", "urd", "uzb", - "vai", "ven", "vec", "vep", "vie", "vls", "vmf", "vol", - "vot", "vro", "vun", - "wln", "wae", "wal", "war", "was", "wbp", "wol", "wuu", - "xal", "xho", "xmf", "xog", - "yao", "yap", "yav", "ybb", "yid", "yor", "yrl", "yue", - "zha", "zap", "zbl", "zea", "zen", "zgh", "zho", "zul", - "zun", "zxx", "zza", -NULL, -/* "in", "iw", "ji", "jw", "mo", "sh", "swc", "tl", */ - "ind", "heb", "yid", "jaw", "mol", "srp", "swc", "tgl", -NULL -}; - -/** - * Table of 2-letter country codes. - * - * This list must be in sorted order. This list is returned directly - * to the user by some API. - * - * This list must be kept in sync with COUNTRIES_3, with corresponding - * entries matched. - * - * This table should be terminated with a NULL entry, followed by a - * second list, and another NULL entry. The first list is visible to - * user code when this array is returned by API. The second list - * contains codes we support, but do not expose through user API. - * - * Notes: - * - * ZR(ZAR) is now CD(COD) and FX(FXX) is PS(PSE) as per - * http://www.evertype.com/standards/iso3166/iso3166-1-en.html added - * new codes keeping the old ones for compatibility updated to include - * 1999/12/03 revisions *CWB* - * - * RO(ROM) is now RO(ROU) according to - * http://www.iso.org/iso/en/prods-services/iso3166ma/03updates-on-iso-3166/nlv3e-rou.html - */ -static const char * const COUNTRIES[] = { - "AD", "AE", "AF", "AG", "AI", "AL", "AM", - "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", - "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", - "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BV", - "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", - "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", - "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DG", "DJ", "DK", - "DM", "DO", "DZ", "EA", "EC", "EE", "EG", "EH", "ER", - "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", - "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", - "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", - "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", - "IC", "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", - "IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", - "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", - "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", - "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", - "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", - "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", - "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", - "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", - "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", - "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA", - "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", - "SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SV", - "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", - "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", - "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", - "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", - "WS", "XK", "YE", "YT", "ZA", "ZM", "ZW", -NULL, - "AN", "BU", "CS", "FX", "RO", "SU", "TP", "YD", "YU", "ZR", /* obsolete country codes */ -NULL -}; - -static const char* const DEPRECATED_COUNTRIES[] = { - "AN", "BU", "CS", "DD", "DY", "FX", "HV", "NH", "RH", "SU", "TP", "UK", "VD", "YD", "YU", "ZR", NULL, NULL /* deprecated country list */ -}; -static const char* const REPLACEMENT_COUNTRIES[] = { -/* "AN", "BU", "CS", "DD", "DY", "FX", "HV", "NH", "RH", "SU", "TP", "UK", "VD", "YD", "YU", "ZR" */ - "CW", "MM", "RS", "DE", "BJ", "FR", "BF", "VU", "ZW", "RU", "TL", "GB", "VN", "YE", "RS", "CD", NULL, NULL /* replacement country codes */ -}; - -/** - * Table of 3-letter country codes. - * - * This is a lookup table used to convert 3-letter country codes to - * their 2-letter equivalent. It must be kept in sync with COUNTRIES. - * For all valid i, COUNTRIES[i] must refer to the same country as - * COUNTRIES_3[i]. The commented-out lines are copied from COUNTRIES - * to make eyeballing this baby easier. - * - * This table should be terminated with a NULL entry, followed by a - * second list, and another NULL entry. The two lists correspond to - * the two lists in COUNTRIES. - */ -static const char * const COUNTRIES_3[] = { -/* "AD", "AE", "AF", "AG", "AI", "AL", "AM", */ - "AND", "ARE", "AFG", "ATG", "AIA", "ALB", "ARM", -/* "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", */ - "AGO", "ATA", "ARG", "ASM", "AUT", "AUS", "ABW", "ALA", "AZE", -/* "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", */ - "BIH", "BRB", "BGD", "BEL", "BFA", "BGR", "BHR", "BDI", -/* "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BV", */ - "BEN", "BLM", "BMU", "BRN", "BOL", "BES", "BRA", "BHS", "BTN", "BVT", -/* "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", */ - "BWA", "BLR", "BLZ", "CAN", "CCK", "COD", "CAF", "COG", -/* "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", */ - "CHE", "CIV", "COK", "CHL", "CMR", "CHN", "COL", "CRI", -/* "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DG", "DJ", "DK", */ - "CUB", "CPV", "CUW", "CXR", "CYP", "CZE", "DEU", "DGA", "DJI", "DNK", -/* "DM", "DO", "DZ", "EA", "EC", "EE", "EG", "EH", "ER", */ - "DMA", "DOM", "DZA", "XEA", "ECU", "EST", "EGY", "ESH", "ERI", -/* "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", */ - "ESP", "ETH", "FIN", "FJI", "FLK", "FSM", "FRO", "FRA", -/* "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", */ - "GAB", "GBR", "GRD", "GEO", "GUF", "GGY", "GHA", "GIB", "GRL", -/* "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", */ - "GMB", "GIN", "GLP", "GNQ", "GRC", "SGS", "GTM", "GUM", -/* "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", */ - "GNB", "GUY", "HKG", "HMD", "HND", "HRV", "HTI", "HUN", -/* "IC", "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS" */ - "XIC", "IDN", "IRL", "ISR", "IMN", "IND", "IOT", "IRQ", "IRN", "ISL", -/* "IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", */ - "ITA", "JEY", "JAM", "JOR", "JPN", "KEN", "KGZ", "KHM", "KIR", -/* "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", */ - "COM", "KNA", "PRK", "KOR", "KWT", "CYM", "KAZ", "LAO", -/* "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", */ - "LBN", "LCA", "LIE", "LKA", "LBR", "LSO", "LTU", "LUX", -/* "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", */ - "LVA", "LBY", "MAR", "MCO", "MDA", "MNE", "MAF", "MDG", "MHL", "MKD", -/* "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", */ - "MLI", "MMR", "MNG", "MAC", "MNP", "MTQ", "MRT", "MSR", -/* "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", */ - "MLT", "MUS", "MDV", "MWI", "MEX", "MYS", "MOZ", "NAM", -/* "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", */ - "NCL", "NER", "NFK", "NGA", "NIC", "NLD", "NOR", "NPL", -/* "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", */ - "NRU", "NIU", "NZL", "OMN", "PAN", "PER", "PYF", "PNG", -/* "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", */ - "PHL", "PAK", "POL", "SPM", "PCN", "PRI", "PSE", "PRT", -/* "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA", */ - "PLW", "PRY", "QAT", "REU", "ROU", "SRB", "RUS", "RWA", "SAU", -/* "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", */ - "SLB", "SYC", "SDN", "SWE", "SGP", "SHN", "SVN", "SJM", -/* "SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SV", */ - "SVK", "SLE", "SMR", "SEN", "SOM", "SUR", "SSD", "STP", "SLV", -/* "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", */ - "SXM", "SYR", "SWZ", "TCA", "TCD", "ATF", "TGO", "THA", "TJK", -/* "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", */ - "TKL", "TLS", "TKM", "TUN", "TON", "TUR", "TTO", "TUV", -/* "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", */ - "TWN", "TZA", "UKR", "UGA", "UMI", "USA", "URY", "UZB", -/* "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", */ - "VAT", "VCT", "VEN", "VGB", "VIR", "VNM", "VUT", "WLF", -/* "WS", "XK", "YE", "YT", "ZA", "ZM", "ZW", */ - "WSM", "XKK", "YEM", "MYT", "ZAF", "ZMB", "ZWE", -NULL, -/* "AN", "BU", "CS", "FX", "RO", "SU", "TP", "YD", "YU", "ZR" */ - "ANT", "BUR", "SCG", "FXX", "ROM", "SUN", "TMP", "YMD", "YUG", "ZAR", -NULL -}; - -typedef struct CanonicalizationMap { - const char *id; /* input ID */ - const char *canonicalID; /* canonicalized output ID */ -} CanonicalizationMap; - -/** - * A map to canonicalize locale IDs. This handles a variety of - * different semantic kinds of transformations. - */ -static const CanonicalizationMap CANONICALIZE_MAP[] = { - { "art__LOJBAN", "jbo" }, /* registered name */ - { "hy__AREVELA", "hy" }, /* Registered IANA variant */ - { "hy__AREVMDA", "hyw" }, /* Registered IANA variant */ - { "zh__GUOYU", "zh" }, /* registered name */ - { "zh__HAKKA", "hak" }, /* registered name */ - { "zh__XIANG", "hsn" }, /* registered name */ - // subtags with 3 chars won't be treated as variants. - { "zh_GAN", "gan" }, /* registered name */ - { "zh_MIN_NAN", "nan" }, /* registered name */ - { "zh_WUU", "wuu" }, /* registered name */ - { "zh_YUE", "yue" }, /* registered name */ -}; - -/* ### BCP47 Conversion *******************************************/ -/* Test if the locale id has BCP47 u extension and does not have '@' */ -#define _hasBCP47Extension(id) (id && uprv_strstr(id, "@") == NULL && getShortestSubtagLength(localeID) == 1) -/* Converts the BCP47 id to Unicode id. Does nothing to id if conversion fails */ -static const char* _ConvertBCP47( - const char* id, char* buffer, int32_t length, - UErrorCode* err, int32_t* pLocaleIdSize) { - const char* finalID; - int32_t localeIDSize = uloc_forLanguageTag(id, buffer, length, NULL, err); - if (localeIDSize <= 0 || U_FAILURE(*err) || *err == U_STRING_NOT_TERMINATED_WARNING) { - finalID=id; - if (*err == U_STRING_NOT_TERMINATED_WARNING) { - *err = U_BUFFER_OVERFLOW_ERROR; - } - } else { - finalID=buffer; - } - if (pLocaleIdSize != nullptr) { - *pLocaleIdSize = localeIDSize; - } - return finalID; -} -/* Gets the size of the shortest subtag in the given localeID. */ -static int32_t getShortestSubtagLength(const char *localeID) { - int32_t localeIDLength = static_cast(uprv_strlen(localeID)); - int32_t length = localeIDLength; - int32_t tmpLength = 0; - int32_t i; - UBool reset = true; - - for (i = 0; i < localeIDLength; i++) { - if (localeID[i] != '_' && localeID[i] != '-') { - if (reset) { - tmpLength = 0; - reset = false; - } - tmpLength++; - } else { - if (tmpLength != 0 && tmpLength < length) { - length = tmpLength; - } - reset = true; - } - } - - return length; -} - -/* ### Keywords **************************************************/ -#define UPRV_ISDIGIT(c) (((c) >= '0') && ((c) <= '9')) -#define UPRV_ISALPHANUM(c) (uprv_isASCIILetter(c) || UPRV_ISDIGIT(c) ) -/* Punctuation/symbols allowed in legacy key values */ -#define UPRV_OK_VALUE_PUNCTUATION(c) ((c) == '_' || (c) == '-' || (c) == '+' || (c) == '/') - -#define ULOC_KEYWORD_BUFFER_LEN 25 -#define ULOC_MAX_NO_KEYWORDS 25 - -U_CAPI const char * U_EXPORT2 -locale_getKeywordsStart(const char *localeID) { - const char *result = NULL; - if((result = uprv_strchr(localeID, '@')) != NULL) { - return result; - } -#if (U_CHARSET_FAMILY == U_EBCDIC_FAMILY) - else { - /* We do this because the @ sign is variant, and the @ sign used on one - EBCDIC machine won't be compiled the same way on other EBCDIC based - machines. */ - static const uint8_t ebcdicSigns[] = { 0x7C, 0x44, 0x66, 0x80, 0xAC, 0xAE, 0xAF, 0xB5, 0xEC, 0xEF, 0x00 }; - const uint8_t *charToFind = ebcdicSigns; - while(*charToFind) { - if((result = uprv_strchr(localeID, *charToFind)) != NULL) { - return result; - } - charToFind++; - } - } -#endif - return NULL; -} - -/** - * @param buf buffer of size [ULOC_KEYWORD_BUFFER_LEN] - * @param keywordName incoming name to be canonicalized - * @param status return status (keyword too long) - * @return length of the keyword name - */ -static int32_t locale_canonKeywordName(char *buf, const char *keywordName, UErrorCode *status) -{ - int32_t keywordNameLen = 0; - - for (; *keywordName != 0; keywordName++) { - if (!UPRV_ISALPHANUM(*keywordName)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed keyword name */ - return 0; - } - if (keywordNameLen < ULOC_KEYWORD_BUFFER_LEN - 1) { - buf[keywordNameLen++] = uprv_tolower(*keywordName); - } else { - /* keyword name too long for internal buffer */ - *status = U_INTERNAL_PROGRAM_ERROR; - return 0; - } - } - if (keywordNameLen == 0) { - *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty keyword name */ - return 0; - } - buf[keywordNameLen] = 0; /* terminate */ - - return keywordNameLen; -} - -typedef struct { - char keyword[ULOC_KEYWORD_BUFFER_LEN]; - int32_t keywordLen; - const char *valueStart; - int32_t valueLen; -} KeywordStruct; - -static int32_t U_CALLCONV -compareKeywordStructs(const void * /*context*/, const void *left, const void *right) { - const char* leftString = ((const KeywordStruct *)left)->keyword; - const char* rightString = ((const KeywordStruct *)right)->keyword; - return uprv_strcmp(leftString, rightString); -} - -U_CFUNC void -ulocimp_getKeywords(const char *localeID, - char prev, - ByteSink& sink, - UBool valuesToo, - UErrorCode *status) -{ - KeywordStruct keywordList[ULOC_MAX_NO_KEYWORDS]; - - int32_t maxKeywords = ULOC_MAX_NO_KEYWORDS; - int32_t numKeywords = 0; - const char* pos = localeID; - const char* equalSign = NULL; - const char* semicolon = NULL; - int32_t i = 0, j, n; - - if(prev == '@') { /* start of keyword definition */ - /* we will grab pairs, trim spaces, lowercase keywords, sort and return */ - do { - UBool duplicate = false; - /* skip leading spaces */ - while(*pos == ' ') { - pos++; - } - if (!*pos) { /* handle trailing "; " */ - break; - } - if(numKeywords == maxKeywords) { - *status = U_INTERNAL_PROGRAM_ERROR; - return; - } - equalSign = uprv_strchr(pos, '='); - semicolon = uprv_strchr(pos, ';'); - /* lack of '=' [foo@currency] is illegal */ - /* ';' before '=' [foo@currency;collation=pinyin] is illegal */ - if(!equalSign || (semicolon && semicolon= ULOC_KEYWORD_BUFFER_LEN) { - /* keyword name too long for internal buffer */ - *status = U_INTERNAL_PROGRAM_ERROR; - return; - } - for(i = 0, n = 0; i < equalSign - pos; ++i) { - if (pos[i] != ' ') { - keywordList[numKeywords].keyword[n++] = uprv_tolower(pos[i]); - } - } - - /* zero-length keyword is an error. */ - if (n == 0) { - *status = U_INVALID_FORMAT_ERROR; - return; - } - - keywordList[numKeywords].keyword[n] = 0; - keywordList[numKeywords].keywordLen = n; - /* now grab the value part. First we skip the '=' */ - equalSign++; - /* then we leading spaces */ - while(*equalSign == ' ') { - equalSign++; - } - - /* Premature end or zero-length value */ - if (!*equalSign || equalSign == semicolon) { - *status = U_INVALID_FORMAT_ERROR; - return; - } - - keywordList[numKeywords].valueStart = equalSign; - - pos = semicolon; - i = 0; - if(pos) { - while(*(pos - i - 1) == ' ') { - i++; - } - keywordList[numKeywords].valueLen = (int32_t)(pos - equalSign - i); - pos++; - } else { - i = (int32_t)uprv_strlen(equalSign); - while(i && equalSign[i-1] == ' ') { - i--; - } - keywordList[numKeywords].valueLen = i; - } - /* If this is a duplicate keyword, then ignore it */ - for (j=0; j startSearchHere && *(keyValueTail-1) == ' ') { - keyValueTail--; - } - /* now keyValueTail points to first char after the keyName */ - /* copy & normalize keyName from locale */ - if (startSearchHere == keyValueTail) { - *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty keyword name in passed-in locale */ - return; - } - keyValueLen = 0; - while (startSearchHere < keyValueTail) { - if (!UPRV_ISALPHANUM(*startSearchHere)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed keyword name */ - return; - } - if (keyValueLen < ULOC_KEYWORD_BUFFER_LEN - 1) { - localeKeywordNameBuffer[keyValueLen++] = uprv_tolower(*startSearchHere++); - } else { - /* keyword name too long for internal buffer */ - *status = U_INTERNAL_PROGRAM_ERROR; - return; - } - } - localeKeywordNameBuffer[keyValueLen] = 0; /* terminate */ - - startSearchHere = uprv_strchr(nextSeparator, ';'); - - if(uprv_strcmp(keywordNameBuffer, localeKeywordNameBuffer) == 0) { - /* current entry matches the keyword. */ - nextSeparator++; /* skip '=' */ - /* First strip leading & trailing spaces (TC decided to tolerate these) */ - while(*nextSeparator == ' ') { - nextSeparator++; - } - keyValueTail = (startSearchHere)? startSearchHere: nextSeparator + uprv_strlen(nextSeparator); - while(keyValueTail > nextSeparator && *(keyValueTail-1) == ' ') { - keyValueTail--; - } - /* Now copy the value, but check well-formedness */ - if (nextSeparator == keyValueTail) { - *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty key value name in passed-in locale */ - return; - } - while (nextSeparator < keyValueTail) { - if (!UPRV_ISALPHANUM(*nextSeparator) && !UPRV_OK_VALUE_PUNCTUATION(*nextSeparator)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed key value */ - return; - } - /* Should we lowercase value to return here? Tests expect as-is. */ - sink.Append(nextSeparator++, 1); - } - return; - } - } - } -} - -U_CAPI int32_t U_EXPORT2 -uloc_setKeywordValue(const char* keywordName, - const char* keywordValue, - char* buffer, int32_t bufferCapacity, - UErrorCode* status) -{ - /* TODO: sorting. removal. */ - int32_t keywordNameLen; - int32_t keywordValueLen; - int32_t bufLen; - int32_t needLen = 0; - char keywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN]; - char keywordValueBuffer[ULOC_KEYWORDS_CAPACITY+1]; - char localeKeywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN]; - int32_t rc; - char* nextSeparator = NULL; - char* nextEqualsign = NULL; - char* startSearchHere = NULL; - char* keywordStart = NULL; - CharString updatedKeysAndValues; - UBool handledInputKeyAndValue = false; - char keyValuePrefix = '@'; - - if(U_FAILURE(*status)) { - return -1; - } - if (*status == U_STRING_NOT_TERMINATED_WARNING) { - *status = U_ZERO_ERROR; - } - if (keywordName == NULL || keywordName[0] == 0 || bufferCapacity <= 1) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - bufLen = (int32_t)uprv_strlen(buffer); - if(bufferCapacity= bufferCapacity) { - *status = U_BUFFER_OVERFLOW_ERROR; - return needLen; /* no change */ - } - *startSearchHere++ = '@'; - uprv_strcpy(startSearchHere, keywordNameBuffer); - startSearchHere += keywordNameLen; - *startSearchHere++ = '='; - uprv_strcpy(startSearchHere, keywordValueBuffer); - U_ASSERT(*status != U_STRING_NOT_TERMINATED_WARNING); - return needLen; - } /* end shortcut - no @ */ - - keywordStart = startSearchHere; - /* search for keyword */ - while(keywordStart) { - const char* keyValueTail; - int32_t keyValueLen; - - keywordStart++; /* skip @ or ; */ - nextEqualsign = uprv_strchr(keywordStart, '='); - if (!nextEqualsign) { - *status = U_ILLEGAL_ARGUMENT_ERROR; /* key must have =value */ - return 0; - } - /* strip leading & trailing spaces (TC decided to tolerate these) */ - while(*keywordStart == ' ') { - keywordStart++; - } - keyValueTail = nextEqualsign; - while (keyValueTail > keywordStart && *(keyValueTail-1) == ' ') { - keyValueTail--; - } - /* now keyValueTail points to first char after the keyName */ - /* copy & normalize keyName from locale */ - if (keywordStart == keyValueTail) { - *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty keyword name in passed-in locale */ - return 0; - } - keyValueLen = 0; - while (keywordStart < keyValueTail) { - if (!UPRV_ISALPHANUM(*keywordStart)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed keyword name */ - return 0; - } - if (keyValueLen < ULOC_KEYWORD_BUFFER_LEN - 1) { - localeKeywordNameBuffer[keyValueLen++] = uprv_tolower(*keywordStart++); - } else { - /* keyword name too long for internal buffer */ - *status = U_INTERNAL_PROGRAM_ERROR; - return 0; - } - } - localeKeywordNameBuffer[keyValueLen] = 0; /* terminate */ - - nextSeparator = uprv_strchr(nextEqualsign, ';'); - - /* start processing the value part */ - nextEqualsign++; /* skip '=' */ - /* First strip leading & trailing spaces (TC decided to tolerate these) */ - while(*nextEqualsign == ' ') { - nextEqualsign++; - } - keyValueTail = (nextSeparator)? nextSeparator: nextEqualsign + uprv_strlen(nextEqualsign); - while(keyValueTail > nextEqualsign && *(keyValueTail-1) == ' ') { - keyValueTail--; - } - if (nextEqualsign == keyValueTail) { - *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty key value in passed-in locale */ - return 0; - } - - rc = uprv_strcmp(keywordNameBuffer, localeKeywordNameBuffer); - if(rc == 0) { - /* Current entry matches the input keyword. Update the entry */ - if(keywordValueLen > 0) { /* updating a value */ - updatedKeysAndValues.append(keyValuePrefix, *status); - keyValuePrefix = ';'; /* for any subsequent key-value pair */ - updatedKeysAndValues.append(keywordNameBuffer, keywordNameLen, *status); - updatedKeysAndValues.append('=', *status); - updatedKeysAndValues.append(keywordValueBuffer, keywordValueLen, *status); - } /* else removing this entry, don't emit anything */ - handledInputKeyAndValue = true; - } else { - /* input keyword sorts earlier than current entry, add before current entry */ - if (rc < 0 && keywordValueLen > 0 && !handledInputKeyAndValue) { - /* insert new entry at this location */ - updatedKeysAndValues.append(keyValuePrefix, *status); - keyValuePrefix = ';'; /* for any subsequent key-value pair */ - updatedKeysAndValues.append(keywordNameBuffer, keywordNameLen, *status); - updatedKeysAndValues.append('=', *status); - updatedKeysAndValues.append(keywordValueBuffer, keywordValueLen, *status); - handledInputKeyAndValue = true; - } - /* copy the current entry */ - updatedKeysAndValues.append(keyValuePrefix, *status); - keyValuePrefix = ';'; /* for any subsequent key-value pair */ - updatedKeysAndValues.append(localeKeywordNameBuffer, keyValueLen, *status); - updatedKeysAndValues.append('=', *status); - updatedKeysAndValues.append(nextEqualsign, static_cast(keyValueTail-nextEqualsign), *status); - } - if (!nextSeparator && keywordValueLen > 0 && !handledInputKeyAndValue) { - /* append new entry at the end, it sorts later than existing entries */ - updatedKeysAndValues.append(keyValuePrefix, *status); - /* skip keyValuePrefix update, no subsequent key-value pair */ - updatedKeysAndValues.append(keywordNameBuffer, keywordNameLen, *status); - updatedKeysAndValues.append('=', *status); - updatedKeysAndValues.append(keywordValueBuffer, keywordValueLen, *status); - handledInputKeyAndValue = true; - } - keywordStart = nextSeparator; - } /* end loop searching */ - - /* Any error from updatedKeysAndValues.append above would be internal and not due to - * problems with the passed-in locale. So if we did encounter problems with the - * passed-in locale above, those errors took precedence and overrode any error - * status from updatedKeysAndValues.append, and also caused a return of 0. If there - * are errors here they are from updatedKeysAndValues.append; they do cause an - * error return but the passed-in locale is unmodified and the original bufLen is - * returned. - */ - if (!handledInputKeyAndValue || U_FAILURE(*status)) { - /* if input key/value specified removal of a keyword not present in locale, or - * there was an error in CharString.append, leave original locale alone. */ - U_ASSERT(*status != U_STRING_NOT_TERMINATED_WARNING); - return bufLen; - } - - // needLen = length of the part before '@' - needLen = (int32_t)(startSearchHere - buffer); - // Check to see can we fit the startSearchHere, if not, return - // U_BUFFER_OVERFLOW_ERROR without copy updatedKeysAndValues into it. - // We do this because this API function does not behave like most others: - // It promises never to set a U_STRING_NOT_TERMINATED_WARNING. - // When the contents fits but without the terminating NUL, in this case we need to not change - // the buffer contents and return with a buffer overflow error. - int32_t appendLength = updatedKeysAndValues.length(); - if (appendLength >= bufferCapacity - needLen) { - *status = U_BUFFER_OVERFLOW_ERROR; - return needLen + appendLength; - } - needLen += updatedKeysAndValues.extract( - startSearchHere, bufferCapacity - needLen, *status); - U_ASSERT(*status != U_STRING_NOT_TERMINATED_WARNING); - return needLen; -} - -/* ### ID parsing implementation **************************************************/ - -#define _isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I')) - -/*returns true if one of the special prefixes is here (s=string) - 'x-' or 'i-' */ -#define _isIDPrefix(s) (_isPrefixLetter(s[0])&&_isIDSeparator(s[1])) - -/* Dot terminates it because of POSIX form where dot precedes the codepage - * except for variant - */ -#define _isTerminator(a) ((a==0)||(a=='.')||(a=='@')) - -/** - * Lookup 'key' in the array 'list'. The array 'list' should contain - * a NULL entry, followed by more entries, and a second NULL entry. - * - * The 'list' param should be LANGUAGES, LANGUAGES_3, COUNTRIES, or - * COUNTRIES_3. - */ -static int16_t _findIndex(const char* const* list, const char* key) -{ - const char* const* anchor = list; - int32_t pass = 0; - - /* Make two passes through two NULL-terminated arrays at 'list' */ - while (pass++ < 2) { - while (*list) { - if (uprv_strcmp(key, *list) == 0) { - return (int16_t)(list - anchor); - } - list++; - } - ++list; /* skip final NULL *CWB*/ - } - return -1; -} - -U_CFUNC const char* -uloc_getCurrentCountryID(const char* oldID){ - int32_t offset = _findIndex(DEPRECATED_COUNTRIES, oldID); - if (offset >= 0) { - return REPLACEMENT_COUNTRIES[offset]; - } - return oldID; -} -U_CFUNC const char* -uloc_getCurrentLanguageID(const char* oldID){ - int32_t offset = _findIndex(DEPRECATED_LANGUAGES, oldID); - if (offset >= 0) { - return REPLACEMENT_LANGUAGES[offset]; - } - return oldID; -} -/* - * the internal functions _getLanguage(), _getCountry(), _getVariant() - * avoid duplicating code to handle the earlier locale ID pieces - * in the functions for the later ones by - * setting the *pEnd pointer to where they stopped parsing - * - * TODO try to use this in Locale - */ -CharString U_EXPORT2 -ulocimp_getLanguage(const char *localeID, - const char **pEnd, - UErrorCode &status) { - CharString result; - - if (uprv_stricmp(localeID, "root") == 0) { - localeID += 4; - } else if (uprv_strnicmp(localeID, "und", 3) == 0 && - (localeID[3] == '\0' || - localeID[3] == '-' || - localeID[3] == '_' || - localeID[3] == '@')) { - localeID += 3; - } - - /* if it starts with i- or x- then copy that prefix */ - if(_isIDPrefix(localeID)) { - result.append((char)uprv_tolower(*localeID), status); - result.append('-', status); - localeID+=2; - } - - /* copy the language as far as possible and count its length */ - while(!_isTerminator(*localeID) && !_isIDSeparator(*localeID)) { - result.append((char)uprv_tolower(*localeID), status); - localeID++; - } - - if(result.length()==3) { - /* convert 3 character code to 2 character code if possible *CWB*/ - int32_t offset = _findIndex(LANGUAGES_3, result.data()); - if(offset>=0) { - result.clear(); - result.append(LANGUAGES[offset], status); - } - } - - if(pEnd!=NULL) { - *pEnd=localeID; - } - - return result; -} - -CharString U_EXPORT2 -ulocimp_getScript(const char *localeID, - const char **pEnd, - UErrorCode &status) { - CharString result; - int32_t idLen = 0; - - if (pEnd != NULL) { - *pEnd = localeID; - } - - /* copy the second item as far as possible and count its length */ - while(!_isTerminator(localeID[idLen]) && !_isIDSeparator(localeID[idLen]) - && uprv_isASCIILetter(localeID[idLen])) { - idLen++; - } - - /* If it's exactly 4 characters long, then it's a script and not a country. */ - if (idLen == 4) { - int32_t i; - if (pEnd != NULL) { - *pEnd = localeID+idLen; - } - if (idLen >= 1) { - result.append((char)uprv_toupper(*(localeID++)), status); - } - for (i = 1; i < idLen; i++) { - result.append((char)uprv_tolower(*(localeID++)), status); - } - } - - return result; -} - -CharString U_EXPORT2 -ulocimp_getCountry(const char *localeID, - const char **pEnd, - UErrorCode &status) { - CharString result; - int32_t idLen=0; - - /* copy the country as far as possible and count its length */ - while(!_isTerminator(localeID[idLen]) && !_isIDSeparator(localeID[idLen])) { - result.append((char)uprv_toupper(localeID[idLen]), status); - idLen++; - } - - /* the country should be either length 2 or 3 */ - if (idLen == 2 || idLen == 3) { - /* convert 3 character code to 2 character code if possible *CWB*/ - if(idLen==3) { - int32_t offset = _findIndex(COUNTRIES_3, result.data()); - if(offset>=0) { - result.clear(); - result.append(COUNTRIES[offset], status); - } - } - localeID+=idLen; - } else { - result.clear(); - } - - if(pEnd!=NULL) { - *pEnd=localeID; - } - - return result; -} - -/** - * @param needSeparator if true, then add leading '_' if any variants - * are added to 'variant' - */ -static void -_getVariant(const char *localeID, - char prev, - ByteSink& sink, - UBool needSeparator) { - UBool hasVariant = false; - - /* get one or more variant tags and separate them with '_' */ - if(_isIDSeparator(prev)) { - /* get a variant string after a '-' or '_' */ - while(!_isTerminator(*localeID)) { - if (needSeparator) { - sink.Append("_", 1); - needSeparator = false; - } - char c = (char)uprv_toupper(*localeID); - if (c == '-') c = '_'; - sink.Append(&c, 1); - hasVariant = true; - localeID++; - } - } - - /* if there is no variant tag after a '-' or '_' then look for '@' */ - if(!hasVariant) { - if(prev=='@') { - /* keep localeID */ - } else if((localeID=locale_getKeywordsStart(localeID))!=NULL) { - ++localeID; /* point after the '@' */ - } else { - return; - } - while(!_isTerminator(*localeID)) { - if (needSeparator) { - sink.Append("_", 1); - needSeparator = false; - } - char c = (char)uprv_toupper(*localeID); - if (c == '-' || c == ',') c = '_'; - sink.Append(&c, 1); - localeID++; - } - } -} - -/* Keyword enumeration */ - -typedef struct UKeywordsContext { - char* keywords; - char* current; -} UKeywordsContext; - -U_CDECL_BEGIN - -static void U_CALLCONV -uloc_kw_closeKeywords(UEnumeration *enumerator) { - uprv_free(((UKeywordsContext *)enumerator->context)->keywords); - uprv_free(enumerator->context); - uprv_free(enumerator); -} - -static int32_t U_CALLCONV -uloc_kw_countKeywords(UEnumeration *en, UErrorCode * /*status*/) { - char *kw = ((UKeywordsContext *)en->context)->keywords; - int32_t result = 0; - while(*kw) { - result++; - kw += uprv_strlen(kw)+1; - } - return result; -} - -static const char * U_CALLCONV -uloc_kw_nextKeyword(UEnumeration* en, - int32_t* resultLength, - UErrorCode* /*status*/) { - const char* result = ((UKeywordsContext *)en->context)->current; - int32_t len = 0; - if(*result) { - len = (int32_t)uprv_strlen(((UKeywordsContext *)en->context)->current); - ((UKeywordsContext *)en->context)->current += len+1; - } else { - result = NULL; - } - if (resultLength) { - *resultLength = len; - } - return result; -} - -static void U_CALLCONV -uloc_kw_resetKeywords(UEnumeration* en, - UErrorCode* /*status*/) { - ((UKeywordsContext *)en->context)->current = ((UKeywordsContext *)en->context)->keywords; -} - -U_CDECL_END - - -static const UEnumeration gKeywordsEnum = { - NULL, - NULL, - uloc_kw_closeKeywords, - uloc_kw_countKeywords, - uenum_unextDefault, - uloc_kw_nextKeyword, - uloc_kw_resetKeywords -}; - -U_CAPI UEnumeration* U_EXPORT2 -uloc_openKeywordList(const char *keywordList, int32_t keywordListSize, UErrorCode* status) -{ - LocalMemory myContext; - LocalMemory result; - - if (U_FAILURE(*status)) { - return nullptr; - } - myContext.adoptInstead(static_cast(uprv_malloc(sizeof(UKeywordsContext)))); - result.adoptInstead(static_cast(uprv_malloc(sizeof(UEnumeration)))); - if (myContext.isNull() || result.isNull()) { - *status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - uprv_memcpy(result.getAlias(), &gKeywordsEnum, sizeof(UEnumeration)); - myContext->keywords = static_cast(uprv_malloc(keywordListSize+1)); - if (myContext->keywords == nullptr) { - *status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - uprv_memcpy(myContext->keywords, keywordList, keywordListSize); - myContext->keywords[keywordListSize] = 0; - myContext->current = myContext->keywords; - result->context = myContext.orphan(); - return result.orphan(); -} - -U_CAPI UEnumeration* U_EXPORT2 -uloc_openKeywords(const char* localeID, - UErrorCode* status) -{ - char tempBuffer[ULOC_FULLNAME_CAPACITY]; - const char* tmpLocaleID; - - if(status==NULL || U_FAILURE(*status)) { - return 0; - } - - if (_hasBCP47Extension(localeID)) { - tmpLocaleID = _ConvertBCP47(localeID, tempBuffer, - sizeof(tempBuffer), status, nullptr); - } else { - if (localeID==NULL) { - localeID=uloc_getDefault(); - } - tmpLocaleID=localeID; - } - - /* Skip the language */ - ulocimp_getLanguage(tmpLocaleID, &tmpLocaleID, *status); - if (U_FAILURE(*status)) { - return 0; - } - - if(_isIDSeparator(*tmpLocaleID)) { - const char *scriptID; - /* Skip the script if available */ - ulocimp_getScript(tmpLocaleID+1, &scriptID, *status); - if (U_FAILURE(*status)) { - return 0; - } - if(scriptID != tmpLocaleID+1) { - /* Found optional script */ - tmpLocaleID = scriptID; - } - /* Skip the Country */ - if (_isIDSeparator(*tmpLocaleID)) { - ulocimp_getCountry(tmpLocaleID+1, &tmpLocaleID, *status); - if (U_FAILURE(*status)) { - return 0; - } - } - } - - /* keywords are located after '@' */ - if((tmpLocaleID = locale_getKeywordsStart(tmpLocaleID)) != NULL) { - CharString keywords; - CharStringByteSink sink(&keywords); - ulocimp_getKeywords(tmpLocaleID+1, '@', sink, false, status); - if (U_FAILURE(*status)) { - return NULL; - } - return uloc_openKeywordList(keywords.data(), keywords.length(), status); - } - return NULL; -} - - -/* bit-flags for 'options' parameter of _canonicalize */ -#define _ULOC_STRIP_KEYWORDS 0x2 -#define _ULOC_CANONICALIZE 0x1 - -#define OPTION_SET(options, mask) ((options & mask) != 0) - -static const char i_default[] = {'i', '-', 'd', 'e', 'f', 'a', 'u', 'l', 't'}; -#define I_DEFAULT_LENGTH UPRV_LENGTHOF(i_default) - -/** - * Canonicalize the given localeID, to level 1 or to level 2, - * depending on the options. To specify level 1, pass in options=0. - * To specify level 2, pass in options=_ULOC_CANONICALIZE. - * - * This is the code underlying uloc_getName and uloc_canonicalize. - */ -static void -_canonicalize(const char* localeID, - ByteSink& sink, - uint32_t options, - UErrorCode* err) { - if (U_FAILURE(*err)) { - return; - } - - int32_t j, fieldCount=0, scriptSize=0, variantSize=0; - PreflightingLocaleIDBuffer tempBuffer; // if localeID has a BCP47 extension, tmpLocaleID points to this - CharString localeIDWithHyphens; // if localeID has a BPC47 extension and have _, tmpLocaleID points to this - const char* origLocaleID; - const char* tmpLocaleID; - const char* keywordAssign = NULL; - const char* separatorIndicator = NULL; - - if (_hasBCP47Extension(localeID)) { - const char* localeIDPtr = localeID; - - // convert all underbars to hyphens, unless the "BCP47 extension" comes at the beginning of the string - if (uprv_strchr(localeID, '_') != nullptr && localeID[1] != '-' && localeID[1] != '_') { - localeIDWithHyphens.append(localeID, -1, *err); - if (U_SUCCESS(*err)) { - for (char* p = localeIDWithHyphens.data(); *p != '\0'; ++p) { - if (*p == '_') { - *p = '-'; - } - } - localeIDPtr = localeIDWithHyphens.data(); - } - } - - do { - // After this call tmpLocaleID may point to localeIDPtr which may - // point to either localeID or localeIDWithHyphens.data(). - tmpLocaleID = _ConvertBCP47(localeIDPtr, tempBuffer.getBuffer(), - tempBuffer.getCapacity(), err, - &(tempBuffer.requestedCapacity)); - } while (tempBuffer.needToTryAgain(err)); - } else { - if (localeID==NULL) { - localeID=uloc_getDefault(); - } - tmpLocaleID=localeID; - } - - origLocaleID=tmpLocaleID; - - /* get all pieces, one after another, and separate with '_' */ - CharString tag = ulocimp_getLanguage(tmpLocaleID, &tmpLocaleID, *err); - - if (tag.length() == I_DEFAULT_LENGTH && - uprv_strncmp(origLocaleID, i_default, I_DEFAULT_LENGTH) == 0) { - tag.clear(); - tag.append(uloc_getDefault(), *err); - } else if(_isIDSeparator(*tmpLocaleID)) { - const char *scriptID; - - ++fieldCount; - tag.append('_', *err); - - CharString script = ulocimp_getScript(tmpLocaleID+1, &scriptID, *err); - tag.append(script, *err); - scriptSize = script.length(); - if(scriptSize > 0) { - /* Found optional script */ - tmpLocaleID = scriptID; - ++fieldCount; - if (_isIDSeparator(*tmpLocaleID)) { - /* If there is something else, then we add the _ */ - tag.append('_', *err); - } - } - - if (_isIDSeparator(*tmpLocaleID)) { - const char *cntryID; - - CharString country = ulocimp_getCountry(tmpLocaleID+1, &cntryID, *err); - tag.append(country, *err); - if (!country.isEmpty()) { - /* Found optional country */ - tmpLocaleID = cntryID; - } - if(_isIDSeparator(*tmpLocaleID)) { - /* If there is something else, then we add the _ if we found country before. */ - if (!_isIDSeparator(*(tmpLocaleID+1))) { - ++fieldCount; - tag.append('_', *err); - } - - variantSize = -tag.length(); - { - CharStringByteSink s(&tag); - _getVariant(tmpLocaleID+1, *tmpLocaleID, s, false); - } - variantSize += tag.length(); - if (variantSize > 0) { - tmpLocaleID += variantSize + 1; /* skip '_' and variant */ - } - } - } - } - - /* Copy POSIX-style charset specifier, if any [mr.utf8] */ - if (!OPTION_SET(options, _ULOC_CANONICALIZE) && *tmpLocaleID == '.') { - UBool done = false; - do { - char c = *tmpLocaleID; - switch (c) { - case 0: - case '@': - done = true; - break; - default: - tag.append(c, *err); - ++tmpLocaleID; - break; - } - } while (!done); - } - - /* Scan ahead to next '@' and determine if it is followed by '=' and/or ';' - After this, tmpLocaleID either points to '@' or is NULL */ - if ((tmpLocaleID=locale_getKeywordsStart(tmpLocaleID))!=NULL) { - keywordAssign = uprv_strchr(tmpLocaleID, '='); - separatorIndicator = uprv_strchr(tmpLocaleID, ';'); - } - - /* Copy POSIX-style variant, if any [mr@FOO] */ - if (!OPTION_SET(options, _ULOC_CANONICALIZE) && - tmpLocaleID != NULL && keywordAssign == NULL) { - for (;;) { - char c = *tmpLocaleID; - if (c == 0) { - break; - } - tag.append(c, *err); - ++tmpLocaleID; - } - } - - if (OPTION_SET(options, _ULOC_CANONICALIZE)) { - /* Handle @FOO variant if @ is present and not followed by = */ - if (tmpLocaleID!=NULL && keywordAssign==NULL) { - /* Add missing '_' if needed */ - if (fieldCount < 2 || (fieldCount < 3 && scriptSize > 0)) { - do { - tag.append('_', *err); - ++fieldCount; - } while(fieldCount<2); - } - - int32_t posixVariantSize = -tag.length(); - { - CharStringByteSink s(&tag); - _getVariant(tmpLocaleID+1, '@', s, (UBool)(variantSize > 0)); - } - posixVariantSize += tag.length(); - if (posixVariantSize > 0) { - variantSize += posixVariantSize; - } - } - - /* Look up the ID in the canonicalization map */ - for (j=0; j keywordAssign)) { - sink.Append("@", 1); - ++fieldCount; - ulocimp_getKeywords(tmpLocaleID+1, '@', sink, true, err); - } - } -} - -/* ### ID parsing API **************************************************/ - -U_CAPI int32_t U_EXPORT2 -uloc_getParent(const char* localeID, - char* parent, - int32_t parentCapacity, - UErrorCode* err) -{ - const char *lastUnderscore; - int32_t i; - - if (U_FAILURE(*err)) - return 0; - - if (localeID == NULL) - localeID = uloc_getDefault(); - - lastUnderscore=uprv_strrchr(localeID, '_'); - if(lastUnderscore!=NULL) { - i=(int32_t)(lastUnderscore-localeID); - } else { - i=0; - } - - if (i > 0) { - if (uprv_strnicmp(localeID, "und_", 4) == 0) { - localeID += 3; - i -= 3; - uprv_memmove(parent, localeID, uprv_min(i, parentCapacity)); - } else if (parent != localeID) { - uprv_memcpy(parent, localeID, uprv_min(i, parentCapacity)); - } - } - - return u_terminateChars(parent, parentCapacity, i, err); -} - -U_CAPI int32_t U_EXPORT2 -uloc_getLanguage(const char* localeID, - char* language, - int32_t languageCapacity, - UErrorCode* err) -{ - /* uloc_getLanguage will return a 2 character iso-639 code if one exists. *CWB*/ - - if (err==NULL || U_FAILURE(*err)) { - return 0; - } - - if(localeID==NULL) { - localeID=uloc_getDefault(); - } - - return ulocimp_getLanguage(localeID, NULL, *err).extract(language, languageCapacity, *err); -} - -U_CAPI int32_t U_EXPORT2 -uloc_getScript(const char* localeID, - char* script, - int32_t scriptCapacity, - UErrorCode* err) -{ - if(err==NULL || U_FAILURE(*err)) { - return 0; - } - - if(localeID==NULL) { - localeID=uloc_getDefault(); - } - - /* skip the language */ - ulocimp_getLanguage(localeID, &localeID, *err); - if (U_FAILURE(*err)) { - return 0; - } - - if(_isIDSeparator(*localeID)) { - return ulocimp_getScript(localeID+1, NULL, *err).extract(script, scriptCapacity, *err); - } - return u_terminateChars(script, scriptCapacity, 0, err); -} - -U_CAPI int32_t U_EXPORT2 -uloc_getCountry(const char* localeID, - char* country, - int32_t countryCapacity, - UErrorCode* err) -{ - if(err==NULL || U_FAILURE(*err)) { - return 0; - } - - if(localeID==NULL) { - localeID=uloc_getDefault(); - } - - /* Skip the language */ - ulocimp_getLanguage(localeID, &localeID, *err); - if (U_FAILURE(*err)) { - return 0; - } - - if(_isIDSeparator(*localeID)) { - const char *scriptID; - /* Skip the script if available */ - ulocimp_getScript(localeID+1, &scriptID, *err); - if (U_FAILURE(*err)) { - return 0; - } - if(scriptID != localeID+1) { - /* Found optional script */ - localeID = scriptID; - } - if(_isIDSeparator(*localeID)) { - return ulocimp_getCountry(localeID+1, NULL, *err).extract(country, countryCapacity, *err); - } - } - return u_terminateChars(country, countryCapacity, 0, err); -} - -U_CAPI int32_t U_EXPORT2 -uloc_getVariant(const char* localeID, - char* variant, - int32_t variantCapacity, - UErrorCode* err) -{ - char tempBuffer[ULOC_FULLNAME_CAPACITY]; - const char* tmpLocaleID; - int32_t i=0; - - if(err==NULL || U_FAILURE(*err)) { - return 0; - } - - if (_hasBCP47Extension(localeID)) { - tmpLocaleID =_ConvertBCP47(localeID, tempBuffer, sizeof(tempBuffer), err, nullptr); - } else { - if (localeID==NULL) { - localeID=uloc_getDefault(); - } - tmpLocaleID=localeID; - } - - /* Skip the language */ - ulocimp_getLanguage(tmpLocaleID, &tmpLocaleID, *err); - if (U_FAILURE(*err)) { - return 0; - } - - if(_isIDSeparator(*tmpLocaleID)) { - const char *scriptID; - /* Skip the script if available */ - ulocimp_getScript(tmpLocaleID+1, &scriptID, *err); - if (U_FAILURE(*err)) { - return 0; - } - if(scriptID != tmpLocaleID+1) { - /* Found optional script */ - tmpLocaleID = scriptID; - } - /* Skip the Country */ - if (_isIDSeparator(*tmpLocaleID)) { - const char *cntryID; - ulocimp_getCountry(tmpLocaleID+1, &cntryID, *err); - if (U_FAILURE(*err)) { - return 0; - } - if (cntryID != tmpLocaleID+1) { - /* Found optional country */ - tmpLocaleID = cntryID; - } - if(_isIDSeparator(*tmpLocaleID)) { - /* If there was no country ID, skip a possible extra IDSeparator */ - if (tmpLocaleID != cntryID && _isIDSeparator(tmpLocaleID[1])) { - tmpLocaleID++; - } - - CheckedArrayByteSink sink(variant, variantCapacity); - _getVariant(tmpLocaleID+1, *tmpLocaleID, sink, false); - - i = sink.NumberOfBytesAppended(); - - if (U_FAILURE(*err)) { - return i; - } - - if (sink.Overflowed()) { - *err = U_BUFFER_OVERFLOW_ERROR; - return i; - } - } - } - } - - return u_terminateChars(variant, variantCapacity, i, err); -} - -U_CAPI int32_t U_EXPORT2 -uloc_getName(const char* localeID, - char* name, - int32_t nameCapacity, - UErrorCode* err) -{ - if (U_FAILURE(*err)) { - return 0; - } - - CheckedArrayByteSink sink(name, nameCapacity); - ulocimp_getName(localeID, sink, err); - - int32_t reslen = sink.NumberOfBytesAppended(); - - if (U_FAILURE(*err)) { - return reslen; - } - - if (sink.Overflowed()) { - *err = U_BUFFER_OVERFLOW_ERROR; - } else { - u_terminateChars(name, nameCapacity, reslen, err); - } - - return reslen; -} - -U_CAPI void U_EXPORT2 -ulocimp_getName(const char* localeID, - ByteSink& sink, - UErrorCode* err) -{ - _canonicalize(localeID, sink, 0, err); -} - -U_CAPI int32_t U_EXPORT2 -uloc_getBaseName(const char* localeID, - char* name, - int32_t nameCapacity, - UErrorCode* err) -{ - if (U_FAILURE(*err)) { - return 0; - } - - CheckedArrayByteSink sink(name, nameCapacity); - ulocimp_getBaseName(localeID, sink, err); - - int32_t reslen = sink.NumberOfBytesAppended(); - - if (U_FAILURE(*err)) { - return reslen; - } - - if (sink.Overflowed()) { - *err = U_BUFFER_OVERFLOW_ERROR; - } else { - u_terminateChars(name, nameCapacity, reslen, err); - } - - return reslen; -} - -U_CAPI void U_EXPORT2 -ulocimp_getBaseName(const char* localeID, - ByteSink& sink, - UErrorCode* err) -{ - _canonicalize(localeID, sink, _ULOC_STRIP_KEYWORDS, err); -} - -U_CAPI int32_t U_EXPORT2 -uloc_canonicalize(const char* localeID, - char* name, - int32_t nameCapacity, - UErrorCode* err) -{ - if (U_FAILURE(*err)) { - return 0; - } - - CheckedArrayByteSink sink(name, nameCapacity); - ulocimp_canonicalize(localeID, sink, err); - - int32_t reslen = sink.NumberOfBytesAppended(); - - if (U_FAILURE(*err)) { - return reslen; - } - - if (sink.Overflowed()) { - *err = U_BUFFER_OVERFLOW_ERROR; - } else { - u_terminateChars(name, nameCapacity, reslen, err); - } - - return reslen; -} - -U_CAPI void U_EXPORT2 -ulocimp_canonicalize(const char* localeID, - ByteSink& sink, - UErrorCode* err) -{ - _canonicalize(localeID, sink, _ULOC_CANONICALIZE, err); -} - -U_CAPI const char* U_EXPORT2 -uloc_getISO3Language(const char* localeID) -{ - int16_t offset; - char lang[ULOC_LANG_CAPACITY]; - UErrorCode err = U_ZERO_ERROR; - - if (localeID == NULL) - { - localeID = uloc_getDefault(); - } - uloc_getLanguage(localeID, lang, ULOC_LANG_CAPACITY, &err); - if (U_FAILURE(err)) - return ""; - offset = _findIndex(LANGUAGES, lang); - if (offset < 0) - return ""; - return LANGUAGES_3[offset]; -} - -U_CAPI const char* U_EXPORT2 -uloc_getISO3Country(const char* localeID) -{ - int16_t offset; - char cntry[ULOC_LANG_CAPACITY]; - UErrorCode err = U_ZERO_ERROR; - - if (localeID == NULL) - { - localeID = uloc_getDefault(); - } - uloc_getCountry(localeID, cntry, ULOC_LANG_CAPACITY, &err); - if (U_FAILURE(err)) - return ""; - offset = _findIndex(COUNTRIES, cntry); - if (offset < 0) - return ""; - - return COUNTRIES_3[offset]; -} - -U_CAPI uint32_t U_EXPORT2 -uloc_getLCID(const char* localeID) -{ - UErrorCode status = U_ZERO_ERROR; - char langID[ULOC_FULLNAME_CAPACITY]; - uint32_t lcid = 0; - - /* Check for incomplete id. */ - if (!localeID || uprv_strlen(localeID) < 2) { - return 0; - } - - // First, attempt Windows platform lookup if available, but fall - // through to catch any special cases (ICU vs Windows name differences). - lcid = uprv_convertToLCIDPlatform(localeID, &status); - if (U_FAILURE(status)) { - return 0; - } - if (lcid > 0) { - // Windows found an LCID, return that - return lcid; - } - - uloc_getLanguage(localeID, langID, sizeof(langID), &status); - if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { - return 0; - } - - if (uprv_strchr(localeID, '@')) { - // uprv_convertToLCID does not support keywords other than collation. - // Remove all keywords except collation. - int32_t len; - char tmpLocaleID[ULOC_FULLNAME_CAPACITY]; - - CharString collVal; - { - CharStringByteSink sink(&collVal); - ulocimp_getKeywordValue(localeID, "collation", sink, &status); - } - - if (U_SUCCESS(status) && !collVal.isEmpty()) { - len = uloc_getBaseName(localeID, tmpLocaleID, - UPRV_LENGTHOF(tmpLocaleID) - 1, &status); - - if (U_SUCCESS(status) && len > 0) { - tmpLocaleID[len] = 0; - - len = uloc_setKeywordValue("collation", collVal.data(), tmpLocaleID, - UPRV_LENGTHOF(tmpLocaleID) - len - 1, &status); - - if (U_SUCCESS(status) && len > 0) { - tmpLocaleID[len] = 0; - return uprv_convertToLCID(langID, tmpLocaleID, &status); - } - } - } - - // fall through - all keywords are simply ignored - status = U_ZERO_ERROR; - } - - return uprv_convertToLCID(langID, localeID, &status); -} - -U_CAPI int32_t U_EXPORT2 -uloc_getLocaleForLCID(uint32_t hostid, char *locale, int32_t localeCapacity, - UErrorCode *status) -{ - return uprv_convertToPosix(hostid, locale, localeCapacity, status); -} - -/* ### Default locale **************************************************/ - -U_CAPI const char* U_EXPORT2 -uloc_getDefault() -{ - return locale_get_default(); -} - -U_CAPI void U_EXPORT2 -uloc_setDefault(const char* newDefaultLocale, - UErrorCode* err) -{ - if (U_FAILURE(*err)) - return; - /* the error code isn't currently used for anything by this function*/ - - /* propagate change to C++ */ - locale_set_default(newDefaultLocale); -} - -/** - * Returns a list of all 2-letter language codes defined in ISO 639. This is a pointer - * to an array of pointers to arrays of char. All of these pointers are owned - * by ICU-- do not delete them, and do not write through them. The array is - * terminated with a null pointer. - */ -U_CAPI const char* const* U_EXPORT2 -uloc_getISOLanguages() -{ - return LANGUAGES; -} - -/** - * Returns a list of all 2-letter country codes defined in ISO 639. This is a - * pointer to an array of pointers to arrays of char. All of these pointers are - * owned by ICU-- do not delete them, and do not write through them. The array is - * terminated with a null pointer. - */ -U_CAPI const char* const* U_EXPORT2 -uloc_getISOCountries() -{ - return COUNTRIES; -} - -U_CAPI const char* U_EXPORT2 -uloc_toUnicodeLocaleKey(const char* keyword) -{ - const char* bcpKey = ulocimp_toBcpKey(keyword); - if (bcpKey == NULL && ultag_isUnicodeLocaleKey(keyword, -1)) { - // unknown keyword, but syntax is fine.. - return keyword; - } - return bcpKey; -} - -U_CAPI const char* U_EXPORT2 -uloc_toUnicodeLocaleType(const char* keyword, const char* value) -{ - const char* bcpType = ulocimp_toBcpType(keyword, value, NULL, NULL); - if (bcpType == NULL && ultag_isUnicodeLocaleType(value, -1)) { - // unknown keyword, but syntax is fine.. - return value; - } - return bcpType; -} - -static UBool -isWellFormedLegacyKey(const char* legacyKey) -{ - const char* p = legacyKey; - while (*p) { - if (!UPRV_ISALPHANUM(*p)) { - return false; - } - p++; - } - return true; -} - -static UBool -isWellFormedLegacyType(const char* legacyType) -{ - const char* p = legacyType; - int32_t alphaNumLen = 0; - while (*p) { - if (*p == '_' || *p == '/' || *p == '-') { - if (alphaNumLen == 0) { - return false; - } - alphaNumLen = 0; - } else if (UPRV_ISALPHANUM(*p)) { - alphaNumLen++; - } else { - return false; - } - p++; - } - return (alphaNumLen != 0); -} - -U_CAPI const char* U_EXPORT2 -uloc_toLegacyKey(const char* keyword) -{ - const char* legacyKey = ulocimp_toLegacyKey(keyword); - if (legacyKey == NULL) { - // Checks if the specified locale key is well-formed with the legacy locale syntax. - // - // Note: - // LDML/CLDR provides some definition of keyword syntax in - // * http://www.unicode.org/reports/tr35/#Unicode_locale_identifier and - // * http://www.unicode.org/reports/tr35/#Old_Locale_Extension_Syntax - // Keys can only consist of [0-9a-zA-Z]. - if (isWellFormedLegacyKey(keyword)) { - return keyword; - } - } - return legacyKey; -} - -U_CAPI const char* U_EXPORT2 -uloc_toLegacyType(const char* keyword, const char* value) -{ - const char* legacyType = ulocimp_toLegacyType(keyword, value, NULL, NULL); - if (legacyType == NULL) { - // Checks if the specified locale type is well-formed with the legacy locale syntax. - // - // Note: - // LDML/CLDR provides some definition of keyword syntax in - // * http://www.unicode.org/reports/tr35/#Unicode_locale_identifier and - // * http://www.unicode.org/reports/tr35/#Old_Locale_Extension_Syntax - // Values (types) can only consist of [0-9a-zA-Z], plus for legacy values - // we allow [/_-+] in the middle (e.g. "Etc/GMT+1", "Asia/Tel_Aviv") - if (isWellFormedLegacyType(value)) { - return value; - } - } - return legacyType; -} - -/*eof*/ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1997-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* File ULOC.CPP +* +* Modification History: +* +* Date Name Description +* 04/01/97 aliu Creation. +* 08/21/98 stephen JDK 1.2 sync +* 12/08/98 rtg New Locale implementation and C API +* 03/15/99 damiba overhaul. +* 04/06/99 stephen changed setDefault() to realloc and copy +* 06/14/99 stephen Changed calls to ures_open for new params +* 07/21/99 stephen Modified setDefault() to propagate to C++ +* 05/14/04 alan 7 years later: refactored, cleaned up, fixed bugs, +* brought canonicalization code into line with spec +*****************************************************************************/ + +/* + POSIX's locale format, from putil.c: [no spaces] + + ll [ _CC ] [ . MM ] [ @ VV] + + l = lang, C = ctry, M = charmap, V = variant +*/ + +#include "unicode/bytestream.h" +#include "unicode/errorcode.h" +#include "unicode/stringpiece.h" +#include "unicode/utypes.h" +#include "unicode/ustring.h" +#include "unicode/uloc.h" + +#include "bytesinkutil.h" +#include "putilimp.h" +#include "ustr_imp.h" +#include "ulocimp.h" +#include "umutex.h" +#include "cstring.h" +#include "cmemory.h" +#include "locmap.h" +#include "uarrsort.h" +#include "uenumimp.h" +#include "uassert.h" +#include "charstr.h" + +U_NAMESPACE_USE + +/* ### Declarations **************************************************/ + +/* Locale stuff from locid.cpp */ +U_CFUNC void locale_set_default(const char *id); +U_CFUNC const char *locale_get_default(); + +/* ### Data tables **************************************************/ + +/** + * Table of language codes, both 2- and 3-letter, with preference + * given to 2-letter codes where possible. Includes 3-letter codes + * that lack a 2-letter equivalent. + * + * This list must be in sorted order. This list is returned directly + * to the user by some API. + * + * This list must be kept in sync with LANGUAGES_3, with corresponding + * entries matched. + * + * This table should be terminated with a nullptr entry, followed by a + * second list, and another nullptr entry. The first list is visible to + * user code when this array is returned by API. The second list + * contains codes we support, but do not expose through user API. + * + * Notes + * + * Tables updated per http://lcweb.loc.gov/standards/iso639-2/ to + * include the revisions up to 2001/7/27 *CWB* + * + * The 3 character codes are the terminology codes like RFC 3066. This + * is compatible with prior ICU codes + * + * "in" "iw" "ji" "jw" & "sh" have been withdrawn but are still in the + * table but now at the end of the table because 3 character codes are + * duplicates. This avoids bad searches going from 3 to 2 character + * codes. + * + * The range qaa-qtz is reserved for local use + */ +/* Generated using org.unicode.cldr.icu.GenerateISO639LanguageTables */ +/* ISO639 table version is 20150505 */ +/* Subsequent hand addition of selected languages */ +static const char * const LANGUAGES[] = { + "aa", "ab", "ace", "ach", "ada", "ady", "ae", "aeb", + "af", "afh", "agq", "ain", "ak", "akk", "akz", "ale", + "aln", "alt", "am", "an", "ang", "anp", "ar", "arc", + "arn", "aro", "arp", "arq", "ars", "arw", "ary", "arz", "as", + "asa", "ase", "ast", "av", "avk", "awa", "ay", "az", + "ba", "bal", "ban", "bar", "bas", "bax", "bbc", "bbj", + "be", "bej", "bem", "bew", "bez", "bfd", "bfq", "bg", + "bgc", "bgn", "bho", "bi", "bik", "bin", "bjn", "bkm", "bla", + "bm", "bn", "bo", "bpy", "bqi", "br", "bra", "brh", + "brx", "bs", "bss", "bua", "bug", "bum", "byn", "byv", + "ca", "cad", "car", "cay", "cch", "ccp", "ce", "ceb", "cgg", + "ch", "chb", "chg", "chk", "chm", "chn", "cho", "chp", + "chr", "chy", "ckb", "co", "cop", "cps", "cr", "crh", + "cs", "csb", "cu", "cv", "cy", + "da", "dak", "dar", "dav", "de", "del", "den", "dgr", + "din", "dje", "doi", "dsb", "dtp", "dua", "dum", "dv", + "dyo", "dyu", "dz", "dzg", + "ebu", "ee", "efi", "egl", "egy", "eka", "el", "elx", + "en", "enm", "eo", "es", "esu", "et", "eu", "ewo", + "ext", + "fa", "fan", "fat", "ff", "fi", "fil", "fit", "fj", + "fo", "fon", "fr", "frc", "frm", "fro", "frp", "frr", + "frs", "fur", "fy", + "ga", "gaa", "gag", "gan", "gay", "gba", "gbz", "gd", + "gez", "gil", "gl", "glk", "gmh", "gn", "goh", "gom", + "gon", "gor", "got", "grb", "grc", "gsw", "gu", "guc", + "gur", "guz", "gv", "gwi", + "ha", "hai", "hak", "haw", "he", "hi", "hif", "hil", + "hit", "hmn", "ho", "hr", "hsb", "hsn", "ht", "hu", + "hup", "hy", "hz", + "ia", "iba", "ibb", "id", "ie", "ig", "ii", "ik", + "ilo", "inh", "io", "is", "it", "iu", "izh", + "ja", "jam", "jbo", "jgo", "jmc", "jpr", "jrb", "jut", + "jv", + "ka", "kaa", "kab", "kac", "kaj", "kam", "kaw", "kbd", + "kbl", "kcg", "kde", "kea", "ken", "kfo", "kg", "kgp", + "kha", "kho", "khq", "khw", "ki", "kiu", "kj", "kk", + "kkj", "kl", "kln", "km", "kmb", "kn", "ko", "koi", + "kok", "kos", "kpe", "kr", "krc", "kri", "krj", "krl", + "kru", "ks", "ksb", "ksf", "ksh", "ku", "kum", "kut", + "kv", "kw", "ky", + "la", "lad", "lag", "lah", "lam", "lb", "lez", "lfn", + "lg", "li", "lij", "liv", "lkt", "lmo", "ln", "lo", + "lol", "loz", "lrc", "lt", "ltg", "lu", "lua", "lui", + "lun", "luo", "lus", "luy", "lv", "lzh", "lzz", + "mad", "maf", "mag", "mai", "mak", "man", "mas", "mde", + "mdf", "mdh", "mdr", "men", "mer", "mfe", "mg", "mga", + "mgh", "mgo", "mh", "mi", "mic", "min", "mis", "mk", + "ml", "mn", "mnc", "mni", + "moh", "mos", "mr", "mrj", + "ms", "mt", "mua", "mul", "mus", "mwl", "mwr", "mwv", + "my", "mye", "myv", "mzn", + "na", "nan", "nap", "naq", "nb", "nd", "nds", "ne", + "new", "ng", "nia", "niu", "njo", "nl", "nmg", "nn", + "nnh", "no", "nog", "non", "nov", "nqo", "nr", "nso", + "nus", "nv", "nwc", "ny", "nym", "nyn", "nyo", "nzi", + "oc", "oj", "om", "or", "os", "osa", "ota", + "pa", "pag", "pal", "pam", "pap", "pau", "pcd", "pcm", "pdc", + "pdt", "peo", "pfl", "phn", "pi", "pl", "pms", "pnt", + "pon", "prg", "pro", "ps", "pt", + "qu", "quc", "qug", + "raj", "rap", "rar", "rgn", "rif", "rm", "rn", "ro", + "rof", "rom", "rtm", "ru", "rue", "rug", "rup", + "rw", "rwk", + "sa", "sad", "sah", "sam", "saq", "sas", "sat", "saz", + "sba", "sbp", "sc", "scn", "sco", "sd", "sdc", "sdh", + "se", "see", "seh", "sei", "sel", "ses", "sg", "sga", + "sgs", "shi", "shn", "shu", "si", "sid", "sk", + "sl", "sli", "sly", "sm", "sma", "smj", "smn", "sms", + "sn", "snk", "so", "sog", "sq", "sr", "srn", "srr", + "ss", "ssy", "st", "stq", "su", "suk", "sus", "sux", + "sv", "sw", "swb", "syc", "syr", "szl", + "ta", "tcy", "te", "tem", "teo", "ter", "tet", "tg", + "th", "ti", "tig", "tiv", "tk", "tkl", "tkr", + "tlh", "tli", "tly", "tmh", "tn", "to", "tog", "tpi", + "tr", "tru", "trv", "ts", "tsd", "tsi", "tt", "ttt", + "tum", "tvl", "tw", "twq", "ty", "tyv", "tzm", + "udm", "ug", "uga", "uk", "umb", "und", "ur", "uz", + "vai", "ve", "vec", "vep", "vi", "vls", "vmf", "vo", + "vot", "vro", "vun", + "wa", "wae", "wal", "war", "was", "wbp", "wo", "wuu", + "xal", "xh", "xmf", "xog", + "yao", "yap", "yav", "ybb", "yi", "yo", "yrl", "yue", + "za", "zap", "zbl", "zea", "zen", "zgh", "zh", "zu", + "zun", "zxx", "zza", +nullptr, + "in", "iw", "ji", "jw", "mo", "sh", "swc", "tl", /* obsolete language codes */ +nullptr +}; + +static const char* const DEPRECATED_LANGUAGES[]={ + "in", "iw", "ji", "jw", "mo", nullptr, nullptr +}; +static const char* const REPLACEMENT_LANGUAGES[]={ + "id", "he", "yi", "jv", "ro", nullptr, nullptr +}; + +/** + * Table of 3-letter language codes. + * + * This is a lookup table used to convert 3-letter language codes to + * their 2-letter equivalent, where possible. It must be kept in sync + * with LANGUAGES. For all valid i, LANGUAGES[i] must refer to the + * same language as LANGUAGES_3[i]. The commented-out lines are + * copied from LANGUAGES to make eyeballing this baby easier. + * + * Where a 3-letter language code has no 2-letter equivalent, the + * 3-letter code occupies both LANGUAGES[i] and LANGUAGES_3[i]. + * + * This table should be terminated with a nullptr entry, followed by a + * second list, and another nullptr entry. The two lists correspond to + * the two lists in LANGUAGES. + */ +/* Generated using org.unicode.cldr.icu.GenerateISO639LanguageTables */ +/* ISO639 table version is 20150505 */ +/* Subsequent hand addition of selected languages */ +static const char * const LANGUAGES_3[] = { + "aar", "abk", "ace", "ach", "ada", "ady", "ave", "aeb", + "afr", "afh", "agq", "ain", "aka", "akk", "akz", "ale", + "aln", "alt", "amh", "arg", "ang", "anp", "ara", "arc", + "arn", "aro", "arp", "arq", "ars", "arw", "ary", "arz", "asm", + "asa", "ase", "ast", "ava", "avk", "awa", "aym", "aze", + "bak", "bal", "ban", "bar", "bas", "bax", "bbc", "bbj", + "bel", "bej", "bem", "bew", "bez", "bfd", "bfq", "bul", + "bgc", "bgn", "bho", "bis", "bik", "bin", "bjn", "bkm", "bla", + "bam", "ben", "bod", "bpy", "bqi", "bre", "bra", "brh", + "brx", "bos", "bss", "bua", "bug", "bum", "byn", "byv", + "cat", "cad", "car", "cay", "cch", "ccp", "che", "ceb", "cgg", + "cha", "chb", "chg", "chk", "chm", "chn", "cho", "chp", + "chr", "chy", "ckb", "cos", "cop", "cps", "cre", "crh", + "ces", "csb", "chu", "chv", "cym", + "dan", "dak", "dar", "dav", "deu", "del", "den", "dgr", + "din", "dje", "doi", "dsb", "dtp", "dua", "dum", "div", + "dyo", "dyu", "dzo", "dzg", + "ebu", "ewe", "efi", "egl", "egy", "eka", "ell", "elx", + "eng", "enm", "epo", "spa", "esu", "est", "eus", "ewo", + "ext", + "fas", "fan", "fat", "ful", "fin", "fil", "fit", "fij", + "fao", "fon", "fra", "frc", "frm", "fro", "frp", "frr", + "frs", "fur", "fry", + "gle", "gaa", "gag", "gan", "gay", "gba", "gbz", "gla", + "gez", "gil", "glg", "glk", "gmh", "grn", "goh", "gom", + "gon", "gor", "got", "grb", "grc", "gsw", "guj", "guc", + "gur", "guz", "glv", "gwi", + "hau", "hai", "hak", "haw", "heb", "hin", "hif", "hil", + "hit", "hmn", "hmo", "hrv", "hsb", "hsn", "hat", "hun", + "hup", "hye", "her", + "ina", "iba", "ibb", "ind", "ile", "ibo", "iii", "ipk", + "ilo", "inh", "ido", "isl", "ita", "iku", "izh", + "jpn", "jam", "jbo", "jgo", "jmc", "jpr", "jrb", "jut", + "jav", + "kat", "kaa", "kab", "kac", "kaj", "kam", "kaw", "kbd", + "kbl", "kcg", "kde", "kea", "ken", "kfo", "kon", "kgp", + "kha", "kho", "khq", "khw", "kik", "kiu", "kua", "kaz", + "kkj", "kal", "kln", "khm", "kmb", "kan", "kor", "koi", + "kok", "kos", "kpe", "kau", "krc", "kri", "krj", "krl", + "kru", "kas", "ksb", "ksf", "ksh", "kur", "kum", "kut", + "kom", "cor", "kir", + "lat", "lad", "lag", "lah", "lam", "ltz", "lez", "lfn", + "lug", "lim", "lij", "liv", "lkt", "lmo", "lin", "lao", + "lol", "loz", "lrc", "lit", "ltg", "lub", "lua", "lui", + "lun", "luo", "lus", "luy", "lav", "lzh", "lzz", + "mad", "maf", "mag", "mai", "mak", "man", "mas", "mde", + "mdf", "mdh", "mdr", "men", "mer", "mfe", "mlg", "mga", + "mgh", "mgo", "mah", "mri", "mic", "min", "mis", "mkd", + "mal", "mon", "mnc", "mni", + "moh", "mos", "mar", "mrj", + "msa", "mlt", "mua", "mul", "mus", "mwl", "mwr", "mwv", + "mya", "mye", "myv", "mzn", + "nau", "nan", "nap", "naq", "nob", "nde", "nds", "nep", + "new", "ndo", "nia", "niu", "njo", "nld", "nmg", "nno", + "nnh", "nor", "nog", "non", "nov", "nqo", "nbl", "nso", + "nus", "nav", "nwc", "nya", "nym", "nyn", "nyo", "nzi", + "oci", "oji", "orm", "ori", "oss", "osa", "ota", + "pan", "pag", "pal", "pam", "pap", "pau", "pcd", "pcm", "pdc", + "pdt", "peo", "pfl", "phn", "pli", "pol", "pms", "pnt", + "pon", "prg", "pro", "pus", "por", + "que", "quc", "qug", + "raj", "rap", "rar", "rgn", "rif", "roh", "run", "ron", + "rof", "rom", "rtm", "rus", "rue", "rug", "rup", + "kin", "rwk", + "san", "sad", "sah", "sam", "saq", "sas", "sat", "saz", + "sba", "sbp", "srd", "scn", "sco", "snd", "sdc", "sdh", + "sme", "see", "seh", "sei", "sel", "ses", "sag", "sga", + "sgs", "shi", "shn", "shu", "sin", "sid", "slk", + "slv", "sli", "sly", "smo", "sma", "smj", "smn", "sms", + "sna", "snk", "som", "sog", "sqi", "srp", "srn", "srr", + "ssw", "ssy", "sot", "stq", "sun", "suk", "sus", "sux", + "swe", "swa", "swb", "syc", "syr", "szl", + "tam", "tcy", "tel", "tem", "teo", "ter", "tet", "tgk", + "tha", "tir", "tig", "tiv", "tuk", "tkl", "tkr", + "tlh", "tli", "tly", "tmh", "tsn", "ton", "tog", "tpi", + "tur", "tru", "trv", "tso", "tsd", "tsi", "tat", "ttt", + "tum", "tvl", "twi", "twq", "tah", "tyv", "tzm", + "udm", "uig", "uga", "ukr", "umb", "und", "urd", "uzb", + "vai", "ven", "vec", "vep", "vie", "vls", "vmf", "vol", + "vot", "vro", "vun", + "wln", "wae", "wal", "war", "was", "wbp", "wol", "wuu", + "xal", "xho", "xmf", "xog", + "yao", "yap", "yav", "ybb", "yid", "yor", "yrl", "yue", + "zha", "zap", "zbl", "zea", "zen", "zgh", "zho", "zul", + "zun", "zxx", "zza", +nullptr, +/* "in", "iw", "ji", "jw", "mo", "sh", "swc", "tl", */ + "ind", "heb", "yid", "jaw", "mol", "srp", "swc", "tgl", +nullptr +}; + +/** + * Table of 2-letter country codes. + * + * This list must be in sorted order. This list is returned directly + * to the user by some API. + * + * This list must be kept in sync with COUNTRIES_3, with corresponding + * entries matched. + * + * This table should be terminated with a nullptr entry, followed by a + * second list, and another nullptr entry. The first list is visible to + * user code when this array is returned by API. The second list + * contains codes we support, but do not expose through user API. + * + * Notes: + * + * ZR(ZAR) is now CD(COD) and FX(FXX) is PS(PSE) as per + * http://www.evertype.com/standards/iso3166/iso3166-1-en.html added + * new codes keeping the old ones for compatibility updated to include + * 1999/12/03 revisions *CWB* + * + * RO(ROM) is now RO(ROU) according to + * http://www.iso.org/iso/en/prods-services/iso3166ma/03updates-on-iso-3166/nlv3e-rou.html + */ +static const char * const COUNTRIES[] = { + "AD", "AE", "AF", "AG", "AI", "AL", "AM", + "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", + "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", + "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BV", + "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", + "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CQ", "CR", + "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DG", "DJ", "DK", + "DM", "DO", "DZ", "EA", "EC", "EE", "EG", "EH", "ER", + "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", + "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", + "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", + "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", + "IC", "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", + "IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", + "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", + "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", + "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", + "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", + "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", + "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", + "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", + "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", + "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA", + "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", + "SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SV", + "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", + "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", + "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", + "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", + "WS", "XK", "YE", "YT", "ZA", "ZM", "ZW", +nullptr, + "AN", "BU", "CS", "FX", "RO", "SU", "TP", "YD", "YU", "ZR", /* obsolete country codes */ +nullptr +}; + +static const char* const DEPRECATED_COUNTRIES[] = { + "AN", "BU", "CS", "DD", "DY", "FX", "HV", "NH", "RH", "SU", "TP", "UK", "VD", "YD", "YU", "ZR", nullptr, nullptr /* deprecated country list */ +}; +static const char* const REPLACEMENT_COUNTRIES[] = { +/* "AN", "BU", "CS", "DD", "DY", "FX", "HV", "NH", "RH", "SU", "TP", "UK", "VD", "YD", "YU", "ZR" */ + "CW", "MM", "RS", "DE", "BJ", "FR", "BF", "VU", "ZW", "RU", "TL", "GB", "VN", "YE", "RS", "CD", nullptr, nullptr /* replacement country codes */ +}; + +/** + * Table of 3-letter country codes. + * + * This is a lookup table used to convert 3-letter country codes to + * their 2-letter equivalent. It must be kept in sync with COUNTRIES. + * For all valid i, COUNTRIES[i] must refer to the same country as + * COUNTRIES_3[i]. The commented-out lines are copied from COUNTRIES + * to make eyeballing this baby easier. + * + * This table should be terminated with a nullptr entry, followed by a + * second list, and another nullptr entry. The two lists correspond to + * the two lists in COUNTRIES. + */ +static const char * const COUNTRIES_3[] = { +/* "AD", "AE", "AF", "AG", "AI", "AL", "AM", */ + "AND", "ARE", "AFG", "ATG", "AIA", "ALB", "ARM", +/* "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", */ + "AGO", "ATA", "ARG", "ASM", "AUT", "AUS", "ABW", "ALA", "AZE", +/* "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", */ + "BIH", "BRB", "BGD", "BEL", "BFA", "BGR", "BHR", "BDI", +/* "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BV", */ + "BEN", "BLM", "BMU", "BRN", "BOL", "BES", "BRA", "BHS", "BTN", "BVT", +/* "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", */ + "BWA", "BLR", "BLZ", "CAN", "CCK", "COD", "CAF", "COG", +/* "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CQ", "CR", */ + "CHE", "CIV", "COK", "CHL", "CMR", "CHN", "COL", "CRQ", "CRI", +/* "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DG", "DJ", "DK", */ + "CUB", "CPV", "CUW", "CXR", "CYP", "CZE", "DEU", "DGA", "DJI", "DNK", +/* "DM", "DO", "DZ", "EA", "EC", "EE", "EG", "EH", "ER", */ + "DMA", "DOM", "DZA", "XEA", "ECU", "EST", "EGY", "ESH", "ERI", +/* "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", */ + "ESP", "ETH", "FIN", "FJI", "FLK", "FSM", "FRO", "FRA", +/* "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", */ + "GAB", "GBR", "GRD", "GEO", "GUF", "GGY", "GHA", "GIB", "GRL", +/* "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", */ + "GMB", "GIN", "GLP", "GNQ", "GRC", "SGS", "GTM", "GUM", +/* "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", */ + "GNB", "GUY", "HKG", "HMD", "HND", "HRV", "HTI", "HUN", +/* "IC", "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS" */ + "XIC", "IDN", "IRL", "ISR", "IMN", "IND", "IOT", "IRQ", "IRN", "ISL", +/* "IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", */ + "ITA", "JEY", "JAM", "JOR", "JPN", "KEN", "KGZ", "KHM", "KIR", +/* "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", */ + "COM", "KNA", "PRK", "KOR", "KWT", "CYM", "KAZ", "LAO", +/* "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", */ + "LBN", "LCA", "LIE", "LKA", "LBR", "LSO", "LTU", "LUX", +/* "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", */ + "LVA", "LBY", "MAR", "MCO", "MDA", "MNE", "MAF", "MDG", "MHL", "MKD", +/* "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", */ + "MLI", "MMR", "MNG", "MAC", "MNP", "MTQ", "MRT", "MSR", +/* "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", */ + "MLT", "MUS", "MDV", "MWI", "MEX", "MYS", "MOZ", "NAM", +/* "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", */ + "NCL", "NER", "NFK", "NGA", "NIC", "NLD", "NOR", "NPL", +/* "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", */ + "NRU", "NIU", "NZL", "OMN", "PAN", "PER", "PYF", "PNG", +/* "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", */ + "PHL", "PAK", "POL", "SPM", "PCN", "PRI", "PSE", "PRT", +/* "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA", */ + "PLW", "PRY", "QAT", "REU", "ROU", "SRB", "RUS", "RWA", "SAU", +/* "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", */ + "SLB", "SYC", "SDN", "SWE", "SGP", "SHN", "SVN", "SJM", +/* "SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SV", */ + "SVK", "SLE", "SMR", "SEN", "SOM", "SUR", "SSD", "STP", "SLV", +/* "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", */ + "SXM", "SYR", "SWZ", "TCA", "TCD", "ATF", "TGO", "THA", "TJK", +/* "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", */ + "TKL", "TLS", "TKM", "TUN", "TON", "TUR", "TTO", "TUV", +/* "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", */ + "TWN", "TZA", "UKR", "UGA", "UMI", "USA", "URY", "UZB", +/* "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", */ + "VAT", "VCT", "VEN", "VGB", "VIR", "VNM", "VUT", "WLF", +/* "WS", "XK", "YE", "YT", "ZA", "ZM", "ZW", */ + "WSM", "XKK", "YEM", "MYT", "ZAF", "ZMB", "ZWE", +nullptr, +/* "AN", "BU", "CS", "FX", "RO", "SU", "TP", "YD", "YU", "ZR" */ + "ANT", "BUR", "SCG", "FXX", "ROM", "SUN", "TMP", "YMD", "YUG", "ZAR", +nullptr +}; + +typedef struct CanonicalizationMap { + const char *id; /* input ID */ + const char *canonicalID; /* canonicalized output ID */ +} CanonicalizationMap; + +/** + * A map to canonicalize locale IDs. This handles a variety of + * different semantic kinds of transformations. + */ +static const CanonicalizationMap CANONICALIZE_MAP[] = { + { "art__LOJBAN", "jbo" }, /* registered name */ + { "hy__AREVELA", "hy" }, /* Registered IANA variant */ + { "hy__AREVMDA", "hyw" }, /* Registered IANA variant */ + { "zh__GUOYU", "zh" }, /* registered name */ + { "zh__HAKKA", "hak" }, /* registered name */ + { "zh__XIANG", "hsn" }, /* registered name */ + // subtags with 3 chars won't be treated as variants. + { "zh_GAN", "gan" }, /* registered name */ + { "zh_MIN_NAN", "nan" }, /* registered name */ + { "zh_WUU", "wuu" }, /* registered name */ + { "zh_YUE", "yue" }, /* registered name */ +}; + +/* ### BCP47 Conversion *******************************************/ +/* Test if the locale id has BCP47 u extension and does not have '@' */ +#define _hasBCP47Extension(id) (id && uprv_strstr(id, "@") == nullptr && getShortestSubtagLength(localeID) == 1) +/* Converts the BCP47 id to Unicode id. Does nothing to id if conversion fails */ +static const char* _ConvertBCP47( + const char* id, char* buffer, int32_t length, + UErrorCode* err, int32_t* pLocaleIdSize) { + const char* finalID; + int32_t localeIDSize = uloc_forLanguageTag(id, buffer, length, nullptr, err); + if (localeIDSize <= 0 || U_FAILURE(*err) || *err == U_STRING_NOT_TERMINATED_WARNING) { + finalID=id; + if (*err == U_STRING_NOT_TERMINATED_WARNING) { + *err = U_BUFFER_OVERFLOW_ERROR; + } + } else { + finalID=buffer; + } + if (pLocaleIdSize != nullptr) { + *pLocaleIdSize = localeIDSize; + } + return finalID; +} +/* Gets the size of the shortest subtag in the given localeID. */ +static int32_t getShortestSubtagLength(const char *localeID) { + int32_t localeIDLength = static_cast(uprv_strlen(localeID)); + int32_t length = localeIDLength; + int32_t tmpLength = 0; + int32_t i; + UBool reset = true; + + for (i = 0; i < localeIDLength; i++) { + if (localeID[i] != '_' && localeID[i] != '-') { + if (reset) { + tmpLength = 0; + reset = false; + } + tmpLength++; + } else { + if (tmpLength != 0 && tmpLength < length) { + length = tmpLength; + } + reset = true; + } + } + + return length; +} + +/* ### Keywords **************************************************/ +#define UPRV_ISDIGIT(c) (((c) >= '0') && ((c) <= '9')) +#define UPRV_ISALPHANUM(c) (uprv_isASCIILetter(c) || UPRV_ISDIGIT(c) ) +/* Punctuation/symbols allowed in legacy key values */ +#define UPRV_OK_VALUE_PUNCTUATION(c) ((c) == '_' || (c) == '-' || (c) == '+' || (c) == '/') + +#define ULOC_KEYWORD_BUFFER_LEN 25 +#define ULOC_MAX_NO_KEYWORDS 25 + +U_CAPI const char * U_EXPORT2 +locale_getKeywordsStart(const char *localeID) { + const char *result = nullptr; + if((result = uprv_strchr(localeID, '@')) != nullptr) { + return result; + } +#if (U_CHARSET_FAMILY == U_EBCDIC_FAMILY) + else { + /* We do this because the @ sign is variant, and the @ sign used on one + EBCDIC machine won't be compiled the same way on other EBCDIC based + machines. */ + static const uint8_t ebcdicSigns[] = { 0x7C, 0x44, 0x66, 0x80, 0xAC, 0xAE, 0xAF, 0xB5, 0xEC, 0xEF, 0x00 }; + const uint8_t *charToFind = ebcdicSigns; + while(*charToFind) { + if((result = uprv_strchr(localeID, *charToFind)) != nullptr) { + return result; + } + charToFind++; + } + } +#endif + return nullptr; +} + +/** + * @param buf buffer of size [ULOC_KEYWORD_BUFFER_LEN] + * @param keywordName incoming name to be canonicalized + * @param status return status (keyword too long) + * @return length of the keyword name + */ +static int32_t locale_canonKeywordName(char *buf, const char *keywordName, UErrorCode *status) +{ + int32_t keywordNameLen = 0; + + for (; *keywordName != 0; keywordName++) { + if (!UPRV_ISALPHANUM(*keywordName)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed keyword name */ + return 0; + } + if (keywordNameLen < ULOC_KEYWORD_BUFFER_LEN - 1) { + buf[keywordNameLen++] = uprv_tolower(*keywordName); + } else { + /* keyword name too long for internal buffer */ + *status = U_INTERNAL_PROGRAM_ERROR; + return 0; + } + } + if (keywordNameLen == 0) { + *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty keyword name */ + return 0; + } + buf[keywordNameLen] = 0; /* terminate */ + + return keywordNameLen; +} + +typedef struct { + char keyword[ULOC_KEYWORD_BUFFER_LEN]; + int32_t keywordLen; + const char *valueStart; + int32_t valueLen; +} KeywordStruct; + +static int32_t U_CALLCONV +compareKeywordStructs(const void * /*context*/, const void *left, const void *right) { + const char* leftString = ((const KeywordStruct *)left)->keyword; + const char* rightString = ((const KeywordStruct *)right)->keyword; + return uprv_strcmp(leftString, rightString); +} + +U_CFUNC void +ulocimp_getKeywords(const char *localeID, + char prev, + ByteSink& sink, + UBool valuesToo, + UErrorCode *status) +{ + KeywordStruct keywordList[ULOC_MAX_NO_KEYWORDS]; + + int32_t maxKeywords = ULOC_MAX_NO_KEYWORDS; + int32_t numKeywords = 0; + const char* pos = localeID; + const char* equalSign = nullptr; + const char* semicolon = nullptr; + int32_t i = 0, j, n; + + if(prev == '@') { /* start of keyword definition */ + /* we will grab pairs, trim spaces, lowercase keywords, sort and return */ + do { + UBool duplicate = false; + /* skip leading spaces */ + while(*pos == ' ') { + pos++; + } + if (!*pos) { /* handle trailing "; " */ + break; + } + if(numKeywords == maxKeywords) { + *status = U_INTERNAL_PROGRAM_ERROR; + return; + } + equalSign = uprv_strchr(pos, '='); + semicolon = uprv_strchr(pos, ';'); + /* lack of '=' [foo@currency] is illegal */ + /* ';' before '=' [foo@currency;collation=pinyin] is illegal */ + if(!equalSign || (semicolon && semicolon= ULOC_KEYWORD_BUFFER_LEN) { + /* keyword name too long for internal buffer */ + *status = U_INTERNAL_PROGRAM_ERROR; + return; + } + for(i = 0, n = 0; i < equalSign - pos; ++i) { + if (pos[i] != ' ') { + keywordList[numKeywords].keyword[n++] = uprv_tolower(pos[i]); + } + } + + /* zero-length keyword is an error. */ + if (n == 0) { + *status = U_INVALID_FORMAT_ERROR; + return; + } + + keywordList[numKeywords].keyword[n] = 0; + keywordList[numKeywords].keywordLen = n; + /* now grab the value part. First we skip the '=' */ + equalSign++; + /* then we leading spaces */ + while(*equalSign == ' ') { + equalSign++; + } + + /* Premature end or zero-length value */ + if (!*equalSign || equalSign == semicolon) { + *status = U_INVALID_FORMAT_ERROR; + return; + } + + keywordList[numKeywords].valueStart = equalSign; + + pos = semicolon; + i = 0; + if(pos) { + while(*(pos - i - 1) == ' ') { + i++; + } + keywordList[numKeywords].valueLen = (int32_t)(pos - equalSign - i); + pos++; + } else { + i = (int32_t)uprv_strlen(equalSign); + while(i && equalSign[i-1] == ' ') { + i--; + } + keywordList[numKeywords].valueLen = i; + } + /* If this is a duplicate keyword, then ignore it */ + for (j=0; j startSearchHere && *(keyValueTail-1) == ' ') { + keyValueTail--; + } + /* now keyValueTail points to first char after the keyName */ + /* copy & normalize keyName from locale */ + if (startSearchHere == keyValueTail) { + *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty keyword name in passed-in locale */ + return; + } + keyValueLen = 0; + while (startSearchHere < keyValueTail) { + if (!UPRV_ISALPHANUM(*startSearchHere)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed keyword name */ + return; + } + if (keyValueLen < ULOC_KEYWORD_BUFFER_LEN - 1) { + localeKeywordNameBuffer[keyValueLen++] = uprv_tolower(*startSearchHere++); + } else { + /* keyword name too long for internal buffer */ + *status = U_INTERNAL_PROGRAM_ERROR; + return; + } + } + localeKeywordNameBuffer[keyValueLen] = 0; /* terminate */ + + startSearchHere = uprv_strchr(nextSeparator, ';'); + + if(uprv_strcmp(keywordNameBuffer, localeKeywordNameBuffer) == 0) { + /* current entry matches the keyword. */ + nextSeparator++; /* skip '=' */ + /* First strip leading & trailing spaces (TC decided to tolerate these) */ + while(*nextSeparator == ' ') { + nextSeparator++; + } + keyValueTail = (startSearchHere)? startSearchHere: nextSeparator + uprv_strlen(nextSeparator); + while(keyValueTail > nextSeparator && *(keyValueTail-1) == ' ') { + keyValueTail--; + } + /* Now copy the value, but check well-formedness */ + if (nextSeparator == keyValueTail) { + *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty key value name in passed-in locale */ + return; + } + while (nextSeparator < keyValueTail) { + if (!UPRV_ISALPHANUM(*nextSeparator) && !UPRV_OK_VALUE_PUNCTUATION(*nextSeparator)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed key value */ + return; + } + /* Should we lowercase value to return here? Tests expect as-is. */ + sink.Append(nextSeparator++, 1); + } + return; + } + } + } +} + +U_CAPI int32_t U_EXPORT2 +uloc_setKeywordValue(const char* keywordName, + const char* keywordValue, + char* buffer, int32_t bufferCapacity, + UErrorCode* status) +{ + /* TODO: sorting. removal. */ + int32_t keywordNameLen; + int32_t keywordValueLen; + int32_t bufLen; + int32_t needLen = 0; + char keywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN]; + char keywordValueBuffer[ULOC_KEYWORDS_CAPACITY+1]; + char localeKeywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN]; + int32_t rc; + char* nextSeparator = nullptr; + char* nextEqualsign = nullptr; + char* startSearchHere = nullptr; + char* keywordStart = nullptr; + CharString updatedKeysAndValues; + UBool handledInputKeyAndValue = false; + char keyValuePrefix = '@'; + + if(U_FAILURE(*status)) { + return -1; + } + if (*status == U_STRING_NOT_TERMINATED_WARNING) { + *status = U_ZERO_ERROR; + } + if (keywordName == nullptr || keywordName[0] == 0 || bufferCapacity <= 1) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + bufLen = (int32_t)uprv_strlen(buffer); + if(bufferCapacity= bufferCapacity) { + *status = U_BUFFER_OVERFLOW_ERROR; + return needLen; /* no change */ + } + *startSearchHere++ = '@'; + uprv_strcpy(startSearchHere, keywordNameBuffer); + startSearchHere += keywordNameLen; + *startSearchHere++ = '='; + uprv_strcpy(startSearchHere, keywordValueBuffer); + U_ASSERT(*status != U_STRING_NOT_TERMINATED_WARNING); + return needLen; + } /* end shortcut - no @ */ + + keywordStart = startSearchHere; + /* search for keyword */ + while(keywordStart) { + const char* keyValueTail; + int32_t keyValueLen; + + keywordStart++; /* skip @ or ; */ + nextEqualsign = uprv_strchr(keywordStart, '='); + if (!nextEqualsign) { + *status = U_ILLEGAL_ARGUMENT_ERROR; /* key must have =value */ + return 0; + } + /* strip leading & trailing spaces (TC decided to tolerate these) */ + while(*keywordStart == ' ') { + keywordStart++; + } + keyValueTail = nextEqualsign; + while (keyValueTail > keywordStart && *(keyValueTail-1) == ' ') { + keyValueTail--; + } + /* now keyValueTail points to first char after the keyName */ + /* copy & normalize keyName from locale */ + if (keywordStart == keyValueTail) { + *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty keyword name in passed-in locale */ + return 0; + } + keyValueLen = 0; + while (keywordStart < keyValueTail) { + if (!UPRV_ISALPHANUM(*keywordStart)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed keyword name */ + return 0; + } + if (keyValueLen < ULOC_KEYWORD_BUFFER_LEN - 1) { + localeKeywordNameBuffer[keyValueLen++] = uprv_tolower(*keywordStart++); + } else { + /* keyword name too long for internal buffer */ + *status = U_INTERNAL_PROGRAM_ERROR; + return 0; + } + } + localeKeywordNameBuffer[keyValueLen] = 0; /* terminate */ + + nextSeparator = uprv_strchr(nextEqualsign, ';'); + + /* start processing the value part */ + nextEqualsign++; /* skip '=' */ + /* First strip leading & trailing spaces (TC decided to tolerate these) */ + while(*nextEqualsign == ' ') { + nextEqualsign++; + } + keyValueTail = (nextSeparator)? nextSeparator: nextEqualsign + uprv_strlen(nextEqualsign); + while(keyValueTail > nextEqualsign && *(keyValueTail-1) == ' ') { + keyValueTail--; + } + if (nextEqualsign == keyValueTail) { + *status = U_ILLEGAL_ARGUMENT_ERROR; /* empty key value in passed-in locale */ + return 0; + } + + rc = uprv_strcmp(keywordNameBuffer, localeKeywordNameBuffer); + if(rc == 0) { + /* Current entry matches the input keyword. Update the entry */ + if(keywordValueLen > 0) { /* updating a value */ + updatedKeysAndValues.append(keyValuePrefix, *status); + keyValuePrefix = ';'; /* for any subsequent key-value pair */ + updatedKeysAndValues.append(keywordNameBuffer, keywordNameLen, *status); + updatedKeysAndValues.append('=', *status); + updatedKeysAndValues.append(keywordValueBuffer, keywordValueLen, *status); + } /* else removing this entry, don't emit anything */ + handledInputKeyAndValue = true; + } else { + /* input keyword sorts earlier than current entry, add before current entry */ + if (rc < 0 && keywordValueLen > 0 && !handledInputKeyAndValue) { + /* insert new entry at this location */ + updatedKeysAndValues.append(keyValuePrefix, *status); + keyValuePrefix = ';'; /* for any subsequent key-value pair */ + updatedKeysAndValues.append(keywordNameBuffer, keywordNameLen, *status); + updatedKeysAndValues.append('=', *status); + updatedKeysAndValues.append(keywordValueBuffer, keywordValueLen, *status); + handledInputKeyAndValue = true; + } + /* copy the current entry */ + updatedKeysAndValues.append(keyValuePrefix, *status); + keyValuePrefix = ';'; /* for any subsequent key-value pair */ + updatedKeysAndValues.append(localeKeywordNameBuffer, keyValueLen, *status); + updatedKeysAndValues.append('=', *status); + updatedKeysAndValues.append(nextEqualsign, static_cast(keyValueTail-nextEqualsign), *status); + } + if (!nextSeparator && keywordValueLen > 0 && !handledInputKeyAndValue) { + /* append new entry at the end, it sorts later than existing entries */ + updatedKeysAndValues.append(keyValuePrefix, *status); + /* skip keyValuePrefix update, no subsequent key-value pair */ + updatedKeysAndValues.append(keywordNameBuffer, keywordNameLen, *status); + updatedKeysAndValues.append('=', *status); + updatedKeysAndValues.append(keywordValueBuffer, keywordValueLen, *status); + handledInputKeyAndValue = true; + } + keywordStart = nextSeparator; + } /* end loop searching */ + + /* Any error from updatedKeysAndValues.append above would be internal and not due to + * problems with the passed-in locale. So if we did encounter problems with the + * passed-in locale above, those errors took precedence and overrode any error + * status from updatedKeysAndValues.append, and also caused a return of 0. If there + * are errors here they are from updatedKeysAndValues.append; they do cause an + * error return but the passed-in locale is unmodified and the original bufLen is + * returned. + */ + if (!handledInputKeyAndValue || U_FAILURE(*status)) { + /* if input key/value specified removal of a keyword not present in locale, or + * there was an error in CharString.append, leave original locale alone. */ + U_ASSERT(*status != U_STRING_NOT_TERMINATED_WARNING); + return bufLen; + } + + // needLen = length of the part before '@' + needLen = (int32_t)(startSearchHere - buffer); + // Check to see can we fit the startSearchHere, if not, return + // U_BUFFER_OVERFLOW_ERROR without copy updatedKeysAndValues into it. + // We do this because this API function does not behave like most others: + // It promises never to set a U_STRING_NOT_TERMINATED_WARNING. + // When the contents fits but without the terminating NUL, in this case we need to not change + // the buffer contents and return with a buffer overflow error. + int32_t appendLength = updatedKeysAndValues.length(); + if (appendLength >= bufferCapacity - needLen) { + *status = U_BUFFER_OVERFLOW_ERROR; + return needLen + appendLength; + } + needLen += updatedKeysAndValues.extract( + startSearchHere, bufferCapacity - needLen, *status); + U_ASSERT(*status != U_STRING_NOT_TERMINATED_WARNING); + return needLen; +} + +/* ### ID parsing implementation **************************************************/ + +#define _isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I')) + +/*returns true if one of the special prefixes is here (s=string) + 'x-' or 'i-' */ +#define _isIDPrefix(s) (_isPrefixLetter(s[0])&&_isIDSeparator(s[1])) + +/* Dot terminates it because of POSIX form where dot precedes the codepage + * except for variant + */ +#define _isTerminator(a) ((a==0)||(a=='.')||(a=='@')) + +/** + * Lookup 'key' in the array 'list'. The array 'list' should contain + * a nullptr entry, followed by more entries, and a second nullptr entry. + * + * The 'list' param should be LANGUAGES, LANGUAGES_3, COUNTRIES, or + * COUNTRIES_3. + */ +static int16_t _findIndex(const char* const* list, const char* key) +{ + const char* const* anchor = list; + int32_t pass = 0; + + /* Make two passes through two nullptr-terminated arrays at 'list' */ + while (pass++ < 2) { + while (*list) { + if (uprv_strcmp(key, *list) == 0) { + return (int16_t)(list - anchor); + } + list++; + } + ++list; /* skip final nullptr *CWB*/ + } + return -1; +} + +U_CFUNC const char* +uloc_getCurrentCountryID(const char* oldID){ + int32_t offset = _findIndex(DEPRECATED_COUNTRIES, oldID); + if (offset >= 0) { + return REPLACEMENT_COUNTRIES[offset]; + } + return oldID; +} +U_CFUNC const char* +uloc_getCurrentLanguageID(const char* oldID){ + int32_t offset = _findIndex(DEPRECATED_LANGUAGES, oldID); + if (offset >= 0) { + return REPLACEMENT_LANGUAGES[offset]; + } + return oldID; +} +/* + * the internal functions _getLanguage(), _getCountry(), _getVariant() + * avoid duplicating code to handle the earlier locale ID pieces + * in the functions for the later ones by + * setting the *pEnd pointer to where they stopped parsing + * + * TODO try to use this in Locale + */ +CharString U_EXPORT2 +ulocimp_getLanguage(const char *localeID, + const char **pEnd, + UErrorCode &status) { + CharString result; + + if (uprv_stricmp(localeID, "root") == 0) { + localeID += 4; + } else if (uprv_strnicmp(localeID, "und", 3) == 0 && + (localeID[3] == '\0' || + localeID[3] == '-' || + localeID[3] == '_' || + localeID[3] == '@')) { + localeID += 3; + } + + /* if it starts with i- or x- then copy that prefix */ + if(_isIDPrefix(localeID)) { + result.append((char)uprv_tolower(*localeID), status); + result.append('-', status); + localeID+=2; + } + + /* copy the language as far as possible and count its length */ + while(!_isTerminator(*localeID) && !_isIDSeparator(*localeID)) { + result.append((char)uprv_tolower(*localeID), status); + localeID++; + } + + if(result.length()==3) { + /* convert 3 character code to 2 character code if possible *CWB*/ + int32_t offset = _findIndex(LANGUAGES_3, result.data()); + if(offset>=0) { + result.clear(); + result.append(LANGUAGES[offset], status); + } + } + + if(pEnd!=nullptr) { + *pEnd=localeID; + } + + return result; +} + +CharString U_EXPORT2 +ulocimp_getScript(const char *localeID, + const char **pEnd, + UErrorCode &status) { + CharString result; + int32_t idLen = 0; + + if (pEnd != nullptr) { + *pEnd = localeID; + } + + /* copy the second item as far as possible and count its length */ + while(!_isTerminator(localeID[idLen]) && !_isIDSeparator(localeID[idLen]) + && uprv_isASCIILetter(localeID[idLen])) { + idLen++; + } + + /* If it's exactly 4 characters long, then it's a script and not a country. */ + if (idLen == 4) { + int32_t i; + if (pEnd != nullptr) { + *pEnd = localeID+idLen; + } + if (idLen >= 1) { + result.append((char)uprv_toupper(*(localeID++)), status); + } + for (i = 1; i < idLen; i++) { + result.append((char)uprv_tolower(*(localeID++)), status); + } + } + + return result; +} + +CharString U_EXPORT2 +ulocimp_getCountry(const char *localeID, + const char **pEnd, + UErrorCode &status) { + CharString result; + int32_t idLen=0; + + /* copy the country as far as possible and count its length */ + while(!_isTerminator(localeID[idLen]) && !_isIDSeparator(localeID[idLen])) { + result.append((char)uprv_toupper(localeID[idLen]), status); + idLen++; + } + + /* the country should be either length 2 or 3 */ + if (idLen == 2 || idLen == 3) { + /* convert 3 character code to 2 character code if possible *CWB*/ + if(idLen==3) { + int32_t offset = _findIndex(COUNTRIES_3, result.data()); + if(offset>=0) { + result.clear(); + result.append(COUNTRIES[offset], status); + } + } + localeID+=idLen; + } else { + result.clear(); + } + + if(pEnd!=nullptr) { + *pEnd=localeID; + } + + return result; +} + +/** + * @param needSeparator if true, then add leading '_' if any variants + * are added to 'variant' + */ +static void +_getVariant(const char *localeID, + char prev, + ByteSink& sink, + UBool needSeparator) { + UBool hasVariant = false; + + /* get one or more variant tags and separate them with '_' */ + if(_isIDSeparator(prev)) { + /* get a variant string after a '-' or '_' */ + while(!_isTerminator(*localeID)) { + if (needSeparator) { + sink.Append("_", 1); + needSeparator = false; + } + char c = (char)uprv_toupper(*localeID); + if (c == '-') c = '_'; + sink.Append(&c, 1); + hasVariant = true; + localeID++; + } + } + + /* if there is no variant tag after a '-' or '_' then look for '@' */ + if(!hasVariant) { + if(prev=='@') { + /* keep localeID */ + } else if((localeID=locale_getKeywordsStart(localeID))!=nullptr) { + ++localeID; /* point after the '@' */ + } else { + return; + } + while(!_isTerminator(*localeID)) { + if (needSeparator) { + sink.Append("_", 1); + needSeparator = false; + } + char c = (char)uprv_toupper(*localeID); + if (c == '-' || c == ',') c = '_'; + sink.Append(&c, 1); + localeID++; + } + } +} + +/* Keyword enumeration */ + +typedef struct UKeywordsContext { + char* keywords; + char* current; +} UKeywordsContext; + +U_CDECL_BEGIN + +static void U_CALLCONV +uloc_kw_closeKeywords(UEnumeration *enumerator) { + uprv_free(((UKeywordsContext *)enumerator->context)->keywords); + uprv_free(enumerator->context); + uprv_free(enumerator); +} + +static int32_t U_CALLCONV +uloc_kw_countKeywords(UEnumeration *en, UErrorCode * /*status*/) { + char *kw = ((UKeywordsContext *)en->context)->keywords; + int32_t result = 0; + while(*kw) { + result++; + kw += uprv_strlen(kw)+1; + } + return result; +} + +static const char * U_CALLCONV +uloc_kw_nextKeyword(UEnumeration* en, + int32_t* resultLength, + UErrorCode* /*status*/) { + const char* result = ((UKeywordsContext *)en->context)->current; + int32_t len = 0; + if(*result) { + len = (int32_t)uprv_strlen(((UKeywordsContext *)en->context)->current); + ((UKeywordsContext *)en->context)->current += len+1; + } else { + result = nullptr; + } + if (resultLength) { + *resultLength = len; + } + return result; +} + +static void U_CALLCONV +uloc_kw_resetKeywords(UEnumeration* en, + UErrorCode* /*status*/) { + ((UKeywordsContext *)en->context)->current = ((UKeywordsContext *)en->context)->keywords; +} + +U_CDECL_END + + +static const UEnumeration gKeywordsEnum = { + nullptr, + nullptr, + uloc_kw_closeKeywords, + uloc_kw_countKeywords, + uenum_unextDefault, + uloc_kw_nextKeyword, + uloc_kw_resetKeywords +}; + +U_CAPI UEnumeration* U_EXPORT2 +uloc_openKeywordList(const char *keywordList, int32_t keywordListSize, UErrorCode* status) +{ + LocalMemory myContext; + LocalMemory result; + + if (U_FAILURE(*status)) { + return nullptr; + } + myContext.adoptInstead(static_cast(uprv_malloc(sizeof(UKeywordsContext)))); + result.adoptInstead(static_cast(uprv_malloc(sizeof(UEnumeration)))); + if (myContext.isNull() || result.isNull()) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memcpy(result.getAlias(), &gKeywordsEnum, sizeof(UEnumeration)); + myContext->keywords = static_cast(uprv_malloc(keywordListSize+1)); + if (myContext->keywords == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memcpy(myContext->keywords, keywordList, keywordListSize); + myContext->keywords[keywordListSize] = 0; + myContext->current = myContext->keywords; + result->context = myContext.orphan(); + return result.orphan(); +} + +U_CAPI UEnumeration* U_EXPORT2 +uloc_openKeywords(const char* localeID, + UErrorCode* status) +{ + char tempBuffer[ULOC_FULLNAME_CAPACITY]; + const char* tmpLocaleID; + + if(status==nullptr || U_FAILURE(*status)) { + return 0; + } + + if (_hasBCP47Extension(localeID)) { + tmpLocaleID = _ConvertBCP47(localeID, tempBuffer, + sizeof(tempBuffer), status, nullptr); + } else { + if (localeID==nullptr) { + localeID=uloc_getDefault(); + } + tmpLocaleID=localeID; + } + + /* Skip the language */ + ulocimp_getLanguage(tmpLocaleID, &tmpLocaleID, *status); + if (U_FAILURE(*status)) { + return 0; + } + + if(_isIDSeparator(*tmpLocaleID)) { + const char *scriptID; + /* Skip the script if available */ + ulocimp_getScript(tmpLocaleID+1, &scriptID, *status); + if (U_FAILURE(*status)) { + return 0; + } + if(scriptID != tmpLocaleID+1) { + /* Found optional script */ + tmpLocaleID = scriptID; + } + /* Skip the Country */ + if (_isIDSeparator(*tmpLocaleID)) { + ulocimp_getCountry(tmpLocaleID+1, &tmpLocaleID, *status); + if (U_FAILURE(*status)) { + return 0; + } + } + } + + /* keywords are located after '@' */ + if((tmpLocaleID = locale_getKeywordsStart(tmpLocaleID)) != nullptr) { + CharString keywords; + CharStringByteSink sink(&keywords); + ulocimp_getKeywords(tmpLocaleID+1, '@', sink, false, status); + if (U_FAILURE(*status)) { + return nullptr; + } + return uloc_openKeywordList(keywords.data(), keywords.length(), status); + } + return nullptr; +} + + +/* bit-flags for 'options' parameter of _canonicalize */ +#define _ULOC_STRIP_KEYWORDS 0x2 +#define _ULOC_CANONICALIZE 0x1 + +#define OPTION_SET(options, mask) ((options & mask) != 0) + +static const char i_default[] = {'i', '-', 'd', 'e', 'f', 'a', 'u', 'l', 't'}; +#define I_DEFAULT_LENGTH UPRV_LENGTHOF(i_default) + +/** + * Canonicalize the given localeID, to level 1 or to level 2, + * depending on the options. To specify level 1, pass in options=0. + * To specify level 2, pass in options=_ULOC_CANONICALIZE. + * + * This is the code underlying uloc_getName and uloc_canonicalize. + */ +static void +_canonicalize(const char* localeID, + ByteSink& sink, + uint32_t options, + UErrorCode* err) { + if (U_FAILURE(*err)) { + return; + } + + int32_t j, fieldCount=0, scriptSize=0, variantSize=0; + PreflightingLocaleIDBuffer tempBuffer; // if localeID has a BCP47 extension, tmpLocaleID points to this + CharString localeIDWithHyphens; // if localeID has a BPC47 extension and have _, tmpLocaleID points to this + const char* origLocaleID; + const char* tmpLocaleID; + const char* keywordAssign = nullptr; + const char* separatorIndicator = nullptr; + + if (_hasBCP47Extension(localeID)) { + const char* localeIDPtr = localeID; + + // convert all underbars to hyphens, unless the "BCP47 extension" comes at the beginning of the string + if (uprv_strchr(localeID, '_') != nullptr && localeID[1] != '-' && localeID[1] != '_') { + localeIDWithHyphens.append(localeID, -1, *err); + if (U_SUCCESS(*err)) { + for (char* p = localeIDWithHyphens.data(); *p != '\0'; ++p) { + if (*p == '_') { + *p = '-'; + } + } + localeIDPtr = localeIDWithHyphens.data(); + } + } + + do { + // After this call tmpLocaleID may point to localeIDPtr which may + // point to either localeID or localeIDWithHyphens.data(). + tmpLocaleID = _ConvertBCP47(localeIDPtr, tempBuffer.getBuffer(), + tempBuffer.getCapacity(), err, + &(tempBuffer.requestedCapacity)); + } while (tempBuffer.needToTryAgain(err)); + } else { + if (localeID==nullptr) { + localeID=uloc_getDefault(); + } + tmpLocaleID=localeID; + } + + origLocaleID=tmpLocaleID; + + /* get all pieces, one after another, and separate with '_' */ + CharString tag = ulocimp_getLanguage(tmpLocaleID, &tmpLocaleID, *err); + + if (tag.length() == I_DEFAULT_LENGTH && + uprv_strncmp(origLocaleID, i_default, I_DEFAULT_LENGTH) == 0) { + tag.clear(); + tag.append(uloc_getDefault(), *err); + } else if(_isIDSeparator(*tmpLocaleID)) { + const char *scriptID; + + ++fieldCount; + tag.append('_', *err); + + CharString script = ulocimp_getScript(tmpLocaleID+1, &scriptID, *err); + tag.append(script, *err); + scriptSize = script.length(); + if(scriptSize > 0) { + /* Found optional script */ + tmpLocaleID = scriptID; + ++fieldCount; + if (_isIDSeparator(*tmpLocaleID)) { + /* If there is something else, then we add the _ */ + tag.append('_', *err); + } + } + + if (_isIDSeparator(*tmpLocaleID)) { + const char *cntryID; + + CharString country = ulocimp_getCountry(tmpLocaleID+1, &cntryID, *err); + tag.append(country, *err); + if (!country.isEmpty()) { + /* Found optional country */ + tmpLocaleID = cntryID; + } + if(_isIDSeparator(*tmpLocaleID)) { + /* If there is something else, then we add the _ if we found country before. */ + if (!_isIDSeparator(*(tmpLocaleID+1))) { + ++fieldCount; + tag.append('_', *err); + } + + variantSize = -tag.length(); + { + CharStringByteSink s(&tag); + _getVariant(tmpLocaleID+1, *tmpLocaleID, s, false); + } + variantSize += tag.length(); + if (variantSize > 0) { + tmpLocaleID += variantSize + 1; /* skip '_' and variant */ + } + } + } + } + + /* Copy POSIX-style charset specifier, if any [mr.utf8] */ + if (!OPTION_SET(options, _ULOC_CANONICALIZE) && *tmpLocaleID == '.') { + UBool done = false; + do { + char c = *tmpLocaleID; + switch (c) { + case 0: + case '@': + done = true; + break; + default: + tag.append(c, *err); + ++tmpLocaleID; + break; + } + } while (!done); + } + + /* Scan ahead to next '@' and determine if it is followed by '=' and/or ';' + After this, tmpLocaleID either points to '@' or is nullptr */ + if ((tmpLocaleID=locale_getKeywordsStart(tmpLocaleID))!=nullptr) { + keywordAssign = uprv_strchr(tmpLocaleID, '='); + separatorIndicator = uprv_strchr(tmpLocaleID, ';'); + } + + /* Copy POSIX-style variant, if any [mr@FOO] */ + if (!OPTION_SET(options, _ULOC_CANONICALIZE) && + tmpLocaleID != nullptr && keywordAssign == nullptr) { + for (;;) { + char c = *tmpLocaleID; + if (c == 0) { + break; + } + tag.append(c, *err); + ++tmpLocaleID; + } + } + + if (OPTION_SET(options, _ULOC_CANONICALIZE)) { + /* Handle @FOO variant if @ is present and not followed by = */ + if (tmpLocaleID!=nullptr && keywordAssign==nullptr) { + /* Add missing '_' if needed */ + if (fieldCount < 2 || (fieldCount < 3 && scriptSize > 0)) { + do { + tag.append('_', *err); + ++fieldCount; + } while(fieldCount<2); + } + + int32_t posixVariantSize = -tag.length(); + { + CharStringByteSink s(&tag); + _getVariant(tmpLocaleID+1, '@', s, (UBool)(variantSize > 0)); + } + posixVariantSize += tag.length(); + if (posixVariantSize > 0) { + variantSize += posixVariantSize; + } + } + + /* Look up the ID in the canonicalization map */ + for (j=0; j keywordAssign)) { + sink.Append("@", 1); + ++fieldCount; + ulocimp_getKeywords(tmpLocaleID+1, '@', sink, true, err); + } + } +} + +/* ### ID parsing API **************************************************/ + +U_CAPI int32_t U_EXPORT2 +uloc_getParent(const char* localeID, + char* parent, + int32_t parentCapacity, + UErrorCode* err) +{ + const char *lastUnderscore; + int32_t i; + + if (U_FAILURE(*err)) + return 0; + + if (localeID == nullptr) + localeID = uloc_getDefault(); + + lastUnderscore=uprv_strrchr(localeID, '_'); + if(lastUnderscore!=nullptr) { + i=(int32_t)(lastUnderscore-localeID); + } else { + i=0; + } + + if (i > 0) { + if (uprv_strnicmp(localeID, "und_", 4) == 0) { + localeID += 3; + i -= 3; + uprv_memmove(parent, localeID, uprv_min(i, parentCapacity)); + } else if (parent != localeID) { + uprv_memcpy(parent, localeID, uprv_min(i, parentCapacity)); + } + } + + return u_terminateChars(parent, parentCapacity, i, err); +} + +U_CAPI int32_t U_EXPORT2 +uloc_getLanguage(const char* localeID, + char* language, + int32_t languageCapacity, + UErrorCode* err) +{ + /* uloc_getLanguage will return a 2 character iso-639 code if one exists. *CWB*/ + + if (err==nullptr || U_FAILURE(*err)) { + return 0; + } + + if(localeID==nullptr) { + localeID=uloc_getDefault(); + } + + return ulocimp_getLanguage(localeID, nullptr, *err).extract(language, languageCapacity, *err); +} + +U_CAPI int32_t U_EXPORT2 +uloc_getScript(const char* localeID, + char* script, + int32_t scriptCapacity, + UErrorCode* err) +{ + if(err==nullptr || U_FAILURE(*err)) { + return 0; + } + + if(localeID==nullptr) { + localeID=uloc_getDefault(); + } + + /* skip the language */ + ulocimp_getLanguage(localeID, &localeID, *err); + if (U_FAILURE(*err)) { + return 0; + } + + if(_isIDSeparator(*localeID)) { + return ulocimp_getScript(localeID+1, nullptr, *err).extract(script, scriptCapacity, *err); + } + return u_terminateChars(script, scriptCapacity, 0, err); +} + +U_CAPI int32_t U_EXPORT2 +uloc_getCountry(const char* localeID, + char* country, + int32_t countryCapacity, + UErrorCode* err) +{ + if(err==nullptr || U_FAILURE(*err)) { + return 0; + } + + if(localeID==nullptr) { + localeID=uloc_getDefault(); + } + + /* Skip the language */ + ulocimp_getLanguage(localeID, &localeID, *err); + if (U_FAILURE(*err)) { + return 0; + } + + if(_isIDSeparator(*localeID)) { + const char *scriptID; + /* Skip the script if available */ + ulocimp_getScript(localeID+1, &scriptID, *err); + if (U_FAILURE(*err)) { + return 0; + } + if(scriptID != localeID+1) { + /* Found optional script */ + localeID = scriptID; + } + if(_isIDSeparator(*localeID)) { + return ulocimp_getCountry(localeID+1, nullptr, *err).extract(country, countryCapacity, *err); + } + } + return u_terminateChars(country, countryCapacity, 0, err); +} + +U_CAPI int32_t U_EXPORT2 +uloc_getVariant(const char* localeID, + char* variant, + int32_t variantCapacity, + UErrorCode* err) +{ + char tempBuffer[ULOC_FULLNAME_CAPACITY]; + const char* tmpLocaleID; + int32_t i=0; + + if(err==nullptr || U_FAILURE(*err)) { + return 0; + } + + if (_hasBCP47Extension(localeID)) { + tmpLocaleID =_ConvertBCP47(localeID, tempBuffer, sizeof(tempBuffer), err, nullptr); + } else { + if (localeID==nullptr) { + localeID=uloc_getDefault(); + } + tmpLocaleID=localeID; + } + + /* Skip the language */ + ulocimp_getLanguage(tmpLocaleID, &tmpLocaleID, *err); + if (U_FAILURE(*err)) { + return 0; + } + + if(_isIDSeparator(*tmpLocaleID)) { + const char *scriptID; + /* Skip the script if available */ + ulocimp_getScript(tmpLocaleID+1, &scriptID, *err); + if (U_FAILURE(*err)) { + return 0; + } + if(scriptID != tmpLocaleID+1) { + /* Found optional script */ + tmpLocaleID = scriptID; + } + /* Skip the Country */ + if (_isIDSeparator(*tmpLocaleID)) { + const char *cntryID; + ulocimp_getCountry(tmpLocaleID+1, &cntryID, *err); + if (U_FAILURE(*err)) { + return 0; + } + if (cntryID != tmpLocaleID+1) { + /* Found optional country */ + tmpLocaleID = cntryID; + } + if(_isIDSeparator(*tmpLocaleID)) { + /* If there was no country ID, skip a possible extra IDSeparator */ + if (tmpLocaleID != cntryID && _isIDSeparator(tmpLocaleID[1])) { + tmpLocaleID++; + } + + CheckedArrayByteSink sink(variant, variantCapacity); + _getVariant(tmpLocaleID+1, *tmpLocaleID, sink, false); + + i = sink.NumberOfBytesAppended(); + + if (U_FAILURE(*err)) { + return i; + } + + if (sink.Overflowed()) { + *err = U_BUFFER_OVERFLOW_ERROR; + return i; + } + } + } + } + + return u_terminateChars(variant, variantCapacity, i, err); +} + +U_CAPI int32_t U_EXPORT2 +uloc_getName(const char* localeID, + char* name, + int32_t nameCapacity, + UErrorCode* err) +{ + if (U_FAILURE(*err)) { + return 0; + } + + CheckedArrayByteSink sink(name, nameCapacity); + ulocimp_getName(localeID, sink, err); + + int32_t reslen = sink.NumberOfBytesAppended(); + + if (U_FAILURE(*err)) { + return reslen; + } + + if (sink.Overflowed()) { + *err = U_BUFFER_OVERFLOW_ERROR; + } else { + u_terminateChars(name, nameCapacity, reslen, err); + } + + return reslen; +} + +U_CAPI void U_EXPORT2 +ulocimp_getName(const char* localeID, + ByteSink& sink, + UErrorCode* err) +{ + _canonicalize(localeID, sink, 0, err); +} + +U_CAPI int32_t U_EXPORT2 +uloc_getBaseName(const char* localeID, + char* name, + int32_t nameCapacity, + UErrorCode* err) +{ + if (U_FAILURE(*err)) { + return 0; + } + + CheckedArrayByteSink sink(name, nameCapacity); + ulocimp_getBaseName(localeID, sink, err); + + int32_t reslen = sink.NumberOfBytesAppended(); + + if (U_FAILURE(*err)) { + return reslen; + } + + if (sink.Overflowed()) { + *err = U_BUFFER_OVERFLOW_ERROR; + } else { + u_terminateChars(name, nameCapacity, reslen, err); + } + + return reslen; +} + +U_CAPI void U_EXPORT2 +ulocimp_getBaseName(const char* localeID, + ByteSink& sink, + UErrorCode* err) +{ + _canonicalize(localeID, sink, _ULOC_STRIP_KEYWORDS, err); +} + +U_CAPI int32_t U_EXPORT2 +uloc_canonicalize(const char* localeID, + char* name, + int32_t nameCapacity, + UErrorCode* err) +{ + if (U_FAILURE(*err)) { + return 0; + } + + CheckedArrayByteSink sink(name, nameCapacity); + ulocimp_canonicalize(localeID, sink, err); + + int32_t reslen = sink.NumberOfBytesAppended(); + + if (U_FAILURE(*err)) { + return reslen; + } + + if (sink.Overflowed()) { + *err = U_BUFFER_OVERFLOW_ERROR; + } else { + u_terminateChars(name, nameCapacity, reslen, err); + } + + return reslen; +} + +U_CAPI void U_EXPORT2 +ulocimp_canonicalize(const char* localeID, + ByteSink& sink, + UErrorCode* err) +{ + _canonicalize(localeID, sink, _ULOC_CANONICALIZE, err); +} + +U_CAPI const char* U_EXPORT2 +uloc_getISO3Language(const char* localeID) +{ + int16_t offset; + char lang[ULOC_LANG_CAPACITY]; + UErrorCode err = U_ZERO_ERROR; + + if (localeID == nullptr) + { + localeID = uloc_getDefault(); + } + uloc_getLanguage(localeID, lang, ULOC_LANG_CAPACITY, &err); + if (U_FAILURE(err)) + return ""; + offset = _findIndex(LANGUAGES, lang); + if (offset < 0) + return ""; + return LANGUAGES_3[offset]; +} + +U_CAPI const char* U_EXPORT2 +uloc_getISO3Country(const char* localeID) +{ + int16_t offset; + char cntry[ULOC_LANG_CAPACITY]; + UErrorCode err = U_ZERO_ERROR; + + if (localeID == nullptr) + { + localeID = uloc_getDefault(); + } + uloc_getCountry(localeID, cntry, ULOC_LANG_CAPACITY, &err); + if (U_FAILURE(err)) + return ""; + offset = _findIndex(COUNTRIES, cntry); + if (offset < 0) + return ""; + + return COUNTRIES_3[offset]; +} + +U_CAPI uint32_t U_EXPORT2 +uloc_getLCID(const char* localeID) +{ + UErrorCode status = U_ZERO_ERROR; + char langID[ULOC_FULLNAME_CAPACITY]; + uint32_t lcid = 0; + + /* Check for incomplete id. */ + if (!localeID || uprv_strlen(localeID) < 2) { + return 0; + } + + // First, attempt Windows platform lookup if available, but fall + // through to catch any special cases (ICU vs Windows name differences). + lcid = uprv_convertToLCIDPlatform(localeID, &status); + if (U_FAILURE(status)) { + return 0; + } + if (lcid > 0) { + // Windows found an LCID, return that + return lcid; + } + + uloc_getLanguage(localeID, langID, sizeof(langID), &status); + if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { + return 0; + } + + if (uprv_strchr(localeID, '@')) { + // uprv_convertToLCID does not support keywords other than collation. + // Remove all keywords except collation. + int32_t len; + char tmpLocaleID[ULOC_FULLNAME_CAPACITY]; + + CharString collVal; + { + CharStringByteSink sink(&collVal); + ulocimp_getKeywordValue(localeID, "collation", sink, &status); + } + + if (U_SUCCESS(status) && !collVal.isEmpty()) { + len = uloc_getBaseName(localeID, tmpLocaleID, + UPRV_LENGTHOF(tmpLocaleID) - 1, &status); + + if (U_SUCCESS(status) && len > 0) { + tmpLocaleID[len] = 0; + + len = uloc_setKeywordValue("collation", collVal.data(), tmpLocaleID, + UPRV_LENGTHOF(tmpLocaleID) - len - 1, &status); + + if (U_SUCCESS(status) && len > 0) { + tmpLocaleID[len] = 0; + return uprv_convertToLCID(langID, tmpLocaleID, &status); + } + } + } + + // fall through - all keywords are simply ignored + status = U_ZERO_ERROR; + } + + return uprv_convertToLCID(langID, localeID, &status); +} + +U_CAPI int32_t U_EXPORT2 +uloc_getLocaleForLCID(uint32_t hostid, char *locale, int32_t localeCapacity, + UErrorCode *status) +{ + return uprv_convertToPosix(hostid, locale, localeCapacity, status); +} + +/* ### Default locale **************************************************/ + +U_CAPI const char* U_EXPORT2 +uloc_getDefault() +{ + return locale_get_default(); +} + +U_CAPI void U_EXPORT2 +uloc_setDefault(const char* newDefaultLocale, + UErrorCode* err) +{ + if (U_FAILURE(*err)) + return; + /* the error code isn't currently used for anything by this function*/ + + /* propagate change to C++ */ + locale_set_default(newDefaultLocale); +} + +/** + * Returns a list of all 2-letter language codes defined in ISO 639. This is a pointer + * to an array of pointers to arrays of char. All of these pointers are owned + * by ICU-- do not delete them, and do not write through them. The array is + * terminated with a null pointer. + */ +U_CAPI const char* const* U_EXPORT2 +uloc_getISOLanguages() +{ + return LANGUAGES; +} + +/** + * Returns a list of all 2-letter country codes defined in ISO 639. This is a + * pointer to an array of pointers to arrays of char. All of these pointers are + * owned by ICU-- do not delete them, and do not write through them. The array is + * terminated with a null pointer. + */ +U_CAPI const char* const* U_EXPORT2 +uloc_getISOCountries() +{ + return COUNTRIES; +} + +U_CAPI const char* U_EXPORT2 +uloc_toUnicodeLocaleKey(const char* keyword) +{ + const char* bcpKey = ulocimp_toBcpKey(keyword); + if (bcpKey == nullptr && ultag_isUnicodeLocaleKey(keyword, -1)) { + // unknown keyword, but syntax is fine.. + return keyword; + } + return bcpKey; +} + +U_CAPI const char* U_EXPORT2 +uloc_toUnicodeLocaleType(const char* keyword, const char* value) +{ + const char* bcpType = ulocimp_toBcpType(keyword, value, nullptr, nullptr); + if (bcpType == nullptr && ultag_isUnicodeLocaleType(value, -1)) { + // unknown keyword, but syntax is fine.. + return value; + } + return bcpType; +} + +static UBool +isWellFormedLegacyKey(const char* legacyKey) +{ + const char* p = legacyKey; + while (*p) { + if (!UPRV_ISALPHANUM(*p)) { + return false; + } + p++; + } + return true; +} + +static UBool +isWellFormedLegacyType(const char* legacyType) +{ + const char* p = legacyType; + int32_t alphaNumLen = 0; + while (*p) { + if (*p == '_' || *p == '/' || *p == '-') { + if (alphaNumLen == 0) { + return false; + } + alphaNumLen = 0; + } else if (UPRV_ISALPHANUM(*p)) { + alphaNumLen++; + } else { + return false; + } + p++; + } + return (alphaNumLen != 0); +} + +U_CAPI const char* U_EXPORT2 +uloc_toLegacyKey(const char* keyword) +{ + const char* legacyKey = ulocimp_toLegacyKey(keyword); + if (legacyKey == nullptr) { + // Checks if the specified locale key is well-formed with the legacy locale syntax. + // + // Note: + // LDML/CLDR provides some definition of keyword syntax in + // * http://www.unicode.org/reports/tr35/#Unicode_locale_identifier and + // * http://www.unicode.org/reports/tr35/#Old_Locale_Extension_Syntax + // Keys can only consist of [0-9a-zA-Z]. + if (isWellFormedLegacyKey(keyword)) { + return keyword; + } + } + return legacyKey; +} + +U_CAPI const char* U_EXPORT2 +uloc_toLegacyType(const char* keyword, const char* value) +{ + const char* legacyType = ulocimp_toLegacyType(keyword, value, nullptr, nullptr); + if (legacyType == nullptr) { + // Checks if the specified locale type is well-formed with the legacy locale syntax. + // + // Note: + // LDML/CLDR provides some definition of keyword syntax in + // * http://www.unicode.org/reports/tr35/#Unicode_locale_identifier and + // * http://www.unicode.org/reports/tr35/#Old_Locale_Extension_Syntax + // Values (types) can only consist of [0-9a-zA-Z], plus for legacy values + // we allow [/_-+] in the middle (e.g. "Etc/GMT+1", "Asia/Tel_Aviv") + if (isWellFormedLegacyType(value)) { + return value; + } + } + return legacyType; +} + +/*eof*/ diff --git a/deps/icu-small/source/common/uloc_keytype.cpp b/deps/icu-small/source/common/uloc_keytype.cpp index 12dc3004924541..4aa22821103ae5 100644 --- a/deps/icu-small/source/common/uloc_keytype.cpp +++ b/deps/icu-small/source/common/uloc_keytype.cpp @@ -1,536 +1,536 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2014-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ -#include - -#include "unicode/utypes.h" -#include "unicode/unistr.h" -#include "unicode/uobject.h" - -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "uassert.h" -#include "ucln_cmn.h" -#include "uhash.h" -#include "umutex.h" -#include "uresimp.h" -#include "uvector.h" -#include "udataswp.h" /* for InvChar functions */ - -static UHashtable* gLocExtKeyMap = NULL; -static icu::UInitOnce gLocExtKeyMapInitOnce {}; - -// bit flags for special types -typedef enum { - SPECIALTYPE_NONE = 0, - SPECIALTYPE_CODEPOINTS = 1, - SPECIALTYPE_REORDER_CODE = 2, - SPECIALTYPE_RG_KEY_VALUE = 4 -} SpecialType; - -struct LocExtKeyData : public icu::UMemory { - const char* legacyId; - const char* bcpId; - icu::LocalUHashtablePointer typeMap; - uint32_t specialTypes; -}; - -struct LocExtType : public icu::UMemory { - const char* legacyId; - const char* bcpId; -}; - -static icu::MemoryPool* gKeyTypeStringPool = NULL; -static icu::MemoryPool* gLocExtKeyDataEntries = NULL; -static icu::MemoryPool* gLocExtTypeEntries = NULL; - -U_CDECL_BEGIN - -static UBool U_CALLCONV -uloc_key_type_cleanup(void) { - if (gLocExtKeyMap != NULL) { - uhash_close(gLocExtKeyMap); - gLocExtKeyMap = NULL; - } - - delete gLocExtKeyDataEntries; - gLocExtKeyDataEntries = NULL; - - delete gLocExtTypeEntries; - gLocExtTypeEntries = NULL; - - delete gKeyTypeStringPool; - gKeyTypeStringPool = NULL; - - gLocExtKeyMapInitOnce.reset(); - return true; -} - -U_CDECL_END - - -static void U_CALLCONV -initFromResourceBundle(UErrorCode& sts) { - U_NAMESPACE_USE - ucln_common_registerCleanup(UCLN_COMMON_LOCALE_KEY_TYPE, uloc_key_type_cleanup); - - gLocExtKeyMap = uhash_open(uhash_hashIChars, uhash_compareIChars, NULL, &sts); - - LocalUResourceBundlePointer keyTypeDataRes(ures_openDirect(NULL, "keyTypeData", &sts)); - LocalUResourceBundlePointer keyMapRes(ures_getByKey(keyTypeDataRes.getAlias(), "keyMap", NULL, &sts)); - LocalUResourceBundlePointer typeMapRes(ures_getByKey(keyTypeDataRes.getAlias(), "typeMap", NULL, &sts)); - - if (U_FAILURE(sts)) { - return; - } - - UErrorCode tmpSts = U_ZERO_ERROR; - LocalUResourceBundlePointer typeAliasRes(ures_getByKey(keyTypeDataRes.getAlias(), "typeAlias", NULL, &tmpSts)); - tmpSts = U_ZERO_ERROR; - LocalUResourceBundlePointer bcpTypeAliasRes(ures_getByKey(keyTypeDataRes.getAlias(), "bcpTypeAlias", NULL, &tmpSts)); - - // initialize pools storing dynamically allocated objects - gKeyTypeStringPool = new icu::MemoryPool; - if (gKeyTypeStringPool == NULL) { - sts = U_MEMORY_ALLOCATION_ERROR; - return; - } - gLocExtKeyDataEntries = new icu::MemoryPool; - if (gLocExtKeyDataEntries == NULL) { - sts = U_MEMORY_ALLOCATION_ERROR; - return; - } - gLocExtTypeEntries = new icu::MemoryPool; - if (gLocExtTypeEntries == NULL) { - sts = U_MEMORY_ALLOCATION_ERROR; - return; - } - - // iterate through keyMap resource - LocalUResourceBundlePointer keyMapEntry; - - while (ures_hasNext(keyMapRes.getAlias())) { - keyMapEntry.adoptInstead(ures_getNextResource(keyMapRes.getAlias(), keyMapEntry.orphan(), &sts)); - if (U_FAILURE(sts)) { - break; - } - const char* legacyKeyId = ures_getKey(keyMapEntry.getAlias()); - UnicodeString uBcpKeyId = ures_getUnicodeString(keyMapEntry.getAlias(), &sts); - if (U_FAILURE(sts)) { - break; - } - - // empty value indicates that BCP key is same with the legacy key. - const char* bcpKeyId = legacyKeyId; - if (!uBcpKeyId.isEmpty()) { - icu::CharString* bcpKeyIdBuf = gKeyTypeStringPool->create(); - if (bcpKeyIdBuf == NULL) { - sts = U_MEMORY_ALLOCATION_ERROR; - break; - } - bcpKeyIdBuf->appendInvariantChars(uBcpKeyId, sts); - if (U_FAILURE(sts)) { - break; - } - bcpKeyId = bcpKeyIdBuf->data(); - } - - UBool isTZ = uprv_strcmp(legacyKeyId, "timezone") == 0; - - UHashtable* typeDataMap = uhash_open(uhash_hashIChars, uhash_compareIChars, NULL, &sts); - if (U_FAILURE(sts)) { - break; - } - uint32_t specialTypes = SPECIALTYPE_NONE; - - LocalUResourceBundlePointer typeAliasResByKey; - LocalUResourceBundlePointer bcpTypeAliasResByKey; - - if (typeAliasRes.isValid()) { - tmpSts = U_ZERO_ERROR; - typeAliasResByKey.adoptInstead(ures_getByKey(typeAliasRes.getAlias(), legacyKeyId, NULL, &tmpSts)); - if (U_FAILURE(tmpSts)) { - typeAliasResByKey.orphan(); - } - } - if (bcpTypeAliasRes.isValid()) { - tmpSts = U_ZERO_ERROR; - bcpTypeAliasResByKey.adoptInstead(ures_getByKey(bcpTypeAliasRes.getAlias(), bcpKeyId, NULL, &tmpSts)); - if (U_FAILURE(tmpSts)) { - bcpTypeAliasResByKey.orphan(); - } - } - - // look up type map for the key, and walk through the mapping data - LocalUResourceBundlePointer typeMapResByKey(ures_getByKey(typeMapRes.getAlias(), legacyKeyId, NULL, &sts)); - if (U_FAILURE(sts)) { - // We fail here if typeMap does not have an entry corresponding to every entry in keyMap (should - // not happen for valid keyTypeData), or if ures_getByKeyfails fails for some other reason - // (e.g. data file cannot be loaded, using stubdata, over-aggressive data filtering has removed - // something like timezoneTypes.res, etc.). Error code is already set. See ICU-21669. - UPRV_UNREACHABLE_ASSERT; - } else { - LocalUResourceBundlePointer typeMapEntry; - - while (ures_hasNext(typeMapResByKey.getAlias())) { - typeMapEntry.adoptInstead(ures_getNextResource(typeMapResByKey.getAlias(), typeMapEntry.orphan(), &sts)); - if (U_FAILURE(sts)) { - break; - } - const char* legacyTypeId = ures_getKey(typeMapEntry.getAlias()); - - // special types - if (uprv_strcmp(legacyTypeId, "CODEPOINTS") == 0) { - specialTypes |= SPECIALTYPE_CODEPOINTS; - continue; - } - if (uprv_strcmp(legacyTypeId, "REORDER_CODE") == 0) { - specialTypes |= SPECIALTYPE_REORDER_CODE; - continue; - } - if (uprv_strcmp(legacyTypeId, "RG_KEY_VALUE") == 0) { - specialTypes |= SPECIALTYPE_RG_KEY_VALUE; - continue; - } - - if (isTZ) { - // a timezone key uses a colon instead of a slash in the resource. - // e.g. America:Los_Angeles - if (uprv_strchr(legacyTypeId, ':') != NULL) { - icu::CharString* legacyTypeIdBuf = - gKeyTypeStringPool->create(legacyTypeId, sts); - if (legacyTypeIdBuf == NULL) { - sts = U_MEMORY_ALLOCATION_ERROR; - break; - } - if (U_FAILURE(sts)) { - break; - } - std::replace( - legacyTypeIdBuf->data(), - legacyTypeIdBuf->data() + legacyTypeIdBuf->length(), - ':', '/'); - legacyTypeId = legacyTypeIdBuf->data(); - } - } - - UnicodeString uBcpTypeId = ures_getUnicodeString(typeMapEntry.getAlias(), &sts); - if (U_FAILURE(sts)) { - break; - } - - // empty value indicates that BCP type is same with the legacy type. - const char* bcpTypeId = legacyTypeId; - if (!uBcpTypeId.isEmpty()) { - icu::CharString* bcpTypeIdBuf = gKeyTypeStringPool->create(); - if (bcpTypeIdBuf == NULL) { - sts = U_MEMORY_ALLOCATION_ERROR; - break; - } - bcpTypeIdBuf->appendInvariantChars(uBcpTypeId, sts); - if (U_FAILURE(sts)) { - break; - } - bcpTypeId = bcpTypeIdBuf->data(); - } - - // Note: legacy type value should never be - // equivalent to bcp type value of a different - // type under the same key. So we use a single - // map for lookup. - LocExtType* t = gLocExtTypeEntries->create(); - if (t == NULL) { - sts = U_MEMORY_ALLOCATION_ERROR; - break; - } - t->bcpId = bcpTypeId; - t->legacyId = legacyTypeId; - - uhash_put(typeDataMap, (void*)legacyTypeId, t, &sts); - if (bcpTypeId != legacyTypeId) { - // different type value - uhash_put(typeDataMap, (void*)bcpTypeId, t, &sts); - } - if (U_FAILURE(sts)) { - break; - } - - // also put aliases in the map - if (typeAliasResByKey.isValid()) { - LocalUResourceBundlePointer typeAliasDataEntry; - - ures_resetIterator(typeAliasResByKey.getAlias()); - while (ures_hasNext(typeAliasResByKey.getAlias()) && U_SUCCESS(sts)) { - int32_t toLen; - typeAliasDataEntry.adoptInstead(ures_getNextResource(typeAliasResByKey.getAlias(), typeAliasDataEntry.orphan(), &sts)); - const UChar* to = ures_getString(typeAliasDataEntry.getAlias(), &toLen, &sts); - if (U_FAILURE(sts)) { - break; - } - // check if this is an alias of canonical legacy type - if (uprv_compareInvWithUChar(NULL, legacyTypeId, -1, to, toLen) == 0) { - const char* from = ures_getKey(typeAliasDataEntry.getAlias()); - if (isTZ) { - // replace colon with slash if necessary - if (uprv_strchr(from, ':') != NULL) { - icu::CharString* fromBuf = - gKeyTypeStringPool->create(from, sts); - if (fromBuf == NULL) { - sts = U_MEMORY_ALLOCATION_ERROR; - break; - } - if (U_FAILURE(sts)) { - break; - } - std::replace( - fromBuf->data(), - fromBuf->data() + fromBuf->length(), - ':', '/'); - from = fromBuf->data(); - } - } - uhash_put(typeDataMap, (void*)from, t, &sts); - } - } - if (U_FAILURE(sts)) { - break; - } - } - - if (bcpTypeAliasResByKey.isValid()) { - LocalUResourceBundlePointer bcpTypeAliasDataEntry; - - ures_resetIterator(bcpTypeAliasResByKey.getAlias()); - while (ures_hasNext(bcpTypeAliasResByKey.getAlias()) && U_SUCCESS(sts)) { - int32_t toLen; - bcpTypeAliasDataEntry.adoptInstead(ures_getNextResource(bcpTypeAliasResByKey.getAlias(), bcpTypeAliasDataEntry.orphan(), &sts)); - const UChar* to = ures_getString(bcpTypeAliasDataEntry.getAlias(), &toLen, &sts); - if (U_FAILURE(sts)) { - break; - } - // check if this is an alias of bcp type - if (uprv_compareInvWithUChar(NULL, bcpTypeId, -1, to, toLen) == 0) { - const char* from = ures_getKey(bcpTypeAliasDataEntry.getAlias()); - uhash_put(typeDataMap, (void*)from, t, &sts); - } - } - if (U_FAILURE(sts)) { - break; - } - } - } - } - if (U_FAILURE(sts)) { - break; - } - - LocExtKeyData* keyData = gLocExtKeyDataEntries->create(); - if (keyData == NULL) { - sts = U_MEMORY_ALLOCATION_ERROR; - break; - } - keyData->bcpId = bcpKeyId; - keyData->legacyId = legacyKeyId; - keyData->specialTypes = specialTypes; - keyData->typeMap.adoptInstead(typeDataMap); - - uhash_put(gLocExtKeyMap, (void*)legacyKeyId, keyData, &sts); - if (legacyKeyId != bcpKeyId) { - // different key value - uhash_put(gLocExtKeyMap, (void*)bcpKeyId, keyData, &sts); - } - if (U_FAILURE(sts)) { - break; - } - } -} - -static UBool -init() { - UErrorCode sts = U_ZERO_ERROR; - umtx_initOnce(gLocExtKeyMapInitOnce, &initFromResourceBundle, sts); - if (U_FAILURE(sts)) { - return false; - } - return true; -} - -static UBool -isSpecialTypeCodepoints(const char* val) { - int32_t subtagLen = 0; - const char* p = val; - while (*p) { - if (*p == '-') { - if (subtagLen < 4 || subtagLen > 6) { - return false; - } - subtagLen = 0; - } else if ((*p >= '0' && *p <= '9') || - (*p >= 'A' && *p <= 'F') || // A-F/a-f are contiguous - (*p >= 'a' && *p <= 'f')) { // also in EBCDIC - subtagLen++; - } else { - return false; - } - p++; - } - return (subtagLen >= 4 && subtagLen <= 6); -} - -static UBool -isSpecialTypeReorderCode(const char* val) { - int32_t subtagLen = 0; - const char* p = val; - while (*p) { - if (*p == '-') { - if (subtagLen < 3 || subtagLen > 8) { - return false; - } - subtagLen = 0; - } else if (uprv_isASCIILetter(*p)) { - subtagLen++; - } else { - return false; - } - p++; - } - return (subtagLen >=3 && subtagLen <=8); -} - -static UBool -isSpecialTypeRgKeyValue(const char* val) { - int32_t subtagLen = 0; - const char* p = val; - while (*p) { - if ( (subtagLen < 2 && uprv_isASCIILetter(*p)) || - (subtagLen >= 2 && (*p == 'Z' || *p == 'z')) ) { - subtagLen++; - } else { - return false; - } - p++; - } - return (subtagLen == 6); -} - -U_CFUNC const char* -ulocimp_toBcpKey(const char* key) { - if (!init()) { - return NULL; - } - - LocExtKeyData* keyData = (LocExtKeyData*)uhash_get(gLocExtKeyMap, key); - if (keyData != NULL) { - return keyData->bcpId; - } - return NULL; -} - -U_CFUNC const char* -ulocimp_toLegacyKey(const char* key) { - if (!init()) { - return NULL; - } - - LocExtKeyData* keyData = (LocExtKeyData*)uhash_get(gLocExtKeyMap, key); - if (keyData != NULL) { - return keyData->legacyId; - } - return NULL; -} - -U_CFUNC const char* -ulocimp_toBcpType(const char* key, const char* type, UBool* isKnownKey, UBool* isSpecialType) { - if (isKnownKey != NULL) { - *isKnownKey = false; - } - if (isSpecialType != NULL) { - *isSpecialType = false; - } - - if (!init()) { - return NULL; - } - - LocExtKeyData* keyData = (LocExtKeyData*)uhash_get(gLocExtKeyMap, key); - if (keyData != NULL) { - if (isKnownKey != NULL) { - *isKnownKey = true; - } - LocExtType* t = (LocExtType*)uhash_get(keyData->typeMap.getAlias(), type); - if (t != NULL) { - return t->bcpId; - } - if (keyData->specialTypes != SPECIALTYPE_NONE) { - UBool matched = false; - if (keyData->specialTypes & SPECIALTYPE_CODEPOINTS) { - matched = isSpecialTypeCodepoints(type); - } - if (!matched && keyData->specialTypes & SPECIALTYPE_REORDER_CODE) { - matched = isSpecialTypeReorderCode(type); - } - if (!matched && keyData->specialTypes & SPECIALTYPE_RG_KEY_VALUE) { - matched = isSpecialTypeRgKeyValue(type); - } - if (matched) { - if (isSpecialType != NULL) { - *isSpecialType = true; - } - return type; - } - } - } - return NULL; -} - - -U_CFUNC const char* -ulocimp_toLegacyType(const char* key, const char* type, UBool* isKnownKey, UBool* isSpecialType) { - if (isKnownKey != NULL) { - *isKnownKey = false; - } - if (isSpecialType != NULL) { - *isSpecialType = false; - } - - if (!init()) { - return NULL; - } - - LocExtKeyData* keyData = (LocExtKeyData*)uhash_get(gLocExtKeyMap, key); - if (keyData != NULL) { - if (isKnownKey != NULL) { - *isKnownKey = true; - } - LocExtType* t = (LocExtType*)uhash_get(keyData->typeMap.getAlias(), type); - if (t != NULL) { - return t->legacyId; - } - if (keyData->specialTypes != SPECIALTYPE_NONE) { - UBool matched = false; - if (keyData->specialTypes & SPECIALTYPE_CODEPOINTS) { - matched = isSpecialTypeCodepoints(type); - } - if (!matched && keyData->specialTypes & SPECIALTYPE_REORDER_CODE) { - matched = isSpecialTypeReorderCode(type); - } - if (!matched && keyData->specialTypes & SPECIALTYPE_RG_KEY_VALUE) { - matched = isSpecialTypeRgKeyValue(type); - } - if (matched) { - if (isSpecialType != NULL) { - *isSpecialType = true; - } - return type; - } - } - } - return NULL; -} - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2014-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ +#include + +#include "unicode/utypes.h" +#include "unicode/unistr.h" +#include "unicode/uobject.h" + +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "uassert.h" +#include "ucln_cmn.h" +#include "uhash.h" +#include "umutex.h" +#include "uresimp.h" +#include "uvector.h" +#include "udataswp.h" /* for InvChar functions */ + +static UHashtable* gLocExtKeyMap = nullptr; +static icu::UInitOnce gLocExtKeyMapInitOnce {}; + +// bit flags for special types +typedef enum { + SPECIALTYPE_NONE = 0, + SPECIALTYPE_CODEPOINTS = 1, + SPECIALTYPE_REORDER_CODE = 2, + SPECIALTYPE_RG_KEY_VALUE = 4 +} SpecialType; + +struct LocExtKeyData : public icu::UMemory { + const char* legacyId; + const char* bcpId; + icu::LocalUHashtablePointer typeMap; + uint32_t specialTypes; +}; + +struct LocExtType : public icu::UMemory { + const char* legacyId; + const char* bcpId; +}; + +static icu::MemoryPool* gKeyTypeStringPool = nullptr; +static icu::MemoryPool* gLocExtKeyDataEntries = nullptr; +static icu::MemoryPool* gLocExtTypeEntries = nullptr; + +U_CDECL_BEGIN + +static UBool U_CALLCONV +uloc_key_type_cleanup() { + if (gLocExtKeyMap != nullptr) { + uhash_close(gLocExtKeyMap); + gLocExtKeyMap = nullptr; + } + + delete gLocExtKeyDataEntries; + gLocExtKeyDataEntries = nullptr; + + delete gLocExtTypeEntries; + gLocExtTypeEntries = nullptr; + + delete gKeyTypeStringPool; + gKeyTypeStringPool = nullptr; + + gLocExtKeyMapInitOnce.reset(); + return true; +} + +U_CDECL_END + + +static void U_CALLCONV +initFromResourceBundle(UErrorCode& sts) { + U_NAMESPACE_USE + ucln_common_registerCleanup(UCLN_COMMON_LOCALE_KEY_TYPE, uloc_key_type_cleanup); + + gLocExtKeyMap = uhash_open(uhash_hashIChars, uhash_compareIChars, nullptr, &sts); + + LocalUResourceBundlePointer keyTypeDataRes(ures_openDirect(nullptr, "keyTypeData", &sts)); + LocalUResourceBundlePointer keyMapRes(ures_getByKey(keyTypeDataRes.getAlias(), "keyMap", nullptr, &sts)); + LocalUResourceBundlePointer typeMapRes(ures_getByKey(keyTypeDataRes.getAlias(), "typeMap", nullptr, &sts)); + + if (U_FAILURE(sts)) { + return; + } + + UErrorCode tmpSts = U_ZERO_ERROR; + LocalUResourceBundlePointer typeAliasRes(ures_getByKey(keyTypeDataRes.getAlias(), "typeAlias", nullptr, &tmpSts)); + tmpSts = U_ZERO_ERROR; + LocalUResourceBundlePointer bcpTypeAliasRes(ures_getByKey(keyTypeDataRes.getAlias(), "bcpTypeAlias", nullptr, &tmpSts)); + + // initialize pools storing dynamically allocated objects + gKeyTypeStringPool = new icu::MemoryPool; + if (gKeyTypeStringPool == nullptr) { + sts = U_MEMORY_ALLOCATION_ERROR; + return; + } + gLocExtKeyDataEntries = new icu::MemoryPool; + if (gLocExtKeyDataEntries == nullptr) { + sts = U_MEMORY_ALLOCATION_ERROR; + return; + } + gLocExtTypeEntries = new icu::MemoryPool; + if (gLocExtTypeEntries == nullptr) { + sts = U_MEMORY_ALLOCATION_ERROR; + return; + } + + // iterate through keyMap resource + LocalUResourceBundlePointer keyMapEntry; + + while (ures_hasNext(keyMapRes.getAlias())) { + keyMapEntry.adoptInstead(ures_getNextResource(keyMapRes.getAlias(), keyMapEntry.orphan(), &sts)); + if (U_FAILURE(sts)) { + break; + } + const char* legacyKeyId = ures_getKey(keyMapEntry.getAlias()); + UnicodeString uBcpKeyId = ures_getUnicodeString(keyMapEntry.getAlias(), &sts); + if (U_FAILURE(sts)) { + break; + } + + // empty value indicates that BCP key is same with the legacy key. + const char* bcpKeyId = legacyKeyId; + if (!uBcpKeyId.isEmpty()) { + icu::CharString* bcpKeyIdBuf = gKeyTypeStringPool->create(); + if (bcpKeyIdBuf == nullptr) { + sts = U_MEMORY_ALLOCATION_ERROR; + break; + } + bcpKeyIdBuf->appendInvariantChars(uBcpKeyId, sts); + if (U_FAILURE(sts)) { + break; + } + bcpKeyId = bcpKeyIdBuf->data(); + } + + UBool isTZ = uprv_strcmp(legacyKeyId, "timezone") == 0; + + UHashtable* typeDataMap = uhash_open(uhash_hashIChars, uhash_compareIChars, nullptr, &sts); + if (U_FAILURE(sts)) { + break; + } + uint32_t specialTypes = SPECIALTYPE_NONE; + + LocalUResourceBundlePointer typeAliasResByKey; + LocalUResourceBundlePointer bcpTypeAliasResByKey; + + if (typeAliasRes.isValid()) { + tmpSts = U_ZERO_ERROR; + typeAliasResByKey.adoptInstead(ures_getByKey(typeAliasRes.getAlias(), legacyKeyId, nullptr, &tmpSts)); + if (U_FAILURE(tmpSts)) { + typeAliasResByKey.orphan(); + } + } + if (bcpTypeAliasRes.isValid()) { + tmpSts = U_ZERO_ERROR; + bcpTypeAliasResByKey.adoptInstead(ures_getByKey(bcpTypeAliasRes.getAlias(), bcpKeyId, nullptr, &tmpSts)); + if (U_FAILURE(tmpSts)) { + bcpTypeAliasResByKey.orphan(); + } + } + + // look up type map for the key, and walk through the mapping data + LocalUResourceBundlePointer typeMapResByKey(ures_getByKey(typeMapRes.getAlias(), legacyKeyId, nullptr, &sts)); + if (U_FAILURE(sts)) { + // We fail here if typeMap does not have an entry corresponding to every entry in keyMap (should + // not happen for valid keyTypeData), or if ures_getByKeyfails fails for some other reason + // (e.g. data file cannot be loaded, using stubdata, over-aggressive data filtering has removed + // something like timezoneTypes.res, etc.). Error code is already set. See ICU-21669. + UPRV_UNREACHABLE_ASSERT; + } else { + LocalUResourceBundlePointer typeMapEntry; + + while (ures_hasNext(typeMapResByKey.getAlias())) { + typeMapEntry.adoptInstead(ures_getNextResource(typeMapResByKey.getAlias(), typeMapEntry.orphan(), &sts)); + if (U_FAILURE(sts)) { + break; + } + const char* legacyTypeId = ures_getKey(typeMapEntry.getAlias()); + + // special types + if (uprv_strcmp(legacyTypeId, "CODEPOINTS") == 0) { + specialTypes |= SPECIALTYPE_CODEPOINTS; + continue; + } + if (uprv_strcmp(legacyTypeId, "REORDER_CODE") == 0) { + specialTypes |= SPECIALTYPE_REORDER_CODE; + continue; + } + if (uprv_strcmp(legacyTypeId, "RG_KEY_VALUE") == 0) { + specialTypes |= SPECIALTYPE_RG_KEY_VALUE; + continue; + } + + if (isTZ) { + // a timezone key uses a colon instead of a slash in the resource. + // e.g. America:Los_Angeles + if (uprv_strchr(legacyTypeId, ':') != nullptr) { + icu::CharString* legacyTypeIdBuf = + gKeyTypeStringPool->create(legacyTypeId, sts); + if (legacyTypeIdBuf == nullptr) { + sts = U_MEMORY_ALLOCATION_ERROR; + break; + } + if (U_FAILURE(sts)) { + break; + } + std::replace( + legacyTypeIdBuf->data(), + legacyTypeIdBuf->data() + legacyTypeIdBuf->length(), + ':', '/'); + legacyTypeId = legacyTypeIdBuf->data(); + } + } + + UnicodeString uBcpTypeId = ures_getUnicodeString(typeMapEntry.getAlias(), &sts); + if (U_FAILURE(sts)) { + break; + } + + // empty value indicates that BCP type is same with the legacy type. + const char* bcpTypeId = legacyTypeId; + if (!uBcpTypeId.isEmpty()) { + icu::CharString* bcpTypeIdBuf = gKeyTypeStringPool->create(); + if (bcpTypeIdBuf == nullptr) { + sts = U_MEMORY_ALLOCATION_ERROR; + break; + } + bcpTypeIdBuf->appendInvariantChars(uBcpTypeId, sts); + if (U_FAILURE(sts)) { + break; + } + bcpTypeId = bcpTypeIdBuf->data(); + } + + // Note: legacy type value should never be + // equivalent to bcp type value of a different + // type under the same key. So we use a single + // map for lookup. + LocExtType* t = gLocExtTypeEntries->create(); + if (t == nullptr) { + sts = U_MEMORY_ALLOCATION_ERROR; + break; + } + t->bcpId = bcpTypeId; + t->legacyId = legacyTypeId; + + uhash_put(typeDataMap, (void*)legacyTypeId, t, &sts); + if (bcpTypeId != legacyTypeId) { + // different type value + uhash_put(typeDataMap, (void*)bcpTypeId, t, &sts); + } + if (U_FAILURE(sts)) { + break; + } + + // also put aliases in the map + if (typeAliasResByKey.isValid()) { + LocalUResourceBundlePointer typeAliasDataEntry; + + ures_resetIterator(typeAliasResByKey.getAlias()); + while (ures_hasNext(typeAliasResByKey.getAlias()) && U_SUCCESS(sts)) { + int32_t toLen; + typeAliasDataEntry.adoptInstead(ures_getNextResource(typeAliasResByKey.getAlias(), typeAliasDataEntry.orphan(), &sts)); + const char16_t* to = ures_getString(typeAliasDataEntry.getAlias(), &toLen, &sts); + if (U_FAILURE(sts)) { + break; + } + // check if this is an alias of canonical legacy type + if (uprv_compareInvWithUChar(nullptr, legacyTypeId, -1, to, toLen) == 0) { + const char* from = ures_getKey(typeAliasDataEntry.getAlias()); + if (isTZ) { + // replace colon with slash if necessary + if (uprv_strchr(from, ':') != nullptr) { + icu::CharString* fromBuf = + gKeyTypeStringPool->create(from, sts); + if (fromBuf == nullptr) { + sts = U_MEMORY_ALLOCATION_ERROR; + break; + } + if (U_FAILURE(sts)) { + break; + } + std::replace( + fromBuf->data(), + fromBuf->data() + fromBuf->length(), + ':', '/'); + from = fromBuf->data(); + } + } + uhash_put(typeDataMap, (void*)from, t, &sts); + } + } + if (U_FAILURE(sts)) { + break; + } + } + + if (bcpTypeAliasResByKey.isValid()) { + LocalUResourceBundlePointer bcpTypeAliasDataEntry; + + ures_resetIterator(bcpTypeAliasResByKey.getAlias()); + while (ures_hasNext(bcpTypeAliasResByKey.getAlias()) && U_SUCCESS(sts)) { + int32_t toLen; + bcpTypeAliasDataEntry.adoptInstead(ures_getNextResource(bcpTypeAliasResByKey.getAlias(), bcpTypeAliasDataEntry.orphan(), &sts)); + const char16_t* to = ures_getString(bcpTypeAliasDataEntry.getAlias(), &toLen, &sts); + if (U_FAILURE(sts)) { + break; + } + // check if this is an alias of bcp type + if (uprv_compareInvWithUChar(nullptr, bcpTypeId, -1, to, toLen) == 0) { + const char* from = ures_getKey(bcpTypeAliasDataEntry.getAlias()); + uhash_put(typeDataMap, (void*)from, t, &sts); + } + } + if (U_FAILURE(sts)) { + break; + } + } + } + } + if (U_FAILURE(sts)) { + break; + } + + LocExtKeyData* keyData = gLocExtKeyDataEntries->create(); + if (keyData == nullptr) { + sts = U_MEMORY_ALLOCATION_ERROR; + break; + } + keyData->bcpId = bcpKeyId; + keyData->legacyId = legacyKeyId; + keyData->specialTypes = specialTypes; + keyData->typeMap.adoptInstead(typeDataMap); + + uhash_put(gLocExtKeyMap, (void*)legacyKeyId, keyData, &sts); + if (legacyKeyId != bcpKeyId) { + // different key value + uhash_put(gLocExtKeyMap, (void*)bcpKeyId, keyData, &sts); + } + if (U_FAILURE(sts)) { + break; + } + } +} + +static UBool +init() { + UErrorCode sts = U_ZERO_ERROR; + umtx_initOnce(gLocExtKeyMapInitOnce, &initFromResourceBundle, sts); + if (U_FAILURE(sts)) { + return false; + } + return true; +} + +static UBool +isSpecialTypeCodepoints(const char* val) { + int32_t subtagLen = 0; + const char* p = val; + while (*p) { + if (*p == '-') { + if (subtagLen < 4 || subtagLen > 6) { + return false; + } + subtagLen = 0; + } else if ((*p >= '0' && *p <= '9') || + (*p >= 'A' && *p <= 'F') || // A-F/a-f are contiguous + (*p >= 'a' && *p <= 'f')) { // also in EBCDIC + subtagLen++; + } else { + return false; + } + p++; + } + return (subtagLen >= 4 && subtagLen <= 6); +} + +static UBool +isSpecialTypeReorderCode(const char* val) { + int32_t subtagLen = 0; + const char* p = val; + while (*p) { + if (*p == '-') { + if (subtagLen < 3 || subtagLen > 8) { + return false; + } + subtagLen = 0; + } else if (uprv_isASCIILetter(*p)) { + subtagLen++; + } else { + return false; + } + p++; + } + return (subtagLen >=3 && subtagLen <=8); +} + +static UBool +isSpecialTypeRgKeyValue(const char* val) { + int32_t subtagLen = 0; + const char* p = val; + while (*p) { + if ( (subtagLen < 2 && uprv_isASCIILetter(*p)) || + (subtagLen >= 2 && (*p == 'Z' || *p == 'z')) ) { + subtagLen++; + } else { + return false; + } + p++; + } + return (subtagLen == 6); +} + +U_CFUNC const char* +ulocimp_toBcpKey(const char* key) { + if (!init()) { + return nullptr; + } + + LocExtKeyData* keyData = (LocExtKeyData*)uhash_get(gLocExtKeyMap, key); + if (keyData != nullptr) { + return keyData->bcpId; + } + return nullptr; +} + +U_CFUNC const char* +ulocimp_toLegacyKey(const char* key) { + if (!init()) { + return nullptr; + } + + LocExtKeyData* keyData = (LocExtKeyData*)uhash_get(gLocExtKeyMap, key); + if (keyData != nullptr) { + return keyData->legacyId; + } + return nullptr; +} + +U_CFUNC const char* +ulocimp_toBcpType(const char* key, const char* type, UBool* isKnownKey, UBool* isSpecialType) { + if (isKnownKey != nullptr) { + *isKnownKey = false; + } + if (isSpecialType != nullptr) { + *isSpecialType = false; + } + + if (!init()) { + return nullptr; + } + + LocExtKeyData* keyData = (LocExtKeyData*)uhash_get(gLocExtKeyMap, key); + if (keyData != nullptr) { + if (isKnownKey != nullptr) { + *isKnownKey = true; + } + LocExtType* t = (LocExtType*)uhash_get(keyData->typeMap.getAlias(), type); + if (t != nullptr) { + return t->bcpId; + } + if (keyData->specialTypes != SPECIALTYPE_NONE) { + UBool matched = false; + if (keyData->specialTypes & SPECIALTYPE_CODEPOINTS) { + matched = isSpecialTypeCodepoints(type); + } + if (!matched && keyData->specialTypes & SPECIALTYPE_REORDER_CODE) { + matched = isSpecialTypeReorderCode(type); + } + if (!matched && keyData->specialTypes & SPECIALTYPE_RG_KEY_VALUE) { + matched = isSpecialTypeRgKeyValue(type); + } + if (matched) { + if (isSpecialType != nullptr) { + *isSpecialType = true; + } + return type; + } + } + } + return nullptr; +} + + +U_CFUNC const char* +ulocimp_toLegacyType(const char* key, const char* type, UBool* isKnownKey, UBool* isSpecialType) { + if (isKnownKey != nullptr) { + *isKnownKey = false; + } + if (isSpecialType != nullptr) { + *isSpecialType = false; + } + + if (!init()) { + return nullptr; + } + + LocExtKeyData* keyData = (LocExtKeyData*)uhash_get(gLocExtKeyMap, key); + if (keyData != nullptr) { + if (isKnownKey != nullptr) { + *isKnownKey = true; + } + LocExtType* t = (LocExtType*)uhash_get(keyData->typeMap.getAlias(), type); + if (t != nullptr) { + return t->legacyId; + } + if (keyData->specialTypes != SPECIALTYPE_NONE) { + UBool matched = false; + if (keyData->specialTypes & SPECIALTYPE_CODEPOINTS) { + matched = isSpecialTypeCodepoints(type); + } + if (!matched && keyData->specialTypes & SPECIALTYPE_REORDER_CODE) { + matched = isSpecialTypeReorderCode(type); + } + if (!matched && keyData->specialTypes & SPECIALTYPE_RG_KEY_VALUE) { + matched = isSpecialTypeRgKeyValue(type); + } + if (matched) { + if (isSpecialType != nullptr) { + *isSpecialType = true; + } + return type; + } + } + } + return nullptr; +} + diff --git a/deps/icu-small/source/common/uloc_tag.cpp b/deps/icu-small/source/common/uloc_tag.cpp index 01a0e0028f57ea..7cef283867e535 100644 --- a/deps/icu-small/source/common/uloc_tag.cpp +++ b/deps/icu-small/source/common/uloc_tag.cpp @@ -1,2869 +1,2869 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2009-2015, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#include "unicode/bytestream.h" -#include "unicode/utypes.h" -#include "unicode/ures.h" -#include "unicode/localpointer.h" -#include "unicode/putil.h" -#include "unicode/uenum.h" -#include "unicode/uloc.h" -#include "ustr_imp.h" -#include "bytesinkutil.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "putilimp.h" -#include "uinvchar.h" -#include "ulocimp.h" -#include "uassert.h" - - -/* struct holding a single variant */ -typedef struct VariantListEntry { - const char *variant; - struct VariantListEntry *next; -} VariantListEntry; - -/* struct holding a single attribute value */ -struct AttributeListEntry : public icu::UMemory { - const char *attribute; - struct AttributeListEntry *next; -}; - -/* struct holding a single extension */ -struct ExtensionListEntry : public icu::UMemory { - const char *key; - const char *value; - struct ExtensionListEntry *next; -}; - -#define MAXEXTLANG 3 -typedef struct ULanguageTag { - char *buf; /* holding parsed subtags */ - const char *language; - const char *extlang[MAXEXTLANG]; - const char *script; - const char *region; - VariantListEntry *variants; - ExtensionListEntry *extensions; - const char *privateuse; - const char *legacy; -} ULanguageTag; - -#define MINLEN 2 -#define SEP '-' -#define PRIVATEUSE 'x' -#define LDMLEXT 'u' - -#define LOCALE_SEP '_' -#define LOCALE_EXT_SEP '@' -#define LOCALE_KEYWORD_SEP ';' -#define LOCALE_KEY_TYPE_SEP '=' - -#define ISALPHA(c) uprv_isASCIILetter(c) -#define ISNUMERIC(c) ((c)>='0' && (c)<='9') - -static const char EMPTY[] = ""; -static const char LANG_UND[] = "und"; -static const char PRIVATEUSE_KEY[] = "x"; -static const char _POSIX[] = "_POSIX"; -static const char POSIX_KEY[] = "va"; -static const char POSIX_VALUE[] = "posix"; -static const char LOCALE_ATTRIBUTE_KEY[] = "attribute"; -static const char PRIVUSE_VARIANT_PREFIX[] = "lvariant"; -static const char LOCALE_TYPE_YES[] = "yes"; - -#define LANG_UND_LEN 3 - -/* - Updated on 2018-09-12 from - https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry . - - This table has 2 parts. The part for - legacy language tags (marked as “Type: grandfathered” in BCP 47) - is generated by the following scripts from the IANA language tag registry. - - curl https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry |\ - egrep -A 7 'Type: grandfathered' | \ - egrep 'Tag|Prefe' | grep -B1 'Preferred' | grep -v '^--' | \ - awk -n '/Tag/ {printf(" \"%s\", ", $2);} /Preferred/ {printf("\"%s\",\n", $2);}' |\ - tr 'A-Z' 'a-z' - - - The 2nd part is made of five ICU-specific entries. They're kept for - the backward compatibility for now, even though there are no preferred - values. They may have to be removed for the strict BCP 47 compliance. - -*/ -static const char* const LEGACY[] = { -/* legacy preferred */ - "art-lojban", "jbo", - "en-gb-oed", "en-gb-oxendict", - "i-ami", "ami", - "i-bnn", "bnn", - "i-hak", "hak", - "i-klingon", "tlh", - "i-lux", "lb", - "i-navajo", "nv", - "i-pwn", "pwn", - "i-tao", "tao", - "i-tay", "tay", - "i-tsu", "tsu", - "no-bok", "nb", - "no-nyn", "nn", - "sgn-be-fr", "sfb", - "sgn-be-nl", "vgt", - "sgn-ch-de", "sgg", - "zh-guoyu", "cmn", - "zh-hakka", "hak", - "zh-min-nan", "nan", - "zh-xiang", "hsn", - - // Legacy tags with no preferred value in the IANA - // registry. Kept for now for the backward compatibility - // because ICU has mapped them this way. - "i-default", "en-x-i-default", - "i-enochian", "und-x-i-enochian", - "i-mingo", "see-x-i-mingo", - "zh-min", "nan-x-zh-min", -}; - -/* - Updated on 2018-09-12 from - https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry . - - The table lists redundant tags with preferred value in the IANA language tag registry. - It's generated with the following command: - - curl https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry |\ - grep 'Type: redundant' -A 5 | egrep '^(Tag:|Prefer)' | grep -B1 'Preferred' | \ - awk -n '/Tag/ {printf(" \"%s\", ", $2);} /Preferred/ {printf("\"%s\",\n", $2);}' | \ - tr 'A-Z' 'a-z' - - In addition, ja-latn-hepburn-heploc is mapped to ja-latn-alalc97 because - a variant tag 'hepburn-heploc' has the preferred subtag, 'alaic97'. -*/ - -static const char* const REDUNDANT[] = { -// redundant preferred - "sgn-br", "bzs", - "sgn-co", "csn", - "sgn-de", "gsg", - "sgn-dk", "dsl", - "sgn-es", "ssp", - "sgn-fr", "fsl", - "sgn-gb", "bfi", - "sgn-gr", "gss", - "sgn-ie", "isg", - "sgn-it", "ise", - "sgn-jp", "jsl", - "sgn-mx", "mfs", - "sgn-ni", "ncs", - "sgn-nl", "dse", - "sgn-no", "nsl", - "sgn-pt", "psr", - "sgn-se", "swl", - "sgn-us", "ase", - "sgn-za", "sfs", - "zh-cmn", "cmn", - "zh-cmn-hans", "cmn-hans", - "zh-cmn-hant", "cmn-hant", - "zh-gan", "gan", - "zh-wuu", "wuu", - "zh-yue", "yue", - - // variant tag with preferred value - "ja-latn-hepburn-heploc", "ja-latn-alalc97", -}; - -/* - Updated on 2018-09-12 from - https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry . - - grep 'Type: language' -A 7 language-subtag-registry | egrep 'Subtag|Prefe' | \ - grep -B1 'Preferred' | grep -v '^--' | \ - awk -n '/Subtag/ {printf(" \"%s\", ", $2);} /Preferred/ {printf("\"%s\",\n", $2);}' - - Make sure that 2-letter language subtags come before 3-letter subtags. -*/ -static const char DEPRECATEDLANGS[][4] = { -/* deprecated new */ - "in", "id", - "iw", "he", - "ji", "yi", - "jw", "jv", - "mo", "ro", - "aam", "aas", - "adp", "dz", - "aue", "ktz", - "ayx", "nun", - "bgm", "bcg", - "bjd", "drl", - "ccq", "rki", - "cjr", "mom", - "cka", "cmr", - "cmk", "xch", - "coy", "pij", - "cqu", "quh", - "drh", "khk", - "drw", "prs", - "gav", "dev", - "gfx", "vaj", - "ggn", "gvr", - "gti", "nyc", - "guv", "duz", - "hrr", "jal", - "ibi", "opa", - "ilw", "gal", - "jeg", "oyb", - "kgc", "tdf", - "kgh", "kml", - "koj", "kwv", - "krm", "bmf", - "ktr", "dtp", - "kvs", "gdj", - "kwq", "yam", - "kxe", "tvd", - "kzj", "dtp", - "kzt", "dtp", - "lii", "raq", - "lmm", "rmx", - "meg", "cir", - "mst", "mry", - "mwj", "vaj", - "myt", "mry", - "nad", "xny", - "ncp", "kdz", - "nnx", "ngv", - "nts", "pij", - "oun", "vaj", - "pcr", "adx", - "pmc", "huw", - "pmu", "phr", - "ppa", "bfy", - "ppr", "lcq", - "pry", "prt", - "puz", "pub", - "sca", "hle", - "skk", "oyb", - "tdu", "dtp", - "thc", "tpo", - "thx", "oyb", - "tie", "ras", - "tkk", "twm", - "tlw", "weo", - "tmp", "tyj", - "tne", "kak", - "tnf", "prs", - "tsf", "taj", - "uok", "ema", - "xba", "cax", - "xia", "acn", - "xkh", "waw", - "xsj", "suj", - "ybd", "rki", - "yma", "lrr", - "ymt", "mtm", - "yos", "zom", - "yuu", "yug", -}; - -/* - Updated on 2018-04-24 from - - curl https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry | \ - grep 'Type: region' -A 7 | egrep 'Subtag|Prefe' | \ - grep -B1 'Preferred' | \ - awk -n '/Subtag/ {printf(" \"%s\", ", $2);} /Preferred/ {printf("\"%s\",\n", $2);}' -*/ -static const char DEPRECATEDREGIONS[][3] = { -/* deprecated new */ - "BU", "MM", - "DD", "DE", - "FX", "FR", - "TP", "TL", - "YD", "YE", - "ZR", "CD", -}; - -/* -* ------------------------------------------------- -* -* These ultag_ functions may be exposed as APIs later -* -* ------------------------------------------------- -*/ - -static ULanguageTag* -ultag_parse(const char* tag, int32_t tagLen, int32_t* parsedLen, UErrorCode* status); - -static void -ultag_close(ULanguageTag* langtag); - -static const char* -ultag_getLanguage(const ULanguageTag* langtag); - -#if 0 -static const char* -ultag_getJDKLanguage(const ULanguageTag* langtag); -#endif - -static const char* -ultag_getExtlang(const ULanguageTag* langtag, int32_t idx); - -static int32_t -ultag_getExtlangSize(const ULanguageTag* langtag); - -static const char* -ultag_getScript(const ULanguageTag* langtag); - -static const char* -ultag_getRegion(const ULanguageTag* langtag); - -static const char* -ultag_getVariant(const ULanguageTag* langtag, int32_t idx); - -static int32_t -ultag_getVariantsSize(const ULanguageTag* langtag); - -static const char* -ultag_getExtensionKey(const ULanguageTag* langtag, int32_t idx); - -static const char* -ultag_getExtensionValue(const ULanguageTag* langtag, int32_t idx); - -static int32_t -ultag_getExtensionsSize(const ULanguageTag* langtag); - -static const char* -ultag_getPrivateUse(const ULanguageTag* langtag); - -#if 0 -static const char* -ultag_getLegacy(const ULanguageTag* langtag); -#endif - -U_NAMESPACE_BEGIN - -/** - * \class LocalULanguageTagPointer - * "Smart pointer" class, closes a ULanguageTag via ultag_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @internal - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalULanguageTagPointer, ULanguageTag, ultag_close); - -U_NAMESPACE_END - -/* -* ------------------------------------------------- -* -* Language subtag syntax validation functions -* -* ------------------------------------------------- -*/ - -static UBool -_isAlphaString(const char* s, int32_t len) { - int32_t i; - for (i = 0; i < len; i++) { - if (!ISALPHA(*(s + i))) { - return false; - } - } - return true; -} - -static UBool -_isNumericString(const char* s, int32_t len) { - int32_t i; - for (i = 0; i < len; i++) { - if (!ISNUMERIC(*(s + i))) { - return false; - } - } - return true; -} - -static UBool -_isAlphaNumericString(const char* s, int32_t len) { - int32_t i; - for (i = 0; i < len; i++) { - if (!ISALPHA(*(s + i)) && !ISNUMERIC(*(s + i))) { - return false; - } - } - return true; -} - -static UBool -_isAlphaNumericStringLimitedLength(const char* s, int32_t len, int32_t min, int32_t max) { - if (len < 0) { - len = (int32_t)uprv_strlen(s); - } - if (len >= min && len <= max && _isAlphaNumericString(s, len)) { - return true; - } - return false; -} - -U_CFUNC UBool -ultag_isLanguageSubtag(const char* s, int32_t len) { - /* - * unicode_language_subtag = alpha{2,3} | alpha{5,8}; - * NOTE: Per ICUTC 2019/01/23- accepting alpha 4 - * See ICU-20372 - */ - if (len < 0) { - len = (int32_t)uprv_strlen(s); - } - if (len >= 2 && len <= 8 && _isAlphaString(s, len)) { - return true; - } - return false; -} - -static UBool -_isExtlangSubtag(const char* s, int32_t len) { - /* - * extlang = 3ALPHA ; selected ISO 639 codes - * *2("-" 3ALPHA) ; permanently reserved - */ - if (len < 0) { - len = (int32_t)uprv_strlen(s); - } - if (len == 3 && _isAlphaString(s, len)) { - return true; - } - return false; -} - -U_CFUNC UBool -ultag_isScriptSubtag(const char* s, int32_t len) { - /* - * script = 4ALPHA ; ISO 15924 code - */ - if (len < 0) { - len = (int32_t)uprv_strlen(s); - } - if (len == 4 && _isAlphaString(s, len)) { - return true; - } - return false; -} - -U_CFUNC UBool -ultag_isRegionSubtag(const char* s, int32_t len) { - /* - * region = 2ALPHA ; ISO 3166-1 code - * / 3DIGIT ; UN M.49 code - */ - if (len < 0) { - len = (int32_t)uprv_strlen(s); - } - if (len == 2 && _isAlphaString(s, len)) { - return true; - } - if (len == 3 && _isNumericString(s, len)) { - return true; - } - return false; -} - -static UBool -_isVariantSubtag(const char* s, int32_t len) { - /* - * variant = 5*8alphanum ; registered variants - * / (DIGIT 3alphanum) - */ - if (len < 0) { - len = (int32_t)uprv_strlen(s); - } - if (_isAlphaNumericStringLimitedLength(s, len, 5, 8)) { - return true; - } - if (len == 4 && ISNUMERIC(*s) && _isAlphaNumericString(s + 1, 3)) { - return true; - } - return false; -} - -static UBool -_isSepListOf(UBool (*test)(const char*, int32_t), const char* s, int32_t len) { - const char *p = s; - const char *pSubtag = NULL; - - if (len < 0) { - len = (int32_t)uprv_strlen(s); - } - - while ((p - s) < len) { - if (*p == SEP) { - if (pSubtag == NULL) { - return false; - } - if (!test(pSubtag, (int32_t)(p - pSubtag))) { - return false; - } - pSubtag = NULL; - } else if (pSubtag == NULL) { - pSubtag = p; - } - p++; - } - if (pSubtag == NULL) { - return false; - } - return test(pSubtag, (int32_t)(p - pSubtag)); -} - -U_CFUNC UBool -ultag_isVariantSubtags(const char* s, int32_t len) { - return _isSepListOf(&_isVariantSubtag, s, len); -} - -// This is for the ICU-specific "lvariant" handling. -static UBool -_isPrivateuseVariantSubtag(const char* s, int32_t len) { - /* - * variant = 1*8alphanum ; registered variants - * / (DIGIT 3alphanum) - */ - return _isAlphaNumericStringLimitedLength(s, len , 1, 8); -} - -static UBool -_isExtensionSingleton(const char* s, int32_t len) { - /* - * extension = singleton 1*("-" (2*8alphanum)) - * - * singleton = DIGIT ; 0 - 9 - * / %x41-57 ; A - W - * / %x59-5A ; Y - Z - * / %x61-77 ; a - w - * / %x79-7A ; y - z - */ - if (len < 0) { - len = (int32_t)uprv_strlen(s); - } - if (len == 1 && (ISALPHA(*s) || ISNUMERIC(*s)) && (uprv_tolower(*s) != PRIVATEUSE)) { - return true; - } - return false; -} - -static UBool -_isExtensionSubtag(const char* s, int32_t len) { - /* - * extension = singleton 1*("-" (2*8alphanum)) - */ - return _isAlphaNumericStringLimitedLength(s, len, 2, 8); -} - -U_CFUNC UBool -ultag_isExtensionSubtags(const char* s, int32_t len) { - return _isSepListOf(&_isExtensionSubtag, s, len); -} - -static UBool -_isPrivateuseValueSubtag(const char* s, int32_t len) { - /* - * privateuse = "x" 1*("-" (1*8alphanum)) - */ - return _isAlphaNumericStringLimitedLength(s, len, 1, 8); -} - -U_CFUNC UBool -ultag_isPrivateuseValueSubtags(const char* s, int32_t len) { - return _isSepListOf(&_isPrivateuseValueSubtag, s, len); -} - -U_CFUNC UBool -ultag_isUnicodeLocaleAttribute(const char* s, int32_t len) { - /* - * attribute = alphanum{3,8} ; - */ - return _isAlphaNumericStringLimitedLength(s, len , 3, 8); -} - -U_CFUNC UBool -ultag_isUnicodeLocaleAttributes(const char* s, int32_t len) { - return _isSepListOf(&ultag_isUnicodeLocaleAttribute, s, len); -} - -U_CFUNC UBool -ultag_isUnicodeLocaleKey(const char* s, int32_t len) { - /* - * key = alphanum alpha ; - */ - if (len < 0) { - len = (int32_t)uprv_strlen(s); - } - if (len == 2 && (ISALPHA(*s) || ISNUMERIC(*s)) && ISALPHA(s[1])) { - return true; - } - return false; -} - -U_CFUNC UBool -_isUnicodeLocaleTypeSubtag(const char*s, int32_t len) { - /* - * alphanum{3,8} - */ - return _isAlphaNumericStringLimitedLength(s, len , 3, 8); -} - -U_CFUNC UBool -ultag_isUnicodeLocaleType(const char*s, int32_t len) { - /* - * type = alphanum{3,8} (sep alphanum{3,8})* ; - */ - return _isSepListOf(&_isUnicodeLocaleTypeSubtag, s, len); -} - -static UBool -_isTKey(const char* s, int32_t len) -{ - /* - * tkey = alpha digit ; - */ - if (len < 0) { - len = (int32_t)uprv_strlen(s); - } - if (len == 2 && ISALPHA(*s) && ISNUMERIC(*(s + 1))) { - return true; - } - return false; -} - -U_CAPI const char * U_EXPORT2 -ultag_getTKeyStart(const char *localeID) { - const char *result = localeID; - const char *sep; - while((sep = uprv_strchr(result, SEP)) != nullptr) { - if (_isTKey(result, static_cast(sep - result))) { - return result; - } - result = ++sep; - } - if (_isTKey(result, -1)) { - return result; - } - return nullptr; -} - -static UBool -_isTValue(const char* s, int32_t len) -{ - /* - * tvalue = (sep alphanum{3,8})+ ; - */ - return _isAlphaNumericStringLimitedLength(s, len , 3, 8); -} - -static UBool -_isTransformedExtensionSubtag(int32_t& state, const char* s, int32_t len) -{ - const int32_t kStart = 0; // Start, wait for unicode_language_subtag, tkey or end - const int32_t kGotLanguage = 1; // Got unicode_language_subtag, wait for unicode_script_subtag, - // unicode_region_subtag, unicode_variant_subtag, tkey or end - const int32_t kGotScript = 2; // Got unicode_script_subtag, wait for unicode_region_subtag, - // unicode_variant_subtag, tkey, or end - const int32_t kGotRegion = 3; // Got unicode_region_subtag, wait for unicode_variant_subtag, - // tkey, or end. - const int32_t kGotVariant = 4; // Got unicode_variant_subtag, wait for unicode_variant_subtag - // tkey or end. - const int32_t kGotTKey = -1; // Got tkey, wait for tvalue. ERROR if stop here. - const int32_t kGotTValue = 6; // Got tvalue, wait for tkey, tvalue or end - - - if (len < 0) { - len = (int32_t)uprv_strlen(s); - } - switch (state) { - case kStart: - if (ultag_isLanguageSubtag(s, len) && len != 4) { - state = kGotLanguage; - return true; - } - if (_isTKey(s, len)) { - state = kGotTKey; - return true; - } - return false; - case kGotLanguage: - if (ultag_isScriptSubtag(s, len)) { - state = kGotScript; - return true; - } - U_FALLTHROUGH; - case kGotScript: - if (ultag_isRegionSubtag(s, len)) { - state = kGotRegion; - return true; - } - U_FALLTHROUGH; - case kGotRegion: - U_FALLTHROUGH; - case kGotVariant: - if (_isVariantSubtag(s, len)) { - state = kGotVariant; - return true; - } - if (_isTKey(s, len)) { - state = kGotTKey; - return true; - } - return false; - case kGotTKey: - if (_isTValue(s, len)) { - state = kGotTValue; - return true; - } - return false; - case kGotTValue: - if (_isTKey(s, len)) { - state = kGotTKey; - return true; - } - if (_isTValue(s, len)) { - return true; - } - return false; - } - return false; -} - -static UBool -_isUnicodeExtensionSubtag(int32_t& state, const char* s, int32_t len) -{ - const int32_t kStart = 0; // Start, wait for a key or attribute or end - const int32_t kGotKey = 1; // Got a key, wait for type or key or end - const int32_t kGotType = 2; // Got a type, wait for key or end - - switch (state) { - case kStart: - if (ultag_isUnicodeLocaleKey(s, len)) { - state = kGotKey; - return true; - } - if (ultag_isUnicodeLocaleAttribute(s, len)) { - return true; - } - return false; - case kGotKey: - if (ultag_isUnicodeLocaleKey(s, len)) { - return true; - } - if (_isUnicodeLocaleTypeSubtag(s, len)) { - state = kGotType; - return true; - } - return false; - case kGotType: - if (ultag_isUnicodeLocaleKey(s, len)) { - state = kGotKey; - return true; - } - if (_isUnicodeLocaleTypeSubtag(s, len)) { - return true; - } - return false; - } - return false; -} - -static UBool -_isStatefulSepListOf(UBool (*test)(int32_t&, const char*, int32_t), const char* s, int32_t len) -{ - int32_t state = 0; - const char* p; - const char* start = s; - int32_t subtagLen = 0; - - if (len < 0) { - len = (int32_t)uprv_strlen(s); - } - - for (p = s; len > 0; p++, len--) { - if (*p == SEP) { - if (!test(state, start, subtagLen)) { - return false; - } - subtagLen = 0; - start = p + 1; - } else { - subtagLen++; - } - } - - if (test(state, start, subtagLen) && state >= 0) { - return true; - } - return false; -} - -U_CFUNC UBool -ultag_isTransformedExtensionSubtags(const char* s, int32_t len) -{ - return _isStatefulSepListOf(&_isTransformedExtensionSubtag, s, len); -} - -U_CFUNC UBool -ultag_isUnicodeExtensionSubtags(const char* s, int32_t len) { - return _isStatefulSepListOf(&_isUnicodeExtensionSubtag, s, len); -} - - -/* -* ------------------------------------------------- -* -* Helper functions -* -* ------------------------------------------------- -*/ - -static UBool -_addVariantToList(VariantListEntry **first, VariantListEntry *var) { - UBool bAdded = true; - - if (*first == NULL) { - var->next = NULL; - *first = var; - } else { - VariantListEntry *prev, *cur; - int32_t cmp; - - /* variants order should be preserved */ - prev = NULL; - cur = *first; - while (true) { - if (cur == NULL) { - prev->next = var; - var->next = NULL; - break; - } - - /* Checking for duplicate variant */ - cmp = uprv_compareInvCharsAsAscii(var->variant, cur->variant); - if (cmp == 0) { - /* duplicated variant */ - bAdded = false; - break; - } - prev = cur; - cur = cur->next; - } - } - - return bAdded; -} - -static UBool -_addAttributeToList(AttributeListEntry **first, AttributeListEntry *attr) { - UBool bAdded = true; - - if (*first == NULL) { - attr->next = NULL; - *first = attr; - } else { - AttributeListEntry *prev, *cur; - int32_t cmp; - - /* reorder variants in alphabetical order */ - prev = NULL; - cur = *first; - while (true) { - if (cur == NULL) { - prev->next = attr; - attr->next = NULL; - break; - } - cmp = uprv_compareInvCharsAsAscii(attr->attribute, cur->attribute); - if (cmp < 0) { - if (prev == NULL) { - *first = attr; - } else { - prev->next = attr; - } - attr->next = cur; - break; - } - if (cmp == 0) { - /* duplicated variant */ - bAdded = false; - break; - } - prev = cur; - cur = cur->next; - } - } - - return bAdded; -} - - -static UBool -_addExtensionToList(ExtensionListEntry **first, ExtensionListEntry *ext, UBool localeToBCP) { - UBool bAdded = true; - - if (*first == NULL) { - ext->next = NULL; - *first = ext; - } else { - ExtensionListEntry *prev, *cur; - int32_t cmp; - - /* reorder variants in alphabetical order */ - prev = NULL; - cur = *first; - while (true) { - if (cur == NULL) { - prev->next = ext; - ext->next = NULL; - break; - } - if (localeToBCP) { - /* special handling for locale to bcp conversion */ - int32_t len, curlen; - - len = (int32_t)uprv_strlen(ext->key); - curlen = (int32_t)uprv_strlen(cur->key); - - if (len == 1 && curlen == 1) { - if (*(ext->key) == *(cur->key)) { - cmp = 0; - } else if (*(ext->key) == PRIVATEUSE) { - cmp = 1; - } else if (*(cur->key) == PRIVATEUSE) { - cmp = -1; - } else { - cmp = *(ext->key) - *(cur->key); - } - } else if (len == 1) { - cmp = *(ext->key) - LDMLEXT; - } else if (curlen == 1) { - cmp = LDMLEXT - *(cur->key); - } else { - cmp = uprv_compareInvCharsAsAscii(ext->key, cur->key); - /* Both are u extension keys - we need special handling for 'attribute' */ - if (cmp != 0) { - if (uprv_strcmp(cur->key, LOCALE_ATTRIBUTE_KEY) == 0) { - cmp = 1; - } else if (uprv_strcmp(ext->key, LOCALE_ATTRIBUTE_KEY) == 0) { - cmp = -1; - } - } - } - } else { - cmp = uprv_compareInvCharsAsAscii(ext->key, cur->key); - } - if (cmp < 0) { - if (prev == NULL) { - *first = ext; - } else { - prev->next = ext; - } - ext->next = cur; - break; - } - if (cmp == 0) { - /* duplicated extension key */ - bAdded = false; - break; - } - prev = cur; - cur = cur->next; - } - } - - return bAdded; -} - -static void -_initializeULanguageTag(ULanguageTag* langtag) { - int32_t i; - - langtag->buf = NULL; - - langtag->language = EMPTY; - for (i = 0; i < MAXEXTLANG; i++) { - langtag->extlang[i] = NULL; - } - - langtag->script = EMPTY; - langtag->region = EMPTY; - - langtag->variants = NULL; - langtag->extensions = NULL; - - langtag->legacy = EMPTY; - langtag->privateuse = EMPTY; -} - -static void -_appendLanguageToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool strict, UErrorCode* status) { - char buf[ULOC_LANG_CAPACITY]; - UErrorCode tmpStatus = U_ZERO_ERROR; - int32_t len, i; - - if (U_FAILURE(*status)) { - return; - } - - len = uloc_getLanguage(localeID, buf, sizeof(buf), &tmpStatus); - if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) { - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - len = 0; - } - - /* Note: returned language code is in lower case letters */ - - if (len == 0) { - sink.Append(LANG_UND, LANG_UND_LEN); - } else if (!ultag_isLanguageSubtag(buf, len)) { - /* invalid language code */ - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - sink.Append(LANG_UND, LANG_UND_LEN); - } else { - /* resolve deprecated */ - for (i = 0; i < UPRV_LENGTHOF(DEPRECATEDLANGS); i += 2) { - // 2-letter deprecated subtags are listede before 3-letter - // ones in DEPRECATEDLANGS[]. Get out of loop on coming - // across the 1st 3-letter subtag, if the input is a 2-letter code. - // to avoid continuing to try when there's no match. - if (uprv_strlen(buf) < uprv_strlen(DEPRECATEDLANGS[i])) break; - if (uprv_compareInvCharsAsAscii(buf, DEPRECATEDLANGS[i]) == 0) { - uprv_strcpy(buf, DEPRECATEDLANGS[i + 1]); - len = (int32_t)uprv_strlen(buf); - break; - } - } - sink.Append(buf, len); - } -} - -static void -_appendScriptToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool strict, UErrorCode* status) { - char buf[ULOC_SCRIPT_CAPACITY]; - UErrorCode tmpStatus = U_ZERO_ERROR; - int32_t len; - - if (U_FAILURE(*status)) { - return; - } - - len = uloc_getScript(localeID, buf, sizeof(buf), &tmpStatus); - if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) { - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - } - return; - } - - if (len > 0) { - if (!ultag_isScriptSubtag(buf, len)) { - /* invalid script code */ - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - } - return; - } else { - sink.Append("-", 1); - sink.Append(buf, len); - } - } -} - -static void -_appendRegionToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool strict, UErrorCode* status) { - char buf[ULOC_COUNTRY_CAPACITY]; - UErrorCode tmpStatus = U_ZERO_ERROR; - int32_t len; - - if (U_FAILURE(*status)) { - return; - } - - len = uloc_getCountry(localeID, buf, sizeof(buf), &tmpStatus); - if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) { - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - } - return; - } - - if (len > 0) { - if (!ultag_isRegionSubtag(buf, len)) { - /* invalid region code */ - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - } - return; - } else { - sink.Append("-", 1); - /* resolve deprecated */ - for (int i = 0; i < UPRV_LENGTHOF(DEPRECATEDREGIONS); i += 2) { - if (uprv_compareInvCharsAsAscii(buf, DEPRECATEDREGIONS[i]) == 0) { - uprv_strcpy(buf, DEPRECATEDREGIONS[i + 1]); - len = (int32_t)uprv_strlen(buf); - break; - } - } - sink.Append(buf, len); - } - } -} - -static void _sortVariants(VariantListEntry* first) { - for (VariantListEntry* var1 = first; var1 != NULL; var1 = var1->next) { - for (VariantListEntry* var2 = var1->next; var2 != NULL; var2 = var2->next) { - // Swap var1->variant and var2->variant. - if (uprv_compareInvCharsAsAscii(var1->variant, var2->variant) > 0) { - const char* temp = var1->variant; - var1->variant = var2->variant; - var2->variant = temp; - } - } - } -} - -static void -_appendVariantsToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool strict, UBool *hadPosix, UErrorCode* status) { - char buf[ULOC_FULLNAME_CAPACITY]; - UErrorCode tmpStatus = U_ZERO_ERROR; - int32_t len, i; - - if (U_FAILURE(*status)) { - return; - } - - len = uloc_getVariant(localeID, buf, sizeof(buf), &tmpStatus); - if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) { - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - } - return; - } - - if (len > 0) { - char *p, *pVar; - UBool bNext = true; - VariantListEntry *var; - VariantListEntry *varFirst = NULL; - - pVar = NULL; - p = buf; - while (bNext) { - if (*p == SEP || *p == LOCALE_SEP || *p == 0) { - if (*p == 0) { - bNext = false; - } else { - *p = 0; /* terminate */ - } - if (pVar == NULL) { - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - /* ignore empty variant */ - } else { - /* ICU uses upper case letters for variants, but - the canonical format is lowercase in BCP47 */ - for (i = 0; *(pVar + i) != 0; i++) { - *(pVar + i) = uprv_tolower(*(pVar + i)); - } - - /* validate */ - if (_isVariantSubtag(pVar, -1)) { - if (uprv_strcmp(pVar,POSIX_VALUE) || len != (int32_t)uprv_strlen(POSIX_VALUE)) { - /* emit the variant to the list */ - var = (VariantListEntry*)uprv_malloc(sizeof(VariantListEntry)); - if (var == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - break; - } - var->variant = pVar; - if (!_addVariantToList(&varFirst, var)) { - /* duplicated variant */ - uprv_free(var); - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - } - } else { - /* Special handling for POSIX variant, need to remember that we had it and then */ - /* treat it like an extension later. */ - *hadPosix = true; - } - } else if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - break; - } else if (_isPrivateuseValueSubtag(pVar, -1)) { - /* Handle private use subtags separately */ - break; - } - } - /* reset variant starting position */ - pVar = NULL; - } else if (pVar == NULL) { - pVar = p; - } - p++; - } - - if (U_SUCCESS(*status)) { - if (varFirst != NULL) { - int32_t varLen; - - /* per UTS35, we should sort the variants */ - _sortVariants(varFirst); - - /* write out validated/normalized variants to the target */ - var = varFirst; - while (var != NULL) { - sink.Append("-", 1); - varLen = (int32_t)uprv_strlen(var->variant); - sink.Append(var->variant, varLen); - var = var->next; - } - } - } - - /* clean up */ - var = varFirst; - while (var != NULL) { - VariantListEntry *tmpVar = var->next; - uprv_free(var); - var = tmpVar; - } - - if (U_FAILURE(*status)) { - return; - } - } -} - -static void -_appendKeywordsToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool strict, UBool hadPosix, UErrorCode* status) { - char attrBuf[ULOC_KEYWORD_AND_VALUES_CAPACITY] = { 0 }; - int32_t attrBufLength = 0; - - icu::MemoryPool attrPool; - icu::MemoryPool extPool; - icu::MemoryPool strPool; - - icu::LocalUEnumerationPointer keywordEnum(uloc_openKeywords(localeID, status)); - if (U_FAILURE(*status) && !hadPosix) { - return; - } - if (keywordEnum.isValid() || hadPosix) { - /* reorder extensions */ - int32_t len; - const char *key; - ExtensionListEntry *firstExt = NULL; - ExtensionListEntry *ext; - AttributeListEntry *firstAttr = NULL; - AttributeListEntry *attr; - icu::MemoryPool extBufPool; - const char *bcpKey=nullptr, *bcpValue=nullptr; - UErrorCode tmpStatus = U_ZERO_ERROR; - int32_t keylen; - UBool isBcpUExt; - - while (true) { - key = uenum_next(keywordEnum.getAlias(), NULL, status); - if (key == NULL) { - break; - } - - icu::CharString buf; - { - icu::CharStringByteSink sink(&buf); - ulocimp_getKeywordValue(localeID, key, sink, &tmpStatus); - } - len = buf.length(); - - if (U_FAILURE(tmpStatus)) { - if (tmpStatus == U_MEMORY_ALLOCATION_ERROR) { - *status = U_MEMORY_ALLOCATION_ERROR; - break; - } - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - /* ignore this keyword */ - tmpStatus = U_ZERO_ERROR; - continue; - } - - keylen = (int32_t)uprv_strlen(key); - isBcpUExt = (keylen > 1); - - /* special keyword used for representing Unicode locale attributes */ - if (uprv_strcmp(key, LOCALE_ATTRIBUTE_KEY) == 0) { - if (len > 0) { - int32_t i = 0; - while (true) { - attrBufLength = 0; - for (; i < len; i++) { - if (buf[i] != '-') { - attrBuf[attrBufLength++] = buf[i]; - } else { - i++; - break; - } - } - if (attrBufLength > 0) { - attrBuf[attrBufLength] = 0; - - } else if (i >= len){ - break; - } - - /* create AttributeListEntry */ - attr = attrPool.create(); - if (attr == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - break; - } - icu::CharString* attrValue = - strPool.create(attrBuf, attrBufLength, *status); - if (attrValue == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - break; - } - if (U_FAILURE(*status)) { - break; - } - attr->attribute = attrValue->data(); - - if (!_addAttributeToList(&firstAttr, attr)) { - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - } - } - /* for a place holder ExtensionListEntry */ - bcpKey = LOCALE_ATTRIBUTE_KEY; - bcpValue = NULL; - } - } else if (isBcpUExt) { - bcpKey = uloc_toUnicodeLocaleKey(key); - if (bcpKey == NULL) { - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - continue; - } - - /* we've checked buf is null-terminated above */ - bcpValue = uloc_toUnicodeLocaleType(key, buf.data()); - if (bcpValue == NULL) { - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - continue; - } - if (bcpValue == buf.data()) { - /* - When uloc_toUnicodeLocaleType(key, buf) returns the - input value as is, the value is well-formed, but has - no known mapping. This implementation normalizes the - value to lower case - */ - icu::CharString* extBuf = extBufPool.create(buf, tmpStatus); - - if (extBuf == nullptr) { - *status = U_MEMORY_ALLOCATION_ERROR; - break; - } - if (U_FAILURE(tmpStatus)) { - *status = tmpStatus; - break; - } - - T_CString_toLowerCase(extBuf->data()); - bcpValue = extBuf->data(); - } - } else { - if (*key == PRIVATEUSE) { - if (!ultag_isPrivateuseValueSubtags(buf.data(), len)) { - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - continue; - } - } else { - if (!_isExtensionSingleton(key, keylen) || !ultag_isExtensionSubtags(buf.data(), len)) { - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - continue; - } - } - bcpKey = key; - icu::CharString* extBuf = - extBufPool.create(buf.data(), len, tmpStatus); - if (extBuf == nullptr) { - *status = U_MEMORY_ALLOCATION_ERROR; - break; - } - if (U_FAILURE(tmpStatus)) { - *status = tmpStatus; - break; - } - bcpValue = extBuf->data(); - } - - /* create ExtensionListEntry */ - ext = extPool.create(); - if (ext == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - break; - } - ext->key = bcpKey; - ext->value = bcpValue; - - if (!_addExtensionToList(&firstExt, ext, true)) { - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - } - } - - /* Special handling for POSIX variant - add the keywords for POSIX */ - if (hadPosix) { - /* create ExtensionListEntry for POSIX */ - ext = extPool.create(); - if (ext == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return; - } - ext->key = POSIX_KEY; - ext->value = POSIX_VALUE; - - if (!_addExtensionToList(&firstExt, ext, true)) { - // Silently ignore errors. - } - } - - if (U_SUCCESS(*status) && (firstExt != NULL || firstAttr != NULL)) { - UBool startLDMLExtension = false; - for (ext = firstExt; ext; ext = ext->next) { - if (!startLDMLExtension && uprv_strlen(ext->key) > 1) { - /* first LDML u singlton extension */ - sink.Append("-u", 2); - startLDMLExtension = true; - } - - /* write out the sorted BCP47 attributes, extensions and private use */ - if (uprv_strcmp(ext->key, LOCALE_ATTRIBUTE_KEY) == 0) { - /* write the value for the attributes */ - for (attr = firstAttr; attr; attr = attr->next) { - sink.Append("-", 1); - sink.Append( - attr->attribute, static_cast(uprv_strlen(attr->attribute))); - } - } else { - sink.Append("-", 1); - sink.Append(ext->key, static_cast(uprv_strlen(ext->key))); - if (uprv_strcmp(ext->value, "true") != 0 && - uprv_strcmp(ext->value, "yes") != 0) { - sink.Append("-", 1); - sink.Append(ext->value, static_cast(uprv_strlen(ext->value))); - } - } - } - } - } -} - -/** - * Append keywords parsed from LDML extension value - * e.g. "u-ca-gregory-co-trad" -> {calendar = gregorian} {collation = traditional} - * Note: char* buf is used for storing keywords - */ -static void -_appendLDMLExtensionAsKeywords(const char* ldmlext, ExtensionListEntry** appendTo, icu::MemoryPool& extPool, icu::MemoryPool& kwdBuf, UBool *posixVariant, UErrorCode *status) { - const char *pTag; /* beginning of current subtag */ - const char *pKwds; /* beginning of key-type pairs */ - UBool variantExists = *posixVariant; - - ExtensionListEntry *kwdFirst = NULL; /* first LDML keyword */ - ExtensionListEntry *kwd, *nextKwd; - - int32_t len; - - /* Reset the posixVariant value */ - *posixVariant = false; - - pTag = ldmlext; - pKwds = NULL; - - { - AttributeListEntry *attrFirst = NULL; /* first attribute */ - AttributeListEntry *attr, *nextAttr; - - char attrBuf[ULOC_KEYWORD_AND_VALUES_CAPACITY]; - int32_t attrBufIdx = 0; - - icu::MemoryPool attrPool; - - /* Iterate through u extension attributes */ - while (*pTag) { - /* locate next separator char */ - for (len = 0; *(pTag + len) && *(pTag + len) != SEP; len++); - - if (ultag_isUnicodeLocaleKey(pTag, len)) { - pKwds = pTag; - break; - } - - /* add this attribute to the list */ - attr = attrPool.create(); - if (attr == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - if (len < (int32_t)sizeof(attrBuf) - attrBufIdx) { - uprv_memcpy(&attrBuf[attrBufIdx], pTag, len); - attrBuf[attrBufIdx + len] = 0; - attr->attribute = &attrBuf[attrBufIdx]; - attrBufIdx += (len + 1); - } else { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - // duplicate attribute is ignored, causes no error. - _addAttributeToList(&attrFirst, attr); - - /* next tag */ - pTag += len; - if (*pTag) { - /* next to the separator */ - pTag++; - } - } - - if (attrFirst) { - /* emit attributes as an LDML keyword, e.g. attribute=attr1-attr2 */ - - kwd = extPool.create(); - if (kwd == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - icu::CharString* value = kwdBuf.create(); - if (value == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - /* attribute subtags sorted in alphabetical order as type */ - attr = attrFirst; - while (attr != NULL) { - nextAttr = attr->next; - if (attr != attrFirst) { - value->append('-', *status); - } - value->append(attr->attribute, *status); - attr = nextAttr; - } - if (U_FAILURE(*status)) { - return; - } - - kwd->key = LOCALE_ATTRIBUTE_KEY; - kwd->value = value->data(); - - if (!_addExtensionToList(&kwdFirst, kwd, false)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - } - } - - if (pKwds) { - const char *pBcpKey = NULL; /* u extension key subtag */ - const char *pBcpType = NULL; /* beginning of u extension type subtag(s) */ - int32_t bcpKeyLen = 0; - int32_t bcpTypeLen = 0; - UBool isDone = false; - - pTag = pKwds; - /* BCP47 representation of LDML key/type pairs */ - while (!isDone) { - const char *pNextBcpKey = NULL; - int32_t nextBcpKeyLen = 0; - UBool emitKeyword = false; - - if (*pTag) { - /* locate next separator char */ - for (len = 0; *(pTag + len) && *(pTag + len) != SEP; len++); - - if (ultag_isUnicodeLocaleKey(pTag, len)) { - if (pBcpKey) { - emitKeyword = true; - pNextBcpKey = pTag; - nextBcpKeyLen = len; - } else { - pBcpKey = pTag; - bcpKeyLen = len; - } - } else { - U_ASSERT(pBcpKey != NULL); - /* within LDML type subtags */ - if (pBcpType) { - bcpTypeLen += (len + 1); - } else { - pBcpType = pTag; - bcpTypeLen = len; - } - } - - /* next tag */ - pTag += len; - if (*pTag) { - /* next to the separator */ - pTag++; - } - } else { - /* processing last one */ - emitKeyword = true; - isDone = true; - } - - if (emitKeyword) { - const char *pKey = NULL; /* LDML key */ - const char *pType = NULL; /* LDML type */ - - char bcpKeyBuf[3]; /* BCP key length is always 2 for now */ - - U_ASSERT(pBcpKey != NULL); - - if (bcpKeyLen >= (int32_t)sizeof(bcpKeyBuf)) { - /* the BCP key is invalid */ - *status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - U_ASSERT(bcpKeyLen <= 2); - - uprv_strncpy(bcpKeyBuf, pBcpKey, bcpKeyLen); - bcpKeyBuf[bcpKeyLen] = 0; - - /* u extension key to LDML key */ - pKey = uloc_toLegacyKey(bcpKeyBuf); - if (pKey == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (pKey == bcpKeyBuf) { - /* - The key returned by toLegacyKey points to the input buffer. - We normalize the result key to lower case. - */ - T_CString_toLowerCase(bcpKeyBuf); - icu::CharString* key = kwdBuf.create(bcpKeyBuf, bcpKeyLen, *status); - if (key == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (U_FAILURE(*status)) { - return; - } - pKey = key->data(); - } - - if (pBcpType) { - char bcpTypeBuf[128]; /* practically long enough even considering multiple subtag type */ - if (bcpTypeLen >= (int32_t)sizeof(bcpTypeBuf)) { - /* the BCP type is too long */ - *status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - uprv_strncpy(bcpTypeBuf, pBcpType, bcpTypeLen); - bcpTypeBuf[bcpTypeLen] = 0; - - /* BCP type to locale type */ - pType = uloc_toLegacyType(pKey, bcpTypeBuf); - if (pType == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (pType == bcpTypeBuf) { - /* - The type returned by toLegacyType points to the input buffer. - We normalize the result type to lower case. - */ - /* normalize to lower case */ - T_CString_toLowerCase(bcpTypeBuf); - icu::CharString* type = kwdBuf.create(bcpTypeBuf, bcpTypeLen, *status); - if (type == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (U_FAILURE(*status)) { - return; - } - pType = type->data(); - } - } else { - /* typeless - default type value is "yes" */ - pType = LOCALE_TYPE_YES; - } - - /* Special handling for u-va-posix, since we want to treat this as a variant, - not as a keyword */ - if (!variantExists && !uprv_strcmp(pKey, POSIX_KEY) && !uprv_strcmp(pType, POSIX_VALUE) ) { - *posixVariant = true; - } else { - /* create an ExtensionListEntry for this keyword */ - kwd = extPool.create(); - if (kwd == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - kwd->key = pKey; - kwd->value = pType; - - if (!_addExtensionToList(&kwdFirst, kwd, false)) { - // duplicate keyword is allowed, Only the first - // is honored. - } - } - - pBcpKey = pNextBcpKey; - bcpKeyLen = pNextBcpKey != NULL ? nextBcpKeyLen : 0; - pBcpType = NULL; - bcpTypeLen = 0; - } - } - } - - kwd = kwdFirst; - while (kwd != NULL) { - nextKwd = kwd->next; - _addExtensionToList(appendTo, kwd, false); - kwd = nextKwd; - } -} - - -static void -_appendKeywords(ULanguageTag* langtag, icu::ByteSink& sink, UErrorCode* status) { - int32_t i, n; - int32_t len; - ExtensionListEntry *kwdFirst = NULL; - ExtensionListEntry *kwd; - const char *key, *type; - icu::MemoryPool extPool; - icu::MemoryPool kwdBuf; - UBool posixVariant = false; - - if (U_FAILURE(*status)) { - return; - } - - n = ultag_getExtensionsSize(langtag); - - /* resolve locale keywords and reordering keys */ - for (i = 0; i < n; i++) { - key = ultag_getExtensionKey(langtag, i); - type = ultag_getExtensionValue(langtag, i); - if (*key == LDMLEXT) { - /* Determine if variants already exists */ - if (ultag_getVariantsSize(langtag)) { - posixVariant = true; - } - - _appendLDMLExtensionAsKeywords(type, &kwdFirst, extPool, kwdBuf, &posixVariant, status); - if (U_FAILURE(*status)) { - break; - } - } else { - kwd = extPool.create(); - if (kwd == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - break; - } - kwd->key = key; - kwd->value = type; - if (!_addExtensionToList(&kwdFirst, kwd, false)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - } - } - - if (U_SUCCESS(*status)) { - type = ultag_getPrivateUse(langtag); - if ((int32_t)uprv_strlen(type) > 0) { - /* add private use as a keyword */ - kwd = extPool.create(); - if (kwd == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - } else { - kwd->key = PRIVATEUSE_KEY; - kwd->value = type; - if (!_addExtensionToList(&kwdFirst, kwd, false)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - } - } - } - } - - /* If a POSIX variant was in the extensions, write it out before writing the keywords. */ - - if (U_SUCCESS(*status) && posixVariant) { - len = (int32_t) uprv_strlen(_POSIX); - sink.Append(_POSIX, len); - } - - if (U_SUCCESS(*status) && kwdFirst != NULL) { - /* write out the sorted keywords */ - UBool firstValue = true; - kwd = kwdFirst; - do { - if (firstValue) { - sink.Append("@", 1); - firstValue = false; - } else { - sink.Append(";", 1); - } - - /* key */ - len = (int32_t)uprv_strlen(kwd->key); - sink.Append(kwd->key, len); - sink.Append("=", 1); - - /* type */ - len = (int32_t)uprv_strlen(kwd->value); - sink.Append(kwd->value, len); - - kwd = kwd->next; - } while (kwd); - } -} - -static void -_appendPrivateuseToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool strict, UBool hadPosix, UErrorCode* status) { - (void)hadPosix; - char buf[ULOC_FULLNAME_CAPACITY]; - char tmpAppend[ULOC_FULLNAME_CAPACITY]; - UErrorCode tmpStatus = U_ZERO_ERROR; - int32_t len, i; - int32_t reslen = 0; - int32_t capacity = sizeof tmpAppend; - - if (U_FAILURE(*status)) { - return; - } - - len = uloc_getVariant(localeID, buf, sizeof(buf), &tmpStatus); - if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) { - if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - } - return; - } - - if (len > 0) { - char *p, *pPriv; - UBool bNext = true; - UBool firstValue = true; - UBool writeValue; - - pPriv = NULL; - p = buf; - while (bNext) { - writeValue = false; - if (*p == SEP || *p == LOCALE_SEP || *p == 0) { - if (*p == 0) { - bNext = false; - } else { - *p = 0; /* terminate */ - } - if (pPriv != NULL) { - /* Private use in the canonical format is lowercase in BCP47 */ - for (i = 0; *(pPriv + i) != 0; i++) { - *(pPriv + i) = uprv_tolower(*(pPriv + i)); - } - - /* validate */ - if (_isPrivateuseValueSubtag(pPriv, -1)) { - if (firstValue) { - if (!_isVariantSubtag(pPriv, -1)) { - writeValue = true; - } - } else { - writeValue = true; - } - } else if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - break; - } else { - break; - } - - if (writeValue) { - if (reslen < capacity) { - tmpAppend[reslen++] = SEP; - } - - if (firstValue) { - if (reslen < capacity) { - tmpAppend[reslen++] = *PRIVATEUSE_KEY; - } - - if (reslen < capacity) { - tmpAppend[reslen++] = SEP; - } - - len = (int32_t)uprv_strlen(PRIVUSE_VARIANT_PREFIX); - if (reslen < capacity) { - uprv_memcpy(tmpAppend + reslen, PRIVUSE_VARIANT_PREFIX, uprv_min(len, capacity - reslen)); - } - reslen += len; - - if (reslen < capacity) { - tmpAppend[reslen++] = SEP; - } - - firstValue = false; - } - - len = (int32_t)uprv_strlen(pPriv); - if (reslen < capacity) { - uprv_memcpy(tmpAppend + reslen, pPriv, uprv_min(len, capacity - reslen)); - } - reslen += len; - } - } - /* reset private use starting position */ - pPriv = NULL; - } else if (pPriv == NULL) { - pPriv = p; - } - p++; - } - - if (U_FAILURE(*status)) { - return; - } - } - - if (U_SUCCESS(*status)) { - len = reslen; - sink.Append(tmpAppend, len); - } -} - -/* -* ------------------------------------------------- -* -* ultag_ functions -* -* ------------------------------------------------- -*/ - -/* Bit flags used by the parser */ -#define LANG 0x0001 -#define EXTL 0x0002 -#define SCRT 0x0004 -#define REGN 0x0008 -#define VART 0x0010 -#define EXTS 0x0020 -#define EXTV 0x0040 -#define PRIV 0x0080 - -/** - * Ticket #12705 - The optimizer in Visual Studio 2015 Update 3 has problems optimizing this function. - * As a work-around, optimization is disabled for this function on VS2015 and VS2017. - * This work-around should be removed once the following versions of Visual Studio are no - * longer supported: All versions of VS2015/VS2017, and versions of VS2019 below 16.4. - */ -#if defined(_MSC_VER) && (_MSC_VER >= 1900) && (_MSC_VER < 1924) -#pragma optimize( "", off ) -#endif - -static ULanguageTag* -ultag_parse(const char* tag, int32_t tagLen, int32_t* parsedLen, UErrorCode* status) { - char *tagBuf; - int16_t next; - char *pSubtag, *pNext, *pLastGoodPosition; - int32_t subtagLen; - int32_t extlangIdx; - ExtensionListEntry *pExtension; - char *pExtValueSubtag, *pExtValueSubtagEnd; - int32_t i; - UBool privateuseVar = false; - int32_t legacyLen = 0; - - if (parsedLen != NULL) { - *parsedLen = 0; - } - - if (U_FAILURE(*status)) { - return NULL; - } - - if (tagLen < 0) { - tagLen = (int32_t)uprv_strlen(tag); - } - - /* copy the entire string */ - tagBuf = (char*)uprv_malloc(tagLen + 1); - if (tagBuf == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - if (tagLen > 0) { - uprv_memcpy(tagBuf, tag, tagLen); - } - *(tagBuf + tagLen) = 0; - - /* create a ULanguageTag */ - icu::LocalULanguageTagPointer t( - (ULanguageTag*)uprv_malloc(sizeof(ULanguageTag))); - if (t.isNull()) { - uprv_free(tagBuf); - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - _initializeULanguageTag(t.getAlias()); - t->buf = tagBuf; - - if (tagLen < MINLEN) { - /* the input tag is too short - return empty ULanguageTag */ - return t.orphan(); - } - - size_t parsedLenDelta = 0; - // Legacy tag will be consider together. Legacy tag with intervening - // script and region such as art-DE-lojban or art-Latn-lojban won't be - // matched. - /* check if the tag is legacy */ - for (i = 0; i < UPRV_LENGTHOF(LEGACY); i += 2) { - int32_t checkLegacyLen = static_cast(uprv_strlen(LEGACY[i])); - if (tagLen < checkLegacyLen) { - continue; - } - if (tagLen > checkLegacyLen && tagBuf[checkLegacyLen] != '-') { - // make sure next char is '-'. - continue; - } - if (uprv_strnicmp(LEGACY[i], tagBuf, checkLegacyLen) == 0) { - int32_t newTagLength; - - legacyLen = checkLegacyLen; /* back up for output parsedLen */ - int32_t replacementLen = static_cast(uprv_strlen(LEGACY[i+1])); - newTagLength = replacementLen + tagLen - checkLegacyLen; - int32_t oldTagLength = tagLen; - if (tagLen < newTagLength) { - uprv_free(tagBuf); - tagBuf = (char*)uprv_malloc(newTagLength + 1); - if (tagBuf == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - t->buf = tagBuf; - tagLen = newTagLength; - } - parsedLenDelta = checkLegacyLen - replacementLen; - uprv_strcpy(t->buf, LEGACY[i + 1]); - if (checkLegacyLen != tagLen) { - uprv_memcpy(t->buf + replacementLen, tag + checkLegacyLen, - oldTagLength - checkLegacyLen); - // NUL-terminate after memcpy(). - t->buf[replacementLen + oldTagLength - checkLegacyLen] = 0; - } - break; - } - } - - if (legacyLen == 0) { - for (i = 0; i < UPRV_LENGTHOF(REDUNDANT); i += 2) { - const char* redundantTag = REDUNDANT[i]; - size_t redundantTagLen = uprv_strlen(redundantTag); - // The preferred tag for a redundant tag is always shorter than redundant - // tag. A redundant tag may or may not be followed by other subtags. - // (i.e. "zh-yue" or "zh-yue-u-co-pinyin"). - if (uprv_strnicmp(redundantTag, tagBuf, static_cast(redundantTagLen)) == 0) { - const char* redundantTagEnd = tagBuf + redundantTagLen; - if (*redundantTagEnd == '\0' || *redundantTagEnd == SEP) { - const char* preferredTag = REDUNDANT[i + 1]; - size_t preferredTagLen = uprv_strlen(preferredTag); - uprv_memcpy(t->buf, preferredTag, preferredTagLen); - if (*redundantTagEnd == SEP) { - uprv_memmove(tagBuf + preferredTagLen, - redundantTagEnd, - tagLen - redundantTagLen + 1); - } else { - tagBuf[preferredTagLen] = '\0'; - } - // parsedLen should be the length of the input - // before redundantTag is replaced by preferredTag. - // Save the delta to add it back later. - parsedLenDelta = redundantTagLen - preferredTagLen; - break; - } - } - } - } - - /* - * langtag = language - * ["-" script] - * ["-" region] - * *("-" variant) - * *("-" extension) - * ["-" privateuse] - */ - - next = LANG | PRIV; - pNext = pLastGoodPosition = tagBuf; - extlangIdx = 0; - pExtension = NULL; - pExtValueSubtag = NULL; - pExtValueSubtagEnd = NULL; - - while (pNext) { - char *pSep; - - pSubtag = pNext; - - /* locate next separator char */ - pSep = pSubtag; - while (*pSep) { - if (*pSep == SEP) { - break; - } - pSep++; - } - if (*pSep == 0) { - /* last subtag */ - pNext = NULL; - } else { - pNext = pSep + 1; - } - subtagLen = (int32_t)(pSep - pSubtag); - - if (next & LANG) { - if (ultag_isLanguageSubtag(pSubtag, subtagLen)) { - *pSep = 0; /* terminate */ - // TODO: move deprecated language code handling here. - t->language = T_CString_toLowerCase(pSubtag); - - pLastGoodPosition = pSep; - next = SCRT | REGN | VART | EXTS | PRIV; - if (subtagLen <= 3) - next |= EXTL; - continue; - } - } - if (next & EXTL) { - if (_isExtlangSubtag(pSubtag, subtagLen)) { - *pSep = 0; - t->extlang[extlangIdx++] = T_CString_toLowerCase(pSubtag); - - pLastGoodPosition = pSep; - if (extlangIdx < 3) { - next = EXTL | SCRT | REGN | VART | EXTS | PRIV; - } else { - next = SCRT | REGN | VART | EXTS | PRIV; - } - continue; - } - } - if (next & SCRT) { - if (ultag_isScriptSubtag(pSubtag, subtagLen)) { - char *p = pSubtag; - - *pSep = 0; - - /* to title case */ - *p = uprv_toupper(*p); - p++; - for (; *p; p++) { - *p = uprv_tolower(*p); - } - - t->script = pSubtag; - - pLastGoodPosition = pSep; - next = REGN | VART | EXTS | PRIV; - continue; - } - } - if (next & REGN) { - if (ultag_isRegionSubtag(pSubtag, subtagLen)) { - *pSep = 0; - // TODO: move deprecated region code handling here. - t->region = T_CString_toUpperCase(pSubtag); - - pLastGoodPosition = pSep; - next = VART | EXTS | PRIV; - continue; - } - } - if (next & VART) { - if (_isVariantSubtag(pSubtag, subtagLen) || - (privateuseVar && _isPrivateuseVariantSubtag(pSubtag, subtagLen))) { - VariantListEntry *var; - UBool isAdded; - - var = (VariantListEntry*)uprv_malloc(sizeof(VariantListEntry)); - if (var == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - *pSep = 0; - var->variant = T_CString_toUpperCase(pSubtag); - isAdded = _addVariantToList(&(t->variants), var); - if (!isAdded) { - /* duplicated variant entry */ - uprv_free(var); - break; - } - pLastGoodPosition = pSep; - next = VART | EXTS | PRIV; - continue; - } - } - if (next & EXTS) { - if (_isExtensionSingleton(pSubtag, subtagLen)) { - if (pExtension != NULL) { - if (pExtValueSubtag == NULL || pExtValueSubtagEnd == NULL) { - /* the previous extension is incomplete */ - uprv_free(pExtension); - pExtension = NULL; - break; - } - - /* terminate the previous extension value */ - *pExtValueSubtagEnd = 0; - pExtension->value = T_CString_toLowerCase(pExtValueSubtag); - - /* insert the extension to the list */ - if (_addExtensionToList(&(t->extensions), pExtension, false)) { - pLastGoodPosition = pExtValueSubtagEnd; - } else { - /* stop parsing here */ - uprv_free(pExtension); - pExtension = NULL; - break; - } - } - - /* create a new extension */ - pExtension = (ExtensionListEntry*)uprv_malloc(sizeof(ExtensionListEntry)); - if (pExtension == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - *pSep = 0; - pExtension->key = T_CString_toLowerCase(pSubtag); - pExtension->value = NULL; /* will be set later */ - - /* - * reset the start and the end location of extension value - * subtags for this extension - */ - pExtValueSubtag = NULL; - pExtValueSubtagEnd = NULL; - - next = EXTV; - continue; - } - } - if (next & EXTV) { - if (_isExtensionSubtag(pSubtag, subtagLen)) { - if (pExtValueSubtag == NULL) { - /* if the start position of this extension's value is not yet, - this one is the first value subtag */ - pExtValueSubtag = pSubtag; - } - - /* Mark the end of this subtag */ - pExtValueSubtagEnd = pSep; - next = EXTS | EXTV | PRIV; - - continue; - } - } - if (next & PRIV) { - if (uprv_tolower(*pSubtag) == PRIVATEUSE && subtagLen == 1) { - char *pPrivuseVal; - - if (pExtension != NULL) { - /* Process the last extension */ - if (pExtValueSubtag == NULL || pExtValueSubtagEnd == NULL) { - /* the previous extension is incomplete */ - uprv_free(pExtension); - pExtension = NULL; - break; - } else { - /* terminate the previous extension value */ - *pExtValueSubtagEnd = 0; - pExtension->value = T_CString_toLowerCase(pExtValueSubtag); - - /* insert the extension to the list */ - if (_addExtensionToList(&(t->extensions), pExtension, false)) { - pLastGoodPosition = pExtValueSubtagEnd; - pExtension = NULL; - } else { - /* stop parsing here */ - uprv_free(pExtension); - pExtension = NULL; - break; - } - } - } - - /* The rest of part will be private use value subtags */ - if (pNext == NULL) { - /* empty private use subtag */ - break; - } - /* back up the private use value start position */ - pPrivuseVal = pNext; - - /* validate private use value subtags */ - while (pNext) { - pSubtag = pNext; - pSep = pSubtag; - while (*pSep) { - if (*pSep == SEP) { - break; - } - pSep++; - } - if (*pSep == 0) { - /* last subtag */ - pNext = NULL; - } else { - pNext = pSep + 1; - } - subtagLen = (int32_t)(pSep - pSubtag); - - if (uprv_strncmp(pSubtag, PRIVUSE_VARIANT_PREFIX, uprv_strlen(PRIVUSE_VARIANT_PREFIX)) == 0) { - *pSep = 0; - next = VART; - privateuseVar = true; - break; - } else if (_isPrivateuseValueSubtag(pSubtag, subtagLen)) { - pLastGoodPosition = pSep; - } else { - break; - } - } - - if (next == VART) { - continue; - } - - if (pLastGoodPosition - pPrivuseVal > 0) { - *pLastGoodPosition = 0; - t->privateuse = T_CString_toLowerCase(pPrivuseVal); - } - /* No more subtags, exiting the parse loop */ - break; - } - break; - } - - /* If we fell through here, it means this subtag is illegal - quit parsing */ - break; - } - - if (pExtension != NULL) { - /* Process the last extension */ - if (pExtValueSubtag == NULL || pExtValueSubtagEnd == NULL) { - /* the previous extension is incomplete */ - uprv_free(pExtension); - } else { - /* terminate the previous extension value */ - *pExtValueSubtagEnd = 0; - pExtension->value = T_CString_toLowerCase(pExtValueSubtag); - /* insert the extension to the list */ - if (_addExtensionToList(&(t->extensions), pExtension, false)) { - pLastGoodPosition = pExtValueSubtagEnd; - } else { - uprv_free(pExtension); - } - } - } - - if (parsedLen != NULL) { - *parsedLen = (int32_t)(pLastGoodPosition - t->buf + parsedLenDelta); - } - - return t.orphan(); -} - -// Ticket #12705 - Turn optimization back on. -#if defined(_MSC_VER) && (_MSC_VER >= 1900) && (_MSC_VER < 1924) -#pragma optimize( "", on ) -#endif - -static void -ultag_close(ULanguageTag* langtag) { - - if (langtag == NULL) { - return; - } - - uprv_free(langtag->buf); - - if (langtag->variants) { - VariantListEntry *curVar = langtag->variants; - while (curVar) { - VariantListEntry *nextVar = curVar->next; - uprv_free(curVar); - curVar = nextVar; - } - } - - if (langtag->extensions) { - ExtensionListEntry *curExt = langtag->extensions; - while (curExt) { - ExtensionListEntry *nextExt = curExt->next; - uprv_free(curExt); - curExt = nextExt; - } - } - - uprv_free(langtag); -} - -static const char* -ultag_getLanguage(const ULanguageTag* langtag) { - return langtag->language; -} - -#if 0 -static const char* -ultag_getJDKLanguage(const ULanguageTag* langtag) { - int32_t i; - for (i = 0; DEPRECATEDLANGS[i] != NULL; i += 2) { - if (uprv_compareInvCharsAsAscii(DEPRECATEDLANGS[i], langtag->language) == 0) { - return DEPRECATEDLANGS[i + 1]; - } - } - return langtag->language; -} -#endif - -static const char* -ultag_getExtlang(const ULanguageTag* langtag, int32_t idx) { - if (idx >= 0 && idx < MAXEXTLANG) { - return langtag->extlang[idx]; - } - return NULL; -} - -static int32_t -ultag_getExtlangSize(const ULanguageTag* langtag) { - int32_t size = 0; - int32_t i; - for (i = 0; i < MAXEXTLANG; i++) { - if (langtag->extlang[i]) { - size++; - } - } - return size; -} - -static const char* -ultag_getScript(const ULanguageTag* langtag) { - return langtag->script; -} - -static const char* -ultag_getRegion(const ULanguageTag* langtag) { - return langtag->region; -} - -static const char* -ultag_getVariant(const ULanguageTag* langtag, int32_t idx) { - const char *var = NULL; - VariantListEntry *cur = langtag->variants; - int32_t i = 0; - while (cur) { - if (i == idx) { - var = cur->variant; - break; - } - cur = cur->next; - i++; - } - return var; -} - -static int32_t -ultag_getVariantsSize(const ULanguageTag* langtag) { - int32_t size = 0; - VariantListEntry *cur = langtag->variants; - while (true) { - if (cur == NULL) { - break; - } - size++; - cur = cur->next; - } - return size; -} - -static const char* -ultag_getExtensionKey(const ULanguageTag* langtag, int32_t idx) { - const char *key = NULL; - ExtensionListEntry *cur = langtag->extensions; - int32_t i = 0; - while (cur) { - if (i == idx) { - key = cur->key; - break; - } - cur = cur->next; - i++; - } - return key; -} - -static const char* -ultag_getExtensionValue(const ULanguageTag* langtag, int32_t idx) { - const char *val = NULL; - ExtensionListEntry *cur = langtag->extensions; - int32_t i = 0; - while (cur) { - if (i == idx) { - val = cur->value; - break; - } - cur = cur->next; - i++; - } - return val; -} - -static int32_t -ultag_getExtensionsSize(const ULanguageTag* langtag) { - int32_t size = 0; - ExtensionListEntry *cur = langtag->extensions; - while (true) { - if (cur == NULL) { - break; - } - size++; - cur = cur->next; - } - return size; -} - -static const char* -ultag_getPrivateUse(const ULanguageTag* langtag) { - return langtag->privateuse; -} - -#if 0 -static const char* -ultag_getLegacy(const ULanguageTag* langtag) { - return langtag->legacy; -} -#endif - - -/* -* ------------------------------------------------- -* -* Locale/BCP47 conversion APIs, exposed as uloc_* -* -* ------------------------------------------------- -*/ -U_CAPI int32_t U_EXPORT2 -uloc_toLanguageTag(const char* localeID, - char* langtag, - int32_t langtagCapacity, - UBool strict, - UErrorCode* status) { - if (U_FAILURE(*status)) { - return 0; - } - - icu::CheckedArrayByteSink sink(langtag, langtagCapacity); - ulocimp_toLanguageTag(localeID, sink, strict, status); - - int32_t reslen = sink.NumberOfBytesAppended(); - - if (U_FAILURE(*status)) { - return reslen; - } - - if (sink.Overflowed()) { - *status = U_BUFFER_OVERFLOW_ERROR; - } else { - u_terminateChars(langtag, langtagCapacity, reslen, status); - } - - return reslen; -} - - -U_CAPI void U_EXPORT2 -ulocimp_toLanguageTag(const char* localeID, - icu::ByteSink& sink, - UBool strict, - UErrorCode* status) { - icu::CharString canonical; - int32_t reslen; - UErrorCode tmpStatus = U_ZERO_ERROR; - UBool hadPosix = false; - const char* pKeywordStart; - - /* Note: uloc_canonicalize returns "en_US_POSIX" for input locale ID "". See #6835 */ - int32_t resultCapacity = static_cast(uprv_strlen(localeID)); - if (resultCapacity > 0) { - char* buffer; - - for (;;) { - buffer = canonical.getAppendBuffer( - /*minCapacity=*/resultCapacity, - /*desiredCapacityHint=*/resultCapacity, - resultCapacity, - tmpStatus); - - if (U_FAILURE(tmpStatus)) { - *status = tmpStatus; - return; - } - - reslen = - uloc_canonicalize(localeID, buffer, resultCapacity, &tmpStatus); - - if (tmpStatus != U_BUFFER_OVERFLOW_ERROR) { - break; - } - - resultCapacity = reslen; - tmpStatus = U_ZERO_ERROR; - } - - if (U_FAILURE(tmpStatus)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - canonical.append(buffer, reslen, tmpStatus); - if (tmpStatus == U_STRING_NOT_TERMINATED_WARNING) { - tmpStatus = U_ZERO_ERROR; // Terminators provided by CharString. - } - - if (U_FAILURE(tmpStatus)) { - *status = tmpStatus; - return; - } - } - - /* For handling special case - private use only tag */ - pKeywordStart = locale_getKeywordsStart(canonical.data()); - if (pKeywordStart == canonical.data()) { - int kwdCnt = 0; - UBool done = false; - - icu::LocalUEnumerationPointer kwdEnum(uloc_openKeywords(canonical.data(), &tmpStatus)); - if (U_SUCCESS(tmpStatus)) { - kwdCnt = uenum_count(kwdEnum.getAlias(), &tmpStatus); - if (kwdCnt == 1) { - const char *key; - int32_t len = 0; - - key = uenum_next(kwdEnum.getAlias(), &len, &tmpStatus); - if (len == 1 && *key == PRIVATEUSE) { - icu::CharString buf; - { - icu::CharStringByteSink sink(&buf); - ulocimp_getKeywordValue(localeID, key, sink, &tmpStatus); - } - if (U_SUCCESS(tmpStatus)) { - if (ultag_isPrivateuseValueSubtags(buf.data(), buf.length())) { - /* return private use only tag */ - sink.Append("und-x-", 6); - sink.Append(buf.data(), buf.length()); - done = true; - } else if (strict) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - done = true; - } - /* if not strict mode, then "und" will be returned */ - } else { - *status = U_ILLEGAL_ARGUMENT_ERROR; - done = true; - } - } - } - if (done) { - return; - } - } - } - - _appendLanguageToLanguageTag(canonical.data(), sink, strict, status); - _appendScriptToLanguageTag(canonical.data(), sink, strict, status); - _appendRegionToLanguageTag(canonical.data(), sink, strict, status); - _appendVariantsToLanguageTag(canonical.data(), sink, strict, &hadPosix, status); - _appendKeywordsToLanguageTag(canonical.data(), sink, strict, hadPosix, status); - _appendPrivateuseToLanguageTag(canonical.data(), sink, strict, hadPosix, status); -} - - -U_CAPI int32_t U_EXPORT2 -uloc_forLanguageTag(const char* langtag, - char* localeID, - int32_t localeIDCapacity, - int32_t* parsedLength, - UErrorCode* status) { - if (U_FAILURE(*status)) { - return 0; - } - - icu::CheckedArrayByteSink sink(localeID, localeIDCapacity); - ulocimp_forLanguageTag(langtag, -1, sink, parsedLength, status); - - int32_t reslen = sink.NumberOfBytesAppended(); - - if (U_FAILURE(*status)) { - return reslen; - } - - if (sink.Overflowed()) { - *status = U_BUFFER_OVERFLOW_ERROR; - } else { - u_terminateChars(localeID, localeIDCapacity, reslen, status); - } - - return reslen; -} - - -U_CAPI void U_EXPORT2 -ulocimp_forLanguageTag(const char* langtag, - int32_t tagLen, - icu::ByteSink& sink, - int32_t* parsedLength, - UErrorCode* status) { - UBool isEmpty = true; - const char *subtag, *p; - int32_t len; - int32_t i, n; - UBool noRegion = true; - - icu::LocalULanguageTagPointer lt(ultag_parse(langtag, tagLen, parsedLength, status)); - if (U_FAILURE(*status)) { - return; - } - - /* language */ - subtag = ultag_getExtlangSize(lt.getAlias()) > 0 ? ultag_getExtlang(lt.getAlias(), 0) : ultag_getLanguage(lt.getAlias()); - if (uprv_compareInvCharsAsAscii(subtag, LANG_UND) != 0) { - len = (int32_t)uprv_strlen(subtag); - if (len > 0) { - sink.Append(subtag, len); - isEmpty = false; - } - } - - /* script */ - subtag = ultag_getScript(lt.getAlias()); - len = (int32_t)uprv_strlen(subtag); - if (len > 0) { - sink.Append("_", 1); - isEmpty = false; - - /* write out the script in title case */ - char c = uprv_toupper(*subtag); - sink.Append(&c, 1); - sink.Append(subtag + 1, len - 1); - } - - /* region */ - subtag = ultag_getRegion(lt.getAlias()); - len = (int32_t)uprv_strlen(subtag); - if (len > 0) { - sink.Append("_", 1); - isEmpty = false; - - /* write out the region in upper case */ - p = subtag; - while (*p) { - char c = uprv_toupper(*p); - sink.Append(&c, 1); - p++; - } - noRegion = false; - } - - /* variants */ - _sortVariants(lt.getAlias()->variants); - n = ultag_getVariantsSize(lt.getAlias()); - if (n > 0) { - if (noRegion) { - sink.Append("_", 1); - isEmpty = false; - } - - for (i = 0; i < n; i++) { - subtag = ultag_getVariant(lt.getAlias(), i); - sink.Append("_", 1); - - /* write out the variant in upper case */ - p = subtag; - while (*p) { - char c = uprv_toupper(*p); - sink.Append(&c, 1); - p++; - } - } - } - - /* keywords */ - n = ultag_getExtensionsSize(lt.getAlias()); - subtag = ultag_getPrivateUse(lt.getAlias()); - if (n > 0 || uprv_strlen(subtag) > 0) { - if (isEmpty && n > 0) { - /* need a language */ - sink.Append(LANG_UND, LANG_UND_LEN); - } - _appendKeywords(lt.getAlias(), sink, status); - } -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2009-2015, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#include "unicode/bytestream.h" +#include "unicode/utypes.h" +#include "unicode/ures.h" +#include "unicode/localpointer.h" +#include "unicode/putil.h" +#include "unicode/uenum.h" +#include "unicode/uloc.h" +#include "ustr_imp.h" +#include "bytesinkutil.h" +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "putilimp.h" +#include "uinvchar.h" +#include "ulocimp.h" +#include "uassert.h" + + +/* struct holding a single variant */ +typedef struct VariantListEntry { + const char *variant; + struct VariantListEntry *next; +} VariantListEntry; + +/* struct holding a single attribute value */ +struct AttributeListEntry : public icu::UMemory { + const char *attribute; + struct AttributeListEntry *next; +}; + +/* struct holding a single extension */ +struct ExtensionListEntry : public icu::UMemory { + const char *key; + const char *value; + struct ExtensionListEntry *next; +}; + +#define MAXEXTLANG 3 +typedef struct ULanguageTag { + char *buf; /* holding parsed subtags */ + const char *language; + const char *extlang[MAXEXTLANG]; + const char *script; + const char *region; + VariantListEntry *variants; + ExtensionListEntry *extensions; + const char *privateuse; + const char *legacy; +} ULanguageTag; + +#define MINLEN 2 +#define SEP '-' +#define PRIVATEUSE 'x' +#define LDMLEXT 'u' + +#define LOCALE_SEP '_' +#define LOCALE_EXT_SEP '@' +#define LOCALE_KEYWORD_SEP ';' +#define LOCALE_KEY_TYPE_SEP '=' + +#define ISALPHA(c) uprv_isASCIILetter(c) +#define ISNUMERIC(c) ((c)>='0' && (c)<='9') + +static const char EMPTY[] = ""; +static const char LANG_UND[] = "und"; +static const char PRIVATEUSE_KEY[] = "x"; +static const char _POSIX[] = "_POSIX"; +static const char POSIX_KEY[] = "va"; +static const char POSIX_VALUE[] = "posix"; +static const char LOCALE_ATTRIBUTE_KEY[] = "attribute"; +static const char PRIVUSE_VARIANT_PREFIX[] = "lvariant"; +static const char LOCALE_TYPE_YES[] = "yes"; + +#define LANG_UND_LEN 3 + +/* + Updated on 2018-09-12 from + https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry . + + This table has 2 parts. The part for + legacy language tags (marked as “Type: grandfathered” in BCP 47) + is generated by the following scripts from the IANA language tag registry. + + curl https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry |\ + egrep -A 7 'Type: grandfathered' | \ + egrep 'Tag|Prefe' | grep -B1 'Preferred' | grep -v '^--' | \ + awk -n '/Tag/ {printf(" \"%s\", ", $2);} /Preferred/ {printf("\"%s\",\n", $2);}' |\ + tr 'A-Z' 'a-z' + + + The 2nd part is made of five ICU-specific entries. They're kept for + the backward compatibility for now, even though there are no preferred + values. They may have to be removed for the strict BCP 47 compliance. + +*/ +static const char* const LEGACY[] = { +/* legacy preferred */ + "art-lojban", "jbo", + "en-gb-oed", "en-gb-oxendict", + "i-ami", "ami", + "i-bnn", "bnn", + "i-hak", "hak", + "i-klingon", "tlh", + "i-lux", "lb", + "i-navajo", "nv", + "i-pwn", "pwn", + "i-tao", "tao", + "i-tay", "tay", + "i-tsu", "tsu", + "no-bok", "nb", + "no-nyn", "nn", + "sgn-be-fr", "sfb", + "sgn-be-nl", "vgt", + "sgn-ch-de", "sgg", + "zh-guoyu", "cmn", + "zh-hakka", "hak", + "zh-min-nan", "nan", + "zh-xiang", "hsn", + + // Legacy tags with no preferred value in the IANA + // registry. Kept for now for the backward compatibility + // because ICU has mapped them this way. + "i-default", "en-x-i-default", + "i-enochian", "und-x-i-enochian", + "i-mingo", "see-x-i-mingo", + "zh-min", "nan-x-zh-min", +}; + +/* + Updated on 2018-09-12 from + https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry . + + The table lists redundant tags with preferred value in the IANA language tag registry. + It's generated with the following command: + + curl https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry |\ + grep 'Type: redundant' -A 5 | egrep '^(Tag:|Prefer)' | grep -B1 'Preferred' | \ + awk -n '/Tag/ {printf(" \"%s\", ", $2);} /Preferred/ {printf("\"%s\",\n", $2);}' | \ + tr 'A-Z' 'a-z' + + In addition, ja-latn-hepburn-heploc is mapped to ja-latn-alalc97 because + a variant tag 'hepburn-heploc' has the preferred subtag, 'alaic97'. +*/ + +static const char* const REDUNDANT[] = { +// redundant preferred + "sgn-br", "bzs", + "sgn-co", "csn", + "sgn-de", "gsg", + "sgn-dk", "dsl", + "sgn-es", "ssp", + "sgn-fr", "fsl", + "sgn-gb", "bfi", + "sgn-gr", "gss", + "sgn-ie", "isg", + "sgn-it", "ise", + "sgn-jp", "jsl", + "sgn-mx", "mfs", + "sgn-ni", "ncs", + "sgn-nl", "dse", + "sgn-no", "nsl", + "sgn-pt", "psr", + "sgn-se", "swl", + "sgn-us", "ase", + "sgn-za", "sfs", + "zh-cmn", "cmn", + "zh-cmn-hans", "cmn-hans", + "zh-cmn-hant", "cmn-hant", + "zh-gan", "gan", + "zh-wuu", "wuu", + "zh-yue", "yue", + + // variant tag with preferred value + "ja-latn-hepburn-heploc", "ja-latn-alalc97", +}; + +/* + Updated on 2018-09-12 from + https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry . + + grep 'Type: language' -A 7 language-subtag-registry | egrep 'Subtag|Prefe' | \ + grep -B1 'Preferred' | grep -v '^--' | \ + awk -n '/Subtag/ {printf(" \"%s\", ", $2);} /Preferred/ {printf("\"%s\",\n", $2);}' + + Make sure that 2-letter language subtags come before 3-letter subtags. +*/ +static const char DEPRECATEDLANGS[][4] = { +/* deprecated new */ + "in", "id", + "iw", "he", + "ji", "yi", + "jw", "jv", + "mo", "ro", + "aam", "aas", + "adp", "dz", + "aue", "ktz", + "ayx", "nun", + "bgm", "bcg", + "bjd", "drl", + "ccq", "rki", + "cjr", "mom", + "cka", "cmr", + "cmk", "xch", + "coy", "pij", + "cqu", "quh", + "drh", "khk", + "drw", "prs", + "gav", "dev", + "gfx", "vaj", + "ggn", "gvr", + "gti", "nyc", + "guv", "duz", + "hrr", "jal", + "ibi", "opa", + "ilw", "gal", + "jeg", "oyb", + "kgc", "tdf", + "kgh", "kml", + "koj", "kwv", + "krm", "bmf", + "ktr", "dtp", + "kvs", "gdj", + "kwq", "yam", + "kxe", "tvd", + "kzj", "dtp", + "kzt", "dtp", + "lii", "raq", + "lmm", "rmx", + "meg", "cir", + "mst", "mry", + "mwj", "vaj", + "myt", "mry", + "nad", "xny", + "ncp", "kdz", + "nnx", "ngv", + "nts", "pij", + "oun", "vaj", + "pcr", "adx", + "pmc", "huw", + "pmu", "phr", + "ppa", "bfy", + "ppr", "lcq", + "pry", "prt", + "puz", "pub", + "sca", "hle", + "skk", "oyb", + "tdu", "dtp", + "thc", "tpo", + "thx", "oyb", + "tie", "ras", + "tkk", "twm", + "tlw", "weo", + "tmp", "tyj", + "tne", "kak", + "tnf", "prs", + "tsf", "taj", + "uok", "ema", + "xba", "cax", + "xia", "acn", + "xkh", "waw", + "xsj", "suj", + "ybd", "rki", + "yma", "lrr", + "ymt", "mtm", + "yos", "zom", + "yuu", "yug", +}; + +/* + Updated on 2018-04-24 from + + curl https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry | \ + grep 'Type: region' -A 7 | egrep 'Subtag|Prefe' | \ + grep -B1 'Preferred' | \ + awk -n '/Subtag/ {printf(" \"%s\", ", $2);} /Preferred/ {printf("\"%s\",\n", $2);}' +*/ +static const char DEPRECATEDREGIONS[][3] = { +/* deprecated new */ + "BU", "MM", + "DD", "DE", + "FX", "FR", + "TP", "TL", + "YD", "YE", + "ZR", "CD", +}; + +/* +* ------------------------------------------------- +* +* These ultag_ functions may be exposed as APIs later +* +* ------------------------------------------------- +*/ + +static ULanguageTag* +ultag_parse(const char* tag, int32_t tagLen, int32_t* parsedLen, UErrorCode* status); + +static void +ultag_close(ULanguageTag* langtag); + +static const char* +ultag_getLanguage(const ULanguageTag* langtag); + +#if 0 +static const char* +ultag_getJDKLanguage(const ULanguageTag* langtag); +#endif + +static const char* +ultag_getExtlang(const ULanguageTag* langtag, int32_t idx); + +static int32_t +ultag_getExtlangSize(const ULanguageTag* langtag); + +static const char* +ultag_getScript(const ULanguageTag* langtag); + +static const char* +ultag_getRegion(const ULanguageTag* langtag); + +static const char* +ultag_getVariant(const ULanguageTag* langtag, int32_t idx); + +static int32_t +ultag_getVariantsSize(const ULanguageTag* langtag); + +static const char* +ultag_getExtensionKey(const ULanguageTag* langtag, int32_t idx); + +static const char* +ultag_getExtensionValue(const ULanguageTag* langtag, int32_t idx); + +static int32_t +ultag_getExtensionsSize(const ULanguageTag* langtag); + +static const char* +ultag_getPrivateUse(const ULanguageTag* langtag); + +#if 0 +static const char* +ultag_getLegacy(const ULanguageTag* langtag); +#endif + +U_NAMESPACE_BEGIN + +/** + * \class LocalULanguageTagPointer + * "Smart pointer" class, closes a ULanguageTag via ultag_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @internal + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalULanguageTagPointer, ULanguageTag, ultag_close); + +U_NAMESPACE_END + +/* +* ------------------------------------------------- +* +* Language subtag syntax validation functions +* +* ------------------------------------------------- +*/ + +static UBool +_isAlphaString(const char* s, int32_t len) { + int32_t i; + for (i = 0; i < len; i++) { + if (!ISALPHA(*(s + i))) { + return false; + } + } + return true; +} + +static UBool +_isNumericString(const char* s, int32_t len) { + int32_t i; + for (i = 0; i < len; i++) { + if (!ISNUMERIC(*(s + i))) { + return false; + } + } + return true; +} + +static UBool +_isAlphaNumericString(const char* s, int32_t len) { + int32_t i; + for (i = 0; i < len; i++) { + if (!ISALPHA(*(s + i)) && !ISNUMERIC(*(s + i))) { + return false; + } + } + return true; +} + +static UBool +_isAlphaNumericStringLimitedLength(const char* s, int32_t len, int32_t min, int32_t max) { + if (len < 0) { + len = (int32_t)uprv_strlen(s); + } + if (len >= min && len <= max && _isAlphaNumericString(s, len)) { + return true; + } + return false; +} + +U_CFUNC UBool +ultag_isLanguageSubtag(const char* s, int32_t len) { + /* + * unicode_language_subtag = alpha{2,3} | alpha{5,8}; + * NOTE: Per ICUTC 2019/01/23- accepting alpha 4 + * See ICU-20372 + */ + if (len < 0) { + len = (int32_t)uprv_strlen(s); + } + if (len >= 2 && len <= 8 && _isAlphaString(s, len)) { + return true; + } + return false; +} + +static UBool +_isExtlangSubtag(const char* s, int32_t len) { + /* + * extlang = 3ALPHA ; selected ISO 639 codes + * *2("-" 3ALPHA) ; permanently reserved + */ + if (len < 0) { + len = (int32_t)uprv_strlen(s); + } + if (len == 3 && _isAlphaString(s, len)) { + return true; + } + return false; +} + +U_CFUNC UBool +ultag_isScriptSubtag(const char* s, int32_t len) { + /* + * script = 4ALPHA ; ISO 15924 code + */ + if (len < 0) { + len = (int32_t)uprv_strlen(s); + } + if (len == 4 && _isAlphaString(s, len)) { + return true; + } + return false; +} + +U_CFUNC UBool +ultag_isRegionSubtag(const char* s, int32_t len) { + /* + * region = 2ALPHA ; ISO 3166-1 code + * / 3DIGIT ; UN M.49 code + */ + if (len < 0) { + len = (int32_t)uprv_strlen(s); + } + if (len == 2 && _isAlphaString(s, len)) { + return true; + } + if (len == 3 && _isNumericString(s, len)) { + return true; + } + return false; +} + +static UBool +_isVariantSubtag(const char* s, int32_t len) { + /* + * variant = 5*8alphanum ; registered variants + * / (DIGIT 3alphanum) + */ + if (len < 0) { + len = (int32_t)uprv_strlen(s); + } + if (_isAlphaNumericStringLimitedLength(s, len, 5, 8)) { + return true; + } + if (len == 4 && ISNUMERIC(*s) && _isAlphaNumericString(s + 1, 3)) { + return true; + } + return false; +} + +static UBool +_isSepListOf(UBool (*test)(const char*, int32_t), const char* s, int32_t len) { + const char *p = s; + const char *pSubtag = nullptr; + + if (len < 0) { + len = (int32_t)uprv_strlen(s); + } + + while ((p - s) < len) { + if (*p == SEP) { + if (pSubtag == nullptr) { + return false; + } + if (!test(pSubtag, (int32_t)(p - pSubtag))) { + return false; + } + pSubtag = nullptr; + } else if (pSubtag == nullptr) { + pSubtag = p; + } + p++; + } + if (pSubtag == nullptr) { + return false; + } + return test(pSubtag, (int32_t)(p - pSubtag)); +} + +U_CFUNC UBool +ultag_isVariantSubtags(const char* s, int32_t len) { + return _isSepListOf(&_isVariantSubtag, s, len); +} + +// This is for the ICU-specific "lvariant" handling. +static UBool +_isPrivateuseVariantSubtag(const char* s, int32_t len) { + /* + * variant = 1*8alphanum ; registered variants + * / (DIGIT 3alphanum) + */ + return _isAlphaNumericStringLimitedLength(s, len , 1, 8); +} + +static UBool +_isExtensionSingleton(const char* s, int32_t len) { + /* + * extension = singleton 1*("-" (2*8alphanum)) + * + * singleton = DIGIT ; 0 - 9 + * / %x41-57 ; A - W + * / %x59-5A ; Y - Z + * / %x61-77 ; a - w + * / %x79-7A ; y - z + */ + if (len < 0) { + len = (int32_t)uprv_strlen(s); + } + if (len == 1 && (ISALPHA(*s) || ISNUMERIC(*s)) && (uprv_tolower(*s) != PRIVATEUSE)) { + return true; + } + return false; +} + +static UBool +_isExtensionSubtag(const char* s, int32_t len) { + /* + * extension = singleton 1*("-" (2*8alphanum)) + */ + return _isAlphaNumericStringLimitedLength(s, len, 2, 8); +} + +U_CFUNC UBool +ultag_isExtensionSubtags(const char* s, int32_t len) { + return _isSepListOf(&_isExtensionSubtag, s, len); +} + +static UBool +_isPrivateuseValueSubtag(const char* s, int32_t len) { + /* + * privateuse = "x" 1*("-" (1*8alphanum)) + */ + return _isAlphaNumericStringLimitedLength(s, len, 1, 8); +} + +U_CFUNC UBool +ultag_isPrivateuseValueSubtags(const char* s, int32_t len) { + return _isSepListOf(&_isPrivateuseValueSubtag, s, len); +} + +U_CFUNC UBool +ultag_isUnicodeLocaleAttribute(const char* s, int32_t len) { + /* + * attribute = alphanum{3,8} ; + */ + return _isAlphaNumericStringLimitedLength(s, len , 3, 8); +} + +U_CFUNC UBool +ultag_isUnicodeLocaleAttributes(const char* s, int32_t len) { + return _isSepListOf(&ultag_isUnicodeLocaleAttribute, s, len); +} + +U_CFUNC UBool +ultag_isUnicodeLocaleKey(const char* s, int32_t len) { + /* + * key = alphanum alpha ; + */ + if (len < 0) { + len = (int32_t)uprv_strlen(s); + } + if (len == 2 && (ISALPHA(*s) || ISNUMERIC(*s)) && ISALPHA(s[1])) { + return true; + } + return false; +} + +U_CFUNC UBool +_isUnicodeLocaleTypeSubtag(const char*s, int32_t len) { + /* + * alphanum{3,8} + */ + return _isAlphaNumericStringLimitedLength(s, len , 3, 8); +} + +U_CFUNC UBool +ultag_isUnicodeLocaleType(const char*s, int32_t len) { + /* + * type = alphanum{3,8} (sep alphanum{3,8})* ; + */ + return _isSepListOf(&_isUnicodeLocaleTypeSubtag, s, len); +} + +static UBool +_isTKey(const char* s, int32_t len) +{ + /* + * tkey = alpha digit ; + */ + if (len < 0) { + len = (int32_t)uprv_strlen(s); + } + if (len == 2 && ISALPHA(*s) && ISNUMERIC(*(s + 1))) { + return true; + } + return false; +} + +U_CAPI const char * U_EXPORT2 +ultag_getTKeyStart(const char *localeID) { + const char *result = localeID; + const char *sep; + while((sep = uprv_strchr(result, SEP)) != nullptr) { + if (_isTKey(result, static_cast(sep - result))) { + return result; + } + result = ++sep; + } + if (_isTKey(result, -1)) { + return result; + } + return nullptr; +} + +static UBool +_isTValue(const char* s, int32_t len) +{ + /* + * tvalue = (sep alphanum{3,8})+ ; + */ + return _isAlphaNumericStringLimitedLength(s, len , 3, 8); +} + +static UBool +_isTransformedExtensionSubtag(int32_t& state, const char* s, int32_t len) +{ + const int32_t kStart = 0; // Start, wait for unicode_language_subtag, tkey or end + const int32_t kGotLanguage = 1; // Got unicode_language_subtag, wait for unicode_script_subtag, + // unicode_region_subtag, unicode_variant_subtag, tkey or end + const int32_t kGotScript = 2; // Got unicode_script_subtag, wait for unicode_region_subtag, + // unicode_variant_subtag, tkey, or end + const int32_t kGotRegion = 3; // Got unicode_region_subtag, wait for unicode_variant_subtag, + // tkey, or end. + const int32_t kGotVariant = 4; // Got unicode_variant_subtag, wait for unicode_variant_subtag + // tkey or end. + const int32_t kGotTKey = -1; // Got tkey, wait for tvalue. ERROR if stop here. + const int32_t kGotTValue = 6; // Got tvalue, wait for tkey, tvalue or end + + + if (len < 0) { + len = (int32_t)uprv_strlen(s); + } + switch (state) { + case kStart: + if (ultag_isLanguageSubtag(s, len) && len != 4) { + state = kGotLanguage; + return true; + } + if (_isTKey(s, len)) { + state = kGotTKey; + return true; + } + return false; + case kGotLanguage: + if (ultag_isScriptSubtag(s, len)) { + state = kGotScript; + return true; + } + U_FALLTHROUGH; + case kGotScript: + if (ultag_isRegionSubtag(s, len)) { + state = kGotRegion; + return true; + } + U_FALLTHROUGH; + case kGotRegion: + U_FALLTHROUGH; + case kGotVariant: + if (_isVariantSubtag(s, len)) { + state = kGotVariant; + return true; + } + if (_isTKey(s, len)) { + state = kGotTKey; + return true; + } + return false; + case kGotTKey: + if (_isTValue(s, len)) { + state = kGotTValue; + return true; + } + return false; + case kGotTValue: + if (_isTKey(s, len)) { + state = kGotTKey; + return true; + } + if (_isTValue(s, len)) { + return true; + } + return false; + } + return false; +} + +static UBool +_isUnicodeExtensionSubtag(int32_t& state, const char* s, int32_t len) +{ + const int32_t kStart = 0; // Start, wait for a key or attribute or end + const int32_t kGotKey = 1; // Got a key, wait for type or key or end + const int32_t kGotType = 2; // Got a type, wait for key or end + + switch (state) { + case kStart: + if (ultag_isUnicodeLocaleKey(s, len)) { + state = kGotKey; + return true; + } + if (ultag_isUnicodeLocaleAttribute(s, len)) { + return true; + } + return false; + case kGotKey: + if (ultag_isUnicodeLocaleKey(s, len)) { + return true; + } + if (_isUnicodeLocaleTypeSubtag(s, len)) { + state = kGotType; + return true; + } + return false; + case kGotType: + if (ultag_isUnicodeLocaleKey(s, len)) { + state = kGotKey; + return true; + } + if (_isUnicodeLocaleTypeSubtag(s, len)) { + return true; + } + return false; + } + return false; +} + +static UBool +_isStatefulSepListOf(UBool (*test)(int32_t&, const char*, int32_t), const char* s, int32_t len) +{ + int32_t state = 0; + const char* p; + const char* start = s; + int32_t subtagLen = 0; + + if (len < 0) { + len = (int32_t)uprv_strlen(s); + } + + for (p = s; len > 0; p++, len--) { + if (*p == SEP) { + if (!test(state, start, subtagLen)) { + return false; + } + subtagLen = 0; + start = p + 1; + } else { + subtagLen++; + } + } + + if (test(state, start, subtagLen) && state >= 0) { + return true; + } + return false; +} + +U_CFUNC UBool +ultag_isTransformedExtensionSubtags(const char* s, int32_t len) +{ + return _isStatefulSepListOf(&_isTransformedExtensionSubtag, s, len); +} + +U_CFUNC UBool +ultag_isUnicodeExtensionSubtags(const char* s, int32_t len) { + return _isStatefulSepListOf(&_isUnicodeExtensionSubtag, s, len); +} + + +/* +* ------------------------------------------------- +* +* Helper functions +* +* ------------------------------------------------- +*/ + +static UBool +_addVariantToList(VariantListEntry **first, VariantListEntry *var) { + UBool bAdded = true; + + if (*first == nullptr) { + var->next = nullptr; + *first = var; + } else { + VariantListEntry *prev, *cur; + int32_t cmp; + + /* variants order should be preserved */ + prev = nullptr; + cur = *first; + while (true) { + if (cur == nullptr) { + prev->next = var; + var->next = nullptr; + break; + } + + /* Checking for duplicate variant */ + cmp = uprv_compareInvCharsAsAscii(var->variant, cur->variant); + if (cmp == 0) { + /* duplicated variant */ + bAdded = false; + break; + } + prev = cur; + cur = cur->next; + } + } + + return bAdded; +} + +static UBool +_addAttributeToList(AttributeListEntry **first, AttributeListEntry *attr) { + UBool bAdded = true; + + if (*first == nullptr) { + attr->next = nullptr; + *first = attr; + } else { + AttributeListEntry *prev, *cur; + int32_t cmp; + + /* reorder variants in alphabetical order */ + prev = nullptr; + cur = *first; + while (true) { + if (cur == nullptr) { + prev->next = attr; + attr->next = nullptr; + break; + } + cmp = uprv_compareInvCharsAsAscii(attr->attribute, cur->attribute); + if (cmp < 0) { + if (prev == nullptr) { + *first = attr; + } else { + prev->next = attr; + } + attr->next = cur; + break; + } + if (cmp == 0) { + /* duplicated variant */ + bAdded = false; + break; + } + prev = cur; + cur = cur->next; + } + } + + return bAdded; +} + + +static UBool +_addExtensionToList(ExtensionListEntry **first, ExtensionListEntry *ext, UBool localeToBCP) { + UBool bAdded = true; + + if (*first == nullptr) { + ext->next = nullptr; + *first = ext; + } else { + ExtensionListEntry *prev, *cur; + int32_t cmp; + + /* reorder variants in alphabetical order */ + prev = nullptr; + cur = *first; + while (true) { + if (cur == nullptr) { + prev->next = ext; + ext->next = nullptr; + break; + } + if (localeToBCP) { + /* special handling for locale to bcp conversion */ + int32_t len, curlen; + + len = (int32_t)uprv_strlen(ext->key); + curlen = (int32_t)uprv_strlen(cur->key); + + if (len == 1 && curlen == 1) { + if (*(ext->key) == *(cur->key)) { + cmp = 0; + } else if (*(ext->key) == PRIVATEUSE) { + cmp = 1; + } else if (*(cur->key) == PRIVATEUSE) { + cmp = -1; + } else { + cmp = *(ext->key) - *(cur->key); + } + } else if (len == 1) { + cmp = *(ext->key) - LDMLEXT; + } else if (curlen == 1) { + cmp = LDMLEXT - *(cur->key); + } else { + cmp = uprv_compareInvCharsAsAscii(ext->key, cur->key); + /* Both are u extension keys - we need special handling for 'attribute' */ + if (cmp != 0) { + if (uprv_strcmp(cur->key, LOCALE_ATTRIBUTE_KEY) == 0) { + cmp = 1; + } else if (uprv_strcmp(ext->key, LOCALE_ATTRIBUTE_KEY) == 0) { + cmp = -1; + } + } + } + } else { + cmp = uprv_compareInvCharsAsAscii(ext->key, cur->key); + } + if (cmp < 0) { + if (prev == nullptr) { + *first = ext; + } else { + prev->next = ext; + } + ext->next = cur; + break; + } + if (cmp == 0) { + /* duplicated extension key */ + bAdded = false; + break; + } + prev = cur; + cur = cur->next; + } + } + + return bAdded; +} + +static void +_initializeULanguageTag(ULanguageTag* langtag) { + int32_t i; + + langtag->buf = nullptr; + + langtag->language = EMPTY; + for (i = 0; i < MAXEXTLANG; i++) { + langtag->extlang[i] = nullptr; + } + + langtag->script = EMPTY; + langtag->region = EMPTY; + + langtag->variants = nullptr; + langtag->extensions = nullptr; + + langtag->legacy = EMPTY; + langtag->privateuse = EMPTY; +} + +static void +_appendLanguageToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool strict, UErrorCode* status) { + char buf[ULOC_LANG_CAPACITY]; + UErrorCode tmpStatus = U_ZERO_ERROR; + int32_t len, i; + + if (U_FAILURE(*status)) { + return; + } + + len = uloc_getLanguage(localeID, buf, sizeof(buf), &tmpStatus); + if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) { + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + len = 0; + } + + /* Note: returned language code is in lower case letters */ + + if (len == 0) { + sink.Append(LANG_UND, LANG_UND_LEN); + } else if (!ultag_isLanguageSubtag(buf, len)) { + /* invalid language code */ + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + sink.Append(LANG_UND, LANG_UND_LEN); + } else { + /* resolve deprecated */ + for (i = 0; i < UPRV_LENGTHOF(DEPRECATEDLANGS); i += 2) { + // 2-letter deprecated subtags are listede before 3-letter + // ones in DEPRECATEDLANGS[]. Get out of loop on coming + // across the 1st 3-letter subtag, if the input is a 2-letter code. + // to avoid continuing to try when there's no match. + if (uprv_strlen(buf) < uprv_strlen(DEPRECATEDLANGS[i])) break; + if (uprv_compareInvCharsAsAscii(buf, DEPRECATEDLANGS[i]) == 0) { + uprv_strcpy(buf, DEPRECATEDLANGS[i + 1]); + len = (int32_t)uprv_strlen(buf); + break; + } + } + sink.Append(buf, len); + } +} + +static void +_appendScriptToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool strict, UErrorCode* status) { + char buf[ULOC_SCRIPT_CAPACITY]; + UErrorCode tmpStatus = U_ZERO_ERROR; + int32_t len; + + if (U_FAILURE(*status)) { + return; + } + + len = uloc_getScript(localeID, buf, sizeof(buf), &tmpStatus); + if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) { + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + } + return; + } + + if (len > 0) { + if (!ultag_isScriptSubtag(buf, len)) { + /* invalid script code */ + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + } + return; + } else { + sink.Append("-", 1); + sink.Append(buf, len); + } + } +} + +static void +_appendRegionToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool strict, UErrorCode* status) { + char buf[ULOC_COUNTRY_CAPACITY]; + UErrorCode tmpStatus = U_ZERO_ERROR; + int32_t len; + + if (U_FAILURE(*status)) { + return; + } + + len = uloc_getCountry(localeID, buf, sizeof(buf), &tmpStatus); + if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) { + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + } + return; + } + + if (len > 0) { + if (!ultag_isRegionSubtag(buf, len)) { + /* invalid region code */ + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + } + return; + } else { + sink.Append("-", 1); + /* resolve deprecated */ + for (int i = 0; i < UPRV_LENGTHOF(DEPRECATEDREGIONS); i += 2) { + if (uprv_compareInvCharsAsAscii(buf, DEPRECATEDREGIONS[i]) == 0) { + uprv_strcpy(buf, DEPRECATEDREGIONS[i + 1]); + len = (int32_t)uprv_strlen(buf); + break; + } + } + sink.Append(buf, len); + } + } +} + +static void _sortVariants(VariantListEntry* first) { + for (VariantListEntry* var1 = first; var1 != nullptr; var1 = var1->next) { + for (VariantListEntry* var2 = var1->next; var2 != nullptr; var2 = var2->next) { + // Swap var1->variant and var2->variant. + if (uprv_compareInvCharsAsAscii(var1->variant, var2->variant) > 0) { + const char* temp = var1->variant; + var1->variant = var2->variant; + var2->variant = temp; + } + } + } +} + +static void +_appendVariantsToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool strict, UBool *hadPosix, UErrorCode* status) { + char buf[ULOC_FULLNAME_CAPACITY]; + UErrorCode tmpStatus = U_ZERO_ERROR; + int32_t len, i; + + if (U_FAILURE(*status)) { + return; + } + + len = uloc_getVariant(localeID, buf, sizeof(buf), &tmpStatus); + if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) { + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + } + return; + } + + if (len > 0) { + char *p, *pVar; + UBool bNext = true; + VariantListEntry *var; + VariantListEntry *varFirst = nullptr; + + pVar = nullptr; + p = buf; + while (bNext) { + if (*p == SEP || *p == LOCALE_SEP || *p == 0) { + if (*p == 0) { + bNext = false; + } else { + *p = 0; /* terminate */ + } + if (pVar == nullptr) { + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + /* ignore empty variant */ + } else { + /* ICU uses upper case letters for variants, but + the canonical format is lowercase in BCP47 */ + for (i = 0; *(pVar + i) != 0; i++) { + *(pVar + i) = uprv_tolower(*(pVar + i)); + } + + /* validate */ + if (_isVariantSubtag(pVar, -1)) { + if (uprv_strcmp(pVar,POSIX_VALUE) || len != (int32_t)uprv_strlen(POSIX_VALUE)) { + /* emit the variant to the list */ + var = (VariantListEntry*)uprv_malloc(sizeof(VariantListEntry)); + if (var == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + break; + } + var->variant = pVar; + if (!_addVariantToList(&varFirst, var)) { + /* duplicated variant */ + uprv_free(var); + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + } + } else { + /* Special handling for POSIX variant, need to remember that we had it and then */ + /* treat it like an extension later. */ + *hadPosix = true; + } + } else if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + break; + } else if (_isPrivateuseValueSubtag(pVar, -1)) { + /* Handle private use subtags separately */ + break; + } + } + /* reset variant starting position */ + pVar = nullptr; + } else if (pVar == nullptr) { + pVar = p; + } + p++; + } + + if (U_SUCCESS(*status)) { + if (varFirst != nullptr) { + int32_t varLen; + + /* per UTS35, we should sort the variants */ + _sortVariants(varFirst); + + /* write out validated/normalized variants to the target */ + var = varFirst; + while (var != nullptr) { + sink.Append("-", 1); + varLen = (int32_t)uprv_strlen(var->variant); + sink.Append(var->variant, varLen); + var = var->next; + } + } + } + + /* clean up */ + var = varFirst; + while (var != nullptr) { + VariantListEntry *tmpVar = var->next; + uprv_free(var); + var = tmpVar; + } + + if (U_FAILURE(*status)) { + return; + } + } +} + +static void +_appendKeywordsToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool strict, UBool hadPosix, UErrorCode* status) { + char attrBuf[ULOC_KEYWORD_AND_VALUES_CAPACITY] = { 0 }; + int32_t attrBufLength = 0; + + icu::MemoryPool attrPool; + icu::MemoryPool extPool; + icu::MemoryPool strPool; + + icu::LocalUEnumerationPointer keywordEnum(uloc_openKeywords(localeID, status)); + if (U_FAILURE(*status) && !hadPosix) { + return; + } + if (keywordEnum.isValid() || hadPosix) { + /* reorder extensions */ + int32_t len; + const char *key; + ExtensionListEntry *firstExt = nullptr; + ExtensionListEntry *ext; + AttributeListEntry *firstAttr = nullptr; + AttributeListEntry *attr; + icu::MemoryPool extBufPool; + const char *bcpKey=nullptr, *bcpValue=nullptr; + UErrorCode tmpStatus = U_ZERO_ERROR; + int32_t keylen; + UBool isBcpUExt; + + while (true) { + key = uenum_next(keywordEnum.getAlias(), nullptr, status); + if (key == nullptr) { + break; + } + + icu::CharString buf; + { + icu::CharStringByteSink sink(&buf); + ulocimp_getKeywordValue(localeID, key, sink, &tmpStatus); + } + len = buf.length(); + + if (U_FAILURE(tmpStatus)) { + if (tmpStatus == U_MEMORY_ALLOCATION_ERROR) { + *status = U_MEMORY_ALLOCATION_ERROR; + break; + } + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + /* ignore this keyword */ + tmpStatus = U_ZERO_ERROR; + continue; + } + + keylen = (int32_t)uprv_strlen(key); + isBcpUExt = (keylen > 1); + + /* special keyword used for representing Unicode locale attributes */ + if (uprv_strcmp(key, LOCALE_ATTRIBUTE_KEY) == 0) { + if (len > 0) { + int32_t i = 0; + while (true) { + attrBufLength = 0; + for (; i < len; i++) { + if (buf[i] != '-') { + attrBuf[attrBufLength++] = buf[i]; + } else { + i++; + break; + } + } + if (attrBufLength > 0) { + attrBuf[attrBufLength] = 0; + + } else if (i >= len){ + break; + } + + /* create AttributeListEntry */ + attr = attrPool.create(); + if (attr == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + break; + } + icu::CharString* attrValue = + strPool.create(attrBuf, attrBufLength, *status); + if (attrValue == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + break; + } + if (U_FAILURE(*status)) { + break; + } + attr->attribute = attrValue->data(); + + if (!_addAttributeToList(&firstAttr, attr)) { + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + } + } + /* for a place holder ExtensionListEntry */ + bcpKey = LOCALE_ATTRIBUTE_KEY; + bcpValue = nullptr; + } + } else if (isBcpUExt) { + bcpKey = uloc_toUnicodeLocaleKey(key); + if (bcpKey == nullptr) { + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + continue; + } + + /* we've checked buf is null-terminated above */ + bcpValue = uloc_toUnicodeLocaleType(key, buf.data()); + if (bcpValue == nullptr) { + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + continue; + } + if (bcpValue == buf.data()) { + /* + When uloc_toUnicodeLocaleType(key, buf) returns the + input value as is, the value is well-formed, but has + no known mapping. This implementation normalizes the + value to lower case + */ + icu::CharString* extBuf = extBufPool.create(buf, tmpStatus); + + if (extBuf == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + break; + } + if (U_FAILURE(tmpStatus)) { + *status = tmpStatus; + break; + } + + T_CString_toLowerCase(extBuf->data()); + bcpValue = extBuf->data(); + } + } else { + if (*key == PRIVATEUSE) { + if (!ultag_isPrivateuseValueSubtags(buf.data(), len)) { + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + continue; + } + } else { + if (!_isExtensionSingleton(key, keylen) || !ultag_isExtensionSubtags(buf.data(), len)) { + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + continue; + } + } + bcpKey = key; + icu::CharString* extBuf = + extBufPool.create(buf.data(), len, tmpStatus); + if (extBuf == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + break; + } + if (U_FAILURE(tmpStatus)) { + *status = tmpStatus; + break; + } + bcpValue = extBuf->data(); + } + + /* create ExtensionListEntry */ + ext = extPool.create(); + if (ext == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + break; + } + ext->key = bcpKey; + ext->value = bcpValue; + + if (!_addExtensionToList(&firstExt, ext, true)) { + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + } + } + + /* Special handling for POSIX variant - add the keywords for POSIX */ + if (hadPosix) { + /* create ExtensionListEntry for POSIX */ + ext = extPool.create(); + if (ext == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return; + } + ext->key = POSIX_KEY; + ext->value = POSIX_VALUE; + + if (!_addExtensionToList(&firstExt, ext, true)) { + // Silently ignore errors. + } + } + + if (U_SUCCESS(*status) && (firstExt != nullptr || firstAttr != nullptr)) { + UBool startLDMLExtension = false; + for (ext = firstExt; ext; ext = ext->next) { + if (!startLDMLExtension && uprv_strlen(ext->key) > 1) { + /* first LDML u singlton extension */ + sink.Append("-u", 2); + startLDMLExtension = true; + } + + /* write out the sorted BCP47 attributes, extensions and private use */ + if (uprv_strcmp(ext->key, LOCALE_ATTRIBUTE_KEY) == 0) { + /* write the value for the attributes */ + for (attr = firstAttr; attr; attr = attr->next) { + sink.Append("-", 1); + sink.Append( + attr->attribute, static_cast(uprv_strlen(attr->attribute))); + } + } else { + sink.Append("-", 1); + sink.Append(ext->key, static_cast(uprv_strlen(ext->key))); + if (uprv_strcmp(ext->value, "true") != 0 && + uprv_strcmp(ext->value, "yes") != 0) { + sink.Append("-", 1); + sink.Append(ext->value, static_cast(uprv_strlen(ext->value))); + } + } + } + } + } +} + +/** + * Append keywords parsed from LDML extension value + * e.g. "u-ca-gregory-co-trad" -> {calendar = gregorian} {collation = traditional} + * Note: char* buf is used for storing keywords + */ +static void +_appendLDMLExtensionAsKeywords(const char* ldmlext, ExtensionListEntry** appendTo, icu::MemoryPool& extPool, icu::MemoryPool& kwdBuf, UBool *posixVariant, UErrorCode *status) { + const char *pTag; /* beginning of current subtag */ + const char *pKwds; /* beginning of key-type pairs */ + UBool variantExists = *posixVariant; + + ExtensionListEntry *kwdFirst = nullptr; /* first LDML keyword */ + ExtensionListEntry *kwd, *nextKwd; + + int32_t len; + + /* Reset the posixVariant value */ + *posixVariant = false; + + pTag = ldmlext; + pKwds = nullptr; + + { + AttributeListEntry *attrFirst = nullptr; /* first attribute */ + AttributeListEntry *attr, *nextAttr; + + char attrBuf[ULOC_KEYWORD_AND_VALUES_CAPACITY]; + int32_t attrBufIdx = 0; + + icu::MemoryPool attrPool; + + /* Iterate through u extension attributes */ + while (*pTag) { + /* locate next separator char */ + for (len = 0; *(pTag + len) && *(pTag + len) != SEP; len++); + + if (ultag_isUnicodeLocaleKey(pTag, len)) { + pKwds = pTag; + break; + } + + /* add this attribute to the list */ + attr = attrPool.create(); + if (attr == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + if (len < (int32_t)sizeof(attrBuf) - attrBufIdx) { + uprv_memcpy(&attrBuf[attrBufIdx], pTag, len); + attrBuf[attrBufIdx + len] = 0; + attr->attribute = &attrBuf[attrBufIdx]; + attrBufIdx += (len + 1); + } else { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + // duplicate attribute is ignored, causes no error. + _addAttributeToList(&attrFirst, attr); + + /* next tag */ + pTag += len; + if (*pTag) { + /* next to the separator */ + pTag++; + } + } + + if (attrFirst) { + /* emit attributes as an LDML keyword, e.g. attribute=attr1-attr2 */ + + kwd = extPool.create(); + if (kwd == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + icu::CharString* value = kwdBuf.create(); + if (value == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + /* attribute subtags sorted in alphabetical order as type */ + attr = attrFirst; + while (attr != nullptr) { + nextAttr = attr->next; + if (attr != attrFirst) { + value->append('-', *status); + } + value->append(attr->attribute, *status); + attr = nextAttr; + } + if (U_FAILURE(*status)) { + return; + } + + kwd->key = LOCALE_ATTRIBUTE_KEY; + kwd->value = value->data(); + + if (!_addExtensionToList(&kwdFirst, kwd, false)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + } + } + + if (pKwds) { + const char *pBcpKey = nullptr; /* u extension key subtag */ + const char *pBcpType = nullptr; /* beginning of u extension type subtag(s) */ + int32_t bcpKeyLen = 0; + int32_t bcpTypeLen = 0; + UBool isDone = false; + + pTag = pKwds; + /* BCP47 representation of LDML key/type pairs */ + while (!isDone) { + const char *pNextBcpKey = nullptr; + int32_t nextBcpKeyLen = 0; + UBool emitKeyword = false; + + if (*pTag) { + /* locate next separator char */ + for (len = 0; *(pTag + len) && *(pTag + len) != SEP; len++); + + if (ultag_isUnicodeLocaleKey(pTag, len)) { + if (pBcpKey) { + emitKeyword = true; + pNextBcpKey = pTag; + nextBcpKeyLen = len; + } else { + pBcpKey = pTag; + bcpKeyLen = len; + } + } else { + U_ASSERT(pBcpKey != nullptr); + /* within LDML type subtags */ + if (pBcpType) { + bcpTypeLen += (len + 1); + } else { + pBcpType = pTag; + bcpTypeLen = len; + } + } + + /* next tag */ + pTag += len; + if (*pTag) { + /* next to the separator */ + pTag++; + } + } else { + /* processing last one */ + emitKeyword = true; + isDone = true; + } + + if (emitKeyword) { + const char *pKey = nullptr; /* LDML key */ + const char *pType = nullptr; /* LDML type */ + + char bcpKeyBuf[3]; /* BCP key length is always 2 for now */ + + U_ASSERT(pBcpKey != nullptr); + + if (bcpKeyLen >= (int32_t)sizeof(bcpKeyBuf)) { + /* the BCP key is invalid */ + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + U_ASSERT(bcpKeyLen <= 2); + + uprv_strncpy(bcpKeyBuf, pBcpKey, bcpKeyLen); + bcpKeyBuf[bcpKeyLen] = 0; + + /* u extension key to LDML key */ + pKey = uloc_toLegacyKey(bcpKeyBuf); + if (pKey == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (pKey == bcpKeyBuf) { + /* + The key returned by toLegacyKey points to the input buffer. + We normalize the result key to lower case. + */ + T_CString_toLowerCase(bcpKeyBuf); + icu::CharString* key = kwdBuf.create(bcpKeyBuf, bcpKeyLen, *status); + if (key == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (U_FAILURE(*status)) { + return; + } + pKey = key->data(); + } + + if (pBcpType) { + char bcpTypeBuf[128]; /* practically long enough even considering multiple subtag type */ + if (bcpTypeLen >= (int32_t)sizeof(bcpTypeBuf)) { + /* the BCP type is too long */ + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + uprv_strncpy(bcpTypeBuf, pBcpType, bcpTypeLen); + bcpTypeBuf[bcpTypeLen] = 0; + + /* BCP type to locale type */ + pType = uloc_toLegacyType(pKey, bcpTypeBuf); + if (pType == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (pType == bcpTypeBuf) { + /* + The type returned by toLegacyType points to the input buffer. + We normalize the result type to lower case. + */ + /* normalize to lower case */ + T_CString_toLowerCase(bcpTypeBuf); + icu::CharString* type = kwdBuf.create(bcpTypeBuf, bcpTypeLen, *status); + if (type == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (U_FAILURE(*status)) { + return; + } + pType = type->data(); + } + } else { + /* typeless - default type value is "yes" */ + pType = LOCALE_TYPE_YES; + } + + /* Special handling for u-va-posix, since we want to treat this as a variant, + not as a keyword */ + if (!variantExists && !uprv_strcmp(pKey, POSIX_KEY) && !uprv_strcmp(pType, POSIX_VALUE) ) { + *posixVariant = true; + } else { + /* create an ExtensionListEntry for this keyword */ + kwd = extPool.create(); + if (kwd == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + kwd->key = pKey; + kwd->value = pType; + + if (!_addExtensionToList(&kwdFirst, kwd, false)) { + // duplicate keyword is allowed, Only the first + // is honored. + } + } + + pBcpKey = pNextBcpKey; + bcpKeyLen = pNextBcpKey != nullptr ? nextBcpKeyLen : 0; + pBcpType = nullptr; + bcpTypeLen = 0; + } + } + } + + kwd = kwdFirst; + while (kwd != nullptr) { + nextKwd = kwd->next; + _addExtensionToList(appendTo, kwd, false); + kwd = nextKwd; + } +} + + +static void +_appendKeywords(ULanguageTag* langtag, icu::ByteSink& sink, UErrorCode* status) { + int32_t i, n; + int32_t len; + ExtensionListEntry *kwdFirst = nullptr; + ExtensionListEntry *kwd; + const char *key, *type; + icu::MemoryPool extPool; + icu::MemoryPool kwdBuf; + UBool posixVariant = false; + + if (U_FAILURE(*status)) { + return; + } + + n = ultag_getExtensionsSize(langtag); + + /* resolve locale keywords and reordering keys */ + for (i = 0; i < n; i++) { + key = ultag_getExtensionKey(langtag, i); + type = ultag_getExtensionValue(langtag, i); + if (*key == LDMLEXT) { + /* Determine if variants already exists */ + if (ultag_getVariantsSize(langtag)) { + posixVariant = true; + } + + _appendLDMLExtensionAsKeywords(type, &kwdFirst, extPool, kwdBuf, &posixVariant, status); + if (U_FAILURE(*status)) { + break; + } + } else { + kwd = extPool.create(); + if (kwd == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + break; + } + kwd->key = key; + kwd->value = type; + if (!_addExtensionToList(&kwdFirst, kwd, false)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + } + } + + if (U_SUCCESS(*status)) { + type = ultag_getPrivateUse(langtag); + if ((int32_t)uprv_strlen(type) > 0) { + /* add private use as a keyword */ + kwd = extPool.create(); + if (kwd == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + } else { + kwd->key = PRIVATEUSE_KEY; + kwd->value = type; + if (!_addExtensionToList(&kwdFirst, kwd, false)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + } + } + } + } + + /* If a POSIX variant was in the extensions, write it out before writing the keywords. */ + + if (U_SUCCESS(*status) && posixVariant) { + len = (int32_t) uprv_strlen(_POSIX); + sink.Append(_POSIX, len); + } + + if (U_SUCCESS(*status) && kwdFirst != nullptr) { + /* write out the sorted keywords */ + UBool firstValue = true; + kwd = kwdFirst; + do { + if (firstValue) { + sink.Append("@", 1); + firstValue = false; + } else { + sink.Append(";", 1); + } + + /* key */ + len = (int32_t)uprv_strlen(kwd->key); + sink.Append(kwd->key, len); + sink.Append("=", 1); + + /* type */ + len = (int32_t)uprv_strlen(kwd->value); + sink.Append(kwd->value, len); + + kwd = kwd->next; + } while (kwd); + } +} + +static void +_appendPrivateuseToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool strict, UBool hadPosix, UErrorCode* status) { + (void)hadPosix; + char buf[ULOC_FULLNAME_CAPACITY]; + char tmpAppend[ULOC_FULLNAME_CAPACITY]; + UErrorCode tmpStatus = U_ZERO_ERROR; + int32_t len, i; + int32_t reslen = 0; + int32_t capacity = sizeof tmpAppend; + + if (U_FAILURE(*status)) { + return; + } + + len = uloc_getVariant(localeID, buf, sizeof(buf), &tmpStatus); + if (U_FAILURE(tmpStatus) || tmpStatus == U_STRING_NOT_TERMINATED_WARNING) { + if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + } + return; + } + + if (len > 0) { + char *p, *pPriv; + UBool bNext = true; + UBool firstValue = true; + UBool writeValue; + + pPriv = nullptr; + p = buf; + while (bNext) { + writeValue = false; + if (*p == SEP || *p == LOCALE_SEP || *p == 0) { + if (*p == 0) { + bNext = false; + } else { + *p = 0; /* terminate */ + } + if (pPriv != nullptr) { + /* Private use in the canonical format is lowercase in BCP47 */ + for (i = 0; *(pPriv + i) != 0; i++) { + *(pPriv + i) = uprv_tolower(*(pPriv + i)); + } + + /* validate */ + if (_isPrivateuseValueSubtag(pPriv, -1)) { + if (firstValue) { + if (!_isVariantSubtag(pPriv, -1)) { + writeValue = true; + } + } else { + writeValue = true; + } + } else if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + break; + } else { + break; + } + + if (writeValue) { + if (reslen < capacity) { + tmpAppend[reslen++] = SEP; + } + + if (firstValue) { + if (reslen < capacity) { + tmpAppend[reslen++] = *PRIVATEUSE_KEY; + } + + if (reslen < capacity) { + tmpAppend[reslen++] = SEP; + } + + len = (int32_t)uprv_strlen(PRIVUSE_VARIANT_PREFIX); + if (reslen < capacity) { + uprv_memcpy(tmpAppend + reslen, PRIVUSE_VARIANT_PREFIX, uprv_min(len, capacity - reslen)); + } + reslen += len; + + if (reslen < capacity) { + tmpAppend[reslen++] = SEP; + } + + firstValue = false; + } + + len = (int32_t)uprv_strlen(pPriv); + if (reslen < capacity) { + uprv_memcpy(tmpAppend + reslen, pPriv, uprv_min(len, capacity - reslen)); + } + reslen += len; + } + } + /* reset private use starting position */ + pPriv = nullptr; + } else if (pPriv == nullptr) { + pPriv = p; + } + p++; + } + + if (U_FAILURE(*status)) { + return; + } + } + + if (U_SUCCESS(*status)) { + len = reslen; + sink.Append(tmpAppend, len); + } +} + +/* +* ------------------------------------------------- +* +* ultag_ functions +* +* ------------------------------------------------- +*/ + +/* Bit flags used by the parser */ +#define LANG 0x0001 +#define EXTL 0x0002 +#define SCRT 0x0004 +#define REGN 0x0008 +#define VART 0x0010 +#define EXTS 0x0020 +#define EXTV 0x0040 +#define PRIV 0x0080 + +/** + * Ticket #12705 - The optimizer in Visual Studio 2015 Update 3 has problems optimizing this function. + * As a work-around, optimization is disabled for this function on VS2015 and VS2017. + * This work-around should be removed once the following versions of Visual Studio are no + * longer supported: All versions of VS2015/VS2017, and versions of VS2019 below 16.4. + */ +#if defined(_MSC_VER) && (_MSC_VER >= 1900) && (_MSC_VER < 1924) +#pragma optimize( "", off ) +#endif + +static ULanguageTag* +ultag_parse(const char* tag, int32_t tagLen, int32_t* parsedLen, UErrorCode* status) { + char *tagBuf; + int16_t next; + char *pSubtag, *pNext, *pLastGoodPosition; + int32_t subtagLen; + int32_t extlangIdx; + ExtensionListEntry *pExtension; + char *pExtValueSubtag, *pExtValueSubtagEnd; + int32_t i; + UBool privateuseVar = false; + int32_t legacyLen = 0; + + if (parsedLen != nullptr) { + *parsedLen = 0; + } + + if (U_FAILURE(*status)) { + return nullptr; + } + + if (tagLen < 0) { + tagLen = (int32_t)uprv_strlen(tag); + } + + /* copy the entire string */ + tagBuf = (char*)uprv_malloc(tagLen + 1); + if (tagBuf == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + if (tagLen > 0) { + uprv_memcpy(tagBuf, tag, tagLen); + } + *(tagBuf + tagLen) = 0; + + /* create a ULanguageTag */ + icu::LocalULanguageTagPointer t( + (ULanguageTag*)uprv_malloc(sizeof(ULanguageTag))); + if (t.isNull()) { + uprv_free(tagBuf); + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + _initializeULanguageTag(t.getAlias()); + t->buf = tagBuf; + + if (tagLen < MINLEN) { + /* the input tag is too short - return empty ULanguageTag */ + return t.orphan(); + } + + size_t parsedLenDelta = 0; + // Legacy tag will be consider together. Legacy tag with intervening + // script and region such as art-DE-lojban or art-Latn-lojban won't be + // matched. + /* check if the tag is legacy */ + for (i = 0; i < UPRV_LENGTHOF(LEGACY); i += 2) { + int32_t checkLegacyLen = static_cast(uprv_strlen(LEGACY[i])); + if (tagLen < checkLegacyLen) { + continue; + } + if (tagLen > checkLegacyLen && tagBuf[checkLegacyLen] != '-') { + // make sure next char is '-'. + continue; + } + if (uprv_strnicmp(LEGACY[i], tagBuf, checkLegacyLen) == 0) { + int32_t newTagLength; + + legacyLen = checkLegacyLen; /* back up for output parsedLen */ + int32_t replacementLen = static_cast(uprv_strlen(LEGACY[i+1])); + newTagLength = replacementLen + tagLen - checkLegacyLen; + int32_t oldTagLength = tagLen; + if (tagLen < newTagLength) { + uprv_free(tagBuf); + tagBuf = (char*)uprv_malloc(newTagLength + 1); + if (tagBuf == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + t->buf = tagBuf; + tagLen = newTagLength; + } + parsedLenDelta = checkLegacyLen - replacementLen; + uprv_strcpy(t->buf, LEGACY[i + 1]); + if (checkLegacyLen != tagLen) { + uprv_memcpy(t->buf + replacementLen, tag + checkLegacyLen, + oldTagLength - checkLegacyLen); + // NUL-terminate after memcpy(). + t->buf[replacementLen + oldTagLength - checkLegacyLen] = 0; + } + break; + } + } + + if (legacyLen == 0) { + for (i = 0; i < UPRV_LENGTHOF(REDUNDANT); i += 2) { + const char* redundantTag = REDUNDANT[i]; + size_t redundantTagLen = uprv_strlen(redundantTag); + // The preferred tag for a redundant tag is always shorter than redundant + // tag. A redundant tag may or may not be followed by other subtags. + // (i.e. "zh-yue" or "zh-yue-u-co-pinyin"). + if (uprv_strnicmp(redundantTag, tagBuf, static_cast(redundantTagLen)) == 0) { + const char* redundantTagEnd = tagBuf + redundantTagLen; + if (*redundantTagEnd == '\0' || *redundantTagEnd == SEP) { + const char* preferredTag = REDUNDANT[i + 1]; + size_t preferredTagLen = uprv_strlen(preferredTag); + uprv_memcpy(t->buf, preferredTag, preferredTagLen); + if (*redundantTagEnd == SEP) { + uprv_memmove(tagBuf + preferredTagLen, + redundantTagEnd, + tagLen - redundantTagLen + 1); + } else { + tagBuf[preferredTagLen] = '\0'; + } + // parsedLen should be the length of the input + // before redundantTag is replaced by preferredTag. + // Save the delta to add it back later. + parsedLenDelta = redundantTagLen - preferredTagLen; + break; + } + } + } + } + + /* + * langtag = language + * ["-" script] + * ["-" region] + * *("-" variant) + * *("-" extension) + * ["-" privateuse] + */ + + next = LANG | PRIV; + pNext = pLastGoodPosition = tagBuf; + extlangIdx = 0; + pExtension = nullptr; + pExtValueSubtag = nullptr; + pExtValueSubtagEnd = nullptr; + + while (pNext) { + char *pSep; + + pSubtag = pNext; + + /* locate next separator char */ + pSep = pSubtag; + while (*pSep) { + if (*pSep == SEP) { + break; + } + pSep++; + } + if (*pSep == 0) { + /* last subtag */ + pNext = nullptr; + } else { + pNext = pSep + 1; + } + subtagLen = (int32_t)(pSep - pSubtag); + + if (next & LANG) { + if (ultag_isLanguageSubtag(pSubtag, subtagLen)) { + *pSep = 0; /* terminate */ + // TODO: move deprecated language code handling here. + t->language = T_CString_toLowerCase(pSubtag); + + pLastGoodPosition = pSep; + next = SCRT | REGN | VART | EXTS | PRIV; + if (subtagLen <= 3) + next |= EXTL; + continue; + } + } + if (next & EXTL) { + if (_isExtlangSubtag(pSubtag, subtagLen)) { + *pSep = 0; + t->extlang[extlangIdx++] = T_CString_toLowerCase(pSubtag); + + pLastGoodPosition = pSep; + if (extlangIdx < 3) { + next = EXTL | SCRT | REGN | VART | EXTS | PRIV; + } else { + next = SCRT | REGN | VART | EXTS | PRIV; + } + continue; + } + } + if (next & SCRT) { + if (ultag_isScriptSubtag(pSubtag, subtagLen)) { + char *p = pSubtag; + + *pSep = 0; + + /* to title case */ + *p = uprv_toupper(*p); + p++; + for (; *p; p++) { + *p = uprv_tolower(*p); + } + + t->script = pSubtag; + + pLastGoodPosition = pSep; + next = REGN | VART | EXTS | PRIV; + continue; + } + } + if (next & REGN) { + if (ultag_isRegionSubtag(pSubtag, subtagLen)) { + *pSep = 0; + // TODO: move deprecated region code handling here. + t->region = T_CString_toUpperCase(pSubtag); + + pLastGoodPosition = pSep; + next = VART | EXTS | PRIV; + continue; + } + } + if (next & VART) { + if (_isVariantSubtag(pSubtag, subtagLen) || + (privateuseVar && _isPrivateuseVariantSubtag(pSubtag, subtagLen))) { + VariantListEntry *var; + UBool isAdded; + + var = (VariantListEntry*)uprv_malloc(sizeof(VariantListEntry)); + if (var == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + *pSep = 0; + var->variant = T_CString_toUpperCase(pSubtag); + isAdded = _addVariantToList(&(t->variants), var); + if (!isAdded) { + /* duplicated variant entry */ + uprv_free(var); + break; + } + pLastGoodPosition = pSep; + next = VART | EXTS | PRIV; + continue; + } + } + if (next & EXTS) { + if (_isExtensionSingleton(pSubtag, subtagLen)) { + if (pExtension != nullptr) { + if (pExtValueSubtag == nullptr || pExtValueSubtagEnd == nullptr) { + /* the previous extension is incomplete */ + uprv_free(pExtension); + pExtension = nullptr; + break; + } + + /* terminate the previous extension value */ + *pExtValueSubtagEnd = 0; + pExtension->value = T_CString_toLowerCase(pExtValueSubtag); + + /* insert the extension to the list */ + if (_addExtensionToList(&(t->extensions), pExtension, false)) { + pLastGoodPosition = pExtValueSubtagEnd; + } else { + /* stop parsing here */ + uprv_free(pExtension); + pExtension = nullptr; + break; + } + } + + /* create a new extension */ + pExtension = (ExtensionListEntry*)uprv_malloc(sizeof(ExtensionListEntry)); + if (pExtension == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + *pSep = 0; + pExtension->key = T_CString_toLowerCase(pSubtag); + pExtension->value = nullptr; /* will be set later */ + + /* + * reset the start and the end location of extension value + * subtags for this extension + */ + pExtValueSubtag = nullptr; + pExtValueSubtagEnd = nullptr; + + next = EXTV; + continue; + } + } + if (next & EXTV) { + if (_isExtensionSubtag(pSubtag, subtagLen)) { + if (pExtValueSubtag == nullptr) { + /* if the start position of this extension's value is not yet, + this one is the first value subtag */ + pExtValueSubtag = pSubtag; + } + + /* Mark the end of this subtag */ + pExtValueSubtagEnd = pSep; + next = EXTS | EXTV | PRIV; + + continue; + } + } + if (next & PRIV) { + if (uprv_tolower(*pSubtag) == PRIVATEUSE && subtagLen == 1) { + char *pPrivuseVal; + + if (pExtension != nullptr) { + /* Process the last extension */ + if (pExtValueSubtag == nullptr || pExtValueSubtagEnd == nullptr) { + /* the previous extension is incomplete */ + uprv_free(pExtension); + pExtension = nullptr; + break; + } else { + /* terminate the previous extension value */ + *pExtValueSubtagEnd = 0; + pExtension->value = T_CString_toLowerCase(pExtValueSubtag); + + /* insert the extension to the list */ + if (_addExtensionToList(&(t->extensions), pExtension, false)) { + pLastGoodPosition = pExtValueSubtagEnd; + pExtension = nullptr; + } else { + /* stop parsing here */ + uprv_free(pExtension); + pExtension = nullptr; + break; + } + } + } + + /* The rest of part will be private use value subtags */ + if (pNext == nullptr) { + /* empty private use subtag */ + break; + } + /* back up the private use value start position */ + pPrivuseVal = pNext; + + /* validate private use value subtags */ + while (pNext) { + pSubtag = pNext; + pSep = pSubtag; + while (*pSep) { + if (*pSep == SEP) { + break; + } + pSep++; + } + if (*pSep == 0) { + /* last subtag */ + pNext = nullptr; + } else { + pNext = pSep + 1; + } + subtagLen = (int32_t)(pSep - pSubtag); + + if (uprv_strncmp(pSubtag, PRIVUSE_VARIANT_PREFIX, uprv_strlen(PRIVUSE_VARIANT_PREFIX)) == 0) { + *pSep = 0; + next = VART; + privateuseVar = true; + break; + } else if (_isPrivateuseValueSubtag(pSubtag, subtagLen)) { + pLastGoodPosition = pSep; + } else { + break; + } + } + + if (next == VART) { + continue; + } + + if (pLastGoodPosition - pPrivuseVal > 0) { + *pLastGoodPosition = 0; + t->privateuse = T_CString_toLowerCase(pPrivuseVal); + } + /* No more subtags, exiting the parse loop */ + break; + } + break; + } + + /* If we fell through here, it means this subtag is illegal - quit parsing */ + break; + } + + if (pExtension != nullptr) { + /* Process the last extension */ + if (pExtValueSubtag == nullptr || pExtValueSubtagEnd == nullptr) { + /* the previous extension is incomplete */ + uprv_free(pExtension); + } else { + /* terminate the previous extension value */ + *pExtValueSubtagEnd = 0; + pExtension->value = T_CString_toLowerCase(pExtValueSubtag); + /* insert the extension to the list */ + if (_addExtensionToList(&(t->extensions), pExtension, false)) { + pLastGoodPosition = pExtValueSubtagEnd; + } else { + uprv_free(pExtension); + } + } + } + + if (parsedLen != nullptr) { + *parsedLen = (int32_t)(pLastGoodPosition - t->buf + parsedLenDelta); + } + + return t.orphan(); +} + +// Ticket #12705 - Turn optimization back on. +#if defined(_MSC_VER) && (_MSC_VER >= 1900) && (_MSC_VER < 1924) +#pragma optimize( "", on ) +#endif + +static void +ultag_close(ULanguageTag* langtag) { + + if (langtag == nullptr) { + return; + } + + uprv_free(langtag->buf); + + if (langtag->variants) { + VariantListEntry *curVar = langtag->variants; + while (curVar) { + VariantListEntry *nextVar = curVar->next; + uprv_free(curVar); + curVar = nextVar; + } + } + + if (langtag->extensions) { + ExtensionListEntry *curExt = langtag->extensions; + while (curExt) { + ExtensionListEntry *nextExt = curExt->next; + uprv_free(curExt); + curExt = nextExt; + } + } + + uprv_free(langtag); +} + +static const char* +ultag_getLanguage(const ULanguageTag* langtag) { + return langtag->language; +} + +#if 0 +static const char* +ultag_getJDKLanguage(const ULanguageTag* langtag) { + int32_t i; + for (i = 0; DEPRECATEDLANGS[i] != nullptr; i += 2) { + if (uprv_compareInvCharsAsAscii(DEPRECATEDLANGS[i], langtag->language) == 0) { + return DEPRECATEDLANGS[i + 1]; + } + } + return langtag->language; +} +#endif + +static const char* +ultag_getExtlang(const ULanguageTag* langtag, int32_t idx) { + if (idx >= 0 && idx < MAXEXTLANG) { + return langtag->extlang[idx]; + } + return nullptr; +} + +static int32_t +ultag_getExtlangSize(const ULanguageTag* langtag) { + int32_t size = 0; + int32_t i; + for (i = 0; i < MAXEXTLANG; i++) { + if (langtag->extlang[i]) { + size++; + } + } + return size; +} + +static const char* +ultag_getScript(const ULanguageTag* langtag) { + return langtag->script; +} + +static const char* +ultag_getRegion(const ULanguageTag* langtag) { + return langtag->region; +} + +static const char* +ultag_getVariant(const ULanguageTag* langtag, int32_t idx) { + const char *var = nullptr; + VariantListEntry *cur = langtag->variants; + int32_t i = 0; + while (cur) { + if (i == idx) { + var = cur->variant; + break; + } + cur = cur->next; + i++; + } + return var; +} + +static int32_t +ultag_getVariantsSize(const ULanguageTag* langtag) { + int32_t size = 0; + VariantListEntry *cur = langtag->variants; + while (true) { + if (cur == nullptr) { + break; + } + size++; + cur = cur->next; + } + return size; +} + +static const char* +ultag_getExtensionKey(const ULanguageTag* langtag, int32_t idx) { + const char *key = nullptr; + ExtensionListEntry *cur = langtag->extensions; + int32_t i = 0; + while (cur) { + if (i == idx) { + key = cur->key; + break; + } + cur = cur->next; + i++; + } + return key; +} + +static const char* +ultag_getExtensionValue(const ULanguageTag* langtag, int32_t idx) { + const char *val = nullptr; + ExtensionListEntry *cur = langtag->extensions; + int32_t i = 0; + while (cur) { + if (i == idx) { + val = cur->value; + break; + } + cur = cur->next; + i++; + } + return val; +} + +static int32_t +ultag_getExtensionsSize(const ULanguageTag* langtag) { + int32_t size = 0; + ExtensionListEntry *cur = langtag->extensions; + while (true) { + if (cur == nullptr) { + break; + } + size++; + cur = cur->next; + } + return size; +} + +static const char* +ultag_getPrivateUse(const ULanguageTag* langtag) { + return langtag->privateuse; +} + +#if 0 +static const char* +ultag_getLegacy(const ULanguageTag* langtag) { + return langtag->legacy; +} +#endif + + +/* +* ------------------------------------------------- +* +* Locale/BCP47 conversion APIs, exposed as uloc_* +* +* ------------------------------------------------- +*/ +U_CAPI int32_t U_EXPORT2 +uloc_toLanguageTag(const char* localeID, + char* langtag, + int32_t langtagCapacity, + UBool strict, + UErrorCode* status) { + if (U_FAILURE(*status)) { + return 0; + } + + icu::CheckedArrayByteSink sink(langtag, langtagCapacity); + ulocimp_toLanguageTag(localeID, sink, strict, status); + + int32_t reslen = sink.NumberOfBytesAppended(); + + if (U_FAILURE(*status)) { + return reslen; + } + + if (sink.Overflowed()) { + *status = U_BUFFER_OVERFLOW_ERROR; + } else { + u_terminateChars(langtag, langtagCapacity, reslen, status); + } + + return reslen; +} + + +U_CAPI void U_EXPORT2 +ulocimp_toLanguageTag(const char* localeID, + icu::ByteSink& sink, + UBool strict, + UErrorCode* status) { + icu::CharString canonical; + int32_t reslen; + UErrorCode tmpStatus = U_ZERO_ERROR; + UBool hadPosix = false; + const char* pKeywordStart; + + /* Note: uloc_canonicalize returns "en_US_POSIX" for input locale ID "". See #6835 */ + int32_t resultCapacity = static_cast(uprv_strlen(localeID)); + if (resultCapacity > 0) { + char* buffer; + + for (;;) { + buffer = canonical.getAppendBuffer( + /*minCapacity=*/resultCapacity, + /*desiredCapacityHint=*/resultCapacity, + resultCapacity, + tmpStatus); + + if (U_FAILURE(tmpStatus)) { + *status = tmpStatus; + return; + } + + reslen = + uloc_canonicalize(localeID, buffer, resultCapacity, &tmpStatus); + + if (tmpStatus != U_BUFFER_OVERFLOW_ERROR) { + break; + } + + resultCapacity = reslen; + tmpStatus = U_ZERO_ERROR; + } + + if (U_FAILURE(tmpStatus)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + canonical.append(buffer, reslen, tmpStatus); + if (tmpStatus == U_STRING_NOT_TERMINATED_WARNING) { + tmpStatus = U_ZERO_ERROR; // Terminators provided by CharString. + } + + if (U_FAILURE(tmpStatus)) { + *status = tmpStatus; + return; + } + } + + /* For handling special case - private use only tag */ + pKeywordStart = locale_getKeywordsStart(canonical.data()); + if (pKeywordStart == canonical.data()) { + int kwdCnt = 0; + UBool done = false; + + icu::LocalUEnumerationPointer kwdEnum(uloc_openKeywords(canonical.data(), &tmpStatus)); + if (U_SUCCESS(tmpStatus)) { + kwdCnt = uenum_count(kwdEnum.getAlias(), &tmpStatus); + if (kwdCnt == 1) { + const char *key; + int32_t len = 0; + + key = uenum_next(kwdEnum.getAlias(), &len, &tmpStatus); + if (len == 1 && *key == PRIVATEUSE) { + icu::CharString buf; + { + icu::CharStringByteSink sink(&buf); + ulocimp_getKeywordValue(localeID, key, sink, &tmpStatus); + } + if (U_SUCCESS(tmpStatus)) { + if (ultag_isPrivateuseValueSubtags(buf.data(), buf.length())) { + /* return private use only tag */ + sink.Append("und-x-", 6); + sink.Append(buf.data(), buf.length()); + done = true; + } else if (strict) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + done = true; + } + /* if not strict mode, then "und" will be returned */ + } else { + *status = U_ILLEGAL_ARGUMENT_ERROR; + done = true; + } + } + } + if (done) { + return; + } + } + } + + _appendLanguageToLanguageTag(canonical.data(), sink, strict, status); + _appendScriptToLanguageTag(canonical.data(), sink, strict, status); + _appendRegionToLanguageTag(canonical.data(), sink, strict, status); + _appendVariantsToLanguageTag(canonical.data(), sink, strict, &hadPosix, status); + _appendKeywordsToLanguageTag(canonical.data(), sink, strict, hadPosix, status); + _appendPrivateuseToLanguageTag(canonical.data(), sink, strict, hadPosix, status); +} + + +U_CAPI int32_t U_EXPORT2 +uloc_forLanguageTag(const char* langtag, + char* localeID, + int32_t localeIDCapacity, + int32_t* parsedLength, + UErrorCode* status) { + if (U_FAILURE(*status)) { + return 0; + } + + icu::CheckedArrayByteSink sink(localeID, localeIDCapacity); + ulocimp_forLanguageTag(langtag, -1, sink, parsedLength, status); + + int32_t reslen = sink.NumberOfBytesAppended(); + + if (U_FAILURE(*status)) { + return reslen; + } + + if (sink.Overflowed()) { + *status = U_BUFFER_OVERFLOW_ERROR; + } else { + u_terminateChars(localeID, localeIDCapacity, reslen, status); + } + + return reslen; +} + + +U_CAPI void U_EXPORT2 +ulocimp_forLanguageTag(const char* langtag, + int32_t tagLen, + icu::ByteSink& sink, + int32_t* parsedLength, + UErrorCode* status) { + UBool isEmpty = true; + const char *subtag, *p; + int32_t len; + int32_t i, n; + UBool noRegion = true; + + icu::LocalULanguageTagPointer lt(ultag_parse(langtag, tagLen, parsedLength, status)); + if (U_FAILURE(*status)) { + return; + } + + /* language */ + subtag = ultag_getExtlangSize(lt.getAlias()) > 0 ? ultag_getExtlang(lt.getAlias(), 0) : ultag_getLanguage(lt.getAlias()); + if (uprv_compareInvCharsAsAscii(subtag, LANG_UND) != 0) { + len = (int32_t)uprv_strlen(subtag); + if (len > 0) { + sink.Append(subtag, len); + isEmpty = false; + } + } + + /* script */ + subtag = ultag_getScript(lt.getAlias()); + len = (int32_t)uprv_strlen(subtag); + if (len > 0) { + sink.Append("_", 1); + isEmpty = false; + + /* write out the script in title case */ + char c = uprv_toupper(*subtag); + sink.Append(&c, 1); + sink.Append(subtag + 1, len - 1); + } + + /* region */ + subtag = ultag_getRegion(lt.getAlias()); + len = (int32_t)uprv_strlen(subtag); + if (len > 0) { + sink.Append("_", 1); + isEmpty = false; + + /* write out the region in upper case */ + p = subtag; + while (*p) { + char c = uprv_toupper(*p); + sink.Append(&c, 1); + p++; + } + noRegion = false; + } + + /* variants */ + _sortVariants(lt.getAlias()->variants); + n = ultag_getVariantsSize(lt.getAlias()); + if (n > 0) { + if (noRegion) { + sink.Append("_", 1); + isEmpty = false; + } + + for (i = 0; i < n; i++) { + subtag = ultag_getVariant(lt.getAlias(), i); + sink.Append("_", 1); + + /* write out the variant in upper case */ + p = subtag; + while (*p) { + char c = uprv_toupper(*p); + sink.Append(&c, 1); + p++; + } + } + } + + /* keywords */ + n = ultag_getExtensionsSize(lt.getAlias()); + subtag = ultag_getPrivateUse(lt.getAlias()); + if (n > 0 || uprv_strlen(subtag) > 0) { + if (isEmpty && n > 0) { + /* need a language */ + sink.Append(LANG_UND, LANG_UND_LEN); + } + _appendKeywords(lt.getAlias(), sink, status); + } +} diff --git a/deps/icu-small/source/common/ulocimp.h b/deps/icu-small/source/common/ulocimp.h index 755e02c6b88eac..3a1e07ae7e605a 100644 --- a/deps/icu-small/source/common/ulocimp.h +++ b/deps/icu-small/source/common/ulocimp.h @@ -1,378 +1,378 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2004-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#ifndef ULOCIMP_H -#define ULOCIMP_H - -#include "unicode/bytestream.h" -#include "unicode/uloc.h" - -#include "charstr.h" - -/** - * Create an iterator over the specified keywords list - * @param keywordList double-null terminated list. Will be copied. - * @param keywordListSize size in bytes of keywordList - * @param status err code - * @return enumeration (owned by caller) of the keyword list. - * @internal ICU 3.0 - */ -U_CAPI UEnumeration* U_EXPORT2 -uloc_openKeywordList(const char *keywordList, int32_t keywordListSize, UErrorCode* status); - -/** - * Look up a resource bundle table item with fallback on the table level. - * This is accessible so it can be called by C++ code. - */ -U_CAPI const UChar * U_EXPORT2 -uloc_getTableStringWithFallback( - const char *path, - const char *locale, - const char *tableKey, - const char *subTableKey, - const char *itemKey, - int32_t *pLength, - UErrorCode *pErrorCode); - -/*returns true if a is an ID separator false otherwise*/ -#define _isIDSeparator(a) (a == '_' || a == '-') - -U_CFUNC const char* -uloc_getCurrentCountryID(const char* oldID); - -U_CFUNC const char* -uloc_getCurrentLanguageID(const char* oldID); - -U_CFUNC void -ulocimp_getKeywords(const char *localeID, - char prev, - icu::ByteSink& sink, - UBool valuesToo, - UErrorCode *status); - -icu::CharString U_EXPORT2 -ulocimp_getLanguage(const char *localeID, - const char **pEnd, - UErrorCode &status); - -icu::CharString U_EXPORT2 -ulocimp_getScript(const char *localeID, - const char **pEnd, - UErrorCode &status); - -icu::CharString U_EXPORT2 -ulocimp_getCountry(const char *localeID, - const char **pEnd, - UErrorCode &status); - -U_CAPI void U_EXPORT2 -ulocimp_getName(const char* localeID, - icu::ByteSink& sink, - UErrorCode* err); - -U_CAPI void U_EXPORT2 -ulocimp_getBaseName(const char* localeID, - icu::ByteSink& sink, - UErrorCode* err); - -U_CAPI void U_EXPORT2 -ulocimp_canonicalize(const char* localeID, - icu::ByteSink& sink, - UErrorCode* err); - -U_CAPI void U_EXPORT2 -ulocimp_getKeywordValue(const char* localeID, - const char* keywordName, - icu::ByteSink& sink, - UErrorCode* status); - -/** - * Writes a well-formed language tag for this locale ID. - * - * **Note**: When `strict` is false, any locale fields which do not satisfy the - * BCP47 syntax requirement will be omitted from the result. When `strict` is - * true, this function sets U_ILLEGAL_ARGUMENT_ERROR to the `err` if any locale - * fields do not satisfy the BCP47 syntax requirement. - * - * @param localeID the input locale ID - * @param sink the output sink receiving the BCP47 language - * tag for this Locale. - * @param strict boolean value indicating if the function returns - * an error for an ill-formed input locale ID. - * @param err error information if receiving the language - * tag failed. - * @return The length of the BCP47 language tag. - * - * @internal ICU 64 - */ -U_CAPI void U_EXPORT2 -ulocimp_toLanguageTag(const char* localeID, - icu::ByteSink& sink, - UBool strict, - UErrorCode* err); - -/** - * Returns a locale ID for the specified BCP47 language tag string. - * If the specified language tag contains any ill-formed subtags, - * the first such subtag and all following subtags are ignored. - *

- * This implements the 'Language-Tag' production of BCP 47, and so - * supports legacy language tags (marked as “Type: grandfathered” in BCP 47) - * (regular and irregular) as well as private use language tags. - * - * Private use tags are represented as 'x-whatever', - * and legacy tags are converted to their canonical replacements where they exist. - * - * Note that a few legacy tags have no modern replacement; - * these will be converted using the fallback described in - * the first paragraph, so some information might be lost. - * - * @param langtag the input BCP47 language tag. - * @param tagLen the length of langtag, or -1 to call uprv_strlen(). - * @param sink the output sink receiving a locale ID for the - * specified BCP47 language tag. - * @param parsedLength if not NULL, successfully parsed length - * for the input language tag is set. - * @param err error information if receiving the locald ID - * failed. - * @internal ICU 63 - */ -U_CAPI void U_EXPORT2 -ulocimp_forLanguageTag(const char* langtag, - int32_t tagLen, - icu::ByteSink& sink, - int32_t* parsedLength, - UErrorCode* err); - -/** - * Get the region to use for supplemental data lookup. Uses - * (1) any region specified by locale tag "rg"; if none then - * (2) any unicode_region_tag in the locale ID; if none then - * (3) if inferRegion is true, the region suggested by - * getLikelySubtags on the localeID. - * If no region is found, returns length 0. - * - * @param localeID - * The complete locale ID (with keywords) from which - * to get the region to use for supplemental data. - * @param inferRegion - * If true, will try to infer region from localeID if - * no other region is found. - * @param region - * Buffer in which to put the region ID found; should - * have a capacity at least ULOC_COUNTRY_CAPACITY. - * @param regionCapacity - * The actual capacity of the region buffer. - * @param status - * Pointer to in/out UErrorCode value for latest status. - * @return - * The length of any region code found, or 0 if none. - * @internal ICU 57 - */ -U_CAPI int32_t U_EXPORT2 -ulocimp_getRegionForSupplementalData(const char *localeID, UBool inferRegion, - char *region, int32_t regionCapacity, UErrorCode* status); - -/** - * Add the likely subtags for a provided locale ID, per the algorithm described - * in the following CLDR technical report: - * - * http://www.unicode.org/reports/tr35/#Likely_Subtags - * - * If localeID is already in the maximal form, or there is no data available - * for maximization, it will be copied to the output buffer. For example, - * "und-Zzzz" cannot be maximized, since there is no reasonable maximization. - * - * Examples: - * - * "en" maximizes to "en_Latn_US" - * - * "de" maximizes to "de_Latn_US" - * - * "sr" maximizes to "sr_Cyrl_RS" - * - * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.) - * - * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.) - * - * @param localeID The locale to maximize - * @param sink The output sink receiving the maximized locale - * @param err Error information if maximizing the locale failed. If the length - * of the localeID and the null-terminator is greater than the maximum allowed size, - * or the localeId is not well-formed, the error code is U_ILLEGAL_ARGUMENT_ERROR. - * @internal ICU 64 - */ -U_CAPI void U_EXPORT2 -ulocimp_addLikelySubtags(const char* localeID, - icu::ByteSink& sink, - UErrorCode* err); - -/** - * Minimize the subtags for a provided locale ID, per the algorithm described - * in the following CLDR technical report: - * - * http://www.unicode.org/reports/tr35/#Likely_Subtags - * - * If localeID is already in the minimal form, or there is no data available - * for minimization, it will be copied to the output buffer. Since the - * minimization algorithm relies on proper maximization, see the comments - * for ulocimp_addLikelySubtags for reasons why there might not be any data. - * - * Examples: - * - * "en_Latn_US" minimizes to "en" - * - * "de_Latn_US" minimizes to "de" - * - * "sr_Cyrl_RS" minimizes to "sr" - * - * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the - * script, and minimizing to "zh" would imply "zh_Hans_CN".) - * - * @param localeID The locale to minimize - * @param sink The output sink receiving the maximized locale - * @param err Error information if minimizing the locale failed. If the length - * of the localeID and the null-terminator is greater than the maximum allowed size, - * or the localeId is not well-formed, the error code is U_ILLEGAL_ARGUMENT_ERROR. - * @internal ICU 64 - */ -U_CAPI void U_EXPORT2 -ulocimp_minimizeSubtags(const char* localeID, - icu::ByteSink& sink, - UErrorCode* err); - -U_CAPI const char * U_EXPORT2 -locale_getKeywordsStart(const char *localeID); - -U_CFUNC UBool -ultag_isExtensionSubtags(const char* s, int32_t len); - -U_CFUNC UBool -ultag_isLanguageSubtag(const char* s, int32_t len); - -U_CFUNC UBool -ultag_isPrivateuseValueSubtags(const char* s, int32_t len); - -U_CFUNC UBool -ultag_isRegionSubtag(const char* s, int32_t len); - -U_CFUNC UBool -ultag_isScriptSubtag(const char* s, int32_t len); - -U_CFUNC UBool -ultag_isTransformedExtensionSubtags(const char* s, int32_t len); - -U_CFUNC UBool -ultag_isUnicodeExtensionSubtags(const char* s, int32_t len); - -U_CFUNC UBool -ultag_isUnicodeLocaleAttribute(const char* s, int32_t len); - -U_CFUNC UBool -ultag_isUnicodeLocaleAttributes(const char* s, int32_t len); - -U_CFUNC UBool -ultag_isUnicodeLocaleKey(const char* s, int32_t len); - -U_CFUNC UBool -ultag_isUnicodeLocaleType(const char* s, int32_t len); - -U_CFUNC UBool -ultag_isVariantSubtags(const char* s, int32_t len); - -U_CAPI const char * U_EXPORT2 -ultag_getTKeyStart(const char *localeID); - -U_CFUNC const char* -ulocimp_toBcpKey(const char* key); - -U_CFUNC const char* -ulocimp_toLegacyKey(const char* key); - -U_CFUNC const char* -ulocimp_toBcpType(const char* key, const char* type, UBool* isKnownKey, UBool* isSpecialType); - -U_CFUNC const char* -ulocimp_toLegacyType(const char* key, const char* type, UBool* isKnownKey, UBool* isSpecialType); - -/* Function for testing purpose */ -U_CAPI const char* const* ulocimp_getKnownCanonicalizedLocaleForTest(int32_t* length); - -// Return true if the value is already canonicalized. -U_CAPI bool ulocimp_isCanonicalizedLocaleForTest(const char* localeName); - -/** - * A utility class for handling locale IDs that may be longer than ULOC_FULLNAME_CAPACITY. - * This encompasses all of the logic to allocate a temporary locale ID buffer on the stack, - * and then, if it's not big enough, reallocate it on the heap and try again. - * - * You use it like this: - * UErrorCode err = U_ZERO_ERROR; - * - * PreflightingLocaleIDBuffer tempBuffer; - * do { - * tempBuffer.requestedCapacity = uloc_doSomething(localeID, tempBuffer.getBuffer(), tempBuffer.getCapacity(), &err); - * } while (tempBuffer.needToTryAgain(&err)); - * if (U_SUCCESS(err)) { - * uloc_doSomethingWithTheResult(tempBuffer.getBuffer()); - * } - */ -class PreflightingLocaleIDBuffer { -private: - char stackBuffer[ULOC_FULLNAME_CAPACITY]; - char* heapBuffer = nullptr; - int32_t capacity = ULOC_FULLNAME_CAPACITY; - -public: - int32_t requestedCapacity = ULOC_FULLNAME_CAPACITY; - - // No heap allocation. Use only on the stack. - static void* U_EXPORT2 operator new(size_t) U_NOEXCEPT = delete; - static void* U_EXPORT2 operator new[](size_t) U_NOEXCEPT = delete; -#if U_HAVE_PLACEMENT_NEW - static void* U_EXPORT2 operator new(size_t, void*) U_NOEXCEPT = delete; -#endif - - PreflightingLocaleIDBuffer() {} - - ~PreflightingLocaleIDBuffer() { uprv_free(heapBuffer); } - - char* getBuffer() { - if (heapBuffer == nullptr) { - return stackBuffer; - } else { - return heapBuffer; - } - } - - int32_t getCapacity() { - return capacity; - } - - bool needToTryAgain(UErrorCode* err) { - if (heapBuffer != nullptr) { - return false; - } - - if (*err == U_BUFFER_OVERFLOW_ERROR || *err == U_STRING_NOT_TERMINATED_WARNING) { - int32_t newCapacity = requestedCapacity + 2; // one for the terminating null, one just for paranoia - heapBuffer = static_cast(uprv_malloc(newCapacity)); - if (heapBuffer == nullptr) { - *err = U_MEMORY_ALLOCATION_ERROR; - } else { - *err = U_ZERO_ERROR; - capacity = newCapacity; - } - return U_SUCCESS(*err); - } - return false; - } -}; - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2004-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#ifndef ULOCIMP_H +#define ULOCIMP_H + +#include "unicode/bytestream.h" +#include "unicode/uloc.h" + +#include "charstr.h" + +/** + * Create an iterator over the specified keywords list + * @param keywordList double-null terminated list. Will be copied. + * @param keywordListSize size in bytes of keywordList + * @param status err code + * @return enumeration (owned by caller) of the keyword list. + * @internal ICU 3.0 + */ +U_CAPI UEnumeration* U_EXPORT2 +uloc_openKeywordList(const char *keywordList, int32_t keywordListSize, UErrorCode* status); + +/** + * Look up a resource bundle table item with fallback on the table level. + * This is accessible so it can be called by C++ code. + */ +U_CAPI const UChar * U_EXPORT2 +uloc_getTableStringWithFallback( + const char *path, + const char *locale, + const char *tableKey, + const char *subTableKey, + const char *itemKey, + int32_t *pLength, + UErrorCode *pErrorCode); + +/*returns true if a is an ID separator false otherwise*/ +#define _isIDSeparator(a) (a == '_' || a == '-') + +U_CFUNC const char* +uloc_getCurrentCountryID(const char* oldID); + +U_CFUNC const char* +uloc_getCurrentLanguageID(const char* oldID); + +U_CFUNC void +ulocimp_getKeywords(const char *localeID, + char prev, + icu::ByteSink& sink, + UBool valuesToo, + UErrorCode *status); + +icu::CharString U_EXPORT2 +ulocimp_getLanguage(const char *localeID, + const char **pEnd, + UErrorCode &status); + +icu::CharString U_EXPORT2 +ulocimp_getScript(const char *localeID, + const char **pEnd, + UErrorCode &status); + +icu::CharString U_EXPORT2 +ulocimp_getCountry(const char *localeID, + const char **pEnd, + UErrorCode &status); + +U_CAPI void U_EXPORT2 +ulocimp_getName(const char* localeID, + icu::ByteSink& sink, + UErrorCode* err); + +U_CAPI void U_EXPORT2 +ulocimp_getBaseName(const char* localeID, + icu::ByteSink& sink, + UErrorCode* err); + +U_CAPI void U_EXPORT2 +ulocimp_canonicalize(const char* localeID, + icu::ByteSink& sink, + UErrorCode* err); + +U_CAPI void U_EXPORT2 +ulocimp_getKeywordValue(const char* localeID, + const char* keywordName, + icu::ByteSink& sink, + UErrorCode* status); + +/** + * Writes a well-formed language tag for this locale ID. + * + * **Note**: When `strict` is false, any locale fields which do not satisfy the + * BCP47 syntax requirement will be omitted from the result. When `strict` is + * true, this function sets U_ILLEGAL_ARGUMENT_ERROR to the `err` if any locale + * fields do not satisfy the BCP47 syntax requirement. + * + * @param localeID the input locale ID + * @param sink the output sink receiving the BCP47 language + * tag for this Locale. + * @param strict boolean value indicating if the function returns + * an error for an ill-formed input locale ID. + * @param err error information if receiving the language + * tag failed. + * @return The length of the BCP47 language tag. + * + * @internal ICU 64 + */ +U_CAPI void U_EXPORT2 +ulocimp_toLanguageTag(const char* localeID, + icu::ByteSink& sink, + UBool strict, + UErrorCode* err); + +/** + * Returns a locale ID for the specified BCP47 language tag string. + * If the specified language tag contains any ill-formed subtags, + * the first such subtag and all following subtags are ignored. + *

+ * This implements the 'Language-Tag' production of BCP 47, and so + * supports legacy language tags (marked as “Type: grandfathered” in BCP 47) + * (regular and irregular) as well as private use language tags. + * + * Private use tags are represented as 'x-whatever', + * and legacy tags are converted to their canonical replacements where they exist. + * + * Note that a few legacy tags have no modern replacement; + * these will be converted using the fallback described in + * the first paragraph, so some information might be lost. + * + * @param langtag the input BCP47 language tag. + * @param tagLen the length of langtag, or -1 to call uprv_strlen(). + * @param sink the output sink receiving a locale ID for the + * specified BCP47 language tag. + * @param parsedLength if not NULL, successfully parsed length + * for the input language tag is set. + * @param err error information if receiving the locald ID + * failed. + * @internal ICU 63 + */ +U_CAPI void U_EXPORT2 +ulocimp_forLanguageTag(const char* langtag, + int32_t tagLen, + icu::ByteSink& sink, + int32_t* parsedLength, + UErrorCode* err); + +/** + * Get the region to use for supplemental data lookup. Uses + * (1) any region specified by locale tag "rg"; if none then + * (2) any unicode_region_tag in the locale ID; if none then + * (3) if inferRegion is true, the region suggested by + * getLikelySubtags on the localeID. + * If no region is found, returns length 0. + * + * @param localeID + * The complete locale ID (with keywords) from which + * to get the region to use for supplemental data. + * @param inferRegion + * If true, will try to infer region from localeID if + * no other region is found. + * @param region + * Buffer in which to put the region ID found; should + * have a capacity at least ULOC_COUNTRY_CAPACITY. + * @param regionCapacity + * The actual capacity of the region buffer. + * @param status + * Pointer to in/out UErrorCode value for latest status. + * @return + * The length of any region code found, or 0 if none. + * @internal ICU 57 + */ +U_CAPI int32_t U_EXPORT2 +ulocimp_getRegionForSupplementalData(const char *localeID, UBool inferRegion, + char *region, int32_t regionCapacity, UErrorCode* status); + +/** + * Add the likely subtags for a provided locale ID, per the algorithm described + * in the following CLDR technical report: + * + * http://www.unicode.org/reports/tr35/#Likely_Subtags + * + * If localeID is already in the maximal form, or there is no data available + * for maximization, it will be copied to the output buffer. For example, + * "und-Zzzz" cannot be maximized, since there is no reasonable maximization. + * + * Examples: + * + * "en" maximizes to "en_Latn_US" + * + * "de" maximizes to "de_Latn_US" + * + * "sr" maximizes to "sr_Cyrl_RS" + * + * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.) + * + * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.) + * + * @param localeID The locale to maximize + * @param sink The output sink receiving the maximized locale + * @param err Error information if maximizing the locale failed. If the length + * of the localeID and the null-terminator is greater than the maximum allowed size, + * or the localeId is not well-formed, the error code is U_ILLEGAL_ARGUMENT_ERROR. + * @internal ICU 64 + */ +U_CAPI void U_EXPORT2 +ulocimp_addLikelySubtags(const char* localeID, + icu::ByteSink& sink, + UErrorCode* err); + +/** + * Minimize the subtags for a provided locale ID, per the algorithm described + * in the following CLDR technical report: + * + * http://www.unicode.org/reports/tr35/#Likely_Subtags + * + * If localeID is already in the minimal form, or there is no data available + * for minimization, it will be copied to the output buffer. Since the + * minimization algorithm relies on proper maximization, see the comments + * for ulocimp_addLikelySubtags for reasons why there might not be any data. + * + * Examples: + * + * "en_Latn_US" minimizes to "en" + * + * "de_Latn_US" minimizes to "de" + * + * "sr_Cyrl_RS" minimizes to "sr" + * + * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the + * script, and minimizing to "zh" would imply "zh_Hans_CN".) + * + * @param localeID The locale to minimize + * @param sink The output sink receiving the maximized locale + * @param err Error information if minimizing the locale failed. If the length + * of the localeID and the null-terminator is greater than the maximum allowed size, + * or the localeId is not well-formed, the error code is U_ILLEGAL_ARGUMENT_ERROR. + * @internal ICU 64 + */ +U_CAPI void U_EXPORT2 +ulocimp_minimizeSubtags(const char* localeID, + icu::ByteSink& sink, + UErrorCode* err); + +U_CAPI const char * U_EXPORT2 +locale_getKeywordsStart(const char *localeID); + +U_CFUNC UBool +ultag_isExtensionSubtags(const char* s, int32_t len); + +U_CFUNC UBool +ultag_isLanguageSubtag(const char* s, int32_t len); + +U_CFUNC UBool +ultag_isPrivateuseValueSubtags(const char* s, int32_t len); + +U_CFUNC UBool +ultag_isRegionSubtag(const char* s, int32_t len); + +U_CFUNC UBool +ultag_isScriptSubtag(const char* s, int32_t len); + +U_CFUNC UBool +ultag_isTransformedExtensionSubtags(const char* s, int32_t len); + +U_CFUNC UBool +ultag_isUnicodeExtensionSubtags(const char* s, int32_t len); + +U_CFUNC UBool +ultag_isUnicodeLocaleAttribute(const char* s, int32_t len); + +U_CFUNC UBool +ultag_isUnicodeLocaleAttributes(const char* s, int32_t len); + +U_CFUNC UBool +ultag_isUnicodeLocaleKey(const char* s, int32_t len); + +U_CFUNC UBool +ultag_isUnicodeLocaleType(const char* s, int32_t len); + +U_CFUNC UBool +ultag_isVariantSubtags(const char* s, int32_t len); + +U_CAPI const char * U_EXPORT2 +ultag_getTKeyStart(const char *localeID); + +U_CFUNC const char* +ulocimp_toBcpKey(const char* key); + +U_CFUNC const char* +ulocimp_toLegacyKey(const char* key); + +U_CFUNC const char* +ulocimp_toBcpType(const char* key, const char* type, UBool* isKnownKey, UBool* isSpecialType); + +U_CFUNC const char* +ulocimp_toLegacyType(const char* key, const char* type, UBool* isKnownKey, UBool* isSpecialType); + +/* Function for testing purpose */ +U_CAPI const char* const* ulocimp_getKnownCanonicalizedLocaleForTest(int32_t* length); + +// Return true if the value is already canonicalized. +U_CAPI bool ulocimp_isCanonicalizedLocaleForTest(const char* localeName); + +/** + * A utility class for handling locale IDs that may be longer than ULOC_FULLNAME_CAPACITY. + * This encompasses all of the logic to allocate a temporary locale ID buffer on the stack, + * and then, if it's not big enough, reallocate it on the heap and try again. + * + * You use it like this: + * UErrorCode err = U_ZERO_ERROR; + * + * PreflightingLocaleIDBuffer tempBuffer; + * do { + * tempBuffer.requestedCapacity = uloc_doSomething(localeID, tempBuffer.getBuffer(), tempBuffer.getCapacity(), &err); + * } while (tempBuffer.needToTryAgain(&err)); + * if (U_SUCCESS(err)) { + * uloc_doSomethingWithTheResult(tempBuffer.getBuffer()); + * } + */ +class PreflightingLocaleIDBuffer { +private: + char stackBuffer[ULOC_FULLNAME_CAPACITY]; + char* heapBuffer = nullptr; + int32_t capacity = ULOC_FULLNAME_CAPACITY; + +public: + int32_t requestedCapacity = ULOC_FULLNAME_CAPACITY; + + // No heap allocation. Use only on the stack. + static void* U_EXPORT2 operator new(size_t) noexcept = delete; + static void* U_EXPORT2 operator new[](size_t) noexcept = delete; +#if U_HAVE_PLACEMENT_NEW + static void* U_EXPORT2 operator new(size_t, void*) noexcept = delete; +#endif + + PreflightingLocaleIDBuffer() {} + + ~PreflightingLocaleIDBuffer() { uprv_free(heapBuffer); } + + char* getBuffer() { + if (heapBuffer == nullptr) { + return stackBuffer; + } else { + return heapBuffer; + } + } + + int32_t getCapacity() { + return capacity; + } + + bool needToTryAgain(UErrorCode* err) { + if (heapBuffer != nullptr) { + return false; + } + + if (*err == U_BUFFER_OVERFLOW_ERROR || *err == U_STRING_NOT_TERMINATED_WARNING) { + int32_t newCapacity = requestedCapacity + 2; // one for the terminating null, one just for paranoia + heapBuffer = static_cast(uprv_malloc(newCapacity)); + if (heapBuffer == nullptr) { + *err = U_MEMORY_ALLOCATION_ERROR; + } else { + *err = U_ZERO_ERROR; + capacity = newCapacity; + } + return U_SUCCESS(*err); + } + return false; + } +}; + +#endif diff --git a/deps/icu-small/source/common/umapfile.cpp b/deps/icu-small/source/common/umapfile.cpp index 145582ea97acd4..9a1ec84928d54f 100644 --- a/deps/icu-small/source/common/umapfile.cpp +++ b/deps/icu-small/source/common/umapfile.cpp @@ -1,530 +1,530 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2013, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************/ - - -/*---------------------------------------------------------------------------- - * - * Memory mapped file wrappers for use by the ICU Data Implementation - * All of the platform-specific implementation for mapping data files - * is here. The rest of the ICU Data implementation uses only the - * wrapper functions. - * - *----------------------------------------------------------------------------*/ -/* Defines _XOPEN_SOURCE for access to POSIX functions. - * Must be before any other #includes. */ -#include "uposixdefs.h" - -#include "unicode/putil.h" -#include "unicode/ustring.h" -#include "udatamem.h" -#include "umapfile.h" - -/* memory-mapping base definitions ------------------------------------------ */ - -#if MAP_IMPLEMENTATION==MAP_WIN32 -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -# define VC_EXTRALEAN -# define NOUSER -# define NOSERVICE -# define NOIME -# define NOMCX - -# if U_PLATFORM_HAS_WINUWP_API == 1 - // Some previous versions of the Windows 10 SDK don't expose various APIs for UWP applications - // to use, even though UWP apps are allowed to call and use them. Temporarily change the - // WINAPI family partition below to Desktop, so that function declarations are visible for UWP. -# include -# if !(WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)) -# pragma push_macro("WINAPI_PARTITION_DESKTOP") -# undef WINAPI_PARTITION_DESKTOP -# define WINAPI_PARTITION_DESKTOP 1 -# define CHANGED_WINAPI_PARTITION_DESKTOP_VALUE -# endif -# endif - -# include - -# if U_PLATFORM_HAS_WINUWP_API == 1 && defined(CHANGED_WINAPI_PARTITION_DESKTOP_VALUE) -# pragma pop_macro("WINAPI_PARTITION_DESKTOP") -# endif - -# include "cmemory.h" - -typedef HANDLE MemoryMap; - -# define IS_MAP(map) ((map)!=nullptr) - -#elif MAP_IMPLEMENTATION==MAP_POSIX || MAP_IMPLEMENTATION==MAP_390DLL - typedef size_t MemoryMap; - -# define IS_MAP(map) ((map)!=0) - -# include -# include -# include -# include - -# ifndef MAP_FAILED -# define MAP_FAILED ((void*)-1) -# endif - -# if MAP_IMPLEMENTATION==MAP_390DLL - /* No memory mapping for 390 batch mode. Fake it using dll loading. */ -# include -# include "cstring.h" -# include "cmemory.h" -# include "unicode/udata.h" -# define LIB_PREFIX "lib" -# define LIB_SUFFIX ".dll" - /* This is inconvenient until we figure out what to do with U_ICUDATA_NAME in utypes.h */ -# define U_ICUDATA_ENTRY_NAME "icudt" U_ICU_VERSION_SHORT U_LIB_SUFFIX_C_NAME_STRING "_dat" -# endif -#elif MAP_IMPLEMENTATION==MAP_STDIO -# include -# include "cmemory.h" - - typedef void *MemoryMap; - -# define IS_MAP(map) ((map)!=nullptr) -#endif - -/*----------------------------------------------------------------------------* - * * - * Memory Mapped File support. Platform dependent implementation of * - * functions used by the rest of the implementation.* - * * - *----------------------------------------------------------------------------*/ -#if MAP_IMPLEMENTATION==MAP_NONE - U_CFUNC UBool - uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) { - if (U_FAILURE(*status)) { - return false; - } - UDataMemory_init(pData); /* Clear the output struct. */ - return false; /* no file access */ - } - - U_CFUNC void uprv_unmapFile(UDataMemory *pData) { - /* nothing to do */ - } -#elif MAP_IMPLEMENTATION==MAP_WIN32 - U_CFUNC UBool - uprv_mapFile( - UDataMemory *pData, /* Fill in with info on the result doing the mapping. */ - /* Output only; any original contents are cleared. */ - const char *path, /* File path to be opened/mapped. */ - UErrorCode *status /* Error status, used to report out-of-memory errors. */ - ) - { - if (U_FAILURE(*status)) { - return false; - } - - HANDLE map = nullptr; - HANDLE file = INVALID_HANDLE_VALUE; - - UDataMemory_init(pData); /* Clear the output struct. */ - - /* open the input file */ -#if U_PLATFORM_HAS_WINUWP_API == 0 - // Note: In the non-UWP code-path (ie: Win32), the value of the path variable might have come from - // the CRT 'getenv' function, and would be therefore be encoded in the default ANSI code page. - // This means that we can't call the *W version of API below, whereas in the UWP code-path - // there is no 'getenv' call, and thus the string will be only UTF-8/Invariant characters. - file=CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL|FILE_FLAG_RANDOM_ACCESS, nullptr); -#else - // Convert from UTF-8 string to UTF-16 string. - wchar_t utf16Path[MAX_PATH]; - int32_t pathUtf16Len = 0; - u_strFromUTF8(reinterpret_cast(utf16Path), static_cast(UPRV_LENGTHOF(utf16Path)), &pathUtf16Len, path, -1, status); - - if (U_FAILURE(*status)) { - return false; - } - if (*status == U_STRING_NOT_TERMINATED_WARNING) { - // Report back an error instead of a warning. - *status = U_BUFFER_OVERFLOW_ERROR; - return false; - } - - file = CreateFileW(utf16Path, GENERIC_READ, FILE_SHARE_READ, nullptr, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr); -#endif - if (file == INVALID_HANDLE_VALUE) { - // If we failed to open the file due to an out-of-memory error, then we want - // to report that error back to the caller. - if (HRESULT_FROM_WIN32(GetLastError()) == E_OUTOFMEMORY) { - *status = U_MEMORY_ALLOCATION_ERROR; - } - return false; - } - - // Note: We use NULL/nullptr for lpAttributes parameter below. - // This means our handle cannot be inherited and we will get the default security descriptor. - /* create an unnamed Windows file-mapping object for the specified file */ - map = CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, nullptr); - - CloseHandle(file); - if (map == nullptr) { - // If we failed to create the mapping due to an out-of-memory error, then - // we want to report that error back to the caller. - if (HRESULT_FROM_WIN32(GetLastError()) == E_OUTOFMEMORY) { - *status = U_MEMORY_ALLOCATION_ERROR; - } - return false; - } - - /* map a view of the file into our address space */ - pData->pHeader = reinterpret_cast(MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0)); - if (pData->pHeader == nullptr) { - CloseHandle(map); - return false; - } - pData->map = map; - return true; - } - - U_CFUNC void - uprv_unmapFile(UDataMemory *pData) { - if (pData != nullptr && pData->map != nullptr) { - UnmapViewOfFile(pData->pHeader); - CloseHandle(pData->map); - pData->pHeader = nullptr; - pData->map = nullptr; - } - } - - - -#elif MAP_IMPLEMENTATION==MAP_POSIX - U_CFUNC UBool - uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) { - int fd; - int length; - struct stat mystat; - void *data; - - if (U_FAILURE(*status)) { - return false; - } - - UDataMemory_init(pData); /* Clear the output struct. */ - - /* determine the length of the file */ - if(stat(path, &mystat)!=0 || mystat.st_size<=0) { - return false; - } - length=mystat.st_size; - - /* open the file */ - fd=open(path, O_RDONLY); - if(fd==-1) { - return false; - } - - /* get a view of the mapping */ -#if U_PLATFORM != U_PF_HPUX - data=mmap(0, length, PROT_READ, MAP_SHARED, fd, 0); -#else - data=mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0); -#endif - close(fd); /* no longer needed */ - if(data==MAP_FAILED) { - // Possibly check the errno value for ENOMEM, and report U_MEMORY_ALLOCATION_ERROR? - return false; - } - - pData->map = (char *)data + length; - pData->pHeader=(const DataHeader *)data; - pData->mapAddr = data; -#if U_PLATFORM == U_PF_IPHONE - posix_madvise(data, length, POSIX_MADV_RANDOM); -#endif - return true; - } - - U_CFUNC void - uprv_unmapFile(UDataMemory *pData) { - if(pData!=nullptr && pData->map!=nullptr) { - size_t dataLen = (char *)pData->map - (char *)pData->mapAddr; - if(munmap(pData->mapAddr, dataLen)==-1) { - } - pData->pHeader=nullptr; - pData->map=0; - pData->mapAddr=nullptr; - } - } - - - -#elif MAP_IMPLEMENTATION==MAP_STDIO - /* copy of the filestrm.c/T_FileStream_size() implementation */ - static int32_t - umap_fsize(FILE *f) { - int32_t savedPos = ftell(f); - int32_t size = 0; - - /*Changes by Bertrand A. D. doesn't affect the current position - goes to the end of the file before ftell*/ - fseek(f, 0, SEEK_END); - size = (int32_t)ftell(f); - fseek(f, savedPos, SEEK_SET); - return size; - } - - U_CFUNC UBool - uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) { - FILE *file; - int32_t fileLength; - void *p; - - if (U_FAILURE(*status)) { - return false; - } - - UDataMemory_init(pData); /* Clear the output struct. */ - /* open the input file */ - file=fopen(path, "rb"); - if(file==nullptr) { - return false; - } - - /* get the file length */ - fileLength=umap_fsize(file); - if(ferror(file) || fileLength<=20) { - fclose(file); - return false; - } - - /* allocate the memory to hold the file data */ - p=uprv_malloc(fileLength); - if(p==nullptr) { - fclose(file); - *status = U_MEMORY_ALLOCATION_ERROR; - return false; - } - - /* read the file */ - if(fileLength!=fread(p, 1, fileLength, file)) { - uprv_free(p); - fclose(file); - return false; - } - - fclose(file); - pData->map=p; - pData->pHeader=(const DataHeader *)p; - pData->mapAddr=p; - return true; - } - - U_CFUNC void - uprv_unmapFile(UDataMemory *pData) { - if(pData!=nullptr && pData->map!=nullptr) { - uprv_free(pData->map); - pData->map = nullptr; - pData->mapAddr = nullptr; - pData->pHeader = nullptr; - } - } - - -#elif MAP_IMPLEMENTATION==MAP_390DLL - /* 390 specific Library Loading. - * This is the only platform left that dynamically loads an ICU Data Library. - * All other platforms use .data files when dynamic loading is required, but - * this turn out to be awkward to support in 390 batch mode. - * - * The idea here is to hide the fact that 390 is using dll loading from the - * rest of ICU, and make it look like there is file loading happening. - * - */ - - static char *strcpy_returnEnd(char *dest, const char *src) - { - while((*dest=*src)!=0) { - ++dest; - ++src; - } - return dest; - } - - /*------------------------------------------------------------------------------ - * - * computeDirPath given a user-supplied path of an item to be opened, - * compute and return - * - the full directory path to be used - * when opening the file. - * - Pointer to null at end of above returned path - * - * Parameters: - * path: input path. Buffer is not altered. - * pathBuffer: Output buffer. Any contents are overwritten. - * - * Returns: - * Pointer to null termination in returned pathBuffer. - * - * TODO: This works the way ICU historically has, but the - * whole data fallback search path is so complicated that - * probably almost no one will ever really understand it, - * the potential for confusion is large. (It's not just - * this one function, but the whole scheme.) - * - *------------------------------------------------------------------------------*/ - static char *uprv_computeDirPath(const char *path, char *pathBuffer) - { - char *finalSlash; /* Ptr to last dir separator in input path, or null if none. */ - int32_t pathLen; /* Length of the returned directory path */ - - finalSlash = 0; - if (path != 0) { - finalSlash = uprv_strrchr(path, U_FILE_SEP_CHAR); - } - - *pathBuffer = 0; - if (finalSlash == 0) { - /* No user-supplied path. - * Copy the ICU_DATA path to the path buffer and return that*/ - const char *icuDataDir; - icuDataDir=u_getDataDirectory(); - if(icuDataDir!=nullptr && *icuDataDir!=0) { - return strcpy_returnEnd(pathBuffer, icuDataDir); - } else { - /* there is no icuDataDir either. Just return the empty pathBuffer. */ - return pathBuffer; - } - } - - /* User supplied path did contain a directory portion. - * Copy it to the output path buffer */ - pathLen = (int32_t)(finalSlash - path + 1); - uprv_memcpy(pathBuffer, path, pathLen); - *(pathBuffer+pathLen) = 0; - return pathBuffer+pathLen; - } - - -# define DATA_TYPE "dat" - - U_CFUNC UBool uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) { - const char *inBasename; - char *basename; - char pathBuffer[1024]; - const DataHeader *pHeader; - dllhandle *handle; - void *val=0; - - if (U_FAILURE(*status)) { - return false; - } - - inBasename=uprv_strrchr(path, U_FILE_SEP_CHAR); - if(inBasename==nullptr) { - inBasename = path; - } else { - inBasename++; - } - basename=uprv_computeDirPath(path, pathBuffer); - if(uprv_strcmp(inBasename, U_ICUDATA_NAME".dat") != 0) { - /* must mmap file... for build */ - int fd; - int length; - struct stat mystat; - void *data; - UDataMemory_init(pData); /* Clear the output struct. */ - - /* determine the length of the file */ - if(stat(path, &mystat)!=0 || mystat.st_size<=0) { - return false; - } - length=mystat.st_size; - - /* open the file */ - fd=open(path, O_RDONLY); - if(fd==-1) { - return false; - } - - /* get a view of the mapping */ - data=mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0); - close(fd); /* no longer needed */ - if(data==MAP_FAILED) { - // Possibly check the errorno value for ENOMEM, and report U_MEMORY_ALLOCATION_ERROR? - return false; - } - pData->map = (char *)data + length; - pData->pHeader=(const DataHeader *)data; - pData->mapAddr = data; - return true; - } - -# ifdef OS390BATCH - /* ### hack: we still need to get u_getDataDirectory() fixed - for OS/390 (batch mode - always return "//"? ) - and this here straightened out with LIB_PREFIX and LIB_SUFFIX (both empty?!) - This is probably due to the strange file system on OS/390. It's more like - a database with short entry names than a typical file system. */ - /* U_ICUDATA_NAME should always have the correct name */ - /* BUT FOR BATCH MODE IT IS AN EXCEPTION BECAUSE */ - /* THE FIRST THREE LETTERS ARE PREASSIGNED TO THE */ - /* PROJECT!!!!! */ - uprv_strcpy(pathBuffer, "IXMI" U_ICU_VERSION_SHORT "DA"); -# else - /* set up the library name */ - uprv_strcpy(basename, LIB_PREFIX U_LIBICUDATA_NAME U_ICU_VERSION_SHORT LIB_SUFFIX); -# endif - -# ifdef UDATA_DEBUG - fprintf(stderr, "dllload: %s ", pathBuffer); -# endif - - handle=dllload(pathBuffer); - -# ifdef UDATA_DEBUG - fprintf(stderr, " -> %08X\n", handle ); -# endif - - if(handle != nullptr) { - /* we have a data DLL - what kind of lookup do we need here? */ - /* try to find the Table of Contents */ - UDataMemory_init(pData); /* Clear the output struct. */ - val=dllqueryvar((dllhandle*)handle, U_ICUDATA_ENTRY_NAME); - if(val == 0) { - /* failed... so keep looking */ - return false; - } -# ifdef UDATA_DEBUG - fprintf(stderr, "dllqueryvar(%08X, %s) -> %08X\n", handle, U_ICUDATA_ENTRY_NAME, val); -# endif - - pData->pHeader=(const DataHeader *)val; - return true; - } else { - return false; /* no handle */ - } - } - - U_CFUNC void uprv_unmapFile(UDataMemory *pData) { - if(pData!=nullptr && pData->map!=nullptr) { - uprv_free(pData->map); - pData->map = nullptr; - pData->mapAddr = nullptr; - pData->pHeader = nullptr; - } - } - -#else -# error MAP_IMPLEMENTATION is set incorrectly -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2013, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************/ + + +/*---------------------------------------------------------------------------- + * + * Memory mapped file wrappers for use by the ICU Data Implementation + * All of the platform-specific implementation for mapping data files + * is here. The rest of the ICU Data implementation uses only the + * wrapper functions. + * + *----------------------------------------------------------------------------*/ +/* Defines _XOPEN_SOURCE for access to POSIX functions. + * Must be before any other #includes. */ +#include "uposixdefs.h" + +#include "unicode/putil.h" +#include "unicode/ustring.h" +#include "udatamem.h" +#include "umapfile.h" + +/* memory-mapping base definitions ------------------------------------------ */ + +#if MAP_IMPLEMENTATION==MAP_WIN32 +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +# define VC_EXTRALEAN +# define NOUSER +# define NOSERVICE +# define NOIME +# define NOMCX + +# if U_PLATFORM_HAS_WINUWP_API == 1 + // Some previous versions of the Windows 10 SDK don't expose various APIs for UWP applications + // to use, even though UWP apps are allowed to call and use them. Temporarily change the + // WINAPI family partition below to Desktop, so that function declarations are visible for UWP. +# include +# if !(WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)) +# pragma push_macro("WINAPI_PARTITION_DESKTOP") +# undef WINAPI_PARTITION_DESKTOP +# define WINAPI_PARTITION_DESKTOP 1 +# define CHANGED_WINAPI_PARTITION_DESKTOP_VALUE +# endif +# endif + +# include + +# if U_PLATFORM_HAS_WINUWP_API == 1 && defined(CHANGED_WINAPI_PARTITION_DESKTOP_VALUE) +# pragma pop_macro("WINAPI_PARTITION_DESKTOP") +# endif + +# include "cmemory.h" + +typedef HANDLE MemoryMap; + +# define IS_MAP(map) ((map)!=nullptr) + +#elif MAP_IMPLEMENTATION==MAP_POSIX || MAP_IMPLEMENTATION==MAP_390DLL + typedef size_t MemoryMap; + +# define IS_MAP(map) ((map)!=0) + +# include +# include +# include +# include + +# ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +# endif + +# if MAP_IMPLEMENTATION==MAP_390DLL + /* No memory mapping for 390 batch mode. Fake it using dll loading. */ +# include +# include "cstring.h" +# include "cmemory.h" +# include "unicode/udata.h" +# define LIB_PREFIX "lib" +# define LIB_SUFFIX ".dll" + /* This is inconvenient until we figure out what to do with U_ICUDATA_NAME in utypes.h */ +# define U_ICUDATA_ENTRY_NAME "icudt" U_ICU_VERSION_SHORT U_LIB_SUFFIX_C_NAME_STRING "_dat" +# endif +#elif MAP_IMPLEMENTATION==MAP_STDIO +# include +# include "cmemory.h" + + typedef void *MemoryMap; + +# define IS_MAP(map) ((map)!=nullptr) +#endif + +/*----------------------------------------------------------------------------* + * * + * Memory Mapped File support. Platform dependent implementation of * + * functions used by the rest of the implementation.* + * * + *----------------------------------------------------------------------------*/ +#if MAP_IMPLEMENTATION==MAP_NONE + U_CFUNC UBool + uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) { + if (U_FAILURE(*status)) { + return false; + } + UDataMemory_init(pData); /* Clear the output struct. */ + return false; /* no file access */ + } + + U_CFUNC void uprv_unmapFile(UDataMemory *pData) { + /* nothing to do */ + } +#elif MAP_IMPLEMENTATION==MAP_WIN32 + U_CFUNC UBool + uprv_mapFile( + UDataMemory *pData, /* Fill in with info on the result doing the mapping. */ + /* Output only; any original contents are cleared. */ + const char *path, /* File path to be opened/mapped. */ + UErrorCode *status /* Error status, used to report out-of-memory errors. */ + ) + { + if (U_FAILURE(*status)) { + return false; + } + + HANDLE map = nullptr; + HANDLE file = INVALID_HANDLE_VALUE; + + UDataMemory_init(pData); /* Clear the output struct. */ + + /* open the input file */ +#if U_PLATFORM_HAS_WINUWP_API == 0 + // Note: In the non-UWP code-path (ie: Win32), the value of the path variable might have come from + // the CRT 'getenv' function, and would be therefore be encoded in the default ANSI code page. + // This means that we can't call the *W version of API below, whereas in the UWP code-path + // there is no 'getenv' call, and thus the string will be only UTF-8/Invariant characters. + file=CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_RANDOM_ACCESS, nullptr); +#else + // Convert from UTF-8 string to UTF-16 string. + wchar_t utf16Path[MAX_PATH]; + int32_t pathUtf16Len = 0; + u_strFromUTF8(reinterpret_cast(utf16Path), static_cast(UPRV_LENGTHOF(utf16Path)), &pathUtf16Len, path, -1, status); + + if (U_FAILURE(*status)) { + return false; + } + if (*status == U_STRING_NOT_TERMINATED_WARNING) { + // Report back an error instead of a warning. + *status = U_BUFFER_OVERFLOW_ERROR; + return false; + } + + file = CreateFileW(utf16Path, GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr); +#endif + if (file == INVALID_HANDLE_VALUE) { + // If we failed to open the file due to an out-of-memory error, then we want + // to report that error back to the caller. + if (HRESULT_FROM_WIN32(GetLastError()) == E_OUTOFMEMORY) { + *status = U_MEMORY_ALLOCATION_ERROR; + } + return false; + } + + // Note: We use nullptr/nullptr for lpAttributes parameter below. + // This means our handle cannot be inherited and we will get the default security descriptor. + /* create an unnamed Windows file-mapping object for the specified file */ + map = CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, nullptr); + + CloseHandle(file); + if (map == nullptr) { + // If we failed to create the mapping due to an out-of-memory error, then + // we want to report that error back to the caller. + if (HRESULT_FROM_WIN32(GetLastError()) == E_OUTOFMEMORY) { + *status = U_MEMORY_ALLOCATION_ERROR; + } + return false; + } + + /* map a view of the file into our address space */ + pData->pHeader = reinterpret_cast(MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0)); + if (pData->pHeader == nullptr) { + CloseHandle(map); + return false; + } + pData->map = map; + return true; + } + + U_CFUNC void + uprv_unmapFile(UDataMemory *pData) { + if (pData != nullptr && pData->map != nullptr) { + UnmapViewOfFile(pData->pHeader); + CloseHandle(pData->map); + pData->pHeader = nullptr; + pData->map = nullptr; + } + } + + + +#elif MAP_IMPLEMENTATION==MAP_POSIX + U_CFUNC UBool + uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) { + int fd; + int length; + struct stat mystat; + void *data; + + if (U_FAILURE(*status)) { + return false; + } + + UDataMemory_init(pData); /* Clear the output struct. */ + + /* determine the length of the file */ + if(stat(path, &mystat)!=0 || mystat.st_size<=0) { + return false; + } + length=mystat.st_size; + + /* open the file */ + fd=open(path, O_RDONLY); + if(fd==-1) { + return false; + } + + /* get a view of the mapping */ +#if U_PLATFORM != U_PF_HPUX + data=mmap(0, length, PROT_READ, MAP_SHARED, fd, 0); +#else + data=mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0); +#endif + close(fd); /* no longer needed */ + if(data==MAP_FAILED) { + // Possibly check the errno value for ENOMEM, and report U_MEMORY_ALLOCATION_ERROR? + return false; + } + + pData->map = (char *)data + length; + pData->pHeader=(const DataHeader *)data; + pData->mapAddr = data; +#if U_PLATFORM == U_PF_IPHONE + posix_madvise(data, length, POSIX_MADV_RANDOM); +#endif + return true; + } + + U_CFUNC void + uprv_unmapFile(UDataMemory *pData) { + if(pData!=nullptr && pData->map!=nullptr) { + size_t dataLen = (char *)pData->map - (char *)pData->mapAddr; + if(munmap(pData->mapAddr, dataLen)==-1) { + } + pData->pHeader=nullptr; + pData->map=0; + pData->mapAddr=nullptr; + } + } + + + +#elif MAP_IMPLEMENTATION==MAP_STDIO + /* copy of the filestrm.c/T_FileStream_size() implementation */ + static int32_t + umap_fsize(FILE *f) { + int32_t savedPos = ftell(f); + int32_t size = 0; + + /*Changes by Bertrand A. D. doesn't affect the current position + goes to the end of the file before ftell*/ + fseek(f, 0, SEEK_END); + size = (int32_t)ftell(f); + fseek(f, savedPos, SEEK_SET); + return size; + } + + U_CFUNC UBool + uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) { + FILE *file; + int32_t fileLength; + void *p; + + if (U_FAILURE(*status)) { + return false; + } + + UDataMemory_init(pData); /* Clear the output struct. */ + /* open the input file */ + file=fopen(path, "rb"); + if(file==nullptr) { + return false; + } + + /* get the file length */ + fileLength=umap_fsize(file); + if(ferror(file) || fileLength<=20) { + fclose(file); + return false; + } + + /* allocate the memory to hold the file data */ + p=uprv_malloc(fileLength); + if(p==nullptr) { + fclose(file); + *status = U_MEMORY_ALLOCATION_ERROR; + return false; + } + + /* read the file */ + if(fileLength!=fread(p, 1, fileLength, file)) { + uprv_free(p); + fclose(file); + return false; + } + + fclose(file); + pData->map=p; + pData->pHeader=(const DataHeader *)p; + pData->mapAddr=p; + return true; + } + + U_CFUNC void + uprv_unmapFile(UDataMemory *pData) { + if(pData!=nullptr && pData->map!=nullptr) { + uprv_free(pData->map); + pData->map = nullptr; + pData->mapAddr = nullptr; + pData->pHeader = nullptr; + } + } + + +#elif MAP_IMPLEMENTATION==MAP_390DLL + /* 390 specific Library Loading. + * This is the only platform left that dynamically loads an ICU Data Library. + * All other platforms use .data files when dynamic loading is required, but + * this turn out to be awkward to support in 390 batch mode. + * + * The idea here is to hide the fact that 390 is using dll loading from the + * rest of ICU, and make it look like there is file loading happening. + * + */ + + static char *strcpy_returnEnd(char *dest, const char *src) + { + while((*dest=*src)!=0) { + ++dest; + ++src; + } + return dest; + } + + /*------------------------------------------------------------------------------ + * + * computeDirPath given a user-supplied path of an item to be opened, + * compute and return + * - the full directory path to be used + * when opening the file. + * - Pointer to null at end of above returned path + * + * Parameters: + * path: input path. Buffer is not altered. + * pathBuffer: Output buffer. Any contents are overwritten. + * + * Returns: + * Pointer to null termination in returned pathBuffer. + * + * TODO: This works the way ICU historically has, but the + * whole data fallback search path is so complicated that + * probably almost no one will ever really understand it, + * the potential for confusion is large. (It's not just + * this one function, but the whole scheme.) + * + *------------------------------------------------------------------------------*/ + static char *uprv_computeDirPath(const char *path, char *pathBuffer) + { + char *finalSlash; /* Ptr to last dir separator in input path, or null if none. */ + int32_t pathLen; /* Length of the returned directory path */ + + finalSlash = 0; + if (path != 0) { + finalSlash = uprv_strrchr(path, U_FILE_SEP_CHAR); + } + + *pathBuffer = 0; + if (finalSlash == 0) { + /* No user-supplied path. + * Copy the ICU_DATA path to the path buffer and return that*/ + const char *icuDataDir; + icuDataDir=u_getDataDirectory(); + if(icuDataDir!=nullptr && *icuDataDir!=0) { + return strcpy_returnEnd(pathBuffer, icuDataDir); + } else { + /* there is no icuDataDir either. Just return the empty pathBuffer. */ + return pathBuffer; + } + } + + /* User supplied path did contain a directory portion. + * Copy it to the output path buffer */ + pathLen = (int32_t)(finalSlash - path + 1); + uprv_memcpy(pathBuffer, path, pathLen); + *(pathBuffer+pathLen) = 0; + return pathBuffer+pathLen; + } + + +# define DATA_TYPE "dat" + + U_CFUNC UBool uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) { + const char *inBasename; + char *basename; + char pathBuffer[1024]; + const DataHeader *pHeader; + dllhandle *handle; + void *val=0; + + if (U_FAILURE(*status)) { + return false; + } + + inBasename=uprv_strrchr(path, U_FILE_SEP_CHAR); + if(inBasename==nullptr) { + inBasename = path; + } else { + inBasename++; + } + basename=uprv_computeDirPath(path, pathBuffer); + if(uprv_strcmp(inBasename, U_ICUDATA_NAME".dat") != 0) { + /* must mmap file... for build */ + int fd; + int length; + struct stat mystat; + void *data; + UDataMemory_init(pData); /* Clear the output struct. */ + + /* determine the length of the file */ + if(stat(path, &mystat)!=0 || mystat.st_size<=0) { + return false; + } + length=mystat.st_size; + + /* open the file */ + fd=open(path, O_RDONLY); + if(fd==-1) { + return false; + } + + /* get a view of the mapping */ + data=mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); /* no longer needed */ + if(data==MAP_FAILED) { + // Possibly check the errorno value for ENOMEM, and report U_MEMORY_ALLOCATION_ERROR? + return false; + } + pData->map = (char *)data + length; + pData->pHeader=(const DataHeader *)data; + pData->mapAddr = data; + return true; + } + +# ifdef OS390BATCH + /* ### hack: we still need to get u_getDataDirectory() fixed + for OS/390 (batch mode - always return "//"? ) + and this here straightened out with LIB_PREFIX and LIB_SUFFIX (both empty?!) + This is probably due to the strange file system on OS/390. It's more like + a database with short entry names than a typical file system. */ + /* U_ICUDATA_NAME should always have the correct name */ + /* BUT FOR BATCH MODE IT IS AN EXCEPTION BECAUSE */ + /* THE FIRST THREE LETTERS ARE PREASSIGNED TO THE */ + /* PROJECT!!!!! */ + uprv_strcpy(pathBuffer, "IXMI" U_ICU_VERSION_SHORT "DA"); +# else + /* set up the library name */ + uprv_strcpy(basename, LIB_PREFIX U_LIBICUDATA_NAME U_ICU_VERSION_SHORT LIB_SUFFIX); +# endif + +# ifdef UDATA_DEBUG + fprintf(stderr, "dllload: %s ", pathBuffer); +# endif + + handle=dllload(pathBuffer); + +# ifdef UDATA_DEBUG + fprintf(stderr, " -> %08X\n", handle ); +# endif + + if(handle != nullptr) { + /* we have a data DLL - what kind of lookup do we need here? */ + /* try to find the Table of Contents */ + UDataMemory_init(pData); /* Clear the output struct. */ + val=dllqueryvar((dllhandle*)handle, U_ICUDATA_ENTRY_NAME); + if(val == 0) { + /* failed... so keep looking */ + return false; + } +# ifdef UDATA_DEBUG + fprintf(stderr, "dllqueryvar(%08X, %s) -> %08X\n", handle, U_ICUDATA_ENTRY_NAME, val); +# endif + + pData->pHeader=(const DataHeader *)val; + return true; + } else { + return false; /* no handle */ + } + } + + U_CFUNC void uprv_unmapFile(UDataMemory *pData) { + if(pData!=nullptr && pData->map!=nullptr) { + uprv_free(pData->map); + pData->map = nullptr; + pData->mapAddr = nullptr; + pData->pHeader = nullptr; + } + } + +#else +# error MAP_IMPLEMENTATION is set incorrectly +#endif diff --git a/deps/icu-small/source/common/umapfile.h b/deps/icu-small/source/common/umapfile.h index 042e71374c1f59..90e613d1384e85 100644 --- a/deps/icu-small/source/common/umapfile.h +++ b/deps/icu-small/source/common/umapfile.h @@ -1,57 +1,57 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************/ - -/*---------------------------------------------------------------------------------- - * - * Memory mapped file wrappers for use by the ICU Data Implementation - * - * Porting note: The implementation of these functions is very platform specific. - * Not all platforms can do real memory mapping. Those that can't - * still must implement these functions, getting the data into memory using - * whatever means are available. - * - * These functions are part of the ICU internal implementation, and - * are not intended to be used directly by applications. - * - *----------------------------------------------------------------------------------*/ - -#ifndef __UMAPFILE_H__ -#define __UMAPFILE_H__ - -#include "unicode/putil.h" -#include "unicode/udata.h" -#include "putilimp.h" - -U_CAPI UBool U_EXPORT2 uprv_mapFile(UDataMemory *pdm, const char *path, UErrorCode *status); -U_CFUNC void uprv_unmapFile(UDataMemory *pData); - -/* MAP_NONE: no memory mapping, no file access at all */ -#define MAP_NONE 0 -#define MAP_WIN32 1 -#define MAP_POSIX 2 -#define MAP_STDIO 3 -#define MAP_390DLL 4 - -#if UCONFIG_NO_FILE_IO -# define MAP_IMPLEMENTATION MAP_NONE -#elif U_PLATFORM_USES_ONLY_WIN32_API -# define MAP_IMPLEMENTATION MAP_WIN32 -#elif U_HAVE_MMAP || U_PLATFORM == U_PF_OS390 -# if U_PLATFORM == U_PF_OS390 && defined (OS390_STUBDATA) - /* No memory mapping for 390 batch mode. Fake it using dll loading. */ -# define MAP_IMPLEMENTATION MAP_390DLL -# else -# define MAP_IMPLEMENTATION MAP_POSIX -# endif -#else /* unknown platform, no memory map implementation: use stdio.h and uprv_malloc() instead */ -# define MAP_IMPLEMENTATION MAP_STDIO -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************/ + +/*---------------------------------------------------------------------------------- + * + * Memory mapped file wrappers for use by the ICU Data Implementation + * + * Porting note: The implementation of these functions is very platform specific. + * Not all platforms can do real memory mapping. Those that can't + * still must implement these functions, getting the data into memory using + * whatever means are available. + * + * These functions are part of the ICU internal implementation, and + * are not intended to be used directly by applications. + * + *----------------------------------------------------------------------------------*/ + +#ifndef __UMAPFILE_H__ +#define __UMAPFILE_H__ + +#include "unicode/putil.h" +#include "unicode/udata.h" +#include "putilimp.h" + +U_CAPI UBool U_EXPORT2 uprv_mapFile(UDataMemory *pdm, const char *path, UErrorCode *status); +U_CFUNC void uprv_unmapFile(UDataMemory *pData); + +/* MAP_NONE: no memory mapping, no file access at all */ +#define MAP_NONE 0 +#define MAP_WIN32 1 +#define MAP_POSIX 2 +#define MAP_STDIO 3 +#define MAP_390DLL 4 + +#if UCONFIG_NO_FILE_IO +# define MAP_IMPLEMENTATION MAP_NONE +#elif U_PLATFORM_USES_ONLY_WIN32_API +# define MAP_IMPLEMENTATION MAP_WIN32 +#elif U_HAVE_MMAP || U_PLATFORM == U_PF_OS390 +# if U_PLATFORM == U_PF_OS390 && defined (OS390_STUBDATA) + /* No memory mapping for 390 batch mode. Fake it using dll loading. */ +# define MAP_IMPLEMENTATION MAP_390DLL +# else +# define MAP_IMPLEMENTATION MAP_POSIX +# endif +#else /* unknown platform, no memory map implementation: use stdio.h and uprv_malloc() instead */ +# define MAP_IMPLEMENTATION MAP_STDIO +#endif + +#endif diff --git a/deps/icu-small/source/common/umath.cpp b/deps/icu-small/source/common/umath.cpp index 7cf4b317494083..0718e282a079de 100644 --- a/deps/icu-small/source/common/umath.cpp +++ b/deps/icu-small/source/common/umath.cpp @@ -1,26 +1,26 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1997-2006, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* This file contains platform independent math. -*/ - -#include "putilimp.h" - -U_CAPI int32_t U_EXPORT2 -uprv_max(int32_t x, int32_t y) -{ - return (x > y ? x : y); -} - -U_CAPI int32_t U_EXPORT2 -uprv_min(int32_t x, int32_t y) -{ - return (x > y ? y : x); -} - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1997-2006, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* This file contains platform independent math. +*/ + +#include "putilimp.h" + +U_CAPI int32_t U_EXPORT2 +uprv_max(int32_t x, int32_t y) +{ + return (x > y ? x : y); +} + +U_CAPI int32_t U_EXPORT2 +uprv_min(int32_t x, int32_t y) +{ + return (x > y ? y : x); +} + diff --git a/deps/icu-small/source/common/umutablecptrie.cpp b/deps/icu-small/source/common/umutablecptrie.cpp index cdbe27080b491c..b6d58585bf50dc 100644 --- a/deps/icu-small/source/common/umutablecptrie.cpp +++ b/deps/icu-small/source/common/umutablecptrie.cpp @@ -1,1852 +1,1852 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// umutablecptrie.cpp (inspired by utrie2_builder.cpp) -// created: 2017dec29 Markus W. Scherer - -// #define UCPTRIE_DEBUG -#ifdef UCPTRIE_DEBUG -# include -#endif - -#include "unicode/utypes.h" -#include "unicode/ucptrie.h" -#include "unicode/umutablecptrie.h" -#include "unicode/uobject.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "uassert.h" -#include "ucptrie_impl.h" - -// ICU-20235 In case Microsoft math.h has defined this, undefine it. -#ifdef OVERFLOW -#undef OVERFLOW -#endif - -U_NAMESPACE_BEGIN - -namespace { - -constexpr int32_t MAX_UNICODE = 0x10ffff; - -constexpr int32_t UNICODE_LIMIT = 0x110000; -constexpr int32_t BMP_LIMIT = 0x10000; -constexpr int32_t ASCII_LIMIT = 0x80; - -constexpr int32_t I_LIMIT = UNICODE_LIMIT >> UCPTRIE_SHIFT_3; -constexpr int32_t BMP_I_LIMIT = BMP_LIMIT >> UCPTRIE_SHIFT_3; -constexpr int32_t ASCII_I_LIMIT = ASCII_LIMIT >> UCPTRIE_SHIFT_3; - -constexpr int32_t SMALL_DATA_BLOCKS_PER_BMP_BLOCK = (1 << (UCPTRIE_FAST_SHIFT - UCPTRIE_SHIFT_3)); - -// Flag values for data blocks. -constexpr uint8_t ALL_SAME = 0; -constexpr uint8_t MIXED = 1; -constexpr uint8_t SAME_AS = 2; - -/** Start with allocation of 16k data entries. */ -constexpr int32_t INITIAL_DATA_LENGTH = ((int32_t)1 << 14); - -/** Grow about 8x each time. */ -constexpr int32_t MEDIUM_DATA_LENGTH = ((int32_t)1 << 17); - -/** - * Maximum length of the build-time data array. - * One entry per 0x110000 code points. - */ -constexpr int32_t MAX_DATA_LENGTH = UNICODE_LIMIT; - -// Flag values for index-3 blocks while compacting/building. -constexpr uint8_t I3_NULL = 0; -constexpr uint8_t I3_BMP = 1; -constexpr uint8_t I3_16 = 2; -constexpr uint8_t I3_18 = 3; - -constexpr int32_t INDEX_3_18BIT_BLOCK_LENGTH = UCPTRIE_INDEX_3_BLOCK_LENGTH + UCPTRIE_INDEX_3_BLOCK_LENGTH / 8; - -class AllSameBlocks; -class MixedBlocks; - -class MutableCodePointTrie : public UMemory { -public: - MutableCodePointTrie(uint32_t initialValue, uint32_t errorValue, UErrorCode &errorCode); - MutableCodePointTrie(const MutableCodePointTrie &other, UErrorCode &errorCode); - MutableCodePointTrie(const MutableCodePointTrie &other) = delete; - ~MutableCodePointTrie(); - - MutableCodePointTrie &operator=(const MutableCodePointTrie &other) = delete; - - static MutableCodePointTrie *fromUCPMap(const UCPMap *map, UErrorCode &errorCode); - static MutableCodePointTrie *fromUCPTrie(const UCPTrie *trie, UErrorCode &errorCode); - - uint32_t get(UChar32 c) const; - int32_t getRange(UChar32 start, UCPMapValueFilter *filter, const void *context, - uint32_t *pValue) const; - - void set(UChar32 c, uint32_t value, UErrorCode &errorCode); - void setRange(UChar32 start, UChar32 end, uint32_t value, UErrorCode &errorCode); - - UCPTrie *build(UCPTrieType type, UCPTrieValueWidth valueWidth, UErrorCode &errorCode); - -private: - void clear(); - - bool ensureHighStart(UChar32 c); - int32_t allocDataBlock(int32_t blockLength); - int32_t getDataBlock(int32_t i); - - void maskValues(uint32_t mask); - UChar32 findHighStart() const; - int32_t compactWholeDataBlocks(int32_t fastILimit, AllSameBlocks &allSameBlocks); - int32_t compactData( - int32_t fastILimit, uint32_t *newData, int32_t newDataCapacity, - int32_t dataNullIndex, MixedBlocks &mixedBlocks, UErrorCode &errorCode); - int32_t compactIndex(int32_t fastILimit, MixedBlocks &mixedBlocks, UErrorCode &errorCode); - int32_t compactTrie(int32_t fastILimit, UErrorCode &errorCode); - - uint32_t *index = nullptr; - int32_t indexCapacity = 0; - int32_t index3NullOffset = -1; - uint32_t *data = nullptr; - int32_t dataCapacity = 0; - int32_t dataLength = 0; - int32_t dataNullOffset = -1; - - uint32_t origInitialValue; - uint32_t initialValue; - uint32_t errorValue; - UChar32 highStart; - uint32_t highValue; -#ifdef UCPTRIE_DEBUG -public: - const char *name; -#endif -private: - /** Temporary array while building the final data. */ - uint16_t *index16 = nullptr; - uint8_t flags[UNICODE_LIMIT >> UCPTRIE_SHIFT_3]; -}; - -MutableCodePointTrie::MutableCodePointTrie(uint32_t iniValue, uint32_t errValue, UErrorCode &errorCode) : - origInitialValue(iniValue), initialValue(iniValue), errorValue(errValue), - highStart(0), highValue(initialValue) -#ifdef UCPTRIE_DEBUG - , name("open") -#endif - { - if (U_FAILURE(errorCode)) { return; } - index = (uint32_t *)uprv_malloc(BMP_I_LIMIT * 4); - data = (uint32_t *)uprv_malloc(INITIAL_DATA_LENGTH * 4); - if (index == nullptr || data == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - indexCapacity = BMP_I_LIMIT; - dataCapacity = INITIAL_DATA_LENGTH; -} - -MutableCodePointTrie::MutableCodePointTrie(const MutableCodePointTrie &other, UErrorCode &errorCode) : - index3NullOffset(other.index3NullOffset), - dataNullOffset(other.dataNullOffset), - origInitialValue(other.origInitialValue), initialValue(other.initialValue), - errorValue(other.errorValue), - highStart(other.highStart), highValue(other.highValue) -#ifdef UCPTRIE_DEBUG - , name("mutable clone") -#endif - { - if (U_FAILURE(errorCode)) { return; } - int32_t iCapacity = highStart <= BMP_LIMIT ? BMP_I_LIMIT : I_LIMIT; - index = (uint32_t *)uprv_malloc(iCapacity * 4); - data = (uint32_t *)uprv_malloc(other.dataCapacity * 4); - if (index == nullptr || data == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - indexCapacity = iCapacity; - dataCapacity = other.dataCapacity; - - int32_t iLimit = highStart >> UCPTRIE_SHIFT_3; - uprv_memcpy(flags, other.flags, iLimit); - uprv_memcpy(index, other.index, iLimit * 4); - uprv_memcpy(data, other.data, (size_t)other.dataLength * 4); - dataLength = other.dataLength; - U_ASSERT(other.index16 == nullptr); -} - -MutableCodePointTrie::~MutableCodePointTrie() { - uprv_free(index); - uprv_free(data); - uprv_free(index16); -} - -MutableCodePointTrie *MutableCodePointTrie::fromUCPMap(const UCPMap *map, UErrorCode &errorCode) { - // Use the highValue as the initialValue to reduce the highStart. - uint32_t errorValue = ucpmap_get(map, -1); - uint32_t initialValue = ucpmap_get(map, 0x10ffff); - LocalPointer mutableTrie( - new MutableCodePointTrie(initialValue, errorValue, errorCode), - errorCode); - if (U_FAILURE(errorCode)) { - return nullptr; - } - UChar32 start = 0, end; - uint32_t value; - while ((end = ucpmap_getRange(map, start, UCPMAP_RANGE_NORMAL, 0, - nullptr, nullptr, &value)) >= 0) { - if (value != initialValue) { - if (start == end) { - mutableTrie->set(start, value, errorCode); - } else { - mutableTrie->setRange(start, end, value, errorCode); - } - } - start = end + 1; - } - if (U_SUCCESS(errorCode)) { - return mutableTrie.orphan(); - } else { - return nullptr; - } -} - -MutableCodePointTrie *MutableCodePointTrie::fromUCPTrie(const UCPTrie *trie, UErrorCode &errorCode) { - // Use the highValue as the initialValue to reduce the highStart. - uint32_t errorValue; - uint32_t initialValue; - switch (trie->valueWidth) { - case UCPTRIE_VALUE_BITS_16: - errorValue = trie->data.ptr16[trie->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET]; - initialValue = trie->data.ptr16[trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET]; - break; - case UCPTRIE_VALUE_BITS_32: - errorValue = trie->data.ptr32[trie->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET]; - initialValue = trie->data.ptr32[trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET]; - break; - case UCPTRIE_VALUE_BITS_8: - errorValue = trie->data.ptr8[trie->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET]; - initialValue = trie->data.ptr8[trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET]; - break; - default: - // Unreachable if the trie is properly initialized. - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - LocalPointer mutableTrie( - new MutableCodePointTrie(initialValue, errorValue, errorCode), - errorCode); - if (U_FAILURE(errorCode)) { - return nullptr; - } - UChar32 start = 0, end; - uint32_t value; - while ((end = ucptrie_getRange(trie, start, UCPMAP_RANGE_NORMAL, 0, - nullptr, nullptr, &value)) >= 0) { - if (value != initialValue) { - if (start == end) { - mutableTrie->set(start, value, errorCode); - } else { - mutableTrie->setRange(start, end, value, errorCode); - } - } - start = end + 1; - } - if (U_SUCCESS(errorCode)) { - return mutableTrie.orphan(); - } else { - return nullptr; - } -} - -void MutableCodePointTrie::clear() { - index3NullOffset = dataNullOffset = -1; - dataLength = 0; - highValue = initialValue = origInitialValue; - highStart = 0; - uprv_free(index16); - index16 = nullptr; -} - -uint32_t MutableCodePointTrie::get(UChar32 c) const { - if ((uint32_t)c > MAX_UNICODE) { - return errorValue; - } - if (c >= highStart) { - return highValue; - } - int32_t i = c >> UCPTRIE_SHIFT_3; - if (flags[i] == ALL_SAME) { - return index[i]; - } else { - return data[index[i] + (c & UCPTRIE_SMALL_DATA_MASK)]; - } -} - -inline uint32_t maybeFilterValue(uint32_t value, uint32_t initialValue, uint32_t nullValue, - UCPMapValueFilter *filter, const void *context) { - if (value == initialValue) { - value = nullValue; - } else if (filter != nullptr) { - value = filter(context, value); - } - return value; -} - -UChar32 MutableCodePointTrie::getRange( - UChar32 start, UCPMapValueFilter *filter, const void *context, - uint32_t *pValue) const { - if ((uint32_t)start > MAX_UNICODE) { - return U_SENTINEL; - } - if (start >= highStart) { - if (pValue != nullptr) { - uint32_t value = highValue; - if (filter != nullptr) { value = filter(context, value); } - *pValue = value; - } - return MAX_UNICODE; - } - uint32_t nullValue = initialValue; - if (filter != nullptr) { nullValue = filter(context, nullValue); } - UChar32 c = start; - uint32_t trieValue, value; - bool haveValue = false; - int32_t i = c >> UCPTRIE_SHIFT_3; - do { - if (flags[i] == ALL_SAME) { - uint32_t trieValue2 = index[i]; - if (haveValue) { - if (trieValue2 != trieValue) { - if (filter == nullptr || - maybeFilterValue(trieValue2, initialValue, nullValue, - filter, context) != value) { - return c - 1; - } - trieValue = trieValue2; // may or may not help - } - } else { - trieValue = trieValue2; - value = maybeFilterValue(trieValue2, initialValue, nullValue, filter, context); - if (pValue != nullptr) { *pValue = value; } - haveValue = true; - } - c = (c + UCPTRIE_SMALL_DATA_BLOCK_LENGTH) & ~UCPTRIE_SMALL_DATA_MASK; - } else /* MIXED */ { - int32_t di = index[i] + (c & UCPTRIE_SMALL_DATA_MASK); - uint32_t trieValue2 = data[di]; - if (haveValue) { - if (trieValue2 != trieValue) { - if (filter == nullptr || - maybeFilterValue(trieValue2, initialValue, nullValue, - filter, context) != value) { - return c - 1; - } - trieValue = trieValue2; // may or may not help - } - } else { - trieValue = trieValue2; - value = maybeFilterValue(trieValue2, initialValue, nullValue, filter, context); - if (pValue != nullptr) { *pValue = value; } - haveValue = true; - } - while ((++c & UCPTRIE_SMALL_DATA_MASK) != 0) { - trieValue2 = data[++di]; - if (trieValue2 != trieValue) { - if (filter == nullptr || - maybeFilterValue(trieValue2, initialValue, nullValue, - filter, context) != value) { - return c - 1; - } - } - trieValue = trieValue2; // may or may not help - } - } - ++i; - } while (c < highStart); - U_ASSERT(haveValue); - if (maybeFilterValue(highValue, initialValue, nullValue, - filter, context) != value) { - return c - 1; - } else { - return MAX_UNICODE; - } -} - -void -writeBlock(uint32_t *block, uint32_t value) { - uint32_t *limit = block + UCPTRIE_SMALL_DATA_BLOCK_LENGTH; - while (block < limit) { - *block++ = value; - } -} - -bool MutableCodePointTrie::ensureHighStart(UChar32 c) { - if (c >= highStart) { - // Round up to a UCPTRIE_CP_PER_INDEX_2_ENTRY boundary to simplify compaction. - c = (c + UCPTRIE_CP_PER_INDEX_2_ENTRY) & ~(UCPTRIE_CP_PER_INDEX_2_ENTRY - 1); - int32_t i = highStart >> UCPTRIE_SHIFT_3; - int32_t iLimit = c >> UCPTRIE_SHIFT_3; - if (iLimit > indexCapacity) { - uint32_t *newIndex = (uint32_t *)uprv_malloc(I_LIMIT * 4); - if (newIndex == nullptr) { return false; } - uprv_memcpy(newIndex, index, i * 4); - uprv_free(index); - index = newIndex; - indexCapacity = I_LIMIT; - } - do { - flags[i] = ALL_SAME; - index[i] = initialValue; - } while(++i < iLimit); - highStart = c; - } - return true; -} - -int32_t MutableCodePointTrie::allocDataBlock(int32_t blockLength) { - int32_t newBlock = dataLength; - int32_t newTop = newBlock + blockLength; - if (newTop > dataCapacity) { - int32_t capacity; - if (dataCapacity < MEDIUM_DATA_LENGTH) { - capacity = MEDIUM_DATA_LENGTH; - } else if (dataCapacity < MAX_DATA_LENGTH) { - capacity = MAX_DATA_LENGTH; - } else { - // Should never occur. - // Either MAX_DATA_LENGTH is incorrect, - // or the code writes more values than should be possible. - return -1; - } - uint32_t *newData = (uint32_t *)uprv_malloc(capacity * 4); - if (newData == nullptr) { - return -1; - } - uprv_memcpy(newData, data, (size_t)dataLength * 4); - uprv_free(data); - data = newData; - dataCapacity = capacity; - } - dataLength = newTop; - return newBlock; -} - -/** - * No error checking for illegal arguments. - * - * @return -1 if no new data block available (out of memory in data array) - * @internal - */ -int32_t MutableCodePointTrie::getDataBlock(int32_t i) { - if (flags[i] == MIXED) { - return index[i]; - } - if (i < BMP_I_LIMIT) { - int32_t newBlock = allocDataBlock(UCPTRIE_FAST_DATA_BLOCK_LENGTH); - if (newBlock < 0) { return newBlock; } - int32_t iStart = i & ~(SMALL_DATA_BLOCKS_PER_BMP_BLOCK -1); - int32_t iLimit = iStart + SMALL_DATA_BLOCKS_PER_BMP_BLOCK; - do { - U_ASSERT(flags[iStart] == ALL_SAME); - writeBlock(data + newBlock, index[iStart]); - flags[iStart] = MIXED; - index[iStart++] = newBlock; - newBlock += UCPTRIE_SMALL_DATA_BLOCK_LENGTH; - } while (iStart < iLimit); - return index[i]; - } else { - int32_t newBlock = allocDataBlock(UCPTRIE_SMALL_DATA_BLOCK_LENGTH); - if (newBlock < 0) { return newBlock; } - writeBlock(data + newBlock, index[i]); - flags[i] = MIXED; - index[i] = newBlock; - return newBlock; - } -} - -void MutableCodePointTrie::set(UChar32 c, uint32_t value, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { - return; - } - if ((uint32_t)c > MAX_UNICODE) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - int32_t block; - if (!ensureHighStart(c) || (block = getDataBlock(c >> UCPTRIE_SHIFT_3)) < 0) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - - data[block + (c & UCPTRIE_SMALL_DATA_MASK)] = value; -} - -void -fillBlock(uint32_t *block, UChar32 start, UChar32 limit, uint32_t value) { - uint32_t *pLimit = block + limit; - block += start; - while (block < pLimit) { - *block++ = value; - } -} - -void MutableCodePointTrie::setRange(UChar32 start, UChar32 end, uint32_t value, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { - return; - } - if ((uint32_t)start > MAX_UNICODE || (uint32_t)end > MAX_UNICODE || start > end) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (!ensureHighStart(end)) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - - UChar32 limit = end + 1; - if (start & UCPTRIE_SMALL_DATA_MASK) { - // Set partial block at [start..following block boundary[. - int32_t block = getDataBlock(start >> UCPTRIE_SHIFT_3); - if (block < 0) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - - UChar32 nextStart = (start + UCPTRIE_SMALL_DATA_MASK) & ~UCPTRIE_SMALL_DATA_MASK; - if (nextStart <= limit) { - fillBlock(data + block, start & UCPTRIE_SMALL_DATA_MASK, UCPTRIE_SMALL_DATA_BLOCK_LENGTH, - value); - start = nextStart; - } else { - fillBlock(data + block, start & UCPTRIE_SMALL_DATA_MASK, limit & UCPTRIE_SMALL_DATA_MASK, - value); - return; - } - } - - // Number of positions in the last, partial block. - int32_t rest = limit & UCPTRIE_SMALL_DATA_MASK; - - // Round down limit to a block boundary. - limit &= ~UCPTRIE_SMALL_DATA_MASK; - - // Iterate over all-value blocks. - while (start < limit) { - int32_t i = start >> UCPTRIE_SHIFT_3; - if (flags[i] == ALL_SAME) { - index[i] = value; - } else /* MIXED */ { - fillBlock(data + index[i], 0, UCPTRIE_SMALL_DATA_BLOCK_LENGTH, value); - } - start += UCPTRIE_SMALL_DATA_BLOCK_LENGTH; - } - - if (rest > 0) { - // Set partial block at [last block boundary..limit[. - int32_t block = getDataBlock(start >> UCPTRIE_SHIFT_3); - if (block < 0) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - - fillBlock(data + block, 0, rest, value); - } -} - -/* compaction --------------------------------------------------------------- */ - -void MutableCodePointTrie::maskValues(uint32_t mask) { - initialValue &= mask; - errorValue &= mask; - highValue &= mask; - int32_t iLimit = highStart >> UCPTRIE_SHIFT_3; - for (int32_t i = 0; i < iLimit; ++i) { - if (flags[i] == ALL_SAME) { - index[i] &= mask; - } - } - for (int32_t i = 0; i < dataLength; ++i) { - data[i] &= mask; - } -} - -template -bool equalBlocks(const UIntA *s, const UIntB *t, int32_t length) { - while (length > 0 && *s == *t) { - ++s; - ++t; - --length; - } - return length == 0; -} - -bool allValuesSameAs(const uint32_t *p, int32_t length, uint32_t value) { - const uint32_t *pLimit = p + length; - while (p < pLimit && *p == value) { ++p; } - return p == pLimit; -} - -/** Search for an identical block. */ -int32_t findSameBlock(const uint16_t *p, int32_t pStart, int32_t length, - const uint16_t *q, int32_t qStart, int32_t blockLength) { - // Ensure that we do not even partially get past length. - length -= blockLength; - - q += qStart; - while (pStart <= length) { - if (equalBlocks(p + pStart, q, blockLength)) { - return pStart; - } - ++pStart; - } - return -1; -} - -int32_t findAllSameBlock(const uint32_t *p, int32_t start, int32_t limit, - uint32_t value, int32_t blockLength) { - // Ensure that we do not even partially get past limit. - limit -= blockLength; - - for (int32_t block = start; block <= limit; ++block) { - if (p[block] == value) { - for (int32_t i = 1;; ++i) { - if (i == blockLength) { - return block; - } - if (p[block + i] != value) { - block += i; - break; - } - } - } - } - return -1; -} - -/** - * Look for maximum overlap of the beginning of the other block - * with the previous, adjacent block. - */ -template -int32_t getOverlap(const UIntA *p, int32_t length, - const UIntB *q, int32_t qStart, int32_t blockLength) { - int32_t overlap = blockLength - 1; - U_ASSERT(overlap <= length); - q += qStart; - while (overlap > 0 && !equalBlocks(p + (length - overlap), q, overlap)) { - --overlap; - } - return overlap; -} - -int32_t getAllSameOverlap(const uint32_t *p, int32_t length, uint32_t value, - int32_t blockLength) { - int32_t min = length - (blockLength - 1); - int32_t i = length; - while (min < i && p[i - 1] == value) { --i; } - return length - i; -} - -bool isStartOfSomeFastBlock(uint32_t dataOffset, const uint32_t index[], int32_t fastILimit) { - for (int32_t i = 0; i < fastILimit; i += SMALL_DATA_BLOCKS_PER_BMP_BLOCK) { - if (index[i] == dataOffset) { - return true; - } - } - return false; -} - -/** - * Finds the start of the last range in the trie by enumerating backward. - * Indexes for code points higher than this will be omitted. - */ -UChar32 MutableCodePointTrie::findHighStart() const { - int32_t i = highStart >> UCPTRIE_SHIFT_3; - while (i > 0) { - bool match; - if (flags[--i] == ALL_SAME) { - match = index[i] == highValue; - } else /* MIXED */ { - const uint32_t *p = data + index[i]; - for (int32_t j = 0;; ++j) { - if (j == UCPTRIE_SMALL_DATA_BLOCK_LENGTH) { - match = true; - break; - } - if (p[j] != highValue) { - match = false; - break; - } - } - } - if (!match) { - return (i + 1) << UCPTRIE_SHIFT_3; - } - } - return 0; -} - -class AllSameBlocks { -public: - static constexpr int32_t NEW_UNIQUE = -1; - static constexpr int32_t OVERFLOW = -2; - - AllSameBlocks() : length(0), mostRecent(-1) {} - - int32_t findOrAdd(int32_t index, int32_t count, uint32_t value) { - if (mostRecent >= 0 && values[mostRecent] == value) { - refCounts[mostRecent] += count; - return indexes[mostRecent]; - } - for (int32_t i = 0; i < length; ++i) { - if (values[i] == value) { - mostRecent = i; - refCounts[i] += count; - return indexes[i]; - } - } - if (length == CAPACITY) { - return OVERFLOW; - } - mostRecent = length; - indexes[length] = index; - values[length] = value; - refCounts[length++] = count; - return NEW_UNIQUE; - } - - /** Replaces the block which has the lowest reference count. */ - void add(int32_t index, int32_t count, uint32_t value) { - U_ASSERT(length == CAPACITY); - int32_t least = -1; - int32_t leastCount = I_LIMIT; - for (int32_t i = 0; i < length; ++i) { - U_ASSERT(values[i] != value); - if (refCounts[i] < leastCount) { - least = i; - leastCount = refCounts[i]; - } - } - U_ASSERT(least >= 0); - mostRecent = least; - indexes[least] = index; - values[least] = value; - refCounts[least] = count; - } - - int32_t findMostUsed() const { - if (length == 0) { return -1; } - int32_t max = -1; - int32_t maxCount = 0; - for (int32_t i = 0; i < length; ++i) { - if (refCounts[i] > maxCount) { - max = i; - maxCount = refCounts[i]; - } - } - return indexes[max]; - } - -private: - static constexpr int32_t CAPACITY = 32; - - int32_t length; - int32_t mostRecent; - - int32_t indexes[CAPACITY]; - uint32_t values[CAPACITY]; - int32_t refCounts[CAPACITY]; -}; - -// Custom hash table for mixed-value blocks to be found anywhere in the -// compacted data or index so far. -class MixedBlocks { -public: - MixedBlocks() {} - ~MixedBlocks() { - uprv_free(table); - } - - bool init(int32_t maxLength, int32_t newBlockLength) { - // We store actual data indexes + 1 to reserve 0 for empty entries. - int32_t maxDataIndex = maxLength - newBlockLength + 1; - int32_t newLength; - if (maxDataIndex <= 0xfff) { // 4k - newLength = 6007; - shift = 12; - mask = 0xfff; - } else if (maxDataIndex <= 0x7fff) { // 32k - newLength = 50021; - shift = 15; - mask = 0x7fff; - } else if (maxDataIndex <= 0x1ffff) { // 128k - newLength = 200003; - shift = 17; - mask = 0x1ffff; - } else { - // maxDataIndex up to around MAX_DATA_LENGTH, ca. 1.1M - newLength = 1500007; - shift = 21; - mask = 0x1fffff; - } - if (newLength > capacity) { - uprv_free(table); - table = (uint32_t *)uprv_malloc(newLength * 4); - if (table == nullptr) { - return false; - } - capacity = newLength; - } - length = newLength; - uprv_memset(table, 0, length * 4); - - blockLength = newBlockLength; - return true; - } - - template - void extend(const UInt *data, int32_t minStart, int32_t prevDataLength, int32_t newDataLength) { - int32_t start = prevDataLength - blockLength; - if (start >= minStart) { - ++start; // Skip the last block that we added last time. - } else { - start = minStart; // Begin with the first full block. - } - for (int32_t end = newDataLength - blockLength; start <= end; ++start) { - uint32_t hashCode = makeHashCode(data, start); - addEntry(data, start, hashCode, start); - } - } - - template - int32_t findBlock(const UIntA *data, const UIntB *blockData, int32_t blockStart) const { - uint32_t hashCode = makeHashCode(blockData, blockStart); - int32_t entryIndex = findEntry(data, blockData, blockStart, hashCode); - if (entryIndex >= 0) { - return (table[entryIndex] & mask) - 1; - } else { - return -1; - } - } - - int32_t findAllSameBlock(const uint32_t *data, uint32_t blockValue) const { - uint32_t hashCode = makeHashCode(blockValue); - int32_t entryIndex = findEntry(data, blockValue, hashCode); - if (entryIndex >= 0) { - return (table[entryIndex] & mask) - 1; - } else { - return -1; - } - } - -private: - template - uint32_t makeHashCode(const UInt *blockData, int32_t blockStart) const { - int32_t blockLimit = blockStart + blockLength; - uint32_t hashCode = blockData[blockStart++]; - do { - hashCode = 37 * hashCode + blockData[blockStart++]; - } while (blockStart < blockLimit); - return hashCode; - } - - uint32_t makeHashCode(uint32_t blockValue) const { - uint32_t hashCode = blockValue; - for (int32_t i = 1; i < blockLength; ++i) { - hashCode = 37 * hashCode + blockValue; - } - return hashCode; - } - - template - void addEntry(const UInt *data, int32_t blockStart, uint32_t hashCode, int32_t dataIndex) { - U_ASSERT(0 <= dataIndex && dataIndex < (int32_t)mask); - int32_t entryIndex = findEntry(data, data, blockStart, hashCode); - if (entryIndex < 0) { - table[~entryIndex] = (hashCode << shift) | (dataIndex + 1); - } - } - - template - int32_t findEntry(const UIntA *data, const UIntB *blockData, int32_t blockStart, - uint32_t hashCode) const { - uint32_t shiftedHashCode = hashCode << shift; - int32_t initialEntryIndex = (hashCode % (length - 1)) + 1; // 1..length-1 - for (int32_t entryIndex = initialEntryIndex;;) { - uint32_t entry = table[entryIndex]; - if (entry == 0) { - return ~entryIndex; - } - if ((entry & ~mask) == shiftedHashCode) { - int32_t dataIndex = (entry & mask) - 1; - if (equalBlocks(data + dataIndex, blockData + blockStart, blockLength)) { - return entryIndex; - } - } - entryIndex = nextIndex(initialEntryIndex, entryIndex); - } - } - - int32_t findEntry(const uint32_t *data, uint32_t blockValue, uint32_t hashCode) const { - uint32_t shiftedHashCode = hashCode << shift; - int32_t initialEntryIndex = (hashCode % (length - 1)) + 1; // 1..length-1 - for (int32_t entryIndex = initialEntryIndex;;) { - uint32_t entry = table[entryIndex]; - if (entry == 0) { - return ~entryIndex; - } - if ((entry & ~mask) == shiftedHashCode) { - int32_t dataIndex = (entry & mask) - 1; - if (allValuesSameAs(data + dataIndex, blockLength, blockValue)) { - return entryIndex; - } - } - entryIndex = nextIndex(initialEntryIndex, entryIndex); - } - } - - inline int32_t nextIndex(int32_t initialEntryIndex, int32_t entryIndex) const { - // U_ASSERT(0 < initialEntryIndex && initialEntryIndex < length); - return (entryIndex + initialEntryIndex) % length; - } - - // Hash table. - // The length is a prime number, larger than the maximum data length. - // The "shift" lower bits store a data index + 1. - // The remaining upper bits store a partial hashCode of the block data values. - uint32_t *table = nullptr; - int32_t capacity = 0; - int32_t length = 0; - int32_t shift = 0; - uint32_t mask = 0; - - int32_t blockLength = 0; -}; - -int32_t MutableCodePointTrie::compactWholeDataBlocks(int32_t fastILimit, AllSameBlocks &allSameBlocks) { -#ifdef UCPTRIE_DEBUG - bool overflow = false; -#endif - - // ASCII data will be stored as a linear table, even if the following code - // does not yet count it that way. - int32_t newDataCapacity = ASCII_LIMIT; - // Add room for a small data null block in case it would match the start of - // a fast data block where dataNullOffset must not be set in that case. - newDataCapacity += UCPTRIE_SMALL_DATA_BLOCK_LENGTH; - // Add room for special values (errorValue, highValue) and padding. - newDataCapacity += 4; - int32_t iLimit = highStart >> UCPTRIE_SHIFT_3; - int32_t blockLength = UCPTRIE_FAST_DATA_BLOCK_LENGTH; - int32_t inc = SMALL_DATA_BLOCKS_PER_BMP_BLOCK; - for (int32_t i = 0; i < iLimit; i += inc) { - if (i == fastILimit) { - blockLength = UCPTRIE_SMALL_DATA_BLOCK_LENGTH; - inc = 1; - } - uint32_t value = index[i]; - if (flags[i] == MIXED) { - // Really mixed? - const uint32_t *p = data + value; - value = *p; - if (allValuesSameAs(p + 1, blockLength - 1, value)) { - flags[i] = ALL_SAME; - index[i] = value; - // Fall through to ALL_SAME handling. - } else { - newDataCapacity += blockLength; - continue; - } - } else { - U_ASSERT(flags[i] == ALL_SAME); - if (inc > 1) { - // Do all of the fast-range data block's ALL_SAME parts have the same value? - bool allSame = true; - int32_t next_i = i + inc; - for (int32_t j = i + 1; j < next_i; ++j) { - U_ASSERT(flags[j] == ALL_SAME); - if (index[j] != value) { - allSame = false; - break; - } - } - if (!allSame) { - // Turn it into a MIXED block. - if (getDataBlock(i) < 0) { - return -1; - } - newDataCapacity += blockLength; - continue; - } - } - } - // Is there another ALL_SAME block with the same value? - int32_t other = allSameBlocks.findOrAdd(i, inc, value); - if (other == AllSameBlocks::OVERFLOW) { - // The fixed-size array overflowed. Slow check for a duplicate block. -#ifdef UCPTRIE_DEBUG - if (!overflow) { - puts("UCPTrie AllSameBlocks overflow"); - overflow = true; - } -#endif - int32_t jInc = SMALL_DATA_BLOCKS_PER_BMP_BLOCK; - for (int32_t j = 0;; j += jInc) { - if (j == i) { - allSameBlocks.add(i, inc, value); - break; - } - if (j == fastILimit) { - jInc = 1; - } - if (flags[j] == ALL_SAME && index[j] == value) { - allSameBlocks.add(j, jInc + inc, value); - other = j; - break; - // We could keep counting blocks with the same value - // before we add the first one, which may improve compaction in rare cases, - // but it would make it slower. - } - } - } - if (other >= 0) { - flags[i] = SAME_AS; - index[i] = other; - } else { - // New unique same-value block. - newDataCapacity += blockLength; - } - } - return newDataCapacity; -} - -#ifdef UCPTRIE_DEBUG -# define DEBUG_DO(expr) expr -#else -# define DEBUG_DO(expr) -#endif - -#ifdef UCPTRIE_DEBUG -// Braille symbols: U+28xx = UTF-8 E2 A0 80..E2 A3 BF -int32_t appendValue(char s[], int32_t length, uint32_t value) { - value ^= value >> 16; - value ^= value >> 8; - s[length] = 0xE2; - s[length + 1] = (char)(0xA0 + ((value >> 6) & 3)); - s[length + 2] = (char)(0x80 + (value & 0x3F)); - return length + 3; -} - -void printBlock(const uint32_t *block, int32_t blockLength, uint32_t value, - UChar32 start, int32_t overlap, uint32_t initialValue) { - char s[UCPTRIE_FAST_DATA_BLOCK_LENGTH * 3 + 3]; - int32_t length = 0; - int32_t i; - for (i = 0; i < overlap; ++i) { - length = appendValue(s, length, 0); // Braille blank - } - s[length++] = '|'; - for (; i < blockLength; ++i) { - if (block != nullptr) { - value = block[i]; - } - if (value == initialValue) { - value = 0x40; // Braille lower left dot - } - length = appendValue(s, length, value); - } - s[length] = 0; - start += overlap; - if (start <= 0xffff) { - printf(" %04lX %s|\n", (long)start, s); - } else if (start <= 0xfffff) { - printf(" %5lX %s|\n", (long)start, s); - } else { - printf(" %6lX %s|\n", (long)start, s); - } -} -#endif - -/** - * Compacts a build-time trie. - * - * The compaction - * - removes blocks that are identical with earlier ones - * - overlaps each new non-duplicate block as much as possible with the previously-written one - * - works with fast-range data blocks whose length is a multiple of that of - * higher-code-point data blocks - * - * It does not try to find an optimal order of writing, deduplicating, and overlapping blocks. - */ -int32_t MutableCodePointTrie::compactData( - int32_t fastILimit, uint32_t *newData, int32_t newDataCapacity, - int32_t dataNullIndex, MixedBlocks &mixedBlocks, UErrorCode &errorCode) { -#ifdef UCPTRIE_DEBUG - int32_t countSame=0, sumOverlaps=0; - bool printData = dataLength == 29088 /* line.brk */ || - // dataLength == 30048 /* CanonIterData */ || - dataLength == 50400 /* zh.txt~stroke */; -#endif - - // The linear ASCII data has been copied into newData already. - int32_t newDataLength = 0; - for (int32_t i = 0; newDataLength < ASCII_LIMIT; - newDataLength += UCPTRIE_FAST_DATA_BLOCK_LENGTH, i += SMALL_DATA_BLOCKS_PER_BMP_BLOCK) { - index[i] = newDataLength; -#ifdef UCPTRIE_DEBUG - if (printData) { - printBlock(newData + newDataLength, UCPTRIE_FAST_DATA_BLOCK_LENGTH, 0, newDataLength, 0, initialValue); - } -#endif - } - - int32_t blockLength = UCPTRIE_FAST_DATA_BLOCK_LENGTH; - if (!mixedBlocks.init(newDataCapacity, blockLength)) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - mixedBlocks.extend(newData, 0, 0, newDataLength); - - int32_t iLimit = highStart >> UCPTRIE_SHIFT_3; - int32_t inc = SMALL_DATA_BLOCKS_PER_BMP_BLOCK; - int32_t fastLength = 0; - for (int32_t i = ASCII_I_LIMIT; i < iLimit; i += inc) { - if (i == fastILimit) { - blockLength = UCPTRIE_SMALL_DATA_BLOCK_LENGTH; - inc = 1; - fastLength = newDataLength; - if (!mixedBlocks.init(newDataCapacity, blockLength)) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - mixedBlocks.extend(newData, 0, 0, newDataLength); - } - if (flags[i] == ALL_SAME) { - uint32_t value = index[i]; - // Find an earlier part of the data array of length blockLength - // that is filled with this value. - int32_t n = mixedBlocks.findAllSameBlock(newData, value); - // If we find a match, and the current block is the data null block, - // and it is not a fast block but matches the start of a fast block, - // then we need to continue looking. - // This is because this small block is shorter than the fast block, - // and not all of the rest of the fast block is filled with this value. - // Otherwise trie.getRange() would detect that the fast block starts at - // dataNullOffset and assume incorrectly that it is filled with the null value. - while (n >= 0 && i == dataNullIndex && i >= fastILimit && n < fastLength && - isStartOfSomeFastBlock(n, index, fastILimit)) { - n = findAllSameBlock(newData, n + 1, newDataLength, value, blockLength); - } - if (n >= 0) { - DEBUG_DO(++countSame); - index[i] = n; - } else { - n = getAllSameOverlap(newData, newDataLength, value, blockLength); - DEBUG_DO(sumOverlaps += n); -#ifdef UCPTRIE_DEBUG - if (printData) { - printBlock(nullptr, blockLength, value, i << UCPTRIE_SHIFT_3, n, initialValue); - } -#endif - index[i] = newDataLength - n; - int32_t prevDataLength = newDataLength; - while (n < blockLength) { - newData[newDataLength++] = value; - ++n; - } - mixedBlocks.extend(newData, 0, prevDataLength, newDataLength); - } - } else if (flags[i] == MIXED) { - const uint32_t *block = data + index[i]; - int32_t n = mixedBlocks.findBlock(newData, block, 0); - if (n >= 0) { - DEBUG_DO(++countSame); - index[i] = n; - } else { - n = getOverlap(newData, newDataLength, block, 0, blockLength); - DEBUG_DO(sumOverlaps += n); -#ifdef UCPTRIE_DEBUG - if (printData) { - printBlock(block, blockLength, 0, i << UCPTRIE_SHIFT_3, n, initialValue); - } -#endif - index[i] = newDataLength - n; - int32_t prevDataLength = newDataLength; - while (n < blockLength) { - newData[newDataLength++] = block[n++]; - } - mixedBlocks.extend(newData, 0, prevDataLength, newDataLength); - } - } else /* SAME_AS */ { - uint32_t j = index[i]; - index[i] = index[j]; - } - } - -#ifdef UCPTRIE_DEBUG - /* we saved some space */ - printf("compacting UCPTrie: count of 32-bit data words %lu->%lu countSame=%ld sumOverlaps=%ld\n", - (long)dataLength, (long)newDataLength, (long)countSame, (long)sumOverlaps); -#endif - return newDataLength; -} - -int32_t MutableCodePointTrie::compactIndex(int32_t fastILimit, MixedBlocks &mixedBlocks, - UErrorCode &errorCode) { - int32_t fastIndexLength = fastILimit >> (UCPTRIE_FAST_SHIFT - UCPTRIE_SHIFT_3); - if ((highStart >> UCPTRIE_FAST_SHIFT) <= fastIndexLength) { - // Only the linear fast index, no multi-stage index tables. - index3NullOffset = UCPTRIE_NO_INDEX3_NULL_OFFSET; - return fastIndexLength; - } - - // Condense the fast index table. - // Also, does it contain an index-3 block with all dataNullOffset? - uint16_t fastIndex[UCPTRIE_BMP_INDEX_LENGTH]; // fastIndexLength - int32_t i3FirstNull = -1; - for (int32_t i = 0, j = 0; i < fastILimit; ++j) { - uint32_t i3 = index[i]; - fastIndex[j] = (uint16_t)i3; - if (i3 == (uint32_t)dataNullOffset) { - if (i3FirstNull < 0) { - i3FirstNull = j; - } else if (index3NullOffset < 0 && - (j - i3FirstNull + 1) == UCPTRIE_INDEX_3_BLOCK_LENGTH) { - index3NullOffset = i3FirstNull; - } - } else { - i3FirstNull = -1; - } - // Set the index entries that compactData() skipped. - // Needed when the multi-stage index covers the fast index range as well. - int32_t iNext = i + SMALL_DATA_BLOCKS_PER_BMP_BLOCK; - while (++i < iNext) { - i3 += UCPTRIE_SMALL_DATA_BLOCK_LENGTH; - index[i] = i3; - } - } - - if (!mixedBlocks.init(fastIndexLength, UCPTRIE_INDEX_3_BLOCK_LENGTH)) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - mixedBlocks.extend(fastIndex, 0, 0, fastIndexLength); - - // Examine index-3 blocks. For each determine one of: - // - same as the index-3 null block - // - same as a fast-index block - // - 16-bit indexes - // - 18-bit indexes - // We store this in the first flags entry for the index-3 block. - // - // Also determine an upper limit for the index-3 table length. - int32_t index3Capacity = 0; - i3FirstNull = index3NullOffset; - bool hasLongI3Blocks = false; - // If the fast index covers the whole BMP, then - // the multi-stage index is only for supplementary code points. - // Otherwise, the multi-stage index covers all of Unicode. - int32_t iStart = fastILimit < BMP_I_LIMIT ? 0 : BMP_I_LIMIT; - int32_t iLimit = highStart >> UCPTRIE_SHIFT_3; - for (int32_t i = iStart; i < iLimit;) { - int32_t j = i; - int32_t jLimit = i + UCPTRIE_INDEX_3_BLOCK_LENGTH; - uint32_t oredI3 = 0; - bool isNull = true; - do { - uint32_t i3 = index[j]; - oredI3 |= i3; - if (i3 != (uint32_t)dataNullOffset) { - isNull = false; - } - } while (++j < jLimit); - if (isNull) { - flags[i] = I3_NULL; - if (i3FirstNull < 0) { - if (oredI3 <= 0xffff) { - index3Capacity += UCPTRIE_INDEX_3_BLOCK_LENGTH; - } else { - index3Capacity += INDEX_3_18BIT_BLOCK_LENGTH; - hasLongI3Blocks = true; - } - i3FirstNull = 0; - } - } else { - if (oredI3 <= 0xffff) { - int32_t n = mixedBlocks.findBlock(fastIndex, index, i); - if (n >= 0) { - flags[i] = I3_BMP; - index[i] = n; - } else { - flags[i] = I3_16; - index3Capacity += UCPTRIE_INDEX_3_BLOCK_LENGTH; - } - } else { - flags[i] = I3_18; - index3Capacity += INDEX_3_18BIT_BLOCK_LENGTH; - hasLongI3Blocks = true; - } - } - i = j; - } - - int32_t index2Capacity = (iLimit - iStart) >> UCPTRIE_SHIFT_2_3; - - // Length of the index-1 table, rounded up. - int32_t index1Length = (index2Capacity + UCPTRIE_INDEX_2_MASK) >> UCPTRIE_SHIFT_1_2; - - // Index table: Fast index, index-1, index-3, index-2. - // +1 for possible index table padding. - int32_t index16Capacity = fastIndexLength + index1Length + index3Capacity + index2Capacity + 1; - index16 = (uint16_t *)uprv_malloc(index16Capacity * 2); - if (index16 == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - uprv_memcpy(index16, fastIndex, fastIndexLength * 2); - - if (!mixedBlocks.init(index16Capacity, UCPTRIE_INDEX_3_BLOCK_LENGTH)) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - MixedBlocks longI3Blocks; - if (hasLongI3Blocks) { - if (!longI3Blocks.init(index16Capacity, INDEX_3_18BIT_BLOCK_LENGTH)) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - } - - // Compact the index-3 table and write an uncompacted version of the index-2 table. - uint16_t index2[UNICODE_LIMIT >> UCPTRIE_SHIFT_2]; // index2Capacity - int32_t i2Length = 0; - i3FirstNull = index3NullOffset; - int32_t index3Start = fastIndexLength + index1Length; - int32_t indexLength = index3Start; - for (int32_t i = iStart; i < iLimit; i += UCPTRIE_INDEX_3_BLOCK_LENGTH) { - int32_t i3; - uint8_t f = flags[i]; - if (f == I3_NULL && i3FirstNull < 0) { - // First index-3 null block. Write & overlap it like a normal block, then remember it. - f = dataNullOffset <= 0xffff ? I3_16 : I3_18; - i3FirstNull = 0; - } - if (f == I3_NULL) { - i3 = index3NullOffset; - } else if (f == I3_BMP) { - i3 = index[i]; - } else if (f == I3_16) { - int32_t n = mixedBlocks.findBlock(index16, index, i); - if (n >= 0) { - i3 = n; - } else { - if (indexLength == index3Start) { - // No overlap at the boundary between the index-1 and index-3 tables. - n = 0; - } else { - n = getOverlap(index16, indexLength, - index, i, UCPTRIE_INDEX_3_BLOCK_LENGTH); - } - i3 = indexLength - n; - int32_t prevIndexLength = indexLength; - while (n < UCPTRIE_INDEX_3_BLOCK_LENGTH) { - index16[indexLength++] = index[i + n++]; - } - mixedBlocks.extend(index16, index3Start, prevIndexLength, indexLength); - if (hasLongI3Blocks) { - longI3Blocks.extend(index16, index3Start, prevIndexLength, indexLength); - } - } - } else { - U_ASSERT(f == I3_18); - U_ASSERT(hasLongI3Blocks); - // Encode an index-3 block that contains one or more data indexes exceeding 16 bits. - int32_t j = i; - int32_t jLimit = i + UCPTRIE_INDEX_3_BLOCK_LENGTH; - int32_t k = indexLength; - do { - ++k; - uint32_t v = index[j++]; - uint32_t upperBits = (v & 0x30000) >> 2; - index16[k++] = v; - v = index[j++]; - upperBits |= (v & 0x30000) >> 4; - index16[k++] = v; - v = index[j++]; - upperBits |= (v & 0x30000) >> 6; - index16[k++] = v; - v = index[j++]; - upperBits |= (v & 0x30000) >> 8; - index16[k++] = v; - v = index[j++]; - upperBits |= (v & 0x30000) >> 10; - index16[k++] = v; - v = index[j++]; - upperBits |= (v & 0x30000) >> 12; - index16[k++] = v; - v = index[j++]; - upperBits |= (v & 0x30000) >> 14; - index16[k++] = v; - v = index[j++]; - upperBits |= (v & 0x30000) >> 16; - index16[k++] = v; - index16[k - 9] = upperBits; - } while (j < jLimit); - int32_t n = longI3Blocks.findBlock(index16, index16, indexLength); - if (n >= 0) { - i3 = n | 0x8000; - } else { - if (indexLength == index3Start) { - // No overlap at the boundary between the index-1 and index-3 tables. - n = 0; - } else { - n = getOverlap(index16, indexLength, - index16, indexLength, INDEX_3_18BIT_BLOCK_LENGTH); - } - i3 = (indexLength - n) | 0x8000; - int32_t prevIndexLength = indexLength; - if (n > 0) { - int32_t start = indexLength; - while (n < INDEX_3_18BIT_BLOCK_LENGTH) { - index16[indexLength++] = index16[start + n++]; - } - } else { - indexLength += INDEX_3_18BIT_BLOCK_LENGTH; - } - mixedBlocks.extend(index16, index3Start, prevIndexLength, indexLength); - if (hasLongI3Blocks) { - longI3Blocks.extend(index16, index3Start, prevIndexLength, indexLength); - } - } - } - if (index3NullOffset < 0 && i3FirstNull >= 0) { - index3NullOffset = i3; - } - // Set the index-2 table entry. - index2[i2Length++] = i3; - } - U_ASSERT(i2Length == index2Capacity); - U_ASSERT(indexLength <= index3Start + index3Capacity); - - if (index3NullOffset < 0) { - index3NullOffset = UCPTRIE_NO_INDEX3_NULL_OFFSET; - } - if (indexLength >= (UCPTRIE_NO_INDEX3_NULL_OFFSET + UCPTRIE_INDEX_3_BLOCK_LENGTH)) { - // The index-3 offsets exceed 15 bits, or - // the last one cannot be distinguished from the no-null-block value. - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - // Compact the index-2 table and write the index-1 table. - static_assert(UCPTRIE_INDEX_2_BLOCK_LENGTH == UCPTRIE_INDEX_3_BLOCK_LENGTH, - "must re-init mixedBlocks"); - int32_t blockLength = UCPTRIE_INDEX_2_BLOCK_LENGTH; - int32_t i1 = fastIndexLength; - for (int32_t i = 0; i < i2Length; i += blockLength) { - int32_t n; - if ((i2Length - i) >= blockLength) { - // normal block - U_ASSERT(blockLength == UCPTRIE_INDEX_2_BLOCK_LENGTH); - n = mixedBlocks.findBlock(index16, index2, i); - } else { - // highStart is inside the last index-2 block. Shorten it. - blockLength = i2Length - i; - n = findSameBlock(index16, index3Start, indexLength, - index2, i, blockLength); - } - int32_t i2; - if (n >= 0) { - i2 = n; - } else { - if (indexLength == index3Start) { - // No overlap at the boundary between the index-1 and index-3/2 tables. - n = 0; - } else { - n = getOverlap(index16, indexLength, index2, i, blockLength); - } - i2 = indexLength - n; - int32_t prevIndexLength = indexLength; - while (n < blockLength) { - index16[indexLength++] = index2[i + n++]; - } - mixedBlocks.extend(index16, index3Start, prevIndexLength, indexLength); - } - // Set the index-1 table entry. - index16[i1++] = i2; - } - U_ASSERT(i1 == index3Start); - U_ASSERT(indexLength <= index16Capacity); - -#ifdef UCPTRIE_DEBUG - /* we saved some space */ - printf("compacting UCPTrie: count of 16-bit index words %lu->%lu\n", - (long)iLimit, (long)indexLength); -#endif - - return indexLength; -} - -int32_t MutableCodePointTrie::compactTrie(int32_t fastILimit, UErrorCode &errorCode) { - // Find the real highStart and round it up. - U_ASSERT((highStart & (UCPTRIE_CP_PER_INDEX_2_ENTRY - 1)) == 0); - highValue = get(MAX_UNICODE); - int32_t realHighStart = findHighStart(); - realHighStart = (realHighStart + (UCPTRIE_CP_PER_INDEX_2_ENTRY - 1)) & - ~(UCPTRIE_CP_PER_INDEX_2_ENTRY - 1); - if (realHighStart == UNICODE_LIMIT) { - highValue = initialValue; - } - -#ifdef UCPTRIE_DEBUG - printf("UCPTrie: highStart U+%06lx highValue 0x%lx initialValue 0x%lx\n", - (long)realHighStart, (long)highValue, (long)initialValue); -#endif - - // We always store indexes and data values for the fast range. - // Pin highStart to the top of that range while building. - UChar32 fastLimit = fastILimit << UCPTRIE_SHIFT_3; - if (realHighStart < fastLimit) { - for (int32_t i = (realHighStart >> UCPTRIE_SHIFT_3); i < fastILimit; ++i) { - flags[i] = ALL_SAME; - index[i] = highValue; - } - highStart = fastLimit; - } else { - highStart = realHighStart; - } - - uint32_t asciiData[ASCII_LIMIT]; - for (int32_t i = 0; i < ASCII_LIMIT; ++i) { - asciiData[i] = get(i); - } - - // First we look for which data blocks have the same value repeated over the whole block, - // deduplicate such blocks, find a good null data block (for faster enumeration), - // and get an upper bound for the necessary data array length. - AllSameBlocks allSameBlocks; - int32_t newDataCapacity = compactWholeDataBlocks(fastILimit, allSameBlocks); - if (newDataCapacity < 0) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - uint32_t *newData = (uint32_t *)uprv_malloc(newDataCapacity * 4); - if (newData == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - uprv_memcpy(newData, asciiData, sizeof(asciiData)); - - int32_t dataNullIndex = allSameBlocks.findMostUsed(); - - MixedBlocks mixedBlocks; - int32_t newDataLength = compactData(fastILimit, newData, newDataCapacity, - dataNullIndex, mixedBlocks, errorCode); - if (U_FAILURE(errorCode)) { return 0; } - U_ASSERT(newDataLength <= newDataCapacity); - uprv_free(data); - data = newData; - dataCapacity = newDataCapacity; - dataLength = newDataLength; - if (dataLength > (0x3ffff + UCPTRIE_SMALL_DATA_BLOCK_LENGTH)) { - // The offset of the last data block is too high to be stored in the index table. - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - if (dataNullIndex >= 0) { - dataNullOffset = index[dataNullIndex]; -#ifdef UCPTRIE_DEBUG - if (data[dataNullOffset] != initialValue) { - printf("UCPTrie initialValue %lx -> more common nullValue %lx\n", - (long)initialValue, (long)data[dataNullOffset]); - } -#endif - initialValue = data[dataNullOffset]; - } else { - dataNullOffset = UCPTRIE_NO_DATA_NULL_OFFSET; - } - - int32_t indexLength = compactIndex(fastILimit, mixedBlocks, errorCode); - highStart = realHighStart; - return indexLength; -} - -UCPTrie *MutableCodePointTrie::build(UCPTrieType type, UCPTrieValueWidth valueWidth, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { - return nullptr; - } - if (type < UCPTRIE_TYPE_FAST || UCPTRIE_TYPE_SMALL < type || - valueWidth < UCPTRIE_VALUE_BITS_16 || UCPTRIE_VALUE_BITS_8 < valueWidth) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - - // The mutable trie always stores 32-bit values. - // When we build a UCPTrie for a smaller value width, we first mask off unused bits - // before compacting the data. - switch (valueWidth) { - case UCPTRIE_VALUE_BITS_32: - break; - case UCPTRIE_VALUE_BITS_16: - maskValues(0xffff); - break; - case UCPTRIE_VALUE_BITS_8: - maskValues(0xff); - break; - default: - break; - } - - UChar32 fastLimit = type == UCPTRIE_TYPE_FAST ? BMP_LIMIT : UCPTRIE_SMALL_LIMIT; - int32_t indexLength = compactTrie(fastLimit >> UCPTRIE_SHIFT_3, errorCode); - if (U_FAILURE(errorCode)) { - clear(); - return nullptr; - } - - // Ensure data table alignment: The index length must be even for uint32_t data. - if (valueWidth == UCPTRIE_VALUE_BITS_32 && (indexLength & 1) != 0) { - index16[indexLength++] = 0xffee; // arbitrary value - } - - // Make the total trie structure length a multiple of 4 bytes by padding the data table, - // and store special values as the last two data values. - int32_t length = indexLength * 2; - if (valueWidth == UCPTRIE_VALUE_BITS_16) { - if (((indexLength ^ dataLength) & 1) != 0) { - // padding - data[dataLength++] = errorValue; - } - if (data[dataLength - 1] != errorValue || data[dataLength - 2] != highValue) { - data[dataLength++] = highValue; - data[dataLength++] = errorValue; - } - length += dataLength * 2; - } else if (valueWidth == UCPTRIE_VALUE_BITS_32) { - // 32-bit data words never need padding to a multiple of 4 bytes. - if (data[dataLength - 1] != errorValue || data[dataLength - 2] != highValue) { - if (data[dataLength - 1] != highValue) { - data[dataLength++] = highValue; - } - data[dataLength++] = errorValue; - } - length += dataLength * 4; - } else { - int32_t and3 = (length + dataLength) & 3; - if (and3 == 0 && data[dataLength - 1] == errorValue && data[dataLength - 2] == highValue) { - // all set - } else if(and3 == 3 && data[dataLength - 1] == highValue) { - data[dataLength++] = errorValue; - } else { - while (and3 != 2) { - data[dataLength++] = highValue; - and3 = (and3 + 1) & 3; - } - data[dataLength++] = highValue; - data[dataLength++] = errorValue; - } - length += dataLength; - } - - // Calculate the total length of the UCPTrie as a single memory block. - length += sizeof(UCPTrie); - U_ASSERT((length & 3) == 0); - - uint8_t *bytes = (uint8_t *)uprv_malloc(length); - if (bytes == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - clear(); - return nullptr; - } - UCPTrie *trie = reinterpret_cast(bytes); - uprv_memset(trie, 0, sizeof(UCPTrie)); - trie->indexLength = indexLength; - trie->dataLength = dataLength; - - trie->highStart = highStart; - // Round up shifted12HighStart to a multiple of 0x1000 for easy testing from UTF-8 lead bytes. - // Runtime code needs to then test for the real highStart as well. - trie->shifted12HighStart = (highStart + 0xfff) >> 12; - trie->type = type; - trie->valueWidth = valueWidth; - - trie->index3NullOffset = index3NullOffset; - trie->dataNullOffset = dataNullOffset; - trie->nullValue = initialValue; - - bytes += sizeof(UCPTrie); - - // Fill the index and data arrays. - uint16_t *dest16 = (uint16_t *)bytes; - trie->index = dest16; - - if (highStart <= fastLimit) { - // Condense only the fast index from the mutable-trie index. - for (int32_t i = 0, j = 0; j < indexLength; i += SMALL_DATA_BLOCKS_PER_BMP_BLOCK, ++j) { - *dest16++ = (uint16_t)index[i]; // dest16[j] - } - } else { - uprv_memcpy(dest16, index16, indexLength * 2); - dest16 += indexLength; - } - bytes += indexLength * 2; - - // Write the data array. - const uint32_t *p = data; - switch (valueWidth) { - case UCPTRIE_VALUE_BITS_16: - // Write 16-bit data values. - trie->data.ptr16 = dest16; - for (int32_t i = dataLength; i > 0; --i) { - *dest16++ = (uint16_t)*p++; - } - break; - case UCPTRIE_VALUE_BITS_32: - // Write 32-bit data values. - trie->data.ptr32 = (uint32_t *)bytes; - uprv_memcpy(bytes, p, (size_t)dataLength * 4); - break; - case UCPTRIE_VALUE_BITS_8: - // Write 8-bit data values. - trie->data.ptr8 = bytes; - for (int32_t i = dataLength; i > 0; --i) { - *bytes++ = (uint8_t)*p++; - } - break; - default: - // Will not occur, valueWidth checked at the beginning. - break; - } - -#ifdef UCPTRIE_DEBUG - trie->name = name; - - ucptrie_printLengths(trie, ""); -#endif - - clear(); - return trie; -} - -} // namespace - -U_NAMESPACE_END - -U_NAMESPACE_USE - -U_CAPI UMutableCPTrie * U_EXPORT2 -umutablecptrie_open(uint32_t initialValue, uint32_t errorValue, UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return nullptr; - } - LocalPointer trie( - new MutableCodePointTrie(initialValue, errorValue, *pErrorCode), *pErrorCode); - if (U_FAILURE(*pErrorCode)) { - return nullptr; - } - return reinterpret_cast(trie.orphan()); -} - -U_CAPI UMutableCPTrie * U_EXPORT2 -umutablecptrie_clone(const UMutableCPTrie *other, UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return nullptr; - } - if (other == nullptr) { - return nullptr; - } - LocalPointer clone( - new MutableCodePointTrie(*reinterpret_cast(other), *pErrorCode), *pErrorCode); - if (U_FAILURE(*pErrorCode)) { - return nullptr; - } - return reinterpret_cast(clone.orphan()); -} - -U_CAPI void U_EXPORT2 -umutablecptrie_close(UMutableCPTrie *trie) { - delete reinterpret_cast(trie); -} - -U_CAPI UMutableCPTrie * U_EXPORT2 -umutablecptrie_fromUCPMap(const UCPMap *map, UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return nullptr; - } - if (map == nullptr) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - return reinterpret_cast(MutableCodePointTrie::fromUCPMap(map, *pErrorCode)); -} - -U_CAPI UMutableCPTrie * U_EXPORT2 -umutablecptrie_fromUCPTrie(const UCPTrie *trie, UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return nullptr; - } - if (trie == nullptr) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - return reinterpret_cast(MutableCodePointTrie::fromUCPTrie(trie, *pErrorCode)); -} - -U_CAPI uint32_t U_EXPORT2 -umutablecptrie_get(const UMutableCPTrie *trie, UChar32 c) { - return reinterpret_cast(trie)->get(c); -} - -namespace { - -UChar32 getRange(const void *trie, UChar32 start, - UCPMapValueFilter *filter, const void *context, uint32_t *pValue) { - return reinterpret_cast(trie)-> - getRange(start, filter, context, pValue); -} - -} // namespace - -U_CAPI UChar32 U_EXPORT2 -umutablecptrie_getRange(const UMutableCPTrie *trie, UChar32 start, - UCPMapRangeOption option, uint32_t surrogateValue, - UCPMapValueFilter *filter, const void *context, uint32_t *pValue) { - return ucptrie_internalGetRange(getRange, trie, start, - option, surrogateValue, - filter, context, pValue); -} - -U_CAPI void U_EXPORT2 -umutablecptrie_set(UMutableCPTrie *trie, UChar32 c, uint32_t value, UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return; - } - reinterpret_cast(trie)->set(c, value, *pErrorCode); -} - -U_CAPI void U_EXPORT2 -umutablecptrie_setRange(UMutableCPTrie *trie, UChar32 start, UChar32 end, - uint32_t value, UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return; - } - reinterpret_cast(trie)->setRange(start, end, value, *pErrorCode); -} - -/* Compact and internally serialize the trie. */ -U_CAPI UCPTrie * U_EXPORT2 -umutablecptrie_buildImmutable(UMutableCPTrie *trie, UCPTrieType type, UCPTrieValueWidth valueWidth, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return nullptr; - } - return reinterpret_cast(trie)->build(type, valueWidth, *pErrorCode); -} - -#ifdef UCPTRIE_DEBUG -U_CFUNC void umutablecptrie_setName(UMutableCPTrie *trie, const char *name) { - reinterpret_cast(trie)->name = name; -} -#endif +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// umutablecptrie.cpp (inspired by utrie2_builder.cpp) +// created: 2017dec29 Markus W. Scherer + +// #define UCPTRIE_DEBUG +#ifdef UCPTRIE_DEBUG +# include +#endif + +#include "unicode/utypes.h" +#include "unicode/ucptrie.h" +#include "unicode/umutablecptrie.h" +#include "unicode/uobject.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "uassert.h" +#include "ucptrie_impl.h" + +// ICU-20235 In case Microsoft math.h has defined this, undefine it. +#ifdef OVERFLOW +#undef OVERFLOW +#endif + +U_NAMESPACE_BEGIN + +namespace { + +constexpr int32_t MAX_UNICODE = 0x10ffff; + +constexpr int32_t UNICODE_LIMIT = 0x110000; +constexpr int32_t BMP_LIMIT = 0x10000; +constexpr int32_t ASCII_LIMIT = 0x80; + +constexpr int32_t I_LIMIT = UNICODE_LIMIT >> UCPTRIE_SHIFT_3; +constexpr int32_t BMP_I_LIMIT = BMP_LIMIT >> UCPTRIE_SHIFT_3; +constexpr int32_t ASCII_I_LIMIT = ASCII_LIMIT >> UCPTRIE_SHIFT_3; + +constexpr int32_t SMALL_DATA_BLOCKS_PER_BMP_BLOCK = (1 << (UCPTRIE_FAST_SHIFT - UCPTRIE_SHIFT_3)); + +// Flag values for data blocks. +constexpr uint8_t ALL_SAME = 0; +constexpr uint8_t MIXED = 1; +constexpr uint8_t SAME_AS = 2; + +/** Start with allocation of 16k data entries. */ +constexpr int32_t INITIAL_DATA_LENGTH = ((int32_t)1 << 14); + +/** Grow about 8x each time. */ +constexpr int32_t MEDIUM_DATA_LENGTH = ((int32_t)1 << 17); + +/** + * Maximum length of the build-time data array. + * One entry per 0x110000 code points. + */ +constexpr int32_t MAX_DATA_LENGTH = UNICODE_LIMIT; + +// Flag values for index-3 blocks while compacting/building. +constexpr uint8_t I3_NULL = 0; +constexpr uint8_t I3_BMP = 1; +constexpr uint8_t I3_16 = 2; +constexpr uint8_t I3_18 = 3; + +constexpr int32_t INDEX_3_18BIT_BLOCK_LENGTH = UCPTRIE_INDEX_3_BLOCK_LENGTH + UCPTRIE_INDEX_3_BLOCK_LENGTH / 8; + +class AllSameBlocks; +class MixedBlocks; + +class MutableCodePointTrie : public UMemory { +public: + MutableCodePointTrie(uint32_t initialValue, uint32_t errorValue, UErrorCode &errorCode); + MutableCodePointTrie(const MutableCodePointTrie &other, UErrorCode &errorCode); + MutableCodePointTrie(const MutableCodePointTrie &other) = delete; + ~MutableCodePointTrie(); + + MutableCodePointTrie &operator=(const MutableCodePointTrie &other) = delete; + + static MutableCodePointTrie *fromUCPMap(const UCPMap *map, UErrorCode &errorCode); + static MutableCodePointTrie *fromUCPTrie(const UCPTrie *trie, UErrorCode &errorCode); + + uint32_t get(UChar32 c) const; + int32_t getRange(UChar32 start, UCPMapValueFilter *filter, const void *context, + uint32_t *pValue) const; + + void set(UChar32 c, uint32_t value, UErrorCode &errorCode); + void setRange(UChar32 start, UChar32 end, uint32_t value, UErrorCode &errorCode); + + UCPTrie *build(UCPTrieType type, UCPTrieValueWidth valueWidth, UErrorCode &errorCode); + +private: + void clear(); + + bool ensureHighStart(UChar32 c); + int32_t allocDataBlock(int32_t blockLength); + int32_t getDataBlock(int32_t i); + + void maskValues(uint32_t mask); + UChar32 findHighStart() const; + int32_t compactWholeDataBlocks(int32_t fastILimit, AllSameBlocks &allSameBlocks); + int32_t compactData( + int32_t fastILimit, uint32_t *newData, int32_t newDataCapacity, + int32_t dataNullIndex, MixedBlocks &mixedBlocks, UErrorCode &errorCode); + int32_t compactIndex(int32_t fastILimit, MixedBlocks &mixedBlocks, UErrorCode &errorCode); + int32_t compactTrie(int32_t fastILimit, UErrorCode &errorCode); + + uint32_t *index = nullptr; + int32_t indexCapacity = 0; + int32_t index3NullOffset = -1; + uint32_t *data = nullptr; + int32_t dataCapacity = 0; + int32_t dataLength = 0; + int32_t dataNullOffset = -1; + + uint32_t origInitialValue; + uint32_t initialValue; + uint32_t errorValue; + UChar32 highStart; + uint32_t highValue; +#ifdef UCPTRIE_DEBUG +public: + const char *name; +#endif +private: + /** Temporary array while building the final data. */ + uint16_t *index16 = nullptr; + uint8_t flags[UNICODE_LIMIT >> UCPTRIE_SHIFT_3]; +}; + +MutableCodePointTrie::MutableCodePointTrie(uint32_t iniValue, uint32_t errValue, UErrorCode &errorCode) : + origInitialValue(iniValue), initialValue(iniValue), errorValue(errValue), + highStart(0), highValue(initialValue) +#ifdef UCPTRIE_DEBUG + , name("open") +#endif + { + if (U_FAILURE(errorCode)) { return; } + index = (uint32_t *)uprv_malloc(BMP_I_LIMIT * 4); + data = (uint32_t *)uprv_malloc(INITIAL_DATA_LENGTH * 4); + if (index == nullptr || data == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + indexCapacity = BMP_I_LIMIT; + dataCapacity = INITIAL_DATA_LENGTH; +} + +MutableCodePointTrie::MutableCodePointTrie(const MutableCodePointTrie &other, UErrorCode &errorCode) : + index3NullOffset(other.index3NullOffset), + dataNullOffset(other.dataNullOffset), + origInitialValue(other.origInitialValue), initialValue(other.initialValue), + errorValue(other.errorValue), + highStart(other.highStart), highValue(other.highValue) +#ifdef UCPTRIE_DEBUG + , name("mutable clone") +#endif + { + if (U_FAILURE(errorCode)) { return; } + int32_t iCapacity = highStart <= BMP_LIMIT ? BMP_I_LIMIT : I_LIMIT; + index = (uint32_t *)uprv_malloc(iCapacity * 4); + data = (uint32_t *)uprv_malloc(other.dataCapacity * 4); + if (index == nullptr || data == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + indexCapacity = iCapacity; + dataCapacity = other.dataCapacity; + + int32_t iLimit = highStart >> UCPTRIE_SHIFT_3; + uprv_memcpy(flags, other.flags, iLimit); + uprv_memcpy(index, other.index, iLimit * 4); + uprv_memcpy(data, other.data, (size_t)other.dataLength * 4); + dataLength = other.dataLength; + U_ASSERT(other.index16 == nullptr); +} + +MutableCodePointTrie::~MutableCodePointTrie() { + uprv_free(index); + uprv_free(data); + uprv_free(index16); +} + +MutableCodePointTrie *MutableCodePointTrie::fromUCPMap(const UCPMap *map, UErrorCode &errorCode) { + // Use the highValue as the initialValue to reduce the highStart. + uint32_t errorValue = ucpmap_get(map, -1); + uint32_t initialValue = ucpmap_get(map, 0x10ffff); + LocalPointer mutableTrie( + new MutableCodePointTrie(initialValue, errorValue, errorCode), + errorCode); + if (U_FAILURE(errorCode)) { + return nullptr; + } + UChar32 start = 0, end; + uint32_t value; + while ((end = ucpmap_getRange(map, start, UCPMAP_RANGE_NORMAL, 0, + nullptr, nullptr, &value)) >= 0) { + if (value != initialValue) { + if (start == end) { + mutableTrie->set(start, value, errorCode); + } else { + mutableTrie->setRange(start, end, value, errorCode); + } + } + start = end + 1; + } + if (U_SUCCESS(errorCode)) { + return mutableTrie.orphan(); + } else { + return nullptr; + } +} + +MutableCodePointTrie *MutableCodePointTrie::fromUCPTrie(const UCPTrie *trie, UErrorCode &errorCode) { + // Use the highValue as the initialValue to reduce the highStart. + uint32_t errorValue; + uint32_t initialValue; + switch (trie->valueWidth) { + case UCPTRIE_VALUE_BITS_16: + errorValue = trie->data.ptr16[trie->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET]; + initialValue = trie->data.ptr16[trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET]; + break; + case UCPTRIE_VALUE_BITS_32: + errorValue = trie->data.ptr32[trie->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET]; + initialValue = trie->data.ptr32[trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET]; + break; + case UCPTRIE_VALUE_BITS_8: + errorValue = trie->data.ptr8[trie->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET]; + initialValue = trie->data.ptr8[trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET]; + break; + default: + // Unreachable if the trie is properly initialized. + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + LocalPointer mutableTrie( + new MutableCodePointTrie(initialValue, errorValue, errorCode), + errorCode); + if (U_FAILURE(errorCode)) { + return nullptr; + } + UChar32 start = 0, end; + uint32_t value; + while ((end = ucptrie_getRange(trie, start, UCPMAP_RANGE_NORMAL, 0, + nullptr, nullptr, &value)) >= 0) { + if (value != initialValue) { + if (start == end) { + mutableTrie->set(start, value, errorCode); + } else { + mutableTrie->setRange(start, end, value, errorCode); + } + } + start = end + 1; + } + if (U_SUCCESS(errorCode)) { + return mutableTrie.orphan(); + } else { + return nullptr; + } +} + +void MutableCodePointTrie::clear() { + index3NullOffset = dataNullOffset = -1; + dataLength = 0; + highValue = initialValue = origInitialValue; + highStart = 0; + uprv_free(index16); + index16 = nullptr; +} + +uint32_t MutableCodePointTrie::get(UChar32 c) const { + if ((uint32_t)c > MAX_UNICODE) { + return errorValue; + } + if (c >= highStart) { + return highValue; + } + int32_t i = c >> UCPTRIE_SHIFT_3; + if (flags[i] == ALL_SAME) { + return index[i]; + } else { + return data[index[i] + (c & UCPTRIE_SMALL_DATA_MASK)]; + } +} + +inline uint32_t maybeFilterValue(uint32_t value, uint32_t initialValue, uint32_t nullValue, + UCPMapValueFilter *filter, const void *context) { + if (value == initialValue) { + value = nullValue; + } else if (filter != nullptr) { + value = filter(context, value); + } + return value; +} + +UChar32 MutableCodePointTrie::getRange( + UChar32 start, UCPMapValueFilter *filter, const void *context, + uint32_t *pValue) const { + if ((uint32_t)start > MAX_UNICODE) { + return U_SENTINEL; + } + if (start >= highStart) { + if (pValue != nullptr) { + uint32_t value = highValue; + if (filter != nullptr) { value = filter(context, value); } + *pValue = value; + } + return MAX_UNICODE; + } + uint32_t nullValue = initialValue; + if (filter != nullptr) { nullValue = filter(context, nullValue); } + UChar32 c = start; + uint32_t trieValue, value; + bool haveValue = false; + int32_t i = c >> UCPTRIE_SHIFT_3; + do { + if (flags[i] == ALL_SAME) { + uint32_t trieValue2 = index[i]; + if (haveValue) { + if (trieValue2 != trieValue) { + if (filter == nullptr || + maybeFilterValue(trieValue2, initialValue, nullValue, + filter, context) != value) { + return c - 1; + } + trieValue = trieValue2; // may or may not help + } + } else { + trieValue = trieValue2; + value = maybeFilterValue(trieValue2, initialValue, nullValue, filter, context); + if (pValue != nullptr) { *pValue = value; } + haveValue = true; + } + c = (c + UCPTRIE_SMALL_DATA_BLOCK_LENGTH) & ~UCPTRIE_SMALL_DATA_MASK; + } else /* MIXED */ { + int32_t di = index[i] + (c & UCPTRIE_SMALL_DATA_MASK); + uint32_t trieValue2 = data[di]; + if (haveValue) { + if (trieValue2 != trieValue) { + if (filter == nullptr || + maybeFilterValue(trieValue2, initialValue, nullValue, + filter, context) != value) { + return c - 1; + } + trieValue = trieValue2; // may or may not help + } + } else { + trieValue = trieValue2; + value = maybeFilterValue(trieValue2, initialValue, nullValue, filter, context); + if (pValue != nullptr) { *pValue = value; } + haveValue = true; + } + while ((++c & UCPTRIE_SMALL_DATA_MASK) != 0) { + trieValue2 = data[++di]; + if (trieValue2 != trieValue) { + if (filter == nullptr || + maybeFilterValue(trieValue2, initialValue, nullValue, + filter, context) != value) { + return c - 1; + } + } + trieValue = trieValue2; // may or may not help + } + } + ++i; + } while (c < highStart); + U_ASSERT(haveValue); + if (maybeFilterValue(highValue, initialValue, nullValue, + filter, context) != value) { + return c - 1; + } else { + return MAX_UNICODE; + } +} + +void +writeBlock(uint32_t *block, uint32_t value) { + uint32_t *limit = block + UCPTRIE_SMALL_DATA_BLOCK_LENGTH; + while (block < limit) { + *block++ = value; + } +} + +bool MutableCodePointTrie::ensureHighStart(UChar32 c) { + if (c >= highStart) { + // Round up to a UCPTRIE_CP_PER_INDEX_2_ENTRY boundary to simplify compaction. + c = (c + UCPTRIE_CP_PER_INDEX_2_ENTRY) & ~(UCPTRIE_CP_PER_INDEX_2_ENTRY - 1); + int32_t i = highStart >> UCPTRIE_SHIFT_3; + int32_t iLimit = c >> UCPTRIE_SHIFT_3; + if (iLimit > indexCapacity) { + uint32_t *newIndex = (uint32_t *)uprv_malloc(I_LIMIT * 4); + if (newIndex == nullptr) { return false; } + uprv_memcpy(newIndex, index, i * 4); + uprv_free(index); + index = newIndex; + indexCapacity = I_LIMIT; + } + do { + flags[i] = ALL_SAME; + index[i] = initialValue; + } while(++i < iLimit); + highStart = c; + } + return true; +} + +int32_t MutableCodePointTrie::allocDataBlock(int32_t blockLength) { + int32_t newBlock = dataLength; + int32_t newTop = newBlock + blockLength; + if (newTop > dataCapacity) { + int32_t capacity; + if (dataCapacity < MEDIUM_DATA_LENGTH) { + capacity = MEDIUM_DATA_LENGTH; + } else if (dataCapacity < MAX_DATA_LENGTH) { + capacity = MAX_DATA_LENGTH; + } else { + // Should never occur. + // Either MAX_DATA_LENGTH is incorrect, + // or the code writes more values than should be possible. + return -1; + } + uint32_t *newData = (uint32_t *)uprv_malloc(capacity * 4); + if (newData == nullptr) { + return -1; + } + uprv_memcpy(newData, data, (size_t)dataLength * 4); + uprv_free(data); + data = newData; + dataCapacity = capacity; + } + dataLength = newTop; + return newBlock; +} + +/** + * No error checking for illegal arguments. + * + * @return -1 if no new data block available (out of memory in data array) + * @internal + */ +int32_t MutableCodePointTrie::getDataBlock(int32_t i) { + if (flags[i] == MIXED) { + return index[i]; + } + if (i < BMP_I_LIMIT) { + int32_t newBlock = allocDataBlock(UCPTRIE_FAST_DATA_BLOCK_LENGTH); + if (newBlock < 0) { return newBlock; } + int32_t iStart = i & ~(SMALL_DATA_BLOCKS_PER_BMP_BLOCK -1); + int32_t iLimit = iStart + SMALL_DATA_BLOCKS_PER_BMP_BLOCK; + do { + U_ASSERT(flags[iStart] == ALL_SAME); + writeBlock(data + newBlock, index[iStart]); + flags[iStart] = MIXED; + index[iStart++] = newBlock; + newBlock += UCPTRIE_SMALL_DATA_BLOCK_LENGTH; + } while (iStart < iLimit); + return index[i]; + } else { + int32_t newBlock = allocDataBlock(UCPTRIE_SMALL_DATA_BLOCK_LENGTH); + if (newBlock < 0) { return newBlock; } + writeBlock(data + newBlock, index[i]); + flags[i] = MIXED; + index[i] = newBlock; + return newBlock; + } +} + +void MutableCodePointTrie::set(UChar32 c, uint32_t value, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { + return; + } + if ((uint32_t)c > MAX_UNICODE) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + int32_t block; + if (!ensureHighStart(c) || (block = getDataBlock(c >> UCPTRIE_SHIFT_3)) < 0) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + + data[block + (c & UCPTRIE_SMALL_DATA_MASK)] = value; +} + +void +fillBlock(uint32_t *block, UChar32 start, UChar32 limit, uint32_t value) { + uint32_t *pLimit = block + limit; + block += start; + while (block < pLimit) { + *block++ = value; + } +} + +void MutableCodePointTrie::setRange(UChar32 start, UChar32 end, uint32_t value, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { + return; + } + if ((uint32_t)start > MAX_UNICODE || (uint32_t)end > MAX_UNICODE || start > end) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (!ensureHighStart(end)) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + + UChar32 limit = end + 1; + if (start & UCPTRIE_SMALL_DATA_MASK) { + // Set partial block at [start..following block boundary[. + int32_t block = getDataBlock(start >> UCPTRIE_SHIFT_3); + if (block < 0) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + + UChar32 nextStart = (start + UCPTRIE_SMALL_DATA_MASK) & ~UCPTRIE_SMALL_DATA_MASK; + if (nextStart <= limit) { + fillBlock(data + block, start & UCPTRIE_SMALL_DATA_MASK, UCPTRIE_SMALL_DATA_BLOCK_LENGTH, + value); + start = nextStart; + } else { + fillBlock(data + block, start & UCPTRIE_SMALL_DATA_MASK, limit & UCPTRIE_SMALL_DATA_MASK, + value); + return; + } + } + + // Number of positions in the last, partial block. + int32_t rest = limit & UCPTRIE_SMALL_DATA_MASK; + + // Round down limit to a block boundary. + limit &= ~UCPTRIE_SMALL_DATA_MASK; + + // Iterate over all-value blocks. + while (start < limit) { + int32_t i = start >> UCPTRIE_SHIFT_3; + if (flags[i] == ALL_SAME) { + index[i] = value; + } else /* MIXED */ { + fillBlock(data + index[i], 0, UCPTRIE_SMALL_DATA_BLOCK_LENGTH, value); + } + start += UCPTRIE_SMALL_DATA_BLOCK_LENGTH; + } + + if (rest > 0) { + // Set partial block at [last block boundary..limit[. + int32_t block = getDataBlock(start >> UCPTRIE_SHIFT_3); + if (block < 0) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + + fillBlock(data + block, 0, rest, value); + } +} + +/* compaction --------------------------------------------------------------- */ + +void MutableCodePointTrie::maskValues(uint32_t mask) { + initialValue &= mask; + errorValue &= mask; + highValue &= mask; + int32_t iLimit = highStart >> UCPTRIE_SHIFT_3; + for (int32_t i = 0; i < iLimit; ++i) { + if (flags[i] == ALL_SAME) { + index[i] &= mask; + } + } + for (int32_t i = 0; i < dataLength; ++i) { + data[i] &= mask; + } +} + +template +bool equalBlocks(const UIntA *s, const UIntB *t, int32_t length) { + while (length > 0 && *s == *t) { + ++s; + ++t; + --length; + } + return length == 0; +} + +bool allValuesSameAs(const uint32_t *p, int32_t length, uint32_t value) { + const uint32_t *pLimit = p + length; + while (p < pLimit && *p == value) { ++p; } + return p == pLimit; +} + +/** Search for an identical block. */ +int32_t findSameBlock(const uint16_t *p, int32_t pStart, int32_t length, + const uint16_t *q, int32_t qStart, int32_t blockLength) { + // Ensure that we do not even partially get past length. + length -= blockLength; + + q += qStart; + while (pStart <= length) { + if (equalBlocks(p + pStart, q, blockLength)) { + return pStart; + } + ++pStart; + } + return -1; +} + +int32_t findAllSameBlock(const uint32_t *p, int32_t start, int32_t limit, + uint32_t value, int32_t blockLength) { + // Ensure that we do not even partially get past limit. + limit -= blockLength; + + for (int32_t block = start; block <= limit; ++block) { + if (p[block] == value) { + for (int32_t i = 1;; ++i) { + if (i == blockLength) { + return block; + } + if (p[block + i] != value) { + block += i; + break; + } + } + } + } + return -1; +} + +/** + * Look for maximum overlap of the beginning of the other block + * with the previous, adjacent block. + */ +template +int32_t getOverlap(const UIntA *p, int32_t length, + const UIntB *q, int32_t qStart, int32_t blockLength) { + int32_t overlap = blockLength - 1; + U_ASSERT(overlap <= length); + q += qStart; + while (overlap > 0 && !equalBlocks(p + (length - overlap), q, overlap)) { + --overlap; + } + return overlap; +} + +int32_t getAllSameOverlap(const uint32_t *p, int32_t length, uint32_t value, + int32_t blockLength) { + int32_t min = length - (blockLength - 1); + int32_t i = length; + while (min < i && p[i - 1] == value) { --i; } + return length - i; +} + +bool isStartOfSomeFastBlock(uint32_t dataOffset, const uint32_t index[], int32_t fastILimit) { + for (int32_t i = 0; i < fastILimit; i += SMALL_DATA_BLOCKS_PER_BMP_BLOCK) { + if (index[i] == dataOffset) { + return true; + } + } + return false; +} + +/** + * Finds the start of the last range in the trie by enumerating backward. + * Indexes for code points higher than this will be omitted. + */ +UChar32 MutableCodePointTrie::findHighStart() const { + int32_t i = highStart >> UCPTRIE_SHIFT_3; + while (i > 0) { + bool match; + if (flags[--i] == ALL_SAME) { + match = index[i] == highValue; + } else /* MIXED */ { + const uint32_t *p = data + index[i]; + for (int32_t j = 0;; ++j) { + if (j == UCPTRIE_SMALL_DATA_BLOCK_LENGTH) { + match = true; + break; + } + if (p[j] != highValue) { + match = false; + break; + } + } + } + if (!match) { + return (i + 1) << UCPTRIE_SHIFT_3; + } + } + return 0; +} + +class AllSameBlocks { +public: + static constexpr int32_t NEW_UNIQUE = -1; + static constexpr int32_t OVERFLOW = -2; + + AllSameBlocks() : length(0), mostRecent(-1) {} + + int32_t findOrAdd(int32_t index, int32_t count, uint32_t value) { + if (mostRecent >= 0 && values[mostRecent] == value) { + refCounts[mostRecent] += count; + return indexes[mostRecent]; + } + for (int32_t i = 0; i < length; ++i) { + if (values[i] == value) { + mostRecent = i; + refCounts[i] += count; + return indexes[i]; + } + } + if (length == CAPACITY) { + return OVERFLOW; + } + mostRecent = length; + indexes[length] = index; + values[length] = value; + refCounts[length++] = count; + return NEW_UNIQUE; + } + + /** Replaces the block which has the lowest reference count. */ + void add(int32_t index, int32_t count, uint32_t value) { + U_ASSERT(length == CAPACITY); + int32_t least = -1; + int32_t leastCount = I_LIMIT; + for (int32_t i = 0; i < length; ++i) { + U_ASSERT(values[i] != value); + if (refCounts[i] < leastCount) { + least = i; + leastCount = refCounts[i]; + } + } + U_ASSERT(least >= 0); + mostRecent = least; + indexes[least] = index; + values[least] = value; + refCounts[least] = count; + } + + int32_t findMostUsed() const { + if (length == 0) { return -1; } + int32_t max = -1; + int32_t maxCount = 0; + for (int32_t i = 0; i < length; ++i) { + if (refCounts[i] > maxCount) { + max = i; + maxCount = refCounts[i]; + } + } + return indexes[max]; + } + +private: + static constexpr int32_t CAPACITY = 32; + + int32_t length; + int32_t mostRecent; + + int32_t indexes[CAPACITY]; + uint32_t values[CAPACITY]; + int32_t refCounts[CAPACITY]; +}; + +// Custom hash table for mixed-value blocks to be found anywhere in the +// compacted data or index so far. +class MixedBlocks { +public: + MixedBlocks() {} + ~MixedBlocks() { + uprv_free(table); + } + + bool init(int32_t maxLength, int32_t newBlockLength) { + // We store actual data indexes + 1 to reserve 0 for empty entries. + int32_t maxDataIndex = maxLength - newBlockLength + 1; + int32_t newLength; + if (maxDataIndex <= 0xfff) { // 4k + newLength = 6007; + shift = 12; + mask = 0xfff; + } else if (maxDataIndex <= 0x7fff) { // 32k + newLength = 50021; + shift = 15; + mask = 0x7fff; + } else if (maxDataIndex <= 0x1ffff) { // 128k + newLength = 200003; + shift = 17; + mask = 0x1ffff; + } else { + // maxDataIndex up to around MAX_DATA_LENGTH, ca. 1.1M + newLength = 1500007; + shift = 21; + mask = 0x1fffff; + } + if (newLength > capacity) { + uprv_free(table); + table = (uint32_t *)uprv_malloc(newLength * 4); + if (table == nullptr) { + return false; + } + capacity = newLength; + } + length = newLength; + uprv_memset(table, 0, length * 4); + + blockLength = newBlockLength; + return true; + } + + template + void extend(const UInt *data, int32_t minStart, int32_t prevDataLength, int32_t newDataLength) { + int32_t start = prevDataLength - blockLength; + if (start >= minStart) { + ++start; // Skip the last block that we added last time. + } else { + start = minStart; // Begin with the first full block. + } + for (int32_t end = newDataLength - blockLength; start <= end; ++start) { + uint32_t hashCode = makeHashCode(data, start); + addEntry(data, start, hashCode, start); + } + } + + template + int32_t findBlock(const UIntA *data, const UIntB *blockData, int32_t blockStart) const { + uint32_t hashCode = makeHashCode(blockData, blockStart); + int32_t entryIndex = findEntry(data, blockData, blockStart, hashCode); + if (entryIndex >= 0) { + return (table[entryIndex] & mask) - 1; + } else { + return -1; + } + } + + int32_t findAllSameBlock(const uint32_t *data, uint32_t blockValue) const { + uint32_t hashCode = makeHashCode(blockValue); + int32_t entryIndex = findEntry(data, blockValue, hashCode); + if (entryIndex >= 0) { + return (table[entryIndex] & mask) - 1; + } else { + return -1; + } + } + +private: + template + uint32_t makeHashCode(const UInt *blockData, int32_t blockStart) const { + int32_t blockLimit = blockStart + blockLength; + uint32_t hashCode = blockData[blockStart++]; + do { + hashCode = 37 * hashCode + blockData[blockStart++]; + } while (blockStart < blockLimit); + return hashCode; + } + + uint32_t makeHashCode(uint32_t blockValue) const { + uint32_t hashCode = blockValue; + for (int32_t i = 1; i < blockLength; ++i) { + hashCode = 37 * hashCode + blockValue; + } + return hashCode; + } + + template + void addEntry(const UInt *data, int32_t blockStart, uint32_t hashCode, int32_t dataIndex) { + U_ASSERT(0 <= dataIndex && dataIndex < (int32_t)mask); + int32_t entryIndex = findEntry(data, data, blockStart, hashCode); + if (entryIndex < 0) { + table[~entryIndex] = (hashCode << shift) | (dataIndex + 1); + } + } + + template + int32_t findEntry(const UIntA *data, const UIntB *blockData, int32_t blockStart, + uint32_t hashCode) const { + uint32_t shiftedHashCode = hashCode << shift; + int32_t initialEntryIndex = (hashCode % (length - 1)) + 1; // 1..length-1 + for (int32_t entryIndex = initialEntryIndex;;) { + uint32_t entry = table[entryIndex]; + if (entry == 0) { + return ~entryIndex; + } + if ((entry & ~mask) == shiftedHashCode) { + int32_t dataIndex = (entry & mask) - 1; + if (equalBlocks(data + dataIndex, blockData + blockStart, blockLength)) { + return entryIndex; + } + } + entryIndex = nextIndex(initialEntryIndex, entryIndex); + } + } + + int32_t findEntry(const uint32_t *data, uint32_t blockValue, uint32_t hashCode) const { + uint32_t shiftedHashCode = hashCode << shift; + int32_t initialEntryIndex = (hashCode % (length - 1)) + 1; // 1..length-1 + for (int32_t entryIndex = initialEntryIndex;;) { + uint32_t entry = table[entryIndex]; + if (entry == 0) { + return ~entryIndex; + } + if ((entry & ~mask) == shiftedHashCode) { + int32_t dataIndex = (entry & mask) - 1; + if (allValuesSameAs(data + dataIndex, blockLength, blockValue)) { + return entryIndex; + } + } + entryIndex = nextIndex(initialEntryIndex, entryIndex); + } + } + + inline int32_t nextIndex(int32_t initialEntryIndex, int32_t entryIndex) const { + // U_ASSERT(0 < initialEntryIndex && initialEntryIndex < length); + return (entryIndex + initialEntryIndex) % length; + } + + // Hash table. + // The length is a prime number, larger than the maximum data length. + // The "shift" lower bits store a data index + 1. + // The remaining upper bits store a partial hashCode of the block data values. + uint32_t *table = nullptr; + int32_t capacity = 0; + int32_t length = 0; + int32_t shift = 0; + uint32_t mask = 0; + + int32_t blockLength = 0; +}; + +int32_t MutableCodePointTrie::compactWholeDataBlocks(int32_t fastILimit, AllSameBlocks &allSameBlocks) { +#ifdef UCPTRIE_DEBUG + bool overflow = false; +#endif + + // ASCII data will be stored as a linear table, even if the following code + // does not yet count it that way. + int32_t newDataCapacity = ASCII_LIMIT; + // Add room for a small data null block in case it would match the start of + // a fast data block where dataNullOffset must not be set in that case. + newDataCapacity += UCPTRIE_SMALL_DATA_BLOCK_LENGTH; + // Add room for special values (errorValue, highValue) and padding. + newDataCapacity += 4; + int32_t iLimit = highStart >> UCPTRIE_SHIFT_3; + int32_t blockLength = UCPTRIE_FAST_DATA_BLOCK_LENGTH; + int32_t inc = SMALL_DATA_BLOCKS_PER_BMP_BLOCK; + for (int32_t i = 0; i < iLimit; i += inc) { + if (i == fastILimit) { + blockLength = UCPTRIE_SMALL_DATA_BLOCK_LENGTH; + inc = 1; + } + uint32_t value = index[i]; + if (flags[i] == MIXED) { + // Really mixed? + const uint32_t *p = data + value; + value = *p; + if (allValuesSameAs(p + 1, blockLength - 1, value)) { + flags[i] = ALL_SAME; + index[i] = value; + // Fall through to ALL_SAME handling. + } else { + newDataCapacity += blockLength; + continue; + } + } else { + U_ASSERT(flags[i] == ALL_SAME); + if (inc > 1) { + // Do all of the fast-range data block's ALL_SAME parts have the same value? + bool allSame = true; + int32_t next_i = i + inc; + for (int32_t j = i + 1; j < next_i; ++j) { + U_ASSERT(flags[j] == ALL_SAME); + if (index[j] != value) { + allSame = false; + break; + } + } + if (!allSame) { + // Turn it into a MIXED block. + if (getDataBlock(i) < 0) { + return -1; + } + newDataCapacity += blockLength; + continue; + } + } + } + // Is there another ALL_SAME block with the same value? + int32_t other = allSameBlocks.findOrAdd(i, inc, value); + if (other == AllSameBlocks::OVERFLOW) { + // The fixed-size array overflowed. Slow check for a duplicate block. +#ifdef UCPTRIE_DEBUG + if (!overflow) { + puts("UCPTrie AllSameBlocks overflow"); + overflow = true; + } +#endif + int32_t jInc = SMALL_DATA_BLOCKS_PER_BMP_BLOCK; + for (int32_t j = 0;; j += jInc) { + if (j == i) { + allSameBlocks.add(i, inc, value); + break; + } + if (j == fastILimit) { + jInc = 1; + } + if (flags[j] == ALL_SAME && index[j] == value) { + allSameBlocks.add(j, jInc + inc, value); + other = j; + break; + // We could keep counting blocks with the same value + // before we add the first one, which may improve compaction in rare cases, + // but it would make it slower. + } + } + } + if (other >= 0) { + flags[i] = SAME_AS; + index[i] = other; + } else { + // New unique same-value block. + newDataCapacity += blockLength; + } + } + return newDataCapacity; +} + +#ifdef UCPTRIE_DEBUG +# define DEBUG_DO(expr) expr +#else +# define DEBUG_DO(expr) +#endif + +#ifdef UCPTRIE_DEBUG +// Braille symbols: U+28xx = UTF-8 E2 A0 80..E2 A3 BF +int32_t appendValue(char s[], int32_t length, uint32_t value) { + value ^= value >> 16; + value ^= value >> 8; + s[length] = 0xE2; + s[length + 1] = (char)(0xA0 + ((value >> 6) & 3)); + s[length + 2] = (char)(0x80 + (value & 0x3F)); + return length + 3; +} + +void printBlock(const uint32_t *block, int32_t blockLength, uint32_t value, + UChar32 start, int32_t overlap, uint32_t initialValue) { + char s[UCPTRIE_FAST_DATA_BLOCK_LENGTH * 3 + 3]; + int32_t length = 0; + int32_t i; + for (i = 0; i < overlap; ++i) { + length = appendValue(s, length, 0); // Braille blank + } + s[length++] = '|'; + for (; i < blockLength; ++i) { + if (block != nullptr) { + value = block[i]; + } + if (value == initialValue) { + value = 0x40; // Braille lower left dot + } + length = appendValue(s, length, value); + } + s[length] = 0; + start += overlap; + if (start <= 0xffff) { + printf(" %04lX %s|\n", (long)start, s); + } else if (start <= 0xfffff) { + printf(" %5lX %s|\n", (long)start, s); + } else { + printf(" %6lX %s|\n", (long)start, s); + } +} +#endif + +/** + * Compacts a build-time trie. + * + * The compaction + * - removes blocks that are identical with earlier ones + * - overlaps each new non-duplicate block as much as possible with the previously-written one + * - works with fast-range data blocks whose length is a multiple of that of + * higher-code-point data blocks + * + * It does not try to find an optimal order of writing, deduplicating, and overlapping blocks. + */ +int32_t MutableCodePointTrie::compactData( + int32_t fastILimit, uint32_t *newData, int32_t newDataCapacity, + int32_t dataNullIndex, MixedBlocks &mixedBlocks, UErrorCode &errorCode) { +#ifdef UCPTRIE_DEBUG + int32_t countSame=0, sumOverlaps=0; + bool printData = dataLength == 29088 /* line.brk */ || + // dataLength == 30048 /* CanonIterData */ || + dataLength == 50400 /* zh.txt~stroke */; +#endif + + // The linear ASCII data has been copied into newData already. + int32_t newDataLength = 0; + for (int32_t i = 0; newDataLength < ASCII_LIMIT; + newDataLength += UCPTRIE_FAST_DATA_BLOCK_LENGTH, i += SMALL_DATA_BLOCKS_PER_BMP_BLOCK) { + index[i] = newDataLength; +#ifdef UCPTRIE_DEBUG + if (printData) { + printBlock(newData + newDataLength, UCPTRIE_FAST_DATA_BLOCK_LENGTH, 0, newDataLength, 0, initialValue); + } +#endif + } + + int32_t blockLength = UCPTRIE_FAST_DATA_BLOCK_LENGTH; + if (!mixedBlocks.init(newDataCapacity, blockLength)) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + mixedBlocks.extend(newData, 0, 0, newDataLength); + + int32_t iLimit = highStart >> UCPTRIE_SHIFT_3; + int32_t inc = SMALL_DATA_BLOCKS_PER_BMP_BLOCK; + int32_t fastLength = 0; + for (int32_t i = ASCII_I_LIMIT; i < iLimit; i += inc) { + if (i == fastILimit) { + blockLength = UCPTRIE_SMALL_DATA_BLOCK_LENGTH; + inc = 1; + fastLength = newDataLength; + if (!mixedBlocks.init(newDataCapacity, blockLength)) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + mixedBlocks.extend(newData, 0, 0, newDataLength); + } + if (flags[i] == ALL_SAME) { + uint32_t value = index[i]; + // Find an earlier part of the data array of length blockLength + // that is filled with this value. + int32_t n = mixedBlocks.findAllSameBlock(newData, value); + // If we find a match, and the current block is the data null block, + // and it is not a fast block but matches the start of a fast block, + // then we need to continue looking. + // This is because this small block is shorter than the fast block, + // and not all of the rest of the fast block is filled with this value. + // Otherwise trie.getRange() would detect that the fast block starts at + // dataNullOffset and assume incorrectly that it is filled with the null value. + while (n >= 0 && i == dataNullIndex && i >= fastILimit && n < fastLength && + isStartOfSomeFastBlock(n, index, fastILimit)) { + n = findAllSameBlock(newData, n + 1, newDataLength, value, blockLength); + } + if (n >= 0) { + DEBUG_DO(++countSame); + index[i] = n; + } else { + n = getAllSameOverlap(newData, newDataLength, value, blockLength); + DEBUG_DO(sumOverlaps += n); +#ifdef UCPTRIE_DEBUG + if (printData) { + printBlock(nullptr, blockLength, value, i << UCPTRIE_SHIFT_3, n, initialValue); + } +#endif + index[i] = newDataLength - n; + int32_t prevDataLength = newDataLength; + while (n < blockLength) { + newData[newDataLength++] = value; + ++n; + } + mixedBlocks.extend(newData, 0, prevDataLength, newDataLength); + } + } else if (flags[i] == MIXED) { + const uint32_t *block = data + index[i]; + int32_t n = mixedBlocks.findBlock(newData, block, 0); + if (n >= 0) { + DEBUG_DO(++countSame); + index[i] = n; + } else { + n = getOverlap(newData, newDataLength, block, 0, blockLength); + DEBUG_DO(sumOverlaps += n); +#ifdef UCPTRIE_DEBUG + if (printData) { + printBlock(block, blockLength, 0, i << UCPTRIE_SHIFT_3, n, initialValue); + } +#endif + index[i] = newDataLength - n; + int32_t prevDataLength = newDataLength; + while (n < blockLength) { + newData[newDataLength++] = block[n++]; + } + mixedBlocks.extend(newData, 0, prevDataLength, newDataLength); + } + } else /* SAME_AS */ { + uint32_t j = index[i]; + index[i] = index[j]; + } + } + +#ifdef UCPTRIE_DEBUG + /* we saved some space */ + printf("compacting UCPTrie: count of 32-bit data words %lu->%lu countSame=%ld sumOverlaps=%ld\n", + (long)dataLength, (long)newDataLength, (long)countSame, (long)sumOverlaps); +#endif + return newDataLength; +} + +int32_t MutableCodePointTrie::compactIndex(int32_t fastILimit, MixedBlocks &mixedBlocks, + UErrorCode &errorCode) { + int32_t fastIndexLength = fastILimit >> (UCPTRIE_FAST_SHIFT - UCPTRIE_SHIFT_3); + if ((highStart >> UCPTRIE_FAST_SHIFT) <= fastIndexLength) { + // Only the linear fast index, no multi-stage index tables. + index3NullOffset = UCPTRIE_NO_INDEX3_NULL_OFFSET; + return fastIndexLength; + } + + // Condense the fast index table. + // Also, does it contain an index-3 block with all dataNullOffset? + uint16_t fastIndex[UCPTRIE_BMP_INDEX_LENGTH]; // fastIndexLength + int32_t i3FirstNull = -1; + for (int32_t i = 0, j = 0; i < fastILimit; ++j) { + uint32_t i3 = index[i]; + fastIndex[j] = (uint16_t)i3; + if (i3 == (uint32_t)dataNullOffset) { + if (i3FirstNull < 0) { + i3FirstNull = j; + } else if (index3NullOffset < 0 && + (j - i3FirstNull + 1) == UCPTRIE_INDEX_3_BLOCK_LENGTH) { + index3NullOffset = i3FirstNull; + } + } else { + i3FirstNull = -1; + } + // Set the index entries that compactData() skipped. + // Needed when the multi-stage index covers the fast index range as well. + int32_t iNext = i + SMALL_DATA_BLOCKS_PER_BMP_BLOCK; + while (++i < iNext) { + i3 += UCPTRIE_SMALL_DATA_BLOCK_LENGTH; + index[i] = i3; + } + } + + if (!mixedBlocks.init(fastIndexLength, UCPTRIE_INDEX_3_BLOCK_LENGTH)) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + mixedBlocks.extend(fastIndex, 0, 0, fastIndexLength); + + // Examine index-3 blocks. For each determine one of: + // - same as the index-3 null block + // - same as a fast-index block + // - 16-bit indexes + // - 18-bit indexes + // We store this in the first flags entry for the index-3 block. + // + // Also determine an upper limit for the index-3 table length. + int32_t index3Capacity = 0; + i3FirstNull = index3NullOffset; + bool hasLongI3Blocks = false; + // If the fast index covers the whole BMP, then + // the multi-stage index is only for supplementary code points. + // Otherwise, the multi-stage index covers all of Unicode. + int32_t iStart = fastILimit < BMP_I_LIMIT ? 0 : BMP_I_LIMIT; + int32_t iLimit = highStart >> UCPTRIE_SHIFT_3; + for (int32_t i = iStart; i < iLimit;) { + int32_t j = i; + int32_t jLimit = i + UCPTRIE_INDEX_3_BLOCK_LENGTH; + uint32_t oredI3 = 0; + bool isNull = true; + do { + uint32_t i3 = index[j]; + oredI3 |= i3; + if (i3 != (uint32_t)dataNullOffset) { + isNull = false; + } + } while (++j < jLimit); + if (isNull) { + flags[i] = I3_NULL; + if (i3FirstNull < 0) { + if (oredI3 <= 0xffff) { + index3Capacity += UCPTRIE_INDEX_3_BLOCK_LENGTH; + } else { + index3Capacity += INDEX_3_18BIT_BLOCK_LENGTH; + hasLongI3Blocks = true; + } + i3FirstNull = 0; + } + } else { + if (oredI3 <= 0xffff) { + int32_t n = mixedBlocks.findBlock(fastIndex, index, i); + if (n >= 0) { + flags[i] = I3_BMP; + index[i] = n; + } else { + flags[i] = I3_16; + index3Capacity += UCPTRIE_INDEX_3_BLOCK_LENGTH; + } + } else { + flags[i] = I3_18; + index3Capacity += INDEX_3_18BIT_BLOCK_LENGTH; + hasLongI3Blocks = true; + } + } + i = j; + } + + int32_t index2Capacity = (iLimit - iStart) >> UCPTRIE_SHIFT_2_3; + + // Length of the index-1 table, rounded up. + int32_t index1Length = (index2Capacity + UCPTRIE_INDEX_2_MASK) >> UCPTRIE_SHIFT_1_2; + + // Index table: Fast index, index-1, index-3, index-2. + // +1 for possible index table padding. + int32_t index16Capacity = fastIndexLength + index1Length + index3Capacity + index2Capacity + 1; + index16 = (uint16_t *)uprv_malloc(index16Capacity * 2); + if (index16 == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + uprv_memcpy(index16, fastIndex, fastIndexLength * 2); + + if (!mixedBlocks.init(index16Capacity, UCPTRIE_INDEX_3_BLOCK_LENGTH)) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + MixedBlocks longI3Blocks; + if (hasLongI3Blocks) { + if (!longI3Blocks.init(index16Capacity, INDEX_3_18BIT_BLOCK_LENGTH)) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + } + + // Compact the index-3 table and write an uncompacted version of the index-2 table. + uint16_t index2[UNICODE_LIMIT >> UCPTRIE_SHIFT_2]; // index2Capacity + int32_t i2Length = 0; + i3FirstNull = index3NullOffset; + int32_t index3Start = fastIndexLength + index1Length; + int32_t indexLength = index3Start; + for (int32_t i = iStart; i < iLimit; i += UCPTRIE_INDEX_3_BLOCK_LENGTH) { + int32_t i3; + uint8_t f = flags[i]; + if (f == I3_NULL && i3FirstNull < 0) { + // First index-3 null block. Write & overlap it like a normal block, then remember it. + f = dataNullOffset <= 0xffff ? I3_16 : I3_18; + i3FirstNull = 0; + } + if (f == I3_NULL) { + i3 = index3NullOffset; + } else if (f == I3_BMP) { + i3 = index[i]; + } else if (f == I3_16) { + int32_t n = mixedBlocks.findBlock(index16, index, i); + if (n >= 0) { + i3 = n; + } else { + if (indexLength == index3Start) { + // No overlap at the boundary between the index-1 and index-3 tables. + n = 0; + } else { + n = getOverlap(index16, indexLength, + index, i, UCPTRIE_INDEX_3_BLOCK_LENGTH); + } + i3 = indexLength - n; + int32_t prevIndexLength = indexLength; + while (n < UCPTRIE_INDEX_3_BLOCK_LENGTH) { + index16[indexLength++] = index[i + n++]; + } + mixedBlocks.extend(index16, index3Start, prevIndexLength, indexLength); + if (hasLongI3Blocks) { + longI3Blocks.extend(index16, index3Start, prevIndexLength, indexLength); + } + } + } else { + U_ASSERT(f == I3_18); + U_ASSERT(hasLongI3Blocks); + // Encode an index-3 block that contains one or more data indexes exceeding 16 bits. + int32_t j = i; + int32_t jLimit = i + UCPTRIE_INDEX_3_BLOCK_LENGTH; + int32_t k = indexLength; + do { + ++k; + uint32_t v = index[j++]; + uint32_t upperBits = (v & 0x30000) >> 2; + index16[k++] = v; + v = index[j++]; + upperBits |= (v & 0x30000) >> 4; + index16[k++] = v; + v = index[j++]; + upperBits |= (v & 0x30000) >> 6; + index16[k++] = v; + v = index[j++]; + upperBits |= (v & 0x30000) >> 8; + index16[k++] = v; + v = index[j++]; + upperBits |= (v & 0x30000) >> 10; + index16[k++] = v; + v = index[j++]; + upperBits |= (v & 0x30000) >> 12; + index16[k++] = v; + v = index[j++]; + upperBits |= (v & 0x30000) >> 14; + index16[k++] = v; + v = index[j++]; + upperBits |= (v & 0x30000) >> 16; + index16[k++] = v; + index16[k - 9] = upperBits; + } while (j < jLimit); + int32_t n = longI3Blocks.findBlock(index16, index16, indexLength); + if (n >= 0) { + i3 = n | 0x8000; + } else { + if (indexLength == index3Start) { + // No overlap at the boundary between the index-1 and index-3 tables. + n = 0; + } else { + n = getOverlap(index16, indexLength, + index16, indexLength, INDEX_3_18BIT_BLOCK_LENGTH); + } + i3 = (indexLength - n) | 0x8000; + int32_t prevIndexLength = indexLength; + if (n > 0) { + int32_t start = indexLength; + while (n < INDEX_3_18BIT_BLOCK_LENGTH) { + index16[indexLength++] = index16[start + n++]; + } + } else { + indexLength += INDEX_3_18BIT_BLOCK_LENGTH; + } + mixedBlocks.extend(index16, index3Start, prevIndexLength, indexLength); + if (hasLongI3Blocks) { + longI3Blocks.extend(index16, index3Start, prevIndexLength, indexLength); + } + } + } + if (index3NullOffset < 0 && i3FirstNull >= 0) { + index3NullOffset = i3; + } + // Set the index-2 table entry. + index2[i2Length++] = i3; + } + U_ASSERT(i2Length == index2Capacity); + U_ASSERT(indexLength <= index3Start + index3Capacity); + + if (index3NullOffset < 0) { + index3NullOffset = UCPTRIE_NO_INDEX3_NULL_OFFSET; + } + if (indexLength >= (UCPTRIE_NO_INDEX3_NULL_OFFSET + UCPTRIE_INDEX_3_BLOCK_LENGTH)) { + // The index-3 offsets exceed 15 bits, or + // the last one cannot be distinguished from the no-null-block value. + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + // Compact the index-2 table and write the index-1 table. + static_assert(UCPTRIE_INDEX_2_BLOCK_LENGTH == UCPTRIE_INDEX_3_BLOCK_LENGTH, + "must re-init mixedBlocks"); + int32_t blockLength = UCPTRIE_INDEX_2_BLOCK_LENGTH; + int32_t i1 = fastIndexLength; + for (int32_t i = 0; i < i2Length; i += blockLength) { + int32_t n; + if ((i2Length - i) >= blockLength) { + // normal block + U_ASSERT(blockLength == UCPTRIE_INDEX_2_BLOCK_LENGTH); + n = mixedBlocks.findBlock(index16, index2, i); + } else { + // highStart is inside the last index-2 block. Shorten it. + blockLength = i2Length - i; + n = findSameBlock(index16, index3Start, indexLength, + index2, i, blockLength); + } + int32_t i2; + if (n >= 0) { + i2 = n; + } else { + if (indexLength == index3Start) { + // No overlap at the boundary between the index-1 and index-3/2 tables. + n = 0; + } else { + n = getOverlap(index16, indexLength, index2, i, blockLength); + } + i2 = indexLength - n; + int32_t prevIndexLength = indexLength; + while (n < blockLength) { + index16[indexLength++] = index2[i + n++]; + } + mixedBlocks.extend(index16, index3Start, prevIndexLength, indexLength); + } + // Set the index-1 table entry. + index16[i1++] = i2; + } + U_ASSERT(i1 == index3Start); + U_ASSERT(indexLength <= index16Capacity); + +#ifdef UCPTRIE_DEBUG + /* we saved some space */ + printf("compacting UCPTrie: count of 16-bit index words %lu->%lu\n", + (long)iLimit, (long)indexLength); +#endif + + return indexLength; +} + +int32_t MutableCodePointTrie::compactTrie(int32_t fastILimit, UErrorCode &errorCode) { + // Find the real highStart and round it up. + U_ASSERT((highStart & (UCPTRIE_CP_PER_INDEX_2_ENTRY - 1)) == 0); + highValue = get(MAX_UNICODE); + int32_t realHighStart = findHighStart(); + realHighStart = (realHighStart + (UCPTRIE_CP_PER_INDEX_2_ENTRY - 1)) & + ~(UCPTRIE_CP_PER_INDEX_2_ENTRY - 1); + if (realHighStart == UNICODE_LIMIT) { + highValue = initialValue; + } + +#ifdef UCPTRIE_DEBUG + printf("UCPTrie: highStart U+%06lx highValue 0x%lx initialValue 0x%lx\n", + (long)realHighStart, (long)highValue, (long)initialValue); +#endif + + // We always store indexes and data values for the fast range. + // Pin highStart to the top of that range while building. + UChar32 fastLimit = fastILimit << UCPTRIE_SHIFT_3; + if (realHighStart < fastLimit) { + for (int32_t i = (realHighStart >> UCPTRIE_SHIFT_3); i < fastILimit; ++i) { + flags[i] = ALL_SAME; + index[i] = highValue; + } + highStart = fastLimit; + } else { + highStart = realHighStart; + } + + uint32_t asciiData[ASCII_LIMIT]; + for (int32_t i = 0; i < ASCII_LIMIT; ++i) { + asciiData[i] = get(i); + } + + // First we look for which data blocks have the same value repeated over the whole block, + // deduplicate such blocks, find a good null data block (for faster enumeration), + // and get an upper bound for the necessary data array length. + AllSameBlocks allSameBlocks; + int32_t newDataCapacity = compactWholeDataBlocks(fastILimit, allSameBlocks); + if (newDataCapacity < 0) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + uint32_t *newData = (uint32_t *)uprv_malloc(newDataCapacity * 4); + if (newData == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + uprv_memcpy(newData, asciiData, sizeof(asciiData)); + + int32_t dataNullIndex = allSameBlocks.findMostUsed(); + + MixedBlocks mixedBlocks; + int32_t newDataLength = compactData(fastILimit, newData, newDataCapacity, + dataNullIndex, mixedBlocks, errorCode); + if (U_FAILURE(errorCode)) { return 0; } + U_ASSERT(newDataLength <= newDataCapacity); + uprv_free(data); + data = newData; + dataCapacity = newDataCapacity; + dataLength = newDataLength; + if (dataLength > (0x3ffff + UCPTRIE_SMALL_DATA_BLOCK_LENGTH)) { + // The offset of the last data block is too high to be stored in the index table. + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + if (dataNullIndex >= 0) { + dataNullOffset = index[dataNullIndex]; +#ifdef UCPTRIE_DEBUG + if (data[dataNullOffset] != initialValue) { + printf("UCPTrie initialValue %lx -> more common nullValue %lx\n", + (long)initialValue, (long)data[dataNullOffset]); + } +#endif + initialValue = data[dataNullOffset]; + } else { + dataNullOffset = UCPTRIE_NO_DATA_NULL_OFFSET; + } + + int32_t indexLength = compactIndex(fastILimit, mixedBlocks, errorCode); + highStart = realHighStart; + return indexLength; +} + +UCPTrie *MutableCodePointTrie::build(UCPTrieType type, UCPTrieValueWidth valueWidth, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { + return nullptr; + } + if (type < UCPTRIE_TYPE_FAST || UCPTRIE_TYPE_SMALL < type || + valueWidth < UCPTRIE_VALUE_BITS_16 || UCPTRIE_VALUE_BITS_8 < valueWidth) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + // The mutable trie always stores 32-bit values. + // When we build a UCPTrie for a smaller value width, we first mask off unused bits + // before compacting the data. + switch (valueWidth) { + case UCPTRIE_VALUE_BITS_32: + break; + case UCPTRIE_VALUE_BITS_16: + maskValues(0xffff); + break; + case UCPTRIE_VALUE_BITS_8: + maskValues(0xff); + break; + default: + break; + } + + UChar32 fastLimit = type == UCPTRIE_TYPE_FAST ? BMP_LIMIT : UCPTRIE_SMALL_LIMIT; + int32_t indexLength = compactTrie(fastLimit >> UCPTRIE_SHIFT_3, errorCode); + if (U_FAILURE(errorCode)) { + clear(); + return nullptr; + } + + // Ensure data table alignment: The index length must be even for uint32_t data. + if (valueWidth == UCPTRIE_VALUE_BITS_32 && (indexLength & 1) != 0) { + index16[indexLength++] = 0xffee; // arbitrary value + } + + // Make the total trie structure length a multiple of 4 bytes by padding the data table, + // and store special values as the last two data values. + int32_t length = indexLength * 2; + if (valueWidth == UCPTRIE_VALUE_BITS_16) { + if (((indexLength ^ dataLength) & 1) != 0) { + // padding + data[dataLength++] = errorValue; + } + if (data[dataLength - 1] != errorValue || data[dataLength - 2] != highValue) { + data[dataLength++] = highValue; + data[dataLength++] = errorValue; + } + length += dataLength * 2; + } else if (valueWidth == UCPTRIE_VALUE_BITS_32) { + // 32-bit data words never need padding to a multiple of 4 bytes. + if (data[dataLength - 1] != errorValue || data[dataLength - 2] != highValue) { + if (data[dataLength - 1] != highValue) { + data[dataLength++] = highValue; + } + data[dataLength++] = errorValue; + } + length += dataLength * 4; + } else { + int32_t and3 = (length + dataLength) & 3; + if (and3 == 0 && data[dataLength - 1] == errorValue && data[dataLength - 2] == highValue) { + // all set + } else if(and3 == 3 && data[dataLength - 1] == highValue) { + data[dataLength++] = errorValue; + } else { + while (and3 != 2) { + data[dataLength++] = highValue; + and3 = (and3 + 1) & 3; + } + data[dataLength++] = highValue; + data[dataLength++] = errorValue; + } + length += dataLength; + } + + // Calculate the total length of the UCPTrie as a single memory block. + length += sizeof(UCPTrie); + U_ASSERT((length & 3) == 0); + + uint8_t *bytes = (uint8_t *)uprv_malloc(length); + if (bytes == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + clear(); + return nullptr; + } + UCPTrie *trie = reinterpret_cast(bytes); + uprv_memset(trie, 0, sizeof(UCPTrie)); + trie->indexLength = indexLength; + trie->dataLength = dataLength; + + trie->highStart = highStart; + // Round up shifted12HighStart to a multiple of 0x1000 for easy testing from UTF-8 lead bytes. + // Runtime code needs to then test for the real highStart as well. + trie->shifted12HighStart = (highStart + 0xfff) >> 12; + trie->type = type; + trie->valueWidth = valueWidth; + + trie->index3NullOffset = index3NullOffset; + trie->dataNullOffset = dataNullOffset; + trie->nullValue = initialValue; + + bytes += sizeof(UCPTrie); + + // Fill the index and data arrays. + uint16_t *dest16 = (uint16_t *)bytes; + trie->index = dest16; + + if (highStart <= fastLimit) { + // Condense only the fast index from the mutable-trie index. + for (int32_t i = 0, j = 0; j < indexLength; i += SMALL_DATA_BLOCKS_PER_BMP_BLOCK, ++j) { + *dest16++ = (uint16_t)index[i]; // dest16[j] + } + } else { + uprv_memcpy(dest16, index16, indexLength * 2); + dest16 += indexLength; + } + bytes += indexLength * 2; + + // Write the data array. + const uint32_t *p = data; + switch (valueWidth) { + case UCPTRIE_VALUE_BITS_16: + // Write 16-bit data values. + trie->data.ptr16 = dest16; + for (int32_t i = dataLength; i > 0; --i) { + *dest16++ = (uint16_t)*p++; + } + break; + case UCPTRIE_VALUE_BITS_32: + // Write 32-bit data values. + trie->data.ptr32 = (uint32_t *)bytes; + uprv_memcpy(bytes, p, (size_t)dataLength * 4); + break; + case UCPTRIE_VALUE_BITS_8: + // Write 8-bit data values. + trie->data.ptr8 = bytes; + for (int32_t i = dataLength; i > 0; --i) { + *bytes++ = (uint8_t)*p++; + } + break; + default: + // Will not occur, valueWidth checked at the beginning. + break; + } + +#ifdef UCPTRIE_DEBUG + trie->name = name; + + ucptrie_printLengths(trie, ""); +#endif + + clear(); + return trie; +} + +} // namespace + +U_NAMESPACE_END + +U_NAMESPACE_USE + +U_CAPI UMutableCPTrie * U_EXPORT2 +umutablecptrie_open(uint32_t initialValue, uint32_t errorValue, UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return nullptr; + } + LocalPointer trie( + new MutableCodePointTrie(initialValue, errorValue, *pErrorCode), *pErrorCode); + if (U_FAILURE(*pErrorCode)) { + return nullptr; + } + return reinterpret_cast(trie.orphan()); +} + +U_CAPI UMutableCPTrie * U_EXPORT2 +umutablecptrie_clone(const UMutableCPTrie *other, UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return nullptr; + } + if (other == nullptr) { + return nullptr; + } + LocalPointer clone( + new MutableCodePointTrie(*reinterpret_cast(other), *pErrorCode), *pErrorCode); + if (U_FAILURE(*pErrorCode)) { + return nullptr; + } + return reinterpret_cast(clone.orphan()); +} + +U_CAPI void U_EXPORT2 +umutablecptrie_close(UMutableCPTrie *trie) { + delete reinterpret_cast(trie); +} + +U_CAPI UMutableCPTrie * U_EXPORT2 +umutablecptrie_fromUCPMap(const UCPMap *map, UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return nullptr; + } + if (map == nullptr) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + return reinterpret_cast(MutableCodePointTrie::fromUCPMap(map, *pErrorCode)); +} + +U_CAPI UMutableCPTrie * U_EXPORT2 +umutablecptrie_fromUCPTrie(const UCPTrie *trie, UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return nullptr; + } + if (trie == nullptr) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + return reinterpret_cast(MutableCodePointTrie::fromUCPTrie(trie, *pErrorCode)); +} + +U_CAPI uint32_t U_EXPORT2 +umutablecptrie_get(const UMutableCPTrie *trie, UChar32 c) { + return reinterpret_cast(trie)->get(c); +} + +namespace { + +UChar32 getRange(const void *trie, UChar32 start, + UCPMapValueFilter *filter, const void *context, uint32_t *pValue) { + return reinterpret_cast(trie)-> + getRange(start, filter, context, pValue); +} + +} // namespace + +U_CAPI UChar32 U_EXPORT2 +umutablecptrie_getRange(const UMutableCPTrie *trie, UChar32 start, + UCPMapRangeOption option, uint32_t surrogateValue, + UCPMapValueFilter *filter, const void *context, uint32_t *pValue) { + return ucptrie_internalGetRange(getRange, trie, start, + option, surrogateValue, + filter, context, pValue); +} + +U_CAPI void U_EXPORT2 +umutablecptrie_set(UMutableCPTrie *trie, UChar32 c, uint32_t value, UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return; + } + reinterpret_cast(trie)->set(c, value, *pErrorCode); +} + +U_CAPI void U_EXPORT2 +umutablecptrie_setRange(UMutableCPTrie *trie, UChar32 start, UChar32 end, + uint32_t value, UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return; + } + reinterpret_cast(trie)->setRange(start, end, value, *pErrorCode); +} + +/* Compact and internally serialize the trie. */ +U_CAPI UCPTrie * U_EXPORT2 +umutablecptrie_buildImmutable(UMutableCPTrie *trie, UCPTrieType type, UCPTrieValueWidth valueWidth, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return nullptr; + } + return reinterpret_cast(trie)->build(type, valueWidth, *pErrorCode); +} + +#ifdef UCPTRIE_DEBUG +U_CFUNC void umutablecptrie_setName(UMutableCPTrie *trie, const char *name) { + reinterpret_cast(trie)->name = name; +} +#endif diff --git a/deps/icu-small/source/common/umutex.cpp b/deps/icu-small/source/common/umutex.cpp index ccbee9960a39e7..f069f9d9788bd8 100644 --- a/deps/icu-small/source/common/umutex.cpp +++ b/deps/icu-small/source/common/umutex.cpp @@ -1,204 +1,204 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1997-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* File umutex.cpp -* -* Modification History: -* -* Date Name Description -* 04/02/97 aliu Creation. -* 04/07/99 srl updated -* 05/13/99 stephen Changed to umutex (from cmutex). -* 11/22/99 aliu Make non-global mutex autoinitialize [j151] -****************************************************************************** -*/ - -#include "umutex.h" - -#include "unicode/utypes.h" -#include "uassert.h" -#include "ucln_cmn.h" -#include "cmemory.h" - -U_NAMESPACE_BEGIN - - -#if defined(U_USER_MUTEX_CPP) -// Support for including an alternate implementation of mutexes has been withdrawn. -// See issue ICU-20185. -#error U_USER_MUTEX_CPP not supported -#endif - - -/************************************************************************************************* - * - * ICU Mutex wrappers. - * - *************************************************************************************************/ - -namespace { -std::mutex *initMutex; -std::condition_variable *initCondition; - -// The ICU global mutex. -// Used when ICU implementation code passes nullptr for the mutex pointer. -UMutex globalMutex; - -std::once_flag initFlag; -std::once_flag *pInitFlag = &initFlag; - -} // Anonymous namespace - -U_CDECL_BEGIN -static UBool U_CALLCONV umtx_cleanup() { - initMutex->~mutex(); - initCondition->~condition_variable(); - UMutex::cleanup(); - - // Reset the once_flag, by destructing it and creating a fresh one in its place. - // Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once(). - pInitFlag->~once_flag(); - pInitFlag = new(&initFlag) std::once_flag(); - return true; -} - -static void U_CALLCONV umtx_init() { - initMutex = STATIC_NEW(std::mutex); - initCondition = STATIC_NEW(std::condition_variable); - ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup); -} -U_CDECL_END - - -std::mutex *UMutex::getMutex() { - std::mutex *retPtr = fMutex.load(std::memory_order_acquire); - if (retPtr == nullptr) { - std::call_once(*pInitFlag, umtx_init); - std::lock_guard guard(*initMutex); - retPtr = fMutex.load(std::memory_order_acquire); - if (retPtr == nullptr) { - fMutex = new(fStorage) std::mutex(); - retPtr = fMutex; - fListLink = gListHead; - gListHead = this; - } - } - U_ASSERT(retPtr != nullptr); - return retPtr; -} - -UMutex *UMutex::gListHead = nullptr; - -void UMutex::cleanup() { - UMutex *next = nullptr; - for (UMutex *m = gListHead; m != nullptr; m = next) { - (*m->fMutex).~mutex(); - m->fMutex = nullptr; - next = m->fListLink; - m->fListLink = nullptr; - } - gListHead = nullptr; -} - - -U_CAPI void U_EXPORT2 -umtx_lock(UMutex *mutex) { - if (mutex == nullptr) { - mutex = &globalMutex; - } - mutex->lock(); -} - - -U_CAPI void U_EXPORT2 -umtx_unlock(UMutex* mutex) -{ - if (mutex == nullptr) { - mutex = &globalMutex; - } - mutex->unlock(); -} - - -/************************************************************************************************* - * - * UInitOnce Implementation - * - *************************************************************************************************/ - -// This function is called when a test of a UInitOnce::fState reveals that -// initialization has not completed, that we either need to call the init -// function on this thread, or wait for some other thread to complete. -// -// The actual call to the init function is made inline by template code -// that knows the C++ types involved. This function returns true if -// the caller needs to call the Init function. -// -U_COMMON_API UBool U_EXPORT2 -umtx_initImplPreInit(UInitOnce &uio) { - std::call_once(*pInitFlag, umtx_init); - std::unique_lock lock(*initMutex); - if (umtx_loadAcquire(uio.fState) == 0) { - umtx_storeRelease(uio.fState, 1); - return true; // Caller will next call the init function. - } else { - while (umtx_loadAcquire(uio.fState) == 1) { - // Another thread is currently running the initialization. - // Wait until it completes. - initCondition->wait(lock); - } - U_ASSERT(uio.fState == 2); - return false; - } -} - - -// This function is called by the thread that ran an initialization function, -// just after completing the function. -// Some threads may be waiting on the condition, requiring the broadcast wakeup. -// Some threads may be racing to test the fState variable outside of the mutex, -// requiring the use of store/release when changing its value. - -U_COMMON_API void U_EXPORT2 -umtx_initImplPostInit(UInitOnce &uio) { - { - std::unique_lock lock(*initMutex); - umtx_storeRelease(uio.fState, 2); - } - initCondition->notify_all(); -} - -U_NAMESPACE_END - -/************************************************************************************************* - * - * Deprecated functions for setting user mutexes. - * - *************************************************************************************************/ - -U_DEPRECATED void U_EXPORT2 -u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *, - UMtxFn *, UMtxFn *, UErrorCode *status) { - if (U_SUCCESS(*status)) { - *status = U_UNSUPPORTED_ERROR; - } - return; -} - - - -U_DEPRECATED void U_EXPORT2 -u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *, - UErrorCode *status) { - if (U_SUCCESS(*status)) { - *status = U_UNSUPPORTED_ERROR; - } - return; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1997-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* File umutex.cpp +* +* Modification History: +* +* Date Name Description +* 04/02/97 aliu Creation. +* 04/07/99 srl updated +* 05/13/99 stephen Changed to umutex (from cmutex). +* 11/22/99 aliu Make non-global mutex autoinitialize [j151] +****************************************************************************** +*/ + +#include "umutex.h" + +#include "unicode/utypes.h" +#include "uassert.h" +#include "ucln_cmn.h" +#include "cmemory.h" + +U_NAMESPACE_BEGIN + + +#if defined(U_USER_MUTEX_CPP) +// Support for including an alternate implementation of mutexes has been withdrawn. +// See issue ICU-20185. +#error U_USER_MUTEX_CPP not supported +#endif + + +/************************************************************************************************* + * + * ICU Mutex wrappers. + * + *************************************************************************************************/ + +namespace { +std::mutex *initMutex; +std::condition_variable *initCondition; + +// The ICU global mutex. +// Used when ICU implementation code passes nullptr for the mutex pointer. +UMutex globalMutex; + +std::once_flag initFlag; +std::once_flag *pInitFlag = &initFlag; + +} // Anonymous namespace + +U_CDECL_BEGIN +static UBool U_CALLCONV umtx_cleanup() { + initMutex->~mutex(); + initCondition->~condition_variable(); + UMutex::cleanup(); + + // Reset the once_flag, by destructing it and creating a fresh one in its place. + // Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once(). + pInitFlag->~once_flag(); + pInitFlag = new(&initFlag) std::once_flag(); + return true; +} + +static void U_CALLCONV umtx_init() { + initMutex = STATIC_NEW(std::mutex); + initCondition = STATIC_NEW(std::condition_variable); + ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup); +} +U_CDECL_END + + +std::mutex *UMutex::getMutex() { + std::mutex *retPtr = fMutex.load(std::memory_order_acquire); + if (retPtr == nullptr) { + std::call_once(*pInitFlag, umtx_init); + std::lock_guard guard(*initMutex); + retPtr = fMutex.load(std::memory_order_acquire); + if (retPtr == nullptr) { + fMutex = new(fStorage) std::mutex(); + retPtr = fMutex; + fListLink = gListHead; + gListHead = this; + } + } + U_ASSERT(retPtr != nullptr); + return retPtr; +} + +UMutex *UMutex::gListHead = nullptr; + +void UMutex::cleanup() { + UMutex *next = nullptr; + for (UMutex *m = gListHead; m != nullptr; m = next) { + (*m->fMutex).~mutex(); + m->fMutex = nullptr; + next = m->fListLink; + m->fListLink = nullptr; + } + gListHead = nullptr; +} + + +U_CAPI void U_EXPORT2 +umtx_lock(UMutex *mutex) { + if (mutex == nullptr) { + mutex = &globalMutex; + } + mutex->lock(); +} + + +U_CAPI void U_EXPORT2 +umtx_unlock(UMutex* mutex) +{ + if (mutex == nullptr) { + mutex = &globalMutex; + } + mutex->unlock(); +} + + +/************************************************************************************************* + * + * UInitOnce Implementation + * + *************************************************************************************************/ + +// This function is called when a test of a UInitOnce::fState reveals that +// initialization has not completed, that we either need to call the init +// function on this thread, or wait for some other thread to complete. +// +// The actual call to the init function is made inline by template code +// that knows the C++ types involved. This function returns true if +// the caller needs to call the Init function. +// +U_COMMON_API UBool U_EXPORT2 +umtx_initImplPreInit(UInitOnce &uio) { + std::call_once(*pInitFlag, umtx_init); + std::unique_lock lock(*initMutex); + if (umtx_loadAcquire(uio.fState) == 0) { + umtx_storeRelease(uio.fState, 1); + return true; // Caller will next call the init function. + } else { + while (umtx_loadAcquire(uio.fState) == 1) { + // Another thread is currently running the initialization. + // Wait until it completes. + initCondition->wait(lock); + } + U_ASSERT(uio.fState == 2); + return false; + } +} + + +// This function is called by the thread that ran an initialization function, +// just after completing the function. +// Some threads may be waiting on the condition, requiring the broadcast wakeup. +// Some threads may be racing to test the fState variable outside of the mutex, +// requiring the use of store/release when changing its value. + +U_COMMON_API void U_EXPORT2 +umtx_initImplPostInit(UInitOnce &uio) { + { + std::unique_lock lock(*initMutex); + umtx_storeRelease(uio.fState, 2); + } + initCondition->notify_all(); +} + +U_NAMESPACE_END + +/************************************************************************************************* + * + * Deprecated functions for setting user mutexes. + * + *************************************************************************************************/ + +U_DEPRECATED void U_EXPORT2 +u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *, + UMtxFn *, UMtxFn *, UErrorCode *status) { + if (U_SUCCESS(*status)) { + *status = U_UNSUPPORTED_ERROR; + } + return; +} + + + +U_DEPRECATED void U_EXPORT2 +u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *, + UErrorCode *status) { + if (U_SUCCESS(*status)) { + *status = U_UNSUPPORTED_ERROR; + } + return; +} diff --git a/deps/icu-small/source/common/umutex.h b/deps/icu-small/source/common/umutex.h index 1b8332409c6dda..47c7106487b6de 100644 --- a/deps/icu-small/source/common/umutex.h +++ b/deps/icu-small/source/common/umutex.h @@ -1,273 +1,273 @@ -// © 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 UMUTEX.H -* -* Modification History: -* -* Date Name Description -* 04/02/97 aliu Creation. -* 04/07/99 srl rewrite - C interface, multiple mutices -* 05/13/99 stephen Changed to umutex (from cmutex) -****************************************************************************** -*/ - -#ifndef UMUTEX_H -#define UMUTEX_H - -#include -#include -#include -#include - -#include "unicode/utypes.h" -#include "unicode/uclean.h" -#include "unicode/uobject.h" - -#include "putilimp.h" - -#if defined(U_USER_ATOMICS_H) || defined(U_USER_MUTEX_H) -// Support for including an alternate implementation of atomic & mutex operations has been withdrawn. -// See issue ICU-20185. -#error U_USER_ATOMICS and U_USER_MUTEX_H are not supported -#endif - -// Export an explicit template instantiation of std::atomic. -// When building DLLs for Windows this is required as it is used as a data member of the exported SharedObject class. -// See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples. -// -// Similar story for std::atomic, and the exported UMutex class. -#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN && !defined(U_IN_DOXYGEN) -#if defined(__clang__) || defined(_MSC_VER) - #if defined(__clang__) - // Suppress the warning that the explicit instantiation after explicit specialization has no effect. - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Winstantiation-after-specialization" - #endif -template struct U_COMMON_API std::atomic; -template struct U_COMMON_API std::atomic; - #if defined(__clang__) - #pragma clang diagnostic pop - #endif -#elif defined(__GNUC__) -// For GCC this class is already exported/visible, so no need for U_COMMON_API. -template struct std::atomic; -template struct std::atomic; -#endif -#endif - - -U_NAMESPACE_BEGIN - -/**************************************************************************** - * - * Low Level Atomic Operations, ICU wrappers for. - * - ****************************************************************************/ - -typedef std::atomic u_atomic_int32_t; - -inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { - return var.load(std::memory_order_acquire); -} - -inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { - var.store(val, std::memory_order_release); -} - -inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { - return var->fetch_add(1) + 1; -} - -inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { - return var->fetch_sub(1) - 1; -} - - -/************************************************************************************************* - * - * UInitOnce Definitions. - * - *************************************************************************************************/ - -struct U_COMMON_API UInitOnce { - u_atomic_int32_t fState {0}; - UErrorCode fErrCode {U_ZERO_ERROR}; - void reset() {fState = 0;} - UBool isReset() {return umtx_loadAcquire(fState) == 0;} -// Note: isReset() is used by service registration code. -// Thread safety of this usage needs review. -}; - -U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &); -U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &); - -template void umtx_initOnce(UInitOnce &uio, T *obj, void (U_CALLCONV T::*fp)()) { - if (umtx_loadAcquire(uio.fState) == 2) { - return; - } - if (umtx_initImplPreInit(uio)) { - (obj->*fp)(); - umtx_initImplPostInit(uio); - } -} - - -// umtx_initOnce variant for plain functions, or static class functions. -// No context parameter. -inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)()) { - if (umtx_loadAcquire(uio.fState) == 2) { - return; - } - if (umtx_initImplPreInit(uio)) { - (*fp)(); - umtx_initImplPostInit(uio); - } -} - -// umtx_initOnce variant for plain functions, or static class functions. -// With ErrorCode, No context parameter. -inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(UErrorCode &), UErrorCode &errCode) { - if (U_FAILURE(errCode)) { - return; - } - if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) { - // We run the initialization. - (*fp)(errCode); - uio.fErrCode = errCode; - umtx_initImplPostInit(uio); - } else { - // Someone else already ran the initialization. - if (U_FAILURE(uio.fErrCode)) { - errCode = uio.fErrCode; - } - } -} - -// umtx_initOnce variant for plain functions, or static class functions, -// with a context parameter. -template void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T), T context) { - if (umtx_loadAcquire(uio.fState) == 2) { - return; - } - if (umtx_initImplPreInit(uio)) { - (*fp)(context); - umtx_initImplPostInit(uio); - } -} - -// umtx_initOnce variant for plain functions, or static class functions, -// with a context parameter and an error code. -template void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T, UErrorCode &), T context, UErrorCode &errCode) { - if (U_FAILURE(errCode)) { - return; - } - if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) { - // We run the initialization. - (*fp)(context, errCode); - uio.fErrCode = errCode; - umtx_initImplPostInit(uio); - } else { - // Someone else already ran the initialization. - if (U_FAILURE(uio.fErrCode)) { - errCode = uio.fErrCode; - } - } -} - -// UMutex should be constexpr-constructible, so that no initialization code -// is run during startup. -// This works on all C++ libraries except MS VS before VS2019. -#if (defined(_CPPLIB_VER) && !defined(_MSVC_STL_VERSION)) || \ - (defined(_MSVC_STL_VERSION) && _MSVC_STL_VERSION < 142) - // (VS std lib older than VS2017) || (VS std lib version < VS2019) -# define UMUTEX_CONSTEXPR -#else -# define UMUTEX_CONSTEXPR constexpr -#endif - -/** - * UMutex - ICU Mutex class. - * - * This is the preferred Mutex class for use within ICU implementation code. - * It is a thin wrapper over C++ std::mutex, with these additions: - * - Static instances are safe, not triggering static construction or destruction, - * and the associated order of construction or destruction issues. - * - Plumbed into u_cleanup() for destructing the underlying std::mutex, - * which frees any OS level resources they may be holding. - * - * Limitations: - * - Static or global instances only. Cannot be heap allocated. Cannot appear as a - * member of another class. - * - No condition variables or other advanced features. If needed, you will need to use - * std::mutex and std::condition_variable directly. For an example, see unifiedcache.cpp - * - * Typical Usage: - * static UMutex myMutex; - * - * { - * Mutex lock(myMutex); - * ... // Do stuff that is protected by myMutex; - * } // myMutex is released when lock goes out of scope. - */ - -class U_COMMON_API UMutex { -public: - UMUTEX_CONSTEXPR UMutex() {} - ~UMutex() = default; - - UMutex(const UMutex &other) = delete; - UMutex &operator =(const UMutex &other) = delete; - void *operator new(size_t) = delete; - - // requirements for C++ BasicLockable, allows UMutex to work with std::lock_guard - void lock() { - std::mutex *m = fMutex.load(std::memory_order_acquire); - if (m == nullptr) { m = getMutex(); } - m->lock(); - } - void unlock() { fMutex.load(std::memory_order_relaxed)->unlock(); } - - static void cleanup(); - -private: - alignas(std::mutex) char fStorage[sizeof(std::mutex)] {}; - std::atomic fMutex { nullptr }; - - /** All initialized UMutexes are kept in a linked list, so that they can be found, - * and the underlying std::mutex destructed, by u_cleanup(). - */ - UMutex *fListLink { nullptr }; - static UMutex *gListHead; - - /** Out-of-line function to lazily initialize a UMutex on first use. - * Initial fast check is inline, in lock(). The returned value may never - * be nullptr. - */ - std::mutex *getMutex(); -}; - - -/* Lock a mutex. - * @param mutex The given mutex to be locked. Pass NULL to specify - * the global ICU mutex. Recursive locks are an error - * and may cause a deadlock on some platforms. - */ -U_CAPI void U_EXPORT2 umtx_lock(UMutex* mutex); - -/* Unlock a mutex. - * @param mutex The given mutex to be unlocked. Pass NULL to specify - * the global ICU mutex. - */ -U_CAPI void U_EXPORT2 umtx_unlock (UMutex* mutex); - - -U_NAMESPACE_END - -#endif /* UMUTEX_H */ -/*eof*/ +// © 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 UMUTEX.H +* +* Modification History: +* +* Date Name Description +* 04/02/97 aliu Creation. +* 04/07/99 srl rewrite - C interface, multiple mutices +* 05/13/99 stephen Changed to umutex (from cmutex) +****************************************************************************** +*/ + +#ifndef UMUTEX_H +#define UMUTEX_H + +#include +#include +#include +#include + +#include "unicode/utypes.h" +#include "unicode/uclean.h" +#include "unicode/uobject.h" + +#include "putilimp.h" + +#if defined(U_USER_ATOMICS_H) || defined(U_USER_MUTEX_H) +// Support for including an alternate implementation of atomic & mutex operations has been withdrawn. +// See issue ICU-20185. +#error U_USER_ATOMICS and U_USER_MUTEX_H are not supported +#endif + +// Export an explicit template instantiation of std::atomic. +// When building DLLs for Windows this is required as it is used as a data member of the exported SharedObject class. +// See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples. +// +// Similar story for std::atomic, and the exported UMutex class. +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN && !defined(U_IN_DOXYGEN) +#if defined(__clang__) || defined(_MSC_VER) + #if defined(__clang__) + // Suppress the warning that the explicit instantiation after explicit specialization has no effect. + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Winstantiation-after-specialization" + #endif +template struct U_COMMON_API std::atomic; +template struct U_COMMON_API std::atomic; + #if defined(__clang__) + #pragma clang diagnostic pop + #endif +#elif defined(__GNUC__) +// For GCC this class is already exported/visible, so no need for U_COMMON_API. +template struct std::atomic; +template struct std::atomic; +#endif +#endif + + +U_NAMESPACE_BEGIN + +/**************************************************************************** + * + * Low Level Atomic Operations, ICU wrappers for. + * + ****************************************************************************/ + +typedef std::atomic u_atomic_int32_t; + +inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { + return var.load(std::memory_order_acquire); +} + +inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { + var.store(val, std::memory_order_release); +} + +inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { + return var->fetch_add(1) + 1; +} + +inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { + return var->fetch_sub(1) - 1; +} + + +/************************************************************************************************* + * + * UInitOnce Definitions. + * + *************************************************************************************************/ + +struct U_COMMON_API UInitOnce { + u_atomic_int32_t fState {0}; + UErrorCode fErrCode {U_ZERO_ERROR}; + void reset() {fState = 0;} + UBool isReset() {return umtx_loadAcquire(fState) == 0;} +// Note: isReset() is used by service registration code. +// Thread safety of this usage needs review. +}; + +U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &); +U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &); + +template void umtx_initOnce(UInitOnce &uio, T *obj, void (U_CALLCONV T::*fp)()) { + if (umtx_loadAcquire(uio.fState) == 2) { + return; + } + if (umtx_initImplPreInit(uio)) { + (obj->*fp)(); + umtx_initImplPostInit(uio); + } +} + + +// umtx_initOnce variant for plain functions, or static class functions. +// No context parameter. +inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)()) { + if (umtx_loadAcquire(uio.fState) == 2) { + return; + } + if (umtx_initImplPreInit(uio)) { + (*fp)(); + umtx_initImplPostInit(uio); + } +} + +// umtx_initOnce variant for plain functions, or static class functions. +// With ErrorCode, No context parameter. +inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(UErrorCode &), UErrorCode &errCode) { + if (U_FAILURE(errCode)) { + return; + } + if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) { + // We run the initialization. + (*fp)(errCode); + uio.fErrCode = errCode; + umtx_initImplPostInit(uio); + } else { + // Someone else already ran the initialization. + if (U_FAILURE(uio.fErrCode)) { + errCode = uio.fErrCode; + } + } +} + +// umtx_initOnce variant for plain functions, or static class functions, +// with a context parameter. +template void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T), T context) { + if (umtx_loadAcquire(uio.fState) == 2) { + return; + } + if (umtx_initImplPreInit(uio)) { + (*fp)(context); + umtx_initImplPostInit(uio); + } +} + +// umtx_initOnce variant for plain functions, or static class functions, +// with a context parameter and an error code. +template void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T, UErrorCode &), T context, UErrorCode &errCode) { + if (U_FAILURE(errCode)) { + return; + } + if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) { + // We run the initialization. + (*fp)(context, errCode); + uio.fErrCode = errCode; + umtx_initImplPostInit(uio); + } else { + // Someone else already ran the initialization. + if (U_FAILURE(uio.fErrCode)) { + errCode = uio.fErrCode; + } + } +} + +// UMutex should be constexpr-constructible, so that no initialization code +// is run during startup. +// This works on all C++ libraries except MS VS before VS2019. +#if (defined(_CPPLIB_VER) && !defined(_MSVC_STL_VERSION)) || \ + (defined(_MSVC_STL_VERSION) && _MSVC_STL_VERSION < 142) + // (VS std lib older than VS2017) || (VS std lib version < VS2019) +# define UMUTEX_CONSTEXPR +#else +# define UMUTEX_CONSTEXPR constexpr +#endif + +/** + * UMutex - ICU Mutex class. + * + * This is the preferred Mutex class for use within ICU implementation code. + * It is a thin wrapper over C++ std::mutex, with these additions: + * - Static instances are safe, not triggering static construction or destruction, + * and the associated order of construction or destruction issues. + * - Plumbed into u_cleanup() for destructing the underlying std::mutex, + * which frees any OS level resources they may be holding. + * + * Limitations: + * - Static or global instances only. Cannot be heap allocated. Cannot appear as a + * member of another class. + * - No condition variables or other advanced features. If needed, you will need to use + * std::mutex and std::condition_variable directly. For an example, see unifiedcache.cpp + * + * Typical Usage: + * static UMutex myMutex; + * + * { + * Mutex lock(myMutex); + * ... // Do stuff that is protected by myMutex; + * } // myMutex is released when lock goes out of scope. + */ + +class U_COMMON_API UMutex { +public: + UMUTEX_CONSTEXPR UMutex() {} + ~UMutex() = default; + + UMutex(const UMutex &other) = delete; + UMutex &operator =(const UMutex &other) = delete; + void *operator new(size_t) = delete; + + // requirements for C++ BasicLockable, allows UMutex to work with std::lock_guard + void lock() { + std::mutex *m = fMutex.load(std::memory_order_acquire); + if (m == nullptr) { m = getMutex(); } + m->lock(); + } + void unlock() { fMutex.load(std::memory_order_relaxed)->unlock(); } + + static void cleanup(); + +private: + alignas(std::mutex) char fStorage[sizeof(std::mutex)] {}; + std::atomic fMutex { nullptr }; + + /** All initialized UMutexes are kept in a linked list, so that they can be found, + * and the underlying std::mutex destructed, by u_cleanup(). + */ + UMutex *fListLink { nullptr }; + static UMutex *gListHead; + + /** Out-of-line function to lazily initialize a UMutex on first use. + * Initial fast check is inline, in lock(). The returned value may never + * be nullptr. + */ + std::mutex *getMutex(); +}; + + +/* Lock a mutex. + * @param mutex The given mutex to be locked. Pass NULL to specify + * the global ICU mutex. Recursive locks are an error + * and may cause a deadlock on some platforms. + */ +U_CAPI void U_EXPORT2 umtx_lock(UMutex* mutex); + +/* Unlock a mutex. + * @param mutex The given mutex to be unlocked. Pass NULL to specify + * the global ICU mutex. + */ +U_CAPI void U_EXPORT2 umtx_unlock (UMutex* mutex); + + +U_NAMESPACE_END + +#endif /* UMUTEX_H */ +/*eof*/ diff --git a/deps/icu-small/source/common/unames.cpp b/deps/icu-small/source/common/unames.cpp index b0ac991e1baeac..7b6c7b81cdf9b1 100644 --- a/deps/icu-small/source/common/unames.cpp +++ b/deps/icu-small/source/common/unames.cpp @@ -1,2108 +1,2108 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: unames.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999oct04 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" -#include "unicode/putil.h" -#include "unicode/uchar.h" -#include "unicode/udata.h" -#include "unicode/utf.h" -#include "unicode/utf16.h" -#include "uassert.h" -#include "ustr_imp.h" -#include "umutex.h" -#include "cmemory.h" -#include "cstring.h" -#include "ucln_cmn.h" -#include "udataswp.h" -#include "uprops.h" - -U_NAMESPACE_BEGIN - -/* prototypes ------------------------------------------------------------- */ - -static const char DATA_NAME[] = "unames"; -static const char DATA_TYPE[] = "icu"; - -#define GROUP_SHIFT 5 -#define LINES_PER_GROUP (1L<groupsOffset) - -typedef struct { - const char *otherName; - UChar32 code; -} FindName; - -#define DO_FIND_NAME NULL - -static UDataMemory *uCharNamesData=NULL; -static UCharNames *uCharNames=NULL; -static icu::UInitOnce gCharNamesInitOnce {}; - -/* - * Maximum length of character names (regular & 1.0). - */ -static int32_t gMaxNameLength=0; - -/* - * Set of chars used in character names (regular & 1.0). - * Chars are platform-dependent (can be EBCDIC). - */ -static uint32_t gNameSet[8]={ 0 }; - -#define U_NONCHARACTER_CODE_POINT U_CHAR_CATEGORY_COUNT -#define U_LEAD_SURROGATE U_CHAR_CATEGORY_COUNT + 1 -#define U_TRAIL_SURROGATE U_CHAR_CATEGORY_COUNT + 2 - -#define U_CHAR_EXTENDED_CATEGORY_COUNT (U_CHAR_CATEGORY_COUNT + 3) - -static const char * const charCatNames[U_CHAR_EXTENDED_CATEGORY_COUNT] = { - "unassigned", - "uppercase letter", - "lowercase letter", - "titlecase letter", - "modifier letter", - "other letter", - "non spacing mark", - "enclosing mark", - "combining spacing mark", - "decimal digit number", - "letter number", - "other number", - "space separator", - "line separator", - "paragraph separator", - "control", - "format", - "private use area", - "surrogate", - "dash punctuation", - "start punctuation", - "end punctuation", - "connector punctuation", - "other punctuation", - "math symbol", - "currency symbol", - "modifier symbol", - "other symbol", - "initial punctuation", - "final punctuation", - "noncharacter", - "lead surrogate", - "trail surrogate" -}; - -/* implementation ----------------------------------------------------------- */ - -static UBool U_CALLCONV unames_cleanup(void) -{ - if(uCharNamesData) { - udata_close(uCharNamesData); - uCharNamesData = NULL; - } - if(uCharNames) { - uCharNames = NULL; - } - gCharNamesInitOnce.reset(); - gMaxNameLength=0; - return true; -} - -static UBool U_CALLCONV -isAcceptable(void * /*context*/, - const char * /*type*/, const char * /*name*/, - const UDataInfo *pInfo) { - return (UBool)( - pInfo->size>=20 && - pInfo->isBigEndian==U_IS_BIG_ENDIAN && - pInfo->charsetFamily==U_CHARSET_FAMILY && - pInfo->dataFormat[0]==0x75 && /* dataFormat="unam" */ - pInfo->dataFormat[1]==0x6e && - pInfo->dataFormat[2]==0x61 && - pInfo->dataFormat[3]==0x6d && - pInfo->formatVersion[0]==1); -} - -static void U_CALLCONV -loadCharNames(UErrorCode &status) { - U_ASSERT(uCharNamesData == NULL); - U_ASSERT(uCharNames == NULL); - - uCharNamesData = udata_openChoice(NULL, DATA_TYPE, DATA_NAME, isAcceptable, NULL, &status); - if(U_FAILURE(status)) { - uCharNamesData = NULL; - } else { - uCharNames = (UCharNames *)udata_getMemory(uCharNamesData); - } - ucln_common_registerCleanup(UCLN_COMMON_UNAMES, unames_cleanup); -} - - -static UBool -isDataLoaded(UErrorCode *pErrorCode) { - umtx_initOnce(gCharNamesInitOnce, &loadCharNames, *pErrorCode); - return U_SUCCESS(*pErrorCode); -} - -#define WRITE_CHAR(buffer, bufferLength, bufferPos, c) UPRV_BLOCK_MACRO_BEGIN { \ - if((bufferLength)>0) { \ - *(buffer)++=c; \ - --(bufferLength); \ - } \ - ++(bufferPos); \ -} UPRV_BLOCK_MACRO_END - -#define U_ISO_COMMENT U_CHAR_NAME_CHOICE_COUNT - -/* - * Important: expandName() and compareName() are almost the same - - * apply fixes to both. - * - * UnicodeData.txt uses ';' as a field separator, so no - * field can contain ';' as part of its contents. - * In unames.dat, it is marked as token[';']==-1 only if the - * semicolon is used in the data file - which is iff we - * have Unicode 1.0 names or ISO comments or aliases. - * So, it will be token[';']==-1 if we store U1.0 names/ISO comments/aliases - * although we know that it will never be part of a name. - */ -static uint16_t -expandName(UCharNames *names, - const uint8_t *name, uint16_t nameLength, UCharNameChoice nameChoice, - char *buffer, uint16_t bufferLength) { - uint16_t *tokens=(uint16_t *)names+8; - uint16_t token, tokenCount=*tokens++, bufferPos=0; - uint8_t *tokenStrings=(uint8_t *)names+names->tokenStringOffset; - uint8_t c; - - if(nameChoice!=U_UNICODE_CHAR_NAME && nameChoice!=U_EXTENDED_CHAR_NAME) { - /* - * skip the modern name if it is not requested _and_ - * if the semicolon byte value is a character, not a token number - */ - if((uint8_t)';'>=tokenCount || tokens[(uint8_t)';']==(uint16_t)(-1)) { - int fieldIndex= nameChoice==U_ISO_COMMENT ? 2 : nameChoice; - do { - while(nameLength>0) { - --nameLength; - if(*name++==';') { - break; - } - } - } while(--fieldIndex>0); - } else { - /* - * the semicolon byte value is a token number, therefore - * only modern names are stored in unames.dat and there is no - * such requested alternate name here - */ - nameLength=0; - } - } - - /* write each letter directly, and write a token word per token */ - while(nameLength>0) { - --nameLength; - c=*name++; - - if(c>=tokenCount) { - if(c!=';') { - /* implicit letter */ - WRITE_CHAR(buffer, bufferLength, bufferPos, c); - } else { - /* finished */ - break; - } - } else { - token=tokens[c]; - if(token==(uint16_t)(-2)) { - /* this is a lead byte for a double-byte token */ - token=tokens[c<<8|*name++]; - --nameLength; - } - if(token==(uint16_t)(-1)) { - if(c!=';') { - /* explicit letter */ - WRITE_CHAR(buffer, bufferLength, bufferPos, c); - } else { - /* stop, but skip the semicolon if we are seeking - extended names and there was no 2.0 name but there - is a 1.0 name. */ - if(!bufferPos && nameChoice == U_EXTENDED_CHAR_NAME) { - if ((uint8_t)';'>=tokenCount || tokens[(uint8_t)';']==(uint16_t)(-1)) { - continue; - } - } - /* finished */ - break; - } - } else { - /* write token word */ - uint8_t *tokenString=tokenStrings+token; - while((c=*tokenString++)!=0) { - WRITE_CHAR(buffer, bufferLength, bufferPos, c); - } - } - } - } - - /* zero-terminate */ - if(bufferLength>0) { - *buffer=0; - } - - return bufferPos; -} - -/* - * compareName() is almost the same as expandName() except that it compares - * the currently expanded name to an input name. - * It returns the match/no match result as soon as possible. - */ -static UBool -compareName(UCharNames *names, - const uint8_t *name, uint16_t nameLength, UCharNameChoice nameChoice, - const char *otherName) { - uint16_t *tokens=(uint16_t *)names+8; - uint16_t token, tokenCount=*tokens++; - uint8_t *tokenStrings=(uint8_t *)names+names->tokenStringOffset; - uint8_t c; - const char *origOtherName = otherName; - - if(nameChoice!=U_UNICODE_CHAR_NAME && nameChoice!=U_EXTENDED_CHAR_NAME) { - /* - * skip the modern name if it is not requested _and_ - * if the semicolon byte value is a character, not a token number - */ - if((uint8_t)';'>=tokenCount || tokens[(uint8_t)';']==(uint16_t)(-1)) { - int fieldIndex= nameChoice==U_ISO_COMMENT ? 2 : nameChoice; - do { - while(nameLength>0) { - --nameLength; - if(*name++==';') { - break; - } - } - } while(--fieldIndex>0); - } else { - /* - * the semicolon byte value is a token number, therefore - * only modern names are stored in unames.dat and there is no - * such requested alternate name here - */ - nameLength=0; - } - } - - /* compare each letter directly, and compare a token word per token */ - while(nameLength>0) { - --nameLength; - c=*name++; - - if(c>=tokenCount) { - if(c!=';') { - /* implicit letter */ - if((char)c!=*otherName++) { - return false; - } - } else { - /* finished */ - break; - } - } else { - token=tokens[c]; - if(token==(uint16_t)(-2)) { - /* this is a lead byte for a double-byte token */ - token=tokens[c<<8|*name++]; - --nameLength; - } - if(token==(uint16_t)(-1)) { - if(c!=';') { - /* explicit letter */ - if((char)c!=*otherName++) { - return false; - } - } else { - /* stop, but skip the semicolon if we are seeking - extended names and there was no 2.0 name but there - is a 1.0 name. */ - if(otherName == origOtherName && nameChoice == U_EXTENDED_CHAR_NAME) { - if ((uint8_t)';'>=tokenCount || tokens[(uint8_t)';']==(uint16_t)(-1)) { - continue; - } - } - /* finished */ - break; - } - } else { - /* write token word */ - uint8_t *tokenString=tokenStrings+token; - while((c=*tokenString++)!=0) { - if((char)c!=*otherName++) { - return false; - } - } - } - } - } - - /* complete match? */ - return (UBool)(*otherName==0); -} - -static uint8_t getCharCat(UChar32 cp) { - uint8_t cat; - - if (U_IS_UNICODE_NONCHAR(cp)) { - return U_NONCHARACTER_CODE_POINT; - } - - if ((cat = u_charType(cp)) == U_SURROGATE) { - cat = U_IS_LEAD(cp) ? U_LEAD_SURROGATE : U_TRAIL_SURROGATE; - } - - return cat; -} - -static const char *getCharCatName(UChar32 cp) { - uint8_t cat = getCharCat(cp); - - /* Return unknown if the table of names above is not up to - date. */ - - if (cat >= UPRV_LENGTHOF(charCatNames)) { - return "unknown"; - } else { - return charCatNames[cat]; - } -} - -static uint16_t getExtName(uint32_t code, char *buffer, uint16_t bufferLength) { - const char *catname = getCharCatName(code); - uint16_t length = 0; - - UChar32 cp; - int ndigits, i; - - WRITE_CHAR(buffer, bufferLength, length, '<'); - while (catname[length - 1]) { - WRITE_CHAR(buffer, bufferLength, length, catname[length - 1]); - } - WRITE_CHAR(buffer, bufferLength, length, '-'); - for (cp = code, ndigits = 0; cp; ++ndigits, cp >>= 4) - ; - if (ndigits < 4) - ndigits = 4; - for (cp = code, i = ndigits; (cp || i > 0) && bufferLength; cp >>= 4, bufferLength--) { - uint8_t v = (uint8_t)(cp & 0xf); - buffer[--i] = (v < 10 ? '0' + v : 'A' + v - 10); - } - buffer += ndigits; - length += static_cast(ndigits); - WRITE_CHAR(buffer, bufferLength, length, '>'); - - return length; -} - -/* - * getGroup() does a binary search for the group that contains the - * Unicode code point "code". - * The return value is always a valid Group* that may contain "code" - * or else is the highest group before "code". - * If the lowest group is after "code", then that one is returned. - */ -static const uint16_t * -getGroup(UCharNames *names, uint32_t code) { - const uint16_t *groups=GET_GROUPS(names); - uint16_t groupMSB=(uint16_t)(code>>GROUP_SHIFT), - start=0, - limit=*groups++, - number; - - /* binary search for the group of names that contains the one for code */ - while(start=0xc, then it forms a length value with the following nibble. - * Calculation see below. - * The offsets and lengths arrays must be at least 33 (one more) long because - * there is no check here at the end if the last nibble is still used. - */ -static const uint8_t * -expandGroupLengths(const uint8_t *s, - uint16_t offsets[LINES_PER_GROUP+1], uint16_t lengths[LINES_PER_GROUP+1]) { - /* read the lengths of the 32 strings in this group and get each string's offset */ - uint16_t i=0, offset=0, length=0; - uint8_t lengthByte; - - /* all 32 lengths must be read to get the offset of the first group string */ - while(i=12) { - /* double-nibble length spread across two bytes */ - length=(uint16_t)(((length&0x3)<<4|lengthByte>>4)+12); - lengthByte&=0xf; - } else if((lengthByte /* &0xf0 */)>=0xc0) { - /* double-nibble length spread across this one byte */ - length=(uint16_t)((lengthByte&0x3f)+12); - } else { - /* single-nibble length in MSBs */ - length=(uint16_t)(lengthByte>>4); - lengthByte&=0xf; - } - - *offsets++=offset; - *lengths++=length; - - offset+=length; - ++i; - - /* read odd nibble - LSBs of lengthByte */ - if((lengthByte&0xf0)==0) { - /* this nibble was not consumed for a double-nibble length above */ - length=lengthByte; - if(length<12) { - /* single-nibble length in LSBs */ - *offsets++=offset; - *lengths++=length; - - offset+=length; - ++i; - } - } else { - length=0; /* prevent double-nibble detection in the next iteration */ - } - } - - /* now, s is at the first group string */ - return s; -} - -static uint16_t -expandGroupName(UCharNames *names, const uint16_t *group, - uint16_t lineNumber, UCharNameChoice nameChoice, - char *buffer, uint16_t bufferLength) { - uint16_t offsets[LINES_PER_GROUP+2], lengths[LINES_PER_GROUP+2]; - const uint8_t *s=(uint8_t *)names+names->groupStringOffset+GET_GROUP_OFFSET(group); - s=expandGroupLengths(s, offsets, lengths); - return expandName(names, s+offsets[lineNumber], lengths[lineNumber], nameChoice, - buffer, bufferLength); -} - -static uint16_t -getName(UCharNames *names, uint32_t code, UCharNameChoice nameChoice, - char *buffer, uint16_t bufferLength) { - const uint16_t *group=getGroup(names, code); - if((uint16_t)(code>>GROUP_SHIFT)==group[GROUP_MSB]) { - return expandGroupName(names, group, (uint16_t)(code&GROUP_MASK), nameChoice, - buffer, bufferLength); - } else { - /* group not found */ - /* zero-terminate */ - if(bufferLength>0) { - *buffer=0; - } - return 0; - } -} - -/* - * enumGroupNames() enumerates all the names in a 32-group - * and either calls the enumerator function or finds a given input name. - */ -static UBool -enumGroupNames(UCharNames *names, const uint16_t *group, - UChar32 start, UChar32 end, - UEnumCharNamesFn *fn, void *context, - UCharNameChoice nameChoice) { - uint16_t offsets[LINES_PER_GROUP+2], lengths[LINES_PER_GROUP+2]; - const uint8_t *s=(uint8_t *)names+names->groupStringOffset+GET_GROUP_OFFSET(group); - - s=expandGroupLengths(s, offsets, lengths); - if(fn!=DO_FIND_NAME) { - char buffer[200]; - uint16_t length; - - while(start<=end) { - length=expandName(names, s+offsets[start&GROUP_MASK], lengths[start&GROUP_MASK], nameChoice, buffer, sizeof(buffer)); - if (!length && nameChoice == U_EXTENDED_CHAR_NAME) { - buffer[length = getExtName(start, buffer, sizeof(buffer))] = 0; - } - /* here, we assume that the buffer is large enough */ - if(length>0) { - if(!fn(context, start, nameChoice, buffer, length)) { - return false; - } - } - ++start; - } - } else { - const char *otherName=((FindName *)context)->otherName; - while(start<=end) { - if(compareName(names, s+offsets[start&GROUP_MASK], lengths[start&GROUP_MASK], nameChoice, otherName)) { - ((FindName *)context)->code=start; - return false; - } - ++start; - } - } - return true; -} - -/* - * enumExtNames enumerate extended names. - * It only needs to do it if it is called with a real function and not - * with the dummy DO_FIND_NAME, because u_charFromName() does a check - * for extended names by itself. - */ -static UBool -enumExtNames(UChar32 start, UChar32 end, - UEnumCharNamesFn *fn, void *context) -{ - if(fn!=DO_FIND_NAME) { - char buffer[200]; - uint16_t length; - - while(start<=end) { - buffer[length = getExtName(start, buffer, sizeof(buffer))] = 0; - /* here, we assume that the buffer is large enough */ - if(length>0) { - if(!fn(context, start, U_EXTENDED_CHAR_NAME, buffer, length)) { - return false; - } - } - ++start; - } - } - - return true; -} - -static UBool -enumNames(UCharNames *names, - UChar32 start, UChar32 limit, - UEnumCharNamesFn *fn, void *context, - UCharNameChoice nameChoice) { - uint16_t startGroupMSB, endGroupMSB, groupCount; - const uint16_t *group, *groupLimit; - - startGroupMSB=(uint16_t)(start>>GROUP_SHIFT); - endGroupMSB=(uint16_t)((limit-1)>>GROUP_SHIFT); - - /* find the group that contains start, or the highest before it */ - group=getGroup(names, start); - - if(startGroupMSBlimit) { - extLimit=limit; - } - if(!enumExtNames(start, extLimit-1, fn, context)) { - return false; - } - start=extLimit; - } - - if(startGroupMSB==endGroupMSB) { - if(startGroupMSB==group[GROUP_MSB]) { - /* if start and limit-1 are in the same group, then enumerate only in that one */ - return enumGroupNames(names, group, start, limit-1, fn, context, nameChoice); - } - } else { - const uint16_t *groups=GET_GROUPS(names); - groupCount=*groups++; - groupLimit=groups+groupCount*GROUP_LENGTH; - - if(startGroupMSB==group[GROUP_MSB]) { - /* enumerate characters in the partial start group */ - if((start&GROUP_MASK)!=0) { - if(!enumGroupNames(names, group, - start, ((UChar32)startGroupMSB<group[GROUP_MSB]) { - /* make sure that we start enumerating with the first group after start */ - const uint16_t *nextGroup=NEXT_GROUP(group); - if (nextGroup < groupLimit && nextGroup[GROUP_MSB] > startGroupMSB && nameChoice == U_EXTENDED_CHAR_NAME) { - UChar32 end = nextGroup[GROUP_MSB] << GROUP_SHIFT; - if (end > limit) { - end = limit; - } - if (!enumExtNames(start, end - 1, fn, context)) { - return false; - } - } - group=nextGroup; - } - - /* enumerate entire groups between the start- and end-groups */ - while(group group[GROUP_MSB] + 1 && nameChoice == U_EXTENDED_CHAR_NAME) { - UChar32 end = nextGroup[GROUP_MSB] << GROUP_SHIFT; - if (end > limit) { - end = limit; - } - if (!enumExtNames((group[GROUP_MSB] + 1) << GROUP_SHIFT, end - 1, fn, context)) { - return false; - } - } - group=nextGroup; - } - - /* enumerate within the end group (group[GROUP_MSB]==endGroupMSB) */ - if(group start) { - start = next; - } - } else { - return true; - } - } - - /* we have not found a group, which means everything is made of - extended names. */ - if (nameChoice == U_EXTENDED_CHAR_NAME) { - if (limit > UCHAR_MAX_VALUE + 1) { - limit = UCHAR_MAX_VALUE + 1; - } - return enumExtNames(start, limit - 1, fn, context); - } - - return true; -} - -static uint16_t -writeFactorSuffix(const uint16_t *factors, uint16_t count, - const char *s, /* suffix elements */ - uint32_t code, - uint16_t indexes[8], /* output fields from here */ - const char *elementBases[8], const char *elements[8], - char *buffer, uint16_t bufferLength) { - uint16_t i, factor, bufferPos=0; - char c; - - /* write elements according to the factors */ - - /* - * the factorized elements are determined by modulo arithmetic - * with the factors of this algorithm - * - * note that for fewer operations, count is decremented here - */ - --count; - for(i=count; i>0; --i) { - factor=factors[i]; - indexes[i]=(uint16_t)(code%factor); - code/=factor; - } - /* - * we don't need to calculate the last modulus because start<=code<=end - * guarantees here that code<=factors[0] - */ - indexes[0]=(uint16_t)code; - - /* write each element */ - for(;;) { - if(elementBases!=NULL) { - *elementBases++=s; - } - - /* skip indexes[i] strings */ - factor=indexes[i]; - while(factor>0) { - while(*s++!=0) {} - --factor; - } - if(elements!=NULL) { - *elements++=s; - } - - /* write element */ - while((c=*s++)!=0) { - WRITE_CHAR(buffer, bufferLength, bufferPos, c); - } - - /* we do not need to perform the rest of this loop for i==count - break here */ - if(i>=count) { - break; - } - - /* skip the rest of the strings for this factors[i] */ - factor=(uint16_t)(factors[i]-indexes[i]-1); - while(factor>0) { - while(*s++!=0) {} - --factor; - } - - ++i; - } - - /* zero-terminate */ - if(bufferLength>0) { - *buffer=0; - } - - return bufferPos; -} - -/* - * Important: - * Parts of findAlgName() are almost the same as some of getAlgName(). - * Fixes must be applied to both. - */ -static uint16_t -getAlgName(AlgorithmicRange *range, uint32_t code, UCharNameChoice nameChoice, - char *buffer, uint16_t bufferLength) { - uint16_t bufferPos=0; - - /* Only the normative character name can be algorithmic. */ - if(nameChoice!=U_UNICODE_CHAR_NAME && nameChoice!=U_EXTENDED_CHAR_NAME) { - /* zero-terminate */ - if(bufferLength>0) { - *buffer=0; - } - return 0; - } - - switch(range->type) { - case 0: { - /* name = prefix hex-digits */ - const char *s=(const char *)(range+1); - char c; - - uint16_t i, count; - - /* copy prefix */ - while((c=*s++)!=0) { - WRITE_CHAR(buffer, bufferLength, bufferPos, c); - } - - /* write hexadecimal code point value */ - count=range->variant; - - /* zero-terminate */ - if(count0;) { - if(--i>=4; - } - - bufferPos+=count; - break; - } - case 1: { - /* name = prefix factorized-elements */ - uint16_t indexes[8]; - const uint16_t *factors=(const uint16_t *)(range+1); - uint16_t count=range->variant; - const char *s=(const char *)(factors+count); - char c; - - /* copy prefix */ - while((c=*s++)!=0) { - WRITE_CHAR(buffer, bufferLength, bufferPos, c); - } - - bufferPos+=writeFactorSuffix(factors, count, - s, code-range->start, indexes, NULL, NULL, buffer, bufferLength); - break; - } - default: - /* undefined type */ - /* zero-terminate */ - if(bufferLength>0) { - *buffer=0; - } - break; - } - - return bufferPos; -} - -/* - * Important: enumAlgNames() and findAlgName() are almost the same. - * Any fix must be applied to both. - */ -static UBool -enumAlgNames(AlgorithmicRange *range, - UChar32 start, UChar32 limit, - UEnumCharNamesFn *fn, void *context, - UCharNameChoice nameChoice) { - char buffer[200]; - uint16_t length; - - if(nameChoice!=U_UNICODE_CHAR_NAME && nameChoice!=U_EXTENDED_CHAR_NAME) { - return true; - } - - switch(range->type) { - case 0: { - char *s, *end; - char c; - - /* get the full name of the start character */ - length=getAlgName(range, (uint32_t)start, nameChoice, buffer, sizeof(buffer)); - if(length<=0) { - return true; - } - - /* call the enumerator function with this first character */ - if(!fn(context, start, nameChoice, buffer, length)) { - return false; - } - - /* go to the end of the name; all these names have the same length */ - end=buffer; - while(*end!=0) { - ++end; - } - - /* enumerate the rest of the names */ - while(++startvariant; - const char *s=(const char *)(factors+count); - char *suffix, *t; - uint16_t prefixLength, i, idx; - - char c; - - /* name = prefix factorized-elements */ - - /* copy prefix */ - suffix=buffer; - prefixLength=0; - while((c=*s++)!=0) { - *suffix++=c; - ++prefixLength; - } - - /* append the suffix of the start character */ - length=(uint16_t)(prefixLength+writeFactorSuffix(factors, count, - s, (uint32_t)start-range->start, - indexes, elementBases, elements, - suffix, (uint16_t)(sizeof(buffer)-prefixLength))); - - /* call the enumerator function with this first character */ - if(!fn(context, start, nameChoice, buffer, length)) { - return false; - } - - /* enumerate the rest of the names */ - while(++starttype) { - case 0: { - /* name = prefix hex-digits */ - const char *s=(const char *)(range+1); - char c; - - uint16_t i, count; - - /* compare prefix */ - while((c=*s++)!=0) { - if((char)c!=*otherName++) { - return 0xffff; - } - } - - /* read hexadecimal code point value */ - count=range->variant; - code=0; - for(i=0; istart<=(uint32_t)code && (uint32_t)code<=range->end) { - return code; - } - break; - } - case 1: { - char buffer[64]; - uint16_t indexes[8]; - const char *elementBases[8], *elements[8]; - const uint16_t *factors=(const uint16_t *)(range+1); - uint16_t count=range->variant; - const char *s=(const char *)(factors+count), *t; - UChar32 start, limit; - uint16_t i, idx; - - char c; - - /* name = prefix factorized-elements */ - - /* compare prefix */ - while((c=*s++)!=0) { - if((char)c!=*otherName++) { - return 0xffff; - } - } - - start=(UChar32)range->start; - limit=(UChar32)(range->end+1); - - /* initialize the suffix elements for enumeration; indexes should all be set to 0 */ - writeFactorSuffix(factors, count, s, 0, - indexes, elementBases, elements, buffer, sizeof(buffer)); - - /* compare the first suffix */ - if(0==uprv_strcmp(otherName, buffer)) { - return start; - } - - /* enumerate and compare the rest of the suffixes */ - while(++start>5]|=((uint32_t)1<<((uint8_t)c&0x1f))) -#define SET_CONTAINS(set, c) (((set)[(uint8_t)c>>5]&((uint32_t)1<<((uint8_t)c&0x1f)))!=0) - -static int32_t -calcStringSetLength(uint32_t set[8], const char *s) { - int32_t length=0; - char c; - - while((c=*s++)!=0) { - SET_ADD(set, c); - ++length; - } - return length; -} - -static int32_t -calcAlgNameSetsLengths(int32_t maxNameLength) { - AlgorithmicRange *range; - uint32_t *p; - uint32_t rangeCount; - int32_t length; - - /* enumerate algorithmic ranges */ - p=(uint32_t *)((uint8_t *)uCharNames+uCharNames->algNamesOffset); - rangeCount=*p; - range=(AlgorithmicRange *)(p+1); - while(rangeCount>0) { - switch(range->type) { - case 0: - /* name = prefix + (range->variant times) hex-digits */ - /* prefix */ - length=calcStringSetLength(gNameSet, (const char *)(range+1))+range->variant; - if(length>maxNameLength) { - maxNameLength=length; - } - break; - case 1: { - /* name = prefix factorized-elements */ - const uint16_t *factors=(const uint16_t *)(range+1); - const char *s; - int32_t i, count=range->variant, factor, factorLength, maxFactorLength; - - /* prefix length */ - s=(const char *)(factors+count); - length=calcStringSetLength(gNameSet, s); - s+=length+1; /* start of factor suffixes */ - - /* get the set and maximum factor suffix length for each factor */ - for(i=0; i0; --factor) { - factorLength=calcStringSetLength(gNameSet, s); - s+=factorLength+1; - if(factorLength>maxFactorLength) { - maxFactorLength=factorLength; - } - } - length+=maxFactorLength; - } - - if(length>maxNameLength) { - maxNameLength=length; - } - break; - } - default: - /* unknown type */ - break; - } - - range=(AlgorithmicRange *)((uint8_t *)range+range->size); - --rangeCount; - } - return maxNameLength; -} - -static int32_t -calcExtNameSetsLengths(int32_t maxNameLength) { - int32_t i, length; - - for(i=0; i - * 1 for - - * 6 for most hex digits per code point - */ - length=9+calcStringSetLength(gNameSet, charCatNames[i]); - if(length>maxNameLength) { - maxNameLength=length; - } - } - return maxNameLength; -} - -static int32_t -calcNameSetLength(const uint16_t *tokens, uint16_t tokenCount, const uint8_t *tokenStrings, int8_t *tokenLengths, - uint32_t set[8], - const uint8_t **pLine, const uint8_t *lineLimit) { - const uint8_t *line=*pLine; - int32_t length=0, tokenLength; - uint16_t c, token; - - while(line!=lineLimit && (c=*line++)!=(uint8_t)';') { - if(c>=tokenCount) { - /* implicit letter */ - SET_ADD(set, c); - ++length; - } else { - token=tokens[c]; - if(token==(uint16_t)(-2)) { - /* this is a lead byte for a double-byte token */ - c=c<<8|*line++; - token=tokens[c]; - } - if(token==(uint16_t)(-1)) { - /* explicit letter */ - SET_ADD(set, c); - ++length; - } else { - /* count token word */ - if(tokenLengths!=NULL) { - /* use cached token length */ - tokenLength=tokenLengths[c]; - if(tokenLength==0) { - tokenLength=calcStringSetLength(set, (const char *)tokenStrings+token); - tokenLengths[c]=(int8_t)tokenLength; - } - } else { - tokenLength=calcStringSetLength(set, (const char *)tokenStrings+token); - } - length+=tokenLength; - } - } - } - - *pLine=line; - return length; -} - -static void -calcGroupNameSetsLengths(int32_t maxNameLength) { - uint16_t offsets[LINES_PER_GROUP+2], lengths[LINES_PER_GROUP+2]; - - uint16_t *tokens=(uint16_t *)uCharNames+8; - uint16_t tokenCount=*tokens++; - uint8_t *tokenStrings=(uint8_t *)uCharNames+uCharNames->tokenStringOffset; - - int8_t *tokenLengths; - - const uint16_t *group; - const uint8_t *s, *line, *lineLimit; - - int32_t groupCount, lineNumber, length; - - tokenLengths=(int8_t *)uprv_malloc(tokenCount); - if(tokenLengths!=NULL) { - uprv_memset(tokenLengths, 0, tokenCount); - } - - group=GET_GROUPS(uCharNames); - groupCount=*group++; - - /* enumerate all groups */ - while(groupCount>0) { - s=(uint8_t *)uCharNames+uCharNames->groupStringOffset+GET_GROUP_OFFSET(group); - s=expandGroupLengths(s, offsets, lengths); - - /* enumerate all lines in each group */ - for(lineNumber=0; lineNumbermaxNameLength) { - maxNameLength=length; - } - if(line==lineLimit) { - continue; - } - - /* read Unicode 1.0 name */ - length=calcNameSetLength(tokens, tokenCount, tokenStrings, tokenLengths, gNameSet, &line, lineLimit); - if(length>maxNameLength) { - maxNameLength=length; - } - if(line==lineLimit) { - continue; - } - - /* read ISO comment */ - /*length=calcNameSetLength(tokens, tokenCount, tokenStrings, tokenLengths, gISOCommentSet, &line, lineLimit);*/ - } - - group=NEXT_GROUP(group); - --groupCount; - } - - if(tokenLengths!=NULL) { - uprv_free(tokenLengths); - } - - /* set gMax... - name length last for threading */ - gMaxNameLength=maxNameLength; -} - -static UBool -calcNameSetsLengths(UErrorCode *pErrorCode) { - static const char extChars[]="0123456789ABCDEF<>-"; - int32_t i, maxNameLength; - - if(gMaxNameLength!=0) { - return true; - } - - if(!isDataLoaded(pErrorCode)) { - return false; - } - - /* set hex digits, used in various names, and <>-, used in extended names */ - for(i=0; i<(int32_t)sizeof(extChars)-1; ++i) { - SET_ADD(gNameSet, extChars[i]); - } - - /* set sets and lengths from algorithmic names */ - maxNameLength=calcAlgNameSetsLengths(0); - - /* set sets and lengths from extended names */ - maxNameLength=calcExtNameSetsLengths(maxNameLength); - - /* set sets and lengths from group names, set global maximum values */ - calcGroupNameSetsLengths(maxNameLength); - - return true; -} - -U_NAMESPACE_END - -/* public API --------------------------------------------------------------- */ - -U_NAMESPACE_USE - -U_CAPI int32_t U_EXPORT2 -u_charName(UChar32 code, UCharNameChoice nameChoice, - char *buffer, int32_t bufferLength, - UErrorCode *pErrorCode) { - AlgorithmicRange *algRange; - uint32_t *p; - uint32_t i; - int32_t length; - - /* check the argument values */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } else if(nameChoice>=U_CHAR_NAME_CHOICE_COUNT || - bufferLength<0 || (bufferLength>0 && buffer==NULL) - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - if((uint32_t)code>UCHAR_MAX_VALUE || !isDataLoaded(pErrorCode)) { - return u_terminateChars(buffer, bufferLength, 0, pErrorCode); - } - - length=0; - - /* try algorithmic names first */ - p=(uint32_t *)((uint8_t *)uCharNames+uCharNames->algNamesOffset); - i=*p; - algRange=(AlgorithmicRange *)(p+1); - while(i>0) { - if(algRange->start<=(uint32_t)code && (uint32_t)code<=algRange->end) { - length=getAlgName(algRange, (uint32_t)code, nameChoice, buffer, (uint16_t)bufferLength); - break; - } - algRange=(AlgorithmicRange *)((uint8_t *)algRange+algRange->size); - --i; - } - - if(i==0) { - if (nameChoice == U_EXTENDED_CHAR_NAME) { - length = getName(uCharNames, (uint32_t )code, U_EXTENDED_CHAR_NAME, buffer, (uint16_t) bufferLength); - if (!length) { - /* extended character name */ - length = getExtName((uint32_t) code, buffer, (uint16_t) bufferLength); - } - } else { - /* normal character name */ - length=getName(uCharNames, (uint32_t)code, nameChoice, buffer, (uint16_t)bufferLength); - } - } - - return u_terminateChars(buffer, bufferLength, length, pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -u_getISOComment(UChar32 /*c*/, - char *dest, int32_t destCapacity, - UErrorCode *pErrorCode) { - /* check the argument values */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } else if(destCapacity<0 || (destCapacity>0 && dest==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - return u_terminateChars(dest, destCapacity, 0, pErrorCode); -} - -U_CAPI UChar32 U_EXPORT2 -u_charFromName(UCharNameChoice nameChoice, - const char *name, - UErrorCode *pErrorCode) { - char upper[120] = {0}; - char lower[120] = {0}; - FindName findName; - AlgorithmicRange *algRange; - uint32_t *p; - uint32_t i; - UChar32 cp = 0; - char c0; - static constexpr UChar32 error = 0xffff; /* Undefined, but use this for backwards compatibility. */ - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return error; - } - - if(nameChoice>=U_CHAR_NAME_CHOICE_COUNT || name==NULL || *name==0) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return error; - } - - if(!isDataLoaded(pErrorCode)) { - return error; - } - - /* construct the uppercase and lowercase of the name first */ - for(i=0; i') { - // Parse a string like "" where HHHH is a hex code point. - uint32_t limit = i; - while (i >= 3 && lower[--i] != '-') {} - - // There should be 1 to 8 hex digits. - int32_t hexLength = limit - (i + 1); - if (i >= 2 && lower[i] == '-' && 1 <= hexLength && hexLength <= 8) { - uint32_t cIdx; - - lower[i] = 0; - - for (++i; i < limit; ++i) { - if (lower[i] >= '0' && lower[i] <= '9') { - cp = (cp << 4) + lower[i] - '0'; - } else if (lower[i] >= 'a' && lower[i] <= 'f') { - cp = (cp << 4) + lower[i] - 'a' + 10; - } else { - *pErrorCode = U_ILLEGAL_CHAR_FOUND; - return error; - } - // Prevent signed-integer overflow and out-of-range code points. - if (cp > UCHAR_MAX_VALUE) { - *pErrorCode = U_ILLEGAL_CHAR_FOUND; - return error; - } - } - - /* Now validate the category name. - We could use a binary search, or a trie, if - we really wanted to. */ - uint8_t cat = getCharCat(cp); - for (lower[i] = 0, cIdx = 0; cIdx < UPRV_LENGTHOF(charCatNames); ++cIdx) { - - if (!uprv_strcmp(lower + 1, charCatNames[cIdx])) { - if (cat == cIdx) { - return cp; - } - break; - } - } - } - } - - *pErrorCode = U_ILLEGAL_CHAR_FOUND; - return error; - } - - /* try algorithmic names now */ - p=(uint32_t *)((uint8_t *)uCharNames+uCharNames->algNamesOffset); - i=*p; - algRange=(AlgorithmicRange *)(p+1); - while(i>0) { - if((cp=findAlgName(algRange, nameChoice, upper))!=0xffff) { - return cp; - } - algRange=(AlgorithmicRange *)((uint8_t *)algRange+algRange->size); - --i; - } - - /* normal character name */ - findName.otherName=upper; - findName.code=error; - enumNames(uCharNames, 0, UCHAR_MAX_VALUE + 1, DO_FIND_NAME, &findName, nameChoice); - if (findName.code == error) { - *pErrorCode = U_ILLEGAL_CHAR_FOUND; - } - return findName.code; -} - -U_CAPI void U_EXPORT2 -u_enumCharNames(UChar32 start, UChar32 limit, - UEnumCharNamesFn *fn, - void *context, - UCharNameChoice nameChoice, - UErrorCode *pErrorCode) { - AlgorithmicRange *algRange; - uint32_t *p; - uint32_t i; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return; - } - - if(nameChoice>=U_CHAR_NAME_CHOICE_COUNT || fn==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - if((uint32_t) limit > UCHAR_MAX_VALUE + 1) { - limit = UCHAR_MAX_VALUE + 1; - } - if((uint32_t)start>=(uint32_t)limit) { - return; - } - - if(!isDataLoaded(pErrorCode)) { - return; - } - - /* interleave the data-driven ones with the algorithmic ones */ - /* iterate over all algorithmic ranges; assume that they are in ascending order */ - p=(uint32_t *)((uint8_t *)uCharNames+uCharNames->algNamesOffset); - i=*p; - algRange=(AlgorithmicRange *)(p+1); - while(i>0) { - /* enumerate the character names before the current algorithmic range */ - /* here: startstart) { - if((uint32_t)limit<=algRange->start) { - enumNames(uCharNames, start, limit, fn, context, nameChoice); - return; - } - if(!enumNames(uCharNames, start, (UChar32)algRange->start, fn, context, nameChoice)) { - return; - } - start=(UChar32)algRange->start; - } - /* enumerate the character names in the current algorithmic range */ - /* here: algRange->start<=startend) { - if((uint32_t)limit<=(algRange->end+1)) { - enumAlgNames(algRange, start, limit, fn, context, nameChoice); - return; - } - if(!enumAlgNames(algRange, start, (UChar32)algRange->end+1, fn, context, nameChoice)) { - return; - } - start=(UChar32)algRange->end+1; - } - /* continue to the next algorithmic range (here: startsize); - --i; - } - /* enumerate the character names after the last algorithmic range */ - enumNames(uCharNames, start, limit, fn, context, nameChoice); -} - -U_CAPI int32_t U_EXPORT2 -uprv_getMaxCharNameLength() { - UErrorCode errorCode=U_ZERO_ERROR; - if(calcNameSetsLengths(&errorCode)) { - return gMaxNameLength; - } else { - return 0; - } -} - -/** - * Converts the char set cset into a Unicode set uset. - * @param cset Set of 256 bit flags corresponding to a set of chars. - * @param uset USet to receive characters. Existing contents are deleted. - */ -static void -charSetToUSet(uint32_t cset[8], const USetAdder *sa) { - UChar us[256]; - char cs[256]; - - int32_t i, length; - UErrorCode errorCode; - - errorCode=U_ZERO_ERROR; - - if(!calcNameSetsLengths(&errorCode)) { - return; - } - - /* build a char string with all chars that are used in character names */ - length=0; - for(i=0; i<256; ++i) { - if(SET_CONTAINS(cset, i)) { - cs[length++]=(char)i; - } - } - - /* convert the char string to a UChar string */ - u_charsToUChars(cs, us, length); - - /* add each UChar to the USet */ - for(i=0; iadd(sa->set, us[i]); - } - } -} - -/** - * Fills set with characters that are used in Unicode character names. - * @param set USet to receive characters. - */ -U_CAPI void U_EXPORT2 -uprv_getCharNameCharacters(const USetAdder *sa) { - charSetToUSet(gNameSet, sa); -} - -/* data swapping ------------------------------------------------------------ */ - -/* - * The token table contains non-negative entries for token bytes, - * and -1 for bytes that represent themselves in the data file's charset. - * -2 entries are used for lead bytes. - * - * Direct bytes (-1 entries) must be translated from the input charset family - * to the output charset family. - * makeTokenMap() writes a permutation mapping for this. - * Use it once for single-/lead-byte tokens and once more for all trail byte - * tokens. (';' is an unused trail byte marked with -1.) - */ -static void -makeTokenMap(const UDataSwapper *ds, - int16_t tokens[], uint16_t tokenCount, - uint8_t map[256], - UErrorCode *pErrorCode) { - UBool usedOutChar[256]; - uint16_t i, j; - uint8_t c1, c2; - - if(U_FAILURE(*pErrorCode)) { - return; - } - - if(ds->inCharset==ds->outCharset) { - /* Same charset family: identity permutation */ - for(i=0; i<256; ++i) { - map[i]=(uint8_t)i; - } - } else { - uprv_memset(map, 0, 256); - uprv_memset(usedOutChar, 0, 256); - - if(tokenCount>256) { - tokenCount=256; - } - - /* set the direct bytes (byte 0 always maps to itself) */ - for(i=1; iswapInvChars(ds, &c1, 1, &c2, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - udata_printError(ds, "unames/makeTokenMap() finds variant character 0x%02x used (input charset family %d)\n", - i, ds->inCharset); - return; - } - - /* enter the converted character into the map and mark it used */ - map[c1]=c2; - usedOutChar[c2]=true; - } - } - - /* set the mappings for the rest of the permutation */ - for(i=j=1; idataFormat[0]==0x75 && /* dataFormat="unam" */ - pInfo->dataFormat[1]==0x6e && - pInfo->dataFormat[2]==0x61 && - pInfo->dataFormat[3]==0x6d && - pInfo->formatVersion[0]==1 - )) { - udata_printError(ds, "uchar_swapNames(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as unames.icu\n", - pInfo->dataFormat[0], pInfo->dataFormat[1], - pInfo->dataFormat[2], pInfo->dataFormat[3], - pInfo->formatVersion[0]); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - inBytes=(const uint8_t *)inData+headerSize; - outBytes=(uint8_t *)outData+headerSize; - if(length<0) { - algNamesOffset=ds->readUInt32(((const uint32_t *)inBytes)[3]); - } else { - length-=headerSize; - if( length<20 || - (uint32_t)length<(algNamesOffset=ds->readUInt32(((const uint32_t *)inBytes)[3])) - ) { - udata_printError(ds, "uchar_swapNames(): too few bytes (%d after header) for unames.icu\n", - length); - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - } - - if(length<0) { - /* preflighting: iterate through algorithmic ranges */ - offset=algNamesOffset; - count=ds->readUInt32(*((const uint32_t *)(inBytes+offset))); - offset+=4; - - for(i=0; ireadUInt16(inRange->size); - } - } else { - /* swap data */ - const uint16_t *p; - uint16_t *q, *temp; - - int16_t tokens[512]; - uint16_t tokenCount; - - uint8_t map[256], trailMap[256]; - - /* copy the data for inaccessible bytes */ - if(inBytes!=outBytes) { - uprv_memcpy(outBytes, inBytes, length); - } - - /* the initial 4 offsets first */ - tokenStringOffset=ds->readUInt32(((const uint32_t *)inBytes)[0]); - groupsOffset=ds->readUInt32(((const uint32_t *)inBytes)[1]); - groupStringOffset=ds->readUInt32(((const uint32_t *)inBytes)[2]); - ds->swapArray32(ds, inBytes, 16, outBytes, pErrorCode); - - /* - * now the tokens table - * it needs to be permutated along with the compressed name strings - */ - p=(const uint16_t *)(inBytes+16); - q=(uint16_t *)(outBytes+16); - - /* read and swap the tokenCount */ - tokenCount=ds->readUInt16(*p); - ds->swapArray16(ds, p, 2, q, pErrorCode); - ++p; - ++q; - - /* read the first 512 tokens and make the token maps */ - if(tokenCount<=512) { - count=tokenCount; - } else { - count=512; - } - for(i=0; i256 ? tokenCount-256 : 0), trailMap, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return 0; - } - - /* - * swap and permutate the tokens - * go through a temporary array to support in-place swapping - */ - temp=(uint16_t *)uprv_malloc(tokenCount*2); - if(temp==NULL) { - udata_printError(ds, "out of memory swapping %u unames.icu tokens\n", - tokenCount); - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return 0; - } - - /* swap and permutate single-/lead-byte tokens */ - for(i=0; iswapArray16(ds, p+i, 2, temp+map[i], pErrorCode); - } - - /* swap and permutate trail-byte tokens */ - for(; iswapArray16(ds, p+i, 2, temp+(i&0xffffff00)+trailMap[i&0xff], pErrorCode); - } - - /* copy the result into the output and free the temporary array */ - uprv_memcpy(q, temp, tokenCount*2); - uprv_free(temp); - - /* - * swap the token strings but not a possible padding byte after - * the terminating NUL of the last string - */ - udata_swapInvStringBlock(ds, inBytes+tokenStringOffset, (int32_t)(groupsOffset-tokenStringOffset), - outBytes+tokenStringOffset, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - udata_printError(ds, "uchar_swapNames(token strings) failed\n"); - return 0; - } - - /* swap the group table */ - count=ds->readUInt16(*((const uint16_t *)(inBytes+groupsOffset))); - ds->swapArray16(ds, inBytes+groupsOffset, (int32_t)((1+count*3)*2), - outBytes+groupsOffset, pErrorCode); - - /* - * swap the group strings - * swap the string bytes but not the nibble-encoded string lengths - */ - if(ds->inCharset!=ds->outCharset) { - uint16_t offsets[LINES_PER_GROUP+1], lengths[LINES_PER_GROUP+1]; - - const uint8_t *inStrings, *nextInStrings; - uint8_t *outStrings; - - uint8_t c; - - inStrings=inBytes+groupStringOffset; - outStrings=outBytes+groupStringOffset; - - stringsCount=algNamesOffset-groupStringOffset; - - /* iterate through string groups until only a few padding bytes are left */ - while(stringsCount>32) { - nextInStrings=expandGroupLengths(inStrings, offsets, lengths); - - /* move past the length bytes */ - stringsCount-=(uint32_t)(nextInStrings-inStrings); - outStrings+=nextInStrings-inStrings; - inStrings=nextInStrings; - - count=offsets[31]+lengths[31]; /* total number of string bytes in this group */ - stringsCount-=count; - - /* swap the string bytes using map[] and trailMap[] */ - while(count>0) { - c=*inStrings++; - *outStrings++=map[c]; - if(tokens[c]!=-2) { - --count; - } else { - /* token lead byte: swap the trail byte, too */ - *outStrings++=trailMap[*inStrings++]; - count-=2; - } - } - } - } - - /* swap the algorithmic ranges */ - offset=algNamesOffset; - count=ds->readUInt32(*((const uint32_t *)(inBytes+offset))); - ds->swapArray32(ds, inBytes+offset, 4, outBytes+offset, pErrorCode); - offset+=4; - - for(i=0; i(uint32_t)length) { - udata_printError(ds, "uchar_swapNames(): too few bytes (%d after header) for unames.icu algorithmic range %u\n", - length, i); - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - inRange=(const AlgorithmicRange *)(inBytes+offset); - outRange=(AlgorithmicRange *)(outBytes+offset); - offset+=ds->readUInt16(inRange->size); - - ds->swapArray32(ds, inRange, 8, outRange, pErrorCode); - ds->swapArray16(ds, &inRange->size, 2, &outRange->size, pErrorCode); - switch(inRange->type) { - case 0: - /* swap prefix string */ - ds->swapInvChars(ds, inRange+1, (int32_t)uprv_strlen((const char *)(inRange+1)), - outRange+1, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - udata_printError(ds, "uchar_swapNames(prefix string of algorithmic range %u) failed\n", - i); - return 0; - } - break; - case 1: - { - /* swap factors and the prefix and factor strings */ - uint32_t factorsCount; - - factorsCount=inRange->variant; - p=(const uint16_t *)(inRange+1); - q=(uint16_t *)(outRange+1); - ds->swapArray16(ds, p, (int32_t)(factorsCount*2), q, pErrorCode); - - /* swap the strings, up to the last terminating NUL */ - p+=factorsCount; - q+=factorsCount; - stringsCount=(uint32_t)((inBytes+offset)-(const uint8_t *)p); - while(stringsCount>0 && ((const uint8_t *)p)[stringsCount-1]!=0) { - --stringsCount; - } - ds->swapInvChars(ds, p, (int32_t)stringsCount, q, pErrorCode); - } - break; - default: - udata_printError(ds, "uchar_swapNames(): unknown type %u of algorithmic range %u\n", - inRange->type, i); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - } - } - - return headerSize+(int32_t)offset; -} - -/* - * Hey, Emacs, please set the following: - * - * Local Variables: - * indent-tabs-mode: nil - * End: - * - */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: unames.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999oct04 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" +#include "unicode/putil.h" +#include "unicode/uchar.h" +#include "unicode/udata.h" +#include "unicode/utf.h" +#include "unicode/utf16.h" +#include "uassert.h" +#include "ustr_imp.h" +#include "umutex.h" +#include "cmemory.h" +#include "cstring.h" +#include "ucln_cmn.h" +#include "udataswp.h" +#include "uprops.h" + +U_NAMESPACE_BEGIN + +/* prototypes ------------------------------------------------------------- */ + +static const char DATA_NAME[] = "unames"; +static const char DATA_TYPE[] = "icu"; + +#define GROUP_SHIFT 5 +#define LINES_PER_GROUP (1L<groupsOffset) + +typedef struct { + const char *otherName; + UChar32 code; +} FindName; + +#define DO_FIND_NAME nullptr + +static UDataMemory *uCharNamesData=nullptr; +static UCharNames *uCharNames=nullptr; +static icu::UInitOnce gCharNamesInitOnce {}; + +/* + * Maximum length of character names (regular & 1.0). + */ +static int32_t gMaxNameLength=0; + +/* + * Set of chars used in character names (regular & 1.0). + * Chars are platform-dependent (can be EBCDIC). + */ +static uint32_t gNameSet[8]={ 0 }; + +#define U_NONCHARACTER_CODE_POINT U_CHAR_CATEGORY_COUNT +#define U_LEAD_SURROGATE U_CHAR_CATEGORY_COUNT + 1 +#define U_TRAIL_SURROGATE U_CHAR_CATEGORY_COUNT + 2 + +#define U_CHAR_EXTENDED_CATEGORY_COUNT (U_CHAR_CATEGORY_COUNT + 3) + +static const char * const charCatNames[U_CHAR_EXTENDED_CATEGORY_COUNT] = { + "unassigned", + "uppercase letter", + "lowercase letter", + "titlecase letter", + "modifier letter", + "other letter", + "non spacing mark", + "enclosing mark", + "combining spacing mark", + "decimal digit number", + "letter number", + "other number", + "space separator", + "line separator", + "paragraph separator", + "control", + "format", + "private use area", + "surrogate", + "dash punctuation", + "start punctuation", + "end punctuation", + "connector punctuation", + "other punctuation", + "math symbol", + "currency symbol", + "modifier symbol", + "other symbol", + "initial punctuation", + "final punctuation", + "noncharacter", + "lead surrogate", + "trail surrogate" +}; + +/* implementation ----------------------------------------------------------- */ + +static UBool U_CALLCONV unames_cleanup() +{ + if(uCharNamesData) { + udata_close(uCharNamesData); + uCharNamesData = nullptr; + } + if(uCharNames) { + uCharNames = nullptr; + } + gCharNamesInitOnce.reset(); + gMaxNameLength=0; + return true; +} + +static UBool U_CALLCONV +isAcceptable(void * /*context*/, + const char * /*type*/, const char * /*name*/, + const UDataInfo *pInfo) { + return (UBool)( + pInfo->size>=20 && + pInfo->isBigEndian==U_IS_BIG_ENDIAN && + pInfo->charsetFamily==U_CHARSET_FAMILY && + pInfo->dataFormat[0]==0x75 && /* dataFormat="unam" */ + pInfo->dataFormat[1]==0x6e && + pInfo->dataFormat[2]==0x61 && + pInfo->dataFormat[3]==0x6d && + pInfo->formatVersion[0]==1); +} + +static void U_CALLCONV +loadCharNames(UErrorCode &status) { + U_ASSERT(uCharNamesData == nullptr); + U_ASSERT(uCharNames == nullptr); + + uCharNamesData = udata_openChoice(nullptr, DATA_TYPE, DATA_NAME, isAcceptable, nullptr, &status); + if(U_FAILURE(status)) { + uCharNamesData = nullptr; + } else { + uCharNames = (UCharNames *)udata_getMemory(uCharNamesData); + } + ucln_common_registerCleanup(UCLN_COMMON_UNAMES, unames_cleanup); +} + + +static UBool +isDataLoaded(UErrorCode *pErrorCode) { + umtx_initOnce(gCharNamesInitOnce, &loadCharNames, *pErrorCode); + return U_SUCCESS(*pErrorCode); +} + +#define WRITE_CHAR(buffer, bufferLength, bufferPos, c) UPRV_BLOCK_MACRO_BEGIN { \ + if((bufferLength)>0) { \ + *(buffer)++=c; \ + --(bufferLength); \ + } \ + ++(bufferPos); \ +} UPRV_BLOCK_MACRO_END + +#define U_ISO_COMMENT U_CHAR_NAME_CHOICE_COUNT + +/* + * Important: expandName() and compareName() are almost the same - + * apply fixes to both. + * + * UnicodeData.txt uses ';' as a field separator, so no + * field can contain ';' as part of its contents. + * In unames.dat, it is marked as token[';']==-1 only if the + * semicolon is used in the data file - which is iff we + * have Unicode 1.0 names or ISO comments or aliases. + * So, it will be token[';']==-1 if we store U1.0 names/ISO comments/aliases + * although we know that it will never be part of a name. + */ +static uint16_t +expandName(UCharNames *names, + const uint8_t *name, uint16_t nameLength, UCharNameChoice nameChoice, + char *buffer, uint16_t bufferLength) { + uint16_t *tokens=(uint16_t *)names+8; + uint16_t token, tokenCount=*tokens++, bufferPos=0; + uint8_t *tokenStrings=(uint8_t *)names+names->tokenStringOffset; + uint8_t c; + + if(nameChoice!=U_UNICODE_CHAR_NAME && nameChoice!=U_EXTENDED_CHAR_NAME) { + /* + * skip the modern name if it is not requested _and_ + * if the semicolon byte value is a character, not a token number + */ + if((uint8_t)';'>=tokenCount || tokens[(uint8_t)';']==(uint16_t)(-1)) { + int fieldIndex= nameChoice==U_ISO_COMMENT ? 2 : nameChoice; + do { + while(nameLength>0) { + --nameLength; + if(*name++==';') { + break; + } + } + } while(--fieldIndex>0); + } else { + /* + * the semicolon byte value is a token number, therefore + * only modern names are stored in unames.dat and there is no + * such requested alternate name here + */ + nameLength=0; + } + } + + /* write each letter directly, and write a token word per token */ + while(nameLength>0) { + --nameLength; + c=*name++; + + if(c>=tokenCount) { + if(c!=';') { + /* implicit letter */ + WRITE_CHAR(buffer, bufferLength, bufferPos, c); + } else { + /* finished */ + break; + } + } else { + token=tokens[c]; + if(token==(uint16_t)(-2)) { + /* this is a lead byte for a double-byte token */ + token=tokens[c<<8|*name++]; + --nameLength; + } + if(token==(uint16_t)(-1)) { + if(c!=';') { + /* explicit letter */ + WRITE_CHAR(buffer, bufferLength, bufferPos, c); + } else { + /* stop, but skip the semicolon if we are seeking + extended names and there was no 2.0 name but there + is a 1.0 name. */ + if(!bufferPos && nameChoice == U_EXTENDED_CHAR_NAME) { + if ((uint8_t)';'>=tokenCount || tokens[(uint8_t)';']==(uint16_t)(-1)) { + continue; + } + } + /* finished */ + break; + } + } else { + /* write token word */ + uint8_t *tokenString=tokenStrings+token; + while((c=*tokenString++)!=0) { + WRITE_CHAR(buffer, bufferLength, bufferPos, c); + } + } + } + } + + /* zero-terminate */ + if(bufferLength>0) { + *buffer=0; + } + + return bufferPos; +} + +/* + * compareName() is almost the same as expandName() except that it compares + * the currently expanded name to an input name. + * It returns the match/no match result as soon as possible. + */ +static UBool +compareName(UCharNames *names, + const uint8_t *name, uint16_t nameLength, UCharNameChoice nameChoice, + const char *otherName) { + uint16_t *tokens=(uint16_t *)names+8; + uint16_t token, tokenCount=*tokens++; + uint8_t *tokenStrings=(uint8_t *)names+names->tokenStringOffset; + uint8_t c; + const char *origOtherName = otherName; + + if(nameChoice!=U_UNICODE_CHAR_NAME && nameChoice!=U_EXTENDED_CHAR_NAME) { + /* + * skip the modern name if it is not requested _and_ + * if the semicolon byte value is a character, not a token number + */ + if((uint8_t)';'>=tokenCount || tokens[(uint8_t)';']==(uint16_t)(-1)) { + int fieldIndex= nameChoice==U_ISO_COMMENT ? 2 : nameChoice; + do { + while(nameLength>0) { + --nameLength; + if(*name++==';') { + break; + } + } + } while(--fieldIndex>0); + } else { + /* + * the semicolon byte value is a token number, therefore + * only modern names are stored in unames.dat and there is no + * such requested alternate name here + */ + nameLength=0; + } + } + + /* compare each letter directly, and compare a token word per token */ + while(nameLength>0) { + --nameLength; + c=*name++; + + if(c>=tokenCount) { + if(c!=';') { + /* implicit letter */ + if((char)c!=*otherName++) { + return false; + } + } else { + /* finished */ + break; + } + } else { + token=tokens[c]; + if(token==(uint16_t)(-2)) { + /* this is a lead byte for a double-byte token */ + token=tokens[c<<8|*name++]; + --nameLength; + } + if(token==(uint16_t)(-1)) { + if(c!=';') { + /* explicit letter */ + if((char)c!=*otherName++) { + return false; + } + } else { + /* stop, but skip the semicolon if we are seeking + extended names and there was no 2.0 name but there + is a 1.0 name. */ + if(otherName == origOtherName && nameChoice == U_EXTENDED_CHAR_NAME) { + if ((uint8_t)';'>=tokenCount || tokens[(uint8_t)';']==(uint16_t)(-1)) { + continue; + } + } + /* finished */ + break; + } + } else { + /* write token word */ + uint8_t *tokenString=tokenStrings+token; + while((c=*tokenString++)!=0) { + if((char)c!=*otherName++) { + return false; + } + } + } + } + } + + /* complete match? */ + return (UBool)(*otherName==0); +} + +static uint8_t getCharCat(UChar32 cp) { + uint8_t cat; + + if (U_IS_UNICODE_NONCHAR(cp)) { + return U_NONCHARACTER_CODE_POINT; + } + + if ((cat = u_charType(cp)) == U_SURROGATE) { + cat = U_IS_LEAD(cp) ? U_LEAD_SURROGATE : U_TRAIL_SURROGATE; + } + + return cat; +} + +static const char *getCharCatName(UChar32 cp) { + uint8_t cat = getCharCat(cp); + + /* Return unknown if the table of names above is not up to + date. */ + + if (cat >= UPRV_LENGTHOF(charCatNames)) { + return "unknown"; + } else { + return charCatNames[cat]; + } +} + +static uint16_t getExtName(uint32_t code, char *buffer, uint16_t bufferLength) { + const char *catname = getCharCatName(code); + uint16_t length = 0; + + UChar32 cp; + int ndigits, i; + + WRITE_CHAR(buffer, bufferLength, length, '<'); + while (catname[length - 1]) { + WRITE_CHAR(buffer, bufferLength, length, catname[length - 1]); + } + WRITE_CHAR(buffer, bufferLength, length, '-'); + for (cp = code, ndigits = 0; cp; ++ndigits, cp >>= 4) + ; + if (ndigits < 4) + ndigits = 4; + for (cp = code, i = ndigits; (cp || i > 0) && bufferLength; cp >>= 4, bufferLength--) { + uint8_t v = (uint8_t)(cp & 0xf); + buffer[--i] = (v < 10 ? '0' + v : 'A' + v - 10); + } + buffer += ndigits; + length += static_cast(ndigits); + WRITE_CHAR(buffer, bufferLength, length, '>'); + + return length; +} + +/* + * getGroup() does a binary search for the group that contains the + * Unicode code point "code". + * The return value is always a valid Group* that may contain "code" + * or else is the highest group before "code". + * If the lowest group is after "code", then that one is returned. + */ +static const uint16_t * +getGroup(UCharNames *names, uint32_t code) { + const uint16_t *groups=GET_GROUPS(names); + uint16_t groupMSB=(uint16_t)(code>>GROUP_SHIFT), + start=0, + limit=*groups++, + number; + + /* binary search for the group of names that contains the one for code */ + while(start=0xc, then it forms a length value with the following nibble. + * Calculation see below. + * The offsets and lengths arrays must be at least 33 (one more) long because + * there is no check here at the end if the last nibble is still used. + */ +static const uint8_t * +expandGroupLengths(const uint8_t *s, + uint16_t offsets[LINES_PER_GROUP+1], uint16_t lengths[LINES_PER_GROUP+1]) { + /* read the lengths of the 32 strings in this group and get each string's offset */ + uint16_t i=0, offset=0, length=0; + uint8_t lengthByte; + + /* all 32 lengths must be read to get the offset of the first group string */ + while(i=12) { + /* double-nibble length spread across two bytes */ + length=(uint16_t)(((length&0x3)<<4|lengthByte>>4)+12); + lengthByte&=0xf; + } else if((lengthByte /* &0xf0 */)>=0xc0) { + /* double-nibble length spread across this one byte */ + length=(uint16_t)((lengthByte&0x3f)+12); + } else { + /* single-nibble length in MSBs */ + length=(uint16_t)(lengthByte>>4); + lengthByte&=0xf; + } + + *offsets++=offset; + *lengths++=length; + + offset+=length; + ++i; + + /* read odd nibble - LSBs of lengthByte */ + if((lengthByte&0xf0)==0) { + /* this nibble was not consumed for a double-nibble length above */ + length=lengthByte; + if(length<12) { + /* single-nibble length in LSBs */ + *offsets++=offset; + *lengths++=length; + + offset+=length; + ++i; + } + } else { + length=0; /* prevent double-nibble detection in the next iteration */ + } + } + + /* now, s is at the first group string */ + return s; +} + +static uint16_t +expandGroupName(UCharNames *names, const uint16_t *group, + uint16_t lineNumber, UCharNameChoice nameChoice, + char *buffer, uint16_t bufferLength) { + uint16_t offsets[LINES_PER_GROUP+2], lengths[LINES_PER_GROUP+2]; + const uint8_t *s=(uint8_t *)names+names->groupStringOffset+GET_GROUP_OFFSET(group); + s=expandGroupLengths(s, offsets, lengths); + return expandName(names, s+offsets[lineNumber], lengths[lineNumber], nameChoice, + buffer, bufferLength); +} + +static uint16_t +getName(UCharNames *names, uint32_t code, UCharNameChoice nameChoice, + char *buffer, uint16_t bufferLength) { + const uint16_t *group=getGroup(names, code); + if((uint16_t)(code>>GROUP_SHIFT)==group[GROUP_MSB]) { + return expandGroupName(names, group, (uint16_t)(code&GROUP_MASK), nameChoice, + buffer, bufferLength); + } else { + /* group not found */ + /* zero-terminate */ + if(bufferLength>0) { + *buffer=0; + } + return 0; + } +} + +/* + * enumGroupNames() enumerates all the names in a 32-group + * and either calls the enumerator function or finds a given input name. + */ +static UBool +enumGroupNames(UCharNames *names, const uint16_t *group, + UChar32 start, UChar32 end, + UEnumCharNamesFn *fn, void *context, + UCharNameChoice nameChoice) { + uint16_t offsets[LINES_PER_GROUP+2], lengths[LINES_PER_GROUP+2]; + const uint8_t *s=(uint8_t *)names+names->groupStringOffset+GET_GROUP_OFFSET(group); + + s=expandGroupLengths(s, offsets, lengths); + if(fn!=DO_FIND_NAME) { + char buffer[200]; + uint16_t length; + + while(start<=end) { + length=expandName(names, s+offsets[start&GROUP_MASK], lengths[start&GROUP_MASK], nameChoice, buffer, sizeof(buffer)); + if (!length && nameChoice == U_EXTENDED_CHAR_NAME) { + buffer[length = getExtName(start, buffer, sizeof(buffer))] = 0; + } + /* here, we assume that the buffer is large enough */ + if(length>0) { + if(!fn(context, start, nameChoice, buffer, length)) { + return false; + } + } + ++start; + } + } else { + const char *otherName=((FindName *)context)->otherName; + while(start<=end) { + if(compareName(names, s+offsets[start&GROUP_MASK], lengths[start&GROUP_MASK], nameChoice, otherName)) { + ((FindName *)context)->code=start; + return false; + } + ++start; + } + } + return true; +} + +/* + * enumExtNames enumerate extended names. + * It only needs to do it if it is called with a real function and not + * with the dummy DO_FIND_NAME, because u_charFromName() does a check + * for extended names by itself. + */ +static UBool +enumExtNames(UChar32 start, UChar32 end, + UEnumCharNamesFn *fn, void *context) +{ + if(fn!=DO_FIND_NAME) { + char buffer[200]; + uint16_t length; + + while(start<=end) { + buffer[length = getExtName(start, buffer, sizeof(buffer))] = 0; + /* here, we assume that the buffer is large enough */ + if(length>0) { + if(!fn(context, start, U_EXTENDED_CHAR_NAME, buffer, length)) { + return false; + } + } + ++start; + } + } + + return true; +} + +static UBool +enumNames(UCharNames *names, + UChar32 start, UChar32 limit, + UEnumCharNamesFn *fn, void *context, + UCharNameChoice nameChoice) { + uint16_t startGroupMSB, endGroupMSB, groupCount; + const uint16_t *group, *groupLimit; + + startGroupMSB=(uint16_t)(start>>GROUP_SHIFT); + endGroupMSB=(uint16_t)((limit-1)>>GROUP_SHIFT); + + /* find the group that contains start, or the highest before it */ + group=getGroup(names, start); + + if(startGroupMSBlimit) { + extLimit=limit; + } + if(!enumExtNames(start, extLimit-1, fn, context)) { + return false; + } + start=extLimit; + } + + if(startGroupMSB==endGroupMSB) { + if(startGroupMSB==group[GROUP_MSB]) { + /* if start and limit-1 are in the same group, then enumerate only in that one */ + return enumGroupNames(names, group, start, limit-1, fn, context, nameChoice); + } + } else { + const uint16_t *groups=GET_GROUPS(names); + groupCount=*groups++; + groupLimit=groups+groupCount*GROUP_LENGTH; + + if(startGroupMSB==group[GROUP_MSB]) { + /* enumerate characters in the partial start group */ + if((start&GROUP_MASK)!=0) { + if(!enumGroupNames(names, group, + start, ((UChar32)startGroupMSB<group[GROUP_MSB]) { + /* make sure that we start enumerating with the first group after start */ + const uint16_t *nextGroup=NEXT_GROUP(group); + if (nextGroup < groupLimit && nextGroup[GROUP_MSB] > startGroupMSB && nameChoice == U_EXTENDED_CHAR_NAME) { + UChar32 end = nextGroup[GROUP_MSB] << GROUP_SHIFT; + if (end > limit) { + end = limit; + } + if (!enumExtNames(start, end - 1, fn, context)) { + return false; + } + } + group=nextGroup; + } + + /* enumerate entire groups between the start- and end-groups */ + while(group group[GROUP_MSB] + 1 && nameChoice == U_EXTENDED_CHAR_NAME) { + UChar32 end = nextGroup[GROUP_MSB] << GROUP_SHIFT; + if (end > limit) { + end = limit; + } + if (!enumExtNames((group[GROUP_MSB] + 1) << GROUP_SHIFT, end - 1, fn, context)) { + return false; + } + } + group=nextGroup; + } + + /* enumerate within the end group (group[GROUP_MSB]==endGroupMSB) */ + if(group start) { + start = next; + } + } else { + return true; + } + } + + /* we have not found a group, which means everything is made of + extended names. */ + if (nameChoice == U_EXTENDED_CHAR_NAME) { + if (limit > UCHAR_MAX_VALUE + 1) { + limit = UCHAR_MAX_VALUE + 1; + } + return enumExtNames(start, limit - 1, fn, context); + } + + return true; +} + +static uint16_t +writeFactorSuffix(const uint16_t *factors, uint16_t count, + const char *s, /* suffix elements */ + uint32_t code, + uint16_t indexes[8], /* output fields from here */ + const char *elementBases[8], const char *elements[8], + char *buffer, uint16_t bufferLength) { + uint16_t i, factor, bufferPos=0; + char c; + + /* write elements according to the factors */ + + /* + * the factorized elements are determined by modulo arithmetic + * with the factors of this algorithm + * + * note that for fewer operations, count is decremented here + */ + --count; + for(i=count; i>0; --i) { + factor=factors[i]; + indexes[i]=(uint16_t)(code%factor); + code/=factor; + } + /* + * we don't need to calculate the last modulus because start<=code<=end + * guarantees here that code<=factors[0] + */ + indexes[0]=(uint16_t)code; + + /* write each element */ + for(;;) { + if(elementBases!=nullptr) { + *elementBases++=s; + } + + /* skip indexes[i] strings */ + factor=indexes[i]; + while(factor>0) { + while(*s++!=0) {} + --factor; + } + if(elements!=nullptr) { + *elements++=s; + } + + /* write element */ + while((c=*s++)!=0) { + WRITE_CHAR(buffer, bufferLength, bufferPos, c); + } + + /* we do not need to perform the rest of this loop for i==count - break here */ + if(i>=count) { + break; + } + + /* skip the rest of the strings for this factors[i] */ + factor=(uint16_t)(factors[i]-indexes[i]-1); + while(factor>0) { + while(*s++!=0) {} + --factor; + } + + ++i; + } + + /* zero-terminate */ + if(bufferLength>0) { + *buffer=0; + } + + return bufferPos; +} + +/* + * Important: + * Parts of findAlgName() are almost the same as some of getAlgName(). + * Fixes must be applied to both. + */ +static uint16_t +getAlgName(AlgorithmicRange *range, uint32_t code, UCharNameChoice nameChoice, + char *buffer, uint16_t bufferLength) { + uint16_t bufferPos=0; + + /* Only the normative character name can be algorithmic. */ + if(nameChoice!=U_UNICODE_CHAR_NAME && nameChoice!=U_EXTENDED_CHAR_NAME) { + /* zero-terminate */ + if(bufferLength>0) { + *buffer=0; + } + return 0; + } + + switch(range->type) { + case 0: { + /* name = prefix hex-digits */ + const char *s=(const char *)(range+1); + char c; + + uint16_t i, count; + + /* copy prefix */ + while((c=*s++)!=0) { + WRITE_CHAR(buffer, bufferLength, bufferPos, c); + } + + /* write hexadecimal code point value */ + count=range->variant; + + /* zero-terminate */ + if(count0;) { + if(--i>=4; + } + + bufferPos+=count; + break; + } + case 1: { + /* name = prefix factorized-elements */ + uint16_t indexes[8]; + const uint16_t *factors=(const uint16_t *)(range+1); + uint16_t count=range->variant; + const char *s=(const char *)(factors+count); + char c; + + /* copy prefix */ + while((c=*s++)!=0) { + WRITE_CHAR(buffer, bufferLength, bufferPos, c); + } + + bufferPos+=writeFactorSuffix(factors, count, + s, code-range->start, indexes, nullptr, nullptr, buffer, bufferLength); + break; + } + default: + /* undefined type */ + /* zero-terminate */ + if(bufferLength>0) { + *buffer=0; + } + break; + } + + return bufferPos; +} + +/* + * Important: enumAlgNames() and findAlgName() are almost the same. + * Any fix must be applied to both. + */ +static UBool +enumAlgNames(AlgorithmicRange *range, + UChar32 start, UChar32 limit, + UEnumCharNamesFn *fn, void *context, + UCharNameChoice nameChoice) { + char buffer[200]; + uint16_t length; + + if(nameChoice!=U_UNICODE_CHAR_NAME && nameChoice!=U_EXTENDED_CHAR_NAME) { + return true; + } + + switch(range->type) { + case 0: { + char *s, *end; + char c; + + /* get the full name of the start character */ + length=getAlgName(range, (uint32_t)start, nameChoice, buffer, sizeof(buffer)); + if(length<=0) { + return true; + } + + /* call the enumerator function with this first character */ + if(!fn(context, start, nameChoice, buffer, length)) { + return false; + } + + /* go to the end of the name; all these names have the same length */ + end=buffer; + while(*end!=0) { + ++end; + } + + /* enumerate the rest of the names */ + while(++startvariant; + const char *s=(const char *)(factors+count); + char *suffix, *t; + uint16_t prefixLength, i, idx; + + char c; + + /* name = prefix factorized-elements */ + + /* copy prefix */ + suffix=buffer; + prefixLength=0; + while((c=*s++)!=0) { + *suffix++=c; + ++prefixLength; + } + + /* append the suffix of the start character */ + length=(uint16_t)(prefixLength+writeFactorSuffix(factors, count, + s, (uint32_t)start-range->start, + indexes, elementBases, elements, + suffix, (uint16_t)(sizeof(buffer)-prefixLength))); + + /* call the enumerator function with this first character */ + if(!fn(context, start, nameChoice, buffer, length)) { + return false; + } + + /* enumerate the rest of the names */ + while(++starttype) { + case 0: { + /* name = prefix hex-digits */ + const char *s=(const char *)(range+1); + char c; + + uint16_t i, count; + + /* compare prefix */ + while((c=*s++)!=0) { + if((char)c!=*otherName++) { + return 0xffff; + } + } + + /* read hexadecimal code point value */ + count=range->variant; + code=0; + for(i=0; istart<=(uint32_t)code && (uint32_t)code<=range->end) { + return code; + } + break; + } + case 1: { + char buffer[64]; + uint16_t indexes[8]; + const char *elementBases[8], *elements[8]; + const uint16_t *factors=(const uint16_t *)(range+1); + uint16_t count=range->variant; + const char *s=(const char *)(factors+count), *t; + UChar32 start, limit; + uint16_t i, idx; + + char c; + + /* name = prefix factorized-elements */ + + /* compare prefix */ + while((c=*s++)!=0) { + if((char)c!=*otherName++) { + return 0xffff; + } + } + + start=(UChar32)range->start; + limit=(UChar32)(range->end+1); + + /* initialize the suffix elements for enumeration; indexes should all be set to 0 */ + writeFactorSuffix(factors, count, s, 0, + indexes, elementBases, elements, buffer, sizeof(buffer)); + + /* compare the first suffix */ + if(0==uprv_strcmp(otherName, buffer)) { + return start; + } + + /* enumerate and compare the rest of the suffixes */ + while(++start>5]|=((uint32_t)1<<((uint8_t)c&0x1f))) +#define SET_CONTAINS(set, c) (((set)[(uint8_t)c>>5]&((uint32_t)1<<((uint8_t)c&0x1f)))!=0) + +static int32_t +calcStringSetLength(uint32_t set[8], const char *s) { + int32_t length=0; + char c; + + while((c=*s++)!=0) { + SET_ADD(set, c); + ++length; + } + return length; +} + +static int32_t +calcAlgNameSetsLengths(int32_t maxNameLength) { + AlgorithmicRange *range; + uint32_t *p; + uint32_t rangeCount; + int32_t length; + + /* enumerate algorithmic ranges */ + p=(uint32_t *)((uint8_t *)uCharNames+uCharNames->algNamesOffset); + rangeCount=*p; + range=(AlgorithmicRange *)(p+1); + while(rangeCount>0) { + switch(range->type) { + case 0: + /* name = prefix + (range->variant times) hex-digits */ + /* prefix */ + length=calcStringSetLength(gNameSet, (const char *)(range+1))+range->variant; + if(length>maxNameLength) { + maxNameLength=length; + } + break; + case 1: { + /* name = prefix factorized-elements */ + const uint16_t *factors=(const uint16_t *)(range+1); + const char *s; + int32_t i, count=range->variant, factor, factorLength, maxFactorLength; + + /* prefix length */ + s=(const char *)(factors+count); + length=calcStringSetLength(gNameSet, s); + s+=length+1; /* start of factor suffixes */ + + /* get the set and maximum factor suffix length for each factor */ + for(i=0; i0; --factor) { + factorLength=calcStringSetLength(gNameSet, s); + s+=factorLength+1; + if(factorLength>maxFactorLength) { + maxFactorLength=factorLength; + } + } + length+=maxFactorLength; + } + + if(length>maxNameLength) { + maxNameLength=length; + } + break; + } + default: + /* unknown type */ + break; + } + + range=(AlgorithmicRange *)((uint8_t *)range+range->size); + --rangeCount; + } + return maxNameLength; +} + +static int32_t +calcExtNameSetsLengths(int32_t maxNameLength) { + int32_t i, length; + + for(i=0; i + * 1 for - + * 6 for most hex digits per code point + */ + length=9+calcStringSetLength(gNameSet, charCatNames[i]); + if(length>maxNameLength) { + maxNameLength=length; + } + } + return maxNameLength; +} + +static int32_t +calcNameSetLength(const uint16_t *tokens, uint16_t tokenCount, const uint8_t *tokenStrings, int8_t *tokenLengths, + uint32_t set[8], + const uint8_t **pLine, const uint8_t *lineLimit) { + const uint8_t *line=*pLine; + int32_t length=0, tokenLength; + uint16_t c, token; + + while(line!=lineLimit && (c=*line++)!=(uint8_t)';') { + if(c>=tokenCount) { + /* implicit letter */ + SET_ADD(set, c); + ++length; + } else { + token=tokens[c]; + if(token==(uint16_t)(-2)) { + /* this is a lead byte for a double-byte token */ + c=c<<8|*line++; + token=tokens[c]; + } + if(token==(uint16_t)(-1)) { + /* explicit letter */ + SET_ADD(set, c); + ++length; + } else { + /* count token word */ + if(tokenLengths!=nullptr) { + /* use cached token length */ + tokenLength=tokenLengths[c]; + if(tokenLength==0) { + tokenLength=calcStringSetLength(set, (const char *)tokenStrings+token); + tokenLengths[c]=(int8_t)tokenLength; + } + } else { + tokenLength=calcStringSetLength(set, (const char *)tokenStrings+token); + } + length+=tokenLength; + } + } + } + + *pLine=line; + return length; +} + +static void +calcGroupNameSetsLengths(int32_t maxNameLength) { + uint16_t offsets[LINES_PER_GROUP+2], lengths[LINES_PER_GROUP+2]; + + uint16_t *tokens=(uint16_t *)uCharNames+8; + uint16_t tokenCount=*tokens++; + uint8_t *tokenStrings=(uint8_t *)uCharNames+uCharNames->tokenStringOffset; + + int8_t *tokenLengths; + + const uint16_t *group; + const uint8_t *s, *line, *lineLimit; + + int32_t groupCount, lineNumber, length; + + tokenLengths=(int8_t *)uprv_malloc(tokenCount); + if(tokenLengths!=nullptr) { + uprv_memset(tokenLengths, 0, tokenCount); + } + + group=GET_GROUPS(uCharNames); + groupCount=*group++; + + /* enumerate all groups */ + while(groupCount>0) { + s=(uint8_t *)uCharNames+uCharNames->groupStringOffset+GET_GROUP_OFFSET(group); + s=expandGroupLengths(s, offsets, lengths); + + /* enumerate all lines in each group */ + for(lineNumber=0; lineNumbermaxNameLength) { + maxNameLength=length; + } + if(line==lineLimit) { + continue; + } + + /* read Unicode 1.0 name */ + length=calcNameSetLength(tokens, tokenCount, tokenStrings, tokenLengths, gNameSet, &line, lineLimit); + if(length>maxNameLength) { + maxNameLength=length; + } + if(line==lineLimit) { + continue; + } + + /* read ISO comment */ + /*length=calcNameSetLength(tokens, tokenCount, tokenStrings, tokenLengths, gISOCommentSet, &line, lineLimit);*/ + } + + group=NEXT_GROUP(group); + --groupCount; + } + + if(tokenLengths!=nullptr) { + uprv_free(tokenLengths); + } + + /* set gMax... - name length last for threading */ + gMaxNameLength=maxNameLength; +} + +static UBool +calcNameSetsLengths(UErrorCode *pErrorCode) { + static const char extChars[]="0123456789ABCDEF<>-"; + int32_t i, maxNameLength; + + if(gMaxNameLength!=0) { + return true; + } + + if(!isDataLoaded(pErrorCode)) { + return false; + } + + /* set hex digits, used in various names, and <>-, used in extended names */ + for(i=0; i<(int32_t)sizeof(extChars)-1; ++i) { + SET_ADD(gNameSet, extChars[i]); + } + + /* set sets and lengths from algorithmic names */ + maxNameLength=calcAlgNameSetsLengths(0); + + /* set sets and lengths from extended names */ + maxNameLength=calcExtNameSetsLengths(maxNameLength); + + /* set sets and lengths from group names, set global maximum values */ + calcGroupNameSetsLengths(maxNameLength); + + return true; +} + +U_NAMESPACE_END + +/* public API --------------------------------------------------------------- */ + +U_NAMESPACE_USE + +U_CAPI int32_t U_EXPORT2 +u_charName(UChar32 code, UCharNameChoice nameChoice, + char *buffer, int32_t bufferLength, + UErrorCode *pErrorCode) { + AlgorithmicRange *algRange; + uint32_t *p; + uint32_t i; + int32_t length; + + /* check the argument values */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } else if(nameChoice>=U_CHAR_NAME_CHOICE_COUNT || + bufferLength<0 || (bufferLength>0 && buffer==nullptr) + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + if((uint32_t)code>UCHAR_MAX_VALUE || !isDataLoaded(pErrorCode)) { + return u_terminateChars(buffer, bufferLength, 0, pErrorCode); + } + + length=0; + + /* try algorithmic names first */ + p=(uint32_t *)((uint8_t *)uCharNames+uCharNames->algNamesOffset); + i=*p; + algRange=(AlgorithmicRange *)(p+1); + while(i>0) { + if(algRange->start<=(uint32_t)code && (uint32_t)code<=algRange->end) { + length=getAlgName(algRange, (uint32_t)code, nameChoice, buffer, (uint16_t)bufferLength); + break; + } + algRange=(AlgorithmicRange *)((uint8_t *)algRange+algRange->size); + --i; + } + + if(i==0) { + if (nameChoice == U_EXTENDED_CHAR_NAME) { + length = getName(uCharNames, (uint32_t )code, U_EXTENDED_CHAR_NAME, buffer, (uint16_t) bufferLength); + if (!length) { + /* extended character name */ + length = getExtName((uint32_t) code, buffer, (uint16_t) bufferLength); + } + } else { + /* normal character name */ + length=getName(uCharNames, (uint32_t)code, nameChoice, buffer, (uint16_t)bufferLength); + } + } + + return u_terminateChars(buffer, bufferLength, length, pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +u_getISOComment(UChar32 /*c*/, + char *dest, int32_t destCapacity, + UErrorCode *pErrorCode) { + /* check the argument values */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } else if(destCapacity<0 || (destCapacity>0 && dest==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + return u_terminateChars(dest, destCapacity, 0, pErrorCode); +} + +U_CAPI UChar32 U_EXPORT2 +u_charFromName(UCharNameChoice nameChoice, + const char *name, + UErrorCode *pErrorCode) { + char upper[120] = {0}; + char lower[120] = {0}; + FindName findName; + AlgorithmicRange *algRange; + uint32_t *p; + uint32_t i; + UChar32 cp = 0; + char c0; + static constexpr UChar32 error = 0xffff; /* Undefined, but use this for backwards compatibility. */ + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return error; + } + + if(nameChoice>=U_CHAR_NAME_CHOICE_COUNT || name==nullptr || *name==0) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return error; + } + + if(!isDataLoaded(pErrorCode)) { + return error; + } + + /* construct the uppercase and lowercase of the name first */ + for(i=0; i') { + // Parse a string like "" where HHHH is a hex code point. + uint32_t limit = i; + while (i >= 3 && lower[--i] != '-') {} + + // There should be 1 to 8 hex digits. + int32_t hexLength = limit - (i + 1); + if (i >= 2 && lower[i] == '-' && 1 <= hexLength && hexLength <= 8) { + uint32_t cIdx; + + lower[i] = 0; + + for (++i; i < limit; ++i) { + if (lower[i] >= '0' && lower[i] <= '9') { + cp = (cp << 4) + lower[i] - '0'; + } else if (lower[i] >= 'a' && lower[i] <= 'f') { + cp = (cp << 4) + lower[i] - 'a' + 10; + } else { + *pErrorCode = U_ILLEGAL_CHAR_FOUND; + return error; + } + // Prevent signed-integer overflow and out-of-range code points. + if (cp > UCHAR_MAX_VALUE) { + *pErrorCode = U_ILLEGAL_CHAR_FOUND; + return error; + } + } + + /* Now validate the category name. + We could use a binary search, or a trie, if + we really wanted to. */ + uint8_t cat = getCharCat(cp); + for (lower[i] = 0, cIdx = 0; cIdx < UPRV_LENGTHOF(charCatNames); ++cIdx) { + + if (!uprv_strcmp(lower + 1, charCatNames[cIdx])) { + if (cat == cIdx) { + return cp; + } + break; + } + } + } + } + + *pErrorCode = U_ILLEGAL_CHAR_FOUND; + return error; + } + + /* try algorithmic names now */ + p=(uint32_t *)((uint8_t *)uCharNames+uCharNames->algNamesOffset); + i=*p; + algRange=(AlgorithmicRange *)(p+1); + while(i>0) { + if((cp=findAlgName(algRange, nameChoice, upper))!=0xffff) { + return cp; + } + algRange=(AlgorithmicRange *)((uint8_t *)algRange+algRange->size); + --i; + } + + /* normal character name */ + findName.otherName=upper; + findName.code=error; + enumNames(uCharNames, 0, UCHAR_MAX_VALUE + 1, DO_FIND_NAME, &findName, nameChoice); + if (findName.code == error) { + *pErrorCode = U_ILLEGAL_CHAR_FOUND; + } + return findName.code; +} + +U_CAPI void U_EXPORT2 +u_enumCharNames(UChar32 start, UChar32 limit, + UEnumCharNamesFn *fn, + void *context, + UCharNameChoice nameChoice, + UErrorCode *pErrorCode) { + AlgorithmicRange *algRange; + uint32_t *p; + uint32_t i; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return; + } + + if(nameChoice>=U_CHAR_NAME_CHOICE_COUNT || fn==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + if((uint32_t) limit > UCHAR_MAX_VALUE + 1) { + limit = UCHAR_MAX_VALUE + 1; + } + if((uint32_t)start>=(uint32_t)limit) { + return; + } + + if(!isDataLoaded(pErrorCode)) { + return; + } + + /* interleave the data-driven ones with the algorithmic ones */ + /* iterate over all algorithmic ranges; assume that they are in ascending order */ + p=(uint32_t *)((uint8_t *)uCharNames+uCharNames->algNamesOffset); + i=*p; + algRange=(AlgorithmicRange *)(p+1); + while(i>0) { + /* enumerate the character names before the current algorithmic range */ + /* here: startstart) { + if((uint32_t)limit<=algRange->start) { + enumNames(uCharNames, start, limit, fn, context, nameChoice); + return; + } + if(!enumNames(uCharNames, start, (UChar32)algRange->start, fn, context, nameChoice)) { + return; + } + start=(UChar32)algRange->start; + } + /* enumerate the character names in the current algorithmic range */ + /* here: algRange->start<=startend) { + if((uint32_t)limit<=(algRange->end+1)) { + enumAlgNames(algRange, start, limit, fn, context, nameChoice); + return; + } + if(!enumAlgNames(algRange, start, (UChar32)algRange->end+1, fn, context, nameChoice)) { + return; + } + start=(UChar32)algRange->end+1; + } + /* continue to the next algorithmic range (here: startsize); + --i; + } + /* enumerate the character names after the last algorithmic range */ + enumNames(uCharNames, start, limit, fn, context, nameChoice); +} + +U_CAPI int32_t U_EXPORT2 +uprv_getMaxCharNameLength() { + UErrorCode errorCode=U_ZERO_ERROR; + if(calcNameSetsLengths(&errorCode)) { + return gMaxNameLength; + } else { + return 0; + } +} + +/** + * Converts the char set cset into a Unicode set uset. + * @param cset Set of 256 bit flags corresponding to a set of chars. + * @param uset USet to receive characters. Existing contents are deleted. + */ +static void +charSetToUSet(uint32_t cset[8], const USetAdder *sa) { + char16_t us[256]; + char cs[256]; + + int32_t i, length; + UErrorCode errorCode; + + errorCode=U_ZERO_ERROR; + + if(!calcNameSetsLengths(&errorCode)) { + return; + } + + /* build a char string with all chars that are used in character names */ + length=0; + for(i=0; i<256; ++i) { + if(SET_CONTAINS(cset, i)) { + cs[length++]=(char)i; + } + } + + /* convert the char string to a char16_t string */ + u_charsToUChars(cs, us, length); + + /* add each char16_t to the USet */ + for(i=0; iadd(sa->set, us[i]); + } + } +} + +/** + * Fills set with characters that are used in Unicode character names. + * @param set USet to receive characters. + */ +U_CAPI void U_EXPORT2 +uprv_getCharNameCharacters(const USetAdder *sa) { + charSetToUSet(gNameSet, sa); +} + +/* data swapping ------------------------------------------------------------ */ + +/* + * The token table contains non-negative entries for token bytes, + * and -1 for bytes that represent themselves in the data file's charset. + * -2 entries are used for lead bytes. + * + * Direct bytes (-1 entries) must be translated from the input charset family + * to the output charset family. + * makeTokenMap() writes a permutation mapping for this. + * Use it once for single-/lead-byte tokens and once more for all trail byte + * tokens. (';' is an unused trail byte marked with -1.) + */ +static void +makeTokenMap(const UDataSwapper *ds, + int16_t tokens[], uint16_t tokenCount, + uint8_t map[256], + UErrorCode *pErrorCode) { + UBool usedOutChar[256]; + uint16_t i, j; + uint8_t c1, c2; + + if(U_FAILURE(*pErrorCode)) { + return; + } + + if(ds->inCharset==ds->outCharset) { + /* Same charset family: identity permutation */ + for(i=0; i<256; ++i) { + map[i]=(uint8_t)i; + } + } else { + uprv_memset(map, 0, 256); + uprv_memset(usedOutChar, 0, 256); + + if(tokenCount>256) { + tokenCount=256; + } + + /* set the direct bytes (byte 0 always maps to itself) */ + for(i=1; iswapInvChars(ds, &c1, 1, &c2, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "unames/makeTokenMap() finds variant character 0x%02x used (input charset family %d)\n", + i, ds->inCharset); + return; + } + + /* enter the converted character into the map and mark it used */ + map[c1]=c2; + usedOutChar[c2]=true; + } + } + + /* set the mappings for the rest of the permutation */ + for(i=j=1; idataFormat[0]==0x75 && /* dataFormat="unam" */ + pInfo->dataFormat[1]==0x6e && + pInfo->dataFormat[2]==0x61 && + pInfo->dataFormat[3]==0x6d && + pInfo->formatVersion[0]==1 + )) { + udata_printError(ds, "uchar_swapNames(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as unames.icu\n", + pInfo->dataFormat[0], pInfo->dataFormat[1], + pInfo->dataFormat[2], pInfo->dataFormat[3], + pInfo->formatVersion[0]); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + inBytes=(const uint8_t *)inData+headerSize; + outBytes=(outData == nullptr) ? nullptr : (uint8_t *)outData+headerSize; + if(length<0) { + algNamesOffset=ds->readUInt32(((const uint32_t *)inBytes)[3]); + } else { + length-=headerSize; + if( length<20 || + (uint32_t)length<(algNamesOffset=ds->readUInt32(((const uint32_t *)inBytes)[3])) + ) { + udata_printError(ds, "uchar_swapNames(): too few bytes (%d after header) for unames.icu\n", + length); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + } + + if(length<0) { + /* preflighting: iterate through algorithmic ranges */ + offset=algNamesOffset; + count=ds->readUInt32(*((const uint32_t *)(inBytes+offset))); + offset+=4; + + for(i=0; ireadUInt16(inRange->size); + } + } else { + /* swap data */ + const uint16_t *p; + uint16_t *q, *temp; + + int16_t tokens[512]; + uint16_t tokenCount; + + uint8_t map[256], trailMap[256]; + + /* copy the data for inaccessible bytes */ + if(inBytes!=outBytes) { + uprv_memcpy(outBytes, inBytes, length); + } + + /* the initial 4 offsets first */ + tokenStringOffset=ds->readUInt32(((const uint32_t *)inBytes)[0]); + groupsOffset=ds->readUInt32(((const uint32_t *)inBytes)[1]); + groupStringOffset=ds->readUInt32(((const uint32_t *)inBytes)[2]); + ds->swapArray32(ds, inBytes, 16, outBytes, pErrorCode); + + /* + * now the tokens table + * it needs to be permutated along with the compressed name strings + */ + p=(const uint16_t *)(inBytes+16); + q=(uint16_t *)(outBytes+16); + + /* read and swap the tokenCount */ + tokenCount=ds->readUInt16(*p); + ds->swapArray16(ds, p, 2, q, pErrorCode); + ++p; + ++q; + + /* read the first 512 tokens and make the token maps */ + if(tokenCount<=512) { + count=tokenCount; + } else { + count=512; + } + for(i=0; i256 ? tokenCount-256 : 0), trailMap, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return 0; + } + + /* + * swap and permutate the tokens + * go through a temporary array to support in-place swapping + */ + temp=(uint16_t *)uprv_malloc(tokenCount*2); + if(temp==nullptr) { + udata_printError(ds, "out of memory swapping %u unames.icu tokens\n", + tokenCount); + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return 0; + } + + /* swap and permutate single-/lead-byte tokens */ + for(i=0; iswapArray16(ds, p+i, 2, temp+map[i], pErrorCode); + } + + /* swap and permutate trail-byte tokens */ + for(; iswapArray16(ds, p+i, 2, temp+(i&0xffffff00)+trailMap[i&0xff], pErrorCode); + } + + /* copy the result into the output and free the temporary array */ + uprv_memcpy(q, temp, tokenCount*2); + uprv_free(temp); + + /* + * swap the token strings but not a possible padding byte after + * the terminating NUL of the last string + */ + udata_swapInvStringBlock(ds, inBytes+tokenStringOffset, (int32_t)(groupsOffset-tokenStringOffset), + outBytes+tokenStringOffset, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "uchar_swapNames(token strings) failed\n"); + return 0; + } + + /* swap the group table */ + count=ds->readUInt16(*((const uint16_t *)(inBytes+groupsOffset))); + ds->swapArray16(ds, inBytes+groupsOffset, (int32_t)((1+count*3)*2), + outBytes+groupsOffset, pErrorCode); + + /* + * swap the group strings + * swap the string bytes but not the nibble-encoded string lengths + */ + if(ds->inCharset!=ds->outCharset) { + uint16_t offsets[LINES_PER_GROUP+1], lengths[LINES_PER_GROUP+1]; + + const uint8_t *inStrings, *nextInStrings; + uint8_t *outStrings; + + uint8_t c; + + inStrings=inBytes+groupStringOffset; + outStrings=outBytes+groupStringOffset; + + stringsCount=algNamesOffset-groupStringOffset; + + /* iterate through string groups until only a few padding bytes are left */ + while(stringsCount>32) { + nextInStrings=expandGroupLengths(inStrings, offsets, lengths); + + /* move past the length bytes */ + stringsCount-=(uint32_t)(nextInStrings-inStrings); + outStrings+=nextInStrings-inStrings; + inStrings=nextInStrings; + + count=offsets[31]+lengths[31]; /* total number of string bytes in this group */ + stringsCount-=count; + + /* swap the string bytes using map[] and trailMap[] */ + while(count>0) { + c=*inStrings++; + *outStrings++=map[c]; + if(tokens[c]!=-2) { + --count; + } else { + /* token lead byte: swap the trail byte, too */ + *outStrings++=trailMap[*inStrings++]; + count-=2; + } + } + } + } + + /* swap the algorithmic ranges */ + offset=algNamesOffset; + count=ds->readUInt32(*((const uint32_t *)(inBytes+offset))); + ds->swapArray32(ds, inBytes+offset, 4, outBytes+offset, pErrorCode); + offset+=4; + + for(i=0; i(uint32_t)length) { + udata_printError(ds, "uchar_swapNames(): too few bytes (%d after header) for unames.icu algorithmic range %u\n", + length, i); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + inRange=(const AlgorithmicRange *)(inBytes+offset); + outRange=(AlgorithmicRange *)(outBytes+offset); + offset+=ds->readUInt16(inRange->size); + + ds->swapArray32(ds, inRange, 8, outRange, pErrorCode); + ds->swapArray16(ds, &inRange->size, 2, &outRange->size, pErrorCode); + switch(inRange->type) { + case 0: + /* swap prefix string */ + ds->swapInvChars(ds, inRange+1, (int32_t)uprv_strlen((const char *)(inRange+1)), + outRange+1, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "uchar_swapNames(prefix string of algorithmic range %u) failed\n", + i); + return 0; + } + break; + case 1: + { + /* swap factors and the prefix and factor strings */ + uint32_t factorsCount; + + factorsCount=inRange->variant; + p=(const uint16_t *)(inRange+1); + q=(uint16_t *)(outRange+1); + ds->swapArray16(ds, p, (int32_t)(factorsCount*2), q, pErrorCode); + + /* swap the strings, up to the last terminating NUL */ + p+=factorsCount; + q+=factorsCount; + stringsCount=(uint32_t)((inBytes+offset)-(const uint8_t *)p); + while(stringsCount>0 && ((const uint8_t *)p)[stringsCount-1]!=0) { + --stringsCount; + } + ds->swapInvChars(ds, p, (int32_t)stringsCount, q, pErrorCode); + } + break; + default: + udata_printError(ds, "uchar_swapNames(): unknown type %u of algorithmic range %u\n", + inRange->type, i); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + } + } + + return headerSize+(int32_t)offset; +} + +/* + * Hey, Emacs, please set the following: + * + * Local Variables: + * indent-tabs-mode: nil + * End: + * + */ diff --git a/deps/icu-small/source/common/unicode/appendable.h b/deps/icu-small/source/common/unicode/appendable.h index f77df88e39c787..977b82d88c2a8c 100644 --- a/deps/icu-small/source/common/unicode/appendable.h +++ b/deps/icu-small/source/common/unicode/appendable.h @@ -1,239 +1,239 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011-2012, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: appendable.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010dec07 -* created by: Markus W. Scherer -*/ - -#ifndef __APPENDABLE_H__ -#define __APPENDABLE_H__ - -/** - * \file - * \brief C++ API: Appendable class: Sink for Unicode code points and 16-bit code units (char16_ts). - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/uobject.h" - -U_NAMESPACE_BEGIN - -class UnicodeString; - -/** - * Base class for objects to which Unicode characters and strings can be appended. - * Combines elements of Java Appendable and ICU4C ByteSink. - * - * This class can be used in APIs where it does not matter whether the actual destination is - * a UnicodeString, a char16_t[] array, a UnicodeSet, or any other object - * that receives and processes characters and/or strings. - * - * Implementation classes must implement at least appendCodeUnit(char16_t). - * The base class provides default implementations for the other methods. - * - * The methods do not take UErrorCode parameters. - * If an error occurs (e.g., out-of-memory), - * in addition to returning false from failing operations, - * the implementation must prevent unexpected behavior (e.g., crashes) - * from further calls and should make the error condition available separately - * (e.g., store a UErrorCode, make/keep a UnicodeString bogus). - * @stable ICU 4.8 - */ -class U_COMMON_API Appendable : public UObject { -public: - /** - * Destructor. - * @stable ICU 4.8 - */ - ~Appendable(); - - /** - * Appends a 16-bit code unit. - * @param c code unit - * @return true if the operation succeeded - * @stable ICU 4.8 - */ - virtual UBool appendCodeUnit(char16_t c) = 0; - - /** - * Appends a code point. - * The default implementation calls appendCodeUnit(char16_t) once or twice. - * @param c code point 0..0x10ffff - * @return true if the operation succeeded - * @stable ICU 4.8 - */ - virtual UBool appendCodePoint(UChar32 c); - - /** - * Appends a string. - * The default implementation calls appendCodeUnit(char16_t) for each code unit. - * @param s string, must not be NULL if length!=0 - * @param length string length, or -1 if NUL-terminated - * @return true if the operation succeeded - * @stable ICU 4.8 - */ - virtual UBool appendString(const char16_t *s, int32_t length); - - /** - * Tells the object that the caller is going to append roughly - * appendCapacity char16_ts. A subclass might use this to pre-allocate - * a larger buffer if necessary. - * The default implementation does nothing. (It always returns true.) - * @param appendCapacity estimated number of char16_ts that will be appended - * @return true if the operation succeeded - * @stable ICU 4.8 - */ - virtual UBool reserveAppendCapacity(int32_t appendCapacity); - - /** - * Returns a writable buffer for appending and writes the buffer's capacity to - * *resultCapacity. Guarantees *resultCapacity>=minCapacity. - * May return a pointer to the caller-owned scratch buffer which must have - * scratchCapacity>=minCapacity. - * The returned buffer is only valid until the next operation - * on this Appendable. - * - * After writing at most *resultCapacity char16_ts, call appendString() with the - * pointer returned from this function and the number of char16_ts written. - * Many appendString() implementations will avoid copying char16_ts if this function - * returned an internal buffer. - * - * Partial usage example: - * \code - * int32_t capacity; - * char16_t* buffer = app.getAppendBuffer(..., &capacity); - * ... Write n char16_ts into buffer, with n <= capacity. - * app.appendString(buffer, n); - * \endcode - * In many implementations, that call to append will avoid copying char16_ts. - * - * If the Appendable allocates or reallocates an internal buffer, it should use - * the desiredCapacityHint if appropriate. - * If a caller cannot provide a reasonable guess at the desired capacity, - * it should pass desiredCapacityHint=0. - * - * If a non-scratch buffer is returned, the caller may only pass - * a prefix to it to appendString(). - * That is, it is not correct to pass an interior pointer to appendString(). - * - * The default implementation always returns the scratch buffer. - * - * @param minCapacity required minimum capacity of the returned buffer; - * must be non-negative - * @param desiredCapacityHint desired capacity of the returned buffer; - * must be non-negative - * @param scratch default caller-owned buffer - * @param scratchCapacity capacity of the scratch buffer - * @param resultCapacity pointer to an integer which will be set to the - * capacity of the returned buffer - * @return a buffer with *resultCapacity>=minCapacity - * @stable ICU 4.8 - */ - virtual char16_t *getAppendBuffer(int32_t minCapacity, - int32_t desiredCapacityHint, - char16_t *scratch, int32_t scratchCapacity, - int32_t *resultCapacity); -}; - -/** - * An Appendable implementation which writes to a UnicodeString. - * - * This class is not intended for public subclassing. - * @stable ICU 4.8 - */ -class U_COMMON_API UnicodeStringAppendable : public Appendable { -public: - /** - * Aliases the UnicodeString (keeps its reference) for writing. - * @param s The UnicodeString to which this Appendable will write. - * @stable ICU 4.8 - */ - explicit UnicodeStringAppendable(UnicodeString &s) : str(s) {} - - /** - * Destructor. - * @stable ICU 4.8 - */ - ~UnicodeStringAppendable(); - - /** - * Appends a 16-bit code unit to the string. - * @param c code unit - * @return true if the operation succeeded - * @stable ICU 4.8 - */ - virtual UBool appendCodeUnit(char16_t c) override; - - /** - * Appends a code point to the string. - * @param c code point 0..0x10ffff - * @return true if the operation succeeded - * @stable ICU 4.8 - */ - virtual UBool appendCodePoint(UChar32 c) override; - - /** - * Appends a string to the UnicodeString. - * @param s string, must not be NULL if length!=0 - * @param length string length, or -1 if NUL-terminated - * @return true if the operation succeeded - * @stable ICU 4.8 - */ - virtual UBool appendString(const char16_t *s, int32_t length) override; - - /** - * Tells the UnicodeString that the caller is going to append roughly - * appendCapacity char16_ts. - * @param appendCapacity estimated number of char16_ts that will be appended - * @return true if the operation succeeded - * @stable ICU 4.8 - */ - virtual UBool reserveAppendCapacity(int32_t appendCapacity) override; - - /** - * Returns a writable buffer for appending and writes the buffer's capacity to - * *resultCapacity. Guarantees *resultCapacity>=minCapacity. - * May return a pointer to the caller-owned scratch buffer which must have - * scratchCapacity>=minCapacity. - * The returned buffer is only valid until the next write operation - * on the UnicodeString. - * - * For details see Appendable::getAppendBuffer(). - * - * @param minCapacity required minimum capacity of the returned buffer; - * must be non-negative - * @param desiredCapacityHint desired capacity of the returned buffer; - * must be non-negative - * @param scratch default caller-owned buffer - * @param scratchCapacity capacity of the scratch buffer - * @param resultCapacity pointer to an integer which will be set to the - * capacity of the returned buffer - * @return a buffer with *resultCapacity>=minCapacity - * @stable ICU 4.8 - */ - virtual char16_t *getAppendBuffer(int32_t minCapacity, - int32_t desiredCapacityHint, - char16_t *scratch, int32_t scratchCapacity, - int32_t *resultCapacity) override; - -private: - UnicodeString &str; -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __APPENDABLE_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2011-2012, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: appendable.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010dec07 +* created by: Markus W. Scherer +*/ + +#ifndef __APPENDABLE_H__ +#define __APPENDABLE_H__ + +/** + * \file + * \brief C++ API: Appendable class: Sink for Unicode code points and 16-bit code units (char16_ts). + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/uobject.h" + +U_NAMESPACE_BEGIN + +class UnicodeString; + +/** + * Base class for objects to which Unicode characters and strings can be appended. + * Combines elements of Java Appendable and ICU4C ByteSink. + * + * This class can be used in APIs where it does not matter whether the actual destination is + * a UnicodeString, a char16_t[] array, a UnicodeSet, or any other object + * that receives and processes characters and/or strings. + * + * Implementation classes must implement at least appendCodeUnit(char16_t). + * The base class provides default implementations for the other methods. + * + * The methods do not take UErrorCode parameters. + * If an error occurs (e.g., out-of-memory), + * in addition to returning false from failing operations, + * the implementation must prevent unexpected behavior (e.g., crashes) + * from further calls and should make the error condition available separately + * (e.g., store a UErrorCode, make/keep a UnicodeString bogus). + * @stable ICU 4.8 + */ +class U_COMMON_API Appendable : public UObject { +public: + /** + * Destructor. + * @stable ICU 4.8 + */ + ~Appendable(); + + /** + * Appends a 16-bit code unit. + * @param c code unit + * @return true if the operation succeeded + * @stable ICU 4.8 + */ + virtual UBool appendCodeUnit(char16_t c) = 0; + + /** + * Appends a code point. + * The default implementation calls appendCodeUnit(char16_t) once or twice. + * @param c code point 0..0x10ffff + * @return true if the operation succeeded + * @stable ICU 4.8 + */ + virtual UBool appendCodePoint(UChar32 c); + + /** + * Appends a string. + * The default implementation calls appendCodeUnit(char16_t) for each code unit. + * @param s string, must not be nullptr if length!=0 + * @param length string length, or -1 if NUL-terminated + * @return true if the operation succeeded + * @stable ICU 4.8 + */ + virtual UBool appendString(const char16_t *s, int32_t length); + + /** + * Tells the object that the caller is going to append roughly + * appendCapacity char16_ts. A subclass might use this to pre-allocate + * a larger buffer if necessary. + * The default implementation does nothing. (It always returns true.) + * @param appendCapacity estimated number of char16_ts that will be appended + * @return true if the operation succeeded + * @stable ICU 4.8 + */ + virtual UBool reserveAppendCapacity(int32_t appendCapacity); + + /** + * Returns a writable buffer for appending and writes the buffer's capacity to + * *resultCapacity. Guarantees *resultCapacity>=minCapacity. + * May return a pointer to the caller-owned scratch buffer which must have + * scratchCapacity>=minCapacity. + * The returned buffer is only valid until the next operation + * on this Appendable. + * + * After writing at most *resultCapacity char16_ts, call appendString() with the + * pointer returned from this function and the number of char16_ts written. + * Many appendString() implementations will avoid copying char16_ts if this function + * returned an internal buffer. + * + * Partial usage example: + * \code + * int32_t capacity; + * char16_t* buffer = app.getAppendBuffer(..., &capacity); + * ... Write n char16_ts into buffer, with n <= capacity. + * app.appendString(buffer, n); + * \endcode + * In many implementations, that call to append will avoid copying char16_ts. + * + * If the Appendable allocates or reallocates an internal buffer, it should use + * the desiredCapacityHint if appropriate. + * If a caller cannot provide a reasonable guess at the desired capacity, + * it should pass desiredCapacityHint=0. + * + * If a non-scratch buffer is returned, the caller may only pass + * a prefix to it to appendString(). + * That is, it is not correct to pass an interior pointer to appendString(). + * + * The default implementation always returns the scratch buffer. + * + * @param minCapacity required minimum capacity of the returned buffer; + * must be non-negative + * @param desiredCapacityHint desired capacity of the returned buffer; + * must be non-negative + * @param scratch default caller-owned buffer + * @param scratchCapacity capacity of the scratch buffer + * @param resultCapacity pointer to an integer which will be set to the + * capacity of the returned buffer + * @return a buffer with *resultCapacity>=minCapacity + * @stable ICU 4.8 + */ + virtual char16_t *getAppendBuffer(int32_t minCapacity, + int32_t desiredCapacityHint, + char16_t *scratch, int32_t scratchCapacity, + int32_t *resultCapacity); +}; + +/** + * An Appendable implementation which writes to a UnicodeString. + * + * This class is not intended for public subclassing. + * @stable ICU 4.8 + */ +class U_COMMON_API UnicodeStringAppendable : public Appendable { +public: + /** + * Aliases the UnicodeString (keeps its reference) for writing. + * @param s The UnicodeString to which this Appendable will write. + * @stable ICU 4.8 + */ + explicit UnicodeStringAppendable(UnicodeString &s) : str(s) {} + + /** + * Destructor. + * @stable ICU 4.8 + */ + ~UnicodeStringAppendable(); + + /** + * Appends a 16-bit code unit to the string. + * @param c code unit + * @return true if the operation succeeded + * @stable ICU 4.8 + */ + virtual UBool appendCodeUnit(char16_t c) override; + + /** + * Appends a code point to the string. + * @param c code point 0..0x10ffff + * @return true if the operation succeeded + * @stable ICU 4.8 + */ + virtual UBool appendCodePoint(UChar32 c) override; + + /** + * Appends a string to the UnicodeString. + * @param s string, must not be nullptr if length!=0 + * @param length string length, or -1 if NUL-terminated + * @return true if the operation succeeded + * @stable ICU 4.8 + */ + virtual UBool appendString(const char16_t *s, int32_t length) override; + + /** + * Tells the UnicodeString that the caller is going to append roughly + * appendCapacity char16_ts. + * @param appendCapacity estimated number of char16_ts that will be appended + * @return true if the operation succeeded + * @stable ICU 4.8 + */ + virtual UBool reserveAppendCapacity(int32_t appendCapacity) override; + + /** + * Returns a writable buffer for appending and writes the buffer's capacity to + * *resultCapacity. Guarantees *resultCapacity>=minCapacity. + * May return a pointer to the caller-owned scratch buffer which must have + * scratchCapacity>=minCapacity. + * The returned buffer is only valid until the next write operation + * on the UnicodeString. + * + * For details see Appendable::getAppendBuffer(). + * + * @param minCapacity required minimum capacity of the returned buffer; + * must be non-negative + * @param desiredCapacityHint desired capacity of the returned buffer; + * must be non-negative + * @param scratch default caller-owned buffer + * @param scratchCapacity capacity of the scratch buffer + * @param resultCapacity pointer to an integer which will be set to the + * capacity of the returned buffer + * @return a buffer with *resultCapacity>=minCapacity + * @stable ICU 4.8 + */ + virtual char16_t *getAppendBuffer(int32_t minCapacity, + int32_t desiredCapacityHint, + char16_t *scratch, int32_t scratchCapacity, + int32_t *resultCapacity) override; + +private: + UnicodeString &str; +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __APPENDABLE_H__ diff --git a/deps/icu-small/source/common/unicode/brkiter.h b/deps/icu-small/source/common/unicode/brkiter.h index 3a121cf703b337..0af189ac1e9aeb 100644 --- a/deps/icu-small/source/common/unicode/brkiter.h +++ b/deps/icu-small/source/common/unicode/brkiter.h @@ -1,670 +1,670 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************** -* Copyright (C) 1997-2016, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************** -* -* File brkiter.h -* -* Modification History: -* -* Date Name Description -* 02/18/97 aliu Added typedef for TextCount. Made DONE const. -* 05/07/97 aliu Fixed DLL declaration. -* 07/09/97 jfitz Renamed BreakIterator and interface synced with JDK -* 08/11/98 helena Sync-up JDK1.2. -* 01/13/2000 helena Added UErrorCode parameter to createXXXInstance methods. -******************************************************************************** -*/ - -#ifndef BRKITER_H -#define BRKITER_H - -#include "unicode/utypes.h" - -/** - * \file - * \brief C++ API: Break Iterator. - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#if UCONFIG_NO_BREAK_ITERATION - -U_NAMESPACE_BEGIN - -/* - * Allow the declaration of APIs with pointers to BreakIterator - * even when break iteration is removed from the build. - */ -class BreakIterator; - -U_NAMESPACE_END - -#else - -#include "unicode/uobject.h" -#include "unicode/unistr.h" -#include "unicode/chariter.h" -#include "unicode/locid.h" -#include "unicode/ubrk.h" -#include "unicode/strenum.h" -#include "unicode/utext.h" -#include "unicode/umisc.h" - -U_NAMESPACE_BEGIN - -/** - * The BreakIterator class implements methods for finding the location - * of boundaries in text. BreakIterator is an abstract base class. - * Instances of BreakIterator maintain a current position and scan over - * text returning the index of characters where boundaries occur. - *

- * Line boundary analysis determines where a text string can be broken - * when line-wrapping. The mechanism correctly handles punctuation and - * hyphenated words. - *

- * Sentence boundary analysis allows selection with correct - * interpretation of periods within numbers and abbreviations, and - * trailing punctuation marks such as quotation marks and parentheses. - *

- * Word boundary analysis is used by search and replace functions, as - * well as within text editing applications that allow the user to - * select words with a double click. Word selection provides correct - * interpretation of punctuation marks within and following - * words. Characters that are not part of a word, such as symbols or - * punctuation marks, have word-breaks on both sides. - *

- * Character boundary analysis allows users to interact with - * characters as they expect to, for example, when moving the cursor - * through a text string. Character boundary analysis provides correct - * navigation of through character strings, regardless of how the - * character is stored. For example, an accented character might be - * stored as a base character and a diacritical mark. What users - * consider to be a character can differ between languages. - *

- * The text boundary positions are found according to the rules - * described in Unicode Standard Annex #29, Text Boundaries, and - * Unicode Standard Annex #14, Line Breaking Properties. These - * are available at http://www.unicode.org/reports/tr14/ and - * http://www.unicode.org/reports/tr29/. - *

- * In addition to the C++ API defined in this header file, a - * plain C API with equivalent functionality is defined in the - * file ubrk.h - *

- * Code snippets illustrating the use of the Break Iterator APIs - * are available in the ICU User Guide, - * https://unicode-org.github.io/icu/userguide/boundaryanalysis/ - * and in the sample program icu/source/samples/break/break.cpp - * - */ -class U_COMMON_API BreakIterator : public UObject { -public: - /** - * destructor - * @stable ICU 2.0 - */ - virtual ~BreakIterator(); - - /** - * Return true if another object is semantically equal to this - * one. The other object should be an instance of the same subclass of - * BreakIterator. Objects of different subclasses are considered - * unequal. - *

- * Return true if this BreakIterator is at the same position in the - * same text, and is the same class and type (word, line, etc.) of - * BreakIterator, as the argument. Text is considered the same if - * it contains the same characters, it need not be the same - * object, and styles are not considered. - * @stable ICU 2.0 - */ - virtual bool operator==(const BreakIterator&) const = 0; - - /** - * Returns the complement of the result of operator== - * @param rhs The BreakIterator to be compared for inequality - * @return the complement of the result of operator== - * @stable ICU 2.0 - */ - bool operator!=(const BreakIterator& rhs) const { return !operator==(rhs); } - - /** - * Return a polymorphic copy of this object. This is an abstract - * method which subclasses implement. - * @stable ICU 2.0 - */ - virtual BreakIterator* clone() const = 0; - - /** - * Return a polymorphic class ID for this object. Different subclasses - * will return distinct unequal values. - * @stable ICU 2.0 - */ - virtual UClassID getDynamicClassID(void) const override = 0; - - /** - * Return a CharacterIterator over the text being analyzed. - * @stable ICU 2.0 - */ - virtual CharacterIterator& getText(void) const = 0; - - - /** - * Get a UText for the text being analyzed. - * The returned UText is a shallow clone of the UText used internally - * by the break iterator implementation. It can safely be used to - * access the text without impacting any break iterator operations, - * but the underlying text itself must not be altered. - * - * @param fillIn A UText to be filled in. If NULL, a new UText will be - * allocated to hold the result. - * @param status receives any error codes. - * @return The current UText for this break iterator. If an input - * UText was provided, it will always be returned. - * @stable ICU 3.4 - */ - virtual UText *getUText(UText *fillIn, UErrorCode &status) const = 0; - - /** - * Change the text over which this operates. The text boundary is - * reset to the start. - * - * The BreakIterator will retain a reference to the supplied string. - * The caller must not modify or delete the text while the BreakIterator - * retains the reference. - * - * @param text The UnicodeString used to change the text. - * @stable ICU 2.0 - */ - virtual void setText(const UnicodeString &text) = 0; - - /** - * Reset the break iterator to operate over the text represented by - * the UText. The iterator position is reset to the start. - * - * This function makes a shallow clone of the supplied UText. This means - * that the caller is free to immediately close or otherwise reuse the - * Utext that was passed as a parameter, but that the underlying text itself - * must not be altered while being referenced by the break iterator. - * - * All index positions returned by break iterator functions are - * native indices from the UText. For example, when breaking UTF-8 - * encoded text, the break positions returned by next(), previous(), etc. - * will be UTF-8 string indices, not UTF-16 positions. - * - * @param text The UText used to change the text. - * @param status receives any error codes. - * @stable ICU 3.4 - */ - virtual void setText(UText *text, UErrorCode &status) = 0; - - /** - * Change the text over which this operates. The text boundary is - * reset to the start. - * Note that setText(UText *) provides similar functionality to this function, - * and is more efficient. - * @param it The CharacterIterator used to change the text. - * @stable ICU 2.0 - */ - virtual void adoptText(CharacterIterator* it) = 0; - - enum { - /** - * DONE is returned by previous() and next() after all valid - * boundaries have been returned. - * @stable ICU 2.0 - */ - DONE = (int32_t)-1 - }; - - /** - * Sets the current iteration position to the beginning of the text, position zero. - * @return The offset of the beginning of the text, zero. - * @stable ICU 2.0 - */ - virtual int32_t first(void) = 0; - - /** - * Set the iterator position to the index immediately BEYOND the last character in the text being scanned. - * @return The index immediately BEYOND the last character in the text being scanned. - * @stable ICU 2.0 - */ - virtual int32_t last(void) = 0; - - /** - * Set the iterator position to the boundary preceding the current boundary. - * @return The character index of the previous text boundary or DONE if all - * boundaries have been returned. - * @stable ICU 2.0 - */ - virtual int32_t previous(void) = 0; - - /** - * Advance the iterator to the boundary following the current boundary. - * @return The character index of the next text boundary or DONE if all - * boundaries have been returned. - * @stable ICU 2.0 - */ - virtual int32_t next(void) = 0; - - /** - * Return character index of the current iterator position within the text. - * @return The boundary most recently returned. - * @stable ICU 2.0 - */ - virtual int32_t current(void) const = 0; - - /** - * Advance the iterator to the first boundary following the specified offset. - * The value returned is always greater than the offset or - * the value BreakIterator.DONE - * @param offset the offset to begin scanning. - * @return The first boundary after the specified offset. - * @stable ICU 2.0 - */ - virtual int32_t following(int32_t offset) = 0; - - /** - * Set the iterator position to the first boundary preceding the specified offset. - * The value returned is always smaller than the offset or - * the value BreakIterator.DONE - * @param offset the offset to begin scanning. - * @return The first boundary before the specified offset. - * @stable ICU 2.0 - */ - virtual int32_t preceding(int32_t offset) = 0; - - /** - * Return true if the specified position is a boundary position. - * As a side effect, the current position of the iterator is set - * to the first boundary position at or following the specified offset. - * @param offset the offset to check. - * @return True if "offset" is a boundary position. - * @stable ICU 2.0 - */ - virtual UBool isBoundary(int32_t offset) = 0; - - /** - * Set the iterator position to the nth boundary from the current boundary - * @param n the number of boundaries to move by. A value of 0 - * does nothing. Negative values move to previous boundaries - * and positive values move to later boundaries. - * @return The new iterator position, or - * DONE if there are fewer than |n| boundaries in the specified direction. - * @stable ICU 2.0 - */ - virtual int32_t next(int32_t n) = 0; - - /** - * For RuleBasedBreakIterators, return the status tag from the break rule - * that determined the boundary at the current iteration position. - *

- * For break iterator types that do not support a rule status, - * a default value of 0 is returned. - *

- * @return the status from the break rule that determined the boundary at - * the current iteration position. - * @see RuleBaseBreakIterator::getRuleStatus() - * @see UWordBreak - * @stable ICU 52 - */ - virtual int32_t getRuleStatus() const; - - /** - * For RuleBasedBreakIterators, get the status (tag) values from the break rule(s) - * that determined the boundary at the current iteration position. - *

- * For break iterator types that do not support rule status, - * no values are returned. - *

- * The returned status value(s) are stored into an array provided by the caller. - * The values are stored in sorted (ascending) order. - * If the capacity of the output array is insufficient to hold the data, - * the output will be truncated to the available length, and a - * U_BUFFER_OVERFLOW_ERROR will be signaled. - *

- * @see RuleBaseBreakIterator::getRuleStatusVec - * - * @param fillInVec an array to be filled in with the status values. - * @param capacity the length of the supplied vector. A length of zero causes - * the function to return the number of status values, in the - * normal way, without attempting to store any values. - * @param status receives error codes. - * @return The number of rule status values from rules that determined - * the boundary at the current iteration position. - * In the event of a U_BUFFER_OVERFLOW_ERROR, the return value - * is the total number of status values that were available, - * not the reduced number that were actually returned. - * @see getRuleStatus - * @stable ICU 52 - */ - virtual int32_t getRuleStatusVec(int32_t *fillInVec, int32_t capacity, UErrorCode &status); - - /** - * Create BreakIterator for word-breaks using the given locale. - * Returns an instance of a BreakIterator implementing word breaks. - * WordBreak is useful for word selection (ex. double click) - * @param where the locale. - * @param status the error code - * @return A BreakIterator for word-breaks. The UErrorCode& status - * parameter is used to return status information to the user. - * To check whether the construction succeeded or not, you should check - * the value of U_SUCCESS(err). If you wish more detailed information, you - * can check for informational error results which still indicate success. - * U_USING_FALLBACK_WARNING indicates that a fall back locale was used. For - * example, 'de_CH' was requested, but nothing was found there, so 'de' was - * used. U_USING_DEFAULT_WARNING indicates that the default locale data was - * used; neither the requested locale nor any of its fall back locales - * could be found. - * The caller owns the returned object and is responsible for deleting it. - * @stable ICU 2.0 - */ - static BreakIterator* U_EXPORT2 - createWordInstance(const Locale& where, UErrorCode& status); - - /** - * Create BreakIterator for line-breaks using specified locale. - * Returns an instance of a BreakIterator implementing line breaks. Line - * breaks are logically possible line breaks, actual line breaks are - * usually determined based on display width. - * LineBreak is useful for word wrapping text. - * @param where the locale. - * @param status The error code. - * @return A BreakIterator for line-breaks. The UErrorCode& status - * parameter is used to return status information to the user. - * To check whether the construction succeeded or not, you should check - * the value of U_SUCCESS(err). If you wish more detailed information, you - * can check for informational error results which still indicate success. - * U_USING_FALLBACK_WARNING indicates that a fall back locale was used. For - * example, 'de_CH' was requested, but nothing was found there, so 'de' was - * used. U_USING_DEFAULT_WARNING indicates that the default locale data was - * used; neither the requested locale nor any of its fall back locales - * could be found. - * The caller owns the returned object and is responsible for deleting it. - * @stable ICU 2.0 - */ - static BreakIterator* U_EXPORT2 - createLineInstance(const Locale& where, UErrorCode& status); - - /** - * Create BreakIterator for character-breaks using specified locale - * Returns an instance of a BreakIterator implementing character breaks. - * Character breaks are boundaries of combining character sequences. - * @param where the locale. - * @param status The error code. - * @return A BreakIterator for character-breaks. The UErrorCode& status - * parameter is used to return status information to the user. - * To check whether the construction succeeded or not, you should check - * the value of U_SUCCESS(err). If you wish more detailed information, you - * can check for informational error results which still indicate success. - * U_USING_FALLBACK_WARNING indicates that a fall back locale was used. For - * example, 'de_CH' was requested, but nothing was found there, so 'de' was - * used. U_USING_DEFAULT_WARNING indicates that the default locale data was - * used; neither the requested locale nor any of its fall back locales - * could be found. - * The caller owns the returned object and is responsible for deleting it. - * @stable ICU 2.0 - */ - static BreakIterator* U_EXPORT2 - createCharacterInstance(const Locale& where, UErrorCode& status); - - /** - * Create BreakIterator for sentence-breaks using specified locale - * Returns an instance of a BreakIterator implementing sentence breaks. - * @param where the locale. - * @param status The error code. - * @return A BreakIterator for sentence-breaks. The UErrorCode& status - * parameter is used to return status information to the user. - * To check whether the construction succeeded or not, you should check - * the value of U_SUCCESS(err). If you wish more detailed information, you - * can check for informational error results which still indicate success. - * U_USING_FALLBACK_WARNING indicates that a fall back locale was used. For - * example, 'de_CH' was requested, but nothing was found there, so 'de' was - * used. U_USING_DEFAULT_WARNING indicates that the default locale data was - * used; neither the requested locale nor any of its fall back locales - * could be found. - * The caller owns the returned object and is responsible for deleting it. - * @stable ICU 2.0 - */ - static BreakIterator* U_EXPORT2 - createSentenceInstance(const Locale& where, UErrorCode& status); - -#ifndef U_HIDE_DEPRECATED_API - /** - * Create BreakIterator for title-casing breaks using the specified locale - * Returns an instance of a BreakIterator implementing title breaks. - * The iterator returned locates title boundaries as described for - * Unicode 3.2 only. For Unicode 4.0 and above title boundary iteration, - * please use a word boundary iterator. See {@link #createWordInstance }. - * - * @param where the locale. - * @param status The error code. - * @return A BreakIterator for title-breaks. The UErrorCode& status - * parameter is used to return status information to the user. - * To check whether the construction succeeded or not, you should check - * the value of U_SUCCESS(err). If you wish more detailed information, you - * can check for informational error results which still indicate success. - * U_USING_FALLBACK_WARNING indicates that a fall back locale was used. For - * example, 'de_CH' was requested, but nothing was found there, so 'de' was - * used. U_USING_DEFAULT_WARNING indicates that the default locale data was - * used; neither the requested locale nor any of its fall back locales - * could be found. - * The caller owns the returned object and is responsible for deleting it. - * @deprecated ICU 64 Use createWordInstance instead. - */ - static BreakIterator* U_EXPORT2 - createTitleInstance(const Locale& where, UErrorCode& status); -#endif /* U_HIDE_DEPRECATED_API */ - - /** - * Get the set of Locales for which TextBoundaries are installed. - *

Note: this will not return locales added through the register - * call. To see the registered locales too, use the getAvailableLocales - * function that returns a StringEnumeration object

- * @param count the output parameter of number of elements in the locale list - * @return available locales - * @stable ICU 2.0 - */ - static const Locale* U_EXPORT2 getAvailableLocales(int32_t& count); - - /** - * Get name of the object for the desired Locale, in the desired language. - * @param objectLocale must be from getAvailableLocales. - * @param displayLocale specifies the desired locale for output. - * @param name the fill-in parameter of the return value - * Uses best match. - * @return user-displayable name - * @stable ICU 2.0 - */ - static UnicodeString& U_EXPORT2 getDisplayName(const Locale& objectLocale, - const Locale& displayLocale, - UnicodeString& name); - - /** - * Get name of the object for the desired Locale, in the language of the - * default locale. - * @param objectLocale must be from getMatchingLocales - * @param name the fill-in parameter of the return value - * @return user-displayable name - * @stable ICU 2.0 - */ - static UnicodeString& U_EXPORT2 getDisplayName(const Locale& objectLocale, - UnicodeString& name); - -#ifndef U_FORCE_HIDE_DEPRECATED_API - /** - * Deprecated functionality. Use clone() instead. - * - * Thread safe client-buffer-based cloning operation - * Do NOT call delete on a safeclone, since 'new' is not used to create it. - * @param stackBuffer user allocated space for the new clone. If NULL new memory will be allocated. - * If buffer is not large enough, new memory will be allocated. - * @param BufferSize reference to size of allocated space. - * If BufferSize == 0, a sufficient size for use in cloning will - * be returned ('pre-flighting') - * If BufferSize is not enough for a stack-based safe clone, - * new memory will be allocated. - * @param status to indicate whether the operation went on smoothly or there were errors - * An informational status value, U_SAFECLONE_ALLOCATED_ERROR, is used if any allocations were - * necessary. - * @return pointer to the new clone - * - * @deprecated ICU 52. Use clone() instead. - */ - virtual BreakIterator * createBufferClone(void *stackBuffer, - int32_t &BufferSize, - UErrorCode &status) = 0; -#endif // U_FORCE_HIDE_DEPRECATED_API - -#ifndef U_HIDE_DEPRECATED_API - - /** - * Determine whether the BreakIterator was created in user memory by - * createBufferClone(), and thus should not be deleted. Such objects - * must be closed by an explicit call to the destructor (not delete). - * @deprecated ICU 52. Always delete the BreakIterator. - */ - inline UBool isBufferClone(void); - -#endif /* U_HIDE_DEPRECATED_API */ - -#if !UCONFIG_NO_SERVICE - /** - * Register a new break iterator of the indicated kind, to use in the given locale. - * The break iterator will be adopted. Clones of the iterator will be returned - * if a request for a break iterator of the given kind matches or falls back to - * this locale. - * Because ICU may choose to cache BreakIterators internally, this must - * be called at application startup, prior to any calls to - * BreakIterator::createXXXInstance to avoid undefined behavior. - * @param toAdopt the BreakIterator instance to be adopted - * @param locale the Locale for which this instance is to be registered - * @param kind the type of iterator for which this instance is to be registered - * @param status the in/out status code, no special meanings are assigned - * @return a registry key that can be used to unregister this instance - * @stable ICU 2.4 - */ - static URegistryKey U_EXPORT2 registerInstance(BreakIterator* toAdopt, - const Locale& locale, - UBreakIteratorType kind, - UErrorCode& status); - - /** - * Unregister a previously-registered BreakIterator using the key returned from the - * register call. Key becomes invalid after a successful call and should not be used again. - * The BreakIterator corresponding to the key will be deleted. - * Because ICU may choose to cache BreakIterators internally, this should - * be called during application shutdown, after all calls to - * BreakIterator::createXXXInstance to avoid undefined behavior. - * @param key the registry key returned by a previous call to registerInstance - * @param status the in/out status code, no special meanings are assigned - * @return true if the iterator for the key was successfully unregistered - * @stable ICU 2.4 - */ - static UBool U_EXPORT2 unregister(URegistryKey key, UErrorCode& status); - - /** - * Return a StringEnumeration over the locales available at the time of the call, - * including registered locales. - * @return a StringEnumeration over the locales available at the time of the call - * @stable ICU 2.4 - */ - static StringEnumeration* U_EXPORT2 getAvailableLocales(void); -#endif - - /** - * Returns the locale for this break iterator. Two flavors are available: valid and - * actual locale. - * @stable ICU 2.8 - */ - Locale getLocale(ULocDataLocaleType type, UErrorCode& status) const; - -#ifndef U_HIDE_INTERNAL_API - /** Get the locale for this break iterator object. You can choose between valid and actual locale. - * @param type type of the locale we're looking for (valid or actual) - * @param status error code for the operation - * @return the locale - * @internal - */ - const char *getLocaleID(ULocDataLocaleType type, UErrorCode& status) const; -#endif /* U_HIDE_INTERNAL_API */ - - /** - * Set the subject text string upon which the break iterator is operating - * without changing any other aspect of the matching state. - * The new and previous text strings must have the same content. - * - * This function is intended for use in environments where ICU is operating on - * strings that may move around in memory. It provides a mechanism for notifying - * ICU that the string has been relocated, and providing a new UText to access the - * string in its new position. - * - * Note that the break iterator implementation never copies the underlying text - * of a string being processed, but always operates directly on the original text - * provided by the user. Refreshing simply drops the references to the old text - * and replaces them with references to the new. - * - * Caution: this function is normally used only by very specialized, - * system-level code. One example use case is with garbage collection that moves - * the text in memory. - * - * @param input The new (moved) text string. - * @param status Receives errors detected by this function. - * @return *this - * - * @stable ICU 49 - */ - virtual BreakIterator &refreshInputText(UText *input, UErrorCode &status) = 0; - - private: - static BreakIterator* buildInstance(const Locale& loc, const char *type, UErrorCode& status); - static BreakIterator* createInstance(const Locale& loc, int32_t kind, UErrorCode& status); - static BreakIterator* makeInstance(const Locale& loc, int32_t kind, UErrorCode& status); - - friend class ICUBreakIteratorFactory; - friend class ICUBreakIteratorService; - -protected: - // Do not enclose protected default/copy constructors with #ifndef U_HIDE_INTERNAL_API - // or else the compiler will create a public ones. - /** @internal */ - BreakIterator(); - /** @internal */ - BreakIterator (const BreakIterator &other); -#ifndef U_HIDE_INTERNAL_API - /** @internal */ - BreakIterator (const Locale& valid, const Locale &actual); - /** @internal. Assignment Operator, used by RuleBasedBreakIterator. */ - BreakIterator &operator = (const BreakIterator &other); -#endif /* U_HIDE_INTERNAL_API */ - -private: - - /** @internal (private) */ - char actualLocale[ULOC_FULLNAME_CAPACITY]; - char validLocale[ULOC_FULLNAME_CAPACITY]; -}; - -#ifndef U_HIDE_DEPRECATED_API - -inline UBool BreakIterator::isBufferClone() -{ - return false; -} - -#endif /* U_HIDE_DEPRECATED_API */ - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // BRKITER_H -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************** +* Copyright (C) 1997-2016, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************** +* +* File brkiter.h +* +* Modification History: +* +* Date Name Description +* 02/18/97 aliu Added typedef for TextCount. Made DONE const. +* 05/07/97 aliu Fixed DLL declaration. +* 07/09/97 jfitz Renamed BreakIterator and interface synced with JDK +* 08/11/98 helena Sync-up JDK1.2. +* 01/13/2000 helena Added UErrorCode parameter to createXXXInstance methods. +******************************************************************************** +*/ + +#ifndef BRKITER_H +#define BRKITER_H + +#include "unicode/utypes.h" + +/** + * \file + * \brief C++ API: Break Iterator. + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#if UCONFIG_NO_BREAK_ITERATION + +U_NAMESPACE_BEGIN + +/* + * Allow the declaration of APIs with pointers to BreakIterator + * even when break iteration is removed from the build. + */ +class BreakIterator; + +U_NAMESPACE_END + +#else + +#include "unicode/uobject.h" +#include "unicode/unistr.h" +#include "unicode/chariter.h" +#include "unicode/locid.h" +#include "unicode/ubrk.h" +#include "unicode/strenum.h" +#include "unicode/utext.h" +#include "unicode/umisc.h" + +U_NAMESPACE_BEGIN + +/** + * The BreakIterator class implements methods for finding the location + * of boundaries in text. BreakIterator is an abstract base class. + * Instances of BreakIterator maintain a current position and scan over + * text returning the index of characters where boundaries occur. + *

+ * Line boundary analysis determines where a text string can be broken + * when line-wrapping. The mechanism correctly handles punctuation and + * hyphenated words. + *

+ * Sentence boundary analysis allows selection with correct + * interpretation of periods within numbers and abbreviations, and + * trailing punctuation marks such as quotation marks and parentheses. + *

+ * Word boundary analysis is used by search and replace functions, as + * well as within text editing applications that allow the user to + * select words with a double click. Word selection provides correct + * interpretation of punctuation marks within and following + * words. Characters that are not part of a word, such as symbols or + * punctuation marks, have word-breaks on both sides. + *

+ * Character boundary analysis allows users to interact with + * characters as they expect to, for example, when moving the cursor + * through a text string. Character boundary analysis provides correct + * navigation of through character strings, regardless of how the + * character is stored. For example, an accented character might be + * stored as a base character and a diacritical mark. What users + * consider to be a character can differ between languages. + *

+ * The text boundary positions are found according to the rules + * described in Unicode Standard Annex #29, Text Boundaries, and + * Unicode Standard Annex #14, Line Breaking Properties. These + * are available at http://www.unicode.org/reports/tr14/ and + * http://www.unicode.org/reports/tr29/. + *

+ * In addition to the C++ API defined in this header file, a + * plain C API with equivalent functionality is defined in the + * file ubrk.h + *

+ * Code snippets illustrating the use of the Break Iterator APIs + * are available in the ICU User Guide, + * https://unicode-org.github.io/icu/userguide/boundaryanalysis/ + * and in the sample program icu/source/samples/break/break.cpp + * + */ +class U_COMMON_API BreakIterator : public UObject { +public: + /** + * destructor + * @stable ICU 2.0 + */ + virtual ~BreakIterator(); + + /** + * Return true if another object is semantically equal to this + * one. The other object should be an instance of the same subclass of + * BreakIterator. Objects of different subclasses are considered + * unequal. + *

+ * Return true if this BreakIterator is at the same position in the + * same text, and is the same class and type (word, line, etc.) of + * BreakIterator, as the argument. Text is considered the same if + * it contains the same characters, it need not be the same + * object, and styles are not considered. + * @stable ICU 2.0 + */ + virtual bool operator==(const BreakIterator&) const = 0; + + /** + * Returns the complement of the result of operator== + * @param rhs The BreakIterator to be compared for inequality + * @return the complement of the result of operator== + * @stable ICU 2.0 + */ + bool operator!=(const BreakIterator& rhs) const { return !operator==(rhs); } + + /** + * Return a polymorphic copy of this object. This is an abstract + * method which subclasses implement. + * @stable ICU 2.0 + */ + virtual BreakIterator* clone() const = 0; + + /** + * Return a polymorphic class ID for this object. Different subclasses + * will return distinct unequal values. + * @stable ICU 2.0 + */ + virtual UClassID getDynamicClassID(void) const override = 0; + + /** + * Return a CharacterIterator over the text being analyzed. + * @stable ICU 2.0 + */ + virtual CharacterIterator& getText(void) const = 0; + + + /** + * Get a UText for the text being analyzed. + * The returned UText is a shallow clone of the UText used internally + * by the break iterator implementation. It can safely be used to + * access the text without impacting any break iterator operations, + * but the underlying text itself must not be altered. + * + * @param fillIn A UText to be filled in. If nullptr, a new UText will be + * allocated to hold the result. + * @param status receives any error codes. + * @return The current UText for this break iterator. If an input + * UText was provided, it will always be returned. + * @stable ICU 3.4 + */ + virtual UText *getUText(UText *fillIn, UErrorCode &status) const = 0; + + /** + * Change the text over which this operates. The text boundary is + * reset to the start. + * + * The BreakIterator will retain a reference to the supplied string. + * The caller must not modify or delete the text while the BreakIterator + * retains the reference. + * + * @param text The UnicodeString used to change the text. + * @stable ICU 2.0 + */ + virtual void setText(const UnicodeString &text) = 0; + + /** + * Reset the break iterator to operate over the text represented by + * the UText. The iterator position is reset to the start. + * + * This function makes a shallow clone of the supplied UText. This means + * that the caller is free to immediately close or otherwise reuse the + * Utext that was passed as a parameter, but that the underlying text itself + * must not be altered while being referenced by the break iterator. + * + * All index positions returned by break iterator functions are + * native indices from the UText. For example, when breaking UTF-8 + * encoded text, the break positions returned by next(), previous(), etc. + * will be UTF-8 string indices, not UTF-16 positions. + * + * @param text The UText used to change the text. + * @param status receives any error codes. + * @stable ICU 3.4 + */ + virtual void setText(UText *text, UErrorCode &status) = 0; + + /** + * Change the text over which this operates. The text boundary is + * reset to the start. + * Note that setText(UText *) provides similar functionality to this function, + * and is more efficient. + * @param it The CharacterIterator used to change the text. + * @stable ICU 2.0 + */ + virtual void adoptText(CharacterIterator* it) = 0; + + enum { + /** + * DONE is returned by previous() and next() after all valid + * boundaries have been returned. + * @stable ICU 2.0 + */ + DONE = (int32_t)-1 + }; + + /** + * Sets the current iteration position to the beginning of the text, position zero. + * @return The offset of the beginning of the text, zero. + * @stable ICU 2.0 + */ + virtual int32_t first(void) = 0; + + /** + * Set the iterator position to the index immediately BEYOND the last character in the text being scanned. + * @return The index immediately BEYOND the last character in the text being scanned. + * @stable ICU 2.0 + */ + virtual int32_t last(void) = 0; + + /** + * Set the iterator position to the boundary preceding the current boundary. + * @return The character index of the previous text boundary or DONE if all + * boundaries have been returned. + * @stable ICU 2.0 + */ + virtual int32_t previous(void) = 0; + + /** + * Advance the iterator to the boundary following the current boundary. + * @return The character index of the next text boundary or DONE if all + * boundaries have been returned. + * @stable ICU 2.0 + */ + virtual int32_t next(void) = 0; + + /** + * Return character index of the current iterator position within the text. + * @return The boundary most recently returned. + * @stable ICU 2.0 + */ + virtual int32_t current(void) const = 0; + + /** + * Advance the iterator to the first boundary following the specified offset. + * The value returned is always greater than the offset or + * the value BreakIterator.DONE + * @param offset the offset to begin scanning. + * @return The first boundary after the specified offset. + * @stable ICU 2.0 + */ + virtual int32_t following(int32_t offset) = 0; + + /** + * Set the iterator position to the first boundary preceding the specified offset. + * The value returned is always smaller than the offset or + * the value BreakIterator.DONE + * @param offset the offset to begin scanning. + * @return The first boundary before the specified offset. + * @stable ICU 2.0 + */ + virtual int32_t preceding(int32_t offset) = 0; + + /** + * Return true if the specified position is a boundary position. + * As a side effect, the current position of the iterator is set + * to the first boundary position at or following the specified offset. + * @param offset the offset to check. + * @return True if "offset" is a boundary position. + * @stable ICU 2.0 + */ + virtual UBool isBoundary(int32_t offset) = 0; + + /** + * Set the iterator position to the nth boundary from the current boundary + * @param n the number of boundaries to move by. A value of 0 + * does nothing. Negative values move to previous boundaries + * and positive values move to later boundaries. + * @return The new iterator position, or + * DONE if there are fewer than |n| boundaries in the specified direction. + * @stable ICU 2.0 + */ + virtual int32_t next(int32_t n) = 0; + + /** + * For RuleBasedBreakIterators, return the status tag from the break rule + * that determined the boundary at the current iteration position. + *

+ * For break iterator types that do not support a rule status, + * a default value of 0 is returned. + *

+ * @return the status from the break rule that determined the boundary at + * the current iteration position. + * @see RuleBaseBreakIterator::getRuleStatus() + * @see UWordBreak + * @stable ICU 52 + */ + virtual int32_t getRuleStatus() const; + + /** + * For RuleBasedBreakIterators, get the status (tag) values from the break rule(s) + * that determined the boundary at the current iteration position. + *

+ * For break iterator types that do not support rule status, + * no values are returned. + *

+ * The returned status value(s) are stored into an array provided by the caller. + * The values are stored in sorted (ascending) order. + * If the capacity of the output array is insufficient to hold the data, + * the output will be truncated to the available length, and a + * U_BUFFER_OVERFLOW_ERROR will be signaled. + *

+ * @see RuleBaseBreakIterator::getRuleStatusVec + * + * @param fillInVec an array to be filled in with the status values. + * @param capacity the length of the supplied vector. A length of zero causes + * the function to return the number of status values, in the + * normal way, without attempting to store any values. + * @param status receives error codes. + * @return The number of rule status values from rules that determined + * the boundary at the current iteration position. + * In the event of a U_BUFFER_OVERFLOW_ERROR, the return value + * is the total number of status values that were available, + * not the reduced number that were actually returned. + * @see getRuleStatus + * @stable ICU 52 + */ + virtual int32_t getRuleStatusVec(int32_t *fillInVec, int32_t capacity, UErrorCode &status); + + /** + * Create BreakIterator for word-breaks using the given locale. + * Returns an instance of a BreakIterator implementing word breaks. + * WordBreak is useful for word selection (ex. double click) + * @param where the locale. + * @param status the error code + * @return A BreakIterator for word-breaks. The UErrorCode& status + * parameter is used to return status information to the user. + * To check whether the construction succeeded or not, you should check + * the value of U_SUCCESS(err). If you wish more detailed information, you + * can check for informational error results which still indicate success. + * U_USING_FALLBACK_WARNING indicates that a fall back locale was used. For + * example, 'de_CH' was requested, but nothing was found there, so 'de' was + * used. U_USING_DEFAULT_WARNING indicates that the default locale data was + * used; neither the requested locale nor any of its fall back locales + * could be found. + * The caller owns the returned object and is responsible for deleting it. + * @stable ICU 2.0 + */ + static BreakIterator* U_EXPORT2 + createWordInstance(const Locale& where, UErrorCode& status); + + /** + * Create BreakIterator for line-breaks using specified locale. + * Returns an instance of a BreakIterator implementing line breaks. Line + * breaks are logically possible line breaks, actual line breaks are + * usually determined based on display width. + * LineBreak is useful for word wrapping text. + * @param where the locale. + * @param status The error code. + * @return A BreakIterator for line-breaks. The UErrorCode& status + * parameter is used to return status information to the user. + * To check whether the construction succeeded or not, you should check + * the value of U_SUCCESS(err). If you wish more detailed information, you + * can check for informational error results which still indicate success. + * U_USING_FALLBACK_WARNING indicates that a fall back locale was used. For + * example, 'de_CH' was requested, but nothing was found there, so 'de' was + * used. U_USING_DEFAULT_WARNING indicates that the default locale data was + * used; neither the requested locale nor any of its fall back locales + * could be found. + * The caller owns the returned object and is responsible for deleting it. + * @stable ICU 2.0 + */ + static BreakIterator* U_EXPORT2 + createLineInstance(const Locale& where, UErrorCode& status); + + /** + * Create BreakIterator for character-breaks using specified locale + * Returns an instance of a BreakIterator implementing character breaks. + * Character breaks are boundaries of combining character sequences. + * @param where the locale. + * @param status The error code. + * @return A BreakIterator for character-breaks. The UErrorCode& status + * parameter is used to return status information to the user. + * To check whether the construction succeeded or not, you should check + * the value of U_SUCCESS(err). If you wish more detailed information, you + * can check for informational error results which still indicate success. + * U_USING_FALLBACK_WARNING indicates that a fall back locale was used. For + * example, 'de_CH' was requested, but nothing was found there, so 'de' was + * used. U_USING_DEFAULT_WARNING indicates that the default locale data was + * used; neither the requested locale nor any of its fall back locales + * could be found. + * The caller owns the returned object and is responsible for deleting it. + * @stable ICU 2.0 + */ + static BreakIterator* U_EXPORT2 + createCharacterInstance(const Locale& where, UErrorCode& status); + + /** + * Create BreakIterator for sentence-breaks using specified locale + * Returns an instance of a BreakIterator implementing sentence breaks. + * @param where the locale. + * @param status The error code. + * @return A BreakIterator for sentence-breaks. The UErrorCode& status + * parameter is used to return status information to the user. + * To check whether the construction succeeded or not, you should check + * the value of U_SUCCESS(err). If you wish more detailed information, you + * can check for informational error results which still indicate success. + * U_USING_FALLBACK_WARNING indicates that a fall back locale was used. For + * example, 'de_CH' was requested, but nothing was found there, so 'de' was + * used. U_USING_DEFAULT_WARNING indicates that the default locale data was + * used; neither the requested locale nor any of its fall back locales + * could be found. + * The caller owns the returned object and is responsible for deleting it. + * @stable ICU 2.0 + */ + static BreakIterator* U_EXPORT2 + createSentenceInstance(const Locale& where, UErrorCode& status); + +#ifndef U_HIDE_DEPRECATED_API + /** + * Create BreakIterator for title-casing breaks using the specified locale + * Returns an instance of a BreakIterator implementing title breaks. + * The iterator returned locates title boundaries as described for + * Unicode 3.2 only. For Unicode 4.0 and above title boundary iteration, + * please use a word boundary iterator. See {@link #createWordInstance }. + * + * @param where the locale. + * @param status The error code. + * @return A BreakIterator for title-breaks. The UErrorCode& status + * parameter is used to return status information to the user. + * To check whether the construction succeeded or not, you should check + * the value of U_SUCCESS(err). If you wish more detailed information, you + * can check for informational error results which still indicate success. + * U_USING_FALLBACK_WARNING indicates that a fall back locale was used. For + * example, 'de_CH' was requested, but nothing was found there, so 'de' was + * used. U_USING_DEFAULT_WARNING indicates that the default locale data was + * used; neither the requested locale nor any of its fall back locales + * could be found. + * The caller owns the returned object and is responsible for deleting it. + * @deprecated ICU 64 Use createWordInstance instead. + */ + static BreakIterator* U_EXPORT2 + createTitleInstance(const Locale& where, UErrorCode& status); +#endif /* U_HIDE_DEPRECATED_API */ + + /** + * Get the set of Locales for which TextBoundaries are installed. + *

Note: this will not return locales added through the register + * call. To see the registered locales too, use the getAvailableLocales + * function that returns a StringEnumeration object

+ * @param count the output parameter of number of elements in the locale list + * @return available locales + * @stable ICU 2.0 + */ + static const Locale* U_EXPORT2 getAvailableLocales(int32_t& count); + + /** + * Get name of the object for the desired Locale, in the desired language. + * @param objectLocale must be from getAvailableLocales. + * @param displayLocale specifies the desired locale for output. + * @param name the fill-in parameter of the return value + * Uses best match. + * @return user-displayable name + * @stable ICU 2.0 + */ + static UnicodeString& U_EXPORT2 getDisplayName(const Locale& objectLocale, + const Locale& displayLocale, + UnicodeString& name); + + /** + * Get name of the object for the desired Locale, in the language of the + * default locale. + * @param objectLocale must be from getMatchingLocales + * @param name the fill-in parameter of the return value + * @return user-displayable name + * @stable ICU 2.0 + */ + static UnicodeString& U_EXPORT2 getDisplayName(const Locale& objectLocale, + UnicodeString& name); + +#ifndef U_FORCE_HIDE_DEPRECATED_API + /** + * Deprecated functionality. Use clone() instead. + * + * Thread safe client-buffer-based cloning operation + * Do NOT call delete on a safeclone, since 'new' is not used to create it. + * @param stackBuffer user allocated space for the new clone. If nullptr new memory will be allocated. + * If buffer is not large enough, new memory will be allocated. + * @param BufferSize reference to size of allocated space. + * If BufferSize == 0, a sufficient size for use in cloning will + * be returned ('pre-flighting') + * If BufferSize is not enough for a stack-based safe clone, + * new memory will be allocated. + * @param status to indicate whether the operation went on smoothly or there were errors + * An informational status value, U_SAFECLONE_ALLOCATED_ERROR, is used if any allocations were + * necessary. + * @return pointer to the new clone + * + * @deprecated ICU 52. Use clone() instead. + */ + virtual BreakIterator * createBufferClone(void *stackBuffer, + int32_t &BufferSize, + UErrorCode &status) = 0; +#endif // U_FORCE_HIDE_DEPRECATED_API + +#ifndef U_HIDE_DEPRECATED_API + + /** + * Determine whether the BreakIterator was created in user memory by + * createBufferClone(), and thus should not be deleted. Such objects + * must be closed by an explicit call to the destructor (not delete). + * @deprecated ICU 52. Always delete the BreakIterator. + */ + inline UBool isBufferClone(void); + +#endif /* U_HIDE_DEPRECATED_API */ + +#if !UCONFIG_NO_SERVICE + /** + * Register a new break iterator of the indicated kind, to use in the given locale. + * The break iterator will be adopted. Clones of the iterator will be returned + * if a request for a break iterator of the given kind matches or falls back to + * this locale. + * Because ICU may choose to cache BreakIterators internally, this must + * be called at application startup, prior to any calls to + * BreakIterator::createXXXInstance to avoid undefined behavior. + * @param toAdopt the BreakIterator instance to be adopted + * @param locale the Locale for which this instance is to be registered + * @param kind the type of iterator for which this instance is to be registered + * @param status the in/out status code, no special meanings are assigned + * @return a registry key that can be used to unregister this instance + * @stable ICU 2.4 + */ + static URegistryKey U_EXPORT2 registerInstance(BreakIterator* toAdopt, + const Locale& locale, + UBreakIteratorType kind, + UErrorCode& status); + + /** + * Unregister a previously-registered BreakIterator using the key returned from the + * register call. Key becomes invalid after a successful call and should not be used again. + * The BreakIterator corresponding to the key will be deleted. + * Because ICU may choose to cache BreakIterators internally, this should + * be called during application shutdown, after all calls to + * BreakIterator::createXXXInstance to avoid undefined behavior. + * @param key the registry key returned by a previous call to registerInstance + * @param status the in/out status code, no special meanings are assigned + * @return true if the iterator for the key was successfully unregistered + * @stable ICU 2.4 + */ + static UBool U_EXPORT2 unregister(URegistryKey key, UErrorCode& status); + + /** + * Return a StringEnumeration over the locales available at the time of the call, + * including registered locales. + * @return a StringEnumeration over the locales available at the time of the call + * @stable ICU 2.4 + */ + static StringEnumeration* U_EXPORT2 getAvailableLocales(void); +#endif + + /** + * Returns the locale for this break iterator. Two flavors are available: valid and + * actual locale. + * @stable ICU 2.8 + */ + Locale getLocale(ULocDataLocaleType type, UErrorCode& status) const; + +#ifndef U_HIDE_INTERNAL_API + /** Get the locale for this break iterator object. You can choose between valid and actual locale. + * @param type type of the locale we're looking for (valid or actual) + * @param status error code for the operation + * @return the locale + * @internal + */ + const char *getLocaleID(ULocDataLocaleType type, UErrorCode& status) const; +#endif /* U_HIDE_INTERNAL_API */ + + /** + * Set the subject text string upon which the break iterator is operating + * without changing any other aspect of the matching state. + * The new and previous text strings must have the same content. + * + * This function is intended for use in environments where ICU is operating on + * strings that may move around in memory. It provides a mechanism for notifying + * ICU that the string has been relocated, and providing a new UText to access the + * string in its new position. + * + * Note that the break iterator implementation never copies the underlying text + * of a string being processed, but always operates directly on the original text + * provided by the user. Refreshing simply drops the references to the old text + * and replaces them with references to the new. + * + * Caution: this function is normally used only by very specialized, + * system-level code. One example use case is with garbage collection that moves + * the text in memory. + * + * @param input The new (moved) text string. + * @param status Receives errors detected by this function. + * @return *this + * + * @stable ICU 49 + */ + virtual BreakIterator &refreshInputText(UText *input, UErrorCode &status) = 0; + + private: + static BreakIterator* buildInstance(const Locale& loc, const char *type, UErrorCode& status); + static BreakIterator* createInstance(const Locale& loc, int32_t kind, UErrorCode& status); + static BreakIterator* makeInstance(const Locale& loc, int32_t kind, UErrorCode& status); + + friend class ICUBreakIteratorFactory; + friend class ICUBreakIteratorService; + +protected: + // Do not enclose protected default/copy constructors with #ifndef U_HIDE_INTERNAL_API + // or else the compiler will create a public ones. + /** @internal */ + BreakIterator(); + /** @internal */ + BreakIterator (const BreakIterator &other); +#ifndef U_HIDE_INTERNAL_API + /** @internal */ + BreakIterator (const Locale& valid, const Locale &actual); + /** @internal. Assignment Operator, used by RuleBasedBreakIterator. */ + BreakIterator &operator = (const BreakIterator &other); +#endif /* U_HIDE_INTERNAL_API */ + +private: + + /** @internal (private) */ + char actualLocale[ULOC_FULLNAME_CAPACITY]; + char validLocale[ULOC_FULLNAME_CAPACITY]; +}; + +#ifndef U_HIDE_DEPRECATED_API + +inline UBool BreakIterator::isBufferClone() +{ + return false; +} + +#endif /* U_HIDE_DEPRECATED_API */ + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // BRKITER_H +//eof diff --git a/deps/icu-small/source/common/unicode/bytestream.h b/deps/icu-small/source/common/unicode/bytestream.h index 997746e428040f..bc1171887cd37c 100644 --- a/deps/icu-small/source/common/unicode/bytestream.h +++ b/deps/icu-small/source/common/unicode/bytestream.h @@ -1,307 +1,307 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// Copyright (C) 2009-2012, International Business Machines -// Corporation and others. All Rights Reserved. -// -// Copyright 2007 Google Inc. All Rights Reserved. -// Author: sanjay@google.com (Sanjay Ghemawat) -// -// Abstract interface that consumes a sequence of bytes (ByteSink). -// -// Used so that we can write a single piece of code that can operate -// on a variety of output string types. -// -// Various implementations of this interface are provided: -// ByteSink: -// CheckedArrayByteSink Write to a flat array, with bounds checking -// StringByteSink Write to an STL string - -// This code is a contribution of Google code, and the style used here is -// a compromise between the original Google code and the ICU coding guidelines. -// For example, data types are ICU-ified (size_t,int->int32_t), -// and API comments doxygen-ified, but function names and behavior are -// as in the original, if possible. -// Assertion-style error handling, not available in ICU, was changed to -// parameter "pinning" similar to UnicodeString. -// -// In addition, this is only a partial port of the original Google code, -// limited to what was needed so far. The (nearly) complete original code -// is in the ICU svn repository at icuhtml/trunk/design/strings/contrib -// (see ICU ticket 6765, r25517). - -#ifndef __BYTESTREAM_H__ -#define __BYTESTREAM_H__ - -/** - * \file - * \brief C++ API: Interface for writing bytes, and implementation classes. - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/uobject.h" -#include "unicode/std_string.h" - -U_NAMESPACE_BEGIN - -/** - * A ByteSink can be filled with bytes. - * @stable ICU 4.2 - */ -class U_COMMON_API ByteSink : public UMemory { -public: - /** - * Default constructor. - * @stable ICU 4.2 - */ - ByteSink() { } - /** - * Virtual destructor. - * @stable ICU 4.2 - */ - virtual ~ByteSink(); - - /** - * Append "bytes[0,n-1]" to this. - * @param bytes the pointer to the bytes - * @param n the number of bytes; must be non-negative - * @stable ICU 4.2 - */ - virtual void Append(const char* bytes, int32_t n) = 0; - - /** - * Appends n bytes to this. Same as Append(). - * Call AppendU8() with u8"string literals" which are const char * in C++11 - * but const char8_t * in C++20. - * If the compiler does support char8_t as a distinct type, - * then an AppendU8() overload for that is defined and will be chosen. - * - * @param bytes the pointer to the bytes - * @param n the number of bytes; must be non-negative - * @stable ICU 67 - */ - inline void AppendU8(const char* bytes, int32_t n) { - Append(bytes, n); - } - -#if defined(__cpp_char8_t) || defined(U_IN_DOXYGEN) - /** - * Appends n bytes to this. Same as Append() but for a const char8_t * pointer. - * Call AppendU8() with u8"string literals" which are const char * in C++11 - * but const char8_t * in C++20. - * If the compiler does support char8_t as a distinct type, - * then this AppendU8() overload for that is defined and will be chosen. - * - * @param bytes the pointer to the bytes - * @param n the number of bytes; must be non-negative - * @stable ICU 67 - */ - inline void AppendU8(const char8_t* bytes, int32_t n) { - Append(reinterpret_cast(bytes), n); - } -#endif - - /** - * Returns a writable buffer for appending and writes the buffer's capacity to - * *result_capacity. Guarantees *result_capacity>=min_capacity. - * May return a pointer to the caller-owned scratch buffer which must have - * scratch_capacity>=min_capacity. - * The returned buffer is only valid until the next operation - * on this ByteSink. - * - * After writing at most *result_capacity bytes, call Append() with the - * pointer returned from this function and the number of bytes written. - * Many Append() implementations will avoid copying bytes if this function - * returned an internal buffer. - * - * Partial usage example: - * int32_t capacity; - * char* buffer = sink->GetAppendBuffer(..., &capacity); - * ... Write n bytes into buffer, with n <= capacity. - * sink->Append(buffer, n); - * In many implementations, that call to Append will avoid copying bytes. - * - * If the ByteSink allocates or reallocates an internal buffer, it should use - * the desired_capacity_hint if appropriate. - * If a caller cannot provide a reasonable guess at the desired capacity, - * it should pass desired_capacity_hint=0. - * - * If a non-scratch buffer is returned, the caller may only pass - * a prefix to it to Append(). - * That is, it is not correct to pass an interior pointer to Append(). - * - * The default implementation always returns the scratch buffer. - * - * @param min_capacity required minimum capacity of the returned buffer; - * must be non-negative - * @param desired_capacity_hint desired capacity of the returned buffer; - * must be non-negative - * @param scratch default caller-owned buffer - * @param scratch_capacity capacity of the scratch buffer - * @param result_capacity pointer to an integer which will be set to the - * capacity of the returned buffer - * @return a buffer with *result_capacity>=min_capacity - * @stable ICU 4.2 - */ - virtual char* GetAppendBuffer(int32_t min_capacity, - int32_t desired_capacity_hint, - char* scratch, int32_t scratch_capacity, - int32_t* result_capacity); - - /** - * Flush internal buffers. - * Some byte sinks use internal buffers or provide buffering - * and require calling Flush() at the end of the stream. - * The ByteSink should be ready for further Append() calls after Flush(). - * The default implementation of Flush() does nothing. - * @stable ICU 4.2 - */ - virtual void Flush(); - -private: - ByteSink(const ByteSink &) = delete; - ByteSink &operator=(const ByteSink &) = delete; -}; - -// ------------------------------------------------------------- -// Some standard implementations - -/** - * Implementation of ByteSink that writes to a flat byte array, - * with bounds-checking: - * This sink will not write more than capacity bytes to outbuf. - * If more than capacity bytes are Append()ed, then excess bytes are ignored, - * and Overflowed() will return true. - * Overflow does not cause a runtime error. - * @stable ICU 4.2 - */ -class U_COMMON_API CheckedArrayByteSink : public ByteSink { -public: - /** - * Constructs a ByteSink that will write to outbuf[0..capacity-1]. - * @param outbuf buffer to write to - * @param capacity size of the buffer - * @stable ICU 4.2 - */ - CheckedArrayByteSink(char* outbuf, int32_t capacity); - /** - * Destructor. - * @stable ICU 4.2 - */ - virtual ~CheckedArrayByteSink(); - /** - * Returns the sink to its original state, without modifying the buffer. - * Useful for reusing both the buffer and the sink for multiple streams. - * Resets the state to NumberOfBytesWritten()=NumberOfBytesAppended()=0 - * and Overflowed()=false. - * @return *this - * @stable ICU 4.6 - */ - virtual CheckedArrayByteSink& Reset(); - /** - * Append "bytes[0,n-1]" to this. - * @param bytes the pointer to the bytes - * @param n the number of bytes; must be non-negative - * @stable ICU 4.2 - */ - virtual void Append(const char* bytes, int32_t n) override; - /** - * Returns a writable buffer for appending and writes the buffer's capacity to - * *result_capacity. For details see the base class documentation. - * @param min_capacity required minimum capacity of the returned buffer; - * must be non-negative - * @param desired_capacity_hint desired capacity of the returned buffer; - * must be non-negative - * @param scratch default caller-owned buffer - * @param scratch_capacity capacity of the scratch buffer - * @param result_capacity pointer to an integer which will be set to the - * capacity of the returned buffer - * @return a buffer with *result_capacity>=min_capacity - * @stable ICU 4.2 - */ - virtual char* GetAppendBuffer(int32_t min_capacity, - int32_t desired_capacity_hint, - char* scratch, int32_t scratch_capacity, - int32_t* result_capacity) override; - /** - * Returns the number of bytes actually written to the sink. - * @return number of bytes written to the buffer - * @stable ICU 4.2 - */ - int32_t NumberOfBytesWritten() const { return size_; } - /** - * Returns true if any bytes were discarded, i.e., if there was an - * attempt to write more than 'capacity' bytes. - * @return true if more than 'capacity' bytes were Append()ed - * @stable ICU 4.2 - */ - UBool Overflowed() const { return overflowed_; } - /** - * Returns the number of bytes appended to the sink. - * If Overflowed() then NumberOfBytesAppended()>NumberOfBytesWritten() - * else they return the same number. - * @return number of bytes written to the buffer - * @stable ICU 4.6 - */ - int32_t NumberOfBytesAppended() const { return appended_; } -private: - char* outbuf_; - const int32_t capacity_; - int32_t size_; - int32_t appended_; - UBool overflowed_; - - CheckedArrayByteSink() = delete; - CheckedArrayByteSink(const CheckedArrayByteSink &) = delete; - CheckedArrayByteSink &operator=(const CheckedArrayByteSink &) = delete; -}; - -/** - * Implementation of ByteSink that writes to a "string". - * The StringClass is usually instantiated with a std::string. - * @stable ICU 4.2 - */ -template -class StringByteSink : public ByteSink { - public: - /** - * Constructs a ByteSink that will append bytes to the dest string. - * @param dest pointer to string object to append to - * @stable ICU 4.2 - */ - StringByteSink(StringClass* dest) : dest_(dest) { } - /** - * Constructs a ByteSink that reserves append capacity and will append bytes to the dest string. - * - * @param dest pointer to string object to append to - * @param initialAppendCapacity capacity beyond dest->length() to be reserve()d - * @stable ICU 60 - */ - StringByteSink(StringClass* dest, int32_t initialAppendCapacity) : dest_(dest) { - if (initialAppendCapacity > 0 && - (uint32_t)initialAppendCapacity > (dest->capacity() - dest->length())) { - dest->reserve(dest->length() + initialAppendCapacity); - } - } - /** - * Append "bytes[0,n-1]" to this. - * @param data the pointer to the bytes - * @param n the number of bytes; must be non-negative - * @stable ICU 4.2 - */ - virtual void Append(const char* data, int32_t n) override { dest_->append(data, n); } - private: - StringClass* dest_; - - StringByteSink() = delete; - StringByteSink(const StringByteSink &) = delete; - StringByteSink &operator=(const StringByteSink &) = delete; -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __BYTESTREAM_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// Copyright (C) 2009-2012, International Business Machines +// Corporation and others. All Rights Reserved. +// +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: sanjay@google.com (Sanjay Ghemawat) +// +// Abstract interface that consumes a sequence of bytes (ByteSink). +// +// Used so that we can write a single piece of code that can operate +// on a variety of output string types. +// +// Various implementations of this interface are provided: +// ByteSink: +// CheckedArrayByteSink Write to a flat array, with bounds checking +// StringByteSink Write to an STL string + +// This code is a contribution of Google code, and the style used here is +// a compromise between the original Google code and the ICU coding guidelines. +// For example, data types are ICU-ified (size_t,int->int32_t), +// and API comments doxygen-ified, but function names and behavior are +// as in the original, if possible. +// Assertion-style error handling, not available in ICU, was changed to +// parameter "pinning" similar to UnicodeString. +// +// In addition, this is only a partial port of the original Google code, +// limited to what was needed so far. The (nearly) complete original code +// is in the ICU svn repository at icuhtml/trunk/design/strings/contrib +// (see ICU ticket 6765, r25517). + +#ifndef __BYTESTREAM_H__ +#define __BYTESTREAM_H__ + +/** + * \file + * \brief C++ API: Interface for writing bytes, and implementation classes. + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/uobject.h" +#include "unicode/std_string.h" + +U_NAMESPACE_BEGIN + +/** + * A ByteSink can be filled with bytes. + * @stable ICU 4.2 + */ +class U_COMMON_API ByteSink : public UMemory { +public: + /** + * Default constructor. + * @stable ICU 4.2 + */ + ByteSink() { } + /** + * Virtual destructor. + * @stable ICU 4.2 + */ + virtual ~ByteSink(); + + /** + * Append "bytes[0,n-1]" to this. + * @param bytes the pointer to the bytes + * @param n the number of bytes; must be non-negative + * @stable ICU 4.2 + */ + virtual void Append(const char* bytes, int32_t n) = 0; + + /** + * Appends n bytes to this. Same as Append(). + * Call AppendU8() with u8"string literals" which are const char * in C++11 + * but const char8_t * in C++20. + * If the compiler does support char8_t as a distinct type, + * then an AppendU8() overload for that is defined and will be chosen. + * + * @param bytes the pointer to the bytes + * @param n the number of bytes; must be non-negative + * @stable ICU 67 + */ + inline void AppendU8(const char* bytes, int32_t n) { + Append(bytes, n); + } + +#if defined(__cpp_char8_t) || defined(U_IN_DOXYGEN) + /** + * Appends n bytes to this. Same as Append() but for a const char8_t * pointer. + * Call AppendU8() with u8"string literals" which are const char * in C++11 + * but const char8_t * in C++20. + * If the compiler does support char8_t as a distinct type, + * then this AppendU8() overload for that is defined and will be chosen. + * + * @param bytes the pointer to the bytes + * @param n the number of bytes; must be non-negative + * @stable ICU 67 + */ + inline void AppendU8(const char8_t* bytes, int32_t n) { + Append(reinterpret_cast(bytes), n); + } +#endif + + /** + * Returns a writable buffer for appending and writes the buffer's capacity to + * *result_capacity. Guarantees *result_capacity>=min_capacity. + * May return a pointer to the caller-owned scratch buffer which must have + * scratch_capacity>=min_capacity. + * The returned buffer is only valid until the next operation + * on this ByteSink. + * + * After writing at most *result_capacity bytes, call Append() with the + * pointer returned from this function and the number of bytes written. + * Many Append() implementations will avoid copying bytes if this function + * returned an internal buffer. + * + * Partial usage example: + * int32_t capacity; + * char* buffer = sink->GetAppendBuffer(..., &capacity); + * ... Write n bytes into buffer, with n <= capacity. + * sink->Append(buffer, n); + * In many implementations, that call to Append will avoid copying bytes. + * + * If the ByteSink allocates or reallocates an internal buffer, it should use + * the desired_capacity_hint if appropriate. + * If a caller cannot provide a reasonable guess at the desired capacity, + * it should pass desired_capacity_hint=0. + * + * If a non-scratch buffer is returned, the caller may only pass + * a prefix to it to Append(). + * That is, it is not correct to pass an interior pointer to Append(). + * + * The default implementation always returns the scratch buffer. + * + * @param min_capacity required minimum capacity of the returned buffer; + * must be non-negative + * @param desired_capacity_hint desired capacity of the returned buffer; + * must be non-negative + * @param scratch default caller-owned buffer + * @param scratch_capacity capacity of the scratch buffer + * @param result_capacity pointer to an integer which will be set to the + * capacity of the returned buffer + * @return a buffer with *result_capacity>=min_capacity + * @stable ICU 4.2 + */ + virtual char* GetAppendBuffer(int32_t min_capacity, + int32_t desired_capacity_hint, + char* scratch, int32_t scratch_capacity, + int32_t* result_capacity); + + /** + * Flush internal buffers. + * Some byte sinks use internal buffers or provide buffering + * and require calling Flush() at the end of the stream. + * The ByteSink should be ready for further Append() calls after Flush(). + * The default implementation of Flush() does nothing. + * @stable ICU 4.2 + */ + virtual void Flush(); + +private: + ByteSink(const ByteSink &) = delete; + ByteSink &operator=(const ByteSink &) = delete; +}; + +// ------------------------------------------------------------- +// Some standard implementations + +/** + * Implementation of ByteSink that writes to a flat byte array, + * with bounds-checking: + * This sink will not write more than capacity bytes to outbuf. + * If more than capacity bytes are Append()ed, then excess bytes are ignored, + * and Overflowed() will return true. + * Overflow does not cause a runtime error. + * @stable ICU 4.2 + */ +class U_COMMON_API CheckedArrayByteSink : public ByteSink { +public: + /** + * Constructs a ByteSink that will write to outbuf[0..capacity-1]. + * @param outbuf buffer to write to + * @param capacity size of the buffer + * @stable ICU 4.2 + */ + CheckedArrayByteSink(char* outbuf, int32_t capacity); + /** + * Destructor. + * @stable ICU 4.2 + */ + virtual ~CheckedArrayByteSink(); + /** + * Returns the sink to its original state, without modifying the buffer. + * Useful for reusing both the buffer and the sink for multiple streams. + * Resets the state to NumberOfBytesWritten()=NumberOfBytesAppended()=0 + * and Overflowed()=false. + * @return *this + * @stable ICU 4.6 + */ + virtual CheckedArrayByteSink& Reset(); + /** + * Append "bytes[0,n-1]" to this. + * @param bytes the pointer to the bytes + * @param n the number of bytes; must be non-negative + * @stable ICU 4.2 + */ + virtual void Append(const char* bytes, int32_t n) override; + /** + * Returns a writable buffer for appending and writes the buffer's capacity to + * *result_capacity. For details see the base class documentation. + * @param min_capacity required minimum capacity of the returned buffer; + * must be non-negative + * @param desired_capacity_hint desired capacity of the returned buffer; + * must be non-negative + * @param scratch default caller-owned buffer + * @param scratch_capacity capacity of the scratch buffer + * @param result_capacity pointer to an integer which will be set to the + * capacity of the returned buffer + * @return a buffer with *result_capacity>=min_capacity + * @stable ICU 4.2 + */ + virtual char* GetAppendBuffer(int32_t min_capacity, + int32_t desired_capacity_hint, + char* scratch, int32_t scratch_capacity, + int32_t* result_capacity) override; + /** + * Returns the number of bytes actually written to the sink. + * @return number of bytes written to the buffer + * @stable ICU 4.2 + */ + int32_t NumberOfBytesWritten() const { return size_; } + /** + * Returns true if any bytes were discarded, i.e., if there was an + * attempt to write more than 'capacity' bytes. + * @return true if more than 'capacity' bytes were Append()ed + * @stable ICU 4.2 + */ + UBool Overflowed() const { return overflowed_; } + /** + * Returns the number of bytes appended to the sink. + * If Overflowed() then NumberOfBytesAppended()>NumberOfBytesWritten() + * else they return the same number. + * @return number of bytes written to the buffer + * @stable ICU 4.6 + */ + int32_t NumberOfBytesAppended() const { return appended_; } +private: + char* outbuf_; + const int32_t capacity_; + int32_t size_; + int32_t appended_; + UBool overflowed_; + + CheckedArrayByteSink() = delete; + CheckedArrayByteSink(const CheckedArrayByteSink &) = delete; + CheckedArrayByteSink &operator=(const CheckedArrayByteSink &) = delete; +}; + +/** + * Implementation of ByteSink that writes to a "string". + * The StringClass is usually instantiated with a std::string. + * @stable ICU 4.2 + */ +template +class StringByteSink : public ByteSink { + public: + /** + * Constructs a ByteSink that will append bytes to the dest string. + * @param dest pointer to string object to append to + * @stable ICU 4.2 + */ + StringByteSink(StringClass* dest) : dest_(dest) { } + /** + * Constructs a ByteSink that reserves append capacity and will append bytes to the dest string. + * + * @param dest pointer to string object to append to + * @param initialAppendCapacity capacity beyond dest->length() to be reserve()d + * @stable ICU 60 + */ + StringByteSink(StringClass* dest, int32_t initialAppendCapacity) : dest_(dest) { + if (initialAppendCapacity > 0 && + (uint32_t)initialAppendCapacity > (dest->capacity() - dest->length())) { + dest->reserve(dest->length() + initialAppendCapacity); + } + } + /** + * Append "bytes[0,n-1]" to this. + * @param data the pointer to the bytes + * @param n the number of bytes; must be non-negative + * @stable ICU 4.2 + */ + virtual void Append(const char* data, int32_t n) override { dest_->append(data, n); } + private: + StringClass* dest_; + + StringByteSink() = delete; + StringByteSink(const StringByteSink &) = delete; + StringByteSink &operator=(const StringByteSink &) = delete; +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __BYTESTREAM_H__ diff --git a/deps/icu-small/source/common/unicode/bytestrie.h b/deps/icu-small/source/common/unicode/bytestrie.h index 8fe66780f5157b..a1de27a81b78e0 100644 --- a/deps/icu-small/source/common/unicode/bytestrie.h +++ b/deps/icu-small/source/common/unicode/bytestrie.h @@ -1,568 +1,568 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2012, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: bytestrie.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010sep25 -* created by: Markus W. Scherer -*/ - -#ifndef __BYTESTRIE_H__ -#define __BYTESTRIE_H__ - -/** - * \file - * \brief C++ API: Trie for mapping byte sequences to integer values. - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/stringpiece.h" -#include "unicode/uobject.h" -#include "unicode/ustringtrie.h" - -class BytesTrieTest; - -U_NAMESPACE_BEGIN - -class ByteSink; -class BytesTrieBuilder; -class CharString; -class UVector32; - -/** - * Light-weight, non-const reader class for a BytesTrie. - * Traverses a byte-serialized data structure with minimal state, - * for mapping byte sequences to non-negative integer values. - * - * This class owns the serialized trie data only if it was constructed by - * the builder's build() method. - * The public constructor and the copy constructor only alias the data (only copy the pointer). - * There is no assignment operator. - * - * This class is not intended for public subclassing. - * @stable ICU 4.8 - */ -class U_COMMON_API BytesTrie : public UMemory { -public: - /** - * Constructs a BytesTrie reader instance. - * - * The trieBytes must contain a copy of a byte sequence from the BytesTrieBuilder, - * starting with the first byte of that sequence. - * The BytesTrie object will not read more bytes than - * the BytesTrieBuilder generated in the corresponding build() call. - * - * The array is not copied/cloned and must not be modified while - * the BytesTrie object is in use. - * - * @param trieBytes The byte array that contains the serialized trie. - * @stable ICU 4.8 - */ - BytesTrie(const void *trieBytes) - : ownedArray_(NULL), bytes_(static_cast(trieBytes)), - pos_(bytes_), remainingMatchLength_(-1) {} - - /** - * Destructor. - * @stable ICU 4.8 - */ - ~BytesTrie(); - - /** - * Copy constructor, copies the other trie reader object and its state, - * but not the byte array which will be shared. (Shallow copy.) - * @param other Another BytesTrie object. - * @stable ICU 4.8 - */ - BytesTrie(const BytesTrie &other) - : ownedArray_(NULL), bytes_(other.bytes_), - pos_(other.pos_), remainingMatchLength_(other.remainingMatchLength_) {} - - /** - * Resets this trie to its initial state. - * @return *this - * @stable ICU 4.8 - */ - BytesTrie &reset() { - pos_=bytes_; - remainingMatchLength_=-1; - return *this; - } - - /** - * Returns the state of this trie as a 64-bit integer. - * The state value is never 0. - * - * @return opaque state value - * @see resetToState64 - * @stable ICU 65 - */ - uint64_t getState64() const { - return (static_cast(remainingMatchLength_ + 2) << kState64RemainingShift) | - (uint64_t)(pos_ - bytes_); - } - - /** - * Resets this trie to the saved state. - * Unlike resetToState(State), the 64-bit state value - * must be from getState64() from the same trie object or - * from one initialized the exact same way. - * Because of no validation, this method is faster. - * - * @param state The opaque trie state value from getState64(). - * @return *this - * @see getState64 - * @see resetToState - * @see reset - * @stable ICU 65 - */ - BytesTrie &resetToState64(uint64_t state) { - remainingMatchLength_ = static_cast(state >> kState64RemainingShift) - 2; - pos_ = bytes_ + (state & kState64PosMask); - return *this; - } - - /** - * BytesTrie state object, for saving a trie's current state - * and resetting the trie back to this state later. - * @stable ICU 4.8 - */ - class State : public UMemory { - public: - /** - * Constructs an empty State. - * @stable ICU 4.8 - */ - State() { bytes=NULL; } - private: - friend class BytesTrie; - - const uint8_t *bytes; - const uint8_t *pos; - int32_t remainingMatchLength; - }; - - /** - * Saves the state of this trie. - * @param state The State object to hold the trie's state. - * @return *this - * @see resetToState - * @stable ICU 4.8 - */ - const BytesTrie &saveState(State &state) const { - state.bytes=bytes_; - state.pos=pos_; - state.remainingMatchLength=remainingMatchLength_; - return *this; - } - - /** - * Resets this trie to the saved state. - * If the state object contains no state, or the state of a different trie, - * then this trie remains unchanged. - * @param state The State object which holds a saved trie state. - * @return *this - * @see saveState - * @see reset - * @stable ICU 4.8 - */ - BytesTrie &resetToState(const State &state) { - if(bytes_==state.bytes && bytes_!=NULL) { - pos_=state.pos; - remainingMatchLength_=state.remainingMatchLength; - } - return *this; - } - - /** - * Determines whether the byte sequence so far matches, whether it has a value, - * and whether another input byte can continue a matching byte sequence. - * @return The match/value Result. - * @stable ICU 4.8 - */ - UStringTrieResult current() const; - - /** - * Traverses the trie from the initial state for this input byte. - * Equivalent to reset().next(inByte). - * @param inByte Input byte value. Values -0x100..-1 are treated like 0..0xff. - * Values below -0x100 and above 0xff will never match. - * @return The match/value Result. - * @stable ICU 4.8 - */ - inline UStringTrieResult first(int32_t inByte) { - remainingMatchLength_=-1; - if(inByte<0) { - inByte+=0x100; - } - return nextImpl(bytes_, inByte); - } - - /** - * Traverses the trie from the current state for this input byte. - * @param inByte Input byte value. Values -0x100..-1 are treated like 0..0xff. - * Values below -0x100 and above 0xff will never match. - * @return The match/value Result. - * @stable ICU 4.8 - */ - UStringTrieResult next(int32_t inByte); - - /** - * Traverses the trie from the current state for this byte sequence. - * Equivalent to - * \code - * Result result=current(); - * for(each c in s) - * if(!USTRINGTRIE_HAS_NEXT(result)) return USTRINGTRIE_NO_MATCH; - * result=next(c); - * return result; - * \endcode - * @param s A string or byte sequence. Can be NULL if length is 0. - * @param length The length of the byte sequence. Can be -1 if NUL-terminated. - * @return The match/value Result. - * @stable ICU 4.8 - */ - UStringTrieResult next(const char *s, int32_t length); - - /** - * Returns a matching byte sequence's value if called immediately after - * current()/first()/next() returned USTRINGTRIE_INTERMEDIATE_VALUE or USTRINGTRIE_FINAL_VALUE. - * getValue() can be called multiple times. - * - * Do not call getValue() after USTRINGTRIE_NO_MATCH or USTRINGTRIE_NO_VALUE! - * @return The value for the byte sequence so far. - * @stable ICU 4.8 - */ - inline int32_t getValue() const { - const uint8_t *pos=pos_; - int32_t leadByte=*pos++; - // U_ASSERT(leadByte>=kMinValueLead); - return readValue(pos, leadByte>>1); - } - - /** - * Determines whether all byte sequences reachable from the current state - * map to the same value. - * @param uniqueValue Receives the unique value, if this function returns true. - * (output-only) - * @return true if all byte sequences reachable from the current state - * map to the same value. - * @stable ICU 4.8 - */ - inline UBool hasUniqueValue(int32_t &uniqueValue) const { - const uint8_t *pos=pos_; - // Skip the rest of a pending linear-match node. - return pos!=NULL && findUniqueValue(pos+remainingMatchLength_+1, false, uniqueValue); - } - - /** - * Finds each byte which continues the byte sequence from the current state. - * That is, each byte b for which it would be next(b)!=USTRINGTRIE_NO_MATCH now. - * @param out Each next byte is appended to this object. - * (Only uses the out.Append(s, length) method.) - * @return the number of bytes which continue the byte sequence from here - * @stable ICU 4.8 - */ - int32_t getNextBytes(ByteSink &out) const; - - /** - * Iterator for all of the (byte sequence, value) pairs in a BytesTrie. - * @stable ICU 4.8 - */ - class U_COMMON_API Iterator : public UMemory { - public: - /** - * Iterates from the root of a byte-serialized BytesTrie. - * @param trieBytes The trie bytes. - * @param maxStringLength If 0, the iterator returns full strings/byte sequences. - * Otherwise, the iterator returns strings with this maximum length. - * @param errorCode Standard 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.) - * @stable ICU 4.8 - */ - Iterator(const void *trieBytes, int32_t maxStringLength, UErrorCode &errorCode); - - /** - * Iterates from the current state of the specified BytesTrie. - * @param trie The trie whose state will be copied for iteration. - * @param maxStringLength If 0, the iterator returns full strings/byte sequences. - * Otherwise, the iterator returns strings with this maximum length. - * @param errorCode Standard 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.) - * @stable ICU 4.8 - */ - Iterator(const BytesTrie &trie, int32_t maxStringLength, UErrorCode &errorCode); - - /** - * Destructor. - * @stable ICU 4.8 - */ - ~Iterator(); - - /** - * Resets this iterator to its initial state. - * @return *this - * @stable ICU 4.8 - */ - Iterator &reset(); - - /** - * @return true if there are more elements. - * @stable ICU 4.8 - */ - UBool hasNext() const; - - /** - * Finds the next (byte sequence, value) pair if there is one. - * - * If the byte sequence is truncated to the maximum length and does not - * have a real value, then the value is set to -1. - * In this case, this "not a real value" is indistinguishable from - * a real value of -1. - * @param errorCode Standard 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.) - * @return true if there is another element. - * @stable ICU 4.8 - */ - UBool next(UErrorCode &errorCode); - - /** - * @return The NUL-terminated byte sequence for the last successful next(). - * @stable ICU 4.8 - */ - StringPiece getString() const; - /** - * @return The value for the last successful next(). - * @stable ICU 4.8 - */ - int32_t getValue() const { return value_; } - - private: - UBool truncateAndStop(); - - const uint8_t *branchNext(const uint8_t *pos, int32_t length, UErrorCode &errorCode); - - const uint8_t *bytes_; - const uint8_t *pos_; - const uint8_t *initialPos_; - int32_t remainingMatchLength_; - int32_t initialRemainingMatchLength_; - - CharString *str_; - int32_t maxLength_; - int32_t value_; - - // The stack stores pairs of integers for backtracking to another - // outbound edge of a branch node. - // The first integer is an offset from bytes_. - // The second integer has the str_->length() from before the node in bits 15..0, - // and the remaining branch length in bits 24..16. (Bits 31..25 are unused.) - // (We could store the remaining branch length minus 1 in bits 23..16 and not use bits 31..24, - // but the code looks more confusing that way.) - UVector32 *stack_; - }; - -private: - friend class BytesTrieBuilder; - friend class ::BytesTrieTest; - - /** - * Constructs a BytesTrie reader instance. - * Unlike the public constructor which just aliases an array, - * this constructor adopts the builder's array. - * This constructor is only called by the builder. - */ - BytesTrie(void *adoptBytes, const void *trieBytes) - : ownedArray_(static_cast(adoptBytes)), - bytes_(static_cast(trieBytes)), - pos_(bytes_), remainingMatchLength_(-1) {} - - // No assignment operator. - BytesTrie &operator=(const BytesTrie &other) = delete; - - inline void stop() { - pos_=NULL; - } - - // Reads a compact 32-bit integer. - // pos is already after the leadByte, and the lead byte is already shifted right by 1. - static int32_t readValue(const uint8_t *pos, int32_t leadByte); - static inline const uint8_t *skipValue(const uint8_t *pos, int32_t leadByte) { - // U_ASSERT(leadByte>=kMinValueLead); - if(leadByte>=(kMinTwoByteValueLead<<1)) { - if(leadByte<(kMinThreeByteValueLead<<1)) { - ++pos; - } else if(leadByte<(kFourByteValueLead<<1)) { - pos+=2; - } else { - pos+=3+((leadByte>>1)&1); - } - } - return pos; - } - static inline const uint8_t *skipValue(const uint8_t *pos) { - int32_t leadByte=*pos++; - return skipValue(pos, leadByte); - } - - // Reads a jump delta and jumps. - static const uint8_t *jumpByDelta(const uint8_t *pos); - - static inline const uint8_t *skipDelta(const uint8_t *pos) { - int32_t delta=*pos++; - if(delta>=kMinTwoByteDeltaLead) { - if(delta>8)+1; // 0x6c - static const int32_t kFourByteValueLead=0x7e; - - // A little more than Unicode code points. (0x11ffff) - static const int32_t kMaxThreeByteValue=((kFourByteValueLead-kMinThreeByteValueLead)<<16)-1; - - static const int32_t kFiveByteValueLead=0x7f; - - // Compact delta integers. - static const int32_t kMaxOneByteDelta=0xbf; - static const int32_t kMinTwoByteDeltaLead=kMaxOneByteDelta+1; // 0xc0 - static const int32_t kMinThreeByteDeltaLead=0xf0; - static const int32_t kFourByteDeltaLead=0xfe; - static const int32_t kFiveByteDeltaLead=0xff; - - static const int32_t kMaxTwoByteDelta=((kMinThreeByteDeltaLead-kMinTwoByteDeltaLead)<<8)-1; // 0x2fff - static const int32_t kMaxThreeByteDelta=((kFourByteDeltaLead-kMinThreeByteDeltaLead)<<16)-1; // 0xdffff - - // For getState64(): - // The remainingMatchLength_ is -1..14=(kMaxLinearMatchLength=0x10)-2 - // so we need at least 5 bits for that. - // We add 2 to store it as a positive value 1..16=kMaxLinearMatchLength. - static constexpr int32_t kState64RemainingShift = 59; - static constexpr uint64_t kState64PosMask = (UINT64_C(1) << kState64RemainingShift) - 1; - - uint8_t *ownedArray_; - - // Fixed value referencing the BytesTrie bytes. - const uint8_t *bytes_; - - // Iterator variables. - - // Pointer to next trie byte to read. NULL if no more matches. - const uint8_t *pos_; - // Remaining length of a linear-match node, minus 1. Negative if not in such a node. - int32_t remainingMatchLength_; -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __BYTESTRIE_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2012, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: bytestrie.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010sep25 +* created by: Markus W. Scherer +*/ + +#ifndef __BYTESTRIE_H__ +#define __BYTESTRIE_H__ + +/** + * \file + * \brief C++ API: Trie for mapping byte sequences to integer values. + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/stringpiece.h" +#include "unicode/uobject.h" +#include "unicode/ustringtrie.h" + +class BytesTrieTest; + +U_NAMESPACE_BEGIN + +class ByteSink; +class BytesTrieBuilder; +class CharString; +class UVector32; + +/** + * Light-weight, non-const reader class for a BytesTrie. + * Traverses a byte-serialized data structure with minimal state, + * for mapping byte sequences to non-negative integer values. + * + * This class owns the serialized trie data only if it was constructed by + * the builder's build() method. + * The public constructor and the copy constructor only alias the data (only copy the pointer). + * There is no assignment operator. + * + * This class is not intended for public subclassing. + * @stable ICU 4.8 + */ +class U_COMMON_API BytesTrie : public UMemory { +public: + /** + * Constructs a BytesTrie reader instance. + * + * The trieBytes must contain a copy of a byte sequence from the BytesTrieBuilder, + * starting with the first byte of that sequence. + * The BytesTrie object will not read more bytes than + * the BytesTrieBuilder generated in the corresponding build() call. + * + * The array is not copied/cloned and must not be modified while + * the BytesTrie object is in use. + * + * @param trieBytes The byte array that contains the serialized trie. + * @stable ICU 4.8 + */ + BytesTrie(const void *trieBytes) + : ownedArray_(nullptr), bytes_(static_cast(trieBytes)), + pos_(bytes_), remainingMatchLength_(-1) {} + + /** + * Destructor. + * @stable ICU 4.8 + */ + ~BytesTrie(); + + /** + * Copy constructor, copies the other trie reader object and its state, + * but not the byte array which will be shared. (Shallow copy.) + * @param other Another BytesTrie object. + * @stable ICU 4.8 + */ + BytesTrie(const BytesTrie &other) + : ownedArray_(nullptr), bytes_(other.bytes_), + pos_(other.pos_), remainingMatchLength_(other.remainingMatchLength_) {} + + /** + * Resets this trie to its initial state. + * @return *this + * @stable ICU 4.8 + */ + BytesTrie &reset() { + pos_=bytes_; + remainingMatchLength_=-1; + return *this; + } + + /** + * Returns the state of this trie as a 64-bit integer. + * The state value is never 0. + * + * @return opaque state value + * @see resetToState64 + * @stable ICU 65 + */ + uint64_t getState64() const { + return (static_cast(remainingMatchLength_ + 2) << kState64RemainingShift) | + (uint64_t)(pos_ - bytes_); + } + + /** + * Resets this trie to the saved state. + * Unlike resetToState(State), the 64-bit state value + * must be from getState64() from the same trie object or + * from one initialized the exact same way. + * Because of no validation, this method is faster. + * + * @param state The opaque trie state value from getState64(). + * @return *this + * @see getState64 + * @see resetToState + * @see reset + * @stable ICU 65 + */ + BytesTrie &resetToState64(uint64_t state) { + remainingMatchLength_ = static_cast(state >> kState64RemainingShift) - 2; + pos_ = bytes_ + (state & kState64PosMask); + return *this; + } + + /** + * BytesTrie state object, for saving a trie's current state + * and resetting the trie back to this state later. + * @stable ICU 4.8 + */ + class State : public UMemory { + public: + /** + * Constructs an empty State. + * @stable ICU 4.8 + */ + State() { bytes=nullptr; } + private: + friend class BytesTrie; + + const uint8_t *bytes; + const uint8_t *pos; + int32_t remainingMatchLength; + }; + + /** + * Saves the state of this trie. + * @param state The State object to hold the trie's state. + * @return *this + * @see resetToState + * @stable ICU 4.8 + */ + const BytesTrie &saveState(State &state) const { + state.bytes=bytes_; + state.pos=pos_; + state.remainingMatchLength=remainingMatchLength_; + return *this; + } + + /** + * Resets this trie to the saved state. + * If the state object contains no state, or the state of a different trie, + * then this trie remains unchanged. + * @param state The State object which holds a saved trie state. + * @return *this + * @see saveState + * @see reset + * @stable ICU 4.8 + */ + BytesTrie &resetToState(const State &state) { + if(bytes_==state.bytes && bytes_!=nullptr) { + pos_=state.pos; + remainingMatchLength_=state.remainingMatchLength; + } + return *this; + } + + /** + * Determines whether the byte sequence so far matches, whether it has a value, + * and whether another input byte can continue a matching byte sequence. + * @return The match/value Result. + * @stable ICU 4.8 + */ + UStringTrieResult current() const; + + /** + * Traverses the trie from the initial state for this input byte. + * Equivalent to reset().next(inByte). + * @param inByte Input byte value. Values -0x100..-1 are treated like 0..0xff. + * Values below -0x100 and above 0xff will never match. + * @return The match/value Result. + * @stable ICU 4.8 + */ + inline UStringTrieResult first(int32_t inByte) { + remainingMatchLength_=-1; + if(inByte<0) { + inByte+=0x100; + } + return nextImpl(bytes_, inByte); + } + + /** + * Traverses the trie from the current state for this input byte. + * @param inByte Input byte value. Values -0x100..-1 are treated like 0..0xff. + * Values below -0x100 and above 0xff will never match. + * @return The match/value Result. + * @stable ICU 4.8 + */ + UStringTrieResult next(int32_t inByte); + + /** + * Traverses the trie from the current state for this byte sequence. + * Equivalent to + * \code + * Result result=current(); + * for(each c in s) + * if(!USTRINGTRIE_HAS_NEXT(result)) return USTRINGTRIE_NO_MATCH; + * result=next(c); + * return result; + * \endcode + * @param s A string or byte sequence. Can be nullptr if length is 0. + * @param length The length of the byte sequence. Can be -1 if NUL-terminated. + * @return The match/value Result. + * @stable ICU 4.8 + */ + UStringTrieResult next(const char *s, int32_t length); + + /** + * Returns a matching byte sequence's value if called immediately after + * current()/first()/next() returned USTRINGTRIE_INTERMEDIATE_VALUE or USTRINGTRIE_FINAL_VALUE. + * getValue() can be called multiple times. + * + * Do not call getValue() after USTRINGTRIE_NO_MATCH or USTRINGTRIE_NO_VALUE! + * @return The value for the byte sequence so far. + * @stable ICU 4.8 + */ + inline int32_t getValue() const { + const uint8_t *pos=pos_; + int32_t leadByte=*pos++; + // U_ASSERT(leadByte>=kMinValueLead); + return readValue(pos, leadByte>>1); + } + + /** + * Determines whether all byte sequences reachable from the current state + * map to the same value. + * @param uniqueValue Receives the unique value, if this function returns true. + * (output-only) + * @return true if all byte sequences reachable from the current state + * map to the same value. + * @stable ICU 4.8 + */ + inline UBool hasUniqueValue(int32_t &uniqueValue) const { + const uint8_t *pos=pos_; + // Skip the rest of a pending linear-match node. + return pos!=nullptr && findUniqueValue(pos+remainingMatchLength_+1, false, uniqueValue); + } + + /** + * Finds each byte which continues the byte sequence from the current state. + * That is, each byte b for which it would be next(b)!=USTRINGTRIE_NO_MATCH now. + * @param out Each next byte is appended to this object. + * (Only uses the out.Append(s, length) method.) + * @return the number of bytes which continue the byte sequence from here + * @stable ICU 4.8 + */ + int32_t getNextBytes(ByteSink &out) const; + + /** + * Iterator for all of the (byte sequence, value) pairs in a BytesTrie. + * @stable ICU 4.8 + */ + class U_COMMON_API Iterator : public UMemory { + public: + /** + * Iterates from the root of a byte-serialized BytesTrie. + * @param trieBytes The trie bytes. + * @param maxStringLength If 0, the iterator returns full strings/byte sequences. + * Otherwise, the iterator returns strings with this maximum length. + * @param errorCode Standard 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.) + * @stable ICU 4.8 + */ + Iterator(const void *trieBytes, int32_t maxStringLength, UErrorCode &errorCode); + + /** + * Iterates from the current state of the specified BytesTrie. + * @param trie The trie whose state will be copied for iteration. + * @param maxStringLength If 0, the iterator returns full strings/byte sequences. + * Otherwise, the iterator returns strings with this maximum length. + * @param errorCode Standard 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.) + * @stable ICU 4.8 + */ + Iterator(const BytesTrie &trie, int32_t maxStringLength, UErrorCode &errorCode); + + /** + * Destructor. + * @stable ICU 4.8 + */ + ~Iterator(); + + /** + * Resets this iterator to its initial state. + * @return *this + * @stable ICU 4.8 + */ + Iterator &reset(); + + /** + * @return true if there are more elements. + * @stable ICU 4.8 + */ + UBool hasNext() const; + + /** + * Finds the next (byte sequence, value) pair if there is one. + * + * If the byte sequence is truncated to the maximum length and does not + * have a real value, then the value is set to -1. + * In this case, this "not a real value" is indistinguishable from + * a real value of -1. + * @param errorCode Standard 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.) + * @return true if there is another element. + * @stable ICU 4.8 + */ + UBool next(UErrorCode &errorCode); + + /** + * @return The NUL-terminated byte sequence for the last successful next(). + * @stable ICU 4.8 + */ + StringPiece getString() const; + /** + * @return The value for the last successful next(). + * @stable ICU 4.8 + */ + int32_t getValue() const { return value_; } + + private: + UBool truncateAndStop(); + + const uint8_t *branchNext(const uint8_t *pos, int32_t length, UErrorCode &errorCode); + + const uint8_t *bytes_; + const uint8_t *pos_; + const uint8_t *initialPos_; + int32_t remainingMatchLength_; + int32_t initialRemainingMatchLength_; + + CharString *str_; + int32_t maxLength_; + int32_t value_; + + // The stack stores pairs of integers for backtracking to another + // outbound edge of a branch node. + // The first integer is an offset from bytes_. + // The second integer has the str_->length() from before the node in bits 15..0, + // and the remaining branch length in bits 24..16. (Bits 31..25 are unused.) + // (We could store the remaining branch length minus 1 in bits 23..16 and not use bits 31..24, + // but the code looks more confusing that way.) + UVector32 *stack_; + }; + +private: + friend class BytesTrieBuilder; + friend class ::BytesTrieTest; + + /** + * Constructs a BytesTrie reader instance. + * Unlike the public constructor which just aliases an array, + * this constructor adopts the builder's array. + * This constructor is only called by the builder. + */ + BytesTrie(void *adoptBytes, const void *trieBytes) + : ownedArray_(static_cast(adoptBytes)), + bytes_(static_cast(trieBytes)), + pos_(bytes_), remainingMatchLength_(-1) {} + + // No assignment operator. + BytesTrie &operator=(const BytesTrie &other) = delete; + + inline void stop() { + pos_=nullptr; + } + + // Reads a compact 32-bit integer. + // pos is already after the leadByte, and the lead byte is already shifted right by 1. + static int32_t readValue(const uint8_t *pos, int32_t leadByte); + static inline const uint8_t *skipValue(const uint8_t *pos, int32_t leadByte) { + // U_ASSERT(leadByte>=kMinValueLead); + if(leadByte>=(kMinTwoByteValueLead<<1)) { + if(leadByte<(kMinThreeByteValueLead<<1)) { + ++pos; + } else if(leadByte<(kFourByteValueLead<<1)) { + pos+=2; + } else { + pos+=3+((leadByte>>1)&1); + } + } + return pos; + } + static inline const uint8_t *skipValue(const uint8_t *pos) { + int32_t leadByte=*pos++; + return skipValue(pos, leadByte); + } + + // Reads a jump delta and jumps. + static const uint8_t *jumpByDelta(const uint8_t *pos); + + static inline const uint8_t *skipDelta(const uint8_t *pos) { + int32_t delta=*pos++; + if(delta>=kMinTwoByteDeltaLead) { + if(delta>8)+1; // 0x6c + static const int32_t kFourByteValueLead=0x7e; + + // A little more than Unicode code points. (0x11ffff) + static const int32_t kMaxThreeByteValue=((kFourByteValueLead-kMinThreeByteValueLead)<<16)-1; + + static const int32_t kFiveByteValueLead=0x7f; + + // Compact delta integers. + static const int32_t kMaxOneByteDelta=0xbf; + static const int32_t kMinTwoByteDeltaLead=kMaxOneByteDelta+1; // 0xc0 + static const int32_t kMinThreeByteDeltaLead=0xf0; + static const int32_t kFourByteDeltaLead=0xfe; + static const int32_t kFiveByteDeltaLead=0xff; + + static const int32_t kMaxTwoByteDelta=((kMinThreeByteDeltaLead-kMinTwoByteDeltaLead)<<8)-1; // 0x2fff + static const int32_t kMaxThreeByteDelta=((kFourByteDeltaLead-kMinThreeByteDeltaLead)<<16)-1; // 0xdffff + + // For getState64(): + // The remainingMatchLength_ is -1..14=(kMaxLinearMatchLength=0x10)-2 + // so we need at least 5 bits for that. + // We add 2 to store it as a positive value 1..16=kMaxLinearMatchLength. + static constexpr int32_t kState64RemainingShift = 59; + static constexpr uint64_t kState64PosMask = (UINT64_C(1) << kState64RemainingShift) - 1; + + uint8_t *ownedArray_; + + // Fixed value referencing the BytesTrie bytes. + const uint8_t *bytes_; + + // Iterator variables. + + // Pointer to next trie byte to read. nullptr if no more matches. + const uint8_t *pos_; + // Remaining length of a linear-match node, minus 1. Negative if not in such a node. + int32_t remainingMatchLength_; +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __BYTESTRIE_H__ diff --git a/deps/icu-small/source/common/unicode/bytestriebuilder.h b/deps/icu-small/source/common/unicode/bytestriebuilder.h index ec9c625473d4f5..298caa4be82179 100644 --- a/deps/icu-small/source/common/unicode/bytestriebuilder.h +++ b/deps/icu-small/source/common/unicode/bytestriebuilder.h @@ -1,193 +1,193 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2016, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: bytestriebuilder.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010sep25 -* created by: Markus W. Scherer -*/ - -/** - * \file - * \brief C++ API: Builder for icu::BytesTrie - */ - -#ifndef __BYTESTRIEBUILDER_H__ -#define __BYTESTRIEBUILDER_H__ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/bytestrie.h" -#include "unicode/stringpiece.h" -#include "unicode/stringtriebuilder.h" - -class BytesTrieTest; - -U_NAMESPACE_BEGIN - -class BytesTrieElement; -class CharString; -/** - * Builder class for BytesTrie. - * - * This class is not intended for public subclassing. - * @stable ICU 4.8 - */ -class U_COMMON_API BytesTrieBuilder : public StringTrieBuilder { -public: - /** - * Constructs an empty builder. - * @param errorCode Standard ICU error code. - * @stable ICU 4.8 - */ - BytesTrieBuilder(UErrorCode &errorCode); - - /** - * Destructor. - * @stable ICU 4.8 - */ - virtual ~BytesTrieBuilder(); - - /** - * Adds a (byte sequence, value) pair. - * The byte sequence must be unique. - * The bytes will be copied; the builder does not keep - * a reference to the input StringPiece or its data(). - * @param s The input byte sequence. - * @param value The value associated with this byte sequence. - * @param errorCode Standard 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.) - * @return *this - * @stable ICU 4.8 - */ - BytesTrieBuilder &add(StringPiece s, int32_t value, UErrorCode &errorCode); - - /** - * Builds a BytesTrie for the add()ed data. - * Once built, no further data can be add()ed until clear() is called. - * - * A BytesTrie cannot be empty. At least one (byte sequence, value) pair - * must have been add()ed. - * - * This method passes ownership of the builder's internal result array to the new trie object. - * Another call to any build() variant will re-serialize the trie. - * After clear() has been called, a new array will be used as well. - * @param buildOption Build option, see UStringTrieBuildOption. - * @param errorCode Standard 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.) - * @return A new BytesTrie for the add()ed data. - * @stable ICU 4.8 - */ - BytesTrie *build(UStringTrieBuildOption buildOption, UErrorCode &errorCode); - - /** - * Builds a BytesTrie for the add()ed data and byte-serializes it. - * Once built, no further data can be add()ed until clear() is called. - * - * A BytesTrie cannot be empty. At least one (byte sequence, value) pair - * must have been add()ed. - * - * Multiple calls to buildStringPiece() return StringPieces referring to the - * builder's same byte array, without rebuilding. - * If buildStringPiece() is called after build(), the trie will be - * re-serialized into a new array (because build() passes on ownership). - * If build() is called after buildStringPiece(), the trie object returned - * by build() will become the owner of the underlying string for the - * previously returned StringPiece. - * After clear() has been called, a new array will be used as well. - * @param buildOption Build option, see UStringTrieBuildOption. - * @param errorCode Standard 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.) - * @return A StringPiece which refers to the byte-serialized BytesTrie for the add()ed data. - * @stable ICU 4.8 - */ - StringPiece buildStringPiece(UStringTrieBuildOption buildOption, UErrorCode &errorCode); - - /** - * Removes all (byte sequence, value) pairs. - * New data can then be add()ed and a new trie can be built. - * @return *this - * @stable ICU 4.8 - */ - BytesTrieBuilder &clear(); - -private: - friend class ::BytesTrieTest; - - BytesTrieBuilder(const BytesTrieBuilder &other) = delete; // no copy constructor - BytesTrieBuilder &operator=(const BytesTrieBuilder &other) = delete; // no assignment operator - - void buildBytes(UStringTrieBuildOption buildOption, UErrorCode &errorCode); - - virtual int32_t getElementStringLength(int32_t i) const override; - virtual char16_t getElementUnit(int32_t i, int32_t byteIndex) const override; - virtual int32_t getElementValue(int32_t i) const override; - - virtual int32_t getLimitOfLinearMatch(int32_t first, int32_t last, int32_t byteIndex) const override; - - virtual int32_t countElementUnits(int32_t start, int32_t limit, int32_t byteIndex) const override; - virtual int32_t skipElementsBySomeUnits(int32_t i, int32_t byteIndex, int32_t count) const override; - virtual int32_t indexOfElementWithNextUnit(int32_t i, int32_t byteIndex, char16_t byte) const override; - - virtual UBool matchNodesCanHaveValues() const override { return false; } - - virtual int32_t getMaxBranchLinearSubNodeLength() const override { return BytesTrie::kMaxBranchLinearSubNodeLength; } - virtual int32_t getMinLinearMatch() const override { return BytesTrie::kMinLinearMatch; } - virtual int32_t getMaxLinearMatchLength() const override { return BytesTrie::kMaxLinearMatchLength; } - - /** - * @internal (private) - */ - class BTLinearMatchNode : public LinearMatchNode { - public: - BTLinearMatchNode(const char *units, int32_t len, Node *nextNode); - virtual bool operator==(const Node &other) const override; - virtual void write(StringTrieBuilder &builder) override; - private: - const char *s; - }; - - virtual Node *createLinearMatchNode(int32_t i, int32_t byteIndex, int32_t length, - Node *nextNode) const override; - - UBool ensureCapacity(int32_t length); - virtual int32_t write(int32_t byte) override; - int32_t write(const char *b, int32_t length); - virtual int32_t writeElementUnits(int32_t i, int32_t byteIndex, int32_t length) override; - virtual int32_t writeValueAndFinal(int32_t i, UBool isFinal) override; - virtual int32_t writeValueAndType(UBool hasValue, int32_t value, int32_t node) override; - virtual int32_t writeDeltaTo(int32_t jumpTarget) override; - static int32_t internalEncodeDelta(int32_t i, char intBytes[]); - - CharString *strings; // Pointer not object so we need not #include internal charstr.h. - BytesTrieElement *elements; - int32_t elementsCapacity; - int32_t elementsLength; - - // Byte serialization of the trie. - // Grows from the back: bytesLength measures from the end of the buffer! - char *bytes; - int32_t bytesCapacity; - int32_t bytesLength; -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __BYTESTRIEBUILDER_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2016, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: bytestriebuilder.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010sep25 +* created by: Markus W. Scherer +*/ + +/** + * \file + * \brief C++ API: Builder for icu::BytesTrie + */ + +#ifndef __BYTESTRIEBUILDER_H__ +#define __BYTESTRIEBUILDER_H__ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/bytestrie.h" +#include "unicode/stringpiece.h" +#include "unicode/stringtriebuilder.h" + +class BytesTrieTest; + +U_NAMESPACE_BEGIN + +class BytesTrieElement; +class CharString; +/** + * Builder class for BytesTrie. + * + * This class is not intended for public subclassing. + * @stable ICU 4.8 + */ +class U_COMMON_API BytesTrieBuilder : public StringTrieBuilder { +public: + /** + * Constructs an empty builder. + * @param errorCode Standard ICU error code. + * @stable ICU 4.8 + */ + BytesTrieBuilder(UErrorCode &errorCode); + + /** + * Destructor. + * @stable ICU 4.8 + */ + virtual ~BytesTrieBuilder(); + + /** + * Adds a (byte sequence, value) pair. + * The byte sequence must be unique. + * The bytes will be copied; the builder does not keep + * a reference to the input StringPiece or its data(). + * @param s The input byte sequence. + * @param value The value associated with this byte sequence. + * @param errorCode Standard 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.) + * @return *this + * @stable ICU 4.8 + */ + BytesTrieBuilder &add(StringPiece s, int32_t value, UErrorCode &errorCode); + + /** + * Builds a BytesTrie for the add()ed data. + * Once built, no further data can be add()ed until clear() is called. + * + * A BytesTrie cannot be empty. At least one (byte sequence, value) pair + * must have been add()ed. + * + * This method passes ownership of the builder's internal result array to the new trie object. + * Another call to any build() variant will re-serialize the trie. + * After clear() has been called, a new array will be used as well. + * @param buildOption Build option, see UStringTrieBuildOption. + * @param errorCode Standard 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.) + * @return A new BytesTrie for the add()ed data. + * @stable ICU 4.8 + */ + BytesTrie *build(UStringTrieBuildOption buildOption, UErrorCode &errorCode); + + /** + * Builds a BytesTrie for the add()ed data and byte-serializes it. + * Once built, no further data can be add()ed until clear() is called. + * + * A BytesTrie cannot be empty. At least one (byte sequence, value) pair + * must have been add()ed. + * + * Multiple calls to buildStringPiece() return StringPieces referring to the + * builder's same byte array, without rebuilding. + * If buildStringPiece() is called after build(), the trie will be + * re-serialized into a new array (because build() passes on ownership). + * If build() is called after buildStringPiece(), the trie object returned + * by build() will become the owner of the underlying string for the + * previously returned StringPiece. + * After clear() has been called, a new array will be used as well. + * @param buildOption Build option, see UStringTrieBuildOption. + * @param errorCode Standard 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.) + * @return A StringPiece which refers to the byte-serialized BytesTrie for the add()ed data. + * @stable ICU 4.8 + */ + StringPiece buildStringPiece(UStringTrieBuildOption buildOption, UErrorCode &errorCode); + + /** + * Removes all (byte sequence, value) pairs. + * New data can then be add()ed and a new trie can be built. + * @return *this + * @stable ICU 4.8 + */ + BytesTrieBuilder &clear(); + +private: + friend class ::BytesTrieTest; + + BytesTrieBuilder(const BytesTrieBuilder &other) = delete; // no copy constructor + BytesTrieBuilder &operator=(const BytesTrieBuilder &other) = delete; // no assignment operator + + void buildBytes(UStringTrieBuildOption buildOption, UErrorCode &errorCode); + + virtual int32_t getElementStringLength(int32_t i) const override; + virtual char16_t getElementUnit(int32_t i, int32_t byteIndex) const override; + virtual int32_t getElementValue(int32_t i) const override; + + virtual int32_t getLimitOfLinearMatch(int32_t first, int32_t last, int32_t byteIndex) const override; + + virtual int32_t countElementUnits(int32_t start, int32_t limit, int32_t byteIndex) const override; + virtual int32_t skipElementsBySomeUnits(int32_t i, int32_t byteIndex, int32_t count) const override; + virtual int32_t indexOfElementWithNextUnit(int32_t i, int32_t byteIndex, char16_t byte) const override; + + virtual UBool matchNodesCanHaveValues() const override { return false; } + + virtual int32_t getMaxBranchLinearSubNodeLength() const override { return BytesTrie::kMaxBranchLinearSubNodeLength; } + virtual int32_t getMinLinearMatch() const override { return BytesTrie::kMinLinearMatch; } + virtual int32_t getMaxLinearMatchLength() const override { return BytesTrie::kMaxLinearMatchLength; } + + /** + * @internal (private) + */ + class BTLinearMatchNode : public LinearMatchNode { + public: + BTLinearMatchNode(const char *units, int32_t len, Node *nextNode); + virtual bool operator==(const Node &other) const override; + virtual void write(StringTrieBuilder &builder) override; + private: + const char *s; + }; + + virtual Node *createLinearMatchNode(int32_t i, int32_t byteIndex, int32_t length, + Node *nextNode) const override; + + UBool ensureCapacity(int32_t length); + virtual int32_t write(int32_t byte) override; + int32_t write(const char *b, int32_t length); + virtual int32_t writeElementUnits(int32_t i, int32_t byteIndex, int32_t length) override; + virtual int32_t writeValueAndFinal(int32_t i, UBool isFinal) override; + virtual int32_t writeValueAndType(UBool hasValue, int32_t value, int32_t node) override; + virtual int32_t writeDeltaTo(int32_t jumpTarget) override; + static int32_t internalEncodeDelta(int32_t i, char intBytes[]); + + CharString *strings; // Pointer not object so we need not #include internal charstr.h. + BytesTrieElement *elements; + int32_t elementsCapacity; + int32_t elementsLength; + + // Byte serialization of the trie. + // Grows from the back: bytesLength measures from the end of the buffer! + char *bytes; + int32_t bytesCapacity; + int32_t bytesLength; +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __BYTESTRIEBUILDER_H__ diff --git a/deps/icu-small/source/common/unicode/caniter.h b/deps/icu-small/source/common/unicode/caniter.h index db400a531bfe4d..ffb66e0370d610 100644 --- a/deps/icu-small/source/common/unicode/caniter.h +++ b/deps/icu-small/source/common/unicode/caniter.h @@ -1,214 +1,214 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ******************************************************************************* - * Copyright (C) 1996-2014, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************************* - */ - -#ifndef CANITER_H -#define CANITER_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#if !UCONFIG_NO_NORMALIZATION - -#include "unicode/uobject.h" -#include "unicode/unistr.h" - -/** - * \file - * \brief C++ API: Canonical Iterator - */ - -/** Should permutation skip characters with combining class zero - * Should be either true or false. This is a compile time option - * @stable ICU 2.4 - */ -#ifndef CANITER_SKIP_ZEROES -#define CANITER_SKIP_ZEROES true -#endif - -U_NAMESPACE_BEGIN - -class Hashtable; -class Normalizer2; -class Normalizer2Impl; - -/** - * This class allows one to iterate through all the strings that are canonically equivalent to a given - * string. For example, here are some sample results: -Results for: {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} -1: \\u0041\\u030A\\u0064\\u0307\\u0327 - = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} -2: \\u0041\\u030A\\u0064\\u0327\\u0307 - = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D}{COMBINING CEDILLA}{COMBINING DOT ABOVE} -3: \\u0041\\u030A\\u1E0B\\u0327 - = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D WITH DOT ABOVE}{COMBINING CEDILLA} -4: \\u0041\\u030A\\u1E11\\u0307 - = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D WITH CEDILLA}{COMBINING DOT ABOVE} -5: \\u00C5\\u0064\\u0307\\u0327 - = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} -6: \\u00C5\\u0064\\u0327\\u0307 - = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D}{COMBINING CEDILLA}{COMBINING DOT ABOVE} -7: \\u00C5\\u1E0B\\u0327 - = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D WITH DOT ABOVE}{COMBINING CEDILLA} -8: \\u00C5\\u1E11\\u0307 - = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D WITH CEDILLA}{COMBINING DOT ABOVE} -9: \\u212B\\u0064\\u0307\\u0327 - = {ANGSTROM SIGN}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} -10: \\u212B\\u0064\\u0327\\u0307 - = {ANGSTROM SIGN}{LATIN SMALL LETTER D}{COMBINING CEDILLA}{COMBINING DOT ABOVE} -11: \\u212B\\u1E0B\\u0327 - = {ANGSTROM SIGN}{LATIN SMALL LETTER D WITH DOT ABOVE}{COMBINING CEDILLA} -12: \\u212B\\u1E11\\u0307 - = {ANGSTROM SIGN}{LATIN SMALL LETTER D WITH CEDILLA}{COMBINING DOT ABOVE} - *
Note: the code is intended for use with small strings, and is not suitable for larger ones, - * since it has not been optimized for that situation. - * Note, CanonicalIterator is not intended to be subclassed. - * @author M. Davis - * @author C++ port by V. Weinstein - * @stable ICU 2.4 - */ -class U_COMMON_API CanonicalIterator U_FINAL : public UObject { -public: - /** - * Construct a CanonicalIterator object - * @param source string to get results for - * @param status Fill-in parameter which receives the status of this operation. - * @stable ICU 2.4 - */ - CanonicalIterator(const UnicodeString &source, UErrorCode &status); - - /** Destructor - * Cleans pieces - * @stable ICU 2.4 - */ - virtual ~CanonicalIterator(); - - /** - * Gets the NFD form of the current source we are iterating over. - * @return gets the source: NOTE: it is the NFD form of source - * @stable ICU 2.4 - */ - UnicodeString getSource(); - - /** - * Resets the iterator so that one can start again from the beginning. - * @stable ICU 2.4 - */ - void reset(); - - /** - * Get the next canonically equivalent string. - *
Warning: The strings are not guaranteed to be in any particular order. - * @return the next string that is canonically equivalent. A bogus string is returned when - * the iteration is done. - * @stable ICU 2.4 - */ - UnicodeString next(); - - /** - * Set a new source for this iterator. Allows object reuse. - * @param newSource the source string to iterate against. This allows the same iterator to be used - * while changing the source string, saving object creation. - * @param status Fill-in parameter which receives the status of this operation. - * @stable ICU 2.4 - */ - void setSource(const UnicodeString &newSource, UErrorCode &status); - -#ifndef U_HIDE_INTERNAL_API - /** - * Dumb recursive implementation of permutation. - * TODO: optimize - * @param source the string to find permutations for - * @param skipZeros determine if skip zeros - * @param result the results in a set. - * @param status Fill-in parameter which receives the status of this operation. - * @internal - */ - static void U_EXPORT2 permute(UnicodeString &source, UBool skipZeros, Hashtable *result, UErrorCode &status); -#endif /* U_HIDE_INTERNAL_API */ - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - * - * @stable ICU 2.2 - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - * - * @stable ICU 2.2 - */ - virtual UClassID getDynamicClassID() const override; - -private: - // ===================== PRIVATES ============================== - // private default constructor - CanonicalIterator() = delete; - - - /** - * Copy constructor. Private for now. - * @internal (private) - */ - CanonicalIterator(const CanonicalIterator& other) = delete; - - /** - * Assignment operator. Private for now. - * @internal (private) - */ - CanonicalIterator& operator=(const CanonicalIterator& other) = delete; - - // fields - UnicodeString source; - UBool done; - - // 2 dimensional array holds the pieces of the string with - // their different canonically equivalent representations - UnicodeString **pieces; - int32_t pieces_length; - int32_t *pieces_lengths; - - // current is used in iterating to combine pieces - int32_t *current; - int32_t current_length; - - // transient fields - UnicodeString buffer; - - const Normalizer2 &nfd; - const Normalizer2Impl &nfcImpl; - - // we have a segment, in NFD. Find all the strings that are canonically equivalent to it. - UnicodeString *getEquivalents(const UnicodeString &segment, int32_t &result_len, UErrorCode &status); //private String[] getEquivalents(String segment) - - //Set getEquivalents2(String segment); - Hashtable *getEquivalents2(Hashtable *fillinResult, const char16_t *segment, int32_t segLen, UErrorCode &status); - //Hashtable *getEquivalents2(const UnicodeString &segment, int32_t segLen, UErrorCode &status); - - /** - * See if the decomposition of cp2 is at segment starting at segmentPos - * (with canonical rearrangement!) - * If so, take the remainder, and return the equivalents - */ - //Set extract(int comp, String segment, int segmentPos, StringBuffer buffer); - Hashtable *extract(Hashtable *fillinResult, UChar32 comp, const char16_t *segment, int32_t segLen, int32_t segmentPos, UErrorCode &status); - //Hashtable *extract(UChar32 comp, const UnicodeString &segment, int32_t segLen, int32_t segmentPos, UErrorCode &status); - - void cleanPieces(); - -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_NORMALIZATION */ - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************************* + * Copyright (C) 1996-2014, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************************* + */ + +#ifndef CANITER_H +#define CANITER_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#if !UCONFIG_NO_NORMALIZATION + +#include "unicode/uobject.h" +#include "unicode/unistr.h" + +/** + * \file + * \brief C++ API: Canonical Iterator + */ + +/** Should permutation skip characters with combining class zero + * Should be either true or false. This is a compile time option + * @stable ICU 2.4 + */ +#ifndef CANITER_SKIP_ZEROES +#define CANITER_SKIP_ZEROES true +#endif + +U_NAMESPACE_BEGIN + +class Hashtable; +class Normalizer2; +class Normalizer2Impl; + +/** + * This class allows one to iterate through all the strings that are canonically equivalent to a given + * string. For example, here are some sample results: +Results for: {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} +1: \\u0041\\u030A\\u0064\\u0307\\u0327 + = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} +2: \\u0041\\u030A\\u0064\\u0327\\u0307 + = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D}{COMBINING CEDILLA}{COMBINING DOT ABOVE} +3: \\u0041\\u030A\\u1E0B\\u0327 + = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D WITH DOT ABOVE}{COMBINING CEDILLA} +4: \\u0041\\u030A\\u1E11\\u0307 + = {LATIN CAPITAL LETTER A}{COMBINING RING ABOVE}{LATIN SMALL LETTER D WITH CEDILLA}{COMBINING DOT ABOVE} +5: \\u00C5\\u0064\\u0307\\u0327 + = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} +6: \\u00C5\\u0064\\u0327\\u0307 + = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D}{COMBINING CEDILLA}{COMBINING DOT ABOVE} +7: \\u00C5\\u1E0B\\u0327 + = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D WITH DOT ABOVE}{COMBINING CEDILLA} +8: \\u00C5\\u1E11\\u0307 + = {LATIN CAPITAL LETTER A WITH RING ABOVE}{LATIN SMALL LETTER D WITH CEDILLA}{COMBINING DOT ABOVE} +9: \\u212B\\u0064\\u0307\\u0327 + = {ANGSTROM SIGN}{LATIN SMALL LETTER D}{COMBINING DOT ABOVE}{COMBINING CEDILLA} +10: \\u212B\\u0064\\u0327\\u0307 + = {ANGSTROM SIGN}{LATIN SMALL LETTER D}{COMBINING CEDILLA}{COMBINING DOT ABOVE} +11: \\u212B\\u1E0B\\u0327 + = {ANGSTROM SIGN}{LATIN SMALL LETTER D WITH DOT ABOVE}{COMBINING CEDILLA} +12: \\u212B\\u1E11\\u0307 + = {ANGSTROM SIGN}{LATIN SMALL LETTER D WITH CEDILLA}{COMBINING DOT ABOVE} + *
Note: the code is intended for use with small strings, and is not suitable for larger ones, + * since it has not been optimized for that situation. + * Note, CanonicalIterator is not intended to be subclassed. + * @author M. Davis + * @author C++ port by V. Weinstein + * @stable ICU 2.4 + */ +class U_COMMON_API CanonicalIterator final : public UObject { +public: + /** + * Construct a CanonicalIterator object + * @param source string to get results for + * @param status Fill-in parameter which receives the status of this operation. + * @stable ICU 2.4 + */ + CanonicalIterator(const UnicodeString &source, UErrorCode &status); + + /** Destructor + * Cleans pieces + * @stable ICU 2.4 + */ + virtual ~CanonicalIterator(); + + /** + * Gets the NFD form of the current source we are iterating over. + * @return gets the source: NOTE: it is the NFD form of source + * @stable ICU 2.4 + */ + UnicodeString getSource(); + + /** + * Resets the iterator so that one can start again from the beginning. + * @stable ICU 2.4 + */ + void reset(); + + /** + * Get the next canonically equivalent string. + *
Warning: The strings are not guaranteed to be in any particular order. + * @return the next string that is canonically equivalent. A bogus string is returned when + * the iteration is done. + * @stable ICU 2.4 + */ + UnicodeString next(); + + /** + * Set a new source for this iterator. Allows object reuse. + * @param newSource the source string to iterate against. This allows the same iterator to be used + * while changing the source string, saving object creation. + * @param status Fill-in parameter which receives the status of this operation. + * @stable ICU 2.4 + */ + void setSource(const UnicodeString &newSource, UErrorCode &status); + +#ifndef U_HIDE_INTERNAL_API + /** + * Dumb recursive implementation of permutation. + * TODO: optimize + * @param source the string to find permutations for + * @param skipZeros determine if skip zeros + * @param result the results in a set. + * @param status Fill-in parameter which receives the status of this operation. + * @internal + */ + static void U_EXPORT2 permute(UnicodeString &source, UBool skipZeros, Hashtable *result, UErrorCode &status); +#endif /* U_HIDE_INTERNAL_API */ + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + * + * @stable ICU 2.2 + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + * + * @stable ICU 2.2 + */ + virtual UClassID getDynamicClassID() const override; + +private: + // ===================== PRIVATES ============================== + // private default constructor + CanonicalIterator() = delete; + + + /** + * Copy constructor. Private for now. + * @internal (private) + */ + CanonicalIterator(const CanonicalIterator& other) = delete; + + /** + * Assignment operator. Private for now. + * @internal (private) + */ + CanonicalIterator& operator=(const CanonicalIterator& other) = delete; + + // fields + UnicodeString source; + UBool done; + + // 2 dimensional array holds the pieces of the string with + // their different canonically equivalent representations + UnicodeString **pieces; + int32_t pieces_length; + int32_t *pieces_lengths; + + // current is used in iterating to combine pieces + int32_t *current; + int32_t current_length; + + // transient fields + UnicodeString buffer; + + const Normalizer2 &nfd; + const Normalizer2Impl &nfcImpl; + + // we have a segment, in NFD. Find all the strings that are canonically equivalent to it. + UnicodeString *getEquivalents(const UnicodeString &segment, int32_t &result_len, UErrorCode &status); //private String[] getEquivalents(String segment) + + //Set getEquivalents2(String segment); + Hashtable *getEquivalents2(Hashtable *fillinResult, const char16_t *segment, int32_t segLen, UErrorCode &status); + //Hashtable *getEquivalents2(const UnicodeString &segment, int32_t segLen, UErrorCode &status); + + /** + * See if the decomposition of cp2 is at segment starting at segmentPos + * (with canonical rearrangement!) + * If so, take the remainder, and return the equivalents + */ + //Set extract(int comp, String segment, int segmentPos, StringBuffer buffer); + Hashtable *extract(Hashtable *fillinResult, UChar32 comp, const char16_t *segment, int32_t segLen, int32_t segmentPos, UErrorCode &status); + //Hashtable *extract(UChar32 comp, const UnicodeString &segment, int32_t segLen, int32_t segmentPos, UErrorCode &status); + + void cleanPieces(); + +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_NORMALIZATION */ + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/casemap.h b/deps/icu-small/source/common/unicode/casemap.h index 53af84fa74d65f..fcc43f14b52af3 100644 --- a/deps/icu-small/source/common/unicode/casemap.h +++ b/deps/icu-small/source/common/unicode/casemap.h @@ -1,497 +1,497 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// casemap.h -// created: 2017jan12 Markus W. Scherer - -#ifndef __CASEMAP_H__ -#define __CASEMAP_H__ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/stringpiece.h" -#include "unicode/uobject.h" - -/** - * \file - * \brief C++ API: Low-level C++ case mapping functions. - */ - -U_NAMESPACE_BEGIN - -class BreakIterator; -class ByteSink; -class Edits; - -/** - * Low-level C++ case mapping functions. - * - * @stable ICU 59 - */ -class U_COMMON_API CaseMap U_FINAL : public UMemory { -public: - /** - * Lowercases a UTF-16 string and optionally records edits. - * Casing is locale-dependent and context-sensitive. - * The result may be longer or shorter than the original. - * The source string and the destination buffer must not overlap. - * - * @param locale The locale ID. ("" = root locale, NULL = default locale.) - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. - * @param src The original string. - * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. - * @param dest A buffer for the result string. The result will be NUL-terminated if - * the buffer is large enough. - * The contents is undefined in case of failure. - * @param destCapacity The size of the buffer (number of char16_ts). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be NULL. - * @param errorCode Reference to an in/out error code value - * which must not indicate a failure before the function call. - * @return The length of the result string, if successful. - * When the result would be longer than destCapacity, - * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. - * - * @see u_strToLower - * @stable ICU 59 - */ - static int32_t toLower( - const char *locale, uint32_t options, - const char16_t *src, int32_t srcLength, - char16_t *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode); - - /** - * Uppercases a UTF-16 string and optionally records edits. - * Casing is locale-dependent and context-sensitive. - * The result may be longer or shorter than the original. - * The source string and the destination buffer must not overlap. - * - * @param locale The locale ID. ("" = root locale, NULL = default locale.) - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. - * @param src The original string. - * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. - * @param dest A buffer for the result string. The result will be NUL-terminated if - * the buffer is large enough. - * The contents is undefined in case of failure. - * @param destCapacity The size of the buffer (number of char16_ts). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be NULL. - * @param errorCode Reference to an in/out error code value - * which must not indicate a failure before the function call. - * @return The length of the result string, if successful. - * When the result would be longer than destCapacity, - * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. - * - * @see u_strToUpper - * @stable ICU 59 - */ - static int32_t toUpper( - const char *locale, uint32_t options, - const char16_t *src, int32_t srcLength, - char16_t *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode); - -#if !UCONFIG_NO_BREAK_ITERATION - - /** - * Titlecases a UTF-16 string and optionally records edits. - * Casing is locale-dependent and context-sensitive. - * The result may be longer or shorter than the original. - * The source string and the destination buffer must not overlap. - * - * Titlecasing uses a break iterator to find the first characters of words - * that are to be titlecased. It titlecases those characters and lowercases - * all others. (This can be modified with options bits.) - * - * @param locale The locale ID. ("" = root locale, NULL = default locale.) - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT, U_EDITS_NO_RESET, - * U_TITLECASE_NO_LOWERCASE, - * U_TITLECASE_NO_BREAK_ADJUSTMENT, U_TITLECASE_ADJUST_TO_CASED, - * U_TITLECASE_WHOLE_STRING, U_TITLECASE_SENTENCES. - * @param iter A break iterator to find the first characters of words that are to be titlecased. - * It is set to the source string (setText()) - * and used one or more times for iteration (first() and next()). - * If NULL, then a word break iterator for the locale is used - * (or something equivalent). - * @param src The original string. - * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. - * @param dest A buffer for the result string. The result will be NUL-terminated if - * the buffer is large enough. - * The contents is undefined in case of failure. - * @param destCapacity The size of the buffer (number of char16_ts). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be NULL. - * @param errorCode Reference to an in/out error code value - * which must not indicate a failure before the function call. - * @return The length of the result string, if successful. - * When the result would be longer than destCapacity, - * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. - * - * @see u_strToTitle - * @see ucasemap_toTitle - * @stable ICU 59 - */ - static int32_t toTitle( - const char *locale, uint32_t options, BreakIterator *iter, - const char16_t *src, int32_t srcLength, - char16_t *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode); - -#endif // UCONFIG_NO_BREAK_ITERATION - - /** - * Case-folds a UTF-16 string and optionally records edits. - * - * Case folding is locale-independent and not context-sensitive, - * but there is an option for whether to include or exclude mappings for dotted I - * and dotless i that are marked with 'T' in CaseFolding.txt. - * - * The result may be longer or shorter than the original. - * The source string and the destination buffer must not overlap. - * - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT, U_EDITS_NO_RESET, - * U_FOLD_CASE_DEFAULT, U_FOLD_CASE_EXCLUDE_SPECIAL_I. - * @param src The original string. - * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. - * @param dest A buffer for the result string. The result will be NUL-terminated if - * the buffer is large enough. - * The contents is undefined in case of failure. - * @param destCapacity The size of the buffer (number of char16_ts). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be NULL. - * @param errorCode Reference to an in/out error code value - * which must not indicate a failure before the function call. - * @return The length of the result string, if successful. - * When the result would be longer than destCapacity, - * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. - * - * @see u_strFoldCase - * @stable ICU 59 - */ - static int32_t fold( - uint32_t options, - const char16_t *src, int32_t srcLength, - char16_t *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode); - - /** - * Lowercases a UTF-8 string and optionally records edits. - * Casing is locale-dependent and context-sensitive. - * The result may be longer or shorter than the original. - * - * @param locale The locale ID. ("" = root locale, NULL = default locale.) - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. - * @param src The original string. - * @param sink A ByteSink to which the result string is written. - * sink.Flush() is called at the end. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be NULL. - * @param errorCode Reference to an in/out error code value - * which must not indicate a failure before the function call. - * - * @see ucasemap_utf8ToLower - * @stable ICU 60 - */ - static void utf8ToLower( - const char *locale, uint32_t options, - StringPiece src, ByteSink &sink, Edits *edits, - UErrorCode &errorCode); - - /** - * Uppercases a UTF-8 string and optionally records edits. - * Casing is locale-dependent and context-sensitive. - * The result may be longer or shorter than the original. - * - * @param locale The locale ID. ("" = root locale, NULL = default locale.) - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. - * @param src The original string. - * @param sink A ByteSink to which the result string is written. - * sink.Flush() is called at the end. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be NULL. - * @param errorCode Reference to an in/out error code value - * which must not indicate a failure before the function call. - * - * @see ucasemap_utf8ToUpper - * @stable ICU 60 - */ - static void utf8ToUpper( - const char *locale, uint32_t options, - StringPiece src, ByteSink &sink, Edits *edits, - UErrorCode &errorCode); - -#if !UCONFIG_NO_BREAK_ITERATION - - /** - * Titlecases a UTF-8 string and optionally records edits. - * Casing is locale-dependent and context-sensitive. - * The result may be longer or shorter than the original. - * - * Titlecasing uses a break iterator to find the first characters of words - * that are to be titlecased. It titlecases those characters and lowercases - * all others. (This can be modified with options bits.) - * - * @param locale The locale ID. ("" = root locale, NULL = default locale.) - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT, U_EDITS_NO_RESET, - * U_TITLECASE_NO_LOWERCASE, - * U_TITLECASE_NO_BREAK_ADJUSTMENT, U_TITLECASE_ADJUST_TO_CASED, - * U_TITLECASE_WHOLE_STRING, U_TITLECASE_SENTENCES. - * @param iter A break iterator to find the first characters of words that are to be titlecased. - * It is set to the source string (setUText()) - * and used one or more times for iteration (first() and next()). - * If NULL, then a word break iterator for the locale is used - * (or something equivalent). - * @param src The original string. - * @param sink A ByteSink to which the result string is written. - * sink.Flush() is called at the end. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be NULL. - * @param errorCode Reference to an in/out error code value - * which must not indicate a failure before the function call. - * - * @see ucasemap_utf8ToTitle - * @stable ICU 60 - */ - static void utf8ToTitle( - const char *locale, uint32_t options, BreakIterator *iter, - StringPiece src, ByteSink &sink, Edits *edits, - UErrorCode &errorCode); - -#endif // UCONFIG_NO_BREAK_ITERATION - - /** - * Case-folds a UTF-8 string and optionally records edits. - * - * Case folding is locale-independent and not context-sensitive, - * but there is an option for whether to include or exclude mappings for dotted I - * and dotless i that are marked with 'T' in CaseFolding.txt. - * - * The result may be longer or shorter than the original. - * - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. - * @param src The original string. - * @param sink A ByteSink to which the result string is written. - * sink.Flush() is called at the end. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be NULL. - * @param errorCode Reference to an in/out error code value - * which must not indicate a failure before the function call. - * - * @see ucasemap_utf8FoldCase - * @stable ICU 60 - */ - static void utf8Fold( - uint32_t options, - StringPiece src, ByteSink &sink, Edits *edits, - UErrorCode &errorCode); - - /** - * Lowercases a UTF-8 string and optionally records edits. - * Casing is locale-dependent and context-sensitive. - * The result may be longer or shorter than the original. - * The source string and the destination buffer must not overlap. - * - * @param locale The locale ID. ("" = root locale, NULL = default locale.) - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. - * @param src The original string. - * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. - * @param dest A buffer for the result string. The result will be NUL-terminated if - * the buffer is large enough. - * The contents is undefined in case of failure. - * @param destCapacity The size of the buffer (number of bytes). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be NULL. - * @param errorCode Reference to an in/out error code value - * which must not indicate a failure before the function call. - * @return The length of the result string, if successful. - * When the result would be longer than destCapacity, - * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. - * - * @see ucasemap_utf8ToLower - * @stable ICU 59 - */ - static int32_t utf8ToLower( - const char *locale, uint32_t options, - const char *src, int32_t srcLength, - char *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode); - - /** - * Uppercases a UTF-8 string and optionally records edits. - * Casing is locale-dependent and context-sensitive. - * The result may be longer or shorter than the original. - * The source string and the destination buffer must not overlap. - * - * @param locale The locale ID. ("" = root locale, NULL = default locale.) - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. - * @param src The original string. - * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. - * @param dest A buffer for the result string. The result will be NUL-terminated if - * the buffer is large enough. - * The contents is undefined in case of failure. - * @param destCapacity The size of the buffer (number of bytes). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be NULL. - * @param errorCode Reference to an in/out error code value - * which must not indicate a failure before the function call. - * @return The length of the result string, if successful. - * When the result would be longer than destCapacity, - * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. - * - * @see ucasemap_utf8ToUpper - * @stable ICU 59 - */ - static int32_t utf8ToUpper( - const char *locale, uint32_t options, - const char *src, int32_t srcLength, - char *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode); - -#if !UCONFIG_NO_BREAK_ITERATION - - /** - * Titlecases a UTF-8 string and optionally records edits. - * Casing is locale-dependent and context-sensitive. - * The result may be longer or shorter than the original. - * The source string and the destination buffer must not overlap. - * - * Titlecasing uses a break iterator to find the first characters of words - * that are to be titlecased. It titlecases those characters and lowercases - * all others. (This can be modified with options bits.) - * - * @param locale The locale ID. ("" = root locale, NULL = default locale.) - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT, U_EDITS_NO_RESET, - * U_TITLECASE_NO_LOWERCASE, - * U_TITLECASE_NO_BREAK_ADJUSTMENT, U_TITLECASE_ADJUST_TO_CASED, - * U_TITLECASE_WHOLE_STRING, U_TITLECASE_SENTENCES. - * @param iter A break iterator to find the first characters of words that are to be titlecased. - * It is set to the source string (setUText()) - * and used one or more times for iteration (first() and next()). - * If NULL, then a word break iterator for the locale is used - * (or something equivalent). - * @param src The original string. - * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. - * @param dest A buffer for the result string. The result will be NUL-terminated if - * the buffer is large enough. - * The contents is undefined in case of failure. - * @param destCapacity The size of the buffer (number of bytes). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be NULL. - * @param errorCode Reference to an in/out error code value - * which must not indicate a failure before the function call. - * @return The length of the result string, if successful. - * When the result would be longer than destCapacity, - * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. - * - * @see ucasemap_utf8ToTitle - * @stable ICU 59 - */ - static int32_t utf8ToTitle( - const char *locale, uint32_t options, BreakIterator *iter, - const char *src, int32_t srcLength, - char *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode); - -#endif // UCONFIG_NO_BREAK_ITERATION - - /** - * Case-folds a UTF-8 string and optionally records edits. - * - * Case folding is locale-independent and not context-sensitive, - * but there is an option for whether to include or exclude mappings for dotted I - * and dotless i that are marked with 'T' in CaseFolding.txt. - * - * The result may be longer or shorter than the original. - * The source string and the destination buffer must not overlap. - * - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT, U_EDITS_NO_RESET, - * U_FOLD_CASE_DEFAULT, U_FOLD_CASE_EXCLUDE_SPECIAL_I. - * @param src The original string. - * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. - * @param dest A buffer for the result string. The result will be NUL-terminated if - * the buffer is large enough. - * The contents is undefined in case of failure. - * @param destCapacity The size of the buffer (number of bytes). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be NULL. - * @param errorCode Reference to an in/out error code value - * which must not indicate a failure before the function call. - * @return The length of the result string, if successful. - * When the result would be longer than destCapacity, - * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. - * - * @see ucasemap_utf8FoldCase - * @stable ICU 59 - */ - static int32_t utf8Fold( - uint32_t options, - const char *src, int32_t srcLength, - char *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode); - -private: - CaseMap() = delete; - CaseMap(const CaseMap &other) = delete; - CaseMap &operator=(const CaseMap &other) = delete; -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __CASEMAP_H__ +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// casemap.h +// created: 2017jan12 Markus W. Scherer + +#ifndef __CASEMAP_H__ +#define __CASEMAP_H__ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/stringpiece.h" +#include "unicode/uobject.h" + +/** + * \file + * \brief C++ API: Low-level C++ case mapping functions. + */ + +U_NAMESPACE_BEGIN + +class BreakIterator; +class ByteSink; +class Edits; + +/** + * Low-level C++ case mapping functions. + * + * @stable ICU 59 + */ +class U_COMMON_API CaseMap final : public UMemory { +public: + /** + * Lowercases a UTF-16 string and optionally records edits. + * Casing is locale-dependent and context-sensitive. + * The result may be longer or shorter than the original. + * The source string and the destination buffer must not overlap. + * + * @param locale The locale ID. ("" = root locale, nullptr = default locale.) + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. + * @param src The original string. + * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. + * @param dest A buffer for the result string. The result will be NUL-terminated if + * the buffer is large enough. + * The contents is undefined in case of failure. + * @param destCapacity The size of the buffer (number of char16_ts). If it is 0, then + * dest may be nullptr and the function will only return the length of the result + * without writing any of the result string. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Reference to an in/out error code value + * which must not indicate a failure before the function call. + * @return The length of the result string, if successful. + * When the result would be longer than destCapacity, + * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. + * + * @see u_strToLower + * @stable ICU 59 + */ + static int32_t toLower( + const char *locale, uint32_t options, + const char16_t *src, int32_t srcLength, + char16_t *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode); + + /** + * Uppercases a UTF-16 string and optionally records edits. + * Casing is locale-dependent and context-sensitive. + * The result may be longer or shorter than the original. + * The source string and the destination buffer must not overlap. + * + * @param locale The locale ID. ("" = root locale, nullptr = default locale.) + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. + * @param src The original string. + * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. + * @param dest A buffer for the result string. The result will be NUL-terminated if + * the buffer is large enough. + * The contents is undefined in case of failure. + * @param destCapacity The size of the buffer (number of char16_ts). If it is 0, then + * dest may be nullptr and the function will only return the length of the result + * without writing any of the result string. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Reference to an in/out error code value + * which must not indicate a failure before the function call. + * @return The length of the result string, if successful. + * When the result would be longer than destCapacity, + * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. + * + * @see u_strToUpper + * @stable ICU 59 + */ + static int32_t toUpper( + const char *locale, uint32_t options, + const char16_t *src, int32_t srcLength, + char16_t *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode); + +#if !UCONFIG_NO_BREAK_ITERATION + + /** + * Titlecases a UTF-16 string and optionally records edits. + * Casing is locale-dependent and context-sensitive. + * The result may be longer or shorter than the original. + * The source string and the destination buffer must not overlap. + * + * Titlecasing uses a break iterator to find the first characters of words + * that are to be titlecased. It titlecases those characters and lowercases + * all others. (This can be modified with options bits.) + * + * @param locale The locale ID. ("" = root locale, nullptr = default locale.) + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT, U_EDITS_NO_RESET, + * U_TITLECASE_NO_LOWERCASE, + * U_TITLECASE_NO_BREAK_ADJUSTMENT, U_TITLECASE_ADJUST_TO_CASED, + * U_TITLECASE_WHOLE_STRING, U_TITLECASE_SENTENCES. + * @param iter A break iterator to find the first characters of words that are to be titlecased. + * It is set to the source string (setText()) + * and used one or more times for iteration (first() and next()). + * If nullptr, then a word break iterator for the locale is used + * (or something equivalent). + * @param src The original string. + * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. + * @param dest A buffer for the result string. The result will be NUL-terminated if + * the buffer is large enough. + * The contents is undefined in case of failure. + * @param destCapacity The size of the buffer (number of char16_ts). If it is 0, then + * dest may be nullptr and the function will only return the length of the result + * without writing any of the result string. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Reference to an in/out error code value + * which must not indicate a failure before the function call. + * @return The length of the result string, if successful. + * When the result would be longer than destCapacity, + * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. + * + * @see u_strToTitle + * @see ucasemap_toTitle + * @stable ICU 59 + */ + static int32_t toTitle( + const char *locale, uint32_t options, BreakIterator *iter, + const char16_t *src, int32_t srcLength, + char16_t *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode); + +#endif // UCONFIG_NO_BREAK_ITERATION + + /** + * Case-folds a UTF-16 string and optionally records edits. + * + * Case folding is locale-independent and not context-sensitive, + * but there is an option for whether to include or exclude mappings for dotted I + * and dotless i that are marked with 'T' in CaseFolding.txt. + * + * The result may be longer or shorter than the original. + * The source string and the destination buffer must not overlap. + * + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT, U_EDITS_NO_RESET, + * U_FOLD_CASE_DEFAULT, U_FOLD_CASE_EXCLUDE_SPECIAL_I. + * @param src The original string. + * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. + * @param dest A buffer for the result string. The result will be NUL-terminated if + * the buffer is large enough. + * The contents is undefined in case of failure. + * @param destCapacity The size of the buffer (number of char16_ts). If it is 0, then + * dest may be nullptr and the function will only return the length of the result + * without writing any of the result string. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Reference to an in/out error code value + * which must not indicate a failure before the function call. + * @return The length of the result string, if successful. + * When the result would be longer than destCapacity, + * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. + * + * @see u_strFoldCase + * @stable ICU 59 + */ + static int32_t fold( + uint32_t options, + const char16_t *src, int32_t srcLength, + char16_t *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode); + + /** + * Lowercases a UTF-8 string and optionally records edits. + * Casing is locale-dependent and context-sensitive. + * The result may be longer or shorter than the original. + * + * @param locale The locale ID. ("" = root locale, nullptr = default locale.) + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. + * @param src The original string. + * @param sink A ByteSink to which the result string is written. + * sink.Flush() is called at the end. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Reference to an in/out error code value + * which must not indicate a failure before the function call. + * + * @see ucasemap_utf8ToLower + * @stable ICU 60 + */ + static void utf8ToLower( + const char *locale, uint32_t options, + StringPiece src, ByteSink &sink, Edits *edits, + UErrorCode &errorCode); + + /** + * Uppercases a UTF-8 string and optionally records edits. + * Casing is locale-dependent and context-sensitive. + * The result may be longer or shorter than the original. + * + * @param locale The locale ID. ("" = root locale, nullptr = default locale.) + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. + * @param src The original string. + * @param sink A ByteSink to which the result string is written. + * sink.Flush() is called at the end. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Reference to an in/out error code value + * which must not indicate a failure before the function call. + * + * @see ucasemap_utf8ToUpper + * @stable ICU 60 + */ + static void utf8ToUpper( + const char *locale, uint32_t options, + StringPiece src, ByteSink &sink, Edits *edits, + UErrorCode &errorCode); + +#if !UCONFIG_NO_BREAK_ITERATION + + /** + * Titlecases a UTF-8 string and optionally records edits. + * Casing is locale-dependent and context-sensitive. + * The result may be longer or shorter than the original. + * + * Titlecasing uses a break iterator to find the first characters of words + * that are to be titlecased. It titlecases those characters and lowercases + * all others. (This can be modified with options bits.) + * + * @param locale The locale ID. ("" = root locale, nullptr = default locale.) + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT, U_EDITS_NO_RESET, + * U_TITLECASE_NO_LOWERCASE, + * U_TITLECASE_NO_BREAK_ADJUSTMENT, U_TITLECASE_ADJUST_TO_CASED, + * U_TITLECASE_WHOLE_STRING, U_TITLECASE_SENTENCES. + * @param iter A break iterator to find the first characters of words that are to be titlecased. + * It is set to the source string (setUText()) + * and used one or more times for iteration (first() and next()). + * If nullptr, then a word break iterator for the locale is used + * (or something equivalent). + * @param src The original string. + * @param sink A ByteSink to which the result string is written. + * sink.Flush() is called at the end. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Reference to an in/out error code value + * which must not indicate a failure before the function call. + * + * @see ucasemap_utf8ToTitle + * @stable ICU 60 + */ + static void utf8ToTitle( + const char *locale, uint32_t options, BreakIterator *iter, + StringPiece src, ByteSink &sink, Edits *edits, + UErrorCode &errorCode); + +#endif // UCONFIG_NO_BREAK_ITERATION + + /** + * Case-folds a UTF-8 string and optionally records edits. + * + * Case folding is locale-independent and not context-sensitive, + * but there is an option for whether to include or exclude mappings for dotted I + * and dotless i that are marked with 'T' in CaseFolding.txt. + * + * The result may be longer or shorter than the original. + * + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. + * @param src The original string. + * @param sink A ByteSink to which the result string is written. + * sink.Flush() is called at the end. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Reference to an in/out error code value + * which must not indicate a failure before the function call. + * + * @see ucasemap_utf8FoldCase + * @stable ICU 60 + */ + static void utf8Fold( + uint32_t options, + StringPiece src, ByteSink &sink, Edits *edits, + UErrorCode &errorCode); + + /** + * Lowercases a UTF-8 string and optionally records edits. + * Casing is locale-dependent and context-sensitive. + * The result may be longer or shorter than the original. + * The source string and the destination buffer must not overlap. + * + * @param locale The locale ID. ("" = root locale, nullptr = default locale.) + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. + * @param src The original string. + * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. + * @param dest A buffer for the result string. The result will be NUL-terminated if + * the buffer is large enough. + * The contents is undefined in case of failure. + * @param destCapacity The size of the buffer (number of bytes). If it is 0, then + * dest may be nullptr and the function will only return the length of the result + * without writing any of the result string. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Reference to an in/out error code value + * which must not indicate a failure before the function call. + * @return The length of the result string, if successful. + * When the result would be longer than destCapacity, + * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. + * + * @see ucasemap_utf8ToLower + * @stable ICU 59 + */ + static int32_t utf8ToLower( + const char *locale, uint32_t options, + const char *src, int32_t srcLength, + char *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode); + + /** + * Uppercases a UTF-8 string and optionally records edits. + * Casing is locale-dependent and context-sensitive. + * The result may be longer or shorter than the original. + * The source string and the destination buffer must not overlap. + * + * @param locale The locale ID. ("" = root locale, nullptr = default locale.) + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. + * @param src The original string. + * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. + * @param dest A buffer for the result string. The result will be NUL-terminated if + * the buffer is large enough. + * The contents is undefined in case of failure. + * @param destCapacity The size of the buffer (number of bytes). If it is 0, then + * dest may be nullptr and the function will only return the length of the result + * without writing any of the result string. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Reference to an in/out error code value + * which must not indicate a failure before the function call. + * @return The length of the result string, if successful. + * When the result would be longer than destCapacity, + * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. + * + * @see ucasemap_utf8ToUpper + * @stable ICU 59 + */ + static int32_t utf8ToUpper( + const char *locale, uint32_t options, + const char *src, int32_t srcLength, + char *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode); + +#if !UCONFIG_NO_BREAK_ITERATION + + /** + * Titlecases a UTF-8 string and optionally records edits. + * Casing is locale-dependent and context-sensitive. + * The result may be longer or shorter than the original. + * The source string and the destination buffer must not overlap. + * + * Titlecasing uses a break iterator to find the first characters of words + * that are to be titlecased. It titlecases those characters and lowercases + * all others. (This can be modified with options bits.) + * + * @param locale The locale ID. ("" = root locale, nullptr = default locale.) + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT, U_EDITS_NO_RESET, + * U_TITLECASE_NO_LOWERCASE, + * U_TITLECASE_NO_BREAK_ADJUSTMENT, U_TITLECASE_ADJUST_TO_CASED, + * U_TITLECASE_WHOLE_STRING, U_TITLECASE_SENTENCES. + * @param iter A break iterator to find the first characters of words that are to be titlecased. + * It is set to the source string (setUText()) + * and used one or more times for iteration (first() and next()). + * If nullptr, then a word break iterator for the locale is used + * (or something equivalent). + * @param src The original string. + * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. + * @param dest A buffer for the result string. The result will be NUL-terminated if + * the buffer is large enough. + * The contents is undefined in case of failure. + * @param destCapacity The size of the buffer (number of bytes). If it is 0, then + * dest may be nullptr and the function will only return the length of the result + * without writing any of the result string. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Reference to an in/out error code value + * which must not indicate a failure before the function call. + * @return The length of the result string, if successful. + * When the result would be longer than destCapacity, + * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. + * + * @see ucasemap_utf8ToTitle + * @stable ICU 59 + */ + static int32_t utf8ToTitle( + const char *locale, uint32_t options, BreakIterator *iter, + const char *src, int32_t srcLength, + char *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode); + +#endif // UCONFIG_NO_BREAK_ITERATION + + /** + * Case-folds a UTF-8 string and optionally records edits. + * + * Case folding is locale-independent and not context-sensitive, + * but there is an option for whether to include or exclude mappings for dotted I + * and dotless i that are marked with 'T' in CaseFolding.txt. + * + * The result may be longer or shorter than the original. + * The source string and the destination buffer must not overlap. + * + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT, U_EDITS_NO_RESET, + * U_FOLD_CASE_DEFAULT, U_FOLD_CASE_EXCLUDE_SPECIAL_I. + * @param src The original string. + * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. + * @param dest A buffer for the result string. The result will be NUL-terminated if + * the buffer is large enough. + * The contents is undefined in case of failure. + * @param destCapacity The size of the buffer (number of bytes). If it is 0, then + * dest may be nullptr and the function will only return the length of the result + * without writing any of the result string. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Reference to an in/out error code value + * which must not indicate a failure before the function call. + * @return The length of the result string, if successful. + * When the result would be longer than destCapacity, + * the full length is returned and a U_BUFFER_OVERFLOW_ERROR is set. + * + * @see ucasemap_utf8FoldCase + * @stable ICU 59 + */ + static int32_t utf8Fold( + uint32_t options, + const char *src, int32_t srcLength, + char *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode); + +private: + CaseMap() = delete; + CaseMap(const CaseMap &other) = delete; + CaseMap &operator=(const CaseMap &other) = delete; +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __CASEMAP_H__ diff --git a/deps/icu-small/source/common/unicode/char16ptr.h b/deps/icu-small/source/common/unicode/char16ptr.h index c8a9ae6c35d646..b0a26c0f4b32a9 100644 --- a/deps/icu-small/source/common/unicode/char16ptr.h +++ b/deps/icu-small/source/common/unicode/char16ptr.h @@ -1,313 +1,313 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// char16ptr.h -// created: 2017feb28 Markus W. Scherer - -#ifndef __CHAR16PTR_H__ -#define __CHAR16PTR_H__ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include - -/** - * \file - * \brief C++ API: char16_t pointer wrappers with - * implicit conversion from bit-compatible raw pointer types. - * Also conversion functions from char16_t * to UChar * and OldUChar *. - */ - -U_NAMESPACE_BEGIN - -/** - * \def U_ALIASING_BARRIER - * Barrier for pointer anti-aliasing optimizations even across function boundaries. - * @internal - */ -#ifdef U_ALIASING_BARRIER - // Use the predefined value. -#elif (defined(__clang__) || defined(__GNUC__)) && U_PLATFORM != U_PF_BROWSER_NATIVE_CLIENT -# define U_ALIASING_BARRIER(ptr) asm volatile("" : : "rm"(ptr) : "memory") -#elif defined(U_IN_DOXYGEN) -# define U_ALIASING_BARRIER(ptr) -#endif - -/** - * char16_t * wrapper with implicit conversion from distinct but bit-compatible pointer types. - * @stable ICU 59 - */ -class U_COMMON_API Char16Ptr U_FINAL { -public: - /** - * Copies the pointer. - * @param p pointer - * @stable ICU 59 - */ - inline Char16Ptr(char16_t *p); -#if !U_CHAR16_IS_TYPEDEF - /** - * Converts the pointer to char16_t *. - * @param p pointer to be converted - * @stable ICU 59 - */ - inline Char16Ptr(uint16_t *p); -#endif -#if U_SIZEOF_WCHAR_T==2 || defined(U_IN_DOXYGEN) - /** - * Converts the pointer to char16_t *. - * (Only defined if U_SIZEOF_WCHAR_T==2.) - * @param p pointer to be converted - * @stable ICU 59 - */ - inline Char16Ptr(wchar_t *p); -#endif - /** - * nullptr constructor. - * @param p nullptr - * @stable ICU 59 - */ - inline Char16Ptr(std::nullptr_t p); - /** - * Destructor. - * @stable ICU 59 - */ - inline ~Char16Ptr(); - - /** - * Pointer access. - * @return the wrapped pointer - * @stable ICU 59 - */ - inline char16_t *get() const; - /** - * char16_t pointer access via type conversion (e.g., static_cast). - * @return the wrapped pointer - * @stable ICU 59 - */ - inline operator char16_t *() const { return get(); } - -private: - Char16Ptr() = delete; - -#ifdef U_ALIASING_BARRIER - template static char16_t *cast(T *t) { - U_ALIASING_BARRIER(t); - return reinterpret_cast(t); - } - - char16_t *p_; -#else - union { - char16_t *cp; - uint16_t *up; - wchar_t *wp; - } u_; -#endif -}; - -/// \cond -#ifdef U_ALIASING_BARRIER - -Char16Ptr::Char16Ptr(char16_t *p) : p_(p) {} -#if !U_CHAR16_IS_TYPEDEF -Char16Ptr::Char16Ptr(uint16_t *p) : p_(cast(p)) {} -#endif -#if U_SIZEOF_WCHAR_T==2 -Char16Ptr::Char16Ptr(wchar_t *p) : p_(cast(p)) {} -#endif -Char16Ptr::Char16Ptr(std::nullptr_t p) : p_(p) {} -Char16Ptr::~Char16Ptr() { - U_ALIASING_BARRIER(p_); -} - -char16_t *Char16Ptr::get() const { return p_; } - -#else - -Char16Ptr::Char16Ptr(char16_t *p) { u_.cp = p; } -#if !U_CHAR16_IS_TYPEDEF -Char16Ptr::Char16Ptr(uint16_t *p) { u_.up = p; } -#endif -#if U_SIZEOF_WCHAR_T==2 -Char16Ptr::Char16Ptr(wchar_t *p) { u_.wp = p; } -#endif -Char16Ptr::Char16Ptr(std::nullptr_t p) { u_.cp = p; } -Char16Ptr::~Char16Ptr() {} - -char16_t *Char16Ptr::get() const { return u_.cp; } - -#endif -/// \endcond - -/** - * const char16_t * wrapper with implicit conversion from distinct but bit-compatible pointer types. - * @stable ICU 59 - */ -class U_COMMON_API ConstChar16Ptr U_FINAL { -public: - /** - * Copies the pointer. - * @param p pointer - * @stable ICU 59 - */ - inline ConstChar16Ptr(const char16_t *p); -#if !U_CHAR16_IS_TYPEDEF - /** - * Converts the pointer to char16_t *. - * @param p pointer to be converted - * @stable ICU 59 - */ - inline ConstChar16Ptr(const uint16_t *p); -#endif -#if U_SIZEOF_WCHAR_T==2 || defined(U_IN_DOXYGEN) - /** - * Converts the pointer to char16_t *. - * (Only defined if U_SIZEOF_WCHAR_T==2.) - * @param p pointer to be converted - * @stable ICU 59 - */ - inline ConstChar16Ptr(const wchar_t *p); -#endif - /** - * nullptr constructor. - * @param p nullptr - * @stable ICU 59 - */ - inline ConstChar16Ptr(const std::nullptr_t p); - - /** - * Destructor. - * @stable ICU 59 - */ - inline ~ConstChar16Ptr(); - - /** - * Pointer access. - * @return the wrapped pointer - * @stable ICU 59 - */ - inline const char16_t *get() const; - /** - * char16_t pointer access via type conversion (e.g., static_cast). - * @return the wrapped pointer - * @stable ICU 59 - */ - inline operator const char16_t *() const { return get(); } - -private: - ConstChar16Ptr() = delete; - -#ifdef U_ALIASING_BARRIER - template static const char16_t *cast(const T *t) { - U_ALIASING_BARRIER(t); - return reinterpret_cast(t); - } - - const char16_t *p_; -#else - union { - const char16_t *cp; - const uint16_t *up; - const wchar_t *wp; - } u_; -#endif -}; - -/// \cond -#ifdef U_ALIASING_BARRIER - -ConstChar16Ptr::ConstChar16Ptr(const char16_t *p) : p_(p) {} -#if !U_CHAR16_IS_TYPEDEF -ConstChar16Ptr::ConstChar16Ptr(const uint16_t *p) : p_(cast(p)) {} -#endif -#if U_SIZEOF_WCHAR_T==2 -ConstChar16Ptr::ConstChar16Ptr(const wchar_t *p) : p_(cast(p)) {} -#endif -ConstChar16Ptr::ConstChar16Ptr(const std::nullptr_t p) : p_(p) {} -ConstChar16Ptr::~ConstChar16Ptr() { - U_ALIASING_BARRIER(p_); -} - -const char16_t *ConstChar16Ptr::get() const { return p_; } - -#else - -ConstChar16Ptr::ConstChar16Ptr(const char16_t *p) { u_.cp = p; } -#if !U_CHAR16_IS_TYPEDEF -ConstChar16Ptr::ConstChar16Ptr(const uint16_t *p) { u_.up = p; } -#endif -#if U_SIZEOF_WCHAR_T==2 -ConstChar16Ptr::ConstChar16Ptr(const wchar_t *p) { u_.wp = p; } -#endif -ConstChar16Ptr::ConstChar16Ptr(const std::nullptr_t p) { u_.cp = p; } -ConstChar16Ptr::~ConstChar16Ptr() {} - -const char16_t *ConstChar16Ptr::get() const { return u_.cp; } - -#endif -/// \endcond - -/** - * Converts from const char16_t * to const UChar *. - * Includes an aliasing barrier if available. - * @param p pointer - * @return p as const UChar * - * @stable ICU 59 - */ -inline const UChar *toUCharPtr(const char16_t *p) { -#ifdef U_ALIASING_BARRIER - U_ALIASING_BARRIER(p); -#endif - return reinterpret_cast(p); -} - -/** - * Converts from char16_t * to UChar *. - * Includes an aliasing barrier if available. - * @param p pointer - * @return p as UChar * - * @stable ICU 59 - */ -inline UChar *toUCharPtr(char16_t *p) { -#ifdef U_ALIASING_BARRIER - U_ALIASING_BARRIER(p); -#endif - return reinterpret_cast(p); -} - -/** - * Converts from const char16_t * to const OldUChar *. - * Includes an aliasing barrier if available. - * @param p pointer - * @return p as const OldUChar * - * @stable ICU 59 - */ -inline const OldUChar *toOldUCharPtr(const char16_t *p) { -#ifdef U_ALIASING_BARRIER - U_ALIASING_BARRIER(p); -#endif - return reinterpret_cast(p); -} - -/** - * Converts from char16_t * to OldUChar *. - * Includes an aliasing barrier if available. - * @param p pointer - * @return p as OldUChar * - * @stable ICU 59 - */ -inline OldUChar *toOldUCharPtr(char16_t *p) { -#ifdef U_ALIASING_BARRIER - U_ALIASING_BARRIER(p); -#endif - return reinterpret_cast(p); -} - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __CHAR16PTR_H__ +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// char16ptr.h +// created: 2017feb28 Markus W. Scherer + +#ifndef __CHAR16PTR_H__ +#define __CHAR16PTR_H__ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include + +/** + * \file + * \brief C++ API: char16_t pointer wrappers with + * implicit conversion from bit-compatible raw pointer types. + * Also conversion functions from char16_t * to UChar * and OldUChar *. + */ + +U_NAMESPACE_BEGIN + +/** + * \def U_ALIASING_BARRIER + * Barrier for pointer anti-aliasing optimizations even across function boundaries. + * @internal + */ +#ifdef U_ALIASING_BARRIER + // Use the predefined value. +#elif (defined(__clang__) || defined(__GNUC__)) && U_PLATFORM != U_PF_BROWSER_NATIVE_CLIENT +# define U_ALIASING_BARRIER(ptr) asm volatile("" : : "rm"(ptr) : "memory") +#elif defined(U_IN_DOXYGEN) +# define U_ALIASING_BARRIER(ptr) +#endif + +/** + * char16_t * wrapper with implicit conversion from distinct but bit-compatible pointer types. + * @stable ICU 59 + */ +class U_COMMON_API Char16Ptr final { +public: + /** + * Copies the pointer. + * @param p pointer + * @stable ICU 59 + */ + inline Char16Ptr(char16_t *p); +#if !U_CHAR16_IS_TYPEDEF + /** + * Converts the pointer to char16_t *. + * @param p pointer to be converted + * @stable ICU 59 + */ + inline Char16Ptr(uint16_t *p); +#endif +#if U_SIZEOF_WCHAR_T==2 || defined(U_IN_DOXYGEN) + /** + * Converts the pointer to char16_t *. + * (Only defined if U_SIZEOF_WCHAR_T==2.) + * @param p pointer to be converted + * @stable ICU 59 + */ + inline Char16Ptr(wchar_t *p); +#endif + /** + * nullptr constructor. + * @param p nullptr + * @stable ICU 59 + */ + inline Char16Ptr(std::nullptr_t p); + /** + * Destructor. + * @stable ICU 59 + */ + inline ~Char16Ptr(); + + /** + * Pointer access. + * @return the wrapped pointer + * @stable ICU 59 + */ + inline char16_t *get() const; + /** + * char16_t pointer access via type conversion (e.g., static_cast). + * @return the wrapped pointer + * @stable ICU 59 + */ + inline operator char16_t *() const { return get(); } + +private: + Char16Ptr() = delete; + +#ifdef U_ALIASING_BARRIER + template static char16_t *cast(T *t) { + U_ALIASING_BARRIER(t); + return reinterpret_cast(t); + } + + char16_t *p_; +#else + union { + char16_t *cp; + uint16_t *up; + wchar_t *wp; + } u_; +#endif +}; + +/// \cond +#ifdef U_ALIASING_BARRIER + +Char16Ptr::Char16Ptr(char16_t *p) : p_(p) {} +#if !U_CHAR16_IS_TYPEDEF +Char16Ptr::Char16Ptr(uint16_t *p) : p_(cast(p)) {} +#endif +#if U_SIZEOF_WCHAR_T==2 +Char16Ptr::Char16Ptr(wchar_t *p) : p_(cast(p)) {} +#endif +Char16Ptr::Char16Ptr(std::nullptr_t p) : p_(p) {} +Char16Ptr::~Char16Ptr() { + U_ALIASING_BARRIER(p_); +} + +char16_t *Char16Ptr::get() const { return p_; } + +#else + +Char16Ptr::Char16Ptr(char16_t *p) { u_.cp = p; } +#if !U_CHAR16_IS_TYPEDEF +Char16Ptr::Char16Ptr(uint16_t *p) { u_.up = p; } +#endif +#if U_SIZEOF_WCHAR_T==2 +Char16Ptr::Char16Ptr(wchar_t *p) { u_.wp = p; } +#endif +Char16Ptr::Char16Ptr(std::nullptr_t p) { u_.cp = p; } +Char16Ptr::~Char16Ptr() {} + +char16_t *Char16Ptr::get() const { return u_.cp; } + +#endif +/// \endcond + +/** + * const char16_t * wrapper with implicit conversion from distinct but bit-compatible pointer types. + * @stable ICU 59 + */ +class U_COMMON_API ConstChar16Ptr final { +public: + /** + * Copies the pointer. + * @param p pointer + * @stable ICU 59 + */ + inline ConstChar16Ptr(const char16_t *p); +#if !U_CHAR16_IS_TYPEDEF + /** + * Converts the pointer to char16_t *. + * @param p pointer to be converted + * @stable ICU 59 + */ + inline ConstChar16Ptr(const uint16_t *p); +#endif +#if U_SIZEOF_WCHAR_T==2 || defined(U_IN_DOXYGEN) + /** + * Converts the pointer to char16_t *. + * (Only defined if U_SIZEOF_WCHAR_T==2.) + * @param p pointer to be converted + * @stable ICU 59 + */ + inline ConstChar16Ptr(const wchar_t *p); +#endif + /** + * nullptr constructor. + * @param p nullptr + * @stable ICU 59 + */ + inline ConstChar16Ptr(const std::nullptr_t p); + + /** + * Destructor. + * @stable ICU 59 + */ + inline ~ConstChar16Ptr(); + + /** + * Pointer access. + * @return the wrapped pointer + * @stable ICU 59 + */ + inline const char16_t *get() const; + /** + * char16_t pointer access via type conversion (e.g., static_cast). + * @return the wrapped pointer + * @stable ICU 59 + */ + inline operator const char16_t *() const { return get(); } + +private: + ConstChar16Ptr() = delete; + +#ifdef U_ALIASING_BARRIER + template static const char16_t *cast(const T *t) { + U_ALIASING_BARRIER(t); + return reinterpret_cast(t); + } + + const char16_t *p_; +#else + union { + const char16_t *cp; + const uint16_t *up; + const wchar_t *wp; + } u_; +#endif +}; + +/// \cond +#ifdef U_ALIASING_BARRIER + +ConstChar16Ptr::ConstChar16Ptr(const char16_t *p) : p_(p) {} +#if !U_CHAR16_IS_TYPEDEF +ConstChar16Ptr::ConstChar16Ptr(const uint16_t *p) : p_(cast(p)) {} +#endif +#if U_SIZEOF_WCHAR_T==2 +ConstChar16Ptr::ConstChar16Ptr(const wchar_t *p) : p_(cast(p)) {} +#endif +ConstChar16Ptr::ConstChar16Ptr(const std::nullptr_t p) : p_(p) {} +ConstChar16Ptr::~ConstChar16Ptr() { + U_ALIASING_BARRIER(p_); +} + +const char16_t *ConstChar16Ptr::get() const { return p_; } + +#else + +ConstChar16Ptr::ConstChar16Ptr(const char16_t *p) { u_.cp = p; } +#if !U_CHAR16_IS_TYPEDEF +ConstChar16Ptr::ConstChar16Ptr(const uint16_t *p) { u_.up = p; } +#endif +#if U_SIZEOF_WCHAR_T==2 +ConstChar16Ptr::ConstChar16Ptr(const wchar_t *p) { u_.wp = p; } +#endif +ConstChar16Ptr::ConstChar16Ptr(const std::nullptr_t p) { u_.cp = p; } +ConstChar16Ptr::~ConstChar16Ptr() {} + +const char16_t *ConstChar16Ptr::get() const { return u_.cp; } + +#endif +/// \endcond + +/** + * Converts from const char16_t * to const UChar *. + * Includes an aliasing barrier if available. + * @param p pointer + * @return p as const UChar * + * @stable ICU 59 + */ +inline const UChar *toUCharPtr(const char16_t *p) { +#ifdef U_ALIASING_BARRIER + U_ALIASING_BARRIER(p); +#endif + return reinterpret_cast(p); +} + +/** + * Converts from char16_t * to UChar *. + * Includes an aliasing barrier if available. + * @param p pointer + * @return p as UChar * + * @stable ICU 59 + */ +inline UChar *toUCharPtr(char16_t *p) { +#ifdef U_ALIASING_BARRIER + U_ALIASING_BARRIER(p); +#endif + return reinterpret_cast(p); +} + +/** + * Converts from const char16_t * to const OldUChar *. + * Includes an aliasing barrier if available. + * @param p pointer + * @return p as const OldUChar * + * @stable ICU 59 + */ +inline const OldUChar *toOldUCharPtr(const char16_t *p) { +#ifdef U_ALIASING_BARRIER + U_ALIASING_BARRIER(p); +#endif + return reinterpret_cast(p); +} + +/** + * Converts from char16_t * to OldUChar *. + * Includes an aliasing barrier if available. + * @param p pointer + * @return p as OldUChar * + * @stable ICU 59 + */ +inline OldUChar *toOldUCharPtr(char16_t *p) { +#ifdef U_ALIASING_BARRIER + U_ALIASING_BARRIER(p); +#endif + return reinterpret_cast(p); +} + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __CHAR16PTR_H__ diff --git a/deps/icu-small/source/common/unicode/chariter.h b/deps/icu-small/source/common/unicode/chariter.h index 4f320b90e2c3a4..b348d91a9e7a15 100644 --- a/deps/icu-small/source/common/unicode/chariter.h +++ b/deps/icu-small/source/common/unicode/chariter.h @@ -1,734 +1,734 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************** -* -* Copyright (C) 1997-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************** -*/ - -#ifndef CHARITER_H -#define CHARITER_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/uobject.h" -#include "unicode/unistr.h" -/** - * \file - * \brief C++ API: Character Iterator - */ - -U_NAMESPACE_BEGIN -/** - * Abstract class that defines an API for forward-only iteration - * on text objects. - * This is a minimal interface for iteration without random access - * or backwards iteration. It is especially useful for wrapping - * streams with converters into an object for collation or - * normalization. - * - *

Characters can be accessed in two ways: as code units or as - * code points. - * Unicode code points are 21-bit integers and are the scalar values - * of Unicode characters. ICU uses the type UChar32 for them. - * Unicode code units are the storage units of a given - * Unicode/UCS Transformation Format (a character encoding scheme). - * With UTF-16, all code points can be represented with either one - * or two code units ("surrogates"). - * String storage is typically based on code units, while properties - * of characters are typically determined using code point values. - * Some processes may be designed to work with sequences of code units, - * or it may be known that all characters that are important to an - * algorithm can be represented with single code units. - * Other processes will need to use the code point access functions.

- * - *

ForwardCharacterIterator provides nextPostInc() to access - * a code unit and advance an internal position into the text object, - * similar to a return text[position++].
- * It provides next32PostInc() to access a code point and advance an internal - * position.

- * - *

next32PostInc() assumes that the current position is that of - * the beginning of a code point, i.e., of its first code unit. - * After next32PostInc(), this will be true again. - * In general, access to code units and code points in the same - * iteration loop should not be mixed. In UTF-16, if the current position - * is on a second code unit (Low Surrogate), then only that code unit - * is returned even by next32PostInc().

- * - *

For iteration with either function, there are two ways to - * check for the end of the iteration. When there are no more - * characters in the text object: - *

    - *
  • The hasNext() function returns false.
  • - *
  • nextPostInc() and next32PostInc() return DONE - * when one attempts to read beyond the end of the text object.
  • - *
- * - * Example: - * \code - * void function1(ForwardCharacterIterator &it) { - * UChar32 c; - * while(it.hasNext()) { - * c=it.next32PostInc(); - * // use c - * } - * } - * - * void function1(ForwardCharacterIterator &it) { - * char16_t c; - * while((c=it.nextPostInc())!=ForwardCharacterIterator::DONE) { - * // use c - * } - * } - * \endcode - *

- * - * @stable ICU 2.0 - */ -class U_COMMON_API ForwardCharacterIterator : public UObject { -public: - /** - * Value returned by most of ForwardCharacterIterator's functions - * when the iterator has reached the limits of its iteration. - * @stable ICU 2.0 - */ - enum { DONE = 0xffff }; - - /** - * Destructor. - * @stable ICU 2.0 - */ - virtual ~ForwardCharacterIterator(); - - /** - * Returns true when both iterators refer to the same - * character in the same character-storage object. - * @param that The ForwardCharacterIterator to be compared for equality - * @return true when both iterators refer to the same - * character in the same character-storage object - * @stable ICU 2.0 - */ - virtual bool operator==(const ForwardCharacterIterator& that) const = 0; - - /** - * Returns true when the iterators refer to different - * text-storage objects, or to different characters in the - * same text-storage object. - * @param that The ForwardCharacterIterator to be compared for inequality - * @return true when the iterators refer to different - * text-storage objects, or to different characters in the - * same text-storage object - * @stable ICU 2.0 - */ - inline bool operator!=(const ForwardCharacterIterator& that) const; - - /** - * Generates a hash code for this iterator. - * @return the hash code. - * @stable ICU 2.0 - */ - virtual int32_t hashCode(void) const = 0; - - /** - * Returns a UClassID for this ForwardCharacterIterator ("poor man's - * RTTI").

Despite the fact that this function is public, - * DO NOT CONSIDER IT PART OF CHARACTERITERATOR'S API! - * @return a UClassID for this ForwardCharacterIterator - * @stable ICU 2.0 - */ - virtual UClassID getDynamicClassID(void) const override = 0; - - /** - * Gets the current code unit for returning and advances to the next code unit - * in the iteration range - * (toward endIndex()). If there are - * no more code units to return, returns DONE. - * @return the current code unit. - * @stable ICU 2.0 - */ - virtual char16_t nextPostInc(void) = 0; - - /** - * Gets the current code point for returning and advances to the next code point - * in the iteration range - * (toward endIndex()). If there are - * no more code points to return, returns DONE. - * @return the current code point. - * @stable ICU 2.0 - */ - virtual UChar32 next32PostInc(void) = 0; - - /** - * Returns false if there are no more code units or code points - * at or after the current position in the iteration range. - * This is used with nextPostInc() or next32PostInc() in forward - * iteration. - * @returns false if there are no more code units or code points - * at or after the current position in the iteration range. - * @stable ICU 2.0 - */ - virtual UBool hasNext() = 0; - -protected: - /** Default constructor to be overridden in the implementing class. @stable ICU 2.0*/ - ForwardCharacterIterator(); - - /** Copy constructor to be overridden in the implementing class. @stable ICU 2.0*/ - ForwardCharacterIterator(const ForwardCharacterIterator &other); - - /** - * Assignment operator to be overridden in the implementing class. - * @stable ICU 2.0 - */ - ForwardCharacterIterator &operator=(const ForwardCharacterIterator&) { return *this; } -}; - -/** - * Abstract class that defines an API for iteration - * on text objects. - * This is an interface for forward and backward iteration - * and random access into a text object. - * - *

The API provides backward compatibility to the Java and older ICU - * CharacterIterator classes but extends them significantly: - *

    - *
  1. CharacterIterator is now a subclass of ForwardCharacterIterator.
  2. - *
  3. While the old API functions provided forward iteration with - * "pre-increment" semantics, the new one also provides functions - * with "post-increment" semantics. They are more efficient and should - * be the preferred iterator functions for new implementations. - * The backward iteration always had "pre-decrement" semantics, which - * are efficient.
  4. - *
  5. Just like ForwardCharacterIterator, it provides access to - * both code units and code points. Code point access versions are available - * for the old and the new iteration semantics.
  6. - *
  7. There are new functions for setting and moving the current position - * without returning a character, for efficiency.
  8. - *
- * - * See ForwardCharacterIterator for examples for using the new forward iteration - * functions. For backward iteration, there is also a hasPrevious() function - * that can be used analogously to hasNext(). - * The old functions work as before and are shown below.

- * - *

Examples for some of the new functions:

- * - * Forward iteration with hasNext(): - * \code - * void forward1(CharacterIterator &it) { - * UChar32 c; - * for(it.setToStart(); it.hasNext();) { - * c=it.next32PostInc(); - * // use c - * } - * } - * \endcode - * Forward iteration more similar to loops with the old forward iteration, - * showing a way to convert simple for() loops: - * \code - * void forward2(CharacterIterator &it) { - * char16_t c; - * for(c=it.firstPostInc(); c!=CharacterIterator::DONE; c=it.nextPostInc()) { - * // use c - * } - * } - * \endcode - * Backward iteration with setToEnd() and hasPrevious(): - * \code - * void backward1(CharacterIterator &it) { - * UChar32 c; - * for(it.setToEnd(); it.hasPrevious();) { - * c=it.previous32(); - * // use c - * } - * } - * \endcode - * Backward iteration with a more traditional for() loop: - * \code - * void backward2(CharacterIterator &it) { - * char16_t c; - * for(c=it.last(); c!=CharacterIterator::DONE; c=it.previous()) { - * // use c - * } - * } - * \endcode - * - * Example for random access: - * \code - * void random(CharacterIterator &it) { - * // set to the third code point from the beginning - * it.move32(3, CharacterIterator::kStart); - * // get a code point from here without moving the position - * UChar32 c=it.current32(); - * // get the position - * int32_t pos=it.getIndex(); - * // get the previous code unit - * char16_t u=it.previous(); - * // move back one more code unit - * it.move(-1, CharacterIterator::kCurrent); - * // set the position back to where it was - * // and read the same code point c and move beyond it - * it.setIndex(pos); - * if(c!=it.next32PostInc()) { - * exit(1); // CharacterIterator inconsistent - * } - * } - * \endcode - * - *

Examples, especially for the old API:

- * - * Function processing characters, in this example simple output - *
- * \code
- *  void processChar( char16_t c )
- *  {
- *      cout << " " << c;
- *  }
- * \endcode
- * 
- * Traverse the text from start to finish - *
 
- * \code
- *  void traverseForward(CharacterIterator& iter)
- *  {
- *      for(char16_t c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
- *          processChar(c);
- *      }
- *  }
- * \endcode
- * 
- * Traverse the text backwards, from end to start - *
- * \code
- *  void traverseBackward(CharacterIterator& iter)
- *  {
- *      for(char16_t c = iter.last(); c != CharacterIterator.DONE; c = iter.previous()) {
- *          processChar(c);
- *      }
- *  }
- * \endcode
- * 
- * Traverse both forward and backward from a given position in the text. - * Calls to notBoundary() in this example represents some additional stopping criteria. - *
- * \code
- * void traverseOut(CharacterIterator& iter, int32_t pos)
- * {
- *      char16_t c;
- *      for (c = iter.setIndex(pos);
- *      c != CharacterIterator.DONE && (Unicode::isLetter(c) || Unicode::isDigit(c));
- *          c = iter.next()) {}
- *      int32_t end = iter.getIndex();
- *      for (c = iter.setIndex(pos);
- *          c != CharacterIterator.DONE && (Unicode::isLetter(c) || Unicode::isDigit(c));
- *          c = iter.previous()) {}
- *      int32_t start = iter.getIndex() + 1;
- *  
- *      cout << "start: " << start << " end: " << end << endl;
- *      for (c = iter.setIndex(start); iter.getIndex() < end; c = iter.next() ) {
- *          processChar(c);
- *     }
- *  }
- * \endcode
- * 
- * Creating a StringCharacterIterator and calling the test functions - *
- * \code
- *  void CharacterIterator_Example( void )
- *   {
- *       cout << endl << "===== CharacterIterator_Example: =====" << endl;
- *       UnicodeString text("Ein kleiner Satz.");
- *       StringCharacterIterator iterator(text);
- *       cout << "----- traverseForward: -----------" << endl;
- *       traverseForward( iterator );
- *       cout << endl << endl << "----- traverseBackward: ----------" << endl;
- *       traverseBackward( iterator );
- *       cout << endl << endl << "----- traverseOut: ---------------" << endl;
- *       traverseOut( iterator, 7 );
- *       cout << endl << endl << "-----" << endl;
- *   }
- * \endcode
- * 
- * - * @stable ICU 2.0 - */ -class U_COMMON_API CharacterIterator : public ForwardCharacterIterator { -public: - /** - * Origin enumeration for the move() and move32() functions. - * @stable ICU 2.0 - */ - enum EOrigin { kStart, kCurrent, kEnd }; - - /** - * Destructor. - * @stable ICU 2.0 - */ - virtual ~CharacterIterator(); - - /** - * Returns a pointer to a new CharacterIterator of the same - * concrete class as this one, and referring to the same - * character in the same text-storage object as this one. The - * caller is responsible for deleting the new clone. - * @return a pointer to a new CharacterIterator - * @stable ICU 2.0 - */ - virtual CharacterIterator* clone() const = 0; - - /** - * Sets the iterator to refer to the first code unit in its - * iteration range, and returns that code unit. - * This can be used to begin an iteration with next(). - * @return the first code unit in its iteration range. - * @stable ICU 2.0 - */ - virtual char16_t first(void) = 0; - - /** - * Sets the iterator to refer to the first code unit in its - * iteration range, returns that code unit, and moves the position - * to the second code unit. This is an alternative to setToStart() - * for forward iteration with nextPostInc(). - * @return the first code unit in its iteration range. - * @stable ICU 2.0 - */ - virtual char16_t firstPostInc(void); - - /** - * Sets the iterator to refer to the first code point in its - * iteration range, and returns that code unit, - * This can be used to begin an iteration with next32(). - * Note that an iteration with next32PostInc(), beginning with, - * e.g., setToStart() or firstPostInc(), is more efficient. - * @return the first code point in its iteration range. - * @stable ICU 2.0 - */ - virtual UChar32 first32(void) = 0; - - /** - * Sets the iterator to refer to the first code point in its - * iteration range, returns that code point, and moves the position - * to the second code point. This is an alternative to setToStart() - * for forward iteration with next32PostInc(). - * @return the first code point in its iteration range. - * @stable ICU 2.0 - */ - virtual UChar32 first32PostInc(void); - - /** - * Sets the iterator to refer to the first code unit or code point in its - * iteration range. This can be used to begin a forward - * iteration with nextPostInc() or next32PostInc(). - * @return the start position of the iteration range - * @stable ICU 2.0 - */ - inline int32_t setToStart(); - - /** - * Sets the iterator to refer to the last code unit in its - * iteration range, and returns that code unit. - * This can be used to begin an iteration with previous(). - * @return the last code unit. - * @stable ICU 2.0 - */ - virtual char16_t last(void) = 0; - - /** - * Sets the iterator to refer to the last code point in its - * iteration range, and returns that code unit. - * This can be used to begin an iteration with previous32(). - * @return the last code point. - * @stable ICU 2.0 - */ - virtual UChar32 last32(void) = 0; - - /** - * Sets the iterator to the end of its iteration range, just behind - * the last code unit or code point. This can be used to begin a backward - * iteration with previous() or previous32(). - * @return the end position of the iteration range - * @stable ICU 2.0 - */ - inline int32_t setToEnd(); - - /** - * Sets the iterator to refer to the "position"-th code unit - * in the text-storage object the iterator refers to, and - * returns that code unit. - * @param position the "position"-th code unit in the text-storage object - * @return the "position"-th code unit. - * @stable ICU 2.0 - */ - virtual char16_t setIndex(int32_t position) = 0; - - /** - * Sets the iterator to refer to the beginning of the code point - * that contains the "position"-th code unit - * in the text-storage object the iterator refers to, and - * returns that code point. - * The current position is adjusted to the beginning of the code point - * (its first code unit). - * @param position the "position"-th code unit in the text-storage object - * @return the "position"-th code point. - * @stable ICU 2.0 - */ - virtual UChar32 setIndex32(int32_t position) = 0; - - /** - * Returns the code unit the iterator currently refers to. - * @return the current code unit. - * @stable ICU 2.0 - */ - virtual char16_t current(void) const = 0; - - /** - * Returns the code point the iterator currently refers to. - * @return the current code point. - * @stable ICU 2.0 - */ - virtual UChar32 current32(void) const = 0; - - /** - * Advances to the next code unit in the iteration range - * (toward endIndex()), and returns that code unit. If there are - * no more code units to return, returns DONE. - * @return the next code unit. - * @stable ICU 2.0 - */ - virtual char16_t next(void) = 0; - - /** - * Advances to the next code point in the iteration range - * (toward endIndex()), and returns that code point. If there are - * no more code points to return, returns DONE. - * Note that iteration with "pre-increment" semantics is less - * efficient than iteration with "post-increment" semantics - * that is provided by next32PostInc(). - * @return the next code point. - * @stable ICU 2.0 - */ - virtual UChar32 next32(void) = 0; - - /** - * Advances to the previous code unit in the iteration range - * (toward startIndex()), and returns that code unit. If there are - * no more code units to return, returns DONE. - * @return the previous code unit. - * @stable ICU 2.0 - */ - virtual char16_t previous(void) = 0; - - /** - * Advances to the previous code point in the iteration range - * (toward startIndex()), and returns that code point. If there are - * no more code points to return, returns DONE. - * @return the previous code point. - * @stable ICU 2.0 - */ - virtual UChar32 previous32(void) = 0; - - /** - * Returns false if there are no more code units or code points - * before the current position in the iteration range. - * This is used with previous() or previous32() in backward - * iteration. - * @return false if there are no more code units or code points - * before the current position in the iteration range, return true otherwise. - * @stable ICU 2.0 - */ - virtual UBool hasPrevious() = 0; - - /** - * Returns the numeric index in the underlying text-storage - * object of the character returned by first(). Since it's - * possible to create an iterator that iterates across only - * part of a text-storage object, this number isn't - * necessarily 0. - * @returns the numeric index in the underlying text-storage - * object of the character returned by first(). - * @stable ICU 2.0 - */ - inline int32_t startIndex(void) const; - - /** - * Returns the numeric index in the underlying text-storage - * object of the position immediately BEYOND the character - * returned by last(). - * @return the numeric index in the underlying text-storage - * object of the position immediately BEYOND the character - * returned by last(). - * @stable ICU 2.0 - */ - inline int32_t endIndex(void) const; - - /** - * Returns the numeric index in the underlying text-storage - * object of the character the iterator currently refers to - * (i.e., the character returned by current()). - * @return the numeric index in the text-storage object of - * the character the iterator currently refers to - * @stable ICU 2.0 - */ - inline int32_t getIndex(void) const; - - /** - * Returns the length of the entire text in the underlying - * text-storage object. - * @return the length of the entire text in the text-storage object - * @stable ICU 2.0 - */ - inline int32_t getLength() const; - - /** - * Moves the current position relative to the start or end of the - * iteration range, or relative to the current position itself. - * The movement is expressed in numbers of code units forward - * or backward by specifying a positive or negative delta. - * @param delta the position relative to origin. A positive delta means forward; - * a negative delta means backward. - * @param origin Origin enumeration {kStart, kCurrent, kEnd} - * @return the new position - * @stable ICU 2.0 - */ - virtual int32_t move(int32_t delta, EOrigin origin) = 0; - - /** - * Moves the current position relative to the start or end of the - * iteration range, or relative to the current position itself. - * The movement is expressed in numbers of code points forward - * or backward by specifying a positive or negative delta. - * @param delta the position relative to origin. A positive delta means forward; - * a negative delta means backward. - * @param origin Origin enumeration {kStart, kCurrent, kEnd} - * @return the new position - * @stable ICU 2.0 - */ -#ifdef move32 - // One of the system headers right now is sometimes defining a conflicting macro we don't use -#undef move32 -#endif - virtual int32_t move32(int32_t delta, EOrigin origin) = 0; - - /** - * Copies the text under iteration into the UnicodeString - * referred to by "result". - * @param result Receives a copy of the text under iteration. - * @stable ICU 2.0 - */ - virtual void getText(UnicodeString& result) = 0; - -protected: - /** - * Empty constructor. - * @stable ICU 2.0 - */ - CharacterIterator(); - - /** - * Constructor, just setting the length field in this base class. - * @stable ICU 2.0 - */ - CharacterIterator(int32_t length); - - /** - * Constructor, just setting the length and position fields in this base class. - * @stable ICU 2.0 - */ - CharacterIterator(int32_t length, int32_t position); - - /** - * Constructor, just setting the length, start, end, and position fields in this base class. - * @stable ICU 2.0 - */ - CharacterIterator(int32_t length, int32_t textBegin, int32_t textEnd, int32_t position); - - /** - * Copy constructor. - * - * @param that The CharacterIterator to be copied - * @stable ICU 2.0 - */ - CharacterIterator(const CharacterIterator &that); - - /** - * Assignment operator. Sets this CharacterIterator to have the same behavior, - * as the one passed in. - * @param that The CharacterIterator passed in. - * @return the newly set CharacterIterator. - * @stable ICU 2.0 - */ - CharacterIterator &operator=(const CharacterIterator &that); - - /** - * Base class text length field. - * Necessary this for correct getText() and hashCode(). - * @stable ICU 2.0 - */ - int32_t textLength; - - /** - * Base class field for the current position. - * @stable ICU 2.0 - */ - int32_t pos; - - /** - * Base class field for the start of the iteration range. - * @stable ICU 2.0 - */ - int32_t begin; - - /** - * Base class field for the end of the iteration range. - * @stable ICU 2.0 - */ - int32_t end; -}; - -inline bool -ForwardCharacterIterator::operator!=(const ForwardCharacterIterator& that) const { - return !operator==(that); -} - -inline int32_t -CharacterIterator::setToStart() { - return move(0, kStart); -} - -inline int32_t -CharacterIterator::setToEnd() { - return move(0, kEnd); -} - -inline int32_t -CharacterIterator::startIndex(void) const { - return begin; -} - -inline int32_t -CharacterIterator::endIndex(void) const { - return end; -} - -inline int32_t -CharacterIterator::getIndex(void) const { - return pos; -} - -inline int32_t -CharacterIterator::getLength(void) const { - return textLength; -} - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************** +* +* Copyright (C) 1997-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************** +*/ + +#ifndef CHARITER_H +#define CHARITER_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/uobject.h" +#include "unicode/unistr.h" +/** + * \file + * \brief C++ API: Character Iterator + */ + +U_NAMESPACE_BEGIN +/** + * Abstract class that defines an API for forward-only iteration + * on text objects. + * This is a minimal interface for iteration without random access + * or backwards iteration. It is especially useful for wrapping + * streams with converters into an object for collation or + * normalization. + * + *

Characters can be accessed in two ways: as code units or as + * code points. + * Unicode code points are 21-bit integers and are the scalar values + * of Unicode characters. ICU uses the type UChar32 for them. + * Unicode code units are the storage units of a given + * Unicode/UCS Transformation Format (a character encoding scheme). + * With UTF-16, all code points can be represented with either one + * or two code units ("surrogates"). + * String storage is typically based on code units, while properties + * of characters are typically determined using code point values. + * Some processes may be designed to work with sequences of code units, + * or it may be known that all characters that are important to an + * algorithm can be represented with single code units. + * Other processes will need to use the code point access functions.

+ * + *

ForwardCharacterIterator provides nextPostInc() to access + * a code unit and advance an internal position into the text object, + * similar to a return text[position++].
+ * It provides next32PostInc() to access a code point and advance an internal + * position.

+ * + *

next32PostInc() assumes that the current position is that of + * the beginning of a code point, i.e., of its first code unit. + * After next32PostInc(), this will be true again. + * In general, access to code units and code points in the same + * iteration loop should not be mixed. In UTF-16, if the current position + * is on a second code unit (Low Surrogate), then only that code unit + * is returned even by next32PostInc().

+ * + *

For iteration with either function, there are two ways to + * check for the end of the iteration. When there are no more + * characters in the text object: + *

    + *
  • The hasNext() function returns false.
  • + *
  • nextPostInc() and next32PostInc() return DONE + * when one attempts to read beyond the end of the text object.
  • + *
+ * + * Example: + * \code + * void function1(ForwardCharacterIterator &it) { + * UChar32 c; + * while(it.hasNext()) { + * c=it.next32PostInc(); + * // use c + * } + * } + * + * void function1(ForwardCharacterIterator &it) { + * char16_t c; + * while((c=it.nextPostInc())!=ForwardCharacterIterator::DONE) { + * // use c + * } + * } + * \endcode + *

+ * + * @stable ICU 2.0 + */ +class U_COMMON_API ForwardCharacterIterator : public UObject { +public: + /** + * Value returned by most of ForwardCharacterIterator's functions + * when the iterator has reached the limits of its iteration. + * @stable ICU 2.0 + */ + enum { DONE = 0xffff }; + + /** + * Destructor. + * @stable ICU 2.0 + */ + virtual ~ForwardCharacterIterator(); + + /** + * Returns true when both iterators refer to the same + * character in the same character-storage object. + * @param that The ForwardCharacterIterator to be compared for equality + * @return true when both iterators refer to the same + * character in the same character-storage object + * @stable ICU 2.0 + */ + virtual bool operator==(const ForwardCharacterIterator& that) const = 0; + + /** + * Returns true when the iterators refer to different + * text-storage objects, or to different characters in the + * same text-storage object. + * @param that The ForwardCharacterIterator to be compared for inequality + * @return true when the iterators refer to different + * text-storage objects, or to different characters in the + * same text-storage object + * @stable ICU 2.0 + */ + inline bool operator!=(const ForwardCharacterIterator& that) const; + + /** + * Generates a hash code for this iterator. + * @return the hash code. + * @stable ICU 2.0 + */ + virtual int32_t hashCode(void) const = 0; + + /** + * Returns a UClassID for this ForwardCharacterIterator ("poor man's + * RTTI").

Despite the fact that this function is public, + * DO NOT CONSIDER IT PART OF CHARACTERITERATOR'S API! + * @return a UClassID for this ForwardCharacterIterator + * @stable ICU 2.0 + */ + virtual UClassID getDynamicClassID(void) const override = 0; + + /** + * Gets the current code unit for returning and advances to the next code unit + * in the iteration range + * (toward endIndex()). If there are + * no more code units to return, returns DONE. + * @return the current code unit. + * @stable ICU 2.0 + */ + virtual char16_t nextPostInc(void) = 0; + + /** + * Gets the current code point for returning and advances to the next code point + * in the iteration range + * (toward endIndex()). If there are + * no more code points to return, returns DONE. + * @return the current code point. + * @stable ICU 2.0 + */ + virtual UChar32 next32PostInc(void) = 0; + + /** + * Returns false if there are no more code units or code points + * at or after the current position in the iteration range. + * This is used with nextPostInc() or next32PostInc() in forward + * iteration. + * @returns false if there are no more code units or code points + * at or after the current position in the iteration range. + * @stable ICU 2.0 + */ + virtual UBool hasNext() = 0; + +protected: + /** Default constructor to be overridden in the implementing class. @stable ICU 2.0*/ + ForwardCharacterIterator(); + + /** Copy constructor to be overridden in the implementing class. @stable ICU 2.0*/ + ForwardCharacterIterator(const ForwardCharacterIterator &other); + + /** + * Assignment operator to be overridden in the implementing class. + * @stable ICU 2.0 + */ + ForwardCharacterIterator &operator=(const ForwardCharacterIterator&) { return *this; } +}; + +/** + * Abstract class that defines an API for iteration + * on text objects. + * This is an interface for forward and backward iteration + * and random access into a text object. + * + *

The API provides backward compatibility to the Java and older ICU + * CharacterIterator classes but extends them significantly: + *

    + *
  1. CharacterIterator is now a subclass of ForwardCharacterIterator.
  2. + *
  3. While the old API functions provided forward iteration with + * "pre-increment" semantics, the new one also provides functions + * with "post-increment" semantics. They are more efficient and should + * be the preferred iterator functions for new implementations. + * The backward iteration always had "pre-decrement" semantics, which + * are efficient.
  4. + *
  5. Just like ForwardCharacterIterator, it provides access to + * both code units and code points. Code point access versions are available + * for the old and the new iteration semantics.
  6. + *
  7. There are new functions for setting and moving the current position + * without returning a character, for efficiency.
  8. + *
+ * + * See ForwardCharacterIterator for examples for using the new forward iteration + * functions. For backward iteration, there is also a hasPrevious() function + * that can be used analogously to hasNext(). + * The old functions work as before and are shown below.

+ * + *

Examples for some of the new functions:

+ * + * Forward iteration with hasNext(): + * \code + * void forward1(CharacterIterator &it) { + * UChar32 c; + * for(it.setToStart(); it.hasNext();) { + * c=it.next32PostInc(); + * // use c + * } + * } + * \endcode + * Forward iteration more similar to loops with the old forward iteration, + * showing a way to convert simple for() loops: + * \code + * void forward2(CharacterIterator &it) { + * char16_t c; + * for(c=it.firstPostInc(); c!=CharacterIterator::DONE; c=it.nextPostInc()) { + * // use c + * } + * } + * \endcode + * Backward iteration with setToEnd() and hasPrevious(): + * \code + * void backward1(CharacterIterator &it) { + * UChar32 c; + * for(it.setToEnd(); it.hasPrevious();) { + * c=it.previous32(); + * // use c + * } + * } + * \endcode + * Backward iteration with a more traditional for() loop: + * \code + * void backward2(CharacterIterator &it) { + * char16_t c; + * for(c=it.last(); c!=CharacterIterator::DONE; c=it.previous()) { + * // use c + * } + * } + * \endcode + * + * Example for random access: + * \code + * void random(CharacterIterator &it) { + * // set to the third code point from the beginning + * it.move32(3, CharacterIterator::kStart); + * // get a code point from here without moving the position + * UChar32 c=it.current32(); + * // get the position + * int32_t pos=it.getIndex(); + * // get the previous code unit + * char16_t u=it.previous(); + * // move back one more code unit + * it.move(-1, CharacterIterator::kCurrent); + * // set the position back to where it was + * // and read the same code point c and move beyond it + * it.setIndex(pos); + * if(c!=it.next32PostInc()) { + * exit(1); // CharacterIterator inconsistent + * } + * } + * \endcode + * + *

Examples, especially for the old API:

+ * + * Function processing characters, in this example simple output + *
+ * \code
+ *  void processChar( char16_t c )
+ *  {
+ *      cout << " " << c;
+ *  }
+ * \endcode
+ * 
+ * Traverse the text from start to finish + *
 
+ * \code
+ *  void traverseForward(CharacterIterator& iter)
+ *  {
+ *      for(char16_t c = iter.first(); c != CharacterIterator::DONE; c = iter.next()) {
+ *          processChar(c);
+ *      }
+ *  }
+ * \endcode
+ * 
+ * Traverse the text backwards, from end to start + *
+ * \code
+ *  void traverseBackward(CharacterIterator& iter)
+ *  {
+ *      for(char16_t c = iter.last(); c != CharacterIterator::DONE; c = iter.previous()) {
+ *          processChar(c);
+ *      }
+ *  }
+ * \endcode
+ * 
+ * Traverse both forward and backward from a given position in the text. + * Calls to notBoundary() in this example represents some additional stopping criteria. + *
+ * \code
+ * void traverseOut(CharacterIterator& iter, int32_t pos)
+ * {
+ *      char16_t c;
+ *      for (c = iter.setIndex(pos);
+ *      c != CharacterIterator::DONE && (Unicode::isLetter(c) || Unicode::isDigit(c));
+ *          c = iter.next()) {}
+ *      int32_t end = iter.getIndex();
+ *      for (c = iter.setIndex(pos);
+ *          c != CharacterIterator::DONE && (Unicode::isLetter(c) || Unicode::isDigit(c));
+ *          c = iter.previous()) {}
+ *      int32_t start = iter.getIndex() + 1;
+ *  
+ *      cout << "start: " << start << " end: " << end << endl;
+ *      for (c = iter.setIndex(start); iter.getIndex() < end; c = iter.next() ) {
+ *          processChar(c);
+ *     }
+ *  }
+ * \endcode
+ * 
+ * Creating a StringCharacterIterator and calling the test functions + *
+ * \code
+ *  void CharacterIterator_Example( void )
+ *   {
+ *       cout << endl << "===== CharacterIterator_Example: =====" << endl;
+ *       UnicodeString text("Ein kleiner Satz.");
+ *       StringCharacterIterator iterator(text);
+ *       cout << "----- traverseForward: -----------" << endl;
+ *       traverseForward( iterator );
+ *       cout << endl << endl << "----- traverseBackward: ----------" << endl;
+ *       traverseBackward( iterator );
+ *       cout << endl << endl << "----- traverseOut: ---------------" << endl;
+ *       traverseOut( iterator, 7 );
+ *       cout << endl << endl << "-----" << endl;
+ *   }
+ * \endcode
+ * 
+ * + * @stable ICU 2.0 + */ +class U_COMMON_API CharacterIterator : public ForwardCharacterIterator { +public: + /** + * Origin enumeration for the move() and move32() functions. + * @stable ICU 2.0 + */ + enum EOrigin { kStart, kCurrent, kEnd }; + + /** + * Destructor. + * @stable ICU 2.0 + */ + virtual ~CharacterIterator(); + + /** + * Returns a pointer to a new CharacterIterator of the same + * concrete class as this one, and referring to the same + * character in the same text-storage object as this one. The + * caller is responsible for deleting the new clone. + * @return a pointer to a new CharacterIterator + * @stable ICU 2.0 + */ + virtual CharacterIterator* clone() const = 0; + + /** + * Sets the iterator to refer to the first code unit in its + * iteration range, and returns that code unit. + * This can be used to begin an iteration with next(). + * @return the first code unit in its iteration range. + * @stable ICU 2.0 + */ + virtual char16_t first(void) = 0; + + /** + * Sets the iterator to refer to the first code unit in its + * iteration range, returns that code unit, and moves the position + * to the second code unit. This is an alternative to setToStart() + * for forward iteration with nextPostInc(). + * @return the first code unit in its iteration range. + * @stable ICU 2.0 + */ + virtual char16_t firstPostInc(void); + + /** + * Sets the iterator to refer to the first code point in its + * iteration range, and returns that code unit, + * This can be used to begin an iteration with next32(). + * Note that an iteration with next32PostInc(), beginning with, + * e.g., setToStart() or firstPostInc(), is more efficient. + * @return the first code point in its iteration range. + * @stable ICU 2.0 + */ + virtual UChar32 first32(void) = 0; + + /** + * Sets the iterator to refer to the first code point in its + * iteration range, returns that code point, and moves the position + * to the second code point. This is an alternative to setToStart() + * for forward iteration with next32PostInc(). + * @return the first code point in its iteration range. + * @stable ICU 2.0 + */ + virtual UChar32 first32PostInc(void); + + /** + * Sets the iterator to refer to the first code unit or code point in its + * iteration range. This can be used to begin a forward + * iteration with nextPostInc() or next32PostInc(). + * @return the start position of the iteration range + * @stable ICU 2.0 + */ + inline int32_t setToStart(); + + /** + * Sets the iterator to refer to the last code unit in its + * iteration range, and returns that code unit. + * This can be used to begin an iteration with previous(). + * @return the last code unit. + * @stable ICU 2.0 + */ + virtual char16_t last(void) = 0; + + /** + * Sets the iterator to refer to the last code point in its + * iteration range, and returns that code unit. + * This can be used to begin an iteration with previous32(). + * @return the last code point. + * @stable ICU 2.0 + */ + virtual UChar32 last32(void) = 0; + + /** + * Sets the iterator to the end of its iteration range, just behind + * the last code unit or code point. This can be used to begin a backward + * iteration with previous() or previous32(). + * @return the end position of the iteration range + * @stable ICU 2.0 + */ + inline int32_t setToEnd(); + + /** + * Sets the iterator to refer to the "position"-th code unit + * in the text-storage object the iterator refers to, and + * returns that code unit. + * @param position the "position"-th code unit in the text-storage object + * @return the "position"-th code unit. + * @stable ICU 2.0 + */ + virtual char16_t setIndex(int32_t position) = 0; + + /** + * Sets the iterator to refer to the beginning of the code point + * that contains the "position"-th code unit + * in the text-storage object the iterator refers to, and + * returns that code point. + * The current position is adjusted to the beginning of the code point + * (its first code unit). + * @param position the "position"-th code unit in the text-storage object + * @return the "position"-th code point. + * @stable ICU 2.0 + */ + virtual UChar32 setIndex32(int32_t position) = 0; + + /** + * Returns the code unit the iterator currently refers to. + * @return the current code unit. + * @stable ICU 2.0 + */ + virtual char16_t current(void) const = 0; + + /** + * Returns the code point the iterator currently refers to. + * @return the current code point. + * @stable ICU 2.0 + */ + virtual UChar32 current32(void) const = 0; + + /** + * Advances to the next code unit in the iteration range + * (toward endIndex()), and returns that code unit. If there are + * no more code units to return, returns DONE. + * @return the next code unit. + * @stable ICU 2.0 + */ + virtual char16_t next(void) = 0; + + /** + * Advances to the next code point in the iteration range + * (toward endIndex()), and returns that code point. If there are + * no more code points to return, returns DONE. + * Note that iteration with "pre-increment" semantics is less + * efficient than iteration with "post-increment" semantics + * that is provided by next32PostInc(). + * @return the next code point. + * @stable ICU 2.0 + */ + virtual UChar32 next32(void) = 0; + + /** + * Advances to the previous code unit in the iteration range + * (toward startIndex()), and returns that code unit. If there are + * no more code units to return, returns DONE. + * @return the previous code unit. + * @stable ICU 2.0 + */ + virtual char16_t previous(void) = 0; + + /** + * Advances to the previous code point in the iteration range + * (toward startIndex()), and returns that code point. If there are + * no more code points to return, returns DONE. + * @return the previous code point. + * @stable ICU 2.0 + */ + virtual UChar32 previous32(void) = 0; + + /** + * Returns false if there are no more code units or code points + * before the current position in the iteration range. + * This is used with previous() or previous32() in backward + * iteration. + * @return false if there are no more code units or code points + * before the current position in the iteration range, return true otherwise. + * @stable ICU 2.0 + */ + virtual UBool hasPrevious() = 0; + + /** + * Returns the numeric index in the underlying text-storage + * object of the character returned by first(). Since it's + * possible to create an iterator that iterates across only + * part of a text-storage object, this number isn't + * necessarily 0. + * @returns the numeric index in the underlying text-storage + * object of the character returned by first(). + * @stable ICU 2.0 + */ + inline int32_t startIndex(void) const; + + /** + * Returns the numeric index in the underlying text-storage + * object of the position immediately BEYOND the character + * returned by last(). + * @return the numeric index in the underlying text-storage + * object of the position immediately BEYOND the character + * returned by last(). + * @stable ICU 2.0 + */ + inline int32_t endIndex(void) const; + + /** + * Returns the numeric index in the underlying text-storage + * object of the character the iterator currently refers to + * (i.e., the character returned by current()). + * @return the numeric index in the text-storage object of + * the character the iterator currently refers to + * @stable ICU 2.0 + */ + inline int32_t getIndex(void) const; + + /** + * Returns the length of the entire text in the underlying + * text-storage object. + * @return the length of the entire text in the text-storage object + * @stable ICU 2.0 + */ + inline int32_t getLength() const; + + /** + * Moves the current position relative to the start or end of the + * iteration range, or relative to the current position itself. + * The movement is expressed in numbers of code units forward + * or backward by specifying a positive or negative delta. + * @param delta the position relative to origin. A positive delta means forward; + * a negative delta means backward. + * @param origin Origin enumeration {kStart, kCurrent, kEnd} + * @return the new position + * @stable ICU 2.0 + */ + virtual int32_t move(int32_t delta, EOrigin origin) = 0; + + /** + * Moves the current position relative to the start or end of the + * iteration range, or relative to the current position itself. + * The movement is expressed in numbers of code points forward + * or backward by specifying a positive or negative delta. + * @param delta the position relative to origin. A positive delta means forward; + * a negative delta means backward. + * @param origin Origin enumeration {kStart, kCurrent, kEnd} + * @return the new position + * @stable ICU 2.0 + */ +#ifdef move32 + // One of the system headers right now is sometimes defining a conflicting macro we don't use +#undef move32 +#endif + virtual int32_t move32(int32_t delta, EOrigin origin) = 0; + + /** + * Copies the text under iteration into the UnicodeString + * referred to by "result". + * @param result Receives a copy of the text under iteration. + * @stable ICU 2.0 + */ + virtual void getText(UnicodeString& result) = 0; + +protected: + /** + * Empty constructor. + * @stable ICU 2.0 + */ + CharacterIterator(); + + /** + * Constructor, just setting the length field in this base class. + * @stable ICU 2.0 + */ + CharacterIterator(int32_t length); + + /** + * Constructor, just setting the length and position fields in this base class. + * @stable ICU 2.0 + */ + CharacterIterator(int32_t length, int32_t position); + + /** + * Constructor, just setting the length, start, end, and position fields in this base class. + * @stable ICU 2.0 + */ + CharacterIterator(int32_t length, int32_t textBegin, int32_t textEnd, int32_t position); + + /** + * Copy constructor. + * + * @param that The CharacterIterator to be copied + * @stable ICU 2.0 + */ + CharacterIterator(const CharacterIterator &that); + + /** + * Assignment operator. Sets this CharacterIterator to have the same behavior, + * as the one passed in. + * @param that The CharacterIterator passed in. + * @return the newly set CharacterIterator. + * @stable ICU 2.0 + */ + CharacterIterator &operator=(const CharacterIterator &that); + + /** + * Base class text length field. + * Necessary this for correct getText() and hashCode(). + * @stable ICU 2.0 + */ + int32_t textLength; + + /** + * Base class field for the current position. + * @stable ICU 2.0 + */ + int32_t pos; + + /** + * Base class field for the start of the iteration range. + * @stable ICU 2.0 + */ + int32_t begin; + + /** + * Base class field for the end of the iteration range. + * @stable ICU 2.0 + */ + int32_t end; +}; + +inline bool +ForwardCharacterIterator::operator!=(const ForwardCharacterIterator& that) const { + return !operator==(that); +} + +inline int32_t +CharacterIterator::setToStart() { + return move(0, kStart); +} + +inline int32_t +CharacterIterator::setToEnd() { + return move(0, kEnd); +} + +inline int32_t +CharacterIterator::startIndex(void) const { + return begin; +} + +inline int32_t +CharacterIterator::endIndex(void) const { + return end; +} + +inline int32_t +CharacterIterator::getIndex(void) const { + return pos; +} + +inline int32_t +CharacterIterator::getLength(void) const { + return textLength; +} + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/dbbi.h b/deps/icu-small/source/common/unicode/dbbi.h index 3de9cc381408c5..808ee1ebecec5c 100644 --- a/deps/icu-small/source/common/unicode/dbbi.h +++ b/deps/icu-small/source/common/unicode/dbbi.h @@ -1,48 +1,48 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2006,2013 IBM Corp. All rights reserved. -********************************************************************** -* Date Name Description -* 12/1/99 rgillam Complete port from Java. -* 01/13/2000 helena Added UErrorCode to ctors. -********************************************************************** -*/ - -#ifndef DBBI_H -#define DBBI_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/rbbi.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -/** - * \file - * \brief C++ API: Dictionary Based Break Iterator - */ - -U_NAMESPACE_BEGIN - -#ifndef U_HIDE_DEPRECATED_API -/** - * An obsolete subclass of RuleBasedBreakIterator. Handling of dictionary- - * based break iteration has been folded into the base class. This class - * is deprecated as of ICU 3.6. - * @deprecated ICU 3.6 - */ -typedef RuleBasedBreakIterator DictionaryBasedBreakIterator; - -#endif /* U_HIDE_DEPRECATED_API */ - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2006,2013 IBM Corp. All rights reserved. +********************************************************************** +* Date Name Description +* 12/1/99 rgillam Complete port from Java. +* 01/13/2000 helena Added UErrorCode to ctors. +********************************************************************** +*/ + +#ifndef DBBI_H +#define DBBI_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/rbbi.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +/** + * \file + * \brief C++ API: Dictionary Based Break Iterator + */ + +U_NAMESPACE_BEGIN + +#ifndef U_HIDE_DEPRECATED_API +/** + * An obsolete subclass of RuleBasedBreakIterator. Handling of dictionary- + * based break iteration has been folded into the base class. This class + * is deprecated as of ICU 3.6. + * @deprecated ICU 3.6 + */ +typedef RuleBasedBreakIterator DictionaryBasedBreakIterator; + +#endif /* U_HIDE_DEPRECATED_API */ + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/docmain.h b/deps/icu-small/source/common/unicode/docmain.h index 4e29e283cdea4b..b72cd58c9b832b 100644 --- a/deps/icu-small/source/common/unicode/docmain.h +++ b/deps/icu-small/source/common/unicode/docmain.h @@ -1,237 +1,237 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/******************************************************************** - * COPYRIGHT: - * Copyright (c) 1997-2012, International Business Machines Corporation and - * others. All Rights Reserved. - * - * FILE NAME: DOCMAIN.h - * - * Date Name Description - * 12/11/2000 Ram Creation. - */ - -/** - * \file - * \brief (Non API- contains Doxygen definitions) - * - * This file contains documentation for Doxygen and does not have - * any significance with respect to C or C++ API - */ - -/*! \mainpage - * - * \section API API Reference Usage - * - *

C++ Programmers:

- *

Use Class Hierarchy or Alphabetical List - * or Compound List - * to find the class you are interested in. For example, to find BreakIterator, - * you can go to the Alphabetical List, then click on - * "BreakIterator". Once you are at the class, you will find an inheritance - * chart, a list of the public members, a detailed description of the class, - * then detailed member descriptions.

- * - *

C Programmers:

- *

Use Module List or File Members - * to find a list of all the functions and constants. - * For example, to find BreakIterator functions you would click on - * File List, - * then find "ubrk.h" and click on it. You will find descriptions of Defines, - * Typedefs, Enumerations, and Functions, with detailed descriptions below. - * If you want to find a specific function, such as ubrk_next(), then click - * first on File Members, then use your browser - * Find dialog to search for "ubrk_next()".

- * - * - *

API References for Previous Releases

- *

The API References for each release of ICU are also available as - * a zip file from the ICU - * download page.

- * - *
- * - *

Architecture (User's Guide)

- * - * - *
- *\htmlonly

Module List

\endhtmlonly - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Module NameCC++
Basic Types and Constantsutypes.hutypes.h
Strings and Character Iterationustring.h, utf8.h, utf16.h, icu::StringPiece, UText, UCharIterator, icu::ByteSinkicu::UnicodeString, icu::CharacterIterator, icu::Appendable, icu::StringPiece,icu::ByteSink
Unicode Character
Properties and Names
uchar.h, uscript.hC API
Sets of Unicode Code Points and Stringsuset.hicu::UnicodeSet
Maps from Unicode Code Points to Integer Valuesucptrie.h, umutablecptrie.hC API
Maps from Strings to Integer Values(no C API)icu::BytesTrie, icu::UCharsTrie
Codepage Conversionucnv.h, ucnvsel.hC API
Codepage Detectionucsdet.hC API
Unicode Text Compressionucnv.h
(encoding name "SCSU" or "BOCU-1")
C API
Locales uloc.hicu::Locale, icu::LocaleBuilder, icu::LocaleMatcher
Resource Bundlesures.hicu::ResourceBundle
Normalizationunorm2.hicu::Normalizer2
Calendars and Time Zonesucal.hicu::Calendar, icu::TimeZone
Date and Time Formattingudat.hicu::DateFormat
Message Formattingumsg.hicu::MessageFormat
List Formattingulistformatter.hicu::ListFormatter
Number Formatting
(includes currency and unit formatting)
unumberformatter.h, unum.hicu::number::NumberFormatter (ICU 60+) or icu::NumberFormat (older versions)
Number Range Formatting
(includes currency and unit ranges)
unumberrangeformatter.hicu::number::NumberRangeFormatter
Number Spellout
(Rule Based Number Formatting)
unum.h
(use UNUM_SPELLOUT)
icu::RuleBasedNumberFormat
Text Transformation
(Transliteration)
utrans.hicu::Transliterator
Bidirectional Algorithmubidi.h, ubiditransform.hC API
Arabic Shapingushape.hC API
Collationucol.hicu::Collator
String Searchingusearch.hicu::StringSearch
Index Characters/
Bucketing for Sorted Lists
(no C API)icu::AlphabeticIndex
Text Boundary Analysis
(Break Iteration)
ubrk.hicu::BreakIterator
Regular Expressionsuregex.hicu::RegexPattern, icu::RegexMatcher
StringPrepusprep.hC API
International Domain Names in Applications:
- * UTS #46 in C/C++, IDNA2003 only via C API
uidna.hidna.h
Identifier Spoofing & Confusabilityuspoof.hC API
Universal Time Scaleutmscale.hC API
Paragraph Layout / Complex Text Layoutplayout.hicu::ParagraphLayout
ICU I/Oustdio.hustream.h
- * This main page is generated from docmain.h - */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 1997-2012, International Business Machines Corporation and + * others. All Rights Reserved. + * + * FILE NAME: DOCMAIN.h + * + * Date Name Description + * 12/11/2000 Ram Creation. + */ + +/** + * \file + * \brief (Non API- contains Doxygen definitions) + * + * This file contains documentation for Doxygen and does not have + * any significance with respect to C or C++ API + */ + +/*! \mainpage + * + * \section API API Reference Usage + * + *

C++ Programmers:

+ *

Use Class Hierarchy or Alphabetical List + * or Compound List + * to find the class you are interested in. For example, to find BreakIterator, + * you can go to the Alphabetical List, then click on + * "BreakIterator". Once you are at the class, you will find an inheritance + * chart, a list of the public members, a detailed description of the class, + * then detailed member descriptions.

+ * + *

C Programmers:

+ *

Use Module List or File Members + * to find a list of all the functions and constants. + * For example, to find BreakIterator functions you would click on + * File List, + * then find "ubrk.h" and click on it. You will find descriptions of Defines, + * Typedefs, Enumerations, and Functions, with detailed descriptions below. + * If you want to find a specific function, such as ubrk_next(), then click + * first on File Members, then use your browser + * Find dialog to search for "ubrk_next()".

+ * + * + *

API References for Previous Releases

+ *

The API References for each release of ICU are also available as + * a zip file from the ICU + * download page.

+ * + *
+ * + *

Architecture (User's Guide)

+ * + * + *
+ *\htmlonly

Module List

\endhtmlonly + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Module NameCC++
Basic Types and Constantsutypes.hutypes.h
Strings and Character Iterationustring.h, utf8.h, utf16.h, icu::StringPiece, UText, UCharIterator, icu::ByteSinkicu::UnicodeString, icu::CharacterIterator, icu::Appendable, icu::StringPiece,icu::ByteSink
Unicode Character
Properties and Names
uchar.h, uscript.hC API
Sets of Unicode Code Points and Stringsuset.hicu::UnicodeSet
Maps from Unicode Code Points to Integer Valuesucptrie.h, umutablecptrie.hC API
Maps from Strings to Integer Values(no C API)icu::BytesTrie, icu::UCharsTrie
Codepage Conversionucnv.h, ucnvsel.hC API
Codepage Detectionucsdet.hC API
Unicode Text Compressionucnv.h
(encoding name "SCSU" or "BOCU-1")
C API
Locales uloc.hicu::Locale, icu::LocaleBuilder, icu::LocaleMatcher
Resource Bundlesures.hicu::ResourceBundle
Normalizationunorm2.hicu::Normalizer2
Calendars and Time Zonesucal.hicu::Calendar, icu::TimeZone
Date and Time Formattingudat.hicu::DateFormat
Message Formattingumsg.hicu::MessageFormat
List Formattingulistformatter.hicu::ListFormatter
Number Formatting
(includes currency and unit formatting)
unumberformatter.h, unum.h, usimplenumberformatter.hicu::number::NumberFormatter (ICU 60+) or icu::NumberFormat (older versions)
icu::number::SimpleNumberFormatter (ICU 73+)
Number Range Formatting
(includes currency and unit ranges)
unumberrangeformatter.hicu::number::NumberRangeFormatter
Number Spellout
(Rule Based Number Formatting)
unum.h
(use UNUM_SPELLOUT)
icu::RuleBasedNumberFormat
Text Transformation
(Transliteration)
utrans.hicu::Transliterator
Bidirectional Algorithmubidi.h, ubiditransform.hC API
Arabic Shapingushape.hC API
Collationucol.hicu::Collator
String Searchingusearch.hicu::StringSearch
Index Characters/
Bucketing for Sorted Lists
(no C API)icu::AlphabeticIndex
Text Boundary Analysis
(Break Iteration)
ubrk.hicu::BreakIterator
Regular Expressionsuregex.hicu::RegexPattern, icu::RegexMatcher
StringPrepusprep.hC API
International Domain Names in Applications:
+ * UTS #46 in C/C++, IDNA2003 only via C API
uidna.hidna.h
Identifier Spoofing & Confusabilityuspoof.hC API
Universal Time Scaleutmscale.hC API
Paragraph Layout / Complex Text Layoutplayout.hicu::ParagraphLayout
ICU I/Oustdio.hustream.h
+ * This main page is generated from docmain.h + */ diff --git a/deps/icu-small/source/common/unicode/dtintrv.h b/deps/icu-small/source/common/unicode/dtintrv.h index 8c172eb7a59acc..2347a5a57406f3 100644 --- a/deps/icu-small/source/common/unicode/dtintrv.h +++ b/deps/icu-small/source/common/unicode/dtintrv.h @@ -1,164 +1,164 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2008-2009, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File DTINTRV.H -* -******************************************************************************* -*/ - -#ifndef __DTINTRV_H__ -#define __DTINTRV_H__ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/uobject.h" - -/** - * \file - * \brief C++ API: Date Interval data type - */ - -U_NAMESPACE_BEGIN - - -/** - * This class represents a date interval. - * It is a pair of UDate representing from UDate 1 to UDate 2. - * @stable ICU 4.0 -**/ -class U_COMMON_API DateInterval : public UObject { -public: - - /** - * Construct a DateInterval given a from date and a to date. - * @param fromDate The from date in date interval. - * @param toDate The to date in date interval. - * @stable ICU 4.0 - */ - DateInterval(UDate fromDate, UDate toDate); - - /** - * destructor - * @stable ICU 4.0 - */ - virtual ~DateInterval(); - - /** - * Get the from date. - * @return the from date in dateInterval. - * @stable ICU 4.0 - */ - inline UDate getFromDate() const; - - /** - * Get the to date. - * @return the to date in dateInterval. - * @stable ICU 4.0 - */ - inline UDate getToDate() const; - - - /** - * Return the class ID for this class. This is useful only for comparing to - * a return value from getDynamicClassID(). For example: - *
-     * .   Base* polymorphic_pointer = createPolymorphicObject();
-     * .   if (polymorphic_pointer->getDynamicClassID() ==
-     * .       derived::getStaticClassID()) ...
-     * 
- * @return The class ID for all objects of this class. - * @stable ICU 4.0 - */ - static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * Returns a unique class ID POLYMORPHICALLY. Pure virtual override. This - * method is to implement a simple version of RTTI, since not all C++ - * compilers support genuine RTTI. Polymorphic operator==() and clone() - * methods call this method. - * - * @return The class ID for this object. All objects of a - * given class have the same class ID. Objects of - * other classes have different class IDs. - * @stable ICU 4.0 - */ - virtual UClassID getDynamicClassID(void) const override; - - - /** - * Copy constructor. - * @stable ICU 4.0 - */ - DateInterval(const DateInterval& other); - - /** - * Default assignment operator - * @stable ICU 4.0 - */ - DateInterval& operator=(const DateInterval&); - - /** - * Equality operator. - * @return true if the two DateIntervals are the same - * @stable ICU 4.0 - */ - virtual bool operator==(const DateInterval& other) const; - - /** - * Non-equality operator - * @return true if the two DateIntervals are not the same - * @stable ICU 4.0 - */ - inline bool operator!=(const DateInterval& other) const; - - - /** - * clone this object. - * The caller owns the result and should delete it when done. - * @return a cloned DateInterval - * @stable ICU 4.0 - */ - virtual DateInterval* clone() const; - -private: - /** - * Default constructor, not implemented. - */ - DateInterval() = delete; - - UDate fromDate; - UDate toDate; - -} ;// end class DateInterval - - -inline UDate -DateInterval::getFromDate() const { - return fromDate; -} - - -inline UDate -DateInterval::getToDate() const { - return toDate; -} - - -inline bool -DateInterval::operator!=(const DateInterval& other) const { - return ( !operator==(other) ); -} - - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2008-2009, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTINTRV.H +* +******************************************************************************* +*/ + +#ifndef __DTINTRV_H__ +#define __DTINTRV_H__ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/uobject.h" + +/** + * \file + * \brief C++ API: Date Interval data type + */ + +U_NAMESPACE_BEGIN + + +/** + * This class represents a date interval. + * It is a pair of UDate representing from UDate 1 to UDate 2. + * @stable ICU 4.0 +**/ +class U_COMMON_API DateInterval : public UObject { +public: + + /** + * Construct a DateInterval given a from date and a to date. + * @param fromDate The from date in date interval. + * @param toDate The to date in date interval. + * @stable ICU 4.0 + */ + DateInterval(UDate fromDate, UDate toDate); + + /** + * destructor + * @stable ICU 4.0 + */ + virtual ~DateInterval(); + + /** + * Get the from date. + * @return the from date in dateInterval. + * @stable ICU 4.0 + */ + inline UDate getFromDate() const; + + /** + * Get the to date. + * @return the to date in dateInterval. + * @stable ICU 4.0 + */ + inline UDate getToDate() const; + + + /** + * Return the class ID for this class. This is useful only for comparing to + * a return value from getDynamicClassID(). For example: + *
+     * .   Base* polymorphic_pointer = createPolymorphicObject();
+     * .   if (polymorphic_pointer->getDynamicClassID() ==
+     * .       derived::getStaticClassID()) ...
+     * 
+ * @return The class ID for all objects of this class. + * @stable ICU 4.0 + */ + static UClassID U_EXPORT2 getStaticClassID(void); + + /** + * Returns a unique class ID POLYMORPHICALLY. Pure virtual override. This + * method is to implement a simple version of RTTI, since not all C++ + * compilers support genuine RTTI. Polymorphic operator==() and clone() + * methods call this method. + * + * @return The class ID for this object. All objects of a + * given class have the same class ID. Objects of + * other classes have different class IDs. + * @stable ICU 4.0 + */ + virtual UClassID getDynamicClassID(void) const override; + + + /** + * Copy constructor. + * @stable ICU 4.0 + */ + DateInterval(const DateInterval& other); + + /** + * Default assignment operator + * @stable ICU 4.0 + */ + DateInterval& operator=(const DateInterval&); + + /** + * Equality operator. + * @return true if the two DateIntervals are the same + * @stable ICU 4.0 + */ + virtual bool operator==(const DateInterval& other) const; + + /** + * Non-equality operator + * @return true if the two DateIntervals are not the same + * @stable ICU 4.0 + */ + inline bool operator!=(const DateInterval& other) const; + + + /** + * clone this object. + * The caller owns the result and should delete it when done. + * @return a cloned DateInterval + * @stable ICU 4.0 + */ + virtual DateInterval* clone() const; + +private: + /** + * Default constructor, not implemented. + */ + DateInterval() = delete; + + UDate fromDate; + UDate toDate; + +} ;// end class DateInterval + + +inline UDate +DateInterval::getFromDate() const { + return fromDate; +} + + +inline UDate +DateInterval::getToDate() const { + return toDate; +} + + +inline bool +DateInterval::operator!=(const DateInterval& other) const { + return ( !operator==(other) ); +} + + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/edits.h b/deps/icu-small/source/common/unicode/edits.h index bfa07fa6765b73..9c0a64858dde34 100644 --- a/deps/icu-small/source/common/unicode/edits.h +++ b/deps/icu-small/source/common/unicode/edits.h @@ -1,531 +1,531 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// edits.h -// created: 2016dec30 Markus W. Scherer - -#ifndef __EDITS_H__ -#define __EDITS_H__ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/uobject.h" - -/** - * \file - * \brief C++ API: C++ class Edits for low-level string transformations on styled text. - */ - -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. - * - * There are two types of edits: change edits and no-change edits. Add edits to - * instances of this class using {@link #addReplace(int32_t, int32_t)} (for change edits) and - * {@link #addUnchanged(int32_t)} (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 - * `Edits::Iterator::next()` methods are called. They are identical to the non-change iterators when - * their `Edits::Iterator::findSourceIndex()` or `Edits::Iterator::findDestinationIndex()` - * 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. - * - * @stable ICU 59 - */ -class U_COMMON_API Edits U_FINAL : public UMemory { -public: - /** - * Constructs an empty object. - * @stable ICU 59 - */ - Edits() : - array(stackArray), capacity(STACK_CAPACITY), length(0), delta(0), numChanges(0), - errorCode_(U_ZERO_ERROR) {} - /** - * Copy constructor. - * @param other source edits - * @stable ICU 60 - */ - Edits(const Edits &other) : - array(stackArray), capacity(STACK_CAPACITY), length(other.length), - delta(other.delta), numChanges(other.numChanges), - errorCode_(other.errorCode_) { - copyArray(other); - } - /** - * Move constructor, might leave src empty. - * This object will have the same contents that the source object had. - * @param src source edits - * @stable ICU 60 - */ - Edits(Edits &&src) U_NOEXCEPT : - array(stackArray), capacity(STACK_CAPACITY), length(src.length), - delta(src.delta), numChanges(src.numChanges), - errorCode_(src.errorCode_) { - moveArray(src); - } - - /** - * Destructor. - * @stable ICU 59 - */ - ~Edits(); - - /** - * Assignment operator. - * @param other source edits - * @return *this - * @stable ICU 60 - */ - Edits &operator=(const Edits &other); - - /** - * Move assignment operator, might leave src empty. - * This object will have the same contents that the source object had. - * The behavior is undefined if *this and src are the same object. - * @param src source edits - * @return *this - * @stable ICU 60 - */ - Edits &operator=(Edits &&src) U_NOEXCEPT; - - /** - * Resets the data but may not release memory. - * @stable ICU 59 - */ - void reset() U_NOEXCEPT; - - /** - * 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 change edit: a record for a text replacement/insertion/deletion. - * Normally called from inside ICU string transformation functions, not user code. - * @stable ICU 59 - */ - void addReplace(int32_t oldLength, int32_t newLength); - /** - * Sets the UErrorCode if an error occurred while recording edits. - * Preserves older error codes in the outErrorCode. - * Normally called from inside ICU string transformation functions, not user code. - * @param outErrorCode Set to an error code if it does not contain one already - * and an error occurred while recording edits. - * Otherwise unchanged. - * @return true if U_FAILURE(outErrorCode) - * @stable ICU 59 - */ - UBool copyErrorTo(UErrorCode &outErrorCode) const; - - /** - * How much longer is the new text compared with the old text? - * @return new length minus old length - * @stable ICU 59 - */ - int32_t lengthDelta() const { return delta; } - /** - * @return true if there are any change edits - * @stable ICU 59 - */ - UBool hasChanges() const { return numChanges != 0; } - - /** - * @return the number of change edits - * @stable ICU 60 - */ - int32_t numberOfChanges() const { return numChanges; } - - /** - * 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 `next()`, `findSourceIndex(int32_t, UErrorCode &)`, - * and `findDestinationIndex(int32_t, UErrorCode &)` 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 - */ - struct U_COMMON_API Iterator U_FINAL : public UMemory { - /** - * Default constructor, empty iterator. - * @stable ICU 60 - */ - Iterator() : - array(nullptr), index(0), length(0), - remaining(0), onlyChanges_(false), coarse(false), - dir(0), changed(false), oldLength_(0), newLength_(0), - srcIndex(0), replIndex(0), destIndex(0) {} - /** - * Copy constructor. - * @stable ICU 59 - */ - Iterator(const Iterator &other) = default; - /** - * Assignment operator. - * @stable ICU 59 - */ - Iterator &operator=(const Iterator &other) = default; - - /** - * 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.) - * @return true if there is another edit - * @stable ICU 59 - */ - UBool next(UErrorCode &errorCode) { return next(onlyChanges_, errorCode); } - - /** - * 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. - * (It may affect the performance of the search.) - * - * The iterator state after this search is undefined - * if the source index is out of bounds for the source string. - * - * @param i source index - * @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.) - * @return true if the edit for the source index was found - * @stable ICU 59 - */ - UBool findSourceIndex(int32_t i, UErrorCode &errorCode) { - return findIndex(i, true, errorCode) == 0; - } - - /** - * 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. - * (It may affect the performance of the search.) - * - * The iterator state after this search is undefined - * if the source index is out of bounds for the source string. - * - * @param i destination index - * @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.) - * @return true if the edit for the destination index was found - * @stable ICU 60 - */ - UBool findDestinationIndex(int32_t i, UErrorCode &errorCode) { - return findIndex(i, false, errorCode) == 0; - } - - /** - * 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. - * - * (This means that indexes to the start and middle of an edit, - * for example around a grapheme cluster, are mapped to indexes - * encompassing the entire edit. - * The alternative, mapping an interior index to the start, - * would map such an interval to an empty one.) - * - * This operation will usually but not always modify this object. - * The iterator state after this search is undefined. - * - * @param i source index - * @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.) - * @return destination index; undefined if i is not 0..string length - * @stable ICU 60 - */ - int32_t destinationIndexFromSourceIndex(int32_t i, UErrorCode &errorCode); - - /** - * 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. - * - * (This means that indexes to the start and middle of an edit, - * for example around a grapheme cluster, are mapped to indexes - * encompassing the entire edit. - * The alternative, mapping an interior index to the start, - * would map such an interval to an empty one.) - * - * This operation will usually but not always modify this object. - * The iterator state after this search is undefined. - * - * @param i destination index - * @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.) - * @return source index; undefined if i is not 0..string length - * @stable ICU 60 - */ - int32_t sourceIndexFromDestinationIndex(int32_t i, UErrorCode &errorCode); - - /** - * 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 - */ - 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. operations specifying the \ref U_OMIT_UNCHANGED_TEXT option). - * 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 { - // 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; - - Iterator(const uint16_t *a, int32_t len, UBool oc, UBool crs); - - int32_t readLength(int32_t head); - void updateNextIndexes(); - void updatePreviousIndexes(); - UBool noNext(); - UBool next(UBool onlyChanges, UErrorCode &errorCode); - UBool previous(UErrorCode &errorCode); - /** @return -1: error or i<0; 0: found; 1: i>=string length */ - int32_t findIndex(int32_t i, UBool findSource, UErrorCode &errorCode); - - const uint16_t *array; - int32_t index, length; - // 0 if we are not within compressed equal-length changes. - // Otherwise the number of remaining changes, including the current one. - int32_t remaining; - UBool onlyChanges_, coarse; - - int8_t dir; // iteration direction: back(<0), initial(0), forward(>0) - UBool changed; - int32_t oldLength_, newLength_; - int32_t srcIndex, replIndex, destIndex; - }; - - /** - * 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 - */ - Iterator getCoarseChangesIterator() const { - return Iterator(array, length, true, true); - } - - /** - * 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 - */ - Iterator getCoarseIterator() const { - return Iterator(array, length, false, true); - } - - /** - * 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 - */ - Iterator getFineChangesIterator() const { - return Iterator(array, length, true, false); - } - - /** - * 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 - */ - Iterator getFineIterator() const { - return Iterator(array, length, false, false); - } - - /** - * Merges the two input Edits and appends the result to this object. - * - * Consider two string transformations (for example, normalization and case mapping) - * where each records Edits in addition to writing an output string.
- * Edits ab reflect how substrings of input string a - * map to substrings of intermediate string b.
- * Edits bc reflect how substrings of intermediate string b - * map to substrings of output string c.
- * This function merges ab and bc such that the additional edits - * recorded in this object reflect how substrings of input string a - * map to substrings of output string c. - * - * If unrelated Edits are passed in where the output string of the first - * has a different length than the input string of the second, - * then a U_ILLEGAL_ARGUMENT_ERROR is reported. - * - * @param ab reflects how substrings of input string a - * map to substrings of intermediate string b. - * @param bc reflects how substrings of intermediate string b - * map to substrings of output string c. - * @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.) - * @return *this, with the merged edits appended - * @stable ICU 60 - */ - Edits &mergeAndAppend(const Edits &ab, const Edits &bc, UErrorCode &errorCode); - -private: - void releaseArray() U_NOEXCEPT; - Edits ©Array(const Edits &other); - Edits &moveArray(Edits &src) U_NOEXCEPT; - - void setLastUnit(int32_t last) { array[length - 1] = (uint16_t)last; } - int32_t lastUnit() const { return length > 0 ? array[length - 1] : 0xffff; } - - void append(int32_t r); - UBool growArray(); - - static const int32_t STACK_CAPACITY = 100; - uint16_t *array; - int32_t capacity; - int32_t length; - int32_t delta; - int32_t numChanges; - UErrorCode errorCode_; - uint16_t stackArray[STACK_CAPACITY]; -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __EDITS_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// edits.h +// created: 2016dec30 Markus W. Scherer + +#ifndef __EDITS_H__ +#define __EDITS_H__ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/uobject.h" + +/** + * \file + * \brief C++ API: C++ class Edits for low-level string transformations on styled text. + */ + +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. + * + * There are two types of edits: change edits and no-change edits. Add edits to + * instances of this class using {@link #addReplace(int32_t, int32_t)} (for change edits) and + * {@link #addUnchanged(int32_t)} (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 + * `Edits::Iterator::next()` methods are called. They are identical to the non-change iterators when + * their `Edits::Iterator::findSourceIndex()` or `Edits::Iterator::findDestinationIndex()` + * 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. + * + * @stable ICU 59 + */ +class U_COMMON_API Edits final : public UMemory { +public: + /** + * Constructs an empty object. + * @stable ICU 59 + */ + Edits() : + array(stackArray), capacity(STACK_CAPACITY), length(0), delta(0), numChanges(0), + errorCode_(U_ZERO_ERROR) {} + /** + * Copy constructor. + * @param other source edits + * @stable ICU 60 + */ + Edits(const Edits &other) : + array(stackArray), capacity(STACK_CAPACITY), length(other.length), + delta(other.delta), numChanges(other.numChanges), + errorCode_(other.errorCode_) { + copyArray(other); + } + /** + * Move constructor, might leave src empty. + * This object will have the same contents that the source object had. + * @param src source edits + * @stable ICU 60 + */ + Edits(Edits &&src) noexcept : + array(stackArray), capacity(STACK_CAPACITY), length(src.length), + delta(src.delta), numChanges(src.numChanges), + errorCode_(src.errorCode_) { + moveArray(src); + } + + /** + * Destructor. + * @stable ICU 59 + */ + ~Edits(); + + /** + * Assignment operator. + * @param other source edits + * @return *this + * @stable ICU 60 + */ + Edits &operator=(const Edits &other); + + /** + * Move assignment operator, might leave src empty. + * This object will have the same contents that the source object had. + * The behavior is undefined if *this and src are the same object. + * @param src source edits + * @return *this + * @stable ICU 60 + */ + Edits &operator=(Edits &&src) noexcept; + + /** + * Resets the data but may not release memory. + * @stable ICU 59 + */ + void reset() noexcept; + + /** + * 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 change edit: a record for a text replacement/insertion/deletion. + * Normally called from inside ICU string transformation functions, not user code. + * @stable ICU 59 + */ + void addReplace(int32_t oldLength, int32_t newLength); + /** + * Sets the UErrorCode if an error occurred while recording edits. + * Preserves older error codes in the outErrorCode. + * Normally called from inside ICU string transformation functions, not user code. + * @param outErrorCode Set to an error code if it does not contain one already + * and an error occurred while recording edits. + * Otherwise unchanged. + * @return true if U_FAILURE(outErrorCode) + * @stable ICU 59 + */ + UBool copyErrorTo(UErrorCode &outErrorCode) const; + + /** + * How much longer is the new text compared with the old text? + * @return new length minus old length + * @stable ICU 59 + */ + int32_t lengthDelta() const { return delta; } + /** + * @return true if there are any change edits + * @stable ICU 59 + */ + UBool hasChanges() const { return numChanges != 0; } + + /** + * @return the number of change edits + * @stable ICU 60 + */ + int32_t numberOfChanges() const { return numChanges; } + + /** + * 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 `next()`, `findSourceIndex(int32_t, UErrorCode &)`, + * and `findDestinationIndex(int32_t, UErrorCode &)` 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 + */ + struct U_COMMON_API Iterator final : public UMemory { + /** + * Default constructor, empty iterator. + * @stable ICU 60 + */ + Iterator() : + array(nullptr), index(0), length(0), + remaining(0), onlyChanges_(false), coarse(false), + dir(0), changed(false), oldLength_(0), newLength_(0), + srcIndex(0), replIndex(0), destIndex(0) {} + /** + * Copy constructor. + * @stable ICU 59 + */ + Iterator(const Iterator &other) = default; + /** + * Assignment operator. + * @stable ICU 59 + */ + Iterator &operator=(const Iterator &other) = default; + + /** + * 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.) + * @return true if there is another edit + * @stable ICU 59 + */ + UBool next(UErrorCode &errorCode) { return next(onlyChanges_, errorCode); } + + /** + * 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. + * (It may affect the performance of the search.) + * + * The iterator state after this search is undefined + * if the source index is out of bounds for the source string. + * + * @param i source index + * @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.) + * @return true if the edit for the source index was found + * @stable ICU 59 + */ + UBool findSourceIndex(int32_t i, UErrorCode &errorCode) { + return findIndex(i, true, errorCode) == 0; + } + + /** + * 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. + * (It may affect the performance of the search.) + * + * The iterator state after this search is undefined + * if the source index is out of bounds for the source string. + * + * @param i destination index + * @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.) + * @return true if the edit for the destination index was found + * @stable ICU 60 + */ + UBool findDestinationIndex(int32_t i, UErrorCode &errorCode) { + return findIndex(i, false, errorCode) == 0; + } + + /** + * 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. + * + * (This means that indexes to the start and middle of an edit, + * for example around a grapheme cluster, are mapped to indexes + * encompassing the entire edit. + * The alternative, mapping an interior index to the start, + * would map such an interval to an empty one.) + * + * This operation will usually but not always modify this object. + * The iterator state after this search is undefined. + * + * @param i source index + * @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.) + * @return destination index; undefined if i is not 0..string length + * @stable ICU 60 + */ + int32_t destinationIndexFromSourceIndex(int32_t i, UErrorCode &errorCode); + + /** + * 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. + * + * (This means that indexes to the start and middle of an edit, + * for example around a grapheme cluster, are mapped to indexes + * encompassing the entire edit. + * The alternative, mapping an interior index to the start, + * would map such an interval to an empty one.) + * + * This operation will usually but not always modify this object. + * The iterator state after this search is undefined. + * + * @param i destination index + * @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.) + * @return source index; undefined if i is not 0..string length + * @stable ICU 60 + */ + int32_t sourceIndexFromDestinationIndex(int32_t i, UErrorCode &errorCode); + + /** + * 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 + */ + 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. operations specifying the \ref U_OMIT_UNCHANGED_TEXT option). + * 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 { + // 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; + + Iterator(const uint16_t *a, int32_t len, UBool oc, UBool crs); + + int32_t readLength(int32_t head); + void updateNextIndexes(); + void updatePreviousIndexes(); + UBool noNext(); + UBool next(UBool onlyChanges, UErrorCode &errorCode); + UBool previous(UErrorCode &errorCode); + /** @return -1: error or i<0; 0: found; 1: i>=string length */ + int32_t findIndex(int32_t i, UBool findSource, UErrorCode &errorCode); + + const uint16_t *array; + int32_t index, length; + // 0 if we are not within compressed equal-length changes. + // Otherwise the number of remaining changes, including the current one. + int32_t remaining; + UBool onlyChanges_, coarse; + + int8_t dir; // iteration direction: back(<0), initial(0), forward(>0) + UBool changed; + int32_t oldLength_, newLength_; + int32_t srcIndex, replIndex, destIndex; + }; + + /** + * 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 + */ + Iterator getCoarseChangesIterator() const { + return Iterator(array, length, true, true); + } + + /** + * 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 + */ + Iterator getCoarseIterator() const { + return Iterator(array, length, false, true); + } + + /** + * 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 + */ + Iterator getFineChangesIterator() const { + return Iterator(array, length, true, false); + } + + /** + * 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 + */ + Iterator getFineIterator() const { + return Iterator(array, length, false, false); + } + + /** + * Merges the two input Edits and appends the result to this object. + * + * Consider two string transformations (for example, normalization and case mapping) + * where each records Edits in addition to writing an output string.
+ * Edits ab reflect how substrings of input string a + * map to substrings of intermediate string b.
+ * Edits bc reflect how substrings of intermediate string b + * map to substrings of output string c.
+ * This function merges ab and bc such that the additional edits + * recorded in this object reflect how substrings of input string a + * map to substrings of output string c. + * + * If unrelated Edits are passed in where the output string of the first + * has a different length than the input string of the second, + * then a U_ILLEGAL_ARGUMENT_ERROR is reported. + * + * @param ab reflects how substrings of input string a + * map to substrings of intermediate string b. + * @param bc reflects how substrings of intermediate string b + * map to substrings of output string c. + * @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.) + * @return *this, with the merged edits appended + * @stable ICU 60 + */ + Edits &mergeAndAppend(const Edits &ab, const Edits &bc, UErrorCode &errorCode); + +private: + void releaseArray() noexcept; + Edits ©Array(const Edits &other); + Edits &moveArray(Edits &src) noexcept; + + void setLastUnit(int32_t last) { array[length - 1] = (uint16_t)last; } + int32_t lastUnit() const { return length > 0 ? array[length - 1] : 0xffff; } + + void append(int32_t r); + UBool growArray(); + + static const int32_t STACK_CAPACITY = 100; + uint16_t *array; + int32_t capacity; + int32_t length; + int32_t delta; + int32_t numChanges; + UErrorCode errorCode_; + uint16_t stackArray[STACK_CAPACITY]; +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __EDITS_H__ diff --git a/deps/icu-small/source/common/unicode/enumset.h b/deps/icu-small/source/common/unicode/enumset.h index bde8c455c0dd2b..335634f3363b84 100644 --- a/deps/icu-small/source/common/unicode/enumset.h +++ b/deps/icu-small/source/common/unicode/enumset.h @@ -1,69 +1,69 @@ -// © 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. -* -****************************************************************************** -*/ - -/** - * \file - * \brief C++: internal template EnumSet<> - */ - -#ifndef ENUMSET_H -#define ENUMSET_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/* Can't use #ifndef U_HIDE_INTERNAL_API for the entire EnumSet class, needed in .h file declarations */ -/** - * enum bitset for boolean fields. Similar to Java EnumSet<>. - * Needs to range check. Used for private instance variables. - * @internal - * \cond - */ -template -class EnumSet { -public: - inline EnumSet() : fBools(0) {} - inline EnumSet(const EnumSet& other) : fBools(other.fBools) {} - inline ~EnumSet() {} -#ifndef U_HIDE_INTERNAL_API - inline void clear() { fBools=0; } - inline void add(T toAdd) { set(toAdd, 1); } - inline void remove(T toRemove) { set(toRemove, 0); } - inline int32_t contains(T toCheck) const { return get(toCheck); } - inline void set(T toSet, int32_t v) { fBools=(fBools&(~flag(toSet)))|(v?(flag(toSet)):0); } - inline int32_t get(T toCheck) const { return (fBools & flag(toCheck))?1:0; } - inline UBool isValidEnum(T toCheck) const { return (toCheck>=minValue&&toCheck& operator=(const EnumSet& other) { - fBools = other.fBools; - return *this; - } - - inline uint32_t getAll() const { - return fBools; - } -#endif /* U_HIDE_INTERNAL_API */ - -private: - inline uint32_t flag(T toCheck) const { return (1<<(toCheck-minValue)); } -private: - uint32_t fBools; -}; - -/** \endcond */ - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ -#endif /* ENUMSET_H */ +// © 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. +* +****************************************************************************** +*/ + +/** + * \file + * \brief C++: internal template EnumSet<> + */ + +#ifndef ENUMSET_H +#define ENUMSET_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/* Can't use #ifndef U_HIDE_INTERNAL_API for the entire EnumSet class, needed in .h file declarations */ +/** + * enum bitset for boolean fields. Similar to Java EnumSet<>. + * Needs to range check. Used for private instance variables. + * @internal + * \cond + */ +template +class EnumSet { +public: + inline EnumSet() : fBools(0) {} + inline EnumSet(const EnumSet& other) : fBools(other.fBools) {} + inline ~EnumSet() {} +#ifndef U_HIDE_INTERNAL_API + inline void clear() { fBools=0; } + inline void add(T toAdd) { set(toAdd, 1); } + inline void remove(T toRemove) { set(toRemove, 0); } + inline int32_t contains(T toCheck) const { return get(toCheck); } + inline void set(T toSet, int32_t v) { fBools=(fBools&(~flag(toSet)))|(v?(flag(toSet)):0); } + inline int32_t get(T toCheck) const { return (fBools & flag(toCheck))?1:0; } + inline UBool isValidEnum(T toCheck) const { return (toCheck>=minValue&&toCheck& operator=(const EnumSet& other) { + fBools = other.fBools; + return *this; + } + + inline uint32_t getAll() const { + return fBools; + } +#endif /* U_HIDE_INTERNAL_API */ + +private: + inline uint32_t flag(T toCheck) const { return (1<<(toCheck-minValue)); } +private: + uint32_t fBools; +}; + +/** \endcond */ + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ +#endif /* ENUMSET_H */ diff --git a/deps/icu-small/source/common/unicode/errorcode.h b/deps/icu-small/source/common/unicode/errorcode.h index fe7b5183232cd1..c37aafda2ce2d7 100644 --- a/deps/icu-small/source/common/unicode/errorcode.h +++ b/deps/icu-small/source/common/unicode/errorcode.h @@ -1,144 +1,144 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2009-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: errorcode.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2009mar10 -* created by: Markus W. Scherer -*/ - -#ifndef __ERRORCODE_H__ -#define __ERRORCODE_H__ - -/** - * \file - * \brief C++ API: ErrorCode class intended to make it easier to use - * ICU C and C++ APIs from C++ user code. - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/uobject.h" - -U_NAMESPACE_BEGIN - -/** - * Wrapper class for UErrorCode, with conversion operators for direct use - * in ICU C and C++ APIs. - * Intended to be used as a base class, where a subclass overrides - * the handleFailure() function so that it throws an exception, - * does an assert(), logs an error, etc. - * This is not an abstract base class. This class can be used and instantiated - * by itself, although it will be more useful when subclassed. - * - * Features: - * - The constructor initializes the internal UErrorCode to U_ZERO_ERROR, - * removing one common source of errors. - * - Same use in C APIs taking a UErrorCode * (pointer) - * and C++ taking UErrorCode & (reference) via conversion operators. - * - Possible automatic checking for success when it goes out of scope. - * - * Note: For automatic checking for success in the destructor, a subclass - * must implement such logic in its own destructor because the base class - * destructor cannot call a subclass function (like handleFailure()). - * The ErrorCode base class destructor does nothing. - * - * Note also: While it is possible for a destructor to throw an exception, - * it is generally unsafe to do so. This means that in a subclass the destructor - * and the handleFailure() function may need to take different actions. - * - * Sample code: - * \code - * class IcuErrorCode: public icu::ErrorCode { - * public: - * virtual ~IcuErrorCode() { // should be defined in .cpp as "key function" - * // Safe because our handleFailure() does not throw exceptions. - * if(isFailure()) { handleFailure(); } - * } - * protected: - * virtual void handleFailure() const { - * log_failure(u_errorName(errorCode)); - * exit(errorCode); - * } - * }; - * IcuErrorCode error_code; - * UConverter *cnv = ucnv_open("Shift-JIS", error_code); - * length = ucnv_fromUChars(dest, capacity, src, length, error_code); - * ucnv_close(cnv); - * // IcuErrorCode destructor checks for success. - * \endcode - * - * @stable ICU 4.2 - */ -class U_COMMON_API ErrorCode: public UMemory { -public: - /** - * Default constructor. Initializes its UErrorCode to U_ZERO_ERROR. - * @stable ICU 4.2 - */ - ErrorCode() : errorCode(U_ZERO_ERROR) {} - /** Destructor, does nothing. See class documentation for details. @stable ICU 4.2 */ - virtual ~ErrorCode(); - /** Conversion operator, returns a reference. @stable ICU 4.2 */ - operator UErrorCode & () { return errorCode; } - /** Conversion operator, returns a pointer. @stable ICU 4.2 */ - operator UErrorCode * () { return &errorCode; } - /** Tests for U_SUCCESS(). @stable ICU 4.2 */ - UBool isSuccess() const { return U_SUCCESS(errorCode); } - /** Tests for U_FAILURE(). @stable ICU 4.2 */ - UBool isFailure() const { return U_FAILURE(errorCode); } - /** Returns the UErrorCode value. @stable ICU 4.2 */ - UErrorCode get() const { return errorCode; } - /** Sets the UErrorCode value. @stable ICU 4.2 */ - void set(UErrorCode value) { errorCode=value; } - /** Returns the UErrorCode value and resets it to U_ZERO_ERROR. @stable ICU 4.2 */ - UErrorCode reset(); - /** - * Asserts isSuccess(). - * In other words, this method checks for a failure code, - * and the base class handles it like this: - * \code - * if(isFailure()) { handleFailure(); } - * \endcode - * @stable ICU 4.4 - */ - void assertSuccess() const; - /** - * Return a string for the UErrorCode value. - * The string will be the same as the name of the error code constant - * in the UErrorCode enum. - * @stable ICU 4.4 - */ - const char* errorName() const; - -protected: - /** - * Internal UErrorCode, accessible to subclasses. - * @stable ICU 4.2 - */ - UErrorCode errorCode; - /** - * Called by assertSuccess() if isFailure() is true. - * A subclass should override this function to deal with a failure code: - * Throw an exception, log an error, terminate the program, or similar. - * @stable ICU 4.2 - */ - virtual void handleFailure() const {} -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __ERRORCODE_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2009-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: errorcode.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2009mar10 +* created by: Markus W. Scherer +*/ + +#ifndef __ERRORCODE_H__ +#define __ERRORCODE_H__ + +/** + * \file + * \brief C++ API: ErrorCode class intended to make it easier to use + * ICU C and C++ APIs from C++ user code. + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/uobject.h" + +U_NAMESPACE_BEGIN + +/** + * Wrapper class for UErrorCode, with conversion operators for direct use + * in ICU C and C++ APIs. + * Intended to be used as a base class, where a subclass overrides + * the handleFailure() function so that it throws an exception, + * does an assert(), logs an error, etc. + * This is not an abstract base class. This class can be used and instantiated + * by itself, although it will be more useful when subclassed. + * + * Features: + * - The constructor initializes the internal UErrorCode to U_ZERO_ERROR, + * removing one common source of errors. + * - Same use in C APIs taking a UErrorCode * (pointer) + * and C++ taking UErrorCode & (reference) via conversion operators. + * - Possible automatic checking for success when it goes out of scope. + * + * Note: For automatic checking for success in the destructor, a subclass + * must implement such logic in its own destructor because the base class + * destructor cannot call a subclass function (like handleFailure()). + * The ErrorCode base class destructor does nothing. + * + * Note also: While it is possible for a destructor to throw an exception, + * it is generally unsafe to do so. This means that in a subclass the destructor + * and the handleFailure() function may need to take different actions. + * + * Sample code: + * \code + * class IcuErrorCode: public icu::ErrorCode { + * public: + * virtual ~IcuErrorCode() { // should be defined in .cpp as "key function" + * // Safe because our handleFailure() does not throw exceptions. + * if(isFailure()) { handleFailure(); } + * } + * protected: + * virtual void handleFailure() const { + * log_failure(u_errorName(errorCode)); + * exit(errorCode); + * } + * }; + * IcuErrorCode error_code; + * UConverter *cnv = ucnv_open("Shift-JIS", error_code); + * length = ucnv_fromUChars(dest, capacity, src, length, error_code); + * ucnv_close(cnv); + * // IcuErrorCode destructor checks for success. + * \endcode + * + * @stable ICU 4.2 + */ +class U_COMMON_API ErrorCode: public UMemory { +public: + /** + * Default constructor. Initializes its UErrorCode to U_ZERO_ERROR. + * @stable ICU 4.2 + */ + ErrorCode() : errorCode(U_ZERO_ERROR) {} + /** Destructor, does nothing. See class documentation for details. @stable ICU 4.2 */ + virtual ~ErrorCode(); + /** Conversion operator, returns a reference. @stable ICU 4.2 */ + operator UErrorCode & () { return errorCode; } + /** Conversion operator, returns a pointer. @stable ICU 4.2 */ + operator UErrorCode * () { return &errorCode; } + /** Tests for U_SUCCESS(). @stable ICU 4.2 */ + UBool isSuccess() const { return U_SUCCESS(errorCode); } + /** Tests for U_FAILURE(). @stable ICU 4.2 */ + UBool isFailure() const { return U_FAILURE(errorCode); } + /** Returns the UErrorCode value. @stable ICU 4.2 */ + UErrorCode get() const { return errorCode; } + /** Sets the UErrorCode value. @stable ICU 4.2 */ + void set(UErrorCode value) { errorCode=value; } + /** Returns the UErrorCode value and resets it to U_ZERO_ERROR. @stable ICU 4.2 */ + UErrorCode reset(); + /** + * Asserts isSuccess(). + * In other words, this method checks for a failure code, + * and the base class handles it like this: + * \code + * if(isFailure()) { handleFailure(); } + * \endcode + * @stable ICU 4.4 + */ + void assertSuccess() const; + /** + * Return a string for the UErrorCode value. + * The string will be the same as the name of the error code constant + * in the UErrorCode enum. + * @stable ICU 4.4 + */ + const char* errorName() const; + +protected: + /** + * Internal UErrorCode, accessible to subclasses. + * @stable ICU 4.2 + */ + UErrorCode errorCode; + /** + * Called by assertSuccess() if isFailure() is true. + * A subclass should override this function to deal with a failure code: + * Throw an exception, log an error, terminate the program, or similar. + * @stable ICU 4.2 + */ + virtual void handleFailure() const {} +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __ERRORCODE_H__ diff --git a/deps/icu-small/source/common/unicode/filteredbrk.h b/deps/icu-small/source/common/unicode/filteredbrk.h index 8b07e39ae12bb6..54d365195b692d 100644 --- a/deps/icu-small/source/common/unicode/filteredbrk.h +++ b/deps/icu-small/source/common/unicode/filteredbrk.h @@ -1,152 +1,152 @@ -// © 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 FILTEREDBRK_H -#define FILTEREDBRK_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/brkiter.h" - -#if !UCONFIG_NO_BREAK_ITERATION && !UCONFIG_NO_FILTERED_BREAK_ITERATION - -U_NAMESPACE_BEGIN - -/** - * \file - * \brief C++ API: FilteredBreakIteratorBuilder - */ - -/** - * The BreakIteratorFilter is used to modify the behavior of a BreakIterator - * by constructing a new BreakIterator which suppresses certain segment boundaries. - * See http://www.unicode.org/reports/tr35/tr35-general.html#Segmentation_Exceptions . - * For example, a typical English Sentence Break Iterator would break on the space - * in the string "Mr. Smith" (resulting in two segments), - * but with "Mr." as an exception, a filtered break iterator - * would consider the string "Mr. Smith" to be a single segment. - * - * @stable ICU 56 - */ -class U_COMMON_API FilteredBreakIteratorBuilder : public UObject { - public: - /** - * destructor. - * @stable ICU 56 - */ - virtual ~FilteredBreakIteratorBuilder(); - - /** - * Construct a FilteredBreakIteratorBuilder based on rules in a locale. - * The rules are taken from CLDR exception data for the locale, - * see http://www.unicode.org/reports/tr35/tr35-general.html#Segmentation_Exceptions - * This is the equivalent of calling createInstance(UErrorCode&) - * and then repeatedly calling addNoBreakAfter(...) with the contents - * of the CLDR exception data. - * @param where the locale. - * @param status The error code. - * @return the new builder - * @stable ICU 56 - */ - static FilteredBreakIteratorBuilder *createInstance(const Locale& where, UErrorCode& status); - -#ifndef U_HIDE_DEPRECATED_API - /** - * This function has been deprecated in favor of createEmptyInstance, which has - * identical behavior. - * @param status The error code. - * @return the new builder - * @deprecated ICU 60 use createEmptyInstance instead - * @see createEmptyInstance() - */ - static FilteredBreakIteratorBuilder *createInstance(UErrorCode &status); -#endif /* U_HIDE_DEPRECATED_API */ - - /** - * Construct an empty FilteredBreakIteratorBuilder. - * In this state, it will not suppress any segment boundaries. - * @param status The error code. - * @return the new builder - * @stable ICU 60 - */ - static FilteredBreakIteratorBuilder *createEmptyInstance(UErrorCode &status); - - /** - * Suppress a certain string from being the end of a segment. - * For example, suppressing "Mr.", then segments ending in "Mr." will not be returned - * by the iterator. - * @param string the string to suppress, such as "Mr." - * @param status error code - * @return returns true if the string was not present and now added, - * false if the call was a no-op because the string was already being suppressed. - * @stable ICU 56 - */ - virtual UBool suppressBreakAfter(const UnicodeString& string, UErrorCode& status) = 0; - - /** - * Stop suppressing a certain string from being the end of the segment. - * This function does not create any new segment boundaries, but only serves to un-do - * the effect of earlier calls to suppressBreakAfter, or to un-do the effect of - * locale data which may be suppressing certain strings. - * @param string the exception to remove - * @param status error code - * @return returns true if the string was present and now removed, - * false if the call was a no-op because the string was not being suppressed. - * @stable ICU 56 - */ - virtual UBool unsuppressBreakAfter(const UnicodeString& string, UErrorCode& status) = 0; - -#ifndef U_FORCE_HIDE_DEPRECATED_API - /** - * This function has been deprecated in favor of wrapIteratorWithFilter() - * The behavior is identical. - * @param adoptBreakIterator the break iterator to adopt - * @param status error code - * @return the new BreakIterator, owned by the caller. - * @deprecated ICU 60 use wrapIteratorWithFilter() instead - * @see wrapBreakIteratorWithFilter() - */ - virtual BreakIterator *build(BreakIterator* adoptBreakIterator, UErrorCode& status) = 0; -#endif // U_FORCE_HIDE_DEPRECATED_API - - /** - * Wrap (adopt) an existing break iterator in a new filtered instance. - * The resulting BreakIterator is owned by the caller. - * The BreakIteratorFilter may be destroyed before the BreakIterator is destroyed. - * Note that the adoptBreakIterator is adopted by the new BreakIterator - * and should no longer be used by the caller. - * The FilteredBreakIteratorBuilder may be reused. - * This function is an alias for build() - * @param adoptBreakIterator the break iterator to adopt - * @param status error code - * @return the new BreakIterator, owned by the caller. - * @stable ICU 60 - */ - inline BreakIterator *wrapIteratorWithFilter(BreakIterator* adoptBreakIterator, UErrorCode& status) { - return build(adoptBreakIterator, status); - } - - protected: - /** - * For subclass use - * @stable ICU 56 - */ - FilteredBreakIteratorBuilder(); -}; - - -U_NAMESPACE_END - -#endif // #if !UCONFIG_NO_BREAK_ITERATION && !UCONFIG_NO_FILTERED_BREAK_ITERATION - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // #ifndef FILTEREDBRK_H +// © 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 FILTEREDBRK_H +#define FILTEREDBRK_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/brkiter.h" + +#if !UCONFIG_NO_BREAK_ITERATION && !UCONFIG_NO_FILTERED_BREAK_ITERATION + +U_NAMESPACE_BEGIN + +/** + * \file + * \brief C++ API: FilteredBreakIteratorBuilder + */ + +/** + * The BreakIteratorFilter is used to modify the behavior of a BreakIterator + * by constructing a new BreakIterator which suppresses certain segment boundaries. + * See http://www.unicode.org/reports/tr35/tr35-general.html#Segmentation_Exceptions . + * For example, a typical English Sentence Break Iterator would break on the space + * in the string "Mr. Smith" (resulting in two segments), + * but with "Mr." as an exception, a filtered break iterator + * would consider the string "Mr. Smith" to be a single segment. + * + * @stable ICU 56 + */ +class U_COMMON_API FilteredBreakIteratorBuilder : public UObject { + public: + /** + * destructor. + * @stable ICU 56 + */ + virtual ~FilteredBreakIteratorBuilder(); + + /** + * Construct a FilteredBreakIteratorBuilder based on rules in a locale. + * The rules are taken from CLDR exception data for the locale, + * see http://www.unicode.org/reports/tr35/tr35-general.html#Segmentation_Exceptions + * This is the equivalent of calling createInstance(UErrorCode&) + * and then repeatedly calling addNoBreakAfter(...) with the contents + * of the CLDR exception data. + * @param where the locale. + * @param status The error code. + * @return the new builder + * @stable ICU 56 + */ + static FilteredBreakIteratorBuilder *createInstance(const Locale& where, UErrorCode& status); + +#ifndef U_HIDE_DEPRECATED_API + /** + * This function has been deprecated in favor of createEmptyInstance, which has + * identical behavior. + * @param status The error code. + * @return the new builder + * @deprecated ICU 60 use createEmptyInstance instead + * @see createEmptyInstance() + */ + static FilteredBreakIteratorBuilder *createInstance(UErrorCode &status); +#endif /* U_HIDE_DEPRECATED_API */ + + /** + * Construct an empty FilteredBreakIteratorBuilder. + * In this state, it will not suppress any segment boundaries. + * @param status The error code. + * @return the new builder + * @stable ICU 60 + */ + static FilteredBreakIteratorBuilder *createEmptyInstance(UErrorCode &status); + + /** + * Suppress a certain string from being the end of a segment. + * For example, suppressing "Mr.", then segments ending in "Mr." will not be returned + * by the iterator. + * @param string the string to suppress, such as "Mr." + * @param status error code + * @return returns true if the string was not present and now added, + * false if the call was a no-op because the string was already being suppressed. + * @stable ICU 56 + */ + virtual UBool suppressBreakAfter(const UnicodeString& string, UErrorCode& status) = 0; + + /** + * Stop suppressing a certain string from being the end of the segment. + * This function does not create any new segment boundaries, but only serves to un-do + * the effect of earlier calls to suppressBreakAfter, or to un-do the effect of + * locale data which may be suppressing certain strings. + * @param string the exception to remove + * @param status error code + * @return returns true if the string was present and now removed, + * false if the call was a no-op because the string was not being suppressed. + * @stable ICU 56 + */ + virtual UBool unsuppressBreakAfter(const UnicodeString& string, UErrorCode& status) = 0; + +#ifndef U_FORCE_HIDE_DEPRECATED_API + /** + * This function has been deprecated in favor of wrapIteratorWithFilter() + * The behavior is identical. + * @param adoptBreakIterator the break iterator to adopt + * @param status error code + * @return the new BreakIterator, owned by the caller. + * @deprecated ICU 60 use wrapIteratorWithFilter() instead + * @see wrapBreakIteratorWithFilter() + */ + virtual BreakIterator *build(BreakIterator* adoptBreakIterator, UErrorCode& status) = 0; +#endif // U_FORCE_HIDE_DEPRECATED_API + + /** + * Wrap (adopt) an existing break iterator in a new filtered instance. + * The resulting BreakIterator is owned by the caller. + * The BreakIteratorFilter may be destroyed before the BreakIterator is destroyed. + * Note that the adoptBreakIterator is adopted by the new BreakIterator + * and should no longer be used by the caller. + * The FilteredBreakIteratorBuilder may be reused. + * This function is an alias for build() + * @param adoptBreakIterator the break iterator to adopt + * @param status error code + * @return the new BreakIterator, owned by the caller. + * @stable ICU 60 + */ + inline BreakIterator *wrapIteratorWithFilter(BreakIterator* adoptBreakIterator, UErrorCode& status) { + return build(adoptBreakIterator, status); + } + + protected: + /** + * For subclass use + * @stable ICU 56 + */ + FilteredBreakIteratorBuilder(); +}; + + +U_NAMESPACE_END + +#endif // #if !UCONFIG_NO_BREAK_ITERATION && !UCONFIG_NO_FILTERED_BREAK_ITERATION + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // #ifndef FILTEREDBRK_H diff --git a/deps/icu-small/source/common/unicode/icudataver.h b/deps/icu-small/source/common/unicode/icudataver.h index f218ed8ebccbcb..7191478d36ae4a 100644 --- a/deps/icu-small/source/common/unicode/icudataver.h +++ b/deps/icu-small/source/common/unicode/icudataver.h @@ -1,43 +1,43 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2009-2013, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -*/ - - -/** - * \file - * \brief C API: access to ICU Data Version number - */ - -#ifndef __ICU_DATA_VER_H__ -#define __ICU_DATA_VER_H__ - -#include "unicode/utypes.h" - -/** - * @stable ICU 49 - */ -#define U_ICU_VERSION_BUNDLE "icuver" - -/** - * @stable ICU 49 - */ -#define U_ICU_DATA_KEY "DataVersion" - -/** - * Retrieves the data version from icuver and stores it in dataVersionFillin. - * - * @param dataVersionFillin icuver data version information to be filled in if not-null - * @param status stores the error code from the calls to resource bundle - * - * @stable ICU 49 - */ -U_CAPI void U_EXPORT2 u_getDataVersion(UVersionInfo dataVersionFillin, UErrorCode *status); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2009-2013, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +*/ + + +/** + * \file + * \brief C API: access to ICU Data Version number + */ + +#ifndef __ICU_DATA_VER_H__ +#define __ICU_DATA_VER_H__ + +#include "unicode/utypes.h" + +/** + * @stable ICU 49 + */ +#define U_ICU_VERSION_BUNDLE "icuver" + +/** + * @stable ICU 49 + */ +#define U_ICU_DATA_KEY "DataVersion" + +/** + * Retrieves the data version from icuver and stores it in dataVersionFillin. + * + * @param dataVersionFillin icuver data version information to be filled in if not-null + * @param status stores the error code from the calls to resource bundle + * + * @stable ICU 49 + */ +U_CAPI void U_EXPORT2 u_getDataVersion(UVersionInfo dataVersionFillin, UErrorCode *status); + +#endif diff --git a/deps/icu-small/source/common/unicode/icuplug.h b/deps/icu-small/source/common/unicode/icuplug.h index 205af360d45e9e..64aa9e6f2af671 100644 --- a/deps/icu-small/source/common/unicode/icuplug.h +++ b/deps/icu-small/source/common/unicode/icuplug.h @@ -1,387 +1,391 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2009-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* FILE NAME : icuplug.h -* -* Date Name Description -* 10/29/2009 sl New. -****************************************************************************** -*/ - -/** - * \file - * \brief C API: ICU Plugin API - * - *

C API: ICU Plugin API

- * - *

C API allowing run-time loadable modules that extend or modify ICU functionality.

- * - *

Loading and Configuration

- * - *

At ICU startup time, the environment variable "ICU_PLUGINS" will be - * queried for a directory name. If it is not set, the preprocessor symbol - * "DEFAULT_ICU_PLUGINS" will be checked for a default value.

- * - *

Within the above-named directory, the file "icuplugins##.txt" will be - * opened, if present, where ## is the major+minor number of the currently - * running ICU (such as, 44 for ICU 4.4, thus icuplugins44.txt)

- * - *

The configuration file has this format:

- * - *
    - *
  • Hash (#) begins a comment line
  • - * - *
  • Non-comment lines have two or three components: - * LIBRARYNAME ENTRYPOINT [ CONFIGURATION .. ]
  • - * - *
  • Tabs or spaces separate the three items.
  • - * - *
  • LIBRARYNAME is the name of a shared library, either a short name if - * it is on the loader path, or a full pathname.
  • - * - *
  • ENTRYPOINT is the short (undecorated) symbol name of the plugin's - * entrypoint, as above.
  • - * - *
  • CONFIGURATION is the entire rest of the line . It's passed as-is to - * the plugin.
  • - *
- * - *

An example configuration file is, in its entirety:

- * - * \code - * # this is icuplugins44.txt - * testplug.dll myPlugin hello=world - * \endcode - *

Plugins are categorized as "high" or "low" level. Low level are those - * which must be run BEFORE high level plugins, and before any operations - * which cause ICU to be 'initialized'. If a plugin is low level but - * causes ICU to allocate memory or become initialized, that plugin is said - * to cause a 'level change'.

- * - *

At load time, ICU first queries all plugins to determine their level, - * then loads all 'low' plugins first, and then loads all 'high' plugins. - * Plugins are otherwise loaded in the order listed in the configuration file.

- * - *

Implementing a Plugin

- * \code - * U_CAPI UPlugTokenReturn U_EXPORT2 - * myPlugin (UPlugData *plug, UPlugReason reason, UErrorCode *status) { - * if(reason==UPLUG_REASON_QUERY) { - * uplug_setPlugName(plug, "Simple Plugin"); - * uplug_setPlugLevel(plug, UPLUG_LEVEL_HIGH); - * } else if(reason==UPLUG_REASON_LOAD) { - * ... Set up some ICU things here.... - * } else if(reason==UPLUG_REASON_UNLOAD) { - * ... unload, clean up ... - * } - * return UPLUG_TOKEN; - * } - * \endcode - * - *

The UPlugData* is an opaque pointer to the plugin-specific data, and is - * used in all other API calls.

- * - *

The API contract is:

- *
  1. The plugin MUST always return UPLUG_TOKEN as a return value- to - * indicate that it is a valid plugin.
  2. - * - *
  3. When the 'reason' parameter is set to UPLUG_REASON_QUERY, the - * plugin MUST call uplug_setPlugLevel() to indicate whether it is a high - * level or low level plugin.
  4. - * - *
  5. When the 'reason' parameter is UPLUG_REASON_QUERY, the plugin - * SHOULD call uplug_setPlugName to indicate a human readable plugin name.
- * - * - * \internal ICU 4.4 Technology Preview - */ - - -#ifndef ICUPLUG_H -#define ICUPLUG_H - -#include "unicode/utypes.h" - - -#if UCONFIG_ENABLE_PLUGINS || defined(U_IN_DOXYGEN) - - - -/* === Basic types === */ - -#ifndef U_HIDE_INTERNAL_API -struct UPlugData; -/** - * @{ - * Typedef for opaque structure passed to/from a plugin. - * Use the APIs to access it. - * @internal ICU 4.4 Technology Preview - */ -typedef struct UPlugData UPlugData; - -/** @} */ - -/** - * Random Token to identify a valid ICU plugin. Plugins must return this - * from the entrypoint. - * @internal ICU 4.4 Technology Preview - */ -#define UPLUG_TOKEN 0x54762486 - -/** - * Max width of names, symbols, and configuration strings - * @internal ICU 4.4 Technology Preview - */ -#define UPLUG_NAME_MAX 100 - - -/** - * Return value from a plugin entrypoint. - * Must always be set to UPLUG_TOKEN - * @see UPLUG_TOKEN - * @internal ICU 4.4 Technology Preview - */ -typedef uint32_t UPlugTokenReturn; - -/** - * Reason code for the entrypoint's call - * @internal ICU 4.4 Technology Preview - */ -typedef enum { - UPLUG_REASON_QUERY = 0, /**< The plugin is being queried for info. **/ - UPLUG_REASON_LOAD = 1, /**< The plugin is being loaded. **/ - UPLUG_REASON_UNLOAD = 2, /**< The plugin is being unloaded. **/ - /** - * Number of known reasons. - * @internal The numeric value may change over time, see ICU ticket #12420. - */ - UPLUG_REASON_COUNT -} UPlugReason; - - -/** - * Level of plugin loading - * INITIAL: UNKNOWN - * QUERY: INVALID -> { LOW | HIGH } - * ERR -> INVALID - * @internal ICU 4.4 Technology Preview - */ -typedef enum { - UPLUG_LEVEL_INVALID = 0, /**< The plugin is invalid, hasn't called uplug_setLevel, or can't load. **/ - UPLUG_LEVEL_UNKNOWN = 1, /**< The plugin is waiting to be installed. **/ - UPLUG_LEVEL_LOW = 2, /**< The plugin must be called before u_init completes **/ - UPLUG_LEVEL_HIGH = 3, /**< The plugin can run at any time. **/ - /** - * Number of known levels. - * @internal The numeric value may change over time, see ICU ticket #12420. - */ - UPLUG_LEVEL_COUNT -} UPlugLevel; - -/** - * Entrypoint for an ICU plugin. - * @param plug the UPlugData handle. - * @param status the plugin's extended status code. - * @return A valid plugin must return UPLUG_TOKEN - * @internal ICU 4.4 Technology Preview - */ -typedef UPlugTokenReturn (U_EXPORT2 UPlugEntrypoint) ( - UPlugData *plug, - UPlugReason reason, - UErrorCode *status); - -/* === Needed for Implementing === */ - -/** - * Request that this plugin not be unloaded at cleanup time. - * This is appropriate for plugins which cannot be cleaned up. - * @see u_cleanup() - * @param plug plugin - * @param dontUnload set true if this plugin can't be unloaded - * @internal ICU 4.4 Technology Preview - */ -U_CAPI void U_EXPORT2 -uplug_setPlugNoUnload(UPlugData *plug, UBool dontUnload); - -/** - * Set the level of this plugin. - * @param plug plugin data handle - * @param level the level of this plugin - * @internal ICU 4.4 Technology Preview - */ -U_CAPI void U_EXPORT2 -uplug_setPlugLevel(UPlugData *plug, UPlugLevel level); - -/** - * Get the level of this plugin. - * @param plug plugin data handle - * @return the level of this plugin - * @internal ICU 4.4 Technology Preview - */ -U_CAPI UPlugLevel U_EXPORT2 -uplug_getPlugLevel(UPlugData *plug); - -/** - * Get the lowest level of plug which can currently load. - * For example, if UPLUG_LEVEL_LOW is returned, then low level plugins may load - * if UPLUG_LEVEL_HIGH is returned, then only high level plugins may load. - * @return the lowest level of plug which can currently load - * @internal ICU 4.4 Technology Preview - */ -U_CAPI UPlugLevel U_EXPORT2 -uplug_getCurrentLevel(void); - - -/** - * Get plug load status - * @return The error code of this plugin's load attempt. - * @internal ICU 4.4 Technology Preview - */ -U_CAPI UErrorCode U_EXPORT2 -uplug_getPlugLoadStatus(UPlugData *plug); - -/** - * Set the human-readable name of this plugin. - * @param plug plugin data handle - * @param name the name of this plugin. The first UPLUG_NAME_MAX characters willi be copied into a new buffer. - * @internal ICU 4.4 Technology Preview - */ -U_CAPI void U_EXPORT2 -uplug_setPlugName(UPlugData *plug, const char *name); - -/** - * Get the human-readable name of this plugin. - * @param plug plugin data handle - * @return the name of this plugin - * @internal ICU 4.4 Technology Preview - */ -U_CAPI const char * U_EXPORT2 -uplug_getPlugName(UPlugData *plug); - -/** - * Return the symbol name for this plugin, if known. - * @param plug plugin data handle - * @return the symbol name, or NULL - * @internal ICU 4.4 Technology Preview - */ -U_CAPI const char * U_EXPORT2 -uplug_getSymbolName(UPlugData *plug); - -/** - * Return the library name for this plugin, if known. - * @param plug plugin data handle - * @param status error code - * @return the library name, or NULL - * @internal ICU 4.4 Technology Preview - */ -U_CAPI const char * U_EXPORT2 -uplug_getLibraryName(UPlugData *plug, UErrorCode *status); - -/** - * Return the library used for this plugin, if known. - * Plugins could use this to load data out of their - * @param plug plugin data handle - * @return the library, or NULL - * @internal ICU 4.4 Technology Preview - */ -U_CAPI void * U_EXPORT2 -uplug_getLibrary(UPlugData *plug); - -/** - * Return the plugin-specific context data. - * @param plug plugin data handle - * @return the context, or NULL if not set - * @internal ICU 4.4 Technology Preview - */ -U_CAPI void * U_EXPORT2 -uplug_getContext(UPlugData *plug); - -/** - * Set the plugin-specific context data. - * @param plug plugin data handle - * @param context new context to set - * @internal ICU 4.4 Technology Preview - */ -U_CAPI void U_EXPORT2 -uplug_setContext(UPlugData *plug, void *context); - - -/** - * Get the configuration string, if available. - * The string is in the platform default codepage. - * @param plug plugin data handle - * @return configuration string, or else null. - * @internal ICU 4.4 Technology Preview - */ -U_CAPI const char * U_EXPORT2 -uplug_getConfiguration(UPlugData *plug); - -/** - * Return all currently installed plugins, from newest to oldest - * Usage Example: - * \code - * UPlugData *plug = NULL; - * while(plug=uplug_nextPlug(plug)) { - * ... do something with 'plug' ... - * } - * \endcode - * Not thread safe- do not call while plugs are added or removed. - * @param prior pass in 'NULL' to get the first (most recent) plug, - * otherwise pass the value returned on a prior call to uplug_nextPlug - * @return the next oldest plugin, or NULL if no more. - * @internal ICU 4.4 Technology Preview - */ -U_CAPI UPlugData* U_EXPORT2 -uplug_nextPlug(UPlugData *prior); - -/** - * Inject a plugin as if it were loaded from a library. - * This is useful for testing plugins. - * Note that it will have a 'NULL' library pointer associated - * with it, and therefore no llibrary will be closed at cleanup time. - * Low level plugins may not be able to load, as ordering can't be enforced. - * @param entrypoint entrypoint to install - * @param config user specified configuration string, if available, or NULL. - * @param status error result - * @return the new UPlugData associated with this plugin, or NULL if error. - * @internal ICU 4.4 Technology Preview - */ -U_CAPI UPlugData* U_EXPORT2 -uplug_loadPlugFromEntrypoint(UPlugEntrypoint *entrypoint, const char *config, UErrorCode *status); - - -/** - * Inject a plugin from a library, as if the information came from a config file. - * Low level plugins may not be able to load, and ordering can't be enforced. - * @param libName DLL name to load - * @param sym symbol of plugin (UPlugEntrypoint function) - * @param config configuration string, or NULL - * @param status error result - * @return the new UPlugData associated with this plugin, or NULL if error. - * @internal ICU 4.4 Technology Preview - */ -U_CAPI UPlugData* U_EXPORT2 -uplug_loadPlugFromLibrary(const char *libName, const char *sym, const char *config, UErrorCode *status); - -/** - * Remove a plugin. - * Will request the plugin to be unloaded, and close the library if needed - * @param plug plugin handle to close - * @param status error result - * @internal ICU 4.4 Technology Preview - */ -U_CAPI void U_EXPORT2 -uplug_removePlug(UPlugData *plug, UErrorCode *status); -#endif /* U_HIDE_INTERNAL_API */ - -#endif /* UCONFIG_ENABLE_PLUGINS */ - -#endif /* _ICUPLUG */ - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2009-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* FILE NAME : icuplug.h +* +* Date Name Description +* 10/29/2009 sl New. +****************************************************************************** +*/ + +/** + * \file + * \brief C API: ICU Plugin API + * + *

C API: ICU Plugin API

+ * + *

C API allowing run-time loadable modules that extend or modify ICU functionality.

+ * + *

Loading and Configuration

+ * + *

At ICU startup time, the environment variable "ICU_PLUGINS" will be + * queried for a directory name. If it is not set, the preprocessor symbol + * "DEFAULT_ICU_PLUGINS" will be checked for a default value.

+ * + *

Within the above-named directory, the file "icuplugins##.txt" will be + * opened, if present, where ## is the major+minor number of the currently + * running ICU (such as, 44 for ICU 4.4, thus icuplugins44.txt)

+ * + *

The configuration file has this format:

+ * + *
    + *
  • Hash (#) begins a comment line
  • + * + *
  • Non-comment lines have two or three components: + * LIBRARYNAME ENTRYPOINT [ CONFIGURATION .. ]
  • + * + *
  • Tabs or spaces separate the three items.
  • + * + *
  • LIBRARYNAME is the name of a shared library, either a short name if + * it is on the loader path, or a full pathname.
  • + * + *
  • ENTRYPOINT is the short (undecorated) symbol name of the plugin's + * entrypoint, as above.
  • + * + *
  • CONFIGURATION is the entire rest of the line . It's passed as-is to + * the plugin.
  • + *
+ * + *

An example configuration file is, in its entirety:

+ * + * \code + * # this is icuplugins44.txt + * testplug.dll myPlugin hello=world + * \endcode + *

Plugins are categorized as "high" or "low" level. Low level are those + * which must be run BEFORE high level plugins, and before any operations + * which cause ICU to be 'initialized'. If a plugin is low level but + * causes ICU to allocate memory or become initialized, that plugin is said + * to cause a 'level change'.

+ * + *

At load time, ICU first queries all plugins to determine their level, + * then loads all 'low' plugins first, and then loads all 'high' plugins. + * Plugins are otherwise loaded in the order listed in the configuration file.

+ * + *

Implementing a Plugin

+ * \code + * U_CAPI UPlugTokenReturn U_EXPORT2 + * myPlugin (UPlugData *plug, UPlugReason reason, UErrorCode *status) { + * if(reason==UPLUG_REASON_QUERY) { + * uplug_setPlugName(plug, "Simple Plugin"); + * uplug_setPlugLevel(plug, UPLUG_LEVEL_HIGH); + * } else if(reason==UPLUG_REASON_LOAD) { + * ... Set up some ICU things here.... + * } else if(reason==UPLUG_REASON_UNLOAD) { + * ... unload, clean up ... + * } + * return UPLUG_TOKEN; + * } + * \endcode + * + *

The UPlugData* is an opaque pointer to the plugin-specific data, and is + * used in all other API calls.

+ * + *

The API contract is:

+ *
  1. The plugin MUST always return UPLUG_TOKEN as a return value- to + * indicate that it is a valid plugin.
  2. + * + *
  3. When the 'reason' parameter is set to UPLUG_REASON_QUERY, the + * plugin MUST call uplug_setPlugLevel() to indicate whether it is a high + * level or low level plugin.
  4. + * + *
  5. When the 'reason' parameter is UPLUG_REASON_QUERY, the plugin + * SHOULD call uplug_setPlugName to indicate a human readable plugin name.
+ * + * + * \internal ICU 4.4 Technology Preview + */ + + +#ifndef ICUPLUG_H +#define ICUPLUG_H + +#include "unicode/utypes.h" + + +#if UCONFIG_ENABLE_PLUGINS || defined(U_IN_DOXYGEN) + + + +/* === Basic types === */ + +#ifndef U_HIDE_INTERNAL_API +struct UPlugData; +/** + * @{ + * Typedef for opaque structure passed to/from a plugin. + * Use the APIs to access it. + * @internal ICU 4.4 Technology Preview + */ +typedef struct UPlugData UPlugData; + +/** @} */ + +/** + * Random Token to identify a valid ICU plugin. Plugins must return this + * from the entrypoint. + * @internal ICU 4.4 Technology Preview + */ +#define UPLUG_TOKEN 0x54762486 + +/** + * Max width of names, symbols, and configuration strings + * @internal ICU 4.4 Technology Preview + */ +#define UPLUG_NAME_MAX 100 + + +/** + * Return value from a plugin entrypoint. + * Must always be set to UPLUG_TOKEN + * @see UPLUG_TOKEN + * @internal ICU 4.4 Technology Preview + */ +typedef uint32_t UPlugTokenReturn; + +/** + * Reason code for the entrypoint's call + * @internal ICU 4.4 Technology Preview + */ +typedef enum { + UPLUG_REASON_QUERY = 0, /**< The plugin is being queried for info. **/ + UPLUG_REASON_LOAD = 1, /**< The plugin is being loaded. **/ + UPLUG_REASON_UNLOAD = 2, /**< The plugin is being unloaded. **/ + /** + * Number of known reasons. + * @internal The numeric value may change over time, see ICU ticket #12420. + */ + UPLUG_REASON_COUNT +} UPlugReason; + + +/** + * Level of plugin loading + * INITIAL: UNKNOWN + * QUERY: INVALID -> { LOW | HIGH } + * ERR -> INVALID + * @internal ICU 4.4 Technology Preview + */ +typedef enum { + UPLUG_LEVEL_INVALID = 0, /**< The plugin is invalid, hasn't called uplug_setLevel, or can't load. **/ + UPLUG_LEVEL_UNKNOWN = 1, /**< The plugin is waiting to be installed. **/ + UPLUG_LEVEL_LOW = 2, /**< The plugin must be called before u_init completes **/ + UPLUG_LEVEL_HIGH = 3, /**< The plugin can run at any time. **/ + /** + * Number of known levels. + * @internal The numeric value may change over time, see ICU ticket #12420. + */ + UPLUG_LEVEL_COUNT +} UPlugLevel; + +/** + * Entrypoint for an ICU plugin. + * @param plug the UPlugData handle. + * @param reason the reason code for the entrypoint's call. + * @param status Standard 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.) + * @return A valid plugin must return UPLUG_TOKEN + * @internal ICU 4.4 Technology Preview + */ +typedef UPlugTokenReturn (U_EXPORT2 UPlugEntrypoint) ( + UPlugData *plug, + UPlugReason reason, + UErrorCode *status); + +/* === Needed for Implementing === */ + +/** + * Request that this plugin not be unloaded at cleanup time. + * This is appropriate for plugins which cannot be cleaned up. + * @see u_cleanup() + * @param plug plugin + * @param dontUnload set true if this plugin can't be unloaded + * @internal ICU 4.4 Technology Preview + */ +U_CAPI void U_EXPORT2 +uplug_setPlugNoUnload(UPlugData *plug, UBool dontUnload); + +/** + * Set the level of this plugin. + * @param plug plugin data handle + * @param level the level of this plugin + * @internal ICU 4.4 Technology Preview + */ +U_CAPI void U_EXPORT2 +uplug_setPlugLevel(UPlugData *plug, UPlugLevel level); + +/** + * Get the level of this plugin. + * @param plug plugin data handle + * @return the level of this plugin + * @internal ICU 4.4 Technology Preview + */ +U_CAPI UPlugLevel U_EXPORT2 +uplug_getPlugLevel(UPlugData *plug); + +/** + * Get the lowest level of plug which can currently load. + * For example, if UPLUG_LEVEL_LOW is returned, then low level plugins may load + * if UPLUG_LEVEL_HIGH is returned, then only high level plugins may load. + * @return the lowest level of plug which can currently load + * @internal ICU 4.4 Technology Preview + */ +U_CAPI UPlugLevel U_EXPORT2 +uplug_getCurrentLevel(void); + + +/** + * Get plug load status + * @return The error code of this plugin's load attempt. + * @internal ICU 4.4 Technology Preview + */ +U_CAPI UErrorCode U_EXPORT2 +uplug_getPlugLoadStatus(UPlugData *plug); + +/** + * Set the human-readable name of this plugin. + * @param plug plugin data handle + * @param name the name of this plugin. The first UPLUG_NAME_MAX characters willi be copied into a new buffer. + * @internal ICU 4.4 Technology Preview + */ +U_CAPI void U_EXPORT2 +uplug_setPlugName(UPlugData *plug, const char *name); + +/** + * Get the human-readable name of this plugin. + * @param plug plugin data handle + * @return the name of this plugin + * @internal ICU 4.4 Technology Preview + */ +U_CAPI const char * U_EXPORT2 +uplug_getPlugName(UPlugData *plug); + +/** + * Return the symbol name for this plugin, if known. + * @param plug plugin data handle + * @return the symbol name, or NULL + * @internal ICU 4.4 Technology Preview + */ +U_CAPI const char * U_EXPORT2 +uplug_getSymbolName(UPlugData *plug); + +/** + * Return the library name for this plugin, if known. + * @param plug plugin data handle + * @param status error code + * @return the library name, or NULL + * @internal ICU 4.4 Technology Preview + */ +U_CAPI const char * U_EXPORT2 +uplug_getLibraryName(UPlugData *plug, UErrorCode *status); + +/** + * Return the library used for this plugin, if known. + * Plugins could use this to load data out of their + * @param plug plugin data handle + * @return the library, or NULL + * @internal ICU 4.4 Technology Preview + */ +U_CAPI void * U_EXPORT2 +uplug_getLibrary(UPlugData *plug); + +/** + * Return the plugin-specific context data. + * @param plug plugin data handle + * @return the context, or NULL if not set + * @internal ICU 4.4 Technology Preview + */ +U_CAPI void * U_EXPORT2 +uplug_getContext(UPlugData *plug); + +/** + * Set the plugin-specific context data. + * @param plug plugin data handle + * @param context new context to set + * @internal ICU 4.4 Technology Preview + */ +U_CAPI void U_EXPORT2 +uplug_setContext(UPlugData *plug, void *context); + + +/** + * Get the configuration string, if available. + * The string is in the platform default codepage. + * @param plug plugin data handle + * @return configuration string, or else null. + * @internal ICU 4.4 Technology Preview + */ +U_CAPI const char * U_EXPORT2 +uplug_getConfiguration(UPlugData *plug); + +/** + * Return all currently installed plugins, from newest to oldest + * Usage Example: + * \code + * UPlugData *plug = NULL; + * while(plug=uplug_nextPlug(plug)) { + * ... do something with 'plug' ... + * } + * \endcode + * Not thread safe- do not call while plugs are added or removed. + * @param prior pass in 'NULL' to get the first (most recent) plug, + * otherwise pass the value returned on a prior call to uplug_nextPlug + * @return the next oldest plugin, or NULL if no more. + * @internal ICU 4.4 Technology Preview + */ +U_CAPI UPlugData* U_EXPORT2 +uplug_nextPlug(UPlugData *prior); + +/** + * Inject a plugin as if it were loaded from a library. + * This is useful for testing plugins. + * Note that it will have a 'NULL' library pointer associated + * with it, and therefore no llibrary will be closed at cleanup time. + * Low level plugins may not be able to load, as ordering can't be enforced. + * @param entrypoint entrypoint to install + * @param config user specified configuration string, if available, or NULL. + * @param status error result + * @return the new UPlugData associated with this plugin, or NULL if error. + * @internal ICU 4.4 Technology Preview + */ +U_CAPI UPlugData* U_EXPORT2 +uplug_loadPlugFromEntrypoint(UPlugEntrypoint *entrypoint, const char *config, UErrorCode *status); + + +/** + * Inject a plugin from a library, as if the information came from a config file. + * Low level plugins may not be able to load, and ordering can't be enforced. + * @param libName DLL name to load + * @param sym symbol of plugin (UPlugEntrypoint function) + * @param config configuration string, or NULL + * @param status error result + * @return the new UPlugData associated with this plugin, or NULL if error. + * @internal ICU 4.4 Technology Preview + */ +U_CAPI UPlugData* U_EXPORT2 +uplug_loadPlugFromLibrary(const char *libName, const char *sym, const char *config, UErrorCode *status); + +/** + * Remove a plugin. + * Will request the plugin to be unloaded, and close the library if needed + * @param plug plugin handle to close + * @param status error result + * @internal ICU 4.4 Technology Preview + */ +U_CAPI void U_EXPORT2 +uplug_removePlug(UPlugData *plug, UErrorCode *status); +#endif /* U_HIDE_INTERNAL_API */ + +#endif /* UCONFIG_ENABLE_PLUGINS */ + +#endif /* _ICUPLUG */ + diff --git a/deps/icu-small/source/common/unicode/idna.h b/deps/icu-small/source/common/unicode/idna.h index 1c57205bae2ef4..e07fa20cf362ee 100644 --- a/deps/icu-small/source/common/unicode/idna.h +++ b/deps/icu-small/source/common/unicode/idna.h @@ -1,330 +1,330 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2012, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: idna.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010mar05 -* created by: Markus W. Scherer -*/ - -#ifndef __IDNA_H__ -#define __IDNA_H__ - -/** - * \file - * \brief C++ API: Internationalizing Domain Names in Applications (IDNA) - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#if !UCONFIG_NO_IDNA - -#include "unicode/bytestream.h" -#include "unicode/stringpiece.h" -#include "unicode/uidna.h" -#include "unicode/unistr.h" - -U_NAMESPACE_BEGIN - -class IDNAInfo; - -/** - * Abstract base class for IDNA processing. - * See http://www.unicode.org/reports/tr46/ - * and http://www.ietf.org/rfc/rfc3490.txt - * - * The IDNA class is not intended for public subclassing. - * - * This C++ API currently only implements UTS #46. - * The uidna.h C API implements both UTS #46 (functions using UIDNA service object) - * and IDNA2003 (functions that do not use a service object). - * @stable ICU 4.6 - */ -class U_COMMON_API IDNA : public UObject { -public: - /** - * Destructor. - * @stable ICU 4.6 - */ - ~IDNA(); - - /** - * Returns an IDNA instance which implements UTS #46. - * Returns an unmodifiable instance, owned by the caller. - * Cache it for multiple operations, and delete it when done. - * The instance is thread-safe, that is, it can be used concurrently. - * - * UTS #46 defines Unicode IDNA Compatibility Processing, - * updated to the latest version of Unicode and compatible with both - * IDNA2003 and IDNA2008. - * - * The worker functions use transitional processing, including deviation mappings, - * unless UIDNA_NONTRANSITIONAL_TO_ASCII or UIDNA_NONTRANSITIONAL_TO_UNICODE - * is used in which case the deviation characters are passed through without change. - * - * Disallowed characters are mapped to U+FFFD. - * - * For available options see the uidna.h header. - * Operations with the UTS #46 instance do not support the - * UIDNA_ALLOW_UNASSIGNED option. - * - * By default, the UTS #46 implementation allows all ASCII characters (as valid or mapped). - * When the UIDNA_USE_STD3_RULES option is used, ASCII characters other than - * letters, digits, hyphen (LDH) and dot/full stop are disallowed and mapped to U+FFFD. - * - * @param options Bit set to modify the processing and error checking. - * See option bit set values in uidna.h. - * @param errorCode Standard 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.) - * @return the UTS #46 IDNA instance, if successful - * @stable ICU 4.6 - */ - static IDNA * - createUTS46Instance(uint32_t options, UErrorCode &errorCode); - - /** - * Converts a single domain name label into its ASCII form for DNS lookup. - * If any processing step fails, then info.hasErrors() will be true and - * the result might not be an ASCII string. - * The label might be modified according to the types of errors. - * Labels with severe errors will be left in (or turned into) their Unicode form. - * - * The UErrorCode indicates an error only in exceptional cases, - * such as a U_MEMORY_ALLOCATION_ERROR. - * - * @param label Input domain name label - * @param dest Destination string object - * @param info Output container of IDNA processing details. - * @param errorCode Standard 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.) - * @return dest - * @stable ICU 4.6 - */ - virtual UnicodeString & - labelToASCII(const UnicodeString &label, UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const = 0; - - /** - * Converts a single domain name label into its Unicode form for human-readable display. - * If any processing step fails, then info.hasErrors() will be true. - * The label might be modified according to the types of errors. - * - * The UErrorCode indicates an error only in exceptional cases, - * such as a U_MEMORY_ALLOCATION_ERROR. - * - * @param label Input domain name label - * @param dest Destination string object - * @param info Output container of IDNA processing details. - * @param errorCode Standard 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.) - * @return dest - * @stable ICU 4.6 - */ - virtual UnicodeString & - labelToUnicode(const UnicodeString &label, UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const = 0; - - /** - * Converts a whole domain name into its ASCII form for DNS lookup. - * If any processing step fails, then info.hasErrors() will be true and - * the result might not be an ASCII string. - * The domain name might be modified according to the types of errors. - * Labels with severe errors will be left in (or turned into) their Unicode form. - * - * The UErrorCode indicates an error only in exceptional cases, - * such as a U_MEMORY_ALLOCATION_ERROR. - * - * @param name Input domain name - * @param dest Destination string object - * @param info Output container of IDNA processing details. - * @param errorCode Standard 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.) - * @return dest - * @stable ICU 4.6 - */ - virtual UnicodeString & - nameToASCII(const UnicodeString &name, UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const = 0; - - /** - * Converts a whole domain name into its Unicode form for human-readable display. - * If any processing step fails, then info.hasErrors() will be true. - * The domain name might be modified according to the types of errors. - * - * The UErrorCode indicates an error only in exceptional cases, - * such as a U_MEMORY_ALLOCATION_ERROR. - * - * @param name Input domain name - * @param dest Destination string object - * @param info Output container of IDNA processing details. - * @param errorCode Standard 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.) - * @return dest - * @stable ICU 4.6 - */ - virtual UnicodeString & - nameToUnicode(const UnicodeString &name, UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const = 0; - - // UTF-8 versions of the processing methods ---------------------------- *** - - /** - * Converts a single domain name label into its ASCII form for DNS lookup. - * UTF-8 version of labelToASCII(), same behavior. - * - * @param label Input domain name label - * @param dest Destination byte sink; Flush()ed if successful - * @param info Output container of IDNA processing details. - * @param errorCode Standard 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.) - * @return dest - * @stable ICU 4.6 - */ - virtual void - labelToASCII_UTF8(StringPiece label, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const; - - /** - * Converts a single domain name label into its Unicode form for human-readable display. - * UTF-8 version of labelToUnicode(), same behavior. - * - * @param label Input domain name label - * @param dest Destination byte sink; Flush()ed if successful - * @param info Output container of IDNA processing details. - * @param errorCode Standard 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.) - * @return dest - * @stable ICU 4.6 - */ - virtual void - labelToUnicodeUTF8(StringPiece label, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const; - - /** - * Converts a whole domain name into its ASCII form for DNS lookup. - * UTF-8 version of nameToASCII(), same behavior. - * - * @param name Input domain name - * @param dest Destination byte sink; Flush()ed if successful - * @param info Output container of IDNA processing details. - * @param errorCode Standard 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.) - * @return dest - * @stable ICU 4.6 - */ - virtual void - nameToASCII_UTF8(StringPiece name, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const; - - /** - * Converts a whole domain name into its Unicode form for human-readable display. - * UTF-8 version of nameToUnicode(), same behavior. - * - * @param name Input domain name - * @param dest Destination byte sink; Flush()ed if successful - * @param info Output container of IDNA processing details. - * @param errorCode Standard 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.) - * @return dest - * @stable ICU 4.6 - */ - virtual void - nameToUnicodeUTF8(StringPiece name, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const; -}; - -class UTS46; - -/** - * Output container for IDNA processing errors. - * The IDNAInfo class is not suitable for subclassing. - * @stable ICU 4.6 - */ -class U_COMMON_API IDNAInfo : public UMemory { -public: - /** - * Constructor for stack allocation. - * @stable ICU 4.6 - */ - IDNAInfo() : errors(0), labelErrors(0), isTransDiff(false), isBiDi(false), isOkBiDi(true) {} - /** - * Were there IDNA processing errors? - * @return true if there were processing errors - * @stable ICU 4.6 - */ - UBool hasErrors() const { return errors!=0; } - /** - * Returns a bit set indicating IDNA processing errors. - * See UIDNA_ERROR_... constants in uidna.h. - * @return bit set of processing errors - * @stable ICU 4.6 - */ - uint32_t getErrors() const { return errors; } - /** - * Returns true if transitional and nontransitional processing produce different results. - * This is the case when the input label or domain name contains - * one or more deviation characters outside a Punycode label (see UTS #46). - *
    - *
  • With nontransitional processing, such characters are - * copied to the destination string. - *
  • With transitional processing, such characters are - * mapped (sharp s/sigma) or removed (joiner/nonjoiner). - *
- * @return true if transitional and nontransitional processing produce different results - * @stable ICU 4.6 - */ - UBool isTransitionalDifferent() const { return isTransDiff; } - -private: - friend class UTS46; - - IDNAInfo(const IDNAInfo &other) = delete; // no copying - IDNAInfo &operator=(const IDNAInfo &other) = delete; // no copying - - void reset() { - errors=labelErrors=0; - isTransDiff=false; - isBiDi=false; - isOkBiDi=true; - } - - uint32_t errors, labelErrors; - UBool isTransDiff; - UBool isBiDi; - UBool isOkBiDi; -}; - -U_NAMESPACE_END - -#endif // UCONFIG_NO_IDNA - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __IDNA_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2012, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: idna.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010mar05 +* created by: Markus W. Scherer +*/ + +#ifndef __IDNA_H__ +#define __IDNA_H__ + +/** + * \file + * \brief C++ API: Internationalizing Domain Names in Applications (IDNA) + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#if !UCONFIG_NO_IDNA + +#include "unicode/bytestream.h" +#include "unicode/stringpiece.h" +#include "unicode/uidna.h" +#include "unicode/unistr.h" + +U_NAMESPACE_BEGIN + +class IDNAInfo; + +/** + * Abstract base class for IDNA processing. + * See http://www.unicode.org/reports/tr46/ + * and http://www.ietf.org/rfc/rfc3490.txt + * + * The IDNA class is not intended for public subclassing. + * + * This C++ API currently only implements UTS #46. + * The uidna.h C API implements both UTS #46 (functions using UIDNA service object) + * and IDNA2003 (functions that do not use a service object). + * @stable ICU 4.6 + */ +class U_COMMON_API IDNA : public UObject { +public: + /** + * Destructor. + * @stable ICU 4.6 + */ + ~IDNA(); + + /** + * Returns an IDNA instance which implements UTS #46. + * Returns an unmodifiable instance, owned by the caller. + * Cache it for multiple operations, and delete it when done. + * The instance is thread-safe, that is, it can be used concurrently. + * + * UTS #46 defines Unicode IDNA Compatibility Processing, + * updated to the latest version of Unicode and compatible with both + * IDNA2003 and IDNA2008. + * + * The worker functions use transitional processing, including deviation mappings, + * unless UIDNA_NONTRANSITIONAL_TO_ASCII or UIDNA_NONTRANSITIONAL_TO_UNICODE + * is used in which case the deviation characters are passed through without change. + * + * Disallowed characters are mapped to U+FFFD. + * + * For available options see the uidna.h header. + * Operations with the UTS #46 instance do not support the + * UIDNA_ALLOW_UNASSIGNED option. + * + * By default, the UTS #46 implementation allows all ASCII characters (as valid or mapped). + * When the UIDNA_USE_STD3_RULES option is used, ASCII characters other than + * letters, digits, hyphen (LDH) and dot/full stop are disallowed and mapped to U+FFFD. + * + * @param options Bit set to modify the processing and error checking. + * See option bit set values in uidna.h. + * @param errorCode Standard 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.) + * @return the UTS #46 IDNA instance, if successful + * @stable ICU 4.6 + */ + static IDNA * + createUTS46Instance(uint32_t options, UErrorCode &errorCode); + + /** + * Converts a single domain name label into its ASCII form for DNS lookup. + * If any processing step fails, then info.hasErrors() will be true and + * the result might not be an ASCII string. + * The label might be modified according to the types of errors. + * Labels with severe errors will be left in (or turned into) their Unicode form. + * + * The UErrorCode indicates an error only in exceptional cases, + * such as a U_MEMORY_ALLOCATION_ERROR. + * + * @param label Input domain name label + * @param dest Destination string object + * @param info Output container of IDNA processing details. + * @param errorCode Standard 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.) + * @return dest + * @stable ICU 4.6 + */ + virtual UnicodeString & + labelToASCII(const UnicodeString &label, UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const = 0; + + /** + * Converts a single domain name label into its Unicode form for human-readable display. + * If any processing step fails, then info.hasErrors() will be true. + * The label might be modified according to the types of errors. + * + * The UErrorCode indicates an error only in exceptional cases, + * such as a U_MEMORY_ALLOCATION_ERROR. + * + * @param label Input domain name label + * @param dest Destination string object + * @param info Output container of IDNA processing details. + * @param errorCode Standard 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.) + * @return dest + * @stable ICU 4.6 + */ + virtual UnicodeString & + labelToUnicode(const UnicodeString &label, UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const = 0; + + /** + * Converts a whole domain name into its ASCII form for DNS lookup. + * If any processing step fails, then info.hasErrors() will be true and + * the result might not be an ASCII string. + * The domain name might be modified according to the types of errors. + * Labels with severe errors will be left in (or turned into) their Unicode form. + * + * The UErrorCode indicates an error only in exceptional cases, + * such as a U_MEMORY_ALLOCATION_ERROR. + * + * @param name Input domain name + * @param dest Destination string object + * @param info Output container of IDNA processing details. + * @param errorCode Standard 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.) + * @return dest + * @stable ICU 4.6 + */ + virtual UnicodeString & + nameToASCII(const UnicodeString &name, UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const = 0; + + /** + * Converts a whole domain name into its Unicode form for human-readable display. + * If any processing step fails, then info.hasErrors() will be true. + * The domain name might be modified according to the types of errors. + * + * The UErrorCode indicates an error only in exceptional cases, + * such as a U_MEMORY_ALLOCATION_ERROR. + * + * @param name Input domain name + * @param dest Destination string object + * @param info Output container of IDNA processing details. + * @param errorCode Standard 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.) + * @return dest + * @stable ICU 4.6 + */ + virtual UnicodeString & + nameToUnicode(const UnicodeString &name, UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const = 0; + + // UTF-8 versions of the processing methods ---------------------------- *** + + /** + * Converts a single domain name label into its ASCII form for DNS lookup. + * UTF-8 version of labelToASCII(), same behavior. + * + * @param label Input domain name label + * @param dest Destination byte sink; Flush()ed if successful + * @param info Output container of IDNA processing details. + * @param errorCode Standard 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.) + * @return dest + * @stable ICU 4.6 + */ + virtual void + labelToASCII_UTF8(StringPiece label, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const; + + /** + * Converts a single domain name label into its Unicode form for human-readable display. + * UTF-8 version of labelToUnicode(), same behavior. + * + * @param label Input domain name label + * @param dest Destination byte sink; Flush()ed if successful + * @param info Output container of IDNA processing details. + * @param errorCode Standard 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.) + * @return dest + * @stable ICU 4.6 + */ + virtual void + labelToUnicodeUTF8(StringPiece label, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const; + + /** + * Converts a whole domain name into its ASCII form for DNS lookup. + * UTF-8 version of nameToASCII(), same behavior. + * + * @param name Input domain name + * @param dest Destination byte sink; Flush()ed if successful + * @param info Output container of IDNA processing details. + * @param errorCode Standard 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.) + * @return dest + * @stable ICU 4.6 + */ + virtual void + nameToASCII_UTF8(StringPiece name, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const; + + /** + * Converts a whole domain name into its Unicode form for human-readable display. + * UTF-8 version of nameToUnicode(), same behavior. + * + * @param name Input domain name + * @param dest Destination byte sink; Flush()ed if successful + * @param info Output container of IDNA processing details. + * @param errorCode Standard 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.) + * @return dest + * @stable ICU 4.6 + */ + virtual void + nameToUnicodeUTF8(StringPiece name, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const; +}; + +class UTS46; + +/** + * Output container for IDNA processing errors. + * The IDNAInfo class is not suitable for subclassing. + * @stable ICU 4.6 + */ +class U_COMMON_API IDNAInfo : public UMemory { +public: + /** + * Constructor for stack allocation. + * @stable ICU 4.6 + */ + IDNAInfo() : errors(0), labelErrors(0), isTransDiff(false), isBiDi(false), isOkBiDi(true) {} + /** + * Were there IDNA processing errors? + * @return true if there were processing errors + * @stable ICU 4.6 + */ + UBool hasErrors() const { return errors!=0; } + /** + * Returns a bit set indicating IDNA processing errors. + * See UIDNA_ERROR_... constants in uidna.h. + * @return bit set of processing errors + * @stable ICU 4.6 + */ + uint32_t getErrors() const { return errors; } + /** + * Returns true if transitional and nontransitional processing produce different results. + * This is the case when the input label or domain name contains + * one or more deviation characters outside a Punycode label (see UTS #46). + *
    + *
  • With nontransitional processing, such characters are + * copied to the destination string. + *
  • With transitional processing, such characters are + * mapped (sharp s/sigma) or removed (joiner/nonjoiner). + *
+ * @return true if transitional and nontransitional processing produce different results + * @stable ICU 4.6 + */ + UBool isTransitionalDifferent() const { return isTransDiff; } + +private: + friend class UTS46; + + IDNAInfo(const IDNAInfo &other) = delete; // no copying + IDNAInfo &operator=(const IDNAInfo &other) = delete; // no copying + + void reset() { + errors=labelErrors=0; + isTransDiff=false; + isBiDi=false; + isOkBiDi=true; + } + + uint32_t errors, labelErrors; + UBool isTransDiff; + UBool isBiDi; + UBool isOkBiDi; +}; + +U_NAMESPACE_END + +#endif // UCONFIG_NO_IDNA + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __IDNA_H__ diff --git a/deps/icu-small/source/common/unicode/localebuilder.h b/deps/icu-small/source/common/unicode/localebuilder.h index f708a7ed7c4cb3..6d0771f02f114e 100644 --- a/deps/icu-small/source/common/unicode/localebuilder.h +++ b/deps/icu-small/source/common/unicode/localebuilder.h @@ -1,309 +1,309 @@ -// © 2018 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -#ifndef __LOCALEBUILDER_H__ -#define __LOCALEBUILDER_H__ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/locid.h" -#include "unicode/localematcher.h" -#include "unicode/stringpiece.h" -#include "unicode/uobject.h" - -/** - * \file - * \brief C++ API: Builder API for Locale - */ - -U_NAMESPACE_BEGIN -class CharString; - -/** - * LocaleBuilder is used to build instances of Locale - * from values configured by the setters. Unlike the Locale - * constructors, the LocaleBuilder checks if a value configured by a - * setter satisfies the syntax requirements defined by the Locale - * class. A Locale object created by a LocaleBuilder is - * well-formed and can be transformed to a well-formed IETF BCP 47 language tag - * without losing information. - * - *

The following example shows how to create a Locale object - * with the LocaleBuilder. - *

- *
- *     UErrorCode status = U_ZERO_ERROR;
- *     Locale aLocale = LocaleBuilder()
- *                          .setLanguage("sr")
- *                          .setScript("Latn")
- *                          .setRegion("RS")
- *                          .build(status);
- *     if (U_SUCCESS(status)) {
- *       // ...
- *     }
- * 
- *
- * - *

LocaleBuilders can be reused; clear() resets all - * fields to their default values. - * - *

LocaleBuilder tracks errors in an internal UErrorCode. For all setters, - * except setLanguageTag and setLocale, LocaleBuilder will return immediately - * if the internal UErrorCode is in error state. - * To reset internal state and error code, call clear method. - * The setLanguageTag and setLocale method will first clear the internal - * UErrorCode, then track the error of the validation of the input parameter - * into the internal UErrorCode. - * - * @stable ICU 64 - */ -class U_COMMON_API LocaleBuilder : public UObject { -public: - /** - * Constructs an empty LocaleBuilder. The default value of all - * fields, extensions, and private use information is the - * empty string. - * - * @stable ICU 64 - */ - LocaleBuilder(); - - /** - * Destructor - * @stable ICU 64 - */ - virtual ~LocaleBuilder(); - - /** - * Resets the LocaleBuilder to match the provided - * locale. Existing state is discarded. - * - *

All fields of the locale must be well-formed. - *

This method clears the internal UErrorCode. - * - * @param locale the locale - * @return This builder. - * - * @stable ICU 64 - */ - LocaleBuilder& setLocale(const Locale& locale); - - /** - * Resets the LocaleBuilder to match the provided IETF BCP 47 language tag. - * Discards the existing state. - * The empty string causes the builder to be reset, like {@link #clear}. - * Legacy language tags (marked as “Type: grandfathered” in BCP 47) - * are converted to their canonical form before being processed. - * Otherwise, the language tag must be well-formed, - * or else the build() method will later report an U_ILLEGAL_ARGUMENT_ERROR. - * - *

This method clears the internal UErrorCode. - * - * @param tag the language tag, defined as IETF BCP 47 language tag. - * @return This builder. - * @stable ICU 64 - */ - LocaleBuilder& setLanguageTag(StringPiece tag); - - /** - * Sets the language. If language is the empty string, the - * language in this LocaleBuilder is removed. Otherwise, the - * language must be well-formed, or else the build() method will - * later report an U_ILLEGAL_ARGUMENT_ERROR. - * - *

The syntax of language value is defined as - * [unicode_language_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_language_subtag). - * - * @param language the language - * @return This builder. - * @stable ICU 64 - */ - LocaleBuilder& setLanguage(StringPiece language); - - /** - * Sets the script. If script is the empty string, the script in - * this LocaleBuilder is removed. - * Otherwise, the script must be well-formed, or else the build() - * method will later report an U_ILLEGAL_ARGUMENT_ERROR. - * - *

The script value is a four-letter script code as - * [unicode_script_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_script_subtag) - * defined by ISO 15924 - * - * @param script the script - * @return This builder. - * @stable ICU 64 - */ - LocaleBuilder& setScript(StringPiece script); - - /** - * Sets the region. If region is the empty string, the region in this - * LocaleBuilder is removed. Otherwise, the region - * must be well-formed, or else the build() method will later report an - * U_ILLEGAL_ARGUMENT_ERROR. - * - *

The region value is defined by - * [unicode_region_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_region_subtag) - * as a two-letter ISO 3166 code or a three-digit UN M.49 area code. - * - *

The region value in the Locale created by the - * LocaleBuilder is always normalized to upper case. - * - * @param region the region - * @return This builder. - * @stable ICU 64 - */ - LocaleBuilder& setRegion(StringPiece region); - - /** - * Sets the variant. If variant is the empty string, the variant in this - * LocaleBuilder is removed. Otherwise, the variant - * must be well-formed, or else the build() method will later report an - * U_ILLEGAL_ARGUMENT_ERROR. - * - *

Note: This method checks if variant - * satisfies the - * [unicode_variant_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_variant_subtag) - * syntax requirements, and normalizes the value to lowercase letters. However, - * the Locale class does not impose any syntactic - * restriction on variant. To set an ill-formed variant, use a Locale constructor. - * If there are multiple unicode_variant_subtag, the caller must concatenate - * them with '-' as separator (ex: "foobar-fibar"). - * - * @param variant the variant - * @return This builder. - * @stable ICU 64 - */ - LocaleBuilder& setVariant(StringPiece variant); - - /** - * Sets the extension for the given key. If the value is the empty string, - * the extension is removed. Otherwise, the key and - * value must be well-formed, or else the build() method will - * later report an U_ILLEGAL_ARGUMENT_ERROR. - * - *

Note: The key ('u') is used for the Unicode locale extension. - * Setting a value for this key replaces any existing Unicode locale key/type - * pairs with those defined in the extension. - * - *

Note: The key ('x') is used for the private use code. To be - * well-formed, the value for this key needs only to have subtags of one to - * eight alphanumeric characters, not two to eight as in the general case. - * - * @param key the extension key - * @param value the extension value - * @return This builder. - * @stable ICU 64 - */ - LocaleBuilder& setExtension(char key, StringPiece value); - - /** - * Sets the Unicode locale keyword type for the given key. If the type - * StringPiece is constructed with a nullptr, the keyword is removed. - * If the type is the empty string, the keyword is set without type subtags. - * Otherwise, the key and type must be well-formed, or else the build() - * method will later report an U_ILLEGAL_ARGUMENT_ERROR. - * - *

Keys and types are converted to lower case. - * - *

Note:Setting the 'u' extension via {@link #setExtension} - * replaces all Unicode locale keywords with those defined in the - * extension. - * - * @param key the Unicode locale key - * @param type the Unicode locale type - * @return This builder. - * @stable ICU 64 - */ - LocaleBuilder& setUnicodeLocaleKeyword( - StringPiece key, StringPiece type); - - /** - * Adds a unicode locale attribute, if not already present, otherwise - * has no effect. The attribute must not be empty string and must be - * well-formed or U_ILLEGAL_ARGUMENT_ERROR will be set to status - * during the build() call. - * - * @param attribute the attribute - * @return This builder. - * @stable ICU 64 - */ - LocaleBuilder& addUnicodeLocaleAttribute(StringPiece attribute); - - /** - * Removes a unicode locale attribute, if present, otherwise has no - * effect. The attribute must not be empty string and must be well-formed - * or U_ILLEGAL_ARGUMENT_ERROR will be set to status during the build() call. - * - *

Attribute comparison for removal is case-insensitive. - * - * @param attribute the attribute - * @return This builder. - * @stable ICU 64 - */ - LocaleBuilder& removeUnicodeLocaleAttribute(StringPiece attribute); - - /** - * Resets the builder to its initial, empty state. - *

This method clears the internal UErrorCode. - * - * @return this builder - * @stable ICU 64 - */ - LocaleBuilder& clear(); - - /** - * Resets the extensions to their initial, empty state. - * Language, script, region and variant are unchanged. - * - * @return this builder - * @stable ICU 64 - */ - LocaleBuilder& clearExtensions(); - - /** - * Returns an instance of Locale created from the fields set - * on this builder. - * If any set methods or during the build() call require memory allocation - * but fail U_MEMORY_ALLOCATION_ERROR will be set to status. - * If any of the fields set by the setters are not well-formed, the status - * will be set to U_ILLEGAL_ARGUMENT_ERROR. The state of the builder will - * not change after the build() call and the caller is free to keep using - * the same builder to build more locales. - * - * @return a new Locale - * @stable ICU 64 - */ - Locale build(UErrorCode& status); - - /** - * Sets the UErrorCode if an error occurred while recording sets. - * Preserves older error codes in the outErrorCode. - * @param outErrorCode Set to an error code that occurred while setting subtags. - * Unchanged if there is no such error or if outErrorCode - * already contained an error. - * @return true if U_FAILURE(outErrorCode) - * @stable ICU 65 - */ - UBool copyErrorTo(UErrorCode &outErrorCode) const; - -private: - friend class LocaleMatcher::Result; - - void copyExtensionsFrom(const Locale& src, UErrorCode& errorCode); - - UErrorCode status_; - char language_[9]; - char script_[5]; - char region_[4]; - CharString *variant_; // Pointer not object so we need not #include internal charstr.h. - icu::Locale *extensions_; // Pointer not object. Storage for all other fields. - -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __LOCALEBUILDER_H__ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +#ifndef __LOCALEBUILDER_H__ +#define __LOCALEBUILDER_H__ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/locid.h" +#include "unicode/localematcher.h" +#include "unicode/stringpiece.h" +#include "unicode/uobject.h" + +/** + * \file + * \brief C++ API: Builder API for Locale + */ + +U_NAMESPACE_BEGIN +class CharString; + +/** + * LocaleBuilder is used to build instances of Locale + * from values configured by the setters. Unlike the Locale + * constructors, the LocaleBuilder checks if a value configured by a + * setter satisfies the syntax requirements defined by the Locale + * class. A Locale object created by a LocaleBuilder is + * well-formed and can be transformed to a well-formed IETF BCP 47 language tag + * without losing information. + * + *

The following example shows how to create a Locale object + * with the LocaleBuilder. + *

+ *
+ *     UErrorCode status = U_ZERO_ERROR;
+ *     Locale aLocale = LocaleBuilder()
+ *                          .setLanguage("sr")
+ *                          .setScript("Latn")
+ *                          .setRegion("RS")
+ *                          .build(status);
+ *     if (U_SUCCESS(status)) {
+ *       // ...
+ *     }
+ * 
+ *
+ * + *

LocaleBuilders can be reused; clear() resets all + * fields to their default values. + * + *

LocaleBuilder tracks errors in an internal UErrorCode. For all setters, + * except setLanguageTag and setLocale, LocaleBuilder will return immediately + * if the internal UErrorCode is in error state. + * To reset internal state and error code, call clear method. + * The setLanguageTag and setLocale method will first clear the internal + * UErrorCode, then track the error of the validation of the input parameter + * into the internal UErrorCode. + * + * @stable ICU 64 + */ +class U_COMMON_API LocaleBuilder : public UObject { +public: + /** + * Constructs an empty LocaleBuilder. The default value of all + * fields, extensions, and private use information is the + * empty string. + * + * @stable ICU 64 + */ + LocaleBuilder(); + + /** + * Destructor + * @stable ICU 64 + */ + virtual ~LocaleBuilder(); + + /** + * Resets the LocaleBuilder to match the provided + * locale. Existing state is discarded. + * + *

All fields of the locale must be well-formed. + *

This method clears the internal UErrorCode. + * + * @param locale the locale + * @return This builder. + * + * @stable ICU 64 + */ + LocaleBuilder& setLocale(const Locale& locale); + + /** + * Resets the LocaleBuilder to match the provided IETF BCP 47 language tag. + * Discards the existing state. + * The empty string causes the builder to be reset, like {@link #clear}. + * Legacy language tags (marked as “Type: grandfathered” in BCP 47) + * are converted to their canonical form before being processed. + * Otherwise, the language tag must be well-formed, + * or else the build() method will later report an U_ILLEGAL_ARGUMENT_ERROR. + * + *

This method clears the internal UErrorCode. + * + * @param tag the language tag, defined as IETF BCP 47 language tag. + * @return This builder. + * @stable ICU 64 + */ + LocaleBuilder& setLanguageTag(StringPiece tag); + + /** + * Sets the language. If language is the empty string, the + * language in this LocaleBuilder is removed. Otherwise, the + * language must be well-formed, or else the build() method will + * later report an U_ILLEGAL_ARGUMENT_ERROR. + * + *

The syntax of language value is defined as + * [unicode_language_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_language_subtag). + * + * @param language the language + * @return This builder. + * @stable ICU 64 + */ + LocaleBuilder& setLanguage(StringPiece language); + + /** + * Sets the script. If script is the empty string, the script in + * this LocaleBuilder is removed. + * Otherwise, the script must be well-formed, or else the build() + * method will later report an U_ILLEGAL_ARGUMENT_ERROR. + * + *

The script value is a four-letter script code as + * [unicode_script_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_script_subtag) + * defined by ISO 15924 + * + * @param script the script + * @return This builder. + * @stable ICU 64 + */ + LocaleBuilder& setScript(StringPiece script); + + /** + * Sets the region. If region is the empty string, the region in this + * LocaleBuilder is removed. Otherwise, the region + * must be well-formed, or else the build() method will later report an + * U_ILLEGAL_ARGUMENT_ERROR. + * + *

The region value is defined by + * [unicode_region_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_region_subtag) + * as a two-letter ISO 3166 code or a three-digit UN M.49 area code. + * + *

The region value in the Locale created by the + * LocaleBuilder is always normalized to upper case. + * + * @param region the region + * @return This builder. + * @stable ICU 64 + */ + LocaleBuilder& setRegion(StringPiece region); + + /** + * Sets the variant. If variant is the empty string, the variant in this + * LocaleBuilder is removed. Otherwise, the variant + * must be well-formed, or else the build() method will later report an + * U_ILLEGAL_ARGUMENT_ERROR. + * + *

Note: This method checks if variant + * satisfies the + * [unicode_variant_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_variant_subtag) + * syntax requirements, and normalizes the value to lowercase letters. However, + * the Locale class does not impose any syntactic + * restriction on variant. To set an ill-formed variant, use a Locale constructor. + * If there are multiple unicode_variant_subtag, the caller must concatenate + * them with '-' as separator (ex: "foobar-fibar"). + * + * @param variant the variant + * @return This builder. + * @stable ICU 64 + */ + LocaleBuilder& setVariant(StringPiece variant); + + /** + * Sets the extension for the given key. If the value is the empty string, + * the extension is removed. Otherwise, the key and + * value must be well-formed, or else the build() method will + * later report an U_ILLEGAL_ARGUMENT_ERROR. + * + *

Note: The key ('u') is used for the Unicode locale extension. + * Setting a value for this key replaces any existing Unicode locale key/type + * pairs with those defined in the extension. + * + *

Note: The key ('x') is used for the private use code. To be + * well-formed, the value for this key needs only to have subtags of one to + * eight alphanumeric characters, not two to eight as in the general case. + * + * @param key the extension key + * @param value the extension value + * @return This builder. + * @stable ICU 64 + */ + LocaleBuilder& setExtension(char key, StringPiece value); + + /** + * Sets the Unicode locale keyword type for the given key. If the type + * StringPiece is constructed with a nullptr, the keyword is removed. + * If the type is the empty string, the keyword is set without type subtags. + * Otherwise, the key and type must be well-formed, or else the build() + * method will later report an U_ILLEGAL_ARGUMENT_ERROR. + * + *

Keys and types are converted to lower case. + * + *

Note:Setting the 'u' extension via {@link #setExtension} + * replaces all Unicode locale keywords with those defined in the + * extension. + * + * @param key the Unicode locale key + * @param type the Unicode locale type + * @return This builder. + * @stable ICU 64 + */ + LocaleBuilder& setUnicodeLocaleKeyword( + StringPiece key, StringPiece type); + + /** + * Adds a unicode locale attribute, if not already present, otherwise + * has no effect. The attribute must not be empty string and must be + * well-formed or U_ILLEGAL_ARGUMENT_ERROR will be set to status + * during the build() call. + * + * @param attribute the attribute + * @return This builder. + * @stable ICU 64 + */ + LocaleBuilder& addUnicodeLocaleAttribute(StringPiece attribute); + + /** + * Removes a unicode locale attribute, if present, otherwise has no + * effect. The attribute must not be empty string and must be well-formed + * or U_ILLEGAL_ARGUMENT_ERROR will be set to status during the build() call. + * + *

Attribute comparison for removal is case-insensitive. + * + * @param attribute the attribute + * @return This builder. + * @stable ICU 64 + */ + LocaleBuilder& removeUnicodeLocaleAttribute(StringPiece attribute); + + /** + * Resets the builder to its initial, empty state. + *

This method clears the internal UErrorCode. + * + * @return this builder + * @stable ICU 64 + */ + LocaleBuilder& clear(); + + /** + * Resets the extensions to their initial, empty state. + * Language, script, region and variant are unchanged. + * + * @return this builder + * @stable ICU 64 + */ + LocaleBuilder& clearExtensions(); + + /** + * Returns an instance of Locale created from the fields set + * on this builder. + * If any set methods or during the build() call require memory allocation + * but fail U_MEMORY_ALLOCATION_ERROR will be set to status. + * If any of the fields set by the setters are not well-formed, the status + * will be set to U_ILLEGAL_ARGUMENT_ERROR. The state of the builder will + * not change after the build() call and the caller is free to keep using + * the same builder to build more locales. + * + * @return a new Locale + * @stable ICU 64 + */ + Locale build(UErrorCode& status); + + /** + * Sets the UErrorCode if an error occurred while recording sets. + * Preserves older error codes in the outErrorCode. + * @param outErrorCode Set to an error code that occurred while setting subtags. + * Unchanged if there is no such error or if outErrorCode + * already contained an error. + * @return true if U_FAILURE(outErrorCode) + * @stable ICU 65 + */ + UBool copyErrorTo(UErrorCode &outErrorCode) const; + +private: + friend class LocaleMatcher::Result; + + void copyExtensionsFrom(const Locale& src, UErrorCode& errorCode); + + UErrorCode status_; + char language_[9]; + char script_[5]; + char region_[4]; + CharString *variant_; // Pointer not object so we need not #include internal charstr.h. + icu::Locale *extensions_; // Pointer not object. Storage for all other fields. + +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __LOCALEBUILDER_H__ diff --git a/deps/icu-small/source/common/unicode/localematcher.h b/deps/icu-small/source/common/unicode/localematcher.h index 0f7e04a3afdcf2..340308c6cbd997 100644 --- a/deps/icu-small/source/common/unicode/localematcher.h +++ b/deps/icu-small/source/common/unicode/localematcher.h @@ -1,708 +1,708 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// localematcher.h -// created: 2019may08 Markus W. Scherer - -#ifndef __LOCALEMATCHER_H__ -#define __LOCALEMATCHER_H__ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/locid.h" -#include "unicode/stringpiece.h" -#include "unicode/uobject.h" - -/** - * \file - * \brief C++ API: Locale matcher: User's desired locales vs. application's supported locales. - */ - -/** - * Builder option for whether the language subtag or the script subtag is most important. - * - * @see LocaleMatcher::Builder#setFavorSubtag(ULocMatchFavorSubtag) - * @stable ICU 65 - */ -enum ULocMatchFavorSubtag { - /** - * Language differences are most important, then script differences, then region differences. - * (This is the default behavior.) - * - * @stable ICU 65 - */ - ULOCMATCH_FAVOR_LANGUAGE, - /** - * Makes script differences matter relatively more than language differences. - * - * @stable ICU 65 - */ - ULOCMATCH_FAVOR_SCRIPT -}; -#ifndef U_IN_DOXYGEN -typedef enum ULocMatchFavorSubtag ULocMatchFavorSubtag; -#endif - -/** - * Builder option for whether all desired locales are treated equally or - * earlier ones are preferred. - * - * @see LocaleMatcher::Builder#setDemotionPerDesiredLocale(ULocMatchDemotion) - * @stable ICU 65 - */ -enum ULocMatchDemotion { - /** - * All desired locales are treated equally. - * - * @stable ICU 65 - */ - ULOCMATCH_DEMOTION_NONE, - /** - * Earlier desired locales are preferred. - * - *

From each desired locale to the next, - * the distance to any supported locale is increased by an additional amount - * which is at least as large as most region mismatches. - * A later desired locale has to have a better match with some supported locale - * due to more than merely having the same region subtag. - * - *

For example: Supported={en, sv} desired=[en-GB, sv] - * yields Result(en-GB, en) because - * with the demotion of sv its perfect match is no better than - * the region distance between the earlier desired locale en-GB and en=en-US. - * - *

Notes: - *

    - *
  • In some cases, language and/or script differences can be as small as - * the typical region difference. (Example: sr-Latn vs. sr-Cyrl) - *
  • It is possible for certain region differences to be larger than usual, - * and larger than the demotion. - * (As of CLDR 35 there is no such case, but - * this is possible in future versions of the data.) - *
- * - * @stable ICU 65 - */ - ULOCMATCH_DEMOTION_REGION -}; -#ifndef U_IN_DOXYGEN -typedef enum ULocMatchDemotion ULocMatchDemotion; -#endif - -/** - * Builder option for whether to include or ignore one-way (fallback) match data. - * The LocaleMatcher uses CLDR languageMatch data which includes fallback (oneway=true) entries. - * Sometimes it is desirable to ignore those. - * - *

For example, consider a web application with the UI in a given language, - * with a link to another, related web app. - * The link should include the UI language, and the target server may also use - * the client’s Accept-Language header data. - * The target server has its own list of supported languages. - * One may want to favor UI language consistency, that is, - * if there is a decent match for the original UI language, we want to use it, - * but not if it is merely a fallback. - * - * @see LocaleMatcher::Builder#setDirection(ULocMatchDirection) - * @stable ICU 67 - */ -enum ULocMatchDirection { - /** - * Locale matching includes one-way matches such as Breton→French. (default) - * - * @stable ICU 67 - */ - ULOCMATCH_DIRECTION_WITH_ONE_WAY, - /** - * Locale matching limited to two-way matches including e.g. Danish↔Norwegian - * but ignoring one-way matches. - * - * @stable ICU 67 - */ - ULOCMATCH_DIRECTION_ONLY_TWO_WAY -}; -#ifndef U_IN_DOXYGEN -typedef enum ULocMatchDirection ULocMatchDirection; -#endif - -struct UHashtable; - -U_NAMESPACE_BEGIN - -struct LSR; - -class LocaleDistance; -class LocaleLsrIterator; -class UVector; -class XLikelySubtags; - -/** - * Immutable class that picks the best match between a user's desired locales and - * an application's supported locales. - * Movable but not copyable. - * - *

Example: - *

- * UErrorCode errorCode = U_ZERO_ERROR;
- * LocaleMatcher matcher = LocaleMatcher::Builder().setSupportedLocales("fr, en-GB, en").build(errorCode);
- * Locale *bestSupported = matcher.getBestLocale(Locale.US, errorCode);  // "en"
- * 
- * - *

A matcher takes into account when languages are close to one another, - * such as Danish and Norwegian, - * and when regional variants are close, like en-GB and en-AU as opposed to en-US. - * - *

If there are multiple supported locales with the same (language, script, region) - * likely subtags, then the current implementation returns the first of those locales. - * It ignores variant subtags (except for pseudolocale variants) and extensions. - * This may change in future versions. - * - *

For example, the current implementation does not distinguish between - * de, de-DE, de-Latn, de-1901, de-u-co-phonebk. - * - *

If you prefer one equivalent locale over another, then provide only the preferred one, - * or place it earlier in the list of supported locales. - * - *

Otherwise, the order of supported locales may have no effect on the best-match results. - * The current implementation compares each desired locale with supported locales - * in the following order: - * 1. Default locale, if supported; - * 2. CLDR "paradigm locales" like en-GB and es-419; - * 3. other supported locales. - * This may change in future versions. - * - *

Often a product will just need one matcher instance, built with the languages - * that it supports. However, it may want multiple instances with different - * default languages based on additional information, such as the domain. - * - *

This class is not intended for public subclassing. - * - * @stable ICU 65 - */ -class U_COMMON_API LocaleMatcher : public UMemory { -public: - /** - * Data for the best-matching pair of a desired and a supported locale. - * Movable but not copyable. - * - * @stable ICU 65 - */ - class U_COMMON_API Result : public UMemory { - public: - /** - * Move constructor; might modify the source. - * This object will have the same contents that the source object had. - * - * @param src Result to move contents from. - * @stable ICU 65 - */ - Result(Result &&src) U_NOEXCEPT; - - /** - * Destructor. - * - * @stable ICU 65 - */ - ~Result(); - - /** - * Move assignment; might modify the source. - * This object will have the same contents that the source object had. - * - * @param src Result to move contents from. - * @stable ICU 65 - */ - Result &operator=(Result &&src) U_NOEXCEPT; - - /** - * Returns the best-matching desired locale. - * nullptr if the list of desired locales is empty or if none matched well enough. - * - * @return the best-matching desired locale, or nullptr. - * @stable ICU 65 - */ - inline const Locale *getDesiredLocale() const { return desiredLocale; } - - /** - * Returns the best-matching supported locale. - * If none matched well enough, this is the default locale. - * The default locale is nullptr if Builder::setNoDefaultLocale() was called, - * or if the list of supported locales is empty and no explicit default locale is set. - * - * @return the best-matching supported locale, or nullptr. - * @stable ICU 65 - */ - inline const Locale *getSupportedLocale() const { return supportedLocale; } - - /** - * Returns the index of the best-matching desired locale in the input Iterable order. - * -1 if the list of desired locales is empty or if none matched well enough. - * - * @return the index of the best-matching desired locale, or -1. - * @stable ICU 65 - */ - inline int32_t getDesiredIndex() const { return desiredIndex; } - - /** - * Returns the index of the best-matching supported locale in the - * constructor’s or builder’s input order (“set” Collection plus “added” locales). - * If the matcher was built from a locale list string, then the iteration order is that - * of a LocalePriorityList built from the same string. - * -1 if the list of supported locales is empty or if none matched well enough. - * - * @return the index of the best-matching supported locale, or -1. - * @stable ICU 65 - */ - inline int32_t getSupportedIndex() const { return supportedIndex; } - - /** - * Takes the best-matching supported locale and adds relevant fields of the - * best-matching desired locale, such as the -t- and -u- extensions. - * May replace some fields of the supported locale. - * The result is the locale that should be used for date and number formatting, collation, etc. - * Returns the root locale if getSupportedLocale() returns nullptr. - * - *

Example: desired=ar-SA-u-nu-latn, supported=ar-EG, resolved locale=ar-SA-u-nu-latn - * - * @return a locale combining the best-matching desired and supported locales. - * @stable ICU 65 - */ - Locale makeResolvedLocale(UErrorCode &errorCode) const; - - private: - Result(const Locale *desired, const Locale *supported, - int32_t desIndex, int32_t suppIndex, UBool owned) : - desiredLocale(desired), supportedLocale(supported), - desiredIndex(desIndex), supportedIndex(suppIndex), - desiredIsOwned(owned) {} - - Result(const Result &other) = delete; - Result &operator=(const Result &other) = delete; - - const Locale *desiredLocale; - const Locale *supportedLocale; - int32_t desiredIndex; - int32_t supportedIndex; - UBool desiredIsOwned; - - friend class LocaleMatcher; - }; - - /** - * LocaleMatcher builder. - * Movable but not copyable. - * - * @stable ICU 65 - */ - class U_COMMON_API Builder : public UMemory { - public: - /** - * Constructs a builder used in chaining parameters for building a LocaleMatcher. - * - * @return a new Builder object - * @stable ICU 65 - */ - Builder() {} - - /** - * Move constructor; might modify the source. - * This builder will have the same contents that the source builder had. - * - * @param src Builder to move contents from. - * @stable ICU 65 - */ - Builder(Builder &&src) U_NOEXCEPT; - - /** - * Destructor. - * - * @stable ICU 65 - */ - ~Builder(); - - /** - * Move assignment; might modify the source. - * This builder will have the same contents that the source builder had. - * - * @param src Builder to move contents from. - * @stable ICU 65 - */ - Builder &operator=(Builder &&src) U_NOEXCEPT; - - /** - * Parses an Accept-Language string - * (RFC 2616 Section 14.4), - * such as "af, en, fr;q=0.9", and sets the supported locales accordingly. - * Allows whitespace in more places but does not allow "*". - * Clears any previously set/added supported locales first. - * - * @param locales the Accept-Language string of locales to set - * @return this Builder object - * @stable ICU 65 - */ - Builder &setSupportedLocalesFromListString(StringPiece locales); - - /** - * Copies the supported locales, preserving iteration order. - * Clears any previously set/added supported locales first. - * Duplicates are allowed, and are not removed. - * - * @param locales the list of locale - * @return this Builder object - * @stable ICU 65 - */ - Builder &setSupportedLocales(Locale::Iterator &locales); - - /** - * Copies the supported locales from the begin/end range, preserving iteration order. - * Clears any previously set/added supported locales first. - * Duplicates are allowed, and are not removed. - * - * Each of the iterator parameter values must be an - * input iterator whose value is convertible to const Locale &. - * - * @param begin Start of range. - * @param end Exclusive end of range. - * @return this Builder object - * @stable ICU 65 - */ - template - Builder &setSupportedLocales(Iter begin, Iter end) { - if (U_FAILURE(errorCode_)) { return *this; } - clearSupportedLocales(); - while (begin != end) { - addSupportedLocale(*begin++); - } - return *this; - } - - /** - * Copies the supported locales from the begin/end range, preserving iteration order. - * Calls the converter to convert each *begin to a Locale or const Locale &. - * Clears any previously set/added supported locales first. - * Duplicates are allowed, and are not removed. - * - * Each of the iterator parameter values must be an - * input iterator whose value is convertible to const Locale &. - * - * @param begin Start of range. - * @param end Exclusive end of range. - * @param converter Converter from *begin to const Locale & or compatible. - * @return this Builder object - * @stable ICU 65 - */ - template - Builder &setSupportedLocalesViaConverter(Iter begin, Iter end, Conv converter) { - if (U_FAILURE(errorCode_)) { return *this; } - clearSupportedLocales(); - while (begin != end) { - addSupportedLocale(converter(*begin++)); - } - return *this; - } - - /** - * Adds another supported locale. - * Duplicates are allowed, and are not removed. - * - * @param locale another locale - * @return this Builder object - * @stable ICU 65 - */ - Builder &addSupportedLocale(const Locale &locale); - - /** - * Sets no default locale. - * There will be no explicit or implicit default locale. - * If there is no good match, then the matcher will return nullptr for the - * best supported locale. - * - * @stable ICU 68 - */ - Builder &setNoDefaultLocale(); - - /** - * Sets the default locale; if nullptr, or if it is not set explicitly, - * then the first supported locale is used as the default locale. - * There is no default locale at all (nullptr will be returned instead) - * if setNoDefaultLocale() is called. - * - * @param defaultLocale the default locale (will be copied) - * @return this Builder object - * @stable ICU 65 - */ - Builder &setDefaultLocale(const Locale *defaultLocale); - - /** - * If ULOCMATCH_FAVOR_SCRIPT, then the language differences are smaller than script - * differences. - * This is used in situations (such as maps) where - * it is better to fall back to the same script than a similar language. - * - * @param subtag the subtag to favor - * @return this Builder object - * @stable ICU 65 - */ - Builder &setFavorSubtag(ULocMatchFavorSubtag subtag); - - /** - * Option for whether all desired locales are treated equally or - * earlier ones are preferred (this is the default). - * - * @param demotion the demotion per desired locale to set. - * @return this Builder object - * @stable ICU 65 - */ - Builder &setDemotionPerDesiredLocale(ULocMatchDemotion demotion); - - /** - * Option for whether to include or ignore one-way (fallback) match data. - * By default, they are included. - * - * @param matchDirection the match direction to set. - * @return this Builder object - * @stable ICU 67 - */ - Builder &setDirection(ULocMatchDirection matchDirection) { - if (U_SUCCESS(errorCode_)) { - direction_ = matchDirection; - } - return *this; - } - - /** - * Sets the maximum distance for an acceptable match. - * The matcher will return a match for a pair of locales only if - * they match at least as well as the pair given here. - * - * For example, setMaxDistance(en-US, en-GB) limits matches to ones where the - * (desired, support) locales have a distance no greater than a region subtag difference. - * This is much stricter than the CLDR default. - * - * The details of locale matching are subject to changes in - * CLDR data and in the algorithm. - * Specifying a maximum distance in relative terms via a sample pair of locales - * insulates from changes that affect all distance metrics similarly, - * but some changes will necessarily affect relative distances between - * different pairs of locales. - * - * @param desired the desired locale for distance comparison. - * @param supported the supported locale for distance comparison. - * @return this Builder object - * @stable ICU 68 - */ - Builder &setMaxDistance(const Locale &desired, const Locale &supported); - - /** - * Sets the UErrorCode if an error occurred while setting parameters. - * Preserves older error codes in the outErrorCode. - * - * @param outErrorCode Set to an error code if it does not contain one already - * and an error occurred while setting parameters. - * Otherwise unchanged. - * @return true if U_FAILURE(outErrorCode) - * @stable ICU 65 - */ - UBool copyErrorTo(UErrorCode &outErrorCode) const; - - /** - * Builds and returns a new locale matcher. - * This builder can continue to be used. - * - * @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.) - * @return LocaleMatcher - * @stable ICU 65 - */ - LocaleMatcher build(UErrorCode &errorCode) const; - - private: - friend class LocaleMatcher; - - Builder(const Builder &other) = delete; - Builder &operator=(const Builder &other) = delete; - - void clearSupportedLocales(); - bool ensureSupportedLocaleVector(); - - UErrorCode errorCode_ = U_ZERO_ERROR; - UVector *supportedLocales_ = nullptr; - int32_t thresholdDistance_ = -1; - ULocMatchDemotion demotion_ = ULOCMATCH_DEMOTION_REGION; - Locale *defaultLocale_ = nullptr; - bool withDefault_ = true; - ULocMatchFavorSubtag favor_ = ULOCMATCH_FAVOR_LANGUAGE; - ULocMatchDirection direction_ = ULOCMATCH_DIRECTION_WITH_ONE_WAY; - Locale *maxDistanceDesired_ = nullptr; - Locale *maxDistanceSupported_ = nullptr; - }; - - // FYI No public LocaleMatcher constructors in C++; use the Builder. - - /** - * Move copy constructor; might modify the source. - * This matcher will have the same settings that the source matcher had. - * @param src source matcher - * @stable ICU 65 - */ - LocaleMatcher(LocaleMatcher &&src) U_NOEXCEPT; - - /** - * Destructor. - * @stable ICU 65 - */ - ~LocaleMatcher(); - - /** - * Move assignment operator; might modify the source. - * This matcher will have the same settings that the source matcher had. - * The behavior is undefined if *this and src are the same object. - * @param src source matcher - * @return *this - * @stable ICU 65 - */ - LocaleMatcher &operator=(LocaleMatcher &&src) U_NOEXCEPT; - - /** - * Returns the supported locale which best matches the desired locale. - * - * @param desiredLocale Typically a user's language. - * @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.) - * @return the best-matching supported locale. - * @stable ICU 65 - */ - const Locale *getBestMatch(const Locale &desiredLocale, UErrorCode &errorCode) const; - - /** - * Returns the supported locale which best matches one of the desired locales. - * - * @param desiredLocales Typically a user's languages, in order of preference (descending). - * @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.) - * @return the best-matching supported locale. - * @stable ICU 65 - */ - const Locale *getBestMatch(Locale::Iterator &desiredLocales, UErrorCode &errorCode) const; - - /** - * Parses an Accept-Language string - * (RFC 2616 Section 14.4), - * such as "af, en, fr;q=0.9", - * and returns the supported locale which best matches one of the desired locales. - * Allows whitespace in more places but does not allow "*". - * - * @param desiredLocaleList Typically a user's languages, as an Accept-Language string. - * @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.) - * @return the best-matching supported locale. - * @stable ICU 65 - */ - const Locale *getBestMatchForListString(StringPiece desiredLocaleList, UErrorCode &errorCode) const; - - /** - * Returns the best match between the desired locale and the supported locales. - * If the result's desired locale is not nullptr, then it is the address of the input locale. - * It has not been cloned. - * - * @param desiredLocale Typically a user's language. - * @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.) - * @return the best-matching pair of the desired and a supported locale. - * @stable ICU 65 - */ - Result getBestMatchResult(const Locale &desiredLocale, UErrorCode &errorCode) const; - - /** - * Returns the best match between the desired and supported locales. - * If the result's desired locale is not nullptr, then it is a clone of - * the best-matching desired locale. The Result object owns the clone. - * - * @param desiredLocales Typically a user's languages, in order of preference (descending). - * @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.) - * @return the best-matching pair of a desired and a supported locale. - * @stable ICU 65 - */ - Result getBestMatchResult(Locale::Iterator &desiredLocales, UErrorCode &errorCode) const; - - /** - * Returns true if the pair of locales matches acceptably. - * This is influenced by Builder options such as setDirection(), setFavorSubtag(), - * and setMaxDistance(). - * - * @param desired The desired locale. - * @param supported The supported locale. - * @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.) - * @return true if the pair of locales matches acceptably. - * @stable ICU 68 - */ - UBool isMatch(const Locale &desired, const Locale &supported, UErrorCode &errorCode) const; - -#ifndef U_HIDE_INTERNAL_API - /** - * Returns a fraction between 0 and 1, where 1 means that the languages are a - * perfect match, and 0 means that they are completely different. - * - *

This is mostly an implementation detail, and the precise values may change over time. - * The implementation may use either the maximized forms or the others ones, or both. - * The implementation may or may not rely on the forms to be consistent with each other. - * - *

Callers should construct and use a matcher rather than match pairs of locales directly. - * - * @param desired Desired locale. - * @param supported Supported locale. - * @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.) - * @return value between 0 and 1, inclusive. - * @internal (has a known user) - */ - double internalMatch(const Locale &desired, const Locale &supported, UErrorCode &errorCode) const; -#endif // U_HIDE_INTERNAL_API - -private: - LocaleMatcher(const Builder &builder, UErrorCode &errorCode); - LocaleMatcher(const LocaleMatcher &other) = delete; - LocaleMatcher &operator=(const LocaleMatcher &other) = delete; - - int32_t putIfAbsent(const LSR &lsr, int32_t i, int32_t suppLength, UErrorCode &errorCode); - - int32_t getBestSuppIndex(LSR desiredLSR, LocaleLsrIterator *remainingIter, UErrorCode &errorCode) const; - - const XLikelySubtags &likelySubtags; - const LocaleDistance &localeDistance; - int32_t thresholdDistance; - int32_t demotionPerDesiredLocale; - ULocMatchFavorSubtag favorSubtag; - ULocMatchDirection direction; - - // These are in input order. - const Locale ** supportedLocales; - LSR *lsrs; - int32_t supportedLocalesLength; - // These are in preference order: 1. Default locale 2. paradigm locales 3. others. - UHashtable *supportedLsrToIndex; // Map - // Array versions of the supportedLsrToIndex keys and values. - // The distance lookup loops over the supportedLSRs and returns the index of the best match. - const LSR **supportedLSRs; - int32_t *supportedIndexes; - int32_t supportedLSRsLength; - Locale *ownedDefaultLocale; - const Locale *defaultLocale; -}; - -U_NAMESPACE_END - -#endif // U_SHOW_CPLUSPLUS_API -#endif // __LOCALEMATCHER_H__ +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// localematcher.h +// created: 2019may08 Markus W. Scherer + +#ifndef __LOCALEMATCHER_H__ +#define __LOCALEMATCHER_H__ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/locid.h" +#include "unicode/stringpiece.h" +#include "unicode/uobject.h" + +/** + * \file + * \brief C++ API: Locale matcher: User's desired locales vs. application's supported locales. + */ + +/** + * Builder option for whether the language subtag or the script subtag is most important. + * + * @see LocaleMatcher::Builder#setFavorSubtag(ULocMatchFavorSubtag) + * @stable ICU 65 + */ +enum ULocMatchFavorSubtag { + /** + * Language differences are most important, then script differences, then region differences. + * (This is the default behavior.) + * + * @stable ICU 65 + */ + ULOCMATCH_FAVOR_LANGUAGE, + /** + * Makes script differences matter relatively more than language differences. + * + * @stable ICU 65 + */ + ULOCMATCH_FAVOR_SCRIPT +}; +#ifndef U_IN_DOXYGEN +typedef enum ULocMatchFavorSubtag ULocMatchFavorSubtag; +#endif + +/** + * Builder option for whether all desired locales are treated equally or + * earlier ones are preferred. + * + * @see LocaleMatcher::Builder#setDemotionPerDesiredLocale(ULocMatchDemotion) + * @stable ICU 65 + */ +enum ULocMatchDemotion { + /** + * All desired locales are treated equally. + * + * @stable ICU 65 + */ + ULOCMATCH_DEMOTION_NONE, + /** + * Earlier desired locales are preferred. + * + *

From each desired locale to the next, + * the distance to any supported locale is increased by an additional amount + * which is at least as large as most region mismatches. + * A later desired locale has to have a better match with some supported locale + * due to more than merely having the same region subtag. + * + *

For example: Supported={en, sv} desired=[en-GB, sv] + * yields Result(en-GB, en) because + * with the demotion of sv its perfect match is no better than + * the region distance between the earlier desired locale en-GB and en=en-US. + * + *

Notes: + *

    + *
  • In some cases, language and/or script differences can be as small as + * the typical region difference. (Example: sr-Latn vs. sr-Cyrl) + *
  • It is possible for certain region differences to be larger than usual, + * and larger than the demotion. + * (As of CLDR 35 there is no such case, but + * this is possible in future versions of the data.) + *
+ * + * @stable ICU 65 + */ + ULOCMATCH_DEMOTION_REGION +}; +#ifndef U_IN_DOXYGEN +typedef enum ULocMatchDemotion ULocMatchDemotion; +#endif + +/** + * Builder option for whether to include or ignore one-way (fallback) match data. + * The LocaleMatcher uses CLDR languageMatch data which includes fallback (oneway=true) entries. + * Sometimes it is desirable to ignore those. + * + *

For example, consider a web application with the UI in a given language, + * with a link to another, related web app. + * The link should include the UI language, and the target server may also use + * the client’s Accept-Language header data. + * The target server has its own list of supported languages. + * One may want to favor UI language consistency, that is, + * if there is a decent match for the original UI language, we want to use it, + * but not if it is merely a fallback. + * + * @see LocaleMatcher::Builder#setDirection(ULocMatchDirection) + * @stable ICU 67 + */ +enum ULocMatchDirection { + /** + * Locale matching includes one-way matches such as Breton→French. (default) + * + * @stable ICU 67 + */ + ULOCMATCH_DIRECTION_WITH_ONE_WAY, + /** + * Locale matching limited to two-way matches including e.g. Danish↔Norwegian + * but ignoring one-way matches. + * + * @stable ICU 67 + */ + ULOCMATCH_DIRECTION_ONLY_TWO_WAY +}; +#ifndef U_IN_DOXYGEN +typedef enum ULocMatchDirection ULocMatchDirection; +#endif + +struct UHashtable; + +U_NAMESPACE_BEGIN + +struct LSR; + +class LocaleDistance; +class LocaleLsrIterator; +class UVector; +class XLikelySubtags; + +/** + * Immutable class that picks the best match between a user's desired locales and + * an application's supported locales. + * Movable but not copyable. + * + *

Example: + *

+ * UErrorCode errorCode = U_ZERO_ERROR;
+ * LocaleMatcher matcher = LocaleMatcher::Builder().setSupportedLocales("fr, en-GB, en").build(errorCode);
+ * Locale *bestSupported = matcher.getBestLocale(Locale.US, errorCode);  // "en"
+ * 
+ * + *

A matcher takes into account when languages are close to one another, + * such as Danish and Norwegian, + * and when regional variants are close, like en-GB and en-AU as opposed to en-US. + * + *

If there are multiple supported locales with the same (language, script, region) + * likely subtags, then the current implementation returns the first of those locales. + * It ignores variant subtags (except for pseudolocale variants) and extensions. + * This may change in future versions. + * + *

For example, the current implementation does not distinguish between + * de, de-DE, de-Latn, de-1901, de-u-co-phonebk. + * + *

If you prefer one equivalent locale over another, then provide only the preferred one, + * or place it earlier in the list of supported locales. + * + *

Otherwise, the order of supported locales may have no effect on the best-match results. + * The current implementation compares each desired locale with supported locales + * in the following order: + * 1. Default locale, if supported; + * 2. CLDR "paradigm locales" like en-GB and es-419; + * 3. other supported locales. + * This may change in future versions. + * + *

Often a product will just need one matcher instance, built with the languages + * that it supports. However, it may want multiple instances with different + * default languages based on additional information, such as the domain. + * + *

This class is not intended for public subclassing. + * + * @stable ICU 65 + */ +class U_COMMON_API LocaleMatcher : public UMemory { +public: + /** + * Data for the best-matching pair of a desired and a supported locale. + * Movable but not copyable. + * + * @stable ICU 65 + */ + class U_COMMON_API Result : public UMemory { + public: + /** + * Move constructor; might modify the source. + * This object will have the same contents that the source object had. + * + * @param src Result to move contents from. + * @stable ICU 65 + */ + Result(Result &&src) noexcept; + + /** + * Destructor. + * + * @stable ICU 65 + */ + ~Result(); + + /** + * Move assignment; might modify the source. + * This object will have the same contents that the source object had. + * + * @param src Result to move contents from. + * @stable ICU 65 + */ + Result &operator=(Result &&src) noexcept; + + /** + * Returns the best-matching desired locale. + * nullptr if the list of desired locales is empty or if none matched well enough. + * + * @return the best-matching desired locale, or nullptr. + * @stable ICU 65 + */ + inline const Locale *getDesiredLocale() const { return desiredLocale; } + + /** + * Returns the best-matching supported locale. + * If none matched well enough, this is the default locale. + * The default locale is nullptr if Builder::setNoDefaultLocale() was called, + * or if the list of supported locales is empty and no explicit default locale is set. + * + * @return the best-matching supported locale, or nullptr. + * @stable ICU 65 + */ + inline const Locale *getSupportedLocale() const { return supportedLocale; } + + /** + * Returns the index of the best-matching desired locale in the input Iterable order. + * -1 if the list of desired locales is empty or if none matched well enough. + * + * @return the index of the best-matching desired locale, or -1. + * @stable ICU 65 + */ + inline int32_t getDesiredIndex() const { return desiredIndex; } + + /** + * Returns the index of the best-matching supported locale in the + * constructor’s or builder’s input order (“set” Collection plus “added” locales). + * If the matcher was built from a locale list string, then the iteration order is that + * of a LocalePriorityList built from the same string. + * -1 if the list of supported locales is empty or if none matched well enough. + * + * @return the index of the best-matching supported locale, or -1. + * @stable ICU 65 + */ + inline int32_t getSupportedIndex() const { return supportedIndex; } + + /** + * Takes the best-matching supported locale and adds relevant fields of the + * best-matching desired locale, such as the -t- and -u- extensions. + * May replace some fields of the supported locale. + * The result is the locale that should be used for date and number formatting, collation, etc. + * Returns the root locale if getSupportedLocale() returns nullptr. + * + *

Example: desired=ar-SA-u-nu-latn, supported=ar-EG, resolved locale=ar-SA-u-nu-latn + * + * @return a locale combining the best-matching desired and supported locales. + * @stable ICU 65 + */ + Locale makeResolvedLocale(UErrorCode &errorCode) const; + + private: + Result(const Locale *desired, const Locale *supported, + int32_t desIndex, int32_t suppIndex, UBool owned) : + desiredLocale(desired), supportedLocale(supported), + desiredIndex(desIndex), supportedIndex(suppIndex), + desiredIsOwned(owned) {} + + Result(const Result &other) = delete; + Result &operator=(const Result &other) = delete; + + const Locale *desiredLocale; + const Locale *supportedLocale; + int32_t desiredIndex; + int32_t supportedIndex; + UBool desiredIsOwned; + + friend class LocaleMatcher; + }; + + /** + * LocaleMatcher builder. + * Movable but not copyable. + * + * @stable ICU 65 + */ + class U_COMMON_API Builder : public UMemory { + public: + /** + * Constructs a builder used in chaining parameters for building a LocaleMatcher. + * + * @return a new Builder object + * @stable ICU 65 + */ + Builder() {} + + /** + * Move constructor; might modify the source. + * This builder will have the same contents that the source builder had. + * + * @param src Builder to move contents from. + * @stable ICU 65 + */ + Builder(Builder &&src) noexcept; + + /** + * Destructor. + * + * @stable ICU 65 + */ + ~Builder(); + + /** + * Move assignment; might modify the source. + * This builder will have the same contents that the source builder had. + * + * @param src Builder to move contents from. + * @stable ICU 65 + */ + Builder &operator=(Builder &&src) noexcept; + + /** + * Parses an Accept-Language string + * (RFC 2616 Section 14.4), + * such as "af, en, fr;q=0.9", and sets the supported locales accordingly. + * Allows whitespace in more places but does not allow "*". + * Clears any previously set/added supported locales first. + * + * @param locales the Accept-Language string of locales to set + * @return this Builder object + * @stable ICU 65 + */ + Builder &setSupportedLocalesFromListString(StringPiece locales); + + /** + * Copies the supported locales, preserving iteration order. + * Clears any previously set/added supported locales first. + * Duplicates are allowed, and are not removed. + * + * @param locales the list of locale + * @return this Builder object + * @stable ICU 65 + */ + Builder &setSupportedLocales(Locale::Iterator &locales); + + /** + * Copies the supported locales from the begin/end range, preserving iteration order. + * Clears any previously set/added supported locales first. + * Duplicates are allowed, and are not removed. + * + * Each of the iterator parameter values must be an + * input iterator whose value is convertible to const Locale &. + * + * @param begin Start of range. + * @param end Exclusive end of range. + * @return this Builder object + * @stable ICU 65 + */ + template + Builder &setSupportedLocales(Iter begin, Iter end) { + if (U_FAILURE(errorCode_)) { return *this; } + clearSupportedLocales(); + while (begin != end) { + addSupportedLocale(*begin++); + } + return *this; + } + + /** + * Copies the supported locales from the begin/end range, preserving iteration order. + * Calls the converter to convert each *begin to a Locale or const Locale &. + * Clears any previously set/added supported locales first. + * Duplicates are allowed, and are not removed. + * + * Each of the iterator parameter values must be an + * input iterator whose value is convertible to const Locale &. + * + * @param begin Start of range. + * @param end Exclusive end of range. + * @param converter Converter from *begin to const Locale & or compatible. + * @return this Builder object + * @stable ICU 65 + */ + template + Builder &setSupportedLocalesViaConverter(Iter begin, Iter end, Conv converter) { + if (U_FAILURE(errorCode_)) { return *this; } + clearSupportedLocales(); + while (begin != end) { + addSupportedLocale(converter(*begin++)); + } + return *this; + } + + /** + * Adds another supported locale. + * Duplicates are allowed, and are not removed. + * + * @param locale another locale + * @return this Builder object + * @stable ICU 65 + */ + Builder &addSupportedLocale(const Locale &locale); + + /** + * Sets no default locale. + * There will be no explicit or implicit default locale. + * If there is no good match, then the matcher will return nullptr for the + * best supported locale. + * + * @stable ICU 68 + */ + Builder &setNoDefaultLocale(); + + /** + * Sets the default locale; if nullptr, or if it is not set explicitly, + * then the first supported locale is used as the default locale. + * There is no default locale at all (nullptr will be returned instead) + * if setNoDefaultLocale() is called. + * + * @param defaultLocale the default locale (will be copied) + * @return this Builder object + * @stable ICU 65 + */ + Builder &setDefaultLocale(const Locale *defaultLocale); + + /** + * If ULOCMATCH_FAVOR_SCRIPT, then the language differences are smaller than script + * differences. + * This is used in situations (such as maps) where + * it is better to fall back to the same script than a similar language. + * + * @param subtag the subtag to favor + * @return this Builder object + * @stable ICU 65 + */ + Builder &setFavorSubtag(ULocMatchFavorSubtag subtag); + + /** + * Option for whether all desired locales are treated equally or + * earlier ones are preferred (this is the default). + * + * @param demotion the demotion per desired locale to set. + * @return this Builder object + * @stable ICU 65 + */ + Builder &setDemotionPerDesiredLocale(ULocMatchDemotion demotion); + + /** + * Option for whether to include or ignore one-way (fallback) match data. + * By default, they are included. + * + * @param matchDirection the match direction to set. + * @return this Builder object + * @stable ICU 67 + */ + Builder &setDirection(ULocMatchDirection matchDirection) { + if (U_SUCCESS(errorCode_)) { + direction_ = matchDirection; + } + return *this; + } + + /** + * Sets the maximum distance for an acceptable match. + * The matcher will return a match for a pair of locales only if + * they match at least as well as the pair given here. + * + * For example, setMaxDistance(en-US, en-GB) limits matches to ones where the + * (desired, support) locales have a distance no greater than a region subtag difference. + * This is much stricter than the CLDR default. + * + * The details of locale matching are subject to changes in + * CLDR data and in the algorithm. + * Specifying a maximum distance in relative terms via a sample pair of locales + * insulates from changes that affect all distance metrics similarly, + * but some changes will necessarily affect relative distances between + * different pairs of locales. + * + * @param desired the desired locale for distance comparison. + * @param supported the supported locale for distance comparison. + * @return this Builder object + * @stable ICU 68 + */ + Builder &setMaxDistance(const Locale &desired, const Locale &supported); + + /** + * Sets the UErrorCode if an error occurred while setting parameters. + * Preserves older error codes in the outErrorCode. + * + * @param outErrorCode Set to an error code if it does not contain one already + * and an error occurred while setting parameters. + * Otherwise unchanged. + * @return true if U_FAILURE(outErrorCode) + * @stable ICU 65 + */ + UBool copyErrorTo(UErrorCode &outErrorCode) const; + + /** + * Builds and returns a new locale matcher. + * This builder can continue to be used. + * + * @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.) + * @return LocaleMatcher + * @stable ICU 65 + */ + LocaleMatcher build(UErrorCode &errorCode) const; + + private: + friend class LocaleMatcher; + + Builder(const Builder &other) = delete; + Builder &operator=(const Builder &other) = delete; + + void clearSupportedLocales(); + bool ensureSupportedLocaleVector(); + + UErrorCode errorCode_ = U_ZERO_ERROR; + UVector *supportedLocales_ = nullptr; + int32_t thresholdDistance_ = -1; + ULocMatchDemotion demotion_ = ULOCMATCH_DEMOTION_REGION; + Locale *defaultLocale_ = nullptr; + bool withDefault_ = true; + ULocMatchFavorSubtag favor_ = ULOCMATCH_FAVOR_LANGUAGE; + ULocMatchDirection direction_ = ULOCMATCH_DIRECTION_WITH_ONE_WAY; + Locale *maxDistanceDesired_ = nullptr; + Locale *maxDistanceSupported_ = nullptr; + }; + + // FYI No public LocaleMatcher constructors in C++; use the Builder. + + /** + * Move copy constructor; might modify the source. + * This matcher will have the same settings that the source matcher had. + * @param src source matcher + * @stable ICU 65 + */ + LocaleMatcher(LocaleMatcher &&src) noexcept; + + /** + * Destructor. + * @stable ICU 65 + */ + ~LocaleMatcher(); + + /** + * Move assignment operator; might modify the source. + * This matcher will have the same settings that the source matcher had. + * The behavior is undefined if *this and src are the same object. + * @param src source matcher + * @return *this + * @stable ICU 65 + */ + LocaleMatcher &operator=(LocaleMatcher &&src) noexcept; + + /** + * Returns the supported locale which best matches the desired locale. + * + * @param desiredLocale Typically a user's language. + * @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.) + * @return the best-matching supported locale. + * @stable ICU 65 + */ + const Locale *getBestMatch(const Locale &desiredLocale, UErrorCode &errorCode) const; + + /** + * Returns the supported locale which best matches one of the desired locales. + * + * @param desiredLocales Typically a user's languages, in order of preference (descending). + * @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.) + * @return the best-matching supported locale. + * @stable ICU 65 + */ + const Locale *getBestMatch(Locale::Iterator &desiredLocales, UErrorCode &errorCode) const; + + /** + * Parses an Accept-Language string + * (RFC 2616 Section 14.4), + * such as "af, en, fr;q=0.9", + * and returns the supported locale which best matches one of the desired locales. + * Allows whitespace in more places but does not allow "*". + * + * @param desiredLocaleList Typically a user's languages, as an Accept-Language string. + * @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.) + * @return the best-matching supported locale. + * @stable ICU 65 + */ + const Locale *getBestMatchForListString(StringPiece desiredLocaleList, UErrorCode &errorCode) const; + + /** + * Returns the best match between the desired locale and the supported locales. + * If the result's desired locale is not nullptr, then it is the address of the input locale. + * It has not been cloned. + * + * @param desiredLocale Typically a user's language. + * @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.) + * @return the best-matching pair of the desired and a supported locale. + * @stable ICU 65 + */ + Result getBestMatchResult(const Locale &desiredLocale, UErrorCode &errorCode) const; + + /** + * Returns the best match between the desired and supported locales. + * If the result's desired locale is not nullptr, then it is a clone of + * the best-matching desired locale. The Result object owns the clone. + * + * @param desiredLocales Typically a user's languages, in order of preference (descending). + * @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.) + * @return the best-matching pair of a desired and a supported locale. + * @stable ICU 65 + */ + Result getBestMatchResult(Locale::Iterator &desiredLocales, UErrorCode &errorCode) const; + + /** + * Returns true if the pair of locales matches acceptably. + * This is influenced by Builder options such as setDirection(), setFavorSubtag(), + * and setMaxDistance(). + * + * @param desired The desired locale. + * @param supported The supported locale. + * @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.) + * @return true if the pair of locales matches acceptably. + * @stable ICU 68 + */ + UBool isMatch(const Locale &desired, const Locale &supported, UErrorCode &errorCode) const; + +#ifndef U_HIDE_INTERNAL_API + /** + * Returns a fraction between 0 and 1, where 1 means that the languages are a + * perfect match, and 0 means that they are completely different. + * + *

This is mostly an implementation detail, and the precise values may change over time. + * The implementation may use either the maximized forms or the others ones, or both. + * The implementation may or may not rely on the forms to be consistent with each other. + * + *

Callers should construct and use a matcher rather than match pairs of locales directly. + * + * @param desired Desired locale. + * @param supported Supported locale. + * @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.) + * @return value between 0 and 1, inclusive. + * @internal (has a known user) + */ + double internalMatch(const Locale &desired, const Locale &supported, UErrorCode &errorCode) const; +#endif // U_HIDE_INTERNAL_API + +private: + LocaleMatcher(const Builder &builder, UErrorCode &errorCode); + LocaleMatcher(const LocaleMatcher &other) = delete; + LocaleMatcher &operator=(const LocaleMatcher &other) = delete; + + int32_t putIfAbsent(const LSR &lsr, int32_t i, int32_t suppLength, UErrorCode &errorCode); + + int32_t getBestSuppIndex(LSR desiredLSR, LocaleLsrIterator *remainingIter, UErrorCode &errorCode) const; + + const XLikelySubtags &likelySubtags; + const LocaleDistance &localeDistance; + int32_t thresholdDistance; + int32_t demotionPerDesiredLocale; + ULocMatchFavorSubtag favorSubtag; + ULocMatchDirection direction; + + // These are in input order. + const Locale ** supportedLocales; + LSR *lsrs; + int32_t supportedLocalesLength; + // These are in preference order: 1. Default locale 2. paradigm locales 3. others. + UHashtable *supportedLsrToIndex; // Map + // Array versions of the supportedLsrToIndex keys and values. + // The distance lookup loops over the supportedLSRs and returns the index of the best match. + const LSR **supportedLSRs; + int32_t *supportedIndexes; + int32_t supportedLSRsLength; + Locale *ownedDefaultLocale; + const Locale *defaultLocale; +}; + +U_NAMESPACE_END + +#endif // U_SHOW_CPLUSPLUS_API +#endif // __LOCALEMATCHER_H__ diff --git a/deps/icu-small/source/common/unicode/localpointer.h b/deps/icu-small/source/common/unicode/localpointer.h index 96c659d10ad6a6..c082177250951a 100644 --- a/deps/icu-small/source/common/unicode/localpointer.h +++ b/deps/icu-small/source/common/unicode/localpointer.h @@ -1,595 +1,595 @@ -// © 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. -* -******************************************************************************* -* file name: localpointer.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2009nov13 -* created by: Markus W. Scherer -*/ - -#ifndef __LOCALPOINTER_H__ -#define __LOCALPOINTER_H__ - -/** - * \file - * \brief C++ API: "Smart pointers" for use with and in ICU4C C++ code. - * - * These classes are inspired by - * - std::auto_ptr - * - boost::scoped_ptr & boost::scoped_array - * - Taligent Safe Pointers (TOnlyPointerTo) - * - * but none of those provide for all of the goals for ICU smart pointers: - * - Smart pointer owns the object and releases it when it goes out of scope. - * - No transfer of ownership via copy/assignment to reduce misuse. Simpler & more robust. - * - ICU-compatible: No exceptions. - * - Need to be able to orphan/release the pointer and its ownership. - * - Need variants for normal C++ object pointers, C++ arrays, and ICU C service objects. - * - * For details see https://icu.unicode.org/design/cpp/scoped_ptr - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include - -U_NAMESPACE_BEGIN - -/** - * "Smart pointer" base class; do not use directly: use LocalPointer etc. - * - * Base class for smart pointer classes that do not throw exceptions. - * - * Do not use this base class directly, since it does not delete its pointer. - * A subclass must implement methods that delete the pointer: - * Destructor and adoptInstead(). - * - * There is no operator T *() provided because the programmer must decide - * whether to use getAlias() (without transfer of ownership) or orphan() - * (with transfer of ownership and NULLing of the pointer). - * - * @see LocalPointer - * @see LocalArray - * @see U_DEFINE_LOCAL_OPEN_POINTER - * @stable ICU 4.4 - */ -template -class LocalPointerBase { -public: - // No heap allocation. Use only on the stack. - static void* U_EXPORT2 operator new(size_t) = delete; - static void* U_EXPORT2 operator new[](size_t) = delete; -#if U_HAVE_PLACEMENT_NEW - static void* U_EXPORT2 operator new(size_t, void*) = delete; -#endif - - /** - * Constructor takes ownership. - * @param p simple pointer to an object that is adopted - * @stable ICU 4.4 - */ - explicit LocalPointerBase(T *p=NULL) : ptr(p) {} - /** - * Destructor deletes the object it owns. - * Subclass must override: Base class does nothing. - * @stable ICU 4.4 - */ - ~LocalPointerBase() { /* delete ptr; */ } - /** - * NULL check. - * @return true if ==NULL - * @stable ICU 4.4 - */ - UBool isNull() const { return ptr==NULL; } - /** - * NULL check. - * @return true if !=NULL - * @stable ICU 4.4 - */ - UBool isValid() const { return ptr!=NULL; } - /** - * Comparison with a simple pointer, so that existing code - * with ==NULL need not be changed. - * @param other simple pointer for comparison - * @return true if this pointer value equals other - * @stable ICU 4.4 - */ - bool operator==(const T *other) const { return ptr==other; } - /** - * Comparison with a simple pointer, so that existing code - * with !=NULL need not be changed. - * @param other simple pointer for comparison - * @return true if this pointer value differs from other - * @stable ICU 4.4 - */ - bool operator!=(const T *other) const { return ptr!=other; } - /** - * Access without ownership change. - * @return the pointer value - * @stable ICU 4.4 - */ - T *getAlias() const { return ptr; } - /** - * Access without ownership change. - * @return the pointer value as a reference - * @stable ICU 4.4 - */ - T &operator*() const { return *ptr; } - /** - * Access without ownership change. - * @return the pointer value - * @stable ICU 4.4 - */ - T *operator->() const { return ptr; } - /** - * Gives up ownership; the internal pointer becomes NULL. - * @return the pointer value; - * caller becomes responsible for deleting the object - * @stable ICU 4.4 - */ - T *orphan() { - T *p=ptr; - ptr=NULL; - return p; - } - /** - * Deletes the object it owns, - * and adopts (takes ownership of) the one passed in. - * Subclass must override: Base class does not delete the object. - * @param p simple pointer to an object that is adopted - * @stable ICU 4.4 - */ - void adoptInstead(T *p) { - // delete ptr; - ptr=p; - } -protected: - /** - * Actual pointer. - * @internal - */ - T *ptr; -private: - // No comparison operators with other LocalPointerBases. - bool operator==(const LocalPointerBase &other); - bool operator!=(const LocalPointerBase &other); - // No ownership sharing: No copy constructor, no assignment operator. - LocalPointerBase(const LocalPointerBase &other); - void operator=(const LocalPointerBase &other); -}; - -/** - * "Smart pointer" class, deletes objects via the standard C++ delete operator. - * For most methods see the LocalPointerBase base class. - * - * Usage example: - * \code - * LocalPointer s(new UnicodeString((UChar32)0x50005)); - * int32_t length=s->length(); // 2 - * char16_t lead=s->charAt(0); // 0xd900 - * if(some condition) { return; } // no need to explicitly delete the pointer - * s.adoptInstead(new UnicodeString((char16_t)0xfffc)); - * length=s->length(); // 1 - * // no need to explicitly delete the pointer - * \endcode - * - * @see LocalPointerBase - * @stable ICU 4.4 - */ -template -class LocalPointer : public LocalPointerBase { -public: - using LocalPointerBase::operator*; - using LocalPointerBase::operator->; - /** - * Constructor takes ownership. - * @param p simple pointer to an object that is adopted - * @stable ICU 4.4 - */ - explicit LocalPointer(T *p=NULL) : LocalPointerBase(p) {} - /** - * Constructor takes ownership and reports an error if NULL. - * - * This constructor is intended to be used with other-class constructors - * that may report a failure UErrorCode, - * so that callers need to check only for U_FAILURE(errorCode) - * and not also separately for isNull(). - * - * @param p simple pointer to an object that is adopted - * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR - * if p==NULL and no other failure code had been set - * @stable ICU 55 - */ - LocalPointer(T *p, UErrorCode &errorCode) : LocalPointerBase(p) { - if(p==NULL && U_SUCCESS(errorCode)) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } - } - /** - * Move constructor, leaves src with isNull(). - * @param src source smart pointer - * @stable ICU 56 - */ - LocalPointer(LocalPointer &&src) U_NOEXCEPT : LocalPointerBase(src.ptr) { - src.ptr=NULL; - } - - /** - * Constructs a LocalPointer from a C++11 std::unique_ptr. - * The LocalPointer steals the object owned by the std::unique_ptr. - * - * This constructor works via move semantics. If your std::unique_ptr is - * in a local variable, you must use std::move. - * - * @param p The std::unique_ptr from which the pointer will be stolen. - * @stable ICU 64 - */ - explicit LocalPointer(std::unique_ptr &&p) - : LocalPointerBase(p.release()) {} - - /** - * Destructor deletes the object it owns. - * @stable ICU 4.4 - */ - ~LocalPointer() { - delete LocalPointerBase::ptr; - } - /** - * Move assignment operator, leaves src with isNull(). - * The behavior is undefined if *this and src are the same object. - * @param src source smart pointer - * @return *this - * @stable ICU 56 - */ - LocalPointer &operator=(LocalPointer &&src) U_NOEXCEPT { - delete LocalPointerBase::ptr; - LocalPointerBase::ptr=src.ptr; - src.ptr=NULL; - return *this; - } - - /** - * Move-assign from an std::unique_ptr to this LocalPointer. - * Steals the pointer from the std::unique_ptr. - * - * @param p The std::unique_ptr from which the pointer will be stolen. - * @return *this - * @stable ICU 64 - */ - LocalPointer &operator=(std::unique_ptr &&p) U_NOEXCEPT { - adoptInstead(p.release()); - return *this; - } - - /** - * Swap pointers. - * @param other other smart pointer - * @stable ICU 56 - */ - void swap(LocalPointer &other) U_NOEXCEPT { - T *temp=LocalPointerBase::ptr; - LocalPointerBase::ptr=other.ptr; - other.ptr=temp; - } - /** - * Non-member LocalPointer swap function. - * @param p1 will get p2's pointer - * @param p2 will get p1's pointer - * @stable ICU 56 - */ - friend inline void swap(LocalPointer &p1, LocalPointer &p2) U_NOEXCEPT { - p1.swap(p2); - } - /** - * Deletes the object it owns, - * and adopts (takes ownership of) the one passed in. - * @param p simple pointer to an object that is adopted - * @stable ICU 4.4 - */ - void adoptInstead(T *p) { - delete LocalPointerBase::ptr; - LocalPointerBase::ptr=p; - } - /** - * Deletes the object it owns, - * and adopts (takes ownership of) the one passed in. - * - * If U_FAILURE(errorCode), then the current object is retained and the new one deleted. - * - * If U_SUCCESS(errorCode) but the input pointer is NULL, - * then U_MEMORY_ALLOCATION_ERROR is set, - * the current object is deleted, and NULL is set. - * - * @param p simple pointer to an object that is adopted - * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR - * if p==NULL and no other failure code had been set - * @stable ICU 55 - */ - void adoptInsteadAndCheckErrorCode(T *p, UErrorCode &errorCode) { - if(U_SUCCESS(errorCode)) { - delete LocalPointerBase::ptr; - LocalPointerBase::ptr=p; - if(p==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } - } else { - delete p; - } - } - - /** - * Conversion operator to a C++11 std::unique_ptr. - * Disowns the object and gives it to the returned std::unique_ptr. - * - * This operator works via move semantics. If your LocalPointer is - * in a local variable, you must use std::move. - * - * @return An std::unique_ptr owning the pointer previously owned by this - * icu::LocalPointer. - * @stable ICU 64 - */ - operator std::unique_ptr () && { - return std::unique_ptr(LocalPointerBase::orphan()); - } -}; - -/** - * "Smart pointer" class, deletes objects via the C++ array delete[] operator. - * For most methods see the LocalPointerBase base class. - * Adds operator[] for array item access. - * - * Usage example: - * \code - * LocalArray a(new UnicodeString[2]); - * a[0].append((char16_t)0x61); - * if(some condition) { return; } // no need to explicitly delete the array - * a.adoptInstead(new UnicodeString[4]); - * a[3].append((char16_t)0x62).append((char16_t)0x63).reverse(); - * // no need to explicitly delete the array - * \endcode - * - * @see LocalPointerBase - * @stable ICU 4.4 - */ -template -class LocalArray : public LocalPointerBase { -public: - using LocalPointerBase::operator*; - using LocalPointerBase::operator->; - /** - * Constructor takes ownership. - * @param p simple pointer to an array of T objects that is adopted - * @stable ICU 4.4 - */ - explicit LocalArray(T *p=NULL) : LocalPointerBase(p) {} - /** - * Constructor takes ownership and reports an error if NULL. - * - * This constructor is intended to be used with other-class constructors - * that may report a failure UErrorCode, - * so that callers need to check only for U_FAILURE(errorCode) - * and not also separately for isNull(). - * - * @param p simple pointer to an array of T objects that is adopted - * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR - * if p==NULL and no other failure code had been set - * @stable ICU 56 - */ - LocalArray(T *p, UErrorCode &errorCode) : LocalPointerBase(p) { - if(p==NULL && U_SUCCESS(errorCode)) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } - } - /** - * Move constructor, leaves src with isNull(). - * @param src source smart pointer - * @stable ICU 56 - */ - LocalArray(LocalArray &&src) U_NOEXCEPT : LocalPointerBase(src.ptr) { - src.ptr=NULL; - } - - /** - * Constructs a LocalArray from a C++11 std::unique_ptr of an array type. - * The LocalPointer steals the array owned by the std::unique_ptr. - * - * This constructor works via move semantics. If your std::unique_ptr is - * in a local variable, you must use std::move. - * - * @param p The std::unique_ptr from which the array will be stolen. - * @stable ICU 64 - */ - explicit LocalArray(std::unique_ptr &&p) - : LocalPointerBase(p.release()) {} - - /** - * Destructor deletes the array it owns. - * @stable ICU 4.4 - */ - ~LocalArray() { - delete[] LocalPointerBase::ptr; - } - /** - * Move assignment operator, leaves src with isNull(). - * The behavior is undefined if *this and src are the same object. - * @param src source smart pointer - * @return *this - * @stable ICU 56 - */ - LocalArray &operator=(LocalArray &&src) U_NOEXCEPT { - delete[] LocalPointerBase::ptr; - LocalPointerBase::ptr=src.ptr; - src.ptr=NULL; - return *this; - } - - /** - * Move-assign from an std::unique_ptr to this LocalPointer. - * Steals the array from the std::unique_ptr. - * - * @param p The std::unique_ptr from which the array will be stolen. - * @return *this - * @stable ICU 64 - */ - LocalArray &operator=(std::unique_ptr &&p) U_NOEXCEPT { - adoptInstead(p.release()); - return *this; - } - - /** - * Swap pointers. - * @param other other smart pointer - * @stable ICU 56 - */ - void swap(LocalArray &other) U_NOEXCEPT { - T *temp=LocalPointerBase::ptr; - LocalPointerBase::ptr=other.ptr; - other.ptr=temp; - } - /** - * Non-member LocalArray swap function. - * @param p1 will get p2's pointer - * @param p2 will get p1's pointer - * @stable ICU 56 - */ - friend inline void swap(LocalArray &p1, LocalArray &p2) U_NOEXCEPT { - p1.swap(p2); - } - /** - * Deletes the array it owns, - * and adopts (takes ownership of) the one passed in. - * @param p simple pointer to an array of T objects that is adopted - * @stable ICU 4.4 - */ - void adoptInstead(T *p) { - delete[] LocalPointerBase::ptr; - LocalPointerBase::ptr=p; - } - /** - * Deletes the array it owns, - * and adopts (takes ownership of) the one passed in. - * - * If U_FAILURE(errorCode), then the current array is retained and the new one deleted. - * - * If U_SUCCESS(errorCode) but the input pointer is NULL, - * then U_MEMORY_ALLOCATION_ERROR is set, - * the current array is deleted, and NULL is set. - * - * @param p simple pointer to an array of T objects that is adopted - * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR - * if p==NULL and no other failure code had been set - * @stable ICU 56 - */ - void adoptInsteadAndCheckErrorCode(T *p, UErrorCode &errorCode) { - if(U_SUCCESS(errorCode)) { - delete[] LocalPointerBase::ptr; - LocalPointerBase::ptr=p; - if(p==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } - } else { - delete[] p; - } - } - /** - * Array item access (writable). - * No index bounds check. - * @param i array index - * @return reference to the array item - * @stable ICU 4.4 - */ - T &operator[](ptrdiff_t i) const { return LocalPointerBase::ptr[i]; } - - /** - * Conversion operator to a C++11 std::unique_ptr. - * Disowns the object and gives it to the returned std::unique_ptr. - * - * This operator works via move semantics. If your LocalPointer is - * in a local variable, you must use std::move. - * - * @return An std::unique_ptr owning the pointer previously owned by this - * icu::LocalPointer. - * @stable ICU 64 - */ - operator std::unique_ptr () && { - return std::unique_ptr(LocalPointerBase::orphan()); - } -}; - -/** - * \def U_DEFINE_LOCAL_OPEN_POINTER - * "Smart pointer" definition macro, deletes objects via the closeFunction. - * Defines a subclass of LocalPointerBase which works just - * like LocalPointer except that this subclass will use the closeFunction - * rather than the C++ delete operator. - * - * Usage example: - * \code - * LocalUCaseMapPointer csm(ucasemap_open(localeID, options, &errorCode)); - * utf8OutLength=ucasemap_utf8ToLower(csm.getAlias(), - * utf8Out, (int32_t)sizeof(utf8Out), - * utf8In, utf8InLength, &errorCode); - * if(U_FAILURE(errorCode)) { return; } // no need to explicitly delete the UCaseMap - * \endcode - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -#define U_DEFINE_LOCAL_OPEN_POINTER(LocalPointerClassName, Type, closeFunction) \ - class LocalPointerClassName : public LocalPointerBase { \ - public: \ - using LocalPointerBase::operator*; \ - using LocalPointerBase::operator->; \ - explicit LocalPointerClassName(Type *p=NULL) : LocalPointerBase(p) {} \ - LocalPointerClassName(LocalPointerClassName &&src) U_NOEXCEPT \ - : LocalPointerBase(src.ptr) { \ - src.ptr=NULL; \ - } \ - /* TODO: Be agnostic of the deleter function signature from the user-provided std::unique_ptr? */ \ - explicit LocalPointerClassName(std::unique_ptr &&p) \ - : LocalPointerBase(p.release()) {} \ - ~LocalPointerClassName() { if (ptr != NULL) { closeFunction(ptr); } } \ - LocalPointerClassName &operator=(LocalPointerClassName &&src) U_NOEXCEPT { \ - if (ptr != NULL) { closeFunction(ptr); } \ - LocalPointerBase::ptr=src.ptr; \ - src.ptr=NULL; \ - return *this; \ - } \ - /* TODO: Be agnostic of the deleter function signature from the user-provided std::unique_ptr? */ \ - LocalPointerClassName &operator=(std::unique_ptr &&p) { \ - adoptInstead(p.release()); \ - return *this; \ - } \ - void swap(LocalPointerClassName &other) U_NOEXCEPT { \ - Type *temp=LocalPointerBase::ptr; \ - LocalPointerBase::ptr=other.ptr; \ - other.ptr=temp; \ - } \ - friend inline void swap(LocalPointerClassName &p1, LocalPointerClassName &p2) U_NOEXCEPT { \ - p1.swap(p2); \ - } \ - void adoptInstead(Type *p) { \ - if (ptr != NULL) { closeFunction(ptr); } \ - ptr=p; \ - } \ - operator std::unique_ptr () && { \ - return std::unique_ptr(LocalPointerBase::orphan(), closeFunction); \ - } \ - } - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ -#endif /* __LOCALPOINTER_H__ */ +// © 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. +* +******************************************************************************* +* file name: localpointer.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2009nov13 +* created by: Markus W. Scherer +*/ + +#ifndef __LOCALPOINTER_H__ +#define __LOCALPOINTER_H__ + +/** + * \file + * \brief C++ API: "Smart pointers" for use with and in ICU4C C++ code. + * + * These classes are inspired by + * - std::auto_ptr + * - boost::scoped_ptr & boost::scoped_array + * - Taligent Safe Pointers (TOnlyPointerTo) + * + * but none of those provide for all of the goals for ICU smart pointers: + * - Smart pointer owns the object and releases it when it goes out of scope. + * - No transfer of ownership via copy/assignment to reduce misuse. Simpler & more robust. + * - ICU-compatible: No exceptions. + * - Need to be able to orphan/release the pointer and its ownership. + * - Need variants for normal C++ object pointers, C++ arrays, and ICU C service objects. + * + * For details see https://icu.unicode.org/design/cpp/scoped_ptr + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include + +U_NAMESPACE_BEGIN + +/** + * "Smart pointer" base class; do not use directly: use LocalPointer etc. + * + * Base class for smart pointer classes that do not throw exceptions. + * + * Do not use this base class directly, since it does not delete its pointer. + * A subclass must implement methods that delete the pointer: + * Destructor and adoptInstead(). + * + * There is no operator T *() provided because the programmer must decide + * whether to use getAlias() (without transfer of ownership) or orphan() + * (with transfer of ownership and NULLing of the pointer). + * + * @see LocalPointer + * @see LocalArray + * @see U_DEFINE_LOCAL_OPEN_POINTER + * @stable ICU 4.4 + */ +template +class LocalPointerBase { +public: + // No heap allocation. Use only on the stack. + static void* U_EXPORT2 operator new(size_t) = delete; + static void* U_EXPORT2 operator new[](size_t) = delete; +#if U_HAVE_PLACEMENT_NEW + static void* U_EXPORT2 operator new(size_t, void*) = delete; +#endif + + /** + * Constructor takes ownership. + * @param p simple pointer to an object that is adopted + * @stable ICU 4.4 + */ + explicit LocalPointerBase(T *p=nullptr) : ptr(p) {} + /** + * Destructor deletes the object it owns. + * Subclass must override: Base class does nothing. + * @stable ICU 4.4 + */ + ~LocalPointerBase() { /* delete ptr; */ } + /** + * nullptr check. + * @return true if ==nullptr + * @stable ICU 4.4 + */ + UBool isNull() const { return ptr==nullptr; } + /** + * nullptr check. + * @return true if !=nullptr + * @stable ICU 4.4 + */ + UBool isValid() const { return ptr!=nullptr; } + /** + * Comparison with a simple pointer, so that existing code + * with ==nullptr need not be changed. + * @param other simple pointer for comparison + * @return true if this pointer value equals other + * @stable ICU 4.4 + */ + bool operator==(const T *other) const { return ptr==other; } + /** + * Comparison with a simple pointer, so that existing code + * with !=nullptr need not be changed. + * @param other simple pointer for comparison + * @return true if this pointer value differs from other + * @stable ICU 4.4 + */ + bool operator!=(const T *other) const { return ptr!=other; } + /** + * Access without ownership change. + * @return the pointer value + * @stable ICU 4.4 + */ + T *getAlias() const { return ptr; } + /** + * Access without ownership change. + * @return the pointer value as a reference + * @stable ICU 4.4 + */ + T &operator*() const { return *ptr; } + /** + * Access without ownership change. + * @return the pointer value + * @stable ICU 4.4 + */ + T *operator->() const { return ptr; } + /** + * Gives up ownership; the internal pointer becomes nullptr. + * @return the pointer value; + * caller becomes responsible for deleting the object + * @stable ICU 4.4 + */ + T *orphan() { + T *p=ptr; + ptr=nullptr; + return p; + } + /** + * Deletes the object it owns, + * and adopts (takes ownership of) the one passed in. + * Subclass must override: Base class does not delete the object. + * @param p simple pointer to an object that is adopted + * @stable ICU 4.4 + */ + void adoptInstead(T *p) { + // delete ptr; + ptr=p; + } +protected: + /** + * Actual pointer. + * @internal + */ + T *ptr; +private: + // No comparison operators with other LocalPointerBases. + bool operator==(const LocalPointerBase &other); + bool operator!=(const LocalPointerBase &other); + // No ownership sharing: No copy constructor, no assignment operator. + LocalPointerBase(const LocalPointerBase &other); + void operator=(const LocalPointerBase &other); +}; + +/** + * "Smart pointer" class, deletes objects via the standard C++ delete operator. + * For most methods see the LocalPointerBase base class. + * + * Usage example: + * \code + * LocalPointer s(new UnicodeString((UChar32)0x50005)); + * int32_t length=s->length(); // 2 + * char16_t lead=s->charAt(0); // 0xd900 + * if(some condition) { return; } // no need to explicitly delete the pointer + * s.adoptInstead(new UnicodeString((char16_t)0xfffc)); + * length=s->length(); // 1 + * // no need to explicitly delete the pointer + * \endcode + * + * @see LocalPointerBase + * @stable ICU 4.4 + */ +template +class LocalPointer : public LocalPointerBase { +public: + using LocalPointerBase::operator*; + using LocalPointerBase::operator->; + /** + * Constructor takes ownership. + * @param p simple pointer to an object that is adopted + * @stable ICU 4.4 + */ + explicit LocalPointer(T *p=nullptr) : LocalPointerBase(p) {} + /** + * Constructor takes ownership and reports an error if nullptr. + * + * This constructor is intended to be used with other-class constructors + * that may report a failure UErrorCode, + * so that callers need to check only for U_FAILURE(errorCode) + * and not also separately for isNull(). + * + * @param p simple pointer to an object that is adopted + * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR + * if p==nullptr and no other failure code had been set + * @stable ICU 55 + */ + LocalPointer(T *p, UErrorCode &errorCode) : LocalPointerBase(p) { + if(p==nullptr && U_SUCCESS(errorCode)) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } + } + /** + * Move constructor, leaves src with isNull(). + * @param src source smart pointer + * @stable ICU 56 + */ + LocalPointer(LocalPointer &&src) noexcept : LocalPointerBase(src.ptr) { + src.ptr=nullptr; + } + + /** + * Constructs a LocalPointer from a C++11 std::unique_ptr. + * The LocalPointer steals the object owned by the std::unique_ptr. + * + * This constructor works via move semantics. If your std::unique_ptr is + * in a local variable, you must use std::move. + * + * @param p The std::unique_ptr from which the pointer will be stolen. + * @stable ICU 64 + */ + explicit LocalPointer(std::unique_ptr &&p) + : LocalPointerBase(p.release()) {} + + /** + * Destructor deletes the object it owns. + * @stable ICU 4.4 + */ + ~LocalPointer() { + delete LocalPointerBase::ptr; + } + /** + * Move assignment operator, leaves src with isNull(). + * The behavior is undefined if *this and src are the same object. + * @param src source smart pointer + * @return *this + * @stable ICU 56 + */ + LocalPointer &operator=(LocalPointer &&src) noexcept { + delete LocalPointerBase::ptr; + LocalPointerBase::ptr=src.ptr; + src.ptr=nullptr; + return *this; + } + + /** + * Move-assign from an std::unique_ptr to this LocalPointer. + * Steals the pointer from the std::unique_ptr. + * + * @param p The std::unique_ptr from which the pointer will be stolen. + * @return *this + * @stable ICU 64 + */ + LocalPointer &operator=(std::unique_ptr &&p) noexcept { + adoptInstead(p.release()); + return *this; + } + + /** + * Swap pointers. + * @param other other smart pointer + * @stable ICU 56 + */ + void swap(LocalPointer &other) noexcept { + T *temp=LocalPointerBase::ptr; + LocalPointerBase::ptr=other.ptr; + other.ptr=temp; + } + /** + * Non-member LocalPointer swap function. + * @param p1 will get p2's pointer + * @param p2 will get p1's pointer + * @stable ICU 56 + */ + friend inline void swap(LocalPointer &p1, LocalPointer &p2) noexcept { + p1.swap(p2); + } + /** + * Deletes the object it owns, + * and adopts (takes ownership of) the one passed in. + * @param p simple pointer to an object that is adopted + * @stable ICU 4.4 + */ + void adoptInstead(T *p) { + delete LocalPointerBase::ptr; + LocalPointerBase::ptr=p; + } + /** + * Deletes the object it owns, + * and adopts (takes ownership of) the one passed in. + * + * If U_FAILURE(errorCode), then the current object is retained and the new one deleted. + * + * If U_SUCCESS(errorCode) but the input pointer is nullptr, + * then U_MEMORY_ALLOCATION_ERROR is set, + * the current object is deleted, and nullptr is set. + * + * @param p simple pointer to an object that is adopted + * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR + * if p==nullptr and no other failure code had been set + * @stable ICU 55 + */ + void adoptInsteadAndCheckErrorCode(T *p, UErrorCode &errorCode) { + if(U_SUCCESS(errorCode)) { + delete LocalPointerBase::ptr; + LocalPointerBase::ptr=p; + if(p==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } + } else { + delete p; + } + } + + /** + * Conversion operator to a C++11 std::unique_ptr. + * Disowns the object and gives it to the returned std::unique_ptr. + * + * This operator works via move semantics. If your LocalPointer is + * in a local variable, you must use std::move. + * + * @return An std::unique_ptr owning the pointer previously owned by this + * icu::LocalPointer. + * @stable ICU 64 + */ + operator std::unique_ptr () && { + return std::unique_ptr(LocalPointerBase::orphan()); + } +}; + +/** + * "Smart pointer" class, deletes objects via the C++ array delete[] operator. + * For most methods see the LocalPointerBase base class. + * Adds operator[] for array item access. + * + * Usage example: + * \code + * LocalArray a(new UnicodeString[2]); + * a[0].append((char16_t)0x61); + * if(some condition) { return; } // no need to explicitly delete the array + * a.adoptInstead(new UnicodeString[4]); + * a[3].append((char16_t)0x62).append((char16_t)0x63).reverse(); + * // no need to explicitly delete the array + * \endcode + * + * @see LocalPointerBase + * @stable ICU 4.4 + */ +template +class LocalArray : public LocalPointerBase { +public: + using LocalPointerBase::operator*; + using LocalPointerBase::operator->; + /** + * Constructor takes ownership. + * @param p simple pointer to an array of T objects that is adopted + * @stable ICU 4.4 + */ + explicit LocalArray(T *p=nullptr) : LocalPointerBase(p) {} + /** + * Constructor takes ownership and reports an error if nullptr. + * + * This constructor is intended to be used with other-class constructors + * that may report a failure UErrorCode, + * so that callers need to check only for U_FAILURE(errorCode) + * and not also separately for isNull(). + * + * @param p simple pointer to an array of T objects that is adopted + * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR + * if p==nullptr and no other failure code had been set + * @stable ICU 56 + */ + LocalArray(T *p, UErrorCode &errorCode) : LocalPointerBase(p) { + if(p==nullptr && U_SUCCESS(errorCode)) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } + } + /** + * Move constructor, leaves src with isNull(). + * @param src source smart pointer + * @stable ICU 56 + */ + LocalArray(LocalArray &&src) noexcept : LocalPointerBase(src.ptr) { + src.ptr=nullptr; + } + + /** + * Constructs a LocalArray from a C++11 std::unique_ptr of an array type. + * The LocalPointer steals the array owned by the std::unique_ptr. + * + * This constructor works via move semantics. If your std::unique_ptr is + * in a local variable, you must use std::move. + * + * @param p The std::unique_ptr from which the array will be stolen. + * @stable ICU 64 + */ + explicit LocalArray(std::unique_ptr &&p) + : LocalPointerBase(p.release()) {} + + /** + * Destructor deletes the array it owns. + * @stable ICU 4.4 + */ + ~LocalArray() { + delete[] LocalPointerBase::ptr; + } + /** + * Move assignment operator, leaves src with isNull(). + * The behavior is undefined if *this and src are the same object. + * @param src source smart pointer + * @return *this + * @stable ICU 56 + */ + LocalArray &operator=(LocalArray &&src) noexcept { + delete[] LocalPointerBase::ptr; + LocalPointerBase::ptr=src.ptr; + src.ptr=nullptr; + return *this; + } + + /** + * Move-assign from an std::unique_ptr to this LocalPointer. + * Steals the array from the std::unique_ptr. + * + * @param p The std::unique_ptr from which the array will be stolen. + * @return *this + * @stable ICU 64 + */ + LocalArray &operator=(std::unique_ptr &&p) noexcept { + adoptInstead(p.release()); + return *this; + } + + /** + * Swap pointers. + * @param other other smart pointer + * @stable ICU 56 + */ + void swap(LocalArray &other) noexcept { + T *temp=LocalPointerBase::ptr; + LocalPointerBase::ptr=other.ptr; + other.ptr=temp; + } + /** + * Non-member LocalArray swap function. + * @param p1 will get p2's pointer + * @param p2 will get p1's pointer + * @stable ICU 56 + */ + friend inline void swap(LocalArray &p1, LocalArray &p2) noexcept { + p1.swap(p2); + } + /** + * Deletes the array it owns, + * and adopts (takes ownership of) the one passed in. + * @param p simple pointer to an array of T objects that is adopted + * @stable ICU 4.4 + */ + void adoptInstead(T *p) { + delete[] LocalPointerBase::ptr; + LocalPointerBase::ptr=p; + } + /** + * Deletes the array it owns, + * and adopts (takes ownership of) the one passed in. + * + * If U_FAILURE(errorCode), then the current array is retained and the new one deleted. + * + * If U_SUCCESS(errorCode) but the input pointer is nullptr, + * then U_MEMORY_ALLOCATION_ERROR is set, + * the current array is deleted, and nullptr is set. + * + * @param p simple pointer to an array of T objects that is adopted + * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR + * if p==nullptr and no other failure code had been set + * @stable ICU 56 + */ + void adoptInsteadAndCheckErrorCode(T *p, UErrorCode &errorCode) { + if(U_SUCCESS(errorCode)) { + delete[] LocalPointerBase::ptr; + LocalPointerBase::ptr=p; + if(p==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } + } else { + delete[] p; + } + } + /** + * Array item access (writable). + * No index bounds check. + * @param i array index + * @return reference to the array item + * @stable ICU 4.4 + */ + T &operator[](ptrdiff_t i) const { return LocalPointerBase::ptr[i]; } + + /** + * Conversion operator to a C++11 std::unique_ptr. + * Disowns the object and gives it to the returned std::unique_ptr. + * + * This operator works via move semantics. If your LocalPointer is + * in a local variable, you must use std::move. + * + * @return An std::unique_ptr owning the pointer previously owned by this + * icu::LocalPointer. + * @stable ICU 64 + */ + operator std::unique_ptr () && { + return std::unique_ptr(LocalPointerBase::orphan()); + } +}; + +/** + * \def U_DEFINE_LOCAL_OPEN_POINTER + * "Smart pointer" definition macro, deletes objects via the closeFunction. + * Defines a subclass of LocalPointerBase which works just + * like LocalPointer except that this subclass will use the closeFunction + * rather than the C++ delete operator. + * + * Usage example: + * \code + * LocalUCaseMapPointer csm(ucasemap_open(localeID, options, &errorCode)); + * utf8OutLength=ucasemap_utf8ToLower(csm.getAlias(), + * utf8Out, (int32_t)sizeof(utf8Out), + * utf8In, utf8InLength, &errorCode); + * if(U_FAILURE(errorCode)) { return; } // no need to explicitly delete the UCaseMap + * \endcode + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +#define U_DEFINE_LOCAL_OPEN_POINTER(LocalPointerClassName, Type, closeFunction) \ + class LocalPointerClassName : public LocalPointerBase { \ + public: \ + using LocalPointerBase::operator*; \ + using LocalPointerBase::operator->; \ + explicit LocalPointerClassName(Type *p=nullptr) : LocalPointerBase(p) {} \ + LocalPointerClassName(LocalPointerClassName &&src) noexcept \ + : LocalPointerBase(src.ptr) { \ + src.ptr=nullptr; \ + } \ + /* TODO: Be agnostic of the deleter function signature from the user-provided std::unique_ptr? */ \ + explicit LocalPointerClassName(std::unique_ptr &&p) \ + : LocalPointerBase(p.release()) {} \ + ~LocalPointerClassName() { if (ptr != nullptr) { closeFunction(ptr); } } \ + LocalPointerClassName &operator=(LocalPointerClassName &&src) noexcept { \ + if (ptr != nullptr) { closeFunction(ptr); } \ + LocalPointerBase::ptr=src.ptr; \ + src.ptr=nullptr; \ + return *this; \ + } \ + /* TODO: Be agnostic of the deleter function signature from the user-provided std::unique_ptr? */ \ + LocalPointerClassName &operator=(std::unique_ptr &&p) { \ + adoptInstead(p.release()); \ + return *this; \ + } \ + void swap(LocalPointerClassName &other) noexcept { \ + Type *temp=LocalPointerBase::ptr; \ + LocalPointerBase::ptr=other.ptr; \ + other.ptr=temp; \ + } \ + friend inline void swap(LocalPointerClassName &p1, LocalPointerClassName &p2) noexcept { \ + p1.swap(p2); \ + } \ + void adoptInstead(Type *p) { \ + if (ptr != nullptr) { closeFunction(ptr); } \ + ptr=p; \ + } \ + operator std::unique_ptr () && { \ + return std::unique_ptr(LocalPointerBase::orphan(), closeFunction); \ + } \ + } + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ +#endif /* __LOCALPOINTER_H__ */ diff --git a/deps/icu-small/source/common/unicode/locdspnm.h b/deps/icu-small/source/common/unicode/locdspnm.h index 4f06f857044c6e..e7e4c828d34110 100644 --- a/deps/icu-small/source/common/unicode/locdspnm.h +++ b/deps/icu-small/source/common/unicode/locdspnm.h @@ -1,211 +1,211 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 2010-2016, International Business Machines Corporation and -* others. All Rights Reserved. -****************************************************************************** -*/ - -#ifndef LOCDSPNM_H -#define LOCDSPNM_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -/** - * \file - * \brief C++ API: Provides display names of Locale and its components. - */ - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/locid.h" -#include "unicode/strenum.h" -#include "unicode/uscript.h" -#include "unicode/uldnames.h" -#include "unicode/udisplaycontext.h" - -U_NAMESPACE_BEGIN - -/** - * Returns display names of Locales and components of Locales. For - * more information on language, script, region, variant, key, and - * values, see Locale. - * @stable ICU 4.4 - */ -class U_COMMON_API LocaleDisplayNames : public UObject { -public: - /** - * Destructor. - * @stable ICU 4.4 - */ - virtual ~LocaleDisplayNames(); - - /** - * Convenience overload of - * {@link #createInstance(const Locale& locale, UDialectHandling dialectHandling)} - * that specifies STANDARD dialect handling. - * @param locale the display locale - * @return a LocaleDisplayNames instance - * @stable ICU 4.4 - */ - inline static LocaleDisplayNames* U_EXPORT2 createInstance(const Locale& locale); - - /** - * Returns an instance of LocaleDisplayNames that returns names - * formatted for the provided locale, using the provided - * dialectHandling. - * - * @param locale the display locale - * @param dialectHandling how to select names for locales - * @return a LocaleDisplayNames instance - * @stable ICU 4.4 - */ - static LocaleDisplayNames* U_EXPORT2 createInstance(const Locale& locale, - UDialectHandling dialectHandling); - - /** - * Returns an instance of LocaleDisplayNames that returns names formatted - * for the provided locale, using the provided UDisplayContext settings. - * - * @param locale the display locale - * @param contexts List of one or more context settings (e.g. for dialect - * handling, capitalization, etc. - * @param length Number of items in the contexts list - * @return a LocaleDisplayNames instance - * @stable ICU 51 - */ - static LocaleDisplayNames* U_EXPORT2 createInstance(const Locale& locale, - UDisplayContext *contexts, int32_t length); - - // getters for state - /** - * Returns the locale used to determine the display names. This is - * not necessarily the same locale passed to {@link #createInstance}. - * @return the display locale - * @stable ICU 4.4 - */ - virtual const Locale& getLocale() const = 0; - - /** - * Returns the dialect handling used in the display names. - * @return the dialect handling enum - * @stable ICU 4.4 - */ - virtual UDialectHandling getDialectHandling() const = 0; - - /** - * Returns the UDisplayContext value for the specified UDisplayContextType. - * @param type the UDisplayContextType whose value to return - * @return the UDisplayContext for the specified type. - * @stable ICU 51 - */ - virtual UDisplayContext getContext(UDisplayContextType type) const = 0; - - // names for entire locales - /** - * Returns the display name of the provided locale. - * @param locale the locale whose display name to return - * @param result receives the locale's display name - * @return the display name of the provided locale - * @stable ICU 4.4 - */ - virtual UnicodeString& localeDisplayName(const Locale& locale, - UnicodeString& result) const = 0; - - /** - * Returns the display name of the provided locale id. - * @param localeId the id of the locale whose display name to return - * @param result receives the locale's display name - * @return the display name of the provided locale - * @stable ICU 4.4 - */ - virtual UnicodeString& localeDisplayName(const char* localeId, - UnicodeString& result) const = 0; - - // names for components of a locale id - /** - * Returns the display name of the provided language code. - * @param lang the language code - * @param result receives the language code's display name - * @return the display name of the provided language code - * @stable ICU 4.4 - */ - virtual UnicodeString& languageDisplayName(const char* lang, - UnicodeString& result) const = 0; - - /** - * Returns the display name of the provided script code. - * @param script the script code - * @param result receives the script code's display name - * @return the display name of the provided script code - * @stable ICU 4.4 - */ - virtual UnicodeString& scriptDisplayName(const char* script, - UnicodeString& result) const = 0; - - /** - * Returns the display name of the provided script code. - * @param scriptCode the script code number - * @param result receives the script code's display name - * @return the display name of the provided script code - * @stable ICU 4.4 - */ - virtual UnicodeString& scriptDisplayName(UScriptCode scriptCode, - UnicodeString& result) const = 0; - - /** - * Returns the display name of the provided region code. - * @param region the region code - * @param result receives the region code's display name - * @return the display name of the provided region code - * @stable ICU 4.4 - */ - virtual UnicodeString& regionDisplayName(const char* region, - UnicodeString& result) const = 0; - - /** - * Returns the display name of the provided variant. - * @param variant the variant string - * @param result receives the variant's display name - * @return the display name of the provided variant - * @stable ICU 4.4 - */ - virtual UnicodeString& variantDisplayName(const char* variant, - UnicodeString& result) const = 0; - - /** - * Returns the display name of the provided locale key. - * @param key the locale key name - * @param result receives the locale key's display name - * @return the display name of the provided locale key - * @stable ICU 4.4 - */ - virtual UnicodeString& keyDisplayName(const char* key, - UnicodeString& result) const = 0; - - /** - * Returns the display name of the provided value (used with the provided key). - * @param key the locale key name - * @param value the locale key's value - * @param result receives the value's display name - * @return the display name of the provided value - * @stable ICU 4.4 - */ - virtual UnicodeString& keyValueDisplayName(const char* key, const char* value, - UnicodeString& result) const = 0; -}; - -inline LocaleDisplayNames* LocaleDisplayNames::createInstance(const Locale& locale) { - return LocaleDisplayNames::createInstance(locale, ULDN_STANDARD_NAMES); -} - -U_NAMESPACE_END - -#endif - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 2010-2016, International Business Machines Corporation and +* others. All Rights Reserved. +****************************************************************************** +*/ + +#ifndef LOCDSPNM_H +#define LOCDSPNM_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +/** + * \file + * \brief C++ API: Provides display names of Locale and its components. + */ + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/locid.h" +#include "unicode/strenum.h" +#include "unicode/uscript.h" +#include "unicode/uldnames.h" +#include "unicode/udisplaycontext.h" + +U_NAMESPACE_BEGIN + +/** + * Returns display names of Locales and components of Locales. For + * more information on language, script, region, variant, key, and + * values, see Locale. + * @stable ICU 4.4 + */ +class U_COMMON_API LocaleDisplayNames : public UObject { +public: + /** + * Destructor. + * @stable ICU 4.4 + */ + virtual ~LocaleDisplayNames(); + + /** + * Convenience overload of + * {@link #createInstance(const Locale& locale, UDialectHandling dialectHandling)} + * that specifies STANDARD dialect handling. + * @param locale the display locale + * @return a LocaleDisplayNames instance + * @stable ICU 4.4 + */ + inline static LocaleDisplayNames* U_EXPORT2 createInstance(const Locale& locale); + + /** + * Returns an instance of LocaleDisplayNames that returns names + * formatted for the provided locale, using the provided + * dialectHandling. + * + * @param locale the display locale + * @param dialectHandling how to select names for locales + * @return a LocaleDisplayNames instance + * @stable ICU 4.4 + */ + static LocaleDisplayNames* U_EXPORT2 createInstance(const Locale& locale, + UDialectHandling dialectHandling); + + /** + * Returns an instance of LocaleDisplayNames that returns names formatted + * for the provided locale, using the provided UDisplayContext settings. + * + * @param locale the display locale + * @param contexts List of one or more context settings (e.g. for dialect + * handling, capitalization, etc. + * @param length Number of items in the contexts list + * @return a LocaleDisplayNames instance + * @stable ICU 51 + */ + static LocaleDisplayNames* U_EXPORT2 createInstance(const Locale& locale, + UDisplayContext *contexts, int32_t length); + + // getters for state + /** + * Returns the locale used to determine the display names. This is + * not necessarily the same locale passed to {@link #createInstance}. + * @return the display locale + * @stable ICU 4.4 + */ + virtual const Locale& getLocale() const = 0; + + /** + * Returns the dialect handling used in the display names. + * @return the dialect handling enum + * @stable ICU 4.4 + */ + virtual UDialectHandling getDialectHandling() const = 0; + + /** + * Returns the UDisplayContext value for the specified UDisplayContextType. + * @param type the UDisplayContextType whose value to return + * @return the UDisplayContext for the specified type. + * @stable ICU 51 + */ + virtual UDisplayContext getContext(UDisplayContextType type) const = 0; + + // names for entire locales + /** + * Returns the display name of the provided locale. + * @param locale the locale whose display name to return + * @param result receives the locale's display name + * @return the display name of the provided locale + * @stable ICU 4.4 + */ + virtual UnicodeString& localeDisplayName(const Locale& locale, + UnicodeString& result) const = 0; + + /** + * Returns the display name of the provided locale id. + * @param localeId the id of the locale whose display name to return + * @param result receives the locale's display name + * @return the display name of the provided locale + * @stable ICU 4.4 + */ + virtual UnicodeString& localeDisplayName(const char* localeId, + UnicodeString& result) const = 0; + + // names for components of a locale id + /** + * Returns the display name of the provided language code. + * @param lang the language code + * @param result receives the language code's display name + * @return the display name of the provided language code + * @stable ICU 4.4 + */ + virtual UnicodeString& languageDisplayName(const char* lang, + UnicodeString& result) const = 0; + + /** + * Returns the display name of the provided script code. + * @param script the script code + * @param result receives the script code's display name + * @return the display name of the provided script code + * @stable ICU 4.4 + */ + virtual UnicodeString& scriptDisplayName(const char* script, + UnicodeString& result) const = 0; + + /** + * Returns the display name of the provided script code. + * @param scriptCode the script code number + * @param result receives the script code's display name + * @return the display name of the provided script code + * @stable ICU 4.4 + */ + virtual UnicodeString& scriptDisplayName(UScriptCode scriptCode, + UnicodeString& result) const = 0; + + /** + * Returns the display name of the provided region code. + * @param region the region code + * @param result receives the region code's display name + * @return the display name of the provided region code + * @stable ICU 4.4 + */ + virtual UnicodeString& regionDisplayName(const char* region, + UnicodeString& result) const = 0; + + /** + * Returns the display name of the provided variant. + * @param variant the variant string + * @param result receives the variant's display name + * @return the display name of the provided variant + * @stable ICU 4.4 + */ + virtual UnicodeString& variantDisplayName(const char* variant, + UnicodeString& result) const = 0; + + /** + * Returns the display name of the provided locale key. + * @param key the locale key name + * @param result receives the locale key's display name + * @return the display name of the provided locale key + * @stable ICU 4.4 + */ + virtual UnicodeString& keyDisplayName(const char* key, + UnicodeString& result) const = 0; + + /** + * Returns the display name of the provided value (used with the provided key). + * @param key the locale key name + * @param value the locale key's value + * @param result receives the value's display name + * @return the display name of the provided value + * @stable ICU 4.4 + */ + virtual UnicodeString& keyValueDisplayName(const char* key, const char* value, + UnicodeString& result) const = 0; +}; + +inline LocaleDisplayNames* LocaleDisplayNames::createInstance(const Locale& locale) { + return LocaleDisplayNames::createInstance(locale, ULDN_STANDARD_NAMES); +} + +U_NAMESPACE_END + +#endif + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/locid.h b/deps/icu-small/source/common/unicode/locid.h index 2f2b3998a78c13..6f4a35ac596163 100644 --- a/deps/icu-small/source/common/unicode/locid.h +++ b/deps/icu-small/source/common/unicode/locid.h @@ -1,1272 +1,1272 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1996-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* File locid.h -* -* Created by: Helena Shih -* -* Modification History: -* -* Date Name Description -* 02/11/97 aliu Changed gLocPath to fgLocPath and added methods to -* get and set it. -* 04/02/97 aliu Made operator!= inline; fixed return value of getName(). -* 04/15/97 aliu Cleanup for AIX/Win32. -* 04/24/97 aliu Numerous changes per code review. -* 08/18/98 stephen Added tokenizeString(),changed getDisplayName() -* 09/08/98 stephen Moved definition of kEmptyString for Mac Port -* 11/09/99 weiv Added const char * getName() const; -* 04/12/00 srl removing unicodestring api's and cached hash code -* 08/10/01 grhoten Change the static Locales to accessor functions -****************************************************************************** -*/ - -#ifndef LOCID_H -#define LOCID_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/bytestream.h" -#include "unicode/localpointer.h" -#include "unicode/strenum.h" -#include "unicode/stringpiece.h" -#include "unicode/uobject.h" -#include "unicode/putil.h" -#include "unicode/uloc.h" - -/** - * \file - * \brief C++ API: Locale ID object. - */ - -U_NAMESPACE_BEGIN - -// Forward Declarations -void U_CALLCONV locale_available_init(); /**< @internal */ - -class StringEnumeration; -class UnicodeString; - -/** - * A Locale object represents a specific geographical, political, - * or cultural region. An operation that requires a Locale to perform - * its task is called locale-sensitive and uses the Locale - * to tailor information for the user. For example, displaying a number - * is a locale-sensitive operation--the number should be formatted - * according to the customs/conventions of the user's native country, - * region, or culture. - * - * The Locale class is not suitable for subclassing. - * - *

- * You can create a Locale object using the constructor in - * this class: - * \htmlonly

\endhtmlonly - *
- *       Locale( const   char*  language,
- *               const   char*  country,
- *               const   char*  variant);
- * 
- * \htmlonly
\endhtmlonly - * The first argument to the constructors is a valid ISO - * Language Code. These codes are the lower-case two-letter - * codes as defined by ISO-639. - * You can find a full list of these codes at: - *
- * http://www.loc.gov/standards/iso639-2/ - * - *

- * The second argument to the constructors is a valid ISO Country - * Code. These codes are the upper-case two-letter codes - * as defined by ISO-3166. - * You can find a full list of these codes at a number of sites, such as: - *
- * http://www.iso.org/iso/en/prods-services/iso3166ma/index.html - * - *

- * The third constructor requires a third argument--the Variant. - * The Variant codes are vendor and browser-specific. - * For example, use REVISED for a language's revised script orthography, and POSIX for POSIX. - * Where there are two variants, separate them with an underscore, and - * put the most important one first. For - * example, a Traditional Spanish collation might be referenced, with - * "ES", "ES", "Traditional_POSIX". - * - *

- * Because a Locale object is just an identifier for a region, - * no validity check is performed when you construct a Locale. - * If you want to see whether particular resources are available for the - * Locale you construct, you must query those resources. For - * example, ask the NumberFormat for the locales it supports - * using its getAvailableLocales method. - *
Note: When you ask for a resource for a particular - * locale, you get back the best available match, not necessarily - * precisely what you asked for. For more information, look at - * ResourceBundle. - * - *

- * The Locale class provides a number of convenient constants - * that you can use to create Locale objects for commonly used - * locales. For example, the following refers to a Locale object - * for the United States: - * \htmlonly

\endhtmlonly - *
- *       Locale::getUS()
- * 
- * \htmlonly
\endhtmlonly - * - *

- * Once you've created a Locale you can query it for information about - * itself. Use getCountry to get the ISO Country Code and - * getLanguage to get the ISO Language Code. You can - * use getDisplayCountry to get the - * name of the country suitable for displaying to the user. Similarly, - * you can use getDisplayLanguage to get the name of - * the language suitable for displaying to the user. Interestingly, - * the getDisplayXXX methods are themselves locale-sensitive - * and have two versions: one that uses the default locale and one - * that takes a locale as an argument and displays the name or country in - * a language appropriate to that locale. - * - *

- * ICU provides a number of classes that perform locale-sensitive - * operations. For example, the NumberFormat class formats - * numbers, currency, or percentages in a locale-sensitive manner. Classes - * such as NumberFormat have a number of convenience methods - * for creating a default object of that type. For example, the - * NumberFormat class provides these three convenience methods - * for creating a default NumberFormat object: - * \htmlonly

\endhtmlonly - *
- *     UErrorCode success = U_ZERO_ERROR;
- *     Locale myLocale;
- *     NumberFormat *nf;
- *
- *     nf = NumberFormat::createInstance( success );          delete nf;
- *     nf = NumberFormat::createCurrencyInstance( success );  delete nf;
- *     nf = NumberFormat::createPercentInstance( success );   delete nf;
- * 
- * \htmlonly
\endhtmlonly - * Each of these methods has two variants; one with an explicit locale - * and one without; the latter using the default locale. - * \htmlonly
\endhtmlonly - *
- *     nf = NumberFormat::createInstance( myLocale, success );          delete nf;
- *     nf = NumberFormat::createCurrencyInstance( myLocale, success );  delete nf;
- *     nf = NumberFormat::createPercentInstance( myLocale, success );   delete nf;
- * 
- * \htmlonly
\endhtmlonly - * A Locale is the mechanism for identifying the kind of object - * (NumberFormat) that you would like to get. The locale is - * just a mechanism for identifying objects, - * not a container for the objects themselves. - * - *

- * Each class that performs locale-sensitive operations allows you - * to get all the available objects of that type. You can sift - * through these objects by language, country, or variant, - * and use the display names to present a menu to the user. - * For example, you can create a menu of all the collation objects - * suitable for a given language. Such classes implement these - * three class methods: - * \htmlonly

\endhtmlonly - *
- *       static Locale* getAvailableLocales(int32_t& numLocales)
- *       static UnicodeString& getDisplayName(const Locale&  objectLocale,
- *                                            const Locale&  displayLocale,
- *                                            UnicodeString& displayName)
- *       static UnicodeString& getDisplayName(const Locale&  objectLocale,
- *                                            UnicodeString& displayName)
- * 
- * \htmlonly
\endhtmlonly - * - * @stable ICU 2.0 - * @see ResourceBundle - */ -class U_COMMON_API Locale : public UObject { -public: - /** Useful constant for the Root locale. @stable ICU 4.4 */ - static const Locale &U_EXPORT2 getRoot(void); - /** Useful constant for this language. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getEnglish(void); - /** Useful constant for this language. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getFrench(void); - /** Useful constant for this language. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getGerman(void); - /** Useful constant for this language. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getItalian(void); - /** Useful constant for this language. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getJapanese(void); - /** Useful constant for this language. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getKorean(void); - /** Useful constant for this language. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getChinese(void); - /** Useful constant for this language. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getSimplifiedChinese(void); - /** Useful constant for this language. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getTraditionalChinese(void); - - /** Useful constant for this country/region. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getFrance(void); - /** Useful constant for this country/region. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getGermany(void); - /** Useful constant for this country/region. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getItaly(void); - /** Useful constant for this country/region. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getJapan(void); - /** Useful constant for this country/region. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getKorea(void); - /** Useful constant for this country/region. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getChina(void); - /** Useful constant for this country/region. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getPRC(void); - /** Useful constant for this country/region. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getTaiwan(void); - /** Useful constant for this country/region. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getUK(void); - /** Useful constant for this country/region. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getUS(void); - /** Useful constant for this country/region. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getCanada(void); - /** Useful constant for this country/region. @stable ICU 2.0 */ - static const Locale &U_EXPORT2 getCanadaFrench(void); - - - /** - * Construct a default locale object, a Locale for the default locale ID. - * - * @see getDefault - * @see uloc_getDefault - * @stable ICU 2.0 - */ - Locale(); - - /** - * Construct a locale from language, country, variant. - * If an error occurs, then the constructed object will be "bogus" - * (isBogus() will return true). - * - * @param language Lowercase two-letter or three-letter ISO-639 code. - * This parameter can instead be an ICU style C locale (e.g. "en_US"), - * but the other parameters must not be used. - * This parameter can be NULL; if so, - * the locale is initialized to match the current default locale. - * (This is the same as using the default constructor.) - * Please note: The Java Locale class does NOT accept the form - * 'new Locale("en_US")' but only 'new Locale("en","US")' - * - * @param country Uppercase two-letter ISO-3166 code. (optional) - * @param variant Uppercase vendor and browser specific code. See class - * description. (optional) - * @param keywordsAndValues A string consisting of keyword/values pairs, such as - * "collation=phonebook;currency=euro" - * - * @see getDefault - * @see uloc_getDefault - * @stable ICU 2.0 - */ - Locale( const char * language, - const char * country = 0, - const char * variant = 0, - const char * keywordsAndValues = 0); - - /** - * Initializes a Locale object from another Locale object. - * - * @param other The Locale object being copied in. - * @stable ICU 2.0 - */ - Locale(const Locale& other); - - /** - * Move constructor; might leave source in bogus state. - * This locale will have the same contents that the source locale had. - * - * @param other The Locale object being moved in. - * @stable ICU 63 - */ - Locale(Locale&& other) U_NOEXCEPT; - - /** - * Destructor - * @stable ICU 2.0 - */ - virtual ~Locale() ; - - /** - * Replaces the entire contents of *this with the specified value. - * - * @param other The Locale object being copied in. - * @return *this - * @stable ICU 2.0 - */ - Locale& operator=(const Locale& other); - - /** - * Move assignment operator; might leave source in bogus state. - * This locale will have the same contents that the source locale had. - * The behavior is undefined if *this and the source are the same object. - * - * @param other The Locale object being moved in. - * @return *this - * @stable ICU 63 - */ - Locale& operator=(Locale&& other) U_NOEXCEPT; - - /** - * Checks if two locale keys are the same. - * - * @param other The locale key object to be compared with this. - * @return true if the two locale keys are the same, false otherwise. - * @stable ICU 2.0 - */ - bool operator==(const Locale& other) const; - - /** - * Checks if two locale keys are not the same. - * - * @param other The locale key object to be compared with this. - * @return true if the two locale keys are not the same, false - * otherwise. - * @stable ICU 2.0 - */ - inline bool operator!=(const Locale& other) const; - - /** - * Clone this object. - * Clones can be used concurrently in multiple threads. - * If an error occurs, then NULL is returned. - * The caller must delete the clone. - * - * @return a clone of this object - * - * @see getDynamicClassID - * @stable ICU 2.8 - */ - Locale *clone() const; - -#ifndef U_HIDE_SYSTEM_API - /** - * Common methods of getting the current default Locale. Used for the - * presentation: menus, dialogs, etc. Generally set once when your applet or - * application is initialized, then never reset. (If you do reset the - * default locale, you probably want to reload your GUI, so that the change - * is reflected in your interface.) - * - * More advanced programs will allow users to use different locales for - * different fields, e.g. in a spreadsheet. - * - * Note that the initial setting will match the host system. - * @return a reference to the Locale object for the default locale ID - * @system - * @stable ICU 2.0 - */ - static const Locale& U_EXPORT2 getDefault(void); - - /** - * Sets the default. Normally set once at the beginning of a process, - * then never reset. - * setDefault() only changes ICU's default locale ID, not - * the default locale ID of the runtime environment. - * - * @param newLocale Locale to set to. If NULL, set to the value obtained - * from the runtime environment. - * @param success The error code. - * @system - * @stable ICU 2.0 - */ - static void U_EXPORT2 setDefault(const Locale& newLocale, - UErrorCode& success); -#endif /* U_HIDE_SYSTEM_API */ - - /** - * Returns a Locale for the specified BCP47 language tag string. - * If the specified language tag contains any ill-formed subtags, - * the first such subtag and all following subtags are ignored. - *

- * This implements the 'Language-Tag' production of BCP 47, and so - * supports legacy language tags (marked as “Type: grandfathered” in BCP 47) - * (regular and irregular) as well as private use language tags. - * - * Private use tags are represented as 'x-whatever', - * and legacy tags are converted to their canonical replacements where they exist. - * - * Note that a few legacy tags have no modern replacement; - * these will be converted using the fallback described in - * the first paragraph, so some information might be lost. - * - * @param tag the input BCP47 language tag. - * @param status error information if creating the Locale failed. - * @return the Locale for the specified BCP47 language tag. - * @stable ICU 63 - */ - static Locale U_EXPORT2 forLanguageTag(StringPiece tag, UErrorCode& status); - - /** - * Returns a well-formed language tag for this Locale. - *

- * Note: Any locale fields which do not satisfy the BCP47 syntax - * requirement will be silently omitted from the result. - * - * If this function fails, partial output may have been written to the sink. - * - * @param sink the output sink receiving the BCP47 language - * tag for this Locale. - * @param status error information if creating the language tag failed. - * @stable ICU 63 - */ - void toLanguageTag(ByteSink& sink, UErrorCode& status) const; - - /** - * Returns a well-formed language tag for this Locale. - *

- * Note: Any locale fields which do not satisfy the BCP47 syntax - * requirement will be silently omitted from the result. - * - * @param status error information if creating the language tag failed. - * @return the BCP47 language tag for this Locale. - * @stable ICU 63 - */ - template - inline StringClass toLanguageTag(UErrorCode& status) const; - - /** - * Creates a locale which has had minimal canonicalization - * as per uloc_getName(). - * @param name The name to create from. If name is null, - * the default Locale is used. - * @return new locale object - * @stable ICU 2.0 - * @see uloc_getName - */ - static Locale U_EXPORT2 createFromName(const char *name); - - /** - * Creates a locale from the given string after canonicalizing - * the string according to CLDR by calling uloc_canonicalize(). - * @param name the locale ID to create from. Must not be NULL. - * @return a new locale object corresponding to the given name - * @stable ICU 3.0 - * @see uloc_canonicalize - */ - static Locale U_EXPORT2 createCanonical(const char* name); - - /** - * Returns the locale's ISO-639 language code. - * @return An alias to the code - * @stable ICU 2.0 - */ - inline const char * getLanguage( ) const; - - /** - * Returns the locale's ISO-15924 abbreviation script code. - * @return An alias to the code - * @see uscript_getShortName - * @see uscript_getCode - * @stable ICU 2.8 - */ - inline const char * getScript( ) const; - - /** - * Returns the locale's ISO-3166 country code. - * @return An alias to the code - * @stable ICU 2.0 - */ - inline const char * getCountry( ) const; - - /** - * Returns the locale's variant code. - * @return An alias to the code - * @stable ICU 2.0 - */ - inline const char * getVariant( ) const; - - /** - * Returns the programmatic name of the entire locale, with the language, - * country and variant separated by underbars. If a field is missing, up - * to two leading underbars will occur. Example: "en", "de_DE", "en_US_WIN", - * "de__POSIX", "fr__MAC", "__MAC", "_MT", "_FR_EURO" - * @return A pointer to "name". - * @stable ICU 2.0 - */ - inline const char * getName() const; - - /** - * Returns the programmatic name of the entire locale as getName() would return, - * but without keywords. - * @return A pointer to "name". - * @see getName - * @stable ICU 2.8 - */ - const char * getBaseName() const; - - /** - * Add the likely subtags for this Locale, per the algorithm described - * in the following CLDR technical report: - * - * http://www.unicode.org/reports/tr35/#Likely_Subtags - * - * If this Locale is already in the maximal form, or not valid, or there is - * no data available for maximization, the Locale will be unchanged. - * - * For example, "und-Zzzz" cannot be maximized, since there is no - * reasonable maximization. - * - * Examples: - * - * "en" maximizes to "en_Latn_US" - * - * "de" maximizes to "de_Latn_US" - * - * "sr" maximizes to "sr_Cyrl_RS" - * - * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.) - * - * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.) - * - * @param status error information if maximizing this Locale failed. - * If this Locale is not well-formed, the error code is - * U_ILLEGAL_ARGUMENT_ERROR. - * @stable ICU 63 - */ - void addLikelySubtags(UErrorCode& status); - - /** - * Minimize the subtags for this Locale, per the algorithm described - * in the following CLDR technical report: - * - * http://www.unicode.org/reports/tr35/#Likely_Subtags - * - * If this Locale is already in the minimal form, or not valid, or there is - * no data available for minimization, the Locale will be unchanged. - * - * Since the minimization algorithm relies on proper maximization, see the - * comments for addLikelySubtags for reasons why there might not be any - * data. - * - * Examples: - * - * "en_Latn_US" minimizes to "en" - * - * "de_Latn_US" minimizes to "de" - * - * "sr_Cyrl_RS" minimizes to "sr" - * - * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the - * script, and minimizing to "zh" would imply "zh_Hans_CN".) - * - * @param status error information if maximizing this Locale failed. - * If this Locale is not well-formed, the error code is - * U_ILLEGAL_ARGUMENT_ERROR. - * @stable ICU 63 - */ - void minimizeSubtags(UErrorCode& status); - - /** - * Canonicalize the locale ID of this object according to CLDR. - * @param status the status code - * @stable ICU 67 - * @see createCanonical - */ - void canonicalize(UErrorCode& status); - - /** - * Gets the list of keywords for the specified locale. - * - * @param status the status code - * @return pointer to StringEnumeration class, or NULL if there are no keywords. - * Client must dispose of it by calling delete. - * @see getKeywords - * @stable ICU 2.8 - */ - StringEnumeration * createKeywords(UErrorCode &status) const; - - /** - * Gets the list of Unicode keywords for the specified locale. - * - * @param status the status code - * @return pointer to StringEnumeration class, or NULL if there are no keywords. - * Client must dispose of it by calling delete. - * @see getUnicodeKeywords - * @stable ICU 63 - */ - StringEnumeration * createUnicodeKeywords(UErrorCode &status) const; - - /** - * Gets the set of keywords for this Locale. - * - * A wrapper to call createKeywords() and write the resulting - * keywords as standard strings (or compatible objects) into any kind of - * container that can be written to by an STL style output iterator. - * - * @param iterator an STL style output iterator to write the keywords to. - * @param status error information if creating set of keywords failed. - * @stable ICU 63 - */ - template - inline void getKeywords(OutputIterator iterator, UErrorCode& status) const; - - /** - * Gets the set of Unicode keywords for this Locale. - * - * A wrapper to call createUnicodeKeywords() and write the resulting - * keywords as standard strings (or compatible objects) into any kind of - * container that can be written to by an STL style output iterator. - * - * @param iterator an STL style output iterator to write the keywords to. - * @param status error information if creating set of keywords failed. - * @stable ICU 63 - */ - template - inline void getUnicodeKeywords(OutputIterator iterator, UErrorCode& status) const; - - /** - * Gets the value for a keyword. - * - * This uses legacy keyword=value pairs, like "collation=phonebook". - * - * ICU4C doesn't do automatic conversion between legacy and Unicode - * keywords and values in getters and setters (as opposed to ICU4J). - * - * @param keywordName name of the keyword for which we want the value. Case insensitive. - * @param buffer The buffer to receive the keyword value. - * @param bufferCapacity The capacity of receiving buffer - * @param status Returns any error information while performing this operation. - * @return the length of the keyword value - * - * @stable ICU 2.8 - */ - int32_t getKeywordValue(const char* keywordName, char *buffer, int32_t bufferCapacity, UErrorCode &status) const; - - /** - * Gets the value for a keyword. - * - * This uses legacy keyword=value pairs, like "collation=phonebook". - * - * ICU4C doesn't do automatic conversion between legacy and Unicode - * keywords and values in getters and setters (as opposed to ICU4J). - * - * @param keywordName name of the keyword for which we want the value. - * @param sink the sink to receive the keyword value. - * @param status error information if getting the value failed. - * @stable ICU 63 - */ - void getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const; - - /** - * Gets the value for a keyword. - * - * This uses legacy keyword=value pairs, like "collation=phonebook". - * - * ICU4C doesn't do automatic conversion between legacy and Unicode - * keywords and values in getters and setters (as opposed to ICU4J). - * - * @param keywordName name of the keyword for which we want the value. - * @param status error information if getting the value failed. - * @return the keyword value. - * @stable ICU 63 - */ - template - inline StringClass getKeywordValue(StringPiece keywordName, UErrorCode& status) const; - - /** - * Gets the Unicode value for a Unicode keyword. - * - * This uses Unicode key-value pairs, like "co-phonebk". - * - * ICU4C doesn't do automatic conversion between legacy and Unicode - * keywords and values in getters and setters (as opposed to ICU4J). - * - * @param keywordName name of the keyword for which we want the value. - * @param sink the sink to receive the keyword value. - * @param status error information if getting the value failed. - * @stable ICU 63 - */ - void getUnicodeKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const; - - /** - * Gets the Unicode value for a Unicode keyword. - * - * This uses Unicode key-value pairs, like "co-phonebk". - * - * ICU4C doesn't do automatic conversion between legacy and Unicode - * keywords and values in getters and setters (as opposed to ICU4J). - * - * @param keywordName name of the keyword for which we want the value. - * @param status error information if getting the value failed. - * @return the keyword value. - * @stable ICU 63 - */ - template - inline StringClass getUnicodeKeywordValue(StringPiece keywordName, UErrorCode& status) const; - - /** - * Sets or removes the value for a keyword. - * - * For removing all keywords, use getBaseName(), - * and construct a new Locale if it differs from getName(). - * - * This uses legacy keyword=value pairs, like "collation=phonebook". - * - * ICU4C doesn't do automatic conversion between legacy and Unicode - * keywords and values in getters and setters (as opposed to ICU4J). - * - * @param keywordName name of the keyword to be set. Case insensitive. - * @param keywordValue value of the keyword to be set. If 0-length or - * NULL, will result in the keyword being removed. No error is given if - * that keyword does not exist. - * @param status Returns any error information while performing this operation. - * - * @stable ICU 49 - */ - void setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status); - - /** - * Sets or removes the value for a keyword. - * - * For removing all keywords, use getBaseName(), - * and construct a new Locale if it differs from getName(). - * - * This uses legacy keyword=value pairs, like "collation=phonebook". - * - * ICU4C doesn't do automatic conversion between legacy and Unicode - * keywords and values in getters and setters (as opposed to ICU4J). - * - * @param keywordName name of the keyword to be set. - * @param keywordValue value of the keyword to be set. If 0-length or - * NULL, will result in the keyword being removed. No error is given if - * that keyword does not exist. - * @param status Returns any error information while performing this operation. - * @stable ICU 63 - */ - void setKeywordValue(StringPiece keywordName, StringPiece keywordValue, UErrorCode& status); - - /** - * Sets or removes the Unicode value for a Unicode keyword. - * - * For removing all keywords, use getBaseName(), - * and construct a new Locale if it differs from getName(). - * - * This uses Unicode key-value pairs, like "co-phonebk". - * - * ICU4C doesn't do automatic conversion between legacy and Unicode - * keywords and values in getters and setters (as opposed to ICU4J). - * - * @param keywordName name of the keyword to be set. - * @param keywordValue value of the keyword to be set. If 0-length or - * NULL, will result in the keyword being removed. No error is given if - * that keyword does not exist. - * @param status Returns any error information while performing this operation. - * @stable ICU 63 - */ - void setUnicodeKeywordValue(StringPiece keywordName, StringPiece keywordValue, UErrorCode& status); - - /** - * returns the locale's three-letter language code, as specified - * in ISO draft standard ISO-639-2. - * @return An alias to the code, or an empty string - * @stable ICU 2.0 - */ - const char * getISO3Language() const; - - /** - * Fills in "name" with the locale's three-letter ISO-3166 country code. - * @return An alias to the code, or an empty string - * @stable ICU 2.0 - */ - const char * getISO3Country() const; - - /** - * Returns the Windows LCID value corresponding to this locale. - * This value is stored in the resource data for the locale as a one-to-four-digit - * hexadecimal number. If the resource is missing, in the wrong format, or - * there is no Windows LCID value that corresponds to this locale, returns 0. - * @stable ICU 2.0 - */ - uint32_t getLCID(void) const; - - /** - * Returns whether this locale's script is written right-to-left. - * If there is no script subtag, then the likely script is used, see uloc_addLikelySubtags(). - * If no likely script is known, then false is returned. - * - * A script is right-to-left according to the CLDR script metadata - * which corresponds to whether the script's letters have Bidi_Class=R or AL. - * - * Returns true for "ar" and "en-Hebr", false for "zh" and "fa-Cyrl". - * - * @return true if the locale's script is written right-to-left - * @stable ICU 54 - */ - UBool isRightToLeft() const; - - /** - * Fills in "dispLang" with the name of this locale's language in a format suitable for - * user display in the default locale. For example, if the locale's language code is - * "fr" and the default locale's language code is "en", this function would set - * dispLang to "French". - * @param dispLang Receives the language's display name. - * @return A reference to "dispLang". - * @stable ICU 2.0 - */ - UnicodeString& getDisplayLanguage(UnicodeString& dispLang) const; - - /** - * Fills in "dispLang" with the name of this locale's language in a format suitable for - * user display in the locale specified by "displayLocale". For example, if the locale's - * language code is "en" and displayLocale's language code is "fr", this function would set - * dispLang to "Anglais". - * @param displayLocale Specifies the locale to be used to display the name. In other words, - * if the locale's language code is "en", passing Locale::getFrench() for - * displayLocale would result in "Anglais", while passing Locale::getGerman() - * for displayLocale would result in "Englisch". - * @param dispLang Receives the language's display name. - * @return A reference to "dispLang". - * @stable ICU 2.0 - */ - UnicodeString& getDisplayLanguage( const Locale& displayLocale, - UnicodeString& dispLang) const; - - /** - * Fills in "dispScript" with the name of this locale's script in a format suitable - * for user display in the default locale. For example, if the locale's script code - * is "LATN" and the default locale's language code is "en", this function would set - * dispScript to "Latin". - * @param dispScript Receives the scripts's display name. - * @return A reference to "dispScript". - * @stable ICU 2.8 - */ - UnicodeString& getDisplayScript( UnicodeString& dispScript) const; - - /** - * Fills in "dispScript" with the name of this locale's country in a format suitable - * for user display in the locale specified by "displayLocale". For example, if the locale's - * script code is "LATN" and displayLocale's language code is "en", this function would set - * dispScript to "Latin". - * @param displayLocale Specifies the locale to be used to display the name. In other - * words, if the locale's script code is "LATN", passing - * Locale::getFrench() for displayLocale would result in "", while - * passing Locale::getGerman() for displayLocale would result in - * "". - * @param dispScript Receives the scripts's display name. - * @return A reference to "dispScript". - * @stable ICU 2.8 - */ - UnicodeString& getDisplayScript( const Locale& displayLocale, - UnicodeString& dispScript) const; - - /** - * Fills in "dispCountry" with the name of this locale's country in a format suitable - * for user display in the default locale. For example, if the locale's country code - * is "FR" and the default locale's language code is "en", this function would set - * dispCountry to "France". - * @param dispCountry Receives the country's display name. - * @return A reference to "dispCountry". - * @stable ICU 2.0 - */ - UnicodeString& getDisplayCountry( UnicodeString& dispCountry) const; - - /** - * Fills in "dispCountry" with the name of this locale's country in a format suitable - * for user display in the locale specified by "displayLocale". For example, if the locale's - * country code is "US" and displayLocale's language code is "fr", this function would set - * dispCountry to "États-Unis". - * @param displayLocale Specifies the locale to be used to display the name. In other - * words, if the locale's country code is "US", passing - * Locale::getFrench() for displayLocale would result in "États-Unis", while - * passing Locale::getGerman() for displayLocale would result in - * "Vereinigte Staaten". - * @param dispCountry Receives the country's display name. - * @return A reference to "dispCountry". - * @stable ICU 2.0 - */ - UnicodeString& getDisplayCountry( const Locale& displayLocale, - UnicodeString& dispCountry) const; - - /** - * Fills in "dispVar" with the name of this locale's variant code in a format suitable - * for user display in the default locale. - * @param dispVar Receives the variant's name. - * @return A reference to "dispVar". - * @stable ICU 2.0 - */ - UnicodeString& getDisplayVariant( UnicodeString& dispVar) const; - - /** - * Fills in "dispVar" with the name of this locale's variant code in a format - * suitable for user display in the locale specified by "displayLocale". - * @param displayLocale Specifies the locale to be used to display the name. - * @param dispVar Receives the variant's display name. - * @return A reference to "dispVar". - * @stable ICU 2.0 - */ - UnicodeString& getDisplayVariant( const Locale& displayLocale, - UnicodeString& dispVar) const; - - /** - * Fills in "name" with the name of this locale in a format suitable for user display - * in the default locale. This function uses getDisplayLanguage(), getDisplayCountry(), - * and getDisplayVariant() to do its work, and outputs the display name in the format - * "language (country[,variant])". For example, if the default locale is en_US, then - * fr_FR's display name would be "French (France)", and es_MX_Traditional's display name - * would be "Spanish (Mexico,Traditional)". - * @param name Receives the locale's display name. - * @return A reference to "name". - * @stable ICU 2.0 - */ - UnicodeString& getDisplayName( UnicodeString& name) const; - - /** - * Fills in "name" with the name of this locale in a format suitable for user display - * in the locale specified by "displayLocale". This function uses getDisplayLanguage(), - * getDisplayCountry(), and getDisplayVariant() to do its work, and outputs the display - * name in the format "language (country[,variant])". For example, if displayLocale is - * fr_FR, then en_US's display name would be "Anglais (États-Unis)", and no_NO_NY's - * display name would be "norvégien (Norvège,NY)". - * @param displayLocale Specifies the locale to be used to display the name. - * @param name Receives the locale's display name. - * @return A reference to "name". - * @stable ICU 2.0 - */ - UnicodeString& getDisplayName( const Locale& displayLocale, - UnicodeString& name) const; - - /** - * Generates a hash code for the locale. - * @stable ICU 2.0 - */ - int32_t hashCode(void) const; - - /** - * Sets the locale to bogus - * A bogus locale represents a non-existing locale associated - * with services that can be instantiated from non-locale data - * in addition to locale (for example, collation can be - * instantiated from a locale and from a rule set). - * @stable ICU 2.1 - */ - void setToBogus(); - - /** - * Gets the bogus state. Locale object can be bogus if it doesn't exist - * @return false if it is a real locale, true if it is a bogus locale - * @stable ICU 2.1 - */ - inline UBool isBogus(void) const; - - /** - * Returns a list of all installed locales. - * @param count Receives the number of locales in the list. - * @return A pointer to an array of Locale objects. This array is the list - * of all locales with installed resource files. The called does NOT - * get ownership of this list, and must NOT delete it. - * @stable ICU 2.0 - */ - static const Locale* U_EXPORT2 getAvailableLocales(int32_t& count); - - /** - * Gets a list of all available 2-letter country codes defined in ISO 3166. This is a - * pointer to an array of pointers to arrays of char. All of these pointers are - * owned by ICU-- do not delete them, and do not write through them. The array is - * terminated with a null pointer. - * @return a list of all available country codes - * @stable ICU 2.0 - */ - static const char* const* U_EXPORT2 getISOCountries(); - - /** - * Gets a list of all available language codes defined in ISO 639. This is a pointer - * to an array of pointers to arrays of char. All of these pointers are owned - * by ICU-- do not delete them, and do not write through them. The array is - * terminated with a null pointer. - * @return a list of all available language codes - * @stable ICU 2.0 - */ - static const char* const* U_EXPORT2 getISOLanguages(); - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - * - * @stable ICU 2.2 - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - * - * @stable ICU 2.2 - */ - virtual UClassID getDynamicClassID() const override; - - /** - * A Locale iterator interface similar to a Java Iterator. - * @stable ICU 65 - */ - class U_COMMON_API Iterator /* not : public UObject because this is an interface/mixin class */ { - public: - /** @stable ICU 65 */ - virtual ~Iterator(); - - /** - * @return true if next() can be called again. - * @stable ICU 65 - */ - virtual UBool hasNext() const = 0; - - /** - * @return the next locale. - * @stable ICU 65 - */ - virtual const Locale &next() = 0; - }; - - /** - * A generic Locale iterator implementation over Locale input iterators. - * @stable ICU 65 - */ - template - class RangeIterator : public Iterator, public UMemory { - public: - /** - * Constructs an iterator from a begin/end range. - * Each of the iterator parameter values must be an - * input iterator whose value is convertible to const Locale &. - * - * @param begin Start of range. - * @param end Exclusive end of range. - * @stable ICU 65 - */ - RangeIterator(Iter begin, Iter end) : it_(begin), end_(end) {} - - /** - * @return true if next() can be called again. - * @stable ICU 65 - */ - UBool hasNext() const override { return it_ != end_; } - - /** - * @return the next locale. - * @stable ICU 65 - */ - const Locale &next() override { return *it_++; } - - private: - Iter it_; - const Iter end_; - }; - - /** - * A generic Locale iterator implementation over Locale input iterators. - * Calls the converter to convert each *begin to a const Locale &. - * @stable ICU 65 - */ - template - class ConvertingIterator : public Iterator, public UMemory { - public: - /** - * Constructs an iterator from a begin/end range. - * Each of the iterator parameter values must be an - * input iterator whose value the converter converts to const Locale &. - * - * @param begin Start of range. - * @param end Exclusive end of range. - * @param converter Converter from *begin to const Locale & or compatible. - * @stable ICU 65 - */ - ConvertingIterator(Iter begin, Iter end, Conv converter) : - it_(begin), end_(end), converter_(converter) {} - - /** - * @return true if next() can be called again. - * @stable ICU 65 - */ - UBool hasNext() const override { return it_ != end_; } - - /** - * @return the next locale. - * @stable ICU 65 - */ - const Locale &next() override { return converter_(*it_++); } - - private: - Iter it_; - const Iter end_; - Conv converter_; - }; - -protected: /* only protected for testing purposes. DO NOT USE. */ -#ifndef U_HIDE_INTERNAL_API - /** - * Set this from a single POSIX style locale string. - * @internal - */ - void setFromPOSIXID(const char *posixID); -#endif /* U_HIDE_INTERNAL_API */ - -private: - /** - * Initialize the locale object with a new name. - * Was deprecated - used in implementation - moved internal - * - * @param cLocaleID The new locale name. - * @param canonicalize whether to call uloc_canonicalize on cLocaleID - */ - Locale& init(const char* cLocaleID, UBool canonicalize); - - /* - * Internal constructor to allow construction of a locale object with - * NO side effects. (Default constructor tries to get - * the default locale.) - */ - enum ELocaleType { - eBOGUS - }; - Locale(ELocaleType); - - /** - * Initialize the locale cache for commonly used locales - */ - static Locale *getLocaleCache(void); - - char language[ULOC_LANG_CAPACITY]; - char script[ULOC_SCRIPT_CAPACITY]; - char country[ULOC_COUNTRY_CAPACITY]; - int32_t variantBegin; - char* fullName; - char fullNameBuffer[ULOC_FULLNAME_CAPACITY]; - // name without keywords - char* baseName; - void initBaseName(UErrorCode& status); - - UBool fIsBogus; - - static const Locale &getLocale(int locid); - - /** - * A friend to allow the default locale to be set by either the C or C++ API. - * @internal (private) - */ - friend Locale *locale_set_default_internal(const char *, UErrorCode& status); - - /** - * @internal (private) - */ - friend void U_CALLCONV locale_available_init(); -}; - -inline bool -Locale::operator!=(const Locale& other) const -{ - return !operator==(other); -} - -template inline StringClass -Locale::toLanguageTag(UErrorCode& status) const -{ - StringClass result; - StringByteSink sink(&result); - toLanguageTag(sink, status); - return result; -} - -inline const char * -Locale::getCountry() const -{ - return country; -} - -inline const char * -Locale::getLanguage() const -{ - return language; -} - -inline const char * -Locale::getScript() const -{ - return script; -} - -inline const char * -Locale::getVariant() const -{ - return &baseName[variantBegin]; -} - -inline const char * -Locale::getName() const -{ - return fullName; -} - -template inline void -Locale::getKeywords(OutputIterator iterator, UErrorCode& status) const -{ - LocalPointer keys(createKeywords(status)); - if (U_FAILURE(status) || keys.isNull()) { - return; - } - for (;;) { - int32_t resultLength; - const char* buffer = keys->next(&resultLength, status); - if (U_FAILURE(status) || buffer == nullptr) { - return; - } - *iterator++ = StringClass(buffer, resultLength); - } -} - -template inline void -Locale::getUnicodeKeywords(OutputIterator iterator, UErrorCode& status) const -{ - LocalPointer keys(createUnicodeKeywords(status)); - if (U_FAILURE(status) || keys.isNull()) { - return; - } - for (;;) { - int32_t resultLength; - const char* buffer = keys->next(&resultLength, status); - if (U_FAILURE(status) || buffer == nullptr) { - return; - } - *iterator++ = StringClass(buffer, resultLength); - } -} - -template inline StringClass -Locale::getKeywordValue(StringPiece keywordName, UErrorCode& status) const -{ - StringClass result; - StringByteSink sink(&result); - getKeywordValue(keywordName, sink, status); - return result; -} - -template inline StringClass -Locale::getUnicodeKeywordValue(StringPiece keywordName, UErrorCode& status) const -{ - StringClass result; - StringByteSink sink(&result); - getUnicodeKeywordValue(keywordName, sink, status); - return result; -} - -inline UBool -Locale::isBogus(void) const { - return fIsBogus; -} - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1996-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* File locid.h +* +* Created by: Helena Shih +* +* Modification History: +* +* Date Name Description +* 02/11/97 aliu Changed gLocPath to fgLocPath and added methods to +* get and set it. +* 04/02/97 aliu Made operator!= inline; fixed return value of getName(). +* 04/15/97 aliu Cleanup for AIX/Win32. +* 04/24/97 aliu Numerous changes per code review. +* 08/18/98 stephen Added tokenizeString(),changed getDisplayName() +* 09/08/98 stephen Moved definition of kEmptyString for Mac Port +* 11/09/99 weiv Added const char * getName() const; +* 04/12/00 srl removing unicodestring api's and cached hash code +* 08/10/01 grhoten Change the static Locales to accessor functions +****************************************************************************** +*/ + +#ifndef LOCID_H +#define LOCID_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/bytestream.h" +#include "unicode/localpointer.h" +#include "unicode/strenum.h" +#include "unicode/stringpiece.h" +#include "unicode/uobject.h" +#include "unicode/putil.h" +#include "unicode/uloc.h" + +/** + * \file + * \brief C++ API: Locale ID object. + */ + +U_NAMESPACE_BEGIN + +// Forward Declarations +void U_CALLCONV locale_available_init(); /**< @internal */ + +class StringEnumeration; +class UnicodeString; + +/** + * A Locale object represents a specific geographical, political, + * or cultural region. An operation that requires a Locale to perform + * its task is called locale-sensitive and uses the Locale + * to tailor information for the user. For example, displaying a number + * is a locale-sensitive operation--the number should be formatted + * according to the customs/conventions of the user's native country, + * region, or culture. + * + * The Locale class is not suitable for subclassing. + * + *

+ * You can create a Locale object using the constructor in + * this class: + * \htmlonly

\endhtmlonly + *
+ *       Locale( const   char*  language,
+ *               const   char*  country,
+ *               const   char*  variant);
+ * 
+ * \htmlonly
\endhtmlonly + * The first argument to the constructors is a valid ISO + * Language Code. These codes are the lower-case two-letter + * codes as defined by ISO-639. + * You can find a full list of these codes at: + *
+ * http://www.loc.gov/standards/iso639-2/ + * + *

+ * The second argument to the constructors is a valid ISO Country + * Code. These codes are the upper-case two-letter codes + * as defined by ISO-3166. + * You can find a full list of these codes at a number of sites, such as: + *
+ * http://www.iso.org/iso/en/prods-services/iso3166ma/index.html + * + *

+ * The third constructor requires a third argument--the Variant. + * The Variant codes are vendor and browser-specific. + * For example, use REVISED for a language's revised script orthography, and POSIX for POSIX. + * Where there are two variants, separate them with an underscore, and + * put the most important one first. For + * example, a Traditional Spanish collation might be referenced, with + * "ES", "ES", "Traditional_POSIX". + * + *

+ * Because a Locale object is just an identifier for a region, + * no validity check is performed when you construct a Locale. + * If you want to see whether particular resources are available for the + * Locale you construct, you must query those resources. For + * example, ask the NumberFormat for the locales it supports + * using its getAvailableLocales method. + *
Note: When you ask for a resource for a particular + * locale, you get back the best available match, not necessarily + * precisely what you asked for. For more information, look at + * ResourceBundle. + * + *

+ * The Locale class provides a number of convenient constants + * that you can use to create Locale objects for commonly used + * locales. For example, the following refers to a Locale object + * for the United States: + * \htmlonly

\endhtmlonly + *
+ *       Locale::getUS()
+ * 
+ * \htmlonly
\endhtmlonly + * + *

+ * Once you've created a Locale you can query it for information about + * itself. Use getCountry to get the ISO Country Code and + * getLanguage to get the ISO Language Code. You can + * use getDisplayCountry to get the + * name of the country suitable for displaying to the user. Similarly, + * you can use getDisplayLanguage to get the name of + * the language suitable for displaying to the user. Interestingly, + * the getDisplayXXX methods are themselves locale-sensitive + * and have two versions: one that uses the default locale and one + * that takes a locale as an argument and displays the name or country in + * a language appropriate to that locale. + * + *

+ * ICU provides a number of classes that perform locale-sensitive + * operations. For example, the NumberFormat class formats + * numbers, currency, or percentages in a locale-sensitive manner. Classes + * such as NumberFormat have a number of convenience methods + * for creating a default object of that type. For example, the + * NumberFormat class provides these three convenience methods + * for creating a default NumberFormat object: + * \htmlonly

\endhtmlonly + *
+ *     UErrorCode success = U_ZERO_ERROR;
+ *     Locale myLocale;
+ *     NumberFormat *nf;
+ *
+ *     nf = NumberFormat::createInstance( success );          delete nf;
+ *     nf = NumberFormat::createCurrencyInstance( success );  delete nf;
+ *     nf = NumberFormat::createPercentInstance( success );   delete nf;
+ * 
+ * \htmlonly
\endhtmlonly + * Each of these methods has two variants; one with an explicit locale + * and one without; the latter using the default locale. + * \htmlonly
\endhtmlonly + *
+ *     nf = NumberFormat::createInstance( myLocale, success );          delete nf;
+ *     nf = NumberFormat::createCurrencyInstance( myLocale, success );  delete nf;
+ *     nf = NumberFormat::createPercentInstance( myLocale, success );   delete nf;
+ * 
+ * \htmlonly
\endhtmlonly + * A Locale is the mechanism for identifying the kind of object + * (NumberFormat) that you would like to get. The locale is + * just a mechanism for identifying objects, + * not a container for the objects themselves. + * + *

+ * Each class that performs locale-sensitive operations allows you + * to get all the available objects of that type. You can sift + * through these objects by language, country, or variant, + * and use the display names to present a menu to the user. + * For example, you can create a menu of all the collation objects + * suitable for a given language. Such classes implement these + * three class methods: + * \htmlonly

\endhtmlonly + *
+ *       static Locale* getAvailableLocales(int32_t& numLocales)
+ *       static UnicodeString& getDisplayName(const Locale&  objectLocale,
+ *                                            const Locale&  displayLocale,
+ *                                            UnicodeString& displayName)
+ *       static UnicodeString& getDisplayName(const Locale&  objectLocale,
+ *                                            UnicodeString& displayName)
+ * 
+ * \htmlonly
\endhtmlonly + * + * @stable ICU 2.0 + * @see ResourceBundle + */ +class U_COMMON_API Locale : public UObject { +public: + /** Useful constant for the Root locale. @stable ICU 4.4 */ + static const Locale &U_EXPORT2 getRoot(void); + /** Useful constant for this language. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getEnglish(void); + /** Useful constant for this language. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getFrench(void); + /** Useful constant for this language. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getGerman(void); + /** Useful constant for this language. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getItalian(void); + /** Useful constant for this language. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getJapanese(void); + /** Useful constant for this language. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getKorean(void); + /** Useful constant for this language. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getChinese(void); + /** Useful constant for this language. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getSimplifiedChinese(void); + /** Useful constant for this language. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getTraditionalChinese(void); + + /** Useful constant for this country/region. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getFrance(void); + /** Useful constant for this country/region. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getGermany(void); + /** Useful constant for this country/region. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getItaly(void); + /** Useful constant for this country/region. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getJapan(void); + /** Useful constant for this country/region. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getKorea(void); + /** Useful constant for this country/region. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getChina(void); + /** Useful constant for this country/region. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getPRC(void); + /** Useful constant for this country/region. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getTaiwan(void); + /** Useful constant for this country/region. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getUK(void); + /** Useful constant for this country/region. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getUS(void); + /** Useful constant for this country/region. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getCanada(void); + /** Useful constant for this country/region. @stable ICU 2.0 */ + static const Locale &U_EXPORT2 getCanadaFrench(void); + + + /** + * Construct a default locale object, a Locale for the default locale ID. + * + * @see getDefault + * @see uloc_getDefault + * @stable ICU 2.0 + */ + Locale(); + + /** + * Construct a locale from language, country, variant. + * If an error occurs, then the constructed object will be "bogus" + * (isBogus() will return true). + * + * @param language Lowercase two-letter or three-letter ISO-639 code. + * This parameter can instead be an ICU style C locale (e.g. "en_US"), + * but the other parameters must not be used. + * This parameter can be nullptr; if so, + * the locale is initialized to match the current default locale. + * (This is the same as using the default constructor.) + * Please note: The Java Locale class does NOT accept the form + * 'new Locale("en_US")' but only 'new Locale("en","US")' + * + * @param country Uppercase two-letter ISO-3166 code. (optional) + * @param variant Uppercase vendor and browser specific code. See class + * description. (optional) + * @param keywordsAndValues A string consisting of keyword/values pairs, such as + * "collation=phonebook;currency=euro" + * + * @see getDefault + * @see uloc_getDefault + * @stable ICU 2.0 + */ + Locale( const char * language, + const char * country = 0, + const char * variant = 0, + const char * keywordsAndValues = 0); + + /** + * Initializes a Locale object from another Locale object. + * + * @param other The Locale object being copied in. + * @stable ICU 2.0 + */ + Locale(const Locale& other); + + /** + * Move constructor; might leave source in bogus state. + * This locale will have the same contents that the source locale had. + * + * @param other The Locale object being moved in. + * @stable ICU 63 + */ + Locale(Locale&& other) noexcept; + + /** + * Destructor + * @stable ICU 2.0 + */ + virtual ~Locale() ; + + /** + * Replaces the entire contents of *this with the specified value. + * + * @param other The Locale object being copied in. + * @return *this + * @stable ICU 2.0 + */ + Locale& operator=(const Locale& other); + + /** + * Move assignment operator; might leave source in bogus state. + * This locale will have the same contents that the source locale had. + * The behavior is undefined if *this and the source are the same object. + * + * @param other The Locale object being moved in. + * @return *this + * @stable ICU 63 + */ + Locale& operator=(Locale&& other) noexcept; + + /** + * Checks if two locale keys are the same. + * + * @param other The locale key object to be compared with this. + * @return true if the two locale keys are the same, false otherwise. + * @stable ICU 2.0 + */ + bool operator==(const Locale& other) const; + + /** + * Checks if two locale keys are not the same. + * + * @param other The locale key object to be compared with this. + * @return true if the two locale keys are not the same, false + * otherwise. + * @stable ICU 2.0 + */ + inline bool operator!=(const Locale& other) const; + + /** + * Clone this object. + * Clones can be used concurrently in multiple threads. + * If an error occurs, then nullptr is returned. + * The caller must delete the clone. + * + * @return a clone of this object + * + * @see getDynamicClassID + * @stable ICU 2.8 + */ + Locale *clone() const; + +#ifndef U_HIDE_SYSTEM_API + /** + * Common methods of getting the current default Locale. Used for the + * presentation: menus, dialogs, etc. Generally set once when your applet or + * application is initialized, then never reset. (If you do reset the + * default locale, you probably want to reload your GUI, so that the change + * is reflected in your interface.) + * + * More advanced programs will allow users to use different locales for + * different fields, e.g. in a spreadsheet. + * + * Note that the initial setting will match the host system. + * @return a reference to the Locale object for the default locale ID + * @system + * @stable ICU 2.0 + */ + static const Locale& U_EXPORT2 getDefault(void); + + /** + * Sets the default. Normally set once at the beginning of a process, + * then never reset. + * setDefault() only changes ICU's default locale ID, not + * the default locale ID of the runtime environment. + * + * @param newLocale Locale to set to. If nullptr, set to the value obtained + * from the runtime environment. + * @param success The error code. + * @system + * @stable ICU 2.0 + */ + static void U_EXPORT2 setDefault(const Locale& newLocale, + UErrorCode& success); +#endif /* U_HIDE_SYSTEM_API */ + + /** + * Returns a Locale for the specified BCP47 language tag string. + * If the specified language tag contains any ill-formed subtags, + * the first such subtag and all following subtags are ignored. + *

+ * This implements the 'Language-Tag' production of BCP 47, and so + * supports legacy language tags (marked as “Type: grandfathered” in BCP 47) + * (regular and irregular) as well as private use language tags. + * + * Private use tags are represented as 'x-whatever', + * and legacy tags are converted to their canonical replacements where they exist. + * + * Note that a few legacy tags have no modern replacement; + * these will be converted using the fallback described in + * the first paragraph, so some information might be lost. + * + * @param tag the input BCP47 language tag. + * @param status error information if creating the Locale failed. + * @return the Locale for the specified BCP47 language tag. + * @stable ICU 63 + */ + static Locale U_EXPORT2 forLanguageTag(StringPiece tag, UErrorCode& status); + + /** + * Returns a well-formed language tag for this Locale. + *

+ * Note: Any locale fields which do not satisfy the BCP47 syntax + * requirement will be silently omitted from the result. + * + * If this function fails, partial output may have been written to the sink. + * + * @param sink the output sink receiving the BCP47 language + * tag for this Locale. + * @param status error information if creating the language tag failed. + * @stable ICU 63 + */ + void toLanguageTag(ByteSink& sink, UErrorCode& status) const; + + /** + * Returns a well-formed language tag for this Locale. + *

+ * Note: Any locale fields which do not satisfy the BCP47 syntax + * requirement will be silently omitted from the result. + * + * @param status error information if creating the language tag failed. + * @return the BCP47 language tag for this Locale. + * @stable ICU 63 + */ + template + inline StringClass toLanguageTag(UErrorCode& status) const; + + /** + * Creates a locale which has had minimal canonicalization + * as per uloc_getName(). + * @param name The name to create from. If name is null, + * the default Locale is used. + * @return new locale object + * @stable ICU 2.0 + * @see uloc_getName + */ + static Locale U_EXPORT2 createFromName(const char *name); + + /** + * Creates a locale from the given string after canonicalizing + * the string according to CLDR by calling uloc_canonicalize(). + * @param name the locale ID to create from. Must not be nullptr. + * @return a new locale object corresponding to the given name + * @stable ICU 3.0 + * @see uloc_canonicalize + */ + static Locale U_EXPORT2 createCanonical(const char* name); + + /** + * Returns the locale's ISO-639 language code. + * @return An alias to the code + * @stable ICU 2.0 + */ + inline const char * getLanguage( ) const; + + /** + * Returns the locale's ISO-15924 abbreviation script code. + * @return An alias to the code + * @see uscript_getShortName + * @see uscript_getCode + * @stable ICU 2.8 + */ + inline const char * getScript( ) const; + + /** + * Returns the locale's ISO-3166 country code. + * @return An alias to the code + * @stable ICU 2.0 + */ + inline const char * getCountry( ) const; + + /** + * Returns the locale's variant code. + * @return An alias to the code + * @stable ICU 2.0 + */ + inline const char * getVariant( ) const; + + /** + * Returns the programmatic name of the entire locale, with the language, + * country and variant separated by underbars. If a field is missing, up + * to two leading underbars will occur. Example: "en", "de_DE", "en_US_WIN", + * "de__POSIX", "fr__MAC", "__MAC", "_MT", "_FR_EURO" + * @return A pointer to "name". + * @stable ICU 2.0 + */ + inline const char * getName() const; + + /** + * Returns the programmatic name of the entire locale as getName() would return, + * but without keywords. + * @return A pointer to "name". + * @see getName + * @stable ICU 2.8 + */ + const char * getBaseName() const; + + /** + * Add the likely subtags for this Locale, per the algorithm described + * in the following CLDR technical report: + * + * http://www.unicode.org/reports/tr35/#Likely_Subtags + * + * If this Locale is already in the maximal form, or not valid, or there is + * no data available for maximization, the Locale will be unchanged. + * + * For example, "und-Zzzz" cannot be maximized, since there is no + * reasonable maximization. + * + * Examples: + * + * "en" maximizes to "en_Latn_US" + * + * "de" maximizes to "de_Latn_US" + * + * "sr" maximizes to "sr_Cyrl_RS" + * + * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.) + * + * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.) + * + * @param status error information if maximizing this Locale failed. + * If this Locale is not well-formed, the error code is + * U_ILLEGAL_ARGUMENT_ERROR. + * @stable ICU 63 + */ + void addLikelySubtags(UErrorCode& status); + + /** + * Minimize the subtags for this Locale, per the algorithm described + * in the following CLDR technical report: + * + * http://www.unicode.org/reports/tr35/#Likely_Subtags + * + * If this Locale is already in the minimal form, or not valid, or there is + * no data available for minimization, the Locale will be unchanged. + * + * Since the minimization algorithm relies on proper maximization, see the + * comments for addLikelySubtags for reasons why there might not be any + * data. + * + * Examples: + * + * "en_Latn_US" minimizes to "en" + * + * "de_Latn_US" minimizes to "de" + * + * "sr_Cyrl_RS" minimizes to "sr" + * + * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the + * script, and minimizing to "zh" would imply "zh_Hans_CN".) + * + * @param status error information if maximizing this Locale failed. + * If this Locale is not well-formed, the error code is + * U_ILLEGAL_ARGUMENT_ERROR. + * @stable ICU 63 + */ + void minimizeSubtags(UErrorCode& status); + + /** + * Canonicalize the locale ID of this object according to CLDR. + * @param status the status code + * @stable ICU 67 + * @see createCanonical + */ + void canonicalize(UErrorCode& status); + + /** + * Gets the list of keywords for the specified locale. + * + * @param status the status code + * @return pointer to StringEnumeration class, or nullptr if there are no keywords. + * Client must dispose of it by calling delete. + * @see getKeywords + * @stable ICU 2.8 + */ + StringEnumeration * createKeywords(UErrorCode &status) const; + + /** + * Gets the list of Unicode keywords for the specified locale. + * + * @param status the status code + * @return pointer to StringEnumeration class, or nullptr if there are no keywords. + * Client must dispose of it by calling delete. + * @see getUnicodeKeywords + * @stable ICU 63 + */ + StringEnumeration * createUnicodeKeywords(UErrorCode &status) const; + + /** + * Gets the set of keywords for this Locale. + * + * A wrapper to call createKeywords() and write the resulting + * keywords as standard strings (or compatible objects) into any kind of + * container that can be written to by an STL style output iterator. + * + * @param iterator an STL style output iterator to write the keywords to. + * @param status error information if creating set of keywords failed. + * @stable ICU 63 + */ + template + inline void getKeywords(OutputIterator iterator, UErrorCode& status) const; + + /** + * Gets the set of Unicode keywords for this Locale. + * + * A wrapper to call createUnicodeKeywords() and write the resulting + * keywords as standard strings (or compatible objects) into any kind of + * container that can be written to by an STL style output iterator. + * + * @param iterator an STL style output iterator to write the keywords to. + * @param status error information if creating set of keywords failed. + * @stable ICU 63 + */ + template + inline void getUnicodeKeywords(OutputIterator iterator, UErrorCode& status) const; + + /** + * Gets the value for a keyword. + * + * This uses legacy keyword=value pairs, like "collation=phonebook". + * + * ICU4C doesn't do automatic conversion between legacy and Unicode + * keywords and values in getters and setters (as opposed to ICU4J). + * + * @param keywordName name of the keyword for which we want the value. Case insensitive. + * @param buffer The buffer to receive the keyword value. + * @param bufferCapacity The capacity of receiving buffer + * @param status Returns any error information while performing this operation. + * @return the length of the keyword value + * + * @stable ICU 2.8 + */ + int32_t getKeywordValue(const char* keywordName, char *buffer, int32_t bufferCapacity, UErrorCode &status) const; + + /** + * Gets the value for a keyword. + * + * This uses legacy keyword=value pairs, like "collation=phonebook". + * + * ICU4C doesn't do automatic conversion between legacy and Unicode + * keywords and values in getters and setters (as opposed to ICU4J). + * + * @param keywordName name of the keyword for which we want the value. + * @param sink the sink to receive the keyword value. + * @param status error information if getting the value failed. + * @stable ICU 63 + */ + void getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const; + + /** + * Gets the value for a keyword. + * + * This uses legacy keyword=value pairs, like "collation=phonebook". + * + * ICU4C doesn't do automatic conversion between legacy and Unicode + * keywords and values in getters and setters (as opposed to ICU4J). + * + * @param keywordName name of the keyword for which we want the value. + * @param status error information if getting the value failed. + * @return the keyword value. + * @stable ICU 63 + */ + template + inline StringClass getKeywordValue(StringPiece keywordName, UErrorCode& status) const; + + /** + * Gets the Unicode value for a Unicode keyword. + * + * This uses Unicode key-value pairs, like "co-phonebk". + * + * ICU4C doesn't do automatic conversion between legacy and Unicode + * keywords and values in getters and setters (as opposed to ICU4J). + * + * @param keywordName name of the keyword for which we want the value. + * @param sink the sink to receive the keyword value. + * @param status error information if getting the value failed. + * @stable ICU 63 + */ + void getUnicodeKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const; + + /** + * Gets the Unicode value for a Unicode keyword. + * + * This uses Unicode key-value pairs, like "co-phonebk". + * + * ICU4C doesn't do automatic conversion between legacy and Unicode + * keywords and values in getters and setters (as opposed to ICU4J). + * + * @param keywordName name of the keyword for which we want the value. + * @param status error information if getting the value failed. + * @return the keyword value. + * @stable ICU 63 + */ + template + inline StringClass getUnicodeKeywordValue(StringPiece keywordName, UErrorCode& status) const; + + /** + * Sets or removes the value for a keyword. + * + * For removing all keywords, use getBaseName(), + * and construct a new Locale if it differs from getName(). + * + * This uses legacy keyword=value pairs, like "collation=phonebook". + * + * ICU4C doesn't do automatic conversion between legacy and Unicode + * keywords and values in getters and setters (as opposed to ICU4J). + * + * @param keywordName name of the keyword to be set. Case insensitive. + * @param keywordValue value of the keyword to be set. If 0-length or + * nullptr, will result in the keyword being removed. No error is given if + * that keyword does not exist. + * @param status Returns any error information while performing this operation. + * + * @stable ICU 49 + */ + void setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status); + + /** + * Sets or removes the value for a keyword. + * + * For removing all keywords, use getBaseName(), + * and construct a new Locale if it differs from getName(). + * + * This uses legacy keyword=value pairs, like "collation=phonebook". + * + * ICU4C doesn't do automatic conversion between legacy and Unicode + * keywords and values in getters and setters (as opposed to ICU4J). + * + * @param keywordName name of the keyword to be set. + * @param keywordValue value of the keyword to be set. If 0-length or + * nullptr, will result in the keyword being removed. No error is given if + * that keyword does not exist. + * @param status Returns any error information while performing this operation. + * @stable ICU 63 + */ + void setKeywordValue(StringPiece keywordName, StringPiece keywordValue, UErrorCode& status); + + /** + * Sets or removes the Unicode value for a Unicode keyword. + * + * For removing all keywords, use getBaseName(), + * and construct a new Locale if it differs from getName(). + * + * This uses Unicode key-value pairs, like "co-phonebk". + * + * ICU4C doesn't do automatic conversion between legacy and Unicode + * keywords and values in getters and setters (as opposed to ICU4J). + * + * @param keywordName name of the keyword to be set. + * @param keywordValue value of the keyword to be set. If 0-length or + * nullptr, will result in the keyword being removed. No error is given if + * that keyword does not exist. + * @param status Returns any error information while performing this operation. + * @stable ICU 63 + */ + void setUnicodeKeywordValue(StringPiece keywordName, StringPiece keywordValue, UErrorCode& status); + + /** + * returns the locale's three-letter language code, as specified + * in ISO draft standard ISO-639-2. + * @return An alias to the code, or an empty string + * @stable ICU 2.0 + */ + const char * getISO3Language() const; + + /** + * Fills in "name" with the locale's three-letter ISO-3166 country code. + * @return An alias to the code, or an empty string + * @stable ICU 2.0 + */ + const char * getISO3Country() const; + + /** + * Returns the Windows LCID value corresponding to this locale. + * This value is stored in the resource data for the locale as a one-to-four-digit + * hexadecimal number. If the resource is missing, in the wrong format, or + * there is no Windows LCID value that corresponds to this locale, returns 0. + * @stable ICU 2.0 + */ + uint32_t getLCID(void) const; + + /** + * Returns whether this locale's script is written right-to-left. + * If there is no script subtag, then the likely script is used, see uloc_addLikelySubtags(). + * If no likely script is known, then false is returned. + * + * A script is right-to-left according to the CLDR script metadata + * which corresponds to whether the script's letters have Bidi_Class=R or AL. + * + * Returns true for "ar" and "en-Hebr", false for "zh" and "fa-Cyrl". + * + * @return true if the locale's script is written right-to-left + * @stable ICU 54 + */ + UBool isRightToLeft() const; + + /** + * Fills in "dispLang" with the name of this locale's language in a format suitable for + * user display in the default locale. For example, if the locale's language code is + * "fr" and the default locale's language code is "en", this function would set + * dispLang to "French". + * @param dispLang Receives the language's display name. + * @return A reference to "dispLang". + * @stable ICU 2.0 + */ + UnicodeString& getDisplayLanguage(UnicodeString& dispLang) const; + + /** + * Fills in "dispLang" with the name of this locale's language in a format suitable for + * user display in the locale specified by "displayLocale". For example, if the locale's + * language code is "en" and displayLocale's language code is "fr", this function would set + * dispLang to "Anglais". + * @param displayLocale Specifies the locale to be used to display the name. In other words, + * if the locale's language code is "en", passing Locale::getFrench() for + * displayLocale would result in "Anglais", while passing Locale::getGerman() + * for displayLocale would result in "Englisch". + * @param dispLang Receives the language's display name. + * @return A reference to "dispLang". + * @stable ICU 2.0 + */ + UnicodeString& getDisplayLanguage( const Locale& displayLocale, + UnicodeString& dispLang) const; + + /** + * Fills in "dispScript" with the name of this locale's script in a format suitable + * for user display in the default locale. For example, if the locale's script code + * is "LATN" and the default locale's language code is "en", this function would set + * dispScript to "Latin". + * @param dispScript Receives the scripts's display name. + * @return A reference to "dispScript". + * @stable ICU 2.8 + */ + UnicodeString& getDisplayScript( UnicodeString& dispScript) const; + + /** + * Fills in "dispScript" with the name of this locale's country in a format suitable + * for user display in the locale specified by "displayLocale". For example, if the locale's + * script code is "LATN" and displayLocale's language code is "en", this function would set + * dispScript to "Latin". + * @param displayLocale Specifies the locale to be used to display the name. In other + * words, if the locale's script code is "LATN", passing + * Locale::getFrench() for displayLocale would result in "", while + * passing Locale::getGerman() for displayLocale would result in + * "". + * @param dispScript Receives the scripts's display name. + * @return A reference to "dispScript". + * @stable ICU 2.8 + */ + UnicodeString& getDisplayScript( const Locale& displayLocale, + UnicodeString& dispScript) const; + + /** + * Fills in "dispCountry" with the name of this locale's country in a format suitable + * for user display in the default locale. For example, if the locale's country code + * is "FR" and the default locale's language code is "en", this function would set + * dispCountry to "France". + * @param dispCountry Receives the country's display name. + * @return A reference to "dispCountry". + * @stable ICU 2.0 + */ + UnicodeString& getDisplayCountry( UnicodeString& dispCountry) const; + + /** + * Fills in "dispCountry" with the name of this locale's country in a format suitable + * for user display in the locale specified by "displayLocale". For example, if the locale's + * country code is "US" and displayLocale's language code is "fr", this function would set + * dispCountry to "États-Unis". + * @param displayLocale Specifies the locale to be used to display the name. In other + * words, if the locale's country code is "US", passing + * Locale::getFrench() for displayLocale would result in "États-Unis", while + * passing Locale::getGerman() for displayLocale would result in + * "Vereinigte Staaten". + * @param dispCountry Receives the country's display name. + * @return A reference to "dispCountry". + * @stable ICU 2.0 + */ + UnicodeString& getDisplayCountry( const Locale& displayLocale, + UnicodeString& dispCountry) const; + + /** + * Fills in "dispVar" with the name of this locale's variant code in a format suitable + * for user display in the default locale. + * @param dispVar Receives the variant's name. + * @return A reference to "dispVar". + * @stable ICU 2.0 + */ + UnicodeString& getDisplayVariant( UnicodeString& dispVar) const; + + /** + * Fills in "dispVar" with the name of this locale's variant code in a format + * suitable for user display in the locale specified by "displayLocale". + * @param displayLocale Specifies the locale to be used to display the name. + * @param dispVar Receives the variant's display name. + * @return A reference to "dispVar". + * @stable ICU 2.0 + */ + UnicodeString& getDisplayVariant( const Locale& displayLocale, + UnicodeString& dispVar) const; + + /** + * Fills in "name" with the name of this locale in a format suitable for user display + * in the default locale. This function uses getDisplayLanguage(), getDisplayCountry(), + * and getDisplayVariant() to do its work, and outputs the display name in the format + * "language (country[,variant])". For example, if the default locale is en_US, then + * fr_FR's display name would be "French (France)", and es_MX_Traditional's display name + * would be "Spanish (Mexico,Traditional)". + * @param name Receives the locale's display name. + * @return A reference to "name". + * @stable ICU 2.0 + */ + UnicodeString& getDisplayName( UnicodeString& name) const; + + /** + * Fills in "name" with the name of this locale in a format suitable for user display + * in the locale specified by "displayLocale". This function uses getDisplayLanguage(), + * getDisplayCountry(), and getDisplayVariant() to do its work, and outputs the display + * name in the format "language (country[,variant])". For example, if displayLocale is + * fr_FR, then en_US's display name would be "Anglais (États-Unis)", and no_NO_NY's + * display name would be "norvégien (Norvège,NY)". + * @param displayLocale Specifies the locale to be used to display the name. + * @param name Receives the locale's display name. + * @return A reference to "name". + * @stable ICU 2.0 + */ + UnicodeString& getDisplayName( const Locale& displayLocale, + UnicodeString& name) const; + + /** + * Generates a hash code for the locale. + * @stable ICU 2.0 + */ + int32_t hashCode(void) const; + + /** + * Sets the locale to bogus + * A bogus locale represents a non-existing locale associated + * with services that can be instantiated from non-locale data + * in addition to locale (for example, collation can be + * instantiated from a locale and from a rule set). + * @stable ICU 2.1 + */ + void setToBogus(); + + /** + * Gets the bogus state. Locale object can be bogus if it doesn't exist + * @return false if it is a real locale, true if it is a bogus locale + * @stable ICU 2.1 + */ + inline UBool isBogus(void) const; + + /** + * Returns a list of all installed locales. + * @param count Receives the number of locales in the list. + * @return A pointer to an array of Locale objects. This array is the list + * of all locales with installed resource files. The called does NOT + * get ownership of this list, and must NOT delete it. + * @stable ICU 2.0 + */ + static const Locale* U_EXPORT2 getAvailableLocales(int32_t& count); + + /** + * Gets a list of all available 2-letter country codes defined in ISO 3166. This is a + * pointer to an array of pointers to arrays of char. All of these pointers are + * owned by ICU-- do not delete them, and do not write through them. The array is + * terminated with a null pointer. + * @return a list of all available country codes + * @stable ICU 2.0 + */ + static const char* const* U_EXPORT2 getISOCountries(); + + /** + * Gets a list of all available language codes defined in ISO 639. This is a pointer + * to an array of pointers to arrays of char. All of these pointers are owned + * by ICU-- do not delete them, and do not write through them. The array is + * terminated with a null pointer. + * @return a list of all available language codes + * @stable ICU 2.0 + */ + static const char* const* U_EXPORT2 getISOLanguages(); + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + * + * @stable ICU 2.2 + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + * + * @stable ICU 2.2 + */ + virtual UClassID getDynamicClassID() const override; + + /** + * A Locale iterator interface similar to a Java Iterator. + * @stable ICU 65 + */ + class U_COMMON_API Iterator /* not : public UObject because this is an interface/mixin class */ { + public: + /** @stable ICU 65 */ + virtual ~Iterator(); + + /** + * @return true if next() can be called again. + * @stable ICU 65 + */ + virtual UBool hasNext() const = 0; + + /** + * @return the next locale. + * @stable ICU 65 + */ + virtual const Locale &next() = 0; + }; + + /** + * A generic Locale iterator implementation over Locale input iterators. + * @stable ICU 65 + */ + template + class RangeIterator : public Iterator, public UMemory { + public: + /** + * Constructs an iterator from a begin/end range. + * Each of the iterator parameter values must be an + * input iterator whose value is convertible to const Locale &. + * + * @param begin Start of range. + * @param end Exclusive end of range. + * @stable ICU 65 + */ + RangeIterator(Iter begin, Iter end) : it_(begin), end_(end) {} + + /** + * @return true if next() can be called again. + * @stable ICU 65 + */ + UBool hasNext() const override { return it_ != end_; } + + /** + * @return the next locale. + * @stable ICU 65 + */ + const Locale &next() override { return *it_++; } + + private: + Iter it_; + const Iter end_; + }; + + /** + * A generic Locale iterator implementation over Locale input iterators. + * Calls the converter to convert each *begin to a const Locale &. + * @stable ICU 65 + */ + template + class ConvertingIterator : public Iterator, public UMemory { + public: + /** + * Constructs an iterator from a begin/end range. + * Each of the iterator parameter values must be an + * input iterator whose value the converter converts to const Locale &. + * + * @param begin Start of range. + * @param end Exclusive end of range. + * @param converter Converter from *begin to const Locale & or compatible. + * @stable ICU 65 + */ + ConvertingIterator(Iter begin, Iter end, Conv converter) : + it_(begin), end_(end), converter_(converter) {} + + /** + * @return true if next() can be called again. + * @stable ICU 65 + */ + UBool hasNext() const override { return it_ != end_; } + + /** + * @return the next locale. + * @stable ICU 65 + */ + const Locale &next() override { return converter_(*it_++); } + + private: + Iter it_; + const Iter end_; + Conv converter_; + }; + +protected: /* only protected for testing purposes. DO NOT USE. */ +#ifndef U_HIDE_INTERNAL_API + /** + * Set this from a single POSIX style locale string. + * @internal + */ + void setFromPOSIXID(const char *posixID); +#endif /* U_HIDE_INTERNAL_API */ + +private: + /** + * Initialize the locale object with a new name. + * Was deprecated - used in implementation - moved internal + * + * @param cLocaleID The new locale name. + * @param canonicalize whether to call uloc_canonicalize on cLocaleID + */ + Locale& init(const char* cLocaleID, UBool canonicalize); + + /* + * Internal constructor to allow construction of a locale object with + * NO side effects. (Default constructor tries to get + * the default locale.) + */ + enum ELocaleType { + eBOGUS + }; + Locale(ELocaleType); + + /** + * Initialize the locale cache for commonly used locales + */ + static Locale *getLocaleCache(void); + + char language[ULOC_LANG_CAPACITY]; + char script[ULOC_SCRIPT_CAPACITY]; + char country[ULOC_COUNTRY_CAPACITY]; + int32_t variantBegin; + char* fullName; + char fullNameBuffer[ULOC_FULLNAME_CAPACITY]; + // name without keywords + char* baseName; + void initBaseName(UErrorCode& status); + + UBool fIsBogus; + + static const Locale &getLocale(int locid); + + /** + * A friend to allow the default locale to be set by either the C or C++ API. + * @internal (private) + */ + friend Locale *locale_set_default_internal(const char *, UErrorCode& status); + + /** + * @internal (private) + */ + friend void U_CALLCONV locale_available_init(); +}; + +inline bool +Locale::operator!=(const Locale& other) const +{ + return !operator==(other); +} + +template inline StringClass +Locale::toLanguageTag(UErrorCode& status) const +{ + StringClass result; + StringByteSink sink(&result); + toLanguageTag(sink, status); + return result; +} + +inline const char * +Locale::getCountry() const +{ + return country; +} + +inline const char * +Locale::getLanguage() const +{ + return language; +} + +inline const char * +Locale::getScript() const +{ + return script; +} + +inline const char * +Locale::getVariant() const +{ + return &baseName[variantBegin]; +} + +inline const char * +Locale::getName() const +{ + return fullName; +} + +template inline void +Locale::getKeywords(OutputIterator iterator, UErrorCode& status) const +{ + LocalPointer keys(createKeywords(status)); + if (U_FAILURE(status) || keys.isNull()) { + return; + } + for (;;) { + int32_t resultLength; + const char* buffer = keys->next(&resultLength, status); + if (U_FAILURE(status) || buffer == nullptr) { + return; + } + *iterator++ = StringClass(buffer, resultLength); + } +} + +template inline void +Locale::getUnicodeKeywords(OutputIterator iterator, UErrorCode& status) const +{ + LocalPointer keys(createUnicodeKeywords(status)); + if (U_FAILURE(status) || keys.isNull()) { + return; + } + for (;;) { + int32_t resultLength; + const char* buffer = keys->next(&resultLength, status); + if (U_FAILURE(status) || buffer == nullptr) { + return; + } + *iterator++ = StringClass(buffer, resultLength); + } +} + +template inline StringClass +Locale::getKeywordValue(StringPiece keywordName, UErrorCode& status) const +{ + StringClass result; + StringByteSink sink(&result); + getKeywordValue(keywordName, sink, status); + return result; +} + +template inline StringClass +Locale::getUnicodeKeywordValue(StringPiece keywordName, UErrorCode& status) const +{ + StringClass result; + StringByteSink sink(&result); + getUnicodeKeywordValue(keywordName, sink, status); + return result; +} + +inline UBool +Locale::isBogus(void) const { + return fIsBogus; +} + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/messagepattern.h b/deps/icu-small/source/common/unicode/messagepattern.h index 4c5be13dbc9168..d8738c1d1cc9b2 100644 --- a/deps/icu-small/source/common/unicode/messagepattern.h +++ b/deps/icu-small/source/common/unicode/messagepattern.h @@ -1,949 +1,949 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011-2013, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: messagepattern.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2011mar14 -* created by: Markus W. Scherer -*/ - -#ifndef __MESSAGEPATTERN_H__ -#define __MESSAGEPATTERN_H__ - -/** - * \file - * \brief C++ API: MessagePattern class: Parses and represents ICU MessageFormat patterns. - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/parseerr.h" -#include "unicode/unistr.h" - -/** - * Mode for when an apostrophe starts quoted literal text for MessageFormat output. - * The default is DOUBLE_OPTIONAL unless overridden via uconfig.h - * (UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE). - *

- * A pair of adjacent apostrophes always results in a single apostrophe in the output, - * even when the pair is between two single, text-quoting apostrophes. - *

- * The following table shows examples of desired MessageFormat.format() output - * with the pattern strings that yield that output. - *

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Desired outputDOUBLE_OPTIONALDOUBLE_REQUIRED
I see {many}I see '{many}'(same)
I said {'Wow!'}I said '{''Wow!''}'(same)
I don't knowI don't know OR
I don''t know
I don''t know
- * @stable ICU 4.8 - * @see UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE - */ -enum UMessagePatternApostropheMode { - /** - * A literal apostrophe is represented by - * either a single or a double apostrophe pattern character. - * Within a MessageFormat pattern, a single apostrophe only starts quoted literal text - * if it immediately precedes a curly brace {}, - * or a pipe symbol | if inside a choice format, - * or a pound symbol # if inside a plural format. - *

- * This is the default behavior starting with ICU 4.8. - * @stable ICU 4.8 - */ - UMSGPAT_APOS_DOUBLE_OPTIONAL, - /** - * A literal apostrophe must be represented by - * a double apostrophe pattern character. - * A single apostrophe always starts quoted literal text. - *

- * This is the behavior of ICU 4.6 and earlier, and of the JDK. - * @stable ICU 4.8 - */ - UMSGPAT_APOS_DOUBLE_REQUIRED -}; -/** - * @stable ICU 4.8 - */ -typedef enum UMessagePatternApostropheMode UMessagePatternApostropheMode; - -/** - * MessagePattern::Part type constants. - * @stable ICU 4.8 - */ -enum UMessagePatternPartType { - /** - * Start of a message pattern (main or nested). - * The length is 0 for the top-level message - * and for a choice argument sub-message, otherwise 1 for the '{'. - * The value indicates the nesting level, starting with 0 for the main message. - *

- * There is always a later MSG_LIMIT part. - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_MSG_START, - /** - * End of a message pattern (main or nested). - * The length is 0 for the top-level message and - * the last sub-message of a choice argument, - * otherwise 1 for the '}' or (in a choice argument style) the '|'. - * The value indicates the nesting level, starting with 0 for the main message. - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_MSG_LIMIT, - /** - * Indicates a substring of the pattern string which is to be skipped when formatting. - * For example, an apostrophe that begins or ends quoted text - * would be indicated with such a part. - * The value is undefined and currently always 0. - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_SKIP_SYNTAX, - /** - * Indicates that a syntax character needs to be inserted for auto-quoting. - * The length is 0. - * The value is the character code of the insertion character. (U+0027=APOSTROPHE) - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_INSERT_CHAR, - /** - * Indicates a syntactic (non-escaped) # symbol in a plural variant. - * When formatting, replace this part's substring with the - * (value-offset) for the plural argument value. - * The value is undefined and currently always 0. - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_REPLACE_NUMBER, - /** - * Start of an argument. - * The length is 1 for the '{'. - * The value is the ordinal value of the ArgType. Use getArgType(). - *

- * This part is followed by either an ARG_NUMBER or ARG_NAME, - * followed by optional argument sub-parts (see UMessagePatternArgType constants) - * and finally an ARG_LIMIT part. - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_ARG_START, - /** - * End of an argument. - * The length is 1 for the '}'. - * The value is the ordinal value of the ArgType. Use getArgType(). - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_ARG_LIMIT, - /** - * The argument number, provided by the value. - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_ARG_NUMBER, - /** - * The argument name. - * The value is undefined and currently always 0. - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_ARG_NAME, - /** - * The argument type. - * The value is undefined and currently always 0. - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_ARG_TYPE, - /** - * The argument style text. - * The value is undefined and currently always 0. - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_ARG_STYLE, - /** - * A selector substring in a "complex" argument style. - * The value is undefined and currently always 0. - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_ARG_SELECTOR, - /** - * An integer value, for example the offset or an explicit selector value - * in a PluralFormat style. - * The part value is the integer value. - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_ARG_INT, - /** - * A numeric value, for example the offset or an explicit selector value - * in a PluralFormat style. - * The part value is an index into an internal array of numeric values; - * use getNumericValue(). - * @stable ICU 4.8 - */ - UMSGPAT_PART_TYPE_ARG_DOUBLE -}; -/** - * @stable ICU 4.8 - */ -typedef enum UMessagePatternPartType UMessagePatternPartType; - -/** - * Argument type constants. - * Returned by Part.getArgType() for ARG_START and ARG_LIMIT parts. - * - * Messages nested inside an argument are each delimited by MSG_START and MSG_LIMIT, - * with a nesting level one greater than the surrounding message. - * @stable ICU 4.8 - */ -enum UMessagePatternArgType { - /** - * The argument has no specified type. - * @stable ICU 4.8 - */ - UMSGPAT_ARG_TYPE_NONE, - /** - * The argument has a "simple" type which is provided by the ARG_TYPE part. - * An ARG_STYLE part might follow that. - * @stable ICU 4.8 - */ - UMSGPAT_ARG_TYPE_SIMPLE, - /** - * The argument is a ChoiceFormat with one or more - * ((ARG_INT | ARG_DOUBLE), ARG_SELECTOR, message) tuples. - * @stable ICU 4.8 - */ - UMSGPAT_ARG_TYPE_CHOICE, - /** - * The argument is a cardinal-number PluralFormat with an optional ARG_INT or ARG_DOUBLE offset - * (e.g., offset:1) - * and one or more (ARG_SELECTOR [explicit-value] message) tuples. - * If the selector has an explicit value (e.g., =2), then - * that value is provided by the ARG_INT or ARG_DOUBLE part preceding the message. - * Otherwise the message immediately follows the ARG_SELECTOR. - * @stable ICU 4.8 - */ - UMSGPAT_ARG_TYPE_PLURAL, - /** - * The argument is a SelectFormat with one or more (ARG_SELECTOR, message) pairs. - * @stable ICU 4.8 - */ - UMSGPAT_ARG_TYPE_SELECT, - /** - * The argument is an ordinal-number PluralFormat - * with the same style parts sequence and semantics as UMSGPAT_ARG_TYPE_PLURAL. - * @stable ICU 50 - */ - UMSGPAT_ARG_TYPE_SELECTORDINAL -}; -/** - * @stable ICU 4.8 - */ -typedef enum UMessagePatternArgType UMessagePatternArgType; - -/** - * \def UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE - * Returns true if the argument type has a plural style part sequence and semantics, - * for example UMSGPAT_ARG_TYPE_PLURAL and UMSGPAT_ARG_TYPE_SELECTORDINAL. - * @stable ICU 50 - */ -#define UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType) \ - ((argType)==UMSGPAT_ARG_TYPE_PLURAL || (argType)==UMSGPAT_ARG_TYPE_SELECTORDINAL) - -enum { - /** - * Return value from MessagePattern.validateArgumentName() for when - * the string is a valid "pattern identifier" but not a number. - * @stable ICU 4.8 - */ - UMSGPAT_ARG_NAME_NOT_NUMBER=-1, - - /** - * Return value from MessagePattern.validateArgumentName() for when - * the string is invalid. - * It might not be a valid "pattern identifier", - * or it have only ASCII digits but there is a leading zero or the number is too large. - * @stable ICU 4.8 - */ - UMSGPAT_ARG_NAME_NOT_VALID=-2 -}; - -/** - * Special value that is returned by getNumericValue(Part) when no - * numeric value is defined for a part. - * @see MessagePattern.getNumericValue() - * @stable ICU 4.8 - */ -#define UMSGPAT_NO_NUMERIC_VALUE ((double)(-123456789)) - -U_NAMESPACE_BEGIN - -class MessagePatternDoubleList; -class MessagePatternPartsList; - -/** - * Parses and represents ICU MessageFormat patterns. - * Also handles patterns for ChoiceFormat, PluralFormat and SelectFormat. - * Used in the implementations of those classes as well as in tools - * for message validation, translation and format conversion. - *

- * The parser handles all syntax relevant for identifying message arguments. - * This includes "complex" arguments whose style strings contain - * nested MessageFormat pattern substrings. - * For "simple" arguments (with no nested MessageFormat pattern substrings), - * the argument style is not parsed any further. - *

- * The parser handles named and numbered message arguments and allows both in one message. - *

- * Once a pattern has been parsed successfully, iterate through the parsed data - * with countParts(), getPart() and related methods. - *

- * The data logically represents a parse tree, but is stored and accessed - * as a list of "parts" for fast and simple parsing and to minimize object allocations. - * Arguments and nested messages are best handled via recursion. - * For every _START "part", MessagePattern.getLimitPartIndex() efficiently returns - * the index of the corresponding _LIMIT "part". - *

- * List of "parts": - *

- * message = MSG_START (SKIP_SYNTAX | INSERT_CHAR | REPLACE_NUMBER | argument)* MSG_LIMIT
- * argument = noneArg | simpleArg | complexArg
- * complexArg = choiceArg | pluralArg | selectArg
- *
- * noneArg = ARG_START.NONE (ARG_NAME | ARG_NUMBER) ARG_LIMIT.NONE
- * simpleArg = ARG_START.SIMPLE (ARG_NAME | ARG_NUMBER) ARG_TYPE [ARG_STYLE] ARG_LIMIT.SIMPLE
- * choiceArg = ARG_START.CHOICE (ARG_NAME | ARG_NUMBER) choiceStyle ARG_LIMIT.CHOICE
- * pluralArg = ARG_START.PLURAL (ARG_NAME | ARG_NUMBER) pluralStyle ARG_LIMIT.PLURAL
- * selectArg = ARG_START.SELECT (ARG_NAME | ARG_NUMBER) selectStyle ARG_LIMIT.SELECT
- *
- * choiceStyle = ((ARG_INT | ARG_DOUBLE) ARG_SELECTOR message)+
- * pluralStyle = [ARG_INT | ARG_DOUBLE] (ARG_SELECTOR [ARG_INT | ARG_DOUBLE] message)+
- * selectStyle = (ARG_SELECTOR message)+
- * 
- *
    - *
  • Literal output text is not represented directly by "parts" but accessed - * between parts of a message, from one part's getLimit() to the next part's getIndex(). - *
  • ARG_START.CHOICE stands for an ARG_START Part with ArgType CHOICE. - *
  • In the choiceStyle, the ARG_SELECTOR has the '<', the '#' or - * the less-than-or-equal-to sign (U+2264). - *
  • In the pluralStyle, the first, optional numeric Part has the "offset:" value. - * The optional numeric Part between each (ARG_SELECTOR, message) pair - * is the value of an explicit-number selector like "=2", - * otherwise the selector is a non-numeric identifier. - *
  • The REPLACE_NUMBER Part can occur only in an immediate sub-message of the pluralStyle. - *
- *

- * This class is not intended for public subclassing. - * - * @stable ICU 4.8 - */ -class U_COMMON_API MessagePattern : public UObject { -public: - /** - * Constructs an empty MessagePattern with default UMessagePatternApostropheMode. - * @param errorCode Standard 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.) - * @stable ICU 4.8 - */ - MessagePattern(UErrorCode &errorCode); - - /** - * Constructs an empty MessagePattern. - * @param mode Explicit UMessagePatternApostropheMode. - * @param errorCode Standard 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.) - * @stable ICU 4.8 - */ - MessagePattern(UMessagePatternApostropheMode mode, UErrorCode &errorCode); - - /** - * Constructs a MessagePattern with default UMessagePatternApostropheMode and - * parses the MessageFormat pattern string. - * @param pattern a MessageFormat pattern string - * @param parseError Struct to receive information on the position - * of an error within the pattern. - * Can be NULL. - * @param errorCode Standard 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.) - * TODO: turn @throws into UErrorCode specifics? - * @throws IllegalArgumentException for syntax errors in the pattern string - * @throws IndexOutOfBoundsException if certain limits are exceeded - * (e.g., argument number too high, argument name too long, etc.) - * @throws NumberFormatException if a number could not be parsed - * @stable ICU 4.8 - */ - MessagePattern(const UnicodeString &pattern, UParseError *parseError, UErrorCode &errorCode); - - /** - * Copy constructor. - * @param other Object to copy. - * @stable ICU 4.8 - */ - MessagePattern(const MessagePattern &other); - - /** - * Assignment operator. - * @param other Object to copy. - * @return *this=other - * @stable ICU 4.8 - */ - MessagePattern &operator=(const MessagePattern &other); - - /** - * Destructor. - * @stable ICU 4.8 - */ - virtual ~MessagePattern(); - - /** - * Parses a MessageFormat pattern string. - * @param pattern a MessageFormat pattern string - * @param parseError Struct to receive information on the position - * of an error within the pattern. - * Can be NULL. - * @param errorCode Standard 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.) - * @return *this - * @throws IllegalArgumentException for syntax errors in the pattern string - * @throws IndexOutOfBoundsException if certain limits are exceeded - * (e.g., argument number too high, argument name too long, etc.) - * @throws NumberFormatException if a number could not be parsed - * @stable ICU 4.8 - */ - MessagePattern &parse(const UnicodeString &pattern, - UParseError *parseError, UErrorCode &errorCode); - - /** - * Parses a ChoiceFormat pattern string. - * @param pattern a ChoiceFormat pattern string - * @param parseError Struct to receive information on the position - * of an error within the pattern. - * Can be NULL. - * @param errorCode Standard 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.) - * @return *this - * @throws IllegalArgumentException for syntax errors in the pattern string - * @throws IndexOutOfBoundsException if certain limits are exceeded - * (e.g., argument number too high, argument name too long, etc.) - * @throws NumberFormatException if a number could not be parsed - * @stable ICU 4.8 - */ - MessagePattern &parseChoiceStyle(const UnicodeString &pattern, - UParseError *parseError, UErrorCode &errorCode); - - /** - * Parses a PluralFormat pattern string. - * @param pattern a PluralFormat pattern string - * @param parseError Struct to receive information on the position - * of an error within the pattern. - * Can be NULL. - * @param errorCode Standard 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.) - * @return *this - * @throws IllegalArgumentException for syntax errors in the pattern string - * @throws IndexOutOfBoundsException if certain limits are exceeded - * (e.g., argument number too high, argument name too long, etc.) - * @throws NumberFormatException if a number could not be parsed - * @stable ICU 4.8 - */ - MessagePattern &parsePluralStyle(const UnicodeString &pattern, - UParseError *parseError, UErrorCode &errorCode); - - /** - * Parses a SelectFormat pattern string. - * @param pattern a SelectFormat pattern string - * @param parseError Struct to receive information on the position - * of an error within the pattern. - * Can be NULL. - * @param errorCode Standard 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.) - * @return *this - * @throws IllegalArgumentException for syntax errors in the pattern string - * @throws IndexOutOfBoundsException if certain limits are exceeded - * (e.g., argument number too high, argument name too long, etc.) - * @throws NumberFormatException if a number could not be parsed - * @stable ICU 4.8 - */ - MessagePattern &parseSelectStyle(const UnicodeString &pattern, - UParseError *parseError, UErrorCode &errorCode); - - /** - * Clears this MessagePattern. - * countParts() will return 0. - * @stable ICU 4.8 - */ - void clear(); - - /** - * Clears this MessagePattern and sets the UMessagePatternApostropheMode. - * countParts() will return 0. - * @param mode The new UMessagePatternApostropheMode. - * @stable ICU 4.8 - */ - void clearPatternAndSetApostropheMode(UMessagePatternApostropheMode mode) { - clear(); - aposMode=mode; - } - - /** - * @param other another object to compare with. - * @return true if this object is equivalent to the other one. - * @stable ICU 4.8 - */ - bool operator==(const MessagePattern &other) const; - - /** - * @param other another object to compare with. - * @return false if this object is equivalent to the other one. - * @stable ICU 4.8 - */ - inline bool operator!=(const MessagePattern &other) const { - return !operator==(other); - } - - /** - * @return A hash code for this object. - * @stable ICU 4.8 - */ - int32_t hashCode() const; - - /** - * @return this instance's UMessagePatternApostropheMode. - * @stable ICU 4.8 - */ - UMessagePatternApostropheMode getApostropheMode() const { - return aposMode; - } - - // Java has package-private jdkAposMode() here. - // In C++, this is declared in the MessageImpl class. - - /** - * @return the parsed pattern string (null if none was parsed). - * @stable ICU 4.8 - */ - const UnicodeString &getPatternString() const { - return msg; - } - - /** - * Does the parsed pattern have named arguments like {first_name}? - * @return true if the parsed pattern has at least one named argument. - * @stable ICU 4.8 - */ - UBool hasNamedArguments() const { - return hasArgNames; - } - - /** - * Does the parsed pattern have numbered arguments like {2}? - * @return true if the parsed pattern has at least one numbered argument. - * @stable ICU 4.8 - */ - UBool hasNumberedArguments() const { - return hasArgNumbers; - } - - /** - * Validates and parses an argument name or argument number string. - * An argument name must be a "pattern identifier", that is, it must contain - * no Unicode Pattern_Syntax or Pattern_White_Space characters. - * If it only contains ASCII digits, then it must be a small integer with no leading zero. - * @param name Input string. - * @return >=0 if the name is a valid number, - * ARG_NAME_NOT_NUMBER (-1) if it is a "pattern identifier" but not all ASCII digits, - * ARG_NAME_NOT_VALID (-2) if it is neither. - * @stable ICU 4.8 - */ - static int32_t validateArgumentName(const UnicodeString &name); - - /** - * Returns a version of the parsed pattern string where each ASCII apostrophe - * is doubled (escaped) if it is not already, and if it is not interpreted as quoting syntax. - *

- * For example, this turns "I don't '{know}' {gender,select,female{h''er}other{h'im}}." - * into "I don''t '{know}' {gender,select,female{h''er}other{h''im}}." - * @return the deep-auto-quoted version of the parsed pattern string. - * @see MessageFormat.autoQuoteApostrophe() - * @stable ICU 4.8 - */ - UnicodeString autoQuoteApostropheDeep() const; - - class Part; - - /** - * Returns the number of "parts" created by parsing the pattern string. - * Returns 0 if no pattern has been parsed or clear() was called. - * @return the number of pattern parts. - * @stable ICU 4.8 - */ - int32_t countParts() const { - return partsLength; - } - - /** - * Gets the i-th pattern "part". - * @param i The index of the Part data. (0..countParts()-1) - * @return the i-th pattern "part". - * @stable ICU 4.8 - */ - const Part &getPart(int32_t i) const { - return parts[i]; - } - - /** - * Returns the UMessagePatternPartType of the i-th pattern "part". - * Convenience method for getPart(i).getType(). - * @param i The index of the Part data. (0..countParts()-1) - * @return The UMessagePatternPartType of the i-th Part. - * @stable ICU 4.8 - */ - UMessagePatternPartType getPartType(int32_t i) const { - return getPart(i).type; - } - - /** - * Returns the pattern index of the specified pattern "part". - * Convenience method for getPart(partIndex).getIndex(). - * @param partIndex The index of the Part data. (0..countParts()-1) - * @return The pattern index of this Part. - * @stable ICU 4.8 - */ - int32_t getPatternIndex(int32_t partIndex) const { - return getPart(partIndex).index; - } - - /** - * Returns the substring of the pattern string indicated by the Part. - * Convenience method for getPatternString().substring(part.getIndex(), part.getLimit()). - * @param part a part of this MessagePattern. - * @return the substring associated with part. - * @stable ICU 4.8 - */ - UnicodeString getSubstring(const Part &part) const { - return msg.tempSubString(part.index, part.length); - } - - /** - * Compares the part's substring with the input string s. - * @param part a part of this MessagePattern. - * @param s a string. - * @return true if getSubstring(part).equals(s). - * @stable ICU 4.8 - */ - UBool partSubstringMatches(const Part &part, const UnicodeString &s) const { - return 0==msg.compare(part.index, part.length, s); - } - - /** - * Returns the numeric value associated with an ARG_INT or ARG_DOUBLE. - * @param part a part of this MessagePattern. - * @return the part's numeric value, or UMSGPAT_NO_NUMERIC_VALUE if this is not a numeric part. - * @stable ICU 4.8 - */ - double getNumericValue(const Part &part) const; - - /** - * Returns the "offset:" value of a PluralFormat argument, or 0 if none is specified. - * @param pluralStart the index of the first PluralFormat argument style part. (0..countParts()-1) - * @return the "offset:" value. - * @stable ICU 4.8 - */ - double getPluralOffset(int32_t pluralStart) const; - - /** - * Returns the index of the ARG|MSG_LIMIT part corresponding to the ARG|MSG_START at start. - * @param start The index of some Part data (0..countParts()-1); - * this Part should be of Type ARG_START or MSG_START. - * @return The first i>start where getPart(i).getType()==ARG|MSG_LIMIT at the same nesting level, - * or start itself if getPartType(msgStart)!=ARG|MSG_START. - * @stable ICU 4.8 - */ - int32_t getLimitPartIndex(int32_t start) const { - int32_t limit=getPart(start).limitPartIndex; - if(limit parts=new ArrayList(); - MessagePatternPartsList *partsList; - Part *parts; - int32_t partsLength; - // ArrayList numericValues; - MessagePatternDoubleList *numericValuesList; - double *numericValues; - int32_t numericValuesLength; - UBool hasArgNames; - UBool hasArgNumbers; - UBool needsAutoQuoting; -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_FORMATTING - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __MESSAGEPATTERN_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2011-2013, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: messagepattern.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2011mar14 +* created by: Markus W. Scherer +*/ + +#ifndef __MESSAGEPATTERN_H__ +#define __MESSAGEPATTERN_H__ + +/** + * \file + * \brief C++ API: MessagePattern class: Parses and represents ICU MessageFormat patterns. + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/parseerr.h" +#include "unicode/unistr.h" + +/** + * Mode for when an apostrophe starts quoted literal text for MessageFormat output. + * The default is DOUBLE_OPTIONAL unless overridden via uconfig.h + * (UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE). + *

+ * A pair of adjacent apostrophes always results in a single apostrophe in the output, + * even when the pair is between two single, text-quoting apostrophes. + *

+ * The following table shows examples of desired MessageFormat.format() output + * with the pattern strings that yield that output. + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Desired outputDOUBLE_OPTIONALDOUBLE_REQUIRED
I see {many}I see '{many}'(same)
I said {'Wow!'}I said '{''Wow!''}'(same)
I don't knowI don't know OR
I don''t know
I don''t know
+ * @stable ICU 4.8 + * @see UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE + */ +enum UMessagePatternApostropheMode { + /** + * A literal apostrophe is represented by + * either a single or a double apostrophe pattern character. + * Within a MessageFormat pattern, a single apostrophe only starts quoted literal text + * if it immediately precedes a curly brace {}, + * or a pipe symbol | if inside a choice format, + * or a pound symbol # if inside a plural format. + *

+ * This is the default behavior starting with ICU 4.8. + * @stable ICU 4.8 + */ + UMSGPAT_APOS_DOUBLE_OPTIONAL, + /** + * A literal apostrophe must be represented by + * a double apostrophe pattern character. + * A single apostrophe always starts quoted literal text. + *

+ * This is the behavior of ICU 4.6 and earlier, and of the JDK. + * @stable ICU 4.8 + */ + UMSGPAT_APOS_DOUBLE_REQUIRED +}; +/** + * @stable ICU 4.8 + */ +typedef enum UMessagePatternApostropheMode UMessagePatternApostropheMode; + +/** + * MessagePattern::Part type constants. + * @stable ICU 4.8 + */ +enum UMessagePatternPartType { + /** + * Start of a message pattern (main or nested). + * The length is 0 for the top-level message + * and for a choice argument sub-message, otherwise 1 for the '{'. + * The value indicates the nesting level, starting with 0 for the main message. + *

+ * There is always a later MSG_LIMIT part. + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_MSG_START, + /** + * End of a message pattern (main or nested). + * The length is 0 for the top-level message and + * the last sub-message of a choice argument, + * otherwise 1 for the '}' or (in a choice argument style) the '|'. + * The value indicates the nesting level, starting with 0 for the main message. + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_MSG_LIMIT, + /** + * Indicates a substring of the pattern string which is to be skipped when formatting. + * For example, an apostrophe that begins or ends quoted text + * would be indicated with such a part. + * The value is undefined and currently always 0. + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_SKIP_SYNTAX, + /** + * Indicates that a syntax character needs to be inserted for auto-quoting. + * The length is 0. + * The value is the character code of the insertion character. (U+0027=APOSTROPHE) + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_INSERT_CHAR, + /** + * Indicates a syntactic (non-escaped) # symbol in a plural variant. + * When formatting, replace this part's substring with the + * (value-offset) for the plural argument value. + * The value is undefined and currently always 0. + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_REPLACE_NUMBER, + /** + * Start of an argument. + * The length is 1 for the '{'. + * The value is the ordinal value of the ArgType. Use getArgType(). + *

+ * This part is followed by either an ARG_NUMBER or ARG_NAME, + * followed by optional argument sub-parts (see UMessagePatternArgType constants) + * and finally an ARG_LIMIT part. + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_ARG_START, + /** + * End of an argument. + * The length is 1 for the '}'. + * The value is the ordinal value of the ArgType. Use getArgType(). + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_ARG_LIMIT, + /** + * The argument number, provided by the value. + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_ARG_NUMBER, + /** + * The argument name. + * The value is undefined and currently always 0. + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_ARG_NAME, + /** + * The argument type. + * The value is undefined and currently always 0. + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_ARG_TYPE, + /** + * The argument style text. + * The value is undefined and currently always 0. + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_ARG_STYLE, + /** + * A selector substring in a "complex" argument style. + * The value is undefined and currently always 0. + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_ARG_SELECTOR, + /** + * An integer value, for example the offset or an explicit selector value + * in a PluralFormat style. + * The part value is the integer value. + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_ARG_INT, + /** + * A numeric value, for example the offset or an explicit selector value + * in a PluralFormat style. + * The part value is an index into an internal array of numeric values; + * use getNumericValue(). + * @stable ICU 4.8 + */ + UMSGPAT_PART_TYPE_ARG_DOUBLE +}; +/** + * @stable ICU 4.8 + */ +typedef enum UMessagePatternPartType UMessagePatternPartType; + +/** + * Argument type constants. + * Returned by Part.getArgType() for ARG_START and ARG_LIMIT parts. + * + * Messages nested inside an argument are each delimited by MSG_START and MSG_LIMIT, + * with a nesting level one greater than the surrounding message. + * @stable ICU 4.8 + */ +enum UMessagePatternArgType { + /** + * The argument has no specified type. + * @stable ICU 4.8 + */ + UMSGPAT_ARG_TYPE_NONE, + /** + * The argument has a "simple" type which is provided by the ARG_TYPE part. + * An ARG_STYLE part might follow that. + * @stable ICU 4.8 + */ + UMSGPAT_ARG_TYPE_SIMPLE, + /** + * The argument is a ChoiceFormat with one or more + * ((ARG_INT | ARG_DOUBLE), ARG_SELECTOR, message) tuples. + * @stable ICU 4.8 + */ + UMSGPAT_ARG_TYPE_CHOICE, + /** + * The argument is a cardinal-number PluralFormat with an optional ARG_INT or ARG_DOUBLE offset + * (e.g., offset:1) + * and one or more (ARG_SELECTOR [explicit-value] message) tuples. + * If the selector has an explicit value (e.g., =2), then + * that value is provided by the ARG_INT or ARG_DOUBLE part preceding the message. + * Otherwise the message immediately follows the ARG_SELECTOR. + * @stable ICU 4.8 + */ + UMSGPAT_ARG_TYPE_PLURAL, + /** + * The argument is a SelectFormat with one or more (ARG_SELECTOR, message) pairs. + * @stable ICU 4.8 + */ + UMSGPAT_ARG_TYPE_SELECT, + /** + * The argument is an ordinal-number PluralFormat + * with the same style parts sequence and semantics as UMSGPAT_ARG_TYPE_PLURAL. + * @stable ICU 50 + */ + UMSGPAT_ARG_TYPE_SELECTORDINAL +}; +/** + * @stable ICU 4.8 + */ +typedef enum UMessagePatternArgType UMessagePatternArgType; + +/** + * \def UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE + * Returns true if the argument type has a plural style part sequence and semantics, + * for example UMSGPAT_ARG_TYPE_PLURAL and UMSGPAT_ARG_TYPE_SELECTORDINAL. + * @stable ICU 50 + */ +#define UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType) \ + ((argType)==UMSGPAT_ARG_TYPE_PLURAL || (argType)==UMSGPAT_ARG_TYPE_SELECTORDINAL) + +enum { + /** + * Return value from MessagePattern.validateArgumentName() for when + * the string is a valid "pattern identifier" but not a number. + * @stable ICU 4.8 + */ + UMSGPAT_ARG_NAME_NOT_NUMBER=-1, + + /** + * Return value from MessagePattern.validateArgumentName() for when + * the string is invalid. + * It might not be a valid "pattern identifier", + * or it have only ASCII digits but there is a leading zero or the number is too large. + * @stable ICU 4.8 + */ + UMSGPAT_ARG_NAME_NOT_VALID=-2 +}; + +/** + * Special value that is returned by getNumericValue(Part) when no + * numeric value is defined for a part. + * @see MessagePattern.getNumericValue() + * @stable ICU 4.8 + */ +#define UMSGPAT_NO_NUMERIC_VALUE ((double)(-123456789)) + +U_NAMESPACE_BEGIN + +class MessagePatternDoubleList; +class MessagePatternPartsList; + +/** + * Parses and represents ICU MessageFormat patterns. + * Also handles patterns for ChoiceFormat, PluralFormat and SelectFormat. + * Used in the implementations of those classes as well as in tools + * for message validation, translation and format conversion. + *

+ * The parser handles all syntax relevant for identifying message arguments. + * This includes "complex" arguments whose style strings contain + * nested MessageFormat pattern substrings. + * For "simple" arguments (with no nested MessageFormat pattern substrings), + * the argument style is not parsed any further. + *

+ * The parser handles named and numbered message arguments and allows both in one message. + *

+ * Once a pattern has been parsed successfully, iterate through the parsed data + * with countParts(), getPart() and related methods. + *

+ * The data logically represents a parse tree, but is stored and accessed + * as a list of "parts" for fast and simple parsing and to minimize object allocations. + * Arguments and nested messages are best handled via recursion. + * For every _START "part", MessagePattern.getLimitPartIndex() efficiently returns + * the index of the corresponding _LIMIT "part". + *

+ * List of "parts": + *

+ * message = MSG_START (SKIP_SYNTAX | INSERT_CHAR | REPLACE_NUMBER | argument)* MSG_LIMIT
+ * argument = noneArg | simpleArg | complexArg
+ * complexArg = choiceArg | pluralArg | selectArg
+ *
+ * noneArg = ARG_START.NONE (ARG_NAME | ARG_NUMBER) ARG_LIMIT.NONE
+ * simpleArg = ARG_START.SIMPLE (ARG_NAME | ARG_NUMBER) ARG_TYPE [ARG_STYLE] ARG_LIMIT.SIMPLE
+ * choiceArg = ARG_START.CHOICE (ARG_NAME | ARG_NUMBER) choiceStyle ARG_LIMIT.CHOICE
+ * pluralArg = ARG_START.PLURAL (ARG_NAME | ARG_NUMBER) pluralStyle ARG_LIMIT.PLURAL
+ * selectArg = ARG_START.SELECT (ARG_NAME | ARG_NUMBER) selectStyle ARG_LIMIT.SELECT
+ *
+ * choiceStyle = ((ARG_INT | ARG_DOUBLE) ARG_SELECTOR message)+
+ * pluralStyle = [ARG_INT | ARG_DOUBLE] (ARG_SELECTOR [ARG_INT | ARG_DOUBLE] message)+
+ * selectStyle = (ARG_SELECTOR message)+
+ * 
+ *
    + *
  • Literal output text is not represented directly by "parts" but accessed + * between parts of a message, from one part's getLimit() to the next part's getIndex(). + *
  • ARG_START.CHOICE stands for an ARG_START Part with ArgType CHOICE. + *
  • In the choiceStyle, the ARG_SELECTOR has the '<', the '#' or + * the less-than-or-equal-to sign (U+2264). + *
  • In the pluralStyle, the first, optional numeric Part has the "offset:" value. + * The optional numeric Part between each (ARG_SELECTOR, message) pair + * is the value of an explicit-number selector like "=2", + * otherwise the selector is a non-numeric identifier. + *
  • The REPLACE_NUMBER Part can occur only in an immediate sub-message of the pluralStyle. + *
+ *

+ * This class is not intended for public subclassing. + * + * @stable ICU 4.8 + */ +class U_COMMON_API MessagePattern : public UObject { +public: + /** + * Constructs an empty MessagePattern with default UMessagePatternApostropheMode. + * @param errorCode Standard 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.) + * @stable ICU 4.8 + */ + MessagePattern(UErrorCode &errorCode); + + /** + * Constructs an empty MessagePattern. + * @param mode Explicit UMessagePatternApostropheMode. + * @param errorCode Standard 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.) + * @stable ICU 4.8 + */ + MessagePattern(UMessagePatternApostropheMode mode, UErrorCode &errorCode); + + /** + * Constructs a MessagePattern with default UMessagePatternApostropheMode and + * parses the MessageFormat pattern string. + * @param pattern a MessageFormat pattern string + * @param parseError Struct to receive information on the position + * of an error within the pattern. + * Can be nullptr. + * @param errorCode Standard 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.) + * TODO: turn @throws into UErrorCode specifics? + * @throws IllegalArgumentException for syntax errors in the pattern string + * @throws IndexOutOfBoundsException if certain limits are exceeded + * (e.g., argument number too high, argument name too long, etc.) + * @throws NumberFormatException if a number could not be parsed + * @stable ICU 4.8 + */ + MessagePattern(const UnicodeString &pattern, UParseError *parseError, UErrorCode &errorCode); + + /** + * Copy constructor. + * @param other Object to copy. + * @stable ICU 4.8 + */ + MessagePattern(const MessagePattern &other); + + /** + * Assignment operator. + * @param other Object to copy. + * @return *this=other + * @stable ICU 4.8 + */ + MessagePattern &operator=(const MessagePattern &other); + + /** + * Destructor. + * @stable ICU 4.8 + */ + virtual ~MessagePattern(); + + /** + * Parses a MessageFormat pattern string. + * @param pattern a MessageFormat pattern string + * @param parseError Struct to receive information on the position + * of an error within the pattern. + * Can be nullptr. + * @param errorCode Standard 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.) + * @return *this + * @throws IllegalArgumentException for syntax errors in the pattern string + * @throws IndexOutOfBoundsException if certain limits are exceeded + * (e.g., argument number too high, argument name too long, etc.) + * @throws NumberFormatException if a number could not be parsed + * @stable ICU 4.8 + */ + MessagePattern &parse(const UnicodeString &pattern, + UParseError *parseError, UErrorCode &errorCode); + + /** + * Parses a ChoiceFormat pattern string. + * @param pattern a ChoiceFormat pattern string + * @param parseError Struct to receive information on the position + * of an error within the pattern. + * Can be nullptr. + * @param errorCode Standard 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.) + * @return *this + * @throws IllegalArgumentException for syntax errors in the pattern string + * @throws IndexOutOfBoundsException if certain limits are exceeded + * (e.g., argument number too high, argument name too long, etc.) + * @throws NumberFormatException if a number could not be parsed + * @stable ICU 4.8 + */ + MessagePattern &parseChoiceStyle(const UnicodeString &pattern, + UParseError *parseError, UErrorCode &errorCode); + + /** + * Parses a PluralFormat pattern string. + * @param pattern a PluralFormat pattern string + * @param parseError Struct to receive information on the position + * of an error within the pattern. + * Can be nullptr. + * @param errorCode Standard 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.) + * @return *this + * @throws IllegalArgumentException for syntax errors in the pattern string + * @throws IndexOutOfBoundsException if certain limits are exceeded + * (e.g., argument number too high, argument name too long, etc.) + * @throws NumberFormatException if a number could not be parsed + * @stable ICU 4.8 + */ + MessagePattern &parsePluralStyle(const UnicodeString &pattern, + UParseError *parseError, UErrorCode &errorCode); + + /** + * Parses a SelectFormat pattern string. + * @param pattern a SelectFormat pattern string + * @param parseError Struct to receive information on the position + * of an error within the pattern. + * Can be nullptr. + * @param errorCode Standard 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.) + * @return *this + * @throws IllegalArgumentException for syntax errors in the pattern string + * @throws IndexOutOfBoundsException if certain limits are exceeded + * (e.g., argument number too high, argument name too long, etc.) + * @throws NumberFormatException if a number could not be parsed + * @stable ICU 4.8 + */ + MessagePattern &parseSelectStyle(const UnicodeString &pattern, + UParseError *parseError, UErrorCode &errorCode); + + /** + * Clears this MessagePattern. + * countParts() will return 0. + * @stable ICU 4.8 + */ + void clear(); + + /** + * Clears this MessagePattern and sets the UMessagePatternApostropheMode. + * countParts() will return 0. + * @param mode The new UMessagePatternApostropheMode. + * @stable ICU 4.8 + */ + void clearPatternAndSetApostropheMode(UMessagePatternApostropheMode mode) { + clear(); + aposMode=mode; + } + + /** + * @param other another object to compare with. + * @return true if this object is equivalent to the other one. + * @stable ICU 4.8 + */ + bool operator==(const MessagePattern &other) const; + + /** + * @param other another object to compare with. + * @return false if this object is equivalent to the other one. + * @stable ICU 4.8 + */ + inline bool operator!=(const MessagePattern &other) const { + return !operator==(other); + } + + /** + * @return A hash code for this object. + * @stable ICU 4.8 + */ + int32_t hashCode() const; + + /** + * @return this instance's UMessagePatternApostropheMode. + * @stable ICU 4.8 + */ + UMessagePatternApostropheMode getApostropheMode() const { + return aposMode; + } + + // Java has package-private jdkAposMode() here. + // In C++, this is declared in the MessageImpl class. + + /** + * @return the parsed pattern string (null if none was parsed). + * @stable ICU 4.8 + */ + const UnicodeString &getPatternString() const { + return msg; + } + + /** + * Does the parsed pattern have named arguments like {first_name}? + * @return true if the parsed pattern has at least one named argument. + * @stable ICU 4.8 + */ + UBool hasNamedArguments() const { + return hasArgNames; + } + + /** + * Does the parsed pattern have numbered arguments like {2}? + * @return true if the parsed pattern has at least one numbered argument. + * @stable ICU 4.8 + */ + UBool hasNumberedArguments() const { + return hasArgNumbers; + } + + /** + * Validates and parses an argument name or argument number string. + * An argument name must be a "pattern identifier", that is, it must contain + * no Unicode Pattern_Syntax or Pattern_White_Space characters. + * If it only contains ASCII digits, then it must be a small integer with no leading zero. + * @param name Input string. + * @return >=0 if the name is a valid number, + * ARG_NAME_NOT_NUMBER (-1) if it is a "pattern identifier" but not all ASCII digits, + * ARG_NAME_NOT_VALID (-2) if it is neither. + * @stable ICU 4.8 + */ + static int32_t validateArgumentName(const UnicodeString &name); + + /** + * Returns a version of the parsed pattern string where each ASCII apostrophe + * is doubled (escaped) if it is not already, and if it is not interpreted as quoting syntax. + *

+ * For example, this turns "I don't '{know}' {gender,select,female{h''er}other{h'im}}." + * into "I don''t '{know}' {gender,select,female{h''er}other{h''im}}." + * @return the deep-auto-quoted version of the parsed pattern string. + * @see MessageFormat.autoQuoteApostrophe() + * @stable ICU 4.8 + */ + UnicodeString autoQuoteApostropheDeep() const; + + class Part; + + /** + * Returns the number of "parts" created by parsing the pattern string. + * Returns 0 if no pattern has been parsed or clear() was called. + * @return the number of pattern parts. + * @stable ICU 4.8 + */ + int32_t countParts() const { + return partsLength; + } + + /** + * Gets the i-th pattern "part". + * @param i The index of the Part data. (0..countParts()-1) + * @return the i-th pattern "part". + * @stable ICU 4.8 + */ + const Part &getPart(int32_t i) const { + return parts[i]; + } + + /** + * Returns the UMessagePatternPartType of the i-th pattern "part". + * Convenience method for getPart(i).getType(). + * @param i The index of the Part data. (0..countParts()-1) + * @return The UMessagePatternPartType of the i-th Part. + * @stable ICU 4.8 + */ + UMessagePatternPartType getPartType(int32_t i) const { + return getPart(i).type; + } + + /** + * Returns the pattern index of the specified pattern "part". + * Convenience method for getPart(partIndex).getIndex(). + * @param partIndex The index of the Part data. (0..countParts()-1) + * @return The pattern index of this Part. + * @stable ICU 4.8 + */ + int32_t getPatternIndex(int32_t partIndex) const { + return getPart(partIndex).index; + } + + /** + * Returns the substring of the pattern string indicated by the Part. + * Convenience method for getPatternString().substring(part.getIndex(), part.getLimit()). + * @param part a part of this MessagePattern. + * @return the substring associated with part. + * @stable ICU 4.8 + */ + UnicodeString getSubstring(const Part &part) const { + return msg.tempSubString(part.index, part.length); + } + + /** + * Compares the part's substring with the input string s. + * @param part a part of this MessagePattern. + * @param s a string. + * @return true if getSubstring(part).equals(s). + * @stable ICU 4.8 + */ + UBool partSubstringMatches(const Part &part, const UnicodeString &s) const { + return 0==msg.compare(part.index, part.length, s); + } + + /** + * Returns the numeric value associated with an ARG_INT or ARG_DOUBLE. + * @param part a part of this MessagePattern. + * @return the part's numeric value, or UMSGPAT_NO_NUMERIC_VALUE if this is not a numeric part. + * @stable ICU 4.8 + */ + double getNumericValue(const Part &part) const; + + /** + * Returns the "offset:" value of a PluralFormat argument, or 0 if none is specified. + * @param pluralStart the index of the first PluralFormat argument style part. (0..countParts()-1) + * @return the "offset:" value. + * @stable ICU 4.8 + */ + double getPluralOffset(int32_t pluralStart) const; + + /** + * Returns the index of the ARG|MSG_LIMIT part corresponding to the ARG|MSG_START at start. + * @param start The index of some Part data (0..countParts()-1); + * this Part should be of Type ARG_START or MSG_START. + * @return The first i>start where getPart(i).getType()==ARG|MSG_LIMIT at the same nesting level, + * or start itself if getPartType(msgStart)!=ARG|MSG_START. + * @stable ICU 4.8 + */ + int32_t getLimitPartIndex(int32_t start) const { + int32_t limit=getPart(start).limitPartIndex; + if(limit parts=new ArrayList(); + MessagePatternPartsList *partsList; + Part *parts; + int32_t partsLength; + // ArrayList numericValues; + MessagePatternDoubleList *numericValuesList; + double *numericValues; + int32_t numericValuesLength; + UBool hasArgNames; + UBool hasArgNumbers; + UBool needsAutoQuoting; +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __MESSAGEPATTERN_H__ diff --git a/deps/icu-small/source/common/unicode/normalizer2.h b/deps/icu-small/source/common/unicode/normalizer2.h index 2d355250c29fde..cdae9f982f1e36 100644 --- a/deps/icu-small/source/common/unicode/normalizer2.h +++ b/deps/icu-small/source/common/unicode/normalizer2.h @@ -1,771 +1,771 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2009-2013, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: normalizer2.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2009nov22 -* created by: Markus W. Scherer -*/ - -#ifndef __NORMALIZER2_H__ -#define __NORMALIZER2_H__ - -/** - * \file - * \brief C++ API: New API for Unicode Normalization. - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#if !UCONFIG_NO_NORMALIZATION - -#include "unicode/stringpiece.h" -#include "unicode/uniset.h" -#include "unicode/unistr.h" -#include "unicode/unorm2.h" - -U_NAMESPACE_BEGIN - -class ByteSink; - -/** - * Unicode normalization functionality for standard Unicode normalization or - * for using custom mapping tables. - * All instances of this class are unmodifiable/immutable. - * Instances returned by getInstance() are singletons that must not be deleted by the caller. - * The Normalizer2 class is not intended for public subclassing. - * - * The primary functions are to produce a normalized string and to detect whether - * a string is already normalized. - * The most commonly used normalization forms are those defined in - * http://www.unicode.org/unicode/reports/tr15/ - * However, this API supports additional normalization forms for specialized purposes. - * For example, NFKC_Casefold is provided via getInstance("nfkc_cf", COMPOSE) - * and can be used in implementations of UTS #46. - * - * Not only are the standard compose and decompose modes supplied, - * but additional modes are provided as documented in the Mode enum. - * - * Some of the functions in this class identify normalization boundaries. - * At a normalization boundary, the portions of the string - * before it and starting from it do not interact and can be handled independently. - * - * The spanQuickCheckYes() stops at a normalization boundary. - * When the goal is a normalized string, then the text before the boundary - * can be copied, and the remainder can be processed with normalizeSecondAndAppend(). - * - * The hasBoundaryBefore(), hasBoundaryAfter() and isInert() functions test whether - * a character is guaranteed to be at a normalization boundary, - * regardless of context. - * This is used for moving from one normalization boundary to the next - * or preceding boundary, and for performing iterative normalization. - * - * Iterative normalization is useful when only a small portion of a - * longer string needs to be processed. - * For example, in ICU, iterative normalization is used by the NormalizationTransliterator - * (to avoid replacing already-normalized text) and ucol_nextSortKeyPart() - * (to process only the substring for which sort key bytes are computed). - * - * The set of normalization boundaries returned by these functions may not be - * complete: There may be more boundaries that could be returned. - * Different functions may return different boundaries. - * @stable ICU 4.4 - */ -class U_COMMON_API Normalizer2 : public UObject { -public: - /** - * Destructor. - * @stable ICU 4.4 - */ - ~Normalizer2(); - - /** - * Returns a Normalizer2 instance for Unicode NFC normalization. - * Same as getInstance(NULL, "nfc", UNORM2_COMPOSE, errorCode). - * Returns an unmodifiable singleton instance. Do not delete it. - * @param errorCode Standard 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.) - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ - static const Normalizer2 * - getNFCInstance(UErrorCode &errorCode); - - /** - * Returns a Normalizer2 instance for Unicode NFD normalization. - * Same as getInstance(NULL, "nfc", UNORM2_DECOMPOSE, errorCode). - * Returns an unmodifiable singleton instance. Do not delete it. - * @param errorCode Standard 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.) - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ - static const Normalizer2 * - getNFDInstance(UErrorCode &errorCode); - - /** - * Returns a Normalizer2 instance for Unicode NFKC normalization. - * Same as getInstance(NULL, "nfkc", UNORM2_COMPOSE, errorCode). - * Returns an unmodifiable singleton instance. Do not delete it. - * @param errorCode Standard 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.) - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ - static const Normalizer2 * - getNFKCInstance(UErrorCode &errorCode); - - /** - * Returns a Normalizer2 instance for Unicode NFKD normalization. - * Same as getInstance(NULL, "nfkc", UNORM2_DECOMPOSE, errorCode). - * Returns an unmodifiable singleton instance. Do not delete it. - * @param errorCode Standard 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.) - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ - static const Normalizer2 * - getNFKDInstance(UErrorCode &errorCode); - - /** - * Returns a Normalizer2 instance for Unicode NFKC_Casefold normalization. - * Same as getInstance(NULL, "nfkc_cf", UNORM2_COMPOSE, errorCode). - * Returns an unmodifiable singleton instance. Do not delete it. - * @param errorCode Standard 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.) - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ - static const Normalizer2 * - getNFKCCasefoldInstance(UErrorCode &errorCode); - - /** - * Returns a Normalizer2 instance which uses the specified data file - * (packageName/name similar to ucnv_openPackage() and ures_open()/ResourceBundle) - * and which composes or decomposes text according to the specified mode. - * Returns an unmodifiable singleton instance. Do not delete it. - * - * Use packageName=NULL for data files that are part of ICU's own data. - * Use name="nfc" and UNORM2_COMPOSE/UNORM2_DECOMPOSE for Unicode standard NFC/NFD. - * Use name="nfkc" and UNORM2_COMPOSE/UNORM2_DECOMPOSE for Unicode standard NFKC/NFKD. - * Use name="nfkc_cf" and UNORM2_COMPOSE for Unicode standard NFKC_CF=NFKC_Casefold. - * - * @param packageName NULL for ICU built-in data, otherwise application data package name - * @param name "nfc" or "nfkc" or "nfkc_cf" or name of custom data file - * @param mode normalization mode (compose or decompose etc.) - * @param errorCode Standard 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.) - * @return the requested Normalizer2, if successful - * @stable ICU 4.4 - */ - static const Normalizer2 * - getInstance(const char *packageName, - const char *name, - UNormalization2Mode mode, - UErrorCode &errorCode); - - /** - * Returns the normalized form of the source string. - * @param src source string - * @param errorCode Standard 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.) - * @return normalized src - * @stable ICU 4.4 - */ - UnicodeString - normalize(const UnicodeString &src, UErrorCode &errorCode) const { - UnicodeString result; - normalize(src, result, errorCode); - return result; - } - /** - * Writes the normalized form of the source string to the destination string - * (replacing its contents) and returns the destination string. - * The source and destination strings must be different objects. - * @param src source string - * @param dest destination string; its contents is replaced with normalized src - * @param errorCode Standard 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.) - * @return dest - * @stable ICU 4.4 - */ - virtual UnicodeString & - normalize(const UnicodeString &src, - UnicodeString &dest, - UErrorCode &errorCode) const = 0; - - /** - * Normalizes a UTF-8 string and optionally records how source substrings - * relate to changed and unchanged result substrings. - * - * Implemented completely for all built-in modes except for FCD. - * The base class implementation converts to & from UTF-16 and does not support edits. - * - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. - * @param src Source UTF-8 string. - * @param sink A ByteSink to which the normalized UTF-8 result string is written. - * sink.Flush() is called at the end. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be nullptr. - * @param errorCode Standard 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.) - * @stable ICU 60 - */ - virtual void - normalizeUTF8(uint32_t options, StringPiece src, ByteSink &sink, - Edits *edits, UErrorCode &errorCode) const; - - /** - * Appends the normalized form of the second string to the first string - * (merging them at the boundary) and returns the first string. - * The result is normalized if the first string was normalized. - * The first and second strings must be different objects. - * @param first string, should be normalized - * @param second string, will be normalized - * @param errorCode Standard 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.) - * @return first - * @stable ICU 4.4 - */ - virtual UnicodeString & - normalizeSecondAndAppend(UnicodeString &first, - const UnicodeString &second, - UErrorCode &errorCode) const = 0; - /** - * Appends the second string to the first string - * (merging them at the boundary) and returns the first string. - * The result is normalized if both the strings were normalized. - * The first and second strings must be different objects. - * @param first string, should be normalized - * @param second string, should be normalized - * @param errorCode Standard 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.) - * @return first - * @stable ICU 4.4 - */ - virtual UnicodeString & - append(UnicodeString &first, - const UnicodeString &second, - UErrorCode &errorCode) const = 0; - - /** - * Gets the decomposition mapping of c. - * Roughly equivalent to normalizing the String form of c - * on a UNORM2_DECOMPOSE Normalizer2 instance, but much faster, and except that this function - * returns false and does not write a string - * if c does not have a decomposition mapping in this instance's data. - * This function is independent of the mode of the Normalizer2. - * @param c code point - * @param decomposition String object which will be set to c's - * decomposition mapping, if there is one. - * @return true if c has a decomposition, otherwise false - * @stable ICU 4.6 - */ - virtual UBool - getDecomposition(UChar32 c, UnicodeString &decomposition) const = 0; - - /** - * Gets the raw decomposition mapping of c. - * - * This is similar to the getDecomposition() method but returns the - * raw decomposition mapping as specified in UnicodeData.txt or - * (for custom data) in the mapping files processed by the gennorm2 tool. - * By contrast, getDecomposition() returns the processed, - * recursively-decomposed version of this mapping. - * - * When used on a standard NFKC Normalizer2 instance, - * getRawDecomposition() returns the Unicode Decomposition_Mapping (dm) property. - * - * When used on a standard NFC Normalizer2 instance, - * it returns the Decomposition_Mapping only if the Decomposition_Type (dt) is Canonical (Can); - * in this case, the result contains either one or two code points (=1..4 char16_ts). - * - * This function is independent of the mode of the Normalizer2. - * The default implementation returns false. - * @param c code point - * @param decomposition String object which will be set to c's - * raw decomposition mapping, if there is one. - * @return true if c has a decomposition, otherwise false - * @stable ICU 49 - */ - virtual UBool - getRawDecomposition(UChar32 c, UnicodeString &decomposition) const; - - /** - * Performs pairwise composition of a & b and returns the composite if there is one. - * - * Returns a composite code point c only if c has a two-way mapping to a+b. - * In standard Unicode normalization, this means that - * c has a canonical decomposition to a+b - * and c does not have the Full_Composition_Exclusion property. - * - * This function is independent of the mode of the Normalizer2. - * The default implementation returns a negative value. - * @param a A (normalization starter) code point. - * @param b Another code point. - * @return The non-negative composite code point if there is one; otherwise a negative value. - * @stable ICU 49 - */ - virtual UChar32 - composePair(UChar32 a, UChar32 b) const; - - /** - * Gets the combining class of c. - * The default implementation returns 0 - * but all standard implementations return the Unicode Canonical_Combining_Class value. - * @param c code point - * @return c's combining class - * @stable ICU 49 - */ - virtual uint8_t - getCombiningClass(UChar32 c) const; - - /** - * Tests if the string is normalized. - * Internally, in cases where the quickCheck() method would return "maybe" - * (which is only possible for the two COMPOSE modes) this method - * resolves to "yes" or "no" to provide a definitive result, - * at the cost of doing more work in those cases. - * @param s input string - * @param errorCode Standard 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.) - * @return true if s is normalized - * @stable ICU 4.4 - */ - virtual UBool - isNormalized(const UnicodeString &s, UErrorCode &errorCode) const = 0; - /** - * Tests if the UTF-8 string is normalized. - * Internally, in cases where the quickCheck() method would return "maybe" - * (which is only possible for the two COMPOSE modes) this method - * resolves to "yes" or "no" to provide a definitive result, - * at the cost of doing more work in those cases. - * - * This works for all normalization modes. - * It is optimized for UTF-8 for all built-in modes except for FCD. - * The base class implementation converts to UTF-16 and calls isNormalized(). - * - * @param s UTF-8 input string - * @param errorCode Standard 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.) - * @return true if s is normalized - * @stable ICU 60 - */ - virtual UBool - isNormalizedUTF8(StringPiece s, UErrorCode &errorCode) const; - - - /** - * Tests if the string is normalized. - * For the two COMPOSE modes, the result could be "maybe" in cases that - * would take a little more work to resolve definitively. - * Use spanQuickCheckYes() and normalizeSecondAndAppend() for a faster - * combination of quick check + normalization, to avoid - * re-checking the "yes" prefix. - * @param s input string - * @param errorCode Standard 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.) - * @return UNormalizationCheckResult - * @stable ICU 4.4 - */ - virtual UNormalizationCheckResult - quickCheck(const UnicodeString &s, UErrorCode &errorCode) const = 0; - - /** - * Returns the end of the normalized substring of the input string. - * In other words, with end=spanQuickCheckYes(s, ec); - * the substring UnicodeString(s, 0, end) - * will pass the quick check with a "yes" result. - * - * The returned end index is usually one or more characters before the - * "no" or "maybe" character: The end index is at a normalization boundary. - * (See the class documentation for more about normalization boundaries.) - * - * When the goal is a normalized string and most input strings are expected - * to be normalized already, then call this method, - * and if it returns a prefix shorter than the input string, - * copy that prefix and use normalizeSecondAndAppend() for the remainder. - * @param s input string - * @param errorCode Standard 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.) - * @return "yes" span end index - * @stable ICU 4.4 - */ - virtual int32_t - spanQuickCheckYes(const UnicodeString &s, UErrorCode &errorCode) const = 0; - - /** - * Tests if the character always has a normalization boundary before it, - * regardless of context. - * If true, then the character does not normalization-interact with - * preceding characters. - * In other words, a string containing this character can be normalized - * by processing portions before this character and starting from this - * character independently. - * This is used for iterative normalization. See the class documentation for details. - * @param c character to test - * @return true if c has a normalization boundary before it - * @stable ICU 4.4 - */ - virtual UBool hasBoundaryBefore(UChar32 c) const = 0; - - /** - * Tests if the character always has a normalization boundary after it, - * regardless of context. - * If true, then the character does not normalization-interact with - * following characters. - * In other words, a string containing this character can be normalized - * by processing portions up to this character and after this - * character independently. - * This is used for iterative normalization. See the class documentation for details. - * Note that this operation may be significantly slower than hasBoundaryBefore(). - * @param c character to test - * @return true if c has a normalization boundary after it - * @stable ICU 4.4 - */ - virtual UBool hasBoundaryAfter(UChar32 c) const = 0; - - /** - * Tests if the character is normalization-inert. - * If true, then the character does not change, nor normalization-interact with - * preceding or following characters. - * In other words, a string containing this character can be normalized - * by processing portions before this character and after this - * character independently. - * This is used for iterative normalization. See the class documentation for details. - * Note that this operation may be significantly slower than hasBoundaryBefore(). - * @param c character to test - * @return true if c is normalization-inert - * @stable ICU 4.4 - */ - virtual UBool isInert(UChar32 c) const = 0; -}; - -/** - * Normalization filtered by a UnicodeSet. - * Normalizes portions of the text contained in the filter set and leaves - * portions not contained in the filter set unchanged. - * Filtering is done via UnicodeSet::span(..., USET_SPAN_SIMPLE). - * Not-in-the-filter text is treated as "is normalized" and "quick check yes". - * This class implements all of (and only) the Normalizer2 API. - * An instance of this class is unmodifiable/immutable but is constructed and - * must be destructed by the owner. - * @stable ICU 4.4 - */ -class U_COMMON_API FilteredNormalizer2 : public Normalizer2 { -public: - /** - * Constructs a filtered normalizer wrapping any Normalizer2 instance - * and a filter set. - * Both are aliased and must not be modified or deleted while this object - * is used. - * The filter set should be frozen; otherwise the performance will suffer greatly. - * @param n2 wrapped Normalizer2 instance - * @param filterSet UnicodeSet which determines the characters to be normalized - * @stable ICU 4.4 - */ - FilteredNormalizer2(const Normalizer2 &n2, const UnicodeSet &filterSet) : - norm2(n2), set(filterSet) {} - - /** - * Destructor. - * @stable ICU 4.4 - */ - ~FilteredNormalizer2(); - - /** - * Writes the normalized form of the source string to the destination string - * (replacing its contents) and returns the destination string. - * The source and destination strings must be different objects. - * @param src source string - * @param dest destination string; its contents is replaced with normalized src - * @param errorCode Standard 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.) - * @return dest - * @stable ICU 4.4 - */ - virtual UnicodeString & - normalize(const UnicodeString &src, - UnicodeString &dest, - UErrorCode &errorCode) const U_OVERRIDE; - - /** - * Normalizes a UTF-8 string and optionally records how source substrings - * relate to changed and unchanged result substrings. - * - * Implemented completely for most built-in modes except for FCD. - * The base class implementation converts to & from UTF-16 and does not support edits. - * - * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. - * @param src Source UTF-8 string. - * @param sink A ByteSink to which the normalized UTF-8 result string is written. - * sink.Flush() is called at the end. - * @param edits Records edits for index mapping, working with styled text, - * and getting only changes (if any). - * The Edits contents is undefined if any error occurs. - * This function calls edits->reset() first unless - * options includes U_EDITS_NO_RESET. edits can be nullptr. - * @param errorCode Standard 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.) - * @stable ICU 60 - */ - virtual void - normalizeUTF8(uint32_t options, StringPiece src, ByteSink &sink, - Edits *edits, UErrorCode &errorCode) const U_OVERRIDE; - - /** - * Appends the normalized form of the second string to the first string - * (merging them at the boundary) and returns the first string. - * The result is normalized if the first string was normalized. - * The first and second strings must be different objects. - * @param first string, should be normalized - * @param second string, will be normalized - * @param errorCode Standard 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.) - * @return first - * @stable ICU 4.4 - */ - virtual UnicodeString & - normalizeSecondAndAppend(UnicodeString &first, - const UnicodeString &second, - UErrorCode &errorCode) const U_OVERRIDE; - /** - * Appends the second string to the first string - * (merging them at the boundary) and returns the first string. - * The result is normalized if both the strings were normalized. - * The first and second strings must be different objects. - * @param first string, should be normalized - * @param second string, should be normalized - * @param errorCode Standard 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.) - * @return first - * @stable ICU 4.4 - */ - virtual UnicodeString & - append(UnicodeString &first, - const UnicodeString &second, - UErrorCode &errorCode) const U_OVERRIDE; - - /** - * Gets the decomposition mapping of c. - * For details see the base class documentation. - * - * This function is independent of the mode of the Normalizer2. - * @param c code point - * @param decomposition String object which will be set to c's - * decomposition mapping, if there is one. - * @return true if c has a decomposition, otherwise false - * @stable ICU 4.6 - */ - virtual UBool - getDecomposition(UChar32 c, UnicodeString &decomposition) const U_OVERRIDE; - - /** - * Gets the raw decomposition mapping of c. - * For details see the base class documentation. - * - * This function is independent of the mode of the Normalizer2. - * @param c code point - * @param decomposition String object which will be set to c's - * raw decomposition mapping, if there is one. - * @return true if c has a decomposition, otherwise false - * @stable ICU 49 - */ - virtual UBool - getRawDecomposition(UChar32 c, UnicodeString &decomposition) const U_OVERRIDE; - - /** - * Performs pairwise composition of a & b and returns the composite if there is one. - * For details see the base class documentation. - * - * This function is independent of the mode of the Normalizer2. - * @param a A (normalization starter) code point. - * @param b Another code point. - * @return The non-negative composite code point if there is one; otherwise a negative value. - * @stable ICU 49 - */ - virtual UChar32 - composePair(UChar32 a, UChar32 b) const U_OVERRIDE; - - /** - * Gets the combining class of c. - * The default implementation returns 0 - * but all standard implementations return the Unicode Canonical_Combining_Class value. - * @param c code point - * @return c's combining class - * @stable ICU 49 - */ - virtual uint8_t - getCombiningClass(UChar32 c) const U_OVERRIDE; - - /** - * Tests if the string is normalized. - * For details see the Normalizer2 base class documentation. - * @param s input string - * @param errorCode Standard 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.) - * @return true if s is normalized - * @stable ICU 4.4 - */ - virtual UBool - isNormalized(const UnicodeString &s, UErrorCode &errorCode) const U_OVERRIDE; - /** - * Tests if the UTF-8 string is normalized. - * Internally, in cases where the quickCheck() method would return "maybe" - * (which is only possible for the two COMPOSE modes) this method - * resolves to "yes" or "no" to provide a definitive result, - * at the cost of doing more work in those cases. - * - * This works for all normalization modes. - * It is optimized for UTF-8 for all built-in modes except for FCD. - * The base class implementation converts to UTF-16 and calls isNormalized(). - * - * @param s UTF-8 input string - * @param errorCode Standard 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.) - * @return true if s is normalized - * @stable ICU 60 - */ - virtual UBool - isNormalizedUTF8(StringPiece s, UErrorCode &errorCode) const U_OVERRIDE; - /** - * Tests if the string is normalized. - * For details see the Normalizer2 base class documentation. - * @param s input string - * @param errorCode Standard 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.) - * @return UNormalizationCheckResult - * @stable ICU 4.4 - */ - virtual UNormalizationCheckResult - quickCheck(const UnicodeString &s, UErrorCode &errorCode) const U_OVERRIDE; - /** - * Returns the end of the normalized substring of the input string. - * For details see the Normalizer2 base class documentation. - * @param s input string - * @param errorCode Standard 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.) - * @return "yes" span end index - * @stable ICU 4.4 - */ - virtual int32_t - spanQuickCheckYes(const UnicodeString &s, UErrorCode &errorCode) const U_OVERRIDE; - - /** - * Tests if the character always has a normalization boundary before it, - * regardless of context. - * For details see the Normalizer2 base class documentation. - * @param c character to test - * @return true if c has a normalization boundary before it - * @stable ICU 4.4 - */ - virtual UBool hasBoundaryBefore(UChar32 c) const U_OVERRIDE; - - /** - * Tests if the character always has a normalization boundary after it, - * regardless of context. - * For details see the Normalizer2 base class documentation. - * @param c character to test - * @return true if c has a normalization boundary after it - * @stable ICU 4.4 - */ - virtual UBool hasBoundaryAfter(UChar32 c) const U_OVERRIDE; - - /** - * Tests if the character is normalization-inert. - * For details see the Normalizer2 base class documentation. - * @param c character to test - * @return true if c is normalization-inert - * @stable ICU 4.4 - */ - virtual UBool isInert(UChar32 c) const U_OVERRIDE; -private: - UnicodeString & - normalize(const UnicodeString &src, - UnicodeString &dest, - USetSpanCondition spanCondition, - UErrorCode &errorCode) const; - - void - normalizeUTF8(uint32_t options, const char *src, int32_t length, - ByteSink &sink, Edits *edits, - USetSpanCondition spanCondition, - UErrorCode &errorCode) const; - - UnicodeString & - normalizeSecondAndAppend(UnicodeString &first, - const UnicodeString &second, - UBool doNormalize, - UErrorCode &errorCode) const; - - const Normalizer2 &norm2; - const UnicodeSet &set; -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_NORMALIZATION - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __NORMALIZER2_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2009-2013, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: normalizer2.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2009nov22 +* created by: Markus W. Scherer +*/ + +#ifndef __NORMALIZER2_H__ +#define __NORMALIZER2_H__ + +/** + * \file + * \brief C++ API: New API for Unicode Normalization. + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#if !UCONFIG_NO_NORMALIZATION + +#include "unicode/stringpiece.h" +#include "unicode/uniset.h" +#include "unicode/unistr.h" +#include "unicode/unorm2.h" + +U_NAMESPACE_BEGIN + +class ByteSink; + +/** + * Unicode normalization functionality for standard Unicode normalization or + * for using custom mapping tables. + * All instances of this class are unmodifiable/immutable. + * Instances returned by getInstance() are singletons that must not be deleted by the caller. + * The Normalizer2 class is not intended for public subclassing. + * + * The primary functions are to produce a normalized string and to detect whether + * a string is already normalized. + * The most commonly used normalization forms are those defined in + * http://www.unicode.org/unicode/reports/tr15/ + * However, this API supports additional normalization forms for specialized purposes. + * For example, NFKC_Casefold is provided via getInstance("nfkc_cf", COMPOSE) + * and can be used in implementations of UTS #46. + * + * Not only are the standard compose and decompose modes supplied, + * but additional modes are provided as documented in the Mode enum. + * + * Some of the functions in this class identify normalization boundaries. + * At a normalization boundary, the portions of the string + * before it and starting from it do not interact and can be handled independently. + * + * The spanQuickCheckYes() stops at a normalization boundary. + * When the goal is a normalized string, then the text before the boundary + * can be copied, and the remainder can be processed with normalizeSecondAndAppend(). + * + * The hasBoundaryBefore(), hasBoundaryAfter() and isInert() functions test whether + * a character is guaranteed to be at a normalization boundary, + * regardless of context. + * This is used for moving from one normalization boundary to the next + * or preceding boundary, and for performing iterative normalization. + * + * Iterative normalization is useful when only a small portion of a + * longer string needs to be processed. + * For example, in ICU, iterative normalization is used by the NormalizationTransliterator + * (to avoid replacing already-normalized text) and ucol_nextSortKeyPart() + * (to process only the substring for which sort key bytes are computed). + * + * The set of normalization boundaries returned by these functions may not be + * complete: There may be more boundaries that could be returned. + * Different functions may return different boundaries. + * @stable ICU 4.4 + */ +class U_COMMON_API Normalizer2 : public UObject { +public: + /** + * Destructor. + * @stable ICU 4.4 + */ + ~Normalizer2(); + + /** + * Returns a Normalizer2 instance for Unicode NFC normalization. + * Same as getInstance(nullptr, "nfc", UNORM2_COMPOSE, errorCode). + * Returns an unmodifiable singleton instance. Do not delete it. + * @param errorCode Standard 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.) + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ + static const Normalizer2 * + getNFCInstance(UErrorCode &errorCode); + + /** + * Returns a Normalizer2 instance for Unicode NFD normalization. + * Same as getInstance(nullptr, "nfc", UNORM2_DECOMPOSE, errorCode). + * Returns an unmodifiable singleton instance. Do not delete it. + * @param errorCode Standard 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.) + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ + static const Normalizer2 * + getNFDInstance(UErrorCode &errorCode); + + /** + * Returns a Normalizer2 instance for Unicode NFKC normalization. + * Same as getInstance(nullptr, "nfkc", UNORM2_COMPOSE, errorCode). + * Returns an unmodifiable singleton instance. Do not delete it. + * @param errorCode Standard 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.) + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ + static const Normalizer2 * + getNFKCInstance(UErrorCode &errorCode); + + /** + * Returns a Normalizer2 instance for Unicode NFKD normalization. + * Same as getInstance(nullptr, "nfkc", UNORM2_DECOMPOSE, errorCode). + * Returns an unmodifiable singleton instance. Do not delete it. + * @param errorCode Standard 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.) + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ + static const Normalizer2 * + getNFKDInstance(UErrorCode &errorCode); + + /** + * Returns a Normalizer2 instance for Unicode NFKC_Casefold normalization. + * Same as getInstance(nullptr, "nfkc_cf", UNORM2_COMPOSE, errorCode). + * Returns an unmodifiable singleton instance. Do not delete it. + * @param errorCode Standard 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.) + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ + static const Normalizer2 * + getNFKCCasefoldInstance(UErrorCode &errorCode); + + /** + * Returns a Normalizer2 instance which uses the specified data file + * (packageName/name similar to ucnv_openPackage() and ures_open()/ResourceBundle) + * and which composes or decomposes text according to the specified mode. + * Returns an unmodifiable singleton instance. Do not delete it. + * + * Use packageName=nullptr for data files that are part of ICU's own data. + * Use name="nfc" and UNORM2_COMPOSE/UNORM2_DECOMPOSE for Unicode standard NFC/NFD. + * Use name="nfkc" and UNORM2_COMPOSE/UNORM2_DECOMPOSE for Unicode standard NFKC/NFKD. + * Use name="nfkc_cf" and UNORM2_COMPOSE for Unicode standard NFKC_CF=NFKC_Casefold. + * + * @param packageName nullptr for ICU built-in data, otherwise application data package name + * @param name "nfc" or "nfkc" or "nfkc_cf" or name of custom data file + * @param mode normalization mode (compose or decompose etc.) + * @param errorCode Standard 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.) + * @return the requested Normalizer2, if successful + * @stable ICU 4.4 + */ + static const Normalizer2 * + getInstance(const char *packageName, + const char *name, + UNormalization2Mode mode, + UErrorCode &errorCode); + + /** + * Returns the normalized form of the source string. + * @param src source string + * @param errorCode Standard 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.) + * @return normalized src + * @stable ICU 4.4 + */ + UnicodeString + normalize(const UnicodeString &src, UErrorCode &errorCode) const { + UnicodeString result; + normalize(src, result, errorCode); + return result; + } + /** + * Writes the normalized form of the source string to the destination string + * (replacing its contents) and returns the destination string. + * The source and destination strings must be different objects. + * @param src source string + * @param dest destination string; its contents is replaced with normalized src + * @param errorCode Standard 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.) + * @return dest + * @stable ICU 4.4 + */ + virtual UnicodeString & + normalize(const UnicodeString &src, + UnicodeString &dest, + UErrorCode &errorCode) const = 0; + + /** + * Normalizes a UTF-8 string and optionally records how source substrings + * relate to changed and unchanged result substrings. + * + * Implemented completely for all built-in modes except for FCD. + * The base class implementation converts to & from UTF-16 and does not support edits. + * + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. + * @param src Source UTF-8 string. + * @param sink A ByteSink to which the normalized UTF-8 result string is written. + * sink.Flush() is called at the end. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Standard 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.) + * @stable ICU 60 + */ + virtual void + normalizeUTF8(uint32_t options, StringPiece src, ByteSink &sink, + Edits *edits, UErrorCode &errorCode) const; + + /** + * Appends the normalized form of the second string to the first string + * (merging them at the boundary) and returns the first string. + * The result is normalized if the first string was normalized. + * The first and second strings must be different objects. + * @param first string, should be normalized + * @param second string, will be normalized + * @param errorCode Standard 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.) + * @return first + * @stable ICU 4.4 + */ + virtual UnicodeString & + normalizeSecondAndAppend(UnicodeString &first, + const UnicodeString &second, + UErrorCode &errorCode) const = 0; + /** + * Appends the second string to the first string + * (merging them at the boundary) and returns the first string. + * The result is normalized if both the strings were normalized. + * The first and second strings must be different objects. + * @param first string, should be normalized + * @param second string, should be normalized + * @param errorCode Standard 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.) + * @return first + * @stable ICU 4.4 + */ + virtual UnicodeString & + append(UnicodeString &first, + const UnicodeString &second, + UErrorCode &errorCode) const = 0; + + /** + * Gets the decomposition mapping of c. + * Roughly equivalent to normalizing the String form of c + * on a UNORM2_DECOMPOSE Normalizer2 instance, but much faster, and except that this function + * returns false and does not write a string + * if c does not have a decomposition mapping in this instance's data. + * This function is independent of the mode of the Normalizer2. + * @param c code point + * @param decomposition String object which will be set to c's + * decomposition mapping, if there is one. + * @return true if c has a decomposition, otherwise false + * @stable ICU 4.6 + */ + virtual UBool + getDecomposition(UChar32 c, UnicodeString &decomposition) const = 0; + + /** + * Gets the raw decomposition mapping of c. + * + * This is similar to the getDecomposition() method but returns the + * raw decomposition mapping as specified in UnicodeData.txt or + * (for custom data) in the mapping files processed by the gennorm2 tool. + * By contrast, getDecomposition() returns the processed, + * recursively-decomposed version of this mapping. + * + * When used on a standard NFKC Normalizer2 instance, + * getRawDecomposition() returns the Unicode Decomposition_Mapping (dm) property. + * + * When used on a standard NFC Normalizer2 instance, + * it returns the Decomposition_Mapping only if the Decomposition_Type (dt) is Canonical (Can); + * in this case, the result contains either one or two code points (=1..4 char16_ts). + * + * This function is independent of the mode of the Normalizer2. + * The default implementation returns false. + * @param c code point + * @param decomposition String object which will be set to c's + * raw decomposition mapping, if there is one. + * @return true if c has a decomposition, otherwise false + * @stable ICU 49 + */ + virtual UBool + getRawDecomposition(UChar32 c, UnicodeString &decomposition) const; + + /** + * Performs pairwise composition of a & b and returns the composite if there is one. + * + * Returns a composite code point c only if c has a two-way mapping to a+b. + * In standard Unicode normalization, this means that + * c has a canonical decomposition to a+b + * and c does not have the Full_Composition_Exclusion property. + * + * This function is independent of the mode of the Normalizer2. + * The default implementation returns a negative value. + * @param a A (normalization starter) code point. + * @param b Another code point. + * @return The non-negative composite code point if there is one; otherwise a negative value. + * @stable ICU 49 + */ + virtual UChar32 + composePair(UChar32 a, UChar32 b) const; + + /** + * Gets the combining class of c. + * The default implementation returns 0 + * but all standard implementations return the Unicode Canonical_Combining_Class value. + * @param c code point + * @return c's combining class + * @stable ICU 49 + */ + virtual uint8_t + getCombiningClass(UChar32 c) const; + + /** + * Tests if the string is normalized. + * Internally, in cases where the quickCheck() method would return "maybe" + * (which is only possible for the two COMPOSE modes) this method + * resolves to "yes" or "no" to provide a definitive result, + * at the cost of doing more work in those cases. + * @param s input string + * @param errorCode Standard 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.) + * @return true if s is normalized + * @stable ICU 4.4 + */ + virtual UBool + isNormalized(const UnicodeString &s, UErrorCode &errorCode) const = 0; + /** + * Tests if the UTF-8 string is normalized. + * Internally, in cases where the quickCheck() method would return "maybe" + * (which is only possible for the two COMPOSE modes) this method + * resolves to "yes" or "no" to provide a definitive result, + * at the cost of doing more work in those cases. + * + * This works for all normalization modes. + * It is optimized for UTF-8 for all built-in modes except for FCD. + * The base class implementation converts to UTF-16 and calls isNormalized(). + * + * @param s UTF-8 input string + * @param errorCode Standard 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.) + * @return true if s is normalized + * @stable ICU 60 + */ + virtual UBool + isNormalizedUTF8(StringPiece s, UErrorCode &errorCode) const; + + + /** + * Tests if the string is normalized. + * For the two COMPOSE modes, the result could be "maybe" in cases that + * would take a little more work to resolve definitively. + * Use spanQuickCheckYes() and normalizeSecondAndAppend() for a faster + * combination of quick check + normalization, to avoid + * re-checking the "yes" prefix. + * @param s input string + * @param errorCode Standard 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.) + * @return UNormalizationCheckResult + * @stable ICU 4.4 + */ + virtual UNormalizationCheckResult + quickCheck(const UnicodeString &s, UErrorCode &errorCode) const = 0; + + /** + * Returns the end of the normalized substring of the input string. + * In other words, with end=spanQuickCheckYes(s, ec); + * the substring UnicodeString(s, 0, end) + * will pass the quick check with a "yes" result. + * + * The returned end index is usually one or more characters before the + * "no" or "maybe" character: The end index is at a normalization boundary. + * (See the class documentation for more about normalization boundaries.) + * + * When the goal is a normalized string and most input strings are expected + * to be normalized already, then call this method, + * and if it returns a prefix shorter than the input string, + * copy that prefix and use normalizeSecondAndAppend() for the remainder. + * @param s input string + * @param errorCode Standard 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.) + * @return "yes" span end index + * @stable ICU 4.4 + */ + virtual int32_t + spanQuickCheckYes(const UnicodeString &s, UErrorCode &errorCode) const = 0; + + /** + * Tests if the character always has a normalization boundary before it, + * regardless of context. + * If true, then the character does not normalization-interact with + * preceding characters. + * In other words, a string containing this character can be normalized + * by processing portions before this character and starting from this + * character independently. + * This is used for iterative normalization. See the class documentation for details. + * @param c character to test + * @return true if c has a normalization boundary before it + * @stable ICU 4.4 + */ + virtual UBool hasBoundaryBefore(UChar32 c) const = 0; + + /** + * Tests if the character always has a normalization boundary after it, + * regardless of context. + * If true, then the character does not normalization-interact with + * following characters. + * In other words, a string containing this character can be normalized + * by processing portions up to this character and after this + * character independently. + * This is used for iterative normalization. See the class documentation for details. + * Note that this operation may be significantly slower than hasBoundaryBefore(). + * @param c character to test + * @return true if c has a normalization boundary after it + * @stable ICU 4.4 + */ + virtual UBool hasBoundaryAfter(UChar32 c) const = 0; + + /** + * Tests if the character is normalization-inert. + * If true, then the character does not change, nor normalization-interact with + * preceding or following characters. + * In other words, a string containing this character can be normalized + * by processing portions before this character and after this + * character independently. + * This is used for iterative normalization. See the class documentation for details. + * Note that this operation may be significantly slower than hasBoundaryBefore(). + * @param c character to test + * @return true if c is normalization-inert + * @stable ICU 4.4 + */ + virtual UBool isInert(UChar32 c) const = 0; +}; + +/** + * Normalization filtered by a UnicodeSet. + * Normalizes portions of the text contained in the filter set and leaves + * portions not contained in the filter set unchanged. + * Filtering is done via UnicodeSet::span(..., USET_SPAN_SIMPLE). + * Not-in-the-filter text is treated as "is normalized" and "quick check yes". + * This class implements all of (and only) the Normalizer2 API. + * An instance of this class is unmodifiable/immutable but is constructed and + * must be destructed by the owner. + * @stable ICU 4.4 + */ +class U_COMMON_API FilteredNormalizer2 : public Normalizer2 { +public: + /** + * Constructs a filtered normalizer wrapping any Normalizer2 instance + * and a filter set. + * Both are aliased and must not be modified or deleted while this object + * is used. + * The filter set should be frozen; otherwise the performance will suffer greatly. + * @param n2 wrapped Normalizer2 instance + * @param filterSet UnicodeSet which determines the characters to be normalized + * @stable ICU 4.4 + */ + FilteredNormalizer2(const Normalizer2 &n2, const UnicodeSet &filterSet) : + norm2(n2), set(filterSet) {} + + /** + * Destructor. + * @stable ICU 4.4 + */ + ~FilteredNormalizer2(); + + /** + * Writes the normalized form of the source string to the destination string + * (replacing its contents) and returns the destination string. + * The source and destination strings must be different objects. + * @param src source string + * @param dest destination string; its contents is replaced with normalized src + * @param errorCode Standard 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.) + * @return dest + * @stable ICU 4.4 + */ + virtual UnicodeString & + normalize(const UnicodeString &src, + UnicodeString &dest, + UErrorCode &errorCode) const override; + + /** + * Normalizes a UTF-8 string and optionally records how source substrings + * relate to changed and unchanged result substrings. + * + * Implemented completely for most built-in modes except for FCD. + * The base class implementation converts to & from UTF-16 and does not support edits. + * + * @param options Options bit set, usually 0. See U_OMIT_UNCHANGED_TEXT and U_EDITS_NO_RESET. + * @param src Source UTF-8 string. + * @param sink A ByteSink to which the normalized UTF-8 result string is written. + * sink.Flush() is called at the end. + * @param edits Records edits for index mapping, working with styled text, + * and getting only changes (if any). + * The Edits contents is undefined if any error occurs. + * This function calls edits->reset() first unless + * options includes U_EDITS_NO_RESET. edits can be nullptr. + * @param errorCode Standard 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.) + * @stable ICU 60 + */ + virtual void + normalizeUTF8(uint32_t options, StringPiece src, ByteSink &sink, + Edits *edits, UErrorCode &errorCode) const override; + + /** + * Appends the normalized form of the second string to the first string + * (merging them at the boundary) and returns the first string. + * The result is normalized if the first string was normalized. + * The first and second strings must be different objects. + * @param first string, should be normalized + * @param second string, will be normalized + * @param errorCode Standard 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.) + * @return first + * @stable ICU 4.4 + */ + virtual UnicodeString & + normalizeSecondAndAppend(UnicodeString &first, + const UnicodeString &second, + UErrorCode &errorCode) const override; + /** + * Appends the second string to the first string + * (merging them at the boundary) and returns the first string. + * The result is normalized if both the strings were normalized. + * The first and second strings must be different objects. + * @param first string, should be normalized + * @param second string, should be normalized + * @param errorCode Standard 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.) + * @return first + * @stable ICU 4.4 + */ + virtual UnicodeString & + append(UnicodeString &first, + const UnicodeString &second, + UErrorCode &errorCode) const override; + + /** + * Gets the decomposition mapping of c. + * For details see the base class documentation. + * + * This function is independent of the mode of the Normalizer2. + * @param c code point + * @param decomposition String object which will be set to c's + * decomposition mapping, if there is one. + * @return true if c has a decomposition, otherwise false + * @stable ICU 4.6 + */ + virtual UBool + getDecomposition(UChar32 c, UnicodeString &decomposition) const override; + + /** + * Gets the raw decomposition mapping of c. + * For details see the base class documentation. + * + * This function is independent of the mode of the Normalizer2. + * @param c code point + * @param decomposition String object which will be set to c's + * raw decomposition mapping, if there is one. + * @return true if c has a decomposition, otherwise false + * @stable ICU 49 + */ + virtual UBool + getRawDecomposition(UChar32 c, UnicodeString &decomposition) const override; + + /** + * Performs pairwise composition of a & b and returns the composite if there is one. + * For details see the base class documentation. + * + * This function is independent of the mode of the Normalizer2. + * @param a A (normalization starter) code point. + * @param b Another code point. + * @return The non-negative composite code point if there is one; otherwise a negative value. + * @stable ICU 49 + */ + virtual UChar32 + composePair(UChar32 a, UChar32 b) const override; + + /** + * Gets the combining class of c. + * The default implementation returns 0 + * but all standard implementations return the Unicode Canonical_Combining_Class value. + * @param c code point + * @return c's combining class + * @stable ICU 49 + */ + virtual uint8_t + getCombiningClass(UChar32 c) const override; + + /** + * Tests if the string is normalized. + * For details see the Normalizer2 base class documentation. + * @param s input string + * @param errorCode Standard 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.) + * @return true if s is normalized + * @stable ICU 4.4 + */ + virtual UBool + isNormalized(const UnicodeString &s, UErrorCode &errorCode) const override; + /** + * Tests if the UTF-8 string is normalized. + * Internally, in cases where the quickCheck() method would return "maybe" + * (which is only possible for the two COMPOSE modes) this method + * resolves to "yes" or "no" to provide a definitive result, + * at the cost of doing more work in those cases. + * + * This works for all normalization modes. + * It is optimized for UTF-8 for all built-in modes except for FCD. + * The base class implementation converts to UTF-16 and calls isNormalized(). + * + * @param s UTF-8 input string + * @param errorCode Standard 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.) + * @return true if s is normalized + * @stable ICU 60 + */ + virtual UBool + isNormalizedUTF8(StringPiece s, UErrorCode &errorCode) const override; + /** + * Tests if the string is normalized. + * For details see the Normalizer2 base class documentation. + * @param s input string + * @param errorCode Standard 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.) + * @return UNormalizationCheckResult + * @stable ICU 4.4 + */ + virtual UNormalizationCheckResult + quickCheck(const UnicodeString &s, UErrorCode &errorCode) const override; + /** + * Returns the end of the normalized substring of the input string. + * For details see the Normalizer2 base class documentation. + * @param s input string + * @param errorCode Standard 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.) + * @return "yes" span end index + * @stable ICU 4.4 + */ + virtual int32_t + spanQuickCheckYes(const UnicodeString &s, UErrorCode &errorCode) const override; + + /** + * Tests if the character always has a normalization boundary before it, + * regardless of context. + * For details see the Normalizer2 base class documentation. + * @param c character to test + * @return true if c has a normalization boundary before it + * @stable ICU 4.4 + */ + virtual UBool hasBoundaryBefore(UChar32 c) const override; + + /** + * Tests if the character always has a normalization boundary after it, + * regardless of context. + * For details see the Normalizer2 base class documentation. + * @param c character to test + * @return true if c has a normalization boundary after it + * @stable ICU 4.4 + */ + virtual UBool hasBoundaryAfter(UChar32 c) const override; + + /** + * Tests if the character is normalization-inert. + * For details see the Normalizer2 base class documentation. + * @param c character to test + * @return true if c is normalization-inert + * @stable ICU 4.4 + */ + virtual UBool isInert(UChar32 c) const override; +private: + UnicodeString & + normalize(const UnicodeString &src, + UnicodeString &dest, + USetSpanCondition spanCondition, + UErrorCode &errorCode) const; + + void + normalizeUTF8(uint32_t options, const char *src, int32_t length, + ByteSink &sink, Edits *edits, + USetSpanCondition spanCondition, + UErrorCode &errorCode) const; + + UnicodeString & + normalizeSecondAndAppend(UnicodeString &first, + const UnicodeString &second, + UBool doNormalize, + UErrorCode &errorCode) const; + + const Normalizer2 &norm2; + const UnicodeSet &set; +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_NORMALIZATION + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __NORMALIZER2_H__ diff --git a/deps/icu-small/source/common/unicode/normlzr.h b/deps/icu-small/source/common/unicode/normlzr.h index 14b246988502da..f699d78f81a5f2 100644 --- a/deps/icu-small/source/common/unicode/normlzr.h +++ b/deps/icu-small/source/common/unicode/normlzr.h @@ -1,816 +1,816 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ******************************************************************** - * COPYRIGHT: - * Copyright (c) 1996-2015, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************** - */ - -#ifndef NORMLZR_H -#define NORMLZR_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -/** - * \file - * \brief C++ API: Unicode Normalization - */ - -#if !UCONFIG_NO_NORMALIZATION - -#include "unicode/chariter.h" -#include "unicode/normalizer2.h" -#include "unicode/unistr.h" -#include "unicode/unorm.h" -#include "unicode/uobject.h" - -U_NAMESPACE_BEGIN -/** - * Old Unicode normalization API. - * - * This API has been replaced by the Normalizer2 class and is only available - * for backward compatibility. This class simply delegates to the Normalizer2 class. - * There is one exception: The new API does not provide a replacement for Normalizer::compare(). - * - * The Normalizer class supports the standard normalization forms described in - * - * Unicode Standard Annex #15: Unicode Normalization Forms. - * - * The Normalizer class consists of two parts: - * - static functions that normalize strings or test if strings are normalized - * - a Normalizer object is an iterator that takes any kind of text and - * provides iteration over its normalized form - * - * The Normalizer class is not suitable for subclassing. - * - * For basic information about normalization forms and details about the C API - * please see the documentation in unorm.h. - * - * The iterator API with the Normalizer constructors and the non-static functions - * use a CharacterIterator as input. It is possible to pass a string which - * is then internally wrapped in a CharacterIterator. - * The input text is not normalized all at once, but incrementally where needed - * (providing efficient random access). - * This allows to pass in a large text but spend only a small amount of time - * normalizing a small part of that text. - * However, if the entire text is normalized, then the iterator will be - * slower than normalizing the entire text at once and iterating over the result. - * A possible use of the Normalizer iterator is also to report an index into the - * original text that is close to where the normalized characters come from. - * - * Important: The iterator API was cleaned up significantly for ICU 2.0. - * The earlier implementation reported the getIndex() inconsistently, - * and previous() could not be used after setIndex(), next(), first(), and current(). - * - * Normalizer allows to start normalizing from anywhere in the input text by - * calling setIndexOnly(), first(), or last(). - * Without calling any of these, the iterator will start at the beginning of the text. - * - * At any time, next() returns the next normalized code point (UChar32), - * with post-increment semantics (like CharacterIterator::next32PostInc()). - * previous() returns the previous normalized code point (UChar32), - * with pre-decrement semantics (like CharacterIterator::previous32()). - * - * current() returns the current code point - * (respectively the one at the newly set index) without moving - * the getIndex(). Note that if the text at the current position - * needs to be normalized, then these functions will do that. - * (This is why current() is not const.) - * It is more efficient to call setIndexOnly() instead, which does not - * normalize. - * - * getIndex() always refers to the position in the input text where the normalized - * code points are returned from. It does not always change with each returned - * code point. - * The code point that is returned from any of the functions - * corresponds to text at or after getIndex(), according to the - * function's iteration semantics (post-increment or pre-decrement). - * - * next() returns a code point from at or after the getIndex() - * from before the next() call. After the next() call, the getIndex() - * might have moved to where the next code point will be returned from - * (from a next() or current() call). - * This is semantically equivalent to array access with array[index++] - * (post-increment semantics). - * - * previous() returns a code point from at or after the getIndex() - * from after the previous() call. - * This is semantically equivalent to array access with array[--index] - * (pre-decrement semantics). - * - * Internally, the Normalizer iterator normalizes a small piece of text - * starting at the getIndex() and ending at a following "safe" index. - * The normalized results is stored in an internal string buffer, and - * the code points are iterated from there. - * With multiple iteration calls, this is repeated until the next piece - * of text needs to be normalized, and the getIndex() needs to be moved. - * - * The following "safe" index, the internal buffer, and the secondary - * iteration index into that buffer are not exposed on the API. - * This also means that it is currently not practical to return to - * a particular, arbitrary position in the text because one would need to - * know, and be able to set, in addition to the getIndex(), at least also the - * current index into the internal buffer. - * It is currently only possible to observe when getIndex() changes - * (with careful consideration of the iteration semantics), - * at which time the internal index will be 0. - * For example, if getIndex() is different after next() than before it, - * then the internal index is 0 and one can return to this getIndex() - * later with setIndexOnly(). - * - * Note: While the setIndex() and getIndex() refer to indices in the - * underlying Unicode input text, the next() and previous() methods - * iterate through characters in the normalized output. - * This means that there is not necessarily a one-to-one correspondence - * between characters returned by next() and previous() and the indices - * passed to and returned from setIndex() and getIndex(). - * It is for this reason that Normalizer does not implement the CharacterIterator interface. - * - * @author Laura Werner, Mark Davis, Markus Scherer - * @stable ICU 2.0 - */ -class U_COMMON_API Normalizer : public UObject { -public: -#ifndef U_HIDE_DEPRECATED_API - /** - * If DONE is returned from an iteration function that returns a code point, - * then there are no more normalization results available. - * @deprecated ICU 56 Use Normalizer2 instead. - */ - enum { - DONE=0xffff - }; - - // Constructors - - /** - * Creates a new Normalizer object for iterating over the - * normalized form of a given string. - *

- * @param str The string to be normalized. The normalization - * will start at the beginning of the string. - * - * @param mode The normalization mode. - * @deprecated ICU 56 Use Normalizer2 instead. - */ - Normalizer(const UnicodeString& str, UNormalizationMode mode); - - /** - * Creates a new Normalizer object for iterating over the - * normalized form of a given string. - *

- * @param str The string to be normalized. The normalization - * will start at the beginning of the string. - * - * @param length Length of the string, or -1 if NUL-terminated. - * @param mode The normalization mode. - * @deprecated ICU 56 Use Normalizer2 instead. - */ - Normalizer(ConstChar16Ptr str, int32_t length, UNormalizationMode mode); - - /** - * Creates a new Normalizer object for iterating over the - * normalized form of the given text. - *

- * @param iter The input text to be normalized. The normalization - * will start at the beginning of the string. - * - * @param mode The normalization mode. - * @deprecated ICU 56 Use Normalizer2 instead. - */ - Normalizer(const CharacterIterator& iter, UNormalizationMode mode); -#endif /* U_HIDE_DEPRECATED_API */ - -#ifndef U_FORCE_HIDE_DEPRECATED_API - /** - * Copy constructor. - * @param copy The object to be copied. - * @deprecated ICU 56 Use Normalizer2 instead. - */ - Normalizer(const Normalizer& copy); - - /** - * Destructor - * @deprecated ICU 56 Use Normalizer2 instead. - */ - virtual ~Normalizer(); -#endif // U_FORCE_HIDE_DEPRECATED_API - - //------------------------------------------------------------------------- - // Static utility methods - //------------------------------------------------------------------------- - -#ifndef U_HIDE_DEPRECATED_API - /** - * Normalizes a UnicodeString according to the specified normalization mode. - * This is a wrapper for unorm_normalize(), using UnicodeString's. - * - * The options parameter specifies which optional - * Normalizer features are to be enabled for this operation. - * - * @param source the input string to be normalized. - * @param mode the normalization mode - * @param options the optional features to be enabled (0 for no options) - * @param result The normalized string (on output). - * @param status The error code. - * @deprecated ICU 56 Use Normalizer2 instead. - */ - static void U_EXPORT2 normalize(const UnicodeString& source, - UNormalizationMode mode, int32_t options, - UnicodeString& result, - UErrorCode &status); - - /** - * Compose a UnicodeString. - * This is equivalent to normalize() with mode UNORM_NFC or UNORM_NFKC. - * This is a wrapper for unorm_normalize(), using UnicodeString's. - * - * The options parameter specifies which optional - * Normalizer features are to be enabled for this operation. - * - * @param source the string to be composed. - * @param compat Perform compatibility decomposition before composition. - * If this argument is false, only canonical - * decomposition will be performed. - * @param options the optional features to be enabled (0 for no options) - * @param result The composed string (on output). - * @param status The error code. - * @deprecated ICU 56 Use Normalizer2 instead. - */ - static void U_EXPORT2 compose(const UnicodeString& source, - UBool compat, int32_t options, - UnicodeString& result, - UErrorCode &status); - - /** - * Static method to decompose a UnicodeString. - * This is equivalent to normalize() with mode UNORM_NFD or UNORM_NFKD. - * This is a wrapper for unorm_normalize(), using UnicodeString's. - * - * The options parameter specifies which optional - * Normalizer features are to be enabled for this operation. - * - * @param source the string to be decomposed. - * @param compat Perform compatibility decomposition. - * If this argument is false, only canonical - * decomposition will be performed. - * @param options the optional features to be enabled (0 for no options) - * @param result The decomposed string (on output). - * @param status The error code. - * @deprecated ICU 56 Use Normalizer2 instead. - */ - static void U_EXPORT2 decompose(const UnicodeString& source, - UBool compat, int32_t options, - UnicodeString& result, - UErrorCode &status); - - /** - * Performing quick check on a string, to quickly determine if the string is - * in a particular normalization format. - * This is a wrapper for unorm_quickCheck(), using a UnicodeString. - * - * Three types of result can be returned UNORM_YES, UNORM_NO or - * UNORM_MAYBE. Result UNORM_YES indicates that the argument - * string is in the desired normalized format, UNORM_NO determines that - * argument string is not in the desired normalized format. A - * UNORM_MAYBE result indicates that a more thorough check is required, - * the user may have to put the string in its normalized form and compare the - * results. - * @param source string for determining if it is in a normalized format - * @param mode normalization format - * @param status A reference to a UErrorCode to receive any errors - * @return UNORM_YES, UNORM_NO or UNORM_MAYBE - * - * @see isNormalized - * @deprecated ICU 56 Use Normalizer2 instead. - */ - static inline UNormalizationCheckResult - quickCheck(const UnicodeString &source, UNormalizationMode mode, UErrorCode &status); - - /** - * Performing quick check on a string; same as the other version of quickCheck - * but takes an extra options parameter like most normalization functions. - * - * @param source string for determining if it is in a normalized format - * @param mode normalization format - * @param options the optional features to be enabled (0 for no options) - * @param status A reference to a UErrorCode to receive any errors - * @return UNORM_YES, UNORM_NO or UNORM_MAYBE - * - * @see isNormalized - * @deprecated ICU 56 Use Normalizer2 instead. - */ - static UNormalizationCheckResult - quickCheck(const UnicodeString &source, UNormalizationMode mode, int32_t options, UErrorCode &status); - - /** - * Test if a string is in a given normalization form. - * This is semantically equivalent to source.equals(normalize(source, mode)) . - * - * Unlike unorm_quickCheck(), this function returns a definitive result, - * never a "maybe". - * For NFD, NFKD, and FCD, both functions work exactly the same. - * For NFC and NFKC where quickCheck may return "maybe", this function will - * perform further tests to arrive at a true/false result. - * - * @param src String that is to be tested if it is in a normalization format. - * @param mode Which normalization form to test for. - * @param errorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return Boolean value indicating whether the source string is in the - * "mode" normalization form. - * - * @see quickCheck - * @deprecated ICU 56 Use Normalizer2 instead. - */ - static inline UBool - isNormalized(const UnicodeString &src, UNormalizationMode mode, UErrorCode &errorCode); - - /** - * Test if a string is in a given normalization form; same as the other version of isNormalized - * but takes an extra options parameter like most normalization functions. - * - * @param src String that is to be tested if it is in a normalization format. - * @param mode Which normalization form to test for. - * @param options the optional features to be enabled (0 for no options) - * @param errorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return Boolean value indicating whether the source string is in the - * "mode" normalization form. - * - * @see quickCheck - * @deprecated ICU 56 Use Normalizer2 instead. - */ - static UBool - isNormalized(const UnicodeString &src, UNormalizationMode mode, int32_t options, UErrorCode &errorCode); - - /** - * Concatenate normalized strings, making sure that the result is normalized as well. - * - * If both the left and the right strings are in - * the normalization form according to "mode/options", - * then the result will be - * - * \code - * dest=normalize(left+right, mode, options) - * \endcode - * - * For details see unorm_concatenate in unorm.h. - * - * @param left Left source string. - * @param right Right source string. - * @param result The output string. - * @param mode The normalization mode. - * @param options A bit set of normalization options. - * @param errorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return result - * - * @see unorm_concatenate - * @see normalize - * @see unorm_next - * @see unorm_previous - * - * @deprecated ICU 56 Use Normalizer2 instead. - */ - static UnicodeString & - U_EXPORT2 concatenate(const UnicodeString &left, const UnicodeString &right, - UnicodeString &result, - UNormalizationMode mode, int32_t options, - UErrorCode &errorCode); -#endif /* U_HIDE_DEPRECATED_API */ - - /** - * Compare two strings for canonical equivalence. - * Further options include case-insensitive comparison and - * code point order (as opposed to code unit order). - * - * Canonical equivalence between two strings is defined as their normalized - * forms (NFD or NFC) being identical. - * This function compares strings incrementally instead of normalizing - * (and optionally case-folding) both strings entirely, - * improving performance significantly. - * - * Bulk normalization is only necessary if the strings do not fulfill the FCD - * conditions. Only in this case, and only if the strings are relatively long, - * is memory allocated temporarily. - * For FCD strings and short non-FCD strings there is no memory allocation. - * - * Semantically, this is equivalent to - * strcmp[CodePointOrder](NFD(foldCase(s1)), NFD(foldCase(s2))) - * where code point order and foldCase are all optional. - * - * UAX 21 2.5 Caseless Matching specifies that for a canonical caseless match - * the case folding must be performed first, then the normalization. - * - * @param s1 First source string. - * @param s2 Second source string. - * - * @param options A bit set of options: - * - U_FOLD_CASE_DEFAULT or 0 is used for default options: - * Case-sensitive comparison in code unit order, and the input strings - * are quick-checked for FCD. - * - * - UNORM_INPUT_IS_FCD - * Set if the caller knows that both s1 and s2 fulfill the FCD conditions. - * If not set, the function will quickCheck for FCD - * and normalize if necessary. - * - * - U_COMPARE_CODE_POINT_ORDER - * Set to choose code point order instead of code unit order - * (see u_strCompare for details). - * - * - U_COMPARE_IGNORE_CASE - * Set to compare strings case-insensitively using case folding, - * instead of case-sensitively. - * If set, then the following case folding options are used. - * - * - Options as used with case-insensitive comparisons, currently: - * - * - U_FOLD_CASE_EXCLUDE_SPECIAL_I - * (see u_strCaseCompare for details) - * - * - regular normalization options shifted left by UNORM_COMPARE_NORM_OPTIONS_SHIFT - * - * @param errorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return <0 or 0 or >0 as usual for string comparisons - * - * @see unorm_compare - * @see normalize - * @see UNORM_FCD - * @see u_strCompare - * @see u_strCaseCompare - * - * @stable ICU 2.2 - */ - static inline int32_t - compare(const UnicodeString &s1, const UnicodeString &s2, - uint32_t options, - UErrorCode &errorCode); - -#ifndef U_HIDE_DEPRECATED_API - //------------------------------------------------------------------------- - // Iteration API - //------------------------------------------------------------------------- - - /** - * Return the current character in the normalized text. - * current() may need to normalize some text at getIndex(). - * The getIndex() is not changed. - * - * @return the current normalized code point - * @deprecated ICU 56 Use Normalizer2 instead. - */ - UChar32 current(void); - - /** - * Return the first character in the normalized text. - * This is equivalent to setIndexOnly(startIndex()) followed by next(). - * (Post-increment semantics.) - * - * @return the first normalized code point - * @deprecated ICU 56 Use Normalizer2 instead. - */ - UChar32 first(void); - - /** - * Return the last character in the normalized text. - * This is equivalent to setIndexOnly(endIndex()) followed by previous(). - * (Pre-decrement semantics.) - * - * @return the last normalized code point - * @deprecated ICU 56 Use Normalizer2 instead. - */ - UChar32 last(void); - - /** - * Return the next character in the normalized text. - * (Post-increment semantics.) - * If the end of the text has already been reached, DONE is returned. - * The DONE value could be confused with a U+FFFF non-character code point - * in the text. If this is possible, you can test getIndex()startIndex() || first()!=DONE). (Calling first() will change - * the iterator state!) - * - * The C API unorm_previous() is more efficient and does not have this ambiguity. - * - * @return the previous normalized code point - * @deprecated ICU 56 Use Normalizer2 instead. - */ - UChar32 previous(void); - - /** - * Set the iteration position in the input text that is being normalized, - * without any immediate normalization. - * After setIndexOnly(), getIndex() will return the same index that is - * specified here. - * - * @param index the desired index in the input text. - * @deprecated ICU 56 Use Normalizer2 instead. - */ - void setIndexOnly(int32_t index); - - /** - * Reset the index to the beginning of the text. - * This is equivalent to setIndexOnly(startIndex)). - * @deprecated ICU 56 Use Normalizer2 instead. - */ - void reset(void); - - /** - * Retrieve the current iteration position in the input text that is - * being normalized. - * - * A following call to next() will return a normalized code point from - * the input text at or after this index. - * - * After a call to previous(), getIndex() will point at or before the - * position in the input text where the normalized code point - * was returned from with previous(). - * - * @return the current index in the input text - * @deprecated ICU 56 Use Normalizer2 instead. - */ - int32_t getIndex(void) const; - - /** - * Retrieve the index of the start of the input text. This is the begin index - * of the CharacterIterator or the start (i.e. index 0) of the string - * over which this Normalizer is iterating. - * - * @return the smallest index in the input text where the Normalizer operates - * @deprecated ICU 56 Use Normalizer2 instead. - */ - int32_t startIndex(void) const; - - /** - * Retrieve the index of the end of the input text. This is the end index - * of the CharacterIterator or the length of the string - * over which this Normalizer is iterating. - * This end index is exclusive, i.e., the Normalizer operates only on characters - * before this index. - * - * @return the first index in the input text where the Normalizer does not operate - * @deprecated ICU 56 Use Normalizer2 instead. - */ - int32_t endIndex(void) const; - - /** - * Returns true when both iterators refer to the same character in the same - * input text. - * - * @param that a Normalizer object to compare this one to - * @return comparison result - * @deprecated ICU 56 Use Normalizer2 instead. - */ - bool operator==(const Normalizer& that) const; - - /** - * Returns false when both iterators refer to the same character in the same - * input text. - * - * @param that a Normalizer object to compare this one to - * @return comparison result - * @deprecated ICU 56 Use Normalizer2 instead. - */ - inline bool operator!=(const Normalizer& that) const; - - /** - * Returns a pointer to a new Normalizer that is a clone of this one. - * The caller is responsible for deleting the new clone. - * @return a pointer to a new Normalizer - * @deprecated ICU 56 Use Normalizer2 instead. - */ - Normalizer* clone() const; - - /** - * Generates a hash code for this iterator. - * - * @return the hash code - * @deprecated ICU 56 Use Normalizer2 instead. - */ - int32_t hashCode(void) const; - - //------------------------------------------------------------------------- - // Property access methods - //------------------------------------------------------------------------- - - /** - * Set the normalization mode for this object. - *

- * Note:If the normalization mode is changed while iterating - * over a string, calls to {@link #next() } and {@link #previous() } may - * return previously buffers characters in the old normalization mode - * until the iteration is able to re-sync at the next base character. - * It is safest to call {@link #setIndexOnly }, {@link #reset() }, - * {@link #setText }, {@link #first() }, - * {@link #last() }, etc. after calling setMode. - *

- * @param newMode the new mode for this Normalizer. - * @see #getUMode - * @deprecated ICU 56 Use Normalizer2 instead. - */ - void setMode(UNormalizationMode newMode); - - /** - * Return the normalization mode for this object. - * - * This is an unusual name because there used to be a getMode() that - * returned a different type. - * - * @return the mode for this Normalizer - * @see #setMode - * @deprecated ICU 56 Use Normalizer2 instead. - */ - UNormalizationMode getUMode(void) const; - - /** - * Set options that affect this Normalizer's operation. - * Options do not change the basic composition or decomposition operation - * that is being performed, but they control whether - * certain optional portions of the operation are done. - * Currently the only available option is obsolete. - * - * It is possible to specify multiple options that are all turned on or off. - * - * @param option the option(s) whose value is/are to be set. - * @param value the new setting for the option. Use true to - * turn the option(s) on and false to turn it/them off. - * - * @see #getOption - * @deprecated ICU 56 Use Normalizer2 instead. - */ - void setOption(int32_t option, - UBool value); - - /** - * Determine whether an option is turned on or off. - * If multiple options are specified, then the result is true if any - * of them are set. - *

- * @param option the option(s) that are to be checked - * @return true if any of the option(s) are set - * @see #setOption - * @deprecated ICU 56 Use Normalizer2 instead. - */ - UBool getOption(int32_t option) const; - - /** - * Set the input text over which this Normalizer will iterate. - * The iteration position is set to the beginning. - * - * @param newText a string that replaces the current input text - * @param status a UErrorCode - * @deprecated ICU 56 Use Normalizer2 instead. - */ - void setText(const UnicodeString& newText, - UErrorCode &status); - - /** - * Set the input text over which this Normalizer will iterate. - * The iteration position is set to the beginning. - * - * @param newText a CharacterIterator object that replaces the current input text - * @param status a UErrorCode - * @deprecated ICU 56 Use Normalizer2 instead. - */ - void setText(const CharacterIterator& newText, - UErrorCode &status); - - /** - * Set the input text over which this Normalizer will iterate. - * The iteration position is set to the beginning. - * - * @param newText a string that replaces the current input text - * @param length the length of the string, or -1 if NUL-terminated - * @param status a UErrorCode - * @deprecated ICU 56 Use Normalizer2 instead. - */ - void setText(ConstChar16Ptr newText, - int32_t length, - UErrorCode &status); - /** - * Copies the input text into the UnicodeString argument. - * - * @param result Receives a copy of the text under iteration. - * @deprecated ICU 56 Use Normalizer2 instead. - */ - void getText(UnicodeString& result); - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - * @returns a UClassID for this class. - * @deprecated ICU 56 Use Normalizer2 instead. - */ - static UClassID U_EXPORT2 getStaticClassID(); -#endif /* U_HIDE_DEPRECATED_API */ - -#ifndef U_FORCE_HIDE_DEPRECATED_API - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - * @return a UClassID for the actual class. - * @deprecated ICU 56 Use Normalizer2 instead. - */ - virtual UClassID getDynamicClassID() const override; -#endif // U_FORCE_HIDE_DEPRECATED_API - -private: - //------------------------------------------------------------------------- - // Private functions - //------------------------------------------------------------------------- - - Normalizer() = delete; // default constructor not implemented - Normalizer &operator=(const Normalizer &that) = delete; // assignment operator not implemented - - // Private utility methods for iteration - // For documentation, see the source code - UBool nextNormalize(); - UBool previousNormalize(); - - void init(); - void clearBuffer(void); - - //------------------------------------------------------------------------- - // Private data - //------------------------------------------------------------------------- - - FilteredNormalizer2*fFilteredNorm2; // owned if not NULL - const Normalizer2 *fNorm2; // not owned; may be equal to fFilteredNorm2 - UNormalizationMode fUMode; // deprecated - int32_t fOptions; - - // The input text and our position in it - CharacterIterator *text; - - // The normalization buffer is the result of normalization - // of the source in [currentIndex..nextIndex[ . - int32_t currentIndex, nextIndex; - - // A buffer for holding intermediate results - UnicodeString buffer; - int32_t bufferPos; -}; - -//------------------------------------------------------------------------- -// Inline implementations -//------------------------------------------------------------------------- - -#ifndef U_HIDE_DEPRECATED_API -inline bool -Normalizer::operator!= (const Normalizer& other) const -{ return ! operator==(other); } - -inline UNormalizationCheckResult -Normalizer::quickCheck(const UnicodeString& source, - UNormalizationMode mode, - UErrorCode &status) { - return quickCheck(source, mode, 0, status); -} - -inline UBool -Normalizer::isNormalized(const UnicodeString& source, - UNormalizationMode mode, - UErrorCode &status) { - return isNormalized(source, mode, 0, status); -} -#endif /* U_HIDE_DEPRECATED_API */ - -inline int32_t -Normalizer::compare(const UnicodeString &s1, const UnicodeString &s2, - uint32_t options, - UErrorCode &errorCode) { - // all argument checking is done in unorm_compare - return unorm_compare(toUCharPtr(s1.getBuffer()), s1.length(), - toUCharPtr(s2.getBuffer()), s2.length(), - options, - &errorCode); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_NORMALIZATION */ - -#endif // NORMLZR_H - -#endif /* U_SHOW_CPLUSPLUS_API */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************** + * COPYRIGHT: + * Copyright (c) 1996-2015, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************** + */ + +#ifndef NORMLZR_H +#define NORMLZR_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +/** + * \file + * \brief C++ API: Unicode Normalization + */ + +#if !UCONFIG_NO_NORMALIZATION + +#include "unicode/chariter.h" +#include "unicode/normalizer2.h" +#include "unicode/unistr.h" +#include "unicode/unorm.h" +#include "unicode/uobject.h" + +U_NAMESPACE_BEGIN +/** + * Old Unicode normalization API. + * + * This API has been replaced by the Normalizer2 class and is only available + * for backward compatibility. This class simply delegates to the Normalizer2 class. + * There is one exception: The new API does not provide a replacement for Normalizer::compare(). + * + * The Normalizer class supports the standard normalization forms described in + * + * Unicode Standard Annex #15: Unicode Normalization Forms. + * + * The Normalizer class consists of two parts: + * - static functions that normalize strings or test if strings are normalized + * - a Normalizer object is an iterator that takes any kind of text and + * provides iteration over its normalized form + * + * The Normalizer class is not suitable for subclassing. + * + * For basic information about normalization forms and details about the C API + * please see the documentation in unorm.h. + * + * The iterator API with the Normalizer constructors and the non-static functions + * use a CharacterIterator as input. It is possible to pass a string which + * is then internally wrapped in a CharacterIterator. + * The input text is not normalized all at once, but incrementally where needed + * (providing efficient random access). + * This allows to pass in a large text but spend only a small amount of time + * normalizing a small part of that text. + * However, if the entire text is normalized, then the iterator will be + * slower than normalizing the entire text at once and iterating over the result. + * A possible use of the Normalizer iterator is also to report an index into the + * original text that is close to where the normalized characters come from. + * + * Important: The iterator API was cleaned up significantly for ICU 2.0. + * The earlier implementation reported the getIndex() inconsistently, + * and previous() could not be used after setIndex(), next(), first(), and current(). + * + * Normalizer allows to start normalizing from anywhere in the input text by + * calling setIndexOnly(), first(), or last(). + * Without calling any of these, the iterator will start at the beginning of the text. + * + * At any time, next() returns the next normalized code point (UChar32), + * with post-increment semantics (like CharacterIterator::next32PostInc()). + * previous() returns the previous normalized code point (UChar32), + * with pre-decrement semantics (like CharacterIterator::previous32()). + * + * current() returns the current code point + * (respectively the one at the newly set index) without moving + * the getIndex(). Note that if the text at the current position + * needs to be normalized, then these functions will do that. + * (This is why current() is not const.) + * It is more efficient to call setIndexOnly() instead, which does not + * normalize. + * + * getIndex() always refers to the position in the input text where the normalized + * code points are returned from. It does not always change with each returned + * code point. + * The code point that is returned from any of the functions + * corresponds to text at or after getIndex(), according to the + * function's iteration semantics (post-increment or pre-decrement). + * + * next() returns a code point from at or after the getIndex() + * from before the next() call. After the next() call, the getIndex() + * might have moved to where the next code point will be returned from + * (from a next() or current() call). + * This is semantically equivalent to array access with array[index++] + * (post-increment semantics). + * + * previous() returns a code point from at or after the getIndex() + * from after the previous() call. + * This is semantically equivalent to array access with array[--index] + * (pre-decrement semantics). + * + * Internally, the Normalizer iterator normalizes a small piece of text + * starting at the getIndex() and ending at a following "safe" index. + * The normalized results is stored in an internal string buffer, and + * the code points are iterated from there. + * With multiple iteration calls, this is repeated until the next piece + * of text needs to be normalized, and the getIndex() needs to be moved. + * + * The following "safe" index, the internal buffer, and the secondary + * iteration index into that buffer are not exposed on the API. + * This also means that it is currently not practical to return to + * a particular, arbitrary position in the text because one would need to + * know, and be able to set, in addition to the getIndex(), at least also the + * current index into the internal buffer. + * It is currently only possible to observe when getIndex() changes + * (with careful consideration of the iteration semantics), + * at which time the internal index will be 0. + * For example, if getIndex() is different after next() than before it, + * then the internal index is 0 and one can return to this getIndex() + * later with setIndexOnly(). + * + * Note: While the setIndex() and getIndex() refer to indices in the + * underlying Unicode input text, the next() and previous() methods + * iterate through characters in the normalized output. + * This means that there is not necessarily a one-to-one correspondence + * between characters returned by next() and previous() and the indices + * passed to and returned from setIndex() and getIndex(). + * It is for this reason that Normalizer does not implement the CharacterIterator interface. + * + * @author Laura Werner, Mark Davis, Markus Scherer + * @stable ICU 2.0 + */ +class U_COMMON_API Normalizer : public UObject { +public: +#ifndef U_HIDE_DEPRECATED_API + /** + * If DONE is returned from an iteration function that returns a code point, + * then there are no more normalization results available. + * @deprecated ICU 56 Use Normalizer2 instead. + */ + enum { + DONE=0xffff + }; + + // Constructors + + /** + * Creates a new Normalizer object for iterating over the + * normalized form of a given string. + *

+ * @param str The string to be normalized. The normalization + * will start at the beginning of the string. + * + * @param mode The normalization mode. + * @deprecated ICU 56 Use Normalizer2 instead. + */ + Normalizer(const UnicodeString& str, UNormalizationMode mode); + + /** + * Creates a new Normalizer object for iterating over the + * normalized form of a given string. + *

+ * @param str The string to be normalized. The normalization + * will start at the beginning of the string. + * + * @param length Length of the string, or -1 if NUL-terminated. + * @param mode The normalization mode. + * @deprecated ICU 56 Use Normalizer2 instead. + */ + Normalizer(ConstChar16Ptr str, int32_t length, UNormalizationMode mode); + + /** + * Creates a new Normalizer object for iterating over the + * normalized form of the given text. + *

+ * @param iter The input text to be normalized. The normalization + * will start at the beginning of the string. + * + * @param mode The normalization mode. + * @deprecated ICU 56 Use Normalizer2 instead. + */ + Normalizer(const CharacterIterator& iter, UNormalizationMode mode); +#endif /* U_HIDE_DEPRECATED_API */ + +#ifndef U_FORCE_HIDE_DEPRECATED_API + /** + * Copy constructor. + * @param copy The object to be copied. + * @deprecated ICU 56 Use Normalizer2 instead. + */ + Normalizer(const Normalizer& copy); + + /** + * Destructor + * @deprecated ICU 56 Use Normalizer2 instead. + */ + virtual ~Normalizer(); +#endif // U_FORCE_HIDE_DEPRECATED_API + + //------------------------------------------------------------------------- + // Static utility methods + //------------------------------------------------------------------------- + +#ifndef U_HIDE_DEPRECATED_API + /** + * Normalizes a UnicodeString according to the specified normalization mode. + * This is a wrapper for unorm_normalize(), using UnicodeString's. + * + * The options parameter specifies which optional + * Normalizer features are to be enabled for this operation. + * + * @param source the input string to be normalized. + * @param mode the normalization mode + * @param options the optional features to be enabled (0 for no options) + * @param result The normalized string (on output). + * @param status The error code. + * @deprecated ICU 56 Use Normalizer2 instead. + */ + static void U_EXPORT2 normalize(const UnicodeString& source, + UNormalizationMode mode, int32_t options, + UnicodeString& result, + UErrorCode &status); + + /** + * Compose a UnicodeString. + * This is equivalent to normalize() with mode UNORM_NFC or UNORM_NFKC. + * This is a wrapper for unorm_normalize(), using UnicodeString's. + * + * The options parameter specifies which optional + * Normalizer features are to be enabled for this operation. + * + * @param source the string to be composed. + * @param compat Perform compatibility decomposition before composition. + * If this argument is false, only canonical + * decomposition will be performed. + * @param options the optional features to be enabled (0 for no options) + * @param result The composed string (on output). + * @param status The error code. + * @deprecated ICU 56 Use Normalizer2 instead. + */ + static void U_EXPORT2 compose(const UnicodeString& source, + UBool compat, int32_t options, + UnicodeString& result, + UErrorCode &status); + + /** + * Static method to decompose a UnicodeString. + * This is equivalent to normalize() with mode UNORM_NFD or UNORM_NFKD. + * This is a wrapper for unorm_normalize(), using UnicodeString's. + * + * The options parameter specifies which optional + * Normalizer features are to be enabled for this operation. + * + * @param source the string to be decomposed. + * @param compat Perform compatibility decomposition. + * If this argument is false, only canonical + * decomposition will be performed. + * @param options the optional features to be enabled (0 for no options) + * @param result The decomposed string (on output). + * @param status The error code. + * @deprecated ICU 56 Use Normalizer2 instead. + */ + static void U_EXPORT2 decompose(const UnicodeString& source, + UBool compat, int32_t options, + UnicodeString& result, + UErrorCode &status); + + /** + * Performing quick check on a string, to quickly determine if the string is + * in a particular normalization format. + * This is a wrapper for unorm_quickCheck(), using a UnicodeString. + * + * Three types of result can be returned UNORM_YES, UNORM_NO or + * UNORM_MAYBE. Result UNORM_YES indicates that the argument + * string is in the desired normalized format, UNORM_NO determines that + * argument string is not in the desired normalized format. A + * UNORM_MAYBE result indicates that a more thorough check is required, + * the user may have to put the string in its normalized form and compare the + * results. + * @param source string for determining if it is in a normalized format + * @param mode normalization format + * @param status A reference to a UErrorCode to receive any errors + * @return UNORM_YES, UNORM_NO or UNORM_MAYBE + * + * @see isNormalized + * @deprecated ICU 56 Use Normalizer2 instead. + */ + static inline UNormalizationCheckResult + quickCheck(const UnicodeString &source, UNormalizationMode mode, UErrorCode &status); + + /** + * Performing quick check on a string; same as the other version of quickCheck + * but takes an extra options parameter like most normalization functions. + * + * @param source string for determining if it is in a normalized format + * @param mode normalization format + * @param options the optional features to be enabled (0 for no options) + * @param status A reference to a UErrorCode to receive any errors + * @return UNORM_YES, UNORM_NO or UNORM_MAYBE + * + * @see isNormalized + * @deprecated ICU 56 Use Normalizer2 instead. + */ + static UNormalizationCheckResult + quickCheck(const UnicodeString &source, UNormalizationMode mode, int32_t options, UErrorCode &status); + + /** + * Test if a string is in a given normalization form. + * This is semantically equivalent to source.equals(normalize(source, mode)) . + * + * Unlike unorm_quickCheck(), this function returns a definitive result, + * never a "maybe". + * For NFD, NFKD, and FCD, both functions work exactly the same. + * For NFC and NFKC where quickCheck may return "maybe", this function will + * perform further tests to arrive at a true/false result. + * + * @param src String that is to be tested if it is in a normalization format. + * @param mode Which normalization form to test for. + * @param errorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return Boolean value indicating whether the source string is in the + * "mode" normalization form. + * + * @see quickCheck + * @deprecated ICU 56 Use Normalizer2 instead. + */ + static inline UBool + isNormalized(const UnicodeString &src, UNormalizationMode mode, UErrorCode &errorCode); + + /** + * Test if a string is in a given normalization form; same as the other version of isNormalized + * but takes an extra options parameter like most normalization functions. + * + * @param src String that is to be tested if it is in a normalization format. + * @param mode Which normalization form to test for. + * @param options the optional features to be enabled (0 for no options) + * @param errorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return Boolean value indicating whether the source string is in the + * "mode" normalization form. + * + * @see quickCheck + * @deprecated ICU 56 Use Normalizer2 instead. + */ + static UBool + isNormalized(const UnicodeString &src, UNormalizationMode mode, int32_t options, UErrorCode &errorCode); + + /** + * Concatenate normalized strings, making sure that the result is normalized as well. + * + * If both the left and the right strings are in + * the normalization form according to "mode/options", + * then the result will be + * + * \code + * dest=normalize(left+right, mode, options) + * \endcode + * + * For details see unorm_concatenate in unorm.h. + * + * @param left Left source string. + * @param right Right source string. + * @param result The output string. + * @param mode The normalization mode. + * @param options A bit set of normalization options. + * @param errorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return result + * + * @see unorm_concatenate + * @see normalize + * @see unorm_next + * @see unorm_previous + * + * @deprecated ICU 56 Use Normalizer2 instead. + */ + static UnicodeString & + U_EXPORT2 concatenate(const UnicodeString &left, const UnicodeString &right, + UnicodeString &result, + UNormalizationMode mode, int32_t options, + UErrorCode &errorCode); +#endif /* U_HIDE_DEPRECATED_API */ + + /** + * Compare two strings for canonical equivalence. + * Further options include case-insensitive comparison and + * code point order (as opposed to code unit order). + * + * Canonical equivalence between two strings is defined as their normalized + * forms (NFD or NFC) being identical. + * This function compares strings incrementally instead of normalizing + * (and optionally case-folding) both strings entirely, + * improving performance significantly. + * + * Bulk normalization is only necessary if the strings do not fulfill the FCD + * conditions. Only in this case, and only if the strings are relatively long, + * is memory allocated temporarily. + * For FCD strings and short non-FCD strings there is no memory allocation. + * + * Semantically, this is equivalent to + * strcmp[CodePointOrder](NFD(foldCase(s1)), NFD(foldCase(s2))) + * where code point order and foldCase are all optional. + * + * UAX 21 2.5 Caseless Matching specifies that for a canonical caseless match + * the case folding must be performed first, then the normalization. + * + * @param s1 First source string. + * @param s2 Second source string. + * + * @param options A bit set of options: + * - U_FOLD_CASE_DEFAULT or 0 is used for default options: + * Case-sensitive comparison in code unit order, and the input strings + * are quick-checked for FCD. + * + * - UNORM_INPUT_IS_FCD + * Set if the caller knows that both s1 and s2 fulfill the FCD conditions. + * If not set, the function will quickCheck for FCD + * and normalize if necessary. + * + * - U_COMPARE_CODE_POINT_ORDER + * Set to choose code point order instead of code unit order + * (see u_strCompare for details). + * + * - U_COMPARE_IGNORE_CASE + * Set to compare strings case-insensitively using case folding, + * instead of case-sensitively. + * If set, then the following case folding options are used. + * + * - Options as used with case-insensitive comparisons, currently: + * + * - U_FOLD_CASE_EXCLUDE_SPECIAL_I + * (see u_strCaseCompare for details) + * + * - regular normalization options shifted left by UNORM_COMPARE_NORM_OPTIONS_SHIFT + * + * @param errorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return <0 or 0 or >0 as usual for string comparisons + * + * @see unorm_compare + * @see normalize + * @see UNORM_FCD + * @see u_strCompare + * @see u_strCaseCompare + * + * @stable ICU 2.2 + */ + static inline int32_t + compare(const UnicodeString &s1, const UnicodeString &s2, + uint32_t options, + UErrorCode &errorCode); + +#ifndef U_HIDE_DEPRECATED_API + //------------------------------------------------------------------------- + // Iteration API + //------------------------------------------------------------------------- + + /** + * Return the current character in the normalized text. + * current() may need to normalize some text at getIndex(). + * The getIndex() is not changed. + * + * @return the current normalized code point + * @deprecated ICU 56 Use Normalizer2 instead. + */ + UChar32 current(void); + + /** + * Return the first character in the normalized text. + * This is equivalent to setIndexOnly(startIndex()) followed by next(). + * (Post-increment semantics.) + * + * @return the first normalized code point + * @deprecated ICU 56 Use Normalizer2 instead. + */ + UChar32 first(void); + + /** + * Return the last character in the normalized text. + * This is equivalent to setIndexOnly(endIndex()) followed by previous(). + * (Pre-decrement semantics.) + * + * @return the last normalized code point + * @deprecated ICU 56 Use Normalizer2 instead. + */ + UChar32 last(void); + + /** + * Return the next character in the normalized text. + * (Post-increment semantics.) + * If the end of the text has already been reached, DONE is returned. + * The DONE value could be confused with a U+FFFF non-character code point + * in the text. If this is possible, you can test getIndex()startIndex() || first()!=DONE). (Calling first() will change + * the iterator state!) + * + * The C API unorm_previous() is more efficient and does not have this ambiguity. + * + * @return the previous normalized code point + * @deprecated ICU 56 Use Normalizer2 instead. + */ + UChar32 previous(void); + + /** + * Set the iteration position in the input text that is being normalized, + * without any immediate normalization. + * After setIndexOnly(), getIndex() will return the same index that is + * specified here. + * + * @param index the desired index in the input text. + * @deprecated ICU 56 Use Normalizer2 instead. + */ + void setIndexOnly(int32_t index); + + /** + * Reset the index to the beginning of the text. + * This is equivalent to setIndexOnly(startIndex)). + * @deprecated ICU 56 Use Normalizer2 instead. + */ + void reset(void); + + /** + * Retrieve the current iteration position in the input text that is + * being normalized. + * + * A following call to next() will return a normalized code point from + * the input text at or after this index. + * + * After a call to previous(), getIndex() will point at or before the + * position in the input text where the normalized code point + * was returned from with previous(). + * + * @return the current index in the input text + * @deprecated ICU 56 Use Normalizer2 instead. + */ + int32_t getIndex(void) const; + + /** + * Retrieve the index of the start of the input text. This is the begin index + * of the CharacterIterator or the start (i.e. index 0) of the string + * over which this Normalizer is iterating. + * + * @return the smallest index in the input text where the Normalizer operates + * @deprecated ICU 56 Use Normalizer2 instead. + */ + int32_t startIndex(void) const; + + /** + * Retrieve the index of the end of the input text. This is the end index + * of the CharacterIterator or the length of the string + * over which this Normalizer is iterating. + * This end index is exclusive, i.e., the Normalizer operates only on characters + * before this index. + * + * @return the first index in the input text where the Normalizer does not operate + * @deprecated ICU 56 Use Normalizer2 instead. + */ + int32_t endIndex(void) const; + + /** + * Returns true when both iterators refer to the same character in the same + * input text. + * + * @param that a Normalizer object to compare this one to + * @return comparison result + * @deprecated ICU 56 Use Normalizer2 instead. + */ + bool operator==(const Normalizer& that) const; + + /** + * Returns false when both iterators refer to the same character in the same + * input text. + * + * @param that a Normalizer object to compare this one to + * @return comparison result + * @deprecated ICU 56 Use Normalizer2 instead. + */ + inline bool operator!=(const Normalizer& that) const; + + /** + * Returns a pointer to a new Normalizer that is a clone of this one. + * The caller is responsible for deleting the new clone. + * @return a pointer to a new Normalizer + * @deprecated ICU 56 Use Normalizer2 instead. + */ + Normalizer* clone() const; + + /** + * Generates a hash code for this iterator. + * + * @return the hash code + * @deprecated ICU 56 Use Normalizer2 instead. + */ + int32_t hashCode(void) const; + + //------------------------------------------------------------------------- + // Property access methods + //------------------------------------------------------------------------- + + /** + * Set the normalization mode for this object. + *

+ * Note:If the normalization mode is changed while iterating + * over a string, calls to {@link #next() } and {@link #previous() } may + * return previously buffers characters in the old normalization mode + * until the iteration is able to re-sync at the next base character. + * It is safest to call {@link #setIndexOnly }, {@link #reset() }, + * {@link #setText }, {@link #first() }, + * {@link #last() }, etc. after calling setMode. + *

+ * @param newMode the new mode for this Normalizer. + * @see #getUMode + * @deprecated ICU 56 Use Normalizer2 instead. + */ + void setMode(UNormalizationMode newMode); + + /** + * Return the normalization mode for this object. + * + * This is an unusual name because there used to be a getMode() that + * returned a different type. + * + * @return the mode for this Normalizer + * @see #setMode + * @deprecated ICU 56 Use Normalizer2 instead. + */ + UNormalizationMode getUMode(void) const; + + /** + * Set options that affect this Normalizer's operation. + * Options do not change the basic composition or decomposition operation + * that is being performed, but they control whether + * certain optional portions of the operation are done. + * Currently the only available option is obsolete. + * + * It is possible to specify multiple options that are all turned on or off. + * + * @param option the option(s) whose value is/are to be set. + * @param value the new setting for the option. Use true to + * turn the option(s) on and false to turn it/them off. + * + * @see #getOption + * @deprecated ICU 56 Use Normalizer2 instead. + */ + void setOption(int32_t option, + UBool value); + + /** + * Determine whether an option is turned on or off. + * If multiple options are specified, then the result is true if any + * of them are set. + *

+ * @param option the option(s) that are to be checked + * @return true if any of the option(s) are set + * @see #setOption + * @deprecated ICU 56 Use Normalizer2 instead. + */ + UBool getOption(int32_t option) const; + + /** + * Set the input text over which this Normalizer will iterate. + * The iteration position is set to the beginning. + * + * @param newText a string that replaces the current input text + * @param status a UErrorCode + * @deprecated ICU 56 Use Normalizer2 instead. + */ + void setText(const UnicodeString& newText, + UErrorCode &status); + + /** + * Set the input text over which this Normalizer will iterate. + * The iteration position is set to the beginning. + * + * @param newText a CharacterIterator object that replaces the current input text + * @param status a UErrorCode + * @deprecated ICU 56 Use Normalizer2 instead. + */ + void setText(const CharacterIterator& newText, + UErrorCode &status); + + /** + * Set the input text over which this Normalizer will iterate. + * The iteration position is set to the beginning. + * + * @param newText a string that replaces the current input text + * @param length the length of the string, or -1 if NUL-terminated + * @param status a UErrorCode + * @deprecated ICU 56 Use Normalizer2 instead. + */ + void setText(ConstChar16Ptr newText, + int32_t length, + UErrorCode &status); + /** + * Copies the input text into the UnicodeString argument. + * + * @param result Receives a copy of the text under iteration. + * @deprecated ICU 56 Use Normalizer2 instead. + */ + void getText(UnicodeString& result); + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + * @returns a UClassID for this class. + * @deprecated ICU 56 Use Normalizer2 instead. + */ + static UClassID U_EXPORT2 getStaticClassID(); +#endif /* U_HIDE_DEPRECATED_API */ + +#ifndef U_FORCE_HIDE_DEPRECATED_API + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + * @return a UClassID for the actual class. + * @deprecated ICU 56 Use Normalizer2 instead. + */ + virtual UClassID getDynamicClassID() const override; +#endif // U_FORCE_HIDE_DEPRECATED_API + +private: + //------------------------------------------------------------------------- + // Private functions + //------------------------------------------------------------------------- + + Normalizer() = delete; // default constructor not implemented + Normalizer &operator=(const Normalizer &that) = delete; // assignment operator not implemented + + // Private utility methods for iteration + // For documentation, see the source code + UBool nextNormalize(); + UBool previousNormalize(); + + void init(); + void clearBuffer(void); + + //------------------------------------------------------------------------- + // Private data + //------------------------------------------------------------------------- + + FilteredNormalizer2*fFilteredNorm2; // owned if not nullptr + const Normalizer2 *fNorm2; // not owned; may be equal to fFilteredNorm2 + UNormalizationMode fUMode; // deprecated + int32_t fOptions; + + // The input text and our position in it + CharacterIterator *text; + + // The normalization buffer is the result of normalization + // of the source in [currentIndex..nextIndex[ . + int32_t currentIndex, nextIndex; + + // A buffer for holding intermediate results + UnicodeString buffer; + int32_t bufferPos; +}; + +//------------------------------------------------------------------------- +// Inline implementations +//------------------------------------------------------------------------- + +#ifndef U_HIDE_DEPRECATED_API +inline bool +Normalizer::operator!= (const Normalizer& other) const +{ return ! operator==(other); } + +inline UNormalizationCheckResult +Normalizer::quickCheck(const UnicodeString& source, + UNormalizationMode mode, + UErrorCode &status) { + return quickCheck(source, mode, 0, status); +} + +inline UBool +Normalizer::isNormalized(const UnicodeString& source, + UNormalizationMode mode, + UErrorCode &status) { + return isNormalized(source, mode, 0, status); +} +#endif /* U_HIDE_DEPRECATED_API */ + +inline int32_t +Normalizer::compare(const UnicodeString &s1, const UnicodeString &s2, + uint32_t options, + UErrorCode &errorCode) { + // all argument checking is done in unorm_compare + return unorm_compare(toUCharPtr(s1.getBuffer()), s1.length(), + toUCharPtr(s2.getBuffer()), s2.length(), + options, + &errorCode); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_NORMALIZATION */ + +#endif // NORMLZR_H + +#endif /* U_SHOW_CPLUSPLUS_API */ diff --git a/deps/icu-small/source/common/unicode/parseerr.h b/deps/icu-small/source/common/unicode/parseerr.h index c23cc273b828eb..2592ede9b5d164 100644 --- a/deps/icu-small/source/common/unicode/parseerr.h +++ b/deps/icu-small/source/common/unicode/parseerr.h @@ -1,94 +1,94 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2005, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 03/14/00 aliu Creation. -* 06/27/00 aliu Change from C++ class to C struct -********************************************************************** -*/ -#ifndef PARSEERR_H -#define PARSEERR_H - -#include "unicode/utypes.h" - - -/** - * \file - * \brief C API: Parse Error Information - */ -/** - * The capacity of the context strings in UParseError. - * @stable ICU 2.0 - */ -enum { U_PARSE_CONTEXT_LEN = 16 }; - -/** - * A UParseError struct is used to returned detailed information about - * parsing errors. It is used by ICU parsing engines that parse long - * rules, patterns, or programs, where the text being parsed is long - * enough that more information than a UErrorCode is needed to - * localize the error. - * - *

The line, offset, and context fields are optional; parsing - * engines may choose not to use to use them. - * - *

The preContext and postContext strings include some part of the - * context surrounding the error. If the source text is "let for=7" - * and "for" is the error (e.g., because it is a reserved word), then - * some examples of what a parser might produce are the following: - * - *

- * preContext   postContext
- * ""           ""            The parser does not support context
- * "let "       "=7"          Pre- and post-context only
- * "let "       "for=7"       Pre- and post-context and error text
- * ""           "for"         Error text only
- * 
- * - *

Examples of engines which use UParseError (or may use it in the - * future) are Transliterator, RuleBasedBreakIterator, and - * RegexPattern. - * - * @stable ICU 2.0 - */ -typedef struct UParseError { - - /** - * The line on which the error occurred. If the parser uses this - * field, it sets it to the line number of the source text line on - * which the error appears, which will be a value >= 1. If the - * parse does not support line numbers, the value will be <= 0. - * @stable ICU 2.0 - */ - int32_t line; - - /** - * The character offset to the error. If the line field is >= 1, - * then this is the offset from the start of the line. Otherwise, - * this is the offset from the start of the text. If the parser - * does not support this field, it will have a value < 0. - * @stable ICU 2.0 - */ - int32_t offset; - - /** - * Textual context before the error. Null-terminated. The empty - * string if not supported by parser. - * @stable ICU 2.0 - */ - UChar preContext[U_PARSE_CONTEXT_LEN]; - - /** - * The error itself and/or textual context after the error. - * Null-terminated. The empty string if not supported by parser. - * @stable ICU 2.0 - */ - UChar postContext[U_PARSE_CONTEXT_LEN]; - -} UParseError; - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2005, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 03/14/00 aliu Creation. +* 06/27/00 aliu Change from C++ class to C struct +********************************************************************** +*/ +#ifndef PARSEERR_H +#define PARSEERR_H + +#include "unicode/utypes.h" + + +/** + * \file + * \brief C API: Parse Error Information + */ +/** + * The capacity of the context strings in UParseError. + * @stable ICU 2.0 + */ +enum { U_PARSE_CONTEXT_LEN = 16 }; + +/** + * A UParseError struct is used to returned detailed information about + * parsing errors. It is used by ICU parsing engines that parse long + * rules, patterns, or programs, where the text being parsed is long + * enough that more information than a UErrorCode is needed to + * localize the error. + * + *

The line, offset, and context fields are optional; parsing + * engines may choose not to use to use them. + * + *

The preContext and postContext strings include some part of the + * context surrounding the error. If the source text is "let for=7" + * and "for" is the error (e.g., because it is a reserved word), then + * some examples of what a parser might produce are the following: + * + *

+ * preContext   postContext
+ * ""           ""            The parser does not support context
+ * "let "       "=7"          Pre- and post-context only
+ * "let "       "for=7"       Pre- and post-context and error text
+ * ""           "for"         Error text only
+ * 
+ * + *

Examples of engines which use UParseError (or may use it in the + * future) are Transliterator, RuleBasedBreakIterator, and + * RegexPattern. + * + * @stable ICU 2.0 + */ +typedef struct UParseError { + + /** + * The line on which the error occurred. If the parser uses this + * field, it sets it to the line number of the source text line on + * which the error appears, which will be a value >= 1. If the + * parse does not support line numbers, the value will be <= 0. + * @stable ICU 2.0 + */ + int32_t line; + + /** + * The character offset to the error. If the line field is >= 1, + * then this is the offset from the start of the line. Otherwise, + * this is the offset from the start of the text. If the parser + * does not support this field, it will have a value < 0. + * @stable ICU 2.0 + */ + int32_t offset; + + /** + * Textual context before the error. Null-terminated. The empty + * string if not supported by parser. + * @stable ICU 2.0 + */ + UChar preContext[U_PARSE_CONTEXT_LEN]; + + /** + * The error itself and/or textual context after the error. + * Null-terminated. The empty string if not supported by parser. + * @stable ICU 2.0 + */ + UChar postContext[U_PARSE_CONTEXT_LEN]; + +} UParseError; + +#endif diff --git a/deps/icu-small/source/common/unicode/parsepos.h b/deps/icu-small/source/common/unicode/parsepos.h index 73945f5f97bcbd..80371ee00f42c2 100644 --- a/deps/icu-small/source/common/unicode/parsepos.h +++ b/deps/icu-small/source/common/unicode/parsepos.h @@ -1,237 +1,237 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -* Copyright (C) 1997-2005, International Business Machines Corporation and others. All Rights Reserved. -******************************************************************************* -* -* File PARSEPOS.H -* -* Modification History: -* -* Date Name Description -* 07/09/97 helena Converted from java. -* 07/17/98 stephen Added errorIndex support. -* 05/11/99 stephen Cleaned up. -******************************************************************************* -*/ - -#ifndef PARSEPOS_H -#define PARSEPOS_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/uobject.h" - - -U_NAMESPACE_BEGIN - -/** - * \file - * \brief C++ API: Canonical Iterator - */ -/** - * ParsePosition is a simple class used by Format - * and its subclasses to keep track of the current position during parsing. - * The parseObject method in the various Format - * classes requires a ParsePosition object as an argument. - * - *

- * By design, as you parse through a string with different formats, - * you can use the same ParsePosition, since the index parameter - * records the current position. - * - * The ParsePosition class is not suitable for subclassing. - * - * @version 1.3 10/30/97 - * @author Mark Davis, Helena Shih - * @see java.text.Format - */ - -class U_COMMON_API ParsePosition : public UObject { -public: - /** - * Default constructor, the index starts with 0 as default. - * @stable ICU 2.0 - */ - ParsePosition() - : UObject(), - index(0), - errorIndex(-1) - {} - - /** - * Create a new ParsePosition with the given initial index. - * @param newIndex the new text offset. - * @stable ICU 2.0 - */ - ParsePosition(int32_t newIndex) - : UObject(), - index(newIndex), - errorIndex(-1) - {} - - /** - * Copy constructor - * @param copy the object to be copied from. - * @stable ICU 2.0 - */ - ParsePosition(const ParsePosition& copy) - : UObject(copy), - index(copy.index), - errorIndex(copy.errorIndex) - {} - - /** - * Destructor - * @stable ICU 2.0 - */ - virtual ~ParsePosition(); - - /** - * Assignment operator - * @stable ICU 2.0 - */ - inline ParsePosition& operator=(const ParsePosition& copy); - - /** - * Equality operator. - * @return true if the two parse positions are equal, false otherwise. - * @stable ICU 2.0 - */ - inline bool operator==(const ParsePosition& that) const; - - /** - * Equality operator. - * @return true if the two parse positions are not equal, false otherwise. - * @stable ICU 2.0 - */ - inline bool operator!=(const ParsePosition& that) const; - - /** - * Clone this object. - * Clones can be used concurrently in multiple threads. - * If an error occurs, then NULL is returned. - * The caller must delete the clone. - * - * @return a clone of this object - * - * @see getDynamicClassID - * @stable ICU 2.8 - */ - ParsePosition *clone() const; - - /** - * Retrieve the current parse position. On input to a parse method, this - * is the index of the character at which parsing will begin; on output, it - * is the index of the character following the last character parsed. - * @return the current index. - * @stable ICU 2.0 - */ - inline int32_t getIndex(void) const; - - /** - * Set the current parse position. - * @param index the new index. - * @stable ICU 2.0 - */ - inline void setIndex(int32_t index); - - /** - * Set the index at which a parse error occurred. Formatters - * should set this before returning an error code from their - * parseObject method. The default value is -1 if this is not - * set. - * @stable ICU 2.0 - */ - inline void setErrorIndex(int32_t ei); - - /** - * Retrieve the index at which an error occurred, or -1 if the - * error index has not been set. - * @stable ICU 2.0 - */ - inline int32_t getErrorIndex(void) const; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - * - * @stable ICU 2.2 - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - * - * @stable ICU 2.2 - */ - virtual UClassID getDynamicClassID() const override; - -private: - /** - * Input: the place you start parsing. - *
Output: position where the parse stopped. - * This is designed to be used serially, - * with each call setting index up for the next one. - */ - int32_t index; - - /** - * The index at which a parse error occurred. - */ - int32_t errorIndex; - -}; - -inline ParsePosition& -ParsePosition::operator=(const ParsePosition& copy) -{ - index = copy.index; - errorIndex = copy.errorIndex; - return *this; -} - -inline bool -ParsePosition::operator==(const ParsePosition& copy) const -{ - if(index != copy.index || errorIndex != copy.errorIndex) - return false; - else - return true; -} - -inline bool -ParsePosition::operator!=(const ParsePosition& copy) const -{ - return !operator==(copy); -} - -inline int32_t -ParsePosition::getIndex() const -{ - return index; -} - -inline void -ParsePosition::setIndex(int32_t offset) -{ - this->index = offset; -} - -inline int32_t -ParsePosition::getErrorIndex() const -{ - return errorIndex; -} - -inline void -ParsePosition::setErrorIndex(int32_t ei) -{ - this->errorIndex = ei; -} -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +* Copyright (C) 1997-2005, International Business Machines Corporation and others. All Rights Reserved. +******************************************************************************* +* +* File PARSEPOS.H +* +* Modification History: +* +* Date Name Description +* 07/09/97 helena Converted from java. +* 07/17/98 stephen Added errorIndex support. +* 05/11/99 stephen Cleaned up. +******************************************************************************* +*/ + +#ifndef PARSEPOS_H +#define PARSEPOS_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/uobject.h" + + +U_NAMESPACE_BEGIN + +/** + * \file + * \brief C++ API: Canonical Iterator + */ +/** + * ParsePosition is a simple class used by Format + * and its subclasses to keep track of the current position during parsing. + * The parseObject method in the various Format + * classes requires a ParsePosition object as an argument. + * + *

+ * By design, as you parse through a string with different formats, + * you can use the same ParsePosition, since the index parameter + * records the current position. + * + * The ParsePosition class is not suitable for subclassing. + * + * @version 1.3 10/30/97 + * @author Mark Davis, Helena Shih + * @see java.text.Format + */ + +class U_COMMON_API ParsePosition : public UObject { +public: + /** + * Default constructor, the index starts with 0 as default. + * @stable ICU 2.0 + */ + ParsePosition() + : UObject(), + index(0), + errorIndex(-1) + {} + + /** + * Create a new ParsePosition with the given initial index. + * @param newIndex the new text offset. + * @stable ICU 2.0 + */ + ParsePosition(int32_t newIndex) + : UObject(), + index(newIndex), + errorIndex(-1) + {} + + /** + * Copy constructor + * @param copy the object to be copied from. + * @stable ICU 2.0 + */ + ParsePosition(const ParsePosition& copy) + : UObject(copy), + index(copy.index), + errorIndex(copy.errorIndex) + {} + + /** + * Destructor + * @stable ICU 2.0 + */ + virtual ~ParsePosition(); + + /** + * Assignment operator + * @stable ICU 2.0 + */ + inline ParsePosition& operator=(const ParsePosition& copy); + + /** + * Equality operator. + * @return true if the two parse positions are equal, false otherwise. + * @stable ICU 2.0 + */ + inline bool operator==(const ParsePosition& that) const; + + /** + * Equality operator. + * @return true if the two parse positions are not equal, false otherwise. + * @stable ICU 2.0 + */ + inline bool operator!=(const ParsePosition& that) const; + + /** + * Clone this object. + * Clones can be used concurrently in multiple threads. + * If an error occurs, then nullptr is returned. + * The caller must delete the clone. + * + * @return a clone of this object + * + * @see getDynamicClassID + * @stable ICU 2.8 + */ + ParsePosition *clone() const; + + /** + * Retrieve the current parse position. On input to a parse method, this + * is the index of the character at which parsing will begin; on output, it + * is the index of the character following the last character parsed. + * @return the current index. + * @stable ICU 2.0 + */ + inline int32_t getIndex(void) const; + + /** + * Set the current parse position. + * @param index the new index. + * @stable ICU 2.0 + */ + inline void setIndex(int32_t index); + + /** + * Set the index at which a parse error occurred. Formatters + * should set this before returning an error code from their + * parseObject method. The default value is -1 if this is not + * set. + * @stable ICU 2.0 + */ + inline void setErrorIndex(int32_t ei); + + /** + * Retrieve the index at which an error occurred, or -1 if the + * error index has not been set. + * @stable ICU 2.0 + */ + inline int32_t getErrorIndex(void) const; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + * + * @stable ICU 2.2 + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + * + * @stable ICU 2.2 + */ + virtual UClassID getDynamicClassID() const override; + +private: + /** + * Input: the place you start parsing. + *
Output: position where the parse stopped. + * This is designed to be used serially, + * with each call setting index up for the next one. + */ + int32_t index; + + /** + * The index at which a parse error occurred. + */ + int32_t errorIndex; + +}; + +inline ParsePosition& +ParsePosition::operator=(const ParsePosition& copy) +{ + index = copy.index; + errorIndex = copy.errorIndex; + return *this; +} + +inline bool +ParsePosition::operator==(const ParsePosition& copy) const +{ + if(index != copy.index || errorIndex != copy.errorIndex) + return false; + else + return true; +} + +inline bool +ParsePosition::operator!=(const ParsePosition& copy) const +{ + return !operator==(copy); +} + +inline int32_t +ParsePosition::getIndex() const +{ + return index; +} + +inline void +ParsePosition::setIndex(int32_t offset) +{ + this->index = offset; +} + +inline int32_t +ParsePosition::getErrorIndex() const +{ + return errorIndex; +} + +inline void +ParsePosition::setErrorIndex(int32_t ei) +{ + this->errorIndex = ei; +} +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/platform.h b/deps/icu-small/source/common/unicode/platform.h index 1605226a797037..6769da1a045cda 100644 --- a/deps/icu-small/source/common/unicode/platform.h +++ b/deps/icu-small/source/common/unicode/platform.h @@ -1,900 +1,881 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1997-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* FILE NAME : platform.h -* -* Date Name Description -* 05/13/98 nos Creation (content moved here from ptypes.h). -* 03/02/99 stephen Added AS400 support. -* 03/30/99 stephen Added Linux support. -* 04/13/99 stephen Reworked for autoconf. -****************************************************************************** -*/ - -#ifndef _PLATFORM_H -#define _PLATFORM_H - -#include "unicode/uconfig.h" -#include "unicode/uvernum.h" - -/** - * \file - * \brief Basic types for the platform. - * - * This file used to be generated by autoconf/configure. - * Starting with ICU 49, platform.h is a normal source file, - * to simplify cross-compiling and working with non-autoconf/make build systems. - * - * When a value in this file does not work on a platform, then please - * try to derive it from the U_PLATFORM value - * (for which we might need a new value constant in rare cases) - * and/or from other macros that are predefined by the compiler - * or defined in standard (POSIX or platform or compiler) headers. - * - * As a temporary workaround, you can add an explicit \#define for some macros - * before it is first tested, or add an equivalent -D macro definition - * to the compiler's command line. - * - * Note: Some compilers provide ways to show the predefined macros. - * For example, with gcc you can compile an empty .c file and have the compiler - * print the predefined macros with - * \code - * gcc -E -dM -x c /dev/null | sort - * \endcode - * (You can provide an actual empty .c file rather than /dev/null. - * -x c++ is for C++.) - */ - -/** - * Define some things so that they can be documented. - * @internal - */ -#ifdef U_IN_DOXYGEN -/* - * Problem: "platform.h:335: warning: documentation for unknown define U_HAVE_STD_STRING found." means that U_HAVE_STD_STRING is not documented. - * Solution: #define any defines for non @internal API here, so that they are visible in the docs. If you just set PREDEFINED in Doxyfile.in, they won't be documented. - */ - -/* None for now. */ -#endif - -/** - * \def U_PLATFORM - * The U_PLATFORM macro defines the platform we're on. - * - * We used to define one different, value-less macro per platform. - * That made it hard to know the set of relevant platforms and macros, - * and hard to deal with variants of platforms. - * - * Starting with ICU 49, we define platforms as numeric macros, - * with ranges of values for related platforms and their variants. - * The U_PLATFORM macro is set to one of these values. - * - * Historical note from the Solaris Wikipedia article: - * AT&T and Sun collaborated on a project to merge the most popular Unix variants - * on the market at that time: BSD, System V, and Xenix. - * This became Unix System V Release 4 (SVR4). - * - * @internal - */ - -/** Unknown platform. @internal */ -#define U_PF_UNKNOWN 0 -/** Windows @internal */ -#define U_PF_WINDOWS 1000 -/** MinGW. Windows, calls to Win32 API, but using GNU gcc and binutils. @internal */ -#define U_PF_MINGW 1800 -/** - * Cygwin. Windows, calls to cygwin1.dll for Posix functions, - * using MSVC or GNU gcc and binutils. - * @internal - */ -#define U_PF_CYGWIN 1900 -/* Reserve 2000 for U_PF_UNIX? */ -/** HP-UX is based on UNIX System V. @internal */ -#define U_PF_HPUX 2100 -/** Solaris is a Unix operating system based on SVR4. @internal */ -#define U_PF_SOLARIS 2600 -/** BSD is a UNIX operating system derivative. @internal */ -#define U_PF_BSD 3000 -/** AIX is based on UNIX System V Releases and 4.3 BSD. @internal */ -#define U_PF_AIX 3100 -/** IRIX is based on UNIX System V with BSD extensions. @internal */ -#define U_PF_IRIX 3200 -/** - * Darwin is a POSIX-compliant operating system, composed of code developed by Apple, - * as well as code derived from NeXTSTEP, BSD, and other projects, - * built around the Mach kernel. - * Darwin forms the core set of components upon which Mac OS X, Apple TV, and iOS are based. - * (Original description modified from WikiPedia.) - * @internal - */ -#define U_PF_DARWIN 3500 -/** iPhone OS (iOS) is a derivative of Mac OS X. @internal */ -#define U_PF_IPHONE 3550 -/** QNX is a commercial Unix-like real-time operating system related to BSD. @internal */ -#define U_PF_QNX 3700 -/** Linux is a Unix-like operating system. @internal */ -#define U_PF_LINUX 4000 -/** - * Native Client is pretty close to Linux. - * See https://developer.chrome.com/native-client and - * http://www.chromium.org/nativeclient - * @internal - */ -#define U_PF_BROWSER_NATIVE_CLIENT 4020 -/** Android is based on Linux. @internal */ -#define U_PF_ANDROID 4050 -/** Fuchsia is a POSIX-ish platform. @internal */ -#define U_PF_FUCHSIA 4100 -/* Maximum value for Linux-based platform is 4499 */ -/** - * Emscripten is a C++ transpiler for the Web that can target asm.js or - * WebAssembly. It provides some POSIX-compatible wrappers and stubs and - * some Linux-like functionality, but is not fully compatible with - * either. - * @internal - */ -#define U_PF_EMSCRIPTEN 5010 -/** z/OS is the successor to OS/390 which was the successor to MVS. @internal */ -#define U_PF_OS390 9000 -/** "IBM i" is the current name of what used to be i5/OS and earlier OS/400. @internal */ -#define U_PF_OS400 9400 - -#ifdef U_PLATFORM - /* Use the predefined value. */ -#elif defined(__MINGW32__) -# define U_PLATFORM U_PF_MINGW -#elif defined(__CYGWIN__) -# define U_PLATFORM U_PF_CYGWIN -#elif defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) -# define U_PLATFORM U_PF_WINDOWS -#elif defined(__ANDROID__) -# define U_PLATFORM U_PF_ANDROID - /* Android wchar_t support depends on the API level. */ -# include -#elif defined(__pnacl__) || defined(__native_client__) -# define U_PLATFORM U_PF_BROWSER_NATIVE_CLIENT -#elif defined(__Fuchsia__) -# define U_PLATFORM U_PF_FUCHSIA -#elif defined(linux) || defined(__linux__) || defined(__linux) -# define U_PLATFORM U_PF_LINUX -#elif defined(__APPLE__) && defined(__MACH__) -# include -# if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && (defined(TARGET_OS_MACCATALYST) && !TARGET_OS_MACCATALYST) /* variant of TARGET_OS_MAC */ -# define U_PLATFORM U_PF_IPHONE -# else -# define U_PLATFORM U_PF_DARWIN -# endif -#elif defined(BSD) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__MirBSD__) -# if defined(__FreeBSD__) -# include -# endif -# define U_PLATFORM U_PF_BSD -#elif defined(sun) || defined(__sun) - /* Check defined(__SVR4) || defined(__svr4__) to distinguish Solaris from SunOS? */ -# define U_PLATFORM U_PF_SOLARIS -# if defined(__GNUC__) - /* Solaris/GCC needs this header file to get the proper endianness. Normally, this - * header file is included with stddef.h but on Solairs/GCC, the GCC version of stddef.h - * is included which does not include this header file. - */ -# include -# endif -#elif defined(_AIX) || defined(__TOS_AIX__) -# define U_PLATFORM U_PF_AIX -#elif defined(_hpux) || defined(hpux) || defined(__hpux) -# define U_PLATFORM U_PF_HPUX -#elif defined(sgi) || defined(__sgi) -# define U_PLATFORM U_PF_IRIX -#elif defined(__QNX__) || defined(__QNXNTO__) -# define U_PLATFORM U_PF_QNX -#elif defined(__TOS_MVS__) -# define U_PLATFORM U_PF_OS390 -#elif defined(__OS400__) || defined(__TOS_OS400__) -# define U_PLATFORM U_PF_OS400 -#elif defined(__EMSCRIPTEN__) -# define U_PLATFORM U_PF_EMSCRIPTEN -#else -# define U_PLATFORM U_PF_UNKNOWN -#endif - -/** - * \def CYGWINMSVC - * Defined if this is Windows with Cygwin, but using MSVC rather than gcc. - * Otherwise undefined. - * @internal - */ -/* Commented out because this is already set in mh-cygwin-msvc -#if U_PLATFORM == U_PF_CYGWIN && defined(_MSC_VER) -# define CYGWINMSVC -#endif -*/ -#ifdef U_IN_DOXYGEN -# define CYGWINMSVC -#endif - -/** - * \def U_PLATFORM_USES_ONLY_WIN32_API - * Defines whether the platform uses only the Win32 API. - * Set to 1 for Windows/MSVC and MinGW but not Cygwin. - * @internal - */ -#ifdef U_PLATFORM_USES_ONLY_WIN32_API - /* Use the predefined value. */ -#elif (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_MINGW) || defined(CYGWINMSVC) -# define U_PLATFORM_USES_ONLY_WIN32_API 1 -#else - /* Cygwin implements POSIX. */ -# define U_PLATFORM_USES_ONLY_WIN32_API 0 -#endif - -/** - * \def U_PLATFORM_HAS_WIN32_API - * Defines whether the Win32 API is available on the platform. - * Set to 1 for Windows/MSVC, MinGW and Cygwin. - * @internal - */ -#ifdef U_PLATFORM_HAS_WIN32_API - /* Use the predefined value. */ -#elif U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN -# define U_PLATFORM_HAS_WIN32_API 1 -#else -# define U_PLATFORM_HAS_WIN32_API 0 -#endif - -/** - * \def U_PLATFORM_HAS_WINUWP_API - * Defines whether target is intended for Universal Windows Platform API - * Set to 1 for Windows10 Release Solution Configuration - * @internal - */ -#ifdef U_PLATFORM_HAS_WINUWP_API - /* Use the predefined value. */ -#else -# define U_PLATFORM_HAS_WINUWP_API 0 -#endif - -/** - * \def U_PLATFORM_IMPLEMENTS_POSIX - * Defines whether the platform implements (most of) the POSIX API. - * Set to 1 for Cygwin and most other platforms. - * @internal - */ -#ifdef U_PLATFORM_IMPLEMENTS_POSIX - /* Use the predefined value. */ -#elif U_PLATFORM_USES_ONLY_WIN32_API -# define U_PLATFORM_IMPLEMENTS_POSIX 0 -#else -# define U_PLATFORM_IMPLEMENTS_POSIX 1 -#endif - -/** - * \def U_PLATFORM_IS_LINUX_BASED - * Defines whether the platform is Linux or one of its derivatives. - * @internal - */ -#ifdef U_PLATFORM_IS_LINUX_BASED - /* Use the predefined value. */ -#elif U_PF_LINUX <= U_PLATFORM && U_PLATFORM <= 4499 -# define U_PLATFORM_IS_LINUX_BASED 1 -#else -# define U_PLATFORM_IS_LINUX_BASED 0 -#endif - -/** - * \def U_PLATFORM_IS_DARWIN_BASED - * Defines whether the platform is Darwin or one of its derivatives. - * @internal - */ -#ifdef U_PLATFORM_IS_DARWIN_BASED - /* Use the predefined value. */ -#elif U_PF_DARWIN <= U_PLATFORM && U_PLATFORM <= U_PF_IPHONE -# define U_PLATFORM_IS_DARWIN_BASED 1 -#else -# define U_PLATFORM_IS_DARWIN_BASED 0 -#endif - -/** - * \def U_HAVE_STDINT_H - * Defines whether stdint.h is available. It is a C99 standard header. - * We used to include inttypes.h which includes stdint.h but we usually do not need - * the additional definitions from inttypes.h. - * @internal - */ -#ifdef U_HAVE_STDINT_H - /* Use the predefined value. */ -#elif U_PLATFORM_USES_ONLY_WIN32_API -# if defined(__BORLANDC__) || U_PLATFORM == U_PF_MINGW || (defined(_MSC_VER) && _MSC_VER>=1600) - /* Windows Visual Studio 9 and below do not have stdint.h & inttypes.h, but VS 2010 adds them. */ -# define U_HAVE_STDINT_H 1 -# else -# define U_HAVE_STDINT_H 0 -# endif -#elif U_PLATFORM == U_PF_SOLARIS - /* Solaris has inttypes.h but not stdint.h. */ -# define U_HAVE_STDINT_H 0 -#elif U_PLATFORM == U_PF_AIX && !defined(_AIX51) && defined(_POWER) - /* PPC AIX <= 4.3 has inttypes.h but not stdint.h. */ -# define U_HAVE_STDINT_H 0 -#else -# define U_HAVE_STDINT_H 1 -#endif - -/** - * \def U_HAVE_INTTYPES_H - * Defines whether inttypes.h is available. It is a C99 standard header. - * We include inttypes.h where it is available but stdint.h is not. - * @internal - */ -#ifdef U_HAVE_INTTYPES_H - /* Use the predefined value. */ -#elif U_PLATFORM == U_PF_SOLARIS - /* Solaris has inttypes.h but not stdint.h. */ -# define U_HAVE_INTTYPES_H 1 -#elif U_PLATFORM == U_PF_AIX && !defined(_AIX51) && defined(_POWER) - /* PPC AIX <= 4.3 has inttypes.h but not stdint.h. */ -# define U_HAVE_INTTYPES_H 1 -#else - /* Most platforms have both inttypes.h and stdint.h, or neither. */ -# define U_HAVE_INTTYPES_H U_HAVE_STDINT_H -#endif - -/*===========================================================================*/ -/** @{ Compiler and environment features */ -/*===========================================================================*/ - -/** - * \def U_GCC_MAJOR_MINOR - * Indicates whether the compiler is gcc (test for != 0), - * and if so, contains its major (times 100) and minor version numbers. - * If the compiler is not gcc, then U_GCC_MAJOR_MINOR == 0. - * - * For example, for testing for whether we have gcc, and whether it's 4.6 or higher, - * use "#if U_GCC_MAJOR_MINOR >= 406". - * @internal - */ -#ifdef __GNUC__ -# define U_GCC_MAJOR_MINOR (__GNUC__ * 100 + __GNUC_MINOR__) -#else -# define U_GCC_MAJOR_MINOR 0 -#endif - -/** - * \def U_IS_BIG_ENDIAN - * Determines the endianness of the platform. - * @internal - */ -#ifdef U_IS_BIG_ENDIAN - /* Use the predefined value. */ -#elif defined(BYTE_ORDER) && defined(BIG_ENDIAN) -# define U_IS_BIG_ENDIAN (BYTE_ORDER == BIG_ENDIAN) -#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) - /* gcc */ -# define U_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) -#elif defined(__BIG_ENDIAN__) || defined(_BIG_ENDIAN) -# define U_IS_BIG_ENDIAN 1 -#elif defined(__LITTLE_ENDIAN__) || defined(_LITTLE_ENDIAN) -# define U_IS_BIG_ENDIAN 0 -#elif U_PLATFORM == U_PF_OS390 || U_PLATFORM == U_PF_OS400 || defined(__s390__) || defined(__s390x__) - /* These platforms do not appear to predefine any endianness macros. */ -# define U_IS_BIG_ENDIAN 1 -#elif defined(_PA_RISC1_0) || defined(_PA_RISC1_1) || defined(_PA_RISC2_0) - /* HPPA do not appear to predefine any endianness macros. */ -# define U_IS_BIG_ENDIAN 1 -#elif defined(sparc) || defined(__sparc) || defined(__sparc__) - /* Some sparc based systems (e.g. Linux) do not predefine any endianness macros. */ -# define U_IS_BIG_ENDIAN 1 -#else -# define U_IS_BIG_ENDIAN 0 -#endif - -/** - * \def U_HAVE_PLACEMENT_NEW - * Determines whether to override placement new and delete for STL. - * @stable ICU 2.6 - */ -#ifdef U_HAVE_PLACEMENT_NEW - /* Use the predefined value. */ -#elif defined(__BORLANDC__) -# define U_HAVE_PLACEMENT_NEW 0 -#else -# define U_HAVE_PLACEMENT_NEW 1 -#endif - -/** - * \def U_HAVE_DEBUG_LOCATION_NEW - * Define this to define the MFC debug version of the operator new. - * - * @stable ICU 3.4 - */ -#ifdef U_HAVE_DEBUG_LOCATION_NEW - /* Use the predefined value. */ -#elif defined(_MSC_VER) -# define U_HAVE_DEBUG_LOCATION_NEW 1 -#else -# define U_HAVE_DEBUG_LOCATION_NEW 0 -#endif - -/* Compatibility with compilers other than clang: http://clang.llvm.org/docs/LanguageExtensions.html */ -#ifdef __has_attribute -# define UPRV_HAS_ATTRIBUTE(x) __has_attribute(x) -#else -# define UPRV_HAS_ATTRIBUTE(x) 0 -#endif -#ifdef __has_cpp_attribute -# define UPRV_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) -#else -# define UPRV_HAS_CPP_ATTRIBUTE(x) 0 -#endif -#ifdef __has_declspec_attribute -# define UPRV_HAS_DECLSPEC_ATTRIBUTE(x) __has_declspec_attribute(x) -#else -# define UPRV_HAS_DECLSPEC_ATTRIBUTE(x) 0 -#endif -#ifdef __has_builtin -# define UPRV_HAS_BUILTIN(x) __has_builtin(x) -#else -# define UPRV_HAS_BUILTIN(x) 0 -#endif -#ifdef __has_feature -# define UPRV_HAS_FEATURE(x) __has_feature(x) -#else -# define UPRV_HAS_FEATURE(x) 0 -#endif -#ifdef __has_extension -# define UPRV_HAS_EXTENSION(x) __has_extension(x) -#else -# define UPRV_HAS_EXTENSION(x) 0 -#endif -#ifdef __has_warning -# define UPRV_HAS_WARNING(x) __has_warning(x) -#else -# define UPRV_HAS_WARNING(x) 0 -#endif - -/** - * \def U_MALLOC_ATTR - * Attribute to mark functions as malloc-like - * @internal - */ -#if defined(__GNUC__) && __GNUC__>=3 -# define U_MALLOC_ATTR __attribute__ ((__malloc__)) -#else -# define U_MALLOC_ATTR -#endif - -/** - * \def U_ALLOC_SIZE_ATTR - * Attribute to specify the size of the allocated buffer for malloc-like functions - * @internal - */ -#if (defined(__GNUC__) && \ - (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) || \ - UPRV_HAS_ATTRIBUTE(alloc_size) -# define U_ALLOC_SIZE_ATTR(X) __attribute__ ((alloc_size(X))) -# define U_ALLOC_SIZE_ATTR2(X,Y) __attribute__ ((alloc_size(X,Y))) -#else -# define U_ALLOC_SIZE_ATTR(X) -# define U_ALLOC_SIZE_ATTR2(X,Y) -#endif - -/** - * \def U_CPLUSPLUS_VERSION - * 0 if no C++; 1, 11, 14, ... if C++. - * Support for specific features cannot always be determined by the C++ version alone. - * @internal - */ -#ifdef U_CPLUSPLUS_VERSION -# if U_CPLUSPLUS_VERSION != 0 && !defined(__cplusplus) -# undef U_CPLUSPLUS_VERSION -# define U_CPLUSPLUS_VERSION 0 -# endif - /* Otherwise use the predefined value. */ -#elif !defined(__cplusplus) -# define U_CPLUSPLUS_VERSION 0 -#elif __cplusplus >= 201402L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -# define U_CPLUSPLUS_VERSION 14 -#elif __cplusplus >= 201103L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L) -# define U_CPLUSPLUS_VERSION 11 -#else - // C++98 or C++03 -# define U_CPLUSPLUS_VERSION 1 -#endif - -#if (U_PLATFORM == U_PF_AIX || U_PLATFORM == U_PF_OS390) && defined(__cplusplus) &&(U_CPLUSPLUS_VERSION < 11) -// add in std::nullptr_t -namespace std { - typedef decltype(nullptr) nullptr_t; -}; -#endif - -/** - * \def U_NOEXCEPT - * "noexcept" if supported, otherwise empty. - * Some code, especially STL containers, uses move semantics of objects only - * if the move constructor and the move operator are declared as not throwing exceptions. - * @internal - */ -#ifdef U_NOEXCEPT - /* Use the predefined value. */ -#else -# define U_NOEXCEPT noexcept -#endif - -/** - * \def U_FALLTHROUGH - * Annotate intentional fall-through between switch labels. - * http://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough - * @internal - */ -#ifndef __cplusplus - // Not for C. -#elif defined(U_FALLTHROUGH) - // Use the predefined value. -#elif defined(__clang__) - // Test for compiler vs. feature separately. - // Other compilers might choke on the feature test. -# if UPRV_HAS_CPP_ATTRIBUTE(clang::fallthrough) || \ - (UPRV_HAS_FEATURE(cxx_attributes) && \ - UPRV_HAS_WARNING("-Wimplicit-fallthrough")) -# define U_FALLTHROUGH [[clang::fallthrough]] -# endif -#elif defined(__GNUC__) && (__GNUC__ >= 7) -# define U_FALLTHROUGH __attribute__((fallthrough)) -#endif - -#ifndef U_FALLTHROUGH -# define U_FALLTHROUGH -#endif - -/** @} */ - -/*===========================================================================*/ -/** @{ Character data types */ -/*===========================================================================*/ - -/** - * U_CHARSET_FAMILY is equal to this value when the platform is an ASCII based platform. - * @stable ICU 2.0 - */ -#define U_ASCII_FAMILY 0 - -/** - * U_CHARSET_FAMILY is equal to this value when the platform is an EBCDIC based platform. - * @stable ICU 2.0 - */ -#define U_EBCDIC_FAMILY 1 - -/** - * \def U_CHARSET_FAMILY - * - *

These definitions allow to specify the encoding of text - * in the char data type as defined by the platform and the compiler. - * It is enough to determine the code point values of "invariant characters", - * which are the ones shared by all encodings that are in use - * on a given platform.

- * - *

Those "invariant characters" should be all the uppercase and lowercase - * latin letters, the digits, the space, and "basic punctuation". - * Also, '\\n', '\\r', '\\t' should be available.

- * - *

The list of "invariant characters" is:
- * \code - * A-Z a-z 0-9 SPACE " % & ' ( ) * + , - . / : ; < = > ? _ - * \endcode - *
- * (52 letters + 10 numbers + 20 punc/sym/space = 82 total)

- * - *

This matches the IBM Syntactic Character Set (CS 640).

- * - *

In other words, all the graphic characters in 7-bit ASCII should - * be safely accessible except the following:

- * - * \code - * '\' - * '[' - * ']' - * '{' - * '}' - * '^' - * '~' - * '!' - * '#' - * '|' - * '$' - * '@' - * '`' - * \endcode - * @stable ICU 2.0 - */ -#ifdef U_CHARSET_FAMILY - /* Use the predefined value. */ -#elif U_PLATFORM == U_PF_OS390 && (!defined(__CHARSET_LIB) || !__CHARSET_LIB) -# define U_CHARSET_FAMILY U_EBCDIC_FAMILY -#elif U_PLATFORM == U_PF_OS400 && !defined(__UTF32__) -# define U_CHARSET_FAMILY U_EBCDIC_FAMILY -#else -# define U_CHARSET_FAMILY U_ASCII_FAMILY -#endif - -/** - * \def U_CHARSET_IS_UTF8 - * - * Hardcode the default charset to UTF-8. - * - * If this is set to 1, then - * - ICU will assume that all non-invariant char*, StringPiece, std::string etc. - * contain UTF-8 text, regardless of what the system API uses - * - some ICU code will use fast functions like u_strFromUTF8() - * rather than the more general and more heavy-weight conversion API (ucnv.h) - * - ucnv_getDefaultName() always returns "UTF-8" - * - ucnv_setDefaultName() is disabled and will not change the default charset - * - static builds of ICU are smaller - * - more functionality is available with the UCONFIG_NO_CONVERSION build-time - * configuration option (see unicode/uconfig.h) - * - the UCONFIG_NO_CONVERSION build option in uconfig.h is more usable - * - * @stable ICU 4.2 - * @see UCONFIG_NO_CONVERSION - */ -#ifdef U_CHARSET_IS_UTF8 - /* Use the predefined value. */ -#elif U_PLATFORM_IS_LINUX_BASED || U_PLATFORM_IS_DARWIN_BASED || \ - U_PLATFORM == U_PF_EMSCRIPTEN -# define U_CHARSET_IS_UTF8 1 -#else -# define U_CHARSET_IS_UTF8 0 -#endif - -/** @} */ - -/*===========================================================================*/ -/** @{ Information about wchar support */ -/*===========================================================================*/ - -/** - * \def U_HAVE_WCHAR_H - * Indicates whether is available (1) or not (0). Set to 1 by default. - * - * @stable ICU 2.0 - */ -#ifdef U_HAVE_WCHAR_H - /* Use the predefined value. */ -#elif U_PLATFORM == U_PF_ANDROID && __ANDROID_API__ < 9 - /* - * Android before Gingerbread (Android 2.3, API level 9) did not support wchar_t. - * The type and header existed, but the library functions did not work as expected. - * The size of wchar_t was 1 but L"xyz" string literals had 32-bit units anyway. - */ -# define U_HAVE_WCHAR_H 0 -#else -# define U_HAVE_WCHAR_H 1 -#endif - -/** - * \def U_SIZEOF_WCHAR_T - * U_SIZEOF_WCHAR_T==sizeof(wchar_t) - * - * @stable ICU 2.0 - */ -#ifdef U_SIZEOF_WCHAR_T - /* Use the predefined value. */ -#elif (U_PLATFORM == U_PF_ANDROID && __ANDROID_API__ < 9) - /* - * Classic Mac OS and Mac OS X before 10.3 (Panther) did not support wchar_t or wstring. - * Newer Mac OS X has size 4. - */ -# define U_SIZEOF_WCHAR_T 1 -#elif U_PLATFORM_HAS_WIN32_API || U_PLATFORM == U_PF_CYGWIN -# define U_SIZEOF_WCHAR_T 2 -#elif U_PLATFORM == U_PF_AIX - /* - * AIX 6.1 information, section "Wide character data representation": - * "... the wchar_t datatype is 32-bit in the 64-bit environment and - * 16-bit in the 32-bit environment." - * and - * "All locales use Unicode for their wide character code values (process code), - * except the IBM-eucTW codeset." - */ -# ifdef __64BIT__ -# define U_SIZEOF_WCHAR_T 4 -# else -# define U_SIZEOF_WCHAR_T 2 -# endif -#elif U_PLATFORM == U_PF_OS390 - /* - * z/OS V1R11 information center, section "LP64 | ILP32": - * "In 31-bit mode, the size of long and pointers is 4 bytes and the size of wchar_t is 2 bytes. - * Under LP64, the size of long and pointer is 8 bytes and the size of wchar_t is 4 bytes." - */ -# ifdef _LP64 -# define U_SIZEOF_WCHAR_T 4 -# else -# define U_SIZEOF_WCHAR_T 2 -# endif -#elif U_PLATFORM == U_PF_OS400 -# if defined(__UTF32__) - /* - * LOCALETYPE(*LOCALEUTF) is specified. - * Wide-character strings are in UTF-32, - * narrow-character strings are in UTF-8. - */ -# define U_SIZEOF_WCHAR_T 4 -# elif defined(__UCS2__) - /* - * LOCALETYPE(*LOCALEUCS2) is specified. - * Wide-character strings are in UCS-2, - * narrow-character strings are in EBCDIC. - */ -# define U_SIZEOF_WCHAR_T 2 -# else - /* - * LOCALETYPE(*CLD) or LOCALETYPE(*LOCALE) is specified. - * Wide-character strings are in 16-bit EBCDIC, - * narrow-character strings are in EBCDIC. - */ -# define U_SIZEOF_WCHAR_T 2 -# endif -#else -# define U_SIZEOF_WCHAR_T 4 -#endif - -#ifndef U_HAVE_WCSCPY -#define U_HAVE_WCSCPY U_HAVE_WCHAR_H -#endif - -/** @} */ - -/** - * \def U_HAVE_CHAR16_T - * Defines whether the char16_t type is available for UTF-16 - * and u"abc" UTF-16 string literals are supported. - * This is a new standard type and standard string literal syntax in C++0x - * but has been available in some compilers before. - * @internal - */ -#ifdef U_HAVE_CHAR16_T - /* Use the predefined value. */ -#else - /* - * Notes: - * Visual Studio 2010 (_MSC_VER==1600) defines char16_t as a typedef - * and does not support u"abc" string literals. - * Visual Studio 2015 (_MSC_VER>=1900) and above adds support for - * both char16_t and u"abc" string literals. - * gcc 4.4 defines the __CHAR16_TYPE__ macro to a usable type but - * does not support u"abc" string literals. - * C++11 and C11 require support for UTF-16 literals - * TODO: Fix for plain C. Doesn't work on Mac. - */ -# if U_CPLUSPLUS_VERSION >= 11 || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) -# define U_HAVE_CHAR16_T 1 -# else -# define U_HAVE_CHAR16_T 0 -# endif -#endif - -/** - * @{ - * \def U_DECLARE_UTF16 - * Do not use this macro because it is not defined on all platforms. - * Use the UNICODE_STRING or U_STRING_DECL macros instead. - * @internal - */ -#ifdef U_DECLARE_UTF16 - /* Use the predefined value. */ -#elif U_HAVE_CHAR16_T \ - || (defined(__xlC__) && defined(__IBM_UTF_LITERAL) && U_SIZEOF_WCHAR_T != 2) \ - || (defined(__HP_aCC) && __HP_aCC >= 035000) \ - || (defined(__HP_cc) && __HP_cc >= 111106) \ - || (defined(U_IN_DOXYGEN)) -# define U_DECLARE_UTF16(string) u ## string -#elif U_SIZEOF_WCHAR_T == 2 \ - && (U_CHARSET_FAMILY == 0 || (U_PF_OS390 <= U_PLATFORM && U_PLATFORM <= U_PF_OS400 && defined(__UCS2__))) -# define U_DECLARE_UTF16(string) L ## string -#else - /* Leave U_DECLARE_UTF16 undefined. See unistr.h. */ -#endif - -/** @} */ - -/*===========================================================================*/ -/** @{ Symbol import-export control */ -/*===========================================================================*/ - -#ifdef U_EXPORT - /* Use the predefined value. */ -#elif defined(U_STATIC_IMPLEMENTATION) -# define U_EXPORT -#elif defined(_MSC_VER) || (UPRV_HAS_DECLSPEC_ATTRIBUTE(__dllexport__) && \ - UPRV_HAS_DECLSPEC_ATTRIBUTE(__dllimport__)) -# define U_EXPORT __declspec(dllexport) -#elif defined(__GNUC__) -# define U_EXPORT __attribute__((visibility("default"))) -#elif (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x550) \ - || (defined(__SUNPRO_C) && __SUNPRO_C >= 0x550) -# define U_EXPORT __global -/*#elif defined(__HP_aCC) || defined(__HP_cc) -# define U_EXPORT __declspec(dllexport)*/ -#else -# define U_EXPORT -#endif - -/* U_CALLCONV is related to U_EXPORT2 */ -#ifdef U_EXPORT2 - /* Use the predefined value. */ -#elif defined(_MSC_VER) -# define U_EXPORT2 __cdecl -#else -# define U_EXPORT2 -#endif - -#ifdef U_IMPORT - /* Use the predefined value. */ -#elif defined(_MSC_VER) || (UPRV_HAS_DECLSPEC_ATTRIBUTE(__dllexport__) && \ - UPRV_HAS_DECLSPEC_ATTRIBUTE(__dllimport__)) - /* Windows needs to export/import data. */ -# define U_IMPORT __declspec(dllimport) -#else -# define U_IMPORT -#endif - -/** - * \def U_HIDDEN - * This is used to mark internal structs declared within external classes, - * to prevent the internal structs from having the same visibility as the - * class within which they are declared. - * @internal - */ -#ifdef U_HIDDEN - /* Use the predefined value. */ -#elif defined(__GNUC__) -# define U_HIDDEN __attribute__((visibility("hidden"))) -#else -# define U_HIDDEN -#endif - -/** - * \def U_CALLCONV - * Similar to U_CDECL_BEGIN/U_CDECL_END, this qualifier is necessary - * in callback function typedefs to make sure that the calling convention - * is compatible. - * - * This is only used for non-ICU-API functions. - * When a function is a public ICU API, - * you must use the U_CAPI and U_EXPORT2 qualifiers. - * - * Please note, you need to use U_CALLCONV after the *. - * - * NO : "static const char U_CALLCONV *func( . . . )" - * YES: "static const char* U_CALLCONV func( . . . )" - * - * @stable ICU 2.0 - */ -#if U_PLATFORM == U_PF_OS390 && defined(__cplusplus) -# define U_CALLCONV __cdecl -#else -# define U_CALLCONV U_EXPORT2 -#endif - -/** - * \def U_CALLCONV_FPTR - * Similar to U_CALLCONV, but only used on function pointers. - * @internal - */ -#if U_PLATFORM == U_PF_OS390 && defined(__cplusplus) -# define U_CALLCONV_FPTR U_CALLCONV -#else -# define U_CALLCONV_FPTR -#endif -/** @} */ - -#endif // _PLATFORM_H +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1997-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* FILE NAME : platform.h +* +* Date Name Description +* 05/13/98 nos Creation (content moved here from ptypes.h). +* 03/02/99 stephen Added AS400 support. +* 03/30/99 stephen Added Linux support. +* 04/13/99 stephen Reworked for autoconf. +****************************************************************************** +*/ + +#ifndef _PLATFORM_H +#define _PLATFORM_H + +#include "unicode/uconfig.h" +#include "unicode/uvernum.h" + +/** + * \file + * \brief Basic types for the platform. + * + * This file used to be generated by autoconf/configure. + * Starting with ICU 49, platform.h is a normal source file, + * to simplify cross-compiling and working with non-autoconf/make build systems. + * + * When a value in this file does not work on a platform, then please + * try to derive it from the U_PLATFORM value + * (for which we might need a new value constant in rare cases) + * and/or from other macros that are predefined by the compiler + * or defined in standard (POSIX or platform or compiler) headers. + * + * As a temporary workaround, you can add an explicit \#define for some macros + * before it is first tested, or add an equivalent -D macro definition + * to the compiler's command line. + * + * Note: Some compilers provide ways to show the predefined macros. + * For example, with gcc you can compile an empty .c file and have the compiler + * print the predefined macros with + * \code + * gcc -E -dM -x c /dev/null | sort + * \endcode + * (You can provide an actual empty .c file rather than /dev/null. + * -x c++ is for C++.) + */ + +/** + * Define some things so that they can be documented. + * @internal + */ +#ifdef U_IN_DOXYGEN +/* + * Problem: "platform.h:335: warning: documentation for unknown define U_HAVE_STD_STRING found." means that U_HAVE_STD_STRING is not documented. + * Solution: #define any defines for non @internal API here, so that they are visible in the docs. If you just set PREDEFINED in Doxyfile.in, they won't be documented. + */ + +/* None for now. */ +#endif + +/** + * \def U_PLATFORM + * The U_PLATFORM macro defines the platform we're on. + * + * We used to define one different, value-less macro per platform. + * That made it hard to know the set of relevant platforms and macros, + * and hard to deal with variants of platforms. + * + * Starting with ICU 49, we define platforms as numeric macros, + * with ranges of values for related platforms and their variants. + * The U_PLATFORM macro is set to one of these values. + * + * Historical note from the Solaris Wikipedia article: + * AT&T and Sun collaborated on a project to merge the most popular Unix variants + * on the market at that time: BSD, System V, and Xenix. + * This became Unix System V Release 4 (SVR4). + * + * @internal + */ + +/** Unknown platform. @internal */ +#define U_PF_UNKNOWN 0 +/** Windows @internal */ +#define U_PF_WINDOWS 1000 +/** MinGW. Windows, calls to Win32 API, but using GNU gcc and binutils. @internal */ +#define U_PF_MINGW 1800 +/** + * Cygwin. Windows, calls to cygwin1.dll for Posix functions, + * using MSVC or GNU gcc and binutils. + * @internal + */ +#define U_PF_CYGWIN 1900 +/* Reserve 2000 for U_PF_UNIX? */ +/** HP-UX is based on UNIX System V. @internal */ +#define U_PF_HPUX 2100 +/** Solaris is a Unix operating system based on SVR4. @internal */ +#define U_PF_SOLARIS 2600 +/** BSD is a UNIX operating system derivative. @internal */ +#define U_PF_BSD 3000 +/** AIX is based on UNIX System V Releases and 4.3 BSD. @internal */ +#define U_PF_AIX 3100 +/** IRIX is based on UNIX System V with BSD extensions. @internal */ +#define U_PF_IRIX 3200 +/** + * Darwin is a POSIX-compliant operating system, composed of code developed by Apple, + * as well as code derived from NeXTSTEP, BSD, and other projects, + * built around the Mach kernel. + * Darwin forms the core set of components upon which Mac OS X, Apple TV, and iOS are based. + * (Original description modified from WikiPedia.) + * @internal + */ +#define U_PF_DARWIN 3500 +/** iPhone OS (iOS) is a derivative of Mac OS X. @internal */ +#define U_PF_IPHONE 3550 +/** QNX is a commercial Unix-like real-time operating system related to BSD. @internal */ +#define U_PF_QNX 3700 +/** Linux is a Unix-like operating system. @internal */ +#define U_PF_LINUX 4000 +/** + * Native Client is pretty close to Linux. + * See https://developer.chrome.com/native-client and + * http://www.chromium.org/nativeclient + * @internal + */ +#define U_PF_BROWSER_NATIVE_CLIENT 4020 +/** Android is based on Linux. @internal */ +#define U_PF_ANDROID 4050 +/** Fuchsia is a POSIX-ish platform. @internal */ +#define U_PF_FUCHSIA 4100 +/* Maximum value for Linux-based platform is 4499 */ +/** + * Emscripten is a C++ transpiler for the Web that can target asm.js or + * WebAssembly. It provides some POSIX-compatible wrappers and stubs and + * some Linux-like functionality, but is not fully compatible with + * either. + * @internal + */ +#define U_PF_EMSCRIPTEN 5010 +/** z/OS is the successor to OS/390 which was the successor to MVS. @internal */ +#define U_PF_OS390 9000 +/** "IBM i" is the current name of what used to be i5/OS and earlier OS/400. @internal */ +#define U_PF_OS400 9400 + +#ifdef U_PLATFORM + /* Use the predefined value. */ +#elif defined(__MINGW32__) +# define U_PLATFORM U_PF_MINGW +#elif defined(__CYGWIN__) +# define U_PLATFORM U_PF_CYGWIN +#elif defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) +# define U_PLATFORM U_PF_WINDOWS +#elif defined(__ANDROID__) +# define U_PLATFORM U_PF_ANDROID + /* Android wchar_t support depends on the API level. */ +# include +#elif defined(__pnacl__) || defined(__native_client__) +# define U_PLATFORM U_PF_BROWSER_NATIVE_CLIENT +#elif defined(__Fuchsia__) +# define U_PLATFORM U_PF_FUCHSIA +#elif defined(linux) || defined(__linux__) || defined(__linux) +# define U_PLATFORM U_PF_LINUX +#elif defined(__APPLE__) && defined(__MACH__) +# include +# if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && (defined(TARGET_OS_MACCATALYST) && !TARGET_OS_MACCATALYST) /* variant of TARGET_OS_MAC */ +# define U_PLATFORM U_PF_IPHONE +# else +# define U_PLATFORM U_PF_DARWIN +# endif +#elif defined(BSD) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__MirBSD__) +# if defined(__FreeBSD__) +# include +# endif +# define U_PLATFORM U_PF_BSD +#elif defined(sun) || defined(__sun) + /* Check defined(__SVR4) || defined(__svr4__) to distinguish Solaris from SunOS? */ +# define U_PLATFORM U_PF_SOLARIS +# if defined(__GNUC__) + /* Solaris/GCC needs this header file to get the proper endianness. Normally, this + * header file is included with stddef.h but on Solairs/GCC, the GCC version of stddef.h + * is included which does not include this header file. + */ +# include +# endif +#elif defined(_AIX) || defined(__TOS_AIX__) +# define U_PLATFORM U_PF_AIX +#elif defined(_hpux) || defined(hpux) || defined(__hpux) +# define U_PLATFORM U_PF_HPUX +#elif defined(sgi) || defined(__sgi) +# define U_PLATFORM U_PF_IRIX +#elif defined(__QNX__) || defined(__QNXNTO__) +# define U_PLATFORM U_PF_QNX +#elif defined(__TOS_MVS__) +# define U_PLATFORM U_PF_OS390 +#elif defined(__OS400__) || defined(__TOS_OS400__) +# define U_PLATFORM U_PF_OS400 +#elif defined(__EMSCRIPTEN__) +# define U_PLATFORM U_PF_EMSCRIPTEN +#else +# define U_PLATFORM U_PF_UNKNOWN +#endif + +/** + * \def CYGWINMSVC + * Defined if this is Windows with Cygwin, but using MSVC rather than gcc. + * Otherwise undefined. + * @internal + */ +/* Commented out because this is already set in mh-cygwin-msvc +#if U_PLATFORM == U_PF_CYGWIN && defined(_MSC_VER) +# define CYGWINMSVC +#endif +*/ +#ifdef U_IN_DOXYGEN +# define CYGWINMSVC +#endif + +/** + * \def U_PLATFORM_USES_ONLY_WIN32_API + * Defines whether the platform uses only the Win32 API. + * Set to 1 for Windows/MSVC and MinGW but not Cygwin. + * @internal + */ +#ifdef U_PLATFORM_USES_ONLY_WIN32_API + /* Use the predefined value. */ +#elif (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_MINGW) || defined(CYGWINMSVC) +# define U_PLATFORM_USES_ONLY_WIN32_API 1 +#else + /* Cygwin implements POSIX. */ +# define U_PLATFORM_USES_ONLY_WIN32_API 0 +#endif + +/** + * \def U_PLATFORM_HAS_WIN32_API + * Defines whether the Win32 API is available on the platform. + * Set to 1 for Windows/MSVC, MinGW and Cygwin. + * @internal + */ +#ifdef U_PLATFORM_HAS_WIN32_API + /* Use the predefined value. */ +#elif U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +# define U_PLATFORM_HAS_WIN32_API 1 +#else +# define U_PLATFORM_HAS_WIN32_API 0 +#endif + +/** + * \def U_PLATFORM_HAS_WINUWP_API + * Defines whether target is intended for Universal Windows Platform API + * Set to 1 for Windows10 Release Solution Configuration + * @internal + */ +#ifdef U_PLATFORM_HAS_WINUWP_API + /* Use the predefined value. */ +#else +# define U_PLATFORM_HAS_WINUWP_API 0 +#endif + +/** + * \def U_PLATFORM_IMPLEMENTS_POSIX + * Defines whether the platform implements (most of) the POSIX API. + * Set to 1 for Cygwin and most other platforms. + * @internal + */ +#ifdef U_PLATFORM_IMPLEMENTS_POSIX + /* Use the predefined value. */ +#elif U_PLATFORM_USES_ONLY_WIN32_API +# define U_PLATFORM_IMPLEMENTS_POSIX 0 +#else +# define U_PLATFORM_IMPLEMENTS_POSIX 1 +#endif + +/** + * \def U_PLATFORM_IS_LINUX_BASED + * Defines whether the platform is Linux or one of its derivatives. + * @internal + */ +#ifdef U_PLATFORM_IS_LINUX_BASED + /* Use the predefined value. */ +#elif U_PF_LINUX <= U_PLATFORM && U_PLATFORM <= 4499 +# define U_PLATFORM_IS_LINUX_BASED 1 +#else +# define U_PLATFORM_IS_LINUX_BASED 0 +#endif + +/** + * \def U_PLATFORM_IS_DARWIN_BASED + * Defines whether the platform is Darwin or one of its derivatives. + * @internal + */ +#ifdef U_PLATFORM_IS_DARWIN_BASED + /* Use the predefined value. */ +#elif U_PF_DARWIN <= U_PLATFORM && U_PLATFORM <= U_PF_IPHONE +# define U_PLATFORM_IS_DARWIN_BASED 1 +#else +# define U_PLATFORM_IS_DARWIN_BASED 0 +#endif + +/** + * \def U_HAVE_STDINT_H + * Defines whether stdint.h is available. It is a C99 standard header. + * We used to include inttypes.h which includes stdint.h but we usually do not need + * the additional definitions from inttypes.h. + * @internal + */ +#ifdef U_HAVE_STDINT_H + /* Use the predefined value. */ +#elif U_PLATFORM_USES_ONLY_WIN32_API +# if defined(__BORLANDC__) || U_PLATFORM == U_PF_MINGW || (defined(_MSC_VER) && _MSC_VER>=1600) + /* Windows Visual Studio 9 and below do not have stdint.h & inttypes.h, but VS 2010 adds them. */ +# define U_HAVE_STDINT_H 1 +# else +# define U_HAVE_STDINT_H 0 +# endif +#elif U_PLATFORM == U_PF_SOLARIS + /* Solaris has inttypes.h but not stdint.h. */ +# define U_HAVE_STDINT_H 0 +#elif U_PLATFORM == U_PF_AIX && !defined(_AIX51) && defined(_POWER) + /* PPC AIX <= 4.3 has inttypes.h but not stdint.h. */ +# define U_HAVE_STDINT_H 0 +#else +# define U_HAVE_STDINT_H 1 +#endif + +/** + * \def U_HAVE_INTTYPES_H + * Defines whether inttypes.h is available. It is a C99 standard header. + * We include inttypes.h where it is available but stdint.h is not. + * @internal + */ +#ifdef U_HAVE_INTTYPES_H + /* Use the predefined value. */ +#elif U_PLATFORM == U_PF_SOLARIS + /* Solaris has inttypes.h but not stdint.h. */ +# define U_HAVE_INTTYPES_H 1 +#elif U_PLATFORM == U_PF_AIX && !defined(_AIX51) && defined(_POWER) + /* PPC AIX <= 4.3 has inttypes.h but not stdint.h. */ +# define U_HAVE_INTTYPES_H 1 +#else + /* Most platforms have both inttypes.h and stdint.h, or neither. */ +# define U_HAVE_INTTYPES_H U_HAVE_STDINT_H +#endif + +/*===========================================================================*/ +/** @{ Compiler and environment features */ +/*===========================================================================*/ + +/** + * \def U_GCC_MAJOR_MINOR + * Indicates whether the compiler is gcc (test for != 0), + * and if so, contains its major (times 100) and minor version numbers. + * If the compiler is not gcc, then U_GCC_MAJOR_MINOR == 0. + * + * For example, for testing for whether we have gcc, and whether it's 4.6 or higher, + * use "#if U_GCC_MAJOR_MINOR >= 406". + * @internal + */ +#ifdef __GNUC__ +# define U_GCC_MAJOR_MINOR (__GNUC__ * 100 + __GNUC_MINOR__) +#else +# define U_GCC_MAJOR_MINOR 0 +#endif + +/** + * \def U_IS_BIG_ENDIAN + * Determines the endianness of the platform. + * @internal + */ +#ifdef U_IS_BIG_ENDIAN + /* Use the predefined value. */ +#elif defined(BYTE_ORDER) && defined(BIG_ENDIAN) +# define U_IS_BIG_ENDIAN (BYTE_ORDER == BIG_ENDIAN) +#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) + /* gcc */ +# define U_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#elif defined(__BIG_ENDIAN__) || defined(_BIG_ENDIAN) +# define U_IS_BIG_ENDIAN 1 +#elif defined(__LITTLE_ENDIAN__) || defined(_LITTLE_ENDIAN) +# define U_IS_BIG_ENDIAN 0 +#elif U_PLATFORM == U_PF_OS390 || U_PLATFORM == U_PF_OS400 || defined(__s390__) || defined(__s390x__) + /* These platforms do not appear to predefine any endianness macros. */ +# define U_IS_BIG_ENDIAN 1 +#elif defined(_PA_RISC1_0) || defined(_PA_RISC1_1) || defined(_PA_RISC2_0) + /* HPPA do not appear to predefine any endianness macros. */ +# define U_IS_BIG_ENDIAN 1 +#elif defined(sparc) || defined(__sparc) || defined(__sparc__) + /* Some sparc based systems (e.g. Linux) do not predefine any endianness macros. */ +# define U_IS_BIG_ENDIAN 1 +#else +# define U_IS_BIG_ENDIAN 0 +#endif + +/** + * \def U_HAVE_PLACEMENT_NEW + * Determines whether to override placement new and delete for STL. + * @stable ICU 2.6 + */ +#ifdef U_HAVE_PLACEMENT_NEW + /* Use the predefined value. */ +#elif defined(__BORLANDC__) +# define U_HAVE_PLACEMENT_NEW 0 +#else +# define U_HAVE_PLACEMENT_NEW 1 +#endif + +/** + * \def U_HAVE_DEBUG_LOCATION_NEW + * Define this to define the MFC debug version of the operator new. + * + * @stable ICU 3.4 + */ +#ifdef U_HAVE_DEBUG_LOCATION_NEW + /* Use the predefined value. */ +#elif defined(_MSC_VER) +# define U_HAVE_DEBUG_LOCATION_NEW 1 +#else +# define U_HAVE_DEBUG_LOCATION_NEW 0 +#endif + +/* Compatibility with compilers other than clang: http://clang.llvm.org/docs/LanguageExtensions.html */ +#ifdef __has_attribute +# define UPRV_HAS_ATTRIBUTE(x) __has_attribute(x) +#else +# define UPRV_HAS_ATTRIBUTE(x) 0 +#endif +#ifdef __has_cpp_attribute +# define UPRV_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define UPRV_HAS_CPP_ATTRIBUTE(x) 0 +#endif +#ifdef __has_declspec_attribute +# define UPRV_HAS_DECLSPEC_ATTRIBUTE(x) __has_declspec_attribute(x) +#else +# define UPRV_HAS_DECLSPEC_ATTRIBUTE(x) 0 +#endif +#ifdef __has_builtin +# define UPRV_HAS_BUILTIN(x) __has_builtin(x) +#else +# define UPRV_HAS_BUILTIN(x) 0 +#endif +#ifdef __has_feature +# define UPRV_HAS_FEATURE(x) __has_feature(x) +#else +# define UPRV_HAS_FEATURE(x) 0 +#endif +#ifdef __has_extension +# define UPRV_HAS_EXTENSION(x) __has_extension(x) +#else +# define UPRV_HAS_EXTENSION(x) 0 +#endif +#ifdef __has_warning +# define UPRV_HAS_WARNING(x) __has_warning(x) +#else +# define UPRV_HAS_WARNING(x) 0 +#endif + + +#if defined(__clang__) +#define UPRV_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize("undefined"))) +#else +#define UPRV_NO_SANITIZE_UNDEFINED +#endif + +/** + * \def U_MALLOC_ATTR + * Attribute to mark functions as malloc-like + * @internal + */ +#if defined(__GNUC__) && __GNUC__>=3 +# define U_MALLOC_ATTR __attribute__ ((__malloc__)) +#else +# define U_MALLOC_ATTR +#endif + +/** + * \def U_ALLOC_SIZE_ATTR + * Attribute to specify the size of the allocated buffer for malloc-like functions + * @internal + */ +#if (defined(__GNUC__) && \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) || \ + UPRV_HAS_ATTRIBUTE(alloc_size) +# define U_ALLOC_SIZE_ATTR(X) __attribute__ ((alloc_size(X))) +# define U_ALLOC_SIZE_ATTR2(X,Y) __attribute__ ((alloc_size(X,Y))) +#else +# define U_ALLOC_SIZE_ATTR(X) +# define U_ALLOC_SIZE_ATTR2(X,Y) +#endif + +/** + * \def U_CPLUSPLUS_VERSION + * 0 if no C++; 1, 11, 14, ... if C++. + * Support for specific features cannot always be determined by the C++ version alone. + * @internal + */ +#ifdef U_CPLUSPLUS_VERSION +# if U_CPLUSPLUS_VERSION != 0 && !defined(__cplusplus) +# undef U_CPLUSPLUS_VERSION +# define U_CPLUSPLUS_VERSION 0 +# endif + /* Otherwise use the predefined value. */ +#elif !defined(__cplusplus) +# define U_CPLUSPLUS_VERSION 0 +#elif __cplusplus >= 201402L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define U_CPLUSPLUS_VERSION 14 +#elif __cplusplus >= 201103L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L) +# define U_CPLUSPLUS_VERSION 11 +#else + // C++98 or C++03 +# define U_CPLUSPLUS_VERSION 1 +#endif + +/** + * \def U_FALLTHROUGH + * Annotate intentional fall-through between switch labels. + * http://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough + * @internal + */ +#ifndef __cplusplus + // Not for C. +#elif defined(U_FALLTHROUGH) + // Use the predefined value. +#elif defined(__clang__) + // Test for compiler vs. feature separately. + // Other compilers might choke on the feature test. +# if UPRV_HAS_CPP_ATTRIBUTE(clang::fallthrough) || \ + (UPRV_HAS_FEATURE(cxx_attributes) && \ + UPRV_HAS_WARNING("-Wimplicit-fallthrough")) +# define U_FALLTHROUGH [[clang::fallthrough]] +# endif +#elif defined(__GNUC__) && (__GNUC__ >= 7) +# define U_FALLTHROUGH __attribute__((fallthrough)) +#endif + +#ifndef U_FALLTHROUGH +# define U_FALLTHROUGH +#endif + +/** @} */ + +/*===========================================================================*/ +/** @{ Character data types */ +/*===========================================================================*/ + +/** + * U_CHARSET_FAMILY is equal to this value when the platform is an ASCII based platform. + * @stable ICU 2.0 + */ +#define U_ASCII_FAMILY 0 + +/** + * U_CHARSET_FAMILY is equal to this value when the platform is an EBCDIC based platform. + * @stable ICU 2.0 + */ +#define U_EBCDIC_FAMILY 1 + +/** + * \def U_CHARSET_FAMILY + * + *

These definitions allow to specify the encoding of text + * in the char data type as defined by the platform and the compiler. + * It is enough to determine the code point values of "invariant characters", + * which are the ones shared by all encodings that are in use + * on a given platform.

+ * + *

Those "invariant characters" should be all the uppercase and lowercase + * latin letters, the digits, the space, and "basic punctuation". + * Also, '\\n', '\\r', '\\t' should be available.

+ * + *

The list of "invariant characters" is:
+ * \code + * A-Z a-z 0-9 SPACE " % & ' ( ) * + , - . / : ; < = > ? _ + * \endcode + *
+ * (52 letters + 10 numbers + 20 punc/sym/space = 82 total)

+ * + *

This matches the IBM Syntactic Character Set (CS 640).

+ * + *

In other words, all the graphic characters in 7-bit ASCII should + * be safely accessible except the following:

+ * + * \code + * '\' + * '[' + * ']' + * '{' + * '}' + * '^' + * '~' + * '!' + * '#' + * '|' + * '$' + * '@' + * '`' + * \endcode + * @stable ICU 2.0 + */ +#ifdef U_CHARSET_FAMILY + /* Use the predefined value. */ +#elif U_PLATFORM == U_PF_OS390 && (!defined(__CHARSET_LIB) || !__CHARSET_LIB) +# define U_CHARSET_FAMILY U_EBCDIC_FAMILY +#elif U_PLATFORM == U_PF_OS400 && !defined(__UTF32__) +# define U_CHARSET_FAMILY U_EBCDIC_FAMILY +#else +# define U_CHARSET_FAMILY U_ASCII_FAMILY +#endif + +/** + * \def U_CHARSET_IS_UTF8 + * + * Hardcode the default charset to UTF-8. + * + * If this is set to 1, then + * - ICU will assume that all non-invariant char*, StringPiece, std::string etc. + * contain UTF-8 text, regardless of what the system API uses + * - some ICU code will use fast functions like u_strFromUTF8() + * rather than the more general and more heavy-weight conversion API (ucnv.h) + * - ucnv_getDefaultName() always returns "UTF-8" + * - ucnv_setDefaultName() is disabled and will not change the default charset + * - static builds of ICU are smaller + * - more functionality is available with the UCONFIG_NO_CONVERSION build-time + * configuration option (see unicode/uconfig.h) + * - the UCONFIG_NO_CONVERSION build option in uconfig.h is more usable + * + * @stable ICU 4.2 + * @see UCONFIG_NO_CONVERSION + */ +#ifdef U_CHARSET_IS_UTF8 + /* Use the predefined value. */ +#elif U_PLATFORM_IS_LINUX_BASED || U_PLATFORM_IS_DARWIN_BASED || \ + U_PLATFORM == U_PF_EMSCRIPTEN +# define U_CHARSET_IS_UTF8 1 +#else +# define U_CHARSET_IS_UTF8 0 +#endif + +/** @} */ + +/*===========================================================================*/ +/** @{ Information about wchar support */ +/*===========================================================================*/ + +/** + * \def U_HAVE_WCHAR_H + * Indicates whether is available (1) or not (0). Set to 1 by default. + * + * @stable ICU 2.0 + */ +#ifdef U_HAVE_WCHAR_H + /* Use the predefined value. */ +#elif U_PLATFORM == U_PF_ANDROID && __ANDROID_API__ < 9 + /* + * Android before Gingerbread (Android 2.3, API level 9) did not support wchar_t. + * The type and header existed, but the library functions did not work as expected. + * The size of wchar_t was 1 but L"xyz" string literals had 32-bit units anyway. + */ +# define U_HAVE_WCHAR_H 0 +#else +# define U_HAVE_WCHAR_H 1 +#endif + +/** + * \def U_SIZEOF_WCHAR_T + * U_SIZEOF_WCHAR_T==sizeof(wchar_t) + * + * @stable ICU 2.0 + */ +#ifdef U_SIZEOF_WCHAR_T + /* Use the predefined value. */ +#elif (U_PLATFORM == U_PF_ANDROID && __ANDROID_API__ < 9) + /* + * Classic Mac OS and Mac OS X before 10.3 (Panther) did not support wchar_t or wstring. + * Newer Mac OS X has size 4. + */ +# define U_SIZEOF_WCHAR_T 1 +#elif U_PLATFORM_HAS_WIN32_API || U_PLATFORM == U_PF_CYGWIN +# define U_SIZEOF_WCHAR_T 2 +#elif U_PLATFORM == U_PF_AIX + /* + * AIX 6.1 information, section "Wide character data representation": + * "... the wchar_t datatype is 32-bit in the 64-bit environment and + * 16-bit in the 32-bit environment." + * and + * "All locales use Unicode for their wide character code values (process code), + * except the IBM-eucTW codeset." + */ +# ifdef __64BIT__ +# define U_SIZEOF_WCHAR_T 4 +# else +# define U_SIZEOF_WCHAR_T 2 +# endif +#elif U_PLATFORM == U_PF_OS390 + /* + * z/OS V1R11 information center, section "LP64 | ILP32": + * "In 31-bit mode, the size of long and pointers is 4 bytes and the size of wchar_t is 2 bytes. + * Under LP64, the size of long and pointer is 8 bytes and the size of wchar_t is 4 bytes." + */ +# ifdef _LP64 +# define U_SIZEOF_WCHAR_T 4 +# else +# define U_SIZEOF_WCHAR_T 2 +# endif +#elif U_PLATFORM == U_PF_OS400 +# if defined(__UTF32__) + /* + * LOCALETYPE(*LOCALEUTF) is specified. + * Wide-character strings are in UTF-32, + * narrow-character strings are in UTF-8. + */ +# define U_SIZEOF_WCHAR_T 4 +# elif defined(__UCS2__) + /* + * LOCALETYPE(*LOCALEUCS2) is specified. + * Wide-character strings are in UCS-2, + * narrow-character strings are in EBCDIC. + */ +# define U_SIZEOF_WCHAR_T 2 +# else + /* + * LOCALETYPE(*CLD) or LOCALETYPE(*LOCALE) is specified. + * Wide-character strings are in 16-bit EBCDIC, + * narrow-character strings are in EBCDIC. + */ +# define U_SIZEOF_WCHAR_T 2 +# endif +#else +# define U_SIZEOF_WCHAR_T 4 +#endif + +#ifndef U_HAVE_WCSCPY +#define U_HAVE_WCSCPY U_HAVE_WCHAR_H +#endif + +/** @} */ + +/** + * \def U_HAVE_CHAR16_T + * Defines whether the char16_t type is available for UTF-16 + * and u"abc" UTF-16 string literals are supported. + * This is a new standard type and standard string literal syntax in C++11 + * but has been available in some compilers before. + * @internal + */ +#ifdef U_HAVE_CHAR16_T + /* Use the predefined value. */ +#else + /* + * Notes: + * C++11 and C11 require support for UTF-16 literals + * TODO: Fix for plain C. Doesn't work on Mac. + */ +# if U_CPLUSPLUS_VERSION >= 11 || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) +# define U_HAVE_CHAR16_T 1 +# else +# define U_HAVE_CHAR16_T 0 +# endif +#endif + +/** + * @{ + * \def U_DECLARE_UTF16 + * Do not use this macro because it is not defined on all platforms. + * Use the UNICODE_STRING or U_STRING_DECL macros instead. + * @internal + */ +#ifdef U_DECLARE_UTF16 + /* Use the predefined value. */ +#elif U_HAVE_CHAR16_T \ + || (defined(__xlC__) && defined(__IBM_UTF_LITERAL) && U_SIZEOF_WCHAR_T != 2) \ + || (defined(__HP_aCC) && __HP_aCC >= 035000) \ + || (defined(__HP_cc) && __HP_cc >= 111106) \ + || (defined(U_IN_DOXYGEN)) +# define U_DECLARE_UTF16(string) u ## string +#elif U_SIZEOF_WCHAR_T == 2 \ + && (U_CHARSET_FAMILY == 0 || (U_PF_OS390 <= U_PLATFORM && U_PLATFORM <= U_PF_OS400 && defined(__UCS2__))) +# define U_DECLARE_UTF16(string) L ## string +#else + /* Leave U_DECLARE_UTF16 undefined. See unistr.h. */ +#endif + +/** @} */ + +/*===========================================================================*/ +/** @{ Symbol import-export control */ +/*===========================================================================*/ + +#ifdef U_EXPORT + /* Use the predefined value. */ +#elif defined(U_STATIC_IMPLEMENTATION) +# define U_EXPORT +#elif defined(_MSC_VER) || (UPRV_HAS_DECLSPEC_ATTRIBUTE(__dllexport__) && \ + UPRV_HAS_DECLSPEC_ATTRIBUTE(__dllimport__)) +# define U_EXPORT __declspec(dllexport) +#elif defined(__GNUC__) +# define U_EXPORT __attribute__((visibility("default"))) +#elif (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x550) \ + || (defined(__SUNPRO_C) && __SUNPRO_C >= 0x550) +# define U_EXPORT __global +/*#elif defined(__HP_aCC) || defined(__HP_cc) +# define U_EXPORT __declspec(dllexport)*/ +#else +# define U_EXPORT +#endif + +/* U_CALLCONV is related to U_EXPORT2 */ +#ifdef U_EXPORT2 + /* Use the predefined value. */ +#elif defined(_MSC_VER) +# define U_EXPORT2 __cdecl +#else +# define U_EXPORT2 +#endif + +#ifdef U_IMPORT + /* Use the predefined value. */ +#elif defined(_MSC_VER) || (UPRV_HAS_DECLSPEC_ATTRIBUTE(__dllexport__) && \ + UPRV_HAS_DECLSPEC_ATTRIBUTE(__dllimport__)) + /* Windows needs to export/import data. */ +# define U_IMPORT __declspec(dllimport) +#else +# define U_IMPORT +#endif + +/** + * \def U_HIDDEN + * This is used to mark internal structs declared within external classes, + * to prevent the internal structs from having the same visibility as the + * class within which they are declared. + * @internal + */ +#ifdef U_HIDDEN + /* Use the predefined value. */ +#elif defined(__GNUC__) +# define U_HIDDEN __attribute__((visibility("hidden"))) +#else +# define U_HIDDEN +#endif + +/** + * \def U_CALLCONV + * Similar to U_CDECL_BEGIN/U_CDECL_END, this qualifier is necessary + * in callback function typedefs to make sure that the calling convention + * is compatible. + * + * This is only used for non-ICU-API functions. + * When a function is a public ICU API, + * you must use the U_CAPI and U_EXPORT2 qualifiers. + * + * Please note, you need to use U_CALLCONV after the *. + * + * NO : "static const char U_CALLCONV *func( . . . )" + * YES: "static const char* U_CALLCONV func( . . . )" + * + * @stable ICU 2.0 + */ +#if U_PLATFORM == U_PF_OS390 && defined(__cplusplus) +# define U_CALLCONV __cdecl +#else +# define U_CALLCONV U_EXPORT2 +#endif + +/** + * \def U_CALLCONV_FPTR + * Similar to U_CALLCONV, but only used on function pointers. + * @internal + */ +#if U_PLATFORM == U_PF_OS390 && defined(__cplusplus) +# define U_CALLCONV_FPTR U_CALLCONV +#else +# define U_CALLCONV_FPTR +#endif +/** @} */ + +#endif // _PLATFORM_H diff --git a/deps/icu-small/source/common/unicode/ptypes.h b/deps/icu-small/source/common/unicode/ptypes.h index 70324ffee3b9c4..2e64f9190378ae 100644 --- a/deps/icu-small/source/common/unicode/ptypes.h +++ b/deps/icu-small/source/common/unicode/ptypes.h @@ -1,130 +1,130 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1997-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* FILE NAME : ptypes.h -* -* Date Name Description -* 05/13/98 nos Creation (content moved here from ptypes.h). -* 03/02/99 stephen Added AS400 support. -* 03/30/99 stephen Added Linux support. -* 04/13/99 stephen Reworked for autoconf. -* 09/18/08 srl Moved basic types back to ptypes.h from platform.h -****************************************************************************** -*/ - -/** - * \file - * \brief C API: Definitions of integer types of various widths - */ - -#ifndef _PTYPES_H -#define _PTYPES_H - -/** - * \def __STDC_LIMIT_MACROS - * According to the Linux stdint.h, the ISO C99 standard specifies that in C++ implementations - * macros like INT32_MIN and UINTPTR_MAX should only be defined if explicitly requested. - * We need to define __STDC_LIMIT_MACROS before including stdint.h in C++ code - * that uses such limit macros. - * @internal - */ -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS -#endif - -/* NULL, size_t, wchar_t */ -#include - -/* - * If all compilers provided all of the C99 headers and types, - * we would just unconditionally #include here - * and not need any of the stuff after including platform.h. - */ - -/* Find out if we have stdint.h etc. */ -#include "unicode/platform.h" - -/*===========================================================================*/ -/* Generic data types */ -/*===========================================================================*/ - -/* If your platform does not have the header, you may - need to edit the typedefs in the #else section below. - Use #if...#else...#endif with predefined compiler macros if possible. */ -#if U_HAVE_STDINT_H - -/* - * We mostly need (which defines the standard integer types) but not . - * includes and adds the printf/scanf helpers PRId32, SCNx16 etc. - * which we almost never use, plus stuff like imaxabs() which we never use. - */ -#include - -#if U_PLATFORM == U_PF_OS390 -/* The features header is needed to get (u)int64_t sometimes. */ -#include -/* z/OS has , but some versions are missing uint8_t (APAR PK62248). */ -#if !defined(__uint8_t) -#define __uint8_t 1 -typedef unsigned char uint8_t; -#endif -#endif /* U_PLATFORM == U_PF_OS390 */ - -#elif U_HAVE_INTTYPES_H - -# include - -#else /* neither U_HAVE_STDINT_H nor U_HAVE_INTTYPES_H */ - -/// \cond -#if ! U_HAVE_INT8_T -typedef signed char int8_t; -#endif - -#if ! U_HAVE_UINT8_T -typedef unsigned char uint8_t; -#endif - -#if ! U_HAVE_INT16_T -typedef signed short int16_t; -#endif - -#if ! U_HAVE_UINT16_T -typedef unsigned short uint16_t; -#endif - -#if ! U_HAVE_INT32_T -typedef signed int int32_t; -#endif - -#if ! U_HAVE_UINT32_T -typedef unsigned int uint32_t; -#endif - -#if ! U_HAVE_INT64_T -#ifdef _MSC_VER - typedef signed __int64 int64_t; -#else - typedef signed long long int64_t; -#endif -#endif - -#if ! U_HAVE_UINT64_T -#ifdef _MSC_VER - typedef unsigned __int64 uint64_t; -#else - typedef unsigned long long uint64_t; -#endif -#endif -/// \endcond - -#endif /* U_HAVE_STDINT_H / U_HAVE_INTTYPES_H */ - -#endif /* _PTYPES_H */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1997-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* FILE NAME : ptypes.h +* +* Date Name Description +* 05/13/98 nos Creation (content moved here from ptypes.h). +* 03/02/99 stephen Added AS400 support. +* 03/30/99 stephen Added Linux support. +* 04/13/99 stephen Reworked for autoconf. +* 09/18/08 srl Moved basic types back to ptypes.h from platform.h +****************************************************************************** +*/ + +/** + * \file + * \brief C API: Definitions of integer types of various widths + */ + +#ifndef _PTYPES_H +#define _PTYPES_H + +/** + * \def __STDC_LIMIT_MACROS + * According to the Linux stdint.h, the ISO C99 standard specifies that in C++ implementations + * macros like INT32_MIN and UINTPTR_MAX should only be defined if explicitly requested. + * We need to define __STDC_LIMIT_MACROS before including stdint.h in C++ code + * that uses such limit macros. + * @internal + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif + +/* NULL, size_t, wchar_t */ +#include + +/* + * If all compilers provided all of the C99 headers and types, + * we would just unconditionally #include here + * and not need any of the stuff after including platform.h. + */ + +/* Find out if we have stdint.h etc. */ +#include "unicode/platform.h" + +/*===========================================================================*/ +/* Generic data types */ +/*===========================================================================*/ + +/* If your platform does not have the header, you may + need to edit the typedefs in the #else section below. + Use #if...#else...#endif with predefined compiler macros if possible. */ +#if U_HAVE_STDINT_H + +/* + * We mostly need (which defines the standard integer types) but not . + * includes and adds the printf/scanf helpers PRId32, SCNx16 etc. + * which we almost never use, plus stuff like imaxabs() which we never use. + */ +#include + +#if U_PLATFORM == U_PF_OS390 +/* The features header is needed to get (u)int64_t sometimes. */ +#include +/* z/OS has , but some versions are missing uint8_t (APAR PK62248). */ +#if !defined(__uint8_t) +#define __uint8_t 1 +typedef unsigned char uint8_t; +#endif +#endif /* U_PLATFORM == U_PF_OS390 */ + +#elif U_HAVE_INTTYPES_H + +# include + +#else /* neither U_HAVE_STDINT_H nor U_HAVE_INTTYPES_H */ + +/// \cond +#if ! U_HAVE_INT8_T +typedef signed char int8_t; +#endif + +#if ! U_HAVE_UINT8_T +typedef unsigned char uint8_t; +#endif + +#if ! U_HAVE_INT16_T +typedef signed short int16_t; +#endif + +#if ! U_HAVE_UINT16_T +typedef unsigned short uint16_t; +#endif + +#if ! U_HAVE_INT32_T +typedef signed int int32_t; +#endif + +#if ! U_HAVE_UINT32_T +typedef unsigned int uint32_t; +#endif + +#if ! U_HAVE_INT64_T +#ifdef _MSC_VER + typedef signed __int64 int64_t; +#else + typedef signed long long int64_t; +#endif +#endif + +#if ! U_HAVE_UINT64_T +#ifdef _MSC_VER + typedef unsigned __int64 uint64_t; +#else + typedef unsigned long long uint64_t; +#endif +#endif +/// \endcond + +#endif /* U_HAVE_STDINT_H / U_HAVE_INTTYPES_H */ + +#endif /* _PTYPES_H */ diff --git a/deps/icu-small/source/common/unicode/putil.h b/deps/icu-small/source/common/unicode/putil.h index 500c21252fc2d7..043f8dd7868aac 100644 --- a/deps/icu-small/source/common/unicode/putil.h +++ b/deps/icu-small/source/common/unicode/putil.h @@ -1,183 +1,183 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1997-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* FILE NAME : putil.h -* -* Date Name Description -* 05/14/98 nos Creation (content moved here from utypes.h). -* 06/17/99 erm Added IEEE_754 -* 07/22/98 stephen Added IEEEremainder, max, min, trunc -* 08/13/98 stephen Added isNegativeInfinity, isPositiveInfinity -* 08/24/98 stephen Added longBitsFromDouble -* 03/02/99 stephen Removed openFile(). Added AS400 support. -* 04/15/99 stephen Converted to C -* 11/15/99 helena Integrated S/390 changes for IEEE support. -* 01/11/00 helena Added u_getVersion. -****************************************************************************** -*/ - -#ifndef PUTIL_H -#define PUTIL_H - -#include "unicode/utypes.h" - /** - * \file - * \brief C API: Platform Utilities - */ - -/*==========================================================================*/ -/* Platform utilities */ -/*==========================================================================*/ - -/** - * Platform utilities isolates the platform dependencies of the - * library. For each platform which this code is ported to, these - * functions may have to be re-implemented. - */ - -/** - * Return the ICU data directory. - * The data directory is where common format ICU data files (.dat files) - * are loaded from. Note that normal use of the built-in ICU - * facilities does not require loading of an external data file; - * unless you are adding custom data to ICU, the data directory - * does not need to be set. - * - * The data directory is determined as follows: - * If u_setDataDirectory() has been called, that is it, otherwise - * if the ICU_DATA environment variable is set, use that, otherwise - * If a data directory was specified at ICU build time - * - * \code - * #define ICU_DATA_DIR "path" - * \endcode - * use that, - * otherwise no data directory is available. - * - * @return the data directory, or an empty string ("") if no data directory has - * been specified. - * - * @stable ICU 2.0 - */ -U_CAPI const char* U_EXPORT2 u_getDataDirectory(void); - - -/** - * Set the ICU data directory. - * The data directory is where common format ICU data files (.dat files) - * are loaded from. Note that normal use of the built-in ICU - * facilities does not require loading of an external data file; - * unless you are adding custom data to ICU, the data directory - * does not need to be set. - * - * This function should be called at most once in a process, before the - * first ICU operation (e.g., u_init()) that will require the loading of an - * ICU data file. - * This function is not thread-safe. Use it before calling ICU APIs from - * multiple threads. - * - * @param directory The directory to be set. - * - * @see u_init - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 u_setDataDirectory(const char *directory); - -#ifndef U_HIDE_INTERNAL_API -/** - * Return the time zone files override directory, or an empty string if - * no directory was specified. Certain time zone resources will be preferentially - * loaded from individual files in this directory. - * - * @return the time zone data override directory. - * @internal - */ -U_CAPI const char * U_EXPORT2 u_getTimeZoneFilesDirectory(UErrorCode *status); - -/** - * Set the time zone files override directory. - * This function is not thread safe; it must not be called concurrently with - * u_getTimeZoneFilesDirectory() or any other use of ICU time zone functions. - * This function should only be called before using any ICU service that - * will access the time zone data. - * @internal - */ -U_CAPI void U_EXPORT2 u_setTimeZoneFilesDirectory(const char *path, UErrorCode *status); -#endif /* U_HIDE_INTERNAL_API */ - - -/** - * @{ - * Filesystem file and path separator characters. - * Example: '/' and ':' on Unix, '\\' and ';' on Windows. - * @stable ICU 2.0 - */ -#if U_PLATFORM_USES_ONLY_WIN32_API -# define U_FILE_SEP_CHAR '\\' -# define U_FILE_ALT_SEP_CHAR '/' -# define U_PATH_SEP_CHAR ';' -# define U_FILE_SEP_STRING "\\" -# define U_FILE_ALT_SEP_STRING "/" -# define U_PATH_SEP_STRING ";" -#else -# define U_FILE_SEP_CHAR '/' -# define U_FILE_ALT_SEP_CHAR '/' -# define U_PATH_SEP_CHAR ':' -# define U_FILE_SEP_STRING "/" -# define U_FILE_ALT_SEP_STRING "/" -# define U_PATH_SEP_STRING ":" -#endif - -/** @} */ - -/** - * Convert char characters to UChar characters. - * This utility function is useful only for "invariant characters" - * that are encoded in the platform default encoding. - * They are a small, constant subset of the encoding and include - * just the latin letters, digits, and some punctuation. - * For details, see U_CHARSET_FAMILY. - * - * @param cs Input string, points to length - * character bytes from a subset of the platform encoding. - * @param us Output string, points to memory for length - * Unicode characters. - * @param length The number of characters to convert; this may - * include the terminating NUL. - * - * @see U_CHARSET_FAMILY - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -u_charsToUChars(const char *cs, UChar *us, int32_t length); - -/** - * Convert UChar characters to char characters. - * This utility function is useful only for "invariant characters" - * that can be encoded in the platform default encoding. - * They are a small, constant subset of the encoding and include - * just the latin letters, digits, and some punctuation. - * For details, see U_CHARSET_FAMILY. - * - * @param us Input string, points to length - * Unicode characters that can be encoded with the - * codepage-invariant subset of the platform encoding. - * @param cs Output string, points to memory for length - * character bytes. - * @param length The number of characters to convert; this may - * include the terminating NUL. - * - * @see U_CHARSET_FAMILY - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -u_UCharsToChars(const UChar *us, char *cs, int32_t length); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1997-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* FILE NAME : putil.h +* +* Date Name Description +* 05/14/98 nos Creation (content moved here from utypes.h). +* 06/17/99 erm Added IEEE_754 +* 07/22/98 stephen Added IEEEremainder, max, min, trunc +* 08/13/98 stephen Added isNegativeInfinity, isPositiveInfinity +* 08/24/98 stephen Added longBitsFromDouble +* 03/02/99 stephen Removed openFile(). Added AS400 support. +* 04/15/99 stephen Converted to C +* 11/15/99 helena Integrated S/390 changes for IEEE support. +* 01/11/00 helena Added u_getVersion. +****************************************************************************** +*/ + +#ifndef PUTIL_H +#define PUTIL_H + +#include "unicode/utypes.h" + /** + * \file + * \brief C API: Platform Utilities + */ + +/*==========================================================================*/ +/* Platform utilities */ +/*==========================================================================*/ + +/** + * Platform utilities isolates the platform dependencies of the + * library. For each platform which this code is ported to, these + * functions may have to be re-implemented. + */ + +/** + * Return the ICU data directory. + * The data directory is where common format ICU data files (.dat files) + * are loaded from. Note that normal use of the built-in ICU + * facilities does not require loading of an external data file; + * unless you are adding custom data to ICU, the data directory + * does not need to be set. + * + * The data directory is determined as follows: + * If u_setDataDirectory() has been called, that is it, otherwise + * if the ICU_DATA environment variable is set, use that, otherwise + * If a data directory was specified at ICU build time + * + * \code + * #define ICU_DATA_DIR "path" + * \endcode + * use that, + * otherwise no data directory is available. + * + * @return the data directory, or an empty string ("") if no data directory has + * been specified. + * + * @stable ICU 2.0 + */ +U_CAPI const char* U_EXPORT2 u_getDataDirectory(void); + + +/** + * Set the ICU data directory. + * The data directory is where common format ICU data files (.dat files) + * are loaded from. Note that normal use of the built-in ICU + * facilities does not require loading of an external data file; + * unless you are adding custom data to ICU, the data directory + * does not need to be set. + * + * This function should be called at most once in a process, before the + * first ICU operation (e.g., u_init()) that will require the loading of an + * ICU data file. + * This function is not thread-safe. Use it before calling ICU APIs from + * multiple threads. + * + * @param directory The directory to be set. + * + * @see u_init + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 u_setDataDirectory(const char *directory); + +#ifndef U_HIDE_INTERNAL_API +/** + * Return the time zone files override directory, or an empty string if + * no directory was specified. Certain time zone resources will be preferentially + * loaded from individual files in this directory. + * + * @return the time zone data override directory. + * @internal + */ +U_CAPI const char * U_EXPORT2 u_getTimeZoneFilesDirectory(UErrorCode *status); + +/** + * Set the time zone files override directory. + * This function is not thread safe; it must not be called concurrently with + * u_getTimeZoneFilesDirectory() or any other use of ICU time zone functions. + * This function should only be called before using any ICU service that + * will access the time zone data. + * @internal + */ +U_CAPI void U_EXPORT2 u_setTimeZoneFilesDirectory(const char *path, UErrorCode *status); +#endif /* U_HIDE_INTERNAL_API */ + + +/** + * @{ + * Filesystem file and path separator characters. + * Example: '/' and ':' on Unix, '\\' and ';' on Windows. + * @stable ICU 2.0 + */ +#if U_PLATFORM_USES_ONLY_WIN32_API +# define U_FILE_SEP_CHAR '\\' +# define U_FILE_ALT_SEP_CHAR '/' +# define U_PATH_SEP_CHAR ';' +# define U_FILE_SEP_STRING "\\" +# define U_FILE_ALT_SEP_STRING "/" +# define U_PATH_SEP_STRING ";" +#else +# define U_FILE_SEP_CHAR '/' +# define U_FILE_ALT_SEP_CHAR '/' +# define U_PATH_SEP_CHAR ':' +# define U_FILE_SEP_STRING "/" +# define U_FILE_ALT_SEP_STRING "/" +# define U_PATH_SEP_STRING ":" +#endif + +/** @} */ + +/** + * Convert char characters to UChar characters. + * This utility function is useful only for "invariant characters" + * that are encoded in the platform default encoding. + * They are a small, constant subset of the encoding and include + * just the latin letters, digits, and some punctuation. + * For details, see U_CHARSET_FAMILY. + * + * @param cs Input string, points to length + * character bytes from a subset of the platform encoding. + * @param us Output string, points to memory for length + * Unicode characters. + * @param length The number of characters to convert; this may + * include the terminating NUL. + * + * @see U_CHARSET_FAMILY + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +u_charsToUChars(const char *cs, UChar *us, int32_t length); + +/** + * Convert UChar characters to char characters. + * This utility function is useful only for "invariant characters" + * that can be encoded in the platform default encoding. + * They are a small, constant subset of the encoding and include + * just the latin letters, digits, and some punctuation. + * For details, see U_CHARSET_FAMILY. + * + * @param us Input string, points to length + * Unicode characters that can be encoded with the + * codepage-invariant subset of the platform encoding. + * @param cs Output string, points to memory for length + * character bytes. + * @param length The number of characters to convert; this may + * include the terminating NUL. + * + * @see U_CHARSET_FAMILY + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +u_UCharsToChars(const UChar *us, char *cs, int32_t length); + +#endif diff --git a/deps/icu-small/source/common/unicode/rbbi.h b/deps/icu-small/source/common/unicode/rbbi.h index d878243e3fe247..e8ec0b39859308 100644 --- a/deps/icu-small/source/common/unicode/rbbi.h +++ b/deps/icu-small/source/common/unicode/rbbi.h @@ -1,745 +1,745 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -*************************************************************************** -* Copyright (C) 1999-2016 International Business Machines Corporation * -* and others. All rights reserved. * -*************************************************************************** - -********************************************************************** -* Date Name Description -* 10/22/99 alan Creation. -* 11/11/99 rgillam Complete port from Java. -********************************************************************** -*/ - -#ifndef RBBI_H -#define RBBI_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -/** - * \file - * \brief C++ API: Rule Based Break Iterator - */ - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/brkiter.h" -#include "unicode/udata.h" -#include "unicode/parseerr.h" -#include "unicode/schriter.h" - -struct UCPTrie; - -U_NAMESPACE_BEGIN - -/** @internal */ -class LanguageBreakEngine; -struct RBBIDataHeader; -class RBBIDataWrapper; -class UnhandledEngine; -class UStack; - -/** - * - * A subclass of BreakIterator whose behavior is specified using a list of rules. - *

Instances of this class are most commonly created by the factory methods of - * BreakIterator::createWordInstance(), BreakIterator::createLineInstance(), etc., - * and then used via the abstract API in class BreakIterator

- * - *

See the ICU User Guide for information on Break Iterator Rules.

- * - *

This class is not intended to be subclassed.

- */ -class U_COMMON_API RuleBasedBreakIterator /*U_FINAL*/ : public BreakIterator { - -private: - /** - * The UText through which this BreakIterator accesses the text - * @internal (private) - */ - UText fText; - -#ifndef U_HIDE_INTERNAL_API -public: -#endif /* U_HIDE_INTERNAL_API */ - /** - * The rule data for this BreakIterator instance. - * Not for general use; Public only for testing purposes. - * @internal - */ - RBBIDataWrapper *fData; -private: - - /** - * The current position of the iterator. Pinned, 0 < fPosition <= text.length. - * Never has the value UBRK_DONE (-1). - */ - int32_t fPosition; - - /** - * TODO: - */ - int32_t fRuleStatusIndex; - - /** - * Cache of previously determined boundary positions. - */ - class BreakCache; - BreakCache *fBreakCache; - - /** - * Cache of boundary positions within a region of text that has been - * sub-divided by dictionary based breaking. - */ - class DictionaryCache; - DictionaryCache *fDictionaryCache; - - /** - * - * If present, UStack of LanguageBreakEngine objects that might handle - * dictionary characters. Searched from top to bottom to find an object to - * handle a given character. - * @internal (private) - */ - UStack *fLanguageBreakEngines; - - /** - * - * If present, the special LanguageBreakEngine used for handling - * characters that are in the dictionary set, but not handled by any - * LanguageBreakEngine. - * @internal (private) - */ - UnhandledEngine *fUnhandledBreakEngine; - - /** - * Counter for the number of characters encountered with the "dictionary" - * flag set. - * @internal (private) - */ - uint32_t fDictionaryCharCount; - - /** - * A character iterator that refers to the same text as the UText, above. - * Only included for compatibility with old API, which was based on CharacterIterators. - * Value may be adopted from outside, or one of fSCharIter or fDCharIter, below. - */ - CharacterIterator *fCharIter; - - /** - * When the input text is provided by a UnicodeString, this will point to - * a characterIterator that wraps that data. Needed only for the - * implementation of getText(), a backwards compatibility issue. - */ - StringCharacterIterator fSCharIter; - - /** - * True when iteration has run off the end, and iterator functions should return UBRK_DONE. - */ - UBool fDone; - - /** - * Array of look-ahead tentative results. - */ - int32_t *fLookAheadMatches; - - /** - * A flag to indicate if phrase based breaking is enabled. - */ - UBool fIsPhraseBreaking; - - //======================================================================= - // constructors - //======================================================================= - - /** - * Constructor from a flattened set of RBBI data in malloced memory. - * RulesBasedBreakIterators built from a custom set of rules - * are created via this constructor; the rules are compiled - * into memory, then the break iterator is constructed here. - * - * The break iterator adopts the memory, and will - * free it when done. - * @internal (private) - */ - RuleBasedBreakIterator(RBBIDataHeader* data, UErrorCode &status); - - /** - * This constructor uses the udata interface to create a BreakIterator - * whose internal tables live in a memory-mapped file. "image" is an - * ICU UDataMemory handle for the pre-compiled break iterator tables. - * @param image handle to the memory image for the break iterator data. - * Ownership of the UDataMemory handle passes to the Break Iterator, - * which will be responsible for closing it when it is no longer needed. - * @param status Information on any errors encountered. - * @param isPhraseBreaking true if phrase based breaking is required, otherwise false. - * @see udata_open - * @see #getBinaryRules - * @internal (private) - */ - RuleBasedBreakIterator(UDataMemory* image, UBool isPhraseBreaking, UErrorCode &status); - - /** @internal */ - friend class RBBIRuleBuilder; - /** @internal */ - friend class BreakIterator; - -public: - - /** Default constructor. Creates an empty shell of an iterator, with no - * rules or text to iterate over. Object can subsequently be assigned to. - * @stable ICU 2.2 - */ - RuleBasedBreakIterator(); - - /** - * Copy constructor. Will produce a break iterator with the same behavior, - * and which iterates over the same text, as the one passed in. - * @param that The RuleBasedBreakIterator passed to be copied - * @stable ICU 2.0 - */ - RuleBasedBreakIterator(const RuleBasedBreakIterator& that); - - /** - * Construct a RuleBasedBreakIterator from a set of rules supplied as a string. - * @param rules The break rules to be used. - * @param parseError In the event of a syntax error in the rules, provides the location - * within the rules of the problem. - * @param status Information on any errors encountered. - * @stable ICU 2.2 - */ - RuleBasedBreakIterator( const UnicodeString &rules, - UParseError &parseError, - UErrorCode &status); - - /** - * Construct a RuleBasedBreakIterator from a set of precompiled binary rules. - * Binary rules are obtained from RulesBasedBreakIterator::getBinaryRules(). - * Construction of a break iterator in this way is substantially faster than - * construction from source rules. - * - * Ownership of the storage containing the compiled rules remains with the - * caller of this function. The compiled rules must not be modified or - * deleted during the life of the break iterator. - * - * The compiled rules are not compatible across different major versions of ICU. - * The compiled rules are compatible only between machines with the same - * byte ordering (little or big endian) and the same base character set family - * (ASCII or EBCDIC). - * - * @see #getBinaryRules - * @param compiledRules A pointer to the compiled break rules to be used. - * @param ruleLength The length of the compiled break rules, in bytes. This - * corresponds to the length value produced by getBinaryRules(). - * @param status Information on any errors encountered, including invalid - * binary rules. - * @stable ICU 4.8 - */ - RuleBasedBreakIterator(const uint8_t *compiledRules, - uint32_t ruleLength, - UErrorCode &status); - - /** - * This constructor uses the udata interface to create a BreakIterator - * whose internal tables live in a memory-mapped file. "image" is an - * ICU UDataMemory handle for the pre-compiled break iterator tables. - * @param image handle to the memory image for the break iterator data. - * Ownership of the UDataMemory handle passes to the Break Iterator, - * which will be responsible for closing it when it is no longer needed. - * @param status Information on any errors encountered. - * @see udata_open - * @see #getBinaryRules - * @stable ICU 2.8 - */ - RuleBasedBreakIterator(UDataMemory* image, UErrorCode &status); - - /** - * Destructor - * @stable ICU 2.0 - */ - virtual ~RuleBasedBreakIterator(); - - /** - * Assignment operator. Sets this iterator to have the same behavior, - * and iterate over the same text, as the one passed in. - * @param that The RuleBasedBreakItertor passed in - * @return the newly created RuleBasedBreakIterator - * @stable ICU 2.0 - */ - RuleBasedBreakIterator& operator=(const RuleBasedBreakIterator& that); - - /** - * Equality operator. Returns true if both BreakIterators are of the - * same class, have the same behavior, and iterate over the same text. - * @param that The BreakIterator to be compared for equality - * @return true if both BreakIterators are of the - * same class, have the same behavior, and iterate over the same text. - * @stable ICU 2.0 - */ - virtual bool operator==(const BreakIterator& that) const override; - - /** - * Not-equal operator. If operator== returns true, this returns false, - * and vice versa. - * @param that The BreakIterator to be compared for inequality - * @return true if both BreakIterators are not same. - * @stable ICU 2.0 - */ - inline bool operator!=(const BreakIterator& that) const; - - /** - * Returns a newly-constructed RuleBasedBreakIterator with the same - * behavior, and iterating over the same text, as this one. - * Differs from the copy constructor in that it is polymorphic, and - * will correctly clone (copy) a derived class. - * clone() is thread safe. Multiple threads may simultaneously - * clone the same source break iterator. - * @return a newly-constructed RuleBasedBreakIterator - * @stable ICU 2.0 - */ - virtual RuleBasedBreakIterator* clone() const override; - - /** - * Compute a hash code for this BreakIterator - * @return A hash code - * @stable ICU 2.0 - */ - virtual int32_t hashCode(void) const; - - /** - * Returns the description used to create this iterator - * @return the description used to create this iterator - * @stable ICU 2.0 - */ - virtual const UnicodeString& getRules(void) const; - - //======================================================================= - // BreakIterator overrides - //======================================================================= - - /** - *

- * Return a CharacterIterator over the text being analyzed. - * The returned character iterator is owned by the break iterator, and must - * not be deleted by the caller. Repeated calls to this function may - * return the same CharacterIterator. - *

- *

- * The returned character iterator must not be used concurrently with - * the break iterator. If concurrent operation is needed, clone the - * returned character iterator first and operate on the clone. - *

- *

- * When the break iterator is operating on text supplied via a UText, - * this function will fail. Lacking any way to signal failures, it - * returns an CharacterIterator containing no text. - * The function getUText() provides similar functionality, - * is reliable, and is more efficient. - *

- * - * TODO: deprecate this function? - * - * @return An iterator over the text being analyzed. - * @stable ICU 2.0 - */ - virtual CharacterIterator& getText(void) const override; - - - /** - * Get a UText for the text being analyzed. - * The returned UText is a shallow clone of the UText used internally - * by the break iterator implementation. It can safely be used to - * access the text without impacting any break iterator operations, - * but the underlying text itself must not be altered. - * - * @param fillIn A UText to be filled in. If NULL, a new UText will be - * allocated to hold the result. - * @param status receives any error codes. - * @return The current UText for this break iterator. If an input - * UText was provided, it will always be returned. - * @stable ICU 3.4 - */ - virtual UText *getUText(UText *fillIn, UErrorCode &status) const override; - - /** - * Set the iterator to analyze a new piece of text. This function resets - * the current iteration position to the beginning of the text. - * @param newText An iterator over the text to analyze. The BreakIterator - * takes ownership of the character iterator. The caller MUST NOT delete it! - * @stable ICU 2.0 - */ - virtual void adoptText(CharacterIterator* newText) override; - - /** - * Set the iterator to analyze a new piece of text. This function resets - * the current iteration position to the beginning of the text. - * - * The BreakIterator will retain a reference to the supplied string. - * The caller must not modify or delete the text while the BreakIterator - * retains the reference. - * - * @param newText The text to analyze. - * @stable ICU 2.0 - */ - virtual void setText(const UnicodeString& newText) override; - - /** - * Reset the break iterator to operate over the text represented by - * the UText. The iterator position is reset to the start. - * - * This function makes a shallow clone of the supplied UText. This means - * that the caller is free to immediately close or otherwise reuse the - * Utext that was passed as a parameter, but that the underlying text itself - * must not be altered while being referenced by the break iterator. - * - * @param text The UText used to change the text. - * @param status Receives any error codes. - * @stable ICU 3.4 - */ - virtual void setText(UText *text, UErrorCode &status) override; - - /** - * Sets the current iteration position to the beginning of the text, position zero. - * @return The offset of the beginning of the text, zero. - * @stable ICU 2.0 - */ - virtual int32_t first(void) override; - - /** - * Sets the current iteration position to the end of the text. - * @return The text's past-the-end offset. - * @stable ICU 2.0 - */ - virtual int32_t last(void) override; - - /** - * Advances the iterator either forward or backward the specified number of steps. - * Negative values move backward, and positive values move forward. This is - * equivalent to repeatedly calling next() or previous(). - * @param n The number of steps to move. The sign indicates the direction - * (negative is backwards, and positive is forwards). - * @return The character offset of the boundary position n boundaries away from - * the current one. - * @stable ICU 2.0 - */ - virtual int32_t next(int32_t n) override; - - /** - * Advances the iterator to the next boundary position. - * @return The position of the first boundary after this one. - * @stable ICU 2.0 - */ - virtual int32_t next(void) override; - - /** - * Moves the iterator backwards, to the last boundary preceding this one. - * @return The position of the last boundary position preceding this one. - * @stable ICU 2.0 - */ - virtual int32_t previous(void) override; - - /** - * Sets the iterator to refer to the first boundary position following - * the specified position. - * @param offset The position from which to begin searching for a break position. - * @return The position of the first break after the current position. - * @stable ICU 2.0 - */ - virtual int32_t following(int32_t offset) override; - - /** - * Sets the iterator to refer to the last boundary position before the - * specified position. - * @param offset The position to begin searching for a break from. - * @return The position of the last boundary before the starting position. - * @stable ICU 2.0 - */ - virtual int32_t preceding(int32_t offset) override; - - /** - * Returns true if the specified position is a boundary position. As a side - * effect, leaves the iterator pointing to the first boundary position at - * or after "offset". - * @param offset the offset to check. - * @return True if "offset" is a boundary position. - * @stable ICU 2.0 - */ - virtual UBool isBoundary(int32_t offset) override; - - /** - * Returns the current iteration position. Note that UBRK_DONE is never - * returned from this function; if iteration has run to the end of a - * string, current() will return the length of the string while - * next() will return UBRK_DONE). - * @return The current iteration position. - * @stable ICU 2.0 - */ - virtual int32_t current(void) const override; - - - /** - * Return the status tag from the break rule that determined the boundary at - * the current iteration position. For break rules that do not specify a - * status, a default value of 0 is returned. If more than one break rule - * would cause a boundary to be located at some position in the text, - * the numerically largest of the applicable status values is returned. - *

- * Of the standard types of ICU break iterators, only word break and - * line break provide status values. The values are defined in - * the header file ubrk.h. For Word breaks, the status allows distinguishing between words - * that contain alphabetic letters, "words" that appear to be numbers, - * punctuation and spaces, words containing ideographic characters, and - * more. For Line Break, the status distinguishes between hard (mandatory) breaks - * and soft (potential) break positions. - *

- * getRuleStatus() can be called after obtaining a boundary - * position from next(), previous(), or - * any other break iterator functions that returns a boundary position. - *

- * Note that getRuleStatus() returns the value corresponding to - * current() index even after next() has returned DONE. - *

- * When creating custom break rules, one is free to define whatever - * status values may be convenient for the application. - *

- * @return the status from the break rule that determined the boundary - * at the current iteration position. - * - * @see UWordBreak - * @stable ICU 2.2 - */ - virtual int32_t getRuleStatus() const override; - - /** - * Get the status (tag) values from the break rule(s) that determined the boundary - * at the current iteration position. - *

- * The returned status value(s) are stored into an array provided by the caller. - * The values are stored in sorted (ascending) order. - * If the capacity of the output array is insufficient to hold the data, - * the output will be truncated to the available length, and a - * U_BUFFER_OVERFLOW_ERROR will be signaled. - * - * @param fillInVec an array to be filled in with the status values. - * @param capacity the length of the supplied vector. A length of zero causes - * the function to return the number of status values, in the - * normal way, without attempting to store any values. - * @param status receives error codes. - * @return The number of rule status values from the rules that determined - * the boundary at the current iteration position. - * In the event of a U_BUFFER_OVERFLOW_ERROR, the return value - * is the total number of status values that were available, - * not the reduced number that were actually returned. - * @see getRuleStatus - * @stable ICU 3.0 - */ - virtual int32_t getRuleStatusVec(int32_t *fillInVec, int32_t capacity, UErrorCode &status) override; - - /** - * Returns a unique class ID POLYMORPHICALLY. Pure virtual override. - * This method is to implement a simple version of RTTI, since not all - * C++ compilers support genuine RTTI. Polymorphic operator==() and - * clone() methods call this method. - * - * @return The class ID for this object. All objects of a - * given class have the same class ID. Objects of - * other classes have different class IDs. - * @stable ICU 2.0 - */ - virtual UClassID getDynamicClassID(void) const override; - - /** - * Returns the class ID for this class. This is useful only for - * comparing to a return value from getDynamicClassID(). For example: - * - * Base* polymorphic_pointer = createPolymorphicObject(); - * if (polymorphic_pointer->getDynamicClassID() == - * Derived::getStaticClassID()) ... - * - * @return The class ID for all objects of this class. - * @stable ICU 2.0 - */ - static UClassID U_EXPORT2 getStaticClassID(void); - -#ifndef U_FORCE_HIDE_DEPRECATED_API - /** - * Deprecated functionality. Use clone() instead. - * - * Create a clone (copy) of this break iterator in memory provided - * by the caller. The idea is to increase performance by avoiding - * a storage allocation. Use of this function is NOT RECOMMENDED. - * Performance gains are minimal, and correct buffer management is - * tricky. Use clone() instead. - * - * @param stackBuffer The pointer to the memory into which the cloned object - * should be placed. If NULL, allocate heap memory - * for the cloned object. - * @param BufferSize The size of the buffer. If zero, return the required - * buffer size, but do not clone the object. If the - * size was too small (but not zero), allocate heap - * storage for the cloned object. - * - * @param status Error status. U_SAFECLONE_ALLOCATED_WARNING will be - * returned if the provided buffer was too small, and - * the clone was therefore put on the heap. - * - * @return Pointer to the clone object. This may differ from the stackBuffer - * address if the byte alignment of the stack buffer was not suitable - * or if the stackBuffer was too small to hold the clone. - * @deprecated ICU 52. Use clone() instead. - */ - virtual RuleBasedBreakIterator *createBufferClone(void *stackBuffer, - int32_t &BufferSize, - UErrorCode &status) override; -#endif // U_FORCE_HIDE_DEPRECATED_API - - /** - * Return the binary form of compiled break rules, - * which can then be used to create a new break iterator at some - * time in the future. Creating a break iterator from pre-compiled rules - * is much faster than building one from the source form of the - * break rules. - * - * The binary data can only be used with the same version of ICU - * and on the same platform type (processor endian-ness) - * - * @param length Returns the length of the binary data. (Out parameter.) - * - * @return A pointer to the binary (compiled) rule data. The storage - * belongs to the RulesBasedBreakIterator object, not the - * caller, and must not be modified or deleted. - * @stable ICU 4.8 - */ - virtual const uint8_t *getBinaryRules(uint32_t &length); - - /** - * Set the subject text string upon which the break iterator is operating - * without changing any other aspect of the matching state. - * The new and previous text strings must have the same content. - * - * This function is intended for use in environments where ICU is operating on - * strings that may move around in memory. It provides a mechanism for notifying - * ICU that the string has been relocated, and providing a new UText to access the - * string in its new position. - * - * Note that the break iterator implementation never copies the underlying text - * of a string being processed, but always operates directly on the original text - * provided by the user. Refreshing simply drops the references to the old text - * and replaces them with references to the new. - * - * Caution: this function is normally used only by very specialized, - * system-level code. One example use case is with garbage collection that moves - * the text in memory. - * - * @param input The new (moved) text string. - * @param status Receives errors detected by this function. - * @return *this - * - * @stable ICU 49 - */ - virtual RuleBasedBreakIterator &refreshInputText(UText *input, UErrorCode &status) override; - - -private: - //======================================================================= - // implementation - //======================================================================= - /** - * Common initialization function, used by constructors and bufferClone. - * @internal (private) - */ - void init(UErrorCode &status); - - /** - * 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 (private) - */ - int32_t handleSafePrevious(int32_t fromPosition); - - /** - * Find a rule-based boundary by running the state machine. - * Input - * fPosition, the position in the text to begin from. - * Output - * fPosition: the boundary following the starting position. - * fDictionaryCharCount the number of dictionary characters encountered. - * If > 0, the segment will be further subdivided - * fRuleStatusIndex Info from the state table indicating which rules caused the boundary. - * - * @internal (private) - */ - int32_t handleNext(); - - /* - * Templatized version of handleNext() and handleSafePrevious(). - * - * There will be exactly four instantiations, two each for 8 and 16 bit tables, - * two each for 8 and 16 bit trie. - * Having separate instantiations for the table types keeps conditional tests of - * the table type out of the inner loops, at the expense of replicated code. - * - * The template parameter for the Trie access function is a value, not a type. - * Doing it this way, the compiler will inline the Trie function in the - * expanded functions. (Both the 8 and 16 bit access functions have the same type - * signature) - */ - - typedef uint16_t (*PTrieFunc)(const UCPTrie *, UChar32); - - template - int32_t handleSafePrevious(int32_t fromPosition); - - template - int32_t handleNext(); - - - /** - * This function returns the appropriate LanguageBreakEngine for a - * given character c. - * @param c A character in the dictionary set - * @internal (private) - */ - const LanguageBreakEngine *getLanguageBreakEngine(UChar32 c); - - public: -#ifndef U_HIDE_INTERNAL_API - /** - * Debugging function only. - * @internal - */ - void dumpCache(); - - /** - * Debugging function only. - * @internal - */ - void dumpTables(); -#endif /* U_HIDE_INTERNAL_API */ -}; - -//------------------------------------------------------------------------------ -// -// Inline Functions Definitions ... -// -//------------------------------------------------------------------------------ - -inline bool RuleBasedBreakIterator::operator!=(const BreakIterator& that) const { - return !operator==(that); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +*************************************************************************** +* Copyright (C) 1999-2016 International Business Machines Corporation * +* and others. All rights reserved. * +*************************************************************************** + +********************************************************************** +* Date Name Description +* 10/22/99 alan Creation. +* 11/11/99 rgillam Complete port from Java. +********************************************************************** +*/ + +#ifndef RBBI_H +#define RBBI_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +/** + * \file + * \brief C++ API: Rule Based Break Iterator + */ + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/brkiter.h" +#include "unicode/udata.h" +#include "unicode/parseerr.h" +#include "unicode/schriter.h" + +struct UCPTrie; + +U_NAMESPACE_BEGIN + +/** @internal */ +class LanguageBreakEngine; +struct RBBIDataHeader; +class RBBIDataWrapper; +class UnhandledEngine; +class UStack; + +/** + * + * A subclass of BreakIterator whose behavior is specified using a list of rules. + *

Instances of this class are most commonly created by the factory methods of + * BreakIterator::createWordInstance(), BreakIterator::createLineInstance(), etc., + * and then used via the abstract API in class BreakIterator

+ * + *

See the ICU User Guide for information on Break Iterator Rules.

+ * + *

This class is not intended to be subclassed.

+ */ +class U_COMMON_API RuleBasedBreakIterator /*final*/ : public BreakIterator { + +private: + /** + * The UText through which this BreakIterator accesses the text + * @internal (private) + */ + UText fText = UTEXT_INITIALIZER; + +#ifndef U_HIDE_INTERNAL_API +public: +#endif /* U_HIDE_INTERNAL_API */ + /** + * The rule data for this BreakIterator instance. + * Not for general use; Public only for testing purposes. + * @internal + */ + RBBIDataWrapper *fData = nullptr; + +private: + /** + * The saved error code associated with this break iterator. + * This is the value to be returned by copyErrorTo(). + */ + UErrorCode fErrorCode = U_ZERO_ERROR; + + /** + * The current position of the iterator. Pinned, 0 < fPosition <= text.length. + * Never has the value UBRK_DONE (-1). + */ + int32_t fPosition = 0; + + /** + * TODO: + */ + int32_t fRuleStatusIndex = 0; + + /** + * Cache of previously determined boundary positions. + */ + class BreakCache; + BreakCache *fBreakCache = nullptr; + + /** + * Cache of boundary positions within a region of text that has been + * sub-divided by dictionary based breaking. + */ + class DictionaryCache; + DictionaryCache *fDictionaryCache = nullptr; + + /** + * + * If present, UStack of LanguageBreakEngine objects that might handle + * dictionary characters. Searched from top to bottom to find an object to + * handle a given character. + * @internal (private) + */ + UStack *fLanguageBreakEngines = nullptr; + + /** + * + * If present, the special LanguageBreakEngine used for handling + * characters that are in the dictionary set, but not handled by any + * LanguageBreakEngine. + * @internal (private) + */ + UnhandledEngine *fUnhandledBreakEngine = nullptr; + + /** + * Counter for the number of characters encountered with the "dictionary" + * flag set. + * @internal (private) + */ + uint32_t fDictionaryCharCount = 0; + + /** + * A character iterator that refers to the same text as the UText, above. + * Only included for compatibility with old API, which was based on CharacterIterators. + * Value may be adopted from outside, or one of fSCharIter or fDCharIter, below. + */ + CharacterIterator *fCharIter = &fSCharIter; + + /** + * When the input text is provided by a UnicodeString, this will point to + * a characterIterator that wraps that data. Needed only for the + * implementation of getText(), a backwards compatibility issue. + */ + UCharCharacterIterator fSCharIter {u"", 0}; + + /** + * True when iteration has run off the end, and iterator functions should return UBRK_DONE. + */ + bool fDone = false; + + /** + * Array of look-ahead tentative results. + */ + int32_t *fLookAheadMatches = nullptr; + + /** + * A flag to indicate if phrase based breaking is enabled. + */ + UBool fIsPhraseBreaking = false; + + //======================================================================= + // constructors + //======================================================================= + + /** + * Constructor from a flattened set of RBBI data in malloced memory. + * RulesBasedBreakIterators built from a custom set of rules + * are created via this constructor; the rules are compiled + * into memory, then the break iterator is constructed here. + * + * The break iterator adopts the memory, and will + * free it when done. + * @internal (private) + */ + RuleBasedBreakIterator(RBBIDataHeader* data, UErrorCode &status); + + /** + * This constructor uses the udata interface to create a BreakIterator + * whose internal tables live in a memory-mapped file. "image" is an + * ICU UDataMemory handle for the pre-compiled break iterator tables. + * @param image handle to the memory image for the break iterator data. + * Ownership of the UDataMemory handle passes to the Break Iterator, + * which will be responsible for closing it when it is no longer needed. + * @param status Information on any errors encountered. + * @param isPhraseBreaking true if phrase based breaking is required, otherwise false. + * @see udata_open + * @see #getBinaryRules + * @internal (private) + */ + RuleBasedBreakIterator(UDataMemory* image, UBool isPhraseBreaking, UErrorCode &status); + + /** @internal */ + friend class RBBIRuleBuilder; + /** @internal */ + friend class BreakIterator; + + /** + * Default constructor with an error code parameter. + * Aside from error handling, otherwise identical to the default constructor. + * Internally, handles common initialization for other constructors. + * @internal (private) + */ + RuleBasedBreakIterator(UErrorCode *status); + +public: + + /** Default constructor. Creates an empty shell of an iterator, with no + * rules or text to iterate over. Object can subsequently be assigned to, + * but is otherwise unusable. + * @stable ICU 2.2 + */ + RuleBasedBreakIterator(); + + /** + * Copy constructor. Will produce a break iterator with the same behavior, + * and which iterates over the same text, as the one passed in. + * @param that The RuleBasedBreakIterator passed to be copied + * @stable ICU 2.0 + */ + RuleBasedBreakIterator(const RuleBasedBreakIterator& that); + + /** + * Construct a RuleBasedBreakIterator from a set of rules supplied as a string. + * @param rules The break rules to be used. + * @param parseError In the event of a syntax error in the rules, provides the location + * within the rules of the problem. + * @param status Information on any errors encountered. + * @stable ICU 2.2 + */ + RuleBasedBreakIterator( const UnicodeString &rules, + UParseError &parseError, + UErrorCode &status); + + /** + * Construct a RuleBasedBreakIterator from a set of precompiled binary rules. + * Binary rules are obtained from RulesBasedBreakIterator::getBinaryRules(). + * Construction of a break iterator in this way is substantially faster than + * construction from source rules. + * + * Ownership of the storage containing the compiled rules remains with the + * caller of this function. The compiled rules must not be modified or + * deleted during the life of the break iterator. + * + * The compiled rules are not compatible across different major versions of ICU. + * The compiled rules are compatible only between machines with the same + * byte ordering (little or big endian) and the same base character set family + * (ASCII or EBCDIC). + * + * @see #getBinaryRules + * @param compiledRules A pointer to the compiled break rules to be used. + * @param ruleLength The length of the compiled break rules, in bytes. This + * corresponds to the length value produced by getBinaryRules(). + * @param status Information on any errors encountered, including invalid + * binary rules. + * @stable ICU 4.8 + */ + RuleBasedBreakIterator(const uint8_t *compiledRules, + uint32_t ruleLength, + UErrorCode &status); + + /** + * This constructor uses the udata interface to create a BreakIterator + * whose internal tables live in a memory-mapped file. "image" is an + * ICU UDataMemory handle for the pre-compiled break iterator tables. + * @param image handle to the memory image for the break iterator data. + * Ownership of the UDataMemory handle passes to the Break Iterator, + * which will be responsible for closing it when it is no longer needed. + * @param status Information on any errors encountered. + * @see udata_open + * @see #getBinaryRules + * @stable ICU 2.8 + */ + RuleBasedBreakIterator(UDataMemory* image, UErrorCode &status); + + /** + * Destructor + * @stable ICU 2.0 + */ + virtual ~RuleBasedBreakIterator(); + + /** + * Assignment operator. Sets this iterator to have the same behavior, + * and iterate over the same text, as the one passed in. + * @param that The RuleBasedBreakItertor passed in + * @return the newly created RuleBasedBreakIterator + * @stable ICU 2.0 + */ + RuleBasedBreakIterator& operator=(const RuleBasedBreakIterator& that); + + /** + * Equality operator. Returns true if both BreakIterators are of the + * same class, have the same behavior, and iterate over the same text. + * @param that The BreakIterator to be compared for equality + * @return true if both BreakIterators are of the + * same class, have the same behavior, and iterate over the same text. + * @stable ICU 2.0 + */ + virtual bool operator==(const BreakIterator& that) const override; + + /** + * Not-equal operator. If operator== returns true, this returns false, + * and vice versa. + * @param that The BreakIterator to be compared for inequality + * @return true if both BreakIterators are not same. + * @stable ICU 2.0 + */ + inline bool operator!=(const BreakIterator& that) const { + return !operator==(that); + } + + /** + * Returns a newly-constructed RuleBasedBreakIterator with the same + * behavior, and iterating over the same text, as this one. + * Differs from the copy constructor in that it is polymorphic, and + * will correctly clone (copy) a derived class. + * clone() is thread safe. Multiple threads may simultaneously + * clone the same source break iterator. + * @return a newly-constructed RuleBasedBreakIterator + * @stable ICU 2.0 + */ + virtual RuleBasedBreakIterator* clone() const override; + + /** + * Compute a hash code for this BreakIterator + * @return A hash code + * @stable ICU 2.0 + */ + virtual int32_t hashCode(void) const; + + /** + * Returns the description used to create this iterator + * @return the description used to create this iterator + * @stable ICU 2.0 + */ + virtual const UnicodeString& getRules(void) const; + + //======================================================================= + // BreakIterator overrides + //======================================================================= + + /** + *

+ * Return a CharacterIterator over the text being analyzed. + * The returned character iterator is owned by the break iterator, and must + * not be deleted by the caller. Repeated calls to this function may + * return the same CharacterIterator. + *

+ *

+ * The returned character iterator must not be used concurrently with + * the break iterator. If concurrent operation is needed, clone the + * returned character iterator first and operate on the clone. + *

+ *

+ * When the break iterator is operating on text supplied via a UText, + * this function will fail, returning a CharacterIterator containing no text. + * The function getUText() provides similar functionality, + * is reliable, and is more efficient. + *

+ * + * TODO: deprecate this function? + * + * @return An iterator over the text being analyzed. + * @stable ICU 2.0 + */ + virtual CharacterIterator& getText(void) const override; + + + /** + * Get a UText for the text being analyzed. + * The returned UText is a shallow clone of the UText used internally + * by the break iterator implementation. It can safely be used to + * access the text without impacting any break iterator operations, + * but the underlying text itself must not be altered. + * + * @param fillIn A UText to be filled in. If nullptr, a new UText will be + * allocated to hold the result. + * @param status receives any error codes. + * @return The current UText for this break iterator. If an input + * UText was provided, it will always be returned. + * @stable ICU 3.4 + */ + virtual UText *getUText(UText *fillIn, UErrorCode &status) const override; + + /** + * Set the iterator to analyze a new piece of text. This function resets + * the current iteration position to the beginning of the text. + * @param newText An iterator over the text to analyze. The BreakIterator + * takes ownership of the character iterator. The caller MUST NOT delete it! + * @stable ICU 2.0 + */ + virtual void adoptText(CharacterIterator* newText) override; + + /** + * Set the iterator to analyze a new piece of text. This function resets + * the current iteration position to the beginning of the text. + * + * The BreakIterator will retain a reference to the supplied string. + * The caller must not modify or delete the text while the BreakIterator + * retains the reference. + * + * @param newText The text to analyze. + * @stable ICU 2.0 + */ + virtual void setText(const UnicodeString& newText) override; + + /** + * Reset the break iterator to operate over the text represented by + * the UText. The iterator position is reset to the start. + * + * This function makes a shallow clone of the supplied UText. This means + * that the caller is free to immediately close or otherwise reuse the + * Utext that was passed as a parameter, but that the underlying text itself + * must not be altered while being referenced by the break iterator. + * + * @param text The UText used to change the text. + * @param status Receives any error codes. + * @stable ICU 3.4 + */ + virtual void setText(UText *text, UErrorCode &status) override; + + /** + * Sets the current iteration position to the beginning of the text, position zero. + * @return The offset of the beginning of the text, zero. + * @stable ICU 2.0 + */ + virtual int32_t first(void) override; + + /** + * Sets the current iteration position to the end of the text. + * @return The text's past-the-end offset. + * @stable ICU 2.0 + */ + virtual int32_t last(void) override; + + /** + * Advances the iterator either forward or backward the specified number of steps. + * Negative values move backward, and positive values move forward. This is + * equivalent to repeatedly calling next() or previous(). + * @param n The number of steps to move. The sign indicates the direction + * (negative is backwards, and positive is forwards). + * @return The character offset of the boundary position n boundaries away from + * the current one. + * @stable ICU 2.0 + */ + virtual int32_t next(int32_t n) override; + + /** + * Advances the iterator to the next boundary position. + * @return The position of the first boundary after this one. + * @stable ICU 2.0 + */ + virtual int32_t next(void) override; + + /** + * Moves the iterator backwards, to the last boundary preceding this one. + * @return The position of the last boundary position preceding this one. + * @stable ICU 2.0 + */ + virtual int32_t previous(void) override; + + /** + * Sets the iterator to refer to the first boundary position following + * the specified position. + * @param offset The position from which to begin searching for a break position. + * @return The position of the first break after the current position. + * @stable ICU 2.0 + */ + virtual int32_t following(int32_t offset) override; + + /** + * Sets the iterator to refer to the last boundary position before the + * specified position. + * @param offset The position to begin searching for a break from. + * @return The position of the last boundary before the starting position. + * @stable ICU 2.0 + */ + virtual int32_t preceding(int32_t offset) override; + + /** + * Returns true if the specified position is a boundary position. As a side + * effect, leaves the iterator pointing to the first boundary position at + * or after "offset". + * @param offset the offset to check. + * @return True if "offset" is a boundary position. + * @stable ICU 2.0 + */ + virtual UBool isBoundary(int32_t offset) override; + + /** + * Returns the current iteration position. Note that UBRK_DONE is never + * returned from this function; if iteration has run to the end of a + * string, current() will return the length of the string while + * next() will return UBRK_DONE). + * @return The current iteration position. + * @stable ICU 2.0 + */ + virtual int32_t current(void) const override; + + + /** + * Return the status tag from the break rule that determined the boundary at + * the current iteration position. For break rules that do not specify a + * status, a default value of 0 is returned. If more than one break rule + * would cause a boundary to be located at some position in the text, + * the numerically largest of the applicable status values is returned. + *

+ * Of the standard types of ICU break iterators, only word break and + * line break provide status values. The values are defined in + * the header file ubrk.h. For Word breaks, the status allows distinguishing between words + * that contain alphabetic letters, "words" that appear to be numbers, + * punctuation and spaces, words containing ideographic characters, and + * more. For Line Break, the status distinguishes between hard (mandatory) breaks + * and soft (potential) break positions. + *

+ * getRuleStatus() can be called after obtaining a boundary + * position from next(), previous(), or + * any other break iterator functions that returns a boundary position. + *

+ * Note that getRuleStatus() returns the value corresponding to + * current() index even after next() has returned DONE. + *

+ * When creating custom break rules, one is free to define whatever + * status values may be convenient for the application. + *

+ * @return the status from the break rule that determined the boundary + * at the current iteration position. + * + * @see UWordBreak + * @stable ICU 2.2 + */ + virtual int32_t getRuleStatus() const override; + + /** + * Get the status (tag) values from the break rule(s) that determined the boundary + * at the current iteration position. + *

+ * The returned status value(s) are stored into an array provided by the caller. + * The values are stored in sorted (ascending) order. + * If the capacity of the output array is insufficient to hold the data, + * the output will be truncated to the available length, and a + * U_BUFFER_OVERFLOW_ERROR will be signaled. + * + * @param fillInVec an array to be filled in with the status values. + * @param capacity the length of the supplied vector. A length of zero causes + * the function to return the number of status values, in the + * normal way, without attempting to store any values. + * @param status receives error codes. + * @return The number of rule status values from the rules that determined + * the boundary at the current iteration position. + * In the event of a U_BUFFER_OVERFLOW_ERROR, the return value + * is the total number of status values that were available, + * not the reduced number that were actually returned. + * @see getRuleStatus + * @stable ICU 3.0 + */ + virtual int32_t getRuleStatusVec(int32_t *fillInVec, int32_t capacity, UErrorCode &status) override; + + /** + * Returns a unique class ID POLYMORPHICALLY. Pure virtual override. + * This method is to implement a simple version of RTTI, since not all + * C++ compilers support genuine RTTI. Polymorphic operator==() and + * clone() methods call this method. + * + * @return The class ID for this object. All objects of a + * given class have the same class ID. Objects of + * other classes have different class IDs. + * @stable ICU 2.0 + */ + virtual UClassID getDynamicClassID(void) const override; + + /** + * Returns the class ID for this class. This is useful only for + * comparing to a return value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @stable ICU 2.0 + */ + static UClassID U_EXPORT2 getStaticClassID(void); + +#ifndef U_FORCE_HIDE_DEPRECATED_API + /** + * Deprecated functionality. Use clone() instead. + * + * Create a clone (copy) of this break iterator in memory provided + * by the caller. The idea is to increase performance by avoiding + * a storage allocation. Use of this function is NOT RECOMMENDED. + * Performance gains are minimal, and correct buffer management is + * tricky. Use clone() instead. + * + * @param stackBuffer The pointer to the memory into which the cloned object + * should be placed. If nullptr, allocate heap memory + * for the cloned object. + * @param BufferSize The size of the buffer. If zero, return the required + * buffer size, but do not clone the object. If the + * size was too small (but not zero), allocate heap + * storage for the cloned object. + * + * @param status Error status. U_SAFECLONE_ALLOCATED_WARNING will be + * returned if the provided buffer was too small, and + * the clone was therefore put on the heap. + * + * @return Pointer to the clone object. This may differ from the stackBuffer + * address if the byte alignment of the stack buffer was not suitable + * or if the stackBuffer was too small to hold the clone. + * @deprecated ICU 52. Use clone() instead. + */ + virtual RuleBasedBreakIterator *createBufferClone(void *stackBuffer, + int32_t &BufferSize, + UErrorCode &status) override; +#endif // U_FORCE_HIDE_DEPRECATED_API + + /** + * Return the binary form of compiled break rules, + * which can then be used to create a new break iterator at some + * time in the future. Creating a break iterator from pre-compiled rules + * is much faster than building one from the source form of the + * break rules. + * + * The binary data can only be used with the same version of ICU + * and on the same platform type (processor endian-ness) + * + * @param length Returns the length of the binary data. (Out parameter.) + * + * @return A pointer to the binary (compiled) rule data. The storage + * belongs to the RulesBasedBreakIterator object, not the + * caller, and must not be modified or deleted. + * @stable ICU 4.8 + */ + virtual const uint8_t *getBinaryRules(uint32_t &length); + + /** + * Set the subject text string upon which the break iterator is operating + * without changing any other aspect of the matching state. + * The new and previous text strings must have the same content. + * + * This function is intended for use in environments where ICU is operating on + * strings that may move around in memory. It provides a mechanism for notifying + * ICU that the string has been relocated, and providing a new UText to access the + * string in its new position. + * + * Note that the break iterator implementation never copies the underlying text + * of a string being processed, but always operates directly on the original text + * provided by the user. Refreshing simply drops the references to the old text + * and replaces them with references to the new. + * + * Caution: this function is normally used only by very specialized, + * system-level code. One example use case is with garbage collection that moves + * the text in memory. + * + * @param input The new (moved) text string. + * @param status Receives errors detected by this function. + * @return *this + * + * @stable ICU 49 + */ + virtual RuleBasedBreakIterator &refreshInputText(UText *input, UErrorCode &status) override; + + +private: + //======================================================================= + // implementation + //======================================================================= + /** + * 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 (private) + */ + int32_t handleSafePrevious(int32_t fromPosition); + + /** + * Find a rule-based boundary by running the state machine. + * Input + * fPosition, the position in the text to begin from. + * Output + * fPosition: the boundary following the starting position. + * fDictionaryCharCount the number of dictionary characters encountered. + * If > 0, the segment will be further subdivided + * fRuleStatusIndex Info from the state table indicating which rules caused the boundary. + * + * @internal (private) + */ + int32_t handleNext(); + + /* + * Templatized version of handleNext() and handleSafePrevious(). + * + * There will be exactly four instantiations, two each for 8 and 16 bit tables, + * two each for 8 and 16 bit trie. + * Having separate instantiations for the table types keeps conditional tests of + * the table type out of the inner loops, at the expense of replicated code. + * + * The template parameter for the Trie access function is a value, not a type. + * Doing it this way, the compiler will inline the Trie function in the + * expanded functions. (Both the 8 and 16 bit access functions have the same type + * signature) + */ + + typedef uint16_t (*PTrieFunc)(const UCPTrie *, UChar32); + + template + int32_t handleSafePrevious(int32_t fromPosition); + + template + int32_t handleNext(); + + + /** + * This function returns the appropriate LanguageBreakEngine for a + * given character c. + * @param c A character in the dictionary set + * @internal (private) + */ + const LanguageBreakEngine *getLanguageBreakEngine(UChar32 c); + + public: +#ifndef U_HIDE_INTERNAL_API + /** + * Debugging function only. + * @internal + */ + void dumpCache(); + + /** + * Debugging function only. + * @internal + */ + void dumpTables(); +#endif /* U_HIDE_INTERNAL_API */ +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/rep.h b/deps/icu-small/source/common/unicode/rep.h index 6dd4530647e628..c06e16cb4a4296 100644 --- a/deps/icu-small/source/common/unicode/rep.h +++ b/deps/icu-small/source/common/unicode/rep.h @@ -1,266 +1,266 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -************************************************************************** -* Copyright (C) 1999-2012, International Business Machines Corporation and -* others. All Rights Reserved. -************************************************************************** -* Date Name Description -* 11/17/99 aliu Creation. Ported from java. Modified to -* match current UnicodeString API. Forced -* to use name "handleReplaceBetween" because -* of existing methods in UnicodeString. -************************************************************************** -*/ - -#ifndef REP_H -#define REP_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/uobject.h" - -/** - * \file - * \brief C++ API: Replaceable String - */ - -U_NAMESPACE_BEGIN - -class UnicodeString; - -/** - * Replaceable is an abstract base class representing a - * string of characters that supports the replacement of a range of - * itself with a new string of characters. It is used by APIs that - * change a piece of text while retaining metadata. Metadata is data - * other than the Unicode characters returned by char32At(). One - * example of metadata is style attributes; another is an edit - * history, marking each character with an author and revision number. - * - *

An implicit aspect of the Replaceable API is that - * during a replace operation, new characters take on the metadata of - * the old characters. For example, if the string "the bold - * font" has range (4, 8) replaced with "strong", then it becomes "the - * strong font". - * - *

Replaceable specifies ranges using a start - * offset and a limit offset. The range of characters thus specified - * includes the characters at offset start..limit-1. That is, the - * start offset is inclusive, and the limit offset is exclusive. - * - *

Replaceable also includes API to access characters - * in the string: length(), charAt(), - * char32At(), and extractBetween(). - * - *

For a subclass to support metadata, typical behavior of - * replace() is the following: - *

    - *
  • Set the metadata of the new text to the metadata of the first - * character replaced
  • - *
  • If no characters are replaced, use the metadata of the - * previous character
  • - *
  • If there is no previous character (i.e. start == 0), use the - * following character
  • - *
  • If there is no following character (i.e. the replaceable was - * empty), use default metadata.
    - *
  • If the code point U+FFFF is seen, it should be interpreted as - * a special marker having no metadata
  • - *
  • - *
- * If this is not the behavior, the subclass should document any differences. - * @author Alan Liu - * @stable ICU 2.0 - */ -class U_COMMON_API Replaceable : public UObject { - -public: - /** - * Destructor. - * @stable ICU 2.0 - */ - virtual ~Replaceable(); - - /** - * Returns the number of 16-bit code units in the text. - * @return number of 16-bit code units in text - * @stable ICU 1.8 - */ - inline int32_t length() const; - - /** - * Returns the 16-bit code unit at the given offset into the text. - * @param offset an integer between 0 and length()-1 - * inclusive - * @return 16-bit code unit of text at given offset - * @stable ICU 1.8 - */ - inline char16_t charAt(int32_t offset) const; - - /** - * Returns the 32-bit code point at the given 16-bit offset into - * the text. This assumes the text is stored as 16-bit code units - * with surrogate pairs intermixed. If the offset of a leading or - * trailing code unit of a surrogate pair is given, return the - * code point of the surrogate pair. - * - * @param offset an integer between 0 and length()-1 - * inclusive - * @return 32-bit code point of text at given offset - * @stable ICU 1.8 - */ - inline UChar32 char32At(int32_t offset) const; - - /** - * Copies characters in the range [start, limit) - * into the UnicodeString target. - * @param start offset of first character which will be copied - * @param limit offset immediately following the last character to - * be copied - * @param target UnicodeString into which to copy characters. - * @return A reference to target - * @stable ICU 2.1 - */ - virtual void extractBetween(int32_t start, - int32_t limit, - UnicodeString& target) const = 0; - - /** - * Replaces a substring of this object with the given text. If the - * characters being replaced have metadata, the new characters - * that replace them should be given the same metadata. - * - *

Subclasses must ensure that if the text between start and - * limit is equal to the replacement text, that replace has no - * effect. That is, any metadata - * should be unaffected. In addition, subclasses are encouraged to - * check for initial and trailing identical characters, and make a - * smaller replacement if possible. This will preserve as much - * metadata as possible. - * @param start the beginning index, inclusive; 0 <= start - * <= limit. - * @param limit the ending index, exclusive; start <= limit - * <= length(). - * @param text the text to replace characters start - * to limit - 1 - * @stable ICU 2.0 - */ - virtual void handleReplaceBetween(int32_t start, - int32_t limit, - const UnicodeString& text) = 0; - // Note: All other methods in this class take the names of - // existing UnicodeString methods. This method is the exception. - // It is named differently because all replace methods of - // UnicodeString return a UnicodeString&. The 'between' is - // required in order to conform to the UnicodeString naming - // convention; API taking start/length are named , and - // those taking start/limit are named . The - // 'handle' is added because 'replaceBetween' and - // 'doReplaceBetween' are already taken. - - /** - * Copies a substring of this object, retaining metadata. - * This method is used to duplicate or reorder substrings. - * The destination index must not overlap the source range. - * - * @param start the beginning index, inclusive; 0 <= start <= - * limit. - * @param limit the ending index, exclusive; start <= limit <= - * length(). - * @param dest the destination index. The characters from - * start..limit-1 will be copied to dest. - * Implementations of this method may assume that dest <= start || - * dest >= limit. - * @stable ICU 2.0 - */ - virtual void copy(int32_t start, int32_t limit, int32_t dest) = 0; - - /** - * Returns true if this object contains metadata. If a - * Replaceable object has metadata, calls to the Replaceable API - * must be made so as to preserve metadata. If it does not, calls - * to the Replaceable API may be optimized to improve performance. - * The default implementation returns true. - * @return true if this object contains metadata - * @stable ICU 2.2 - */ - virtual UBool hasMetaData() const; - - /** - * Clone this object, an instance of a subclass of Replaceable. - * Clones can be used concurrently in multiple threads. - * If a subclass does not implement clone(), or if an error occurs, - * then NULL is returned. - * The caller must delete the clone. - * - * @return a clone of this object - * - * @see getDynamicClassID - * @stable ICU 2.6 - */ - virtual Replaceable *clone() const; - -protected: - - /** - * Default constructor. - * @stable ICU 2.4 - */ - inline Replaceable(); - - /* - * Assignment operator not declared. The compiler will provide one - * which does nothing since this class does not contain any data members. - * API/code coverage may show the assignment operator as present and - * untested - ignore. - * Subclasses need this assignment operator if they use compiler-provided - * assignment operators of their own. An alternative to not declaring one - * here would be to declare and empty-implement a protected or public one. - Replaceable &Replaceable::operator=(const Replaceable &); - */ - - /** - * Virtual version of length(). - * @stable ICU 2.4 - */ - virtual int32_t getLength() const = 0; - - /** - * Virtual version of charAt(). - * @stable ICU 2.4 - */ - virtual char16_t getCharAt(int32_t offset) const = 0; - - /** - * Virtual version of char32At(). - * @stable ICU 2.4 - */ - virtual UChar32 getChar32At(int32_t offset) const = 0; -}; - -inline Replaceable::Replaceable() {} - -inline int32_t -Replaceable::length() const { - return getLength(); -} - -inline char16_t -Replaceable::charAt(int32_t offset) const { - return getCharAt(offset); -} - -inline UChar32 -Replaceable::char32At(int32_t offset) const { - return getChar32At(offset); -} - -// There is no rep.cpp, see unistr.cpp for Replaceable function implementations. - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +************************************************************************** +* Copyright (C) 1999-2012, International Business Machines Corporation and +* others. All Rights Reserved. +************************************************************************** +* Date Name Description +* 11/17/99 aliu Creation. Ported from java. Modified to +* match current UnicodeString API. Forced +* to use name "handleReplaceBetween" because +* of existing methods in UnicodeString. +************************************************************************** +*/ + +#ifndef REP_H +#define REP_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/uobject.h" + +/** + * \file + * \brief C++ API: Replaceable String + */ + +U_NAMESPACE_BEGIN + +class UnicodeString; + +/** + * Replaceable is an abstract base class representing a + * string of characters that supports the replacement of a range of + * itself with a new string of characters. It is used by APIs that + * change a piece of text while retaining metadata. Metadata is data + * other than the Unicode characters returned by char32At(). One + * example of metadata is style attributes; another is an edit + * history, marking each character with an author and revision number. + * + *

An implicit aspect of the Replaceable API is that + * during a replace operation, new characters take on the metadata of + * the old characters. For example, if the string "the bold + * font" has range (4, 8) replaced with "strong", then it becomes "the + * strong font". + * + *

Replaceable specifies ranges using a start + * offset and a limit offset. The range of characters thus specified + * includes the characters at offset start..limit-1. That is, the + * start offset is inclusive, and the limit offset is exclusive. + * + *

Replaceable also includes API to access characters + * in the string: length(), charAt(), + * char32At(), and extractBetween(). + * + *

For a subclass to support metadata, typical behavior of + * replace() is the following: + *

    + *
  • Set the metadata of the new text to the metadata of the first + * character replaced
  • + *
  • If no characters are replaced, use the metadata of the + * previous character
  • + *
  • If there is no previous character (i.e. start == 0), use the + * following character
  • + *
  • If there is no following character (i.e. the replaceable was + * empty), use default metadata.
    + *
  • If the code point U+FFFF is seen, it should be interpreted as + * a special marker having no metadata
  • + *
  • + *
+ * If this is not the behavior, the subclass should document any differences. + * @author Alan Liu + * @stable ICU 2.0 + */ +class U_COMMON_API Replaceable : public UObject { + +public: + /** + * Destructor. + * @stable ICU 2.0 + */ + virtual ~Replaceable(); + + /** + * Returns the number of 16-bit code units in the text. + * @return number of 16-bit code units in text + * @stable ICU 1.8 + */ + inline int32_t length() const; + + /** + * Returns the 16-bit code unit at the given offset into the text. + * @param offset an integer between 0 and length()-1 + * inclusive + * @return 16-bit code unit of text at given offset + * @stable ICU 1.8 + */ + inline char16_t charAt(int32_t offset) const; + + /** + * Returns the 32-bit code point at the given 16-bit offset into + * the text. This assumes the text is stored as 16-bit code units + * with surrogate pairs intermixed. If the offset of a leading or + * trailing code unit of a surrogate pair is given, return the + * code point of the surrogate pair. + * + * @param offset an integer between 0 and length()-1 + * inclusive + * @return 32-bit code point of text at given offset + * @stable ICU 1.8 + */ + inline UChar32 char32At(int32_t offset) const; + + /** + * Copies characters in the range [start, limit) + * into the UnicodeString target. + * @param start offset of first character which will be copied + * @param limit offset immediately following the last character to + * be copied + * @param target UnicodeString into which to copy characters. + * @return A reference to target + * @stable ICU 2.1 + */ + virtual void extractBetween(int32_t start, + int32_t limit, + UnicodeString& target) const = 0; + + /** + * Replaces a substring of this object with the given text. If the + * characters being replaced have metadata, the new characters + * that replace them should be given the same metadata. + * + *

Subclasses must ensure that if the text between start and + * limit is equal to the replacement text, that replace has no + * effect. That is, any metadata + * should be unaffected. In addition, subclasses are encouraged to + * check for initial and trailing identical characters, and make a + * smaller replacement if possible. This will preserve as much + * metadata as possible. + * @param start the beginning index, inclusive; 0 <= start + * <= limit. + * @param limit the ending index, exclusive; start <= limit + * <= length(). + * @param text the text to replace characters start + * to limit - 1 + * @stable ICU 2.0 + */ + virtual void handleReplaceBetween(int32_t start, + int32_t limit, + const UnicodeString& text) = 0; + // Note: All other methods in this class take the names of + // existing UnicodeString methods. This method is the exception. + // It is named differently because all replace methods of + // UnicodeString return a UnicodeString&. The 'between' is + // required in order to conform to the UnicodeString naming + // convention; API taking start/length are named , and + // those taking start/limit are named . The + // 'handle' is added because 'replaceBetween' and + // 'doReplaceBetween' are already taken. + + /** + * Copies a substring of this object, retaining metadata. + * This method is used to duplicate or reorder substrings. + * The destination index must not overlap the source range. + * + * @param start the beginning index, inclusive; 0 <= start <= + * limit. + * @param limit the ending index, exclusive; start <= limit <= + * length(). + * @param dest the destination index. The characters from + * start..limit-1 will be copied to dest. + * Implementations of this method may assume that dest <= start || + * dest >= limit. + * @stable ICU 2.0 + */ + virtual void copy(int32_t start, int32_t limit, int32_t dest) = 0; + + /** + * Returns true if this object contains metadata. If a + * Replaceable object has metadata, calls to the Replaceable API + * must be made so as to preserve metadata. If it does not, calls + * to the Replaceable API may be optimized to improve performance. + * The default implementation returns true. + * @return true if this object contains metadata + * @stable ICU 2.2 + */ + virtual UBool hasMetaData() const; + + /** + * Clone this object, an instance of a subclass of Replaceable. + * Clones can be used concurrently in multiple threads. + * If a subclass does not implement clone(), or if an error occurs, + * then nullptr is returned. + * The caller must delete the clone. + * + * @return a clone of this object + * + * @see getDynamicClassID + * @stable ICU 2.6 + */ + virtual Replaceable *clone() const; + +protected: + + /** + * Default constructor. + * @stable ICU 2.4 + */ + inline Replaceable(); + + /* + * Assignment operator not declared. The compiler will provide one + * which does nothing since this class does not contain any data members. + * API/code coverage may show the assignment operator as present and + * untested - ignore. + * Subclasses need this assignment operator if they use compiler-provided + * assignment operators of their own. An alternative to not declaring one + * here would be to declare and empty-implement a protected or public one. + Replaceable &Replaceable::operator=(const Replaceable &); + */ + + /** + * Virtual version of length(). + * @stable ICU 2.4 + */ + virtual int32_t getLength() const = 0; + + /** + * Virtual version of charAt(). + * @stable ICU 2.4 + */ + virtual char16_t getCharAt(int32_t offset) const = 0; + + /** + * Virtual version of char32At(). + * @stable ICU 2.4 + */ + virtual UChar32 getChar32At(int32_t offset) const = 0; +}; + +inline Replaceable::Replaceable() {} + +inline int32_t +Replaceable::length() const { + return getLength(); +} + +inline char16_t +Replaceable::charAt(int32_t offset) const { + return getCharAt(offset); +} + +inline UChar32 +Replaceable::char32At(int32_t offset) const { + return getChar32At(offset); +} + +// There is no rep.cpp, see unistr.cpp for Replaceable function implementations. + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/resbund.h b/deps/icu-small/source/common/unicode/resbund.h index 6e26a40591fb37..afa6b88cf8c7ff 100644 --- a/deps/icu-small/source/common/unicode/resbund.h +++ b/deps/icu-small/source/common/unicode/resbund.h @@ -1,498 +1,498 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1996-2013, International Business Machines Corporation -* and others. All Rights Reserved. -* -****************************************************************************** -* -* File resbund.h -* -* CREATED BY -* Richard Gillam -* -* Modification History: -* -* Date Name Description -* 2/5/97 aliu Added scanForLocaleInFile. Added -* constructor which attempts to read resource bundle -* from a specific file, without searching other files. -* 2/11/97 aliu Added UErrorCode return values to constructors. Fixed -* infinite loops in scanForFile and scanForLocale. -* Modified getRawResourceData to not delete storage -* in localeData and resourceData which it doesn't own. -* Added Mac compatibility #ifdefs for tellp() and -* ios::nocreate. -* 2/18/97 helena Updated with 100% documentation coverage. -* 3/13/97 aliu Rewrote to load in entire resource bundle and store -* it as a Hashtable of ResourceBundleData objects. -* Added state table to govern parsing of files. -* Modified to load locale index out of new file -* distinct from default.txt. -* 3/25/97 aliu Modified to support 2-d arrays, needed for timezone -* data. Added support for custom file suffixes. Again, -* needed to support timezone data. -* 4/7/97 aliu Cleaned up. -* 03/02/99 stephen Removed dependency on FILE*. -* 03/29/99 helena Merged Bertrand and Stephen's changes. -* 06/11/99 stephen Removed parsing of .txt files. -* Reworked to use new binary format. -* Cleaned up. -* 06/14/99 stephen Removed methods taking a filename suffix. -* 11/09/99 weiv Added getLocale(), fRealLocale, removed fRealLocaleID -****************************************************************************** -*/ - -#ifndef RESBUND_H -#define RESBUND_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/uobject.h" -#include "unicode/ures.h" -#include "unicode/unistr.h" -#include "unicode/locid.h" - -/** - * \file - * \brief C++ API: Resource Bundle - */ - -U_NAMESPACE_BEGIN - -/** - * A class representing a collection of resource information pertaining to a given - * locale. A resource bundle provides a way of accessing locale- specific information in - * a data file. You create a resource bundle that manages the resources for a given - * locale and then ask it for individual resources. - *

- * Resource bundles in ICU4C are currently defined using text files which conform to the following - * BNF definition. - * More on resource bundle concepts and syntax can be found in the - * Users Guide. - *

- * - * The ResourceBundle class is not suitable for subclassing. - * - * @stable ICU 2.0 - */ -class U_COMMON_API ResourceBundle : public UObject { -public: - /** - * Constructor - * - * @param packageName The packageName and locale together point to an ICU udata object, - * as defined by udata_open( packageName, "res", locale, err) - * or equivalent. Typically, packageName will refer to a (.dat) file, or to - * a package registered with udata_setAppData(). Using a full file or directory - * pathname for packageName is deprecated. - * @param locale This is the locale this resource bundle is for. To get resources - * for the French locale, for example, you would create a - * ResourceBundle passing Locale::FRENCH for the "locale" parameter, - * and all subsequent calls to that resource bundle will return - * resources that pertain to the French locale. If the caller doesn't - * pass a locale parameter, the default locale for the system (as - * returned by Locale::getDefault()) will be used. - * @param err The Error Code. - * The UErrorCode& err parameter is used to return status information to the user. To - * check whether the construction succeeded or not, you should check the value of - * U_SUCCESS(err). If you wish more detailed information, you can check for - * informational error results which still indicate success. U_USING_FALLBACK_WARNING - * indicates that a fall back locale was used. For example, 'de_CH' was requested, - * but nothing was found there, so 'de' was used. U_USING_DEFAULT_WARNING indicates that - * the default locale data was used; neither the requested locale nor any of its - * fall back locales could be found. - * @stable ICU 2.0 - */ - ResourceBundle(const UnicodeString& packageName, - const Locale& locale, - UErrorCode& err); - - /** - * Construct a resource bundle for the default bundle in the specified package. - * - * @param packageName The packageName and locale together point to an ICU udata object, - * as defined by udata_open( packageName, "res", locale, err) - * or equivalent. Typically, packageName will refer to a (.dat) file, or to - * a package registered with udata_setAppData(). Using a full file or directory - * pathname for packageName is deprecated. - * @param err A UErrorCode value - * @stable ICU 2.0 - */ - ResourceBundle(const UnicodeString& packageName, - UErrorCode& err); - - /** - * Construct a resource bundle for the ICU default bundle. - * - * @param err A UErrorCode value - * @stable ICU 2.0 - */ - ResourceBundle(UErrorCode &err); - - /** - * Standard constructor, constructs a resource bundle for the locale-specific - * bundle in the specified package. - * - * @param packageName The packageName and locale together point to an ICU udata object, - * as defined by udata_open( packageName, "res", locale, err) - * or equivalent. Typically, packageName will refer to a (.dat) file, or to - * a package registered with udata_setAppData(). Using a full file or directory - * pathname for packageName is deprecated. - * NULL is used to refer to ICU data. - * @param locale The locale for which to open a resource bundle. - * @param err A UErrorCode value - * @stable ICU 2.0 - */ - ResourceBundle(const char* packageName, - const Locale& locale, - UErrorCode& err); - - /** - * Copy constructor. - * - * @param original The resource bundle to copy. - * @stable ICU 2.0 - */ - ResourceBundle(const ResourceBundle &original); - - /** - * Constructor from a C UResourceBundle. The resource bundle is - * copied and not adopted. ures_close will still need to be used on the - * original resource bundle. - * - * @param res A pointer to the C resource bundle. - * @param status A UErrorCode value. - * @stable ICU 2.0 - */ - ResourceBundle(UResourceBundle *res, - UErrorCode &status); - - /** - * Assignment operator. - * - * @param other The resource bundle to copy. - * @stable ICU 2.0 - */ - ResourceBundle& - operator=(const ResourceBundle& other); - - /** Destructor. - * @stable ICU 2.0 - */ - virtual ~ResourceBundle(); - - /** - * Clone this object. - * Clones can be used concurrently in multiple threads. - * If an error occurs, then NULL is returned. - * The caller must delete the clone. - * - * @return a clone of this object - * - * @see getDynamicClassID - * @stable ICU 2.8 - */ - ResourceBundle *clone() const; - - /** - * Returns the size of a resource. Size for scalar types is always 1, and for vector/table types is - * the number of child resources. - * @warning Integer array is treated as a scalar type. There are no - * APIs to access individual members of an integer array. It - * is always returned as a whole. - * - * @return number of resources in a given resource. - * @stable ICU 2.0 - */ - int32_t - getSize(void) const; - - /** - * returns a string from a string resource type - * - * @param status fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * could be a warning - * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING - * @return a pointer to a zero-terminated char16_t array which lives in a memory mapped/DLL file. - * @stable ICU 2.0 - */ - UnicodeString - getString(UErrorCode& status) const; - - /** - * returns a binary data from a resource. Can be used at most primitive resource types (binaries, - * strings, ints) - * - * @param len fills in the length of resulting byte chunk - * @param status fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * could be a warning - * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING - * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file. - * @stable ICU 2.0 - */ - const uint8_t* - getBinary(int32_t& len, UErrorCode& status) const; - - - /** - * returns an integer vector from a resource. - * - * @param len fills in the length of resulting integer vector - * @param status fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * could be a warning - * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING - * @return a pointer to a vector of integers that lives in a memory mapped/DLL file. - * @stable ICU 2.0 - */ - const int32_t* - getIntVector(int32_t& len, UErrorCode& status) const; - - /** - * returns an unsigned integer from a resource. - * This integer is originally 28 bits. - * - * @param status fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * could be a warning - * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING - * @return an unsigned integer value - * @stable ICU 2.0 - */ - uint32_t - getUInt(UErrorCode& status) const; - - /** - * returns a signed integer from a resource. - * This integer is originally 28 bit and the sign gets propagated. - * - * @param status fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * could be a warning - * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING - * @return a signed integer value - * @stable ICU 2.0 - */ - int32_t - getInt(UErrorCode& status) const; - - /** - * Checks whether the resource has another element to iterate over. - * - * @return true if there are more elements, false if there is no more elements - * @stable ICU 2.0 - */ - UBool - hasNext(void) const; - - /** - * Resets the internal context of a resource so that iteration starts from the first element. - * - * @stable ICU 2.0 - */ - void - resetIterator(void); - - /** - * Returns the key associated with this resource. Not all the resources have a key - only - * those that are members of a table. - * - * @return a key associated to this resource, or NULL if it doesn't have a key - * @stable ICU 2.0 - */ - const char* - getKey(void) const; - - /** - * Gets the locale ID of the resource bundle as a string. - * Same as getLocale().getName() . - * - * @return the locale ID of the resource bundle as a string - * @stable ICU 2.0 - */ - const char* - getName(void) const; - - - /** - * Returns the type of a resource. Available types are defined in enum UResType - * - * @return type of the given resource. - * @stable ICU 2.0 - */ - UResType - getType(void) const; - - /** - * Returns the next resource in a given resource or NULL if there are no more resources - * - * @param status fills in the outgoing error code - * @return ResourceBundle object. - * @stable ICU 2.0 - */ - ResourceBundle - getNext(UErrorCode& status); - - /** - * Returns the next string in a resource or NULL if there are no more resources - * to iterate over. - * - * @param status fills in the outgoing error code - * @return an UnicodeString object. - * @stable ICU 2.0 - */ - UnicodeString - getNextString(UErrorCode& status); - - /** - * Returns the next string in a resource or NULL if there are no more resources - * to iterate over. - * - * @param key fill in for key associated with this string - * @param status fills in the outgoing error code - * @return an UnicodeString object. - * @stable ICU 2.0 - */ - UnicodeString - getNextString(const char ** key, - UErrorCode& status); - - /** - * Returns the resource in a resource at the specified index. - * - * @param index an index to the wanted resource. - * @param status fills in the outgoing error code - * @return ResourceBundle object. If there is an error, resource is invalid. - * @stable ICU 2.0 - */ - ResourceBundle - get(int32_t index, - UErrorCode& status) const; - - /** - * Returns the string in a given resource at the specified index. - * - * @param index an index to the wanted string. - * @param status fills in the outgoing error code - * @return an UnicodeString object. If there is an error, string is bogus - * @stable ICU 2.0 - */ - UnicodeString - getStringEx(int32_t index, - UErrorCode& status) const; - - /** - * Returns a resource in a resource that has a given key. This procedure works only with table - * resources. - * - * @param key a key associated with the wanted resource - * @param status fills in the outgoing error code. - * @return ResourceBundle object. If there is an error, resource is invalid. - * @stable ICU 2.0 - */ - ResourceBundle - get(const char* key, - UErrorCode& status) const; - - /** - * Returns a string in a resource that has a given key. This procedure works only with table - * resources. - * - * @param key a key associated with the wanted string - * @param status fills in the outgoing error code - * @return an UnicodeString object. If there is an error, string is bogus - * @stable ICU 2.0 - */ - UnicodeString - getStringEx(const char* key, - UErrorCode& status) const; - -#ifndef U_HIDE_DEPRECATED_API - /** - * Return the version number associated with this ResourceBundle as a string. Please - * use getVersion, as this method is going to be deprecated. - * - * @return A version number string as specified in the resource bundle or its parent. - * The caller does not own this string. - * @see getVersion - * @deprecated ICU 2.8 Use getVersion instead. - */ - const char* - getVersionNumber(void) const; -#endif /* U_HIDE_DEPRECATED_API */ - - /** - * Return the version number associated with this ResourceBundle as a UVersionInfo array. - * - * @param versionInfo A UVersionInfo array that is filled with the version number - * as specified in the resource bundle or its parent. - * @stable ICU 2.0 - */ - void - getVersion(UVersionInfo versionInfo) const; - -#ifndef U_HIDE_DEPRECATED_API - /** - * Return the Locale associated with this ResourceBundle. - * - * @return a Locale object - * @deprecated ICU 2.8 Use getLocale(ULocDataLocaleType type, UErrorCode &status) overload instead. - */ - const Locale& - getLocale(void) const; -#endif /* U_HIDE_DEPRECATED_API */ - - /** - * Return the Locale associated with this ResourceBundle. - * @param type You can choose between requested, valid and actual - * locale. For description see the definition of - * ULocDataLocaleType in uloc.h - * @param status just for catching illegal arguments - * - * @return a Locale object - * @stable ICU 2.8 - */ - const Locale - getLocale(ULocDataLocaleType type, UErrorCode &status) const; -#ifndef U_HIDE_INTERNAL_API - /** - * This API implements multilevel fallback - * @internal - */ - ResourceBundle - getWithFallback(const char* key, UErrorCode& status); -#endif /* U_HIDE_INTERNAL_API */ - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - * - * @stable ICU 2.2 - */ - virtual UClassID getDynamicClassID() const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - * - * @stable ICU 2.2 - */ - static UClassID U_EXPORT2 getStaticClassID(); - -private: - ResourceBundle() = delete; // default constructor not implemented - - UResourceBundle *fResource; - void constructForLocale(const UnicodeString& path, const Locale& locale, UErrorCode& error); - Locale *fLocale; -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1996-2013, International Business Machines Corporation +* and others. All Rights Reserved. +* +****************************************************************************** +* +* File resbund.h +* +* CREATED BY +* Richard Gillam +* +* Modification History: +* +* Date Name Description +* 2/5/97 aliu Added scanForLocaleInFile. Added +* constructor which attempts to read resource bundle +* from a specific file, without searching other files. +* 2/11/97 aliu Added UErrorCode return values to constructors. Fixed +* infinite loops in scanForFile and scanForLocale. +* Modified getRawResourceData to not delete storage +* in localeData and resourceData which it doesn't own. +* Added Mac compatibility #ifdefs for tellp() and +* ios::nocreate. +* 2/18/97 helena Updated with 100% documentation coverage. +* 3/13/97 aliu Rewrote to load in entire resource bundle and store +* it as a Hashtable of ResourceBundleData objects. +* Added state table to govern parsing of files. +* Modified to load locale index out of new file +* distinct from default.txt. +* 3/25/97 aliu Modified to support 2-d arrays, needed for timezone +* data. Added support for custom file suffixes. Again, +* needed to support timezone data. +* 4/7/97 aliu Cleaned up. +* 03/02/99 stephen Removed dependency on FILE*. +* 03/29/99 helena Merged Bertrand and Stephen's changes. +* 06/11/99 stephen Removed parsing of .txt files. +* Reworked to use new binary format. +* Cleaned up. +* 06/14/99 stephen Removed methods taking a filename suffix. +* 11/09/99 weiv Added getLocale(), fRealLocale, removed fRealLocaleID +****************************************************************************** +*/ + +#ifndef RESBUND_H +#define RESBUND_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/uobject.h" +#include "unicode/ures.h" +#include "unicode/unistr.h" +#include "unicode/locid.h" + +/** + * \file + * \brief C++ API: Resource Bundle + */ + +U_NAMESPACE_BEGIN + +/** + * A class representing a collection of resource information pertaining to a given + * locale. A resource bundle provides a way of accessing locale- specific information in + * a data file. You create a resource bundle that manages the resources for a given + * locale and then ask it for individual resources. + *

+ * Resource bundles in ICU4C are currently defined using text files which conform to the following + * BNF definition. + * More on resource bundle concepts and syntax can be found in the + * Users Guide. + *

+ * + * The ResourceBundle class is not suitable for subclassing. + * + * @stable ICU 2.0 + */ +class U_COMMON_API ResourceBundle : public UObject { +public: + /** + * Constructor + * + * @param packageName The packageName and locale together point to an ICU udata object, + * as defined by udata_open( packageName, "res", locale, err) + * or equivalent. Typically, packageName will refer to a (.dat) file, or to + * a package registered with udata_setAppData(). Using a full file or directory + * pathname for packageName is deprecated. + * @param locale This is the locale this resource bundle is for. To get resources + * for the French locale, for example, you would create a + * ResourceBundle passing Locale::FRENCH for the "locale" parameter, + * and all subsequent calls to that resource bundle will return + * resources that pertain to the French locale. If the caller doesn't + * pass a locale parameter, the default locale for the system (as + * returned by Locale::getDefault()) will be used. + * @param err The Error Code. + * The UErrorCode& err parameter is used to return status information to the user. To + * check whether the construction succeeded or not, you should check the value of + * U_SUCCESS(err). If you wish more detailed information, you can check for + * informational error results which still indicate success. U_USING_FALLBACK_WARNING + * indicates that a fall back locale was used. For example, 'de_CH' was requested, + * but nothing was found there, so 'de' was used. U_USING_DEFAULT_WARNING indicates that + * the default locale data was used; neither the requested locale nor any of its + * fall back locales could be found. + * @stable ICU 2.0 + */ + ResourceBundle(const UnicodeString& packageName, + const Locale& locale, + UErrorCode& err); + + /** + * Construct a resource bundle for the default bundle in the specified package. + * + * @param packageName The packageName and locale together point to an ICU udata object, + * as defined by udata_open( packageName, "res", locale, err) + * or equivalent. Typically, packageName will refer to a (.dat) file, or to + * a package registered with udata_setAppData(). Using a full file or directory + * pathname for packageName is deprecated. + * @param err A UErrorCode value + * @stable ICU 2.0 + */ + ResourceBundle(const UnicodeString& packageName, + UErrorCode& err); + + /** + * Construct a resource bundle for the ICU default bundle. + * + * @param err A UErrorCode value + * @stable ICU 2.0 + */ + ResourceBundle(UErrorCode &err); + + /** + * Standard constructor, constructs a resource bundle for the locale-specific + * bundle in the specified package. + * + * @param packageName The packageName and locale together point to an ICU udata object, + * as defined by udata_open( packageName, "res", locale, err) + * or equivalent. Typically, packageName will refer to a (.dat) file, or to + * a package registered with udata_setAppData(). Using a full file or directory + * pathname for packageName is deprecated. + * nullptr is used to refer to ICU data. + * @param locale The locale for which to open a resource bundle. + * @param err A UErrorCode value + * @stable ICU 2.0 + */ + ResourceBundle(const char* packageName, + const Locale& locale, + UErrorCode& err); + + /** + * Copy constructor. + * + * @param original The resource bundle to copy. + * @stable ICU 2.0 + */ + ResourceBundle(const ResourceBundle &original); + + /** + * Constructor from a C UResourceBundle. The resource bundle is + * copied and not adopted. ures_close will still need to be used on the + * original resource bundle. + * + * @param res A pointer to the C resource bundle. + * @param status A UErrorCode value. + * @stable ICU 2.0 + */ + ResourceBundle(UResourceBundle *res, + UErrorCode &status); + + /** + * Assignment operator. + * + * @param other The resource bundle to copy. + * @stable ICU 2.0 + */ + ResourceBundle& + operator=(const ResourceBundle& other); + + /** Destructor. + * @stable ICU 2.0 + */ + virtual ~ResourceBundle(); + + /** + * Clone this object. + * Clones can be used concurrently in multiple threads. + * If an error occurs, then nullptr is returned. + * The caller must delete the clone. + * + * @return a clone of this object + * + * @see getDynamicClassID + * @stable ICU 2.8 + */ + ResourceBundle *clone() const; + + /** + * Returns the size of a resource. Size for scalar types is always 1, and for vector/table types is + * the number of child resources. + * @warning Integer array is treated as a scalar type. There are no + * APIs to access individual members of an integer array. It + * is always returned as a whole. + * + * @return number of resources in a given resource. + * @stable ICU 2.0 + */ + int32_t + getSize(void) const; + + /** + * returns a string from a string resource type + * + * @param status fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * could be a warning + * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING + * @return a pointer to a zero-terminated char16_t array which lives in a memory mapped/DLL file. + * @stable ICU 2.0 + */ + UnicodeString + getString(UErrorCode& status) const; + + /** + * returns a binary data from a resource. Can be used at most primitive resource types (binaries, + * strings, ints) + * + * @param len fills in the length of resulting byte chunk + * @param status fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * could be a warning + * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING + * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file. + * @stable ICU 2.0 + */ + const uint8_t* + getBinary(int32_t& len, UErrorCode& status) const; + + + /** + * returns an integer vector from a resource. + * + * @param len fills in the length of resulting integer vector + * @param status fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * could be a warning + * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING + * @return a pointer to a vector of integers that lives in a memory mapped/DLL file. + * @stable ICU 2.0 + */ + const int32_t* + getIntVector(int32_t& len, UErrorCode& status) const; + + /** + * returns an unsigned integer from a resource. + * This integer is originally 28 bits. + * + * @param status fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * could be a warning + * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING + * @return an unsigned integer value + * @stable ICU 2.0 + */ + uint32_t + getUInt(UErrorCode& status) const; + + /** + * returns a signed integer from a resource. + * This integer is originally 28 bit and the sign gets propagated. + * + * @param status fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * could be a warning + * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING + * @return a signed integer value + * @stable ICU 2.0 + */ + int32_t + getInt(UErrorCode& status) const; + + /** + * Checks whether the resource has another element to iterate over. + * + * @return true if there are more elements, false if there is no more elements + * @stable ICU 2.0 + */ + UBool + hasNext(void) const; + + /** + * Resets the internal context of a resource so that iteration starts from the first element. + * + * @stable ICU 2.0 + */ + void + resetIterator(void); + + /** + * Returns the key associated with this resource. Not all the resources have a key - only + * those that are members of a table. + * + * @return a key associated to this resource, or nullptr if it doesn't have a key + * @stable ICU 2.0 + */ + const char* + getKey(void) const; + + /** + * Gets the locale ID of the resource bundle as a string. + * Same as getLocale().getName() . + * + * @return the locale ID of the resource bundle as a string + * @stable ICU 2.0 + */ + const char* + getName(void) const; + + + /** + * Returns the type of a resource. Available types are defined in enum UResType + * + * @return type of the given resource. + * @stable ICU 2.0 + */ + UResType + getType(void) const; + + /** + * Returns the next resource in a given resource or nullptr if there are no more resources + * + * @param status fills in the outgoing error code + * @return ResourceBundle object. + * @stable ICU 2.0 + */ + ResourceBundle + getNext(UErrorCode& status); + + /** + * Returns the next string in a resource or nullptr if there are no more resources + * to iterate over. + * + * @param status fills in the outgoing error code + * @return an UnicodeString object. + * @stable ICU 2.0 + */ + UnicodeString + getNextString(UErrorCode& status); + + /** + * Returns the next string in a resource or nullptr if there are no more resources + * to iterate over. + * + * @param key fill in for key associated with this string + * @param status fills in the outgoing error code + * @return an UnicodeString object. + * @stable ICU 2.0 + */ + UnicodeString + getNextString(const char ** key, + UErrorCode& status); + + /** + * Returns the resource in a resource at the specified index. + * + * @param index an index to the wanted resource. + * @param status fills in the outgoing error code + * @return ResourceBundle object. If there is an error, resource is invalid. + * @stable ICU 2.0 + */ + ResourceBundle + get(int32_t index, + UErrorCode& status) const; + + /** + * Returns the string in a given resource at the specified index. + * + * @param index an index to the wanted string. + * @param status fills in the outgoing error code + * @return an UnicodeString object. If there is an error, string is bogus + * @stable ICU 2.0 + */ + UnicodeString + getStringEx(int32_t index, + UErrorCode& status) const; + + /** + * Returns a resource in a resource that has a given key. This procedure works only with table + * resources. + * + * @param key a key associated with the wanted resource + * @param status fills in the outgoing error code. + * @return ResourceBundle object. If there is an error, resource is invalid. + * @stable ICU 2.0 + */ + ResourceBundle + get(const char* key, + UErrorCode& status) const; + + /** + * Returns a string in a resource that has a given key. This procedure works only with table + * resources. + * + * @param key a key associated with the wanted string + * @param status fills in the outgoing error code + * @return an UnicodeString object. If there is an error, string is bogus + * @stable ICU 2.0 + */ + UnicodeString + getStringEx(const char* key, + UErrorCode& status) const; + +#ifndef U_HIDE_DEPRECATED_API + /** + * Return the version number associated with this ResourceBundle as a string. Please + * use getVersion, as this method is going to be deprecated. + * + * @return A version number string as specified in the resource bundle or its parent. + * The caller does not own this string. + * @see getVersion + * @deprecated ICU 2.8 Use getVersion instead. + */ + const char* + getVersionNumber(void) const; +#endif /* U_HIDE_DEPRECATED_API */ + + /** + * Return the version number associated with this ResourceBundle as a UVersionInfo array. + * + * @param versionInfo A UVersionInfo array that is filled with the version number + * as specified in the resource bundle or its parent. + * @stable ICU 2.0 + */ + void + getVersion(UVersionInfo versionInfo) const; + +#ifndef U_HIDE_DEPRECATED_API + /** + * Return the Locale associated with this ResourceBundle. + * + * @return a Locale object + * @deprecated ICU 2.8 Use getLocale(ULocDataLocaleType type, UErrorCode &status) overload instead. + */ + const Locale& + getLocale(void) const; +#endif /* U_HIDE_DEPRECATED_API */ + + /** + * Return the Locale associated with this ResourceBundle. + * @param type You can choose between requested, valid and actual + * locale. For description see the definition of + * ULocDataLocaleType in uloc.h + * @param status just for catching illegal arguments + * + * @return a Locale object + * @stable ICU 2.8 + */ + const Locale + getLocale(ULocDataLocaleType type, UErrorCode &status) const; +#ifndef U_HIDE_INTERNAL_API + /** + * This API implements multilevel fallback + * @internal + */ + ResourceBundle + getWithFallback(const char* key, UErrorCode& status); +#endif /* U_HIDE_INTERNAL_API */ + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + * + * @stable ICU 2.2 + */ + virtual UClassID getDynamicClassID() const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + * + * @stable ICU 2.2 + */ + static UClassID U_EXPORT2 getStaticClassID(); + +private: + ResourceBundle() = delete; // default constructor not implemented + + UResourceBundle *fResource; + void constructForLocale(const UnicodeString& path, const Locale& locale, UErrorCode& error); + Locale *fLocale; +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/schriter.h b/deps/icu-small/source/common/unicode/schriter.h index a2ab17982d1010..de4591771bef49 100644 --- a/deps/icu-small/source/common/unicode/schriter.h +++ b/deps/icu-small/source/common/unicode/schriter.h @@ -1,187 +1,187 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1998-2005, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* File schriter.h -* -* Modification History: -* -* Date Name Description -* 05/05/99 stephen Cleaned up. -****************************************************************************** -*/ - -#ifndef SCHRITER_H -#define SCHRITER_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/chariter.h" -#include "unicode/uchriter.h" - -/** - * \file - * \brief C++ API: String Character Iterator - */ - -U_NAMESPACE_BEGIN -/** - * A concrete subclass of CharacterIterator that iterates over the - * characters (code units or code points) in a UnicodeString. - * It's possible not only to create an - * iterator that iterates over an entire UnicodeString, but also to - * create one that iterates over only a subrange of a UnicodeString - * (iterators over different subranges of the same UnicodeString don't - * compare equal). - * @see CharacterIterator - * @see ForwardCharacterIterator - * @stable ICU 2.0 - */ -class U_COMMON_API StringCharacterIterator : public UCharCharacterIterator { -public: - /** - * Create an iterator over the UnicodeString referred to by "textStr". - * The UnicodeString object is copied. - * The iteration range is the whole string, and the starting position is 0. - * @param textStr The unicode string used to create an iterator - * @stable ICU 2.0 - */ - StringCharacterIterator(const UnicodeString& textStr); - - /** - * Create an iterator over the UnicodeString referred to by "textStr". - * The iteration range is the whole string, and the starting - * position is specified by "textPos". If "textPos" is outside the valid - * iteration range, the behavior of this object is undefined. - * @param textStr The unicode string used to create an iterator - * @param textPos The starting position of the iteration - * @stable ICU 2.0 - */ - StringCharacterIterator(const UnicodeString& textStr, - int32_t textPos); - - /** - * Create an iterator over the UnicodeString referred to by "textStr". - * The UnicodeString object is copied. - * The iteration range begins with the code unit specified by - * "textBegin" and ends with the code unit BEFORE the code unit specified - * by "textEnd". The starting position is specified by "textPos". If - * "textBegin" and "textEnd" don't form a valid range on "text" (i.e., - * textBegin >= textEnd or either is negative or greater than text.size()), - * or "textPos" is outside the range defined by "textBegin" and "textEnd", - * the behavior of this iterator is undefined. - * @param textStr The unicode string used to create the StringCharacterIterator - * @param textBegin The begin position of the iteration range - * @param textEnd The end position of the iteration range - * @param textPos The starting position of the iteration - * @stable ICU 2.0 - */ - StringCharacterIterator(const UnicodeString& textStr, - int32_t textBegin, - int32_t textEnd, - int32_t textPos); - - /** - * Copy constructor. The new iterator iterates over the same range - * of the same string as "that", and its initial position is the - * same as "that"'s current position. - * The UnicodeString object in "that" is copied. - * @param that The StringCharacterIterator to be copied - * @stable ICU 2.0 - */ - StringCharacterIterator(const StringCharacterIterator& that); - - /** - * Destructor. - * @stable ICU 2.0 - */ - virtual ~StringCharacterIterator(); - - /** - * Assignment operator. *this is altered to iterate over the same - * range of the same string as "that", and refers to the same - * character within that string as "that" does. - * @param that The object to be copied. - * @return the newly created object. - * @stable ICU 2.0 - */ - StringCharacterIterator& - operator=(const StringCharacterIterator& that); - - /** - * Returns true if the iterators iterate over the same range of the - * same string and are pointing at the same character. - * @param that The ForwardCharacterIterator to be compared for equality - * @return true if the iterators iterate over the same range of the - * same string and are pointing at the same character. - * @stable ICU 2.0 - */ - virtual bool operator==(const ForwardCharacterIterator& that) const override; - - /** - * Returns a new StringCharacterIterator referring to the same - * character in the same range of the same string as this one. The - * caller must delete the new iterator. - * @return the newly cloned object. - * @stable ICU 2.0 - */ - virtual StringCharacterIterator* clone() const override; - - /** - * Sets the iterator to iterate over the provided string. - * @param newText The string to be iterated over - * @stable ICU 2.0 - */ - void setText(const UnicodeString& newText); - - /** - * Copies the UnicodeString under iteration into the UnicodeString - * referred to by "result". Even if this iterator iterates across - * only a part of this string, the whole string is copied. - * @param result Receives a copy of the text under iteration. - * @stable ICU 2.0 - */ - virtual void getText(UnicodeString& result) override; - - /** - * Return a class ID for this object (not really public) - * @return a class ID for this object. - * @stable ICU 2.0 - */ - virtual UClassID getDynamicClassID(void) const override; - - /** - * Return a class ID for this class (not really public) - * @return a class ID for this class - * @stable ICU 2.0 - */ - static UClassID U_EXPORT2 getStaticClassID(void); - -protected: - /** - * Default constructor, iteration over empty string. - * @stable ICU 2.0 - */ - StringCharacterIterator(); - - /** - * Copy of the iterated string object. - * @stable ICU 2.0 - */ - UnicodeString text; - -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1998-2005, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* File schriter.h +* +* Modification History: +* +* Date Name Description +* 05/05/99 stephen Cleaned up. +****************************************************************************** +*/ + +#ifndef SCHRITER_H +#define SCHRITER_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/chariter.h" +#include "unicode/uchriter.h" + +/** + * \file + * \brief C++ API: String Character Iterator + */ + +U_NAMESPACE_BEGIN +/** + * A concrete subclass of CharacterIterator that iterates over the + * characters (code units or code points) in a UnicodeString. + * It's possible not only to create an + * iterator that iterates over an entire UnicodeString, but also to + * create one that iterates over only a subrange of a UnicodeString + * (iterators over different subranges of the same UnicodeString don't + * compare equal). + * @see CharacterIterator + * @see ForwardCharacterIterator + * @stable ICU 2.0 + */ +class U_COMMON_API StringCharacterIterator : public UCharCharacterIterator { +public: + /** + * Create an iterator over the UnicodeString referred to by "textStr". + * The UnicodeString object is copied. + * The iteration range is the whole string, and the starting position is 0. + * @param textStr The unicode string used to create an iterator + * @stable ICU 2.0 + */ + StringCharacterIterator(const UnicodeString& textStr); + + /** + * Create an iterator over the UnicodeString referred to by "textStr". + * The iteration range is the whole string, and the starting + * position is specified by "textPos". If "textPos" is outside the valid + * iteration range, the behavior of this object is undefined. + * @param textStr The unicode string used to create an iterator + * @param textPos The starting position of the iteration + * @stable ICU 2.0 + */ + StringCharacterIterator(const UnicodeString& textStr, + int32_t textPos); + + /** + * Create an iterator over the UnicodeString referred to by "textStr". + * The UnicodeString object is copied. + * The iteration range begins with the code unit specified by + * "textBegin" and ends with the code unit BEFORE the code unit specified + * by "textEnd". The starting position is specified by "textPos". If + * "textBegin" and "textEnd" don't form a valid range on "text" (i.e., + * textBegin >= textEnd or either is negative or greater than text.size()), + * or "textPos" is outside the range defined by "textBegin" and "textEnd", + * the behavior of this iterator is undefined. + * @param textStr The unicode string used to create the StringCharacterIterator + * @param textBegin The begin position of the iteration range + * @param textEnd The end position of the iteration range + * @param textPos The starting position of the iteration + * @stable ICU 2.0 + */ + StringCharacterIterator(const UnicodeString& textStr, + int32_t textBegin, + int32_t textEnd, + int32_t textPos); + + /** + * Copy constructor. The new iterator iterates over the same range + * of the same string as "that", and its initial position is the + * same as "that"'s current position. + * The UnicodeString object in "that" is copied. + * @param that The StringCharacterIterator to be copied + * @stable ICU 2.0 + */ + StringCharacterIterator(const StringCharacterIterator& that); + + /** + * Destructor. + * @stable ICU 2.0 + */ + virtual ~StringCharacterIterator(); + + /** + * Assignment operator. *this is altered to iterate over the same + * range of the same string as "that", and refers to the same + * character within that string as "that" does. + * @param that The object to be copied. + * @return the newly created object. + * @stable ICU 2.0 + */ + StringCharacterIterator& + operator=(const StringCharacterIterator& that); + + /** + * Returns true if the iterators iterate over the same range of the + * same string and are pointing at the same character. + * @param that The ForwardCharacterIterator to be compared for equality + * @return true if the iterators iterate over the same range of the + * same string and are pointing at the same character. + * @stable ICU 2.0 + */ + virtual bool operator==(const ForwardCharacterIterator& that) const override; + + /** + * Returns a new StringCharacterIterator referring to the same + * character in the same range of the same string as this one. The + * caller must delete the new iterator. + * @return the newly cloned object. + * @stable ICU 2.0 + */ + virtual StringCharacterIterator* clone() const override; + + /** + * Sets the iterator to iterate over the provided string. + * @param newText The string to be iterated over + * @stable ICU 2.0 + */ + void setText(const UnicodeString& newText); + + /** + * Copies the UnicodeString under iteration into the UnicodeString + * referred to by "result". Even if this iterator iterates across + * only a part of this string, the whole string is copied. + * @param result Receives a copy of the text under iteration. + * @stable ICU 2.0 + */ + virtual void getText(UnicodeString& result) override; + + /** + * Return a class ID for this object (not really public) + * @return a class ID for this object. + * @stable ICU 2.0 + */ + virtual UClassID getDynamicClassID(void) const override; + + /** + * Return a class ID for this class (not really public) + * @return a class ID for this class + * @stable ICU 2.0 + */ + static UClassID U_EXPORT2 getStaticClassID(void); + +protected: + /** + * Default constructor, iteration over empty string. + * @stable ICU 2.0 + */ + StringCharacterIterator(); + + /** + * Copy of the iterated string object. + * @stable ICU 2.0 + */ + UnicodeString text; + +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/simpleformatter.h b/deps/icu-small/source/common/unicode/simpleformatter.h index 6d9c04ace2359a..bd6cb4076f03ef 100644 --- a/deps/icu-small/source/common/unicode/simpleformatter.h +++ b/deps/icu-small/source/common/unicode/simpleformatter.h @@ -1,341 +1,341 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 2014-2016, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* simpleformatter.h -*/ - -#ifndef __SIMPLEFORMATTER_H__ -#define __SIMPLEFORMATTER_H__ - -/** - * \file - * \brief C++ API: Simple formatter, minimal subset of MessageFormat. - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/unistr.h" - -U_NAMESPACE_BEGIN - -// Forward declaration: -namespace number { -namespace impl { -class SimpleModifier; -} -} - -/** - * Formats simple patterns like "{1} was born in {0}". - * Minimal subset of MessageFormat; fast, simple, minimal dependencies. - * Supports only numbered arguments with no type nor style parameters, - * and formats only string values. - * Quoting via ASCII apostrophe compatible with ICU MessageFormat default behavior. - * - * Factory methods set error codes for syntax errors - * and for too few or too many arguments/placeholders. - * - * SimpleFormatter objects are thread-safe except for assignment and applying new patterns. - * - * Example: - *

- * UErrorCode errorCode = U_ZERO_ERROR;
- * SimpleFormatter fmt("{1} '{born}' in {0}", errorCode);
- * UnicodeString result;
- *
- * // Output: "paul {born} in england"
- * fmt.format("england", "paul", result, errorCode);
- * 
- * - * This class is not intended for public subclassing. - * - * @see MessageFormat - * @see UMessagePatternApostropheMode - * @stable ICU 57 - */ -class U_COMMON_API SimpleFormatter U_FINAL : public UMemory { -public: - /** - * Default constructor. - * @stable ICU 57 - */ - SimpleFormatter() : compiledPattern((char16_t)0) {} - - /** - * Constructs a formatter from the pattern string. - * - * @param pattern The pattern string. - * @param errorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * Set to U_ILLEGAL_ARGUMENT_ERROR for bad argument syntax. - * @stable ICU 57 - */ - SimpleFormatter(const UnicodeString& pattern, UErrorCode &errorCode) { - applyPattern(pattern, errorCode); - } - - /** - * Constructs a formatter from the pattern string. - * The number of arguments checked against the given limits is the - * highest argument number plus one, not the number of occurrences of arguments. - * - * @param pattern The pattern string. - * @param min The pattern must have at least this many arguments. - * @param max The pattern must have at most this many arguments. - * @param errorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * Set to U_ILLEGAL_ARGUMENT_ERROR for bad argument syntax and - * too few or too many arguments. - * @stable ICU 57 - */ - SimpleFormatter(const UnicodeString& pattern, int32_t min, int32_t max, - UErrorCode &errorCode) { - applyPatternMinMaxArguments(pattern, min, max, errorCode); - } - - /** - * Copy constructor. - * @stable ICU 57 - */ - SimpleFormatter(const SimpleFormatter& other) - : compiledPattern(other.compiledPattern) {} - - /** - * Assignment operator. - * @stable ICU 57 - */ - SimpleFormatter &operator=(const SimpleFormatter& other); - - /** - * Destructor. - * @stable ICU 57 - */ - ~SimpleFormatter(); - - /** - * Changes this object according to the new pattern. - * - * @param pattern The pattern string. - * @param errorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * Set to U_ILLEGAL_ARGUMENT_ERROR for bad argument syntax. - * @return true if U_SUCCESS(errorCode). - * @stable ICU 57 - */ - UBool applyPattern(const UnicodeString &pattern, UErrorCode &errorCode) { - return applyPatternMinMaxArguments(pattern, 0, INT32_MAX, errorCode); - } - - /** - * Changes this object according to the new pattern. - * The number of arguments checked against the given limits is the - * highest argument number plus one, not the number of occurrences of arguments. - * - * @param pattern The pattern string. - * @param min The pattern must have at least this many arguments. - * @param max The pattern must have at most this many arguments. - * @param errorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * Set to U_ILLEGAL_ARGUMENT_ERROR for bad argument syntax and - * too few or too many arguments. - * @return true if U_SUCCESS(errorCode). - * @stable ICU 57 - */ - UBool applyPatternMinMaxArguments(const UnicodeString &pattern, - int32_t min, int32_t max, UErrorCode &errorCode); - - /** - * @return The max argument number + 1. - * @stable ICU 57 - */ - int32_t getArgumentLimit() const { - return getArgumentLimit(compiledPattern.getBuffer(), compiledPattern.length()); - } - - /** - * Formats the given value, appending to the appendTo builder. - * The argument value must not be the same object as appendTo. - * getArgumentLimit() must be at most 1. - * - * @param value0 Value for argument {0}. - * @param appendTo Gets the formatted pattern and value appended. - * @param errorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return appendTo - * @stable ICU 57 - */ - UnicodeString &format( - const UnicodeString &value0, - UnicodeString &appendTo, UErrorCode &errorCode) const; - - /** - * Formats the given values, appending to the appendTo builder. - * An argument value must not be the same object as appendTo. - * getArgumentLimit() must be at most 2. - * - * @param value0 Value for argument {0}. - * @param value1 Value for argument {1}. - * @param appendTo Gets the formatted pattern and values appended. - * @param errorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return appendTo - * @stable ICU 57 - */ - UnicodeString &format( - const UnicodeString &value0, - const UnicodeString &value1, - UnicodeString &appendTo, UErrorCode &errorCode) const; - - /** - * Formats the given values, appending to the appendTo builder. - * An argument value must not be the same object as appendTo. - * getArgumentLimit() must be at most 3. - * - * @param value0 Value for argument {0}. - * @param value1 Value for argument {1}. - * @param value2 Value for argument {2}. - * @param appendTo Gets the formatted pattern and values appended. - * @param errorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return appendTo - * @stable ICU 57 - */ - UnicodeString &format( - const UnicodeString &value0, - const UnicodeString &value1, - const UnicodeString &value2, - UnicodeString &appendTo, UErrorCode &errorCode) const; - - /** - * Formats the given values, appending to the appendTo string. - * - * @param values The argument values. - * An argument value must not be the same object as appendTo. - * Can be NULL if valuesLength==getArgumentLimit()==0. - * @param valuesLength The length of the values array. - * Must be at least getArgumentLimit(). - * @param appendTo Gets the formatted pattern and values appended. - * @param offsets offsets[i] receives the offset of where - * values[i] replaced pattern argument {i}. - * Can be shorter or longer than values. Can be NULL if offsetsLength==0. - * If there is no {i} in the pattern, then offsets[i] is set to -1. - * @param offsetsLength The length of the offsets array. - * @param errorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return appendTo - * @stable ICU 57 - */ - UnicodeString &formatAndAppend( - const UnicodeString *const *values, int32_t valuesLength, - UnicodeString &appendTo, - int32_t *offsets, int32_t offsetsLength, UErrorCode &errorCode) const; - - /** - * Formats the given values, replacing the contents of the result string. - * May optimize by actually appending to the result if it is the same object - * as the value corresponding to the initial argument in the pattern. - * - * @param values The argument values. - * An argument value may be the same object as result. - * Can be NULL if valuesLength==getArgumentLimit()==0. - * @param valuesLength The length of the values array. - * Must be at least getArgumentLimit(). - * @param result Gets its contents replaced by the formatted pattern and values. - * @param offsets offsets[i] receives the offset of where - * values[i] replaced pattern argument {i}. - * Can be shorter or longer than values. Can be NULL if offsetsLength==0. - * If there is no {i} in the pattern, then offsets[i] is set to -1. - * @param offsetsLength The length of the offsets array. - * @param errorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return result - * @stable ICU 57 - */ - UnicodeString &formatAndReplace( - const UnicodeString *const *values, int32_t valuesLength, - UnicodeString &result, - int32_t *offsets, int32_t offsetsLength, UErrorCode &errorCode) const; - - /** - * Returns the pattern text with none of the arguments. - * Like formatting with all-empty string values. - * @stable ICU 57 - */ - UnicodeString getTextWithNoArguments() const { - return getTextWithNoArguments( - compiledPattern.getBuffer(), - compiledPattern.length(), - nullptr, - 0); - } - -#ifndef U_HIDE_INTERNAL_API - /** - * Returns the pattern text with none of the arguments. - * Like formatting with all-empty string values. - * - * TODO(ICU-20406): Replace this with an Iterator interface. - * - * @param offsets offsets[i] receives the offset of where {i} was located - * before it was replaced by an empty string. - * For example, "a{0}b{1}" produces offset 1 for i=0 and 2 for i=1. - * Can be nullptr if offsetsLength==0. - * If there is no {i} in the pattern, then offsets[i] is set to -1. - * @param offsetsLength The length of the offsets array. - * - * @internal - */ - UnicodeString getTextWithNoArguments(int32_t *offsets, int32_t offsetsLength) const { - return getTextWithNoArguments( - compiledPattern.getBuffer(), - compiledPattern.length(), - offsets, - offsetsLength); - } -#endif // U_HIDE_INTERNAL_API - -private: - /** - * Binary representation of the compiled pattern. - * Index 0: One more than the highest argument number. - * Followed by zero or more arguments or literal-text segments. - * - * An argument is stored as its number, less than ARG_NUM_LIMIT. - * A literal-text segment is stored as its length (at least 1) offset by ARG_NUM_LIMIT, - * followed by that many chars. - */ - UnicodeString compiledPattern; - - static inline int32_t getArgumentLimit(const char16_t *compiledPattern, - int32_t compiledPatternLength) { - return compiledPatternLength == 0 ? 0 : compiledPattern[0]; - } - - static UnicodeString getTextWithNoArguments( - const char16_t *compiledPattern, - int32_t compiledPatternLength, - int32_t *offsets, - int32_t offsetsLength); - - static UnicodeString &format( - const char16_t *compiledPattern, int32_t compiledPatternLength, - const UnicodeString *const *values, - UnicodeString &result, const UnicodeString *resultCopy, UBool forbidResultAsValue, - int32_t *offsets, int32_t offsetsLength, - UErrorCode &errorCode); - - // Give access to internals to SimpleModifier for number formatting - friend class number::impl::SimpleModifier; -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __SIMPLEFORMATTER_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 2014-2016, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* simpleformatter.h +*/ + +#ifndef __SIMPLEFORMATTER_H__ +#define __SIMPLEFORMATTER_H__ + +/** + * \file + * \brief C++ API: Simple formatter, minimal subset of MessageFormat. + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/unistr.h" + +U_NAMESPACE_BEGIN + +// Forward declaration: +namespace number { +namespace impl { +class SimpleModifier; +} +} + +/** + * Formats simple patterns like "{1} was born in {0}". + * Minimal subset of MessageFormat; fast, simple, minimal dependencies. + * Supports only numbered arguments with no type nor style parameters, + * and formats only string values. + * Quoting via ASCII apostrophe compatible with ICU MessageFormat default behavior. + * + * Factory methods set error codes for syntax errors + * and for too few or too many arguments/placeholders. + * + * SimpleFormatter objects are thread-safe except for assignment and applying new patterns. + * + * Example: + *
+ * UErrorCode errorCode = U_ZERO_ERROR;
+ * SimpleFormatter fmt("{1} '{born}' in {0}", errorCode);
+ * UnicodeString result;
+ *
+ * // Output: "paul {born} in england"
+ * fmt.format("england", "paul", result, errorCode);
+ * 
+ * + * This class is not intended for public subclassing. + * + * @see MessageFormat + * @see UMessagePatternApostropheMode + * @stable ICU 57 + */ +class U_COMMON_API SimpleFormatter final : public UMemory { +public: + /** + * Default constructor. + * @stable ICU 57 + */ + SimpleFormatter() : compiledPattern((char16_t)0) {} + + /** + * Constructs a formatter from the pattern string. + * + * @param pattern The pattern string. + * @param errorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * Set to U_ILLEGAL_ARGUMENT_ERROR for bad argument syntax. + * @stable ICU 57 + */ + SimpleFormatter(const UnicodeString& pattern, UErrorCode &errorCode) { + applyPattern(pattern, errorCode); + } + + /** + * Constructs a formatter from the pattern string. + * The number of arguments checked against the given limits is the + * highest argument number plus one, not the number of occurrences of arguments. + * + * @param pattern The pattern string. + * @param min The pattern must have at least this many arguments. + * @param max The pattern must have at most this many arguments. + * @param errorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * Set to U_ILLEGAL_ARGUMENT_ERROR for bad argument syntax and + * too few or too many arguments. + * @stable ICU 57 + */ + SimpleFormatter(const UnicodeString& pattern, int32_t min, int32_t max, + UErrorCode &errorCode) { + applyPatternMinMaxArguments(pattern, min, max, errorCode); + } + + /** + * Copy constructor. + * @stable ICU 57 + */ + SimpleFormatter(const SimpleFormatter& other) + : compiledPattern(other.compiledPattern) {} + + /** + * Assignment operator. + * @stable ICU 57 + */ + SimpleFormatter &operator=(const SimpleFormatter& other); + + /** + * Destructor. + * @stable ICU 57 + */ + ~SimpleFormatter(); + + /** + * Changes this object according to the new pattern. + * + * @param pattern The pattern string. + * @param errorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * Set to U_ILLEGAL_ARGUMENT_ERROR for bad argument syntax. + * @return true if U_SUCCESS(errorCode). + * @stable ICU 57 + */ + UBool applyPattern(const UnicodeString &pattern, UErrorCode &errorCode) { + return applyPatternMinMaxArguments(pattern, 0, INT32_MAX, errorCode); + } + + /** + * Changes this object according to the new pattern. + * The number of arguments checked against the given limits is the + * highest argument number plus one, not the number of occurrences of arguments. + * + * @param pattern The pattern string. + * @param min The pattern must have at least this many arguments. + * @param max The pattern must have at most this many arguments. + * @param errorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * Set to U_ILLEGAL_ARGUMENT_ERROR for bad argument syntax and + * too few or too many arguments. + * @return true if U_SUCCESS(errorCode). + * @stable ICU 57 + */ + UBool applyPatternMinMaxArguments(const UnicodeString &pattern, + int32_t min, int32_t max, UErrorCode &errorCode); + + /** + * @return The max argument number + 1. + * @stable ICU 57 + */ + int32_t getArgumentLimit() const { + return getArgumentLimit(compiledPattern.getBuffer(), compiledPattern.length()); + } + + /** + * Formats the given value, appending to the appendTo builder. + * The argument value must not be the same object as appendTo. + * getArgumentLimit() must be at most 1. + * + * @param value0 Value for argument {0}. + * @param appendTo Gets the formatted pattern and value appended. + * @param errorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return appendTo + * @stable ICU 57 + */ + UnicodeString &format( + const UnicodeString &value0, + UnicodeString &appendTo, UErrorCode &errorCode) const; + + /** + * Formats the given values, appending to the appendTo builder. + * An argument value must not be the same object as appendTo. + * getArgumentLimit() must be at most 2. + * + * @param value0 Value for argument {0}. + * @param value1 Value for argument {1}. + * @param appendTo Gets the formatted pattern and values appended. + * @param errorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return appendTo + * @stable ICU 57 + */ + UnicodeString &format( + const UnicodeString &value0, + const UnicodeString &value1, + UnicodeString &appendTo, UErrorCode &errorCode) const; + + /** + * Formats the given values, appending to the appendTo builder. + * An argument value must not be the same object as appendTo. + * getArgumentLimit() must be at most 3. + * + * @param value0 Value for argument {0}. + * @param value1 Value for argument {1}. + * @param value2 Value for argument {2}. + * @param appendTo Gets the formatted pattern and values appended. + * @param errorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return appendTo + * @stable ICU 57 + */ + UnicodeString &format( + const UnicodeString &value0, + const UnicodeString &value1, + const UnicodeString &value2, + UnicodeString &appendTo, UErrorCode &errorCode) const; + + /** + * Formats the given values, appending to the appendTo string. + * + * @param values The argument values. + * An argument value must not be the same object as appendTo. + * Can be nullptr if valuesLength==getArgumentLimit()==0. + * @param valuesLength The length of the values array. + * Must be at least getArgumentLimit(). + * @param appendTo Gets the formatted pattern and values appended. + * @param offsets offsets[i] receives the offset of where + * values[i] replaced pattern argument {i}. + * Can be shorter or longer than values. Can be nullptr if offsetsLength==0. + * If there is no {i} in the pattern, then offsets[i] is set to -1. + * @param offsetsLength The length of the offsets array. + * @param errorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return appendTo + * @stable ICU 57 + */ + UnicodeString &formatAndAppend( + const UnicodeString *const *values, int32_t valuesLength, + UnicodeString &appendTo, + int32_t *offsets, int32_t offsetsLength, UErrorCode &errorCode) const; + + /** + * Formats the given values, replacing the contents of the result string. + * May optimize by actually appending to the result if it is the same object + * as the value corresponding to the initial argument in the pattern. + * + * @param values The argument values. + * An argument value may be the same object as result. + * Can be nullptr if valuesLength==getArgumentLimit()==0. + * @param valuesLength The length of the values array. + * Must be at least getArgumentLimit(). + * @param result Gets its contents replaced by the formatted pattern and values. + * @param offsets offsets[i] receives the offset of where + * values[i] replaced pattern argument {i}. + * Can be shorter or longer than values. Can be nullptr if offsetsLength==0. + * If there is no {i} in the pattern, then offsets[i] is set to -1. + * @param offsetsLength The length of the offsets array. + * @param errorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return result + * @stable ICU 57 + */ + UnicodeString &formatAndReplace( + const UnicodeString *const *values, int32_t valuesLength, + UnicodeString &result, + int32_t *offsets, int32_t offsetsLength, UErrorCode &errorCode) const; + + /** + * Returns the pattern text with none of the arguments. + * Like formatting with all-empty string values. + * @stable ICU 57 + */ + UnicodeString getTextWithNoArguments() const { + return getTextWithNoArguments( + compiledPattern.getBuffer(), + compiledPattern.length(), + nullptr, + 0); + } + +#ifndef U_HIDE_INTERNAL_API + /** + * Returns the pattern text with none of the arguments. + * Like formatting with all-empty string values. + * + * TODO(ICU-20406): Replace this with an Iterator interface. + * + * @param offsets offsets[i] receives the offset of where {i} was located + * before it was replaced by an empty string. + * For example, "a{0}b{1}" produces offset 1 for i=0 and 2 for i=1. + * Can be nullptr if offsetsLength==0. + * If there is no {i} in the pattern, then offsets[i] is set to -1. + * @param offsetsLength The length of the offsets array. + * + * @internal + */ + UnicodeString getTextWithNoArguments(int32_t *offsets, int32_t offsetsLength) const { + return getTextWithNoArguments( + compiledPattern.getBuffer(), + compiledPattern.length(), + offsets, + offsetsLength); + } +#endif // U_HIDE_INTERNAL_API + +private: + /** + * Binary representation of the compiled pattern. + * Index 0: One more than the highest argument number. + * Followed by zero or more arguments or literal-text segments. + * + * An argument is stored as its number, less than ARG_NUM_LIMIT. + * A literal-text segment is stored as its length (at least 1) offset by ARG_NUM_LIMIT, + * followed by that many chars. + */ + UnicodeString compiledPattern; + + static inline int32_t getArgumentLimit(const char16_t *compiledPattern, + int32_t compiledPatternLength) { + return compiledPatternLength == 0 ? 0 : compiledPattern[0]; + } + + static UnicodeString getTextWithNoArguments( + const char16_t *compiledPattern, + int32_t compiledPatternLength, + int32_t *offsets, + int32_t offsetsLength); + + static UnicodeString &format( + const char16_t *compiledPattern, int32_t compiledPatternLength, + const UnicodeString *const *values, + UnicodeString &result, const UnicodeString *resultCopy, UBool forbidResultAsValue, + int32_t *offsets, int32_t offsetsLength, + UErrorCode &errorCode); + + // Give access to internals to SimpleModifier for number formatting + friend class number::impl::SimpleModifier; +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __SIMPLEFORMATTER_H__ diff --git a/deps/icu-small/source/common/unicode/std_string.h b/deps/icu-small/source/common/unicode/std_string.h index bf87230167ecf2..04a96ccb472696 100644 --- a/deps/icu-small/source/common/unicode/std_string.h +++ b/deps/icu-small/source/common/unicode/std_string.h @@ -1,41 +1,41 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2009-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: std_string.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2009feb19 -* created by: Markus W. Scherer -*/ - -#ifndef __STD_STRING_H__ -#define __STD_STRING_H__ - -/** - * \file - * \brief C++ API: Central ICU header for including the C++ standard <string> - * header and for related definitions. - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -// Workaround for a libstdc++ bug before libstdc++4.6 (2011). -// https://bugs.llvm.org/show_bug.cgi?id=13364 -#if defined(__GLIBCXX__) -namespace std { class type_info; } -#endif -#include - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __STD_STRING_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2009-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: std_string.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2009feb19 +* created by: Markus W. Scherer +*/ + +#ifndef __STD_STRING_H__ +#define __STD_STRING_H__ + +/** + * \file + * \brief C++ API: Central ICU header for including the C++ standard <string> + * header and for related definitions. + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +// Workaround for a libstdc++ bug before libstdc++4.6 (2011). +// https://bugs.llvm.org/show_bug.cgi?id=13364 +#if defined(__GLIBCXX__) +namespace std { class type_info; } +#endif +#include + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __STD_STRING_H__ diff --git a/deps/icu-small/source/common/unicode/strenum.h b/deps/icu-small/source/common/unicode/strenum.h index 1d1b37940a87d9..472ed6a920e5e7 100644 --- a/deps/icu-small/source/common/unicode/strenum.h +++ b/deps/icu-small/source/common/unicode/strenum.h @@ -1,281 +1,281 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -*/ - -#ifndef STRENUM_H -#define STRENUM_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/uobject.h" -#include "unicode/unistr.h" - -/** - * \file - * \brief C++ API: String Enumeration - */ - -U_NAMESPACE_BEGIN - -/** - * Base class for 'pure' C++ implementations of uenum api. Adds a - * method that returns the next UnicodeString since in C++ this can - * be a common storage format for strings. - * - *

The model is that the enumeration is over strings maintained by - * a 'service.' At any point, the service might change, invalidating - * the enumerator (though this is expected to be rare). The iterator - * returns an error if this has occurred. Lack of the error is no - * guarantee that the service didn't change immediately after the - * call, so the returned string still might not be 'valid' on - * subsequent use.

- * - *

Strings may take the form of const char*, const char16_t*, or const - * UnicodeString*. The type you get is determine by the variant of - * 'next' that you call. In general the StringEnumeration is - * optimized for one of these types, but all StringEnumerations can - * return all types. Returned strings are each terminated with a NUL. - * Depending on the service data, they might also include embedded NUL - * characters, so API is provided to optionally return the true - * length, counting the embedded NULs but not counting the terminating - * NUL.

- * - *

The pointers returned by next, unext, and snext become invalid - * upon any subsequent call to the enumeration's destructor, next, - * unext, snext, or reset.

- * - * ICU 2.8 adds some default implementations and helper functions - * for subclasses. - * - * @stable ICU 2.4 - */ -class U_COMMON_API StringEnumeration : public UObject { -public: - /** - * Destructor. - * @stable ICU 2.4 - */ - virtual ~StringEnumeration(); - - /** - * Clone this object, an instance of a subclass of StringEnumeration. - * Clones can be used concurrently in multiple threads. - * If a subclass does not implement clone(), or if an error occurs, - * then NULL is returned. - * The caller must delete the clone. - * - * @return a clone of this object - * - * @see getDynamicClassID - * @stable ICU 2.8 - */ - virtual StringEnumeration *clone() const; - - /** - *

Return the number of elements that the iterator traverses. If - * the iterator is out of sync with its service, status is set to - * U_ENUM_OUT_OF_SYNC_ERROR, and the return value is zero.

- * - *

The return value will not change except possibly as a result of - * a subsequent call to reset, or if the iterator becomes out of sync.

- * - *

This is a convenience function. It can end up being very - * expensive as all the items might have to be pre-fetched - * (depending on the storage format of the data being - * traversed).

- * - * @param status the error code. - * @return number of elements in the iterator. - * - * @stable ICU 2.4 */ - virtual int32_t count(UErrorCode& status) const = 0; - - /** - *

Returns the next element as a NUL-terminated char*. If there - * are no more elements, returns NULL. If the resultLength pointer - * is not NULL, the length of the string (not counting the - * terminating NUL) is returned at that address. If an error - * status is returned, the value at resultLength is undefined.

- * - *

The returned pointer is owned by this iterator and must not be - * deleted by the caller. The pointer is valid until the next call - * to next, unext, snext, reset, or the enumerator's destructor.

- * - *

If the iterator is out of sync with its service, status is set - * to U_ENUM_OUT_OF_SYNC_ERROR and NULL is returned.

- * - *

If the native service string is a char16_t* string, it is - * converted to char* with the invariant converter. If the - * conversion fails (because a character cannot be converted) then - * status is set to U_INVARIANT_CONVERSION_ERROR and the return - * value is undefined (though not NULL).

- * - * Starting with ICU 2.8, the default implementation calls snext() - * and handles the conversion. - * Either next() or snext() must be implemented differently by a subclass. - * - * @param status the error code. - * @param resultLength a pointer to receive the length, can be NULL. - * @return a pointer to the string, or NULL. - * - * @stable ICU 2.4 - */ - virtual const char* next(int32_t *resultLength, UErrorCode& status); - - /** - *

Returns the next element as a NUL-terminated char16_t*. If there - * are no more elements, returns NULL. If the resultLength pointer - * is not NULL, the length of the string (not counting the - * terminating NUL) is returned at that address. If an error - * status is returned, the value at resultLength is undefined.

- * - *

The returned pointer is owned by this iterator and must not be - * deleted by the caller. The pointer is valid until the next call - * to next, unext, snext, reset, or the enumerator's destructor.

- * - *

If the iterator is out of sync with its service, status is set - * to U_ENUM_OUT_OF_SYNC_ERROR and NULL is returned.

- * - * Starting with ICU 2.8, the default implementation calls snext() - * and handles the conversion. - * - * @param status the error code. - * @param resultLength a pointer to receive the length, can be NULL. - * @return a pointer to the string, or NULL. - * - * @stable ICU 2.4 - */ - virtual const char16_t* unext(int32_t *resultLength, UErrorCode& status); - - /** - *

Returns the next element a UnicodeString*. If there are no - * more elements, returns NULL.

- * - *

The returned pointer is owned by this iterator and must not be - * deleted by the caller. The pointer is valid until the next call - * to next, unext, snext, reset, or the enumerator's destructor.

- * - *

If the iterator is out of sync with its service, status is set - * to U_ENUM_OUT_OF_SYNC_ERROR and NULL is returned.

- * - * Starting with ICU 2.8, the default implementation calls next() - * and handles the conversion. - * Either next() or snext() must be implemented differently by a subclass. - * - * @param status the error code. - * @return a pointer to the string, or NULL. - * - * @stable ICU 2.4 - */ - virtual const UnicodeString* snext(UErrorCode& status); - - /** - *

Resets the iterator. This re-establishes sync with the - * service and rewinds the iterator to start at the first - * element.

- * - *

Previous pointers returned by next, unext, or snext become - * invalid, and the value returned by count might change.

- * - * @param status the error code. - * - * @stable ICU 2.4 - */ - virtual void reset(UErrorCode& status) = 0; - - /** - * Compares this enumeration to other to check if both are equal - * - * @param that The other string enumeration to compare this object to - * @return true if the enumerations are equal. false if not. - * @stable ICU 3.6 - */ - virtual bool operator==(const StringEnumeration& that)const; - /** - * Compares this enumeration to other to check if both are not equal - * - * @param that The other string enumeration to compare this object to - * @return true if the enumerations are equal. false if not. - * @stable ICU 3.6 - */ - virtual bool operator!=(const StringEnumeration& that)const; - -protected: - /** - * UnicodeString field for use with default implementations and subclasses. - * @stable ICU 2.8 - */ - UnicodeString unistr; - /** - * char * default buffer for use with default implementations and subclasses. - * @stable ICU 2.8 - */ - char charsBuffer[32]; - /** - * char * buffer for use with default implementations and subclasses. - * Allocated in constructor and in ensureCharsCapacity(). - * @stable ICU 2.8 - */ - char *chars; - /** - * Capacity of chars, for use with default implementations and subclasses. - * @stable ICU 2.8 - */ - int32_t charsCapacity; - - /** - * Default constructor for use with default implementations and subclasses. - * @stable ICU 2.8 - */ - StringEnumeration(); - - /** - * Ensures that chars is at least as large as the requested capacity. - * For use with default implementations and subclasses. - * - * @param capacity Requested capacity. - * @param status ICU in/out error code. - * @stable ICU 2.8 - */ - void ensureCharsCapacity(int32_t capacity, UErrorCode &status); - - /** - * Converts s to Unicode and sets unistr to the result. - * For use with default implementations and subclasses, - * especially for implementations of snext() in terms of next(). - * This is provided with a helper function instead of a default implementation - * of snext() to avoid potential infinite loops between next() and snext(). - * - * For example: - * \code - * const UnicodeString* snext(UErrorCode& status) { - * int32_t resultLength=0; - * const char *s=next(&resultLength, status); - * return setChars(s, resultLength, status); - * } - * \endcode - * - * @param s String to be converted to Unicode. - * @param length Length of the string. - * @param status ICU in/out error code. - * @return A pointer to unistr. - * @stable ICU 2.8 - */ - UnicodeString *setChars(const char *s, int32_t length, UErrorCode &status); -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -/* STRENUM_H */ -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +*/ + +#ifndef STRENUM_H +#define STRENUM_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/uobject.h" +#include "unicode/unistr.h" + +/** + * \file + * \brief C++ API: String Enumeration + */ + +U_NAMESPACE_BEGIN + +/** + * Base class for 'pure' C++ implementations of uenum api. Adds a + * method that returns the next UnicodeString since in C++ this can + * be a common storage format for strings. + * + *

The model is that the enumeration is over strings maintained by + * a 'service.' At any point, the service might change, invalidating + * the enumerator (though this is expected to be rare). The iterator + * returns an error if this has occurred. Lack of the error is no + * guarantee that the service didn't change immediately after the + * call, so the returned string still might not be 'valid' on + * subsequent use.

+ * + *

Strings may take the form of const char*, const char16_t*, or const + * UnicodeString*. The type you get is determine by the variant of + * 'next' that you call. In general the StringEnumeration is + * optimized for one of these types, but all StringEnumerations can + * return all types. Returned strings are each terminated with a NUL. + * Depending on the service data, they might also include embedded NUL + * characters, so API is provided to optionally return the true + * length, counting the embedded NULs but not counting the terminating + * NUL.

+ * + *

The pointers returned by next, unext, and snext become invalid + * upon any subsequent call to the enumeration's destructor, next, + * unext, snext, or reset.

+ * + * ICU 2.8 adds some default implementations and helper functions + * for subclasses. + * + * @stable ICU 2.4 + */ +class U_COMMON_API StringEnumeration : public UObject { +public: + /** + * Destructor. + * @stable ICU 2.4 + */ + virtual ~StringEnumeration(); + + /** + * Clone this object, an instance of a subclass of StringEnumeration. + * Clones can be used concurrently in multiple threads. + * If a subclass does not implement clone(), or if an error occurs, + * then nullptr is returned. + * The caller must delete the clone. + * + * @return a clone of this object + * + * @see getDynamicClassID + * @stable ICU 2.8 + */ + virtual StringEnumeration *clone() const; + + /** + *

Return the number of elements that the iterator traverses. If + * the iterator is out of sync with its service, status is set to + * U_ENUM_OUT_OF_SYNC_ERROR, and the return value is zero.

+ * + *

The return value will not change except possibly as a result of + * a subsequent call to reset, or if the iterator becomes out of sync.

+ * + *

This is a convenience function. It can end up being very + * expensive as all the items might have to be pre-fetched + * (depending on the storage format of the data being + * traversed).

+ * + * @param status the error code. + * @return number of elements in the iterator. + * + * @stable ICU 2.4 */ + virtual int32_t count(UErrorCode& status) const = 0; + + /** + *

Returns the next element as a NUL-terminated char*. If there + * are no more elements, returns nullptr. If the resultLength pointer + * is not nullptr, the length of the string (not counting the + * terminating NUL) is returned at that address. If an error + * status is returned, the value at resultLength is undefined.

+ * + *

The returned pointer is owned by this iterator and must not be + * deleted by the caller. The pointer is valid until the next call + * to next, unext, snext, reset, or the enumerator's destructor.

+ * + *

If the iterator is out of sync with its service, status is set + * to U_ENUM_OUT_OF_SYNC_ERROR and nullptr is returned.

+ * + *

If the native service string is a char16_t* string, it is + * converted to char* with the invariant converter. If the + * conversion fails (because a character cannot be converted) then + * status is set to U_INVARIANT_CONVERSION_ERROR and the return + * value is undefined (though not nullptr).

+ * + * Starting with ICU 2.8, the default implementation calls snext() + * and handles the conversion. + * Either next() or snext() must be implemented differently by a subclass. + * + * @param status the error code. + * @param resultLength a pointer to receive the length, can be nullptr. + * @return a pointer to the string, or nullptr. + * + * @stable ICU 2.4 + */ + virtual const char* next(int32_t *resultLength, UErrorCode& status); + + /** + *

Returns the next element as a NUL-terminated char16_t*. If there + * are no more elements, returns nullptr. If the resultLength pointer + * is not nullptr, the length of the string (not counting the + * terminating NUL) is returned at that address. If an error + * status is returned, the value at resultLength is undefined.

+ * + *

The returned pointer is owned by this iterator and must not be + * deleted by the caller. The pointer is valid until the next call + * to next, unext, snext, reset, or the enumerator's destructor.

+ * + *

If the iterator is out of sync with its service, status is set + * to U_ENUM_OUT_OF_SYNC_ERROR and nullptr is returned.

+ * + * Starting with ICU 2.8, the default implementation calls snext() + * and handles the conversion. + * + * @param status the error code. + * @param resultLength a pointer to receive the length, can be nullptr. + * @return a pointer to the string, or nullptr. + * + * @stable ICU 2.4 + */ + virtual const char16_t* unext(int32_t *resultLength, UErrorCode& status); + + /** + *

Returns the next element a UnicodeString*. If there are no + * more elements, returns nullptr.

+ * + *

The returned pointer is owned by this iterator and must not be + * deleted by the caller. The pointer is valid until the next call + * to next, unext, snext, reset, or the enumerator's destructor.

+ * + *

If the iterator is out of sync with its service, status is set + * to U_ENUM_OUT_OF_SYNC_ERROR and nullptr is returned.

+ * + * Starting with ICU 2.8, the default implementation calls next() + * and handles the conversion. + * Either next() or snext() must be implemented differently by a subclass. + * + * @param status the error code. + * @return a pointer to the string, or nullptr. + * + * @stable ICU 2.4 + */ + virtual const UnicodeString* snext(UErrorCode& status); + + /** + *

Resets the iterator. This re-establishes sync with the + * service and rewinds the iterator to start at the first + * element.

+ * + *

Previous pointers returned by next, unext, or snext become + * invalid, and the value returned by count might change.

+ * + * @param status the error code. + * + * @stable ICU 2.4 + */ + virtual void reset(UErrorCode& status) = 0; + + /** + * Compares this enumeration to other to check if both are equal + * + * @param that The other string enumeration to compare this object to + * @return true if the enumerations are equal. false if not. + * @stable ICU 3.6 + */ + virtual bool operator==(const StringEnumeration& that)const; + /** + * Compares this enumeration to other to check if both are not equal + * + * @param that The other string enumeration to compare this object to + * @return true if the enumerations are equal. false if not. + * @stable ICU 3.6 + */ + virtual bool operator!=(const StringEnumeration& that)const; + +protected: + /** + * UnicodeString field for use with default implementations and subclasses. + * @stable ICU 2.8 + */ + UnicodeString unistr; + /** + * char * default buffer for use with default implementations and subclasses. + * @stable ICU 2.8 + */ + char charsBuffer[32]; + /** + * char * buffer for use with default implementations and subclasses. + * Allocated in constructor and in ensureCharsCapacity(). + * @stable ICU 2.8 + */ + char *chars; + /** + * Capacity of chars, for use with default implementations and subclasses. + * @stable ICU 2.8 + */ + int32_t charsCapacity; + + /** + * Default constructor for use with default implementations and subclasses. + * @stable ICU 2.8 + */ + StringEnumeration(); + + /** + * Ensures that chars is at least as large as the requested capacity. + * For use with default implementations and subclasses. + * + * @param capacity Requested capacity. + * @param status ICU in/out error code. + * @stable ICU 2.8 + */ + void ensureCharsCapacity(int32_t capacity, UErrorCode &status); + + /** + * Converts s to Unicode and sets unistr to the result. + * For use with default implementations and subclasses, + * especially for implementations of snext() in terms of next(). + * This is provided with a helper function instead of a default implementation + * of snext() to avoid potential infinite loops between next() and snext(). + * + * For example: + * \code + * const UnicodeString* snext(UErrorCode& status) { + * int32_t resultLength=0; + * const char *s=next(&resultLength, status); + * return setChars(s, resultLength, status); + * } + * \endcode + * + * @param s String to be converted to Unicode. + * @param length Length of the string. + * @param status ICU in/out error code. + * @return A pointer to unistr. + * @stable ICU 2.8 + */ + UnicodeString *setChars(const char *s, int32_t length, UErrorCode &status); +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +/* STRENUM_H */ +#endif diff --git a/deps/icu-small/source/common/unicode/stringoptions.h b/deps/icu-small/source/common/unicode/stringoptions.h index 7b9f70944f62db..09444284cad0be 100644 --- a/deps/icu-small/source/common/unicode/stringoptions.h +++ b/deps/icu-small/source/common/unicode/stringoptions.h @@ -1,190 +1,190 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// stringoptions.h -// created: 2017jun08 Markus W. Scherer - -#ifndef __STRINGOPTIONS_H__ -#define __STRINGOPTIONS_H__ - -#include "unicode/utypes.h" - -/** - * \file - * \brief C API: Bit set option bit constants for various string and character processing functions. - */ - -/** - * Option value for case folding: Use default mappings defined in CaseFolding.txt. - * - * @stable ICU 2.0 - */ -#define U_FOLD_CASE_DEFAULT 0 - -/** - * Option value for case folding: - * - * Use the modified set of mappings provided in CaseFolding.txt to handle dotted I - * and dotless i appropriately for Turkic languages (tr, az). - * - * Before Unicode 3.2, CaseFolding.txt contains mappings marked with 'I' that - * are to be included for default mappings and - * excluded for the Turkic-specific mappings. - * - * Unicode 3.2 CaseFolding.txt instead contains mappings marked with 'T' that - * are to be excluded for default mappings and - * included for the Turkic-specific mappings. - * - * @stable ICU 2.0 - */ -#define U_FOLD_CASE_EXCLUDE_SPECIAL_I 1 - -/** - * Titlecase the string as a whole rather than each word. - * (Titlecase only the character at index 0, possibly adjusted.) - * Option bits value for titlecasing APIs that take an options bit set. - * - * It is an error to specify multiple titlecasing iterator options together, - * including both an options bit and an explicit BreakIterator. - * - * @see U_TITLECASE_ADJUST_TO_CASED - * @stable ICU 60 - */ -#define U_TITLECASE_WHOLE_STRING 0x20 - -/** - * Titlecase sentences rather than words. - * (Titlecase only the first character of each sentence, possibly adjusted.) - * Option bits value for titlecasing APIs that take an options bit set. - * - * It is an error to specify multiple titlecasing iterator options together, - * including both an options bit and an explicit BreakIterator. - * - * @see U_TITLECASE_ADJUST_TO_CASED - * @stable ICU 60 - */ -#define U_TITLECASE_SENTENCES 0x40 - -/** - * Do not lowercase non-initial parts of words when titlecasing. - * Option bit for titlecasing APIs that take an options bit set. - * - * By default, titlecasing will titlecase the character at each - * (possibly adjusted) BreakIterator index and - * lowercase all other characters up to the next iterator index. - * With this option, the other characters will not be modified. - * - * @see U_TITLECASE_ADJUST_TO_CASED - * @see UnicodeString::toTitle - * @see CaseMap::toTitle - * @see ucasemap_setOptions - * @see ucasemap_toTitle - * @see ucasemap_utf8ToTitle - * @stable ICU 3.8 - */ -#define U_TITLECASE_NO_LOWERCASE 0x100 - -/** - * Do not adjust the titlecasing BreakIterator indexes; - * titlecase exactly the characters at breaks from the iterator. - * Option bit for titlecasing APIs that take an options bit set. - * - * By default, titlecasing will take each break iterator index, - * adjust it to the next relevant character (see U_TITLECASE_ADJUST_TO_CASED), - * and titlecase that one. - * - * Other characters are lowercased. - * - * It is an error to specify multiple titlecasing adjustment options together. - * - * @see U_TITLECASE_ADJUST_TO_CASED - * @see U_TITLECASE_NO_LOWERCASE - * @see UnicodeString::toTitle - * @see CaseMap::toTitle - * @see ucasemap_setOptions - * @see ucasemap_toTitle - * @see ucasemap_utf8ToTitle - * @stable ICU 3.8 - */ -#define U_TITLECASE_NO_BREAK_ADJUSTMENT 0x200 - -/** - * Adjust each titlecasing BreakIterator index to the next cased character. - * (See the Unicode Standard, chapter 3, Default Case Conversion, R3 toTitlecase(X).) - * Option bit for titlecasing APIs that take an options bit set. - * - * This used to be the default index adjustment in ICU. - * Since ICU 60, the default index adjustment is to the next character that is - * a letter, number, symbol, or private use code point. - * (Uncased modifier letters are skipped.) - * The difference in behavior is small for word titlecasing, - * but the new adjustment is much better for whole-string and sentence titlecasing: - * It yields "49ers" and "«丰(abc)»" instead of "49Ers" and "«丰(Abc)»". - * - * It is an error to specify multiple titlecasing adjustment options together. - * - * @see U_TITLECASE_NO_BREAK_ADJUSTMENT - * @stable ICU 60 - */ -#define U_TITLECASE_ADJUST_TO_CASED 0x400 - -/** - * Option for string transformation functions to not first reset the Edits object. - * Used for example in some case-mapping and normalization functions. - * - * @see CaseMap - * @see Edits - * @see Normalizer2 - * @stable ICU 60 - */ -#define U_EDITS_NO_RESET 0x2000 - -/** - * Omit unchanged text when recording how source substrings - * relate to changed and unchanged result substrings. - * Used for example in some case-mapping and normalization functions. - * - * @see CaseMap - * @see Edits - * @see Normalizer2 - * @stable ICU 60 - */ -#define U_OMIT_UNCHANGED_TEXT 0x4000 - -/** - * Option bit for u_strCaseCompare, u_strcasecmp, unorm_compare, etc: - * Compare strings in code point order instead of code unit order. - * @stable ICU 2.2 - */ -#define U_COMPARE_CODE_POINT_ORDER 0x8000 - -/** - * Option bit for unorm_compare: - * Perform case-insensitive comparison. - * @stable ICU 2.2 - */ -#define U_COMPARE_IGNORE_CASE 0x10000 - -/** - * Option bit for unorm_compare: - * Both input strings are assumed to fulfill FCD conditions. - * @stable ICU 2.2 - */ -#define UNORM_INPUT_IS_FCD 0x20000 - -// Related definitions elsewhere. -// Options that are not meaningful in the same functions -// can share the same bits. -// -// Public: -// unicode/unorm.h #define UNORM_COMPARE_NORM_OPTIONS_SHIFT 20 -// -// Internal: (may change or be removed) -// ucase.h #define _STRCASECMP_OPTIONS_MASK 0xffff -// ucase.h #define _FOLD_CASE_OPTIONS_MASK 7 -// ucasemap_imp.h #define U_TITLECASE_ITERATOR_MASK 0xe0 -// ucasemap_imp.h #define U_TITLECASE_ADJUSTMENT_MASK 0x600 -// ustr_imp.h #define _STRNCMP_STYLE 0x1000 -// unormcmp.cpp #define _COMPARE_EQUIV 0x80000 - -#endif // __STRINGOPTIONS_H__ +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// stringoptions.h +// created: 2017jun08 Markus W. Scherer + +#ifndef __STRINGOPTIONS_H__ +#define __STRINGOPTIONS_H__ + +#include "unicode/utypes.h" + +/** + * \file + * \brief C API: Bit set option bit constants for various string and character processing functions. + */ + +/** + * Option value for case folding: Use default mappings defined in CaseFolding.txt. + * + * @stable ICU 2.0 + */ +#define U_FOLD_CASE_DEFAULT 0 + +/** + * Option value for case folding: + * + * Use the modified set of mappings provided in CaseFolding.txt to handle dotted I + * and dotless i appropriately for Turkic languages (tr, az). + * + * Before Unicode 3.2, CaseFolding.txt contains mappings marked with 'I' that + * are to be included for default mappings and + * excluded for the Turkic-specific mappings. + * + * Unicode 3.2 CaseFolding.txt instead contains mappings marked with 'T' that + * are to be excluded for default mappings and + * included for the Turkic-specific mappings. + * + * @stable ICU 2.0 + */ +#define U_FOLD_CASE_EXCLUDE_SPECIAL_I 1 + +/** + * Titlecase the string as a whole rather than each word. + * (Titlecase only the character at index 0, possibly adjusted.) + * Option bits value for titlecasing APIs that take an options bit set. + * + * It is an error to specify multiple titlecasing iterator options together, + * including both an options bit and an explicit BreakIterator. + * + * @see U_TITLECASE_ADJUST_TO_CASED + * @stable ICU 60 + */ +#define U_TITLECASE_WHOLE_STRING 0x20 + +/** + * Titlecase sentences rather than words. + * (Titlecase only the first character of each sentence, possibly adjusted.) + * Option bits value for titlecasing APIs that take an options bit set. + * + * It is an error to specify multiple titlecasing iterator options together, + * including both an options bit and an explicit BreakIterator. + * + * @see U_TITLECASE_ADJUST_TO_CASED + * @stable ICU 60 + */ +#define U_TITLECASE_SENTENCES 0x40 + +/** + * Do not lowercase non-initial parts of words when titlecasing. + * Option bit for titlecasing APIs that take an options bit set. + * + * By default, titlecasing will titlecase the character at each + * (possibly adjusted) BreakIterator index and + * lowercase all other characters up to the next iterator index. + * With this option, the other characters will not be modified. + * + * @see U_TITLECASE_ADJUST_TO_CASED + * @see UnicodeString::toTitle + * @see CaseMap::toTitle + * @see ucasemap_setOptions + * @see ucasemap_toTitle + * @see ucasemap_utf8ToTitle + * @stable ICU 3.8 + */ +#define U_TITLECASE_NO_LOWERCASE 0x100 + +/** + * Do not adjust the titlecasing BreakIterator indexes; + * titlecase exactly the characters at breaks from the iterator. + * Option bit for titlecasing APIs that take an options bit set. + * + * By default, titlecasing will take each break iterator index, + * adjust it to the next relevant character (see U_TITLECASE_ADJUST_TO_CASED), + * and titlecase that one. + * + * Other characters are lowercased. + * + * It is an error to specify multiple titlecasing adjustment options together. + * + * @see U_TITLECASE_ADJUST_TO_CASED + * @see U_TITLECASE_NO_LOWERCASE + * @see UnicodeString::toTitle + * @see CaseMap::toTitle + * @see ucasemap_setOptions + * @see ucasemap_toTitle + * @see ucasemap_utf8ToTitle + * @stable ICU 3.8 + */ +#define U_TITLECASE_NO_BREAK_ADJUSTMENT 0x200 + +/** + * Adjust each titlecasing BreakIterator index to the next cased character. + * (See the Unicode Standard, chapter 3, Default Case Conversion, R3 toTitlecase(X).) + * Option bit for titlecasing APIs that take an options bit set. + * + * This used to be the default index adjustment in ICU. + * Since ICU 60, the default index adjustment is to the next character that is + * a letter, number, symbol, or private use code point. + * (Uncased modifier letters are skipped.) + * The difference in behavior is small for word titlecasing, + * but the new adjustment is much better for whole-string and sentence titlecasing: + * It yields "49ers" and "«丰(abc)»" instead of "49Ers" and "«丰(Abc)»". + * + * It is an error to specify multiple titlecasing adjustment options together. + * + * @see U_TITLECASE_NO_BREAK_ADJUSTMENT + * @stable ICU 60 + */ +#define U_TITLECASE_ADJUST_TO_CASED 0x400 + +/** + * Option for string transformation functions to not first reset the Edits object. + * Used for example in some case-mapping and normalization functions. + * + * @see CaseMap + * @see Edits + * @see Normalizer2 + * @stable ICU 60 + */ +#define U_EDITS_NO_RESET 0x2000 + +/** + * Omit unchanged text when recording how source substrings + * relate to changed and unchanged result substrings. + * Used for example in some case-mapping and normalization functions. + * + * @see CaseMap + * @see Edits + * @see Normalizer2 + * @stable ICU 60 + */ +#define U_OMIT_UNCHANGED_TEXT 0x4000 + +/** + * Option bit for u_strCaseCompare, u_strcasecmp, unorm_compare, etc: + * Compare strings in code point order instead of code unit order. + * @stable ICU 2.2 + */ +#define U_COMPARE_CODE_POINT_ORDER 0x8000 + +/** + * Option bit for unorm_compare: + * Perform case-insensitive comparison. + * @stable ICU 2.2 + */ +#define U_COMPARE_IGNORE_CASE 0x10000 + +/** + * Option bit for unorm_compare: + * Both input strings are assumed to fulfill FCD conditions. + * @stable ICU 2.2 + */ +#define UNORM_INPUT_IS_FCD 0x20000 + +// Related definitions elsewhere. +// Options that are not meaningful in the same functions +// can share the same bits. +// +// Public: +// unicode/unorm.h #define UNORM_COMPARE_NORM_OPTIONS_SHIFT 20 +// +// Internal: (may change or be removed) +// ucase.h #define _STRCASECMP_OPTIONS_MASK 0xffff +// ucase.h #define _FOLD_CASE_OPTIONS_MASK 7 +// ucasemap_imp.h #define U_TITLECASE_ITERATOR_MASK 0xe0 +// ucasemap_imp.h #define U_TITLECASE_ADJUSTMENT_MASK 0x600 +// ustr_imp.h #define _STRNCMP_STYLE 0x1000 +// unormcmp.cpp #define _COMPARE_EQUIV 0x80000 + +#endif // __STRINGOPTIONS_H__ diff --git a/deps/icu-small/source/common/unicode/stringpiece.h b/deps/icu-small/source/common/unicode/stringpiece.h index df7f36089dd7c5..0277da2617de86 100644 --- a/deps/icu-small/source/common/unicode/stringpiece.h +++ b/deps/icu-small/source/common/unicode/stringpiece.h @@ -1,343 +1,343 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// Copyright (C) 2009-2013, International Business Machines -// Corporation and others. All Rights Reserved. -// -// Copyright 2001 and onwards Google Inc. -// Author: Sanjay Ghemawat - -// This code is a contribution of Google code, and the style used here is -// a compromise between the original Google code and the ICU coding guidelines. -// For example, data types are ICU-ified (size_t,int->int32_t), -// and API comments doxygen-ified, but function names and behavior are -// as in the original, if possible. -// Assertion-style error handling, not available in ICU, was changed to -// parameter "pinning" similar to UnicodeString. -// -// In addition, this is only a partial port of the original Google code, -// limited to what was needed so far. The (nearly) complete original code -// is in the ICU svn repository at icuhtml/trunk/design/strings/contrib -// (see ICU ticket 6765, r25517). - -#ifndef __STRINGPIECE_H__ -#define __STRINGPIECE_H__ - -/** - * \file - * \brief C++ API: StringPiece: Read-only byte string wrapper class. - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include -#include - -#include "unicode/uobject.h" -#include "unicode/std_string.h" - -// Arghh! I wish C++ literals were "string". - -U_NAMESPACE_BEGIN - -/** - * A string-like object that points to a sized piece of memory. - * - * We provide non-explicit singleton constructors so users can pass - * in a "const char*" or a "string" wherever a "StringPiece" is - * expected. - * - * Functions or methods may use StringPiece parameters to accept either a - * "const char*" or a "string" value that will be implicitly converted to a - * StringPiece. - * - * Systematic usage of StringPiece is encouraged as it will reduce unnecessary - * conversions from "const char*" to "string" and back again. - * - * @stable ICU 4.2 - */ -class U_COMMON_API StringPiece : public UMemory { - private: - const char* ptr_; - int32_t length_; - - public: - /** - * Default constructor, creates an empty StringPiece. - * @stable ICU 4.2 - */ - StringPiece() : ptr_(nullptr), length_(0) { } - - /** - * Constructs from a NUL-terminated const char * pointer. - * @param str a NUL-terminated const char * pointer - * @stable ICU 4.2 - */ - StringPiece(const char* str); -#if defined(__cpp_char8_t) || defined(U_IN_DOXYGEN) - /** - * Constructs from a NUL-terminated const char8_t * pointer. - * @param str a NUL-terminated const char8_t * pointer - * @stable ICU 67 - */ - StringPiece(const char8_t* str) : StringPiece(reinterpret_cast(str)) {} -#endif - /** - * Constructs an empty StringPiece. - * Needed for type disambiguation from multiple other overloads. - * @param p nullptr - * @stable ICU 67 - */ - StringPiece(std::nullptr_t p) : ptr_(p), length_(0) {} - - /** - * Constructs from a std::string. - * @stable ICU 4.2 - */ - StringPiece(const std::string& str) - : ptr_(str.data()), length_(static_cast(str.size())) { } -#if defined(__cpp_lib_char8_t) || defined(U_IN_DOXYGEN) - /** - * Constructs from a std::u8string. - * @stable ICU 67 - */ - StringPiece(const std::u8string& str) - : ptr_(reinterpret_cast(str.data())), - length_(static_cast(str.size())) { } -#endif - - /** - * Constructs from some other implementation of a string piece class, from any - * C++ record type that has these two methods: - * - * \code{.cpp} - * - * struct OtherStringPieceClass { - * const char* data(); // or const char8_t* - * size_t size(); - * }; - * - * \endcode - * - * The other string piece class will typically be std::string_view from C++17 - * or absl::string_view from Abseil. - * - * Starting with C++20, data() may also return a const char8_t* pointer, - * as from std::u8string_view. - * - * @param str the other string piece - * @stable ICU 65 - */ - template ::value -#if defined(__cpp_char8_t) - || std::is_same::value -#endif - ) && - std::is_same::value>::type> - StringPiece(T str) - : ptr_(reinterpret_cast(str.data())), - length_(static_cast(str.size())) {} - - /** - * Constructs from a const char * pointer and a specified length. - * @param offset a const char * pointer (need not be terminated) - * @param len the length of the string; must be non-negative - * @stable ICU 4.2 - */ - StringPiece(const char* offset, int32_t len) : ptr_(offset), length_(len) { } -#if defined(__cpp_char8_t) || defined(U_IN_DOXYGEN) - /** - * Constructs from a const char8_t * pointer and a specified length. - * @param str a const char8_t * pointer (need not be terminated) - * @param len the length of the string; must be non-negative - * @stable ICU 67 - */ - StringPiece(const char8_t* str, int32_t len) : - StringPiece(reinterpret_cast(str), len) {} -#endif - - /** - * Substring of another StringPiece. - * @param x the other StringPiece - * @param pos start position in x; must be non-negative and <= x.length(). - * @stable ICU 4.2 - */ - StringPiece(const StringPiece& x, int32_t pos); - /** - * Substring of another StringPiece. - * @param x the other StringPiece - * @param pos start position in x; must be non-negative and <= x.length(). - * @param len length of the substring; - * must be non-negative and will be pinned to at most x.length() - pos. - * @stable ICU 4.2 - */ - StringPiece(const StringPiece& x, int32_t pos, int32_t len); - - /** - * Returns the string pointer. May be nullptr if it is empty. - * - * data() may return a pointer to a buffer with embedded NULs, and the - * returned buffer may or may not be null terminated. Therefore it is - * typically a mistake to pass data() to a routine that expects a NUL - * terminated string. - * @return the string pointer - * @stable ICU 4.2 - */ - const char* data() const { return ptr_; } - /** - * Returns the string length. Same as length(). - * @return the string length - * @stable ICU 4.2 - */ - int32_t size() const { return length_; } - /** - * Returns the string length. Same as size(). - * @return the string length - * @stable ICU 4.2 - */ - int32_t length() const { return length_; } - /** - * Returns whether the string is empty. - * @return true if the string is empty - * @stable ICU 4.2 - */ - UBool empty() const { return length_ == 0; } - - /** - * Sets to an empty string. - * @stable ICU 4.2 - */ - void clear() { ptr_ = nullptr; length_ = 0; } - - /** - * Reset the stringpiece to refer to new data. - * @param xdata pointer the new string data. Need not be nul terminated. - * @param len the length of the new data - * @stable ICU 4.8 - */ - void set(const char* xdata, int32_t len) { ptr_ = xdata; length_ = len; } - - /** - * Reset the stringpiece to refer to new data. - * @param str a pointer to a NUL-terminated string. - * @stable ICU 4.8 - */ - void set(const char* str); - -#if defined(__cpp_char8_t) || defined(U_IN_DOXYGEN) - /** - * Resets the stringpiece to refer to new data. - * @param xdata pointer the new string data. Need not be NUL-terminated. - * @param len the length of the new data - * @stable ICU 67 - */ - inline void set(const char8_t* xdata, int32_t len) { - set(reinterpret_cast(xdata), len); - } - - /** - * Resets the stringpiece to refer to new data. - * @param str a pointer to a NUL-terminated string. - * @stable ICU 67 - */ - inline void set(const char8_t* str) { - set(reinterpret_cast(str)); - } -#endif - - /** - * Removes the first n string units. - * @param n prefix length, must be non-negative and <=length() - * @stable ICU 4.2 - */ - void remove_prefix(int32_t n) { - if (n >= 0) { - if (n > length_) { - n = length_; - } - ptr_ += n; - length_ -= n; - } - } - - /** - * Removes the last n string units. - * @param n suffix length, must be non-negative and <=length() - * @stable ICU 4.2 - */ - void remove_suffix(int32_t n) { - if (n >= 0) { - if (n <= length_) { - length_ -= n; - } else { - length_ = 0; - } - } - } - - /** - * Searches the StringPiece for the given search string (needle); - * @param needle The string for which to search. - * @param offset Where to start searching within this string (haystack). - * @return The offset of needle in haystack, or -1 if not found. - * @stable ICU 67 - */ - int32_t find(StringPiece needle, int32_t offset); - - /** - * Compares this StringPiece with the other StringPiece, with semantics - * similar to std::string::compare(). - * @param other The string to compare to. - * @return below zero if this < other; above zero if this > other; 0 if this == other. - * @stable ICU 67 - */ - int32_t compare(StringPiece other); - - /** - * Maximum integer, used as a default value for substring methods. - * @stable ICU 4.2 - */ - static const int32_t npos; // = 0x7fffffff; - - /** - * Returns a substring of this StringPiece. - * @param pos start position; must be non-negative and <= length(). - * @param len length of the substring; - * must be non-negative and will be pinned to at most length() - pos. - * @return the substring StringPiece - * @stable ICU 4.2 - */ - StringPiece substr(int32_t pos, int32_t len = npos) const { - return StringPiece(*this, pos, len); - } -}; - -/** - * Global operator == for StringPiece - * @param x The first StringPiece to compare. - * @param y The second StringPiece to compare. - * @return true if the string data is equal - * @stable ICU 4.8 - */ -U_EXPORT UBool U_EXPORT2 -operator==(const StringPiece& x, const StringPiece& y); - -/** - * Global operator != for StringPiece - * @param x The first StringPiece to compare. - * @param y The second StringPiece to compare. - * @return true if the string data is not equal - * @stable ICU 4.8 - */ -inline bool operator!=(const StringPiece& x, const StringPiece& y) { - return !(x == y); -} - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __STRINGPIECE_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// Copyright (C) 2009-2013, International Business Machines +// Corporation and others. All Rights Reserved. +// +// Copyright 2001 and onwards Google Inc. +// Author: Sanjay Ghemawat + +// This code is a contribution of Google code, and the style used here is +// a compromise between the original Google code and the ICU coding guidelines. +// For example, data types are ICU-ified (size_t,int->int32_t), +// and API comments doxygen-ified, but function names and behavior are +// as in the original, if possible. +// Assertion-style error handling, not available in ICU, was changed to +// parameter "pinning" similar to UnicodeString. +// +// In addition, this is only a partial port of the original Google code, +// limited to what was needed so far. The (nearly) complete original code +// is in the ICU svn repository at icuhtml/trunk/design/strings/contrib +// (see ICU ticket 6765, r25517). + +#ifndef __STRINGPIECE_H__ +#define __STRINGPIECE_H__ + +/** + * \file + * \brief C++ API: StringPiece: Read-only byte string wrapper class. + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include +#include + +#include "unicode/uobject.h" +#include "unicode/std_string.h" + +// Arghh! I wish C++ literals were "string". + +U_NAMESPACE_BEGIN + +/** + * A string-like object that points to a sized piece of memory. + * + * We provide non-explicit singleton constructors so users can pass + * in a "const char*" or a "string" wherever a "StringPiece" is + * expected. + * + * Functions or methods may use StringPiece parameters to accept either a + * "const char*" or a "string" value that will be implicitly converted to a + * StringPiece. + * + * Systematic usage of StringPiece is encouraged as it will reduce unnecessary + * conversions from "const char*" to "string" and back again. + * + * @stable ICU 4.2 + */ +class U_COMMON_API StringPiece : public UMemory { + private: + const char* ptr_; + int32_t length_; + + public: + /** + * Default constructor, creates an empty StringPiece. + * @stable ICU 4.2 + */ + StringPiece() : ptr_(nullptr), length_(0) { } + + /** + * Constructs from a NUL-terminated const char * pointer. + * @param str a NUL-terminated const char * pointer + * @stable ICU 4.2 + */ + StringPiece(const char* str); +#if defined(__cpp_char8_t) || defined(U_IN_DOXYGEN) + /** + * Constructs from a NUL-terminated const char8_t * pointer. + * @param str a NUL-terminated const char8_t * pointer + * @stable ICU 67 + */ + StringPiece(const char8_t* str) : StringPiece(reinterpret_cast(str)) {} +#endif + /** + * Constructs an empty StringPiece. + * Needed for type disambiguation from multiple other overloads. + * @param p nullptr + * @stable ICU 67 + */ + StringPiece(std::nullptr_t p) : ptr_(p), length_(0) {} + + /** + * Constructs from a std::string. + * @stable ICU 4.2 + */ + StringPiece(const std::string& str) + : ptr_(str.data()), length_(static_cast(str.size())) { } +#if defined(__cpp_lib_char8_t) || defined(U_IN_DOXYGEN) + /** + * Constructs from a std::u8string. + * @stable ICU 67 + */ + StringPiece(const std::u8string& str) + : ptr_(reinterpret_cast(str.data())), + length_(static_cast(str.size())) { } +#endif + + /** + * Constructs from some other implementation of a string piece class, from any + * C++ record type that has these two methods: + * + * \code{.cpp} + * + * struct OtherStringPieceClass { + * const char* data(); // or const char8_t* + * size_t size(); + * }; + * + * \endcode + * + * The other string piece class will typically be std::string_view from C++17 + * or absl::string_view from Abseil. + * + * Starting with C++20, data() may also return a const char8_t* pointer, + * as from std::u8string_view. + * + * @param str the other string piece + * @stable ICU 65 + */ + template ::value +#if defined(__cpp_char8_t) + || std::is_same::value +#endif + ) && + std::is_same::value>::type> + StringPiece(T str) + : ptr_(reinterpret_cast(str.data())), + length_(static_cast(str.size())) {} + + /** + * Constructs from a const char * pointer and a specified length. + * @param offset a const char * pointer (need not be terminated) + * @param len the length of the string; must be non-negative + * @stable ICU 4.2 + */ + StringPiece(const char* offset, int32_t len) : ptr_(offset), length_(len) { } +#if defined(__cpp_char8_t) || defined(U_IN_DOXYGEN) + /** + * Constructs from a const char8_t * pointer and a specified length. + * @param str a const char8_t * pointer (need not be terminated) + * @param len the length of the string; must be non-negative + * @stable ICU 67 + */ + StringPiece(const char8_t* str, int32_t len) : + StringPiece(reinterpret_cast(str), len) {} +#endif + + /** + * Substring of another StringPiece. + * @param x the other StringPiece + * @param pos start position in x; must be non-negative and <= x.length(). + * @stable ICU 4.2 + */ + StringPiece(const StringPiece& x, int32_t pos); + /** + * Substring of another StringPiece. + * @param x the other StringPiece + * @param pos start position in x; must be non-negative and <= x.length(). + * @param len length of the substring; + * must be non-negative and will be pinned to at most x.length() - pos. + * @stable ICU 4.2 + */ + StringPiece(const StringPiece& x, int32_t pos, int32_t len); + + /** + * Returns the string pointer. May be nullptr if it is empty. + * + * data() may return a pointer to a buffer with embedded NULs, and the + * returned buffer may or may not be null terminated. Therefore it is + * typically a mistake to pass data() to a routine that expects a NUL + * terminated string. + * @return the string pointer + * @stable ICU 4.2 + */ + const char* data() const { return ptr_; } + /** + * Returns the string length. Same as length(). + * @return the string length + * @stable ICU 4.2 + */ + int32_t size() const { return length_; } + /** + * Returns the string length. Same as size(). + * @return the string length + * @stable ICU 4.2 + */ + int32_t length() const { return length_; } + /** + * Returns whether the string is empty. + * @return true if the string is empty + * @stable ICU 4.2 + */ + UBool empty() const { return length_ == 0; } + + /** + * Sets to an empty string. + * @stable ICU 4.2 + */ + void clear() { ptr_ = nullptr; length_ = 0; } + + /** + * Reset the stringpiece to refer to new data. + * @param xdata pointer the new string data. Need not be nul terminated. + * @param len the length of the new data + * @stable ICU 4.8 + */ + void set(const char* xdata, int32_t len) { ptr_ = xdata; length_ = len; } + + /** + * Reset the stringpiece to refer to new data. + * @param str a pointer to a NUL-terminated string. + * @stable ICU 4.8 + */ + void set(const char* str); + +#if defined(__cpp_char8_t) || defined(U_IN_DOXYGEN) + /** + * Resets the stringpiece to refer to new data. + * @param xdata pointer the new string data. Need not be NUL-terminated. + * @param len the length of the new data + * @stable ICU 67 + */ + inline void set(const char8_t* xdata, int32_t len) { + set(reinterpret_cast(xdata), len); + } + + /** + * Resets the stringpiece to refer to new data. + * @param str a pointer to a NUL-terminated string. + * @stable ICU 67 + */ + inline void set(const char8_t* str) { + set(reinterpret_cast(str)); + } +#endif + + /** + * Removes the first n string units. + * @param n prefix length, must be non-negative and <=length() + * @stable ICU 4.2 + */ + void remove_prefix(int32_t n) { + if (n >= 0) { + if (n > length_) { + n = length_; + } + ptr_ += n; + length_ -= n; + } + } + + /** + * Removes the last n string units. + * @param n suffix length, must be non-negative and <=length() + * @stable ICU 4.2 + */ + void remove_suffix(int32_t n) { + if (n >= 0) { + if (n <= length_) { + length_ -= n; + } else { + length_ = 0; + } + } + } + + /** + * Searches the StringPiece for the given search string (needle); + * @param needle The string for which to search. + * @param offset Where to start searching within this string (haystack). + * @return The offset of needle in haystack, or -1 if not found. + * @stable ICU 67 + */ + int32_t find(StringPiece needle, int32_t offset); + + /** + * Compares this StringPiece with the other StringPiece, with semantics + * similar to std::string::compare(). + * @param other The string to compare to. + * @return below zero if this < other; above zero if this > other; 0 if this == other. + * @stable ICU 67 + */ + int32_t compare(StringPiece other); + + /** + * Maximum integer, used as a default value for substring methods. + * @stable ICU 4.2 + */ + static const int32_t npos; // = 0x7fffffff; + + /** + * Returns a substring of this StringPiece. + * @param pos start position; must be non-negative and <= length(). + * @param len length of the substring; + * must be non-negative and will be pinned to at most length() - pos. + * @return the substring StringPiece + * @stable ICU 4.2 + */ + StringPiece substr(int32_t pos, int32_t len = npos) const { + return StringPiece(*this, pos, len); + } +}; + +/** + * Global operator == for StringPiece + * @param x The first StringPiece to compare. + * @param y The second StringPiece to compare. + * @return true if the string data is equal + * @stable ICU 4.8 + */ +U_EXPORT UBool U_EXPORT2 +operator==(const StringPiece& x, const StringPiece& y); + +/** + * Global operator != for StringPiece + * @param x The first StringPiece to compare. + * @param y The second StringPiece to compare. + * @return true if the string data is not equal + * @stable ICU 4.8 + */ +inline bool operator!=(const StringPiece& x, const StringPiece& y) { + return !(x == y); +} + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __STRINGPIECE_H__ diff --git a/deps/icu-small/source/common/unicode/stringtriebuilder.h b/deps/icu-small/source/common/unicode/stringtriebuilder.h index b7a9b23d22bf35..55a7a4633e11d4 100644 --- a/deps/icu-small/source/common/unicode/stringtriebuilder.h +++ b/deps/icu-small/source/common/unicode/stringtriebuilder.h @@ -1,426 +1,426 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2012,2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: stringtriebuilder.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010dec24 -* created by: Markus W. Scherer -*/ - -#ifndef __STRINGTRIEBUILDER_H__ -#define __STRINGTRIEBUILDER_H__ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/uobject.h" - -/** - * \file - * \brief C++ API: Builder API for trie builders - */ - -// Forward declaration. -/// \cond -struct UHashtable; -typedef struct UHashtable UHashtable; -/// \endcond - -/** - * Build options for BytesTrieBuilder and CharsTrieBuilder. - * @stable ICU 4.8 - */ -enum UStringTrieBuildOption { - /** - * Builds a trie quickly. - * @stable ICU 4.8 - */ - USTRINGTRIE_BUILD_FAST, - /** - * Builds a trie more slowly, attempting to generate - * a shorter but equivalent serialization. - * This build option also uses more memory. - * - * This option can be effective when many integer values are the same - * and string/byte sequence suffixes can be shared. - * Runtime speed is not expected to improve. - * @stable ICU 4.8 - */ - USTRINGTRIE_BUILD_SMALL -}; - -U_NAMESPACE_BEGIN - -/** - * Base class for string trie builder classes. - * - * This class is not intended for public subclassing. - * @stable ICU 4.8 - */ -class U_COMMON_API StringTrieBuilder : public UObject { -public: -#ifndef U_HIDE_INTERNAL_API - /** @internal */ - static int32_t hashNode(const void *node); - /** @internal */ - static UBool equalNodes(const void *left, const void *right); -#endif /* U_HIDE_INTERNAL_API */ - -protected: - // Do not enclose the protected default constructor with #ifndef U_HIDE_INTERNAL_API - // or else the compiler will create a public default constructor. - /** @internal */ - StringTrieBuilder(); - /** @internal */ - virtual ~StringTrieBuilder(); - -#ifndef U_HIDE_INTERNAL_API - /** @internal */ - void createCompactBuilder(int32_t sizeGuess, UErrorCode &errorCode); - /** @internal */ - void deleteCompactBuilder(); - - /** @internal */ - void build(UStringTrieBuildOption buildOption, int32_t elementsLength, UErrorCode &errorCode); - - /** @internal */ - int32_t writeNode(int32_t start, int32_t limit, int32_t unitIndex); - /** @internal */ - int32_t writeBranchSubNode(int32_t start, int32_t limit, int32_t unitIndex, int32_t length); -#endif /* U_HIDE_INTERNAL_API */ - - class Node; - -#ifndef U_HIDE_INTERNAL_API - /** @internal */ - Node *makeNode(int32_t start, int32_t limit, int32_t unitIndex, UErrorCode &errorCode); - /** @internal */ - Node *makeBranchSubNode(int32_t start, int32_t limit, int32_t unitIndex, - int32_t length, UErrorCode &errorCode); -#endif /* U_HIDE_INTERNAL_API */ - - /** @internal */ - virtual int32_t getElementStringLength(int32_t i) const = 0; - /** @internal */ - virtual char16_t getElementUnit(int32_t i, int32_t unitIndex) const = 0; - /** @internal */ - virtual int32_t getElementValue(int32_t i) const = 0; - - // Finds the first unit index after this one where - // the first and last element have different units again. - /** @internal */ - virtual int32_t getLimitOfLinearMatch(int32_t first, int32_t last, int32_t unitIndex) const = 0; - - // Number of different units at unitIndex. - /** @internal */ - virtual int32_t countElementUnits(int32_t start, int32_t limit, int32_t unitIndex) const = 0; - /** @internal */ - virtual int32_t skipElementsBySomeUnits(int32_t i, int32_t unitIndex, int32_t count) const = 0; - /** @internal */ - virtual int32_t indexOfElementWithNextUnit(int32_t i, int32_t unitIndex, char16_t unit) const = 0; - - /** @internal */ - virtual UBool matchNodesCanHaveValues() const = 0; - - /** @internal */ - virtual int32_t getMaxBranchLinearSubNodeLength() const = 0; - /** @internal */ - virtual int32_t getMinLinearMatch() const = 0; - /** @internal */ - virtual int32_t getMaxLinearMatchLength() const = 0; - -#ifndef U_HIDE_INTERNAL_API - // max(BytesTrie::kMaxBranchLinearSubNodeLength, UCharsTrie::kMaxBranchLinearSubNodeLength). - /** @internal */ - static const int32_t kMaxBranchLinearSubNodeLength=5; - - // Maximum number of nested split-branch levels for a branch on all 2^16 possible char16_t units. - // log2(2^16/kMaxBranchLinearSubNodeLength) rounded up. - /** @internal */ - static const int32_t kMaxSplitBranchLevels=14; - - /** - * Makes sure that there is only one unique node registered that is - * equivalent to newNode. - * @param newNode Input node. The builder takes ownership. - * @param errorCode ICU in/out UErrorCode. - Set to U_MEMORY_ALLOCATION_ERROR if it was success but newNode==NULL. - * @return newNode if it is the first of its kind, or - * an equivalent node if newNode is a duplicate. - * @internal - */ - Node *registerNode(Node *newNode, UErrorCode &errorCode); - /** - * Makes sure that there is only one unique FinalValueNode registered - * with this value. - * Avoids creating a node if the value is a duplicate. - * @param value A final value. - * @param errorCode ICU in/out UErrorCode. - Set to U_MEMORY_ALLOCATION_ERROR if it was success but newNode==NULL. - * @return A FinalValueNode with the given value. - * @internal - */ - Node *registerFinalValue(int32_t value, UErrorCode &errorCode); -#endif /* U_HIDE_INTERNAL_API */ - - /* - * C++ note: - * registerNode() and registerFinalValue() take ownership of their input nodes, - * and only return owned nodes. - * If they see a failure UErrorCode, they will delete the input node. - * If they get a NULL pointer, they will record a U_MEMORY_ALLOCATION_ERROR. - * If there is a failure, they return NULL. - * - * NULL Node pointers can be safely passed into other Nodes because - * they call the static Node::hashCode() which checks for a NULL pointer first. - * - * Therefore, as long as builder functions register a new node, - * they need to check for failures only before explicitly dereferencing - * a Node pointer, or before setting a new UErrorCode. - */ - - // Hash set of nodes, maps from nodes to integer 1. - /** @internal */ - UHashtable *nodes; - - // Do not conditionalize the following with #ifndef U_HIDE_INTERNAL_API, - // it is needed for layout of other objects. - /** - * @internal - * \cond - */ - class Node : public UObject { - public: - Node(int32_t initialHash) : hash(initialHash), offset(0) {} - inline int32_t hashCode() const { return hash; } - // Handles node==NULL. - static inline int32_t hashCode(const Node *node) { return node==NULL ? 0 : node->hashCode(); } - // Base class operator==() compares the actual class types. - virtual bool operator==(const Node &other) const; - inline bool operator!=(const Node &other) const { return !operator==(other); } - /** - * Traverses the Node graph and numbers branch edges, with rightmost edges first. - * This is to avoid writing a duplicate node twice. - * - * Branch nodes in this trie data structure are not symmetric. - * Most branch edges "jump" to other nodes but the rightmost branch edges - * just continue without a jump. - * Therefore, write() must write the rightmost branch edge last - * (trie units are written backwards), and must write it at that point even if - * it is a duplicate of a node previously written elsewhere. - * - * This function visits and marks right branch edges first. - * Edges are numbered with increasingly negative values because we share the - * offset field which gets positive values when nodes are written. - * A branch edge also remembers the first number for any of its edges. - * - * When a further-left branch edge has a number in the range of the rightmost - * edge's numbers, then it will be written as part of the required right edge - * and we can avoid writing it first. - * - * After root.markRightEdgesFirst(-1) the offsets of all nodes are negative - * edge numbers. - * - * @param edgeNumber The first edge number for this node and its sub-nodes. - * @return An edge number that is at least the maximum-negative - * of the input edge number and the numbers of this node and all of its sub-nodes. - */ - virtual int32_t markRightEdgesFirst(int32_t edgeNumber); - // write() must set the offset to a positive value. - virtual void write(StringTrieBuilder &builder) = 0; - // See markRightEdgesFirst. - inline void writeUnlessInsideRightEdge(int32_t firstRight, int32_t lastRight, - StringTrieBuilder &builder) { - // Note: Edge numbers are negative, lastRight<=firstRight. - // If offset>0 then this node and its sub-nodes have been written already - // and we need not write them again. - // If this node is part of the unwritten right branch edge, - // then we wait until that is written. - if(offset<0 && (offsethashCode(); } + // Base class operator==() compares the actual class types. + virtual bool operator==(const Node &other) const; + inline bool operator!=(const Node &other) const { return !operator==(other); } + /** + * Traverses the Node graph and numbers branch edges, with rightmost edges first. + * This is to avoid writing a duplicate node twice. + * + * Branch nodes in this trie data structure are not symmetric. + * Most branch edges "jump" to other nodes but the rightmost branch edges + * just continue without a jump. + * Therefore, write() must write the rightmost branch edge last + * (trie units are written backwards), and must write it at that point even if + * it is a duplicate of a node previously written elsewhere. + * + * This function visits and marks right branch edges first. + * Edges are numbered with increasingly negative values because we share the + * offset field which gets positive values when nodes are written. + * A branch edge also remembers the first number for any of its edges. + * + * When a further-left branch edge has a number in the range of the rightmost + * edge's numbers, then it will be written as part of the required right edge + * and we can avoid writing it first. + * + * After root.markRightEdgesFirst(-1) the offsets of all nodes are negative + * edge numbers. + * + * @param edgeNumber The first edge number for this node and its sub-nodes. + * @return An edge number that is at least the maximum-negative + * of the input edge number and the numbers of this node and all of its sub-nodes. + */ + virtual int32_t markRightEdgesFirst(int32_t edgeNumber); + // write() must set the offset to a positive value. + virtual void write(StringTrieBuilder &builder) = 0; + // See markRightEdgesFirst. + inline void writeUnlessInsideRightEdge(int32_t firstRight, int32_t lastRight, + StringTrieBuilder &builder) { + // Note: Edge numbers are negative, lastRight<=firstRight. + // If offset>0 then this node and its sub-nodes have been written already + // and we need not write them again. + // If this node is part of the unwritten right branch edge, + // then we wait until that is written. + if(offset<0 && (offsetA symbol table maintains two kinds of mappings. The first is - * between symbolic names and their values. For example, if the - * variable with the name "start" is set to the value "alpha" - * (perhaps, though not necessarily, through an expression such as - * "$start=alpha"), then the call lookup("start") will return the - * char[] array ['a', 'l', 'p', 'h', 'a']. - * - *

The second kind of mapping is between character values and - * UnicodeMatcher objects. This is used by RuleBasedTransliterator, - * which uses characters in the private use area to represent objects - * such as UnicodeSets. If U+E015 is mapped to the UnicodeSet [a-z], - * then lookupMatcher(0xE015) will return the UnicodeSet [a-z]. - * - *

Finally, a symbol table defines parsing behavior for symbolic - * names. All symbolic names start with the SYMBOL_REF character. - * When a parser encounters this character, it calls parseReference() - * with the position immediately following the SYMBOL_REF. The symbol - * table parses the name, if there is one, and returns it. - * - * @stable ICU 2.8 - */ -class U_COMMON_API SymbolTable /* not : public UObject because this is an interface/mixin class */ { -public: - - /** - * The character preceding a symbol reference name. - * @stable ICU 2.8 - */ - enum { SYMBOL_REF = 0x0024 /*$*/ }; - - /** - * Destructor. - * @stable ICU 2.8 - */ - virtual ~SymbolTable(); - - /** - * Lookup the characters associated with this string and return it. - * Return NULL if no such name exists. The resultant - * string may have length zero. - * @param s the symbolic name to lookup - * @return a string containing the name's value, or NULL if - * there is no mapping for s. - * @stable ICU 2.8 - */ - virtual const UnicodeString* lookup(const UnicodeString& s) const = 0; - - /** - * Lookup the UnicodeMatcher associated with the given character, and - * return it. Return NULL if not found. - * @param ch a 32-bit code point from 0 to 0x10FFFF inclusive. - * @return the UnicodeMatcher object represented by the given - * character, or NULL if there is no mapping for ch. - * @stable ICU 2.8 - */ - virtual const UnicodeFunctor* lookupMatcher(UChar32 ch) const = 0; - - /** - * Parse a symbol reference name from the given string, starting - * at the given position. If no valid symbol reference name is - * found, return the empty string and leave pos unchanged. That is, if the - * character at pos cannot start a name, or if pos is at or after - * text.length(), then return an empty string. This indicates an - * isolated SYMBOL_REF character. - * @param text the text to parse for the name - * @param pos on entry, the index of the first character to parse. - * This is the character following the SYMBOL_REF character. On - * exit, the index after the last parsed character. If the parse - * failed, pos is unchanged on exit. - * @param limit the index after the last character to be parsed. - * @return the parsed name, or an empty string if there is no - * valid symbolic name at the given position. - * @stable ICU 2.8 - */ - virtual UnicodeString parseReference(const UnicodeString& text, - ParsePosition& pos, int32_t limit) const = 0; -}; -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2000-2005, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 02/04/00 aliu Creation. +********************************************************************** +*/ +#ifndef SYMTABLE_H +#define SYMTABLE_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/uobject.h" + +/** + * \file + * \brief C++ API: An interface that defines both lookup protocol and parsing of + * symbolic names. + */ + +U_NAMESPACE_BEGIN + +class ParsePosition; +class UnicodeFunctor; +class UnicodeSet; +class UnicodeString; + +/** + * An interface that defines both lookup protocol and parsing of + * symbolic names. + * + *

A symbol table maintains two kinds of mappings. The first is + * between symbolic names and their values. For example, if the + * variable with the name "start" is set to the value "alpha" + * (perhaps, though not necessarily, through an expression such as + * "$start=alpha"), then the call lookup("start") will return the + * char[] array ['a', 'l', 'p', 'h', 'a']. + * + *

The second kind of mapping is between character values and + * UnicodeMatcher objects. This is used by RuleBasedTransliterator, + * which uses characters in the private use area to represent objects + * such as UnicodeSets. If U+E015 is mapped to the UnicodeSet [a-z], + * then lookupMatcher(0xE015) will return the UnicodeSet [a-z]. + * + *

Finally, a symbol table defines parsing behavior for symbolic + * names. All symbolic names start with the SYMBOL_REF character. + * When a parser encounters this character, it calls parseReference() + * with the position immediately following the SYMBOL_REF. The symbol + * table parses the name, if there is one, and returns it. + * + * @stable ICU 2.8 + */ +class U_COMMON_API SymbolTable /* not : public UObject because this is an interface/mixin class */ { +public: + + /** + * The character preceding a symbol reference name. + * @stable ICU 2.8 + */ + enum { SYMBOL_REF = 0x0024 /*$*/ }; + + /** + * Destructor. + * @stable ICU 2.8 + */ + virtual ~SymbolTable(); + + /** + * Lookup the characters associated with this string and return it. + * Return nullptr if no such name exists. The resultant + * string may have length zero. + * @param s the symbolic name to lookup + * @return a string containing the name's value, or nullptr if + * there is no mapping for s. + * @stable ICU 2.8 + */ + virtual const UnicodeString* lookup(const UnicodeString& s) const = 0; + + /** + * Lookup the UnicodeMatcher associated with the given character, and + * return it. Return nullptr if not found. + * @param ch a 32-bit code point from 0 to 0x10FFFF inclusive. + * @return the UnicodeMatcher object represented by the given + * character, or nullptr if there is no mapping for ch. + * @stable ICU 2.8 + */ + virtual const UnicodeFunctor* lookupMatcher(UChar32 ch) const = 0; + + /** + * Parse a symbol reference name from the given string, starting + * at the given position. If no valid symbol reference name is + * found, return the empty string and leave pos unchanged. That is, if the + * character at pos cannot start a name, or if pos is at or after + * text.length(), then return an empty string. This indicates an + * isolated SYMBOL_REF character. + * @param text the text to parse for the name + * @param pos on entry, the index of the first character to parse. + * This is the character following the SYMBOL_REF character. On + * exit, the index after the last parsed character. If the parse + * failed, pos is unchanged on exit. + * @param limit the index after the last character to be parsed. + * @return the parsed name, or an empty string if there is no + * valid symbolic name at the given position. + * @stable ICU 2.8 + */ + virtual UnicodeString parseReference(const UnicodeString& text, + ParsePosition& pos, int32_t limit) const = 0; +}; +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/ubidi.h b/deps/icu-small/source/common/unicode/ubidi.h index 536f4172bc202c..f1467a2dafad4f 100644 --- a/deps/icu-small/source/common/unicode/ubidi.h +++ b/deps/icu-small/source/common/unicode/ubidi.h @@ -1,2211 +1,2211 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2013, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: ubidi.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999jul27 -* created by: Markus W. Scherer, updated by Matitiahu Allouche -*/ - -#ifndef UBIDI_H -#define UBIDI_H - -#include "unicode/utypes.h" -#include "unicode/uchar.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -/** - *\file - * \brief C API: Bidi algorithm - * - *

Bidi algorithm for ICU

- * - * This is an implementation of the Unicode Bidirectional Algorithm. - * The algorithm is defined in the - * Unicode Standard Annex #9.

- * - * Note: Libraries that perform a bidirectional algorithm and - * reorder strings accordingly are sometimes called "Storage Layout Engines". - * ICU's Bidi and shaping (u_shapeArabic()) APIs can be used at the core of such - * "Storage Layout Engines". - * - *

General remarks about the API:

- * - * In functions with an error code parameter, - * the pErrorCode pointer must be valid - * and the value that it points to must not indicate a failure before - * the function call. Otherwise, the function returns immediately. - * After the function call, the value indicates success or failure.

- * - * The "limit" of a sequence of characters is the position just after their - * last character, i.e., one more than that position.

- * - * Some of the API functions provide access to "runs". - * Such a "run" is defined as a sequence of characters - * that are at the same embedding level - * after performing the Bidi algorithm.

- * - * @author Markus W. Scherer - * @version 1.0 - * - * - *

Sample code for the ICU Bidi API

- * - *
Rendering a paragraph with the ICU Bidi API
- * - * This is (hypothetical) sample code that illustrates - * how the ICU Bidi API could be used to render a paragraph of text. - * Rendering code depends highly on the graphics system, - * therefore this sample code must make a lot of assumptions, - * which may or may not match any existing graphics system's properties. - * - *

The basic assumptions are:

- *
    - *
  • Rendering is done from left to right on a horizontal line.
  • - *
  • A run of single-style, unidirectional text can be rendered at once.
  • - *
  • Such a run of text is passed to the graphics system with - * characters (code units) in logical order.
  • - *
  • The line-breaking algorithm is very complicated - * and Locale-dependent - - * and therefore its implementation omitted from this sample code.
  • - *
- * - *
- * \code
- *#include 
- *
- *typedef enum {
- *     styleNormal=0, styleSelected=1,
- *     styleBold=2, styleItalics=4,
- *     styleSuper=8, styleSub=16
- *} Style;
- *
- *typedef struct { int32_t limit; Style style; } StyleRun;
- *
- *int getTextWidth(const UChar *text, int32_t start, int32_t limit,
- *                  const StyleRun *styleRuns, int styleRunCount);
- *
- * // set *pLimit and *pStyleRunLimit for a line
- * // from text[start] and from styleRuns[styleRunStart]
- * // using ubidi_getLogicalRun(para, ...)
- *void getLineBreak(const UChar *text, int32_t start, int32_t *pLimit,
- *                  UBiDi *para,
- *                  const StyleRun *styleRuns, int styleRunStart, int *pStyleRunLimit,
- *                  int *pLineWidth);
- *
- * // render runs on a line sequentially, always from left to right
- *
- * // prepare rendering a new line
- * void startLine(UBiDiDirection textDirection, int lineWidth);
- *
- * // render a run of text and advance to the right by the run width
- * // the text[start..limit-1] is always in logical order
- * void renderRun(const UChar *text, int32_t start, int32_t limit,
- *               UBiDiDirection textDirection, Style style);
- *
- * // We could compute a cross-product
- * // from the style runs with the directional runs
- * // and then reorder it.
- * // Instead, here we iterate over each run type
- * // and render the intersections -
- * // with shortcuts in simple (and common) cases.
- * // renderParagraph() is the main function.
- *
- * // render a directional run with
- * // (possibly) multiple style runs intersecting with it
- * void renderDirectionalRun(const UChar *text,
- *                           int32_t start, int32_t limit,
- *                           UBiDiDirection direction,
- *                           const StyleRun *styleRuns, int styleRunCount) {
- *     int i;
- *
- *     // iterate over style runs
- *     if(direction==UBIDI_LTR) {
- *         int styleLimit;
- *
- *         for(i=0; ilimit) { styleLimit=limit; }
- *                 renderRun(text, start, styleLimit,
- *                           direction, styleRuns[i].style);
- *                 if(styleLimit==limit) { break; }
- *                 start=styleLimit;
- *             }
- *         }
- *     } else {
- *         int styleStart;
- *
- *         for(i=styleRunCount-1; i>=0; --i) {
- *             if(i>0) {
- *                 styleStart=styleRuns[i-1].limit;
- *             } else {
- *                 styleStart=0;
- *             }
- *             if(limit>=styleStart) {
- *                 if(styleStart=length
- *
- *         width=getTextWidth(text, 0, length, styleRuns, styleRunCount);
- *         if(width<=lineWidth) {
- *             // everything fits onto one line
- *
- *            // prepare rendering a new line from either left or right
- *             startLine(paraLevel, width);
- *
- *             renderLine(para, text, 0, length,
- *                        styleRuns, styleRunCount, pErrorCode);
- *         } else {
- *             UBiDi *line;
- *
- *             // we need to render several lines
- *             line=ubidi_openSized(length, 0, pErrorCode);
- *             if(line!=NULL) {
- *                 int32_t start=0, limit;
- *                 int styleRunStart=0, styleRunLimit;
- *
- *                 for(;;) {
- *                     limit=length;
- *                     styleRunLimit=styleRunCount;
- *                     getLineBreak(text, start, &limit, para,
- *                                  styleRuns, styleRunStart, &styleRunLimit,
- *                                 &width);
- *                     ubidi_setLine(para, start, limit, line, pErrorCode);
- *                     if(U_SUCCESS(*pErrorCode)) {
- *                         // prepare rendering a new line
- *                         // from either left or right
- *                         startLine(paraLevel, width);
- *
- *                         renderLine(line, text, start, limit,
- *                                    styleRuns+styleRunStart,
- *                                    styleRunLimit-styleRunStart, pErrorCode);
- *                     }
- *                     if(limit==length) { break; }
- *                     start=limit;
- *                     styleRunStart=styleRunLimit-1;
- *                     if(start>=styleRuns[styleRunStart].limit) {
- *                         ++styleRunStart;
- *                     }
- *                 }
- *
- *                 ubidi_close(line);
- *             }
- *        }
- *    }
- *
- *     ubidi_close(para);
- *}
- *\endcode
- * 
- */ - -/*DOCXX_TAG*/ -/*@{*/ - -/** - * UBiDiLevel is the type of the level values in this - * Bidi implementation. - * It holds an embedding level and indicates the visual direction - * by its bit 0 (even/odd value).

- * - * It can also hold non-level values for the - * paraLevel and embeddingLevels - * arguments of ubidi_setPara(); there: - *

    - *
  • bit 7 of an embeddingLevels[] - * value indicates whether the using application is - * specifying the level of a character to override whatever the - * Bidi implementation would resolve it to.
  • - *
  • paraLevel can be set to the - * pseudo-level values UBIDI_DEFAULT_LTR - * and UBIDI_DEFAULT_RTL.
  • - *
- * - * @see ubidi_setPara - * - *

The related constants are not real, valid level values. - * UBIDI_DEFAULT_XXX can be used to specify - * a default for the paragraph level for - * when the ubidi_setPara() function - * shall determine it but there is no - * strongly typed character in the input.

- * - * Note that the value for UBIDI_DEFAULT_LTR is even - * and the one for UBIDI_DEFAULT_RTL is odd, - * just like with normal LTR and RTL level values - - * these special values are designed that way. Also, the implementation - * assumes that UBIDI_MAX_EXPLICIT_LEVEL is odd. - * - * Note: The numeric values of the related constants will not change: - * They are tied to the use of 7-bit byte values (plus the override bit) - * and of the UBiDiLevel=uint8_t data type in this API. - * - * @see UBIDI_DEFAULT_LTR - * @see UBIDI_DEFAULT_RTL - * @see UBIDI_LEVEL_OVERRIDE - * @see UBIDI_MAX_EXPLICIT_LEVEL - * @stable ICU 2.0 - */ -typedef uint8_t UBiDiLevel; - -/** Paragraph level setting.

- * - * Constant indicating that the base direction depends on the first strong - * directional character in the text according to the Unicode Bidirectional - * Algorithm. If no strong directional character is present, - * then set the paragraph level to 0 (left-to-right).

- * - * If this value is used in conjunction with reordering modes - * UBIDI_REORDER_INVERSE_LIKE_DIRECT or - * UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL, the text to reorder - * is assumed to be visual LTR, and the text after reordering is required - * to be the corresponding logical string with appropriate contextual - * direction. The direction of the result string will be RTL if either - * the righmost or leftmost strong character of the source text is RTL - * or Arabic Letter, the direction will be LTR otherwise.

- * - * If reordering option UBIDI_OPTION_INSERT_MARKS is set, an RLM may - * be added at the beginning of the result string to ensure round trip - * (that the result string, when reordered back to visual, will produce - * the original source text). - * @see UBIDI_REORDER_INVERSE_LIKE_DIRECT - * @see UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL - * @stable ICU 2.0 - */ -#define UBIDI_DEFAULT_LTR 0xfe - -/** Paragraph level setting.

- * - * Constant indicating that the base direction depends on the first strong - * directional character in the text according to the Unicode Bidirectional - * Algorithm. If no strong directional character is present, - * then set the paragraph level to 1 (right-to-left).

- * - * If this value is used in conjunction with reordering modes - * UBIDI_REORDER_INVERSE_LIKE_DIRECT or - * UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL, the text to reorder - * is assumed to be visual LTR, and the text after reordering is required - * to be the corresponding logical string with appropriate contextual - * direction. The direction of the result string will be RTL if either - * the righmost or leftmost strong character of the source text is RTL - * or Arabic Letter, or if the text contains no strong character; - * the direction will be LTR otherwise.

- * - * If reordering option UBIDI_OPTION_INSERT_MARKS is set, an RLM may - * be added at the beginning of the result string to ensure round trip - * (that the result string, when reordered back to visual, will produce - * the original source text). - * @see UBIDI_REORDER_INVERSE_LIKE_DIRECT - * @see UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL - * @stable ICU 2.0 - */ -#define UBIDI_DEFAULT_RTL 0xff - -/** - * Maximum explicit embedding level. - * Same as the max_depth value in the - * Unicode Bidirectional Algorithm. - * (The maximum resolved level can be up to UBIDI_MAX_EXPLICIT_LEVEL+1). - * @stable ICU 2.0 - */ -#define UBIDI_MAX_EXPLICIT_LEVEL 125 - -/** Bit flag for level input. - * Overrides directional properties. - * @stable ICU 2.0 - */ -#define UBIDI_LEVEL_OVERRIDE 0x80 - -/** - * Special value which can be returned by the mapping functions when a logical - * index has no corresponding visual index or vice-versa. This may happen - * for the logical-to-visual mapping of a Bidi control when option - * #UBIDI_OPTION_REMOVE_CONTROLS is specified. This can also happen - * for the visual-to-logical mapping of a Bidi mark (LRM or RLM) inserted - * by option #UBIDI_OPTION_INSERT_MARKS. - * @see ubidi_getVisualIndex - * @see ubidi_getVisualMap - * @see ubidi_getLogicalIndex - * @see ubidi_getLogicalMap - * @stable ICU 3.6 - */ -#define UBIDI_MAP_NOWHERE (-1) - -/** - * UBiDiDirection values indicate the text direction. - * @stable ICU 2.0 - */ -enum UBiDiDirection { - /** Left-to-right text. This is a 0 value. - *

    - *
  • As return value for ubidi_getDirection(), it means - * that the source string contains no right-to-left characters, or - * that the source string is empty and the paragraph level is even. - *
  • As return value for ubidi_getBaseDirection(), it - * means that the first strong character of the source string has - * a left-to-right direction. - *
- * @stable ICU 2.0 - */ - UBIDI_LTR, - /** Right-to-left text. This is a 1 value. - *
    - *
  • As return value for ubidi_getDirection(), it means - * that the source string contains no left-to-right characters, or - * that the source string is empty and the paragraph level is odd. - *
  • As return value for ubidi_getBaseDirection(), it - * means that the first strong character of the source string has - * a right-to-left direction. - *
- * @stable ICU 2.0 - */ - UBIDI_RTL, - /** Mixed-directional text. - *

As return value for ubidi_getDirection(), it means - * that the source string contains both left-to-right and - * right-to-left characters. - * @stable ICU 2.0 - */ - UBIDI_MIXED, - /** No strongly directional text. - *

As return value for ubidi_getBaseDirection(), it means - * that the source string is missing or empty, or contains neither left-to-right - * nor right-to-left characters. - * @stable ICU 4.6 - */ - UBIDI_NEUTRAL -}; - -/** @stable ICU 2.0 */ -typedef enum UBiDiDirection UBiDiDirection; - -/** - * Forward declaration of the UBiDi structure for the declaration of - * the API functions. Its fields are implementation-specific.

- * This structure holds information about a paragraph (or multiple paragraphs) - * of text with Bidi-algorithm-related details, or about one line of - * such a paragraph.

- * Reordering can be done on a line, or on one or more paragraphs which are - * then interpreted each as one single line. - * @stable ICU 2.0 - */ -struct UBiDi; - -/** @stable ICU 2.0 */ -typedef struct UBiDi UBiDi; - -/** - * Allocate a UBiDi structure. - * Such an object is initially empty. It is assigned - * the Bidi properties of a piece of text containing one or more paragraphs - * by ubidi_setPara() - * or the Bidi properties of a line within a paragraph by - * ubidi_setLine().

- * This object can be reused for as long as it is not deallocated - * by calling ubidi_close().

- * ubidi_setPara() and ubidi_setLine() will allocate - * additional memory for internal structures as necessary. - * - * @return An empty UBiDi object. - * @stable ICU 2.0 - */ -U_CAPI UBiDi * U_EXPORT2 -ubidi_open(void); - -/** - * Allocate a UBiDi structure with preallocated memory - * for internal structures. - * This function provides a UBiDi object like ubidi_open() - * with no arguments, but it also preallocates memory for internal structures - * according to the sizings supplied by the caller.

- * Subsequent functions will not allocate any more memory, and are thus - * guaranteed not to fail because of lack of memory.

- * The preallocation can be limited to some of the internal memory - * by setting some values to 0 here. That means that if, e.g., - * maxRunCount cannot be reasonably predetermined and should not - * be set to maxLength (the only failproof value) to avoid - * wasting memory, then maxRunCount could be set to 0 here - * and the internal structures that are associated with it will be allocated - * on demand, just like with ubidi_open(). - * - * @param maxLength is the maximum text or line length that internal memory - * will be preallocated for. An attempt to associate this object with a - * longer text will fail, unless this value is 0, which leaves the allocation - * up to the implementation. - * - * @param maxRunCount is the maximum anticipated number of same-level runs - * that internal memory will be preallocated for. An attempt to access - * visual runs on an object that was not preallocated for as many runs - * as the text was actually resolved to will fail, - * unless this value is 0, which leaves the allocation up to the implementation.

- * The number of runs depends on the actual text and maybe anywhere between - * 1 and maxLength. It is typically small. - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @return An empty UBiDi object with preallocated memory. - * @stable ICU 2.0 - */ -U_CAPI UBiDi * U_EXPORT2 -ubidi_openSized(int32_t maxLength, int32_t maxRunCount, UErrorCode *pErrorCode); - -/** - * ubidi_close() must be called to free the memory - * associated with a UBiDi object.

- * - * Important: - * A parent UBiDi object must not be destroyed or reused if - * it still has children. - * If a UBiDi object has become the child - * of another one (its parent) by calling - * ubidi_setLine(), then the child object must - * be destroyed (closed) or reused (by calling - * ubidi_setPara() or ubidi_setLine()) - * before the parent object. - * - * @param pBiDi is a UBiDi object. - * - * @see ubidi_setPara - * @see ubidi_setLine - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ubidi_close(UBiDi *pBiDi); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUBiDiPointer - * "Smart pointer" class, closes a UBiDi via ubidi_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUBiDiPointer, UBiDi, ubidi_close); - -U_NAMESPACE_END - -#endif - -/** - * Modify the operation of the Bidi algorithm such that it - * approximates an "inverse Bidi" algorithm. This function - * must be called before ubidi_setPara(). - * - *

The normal operation of the Bidi algorithm as described - * in the Unicode Technical Report is to take text stored in logical - * (keyboard, typing) order and to determine the reordering of it for visual - * rendering. - * Some legacy systems store text in visual order, and for operations - * with standard, Unicode-based algorithms, the text needs to be transformed - * to logical order. This is effectively the inverse algorithm of the - * described Bidi algorithm. Note that there is no standard algorithm for - * this "inverse Bidi" and that the current implementation provides only an - * approximation of "inverse Bidi".

- * - *

With isInverse set to true, - * this function changes the behavior of some of the subsequent functions - * in a way that they can be used for the inverse Bidi algorithm. - * Specifically, runs of text with numeric characters will be treated in a - * special way and may need to be surrounded with LRM characters when they are - * written in reordered sequence.

- * - *

Output runs should be retrieved using ubidi_getVisualRun(). - * Since the actual input for "inverse Bidi" is visually ordered text and - * ubidi_getVisualRun() gets the reordered runs, these are actually - * the runs of the logically ordered output.

- * - *

Calling this function with argument isInverse set to - * true is equivalent to calling - * ubidi_setReorderingMode with argument - * reorderingMode - * set to #UBIDI_REORDER_INVERSE_NUMBERS_AS_L.
- * Calling this function with argument isInverse set to - * false is equivalent to calling - * ubidi_setReorderingMode with argument - * reorderingMode - * set to #UBIDI_REORDER_DEFAULT. - * - * @param pBiDi is a UBiDi object. - * - * @param isInverse specifies "forward" or "inverse" Bidi operation. - * - * @see ubidi_setPara - * @see ubidi_writeReordered - * @see ubidi_setReorderingMode - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ubidi_setInverse(UBiDi *pBiDi, UBool isInverse); - -/** - * Is this Bidi object set to perform the inverse Bidi algorithm? - *

Note: calling this function after setting the reordering mode with - * ubidi_setReorderingMode will return true if the - * reordering mode was set to #UBIDI_REORDER_INVERSE_NUMBERS_AS_L, - * false for all other values.

- * - * @param pBiDi is a UBiDi object. - * @return true if the Bidi object is set to perform the inverse Bidi algorithm - * by handling numbers as L. - * - * @see ubidi_setInverse - * @see ubidi_setReorderingMode - * @stable ICU 2.0 - */ - -U_CAPI UBool U_EXPORT2 -ubidi_isInverse(UBiDi *pBiDi); - -/** - * Specify whether block separators must be allocated level zero, - * so that successive paragraphs will progress from left to right. - * This function must be called before ubidi_setPara(). - * Paragraph separators (B) may appear in the text. Setting them to level zero - * means that all paragraph separators (including one possibly appearing - * in the last text position) are kept in the reordered text after the text - * that they follow in the source text. - * When this feature is not enabled, a paragraph separator at the last - * position of the text before reordering will go to the first position - * of the reordered text when the paragraph level is odd. - * - * @param pBiDi is a UBiDi object. - * - * @param orderParagraphsLTR specifies whether paragraph separators (B) must - * receive level 0, so that successive paragraphs progress from left to right. - * - * @see ubidi_setPara - * @stable ICU 3.4 - */ -U_CAPI void U_EXPORT2 -ubidi_orderParagraphsLTR(UBiDi *pBiDi, UBool orderParagraphsLTR); - -/** - * Is this Bidi object set to allocate level 0 to block separators so that - * successive paragraphs progress from left to right? - * - * @param pBiDi is a UBiDi object. - * @return true if the Bidi object is set to allocate level 0 to block - * separators. - * - * @see ubidi_orderParagraphsLTR - * @stable ICU 3.4 - */ -U_CAPI UBool U_EXPORT2 -ubidi_isOrderParagraphsLTR(UBiDi *pBiDi); - -/** - * UBiDiReorderingMode values indicate which variant of the Bidi - * algorithm to use. - * - * @see ubidi_setReorderingMode - * @stable ICU 3.6 - */ -typedef enum UBiDiReorderingMode { - /** Regular Logical to Visual Bidi algorithm according to Unicode. - * This is a 0 value. - * @stable ICU 3.6 */ - UBIDI_REORDER_DEFAULT = 0, - /** Logical to Visual algorithm which handles numbers in a way which - * mimics the behavior of Windows XP. - * @stable ICU 3.6 */ - UBIDI_REORDER_NUMBERS_SPECIAL, - /** Logical to Visual algorithm grouping numbers with adjacent R characters - * (reversible algorithm). - * @stable ICU 3.6 */ - UBIDI_REORDER_GROUP_NUMBERS_WITH_R, - /** Reorder runs only to transform a Logical LTR string to the Logical RTL - * string with the same display, or vice-versa.
- * If this mode is set together with option - * #UBIDI_OPTION_INSERT_MARKS, some Bidi controls in the source - * text may be removed and other controls may be added to produce the - * minimum combination which has the required display. - * @stable ICU 3.6 */ - UBIDI_REORDER_RUNS_ONLY, - /** Visual to Logical algorithm which handles numbers like L - * (same algorithm as selected by ubidi_setInverse(true). - * @see ubidi_setInverse - * @stable ICU 3.6 */ - UBIDI_REORDER_INVERSE_NUMBERS_AS_L, - /** Visual to Logical algorithm equivalent to the regular Logical to Visual - * algorithm. - * @stable ICU 3.6 */ - UBIDI_REORDER_INVERSE_LIKE_DIRECT, - /** Inverse Bidi (Visual to Logical) algorithm for the - * UBIDI_REORDER_NUMBERS_SPECIAL Bidi algorithm. - * @stable ICU 3.6 */ - UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL, -#ifndef U_HIDE_DEPRECATED_API - /** - * Number of values for reordering mode. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UBIDI_REORDER_COUNT -#endif // U_HIDE_DEPRECATED_API -} UBiDiReorderingMode; - -/** - * Modify the operation of the Bidi algorithm such that it implements some - * variant to the basic Bidi algorithm or approximates an "inverse Bidi" - * algorithm, depending on different values of the "reordering mode". - * This function must be called before ubidi_setPara(), and stays - * in effect until called again with a different argument. - * - *

The normal operation of the Bidi algorithm as described - * in the Unicode Standard Annex #9 is to take text stored in logical - * (keyboard, typing) order and to determine how to reorder it for visual - * rendering.

- * - *

With the reordering mode set to a value other than - * #UBIDI_REORDER_DEFAULT, this function changes the behavior of - * some of the subsequent functions in a way such that they implement an - * inverse Bidi algorithm or some other algorithm variants.

- * - *

Some legacy systems store text in visual order, and for operations - * with standard, Unicode-based algorithms, the text needs to be transformed - * into logical order. This is effectively the inverse algorithm of the - * described Bidi algorithm. Note that there is no standard algorithm for - * this "inverse Bidi", so a number of variants are implemented here.

- * - *

In other cases, it may be desirable to emulate some variant of the - * Logical to Visual algorithm (e.g. one used in MS Windows), or perform a - * Logical to Logical transformation.

- * - *
    - *
  • When the reordering mode is set to #UBIDI_REORDER_DEFAULT, - * the standard Bidi Logical to Visual algorithm is applied.
  • - * - *
  • When the reordering mode is set to - * #UBIDI_REORDER_NUMBERS_SPECIAL, - * the algorithm used to perform Bidi transformations when calling - * ubidi_setPara should approximate the algorithm used in - * Microsoft Windows XP rather than strictly conform to the Unicode Bidi - * algorithm. - *
    - * The differences between the basic algorithm and the algorithm addressed - * by this option are as follows: - *
      - *
    • Within text at an even embedding level, the sequence "123AB" - * (where AB represent R or AL letters) is transformed to "123BA" by the - * Unicode algorithm and to "BA123" by the Windows algorithm.
    • - *
    • Arabic-Indic numbers (AN) are handled by the Windows algorithm just - * like regular numbers (EN).
    • - *
  • - * - *
  • When the reordering mode is set to - * #UBIDI_REORDER_GROUP_NUMBERS_WITH_R, - * numbers located between LTR text and RTL text are associated with the RTL - * text. For instance, an LTR paragraph with content "abc 123 DEF" (where - * upper case letters represent RTL characters) will be transformed to - * "abc FED 123" (and not "abc 123 FED"), "DEF 123 abc" will be transformed - * to "123 FED abc" and "123 FED abc" will be transformed to "DEF 123 abc". - * This makes the algorithm reversible and makes it useful when round trip - * (from visual to logical and back to visual) must be achieved without - * adding LRM characters. However, this is a variation from the standard - * Unicode Bidi algorithm.
    - * The source text should not contain Bidi control characters other than LRM - * or RLM.
  • - * - *
  • When the reordering mode is set to - * #UBIDI_REORDER_RUNS_ONLY, - * a "Logical to Logical" transformation must be performed: - *
      - *
    • If the default text level of the source text (argument paraLevel - * in ubidi_setPara) is even, the source text will be handled as - * LTR logical text and will be transformed to the RTL logical text which has - * the same LTR visual display.
    • - *
    • If the default level of the source text is odd, the source text - * will be handled as RTL logical text and will be transformed to the - * LTR logical text which has the same LTR visual display.
    • - *
    - * This mode may be needed when logical text which is basically Arabic or - * Hebrew, with possible included numbers or phrases in English, has to be - * displayed as if it had an even embedding level (this can happen if the - * displaying application treats all text as if it was basically LTR). - *
    - * This mode may also be needed in the reverse case, when logical text which is - * basically English, with possible included phrases in Arabic or Hebrew, has to - * be displayed as if it had an odd embedding level. - *
    - * Both cases could be handled by adding LRE or RLE at the head of the text, - * if the display subsystem supports these formatting controls. If it does not, - * the problem may be handled by transforming the source text in this mode - * before displaying it, so that it will be displayed properly.
    - * The source text should not contain Bidi control characters other than LRM - * or RLM.
  • - * - *
  • When the reordering mode is set to - * #UBIDI_REORDER_INVERSE_NUMBERS_AS_L, an "inverse Bidi" algorithm - * is applied. - * Runs of text with numeric characters will be treated like LTR letters and - * may need to be surrounded with LRM characters when they are written in - * reordered sequence (the option #UBIDI_INSERT_LRM_FOR_NUMERIC can - * be used with function ubidi_writeReordered to this end. This - * mode is equivalent to calling ubidi_setInverse() with - * argument isInverse set to true.
  • - * - *
  • When the reordering mode is set to - * #UBIDI_REORDER_INVERSE_LIKE_DIRECT, the "direct" Logical to Visual - * Bidi algorithm is used as an approximation of an "inverse Bidi" algorithm. - * This mode is similar to mode #UBIDI_REORDER_INVERSE_NUMBERS_AS_L - * but is closer to the regular Bidi algorithm. - *
    - * For example, an LTR paragraph with the content "FED 123 456 CBA" (where - * upper case represents RTL characters) will be transformed to - * "ABC 456 123 DEF", as opposed to "DEF 123 456 ABC" - * with mode UBIDI_REORDER_INVERSE_NUMBERS_AS_L.
    - * When used in conjunction with option - * #UBIDI_OPTION_INSERT_MARKS, this mode generally - * adds Bidi marks to the output significantly more sparingly than mode - * #UBIDI_REORDER_INVERSE_NUMBERS_AS_L with option - * #UBIDI_INSERT_LRM_FOR_NUMERIC in calls to - * ubidi_writeReordered.
  • - * - *
  • When the reordering mode is set to - * #UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL, the Logical to Visual - * Bidi algorithm used in Windows XP is used as an approximation of an "inverse Bidi" algorithm. - *
    - * For example, an LTR paragraph with the content "abc FED123" (where - * upper case represents RTL characters) will be transformed to "abc 123DEF."
  • - *
- * - *

In all the reordering modes specifying an "inverse Bidi" algorithm - * (i.e. those with a name starting with UBIDI_REORDER_INVERSE), - * output runs should be retrieved using - * ubidi_getVisualRun(), and the output text with - * ubidi_writeReordered(). The caller should keep in mind that in - * "inverse Bidi" modes the input is actually visually ordered text and - * reordered output returned by ubidi_getVisualRun() or - * ubidi_writeReordered() are actually runs or character string - * of logically ordered output.
- * For all the "inverse Bidi" modes, the source text should not contain - * Bidi control characters other than LRM or RLM.

- * - *

Note that option #UBIDI_OUTPUT_REVERSE of - * ubidi_writeReordered has no useful meaning and should not be - * used in conjunction with any value of the reordering mode specifying - * "inverse Bidi" or with value UBIDI_REORDER_RUNS_ONLY. - * - * @param pBiDi is a UBiDi object. - * @param reorderingMode specifies the required variant of the Bidi algorithm. - * - * @see UBiDiReorderingMode - * @see ubidi_setInverse - * @see ubidi_setPara - * @see ubidi_writeReordered - * @stable ICU 3.6 - */ -U_CAPI void U_EXPORT2 -ubidi_setReorderingMode(UBiDi *pBiDi, UBiDiReorderingMode reorderingMode); - -/** - * What is the requested reordering mode for a given Bidi object? - * - * @param pBiDi is a UBiDi object. - * @return the current reordering mode of the Bidi object - * @see ubidi_setReorderingMode - * @stable ICU 3.6 - */ -U_CAPI UBiDiReorderingMode U_EXPORT2 -ubidi_getReorderingMode(UBiDi *pBiDi); - -/** - * UBiDiReorderingOption values indicate which options are - * specified to affect the Bidi algorithm. - * - * @see ubidi_setReorderingOptions - * @stable ICU 3.6 - */ -typedef enum UBiDiReorderingOption { - /** - * option value for ubidi_setReorderingOptions: - * disable all the options which can be set with this function - * @see ubidi_setReorderingOptions - * @stable ICU 3.6 - */ - UBIDI_OPTION_DEFAULT = 0, - - /** - * option bit for ubidi_setReorderingOptions: - * insert Bidi marks (LRM or RLM) when needed to ensure correct result of - * a reordering to a Logical order - * - *

This option must be set or reset before calling - * ubidi_setPara.

- * - *

This option is significant only with reordering modes which generate - * a result with Logical order, specifically:

- *
    - *
  • #UBIDI_REORDER_RUNS_ONLY
  • - *
  • #UBIDI_REORDER_INVERSE_NUMBERS_AS_L
  • - *
  • #UBIDI_REORDER_INVERSE_LIKE_DIRECT
  • - *
  • #UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL
  • - *
- * - *

If this option is set in conjunction with reordering mode - * #UBIDI_REORDER_INVERSE_NUMBERS_AS_L or with calling - * ubidi_setInverse(true), it implies - * option #UBIDI_INSERT_LRM_FOR_NUMERIC - * in calls to function ubidi_writeReordered().

- * - *

For other reordering modes, a minimum number of LRM or RLM characters - * will be added to the source text after reordering it so as to ensure - * round trip, i.e. when applying the inverse reordering mode on the - * resulting logical text with removal of Bidi marks - * (option #UBIDI_OPTION_REMOVE_CONTROLS set before calling - * ubidi_setPara() or option #UBIDI_REMOVE_BIDI_CONTROLS - * in ubidi_writeReordered), the result will be identical to the - * source text in the first transformation. - * - *

This option will be ignored if specified together with option - * #UBIDI_OPTION_REMOVE_CONTROLS. It inhibits option - * UBIDI_REMOVE_BIDI_CONTROLS in calls to function - * ubidi_writeReordered() and it implies option - * #UBIDI_INSERT_LRM_FOR_NUMERIC in calls to function - * ubidi_writeReordered() if the reordering mode is - * #UBIDI_REORDER_INVERSE_NUMBERS_AS_L.

- * - * @see ubidi_setReorderingMode - * @see ubidi_setReorderingOptions - * @stable ICU 3.6 - */ - UBIDI_OPTION_INSERT_MARKS = 1, - - /** - * option bit for ubidi_setReorderingOptions: - * remove Bidi control characters - * - *

This option must be set or reset before calling - * ubidi_setPara.

- * - *

This option nullifies option #UBIDI_OPTION_INSERT_MARKS. - * It inhibits option #UBIDI_INSERT_LRM_FOR_NUMERIC in calls - * to function ubidi_writeReordered() and it implies option - * #UBIDI_REMOVE_BIDI_CONTROLS in calls to that function.

- * - * @see ubidi_setReorderingMode - * @see ubidi_setReorderingOptions - * @stable ICU 3.6 - */ - UBIDI_OPTION_REMOVE_CONTROLS = 2, - - /** - * option bit for ubidi_setReorderingOptions: - * process the output as part of a stream to be continued - * - *

This option must be set or reset before calling - * ubidi_setPara.

- * - *

This option specifies that the caller is interested in processing large - * text object in parts. - * The results of the successive calls are expected to be concatenated by the - * caller. Only the call for the last part will have this option bit off.

- * - *

When this option bit is on, ubidi_setPara() may process - * less than the full source text in order to truncate the text at a meaningful - * boundary. The caller should call ubidi_getProcessedLength() - * immediately after calling ubidi_setPara() in order to - * determine how much of the source text has been processed. - * Source text beyond that length should be resubmitted in following calls to - * ubidi_setPara. The processed length may be less than - * the length of the source text if a character preceding the last character of - * the source text constitutes a reasonable boundary (like a block separator) - * for text to be continued.
- * If the last character of the source text constitutes a reasonable - * boundary, the whole text will be processed at once.
- * If nowhere in the source text there exists - * such a reasonable boundary, the processed length will be zero.
- * The caller should check for such an occurrence and do one of the following: - *

  • submit a larger amount of text with a better chance to include - * a reasonable boundary.
  • - *
  • resubmit the same text after turning off option - * UBIDI_OPTION_STREAMING.
- * In all cases, this option should be turned off before processing the last - * part of the text.

- * - *

When the UBIDI_OPTION_STREAMING option is used, - * it is recommended to call ubidi_orderParagraphsLTR() with - * argument orderParagraphsLTR set to true before - * calling ubidi_setPara so that later paragraphs may be - * concatenated to previous paragraphs on the right.

- * - * @see ubidi_setReorderingMode - * @see ubidi_setReorderingOptions - * @see ubidi_getProcessedLength - * @see ubidi_orderParagraphsLTR - * @stable ICU 3.6 - */ - UBIDI_OPTION_STREAMING = 4 -} UBiDiReorderingOption; - -/** - * Specify which of the reordering options - * should be applied during Bidi transformations. - * - * @param pBiDi is a UBiDi object. - * @param reorderingOptions is a combination of zero or more of the following - * options: - * #UBIDI_OPTION_DEFAULT, #UBIDI_OPTION_INSERT_MARKS, - * #UBIDI_OPTION_REMOVE_CONTROLS, #UBIDI_OPTION_STREAMING. - * - * @see ubidi_getReorderingOptions - * @stable ICU 3.6 - */ -U_CAPI void U_EXPORT2 -ubidi_setReorderingOptions(UBiDi *pBiDi, uint32_t reorderingOptions); - -/** - * What are the reordering options applied to a given Bidi object? - * - * @param pBiDi is a UBiDi object. - * @return the current reordering options of the Bidi object - * @see ubidi_setReorderingOptions - * @stable ICU 3.6 - */ -U_CAPI uint32_t U_EXPORT2 -ubidi_getReorderingOptions(UBiDi *pBiDi); - -/** - * Set the context before a call to ubidi_setPara().

- * - * ubidi_setPara() computes the left-right directionality for a given piece - * of text which is supplied as one of its arguments. Sometimes this piece - * of text (the "main text") should be considered in context, because text - * appearing before ("prologue") and/or after ("epilogue") the main text - * may affect the result of this computation.

- * - * This function specifies the prologue and/or the epilogue for the next - * call to ubidi_setPara(). The characters specified as prologue and - * epilogue should not be modified by the calling program until the call - * to ubidi_setPara() has returned. If successive calls to ubidi_setPara() - * all need specification of a context, ubidi_setContext() must be called - * before each call to ubidi_setPara(). In other words, a context is not - * "remembered" after the following successful call to ubidi_setPara().

- * - * If a call to ubidi_setPara() specifies UBIDI_DEFAULT_LTR or - * UBIDI_DEFAULT_RTL as paraLevel and is preceded by a call to - * ubidi_setContext() which specifies a prologue, the paragraph level will - * be computed taking in consideration the text in the prologue.

- * - * When ubidi_setPara() is called without a previous call to - * ubidi_setContext, the main text is handled as if preceded and followed - * by strong directional characters at the current paragraph level. - * Calling ubidi_setContext() with specification of a prologue will change - * this behavior by handling the main text as if preceded by the last - * strong character appearing in the prologue, if any. - * Calling ubidi_setContext() with specification of an epilogue will change - * the behavior of ubidi_setPara() by handling the main text as if followed - * by the first strong character or digit appearing in the epilogue, if any.

- * - * Note 1: if ubidi_setContext is called repeatedly without - * calling ubidi_setPara, the earlier calls have no effect, - * only the last call will be remembered for the next call to - * ubidi_setPara.

- * - * Note 2: calling ubidi_setContext(pBiDi, NULL, 0, NULL, 0, &errorCode) - * cancels any previous setting of non-empty prologue or epilogue. - * The next call to ubidi_setPara() will process no - * prologue or epilogue.

- * - * Note 3: users must be aware that even after setting the context - * before a call to ubidi_setPara() to perform e.g. a logical to visual - * transformation, the resulting string may not be identical to what it - * would have been if all the text, including prologue and epilogue, had - * been processed together.
- * Example (upper case letters represent RTL characters):
- *   prologue = "abc DE"
- *   epilogue = none
- *   main text = "FGH xyz"
- *   paraLevel = UBIDI_LTR
- *   display without prologue = "HGF xyz" - * ("HGF" is adjacent to "xyz")
- *   display with prologue = "abc HGFED xyz" - * ("HGF" is not adjacent to "xyz")
- * - * @param pBiDi is a paragraph UBiDi object. - * - * @param prologue is a pointer to the text which precedes the text that - * will be specified in a coming call to ubidi_setPara(). - * If there is no prologue to consider, then proLength - * must be zero and this pointer can be NULL. - * - * @param proLength is the length of the prologue; if proLength==-1 - * then the prologue must be zero-terminated. - * Otherwise proLength must be >= 0. If proLength==0, it means - * that there is no prologue to consider. - * - * @param epilogue is a pointer to the text which follows the text that - * will be specified in a coming call to ubidi_setPara(). - * If there is no epilogue to consider, then epiLength - * must be zero and this pointer can be NULL. - * - * @param epiLength is the length of the epilogue; if epiLength==-1 - * then the epilogue must be zero-terminated. - * Otherwise epiLength must be >= 0. If epiLength==0, it means - * that there is no epilogue to consider. - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @see ubidi_setPara - * @stable ICU 4.8 - */ -U_CAPI void U_EXPORT2 -ubidi_setContext(UBiDi *pBiDi, - const UChar *prologue, int32_t proLength, - const UChar *epilogue, int32_t epiLength, - UErrorCode *pErrorCode); - -/** - * Perform the Unicode Bidi algorithm. It is defined in the - * Unicode Standard Annex #9, - * version 13, - * also described in The Unicode Standard, Version 4.0 .

- * - * This function takes a piece of plain text containing one or more paragraphs, - * with or without externally specified embedding levels from styled - * text and computes the left-right-directionality of each character.

- * - * If the entire text is all of the same directionality, then - * the function may not perform all the steps described by the algorithm, - * i.e., some levels may not be the same as if all steps were performed. - * This is not relevant for unidirectional text.
- * For example, in pure LTR text with numbers the numbers would get - * a resolved level of 2 higher than the surrounding text according to - * the algorithm. This implementation may set all resolved levels to - * the same value in such a case.

- * - * The text can be composed of multiple paragraphs. Occurrence of a block - * separator in the text terminates a paragraph, and whatever comes next starts - * a new paragraph. The exception to this rule is when a Carriage Return (CR) - * is followed by a Line Feed (LF). Both CR and LF are block separators, but - * in that case, the pair of characters is considered as terminating the - * preceding paragraph, and a new paragraph will be started by a character - * coming after the LF. - * - * @param pBiDi A UBiDi object allocated with ubidi_open() - * which will be set to contain the reordering information, - * especially the resolved levels for all the characters in text. - * - * @param text is a pointer to the text that the Bidi algorithm will be performed on. - * This pointer is stored in the UBiDi object and can be retrieved - * with ubidi_getText().
- * Note: the text must be (at least) length long. - * - * @param length is the length of the text; if length==-1 then - * the text must be zero-terminated. - * - * @param paraLevel specifies the default level for the text; - * it is typically 0 (LTR) or 1 (RTL). - * If the function shall determine the paragraph level from the text, - * then paraLevel can be set to - * either #UBIDI_DEFAULT_LTR - * or #UBIDI_DEFAULT_RTL; if the text contains multiple - * paragraphs, the paragraph level shall be determined separately for - * each paragraph; if a paragraph does not include any strongly typed - * character, then the desired default is used (0 for LTR or 1 for RTL). - * Any other value between 0 and #UBIDI_MAX_EXPLICIT_LEVEL - * is also valid, with odd levels indicating RTL. - * - * @param embeddingLevels (in) may be used to preset the embedding and override levels, - * ignoring characters like LRE and PDF in the text. - * A level overrides the directional property of its corresponding - * (same index) character if the level has the - * #UBIDI_LEVEL_OVERRIDE bit set.

- * Aside from that bit, it must be - * paraLevel<=embeddingLevels[]<=UBIDI_MAX_EXPLICIT_LEVEL, - * except that level 0 is always allowed. - * Level 0 for a paragraph separator prevents reordering of paragraphs; - * this only works reliably if #UBIDI_LEVEL_OVERRIDE - * is also set for paragraph separators. - * Level 0 for other characters is treated as a wildcard - * and is lifted up to the resolved level of the surrounding paragraph.

- * Caution: A copy of this pointer, not of the levels, - * will be stored in the UBiDi object; - * the embeddingLevels array must not be - * deallocated before the UBiDi structure is destroyed or reused, - * and the embeddingLevels - * should not be modified to avoid unexpected results on subsequent Bidi operations. - * However, the ubidi_setPara() and - * ubidi_setLine() functions may modify some or all of the levels.

- * After the UBiDi object is reused or destroyed, the caller - * must take care of the deallocation of the embeddingLevels array.

- * Note: the embeddingLevels array must be - * at least length long. - * This pointer can be NULL if this - * value is not necessary. - * - * @param pErrorCode must be a valid pointer to an error code value. - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ubidi_setPara(UBiDi *pBiDi, const UChar *text, int32_t length, - UBiDiLevel paraLevel, UBiDiLevel *embeddingLevels, - UErrorCode *pErrorCode); - -/** - * ubidi_setLine() sets a UBiDi to - * contain the reordering information, especially the resolved levels, - * for all the characters in a line of text. This line of text is - * specified by referring to a UBiDi object representing - * this information for a piece of text containing one or more paragraphs, - * and by specifying a range of indexes in this text.

- * In the new line object, the indexes will range from 0 to limit-start-1.

- * - * This is used after calling ubidi_setPara() - * for a piece of text, and after line-breaking on that text. - * It is not necessary if each paragraph is treated as a single line.

- * - * After line-breaking, rules (L1) and (L2) for the treatment of - * trailing WS and for reordering are performed on - * a UBiDi object that represents a line.

- * - * Important: pLineBiDi shares data with - * pParaBiDi. - * You must destroy or reuse pLineBiDi before pParaBiDi. - * In other words, you must destroy or reuse the UBiDi object for a line - * before the object for its parent paragraph.

- * - * The text pointer that was stored in pParaBiDi is also copied, - * and start is added to it so that it points to the beginning of the - * line for this object. - * - * @param pParaBiDi is the parent paragraph object. It must have been set - * by a successful call to ubidi_setPara. - * - * @param start is the line's first index into the text. - * - * @param limit is just behind the line's last index into the text - * (its last index +1).
- * It must be 0<=startcontaining paragraph limit. - * If the specified line crosses a paragraph boundary, the function - * will terminate with error code U_ILLEGAL_ARGUMENT_ERROR. - * - * @param pLineBiDi is the object that will now represent a line of the text. - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @see ubidi_setPara - * @see ubidi_getProcessedLength - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ubidi_setLine(const UBiDi *pParaBiDi, - int32_t start, int32_t limit, - UBiDi *pLineBiDi, - UErrorCode *pErrorCode); - -/** - * Get the directionality of the text. - * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @return a value of UBIDI_LTR, UBIDI_RTL - * or UBIDI_MIXED - * that indicates if the entire text - * represented by this object is unidirectional, - * and which direction, or if it is mixed-directional. - * Note - The value UBIDI_NEUTRAL is never returned from this method. - * - * @see UBiDiDirection - * @stable ICU 2.0 - */ -U_CAPI UBiDiDirection U_EXPORT2 -ubidi_getDirection(const UBiDi *pBiDi); - -/** - * Gets the base direction of the text provided according - * to the Unicode Bidirectional Algorithm. The base direction - * is derived from the first character in the string with bidirectional - * character type L, R, or AL. If the first such character has type L, - * UBIDI_LTR is returned. If the first such character has - * type R or AL, UBIDI_RTL is returned. If the string does - * not contain any character of these types, then - * UBIDI_NEUTRAL is returned. - * - * This is a lightweight function for use when only the base direction - * is needed and no further bidi processing of the text is needed. - * - * @param text is a pointer to the text whose base - * direction is needed. - * Note: the text must be (at least) @c length long. - * - * @param length is the length of the text; - * if length==-1 then the text - * must be zero-terminated. - * - * @return UBIDI_LTR, UBIDI_RTL, - * UBIDI_NEUTRAL - * - * @see UBiDiDirection - * @stable ICU 4.6 - */ -U_CAPI UBiDiDirection U_EXPORT2 -ubidi_getBaseDirection(const UChar *text, int32_t length ); - -/** - * Get the pointer to the text. - * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @return The pointer to the text that the UBiDi object was created for. - * - * @see ubidi_setPara - * @see ubidi_setLine - * @stable ICU 2.0 - */ -U_CAPI const UChar * U_EXPORT2 -ubidi_getText(const UBiDi *pBiDi); - -/** - * Get the length of the text. - * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @return The length of the text that the UBiDi object was created for. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ubidi_getLength(const UBiDi *pBiDi); - -/** - * Get the paragraph level of the text. - * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @return The paragraph level. If there are multiple paragraphs, their - * level may vary if the required paraLevel is UBIDI_DEFAULT_LTR or - * UBIDI_DEFAULT_RTL. In that case, the level of the first paragraph - * is returned. - * - * @see UBiDiLevel - * @see ubidi_getParagraph - * @see ubidi_getParagraphByIndex - * @stable ICU 2.0 - */ -U_CAPI UBiDiLevel U_EXPORT2 -ubidi_getParaLevel(const UBiDi *pBiDi); - -/** - * Get the number of paragraphs. - * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @return The number of paragraphs. - * @stable ICU 3.4 - */ -U_CAPI int32_t U_EXPORT2 -ubidi_countParagraphs(UBiDi *pBiDi); - -/** - * Get a paragraph, given a position within the text. - * This function returns information about a paragraph.
- * Note: if the paragraph index is known, it is more efficient to - * retrieve the paragraph information using ubidi_getParagraphByIndex().

- * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @param charIndex is the index of a character within the text, in the - * range [0..ubidi_getProcessedLength(pBiDi)-1]. - * - * @param pParaStart will receive the index of the first character of the - * paragraph in the text. - * This pointer can be NULL if this - * value is not necessary. - * - * @param pParaLimit will receive the limit of the paragraph. - * The l-value that you point to here may be the - * same expression (variable) as the one for - * charIndex. - * This pointer can be NULL if this - * value is not necessary. - * - * @param pParaLevel will receive the level of the paragraph. - * This pointer can be NULL if this - * value is not necessary. - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @return The index of the paragraph containing the specified position. - * - * @see ubidi_getProcessedLength - * @stable ICU 3.4 - */ -U_CAPI int32_t U_EXPORT2 -ubidi_getParagraph(const UBiDi *pBiDi, int32_t charIndex, int32_t *pParaStart, - int32_t *pParaLimit, UBiDiLevel *pParaLevel, - UErrorCode *pErrorCode); - -/** - * Get a paragraph, given the index of this paragraph. - * - * This function returns information about a paragraph.

- * - * @param pBiDi is the paragraph UBiDi object. - * - * @param paraIndex is the number of the paragraph, in the - * range [0..ubidi_countParagraphs(pBiDi)-1]. - * - * @param pParaStart will receive the index of the first character of the - * paragraph in the text. - * This pointer can be NULL if this - * value is not necessary. - * - * @param pParaLimit will receive the limit of the paragraph. - * This pointer can be NULL if this - * value is not necessary. - * - * @param pParaLevel will receive the level of the paragraph. - * This pointer can be NULL if this - * value is not necessary. - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @stable ICU 3.4 - */ -U_CAPI void U_EXPORT2 -ubidi_getParagraphByIndex(const UBiDi *pBiDi, int32_t paraIndex, - int32_t *pParaStart, int32_t *pParaLimit, - UBiDiLevel *pParaLevel, UErrorCode *pErrorCode); - -/** - * Get the level for one character. - * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @param charIndex the index of a character. It must be in the range - * [0..ubidi_getProcessedLength(pBiDi)]. - * - * @return The level for the character at charIndex (0 if charIndex is not - * in the valid range). - * - * @see UBiDiLevel - * @see ubidi_getProcessedLength - * @stable ICU 2.0 - */ -U_CAPI UBiDiLevel U_EXPORT2 -ubidi_getLevelAt(const UBiDi *pBiDi, int32_t charIndex); - -/** - * Get an array of levels for each character.

- * - * Note that this function may allocate memory under some - * circumstances, unlike ubidi_getLevelAt(). - * - * @param pBiDi is the paragraph or line UBiDi object, whose - * text length must be strictly positive. - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @return The levels array for the text, - * or NULL if an error occurs. - * - * @see UBiDiLevel - * @see ubidi_getProcessedLength - * @stable ICU 2.0 - */ -U_CAPI const UBiDiLevel * U_EXPORT2 -ubidi_getLevels(UBiDi *pBiDi, UErrorCode *pErrorCode); - -/** - * Get a logical run. - * This function returns information about a run and is used - * to retrieve runs in logical order.

- * This is especially useful for line-breaking on a paragraph. - * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @param logicalPosition is a logical position within the source text. - * - * @param pLogicalLimit will receive the limit of the corresponding run. - * The l-value that you point to here may be the - * same expression (variable) as the one for - * logicalPosition. - * This pointer can be NULL if this - * value is not necessary. - * - * @param pLevel will receive the level of the corresponding run. - * This pointer can be NULL if this - * value is not necessary. - * - * @see ubidi_getProcessedLength - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ubidi_getLogicalRun(const UBiDi *pBiDi, int32_t logicalPosition, - int32_t *pLogicalLimit, UBiDiLevel *pLevel); - -/** - * Get the number of runs. - * This function may invoke the actual reordering on the - * UBiDi object, after ubidi_setPara() - * may have resolved only the levels of the text. Therefore, - * ubidi_countRuns() may have to allocate memory, - * and may fail doing so. - * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @return The number of runs. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ubidi_countRuns(UBiDi *pBiDi, UErrorCode *pErrorCode); - -/** - * Get one run's logical start, length, and directionality, - * which can be 0 for LTR or 1 for RTL. - * In an RTL run, the character at the logical start is - * visually on the right of the displayed run. - * The length is the number of characters in the run.

- * ubidi_countRuns() should be called - * before the runs are retrieved. - * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @param runIndex is the number of the run in visual order, in the - * range [0..ubidi_countRuns(pBiDi)-1]. - * - * @param pLogicalStart is the first logical character index in the text. - * The pointer may be NULL if this index is not needed. - * - * @param pLength is the number of characters (at least one) in the run. - * The pointer may be NULL if this is not needed. - * - * @return the directionality of the run, - * UBIDI_LTR==0 or UBIDI_RTL==1, - * never UBIDI_MIXED, - * never UBIDI_NEUTRAL. - * - * @see ubidi_countRuns - * - * Example: - *

- * \code
- * int32_t i, count=ubidi_countRuns(pBiDi),
- *         logicalStart, visualIndex=0, length;
- * for(i=0; i0);
- *     } else {
- *         logicalStart+=length;  // logicalLimit
- *         do { // RTL
- *             show_char(text[--logicalStart], visualIndex++);
- *         } while(--length>0);
- *     }
- * }
- *\endcode
- * 
- * - * Note that in right-to-left runs, code like this places - * second surrogates before first ones (which is generally a bad idea) - * and combining characters before base characters. - *

- * Use of ubidi_writeReordered(), optionally with the - * #UBIDI_KEEP_BASE_COMBINING option, can be considered in order - * to avoid these issues. - * @stable ICU 2.0 - */ -U_CAPI UBiDiDirection U_EXPORT2 -ubidi_getVisualRun(UBiDi *pBiDi, int32_t runIndex, - int32_t *pLogicalStart, int32_t *pLength); - -/** - * Get the visual position from a logical text position. - * If such a mapping is used many times on the same - * UBiDi object, then calling - * ubidi_getLogicalMap() is more efficient.

- * - * The value returned may be #UBIDI_MAP_NOWHERE if there is no - * visual position because the corresponding text character is a Bidi control - * removed from output by the option #UBIDI_OPTION_REMOVE_CONTROLS. - *

- * When the visual output is altered by using options of - * ubidi_writeReordered() such as UBIDI_INSERT_LRM_FOR_NUMERIC, - * UBIDI_KEEP_BASE_COMBINING, UBIDI_OUTPUT_REVERSE, - * UBIDI_REMOVE_BIDI_CONTROLS, the visual position returned may not - * be correct. It is advised to use, when possible, reordering options - * such as UBIDI_OPTION_INSERT_MARKS and UBIDI_OPTION_REMOVE_CONTROLS. - *

- * Note that in right-to-left runs, this mapping places - * second surrogates before first ones (which is generally a bad idea) - * and combining characters before base characters. - * Use of ubidi_writeReordered(), optionally with the - * #UBIDI_KEEP_BASE_COMBINING option can be considered instead - * of using the mapping, in order to avoid these issues. - * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @param logicalIndex is the index of a character in the text. - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @return The visual position of this character. - * - * @see ubidi_getLogicalMap - * @see ubidi_getLogicalIndex - * @see ubidi_getProcessedLength - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ubidi_getVisualIndex(UBiDi *pBiDi, int32_t logicalIndex, UErrorCode *pErrorCode); - -/** - * Get the logical text position from a visual position. - * If such a mapping is used many times on the same - * UBiDi object, then calling - * ubidi_getVisualMap() is more efficient.

- * - * The value returned may be #UBIDI_MAP_NOWHERE if there is no - * logical position because the corresponding text character is a Bidi mark - * inserted in the output by option #UBIDI_OPTION_INSERT_MARKS. - *

- * This is the inverse function to ubidi_getVisualIndex(). - *

- * When the visual output is altered by using options of - * ubidi_writeReordered() such as UBIDI_INSERT_LRM_FOR_NUMERIC, - * UBIDI_KEEP_BASE_COMBINING, UBIDI_OUTPUT_REVERSE, - * UBIDI_REMOVE_BIDI_CONTROLS, the logical position returned may not - * be correct. It is advised to use, when possible, reordering options - * such as UBIDI_OPTION_INSERT_MARKS and UBIDI_OPTION_REMOVE_CONTROLS. - * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @param visualIndex is the visual position of a character. - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @return The index of this character in the text. - * - * @see ubidi_getVisualMap - * @see ubidi_getVisualIndex - * @see ubidi_getResultLength - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ubidi_getLogicalIndex(UBiDi *pBiDi, int32_t visualIndex, UErrorCode *pErrorCode); - -/** - * Get a logical-to-visual index map (array) for the characters in the UBiDi - * (paragraph or line) object. - *

- * Some values in the map may be #UBIDI_MAP_NOWHERE if the - * corresponding text characters are Bidi controls removed from the visual - * output by the option #UBIDI_OPTION_REMOVE_CONTROLS. - *

- * When the visual output is altered by using options of - * ubidi_writeReordered() such as UBIDI_INSERT_LRM_FOR_NUMERIC, - * UBIDI_KEEP_BASE_COMBINING, UBIDI_OUTPUT_REVERSE, - * UBIDI_REMOVE_BIDI_CONTROLS, the visual positions returned may not - * be correct. It is advised to use, when possible, reordering options - * such as UBIDI_OPTION_INSERT_MARKS and UBIDI_OPTION_REMOVE_CONTROLS. - *

- * Note that in right-to-left runs, this mapping places - * second surrogates before first ones (which is generally a bad idea) - * and combining characters before base characters. - * Use of ubidi_writeReordered(), optionally with the - * #UBIDI_KEEP_BASE_COMBINING option can be considered instead - * of using the mapping, in order to avoid these issues. - * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @param indexMap is a pointer to an array of ubidi_getProcessedLength() - * indexes which will reflect the reordering of the characters. - * If option #UBIDI_OPTION_INSERT_MARKS is set, the number - * of elements allocated in indexMap must be no less than - * ubidi_getResultLength(). - * The array does not need to be initialized.

- * The index map will result in indexMap[logicalIndex]==visualIndex. - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @see ubidi_getVisualMap - * @see ubidi_getVisualIndex - * @see ubidi_getProcessedLength - * @see ubidi_getResultLength - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ubidi_getLogicalMap(UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode); - -/** - * Get a visual-to-logical index map (array) for the characters in the UBiDi - * (paragraph or line) object. - *

- * Some values in the map may be #UBIDI_MAP_NOWHERE if the - * corresponding text characters are Bidi marks inserted in the visual output - * by the option #UBIDI_OPTION_INSERT_MARKS. - *

- * When the visual output is altered by using options of - * ubidi_writeReordered() such as UBIDI_INSERT_LRM_FOR_NUMERIC, - * UBIDI_KEEP_BASE_COMBINING, UBIDI_OUTPUT_REVERSE, - * UBIDI_REMOVE_BIDI_CONTROLS, the logical positions returned may not - * be correct. It is advised to use, when possible, reordering options - * such as UBIDI_OPTION_INSERT_MARKS and UBIDI_OPTION_REMOVE_CONTROLS. - * - * @param pBiDi is the paragraph or line UBiDi object. - * - * @param indexMap is a pointer to an array of ubidi_getResultLength() - * indexes which will reflect the reordering of the characters. - * If option #UBIDI_OPTION_REMOVE_CONTROLS is set, the number - * of elements allocated in indexMap must be no less than - * ubidi_getProcessedLength(). - * The array does not need to be initialized.

- * The index map will result in indexMap[visualIndex]==logicalIndex. - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @see ubidi_getLogicalMap - * @see ubidi_getLogicalIndex - * @see ubidi_getProcessedLength - * @see ubidi_getResultLength - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ubidi_getVisualMap(UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode); - -/** - * This is a convenience function that does not use a UBiDi object. - * It is intended to be used for when an application has determined the levels - * of objects (character sequences) and just needs to have them reordered (L2). - * This is equivalent to using ubidi_getLogicalMap() on a - * UBiDi object. - * - * @param levels is an array with length levels that have been determined by - * the application. - * - * @param length is the number of levels in the array, or, semantically, - * the number of objects to be reordered. - * It must be length>0. - * - * @param indexMap is a pointer to an array of length - * indexes which will reflect the reordering of the characters. - * The array does not need to be initialized.

- * The index map will result in indexMap[logicalIndex]==visualIndex. - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ubidi_reorderLogical(const UBiDiLevel *levels, int32_t length, int32_t *indexMap); - -/** - * This is a convenience function that does not use a UBiDi object. - * It is intended to be used for when an application has determined the levels - * of objects (character sequences) and just needs to have them reordered (L2). - * This is equivalent to using ubidi_getVisualMap() on a - * UBiDi object. - * - * @param levels is an array with length levels that have been determined by - * the application. - * - * @param length is the number of levels in the array, or, semantically, - * the number of objects to be reordered. - * It must be length>0. - * - * @param indexMap is a pointer to an array of length - * indexes which will reflect the reordering of the characters. - * The array does not need to be initialized.

- * The index map will result in indexMap[visualIndex]==logicalIndex. - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ubidi_reorderVisual(const UBiDiLevel *levels, int32_t length, int32_t *indexMap); - -/** - * Invert an index map. - * The index mapping of the first map is inverted and written to - * the second one. - * - * @param srcMap is an array with length elements - * which defines the original mapping from a source array containing - * length elements to a destination array. - * Some elements of the source array may have no mapping in the - * destination array. In that case, their value will be - * the special value UBIDI_MAP_NOWHERE. - * All elements must be >=0 or equal to UBIDI_MAP_NOWHERE. - * Some elements may have a value >= length, if the - * destination array has more elements than the source array. - * There must be no duplicate indexes (two or more elements with the - * same value except UBIDI_MAP_NOWHERE). - * - * @param destMap is an array with a number of elements equal to 1 + the highest - * value in srcMap. - * destMap will be filled with the inverse mapping. - * If element with index i in srcMap has a value k different - * from UBIDI_MAP_NOWHERE, this means that element i of - * the source array maps to element k in the destination array. - * The inverse map will have value i in its k-th element. - * For all elements of the destination array which do not map to - * an element in the source array, the corresponding element in the - * inverse map will have a value equal to UBIDI_MAP_NOWHERE. - * - * @param length is the length of each array. - * @see UBIDI_MAP_NOWHERE - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ubidi_invertMap(const int32_t *srcMap, int32_t *destMap, int32_t length); - -/** option flags for ubidi_writeReordered() */ - -/** - * option bit for ubidi_writeReordered(): - * keep combining characters after their base characters in RTL runs - * - * @see ubidi_writeReordered - * @stable ICU 2.0 - */ -#define UBIDI_KEEP_BASE_COMBINING 1 - -/** - * option bit for ubidi_writeReordered(): - * replace characters with the "mirrored" property in RTL runs - * by their mirror-image mappings - * - * @see ubidi_writeReordered - * @stable ICU 2.0 - */ -#define UBIDI_DO_MIRRORING 2 - -/** - * option bit for ubidi_writeReordered(): - * surround the run with LRMs if necessary; - * this is part of the approximate "inverse Bidi" algorithm - * - *

This option does not imply corresponding adjustment of the index - * mappings.

- * - * @see ubidi_setInverse - * @see ubidi_writeReordered - * @stable ICU 2.0 - */ -#define UBIDI_INSERT_LRM_FOR_NUMERIC 4 - -/** - * option bit for ubidi_writeReordered(): - * remove Bidi control characters - * (this does not affect #UBIDI_INSERT_LRM_FOR_NUMERIC) - * - *

This option does not imply corresponding adjustment of the index - * mappings.

- * - * @see ubidi_writeReordered - * @stable ICU 2.0 - */ -#define UBIDI_REMOVE_BIDI_CONTROLS 8 - -/** - * option bit for ubidi_writeReordered(): - * write the output in reverse order - * - *

This has the same effect as calling ubidi_writeReordered() - * first without this option, and then calling - * ubidi_writeReverse() without mirroring. - * Doing this in the same step is faster and avoids a temporary buffer. - * An example for using this option is output to a character terminal that - * is designed for RTL scripts and stores text in reverse order.

- * - * @see ubidi_writeReordered - * @stable ICU 2.0 - */ -#define UBIDI_OUTPUT_REVERSE 16 - -/** - * Get the length of the source text processed by the last call to - * ubidi_setPara(). This length may be different from the length - * of the source text if option #UBIDI_OPTION_STREAMING - * has been set. - *
- * Note that whenever the length of the text affects the execution or the - * result of a function, it is the processed length which must be considered, - * except for ubidi_setPara (which receives unprocessed source - * text) and ubidi_getLength (which returns the original length - * of the source text).
- * In particular, the processed length is the one to consider in the following - * cases: - *
    - *
  • maximum value of the limit argument of - * ubidi_setLine
  • - *
  • maximum value of the charIndex argument of - * ubidi_getParagraph
  • - *
  • maximum value of the charIndex argument of - * ubidi_getLevelAt
  • - *
  • number of elements in the array returned by ubidi_getLevels
  • - *
  • maximum value of the logicalStart argument of - * ubidi_getLogicalRun
  • - *
  • maximum value of the logicalIndex argument of - * ubidi_getVisualIndex
  • - *
  • number of elements filled in the *indexMap argument of - * ubidi_getLogicalMap
  • - *
  • length of text processed by ubidi_writeReordered
  • - *
- * - * @param pBiDi is the paragraph UBiDi object. - * - * @return The length of the part of the source text processed by - * the last call to ubidi_setPara. - * @see ubidi_setPara - * @see UBIDI_OPTION_STREAMING - * @stable ICU 3.6 - */ -U_CAPI int32_t U_EXPORT2 -ubidi_getProcessedLength(const UBiDi *pBiDi); - -/** - * Get the length of the reordered text resulting from the last call to - * ubidi_setPara(). This length may be different from the length - * of the source text if option #UBIDI_OPTION_INSERT_MARKS - * or option #UBIDI_OPTION_REMOVE_CONTROLS has been set. - *
- * This resulting length is the one to consider in the following cases: - *
    - *
  • maximum value of the visualIndex argument of - * ubidi_getLogicalIndex
  • - *
  • number of elements of the *indexMap argument of - * ubidi_getVisualMap
  • - *
- * Note that this length stays identical to the source text length if - * Bidi marks are inserted or removed using option bits of - * ubidi_writeReordered, or if option - * #UBIDI_REORDER_INVERSE_NUMBERS_AS_L has been set. - * - * @param pBiDi is the paragraph UBiDi object. - * - * @return The length of the reordered text resulting from - * the last call to ubidi_setPara. - * @see ubidi_setPara - * @see UBIDI_OPTION_INSERT_MARKS - * @see UBIDI_OPTION_REMOVE_CONTROLS - * @stable ICU 3.6 - */ -U_CAPI int32_t U_EXPORT2 -ubidi_getResultLength(const UBiDi *pBiDi); - -U_CDECL_BEGIN - -#ifndef U_HIDE_DEPRECATED_API -/** - * Value returned by UBiDiClassCallback callbacks when - * there is no need to override the standard Bidi class for a given code point. - * - * This constant is deprecated; use u_getIntPropertyMaxValue(UCHAR_BIDI_CLASS)+1 instead. - * - * @see UBiDiClassCallback - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ -#define U_BIDI_CLASS_DEFAULT U_CHAR_DIRECTION_COUNT -#endif // U_HIDE_DEPRECATED_API - -/** - * Callback type declaration for overriding default Bidi class values with - * custom ones. - *

Usually, the function pointer will be propagated to a UBiDi - * object by calling the ubidi_setClassCallback() function; - * then the callback will be invoked by the UBA implementation any time the - * class of a character is to be determined.

- * - * @param context is a pointer to the callback private data. - * - * @param c is the code point to get a Bidi class for. - * - * @return The directional property / Bidi class for the given code point - * c if the default class has been overridden, or - * u_getIntPropertyMaxValue(UCHAR_BIDI_CLASS)+1 - * if the standard Bidi class value for c is to be used. - * @see ubidi_setClassCallback - * @see ubidi_getClassCallback - * @stable ICU 3.6 - */ -typedef UCharDirection U_CALLCONV -UBiDiClassCallback(const void *context, UChar32 c); - -U_CDECL_END - -/** - * Retrieve the Bidi class for a given code point. - *

If a #UBiDiClassCallback callback is defined and returns a - * value other than u_getIntPropertyMaxValue(UCHAR_BIDI_CLASS)+1, - * that value is used; otherwise the default class determination mechanism is invoked.

- * - * @param pBiDi is the paragraph UBiDi object. - * - * @param c is the code point whose Bidi class must be retrieved. - * - * @return The Bidi class for character c based - * on the given pBiDi instance. - * @see UBiDiClassCallback - * @stable ICU 3.6 - */ -U_CAPI UCharDirection U_EXPORT2 -ubidi_getCustomizedClass(UBiDi *pBiDi, UChar32 c); - -/** - * Set the callback function and callback data used by the UBA - * implementation for Bidi class determination. - *

This may be useful for assigning Bidi classes to PUA characters, or - * for special application needs. For instance, an application may want to - * handle all spaces like L or R characters (according to the base direction) - * when creating the visual ordering of logical lines which are part of a report - * organized in columns: there should not be interaction between adjacent - * cells.

- * - * @param pBiDi is the paragraph UBiDi object. - * - * @param newFn is the new callback function pointer. - * - * @param newContext is the new callback context pointer. This can be NULL. - * - * @param oldFn fillin: Returns the old callback function pointer. This can be - * NULL. - * - * @param oldContext fillin: Returns the old callback's context. This can be - * NULL. - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @see ubidi_getClassCallback - * @stable ICU 3.6 - */ -U_CAPI void U_EXPORT2 -ubidi_setClassCallback(UBiDi *pBiDi, UBiDiClassCallback *newFn, - const void *newContext, UBiDiClassCallback **oldFn, - const void **oldContext, UErrorCode *pErrorCode); - -/** - * Get the current callback function used for Bidi class determination. - * - * @param pBiDi is the paragraph UBiDi object. - * - * @param fn fillin: Returns the callback function pointer. - * - * @param context fillin: Returns the callback's private context. - * - * @see ubidi_setClassCallback - * @stable ICU 3.6 - */ -U_CAPI void U_EXPORT2 -ubidi_getClassCallback(UBiDi *pBiDi, UBiDiClassCallback **fn, const void **context); - -/** - * Take a UBiDi object containing the reordering - * information for a piece of text (one or more paragraphs) set by - * ubidi_setPara() or for a line of text set by - * ubidi_setLine() and write a reordered string to the - * destination buffer. - * - * This function preserves the integrity of characters with multiple - * code units and (optionally) combining characters. - * Characters in RTL runs can be replaced by mirror-image characters - * in the destination buffer. Note that "real" mirroring has - * to be done in a rendering engine by glyph selection - * and that for many "mirrored" characters there are no - * Unicode characters as mirror-image equivalents. - * There are also options to insert or remove Bidi control - * characters; see the description of the destSize - * and options parameters and of the option bit flags. - * - * @param pBiDi A pointer to a UBiDi object that - * is set by ubidi_setPara() or - * ubidi_setLine() and contains the reordering - * information for the text that it was defined for, - * as well as a pointer to that text.

- * The text was aliased (only the pointer was stored - * without copying the contents) and must not have been modified - * since the ubidi_setPara() call. - * - * @param dest A pointer to where the reordered text is to be copied. - * The source text and dest[destSize] - * must not overlap. - * - * @param destSize The size of the dest buffer, - * in number of UChars. - * If the UBIDI_INSERT_LRM_FOR_NUMERIC - * option is set, then the destination length could be - * as large as - * ubidi_getLength(pBiDi)+2*ubidi_countRuns(pBiDi). - * If the UBIDI_REMOVE_BIDI_CONTROLS option - * is set, then the destination length may be less than - * ubidi_getLength(pBiDi). - * If none of these options is set, then the destination length - * will be exactly ubidi_getProcessedLength(pBiDi). - * - * @param options A bit set of options for the reordering that control - * how the reordered text is written. - * The options include mirroring the characters on a code - * point basis and inserting LRM characters, which is used - * especially for transforming visually stored text - * to logically stored text (although this is still an - * imperfect implementation of an "inverse Bidi" algorithm - * because it uses the "forward Bidi" algorithm at its core). - * The available options are: - * #UBIDI_DO_MIRRORING, - * #UBIDI_INSERT_LRM_FOR_NUMERIC, - * #UBIDI_KEEP_BASE_COMBINING, - * #UBIDI_OUTPUT_REVERSE, - * #UBIDI_REMOVE_BIDI_CONTROLS - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @return The length of the output string. - * - * @see ubidi_getProcessedLength - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ubidi_writeReordered(UBiDi *pBiDi, - UChar *dest, int32_t destSize, - uint16_t options, - UErrorCode *pErrorCode); - -/** - * Reverse a Right-To-Left run of Unicode text. - * - * This function preserves the integrity of characters with multiple - * code units and (optionally) combining characters. - * Characters can be replaced by mirror-image characters - * in the destination buffer. Note that "real" mirroring has - * to be done in a rendering engine by glyph selection - * and that for many "mirrored" characters there are no - * Unicode characters as mirror-image equivalents. - * There are also options to insert or remove Bidi control - * characters. - * - * This function is the implementation for reversing RTL runs as part - * of ubidi_writeReordered(). For detailed descriptions - * of the parameters, see there. - * Since no Bidi controls are inserted here, the output string length - * will never exceed srcLength. - * - * @see ubidi_writeReordered - * - * @param src A pointer to the RTL run text. - * - * @param srcLength The length of the RTL run. - * - * @param dest A pointer to where the reordered text is to be copied. - * src[srcLength] and dest[destSize] - * must not overlap. - * - * @param destSize The size of the dest buffer, - * in number of UChars. - * If the UBIDI_REMOVE_BIDI_CONTROLS option - * is set, then the destination length may be less than - * srcLength. - * If this option is not set, then the destination length - * will be exactly srcLength. - * - * @param options A bit set of options for the reordering that control - * how the reordered text is written. - * See the options parameter in ubidi_writeReordered(). - * - * @param pErrorCode must be a valid pointer to an error code value. - * - * @return The length of the output string. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ubidi_writeReverse(const UChar *src, int32_t srcLength, - UChar *dest, int32_t destSize, - uint16_t options, - UErrorCode *pErrorCode); - -/*#define BIDI_SAMPLE_CODE*/ -/*@}*/ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2013, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ubidi.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999jul27 +* created by: Markus W. Scherer, updated by Matitiahu Allouche +*/ + +#ifndef UBIDI_H +#define UBIDI_H + +#include "unicode/utypes.h" +#include "unicode/uchar.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +/** + *\file + * \brief C API: Bidi algorithm + * + *

Bidi algorithm for ICU

+ * + * This is an implementation of the Unicode Bidirectional Algorithm. + * The algorithm is defined in the + * Unicode Standard Annex #9.

+ * + * Note: Libraries that perform a bidirectional algorithm and + * reorder strings accordingly are sometimes called "Storage Layout Engines". + * ICU's Bidi and shaping (u_shapeArabic()) APIs can be used at the core of such + * "Storage Layout Engines". + * + *

General remarks about the API:

+ * + * In functions with an error code parameter, + * the pErrorCode pointer must be valid + * and the value that it points to must not indicate a failure before + * the function call. Otherwise, the function returns immediately. + * After the function call, the value indicates success or failure.

+ * + * The "limit" of a sequence of characters is the position just after their + * last character, i.e., one more than that position.

+ * + * Some of the API functions provide access to "runs". + * Such a "run" is defined as a sequence of characters + * that are at the same embedding level + * after performing the Bidi algorithm.

+ * + * @author Markus W. Scherer + * @version 1.0 + * + * + *

Sample code for the ICU Bidi API

+ * + *
Rendering a paragraph with the ICU Bidi API
+ * + * This is (hypothetical) sample code that illustrates + * how the ICU Bidi API could be used to render a paragraph of text. + * Rendering code depends highly on the graphics system, + * therefore this sample code must make a lot of assumptions, + * which may or may not match any existing graphics system's properties. + * + *

The basic assumptions are:

+ *
    + *
  • Rendering is done from left to right on a horizontal line.
  • + *
  • A run of single-style, unidirectional text can be rendered at once.
  • + *
  • Such a run of text is passed to the graphics system with + * characters (code units) in logical order.
  • + *
  • The line-breaking algorithm is very complicated + * and Locale-dependent - + * and therefore its implementation omitted from this sample code.
  • + *
+ * + *
+ * \code
+ *#include 
+ *
+ *typedef enum {
+ *     styleNormal=0, styleSelected=1,
+ *     styleBold=2, styleItalics=4,
+ *     styleSuper=8, styleSub=16
+ *} Style;
+ *
+ *typedef struct { int32_t limit; Style style; } StyleRun;
+ *
+ *int getTextWidth(const UChar *text, int32_t start, int32_t limit,
+ *                  const StyleRun *styleRuns, int styleRunCount);
+ *
+ * // set *pLimit and *pStyleRunLimit for a line
+ * // from text[start] and from styleRuns[styleRunStart]
+ * // using ubidi_getLogicalRun(para, ...)
+ *void getLineBreak(const UChar *text, int32_t start, int32_t *pLimit,
+ *                  UBiDi *para,
+ *                  const StyleRun *styleRuns, int styleRunStart, int *pStyleRunLimit,
+ *                  int *pLineWidth);
+ *
+ * // render runs on a line sequentially, always from left to right
+ *
+ * // prepare rendering a new line
+ * void startLine(UBiDiDirection textDirection, int lineWidth);
+ *
+ * // render a run of text and advance to the right by the run width
+ * // the text[start..limit-1] is always in logical order
+ * void renderRun(const UChar *text, int32_t start, int32_t limit,
+ *               UBiDiDirection textDirection, Style style);
+ *
+ * // We could compute a cross-product
+ * // from the style runs with the directional runs
+ * // and then reorder it.
+ * // Instead, here we iterate over each run type
+ * // and render the intersections -
+ * // with shortcuts in simple (and common) cases.
+ * // renderParagraph() is the main function.
+ *
+ * // render a directional run with
+ * // (possibly) multiple style runs intersecting with it
+ * void renderDirectionalRun(const UChar *text,
+ *                           int32_t start, int32_t limit,
+ *                           UBiDiDirection direction,
+ *                           const StyleRun *styleRuns, int styleRunCount) {
+ *     int i;
+ *
+ *     // iterate over style runs
+ *     if(direction==UBIDI_LTR) {
+ *         int styleLimit;
+ *
+ *         for(i=0; ilimit) { styleLimit=limit; }
+ *                 renderRun(text, start, styleLimit,
+ *                           direction, styleRuns[i].style);
+ *                 if(styleLimit==limit) { break; }
+ *                 start=styleLimit;
+ *             }
+ *         }
+ *     } else {
+ *         int styleStart;
+ *
+ *         for(i=styleRunCount-1; i>=0; --i) {
+ *             if(i>0) {
+ *                 styleStart=styleRuns[i-1].limit;
+ *             } else {
+ *                 styleStart=0;
+ *             }
+ *             if(limit>=styleStart) {
+ *                 if(styleStart=length
+ *
+ *         width=getTextWidth(text, 0, length, styleRuns, styleRunCount);
+ *         if(width<=lineWidth) {
+ *             // everything fits onto one line
+ *
+ *            // prepare rendering a new line from either left or right
+ *             startLine(paraLevel, width);
+ *
+ *             renderLine(para, text, 0, length,
+ *                        styleRuns, styleRunCount, pErrorCode);
+ *         } else {
+ *             UBiDi *line;
+ *
+ *             // we need to render several lines
+ *             line=ubidi_openSized(length, 0, pErrorCode);
+ *             if(line!=NULL) {
+ *                 int32_t start=0, limit;
+ *                 int styleRunStart=0, styleRunLimit;
+ *
+ *                 for(;;) {
+ *                     limit=length;
+ *                     styleRunLimit=styleRunCount;
+ *                     getLineBreak(text, start, &limit, para,
+ *                                  styleRuns, styleRunStart, &styleRunLimit,
+ *                                 &width);
+ *                     ubidi_setLine(para, start, limit, line, pErrorCode);
+ *                     if(U_SUCCESS(*pErrorCode)) {
+ *                         // prepare rendering a new line
+ *                         // from either left or right
+ *                         startLine(paraLevel, width);
+ *
+ *                         renderLine(line, text, start, limit,
+ *                                    styleRuns+styleRunStart,
+ *                                    styleRunLimit-styleRunStart, pErrorCode);
+ *                     }
+ *                     if(limit==length) { break; }
+ *                     start=limit;
+ *                     styleRunStart=styleRunLimit-1;
+ *                     if(start>=styleRuns[styleRunStart].limit) {
+ *                         ++styleRunStart;
+ *                     }
+ *                 }
+ *
+ *                 ubidi_close(line);
+ *             }
+ *        }
+ *    }
+ *
+ *     ubidi_close(para);
+ *}
+ *\endcode
+ * 
+ */ + +/*DOCXX_TAG*/ +/*@{*/ + +/** + * UBiDiLevel is the type of the level values in this + * Bidi implementation. + * It holds an embedding level and indicates the visual direction + * by its bit 0 (even/odd value).

+ * + * It can also hold non-level values for the + * paraLevel and embeddingLevels + * arguments of ubidi_setPara(); there: + *

    + *
  • bit 7 of an embeddingLevels[] + * value indicates whether the using application is + * specifying the level of a character to override whatever the + * Bidi implementation would resolve it to.
  • + *
  • paraLevel can be set to the + * pseudo-level values UBIDI_DEFAULT_LTR + * and UBIDI_DEFAULT_RTL.
  • + *
+ * + * @see ubidi_setPara + * + *

The related constants are not real, valid level values. + * UBIDI_DEFAULT_XXX can be used to specify + * a default for the paragraph level for + * when the ubidi_setPara() function + * shall determine it but there is no + * strongly typed character in the input.

+ * + * Note that the value for UBIDI_DEFAULT_LTR is even + * and the one for UBIDI_DEFAULT_RTL is odd, + * just like with normal LTR and RTL level values - + * these special values are designed that way. Also, the implementation + * assumes that UBIDI_MAX_EXPLICIT_LEVEL is odd. + * + * Note: The numeric values of the related constants will not change: + * They are tied to the use of 7-bit byte values (plus the override bit) + * and of the UBiDiLevel=uint8_t data type in this API. + * + * @see UBIDI_DEFAULT_LTR + * @see UBIDI_DEFAULT_RTL + * @see UBIDI_LEVEL_OVERRIDE + * @see UBIDI_MAX_EXPLICIT_LEVEL + * @stable ICU 2.0 + */ +typedef uint8_t UBiDiLevel; + +/** Paragraph level setting.

+ * + * Constant indicating that the base direction depends on the first strong + * directional character in the text according to the Unicode Bidirectional + * Algorithm. If no strong directional character is present, + * then set the paragraph level to 0 (left-to-right).

+ * + * If this value is used in conjunction with reordering modes + * UBIDI_REORDER_INVERSE_LIKE_DIRECT or + * UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL, the text to reorder + * is assumed to be visual LTR, and the text after reordering is required + * to be the corresponding logical string with appropriate contextual + * direction. The direction of the result string will be RTL if either + * the righmost or leftmost strong character of the source text is RTL + * or Arabic Letter, the direction will be LTR otherwise.

+ * + * If reordering option UBIDI_OPTION_INSERT_MARKS is set, an RLM may + * be added at the beginning of the result string to ensure round trip + * (that the result string, when reordered back to visual, will produce + * the original source text). + * @see UBIDI_REORDER_INVERSE_LIKE_DIRECT + * @see UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL + * @stable ICU 2.0 + */ +#define UBIDI_DEFAULT_LTR 0xfe + +/** Paragraph level setting.

+ * + * Constant indicating that the base direction depends on the first strong + * directional character in the text according to the Unicode Bidirectional + * Algorithm. If no strong directional character is present, + * then set the paragraph level to 1 (right-to-left).

+ * + * If this value is used in conjunction with reordering modes + * UBIDI_REORDER_INVERSE_LIKE_DIRECT or + * UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL, the text to reorder + * is assumed to be visual LTR, and the text after reordering is required + * to be the corresponding logical string with appropriate contextual + * direction. The direction of the result string will be RTL if either + * the righmost or leftmost strong character of the source text is RTL + * or Arabic Letter, or if the text contains no strong character; + * the direction will be LTR otherwise.

+ * + * If reordering option UBIDI_OPTION_INSERT_MARKS is set, an RLM may + * be added at the beginning of the result string to ensure round trip + * (that the result string, when reordered back to visual, will produce + * the original source text). + * @see UBIDI_REORDER_INVERSE_LIKE_DIRECT + * @see UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL + * @stable ICU 2.0 + */ +#define UBIDI_DEFAULT_RTL 0xff + +/** + * Maximum explicit embedding level. + * Same as the max_depth value in the + * Unicode Bidirectional Algorithm. + * (The maximum resolved level can be up to UBIDI_MAX_EXPLICIT_LEVEL+1). + * @stable ICU 2.0 + */ +#define UBIDI_MAX_EXPLICIT_LEVEL 125 + +/** Bit flag for level input. + * Overrides directional properties. + * @stable ICU 2.0 + */ +#define UBIDI_LEVEL_OVERRIDE 0x80 + +/** + * Special value which can be returned by the mapping functions when a logical + * index has no corresponding visual index or vice-versa. This may happen + * for the logical-to-visual mapping of a Bidi control when option + * #UBIDI_OPTION_REMOVE_CONTROLS is specified. This can also happen + * for the visual-to-logical mapping of a Bidi mark (LRM or RLM) inserted + * by option #UBIDI_OPTION_INSERT_MARKS. + * @see ubidi_getVisualIndex + * @see ubidi_getVisualMap + * @see ubidi_getLogicalIndex + * @see ubidi_getLogicalMap + * @stable ICU 3.6 + */ +#define UBIDI_MAP_NOWHERE (-1) + +/** + * UBiDiDirection values indicate the text direction. + * @stable ICU 2.0 + */ +enum UBiDiDirection { + /** Left-to-right text. This is a 0 value. + *

    + *
  • As return value for ubidi_getDirection(), it means + * that the source string contains no right-to-left characters, or + * that the source string is empty and the paragraph level is even. + *
  • As return value for ubidi_getBaseDirection(), it + * means that the first strong character of the source string has + * a left-to-right direction. + *
+ * @stable ICU 2.0 + */ + UBIDI_LTR, + /** Right-to-left text. This is a 1 value. + *
    + *
  • As return value for ubidi_getDirection(), it means + * that the source string contains no left-to-right characters, or + * that the source string is empty and the paragraph level is odd. + *
  • As return value for ubidi_getBaseDirection(), it + * means that the first strong character of the source string has + * a right-to-left direction. + *
+ * @stable ICU 2.0 + */ + UBIDI_RTL, + /** Mixed-directional text. + *

As return value for ubidi_getDirection(), it means + * that the source string contains both left-to-right and + * right-to-left characters. + * @stable ICU 2.0 + */ + UBIDI_MIXED, + /** No strongly directional text. + *

As return value for ubidi_getBaseDirection(), it means + * that the source string is missing or empty, or contains neither left-to-right + * nor right-to-left characters. + * @stable ICU 4.6 + */ + UBIDI_NEUTRAL +}; + +/** @stable ICU 2.0 */ +typedef enum UBiDiDirection UBiDiDirection; + +/** + * Forward declaration of the UBiDi structure for the declaration of + * the API functions. Its fields are implementation-specific.

+ * This structure holds information about a paragraph (or multiple paragraphs) + * of text with Bidi-algorithm-related details, or about one line of + * such a paragraph.

+ * Reordering can be done on a line, or on one or more paragraphs which are + * then interpreted each as one single line. + * @stable ICU 2.0 + */ +struct UBiDi; + +/** @stable ICU 2.0 */ +typedef struct UBiDi UBiDi; + +/** + * Allocate a UBiDi structure. + * Such an object is initially empty. It is assigned + * the Bidi properties of a piece of text containing one or more paragraphs + * by ubidi_setPara() + * or the Bidi properties of a line within a paragraph by + * ubidi_setLine().

+ * This object can be reused for as long as it is not deallocated + * by calling ubidi_close().

+ * ubidi_setPara() and ubidi_setLine() will allocate + * additional memory for internal structures as necessary. + * + * @return An empty UBiDi object. + * @stable ICU 2.0 + */ +U_CAPI UBiDi * U_EXPORT2 +ubidi_open(void); + +/** + * Allocate a UBiDi structure with preallocated memory + * for internal structures. + * This function provides a UBiDi object like ubidi_open() + * with no arguments, but it also preallocates memory for internal structures + * according to the sizings supplied by the caller.

+ * Subsequent functions will not allocate any more memory, and are thus + * guaranteed not to fail because of lack of memory.

+ * The preallocation can be limited to some of the internal memory + * by setting some values to 0 here. That means that if, e.g., + * maxRunCount cannot be reasonably predetermined and should not + * be set to maxLength (the only failproof value) to avoid + * wasting memory, then maxRunCount could be set to 0 here + * and the internal structures that are associated with it will be allocated + * on demand, just like with ubidi_open(). + * + * @param maxLength is the maximum text or line length that internal memory + * will be preallocated for. An attempt to associate this object with a + * longer text will fail, unless this value is 0, which leaves the allocation + * up to the implementation. + * + * @param maxRunCount is the maximum anticipated number of same-level runs + * that internal memory will be preallocated for. An attempt to access + * visual runs on an object that was not preallocated for as many runs + * as the text was actually resolved to will fail, + * unless this value is 0, which leaves the allocation up to the implementation.

+ * The number of runs depends on the actual text and maybe anywhere between + * 1 and maxLength. It is typically small. + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @return An empty UBiDi object with preallocated memory. + * @stable ICU 2.0 + */ +U_CAPI UBiDi * U_EXPORT2 +ubidi_openSized(int32_t maxLength, int32_t maxRunCount, UErrorCode *pErrorCode); + +/** + * ubidi_close() must be called to free the memory + * associated with a UBiDi object.

+ * + * Important: + * A parent UBiDi object must not be destroyed or reused if + * it still has children. + * If a UBiDi object has become the child + * of another one (its parent) by calling + * ubidi_setLine(), then the child object must + * be destroyed (closed) or reused (by calling + * ubidi_setPara() or ubidi_setLine()) + * before the parent object. + * + * @param pBiDi is a UBiDi object. + * + * @see ubidi_setPara + * @see ubidi_setLine + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ubidi_close(UBiDi *pBiDi); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUBiDiPointer + * "Smart pointer" class, closes a UBiDi via ubidi_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUBiDiPointer, UBiDi, ubidi_close); + +U_NAMESPACE_END + +#endif + +/** + * Modify the operation of the Bidi algorithm such that it + * approximates an "inverse Bidi" algorithm. This function + * must be called before ubidi_setPara(). + * + *

The normal operation of the Bidi algorithm as described + * in the Unicode Technical Report is to take text stored in logical + * (keyboard, typing) order and to determine the reordering of it for visual + * rendering. + * Some legacy systems store text in visual order, and for operations + * with standard, Unicode-based algorithms, the text needs to be transformed + * to logical order. This is effectively the inverse algorithm of the + * described Bidi algorithm. Note that there is no standard algorithm for + * this "inverse Bidi" and that the current implementation provides only an + * approximation of "inverse Bidi".

+ * + *

With isInverse set to true, + * this function changes the behavior of some of the subsequent functions + * in a way that they can be used for the inverse Bidi algorithm. + * Specifically, runs of text with numeric characters will be treated in a + * special way and may need to be surrounded with LRM characters when they are + * written in reordered sequence.

+ * + *

Output runs should be retrieved using ubidi_getVisualRun(). + * Since the actual input for "inverse Bidi" is visually ordered text and + * ubidi_getVisualRun() gets the reordered runs, these are actually + * the runs of the logically ordered output.

+ * + *

Calling this function with argument isInverse set to + * true is equivalent to calling + * ubidi_setReorderingMode with argument + * reorderingMode + * set to #UBIDI_REORDER_INVERSE_NUMBERS_AS_L.
+ * Calling this function with argument isInverse set to + * false is equivalent to calling + * ubidi_setReorderingMode with argument + * reorderingMode + * set to #UBIDI_REORDER_DEFAULT. + * + * @param pBiDi is a UBiDi object. + * + * @param isInverse specifies "forward" or "inverse" Bidi operation. + * + * @see ubidi_setPara + * @see ubidi_writeReordered + * @see ubidi_setReorderingMode + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ubidi_setInverse(UBiDi *pBiDi, UBool isInverse); + +/** + * Is this Bidi object set to perform the inverse Bidi algorithm? + *

Note: calling this function after setting the reordering mode with + * ubidi_setReorderingMode will return true if the + * reordering mode was set to #UBIDI_REORDER_INVERSE_NUMBERS_AS_L, + * false for all other values.

+ * + * @param pBiDi is a UBiDi object. + * @return true if the Bidi object is set to perform the inverse Bidi algorithm + * by handling numbers as L. + * + * @see ubidi_setInverse + * @see ubidi_setReorderingMode + * @stable ICU 2.0 + */ + +U_CAPI UBool U_EXPORT2 +ubidi_isInverse(UBiDi *pBiDi); + +/** + * Specify whether block separators must be allocated level zero, + * so that successive paragraphs will progress from left to right. + * This function must be called before ubidi_setPara(). + * Paragraph separators (B) may appear in the text. Setting them to level zero + * means that all paragraph separators (including one possibly appearing + * in the last text position) are kept in the reordered text after the text + * that they follow in the source text. + * When this feature is not enabled, a paragraph separator at the last + * position of the text before reordering will go to the first position + * of the reordered text when the paragraph level is odd. + * + * @param pBiDi is a UBiDi object. + * + * @param orderParagraphsLTR specifies whether paragraph separators (B) must + * receive level 0, so that successive paragraphs progress from left to right. + * + * @see ubidi_setPara + * @stable ICU 3.4 + */ +U_CAPI void U_EXPORT2 +ubidi_orderParagraphsLTR(UBiDi *pBiDi, UBool orderParagraphsLTR); + +/** + * Is this Bidi object set to allocate level 0 to block separators so that + * successive paragraphs progress from left to right? + * + * @param pBiDi is a UBiDi object. + * @return true if the Bidi object is set to allocate level 0 to block + * separators. + * + * @see ubidi_orderParagraphsLTR + * @stable ICU 3.4 + */ +U_CAPI UBool U_EXPORT2 +ubidi_isOrderParagraphsLTR(UBiDi *pBiDi); + +/** + * UBiDiReorderingMode values indicate which variant of the Bidi + * algorithm to use. + * + * @see ubidi_setReorderingMode + * @stable ICU 3.6 + */ +typedef enum UBiDiReorderingMode { + /** Regular Logical to Visual Bidi algorithm according to Unicode. + * This is a 0 value. + * @stable ICU 3.6 */ + UBIDI_REORDER_DEFAULT = 0, + /** Logical to Visual algorithm which handles numbers in a way which + * mimics the behavior of Windows XP. + * @stable ICU 3.6 */ + UBIDI_REORDER_NUMBERS_SPECIAL, + /** Logical to Visual algorithm grouping numbers with adjacent R characters + * (reversible algorithm). + * @stable ICU 3.6 */ + UBIDI_REORDER_GROUP_NUMBERS_WITH_R, + /** Reorder runs only to transform a Logical LTR string to the Logical RTL + * string with the same display, or vice-versa.
+ * If this mode is set together with option + * #UBIDI_OPTION_INSERT_MARKS, some Bidi controls in the source + * text may be removed and other controls may be added to produce the + * minimum combination which has the required display. + * @stable ICU 3.6 */ + UBIDI_REORDER_RUNS_ONLY, + /** Visual to Logical algorithm which handles numbers like L + * (same algorithm as selected by ubidi_setInverse(true). + * @see ubidi_setInverse + * @stable ICU 3.6 */ + UBIDI_REORDER_INVERSE_NUMBERS_AS_L, + /** Visual to Logical algorithm equivalent to the regular Logical to Visual + * algorithm. + * @stable ICU 3.6 */ + UBIDI_REORDER_INVERSE_LIKE_DIRECT, + /** Inverse Bidi (Visual to Logical) algorithm for the + * UBIDI_REORDER_NUMBERS_SPECIAL Bidi algorithm. + * @stable ICU 3.6 */ + UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL, +#ifndef U_HIDE_DEPRECATED_API + /** + * Number of values for reordering mode. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UBIDI_REORDER_COUNT +#endif // U_HIDE_DEPRECATED_API +} UBiDiReorderingMode; + +/** + * Modify the operation of the Bidi algorithm such that it implements some + * variant to the basic Bidi algorithm or approximates an "inverse Bidi" + * algorithm, depending on different values of the "reordering mode". + * This function must be called before ubidi_setPara(), and stays + * in effect until called again with a different argument. + * + *

The normal operation of the Bidi algorithm as described + * in the Unicode Standard Annex #9 is to take text stored in logical + * (keyboard, typing) order and to determine how to reorder it for visual + * rendering.

+ * + *

With the reordering mode set to a value other than + * #UBIDI_REORDER_DEFAULT, this function changes the behavior of + * some of the subsequent functions in a way such that they implement an + * inverse Bidi algorithm or some other algorithm variants.

+ * + *

Some legacy systems store text in visual order, and for operations + * with standard, Unicode-based algorithms, the text needs to be transformed + * into logical order. This is effectively the inverse algorithm of the + * described Bidi algorithm. Note that there is no standard algorithm for + * this "inverse Bidi", so a number of variants are implemented here.

+ * + *

In other cases, it may be desirable to emulate some variant of the + * Logical to Visual algorithm (e.g. one used in MS Windows), or perform a + * Logical to Logical transformation.

+ * + *
    + *
  • When the reordering mode is set to #UBIDI_REORDER_DEFAULT, + * the standard Bidi Logical to Visual algorithm is applied.
  • + * + *
  • When the reordering mode is set to + * #UBIDI_REORDER_NUMBERS_SPECIAL, + * the algorithm used to perform Bidi transformations when calling + * ubidi_setPara should approximate the algorithm used in + * Microsoft Windows XP rather than strictly conform to the Unicode Bidi + * algorithm. + *
    + * The differences between the basic algorithm and the algorithm addressed + * by this option are as follows: + *
      + *
    • Within text at an even embedding level, the sequence "123AB" + * (where AB represent R or AL letters) is transformed to "123BA" by the + * Unicode algorithm and to "BA123" by the Windows algorithm.
    • + *
    • Arabic-Indic numbers (AN) are handled by the Windows algorithm just + * like regular numbers (EN).
    • + *
  • + * + *
  • When the reordering mode is set to + * #UBIDI_REORDER_GROUP_NUMBERS_WITH_R, + * numbers located between LTR text and RTL text are associated with the RTL + * text. For instance, an LTR paragraph with content "abc 123 DEF" (where + * upper case letters represent RTL characters) will be transformed to + * "abc FED 123" (and not "abc 123 FED"), "DEF 123 abc" will be transformed + * to "123 FED abc" and "123 FED abc" will be transformed to "DEF 123 abc". + * This makes the algorithm reversible and makes it useful when round trip + * (from visual to logical and back to visual) must be achieved without + * adding LRM characters. However, this is a variation from the standard + * Unicode Bidi algorithm.
    + * The source text should not contain Bidi control characters other than LRM + * or RLM.
  • + * + *
  • When the reordering mode is set to + * #UBIDI_REORDER_RUNS_ONLY, + * a "Logical to Logical" transformation must be performed: + *
      + *
    • If the default text level of the source text (argument paraLevel + * in ubidi_setPara) is even, the source text will be handled as + * LTR logical text and will be transformed to the RTL logical text which has + * the same LTR visual display.
    • + *
    • If the default level of the source text is odd, the source text + * will be handled as RTL logical text and will be transformed to the + * LTR logical text which has the same LTR visual display.
    • + *
    + * This mode may be needed when logical text which is basically Arabic or + * Hebrew, with possible included numbers or phrases in English, has to be + * displayed as if it had an even embedding level (this can happen if the + * displaying application treats all text as if it was basically LTR). + *
    + * This mode may also be needed in the reverse case, when logical text which is + * basically English, with possible included phrases in Arabic or Hebrew, has to + * be displayed as if it had an odd embedding level. + *
    + * Both cases could be handled by adding LRE or RLE at the head of the text, + * if the display subsystem supports these formatting controls. If it does not, + * the problem may be handled by transforming the source text in this mode + * before displaying it, so that it will be displayed properly.
    + * The source text should not contain Bidi control characters other than LRM + * or RLM.
  • + * + *
  • When the reordering mode is set to + * #UBIDI_REORDER_INVERSE_NUMBERS_AS_L, an "inverse Bidi" algorithm + * is applied. + * Runs of text with numeric characters will be treated like LTR letters and + * may need to be surrounded with LRM characters when they are written in + * reordered sequence (the option #UBIDI_INSERT_LRM_FOR_NUMERIC can + * be used with function ubidi_writeReordered to this end. This + * mode is equivalent to calling ubidi_setInverse() with + * argument isInverse set to true.
  • + * + *
  • When the reordering mode is set to + * #UBIDI_REORDER_INVERSE_LIKE_DIRECT, the "direct" Logical to Visual + * Bidi algorithm is used as an approximation of an "inverse Bidi" algorithm. + * This mode is similar to mode #UBIDI_REORDER_INVERSE_NUMBERS_AS_L + * but is closer to the regular Bidi algorithm. + *
    + * For example, an LTR paragraph with the content "FED 123 456 CBA" (where + * upper case represents RTL characters) will be transformed to + * "ABC 456 123 DEF", as opposed to "DEF 123 456 ABC" + * with mode UBIDI_REORDER_INVERSE_NUMBERS_AS_L.
    + * When used in conjunction with option + * #UBIDI_OPTION_INSERT_MARKS, this mode generally + * adds Bidi marks to the output significantly more sparingly than mode + * #UBIDI_REORDER_INVERSE_NUMBERS_AS_L with option + * #UBIDI_INSERT_LRM_FOR_NUMERIC in calls to + * ubidi_writeReordered.
  • + * + *
  • When the reordering mode is set to + * #UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL, the Logical to Visual + * Bidi algorithm used in Windows XP is used as an approximation of an "inverse Bidi" algorithm. + *
    + * For example, an LTR paragraph with the content "abc FED123" (where + * upper case represents RTL characters) will be transformed to "abc 123DEF."
  • + *
+ * + *

In all the reordering modes specifying an "inverse Bidi" algorithm + * (i.e. those with a name starting with UBIDI_REORDER_INVERSE), + * output runs should be retrieved using + * ubidi_getVisualRun(), and the output text with + * ubidi_writeReordered(). The caller should keep in mind that in + * "inverse Bidi" modes the input is actually visually ordered text and + * reordered output returned by ubidi_getVisualRun() or + * ubidi_writeReordered() are actually runs or character string + * of logically ordered output.
+ * For all the "inverse Bidi" modes, the source text should not contain + * Bidi control characters other than LRM or RLM.

+ * + *

Note that option #UBIDI_OUTPUT_REVERSE of + * ubidi_writeReordered has no useful meaning and should not be + * used in conjunction with any value of the reordering mode specifying + * "inverse Bidi" or with value UBIDI_REORDER_RUNS_ONLY. + * + * @param pBiDi is a UBiDi object. + * @param reorderingMode specifies the required variant of the Bidi algorithm. + * + * @see UBiDiReorderingMode + * @see ubidi_setInverse + * @see ubidi_setPara + * @see ubidi_writeReordered + * @stable ICU 3.6 + */ +U_CAPI void U_EXPORT2 +ubidi_setReorderingMode(UBiDi *pBiDi, UBiDiReorderingMode reorderingMode); + +/** + * What is the requested reordering mode for a given Bidi object? + * + * @param pBiDi is a UBiDi object. + * @return the current reordering mode of the Bidi object + * @see ubidi_setReorderingMode + * @stable ICU 3.6 + */ +U_CAPI UBiDiReorderingMode U_EXPORT2 +ubidi_getReorderingMode(UBiDi *pBiDi); + +/** + * UBiDiReorderingOption values indicate which options are + * specified to affect the Bidi algorithm. + * + * @see ubidi_setReorderingOptions + * @stable ICU 3.6 + */ +typedef enum UBiDiReorderingOption { + /** + * option value for ubidi_setReorderingOptions: + * disable all the options which can be set with this function + * @see ubidi_setReorderingOptions + * @stable ICU 3.6 + */ + UBIDI_OPTION_DEFAULT = 0, + + /** + * option bit for ubidi_setReorderingOptions: + * insert Bidi marks (LRM or RLM) when needed to ensure correct result of + * a reordering to a Logical order + * + *

This option must be set or reset before calling + * ubidi_setPara.

+ * + *

This option is significant only with reordering modes which generate + * a result with Logical order, specifically:

+ *
    + *
  • #UBIDI_REORDER_RUNS_ONLY
  • + *
  • #UBIDI_REORDER_INVERSE_NUMBERS_AS_L
  • + *
  • #UBIDI_REORDER_INVERSE_LIKE_DIRECT
  • + *
  • #UBIDI_REORDER_INVERSE_FOR_NUMBERS_SPECIAL
  • + *
+ * + *

If this option is set in conjunction with reordering mode + * #UBIDI_REORDER_INVERSE_NUMBERS_AS_L or with calling + * ubidi_setInverse(true), it implies + * option #UBIDI_INSERT_LRM_FOR_NUMERIC + * in calls to function ubidi_writeReordered().

+ * + *

For other reordering modes, a minimum number of LRM or RLM characters + * will be added to the source text after reordering it so as to ensure + * round trip, i.e. when applying the inverse reordering mode on the + * resulting logical text with removal of Bidi marks + * (option #UBIDI_OPTION_REMOVE_CONTROLS set before calling + * ubidi_setPara() or option #UBIDI_REMOVE_BIDI_CONTROLS + * in ubidi_writeReordered), the result will be identical to the + * source text in the first transformation. + * + *

This option will be ignored if specified together with option + * #UBIDI_OPTION_REMOVE_CONTROLS. It inhibits option + * UBIDI_REMOVE_BIDI_CONTROLS in calls to function + * ubidi_writeReordered() and it implies option + * #UBIDI_INSERT_LRM_FOR_NUMERIC in calls to function + * ubidi_writeReordered() if the reordering mode is + * #UBIDI_REORDER_INVERSE_NUMBERS_AS_L.

+ * + * @see ubidi_setReorderingMode + * @see ubidi_setReorderingOptions + * @stable ICU 3.6 + */ + UBIDI_OPTION_INSERT_MARKS = 1, + + /** + * option bit for ubidi_setReorderingOptions: + * remove Bidi control characters + * + *

This option must be set or reset before calling + * ubidi_setPara.

+ * + *

This option nullifies option #UBIDI_OPTION_INSERT_MARKS. + * It inhibits option #UBIDI_INSERT_LRM_FOR_NUMERIC in calls + * to function ubidi_writeReordered() and it implies option + * #UBIDI_REMOVE_BIDI_CONTROLS in calls to that function.

+ * + * @see ubidi_setReorderingMode + * @see ubidi_setReorderingOptions + * @stable ICU 3.6 + */ + UBIDI_OPTION_REMOVE_CONTROLS = 2, + + /** + * option bit for ubidi_setReorderingOptions: + * process the output as part of a stream to be continued + * + *

This option must be set or reset before calling + * ubidi_setPara.

+ * + *

This option specifies that the caller is interested in processing large + * text object in parts. + * The results of the successive calls are expected to be concatenated by the + * caller. Only the call for the last part will have this option bit off.

+ * + *

When this option bit is on, ubidi_setPara() may process + * less than the full source text in order to truncate the text at a meaningful + * boundary. The caller should call ubidi_getProcessedLength() + * immediately after calling ubidi_setPara() in order to + * determine how much of the source text has been processed. + * Source text beyond that length should be resubmitted in following calls to + * ubidi_setPara. The processed length may be less than + * the length of the source text if a character preceding the last character of + * the source text constitutes a reasonable boundary (like a block separator) + * for text to be continued.
+ * If the last character of the source text constitutes a reasonable + * boundary, the whole text will be processed at once.
+ * If nowhere in the source text there exists + * such a reasonable boundary, the processed length will be zero.
+ * The caller should check for such an occurrence and do one of the following: + *

  • submit a larger amount of text with a better chance to include + * a reasonable boundary.
  • + *
  • resubmit the same text after turning off option + * UBIDI_OPTION_STREAMING.
+ * In all cases, this option should be turned off before processing the last + * part of the text.

+ * + *

When the UBIDI_OPTION_STREAMING option is used, + * it is recommended to call ubidi_orderParagraphsLTR() with + * argument orderParagraphsLTR set to true before + * calling ubidi_setPara so that later paragraphs may be + * concatenated to previous paragraphs on the right.

+ * + * @see ubidi_setReorderingMode + * @see ubidi_setReorderingOptions + * @see ubidi_getProcessedLength + * @see ubidi_orderParagraphsLTR + * @stable ICU 3.6 + */ + UBIDI_OPTION_STREAMING = 4 +} UBiDiReorderingOption; + +/** + * Specify which of the reordering options + * should be applied during Bidi transformations. + * + * @param pBiDi is a UBiDi object. + * @param reorderingOptions is a combination of zero or more of the following + * options: + * #UBIDI_OPTION_DEFAULT, #UBIDI_OPTION_INSERT_MARKS, + * #UBIDI_OPTION_REMOVE_CONTROLS, #UBIDI_OPTION_STREAMING. + * + * @see ubidi_getReorderingOptions + * @stable ICU 3.6 + */ +U_CAPI void U_EXPORT2 +ubidi_setReorderingOptions(UBiDi *pBiDi, uint32_t reorderingOptions); + +/** + * What are the reordering options applied to a given Bidi object? + * + * @param pBiDi is a UBiDi object. + * @return the current reordering options of the Bidi object + * @see ubidi_setReorderingOptions + * @stable ICU 3.6 + */ +U_CAPI uint32_t U_EXPORT2 +ubidi_getReorderingOptions(UBiDi *pBiDi); + +/** + * Set the context before a call to ubidi_setPara().

+ * + * ubidi_setPara() computes the left-right directionality for a given piece + * of text which is supplied as one of its arguments. Sometimes this piece + * of text (the "main text") should be considered in context, because text + * appearing before ("prologue") and/or after ("epilogue") the main text + * may affect the result of this computation.

+ * + * This function specifies the prologue and/or the epilogue for the next + * call to ubidi_setPara(). The characters specified as prologue and + * epilogue should not be modified by the calling program until the call + * to ubidi_setPara() has returned. If successive calls to ubidi_setPara() + * all need specification of a context, ubidi_setContext() must be called + * before each call to ubidi_setPara(). In other words, a context is not + * "remembered" after the following successful call to ubidi_setPara().

+ * + * If a call to ubidi_setPara() specifies UBIDI_DEFAULT_LTR or + * UBIDI_DEFAULT_RTL as paraLevel and is preceded by a call to + * ubidi_setContext() which specifies a prologue, the paragraph level will + * be computed taking in consideration the text in the prologue.

+ * + * When ubidi_setPara() is called without a previous call to + * ubidi_setContext, the main text is handled as if preceded and followed + * by strong directional characters at the current paragraph level. + * Calling ubidi_setContext() with specification of a prologue will change + * this behavior by handling the main text as if preceded by the last + * strong character appearing in the prologue, if any. + * Calling ubidi_setContext() with specification of an epilogue will change + * the behavior of ubidi_setPara() by handling the main text as if followed + * by the first strong character or digit appearing in the epilogue, if any.

+ * + * Note 1: if ubidi_setContext is called repeatedly without + * calling ubidi_setPara, the earlier calls have no effect, + * only the last call will be remembered for the next call to + * ubidi_setPara.

+ * + * Note 2: calling ubidi_setContext(pBiDi, NULL, 0, NULL, 0, &errorCode) + * cancels any previous setting of non-empty prologue or epilogue. + * The next call to ubidi_setPara() will process no + * prologue or epilogue.

+ * + * Note 3: users must be aware that even after setting the context + * before a call to ubidi_setPara() to perform e.g. a logical to visual + * transformation, the resulting string may not be identical to what it + * would have been if all the text, including prologue and epilogue, had + * been processed together.
+ * Example (upper case letters represent RTL characters):
+ *   prologue = "abc DE"
+ *   epilogue = none
+ *   main text = "FGH xyz"
+ *   paraLevel = UBIDI_LTR
+ *   display without prologue = "HGF xyz" + * ("HGF" is adjacent to "xyz")
+ *   display with prologue = "abc HGFED xyz" + * ("HGF" is not adjacent to "xyz")
+ * + * @param pBiDi is a paragraph UBiDi object. + * + * @param prologue is a pointer to the text which precedes the text that + * will be specified in a coming call to ubidi_setPara(). + * If there is no prologue to consider, then proLength + * must be zero and this pointer can be NULL. + * + * @param proLength is the length of the prologue; if proLength==-1 + * then the prologue must be zero-terminated. + * Otherwise proLength must be >= 0. If proLength==0, it means + * that there is no prologue to consider. + * + * @param epilogue is a pointer to the text which follows the text that + * will be specified in a coming call to ubidi_setPara(). + * If there is no epilogue to consider, then epiLength + * must be zero and this pointer can be NULL. + * + * @param epiLength is the length of the epilogue; if epiLength==-1 + * then the epilogue must be zero-terminated. + * Otherwise epiLength must be >= 0. If epiLength==0, it means + * that there is no epilogue to consider. + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @see ubidi_setPara + * @stable ICU 4.8 + */ +U_CAPI void U_EXPORT2 +ubidi_setContext(UBiDi *pBiDi, + const UChar *prologue, int32_t proLength, + const UChar *epilogue, int32_t epiLength, + UErrorCode *pErrorCode); + +/** + * Perform the Unicode Bidi algorithm. It is defined in the + * Unicode Standard Annex #9, + * version 13, + * also described in The Unicode Standard, Version 4.0 .

+ * + * This function takes a piece of plain text containing one or more paragraphs, + * with or without externally specified embedding levels from styled + * text and computes the left-right-directionality of each character.

+ * + * If the entire text is all of the same directionality, then + * the function may not perform all the steps described by the algorithm, + * i.e., some levels may not be the same as if all steps were performed. + * This is not relevant for unidirectional text.
+ * For example, in pure LTR text with numbers the numbers would get + * a resolved level of 2 higher than the surrounding text according to + * the algorithm. This implementation may set all resolved levels to + * the same value in such a case.

+ * + * The text can be composed of multiple paragraphs. Occurrence of a block + * separator in the text terminates a paragraph, and whatever comes next starts + * a new paragraph. The exception to this rule is when a Carriage Return (CR) + * is followed by a Line Feed (LF). Both CR and LF are block separators, but + * in that case, the pair of characters is considered as terminating the + * preceding paragraph, and a new paragraph will be started by a character + * coming after the LF. + * + * @param pBiDi A UBiDi object allocated with ubidi_open() + * which will be set to contain the reordering information, + * especially the resolved levels for all the characters in text. + * + * @param text is a pointer to the text that the Bidi algorithm will be performed on. + * This pointer is stored in the UBiDi object and can be retrieved + * with ubidi_getText().
+ * Note: the text must be (at least) length long. + * + * @param length is the length of the text; if length==-1 then + * the text must be zero-terminated. + * + * @param paraLevel specifies the default level for the text; + * it is typically 0 (LTR) or 1 (RTL). + * If the function shall determine the paragraph level from the text, + * then paraLevel can be set to + * either #UBIDI_DEFAULT_LTR + * or #UBIDI_DEFAULT_RTL; if the text contains multiple + * paragraphs, the paragraph level shall be determined separately for + * each paragraph; if a paragraph does not include any strongly typed + * character, then the desired default is used (0 for LTR or 1 for RTL). + * Any other value between 0 and #UBIDI_MAX_EXPLICIT_LEVEL + * is also valid, with odd levels indicating RTL. + * + * @param embeddingLevels (in) may be used to preset the embedding and override levels, + * ignoring characters like LRE and PDF in the text. + * A level overrides the directional property of its corresponding + * (same index) character if the level has the + * #UBIDI_LEVEL_OVERRIDE bit set.

+ * Aside from that bit, it must be + * paraLevel<=embeddingLevels[]<=UBIDI_MAX_EXPLICIT_LEVEL, + * except that level 0 is always allowed. + * Level 0 for a paragraph separator prevents reordering of paragraphs; + * this only works reliably if #UBIDI_LEVEL_OVERRIDE + * is also set for paragraph separators. + * Level 0 for other characters is treated as a wildcard + * and is lifted up to the resolved level of the surrounding paragraph.

+ * Caution: A copy of this pointer, not of the levels, + * will be stored in the UBiDi object; + * the embeddingLevels array must not be + * deallocated before the UBiDi structure is destroyed or reused, + * and the embeddingLevels + * should not be modified to avoid unexpected results on subsequent Bidi operations. + * However, the ubidi_setPara() and + * ubidi_setLine() functions may modify some or all of the levels.

+ * After the UBiDi object is reused or destroyed, the caller + * must take care of the deallocation of the embeddingLevels array.

+ * Note: the embeddingLevels array must be + * at least length long. + * This pointer can be NULL if this + * value is not necessary. + * + * @param pErrorCode must be a valid pointer to an error code value. + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ubidi_setPara(UBiDi *pBiDi, const UChar *text, int32_t length, + UBiDiLevel paraLevel, UBiDiLevel *embeddingLevels, + UErrorCode *pErrorCode); + +/** + * ubidi_setLine() sets a UBiDi to + * contain the reordering information, especially the resolved levels, + * for all the characters in a line of text. This line of text is + * specified by referring to a UBiDi object representing + * this information for a piece of text containing one or more paragraphs, + * and by specifying a range of indexes in this text.

+ * In the new line object, the indexes will range from 0 to limit-start-1.

+ * + * This is used after calling ubidi_setPara() + * for a piece of text, and after line-breaking on that text. + * It is not necessary if each paragraph is treated as a single line.

+ * + * After line-breaking, rules (L1) and (L2) for the treatment of + * trailing WS and for reordering are performed on + * a UBiDi object that represents a line.

+ * + * Important: pLineBiDi shares data with + * pParaBiDi. + * You must destroy or reuse pLineBiDi before pParaBiDi. + * In other words, you must destroy or reuse the UBiDi object for a line + * before the object for its parent paragraph.

+ * + * The text pointer that was stored in pParaBiDi is also copied, + * and start is added to it so that it points to the beginning of the + * line for this object. + * + * @param pParaBiDi is the parent paragraph object. It must have been set + * by a successful call to ubidi_setPara. + * + * @param start is the line's first index into the text. + * + * @param limit is just behind the line's last index into the text + * (its last index +1).
+ * It must be 0<=startcontaining paragraph limit. + * If the specified line crosses a paragraph boundary, the function + * will terminate with error code U_ILLEGAL_ARGUMENT_ERROR. + * + * @param pLineBiDi is the object that will now represent a line of the text. + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @see ubidi_setPara + * @see ubidi_getProcessedLength + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ubidi_setLine(const UBiDi *pParaBiDi, + int32_t start, int32_t limit, + UBiDi *pLineBiDi, + UErrorCode *pErrorCode); + +/** + * Get the directionality of the text. + * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @return a value of UBIDI_LTR, UBIDI_RTL + * or UBIDI_MIXED + * that indicates if the entire text + * represented by this object is unidirectional, + * and which direction, or if it is mixed-directional. + * Note - The value UBIDI_NEUTRAL is never returned from this method. + * + * @see UBiDiDirection + * @stable ICU 2.0 + */ +U_CAPI UBiDiDirection U_EXPORT2 +ubidi_getDirection(const UBiDi *pBiDi); + +/** + * Gets the base direction of the text provided according + * to the Unicode Bidirectional Algorithm. The base direction + * is derived from the first character in the string with bidirectional + * character type L, R, or AL. If the first such character has type L, + * UBIDI_LTR is returned. If the first such character has + * type R or AL, UBIDI_RTL is returned. If the string does + * not contain any character of these types, then + * UBIDI_NEUTRAL is returned. + * + * This is a lightweight function for use when only the base direction + * is needed and no further bidi processing of the text is needed. + * + * @param text is a pointer to the text whose base + * direction is needed. + * Note: the text must be (at least) @c length long. + * + * @param length is the length of the text; + * if length==-1 then the text + * must be zero-terminated. + * + * @return UBIDI_LTR, UBIDI_RTL, + * UBIDI_NEUTRAL + * + * @see UBiDiDirection + * @stable ICU 4.6 + */ +U_CAPI UBiDiDirection U_EXPORT2 +ubidi_getBaseDirection(const UChar *text, int32_t length ); + +/** + * Get the pointer to the text. + * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @return The pointer to the text that the UBiDi object was created for. + * + * @see ubidi_setPara + * @see ubidi_setLine + * @stable ICU 2.0 + */ +U_CAPI const UChar * U_EXPORT2 +ubidi_getText(const UBiDi *pBiDi); + +/** + * Get the length of the text. + * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @return The length of the text that the UBiDi object was created for. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ubidi_getLength(const UBiDi *pBiDi); + +/** + * Get the paragraph level of the text. + * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @return The paragraph level. If there are multiple paragraphs, their + * level may vary if the required paraLevel is UBIDI_DEFAULT_LTR or + * UBIDI_DEFAULT_RTL. In that case, the level of the first paragraph + * is returned. + * + * @see UBiDiLevel + * @see ubidi_getParagraph + * @see ubidi_getParagraphByIndex + * @stable ICU 2.0 + */ +U_CAPI UBiDiLevel U_EXPORT2 +ubidi_getParaLevel(const UBiDi *pBiDi); + +/** + * Get the number of paragraphs. + * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @return The number of paragraphs. + * @stable ICU 3.4 + */ +U_CAPI int32_t U_EXPORT2 +ubidi_countParagraphs(UBiDi *pBiDi); + +/** + * Get a paragraph, given a position within the text. + * This function returns information about a paragraph.
+ * Note: if the paragraph index is known, it is more efficient to + * retrieve the paragraph information using ubidi_getParagraphByIndex().

+ * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @param charIndex is the index of a character within the text, in the + * range [0..ubidi_getProcessedLength(pBiDi)-1]. + * + * @param pParaStart will receive the index of the first character of the + * paragraph in the text. + * This pointer can be NULL if this + * value is not necessary. + * + * @param pParaLimit will receive the limit of the paragraph. + * The l-value that you point to here may be the + * same expression (variable) as the one for + * charIndex. + * This pointer can be NULL if this + * value is not necessary. + * + * @param pParaLevel will receive the level of the paragraph. + * This pointer can be NULL if this + * value is not necessary. + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @return The index of the paragraph containing the specified position. + * + * @see ubidi_getProcessedLength + * @stable ICU 3.4 + */ +U_CAPI int32_t U_EXPORT2 +ubidi_getParagraph(const UBiDi *pBiDi, int32_t charIndex, int32_t *pParaStart, + int32_t *pParaLimit, UBiDiLevel *pParaLevel, + UErrorCode *pErrorCode); + +/** + * Get a paragraph, given the index of this paragraph. + * + * This function returns information about a paragraph.

+ * + * @param pBiDi is the paragraph UBiDi object. + * + * @param paraIndex is the number of the paragraph, in the + * range [0..ubidi_countParagraphs(pBiDi)-1]. + * + * @param pParaStart will receive the index of the first character of the + * paragraph in the text. + * This pointer can be NULL if this + * value is not necessary. + * + * @param pParaLimit will receive the limit of the paragraph. + * This pointer can be NULL if this + * value is not necessary. + * + * @param pParaLevel will receive the level of the paragraph. + * This pointer can be NULL if this + * value is not necessary. + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @stable ICU 3.4 + */ +U_CAPI void U_EXPORT2 +ubidi_getParagraphByIndex(const UBiDi *pBiDi, int32_t paraIndex, + int32_t *pParaStart, int32_t *pParaLimit, + UBiDiLevel *pParaLevel, UErrorCode *pErrorCode); + +/** + * Get the level for one character. + * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @param charIndex the index of a character. It must be in the range + * [0..ubidi_getProcessedLength(pBiDi)]. + * + * @return The level for the character at charIndex (0 if charIndex is not + * in the valid range). + * + * @see UBiDiLevel + * @see ubidi_getProcessedLength + * @stable ICU 2.0 + */ +U_CAPI UBiDiLevel U_EXPORT2 +ubidi_getLevelAt(const UBiDi *pBiDi, int32_t charIndex); + +/** + * Get an array of levels for each character.

+ * + * Note that this function may allocate memory under some + * circumstances, unlike ubidi_getLevelAt(). + * + * @param pBiDi is the paragraph or line UBiDi object, whose + * text length must be strictly positive. + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @return The levels array for the text, + * or NULL if an error occurs. + * + * @see UBiDiLevel + * @see ubidi_getProcessedLength + * @stable ICU 2.0 + */ +U_CAPI const UBiDiLevel * U_EXPORT2 +ubidi_getLevels(UBiDi *pBiDi, UErrorCode *pErrorCode); + +/** + * Get a logical run. + * This function returns information about a run and is used + * to retrieve runs in logical order.

+ * This is especially useful for line-breaking on a paragraph. + * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @param logicalPosition is a logical position within the source text. + * + * @param pLogicalLimit will receive the limit of the corresponding run. + * The l-value that you point to here may be the + * same expression (variable) as the one for + * logicalPosition. + * This pointer can be NULL if this + * value is not necessary. + * + * @param pLevel will receive the level of the corresponding run. + * This pointer can be NULL if this + * value is not necessary. + * + * @see ubidi_getProcessedLength + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ubidi_getLogicalRun(const UBiDi *pBiDi, int32_t logicalPosition, + int32_t *pLogicalLimit, UBiDiLevel *pLevel); + +/** + * Get the number of runs. + * This function may invoke the actual reordering on the + * UBiDi object, after ubidi_setPara() + * may have resolved only the levels of the text. Therefore, + * ubidi_countRuns() may have to allocate memory, + * and may fail doing so. + * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @return The number of runs. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ubidi_countRuns(UBiDi *pBiDi, UErrorCode *pErrorCode); + +/** + * Get one run's logical start, length, and directionality, + * which can be 0 for LTR or 1 for RTL. + * In an RTL run, the character at the logical start is + * visually on the right of the displayed run. + * The length is the number of characters in the run.

+ * ubidi_countRuns() should be called + * before the runs are retrieved. + * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @param runIndex is the number of the run in visual order, in the + * range [0..ubidi_countRuns(pBiDi)-1]. + * + * @param pLogicalStart is the first logical character index in the text. + * The pointer may be NULL if this index is not needed. + * + * @param pLength is the number of characters (at least one) in the run. + * The pointer may be NULL if this is not needed. + * + * @return the directionality of the run, + * UBIDI_LTR==0 or UBIDI_RTL==1, + * never UBIDI_MIXED, + * never UBIDI_NEUTRAL. + * + * @see ubidi_countRuns + * + * Example: + *

+ * \code
+ * int32_t i, count=ubidi_countRuns(pBiDi),
+ *         logicalStart, visualIndex=0, length;
+ * for(i=0; i0);
+ *     } else {
+ *         logicalStart+=length;  // logicalLimit
+ *         do { // RTL
+ *             show_char(text[--logicalStart], visualIndex++);
+ *         } while(--length>0);
+ *     }
+ * }
+ *\endcode
+ * 
+ * + * Note that in right-to-left runs, code like this places + * second surrogates before first ones (which is generally a bad idea) + * and combining characters before base characters. + *

+ * Use of ubidi_writeReordered(), optionally with the + * #UBIDI_KEEP_BASE_COMBINING option, can be considered in order + * to avoid these issues. + * @stable ICU 2.0 + */ +U_CAPI UBiDiDirection U_EXPORT2 +ubidi_getVisualRun(UBiDi *pBiDi, int32_t runIndex, + int32_t *pLogicalStart, int32_t *pLength); + +/** + * Get the visual position from a logical text position. + * If such a mapping is used many times on the same + * UBiDi object, then calling + * ubidi_getLogicalMap() is more efficient.

+ * + * The value returned may be #UBIDI_MAP_NOWHERE if there is no + * visual position because the corresponding text character is a Bidi control + * removed from output by the option #UBIDI_OPTION_REMOVE_CONTROLS. + *

+ * When the visual output is altered by using options of + * ubidi_writeReordered() such as UBIDI_INSERT_LRM_FOR_NUMERIC, + * UBIDI_KEEP_BASE_COMBINING, UBIDI_OUTPUT_REVERSE, + * UBIDI_REMOVE_BIDI_CONTROLS, the visual position returned may not + * be correct. It is advised to use, when possible, reordering options + * such as UBIDI_OPTION_INSERT_MARKS and UBIDI_OPTION_REMOVE_CONTROLS. + *

+ * Note that in right-to-left runs, this mapping places + * second surrogates before first ones (which is generally a bad idea) + * and combining characters before base characters. + * Use of ubidi_writeReordered(), optionally with the + * #UBIDI_KEEP_BASE_COMBINING option can be considered instead + * of using the mapping, in order to avoid these issues. + * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @param logicalIndex is the index of a character in the text. + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @return The visual position of this character. + * + * @see ubidi_getLogicalMap + * @see ubidi_getLogicalIndex + * @see ubidi_getProcessedLength + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ubidi_getVisualIndex(UBiDi *pBiDi, int32_t logicalIndex, UErrorCode *pErrorCode); + +/** + * Get the logical text position from a visual position. + * If such a mapping is used many times on the same + * UBiDi object, then calling + * ubidi_getVisualMap() is more efficient.

+ * + * The value returned may be #UBIDI_MAP_NOWHERE if there is no + * logical position because the corresponding text character is a Bidi mark + * inserted in the output by option #UBIDI_OPTION_INSERT_MARKS. + *

+ * This is the inverse function to ubidi_getVisualIndex(). + *

+ * When the visual output is altered by using options of + * ubidi_writeReordered() such as UBIDI_INSERT_LRM_FOR_NUMERIC, + * UBIDI_KEEP_BASE_COMBINING, UBIDI_OUTPUT_REVERSE, + * UBIDI_REMOVE_BIDI_CONTROLS, the logical position returned may not + * be correct. It is advised to use, when possible, reordering options + * such as UBIDI_OPTION_INSERT_MARKS and UBIDI_OPTION_REMOVE_CONTROLS. + * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @param visualIndex is the visual position of a character. + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @return The index of this character in the text. + * + * @see ubidi_getVisualMap + * @see ubidi_getVisualIndex + * @see ubidi_getResultLength + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ubidi_getLogicalIndex(UBiDi *pBiDi, int32_t visualIndex, UErrorCode *pErrorCode); + +/** + * Get a logical-to-visual index map (array) for the characters in the UBiDi + * (paragraph or line) object. + *

+ * Some values in the map may be #UBIDI_MAP_NOWHERE if the + * corresponding text characters are Bidi controls removed from the visual + * output by the option #UBIDI_OPTION_REMOVE_CONTROLS. + *

+ * When the visual output is altered by using options of + * ubidi_writeReordered() such as UBIDI_INSERT_LRM_FOR_NUMERIC, + * UBIDI_KEEP_BASE_COMBINING, UBIDI_OUTPUT_REVERSE, + * UBIDI_REMOVE_BIDI_CONTROLS, the visual positions returned may not + * be correct. It is advised to use, when possible, reordering options + * such as UBIDI_OPTION_INSERT_MARKS and UBIDI_OPTION_REMOVE_CONTROLS. + *

+ * Note that in right-to-left runs, this mapping places + * second surrogates before first ones (which is generally a bad idea) + * and combining characters before base characters. + * Use of ubidi_writeReordered(), optionally with the + * #UBIDI_KEEP_BASE_COMBINING option can be considered instead + * of using the mapping, in order to avoid these issues. + * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @param indexMap is a pointer to an array of ubidi_getProcessedLength() + * indexes which will reflect the reordering of the characters. + * If option #UBIDI_OPTION_INSERT_MARKS is set, the number + * of elements allocated in indexMap must be no less than + * ubidi_getResultLength(). + * The array does not need to be initialized.

+ * The index map will result in indexMap[logicalIndex]==visualIndex. + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @see ubidi_getVisualMap + * @see ubidi_getVisualIndex + * @see ubidi_getProcessedLength + * @see ubidi_getResultLength + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ubidi_getLogicalMap(UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode); + +/** + * Get a visual-to-logical index map (array) for the characters in the UBiDi + * (paragraph or line) object. + *

+ * Some values in the map may be #UBIDI_MAP_NOWHERE if the + * corresponding text characters are Bidi marks inserted in the visual output + * by the option #UBIDI_OPTION_INSERT_MARKS. + *

+ * When the visual output is altered by using options of + * ubidi_writeReordered() such as UBIDI_INSERT_LRM_FOR_NUMERIC, + * UBIDI_KEEP_BASE_COMBINING, UBIDI_OUTPUT_REVERSE, + * UBIDI_REMOVE_BIDI_CONTROLS, the logical positions returned may not + * be correct. It is advised to use, when possible, reordering options + * such as UBIDI_OPTION_INSERT_MARKS and UBIDI_OPTION_REMOVE_CONTROLS. + * + * @param pBiDi is the paragraph or line UBiDi object. + * + * @param indexMap is a pointer to an array of ubidi_getResultLength() + * indexes which will reflect the reordering of the characters. + * If option #UBIDI_OPTION_REMOVE_CONTROLS is set, the number + * of elements allocated in indexMap must be no less than + * ubidi_getProcessedLength(). + * The array does not need to be initialized.

+ * The index map will result in indexMap[visualIndex]==logicalIndex. + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @see ubidi_getLogicalMap + * @see ubidi_getLogicalIndex + * @see ubidi_getProcessedLength + * @see ubidi_getResultLength + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ubidi_getVisualMap(UBiDi *pBiDi, int32_t *indexMap, UErrorCode *pErrorCode); + +/** + * This is a convenience function that does not use a UBiDi object. + * It is intended to be used for when an application has determined the levels + * of objects (character sequences) and just needs to have them reordered (L2). + * This is equivalent to using ubidi_getLogicalMap() on a + * UBiDi object. + * + * @param levels is an array with length levels that have been determined by + * the application. + * + * @param length is the number of levels in the array, or, semantically, + * the number of objects to be reordered. + * It must be length>0. + * + * @param indexMap is a pointer to an array of length + * indexes which will reflect the reordering of the characters. + * The array does not need to be initialized.

+ * The index map will result in indexMap[logicalIndex]==visualIndex. + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ubidi_reorderLogical(const UBiDiLevel *levels, int32_t length, int32_t *indexMap); + +/** + * This is a convenience function that does not use a UBiDi object. + * It is intended to be used for when an application has determined the levels + * of objects (character sequences) and just needs to have them reordered (L2). + * This is equivalent to using ubidi_getVisualMap() on a + * UBiDi object. + * + * @param levels is an array with length levels that have been determined by + * the application. + * + * @param length is the number of levels in the array, or, semantically, + * the number of objects to be reordered. + * It must be length>0. + * + * @param indexMap is a pointer to an array of length + * indexes which will reflect the reordering of the characters. + * The array does not need to be initialized.

+ * The index map will result in indexMap[visualIndex]==logicalIndex. + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ubidi_reorderVisual(const UBiDiLevel *levels, int32_t length, int32_t *indexMap); + +/** + * Invert an index map. + * The index mapping of the first map is inverted and written to + * the second one. + * + * @param srcMap is an array with length elements + * which defines the original mapping from a source array containing + * length elements to a destination array. + * Some elements of the source array may have no mapping in the + * destination array. In that case, their value will be + * the special value UBIDI_MAP_NOWHERE. + * All elements must be >=0 or equal to UBIDI_MAP_NOWHERE. + * Some elements may have a value >= length, if the + * destination array has more elements than the source array. + * There must be no duplicate indexes (two or more elements with the + * same value except UBIDI_MAP_NOWHERE). + * + * @param destMap is an array with a number of elements equal to 1 + the highest + * value in srcMap. + * destMap will be filled with the inverse mapping. + * If element with index i in srcMap has a value k different + * from UBIDI_MAP_NOWHERE, this means that element i of + * the source array maps to element k in the destination array. + * The inverse map will have value i in its k-th element. + * For all elements of the destination array which do not map to + * an element in the source array, the corresponding element in the + * inverse map will have a value equal to UBIDI_MAP_NOWHERE. + * + * @param length is the length of each array. + * @see UBIDI_MAP_NOWHERE + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ubidi_invertMap(const int32_t *srcMap, int32_t *destMap, int32_t length); + +/** option flags for ubidi_writeReordered() */ + +/** + * option bit for ubidi_writeReordered(): + * keep combining characters after their base characters in RTL runs + * + * @see ubidi_writeReordered + * @stable ICU 2.0 + */ +#define UBIDI_KEEP_BASE_COMBINING 1 + +/** + * option bit for ubidi_writeReordered(): + * replace characters with the "mirrored" property in RTL runs + * by their mirror-image mappings + * + * @see ubidi_writeReordered + * @stable ICU 2.0 + */ +#define UBIDI_DO_MIRRORING 2 + +/** + * option bit for ubidi_writeReordered(): + * surround the run with LRMs if necessary; + * this is part of the approximate "inverse Bidi" algorithm + * + *

This option does not imply corresponding adjustment of the index + * mappings.

+ * + * @see ubidi_setInverse + * @see ubidi_writeReordered + * @stable ICU 2.0 + */ +#define UBIDI_INSERT_LRM_FOR_NUMERIC 4 + +/** + * option bit for ubidi_writeReordered(): + * remove Bidi control characters + * (this does not affect #UBIDI_INSERT_LRM_FOR_NUMERIC) + * + *

This option does not imply corresponding adjustment of the index + * mappings.

+ * + * @see ubidi_writeReordered + * @stable ICU 2.0 + */ +#define UBIDI_REMOVE_BIDI_CONTROLS 8 + +/** + * option bit for ubidi_writeReordered(): + * write the output in reverse order + * + *

This has the same effect as calling ubidi_writeReordered() + * first without this option, and then calling + * ubidi_writeReverse() without mirroring. + * Doing this in the same step is faster and avoids a temporary buffer. + * An example for using this option is output to a character terminal that + * is designed for RTL scripts and stores text in reverse order.

+ * + * @see ubidi_writeReordered + * @stable ICU 2.0 + */ +#define UBIDI_OUTPUT_REVERSE 16 + +/** + * Get the length of the source text processed by the last call to + * ubidi_setPara(). This length may be different from the length + * of the source text if option #UBIDI_OPTION_STREAMING + * has been set. + *
+ * Note that whenever the length of the text affects the execution or the + * result of a function, it is the processed length which must be considered, + * except for ubidi_setPara (which receives unprocessed source + * text) and ubidi_getLength (which returns the original length + * of the source text).
+ * In particular, the processed length is the one to consider in the following + * cases: + *
    + *
  • maximum value of the limit argument of + * ubidi_setLine
  • + *
  • maximum value of the charIndex argument of + * ubidi_getParagraph
  • + *
  • maximum value of the charIndex argument of + * ubidi_getLevelAt
  • + *
  • number of elements in the array returned by ubidi_getLevels
  • + *
  • maximum value of the logicalStart argument of + * ubidi_getLogicalRun
  • + *
  • maximum value of the logicalIndex argument of + * ubidi_getVisualIndex
  • + *
  • number of elements filled in the *indexMap argument of + * ubidi_getLogicalMap
  • + *
  • length of text processed by ubidi_writeReordered
  • + *
+ * + * @param pBiDi is the paragraph UBiDi object. + * + * @return The length of the part of the source text processed by + * the last call to ubidi_setPara. + * @see ubidi_setPara + * @see UBIDI_OPTION_STREAMING + * @stable ICU 3.6 + */ +U_CAPI int32_t U_EXPORT2 +ubidi_getProcessedLength(const UBiDi *pBiDi); + +/** + * Get the length of the reordered text resulting from the last call to + * ubidi_setPara(). This length may be different from the length + * of the source text if option #UBIDI_OPTION_INSERT_MARKS + * or option #UBIDI_OPTION_REMOVE_CONTROLS has been set. + *
+ * This resulting length is the one to consider in the following cases: + *
    + *
  • maximum value of the visualIndex argument of + * ubidi_getLogicalIndex
  • + *
  • number of elements of the *indexMap argument of + * ubidi_getVisualMap
  • + *
+ * Note that this length stays identical to the source text length if + * Bidi marks are inserted or removed using option bits of + * ubidi_writeReordered, or if option + * #UBIDI_REORDER_INVERSE_NUMBERS_AS_L has been set. + * + * @param pBiDi is the paragraph UBiDi object. + * + * @return The length of the reordered text resulting from + * the last call to ubidi_setPara. + * @see ubidi_setPara + * @see UBIDI_OPTION_INSERT_MARKS + * @see UBIDI_OPTION_REMOVE_CONTROLS + * @stable ICU 3.6 + */ +U_CAPI int32_t U_EXPORT2 +ubidi_getResultLength(const UBiDi *pBiDi); + +U_CDECL_BEGIN + +#ifndef U_HIDE_DEPRECATED_API +/** + * Value returned by UBiDiClassCallback callbacks when + * there is no need to override the standard Bidi class for a given code point. + * + * This constant is deprecated; use u_getIntPropertyMaxValue(UCHAR_BIDI_CLASS)+1 instead. + * + * @see UBiDiClassCallback + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ +#define U_BIDI_CLASS_DEFAULT U_CHAR_DIRECTION_COUNT +#endif // U_HIDE_DEPRECATED_API + +/** + * Callback type declaration for overriding default Bidi class values with + * custom ones. + *

Usually, the function pointer will be propagated to a UBiDi + * object by calling the ubidi_setClassCallback() function; + * then the callback will be invoked by the UBA implementation any time the + * class of a character is to be determined.

+ * + * @param context is a pointer to the callback private data. + * + * @param c is the code point to get a Bidi class for. + * + * @return The directional property / Bidi class for the given code point + * c if the default class has been overridden, or + * u_getIntPropertyMaxValue(UCHAR_BIDI_CLASS)+1 + * if the standard Bidi class value for c is to be used. + * @see ubidi_setClassCallback + * @see ubidi_getClassCallback + * @stable ICU 3.6 + */ +typedef UCharDirection U_CALLCONV +UBiDiClassCallback(const void *context, UChar32 c); + +U_CDECL_END + +/** + * Retrieve the Bidi class for a given code point. + *

If a #UBiDiClassCallback callback is defined and returns a + * value other than u_getIntPropertyMaxValue(UCHAR_BIDI_CLASS)+1, + * that value is used; otherwise the default class determination mechanism is invoked.

+ * + * @param pBiDi is the paragraph UBiDi object. + * + * @param c is the code point whose Bidi class must be retrieved. + * + * @return The Bidi class for character c based + * on the given pBiDi instance. + * @see UBiDiClassCallback + * @stable ICU 3.6 + */ +U_CAPI UCharDirection U_EXPORT2 +ubidi_getCustomizedClass(UBiDi *pBiDi, UChar32 c); + +/** + * Set the callback function and callback data used by the UBA + * implementation for Bidi class determination. + *

This may be useful for assigning Bidi classes to PUA characters, or + * for special application needs. For instance, an application may want to + * handle all spaces like L or R characters (according to the base direction) + * when creating the visual ordering of logical lines which are part of a report + * organized in columns: there should not be interaction between adjacent + * cells.

+ * + * @param pBiDi is the paragraph UBiDi object. + * + * @param newFn is the new callback function pointer. + * + * @param newContext is the new callback context pointer. This can be NULL. + * + * @param oldFn fillin: Returns the old callback function pointer. This can be + * NULL. + * + * @param oldContext fillin: Returns the old callback's context. This can be + * NULL. + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @see ubidi_getClassCallback + * @stable ICU 3.6 + */ +U_CAPI void U_EXPORT2 +ubidi_setClassCallback(UBiDi *pBiDi, UBiDiClassCallback *newFn, + const void *newContext, UBiDiClassCallback **oldFn, + const void **oldContext, UErrorCode *pErrorCode); + +/** + * Get the current callback function used for Bidi class determination. + * + * @param pBiDi is the paragraph UBiDi object. + * + * @param fn fillin: Returns the callback function pointer. + * + * @param context fillin: Returns the callback's private context. + * + * @see ubidi_setClassCallback + * @stable ICU 3.6 + */ +U_CAPI void U_EXPORT2 +ubidi_getClassCallback(UBiDi *pBiDi, UBiDiClassCallback **fn, const void **context); + +/** + * Take a UBiDi object containing the reordering + * information for a piece of text (one or more paragraphs) set by + * ubidi_setPara() or for a line of text set by + * ubidi_setLine() and write a reordered string to the + * destination buffer. + * + * This function preserves the integrity of characters with multiple + * code units and (optionally) combining characters. + * Characters in RTL runs can be replaced by mirror-image characters + * in the destination buffer. Note that "real" mirroring has + * to be done in a rendering engine by glyph selection + * and that for many "mirrored" characters there are no + * Unicode characters as mirror-image equivalents. + * There are also options to insert or remove Bidi control + * characters; see the description of the destSize + * and options parameters and of the option bit flags. + * + * @param pBiDi A pointer to a UBiDi object that + * is set by ubidi_setPara() or + * ubidi_setLine() and contains the reordering + * information for the text that it was defined for, + * as well as a pointer to that text.

+ * The text was aliased (only the pointer was stored + * without copying the contents) and must not have been modified + * since the ubidi_setPara() call. + * + * @param dest A pointer to where the reordered text is to be copied. + * The source text and dest[destSize] + * must not overlap. + * + * @param destSize The size of the dest buffer, + * in number of UChars. + * If the UBIDI_INSERT_LRM_FOR_NUMERIC + * option is set, then the destination length could be + * as large as + * ubidi_getLength(pBiDi)+2*ubidi_countRuns(pBiDi). + * If the UBIDI_REMOVE_BIDI_CONTROLS option + * is set, then the destination length may be less than + * ubidi_getLength(pBiDi). + * If none of these options is set, then the destination length + * will be exactly ubidi_getProcessedLength(pBiDi). + * + * @param options A bit set of options for the reordering that control + * how the reordered text is written. + * The options include mirroring the characters on a code + * point basis and inserting LRM characters, which is used + * especially for transforming visually stored text + * to logically stored text (although this is still an + * imperfect implementation of an "inverse Bidi" algorithm + * because it uses the "forward Bidi" algorithm at its core). + * The available options are: + * #UBIDI_DO_MIRRORING, + * #UBIDI_INSERT_LRM_FOR_NUMERIC, + * #UBIDI_KEEP_BASE_COMBINING, + * #UBIDI_OUTPUT_REVERSE, + * #UBIDI_REMOVE_BIDI_CONTROLS + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @return The length of the output string. + * + * @see ubidi_getProcessedLength + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ubidi_writeReordered(UBiDi *pBiDi, + UChar *dest, int32_t destSize, + uint16_t options, + UErrorCode *pErrorCode); + +/** + * Reverse a Right-To-Left run of Unicode text. + * + * This function preserves the integrity of characters with multiple + * code units and (optionally) combining characters. + * Characters can be replaced by mirror-image characters + * in the destination buffer. Note that "real" mirroring has + * to be done in a rendering engine by glyph selection + * and that for many "mirrored" characters there are no + * Unicode characters as mirror-image equivalents. + * There are also options to insert or remove Bidi control + * characters. + * + * This function is the implementation for reversing RTL runs as part + * of ubidi_writeReordered(). For detailed descriptions + * of the parameters, see there. + * Since no Bidi controls are inserted here, the output string length + * will never exceed srcLength. + * + * @see ubidi_writeReordered + * + * @param src A pointer to the RTL run text. + * + * @param srcLength The length of the RTL run. + * + * @param dest A pointer to where the reordered text is to be copied. + * src[srcLength] and dest[destSize] + * must not overlap. + * + * @param destSize The size of the dest buffer, + * in number of UChars. + * If the UBIDI_REMOVE_BIDI_CONTROLS option + * is set, then the destination length may be less than + * srcLength. + * If this option is not set, then the destination length + * will be exactly srcLength. + * + * @param options A bit set of options for the reordering that control + * how the reordered text is written. + * See the options parameter in ubidi_writeReordered(). + * + * @param pErrorCode must be a valid pointer to an error code value. + * + * @return The length of the output string. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ubidi_writeReverse(const UChar *src, int32_t srcLength, + UChar *dest, int32_t destSize, + uint16_t options, + UErrorCode *pErrorCode); + +/*#define BIDI_SAMPLE_CODE*/ +/*@}*/ + +#endif diff --git a/deps/icu-small/source/common/unicode/ubiditransform.h b/deps/icu-small/source/common/unicode/ubiditransform.h index 24433aa8aca0fc..f57095ccf3d27d 100644 --- a/deps/icu-small/source/common/unicode/ubiditransform.h +++ b/deps/icu-small/source/common/unicode/ubiditransform.h @@ -1,326 +1,326 @@ -/* -****************************************************************************** -* -* © 2016 and later: Unicode, Inc. and others. -* License & terms of use: http://www.unicode.org/copyright.html -* -****************************************************************************** -* file name: ubiditransform.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2016jul24 -* created by: Lina Kemmel -* -*/ - -#ifndef UBIDITRANSFORM_H -#define UBIDITRANSFORM_H - -#include "unicode/utypes.h" -#include "unicode/ubidi.h" -#include "unicode/uchar.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -/** - * \file - * \brief C API: Bidi Transformations - */ - -/** - * `UBiDiOrder` indicates the order of text. - * - * This bidi transformation engine supports all possible combinations (4 in - * total) of input and output text order: - * - * - : unless the output direction is RTL, this - * corresponds to a normal operation of the Bidi algorithm as described in the - * Unicode Technical Report and implemented by `UBiDi` when the - * reordering mode is set to `UBIDI_REORDER_DEFAULT`. Visual RTL - * mode is not supported by `UBiDi` and is accomplished through - * reversing a visual LTR string, - * - * - : unless the input direction is RTL, this - * corresponds to an "inverse bidi algorithm" in `UBiDi` with the - * reordering mode set to `UBIDI_REORDER_INVERSE_LIKE_DIRECT`. - * Visual RTL mode is not not supported by `UBiDi` and is - * accomplished through reversing a visual LTR string, - * - * - : if the input and output base directions - * mismatch, this corresponds to the `UBiDi` implementation with the - * reordering mode set to `UBIDI_REORDER_RUNS_ONLY`; and if the - * input and output base directions are identical, the transformation engine - * will only handle character mirroring and Arabic shaping operations without - * reordering, - * - * - : this reordering mode is not supported by - * the `UBiDi` engine; it implies character mirroring, Arabic - * shaping, and - if the input/output base directions mismatch - string - * reverse operations. - * @see ubidi_setInverse - * @see ubidi_setReorderingMode - * @see UBIDI_REORDER_DEFAULT - * @see UBIDI_REORDER_INVERSE_LIKE_DIRECT - * @see UBIDI_REORDER_RUNS_ONLY - * @stable ICU 58 - */ -typedef enum { - /** 0: Constant indicating a logical order. - * This is the default for input text. - * @stable ICU 58 - */ - UBIDI_LOGICAL = 0, - /** 1: Constant indicating a visual order. - * This is a default for output text. - * @stable ICU 58 - */ - UBIDI_VISUAL -} UBiDiOrder; - -/** - * UBiDiMirroring indicates whether or not characters with the - * "mirrored" property in RTL runs should be replaced with their mirror-image - * counterparts. - * @see UBIDI_DO_MIRRORING - * @see ubidi_setReorderingOptions - * @see ubidi_writeReordered - * @see ubidi_writeReverse - * @stable ICU 58 - */ -typedef enum { - /** 0: Constant indicating that character mirroring should not be - * performed. - * This is the default. - * @stable ICU 58 - */ - UBIDI_MIRRORING_OFF = 0, - /** 1: Constant indicating that character mirroring should be performed. - * This corresponds to calling ubidi_writeReordered or - * ubidi_writeReverse with the - * UBIDI_DO_MIRRORING option bit set. - * @stable ICU 58 - */ - UBIDI_MIRRORING_ON -} UBiDiMirroring; - -/** - * Forward declaration of the UBiDiTransform structure that stores - * information used by the layout transformation engine. - * @stable ICU 58 - */ -typedef struct UBiDiTransform UBiDiTransform; - -/** - * Performs transformation of text from the bidi layout defined by the input - * ordering scheme to the bidi layout defined by the output ordering scheme, - * and applies character mirroring and Arabic shaping operations.

- * In terms of UBiDi, such a transformation implies: - *

    - *
  • calling ubidi_setReorderingMode as needed (when the - * reordering mode is other than normal),
  • - *
  • calling ubidi_setInverse as needed (when text should be - * transformed from a visual to a logical form),
  • - *
  • resolving embedding levels of each character in the input text by - * calling ubidi_setPara,
  • - *
  • reordering the characters based on the computed embedding levels, also - * performing character mirroring as needed, and streaming the result to the - * output, by calling ubidi_writeReordered,
  • - *
  • performing Arabic digit and letter shaping on the output text by calling - * u_shapeArabic.
  • - *
- * An "ordering scheme" encompasses the base direction and the order of text, - * and these characteristics must be defined by the caller for both input and - * output explicitly .

- * There are 36 possible combinations of ordering schemes, - * which are partially supported by UBiDi already. Examples of the - * currently supported combinations: - *

    - *
  • : this is equivalent to calling - * ubidi_setPara with paraLevel == UBIDI_LTR,
  • - *
  • : this is equivalent to calling - * ubidi_setPara with paraLevel == UBIDI_RTL,
  • - *
  • : this is equivalent to - * calling ubidi_setPara with - * paraLevel == UBIDI_DEFAULT_LTR,
  • - *
  • : this is equivalent to - * calling ubidi_setPara with - * paraLevel == UBIDI_DEFAULT_RTL,
  • - *
  • : this is equivalent to - * calling ubidi_setInverse(UBiDi*, true) and then - * ubidi_setPara with paraLevel == UBIDI_LTR,
  • - *
  • : this is equivalent to - * calling ubidi_setInverse(UBiDi*, true) and then - * ubidi_setPara with paraLevel == UBIDI_RTL.
  • - *
- * All combinations that involve the Visual RTL scheme are unsupported by - * UBiDi, for instance: - *
    - *
  • ,
  • - *
  • .
  • - *
- *

Example of usage of the transformation engine:
- *

- * \code
- * UChar text1[] = {'a', 'b', 'c', 0x0625, '1', 0};
- * UChar text2[] = {'a', 'b', 'c', 0x0625, '1', 0};
- * UErrorCode errorCode = U_ZERO_ERROR;
- * // Run a transformation.
- * ubiditransform_transform(pBidiTransform,
- *          text1, -1, text2, -1,
- *          UBIDI_LTR, UBIDI_VISUAL,
- *          UBIDI_RTL, UBIDI_LOGICAL,
- *          UBIDI_MIRRORING_OFF,
- *          U_SHAPE_DIGITS_AN2EN | U_SHAPE_DIGIT_TYPE_AN_EXTENDED,
- *          &errorCode);
- * // Do something with text2.
- *  text2[4] = '2';
- * // Run a reverse transformation.
- * ubiditransform_transform(pBidiTransform,
- *          text2, -1, text1, -1,
- *          UBIDI_RTL, UBIDI_LOGICAL,
- *          UBIDI_LTR, UBIDI_VISUAL,
- *          UBIDI_MIRRORING_OFF,
- *          U_SHAPE_DIGITS_EN2AN | U_SHAPE_DIGIT_TYPE_AN_EXTENDED,
- *          &errorCode);
- *\endcode
- * 
- *

- * - * @param pBiDiTransform A pointer to a UBiDiTransform object - * allocated with ubiditransform_open() or - * NULL.

- * This object serves for one-time setup to amortize initialization - * overheads. Use of this object is not thread-safe. All other threads - * should allocate a new UBiDiTransform object by calling - * ubiditransform_open() before using it. Alternatively, - * a caller can set this parameter to NULL, in which case - * the object will be allocated by the engine on the fly.

- * @param src A pointer to the text that the Bidi layout transformations will - * be performed on. - *

Note: the text must be (at least) - * srcLength long.

- * @param srcLength The length of the text, in number of UChars. If - * length == -1 then the text must be zero-terminated. - * @param dest A pointer to where the processed text is to be copied. - * @param destSize The size of the dest buffer, in number of - * UChars. If the U_SHAPE_LETTERS_UNSHAPE option is set, - * then the destination length could be as large as - * srcLength * 2. Otherwise, the destination length will - * not exceed srcLength. If the caller reserves the last - * position for zero-termination, it should be excluded from - * destSize. - *

destSize == -1 is allowed and makes sense when - * dest was holds some meaningful value, e.g. that of - * src. In this case dest must be - * zero-terminated.

- * @param inParaLevel A base embedding level of the input as defined in - * ubidi_setPara documentation for the - * paraLevel parameter. - * @param inOrder An order of the input, which can be one of the - * UBiDiOrder values. - * @param outParaLevel A base embedding level of the output as defined in - * ubidi_setPara documentation for the - * paraLevel parameter. - * @param outOrder An order of the output, which can be one of the - * UBiDiOrder values. - * @param doMirroring Indicates whether or not to perform character mirroring, - * and can accept one of the UBiDiMirroring values. - * @param shapingOptions Arabic digit and letter shaping options defined in the - * ushape.h documentation. - *

Note: Direction indicator options are computed by - * the transformation engine based on the effective ordering schemes, so - * user-defined direction indicators will be ignored.

- * @param pErrorCode A pointer to an error code value. - * - * @return The destination length, i.e. the number of UChars written to - * dest. If the transformation fails, the return value - * will be 0 (and the error code will be written to - * pErrorCode). - * - * @see UBiDiLevel - * @see UBiDiOrder - * @see UBiDiMirroring - * @see ubidi_setPara - * @see u_shapeArabic - * @stable ICU 58 - */ -U_CAPI uint32_t U_EXPORT2 -ubiditransform_transform(UBiDiTransform *pBiDiTransform, - const UChar *src, int32_t srcLength, - UChar *dest, int32_t destSize, - UBiDiLevel inParaLevel, UBiDiOrder inOrder, - UBiDiLevel outParaLevel, UBiDiOrder outOrder, - UBiDiMirroring doMirroring, uint32_t shapingOptions, - UErrorCode *pErrorCode); - -/** - * Allocates a UBiDiTransform object. This object can be reused, - * e.g. with different ordering schemes, mirroring or shaping options.

- * Note:The object can only be reused in the same thread. - * All other threads should allocate a new UBiDiTransform object - * before using it.

- * Example of usage:

- *

- * \code
- * UErrorCode errorCode = U_ZERO_ERROR;
- * // Open a new UBiDiTransform.
- * UBiDiTransform* transform = ubiditransform_open(&errorCode);
- * // Run a transformation.
- * ubiditransform_transform(transform,
- *          text1, -1, text2, -1,
- *          UBIDI_RTL, UBIDI_LOGICAL,
- *          UBIDI_LTR, UBIDI_VISUAL,
- *          UBIDI_MIRRORING_ON,
- *          U_SHAPE_DIGITS_EN2AN,
- *          &errorCode);
- * // Do something with the output text and invoke another transformation using
- * //   that text as input.
- * ubiditransform_transform(transform,
- *          text2, -1, text3, -1,
- *          UBIDI_LTR, UBIDI_VISUAL,
- *          UBIDI_RTL, UBIDI_VISUAL,
- *          UBIDI_MIRRORING_ON,
- *          0, &errorCode);
- *\endcode
- * 
- *

- * The UBiDiTransform object must be deallocated by calling - * ubiditransform_close(). - * - * @return An empty UBiDiTransform object. - * @stable ICU 58 - */ -U_CAPI UBiDiTransform* U_EXPORT2 -ubiditransform_open(UErrorCode *pErrorCode); - -/** - * Deallocates the given UBiDiTransform object. - * @stable ICU 58 - */ -U_CAPI void U_EXPORT2 -ubiditransform_close(UBiDiTransform *pBidiTransform); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUBiDiTransformPointer - * "Smart pointer" class, closes a UBiDiTransform via ubiditransform_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 58 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUBiDiTransformPointer, UBiDiTransform, ubiditransform_close); - -U_NAMESPACE_END - -#endif - -#endif +/* +****************************************************************************** +* +* © 2016 and later: Unicode, Inc. and others. +* License & terms of use: http://www.unicode.org/copyright.html +* +****************************************************************************** +* file name: ubiditransform.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2016jul24 +* created by: Lina Kemmel +* +*/ + +#ifndef UBIDITRANSFORM_H +#define UBIDITRANSFORM_H + +#include "unicode/utypes.h" +#include "unicode/ubidi.h" +#include "unicode/uchar.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +/** + * \file + * \brief C API: Bidi Transformations + */ + +/** + * `UBiDiOrder` indicates the order of text. + * + * This bidi transformation engine supports all possible combinations (4 in + * total) of input and output text order: + * + * - : unless the output direction is RTL, this + * corresponds to a normal operation of the Bidi algorithm as described in the + * Unicode Technical Report and implemented by `UBiDi` when the + * reordering mode is set to `UBIDI_REORDER_DEFAULT`. Visual RTL + * mode is not supported by `UBiDi` and is accomplished through + * reversing a visual LTR string, + * + * - : unless the input direction is RTL, this + * corresponds to an "inverse bidi algorithm" in `UBiDi` with the + * reordering mode set to `UBIDI_REORDER_INVERSE_LIKE_DIRECT`. + * Visual RTL mode is not not supported by `UBiDi` and is + * accomplished through reversing a visual LTR string, + * + * - : if the input and output base directions + * mismatch, this corresponds to the `UBiDi` implementation with the + * reordering mode set to `UBIDI_REORDER_RUNS_ONLY`; and if the + * input and output base directions are identical, the transformation engine + * will only handle character mirroring and Arabic shaping operations without + * reordering, + * + * - : this reordering mode is not supported by + * the `UBiDi` engine; it implies character mirroring, Arabic + * shaping, and - if the input/output base directions mismatch - string + * reverse operations. + * @see ubidi_setInverse + * @see ubidi_setReorderingMode + * @see UBIDI_REORDER_DEFAULT + * @see UBIDI_REORDER_INVERSE_LIKE_DIRECT + * @see UBIDI_REORDER_RUNS_ONLY + * @stable ICU 58 + */ +typedef enum { + /** 0: Constant indicating a logical order. + * This is the default for input text. + * @stable ICU 58 + */ + UBIDI_LOGICAL = 0, + /** 1: Constant indicating a visual order. + * This is a default for output text. + * @stable ICU 58 + */ + UBIDI_VISUAL +} UBiDiOrder; + +/** + * UBiDiMirroring indicates whether or not characters with the + * "mirrored" property in RTL runs should be replaced with their mirror-image + * counterparts. + * @see UBIDI_DO_MIRRORING + * @see ubidi_setReorderingOptions + * @see ubidi_writeReordered + * @see ubidi_writeReverse + * @stable ICU 58 + */ +typedef enum { + /** 0: Constant indicating that character mirroring should not be + * performed. + * This is the default. + * @stable ICU 58 + */ + UBIDI_MIRRORING_OFF = 0, + /** 1: Constant indicating that character mirroring should be performed. + * This corresponds to calling ubidi_writeReordered or + * ubidi_writeReverse with the + * UBIDI_DO_MIRRORING option bit set. + * @stable ICU 58 + */ + UBIDI_MIRRORING_ON +} UBiDiMirroring; + +/** + * Forward declaration of the UBiDiTransform structure that stores + * information used by the layout transformation engine. + * @stable ICU 58 + */ +typedef struct UBiDiTransform UBiDiTransform; + +/** + * Performs transformation of text from the bidi layout defined by the input + * ordering scheme to the bidi layout defined by the output ordering scheme, + * and applies character mirroring and Arabic shaping operations.

+ * In terms of UBiDi, such a transformation implies: + *

    + *
  • calling ubidi_setReorderingMode as needed (when the + * reordering mode is other than normal),
  • + *
  • calling ubidi_setInverse as needed (when text should be + * transformed from a visual to a logical form),
  • + *
  • resolving embedding levels of each character in the input text by + * calling ubidi_setPara,
  • + *
  • reordering the characters based on the computed embedding levels, also + * performing character mirroring as needed, and streaming the result to the + * output, by calling ubidi_writeReordered,
  • + *
  • performing Arabic digit and letter shaping on the output text by calling + * u_shapeArabic.
  • + *
+ * An "ordering scheme" encompasses the base direction and the order of text, + * and these characteristics must be defined by the caller for both input and + * output explicitly .

+ * There are 36 possible combinations of ordering schemes, + * which are partially supported by UBiDi already. Examples of the + * currently supported combinations: + *

    + *
  • : this is equivalent to calling + * ubidi_setPara with paraLevel == UBIDI_LTR,
  • + *
  • : this is equivalent to calling + * ubidi_setPara with paraLevel == UBIDI_RTL,
  • + *
  • : this is equivalent to + * calling ubidi_setPara with + * paraLevel == UBIDI_DEFAULT_LTR,
  • + *
  • : this is equivalent to + * calling ubidi_setPara with + * paraLevel == UBIDI_DEFAULT_RTL,
  • + *
  • : this is equivalent to + * calling ubidi_setInverse(UBiDi*, true) and then + * ubidi_setPara with paraLevel == UBIDI_LTR,
  • + *
  • : this is equivalent to + * calling ubidi_setInverse(UBiDi*, true) and then + * ubidi_setPara with paraLevel == UBIDI_RTL.
  • + *
+ * All combinations that involve the Visual RTL scheme are unsupported by + * UBiDi, for instance: + *
    + *
  • ,
  • + *
  • .
  • + *
+ *

Example of usage of the transformation engine:
+ *

+ * \code
+ * UChar text1[] = {'a', 'b', 'c', 0x0625, '1', 0};
+ * UChar text2[] = {'a', 'b', 'c', 0x0625, '1', 0};
+ * UErrorCode errorCode = U_ZERO_ERROR;
+ * // Run a transformation.
+ * ubiditransform_transform(pBidiTransform,
+ *          text1, -1, text2, -1,
+ *          UBIDI_LTR, UBIDI_VISUAL,
+ *          UBIDI_RTL, UBIDI_LOGICAL,
+ *          UBIDI_MIRRORING_OFF,
+ *          U_SHAPE_DIGITS_AN2EN | U_SHAPE_DIGIT_TYPE_AN_EXTENDED,
+ *          &errorCode);
+ * // Do something with text2.
+ *  text2[4] = '2';
+ * // Run a reverse transformation.
+ * ubiditransform_transform(pBidiTransform,
+ *          text2, -1, text1, -1,
+ *          UBIDI_RTL, UBIDI_LOGICAL,
+ *          UBIDI_LTR, UBIDI_VISUAL,
+ *          UBIDI_MIRRORING_OFF,
+ *          U_SHAPE_DIGITS_EN2AN | U_SHAPE_DIGIT_TYPE_AN_EXTENDED,
+ *          &errorCode);
+ *\endcode
+ * 
+ *

+ * + * @param pBiDiTransform A pointer to a UBiDiTransform object + * allocated with ubiditransform_open() or + * NULL.

+ * This object serves for one-time setup to amortize initialization + * overheads. Use of this object is not thread-safe. All other threads + * should allocate a new UBiDiTransform object by calling + * ubiditransform_open() before using it. Alternatively, + * a caller can set this parameter to NULL, in which case + * the object will be allocated by the engine on the fly.

+ * @param src A pointer to the text that the Bidi layout transformations will + * be performed on. + *

Note: the text must be (at least) + * srcLength long.

+ * @param srcLength The length of the text, in number of UChars. If + * length == -1 then the text must be zero-terminated. + * @param dest A pointer to where the processed text is to be copied. + * @param destSize The size of the dest buffer, in number of + * UChars. If the U_SHAPE_LETTERS_UNSHAPE option is set, + * then the destination length could be as large as + * srcLength * 2. Otherwise, the destination length will + * not exceed srcLength. If the caller reserves the last + * position for zero-termination, it should be excluded from + * destSize. + *

destSize == -1 is allowed and makes sense when + * dest was holds some meaningful value, e.g. that of + * src. In this case dest must be + * zero-terminated.

+ * @param inParaLevel A base embedding level of the input as defined in + * ubidi_setPara documentation for the + * paraLevel parameter. + * @param inOrder An order of the input, which can be one of the + * UBiDiOrder values. + * @param outParaLevel A base embedding level of the output as defined in + * ubidi_setPara documentation for the + * paraLevel parameter. + * @param outOrder An order of the output, which can be one of the + * UBiDiOrder values. + * @param doMirroring Indicates whether or not to perform character mirroring, + * and can accept one of the UBiDiMirroring values. + * @param shapingOptions Arabic digit and letter shaping options defined in the + * ushape.h documentation. + *

Note: Direction indicator options are computed by + * the transformation engine based on the effective ordering schemes, so + * user-defined direction indicators will be ignored.

+ * @param pErrorCode A pointer to an error code value. + * + * @return The destination length, i.e. the number of UChars written to + * dest. If the transformation fails, the return value + * will be 0 (and the error code will be written to + * pErrorCode). + * + * @see UBiDiLevel + * @see UBiDiOrder + * @see UBiDiMirroring + * @see ubidi_setPara + * @see u_shapeArabic + * @stable ICU 58 + */ +U_CAPI uint32_t U_EXPORT2 +ubiditransform_transform(UBiDiTransform *pBiDiTransform, + const UChar *src, int32_t srcLength, + UChar *dest, int32_t destSize, + UBiDiLevel inParaLevel, UBiDiOrder inOrder, + UBiDiLevel outParaLevel, UBiDiOrder outOrder, + UBiDiMirroring doMirroring, uint32_t shapingOptions, + UErrorCode *pErrorCode); + +/** + * Allocates a UBiDiTransform object. This object can be reused, + * e.g. with different ordering schemes, mirroring or shaping options.

+ * Note:The object can only be reused in the same thread. + * All other threads should allocate a new UBiDiTransform object + * before using it.

+ * Example of usage:

+ *

+ * \code
+ * UErrorCode errorCode = U_ZERO_ERROR;
+ * // Open a new UBiDiTransform.
+ * UBiDiTransform* transform = ubiditransform_open(&errorCode);
+ * // Run a transformation.
+ * ubiditransform_transform(transform,
+ *          text1, -1, text2, -1,
+ *          UBIDI_RTL, UBIDI_LOGICAL,
+ *          UBIDI_LTR, UBIDI_VISUAL,
+ *          UBIDI_MIRRORING_ON,
+ *          U_SHAPE_DIGITS_EN2AN,
+ *          &errorCode);
+ * // Do something with the output text and invoke another transformation using
+ * //   that text as input.
+ * ubiditransform_transform(transform,
+ *          text2, -1, text3, -1,
+ *          UBIDI_LTR, UBIDI_VISUAL,
+ *          UBIDI_RTL, UBIDI_VISUAL,
+ *          UBIDI_MIRRORING_ON,
+ *          0, &errorCode);
+ *\endcode
+ * 
+ *

+ * The UBiDiTransform object must be deallocated by calling + * ubiditransform_close(). + * + * @return An empty UBiDiTransform object. + * @stable ICU 58 + */ +U_CAPI UBiDiTransform* U_EXPORT2 +ubiditransform_open(UErrorCode *pErrorCode); + +/** + * Deallocates the given UBiDiTransform object. + * @stable ICU 58 + */ +U_CAPI void U_EXPORT2 +ubiditransform_close(UBiDiTransform *pBidiTransform); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUBiDiTransformPointer + * "Smart pointer" class, closes a UBiDiTransform via ubiditransform_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 58 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUBiDiTransformPointer, UBiDiTransform, ubiditransform_close); + +U_NAMESPACE_END + +#endif + +#endif diff --git a/deps/icu-small/source/common/unicode/ubrk.h b/deps/icu-small/source/common/unicode/ubrk.h index 2b3dc7aa576803..412b6a1d868ed7 100644 --- a/deps/icu-small/source/common/unicode/ubrk.h +++ b/deps/icu-small/source/common/unicode/ubrk.h @@ -1,647 +1,647 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 1996-2015, International Business Machines Corporation and others. -* All Rights Reserved. -****************************************************************************** -*/ - -#ifndef UBRK_H -#define UBRK_H - -#include "unicode/utypes.h" -#include "unicode/uloc.h" -#include "unicode/utext.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -/** - * A text-break iterator. - * For usage in C programs. - */ -#ifndef UBRK_TYPEDEF_UBREAK_ITERATOR -# define UBRK_TYPEDEF_UBREAK_ITERATOR - /** - * Opaque type representing an ICU Break iterator object. - * @stable ICU 2.0 - */ - typedef struct UBreakIterator UBreakIterator; -#endif - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/parseerr.h" - -/** - * \file - * \brief C API: BreakIterator - * - *

BreakIterator C API

- * - * The BreakIterator C API defines methods for finding the location - * of boundaries in text. Pointer to a UBreakIterator maintain a - * current position and scan over text returning the index of characters - * where boundaries occur. - *

- * Line boundary analysis determines where a text string can be broken - * when line-wrapping. The mechanism correctly handles punctuation and - * hyphenated words. - *

- * Note: The locale keyword "lb" can be used to modify line break - * behavior according to the CSS level 3 line-break options, see - * . For example: - * "ja@lb=strict", "zh@lb=loose". - *

- * Sentence boundary analysis allows selection with correct - * interpretation of periods within numbers and abbreviations, and - * trailing punctuation marks such as quotation marks and parentheses. - *

- * Note: The locale keyword "ss" can be used to enable use of - * segmentation suppression data (preventing breaks in English after - * abbreviations such as "Mr." or "Est.", for example), as follows: - * "en@ss=standard". - *

- * Word boundary analysis is used by search and replace functions, as - * well as within text editing applications that allow the user to - * select words with a double click. Word selection provides correct - * interpretation of punctuation marks within and following - * words. Characters that are not part of a word, such as symbols or - * punctuation marks, have word-breaks on both sides. - *

- * Character boundary analysis identifies the boundaries of - * "Extended Grapheme Clusters", which are groupings of codepoints - * that should be treated as character-like units for many text operations. - * Please see Unicode Standard Annex #29, Unicode Text Segmentation, - * http://www.unicode.org/reports/tr29/ for additional information - * on grapheme clusters and guidelines on their use. - *

- * Title boundary analysis locates all positions, - * typically starts of words, that should be set to Title Case - * when title casing the text. - *

- * The text boundary positions are found according to the rules - * described in Unicode Standard Annex #29, Text Boundaries, and - * Unicode Standard Annex #14, Line Breaking Properties. These - * are available at http://www.unicode.org/reports/tr14/ and - * http://www.unicode.org/reports/tr29/. - *

- * In addition to the plain C API defined in this header file, an - * object oriented C++ API with equivalent functionality is defined in the - * file brkiter.h. - *

- * Code snippets illustrating the use of the Break Iterator APIs - * are available in the ICU User Guide, - * https://unicode-org.github.io/icu/userguide/boundaryanalysis/ - * and in the sample program icu/source/samples/break/break.cpp - */ - -/** The possible types of text boundaries. @stable ICU 2.0 */ -typedef enum UBreakIteratorType { - /** Character breaks @stable ICU 2.0 */ - UBRK_CHARACTER = 0, - /** Word breaks @stable ICU 2.0 */ - UBRK_WORD = 1, - /** Line breaks @stable ICU 2.0 */ - UBRK_LINE = 2, - /** Sentence breaks @stable ICU 2.0 */ - UBRK_SENTENCE = 3, - -#ifndef U_HIDE_DEPRECATED_API - /** - * Title Case breaks - * The iterator created using this type locates title boundaries as described for - * Unicode 3.2 only. For Unicode 4.0 and above title boundary iteration, - * please use Word Boundary iterator. - * - * @deprecated ICU 2.8 Use the word break iterator for titlecasing for Unicode 4 and later. - */ - UBRK_TITLE = 4, - /** - * One more than the highest normal UBreakIteratorType value. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UBRK_COUNT = 5 -#endif // U_HIDE_DEPRECATED_API -} UBreakIteratorType; - -/** Value indicating all text boundaries have been returned. - * @stable ICU 2.0 - */ -#define UBRK_DONE ((int32_t) -1) - - -/** - * Enum constants for the word break tags returned by - * getRuleStatus(). A range of values is defined for each category of - * word, to allow for further subdivisions of a category in future releases. - * Applications should check for tag values falling within the range, rather - * than for single individual values. - * - * The numeric values of all of these constants are stable (will not change). - * - * @stable ICU 2.2 -*/ -typedef enum UWordBreak { - /** Tag value for "words" that do not fit into any of other categories. - * Includes spaces and most punctuation. */ - UBRK_WORD_NONE = 0, - /** Upper bound for tags for uncategorized words. */ - UBRK_WORD_NONE_LIMIT = 100, - /** Tag value for words that appear to be numbers, lower limit. */ - UBRK_WORD_NUMBER = 100, - /** Tag value for words that appear to be numbers, upper limit. */ - UBRK_WORD_NUMBER_LIMIT = 200, - /** Tag value for words that contain letters, excluding - * hiragana, katakana or ideographic characters, lower limit. */ - UBRK_WORD_LETTER = 200, - /** Tag value for words containing letters, upper limit */ - UBRK_WORD_LETTER_LIMIT = 300, - /** Tag value for words containing kana characters, lower limit */ - UBRK_WORD_KANA = 300, - /** Tag value for words containing kana characters, upper limit */ - UBRK_WORD_KANA_LIMIT = 400, - /** Tag value for words containing ideographic characters, lower limit */ - UBRK_WORD_IDEO = 400, - /** Tag value for words containing ideographic characters, upper limit */ - UBRK_WORD_IDEO_LIMIT = 500 -} UWordBreak; - -/** - * Enum constants for the line break tags returned by getRuleStatus(). - * A range of values is defined for each category of - * word, to allow for further subdivisions of a category in future releases. - * Applications should check for tag values falling within the range, rather - * than for single individual values. - * - * The numeric values of all of these constants are stable (will not change). - * - * @stable ICU 2.8 -*/ -typedef enum ULineBreakTag { - /** Tag value for soft line breaks, positions at which a line break - * is acceptable but not required */ - UBRK_LINE_SOFT = 0, - /** Upper bound for soft line breaks. */ - UBRK_LINE_SOFT_LIMIT = 100, - /** Tag value for a hard, or mandatory line break */ - UBRK_LINE_HARD = 100, - /** Upper bound for hard line breaks. */ - UBRK_LINE_HARD_LIMIT = 200 -} ULineBreakTag; - - - -/** - * Enum constants for the sentence break tags returned by getRuleStatus(). - * A range of values is defined for each category of - * sentence, to allow for further subdivisions of a category in future releases. - * Applications should check for tag values falling within the range, rather - * than for single individual values. - * - * The numeric values of all of these constants are stable (will not change). - * - * @stable ICU 2.8 -*/ -typedef enum USentenceBreakTag { - /** Tag value for for sentences ending with a sentence terminator - * ('.', '?', '!', etc.) character, possibly followed by a - * hard separator (CR, LF, PS, etc.) - */ - UBRK_SENTENCE_TERM = 0, - /** Upper bound for tags for sentences ended by sentence terminators. */ - UBRK_SENTENCE_TERM_LIMIT = 100, - /** Tag value for for sentences that do not contain an ending - * sentence terminator ('.', '?', '!', etc.) character, but - * are ended only by a hard separator (CR, LF, PS, etc.) or end of input. - */ - UBRK_SENTENCE_SEP = 100, - /** Upper bound for tags for sentences ended by a separator. */ - UBRK_SENTENCE_SEP_LIMIT = 200 - /** Tag value for a hard, or mandatory line break */ -} USentenceBreakTag; - - -/** - * Open a new UBreakIterator for locating text boundaries for a specified locale. - * A UBreakIterator may be used for detecting character, line, word, - * and sentence breaks in text. - * @param type The type of UBreakIterator to open: one of UBRK_CHARACTER, UBRK_WORD, - * UBRK_LINE, UBRK_SENTENCE - * @param locale The locale specifying the text-breaking conventions. Note that - * locale keys such as "lb" and "ss" may be used to modify text break behavior, - * see general discussion of BreakIterator C API. - * @param text The text to be iterated over. May be null, in which case ubrk_setText() is - * used to specify the text to be iterated. - * @param textLength The number of characters in text, or -1 if null-terminated. - * @param status A UErrorCode to receive any errors. - * @return A UBreakIterator for the specified locale. - * @see ubrk_openRules - * @stable ICU 2.0 - */ -U_CAPI UBreakIterator* U_EXPORT2 -ubrk_open(UBreakIteratorType type, - const char *locale, - const UChar *text, - int32_t textLength, - UErrorCode *status); - -/** - * Open a new UBreakIterator for locating text boundaries using specified breaking rules. - * The rule syntax is ... (TBD) - * @param rules A set of rules specifying the text breaking conventions. - * @param rulesLength The number of characters in rules, or -1 if null-terminated. - * @param text The text to be iterated over. May be null, in which case ubrk_setText() is - * used to specify the text to be iterated. - * @param textLength The number of characters in text, or -1 if null-terminated. - * @param parseErr Receives position and context information for any syntax errors - * detected while parsing the rules. - * @param status A UErrorCode to receive any errors. - * @return A UBreakIterator for the specified rules. - * @see ubrk_open - * @stable ICU 2.2 - */ -U_CAPI UBreakIterator* U_EXPORT2 -ubrk_openRules(const UChar *rules, - int32_t rulesLength, - const UChar *text, - int32_t textLength, - UParseError *parseErr, - UErrorCode *status); - -/** - * Open a new UBreakIterator for locating text boundaries using precompiled binary rules. - * Opening a UBreakIterator this way is substantially faster than using ubrk_openRules. - * Binary rules may be obtained using ubrk_getBinaryRules. The compiled rules are not - * compatible across different major versions of ICU, nor across platforms of different - * endianness or different base character set family (ASCII vs EBCDIC). - * @param binaryRules A set of compiled binary rules specifying the text breaking - * conventions. Ownership of the storage containing the compiled - * rules remains with the caller of this function. The compiled - * rules must not be modified or deleted during the life of the - * break iterator. - * @param rulesLength The length of binaryRules in bytes; must be >= 0. - * @param text The text to be iterated over. May be null, in which case - * ubrk_setText() is used to specify the text to be iterated. - * @param textLength The number of characters in text, or -1 if null-terminated. - * @param status Pointer to UErrorCode to receive any errors. - * @return UBreakIterator for the specified rules. - * @see ubrk_getBinaryRules - * @stable ICU 59 - */ -U_CAPI UBreakIterator* U_EXPORT2 -ubrk_openBinaryRules(const uint8_t *binaryRules, int32_t rulesLength, - const UChar * text, int32_t textLength, - UErrorCode * status); - -#ifndef U_HIDE_DEPRECATED_API - -/** - * Thread safe cloning operation - * @param bi iterator to be cloned - * @param stackBuffer Deprecated functionality as of ICU 52, use NULL.
- * user allocated space for the new clone. If NULL new memory will be allocated. - * If buffer is not large enough, new memory will be allocated. - * Clients can use the U_BRK_SAFECLONE_BUFFERSIZE. - * @param pBufferSize Deprecated functionality as of ICU 52, use NULL or 1.
- * pointer to size of allocated space. - * If *pBufferSize == 0, a sufficient size for use in cloning will - * be returned ('pre-flighting') - * If *pBufferSize is not enough for a stack-based safe clone, - * new memory will be allocated. - * @param status to indicate whether the operation went on smoothly or there were errors - * An informational status value, U_SAFECLONE_ALLOCATED_ERROR, is used - * if pBufferSize != NULL and any allocations were necessary - * @return pointer to the new clone - * @deprecated ICU 69 Use ubrk_clone() instead. - */ -U_DEPRECATED UBreakIterator * U_EXPORT2 -ubrk_safeClone( - const UBreakIterator *bi, - void *stackBuffer, - int32_t *pBufferSize, - UErrorCode *status); - -#endif /* U_HIDE_DEPRECATED_API */ - -/** - * Thread safe cloning operation. - * @param bi iterator to be cloned - * @param status to indicate whether the operation went on smoothly or there were errors - * @return pointer to the new clone - * @stable ICU 69 - */ -U_CAPI UBreakIterator * U_EXPORT2 -ubrk_clone(const UBreakIterator *bi, - UErrorCode *status); - -#ifndef U_HIDE_DEPRECATED_API - -/** - * A recommended size (in bytes) for the memory buffer to be passed to ubrk_saveClone(). - * @deprecated ICU 52. Do not rely on ubrk_safeClone() cloning into any provided buffer. - */ -#define U_BRK_SAFECLONE_BUFFERSIZE 1 - -#endif /* U_HIDE_DEPRECATED_API */ - -/** -* Close a UBreakIterator. -* Once closed, a UBreakIterator may no longer be used. -* @param bi The break iterator to close. - * @stable ICU 2.0 -*/ -U_CAPI void U_EXPORT2 -ubrk_close(UBreakIterator *bi); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUBreakIteratorPointer - * "Smart pointer" class, closes a UBreakIterator via ubrk_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUBreakIteratorPointer, UBreakIterator, ubrk_close); - -U_NAMESPACE_END - -#endif - -/** - * Sets an existing iterator to point to a new piece of text. - * The break iterator retains a pointer to the supplied text. - * The caller must not modify or delete the text while the BreakIterator - * retains the reference. - * - * @param bi The iterator to use - * @param text The text to be set - * @param textLength The length of the text - * @param status The error code - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ubrk_setText(UBreakIterator* bi, - const UChar* text, - int32_t textLength, - UErrorCode* status); - - -/** - * Sets an existing iterator to point to a new piece of text. - * - * All index positions returned by break iterator functions are - * native indices from the UText. For example, when breaking UTF-8 - * encoded text, the break positions returned by \ref ubrk_next, \ref ubrk_previous, etc. - * will be UTF-8 string indices, not UTF-16 positions. - * - * @param bi The iterator to use - * @param text The text to be set. - * This function makes a shallow clone of the supplied UText. This means - * that the caller is free to immediately close or otherwise reuse the - * UText that was passed as a parameter, but that the underlying text itself - * must not be altered while being referenced by the break iterator. - * @param status The error code - * @stable ICU 3.4 - */ -U_CAPI void U_EXPORT2 -ubrk_setUText(UBreakIterator* bi, - UText* text, - UErrorCode* status); - - - -/** - * Determine the most recently-returned text boundary. - * - * @param bi The break iterator to use. - * @return The character index most recently returned by \ref ubrk_next, \ref ubrk_previous, - * \ref ubrk_first, or \ref ubrk_last. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ubrk_current(const UBreakIterator *bi); - -/** - * Advance the iterator to the boundary following the current boundary. - * - * @param bi The break iterator to use. - * @return The character index of the next text boundary, or UBRK_DONE - * if all text boundaries have been returned. - * @see ubrk_previous - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ubrk_next(UBreakIterator *bi); - -/** - * Set the iterator position to the boundary preceding the current boundary. - * - * @param bi The break iterator to use. - * @return The character index of the preceding text boundary, or UBRK_DONE - * if all text boundaries have been returned. - * @see ubrk_next - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ubrk_previous(UBreakIterator *bi); - -/** - * Set the iterator position to zero, the start of the text being scanned. - * @param bi The break iterator to use. - * @return The new iterator position (zero). - * @see ubrk_last - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ubrk_first(UBreakIterator *bi); - -/** - * Set the iterator position to the index immediately beyond the last character in the text being scanned. - * This is not the same as the last character. - * @param bi The break iterator to use. - * @return The character offset immediately beyond the last character in the - * text being scanned. - * @see ubrk_first - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ubrk_last(UBreakIterator *bi); - -/** - * Set the iterator position to the first boundary preceding the specified offset. - * The new position is always smaller than offset, or UBRK_DONE. - * @param bi The break iterator to use. - * @param offset The offset to begin scanning. - * @return The text boundary preceding offset, or UBRK_DONE. - * @see ubrk_following - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ubrk_preceding(UBreakIterator *bi, - int32_t offset); - -/** - * Advance the iterator to the first boundary following the specified offset. - * The value returned is always greater than offset, or UBRK_DONE. - * @param bi The break iterator to use. - * @param offset The offset to begin scanning. - * @return The text boundary following offset, or UBRK_DONE. - * @see ubrk_preceding - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ubrk_following(UBreakIterator *bi, - int32_t offset); - -/** -* Get a locale for which text breaking information is available. -* A UBreakIterator in a locale returned by this function will perform the correct -* text breaking for the locale. -* @param index The index of the desired locale. -* @return A locale for which number text breaking information is available, or 0 if none. -* @see ubrk_countAvailable -* @stable ICU 2.0 -*/ -U_CAPI const char* U_EXPORT2 -ubrk_getAvailable(int32_t index); - -/** -* Determine how many locales have text breaking information available. -* This function is most useful as determining the loop ending condition for -* calls to \ref ubrk_getAvailable. -* @return The number of locales for which text breaking information is available. -* @see ubrk_getAvailable -* @stable ICU 2.0 -*/ -U_CAPI int32_t U_EXPORT2 -ubrk_countAvailable(void); - - -/** -* Returns true if the specified position is a boundary position. As a side -* effect, leaves the iterator pointing to the first boundary position at -* or after "offset". -* @param bi The break iterator to use. -* @param offset the offset to check. -* @return True if "offset" is a boundary position. -* @stable ICU 2.0 -*/ -U_CAPI UBool U_EXPORT2 -ubrk_isBoundary(UBreakIterator *bi, int32_t offset); - -/** - * Return the status from the break rule that determined the most recently - * returned break position. The values appear in the rule source - * within brackets, {123}, for example. For rules that do not specify a - * status, a default value of 0 is returned. - *

- * For word break iterators, the possible values are defined in enum UWordBreak. - * @stable ICU 2.2 - */ -U_CAPI int32_t U_EXPORT2 -ubrk_getRuleStatus(UBreakIterator *bi); - -/** - * Get the statuses from the break rules that determined the most recently - * returned break position. The values appear in the rule source - * within brackets, {123}, for example. The default status value for rules - * that do not explicitly provide one is zero. - *

- * For word break iterators, the possible values are defined in enum UWordBreak. - * @param bi The break iterator to use - * @param fillInVec an array to be filled in with the status values. - * @param capacity the length of the supplied vector. A length of zero causes - * the function to return the number of status values, in the - * normal way, without attempting to store any values. - * @param status receives error codes. - * @return The number of rule status values from rules that determined - * the most recent boundary returned by the break iterator. - * @stable ICU 3.0 - */ -U_CAPI int32_t U_EXPORT2 -ubrk_getRuleStatusVec(UBreakIterator *bi, int32_t *fillInVec, int32_t capacity, UErrorCode *status); - -/** - * Return the locale of the break iterator. You can choose between the valid and - * the actual locale. - * @param bi break iterator - * @param type locale type (valid or actual) - * @param status error code - * @return locale string - * @stable ICU 2.8 - */ -U_CAPI const char* U_EXPORT2 -ubrk_getLocaleByType(const UBreakIterator *bi, ULocDataLocaleType type, UErrorCode* status); - -/** - * Set the subject text string upon which the break iterator is operating - * without changing any other aspect of the state. - * The new and previous text strings must have the same content. - * - * This function is intended for use in environments where ICU is operating on - * strings that may move around in memory. It provides a mechanism for notifying - * ICU that the string has been relocated, and providing a new UText to access the - * string in its new position. - * - * Note that the break iterator never copies the underlying text - * of a string being processed, but always operates directly on the original text - * provided by the user. Refreshing simply drops the references to the old text - * and replaces them with references to the new. - * - * Caution: this function is normally used only by very specialized - * system-level code. One example use case is with garbage collection - * that moves the text in memory. - * - * @param bi The break iterator. - * @param text The new (moved) text string. - * @param status Receives errors detected by this function. - * - * @stable ICU 49 - */ -U_CAPI void U_EXPORT2 -ubrk_refreshUText(UBreakIterator *bi, - UText *text, - UErrorCode *status); - - -/** - * Get a compiled binary version of the rules specifying the behavior of a UBreakIterator. - * The binary rules may be used with ubrk_openBinaryRules to open a new UBreakIterator - * more quickly than using ubrk_openRules. The compiled rules are not compatible across - * different major versions of ICU, nor across platforms of different endianness or - * different base character set family (ASCII vs EBCDIC). Supports preflighting (with - * binaryRules=NULL and rulesCapacity=0) to get the rules length without copying them to - * the binaryRules buffer. However, whether preflighting or not, if the actual length - * is greater than INT32_MAX, then the function returns 0 and sets *status to - * U_INDEX_OUTOFBOUNDS_ERROR. - - * @param bi The break iterator to use. - * @param binaryRules Buffer to receive the compiled binary rules; set to NULL for - * preflighting. - * @param rulesCapacity Capacity (in bytes) of the binaryRules buffer; set to 0 for - * preflighting. Must be >= 0. - * @param status Pointer to UErrorCode to receive any errors, such as - * U_BUFFER_OVERFLOW_ERROR, U_INDEX_OUTOFBOUNDS_ERROR, or - * U_ILLEGAL_ARGUMENT_ERROR. - * @return The actual byte length of the binary rules, if <= INT32_MAX; - * otherwise 0. If not preflighting and this is larger than - * rulesCapacity, *status will be set to an error. - * @see ubrk_openBinaryRules - * @stable ICU 59 - */ -U_CAPI int32_t U_EXPORT2 -ubrk_getBinaryRules(UBreakIterator *bi, - uint8_t * binaryRules, int32_t rulesCapacity, - UErrorCode * status); - -#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 1996-2015, International Business Machines Corporation and others. +* All Rights Reserved. +****************************************************************************** +*/ + +#ifndef UBRK_H +#define UBRK_H + +#include "unicode/utypes.h" +#include "unicode/uloc.h" +#include "unicode/utext.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +/** + * A text-break iterator. + * For usage in C programs. + */ +#ifndef UBRK_TYPEDEF_UBREAK_ITERATOR +# define UBRK_TYPEDEF_UBREAK_ITERATOR + /** + * Opaque type representing an ICU Break iterator object. + * @stable ICU 2.0 + */ + typedef struct UBreakIterator UBreakIterator; +#endif + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/parseerr.h" + +/** + * \file + * \brief C API: BreakIterator + * + *

BreakIterator C API

+ * + * The BreakIterator C API defines methods for finding the location + * of boundaries in text. Pointer to a UBreakIterator maintain a + * current position and scan over text returning the index of characters + * where boundaries occur. + *

+ * Line boundary analysis determines where a text string can be broken + * when line-wrapping. The mechanism correctly handles punctuation and + * hyphenated words. + *

+ * Note: The locale keyword "lb" can be used to modify line break + * behavior according to the CSS level 3 line-break options, see + * . For example: + * "ja@lb=strict", "zh@lb=loose". + *

+ * Sentence boundary analysis allows selection with correct + * interpretation of periods within numbers and abbreviations, and + * trailing punctuation marks such as quotation marks and parentheses. + *

+ * Note: The locale keyword "ss" can be used to enable use of + * segmentation suppression data (preventing breaks in English after + * abbreviations such as "Mr." or "Est.", for example), as follows: + * "en@ss=standard". + *

+ * Word boundary analysis is used by search and replace functions, as + * well as within text editing applications that allow the user to + * select words with a double click. Word selection provides correct + * interpretation of punctuation marks within and following + * words. Characters that are not part of a word, such as symbols or + * punctuation marks, have word-breaks on both sides. + *

+ * Character boundary analysis identifies the boundaries of + * "Extended Grapheme Clusters", which are groupings of codepoints + * that should be treated as character-like units for many text operations. + * Please see Unicode Standard Annex #29, Unicode Text Segmentation, + * http://www.unicode.org/reports/tr29/ for additional information + * on grapheme clusters and guidelines on their use. + *

+ * Title boundary analysis locates all positions, + * typically starts of words, that should be set to Title Case + * when title casing the text. + *

+ * The text boundary positions are found according to the rules + * described in Unicode Standard Annex #29, Text Boundaries, and + * Unicode Standard Annex #14, Line Breaking Properties. These + * are available at http://www.unicode.org/reports/tr14/ and + * http://www.unicode.org/reports/tr29/. + *

+ * In addition to the plain C API defined in this header file, an + * object oriented C++ API with equivalent functionality is defined in the + * file brkiter.h. + *

+ * Code snippets illustrating the use of the Break Iterator APIs + * are available in the ICU User Guide, + * https://unicode-org.github.io/icu/userguide/boundaryanalysis/ + * and in the sample program icu/source/samples/break/break.cpp + */ + +/** The possible types of text boundaries. @stable ICU 2.0 */ +typedef enum UBreakIteratorType { + /** Character breaks @stable ICU 2.0 */ + UBRK_CHARACTER = 0, + /** Word breaks @stable ICU 2.0 */ + UBRK_WORD = 1, + /** Line breaks @stable ICU 2.0 */ + UBRK_LINE = 2, + /** Sentence breaks @stable ICU 2.0 */ + UBRK_SENTENCE = 3, + +#ifndef U_HIDE_DEPRECATED_API + /** + * Title Case breaks + * The iterator created using this type locates title boundaries as described for + * Unicode 3.2 only. For Unicode 4.0 and above title boundary iteration, + * please use Word Boundary iterator. + * + * @deprecated ICU 2.8 Use the word break iterator for titlecasing for Unicode 4 and later. + */ + UBRK_TITLE = 4, + /** + * One more than the highest normal UBreakIteratorType value. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UBRK_COUNT = 5 +#endif // U_HIDE_DEPRECATED_API +} UBreakIteratorType; + +/** Value indicating all text boundaries have been returned. + * @stable ICU 2.0 + */ +#define UBRK_DONE ((int32_t) -1) + + +/** + * Enum constants for the word break tags returned by + * getRuleStatus(). A range of values is defined for each category of + * word, to allow for further subdivisions of a category in future releases. + * Applications should check for tag values falling within the range, rather + * than for single individual values. + * + * The numeric values of all of these constants are stable (will not change). + * + * @stable ICU 2.2 +*/ +typedef enum UWordBreak { + /** Tag value for "words" that do not fit into any of other categories. + * Includes spaces and most punctuation. */ + UBRK_WORD_NONE = 0, + /** Upper bound for tags for uncategorized words. */ + UBRK_WORD_NONE_LIMIT = 100, + /** Tag value for words that appear to be numbers, lower limit. */ + UBRK_WORD_NUMBER = 100, + /** Tag value for words that appear to be numbers, upper limit. */ + UBRK_WORD_NUMBER_LIMIT = 200, + /** Tag value for words that contain letters, excluding + * hiragana, katakana or ideographic characters, lower limit. */ + UBRK_WORD_LETTER = 200, + /** Tag value for words containing letters, upper limit */ + UBRK_WORD_LETTER_LIMIT = 300, + /** Tag value for words containing kana characters, lower limit */ + UBRK_WORD_KANA = 300, + /** Tag value for words containing kana characters, upper limit */ + UBRK_WORD_KANA_LIMIT = 400, + /** Tag value for words containing ideographic characters, lower limit */ + UBRK_WORD_IDEO = 400, + /** Tag value for words containing ideographic characters, upper limit */ + UBRK_WORD_IDEO_LIMIT = 500 +} UWordBreak; + +/** + * Enum constants for the line break tags returned by getRuleStatus(). + * A range of values is defined for each category of + * word, to allow for further subdivisions of a category in future releases. + * Applications should check for tag values falling within the range, rather + * than for single individual values. + * + * The numeric values of all of these constants are stable (will not change). + * + * @stable ICU 2.8 +*/ +typedef enum ULineBreakTag { + /** Tag value for soft line breaks, positions at which a line break + * is acceptable but not required */ + UBRK_LINE_SOFT = 0, + /** Upper bound for soft line breaks. */ + UBRK_LINE_SOFT_LIMIT = 100, + /** Tag value for a hard, or mandatory line break */ + UBRK_LINE_HARD = 100, + /** Upper bound for hard line breaks. */ + UBRK_LINE_HARD_LIMIT = 200 +} ULineBreakTag; + + + +/** + * Enum constants for the sentence break tags returned by getRuleStatus(). + * A range of values is defined for each category of + * sentence, to allow for further subdivisions of a category in future releases. + * Applications should check for tag values falling within the range, rather + * than for single individual values. + * + * The numeric values of all of these constants are stable (will not change). + * + * @stable ICU 2.8 +*/ +typedef enum USentenceBreakTag { + /** Tag value for for sentences ending with a sentence terminator + * ('.', '?', '!', etc.) character, possibly followed by a + * hard separator (CR, LF, PS, etc.) + */ + UBRK_SENTENCE_TERM = 0, + /** Upper bound for tags for sentences ended by sentence terminators. */ + UBRK_SENTENCE_TERM_LIMIT = 100, + /** Tag value for for sentences that do not contain an ending + * sentence terminator ('.', '?', '!', etc.) character, but + * are ended only by a hard separator (CR, LF, PS, etc.) or end of input. + */ + UBRK_SENTENCE_SEP = 100, + /** Upper bound for tags for sentences ended by a separator. */ + UBRK_SENTENCE_SEP_LIMIT = 200 + /** Tag value for a hard, or mandatory line break */ +} USentenceBreakTag; + + +/** + * Open a new UBreakIterator for locating text boundaries for a specified locale. + * A UBreakIterator may be used for detecting character, line, word, + * and sentence breaks in text. + * @param type The type of UBreakIterator to open: one of UBRK_CHARACTER, UBRK_WORD, + * UBRK_LINE, UBRK_SENTENCE + * @param locale The locale specifying the text-breaking conventions. Note that + * locale keys such as "lb" and "ss" may be used to modify text break behavior, + * see general discussion of BreakIterator C API. + * @param text The text to be iterated over. May be null, in which case ubrk_setText() is + * used to specify the text to be iterated. + * @param textLength The number of characters in text, or -1 if null-terminated. + * @param status A UErrorCode to receive any errors. + * @return A UBreakIterator for the specified locale. + * @see ubrk_openRules + * @stable ICU 2.0 + */ +U_CAPI UBreakIterator* U_EXPORT2 +ubrk_open(UBreakIteratorType type, + const char *locale, + const UChar *text, + int32_t textLength, + UErrorCode *status); + +/** + * Open a new UBreakIterator for locating text boundaries using specified breaking rules. + * The rule syntax is ... (TBD) + * @param rules A set of rules specifying the text breaking conventions. + * @param rulesLength The number of characters in rules, or -1 if null-terminated. + * @param text The text to be iterated over. May be null, in which case ubrk_setText() is + * used to specify the text to be iterated. + * @param textLength The number of characters in text, or -1 if null-terminated. + * @param parseErr Receives position and context information for any syntax errors + * detected while parsing the rules. + * @param status A UErrorCode to receive any errors. + * @return A UBreakIterator for the specified rules. + * @see ubrk_open + * @stable ICU 2.2 + */ +U_CAPI UBreakIterator* U_EXPORT2 +ubrk_openRules(const UChar *rules, + int32_t rulesLength, + const UChar *text, + int32_t textLength, + UParseError *parseErr, + UErrorCode *status); + +/** + * Open a new UBreakIterator for locating text boundaries using precompiled binary rules. + * Opening a UBreakIterator this way is substantially faster than using ubrk_openRules. + * Binary rules may be obtained using ubrk_getBinaryRules. The compiled rules are not + * compatible across different major versions of ICU, nor across platforms of different + * endianness or different base character set family (ASCII vs EBCDIC). + * @param binaryRules A set of compiled binary rules specifying the text breaking + * conventions. Ownership of the storage containing the compiled + * rules remains with the caller of this function. The compiled + * rules must not be modified or deleted during the life of the + * break iterator. + * @param rulesLength The length of binaryRules in bytes; must be >= 0. + * @param text The text to be iterated over. May be null, in which case + * ubrk_setText() is used to specify the text to be iterated. + * @param textLength The number of characters in text, or -1 if null-terminated. + * @param status Pointer to UErrorCode to receive any errors. + * @return UBreakIterator for the specified rules. + * @see ubrk_getBinaryRules + * @stable ICU 59 + */ +U_CAPI UBreakIterator* U_EXPORT2 +ubrk_openBinaryRules(const uint8_t *binaryRules, int32_t rulesLength, + const UChar * text, int32_t textLength, + UErrorCode * status); + +#ifndef U_HIDE_DEPRECATED_API + +/** + * Thread safe cloning operation + * @param bi iterator to be cloned + * @param stackBuffer Deprecated functionality as of ICU 52, use NULL.
+ * user allocated space for the new clone. If NULL new memory will be allocated. + * If buffer is not large enough, new memory will be allocated. + * Clients can use the U_BRK_SAFECLONE_BUFFERSIZE. + * @param pBufferSize Deprecated functionality as of ICU 52, use NULL or 1.
+ * pointer to size of allocated space. + * If *pBufferSize == 0, a sufficient size for use in cloning will + * be returned ('pre-flighting') + * If *pBufferSize is not enough for a stack-based safe clone, + * new memory will be allocated. + * @param status to indicate whether the operation went on smoothly or there were errors + * An informational status value, U_SAFECLONE_ALLOCATED_ERROR, is used + * if pBufferSize != NULL and any allocations were necessary + * @return pointer to the new clone + * @deprecated ICU 69 Use ubrk_clone() instead. + */ +U_DEPRECATED UBreakIterator * U_EXPORT2 +ubrk_safeClone( + const UBreakIterator *bi, + void *stackBuffer, + int32_t *pBufferSize, + UErrorCode *status); + +#endif /* U_HIDE_DEPRECATED_API */ + +/** + * Thread safe cloning operation. + * @param bi iterator to be cloned + * @param status to indicate whether the operation went on smoothly or there were errors + * @return pointer to the new clone + * @stable ICU 69 + */ +U_CAPI UBreakIterator * U_EXPORT2 +ubrk_clone(const UBreakIterator *bi, + UErrorCode *status); + +#ifndef U_HIDE_DEPRECATED_API + +/** + * A recommended size (in bytes) for the memory buffer to be passed to ubrk_saveClone(). + * @deprecated ICU 52. Do not rely on ubrk_safeClone() cloning into any provided buffer. + */ +#define U_BRK_SAFECLONE_BUFFERSIZE 1 + +#endif /* U_HIDE_DEPRECATED_API */ + +/** +* Close a UBreakIterator. +* Once closed, a UBreakIterator may no longer be used. +* @param bi The break iterator to close. + * @stable ICU 2.0 +*/ +U_CAPI void U_EXPORT2 +ubrk_close(UBreakIterator *bi); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUBreakIteratorPointer + * "Smart pointer" class, closes a UBreakIterator via ubrk_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUBreakIteratorPointer, UBreakIterator, ubrk_close); + +U_NAMESPACE_END + +#endif + +/** + * Sets an existing iterator to point to a new piece of text. + * The break iterator retains a pointer to the supplied text. + * The caller must not modify or delete the text while the BreakIterator + * retains the reference. + * + * @param bi The iterator to use + * @param text The text to be set + * @param textLength The length of the text + * @param status The error code + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ubrk_setText(UBreakIterator* bi, + const UChar* text, + int32_t textLength, + UErrorCode* status); + + +/** + * Sets an existing iterator to point to a new piece of text. + * + * All index positions returned by break iterator functions are + * native indices from the UText. For example, when breaking UTF-8 + * encoded text, the break positions returned by \ref ubrk_next, \ref ubrk_previous, etc. + * will be UTF-8 string indices, not UTF-16 positions. + * + * @param bi The iterator to use + * @param text The text to be set. + * This function makes a shallow clone of the supplied UText. This means + * that the caller is free to immediately close or otherwise reuse the + * UText that was passed as a parameter, but that the underlying text itself + * must not be altered while being referenced by the break iterator. + * @param status The error code + * @stable ICU 3.4 + */ +U_CAPI void U_EXPORT2 +ubrk_setUText(UBreakIterator* bi, + UText* text, + UErrorCode* status); + + + +/** + * Determine the most recently-returned text boundary. + * + * @param bi The break iterator to use. + * @return The character index most recently returned by \ref ubrk_next, \ref ubrk_previous, + * \ref ubrk_first, or \ref ubrk_last. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ubrk_current(const UBreakIterator *bi); + +/** + * Advance the iterator to the boundary following the current boundary. + * + * @param bi The break iterator to use. + * @return The character index of the next text boundary, or UBRK_DONE + * if all text boundaries have been returned. + * @see ubrk_previous + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ubrk_next(UBreakIterator *bi); + +/** + * Set the iterator position to the boundary preceding the current boundary. + * + * @param bi The break iterator to use. + * @return The character index of the preceding text boundary, or UBRK_DONE + * if all text boundaries have been returned. + * @see ubrk_next + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ubrk_previous(UBreakIterator *bi); + +/** + * Set the iterator position to zero, the start of the text being scanned. + * @param bi The break iterator to use. + * @return The new iterator position (zero). + * @see ubrk_last + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ubrk_first(UBreakIterator *bi); + +/** + * Set the iterator position to the index immediately beyond the last character in the text being scanned. + * This is not the same as the last character. + * @param bi The break iterator to use. + * @return The character offset immediately beyond the last character in the + * text being scanned. + * @see ubrk_first + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ubrk_last(UBreakIterator *bi); + +/** + * Set the iterator position to the first boundary preceding the specified offset. + * The new position is always smaller than offset, or UBRK_DONE. + * @param bi The break iterator to use. + * @param offset The offset to begin scanning. + * @return The text boundary preceding offset, or UBRK_DONE. + * @see ubrk_following + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ubrk_preceding(UBreakIterator *bi, + int32_t offset); + +/** + * Advance the iterator to the first boundary following the specified offset. + * The value returned is always greater than offset, or UBRK_DONE. + * @param bi The break iterator to use. + * @param offset The offset to begin scanning. + * @return The text boundary following offset, or UBRK_DONE. + * @see ubrk_preceding + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ubrk_following(UBreakIterator *bi, + int32_t offset); + +/** +* Get a locale for which text breaking information is available. +* A UBreakIterator in a locale returned by this function will perform the correct +* text breaking for the locale. +* @param index The index of the desired locale. +* @return A locale for which number text breaking information is available, or 0 if none. +* @see ubrk_countAvailable +* @stable ICU 2.0 +*/ +U_CAPI const char* U_EXPORT2 +ubrk_getAvailable(int32_t index); + +/** +* Determine how many locales have text breaking information available. +* This function is most useful as determining the loop ending condition for +* calls to \ref ubrk_getAvailable. +* @return The number of locales for which text breaking information is available. +* @see ubrk_getAvailable +* @stable ICU 2.0 +*/ +U_CAPI int32_t U_EXPORT2 +ubrk_countAvailable(void); + + +/** +* Returns true if the specified position is a boundary position. As a side +* effect, leaves the iterator pointing to the first boundary position at +* or after "offset". +* @param bi The break iterator to use. +* @param offset the offset to check. +* @return True if "offset" is a boundary position. +* @stable ICU 2.0 +*/ +U_CAPI UBool U_EXPORT2 +ubrk_isBoundary(UBreakIterator *bi, int32_t offset); + +/** + * Return the status from the break rule that determined the most recently + * returned break position. The values appear in the rule source + * within brackets, {123}, for example. For rules that do not specify a + * status, a default value of 0 is returned. + *

+ * For word break iterators, the possible values are defined in enum UWordBreak. + * @stable ICU 2.2 + */ +U_CAPI int32_t U_EXPORT2 +ubrk_getRuleStatus(UBreakIterator *bi); + +/** + * Get the statuses from the break rules that determined the most recently + * returned break position. The values appear in the rule source + * within brackets, {123}, for example. The default status value for rules + * that do not explicitly provide one is zero. + *

+ * For word break iterators, the possible values are defined in enum UWordBreak. + * @param bi The break iterator to use + * @param fillInVec an array to be filled in with the status values. + * @param capacity the length of the supplied vector. A length of zero causes + * the function to return the number of status values, in the + * normal way, without attempting to store any values. + * @param status receives error codes. + * @return The number of rule status values from rules that determined + * the most recent boundary returned by the break iterator. + * @stable ICU 3.0 + */ +U_CAPI int32_t U_EXPORT2 +ubrk_getRuleStatusVec(UBreakIterator *bi, int32_t *fillInVec, int32_t capacity, UErrorCode *status); + +/** + * Return the locale of the break iterator. You can choose between the valid and + * the actual locale. + * @param bi break iterator + * @param type locale type (valid or actual) + * @param status error code + * @return locale string + * @stable ICU 2.8 + */ +U_CAPI const char* U_EXPORT2 +ubrk_getLocaleByType(const UBreakIterator *bi, ULocDataLocaleType type, UErrorCode* status); + +/** + * Set the subject text string upon which the break iterator is operating + * without changing any other aspect of the state. + * The new and previous text strings must have the same content. + * + * This function is intended for use in environments where ICU is operating on + * strings that may move around in memory. It provides a mechanism for notifying + * ICU that the string has been relocated, and providing a new UText to access the + * string in its new position. + * + * Note that the break iterator never copies the underlying text + * of a string being processed, but always operates directly on the original text + * provided by the user. Refreshing simply drops the references to the old text + * and replaces them with references to the new. + * + * Caution: this function is normally used only by very specialized + * system-level code. One example use case is with garbage collection + * that moves the text in memory. + * + * @param bi The break iterator. + * @param text The new (moved) text string. + * @param status Receives errors detected by this function. + * + * @stable ICU 49 + */ +U_CAPI void U_EXPORT2 +ubrk_refreshUText(UBreakIterator *bi, + UText *text, + UErrorCode *status); + + +/** + * Get a compiled binary version of the rules specifying the behavior of a UBreakIterator. + * The binary rules may be used with ubrk_openBinaryRules to open a new UBreakIterator + * more quickly than using ubrk_openRules. The compiled rules are not compatible across + * different major versions of ICU, nor across platforms of different endianness or + * different base character set family (ASCII vs EBCDIC). Supports preflighting (with + * binaryRules=NULL and rulesCapacity=0) to get the rules length without copying them to + * the binaryRules buffer. However, whether preflighting or not, if the actual length + * is greater than INT32_MAX, then the function returns 0 and sets *status to + * U_INDEX_OUTOFBOUNDS_ERROR. + + * @param bi The break iterator to use. + * @param binaryRules Buffer to receive the compiled binary rules; set to NULL for + * preflighting. + * @param rulesCapacity Capacity (in bytes) of the binaryRules buffer; set to 0 for + * preflighting. Must be >= 0. + * @param status Pointer to UErrorCode to receive any errors, such as + * U_BUFFER_OVERFLOW_ERROR, U_INDEX_OUTOFBOUNDS_ERROR, or + * U_ILLEGAL_ARGUMENT_ERROR. + * @return The actual byte length of the binary rules, if <= INT32_MAX; + * otherwise 0. If not preflighting and this is larger than + * rulesCapacity, *status will be set to an error. + * @see ubrk_openBinaryRules + * @stable ICU 59 + */ +U_CAPI int32_t U_EXPORT2 +ubrk_getBinaryRules(UBreakIterator *bi, + uint8_t * binaryRules, int32_t rulesCapacity, + UErrorCode * status); + +#endif /* #if !UCONFIG_NO_BREAK_ITERATION */ + +#endif diff --git a/deps/icu-small/source/common/unicode/ucasemap.h b/deps/icu-small/source/common/unicode/ucasemap.h index d1c1b483ab337e..68c62ce80e93ed 100644 --- a/deps/icu-small/source/common/unicode/ucasemap.h +++ b/deps/icu-small/source/common/unicode/ucasemap.h @@ -1,388 +1,388 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2005-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: ucasemap.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2005may06 -* created by: Markus W. Scherer -* -* Case mapping service object and functions using it. -*/ - -#ifndef __UCASEMAP_H__ -#define __UCASEMAP_H__ - -#include "unicode/utypes.h" -#include "unicode/stringoptions.h" -#include "unicode/ustring.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -/** - * \file - * \brief C API: Unicode case mapping functions using a UCaseMap service object. - * - * The service object takes care of memory allocations, data loading, and setup - * for the attributes, as usual. - * - * Currently, the functionality provided here does not overlap with uchar.h - * and ustring.h, except for ucasemap_toTitle(). - * - * ucasemap_utf8XYZ() functions operate directly on UTF-8 strings. - */ - -/** - * UCaseMap is an opaque service object for newer ICU case mapping functions. - * Older functions did not use a service object. - * @stable ICU 3.4 - */ -struct UCaseMap; -typedef struct UCaseMap UCaseMap; /**< C typedef for struct UCaseMap. @stable ICU 3.4 */ - -/** - * Open a UCaseMap service object for a locale and a set of options. - * The locale ID and options are preprocessed so that functions using the - * service object need not process them in each call. - * - * @param locale ICU locale ID, used for language-dependent - * upper-/lower-/title-casing according to the Unicode standard. - * Usual semantics: ""=root, NULL=default locale, etc. - * @param options Options bit set, used for case folding and string comparisons. - * Same flags as for u_foldCase(), u_strFoldCase(), - * u_strCaseCompare(), etc. - * Use 0 or U_FOLD_CASE_DEFAULT for default behavior. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return Pointer to a UCaseMap service object, if successful. - * - * @see U_FOLD_CASE_DEFAULT - * @see U_FOLD_CASE_EXCLUDE_SPECIAL_I - * @see U_TITLECASE_NO_LOWERCASE - * @see U_TITLECASE_NO_BREAK_ADJUSTMENT - * @stable ICU 3.4 - */ -U_CAPI UCaseMap * U_EXPORT2 -ucasemap_open(const char *locale, uint32_t options, UErrorCode *pErrorCode); - -/** - * Close a UCaseMap service object. - * @param csm Object to be closed. - * @stable ICU 3.4 - */ -U_CAPI void U_EXPORT2 -ucasemap_close(UCaseMap *csm); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUCaseMapPointer - * "Smart pointer" class, closes a UCaseMap via ucasemap_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUCaseMapPointer, UCaseMap, ucasemap_close); - -U_NAMESPACE_END - -#endif - -/** - * Get the locale ID that is used for language-dependent case mappings. - * @param csm UCaseMap service object. - * @return locale ID - * @stable ICU 3.4 - */ -U_CAPI const char * U_EXPORT2 -ucasemap_getLocale(const UCaseMap *csm); - -/** - * Get the options bit set that is used for case folding and string comparisons. - * @param csm UCaseMap service object. - * @return options bit set - * @stable ICU 3.4 - */ -U_CAPI uint32_t U_EXPORT2 -ucasemap_getOptions(const UCaseMap *csm); - -/** - * Set the locale ID that is used for language-dependent case mappings. - * - * @param csm UCaseMap service object. - * @param locale Locale ID, see ucasemap_open(). - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * - * @see ucasemap_open - * @stable ICU 3.4 - */ -U_CAPI void U_EXPORT2 -ucasemap_setLocale(UCaseMap *csm, const char *locale, UErrorCode *pErrorCode); - -/** - * Set the options bit set that is used for case folding and string comparisons. - * - * @param csm UCaseMap service object. - * @param options Options bit set, see ucasemap_open(). - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * - * @see ucasemap_open - * @stable ICU 3.4 - */ -U_CAPI void U_EXPORT2 -ucasemap_setOptions(UCaseMap *csm, uint32_t options, UErrorCode *pErrorCode); - -#if !UCONFIG_NO_BREAK_ITERATION - -/** - * Get the break iterator that is used for titlecasing. - * Do not modify the returned break iterator. - * @param csm UCaseMap service object. - * @return titlecasing break iterator - * @stable ICU 3.8 - */ -U_CAPI const UBreakIterator * U_EXPORT2 -ucasemap_getBreakIterator(const UCaseMap *csm); - -/** - * Set the break iterator that is used for titlecasing. - * The UCaseMap service object releases a previously set break iterator - * and "adopts" this new one, taking ownership of it. - * It will be released in a subsequent call to ucasemap_setBreakIterator() - * or ucasemap_close(). - * - * Break iterator operations are not thread-safe. Therefore, titlecasing - * functions use non-const UCaseMap objects. It is not possible to titlecase - * strings concurrently using the same UCaseMap. - * - * @param csm UCaseMap service object. - * @param iterToAdopt Break iterator to be adopted for titlecasing. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * - * @see ucasemap_toTitle - * @see ucasemap_utf8ToTitle - * @stable ICU 3.8 - */ -U_CAPI void U_EXPORT2 -ucasemap_setBreakIterator(UCaseMap *csm, UBreakIterator *iterToAdopt, UErrorCode *pErrorCode); - -/** - * Titlecase a UTF-16 string. This function is almost a duplicate of u_strToTitle(), - * except that it takes ucasemap_setOptions() into account and has performance - * advantages from being able to use a UCaseMap object for multiple case mapping - * operations, saving setup time. - * - * Casing is locale-dependent and context-sensitive. - * Titlecasing uses a break iterator to find the first characters of words - * that are to be titlecased. It titlecases those characters and lowercases - * all others. (This can be modified with ucasemap_setOptions().) - * - * Note: This function takes a non-const UCaseMap pointer because it will - * open a default break iterator if no break iterator was set yet, - * and effectively call ucasemap_setBreakIterator(); - * also because the break iterator is stateful and will be modified during - * the iteration. - * - * The titlecase break iterator can be provided to customize for arbitrary - * styles, using rules and dictionaries beyond the standard iterators. - * The standard titlecase iterator for the root locale implements the - * algorithm of Unicode TR 21. - * - * This function uses only the setText(), first() and next() methods of the - * provided break iterator. - * - * The result may be longer or shorter than the original. - * The source string and the destination buffer must not overlap. - * - * @param csm UCaseMap service object. This pointer is non-const! - * See the note above for details. - * @param dest A buffer for the result string. The result will be NUL-terminated if - * the buffer is large enough. - * The contents is undefined in case of failure. - * @param destCapacity The size of the buffer (number of UChars). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param src The original string. - * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return The length of the result string, if successful - or in case of a buffer overflow, - * in which case it will be greater than destCapacity. - * - * @see u_strToTitle - * @stable ICU 3.8 - */ -U_CAPI int32_t U_EXPORT2 -ucasemap_toTitle(UCaseMap *csm, - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - UErrorCode *pErrorCode); - -#endif // UCONFIG_NO_BREAK_ITERATION - -/** - * Lowercase the characters in a UTF-8 string. - * Casing is locale-dependent and context-sensitive. - * The result may be longer or shorter than the original. - * The source string and the destination buffer must not overlap. - * - * @param csm UCaseMap service object. - * @param dest A buffer for the result string. The result will be NUL-terminated if - * the buffer is large enough. - * The contents is undefined in case of failure. - * @param destCapacity The size of the buffer (number of bytes). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param src The original string. - * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return The length of the result string, if successful - or in case of a buffer overflow, - * in which case it will be greater than destCapacity. - * - * @see u_strToLower - * @stable ICU 3.4 - */ -U_CAPI int32_t U_EXPORT2 -ucasemap_utf8ToLower(const UCaseMap *csm, - char *dest, int32_t destCapacity, - const char *src, int32_t srcLength, - UErrorCode *pErrorCode); - -/** - * Uppercase the characters in a UTF-8 string. - * Casing is locale-dependent and context-sensitive. - * The result may be longer or shorter than the original. - * The source string and the destination buffer must not overlap. - * - * @param csm UCaseMap service object. - * @param dest A buffer for the result string. The result will be NUL-terminated if - * the buffer is large enough. - * The contents is undefined in case of failure. - * @param destCapacity The size of the buffer (number of bytes). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param src The original string. - * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return The length of the result string, if successful - or in case of a buffer overflow, - * in which case it will be greater than destCapacity. - * - * @see u_strToUpper - * @stable ICU 3.4 - */ -U_CAPI int32_t U_EXPORT2 -ucasemap_utf8ToUpper(const UCaseMap *csm, - char *dest, int32_t destCapacity, - const char *src, int32_t srcLength, - UErrorCode *pErrorCode); - -#if !UCONFIG_NO_BREAK_ITERATION - -/** - * Titlecase a UTF-8 string. - * Casing is locale-dependent and context-sensitive. - * Titlecasing uses a break iterator to find the first characters of words - * that are to be titlecased. It titlecases those characters and lowercases - * all others. (This can be modified with ucasemap_setOptions().) - * - * Note: This function takes a non-const UCaseMap pointer because it will - * open a default break iterator if no break iterator was set yet, - * and effectively call ucasemap_setBreakIterator(); - * also because the break iterator is stateful and will be modified during - * the iteration. - * - * The titlecase break iterator can be provided to customize for arbitrary - * styles, using rules and dictionaries beyond the standard iterators. - * The standard titlecase iterator for the root locale implements the - * algorithm of Unicode TR 21. - * - * This function uses only the setUText(), first(), next() and close() methods of the - * provided break iterator. - * - * The result may be longer or shorter than the original. - * The source string and the destination buffer must not overlap. - * - * @param csm UCaseMap service object. This pointer is non-const! - * See the note above for details. - * @param dest A buffer for the result string. The result will be NUL-terminated if - * the buffer is large enough. - * The contents is undefined in case of failure. - * @param destCapacity The size of the buffer (number of bytes). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param src The original string. - * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return The length of the result string, if successful - or in case of a buffer overflow, - * in which case it will be greater than destCapacity. - * - * @see u_strToTitle - * @see U_TITLECASE_NO_LOWERCASE - * @see U_TITLECASE_NO_BREAK_ADJUSTMENT - * @stable ICU 3.8 - */ -U_CAPI int32_t U_EXPORT2 -ucasemap_utf8ToTitle(UCaseMap *csm, - char *dest, int32_t destCapacity, - const char *src, int32_t srcLength, - UErrorCode *pErrorCode); - -#endif - -/** - * Case-folds the characters in a UTF-8 string. - * - * Case-folding is locale-independent and not context-sensitive, - * but there is an option for whether to include or exclude mappings for dotted I - * and dotless i that are marked with 'T' in CaseFolding.txt. - * - * The result may be longer or shorter than the original. - * The source string and the destination buffer must not overlap. - * - * @param csm UCaseMap service object. - * @param dest A buffer for the result string. The result will be NUL-terminated if - * the buffer is large enough. - * The contents is undefined in case of failure. - * @param destCapacity The size of the buffer (number of bytes). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param src The original string. - * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return The length of the result string, if successful - or in case of a buffer overflow, - * in which case it will be greater than destCapacity. - * - * @see u_strFoldCase - * @see ucasemap_setOptions - * @see U_FOLD_CASE_DEFAULT - * @see U_FOLD_CASE_EXCLUDE_SPECIAL_I - * @stable ICU 3.8 - */ -U_CAPI int32_t U_EXPORT2 -ucasemap_utf8FoldCase(const UCaseMap *csm, - char *dest, int32_t destCapacity, - const char *src, int32_t srcLength, - UErrorCode *pErrorCode); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2005-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: ucasemap.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2005may06 +* created by: Markus W. Scherer +* +* Case mapping service object and functions using it. +*/ + +#ifndef __UCASEMAP_H__ +#define __UCASEMAP_H__ + +#include "unicode/utypes.h" +#include "unicode/stringoptions.h" +#include "unicode/ustring.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +/** + * \file + * \brief C API: Unicode case mapping functions using a UCaseMap service object. + * + * The service object takes care of memory allocations, data loading, and setup + * for the attributes, as usual. + * + * Currently, the functionality provided here does not overlap with uchar.h + * and ustring.h, except for ucasemap_toTitle(). + * + * ucasemap_utf8XYZ() functions operate directly on UTF-8 strings. + */ + +/** + * UCaseMap is an opaque service object for newer ICU case mapping functions. + * Older functions did not use a service object. + * @stable ICU 3.4 + */ +struct UCaseMap; +typedef struct UCaseMap UCaseMap; /**< C typedef for struct UCaseMap. @stable ICU 3.4 */ + +/** + * Open a UCaseMap service object for a locale and a set of options. + * The locale ID and options are preprocessed so that functions using the + * service object need not process them in each call. + * + * @param locale ICU locale ID, used for language-dependent + * upper-/lower-/title-casing according to the Unicode standard. + * Usual semantics: ""=root, NULL=default locale, etc. + * @param options Options bit set, used for case folding and string comparisons. + * Same flags as for u_foldCase(), u_strFoldCase(), + * u_strCaseCompare(), etc. + * Use 0 or U_FOLD_CASE_DEFAULT for default behavior. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return Pointer to a UCaseMap service object, if successful. + * + * @see U_FOLD_CASE_DEFAULT + * @see U_FOLD_CASE_EXCLUDE_SPECIAL_I + * @see U_TITLECASE_NO_LOWERCASE + * @see U_TITLECASE_NO_BREAK_ADJUSTMENT + * @stable ICU 3.4 + */ +U_CAPI UCaseMap * U_EXPORT2 +ucasemap_open(const char *locale, uint32_t options, UErrorCode *pErrorCode); + +/** + * Close a UCaseMap service object. + * @param csm Object to be closed. + * @stable ICU 3.4 + */ +U_CAPI void U_EXPORT2 +ucasemap_close(UCaseMap *csm); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUCaseMapPointer + * "Smart pointer" class, closes a UCaseMap via ucasemap_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUCaseMapPointer, UCaseMap, ucasemap_close); + +U_NAMESPACE_END + +#endif + +/** + * Get the locale ID that is used for language-dependent case mappings. + * @param csm UCaseMap service object. + * @return locale ID + * @stable ICU 3.4 + */ +U_CAPI const char * U_EXPORT2 +ucasemap_getLocale(const UCaseMap *csm); + +/** + * Get the options bit set that is used for case folding and string comparisons. + * @param csm UCaseMap service object. + * @return options bit set + * @stable ICU 3.4 + */ +U_CAPI uint32_t U_EXPORT2 +ucasemap_getOptions(const UCaseMap *csm); + +/** + * Set the locale ID that is used for language-dependent case mappings. + * + * @param csm UCaseMap service object. + * @param locale Locale ID, see ucasemap_open(). + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * + * @see ucasemap_open + * @stable ICU 3.4 + */ +U_CAPI void U_EXPORT2 +ucasemap_setLocale(UCaseMap *csm, const char *locale, UErrorCode *pErrorCode); + +/** + * Set the options bit set that is used for case folding and string comparisons. + * + * @param csm UCaseMap service object. + * @param options Options bit set, see ucasemap_open(). + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * + * @see ucasemap_open + * @stable ICU 3.4 + */ +U_CAPI void U_EXPORT2 +ucasemap_setOptions(UCaseMap *csm, uint32_t options, UErrorCode *pErrorCode); + +#if !UCONFIG_NO_BREAK_ITERATION + +/** + * Get the break iterator that is used for titlecasing. + * Do not modify the returned break iterator. + * @param csm UCaseMap service object. + * @return titlecasing break iterator + * @stable ICU 3.8 + */ +U_CAPI const UBreakIterator * U_EXPORT2 +ucasemap_getBreakIterator(const UCaseMap *csm); + +/** + * Set the break iterator that is used for titlecasing. + * The UCaseMap service object releases a previously set break iterator + * and "adopts" this new one, taking ownership of it. + * It will be released in a subsequent call to ucasemap_setBreakIterator() + * or ucasemap_close(). + * + * Break iterator operations are not thread-safe. Therefore, titlecasing + * functions use non-const UCaseMap objects. It is not possible to titlecase + * strings concurrently using the same UCaseMap. + * + * @param csm UCaseMap service object. + * @param iterToAdopt Break iterator to be adopted for titlecasing. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * + * @see ucasemap_toTitle + * @see ucasemap_utf8ToTitle + * @stable ICU 3.8 + */ +U_CAPI void U_EXPORT2 +ucasemap_setBreakIterator(UCaseMap *csm, UBreakIterator *iterToAdopt, UErrorCode *pErrorCode); + +/** + * Titlecase a UTF-16 string. This function is almost a duplicate of u_strToTitle(), + * except that it takes ucasemap_setOptions() into account and has performance + * advantages from being able to use a UCaseMap object for multiple case mapping + * operations, saving setup time. + * + * Casing is locale-dependent and context-sensitive. + * Titlecasing uses a break iterator to find the first characters of words + * that are to be titlecased. It titlecases those characters and lowercases + * all others. (This can be modified with ucasemap_setOptions().) + * + * Note: This function takes a non-const UCaseMap pointer because it will + * open a default break iterator if no break iterator was set yet, + * and effectively call ucasemap_setBreakIterator(); + * also because the break iterator is stateful and will be modified during + * the iteration. + * + * The titlecase break iterator can be provided to customize for arbitrary + * styles, using rules and dictionaries beyond the standard iterators. + * The standard titlecase iterator for the root locale implements the + * algorithm of Unicode TR 21. + * + * This function uses only the setText(), first() and next() methods of the + * provided break iterator. + * + * The result may be longer or shorter than the original. + * The source string and the destination buffer must not overlap. + * + * @param csm UCaseMap service object. This pointer is non-const! + * See the note above for details. + * @param dest A buffer for the result string. The result will be NUL-terminated if + * the buffer is large enough. + * The contents is undefined in case of failure. + * @param destCapacity The size of the buffer (number of UChars). If it is 0, then + * dest may be NULL and the function will only return the length of the result + * without writing any of the result string. + * @param src The original string. + * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return The length of the result string, if successful - or in case of a buffer overflow, + * in which case it will be greater than destCapacity. + * + * @see u_strToTitle + * @stable ICU 3.8 + */ +U_CAPI int32_t U_EXPORT2 +ucasemap_toTitle(UCaseMap *csm, + UChar *dest, int32_t destCapacity, + const UChar *src, int32_t srcLength, + UErrorCode *pErrorCode); + +#endif // UCONFIG_NO_BREAK_ITERATION + +/** + * Lowercase the characters in a UTF-8 string. + * Casing is locale-dependent and context-sensitive. + * The result may be longer or shorter than the original. + * The source string and the destination buffer must not overlap. + * + * @param csm UCaseMap service object. + * @param dest A buffer for the result string. The result will be NUL-terminated if + * the buffer is large enough. + * The contents is undefined in case of failure. + * @param destCapacity The size of the buffer (number of bytes). If it is 0, then + * dest may be NULL and the function will only return the length of the result + * without writing any of the result string. + * @param src The original string. + * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return The length of the result string, if successful - or in case of a buffer overflow, + * in which case it will be greater than destCapacity. + * + * @see u_strToLower + * @stable ICU 3.4 + */ +U_CAPI int32_t U_EXPORT2 +ucasemap_utf8ToLower(const UCaseMap *csm, + char *dest, int32_t destCapacity, + const char *src, int32_t srcLength, + UErrorCode *pErrorCode); + +/** + * Uppercase the characters in a UTF-8 string. + * Casing is locale-dependent and context-sensitive. + * The result may be longer or shorter than the original. + * The source string and the destination buffer must not overlap. + * + * @param csm UCaseMap service object. + * @param dest A buffer for the result string. The result will be NUL-terminated if + * the buffer is large enough. + * The contents is undefined in case of failure. + * @param destCapacity The size of the buffer (number of bytes). If it is 0, then + * dest may be NULL and the function will only return the length of the result + * without writing any of the result string. + * @param src The original string. + * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return The length of the result string, if successful - or in case of a buffer overflow, + * in which case it will be greater than destCapacity. + * + * @see u_strToUpper + * @stable ICU 3.4 + */ +U_CAPI int32_t U_EXPORT2 +ucasemap_utf8ToUpper(const UCaseMap *csm, + char *dest, int32_t destCapacity, + const char *src, int32_t srcLength, + UErrorCode *pErrorCode); + +#if !UCONFIG_NO_BREAK_ITERATION + +/** + * Titlecase a UTF-8 string. + * Casing is locale-dependent and context-sensitive. + * Titlecasing uses a break iterator to find the first characters of words + * that are to be titlecased. It titlecases those characters and lowercases + * all others. (This can be modified with ucasemap_setOptions().) + * + * Note: This function takes a non-const UCaseMap pointer because it will + * open a default break iterator if no break iterator was set yet, + * and effectively call ucasemap_setBreakIterator(); + * also because the break iterator is stateful and will be modified during + * the iteration. + * + * The titlecase break iterator can be provided to customize for arbitrary + * styles, using rules and dictionaries beyond the standard iterators. + * The standard titlecase iterator for the root locale implements the + * algorithm of Unicode TR 21. + * + * This function uses only the setUText(), first(), next() and close() methods of the + * provided break iterator. + * + * The result may be longer or shorter than the original. + * The source string and the destination buffer must not overlap. + * + * @param csm UCaseMap service object. This pointer is non-const! + * See the note above for details. + * @param dest A buffer for the result string. The result will be NUL-terminated if + * the buffer is large enough. + * The contents is undefined in case of failure. + * @param destCapacity The size of the buffer (number of bytes). If it is 0, then + * dest may be NULL and the function will only return the length of the result + * without writing any of the result string. + * @param src The original string. + * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return The length of the result string, if successful - or in case of a buffer overflow, + * in which case it will be greater than destCapacity. + * + * @see u_strToTitle + * @see U_TITLECASE_NO_LOWERCASE + * @see U_TITLECASE_NO_BREAK_ADJUSTMENT + * @stable ICU 3.8 + */ +U_CAPI int32_t U_EXPORT2 +ucasemap_utf8ToTitle(UCaseMap *csm, + char *dest, int32_t destCapacity, + const char *src, int32_t srcLength, + UErrorCode *pErrorCode); + +#endif + +/** + * Case-folds the characters in a UTF-8 string. + * + * Case-folding is locale-independent and not context-sensitive, + * but there is an option for whether to include or exclude mappings for dotted I + * and dotless i that are marked with 'T' in CaseFolding.txt. + * + * The result may be longer or shorter than the original. + * The source string and the destination buffer must not overlap. + * + * @param csm UCaseMap service object. + * @param dest A buffer for the result string. The result will be NUL-terminated if + * the buffer is large enough. + * The contents is undefined in case of failure. + * @param destCapacity The size of the buffer (number of bytes). If it is 0, then + * dest may be NULL and the function will only return the length of the result + * without writing any of the result string. + * @param src The original string. + * @param srcLength The length of the original string. If -1, then src must be NUL-terminated. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return The length of the result string, if successful - or in case of a buffer overflow, + * in which case it will be greater than destCapacity. + * + * @see u_strFoldCase + * @see ucasemap_setOptions + * @see U_FOLD_CASE_DEFAULT + * @see U_FOLD_CASE_EXCLUDE_SPECIAL_I + * @stable ICU 3.8 + */ +U_CAPI int32_t U_EXPORT2 +ucasemap_utf8FoldCase(const UCaseMap *csm, + char *dest, int32_t destCapacity, + const char *src, int32_t srcLength, + UErrorCode *pErrorCode); + +#endif diff --git a/deps/icu-small/source/common/unicode/ucat.h b/deps/icu-small/source/common/unicode/ucat.h index 93850348fff318..7271ad48180e3b 100644 --- a/deps/icu-small/source/common/unicode/ucat.h +++ b/deps/icu-small/source/common/unicode/ucat.h @@ -1,160 +1,160 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2003-2004, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: March 19 2003 -* Since: ICU 2.6 -********************************************************************** -*/ -#ifndef UCAT_H -#define UCAT_H - -#include "unicode/utypes.h" -#include "unicode/ures.h" - -/** - * \file - * \brief C API: Message Catalog Wrappers - * - * This C API provides look-alike functions that deliberately resemble - * the POSIX catopen, catclose, and catgets functions. The underlying - * implementation is in terms of ICU resource bundles, rather than - * POSIX message catalogs. - * - * The ICU resource bundles obey standard ICU inheritance policies. - * To facilitate this, sets and messages are flattened into one tier. - * This is done by creating resource bundle keys of the form - * <set_num>%<msg_num> where set_num is the set number and msg_num is - * the message number, formatted as decimal strings. - * - * Example: Consider a message catalog containing two sets: - * - * Set 1: Message 4 = "Good morning." - * Message 5 = "Good afternoon." - * Message 7 = "Good evening." - * Message 8 = "Good night." - * Set 4: Message 14 = "Please " - * Message 19 = "Thank you." - * Message 20 = "Sincerely," - * - * The ICU resource bundle source file would, assuming it is named - * "greet.txt", would look like this: - * - * greet - * { - * 1%4 { "Good morning." } - * 1%5 { "Good afternoon." } - * 1%7 { "Good evening." } - * 1%8 { "Good night." } - * - * 4%14 { "Please " } - * 4%19 { "Thank you." } - * 4%20 { "Sincerely," } - * } - * - * The catgets function is commonly used in combination with functions - * like printf and strftime. ICU components like message format can - * be used instead, although they use a different format syntax. - * There is an ICU package, icuio, that provides some of - * the POSIX-style formatting API. - */ - -U_CDECL_BEGIN - -/** - * An ICU message catalog descriptor, analogous to nl_catd. - * - * @stable ICU 2.6 - */ -typedef UResourceBundle* u_nl_catd; - -/** - * Open and return an ICU message catalog descriptor. The descriptor - * may be passed to u_catgets() to retrieve localized strings. - * - * @param name string containing the full path pointing to the - * directory where the resources reside followed by the package name - * e.g. "/usr/resource/my_app/resources/guimessages" on a Unix system. - * If NULL, ICU default data files will be used. - * - * Unlike POSIX, environment variables are not interpolated within the - * name. - * - * @param locale the locale for which we want to open the resource. If - * NULL, the default ICU locale will be used (see uloc_getDefault). If - * strlen(locale) == 0, the root locale will be used. - * - * @param ec input/output error code. Upon output, - * U_USING_FALLBACK_WARNING indicates that a fallback locale was - * used. For example, 'de_CH' was requested, but nothing was found - * there, so 'de' was used. U_USING_DEFAULT_WARNING indicates that the - * default locale data or root locale data was used; neither the - * requested locale nor any of its fallback locales were found. - * - * @return a message catalog descriptor that may be passed to - * u_catgets(). If the ec parameter indicates success, then the caller - * is responsible for calling u_catclose() to close the message - * catalog. If the ec parameter indicates failure, then NULL will be - * returned. - * - * @stable ICU 2.6 - */ -U_CAPI u_nl_catd U_EXPORT2 -u_catopen(const char* name, const char* locale, UErrorCode* ec); - -/** - * Close an ICU message catalog, given its descriptor. - * - * @param catd a message catalog descriptor to be closed. May be NULL, - * in which case no action is taken. - * - * @stable ICU 2.6 - */ -U_CAPI void U_EXPORT2 -u_catclose(u_nl_catd catd); - -/** - * Retrieve a localized string from an ICU message catalog. - * - * @param catd a message catalog descriptor returned by u_catopen. - * - * @param set_num the message catalog set number. Sets need not be - * numbered consecutively. - * - * @param msg_num the message catalog message number within the - * set. Messages need not be numbered consecutively. - * - * @param s the default string. This is returned if the string - * specified by the set_num and msg_num is not found. It must be - * zero-terminated. - * - * @param len fill-in parameter to receive the length of the result. - * May be NULL, in which case it is ignored. - * - * @param ec input/output error code. May be U_USING_FALLBACK_WARNING - * or U_USING_DEFAULT_WARNING. U_MISSING_RESOURCE_ERROR indicates that - * the set_num/msg_num tuple does not specify a valid message string - * in this catalog. - * - * @return a pointer to a zero-terminated UChar array which lives in - * an internal buffer area, typically a memory mapped/DLL file. The - * caller must NOT delete this pointer. If the call is unsuccessful - * for any reason, then s is returned. This includes the situation in - * which ec indicates a failing error code upon entry to this - * function. - * - * @stable ICU 2.6 - */ -U_CAPI const UChar* U_EXPORT2 -u_catgets(u_nl_catd catd, int32_t set_num, int32_t msg_num, - const UChar* s, - int32_t* len, UErrorCode* ec); - -U_CDECL_END - -#endif /*UCAT_H*/ -/*eof*/ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2003-2004, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: March 19 2003 +* Since: ICU 2.6 +********************************************************************** +*/ +#ifndef UCAT_H +#define UCAT_H + +#include "unicode/utypes.h" +#include "unicode/ures.h" + +/** + * \file + * \brief C API: Message Catalog Wrappers + * + * This C API provides look-alike functions that deliberately resemble + * the POSIX catopen, catclose, and catgets functions. The underlying + * implementation is in terms of ICU resource bundles, rather than + * POSIX message catalogs. + * + * The ICU resource bundles obey standard ICU inheritance policies. + * To facilitate this, sets and messages are flattened into one tier. + * This is done by creating resource bundle keys of the form + * <set_num>%<msg_num> where set_num is the set number and msg_num is + * the message number, formatted as decimal strings. + * + * Example: Consider a message catalog containing two sets: + * + * Set 1: Message 4 = "Good morning." + * Message 5 = "Good afternoon." + * Message 7 = "Good evening." + * Message 8 = "Good night." + * Set 4: Message 14 = "Please " + * Message 19 = "Thank you." + * Message 20 = "Sincerely," + * + * The ICU resource bundle source file would, assuming it is named + * "greet.txt", would look like this: + * + * greet + * { + * 1%4 { "Good morning." } + * 1%5 { "Good afternoon." } + * 1%7 { "Good evening." } + * 1%8 { "Good night." } + * + * 4%14 { "Please " } + * 4%19 { "Thank you." } + * 4%20 { "Sincerely," } + * } + * + * The catgets function is commonly used in combination with functions + * like printf and strftime. ICU components like message format can + * be used instead, although they use a different format syntax. + * There is an ICU package, icuio, that provides some of + * the POSIX-style formatting API. + */ + +U_CDECL_BEGIN + +/** + * An ICU message catalog descriptor, analogous to nl_catd. + * + * @stable ICU 2.6 + */ +typedef UResourceBundle* u_nl_catd; + +/** + * Open and return an ICU message catalog descriptor. The descriptor + * may be passed to u_catgets() to retrieve localized strings. + * + * @param name string containing the full path pointing to the + * directory where the resources reside followed by the package name + * e.g. "/usr/resource/my_app/resources/guimessages" on a Unix system. + * If NULL, ICU default data files will be used. + * + * Unlike POSIX, environment variables are not interpolated within the + * name. + * + * @param locale the locale for which we want to open the resource. If + * NULL, the default ICU locale will be used (see uloc_getDefault). If + * strlen(locale) == 0, the root locale will be used. + * + * @param ec input/output error code. Upon output, + * U_USING_FALLBACK_WARNING indicates that a fallback locale was + * used. For example, 'de_CH' was requested, but nothing was found + * there, so 'de' was used. U_USING_DEFAULT_WARNING indicates that the + * default locale data or root locale data was used; neither the + * requested locale nor any of its fallback locales were found. + * + * @return a message catalog descriptor that may be passed to + * u_catgets(). If the ec parameter indicates success, then the caller + * is responsible for calling u_catclose() to close the message + * catalog. If the ec parameter indicates failure, then NULL will be + * returned. + * + * @stable ICU 2.6 + */ +U_CAPI u_nl_catd U_EXPORT2 +u_catopen(const char* name, const char* locale, UErrorCode* ec); + +/** + * Close an ICU message catalog, given its descriptor. + * + * @param catd a message catalog descriptor to be closed. May be NULL, + * in which case no action is taken. + * + * @stable ICU 2.6 + */ +U_CAPI void U_EXPORT2 +u_catclose(u_nl_catd catd); + +/** + * Retrieve a localized string from an ICU message catalog. + * + * @param catd a message catalog descriptor returned by u_catopen. + * + * @param set_num the message catalog set number. Sets need not be + * numbered consecutively. + * + * @param msg_num the message catalog message number within the + * set. Messages need not be numbered consecutively. + * + * @param s the default string. This is returned if the string + * specified by the set_num and msg_num is not found. It must be + * zero-terminated. + * + * @param len fill-in parameter to receive the length of the result. + * May be NULL, in which case it is ignored. + * + * @param ec input/output error code. May be U_USING_FALLBACK_WARNING + * or U_USING_DEFAULT_WARNING. U_MISSING_RESOURCE_ERROR indicates that + * the set_num/msg_num tuple does not specify a valid message string + * in this catalog. + * + * @return a pointer to a zero-terminated UChar array which lives in + * an internal buffer area, typically a memory mapped/DLL file. The + * caller must NOT delete this pointer. If the call is unsuccessful + * for any reason, then s is returned. This includes the situation in + * which ec indicates a failing error code upon entry to this + * function. + * + * @stable ICU 2.6 + */ +U_CAPI const UChar* U_EXPORT2 +u_catgets(u_nl_catd catd, int32_t set_num, int32_t msg_num, + const UChar* s, + int32_t* len, UErrorCode* ec); + +U_CDECL_END + +#endif /*UCAT_H*/ +/*eof*/ diff --git a/deps/icu-small/source/common/unicode/uchar.h b/deps/icu-small/source/common/unicode/uchar.h index 6bb68e62a9d7fc..0be497a79749ad 100644 --- a/deps/icu-small/source/common/unicode/uchar.h +++ b/deps/icu-small/source/common/unicode/uchar.h @@ -1,4179 +1,4169 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1997-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* File UCHAR.H -* -* Modification History: -* -* Date Name Description -* 04/02/97 aliu Creation. -* 03/29/99 helena Updated for C APIs. -* 4/15/99 Madhu Updated for C Implementation and Javadoc -* 5/20/99 Madhu Added the function u_getVersion() -* 8/19/1999 srl Upgraded scripts to Unicode 3.0 -* 8/27/1999 schererm UCharDirection constants: U_... -* 11/11/1999 weiv added u_isalnum(), cleaned comments -* 01/11/2000 helena Renamed u_getVersion to u_getUnicodeVersion(). -****************************************************************************** -*/ - -#ifndef UCHAR_H -#define UCHAR_H - -#include "unicode/utypes.h" -#include "unicode/stringoptions.h" -#include "unicode/ucpmap.h" - -#if !defined(USET_DEFINED) && !defined(U_IN_DOXYGEN) - -#define USET_DEFINED - -/** - * USet is the C API type corresponding to C++ class UnicodeSet. - * It is forward-declared here to avoid including unicode/uset.h file if related - * APIs are not used. - * - * @see ucnv_getUnicodeSet - * @stable ICU 2.4 - */ -typedef struct USet USet; - -#endif - - -U_CDECL_BEGIN - -/*==========================================================================*/ -/* Unicode version number */ -/*==========================================================================*/ -/** - * Unicode version number, default for the current ICU version. - * The actual Unicode Character Database (UCD) data is stored in uprops.dat - * and may be generated from UCD files from a different Unicode version. - * Call u_getUnicodeVersion to get the actual Unicode version of the data. - * - * @see u_getUnicodeVersion - * @stable ICU 2.0 - */ -#define U_UNICODE_VERSION "15.0" - -/** - * \file - * \brief C API: Unicode Properties - * - * This C API provides low-level access to the Unicode Character Database. - * In addition to raw property values, some convenience functions calculate - * derived properties, for example for Java-style programming. - * - * Unicode assigns each code point (not just assigned character) values for - * many properties. - * Most of them are simple boolean flags, or constants from a small enumerated list. - * For some properties, values are strings or other relatively more complex types. - * - * For more information see - * "About the Unicode Character Database" (http://www.unicode.org/ucd/) - * and the ICU User Guide chapter on Properties (https://unicode-org.github.io/icu/userguide/strings/properties). - * - * Many properties are accessible via generic functions that take a UProperty selector. - * - u_hasBinaryProperty() returns a binary value (true/false) per property and code point. - * - u_getIntPropertyValue() returns an integer value per property and code point. - * For each supported enumerated or catalog property, there is - * an enum type for all of the property's values, and - * u_getIntPropertyValue() returns the numeric values of those constants. - * - u_getBinaryPropertySet() returns a set for each ICU-supported binary property with - * all code points for which the property is true. - * - u_getIntPropertyMap() returns a map for each - * ICU-supported enumerated/catalog/int-valued property which - * maps all Unicode code points to their values for that property. - * - * Many functions are designed to match java.lang.Character functions. - * See the individual function documentation, - * and see the JDK 1.4 java.lang.Character documentation - * at http://java.sun.com/j2se/1.4/docs/api/java/lang/Character.html - * - * There are also functions that provide easy migration from C/POSIX functions - * like isblank(). Their use is generally discouraged because the C/POSIX - * standards do not define their semantics beyond the ASCII range, which means - * that different implementations exhibit very different behavior. - * Instead, Unicode properties should be used directly. - * - * There are also only a few, broad C/POSIX character classes, and they tend - * to be used for conflicting purposes. For example, the "isalpha()" class - * is sometimes used to determine word boundaries, while a more sophisticated - * approach would at least distinguish initial letters from continuation - * characters (the latter including combining marks). - * (In ICU, BreakIterator is the most sophisticated API for word boundaries.) - * Another example: There is no "istitle()" class for titlecase characters. - * - * ICU 3.4 and later provides API access for all twelve C/POSIX character classes. - * ICU implements them according to the Standard Recommendations in - * Annex C: Compatibility Properties of UTS #18 Unicode Regular Expressions - * (http://www.unicode.org/reports/tr18/#Compatibility_Properties). - * - * API access for C/POSIX character classes is as follows: - * - alpha: u_isUAlphabetic(c) or u_hasBinaryProperty(c, UCHAR_ALPHABETIC) - * - lower: u_isULowercase(c) or u_hasBinaryProperty(c, UCHAR_LOWERCASE) - * - upper: u_isUUppercase(c) or u_hasBinaryProperty(c, UCHAR_UPPERCASE) - * - punct: u_ispunct(c) - * - digit: u_isdigit(c) or u_charType(c)==U_DECIMAL_DIGIT_NUMBER - * - xdigit: u_isxdigit(c) or u_hasBinaryProperty(c, UCHAR_POSIX_XDIGIT) - * - alnum: u_hasBinaryProperty(c, UCHAR_POSIX_ALNUM) - * - space: u_isUWhiteSpace(c) or u_hasBinaryProperty(c, UCHAR_WHITE_SPACE) - * - blank: u_isblank(c) or u_hasBinaryProperty(c, UCHAR_POSIX_BLANK) - * - cntrl: u_charType(c)==U_CONTROL_CHAR - * - graph: u_hasBinaryProperty(c, UCHAR_POSIX_GRAPH) - * - print: u_hasBinaryProperty(c, UCHAR_POSIX_PRINT) - * - * Note: Some of the u_isxyz() functions in uchar.h predate, and do not match, - * the Standard Recommendations in UTS #18. Instead, they match Java - * functions according to their API documentation. - * - * \htmlonly - * The C/POSIX character classes are also available in UnicodeSet patterns, - * using patterns like [:graph:] or \p{graph}. - * \endhtmlonly - * - * Note: There are several ICU whitespace functions. - * Comparison: - * - u_isUWhiteSpace=UCHAR_WHITE_SPACE: Unicode White_Space property; - * most of general categories "Z" (separators) + most whitespace ISO controls - * (including no-break spaces, but excluding IS1..IS4) - * - u_isWhitespace: Java isWhitespace; Z + whitespace ISO controls but excluding no-break spaces - * - u_isJavaSpaceChar: Java isSpaceChar; just Z (including no-break spaces) - * - u_isspace: Z + whitespace ISO controls (including no-break spaces) - * - u_isblank: "horizontal spaces" = TAB + Zs - */ - -/** - * Constants. - */ - -/** The lowest Unicode code point value. Code points are non-negative. @stable ICU 2.0 */ -#define UCHAR_MIN_VALUE 0 - -/** - * The highest Unicode code point value (scalar value) according to - * The Unicode Standard. This is a 21-bit value (20.1 bits, rounded up). - * For a single character, UChar32 is a simple type that can hold any code point value. - * - * @see UChar32 - * @stable ICU 2.0 - */ -#define UCHAR_MAX_VALUE 0x10ffff - -/** - * Get a single-bit bit set (a flag) from a bit number 0..31. - * @stable ICU 2.1 - */ -#define U_MASK(x) ((uint32_t)1<<(x)) - -/** - * Selection constants for Unicode properties. - * These constants are used in functions like u_hasBinaryProperty to select - * one of the Unicode properties. - * - * The properties APIs are intended to reflect Unicode properties as defined - * in the Unicode Character Database (UCD) and Unicode Technical Reports (UTR). - * - * For details about the properties see - * UAX #44: Unicode Character Database (http://www.unicode.org/reports/tr44/). - * - * Important: If ICU is built with UCD files from Unicode versions below, e.g., 3.2, - * then properties marked with "new in Unicode 3.2" are not or not fully available. - * Check u_getUnicodeVersion to be sure. - * - * @see u_hasBinaryProperty - * @see u_getIntPropertyValue - * @see u_getUnicodeVersion - * @stable ICU 2.1 - */ -typedef enum UProperty { - /* - * Note: UProperty constants are parsed by preparseucd.py. - * It matches lines like - * UCHAR_=, - */ - - /* Note: Place UCHAR_ALPHABETIC before UCHAR_BINARY_START so that - debuggers display UCHAR_ALPHABETIC as the symbolic name for 0, - rather than UCHAR_BINARY_START. Likewise for other *_START - identifiers. */ - - /** Binary property Alphabetic. Same as u_isUAlphabetic, different from u_isalpha. - Lu+Ll+Lt+Lm+Lo+Nl+Other_Alphabetic @stable ICU 2.1 */ - UCHAR_ALPHABETIC=0, - /** First constant for binary Unicode properties. @stable ICU 2.1 */ - UCHAR_BINARY_START=UCHAR_ALPHABETIC, - /** Binary property ASCII_Hex_Digit. 0-9 A-F a-f @stable ICU 2.1 */ - UCHAR_ASCII_HEX_DIGIT=1, - /** Binary property Bidi_Control. - Format controls which have specific functions - in the Bidi Algorithm. @stable ICU 2.1 */ - UCHAR_BIDI_CONTROL=2, - /** Binary property Bidi_Mirrored. - Characters that may change display in RTL text. - Same as u_isMirrored. - See Bidi Algorithm, UTR 9. @stable ICU 2.1 */ - UCHAR_BIDI_MIRRORED=3, - /** Binary property Dash. Variations of dashes. @stable ICU 2.1 */ - UCHAR_DASH=4, - /** Binary property Default_Ignorable_Code_Point (new in Unicode 3.2). - Ignorable in most processing. - <2060..206F, FFF0..FFFB, E0000..E0FFF>+Other_Default_Ignorable_Code_Point+(Cf+Cc+Cs-White_Space) @stable ICU 2.1 */ - UCHAR_DEFAULT_IGNORABLE_CODE_POINT=5, - /** Binary property Deprecated (new in Unicode 3.2). - The usage of deprecated characters is strongly discouraged. @stable ICU 2.1 */ - UCHAR_DEPRECATED=6, - /** Binary property Diacritic. Characters that linguistically modify - the meaning of another character to which they apply. @stable ICU 2.1 */ - UCHAR_DIACRITIC=7, - /** Binary property Extender. - Extend the value or shape of a preceding alphabetic character, - e.g., length and iteration marks. @stable ICU 2.1 */ - UCHAR_EXTENDER=8, - /** Binary property Full_Composition_Exclusion. - CompositionExclusions.txt+Singleton Decompositions+ - Non-Starter Decompositions. @stable ICU 2.1 */ - UCHAR_FULL_COMPOSITION_EXCLUSION=9, - /** Binary property Grapheme_Base (new in Unicode 3.2). - For programmatic determination of grapheme cluster boundaries. - [0..10FFFF]-Cc-Cf-Cs-Co-Cn-Zl-Zp-Grapheme_Link-Grapheme_Extend-CGJ @stable ICU 2.1 */ - UCHAR_GRAPHEME_BASE=10, - /** Binary property Grapheme_Extend (new in Unicode 3.2). - For programmatic determination of grapheme cluster boundaries. - Me+Mn+Mc+Other_Grapheme_Extend-Grapheme_Link-CGJ @stable ICU 2.1 */ - UCHAR_GRAPHEME_EXTEND=11, - /** Binary property Grapheme_Link (new in Unicode 3.2). - For programmatic determination of grapheme cluster boundaries. @stable ICU 2.1 */ - UCHAR_GRAPHEME_LINK=12, - /** Binary property Hex_Digit. - Characters commonly used for hexadecimal numbers. @stable ICU 2.1 */ - UCHAR_HEX_DIGIT=13, - /** Binary property Hyphen. Dashes used to mark connections - between pieces of words, plus the Katakana middle dot. @stable ICU 2.1 */ - UCHAR_HYPHEN=14, - /** Binary property ID_Continue. - Characters that can continue an identifier. - DerivedCoreProperties.txt also says "NOTE: Cf characters should be filtered out." - ID_Start+Mn+Mc+Nd+Pc @stable ICU 2.1 */ - UCHAR_ID_CONTINUE=15, - /** Binary property ID_Start. - Characters that can start an identifier. - Lu+Ll+Lt+Lm+Lo+Nl @stable ICU 2.1 */ - UCHAR_ID_START=16, - /** Binary property Ideographic. - CJKV ideographs. @stable ICU 2.1 */ - UCHAR_IDEOGRAPHIC=17, - /** Binary property IDS_Binary_Operator (new in Unicode 3.2). - For programmatic determination of - Ideographic Description Sequences. @stable ICU 2.1 */ - UCHAR_IDS_BINARY_OPERATOR=18, - /** Binary property IDS_Trinary_Operator (new in Unicode 3.2). - For programmatic determination of - Ideographic Description Sequences. @stable ICU 2.1 */ - UCHAR_IDS_TRINARY_OPERATOR=19, - /** Binary property Join_Control. - Format controls for cursive joining and ligation. @stable ICU 2.1 */ - UCHAR_JOIN_CONTROL=20, - /** Binary property Logical_Order_Exception (new in Unicode 3.2). - Characters that do not use logical order and - require special handling in most processing. @stable ICU 2.1 */ - UCHAR_LOGICAL_ORDER_EXCEPTION=21, - /** Binary property Lowercase. Same as u_isULowercase, different from u_islower. - Ll+Other_Lowercase @stable ICU 2.1 */ - UCHAR_LOWERCASE=22, - /** Binary property Math. Sm+Other_Math @stable ICU 2.1 */ - UCHAR_MATH=23, - /** Binary property Noncharacter_Code_Point. - Code points that are explicitly defined as illegal - for the encoding of characters. @stable ICU 2.1 */ - UCHAR_NONCHARACTER_CODE_POINT=24, - /** Binary property Quotation_Mark. @stable ICU 2.1 */ - UCHAR_QUOTATION_MARK=25, - /** Binary property Radical (new in Unicode 3.2). - For programmatic determination of - Ideographic Description Sequences. @stable ICU 2.1 */ - UCHAR_RADICAL=26, - /** Binary property Soft_Dotted (new in Unicode 3.2). - Characters with a "soft dot", like i or j. - An accent placed on these characters causes - the dot to disappear. @stable ICU 2.1 */ - UCHAR_SOFT_DOTTED=27, - /** Binary property Terminal_Punctuation. - Punctuation characters that generally mark - the end of textual units. @stable ICU 2.1 */ - UCHAR_TERMINAL_PUNCTUATION=28, - /** Binary property Unified_Ideograph (new in Unicode 3.2). - For programmatic determination of - Ideographic Description Sequences. @stable ICU 2.1 */ - UCHAR_UNIFIED_IDEOGRAPH=29, - /** Binary property Uppercase. Same as u_isUUppercase, different from u_isupper. - Lu+Other_Uppercase @stable ICU 2.1 */ - UCHAR_UPPERCASE=30, - /** Binary property White_Space. - Same as u_isUWhiteSpace, different from u_isspace and u_isWhitespace. - Space characters+TAB+CR+LF-ZWSP-ZWNBSP @stable ICU 2.1 */ - UCHAR_WHITE_SPACE=31, - /** Binary property XID_Continue. - ID_Continue modified to allow closure under - normalization forms NFKC and NFKD. @stable ICU 2.1 */ - UCHAR_XID_CONTINUE=32, - /** Binary property XID_Start. ID_Start modified to allow - closure under normalization forms NFKC and NFKD. @stable ICU 2.1 */ - UCHAR_XID_START=33, - /** Binary property Case_Sensitive. Either the source of a case - mapping or _in_ the target of a case mapping. Not the same as - the general category Cased_Letter. @stable ICU 2.6 */ - UCHAR_CASE_SENSITIVE=34, - /** Binary property STerm (new in Unicode 4.0.1). - Sentence Terminal. Used in UAX #29: Text Boundaries - (http://www.unicode.org/reports/tr29/) - @stable ICU 3.0 */ - UCHAR_S_TERM=35, - /** Binary property Variation_Selector (new in Unicode 4.0.1). - Indicates all those characters that qualify as Variation Selectors. - For details on the behavior of these characters, - see StandardizedVariants.html and 15.6 Variation Selectors. - @stable ICU 3.0 */ - UCHAR_VARIATION_SELECTOR=36, - /** Binary property NFD_Inert. - ICU-specific property for characters that are inert under NFD, - i.e., they do not interact with adjacent characters. - See the documentation for the Normalizer2 class and the - Normalizer2::isInert() method. - @stable ICU 3.0 */ - UCHAR_NFD_INERT=37, - /** Binary property NFKD_Inert. - ICU-specific property for characters that are inert under NFKD, - i.e., they do not interact with adjacent characters. - See the documentation for the Normalizer2 class and the - Normalizer2::isInert() method. - @stable ICU 3.0 */ - UCHAR_NFKD_INERT=38, - /** Binary property NFC_Inert. - ICU-specific property for characters that are inert under NFC, - i.e., they do not interact with adjacent characters. - See the documentation for the Normalizer2 class and the - Normalizer2::isInert() method. - @stable ICU 3.0 */ - UCHAR_NFC_INERT=39, - /** Binary property NFKC_Inert. - ICU-specific property for characters that are inert under NFKC, - i.e., they do not interact with adjacent characters. - See the documentation for the Normalizer2 class and the - Normalizer2::isInert() method. - @stable ICU 3.0 */ - UCHAR_NFKC_INERT=40, - /** Binary Property Segment_Starter. - ICU-specific property for characters that are starters in terms of - Unicode normalization and combining character sequences. - They have ccc=0 and do not occur in non-initial position of the - canonical decomposition of any character - (like a-umlaut in NFD and a Jamo T in an NFD(Hangul LVT)). - ICU uses this property for segmenting a string for generating a set of - canonically equivalent strings, e.g. for canonical closure while - processing collation tailoring rules. - @stable ICU 3.0 */ - UCHAR_SEGMENT_STARTER=41, - /** Binary property Pattern_Syntax (new in Unicode 4.1). - See UAX #31 Identifier and Pattern Syntax - (http://www.unicode.org/reports/tr31/) - @stable ICU 3.4 */ - UCHAR_PATTERN_SYNTAX=42, - /** Binary property Pattern_White_Space (new in Unicode 4.1). - See UAX #31 Identifier and Pattern Syntax - (http://www.unicode.org/reports/tr31/) - @stable ICU 3.4 */ - UCHAR_PATTERN_WHITE_SPACE=43, - /** Binary property alnum (a C/POSIX character class). - Implemented according to the UTS #18 Annex C Standard Recommendation. - See the uchar.h file documentation. - @stable ICU 3.4 */ - UCHAR_POSIX_ALNUM=44, - /** Binary property blank (a C/POSIX character class). - Implemented according to the UTS #18 Annex C Standard Recommendation. - See the uchar.h file documentation. - @stable ICU 3.4 */ - UCHAR_POSIX_BLANK=45, - /** Binary property graph (a C/POSIX character class). - Implemented according to the UTS #18 Annex C Standard Recommendation. - See the uchar.h file documentation. - @stable ICU 3.4 */ - UCHAR_POSIX_GRAPH=46, - /** Binary property print (a C/POSIX character class). - Implemented according to the UTS #18 Annex C Standard Recommendation. - See the uchar.h file documentation. - @stable ICU 3.4 */ - UCHAR_POSIX_PRINT=47, - /** Binary property xdigit (a C/POSIX character class). - Implemented according to the UTS #18 Annex C Standard Recommendation. - See the uchar.h file documentation. - @stable ICU 3.4 */ - UCHAR_POSIX_XDIGIT=48, - /** Binary property Cased. For Lowercase, Uppercase and Titlecase characters. @stable ICU 4.4 */ - UCHAR_CASED=49, - /** Binary property Case_Ignorable. Used in context-sensitive case mappings. @stable ICU 4.4 */ - UCHAR_CASE_IGNORABLE=50, - /** Binary property Changes_When_Lowercased. @stable ICU 4.4 */ - UCHAR_CHANGES_WHEN_LOWERCASED=51, - /** Binary property Changes_When_Uppercased. @stable ICU 4.4 */ - UCHAR_CHANGES_WHEN_UPPERCASED=52, - /** Binary property Changes_When_Titlecased. @stable ICU 4.4 */ - UCHAR_CHANGES_WHEN_TITLECASED=53, - /** Binary property Changes_When_Casefolded. @stable ICU 4.4 */ - UCHAR_CHANGES_WHEN_CASEFOLDED=54, - /** Binary property Changes_When_Casemapped. @stable ICU 4.4 */ - UCHAR_CHANGES_WHEN_CASEMAPPED=55, - /** Binary property Changes_When_NFKC_Casefolded. @stable ICU 4.4 */ - UCHAR_CHANGES_WHEN_NFKC_CASEFOLDED=56, - /** - * Binary property Emoji. - * See http://www.unicode.org/reports/tr51/#Emoji_Properties - * - * @stable ICU 57 - */ - UCHAR_EMOJI=57, - /** - * Binary property Emoji_Presentation. - * See http://www.unicode.org/reports/tr51/#Emoji_Properties - * - * @stable ICU 57 - */ - UCHAR_EMOJI_PRESENTATION=58, - /** - * Binary property Emoji_Modifier. - * See http://www.unicode.org/reports/tr51/#Emoji_Properties - * - * @stable ICU 57 - */ - UCHAR_EMOJI_MODIFIER=59, - /** - * Binary property Emoji_Modifier_Base. - * See http://www.unicode.org/reports/tr51/#Emoji_Properties - * - * @stable ICU 57 - */ - UCHAR_EMOJI_MODIFIER_BASE=60, - /** - * Binary property Emoji_Component. - * See http://www.unicode.org/reports/tr51/#Emoji_Properties - * - * @stable ICU 60 - */ - UCHAR_EMOJI_COMPONENT=61, - /** - * Binary property Regional_Indicator. - * @stable ICU 60 - */ - UCHAR_REGIONAL_INDICATOR=62, - /** - * Binary property Prepended_Concatenation_Mark. - * @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, - /** - * Binary property of strings Basic_Emoji. - * See https://www.unicode.org/reports/tr51/#Emoji_Sets - * - * @stable ICU 70 - */ - UCHAR_BASIC_EMOJI=65, - /** - * Binary property of strings Emoji_Keycap_Sequence. - * See https://www.unicode.org/reports/tr51/#Emoji_Sets - * - * @stable ICU 70 - */ - UCHAR_EMOJI_KEYCAP_SEQUENCE=66, - /** - * Binary property of strings RGI_Emoji_Modifier_Sequence. - * See https://www.unicode.org/reports/tr51/#Emoji_Sets - * - * @stable ICU 70 - */ - UCHAR_RGI_EMOJI_MODIFIER_SEQUENCE=67, - /** - * Binary property of strings RGI_Emoji_Flag_Sequence. - * See https://www.unicode.org/reports/tr51/#Emoji_Sets - * - * @stable ICU 70 - */ - UCHAR_RGI_EMOJI_FLAG_SEQUENCE=68, - /** - * Binary property of strings RGI_Emoji_Tag_Sequence. - * See https://www.unicode.org/reports/tr51/#Emoji_Sets - * - * @stable ICU 70 - */ - UCHAR_RGI_EMOJI_TAG_SEQUENCE=69, - /** - * Binary property of strings RGI_Emoji_ZWJ_Sequence. - * See https://www.unicode.org/reports/tr51/#Emoji_Sets - * - * @stable ICU 70 - */ - UCHAR_RGI_EMOJI_ZWJ_SEQUENCE=70, - /** - * Binary property of strings RGI_Emoji. - * See https://www.unicode.org/reports/tr51/#Emoji_Sets - * - * @stable ICU 70 - */ - UCHAR_RGI_EMOJI=71, -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the last constant for binary Unicode properties. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UCHAR_BINARY_LIMIT=72, -#endif // U_HIDE_DEPRECATED_API - - /** Enumerated property Bidi_Class. - Same as u_charDirection, returns UCharDirection values. @stable ICU 2.2 */ - UCHAR_BIDI_CLASS=0x1000, - /** First constant for enumerated/integer Unicode properties. @stable ICU 2.2 */ - UCHAR_INT_START=UCHAR_BIDI_CLASS, - /** Enumerated property Block. - Same as ublock_getCode, returns UBlockCode values. @stable ICU 2.2 */ - UCHAR_BLOCK=0x1001, - /** Enumerated property Canonical_Combining_Class. - Same as u_getCombiningClass, returns 8-bit numeric values. @stable ICU 2.2 */ - UCHAR_CANONICAL_COMBINING_CLASS=0x1002, - /** Enumerated property Decomposition_Type. - Returns UDecompositionType values. @stable ICU 2.2 */ - UCHAR_DECOMPOSITION_TYPE=0x1003, - /** Enumerated property East_Asian_Width. - See http://www.unicode.org/reports/tr11/ - Returns UEastAsianWidth values. @stable ICU 2.2 */ - UCHAR_EAST_ASIAN_WIDTH=0x1004, - /** Enumerated property General_Category. - Same as u_charType, returns UCharCategory values. @stable ICU 2.2 */ - UCHAR_GENERAL_CATEGORY=0x1005, - /** Enumerated property Joining_Group. - Returns UJoiningGroup values. @stable ICU 2.2 */ - UCHAR_JOINING_GROUP=0x1006, - /** Enumerated property Joining_Type. - Returns UJoiningType values. @stable ICU 2.2 */ - UCHAR_JOINING_TYPE=0x1007, - /** Enumerated property Line_Break. - Returns ULineBreak values. @stable ICU 2.2 */ - UCHAR_LINE_BREAK=0x1008, - /** Enumerated property Numeric_Type. - Returns UNumericType values. @stable ICU 2.2 */ - UCHAR_NUMERIC_TYPE=0x1009, - /** Enumerated property Script. - Same as uscript_getScript, returns UScriptCode values. @stable ICU 2.2 */ - UCHAR_SCRIPT=0x100A, - /** Enumerated property Hangul_Syllable_Type, new in Unicode 4. - Returns UHangulSyllableType values. @stable ICU 2.6 */ - UCHAR_HANGUL_SYLLABLE_TYPE=0x100B, - /** Enumerated property NFD_Quick_Check. - Returns UNormalizationCheckResult values. @stable ICU 3.0 */ - UCHAR_NFD_QUICK_CHECK=0x100C, - /** Enumerated property NFKD_Quick_Check. - Returns UNormalizationCheckResult values. @stable ICU 3.0 */ - UCHAR_NFKD_QUICK_CHECK=0x100D, - /** Enumerated property NFC_Quick_Check. - Returns UNormalizationCheckResult values. @stable ICU 3.0 */ - UCHAR_NFC_QUICK_CHECK=0x100E, - /** Enumerated property NFKC_Quick_Check. - Returns UNormalizationCheckResult values. @stable ICU 3.0 */ - UCHAR_NFKC_QUICK_CHECK=0x100F, - /** Enumerated property Lead_Canonical_Combining_Class. - ICU-specific property for the ccc of the first code point - of the decomposition, or lccc(c)=ccc(NFD(c)[0]). - Useful for checking for canonically ordered text; - see UNORM_FCD and http://www.unicode.org/notes/tn5/#FCD . - Returns 8-bit numeric values like UCHAR_CANONICAL_COMBINING_CLASS. @stable ICU 3.0 */ - UCHAR_LEAD_CANONICAL_COMBINING_CLASS=0x1010, - /** Enumerated property Trail_Canonical_Combining_Class. - ICU-specific property for the ccc of the last code point - of the decomposition, or tccc(c)=ccc(NFD(c)[last]). - Useful for checking for canonically ordered text; - see UNORM_FCD and http://www.unicode.org/notes/tn5/#FCD . - Returns 8-bit numeric values like UCHAR_CANONICAL_COMBINING_CLASS. @stable ICU 3.0 */ - UCHAR_TRAIL_CANONICAL_COMBINING_CLASS=0x1011, - /** Enumerated property Grapheme_Cluster_Break (new in Unicode 4.1). - Used in UAX #29: Text Boundaries - (http://www.unicode.org/reports/tr29/) - Returns UGraphemeClusterBreak values. @stable ICU 3.4 */ - UCHAR_GRAPHEME_CLUSTER_BREAK=0x1012, - /** Enumerated property Sentence_Break (new in Unicode 4.1). - Used in UAX #29: Text Boundaries - (http://www.unicode.org/reports/tr29/) - Returns USentenceBreak values. @stable ICU 3.4 */ - UCHAR_SENTENCE_BREAK=0x1013, - /** Enumerated property Word_Break (new in Unicode 4.1). - Used in UAX #29: Text Boundaries - (http://www.unicode.org/reports/tr29/) - Returns UWordBreakValues values. @stable ICU 3.4 */ - UCHAR_WORD_BREAK=0x1014, - /** Enumerated property Bidi_Paired_Bracket_Type (new in Unicode 6.3). - Used in UAX #9: Unicode Bidirectional Algorithm - (http://www.unicode.org/reports/tr9/) - Returns UBidiPairedBracketType values. @stable ICU 52 */ - UCHAR_BIDI_PAIRED_BRACKET_TYPE=0x1015, - /** - * Enumerated property Indic_Positional_Category. - * New in Unicode 6.0 as provisional property Indic_Matra_Category; - * renamed and changed to informative in Unicode 8.0. - * See http://www.unicode.org/reports/tr44/#IndicPositionalCategory.txt - * @stable ICU 63 - */ - UCHAR_INDIC_POSITIONAL_CATEGORY=0x1016, - /** - * Enumerated property Indic_Syllabic_Category. - * New in Unicode 6.0 as provisional; informative since Unicode 8.0. - * See http://www.unicode.org/reports/tr44/#IndicSyllabicCategory.txt - * @stable ICU 63 - */ - UCHAR_INDIC_SYLLABIC_CATEGORY=0x1017, - /** - * Enumerated property Vertical_Orientation. - * Used for UAX #50 Unicode Vertical Text Layout (https://www.unicode.org/reports/tr50/). - * New as a UCD property in Unicode 10.0. - * @stable ICU 63 - */ - UCHAR_VERTICAL_ORIENTATION=0x1018, -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the last constant for enumerated/integer Unicode properties. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UCHAR_INT_LIMIT=0x1019, -#endif // U_HIDE_DEPRECATED_API - - /** Bitmask property General_Category_Mask. - This is the General_Category property returned as a bit mask. - When used in u_getIntPropertyValue(c), same as U_MASK(u_charType(c)), - returns bit masks for UCharCategory values where exactly one bit is set. - When used with u_getPropertyValueName() and u_getPropertyValueEnum(), - a multi-bit mask is used for sets of categories like "Letters". - Mask values should be cast to uint32_t. - @stable ICU 2.4 */ - UCHAR_GENERAL_CATEGORY_MASK=0x2000, - /** First constant for bit-mask Unicode properties. @stable ICU 2.4 */ - UCHAR_MASK_START=UCHAR_GENERAL_CATEGORY_MASK, -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the last constant for bit-mask Unicode properties. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UCHAR_MASK_LIMIT=0x2001, -#endif // U_HIDE_DEPRECATED_API - - /** Double property Numeric_Value. - Corresponds to u_getNumericValue. @stable ICU 2.4 */ - UCHAR_NUMERIC_VALUE=0x3000, - /** First constant for double Unicode properties. @stable ICU 2.4 */ - UCHAR_DOUBLE_START=UCHAR_NUMERIC_VALUE, -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the last constant for double Unicode properties. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UCHAR_DOUBLE_LIMIT=0x3001, -#endif // U_HIDE_DEPRECATED_API - - /** String property Age. - Corresponds to u_charAge. @stable ICU 2.4 */ - UCHAR_AGE=0x4000, - /** First constant for string Unicode properties. @stable ICU 2.4 */ - UCHAR_STRING_START=UCHAR_AGE, - /** String property Bidi_Mirroring_Glyph. - Corresponds to u_charMirror. @stable ICU 2.4 */ - UCHAR_BIDI_MIRRORING_GLYPH=0x4001, - /** String property Case_Folding. - Corresponds to u_strFoldCase in ustring.h. @stable ICU 2.4 */ - UCHAR_CASE_FOLDING=0x4002, -#ifndef U_HIDE_DEPRECATED_API - /** Deprecated string property ISO_Comment. - Corresponds to u_getISOComment. @deprecated ICU 49 */ - UCHAR_ISO_COMMENT=0x4003, -#endif /* U_HIDE_DEPRECATED_API */ - /** String property Lowercase_Mapping. - Corresponds to u_strToLower in ustring.h. @stable ICU 2.4 */ - UCHAR_LOWERCASE_MAPPING=0x4004, - /** String property Name. - Corresponds to u_charName. @stable ICU 2.4 */ - UCHAR_NAME=0x4005, - /** String property Simple_Case_Folding. - Corresponds to u_foldCase. @stable ICU 2.4 */ - UCHAR_SIMPLE_CASE_FOLDING=0x4006, - /** String property Simple_Lowercase_Mapping. - Corresponds to u_tolower. @stable ICU 2.4 */ - UCHAR_SIMPLE_LOWERCASE_MAPPING=0x4007, - /** String property Simple_Titlecase_Mapping. - Corresponds to u_totitle. @stable ICU 2.4 */ - UCHAR_SIMPLE_TITLECASE_MAPPING=0x4008, - /** String property Simple_Uppercase_Mapping. - Corresponds to u_toupper. @stable ICU 2.4 */ - UCHAR_SIMPLE_UPPERCASE_MAPPING=0x4009, - /** String property Titlecase_Mapping. - Corresponds to u_strToTitle in ustring.h. @stable ICU 2.4 */ - UCHAR_TITLECASE_MAPPING=0x400A, -#ifndef U_HIDE_DEPRECATED_API - /** String property Unicode_1_Name. - This property is of little practical value. - Beginning with ICU 49, ICU APIs return an empty string for this property. - Corresponds to u_charName(U_UNICODE_10_CHAR_NAME). @deprecated ICU 49 */ - UCHAR_UNICODE_1_NAME=0x400B, -#endif /* U_HIDE_DEPRECATED_API */ - /** String property Uppercase_Mapping. - Corresponds to u_strToUpper in ustring.h. @stable ICU 2.4 */ - UCHAR_UPPERCASE_MAPPING=0x400C, - /** String property Bidi_Paired_Bracket (new in Unicode 6.3). - Corresponds to u_getBidiPairedBracket. @stable ICU 52 */ - UCHAR_BIDI_PAIRED_BRACKET=0x400D, -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the last constant for string Unicode properties. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UCHAR_STRING_LIMIT=0x400E, -#endif // U_HIDE_DEPRECATED_API - - /** Miscellaneous property Script_Extensions (new in Unicode 6.0). - Some characters are commonly used in multiple scripts. - For more information, see UAX #24: http://www.unicode.org/reports/tr24/. - Corresponds to uscript_hasScript and uscript_getScriptExtensions in uscript.h. - @stable ICU 4.6 */ - UCHAR_SCRIPT_EXTENSIONS=0x7000, - /** First constant for Unicode properties with unusual value types. @stable ICU 4.6 */ - UCHAR_OTHER_PROPERTY_START=UCHAR_SCRIPT_EXTENSIONS, -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the last constant for Unicode properties with unusual value types. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UCHAR_OTHER_PROPERTY_LIMIT=0x7001, -#endif // U_HIDE_DEPRECATED_API - - /** Represents a nonexistent or invalid property or property value. @stable ICU 2.4 */ - UCHAR_INVALID_CODE = -1 -} UProperty; - -/** - * Data for enumerated Unicode general category types. - * See http://www.unicode.org/Public/UNIDATA/UnicodeData.html . - * @stable ICU 2.0 - */ -typedef enum UCharCategory -{ - /* - * Note: UCharCategory constants and their API comments are parsed by preparseucd.py. - * It matches pairs of lines like - * / ** comment... * / - * U_<[A-Z_]+> = , - */ - - /** Non-category for unassigned and non-character code points. @stable ICU 2.0 */ - U_UNASSIGNED = 0, - /** Cn "Other, Not Assigned (no characters in [UnicodeData.txt] have this property)" (same as U_UNASSIGNED!) @stable ICU 2.0 */ - U_GENERAL_OTHER_TYPES = 0, - /** Lu @stable ICU 2.0 */ - U_UPPERCASE_LETTER = 1, - /** Ll @stable ICU 2.0 */ - U_LOWERCASE_LETTER = 2, - /** Lt @stable ICU 2.0 */ - U_TITLECASE_LETTER = 3, - /** Lm @stable ICU 2.0 */ - U_MODIFIER_LETTER = 4, - /** Lo @stable ICU 2.0 */ - U_OTHER_LETTER = 5, - /** Mn @stable ICU 2.0 */ - U_NON_SPACING_MARK = 6, - /** Me @stable ICU 2.0 */ - U_ENCLOSING_MARK = 7, - /** Mc @stable ICU 2.0 */ - U_COMBINING_SPACING_MARK = 8, - /** Nd @stable ICU 2.0 */ - U_DECIMAL_DIGIT_NUMBER = 9, - /** Nl @stable ICU 2.0 */ - U_LETTER_NUMBER = 10, - /** No @stable ICU 2.0 */ - U_OTHER_NUMBER = 11, - /** Zs @stable ICU 2.0 */ - U_SPACE_SEPARATOR = 12, - /** Zl @stable ICU 2.0 */ - U_LINE_SEPARATOR = 13, - /** Zp @stable ICU 2.0 */ - U_PARAGRAPH_SEPARATOR = 14, - /** Cc @stable ICU 2.0 */ - U_CONTROL_CHAR = 15, - /** Cf @stable ICU 2.0 */ - U_FORMAT_CHAR = 16, - /** Co @stable ICU 2.0 */ - U_PRIVATE_USE_CHAR = 17, - /** Cs @stable ICU 2.0 */ - U_SURROGATE = 18, - /** Pd @stable ICU 2.0 */ - U_DASH_PUNCTUATION = 19, - /** Ps @stable ICU 2.0 */ - U_START_PUNCTUATION = 20, - /** Pe @stable ICU 2.0 */ - U_END_PUNCTUATION = 21, - /** Pc @stable ICU 2.0 */ - U_CONNECTOR_PUNCTUATION = 22, - /** Po @stable ICU 2.0 */ - U_OTHER_PUNCTUATION = 23, - /** Sm @stable ICU 2.0 */ - U_MATH_SYMBOL = 24, - /** Sc @stable ICU 2.0 */ - U_CURRENCY_SYMBOL = 25, - /** Sk @stable ICU 2.0 */ - U_MODIFIER_SYMBOL = 26, - /** So @stable ICU 2.0 */ - U_OTHER_SYMBOL = 27, - /** Pi @stable ICU 2.0 */ - U_INITIAL_PUNCTUATION = 28, - /** Pf @stable ICU 2.0 */ - U_FINAL_PUNCTUATION = 29, - /** - * One higher than the last enum UCharCategory constant. - * This numeric value is stable (will not change), see - * http://www.unicode.org/policies/stability_policy.html#Property_Value - * - * @stable ICU 2.0 - */ - U_CHAR_CATEGORY_COUNT -} UCharCategory; - -/** - * U_GC_XX_MASK constants are bit flags corresponding to Unicode - * general category values. - * For each category, the nth bit is set if the numeric value of the - * corresponding UCharCategory constant is n. - * - * There are also some U_GC_Y_MASK constants for groups of general categories - * like L for all letter categories. - * - * @see u_charType - * @see U_GET_GC_MASK - * @see UCharCategory - * @stable ICU 2.1 - */ -#define U_GC_CN_MASK U_MASK(U_GENERAL_OTHER_TYPES) - -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_LU_MASK U_MASK(U_UPPERCASE_LETTER) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_LL_MASK U_MASK(U_LOWERCASE_LETTER) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_LT_MASK U_MASK(U_TITLECASE_LETTER) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_LM_MASK U_MASK(U_MODIFIER_LETTER) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_LO_MASK U_MASK(U_OTHER_LETTER) - -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_MN_MASK U_MASK(U_NON_SPACING_MARK) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_ME_MASK U_MASK(U_ENCLOSING_MARK) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_MC_MASK U_MASK(U_COMBINING_SPACING_MARK) - -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_ND_MASK U_MASK(U_DECIMAL_DIGIT_NUMBER) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_NL_MASK U_MASK(U_LETTER_NUMBER) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_NO_MASK U_MASK(U_OTHER_NUMBER) - -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_ZS_MASK U_MASK(U_SPACE_SEPARATOR) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_ZL_MASK U_MASK(U_LINE_SEPARATOR) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_ZP_MASK U_MASK(U_PARAGRAPH_SEPARATOR) - -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_CC_MASK U_MASK(U_CONTROL_CHAR) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_CF_MASK U_MASK(U_FORMAT_CHAR) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_CO_MASK U_MASK(U_PRIVATE_USE_CHAR) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_CS_MASK U_MASK(U_SURROGATE) - -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_PD_MASK U_MASK(U_DASH_PUNCTUATION) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_PS_MASK U_MASK(U_START_PUNCTUATION) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_PE_MASK U_MASK(U_END_PUNCTUATION) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_PC_MASK U_MASK(U_CONNECTOR_PUNCTUATION) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_PO_MASK U_MASK(U_OTHER_PUNCTUATION) - -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_SM_MASK U_MASK(U_MATH_SYMBOL) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_SC_MASK U_MASK(U_CURRENCY_SYMBOL) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_SK_MASK U_MASK(U_MODIFIER_SYMBOL) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_SO_MASK U_MASK(U_OTHER_SYMBOL) - -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_PI_MASK U_MASK(U_INITIAL_PUNCTUATION) -/** Mask constant for a UCharCategory. @stable ICU 2.1 */ -#define U_GC_PF_MASK U_MASK(U_FINAL_PUNCTUATION) - - -/** Mask constant for multiple UCharCategory bits (L Letters). @stable ICU 2.1 */ -#define U_GC_L_MASK \ - (U_GC_LU_MASK|U_GC_LL_MASK|U_GC_LT_MASK|U_GC_LM_MASK|U_GC_LO_MASK) - -/** Mask constant for multiple UCharCategory bits (LC Cased Letters). @stable ICU 2.1 */ -#define U_GC_LC_MASK \ - (U_GC_LU_MASK|U_GC_LL_MASK|U_GC_LT_MASK) - -/** Mask constant for multiple UCharCategory bits (M Marks). @stable ICU 2.1 */ -#define U_GC_M_MASK (U_GC_MN_MASK|U_GC_ME_MASK|U_GC_MC_MASK) - -/** Mask constant for multiple UCharCategory bits (N Numbers). @stable ICU 2.1 */ -#define U_GC_N_MASK (U_GC_ND_MASK|U_GC_NL_MASK|U_GC_NO_MASK) - -/** Mask constant for multiple UCharCategory bits (Z Separators). @stable ICU 2.1 */ -#define U_GC_Z_MASK (U_GC_ZS_MASK|U_GC_ZL_MASK|U_GC_ZP_MASK) - -/** Mask constant for multiple UCharCategory bits (C Others). @stable ICU 2.1 */ -#define U_GC_C_MASK \ - (U_GC_CN_MASK|U_GC_CC_MASK|U_GC_CF_MASK|U_GC_CO_MASK|U_GC_CS_MASK) - -/** Mask constant for multiple UCharCategory bits (P Punctuation). @stable ICU 2.1 */ -#define U_GC_P_MASK \ - (U_GC_PD_MASK|U_GC_PS_MASK|U_GC_PE_MASK|U_GC_PC_MASK|U_GC_PO_MASK| \ - U_GC_PI_MASK|U_GC_PF_MASK) - -/** Mask constant for multiple UCharCategory bits (S Symbols). @stable ICU 2.1 */ -#define U_GC_S_MASK (U_GC_SM_MASK|U_GC_SC_MASK|U_GC_SK_MASK|U_GC_SO_MASK) - -/** - * This specifies the language directional property of a character set. - * @stable ICU 2.0 - */ -typedef enum UCharDirection { - /* - * Note: UCharDirection constants and their API comments are parsed by preparseucd.py. - * It matches pairs of lines like - * / ** comment... * / - * U_<[A-Z_]+> = , - */ - - /** L @stable ICU 2.0 */ - U_LEFT_TO_RIGHT = 0, - /** R @stable ICU 2.0 */ - U_RIGHT_TO_LEFT = 1, - /** EN @stable ICU 2.0 */ - U_EUROPEAN_NUMBER = 2, - /** ES @stable ICU 2.0 */ - U_EUROPEAN_NUMBER_SEPARATOR = 3, - /** ET @stable ICU 2.0 */ - U_EUROPEAN_NUMBER_TERMINATOR = 4, - /** AN @stable ICU 2.0 */ - U_ARABIC_NUMBER = 5, - /** CS @stable ICU 2.0 */ - U_COMMON_NUMBER_SEPARATOR = 6, - /** B @stable ICU 2.0 */ - U_BLOCK_SEPARATOR = 7, - /** S @stable ICU 2.0 */ - U_SEGMENT_SEPARATOR = 8, - /** WS @stable ICU 2.0 */ - U_WHITE_SPACE_NEUTRAL = 9, - /** ON @stable ICU 2.0 */ - U_OTHER_NEUTRAL = 10, - /** LRE @stable ICU 2.0 */ - U_LEFT_TO_RIGHT_EMBEDDING = 11, - /** LRO @stable ICU 2.0 */ - U_LEFT_TO_RIGHT_OVERRIDE = 12, - /** AL @stable ICU 2.0 */ - U_RIGHT_TO_LEFT_ARABIC = 13, - /** RLE @stable ICU 2.0 */ - U_RIGHT_TO_LEFT_EMBEDDING = 14, - /** RLO @stable ICU 2.0 */ - U_RIGHT_TO_LEFT_OVERRIDE = 15, - /** PDF @stable ICU 2.0 */ - U_POP_DIRECTIONAL_FORMAT = 16, - /** NSM @stable ICU 2.0 */ - U_DIR_NON_SPACING_MARK = 17, - /** BN @stable ICU 2.0 */ - U_BOUNDARY_NEUTRAL = 18, - /** FSI @stable ICU 52 */ - U_FIRST_STRONG_ISOLATE = 19, - /** LRI @stable ICU 52 */ - U_LEFT_TO_RIGHT_ISOLATE = 20, - /** RLI @stable ICU 52 */ - U_RIGHT_TO_LEFT_ISOLATE = 21, - /** PDI @stable ICU 52 */ - U_POP_DIRECTIONAL_ISOLATE = 22, -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest UCharDirection value. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_BIDI_CLASS). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_CHAR_DIRECTION_COUNT -#endif // U_HIDE_DEPRECATED_API -} UCharDirection; - -/** - * Bidi Paired Bracket Type constants. - * - * @see UCHAR_BIDI_PAIRED_BRACKET_TYPE - * @stable ICU 52 - */ -typedef enum UBidiPairedBracketType { - /* - * Note: UBidiPairedBracketType constants are parsed by preparseucd.py. - * It matches lines like - * U_BPT_ - */ - - /** Not a paired bracket. @stable ICU 52 */ - U_BPT_NONE, - /** Open paired bracket. @stable ICU 52 */ - U_BPT_OPEN, - /** Close paired bracket. @stable ICU 52 */ - U_BPT_CLOSE, -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal UBidiPairedBracketType value. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_BIDI_PAIRED_BRACKET_TYPE). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_BPT_COUNT /* 3 */ -#endif // U_HIDE_DEPRECATED_API -} UBidiPairedBracketType; - -/** - * Constants for Unicode blocks, see the Unicode Data file Blocks.txt - * @stable ICU 2.0 - */ -enum UBlockCode { - /* - * Note: UBlockCode constants are parsed by preparseucd.py. - * It matches lines like - * UBLOCK_ = , - */ - - /** New No_Block value in Unicode 4. @stable ICU 2.6 */ - UBLOCK_NO_BLOCK = 0, /*[none]*/ /* Special range indicating No_Block */ - - /** @stable ICU 2.0 */ - UBLOCK_BASIC_LATIN = 1, /*[0000]*/ - - /** @stable ICU 2.0 */ - UBLOCK_LATIN_1_SUPPLEMENT=2, /*[0080]*/ - - /** @stable ICU 2.0 */ - UBLOCK_LATIN_EXTENDED_A =3, /*[0100]*/ - - /** @stable ICU 2.0 */ - UBLOCK_LATIN_EXTENDED_B =4, /*[0180]*/ - - /** @stable ICU 2.0 */ - UBLOCK_IPA_EXTENSIONS =5, /*[0250]*/ - - /** @stable ICU 2.0 */ - UBLOCK_SPACING_MODIFIER_LETTERS =6, /*[02B0]*/ - - /** @stable ICU 2.0 */ - UBLOCK_COMBINING_DIACRITICAL_MARKS =7, /*[0300]*/ - - /** - * Unicode 3.2 renames this block to "Greek and Coptic". - * @stable ICU 2.0 - */ - UBLOCK_GREEK =8, /*[0370]*/ - - /** @stable ICU 2.0 */ - UBLOCK_CYRILLIC =9, /*[0400]*/ - - /** @stable ICU 2.0 */ - UBLOCK_ARMENIAN =10, /*[0530]*/ - - /** @stable ICU 2.0 */ - UBLOCK_HEBREW =11, /*[0590]*/ - - /** @stable ICU 2.0 */ - UBLOCK_ARABIC =12, /*[0600]*/ - - /** @stable ICU 2.0 */ - UBLOCK_SYRIAC =13, /*[0700]*/ - - /** @stable ICU 2.0 */ - UBLOCK_THAANA =14, /*[0780]*/ - - /** @stable ICU 2.0 */ - UBLOCK_DEVANAGARI =15, /*[0900]*/ - - /** @stable ICU 2.0 */ - UBLOCK_BENGALI =16, /*[0980]*/ - - /** @stable ICU 2.0 */ - UBLOCK_GURMUKHI =17, /*[0A00]*/ - - /** @stable ICU 2.0 */ - UBLOCK_GUJARATI =18, /*[0A80]*/ - - /** @stable ICU 2.0 */ - UBLOCK_ORIYA =19, /*[0B00]*/ - - /** @stable ICU 2.0 */ - UBLOCK_TAMIL =20, /*[0B80]*/ - - /** @stable ICU 2.0 */ - UBLOCK_TELUGU =21, /*[0C00]*/ - - /** @stable ICU 2.0 */ - UBLOCK_KANNADA =22, /*[0C80]*/ - - /** @stable ICU 2.0 */ - UBLOCK_MALAYALAM =23, /*[0D00]*/ - - /** @stable ICU 2.0 */ - UBLOCK_SINHALA =24, /*[0D80]*/ - - /** @stable ICU 2.0 */ - UBLOCK_THAI =25, /*[0E00]*/ - - /** @stable ICU 2.0 */ - UBLOCK_LAO =26, /*[0E80]*/ - - /** @stable ICU 2.0 */ - UBLOCK_TIBETAN =27, /*[0F00]*/ - - /** @stable ICU 2.0 */ - UBLOCK_MYANMAR =28, /*[1000]*/ - - /** @stable ICU 2.0 */ - UBLOCK_GEORGIAN =29, /*[10A0]*/ - - /** @stable ICU 2.0 */ - UBLOCK_HANGUL_JAMO =30, /*[1100]*/ - - /** @stable ICU 2.0 */ - UBLOCK_ETHIOPIC =31, /*[1200]*/ - - /** @stable ICU 2.0 */ - UBLOCK_CHEROKEE =32, /*[13A0]*/ - - /** @stable ICU 2.0 */ - UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS =33, /*[1400]*/ - - /** @stable ICU 2.0 */ - UBLOCK_OGHAM =34, /*[1680]*/ - - /** @stable ICU 2.0 */ - UBLOCK_RUNIC =35, /*[16A0]*/ - - /** @stable ICU 2.0 */ - UBLOCK_KHMER =36, /*[1780]*/ - - /** @stable ICU 2.0 */ - UBLOCK_MONGOLIAN =37, /*[1800]*/ - - /** @stable ICU 2.0 */ - UBLOCK_LATIN_EXTENDED_ADDITIONAL =38, /*[1E00]*/ - - /** @stable ICU 2.0 */ - UBLOCK_GREEK_EXTENDED =39, /*[1F00]*/ - - /** @stable ICU 2.0 */ - UBLOCK_GENERAL_PUNCTUATION =40, /*[2000]*/ - - /** @stable ICU 2.0 */ - UBLOCK_SUPERSCRIPTS_AND_SUBSCRIPTS =41, /*[2070]*/ - - /** @stable ICU 2.0 */ - UBLOCK_CURRENCY_SYMBOLS =42, /*[20A0]*/ - - /** - * Unicode 3.2 renames this block to "Combining Diacritical Marks for Symbols". - * @stable ICU 2.0 - */ - UBLOCK_COMBINING_MARKS_FOR_SYMBOLS =43, /*[20D0]*/ - - /** @stable ICU 2.0 */ - UBLOCK_LETTERLIKE_SYMBOLS =44, /*[2100]*/ - - /** @stable ICU 2.0 */ - UBLOCK_NUMBER_FORMS =45, /*[2150]*/ - - /** @stable ICU 2.0 */ - UBLOCK_ARROWS =46, /*[2190]*/ - - /** @stable ICU 2.0 */ - UBLOCK_MATHEMATICAL_OPERATORS =47, /*[2200]*/ - - /** @stable ICU 2.0 */ - UBLOCK_MISCELLANEOUS_TECHNICAL =48, /*[2300]*/ - - /** @stable ICU 2.0 */ - UBLOCK_CONTROL_PICTURES =49, /*[2400]*/ - - /** @stable ICU 2.0 */ - UBLOCK_OPTICAL_CHARACTER_RECOGNITION =50, /*[2440]*/ - - /** @stable ICU 2.0 */ - UBLOCK_ENCLOSED_ALPHANUMERICS =51, /*[2460]*/ - - /** @stable ICU 2.0 */ - UBLOCK_BOX_DRAWING =52, /*[2500]*/ - - /** @stable ICU 2.0 */ - UBLOCK_BLOCK_ELEMENTS =53, /*[2580]*/ - - /** @stable ICU 2.0 */ - UBLOCK_GEOMETRIC_SHAPES =54, /*[25A0]*/ - - /** @stable ICU 2.0 */ - UBLOCK_MISCELLANEOUS_SYMBOLS =55, /*[2600]*/ - - /** @stable ICU 2.0 */ - UBLOCK_DINGBATS =56, /*[2700]*/ - - /** @stable ICU 2.0 */ - UBLOCK_BRAILLE_PATTERNS =57, /*[2800]*/ - - /** @stable ICU 2.0 */ - UBLOCK_CJK_RADICALS_SUPPLEMENT =58, /*[2E80]*/ - - /** @stable ICU 2.0 */ - UBLOCK_KANGXI_RADICALS =59, /*[2F00]*/ - - /** @stable ICU 2.0 */ - UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS =60, /*[2FF0]*/ - - /** @stable ICU 2.0 */ - UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION =61, /*[3000]*/ - - /** @stable ICU 2.0 */ - UBLOCK_HIRAGANA =62, /*[3040]*/ - - /** @stable ICU 2.0 */ - UBLOCK_KATAKANA =63, /*[30A0]*/ - - /** @stable ICU 2.0 */ - UBLOCK_BOPOMOFO =64, /*[3100]*/ - - /** @stable ICU 2.0 */ - UBLOCK_HANGUL_COMPATIBILITY_JAMO =65, /*[3130]*/ - - /** @stable ICU 2.0 */ - UBLOCK_KANBUN =66, /*[3190]*/ - - /** @stable ICU 2.0 */ - UBLOCK_BOPOMOFO_EXTENDED =67, /*[31A0]*/ - - /** @stable ICU 2.0 */ - UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS =68, /*[3200]*/ - - /** @stable ICU 2.0 */ - UBLOCK_CJK_COMPATIBILITY =69, /*[3300]*/ - - /** @stable ICU 2.0 */ - UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A =70, /*[3400]*/ - - /** @stable ICU 2.0 */ - UBLOCK_CJK_UNIFIED_IDEOGRAPHS =71, /*[4E00]*/ - - /** @stable ICU 2.0 */ - UBLOCK_YI_SYLLABLES =72, /*[A000]*/ - - /** @stable ICU 2.0 */ - UBLOCK_YI_RADICALS =73, /*[A490]*/ - - /** @stable ICU 2.0 */ - UBLOCK_HANGUL_SYLLABLES =74, /*[AC00]*/ - - /** @stable ICU 2.0 */ - UBLOCK_HIGH_SURROGATES =75, /*[D800]*/ - - /** @stable ICU 2.0 */ - UBLOCK_HIGH_PRIVATE_USE_SURROGATES =76, /*[DB80]*/ - - /** @stable ICU 2.0 */ - UBLOCK_LOW_SURROGATES =77, /*[DC00]*/ - - /** - * Same as UBLOCK_PRIVATE_USE. - * Until Unicode 3.1.1, the corresponding block name was "Private Use", - * and multiple code point ranges had this block. - * Unicode 3.2 renames the block for the BMP PUA to "Private Use Area" and - * adds separate blocks for the supplementary PUAs. - * - * @stable ICU 2.0 - */ - UBLOCK_PRIVATE_USE_AREA =78, /*[E000]*/ - /** - * Same as UBLOCK_PRIVATE_USE_AREA. - * Until Unicode 3.1.1, the corresponding block name was "Private Use", - * and multiple code point ranges had this block. - * Unicode 3.2 renames the block for the BMP PUA to "Private Use Area" and - * adds separate blocks for the supplementary PUAs. - * - * @stable ICU 2.0 - */ - UBLOCK_PRIVATE_USE = UBLOCK_PRIVATE_USE_AREA, - - /** @stable ICU 2.0 */ - UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS =79, /*[F900]*/ - - /** @stable ICU 2.0 */ - UBLOCK_ALPHABETIC_PRESENTATION_FORMS =80, /*[FB00]*/ - - /** @stable ICU 2.0 */ - UBLOCK_ARABIC_PRESENTATION_FORMS_A =81, /*[FB50]*/ - - /** @stable ICU 2.0 */ - UBLOCK_COMBINING_HALF_MARKS =82, /*[FE20]*/ - - /** @stable ICU 2.0 */ - UBLOCK_CJK_COMPATIBILITY_FORMS =83, /*[FE30]*/ - - /** @stable ICU 2.0 */ - UBLOCK_SMALL_FORM_VARIANTS =84, /*[FE50]*/ - - /** @stable ICU 2.0 */ - UBLOCK_ARABIC_PRESENTATION_FORMS_B =85, /*[FE70]*/ - - /** @stable ICU 2.0 */ - UBLOCK_SPECIALS =86, /*[FFF0]*/ - - /** @stable ICU 2.0 */ - UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS =87, /*[FF00]*/ - - /* New blocks in Unicode 3.1 */ - - /** @stable ICU 2.0 */ - UBLOCK_OLD_ITALIC = 88, /*[10300]*/ - /** @stable ICU 2.0 */ - UBLOCK_GOTHIC = 89, /*[10330]*/ - /** @stable ICU 2.0 */ - UBLOCK_DESERET = 90, /*[10400]*/ - /** @stable ICU 2.0 */ - UBLOCK_BYZANTINE_MUSICAL_SYMBOLS = 91, /*[1D000]*/ - /** @stable ICU 2.0 */ - UBLOCK_MUSICAL_SYMBOLS = 92, /*[1D100]*/ - /** @stable ICU 2.0 */ - UBLOCK_MATHEMATICAL_ALPHANUMERIC_SYMBOLS = 93, /*[1D400]*/ - /** @stable ICU 2.0 */ - UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B = 94, /*[20000]*/ - /** @stable ICU 2.0 */ - UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT = 95, /*[2F800]*/ - /** @stable ICU 2.0 */ - UBLOCK_TAGS = 96, /*[E0000]*/ - - /* New blocks in Unicode 3.2 */ - - /** @stable ICU 3.0 */ - UBLOCK_CYRILLIC_SUPPLEMENT = 97, /*[0500]*/ - /** - * Unicode 4.0.1 renames the "Cyrillic Supplementary" block to "Cyrillic Supplement". - * @stable ICU 2.2 - */ - UBLOCK_CYRILLIC_SUPPLEMENTARY = UBLOCK_CYRILLIC_SUPPLEMENT, - /** @stable ICU 2.2 */ - UBLOCK_TAGALOG = 98, /*[1700]*/ - /** @stable ICU 2.2 */ - UBLOCK_HANUNOO = 99, /*[1720]*/ - /** @stable ICU 2.2 */ - UBLOCK_BUHID = 100, /*[1740]*/ - /** @stable ICU 2.2 */ - UBLOCK_TAGBANWA = 101, /*[1760]*/ - /** @stable ICU 2.2 */ - UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A = 102, /*[27C0]*/ - /** @stable ICU 2.2 */ - UBLOCK_SUPPLEMENTAL_ARROWS_A = 103, /*[27F0]*/ - /** @stable ICU 2.2 */ - UBLOCK_SUPPLEMENTAL_ARROWS_B = 104, /*[2900]*/ - /** @stable ICU 2.2 */ - UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B = 105, /*[2980]*/ - /** @stable ICU 2.2 */ - UBLOCK_SUPPLEMENTAL_MATHEMATICAL_OPERATORS = 106, /*[2A00]*/ - /** @stable ICU 2.2 */ - UBLOCK_KATAKANA_PHONETIC_EXTENSIONS = 107, /*[31F0]*/ - /** @stable ICU 2.2 */ - UBLOCK_VARIATION_SELECTORS = 108, /*[FE00]*/ - /** @stable ICU 2.2 */ - UBLOCK_SUPPLEMENTARY_PRIVATE_USE_AREA_A = 109, /*[F0000]*/ - /** @stable ICU 2.2 */ - UBLOCK_SUPPLEMENTARY_PRIVATE_USE_AREA_B = 110, /*[100000]*/ - - /* New blocks in Unicode 4 */ - - /** @stable ICU 2.6 */ - UBLOCK_LIMBU = 111, /*[1900]*/ - /** @stable ICU 2.6 */ - UBLOCK_TAI_LE = 112, /*[1950]*/ - /** @stable ICU 2.6 */ - UBLOCK_KHMER_SYMBOLS = 113, /*[19E0]*/ - /** @stable ICU 2.6 */ - UBLOCK_PHONETIC_EXTENSIONS = 114, /*[1D00]*/ - /** @stable ICU 2.6 */ - UBLOCK_MISCELLANEOUS_SYMBOLS_AND_ARROWS = 115, /*[2B00]*/ - /** @stable ICU 2.6 */ - UBLOCK_YIJING_HEXAGRAM_SYMBOLS = 116, /*[4DC0]*/ - /** @stable ICU 2.6 */ - UBLOCK_LINEAR_B_SYLLABARY = 117, /*[10000]*/ - /** @stable ICU 2.6 */ - UBLOCK_LINEAR_B_IDEOGRAMS = 118, /*[10080]*/ - /** @stable ICU 2.6 */ - UBLOCK_AEGEAN_NUMBERS = 119, /*[10100]*/ - /** @stable ICU 2.6 */ - UBLOCK_UGARITIC = 120, /*[10380]*/ - /** @stable ICU 2.6 */ - UBLOCK_SHAVIAN = 121, /*[10450]*/ - /** @stable ICU 2.6 */ - UBLOCK_OSMANYA = 122, /*[10480]*/ - /** @stable ICU 2.6 */ - UBLOCK_CYPRIOT_SYLLABARY = 123, /*[10800]*/ - /** @stable ICU 2.6 */ - UBLOCK_TAI_XUAN_JING_SYMBOLS = 124, /*[1D300]*/ - /** @stable ICU 2.6 */ - UBLOCK_VARIATION_SELECTORS_SUPPLEMENT = 125, /*[E0100]*/ - - /* New blocks in Unicode 4.1 */ - - /** @stable ICU 3.4 */ - UBLOCK_ANCIENT_GREEK_MUSICAL_NOTATION = 126, /*[1D200]*/ - /** @stable ICU 3.4 */ - UBLOCK_ANCIENT_GREEK_NUMBERS = 127, /*[10140]*/ - /** @stable ICU 3.4 */ - UBLOCK_ARABIC_SUPPLEMENT = 128, /*[0750]*/ - /** @stable ICU 3.4 */ - UBLOCK_BUGINESE = 129, /*[1A00]*/ - /** @stable ICU 3.4 */ - UBLOCK_CJK_STROKES = 130, /*[31C0]*/ - /** @stable ICU 3.4 */ - UBLOCK_COMBINING_DIACRITICAL_MARKS_SUPPLEMENT = 131, /*[1DC0]*/ - /** @stable ICU 3.4 */ - UBLOCK_COPTIC = 132, /*[2C80]*/ - /** @stable ICU 3.4 */ - UBLOCK_ETHIOPIC_EXTENDED = 133, /*[2D80]*/ - /** @stable ICU 3.4 */ - UBLOCK_ETHIOPIC_SUPPLEMENT = 134, /*[1380]*/ - /** @stable ICU 3.4 */ - UBLOCK_GEORGIAN_SUPPLEMENT = 135, /*[2D00]*/ - /** @stable ICU 3.4 */ - UBLOCK_GLAGOLITIC = 136, /*[2C00]*/ - /** @stable ICU 3.4 */ - UBLOCK_KHAROSHTHI = 137, /*[10A00]*/ - /** @stable ICU 3.4 */ - UBLOCK_MODIFIER_TONE_LETTERS = 138, /*[A700]*/ - /** @stable ICU 3.4 */ - UBLOCK_NEW_TAI_LUE = 139, /*[1980]*/ - /** @stable ICU 3.4 */ - UBLOCK_OLD_PERSIAN = 140, /*[103A0]*/ - /** @stable ICU 3.4 */ - UBLOCK_PHONETIC_EXTENSIONS_SUPPLEMENT = 141, /*[1D80]*/ - /** @stable ICU 3.4 */ - UBLOCK_SUPPLEMENTAL_PUNCTUATION = 142, /*[2E00]*/ - /** @stable ICU 3.4 */ - UBLOCK_SYLOTI_NAGRI = 143, /*[A800]*/ - /** @stable ICU 3.4 */ - UBLOCK_TIFINAGH = 144, /*[2D30]*/ - /** @stable ICU 3.4 */ - UBLOCK_VERTICAL_FORMS = 145, /*[FE10]*/ - - /* New blocks in Unicode 5.0 */ - - /** @stable ICU 3.6 */ - UBLOCK_NKO = 146, /*[07C0]*/ - /** @stable ICU 3.6 */ - UBLOCK_BALINESE = 147, /*[1B00]*/ - /** @stable ICU 3.6 */ - UBLOCK_LATIN_EXTENDED_C = 148, /*[2C60]*/ - /** @stable ICU 3.6 */ - UBLOCK_LATIN_EXTENDED_D = 149, /*[A720]*/ - /** @stable ICU 3.6 */ - UBLOCK_PHAGS_PA = 150, /*[A840]*/ - /** @stable ICU 3.6 */ - UBLOCK_PHOENICIAN = 151, /*[10900]*/ - /** @stable ICU 3.6 */ - UBLOCK_CUNEIFORM = 152, /*[12000]*/ - /** @stable ICU 3.6 */ - UBLOCK_CUNEIFORM_NUMBERS_AND_PUNCTUATION = 153, /*[12400]*/ - /** @stable ICU 3.6 */ - UBLOCK_COUNTING_ROD_NUMERALS = 154, /*[1D360]*/ - - /* New blocks in Unicode 5.1 */ - - /** @stable ICU 4.0 */ - UBLOCK_SUNDANESE = 155, /*[1B80]*/ - /** @stable ICU 4.0 */ - UBLOCK_LEPCHA = 156, /*[1C00]*/ - /** @stable ICU 4.0 */ - UBLOCK_OL_CHIKI = 157, /*[1C50]*/ - /** @stable ICU 4.0 */ - UBLOCK_CYRILLIC_EXTENDED_A = 158, /*[2DE0]*/ - /** @stable ICU 4.0 */ - UBLOCK_VAI = 159, /*[A500]*/ - /** @stable ICU 4.0 */ - UBLOCK_CYRILLIC_EXTENDED_B = 160, /*[A640]*/ - /** @stable ICU 4.0 */ - UBLOCK_SAURASHTRA = 161, /*[A880]*/ - /** @stable ICU 4.0 */ - UBLOCK_KAYAH_LI = 162, /*[A900]*/ - /** @stable ICU 4.0 */ - UBLOCK_REJANG = 163, /*[A930]*/ - /** @stable ICU 4.0 */ - UBLOCK_CHAM = 164, /*[AA00]*/ - /** @stable ICU 4.0 */ - UBLOCK_ANCIENT_SYMBOLS = 165, /*[10190]*/ - /** @stable ICU 4.0 */ - UBLOCK_PHAISTOS_DISC = 166, /*[101D0]*/ - /** @stable ICU 4.0 */ - UBLOCK_LYCIAN = 167, /*[10280]*/ - /** @stable ICU 4.0 */ - UBLOCK_CARIAN = 168, /*[102A0]*/ - /** @stable ICU 4.0 */ - UBLOCK_LYDIAN = 169, /*[10920]*/ - /** @stable ICU 4.0 */ - UBLOCK_MAHJONG_TILES = 170, /*[1F000]*/ - /** @stable ICU 4.0 */ - UBLOCK_DOMINO_TILES = 171, /*[1F030]*/ - - /* New blocks in Unicode 5.2 */ - - /** @stable ICU 4.4 */ - UBLOCK_SAMARITAN = 172, /*[0800]*/ - /** @stable ICU 4.4 */ - UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED = 173, /*[18B0]*/ - /** @stable ICU 4.4 */ - UBLOCK_TAI_THAM = 174, /*[1A20]*/ - /** @stable ICU 4.4 */ - UBLOCK_VEDIC_EXTENSIONS = 175, /*[1CD0]*/ - /** @stable ICU 4.4 */ - UBLOCK_LISU = 176, /*[A4D0]*/ - /** @stable ICU 4.4 */ - UBLOCK_BAMUM = 177, /*[A6A0]*/ - /** @stable ICU 4.4 */ - UBLOCK_COMMON_INDIC_NUMBER_FORMS = 178, /*[A830]*/ - /** @stable ICU 4.4 */ - UBLOCK_DEVANAGARI_EXTENDED = 179, /*[A8E0]*/ - /** @stable ICU 4.4 */ - UBLOCK_HANGUL_JAMO_EXTENDED_A = 180, /*[A960]*/ - /** @stable ICU 4.4 */ - UBLOCK_JAVANESE = 181, /*[A980]*/ - /** @stable ICU 4.4 */ - UBLOCK_MYANMAR_EXTENDED_A = 182, /*[AA60]*/ - /** @stable ICU 4.4 */ - UBLOCK_TAI_VIET = 183, /*[AA80]*/ - /** @stable ICU 4.4 */ - UBLOCK_MEETEI_MAYEK = 184, /*[ABC0]*/ - /** @stable ICU 4.4 */ - UBLOCK_HANGUL_JAMO_EXTENDED_B = 185, /*[D7B0]*/ - /** @stable ICU 4.4 */ - UBLOCK_IMPERIAL_ARAMAIC = 186, /*[10840]*/ - /** @stable ICU 4.4 */ - UBLOCK_OLD_SOUTH_ARABIAN = 187, /*[10A60]*/ - /** @stable ICU 4.4 */ - UBLOCK_AVESTAN = 188, /*[10B00]*/ - /** @stable ICU 4.4 */ - UBLOCK_INSCRIPTIONAL_PARTHIAN = 189, /*[10B40]*/ - /** @stable ICU 4.4 */ - UBLOCK_INSCRIPTIONAL_PAHLAVI = 190, /*[10B60]*/ - /** @stable ICU 4.4 */ - UBLOCK_OLD_TURKIC = 191, /*[10C00]*/ - /** @stable ICU 4.4 */ - UBLOCK_RUMI_NUMERAL_SYMBOLS = 192, /*[10E60]*/ - /** @stable ICU 4.4 */ - UBLOCK_KAITHI = 193, /*[11080]*/ - /** @stable ICU 4.4 */ - UBLOCK_EGYPTIAN_HIEROGLYPHS = 194, /*[13000]*/ - /** @stable ICU 4.4 */ - UBLOCK_ENCLOSED_ALPHANUMERIC_SUPPLEMENT = 195, /*[1F100]*/ - /** @stable ICU 4.4 */ - UBLOCK_ENCLOSED_IDEOGRAPHIC_SUPPLEMENT = 196, /*[1F200]*/ - /** @stable ICU 4.4 */ - UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C = 197, /*[2A700]*/ - - /* New blocks in Unicode 6.0 */ - - /** @stable ICU 4.6 */ - UBLOCK_MANDAIC = 198, /*[0840]*/ - /** @stable ICU 4.6 */ - UBLOCK_BATAK = 199, /*[1BC0]*/ - /** @stable ICU 4.6 */ - UBLOCK_ETHIOPIC_EXTENDED_A = 200, /*[AB00]*/ - /** @stable ICU 4.6 */ - UBLOCK_BRAHMI = 201, /*[11000]*/ - /** @stable ICU 4.6 */ - UBLOCK_BAMUM_SUPPLEMENT = 202, /*[16800]*/ - /** @stable ICU 4.6 */ - UBLOCK_KANA_SUPPLEMENT = 203, /*[1B000]*/ - /** @stable ICU 4.6 */ - UBLOCK_PLAYING_CARDS = 204, /*[1F0A0]*/ - /** @stable ICU 4.6 */ - UBLOCK_MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS = 205, /*[1F300]*/ - /** @stable ICU 4.6 */ - UBLOCK_EMOTICONS = 206, /*[1F600]*/ - /** @stable ICU 4.6 */ - UBLOCK_TRANSPORT_AND_MAP_SYMBOLS = 207, /*[1F680]*/ - /** @stable ICU 4.6 */ - UBLOCK_ALCHEMICAL_SYMBOLS = 208, /*[1F700]*/ - /** @stable ICU 4.6 */ - UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D = 209, /*[2B740]*/ - - /* New blocks in Unicode 6.1 */ - - /** @stable ICU 49 */ - UBLOCK_ARABIC_EXTENDED_A = 210, /*[08A0]*/ - /** @stable ICU 49 */ - UBLOCK_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS = 211, /*[1EE00]*/ - /** @stable ICU 49 */ - UBLOCK_CHAKMA = 212, /*[11100]*/ - /** @stable ICU 49 */ - UBLOCK_MEETEI_MAYEK_EXTENSIONS = 213, /*[AAE0]*/ - /** @stable ICU 49 */ - UBLOCK_MEROITIC_CURSIVE = 214, /*[109A0]*/ - /** @stable ICU 49 */ - UBLOCK_MEROITIC_HIEROGLYPHS = 215, /*[10980]*/ - /** @stable ICU 49 */ - UBLOCK_MIAO = 216, /*[16F00]*/ - /** @stable ICU 49 */ - UBLOCK_SHARADA = 217, /*[11180]*/ - /** @stable ICU 49 */ - UBLOCK_SORA_SOMPENG = 218, /*[110D0]*/ - /** @stable ICU 49 */ - UBLOCK_SUNDANESE_SUPPLEMENT = 219, /*[1CC0]*/ - /** @stable ICU 49 */ - UBLOCK_TAKRI = 220, /*[11680]*/ - - /* New blocks in Unicode 7.0 */ - - /** @stable ICU 54 */ - UBLOCK_BASSA_VAH = 221, /*[16AD0]*/ - /** @stable ICU 54 */ - UBLOCK_CAUCASIAN_ALBANIAN = 222, /*[10530]*/ - /** @stable ICU 54 */ - UBLOCK_COPTIC_EPACT_NUMBERS = 223, /*[102E0]*/ - /** @stable ICU 54 */ - UBLOCK_COMBINING_DIACRITICAL_MARKS_EXTENDED = 224, /*[1AB0]*/ - /** @stable ICU 54 */ - UBLOCK_DUPLOYAN = 225, /*[1BC00]*/ - /** @stable ICU 54 */ - UBLOCK_ELBASAN = 226, /*[10500]*/ - /** @stable ICU 54 */ - UBLOCK_GEOMETRIC_SHAPES_EXTENDED = 227, /*[1F780]*/ - /** @stable ICU 54 */ - UBLOCK_GRANTHA = 228, /*[11300]*/ - /** @stable ICU 54 */ - UBLOCK_KHOJKI = 229, /*[11200]*/ - /** @stable ICU 54 */ - UBLOCK_KHUDAWADI = 230, /*[112B0]*/ - /** @stable ICU 54 */ - UBLOCK_LATIN_EXTENDED_E = 231, /*[AB30]*/ - /** @stable ICU 54 */ - UBLOCK_LINEAR_A = 232, /*[10600]*/ - /** @stable ICU 54 */ - UBLOCK_MAHAJANI = 233, /*[11150]*/ - /** @stable ICU 54 */ - UBLOCK_MANICHAEAN = 234, /*[10AC0]*/ - /** @stable ICU 54 */ - UBLOCK_MENDE_KIKAKUI = 235, /*[1E800]*/ - /** @stable ICU 54 */ - UBLOCK_MODI = 236, /*[11600]*/ - /** @stable ICU 54 */ - UBLOCK_MRO = 237, /*[16A40]*/ - /** @stable ICU 54 */ - UBLOCK_MYANMAR_EXTENDED_B = 238, /*[A9E0]*/ - /** @stable ICU 54 */ - UBLOCK_NABATAEAN = 239, /*[10880]*/ - /** @stable ICU 54 */ - UBLOCK_OLD_NORTH_ARABIAN = 240, /*[10A80]*/ - /** @stable ICU 54 */ - UBLOCK_OLD_PERMIC = 241, /*[10350]*/ - /** @stable ICU 54 */ - UBLOCK_ORNAMENTAL_DINGBATS = 242, /*[1F650]*/ - /** @stable ICU 54 */ - UBLOCK_PAHAWH_HMONG = 243, /*[16B00]*/ - /** @stable ICU 54 */ - UBLOCK_PALMYRENE = 244, /*[10860]*/ - /** @stable ICU 54 */ - UBLOCK_PAU_CIN_HAU = 245, /*[11AC0]*/ - /** @stable ICU 54 */ - UBLOCK_PSALTER_PAHLAVI = 246, /*[10B80]*/ - /** @stable ICU 54 */ - UBLOCK_SHORTHAND_FORMAT_CONTROLS = 247, /*[1BCA0]*/ - /** @stable ICU 54 */ - UBLOCK_SIDDHAM = 248, /*[11580]*/ - /** @stable ICU 54 */ - UBLOCK_SINHALA_ARCHAIC_NUMBERS = 249, /*[111E0]*/ - /** @stable ICU 54 */ - UBLOCK_SUPPLEMENTAL_ARROWS_C = 250, /*[1F800]*/ - /** @stable ICU 54 */ - UBLOCK_TIRHUTA = 251, /*[11480]*/ - /** @stable ICU 54 */ - UBLOCK_WARANG_CITI = 252, /*[118A0]*/ - - /* New blocks in Unicode 8.0 */ - - /** @stable ICU 56 */ - UBLOCK_AHOM = 253, /*[11700]*/ - /** @stable ICU 56 */ - UBLOCK_ANATOLIAN_HIEROGLYPHS = 254, /*[14400]*/ - /** @stable ICU 56 */ - UBLOCK_CHEROKEE_SUPPLEMENT = 255, /*[AB70]*/ - /** @stable ICU 56 */ - UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E = 256, /*[2B820]*/ - /** @stable ICU 56 */ - UBLOCK_EARLY_DYNASTIC_CUNEIFORM = 257, /*[12480]*/ - /** @stable ICU 56 */ - UBLOCK_HATRAN = 258, /*[108E0]*/ - /** @stable ICU 56 */ - UBLOCK_MULTANI = 259, /*[11280]*/ - /** @stable ICU 56 */ - UBLOCK_OLD_HUNGARIAN = 260, /*[10C80]*/ - /** @stable ICU 56 */ - UBLOCK_SUPPLEMENTAL_SYMBOLS_AND_PICTOGRAPHS = 261, /*[1F900]*/ - /** @stable ICU 56 */ - UBLOCK_SUTTON_SIGNWRITING = 262, /*[1D800]*/ - - /* New blocks in Unicode 9.0 */ - - /** @stable ICU 58 */ - UBLOCK_ADLAM = 263, /*[1E900]*/ - /** @stable ICU 58 */ - UBLOCK_BHAIKSUKI = 264, /*[11C00]*/ - /** @stable ICU 58 */ - UBLOCK_CYRILLIC_EXTENDED_C = 265, /*[1C80]*/ - /** @stable ICU 58 */ - UBLOCK_GLAGOLITIC_SUPPLEMENT = 266, /*[1E000]*/ - /** @stable ICU 58 */ - UBLOCK_IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION = 267, /*[16FE0]*/ - /** @stable ICU 58 */ - UBLOCK_MARCHEN = 268, /*[11C70]*/ - /** @stable ICU 58 */ - UBLOCK_MONGOLIAN_SUPPLEMENT = 269, /*[11660]*/ - /** @stable ICU 58 */ - UBLOCK_NEWA = 270, /*[11400]*/ - /** @stable ICU 58 */ - UBLOCK_OSAGE = 271, /*[104B0]*/ - /** @stable ICU 58 */ - UBLOCK_TANGUT = 272, /*[17000]*/ - /** @stable ICU 58 */ - UBLOCK_TANGUT_COMPONENTS = 273, /*[18800]*/ - - // New blocks in Unicode 10.0 - - /** @stable ICU 60 */ - UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F = 274, /*[2CEB0]*/ - /** @stable ICU 60 */ - UBLOCK_KANA_EXTENDED_A = 275, /*[1B100]*/ - /** @stable ICU 60 */ - UBLOCK_MASARAM_GONDI = 276, /*[11D00]*/ - /** @stable ICU 60 */ - UBLOCK_NUSHU = 277, /*[1B170]*/ - /** @stable ICU 60 */ - UBLOCK_SOYOMBO = 278, /*[11A50]*/ - /** @stable ICU 60 */ - UBLOCK_SYRIAC_SUPPLEMENT = 279, /*[0860]*/ - /** @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]*/ - - // New blocks in Unicode 12.0 - - /** @stable ICU 64 */ - UBLOCK_EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS = 292, /*[13430]*/ - /** @stable ICU 64 */ - UBLOCK_ELYMAIC = 293, /*[10FE0]*/ - /** @stable ICU 64 */ - UBLOCK_NANDINAGARI = 294, /*[119A0]*/ - /** @stable ICU 64 */ - UBLOCK_NYIAKENG_PUACHUE_HMONG = 295, /*[1E100]*/ - /** @stable ICU 64 */ - UBLOCK_OTTOMAN_SIYAQ_NUMBERS = 296, /*[1ED00]*/ - /** @stable ICU 64 */ - UBLOCK_SMALL_KANA_EXTENSION = 297, /*[1B130]*/ - /** @stable ICU 64 */ - UBLOCK_SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A = 298, /*[1FA70]*/ - /** @stable ICU 64 */ - UBLOCK_TAMIL_SUPPLEMENT = 299, /*[11FC0]*/ - /** @stable ICU 64 */ - UBLOCK_WANCHO = 300, /*[1E2C0]*/ - - // New blocks in Unicode 13.0 - - /** @stable ICU 66 */ - UBLOCK_CHORASMIAN = 301, /*[10FB0]*/ - /** @stable ICU 66 */ - UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G = 302, /*[30000]*/ - /** @stable ICU 66 */ - UBLOCK_DIVES_AKURU = 303, /*[11900]*/ - /** @stable ICU 66 */ - UBLOCK_KHITAN_SMALL_SCRIPT = 304, /*[18B00]*/ - /** @stable ICU 66 */ - UBLOCK_LISU_SUPPLEMENT = 305, /*[11FB0]*/ - /** @stable ICU 66 */ - UBLOCK_SYMBOLS_FOR_LEGACY_COMPUTING = 306, /*[1FB00]*/ - /** @stable ICU 66 */ - UBLOCK_TANGUT_SUPPLEMENT = 307, /*[18D00]*/ - /** @stable ICU 66 */ - UBLOCK_YEZIDI = 308, /*[10E80]*/ - - // New blocks in Unicode 14.0 - - /** @stable ICU 70 */ - UBLOCK_ARABIC_EXTENDED_B = 309, /*[0870]*/ - /** @stable ICU 70 */ - UBLOCK_CYPRO_MINOAN = 310, /*[12F90]*/ - /** @stable ICU 70 */ - UBLOCK_ETHIOPIC_EXTENDED_B = 311, /*[1E7E0]*/ - /** @stable ICU 70 */ - UBLOCK_KANA_EXTENDED_B = 312, /*[1AFF0]*/ - /** @stable ICU 70 */ - UBLOCK_LATIN_EXTENDED_F = 313, /*[10780]*/ - /** @stable ICU 70 */ - UBLOCK_LATIN_EXTENDED_G = 314, /*[1DF00]*/ - /** @stable ICU 70 */ - UBLOCK_OLD_UYGHUR = 315, /*[10F70]*/ - /** @stable ICU 70 */ - UBLOCK_TANGSA = 316, /*[16A70]*/ - /** @stable ICU 70 */ - UBLOCK_TOTO = 317, /*[1E290]*/ - /** @stable ICU 70 */ - UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED_A = 318, /*[11AB0]*/ - /** @stable ICU 70 */ - UBLOCK_VITHKUQI = 319, /*[10570]*/ - /** @stable ICU 70 */ - UBLOCK_ZNAMENNY_MUSICAL_NOTATION = 320, /*[1CF00]*/ - - // New blocks in Unicode 15.0 - - /** @stable ICU 72 */ - UBLOCK_ARABIC_EXTENDED_C = 321, /*[10EC0]*/ - /** @stable ICU 72 */ - UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_H = 322, /*[31350]*/ - /** @stable ICU 72 */ - UBLOCK_CYRILLIC_EXTENDED_D = 323, /*[1E030]*/ - /** @stable ICU 72 */ - UBLOCK_DEVANAGARI_EXTENDED_A = 324, /*[11B00]*/ - /** @stable ICU 72 */ - UBLOCK_KAKTOVIK_NUMERALS = 325, /*[1D2C0]*/ - /** @stable ICU 72 */ - UBLOCK_KAWI = 326, /*[11F00]*/ - /** @stable ICU 72 */ - UBLOCK_NAG_MUNDARI = 327, /*[1E4D0]*/ - -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal UBlockCode value. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_BLOCK). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UBLOCK_COUNT = 328, -#endif // U_HIDE_DEPRECATED_API - - /** @stable ICU 2.0 */ - UBLOCK_INVALID_CODE=-1 -}; - -/** @stable ICU 2.0 */ -typedef enum UBlockCode UBlockCode; - -/** - * East Asian Width constants. - * - * @see UCHAR_EAST_ASIAN_WIDTH - * @see u_getIntPropertyValue - * @stable ICU 2.2 - */ -typedef enum UEastAsianWidth { - /* - * Note: UEastAsianWidth constants are parsed by preparseucd.py. - * It matches lines like - * U_EA_ - */ - - U_EA_NEUTRAL, /*[N]*/ - U_EA_AMBIGUOUS, /*[A]*/ - U_EA_HALFWIDTH, /*[H]*/ - U_EA_FULLWIDTH, /*[F]*/ - U_EA_NARROW, /*[Na]*/ - U_EA_WIDE, /*[W]*/ -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal UEastAsianWidth value. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_EAST_ASIAN_WIDTH). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_EA_COUNT -#endif // U_HIDE_DEPRECATED_API -} UEastAsianWidth; - -/** - * Selector constants for u_charName(). - * u_charName() returns the "modern" name of a - * Unicode character; or the name that was defined in - * Unicode version 1.0, before the Unicode standard merged - * with ISO-10646; or an "extended" name that gives each - * Unicode code point a unique name. - * - * @see u_charName - * @stable ICU 2.0 - */ -typedef enum UCharNameChoice { - /** Unicode character name (Name property). @stable ICU 2.0 */ - U_UNICODE_CHAR_NAME, -#ifndef U_HIDE_DEPRECATED_API - /** - * The Unicode_1_Name property value which is of little practical value. - * Beginning with ICU 49, ICU APIs return an empty string for this name choice. - * @deprecated ICU 49 - */ - U_UNICODE_10_CHAR_NAME, -#endif /* U_HIDE_DEPRECATED_API */ - /** Standard or synthetic character name. @stable ICU 2.0 */ - U_EXTENDED_CHAR_NAME = U_UNICODE_CHAR_NAME+2, - /** Corrected name from NameAliases.txt. @stable ICU 4.4 */ - U_CHAR_NAME_ALIAS, -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal UCharNameChoice value. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_CHAR_NAME_CHOICE_COUNT -#endif // U_HIDE_DEPRECATED_API -} UCharNameChoice; - -/** - * Selector constants for u_getPropertyName() and - * u_getPropertyValueName(). These selectors are used to choose which - * name is returned for a given property or value. All properties and - * values have a long name. Most have a short name, but some do not. - * Unicode allows for additional names, beyond the long and short - * name, which would be indicated by U_LONG_PROPERTY_NAME + i, where - * i=1, 2,... - * - * @see u_getPropertyName() - * @see u_getPropertyValueName() - * @stable ICU 2.4 - */ -typedef enum UPropertyNameChoice { - U_SHORT_PROPERTY_NAME, - U_LONG_PROPERTY_NAME, -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal UPropertyNameChoice value. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_PROPERTY_NAME_CHOICE_COUNT -#endif // U_HIDE_DEPRECATED_API -} UPropertyNameChoice; - -/** - * Decomposition Type constants. - * - * @see UCHAR_DECOMPOSITION_TYPE - * @stable ICU 2.2 - */ -typedef enum UDecompositionType { - /* - * Note: UDecompositionType constants are parsed by preparseucd.py. - * It matches lines like - * U_DT_ - */ - - U_DT_NONE, /*[none]*/ - U_DT_CANONICAL, /*[can]*/ - U_DT_COMPAT, /*[com]*/ - U_DT_CIRCLE, /*[enc]*/ - U_DT_FINAL, /*[fin]*/ - U_DT_FONT, /*[font]*/ - U_DT_FRACTION, /*[fra]*/ - U_DT_INITIAL, /*[init]*/ - U_DT_ISOLATED, /*[iso]*/ - U_DT_MEDIAL, /*[med]*/ - U_DT_NARROW, /*[nar]*/ - U_DT_NOBREAK, /*[nb]*/ - U_DT_SMALL, /*[sml]*/ - U_DT_SQUARE, /*[sqr]*/ - U_DT_SUB, /*[sub]*/ - U_DT_SUPER, /*[sup]*/ - U_DT_VERTICAL, /*[vert]*/ - U_DT_WIDE, /*[wide]*/ -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal UDecompositionType value. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_DECOMPOSITION_TYPE). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_DT_COUNT /* 18 */ -#endif // U_HIDE_DEPRECATED_API -} UDecompositionType; - -/** - * Joining Type constants. - * - * @see UCHAR_JOINING_TYPE - * @stable ICU 2.2 - */ -typedef enum UJoiningType { - /* - * Note: UJoiningType constants are parsed by preparseucd.py. - * It matches lines like - * U_JT_ - */ - - U_JT_NON_JOINING, /*[U]*/ - U_JT_JOIN_CAUSING, /*[C]*/ - U_JT_DUAL_JOINING, /*[D]*/ - U_JT_LEFT_JOINING, /*[L]*/ - U_JT_RIGHT_JOINING, /*[R]*/ - U_JT_TRANSPARENT, /*[T]*/ -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal UJoiningType value. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_JOINING_TYPE). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_JT_COUNT /* 6 */ -#endif // U_HIDE_DEPRECATED_API -} UJoiningType; - -/** - * Joining Group constants. - * - * @see UCHAR_JOINING_GROUP - * @stable ICU 2.2 - */ -typedef enum UJoiningGroup { - /* - * Note: UJoiningGroup constants are parsed by preparseucd.py. - * It matches lines like - * U_JG_ - */ - - U_JG_NO_JOINING_GROUP, - U_JG_AIN, - U_JG_ALAPH, - U_JG_ALEF, - U_JG_BEH, - U_JG_BETH, - U_JG_DAL, - U_JG_DALATH_RISH, - U_JG_E, - U_JG_FEH, - U_JG_FINAL_SEMKATH, - U_JG_GAF, - U_JG_GAMAL, - U_JG_HAH, - U_JG_TEH_MARBUTA_GOAL, /**< @stable ICU 4.6 */ - U_JG_HAMZA_ON_HEH_GOAL=U_JG_TEH_MARBUTA_GOAL, - U_JG_HE, - U_JG_HEH, - U_JG_HEH_GOAL, - U_JG_HETH, - U_JG_KAF, - U_JG_KAPH, - U_JG_KNOTTED_HEH, - U_JG_LAM, - U_JG_LAMADH, - U_JG_MEEM, - U_JG_MIM, - U_JG_NOON, - U_JG_NUN, - U_JG_PE, - U_JG_QAF, - U_JG_QAPH, - U_JG_REH, - U_JG_REVERSED_PE, - U_JG_SAD, - U_JG_SADHE, - U_JG_SEEN, - U_JG_SEMKATH, - U_JG_SHIN, - U_JG_SWASH_KAF, - U_JG_SYRIAC_WAW, - U_JG_TAH, - U_JG_TAW, - U_JG_TEH_MARBUTA, - U_JG_TETH, - U_JG_WAW, - U_JG_YEH, - U_JG_YEH_BARREE, - U_JG_YEH_WITH_TAIL, - U_JG_YUDH, - U_JG_YUDH_HE, - U_JG_ZAIN, - U_JG_FE, /**< @stable ICU 2.6 */ - U_JG_KHAPH, /**< @stable ICU 2.6 */ - U_JG_ZHAIN, /**< @stable ICU 2.6 */ - U_JG_BURUSHASKI_YEH_BARREE, /**< @stable ICU 4.0 */ - U_JG_FARSI_YEH, /**< @stable ICU 4.4 */ - U_JG_NYA, /**< @stable ICU 4.4 */ - U_JG_ROHINGYA_YEH, /**< @stable ICU 49 */ - U_JG_MANICHAEAN_ALEPH, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_AYIN, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_BETH, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_DALETH, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_DHAMEDH, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_FIVE, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_GIMEL, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_HETH, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_HUNDRED, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_KAPH, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_LAMEDH, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_MEM, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_NUN, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_ONE, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_PE, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_QOPH, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_RESH, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_SADHE, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_SAMEKH, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_TAW, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_TEN, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_TETH, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_THAMEDH, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_TWENTY, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_WAW, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_YODH, /**< @stable ICU 54 */ - U_JG_MANICHAEAN_ZAYIN, /**< @stable ICU 54 */ - U_JG_STRAIGHT_WAW, /**< @stable ICU 54 */ - U_JG_AFRICAN_FEH, /**< @stable ICU 58 */ - U_JG_AFRICAN_NOON, /**< @stable ICU 58 */ - U_JG_AFRICAN_QAF, /**< @stable ICU 58 */ - - U_JG_MALAYALAM_BHA, /**< @stable ICU 60 */ - U_JG_MALAYALAM_JA, /**< @stable ICU 60 */ - U_JG_MALAYALAM_LLA, /**< @stable ICU 60 */ - U_JG_MALAYALAM_LLLA, /**< @stable ICU 60 */ - U_JG_MALAYALAM_NGA, /**< @stable ICU 60 */ - U_JG_MALAYALAM_NNA, /**< @stable ICU 60 */ - U_JG_MALAYALAM_NNNA, /**< @stable ICU 60 */ - U_JG_MALAYALAM_NYA, /**< @stable ICU 60 */ - U_JG_MALAYALAM_RA, /**< @stable ICU 60 */ - 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 */ - - U_JG_THIN_YEH, /**< @stable ICU 70 */ - U_JG_VERTICAL_TAIL, /**< @stable ICU 70 */ - -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal UJoiningGroup value. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_JOINING_GROUP). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_JG_COUNT -#endif // U_HIDE_DEPRECATED_API -} UJoiningGroup; - -/** - * Grapheme Cluster Break constants. - * - * @see UCHAR_GRAPHEME_CLUSTER_BREAK - * @stable ICU 3.4 - */ -typedef enum UGraphemeClusterBreak { - /* - * Note: UGraphemeClusterBreak constants are parsed by preparseucd.py. - * It matches lines like - * U_GCB_ - */ - - U_GCB_OTHER = 0, /*[XX]*/ - U_GCB_CONTROL = 1, /*[CN]*/ - U_GCB_CR = 2, /*[CR]*/ - U_GCB_EXTEND = 3, /*[EX]*/ - U_GCB_L = 4, /*[L]*/ - U_GCB_LF = 5, /*[LF]*/ - U_GCB_LV = 6, /*[LV]*/ - U_GCB_LVT = 7, /*[LVT]*/ - U_GCB_T = 8, /*[T]*/ - U_GCB_V = 9, /*[V]*/ - /** @stable ICU 4.0 */ - U_GCB_SPACING_MARK = 10, /*[SM]*/ /* from here on: new in Unicode 5.1/ICU 4.0 */ - /** @stable ICU 4.0 */ - U_GCB_PREPEND = 11, /*[PP]*/ - /** @stable ICU 50 */ - U_GCB_REGIONAL_INDICATOR = 12, /*[RI]*/ /* new in Unicode 6.2/ICU 50 */ - /** @stable ICU 58 */ - U_GCB_E_BASE = 13, /*[EB]*/ /* from here on: new in Unicode 9.0/ICU 58 */ - /** @stable ICU 58 */ - U_GCB_E_BASE_GAZ = 14, /*[EBG]*/ - /** @stable ICU 58 */ - U_GCB_E_MODIFIER = 15, /*[EM]*/ - /** @stable ICU 58 */ - 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. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_GRAPHEME_CLUSTER_BREAK). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_GCB_COUNT = 18 -#endif // U_HIDE_DEPRECATED_API -} UGraphemeClusterBreak; - -/** - * Word Break constants. - * (UWordBreak is a pre-existing enum type in ubrk.h for word break status tags.) - * - * @see UCHAR_WORD_BREAK - * @stable ICU 3.4 - */ -typedef enum UWordBreakValues { - /* - * Note: UWordBreakValues constants are parsed by preparseucd.py. - * It matches lines like - * U_WB_ - */ - - U_WB_OTHER = 0, /*[XX]*/ - U_WB_ALETTER = 1, /*[LE]*/ - U_WB_FORMAT = 2, /*[FO]*/ - U_WB_KATAKANA = 3, /*[KA]*/ - U_WB_MIDLETTER = 4, /*[ML]*/ - U_WB_MIDNUM = 5, /*[MN]*/ - U_WB_NUMERIC = 6, /*[NU]*/ - U_WB_EXTENDNUMLET = 7, /*[EX]*/ - /** @stable ICU 4.0 */ - U_WB_CR = 8, /*[CR]*/ /* from here on: new in Unicode 5.1/ICU 4.0 */ - /** @stable ICU 4.0 */ - U_WB_EXTEND = 9, /*[Extend]*/ - /** @stable ICU 4.0 */ - U_WB_LF = 10, /*[LF]*/ - /** @stable ICU 4.0 */ - U_WB_MIDNUMLET =11, /*[MB]*/ - /** @stable ICU 4.0 */ - U_WB_NEWLINE =12, /*[NL]*/ - /** @stable ICU 50 */ - U_WB_REGIONAL_INDICATOR = 13, /*[RI]*/ /* new in Unicode 6.2/ICU 50 */ - /** @stable ICU 52 */ - U_WB_HEBREW_LETTER = 14, /*[HL]*/ /* from here on: new in Unicode 6.3/ICU 52 */ - /** @stable ICU 52 */ - U_WB_SINGLE_QUOTE = 15, /*[SQ]*/ - /** @stable ICU 52 */ - U_WB_DOUBLE_QUOTE = 16, /*[DQ]*/ - /** @stable ICU 58 */ - U_WB_E_BASE = 17, /*[EB]*/ /* from here on: new in Unicode 9.0/ICU 58 */ - /** @stable ICU 58 */ - U_WB_E_BASE_GAZ = 18, /*[EBG]*/ - /** @stable ICU 58 */ - U_WB_E_MODIFIER = 19, /*[EM]*/ - /** @stable ICU 58 */ - 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. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_WORD_BREAK). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_WB_COUNT = 23 -#endif // U_HIDE_DEPRECATED_API -} UWordBreakValues; - -/** - * Sentence Break constants. - * - * @see UCHAR_SENTENCE_BREAK - * @stable ICU 3.4 - */ -typedef enum USentenceBreak { - /* - * Note: USentenceBreak constants are parsed by preparseucd.py. - * It matches lines like - * U_SB_ - */ - - U_SB_OTHER = 0, /*[XX]*/ - U_SB_ATERM = 1, /*[AT]*/ - U_SB_CLOSE = 2, /*[CL]*/ - U_SB_FORMAT = 3, /*[FO]*/ - U_SB_LOWER = 4, /*[LO]*/ - U_SB_NUMERIC = 5, /*[NU]*/ - U_SB_OLETTER = 6, /*[LE]*/ - U_SB_SEP = 7, /*[SE]*/ - U_SB_SP = 8, /*[SP]*/ - U_SB_STERM = 9, /*[ST]*/ - U_SB_UPPER = 10, /*[UP]*/ - U_SB_CR = 11, /*[CR]*/ /* from here on: new in Unicode 5.1/ICU 4.0 */ - U_SB_EXTEND = 12, /*[EX]*/ - U_SB_LF = 13, /*[LF]*/ - U_SB_SCONTINUE = 14, /*[SC]*/ -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal USentenceBreak value. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_SENTENCE_BREAK). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_SB_COUNT = 15 -#endif // U_HIDE_DEPRECATED_API -} USentenceBreak; - -/** - * Line Break constants. - * - * @see UCHAR_LINE_BREAK - * @stable ICU 2.2 - */ -typedef enum ULineBreak { - /* - * Note: ULineBreak constants are parsed by preparseucd.py. - * It matches lines like - * U_LB_ - */ - - U_LB_UNKNOWN = 0, /*[XX]*/ - U_LB_AMBIGUOUS = 1, /*[AI]*/ - U_LB_ALPHABETIC = 2, /*[AL]*/ - U_LB_BREAK_BOTH = 3, /*[B2]*/ - U_LB_BREAK_AFTER = 4, /*[BA]*/ - U_LB_BREAK_BEFORE = 5, /*[BB]*/ - U_LB_MANDATORY_BREAK = 6, /*[BK]*/ - U_LB_CONTINGENT_BREAK = 7, /*[CB]*/ - U_LB_CLOSE_PUNCTUATION = 8, /*[CL]*/ - U_LB_COMBINING_MARK = 9, /*[CM]*/ - U_LB_CARRIAGE_RETURN = 10, /*[CR]*/ - U_LB_EXCLAMATION = 11, /*[EX]*/ - U_LB_GLUE = 12, /*[GL]*/ - U_LB_HYPHEN = 13, /*[HY]*/ - U_LB_IDEOGRAPHIC = 14, /*[ID]*/ - /** Renamed from the misspelled "inseperable" in Unicode 4.0.1/ICU 3.0 @stable ICU 3.0 */ - U_LB_INSEPARABLE = 15, /*[IN]*/ - U_LB_INSEPERABLE = U_LB_INSEPARABLE, - U_LB_INFIX_NUMERIC = 16, /*[IS]*/ - U_LB_LINE_FEED = 17, /*[LF]*/ - U_LB_NONSTARTER = 18, /*[NS]*/ - U_LB_NUMERIC = 19, /*[NU]*/ - U_LB_OPEN_PUNCTUATION = 20, /*[OP]*/ - U_LB_POSTFIX_NUMERIC = 21, /*[PO]*/ - U_LB_PREFIX_NUMERIC = 22, /*[PR]*/ - U_LB_QUOTATION = 23, /*[QU]*/ - U_LB_COMPLEX_CONTEXT = 24, /*[SA]*/ - U_LB_SURROGATE = 25, /*[SG]*/ - U_LB_SPACE = 26, /*[SP]*/ - U_LB_BREAK_SYMBOLS = 27, /*[SY]*/ - U_LB_ZWSPACE = 28, /*[ZW]*/ - /** @stable ICU 2.6 */ - U_LB_NEXT_LINE = 29, /*[NL]*/ /* from here on: new in Unicode 4/ICU 2.6 */ - /** @stable ICU 2.6 */ - U_LB_WORD_JOINER = 30, /*[WJ]*/ - /** @stable ICU 3.4 */ - U_LB_H2 = 31, /*[H2]*/ /* from here on: new in Unicode 4.1/ICU 3.4 */ - /** @stable ICU 3.4 */ - U_LB_H3 = 32, /*[H3]*/ - /** @stable ICU 3.4 */ - U_LB_JL = 33, /*[JL]*/ - /** @stable ICU 3.4 */ - U_LB_JT = 34, /*[JT]*/ - /** @stable ICU 3.4 */ - U_LB_JV = 35, /*[JV]*/ - /** @stable ICU 4.4 */ - U_LB_CLOSE_PARENTHESIS = 36, /*[CP]*/ /* new in Unicode 5.2/ICU 4.4 */ - /** @stable ICU 49 */ - U_LB_CONDITIONAL_JAPANESE_STARTER = 37,/*[CJ]*/ /* new in Unicode 6.1/ICU 49 */ - /** @stable ICU 49 */ - U_LB_HEBREW_LETTER = 38, /*[HL]*/ /* new in Unicode 6.1/ICU 49 */ - /** @stable ICU 50 */ - U_LB_REGIONAL_INDICATOR = 39,/*[RI]*/ /* new in Unicode 6.2/ICU 50 */ - /** @stable ICU 58 */ - U_LB_E_BASE = 40, /*[EB]*/ /* from here on: new in Unicode 9.0/ICU 58 */ - /** @stable ICU 58 */ - U_LB_E_MODIFIER = 41, /*[EM]*/ - /** @stable ICU 58 */ - U_LB_ZWJ = 42, /*[ZWJ]*/ -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal ULineBreak value. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_LINE_BREAK). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_LB_COUNT = 43 -#endif // U_HIDE_DEPRECATED_API -} ULineBreak; - -/** - * Numeric Type constants. - * - * @see UCHAR_NUMERIC_TYPE - * @stable ICU 2.2 - */ -typedef enum UNumericType { - /* - * Note: UNumericType constants are parsed by preparseucd.py. - * It matches lines like - * U_NT_ - */ - - U_NT_NONE, /*[None]*/ - U_NT_DECIMAL, /*[de]*/ - U_NT_DIGIT, /*[di]*/ - U_NT_NUMERIC, /*[nu]*/ -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal UNumericType value. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_NUMERIC_TYPE). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_NT_COUNT -#endif // U_HIDE_DEPRECATED_API -} UNumericType; - -/** - * Hangul Syllable Type constants. - * - * @see UCHAR_HANGUL_SYLLABLE_TYPE - * @stable ICU 2.6 - */ -typedef enum UHangulSyllableType { - /* - * Note: UHangulSyllableType constants are parsed by preparseucd.py. - * It matches lines like - * U_HST_ - */ - - U_HST_NOT_APPLICABLE, /*[NA]*/ - U_HST_LEADING_JAMO, /*[L]*/ - U_HST_VOWEL_JAMO, /*[V]*/ - U_HST_TRAILING_JAMO, /*[T]*/ - U_HST_LV_SYLLABLE, /*[LV]*/ - U_HST_LVT_SYLLABLE, /*[LVT]*/ -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal UHangulSyllableType value. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_HANGUL_SYLLABLE_TYPE). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_HST_COUNT -#endif // U_HIDE_DEPRECATED_API -} UHangulSyllableType; - -/** - * Indic Positional Category constants. - * - * @see UCHAR_INDIC_POSITIONAL_CATEGORY - * @stable ICU 63 - */ -typedef enum UIndicPositionalCategory { - /* - * Note: UIndicPositionalCategory constants are parsed by preparseucd.py. - * It matches lines like - * U_INPC_ - */ - - /** @stable ICU 63 */ - U_INPC_NA, - /** @stable ICU 63 */ - U_INPC_BOTTOM, - /** @stable ICU 63 */ - U_INPC_BOTTOM_AND_LEFT, - /** @stable ICU 63 */ - U_INPC_BOTTOM_AND_RIGHT, - /** @stable ICU 63 */ - U_INPC_LEFT, - /** @stable ICU 63 */ - U_INPC_LEFT_AND_RIGHT, - /** @stable ICU 63 */ - U_INPC_OVERSTRUCK, - /** @stable ICU 63 */ - U_INPC_RIGHT, - /** @stable ICU 63 */ - U_INPC_TOP, - /** @stable ICU 63 */ - U_INPC_TOP_AND_BOTTOM, - /** @stable ICU 63 */ - U_INPC_TOP_AND_BOTTOM_AND_RIGHT, - /** @stable ICU 63 */ - U_INPC_TOP_AND_LEFT, - /** @stable ICU 63 */ - U_INPC_TOP_AND_LEFT_AND_RIGHT, - /** @stable ICU 63 */ - U_INPC_TOP_AND_RIGHT, - /** @stable ICU 63 */ - U_INPC_VISUAL_ORDER_LEFT, - /** @stable ICU 66 */ - U_INPC_TOP_AND_BOTTOM_AND_LEFT, -} UIndicPositionalCategory; - -/** - * Indic Syllabic Category constants. - * - * @see UCHAR_INDIC_SYLLABIC_CATEGORY - * @stable ICU 63 - */ -typedef enum UIndicSyllabicCategory { - /* - * Note: UIndicSyllabicCategory constants are parsed by preparseucd.py. - * It matches lines like - * U_INSC_ - */ - - /** @stable ICU 63 */ - U_INSC_OTHER, - /** @stable ICU 63 */ - U_INSC_AVAGRAHA, - /** @stable ICU 63 */ - U_INSC_BINDU, - /** @stable ICU 63 */ - U_INSC_BRAHMI_JOINING_NUMBER, - /** @stable ICU 63 */ - U_INSC_CANTILLATION_MARK, - /** @stable ICU 63 */ - U_INSC_CONSONANT, - /** @stable ICU 63 */ - U_INSC_CONSONANT_DEAD, - /** @stable ICU 63 */ - U_INSC_CONSONANT_FINAL, - /** @stable ICU 63 */ - U_INSC_CONSONANT_HEAD_LETTER, - /** @stable ICU 63 */ - U_INSC_CONSONANT_INITIAL_POSTFIXED, - /** @stable ICU 63 */ - U_INSC_CONSONANT_KILLER, - /** @stable ICU 63 */ - U_INSC_CONSONANT_MEDIAL, - /** @stable ICU 63 */ - U_INSC_CONSONANT_PLACEHOLDER, - /** @stable ICU 63 */ - U_INSC_CONSONANT_PRECEDING_REPHA, - /** @stable ICU 63 */ - U_INSC_CONSONANT_PREFIXED, - /** @stable ICU 63 */ - U_INSC_CONSONANT_SUBJOINED, - /** @stable ICU 63 */ - U_INSC_CONSONANT_SUCCEEDING_REPHA, - /** @stable ICU 63 */ - U_INSC_CONSONANT_WITH_STACKER, - /** @stable ICU 63 */ - U_INSC_GEMINATION_MARK, - /** @stable ICU 63 */ - U_INSC_INVISIBLE_STACKER, - /** @stable ICU 63 */ - U_INSC_JOINER, - /** @stable ICU 63 */ - U_INSC_MODIFYING_LETTER, - /** @stable ICU 63 */ - U_INSC_NON_JOINER, - /** @stable ICU 63 */ - U_INSC_NUKTA, - /** @stable ICU 63 */ - U_INSC_NUMBER, - /** @stable ICU 63 */ - U_INSC_NUMBER_JOINER, - /** @stable ICU 63 */ - U_INSC_PURE_KILLER, - /** @stable ICU 63 */ - U_INSC_REGISTER_SHIFTER, - /** @stable ICU 63 */ - U_INSC_SYLLABLE_MODIFIER, - /** @stable ICU 63 */ - U_INSC_TONE_LETTER, - /** @stable ICU 63 */ - U_INSC_TONE_MARK, - /** @stable ICU 63 */ - U_INSC_VIRAMA, - /** @stable ICU 63 */ - U_INSC_VISARGA, - /** @stable ICU 63 */ - U_INSC_VOWEL, - /** @stable ICU 63 */ - U_INSC_VOWEL_DEPENDENT, - /** @stable ICU 63 */ - U_INSC_VOWEL_INDEPENDENT, -} UIndicSyllabicCategory; - -/** - * Vertical Orientation constants. - * - * @see UCHAR_VERTICAL_ORIENTATION - * @stable ICU 63 - */ -typedef enum UVerticalOrientation { - /* - * Note: UVerticalOrientation constants are parsed by preparseucd.py. - * It matches lines like - * U_VO_ - */ - - /** @stable ICU 63 */ - U_VO_ROTATED, - /** @stable ICU 63 */ - U_VO_TRANSFORMED_ROTATED, - /** @stable ICU 63 */ - U_VO_TRANSFORMED_UPRIGHT, - /** @stable ICU 63 */ - U_VO_UPRIGHT, -} UVerticalOrientation; - -/** - * Check a binary Unicode property for a code point. - * - * Unicode, especially in version 3.2, defines many more properties than the - * original set in UnicodeData.txt. - * - * The properties APIs are intended to reflect Unicode properties as defined - * in the Unicode Character Database (UCD) and Unicode Technical Reports (UTR). - * For details about the properties see http://www.unicode.org/ucd/ . - * For names of Unicode properties see the UCD file PropertyAliases.txt. - * - * Important: If ICU is built with UCD files from Unicode versions below 3.2, - * then properties marked with "new in Unicode 3.2" are not or not fully available. - * - * @param c Code point to test. - * @param which UProperty selector constant, identifies which binary property to check. - * Must be UCHAR_BINARY_START<=which<UCHAR_BINARY_LIMIT. - * @return true or false according to the binary Unicode property value for c. - * Also false if 'which' is out of bounds or if the Unicode version - * does not have data for the property at all. - * - * @see UProperty - * @see u_getBinaryPropertySet - * @see u_getIntPropertyValue - * @see u_getUnicodeVersion - * @stable ICU 2.1 - */ -U_CAPI UBool U_EXPORT2 -u_hasBinaryProperty(UChar32 c, UProperty which); - -/** - * Returns true if the property is true for the string. - * Same as u_hasBinaryProperty(single code point, which) - * if the string contains exactly one code point. - * - * Most properties apply only to single code points. - * UTS #51 Unicode Emoji - * defines several properties of strings. - * - * @param s String to test. - * @param length Length of the string, or negative if NUL-terminated. - * @param which UProperty selector constant, identifies which binary property to check. - * Must be UCHAR_BINARY_START<=which<UCHAR_BINARY_LIMIT. - * @return true or false according to the binary Unicode property value for the string. - * Also false if 'which' is out of bounds or if the Unicode version - * does not have data for the property at all. - * - * @see UProperty - * @see u_hasBinaryProperty - * @see u_getBinaryPropertySet - * @see u_getIntPropertyValue - * @see u_getUnicodeVersion - * @stable ICU 70 - */ -U_CAPI UBool U_EXPORT2 -u_stringHasBinaryProperty(const UChar *s, int32_t length, UProperty which); - -/** - * Returns a frozen USet for a binary property. - * The library retains ownership over the returned object. - * Sets an error code if the property number is not one for a binary property. - * - * The returned set contains all code points for which the property is true. - * - * @param property UCHAR_BINARY_START..UCHAR_BINARY_LIMIT-1 - * @param pErrorCode an in/out ICU UErrorCode - * @return the property as a set - * @see UProperty - * @see u_hasBinaryProperty - * @see Unicode::fromUSet - * @stable ICU 63 - */ -U_CAPI const USet * U_EXPORT2 -u_getBinaryPropertySet(UProperty property, UErrorCode *pErrorCode); - -/** - * Check if a code point has the Alphabetic Unicode property. - * Same as u_hasBinaryProperty(c, UCHAR_ALPHABETIC). - * This is different from u_isalpha! - * @param c Code point to test - * @return true if the code point has the Alphabetic Unicode property, false otherwise - * - * @see UCHAR_ALPHABETIC - * @see u_isalpha - * @see u_hasBinaryProperty - * @stable ICU 2.1 - */ -U_CAPI UBool U_EXPORT2 -u_isUAlphabetic(UChar32 c); - -/** - * Check if a code point has the Lowercase Unicode property. - * Same as u_hasBinaryProperty(c, UCHAR_LOWERCASE). - * This is different from u_islower! - * @param c Code point to test - * @return true if the code point has the Lowercase Unicode property, false otherwise - * - * @see UCHAR_LOWERCASE - * @see u_islower - * @see u_hasBinaryProperty - * @stable ICU 2.1 - */ -U_CAPI UBool U_EXPORT2 -u_isULowercase(UChar32 c); - -/** - * Check if a code point has the Uppercase Unicode property. - * Same as u_hasBinaryProperty(c, UCHAR_UPPERCASE). - * This is different from u_isupper! - * @param c Code point to test - * @return true if the code point has the Uppercase Unicode property, false otherwise - * - * @see UCHAR_UPPERCASE - * @see u_isupper - * @see u_hasBinaryProperty - * @stable ICU 2.1 - */ -U_CAPI UBool U_EXPORT2 -u_isUUppercase(UChar32 c); - -/** - * Check if a code point has the White_Space Unicode property. - * Same as u_hasBinaryProperty(c, UCHAR_WHITE_SPACE). - * This is different from both u_isspace and u_isWhitespace! - * - * Note: There are several ICU whitespace functions; please see the uchar.h - * file documentation for a detailed comparison. - * - * @param c Code point to test - * @return true if the code point has the White_Space Unicode property, false otherwise. - * - * @see UCHAR_WHITE_SPACE - * @see u_isWhitespace - * @see u_isspace - * @see u_isJavaSpaceChar - * @see u_hasBinaryProperty - * @stable ICU 2.1 - */ -U_CAPI UBool U_EXPORT2 -u_isUWhiteSpace(UChar32 c); - -/** - * Get the property value for an enumerated or integer Unicode property for a code point. - * Also returns binary and mask property values. - * - * Unicode, especially in version 3.2, defines many more properties than the - * original set in UnicodeData.txt. - * - * The properties APIs are intended to reflect Unicode properties as defined - * in the Unicode Character Database (UCD) and Unicode Technical Reports (UTR). - * For details about the properties see http://www.unicode.org/ . - * For names of Unicode properties see the UCD file PropertyAliases.txt. - * - * Sample usage: - * UEastAsianWidth ea=(UEastAsianWidth)u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH); - * UBool b=(UBool)u_getIntPropertyValue(c, UCHAR_IDEOGRAPHIC); - * - * @param c Code point to test. - * @param which UProperty selector constant, identifies which property to check. - * Must be UCHAR_BINARY_START<=which=0. - * True for characters with general category "Nd" (decimal digit numbers) - * as well as Latin letters a-f and A-F in both ASCII and Fullwidth ASCII. - * (That is, for letters with code points - * 0041..0046, 0061..0066, FF21..FF26, FF41..FF46.) - * - * In order to narrow the definition of hexadecimal digits to only ASCII - * characters, use (c<=0x7f && u_isxdigit(c)). - * - * This is a C/POSIX migration function. - * See the comments about C/POSIX character classification functions in the - * documentation at the top of this header file. - * - * @param c the code point to be tested - * @return true if the code point is a hexadecimal digit - * - * @stable ICU 2.6 - */ -U_CAPI UBool U_EXPORT2 -u_isxdigit(UChar32 c); - -/** - * Determines whether the specified code point is a punctuation character. - * True for characters with general categories "P" (punctuation). - * - * This is a C/POSIX migration function. - * See the comments about C/POSIX character classification functions in the - * documentation at the top of this header file. - * - * @param c the code point to be tested - * @return true if the code point is a punctuation character - * - * @stable ICU 2.6 - */ -U_CAPI UBool U_EXPORT2 -u_ispunct(UChar32 c); - -/** - * Determines whether the specified code point is a "graphic" character - * (printable, excluding spaces). - * true for all characters except those with general categories - * "Cc" (control codes), "Cf" (format controls), "Cs" (surrogates), - * "Cn" (unassigned), and "Z" (separators). - * - * This is a C/POSIX migration function. - * See the comments about C/POSIX character classification functions in the - * documentation at the top of this header file. - * - * @param c the code point to be tested - * @return true if the code point is a "graphic" character - * - * @stable ICU 2.6 - */ -U_CAPI UBool U_EXPORT2 -u_isgraph(UChar32 c); - -/** - * Determines whether the specified code point is a "blank" or "horizontal space", - * a character that visibly separates words on a line. - * The following are equivalent definitions: - * - * true for Unicode White_Space characters except for "vertical space controls" - * where "vertical space controls" are the following characters: - * U+000A (LF) U+000B (VT) U+000C (FF) U+000D (CR) U+0085 (NEL) U+2028 (LS) U+2029 (PS) - * - * same as - * - * true for U+0009 (TAB) and characters with general category "Zs" (space separators). - * - * Note: There are several ICU whitespace functions; please see the uchar.h - * file documentation for a detailed comparison. - * - * This is a C/POSIX migration function. - * See the comments about C/POSIX character classification functions in the - * documentation at the top of this header file. - * - * @param c the code point to be tested - * @return true if the code point is a "blank" - * - * @stable ICU 2.6 - */ -U_CAPI UBool U_EXPORT2 -u_isblank(UChar32 c); - -/** - * Determines whether the specified code point is "defined", - * which usually means that it is assigned a character. - * True for general categories other than "Cn" (other, not assigned), - * i.e., true for all code points mentioned in UnicodeData.txt. - * - * Note that non-character code points (e.g., U+FDD0) are not "defined" - * (they are Cn), but surrogate code points are "defined" (Cs). - * - * Same as java.lang.Character.isDefined(). - * - * @param c the code point to be tested - * @return true if the code point is assigned a character - * - * @see u_isdigit - * @see u_isalpha - * @see u_isalnum - * @see u_isupper - * @see u_islower - * @see u_istitle - * @stable ICU 2.0 - */ -U_CAPI UBool U_EXPORT2 -u_isdefined(UChar32 c); - -/** - * Determines if the specified character is a space character or not. - * - * Note: There are several ICU whitespace functions; please see the uchar.h - * file documentation for a detailed comparison. - * - * This is a C/POSIX migration function. - * See the comments about C/POSIX character classification functions in the - * documentation at the top of this header file. - * - * @param c the character to be tested - * @return true if the character is a space character; false otherwise. - * - * @see u_isJavaSpaceChar - * @see u_isWhitespace - * @see u_isUWhiteSpace - * @stable ICU 2.0 - */ -U_CAPI UBool U_EXPORT2 -u_isspace(UChar32 c); - -/** - * Determine if the specified code point is a space character according to Java. - * True for characters with general categories "Z" (separators), - * which does not include control codes (e.g., TAB or Line Feed). - * - * Same as java.lang.Character.isSpaceChar(). - * - * Note: There are several ICU whitespace functions; please see the uchar.h - * file documentation for a detailed comparison. - * - * @param c the code point to be tested - * @return true if the code point is a space character according to Character.isSpaceChar() - * - * @see u_isspace - * @see u_isWhitespace - * @see u_isUWhiteSpace - * @stable ICU 2.6 - */ -U_CAPI UBool U_EXPORT2 -u_isJavaSpaceChar(UChar32 c); - -/** - * Determines if the specified code point is a whitespace character according to Java/ICU. - * A character is considered to be a Java whitespace character if and only - * if it satisfies one of the following criteria: - * - * - It is a Unicode Separator character (categories "Z" = "Zs" or "Zl" or "Zp"), but is not - * also a non-breaking space (U+00A0 NBSP or U+2007 Figure Space or U+202F Narrow NBSP). - * - It is U+0009 HORIZONTAL TABULATION. - * - It is U+000A LINE FEED. - * - It is U+000B VERTICAL TABULATION. - * - It is U+000C FORM FEED. - * - It is U+000D CARRIAGE RETURN. - * - It is U+001C FILE SEPARATOR. - * - It is U+001D GROUP SEPARATOR. - * - It is U+001E RECORD SEPARATOR. - * - It is U+001F UNIT SEPARATOR. - * - * This API tries to sync with the semantics of Java's - * java.lang.Character.isWhitespace(), but it may not return - * the exact same results because of the Unicode version - * difference. - * - * Note: Unicode 4.0.1 changed U+200B ZERO WIDTH SPACE from a Space Separator (Zs) - * to a Format Control (Cf). Since then, isWhitespace(0x200b) returns false. - * See http://www.unicode.org/versions/Unicode4.0.1/ - * - * Note: There are several ICU whitespace functions; please see the uchar.h - * file documentation for a detailed comparison. - * - * @param c the code point to be tested - * @return true if the code point is a whitespace character according to Java/ICU - * - * @see u_isspace - * @see u_isJavaSpaceChar - * @see u_isUWhiteSpace - * @stable ICU 2.0 - */ -U_CAPI UBool U_EXPORT2 -u_isWhitespace(UChar32 c); - -/** - * Determines whether the specified code point is a control character - * (as defined by this function). - * A control character is one of the following: - * - ISO 8-bit control character (U+0000..U+001f and U+007f..U+009f) - * - U_CONTROL_CHAR (Cc) - * - U_FORMAT_CHAR (Cf) - * - U_LINE_SEPARATOR (Zl) - * - U_PARAGRAPH_SEPARATOR (Zp) - * - * This is a C/POSIX migration function. - * See the comments about C/POSIX character classification functions in the - * documentation at the top of this header file. - * - * @param c the code point to be tested - * @return true if the code point is a control character - * - * @see UCHAR_DEFAULT_IGNORABLE_CODE_POINT - * @see u_isprint - * @stable ICU 2.0 - */ -U_CAPI UBool U_EXPORT2 -u_iscntrl(UChar32 c); - -/** - * Determines whether the specified code point is an ISO control code. - * True for U+0000..U+001f and U+007f..U+009f (general category "Cc"). - * - * Same as java.lang.Character.isISOControl(). - * - * @param c the code point to be tested - * @return true if the code point is an ISO control code - * - * @see u_iscntrl - * @stable ICU 2.6 - */ -U_CAPI UBool U_EXPORT2 -u_isISOControl(UChar32 c); - -/** - * Determines whether the specified code point is a printable character. - * True for general categories other than "C" (controls). - * - * This is a C/POSIX migration function. - * See the comments about C/POSIX character classification functions in the - * documentation at the top of this header file. - * - * @param c the code point to be tested - * @return true if the code point is a printable character - * - * @see UCHAR_DEFAULT_IGNORABLE_CODE_POINT - * @see u_iscntrl - * @stable ICU 2.0 - */ -U_CAPI UBool U_EXPORT2 -u_isprint(UChar32 c); - -/** - * Non-standard: Determines whether the specified code point is a base character. - * True for general categories "L" (letters), "N" (numbers), - * "Mc" (spacing combining marks), and "Me" (enclosing marks). - * - * Note that this is different from the Unicode Standard definition in - * chapter 3.6, conformance clause D51 “Base character”, - * which defines base characters as the code points with general categories - * Letter (L), Number (N), Punctuation (P), Symbol (S), or Space Separator (Zs). - * - * @param c the code point to be tested - * @return true if the code point is a base character according to this function - * - * @see u_isalpha - * @see u_isdigit - * @stable ICU 2.0 - */ -U_CAPI UBool U_EXPORT2 -u_isbase(UChar32 c); - -/** - * Returns the bidirectional category value for the code point, - * which is used in the Unicode bidirectional algorithm - * (UAX #9 http://www.unicode.org/reports/tr9/). - * Note that some unassigned code points have bidi values - * of R or AL because they are in blocks that are reserved - * for Right-To-Left scripts. - * - * Same as java.lang.Character.getDirectionality() - * - * @param c the code point to be tested - * @return the bidirectional category (UCharDirection) value - * - * @see UCharDirection - * @stable ICU 2.0 - */ -U_CAPI UCharDirection U_EXPORT2 -u_charDirection(UChar32 c); - -/** - * Determines whether the code point has the Bidi_Mirrored property. - * This property is set for characters that are commonly used in - * Right-To-Left contexts and need to be displayed with a "mirrored" - * glyph. - * - * Same as java.lang.Character.isMirrored(). - * Same as UCHAR_BIDI_MIRRORED - * - * @param c the code point to be tested - * @return true if the character has the Bidi_Mirrored property - * - * @see UCHAR_BIDI_MIRRORED - * @stable ICU 2.0 - */ -U_CAPI UBool U_EXPORT2 -u_isMirrored(UChar32 c); - -/** - * Maps the specified character to a "mirror-image" character. - * For characters with the Bidi_Mirrored property, implementations - * sometimes need a "poor man's" mapping to another Unicode - * character (code point) such that the default glyph may serve - * as the mirror-image of the default glyph of the specified - * character. This is useful for text conversion to and from - * codepages with visual order, and for displays without glyph - * selection capabilities. - * - * @param c the code point to be mapped - * @return another Unicode code point that may serve as a mirror-image - * substitute, or c itself if there is no such mapping or c - * does not have the Bidi_Mirrored property - * - * @see UCHAR_BIDI_MIRRORED - * @see u_isMirrored - * @stable ICU 2.0 - */ -U_CAPI UChar32 U_EXPORT2 -u_charMirror(UChar32 c); - -/** - * Maps the specified character to its paired bracket character. - * For Bidi_Paired_Bracket_Type!=None, this is the same as u_charMirror(). - * Otherwise c itself is returned. - * See http://www.unicode.org/reports/tr9/ - * - * @param c the code point to be mapped - * @return the paired bracket code point, - * or c itself if there is no such mapping - * (Bidi_Paired_Bracket_Type=None) - * - * @see UCHAR_BIDI_PAIRED_BRACKET - * @see UCHAR_BIDI_PAIRED_BRACKET_TYPE - * @see u_charMirror - * @stable ICU 52 - */ -U_CAPI UChar32 U_EXPORT2 -u_getBidiPairedBracket(UChar32 c); - -/** - * Returns the general category value for the code point. - * - * Same as java.lang.Character.getType(). - * - * @param c the code point to be tested - * @return the general category (UCharCategory) value - * - * @see UCharCategory - * @stable ICU 2.0 - */ -U_CAPI int8_t U_EXPORT2 -u_charType(UChar32 c); - -/** - * Get a single-bit bit set for the general category of a character. - * This bit set can be compared bitwise with U_GC_SM_MASK, U_GC_L_MASK, etc. - * Same as U_MASK(u_charType(c)). - * - * @param c the code point to be tested - * @return a single-bit mask corresponding to the general category (UCharCategory) value - * - * @see u_charType - * @see UCharCategory - * @see U_GC_CN_MASK - * @stable ICU 2.1 - */ -#define U_GET_GC_MASK(c) U_MASK(u_charType(c)) - -/** - * Callback from u_enumCharTypes(), is called for each contiguous range - * of code points c (where start<=cnameChoice, the character name written - * into the buffer is the "modern" name or the name that was defined - * in Unicode version 1.0. - * The name contains only "invariant" characters - * like A-Z, 0-9, space, and '-'. - * Unicode 1.0 names are only retrieved if they are different from the modern - * names and if the data file contains the data for them. gennames may or may - * not be called with a command line option to include 1.0 names in unames.dat. - * - * @param code The character (code point) for which to get the name. - * It must be 0<=code<=0x10ffff. - * @param nameChoice Selector for which name to get. - * @param buffer Destination address for copying the name. - * The name will always be zero-terminated. - * If there is no name, then the buffer will be set to the empty string. - * @param bufferLength ==sizeof(buffer) - * @param pErrorCode Pointer to a UErrorCode variable; - * check for U_SUCCESS() after u_charName() - * returns. - * @return The length of the name, or 0 if there is no name for this character. - * If the bufferLength is less than or equal to the length, then the buffer - * contains the truncated name and the returned length indicates the full - * length of the name. - * The length does not include the zero-termination. - * - * @see UCharNameChoice - * @see u_charFromName - * @see u_enumCharNames - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_charName(UChar32 code, UCharNameChoice nameChoice, - char *buffer, int32_t bufferLength, - UErrorCode *pErrorCode); - -#ifndef U_HIDE_DEPRECATED_API -/** - * Returns an empty string. - * Used to return the ISO 10646 comment for a character. - * The Unicode ISO_Comment property is deprecated and has no values. - * - * @param c The character (code point) for which to get the ISO comment. - * It must be 0<=c<=0x10ffff. - * @param dest Destination address for copying the comment. - * The comment will be zero-terminated if possible. - * If there is no comment, then the buffer will be set to the empty string. - * @param destCapacity ==sizeof(dest) - * @param pErrorCode Pointer to a UErrorCode variable; - * check for U_SUCCESS() after u_getISOComment() - * returns. - * @return 0 - * - * @deprecated ICU 49 - */ -U_DEPRECATED int32_t U_EXPORT2 -u_getISOComment(UChar32 c, - char *dest, int32_t destCapacity, - UErrorCode *pErrorCode); -#endif /* U_HIDE_DEPRECATED_API */ - -/** - * Find a Unicode character by its name and return its code point value. - * The name is matched exactly and completely. - * If the name does not correspond to a code point, pErrorCode - * is set to U_INVALID_CHAR_FOUND. - * A Unicode 1.0 name is matched only if it differs from the modern name. - * Unicode names are all uppercase. Extended names are lowercase followed - * by an uppercase hexadecimal number, and within angle brackets. - * - * @param nameChoice Selector for which name to match. - * @param name The name to match. - * @param pErrorCode Pointer to a UErrorCode variable - * @return The Unicode value of the code point with the given name, - * or an undefined value if there is no such code point. - * - * @see UCharNameChoice - * @see u_charName - * @see u_enumCharNames - * @stable ICU 1.7 - */ -U_CAPI UChar32 U_EXPORT2 -u_charFromName(UCharNameChoice nameChoice, - const char *name, - UErrorCode *pErrorCode); - -/** - * Type of a callback function for u_enumCharNames() that gets called - * for each Unicode character with the code point value and - * the character name. - * If such a function returns false, then the enumeration is stopped. - * - * @param context The context pointer that was passed to u_enumCharNames(). - * @param code The Unicode code point for the character with this name. - * @param nameChoice Selector for which kind of names is enumerated. - * @param name The character's name, zero-terminated. - * @param length The length of the name. - * @return true if the enumeration should continue, false to stop it. - * - * @see UCharNameChoice - * @see u_enumCharNames - * @stable ICU 1.7 - */ -typedef UBool U_CALLCONV UEnumCharNamesFn(void *context, - UChar32 code, - UCharNameChoice nameChoice, - const char *name, - int32_t length); - -/** - * Enumerate all assigned Unicode characters between the start and limit - * code points (start inclusive, limit exclusive) and call a function - * for each, passing the code point value and the character name. - * For Unicode 1.0 names, only those are enumerated that differ from the - * modern names. - * - * @param start The first code point in the enumeration range. - * @param limit One more than the last code point in the enumeration range - * (the first one after the range). - * @param fn The function that is to be called for each character name. - * @param context An arbitrary pointer that is passed to the function. - * @param nameChoice Selector for which kind of names to enumerate. - * @param pErrorCode Pointer to a UErrorCode variable - * - * @see UCharNameChoice - * @see UEnumCharNamesFn - * @see u_charName - * @see u_charFromName - * @stable ICU 1.7 - */ -U_CAPI void U_EXPORT2 -u_enumCharNames(UChar32 start, UChar32 limit, - UEnumCharNamesFn *fn, - void *context, - UCharNameChoice nameChoice, - UErrorCode *pErrorCode); - -/** - * Return the Unicode name for a given property, as given in the - * Unicode database file PropertyAliases.txt. - * - * In addition, this function maps the property - * UCHAR_GENERAL_CATEGORY_MASK to the synthetic names "gcm" / - * "General_Category_Mask". These names are not in - * PropertyAliases.txt. - * - * @param property UProperty selector other than UCHAR_INVALID_CODE. - * If out of range, NULL is returned. - * - * @param nameChoice selector for which name to get. If out of range, - * NULL is returned. All properties have a long name. Most - * have a short name, but some do not. Unicode allows for - * additional names; if present these will be returned by - * U_LONG_PROPERTY_NAME + i, where i=1, 2,... - * - * @return a pointer to the name, or NULL if either the - * property or the nameChoice is out of range. If a given - * nameChoice returns NULL, then all larger values of - * nameChoice will return NULL, with one exception: if NULL is - * returned for U_SHORT_PROPERTY_NAME, then - * U_LONG_PROPERTY_NAME (and higher) may still return a - * non-NULL value. The returned pointer is valid until - * u_cleanup() is called. - * - * @see UProperty - * @see UPropertyNameChoice - * @stable ICU 2.4 - */ -U_CAPI const char* U_EXPORT2 -u_getPropertyName(UProperty property, - UPropertyNameChoice nameChoice); - -/** - * Return the UProperty enum for a given property name, as specified - * in the Unicode database file PropertyAliases.txt. Short, long, and - * any other variants are recognized. - * - * In addition, this function maps the synthetic names "gcm" / - * "General_Category_Mask" to the property - * UCHAR_GENERAL_CATEGORY_MASK. These names are not in - * PropertyAliases.txt. - * - * @param alias the property name to be matched. The name is compared - * using "loose matching" as described in PropertyAliases.txt. - * - * @return a UProperty enum, or UCHAR_INVALID_CODE if the given name - * does not match any property. - * - * @see UProperty - * @stable ICU 2.4 - */ -U_CAPI UProperty U_EXPORT2 -u_getPropertyEnum(const char* alias); - -/** - * Return the Unicode name for a given property value, as given in the - * Unicode database file PropertyValueAliases.txt. - * - * Note: Some of the names in PropertyValueAliases.txt can only be - * retrieved using UCHAR_GENERAL_CATEGORY_MASK, not - * UCHAR_GENERAL_CATEGORY. These include: "C" / "Other", "L" / - * "Letter", "LC" / "Cased_Letter", "M" / "Mark", "N" / "Number", "P" - * / "Punctuation", "S" / "Symbol", and "Z" / "Separator". - * - * @param property UProperty selector constant. - * Must be UCHAR_BINARY_START<=which2<=radix<=36 or if the - * value of c is not a valid digit in the specified - * radix, -1 is returned. A character is a valid digit - * if at least one of the following is true: - *

    - *
  • The character has a decimal digit value. - * Such characters have the general category "Nd" (decimal digit numbers) - * and a Numeric_Type of Decimal. - * In this case the value is the character's decimal digit value.
  • - *
  • The character is one of the uppercase Latin letters - * 'A' through 'Z'. - * In this case the value is c-'A'+10.
  • - *
  • The character is one of the lowercase Latin letters - * 'a' through 'z'. - * In this case the value is ch-'a'+10.
  • - *
  • Latin letters from both the ASCII range (0061..007A, 0041..005A) - * as well as from the Fullwidth ASCII range (FF41..FF5A, FF21..FF3A) - * are recognized.
  • - *
- * - * Same as java.lang.Character.digit(). - * - * @param ch the code point to be tested. - * @param radix the radix. - * @return the numeric value represented by the character in the - * specified radix, - * or -1 if there is no value or if the value exceeds the radix. - * - * @see UCHAR_NUMERIC_TYPE - * @see u_forDigit - * @see u_charDigitValue - * @see u_isdigit - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_digit(UChar32 ch, int8_t radix); - -/** - * Determines the character representation for a specific digit in - * the specified radix. If the value of radix is not a - * valid radix, or the value of digit is not a valid - * digit in the specified radix, the null character - * (U+0000) is returned. - *

- * The radix argument is valid if it is greater than or - * equal to 2 and less than or equal to 36. - * The digit argument is valid if - * 0 <= digit < radix. - *

- * If the digit is less than 10, then - * '0' + digit is returned. Otherwise, the value - * 'a' + digit - 10 is returned. - * - * Same as java.lang.Character.forDigit(). - * - * @param digit the number to convert to a character. - * @param radix the radix. - * @return the char representation of the specified digit - * in the specified radix. - * - * @see u_digit - * @see u_charDigitValue - * @see u_isdigit - * @stable ICU 2.0 - */ -U_CAPI UChar32 U_EXPORT2 -u_forDigit(int32_t digit, int8_t radix); - -/** - * Get the "age" of the code point. - * The "age" is the Unicode version when the code point was first - * designated (as a non-character or for Private Use) - * or assigned a character. - * This can be useful to avoid emitting code points to receiving - * processes that do not accept newer characters. - * The data is from the UCD file DerivedAge.txt. - * - * @param c The code point. - * @param versionArray The Unicode version number array, to be filled in. - * - * @stable ICU 2.1 - */ -U_CAPI void U_EXPORT2 -u_charAge(UChar32 c, UVersionInfo versionArray); - -/** - * Gets the Unicode version information. - * The version array is filled in with the version information - * for the Unicode standard that is currently used by ICU. - * For example, Unicode version 3.1.1 is represented as an array with - * the values { 3, 1, 1, 0 }. - * - * @param versionArray an output array that will be filled in with - * the Unicode version number - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -u_getUnicodeVersion(UVersionInfo versionArray); - -#if !UCONFIG_NO_NORMALIZATION -/** - * Get the FC_NFKC_Closure property string for a character. - * See Unicode Standard Annex #15 for details, search for "FC_NFKC_Closure" - * or for "FNC": http://www.unicode.org/reports/tr15/ - * - * @param c The character (code point) for which to get the FC_NFKC_Closure string. - * It must be 0<=c<=0x10ffff. - * @param dest Destination address for copying the string. - * The string will be zero-terminated if possible. - * If there is no FC_NFKC_Closure string, - * then the buffer will be set to the empty string. - * @param destCapacity ==sizeof(dest) - * @param pErrorCode Pointer to a UErrorCode variable. - * @return The length of the string, or 0 if there is no FC_NFKC_Closure string for this character. - * If the destCapacity is less than or equal to the length, then the buffer - * contains the truncated name and the returned length indicates the full - * length of the name. - * The length does not include the zero-termination. - * - * @stable ICU 2.2 - */ -U_CAPI int32_t U_EXPORT2 -u_getFC_NFKC_Closure(UChar32 c, UChar *dest, int32_t destCapacity, UErrorCode *pErrorCode); - -#endif - - -U_CDECL_END - -#endif /*_UCHAR*/ -/*eof*/ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1997-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* File UCHAR.H +* +* Modification History: +* +* Date Name Description +* 04/02/97 aliu Creation. +* 03/29/99 helena Updated for C APIs. +* 4/15/99 Madhu Updated for C Implementation and Javadoc +* 5/20/99 Madhu Added the function u_getVersion() +* 8/19/1999 srl Upgraded scripts to Unicode 3.0 +* 8/27/1999 schererm UCharDirection constants: U_... +* 11/11/1999 weiv added u_isalnum(), cleaned comments +* 01/11/2000 helena Renamed u_getVersion to u_getUnicodeVersion(). +****************************************************************************** +*/ + +#ifndef UCHAR_H +#define UCHAR_H + +#include "unicode/utypes.h" +#include "unicode/stringoptions.h" +#include "unicode/ucpmap.h" + +#if !defined(USET_DEFINED) && !defined(U_IN_DOXYGEN) + +#define USET_DEFINED + +/** + * USet is the C API type corresponding to C++ class UnicodeSet. + * It is forward-declared here to avoid including unicode/uset.h file if related + * APIs are not used. + * + * @see ucnv_getUnicodeSet + * @stable ICU 2.4 + */ +typedef struct USet USet; + +#endif + + +U_CDECL_BEGIN + +/*==========================================================================*/ +/* Unicode version number */ +/*==========================================================================*/ +/** + * Unicode version number, default for the current ICU version. + * The actual Unicode Character Database (UCD) data is stored in uprops.dat + * and may be generated from UCD files from a different Unicode version. + * Call u_getUnicodeVersion to get the actual Unicode version of the data. + * + * @see u_getUnicodeVersion + * @stable ICU 2.0 + */ +#define U_UNICODE_VERSION "15.0" + +/** + * \file + * \brief C API: Unicode Properties + * + * This C API provides low-level access to the Unicode Character Database. + * In addition to raw property values, some convenience functions calculate + * derived properties, for example for Java-style programming. + * + * Unicode assigns each code point (not just assigned character) values for + * many properties. + * Most of them are simple boolean flags, or constants from a small enumerated list. + * For some properties, values are strings or other relatively more complex types. + * + * For more information see + * "About the Unicode Character Database" (http://www.unicode.org/ucd/) + * and the ICU User Guide chapter on Properties (https://unicode-org.github.io/icu/userguide/strings/properties). + * + * Many properties are accessible via generic functions that take a UProperty selector. + * - u_hasBinaryProperty() returns a binary value (true/false) per property and code point. + * - u_getIntPropertyValue() returns an integer value per property and code point. + * For each supported enumerated or catalog property, there is + * an enum type for all of the property's values, and + * u_getIntPropertyValue() returns the numeric values of those constants. + * - u_getBinaryPropertySet() returns a set for each ICU-supported binary property with + * all code points for which the property is true. + * - u_getIntPropertyMap() returns a map for each + * ICU-supported enumerated/catalog/int-valued property which + * maps all Unicode code points to their values for that property. + * + * Many functions are designed to match java.lang.Character functions. + * See the individual function documentation, + * and see the JDK 1.4 java.lang.Character documentation + * at http://java.sun.com/j2se/1.4/docs/api/java/lang/Character.html + * + * There are also functions that provide easy migration from C/POSIX functions + * like isblank(). Their use is generally discouraged because the C/POSIX + * standards do not define their semantics beyond the ASCII range, which means + * that different implementations exhibit very different behavior. + * Instead, Unicode properties should be used directly. + * + * There are also only a few, broad C/POSIX character classes, and they tend + * to be used for conflicting purposes. For example, the "isalpha()" class + * is sometimes used to determine word boundaries, while a more sophisticated + * approach would at least distinguish initial letters from continuation + * characters (the latter including combining marks). + * (In ICU, BreakIterator is the most sophisticated API for word boundaries.) + * Another example: There is no "istitle()" class for titlecase characters. + * + * ICU 3.4 and later provides API access for all twelve C/POSIX character classes. + * ICU implements them according to the Standard Recommendations in + * Annex C: Compatibility Properties of UTS #18 Unicode Regular Expressions + * (http://www.unicode.org/reports/tr18/#Compatibility_Properties). + * + * API access for C/POSIX character classes is as follows: + * - alpha: u_isUAlphabetic(c) or u_hasBinaryProperty(c, UCHAR_ALPHABETIC) + * - lower: u_isULowercase(c) or u_hasBinaryProperty(c, UCHAR_LOWERCASE) + * - upper: u_isUUppercase(c) or u_hasBinaryProperty(c, UCHAR_UPPERCASE) + * - punct: u_ispunct(c) + * - digit: u_isdigit(c) or u_charType(c)==U_DECIMAL_DIGIT_NUMBER + * - xdigit: u_isxdigit(c) or u_hasBinaryProperty(c, UCHAR_POSIX_XDIGIT) + * - alnum: u_hasBinaryProperty(c, UCHAR_POSIX_ALNUM) + * - space: u_isUWhiteSpace(c) or u_hasBinaryProperty(c, UCHAR_WHITE_SPACE) + * - blank: u_isblank(c) or u_hasBinaryProperty(c, UCHAR_POSIX_BLANK) + * - cntrl: u_charType(c)==U_CONTROL_CHAR + * - graph: u_hasBinaryProperty(c, UCHAR_POSIX_GRAPH) + * - print: u_hasBinaryProperty(c, UCHAR_POSIX_PRINT) + * + * Note: Some of the u_isxyz() functions in uchar.h predate, and do not match, + * the Standard Recommendations in UTS #18. Instead, they match Java + * functions according to their API documentation. + * + * \htmlonly + * The C/POSIX character classes are also available in UnicodeSet patterns, + * using patterns like [:graph:] or \p{graph}. + * \endhtmlonly + * + * Note: There are several ICU whitespace functions. + * Comparison: + * - u_isUWhiteSpace=UCHAR_WHITE_SPACE: Unicode White_Space property; + * most of general categories "Z" (separators) + most whitespace ISO controls + * (including no-break spaces, but excluding IS1..IS4) + * - u_isWhitespace: Java isWhitespace; Z + whitespace ISO controls but excluding no-break spaces + * - u_isJavaSpaceChar: Java isSpaceChar; just Z (including no-break spaces) + * - u_isspace: Z + whitespace ISO controls (including no-break spaces) + * - u_isblank: "horizontal spaces" = TAB + Zs + */ + +/** + * Constants. + */ + +/** The lowest Unicode code point value. Code points are non-negative. @stable ICU 2.0 */ +#define UCHAR_MIN_VALUE 0 + +/** + * The highest Unicode code point value (scalar value) according to + * The Unicode Standard. This is a 21-bit value (20.1 bits, rounded up). + * For a single character, UChar32 is a simple type that can hold any code point value. + * + * @see UChar32 + * @stable ICU 2.0 + */ +#define UCHAR_MAX_VALUE 0x10ffff + +/** + * Get a single-bit bit set (a flag) from a bit number 0..31. + * @stable ICU 2.1 + */ +#define U_MASK(x) ((uint32_t)1<<(x)) + +/** + * Selection constants for Unicode properties. + * These constants are used in functions like u_hasBinaryProperty to select + * one of the Unicode properties. + * + * The properties APIs are intended to reflect Unicode properties as defined + * in the Unicode Character Database (UCD) and Unicode Technical Reports (UTR). + * + * For details about the properties see + * UAX #44: Unicode Character Database (http://www.unicode.org/reports/tr44/). + * + * Important: If ICU is built with UCD files from Unicode versions below, e.g., 3.2, + * then properties marked with "new in Unicode 3.2" are not or not fully available. + * Check u_getUnicodeVersion to be sure. + * + * @see u_hasBinaryProperty + * @see u_getIntPropertyValue + * @see u_getUnicodeVersion + * @stable ICU 2.1 + */ +typedef enum UProperty { + /* + * Note: UProperty constants are parsed by preparseucd.py. + * It matches lines like + * UCHAR_=, + */ + + /* Note: Place UCHAR_ALPHABETIC before UCHAR_BINARY_START so that + debuggers display UCHAR_ALPHABETIC as the symbolic name for 0, + rather than UCHAR_BINARY_START. Likewise for other *_START + identifiers. */ + + /** Binary property Alphabetic. Same as u_isUAlphabetic, different from u_isalpha. + Lu+Ll+Lt+Lm+Lo+Nl+Other_Alphabetic @stable ICU 2.1 */ + UCHAR_ALPHABETIC=0, + /** First constant for binary Unicode properties. @stable ICU 2.1 */ + UCHAR_BINARY_START=UCHAR_ALPHABETIC, + /** Binary property ASCII_Hex_Digit. 0-9 A-F a-f @stable ICU 2.1 */ + UCHAR_ASCII_HEX_DIGIT=1, + /** Binary property Bidi_Control. + Format controls which have specific functions + in the Bidi Algorithm. @stable ICU 2.1 */ + UCHAR_BIDI_CONTROL=2, + /** Binary property Bidi_Mirrored. + Characters that may change display in RTL text. + Same as u_isMirrored. + See Bidi Algorithm, UTR 9. @stable ICU 2.1 */ + UCHAR_BIDI_MIRRORED=3, + /** Binary property Dash. Variations of dashes. @stable ICU 2.1 */ + UCHAR_DASH=4, + /** Binary property Default_Ignorable_Code_Point (new in Unicode 3.2). + Ignorable in most processing. + <2060..206F, FFF0..FFFB, E0000..E0FFF>+Other_Default_Ignorable_Code_Point+(Cf+Cc+Cs-White_Space) @stable ICU 2.1 */ + UCHAR_DEFAULT_IGNORABLE_CODE_POINT=5, + /** Binary property Deprecated (new in Unicode 3.2). + The usage of deprecated characters is strongly discouraged. @stable ICU 2.1 */ + UCHAR_DEPRECATED=6, + /** Binary property Diacritic. Characters that linguistically modify + the meaning of another character to which they apply. @stable ICU 2.1 */ + UCHAR_DIACRITIC=7, + /** Binary property Extender. + Extend the value or shape of a preceding alphabetic character, + e.g., length and iteration marks. @stable ICU 2.1 */ + UCHAR_EXTENDER=8, + /** Binary property Full_Composition_Exclusion. + CompositionExclusions.txt+Singleton Decompositions+ + Non-Starter Decompositions. @stable ICU 2.1 */ + UCHAR_FULL_COMPOSITION_EXCLUSION=9, + /** Binary property Grapheme_Base (new in Unicode 3.2). + For programmatic determination of grapheme cluster boundaries. + [0..10FFFF]-Cc-Cf-Cs-Co-Cn-Zl-Zp-Grapheme_Link-Grapheme_Extend-CGJ @stable ICU 2.1 */ + UCHAR_GRAPHEME_BASE=10, + /** Binary property Grapheme_Extend (new in Unicode 3.2). + For programmatic determination of grapheme cluster boundaries. + Me+Mn+Mc+Other_Grapheme_Extend-Grapheme_Link-CGJ @stable ICU 2.1 */ + UCHAR_GRAPHEME_EXTEND=11, + /** Binary property Grapheme_Link (new in Unicode 3.2). + For programmatic determination of grapheme cluster boundaries. @stable ICU 2.1 */ + UCHAR_GRAPHEME_LINK=12, + /** Binary property Hex_Digit. + Characters commonly used for hexadecimal numbers. @stable ICU 2.1 */ + UCHAR_HEX_DIGIT=13, + /** Binary property Hyphen. Dashes used to mark connections + between pieces of words, plus the Katakana middle dot. @stable ICU 2.1 */ + UCHAR_HYPHEN=14, + /** Binary property ID_Continue. + Characters that can continue an identifier. + DerivedCoreProperties.txt also says "NOTE: Cf characters should be filtered out." + ID_Start+Mn+Mc+Nd+Pc @stable ICU 2.1 */ + UCHAR_ID_CONTINUE=15, + /** Binary property ID_Start. + Characters that can start an identifier. + Lu+Ll+Lt+Lm+Lo+Nl @stable ICU 2.1 */ + UCHAR_ID_START=16, + /** Binary property Ideographic. + CJKV ideographs. @stable ICU 2.1 */ + UCHAR_IDEOGRAPHIC=17, + /** Binary property IDS_Binary_Operator (new in Unicode 3.2). + For programmatic determination of + Ideographic Description Sequences. @stable ICU 2.1 */ + UCHAR_IDS_BINARY_OPERATOR=18, + /** Binary property IDS_Trinary_Operator (new in Unicode 3.2). + For programmatic determination of + Ideographic Description Sequences. @stable ICU 2.1 */ + UCHAR_IDS_TRINARY_OPERATOR=19, + /** Binary property Join_Control. + Format controls for cursive joining and ligation. @stable ICU 2.1 */ + UCHAR_JOIN_CONTROL=20, + /** Binary property Logical_Order_Exception (new in Unicode 3.2). + Characters that do not use logical order and + require special handling in most processing. @stable ICU 2.1 */ + UCHAR_LOGICAL_ORDER_EXCEPTION=21, + /** Binary property Lowercase. Same as u_isULowercase, different from u_islower. + Ll+Other_Lowercase @stable ICU 2.1 */ + UCHAR_LOWERCASE=22, + /** Binary property Math. Sm+Other_Math @stable ICU 2.1 */ + UCHAR_MATH=23, + /** Binary property Noncharacter_Code_Point. + Code points that are explicitly defined as illegal + for the encoding of characters. @stable ICU 2.1 */ + UCHAR_NONCHARACTER_CODE_POINT=24, + /** Binary property Quotation_Mark. @stable ICU 2.1 */ + UCHAR_QUOTATION_MARK=25, + /** Binary property Radical (new in Unicode 3.2). + For programmatic determination of + Ideographic Description Sequences. @stable ICU 2.1 */ + UCHAR_RADICAL=26, + /** Binary property Soft_Dotted (new in Unicode 3.2). + Characters with a "soft dot", like i or j. + An accent placed on these characters causes + the dot to disappear. @stable ICU 2.1 */ + UCHAR_SOFT_DOTTED=27, + /** Binary property Terminal_Punctuation. + Punctuation characters that generally mark + the end of textual units. @stable ICU 2.1 */ + UCHAR_TERMINAL_PUNCTUATION=28, + /** Binary property Unified_Ideograph (new in Unicode 3.2). + For programmatic determination of + Ideographic Description Sequences. @stable ICU 2.1 */ + UCHAR_UNIFIED_IDEOGRAPH=29, + /** Binary property Uppercase. Same as u_isUUppercase, different from u_isupper. + Lu+Other_Uppercase @stable ICU 2.1 */ + UCHAR_UPPERCASE=30, + /** Binary property White_Space. + Same as u_isUWhiteSpace, different from u_isspace and u_isWhitespace. + Space characters+TAB+CR+LF-ZWSP-ZWNBSP @stable ICU 2.1 */ + UCHAR_WHITE_SPACE=31, + /** Binary property XID_Continue. + ID_Continue modified to allow closure under + normalization forms NFKC and NFKD. @stable ICU 2.1 */ + UCHAR_XID_CONTINUE=32, + /** Binary property XID_Start. ID_Start modified to allow + closure under normalization forms NFKC and NFKD. @stable ICU 2.1 */ + UCHAR_XID_START=33, + /** Binary property Case_Sensitive. Either the source of a case + mapping or _in_ the target of a case mapping. Not the same as + the general category Cased_Letter. @stable ICU 2.6 */ + UCHAR_CASE_SENSITIVE=34, + /** Binary property STerm (new in Unicode 4.0.1). + Sentence Terminal. Used in UAX #29: Text Boundaries + (http://www.unicode.org/reports/tr29/) + @stable ICU 3.0 */ + UCHAR_S_TERM=35, + /** Binary property Variation_Selector (new in Unicode 4.0.1). + Indicates all those characters that qualify as Variation Selectors. + For details on the behavior of these characters, + see StandardizedVariants.html and 15.6 Variation Selectors. + @stable ICU 3.0 */ + UCHAR_VARIATION_SELECTOR=36, + /** Binary property NFD_Inert. + ICU-specific property for characters that are inert under NFD, + i.e., they do not interact with adjacent characters. + See the documentation for the Normalizer2 class and the + Normalizer2::isInert() method. + @stable ICU 3.0 */ + UCHAR_NFD_INERT=37, + /** Binary property NFKD_Inert. + ICU-specific property for characters that are inert under NFKD, + i.e., they do not interact with adjacent characters. + See the documentation for the Normalizer2 class and the + Normalizer2::isInert() method. + @stable ICU 3.0 */ + UCHAR_NFKD_INERT=38, + /** Binary property NFC_Inert. + ICU-specific property for characters that are inert under NFC, + i.e., they do not interact with adjacent characters. + See the documentation for the Normalizer2 class and the + Normalizer2::isInert() method. + @stable ICU 3.0 */ + UCHAR_NFC_INERT=39, + /** Binary property NFKC_Inert. + ICU-specific property for characters that are inert under NFKC, + i.e., they do not interact with adjacent characters. + See the documentation for the Normalizer2 class and the + Normalizer2::isInert() method. + @stable ICU 3.0 */ + UCHAR_NFKC_INERT=40, + /** Binary Property Segment_Starter. + ICU-specific property for characters that are starters in terms of + Unicode normalization and combining character sequences. + They have ccc=0 and do not occur in non-initial position of the + canonical decomposition of any character + (like a-umlaut in NFD and a Jamo T in an NFD(Hangul LVT)). + ICU uses this property for segmenting a string for generating a set of + canonically equivalent strings, e.g. for canonical closure while + processing collation tailoring rules. + @stable ICU 3.0 */ + UCHAR_SEGMENT_STARTER=41, + /** Binary property Pattern_Syntax (new in Unicode 4.1). + See UAX #31 Identifier and Pattern Syntax + (http://www.unicode.org/reports/tr31/) + @stable ICU 3.4 */ + UCHAR_PATTERN_SYNTAX=42, + /** Binary property Pattern_White_Space (new in Unicode 4.1). + See UAX #31 Identifier and Pattern Syntax + (http://www.unicode.org/reports/tr31/) + @stable ICU 3.4 */ + UCHAR_PATTERN_WHITE_SPACE=43, + /** Binary property alnum (a C/POSIX character class). + Implemented according to the UTS #18 Annex C Standard Recommendation. + See the uchar.h file documentation. + @stable ICU 3.4 */ + UCHAR_POSIX_ALNUM=44, + /** Binary property blank (a C/POSIX character class). + Implemented according to the UTS #18 Annex C Standard Recommendation. + See the uchar.h file documentation. + @stable ICU 3.4 */ + UCHAR_POSIX_BLANK=45, + /** Binary property graph (a C/POSIX character class). + Implemented according to the UTS #18 Annex C Standard Recommendation. + See the uchar.h file documentation. + @stable ICU 3.4 */ + UCHAR_POSIX_GRAPH=46, + /** Binary property print (a C/POSIX character class). + Implemented according to the UTS #18 Annex C Standard Recommendation. + See the uchar.h file documentation. + @stable ICU 3.4 */ + UCHAR_POSIX_PRINT=47, + /** Binary property xdigit (a C/POSIX character class). + Implemented according to the UTS #18 Annex C Standard Recommendation. + See the uchar.h file documentation. + @stable ICU 3.4 */ + UCHAR_POSIX_XDIGIT=48, + /** Binary property Cased. For Lowercase, Uppercase and Titlecase characters. @stable ICU 4.4 */ + UCHAR_CASED=49, + /** Binary property Case_Ignorable. Used in context-sensitive case mappings. @stable ICU 4.4 */ + UCHAR_CASE_IGNORABLE=50, + /** Binary property Changes_When_Lowercased. @stable ICU 4.4 */ + UCHAR_CHANGES_WHEN_LOWERCASED=51, + /** Binary property Changes_When_Uppercased. @stable ICU 4.4 */ + UCHAR_CHANGES_WHEN_UPPERCASED=52, + /** Binary property Changes_When_Titlecased. @stable ICU 4.4 */ + UCHAR_CHANGES_WHEN_TITLECASED=53, + /** Binary property Changes_When_Casefolded. @stable ICU 4.4 */ + UCHAR_CHANGES_WHEN_CASEFOLDED=54, + /** Binary property Changes_When_Casemapped. @stable ICU 4.4 */ + UCHAR_CHANGES_WHEN_CASEMAPPED=55, + /** Binary property Changes_When_NFKC_Casefolded. @stable ICU 4.4 */ + UCHAR_CHANGES_WHEN_NFKC_CASEFOLDED=56, + /** + * Binary property Emoji. + * See http://www.unicode.org/reports/tr51/#Emoji_Properties + * + * @stable ICU 57 + */ + UCHAR_EMOJI=57, + /** + * Binary property Emoji_Presentation. + * See http://www.unicode.org/reports/tr51/#Emoji_Properties + * + * @stable ICU 57 + */ + UCHAR_EMOJI_PRESENTATION=58, + /** + * Binary property Emoji_Modifier. + * See http://www.unicode.org/reports/tr51/#Emoji_Properties + * + * @stable ICU 57 + */ + UCHAR_EMOJI_MODIFIER=59, + /** + * Binary property Emoji_Modifier_Base. + * See http://www.unicode.org/reports/tr51/#Emoji_Properties + * + * @stable ICU 57 + */ + UCHAR_EMOJI_MODIFIER_BASE=60, + /** + * Binary property Emoji_Component. + * See http://www.unicode.org/reports/tr51/#Emoji_Properties + * + * @stable ICU 60 + */ + UCHAR_EMOJI_COMPONENT=61, + /** + * Binary property Regional_Indicator. + * @stable ICU 60 + */ + UCHAR_REGIONAL_INDICATOR=62, + /** + * Binary property Prepended_Concatenation_Mark. + * @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, + /** + * Binary property of strings Basic_Emoji. + * See https://www.unicode.org/reports/tr51/#Emoji_Sets + * + * @stable ICU 70 + */ + UCHAR_BASIC_EMOJI=65, + /** + * Binary property of strings Emoji_Keycap_Sequence. + * See https://www.unicode.org/reports/tr51/#Emoji_Sets + * + * @stable ICU 70 + */ + UCHAR_EMOJI_KEYCAP_SEQUENCE=66, + /** + * Binary property of strings RGI_Emoji_Modifier_Sequence. + * See https://www.unicode.org/reports/tr51/#Emoji_Sets + * + * @stable ICU 70 + */ + UCHAR_RGI_EMOJI_MODIFIER_SEQUENCE=67, + /** + * Binary property of strings RGI_Emoji_Flag_Sequence. + * See https://www.unicode.org/reports/tr51/#Emoji_Sets + * + * @stable ICU 70 + */ + UCHAR_RGI_EMOJI_FLAG_SEQUENCE=68, + /** + * Binary property of strings RGI_Emoji_Tag_Sequence. + * See https://www.unicode.org/reports/tr51/#Emoji_Sets + * + * @stable ICU 70 + */ + UCHAR_RGI_EMOJI_TAG_SEQUENCE=69, + /** + * Binary property of strings RGI_Emoji_ZWJ_Sequence. + * See https://www.unicode.org/reports/tr51/#Emoji_Sets + * + * @stable ICU 70 + */ + UCHAR_RGI_EMOJI_ZWJ_SEQUENCE=70, + /** + * Binary property of strings RGI_Emoji. + * See https://www.unicode.org/reports/tr51/#Emoji_Sets + * + * @stable ICU 70 + */ + UCHAR_RGI_EMOJI=71, +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the last constant for binary Unicode properties. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UCHAR_BINARY_LIMIT=72, +#endif // U_HIDE_DEPRECATED_API + + /** Enumerated property Bidi_Class. + Same as u_charDirection, returns UCharDirection values. @stable ICU 2.2 */ + UCHAR_BIDI_CLASS=0x1000, + /** First constant for enumerated/integer Unicode properties. @stable ICU 2.2 */ + UCHAR_INT_START=UCHAR_BIDI_CLASS, + /** Enumerated property Block. + Same as ublock_getCode, returns UBlockCode values. @stable ICU 2.2 */ + UCHAR_BLOCK=0x1001, + /** Enumerated property Canonical_Combining_Class. + Same as u_getCombiningClass, returns 8-bit numeric values. @stable ICU 2.2 */ + UCHAR_CANONICAL_COMBINING_CLASS=0x1002, + /** Enumerated property Decomposition_Type. + Returns UDecompositionType values. @stable ICU 2.2 */ + UCHAR_DECOMPOSITION_TYPE=0x1003, + /** Enumerated property East_Asian_Width. + See http://www.unicode.org/reports/tr11/ + Returns UEastAsianWidth values. @stable ICU 2.2 */ + UCHAR_EAST_ASIAN_WIDTH=0x1004, + /** Enumerated property General_Category. + Same as u_charType, returns UCharCategory values. @stable ICU 2.2 */ + UCHAR_GENERAL_CATEGORY=0x1005, + /** Enumerated property Joining_Group. + Returns UJoiningGroup values. @stable ICU 2.2 */ + UCHAR_JOINING_GROUP=0x1006, + /** Enumerated property Joining_Type. + Returns UJoiningType values. @stable ICU 2.2 */ + UCHAR_JOINING_TYPE=0x1007, + /** Enumerated property Line_Break. + Returns ULineBreak values. @stable ICU 2.2 */ + UCHAR_LINE_BREAK=0x1008, + /** Enumerated property Numeric_Type. + Returns UNumericType values. @stable ICU 2.2 */ + UCHAR_NUMERIC_TYPE=0x1009, + /** Enumerated property Script. + Same as uscript_getScript, returns UScriptCode values. @stable ICU 2.2 */ + UCHAR_SCRIPT=0x100A, + /** Enumerated property Hangul_Syllable_Type, new in Unicode 4. + Returns UHangulSyllableType values. @stable ICU 2.6 */ + UCHAR_HANGUL_SYLLABLE_TYPE=0x100B, + /** Enumerated property NFD_Quick_Check. + Returns UNormalizationCheckResult values. @stable ICU 3.0 */ + UCHAR_NFD_QUICK_CHECK=0x100C, + /** Enumerated property NFKD_Quick_Check. + Returns UNormalizationCheckResult values. @stable ICU 3.0 */ + UCHAR_NFKD_QUICK_CHECK=0x100D, + /** Enumerated property NFC_Quick_Check. + Returns UNormalizationCheckResult values. @stable ICU 3.0 */ + UCHAR_NFC_QUICK_CHECK=0x100E, + /** Enumerated property NFKC_Quick_Check. + Returns UNormalizationCheckResult values. @stable ICU 3.0 */ + UCHAR_NFKC_QUICK_CHECK=0x100F, + /** Enumerated property Lead_Canonical_Combining_Class. + ICU-specific property for the ccc of the first code point + of the decomposition, or lccc(c)=ccc(NFD(c)[0]). + Useful for checking for canonically ordered text; + see UNORM_FCD and http://www.unicode.org/notes/tn5/#FCD . + Returns 8-bit numeric values like UCHAR_CANONICAL_COMBINING_CLASS. @stable ICU 3.0 */ + UCHAR_LEAD_CANONICAL_COMBINING_CLASS=0x1010, + /** Enumerated property Trail_Canonical_Combining_Class. + ICU-specific property for the ccc of the last code point + of the decomposition, or tccc(c)=ccc(NFD(c)[last]). + Useful for checking for canonically ordered text; + see UNORM_FCD and http://www.unicode.org/notes/tn5/#FCD . + Returns 8-bit numeric values like UCHAR_CANONICAL_COMBINING_CLASS. @stable ICU 3.0 */ + UCHAR_TRAIL_CANONICAL_COMBINING_CLASS=0x1011, + /** Enumerated property Grapheme_Cluster_Break (new in Unicode 4.1). + Used in UAX #29: Text Boundaries + (http://www.unicode.org/reports/tr29/) + Returns UGraphemeClusterBreak values. @stable ICU 3.4 */ + UCHAR_GRAPHEME_CLUSTER_BREAK=0x1012, + /** Enumerated property Sentence_Break (new in Unicode 4.1). + Used in UAX #29: Text Boundaries + (http://www.unicode.org/reports/tr29/) + Returns USentenceBreak values. @stable ICU 3.4 */ + UCHAR_SENTENCE_BREAK=0x1013, + /** Enumerated property Word_Break (new in Unicode 4.1). + Used in UAX #29: Text Boundaries + (http://www.unicode.org/reports/tr29/) + Returns UWordBreakValues values. @stable ICU 3.4 */ + UCHAR_WORD_BREAK=0x1014, + /** Enumerated property Bidi_Paired_Bracket_Type (new in Unicode 6.3). + Used in UAX #9: Unicode Bidirectional Algorithm + (http://www.unicode.org/reports/tr9/) + Returns UBidiPairedBracketType values. @stable ICU 52 */ + UCHAR_BIDI_PAIRED_BRACKET_TYPE=0x1015, + /** + * Enumerated property Indic_Positional_Category. + * New in Unicode 6.0 as provisional property Indic_Matra_Category; + * renamed and changed to informative in Unicode 8.0. + * See http://www.unicode.org/reports/tr44/#IndicPositionalCategory.txt + * @stable ICU 63 + */ + UCHAR_INDIC_POSITIONAL_CATEGORY=0x1016, + /** + * Enumerated property Indic_Syllabic_Category. + * New in Unicode 6.0 as provisional; informative since Unicode 8.0. + * See http://www.unicode.org/reports/tr44/#IndicSyllabicCategory.txt + * @stable ICU 63 + */ + UCHAR_INDIC_SYLLABIC_CATEGORY=0x1017, + /** + * Enumerated property Vertical_Orientation. + * Used for UAX #50 Unicode Vertical Text Layout (https://www.unicode.org/reports/tr50/). + * New as a UCD property in Unicode 10.0. + * @stable ICU 63 + */ + UCHAR_VERTICAL_ORIENTATION=0x1018, +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the last constant for enumerated/integer Unicode properties. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UCHAR_INT_LIMIT=0x1019, +#endif // U_HIDE_DEPRECATED_API + + /** Bitmask property General_Category_Mask. + This is the General_Category property returned as a bit mask. + When used in u_getIntPropertyValue(c), same as U_MASK(u_charType(c)), + returns bit masks for UCharCategory values where exactly one bit is set. + When used with u_getPropertyValueName() and u_getPropertyValueEnum(), + a multi-bit mask is used for sets of categories like "Letters". + Mask values should be cast to uint32_t. + @stable ICU 2.4 */ + UCHAR_GENERAL_CATEGORY_MASK=0x2000, + /** First constant for bit-mask Unicode properties. @stable ICU 2.4 */ + UCHAR_MASK_START=UCHAR_GENERAL_CATEGORY_MASK, +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the last constant for bit-mask Unicode properties. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UCHAR_MASK_LIMIT=0x2001, +#endif // U_HIDE_DEPRECATED_API + + /** Double property Numeric_Value. + Corresponds to u_getNumericValue. @stable ICU 2.4 */ + UCHAR_NUMERIC_VALUE=0x3000, + /** First constant for double Unicode properties. @stable ICU 2.4 */ + UCHAR_DOUBLE_START=UCHAR_NUMERIC_VALUE, +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the last constant for double Unicode properties. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UCHAR_DOUBLE_LIMIT=0x3001, +#endif // U_HIDE_DEPRECATED_API + + /** String property Age. + Corresponds to u_charAge. @stable ICU 2.4 */ + UCHAR_AGE=0x4000, + /** First constant for string Unicode properties. @stable ICU 2.4 */ + UCHAR_STRING_START=UCHAR_AGE, + /** String property Bidi_Mirroring_Glyph. + Corresponds to u_charMirror. @stable ICU 2.4 */ + UCHAR_BIDI_MIRRORING_GLYPH=0x4001, + /** String property Case_Folding. + Corresponds to u_strFoldCase in ustring.h. @stable ICU 2.4 */ + UCHAR_CASE_FOLDING=0x4002, +#ifndef U_HIDE_DEPRECATED_API + /** Deprecated string property ISO_Comment. + Corresponds to u_getISOComment. @deprecated ICU 49 */ + UCHAR_ISO_COMMENT=0x4003, +#endif /* U_HIDE_DEPRECATED_API */ + /** String property Lowercase_Mapping. + Corresponds to u_strToLower in ustring.h. @stable ICU 2.4 */ + UCHAR_LOWERCASE_MAPPING=0x4004, + /** String property Name. + Corresponds to u_charName. @stable ICU 2.4 */ + UCHAR_NAME=0x4005, + /** String property Simple_Case_Folding. + Corresponds to u_foldCase. @stable ICU 2.4 */ + UCHAR_SIMPLE_CASE_FOLDING=0x4006, + /** String property Simple_Lowercase_Mapping. + Corresponds to u_tolower. @stable ICU 2.4 */ + UCHAR_SIMPLE_LOWERCASE_MAPPING=0x4007, + /** String property Simple_Titlecase_Mapping. + Corresponds to u_totitle. @stable ICU 2.4 */ + UCHAR_SIMPLE_TITLECASE_MAPPING=0x4008, + /** String property Simple_Uppercase_Mapping. + Corresponds to u_toupper. @stable ICU 2.4 */ + UCHAR_SIMPLE_UPPERCASE_MAPPING=0x4009, + /** String property Titlecase_Mapping. + Corresponds to u_strToTitle in ustring.h. @stable ICU 2.4 */ + UCHAR_TITLECASE_MAPPING=0x400A, +#ifndef U_HIDE_DEPRECATED_API + /** String property Unicode_1_Name. + This property is of little practical value. + Beginning with ICU 49, ICU APIs return an empty string for this property. + Corresponds to u_charName(U_UNICODE_10_CHAR_NAME). @deprecated ICU 49 */ + UCHAR_UNICODE_1_NAME=0x400B, +#endif /* U_HIDE_DEPRECATED_API */ + /** String property Uppercase_Mapping. + Corresponds to u_strToUpper in ustring.h. @stable ICU 2.4 */ + UCHAR_UPPERCASE_MAPPING=0x400C, + /** String property Bidi_Paired_Bracket (new in Unicode 6.3). + Corresponds to u_getBidiPairedBracket. @stable ICU 52 */ + UCHAR_BIDI_PAIRED_BRACKET=0x400D, +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the last constant for string Unicode properties. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UCHAR_STRING_LIMIT=0x400E, +#endif // U_HIDE_DEPRECATED_API + + /** Miscellaneous property Script_Extensions (new in Unicode 6.0). + Some characters are commonly used in multiple scripts. + For more information, see UAX #24: http://www.unicode.org/reports/tr24/. + Corresponds to uscript_hasScript and uscript_getScriptExtensions in uscript.h. + @stable ICU 4.6 */ + UCHAR_SCRIPT_EXTENSIONS=0x7000, + /** First constant for Unicode properties with unusual value types. @stable ICU 4.6 */ + UCHAR_OTHER_PROPERTY_START=UCHAR_SCRIPT_EXTENSIONS, +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the last constant for Unicode properties with unusual value types. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UCHAR_OTHER_PROPERTY_LIMIT=0x7001, +#endif // U_HIDE_DEPRECATED_API + + /** Represents a nonexistent or invalid property or property value. @stable ICU 2.4 */ + UCHAR_INVALID_CODE = -1 +} UProperty; + +/** + * Data for enumerated Unicode general category types. + * See http://www.unicode.org/Public/UNIDATA/UnicodeData.html . + * @stable ICU 2.0 + */ +typedef enum UCharCategory +{ + /* + * Note: UCharCategory constants and their API comments are parsed by preparseucd.py. + * It matches pairs of lines like + * / ** comment... * / + * U_<[A-Z_]+> = , + */ + + /** Non-category for unassigned and non-character code points. @stable ICU 2.0 */ + U_UNASSIGNED = 0, + /** Cn "Other, Not Assigned (no characters in [UnicodeData.txt] have this property)" (same as U_UNASSIGNED!) @stable ICU 2.0 */ + U_GENERAL_OTHER_TYPES = 0, + /** Lu @stable ICU 2.0 */ + U_UPPERCASE_LETTER = 1, + /** Ll @stable ICU 2.0 */ + U_LOWERCASE_LETTER = 2, + /** Lt @stable ICU 2.0 */ + U_TITLECASE_LETTER = 3, + /** Lm @stable ICU 2.0 */ + U_MODIFIER_LETTER = 4, + /** Lo @stable ICU 2.0 */ + U_OTHER_LETTER = 5, + /** Mn @stable ICU 2.0 */ + U_NON_SPACING_MARK = 6, + /** Me @stable ICU 2.0 */ + U_ENCLOSING_MARK = 7, + /** Mc @stable ICU 2.0 */ + U_COMBINING_SPACING_MARK = 8, + /** Nd @stable ICU 2.0 */ + U_DECIMAL_DIGIT_NUMBER = 9, + /** Nl @stable ICU 2.0 */ + U_LETTER_NUMBER = 10, + /** No @stable ICU 2.0 */ + U_OTHER_NUMBER = 11, + /** Zs @stable ICU 2.0 */ + U_SPACE_SEPARATOR = 12, + /** Zl @stable ICU 2.0 */ + U_LINE_SEPARATOR = 13, + /** Zp @stable ICU 2.0 */ + U_PARAGRAPH_SEPARATOR = 14, + /** Cc @stable ICU 2.0 */ + U_CONTROL_CHAR = 15, + /** Cf @stable ICU 2.0 */ + U_FORMAT_CHAR = 16, + /** Co @stable ICU 2.0 */ + U_PRIVATE_USE_CHAR = 17, + /** Cs @stable ICU 2.0 */ + U_SURROGATE = 18, + /** Pd @stable ICU 2.0 */ + U_DASH_PUNCTUATION = 19, + /** Ps @stable ICU 2.0 */ + U_START_PUNCTUATION = 20, + /** Pe @stable ICU 2.0 */ + U_END_PUNCTUATION = 21, + /** Pc @stable ICU 2.0 */ + U_CONNECTOR_PUNCTUATION = 22, + /** Po @stable ICU 2.0 */ + U_OTHER_PUNCTUATION = 23, + /** Sm @stable ICU 2.0 */ + U_MATH_SYMBOL = 24, + /** Sc @stable ICU 2.0 */ + U_CURRENCY_SYMBOL = 25, + /** Sk @stable ICU 2.0 */ + U_MODIFIER_SYMBOL = 26, + /** So @stable ICU 2.0 */ + U_OTHER_SYMBOL = 27, + /** Pi @stable ICU 2.0 */ + U_INITIAL_PUNCTUATION = 28, + /** Pf @stable ICU 2.0 */ + U_FINAL_PUNCTUATION = 29, + /** + * One higher than the last enum UCharCategory constant. + * This numeric value is stable (will not change), see + * http://www.unicode.org/policies/stability_policy.html#Property_Value + * + * @stable ICU 2.0 + */ + U_CHAR_CATEGORY_COUNT +} UCharCategory; + +/** + * U_GC_XX_MASK constants are bit flags corresponding to Unicode + * general category values. + * For each category, the nth bit is set if the numeric value of the + * corresponding UCharCategory constant is n. + * + * There are also some U_GC_Y_MASK constants for groups of general categories + * like L for all letter categories. + * + * @see u_charType + * @see U_GET_GC_MASK + * @see UCharCategory + * @stable ICU 2.1 + */ +#define U_GC_CN_MASK U_MASK(U_GENERAL_OTHER_TYPES) + +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_LU_MASK U_MASK(U_UPPERCASE_LETTER) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_LL_MASK U_MASK(U_LOWERCASE_LETTER) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_LT_MASK U_MASK(U_TITLECASE_LETTER) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_LM_MASK U_MASK(U_MODIFIER_LETTER) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_LO_MASK U_MASK(U_OTHER_LETTER) + +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_MN_MASK U_MASK(U_NON_SPACING_MARK) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_ME_MASK U_MASK(U_ENCLOSING_MARK) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_MC_MASK U_MASK(U_COMBINING_SPACING_MARK) + +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_ND_MASK U_MASK(U_DECIMAL_DIGIT_NUMBER) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_NL_MASK U_MASK(U_LETTER_NUMBER) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_NO_MASK U_MASK(U_OTHER_NUMBER) + +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_ZS_MASK U_MASK(U_SPACE_SEPARATOR) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_ZL_MASK U_MASK(U_LINE_SEPARATOR) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_ZP_MASK U_MASK(U_PARAGRAPH_SEPARATOR) + +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_CC_MASK U_MASK(U_CONTROL_CHAR) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_CF_MASK U_MASK(U_FORMAT_CHAR) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_CO_MASK U_MASK(U_PRIVATE_USE_CHAR) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_CS_MASK U_MASK(U_SURROGATE) + +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_PD_MASK U_MASK(U_DASH_PUNCTUATION) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_PS_MASK U_MASK(U_START_PUNCTUATION) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_PE_MASK U_MASK(U_END_PUNCTUATION) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_PC_MASK U_MASK(U_CONNECTOR_PUNCTUATION) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_PO_MASK U_MASK(U_OTHER_PUNCTUATION) + +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_SM_MASK U_MASK(U_MATH_SYMBOL) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_SC_MASK U_MASK(U_CURRENCY_SYMBOL) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_SK_MASK U_MASK(U_MODIFIER_SYMBOL) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_SO_MASK U_MASK(U_OTHER_SYMBOL) + +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_PI_MASK U_MASK(U_INITIAL_PUNCTUATION) +/** Mask constant for a UCharCategory. @stable ICU 2.1 */ +#define U_GC_PF_MASK U_MASK(U_FINAL_PUNCTUATION) + + +/** Mask constant for multiple UCharCategory bits (L Letters). @stable ICU 2.1 */ +#define U_GC_L_MASK \ + (U_GC_LU_MASK|U_GC_LL_MASK|U_GC_LT_MASK|U_GC_LM_MASK|U_GC_LO_MASK) + +/** Mask constant for multiple UCharCategory bits (LC Cased Letters). @stable ICU 2.1 */ +#define U_GC_LC_MASK \ + (U_GC_LU_MASK|U_GC_LL_MASK|U_GC_LT_MASK) + +/** Mask constant for multiple UCharCategory bits (M Marks). @stable ICU 2.1 */ +#define U_GC_M_MASK (U_GC_MN_MASK|U_GC_ME_MASK|U_GC_MC_MASK) + +/** Mask constant for multiple UCharCategory bits (N Numbers). @stable ICU 2.1 */ +#define U_GC_N_MASK (U_GC_ND_MASK|U_GC_NL_MASK|U_GC_NO_MASK) + +/** Mask constant for multiple UCharCategory bits (Z Separators). @stable ICU 2.1 */ +#define U_GC_Z_MASK (U_GC_ZS_MASK|U_GC_ZL_MASK|U_GC_ZP_MASK) + +/** Mask constant for multiple UCharCategory bits (C Others). @stable ICU 2.1 */ +#define U_GC_C_MASK \ + (U_GC_CN_MASK|U_GC_CC_MASK|U_GC_CF_MASK|U_GC_CO_MASK|U_GC_CS_MASK) + +/** Mask constant for multiple UCharCategory bits (P Punctuation). @stable ICU 2.1 */ +#define U_GC_P_MASK \ + (U_GC_PD_MASK|U_GC_PS_MASK|U_GC_PE_MASK|U_GC_PC_MASK|U_GC_PO_MASK| \ + U_GC_PI_MASK|U_GC_PF_MASK) + +/** Mask constant for multiple UCharCategory bits (S Symbols). @stable ICU 2.1 */ +#define U_GC_S_MASK (U_GC_SM_MASK|U_GC_SC_MASK|U_GC_SK_MASK|U_GC_SO_MASK) + +/** + * This specifies the language directional property of a character set. + * @stable ICU 2.0 + */ +typedef enum UCharDirection { + /* + * Note: UCharDirection constants and their API comments are parsed by preparseucd.py. + * It matches pairs of lines like + * / ** comment... * / + * U_<[A-Z_]+> = , + */ + + /** L @stable ICU 2.0 */ + U_LEFT_TO_RIGHT = 0, + /** R @stable ICU 2.0 */ + U_RIGHT_TO_LEFT = 1, + /** EN @stable ICU 2.0 */ + U_EUROPEAN_NUMBER = 2, + /** ES @stable ICU 2.0 */ + U_EUROPEAN_NUMBER_SEPARATOR = 3, + /** ET @stable ICU 2.0 */ + U_EUROPEAN_NUMBER_TERMINATOR = 4, + /** AN @stable ICU 2.0 */ + U_ARABIC_NUMBER = 5, + /** CS @stable ICU 2.0 */ + U_COMMON_NUMBER_SEPARATOR = 6, + /** B @stable ICU 2.0 */ + U_BLOCK_SEPARATOR = 7, + /** S @stable ICU 2.0 */ + U_SEGMENT_SEPARATOR = 8, + /** WS @stable ICU 2.0 */ + U_WHITE_SPACE_NEUTRAL = 9, + /** ON @stable ICU 2.0 */ + U_OTHER_NEUTRAL = 10, + /** LRE @stable ICU 2.0 */ + U_LEFT_TO_RIGHT_EMBEDDING = 11, + /** LRO @stable ICU 2.0 */ + U_LEFT_TO_RIGHT_OVERRIDE = 12, + /** AL @stable ICU 2.0 */ + U_RIGHT_TO_LEFT_ARABIC = 13, + /** RLE @stable ICU 2.0 */ + U_RIGHT_TO_LEFT_EMBEDDING = 14, + /** RLO @stable ICU 2.0 */ + U_RIGHT_TO_LEFT_OVERRIDE = 15, + /** PDF @stable ICU 2.0 */ + U_POP_DIRECTIONAL_FORMAT = 16, + /** NSM @stable ICU 2.0 */ + U_DIR_NON_SPACING_MARK = 17, + /** BN @stable ICU 2.0 */ + U_BOUNDARY_NEUTRAL = 18, + /** FSI @stable ICU 52 */ + U_FIRST_STRONG_ISOLATE = 19, + /** LRI @stable ICU 52 */ + U_LEFT_TO_RIGHT_ISOLATE = 20, + /** RLI @stable ICU 52 */ + U_RIGHT_TO_LEFT_ISOLATE = 21, + /** PDI @stable ICU 52 */ + U_POP_DIRECTIONAL_ISOLATE = 22, +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest UCharDirection value. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_BIDI_CLASS). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_CHAR_DIRECTION_COUNT +#endif // U_HIDE_DEPRECATED_API +} UCharDirection; + +/** + * Bidi Paired Bracket Type constants. + * + * @see UCHAR_BIDI_PAIRED_BRACKET_TYPE + * @stable ICU 52 + */ +typedef enum UBidiPairedBracketType { + /* + * Note: UBidiPairedBracketType constants are parsed by preparseucd.py. + * It matches lines like + * U_BPT_ + */ + + /** Not a paired bracket. @stable ICU 52 */ + U_BPT_NONE, + /** Open paired bracket. @stable ICU 52 */ + U_BPT_OPEN, + /** Close paired bracket. @stable ICU 52 */ + U_BPT_CLOSE, +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal UBidiPairedBracketType value. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_BIDI_PAIRED_BRACKET_TYPE). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_BPT_COUNT /* 3 */ +#endif // U_HIDE_DEPRECATED_API +} UBidiPairedBracketType; + +/** + * Constants for Unicode blocks, see the Unicode Data file Blocks.txt + * @stable ICU 2.0 + */ +enum UBlockCode { + /* + * Note: UBlockCode constants are parsed by preparseucd.py. + * It matches lines like + * UBLOCK_ = , + */ + + /** New No_Block value in Unicode 4. @stable ICU 2.6 */ + UBLOCK_NO_BLOCK = 0, /*[none]*/ /* Special range indicating No_Block */ + + /** @stable ICU 2.0 */ + UBLOCK_BASIC_LATIN = 1, /*[0000]*/ + + /** @stable ICU 2.0 */ + UBLOCK_LATIN_1_SUPPLEMENT=2, /*[0080]*/ + + /** @stable ICU 2.0 */ + UBLOCK_LATIN_EXTENDED_A =3, /*[0100]*/ + + /** @stable ICU 2.0 */ + UBLOCK_LATIN_EXTENDED_B =4, /*[0180]*/ + + /** @stable ICU 2.0 */ + UBLOCK_IPA_EXTENSIONS =5, /*[0250]*/ + + /** @stable ICU 2.0 */ + UBLOCK_SPACING_MODIFIER_LETTERS =6, /*[02B0]*/ + + /** @stable ICU 2.0 */ + UBLOCK_COMBINING_DIACRITICAL_MARKS =7, /*[0300]*/ + + /** + * Unicode 3.2 renames this block to "Greek and Coptic". + * @stable ICU 2.0 + */ + UBLOCK_GREEK =8, /*[0370]*/ + + /** @stable ICU 2.0 */ + UBLOCK_CYRILLIC =9, /*[0400]*/ + + /** @stable ICU 2.0 */ + UBLOCK_ARMENIAN =10, /*[0530]*/ + + /** @stable ICU 2.0 */ + UBLOCK_HEBREW =11, /*[0590]*/ + + /** @stable ICU 2.0 */ + UBLOCK_ARABIC =12, /*[0600]*/ + + /** @stable ICU 2.0 */ + UBLOCK_SYRIAC =13, /*[0700]*/ + + /** @stable ICU 2.0 */ + UBLOCK_THAANA =14, /*[0780]*/ + + /** @stable ICU 2.0 */ + UBLOCK_DEVANAGARI =15, /*[0900]*/ + + /** @stable ICU 2.0 */ + UBLOCK_BENGALI =16, /*[0980]*/ + + /** @stable ICU 2.0 */ + UBLOCK_GURMUKHI =17, /*[0A00]*/ + + /** @stable ICU 2.0 */ + UBLOCK_GUJARATI =18, /*[0A80]*/ + + /** @stable ICU 2.0 */ + UBLOCK_ORIYA =19, /*[0B00]*/ + + /** @stable ICU 2.0 */ + UBLOCK_TAMIL =20, /*[0B80]*/ + + /** @stable ICU 2.0 */ + UBLOCK_TELUGU =21, /*[0C00]*/ + + /** @stable ICU 2.0 */ + UBLOCK_KANNADA =22, /*[0C80]*/ + + /** @stable ICU 2.0 */ + UBLOCK_MALAYALAM =23, /*[0D00]*/ + + /** @stable ICU 2.0 */ + UBLOCK_SINHALA =24, /*[0D80]*/ + + /** @stable ICU 2.0 */ + UBLOCK_THAI =25, /*[0E00]*/ + + /** @stable ICU 2.0 */ + UBLOCK_LAO =26, /*[0E80]*/ + + /** @stable ICU 2.0 */ + UBLOCK_TIBETAN =27, /*[0F00]*/ + + /** @stable ICU 2.0 */ + UBLOCK_MYANMAR =28, /*[1000]*/ + + /** @stable ICU 2.0 */ + UBLOCK_GEORGIAN =29, /*[10A0]*/ + + /** @stable ICU 2.0 */ + UBLOCK_HANGUL_JAMO =30, /*[1100]*/ + + /** @stable ICU 2.0 */ + UBLOCK_ETHIOPIC =31, /*[1200]*/ + + /** @stable ICU 2.0 */ + UBLOCK_CHEROKEE =32, /*[13A0]*/ + + /** @stable ICU 2.0 */ + UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS =33, /*[1400]*/ + + /** @stable ICU 2.0 */ + UBLOCK_OGHAM =34, /*[1680]*/ + + /** @stable ICU 2.0 */ + UBLOCK_RUNIC =35, /*[16A0]*/ + + /** @stable ICU 2.0 */ + UBLOCK_KHMER =36, /*[1780]*/ + + /** @stable ICU 2.0 */ + UBLOCK_MONGOLIAN =37, /*[1800]*/ + + /** @stable ICU 2.0 */ + UBLOCK_LATIN_EXTENDED_ADDITIONAL =38, /*[1E00]*/ + + /** @stable ICU 2.0 */ + UBLOCK_GREEK_EXTENDED =39, /*[1F00]*/ + + /** @stable ICU 2.0 */ + UBLOCK_GENERAL_PUNCTUATION =40, /*[2000]*/ + + /** @stable ICU 2.0 */ + UBLOCK_SUPERSCRIPTS_AND_SUBSCRIPTS =41, /*[2070]*/ + + /** @stable ICU 2.0 */ + UBLOCK_CURRENCY_SYMBOLS =42, /*[20A0]*/ + + /** + * Unicode 3.2 renames this block to "Combining Diacritical Marks for Symbols". + * @stable ICU 2.0 + */ + UBLOCK_COMBINING_MARKS_FOR_SYMBOLS =43, /*[20D0]*/ + + /** @stable ICU 2.0 */ + UBLOCK_LETTERLIKE_SYMBOLS =44, /*[2100]*/ + + /** @stable ICU 2.0 */ + UBLOCK_NUMBER_FORMS =45, /*[2150]*/ + + /** @stable ICU 2.0 */ + UBLOCK_ARROWS =46, /*[2190]*/ + + /** @stable ICU 2.0 */ + UBLOCK_MATHEMATICAL_OPERATORS =47, /*[2200]*/ + + /** @stable ICU 2.0 */ + UBLOCK_MISCELLANEOUS_TECHNICAL =48, /*[2300]*/ + + /** @stable ICU 2.0 */ + UBLOCK_CONTROL_PICTURES =49, /*[2400]*/ + + /** @stable ICU 2.0 */ + UBLOCK_OPTICAL_CHARACTER_RECOGNITION =50, /*[2440]*/ + + /** @stable ICU 2.0 */ + UBLOCK_ENCLOSED_ALPHANUMERICS =51, /*[2460]*/ + + /** @stable ICU 2.0 */ + UBLOCK_BOX_DRAWING =52, /*[2500]*/ + + /** @stable ICU 2.0 */ + UBLOCK_BLOCK_ELEMENTS =53, /*[2580]*/ + + /** @stable ICU 2.0 */ + UBLOCK_GEOMETRIC_SHAPES =54, /*[25A0]*/ + + /** @stable ICU 2.0 */ + UBLOCK_MISCELLANEOUS_SYMBOLS =55, /*[2600]*/ + + /** @stable ICU 2.0 */ + UBLOCK_DINGBATS =56, /*[2700]*/ + + /** @stable ICU 2.0 */ + UBLOCK_BRAILLE_PATTERNS =57, /*[2800]*/ + + /** @stable ICU 2.0 */ + UBLOCK_CJK_RADICALS_SUPPLEMENT =58, /*[2E80]*/ + + /** @stable ICU 2.0 */ + UBLOCK_KANGXI_RADICALS =59, /*[2F00]*/ + + /** @stable ICU 2.0 */ + UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS =60, /*[2FF0]*/ + + /** @stable ICU 2.0 */ + UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION =61, /*[3000]*/ + + /** @stable ICU 2.0 */ + UBLOCK_HIRAGANA =62, /*[3040]*/ + + /** @stable ICU 2.0 */ + UBLOCK_KATAKANA =63, /*[30A0]*/ + + /** @stable ICU 2.0 */ + UBLOCK_BOPOMOFO =64, /*[3100]*/ + + /** @stable ICU 2.0 */ + UBLOCK_HANGUL_COMPATIBILITY_JAMO =65, /*[3130]*/ + + /** @stable ICU 2.0 */ + UBLOCK_KANBUN =66, /*[3190]*/ + + /** @stable ICU 2.0 */ + UBLOCK_BOPOMOFO_EXTENDED =67, /*[31A0]*/ + + /** @stable ICU 2.0 */ + UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS =68, /*[3200]*/ + + /** @stable ICU 2.0 */ + UBLOCK_CJK_COMPATIBILITY =69, /*[3300]*/ + + /** @stable ICU 2.0 */ + UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A =70, /*[3400]*/ + + /** @stable ICU 2.0 */ + UBLOCK_CJK_UNIFIED_IDEOGRAPHS =71, /*[4E00]*/ + + /** @stable ICU 2.0 */ + UBLOCK_YI_SYLLABLES =72, /*[A000]*/ + + /** @stable ICU 2.0 */ + UBLOCK_YI_RADICALS =73, /*[A490]*/ + + /** @stable ICU 2.0 */ + UBLOCK_HANGUL_SYLLABLES =74, /*[AC00]*/ + + /** @stable ICU 2.0 */ + UBLOCK_HIGH_SURROGATES =75, /*[D800]*/ + + /** @stable ICU 2.0 */ + UBLOCK_HIGH_PRIVATE_USE_SURROGATES =76, /*[DB80]*/ + + /** @stable ICU 2.0 */ + UBLOCK_LOW_SURROGATES =77, /*[DC00]*/ + + /** + * Same as UBLOCK_PRIVATE_USE. + * Until Unicode 3.1.1, the corresponding block name was "Private Use", + * and multiple code point ranges had this block. + * Unicode 3.2 renames the block for the BMP PUA to "Private Use Area" and + * adds separate blocks for the supplementary PUAs. + * + * @stable ICU 2.0 + */ + UBLOCK_PRIVATE_USE_AREA =78, /*[E000]*/ + /** + * Same as UBLOCK_PRIVATE_USE_AREA. + * Until Unicode 3.1.1, the corresponding block name was "Private Use", + * and multiple code point ranges had this block. + * Unicode 3.2 renames the block for the BMP PUA to "Private Use Area" and + * adds separate blocks for the supplementary PUAs. + * + * @stable ICU 2.0 + */ + UBLOCK_PRIVATE_USE = UBLOCK_PRIVATE_USE_AREA, + + /** @stable ICU 2.0 */ + UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS =79, /*[F900]*/ + + /** @stable ICU 2.0 */ + UBLOCK_ALPHABETIC_PRESENTATION_FORMS =80, /*[FB00]*/ + + /** @stable ICU 2.0 */ + UBLOCK_ARABIC_PRESENTATION_FORMS_A =81, /*[FB50]*/ + + /** @stable ICU 2.0 */ + UBLOCK_COMBINING_HALF_MARKS =82, /*[FE20]*/ + + /** @stable ICU 2.0 */ + UBLOCK_CJK_COMPATIBILITY_FORMS =83, /*[FE30]*/ + + /** @stable ICU 2.0 */ + UBLOCK_SMALL_FORM_VARIANTS =84, /*[FE50]*/ + + /** @stable ICU 2.0 */ + UBLOCK_ARABIC_PRESENTATION_FORMS_B =85, /*[FE70]*/ + + /** @stable ICU 2.0 */ + UBLOCK_SPECIALS =86, /*[FFF0]*/ + + /** @stable ICU 2.0 */ + UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS =87, /*[FF00]*/ + + /* New blocks in Unicode 3.1 */ + + /** @stable ICU 2.0 */ + UBLOCK_OLD_ITALIC = 88, /*[10300]*/ + /** @stable ICU 2.0 */ + UBLOCK_GOTHIC = 89, /*[10330]*/ + /** @stable ICU 2.0 */ + UBLOCK_DESERET = 90, /*[10400]*/ + /** @stable ICU 2.0 */ + UBLOCK_BYZANTINE_MUSICAL_SYMBOLS = 91, /*[1D000]*/ + /** @stable ICU 2.0 */ + UBLOCK_MUSICAL_SYMBOLS = 92, /*[1D100]*/ + /** @stable ICU 2.0 */ + UBLOCK_MATHEMATICAL_ALPHANUMERIC_SYMBOLS = 93, /*[1D400]*/ + /** @stable ICU 2.0 */ + UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B = 94, /*[20000]*/ + /** @stable ICU 2.0 */ + UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT = 95, /*[2F800]*/ + /** @stable ICU 2.0 */ + UBLOCK_TAGS = 96, /*[E0000]*/ + + /* New blocks in Unicode 3.2 */ + + /** @stable ICU 3.0 */ + UBLOCK_CYRILLIC_SUPPLEMENT = 97, /*[0500]*/ + /** + * Unicode 4.0.1 renames the "Cyrillic Supplementary" block to "Cyrillic Supplement". + * @stable ICU 2.2 + */ + UBLOCK_CYRILLIC_SUPPLEMENTARY = UBLOCK_CYRILLIC_SUPPLEMENT, + /** @stable ICU 2.2 */ + UBLOCK_TAGALOG = 98, /*[1700]*/ + /** @stable ICU 2.2 */ + UBLOCK_HANUNOO = 99, /*[1720]*/ + /** @stable ICU 2.2 */ + UBLOCK_BUHID = 100, /*[1740]*/ + /** @stable ICU 2.2 */ + UBLOCK_TAGBANWA = 101, /*[1760]*/ + /** @stable ICU 2.2 */ + UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A = 102, /*[27C0]*/ + /** @stable ICU 2.2 */ + UBLOCK_SUPPLEMENTAL_ARROWS_A = 103, /*[27F0]*/ + /** @stable ICU 2.2 */ + UBLOCK_SUPPLEMENTAL_ARROWS_B = 104, /*[2900]*/ + /** @stable ICU 2.2 */ + UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B = 105, /*[2980]*/ + /** @stable ICU 2.2 */ + UBLOCK_SUPPLEMENTAL_MATHEMATICAL_OPERATORS = 106, /*[2A00]*/ + /** @stable ICU 2.2 */ + UBLOCK_KATAKANA_PHONETIC_EXTENSIONS = 107, /*[31F0]*/ + /** @stable ICU 2.2 */ + UBLOCK_VARIATION_SELECTORS = 108, /*[FE00]*/ + /** @stable ICU 2.2 */ + UBLOCK_SUPPLEMENTARY_PRIVATE_USE_AREA_A = 109, /*[F0000]*/ + /** @stable ICU 2.2 */ + UBLOCK_SUPPLEMENTARY_PRIVATE_USE_AREA_B = 110, /*[100000]*/ + + /* New blocks in Unicode 4 */ + + /** @stable ICU 2.6 */ + UBLOCK_LIMBU = 111, /*[1900]*/ + /** @stable ICU 2.6 */ + UBLOCK_TAI_LE = 112, /*[1950]*/ + /** @stable ICU 2.6 */ + UBLOCK_KHMER_SYMBOLS = 113, /*[19E0]*/ + /** @stable ICU 2.6 */ + UBLOCK_PHONETIC_EXTENSIONS = 114, /*[1D00]*/ + /** @stable ICU 2.6 */ + UBLOCK_MISCELLANEOUS_SYMBOLS_AND_ARROWS = 115, /*[2B00]*/ + /** @stable ICU 2.6 */ + UBLOCK_YIJING_HEXAGRAM_SYMBOLS = 116, /*[4DC0]*/ + /** @stable ICU 2.6 */ + UBLOCK_LINEAR_B_SYLLABARY = 117, /*[10000]*/ + /** @stable ICU 2.6 */ + UBLOCK_LINEAR_B_IDEOGRAMS = 118, /*[10080]*/ + /** @stable ICU 2.6 */ + UBLOCK_AEGEAN_NUMBERS = 119, /*[10100]*/ + /** @stable ICU 2.6 */ + UBLOCK_UGARITIC = 120, /*[10380]*/ + /** @stable ICU 2.6 */ + UBLOCK_SHAVIAN = 121, /*[10450]*/ + /** @stable ICU 2.6 */ + UBLOCK_OSMANYA = 122, /*[10480]*/ + /** @stable ICU 2.6 */ + UBLOCK_CYPRIOT_SYLLABARY = 123, /*[10800]*/ + /** @stable ICU 2.6 */ + UBLOCK_TAI_XUAN_JING_SYMBOLS = 124, /*[1D300]*/ + /** @stable ICU 2.6 */ + UBLOCK_VARIATION_SELECTORS_SUPPLEMENT = 125, /*[E0100]*/ + + /* New blocks in Unicode 4.1 */ + + /** @stable ICU 3.4 */ + UBLOCK_ANCIENT_GREEK_MUSICAL_NOTATION = 126, /*[1D200]*/ + /** @stable ICU 3.4 */ + UBLOCK_ANCIENT_GREEK_NUMBERS = 127, /*[10140]*/ + /** @stable ICU 3.4 */ + UBLOCK_ARABIC_SUPPLEMENT = 128, /*[0750]*/ + /** @stable ICU 3.4 */ + UBLOCK_BUGINESE = 129, /*[1A00]*/ + /** @stable ICU 3.4 */ + UBLOCK_CJK_STROKES = 130, /*[31C0]*/ + /** @stable ICU 3.4 */ + UBLOCK_COMBINING_DIACRITICAL_MARKS_SUPPLEMENT = 131, /*[1DC0]*/ + /** @stable ICU 3.4 */ + UBLOCK_COPTIC = 132, /*[2C80]*/ + /** @stable ICU 3.4 */ + UBLOCK_ETHIOPIC_EXTENDED = 133, /*[2D80]*/ + /** @stable ICU 3.4 */ + UBLOCK_ETHIOPIC_SUPPLEMENT = 134, /*[1380]*/ + /** @stable ICU 3.4 */ + UBLOCK_GEORGIAN_SUPPLEMENT = 135, /*[2D00]*/ + /** @stable ICU 3.4 */ + UBLOCK_GLAGOLITIC = 136, /*[2C00]*/ + /** @stable ICU 3.4 */ + UBLOCK_KHAROSHTHI = 137, /*[10A00]*/ + /** @stable ICU 3.4 */ + UBLOCK_MODIFIER_TONE_LETTERS = 138, /*[A700]*/ + /** @stable ICU 3.4 */ + UBLOCK_NEW_TAI_LUE = 139, /*[1980]*/ + /** @stable ICU 3.4 */ + UBLOCK_OLD_PERSIAN = 140, /*[103A0]*/ + /** @stable ICU 3.4 */ + UBLOCK_PHONETIC_EXTENSIONS_SUPPLEMENT = 141, /*[1D80]*/ + /** @stable ICU 3.4 */ + UBLOCK_SUPPLEMENTAL_PUNCTUATION = 142, /*[2E00]*/ + /** @stable ICU 3.4 */ + UBLOCK_SYLOTI_NAGRI = 143, /*[A800]*/ + /** @stable ICU 3.4 */ + UBLOCK_TIFINAGH = 144, /*[2D30]*/ + /** @stable ICU 3.4 */ + UBLOCK_VERTICAL_FORMS = 145, /*[FE10]*/ + + /* New blocks in Unicode 5.0 */ + + /** @stable ICU 3.6 */ + UBLOCK_NKO = 146, /*[07C0]*/ + /** @stable ICU 3.6 */ + UBLOCK_BALINESE = 147, /*[1B00]*/ + /** @stable ICU 3.6 */ + UBLOCK_LATIN_EXTENDED_C = 148, /*[2C60]*/ + /** @stable ICU 3.6 */ + UBLOCK_LATIN_EXTENDED_D = 149, /*[A720]*/ + /** @stable ICU 3.6 */ + UBLOCK_PHAGS_PA = 150, /*[A840]*/ + /** @stable ICU 3.6 */ + UBLOCK_PHOENICIAN = 151, /*[10900]*/ + /** @stable ICU 3.6 */ + UBLOCK_CUNEIFORM = 152, /*[12000]*/ + /** @stable ICU 3.6 */ + UBLOCK_CUNEIFORM_NUMBERS_AND_PUNCTUATION = 153, /*[12400]*/ + /** @stable ICU 3.6 */ + UBLOCK_COUNTING_ROD_NUMERALS = 154, /*[1D360]*/ + + /* New blocks in Unicode 5.1 */ + + /** @stable ICU 4.0 */ + UBLOCK_SUNDANESE = 155, /*[1B80]*/ + /** @stable ICU 4.0 */ + UBLOCK_LEPCHA = 156, /*[1C00]*/ + /** @stable ICU 4.0 */ + UBLOCK_OL_CHIKI = 157, /*[1C50]*/ + /** @stable ICU 4.0 */ + UBLOCK_CYRILLIC_EXTENDED_A = 158, /*[2DE0]*/ + /** @stable ICU 4.0 */ + UBLOCK_VAI = 159, /*[A500]*/ + /** @stable ICU 4.0 */ + UBLOCK_CYRILLIC_EXTENDED_B = 160, /*[A640]*/ + /** @stable ICU 4.0 */ + UBLOCK_SAURASHTRA = 161, /*[A880]*/ + /** @stable ICU 4.0 */ + UBLOCK_KAYAH_LI = 162, /*[A900]*/ + /** @stable ICU 4.0 */ + UBLOCK_REJANG = 163, /*[A930]*/ + /** @stable ICU 4.0 */ + UBLOCK_CHAM = 164, /*[AA00]*/ + /** @stable ICU 4.0 */ + UBLOCK_ANCIENT_SYMBOLS = 165, /*[10190]*/ + /** @stable ICU 4.0 */ + UBLOCK_PHAISTOS_DISC = 166, /*[101D0]*/ + /** @stable ICU 4.0 */ + UBLOCK_LYCIAN = 167, /*[10280]*/ + /** @stable ICU 4.0 */ + UBLOCK_CARIAN = 168, /*[102A0]*/ + /** @stable ICU 4.0 */ + UBLOCK_LYDIAN = 169, /*[10920]*/ + /** @stable ICU 4.0 */ + UBLOCK_MAHJONG_TILES = 170, /*[1F000]*/ + /** @stable ICU 4.0 */ + UBLOCK_DOMINO_TILES = 171, /*[1F030]*/ + + /* New blocks in Unicode 5.2 */ + + /** @stable ICU 4.4 */ + UBLOCK_SAMARITAN = 172, /*[0800]*/ + /** @stable ICU 4.4 */ + UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED = 173, /*[18B0]*/ + /** @stable ICU 4.4 */ + UBLOCK_TAI_THAM = 174, /*[1A20]*/ + /** @stable ICU 4.4 */ + UBLOCK_VEDIC_EXTENSIONS = 175, /*[1CD0]*/ + /** @stable ICU 4.4 */ + UBLOCK_LISU = 176, /*[A4D0]*/ + /** @stable ICU 4.4 */ + UBLOCK_BAMUM = 177, /*[A6A0]*/ + /** @stable ICU 4.4 */ + UBLOCK_COMMON_INDIC_NUMBER_FORMS = 178, /*[A830]*/ + /** @stable ICU 4.4 */ + UBLOCK_DEVANAGARI_EXTENDED = 179, /*[A8E0]*/ + /** @stable ICU 4.4 */ + UBLOCK_HANGUL_JAMO_EXTENDED_A = 180, /*[A960]*/ + /** @stable ICU 4.4 */ + UBLOCK_JAVANESE = 181, /*[A980]*/ + /** @stable ICU 4.4 */ + UBLOCK_MYANMAR_EXTENDED_A = 182, /*[AA60]*/ + /** @stable ICU 4.4 */ + UBLOCK_TAI_VIET = 183, /*[AA80]*/ + /** @stable ICU 4.4 */ + UBLOCK_MEETEI_MAYEK = 184, /*[ABC0]*/ + /** @stable ICU 4.4 */ + UBLOCK_HANGUL_JAMO_EXTENDED_B = 185, /*[D7B0]*/ + /** @stable ICU 4.4 */ + UBLOCK_IMPERIAL_ARAMAIC = 186, /*[10840]*/ + /** @stable ICU 4.4 */ + UBLOCK_OLD_SOUTH_ARABIAN = 187, /*[10A60]*/ + /** @stable ICU 4.4 */ + UBLOCK_AVESTAN = 188, /*[10B00]*/ + /** @stable ICU 4.4 */ + UBLOCK_INSCRIPTIONAL_PARTHIAN = 189, /*[10B40]*/ + /** @stable ICU 4.4 */ + UBLOCK_INSCRIPTIONAL_PAHLAVI = 190, /*[10B60]*/ + /** @stable ICU 4.4 */ + UBLOCK_OLD_TURKIC = 191, /*[10C00]*/ + /** @stable ICU 4.4 */ + UBLOCK_RUMI_NUMERAL_SYMBOLS = 192, /*[10E60]*/ + /** @stable ICU 4.4 */ + UBLOCK_KAITHI = 193, /*[11080]*/ + /** @stable ICU 4.4 */ + UBLOCK_EGYPTIAN_HIEROGLYPHS = 194, /*[13000]*/ + /** @stable ICU 4.4 */ + UBLOCK_ENCLOSED_ALPHANUMERIC_SUPPLEMENT = 195, /*[1F100]*/ + /** @stable ICU 4.4 */ + UBLOCK_ENCLOSED_IDEOGRAPHIC_SUPPLEMENT = 196, /*[1F200]*/ + /** @stable ICU 4.4 */ + UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C = 197, /*[2A700]*/ + + /* New blocks in Unicode 6.0 */ + + /** @stable ICU 4.6 */ + UBLOCK_MANDAIC = 198, /*[0840]*/ + /** @stable ICU 4.6 */ + UBLOCK_BATAK = 199, /*[1BC0]*/ + /** @stable ICU 4.6 */ + UBLOCK_ETHIOPIC_EXTENDED_A = 200, /*[AB00]*/ + /** @stable ICU 4.6 */ + UBLOCK_BRAHMI = 201, /*[11000]*/ + /** @stable ICU 4.6 */ + UBLOCK_BAMUM_SUPPLEMENT = 202, /*[16800]*/ + /** @stable ICU 4.6 */ + UBLOCK_KANA_SUPPLEMENT = 203, /*[1B000]*/ + /** @stable ICU 4.6 */ + UBLOCK_PLAYING_CARDS = 204, /*[1F0A0]*/ + /** @stable ICU 4.6 */ + UBLOCK_MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS = 205, /*[1F300]*/ + /** @stable ICU 4.6 */ + UBLOCK_EMOTICONS = 206, /*[1F600]*/ + /** @stable ICU 4.6 */ + UBLOCK_TRANSPORT_AND_MAP_SYMBOLS = 207, /*[1F680]*/ + /** @stable ICU 4.6 */ + UBLOCK_ALCHEMICAL_SYMBOLS = 208, /*[1F700]*/ + /** @stable ICU 4.6 */ + UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D = 209, /*[2B740]*/ + + /* New blocks in Unicode 6.1 */ + + /** @stable ICU 49 */ + UBLOCK_ARABIC_EXTENDED_A = 210, /*[08A0]*/ + /** @stable ICU 49 */ + UBLOCK_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS = 211, /*[1EE00]*/ + /** @stable ICU 49 */ + UBLOCK_CHAKMA = 212, /*[11100]*/ + /** @stable ICU 49 */ + UBLOCK_MEETEI_MAYEK_EXTENSIONS = 213, /*[AAE0]*/ + /** @stable ICU 49 */ + UBLOCK_MEROITIC_CURSIVE = 214, /*[109A0]*/ + /** @stable ICU 49 */ + UBLOCK_MEROITIC_HIEROGLYPHS = 215, /*[10980]*/ + /** @stable ICU 49 */ + UBLOCK_MIAO = 216, /*[16F00]*/ + /** @stable ICU 49 */ + UBLOCK_SHARADA = 217, /*[11180]*/ + /** @stable ICU 49 */ + UBLOCK_SORA_SOMPENG = 218, /*[110D0]*/ + /** @stable ICU 49 */ + UBLOCK_SUNDANESE_SUPPLEMENT = 219, /*[1CC0]*/ + /** @stable ICU 49 */ + UBLOCK_TAKRI = 220, /*[11680]*/ + + /* New blocks in Unicode 7.0 */ + + /** @stable ICU 54 */ + UBLOCK_BASSA_VAH = 221, /*[16AD0]*/ + /** @stable ICU 54 */ + UBLOCK_CAUCASIAN_ALBANIAN = 222, /*[10530]*/ + /** @stable ICU 54 */ + UBLOCK_COPTIC_EPACT_NUMBERS = 223, /*[102E0]*/ + /** @stable ICU 54 */ + UBLOCK_COMBINING_DIACRITICAL_MARKS_EXTENDED = 224, /*[1AB0]*/ + /** @stable ICU 54 */ + UBLOCK_DUPLOYAN = 225, /*[1BC00]*/ + /** @stable ICU 54 */ + UBLOCK_ELBASAN = 226, /*[10500]*/ + /** @stable ICU 54 */ + UBLOCK_GEOMETRIC_SHAPES_EXTENDED = 227, /*[1F780]*/ + /** @stable ICU 54 */ + UBLOCK_GRANTHA = 228, /*[11300]*/ + /** @stable ICU 54 */ + UBLOCK_KHOJKI = 229, /*[11200]*/ + /** @stable ICU 54 */ + UBLOCK_KHUDAWADI = 230, /*[112B0]*/ + /** @stable ICU 54 */ + UBLOCK_LATIN_EXTENDED_E = 231, /*[AB30]*/ + /** @stable ICU 54 */ + UBLOCK_LINEAR_A = 232, /*[10600]*/ + /** @stable ICU 54 */ + UBLOCK_MAHAJANI = 233, /*[11150]*/ + /** @stable ICU 54 */ + UBLOCK_MANICHAEAN = 234, /*[10AC0]*/ + /** @stable ICU 54 */ + UBLOCK_MENDE_KIKAKUI = 235, /*[1E800]*/ + /** @stable ICU 54 */ + UBLOCK_MODI = 236, /*[11600]*/ + /** @stable ICU 54 */ + UBLOCK_MRO = 237, /*[16A40]*/ + /** @stable ICU 54 */ + UBLOCK_MYANMAR_EXTENDED_B = 238, /*[A9E0]*/ + /** @stable ICU 54 */ + UBLOCK_NABATAEAN = 239, /*[10880]*/ + /** @stable ICU 54 */ + UBLOCK_OLD_NORTH_ARABIAN = 240, /*[10A80]*/ + /** @stable ICU 54 */ + UBLOCK_OLD_PERMIC = 241, /*[10350]*/ + /** @stable ICU 54 */ + UBLOCK_ORNAMENTAL_DINGBATS = 242, /*[1F650]*/ + /** @stable ICU 54 */ + UBLOCK_PAHAWH_HMONG = 243, /*[16B00]*/ + /** @stable ICU 54 */ + UBLOCK_PALMYRENE = 244, /*[10860]*/ + /** @stable ICU 54 */ + UBLOCK_PAU_CIN_HAU = 245, /*[11AC0]*/ + /** @stable ICU 54 */ + UBLOCK_PSALTER_PAHLAVI = 246, /*[10B80]*/ + /** @stable ICU 54 */ + UBLOCK_SHORTHAND_FORMAT_CONTROLS = 247, /*[1BCA0]*/ + /** @stable ICU 54 */ + UBLOCK_SIDDHAM = 248, /*[11580]*/ + /** @stable ICU 54 */ + UBLOCK_SINHALA_ARCHAIC_NUMBERS = 249, /*[111E0]*/ + /** @stable ICU 54 */ + UBLOCK_SUPPLEMENTAL_ARROWS_C = 250, /*[1F800]*/ + /** @stable ICU 54 */ + UBLOCK_TIRHUTA = 251, /*[11480]*/ + /** @stable ICU 54 */ + UBLOCK_WARANG_CITI = 252, /*[118A0]*/ + + /* New blocks in Unicode 8.0 */ + + /** @stable ICU 56 */ + UBLOCK_AHOM = 253, /*[11700]*/ + /** @stable ICU 56 */ + UBLOCK_ANATOLIAN_HIEROGLYPHS = 254, /*[14400]*/ + /** @stable ICU 56 */ + UBLOCK_CHEROKEE_SUPPLEMENT = 255, /*[AB70]*/ + /** @stable ICU 56 */ + UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E = 256, /*[2B820]*/ + /** @stable ICU 56 */ + UBLOCK_EARLY_DYNASTIC_CUNEIFORM = 257, /*[12480]*/ + /** @stable ICU 56 */ + UBLOCK_HATRAN = 258, /*[108E0]*/ + /** @stable ICU 56 */ + UBLOCK_MULTANI = 259, /*[11280]*/ + /** @stable ICU 56 */ + UBLOCK_OLD_HUNGARIAN = 260, /*[10C80]*/ + /** @stable ICU 56 */ + UBLOCK_SUPPLEMENTAL_SYMBOLS_AND_PICTOGRAPHS = 261, /*[1F900]*/ + /** @stable ICU 56 */ + UBLOCK_SUTTON_SIGNWRITING = 262, /*[1D800]*/ + + /* New blocks in Unicode 9.0 */ + + /** @stable ICU 58 */ + UBLOCK_ADLAM = 263, /*[1E900]*/ + /** @stable ICU 58 */ + UBLOCK_BHAIKSUKI = 264, /*[11C00]*/ + /** @stable ICU 58 */ + UBLOCK_CYRILLIC_EXTENDED_C = 265, /*[1C80]*/ + /** @stable ICU 58 */ + UBLOCK_GLAGOLITIC_SUPPLEMENT = 266, /*[1E000]*/ + /** @stable ICU 58 */ + UBLOCK_IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION = 267, /*[16FE0]*/ + /** @stable ICU 58 */ + UBLOCK_MARCHEN = 268, /*[11C70]*/ + /** @stable ICU 58 */ + UBLOCK_MONGOLIAN_SUPPLEMENT = 269, /*[11660]*/ + /** @stable ICU 58 */ + UBLOCK_NEWA = 270, /*[11400]*/ + /** @stable ICU 58 */ + UBLOCK_OSAGE = 271, /*[104B0]*/ + /** @stable ICU 58 */ + UBLOCK_TANGUT = 272, /*[17000]*/ + /** @stable ICU 58 */ + UBLOCK_TANGUT_COMPONENTS = 273, /*[18800]*/ + + // New blocks in Unicode 10.0 + + /** @stable ICU 60 */ + UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F = 274, /*[2CEB0]*/ + /** @stable ICU 60 */ + UBLOCK_KANA_EXTENDED_A = 275, /*[1B100]*/ + /** @stable ICU 60 */ + UBLOCK_MASARAM_GONDI = 276, /*[11D00]*/ + /** @stable ICU 60 */ + UBLOCK_NUSHU = 277, /*[1B170]*/ + /** @stable ICU 60 */ + UBLOCK_SOYOMBO = 278, /*[11A50]*/ + /** @stable ICU 60 */ + UBLOCK_SYRIAC_SUPPLEMENT = 279, /*[0860]*/ + /** @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]*/ + + // New blocks in Unicode 12.0 + + /** @stable ICU 64 */ + UBLOCK_EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS = 292, /*[13430]*/ + /** @stable ICU 64 */ + UBLOCK_ELYMAIC = 293, /*[10FE0]*/ + /** @stable ICU 64 */ + UBLOCK_NANDINAGARI = 294, /*[119A0]*/ + /** @stable ICU 64 */ + UBLOCK_NYIAKENG_PUACHUE_HMONG = 295, /*[1E100]*/ + /** @stable ICU 64 */ + UBLOCK_OTTOMAN_SIYAQ_NUMBERS = 296, /*[1ED00]*/ + /** @stable ICU 64 */ + UBLOCK_SMALL_KANA_EXTENSION = 297, /*[1B130]*/ + /** @stable ICU 64 */ + UBLOCK_SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A = 298, /*[1FA70]*/ + /** @stable ICU 64 */ + UBLOCK_TAMIL_SUPPLEMENT = 299, /*[11FC0]*/ + /** @stable ICU 64 */ + UBLOCK_WANCHO = 300, /*[1E2C0]*/ + + // New blocks in Unicode 13.0 + + /** @stable ICU 66 */ + UBLOCK_CHORASMIAN = 301, /*[10FB0]*/ + /** @stable ICU 66 */ + UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G = 302, /*[30000]*/ + /** @stable ICU 66 */ + UBLOCK_DIVES_AKURU = 303, /*[11900]*/ + /** @stable ICU 66 */ + UBLOCK_KHITAN_SMALL_SCRIPT = 304, /*[18B00]*/ + /** @stable ICU 66 */ + UBLOCK_LISU_SUPPLEMENT = 305, /*[11FB0]*/ + /** @stable ICU 66 */ + UBLOCK_SYMBOLS_FOR_LEGACY_COMPUTING = 306, /*[1FB00]*/ + /** @stable ICU 66 */ + UBLOCK_TANGUT_SUPPLEMENT = 307, /*[18D00]*/ + /** @stable ICU 66 */ + UBLOCK_YEZIDI = 308, /*[10E80]*/ + + // New blocks in Unicode 14.0 + + /** @stable ICU 70 */ + UBLOCK_ARABIC_EXTENDED_B = 309, /*[0870]*/ + /** @stable ICU 70 */ + UBLOCK_CYPRO_MINOAN = 310, /*[12F90]*/ + /** @stable ICU 70 */ + UBLOCK_ETHIOPIC_EXTENDED_B = 311, /*[1E7E0]*/ + /** @stable ICU 70 */ + UBLOCK_KANA_EXTENDED_B = 312, /*[1AFF0]*/ + /** @stable ICU 70 */ + UBLOCK_LATIN_EXTENDED_F = 313, /*[10780]*/ + /** @stable ICU 70 */ + UBLOCK_LATIN_EXTENDED_G = 314, /*[1DF00]*/ + /** @stable ICU 70 */ + UBLOCK_OLD_UYGHUR = 315, /*[10F70]*/ + /** @stable ICU 70 */ + UBLOCK_TANGSA = 316, /*[16A70]*/ + /** @stable ICU 70 */ + UBLOCK_TOTO = 317, /*[1E290]*/ + /** @stable ICU 70 */ + UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED_A = 318, /*[11AB0]*/ + /** @stable ICU 70 */ + UBLOCK_VITHKUQI = 319, /*[10570]*/ + /** @stable ICU 70 */ + UBLOCK_ZNAMENNY_MUSICAL_NOTATION = 320, /*[1CF00]*/ + + // New blocks in Unicode 15.0 + + /** @stable ICU 72 */ + UBLOCK_ARABIC_EXTENDED_C = 321, /*[10EC0]*/ + /** @stable ICU 72 */ + UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_H = 322, /*[31350]*/ + /** @stable ICU 72 */ + UBLOCK_CYRILLIC_EXTENDED_D = 323, /*[1E030]*/ + /** @stable ICU 72 */ + UBLOCK_DEVANAGARI_EXTENDED_A = 324, /*[11B00]*/ + /** @stable ICU 72 */ + UBLOCK_KAKTOVIK_NUMERALS = 325, /*[1D2C0]*/ + /** @stable ICU 72 */ + UBLOCK_KAWI = 326, /*[11F00]*/ + /** @stable ICU 72 */ + UBLOCK_NAG_MUNDARI = 327, /*[1E4D0]*/ + +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal UBlockCode value. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_BLOCK). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UBLOCK_COUNT = 328, +#endif // U_HIDE_DEPRECATED_API + + /** @stable ICU 2.0 */ + UBLOCK_INVALID_CODE=-1 +}; + +/** @stable ICU 2.0 */ +typedef enum UBlockCode UBlockCode; + +/** + * East Asian Width constants. + * + * @see UCHAR_EAST_ASIAN_WIDTH + * @see u_getIntPropertyValue + * @stable ICU 2.2 + */ +typedef enum UEastAsianWidth { + /* + * Note: UEastAsianWidth constants are parsed by preparseucd.py. + * It matches lines like + * U_EA_ + */ + + U_EA_NEUTRAL, /*[N]*/ + U_EA_AMBIGUOUS, /*[A]*/ + U_EA_HALFWIDTH, /*[H]*/ + U_EA_FULLWIDTH, /*[F]*/ + U_EA_NARROW, /*[Na]*/ + U_EA_WIDE, /*[W]*/ +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal UEastAsianWidth value. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_EAST_ASIAN_WIDTH). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_EA_COUNT +#endif // U_HIDE_DEPRECATED_API +} UEastAsianWidth; + +/** + * Selector constants for u_charName(). + * u_charName() returns the "modern" name of a + * Unicode character; or the name that was defined in + * Unicode version 1.0, before the Unicode standard merged + * with ISO-10646; or an "extended" name that gives each + * Unicode code point a unique name. + * + * @see u_charName + * @stable ICU 2.0 + */ +typedef enum UCharNameChoice { + /** Unicode character name (Name property). @stable ICU 2.0 */ + U_UNICODE_CHAR_NAME, +#ifndef U_HIDE_DEPRECATED_API + /** + * The Unicode_1_Name property value which is of little practical value. + * Beginning with ICU 49, ICU APIs return an empty string for this name choice. + * @deprecated ICU 49 + */ + U_UNICODE_10_CHAR_NAME, +#endif /* U_HIDE_DEPRECATED_API */ + /** Standard or synthetic character name. @stable ICU 2.0 */ + U_EXTENDED_CHAR_NAME = U_UNICODE_CHAR_NAME+2, + /** Corrected name from NameAliases.txt. @stable ICU 4.4 */ + U_CHAR_NAME_ALIAS, +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal UCharNameChoice value. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_CHAR_NAME_CHOICE_COUNT +#endif // U_HIDE_DEPRECATED_API +} UCharNameChoice; + +/** + * Selector constants for u_getPropertyName() and + * u_getPropertyValueName(). These selectors are used to choose which + * name is returned for a given property or value. All properties and + * values have a long name. Most have a short name, but some do not. + * Unicode allows for additional names, beyond the long and short + * name, which would be indicated by U_LONG_PROPERTY_NAME + i, where + * i=1, 2,... + * + * @see u_getPropertyName() + * @see u_getPropertyValueName() + * @stable ICU 2.4 + */ +typedef enum UPropertyNameChoice { + U_SHORT_PROPERTY_NAME, + U_LONG_PROPERTY_NAME, +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal UPropertyNameChoice value. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_PROPERTY_NAME_CHOICE_COUNT +#endif // U_HIDE_DEPRECATED_API +} UPropertyNameChoice; + +/** + * Decomposition Type constants. + * + * @see UCHAR_DECOMPOSITION_TYPE + * @stable ICU 2.2 + */ +typedef enum UDecompositionType { + /* + * Note: UDecompositionType constants are parsed by preparseucd.py. + * It matches lines like + * U_DT_ + */ + + U_DT_NONE, /*[none]*/ + U_DT_CANONICAL, /*[can]*/ + U_DT_COMPAT, /*[com]*/ + U_DT_CIRCLE, /*[enc]*/ + U_DT_FINAL, /*[fin]*/ + U_DT_FONT, /*[font]*/ + U_DT_FRACTION, /*[fra]*/ + U_DT_INITIAL, /*[init]*/ + U_DT_ISOLATED, /*[iso]*/ + U_DT_MEDIAL, /*[med]*/ + U_DT_NARROW, /*[nar]*/ + U_DT_NOBREAK, /*[nb]*/ + U_DT_SMALL, /*[sml]*/ + U_DT_SQUARE, /*[sqr]*/ + U_DT_SUB, /*[sub]*/ + U_DT_SUPER, /*[sup]*/ + U_DT_VERTICAL, /*[vert]*/ + U_DT_WIDE, /*[wide]*/ +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal UDecompositionType value. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_DECOMPOSITION_TYPE). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_DT_COUNT /* 18 */ +#endif // U_HIDE_DEPRECATED_API +} UDecompositionType; + +/** + * Joining Type constants. + * + * @see UCHAR_JOINING_TYPE + * @stable ICU 2.2 + */ +typedef enum UJoiningType { + /* + * Note: UJoiningType constants are parsed by preparseucd.py. + * It matches lines like + * U_JT_ + */ + + U_JT_NON_JOINING, /*[U]*/ + U_JT_JOIN_CAUSING, /*[C]*/ + U_JT_DUAL_JOINING, /*[D]*/ + U_JT_LEFT_JOINING, /*[L]*/ + U_JT_RIGHT_JOINING, /*[R]*/ + U_JT_TRANSPARENT, /*[T]*/ +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal UJoiningType value. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_JOINING_TYPE). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_JT_COUNT /* 6 */ +#endif // U_HIDE_DEPRECATED_API +} UJoiningType; + +/** + * Joining Group constants. + * + * @see UCHAR_JOINING_GROUP + * @stable ICU 2.2 + */ +typedef enum UJoiningGroup { + /* + * Note: UJoiningGroup constants are parsed by preparseucd.py. + * It matches lines like + * U_JG_ + */ + + U_JG_NO_JOINING_GROUP, + U_JG_AIN, + U_JG_ALAPH, + U_JG_ALEF, + U_JG_BEH, + U_JG_BETH, + U_JG_DAL, + U_JG_DALATH_RISH, + U_JG_E, + U_JG_FEH, + U_JG_FINAL_SEMKATH, + U_JG_GAF, + U_JG_GAMAL, + U_JG_HAH, + U_JG_TEH_MARBUTA_GOAL, /**< @stable ICU 4.6 */ + U_JG_HAMZA_ON_HEH_GOAL=U_JG_TEH_MARBUTA_GOAL, + U_JG_HE, + U_JG_HEH, + U_JG_HEH_GOAL, + U_JG_HETH, + U_JG_KAF, + U_JG_KAPH, + U_JG_KNOTTED_HEH, + U_JG_LAM, + U_JG_LAMADH, + U_JG_MEEM, + U_JG_MIM, + U_JG_NOON, + U_JG_NUN, + U_JG_PE, + U_JG_QAF, + U_JG_QAPH, + U_JG_REH, + U_JG_REVERSED_PE, + U_JG_SAD, + U_JG_SADHE, + U_JG_SEEN, + U_JG_SEMKATH, + U_JG_SHIN, + U_JG_SWASH_KAF, + U_JG_SYRIAC_WAW, + U_JG_TAH, + U_JG_TAW, + U_JG_TEH_MARBUTA, + U_JG_TETH, + U_JG_WAW, + U_JG_YEH, + U_JG_YEH_BARREE, + U_JG_YEH_WITH_TAIL, + U_JG_YUDH, + U_JG_YUDH_HE, + U_JG_ZAIN, + U_JG_FE, /**< @stable ICU 2.6 */ + U_JG_KHAPH, /**< @stable ICU 2.6 */ + U_JG_ZHAIN, /**< @stable ICU 2.6 */ + U_JG_BURUSHASKI_YEH_BARREE, /**< @stable ICU 4.0 */ + U_JG_FARSI_YEH, /**< @stable ICU 4.4 */ + U_JG_NYA, /**< @stable ICU 4.4 */ + U_JG_ROHINGYA_YEH, /**< @stable ICU 49 */ + U_JG_MANICHAEAN_ALEPH, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_AYIN, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_BETH, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_DALETH, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_DHAMEDH, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_FIVE, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_GIMEL, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_HETH, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_HUNDRED, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_KAPH, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_LAMEDH, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_MEM, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_NUN, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_ONE, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_PE, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_QOPH, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_RESH, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_SADHE, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_SAMEKH, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_TAW, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_TEN, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_TETH, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_THAMEDH, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_TWENTY, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_WAW, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_YODH, /**< @stable ICU 54 */ + U_JG_MANICHAEAN_ZAYIN, /**< @stable ICU 54 */ + U_JG_STRAIGHT_WAW, /**< @stable ICU 54 */ + U_JG_AFRICAN_FEH, /**< @stable ICU 58 */ + U_JG_AFRICAN_NOON, /**< @stable ICU 58 */ + U_JG_AFRICAN_QAF, /**< @stable ICU 58 */ + + U_JG_MALAYALAM_BHA, /**< @stable ICU 60 */ + U_JG_MALAYALAM_JA, /**< @stable ICU 60 */ + U_JG_MALAYALAM_LLA, /**< @stable ICU 60 */ + U_JG_MALAYALAM_LLLA, /**< @stable ICU 60 */ + U_JG_MALAYALAM_NGA, /**< @stable ICU 60 */ + U_JG_MALAYALAM_NNA, /**< @stable ICU 60 */ + U_JG_MALAYALAM_NNNA, /**< @stable ICU 60 */ + U_JG_MALAYALAM_NYA, /**< @stable ICU 60 */ + U_JG_MALAYALAM_RA, /**< @stable ICU 60 */ + 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 */ + + U_JG_THIN_YEH, /**< @stable ICU 70 */ + U_JG_VERTICAL_TAIL, /**< @stable ICU 70 */ + +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal UJoiningGroup value. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_JOINING_GROUP). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_JG_COUNT +#endif // U_HIDE_DEPRECATED_API +} UJoiningGroup; + +/** + * Grapheme Cluster Break constants. + * + * @see UCHAR_GRAPHEME_CLUSTER_BREAK + * @stable ICU 3.4 + */ +typedef enum UGraphemeClusterBreak { + /* + * Note: UGraphemeClusterBreak constants are parsed by preparseucd.py. + * It matches lines like + * U_GCB_ + */ + + U_GCB_OTHER = 0, /*[XX]*/ + U_GCB_CONTROL = 1, /*[CN]*/ + U_GCB_CR = 2, /*[CR]*/ + U_GCB_EXTEND = 3, /*[EX]*/ + U_GCB_L = 4, /*[L]*/ + U_GCB_LF = 5, /*[LF]*/ + U_GCB_LV = 6, /*[LV]*/ + U_GCB_LVT = 7, /*[LVT]*/ + U_GCB_T = 8, /*[T]*/ + U_GCB_V = 9, /*[V]*/ + /** @stable ICU 4.0 */ + U_GCB_SPACING_MARK = 10, /*[SM]*/ /* from here on: new in Unicode 5.1/ICU 4.0 */ + /** @stable ICU 4.0 */ + U_GCB_PREPEND = 11, /*[PP]*/ + /** @stable ICU 50 */ + U_GCB_REGIONAL_INDICATOR = 12, /*[RI]*/ /* new in Unicode 6.2/ICU 50 */ + /** @stable ICU 58 */ + U_GCB_E_BASE = 13, /*[EB]*/ /* from here on: new in Unicode 9.0/ICU 58 */ + /** @stable ICU 58 */ + U_GCB_E_BASE_GAZ = 14, /*[EBG]*/ + /** @stable ICU 58 */ + U_GCB_E_MODIFIER = 15, /*[EM]*/ + /** @stable ICU 58 */ + 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. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_GRAPHEME_CLUSTER_BREAK). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_GCB_COUNT = 18 +#endif // U_HIDE_DEPRECATED_API +} UGraphemeClusterBreak; + +/** + * Word Break constants. + * (UWordBreak is a pre-existing enum type in ubrk.h for word break status tags.) + * + * @see UCHAR_WORD_BREAK + * @stable ICU 3.4 + */ +typedef enum UWordBreakValues { + /* + * Note: UWordBreakValues constants are parsed by preparseucd.py. + * It matches lines like + * U_WB_ + */ + + U_WB_OTHER = 0, /*[XX]*/ + U_WB_ALETTER = 1, /*[LE]*/ + U_WB_FORMAT = 2, /*[FO]*/ + U_WB_KATAKANA = 3, /*[KA]*/ + U_WB_MIDLETTER = 4, /*[ML]*/ + U_WB_MIDNUM = 5, /*[MN]*/ + U_WB_NUMERIC = 6, /*[NU]*/ + U_WB_EXTENDNUMLET = 7, /*[EX]*/ + /** @stable ICU 4.0 */ + U_WB_CR = 8, /*[CR]*/ /* from here on: new in Unicode 5.1/ICU 4.0 */ + /** @stable ICU 4.0 */ + U_WB_EXTEND = 9, /*[Extend]*/ + /** @stable ICU 4.0 */ + U_WB_LF = 10, /*[LF]*/ + /** @stable ICU 4.0 */ + U_WB_MIDNUMLET =11, /*[MB]*/ + /** @stable ICU 4.0 */ + U_WB_NEWLINE =12, /*[NL]*/ + /** @stable ICU 50 */ + U_WB_REGIONAL_INDICATOR = 13, /*[RI]*/ /* new in Unicode 6.2/ICU 50 */ + /** @stable ICU 52 */ + U_WB_HEBREW_LETTER = 14, /*[HL]*/ /* from here on: new in Unicode 6.3/ICU 52 */ + /** @stable ICU 52 */ + U_WB_SINGLE_QUOTE = 15, /*[SQ]*/ + /** @stable ICU 52 */ + U_WB_DOUBLE_QUOTE = 16, /*[DQ]*/ + /** @stable ICU 58 */ + U_WB_E_BASE = 17, /*[EB]*/ /* from here on: new in Unicode 9.0/ICU 58 */ + /** @stable ICU 58 */ + U_WB_E_BASE_GAZ = 18, /*[EBG]*/ + /** @stable ICU 58 */ + U_WB_E_MODIFIER = 19, /*[EM]*/ + /** @stable ICU 58 */ + 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. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_WORD_BREAK). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_WB_COUNT = 23 +#endif // U_HIDE_DEPRECATED_API +} UWordBreakValues; + +/** + * Sentence Break constants. + * + * @see UCHAR_SENTENCE_BREAK + * @stable ICU 3.4 + */ +typedef enum USentenceBreak { + /* + * Note: USentenceBreak constants are parsed by preparseucd.py. + * It matches lines like + * U_SB_ + */ + + U_SB_OTHER = 0, /*[XX]*/ + U_SB_ATERM = 1, /*[AT]*/ + U_SB_CLOSE = 2, /*[CL]*/ + U_SB_FORMAT = 3, /*[FO]*/ + U_SB_LOWER = 4, /*[LO]*/ + U_SB_NUMERIC = 5, /*[NU]*/ + U_SB_OLETTER = 6, /*[LE]*/ + U_SB_SEP = 7, /*[SE]*/ + U_SB_SP = 8, /*[SP]*/ + U_SB_STERM = 9, /*[ST]*/ + U_SB_UPPER = 10, /*[UP]*/ + U_SB_CR = 11, /*[CR]*/ /* from here on: new in Unicode 5.1/ICU 4.0 */ + U_SB_EXTEND = 12, /*[EX]*/ + U_SB_LF = 13, /*[LF]*/ + U_SB_SCONTINUE = 14, /*[SC]*/ +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal USentenceBreak value. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_SENTENCE_BREAK). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_SB_COUNT = 15 +#endif // U_HIDE_DEPRECATED_API +} USentenceBreak; + +/** + * Line Break constants. + * + * @see UCHAR_LINE_BREAK + * @stable ICU 2.2 + */ +typedef enum ULineBreak { + /* + * Note: ULineBreak constants are parsed by preparseucd.py. + * It matches lines like + * U_LB_ + */ + + U_LB_UNKNOWN = 0, /*[XX]*/ + U_LB_AMBIGUOUS = 1, /*[AI]*/ + U_LB_ALPHABETIC = 2, /*[AL]*/ + U_LB_BREAK_BOTH = 3, /*[B2]*/ + U_LB_BREAK_AFTER = 4, /*[BA]*/ + U_LB_BREAK_BEFORE = 5, /*[BB]*/ + U_LB_MANDATORY_BREAK = 6, /*[BK]*/ + U_LB_CONTINGENT_BREAK = 7, /*[CB]*/ + U_LB_CLOSE_PUNCTUATION = 8, /*[CL]*/ + U_LB_COMBINING_MARK = 9, /*[CM]*/ + U_LB_CARRIAGE_RETURN = 10, /*[CR]*/ + U_LB_EXCLAMATION = 11, /*[EX]*/ + U_LB_GLUE = 12, /*[GL]*/ + U_LB_HYPHEN = 13, /*[HY]*/ + U_LB_IDEOGRAPHIC = 14, /*[ID]*/ + /** Renamed from the misspelled "inseperable" in Unicode 4.0.1/ICU 3.0 @stable ICU 3.0 */ + U_LB_INSEPARABLE = 15, /*[IN]*/ + U_LB_INSEPERABLE = U_LB_INSEPARABLE, + U_LB_INFIX_NUMERIC = 16, /*[IS]*/ + U_LB_LINE_FEED = 17, /*[LF]*/ + U_LB_NONSTARTER = 18, /*[NS]*/ + U_LB_NUMERIC = 19, /*[NU]*/ + U_LB_OPEN_PUNCTUATION = 20, /*[OP]*/ + U_LB_POSTFIX_NUMERIC = 21, /*[PO]*/ + U_LB_PREFIX_NUMERIC = 22, /*[PR]*/ + U_LB_QUOTATION = 23, /*[QU]*/ + U_LB_COMPLEX_CONTEXT = 24, /*[SA]*/ + U_LB_SURROGATE = 25, /*[SG]*/ + U_LB_SPACE = 26, /*[SP]*/ + U_LB_BREAK_SYMBOLS = 27, /*[SY]*/ + U_LB_ZWSPACE = 28, /*[ZW]*/ + /** @stable ICU 2.6 */ + U_LB_NEXT_LINE = 29, /*[NL]*/ /* from here on: new in Unicode 4/ICU 2.6 */ + /** @stable ICU 2.6 */ + U_LB_WORD_JOINER = 30, /*[WJ]*/ + /** @stable ICU 3.4 */ + U_LB_H2 = 31, /*[H2]*/ /* from here on: new in Unicode 4.1/ICU 3.4 */ + /** @stable ICU 3.4 */ + U_LB_H3 = 32, /*[H3]*/ + /** @stable ICU 3.4 */ + U_LB_JL = 33, /*[JL]*/ + /** @stable ICU 3.4 */ + U_LB_JT = 34, /*[JT]*/ + /** @stable ICU 3.4 */ + U_LB_JV = 35, /*[JV]*/ + /** @stable ICU 4.4 */ + U_LB_CLOSE_PARENTHESIS = 36, /*[CP]*/ /* new in Unicode 5.2/ICU 4.4 */ + /** @stable ICU 49 */ + U_LB_CONDITIONAL_JAPANESE_STARTER = 37,/*[CJ]*/ /* new in Unicode 6.1/ICU 49 */ + /** @stable ICU 49 */ + U_LB_HEBREW_LETTER = 38, /*[HL]*/ /* new in Unicode 6.1/ICU 49 */ + /** @stable ICU 50 */ + U_LB_REGIONAL_INDICATOR = 39,/*[RI]*/ /* new in Unicode 6.2/ICU 50 */ + /** @stable ICU 58 */ + U_LB_E_BASE = 40, /*[EB]*/ /* from here on: new in Unicode 9.0/ICU 58 */ + /** @stable ICU 58 */ + U_LB_E_MODIFIER = 41, /*[EM]*/ + /** @stable ICU 58 */ + U_LB_ZWJ = 42, /*[ZWJ]*/ +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal ULineBreak value. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_LINE_BREAK). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_LB_COUNT = 43 +#endif // U_HIDE_DEPRECATED_API +} ULineBreak; + +/** + * Numeric Type constants. + * + * @see UCHAR_NUMERIC_TYPE + * @stable ICU 2.2 + */ +typedef enum UNumericType { + /* + * Note: UNumericType constants are parsed by preparseucd.py. + * It matches lines like + * U_NT_ + */ + + U_NT_NONE, /*[None]*/ + U_NT_DECIMAL, /*[de]*/ + U_NT_DIGIT, /*[di]*/ + U_NT_NUMERIC, /*[nu]*/ +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal UNumericType value. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_NUMERIC_TYPE). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_NT_COUNT +#endif // U_HIDE_DEPRECATED_API +} UNumericType; + +/** + * Hangul Syllable Type constants. + * + * @see UCHAR_HANGUL_SYLLABLE_TYPE + * @stable ICU 2.6 + */ +typedef enum UHangulSyllableType { + /* + * Note: UHangulSyllableType constants are parsed by preparseucd.py. + * It matches lines like + * U_HST_ + */ + + U_HST_NOT_APPLICABLE, /*[NA]*/ + U_HST_LEADING_JAMO, /*[L]*/ + U_HST_VOWEL_JAMO, /*[V]*/ + U_HST_TRAILING_JAMO, /*[T]*/ + U_HST_LV_SYLLABLE, /*[LV]*/ + U_HST_LVT_SYLLABLE, /*[LVT]*/ +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal UHangulSyllableType value. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_HANGUL_SYLLABLE_TYPE). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_HST_COUNT +#endif // U_HIDE_DEPRECATED_API +} UHangulSyllableType; + +/** + * Indic Positional Category constants. + * + * @see UCHAR_INDIC_POSITIONAL_CATEGORY + * @stable ICU 63 + */ +typedef enum UIndicPositionalCategory { + /* + * Note: UIndicPositionalCategory constants are parsed by preparseucd.py. + * It matches lines like + * U_INPC_ + */ + + /** @stable ICU 63 */ + U_INPC_NA, + /** @stable ICU 63 */ + U_INPC_BOTTOM, + /** @stable ICU 63 */ + U_INPC_BOTTOM_AND_LEFT, + /** @stable ICU 63 */ + U_INPC_BOTTOM_AND_RIGHT, + /** @stable ICU 63 */ + U_INPC_LEFT, + /** @stable ICU 63 */ + U_INPC_LEFT_AND_RIGHT, + /** @stable ICU 63 */ + U_INPC_OVERSTRUCK, + /** @stable ICU 63 */ + U_INPC_RIGHT, + /** @stable ICU 63 */ + U_INPC_TOP, + /** @stable ICU 63 */ + U_INPC_TOP_AND_BOTTOM, + /** @stable ICU 63 */ + U_INPC_TOP_AND_BOTTOM_AND_RIGHT, + /** @stable ICU 63 */ + U_INPC_TOP_AND_LEFT, + /** @stable ICU 63 */ + U_INPC_TOP_AND_LEFT_AND_RIGHT, + /** @stable ICU 63 */ + U_INPC_TOP_AND_RIGHT, + /** @stable ICU 63 */ + U_INPC_VISUAL_ORDER_LEFT, + /** @stable ICU 66 */ + U_INPC_TOP_AND_BOTTOM_AND_LEFT, +} UIndicPositionalCategory; + +/** + * Indic Syllabic Category constants. + * + * @see UCHAR_INDIC_SYLLABIC_CATEGORY + * @stable ICU 63 + */ +typedef enum UIndicSyllabicCategory { + /* + * Note: UIndicSyllabicCategory constants are parsed by preparseucd.py. + * It matches lines like + * U_INSC_ + */ + + /** @stable ICU 63 */ + U_INSC_OTHER, + /** @stable ICU 63 */ + U_INSC_AVAGRAHA, + /** @stable ICU 63 */ + U_INSC_BINDU, + /** @stable ICU 63 */ + U_INSC_BRAHMI_JOINING_NUMBER, + /** @stable ICU 63 */ + U_INSC_CANTILLATION_MARK, + /** @stable ICU 63 */ + U_INSC_CONSONANT, + /** @stable ICU 63 */ + U_INSC_CONSONANT_DEAD, + /** @stable ICU 63 */ + U_INSC_CONSONANT_FINAL, + /** @stable ICU 63 */ + U_INSC_CONSONANT_HEAD_LETTER, + /** @stable ICU 63 */ + U_INSC_CONSONANT_INITIAL_POSTFIXED, + /** @stable ICU 63 */ + U_INSC_CONSONANT_KILLER, + /** @stable ICU 63 */ + U_INSC_CONSONANT_MEDIAL, + /** @stable ICU 63 */ + U_INSC_CONSONANT_PLACEHOLDER, + /** @stable ICU 63 */ + U_INSC_CONSONANT_PRECEDING_REPHA, + /** @stable ICU 63 */ + U_INSC_CONSONANT_PREFIXED, + /** @stable ICU 63 */ + U_INSC_CONSONANT_SUBJOINED, + /** @stable ICU 63 */ + U_INSC_CONSONANT_SUCCEEDING_REPHA, + /** @stable ICU 63 */ + U_INSC_CONSONANT_WITH_STACKER, + /** @stable ICU 63 */ + U_INSC_GEMINATION_MARK, + /** @stable ICU 63 */ + U_INSC_INVISIBLE_STACKER, + /** @stable ICU 63 */ + U_INSC_JOINER, + /** @stable ICU 63 */ + U_INSC_MODIFYING_LETTER, + /** @stable ICU 63 */ + U_INSC_NON_JOINER, + /** @stable ICU 63 */ + U_INSC_NUKTA, + /** @stable ICU 63 */ + U_INSC_NUMBER, + /** @stable ICU 63 */ + U_INSC_NUMBER_JOINER, + /** @stable ICU 63 */ + U_INSC_PURE_KILLER, + /** @stable ICU 63 */ + U_INSC_REGISTER_SHIFTER, + /** @stable ICU 63 */ + U_INSC_SYLLABLE_MODIFIER, + /** @stable ICU 63 */ + U_INSC_TONE_LETTER, + /** @stable ICU 63 */ + U_INSC_TONE_MARK, + /** @stable ICU 63 */ + U_INSC_VIRAMA, + /** @stable ICU 63 */ + U_INSC_VISARGA, + /** @stable ICU 63 */ + U_INSC_VOWEL, + /** @stable ICU 63 */ + U_INSC_VOWEL_DEPENDENT, + /** @stable ICU 63 */ + U_INSC_VOWEL_INDEPENDENT, +} UIndicSyllabicCategory; + +/** + * Vertical Orientation constants. + * + * @see UCHAR_VERTICAL_ORIENTATION + * @stable ICU 63 + */ +typedef enum UVerticalOrientation { + /* + * Note: UVerticalOrientation constants are parsed by preparseucd.py. + * It matches lines like + * U_VO_ + */ + + /** @stable ICU 63 */ + U_VO_ROTATED, + /** @stable ICU 63 */ + U_VO_TRANSFORMED_ROTATED, + /** @stable ICU 63 */ + U_VO_TRANSFORMED_UPRIGHT, + /** @stable ICU 63 */ + U_VO_UPRIGHT, +} UVerticalOrientation; + +/** + * Check a binary Unicode property for a code point. + * + * Unicode, especially in version 3.2, defines many more properties than the + * original set in UnicodeData.txt. + * + * The properties APIs are intended to reflect Unicode properties as defined + * in the Unicode Character Database (UCD) and Unicode Technical Reports (UTR). + * For details about the properties see http://www.unicode.org/ucd/ . + * For names of Unicode properties see the UCD file PropertyAliases.txt. + * + * Important: If ICU is built with UCD files from Unicode versions below 3.2, + * then properties marked with "new in Unicode 3.2" are not or not fully available. + * + * @param c Code point to test. + * @param which UProperty selector constant, identifies which binary property to check. + * Must be UCHAR_BINARY_START<=which<UCHAR_BINARY_LIMIT. + * @return true or false according to the binary Unicode property value for c. + * Also false if 'which' is out of bounds or if the Unicode version + * does not have data for the property at all. + * + * @see UProperty + * @see u_getBinaryPropertySet + * @see u_getIntPropertyValue + * @see u_getUnicodeVersion + * @stable ICU 2.1 + */ +U_CAPI UBool U_EXPORT2 +u_hasBinaryProperty(UChar32 c, UProperty which); + +/** + * Returns true if the property is true for the string. + * Same as u_hasBinaryProperty(single code point, which) + * if the string contains exactly one code point. + * + * Most properties apply only to single code points. + * UTS #51 Unicode Emoji + * defines several properties of strings. + * + * @param s String to test. + * @param length Length of the string, or negative if NUL-terminated. + * @param which UProperty selector constant, identifies which binary property to check. + * Must be UCHAR_BINARY_START<=which<UCHAR_BINARY_LIMIT. + * @return true or false according to the binary Unicode property value for the string. + * Also false if 'which' is out of bounds or if the Unicode version + * does not have data for the property at all. + * + * @see UProperty + * @see u_hasBinaryProperty + * @see u_getBinaryPropertySet + * @see u_getIntPropertyValue + * @see u_getUnicodeVersion + * @stable ICU 70 + */ +U_CAPI UBool U_EXPORT2 +u_stringHasBinaryProperty(const UChar *s, int32_t length, UProperty which); + +/** + * Returns a frozen USet for a binary property. + * The library retains ownership over the returned object. + * Sets an error code if the property number is not one for a binary property. + * + * The returned set contains all code points for which the property is true. + * + * @param property UCHAR_BINARY_START..UCHAR_BINARY_LIMIT-1 + * @param pErrorCode an in/out ICU UErrorCode + * @return the property as a set + * @see UProperty + * @see u_hasBinaryProperty + * @see Unicode::fromUSet + * @stable ICU 63 + */ +U_CAPI const USet * U_EXPORT2 +u_getBinaryPropertySet(UProperty property, UErrorCode *pErrorCode); + +/** + * Check if a code point has the Alphabetic Unicode property. + * Same as u_hasBinaryProperty(c, UCHAR_ALPHABETIC). + * This is different from u_isalpha! + * @param c Code point to test + * @return true if the code point has the Alphabetic Unicode property, false otherwise + * + * @see UCHAR_ALPHABETIC + * @see u_isalpha + * @see u_hasBinaryProperty + * @stable ICU 2.1 + */ +U_CAPI UBool U_EXPORT2 +u_isUAlphabetic(UChar32 c); + +/** + * Check if a code point has the Lowercase Unicode property. + * Same as u_hasBinaryProperty(c, UCHAR_LOWERCASE). + * This is different from u_islower! + * @param c Code point to test + * @return true if the code point has the Lowercase Unicode property, false otherwise + * + * @see UCHAR_LOWERCASE + * @see u_islower + * @see u_hasBinaryProperty + * @stable ICU 2.1 + */ +U_CAPI UBool U_EXPORT2 +u_isULowercase(UChar32 c); + +/** + * Check if a code point has the Uppercase Unicode property. + * Same as u_hasBinaryProperty(c, UCHAR_UPPERCASE). + * This is different from u_isupper! + * @param c Code point to test + * @return true if the code point has the Uppercase Unicode property, false otherwise + * + * @see UCHAR_UPPERCASE + * @see u_isupper + * @see u_hasBinaryProperty + * @stable ICU 2.1 + */ +U_CAPI UBool U_EXPORT2 +u_isUUppercase(UChar32 c); + +/** + * Check if a code point has the White_Space Unicode property. + * Same as u_hasBinaryProperty(c, UCHAR_WHITE_SPACE). + * This is different from both u_isspace and u_isWhitespace! + * + * Note: There are several ICU whitespace functions; please see the uchar.h + * file documentation for a detailed comparison. + * + * @param c Code point to test + * @return true if the code point has the White_Space Unicode property, false otherwise. + * + * @see UCHAR_WHITE_SPACE + * @see u_isWhitespace + * @see u_isspace + * @see u_isJavaSpaceChar + * @see u_hasBinaryProperty + * @stable ICU 2.1 + */ +U_CAPI UBool U_EXPORT2 +u_isUWhiteSpace(UChar32 c); + +/** + * Get the property value for an enumerated or integer Unicode property for a code point. + * Also returns binary and mask property values. + * + * Unicode, especially in version 3.2, defines many more properties than the + * original set in UnicodeData.txt. + * + * The properties APIs are intended to reflect Unicode properties as defined + * in the Unicode Character Database (UCD) and Unicode Technical Reports (UTR). + * For details about the properties see http://www.unicode.org/ . + * For names of Unicode properties see the UCD file PropertyAliases.txt. + * + * Sample usage: + * UEastAsianWidth ea=(UEastAsianWidth)u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH); + * UBool b=(UBool)u_getIntPropertyValue(c, UCHAR_IDEOGRAPHIC); + * + * @param c Code point to test. + * @param which UProperty selector constant, identifies which property to check. + * Must be UCHAR_BINARY_START<=which=0. + * True for characters with general category "Nd" (decimal digit numbers) + * as well as Latin letters a-f and A-F in both ASCII and Fullwidth ASCII. + * (That is, for letters with code points + * 0041..0046, 0061..0066, FF21..FF26, FF41..FF46.) + * + * In order to narrow the definition of hexadecimal digits to only ASCII + * characters, use (c<=0x7f && u_isxdigit(c)). + * + * This is a C/POSIX migration function. + * See the comments about C/POSIX character classification functions in the + * documentation at the top of this header file. + * + * @param c the code point to be tested + * @return true if the code point is a hexadecimal digit + * + * @stable ICU 2.6 + */ +U_CAPI UBool U_EXPORT2 +u_isxdigit(UChar32 c); + +/** + * Determines whether the specified code point is a punctuation character. + * True for characters with general categories "P" (punctuation). + * + * This is a C/POSIX migration function. + * See the comments about C/POSIX character classification functions in the + * documentation at the top of this header file. + * + * @param c the code point to be tested + * @return true if the code point is a punctuation character + * + * @stable ICU 2.6 + */ +U_CAPI UBool U_EXPORT2 +u_ispunct(UChar32 c); + +/** + * Determines whether the specified code point is a "graphic" character + * (printable, excluding spaces). + * true for all characters except those with general categories + * "Cc" (control codes), "Cf" (format controls), "Cs" (surrogates), + * "Cn" (unassigned), and "Z" (separators). + * + * This is a C/POSIX migration function. + * See the comments about C/POSIX character classification functions in the + * documentation at the top of this header file. + * + * @param c the code point to be tested + * @return true if the code point is a "graphic" character + * + * @stable ICU 2.6 + */ +U_CAPI UBool U_EXPORT2 +u_isgraph(UChar32 c); + +/** + * Determines whether the specified code point is a "blank" or "horizontal space", + * a character that visibly separates words on a line. + * The following are equivalent definitions: + * + * true for Unicode White_Space characters except for "vertical space controls" + * where "vertical space controls" are the following characters: + * U+000A (LF) U+000B (VT) U+000C (FF) U+000D (CR) U+0085 (NEL) U+2028 (LS) U+2029 (PS) + * + * same as + * + * true for U+0009 (TAB) and characters with general category "Zs" (space separators). + * + * Note: There are several ICU whitespace functions; please see the uchar.h + * file documentation for a detailed comparison. + * + * This is a C/POSIX migration function. + * See the comments about C/POSIX character classification functions in the + * documentation at the top of this header file. + * + * @param c the code point to be tested + * @return true if the code point is a "blank" + * + * @stable ICU 2.6 + */ +U_CAPI UBool U_EXPORT2 +u_isblank(UChar32 c); + +/** + * Determines whether the specified code point is "defined", + * which usually means that it is assigned a character. + * True for general categories other than "Cn" (other, not assigned), + * i.e., true for all code points mentioned in UnicodeData.txt. + * + * Note that non-character code points (e.g., U+FDD0) are not "defined" + * (they are Cn), but surrogate code points are "defined" (Cs). + * + * Same as java.lang.Character.isDefined(). + * + * @param c the code point to be tested + * @return true if the code point is assigned a character + * + * @see u_isdigit + * @see u_isalpha + * @see u_isalnum + * @see u_isupper + * @see u_islower + * @see u_istitle + * @stable ICU 2.0 + */ +U_CAPI UBool U_EXPORT2 +u_isdefined(UChar32 c); + +/** + * Determines if the specified character is a space character or not. + * + * Note: There are several ICU whitespace functions; please see the uchar.h + * file documentation for a detailed comparison. + * + * This is a C/POSIX migration function. + * See the comments about C/POSIX character classification functions in the + * documentation at the top of this header file. + * + * @param c the character to be tested + * @return true if the character is a space character; false otherwise. + * + * @see u_isJavaSpaceChar + * @see u_isWhitespace + * @see u_isUWhiteSpace + * @stable ICU 2.0 + */ +U_CAPI UBool U_EXPORT2 +u_isspace(UChar32 c); + +/** + * Determine if the specified code point is a space character according to Java. + * True for characters with general categories "Z" (separators), + * which does not include control codes (e.g., TAB or Line Feed). + * + * Same as java.lang.Character.isSpaceChar(). + * + * Note: There are several ICU whitespace functions; please see the uchar.h + * file documentation for a detailed comparison. + * + * @param c the code point to be tested + * @return true if the code point is a space character according to Character.isSpaceChar() + * + * @see u_isspace + * @see u_isWhitespace + * @see u_isUWhiteSpace + * @stable ICU 2.6 + */ +U_CAPI UBool U_EXPORT2 +u_isJavaSpaceChar(UChar32 c); + +/** + * Determines if the specified code point is a whitespace character according to Java/ICU. + * A character is considered to be a Java whitespace character if and only + * if it satisfies one of the following criteria: + * + * - It is a Unicode Separator character (categories "Z" = "Zs" or "Zl" or "Zp"), but is not + * also a non-breaking space (U+00A0 NBSP or U+2007 Figure Space or U+202F Narrow NBSP). + * - It is U+0009 HORIZONTAL TABULATION. + * - It is U+000A LINE FEED. + * - It is U+000B VERTICAL TABULATION. + * - It is U+000C FORM FEED. + * - It is U+000D CARRIAGE RETURN. + * - It is U+001C FILE SEPARATOR. + * - It is U+001D GROUP SEPARATOR. + * - It is U+001E RECORD SEPARATOR. + * - It is U+001F UNIT SEPARATOR. + * + * This API tries to sync with the semantics of Java's + * java.lang.Character.isWhitespace(), but it may not return + * the exact same results because of the Unicode version + * difference. + * + * Note: Unicode 4.0.1 changed U+200B ZERO WIDTH SPACE from a Space Separator (Zs) + * to a Format Control (Cf). Since then, isWhitespace(0x200b) returns false. + * See http://www.unicode.org/versions/Unicode4.0.1/ + * + * Note: There are several ICU whitespace functions; please see the uchar.h + * file documentation for a detailed comparison. + * + * @param c the code point to be tested + * @return true if the code point is a whitespace character according to Java/ICU + * + * @see u_isspace + * @see u_isJavaSpaceChar + * @see u_isUWhiteSpace + * @stable ICU 2.0 + */ +U_CAPI UBool U_EXPORT2 +u_isWhitespace(UChar32 c); + +/** + * Determines whether the specified code point is a control character + * (as defined by this function). + * A control character is one of the following: + * - ISO 8-bit control character (U+0000..U+001f and U+007f..U+009f) + * - U_CONTROL_CHAR (Cc) + * - U_FORMAT_CHAR (Cf) + * - U_LINE_SEPARATOR (Zl) + * - U_PARAGRAPH_SEPARATOR (Zp) + * + * This is a C/POSIX migration function. + * See the comments about C/POSIX character classification functions in the + * documentation at the top of this header file. + * + * @param c the code point to be tested + * @return true if the code point is a control character + * + * @see UCHAR_DEFAULT_IGNORABLE_CODE_POINT + * @see u_isprint + * @stable ICU 2.0 + */ +U_CAPI UBool U_EXPORT2 +u_iscntrl(UChar32 c); + +/** + * Determines whether the specified code point is an ISO control code. + * True for U+0000..U+001f and U+007f..U+009f (general category "Cc"). + * + * Same as java.lang.Character.isISOControl(). + * + * @param c the code point to be tested + * @return true if the code point is an ISO control code + * + * @see u_iscntrl + * @stable ICU 2.6 + */ +U_CAPI UBool U_EXPORT2 +u_isISOControl(UChar32 c); + +/** + * Determines whether the specified code point is a printable character. + * True for general categories other than "C" (controls). + * + * This is a C/POSIX migration function. + * See the comments about C/POSIX character classification functions in the + * documentation at the top of this header file. + * + * @param c the code point to be tested + * @return true if the code point is a printable character + * + * @see UCHAR_DEFAULT_IGNORABLE_CODE_POINT + * @see u_iscntrl + * @stable ICU 2.0 + */ +U_CAPI UBool U_EXPORT2 +u_isprint(UChar32 c); + +/** + * Non-standard: Determines whether the specified code point is a base character. + * True for general categories "L" (letters), "N" (numbers), + * "Mc" (spacing combining marks), and "Me" (enclosing marks). + * + * Note that this is different from the Unicode Standard definition in + * chapter 3.6, conformance clause D51 “Base character”, + * which defines base characters as the code points with general categories + * Letter (L), Number (N), Punctuation (P), Symbol (S), or Space Separator (Zs). + * + * @param c the code point to be tested + * @return true if the code point is a base character according to this function + * + * @see u_isalpha + * @see u_isdigit + * @stable ICU 2.0 + */ +U_CAPI UBool U_EXPORT2 +u_isbase(UChar32 c); + +/** + * Returns the bidirectional category value for the code point, + * which is used in the Unicode bidirectional algorithm + * (UAX #9 http://www.unicode.org/reports/tr9/). + * Note that some unassigned code points have bidi values + * of R or AL because they are in blocks that are reserved + * for Right-To-Left scripts. + * + * Same as java.lang.Character.getDirectionality() + * + * @param c the code point to be tested + * @return the bidirectional category (UCharDirection) value + * + * @see UCharDirection + * @stable ICU 2.0 + */ +U_CAPI UCharDirection U_EXPORT2 +u_charDirection(UChar32 c); + +/** + * Determines whether the code point has the Bidi_Mirrored property. + * This property is set for characters that are commonly used in + * Right-To-Left contexts and need to be displayed with a "mirrored" + * glyph. + * + * Same as java.lang.Character.isMirrored(). + * Same as UCHAR_BIDI_MIRRORED + * + * @param c the code point to be tested + * @return true if the character has the Bidi_Mirrored property + * + * @see UCHAR_BIDI_MIRRORED + * @stable ICU 2.0 + */ +U_CAPI UBool U_EXPORT2 +u_isMirrored(UChar32 c); + +/** + * Maps the specified character to a "mirror-image" character. + * For characters with the Bidi_Mirrored property, implementations + * sometimes need a "poor man's" mapping to another Unicode + * character (code point) such that the default glyph may serve + * as the mirror-image of the default glyph of the specified + * character. This is useful for text conversion to and from + * codepages with visual order, and for displays without glyph + * selection capabilities. + * + * @param c the code point to be mapped + * @return another Unicode code point that may serve as a mirror-image + * substitute, or c itself if there is no such mapping or c + * does not have the Bidi_Mirrored property + * + * @see UCHAR_BIDI_MIRRORED + * @see u_isMirrored + * @stable ICU 2.0 + */ +U_CAPI UChar32 U_EXPORT2 +u_charMirror(UChar32 c); + +/** + * Maps the specified character to its paired bracket character. + * For Bidi_Paired_Bracket_Type!=None, this is the same as u_charMirror(). + * Otherwise c itself is returned. + * See http://www.unicode.org/reports/tr9/ + * + * @param c the code point to be mapped + * @return the paired bracket code point, + * or c itself if there is no such mapping + * (Bidi_Paired_Bracket_Type=None) + * + * @see UCHAR_BIDI_PAIRED_BRACKET + * @see UCHAR_BIDI_PAIRED_BRACKET_TYPE + * @see u_charMirror + * @stable ICU 52 + */ +U_CAPI UChar32 U_EXPORT2 +u_getBidiPairedBracket(UChar32 c); + +/** + * Returns the general category value for the code point. + * + * Same as java.lang.Character.getType(). + * + * @param c the code point to be tested + * @return the general category (UCharCategory) value + * + * @see UCharCategory + * @stable ICU 2.0 + */ +U_CAPI int8_t U_EXPORT2 +u_charType(UChar32 c); + +/** + * Get a single-bit bit set for the general category of a character. + * This bit set can be compared bitwise with U_GC_SM_MASK, U_GC_L_MASK, etc. + * Same as U_MASK(u_charType(c)). + * + * @param c the code point to be tested + * @return a single-bit mask corresponding to the general category (UCharCategory) value + * + * @see u_charType + * @see UCharCategory + * @see U_GC_CN_MASK + * @stable ICU 2.1 + */ +#define U_GET_GC_MASK(c) U_MASK(u_charType(c)) + +/** + * Callback from u_enumCharTypes(), is called for each contiguous range + * of code points c (where start<=cnameChoice, the character name written + * into the buffer is the "modern" name or the name that was defined + * in Unicode version 1.0. + * The name contains only "invariant" characters + * like A-Z, 0-9, space, and '-'. + * Unicode 1.0 names are only retrieved if they are different from the modern + * names and if the data file contains the data for them. gennames may or may + * not be called with a command line option to include 1.0 names in unames.dat. + * + * @param code The character (code point) for which to get the name. + * It must be 0<=code<=0x10ffff. + * @param nameChoice Selector for which name to get. + * @param buffer Destination address for copying the name. + * The name will always be zero-terminated. + * If there is no name, then the buffer will be set to the empty string. + * @param bufferLength ==sizeof(buffer) + * @param pErrorCode Pointer to a UErrorCode variable; + * check for U_SUCCESS() after u_charName() + * returns. + * @return The length of the name, or 0 if there is no name for this character. + * If the bufferLength is less than or equal to the length, then the buffer + * contains the truncated name and the returned length indicates the full + * length of the name. + * The length does not include the zero-termination. + * + * @see UCharNameChoice + * @see u_charFromName + * @see u_enumCharNames + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_charName(UChar32 code, UCharNameChoice nameChoice, + char *buffer, int32_t bufferLength, + UErrorCode *pErrorCode); + +#ifndef U_HIDE_DEPRECATED_API +/** + * Returns an empty string. + * Used to return the ISO 10646 comment for a character. + * The Unicode ISO_Comment property is deprecated and has no values. + * + * @param c The character (code point) for which to get the ISO comment. + * It must be 0<=c<=0x10ffff. + * @param dest Destination address for copying the comment. + * The comment will be zero-terminated if possible. + * If there is no comment, then the buffer will be set to the empty string. + * @param destCapacity ==sizeof(dest) + * @param pErrorCode Pointer to a UErrorCode variable; + * check for U_SUCCESS() after u_getISOComment() + * returns. + * @return 0 + * + * @deprecated ICU 49 + */ +U_DEPRECATED int32_t U_EXPORT2 +u_getISOComment(UChar32 c, + char *dest, int32_t destCapacity, + UErrorCode *pErrorCode); +#endif /* U_HIDE_DEPRECATED_API */ + +/** + * Find a Unicode character by its name and return its code point value. + * The name is matched exactly and completely. + * If the name does not correspond to a code point, pErrorCode + * is set to U_INVALID_CHAR_FOUND. + * A Unicode 1.0 name is matched only if it differs from the modern name. + * Unicode names are all uppercase. Extended names are lowercase followed + * by an uppercase hexadecimal number, and within angle brackets. + * + * @param nameChoice Selector for which name to match. + * @param name The name to match. + * @param pErrorCode Pointer to a UErrorCode variable + * @return The Unicode value of the code point with the given name, + * or an undefined value if there is no such code point. + * + * @see UCharNameChoice + * @see u_charName + * @see u_enumCharNames + * @stable ICU 1.7 + */ +U_CAPI UChar32 U_EXPORT2 +u_charFromName(UCharNameChoice nameChoice, + const char *name, + UErrorCode *pErrorCode); + +/** + * Type of a callback function for u_enumCharNames() that gets called + * for each Unicode character with the code point value and + * the character name. + * If such a function returns false, then the enumeration is stopped. + * + * @param context The context pointer that was passed to u_enumCharNames(). + * @param code The Unicode code point for the character with this name. + * @param nameChoice Selector for which kind of names is enumerated. + * @param name The character's name, zero-terminated. + * @param length The length of the name. + * @return true if the enumeration should continue, false to stop it. + * + * @see UCharNameChoice + * @see u_enumCharNames + * @stable ICU 1.7 + */ +typedef UBool U_CALLCONV UEnumCharNamesFn(void *context, + UChar32 code, + UCharNameChoice nameChoice, + const char *name, + int32_t length); + +/** + * Enumerate all assigned Unicode characters between the start and limit + * code points (start inclusive, limit exclusive) and call a function + * for each, passing the code point value and the character name. + * For Unicode 1.0 names, only those are enumerated that differ from the + * modern names. + * + * @param start The first code point in the enumeration range. + * @param limit One more than the last code point in the enumeration range + * (the first one after the range). + * @param fn The function that is to be called for each character name. + * @param context An arbitrary pointer that is passed to the function. + * @param nameChoice Selector for which kind of names to enumerate. + * @param pErrorCode Pointer to a UErrorCode variable + * + * @see UCharNameChoice + * @see UEnumCharNamesFn + * @see u_charName + * @see u_charFromName + * @stable ICU 1.7 + */ +U_CAPI void U_EXPORT2 +u_enumCharNames(UChar32 start, UChar32 limit, + UEnumCharNamesFn *fn, + void *context, + UCharNameChoice nameChoice, + UErrorCode *pErrorCode); + +/** + * Return the Unicode name for a given property, as given in the + * Unicode database file PropertyAliases.txt. + * + * In addition, this function maps the property + * UCHAR_GENERAL_CATEGORY_MASK to the synthetic names "gcm" / + * "General_Category_Mask". These names are not in + * PropertyAliases.txt. + * + * @param property UProperty selector other than UCHAR_INVALID_CODE. + * If out of range, NULL is returned. + * + * @param nameChoice selector for which name to get. If out of range, + * NULL is returned. All properties have a long name. Most + * have a short name, but some do not. Unicode allows for + * additional names; if present these will be returned by + * U_LONG_PROPERTY_NAME + i, where i=1, 2,... + * + * @return a pointer to the name, or NULL if either the + * property or the nameChoice is out of range. If a given + * nameChoice returns NULL, then all larger values of + * nameChoice will return NULL, with one exception: if NULL is + * returned for U_SHORT_PROPERTY_NAME, then + * U_LONG_PROPERTY_NAME (and higher) may still return a + * non-NULL value. The returned pointer is valid until + * u_cleanup() is called. + * + * @see UProperty + * @see UPropertyNameChoice + * @stable ICU 2.4 + */ +U_CAPI const char* U_EXPORT2 +u_getPropertyName(UProperty property, + UPropertyNameChoice nameChoice); + +/** + * Return the UProperty enum for a given property name, as specified + * in the Unicode database file PropertyAliases.txt. Short, long, and + * any other variants are recognized. + * + * In addition, this function maps the synthetic names "gcm" / + * "General_Category_Mask" to the property + * UCHAR_GENERAL_CATEGORY_MASK. These names are not in + * PropertyAliases.txt. + * + * @param alias the property name to be matched. The name is compared + * using "loose matching" as described in PropertyAliases.txt. + * + * @return a UProperty enum, or UCHAR_INVALID_CODE if the given name + * does not match any property. + * + * @see UProperty + * @stable ICU 2.4 + */ +U_CAPI UProperty U_EXPORT2 +u_getPropertyEnum(const char* alias); + +/** + * Return the Unicode name for a given property value, as given in the + * Unicode database file PropertyValueAliases.txt. + * + * Note: Some of the names in PropertyValueAliases.txt can only be + * retrieved using UCHAR_GENERAL_CATEGORY_MASK, not + * UCHAR_GENERAL_CATEGORY. These include: "C" / "Other", "L" / + * "Letter", "LC" / "Cased_Letter", "M" / "Mark", "N" / "Number", "P" + * / "Punctuation", "S" / "Symbol", and "Z" / "Separator". + * + * @param property UProperty selector constant. + * Must be UCHAR_BINARY_START<=which2<=radix<=36 or if the + * value of c is not a valid digit in the specified + * radix, -1 is returned. A character is a valid digit + * if at least one of the following is true: + *

    + *
  • The character has a decimal digit value. + * Such characters have the general category "Nd" (decimal digit numbers) + * and a Numeric_Type of Decimal. + * In this case the value is the character's decimal digit value.
  • + *
  • The character is one of the uppercase Latin letters + * 'A' through 'Z'. + * In this case the value is c-'A'+10.
  • + *
  • The character is one of the lowercase Latin letters + * 'a' through 'z'. + * In this case the value is ch-'a'+10.
  • + *
  • Latin letters from both the ASCII range (0061..007A, 0041..005A) + * as well as from the Fullwidth ASCII range (FF41..FF5A, FF21..FF3A) + * are recognized.
  • + *
+ * + * Same as java.lang.Character.digit(). + * + * @param ch the code point to be tested. + * @param radix the radix. + * @return the numeric value represented by the character in the + * specified radix, + * or -1 if there is no value or if the value exceeds the radix. + * + * @see UCHAR_NUMERIC_TYPE + * @see u_forDigit + * @see u_charDigitValue + * @see u_isdigit + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_digit(UChar32 ch, int8_t radix); + +/** + * Determines the character representation for a specific digit in + * the specified radix. If the value of radix is not a + * valid radix, or the value of digit is not a valid + * digit in the specified radix, the null character + * (U+0000) is returned. + *

+ * The radix argument is valid if it is greater than or + * equal to 2 and less than or equal to 36. + * The digit argument is valid if + * 0 <= digit < radix. + *

+ * If the digit is less than 10, then + * '0' + digit is returned. Otherwise, the value + * 'a' + digit - 10 is returned. + * + * Same as java.lang.Character.forDigit(). + * + * @param digit the number to convert to a character. + * @param radix the radix. + * @return the char representation of the specified digit + * in the specified radix. + * + * @see u_digit + * @see u_charDigitValue + * @see u_isdigit + * @stable ICU 2.0 + */ +U_CAPI UChar32 U_EXPORT2 +u_forDigit(int32_t digit, int8_t radix); + +/** + * Get the "age" of the code point. + * The "age" is the Unicode version when the code point was first + * designated (as a non-character or for Private Use) + * or assigned a character. + * This can be useful to avoid emitting code points to receiving + * processes that do not accept newer characters. + * The data is from the UCD file DerivedAge.txt. + * + * @param c The code point. + * @param versionArray The Unicode version number array, to be filled in. + * + * @stable ICU 2.1 + */ +U_CAPI void U_EXPORT2 +u_charAge(UChar32 c, UVersionInfo versionArray); + +/** + * Gets the Unicode version information. + * The version array is filled in with the version information + * for the Unicode standard that is currently used by ICU. + * For example, Unicode version 3.1.1 is represented as an array with + * the values { 3, 1, 1, 0 }. + * + * @param versionArray an output array that will be filled in with + * the Unicode version number + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +u_getUnicodeVersion(UVersionInfo versionArray); + +#if !UCONFIG_NO_NORMALIZATION +/** + * Get the FC_NFKC_Closure property string for a character. + * See Unicode Standard Annex #15 for details, search for "FC_NFKC_Closure" + * or for "FNC": http://www.unicode.org/reports/tr15/ + * + * @param c The character (code point) for which to get the FC_NFKC_Closure string. + * It must be 0<=c<=0x10ffff. + * @param dest Destination address for copying the string. + * The string will be zero-terminated if possible. + * If there is no FC_NFKC_Closure string, + * then the buffer will be set to the empty string. + * @param destCapacity ==sizeof(dest) + * @param pErrorCode Pointer to a UErrorCode variable. + * @return The length of the string, or 0 if there is no FC_NFKC_Closure string for this character. + * If the destCapacity is less than or equal to the length, then the buffer + * contains the truncated name and the returned length indicates the full + * length of the name. + * The length does not include the zero-termination. + * + * @stable ICU 2.2 + */ +U_CAPI int32_t U_EXPORT2 +u_getFC_NFKC_Closure(UChar32 c, UChar *dest, int32_t destCapacity, UErrorCode *pErrorCode); + +#endif + + +U_CDECL_END + +#endif /*_UCHAR*/ +/*eof*/ diff --git a/deps/icu-small/source/common/unicode/ucharstrie.h b/deps/icu-small/source/common/unicode/ucharstrie.h index 064244a74c1691..8978a85cc03077 100644 --- a/deps/icu-small/source/common/unicode/ucharstrie.h +++ b/deps/icu-small/source/common/unicode/ucharstrie.h @@ -1,623 +1,623 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2012, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: ucharstrie.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010nov14 -* created by: Markus W. Scherer -*/ - -#ifndef __UCHARSTRIE_H__ -#define __UCHARSTRIE_H__ - -/** - * \file - * \brief C++ API: Trie for mapping Unicode strings (or 16-bit-unit sequences) - * to integer values. - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/unistr.h" -#include "unicode/uobject.h" -#include "unicode/ustringtrie.h" - -U_NAMESPACE_BEGIN - -class Appendable; -class UCharsTrieBuilder; -class UVector32; - -/** - * Light-weight, non-const reader class for a UCharsTrie. - * Traverses a char16_t-serialized data structure with minimal state, - * for mapping strings (16-bit-unit sequences) to non-negative integer values. - * - * This class owns the serialized trie data only if it was constructed by - * the builder's build() method. - * The public constructor and the copy constructor only alias the data (only copy the pointer). - * There is no assignment operator. - * - * This class is not intended for public subclassing. - * @stable ICU 4.8 - */ -class U_COMMON_API UCharsTrie : public UMemory { -public: - /** - * Constructs a UCharsTrie reader instance. - * - * The trieUChars must contain a copy of a char16_t sequence from the UCharsTrieBuilder, - * starting with the first char16_t of that sequence. - * The UCharsTrie object will not read more char16_ts than - * the UCharsTrieBuilder generated in the corresponding build() call. - * - * The array is not copied/cloned and must not be modified while - * the UCharsTrie object is in use. - * - * @param trieUChars The char16_t array that contains the serialized trie. - * @stable ICU 4.8 - */ - UCharsTrie(ConstChar16Ptr trieUChars) - : ownedArray_(NULL), uchars_(trieUChars), - pos_(uchars_), remainingMatchLength_(-1) {} - - /** - * Destructor. - * @stable ICU 4.8 - */ - ~UCharsTrie(); - - /** - * Copy constructor, copies the other trie reader object and its state, - * but not the char16_t array which will be shared. (Shallow copy.) - * @param other Another UCharsTrie object. - * @stable ICU 4.8 - */ - UCharsTrie(const UCharsTrie &other) - : ownedArray_(NULL), uchars_(other.uchars_), - pos_(other.pos_), remainingMatchLength_(other.remainingMatchLength_) {} - - /** - * Resets this trie to its initial state. - * @return *this - * @stable ICU 4.8 - */ - UCharsTrie &reset() { - pos_=uchars_; - remainingMatchLength_=-1; - return *this; - } - - /** - * Returns the state of this trie as a 64-bit integer. - * The state value is never 0. - * - * @return opaque state value - * @see resetToState64 - * @stable ICU 65 - */ - uint64_t getState64() const { - return (static_cast(remainingMatchLength_ + 2) << kState64RemainingShift) | - (uint64_t)(pos_ - uchars_); - } - - /** - * Resets this trie to the saved state. - * Unlike resetToState(State), the 64-bit state value - * must be from getState64() from the same trie object or - * from one initialized the exact same way. - * Because of no validation, this method is faster. - * - * @param state The opaque trie state value from getState64(). - * @return *this - * @see getState64 - * @see resetToState - * @see reset - * @stable ICU 65 - */ - UCharsTrie &resetToState64(uint64_t state) { - remainingMatchLength_ = static_cast(state >> kState64RemainingShift) - 2; - pos_ = uchars_ + (state & kState64PosMask); - return *this; - } - - /** - * UCharsTrie state object, for saving a trie's current state - * and resetting the trie back to this state later. - * @stable ICU 4.8 - */ - class State : public UMemory { - public: - /** - * Constructs an empty State. - * @stable ICU 4.8 - */ - State() { uchars=NULL; } - private: - friend class UCharsTrie; - - const char16_t *uchars; - const char16_t *pos; - int32_t remainingMatchLength; - }; - - /** - * Saves the state of this trie. - * @param state The State object to hold the trie's state. - * @return *this - * @see resetToState - * @stable ICU 4.8 - */ - const UCharsTrie &saveState(State &state) const { - state.uchars=uchars_; - state.pos=pos_; - state.remainingMatchLength=remainingMatchLength_; - return *this; - } - - /** - * Resets this trie to the saved state. - * If the state object contains no state, or the state of a different trie, - * then this trie remains unchanged. - * @param state The State object which holds a saved trie state. - * @return *this - * @see saveState - * @see reset - * @stable ICU 4.8 - */ - UCharsTrie &resetToState(const State &state) { - if(uchars_==state.uchars && uchars_!=NULL) { - pos_=state.pos; - remainingMatchLength_=state.remainingMatchLength; - } - return *this; - } - - /** - * Determines whether the string so far matches, whether it has a value, - * and whether another input char16_t can continue a matching string. - * @return The match/value Result. - * @stable ICU 4.8 - */ - UStringTrieResult current() const; - - /** - * Traverses the trie from the initial state for this input char16_t. - * Equivalent to reset().next(uchar). - * @param uchar Input char value. Values below 0 and above 0xffff will never match. - * @return The match/value Result. - * @stable ICU 4.8 - */ - inline UStringTrieResult first(int32_t uchar) { - remainingMatchLength_=-1; - return nextImpl(uchars_, uchar); - } - - /** - * Traverses the trie from the initial state for the - * one or two UTF-16 code units for this input code point. - * Equivalent to reset().nextForCodePoint(cp). - * @param cp A Unicode code point 0..0x10ffff. - * @return The match/value Result. - * @stable ICU 4.8 - */ - UStringTrieResult firstForCodePoint(UChar32 cp); - - /** - * Traverses the trie from the current state for this input char16_t. - * @param uchar Input char value. Values below 0 and above 0xffff will never match. - * @return The match/value Result. - * @stable ICU 4.8 - */ - UStringTrieResult next(int32_t uchar); - - /** - * Traverses the trie from the current state for the - * one or two UTF-16 code units for this input code point. - * @param cp A Unicode code point 0..0x10ffff. - * @return The match/value Result. - * @stable ICU 4.8 - */ - UStringTrieResult nextForCodePoint(UChar32 cp); - - /** - * Traverses the trie from the current state for this string. - * Equivalent to - * \code - * Result result=current(); - * for(each c in s) - * if(!USTRINGTRIE_HAS_NEXT(result)) return USTRINGTRIE_NO_MATCH; - * result=next(c); - * return result; - * \endcode - * @param s A string. Can be NULL if length is 0. - * @param length The length of the string. Can be -1 if NUL-terminated. - * @return The match/value Result. - * @stable ICU 4.8 - */ - UStringTrieResult next(ConstChar16Ptr s, int32_t length); - - /** - * Returns a matching string's value if called immediately after - * current()/first()/next() returned USTRINGTRIE_INTERMEDIATE_VALUE or USTRINGTRIE_FINAL_VALUE. - * getValue() can be called multiple times. - * - * Do not call getValue() after USTRINGTRIE_NO_MATCH or USTRINGTRIE_NO_VALUE! - * @return The value for the string so far. - * @stable ICU 4.8 - */ - inline int32_t getValue() const { - const char16_t *pos=pos_; - int32_t leadUnit=*pos++; - // U_ASSERT(leadUnit>=kMinValueLead); - return leadUnit&kValueIsFinal ? - readValue(pos, leadUnit&0x7fff) : readNodeValue(pos, leadUnit); - } - - /** - * Determines whether all strings reachable from the current state - * map to the same value. - * @param uniqueValue Receives the unique value, if this function returns true. - * (output-only) - * @return true if all strings reachable from the current state - * map to the same value. - * @stable ICU 4.8 - */ - inline UBool hasUniqueValue(int32_t &uniqueValue) const { - const char16_t *pos=pos_; - // Skip the rest of a pending linear-match node. - return pos!=NULL && findUniqueValue(pos+remainingMatchLength_+1, false, uniqueValue); - } - - /** - * Finds each char16_t which continues the string from the current state. - * That is, each char16_t c for which it would be next(c)!=USTRINGTRIE_NO_MATCH now. - * @param out Each next char16_t is appended to this object. - * @return the number of char16_ts which continue the string from here - * @stable ICU 4.8 - */ - int32_t getNextUChars(Appendable &out) const; - - /** - * Iterator for all of the (string, value) pairs in a UCharsTrie. - * @stable ICU 4.8 - */ - class U_COMMON_API Iterator : public UMemory { - public: - /** - * Iterates from the root of a char16_t-serialized UCharsTrie. - * @param trieUChars The trie char16_ts. - * @param maxStringLength If 0, the iterator returns full strings. - * Otherwise, the iterator returns strings with this maximum length. - * @param errorCode Standard 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.) - * @stable ICU 4.8 - */ - Iterator(ConstChar16Ptr trieUChars, int32_t maxStringLength, UErrorCode &errorCode); - - /** - * Iterates from the current state of the specified UCharsTrie. - * @param trie The trie whose state will be copied for iteration. - * @param maxStringLength If 0, the iterator returns full strings. - * Otherwise, the iterator returns strings with this maximum length. - * @param errorCode Standard 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.) - * @stable ICU 4.8 - */ - Iterator(const UCharsTrie &trie, int32_t maxStringLength, UErrorCode &errorCode); - - /** - * Destructor. - * @stable ICU 4.8 - */ - ~Iterator(); - - /** - * Resets this iterator to its initial state. - * @return *this - * @stable ICU 4.8 - */ - Iterator &reset(); - - /** - * @return true if there are more elements. - * @stable ICU 4.8 - */ - UBool hasNext() const; - - /** - * Finds the next (string, value) pair if there is one. - * - * If the string is truncated to the maximum length and does not - * have a real value, then the value is set to -1. - * In this case, this "not a real value" is indistinguishable from - * a real value of -1. - * @param errorCode Standard 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.) - * @return true if there is another element. - * @stable ICU 4.8 - */ - UBool next(UErrorCode &errorCode); - - /** - * @return The string for the last successful next(). - * @stable ICU 4.8 - */ - const UnicodeString &getString() const { return str_; } - /** - * @return The value for the last successful next(). - * @stable ICU 4.8 - */ - int32_t getValue() const { return value_; } - - private: - UBool truncateAndStop() { - pos_=NULL; - value_=-1; // no real value for str - return true; - } - - const char16_t *branchNext(const char16_t *pos, int32_t length, UErrorCode &errorCode); - - const char16_t *uchars_; - const char16_t *pos_; - const char16_t *initialPos_; - int32_t remainingMatchLength_; - int32_t initialRemainingMatchLength_; - UBool skipValue_; // Skip intermediate value which was already delivered. - - UnicodeString str_; - int32_t maxLength_; - int32_t value_; - - // The stack stores pairs of integers for backtracking to another - // outbound edge of a branch node. - // The first integer is an offset from uchars_. - // The second integer has the str_.length() from before the node in bits 15..0, - // and the remaining branch length in bits 31..16. - // (We could store the remaining branch length minus 1 in bits 30..16 and not use the sign bit, - // but the code looks more confusing that way.) - UVector32 *stack_; - }; - -private: - friend class UCharsTrieBuilder; - - /** - * Constructs a UCharsTrie reader instance. - * Unlike the public constructor which just aliases an array, - * this constructor adopts the builder's array. - * This constructor is only called by the builder. - */ - UCharsTrie(char16_t *adoptUChars, const char16_t *trieUChars) - : ownedArray_(adoptUChars), uchars_(trieUChars), - pos_(uchars_), remainingMatchLength_(-1) {} - - // No assignment operator. - UCharsTrie &operator=(const UCharsTrie &other) = delete; - - inline void stop() { - pos_=NULL; - } - - // Reads a compact 32-bit integer. - // pos is already after the leadUnit, and the lead unit has bit 15 reset. - static inline int32_t readValue(const char16_t *pos, int32_t leadUnit) { - int32_t value; - if(leadUnit=kMinTwoUnitValueLead) { - if(leadUnit>6)-1; - } else if(leadUnit=kMinTwoUnitNodeValueLead) { - if(leadUnit=kMinTwoUnitDeltaLead) { - if(delta==kThreeUnitDeltaLead) { - delta=(pos[0]<<16)|pos[1]; - pos+=2; - } else { - delta=((delta-kMinTwoUnitDeltaLead)<<16)|*pos++; - } - } - return pos+delta; - } - - static const char16_t *skipDelta(const char16_t *pos) { - int32_t delta=*pos++; - if(delta>=kMinTwoUnitDeltaLead) { - if(delta==kThreeUnitDeltaLead) { - pos+=2; - } else { - ++pos; - } - } - return pos; - } - - static inline UStringTrieResult valueResult(int32_t node) { - return (UStringTrieResult)(USTRINGTRIE_INTERMEDIATE_VALUE-(node>>15)); - } - - // Handles a branch node for both next(uchar) and next(string). - UStringTrieResult branchNext(const char16_t *pos, int32_t length, int32_t uchar); - - // Requires remainingLength_<0. - UStringTrieResult nextImpl(const char16_t *pos, int32_t uchar); - - // Helper functions for hasUniqueValue(). - // Recursively finds a unique value (or whether there is not a unique one) - // from a branch. - static const char16_t *findUniqueValueFromBranch(const char16_t *pos, int32_t length, - UBool haveUniqueValue, int32_t &uniqueValue); - // Recursively finds a unique value (or whether there is not a unique one) - // starting from a position on a node lead unit. - static UBool findUniqueValue(const char16_t *pos, UBool haveUniqueValue, int32_t &uniqueValue); - - // Helper functions for getNextUChars(). - // getNextUChars() when pos is on a branch node. - static void getNextBranchUChars(const char16_t *pos, int32_t length, Appendable &out); - - // UCharsTrie data structure - // - // The trie consists of a series of char16_t-serialized nodes for incremental - // Unicode string/char16_t sequence matching. (char16_t=16-bit unsigned integer) - // The root node is at the beginning of the trie data. - // - // Types of nodes are distinguished by their node lead unit ranges. - // After each node, except a final-value node, another node follows to - // encode match values or continue matching further units. - // - // Node types: - // - Final-value node: Stores a 32-bit integer in a compact, variable-length format. - // The value is for the string/char16_t sequence so far. - // - Match node, optionally with an intermediate value in a different compact format. - // The value, if present, is for the string/char16_t sequence so far. - // - // Aside from the value, which uses the node lead unit's high bits: - // - // - Linear-match node: Matches a number of units. - // - Branch node: Branches to other nodes according to the current input unit. - // The node unit is the length of the branch (number of units to select from) - // minus 1. It is followed by a sub-node: - // - If the length is at most kMaxBranchLinearSubNodeLength, then - // there are length-1 (key, value) pairs and then one more comparison unit. - // If one of the key units matches, then the value is either a final value for - // the string so far, or a "jump" delta to the next node. - // If the last unit matches, then matching continues with the next node. - // (Values have the same encoding as final-value nodes.) - // - If the length is greater than kMaxBranchLinearSubNodeLength, then - // there is one unit and one "jump" delta. - // If the input unit is less than the sub-node unit, then "jump" by delta to - // the next sub-node which will have a length of length/2. - // (The delta has its own compact encoding.) - // Otherwise, skip the "jump" delta to the next sub-node - // which will have a length of length-length/2. - - // Match-node lead unit values, after masking off intermediate-value bits: - - // 0000..002f: Branch node. If node!=0 then the length is node+1, otherwise - // the length is one more than the next unit. - - // For a branch sub-node with at most this many entries, we drop down - // to a linear search. - static const int32_t kMaxBranchLinearSubNodeLength=5; - - // 0030..003f: Linear-match node, match 1..16 units and continue reading the next node. - static const int32_t kMinLinearMatch=0x30; - static const int32_t kMaxLinearMatchLength=0x10; - - // Match-node lead unit bits 14..6 for the optional intermediate value. - // If these bits are 0, then there is no intermediate value. - // Otherwise, see the *NodeValue* constants below. - static const int32_t kMinValueLead=kMinLinearMatch+kMaxLinearMatchLength; // 0x0040 - static const int32_t kNodeTypeMask=kMinValueLead-1; // 0x003f - - // A final-value node has bit 15 set. - static const int32_t kValueIsFinal=0x8000; - - // Compact value: After testing and masking off bit 15, use the following thresholds. - static const int32_t kMaxOneUnitValue=0x3fff; - - static const int32_t kMinTwoUnitValueLead=kMaxOneUnitValue+1; // 0x4000 - static const int32_t kThreeUnitValueLead=0x7fff; - - static const int32_t kMaxTwoUnitValue=((kThreeUnitValueLead-kMinTwoUnitValueLead)<<16)-1; // 0x3ffeffff - - // Compact intermediate-value integer, lead unit shared with a branch or linear-match node. - static const int32_t kMaxOneUnitNodeValue=0xff; - static const int32_t kMinTwoUnitNodeValueLead=kMinValueLead+((kMaxOneUnitNodeValue+1)<<6); // 0x4040 - static const int32_t kThreeUnitNodeValueLead=0x7fc0; - - static const int32_t kMaxTwoUnitNodeValue= - ((kThreeUnitNodeValueLead-kMinTwoUnitNodeValueLead)<<10)-1; // 0xfdffff - - // Compact delta integers. - static const int32_t kMaxOneUnitDelta=0xfbff; - static const int32_t kMinTwoUnitDeltaLead=kMaxOneUnitDelta+1; // 0xfc00 - static const int32_t kThreeUnitDeltaLead=0xffff; - - static const int32_t kMaxTwoUnitDelta=((kThreeUnitDeltaLead-kMinTwoUnitDeltaLead)<<16)-1; // 0x03feffff - - // For getState64(): - // The remainingMatchLength_ is -1..14=(kMaxLinearMatchLength=0x10)-2 - // so we need at least 5 bits for that. - // We add 2 to store it as a positive value 1..16=kMaxLinearMatchLength. - static constexpr int32_t kState64RemainingShift = 59; - static constexpr uint64_t kState64PosMask = (UINT64_C(1) << kState64RemainingShift) - 1; - - char16_t *ownedArray_; - - // Fixed value referencing the UCharsTrie words. - const char16_t *uchars_; - - // Iterator variables. - - // Pointer to next trie unit to read. NULL if no more matches. - const char16_t *pos_; - // Remaining length of a linear-match node, minus 1. Negative if not in such a node. - int32_t remainingMatchLength_; -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __UCHARSTRIE_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2012, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: ucharstrie.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010nov14 +* created by: Markus W. Scherer +*/ + +#ifndef __UCHARSTRIE_H__ +#define __UCHARSTRIE_H__ + +/** + * \file + * \brief C++ API: Trie for mapping Unicode strings (or 16-bit-unit sequences) + * to integer values. + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/unistr.h" +#include "unicode/uobject.h" +#include "unicode/ustringtrie.h" + +U_NAMESPACE_BEGIN + +class Appendable; +class UCharsTrieBuilder; +class UVector32; + +/** + * Light-weight, non-const reader class for a UCharsTrie. + * Traverses a char16_t-serialized data structure with minimal state, + * for mapping strings (16-bit-unit sequences) to non-negative integer values. + * + * This class owns the serialized trie data only if it was constructed by + * the builder's build() method. + * The public constructor and the copy constructor only alias the data (only copy the pointer). + * There is no assignment operator. + * + * This class is not intended for public subclassing. + * @stable ICU 4.8 + */ +class U_COMMON_API UCharsTrie : public UMemory { +public: + /** + * Constructs a UCharsTrie reader instance. + * + * The trieUChars must contain a copy of a char16_t sequence from the UCharsTrieBuilder, + * starting with the first char16_t of that sequence. + * The UCharsTrie object will not read more char16_ts than + * the UCharsTrieBuilder generated in the corresponding build() call. + * + * The array is not copied/cloned and must not be modified while + * the UCharsTrie object is in use. + * + * @param trieUChars The char16_t array that contains the serialized trie. + * @stable ICU 4.8 + */ + UCharsTrie(ConstChar16Ptr trieUChars) + : ownedArray_(nullptr), uchars_(trieUChars), + pos_(uchars_), remainingMatchLength_(-1) {} + + /** + * Destructor. + * @stable ICU 4.8 + */ + ~UCharsTrie(); + + /** + * Copy constructor, copies the other trie reader object and its state, + * but not the char16_t array which will be shared. (Shallow copy.) + * @param other Another UCharsTrie object. + * @stable ICU 4.8 + */ + UCharsTrie(const UCharsTrie &other) + : ownedArray_(nullptr), uchars_(other.uchars_), + pos_(other.pos_), remainingMatchLength_(other.remainingMatchLength_) {} + + /** + * Resets this trie to its initial state. + * @return *this + * @stable ICU 4.8 + */ + UCharsTrie &reset() { + pos_=uchars_; + remainingMatchLength_=-1; + return *this; + } + + /** + * Returns the state of this trie as a 64-bit integer. + * The state value is never 0. + * + * @return opaque state value + * @see resetToState64 + * @stable ICU 65 + */ + uint64_t getState64() const { + return (static_cast(remainingMatchLength_ + 2) << kState64RemainingShift) | + (uint64_t)(pos_ - uchars_); + } + + /** + * Resets this trie to the saved state. + * Unlike resetToState(State), the 64-bit state value + * must be from getState64() from the same trie object or + * from one initialized the exact same way. + * Because of no validation, this method is faster. + * + * @param state The opaque trie state value from getState64(). + * @return *this + * @see getState64 + * @see resetToState + * @see reset + * @stable ICU 65 + */ + UCharsTrie &resetToState64(uint64_t state) { + remainingMatchLength_ = static_cast(state >> kState64RemainingShift) - 2; + pos_ = uchars_ + (state & kState64PosMask); + return *this; + } + + /** + * UCharsTrie state object, for saving a trie's current state + * and resetting the trie back to this state later. + * @stable ICU 4.8 + */ + class State : public UMemory { + public: + /** + * Constructs an empty State. + * @stable ICU 4.8 + */ + State() { uchars=nullptr; } + private: + friend class UCharsTrie; + + const char16_t *uchars; + const char16_t *pos; + int32_t remainingMatchLength; + }; + + /** + * Saves the state of this trie. + * @param state The State object to hold the trie's state. + * @return *this + * @see resetToState + * @stable ICU 4.8 + */ + const UCharsTrie &saveState(State &state) const { + state.uchars=uchars_; + state.pos=pos_; + state.remainingMatchLength=remainingMatchLength_; + return *this; + } + + /** + * Resets this trie to the saved state. + * If the state object contains no state, or the state of a different trie, + * then this trie remains unchanged. + * @param state The State object which holds a saved trie state. + * @return *this + * @see saveState + * @see reset + * @stable ICU 4.8 + */ + UCharsTrie &resetToState(const State &state) { + if(uchars_==state.uchars && uchars_!=nullptr) { + pos_=state.pos; + remainingMatchLength_=state.remainingMatchLength; + } + return *this; + } + + /** + * Determines whether the string so far matches, whether it has a value, + * and whether another input char16_t can continue a matching string. + * @return The match/value Result. + * @stable ICU 4.8 + */ + UStringTrieResult current() const; + + /** + * Traverses the trie from the initial state for this input char16_t. + * Equivalent to reset().next(uchar). + * @param uchar Input char value. Values below 0 and above 0xffff will never match. + * @return The match/value Result. + * @stable ICU 4.8 + */ + inline UStringTrieResult first(int32_t uchar) { + remainingMatchLength_=-1; + return nextImpl(uchars_, uchar); + } + + /** + * Traverses the trie from the initial state for the + * one or two UTF-16 code units for this input code point. + * Equivalent to reset().nextForCodePoint(cp). + * @param cp A Unicode code point 0..0x10ffff. + * @return The match/value Result. + * @stable ICU 4.8 + */ + UStringTrieResult firstForCodePoint(UChar32 cp); + + /** + * Traverses the trie from the current state for this input char16_t. + * @param uchar Input char value. Values below 0 and above 0xffff will never match. + * @return The match/value Result. + * @stable ICU 4.8 + */ + UStringTrieResult next(int32_t uchar); + + /** + * Traverses the trie from the current state for the + * one or two UTF-16 code units for this input code point. + * @param cp A Unicode code point 0..0x10ffff. + * @return The match/value Result. + * @stable ICU 4.8 + */ + UStringTrieResult nextForCodePoint(UChar32 cp); + + /** + * Traverses the trie from the current state for this string. + * Equivalent to + * \code + * Result result=current(); + * for(each c in s) + * if(!USTRINGTRIE_HAS_NEXT(result)) return USTRINGTRIE_NO_MATCH; + * result=next(c); + * return result; + * \endcode + * @param s A string. Can be nullptr if length is 0. + * @param length The length of the string. Can be -1 if NUL-terminated. + * @return The match/value Result. + * @stable ICU 4.8 + */ + UStringTrieResult next(ConstChar16Ptr s, int32_t length); + + /** + * Returns a matching string's value if called immediately after + * current()/first()/next() returned USTRINGTRIE_INTERMEDIATE_VALUE or USTRINGTRIE_FINAL_VALUE. + * getValue() can be called multiple times. + * + * Do not call getValue() after USTRINGTRIE_NO_MATCH or USTRINGTRIE_NO_VALUE! + * @return The value for the string so far. + * @stable ICU 4.8 + */ + inline int32_t getValue() const { + const char16_t *pos=pos_; + int32_t leadUnit=*pos++; + // U_ASSERT(leadUnit>=kMinValueLead); + return leadUnit&kValueIsFinal ? + readValue(pos, leadUnit&0x7fff) : readNodeValue(pos, leadUnit); + } + + /** + * Determines whether all strings reachable from the current state + * map to the same value. + * @param uniqueValue Receives the unique value, if this function returns true. + * (output-only) + * @return true if all strings reachable from the current state + * map to the same value. + * @stable ICU 4.8 + */ + inline UBool hasUniqueValue(int32_t &uniqueValue) const { + const char16_t *pos=pos_; + // Skip the rest of a pending linear-match node. + return pos!=nullptr && findUniqueValue(pos+remainingMatchLength_+1, false, uniqueValue); + } + + /** + * Finds each char16_t which continues the string from the current state. + * That is, each char16_t c for which it would be next(c)!=USTRINGTRIE_NO_MATCH now. + * @param out Each next char16_t is appended to this object. + * @return the number of char16_ts which continue the string from here + * @stable ICU 4.8 + */ + int32_t getNextUChars(Appendable &out) const; + + /** + * Iterator for all of the (string, value) pairs in a UCharsTrie. + * @stable ICU 4.8 + */ + class U_COMMON_API Iterator : public UMemory { + public: + /** + * Iterates from the root of a char16_t-serialized UCharsTrie. + * @param trieUChars The trie char16_ts. + * @param maxStringLength If 0, the iterator returns full strings. + * Otherwise, the iterator returns strings with this maximum length. + * @param errorCode Standard 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.) + * @stable ICU 4.8 + */ + Iterator(ConstChar16Ptr trieUChars, int32_t maxStringLength, UErrorCode &errorCode); + + /** + * Iterates from the current state of the specified UCharsTrie. + * @param trie The trie whose state will be copied for iteration. + * @param maxStringLength If 0, the iterator returns full strings. + * Otherwise, the iterator returns strings with this maximum length. + * @param errorCode Standard 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.) + * @stable ICU 4.8 + */ + Iterator(const UCharsTrie &trie, int32_t maxStringLength, UErrorCode &errorCode); + + /** + * Destructor. + * @stable ICU 4.8 + */ + ~Iterator(); + + /** + * Resets this iterator to its initial state. + * @return *this + * @stable ICU 4.8 + */ + Iterator &reset(); + + /** + * @return true if there are more elements. + * @stable ICU 4.8 + */ + UBool hasNext() const; + + /** + * Finds the next (string, value) pair if there is one. + * + * If the string is truncated to the maximum length and does not + * have a real value, then the value is set to -1. + * In this case, this "not a real value" is indistinguishable from + * a real value of -1. + * @param errorCode Standard 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.) + * @return true if there is another element. + * @stable ICU 4.8 + */ + UBool next(UErrorCode &errorCode); + + /** + * @return The string for the last successful next(). + * @stable ICU 4.8 + */ + const UnicodeString &getString() const { return str_; } + /** + * @return The value for the last successful next(). + * @stable ICU 4.8 + */ + int32_t getValue() const { return value_; } + + private: + UBool truncateAndStop() { + pos_=nullptr; + value_=-1; // no real value for str + return true; + } + + const char16_t *branchNext(const char16_t *pos, int32_t length, UErrorCode &errorCode); + + const char16_t *uchars_; + const char16_t *pos_; + const char16_t *initialPos_; + int32_t remainingMatchLength_; + int32_t initialRemainingMatchLength_; + UBool skipValue_; // Skip intermediate value which was already delivered. + + UnicodeString str_; + int32_t maxLength_; + int32_t value_; + + // The stack stores pairs of integers for backtracking to another + // outbound edge of a branch node. + // The first integer is an offset from uchars_. + // The second integer has the str_.length() from before the node in bits 15..0, + // and the remaining branch length in bits 31..16. + // (We could store the remaining branch length minus 1 in bits 30..16 and not use the sign bit, + // but the code looks more confusing that way.) + UVector32 *stack_; + }; + +private: + friend class UCharsTrieBuilder; + + /** + * Constructs a UCharsTrie reader instance. + * Unlike the public constructor which just aliases an array, + * this constructor adopts the builder's array. + * This constructor is only called by the builder. + */ + UCharsTrie(char16_t *adoptUChars, const char16_t *trieUChars) + : ownedArray_(adoptUChars), uchars_(trieUChars), + pos_(uchars_), remainingMatchLength_(-1) {} + + // No assignment operator. + UCharsTrie &operator=(const UCharsTrie &other) = delete; + + inline void stop() { + pos_=nullptr; + } + + // Reads a compact 32-bit integer. + // pos is already after the leadUnit, and the lead unit has bit 15 reset. + static inline int32_t readValue(const char16_t *pos, int32_t leadUnit) { + int32_t value; + if(leadUnit=kMinTwoUnitValueLead) { + if(leadUnit>6)-1; + } else if(leadUnit=kMinTwoUnitNodeValueLead) { + if(leadUnit=kMinTwoUnitDeltaLead) { + if(delta==kThreeUnitDeltaLead) { + delta=(pos[0]<<16)|pos[1]; + pos+=2; + } else { + delta=((delta-kMinTwoUnitDeltaLead)<<16)|*pos++; + } + } + return pos+delta; + } + + static const char16_t *skipDelta(const char16_t *pos) { + int32_t delta=*pos++; + if(delta>=kMinTwoUnitDeltaLead) { + if(delta==kThreeUnitDeltaLead) { + pos+=2; + } else { + ++pos; + } + } + return pos; + } + + static inline UStringTrieResult valueResult(int32_t node) { + return (UStringTrieResult)(USTRINGTRIE_INTERMEDIATE_VALUE-(node>>15)); + } + + // Handles a branch node for both next(uchar) and next(string). + UStringTrieResult branchNext(const char16_t *pos, int32_t length, int32_t uchar); + + // Requires remainingLength_<0. + UStringTrieResult nextImpl(const char16_t *pos, int32_t uchar); + + // Helper functions for hasUniqueValue(). + // Recursively finds a unique value (or whether there is not a unique one) + // from a branch. + static const char16_t *findUniqueValueFromBranch(const char16_t *pos, int32_t length, + UBool haveUniqueValue, int32_t &uniqueValue); + // Recursively finds a unique value (or whether there is not a unique one) + // starting from a position on a node lead unit. + static UBool findUniqueValue(const char16_t *pos, UBool haveUniqueValue, int32_t &uniqueValue); + + // Helper functions for getNextUChars(). + // getNextUChars() when pos is on a branch node. + static void getNextBranchUChars(const char16_t *pos, int32_t length, Appendable &out); + + // UCharsTrie data structure + // + // The trie consists of a series of char16_t-serialized nodes for incremental + // Unicode string/char16_t sequence matching. (char16_t=16-bit unsigned integer) + // The root node is at the beginning of the trie data. + // + // Types of nodes are distinguished by their node lead unit ranges. + // After each node, except a final-value node, another node follows to + // encode match values or continue matching further units. + // + // Node types: + // - Final-value node: Stores a 32-bit integer in a compact, variable-length format. + // The value is for the string/char16_t sequence so far. + // - Match node, optionally with an intermediate value in a different compact format. + // The value, if present, is for the string/char16_t sequence so far. + // + // Aside from the value, which uses the node lead unit's high bits: + // + // - Linear-match node: Matches a number of units. + // - Branch node: Branches to other nodes according to the current input unit. + // The node unit is the length of the branch (number of units to select from) + // minus 1. It is followed by a sub-node: + // - If the length is at most kMaxBranchLinearSubNodeLength, then + // there are length-1 (key, value) pairs and then one more comparison unit. + // If one of the key units matches, then the value is either a final value for + // the string so far, or a "jump" delta to the next node. + // If the last unit matches, then matching continues with the next node. + // (Values have the same encoding as final-value nodes.) + // - If the length is greater than kMaxBranchLinearSubNodeLength, then + // there is one unit and one "jump" delta. + // If the input unit is less than the sub-node unit, then "jump" by delta to + // the next sub-node which will have a length of length/2. + // (The delta has its own compact encoding.) + // Otherwise, skip the "jump" delta to the next sub-node + // which will have a length of length-length/2. + + // Match-node lead unit values, after masking off intermediate-value bits: + + // 0000..002f: Branch node. If node!=0 then the length is node+1, otherwise + // the length is one more than the next unit. + + // For a branch sub-node with at most this many entries, we drop down + // to a linear search. + static const int32_t kMaxBranchLinearSubNodeLength=5; + + // 0030..003f: Linear-match node, match 1..16 units and continue reading the next node. + static const int32_t kMinLinearMatch=0x30; + static const int32_t kMaxLinearMatchLength=0x10; + + // Match-node lead unit bits 14..6 for the optional intermediate value. + // If these bits are 0, then there is no intermediate value. + // Otherwise, see the *NodeValue* constants below. + static const int32_t kMinValueLead=kMinLinearMatch+kMaxLinearMatchLength; // 0x0040 + static const int32_t kNodeTypeMask=kMinValueLead-1; // 0x003f + + // A final-value node has bit 15 set. + static const int32_t kValueIsFinal=0x8000; + + // Compact value: After testing and masking off bit 15, use the following thresholds. + static const int32_t kMaxOneUnitValue=0x3fff; + + static const int32_t kMinTwoUnitValueLead=kMaxOneUnitValue+1; // 0x4000 + static const int32_t kThreeUnitValueLead=0x7fff; + + static const int32_t kMaxTwoUnitValue=((kThreeUnitValueLead-kMinTwoUnitValueLead)<<16)-1; // 0x3ffeffff + + // Compact intermediate-value integer, lead unit shared with a branch or linear-match node. + static const int32_t kMaxOneUnitNodeValue=0xff; + static const int32_t kMinTwoUnitNodeValueLead=kMinValueLead+((kMaxOneUnitNodeValue+1)<<6); // 0x4040 + static const int32_t kThreeUnitNodeValueLead=0x7fc0; + + static const int32_t kMaxTwoUnitNodeValue= + ((kThreeUnitNodeValueLead-kMinTwoUnitNodeValueLead)<<10)-1; // 0xfdffff + + // Compact delta integers. + static const int32_t kMaxOneUnitDelta=0xfbff; + static const int32_t kMinTwoUnitDeltaLead=kMaxOneUnitDelta+1; // 0xfc00 + static const int32_t kThreeUnitDeltaLead=0xffff; + + static const int32_t kMaxTwoUnitDelta=((kThreeUnitDeltaLead-kMinTwoUnitDeltaLead)<<16)-1; // 0x03feffff + + // For getState64(): + // The remainingMatchLength_ is -1..14=(kMaxLinearMatchLength=0x10)-2 + // so we need at least 5 bits for that. + // We add 2 to store it as a positive value 1..16=kMaxLinearMatchLength. + static constexpr int32_t kState64RemainingShift = 59; + static constexpr uint64_t kState64PosMask = (UINT64_C(1) << kState64RemainingShift) - 1; + + char16_t *ownedArray_; + + // Fixed value referencing the UCharsTrie words. + const char16_t *uchars_; + + // Iterator variables. + + // Pointer to next trie unit to read. nullptr if no more matches. + const char16_t *pos_; + // Remaining length of a linear-match node, minus 1. Negative if not in such a node. + int32_t remainingMatchLength_; +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __UCHARSTRIE_H__ diff --git a/deps/icu-small/source/common/unicode/ucharstriebuilder.h b/deps/icu-small/source/common/unicode/ucharstriebuilder.h index 5c8aa33ffb39f5..682c2ddebe2f96 100644 --- a/deps/icu-small/source/common/unicode/ucharstriebuilder.h +++ b/deps/icu-small/source/common/unicode/ucharstriebuilder.h @@ -1,193 +1,193 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2016, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: ucharstriebuilder.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010nov14 -* created by: Markus W. Scherer -*/ - -#ifndef __UCHARSTRIEBUILDER_H__ -#define __UCHARSTRIEBUILDER_H__ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/stringtriebuilder.h" -#include "unicode/ucharstrie.h" -#include "unicode/unistr.h" - -/** - * \file - * \brief C++ API: Builder for icu::UCharsTrie - */ - -U_NAMESPACE_BEGIN - -class UCharsTrieElement; - -/** - * Builder class for UCharsTrie. - * - * This class is not intended for public subclassing. - * @stable ICU 4.8 - */ -class U_COMMON_API UCharsTrieBuilder : public StringTrieBuilder { -public: - /** - * Constructs an empty builder. - * @param errorCode Standard ICU error code. - * @stable ICU 4.8 - */ - UCharsTrieBuilder(UErrorCode &errorCode); - - /** - * Destructor. - * @stable ICU 4.8 - */ - virtual ~UCharsTrieBuilder(); - - /** - * Adds a (string, value) pair. - * The string must be unique. - * The string contents will be copied; the builder does not keep - * a reference to the input UnicodeString or its buffer. - * @param s The input string. - * @param value The value associated with this string. - * @param errorCode Standard 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.) - * @return *this - * @stable ICU 4.8 - */ - UCharsTrieBuilder &add(const UnicodeString &s, int32_t value, UErrorCode &errorCode); - - /** - * Builds a UCharsTrie for the add()ed data. - * Once built, no further data can be add()ed until clear() is called. - * - * A UCharsTrie cannot be empty. At least one (string, value) pair - * must have been add()ed. - * - * This method passes ownership of the builder's internal result array to the new trie object. - * Another call to any build() variant will re-serialize the trie. - * After clear() has been called, a new array will be used as well. - * @param buildOption Build option, see UStringTrieBuildOption. - * @param errorCode Standard 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.) - * @return A new UCharsTrie for the add()ed data. - * @stable ICU 4.8 - */ - UCharsTrie *build(UStringTrieBuildOption buildOption, UErrorCode &errorCode); - - /** - * Builds a UCharsTrie for the add()ed data and char16_t-serializes it. - * Once built, no further data can be add()ed until clear() is called. - * - * A UCharsTrie cannot be empty. At least one (string, value) pair - * must have been add()ed. - * - * Multiple calls to buildUnicodeString() set the UnicodeStrings to the - * builder's same char16_t array, without rebuilding. - * If buildUnicodeString() is called after build(), the trie will be - * re-serialized into a new array (because build() passes on ownership). - * If build() is called after buildUnicodeString(), the trie object returned - * by build() will become the owner of the underlying data for the - * previously returned UnicodeString. - * After clear() has been called, a new array will be used as well. - * @param buildOption Build option, see UStringTrieBuildOption. - * @param result A UnicodeString which will be set to the char16_t-serialized - * UCharsTrie for the add()ed data. - * @param errorCode Standard 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.) - * @return result - * @stable ICU 4.8 - */ - UnicodeString &buildUnicodeString(UStringTrieBuildOption buildOption, UnicodeString &result, - UErrorCode &errorCode); - - /** - * Removes all (string, value) pairs. - * New data can then be add()ed and a new trie can be built. - * @return *this - * @stable ICU 4.8 - */ - UCharsTrieBuilder &clear() { - strings.remove(); - elementsLength=0; - ucharsLength=0; - return *this; - } - -private: - UCharsTrieBuilder(const UCharsTrieBuilder &other) = delete; // no copy constructor - UCharsTrieBuilder &operator=(const UCharsTrieBuilder &other) = delete; // no assignment operator - - void buildUChars(UStringTrieBuildOption buildOption, UErrorCode &errorCode); - - virtual int32_t getElementStringLength(int32_t i) const override; - virtual char16_t getElementUnit(int32_t i, int32_t unitIndex) const override; - virtual int32_t getElementValue(int32_t i) const override; - - virtual int32_t getLimitOfLinearMatch(int32_t first, int32_t last, int32_t unitIndex) const override; - - virtual int32_t countElementUnits(int32_t start, int32_t limit, int32_t unitIndex) const override; - virtual int32_t skipElementsBySomeUnits(int32_t i, int32_t unitIndex, int32_t count) const override; - virtual int32_t indexOfElementWithNextUnit(int32_t i, int32_t unitIndex, char16_t unit) const override; - - virtual UBool matchNodesCanHaveValues() const override { return true; } - - virtual int32_t getMaxBranchLinearSubNodeLength() const override { return UCharsTrie::kMaxBranchLinearSubNodeLength; } - virtual int32_t getMinLinearMatch() const override { return UCharsTrie::kMinLinearMatch; } - virtual int32_t getMaxLinearMatchLength() const override { return UCharsTrie::kMaxLinearMatchLength; } - - class UCTLinearMatchNode : public LinearMatchNode { - public: - UCTLinearMatchNode(const char16_t *units, int32_t len, Node *nextNode); - virtual bool operator==(const Node &other) const override; - virtual void write(StringTrieBuilder &builder) override; - private: - const char16_t *s; - }; - - virtual Node *createLinearMatchNode(int32_t i, int32_t unitIndex, int32_t length, - Node *nextNode) const override; - - UBool ensureCapacity(int32_t length); - virtual int32_t write(int32_t unit) override; - int32_t write(const char16_t *s, int32_t length); - virtual int32_t writeElementUnits(int32_t i, int32_t unitIndex, int32_t length) override; - virtual int32_t writeValueAndFinal(int32_t i, UBool isFinal) override; - virtual int32_t writeValueAndType(UBool hasValue, int32_t value, int32_t node) override; - virtual int32_t writeDeltaTo(int32_t jumpTarget) override; - - UnicodeString strings; - UCharsTrieElement *elements; - int32_t elementsCapacity; - int32_t elementsLength; - - // char16_t serialization of the trie. - // Grows from the back: ucharsLength measures from the end of the buffer! - char16_t *uchars; - int32_t ucharsCapacity; - int32_t ucharsLength; -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif // __UCHARSTRIEBUILDER_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2016, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: ucharstriebuilder.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010nov14 +* created by: Markus W. Scherer +*/ + +#ifndef __UCHARSTRIEBUILDER_H__ +#define __UCHARSTRIEBUILDER_H__ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/stringtriebuilder.h" +#include "unicode/ucharstrie.h" +#include "unicode/unistr.h" + +/** + * \file + * \brief C++ API: Builder for icu::UCharsTrie + */ + +U_NAMESPACE_BEGIN + +class UCharsTrieElement; + +/** + * Builder class for UCharsTrie. + * + * This class is not intended for public subclassing. + * @stable ICU 4.8 + */ +class U_COMMON_API UCharsTrieBuilder : public StringTrieBuilder { +public: + /** + * Constructs an empty builder. + * @param errorCode Standard ICU error code. + * @stable ICU 4.8 + */ + UCharsTrieBuilder(UErrorCode &errorCode); + + /** + * Destructor. + * @stable ICU 4.8 + */ + virtual ~UCharsTrieBuilder(); + + /** + * Adds a (string, value) pair. + * The string must be unique. + * The string contents will be copied; the builder does not keep + * a reference to the input UnicodeString or its buffer. + * @param s The input string. + * @param value The value associated with this string. + * @param errorCode Standard 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.) + * @return *this + * @stable ICU 4.8 + */ + UCharsTrieBuilder &add(const UnicodeString &s, int32_t value, UErrorCode &errorCode); + + /** + * Builds a UCharsTrie for the add()ed data. + * Once built, no further data can be add()ed until clear() is called. + * + * A UCharsTrie cannot be empty. At least one (string, value) pair + * must have been add()ed. + * + * This method passes ownership of the builder's internal result array to the new trie object. + * Another call to any build() variant will re-serialize the trie. + * After clear() has been called, a new array will be used as well. + * @param buildOption Build option, see UStringTrieBuildOption. + * @param errorCode Standard 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.) + * @return A new UCharsTrie for the add()ed data. + * @stable ICU 4.8 + */ + UCharsTrie *build(UStringTrieBuildOption buildOption, UErrorCode &errorCode); + + /** + * Builds a UCharsTrie for the add()ed data and char16_t-serializes it. + * Once built, no further data can be add()ed until clear() is called. + * + * A UCharsTrie cannot be empty. At least one (string, value) pair + * must have been add()ed. + * + * Multiple calls to buildUnicodeString() set the UnicodeStrings to the + * builder's same char16_t array, without rebuilding. + * If buildUnicodeString() is called after build(), the trie will be + * re-serialized into a new array (because build() passes on ownership). + * If build() is called after buildUnicodeString(), the trie object returned + * by build() will become the owner of the underlying data for the + * previously returned UnicodeString. + * After clear() has been called, a new array will be used as well. + * @param buildOption Build option, see UStringTrieBuildOption. + * @param result A UnicodeString which will be set to the char16_t-serialized + * UCharsTrie for the add()ed data. + * @param errorCode Standard 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.) + * @return result + * @stable ICU 4.8 + */ + UnicodeString &buildUnicodeString(UStringTrieBuildOption buildOption, UnicodeString &result, + UErrorCode &errorCode); + + /** + * Removes all (string, value) pairs. + * New data can then be add()ed and a new trie can be built. + * @return *this + * @stable ICU 4.8 + */ + UCharsTrieBuilder &clear() { + strings.remove(); + elementsLength=0; + ucharsLength=0; + return *this; + } + +private: + UCharsTrieBuilder(const UCharsTrieBuilder &other) = delete; // no copy constructor + UCharsTrieBuilder &operator=(const UCharsTrieBuilder &other) = delete; // no assignment operator + + void buildUChars(UStringTrieBuildOption buildOption, UErrorCode &errorCode); + + virtual int32_t getElementStringLength(int32_t i) const override; + virtual char16_t getElementUnit(int32_t i, int32_t unitIndex) const override; + virtual int32_t getElementValue(int32_t i) const override; + + virtual int32_t getLimitOfLinearMatch(int32_t first, int32_t last, int32_t unitIndex) const override; + + virtual int32_t countElementUnits(int32_t start, int32_t limit, int32_t unitIndex) const override; + virtual int32_t skipElementsBySomeUnits(int32_t i, int32_t unitIndex, int32_t count) const override; + virtual int32_t indexOfElementWithNextUnit(int32_t i, int32_t unitIndex, char16_t unit) const override; + + virtual UBool matchNodesCanHaveValues() const override { return true; } + + virtual int32_t getMaxBranchLinearSubNodeLength() const override { return UCharsTrie::kMaxBranchLinearSubNodeLength; } + virtual int32_t getMinLinearMatch() const override { return UCharsTrie::kMinLinearMatch; } + virtual int32_t getMaxLinearMatchLength() const override { return UCharsTrie::kMaxLinearMatchLength; } + + class UCTLinearMatchNode : public LinearMatchNode { + public: + UCTLinearMatchNode(const char16_t *units, int32_t len, Node *nextNode); + virtual bool operator==(const Node &other) const override; + virtual void write(StringTrieBuilder &builder) override; + private: + const char16_t *s; + }; + + virtual Node *createLinearMatchNode(int32_t i, int32_t unitIndex, int32_t length, + Node *nextNode) const override; + + UBool ensureCapacity(int32_t length); + virtual int32_t write(int32_t unit) override; + int32_t write(const char16_t *s, int32_t length); + virtual int32_t writeElementUnits(int32_t i, int32_t unitIndex, int32_t length) override; + virtual int32_t writeValueAndFinal(int32_t i, UBool isFinal) override; + virtual int32_t writeValueAndType(UBool hasValue, int32_t value, int32_t node) override; + virtual int32_t writeDeltaTo(int32_t jumpTarget) override; + + UnicodeString strings; + UCharsTrieElement *elements; + int32_t elementsCapacity; + int32_t elementsLength; + + // char16_t serialization of the trie. + // Grows from the back: ucharsLength measures from the end of the buffer! + char16_t *uchars; + int32_t ucharsCapacity; + int32_t ucharsLength; +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif // __UCHARSTRIEBUILDER_H__ diff --git a/deps/icu-small/source/common/unicode/uchriter.h b/deps/icu-small/source/common/unicode/uchriter.h index 9fae5e7de08fee..7260651f16c82a 100644 --- a/deps/icu-small/source/common/unicode/uchriter.h +++ b/deps/icu-small/source/common/unicode/uchriter.h @@ -1,393 +1,393 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1998-2005, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#ifndef UCHRITER_H -#define UCHRITER_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/chariter.h" - -/** - * \file - * \brief C++ API: char16_t Character Iterator - */ - -U_NAMESPACE_BEGIN - -/** - * A concrete subclass of CharacterIterator that iterates over the - * characters (code units or code points) in a char16_t array. - * It's possible not only to create an - * iterator that iterates over an entire char16_t array, but also to - * create one that iterates over only a subrange of a char16_t array - * (iterators over different subranges of the same char16_t array don't - * compare equal). - * @see CharacterIterator - * @see ForwardCharacterIterator - * @stable ICU 2.0 - */ -class U_COMMON_API UCharCharacterIterator : public CharacterIterator { -public: - /** - * Create an iterator over the char16_t array referred to by "textPtr". - * The iteration range is 0 to length-1. - * text is only aliased, not adopted (the - * destructor will not delete it). - * @param textPtr The char16_t array to be iterated over - * @param length The length of the char16_t array - * @stable ICU 2.0 - */ - UCharCharacterIterator(ConstChar16Ptr textPtr, int32_t length); - - /** - * Create an iterator over the char16_t array referred to by "textPtr". - * The iteration range is 0 to length-1. - * text is only aliased, not adopted (the - * destructor will not delete it). - * The starting - * position is specified by "position". If "position" is outside the valid - * iteration range, the behavior of this object is undefined. - * @param textPtr The char16_t array to be iterated over - * @param length The length of the char16_t array - * @param position The starting position of the iteration - * @stable ICU 2.0 - */ - UCharCharacterIterator(ConstChar16Ptr textPtr, int32_t length, - int32_t position); - - /** - * Create an iterator over the char16_t array referred to by "textPtr". - * The iteration range is 0 to end-1. - * text is only aliased, not adopted (the - * destructor will not delete it). - * The starting - * position is specified by "position". If begin and end do not - * form a valid iteration range or "position" is outside the valid - * iteration range, the behavior of this object is undefined. - * @param textPtr The char16_t array to be iterated over - * @param length The length of the char16_t array - * @param textBegin The begin position of the iteration range - * @param textEnd The end position of the iteration range - * @param position The starting position of the iteration - * @stable ICU 2.0 - */ - UCharCharacterIterator(ConstChar16Ptr textPtr, int32_t length, - int32_t textBegin, - int32_t textEnd, - int32_t position); - - /** - * Copy constructor. The new iterator iterates over the same range - * of the same string as "that", and its initial position is the - * same as "that"'s current position. - * @param that The UCharCharacterIterator to be copied - * @stable ICU 2.0 - */ - UCharCharacterIterator(const UCharCharacterIterator& that); - - /** - * Destructor. - * @stable ICU 2.0 - */ - virtual ~UCharCharacterIterator(); - - /** - * Assignment operator. *this is altered to iterate over the sane - * range of the same string as "that", and refers to the same - * character within that string as "that" does. - * @param that The object to be copied - * @return the newly created object - * @stable ICU 2.0 - */ - UCharCharacterIterator& - operator=(const UCharCharacterIterator& that); - - /** - * Returns true if the iterators iterate over the same range of the - * same string and are pointing at the same character. - * @param that The ForwardCharacterIterator used to be compared for equality - * @return true if the iterators iterate over the same range of the - * same string and are pointing at the same character. - * @stable ICU 2.0 - */ - virtual bool operator==(const ForwardCharacterIterator& that) const override; - - /** - * Generates a hash code for this iterator. - * @return the hash code. - * @stable ICU 2.0 - */ - virtual int32_t hashCode(void) const override; - - /** - * Returns a new UCharCharacterIterator referring to the same - * character in the same range of the same string as this one. The - * caller must delete the new iterator. - * @return the CharacterIterator newly created - * @stable ICU 2.0 - */ - virtual UCharCharacterIterator* clone() const override; - - /** - * Sets the iterator to refer to the first code unit in its - * iteration range, and returns that code unit. - * This can be used to begin an iteration with next(). - * @return the first code unit in its iteration range. - * @stable ICU 2.0 - */ - virtual char16_t first(void) override; - - /** - * Sets the iterator to refer to the first code unit in its - * iteration range, returns that code unit, and moves the position - * to the second code unit. This is an alternative to setToStart() - * for forward iteration with nextPostInc(). - * @return the first code unit in its iteration range - * @stable ICU 2.0 - */ - virtual char16_t firstPostInc(void) override; - - /** - * Sets the iterator to refer to the first code point in its - * iteration range, and returns that code unit, - * This can be used to begin an iteration with next32(). - * Note that an iteration with next32PostInc(), beginning with, - * e.g., setToStart() or firstPostInc(), is more efficient. - * @return the first code point in its iteration range - * @stable ICU 2.0 - */ - virtual UChar32 first32(void) override; - - /** - * Sets the iterator to refer to the first code point in its - * iteration range, returns that code point, and moves the position - * to the second code point. This is an alternative to setToStart() - * for forward iteration with next32PostInc(). - * @return the first code point in its iteration range. - * @stable ICU 2.0 - */ - virtual UChar32 first32PostInc(void) override; - - /** - * Sets the iterator to refer to the last code unit in its - * iteration range, and returns that code unit. - * This can be used to begin an iteration with previous(). - * @return the last code unit in its iteration range. - * @stable ICU 2.0 - */ - virtual char16_t last(void) override; - - /** - * Sets the iterator to refer to the last code point in its - * iteration range, and returns that code unit. - * This can be used to begin an iteration with previous32(). - * @return the last code point in its iteration range. - * @stable ICU 2.0 - */ - virtual UChar32 last32(void) override; - - /** - * Sets the iterator to refer to the "position"-th code unit - * in the text-storage object the iterator refers to, and - * returns that code unit. - * @param position the position within the text-storage object - * @return the code unit - * @stable ICU 2.0 - */ - virtual char16_t setIndex(int32_t position) override; - - /** - * Sets the iterator to refer to the beginning of the code point - * that contains the "position"-th code unit - * in the text-storage object the iterator refers to, and - * returns that code point. - * The current position is adjusted to the beginning of the code point - * (its first code unit). - * @param position the position within the text-storage object - * @return the code unit - * @stable ICU 2.0 - */ - virtual UChar32 setIndex32(int32_t position) override; - - /** - * Returns the code unit the iterator currently refers to. - * @return the code unit the iterator currently refers to. - * @stable ICU 2.0 - */ - virtual char16_t current(void) const override; - - /** - * Returns the code point the iterator currently refers to. - * @return the code point the iterator currently refers to. - * @stable ICU 2.0 - */ - virtual UChar32 current32(void) const override; - - /** - * Advances to the next code unit in the iteration range (toward - * endIndex()), and returns that code unit. If there are no more - * code units to return, returns DONE. - * @return the next code unit in the iteration range. - * @stable ICU 2.0 - */ - virtual char16_t next(void) override; - - /** - * Gets the current code unit for returning and advances to the next code unit - * in the iteration range - * (toward endIndex()). If there are - * no more code units to return, returns DONE. - * @return the current code unit. - * @stable ICU 2.0 - */ - virtual char16_t nextPostInc(void) override; - - /** - * Advances to the next code point in the iteration range (toward - * endIndex()), and returns that code point. If there are no more - * code points to return, returns DONE. - * Note that iteration with "pre-increment" semantics is less - * efficient than iteration with "post-increment" semantics - * that is provided by next32PostInc(). - * @return the next code point in the iteration range. - * @stable ICU 2.0 - */ - virtual UChar32 next32(void) override; - - /** - * Gets the current code point for returning and advances to the next code point - * in the iteration range - * (toward endIndex()). If there are - * no more code points to return, returns DONE. - * @return the current point. - * @stable ICU 2.0 - */ - virtual UChar32 next32PostInc(void) override; - - /** - * Returns false if there are no more code units or code points - * at or after the current position in the iteration range. - * This is used with nextPostInc() or next32PostInc() in forward - * iteration. - * @return false if there are no more code units or code points - * at or after the current position in the iteration range. - * @stable ICU 2.0 - */ - virtual UBool hasNext() override; - - /** - * Advances to the previous code unit in the iteration range (toward - * startIndex()), and returns that code unit. If there are no more - * code units to return, returns DONE. - * @return the previous code unit in the iteration range. - * @stable ICU 2.0 - */ - virtual char16_t previous(void) override; - - /** - * Advances to the previous code point in the iteration range (toward - * startIndex()), and returns that code point. If there are no more - * code points to return, returns DONE. - * @return the previous code point in the iteration range. - * @stable ICU 2.0 - */ - virtual UChar32 previous32(void) override; - - /** - * Returns false if there are no more code units or code points - * before the current position in the iteration range. - * This is used with previous() or previous32() in backward - * iteration. - * @return false if there are no more code units or code points - * before the current position in the iteration range. - * @stable ICU 2.0 - */ - virtual UBool hasPrevious() override; - - /** - * Moves the current position relative to the start or end of the - * iteration range, or relative to the current position itself. - * The movement is expressed in numbers of code units forward - * or backward by specifying a positive or negative delta. - * @param delta the position relative to origin. A positive delta means forward; - * a negative delta means backward. - * @param origin Origin enumeration {kStart, kCurrent, kEnd} - * @return the new position - * @stable ICU 2.0 - */ - virtual int32_t move(int32_t delta, EOrigin origin) override; - - /** - * Moves the current position relative to the start or end of the - * iteration range, or relative to the current position itself. - * The movement is expressed in numbers of code points forward - * or backward by specifying a positive or negative delta. - * @param delta the position relative to origin. A positive delta means forward; - * a negative delta means backward. - * @param origin Origin enumeration {kStart, kCurrent, kEnd} - * @return the new position - * @stable ICU 2.0 - */ -#ifdef move32 - // One of the system headers right now is sometimes defining a conflicting macro we don't use -#undef move32 -#endif - virtual int32_t move32(int32_t delta, EOrigin origin) override; - - /** - * Sets the iterator to iterate over a new range of text - * @stable ICU 2.0 - */ - void setText(ConstChar16Ptr newText, int32_t newTextLength); - - /** - * Copies the char16_t array under iteration into the UnicodeString - * referred to by "result". Even if this iterator iterates across - * only a part of this string, the whole string is copied. - * @param result Receives a copy of the text under iteration. - * @stable ICU 2.0 - */ - virtual void getText(UnicodeString& result) override; - - /** - * Return a class ID for this class (not really public) - * @return a class ID for this class - * @stable ICU 2.0 - */ - static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * Return a class ID for this object (not really public) - * @return a class ID for this object. - * @stable ICU 2.0 - */ - virtual UClassID getDynamicClassID(void) const override; - -protected: - /** - * Protected constructor - * @stable ICU 2.0 - */ - UCharCharacterIterator(); - /** - * Protected member text - * @stable ICU 2.0 - */ - const char16_t* text; - -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1998-2005, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#ifndef UCHRITER_H +#define UCHRITER_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/chariter.h" + +/** + * \file + * \brief C++ API: char16_t Character Iterator + */ + +U_NAMESPACE_BEGIN + +/** + * A concrete subclass of CharacterIterator that iterates over the + * characters (code units or code points) in a char16_t array. + * It's possible not only to create an + * iterator that iterates over an entire char16_t array, but also to + * create one that iterates over only a subrange of a char16_t array + * (iterators over different subranges of the same char16_t array don't + * compare equal). + * @see CharacterIterator + * @see ForwardCharacterIterator + * @stable ICU 2.0 + */ +class U_COMMON_API UCharCharacterIterator : public CharacterIterator { +public: + /** + * Create an iterator over the char16_t array referred to by "textPtr". + * The iteration range is 0 to length-1. + * text is only aliased, not adopted (the + * destructor will not delete it). + * @param textPtr The char16_t array to be iterated over + * @param length The length of the char16_t array + * @stable ICU 2.0 + */ + UCharCharacterIterator(ConstChar16Ptr textPtr, int32_t length); + + /** + * Create an iterator over the char16_t array referred to by "textPtr". + * The iteration range is 0 to length-1. + * text is only aliased, not adopted (the + * destructor will not delete it). + * The starting + * position is specified by "position". If "position" is outside the valid + * iteration range, the behavior of this object is undefined. + * @param textPtr The char16_t array to be iterated over + * @param length The length of the char16_t array + * @param position The starting position of the iteration + * @stable ICU 2.0 + */ + UCharCharacterIterator(ConstChar16Ptr textPtr, int32_t length, + int32_t position); + + /** + * Create an iterator over the char16_t array referred to by "textPtr". + * The iteration range is 0 to end-1. + * text is only aliased, not adopted (the + * destructor will not delete it). + * The starting + * position is specified by "position". If begin and end do not + * form a valid iteration range or "position" is outside the valid + * iteration range, the behavior of this object is undefined. + * @param textPtr The char16_t array to be iterated over + * @param length The length of the char16_t array + * @param textBegin The begin position of the iteration range + * @param textEnd The end position of the iteration range + * @param position The starting position of the iteration + * @stable ICU 2.0 + */ + UCharCharacterIterator(ConstChar16Ptr textPtr, int32_t length, + int32_t textBegin, + int32_t textEnd, + int32_t position); + + /** + * Copy constructor. The new iterator iterates over the same range + * of the same string as "that", and its initial position is the + * same as "that"'s current position. + * @param that The UCharCharacterIterator to be copied + * @stable ICU 2.0 + */ + UCharCharacterIterator(const UCharCharacterIterator& that); + + /** + * Destructor. + * @stable ICU 2.0 + */ + virtual ~UCharCharacterIterator(); + + /** + * Assignment operator. *this is altered to iterate over the sane + * range of the same string as "that", and refers to the same + * character within that string as "that" does. + * @param that The object to be copied + * @return the newly created object + * @stable ICU 2.0 + */ + UCharCharacterIterator& + operator=(const UCharCharacterIterator& that); + + /** + * Returns true if the iterators iterate over the same range of the + * same string and are pointing at the same character. + * @param that The ForwardCharacterIterator used to be compared for equality + * @return true if the iterators iterate over the same range of the + * same string and are pointing at the same character. + * @stable ICU 2.0 + */ + virtual bool operator==(const ForwardCharacterIterator& that) const override; + + /** + * Generates a hash code for this iterator. + * @return the hash code. + * @stable ICU 2.0 + */ + virtual int32_t hashCode(void) const override; + + /** + * Returns a new UCharCharacterIterator referring to the same + * character in the same range of the same string as this one. The + * caller must delete the new iterator. + * @return the CharacterIterator newly created + * @stable ICU 2.0 + */ + virtual UCharCharacterIterator* clone() const override; + + /** + * Sets the iterator to refer to the first code unit in its + * iteration range, and returns that code unit. + * This can be used to begin an iteration with next(). + * @return the first code unit in its iteration range. + * @stable ICU 2.0 + */ + virtual char16_t first(void) override; + + /** + * Sets the iterator to refer to the first code unit in its + * iteration range, returns that code unit, and moves the position + * to the second code unit. This is an alternative to setToStart() + * for forward iteration with nextPostInc(). + * @return the first code unit in its iteration range + * @stable ICU 2.0 + */ + virtual char16_t firstPostInc(void) override; + + /** + * Sets the iterator to refer to the first code point in its + * iteration range, and returns that code unit, + * This can be used to begin an iteration with next32(). + * Note that an iteration with next32PostInc(), beginning with, + * e.g., setToStart() or firstPostInc(), is more efficient. + * @return the first code point in its iteration range + * @stable ICU 2.0 + */ + virtual UChar32 first32(void) override; + + /** + * Sets the iterator to refer to the first code point in its + * iteration range, returns that code point, and moves the position + * to the second code point. This is an alternative to setToStart() + * for forward iteration with next32PostInc(). + * @return the first code point in its iteration range. + * @stable ICU 2.0 + */ + virtual UChar32 first32PostInc(void) override; + + /** + * Sets the iterator to refer to the last code unit in its + * iteration range, and returns that code unit. + * This can be used to begin an iteration with previous(). + * @return the last code unit in its iteration range. + * @stable ICU 2.0 + */ + virtual char16_t last(void) override; + + /** + * Sets the iterator to refer to the last code point in its + * iteration range, and returns that code unit. + * This can be used to begin an iteration with previous32(). + * @return the last code point in its iteration range. + * @stable ICU 2.0 + */ + virtual UChar32 last32(void) override; + + /** + * Sets the iterator to refer to the "position"-th code unit + * in the text-storage object the iterator refers to, and + * returns that code unit. + * @param position the position within the text-storage object + * @return the code unit + * @stable ICU 2.0 + */ + virtual char16_t setIndex(int32_t position) override; + + /** + * Sets the iterator to refer to the beginning of the code point + * that contains the "position"-th code unit + * in the text-storage object the iterator refers to, and + * returns that code point. + * The current position is adjusted to the beginning of the code point + * (its first code unit). + * @param position the position within the text-storage object + * @return the code unit + * @stable ICU 2.0 + */ + virtual UChar32 setIndex32(int32_t position) override; + + /** + * Returns the code unit the iterator currently refers to. + * @return the code unit the iterator currently refers to. + * @stable ICU 2.0 + */ + virtual char16_t current(void) const override; + + /** + * Returns the code point the iterator currently refers to. + * @return the code point the iterator currently refers to. + * @stable ICU 2.0 + */ + virtual UChar32 current32(void) const override; + + /** + * Advances to the next code unit in the iteration range (toward + * endIndex()), and returns that code unit. If there are no more + * code units to return, returns DONE. + * @return the next code unit in the iteration range. + * @stable ICU 2.0 + */ + virtual char16_t next(void) override; + + /** + * Gets the current code unit for returning and advances to the next code unit + * in the iteration range + * (toward endIndex()). If there are + * no more code units to return, returns DONE. + * @return the current code unit. + * @stable ICU 2.0 + */ + virtual char16_t nextPostInc(void) override; + + /** + * Advances to the next code point in the iteration range (toward + * endIndex()), and returns that code point. If there are no more + * code points to return, returns DONE. + * Note that iteration with "pre-increment" semantics is less + * efficient than iteration with "post-increment" semantics + * that is provided by next32PostInc(). + * @return the next code point in the iteration range. + * @stable ICU 2.0 + */ + virtual UChar32 next32(void) override; + + /** + * Gets the current code point for returning and advances to the next code point + * in the iteration range + * (toward endIndex()). If there are + * no more code points to return, returns DONE. + * @return the current point. + * @stable ICU 2.0 + */ + virtual UChar32 next32PostInc(void) override; + + /** + * Returns false if there are no more code units or code points + * at or after the current position in the iteration range. + * This is used with nextPostInc() or next32PostInc() in forward + * iteration. + * @return false if there are no more code units or code points + * at or after the current position in the iteration range. + * @stable ICU 2.0 + */ + virtual UBool hasNext() override; + + /** + * Advances to the previous code unit in the iteration range (toward + * startIndex()), and returns that code unit. If there are no more + * code units to return, returns DONE. + * @return the previous code unit in the iteration range. + * @stable ICU 2.0 + */ + virtual char16_t previous(void) override; + + /** + * Advances to the previous code point in the iteration range (toward + * startIndex()), and returns that code point. If there are no more + * code points to return, returns DONE. + * @return the previous code point in the iteration range. + * @stable ICU 2.0 + */ + virtual UChar32 previous32(void) override; + + /** + * Returns false if there are no more code units or code points + * before the current position in the iteration range. + * This is used with previous() or previous32() in backward + * iteration. + * @return false if there are no more code units or code points + * before the current position in the iteration range. + * @stable ICU 2.0 + */ + virtual UBool hasPrevious() override; + + /** + * Moves the current position relative to the start or end of the + * iteration range, or relative to the current position itself. + * The movement is expressed in numbers of code units forward + * or backward by specifying a positive or negative delta. + * @param delta the position relative to origin. A positive delta means forward; + * a negative delta means backward. + * @param origin Origin enumeration {kStart, kCurrent, kEnd} + * @return the new position + * @stable ICU 2.0 + */ + virtual int32_t move(int32_t delta, EOrigin origin) override; + + /** + * Moves the current position relative to the start or end of the + * iteration range, or relative to the current position itself. + * The movement is expressed in numbers of code points forward + * or backward by specifying a positive or negative delta. + * @param delta the position relative to origin. A positive delta means forward; + * a negative delta means backward. + * @param origin Origin enumeration {kStart, kCurrent, kEnd} + * @return the new position + * @stable ICU 2.0 + */ +#ifdef move32 + // One of the system headers right now is sometimes defining a conflicting macro we don't use +#undef move32 +#endif + virtual int32_t move32(int32_t delta, EOrigin origin) override; + + /** + * Sets the iterator to iterate over a new range of text + * @stable ICU 2.0 + */ + void setText(ConstChar16Ptr newText, int32_t newTextLength); + + /** + * Copies the char16_t array under iteration into the UnicodeString + * referred to by "result". Even if this iterator iterates across + * only a part of this string, the whole string is copied. + * @param result Receives a copy of the text under iteration. + * @stable ICU 2.0 + */ + virtual void getText(UnicodeString& result) override; + + /** + * Return a class ID for this class (not really public) + * @return a class ID for this class + * @stable ICU 2.0 + */ + static UClassID U_EXPORT2 getStaticClassID(void); + + /** + * Return a class ID for this object (not really public) + * @return a class ID for this object. + * @stable ICU 2.0 + */ + virtual UClassID getDynamicClassID(void) const override; + +protected: + /** + * Protected constructor + * @stable ICU 2.0 + */ + UCharCharacterIterator(); + /** + * Protected member text + * @stable ICU 2.0 + */ + const char16_t* text; + +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/uclean.h b/deps/icu-small/source/common/unicode/uclean.h index c2d920a16ef48a..ce5bd983e0ca21 100644 --- a/deps/icu-small/source/common/unicode/uclean.h +++ b/deps/icu-small/source/common/unicode/uclean.h @@ -1,262 +1,262 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 2001-2014, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* file name: uclean.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2001July05 -* created by: George Rhoten -*/ - -#ifndef __UCLEAN_H__ -#define __UCLEAN_H__ - -#include "unicode/utypes.h" -/** - * \file - * \brief C API: Initialize and clean up ICU - */ - -/** - * Initialize ICU. - * - * Use of this function is optional. It is OK to simply use ICU - * services and functions without first having initialized - * ICU by calling u_init(). - * - * u_init() will attempt to load some part of ICU's data, and is - * useful as a test for configuration or installation problems that - * leave the ICU data inaccessible. A successful invocation of u_init() - * does not, however, guarantee that all ICU data is accessible. - * - * Multiple calls to u_init() cause no harm, aside from the small amount - * of time required. - * - * In old versions of ICU, u_init() was required in multi-threaded applications - * to ensure the thread safety of ICU. u_init() is no longer needed for this purpose. - * - * @param status An ICU UErrorCode parameter. It must not be NULL. - * An Error will be returned if some required part of ICU data can not - * be loaded or initialized. - * The function returns immediately if the input error code indicates a - * failure, as usual. - * - * @stable ICU 2.6 - */ -U_CAPI void U_EXPORT2 -u_init(UErrorCode *status); - -#ifndef U_HIDE_SYSTEM_API -/** - * Clean up the system resources, such as allocated memory or open files, - * used in all ICU libraries. This will free/delete all memory owned by the - * ICU libraries, and return them to their original load state. All open ICU - * items (collators, resource bundles, converters, etc.) must be closed before - * calling this function, otherwise ICU may not free its allocated memory - * (e.g. close your converters and resource bundles before calling this - * function). Generally, this function should be called once just before - * an application exits. For applications that dynamically load and unload - * the ICU libraries (relatively uncommon), u_cleanup() should be called - * just before the library unload. - *

- * u_cleanup() also clears any ICU heap functions, mutex functions or - * trace functions that may have been set for the process. - * This has the effect of restoring ICU to its initial condition, before - * any of these override functions were installed. Refer to - * u_setMemoryFunctions(), u_setMutexFunctions and - * utrace_setFunctions(). If ICU is to be reinitialized after - * calling u_cleanup(), these runtime override functions will need to - * be set up again if they are still required. - *

- * u_cleanup() is not thread safe. All other threads should stop using ICU - * before calling this function. - *

- * Any open ICU items will be left in an undefined state by u_cleanup(), - * and any subsequent attempt to use such an item will give unpredictable - * results. - *

- * After calling u_cleanup(), an application may continue to use ICU by - * calling u_init(). An application must invoke u_init() first from one single - * thread before allowing other threads call u_init(). All threads existing - * at the time of the first thread's call to u_init() must also call - * u_init() themselves before continuing with other ICU operations. - *

- * The use of u_cleanup() just before an application terminates is optional, - * but it should be called only once for performance reasons. The primary - * benefit is to eliminate reports of memory or resource leaks originating - * in ICU code from the results generated by heap analysis tools. - *

- * Use this function with great care! - *

- * - * @stable ICU 2.0 - * @system - */ -U_CAPI void U_EXPORT2 -u_cleanup(void); - -U_CDECL_BEGIN -/** - * Pointer type for a user supplied memory allocation function. - * @param context user supplied value, obtained from u_setMemoryFunctions(). - * @param size The number of bytes to be allocated - * @return Pointer to the newly allocated memory, or NULL if the allocation failed. - * @stable ICU 2.8 - * @system - */ -typedef void *U_CALLCONV UMemAllocFn(const void *context, size_t size); -/** - * Pointer type for a user supplied memory re-allocation function. - * @param context user supplied value, obtained from u_setMemoryFunctions(). - * @param size The number of bytes to be allocated - * @return Pointer to the newly allocated memory, or NULL if the allocation failed. - * @stable ICU 2.8 - * @system - */ -typedef void *U_CALLCONV UMemReallocFn(const void *context, void *mem, size_t size); -/** - * Pointer type for a user supplied memory free function. Behavior should be - * similar the standard C library free(). - * @param context user supplied value, obtained from u_setMemoryFunctions(). - * @param mem Pointer to the memory block to be resized - * @param size The new size for the block - * @return Pointer to the resized memory block, or NULL if the resizing failed. - * @stable ICU 2.8 - * @system - */ -typedef void U_CALLCONV UMemFreeFn (const void *context, void *mem); - -/** - * Set the functions that ICU will use for memory allocation. - * Use of this function is optional; by default (without this function), ICU will - * use the standard C library malloc() and free() functions. - * This function can only be used when ICU is in an initial, unused state, before - * u_init() has been called. - * @param context This pointer value will be saved, and then (later) passed as - * a parameter to the memory functions each time they - * are called. - * @param a Pointer to a user-supplied malloc function. - * @param r Pointer to a user-supplied realloc function. - * @param f Pointer to a user-supplied free function. - * @param status Receives error values. - * @stable ICU 2.8 - * @system - */ -U_CAPI void U_EXPORT2 -u_setMemoryFunctions(const void *context, UMemAllocFn * U_CALLCONV_FPTR a, UMemReallocFn * U_CALLCONV_FPTR r, UMemFreeFn * U_CALLCONV_FPTR f, - UErrorCode *status); - -U_CDECL_END - -#ifndef U_HIDE_DEPRECATED_API -/********************************************************************************* - * - * Deprecated Functions - * - * The following functions for user supplied mutexes are no longer supported. - * Any attempt to use them will return a U_UNSUPPORTED_ERROR. - * - **********************************************************************************/ - -/** - * An opaque pointer type that represents an ICU mutex. - * For user-implemented mutexes, the value will typically point to a - * struct or object that implements the mutex. - * @deprecated ICU 52. This type is no longer supported. - * @system - */ -typedef void *UMTX; - -U_CDECL_BEGIN -/** - * Function Pointer type for a user supplied mutex initialization function. - * The user-supplied function will be called by ICU whenever ICU needs to create a - * new mutex. The function implementation should create a mutex, and store a pointer - * to something that uniquely identifies the mutex into the UMTX that is supplied - * as a parameter. - * @param context user supplied value, obtained from u_setMutexFunctions(). - * @param mutex Receives a pointer that identifies the new mutex. - * The mutex init function must set the UMTX to a non-null value. - * Subsequent calls by ICU to lock, unlock, or destroy a mutex will - * identify the mutex by the UMTX value. - * @param status Error status. Report errors back to ICU by setting this variable - * with an error code. - * @deprecated ICU 52. This function is no longer supported. - * @system - */ -typedef void U_CALLCONV UMtxInitFn (const void *context, UMTX *mutex, UErrorCode* status); - - -/** - * Function Pointer type for a user supplied mutex functions. - * One of the user-supplied functions with this signature will be called by ICU - * whenever ICU needs to lock, unlock, or destroy a mutex. - * @param context user supplied value, obtained from u_setMutexFunctions(). - * @param mutex specify the mutex on which to operate. - * @deprecated ICU 52. This function is no longer supported. - * @system - */ -typedef void U_CALLCONV UMtxFn (const void *context, UMTX *mutex); -U_CDECL_END - -/** - * Set the functions that ICU will use for mutex operations - * Use of this function is optional; by default (without this function), ICU will - * directly access system functions for mutex operations - * This function can only be used when ICU is in an initial, unused state, before - * u_init() has been called. - * @param context This pointer value will be saved, and then (later) passed as - * a parameter to the user-supplied mutex functions each time they - * are called. - * @param init Pointer to a mutex initialization function. Must be non-null. - * @param destroy Pointer to the mutex destroy function. Must be non-null. - * @param lock pointer to the mutex lock function. Must be non-null. - * @param unlock Pointer to the mutex unlock function. Must be non-null. - * @param status Receives error values. - * @deprecated ICU 52. This function is no longer supported. - * @system - */ -U_DEPRECATED void U_EXPORT2 -u_setMutexFunctions(const void *context, UMtxInitFn *init, UMtxFn *destroy, UMtxFn *lock, UMtxFn *unlock, - UErrorCode *status); - - -/** - * Pointer type for a user supplied atomic increment or decrement function. - * @param context user supplied value, obtained from u_setAtomicIncDecFunctions(). - * @param p Pointer to a 32 bit int to be incremented or decremented - * @return The value of the variable after the inc or dec operation. - * @deprecated ICU 52. This function is no longer supported. - * @system - */ -typedef int32_t U_CALLCONV UMtxAtomicFn(const void *context, int32_t *p); - -/** - * Set the functions that ICU will use for atomic increment and decrement of int32_t values. - * Use of this function is optional; by default (without this function), ICU will - * use its own internal implementation of atomic increment/decrement. - * This function can only be used when ICU is in an initial, unused state, before - * u_init() has been called. - * @param context This pointer value will be saved, and then (later) passed as - * a parameter to the increment and decrement functions each time they - * are called. This function can only be called - * @param inc Pointer to a function to do an atomic increment operation. Must be non-null. - * @param dec Pointer to a function to do an atomic decrement operation. Must be non-null. - * @param status Receives error values. - * @deprecated ICU 52. This function is no longer supported. - * @system - */ -U_DEPRECATED void U_EXPORT2 -u_setAtomicIncDecFunctions(const void *context, UMtxAtomicFn *inc, UMtxAtomicFn *dec, - UErrorCode *status); - -#endif /* U_HIDE_DEPRECATED_API */ -#endif /* U_HIDE_SYSTEM_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 2001-2014, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* file name: uclean.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2001July05 +* created by: George Rhoten +*/ + +#ifndef __UCLEAN_H__ +#define __UCLEAN_H__ + +#include "unicode/utypes.h" +/** + * \file + * \brief C API: Initialize and clean up ICU + */ + +/** + * Initialize ICU. + * + * Use of this function is optional. It is OK to simply use ICU + * services and functions without first having initialized + * ICU by calling u_init(). + * + * u_init() will attempt to load some part of ICU's data, and is + * useful as a test for configuration or installation problems that + * leave the ICU data inaccessible. A successful invocation of u_init() + * does not, however, guarantee that all ICU data is accessible. + * + * Multiple calls to u_init() cause no harm, aside from the small amount + * of time required. + * + * In old versions of ICU, u_init() was required in multi-threaded applications + * to ensure the thread safety of ICU. u_init() is no longer needed for this purpose. + * + * @param status An ICU UErrorCode parameter. It must not be NULL. + * An Error will be returned if some required part of ICU data can not + * be loaded or initialized. + * The function returns immediately if the input error code indicates a + * failure, as usual. + * + * @stable ICU 2.6 + */ +U_CAPI void U_EXPORT2 +u_init(UErrorCode *status); + +#ifndef U_HIDE_SYSTEM_API +/** + * Clean up the system resources, such as allocated memory or open files, + * used in all ICU libraries. This will free/delete all memory owned by the + * ICU libraries, and return them to their original load state. All open ICU + * items (collators, resource bundles, converters, etc.) must be closed before + * calling this function, otherwise ICU may not free its allocated memory + * (e.g. close your converters and resource bundles before calling this + * function). Generally, this function should be called once just before + * an application exits. For applications that dynamically load and unload + * the ICU libraries (relatively uncommon), u_cleanup() should be called + * just before the library unload. + *

+ * u_cleanup() also clears any ICU heap functions, mutex functions or + * trace functions that may have been set for the process. + * This has the effect of restoring ICU to its initial condition, before + * any of these override functions were installed. Refer to + * u_setMemoryFunctions(), u_setMutexFunctions and + * utrace_setFunctions(). If ICU is to be reinitialized after + * calling u_cleanup(), these runtime override functions will need to + * be set up again if they are still required. + *

+ * u_cleanup() is not thread safe. All other threads should stop using ICU + * before calling this function. + *

+ * Any open ICU items will be left in an undefined state by u_cleanup(), + * and any subsequent attempt to use such an item will give unpredictable + * results. + *

+ * After calling u_cleanup(), an application may continue to use ICU by + * calling u_init(). An application must invoke u_init() first from one single + * thread before allowing other threads call u_init(). All threads existing + * at the time of the first thread's call to u_init() must also call + * u_init() themselves before continuing with other ICU operations. + *

+ * The use of u_cleanup() just before an application terminates is optional, + * but it should be called only once for performance reasons. The primary + * benefit is to eliminate reports of memory or resource leaks originating + * in ICU code from the results generated by heap analysis tools. + *

+ * Use this function with great care! + *

+ * + * @stable ICU 2.0 + * @system + */ +U_CAPI void U_EXPORT2 +u_cleanup(void); + +U_CDECL_BEGIN +/** + * Pointer type for a user supplied memory allocation function. + * @param context user supplied value, obtained from u_setMemoryFunctions(). + * @param size The number of bytes to be allocated + * @return Pointer to the newly allocated memory, or NULL if the allocation failed. + * @stable ICU 2.8 + * @system + */ +typedef void *U_CALLCONV UMemAllocFn(const void *context, size_t size); +/** + * Pointer type for a user supplied memory re-allocation function. + * @param context user supplied value, obtained from u_setMemoryFunctions(). + * @param mem Pointer to the memory block to be resized. + * @param size The new size for the block. + * @return Pointer to the newly allocated memory, or NULL if the allocation failed. + * @stable ICU 2.8 + * @system + */ +typedef void *U_CALLCONV UMemReallocFn(const void *context, void *mem, size_t size); +/** + * Pointer type for a user supplied memory free function. Behavior should be + * similar the standard C library free(). + * @param context user supplied value, obtained from u_setMemoryFunctions(). + * @param mem Pointer to the memory block to be freed. + * @return Pointer to the resized memory block, or NULL if the resizing failed. + * @stable ICU 2.8 + * @system + */ +typedef void U_CALLCONV UMemFreeFn (const void *context, void *mem); + +/** + * Set the functions that ICU will use for memory allocation. + * Use of this function is optional; by default (without this function), ICU will + * use the standard C library malloc() and free() functions. + * This function can only be used when ICU is in an initial, unused state, before + * u_init() has been called. + * @param context This pointer value will be saved, and then (later) passed as + * a parameter to the memory functions each time they + * are called. + * @param a Pointer to a user-supplied malloc function. + * @param r Pointer to a user-supplied realloc function. + * @param f Pointer to a user-supplied free function. + * @param status Receives error values. + * @stable ICU 2.8 + * @system + */ +U_CAPI void U_EXPORT2 +u_setMemoryFunctions(const void *context, UMemAllocFn * U_CALLCONV_FPTR a, UMemReallocFn * U_CALLCONV_FPTR r, UMemFreeFn * U_CALLCONV_FPTR f, + UErrorCode *status); + +U_CDECL_END + +#ifndef U_HIDE_DEPRECATED_API +/********************************************************************************* + * + * Deprecated Functions + * + * The following functions for user supplied mutexes are no longer supported. + * Any attempt to use them will return a U_UNSUPPORTED_ERROR. + * + **********************************************************************************/ + +/** + * An opaque pointer type that represents an ICU mutex. + * For user-implemented mutexes, the value will typically point to a + * struct or object that implements the mutex. + * @deprecated ICU 52. This type is no longer supported. + * @system + */ +typedef void *UMTX; + +U_CDECL_BEGIN +/** + * Function Pointer type for a user supplied mutex initialization function. + * The user-supplied function will be called by ICU whenever ICU needs to create a + * new mutex. The function implementation should create a mutex, and store a pointer + * to something that uniquely identifies the mutex into the UMTX that is supplied + * as a parameter. + * @param context user supplied value, obtained from u_setMutexFunctions(). + * @param mutex Receives a pointer that identifies the new mutex. + * The mutex init function must set the UMTX to a non-null value. + * Subsequent calls by ICU to lock, unlock, or destroy a mutex will + * identify the mutex by the UMTX value. + * @param status Error status. Report errors back to ICU by setting this variable + * with an error code. + * @deprecated ICU 52. This function is no longer supported. + * @system + */ +typedef void U_CALLCONV UMtxInitFn (const void *context, UMTX *mutex, UErrorCode* status); + + +/** + * Function Pointer type for a user supplied mutex functions. + * One of the user-supplied functions with this signature will be called by ICU + * whenever ICU needs to lock, unlock, or destroy a mutex. + * @param context user supplied value, obtained from u_setMutexFunctions(). + * @param mutex specify the mutex on which to operate. + * @deprecated ICU 52. This function is no longer supported. + * @system + */ +typedef void U_CALLCONV UMtxFn (const void *context, UMTX *mutex); +U_CDECL_END + +/** + * Set the functions that ICU will use for mutex operations + * Use of this function is optional; by default (without this function), ICU will + * directly access system functions for mutex operations + * This function can only be used when ICU is in an initial, unused state, before + * u_init() has been called. + * @param context This pointer value will be saved, and then (later) passed as + * a parameter to the user-supplied mutex functions each time they + * are called. + * @param init Pointer to a mutex initialization function. Must be non-null. + * @param destroy Pointer to the mutex destroy function. Must be non-null. + * @param lock pointer to the mutex lock function. Must be non-null. + * @param unlock Pointer to the mutex unlock function. Must be non-null. + * @param status Receives error values. + * @deprecated ICU 52. This function is no longer supported. + * @system + */ +U_DEPRECATED void U_EXPORT2 +u_setMutexFunctions(const void *context, UMtxInitFn *init, UMtxFn *destroy, UMtxFn *lock, UMtxFn *unlock, + UErrorCode *status); + + +/** + * Pointer type for a user supplied atomic increment or decrement function. + * @param context user supplied value, obtained from u_setAtomicIncDecFunctions(). + * @param p Pointer to a 32 bit int to be incremented or decremented + * @return The value of the variable after the inc or dec operation. + * @deprecated ICU 52. This function is no longer supported. + * @system + */ +typedef int32_t U_CALLCONV UMtxAtomicFn(const void *context, int32_t *p); + +/** + * Set the functions that ICU will use for atomic increment and decrement of int32_t values. + * Use of this function is optional; by default (without this function), ICU will + * use its own internal implementation of atomic increment/decrement. + * This function can only be used when ICU is in an initial, unused state, before + * u_init() has been called. + * @param context This pointer value will be saved, and then (later) passed as + * a parameter to the increment and decrement functions each time they + * are called. This function can only be called + * @param inc Pointer to a function to do an atomic increment operation. Must be non-null. + * @param dec Pointer to a function to do an atomic decrement operation. Must be non-null. + * @param status Receives error values. + * @deprecated ICU 52. This function is no longer supported. + * @system + */ +U_DEPRECATED void U_EXPORT2 +u_setAtomicIncDecFunctions(const void *context, UMtxAtomicFn *inc, UMtxAtomicFn *dec, + UErrorCode *status); + +#endif /* U_HIDE_DEPRECATED_API */ +#endif /* U_HIDE_SYSTEM_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/ucnv.h b/deps/icu-small/source/common/unicode/ucnv.h index 20c173b662832d..e1d58c7bc29f25 100644 --- a/deps/icu-small/source/common/unicode/ucnv.h +++ b/deps/icu-small/source/common/unicode/ucnv.h @@ -1,2056 +1,2056 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** - * ucnv.h: - * External APIs for the ICU's codeset conversion library - * Bertrand A. Damiba - * - * Modification History: - * - * Date Name Description - * 04/04/99 helena Fixed internal header inclusion. - * 05/11/00 helena Added setFallback and usesFallback APIs. - * 06/29/2000 helena Major rewrite of the callback APIs. - * 12/07/2000 srl Update of documentation - */ - -/** - * \file - * \brief C API: Character conversion - * - *

Character Conversion C API

- * - *

This API is used to convert codepage or character encoded data to and - * from UTF-16. You can open a converter with {@link ucnv_open() }. With that - * converter, you can get its properties, set options, convert your data and - * close the converter.

- * - *

Since many software programs recognize different converter names for - * different types of converters, there are other functions in this API to - * iterate over the converter aliases. The functions {@link ucnv_getAvailableName() }, - * {@link ucnv_getAlias() } and {@link ucnv_getStandardName() } are some of the - * more frequently used alias functions to get this information.

- * - *

When a converter encounters an illegal, irregular, invalid or unmappable character - * its default behavior is to use a substitution character to replace the - * bad byte sequence. This behavior can be changed by using {@link ucnv_setFromUCallBack() } - * or {@link ucnv_setToUCallBack() } on the converter. The header ucnv_err.h defines - * many other callback actions that can be used instead of a character substitution.

- * - *

More information about this API can be found in our - * User Guide.

- */ - -#ifndef UCNV_H -#define UCNV_H - -#include "unicode/ucnv_err.h" -#include "unicode/uenum.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -#if !defined(USET_DEFINED) && !defined(U_IN_DOXYGEN) - -#define USET_DEFINED - -/** - * USet is the C API type corresponding to C++ class UnicodeSet. - * It is forward-declared here to avoid including unicode/uset.h file if related - * conversion APIs are not used. - * - * @see ucnv_getUnicodeSet - * @stable ICU 2.4 - */ -typedef struct USet USet; - -#endif - -#if !UCONFIG_NO_CONVERSION - -U_CDECL_BEGIN - -/** Maximum length of a converter name including the terminating NULL @stable ICU 2.0 */ -#define UCNV_MAX_CONVERTER_NAME_LENGTH 60 -/** Maximum length of a converter name including path and terminating NULL @stable ICU 2.0 */ -#define UCNV_MAX_FULL_FILE_NAME_LENGTH (600+UCNV_MAX_CONVERTER_NAME_LENGTH) - -/** Shift in for EBDCDIC_STATEFUL and iso2022 states @stable ICU 2.0 */ -#define UCNV_SI 0x0F -/** Shift out for EBDCDIC_STATEFUL and iso2022 states @stable ICU 2.0 */ -#define UCNV_SO 0x0E - -/** - * Enum for specifying basic types of converters - * @see ucnv_getType - * @stable ICU 2.0 - */ -typedef enum { - /** @stable ICU 2.0 */ - UCNV_UNSUPPORTED_CONVERTER = -1, - /** @stable ICU 2.0 */ - UCNV_SBCS = 0, - /** @stable ICU 2.0 */ - UCNV_DBCS = 1, - /** @stable ICU 2.0 */ - UCNV_MBCS = 2, - /** @stable ICU 2.0 */ - UCNV_LATIN_1 = 3, - /** @stable ICU 2.0 */ - UCNV_UTF8 = 4, - /** @stable ICU 2.0 */ - UCNV_UTF16_BigEndian = 5, - /** @stable ICU 2.0 */ - UCNV_UTF16_LittleEndian = 6, - /** @stable ICU 2.0 */ - UCNV_UTF32_BigEndian = 7, - /** @stable ICU 2.0 */ - UCNV_UTF32_LittleEndian = 8, - /** @stable ICU 2.0 */ - UCNV_EBCDIC_STATEFUL = 9, - /** @stable ICU 2.0 */ - UCNV_ISO_2022 = 10, - - /** @stable ICU 2.0 */ - UCNV_LMBCS_1 = 11, - /** @stable ICU 2.0 */ - UCNV_LMBCS_2, - /** @stable ICU 2.0 */ - UCNV_LMBCS_3, - /** @stable ICU 2.0 */ - UCNV_LMBCS_4, - /** @stable ICU 2.0 */ - UCNV_LMBCS_5, - /** @stable ICU 2.0 */ - UCNV_LMBCS_6, - /** @stable ICU 2.0 */ - UCNV_LMBCS_8, - /** @stable ICU 2.0 */ - UCNV_LMBCS_11, - /** @stable ICU 2.0 */ - UCNV_LMBCS_16, - /** @stable ICU 2.0 */ - UCNV_LMBCS_17, - /** @stable ICU 2.0 */ - UCNV_LMBCS_18, - /** @stable ICU 2.0 */ - UCNV_LMBCS_19, - /** @stable ICU 2.0 */ - UCNV_LMBCS_LAST = UCNV_LMBCS_19, - /** @stable ICU 2.0 */ - UCNV_HZ, - /** @stable ICU 2.0 */ - UCNV_SCSU, - /** @stable ICU 2.0 */ - UCNV_ISCII, - /** @stable ICU 2.0 */ - UCNV_US_ASCII, - /** @stable ICU 2.0 */ - UCNV_UTF7, - /** @stable ICU 2.2 */ - UCNV_BOCU1, - /** @stable ICU 2.2 */ - UCNV_UTF16, - /** @stable ICU 2.2 */ - UCNV_UTF32, - /** @stable ICU 2.2 */ - UCNV_CESU8, - /** @stable ICU 2.4 */ - UCNV_IMAP_MAILBOX, - /** @stable ICU 4.8 */ - UCNV_COMPOUND_TEXT, - - /* Number of converter types for which we have conversion routines. */ - UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES -} UConverterType; - -/** - * Enum for specifying which platform a converter ID refers to. - * The use of platform/CCSID is not recommended. See ucnv_openCCSID(). - * - * @see ucnv_getPlatform - * @see ucnv_openCCSID - * @see ucnv_getCCSID - * @stable ICU 2.0 - */ -typedef enum { - UCNV_UNKNOWN = -1, - UCNV_IBM = 0 -} UConverterPlatform; - -/** - * Function pointer for error callback in the codepage to unicode direction. - * Called when an error has occurred in conversion to unicode, or on open/close of the callback (see reason). - * @param context Pointer to the callback's private data - * @param args Information about the conversion in progress - * @param codeUnits Points to 'length' bytes of the concerned codepage sequence - * @param length Size (in bytes) of the concerned codepage sequence - * @param reason Defines the reason the callback was invoked - * @param pErrorCode ICU error code in/out parameter. - * For converter callback functions, set to a conversion error - * before the call, and the callback may reset it to U_ZERO_ERROR. - * @see ucnv_setToUCallBack - * @see UConverterToUnicodeArgs - * @stable ICU 2.0 - */ -typedef void (U_EXPORT2 *UConverterToUCallback) ( - const void* context, - UConverterToUnicodeArgs *args, - const char *codeUnits, - int32_t length, - UConverterCallbackReason reason, - UErrorCode *pErrorCode); - -/** - * Function pointer for error callback in the unicode to codepage direction. - * Called when an error has occurred in conversion from unicode, or on open/close of the callback (see reason). - * @param context Pointer to the callback's private data - * @param args Information about the conversion in progress - * @param codeUnits Points to 'length' UChars of the concerned Unicode sequence - * @param length Size (in bytes) of the concerned codepage sequence - * @param codePoint Single UChar32 (UTF-32) containing the concerend Unicode codepoint. - * @param reason Defines the reason the callback was invoked - * @param pErrorCode ICU error code in/out parameter. - * For converter callback functions, set to a conversion error - * before the call, and the callback may reset it to U_ZERO_ERROR. - * @see ucnv_setFromUCallBack - * @stable ICU 2.0 - */ -typedef void (U_EXPORT2 *UConverterFromUCallback) ( - const void* context, - UConverterFromUnicodeArgs *args, - const UChar* codeUnits, - int32_t length, - UChar32 codePoint, - UConverterCallbackReason reason, - UErrorCode *pErrorCode); - -U_CDECL_END - -/** - * Character that separates converter names from options and options from each other. - * @see ucnv_open - * @stable ICU 2.0 - */ -#define UCNV_OPTION_SEP_CHAR ',' - -/** - * String version of UCNV_OPTION_SEP_CHAR. - * @see ucnv_open - * @stable ICU 2.0 - */ -#define UCNV_OPTION_SEP_STRING "," - -/** - * Character that separates a converter option from its value. - * @see ucnv_open - * @stable ICU 2.0 - */ -#define UCNV_VALUE_SEP_CHAR '=' - -/** - * String version of UCNV_VALUE_SEP_CHAR. - * @see ucnv_open - * @stable ICU 2.0 - */ -#define UCNV_VALUE_SEP_STRING "=" - -/** - * Converter option for specifying a locale. - * For example, ucnv_open("SCSU,locale=ja", &errorCode); - * See convrtrs.txt. - * - * @see ucnv_open - * @stable ICU 2.0 - */ -#define UCNV_LOCALE_OPTION_STRING ",locale=" - -/** - * Converter option for specifying a version selector (0..9) for some converters. - * For example, - * \code - * ucnv_open("UTF-7,version=1", &errorCode); - * \endcode - * See convrtrs.txt. - * - * @see ucnv_open - * @stable ICU 2.4 - */ -#define UCNV_VERSION_OPTION_STRING ",version=" - -/** - * Converter option for EBCDIC SBCS or mixed-SBCS/DBCS (stateful) codepages. - * Swaps Unicode mappings for EBCDIC LF and NL codes, as used on - * S/390 (z/OS) Unix System Services (Open Edition). - * For example, ucnv_open("ibm-1047,swaplfnl", &errorCode); - * See convrtrs.txt. - * - * @see ucnv_open - * @stable ICU 2.4 - */ -#define UCNV_SWAP_LFNL_OPTION_STRING ",swaplfnl" - -/** - * Do a fuzzy compare of two converter/alias names. - * The comparison is case-insensitive, ignores leading zeroes if they are not - * followed by further digits, and ignores all but letters and digits. - * Thus the strings "UTF-8", "utf_8", "u*T@f08" and "Utf 8" are exactly equivalent. - * See section 1.4, Charset Alias Matching in Unicode Technical Standard #22 - * at http://www.unicode.org/reports/tr22/ - * - * @param name1 a converter name or alias, zero-terminated - * @param name2 a converter name or alias, zero-terminated - * @return 0 if the names match, or a negative value if the name1 - * lexically precedes name2, or a positive value if the name1 - * lexically follows name2. - * @stable ICU 2.0 - */ -U_CAPI int U_EXPORT2 -ucnv_compareNames(const char *name1, const char *name2); - - -/** - * Creates a UConverter object with the name of a coded character set specified as a C string. - * The actual name will be resolved with the alias file - * using a case-insensitive string comparison that ignores - * leading zeroes and all non-alphanumeric characters. - * E.g., the names "UTF8", "utf-8", "u*T@f08" and "Utf 8" are all equivalent. - * (See also ucnv_compareNames().) - * If NULL is passed for the converter name, it will create one with the - * getDefaultName return value. - * - *

A converter name for ICU 1.5 and above may contain options - * like a locale specification to control the specific behavior of - * the newly instantiated converter. - * The meaning of the options depends on the particular converter. - * If an option is not defined for or recognized by a given converter, then it is ignored.

- * - *

Options are appended to the converter name string, with a - * UCNV_OPTION_SEP_CHAR between the name and the first option and - * also between adjacent options.

- * - *

If the alias is ambiguous, then the preferred converter is used - * and the status is set to U_AMBIGUOUS_ALIAS_WARNING.

- * - *

The conversion behavior and names can vary between platforms. ICU may - * convert some characters differently from other platforms. Details on this topic - * are in the User - * Guide. Aliases starting with a "cp" prefix have no specific meaning - * other than its an alias starting with the letters "cp". Please do not - * associate any meaning to these aliases.

- * - * \snippet samples/ucnv/convsamp.cpp ucnv_open - * - * @param converterName Name of the coded character set table. - * This may have options appended to the string. - * IANA alias character set names, IBM CCSIDs starting with "ibm-", - * Windows codepage numbers starting with "windows-" are frequently - * used for this parameter. See ucnv_getAvailableName and - * ucnv_getAlias for a complete list that is available. - * If this parameter is NULL, the default converter will be used. - * @param err outgoing error status U_MEMORY_ALLOCATION_ERROR, U_FILE_ACCESS_ERROR - * @return the created Unicode converter object, or NULL if an error occurred - * @see ucnv_openU - * @see ucnv_openCCSID - * @see ucnv_getAvailableName - * @see ucnv_getAlias - * @see ucnv_getDefaultName - * @see ucnv_close - * @see ucnv_compareNames - * @stable ICU 2.0 - */ -U_CAPI UConverter* U_EXPORT2 -ucnv_open(const char *converterName, UErrorCode *err); - - -/** - * Creates a Unicode converter with the names specified as unicode string. - * The name should be limited to the ASCII-7 alphanumerics range. - * The actual name will be resolved with the alias file - * using a case-insensitive string comparison that ignores - * leading zeroes and all non-alphanumeric characters. - * E.g., the names "UTF8", "utf-8", "u*T@f08" and "Utf 8" are all equivalent. - * (See also ucnv_compareNames().) - * If NULL is passed for the converter name, it will create - * one with the ucnv_getDefaultName() return value. - * If the alias is ambiguous, then the preferred converter is used - * and the status is set to U_AMBIGUOUS_ALIAS_WARNING. - * - *

See ucnv_open for the complete details

- * @param name Name of the UConverter table in a zero terminated - * Unicode string - * @param err outgoing error status U_MEMORY_ALLOCATION_ERROR, - * U_FILE_ACCESS_ERROR - * @return the created Unicode converter object, or NULL if an - * error occurred - * @see ucnv_open - * @see ucnv_openCCSID - * @see ucnv_close - * @see ucnv_compareNames - * @stable ICU 2.0 - */ -U_CAPI UConverter* U_EXPORT2 -ucnv_openU(const UChar *name, - UErrorCode *err); - -/** - * Creates a UConverter object from a CCSID number and platform pair. - * Note that the usefulness of this function is limited to platforms with numeric - * encoding IDs. Only IBM and Microsoft platforms use numeric (16-bit) identifiers for - * encodings. - * - * In addition, IBM CCSIDs and Unicode conversion tables are not 1:1 related. - * For many IBM CCSIDs there are multiple (up to six) Unicode conversion tables, and - * for some Unicode conversion tables there are multiple CCSIDs. - * Some "alternate" Unicode conversion tables are provided by the - * IBM CDRA conversion table registry. - * The most prominent example of a systematic modification of conversion tables that is - * not provided in the form of conversion table files in the repository is - * that S/390 Unix System Services swaps the codes for Line Feed and New Line in all - * EBCDIC codepages, which requires such a swap in the Unicode conversion tables as well. - * - * Only IBM default conversion tables are accessible with ucnv_openCCSID(). - * ucnv_getCCSID() will return the same CCSID for all conversion tables that are associated - * with that CCSID. - * - * Currently, the only "platform" supported in the ICU converter API is UCNV_IBM. - * - * In summary, the use of CCSIDs and the associated API functions is not recommended. - * - * In order to open a converter with the default IBM CDRA Unicode conversion table, - * you can use this function or use the prefix "ibm-": - * \code - * char name[20]; - * sprintf(name, "ibm-%hu", ccsid); - * cnv=ucnv_open(name, &errorCode); - * \endcode - * - * In order to open a converter with the IBM S/390 Unix System Services variant - * of a Unicode/EBCDIC conversion table, - * you can use the prefix "ibm-" together with the option string UCNV_SWAP_LFNL_OPTION_STRING: - * \code - * char name[20]; - * sprintf(name, "ibm-%hu" UCNV_SWAP_LFNL_OPTION_STRING, ccsid); - * cnv=ucnv_open(name, &errorCode); - * \endcode - * - * In order to open a converter from a Microsoft codepage number, use the prefix "cp": - * \code - * char name[20]; - * sprintf(name, "cp%hu", codepageID); - * cnv=ucnv_open(name, &errorCode); - * \endcode - * - * If the alias is ambiguous, then the preferred converter is used - * and the status is set to U_AMBIGUOUS_ALIAS_WARNING. - * - * @param codepage codepage number to create - * @param platform the platform in which the codepage number exists - * @param err error status U_MEMORY_ALLOCATION_ERROR, U_FILE_ACCESS_ERROR - * @return the created Unicode converter object, or NULL if an error - * occurred. - * @see ucnv_open - * @see ucnv_openU - * @see ucnv_close - * @see ucnv_getCCSID - * @see ucnv_getPlatform - * @see UConverterPlatform - * @stable ICU 2.0 - */ -U_CAPI UConverter* U_EXPORT2 -ucnv_openCCSID(int32_t codepage, - UConverterPlatform platform, - UErrorCode * err); - -/** - *

Creates a UConverter object specified from a packageName and a converterName.

- * - *

The packageName and converterName must point to an ICU udata object, as defined by - * udata_open( packageName, "cnv", converterName, err) or equivalent. - * Typically, packageName will refer to a (.dat) file, or to a package registered with - * udata_setAppData(). Using a full file or directory pathname for packageName is deprecated.

- * - *

The name will NOT be looked up in the alias mechanism, nor will the converter be - * stored in the converter cache or the alias table. The only way to open further converters - * is call this function multiple times, or use the ucnv_clone() function to clone a - * 'primary' converter.

- * - *

A future version of ICU may add alias table lookups and/or caching - * to this function.

- * - *

Example Use: - * cnv = ucnv_openPackage("myapp", "myconverter", &err); - *

- * - * @param packageName name of the package (equivalent to 'path' in udata_open() call) - * @param converterName name of the data item to be used, without suffix. - * @param err outgoing error status U_MEMORY_ALLOCATION_ERROR, U_FILE_ACCESS_ERROR - * @return the created Unicode converter object, or NULL if an error occurred - * @see udata_open - * @see ucnv_open - * @see ucnv_clone - * @see ucnv_close - * @stable ICU 2.2 - */ -U_CAPI UConverter* U_EXPORT2 -ucnv_openPackage(const char *packageName, const char *converterName, UErrorCode *err); - -/** - * Thread safe converter cloning operation. - * - * You must ucnv_close() the clone. - * - * @param cnv converter to be cloned - * @param status to indicate whether the operation went on smoothly or there were errors - * @return pointer to the new clone - * @stable ICU 71 - */ -U_CAPI UConverter* U_EXPORT2 ucnv_clone(const UConverter *cnv, UErrorCode *status); - -#ifndef U_HIDE_DEPRECATED_API - -/** - * Thread safe converter cloning operation. - * For most efficient operation, pass in a stackBuffer (and a *pBufferSize) - * with at least U_CNV_SAFECLONE_BUFFERSIZE bytes of space. - * If the buffer size is sufficient, then the clone will use the stack buffer; - * otherwise, it will be allocated, and *pBufferSize will indicate - * the actual size. (This should not occur with U_CNV_SAFECLONE_BUFFERSIZE.) - * - * You must ucnv_close() the clone in any case. - * - * If *pBufferSize==0, (regardless of whether stackBuffer==NULL or not) - * then *pBufferSize will be changed to a sufficient size - * for cloning this converter, - * without actually cloning the converter ("pure pre-flighting"). - * - * If *pBufferSize is greater than zero but not large enough for a stack-based - * clone, then the converter is cloned using newly allocated memory - * and *pBufferSize is changed to the necessary size. - * - * If the converter clone fits into the stack buffer but the stack buffer is not - * sufficiently aligned for the clone, then the clone will use an - * adjusted pointer and use an accordingly smaller buffer size. - * - * @param cnv converter to be cloned - * @param stackBuffer Deprecated functionality as of ICU 52, use NULL.
- * user allocated space for the new clone. If NULL new memory will be allocated. - * If buffer is not large enough, new memory will be allocated. - * Clients can use the U_CNV_SAFECLONE_BUFFERSIZE. This will probably be enough to avoid memory allocations. - * @param pBufferSize Deprecated functionality as of ICU 52, use NULL or 1.
- * pointer to size of allocated space. - * @param status to indicate whether the operation went on smoothly or there were errors - * An informational status value, U_SAFECLONE_ALLOCATED_WARNING, - * is used if pBufferSize != NULL and any allocations were necessary - * However, it is better to check if *pBufferSize grew for checking for - * allocations because warning codes can be overridden by subsequent - * function calls. - * @return pointer to the new clone - * @deprecated ICU 71 Use ucnv_clone() instead. - */ -U_DEPRECATED UConverter * U_EXPORT2 -ucnv_safeClone(const UConverter *cnv, - void *stackBuffer, - int32_t *pBufferSize, - UErrorCode *status); - -/** - * \def U_CNV_SAFECLONE_BUFFERSIZE - * Definition of a buffer size that is designed to be large enough for - * converters to be cloned with ucnv_safeClone(). - * @deprecated ICU 52. Do not rely on ucnv_safeClone() cloning into any provided buffer. - */ -#define U_CNV_SAFECLONE_BUFFERSIZE 1024 - -#endif /* U_HIDE_DEPRECATED_API */ - -/** - * Deletes the unicode converter and releases resources associated - * with just this instance. - * Does not free up shared converter tables. - * - * @param converter the converter object to be deleted - * @see ucnv_open - * @see ucnv_openU - * @see ucnv_openCCSID - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_close(UConverter * converter); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUConverterPointer - * "Smart pointer" class, closes a UConverter via ucnv_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUConverterPointer, UConverter, ucnv_close); - -U_NAMESPACE_END - -#endif - -/** - * Fills in the output parameter, subChars, with the substitution characters - * as multiple bytes. - * If ucnv_setSubstString() set a Unicode string because the converter is - * stateful, then subChars will be an empty string. - * - * @param converter the Unicode converter - * @param subChars the substitution characters - * @param len on input the capacity of subChars, on output the number - * of bytes copied to it - * @param err the outgoing error status code. - * If the substitution character array is too small, an - * U_INDEX_OUTOFBOUNDS_ERROR will be returned. - * @see ucnv_setSubstString - * @see ucnv_setSubstChars - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_getSubstChars(const UConverter *converter, - char *subChars, - int8_t *len, - UErrorCode *err); - -/** - * Sets the substitution chars when converting from unicode to a codepage. The - * substitution is specified as a string of 1-4 bytes, and may contain - * NULL bytes. - * The subChars must represent a single character. The caller needs to know the - * byte sequence of a valid character in the converter's charset. - * For some converters, for example some ISO 2022 variants, only single-byte - * substitution characters may be supported. - * The newer ucnv_setSubstString() function relaxes these limitations. - * - * @param converter the Unicode converter - * @param subChars the substitution character byte sequence we want set - * @param len the number of bytes in subChars - * @param err the error status code. U_INDEX_OUTOFBOUNDS_ERROR if - * len is bigger than the maximum number of bytes allowed in subchars - * @see ucnv_setSubstString - * @see ucnv_getSubstChars - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_setSubstChars(UConverter *converter, - const char *subChars, - int8_t len, - UErrorCode *err); - -/** - * Set a substitution string for converting from Unicode to a charset. - * The caller need not know the charset byte sequence for each charset. - * - * Unlike ucnv_setSubstChars() which is designed to set a charset byte sequence - * for a single character, this function takes a Unicode string with - * zero, one or more characters, and immediately verifies that the string can be - * converted to the charset. - * If not, or if the result is too long (more than 32 bytes as of ICU 3.6), - * then the function returns with an error accordingly. - * - * Also unlike ucnv_setSubstChars(), this function works for stateful charsets - * by converting on the fly at the point of substitution rather than setting - * a fixed byte sequence. - * - * @param cnv The UConverter object. - * @param s The Unicode string. - * @param length The number of UChars in s, or -1 for a NUL-terminated string. - * @param err Pointer to a standard 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.) - * - * @see ucnv_setSubstChars - * @see ucnv_getSubstChars - * @stable ICU 3.6 - */ -U_CAPI void U_EXPORT2 -ucnv_setSubstString(UConverter *cnv, - const UChar *s, - int32_t length, - UErrorCode *err); - -/** - * Fills in the output parameter, errBytes, with the error characters from the - * last failing conversion. - * - * @param converter the Unicode converter - * @param errBytes the codepage bytes which were in error - * @param len on input the capacity of errBytes, on output the number of - * bytes which were copied to it - * @param err the error status code. - * If the substitution character array is too small, an - * U_INDEX_OUTOFBOUNDS_ERROR will be returned. - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_getInvalidChars(const UConverter *converter, - char *errBytes, - int8_t *len, - UErrorCode *err); - -/** - * Fills in the output parameter, errChars, with the error characters from the - * last failing conversion. - * - * @param converter the Unicode converter - * @param errUChars the UChars which were in error - * @param len on input the capacity of errUChars, on output the number of - * UChars which were copied to it - * @param err the error status code. - * If the substitution character array is too small, an - * U_INDEX_OUTOFBOUNDS_ERROR will be returned. - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_getInvalidUChars(const UConverter *converter, - UChar *errUChars, - int8_t *len, - UErrorCode *err); - -/** - * Resets the state of a converter to the default state. This is used - * in the case of an error, to restart a conversion from a known default state. - * It will also empty the internal output buffers. - * @param converter the Unicode converter - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_reset(UConverter *converter); - -/** - * Resets the to-Unicode part of a converter state to the default state. - * This is used in the case of an error to restart a conversion to - * Unicode to a known default state. It will also empty the internal - * output buffers used for the conversion to Unicode codepoints. - * @param converter the Unicode converter - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_resetToUnicode(UConverter *converter); - -/** - * Resets the from-Unicode part of a converter state to the default state. - * This is used in the case of an error to restart a conversion from - * Unicode to a known default state. It will also empty the internal output - * buffers used for the conversion from Unicode codepoints. - * @param converter the Unicode converter - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_resetFromUnicode(UConverter *converter); - -/** - * Returns the maximum number of bytes that are output per UChar in conversion - * from Unicode using this converter. - * The returned number can be used with UCNV_GET_MAX_BYTES_FOR_STRING - * to calculate the size of a target buffer for conversion from Unicode. - * - * Note: Before ICU 2.8, this function did not return reliable numbers for - * some stateful converters (EBCDIC_STATEFUL, ISO-2022) and LMBCS. - * - * This number may not be the same as the maximum number of bytes per - * "conversion unit". In other words, it may not be the intuitively expected - * number of bytes per character that would be published for a charset, - * and may not fulfill any other purpose than the allocation of an output - * buffer of guaranteed sufficient size for a given input length and converter. - * - * Examples for special cases that are taken into account: - * - Supplementary code points may convert to more bytes than BMP code points. - * This function returns bytes per UChar (UTF-16 code unit), not per - * Unicode code point, for efficient buffer allocation. - * - State-shifting output (SI/SO, escapes, etc.) from stateful converters. - * - When m input UChars are converted to n output bytes, then the maximum m/n - * is taken into account. - * - * The number returned here does not take into account - * (see UCNV_GET_MAX_BYTES_FOR_STRING): - * - callbacks which output more than one charset character sequence per call, - * like escape callbacks - * - initial and final non-character bytes that are output by some converters - * (automatic BOMs, initial escape sequence, final SI, etc.) - * - * Examples for returned values: - * - SBCS charsets: 1 - * - Shift-JIS: 2 - * - UTF-16: 2 (2 per BMP, 4 per surrogate _pair_, BOM not counted) - * - UTF-8: 3 (3 per BMP, 4 per surrogate _pair_) - * - EBCDIC_STATEFUL (EBCDIC mixed SBCS/DBCS): 3 (SO + DBCS) - * - ISO-2022: 3 (always outputs UTF-8) - * - ISO-2022-JP: 6 (4-byte escape sequences + DBCS) - * - ISO-2022-CN: 8 (4-byte designator sequences + 2-byte SS2/SS3 + DBCS) - * - * @param converter The Unicode converter. - * @return The maximum number of bytes per UChar (16 bit code unit) - * that are output by ucnv_fromUnicode(), - * to be used together with UCNV_GET_MAX_BYTES_FOR_STRING - * for buffer allocation. - * - * @see UCNV_GET_MAX_BYTES_FOR_STRING - * @see ucnv_getMinCharSize - * @stable ICU 2.0 - */ -U_CAPI int8_t U_EXPORT2 -ucnv_getMaxCharSize(const UConverter *converter); - -/** - * Calculates the size of a buffer for conversion from Unicode to a charset. - * The calculated size is guaranteed to be sufficient for this conversion. - * - * It takes into account initial and final non-character bytes that are output - * by some converters. - * It does not take into account callbacks which output more than one charset - * character sequence per call, like escape callbacks. - * The default (substitution) callback only outputs one charset character sequence. - * - * @param length Number of UChars to be converted. - * @param maxCharSize Return value from ucnv_getMaxCharSize() for the converter - * that will be used. - * @return Size of a buffer that will be large enough to hold the output bytes of - * converting length UChars with the converter that returned the maxCharSize. - * - * @see ucnv_getMaxCharSize - * @stable ICU 2.8 - */ -#define UCNV_GET_MAX_BYTES_FOR_STRING(length, maxCharSize) \ - (((int32_t)(length)+10)*(int32_t)(maxCharSize)) - -/** - * Returns the minimum byte length (per codepoint) for characters in this codepage. - * This is usually either 1 or 2. - * @param converter the Unicode converter - * @return the minimum number of bytes per codepoint allowed by this particular converter - * @see ucnv_getMaxCharSize - * @stable ICU 2.0 - */ -U_CAPI int8_t U_EXPORT2 -ucnv_getMinCharSize(const UConverter *converter); - -/** - * Returns the display name of the converter passed in based on the Locale - * passed in. If the locale contains no display name, the internal ASCII - * name will be filled in. - * - * @param converter the Unicode converter. - * @param displayLocale is the specific Locale we want to localized for - * @param displayName user provided buffer to be filled in - * @param displayNameCapacity size of displayName Buffer - * @param err error status code - * @return displayNameLength number of UChar needed in displayName - * @see ucnv_getName - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ucnv_getDisplayName(const UConverter *converter, - const char *displayLocale, - UChar *displayName, - int32_t displayNameCapacity, - UErrorCode *err); - -/** - * Gets the internal, canonical name of the converter (zero-terminated). - * The lifetime of the returned string will be that of the converter - * passed to this function. - * @param converter the Unicode converter - * @param err UErrorCode status - * @return the internal name of the converter - * @see ucnv_getDisplayName - * @stable ICU 2.0 - */ -U_CAPI const char * U_EXPORT2 -ucnv_getName(const UConverter *converter, UErrorCode *err); - -/** - * Gets a codepage number associated with the converter. This is not guaranteed - * to be the one used to create the converter. Some converters do not represent - * platform registered codepages and return zero for the codepage number. - * The error code fill-in parameter indicates if the codepage number - * is available. - * Does not check if the converter is NULL or if converter's data - * table is NULL. - * - * Important: The use of CCSIDs is not recommended because it is limited - * to only two platforms in principle and only one (UCNV_IBM) in the current - * ICU converter API. - * Also, CCSIDs are insufficient to identify IBM Unicode conversion tables precisely. - * For more details see ucnv_openCCSID(). - * - * @param converter the Unicode converter - * @param err the error status code. - * @return If any error occurs, -1 will be returned otherwise, the codepage number - * will be returned - * @see ucnv_openCCSID - * @see ucnv_getPlatform - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ucnv_getCCSID(const UConverter *converter, - UErrorCode *err); - -/** - * Gets a codepage platform associated with the converter. Currently, - * only UCNV_IBM will be returned. - * Does not test if the converter is NULL or if converter's data - * table is NULL. - * @param converter the Unicode converter - * @param err the error status code. - * @return The codepage platform - * @stable ICU 2.0 - */ -U_CAPI UConverterPlatform U_EXPORT2 -ucnv_getPlatform(const UConverter *converter, - UErrorCode *err); - -/** - * Gets the type of the converter - * e.g. SBCS, MBCS, DBCS, UTF8, UTF16_BE, UTF16_LE, ISO_2022, - * EBCDIC_STATEFUL, LATIN_1 - * @param converter a valid, opened converter - * @return the type of the converter - * @stable ICU 2.0 - */ -U_CAPI UConverterType U_EXPORT2 -ucnv_getType(const UConverter * converter); - -/** - * Gets the "starter" (lead) bytes for converters of type MBCS. - * Will fill in an U_ILLEGAL_ARGUMENT_ERROR if converter passed in - * is not MBCS. Fills in an array of type UBool, with the value of the byte - * as offset to the array. For example, if (starters[0x20] == true) at return, - * it means that the byte 0x20 is a starter byte in this converter. - * Context pointers are always owned by the caller. - * - * @param converter a valid, opened converter of type MBCS - * @param starters an array of size 256 to be filled in - * @param err error status, U_ILLEGAL_ARGUMENT_ERROR if the - * converter is not a type which can return starters. - * @see ucnv_getType - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_getStarters(const UConverter* converter, - UBool starters[256], - UErrorCode* err); - - -/** - * Selectors for Unicode sets that can be returned by ucnv_getUnicodeSet(). - * @see ucnv_getUnicodeSet - * @stable ICU 2.6 - */ -typedef enum UConverterUnicodeSet { - /** Select the set of roundtrippable Unicode code points. @stable ICU 2.6 */ - UCNV_ROUNDTRIP_SET, - /** Select the set of Unicode code points with roundtrip or fallback mappings. @stable ICU 4.0 */ - UCNV_ROUNDTRIP_AND_FALLBACK_SET, -#ifndef U_HIDE_DEPRECATED_API - /** - * Number of UConverterUnicodeSet selectors. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UCNV_SET_COUNT -#endif // U_HIDE_DEPRECATED_API -} UConverterUnicodeSet; - - -/** - * Returns the set of Unicode code points that can be converted by an ICU converter. - * - * Returns one of several kinds of set: - * - * 1. UCNV_ROUNDTRIP_SET - * - * The set of all Unicode code points that can be roundtrip-converted - * (converted without any data loss) with the converter (ucnv_fromUnicode()). - * This set will not include code points that have fallback mappings - * or are only the result of reverse fallback mappings. - * This set will also not include PUA code points with fallbacks, although - * ucnv_fromUnicode() will always uses those mappings despite ucnv_setFallback(). - * See UTR #22 "Character Mapping Markup Language" - * at http://www.unicode.org/reports/tr22/ - * - * This is useful for example for - * - checking that a string or document can be roundtrip-converted with a converter, - * without/before actually performing the conversion - * - testing if a converter can be used for text for typical text for a certain locale, - * by comparing its roundtrip set with the set of ExemplarCharacters from - * ICU's locale data or other sources - * - * 2. UCNV_ROUNDTRIP_AND_FALLBACK_SET - * - * The set of all Unicode code points that can be converted with the converter (ucnv_fromUnicode()) - * when fallbacks are turned on (see ucnv_setFallback()). - * This set includes all code points with roundtrips and fallbacks (but not reverse fallbacks). - * - * In the future, there may be more UConverterUnicodeSet choices to select - * sets with different properties. - * - * @param cnv The converter for which a set is requested. - * @param setFillIn A valid USet *. It will be cleared by this function before - * the converter's specific set is filled into the USet. - * @param whichSet A UConverterUnicodeSet selector; - * currently UCNV_ROUNDTRIP_SET is the only supported value. - * @param pErrorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * - * @see UConverterUnicodeSet - * @see uset_open - * @see uset_close - * @stable ICU 2.6 - */ -U_CAPI void U_EXPORT2 -ucnv_getUnicodeSet(const UConverter *cnv, - USet *setFillIn, - UConverterUnicodeSet whichSet, - UErrorCode *pErrorCode); - -/** - * Gets the current callback function used by the converter when an illegal - * or invalid codepage sequence is found. - * Context pointers are always owned by the caller. - * - * @param converter the unicode converter - * @param action fillin: returns the callback function pointer - * @param context fillin: returns the callback's private void* context - * @see ucnv_setToUCallBack - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_getToUCallBack (const UConverter * converter, - UConverterToUCallback *action, - const void **context); - -/** - * Gets the current callback function used by the converter when illegal - * or invalid Unicode sequence is found. - * Context pointers are always owned by the caller. - * - * @param converter the unicode converter - * @param action fillin: returns the callback function pointer - * @param context fillin: returns the callback's private void* context - * @see ucnv_setFromUCallBack - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_getFromUCallBack (const UConverter * converter, - UConverterFromUCallback *action, - const void **context); - -/** - * Changes the callback function used by the converter when - * an illegal or invalid sequence is found. - * Context pointers are always owned by the caller. - * Predefined actions and contexts can be found in the ucnv_err.h header. - * - * @param converter the unicode converter - * @param newAction the new callback function - * @param newContext the new toUnicode callback context pointer. This can be NULL. - * @param oldAction fillin: returns the old callback function pointer. This can be NULL. - * @param oldContext fillin: returns the old callback's private void* context. This can be NULL. - * @param err The error code status - * @see ucnv_getToUCallBack - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_setToUCallBack (UConverter * converter, - UConverterToUCallback newAction, - const void* newContext, - UConverterToUCallback *oldAction, - const void** oldContext, - UErrorCode * err); - -/** - * Changes the current callback function used by the converter when - * an illegal or invalid sequence is found. - * Context pointers are always owned by the caller. - * Predefined actions and contexts can be found in the ucnv_err.h header. - * - * @param converter the unicode converter - * @param newAction the new callback function - * @param newContext the new fromUnicode callback context pointer. This can be NULL. - * @param oldAction fillin: returns the old callback function pointer. This can be NULL. - * @param oldContext fillin: returns the old callback's private void* context. This can be NULL. - * @param err The error code status - * @see ucnv_getFromUCallBack - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_setFromUCallBack (UConverter * converter, - UConverterFromUCallback newAction, - const void *newContext, - UConverterFromUCallback *oldAction, - const void **oldContext, - UErrorCode * err); - -/** - * Converts an array of unicode characters to an array of codepage - * characters. This function is optimized for converting a continuous - * stream of data in buffer-sized chunks, where the entire source and - * target does not fit in available buffers. - * - * The source pointer is an in/out parameter. It starts out pointing where the - * conversion is to begin, and ends up pointing after the last UChar consumed. - * - * Target similarly starts out pointer at the first available byte in the output - * buffer, and ends up pointing after the last byte written to the output. - * - * The converter always attempts to consume the entire source buffer, unless - * (1.) the target buffer is full, or (2.) a failing error is returned from the - * current callback function. When a successful error status has been - * returned, it means that all of the source buffer has been - * consumed. At that point, the caller should reset the source and - * sourceLimit pointers to point to the next chunk. - * - * At the end of the stream (flush==true), the input is completely consumed - * when *source==sourceLimit and no error code is set. - * The converter object is then automatically reset by this function. - * (This means that a converter need not be reset explicitly between data - * streams if it finishes the previous stream without errors.) - * - * This is a stateful conversion. Additionally, even when all source data has - * been consumed, some data may be in the converters' internal state. - * Call this function repeatedly, updating the target pointers with - * the next empty chunk of target in case of a - * U_BUFFER_OVERFLOW_ERROR, and updating the source pointers - * with the next chunk of source when a successful error status is - * returned, until there are no more chunks of source data. - * @param converter the Unicode converter - * @param target I/O parameter. Input : Points to the beginning of the buffer to copy - * codepage characters to. Output : points to after the last codepage character copied - * to target. - * @param targetLimit the pointer just after last of the target buffer - * @param source I/O parameter, pointer to pointer to the source Unicode character buffer. - * @param sourceLimit the pointer just after the last of the source buffer - * @param offsets if NULL is passed, nothing will happen to it, otherwise it needs to have the same number - * of allocated cells as target. Will fill in offsets from target to source pointer - * e.g: offsets[3] is equal to 6, it means that the target[3] was a result of transcoding source[6] - * For output data carried across calls, and other data without a specific source character - * (such as from escape sequences or callbacks) -1 will be placed for offsets. - * @param flush set to true if the current source buffer is the last available - * chunk of the source, false otherwise. Note that if a failing status is returned, - * this function may have to be called multiple times with flush set to true until - * the source buffer is consumed. - * @param err the error status. U_ILLEGAL_ARGUMENT_ERROR will be set if the - * converter is NULL. - * U_BUFFER_OVERFLOW_ERROR will be set if the target is full and there is - * still data to be written to the target. - * @see ucnv_fromUChars - * @see ucnv_convert - * @see ucnv_getMinCharSize - * @see ucnv_setToUCallBack - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_fromUnicode (UConverter * converter, - char **target, - const char *targetLimit, - const UChar ** source, - const UChar * sourceLimit, - int32_t* offsets, - UBool flush, - UErrorCode * err); - -/** - * Converts a buffer of codepage bytes into an array of unicode UChars - * characters. This function is optimized for converting a continuous - * stream of data in buffer-sized chunks, where the entire source and - * target does not fit in available buffers. - * - * The source pointer is an in/out parameter. It starts out pointing where the - * conversion is to begin, and ends up pointing after the last byte of source consumed. - * - * Target similarly starts out pointer at the first available UChar in the output - * buffer, and ends up pointing after the last UChar written to the output. - * It does NOT necessarily keep UChar sequences together. - * - * The converter always attempts to consume the entire source buffer, unless - * (1.) the target buffer is full, or (2.) a failing error is returned from the - * current callback function. When a successful error status has been - * returned, it means that all of the source buffer has been - * consumed. At that point, the caller should reset the source and - * sourceLimit pointers to point to the next chunk. - * - * At the end of the stream (flush==true), the input is completely consumed - * when *source==sourceLimit and no error code is set - * The converter object is then automatically reset by this function. - * (This means that a converter need not be reset explicitly between data - * streams if it finishes the previous stream without errors.) - * - * This is a stateful conversion. Additionally, even when all source data has - * been consumed, some data may be in the converters' internal state. - * Call this function repeatedly, updating the target pointers with - * the next empty chunk of target in case of a - * U_BUFFER_OVERFLOW_ERROR, and updating the source pointers - * with the next chunk of source when a successful error status is - * returned, until there are no more chunks of source data. - * @param converter the Unicode converter - * @param target I/O parameter. Input : Points to the beginning of the buffer to copy - * UChars into. Output : points to after the last UChar copied. - * @param targetLimit the pointer just after the end of the target buffer - * @param source I/O parameter, pointer to pointer to the source codepage buffer. - * @param sourceLimit the pointer to the byte after the end of the source buffer - * @param offsets if NULL is passed, nothing will happen to it, otherwise it needs to have the same number - * of allocated cells as target. Will fill in offsets from target to source pointer - * e.g: offsets[3] is equal to 6, it means that the target[3] was a result of transcoding source[6] - * For output data carried across calls, and other data without a specific source character - * (such as from escape sequences or callbacks) -1 will be placed for offsets. - * @param flush set to true if the current source buffer is the last available - * chunk of the source, false otherwise. Note that if a failing status is returned, - * this function may have to be called multiple times with flush set to true until - * the source buffer is consumed. - * @param err the error status. U_ILLEGAL_ARGUMENT_ERROR will be set if the - * converter is NULL. - * U_BUFFER_OVERFLOW_ERROR will be set if the target is full and there is - * still data to be written to the target. - * @see ucnv_fromUChars - * @see ucnv_convert - * @see ucnv_getMinCharSize - * @see ucnv_setFromUCallBack - * @see ucnv_getNextUChar - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_toUnicode(UConverter *converter, - UChar **target, - const UChar *targetLimit, - const char **source, - const char *sourceLimit, - int32_t *offsets, - UBool flush, - UErrorCode *err); - -/** - * Convert the Unicode string into a codepage string using an existing UConverter. - * The output string is NUL-terminated if possible. - * - * This function is a more convenient but less powerful version of ucnv_fromUnicode(). - * It is only useful for whole strings, not for streaming conversion. - * - * The maximum output buffer capacity required (barring output from callbacks) will be - * UCNV_GET_MAX_BYTES_FOR_STRING(srcLength, ucnv_getMaxCharSize(cnv)). - * - * @param cnv the converter object to be used (ucnv_resetFromUnicode() will be called) - * @param src the input Unicode string - * @param srcLength the input string length, or -1 if NUL-terminated - * @param dest destination string buffer, can be NULL if destCapacity==0 - * @param destCapacity the number of chars available at dest - * @param pErrorCode normal ICU error code; - * common error codes that may be set by this function include - * U_BUFFER_OVERFLOW_ERROR, U_STRING_NOT_TERMINATED_WARNING, - * U_ILLEGAL_ARGUMENT_ERROR, and conversion errors - * @return the length of the output string, not counting the terminating NUL; - * if the length is greater than destCapacity, then the string will not fit - * and a buffer of the indicated length would need to be passed in - * @see ucnv_fromUnicode - * @see ucnv_convert - * @see UCNV_GET_MAX_BYTES_FOR_STRING - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ucnv_fromUChars(UConverter *cnv, - char *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - UErrorCode *pErrorCode); - -/** - * Convert the codepage string into a Unicode string using an existing UConverter. - * The output string is NUL-terminated if possible. - * - * This function is a more convenient but less powerful version of ucnv_toUnicode(). - * It is only useful for whole strings, not for streaming conversion. - * - * The maximum output buffer capacity required (barring output from callbacks) will be - * 2*srcLength (each char may be converted into a surrogate pair). - * - * @param cnv the converter object to be used (ucnv_resetToUnicode() will be called) - * @param src the input codepage string - * @param srcLength the input string length, or -1 if NUL-terminated - * @param dest destination string buffer, can be NULL if destCapacity==0 - * @param destCapacity the number of UChars available at dest - * @param pErrorCode normal ICU error code; - * common error codes that may be set by this function include - * U_BUFFER_OVERFLOW_ERROR, U_STRING_NOT_TERMINATED_WARNING, - * U_ILLEGAL_ARGUMENT_ERROR, and conversion errors - * @return the length of the output string, not counting the terminating NUL; - * if the length is greater than destCapacity, then the string will not fit - * and a buffer of the indicated length would need to be passed in - * @see ucnv_toUnicode - * @see ucnv_convert - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ucnv_toUChars(UConverter *cnv, - UChar *dest, int32_t destCapacity, - const char *src, int32_t srcLength, - UErrorCode *pErrorCode); - -/** - * Convert a codepage buffer into Unicode one character at a time. - * The input is completely consumed when the U_INDEX_OUTOFBOUNDS_ERROR is set. - * - * Advantage compared to ucnv_toUnicode() or ucnv_toUChars(): - * - Faster for small amounts of data, for most converters, e.g., - * US-ASCII, ISO-8859-1, UTF-8/16/32, and most "normal" charsets. - * (For complex converters, e.g., SCSU, UTF-7 and ISO 2022 variants, - * it uses ucnv_toUnicode() internally.) - * - Convenient. - * - * Limitations compared to ucnv_toUnicode(): - * - Always assumes flush=true. - * This makes ucnv_getNextUChar() unsuitable for "streaming" conversion, - * that is, for where the input is supplied in multiple buffers, - * because ucnv_getNextUChar() will assume the end of the input at the end - * of the first buffer. - * - Does not provide offset output. - * - * It is possible to "mix" ucnv_getNextUChar() and ucnv_toUnicode() because - * ucnv_getNextUChar() uses the current state of the converter - * (unlike ucnv_toUChars() which always resets first). - * However, if ucnv_getNextUChar() is called after ucnv_toUnicode() - * stopped in the middle of a character sequence (with flush=false), - * then ucnv_getNextUChar() will always use the slower ucnv_toUnicode() - * internally until the next character boundary. - * (This is new in ICU 2.6. In earlier releases, ucnv_getNextUChar() had to - * start at a character boundary.) - * - * Instead of using ucnv_getNextUChar(), it is recommended - * to convert using ucnv_toUnicode() or ucnv_toUChars() - * and then iterate over the text using U16_NEXT() or a UCharIterator (uiter.h) - * or a C++ CharacterIterator or similar. - * This allows streaming conversion and offset output, for example. - * - *

Handling of surrogate pairs and supplementary-plane code points:
- * There are two different kinds of codepages that provide mappings for surrogate characters: - *

    - *
  • Codepages like UTF-8, UTF-32, and GB 18030 provide direct representations for Unicode - * code points U+10000-U+10ffff as well as for single surrogates U+d800-U+dfff. - * Each valid sequence will result in exactly one returned code point. - * If a sequence results in a single surrogate, then that will be returned - * by itself, even if a neighboring sequence encodes the matching surrogate.
  • - *
  • Codepages like SCSU and LMBCS (and UTF-16) provide direct representations only for BMP code points - * including surrogates. Code points in supplementary planes are represented with - * two sequences, each encoding a surrogate. - * For these codepages, matching pairs of surrogates will be combined into single - * code points for returning from this function. - * (Note that SCSU is actually a mix of these codepage types.)
  • - *

- * - * @param converter an open UConverter - * @param source the address of a pointer to the codepage buffer, will be - * updated to point after the bytes consumed in the conversion call. - * @param sourceLimit points to the end of the input buffer - * @param err fills in error status (see ucnv_toUnicode) - * U_INDEX_OUTOFBOUNDS_ERROR will be set if the input - * is empty or does not convert to any output (e.g.: pure state-change - * codes SI/SO, escape sequences for ISO 2022, - * or if the callback did not output anything, ...). - * This function will not set a U_BUFFER_OVERFLOW_ERROR because - * the "buffer" is the return code. However, there might be subsequent output - * stored in the converter object - * that will be returned in following calls to this function. - * @return a UChar32 resulting from the partial conversion of source - * @see ucnv_toUnicode - * @see ucnv_toUChars - * @see ucnv_convert - * @stable ICU 2.0 - */ -U_CAPI UChar32 U_EXPORT2 -ucnv_getNextUChar(UConverter * converter, - const char **source, - const char * sourceLimit, - UErrorCode * err); - -/** - * Convert from one external charset to another using two existing UConverters. - * Internally, two conversions - ucnv_toUnicode() and ucnv_fromUnicode() - - * are used, "pivoting" through 16-bit Unicode. - * - * Important: For streaming conversion (multiple function calls for successive - * parts of a text stream), the caller must provide a pivot buffer explicitly, - * and must preserve the pivot buffer and associated pointers from one - * call to another. (The buffer may be moved if its contents and the relative - * pointer positions are preserved.) - * - * There is a similar function, ucnv_convert(), - * which has the following limitations: - * - it takes charset names, not converter objects, so that - * - two converters are opened for each call - * - only single-string conversion is possible, not streaming operation - * - it does not provide enough information to find out, - * in case of failure, whether the toUnicode or - * the fromUnicode conversion failed - * - * By contrast, ucnv_convertEx() - * - takes UConverter parameters instead of charset names - * - fully exposes the pivot buffer for streaming conversion and complete error handling - * - * ucnv_convertEx() also provides further convenience: - * - an option to reset the converters at the beginning - * (if reset==true, see parameters; - * also sets *pivotTarget=*pivotSource=pivotStart) - * - allow NUL-terminated input - * (only a single NUL byte, will not work for charsets with multi-byte NULs) - * (if sourceLimit==NULL, see parameters) - * - terminate with a NUL on output - * (only a single NUL byte, not useful for charsets with multi-byte NULs), - * or set U_STRING_NOT_TERMINATED_WARNING if the output exactly fills - * the target buffer - * - the pivot buffer can be provided internally; - * possible only for whole-string conversion, not streaming conversion; - * in this case, the caller will not be able to get details about where an - * error occurred - * (if pivotStart==NULL, see below) - * - * The function returns when one of the following is true: - * - the entire source text has been converted successfully to the target buffer - * - a target buffer overflow occurred (U_BUFFER_OVERFLOW_ERROR) - * - a conversion error occurred - * (other U_FAILURE(), see description of pErrorCode) - * - * Limitation compared to the direct use of - * ucnv_fromUnicode() and ucnv_toUnicode(): - * ucnv_convertEx() does not provide offset information. - * - * Limitation compared to ucnv_fromUChars() and ucnv_toUChars(): - * ucnv_convertEx() does not support preflighting directly. - * - * Sample code for converting a single string from - * one external charset to UTF-8, ignoring the location of errors: - * - * \code - * int32_t - * myToUTF8(UConverter *cnv, - * const char *s, int32_t length, - * char *u8, int32_t capacity, - * UErrorCode *pErrorCode) { - * UConverter *utf8Cnv; - * char *target; - * - * if(U_FAILURE(*pErrorCode)) { - * return 0; - * } - * - * utf8Cnv=myGetCachedUTF8Converter(pErrorCode); - * if(U_FAILURE(*pErrorCode)) { - * return 0; - * } - * - * if(length<0) { - * length=strlen(s); - * } - * target=u8; - * ucnv_convertEx(utf8Cnv, cnv, - * &target, u8+capacity, - * &s, s+length, - * NULL, NULL, NULL, NULL, - * true, true, - * pErrorCode); - * - * myReleaseCachedUTF8Converter(utf8Cnv); - * - * // return the output string length, but without preflighting - * return (int32_t)(target-u8); - * } - * \endcode - * - * @param targetCnv Output converter, used to convert from the UTF-16 pivot - * to the target using ucnv_fromUnicode(). - * @param sourceCnv Input converter, used to convert from the source to - * the UTF-16 pivot using ucnv_toUnicode(). - * @param target I/O parameter, same as for ucnv_fromUChars(). - * Input: *target points to the beginning of the target buffer. - * Output: *target points to the first unit after the last char written. - * @param targetLimit Pointer to the first unit after the target buffer. - * @param source I/O parameter, same as for ucnv_toUChars(). - * Input: *source points to the beginning of the source buffer. - * Output: *source points to the first unit after the last char read. - * @param sourceLimit Pointer to the first unit after the source buffer. - * @param pivotStart Pointer to the UTF-16 pivot buffer. If pivotStart==NULL, - * then an internal buffer is used and the other pivot - * arguments are ignored and can be NULL as well. - * @param pivotSource I/O parameter, same as source in ucnv_fromUChars() for - * conversion from the pivot buffer to the target buffer. - * @param pivotTarget I/O parameter, same as target in ucnv_toUChars() for - * conversion from the source buffer to the pivot buffer. - * It must be pivotStart<=*pivotSource<=*pivotTarget<=pivotLimit - * and pivotStart[0..ucnv_countAvailable()]) - * @return a pointer a string (library owned), or NULL if the index is out of bounds. - * @see ucnv_countAvailable - * @stable ICU 2.0 - */ -U_CAPI const char* U_EXPORT2 -ucnv_getAvailableName(int32_t n); - -/** - * Returns a UEnumeration to enumerate all of the canonical converter - * names, as per the alias file, regardless of the ability to open each - * converter. - * - * @return A UEnumeration object for getting all the recognized canonical - * converter names. - * @see ucnv_getAvailableName - * @see uenum_close - * @see uenum_next - * @stable ICU 2.4 - */ -U_CAPI UEnumeration * U_EXPORT2 -ucnv_openAllNames(UErrorCode *pErrorCode); - -/** - * Gives the number of aliases for a given converter or alias name. - * If the alias is ambiguous, then the preferred converter is used - * and the status is set to U_AMBIGUOUS_ALIAS_WARNING. - * This method only enumerates the listed entries in the alias file. - * @param alias alias name - * @param pErrorCode error status - * @return number of names on alias list for given alias - * @stable ICU 2.0 - */ -U_CAPI uint16_t U_EXPORT2 -ucnv_countAliases(const char *alias, UErrorCode *pErrorCode); - -/** - * Gives the name of the alias at given index of alias list. - * This method only enumerates the listed entries in the alias file. - * If the alias is ambiguous, then the preferred converter is used - * and the status is set to U_AMBIGUOUS_ALIAS_WARNING. - * @param alias alias name - * @param n index in alias list - * @param pErrorCode result of operation - * @return returns the name of the alias at given index - * @see ucnv_countAliases - * @stable ICU 2.0 - */ -U_CAPI const char * U_EXPORT2 -ucnv_getAlias(const char *alias, uint16_t n, UErrorCode *pErrorCode); - -/** - * Fill-up the list of alias names for the given alias. - * This method only enumerates the listed entries in the alias file. - * If the alias is ambiguous, then the preferred converter is used - * and the status is set to U_AMBIGUOUS_ALIAS_WARNING. - * @param alias alias name - * @param aliases fill-in list, aliases is a pointer to an array of - * ucnv_countAliases() string-pointers - * (const char *) that will be filled in. - * The strings themselves are owned by the library. - * @param pErrorCode result of operation - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_getAliases(const char *alias, const char **aliases, UErrorCode *pErrorCode); - -/** - * Return a new UEnumeration object for enumerating all the - * alias names for a given converter that are recognized by a standard. - * This method only enumerates the listed entries in the alias file. - * The convrtrs.txt file can be modified to change the results of - * this function. - * The first result in this list is the same result given by - * ucnv_getStandardName, which is the default alias for - * the specified standard name. The returned object must be closed with - * uenum_close when you are done with the object. - * - * @param convName original converter name - * @param standard name of the standard governing the names; MIME and IANA - * are such standards - * @param pErrorCode The error code - * @return A UEnumeration object for getting all aliases that are recognized - * by a standard. If any of the parameters are invalid, NULL - * is returned. - * @see ucnv_getStandardName - * @see uenum_close - * @see uenum_next - * @stable ICU 2.2 - */ -U_CAPI UEnumeration * U_EXPORT2 -ucnv_openStandardNames(const char *convName, - const char *standard, - UErrorCode *pErrorCode); - -/** - * Gives the number of standards associated to converter names. - * @return number of standards - * @stable ICU 2.0 - */ -U_CAPI uint16_t U_EXPORT2 -ucnv_countStandards(void); - -/** - * Gives the name of the standard at given index of standard list. - * @param n index in standard list - * @param pErrorCode result of operation - * @return returns the name of the standard at given index. Owned by the library. - * @stable ICU 2.0 - */ -U_CAPI const char * U_EXPORT2 -ucnv_getStandard(uint16_t n, UErrorCode *pErrorCode); - -/** - * Returns a standard name for a given converter name. - *

- * Example alias table:
- * conv alias1 { STANDARD1 } alias2 { STANDARD1* } - *

- * Result of ucnv_getStandardName("conv", "STANDARD1") from example - * alias table:
- * "alias2" - * - * @param name original converter name - * @param standard name of the standard governing the names; MIME and IANA - * are such standards - * @param pErrorCode result of operation - * @return returns the standard converter name; - * if a standard converter name cannot be determined, - * then NULL is returned. Owned by the library. - * @stable ICU 2.0 - */ -U_CAPI const char * U_EXPORT2 -ucnv_getStandardName(const char *name, const char *standard, UErrorCode *pErrorCode); - -/** - * This function will return the internal canonical converter name of the - * tagged alias. This is the opposite of ucnv_openStandardNames, which - * returns the tagged alias given the canonical name. - *

- * Example alias table:
- * conv alias1 { STANDARD1 } alias2 { STANDARD1* } - *

- * Result of ucnv_getStandardName("alias1", "STANDARD1") from example - * alias table:
- * "conv" - * - * @return returns the canonical converter name; - * if a standard or alias name cannot be determined, - * then NULL is returned. The returned string is - * owned by the library. - * @see ucnv_getStandardName - * @stable ICU 2.4 - */ -U_CAPI const char * U_EXPORT2 -ucnv_getCanonicalName(const char *alias, const char *standard, UErrorCode *pErrorCode); - -/** - * Returns the current default converter name. If you want to open - * a default converter, you do not need to use this function. - * It is faster if you pass a NULL argument to ucnv_open the - * default converter. - * - * If U_CHARSET_IS_UTF8 is defined to 1 in utypes.h then this function - * always returns "UTF-8". - * - * @return returns the current default converter name. - * Storage owned by the library - * @see ucnv_setDefaultName - * @stable ICU 2.0 - */ -U_CAPI const char * U_EXPORT2 -ucnv_getDefaultName(void); - -#ifndef U_HIDE_SYSTEM_API -/** - * This function is not thread safe. DO NOT call this function when ANY ICU - * function is being used from more than one thread! This function sets the - * current default converter name. If this function needs to be called, it - * should be called during application initialization. Most of the time, the - * results from ucnv_getDefaultName() or ucnv_open with a NULL string argument - * is sufficient for your application. - * - * If U_CHARSET_IS_UTF8 is defined to 1 in utypes.h then this function - * does nothing. - * - * @param name the converter name to be the default (must be known by ICU). - * @see ucnv_getDefaultName - * @system - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_setDefaultName(const char *name); -#endif /* U_HIDE_SYSTEM_API */ - -/** - * Fixes the backslash character mismapping. For example, in SJIS, the backslash - * character in the ASCII portion is also used to represent the yen currency sign. - * When mapping from Unicode character 0x005C, it's unclear whether to map the - * character back to yen or backslash in SJIS. This function will take the input - * buffer and replace all the yen sign characters with backslash. This is necessary - * when the user tries to open a file with the input buffer on Windows. - * This function will test the converter to see whether such mapping is - * required. You can sometimes avoid using this function by using the correct version - * of Shift-JIS. - * - * @param cnv The converter representing the target codepage. - * @param source the input buffer to be fixed - * @param sourceLen the length of the input buffer - * @see ucnv_isAmbiguous - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_fixFileSeparator(const UConverter *cnv, UChar *source, int32_t sourceLen); - -/** - * Determines if the converter contains ambiguous mappings of the same - * character or not. - * @param cnv the converter to be tested - * @return true if the converter contains ambiguous mapping of the same - * character, false otherwise. - * @stable ICU 2.0 - */ -U_CAPI UBool U_EXPORT2 -ucnv_isAmbiguous(const UConverter *cnv); - -/** - * Sets the converter to use fallback mappings or not. - * Regardless of this flag, the converter will always use - * fallbacks from Unicode Private Use code points, as well as - * reverse fallbacks (to Unicode). - * For details see ".ucm File Format" - * in the Conversion Data chapter of the ICU User Guide: - * https://unicode-org.github.io/icu/userguide/conversion/data.html#ucm-file-format - * - * @param cnv The converter to set the fallback mapping usage on. - * @param usesFallback true if the user wants the converter to take advantage of the fallback - * mapping, false otherwise. - * @stable ICU 2.0 - * @see ucnv_usesFallback - */ -U_CAPI void U_EXPORT2 -ucnv_setFallback(UConverter *cnv, UBool usesFallback); - -/** - * Determines if the converter uses fallback mappings or not. - * This flag has restrictions, see ucnv_setFallback(). - * - * @param cnv The converter to be tested - * @return true if the converter uses fallback, false otherwise. - * @stable ICU 2.0 - * @see ucnv_setFallback - */ -U_CAPI UBool U_EXPORT2 -ucnv_usesFallback(const UConverter *cnv); - -/** - * Detects Unicode signature byte sequences at the start of the byte stream - * and returns the charset name of the indicated Unicode charset. - * NULL is returned when no Unicode signature is recognized. - * The number of bytes in the signature is output as well. - * - * The caller can ucnv_open() a converter using the charset name. - * The first code unit (UChar) from the start of the stream will be U+FEFF - * (the Unicode BOM/signature character) and can usually be ignored. - * - * For most Unicode charsets it is also possible to ignore the indicated - * number of initial stream bytes and start converting after them. - * However, there are stateful Unicode charsets (UTF-7 and BOCU-1) for which - * this will not work. Therefore, it is best to ignore the first output UChar - * instead of the input signature bytes. - *

- * Usage: - * \snippet samples/ucnv/convsamp.cpp ucnv_detectUnicodeSignature - * - * @param source The source string in which the signature should be detected. - * @param sourceLength Length of the input string, or -1 if terminated with a NUL byte. - * @param signatureLength A pointer to int32_t to receive the number of bytes that make up the signature - * of the detected UTF. 0 if not detected. - * Can be a NULL pointer. - * @param pErrorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return The name of the encoding detected. NULL if encoding is not detected. - * @stable ICU 2.4 - */ -U_CAPI const char* U_EXPORT2 -ucnv_detectUnicodeSignature(const char* source, - int32_t sourceLength, - int32_t *signatureLength, - UErrorCode *pErrorCode); - -/** - * Returns the number of UChars held in the converter's internal state - * because more input is needed for completing the conversion. This function is - * useful for mapping semantics of ICU's converter interface to those of iconv, - * and this information is not needed for normal conversion. - * @param cnv The converter in which the input is held - * @param status ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return The number of UChars in the state. -1 if an error is encountered. - * @stable ICU 3.4 - */ -U_CAPI int32_t U_EXPORT2 -ucnv_fromUCountPending(const UConverter* cnv, UErrorCode* status); - -/** - * Returns the number of chars held in the converter's internal state - * because more input is needed for completing the conversion. This function is - * useful for mapping semantics of ICU's converter interface to those of iconv, - * and this information is not needed for normal conversion. - * @param cnv The converter in which the input is held as internal state - * @param status ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return The number of chars in the state. -1 if an error is encountered. - * @stable ICU 3.4 - */ -U_CAPI int32_t U_EXPORT2 -ucnv_toUCountPending(const UConverter* cnv, UErrorCode* status); - -/** - * Returns whether or not the charset of the converter has a fixed number of bytes - * per charset character. - * An example of this are converters that are of the type UCNV_SBCS or UCNV_DBCS. - * Another example is UTF-32 which is always 4 bytes per character. - * A Unicode code point may be represented by more than one UTF-8 or UTF-16 code unit - * but a UTF-32 converter encodes each code point with 4 bytes. - * Note: This method is not intended to be used to determine whether the charset has a - * fixed ratio of bytes to Unicode codes units for any particular Unicode encoding form. - * false is returned with the UErrorCode if error occurs or cnv is NULL. - * @param cnv The converter to be tested - * @param status ICU error code in/out parameter - * @return true if the converter is fixed-width - * @stable ICU 4.8 - */ -U_CAPI UBool U_EXPORT2 -ucnv_isFixedWidth(UConverter *cnv, UErrorCode *status); - -#endif - -#endif -/*_UCNV*/ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** + * ucnv.h: + * External APIs for the ICU's codeset conversion library + * Bertrand A. Damiba + * + * Modification History: + * + * Date Name Description + * 04/04/99 helena Fixed internal header inclusion. + * 05/11/00 helena Added setFallback and usesFallback APIs. + * 06/29/2000 helena Major rewrite of the callback APIs. + * 12/07/2000 srl Update of documentation + */ + +/** + * \file + * \brief C API: Character conversion + * + *

Character Conversion C API

+ * + *

This API is used to convert codepage or character encoded data to and + * from UTF-16. You can open a converter with {@link ucnv_open() }. With that + * converter, you can get its properties, set options, convert your data and + * close the converter.

+ * + *

Since many software programs recognize different converter names for + * different types of converters, there are other functions in this API to + * iterate over the converter aliases. The functions {@link ucnv_getAvailableName() }, + * {@link ucnv_getAlias() } and {@link ucnv_getStandardName() } are some of the + * more frequently used alias functions to get this information.

+ * + *

When a converter encounters an illegal, irregular, invalid or unmappable character + * its default behavior is to use a substitution character to replace the + * bad byte sequence. This behavior can be changed by using {@link ucnv_setFromUCallBack() } + * or {@link ucnv_setToUCallBack() } on the converter. The header ucnv_err.h defines + * many other callback actions that can be used instead of a character substitution.

+ * + *

More information about this API can be found in our + * User Guide.

+ */ + +#ifndef UCNV_H +#define UCNV_H + +#include "unicode/ucnv_err.h" +#include "unicode/uenum.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +#if !defined(USET_DEFINED) && !defined(U_IN_DOXYGEN) + +#define USET_DEFINED + +/** + * USet is the C API type corresponding to C++ class UnicodeSet. + * It is forward-declared here to avoid including unicode/uset.h file if related + * conversion APIs are not used. + * + * @see ucnv_getUnicodeSet + * @stable ICU 2.4 + */ +typedef struct USet USet; + +#endif + +#if !UCONFIG_NO_CONVERSION + +U_CDECL_BEGIN + +/** Maximum length of a converter name including the terminating NULL @stable ICU 2.0 */ +#define UCNV_MAX_CONVERTER_NAME_LENGTH 60 +/** Maximum length of a converter name including path and terminating NULL @stable ICU 2.0 */ +#define UCNV_MAX_FULL_FILE_NAME_LENGTH (600+UCNV_MAX_CONVERTER_NAME_LENGTH) + +/** Shift in for EBDCDIC_STATEFUL and iso2022 states @stable ICU 2.0 */ +#define UCNV_SI 0x0F +/** Shift out for EBDCDIC_STATEFUL and iso2022 states @stable ICU 2.0 */ +#define UCNV_SO 0x0E + +/** + * Enum for specifying basic types of converters + * @see ucnv_getType + * @stable ICU 2.0 + */ +typedef enum { + /** @stable ICU 2.0 */ + UCNV_UNSUPPORTED_CONVERTER = -1, + /** @stable ICU 2.0 */ + UCNV_SBCS = 0, + /** @stable ICU 2.0 */ + UCNV_DBCS = 1, + /** @stable ICU 2.0 */ + UCNV_MBCS = 2, + /** @stable ICU 2.0 */ + UCNV_LATIN_1 = 3, + /** @stable ICU 2.0 */ + UCNV_UTF8 = 4, + /** @stable ICU 2.0 */ + UCNV_UTF16_BigEndian = 5, + /** @stable ICU 2.0 */ + UCNV_UTF16_LittleEndian = 6, + /** @stable ICU 2.0 */ + UCNV_UTF32_BigEndian = 7, + /** @stable ICU 2.0 */ + UCNV_UTF32_LittleEndian = 8, + /** @stable ICU 2.0 */ + UCNV_EBCDIC_STATEFUL = 9, + /** @stable ICU 2.0 */ + UCNV_ISO_2022 = 10, + + /** @stable ICU 2.0 */ + UCNV_LMBCS_1 = 11, + /** @stable ICU 2.0 */ + UCNV_LMBCS_2, + /** @stable ICU 2.0 */ + UCNV_LMBCS_3, + /** @stable ICU 2.0 */ + UCNV_LMBCS_4, + /** @stable ICU 2.0 */ + UCNV_LMBCS_5, + /** @stable ICU 2.0 */ + UCNV_LMBCS_6, + /** @stable ICU 2.0 */ + UCNV_LMBCS_8, + /** @stable ICU 2.0 */ + UCNV_LMBCS_11, + /** @stable ICU 2.0 */ + UCNV_LMBCS_16, + /** @stable ICU 2.0 */ + UCNV_LMBCS_17, + /** @stable ICU 2.0 */ + UCNV_LMBCS_18, + /** @stable ICU 2.0 */ + UCNV_LMBCS_19, + /** @stable ICU 2.0 */ + UCNV_LMBCS_LAST = UCNV_LMBCS_19, + /** @stable ICU 2.0 */ + UCNV_HZ, + /** @stable ICU 2.0 */ + UCNV_SCSU, + /** @stable ICU 2.0 */ + UCNV_ISCII, + /** @stable ICU 2.0 */ + UCNV_US_ASCII, + /** @stable ICU 2.0 */ + UCNV_UTF7, + /** @stable ICU 2.2 */ + UCNV_BOCU1, + /** @stable ICU 2.2 */ + UCNV_UTF16, + /** @stable ICU 2.2 */ + UCNV_UTF32, + /** @stable ICU 2.2 */ + UCNV_CESU8, + /** @stable ICU 2.4 */ + UCNV_IMAP_MAILBOX, + /** @stable ICU 4.8 */ + UCNV_COMPOUND_TEXT, + + /* Number of converter types for which we have conversion routines. */ + UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES +} UConverterType; + +/** + * Enum for specifying which platform a converter ID refers to. + * The use of platform/CCSID is not recommended. See ucnv_openCCSID(). + * + * @see ucnv_getPlatform + * @see ucnv_openCCSID + * @see ucnv_getCCSID + * @stable ICU 2.0 + */ +typedef enum { + UCNV_UNKNOWN = -1, + UCNV_IBM = 0 +} UConverterPlatform; + +/** + * Function pointer for error callback in the codepage to unicode direction. + * Called when an error has occurred in conversion to unicode, or on open/close of the callback (see reason). + * @param context Pointer to the callback's private data + * @param args Information about the conversion in progress + * @param codeUnits Points to 'length' bytes of the concerned codepage sequence + * @param length Size (in bytes) of the concerned codepage sequence + * @param reason Defines the reason the callback was invoked + * @param pErrorCode ICU error code in/out parameter. + * For converter callback functions, set to a conversion error + * before the call, and the callback may reset it to U_ZERO_ERROR. + * @see ucnv_setToUCallBack + * @see UConverterToUnicodeArgs + * @stable ICU 2.0 + */ +typedef void (U_EXPORT2 *UConverterToUCallback) ( + const void* context, + UConverterToUnicodeArgs *args, + const char *codeUnits, + int32_t length, + UConverterCallbackReason reason, + UErrorCode *pErrorCode); + +/** + * Function pointer for error callback in the unicode to codepage direction. + * Called when an error has occurred in conversion from unicode, or on open/close of the callback (see reason). + * @param context Pointer to the callback's private data + * @param args Information about the conversion in progress + * @param codeUnits Points to 'length' UChars of the concerned Unicode sequence + * @param length Size (in bytes) of the concerned codepage sequence + * @param codePoint Single UChar32 (UTF-32) containing the concerend Unicode codepoint. + * @param reason Defines the reason the callback was invoked + * @param pErrorCode ICU error code in/out parameter. + * For converter callback functions, set to a conversion error + * before the call, and the callback may reset it to U_ZERO_ERROR. + * @see ucnv_setFromUCallBack + * @stable ICU 2.0 + */ +typedef void (U_EXPORT2 *UConverterFromUCallback) ( + const void* context, + UConverterFromUnicodeArgs *args, + const UChar* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode *pErrorCode); + +U_CDECL_END + +/** + * Character that separates converter names from options and options from each other. + * @see ucnv_open + * @stable ICU 2.0 + */ +#define UCNV_OPTION_SEP_CHAR ',' + +/** + * String version of UCNV_OPTION_SEP_CHAR. + * @see ucnv_open + * @stable ICU 2.0 + */ +#define UCNV_OPTION_SEP_STRING "," + +/** + * Character that separates a converter option from its value. + * @see ucnv_open + * @stable ICU 2.0 + */ +#define UCNV_VALUE_SEP_CHAR '=' + +/** + * String version of UCNV_VALUE_SEP_CHAR. + * @see ucnv_open + * @stable ICU 2.0 + */ +#define UCNV_VALUE_SEP_STRING "=" + +/** + * Converter option for specifying a locale. + * For example, ucnv_open("SCSU,locale=ja", &errorCode); + * See convrtrs.txt. + * + * @see ucnv_open + * @stable ICU 2.0 + */ +#define UCNV_LOCALE_OPTION_STRING ",locale=" + +/** + * Converter option for specifying a version selector (0..9) for some converters. + * For example, + * \code + * ucnv_open("UTF-7,version=1", &errorCode); + * \endcode + * See convrtrs.txt. + * + * @see ucnv_open + * @stable ICU 2.4 + */ +#define UCNV_VERSION_OPTION_STRING ",version=" + +/** + * Converter option for EBCDIC SBCS or mixed-SBCS/DBCS (stateful) codepages. + * Swaps Unicode mappings for EBCDIC LF and NL codes, as used on + * S/390 (z/OS) Unix System Services (Open Edition). + * For example, ucnv_open("ibm-1047,swaplfnl", &errorCode); + * See convrtrs.txt. + * + * @see ucnv_open + * @stable ICU 2.4 + */ +#define UCNV_SWAP_LFNL_OPTION_STRING ",swaplfnl" + +/** + * Do a fuzzy compare of two converter/alias names. + * The comparison is case-insensitive, ignores leading zeroes if they are not + * followed by further digits, and ignores all but letters and digits. + * Thus the strings "UTF-8", "utf_8", "u*T@f08" and "Utf 8" are exactly equivalent. + * See section 1.4, Charset Alias Matching in Unicode Technical Standard #22 + * at http://www.unicode.org/reports/tr22/ + * + * @param name1 a converter name or alias, zero-terminated + * @param name2 a converter name or alias, zero-terminated + * @return 0 if the names match, or a negative value if the name1 + * lexically precedes name2, or a positive value if the name1 + * lexically follows name2. + * @stable ICU 2.0 + */ +U_CAPI int U_EXPORT2 +ucnv_compareNames(const char *name1, const char *name2); + + +/** + * Creates a UConverter object with the name of a coded character set specified as a C string. + * The actual name will be resolved with the alias file + * using a case-insensitive string comparison that ignores + * leading zeroes and all non-alphanumeric characters. + * E.g., the names "UTF8", "utf-8", "u*T@f08" and "Utf 8" are all equivalent. + * (See also ucnv_compareNames().) + * If NULL is passed for the converter name, it will create one with the + * getDefaultName return value. + * + *

A converter name for ICU 1.5 and above may contain options + * like a locale specification to control the specific behavior of + * the newly instantiated converter. + * The meaning of the options depends on the particular converter. + * If an option is not defined for or recognized by a given converter, then it is ignored.

+ * + *

Options are appended to the converter name string, with a + * UCNV_OPTION_SEP_CHAR between the name and the first option and + * also between adjacent options.

+ * + *

If the alias is ambiguous, then the preferred converter is used + * and the status is set to U_AMBIGUOUS_ALIAS_WARNING.

+ * + *

The conversion behavior and names can vary between platforms. ICU may + * convert some characters differently from other platforms. Details on this topic + * are in the User + * Guide. Aliases starting with a "cp" prefix have no specific meaning + * other than its an alias starting with the letters "cp". Please do not + * associate any meaning to these aliases.

+ * + * \snippet samples/ucnv/convsamp.cpp ucnv_open + * + * @param converterName Name of the coded character set table. + * This may have options appended to the string. + * IANA alias character set names, IBM CCSIDs starting with "ibm-", + * Windows codepage numbers starting with "windows-" are frequently + * used for this parameter. See ucnv_getAvailableName and + * ucnv_getAlias for a complete list that is available. + * If this parameter is NULL, the default converter will be used. + * @param err outgoing error status U_MEMORY_ALLOCATION_ERROR, U_FILE_ACCESS_ERROR + * @return the created Unicode converter object, or NULL if an error occurred + * @see ucnv_openU + * @see ucnv_openCCSID + * @see ucnv_getAvailableName + * @see ucnv_getAlias + * @see ucnv_getDefaultName + * @see ucnv_close + * @see ucnv_compareNames + * @stable ICU 2.0 + */ +U_CAPI UConverter* U_EXPORT2 +ucnv_open(const char *converterName, UErrorCode *err); + + +/** + * Creates a Unicode converter with the names specified as unicode string. + * The name should be limited to the ASCII-7 alphanumerics range. + * The actual name will be resolved with the alias file + * using a case-insensitive string comparison that ignores + * leading zeroes and all non-alphanumeric characters. + * E.g., the names "UTF8", "utf-8", "u*T@f08" and "Utf 8" are all equivalent. + * (See also ucnv_compareNames().) + * If NULL is passed for the converter name, it will create + * one with the ucnv_getDefaultName() return value. + * If the alias is ambiguous, then the preferred converter is used + * and the status is set to U_AMBIGUOUS_ALIAS_WARNING. + * + *

See ucnv_open for the complete details

+ * @param name Name of the UConverter table in a zero terminated + * Unicode string + * @param err outgoing error status U_MEMORY_ALLOCATION_ERROR, + * U_FILE_ACCESS_ERROR + * @return the created Unicode converter object, or NULL if an + * error occurred + * @see ucnv_open + * @see ucnv_openCCSID + * @see ucnv_close + * @see ucnv_compareNames + * @stable ICU 2.0 + */ +U_CAPI UConverter* U_EXPORT2 +ucnv_openU(const UChar *name, + UErrorCode *err); + +/** + * Creates a UConverter object from a CCSID number and platform pair. + * Note that the usefulness of this function is limited to platforms with numeric + * encoding IDs. Only IBM and Microsoft platforms use numeric (16-bit) identifiers for + * encodings. + * + * In addition, IBM CCSIDs and Unicode conversion tables are not 1:1 related. + * For many IBM CCSIDs there are multiple (up to six) Unicode conversion tables, and + * for some Unicode conversion tables there are multiple CCSIDs. + * Some "alternate" Unicode conversion tables are provided by the + * IBM CDRA conversion table registry. + * The most prominent example of a systematic modification of conversion tables that is + * not provided in the form of conversion table files in the repository is + * that S/390 Unix System Services swaps the codes for Line Feed and New Line in all + * EBCDIC codepages, which requires such a swap in the Unicode conversion tables as well. + * + * Only IBM default conversion tables are accessible with ucnv_openCCSID(). + * ucnv_getCCSID() will return the same CCSID for all conversion tables that are associated + * with that CCSID. + * + * Currently, the only "platform" supported in the ICU converter API is UCNV_IBM. + * + * In summary, the use of CCSIDs and the associated API functions is not recommended. + * + * In order to open a converter with the default IBM CDRA Unicode conversion table, + * you can use this function or use the prefix "ibm-": + * \code + * char name[20]; + * sprintf(name, "ibm-%hu", ccsid); + * cnv=ucnv_open(name, &errorCode); + * \endcode + * + * In order to open a converter with the IBM S/390 Unix System Services variant + * of a Unicode/EBCDIC conversion table, + * you can use the prefix "ibm-" together with the option string UCNV_SWAP_LFNL_OPTION_STRING: + * \code + * char name[20]; + * sprintf(name, "ibm-%hu" UCNV_SWAP_LFNL_OPTION_STRING, ccsid); + * cnv=ucnv_open(name, &errorCode); + * \endcode + * + * In order to open a converter from a Microsoft codepage number, use the prefix "cp": + * \code + * char name[20]; + * sprintf(name, "cp%hu", codepageID); + * cnv=ucnv_open(name, &errorCode); + * \endcode + * + * If the alias is ambiguous, then the preferred converter is used + * and the status is set to U_AMBIGUOUS_ALIAS_WARNING. + * + * @param codepage codepage number to create + * @param platform the platform in which the codepage number exists + * @param err error status U_MEMORY_ALLOCATION_ERROR, U_FILE_ACCESS_ERROR + * @return the created Unicode converter object, or NULL if an error + * occurred. + * @see ucnv_open + * @see ucnv_openU + * @see ucnv_close + * @see ucnv_getCCSID + * @see ucnv_getPlatform + * @see UConverterPlatform + * @stable ICU 2.0 + */ +U_CAPI UConverter* U_EXPORT2 +ucnv_openCCSID(int32_t codepage, + UConverterPlatform platform, + UErrorCode * err); + +/** + *

Creates a UConverter object specified from a packageName and a converterName.

+ * + *

The packageName and converterName must point to an ICU udata object, as defined by + * udata_open( packageName, "cnv", converterName, err) or equivalent. + * Typically, packageName will refer to a (.dat) file, or to a package registered with + * udata_setAppData(). Using a full file or directory pathname for packageName is deprecated.

+ * + *

The name will NOT be looked up in the alias mechanism, nor will the converter be + * stored in the converter cache or the alias table. The only way to open further converters + * is call this function multiple times, or use the ucnv_clone() function to clone a + * 'primary' converter.

+ * + *

A future version of ICU may add alias table lookups and/or caching + * to this function.

+ * + *

Example Use: + * cnv = ucnv_openPackage("myapp", "myconverter", &err); + *

+ * + * @param packageName name of the package (equivalent to 'path' in udata_open() call) + * @param converterName name of the data item to be used, without suffix. + * @param err outgoing error status U_MEMORY_ALLOCATION_ERROR, U_FILE_ACCESS_ERROR + * @return the created Unicode converter object, or NULL if an error occurred + * @see udata_open + * @see ucnv_open + * @see ucnv_clone + * @see ucnv_close + * @stable ICU 2.2 + */ +U_CAPI UConverter* U_EXPORT2 +ucnv_openPackage(const char *packageName, const char *converterName, UErrorCode *err); + +/** + * Thread safe converter cloning operation. + * + * You must ucnv_close() the clone. + * + * @param cnv converter to be cloned + * @param status to indicate whether the operation went on smoothly or there were errors + * @return pointer to the new clone + * @stable ICU 71 + */ +U_CAPI UConverter* U_EXPORT2 ucnv_clone(const UConverter *cnv, UErrorCode *status); + +#ifndef U_HIDE_DEPRECATED_API + +/** + * Thread safe converter cloning operation. + * For most efficient operation, pass in a stackBuffer (and a *pBufferSize) + * with at least U_CNV_SAFECLONE_BUFFERSIZE bytes of space. + * If the buffer size is sufficient, then the clone will use the stack buffer; + * otherwise, it will be allocated, and *pBufferSize will indicate + * the actual size. (This should not occur with U_CNV_SAFECLONE_BUFFERSIZE.) + * + * You must ucnv_close() the clone in any case. + * + * If *pBufferSize==0, (regardless of whether stackBuffer==NULL or not) + * then *pBufferSize will be changed to a sufficient size + * for cloning this converter, + * without actually cloning the converter ("pure pre-flighting"). + * + * If *pBufferSize is greater than zero but not large enough for a stack-based + * clone, then the converter is cloned using newly allocated memory + * and *pBufferSize is changed to the necessary size. + * + * If the converter clone fits into the stack buffer but the stack buffer is not + * sufficiently aligned for the clone, then the clone will use an + * adjusted pointer and use an accordingly smaller buffer size. + * + * @param cnv converter to be cloned + * @param stackBuffer Deprecated functionality as of ICU 52, use NULL.
+ * user allocated space for the new clone. If NULL new memory will be allocated. + * If buffer is not large enough, new memory will be allocated. + * Clients can use the U_CNV_SAFECLONE_BUFFERSIZE. This will probably be enough to avoid memory allocations. + * @param pBufferSize Deprecated functionality as of ICU 52, use NULL or 1.
+ * pointer to size of allocated space. + * @param status to indicate whether the operation went on smoothly or there were errors + * An informational status value, U_SAFECLONE_ALLOCATED_WARNING, + * is used if pBufferSize != NULL and any allocations were necessary + * However, it is better to check if *pBufferSize grew for checking for + * allocations because warning codes can be overridden by subsequent + * function calls. + * @return pointer to the new clone + * @deprecated ICU 71 Use ucnv_clone() instead. + */ +U_DEPRECATED UConverter * U_EXPORT2 +ucnv_safeClone(const UConverter *cnv, + void *stackBuffer, + int32_t *pBufferSize, + UErrorCode *status); + +/** + * \def U_CNV_SAFECLONE_BUFFERSIZE + * Definition of a buffer size that is designed to be large enough for + * converters to be cloned with ucnv_safeClone(). + * @deprecated ICU 52. Do not rely on ucnv_safeClone() cloning into any provided buffer. + */ +#define U_CNV_SAFECLONE_BUFFERSIZE 1024 + +#endif /* U_HIDE_DEPRECATED_API */ + +/** + * Deletes the unicode converter and releases resources associated + * with just this instance. + * Does not free up shared converter tables. + * + * @param converter the converter object to be deleted + * @see ucnv_open + * @see ucnv_openU + * @see ucnv_openCCSID + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_close(UConverter * converter); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUConverterPointer + * "Smart pointer" class, closes a UConverter via ucnv_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUConverterPointer, UConverter, ucnv_close); + +U_NAMESPACE_END + +#endif + +/** + * Fills in the output parameter, subChars, with the substitution characters + * as multiple bytes. + * If ucnv_setSubstString() set a Unicode string because the converter is + * stateful, then subChars will be an empty string. + * + * @param converter the Unicode converter + * @param subChars the substitution characters + * @param len on input the capacity of subChars, on output the number + * of bytes copied to it + * @param err the outgoing error status code. + * If the substitution character array is too small, an + * U_INDEX_OUTOFBOUNDS_ERROR will be returned. + * @see ucnv_setSubstString + * @see ucnv_setSubstChars + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_getSubstChars(const UConverter *converter, + char *subChars, + int8_t *len, + UErrorCode *err); + +/** + * Sets the substitution chars when converting from unicode to a codepage. The + * substitution is specified as a string of 1-4 bytes, and may contain + * NULL bytes. + * The subChars must represent a single character. The caller needs to know the + * byte sequence of a valid character in the converter's charset. + * For some converters, for example some ISO 2022 variants, only single-byte + * substitution characters may be supported. + * The newer ucnv_setSubstString() function relaxes these limitations. + * + * @param converter the Unicode converter + * @param subChars the substitution character byte sequence we want set + * @param len the number of bytes in subChars + * @param err the error status code. U_INDEX_OUTOFBOUNDS_ERROR if + * len is bigger than the maximum number of bytes allowed in subchars + * @see ucnv_setSubstString + * @see ucnv_getSubstChars + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_setSubstChars(UConverter *converter, + const char *subChars, + int8_t len, + UErrorCode *err); + +/** + * Set a substitution string for converting from Unicode to a charset. + * The caller need not know the charset byte sequence for each charset. + * + * Unlike ucnv_setSubstChars() which is designed to set a charset byte sequence + * for a single character, this function takes a Unicode string with + * zero, one or more characters, and immediately verifies that the string can be + * converted to the charset. + * If not, or if the result is too long (more than 32 bytes as of ICU 3.6), + * then the function returns with an error accordingly. + * + * Also unlike ucnv_setSubstChars(), this function works for stateful charsets + * by converting on the fly at the point of substitution rather than setting + * a fixed byte sequence. + * + * @param cnv The UConverter object. + * @param s The Unicode string. + * @param length The number of UChars in s, or -1 for a NUL-terminated string. + * @param err Pointer to a standard 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.) + * + * @see ucnv_setSubstChars + * @see ucnv_getSubstChars + * @stable ICU 3.6 + */ +U_CAPI void U_EXPORT2 +ucnv_setSubstString(UConverter *cnv, + const UChar *s, + int32_t length, + UErrorCode *err); + +/** + * Fills in the output parameter, errBytes, with the error characters from the + * last failing conversion. + * + * @param converter the Unicode converter + * @param errBytes the codepage bytes which were in error + * @param len on input the capacity of errBytes, on output the number of + * bytes which were copied to it + * @param err the error status code. + * If the substitution character array is too small, an + * U_INDEX_OUTOFBOUNDS_ERROR will be returned. + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_getInvalidChars(const UConverter *converter, + char *errBytes, + int8_t *len, + UErrorCode *err); + +/** + * Fills in the output parameter, errChars, with the error characters from the + * last failing conversion. + * + * @param converter the Unicode converter + * @param errUChars the UChars which were in error + * @param len on input the capacity of errUChars, on output the number of + * UChars which were copied to it + * @param err the error status code. + * If the substitution character array is too small, an + * U_INDEX_OUTOFBOUNDS_ERROR will be returned. + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_getInvalidUChars(const UConverter *converter, + UChar *errUChars, + int8_t *len, + UErrorCode *err); + +/** + * Resets the state of a converter to the default state. This is used + * in the case of an error, to restart a conversion from a known default state. + * It will also empty the internal output buffers. + * @param converter the Unicode converter + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_reset(UConverter *converter); + +/** + * Resets the to-Unicode part of a converter state to the default state. + * This is used in the case of an error to restart a conversion to + * Unicode to a known default state. It will also empty the internal + * output buffers used for the conversion to Unicode codepoints. + * @param converter the Unicode converter + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_resetToUnicode(UConverter *converter); + +/** + * Resets the from-Unicode part of a converter state to the default state. + * This is used in the case of an error to restart a conversion from + * Unicode to a known default state. It will also empty the internal output + * buffers used for the conversion from Unicode codepoints. + * @param converter the Unicode converter + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_resetFromUnicode(UConverter *converter); + +/** + * Returns the maximum number of bytes that are output per UChar in conversion + * from Unicode using this converter. + * The returned number can be used with UCNV_GET_MAX_BYTES_FOR_STRING + * to calculate the size of a target buffer for conversion from Unicode. + * + * Note: Before ICU 2.8, this function did not return reliable numbers for + * some stateful converters (EBCDIC_STATEFUL, ISO-2022) and LMBCS. + * + * This number may not be the same as the maximum number of bytes per + * "conversion unit". In other words, it may not be the intuitively expected + * number of bytes per character that would be published for a charset, + * and may not fulfill any other purpose than the allocation of an output + * buffer of guaranteed sufficient size for a given input length and converter. + * + * Examples for special cases that are taken into account: + * - Supplementary code points may convert to more bytes than BMP code points. + * This function returns bytes per UChar (UTF-16 code unit), not per + * Unicode code point, for efficient buffer allocation. + * - State-shifting output (SI/SO, escapes, etc.) from stateful converters. + * - When m input UChars are converted to n output bytes, then the maximum m/n + * is taken into account. + * + * The number returned here does not take into account + * (see UCNV_GET_MAX_BYTES_FOR_STRING): + * - callbacks which output more than one charset character sequence per call, + * like escape callbacks + * - initial and final non-character bytes that are output by some converters + * (automatic BOMs, initial escape sequence, final SI, etc.) + * + * Examples for returned values: + * - SBCS charsets: 1 + * - Shift-JIS: 2 + * - UTF-16: 2 (2 per BMP, 4 per surrogate _pair_, BOM not counted) + * - UTF-8: 3 (3 per BMP, 4 per surrogate _pair_) + * - EBCDIC_STATEFUL (EBCDIC mixed SBCS/DBCS): 3 (SO + DBCS) + * - ISO-2022: 3 (always outputs UTF-8) + * - ISO-2022-JP: 6 (4-byte escape sequences + DBCS) + * - ISO-2022-CN: 8 (4-byte designator sequences + 2-byte SS2/SS3 + DBCS) + * + * @param converter The Unicode converter. + * @return The maximum number of bytes per UChar (16 bit code unit) + * that are output by ucnv_fromUnicode(), + * to be used together with UCNV_GET_MAX_BYTES_FOR_STRING + * for buffer allocation. + * + * @see UCNV_GET_MAX_BYTES_FOR_STRING + * @see ucnv_getMinCharSize + * @stable ICU 2.0 + */ +U_CAPI int8_t U_EXPORT2 +ucnv_getMaxCharSize(const UConverter *converter); + +/** + * Calculates the size of a buffer for conversion from Unicode to a charset. + * The calculated size is guaranteed to be sufficient for this conversion. + * + * It takes into account initial and final non-character bytes that are output + * by some converters. + * It does not take into account callbacks which output more than one charset + * character sequence per call, like escape callbacks. + * The default (substitution) callback only outputs one charset character sequence. + * + * @param length Number of UChars to be converted. + * @param maxCharSize Return value from ucnv_getMaxCharSize() for the converter + * that will be used. + * @return Size of a buffer that will be large enough to hold the output bytes of + * converting length UChars with the converter that returned the maxCharSize. + * + * @see ucnv_getMaxCharSize + * @stable ICU 2.8 + */ +#define UCNV_GET_MAX_BYTES_FOR_STRING(length, maxCharSize) \ + (((int32_t)(length)+10)*(int32_t)(maxCharSize)) + +/** + * Returns the minimum byte length (per codepoint) for characters in this codepage. + * This is usually either 1 or 2. + * @param converter the Unicode converter + * @return the minimum number of bytes per codepoint allowed by this particular converter + * @see ucnv_getMaxCharSize + * @stable ICU 2.0 + */ +U_CAPI int8_t U_EXPORT2 +ucnv_getMinCharSize(const UConverter *converter); + +/** + * Returns the display name of the converter passed in based on the Locale + * passed in. If the locale contains no display name, the internal ASCII + * name will be filled in. + * + * @param converter the Unicode converter. + * @param displayLocale is the specific Locale we want to localized for + * @param displayName user provided buffer to be filled in + * @param displayNameCapacity size of displayName Buffer + * @param err error status code + * @return displayNameLength number of UChar needed in displayName + * @see ucnv_getName + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ucnv_getDisplayName(const UConverter *converter, + const char *displayLocale, + UChar *displayName, + int32_t displayNameCapacity, + UErrorCode *err); + +/** + * Gets the internal, canonical name of the converter (zero-terminated). + * The lifetime of the returned string will be that of the converter + * passed to this function. + * @param converter the Unicode converter + * @param err UErrorCode status + * @return the internal name of the converter + * @see ucnv_getDisplayName + * @stable ICU 2.0 + */ +U_CAPI const char * U_EXPORT2 +ucnv_getName(const UConverter *converter, UErrorCode *err); + +/** + * Gets a codepage number associated with the converter. This is not guaranteed + * to be the one used to create the converter. Some converters do not represent + * platform registered codepages and return zero for the codepage number. + * The error code fill-in parameter indicates if the codepage number + * is available. + * Does not check if the converter is NULL or if converter's data + * table is NULL. + * + * Important: The use of CCSIDs is not recommended because it is limited + * to only two platforms in principle and only one (UCNV_IBM) in the current + * ICU converter API. + * Also, CCSIDs are insufficient to identify IBM Unicode conversion tables precisely. + * For more details see ucnv_openCCSID(). + * + * @param converter the Unicode converter + * @param err the error status code. + * @return If any error occurs, -1 will be returned otherwise, the codepage number + * will be returned + * @see ucnv_openCCSID + * @see ucnv_getPlatform + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ucnv_getCCSID(const UConverter *converter, + UErrorCode *err); + +/** + * Gets a codepage platform associated with the converter. Currently, + * only UCNV_IBM will be returned. + * Does not test if the converter is NULL or if converter's data + * table is NULL. + * @param converter the Unicode converter + * @param err the error status code. + * @return The codepage platform + * @stable ICU 2.0 + */ +U_CAPI UConverterPlatform U_EXPORT2 +ucnv_getPlatform(const UConverter *converter, + UErrorCode *err); + +/** + * Gets the type of the converter + * e.g. SBCS, MBCS, DBCS, UTF8, UTF16_BE, UTF16_LE, ISO_2022, + * EBCDIC_STATEFUL, LATIN_1 + * @param converter a valid, opened converter + * @return the type of the converter + * @stable ICU 2.0 + */ +U_CAPI UConverterType U_EXPORT2 +ucnv_getType(const UConverter * converter); + +/** + * Gets the "starter" (lead) bytes for converters of type MBCS. + * Will fill in an U_ILLEGAL_ARGUMENT_ERROR if converter passed in + * is not MBCS. Fills in an array of type UBool, with the value of the byte + * as offset to the array. For example, if (starters[0x20] == true) at return, + * it means that the byte 0x20 is a starter byte in this converter. + * Context pointers are always owned by the caller. + * + * @param converter a valid, opened converter of type MBCS + * @param starters an array of size 256 to be filled in + * @param err error status, U_ILLEGAL_ARGUMENT_ERROR if the + * converter is not a type which can return starters. + * @see ucnv_getType + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_getStarters(const UConverter* converter, + UBool starters[256], + UErrorCode* err); + + +/** + * Selectors for Unicode sets that can be returned by ucnv_getUnicodeSet(). + * @see ucnv_getUnicodeSet + * @stable ICU 2.6 + */ +typedef enum UConverterUnicodeSet { + /** Select the set of roundtrippable Unicode code points. @stable ICU 2.6 */ + UCNV_ROUNDTRIP_SET, + /** Select the set of Unicode code points with roundtrip or fallback mappings. @stable ICU 4.0 */ + UCNV_ROUNDTRIP_AND_FALLBACK_SET, +#ifndef U_HIDE_DEPRECATED_API + /** + * Number of UConverterUnicodeSet selectors. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UCNV_SET_COUNT +#endif // U_HIDE_DEPRECATED_API +} UConverterUnicodeSet; + + +/** + * Returns the set of Unicode code points that can be converted by an ICU converter. + * + * Returns one of several kinds of set: + * + * 1. UCNV_ROUNDTRIP_SET + * + * The set of all Unicode code points that can be roundtrip-converted + * (converted without any data loss) with the converter (ucnv_fromUnicode()). + * This set will not include code points that have fallback mappings + * or are only the result of reverse fallback mappings. + * This set will also not include PUA code points with fallbacks, although + * ucnv_fromUnicode() will always uses those mappings despite ucnv_setFallback(). + * See UTR #22 "Character Mapping Markup Language" + * at http://www.unicode.org/reports/tr22/ + * + * This is useful for example for + * - checking that a string or document can be roundtrip-converted with a converter, + * without/before actually performing the conversion + * - testing if a converter can be used for text for typical text for a certain locale, + * by comparing its roundtrip set with the set of ExemplarCharacters from + * ICU's locale data or other sources + * + * 2. UCNV_ROUNDTRIP_AND_FALLBACK_SET + * + * The set of all Unicode code points that can be converted with the converter (ucnv_fromUnicode()) + * when fallbacks are turned on (see ucnv_setFallback()). + * This set includes all code points with roundtrips and fallbacks (but not reverse fallbacks). + * + * In the future, there may be more UConverterUnicodeSet choices to select + * sets with different properties. + * + * @param cnv The converter for which a set is requested. + * @param setFillIn A valid USet *. It will be cleared by this function before + * the converter's specific set is filled into the USet. + * @param whichSet A UConverterUnicodeSet selector; + * currently UCNV_ROUNDTRIP_SET is the only supported value. + * @param pErrorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * + * @see UConverterUnicodeSet + * @see uset_open + * @see uset_close + * @stable ICU 2.6 + */ +U_CAPI void U_EXPORT2 +ucnv_getUnicodeSet(const UConverter *cnv, + USet *setFillIn, + UConverterUnicodeSet whichSet, + UErrorCode *pErrorCode); + +/** + * Gets the current callback function used by the converter when an illegal + * or invalid codepage sequence is found. + * Context pointers are always owned by the caller. + * + * @param converter the unicode converter + * @param action fillin: returns the callback function pointer + * @param context fillin: returns the callback's private void* context + * @see ucnv_setToUCallBack + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_getToUCallBack (const UConverter * converter, + UConverterToUCallback *action, + const void **context); + +/** + * Gets the current callback function used by the converter when illegal + * or invalid Unicode sequence is found. + * Context pointers are always owned by the caller. + * + * @param converter the unicode converter + * @param action fillin: returns the callback function pointer + * @param context fillin: returns the callback's private void* context + * @see ucnv_setFromUCallBack + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_getFromUCallBack (const UConverter * converter, + UConverterFromUCallback *action, + const void **context); + +/** + * Changes the callback function used by the converter when + * an illegal or invalid sequence is found. + * Context pointers are always owned by the caller. + * Predefined actions and contexts can be found in the ucnv_err.h header. + * + * @param converter the unicode converter + * @param newAction the new callback function + * @param newContext the new toUnicode callback context pointer. This can be NULL. + * @param oldAction fillin: returns the old callback function pointer. This can be NULL. + * @param oldContext fillin: returns the old callback's private void* context. This can be NULL. + * @param err The error code status + * @see ucnv_getToUCallBack + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_setToUCallBack (UConverter * converter, + UConverterToUCallback newAction, + const void* newContext, + UConverterToUCallback *oldAction, + const void** oldContext, + UErrorCode * err); + +/** + * Changes the current callback function used by the converter when + * an illegal or invalid sequence is found. + * Context pointers are always owned by the caller. + * Predefined actions and contexts can be found in the ucnv_err.h header. + * + * @param converter the unicode converter + * @param newAction the new callback function + * @param newContext the new fromUnicode callback context pointer. This can be NULL. + * @param oldAction fillin: returns the old callback function pointer. This can be NULL. + * @param oldContext fillin: returns the old callback's private void* context. This can be NULL. + * @param err The error code status + * @see ucnv_getFromUCallBack + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_setFromUCallBack (UConverter * converter, + UConverterFromUCallback newAction, + const void *newContext, + UConverterFromUCallback *oldAction, + const void **oldContext, + UErrorCode * err); + +/** + * Converts an array of unicode characters to an array of codepage + * characters. This function is optimized for converting a continuous + * stream of data in buffer-sized chunks, where the entire source and + * target does not fit in available buffers. + * + * The source pointer is an in/out parameter. It starts out pointing where the + * conversion is to begin, and ends up pointing after the last UChar consumed. + * + * Target similarly starts out pointer at the first available byte in the output + * buffer, and ends up pointing after the last byte written to the output. + * + * The converter always attempts to consume the entire source buffer, unless + * (1.) the target buffer is full, or (2.) a failing error is returned from the + * current callback function. When a successful error status has been + * returned, it means that all of the source buffer has been + * consumed. At that point, the caller should reset the source and + * sourceLimit pointers to point to the next chunk. + * + * At the end of the stream (flush==true), the input is completely consumed + * when *source==sourceLimit and no error code is set. + * The converter object is then automatically reset by this function. + * (This means that a converter need not be reset explicitly between data + * streams if it finishes the previous stream without errors.) + * + * This is a stateful conversion. Additionally, even when all source data has + * been consumed, some data may be in the converters' internal state. + * Call this function repeatedly, updating the target pointers with + * the next empty chunk of target in case of a + * U_BUFFER_OVERFLOW_ERROR, and updating the source pointers + * with the next chunk of source when a successful error status is + * returned, until there are no more chunks of source data. + * @param converter the Unicode converter + * @param target I/O parameter. Input : Points to the beginning of the buffer to copy + * codepage characters to. Output : points to after the last codepage character copied + * to target. + * @param targetLimit the pointer just after last of the target buffer + * @param source I/O parameter, pointer to pointer to the source Unicode character buffer. + * @param sourceLimit the pointer just after the last of the source buffer + * @param offsets if NULL is passed, nothing will happen to it, otherwise it needs to have the same number + * of allocated cells as target. Will fill in offsets from target to source pointer + * e.g: offsets[3] is equal to 6, it means that the target[3] was a result of transcoding source[6] + * For output data carried across calls, and other data without a specific source character + * (such as from escape sequences or callbacks) -1 will be placed for offsets. + * @param flush set to true if the current source buffer is the last available + * chunk of the source, false otherwise. Note that if a failing status is returned, + * this function may have to be called multiple times with flush set to true until + * the source buffer is consumed. + * @param err the error status. U_ILLEGAL_ARGUMENT_ERROR will be set if the + * converter is NULL. + * U_BUFFER_OVERFLOW_ERROR will be set if the target is full and there is + * still data to be written to the target. + * @see ucnv_fromUChars + * @see ucnv_convert + * @see ucnv_getMinCharSize + * @see ucnv_setToUCallBack + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_fromUnicode (UConverter * converter, + char **target, + const char *targetLimit, + const UChar ** source, + const UChar * sourceLimit, + int32_t* offsets, + UBool flush, + UErrorCode * err); + +/** + * Converts a buffer of codepage bytes into an array of unicode UChars + * characters. This function is optimized for converting a continuous + * stream of data in buffer-sized chunks, where the entire source and + * target does not fit in available buffers. + * + * The source pointer is an in/out parameter. It starts out pointing where the + * conversion is to begin, and ends up pointing after the last byte of source consumed. + * + * Target similarly starts out pointer at the first available UChar in the output + * buffer, and ends up pointing after the last UChar written to the output. + * It does NOT necessarily keep UChar sequences together. + * + * The converter always attempts to consume the entire source buffer, unless + * (1.) the target buffer is full, or (2.) a failing error is returned from the + * current callback function. When a successful error status has been + * returned, it means that all of the source buffer has been + * consumed. At that point, the caller should reset the source and + * sourceLimit pointers to point to the next chunk. + * + * At the end of the stream (flush==true), the input is completely consumed + * when *source==sourceLimit and no error code is set + * The converter object is then automatically reset by this function. + * (This means that a converter need not be reset explicitly between data + * streams if it finishes the previous stream without errors.) + * + * This is a stateful conversion. Additionally, even when all source data has + * been consumed, some data may be in the converters' internal state. + * Call this function repeatedly, updating the target pointers with + * the next empty chunk of target in case of a + * U_BUFFER_OVERFLOW_ERROR, and updating the source pointers + * with the next chunk of source when a successful error status is + * returned, until there are no more chunks of source data. + * @param converter the Unicode converter + * @param target I/O parameter. Input : Points to the beginning of the buffer to copy + * UChars into. Output : points to after the last UChar copied. + * @param targetLimit the pointer just after the end of the target buffer + * @param source I/O parameter, pointer to pointer to the source codepage buffer. + * @param sourceLimit the pointer to the byte after the end of the source buffer + * @param offsets if NULL is passed, nothing will happen to it, otherwise it needs to have the same number + * of allocated cells as target. Will fill in offsets from target to source pointer + * e.g: offsets[3] is equal to 6, it means that the target[3] was a result of transcoding source[6] + * For output data carried across calls, and other data without a specific source character + * (such as from escape sequences or callbacks) -1 will be placed for offsets. + * @param flush set to true if the current source buffer is the last available + * chunk of the source, false otherwise. Note that if a failing status is returned, + * this function may have to be called multiple times with flush set to true until + * the source buffer is consumed. + * @param err the error status. U_ILLEGAL_ARGUMENT_ERROR will be set if the + * converter is NULL. + * U_BUFFER_OVERFLOW_ERROR will be set if the target is full and there is + * still data to be written to the target. + * @see ucnv_fromUChars + * @see ucnv_convert + * @see ucnv_getMinCharSize + * @see ucnv_setFromUCallBack + * @see ucnv_getNextUChar + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_toUnicode(UConverter *converter, + UChar **target, + const UChar *targetLimit, + const char **source, + const char *sourceLimit, + int32_t *offsets, + UBool flush, + UErrorCode *err); + +/** + * Convert the Unicode string into a codepage string using an existing UConverter. + * The output string is NUL-terminated if possible. + * + * This function is a more convenient but less powerful version of ucnv_fromUnicode(). + * It is only useful for whole strings, not for streaming conversion. + * + * The maximum output buffer capacity required (barring output from callbacks) will be + * UCNV_GET_MAX_BYTES_FOR_STRING(srcLength, ucnv_getMaxCharSize(cnv)). + * + * @param cnv the converter object to be used (ucnv_resetFromUnicode() will be called) + * @param src the input Unicode string + * @param srcLength the input string length, or -1 if NUL-terminated + * @param dest destination string buffer, can be NULL if destCapacity==0 + * @param destCapacity the number of chars available at dest + * @param pErrorCode normal ICU error code; + * common error codes that may be set by this function include + * U_BUFFER_OVERFLOW_ERROR, U_STRING_NOT_TERMINATED_WARNING, + * U_ILLEGAL_ARGUMENT_ERROR, and conversion errors + * @return the length of the output string, not counting the terminating NUL; + * if the length is greater than destCapacity, then the string will not fit + * and a buffer of the indicated length would need to be passed in + * @see ucnv_fromUnicode + * @see ucnv_convert + * @see UCNV_GET_MAX_BYTES_FOR_STRING + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ucnv_fromUChars(UConverter *cnv, + char *dest, int32_t destCapacity, + const UChar *src, int32_t srcLength, + UErrorCode *pErrorCode); + +/** + * Convert the codepage string into a Unicode string using an existing UConverter. + * The output string is NUL-terminated if possible. + * + * This function is a more convenient but less powerful version of ucnv_toUnicode(). + * It is only useful for whole strings, not for streaming conversion. + * + * The maximum output buffer capacity required (barring output from callbacks) will be + * 2*srcLength (each char may be converted into a surrogate pair). + * + * @param cnv the converter object to be used (ucnv_resetToUnicode() will be called) + * @param src the input codepage string + * @param srcLength the input string length, or -1 if NUL-terminated + * @param dest destination string buffer, can be NULL if destCapacity==0 + * @param destCapacity the number of UChars available at dest + * @param pErrorCode normal ICU error code; + * common error codes that may be set by this function include + * U_BUFFER_OVERFLOW_ERROR, U_STRING_NOT_TERMINATED_WARNING, + * U_ILLEGAL_ARGUMENT_ERROR, and conversion errors + * @return the length of the output string, not counting the terminating NUL; + * if the length is greater than destCapacity, then the string will not fit + * and a buffer of the indicated length would need to be passed in + * @see ucnv_toUnicode + * @see ucnv_convert + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ucnv_toUChars(UConverter *cnv, + UChar *dest, int32_t destCapacity, + const char *src, int32_t srcLength, + UErrorCode *pErrorCode); + +/** + * Convert a codepage buffer into Unicode one character at a time. + * The input is completely consumed when the U_INDEX_OUTOFBOUNDS_ERROR is set. + * + * Advantage compared to ucnv_toUnicode() or ucnv_toUChars(): + * - Faster for small amounts of data, for most converters, e.g., + * US-ASCII, ISO-8859-1, UTF-8/16/32, and most "normal" charsets. + * (For complex converters, e.g., SCSU, UTF-7 and ISO 2022 variants, + * it uses ucnv_toUnicode() internally.) + * - Convenient. + * + * Limitations compared to ucnv_toUnicode(): + * - Always assumes flush=true. + * This makes ucnv_getNextUChar() unsuitable for "streaming" conversion, + * that is, for where the input is supplied in multiple buffers, + * because ucnv_getNextUChar() will assume the end of the input at the end + * of the first buffer. + * - Does not provide offset output. + * + * It is possible to "mix" ucnv_getNextUChar() and ucnv_toUnicode() because + * ucnv_getNextUChar() uses the current state of the converter + * (unlike ucnv_toUChars() which always resets first). + * However, if ucnv_getNextUChar() is called after ucnv_toUnicode() + * stopped in the middle of a character sequence (with flush=false), + * then ucnv_getNextUChar() will always use the slower ucnv_toUnicode() + * internally until the next character boundary. + * (This is new in ICU 2.6. In earlier releases, ucnv_getNextUChar() had to + * start at a character boundary.) + * + * Instead of using ucnv_getNextUChar(), it is recommended + * to convert using ucnv_toUnicode() or ucnv_toUChars() + * and then iterate over the text using U16_NEXT() or a UCharIterator (uiter.h) + * or a C++ CharacterIterator or similar. + * This allows streaming conversion and offset output, for example. + * + *

Handling of surrogate pairs and supplementary-plane code points:
+ * There are two different kinds of codepages that provide mappings for surrogate characters: + *

    + *
  • Codepages like UTF-8, UTF-32, and GB 18030 provide direct representations for Unicode + * code points U+10000-U+10ffff as well as for single surrogates U+d800-U+dfff. + * Each valid sequence will result in exactly one returned code point. + * If a sequence results in a single surrogate, then that will be returned + * by itself, even if a neighboring sequence encodes the matching surrogate.
  • + *
  • Codepages like SCSU and LMBCS (and UTF-16) provide direct representations only for BMP code points + * including surrogates. Code points in supplementary planes are represented with + * two sequences, each encoding a surrogate. + * For these codepages, matching pairs of surrogates will be combined into single + * code points for returning from this function. + * (Note that SCSU is actually a mix of these codepage types.)
  • + *

+ * + * @param converter an open UConverter + * @param source the address of a pointer to the codepage buffer, will be + * updated to point after the bytes consumed in the conversion call. + * @param sourceLimit points to the end of the input buffer + * @param err fills in error status (see ucnv_toUnicode) + * U_INDEX_OUTOFBOUNDS_ERROR will be set if the input + * is empty or does not convert to any output (e.g.: pure state-change + * codes SI/SO, escape sequences for ISO 2022, + * or if the callback did not output anything, ...). + * This function will not set a U_BUFFER_OVERFLOW_ERROR because + * the "buffer" is the return code. However, there might be subsequent output + * stored in the converter object + * that will be returned in following calls to this function. + * @return a UChar32 resulting from the partial conversion of source + * @see ucnv_toUnicode + * @see ucnv_toUChars + * @see ucnv_convert + * @stable ICU 2.0 + */ +U_CAPI UChar32 U_EXPORT2 +ucnv_getNextUChar(UConverter * converter, + const char **source, + const char * sourceLimit, + UErrorCode * err); + +/** + * Convert from one external charset to another using two existing UConverters. + * Internally, two conversions - ucnv_toUnicode() and ucnv_fromUnicode() - + * are used, "pivoting" through 16-bit Unicode. + * + * Important: For streaming conversion (multiple function calls for successive + * parts of a text stream), the caller must provide a pivot buffer explicitly, + * and must preserve the pivot buffer and associated pointers from one + * call to another. (The buffer may be moved if its contents and the relative + * pointer positions are preserved.) + * + * There is a similar function, ucnv_convert(), + * which has the following limitations: + * - it takes charset names, not converter objects, so that + * - two converters are opened for each call + * - only single-string conversion is possible, not streaming operation + * - it does not provide enough information to find out, + * in case of failure, whether the toUnicode or + * the fromUnicode conversion failed + * + * By contrast, ucnv_convertEx() + * - takes UConverter parameters instead of charset names + * - fully exposes the pivot buffer for streaming conversion and complete error handling + * + * ucnv_convertEx() also provides further convenience: + * - an option to reset the converters at the beginning + * (if reset==true, see parameters; + * also sets *pivotTarget=*pivotSource=pivotStart) + * - allow NUL-terminated input + * (only a single NUL byte, will not work for charsets with multi-byte NULs) + * (if sourceLimit==NULL, see parameters) + * - terminate with a NUL on output + * (only a single NUL byte, not useful for charsets with multi-byte NULs), + * or set U_STRING_NOT_TERMINATED_WARNING if the output exactly fills + * the target buffer + * - the pivot buffer can be provided internally; + * possible only for whole-string conversion, not streaming conversion; + * in this case, the caller will not be able to get details about where an + * error occurred + * (if pivotStart==NULL, see below) + * + * The function returns when one of the following is true: + * - the entire source text has been converted successfully to the target buffer + * - a target buffer overflow occurred (U_BUFFER_OVERFLOW_ERROR) + * - a conversion error occurred + * (other U_FAILURE(), see description of pErrorCode) + * + * Limitation compared to the direct use of + * ucnv_fromUnicode() and ucnv_toUnicode(): + * ucnv_convertEx() does not provide offset information. + * + * Limitation compared to ucnv_fromUChars() and ucnv_toUChars(): + * ucnv_convertEx() does not support preflighting directly. + * + * Sample code for converting a single string from + * one external charset to UTF-8, ignoring the location of errors: + * + * \code + * int32_t + * myToUTF8(UConverter *cnv, + * const char *s, int32_t length, + * char *u8, int32_t capacity, + * UErrorCode *pErrorCode) { + * UConverter *utf8Cnv; + * char *target; + * + * if(U_FAILURE(*pErrorCode)) { + * return 0; + * } + * + * utf8Cnv=myGetCachedUTF8Converter(pErrorCode); + * if(U_FAILURE(*pErrorCode)) { + * return 0; + * } + * + * if(length<0) { + * length=strlen(s); + * } + * target=u8; + * ucnv_convertEx(utf8Cnv, cnv, + * &target, u8+capacity, + * &s, s+length, + * NULL, NULL, NULL, NULL, + * true, true, + * pErrorCode); + * + * myReleaseCachedUTF8Converter(utf8Cnv); + * + * // return the output string length, but without preflighting + * return (int32_t)(target-u8); + * } + * \endcode + * + * @param targetCnv Output converter, used to convert from the UTF-16 pivot + * to the target using ucnv_fromUnicode(). + * @param sourceCnv Input converter, used to convert from the source to + * the UTF-16 pivot using ucnv_toUnicode(). + * @param target I/O parameter, same as for ucnv_fromUChars(). + * Input: *target points to the beginning of the target buffer. + * Output: *target points to the first unit after the last char written. + * @param targetLimit Pointer to the first unit after the target buffer. + * @param source I/O parameter, same as for ucnv_toUChars(). + * Input: *source points to the beginning of the source buffer. + * Output: *source points to the first unit after the last char read. + * @param sourceLimit Pointer to the first unit after the source buffer. + * @param pivotStart Pointer to the UTF-16 pivot buffer. If pivotStart==NULL, + * then an internal buffer is used and the other pivot + * arguments are ignored and can be NULL as well. + * @param pivotSource I/O parameter, same as source in ucnv_fromUChars() for + * conversion from the pivot buffer to the target buffer. + * @param pivotTarget I/O parameter, same as target in ucnv_toUChars() for + * conversion from the source buffer to the pivot buffer. + * It must be pivotStart<=*pivotSource<=*pivotTarget<=pivotLimit + * and pivotStart[0..ucnv_countAvailable()]) + * @return a pointer a string (library owned), or NULL if the index is out of bounds. + * @see ucnv_countAvailable + * @stable ICU 2.0 + */ +U_CAPI const char* U_EXPORT2 +ucnv_getAvailableName(int32_t n); + +/** + * Returns a UEnumeration to enumerate all of the canonical converter + * names, as per the alias file, regardless of the ability to open each + * converter. + * + * @return A UEnumeration object for getting all the recognized canonical + * converter names. + * @see ucnv_getAvailableName + * @see uenum_close + * @see uenum_next + * @stable ICU 2.4 + */ +U_CAPI UEnumeration * U_EXPORT2 +ucnv_openAllNames(UErrorCode *pErrorCode); + +/** + * Gives the number of aliases for a given converter or alias name. + * If the alias is ambiguous, then the preferred converter is used + * and the status is set to U_AMBIGUOUS_ALIAS_WARNING. + * This method only enumerates the listed entries in the alias file. + * @param alias alias name + * @param pErrorCode error status + * @return number of names on alias list for given alias + * @stable ICU 2.0 + */ +U_CAPI uint16_t U_EXPORT2 +ucnv_countAliases(const char *alias, UErrorCode *pErrorCode); + +/** + * Gives the name of the alias at given index of alias list. + * This method only enumerates the listed entries in the alias file. + * If the alias is ambiguous, then the preferred converter is used + * and the status is set to U_AMBIGUOUS_ALIAS_WARNING. + * @param alias alias name + * @param n index in alias list + * @param pErrorCode result of operation + * @return returns the name of the alias at given index + * @see ucnv_countAliases + * @stable ICU 2.0 + */ +U_CAPI const char * U_EXPORT2 +ucnv_getAlias(const char *alias, uint16_t n, UErrorCode *pErrorCode); + +/** + * Fill-up the list of alias names for the given alias. + * This method only enumerates the listed entries in the alias file. + * If the alias is ambiguous, then the preferred converter is used + * and the status is set to U_AMBIGUOUS_ALIAS_WARNING. + * @param alias alias name + * @param aliases fill-in list, aliases is a pointer to an array of + * ucnv_countAliases() string-pointers + * (const char *) that will be filled in. + * The strings themselves are owned by the library. + * @param pErrorCode result of operation + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_getAliases(const char *alias, const char **aliases, UErrorCode *pErrorCode); + +/** + * Return a new UEnumeration object for enumerating all the + * alias names for a given converter that are recognized by a standard. + * This method only enumerates the listed entries in the alias file. + * The convrtrs.txt file can be modified to change the results of + * this function. + * The first result in this list is the same result given by + * ucnv_getStandardName, which is the default alias for + * the specified standard name. The returned object must be closed with + * uenum_close when you are done with the object. + * + * @param convName original converter name + * @param standard name of the standard governing the names; MIME and IANA + * are such standards + * @param pErrorCode The error code + * @return A UEnumeration object for getting all aliases that are recognized + * by a standard. If any of the parameters are invalid, NULL + * is returned. + * @see ucnv_getStandardName + * @see uenum_close + * @see uenum_next + * @stable ICU 2.2 + */ +U_CAPI UEnumeration * U_EXPORT2 +ucnv_openStandardNames(const char *convName, + const char *standard, + UErrorCode *pErrorCode); + +/** + * Gives the number of standards associated to converter names. + * @return number of standards + * @stable ICU 2.0 + */ +U_CAPI uint16_t U_EXPORT2 +ucnv_countStandards(void); + +/** + * Gives the name of the standard at given index of standard list. + * @param n index in standard list + * @param pErrorCode result of operation + * @return returns the name of the standard at given index. Owned by the library. + * @stable ICU 2.0 + */ +U_CAPI const char * U_EXPORT2 +ucnv_getStandard(uint16_t n, UErrorCode *pErrorCode); + +/** + * Returns a standard name for a given converter name. + *

+ * Example alias table:
+ * conv alias1 { STANDARD1 } alias2 { STANDARD1* } + *

+ * Result of ucnv_getStandardName("conv", "STANDARD1") from example + * alias table:
+ * "alias2" + * + * @param name original converter name + * @param standard name of the standard governing the names; MIME and IANA + * are such standards + * @param pErrorCode result of operation + * @return returns the standard converter name; + * if a standard converter name cannot be determined, + * then NULL is returned. Owned by the library. + * @stable ICU 2.0 + */ +U_CAPI const char * U_EXPORT2 +ucnv_getStandardName(const char *name, const char *standard, UErrorCode *pErrorCode); + +/** + * This function will return the internal canonical converter name of the + * tagged alias. This is the opposite of ucnv_openStandardNames, which + * returns the tagged alias given the canonical name. + *

+ * Example alias table:
+ * conv alias1 { STANDARD1 } alias2 { STANDARD1* } + *

+ * Result of ucnv_getStandardName("alias1", "STANDARD1") from example + * alias table:
+ * "conv" + * + * @return returns the canonical converter name; + * if a standard or alias name cannot be determined, + * then NULL is returned. The returned string is + * owned by the library. + * @see ucnv_getStandardName + * @stable ICU 2.4 + */ +U_CAPI const char * U_EXPORT2 +ucnv_getCanonicalName(const char *alias, const char *standard, UErrorCode *pErrorCode); + +/** + * Returns the current default converter name. If you want to open + * a default converter, you do not need to use this function. + * It is faster if you pass a NULL argument to ucnv_open the + * default converter. + * + * If U_CHARSET_IS_UTF8 is defined to 1 in utypes.h then this function + * always returns "UTF-8". + * + * @return returns the current default converter name. + * Storage owned by the library + * @see ucnv_setDefaultName + * @stable ICU 2.0 + */ +U_CAPI const char * U_EXPORT2 +ucnv_getDefaultName(void); + +#ifndef U_HIDE_SYSTEM_API +/** + * This function is not thread safe. DO NOT call this function when ANY ICU + * function is being used from more than one thread! This function sets the + * current default converter name. If this function needs to be called, it + * should be called during application initialization. Most of the time, the + * results from ucnv_getDefaultName() or ucnv_open with a NULL string argument + * is sufficient for your application. + * + * If U_CHARSET_IS_UTF8 is defined to 1 in utypes.h then this function + * does nothing. + * + * @param name the converter name to be the default (must be known by ICU). + * @see ucnv_getDefaultName + * @system + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_setDefaultName(const char *name); +#endif /* U_HIDE_SYSTEM_API */ + +/** + * Fixes the backslash character mismapping. For example, in SJIS, the backslash + * character in the ASCII portion is also used to represent the yen currency sign. + * When mapping from Unicode character 0x005C, it's unclear whether to map the + * character back to yen or backslash in SJIS. This function will take the input + * buffer and replace all the yen sign characters with backslash. This is necessary + * when the user tries to open a file with the input buffer on Windows. + * This function will test the converter to see whether such mapping is + * required. You can sometimes avoid using this function by using the correct version + * of Shift-JIS. + * + * @param cnv The converter representing the target codepage. + * @param source the input buffer to be fixed + * @param sourceLen the length of the input buffer + * @see ucnv_isAmbiguous + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_fixFileSeparator(const UConverter *cnv, UChar *source, int32_t sourceLen); + +/** + * Determines if the converter contains ambiguous mappings of the same + * character or not. + * @param cnv the converter to be tested + * @return true if the converter contains ambiguous mapping of the same + * character, false otherwise. + * @stable ICU 2.0 + */ +U_CAPI UBool U_EXPORT2 +ucnv_isAmbiguous(const UConverter *cnv); + +/** + * Sets the converter to use fallback mappings or not. + * Regardless of this flag, the converter will always use + * fallbacks from Unicode Private Use code points, as well as + * reverse fallbacks (to Unicode). + * For details see ".ucm File Format" + * in the Conversion Data chapter of the ICU User Guide: + * https://unicode-org.github.io/icu/userguide/conversion/data.html#ucm-file-format + * + * @param cnv The converter to set the fallback mapping usage on. + * @param usesFallback true if the user wants the converter to take advantage of the fallback + * mapping, false otherwise. + * @stable ICU 2.0 + * @see ucnv_usesFallback + */ +U_CAPI void U_EXPORT2 +ucnv_setFallback(UConverter *cnv, UBool usesFallback); + +/** + * Determines if the converter uses fallback mappings or not. + * This flag has restrictions, see ucnv_setFallback(). + * + * @param cnv The converter to be tested + * @return true if the converter uses fallback, false otherwise. + * @stable ICU 2.0 + * @see ucnv_setFallback + */ +U_CAPI UBool U_EXPORT2 +ucnv_usesFallback(const UConverter *cnv); + +/** + * Detects Unicode signature byte sequences at the start of the byte stream + * and returns the charset name of the indicated Unicode charset. + * NULL is returned when no Unicode signature is recognized. + * The number of bytes in the signature is output as well. + * + * The caller can ucnv_open() a converter using the charset name. + * The first code unit (UChar) from the start of the stream will be U+FEFF + * (the Unicode BOM/signature character) and can usually be ignored. + * + * For most Unicode charsets it is also possible to ignore the indicated + * number of initial stream bytes and start converting after them. + * However, there are stateful Unicode charsets (UTF-7 and BOCU-1) for which + * this will not work. Therefore, it is best to ignore the first output UChar + * instead of the input signature bytes. + *

+ * Usage: + * \snippet samples/ucnv/convsamp.cpp ucnv_detectUnicodeSignature + * + * @param source The source string in which the signature should be detected. + * @param sourceLength Length of the input string, or -1 if terminated with a NUL byte. + * @param signatureLength A pointer to int32_t to receive the number of bytes that make up the signature + * of the detected UTF. 0 if not detected. + * Can be a NULL pointer. + * @param pErrorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return The name of the encoding detected. NULL if encoding is not detected. + * @stable ICU 2.4 + */ +U_CAPI const char* U_EXPORT2 +ucnv_detectUnicodeSignature(const char* source, + int32_t sourceLength, + int32_t *signatureLength, + UErrorCode *pErrorCode); + +/** + * Returns the number of UChars held in the converter's internal state + * because more input is needed for completing the conversion. This function is + * useful for mapping semantics of ICU's converter interface to those of iconv, + * and this information is not needed for normal conversion. + * @param cnv The converter in which the input is held + * @param status ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return The number of UChars in the state. -1 if an error is encountered. + * @stable ICU 3.4 + */ +U_CAPI int32_t U_EXPORT2 +ucnv_fromUCountPending(const UConverter* cnv, UErrorCode* status); + +/** + * Returns the number of chars held in the converter's internal state + * because more input is needed for completing the conversion. This function is + * useful for mapping semantics of ICU's converter interface to those of iconv, + * and this information is not needed for normal conversion. + * @param cnv The converter in which the input is held as internal state + * @param status ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return The number of chars in the state. -1 if an error is encountered. + * @stable ICU 3.4 + */ +U_CAPI int32_t U_EXPORT2 +ucnv_toUCountPending(const UConverter* cnv, UErrorCode* status); + +/** + * Returns whether or not the charset of the converter has a fixed number of bytes + * per charset character. + * An example of this are converters that are of the type UCNV_SBCS or UCNV_DBCS. + * Another example is UTF-32 which is always 4 bytes per character. + * A Unicode code point may be represented by more than one UTF-8 or UTF-16 code unit + * but a UTF-32 converter encodes each code point with 4 bytes. + * Note: This method is not intended to be used to determine whether the charset has a + * fixed ratio of bytes to Unicode codes units for any particular Unicode encoding form. + * false is returned with the UErrorCode if error occurs or cnv is NULL. + * @param cnv The converter to be tested + * @param status ICU error code in/out parameter + * @return true if the converter is fixed-width + * @stable ICU 4.8 + */ +U_CAPI UBool U_EXPORT2 +ucnv_isFixedWidth(UConverter *cnv, UErrorCode *status); + +#endif + +#endif +/*_UCNV*/ diff --git a/deps/icu-small/source/common/unicode/ucnv_cb.h b/deps/icu-small/source/common/unicode/ucnv_cb.h index b4ef99208b15dd..accfaf125504dd 100644 --- a/deps/icu-small/source/common/unicode/ucnv_cb.h +++ b/deps/icu-small/source/common/unicode/ucnv_cb.h @@ -1,164 +1,164 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2000-2004, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** - * ucnv_cb.h: - * External APIs for the ICU's codeset conversion library - * Helena Shih - * - * Modification History: - * - * Date Name Description - */ - -/** - * \file - * \brief C API: UConverter functions to aid the writers of callbacks - * - *

Callback API for UConverter

- * - * These functions are provided here for the convenience of the callback - * writer. If you are just looking for callback functions to use, please - * see ucnv_err.h. DO NOT call these functions directly when you are - * working with converters, unless your code has been called as a callback - * via ucnv_setFromUCallback or ucnv_setToUCallback !! - * - * A note about error codes and overflow. Unlike other ICU functions, - * these functions do not expect the error status to be U_ZERO_ERROR. - * Callbacks must be much more careful about their error codes. - * The error codes used here are in/out parameters, which should be passed - * back in the callback's error parameter. - * - * For example, if you call ucnv_cbfromUWriteBytes to write data out - * to the output codepage, it may return U_BUFFER_OVERFLOW_ERROR if - * the data did not fit in the target. But this isn't a failing error, - * in fact, ucnv_cbfromUWriteBytes may be called AGAIN with the error - * status still U_BUFFER_OVERFLOW_ERROR to attempt to write further bytes, - * which will also go into the internal overflow buffers. - * - * Concerning offsets, the 'offset' parameters here are relative to the start - * of SOURCE. For example, Suppose the string "ABCD" was being converted - * from Unicode into a codepage which doesn't have a mapping for 'B'. - * 'A' will be written out correctly, but - * The FromU Callback will be called on an unassigned character for 'B'. - * At this point, this is the state of the world: - * Target: A [..] [points after A] - * Source: A B [C] D [points to C - B has been consumed] - * 0 1 2 3 - * codePoint = "B" [the unassigned codepoint] - * - * Now, suppose a callback wants to write the substitution character '?' to - * the target. It calls ucnv_cbFromUWriteBytes() to write the ?. - * It should pass ZERO as the offset, because the offset as far as the - * callback is concerned is relative to the SOURCE pointer [which points - * before 'C'.] If the callback goes into the args and consumes 'C' also, - * it would call FromUWriteBytes with an offset of 1 (and advance the source - * pointer). - * - */ - -#ifndef UCNV_CB_H -#define UCNV_CB_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ucnv.h" -#include "unicode/ucnv_err.h" - -/** - * ONLY used by FromU callback functions. - * Writes out the specified byte output bytes to the target byte buffer or to converter internal buffers. - * - * @param args callback fromUnicode arguments - * @param source source bytes to write - * @param length length of bytes to write - * @param offsetIndex the relative offset index from callback. - * @param err error status. If U_BUFFER_OVERFLOW is returned, then U_BUFFER_OVERFLOW must - * be returned to the user, because it means that not all data could be written into the target buffer, and some is - * in the converter error buffer. - * @see ucnv_cbFromUWriteSub - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_cbFromUWriteBytes (UConverterFromUnicodeArgs *args, - const char* source, - int32_t length, - int32_t offsetIndex, - UErrorCode * err); - -/** - * ONLY used by FromU callback functions. - * This function will write out the correct substitution character sequence - * to the target. - * - * @param args callback fromUnicode arguments - * @param offsetIndex the relative offset index from the current source pointer to be used - * @param err error status. If U_BUFFER_OVERFLOW is returned, then U_BUFFER_OVERFLOW must - * be returned to the user, because it means that not all data could be written into the target buffer, and some is - * in the converter error buffer. - * @see ucnv_cbFromUWriteBytes - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ucnv_cbFromUWriteSub (UConverterFromUnicodeArgs *args, - int32_t offsetIndex, - UErrorCode * err); - -/** - * ONLY used by fromU callback functions. - * This function will write out the error character(s) to the target UChar buffer. - * - * @param args callback fromUnicode arguments - * @param source pointer to pointer to first UChar to write [on exit: 1 after last UChar processed] - * @param sourceLimit pointer after last UChar to write - * @param offsetIndex the relative offset index from callback which will be set - * @param err error status U_BUFFER_OVERFLOW - * @see ucnv_cbToUWriteSub - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 ucnv_cbFromUWriteUChars(UConverterFromUnicodeArgs *args, - const UChar** source, - const UChar* sourceLimit, - int32_t offsetIndex, - UErrorCode * err); - -/** - * ONLY used by ToU callback functions. - * This function will write out the specified characters to the target - * UChar buffer. - * - * @param args callback toUnicode arguments - * @param source source string to write - * @param length the length of source string - * @param offsetIndex the relative offset index which will be written. - * @param err error status U_BUFFER_OVERFLOW - * @see ucnv_cbToUWriteSub - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 ucnv_cbToUWriteUChars (UConverterToUnicodeArgs *args, - const UChar* source, - int32_t length, - int32_t offsetIndex, - UErrorCode * err); - -/** - * ONLY used by ToU callback functions. - * This function will write out the Unicode substitution character (U+FFFD). - * - * @param args callback fromUnicode arguments - * @param offsetIndex the relative offset index from callback. - * @param err error status U_BUFFER_OVERFLOW - * @see ucnv_cbToUWriteUChars - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 ucnv_cbToUWriteSub (UConverterToUnicodeArgs *args, - int32_t offsetIndex, - UErrorCode * err); -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2000-2004, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** + * ucnv_cb.h: + * External APIs for the ICU's codeset conversion library + * Helena Shih + * + * Modification History: + * + * Date Name Description + */ + +/** + * \file + * \brief C API: UConverter functions to aid the writers of callbacks + * + *

Callback API for UConverter

+ * + * These functions are provided here for the convenience of the callback + * writer. If you are just looking for callback functions to use, please + * see ucnv_err.h. DO NOT call these functions directly when you are + * working with converters, unless your code has been called as a callback + * via ucnv_setFromUCallback or ucnv_setToUCallback !! + * + * A note about error codes and overflow. Unlike other ICU functions, + * these functions do not expect the error status to be U_ZERO_ERROR. + * Callbacks must be much more careful about their error codes. + * The error codes used here are in/out parameters, which should be passed + * back in the callback's error parameter. + * + * For example, if you call ucnv_cbfromUWriteBytes to write data out + * to the output codepage, it may return U_BUFFER_OVERFLOW_ERROR if + * the data did not fit in the target. But this isn't a failing error, + * in fact, ucnv_cbfromUWriteBytes may be called AGAIN with the error + * status still U_BUFFER_OVERFLOW_ERROR to attempt to write further bytes, + * which will also go into the internal overflow buffers. + * + * Concerning offsets, the 'offset' parameters here are relative to the start + * of SOURCE. For example, Suppose the string "ABCD" was being converted + * from Unicode into a codepage which doesn't have a mapping for 'B'. + * 'A' will be written out correctly, but + * The FromU Callback will be called on an unassigned character for 'B'. + * At this point, this is the state of the world: + * Target: A [..] [points after A] + * Source: A B [C] D [points to C - B has been consumed] + * 0 1 2 3 + * codePoint = "B" [the unassigned codepoint] + * + * Now, suppose a callback wants to write the substitution character '?' to + * the target. It calls ucnv_cbFromUWriteBytes() to write the ?. + * It should pass ZERO as the offset, because the offset as far as the + * callback is concerned is relative to the SOURCE pointer [which points + * before 'C'.] If the callback goes into the args and consumes 'C' also, + * it would call FromUWriteBytes with an offset of 1 (and advance the source + * pointer). + * + */ + +#ifndef UCNV_CB_H +#define UCNV_CB_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ucnv.h" +#include "unicode/ucnv_err.h" + +/** + * ONLY used by FromU callback functions. + * Writes out the specified byte output bytes to the target byte buffer or to converter internal buffers. + * + * @param args callback fromUnicode arguments + * @param source source bytes to write + * @param length length of bytes to write + * @param offsetIndex the relative offset index from callback. + * @param err error status. If U_BUFFER_OVERFLOW is returned, then U_BUFFER_OVERFLOW must + * be returned to the user, because it means that not all data could be written into the target buffer, and some is + * in the converter error buffer. + * @see ucnv_cbFromUWriteSub + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_cbFromUWriteBytes (UConverterFromUnicodeArgs *args, + const char* source, + int32_t length, + int32_t offsetIndex, + UErrorCode * err); + +/** + * ONLY used by FromU callback functions. + * This function will write out the correct substitution character sequence + * to the target. + * + * @param args callback fromUnicode arguments + * @param offsetIndex the relative offset index from the current source pointer to be used + * @param err error status. If U_BUFFER_OVERFLOW is returned, then U_BUFFER_OVERFLOW must + * be returned to the user, because it means that not all data could be written into the target buffer, and some is + * in the converter error buffer. + * @see ucnv_cbFromUWriteBytes + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ucnv_cbFromUWriteSub (UConverterFromUnicodeArgs *args, + int32_t offsetIndex, + UErrorCode * err); + +/** + * ONLY used by fromU callback functions. + * This function will write out the error character(s) to the target UChar buffer. + * + * @param args callback fromUnicode arguments + * @param source pointer to pointer to first UChar to write [on exit: 1 after last UChar processed] + * @param sourceLimit pointer after last UChar to write + * @param offsetIndex the relative offset index from callback which will be set + * @param err error status U_BUFFER_OVERFLOW + * @see ucnv_cbToUWriteSub + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 ucnv_cbFromUWriteUChars(UConverterFromUnicodeArgs *args, + const UChar** source, + const UChar* sourceLimit, + int32_t offsetIndex, + UErrorCode * err); + +/** + * ONLY used by ToU callback functions. + * This function will write out the specified characters to the target + * UChar buffer. + * + * @param args callback toUnicode arguments + * @param source source string to write + * @param length the length of source string + * @param offsetIndex the relative offset index which will be written. + * @param err error status U_BUFFER_OVERFLOW + * @see ucnv_cbToUWriteSub + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 ucnv_cbToUWriteUChars (UConverterToUnicodeArgs *args, + const UChar* source, + int32_t length, + int32_t offsetIndex, + UErrorCode * err); + +/** + * ONLY used by ToU callback functions. + * This function will write out the Unicode substitution character (U+FFFD). + * + * @param args callback fromUnicode arguments + * @param offsetIndex the relative offset index from callback. + * @param err error status U_BUFFER_OVERFLOW + * @see ucnv_cbToUWriteUChars + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 ucnv_cbToUWriteSub (UConverterToUnicodeArgs *args, + int32_t offsetIndex, + UErrorCode * err); +#endif + +#endif diff --git a/deps/icu-small/source/common/unicode/ucnv_err.h b/deps/icu-small/source/common/unicode/ucnv_err.h index c743e5614f4ade..f52f8f17146305 100644 --- a/deps/icu-small/source/common/unicode/ucnv_err.h +++ b/deps/icu-small/source/common/unicode/ucnv_err.h @@ -1,465 +1,465 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2009, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** - * - * - * ucnv_err.h: - */ - -/** - * \file - * \brief C API: UConverter predefined error callbacks - * - *

Error Behaviour Functions

- * Defines some error behaviour functions called by ucnv_{from,to}Unicode - * These are provided as part of ICU and many are stable, but they - * can also be considered only as an example of what can be done with - * callbacks. You may of course write your own. - * - * If you want to write your own, you may also find the functions from - * ucnv_cb.h useful when writing your own callbacks. - * - * These functions, although public, should NEVER be called directly. - * They should be used as parameters to the ucnv_setFromUCallback - * and ucnv_setToUCallback functions, to set the behaviour of a converter - * when it encounters ILLEGAL/UNMAPPED/INVALID sequences. - * - * usage example: 'STOP' doesn't need any context, but newContext - * could be set to something other than 'NULL' if needed. The available - * contexts in this header can modify the default behavior of the callback. - * - * \code - * UErrorCode err = U_ZERO_ERROR; - * UConverter *myConverter = ucnv_open("ibm-949", &err); - * const void *oldContext; - * UConverterFromUCallback oldAction; - * - * - * if (U_SUCCESS(err)) - * { - * ucnv_setFromUCallBack(myConverter, - * UCNV_FROM_U_CALLBACK_STOP, - * NULL, - * &oldAction, - * &oldContext, - * &status); - * } - * \endcode - * - * The code above tells "myConverter" to stop when it encounters an - * ILLEGAL/TRUNCATED/INVALID sequences when it is used to convert from - * Unicode -> Codepage. The behavior from Codepage to Unicode is not changed, - * and ucnv_setToUCallBack would need to be called in order to change - * that behavior too. - * - * Here is an example with a context: - * - * \code - * UErrorCode err = U_ZERO_ERROR; - * UConverter *myConverter = ucnv_open("ibm-949", &err); - * const void *oldContext; - * UConverterFromUCallback oldAction; - * - * - * if (U_SUCCESS(err)) - * { - * ucnv_setToUCallBack(myConverter, - * UCNV_TO_U_CALLBACK_SUBSTITUTE, - * UCNV_SUB_STOP_ON_ILLEGAL, - * &oldAction, - * &oldContext, - * &status); - * } - * \endcode - * - * The code above tells "myConverter" to stop when it encounters an - * ILLEGAL/TRUNCATED/INVALID sequences when it is used to convert from - * Codepage -> Unicode. Any unmapped and legal characters will be - * substituted to be the default substitution character. - */ - -#ifndef UCNV_ERR_H -#define UCNV_ERR_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -/** Forward declaring the UConverter structure. @stable ICU 2.0 */ -struct UConverter; - -/** @stable ICU 2.0 */ -typedef struct UConverter UConverter; - -/** - * FROM_U, TO_U context options for sub callback - * @stable ICU 2.0 - */ -#define UCNV_SUB_STOP_ON_ILLEGAL "i" - -/** - * FROM_U, TO_U context options for skip callback - * @stable ICU 2.0 - */ -#define UCNV_SKIP_STOP_ON_ILLEGAL "i" - -/** - * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to ICU (%UXXXX) - * @stable ICU 2.0 - */ -#define UCNV_ESCAPE_ICU NULL -/** - * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to JAVA (\\uXXXX) - * @stable ICU 2.0 - */ -#define UCNV_ESCAPE_JAVA "J" -/** - * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to C (\\uXXXX \\UXXXXXXXX) - * TO_U_CALLBACK_ESCAPE option to escape the character value according to C (\\xXXXX) - * @stable ICU 2.0 - */ -#define UCNV_ESCAPE_C "C" -/** - * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to XML Decimal escape \htmlonly(&#DDDD;)\endhtmlonly - * TO_U_CALLBACK_ESCAPE context option to escape the character value according to XML Decimal escape \htmlonly(&#DDDD;)\endhtmlonly - * @stable ICU 2.0 - */ -#define UCNV_ESCAPE_XML_DEC "D" -/** - * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to XML Hex escape \htmlonly(&#xXXXX;)\endhtmlonly - * TO_U_CALLBACK_ESCAPE context option to escape the character value according to XML Hex escape \htmlonly(&#xXXXX;)\endhtmlonly - * @stable ICU 2.0 - */ -#define UCNV_ESCAPE_XML_HEX "X" -/** - * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to Unicode (U+XXXXX) - * @stable ICU 2.0 - */ -#define UCNV_ESCAPE_UNICODE "U" - -/** - * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to CSS2 conventions (\\HH..H, that is, - * a backslash, 1..6 hex digits, and a space) - * @stable ICU 4.0 - */ -#define UCNV_ESCAPE_CSS2 "S" - -/** - * The process condition code to be used with the callbacks. - * Codes which are greater than UCNV_IRREGULAR should be - * passed on to any chained callbacks. - * @stable ICU 2.0 - */ -typedef enum { - UCNV_UNASSIGNED = 0, /**< The code point is unassigned. - The error code U_INVALID_CHAR_FOUND will be set. */ - UCNV_ILLEGAL = 1, /**< The code point is illegal. For example, - \\x81\\x2E is illegal in SJIS because \\x2E - is not a valid trail byte for the \\x81 - lead byte. - Also, starting with Unicode 3.0.1, non-shortest byte sequences - in UTF-8 (like \\xC1\\xA1 instead of \\x61 for U+0061) - are also illegal, not just irregular. - The error code U_ILLEGAL_CHAR_FOUND will be set. */ - UCNV_IRREGULAR = 2, /**< The codepoint is not a regular sequence in - the encoding. For example, \\xED\\xA0\\x80..\\xED\\xBF\\xBF - are irregular UTF-8 byte sequences for single surrogate - code points. - The error code U_INVALID_CHAR_FOUND will be set. */ - UCNV_RESET = 3, /**< The callback is called with this reason when a - 'reset' has occurred. Callback should reset all - state. */ - UCNV_CLOSE = 4, /**< Called when the converter is closed. The - callback should release any allocated memory.*/ - UCNV_CLONE = 5 /**< Called when ucnv_safeClone() is called on the - converter. the pointer available as the - 'context' is an alias to the original converters' - context pointer. If the context must be owned - by the new converter, the callback must clone - the data and call ucnv_setFromUCallback - (or setToUCallback) with the correct pointer. - @stable ICU 2.2 - */ -} UConverterCallbackReason; - - -/** - * The structure for the fromUnicode callback function parameter. - * @stable ICU 2.0 - */ -typedef struct { - uint16_t size; /**< The size of this struct. @stable ICU 2.0 */ - UBool flush; /**< The internal state of converter will be reset and data flushed if set to true. @stable ICU 2.0 */ - UConverter *converter; /**< Pointer to the converter that is opened and to which this struct is passed as an argument. @stable ICU 2.0 */ - const UChar *source; /**< Pointer to the source source buffer. @stable ICU 2.0 */ - const UChar *sourceLimit; /**< Pointer to the limit (end + 1) of source buffer. @stable ICU 2.0 */ - char *target; /**< Pointer to the target buffer. @stable ICU 2.0 */ - const char *targetLimit; /**< Pointer to the limit (end + 1) of target buffer. @stable ICU 2.0 */ - int32_t *offsets; /**< Pointer to the buffer that receives the offsets. *offset = blah ; offset++;. @stable ICU 2.0 */ -} UConverterFromUnicodeArgs; - - -/** - * The structure for the toUnicode callback function parameter. - * @stable ICU 2.0 - */ -typedef struct { - uint16_t size; /**< The size of this struct @stable ICU 2.0 */ - UBool flush; /**< The internal state of converter will be reset and data flushed if set to true. @stable ICU 2.0 */ - UConverter *converter; /**< Pointer to the converter that is opened and to which this struct is passed as an argument. @stable ICU 2.0 */ - const char *source; /**< Pointer to the source source buffer. @stable ICU 2.0 */ - const char *sourceLimit; /**< Pointer to the limit (end + 1) of source buffer. @stable ICU 2.0 */ - UChar *target; /**< Pointer to the target buffer. @stable ICU 2.0 */ - const UChar *targetLimit; /**< Pointer to the limit (end + 1) of target buffer. @stable ICU 2.0 */ - int32_t *offsets; /**< Pointer to the buffer that receives the offsets. *offset = blah ; offset++;. @stable ICU 2.0 */ -} UConverterToUnicodeArgs; - - -/** - * DO NOT CALL THIS FUNCTION DIRECTLY! - * This From Unicode callback STOPS at the ILLEGAL_SEQUENCE, - * returning the error code back to the caller immediately. - * - * @param context Pointer to the callback's private data - * @param fromUArgs Information about the conversion in progress - * @param codeUnits Points to 'length' UChars of the concerned Unicode sequence - * @param length Size (in bytes) of the concerned codepage sequence - * @param codePoint Single UChar32 (UTF-32) containing the concerend Unicode codepoint. - * @param reason Defines the reason the callback was invoked - * @param err This should always be set to a failure status prior to calling. - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 UCNV_FROM_U_CALLBACK_STOP ( - const void *context, - UConverterFromUnicodeArgs *fromUArgs, - const UChar* codeUnits, - int32_t length, - UChar32 codePoint, - UConverterCallbackReason reason, - UErrorCode * err); - - - -/** - * DO NOT CALL THIS FUNCTION DIRECTLY! - * This To Unicode callback STOPS at the ILLEGAL_SEQUENCE, - * returning the error code back to the caller immediately. - * - * @param context Pointer to the callback's private data - * @param toUArgs Information about the conversion in progress - * @param codeUnits Points to 'length' bytes of the concerned codepage sequence - * @param length Size (in bytes) of the concerned codepage sequence - * @param reason Defines the reason the callback was invoked - * @param err This should always be set to a failure status prior to calling. - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 UCNV_TO_U_CALLBACK_STOP ( - const void *context, - UConverterToUnicodeArgs *toUArgs, - const char* codeUnits, - int32_t length, - UConverterCallbackReason reason, - UErrorCode * err); - -/** - * DO NOT CALL THIS FUNCTION DIRECTLY! - * This From Unicode callback skips any ILLEGAL_SEQUENCE, or - * skips only UNASSIGNED_SEQUENCE depending on the context parameter - * simply ignoring those characters. - * - * @param context The function currently recognizes the callback options: - * UCNV_SKIP_STOP_ON_ILLEGAL: STOPS at the ILLEGAL_SEQUENCE, - * returning the error code back to the caller immediately. - * NULL: Skips any ILLEGAL_SEQUENCE - * @param fromUArgs Information about the conversion in progress - * @param codeUnits Points to 'length' UChars of the concerned Unicode sequence - * @param length Size (in bytes) of the concerned codepage sequence - * @param codePoint Single UChar32 (UTF-32) containing the concerend Unicode codepoint. - * @param reason Defines the reason the callback was invoked - * @param err Return value will be set to success if the callback was handled, - * otherwise this value will be set to a failure status. - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 UCNV_FROM_U_CALLBACK_SKIP ( - const void *context, - UConverterFromUnicodeArgs *fromUArgs, - const UChar* codeUnits, - int32_t length, - UChar32 codePoint, - UConverterCallbackReason reason, - UErrorCode * err); - -/** - * DO NOT CALL THIS FUNCTION DIRECTLY! - * This From Unicode callback will Substitute the ILLEGAL SEQUENCE, or - * UNASSIGNED_SEQUENCE depending on context parameter, with the - * current substitution string for the converter. This is the default - * callback. - * - * @param context The function currently recognizes the callback options: - * UCNV_SUB_STOP_ON_ILLEGAL: STOPS at the ILLEGAL_SEQUENCE, - * returning the error code back to the caller immediately. - * NULL: Substitutes any ILLEGAL_SEQUENCE - * @param fromUArgs Information about the conversion in progress - * @param codeUnits Points to 'length' UChars of the concerned Unicode sequence - * @param length Size (in bytes) of the concerned codepage sequence - * @param codePoint Single UChar32 (UTF-32) containing the concerend Unicode codepoint. - * @param reason Defines the reason the callback was invoked - * @param err Return value will be set to success if the callback was handled, - * otherwise this value will be set to a failure status. - * @see ucnv_setSubstChars - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 UCNV_FROM_U_CALLBACK_SUBSTITUTE ( - const void *context, - UConverterFromUnicodeArgs *fromUArgs, - const UChar* codeUnits, - int32_t length, - UChar32 codePoint, - UConverterCallbackReason reason, - UErrorCode * err); - -/** - * DO NOT CALL THIS FUNCTION DIRECTLY! - * This From Unicode callback will Substitute the ILLEGAL SEQUENCE with the - * hexadecimal representation of the illegal codepoints - * - * @param context The function currently recognizes the callback options: - *
    - *
  • UCNV_ESCAPE_ICU: Substitutes the ILLEGAL SEQUENCE with the hexadecimal - * representation in the format %UXXXX, e.g. "%uFFFE%u00AC%uC8FE"). - * In the Event the converter doesn't support the characters {%,U}[A-F][0-9], - * it will substitute the illegal sequence with the substitution characters. - * Note that codeUnit(32bit int eg: unit of a surrogate pair) is represented as - * %UD84D%UDC56
  • - *
  • UCNV_ESCAPE_JAVA: Substitutes the ILLEGAL SEQUENCE with the hexadecimal - * representation in the format \\uXXXX, e.g. "\\uFFFE\\u00AC\\uC8FE"). - * In the Event the converter doesn't support the characters {\,u}[A-F][0-9], - * it will substitute the illegal sequence with the substitution characters. - * Note that codeUnit(32bit int eg: unit of a surrogate pair) is represented as - * \\uD84D\\uDC56
  • - *
  • UCNV_ESCAPE_C: Substitutes the ILLEGAL SEQUENCE with the hexadecimal - * representation in the format \\uXXXX, e.g. "\\uFFFE\\u00AC\\uC8FE"). - * In the Event the converter doesn't support the characters {\,u,U}[A-F][0-9], - * it will substitute the illegal sequence with the substitution characters. - * Note that codeUnit(32bit int eg: unit of a surrogate pair) is represented as - * \\U00023456
  • - *
  • UCNV_ESCAPE_XML_DEC: Substitutes the ILLEGAL SEQUENCE with the decimal - * representation in the format \htmlonly&#DDDDDDDD;, e.g. "&#65534;&#172;&#51454;")\endhtmlonly. - * In the Event the converter doesn't support the characters {&,#}[0-9], - * it will substitute the illegal sequence with the substitution characters. - * Note that codeUnit(32bit int eg: unit of a surrogate pair) is represented as - * &#144470; and Zero padding is ignored.
  • - *
  • UCNV_ESCAPE_XML_HEX:Substitutes the ILLEGAL SEQUENCE with the decimal - * representation in the format \htmlonly&#xXXXX; e.g. "&#xFFFE;&#x00AC;&#xC8FE;")\endhtmlonly. - * In the Event the converter doesn't support the characters {&,#,x}[0-9], - * it will substitute the illegal sequence with the substitution characters. - * Note that codeUnit(32bit int eg: unit of a surrogate pair) is represented as - * \htmlonly&#x23456;\endhtmlonly
  • - *
- * @param fromUArgs Information about the conversion in progress - * @param codeUnits Points to 'length' UChars of the concerned Unicode sequence - * @param length Size (in bytes) of the concerned codepage sequence - * @param codePoint Single UChar32 (UTF-32) containing the concerend Unicode codepoint. - * @param reason Defines the reason the callback was invoked - * @param err Return value will be set to success if the callback was handled, - * otherwise this value will be set to a failure status. - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 UCNV_FROM_U_CALLBACK_ESCAPE ( - const void *context, - UConverterFromUnicodeArgs *fromUArgs, - const UChar* codeUnits, - int32_t length, - UChar32 codePoint, - UConverterCallbackReason reason, - UErrorCode * err); - - -/** - * DO NOT CALL THIS FUNCTION DIRECTLY! - * This To Unicode callback skips any ILLEGAL_SEQUENCE, or - * skips only UNASSIGNED_SEQUENCE depending on the context parameter - * simply ignoring those characters. - * - * @param context The function currently recognizes the callback options: - * UCNV_SKIP_STOP_ON_ILLEGAL: STOPS at the ILLEGAL_SEQUENCE, - * returning the error code back to the caller immediately. - * NULL: Skips any ILLEGAL_SEQUENCE - * @param toUArgs Information about the conversion in progress - * @param codeUnits Points to 'length' bytes of the concerned codepage sequence - * @param length Size (in bytes) of the concerned codepage sequence - * @param reason Defines the reason the callback was invoked - * @param err Return value will be set to success if the callback was handled, - * otherwise this value will be set to a failure status. - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 UCNV_TO_U_CALLBACK_SKIP ( - const void *context, - UConverterToUnicodeArgs *toUArgs, - const char* codeUnits, - int32_t length, - UConverterCallbackReason reason, - UErrorCode * err); - -/** - * DO NOT CALL THIS FUNCTION DIRECTLY! - * This To Unicode callback will Substitute the ILLEGAL SEQUENCE,or - * UNASSIGNED_SEQUENCE depending on context parameter, with the - * Unicode substitution character, U+FFFD. - * - * @param context The function currently recognizes the callback options: - * UCNV_SUB_STOP_ON_ILLEGAL: STOPS at the ILLEGAL_SEQUENCE, - * returning the error code back to the caller immediately. - * NULL: Substitutes any ILLEGAL_SEQUENCE - * @param toUArgs Information about the conversion in progress - * @param codeUnits Points to 'length' bytes of the concerned codepage sequence - * @param length Size (in bytes) of the concerned codepage sequence - * @param reason Defines the reason the callback was invoked - * @param err Return value will be set to success if the callback was handled, - * otherwise this value will be set to a failure status. - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 UCNV_TO_U_CALLBACK_SUBSTITUTE ( - const void *context, - UConverterToUnicodeArgs *toUArgs, - const char* codeUnits, - int32_t length, - UConverterCallbackReason reason, - UErrorCode * err); - -/** - * DO NOT CALL THIS FUNCTION DIRECTLY! - * This To Unicode callback will Substitute the ILLEGAL SEQUENCE with the - * hexadecimal representation of the illegal bytes - * (in the format %XNN, e.g. "%XFF%X0A%XC8%X03"). - * - * @param context This function currently recognizes the callback options: - * UCNV_ESCAPE_ICU, UCNV_ESCAPE_JAVA, UCNV_ESCAPE_C, UCNV_ESCAPE_XML_DEC, - * UCNV_ESCAPE_XML_HEX and UCNV_ESCAPE_UNICODE. - * @param toUArgs Information about the conversion in progress - * @param codeUnits Points to 'length' bytes of the concerned codepage sequence - * @param length Size (in bytes) of the concerned codepage sequence - * @param reason Defines the reason the callback was invoked - * @param err Return value will be set to success if the callback was handled, - * otherwise this value will be set to a failure status. - * @stable ICU 2.0 - */ - -U_CAPI void U_EXPORT2 UCNV_TO_U_CALLBACK_ESCAPE ( - const void *context, - UConverterToUnicodeArgs *toUArgs, - const char* codeUnits, - int32_t length, - UConverterCallbackReason reason, - UErrorCode * err); - -#endif - -#endif - -/*UCNV_ERR_H*/ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2009, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** + * + * + * ucnv_err.h: + */ + +/** + * \file + * \brief C API: UConverter predefined error callbacks + * + *

Error Behaviour Functions

+ * Defines some error behaviour functions called by ucnv_{from,to}Unicode + * These are provided as part of ICU and many are stable, but they + * can also be considered only as an example of what can be done with + * callbacks. You may of course write your own. + * + * If you want to write your own, you may also find the functions from + * ucnv_cb.h useful when writing your own callbacks. + * + * These functions, although public, should NEVER be called directly. + * They should be used as parameters to the ucnv_setFromUCallback + * and ucnv_setToUCallback functions, to set the behaviour of a converter + * when it encounters ILLEGAL/UNMAPPED/INVALID sequences. + * + * usage example: 'STOP' doesn't need any context, but newContext + * could be set to something other than 'NULL' if needed. The available + * contexts in this header can modify the default behavior of the callback. + * + * \code + * UErrorCode err = U_ZERO_ERROR; + * UConverter *myConverter = ucnv_open("ibm-949", &err); + * const void *oldContext; + * UConverterFromUCallback oldAction; + * + * + * if (U_SUCCESS(err)) + * { + * ucnv_setFromUCallBack(myConverter, + * UCNV_FROM_U_CALLBACK_STOP, + * NULL, + * &oldAction, + * &oldContext, + * &status); + * } + * \endcode + * + * The code above tells "myConverter" to stop when it encounters an + * ILLEGAL/TRUNCATED/INVALID sequences when it is used to convert from + * Unicode -> Codepage. The behavior from Codepage to Unicode is not changed, + * and ucnv_setToUCallBack would need to be called in order to change + * that behavior too. + * + * Here is an example with a context: + * + * \code + * UErrorCode err = U_ZERO_ERROR; + * UConverter *myConverter = ucnv_open("ibm-949", &err); + * const void *oldContext; + * UConverterFromUCallback oldAction; + * + * + * if (U_SUCCESS(err)) + * { + * ucnv_setToUCallBack(myConverter, + * UCNV_TO_U_CALLBACK_SUBSTITUTE, + * UCNV_SUB_STOP_ON_ILLEGAL, + * &oldAction, + * &oldContext, + * &status); + * } + * \endcode + * + * The code above tells "myConverter" to stop when it encounters an + * ILLEGAL/TRUNCATED/INVALID sequences when it is used to convert from + * Codepage -> Unicode. Any unmapped and legal characters will be + * substituted to be the default substitution character. + */ + +#ifndef UCNV_ERR_H +#define UCNV_ERR_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +/** Forward declaring the UConverter structure. @stable ICU 2.0 */ +struct UConverter; + +/** @stable ICU 2.0 */ +typedef struct UConverter UConverter; + +/** + * FROM_U, TO_U context options for sub callback + * @stable ICU 2.0 + */ +#define UCNV_SUB_STOP_ON_ILLEGAL "i" + +/** + * FROM_U, TO_U context options for skip callback + * @stable ICU 2.0 + */ +#define UCNV_SKIP_STOP_ON_ILLEGAL "i" + +/** + * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to ICU (%UXXXX) + * @stable ICU 2.0 + */ +#define UCNV_ESCAPE_ICU NULL +/** + * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to JAVA (\\uXXXX) + * @stable ICU 2.0 + */ +#define UCNV_ESCAPE_JAVA "J" +/** + * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to C (\\uXXXX \\UXXXXXXXX) + * TO_U_CALLBACK_ESCAPE option to escape the character value according to C (\\xXXXX) + * @stable ICU 2.0 + */ +#define UCNV_ESCAPE_C "C" +/** + * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to XML Decimal escape \htmlonly(&#DDDD;)\endhtmlonly + * TO_U_CALLBACK_ESCAPE context option to escape the character value according to XML Decimal escape \htmlonly(&#DDDD;)\endhtmlonly + * @stable ICU 2.0 + */ +#define UCNV_ESCAPE_XML_DEC "D" +/** + * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to XML Hex escape \htmlonly(&#xXXXX;)\endhtmlonly + * TO_U_CALLBACK_ESCAPE context option to escape the character value according to XML Hex escape \htmlonly(&#xXXXX;)\endhtmlonly + * @stable ICU 2.0 + */ +#define UCNV_ESCAPE_XML_HEX "X" +/** + * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to Unicode (U+XXXXX) + * @stable ICU 2.0 + */ +#define UCNV_ESCAPE_UNICODE "U" + +/** + * FROM_U_CALLBACK_ESCAPE context option to escape the code unit according to CSS2 conventions (\\HH..H, that is, + * a backslash, 1..6 hex digits, and a space) + * @stable ICU 4.0 + */ +#define UCNV_ESCAPE_CSS2 "S" + +/** + * The process condition code to be used with the callbacks. + * Codes which are greater than UCNV_IRREGULAR should be + * passed on to any chained callbacks. + * @stable ICU 2.0 + */ +typedef enum { + UCNV_UNASSIGNED = 0, /**< The code point is unassigned. + The error code U_INVALID_CHAR_FOUND will be set. */ + UCNV_ILLEGAL = 1, /**< The code point is illegal. For example, + \\x81\\x2E is illegal in SJIS because \\x2E + is not a valid trail byte for the \\x81 + lead byte. + Also, starting with Unicode 3.0.1, non-shortest byte sequences + in UTF-8 (like \\xC1\\xA1 instead of \\x61 for U+0061) + are also illegal, not just irregular. + The error code U_ILLEGAL_CHAR_FOUND will be set. */ + UCNV_IRREGULAR = 2, /**< The codepoint is not a regular sequence in + the encoding. For example, \\xED\\xA0\\x80..\\xED\\xBF\\xBF + are irregular UTF-8 byte sequences for single surrogate + code points. + The error code U_INVALID_CHAR_FOUND will be set. */ + UCNV_RESET = 3, /**< The callback is called with this reason when a + 'reset' has occurred. Callback should reset all + state. */ + UCNV_CLOSE = 4, /**< Called when the converter is closed. The + callback should release any allocated memory.*/ + UCNV_CLONE = 5 /**< Called when ucnv_safeClone() is called on the + converter. the pointer available as the + 'context' is an alias to the original converters' + context pointer. If the context must be owned + by the new converter, the callback must clone + the data and call ucnv_setFromUCallback + (or setToUCallback) with the correct pointer. + @stable ICU 2.2 + */ +} UConverterCallbackReason; + + +/** + * The structure for the fromUnicode callback function parameter. + * @stable ICU 2.0 + */ +typedef struct { + uint16_t size; /**< The size of this struct. @stable ICU 2.0 */ + UBool flush; /**< The internal state of converter will be reset and data flushed if set to true. @stable ICU 2.0 */ + UConverter *converter; /**< Pointer to the converter that is opened and to which this struct is passed as an argument. @stable ICU 2.0 */ + const UChar *source; /**< Pointer to the source source buffer. @stable ICU 2.0 */ + const UChar *sourceLimit; /**< Pointer to the limit (end + 1) of source buffer. @stable ICU 2.0 */ + char *target; /**< Pointer to the target buffer. @stable ICU 2.0 */ + const char *targetLimit; /**< Pointer to the limit (end + 1) of target buffer. @stable ICU 2.0 */ + int32_t *offsets; /**< Pointer to the buffer that receives the offsets. *offset = blah ; offset++;. @stable ICU 2.0 */ +} UConverterFromUnicodeArgs; + + +/** + * The structure for the toUnicode callback function parameter. + * @stable ICU 2.0 + */ +typedef struct { + uint16_t size; /**< The size of this struct @stable ICU 2.0 */ + UBool flush; /**< The internal state of converter will be reset and data flushed if set to true. @stable ICU 2.0 */ + UConverter *converter; /**< Pointer to the converter that is opened and to which this struct is passed as an argument. @stable ICU 2.0 */ + const char *source; /**< Pointer to the source source buffer. @stable ICU 2.0 */ + const char *sourceLimit; /**< Pointer to the limit (end + 1) of source buffer. @stable ICU 2.0 */ + UChar *target; /**< Pointer to the target buffer. @stable ICU 2.0 */ + const UChar *targetLimit; /**< Pointer to the limit (end + 1) of target buffer. @stable ICU 2.0 */ + int32_t *offsets; /**< Pointer to the buffer that receives the offsets. *offset = blah ; offset++;. @stable ICU 2.0 */ +} UConverterToUnicodeArgs; + + +/** + * DO NOT CALL THIS FUNCTION DIRECTLY! + * This From Unicode callback STOPS at the ILLEGAL_SEQUENCE, + * returning the error code back to the caller immediately. + * + * @param context Pointer to the callback's private data + * @param fromUArgs Information about the conversion in progress + * @param codeUnits Points to 'length' UChars of the concerned Unicode sequence + * @param length Size (in bytes) of the concerned codepage sequence + * @param codePoint Single UChar32 (UTF-32) containing the concerend Unicode codepoint. + * @param reason Defines the reason the callback was invoked + * @param err This should always be set to a failure status prior to calling. + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 UCNV_FROM_U_CALLBACK_STOP ( + const void *context, + UConverterFromUnicodeArgs *fromUArgs, + const UChar* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode * err); + + + +/** + * DO NOT CALL THIS FUNCTION DIRECTLY! + * This To Unicode callback STOPS at the ILLEGAL_SEQUENCE, + * returning the error code back to the caller immediately. + * + * @param context Pointer to the callback's private data + * @param toUArgs Information about the conversion in progress + * @param codeUnits Points to 'length' bytes of the concerned codepage sequence + * @param length Size (in bytes) of the concerned codepage sequence + * @param reason Defines the reason the callback was invoked + * @param err This should always be set to a failure status prior to calling. + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 UCNV_TO_U_CALLBACK_STOP ( + const void *context, + UConverterToUnicodeArgs *toUArgs, + const char* codeUnits, + int32_t length, + UConverterCallbackReason reason, + UErrorCode * err); + +/** + * DO NOT CALL THIS FUNCTION DIRECTLY! + * This From Unicode callback skips any ILLEGAL_SEQUENCE, or + * skips only UNASSIGNED_SEQUENCE depending on the context parameter + * simply ignoring those characters. + * + * @param context The function currently recognizes the callback options: + * UCNV_SKIP_STOP_ON_ILLEGAL: STOPS at the ILLEGAL_SEQUENCE, + * returning the error code back to the caller immediately. + * NULL: Skips any ILLEGAL_SEQUENCE + * @param fromUArgs Information about the conversion in progress + * @param codeUnits Points to 'length' UChars of the concerned Unicode sequence + * @param length Size (in bytes) of the concerned codepage sequence + * @param codePoint Single UChar32 (UTF-32) containing the concerend Unicode codepoint. + * @param reason Defines the reason the callback was invoked + * @param err Return value will be set to success if the callback was handled, + * otherwise this value will be set to a failure status. + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 UCNV_FROM_U_CALLBACK_SKIP ( + const void *context, + UConverterFromUnicodeArgs *fromUArgs, + const UChar* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode * err); + +/** + * DO NOT CALL THIS FUNCTION DIRECTLY! + * This From Unicode callback will Substitute the ILLEGAL SEQUENCE, or + * UNASSIGNED_SEQUENCE depending on context parameter, with the + * current substitution string for the converter. This is the default + * callback. + * + * @param context The function currently recognizes the callback options: + * UCNV_SUB_STOP_ON_ILLEGAL: STOPS at the ILLEGAL_SEQUENCE, + * returning the error code back to the caller immediately. + * NULL: Substitutes any ILLEGAL_SEQUENCE + * @param fromUArgs Information about the conversion in progress + * @param codeUnits Points to 'length' UChars of the concerned Unicode sequence + * @param length Size (in bytes) of the concerned codepage sequence + * @param codePoint Single UChar32 (UTF-32) containing the concerend Unicode codepoint. + * @param reason Defines the reason the callback was invoked + * @param err Return value will be set to success if the callback was handled, + * otherwise this value will be set to a failure status. + * @see ucnv_setSubstChars + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 UCNV_FROM_U_CALLBACK_SUBSTITUTE ( + const void *context, + UConverterFromUnicodeArgs *fromUArgs, + const UChar* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode * err); + +/** + * DO NOT CALL THIS FUNCTION DIRECTLY! + * This From Unicode callback will Substitute the ILLEGAL SEQUENCE with the + * hexadecimal representation of the illegal codepoints + * + * @param context The function currently recognizes the callback options: + *
    + *
  • UCNV_ESCAPE_ICU: Substitutes the ILLEGAL SEQUENCE with the hexadecimal + * representation in the format %UXXXX, e.g. "%uFFFE%u00AC%uC8FE"). + * In the Event the converter doesn't support the characters {%,U}[A-F][0-9], + * it will substitute the illegal sequence with the substitution characters. + * Note that codeUnit(32bit int eg: unit of a surrogate pair) is represented as + * %UD84D%UDC56
  • + *
  • UCNV_ESCAPE_JAVA: Substitutes the ILLEGAL SEQUENCE with the hexadecimal + * representation in the format \\uXXXX, e.g. "\\uFFFE\\u00AC\\uC8FE"). + * In the Event the converter doesn't support the characters {\,u}[A-F][0-9], + * it will substitute the illegal sequence with the substitution characters. + * Note that codeUnit(32bit int eg: unit of a surrogate pair) is represented as + * \\uD84D\\uDC56
  • + *
  • UCNV_ESCAPE_C: Substitutes the ILLEGAL SEQUENCE with the hexadecimal + * representation in the format \\uXXXX, e.g. "\\uFFFE\\u00AC\\uC8FE"). + * In the Event the converter doesn't support the characters {\,u,U}[A-F][0-9], + * it will substitute the illegal sequence with the substitution characters. + * Note that codeUnit(32bit int eg: unit of a surrogate pair) is represented as + * \\U00023456
  • + *
  • UCNV_ESCAPE_XML_DEC: Substitutes the ILLEGAL SEQUENCE with the decimal + * representation in the format \htmlonly&#DDDDDDDD;, e.g. "&#65534;&#172;&#51454;")\endhtmlonly. + * In the Event the converter doesn't support the characters {&,#}[0-9], + * it will substitute the illegal sequence with the substitution characters. + * Note that codeUnit(32bit int eg: unit of a surrogate pair) is represented as + * &#144470; and Zero padding is ignored.
  • + *
  • UCNV_ESCAPE_XML_HEX:Substitutes the ILLEGAL SEQUENCE with the decimal + * representation in the format \htmlonly&#xXXXX; e.g. "&#xFFFE;&#x00AC;&#xC8FE;")\endhtmlonly. + * In the Event the converter doesn't support the characters {&,#,x}[0-9], + * it will substitute the illegal sequence with the substitution characters. + * Note that codeUnit(32bit int eg: unit of a surrogate pair) is represented as + * \htmlonly&#x23456;\endhtmlonly
  • + *
+ * @param fromUArgs Information about the conversion in progress + * @param codeUnits Points to 'length' UChars of the concerned Unicode sequence + * @param length Size (in bytes) of the concerned codepage sequence + * @param codePoint Single UChar32 (UTF-32) containing the concerend Unicode codepoint. + * @param reason Defines the reason the callback was invoked + * @param err Return value will be set to success if the callback was handled, + * otherwise this value will be set to a failure status. + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 UCNV_FROM_U_CALLBACK_ESCAPE ( + const void *context, + UConverterFromUnicodeArgs *fromUArgs, + const UChar* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode * err); + + +/** + * DO NOT CALL THIS FUNCTION DIRECTLY! + * This To Unicode callback skips any ILLEGAL_SEQUENCE, or + * skips only UNASSIGNED_SEQUENCE depending on the context parameter + * simply ignoring those characters. + * + * @param context The function currently recognizes the callback options: + * UCNV_SKIP_STOP_ON_ILLEGAL: STOPS at the ILLEGAL_SEQUENCE, + * returning the error code back to the caller immediately. + * NULL: Skips any ILLEGAL_SEQUENCE + * @param toUArgs Information about the conversion in progress + * @param codeUnits Points to 'length' bytes of the concerned codepage sequence + * @param length Size (in bytes) of the concerned codepage sequence + * @param reason Defines the reason the callback was invoked + * @param err Return value will be set to success if the callback was handled, + * otherwise this value will be set to a failure status. + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 UCNV_TO_U_CALLBACK_SKIP ( + const void *context, + UConverterToUnicodeArgs *toUArgs, + const char* codeUnits, + int32_t length, + UConverterCallbackReason reason, + UErrorCode * err); + +/** + * DO NOT CALL THIS FUNCTION DIRECTLY! + * This To Unicode callback will Substitute the ILLEGAL SEQUENCE,or + * UNASSIGNED_SEQUENCE depending on context parameter, with the + * Unicode substitution character, U+FFFD. + * + * @param context The function currently recognizes the callback options: + * UCNV_SUB_STOP_ON_ILLEGAL: STOPS at the ILLEGAL_SEQUENCE, + * returning the error code back to the caller immediately. + * NULL: Substitutes any ILLEGAL_SEQUENCE + * @param toUArgs Information about the conversion in progress + * @param codeUnits Points to 'length' bytes of the concerned codepage sequence + * @param length Size (in bytes) of the concerned codepage sequence + * @param reason Defines the reason the callback was invoked + * @param err Return value will be set to success if the callback was handled, + * otherwise this value will be set to a failure status. + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 UCNV_TO_U_CALLBACK_SUBSTITUTE ( + const void *context, + UConverterToUnicodeArgs *toUArgs, + const char* codeUnits, + int32_t length, + UConverterCallbackReason reason, + UErrorCode * err); + +/** + * DO NOT CALL THIS FUNCTION DIRECTLY! + * This To Unicode callback will Substitute the ILLEGAL SEQUENCE with the + * hexadecimal representation of the illegal bytes + * (in the format %XNN, e.g. "%XFF%X0A%XC8%X03"). + * + * @param context This function currently recognizes the callback options: + * UCNV_ESCAPE_ICU, UCNV_ESCAPE_JAVA, UCNV_ESCAPE_C, UCNV_ESCAPE_XML_DEC, + * UCNV_ESCAPE_XML_HEX and UCNV_ESCAPE_UNICODE. + * @param toUArgs Information about the conversion in progress + * @param codeUnits Points to 'length' bytes of the concerned codepage sequence + * @param length Size (in bytes) of the concerned codepage sequence + * @param reason Defines the reason the callback was invoked + * @param err Return value will be set to success if the callback was handled, + * otherwise this value will be set to a failure status. + * @stable ICU 2.0 + */ + +U_CAPI void U_EXPORT2 UCNV_TO_U_CALLBACK_ESCAPE ( + const void *context, + UConverterToUnicodeArgs *toUArgs, + const char* codeUnits, + int32_t length, + UConverterCallbackReason reason, + UErrorCode * err); + +#endif + +#endif + +/*UCNV_ERR_H*/ diff --git a/deps/icu-small/source/common/unicode/ucnvsel.h b/deps/icu-small/source/common/unicode/ucnvsel.h index 9373ec951bf83a..849f8b912a3a01 100644 --- a/deps/icu-small/source/common/unicode/ucnvsel.h +++ b/deps/icu-small/source/common/unicode/ucnvsel.h @@ -1,193 +1,193 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2008-2011, International Business Machines -* Corporation, Google and others. All Rights Reserved. -* -******************************************************************************* -*/ -/* - * Author : eldawy@google.com (Mohamed Eldawy) - * ucnvsel.h - * - * Purpose: To generate a list of encodings capable of handling - * a given Unicode text - * - * Started 09-April-2008 - */ - -#ifndef __ICU_UCNV_SEL_H__ -#define __ICU_UCNV_SEL_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/uset.h" -#include "unicode/utf16.h" -#include "unicode/uenum.h" -#include "unicode/ucnv.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -/** - * \file - * \brief C API: Encoding/charset encoding selector - * - * A converter selector is built with a set of encoding/charset names - * and given an input string returns the set of names of the - * corresponding converters which can convert the string. - * - * A converter selector can be serialized into a buffer and reopened - * from the serialized form. - */ - -struct UConverterSelector; -/** - * @{ - * Typedef for selector data structure. - */ -typedef struct UConverterSelector UConverterSelector; -/** @} */ - -/** - * Open a selector. - * If converterListSize is 0, build for all available converters. - * If excludedCodePoints is NULL, don't exclude any code points. - * - * @param converterList a pointer to encoding names needed to be involved. - * Can be NULL if converterListSize==0. - * The list and the names will be cloned, and the caller - * retains ownership of the original. - * @param converterListSize number of encodings in above list. - * If 0, builds a selector for all available converters. - * @param excludedCodePoints a set of code points to be excluded from consideration. - * That is, excluded code points in a string do not change - * the selection result. (They might be handled by a callback.) - * Use NULL to exclude nothing. - * @param whichSet what converter set to use? Use this to determine whether - * to consider only roundtrip mappings or also fallbacks. - * @param status an in/out ICU UErrorCode - * @return the new selector - * - * @stable ICU 4.2 - */ -U_CAPI UConverterSelector* U_EXPORT2 -ucnvsel_open(const char* const* converterList, int32_t converterListSize, - const USet* excludedCodePoints, - const UConverterUnicodeSet whichSet, UErrorCode* status); - -/** - * Closes a selector. - * If any Enumerations were returned by ucnv_select*, they become invalid. - * They can be closed before or after calling ucnv_closeSelector, - * but should never be used after the selector is closed. - * - * @see ucnv_selectForString - * @see ucnv_selectForUTF8 - * - * @param sel selector to close - * - * @stable ICU 4.2 - */ -U_CAPI void U_EXPORT2 -ucnvsel_close(UConverterSelector *sel); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUConverterSelectorPointer - * "Smart pointer" class, closes a UConverterSelector via ucnvsel_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUConverterSelectorPointer, UConverterSelector, ucnvsel_close); - -U_NAMESPACE_END - -#endif - -/** - * Open a selector from its serialized form. - * The buffer must remain valid and unchanged for the lifetime of the selector. - * This is much faster than creating a selector from scratch. - * Using a serialized form from a different machine (endianness/charset) is supported. - * - * @param buffer pointer to the serialized form of a converter selector; - * must be 32-bit-aligned - * @param length the capacity of this buffer (can be equal to or larger than - * the actual data length) - * @param status an in/out ICU UErrorCode - * @return the new selector - * - * @stable ICU 4.2 - */ -U_CAPI UConverterSelector* U_EXPORT2 -ucnvsel_openFromSerialized(const void* buffer, int32_t length, UErrorCode* status); - -/** - * Serialize a selector into a linear buffer. - * The serialized form is portable to different machines. - * - * @param sel selector to consider - * @param buffer pointer to 32-bit-aligned memory to be filled with the - * serialized form of this converter selector - * @param bufferCapacity the capacity of this buffer - * @param status an in/out ICU UErrorCode - * @return the required buffer capacity to hold serialize data (even if the call fails - * with a U_BUFFER_OVERFLOW_ERROR, it will return the required capacity) - * - * @stable ICU 4.2 - */ -U_CAPI int32_t U_EXPORT2 -ucnvsel_serialize(const UConverterSelector* sel, - void* buffer, int32_t bufferCapacity, UErrorCode* status); - -/** - * Select converters that can map all characters in a UTF-16 string, - * ignoring the excluded code points. - * - * @param sel a selector - * @param s UTF-16 string - * @param length length of the string, or -1 if NUL-terminated - * @param status an in/out ICU UErrorCode - * @return an enumeration containing encoding names. - * The returned encoding names and their order will be the same as - * supplied when building the selector. - * - * @stable ICU 4.2 - */ -U_CAPI UEnumeration * U_EXPORT2 -ucnvsel_selectForString(const UConverterSelector* sel, - const UChar *s, int32_t length, UErrorCode *status); - -/** - * Select converters that can map all characters in a UTF-8 string, - * ignoring the excluded code points. - * - * @param sel a selector - * @param s UTF-8 string - * @param length length of the string, or -1 if NUL-terminated - * @param status an in/out ICU UErrorCode - * @return an enumeration containing encoding names. - * The returned encoding names and their order will be the same as - * supplied when building the selector. - * - * @stable ICU 4.2 - */ -U_CAPI UEnumeration * U_EXPORT2 -ucnvsel_selectForUTF8(const UConverterSelector* sel, - const char *s, int32_t length, UErrorCode *status); - -#endif /* !UCONFIG_NO_CONVERSION */ - -#endif /* __ICU_UCNV_SEL_H__ */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2008-2011, International Business Machines +* Corporation, Google and others. All Rights Reserved. +* +******************************************************************************* +*/ +/* + * Author : eldawy@google.com (Mohamed Eldawy) + * ucnvsel.h + * + * Purpose: To generate a list of encodings capable of handling + * a given Unicode text + * + * Started 09-April-2008 + */ + +#ifndef __ICU_UCNV_SEL_H__ +#define __ICU_UCNV_SEL_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/uset.h" +#include "unicode/utf16.h" +#include "unicode/uenum.h" +#include "unicode/ucnv.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +/** + * \file + * \brief C API: Encoding/charset encoding selector + * + * A converter selector is built with a set of encoding/charset names + * and given an input string returns the set of names of the + * corresponding converters which can convert the string. + * + * A converter selector can be serialized into a buffer and reopened + * from the serialized form. + */ + +struct UConverterSelector; +/** + * @{ + * Typedef for selector data structure. + */ +typedef struct UConverterSelector UConverterSelector; +/** @} */ + +/** + * Open a selector. + * If converterListSize is 0, build for all available converters. + * If excludedCodePoints is NULL, don't exclude any code points. + * + * @param converterList a pointer to encoding names needed to be involved. + * Can be NULL if converterListSize==0. + * The list and the names will be cloned, and the caller + * retains ownership of the original. + * @param converterListSize number of encodings in above list. + * If 0, builds a selector for all available converters. + * @param excludedCodePoints a set of code points to be excluded from consideration. + * That is, excluded code points in a string do not change + * the selection result. (They might be handled by a callback.) + * Use NULL to exclude nothing. + * @param whichSet what converter set to use? Use this to determine whether + * to consider only roundtrip mappings or also fallbacks. + * @param status an in/out ICU UErrorCode + * @return the new selector + * + * @stable ICU 4.2 + */ +U_CAPI UConverterSelector* U_EXPORT2 +ucnvsel_open(const char* const* converterList, int32_t converterListSize, + const USet* excludedCodePoints, + const UConverterUnicodeSet whichSet, UErrorCode* status); + +/** + * Closes a selector. + * If any Enumerations were returned by ucnv_select*, they become invalid. + * They can be closed before or after calling ucnv_closeSelector, + * but should never be used after the selector is closed. + * + * @see ucnv_selectForString + * @see ucnv_selectForUTF8 + * + * @param sel selector to close + * + * @stable ICU 4.2 + */ +U_CAPI void U_EXPORT2 +ucnvsel_close(UConverterSelector *sel); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUConverterSelectorPointer + * "Smart pointer" class, closes a UConverterSelector via ucnvsel_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUConverterSelectorPointer, UConverterSelector, ucnvsel_close); + +U_NAMESPACE_END + +#endif + +/** + * Open a selector from its serialized form. + * The buffer must remain valid and unchanged for the lifetime of the selector. + * This is much faster than creating a selector from scratch. + * Using a serialized form from a different machine (endianness/charset) is supported. + * + * @param buffer pointer to the serialized form of a converter selector; + * must be 32-bit-aligned + * @param length the capacity of this buffer (can be equal to or larger than + * the actual data length) + * @param status an in/out ICU UErrorCode + * @return the new selector + * + * @stable ICU 4.2 + */ +U_CAPI UConverterSelector* U_EXPORT2 +ucnvsel_openFromSerialized(const void* buffer, int32_t length, UErrorCode* status); + +/** + * Serialize a selector into a linear buffer. + * The serialized form is portable to different machines. + * + * @param sel selector to consider + * @param buffer pointer to 32-bit-aligned memory to be filled with the + * serialized form of this converter selector + * @param bufferCapacity the capacity of this buffer + * @param status an in/out ICU UErrorCode + * @return the required buffer capacity to hold serialize data (even if the call fails + * with a U_BUFFER_OVERFLOW_ERROR, it will return the required capacity) + * + * @stable ICU 4.2 + */ +U_CAPI int32_t U_EXPORT2 +ucnvsel_serialize(const UConverterSelector* sel, + void* buffer, int32_t bufferCapacity, UErrorCode* status); + +/** + * Select converters that can map all characters in a UTF-16 string, + * ignoring the excluded code points. + * + * @param sel a selector + * @param s UTF-16 string + * @param length length of the string, or -1 if NUL-terminated + * @param status an in/out ICU UErrorCode + * @return an enumeration containing encoding names. + * The returned encoding names and their order will be the same as + * supplied when building the selector. + * + * @stable ICU 4.2 + */ +U_CAPI UEnumeration * U_EXPORT2 +ucnvsel_selectForString(const UConverterSelector* sel, + const UChar *s, int32_t length, UErrorCode *status); + +/** + * Select converters that can map all characters in a UTF-8 string, + * ignoring the excluded code points. + * + * @param sel a selector + * @param s UTF-8 string + * @param length length of the string, or -1 if NUL-terminated + * @param status an in/out ICU UErrorCode + * @return an enumeration containing encoding names. + * The returned encoding names and their order will be the same as + * supplied when building the selector. + * + * @stable ICU 4.2 + */ +U_CAPI UEnumeration * U_EXPORT2 +ucnvsel_selectForUTF8(const UConverterSelector* sel, + const char *s, int32_t length, UErrorCode *status); + +#endif /* !UCONFIG_NO_CONVERSION */ + +#endif /* __ICU_UCNV_SEL_H__ */ diff --git a/deps/icu-small/source/common/unicode/uconfig.h b/deps/icu-small/source/common/unicode/uconfig.h index bbc232d1ed8fdf..f3521be95ebb84 100644 --- a/deps/icu-small/source/common/unicode/uconfig.h +++ b/deps/icu-small/source/common/unicode/uconfig.h @@ -1,456 +1,466 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2002-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: uconfig.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002sep19 -* created by: Markus W. Scherer -*/ - -#ifndef __UCONFIG_H__ -#define __UCONFIG_H__ - - -/*! - * \file - * \brief User-configurable settings - * - * Miscellaneous switches: - * - * A number of macros affect a variety of minor aspects of ICU. - * Most of them used to be defined elsewhere (e.g., in utypes.h or platform.h) - * and moved here to make them easier to find. - * - * Switches for excluding parts of ICU library code modules: - * - * Changing these macros allows building partial, smaller libraries for special purposes. - * By default, all modules are built. - * The switches are fairly coarse, controlling large modules. - * Basic services cannot be turned off. - * - * Building with any of these options does not guarantee that the - * ICU build process will completely work. It is recommended that - * the ICU libraries and data be built using the normal build. - * At that time you should remove the data used by those services. - * After building the ICU data library, you should rebuild the ICU - * libraries with these switches customized to your needs. - * - * @stable ICU 2.4 - */ - -/** - * If this switch is defined, ICU will attempt to load a header file named "uconfig_local.h" - * prior to determining default settings for uconfig variables. - * - * @internal ICU 4.0 - */ -#if defined(UCONFIG_USE_LOCAL) -#include "uconfig_local.h" -#endif - -/** - * \def U_DEBUG - * Determines whether to include debugging code. - * Automatically set on Windows, but most compilers do not have - * related predefined macros. - * @internal - */ -#ifdef U_DEBUG - /* Use the predefined value. */ -#elif defined(_DEBUG) - /* - * _DEBUG is defined by Visual Studio debug compilation. - * Do *not* test for its NDEBUG macro: It is an orthogonal macro - * which disables assert(). - */ -# define U_DEBUG 1 -# else -# define U_DEBUG 0 -#endif - -/** - * Determines whether to enable auto cleanup of libraries. - * @internal - */ -#ifndef UCLN_NO_AUTO_CLEANUP -#define UCLN_NO_AUTO_CLEANUP 1 -#endif - -/** - * \def U_DISABLE_RENAMING - * Determines whether to disable renaming or not. - * @internal - */ -#ifndef U_DISABLE_RENAMING -#define U_DISABLE_RENAMING 0 -#endif - -/** - * \def U_NO_DEFAULT_INCLUDE_UTF_HEADERS - * Determines whether utypes.h includes utf.h, utf8.h, utf16.h and utf_old.h. - * utypes.h includes those headers if this macro is defined to 0. - * Otherwise, each those headers must be included explicitly when using one of their macros. - * Defaults to 0 for backward compatibility, except inside ICU. - * @stable ICU 49 - */ -#ifdef U_NO_DEFAULT_INCLUDE_UTF_HEADERS - /* Use the predefined value. */ -#elif defined(U_COMBINED_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) || defined(U_I18N_IMPLEMENTATION) || \ - defined(U_IO_IMPLEMENTATION) || defined(U_LAYOUT_IMPLEMENTATION) || defined(U_LAYOUTEX_IMPLEMENTATION) || \ - defined(U_TOOLUTIL_IMPLEMENTATION) -# define U_NO_DEFAULT_INCLUDE_UTF_HEADERS 1 -#else -# define U_NO_DEFAULT_INCLUDE_UTF_HEADERS 0 -#endif - -/** - * \def U_OVERRIDE_CXX_ALLOCATION - * Determines whether to override new and delete. - * ICU is normally built such that all of its C++ classes, via their UMemory base, - * override operators new and delete to use its internal, customizable, - * non-exception-throwing memory allocation functions. (Default value 1 for this macro.) - * - * This is especially important when the application and its libraries use multiple heaps. - * For example, on Windows, this allows the ICU DLL to be used by - * applications that statically link the C Runtime library. - * - * @stable ICU 2.2 - */ -#ifndef U_OVERRIDE_CXX_ALLOCATION -#define U_OVERRIDE_CXX_ALLOCATION 1 -#endif - -/** - * \def U_ENABLE_TRACING - * Determines whether to enable tracing. - * @internal - */ -#ifndef U_ENABLE_TRACING -#define U_ENABLE_TRACING 0 -#endif - -/** - * \def UCONFIG_ENABLE_PLUGINS - * Determines whether to enable ICU plugins. - * @internal - */ -#ifndef UCONFIG_ENABLE_PLUGINS -#define UCONFIG_ENABLE_PLUGINS 0 -#endif - -/** - * \def U_ENABLE_DYLOAD - * Whether to enable Dynamic loading in ICU. - * @internal - */ -#ifndef U_ENABLE_DYLOAD -#define U_ENABLE_DYLOAD 1 -#endif - -/** - * \def U_CHECK_DYLOAD - * Whether to test Dynamic loading as an OS capability. - * @internal - */ -#ifndef U_CHECK_DYLOAD -#define U_CHECK_DYLOAD 1 -#endif - -/** - * \def U_DEFAULT_SHOW_DRAFT - * Do we allow ICU users to use the draft APIs by default? - * @internal - */ -#ifndef U_DEFAULT_SHOW_DRAFT -#define U_DEFAULT_SHOW_DRAFT 1 -#endif - -/*===========================================================================*/ -/* Custom icu entry point renaming */ -/*===========================================================================*/ - -/** - * \def U_HAVE_LIB_SUFFIX - * 1 if a custom library suffix is set. - * @internal - */ -#ifdef U_HAVE_LIB_SUFFIX - /* Use the predefined value. */ -#elif defined(U_LIB_SUFFIX_C_NAME) || defined(U_IN_DOXYGEN) -# define U_HAVE_LIB_SUFFIX 1 -#endif - -/** - * \def U_LIB_SUFFIX_C_NAME_STRING - * Defines the library suffix as a string with C syntax. - * @internal - */ -#ifdef U_LIB_SUFFIX_C_NAME_STRING - /* Use the predefined value. */ -#elif defined(U_LIB_SUFFIX_C_NAME) -# define CONVERT_TO_STRING(s) #s -# define U_LIB_SUFFIX_C_NAME_STRING CONVERT_TO_STRING(U_LIB_SUFFIX_C_NAME) -#else -# define U_LIB_SUFFIX_C_NAME_STRING "" -#endif - -/* common/i18n library switches --------------------------------------------- */ - -/** - * \def UCONFIG_ONLY_COLLATION - * This switch turns off modules that are not needed for collation. - * - * It does not turn off legacy conversion because that is necessary - * for ICU to work on EBCDIC platforms (for the default converter). - * If you want "only collation" and do not build for EBCDIC, - * then you can define UCONFIG_NO_CONVERSION or UCONFIG_NO_LEGACY_CONVERSION to 1 as well. - * - * @stable ICU 2.4 - */ -#ifndef UCONFIG_ONLY_COLLATION -# define UCONFIG_ONLY_COLLATION 0 -#endif - -#if UCONFIG_ONLY_COLLATION - /* common library */ -# define UCONFIG_NO_BREAK_ITERATION 1 -# define UCONFIG_NO_IDNA 1 - - /* i18n library */ -# if UCONFIG_NO_COLLATION -# error Contradictory collation switches in uconfig.h. -# endif -# define UCONFIG_NO_FORMATTING 1 -# define UCONFIG_NO_TRANSLITERATION 1 -# define UCONFIG_NO_REGULAR_EXPRESSIONS 1 -#endif - -/* common library switches -------------------------------------------------- */ - -/** - * \def UCONFIG_NO_FILE_IO - * This switch turns off all file access in the common library - * where file access is only used for data loading. - * ICU data must then be provided in the form of a data DLL (or with an - * equivalent way to link to the data residing in an executable, - * as in building a combined library with both the common library's code and - * the data), or via udata_setCommonData(). - * Application data must be provided via udata_setAppData() or by using - * "open" functions that take pointers to data, for example ucol_openBinary(). - * - * File access is not used at all in the i18n library. - * - * File access cannot be turned off for the icuio library or for the ICU - * test suites and ICU tools. - * - * @stable ICU 3.6 - */ -#ifndef UCONFIG_NO_FILE_IO -# define UCONFIG_NO_FILE_IO 0 -#endif - -#if UCONFIG_NO_FILE_IO && defined(U_TIMEZONE_FILES_DIR) -# error Contradictory file io switches in uconfig.h. -#endif - -/** - * \def UCONFIG_NO_CONVERSION - * ICU will not completely build (compiling the tools fails) with this - * switch turned on. - * This switch turns off all converters. - * - * You may want to use this together with U_CHARSET_IS_UTF8 defined to 1 - * in utypes.h if char* strings in your environment are always in UTF-8. - * - * @stable ICU 3.2 - * @see U_CHARSET_IS_UTF8 - */ -#ifndef UCONFIG_NO_CONVERSION -# define UCONFIG_NO_CONVERSION 0 -#endif - -#if UCONFIG_NO_CONVERSION -# define UCONFIG_NO_LEGACY_CONVERSION 1 -#endif - -/** - * \def UCONFIG_ONLY_HTML_CONVERSION - * This switch turns off all of the converters NOT listed in - * the HTML encoding standard: - * http://www.w3.org/TR/encoding/#names-and-labels - * - * This is not possible on EBCDIC platforms - * because they need ibm-37 or ibm-1047 default converters. - * - * @stable ICU 55 - */ -#ifndef UCONFIG_ONLY_HTML_CONVERSION -# define UCONFIG_ONLY_HTML_CONVERSION 0 -#endif - -/** - * \def UCONFIG_NO_LEGACY_CONVERSION - * This switch turns off all converters except for - * - Unicode charsets (UTF-7/8/16/32, CESU-8, SCSU, BOCU-1) - * - US-ASCII - * - ISO-8859-1 - * - * Turning off legacy conversion is not possible on EBCDIC platforms - * because they need ibm-37 or ibm-1047 default converters. - * - * @stable ICU 2.4 - */ -#ifndef UCONFIG_NO_LEGACY_CONVERSION -# define UCONFIG_NO_LEGACY_CONVERSION 0 -#endif - -/** - * \def UCONFIG_NO_NORMALIZATION - * This switch turns off normalization. - * It implies turning off several other services as well, for example - * collation and IDNA. - * - * @stable ICU 2.6 - */ -#ifndef UCONFIG_NO_NORMALIZATION -# define UCONFIG_NO_NORMALIZATION 0 -#endif - -#if UCONFIG_NO_NORMALIZATION - /* common library */ - /* ICU 50 CJK dictionary BreakIterator uses normalization */ -# define UCONFIG_NO_BREAK_ITERATION 1 - /* IDNA (UTS #46) is implemented via normalization */ -# define UCONFIG_NO_IDNA 1 - - /* i18n library */ -# if UCONFIG_ONLY_COLLATION -# error Contradictory collation switches in uconfig.h. -# endif -# define UCONFIG_NO_COLLATION 1 -# define UCONFIG_NO_TRANSLITERATION 1 -#endif - -/** - * \def UCONFIG_NO_BREAK_ITERATION - * This switch turns off break iteration. - * - * @stable ICU 2.4 - */ -#ifndef UCONFIG_NO_BREAK_ITERATION -# define UCONFIG_NO_BREAK_ITERATION 0 -#endif - -/** - * \def UCONFIG_NO_IDNA - * This switch turns off IDNA. - * - * @stable ICU 2.6 - */ -#ifndef UCONFIG_NO_IDNA -# define UCONFIG_NO_IDNA 0 -#endif - -/** - * \def UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE - * Determines the default UMessagePatternApostropheMode. - * See the documentation for that enum. - * - * @stable ICU 4.8 - */ -#ifndef UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE -# define UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE UMSGPAT_APOS_DOUBLE_OPTIONAL -#endif - -/** - * \def UCONFIG_USE_WINDOWS_LCID_MAPPING_API - * On platforms where U_PLATFORM_HAS_WIN32_API is true, this switch determines - * if the Windows platform APIs are used for LCID<->Locale Name conversions. - * Otherwise, only the built-in ICU tables are used. - * - * @internal ICU 64 - */ -#ifndef UCONFIG_USE_WINDOWS_LCID_MAPPING_API -# define UCONFIG_USE_WINDOWS_LCID_MAPPING_API 1 -#endif - -/* i18n library switches ---------------------------------------------------- */ - -/** - * \def UCONFIG_NO_COLLATION - * This switch turns off collation and collation-based string search. - * - * @stable ICU 2.4 - */ -#ifndef UCONFIG_NO_COLLATION -# define UCONFIG_NO_COLLATION 0 -#endif - -/** - * \def UCONFIG_NO_FORMATTING - * This switch turns off formatting and calendar/timezone services. - * - * @stable ICU 2.4 - */ -#ifndef UCONFIG_NO_FORMATTING -# define UCONFIG_NO_FORMATTING 0 -#endif - -/** - * \def UCONFIG_NO_TRANSLITERATION - * This switch turns off transliteration. - * - * @stable ICU 2.4 - */ -#ifndef UCONFIG_NO_TRANSLITERATION -# define UCONFIG_NO_TRANSLITERATION 0 -#endif - -/** - * \def UCONFIG_NO_REGULAR_EXPRESSIONS - * This switch turns off regular expressions. - * - * @stable ICU 2.4 - */ -#ifndef UCONFIG_NO_REGULAR_EXPRESSIONS -# define UCONFIG_NO_REGULAR_EXPRESSIONS 0 -#endif - -/** - * \def UCONFIG_NO_SERVICE - * This switch turns off service registration. - * - * @stable ICU 3.2 - */ -#ifndef UCONFIG_NO_SERVICE -# define UCONFIG_NO_SERVICE 0 -#endif - -/** - * \def UCONFIG_HAVE_PARSEALLINPUT - * This switch turns on the "parse all input" attribute. Binary incompatible. - * - * @internal - */ -#ifndef UCONFIG_HAVE_PARSEALLINPUT -# define UCONFIG_HAVE_PARSEALLINPUT 1 -#endif - -/** - * \def UCONFIG_NO_FILTERED_BREAK_ITERATION - * This switch turns off filtered break iteration code. - * - * @internal - */ -#ifndef UCONFIG_NO_FILTERED_BREAK_ITERATION -# define UCONFIG_NO_FILTERED_BREAK_ITERATION 0 -#endif - -#endif // __UCONFIG_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2002-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: uconfig.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002sep19 +* created by: Markus W. Scherer +*/ + +#ifndef __UCONFIG_H__ +#define __UCONFIG_H__ + + +/*! + * \file + * \brief User-configurable settings + * + * Miscellaneous switches: + * + * A number of macros affect a variety of minor aspects of ICU. + * Most of them used to be defined elsewhere (e.g., in utypes.h or platform.h) + * and moved here to make them easier to find. + * + * Switches for excluding parts of ICU library code modules: + * + * Changing these macros allows building partial, smaller libraries for special purposes. + * By default, all modules are built. + * The switches are fairly coarse, controlling large modules. + * Basic services cannot be turned off. + * + * Building with any of these options does not guarantee that the + * ICU build process will completely work. It is recommended that + * the ICU libraries and data be built using the normal build. + * At that time you should remove the data used by those services. + * After building the ICU data library, you should rebuild the ICU + * libraries with these switches customized to your needs. + * + * @stable ICU 2.4 + */ + +/** + * If this switch is defined, ICU will attempt to load a header file named "uconfig_local.h" + * prior to determining default settings for uconfig variables. + * + * @internal ICU 4.0 + */ +#if defined(UCONFIG_USE_LOCAL) +#include "uconfig_local.h" +#endif + +/** + * \def U_DEBUG + * Determines whether to include debugging code. + * Automatically set on Windows, but most compilers do not have + * related predefined macros. + * @internal + */ +#ifdef U_DEBUG + /* Use the predefined value. */ +#elif defined(_DEBUG) + /* + * _DEBUG is defined by Visual Studio debug compilation. + * Do *not* test for its NDEBUG macro: It is an orthogonal macro + * which disables assert(). + */ +# define U_DEBUG 1 +# else +# define U_DEBUG 0 +#endif + +/** + * Determines whether to enable auto cleanup of libraries. + * @internal + */ +#ifndef UCLN_NO_AUTO_CLEANUP +#define UCLN_NO_AUTO_CLEANUP 1 +#endif + +/** + * \def U_DISABLE_RENAMING + * Determines whether to disable renaming or not. + * @internal + */ +#ifndef U_DISABLE_RENAMING +#define U_DISABLE_RENAMING 0 +#endif + +/** + * \def U_NO_DEFAULT_INCLUDE_UTF_HEADERS + * Determines whether utypes.h includes utf.h, utf8.h, utf16.h and utf_old.h. + * utypes.h includes those headers if this macro is defined to 0. + * Otherwise, each those headers must be included explicitly when using one of their macros. + * Defaults to 0 for backward compatibility, except inside ICU. + * @stable ICU 49 + */ +#ifdef U_NO_DEFAULT_INCLUDE_UTF_HEADERS + /* Use the predefined value. */ +#elif defined(U_COMBINED_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) || defined(U_I18N_IMPLEMENTATION) || \ + defined(U_IO_IMPLEMENTATION) || defined(U_LAYOUT_IMPLEMENTATION) || defined(U_LAYOUTEX_IMPLEMENTATION) || \ + defined(U_TOOLUTIL_IMPLEMENTATION) +# define U_NO_DEFAULT_INCLUDE_UTF_HEADERS 1 +#else +# define U_NO_DEFAULT_INCLUDE_UTF_HEADERS 0 +#endif + +/** + * \def U_OVERRIDE_CXX_ALLOCATION + * Determines whether to override new and delete. + * ICU is normally built such that all of its C++ classes, via their UMemory base, + * override operators new and delete to use its internal, customizable, + * non-exception-throwing memory allocation functions. (Default value 1 for this macro.) + * + * This is especially important when the application and its libraries use multiple heaps. + * For example, on Windows, this allows the ICU DLL to be used by + * applications that statically link the C Runtime library. + * + * @stable ICU 2.2 + */ +#ifndef U_OVERRIDE_CXX_ALLOCATION +#define U_OVERRIDE_CXX_ALLOCATION 1 +#endif + +/** + * \def U_ENABLE_TRACING + * Determines whether to enable tracing. + * @internal + */ +#ifndef U_ENABLE_TRACING +#define U_ENABLE_TRACING 0 +#endif + +/** + * \def UCONFIG_ENABLE_PLUGINS + * Determines whether to enable ICU plugins. + * @internal + */ +#ifndef UCONFIG_ENABLE_PLUGINS +#define UCONFIG_ENABLE_PLUGINS 0 +#endif + +/** + * \def U_ENABLE_DYLOAD + * Whether to enable Dynamic loading in ICU. + * @internal + */ +#ifndef U_ENABLE_DYLOAD +#define U_ENABLE_DYLOAD 1 +#endif + +/** + * \def U_CHECK_DYLOAD + * Whether to test Dynamic loading as an OS capability. + * @internal + */ +#ifndef U_CHECK_DYLOAD +#define U_CHECK_DYLOAD 1 +#endif + +/** + * \def U_DEFAULT_SHOW_DRAFT + * Do we allow ICU users to use the draft APIs by default? + * @internal + */ +#ifndef U_DEFAULT_SHOW_DRAFT +#define U_DEFAULT_SHOW_DRAFT 1 +#endif + +/*===========================================================================*/ +/* Custom icu entry point renaming */ +/*===========================================================================*/ + +/** + * \def U_HAVE_LIB_SUFFIX + * 1 if a custom library suffix is set. + * @internal + */ +#ifdef U_HAVE_LIB_SUFFIX + /* Use the predefined value. */ +#elif defined(U_LIB_SUFFIX_C_NAME) || defined(U_IN_DOXYGEN) +# define U_HAVE_LIB_SUFFIX 1 +#endif + +/** + * \def U_LIB_SUFFIX_C_NAME_STRING + * Defines the library suffix as a string with C syntax. + * @internal + */ +#ifdef U_LIB_SUFFIX_C_NAME_STRING + /* Use the predefined value. */ +#elif defined(U_LIB_SUFFIX_C_NAME) +# define CONVERT_TO_STRING(s) #s +# define U_LIB_SUFFIX_C_NAME_STRING CONVERT_TO_STRING(U_LIB_SUFFIX_C_NAME) +#else +# define U_LIB_SUFFIX_C_NAME_STRING "" +#endif + +/* common/i18n library switches --------------------------------------------- */ + +/** + * \def UCONFIG_ONLY_COLLATION + * This switch turns off modules that are not needed for collation. + * + * It does not turn off legacy conversion because that is necessary + * for ICU to work on EBCDIC platforms (for the default converter). + * If you want "only collation" and do not build for EBCDIC, + * then you can define UCONFIG_NO_CONVERSION or UCONFIG_NO_LEGACY_CONVERSION to 1 as well. + * + * @stable ICU 2.4 + */ +#ifndef UCONFIG_ONLY_COLLATION +# define UCONFIG_ONLY_COLLATION 0 +#endif + +#if UCONFIG_ONLY_COLLATION + /* common library */ +# define UCONFIG_NO_BREAK_ITERATION 1 +# define UCONFIG_NO_IDNA 1 + + /* i18n library */ +# if UCONFIG_NO_COLLATION +# error Contradictory collation switches in uconfig.h. +# endif +# define UCONFIG_NO_FORMATTING 1 +# define UCONFIG_NO_TRANSLITERATION 1 +# define UCONFIG_NO_REGULAR_EXPRESSIONS 1 +#endif + +/* common library switches -------------------------------------------------- */ + +/** + * \def UCONFIG_NO_FILE_IO + * This switch turns off all file access in the common library + * where file access is only used for data loading. + * ICU data must then be provided in the form of a data DLL (or with an + * equivalent way to link to the data residing in an executable, + * as in building a combined library with both the common library's code and + * the data), or via udata_setCommonData(). + * Application data must be provided via udata_setAppData() or by using + * "open" functions that take pointers to data, for example ucol_openBinary(). + * + * File access is not used at all in the i18n library. + * + * File access cannot be turned off for the icuio library or for the ICU + * test suites and ICU tools. + * + * @stable ICU 3.6 + */ +#ifndef UCONFIG_NO_FILE_IO +# define UCONFIG_NO_FILE_IO 0 +#endif + +#if UCONFIG_NO_FILE_IO && defined(U_TIMEZONE_FILES_DIR) +# error Contradictory file io switches in uconfig.h. +#endif + +/** + * \def UCONFIG_NO_CONVERSION + * ICU will not completely build (compiling the tools fails) with this + * switch turned on. + * This switch turns off all converters. + * + * You may want to use this together with U_CHARSET_IS_UTF8 defined to 1 + * in utypes.h if char* strings in your environment are always in UTF-8. + * + * @stable ICU 3.2 + * @see U_CHARSET_IS_UTF8 + */ +#ifndef UCONFIG_NO_CONVERSION +# define UCONFIG_NO_CONVERSION 0 +#endif + +#if UCONFIG_NO_CONVERSION +# define UCONFIG_NO_LEGACY_CONVERSION 1 +#endif + +/** + * \def UCONFIG_ONLY_HTML_CONVERSION + * This switch turns off all of the converters NOT listed in + * the HTML encoding standard: + * http://www.w3.org/TR/encoding/#names-and-labels + * + * This is not possible on EBCDIC platforms + * because they need ibm-37 or ibm-1047 default converters. + * + * @stable ICU 55 + */ +#ifndef UCONFIG_ONLY_HTML_CONVERSION +# define UCONFIG_ONLY_HTML_CONVERSION 0 +#endif + +/** + * \def UCONFIG_NO_LEGACY_CONVERSION + * This switch turns off all converters except for + * - Unicode charsets (UTF-7/8/16/32, CESU-8, SCSU, BOCU-1) + * - US-ASCII + * - ISO-8859-1 + * + * Turning off legacy conversion is not possible on EBCDIC platforms + * because they need ibm-37 or ibm-1047 default converters. + * + * @stable ICU 2.4 + */ +#ifndef UCONFIG_NO_LEGACY_CONVERSION +# define UCONFIG_NO_LEGACY_CONVERSION 0 +#endif + +/** + * \def UCONFIG_NO_NORMALIZATION + * This switch turns off normalization. + * It implies turning off several other services as well, for example + * collation and IDNA. + * + * @stable ICU 2.6 + */ +#ifndef UCONFIG_NO_NORMALIZATION +# define UCONFIG_NO_NORMALIZATION 0 +#endif + +/** + * \def UCONFIG_USE_ML_PHRASE_BREAKING + * This switch turns on BudouX ML phrase-based line breaking, rather than using the dictionary. + * + * @internal + */ +#ifndef UCONFIG_USE_ML_PHRASE_BREAKING +# define UCONFIG_USE_ML_PHRASE_BREAKING 0 +#endif + +#if UCONFIG_NO_NORMALIZATION + /* common library */ + /* ICU 50 CJK dictionary BreakIterator uses normalization */ +# define UCONFIG_NO_BREAK_ITERATION 1 + /* IDNA (UTS #46) is implemented via normalization */ +# define UCONFIG_NO_IDNA 1 + + /* i18n library */ +# if UCONFIG_ONLY_COLLATION +# error Contradictory collation switches in uconfig.h. +# endif +# define UCONFIG_NO_COLLATION 1 +# define UCONFIG_NO_TRANSLITERATION 1 +#endif + +/** + * \def UCONFIG_NO_BREAK_ITERATION + * This switch turns off break iteration. + * + * @stable ICU 2.4 + */ +#ifndef UCONFIG_NO_BREAK_ITERATION +# define UCONFIG_NO_BREAK_ITERATION 0 +#endif + +/** + * \def UCONFIG_NO_IDNA + * This switch turns off IDNA. + * + * @stable ICU 2.6 + */ +#ifndef UCONFIG_NO_IDNA +# define UCONFIG_NO_IDNA 0 +#endif + +/** + * \def UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE + * Determines the default UMessagePatternApostropheMode. + * See the documentation for that enum. + * + * @stable ICU 4.8 + */ +#ifndef UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE +# define UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE UMSGPAT_APOS_DOUBLE_OPTIONAL +#endif + +/** + * \def UCONFIG_USE_WINDOWS_LCID_MAPPING_API + * On platforms where U_PLATFORM_HAS_WIN32_API is true, this switch determines + * if the Windows platform APIs are used for LCID<->Locale Name conversions. + * Otherwise, only the built-in ICU tables are used. + * + * @internal ICU 64 + */ +#ifndef UCONFIG_USE_WINDOWS_LCID_MAPPING_API +# define UCONFIG_USE_WINDOWS_LCID_MAPPING_API 1 +#endif + +/* i18n library switches ---------------------------------------------------- */ + +/** + * \def UCONFIG_NO_COLLATION + * This switch turns off collation and collation-based string search. + * + * @stable ICU 2.4 + */ +#ifndef UCONFIG_NO_COLLATION +# define UCONFIG_NO_COLLATION 0 +#endif + +/** + * \def UCONFIG_NO_FORMATTING + * This switch turns off formatting and calendar/timezone services. + * + * @stable ICU 2.4 + */ +#ifndef UCONFIG_NO_FORMATTING +# define UCONFIG_NO_FORMATTING 0 +#endif + +/** + * \def UCONFIG_NO_TRANSLITERATION + * This switch turns off transliteration. + * + * @stable ICU 2.4 + */ +#ifndef UCONFIG_NO_TRANSLITERATION +# define UCONFIG_NO_TRANSLITERATION 0 +#endif + +/** + * \def UCONFIG_NO_REGULAR_EXPRESSIONS + * This switch turns off regular expressions. + * + * @stable ICU 2.4 + */ +#ifndef UCONFIG_NO_REGULAR_EXPRESSIONS +# define UCONFIG_NO_REGULAR_EXPRESSIONS 0 +#endif + +/** + * \def UCONFIG_NO_SERVICE + * This switch turns off service registration. + * + * @stable ICU 3.2 + */ +#ifndef UCONFIG_NO_SERVICE +# define UCONFIG_NO_SERVICE 0 +#endif + +/** + * \def UCONFIG_HAVE_PARSEALLINPUT + * This switch turns on the "parse all input" attribute. Binary incompatible. + * + * @internal + */ +#ifndef UCONFIG_HAVE_PARSEALLINPUT +# define UCONFIG_HAVE_PARSEALLINPUT 1 +#endif + +/** + * \def UCONFIG_NO_FILTERED_BREAK_ITERATION + * This switch turns off filtered break iteration code. + * + * @internal + */ +#ifndef UCONFIG_NO_FILTERED_BREAK_ITERATION +# define UCONFIG_NO_FILTERED_BREAK_ITERATION 0 +#endif + +#endif // __UCONFIG_H__ diff --git a/deps/icu-small/source/common/unicode/ucpmap.h b/deps/icu-small/source/common/unicode/ucpmap.h index a740bd160fc155..f2c3bef15e1928 100644 --- a/deps/icu-small/source/common/unicode/ucpmap.h +++ b/deps/icu-small/source/common/unicode/ucpmap.h @@ -1,158 +1,158 @@ -// © 2018 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// ucpmap.h -// created: 2018sep03 Markus W. Scherer - -#ifndef __UCPMAP_H__ -#define __UCPMAP_H__ - -#include "unicode/utypes.h" - -U_CDECL_BEGIN - -/** - * \file - * \brief C API: This file defines an abstract map from Unicode code points to integer values. - * - * @see UCPMap - * @see UCPTrie - * @see UMutableCPTrie - */ - -/** - * Abstract map from Unicode code points (U+0000..U+10FFFF) to integer values. - * - * @see UCPTrie - * @see UMutableCPTrie - * @stable ICU 63 - */ -typedef struct UCPMap UCPMap; - -/** - * Selectors for how ucpmap_getRange() etc. should report value ranges overlapping with surrogates. - * Most users should use UCPMAP_RANGE_NORMAL. - * - * @see ucpmap_getRange - * @see ucptrie_getRange - * @see umutablecptrie_getRange - * @stable ICU 63 - */ -enum UCPMapRangeOption { - /** - * ucpmap_getRange() enumerates all same-value ranges as stored in the map. - * Most users should use this option. - * @stable ICU 63 - */ - UCPMAP_RANGE_NORMAL, - /** - * ucpmap_getRange() enumerates all same-value ranges as stored in the map, - * except that lead surrogates (U+D800..U+DBFF) are treated as having the - * surrogateValue, which is passed to getRange() as a separate parameter. - * The surrogateValue is not transformed via filter(). - * See U_IS_LEAD(c). - * - * Most users should use UCPMAP_RANGE_NORMAL instead. - * - * This option is useful for maps that map surrogate code *units* to - * special values optimized for UTF-16 string processing - * or for special error behavior for unpaired surrogates, - * but those values are not to be associated with the lead surrogate code *points*. - * @stable ICU 63 - */ - UCPMAP_RANGE_FIXED_LEAD_SURROGATES, - /** - * ucpmap_getRange() enumerates all same-value ranges as stored in the map, - * except that all surrogates (U+D800..U+DFFF) are treated as having the - * surrogateValue, which is passed to getRange() as a separate parameter. - * The surrogateValue is not transformed via filter(). - * See U_IS_SURROGATE(c). - * - * Most users should use UCPMAP_RANGE_NORMAL instead. - * - * This option is useful for maps that map surrogate code *units* to - * special values optimized for UTF-16 string processing - * or for special error behavior for unpaired surrogates, - * but those values are not to be associated with the lead surrogate code *points*. - * @stable ICU 63 - */ - UCPMAP_RANGE_FIXED_ALL_SURROGATES -}; -#ifndef U_IN_DOXYGEN -typedef enum UCPMapRangeOption UCPMapRangeOption; -#endif - -/** - * Returns the value for a code point as stored in the map, with range checking. - * Returns an implementation-defined error value if c is not in the range 0..U+10FFFF. - * - * @param map the map - * @param c the code point - * @return the map value, - * or an implementation-defined error value if the code point is not in the range 0..U+10FFFF - * @stable ICU 63 - */ -U_CAPI uint32_t U_EXPORT2 -ucpmap_get(const UCPMap *map, UChar32 c); - -/** - * Callback function type: Modifies a map value. - * Optionally called by ucpmap_getRange()/ucptrie_getRange()/umutablecptrie_getRange(). - * The modified value will be returned by the getRange function. - * - * Can be used to ignore some of the value bits, - * make a filter for one of several values, - * return a value index computed from the map value, etc. - * - * @param context an opaque pointer, as passed into the getRange function - * @param value a value from the map - * @return the modified value - * @stable ICU 63 - */ -typedef uint32_t U_CALLCONV -UCPMapValueFilter(const void *context, uint32_t value); - -/** - * Returns the last code point such that all those from start to there have the same value. - * Can be used to efficiently iterate over all same-value ranges in a map. - * (This is normally faster than iterating over code points and get()ting each value, - * but much slower than a data structure that stores ranges directly.) - * - * If the UCPMapValueFilter function pointer is not NULL, then - * the value to be delivered is passed through that function, and the return value is the end - * of the range where all values are modified to the same actual value. - * The value is unchanged if that function pointer is NULL. - * - * Example: - * \code - * UChar32 start = 0, end; - * uint32_t value; - * while ((end = ucpmap_getRange(map, start, UCPMAP_RANGE_NORMAL, 0, - * NULL, NULL, &value)) >= 0) { - * // Work with the range start..end and its value. - * start = end + 1; - * } - * \endcode - * - * @param map the map - * @param start range start - * @param option defines whether surrogates are treated normally, - * or as having the surrogateValue; usually UCPMAP_RANGE_NORMAL - * @param surrogateValue value for surrogates; ignored if option==UCPMAP_RANGE_NORMAL - * @param filter a pointer to a function that may modify the map data value, - * or NULL if the values from the map are to be used unmodified - * @param context an opaque pointer that is passed on to the filter function - * @param pValue if not NULL, receives the value that every code point start..end has; - * may have been modified by filter(context, map value) - * if that function pointer is not NULL - * @return the range end code point, or -1 if start is not a valid code point - * @stable ICU 63 - */ -U_CAPI UChar32 U_EXPORT2 -ucpmap_getRange(const UCPMap *map, UChar32 start, - UCPMapRangeOption option, uint32_t surrogateValue, - UCPMapValueFilter *filter, const void *context, uint32_t *pValue); - -U_CDECL_END - -#endif +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// ucpmap.h +// created: 2018sep03 Markus W. Scherer + +#ifndef __UCPMAP_H__ +#define __UCPMAP_H__ + +#include "unicode/utypes.h" + +U_CDECL_BEGIN + +/** + * \file + * \brief C API: This file defines an abstract map from Unicode code points to integer values. + * + * @see UCPMap + * @see UCPTrie + * @see UMutableCPTrie + */ + +/** + * Abstract map from Unicode code points (U+0000..U+10FFFF) to integer values. + * + * @see UCPTrie + * @see UMutableCPTrie + * @stable ICU 63 + */ +typedef struct UCPMap UCPMap; + +/** + * Selectors for how ucpmap_getRange() etc. should report value ranges overlapping with surrogates. + * Most users should use UCPMAP_RANGE_NORMAL. + * + * @see ucpmap_getRange + * @see ucptrie_getRange + * @see umutablecptrie_getRange + * @stable ICU 63 + */ +enum UCPMapRangeOption { + /** + * ucpmap_getRange() enumerates all same-value ranges as stored in the map. + * Most users should use this option. + * @stable ICU 63 + */ + UCPMAP_RANGE_NORMAL, + /** + * ucpmap_getRange() enumerates all same-value ranges as stored in the map, + * except that lead surrogates (U+D800..U+DBFF) are treated as having the + * surrogateValue, which is passed to getRange() as a separate parameter. + * The surrogateValue is not transformed via filter(). + * See U_IS_LEAD(c). + * + * Most users should use UCPMAP_RANGE_NORMAL instead. + * + * This option is useful for maps that map surrogate code *units* to + * special values optimized for UTF-16 string processing + * or for special error behavior for unpaired surrogates, + * but those values are not to be associated with the lead surrogate code *points*. + * @stable ICU 63 + */ + UCPMAP_RANGE_FIXED_LEAD_SURROGATES, + /** + * ucpmap_getRange() enumerates all same-value ranges as stored in the map, + * except that all surrogates (U+D800..U+DFFF) are treated as having the + * surrogateValue, which is passed to getRange() as a separate parameter. + * The surrogateValue is not transformed via filter(). + * See U_IS_SURROGATE(c). + * + * Most users should use UCPMAP_RANGE_NORMAL instead. + * + * This option is useful for maps that map surrogate code *units* to + * special values optimized for UTF-16 string processing + * or for special error behavior for unpaired surrogates, + * but those values are not to be associated with the lead surrogate code *points*. + * @stable ICU 63 + */ + UCPMAP_RANGE_FIXED_ALL_SURROGATES +}; +#ifndef U_IN_DOXYGEN +typedef enum UCPMapRangeOption UCPMapRangeOption; +#endif + +/** + * Returns the value for a code point as stored in the map, with range checking. + * Returns an implementation-defined error value if c is not in the range 0..U+10FFFF. + * + * @param map the map + * @param c the code point + * @return the map value, + * or an implementation-defined error value if the code point is not in the range 0..U+10FFFF + * @stable ICU 63 + */ +U_CAPI uint32_t U_EXPORT2 +ucpmap_get(const UCPMap *map, UChar32 c); + +/** + * Callback function type: Modifies a map value. + * Optionally called by ucpmap_getRange()/ucptrie_getRange()/umutablecptrie_getRange(). + * The modified value will be returned by the getRange function. + * + * Can be used to ignore some of the value bits, + * make a filter for one of several values, + * return a value index computed from the map value, etc. + * + * @param context an opaque pointer, as passed into the getRange function + * @param value a value from the map + * @return the modified value + * @stable ICU 63 + */ +typedef uint32_t U_CALLCONV +UCPMapValueFilter(const void *context, uint32_t value); + +/** + * Returns the last code point such that all those from start to there have the same value. + * Can be used to efficiently iterate over all same-value ranges in a map. + * (This is normally faster than iterating over code points and get()ting each value, + * but much slower than a data structure that stores ranges directly.) + * + * If the UCPMapValueFilter function pointer is not NULL, then + * the value to be delivered is passed through that function, and the return value is the end + * of the range where all values are modified to the same actual value. + * The value is unchanged if that function pointer is NULL. + * + * Example: + * \code + * UChar32 start = 0, end; + * uint32_t value; + * while ((end = ucpmap_getRange(map, start, UCPMAP_RANGE_NORMAL, 0, + * NULL, NULL, &value)) >= 0) { + * // Work with the range start..end and its value. + * start = end + 1; + * } + * \endcode + * + * @param map the map + * @param start range start + * @param option defines whether surrogates are treated normally, + * or as having the surrogateValue; usually UCPMAP_RANGE_NORMAL + * @param surrogateValue value for surrogates; ignored if option==UCPMAP_RANGE_NORMAL + * @param filter a pointer to a function that may modify the map data value, + * or NULL if the values from the map are to be used unmodified + * @param context an opaque pointer that is passed on to the filter function + * @param pValue if not NULL, receives the value that every code point start..end has; + * may have been modified by filter(context, map value) + * if that function pointer is not NULL + * @return the range end code point, or -1 if start is not a valid code point + * @stable ICU 63 + */ +U_CAPI UChar32 U_EXPORT2 +ucpmap_getRange(const UCPMap *map, UChar32 start, + UCPMapRangeOption option, uint32_t surrogateValue, + UCPMapValueFilter *filter, const void *context, uint32_t *pValue); + +U_CDECL_END + +#endif diff --git a/deps/icu-small/source/common/unicode/ucptrie.h b/deps/icu-small/source/common/unicode/ucptrie.h index dadef79c512049..e7685a3384cf45 100644 --- a/deps/icu-small/source/common/unicode/ucptrie.h +++ b/deps/icu-small/source/common/unicode/ucptrie.h @@ -1,645 +1,645 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// ucptrie.h (modified from utrie2.h) -// created: 2017dec29 Markus W. Scherer - -#ifndef __UCPTRIE_H__ -#define __UCPTRIE_H__ - -#include "unicode/utypes.h" -#include "unicode/ucpmap.h" -#include "unicode/utf8.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -U_CDECL_BEGIN - -/** - * \file - * \brief C API: This file defines an immutable Unicode code point trie. - * - * @see UCPTrie - * @see UMutableCPTrie - */ - -#ifndef U_IN_DOXYGEN -/** @internal */ -typedef union UCPTrieData { - /** @internal */ - const void *ptr0; - /** @internal */ - const uint16_t *ptr16; - /** @internal */ - const uint32_t *ptr32; - /** @internal */ - const uint8_t *ptr8; -} UCPTrieData; -#endif - -/** - * Immutable Unicode code point trie structure. - * Fast, reasonably compact, map from Unicode code points (U+0000..U+10FFFF) to integer values. - * For details see https://icu.unicode.org/design/struct/utrie - * - * Do not access UCPTrie fields directly; use public functions and macros. - * Functions are easy to use: They support all trie types and value widths. - * - * When performance is really important, macros provide faster access. - * Most macros are specific to either "fast" or "small" tries, see UCPTrieType. - * There are "fast" macros for special optimized use cases. - * - * The macros will return bogus values, or may crash, if used on the wrong type or value width. - * - * @see UMutableCPTrie - * @stable ICU 63 - */ -struct UCPTrie { -#ifndef U_IN_DOXYGEN - /** @internal */ - const uint16_t *index; - /** @internal */ - UCPTrieData data; - - /** @internal */ - int32_t indexLength; - /** @internal */ - int32_t dataLength; - /** Start of the last range which ends at U+10FFFF. @internal */ - UChar32 highStart; - /** highStart>>12 @internal */ - uint16_t shifted12HighStart; - - /** @internal */ - int8_t type; // UCPTrieType - /** @internal */ - int8_t valueWidth; // UCPTrieValueWidth - - /** padding/reserved @internal */ - uint32_t reserved32; - /** padding/reserved @internal */ - uint16_t reserved16; - - /** - * Internal index-3 null block offset. - * Set to an impossibly high value (e.g., 0xffff) if there is no dedicated index-3 null block. - * @internal - */ - uint16_t index3NullOffset; - /** - * Internal data null block offset, not shifted. - * Set to an impossibly high value (e.g., 0xfffff) if there is no dedicated data null block. - * @internal - */ - int32_t dataNullOffset; - /** @internal */ - uint32_t nullValue; - -#ifdef UCPTRIE_DEBUG - /** @internal */ - const char *name; -#endif -#endif -}; -#ifndef U_IN_DOXYGEN -typedef struct UCPTrie UCPTrie; -#endif - -/** - * Selectors for the type of a UCPTrie. - * Different trade-offs for size vs. speed. - * - * @see umutablecptrie_buildImmutable - * @see ucptrie_openFromBinary - * @see ucptrie_getType - * @stable ICU 63 - */ -enum UCPTrieType { - /** - * For ucptrie_openFromBinary() to accept any type. - * ucptrie_getType() will return the actual type. - * @stable ICU 63 - */ - UCPTRIE_TYPE_ANY = -1, - /** - * Fast/simple/larger BMP data structure. Use functions and "fast" macros. - * @stable ICU 63 - */ - UCPTRIE_TYPE_FAST, - /** - * Small/slower BMP data structure. Use functions and "small" macros. - * @stable ICU 63 - */ - UCPTRIE_TYPE_SMALL -}; -#ifndef U_IN_DOXYGEN -typedef enum UCPTrieType UCPTrieType; -#endif - -/** - * Selectors for the number of bits in a UCPTrie data value. - * - * @see umutablecptrie_buildImmutable - * @see ucptrie_openFromBinary - * @see ucptrie_getValueWidth - * @stable ICU 63 - */ -enum UCPTrieValueWidth { - /** - * For ucptrie_openFromBinary() to accept any data value width. - * ucptrie_getValueWidth() will return the actual data value width. - * @stable ICU 63 - */ - UCPTRIE_VALUE_BITS_ANY = -1, - /** - * The trie stores 16 bits per data value. - * It returns them as unsigned values 0..0xffff=65535. - * @stable ICU 63 - */ - UCPTRIE_VALUE_BITS_16, - /** - * The trie stores 32 bits per data value. - * @stable ICU 63 - */ - UCPTRIE_VALUE_BITS_32, - /** - * The trie stores 8 bits per data value. - * It returns them as unsigned values 0..0xff=255. - * @stable ICU 63 - */ - UCPTRIE_VALUE_BITS_8 -}; -#ifndef U_IN_DOXYGEN -typedef enum UCPTrieValueWidth UCPTrieValueWidth; -#endif - -/** - * Opens a trie from its binary form, stored in 32-bit-aligned memory. - * Inverse of ucptrie_toBinary(). - * - * The memory must remain valid and unchanged as long as the trie is used. - * You must ucptrie_close() the trie once you are done using it. - * - * @param type selects the trie type; results in an - * U_INVALID_FORMAT_ERROR if it does not match the binary data; - * use UCPTRIE_TYPE_ANY to accept any type - * @param valueWidth selects the number of bits in a data value; results in an - * U_INVALID_FORMAT_ERROR if it does not match the binary data; - * use UCPTRIE_VALUE_BITS_ANY to accept any data value width - * @param data a pointer to 32-bit-aligned memory containing the binary data of a UCPTrie - * @param length the number of bytes available at data; - * can be more than necessary - * @param pActualLength receives the actual number of bytes at data taken up by the trie data; - * can be NULL - * @param pErrorCode an in/out ICU UErrorCode - * @return the trie - * - * @see umutablecptrie_open - * @see umutablecptrie_buildImmutable - * @see ucptrie_toBinary - * @stable ICU 63 - */ -U_CAPI UCPTrie * U_EXPORT2 -ucptrie_openFromBinary(UCPTrieType type, UCPTrieValueWidth valueWidth, - const void *data, int32_t length, int32_t *pActualLength, - UErrorCode *pErrorCode); - -/** - * Closes a trie and releases associated memory. - * - * @param trie the trie - * @stable ICU 63 - */ -U_CAPI void U_EXPORT2 -ucptrie_close(UCPTrie *trie); - -/** - * Returns the trie type. - * - * @param trie the trie - * @return the trie type - * @see ucptrie_openFromBinary - * @see UCPTRIE_TYPE_ANY - * @stable ICU 63 - */ -U_CAPI UCPTrieType U_EXPORT2 -ucptrie_getType(const UCPTrie *trie); - -/** - * Returns the number of bits in a trie data value. - * - * @param trie the trie - * @return the number of bits in a trie data value - * @see ucptrie_openFromBinary - * @see UCPTRIE_VALUE_BITS_ANY - * @stable ICU 63 - */ -U_CAPI UCPTrieValueWidth U_EXPORT2 -ucptrie_getValueWidth(const UCPTrie *trie); - -/** - * Returns the value for a code point as stored in the trie, with range checking. - * Returns the trie error value if c is not in the range 0..U+10FFFF. - * - * Easier to use than UCPTRIE_FAST_GET() and similar macros but slower. - * Easier to use because, unlike the macros, this function works on all UCPTrie - * objects, for all types and value widths. - * - * @param trie the trie - * @param c the code point - * @return the trie value, - * or the trie error value if the code point is not in the range 0..U+10FFFF - * @stable ICU 63 - */ -U_CAPI uint32_t U_EXPORT2 -ucptrie_get(const UCPTrie *trie, UChar32 c); - -/** - * Returns the last code point such that all those from start to there have the same value. - * Can be used to efficiently iterate over all same-value ranges in a trie. - * (This is normally faster than iterating over code points and get()ting each value, - * but much slower than a data structure that stores ranges directly.) - * - * If the UCPMapValueFilter function pointer is not NULL, then - * the value to be delivered is passed through that function, and the return value is the end - * of the range where all values are modified to the same actual value. - * The value is unchanged if that function pointer is NULL. - * - * Example: - * \code - * UChar32 start = 0, end; - * uint32_t value; - * while ((end = ucptrie_getRange(trie, start, UCPMAP_RANGE_NORMAL, 0, - * NULL, NULL, &value)) >= 0) { - * // Work with the range start..end and its value. - * start = end + 1; - * } - * \endcode - * - * @param trie the trie - * @param start range start - * @param option defines whether surrogates are treated normally, - * or as having the surrogateValue; usually UCPMAP_RANGE_NORMAL - * @param surrogateValue value for surrogates; ignored if option==UCPMAP_RANGE_NORMAL - * @param filter a pointer to a function that may modify the trie data value, - * or NULL if the values from the trie are to be used unmodified - * @param context an opaque pointer that is passed on to the filter function - * @param pValue if not NULL, receives the value that every code point start..end has; - * may have been modified by filter(context, trie value) - * if that function pointer is not NULL - * @return the range end code point, or -1 if start is not a valid code point - * @stable ICU 63 - */ -U_CAPI UChar32 U_EXPORT2 -ucptrie_getRange(const UCPTrie *trie, UChar32 start, - UCPMapRangeOption option, uint32_t surrogateValue, - UCPMapValueFilter *filter, const void *context, uint32_t *pValue); - -/** - * Writes a memory-mappable form of the trie into 32-bit aligned memory. - * Inverse of ucptrie_openFromBinary(). - * - * @param trie the trie - * @param data a pointer to 32-bit-aligned memory to be filled with the trie data; - * can be NULL if capacity==0 - * @param capacity the number of bytes available at data, or 0 for pure preflighting - * @param pErrorCode an in/out ICU UErrorCode; - * U_BUFFER_OVERFLOW_ERROR if the capacity is too small - * @return the number of bytes written or (if buffer overflow) needed for the trie - * - * @see ucptrie_openFromBinary() - * @stable ICU 63 - */ -U_CAPI int32_t U_EXPORT2 -ucptrie_toBinary(const UCPTrie *trie, void *data, int32_t capacity, UErrorCode *pErrorCode); - -/** - * Macro parameter value for a trie with 16-bit data values. - * Use the name of this macro as a "dataAccess" parameter in other macros. - * Do not use this macro in any other way. - * - * @see UCPTRIE_VALUE_BITS_16 - * @stable ICU 63 - */ -#define UCPTRIE_16(trie, i) ((trie)->data.ptr16[i]) - -/** - * Macro parameter value for a trie with 32-bit data values. - * Use the name of this macro as a "dataAccess" parameter in other macros. - * Do not use this macro in any other way. - * - * @see UCPTRIE_VALUE_BITS_32 - * @stable ICU 63 - */ -#define UCPTRIE_32(trie, i) ((trie)->data.ptr32[i]) - -/** - * Macro parameter value for a trie with 8-bit data values. - * Use the name of this macro as a "dataAccess" parameter in other macros. - * Do not use this macro in any other way. - * - * @see UCPTRIE_VALUE_BITS_8 - * @stable ICU 63 - */ -#define UCPTRIE_8(trie, i) ((trie)->data.ptr8[i]) - -/** - * Returns a trie value for a code point, with range checking. - * Returns the trie error value if c is not in the range 0..U+10FFFF. - * - * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST - * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width - * @param c (UChar32, in) the input code point - * @return The code point's trie value. - * @stable ICU 63 - */ -#define UCPTRIE_FAST_GET(trie, dataAccess, c) dataAccess(trie, _UCPTRIE_CP_INDEX(trie, 0xffff, c)) - -/** - * Returns a 16-bit trie value for a code point, with range checking. - * Returns the trie error value if c is not in the range U+0000..U+10FFFF. - * - * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_SMALL - * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width - * @param c (UChar32, in) the input code point - * @return The code point's trie value. - * @stable ICU 63 - */ -#define UCPTRIE_SMALL_GET(trie, dataAccess, c) \ - dataAccess(trie, _UCPTRIE_CP_INDEX(trie, UCPTRIE_SMALL_MAX, c)) - -/** - * UTF-16: Reads the next code point (UChar32 c, out), post-increments src, - * and gets a value from the trie. - * Sets the trie error value if c is an unpaired surrogate. - * - * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST - * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width - * @param src (const UChar *, in/out) the source text pointer - * @param limit (const UChar *, in) the limit pointer for the text, or NULL if NUL-terminated - * @param c (UChar32, out) variable for the code point - * @param result (out) variable for the trie lookup result - * @stable ICU 63 - */ -#define UCPTRIE_FAST_U16_NEXT(trie, dataAccess, src, limit, c, result) UPRV_BLOCK_MACRO_BEGIN { \ - (c) = *(src)++; \ - int32_t __index; \ - if (!U16_IS_SURROGATE(c)) { \ - __index = _UCPTRIE_FAST_INDEX(trie, c); \ - } else { \ - uint16_t __c2; \ - if (U16_IS_SURROGATE_LEAD(c) && (src) != (limit) && U16_IS_TRAIL(__c2 = *(src))) { \ - ++(src); \ - (c) = U16_GET_SUPPLEMENTARY((c), __c2); \ - __index = _UCPTRIE_SMALL_INDEX(trie, c); \ - } else { \ - __index = (trie)->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET; \ - } \ - } \ - (result) = dataAccess(trie, __index); \ -} UPRV_BLOCK_MACRO_END - -/** - * UTF-16: Reads the previous code point (UChar32 c, out), pre-decrements src, - * and gets a value from the trie. - * Sets the trie error value if c is an unpaired surrogate. - * - * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST - * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width - * @param start (const UChar *, in) the start pointer for the text - * @param src (const UChar *, in/out) the source text pointer - * @param c (UChar32, out) variable for the code point - * @param result (out) variable for the trie lookup result - * @stable ICU 63 - */ -#define UCPTRIE_FAST_U16_PREV(trie, dataAccess, start, src, c, result) UPRV_BLOCK_MACRO_BEGIN { \ - (c) = *--(src); \ - int32_t __index; \ - if (!U16_IS_SURROGATE(c)) { \ - __index = _UCPTRIE_FAST_INDEX(trie, c); \ - } else { \ - uint16_t __c2; \ - if (U16_IS_SURROGATE_TRAIL(c) && (src) != (start) && U16_IS_LEAD(__c2 = *((src) - 1))) { \ - --(src); \ - (c) = U16_GET_SUPPLEMENTARY(__c2, (c)); \ - __index = _UCPTRIE_SMALL_INDEX(trie, c); \ - } else { \ - __index = (trie)->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET; \ - } \ - } \ - (result) = dataAccess(trie, __index); \ -} UPRV_BLOCK_MACRO_END - -/** - * UTF-8: Post-increments src and gets a value from the trie. - * Sets the trie error value for an ill-formed byte sequence. - * - * Unlike UCPTRIE_FAST_U16_NEXT() this UTF-8 macro does not provide the code point - * because it would be more work to do so and is often not needed. - * If the trie value differs from the error value, then the byte sequence is well-formed, - * and the code point can be assembled without revalidation. - * - * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST - * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width - * @param src (const char *, in/out) the source text pointer - * @param limit (const char *, in) the limit pointer for the text (must not be NULL) - * @param result (out) variable for the trie lookup result - * @stable ICU 63 - */ -#define UCPTRIE_FAST_U8_NEXT(trie, dataAccess, src, limit, result) UPRV_BLOCK_MACRO_BEGIN { \ - int32_t __lead = (uint8_t)*(src)++; \ - if (!U8_IS_SINGLE(__lead)) { \ - uint8_t __t1, __t2, __t3; \ - if ((src) != (limit) && \ - (__lead >= 0xe0 ? \ - __lead < 0xf0 ? /* U+0800..U+FFFF except surrogates */ \ - U8_LEAD3_T1_BITS[__lead &= 0xf] & (1 << ((__t1 = *(src)) >> 5)) && \ - ++(src) != (limit) && (__t2 = *(src) - 0x80) <= 0x3f && \ - (__lead = ((int32_t)(trie)->index[(__lead << 6) + (__t1 & 0x3f)]) + __t2, 1) \ - : /* U+10000..U+10FFFF */ \ - (__lead -= 0xf0) <= 4 && \ - U8_LEAD4_T1_BITS[(__t1 = *(src)) >> 4] & (1 << __lead) && \ - (__lead = (__lead << 6) | (__t1 & 0x3f), ++(src) != (limit)) && \ - (__t2 = *(src) - 0x80) <= 0x3f && \ - ++(src) != (limit) && (__t3 = *(src) - 0x80) <= 0x3f && \ - (__lead = __lead >= (trie)->shifted12HighStart ? \ - (trie)->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET : \ - ucptrie_internalSmallU8Index((trie), __lead, __t2, __t3), 1) \ - : /* U+0080..U+07FF */ \ - __lead >= 0xc2 && (__t1 = *(src) - 0x80) <= 0x3f && \ - (__lead = (int32_t)(trie)->index[__lead & 0x1f] + __t1, 1))) { \ - ++(src); \ - } else { \ - __lead = (trie)->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET; /* ill-formed*/ \ - } \ - } \ - (result) = dataAccess(trie, __lead); \ -} UPRV_BLOCK_MACRO_END - -/** - * UTF-8: Pre-decrements src and gets a value from the trie. - * Sets the trie error value for an ill-formed byte sequence. - * - * Unlike UCPTRIE_FAST_U16_PREV() this UTF-8 macro does not provide the code point - * because it would be more work to do so and is often not needed. - * If the trie value differs from the error value, then the byte sequence is well-formed, - * and the code point can be assembled without revalidation. - * - * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST - * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width - * @param start (const char *, in) the start pointer for the text - * @param src (const char *, in/out) the source text pointer - * @param result (out) variable for the trie lookup result - * @stable ICU 63 - */ -#define UCPTRIE_FAST_U8_PREV(trie, dataAccess, start, src, result) UPRV_BLOCK_MACRO_BEGIN { \ - int32_t __index = (uint8_t)*--(src); \ - if (!U8_IS_SINGLE(__index)) { \ - __index = ucptrie_internalU8PrevIndex((trie), __index, (const uint8_t *)(start), \ - (const uint8_t *)(src)); \ - (src) -= __index & 7; \ - __index >>= 3; \ - } \ - (result) = dataAccess(trie, __index); \ -} UPRV_BLOCK_MACRO_END - -/** - * Returns a trie value for an ASCII code point, without range checking. - * - * @param trie (const UCPTrie *, in) the trie (of either fast or small type) - * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width - * @param c (UChar32, in) the input code point; must be U+0000..U+007F - * @return The ASCII code point's trie value. - * @stable ICU 63 - */ -#define UCPTRIE_ASCII_GET(trie, dataAccess, c) dataAccess(trie, c) - -/** - * Returns a trie value for a BMP code point (U+0000..U+FFFF), without range checking. - * Can be used to look up a value for a UTF-16 code unit if other parts of - * the string processing check for surrogates. - * - * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST - * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width - * @param c (UChar32, in) the input code point, must be U+0000..U+FFFF - * @return The BMP code point's trie value. - * @stable ICU 63 - */ -#define UCPTRIE_FAST_BMP_GET(trie, dataAccess, c) dataAccess(trie, _UCPTRIE_FAST_INDEX(trie, c)) - -/** - * Returns a trie value for a supplementary code point (U+10000..U+10FFFF), - * without range checking. - * - * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST - * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width - * @param c (UChar32, in) the input code point, must be U+10000..U+10FFFF - * @return The supplementary code point's trie value. - * @stable ICU 63 - */ -#define UCPTRIE_FAST_SUPP_GET(trie, dataAccess, c) dataAccess(trie, _UCPTRIE_SMALL_INDEX(trie, c)) - -/* Internal definitions ----------------------------------------------------- */ - -#ifndef U_IN_DOXYGEN - -/** - * Internal implementation constants. - * These are needed for the API macros, but users should not use these directly. - * @internal - */ -enum { - /** @internal */ - UCPTRIE_FAST_SHIFT = 6, - - /** Number of entries in a data block for code points below the fast limit. 64=0x40 @internal */ - UCPTRIE_FAST_DATA_BLOCK_LENGTH = 1 << UCPTRIE_FAST_SHIFT, - - /** Mask for getting the lower bits for the in-fast-data-block offset. @internal */ - UCPTRIE_FAST_DATA_MASK = UCPTRIE_FAST_DATA_BLOCK_LENGTH - 1, - - /** @internal */ - UCPTRIE_SMALL_MAX = 0xfff, - - /** - * Offset from dataLength (to be subtracted) for fetching the - * value returned for out-of-range code points and ill-formed UTF-8/16. - * @internal - */ - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET = 1, - /** - * Offset from dataLength (to be subtracted) for fetching the - * value returned for code points highStart..U+10FFFF. - * @internal - */ - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET = 2 -}; - -/* Internal functions and macros -------------------------------------------- */ -// Do not conditionalize with #ifndef U_HIDE_INTERNAL_API, needed for public API - -/** @internal */ -U_CAPI int32_t U_EXPORT2 -ucptrie_internalSmallIndex(const UCPTrie *trie, UChar32 c); - -/** @internal */ -U_CAPI int32_t U_EXPORT2 -ucptrie_internalSmallU8Index(const UCPTrie *trie, int32_t lt1, uint8_t t2, uint8_t t3); - -/** - * Internal function for part of the UCPTRIE_FAST_U8_PREVxx() macro implementations. - * Do not call directly. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -ucptrie_internalU8PrevIndex(const UCPTrie *trie, UChar32 c, - const uint8_t *start, const uint8_t *src); - -/** Internal trie getter for a code point below the fast limit. Returns the data index. @internal */ -#define _UCPTRIE_FAST_INDEX(trie, c) \ - ((int32_t)(trie)->index[(c) >> UCPTRIE_FAST_SHIFT] + ((c) & UCPTRIE_FAST_DATA_MASK)) - -/** Internal trie getter for a code point at or above the fast limit. Returns the data index. @internal */ -#define _UCPTRIE_SMALL_INDEX(trie, c) \ - ((c) >= (trie)->highStart ? \ - (trie)->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET : \ - ucptrie_internalSmallIndex(trie, c)) - -/** - * Internal trie getter for a code point, with checking that c is in U+0000..10FFFF. - * Returns the data index. - * @internal - */ -#define _UCPTRIE_CP_INDEX(trie, fastMax, c) \ - ((uint32_t)(c) <= (uint32_t)(fastMax) ? \ - _UCPTRIE_FAST_INDEX(trie, c) : \ - (uint32_t)(c) <= 0x10ffff ? \ - _UCPTRIE_SMALL_INDEX(trie, c) : \ - (trie)->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET) - -U_CDECL_END - -#endif // U_IN_DOXYGEN - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUCPTriePointer - * "Smart pointer" class, closes a UCPTrie via ucptrie_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 63 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUCPTriePointer, UCPTrie, ucptrie_close); - -U_NAMESPACE_END - -#endif // U_SHOW_CPLUSPLUS_API - -#endif +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// ucptrie.h (modified from utrie2.h) +// created: 2017dec29 Markus W. Scherer + +#ifndef __UCPTRIE_H__ +#define __UCPTRIE_H__ + +#include "unicode/utypes.h" +#include "unicode/ucpmap.h" +#include "unicode/utf8.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +U_CDECL_BEGIN + +/** + * \file + * \brief C API: This file defines an immutable Unicode code point trie. + * + * @see UCPTrie + * @see UMutableCPTrie + */ + +#ifndef U_IN_DOXYGEN +/** @internal */ +typedef union UCPTrieData { + /** @internal */ + const void *ptr0; + /** @internal */ + const uint16_t *ptr16; + /** @internal */ + const uint32_t *ptr32; + /** @internal */ + const uint8_t *ptr8; +} UCPTrieData; +#endif + +/** + * Immutable Unicode code point trie structure. + * Fast, reasonably compact, map from Unicode code points (U+0000..U+10FFFF) to integer values. + * For details see https://icu.unicode.org/design/struct/utrie + * + * Do not access UCPTrie fields directly; use public functions and macros. + * Functions are easy to use: They support all trie types and value widths. + * + * When performance is really important, macros provide faster access. + * Most macros are specific to either "fast" or "small" tries, see UCPTrieType. + * There are "fast" macros for special optimized use cases. + * + * The macros will return bogus values, or may crash, if used on the wrong type or value width. + * + * @see UMutableCPTrie + * @stable ICU 63 + */ +struct UCPTrie { +#ifndef U_IN_DOXYGEN + /** @internal */ + const uint16_t *index; + /** @internal */ + UCPTrieData data; + + /** @internal */ + int32_t indexLength; + /** @internal */ + int32_t dataLength; + /** Start of the last range which ends at U+10FFFF. @internal */ + UChar32 highStart; + /** highStart>>12 @internal */ + uint16_t shifted12HighStart; + + /** @internal */ + int8_t type; // UCPTrieType + /** @internal */ + int8_t valueWidth; // UCPTrieValueWidth + + /** padding/reserved @internal */ + uint32_t reserved32; + /** padding/reserved @internal */ + uint16_t reserved16; + + /** + * Internal index-3 null block offset. + * Set to an impossibly high value (e.g., 0xffff) if there is no dedicated index-3 null block. + * @internal + */ + uint16_t index3NullOffset; + /** + * Internal data null block offset, not shifted. + * Set to an impossibly high value (e.g., 0xfffff) if there is no dedicated data null block. + * @internal + */ + int32_t dataNullOffset; + /** @internal */ + uint32_t nullValue; + +#ifdef UCPTRIE_DEBUG + /** @internal */ + const char *name; +#endif +#endif +}; +#ifndef U_IN_DOXYGEN +typedef struct UCPTrie UCPTrie; +#endif + +/** + * Selectors for the type of a UCPTrie. + * Different trade-offs for size vs. speed. + * + * @see umutablecptrie_buildImmutable + * @see ucptrie_openFromBinary + * @see ucptrie_getType + * @stable ICU 63 + */ +enum UCPTrieType { + /** + * For ucptrie_openFromBinary() to accept any type. + * ucptrie_getType() will return the actual type. + * @stable ICU 63 + */ + UCPTRIE_TYPE_ANY = -1, + /** + * Fast/simple/larger BMP data structure. Use functions and "fast" macros. + * @stable ICU 63 + */ + UCPTRIE_TYPE_FAST, + /** + * Small/slower BMP data structure. Use functions and "small" macros. + * @stable ICU 63 + */ + UCPTRIE_TYPE_SMALL +}; +#ifndef U_IN_DOXYGEN +typedef enum UCPTrieType UCPTrieType; +#endif + +/** + * Selectors for the number of bits in a UCPTrie data value. + * + * @see umutablecptrie_buildImmutable + * @see ucptrie_openFromBinary + * @see ucptrie_getValueWidth + * @stable ICU 63 + */ +enum UCPTrieValueWidth { + /** + * For ucptrie_openFromBinary() to accept any data value width. + * ucptrie_getValueWidth() will return the actual data value width. + * @stable ICU 63 + */ + UCPTRIE_VALUE_BITS_ANY = -1, + /** + * The trie stores 16 bits per data value. + * It returns them as unsigned values 0..0xffff=65535. + * @stable ICU 63 + */ + UCPTRIE_VALUE_BITS_16, + /** + * The trie stores 32 bits per data value. + * @stable ICU 63 + */ + UCPTRIE_VALUE_BITS_32, + /** + * The trie stores 8 bits per data value. + * It returns them as unsigned values 0..0xff=255. + * @stable ICU 63 + */ + UCPTRIE_VALUE_BITS_8 +}; +#ifndef U_IN_DOXYGEN +typedef enum UCPTrieValueWidth UCPTrieValueWidth; +#endif + +/** + * Opens a trie from its binary form, stored in 32-bit-aligned memory. + * Inverse of ucptrie_toBinary(). + * + * The memory must remain valid and unchanged as long as the trie is used. + * You must ucptrie_close() the trie once you are done using it. + * + * @param type selects the trie type; results in an + * U_INVALID_FORMAT_ERROR if it does not match the binary data; + * use UCPTRIE_TYPE_ANY to accept any type + * @param valueWidth selects the number of bits in a data value; results in an + * U_INVALID_FORMAT_ERROR if it does not match the binary data; + * use UCPTRIE_VALUE_BITS_ANY to accept any data value width + * @param data a pointer to 32-bit-aligned memory containing the binary data of a UCPTrie + * @param length the number of bytes available at data; + * can be more than necessary + * @param pActualLength receives the actual number of bytes at data taken up by the trie data; + * can be NULL + * @param pErrorCode an in/out ICU UErrorCode + * @return the trie + * + * @see umutablecptrie_open + * @see umutablecptrie_buildImmutable + * @see ucptrie_toBinary + * @stable ICU 63 + */ +U_CAPI UCPTrie * U_EXPORT2 +ucptrie_openFromBinary(UCPTrieType type, UCPTrieValueWidth valueWidth, + const void *data, int32_t length, int32_t *pActualLength, + UErrorCode *pErrorCode); + +/** + * Closes a trie and releases associated memory. + * + * @param trie the trie + * @stable ICU 63 + */ +U_CAPI void U_EXPORT2 +ucptrie_close(UCPTrie *trie); + +/** + * Returns the trie type. + * + * @param trie the trie + * @return the trie type + * @see ucptrie_openFromBinary + * @see UCPTRIE_TYPE_ANY + * @stable ICU 63 + */ +U_CAPI UCPTrieType U_EXPORT2 +ucptrie_getType(const UCPTrie *trie); + +/** + * Returns the number of bits in a trie data value. + * + * @param trie the trie + * @return the number of bits in a trie data value + * @see ucptrie_openFromBinary + * @see UCPTRIE_VALUE_BITS_ANY + * @stable ICU 63 + */ +U_CAPI UCPTrieValueWidth U_EXPORT2 +ucptrie_getValueWidth(const UCPTrie *trie); + +/** + * Returns the value for a code point as stored in the trie, with range checking. + * Returns the trie error value if c is not in the range 0..U+10FFFF. + * + * Easier to use than UCPTRIE_FAST_GET() and similar macros but slower. + * Easier to use because, unlike the macros, this function works on all UCPTrie + * objects, for all types and value widths. + * + * @param trie the trie + * @param c the code point + * @return the trie value, + * or the trie error value if the code point is not in the range 0..U+10FFFF + * @stable ICU 63 + */ +U_CAPI uint32_t U_EXPORT2 +ucptrie_get(const UCPTrie *trie, UChar32 c); + +/** + * Returns the last code point such that all those from start to there have the same value. + * Can be used to efficiently iterate over all same-value ranges in a trie. + * (This is normally faster than iterating over code points and get()ting each value, + * but much slower than a data structure that stores ranges directly.) + * + * If the UCPMapValueFilter function pointer is not NULL, then + * the value to be delivered is passed through that function, and the return value is the end + * of the range where all values are modified to the same actual value. + * The value is unchanged if that function pointer is NULL. + * + * Example: + * \code + * UChar32 start = 0, end; + * uint32_t value; + * while ((end = ucptrie_getRange(trie, start, UCPMAP_RANGE_NORMAL, 0, + * NULL, NULL, &value)) >= 0) { + * // Work with the range start..end and its value. + * start = end + 1; + * } + * \endcode + * + * @param trie the trie + * @param start range start + * @param option defines whether surrogates are treated normally, + * or as having the surrogateValue; usually UCPMAP_RANGE_NORMAL + * @param surrogateValue value for surrogates; ignored if option==UCPMAP_RANGE_NORMAL + * @param filter a pointer to a function that may modify the trie data value, + * or NULL if the values from the trie are to be used unmodified + * @param context an opaque pointer that is passed on to the filter function + * @param pValue if not NULL, receives the value that every code point start..end has; + * may have been modified by filter(context, trie value) + * if that function pointer is not NULL + * @return the range end code point, or -1 if start is not a valid code point + * @stable ICU 63 + */ +U_CAPI UChar32 U_EXPORT2 +ucptrie_getRange(const UCPTrie *trie, UChar32 start, + UCPMapRangeOption option, uint32_t surrogateValue, + UCPMapValueFilter *filter, const void *context, uint32_t *pValue); + +/** + * Writes a memory-mappable form of the trie into 32-bit aligned memory. + * Inverse of ucptrie_openFromBinary(). + * + * @param trie the trie + * @param data a pointer to 32-bit-aligned memory to be filled with the trie data; + * can be NULL if capacity==0 + * @param capacity the number of bytes available at data, or 0 for pure preflighting + * @param pErrorCode an in/out ICU UErrorCode; + * U_BUFFER_OVERFLOW_ERROR if the capacity is too small + * @return the number of bytes written or (if buffer overflow) needed for the trie + * + * @see ucptrie_openFromBinary() + * @stable ICU 63 + */ +U_CAPI int32_t U_EXPORT2 +ucptrie_toBinary(const UCPTrie *trie, void *data, int32_t capacity, UErrorCode *pErrorCode); + +/** + * Macro parameter value for a trie with 16-bit data values. + * Use the name of this macro as a "dataAccess" parameter in other macros. + * Do not use this macro in any other way. + * + * @see UCPTRIE_VALUE_BITS_16 + * @stable ICU 63 + */ +#define UCPTRIE_16(trie, i) ((trie)->data.ptr16[i]) + +/** + * Macro parameter value for a trie with 32-bit data values. + * Use the name of this macro as a "dataAccess" parameter in other macros. + * Do not use this macro in any other way. + * + * @see UCPTRIE_VALUE_BITS_32 + * @stable ICU 63 + */ +#define UCPTRIE_32(trie, i) ((trie)->data.ptr32[i]) + +/** + * Macro parameter value for a trie with 8-bit data values. + * Use the name of this macro as a "dataAccess" parameter in other macros. + * Do not use this macro in any other way. + * + * @see UCPTRIE_VALUE_BITS_8 + * @stable ICU 63 + */ +#define UCPTRIE_8(trie, i) ((trie)->data.ptr8[i]) + +/** + * Returns a trie value for a code point, with range checking. + * Returns the trie error value if c is not in the range 0..U+10FFFF. + * + * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST + * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width + * @param c (UChar32, in) the input code point + * @return The code point's trie value. + * @stable ICU 63 + */ +#define UCPTRIE_FAST_GET(trie, dataAccess, c) dataAccess(trie, _UCPTRIE_CP_INDEX(trie, 0xffff, c)) + +/** + * Returns a 16-bit trie value for a code point, with range checking. + * Returns the trie error value if c is not in the range U+0000..U+10FFFF. + * + * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_SMALL + * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width + * @param c (UChar32, in) the input code point + * @return The code point's trie value. + * @stable ICU 63 + */ +#define UCPTRIE_SMALL_GET(trie, dataAccess, c) \ + dataAccess(trie, _UCPTRIE_CP_INDEX(trie, UCPTRIE_SMALL_MAX, c)) + +/** + * UTF-16: Reads the next code point (UChar32 c, out), post-increments src, + * and gets a value from the trie. + * Sets the trie error value if c is an unpaired surrogate. + * + * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST + * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width + * @param src (const UChar *, in/out) the source text pointer + * @param limit (const UChar *, in) the limit pointer for the text, or NULL if NUL-terminated + * @param c (UChar32, out) variable for the code point + * @param result (out) variable for the trie lookup result + * @stable ICU 63 + */ +#define UCPTRIE_FAST_U16_NEXT(trie, dataAccess, src, limit, c, result) UPRV_BLOCK_MACRO_BEGIN { \ + (c) = *(src)++; \ + int32_t __index; \ + if (!U16_IS_SURROGATE(c)) { \ + __index = _UCPTRIE_FAST_INDEX(trie, c); \ + } else { \ + uint16_t __c2; \ + if (U16_IS_SURROGATE_LEAD(c) && (src) != (limit) && U16_IS_TRAIL(__c2 = *(src))) { \ + ++(src); \ + (c) = U16_GET_SUPPLEMENTARY((c), __c2); \ + __index = _UCPTRIE_SMALL_INDEX(trie, c); \ + } else { \ + __index = (trie)->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET; \ + } \ + } \ + (result) = dataAccess(trie, __index); \ +} UPRV_BLOCK_MACRO_END + +/** + * UTF-16: Reads the previous code point (UChar32 c, out), pre-decrements src, + * and gets a value from the trie. + * Sets the trie error value if c is an unpaired surrogate. + * + * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST + * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width + * @param start (const UChar *, in) the start pointer for the text + * @param src (const UChar *, in/out) the source text pointer + * @param c (UChar32, out) variable for the code point + * @param result (out) variable for the trie lookup result + * @stable ICU 63 + */ +#define UCPTRIE_FAST_U16_PREV(trie, dataAccess, start, src, c, result) UPRV_BLOCK_MACRO_BEGIN { \ + (c) = *--(src); \ + int32_t __index; \ + if (!U16_IS_SURROGATE(c)) { \ + __index = _UCPTRIE_FAST_INDEX(trie, c); \ + } else { \ + uint16_t __c2; \ + if (U16_IS_SURROGATE_TRAIL(c) && (src) != (start) && U16_IS_LEAD(__c2 = *((src) - 1))) { \ + --(src); \ + (c) = U16_GET_SUPPLEMENTARY(__c2, (c)); \ + __index = _UCPTRIE_SMALL_INDEX(trie, c); \ + } else { \ + __index = (trie)->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET; \ + } \ + } \ + (result) = dataAccess(trie, __index); \ +} UPRV_BLOCK_MACRO_END + +/** + * UTF-8: Post-increments src and gets a value from the trie. + * Sets the trie error value for an ill-formed byte sequence. + * + * Unlike UCPTRIE_FAST_U16_NEXT() this UTF-8 macro does not provide the code point + * because it would be more work to do so and is often not needed. + * If the trie value differs from the error value, then the byte sequence is well-formed, + * and the code point can be assembled without revalidation. + * + * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST + * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width + * @param src (const char *, in/out) the source text pointer + * @param limit (const char *, in) the limit pointer for the text (must not be NULL) + * @param result (out) variable for the trie lookup result + * @stable ICU 63 + */ +#define UCPTRIE_FAST_U8_NEXT(trie, dataAccess, src, limit, result) UPRV_BLOCK_MACRO_BEGIN { \ + int32_t __lead = (uint8_t)*(src)++; \ + if (!U8_IS_SINGLE(__lead)) { \ + uint8_t __t1, __t2, __t3; \ + if ((src) != (limit) && \ + (__lead >= 0xe0 ? \ + __lead < 0xf0 ? /* U+0800..U+FFFF except surrogates */ \ + U8_LEAD3_T1_BITS[__lead &= 0xf] & (1 << ((__t1 = *(src)) >> 5)) && \ + ++(src) != (limit) && (__t2 = *(src) - 0x80) <= 0x3f && \ + (__lead = ((int32_t)(trie)->index[(__lead << 6) + (__t1 & 0x3f)]) + __t2, 1) \ + : /* U+10000..U+10FFFF */ \ + (__lead -= 0xf0) <= 4 && \ + U8_LEAD4_T1_BITS[(__t1 = *(src)) >> 4] & (1 << __lead) && \ + (__lead = (__lead << 6) | (__t1 & 0x3f), ++(src) != (limit)) && \ + (__t2 = *(src) - 0x80) <= 0x3f && \ + ++(src) != (limit) && (__t3 = *(src) - 0x80) <= 0x3f && \ + (__lead = __lead >= (trie)->shifted12HighStart ? \ + (trie)->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET : \ + ucptrie_internalSmallU8Index((trie), __lead, __t2, __t3), 1) \ + : /* U+0080..U+07FF */ \ + __lead >= 0xc2 && (__t1 = *(src) - 0x80) <= 0x3f && \ + (__lead = (int32_t)(trie)->index[__lead & 0x1f] + __t1, 1))) { \ + ++(src); \ + } else { \ + __lead = (trie)->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET; /* ill-formed*/ \ + } \ + } \ + (result) = dataAccess(trie, __lead); \ +} UPRV_BLOCK_MACRO_END + +/** + * UTF-8: Pre-decrements src and gets a value from the trie. + * Sets the trie error value for an ill-formed byte sequence. + * + * Unlike UCPTRIE_FAST_U16_PREV() this UTF-8 macro does not provide the code point + * because it would be more work to do so and is often not needed. + * If the trie value differs from the error value, then the byte sequence is well-formed, + * and the code point can be assembled without revalidation. + * + * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST + * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width + * @param start (const char *, in) the start pointer for the text + * @param src (const char *, in/out) the source text pointer + * @param result (out) variable for the trie lookup result + * @stable ICU 63 + */ +#define UCPTRIE_FAST_U8_PREV(trie, dataAccess, start, src, result) UPRV_BLOCK_MACRO_BEGIN { \ + int32_t __index = (uint8_t)*--(src); \ + if (!U8_IS_SINGLE(__index)) { \ + __index = ucptrie_internalU8PrevIndex((trie), __index, (const uint8_t *)(start), \ + (const uint8_t *)(src)); \ + (src) -= __index & 7; \ + __index >>= 3; \ + } \ + (result) = dataAccess(trie, __index); \ +} UPRV_BLOCK_MACRO_END + +/** + * Returns a trie value for an ASCII code point, without range checking. + * + * @param trie (const UCPTrie *, in) the trie (of either fast or small type) + * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width + * @param c (UChar32, in) the input code point; must be U+0000..U+007F + * @return The ASCII code point's trie value. + * @stable ICU 63 + */ +#define UCPTRIE_ASCII_GET(trie, dataAccess, c) dataAccess(trie, c) + +/** + * Returns a trie value for a BMP code point (U+0000..U+FFFF), without range checking. + * Can be used to look up a value for a UTF-16 code unit if other parts of + * the string processing check for surrogates. + * + * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST + * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width + * @param c (UChar32, in) the input code point, must be U+0000..U+FFFF + * @return The BMP code point's trie value. + * @stable ICU 63 + */ +#define UCPTRIE_FAST_BMP_GET(trie, dataAccess, c) dataAccess(trie, _UCPTRIE_FAST_INDEX(trie, c)) + +/** + * Returns a trie value for a supplementary code point (U+10000..U+10FFFF), + * without range checking. + * + * @param trie (const UCPTrie *, in) the trie; must have type UCPTRIE_TYPE_FAST + * @param dataAccess UCPTRIE_16, UCPTRIE_32, or UCPTRIE_8 according to the trie’s value width + * @param c (UChar32, in) the input code point, must be U+10000..U+10FFFF + * @return The supplementary code point's trie value. + * @stable ICU 63 + */ +#define UCPTRIE_FAST_SUPP_GET(trie, dataAccess, c) dataAccess(trie, _UCPTRIE_SMALL_INDEX(trie, c)) + +/* Internal definitions ----------------------------------------------------- */ + +#ifndef U_IN_DOXYGEN + +/** + * Internal implementation constants. + * These are needed for the API macros, but users should not use these directly. + * @internal + */ +enum { + /** @internal */ + UCPTRIE_FAST_SHIFT = 6, + + /** Number of entries in a data block for code points below the fast limit. 64=0x40 @internal */ + UCPTRIE_FAST_DATA_BLOCK_LENGTH = 1 << UCPTRIE_FAST_SHIFT, + + /** Mask for getting the lower bits for the in-fast-data-block offset. @internal */ + UCPTRIE_FAST_DATA_MASK = UCPTRIE_FAST_DATA_BLOCK_LENGTH - 1, + + /** @internal */ + UCPTRIE_SMALL_MAX = 0xfff, + + /** + * Offset from dataLength (to be subtracted) for fetching the + * value returned for out-of-range code points and ill-formed UTF-8/16. + * @internal + */ + UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET = 1, + /** + * Offset from dataLength (to be subtracted) for fetching the + * value returned for code points highStart..U+10FFFF. + * @internal + */ + UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET = 2 +}; + +/* Internal functions and macros -------------------------------------------- */ +// Do not conditionalize with #ifndef U_HIDE_INTERNAL_API, needed for public API + +/** @internal */ +U_CAPI int32_t U_EXPORT2 +ucptrie_internalSmallIndex(const UCPTrie *trie, UChar32 c); + +/** @internal */ +U_CAPI int32_t U_EXPORT2 +ucptrie_internalSmallU8Index(const UCPTrie *trie, int32_t lt1, uint8_t t2, uint8_t t3); + +/** + * Internal function for part of the UCPTRIE_FAST_U8_PREVxx() macro implementations. + * Do not call directly. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +ucptrie_internalU8PrevIndex(const UCPTrie *trie, UChar32 c, + const uint8_t *start, const uint8_t *src); + +/** Internal trie getter for a code point below the fast limit. Returns the data index. @internal */ +#define _UCPTRIE_FAST_INDEX(trie, c) \ + ((int32_t)(trie)->index[(c) >> UCPTRIE_FAST_SHIFT] + ((c) & UCPTRIE_FAST_DATA_MASK)) + +/** Internal trie getter for a code point at or above the fast limit. Returns the data index. @internal */ +#define _UCPTRIE_SMALL_INDEX(trie, c) \ + ((c) >= (trie)->highStart ? \ + (trie)->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET : \ + ucptrie_internalSmallIndex(trie, c)) + +/** + * Internal trie getter for a code point, with checking that c is in U+0000..10FFFF. + * Returns the data index. + * @internal + */ +#define _UCPTRIE_CP_INDEX(trie, fastMax, c) \ + ((uint32_t)(c) <= (uint32_t)(fastMax) ? \ + _UCPTRIE_FAST_INDEX(trie, c) : \ + (uint32_t)(c) <= 0x10ffff ? \ + _UCPTRIE_SMALL_INDEX(trie, c) : \ + (trie)->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET) + +U_CDECL_END + +#endif // U_IN_DOXYGEN + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUCPTriePointer + * "Smart pointer" class, closes a UCPTrie via ucptrie_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 63 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUCPTriePointer, UCPTrie, ucptrie_close); + +U_NAMESPACE_END + +#endif // U_SHOW_CPLUSPLUS_API + +#endif diff --git a/deps/icu-small/source/common/unicode/ucurr.h b/deps/icu-small/source/common/unicode/ucurr.h index 5589e799904914..18ca414d7e7357 100644 --- a/deps/icu-small/source/common/unicode/ucurr.h +++ b/deps/icu-small/source/common/unicode/ucurr.h @@ -1,466 +1,466 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ -#ifndef _UCURR_H_ -#define _UCURR_H_ - -#include "unicode/utypes.h" -#include "unicode/uenum.h" - -/** - * \file - * \brief C API: Encapsulates information about a currency. - * - * The ucurr API encapsulates information about a currency, as defined by - * ISO 4217. A currency is represented by a 3-character string - * containing its ISO 4217 code. This API can return various data - * necessary the proper display of a currency: - * - *
  • A display symbol, for a specific locale - *
  • The number of fraction digits to display - *
  • A rounding increment - *
- * - * The DecimalFormat class uses these data to display - * currencies. - * @author Alan Liu - * @since ICU 2.2 - */ - -#if !UCONFIG_NO_FORMATTING - -/** - * Currency Usage used for Decimal Format - * @stable ICU 54 - */ -enum UCurrencyUsage { - /** - * a setting to specify currency usage which determines currency digit - * and rounding for standard usage, for example: "50.00 NT$" - * used as DEFAULT value - * @stable ICU 54 - */ - UCURR_USAGE_STANDARD=0, - /** - * a setting to specify currency usage which determines currency digit - * and rounding for cash usage, for example: "50 NT$" - * @stable ICU 54 - */ - UCURR_USAGE_CASH=1, -#ifndef U_HIDE_DEPRECATED_API - /** - * One higher than the last enum UCurrencyUsage constant. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UCURR_USAGE_COUNT=2 -#endif // U_HIDE_DEPRECATED_API -}; -/** Currency Usage used for Decimal Format */ -typedef enum UCurrencyUsage UCurrencyUsage; - -/** - * Finds a currency code for the given locale. - * @param locale the locale for which to retrieve a currency code. - * Currency can be specified by the "currency" keyword - * in which case it overrides the default currency code - * @param buff fill in buffer. Can be NULL for preflighting. - * @param buffCapacity capacity of the fill in buffer. Can be 0 for - * preflighting. If it is non-zero, the buff parameter - * must not be NULL. - * @param ec error code - * @return length of the currency string. It should always be 3. If 0, - * currency couldn't be found or the input values are - * invalid. - * @stable ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -ucurr_forLocale(const char* locale, - UChar* buff, - int32_t buffCapacity, - UErrorCode* ec); - -/** - * Selector constants for ucurr_getName(). - * - * @see ucurr_getName - * @stable ICU 2.6 - */ -typedef enum UCurrNameStyle { - /** - * Selector for ucurr_getName indicating a symbolic name for a - * currency, such as "$" for USD. - * @stable ICU 2.6 - */ - UCURR_SYMBOL_NAME, - - /** - * Selector for ucurr_getName indicating the long name for a - * currency, such as "US Dollar" for USD. - * @stable ICU 2.6 - */ - UCURR_LONG_NAME, - - /** - * Selector for getName() indicating the narrow currency symbol. - * The narrow currency symbol is similar to the regular currency - * symbol, but it always takes the shortest form: for example, - * "$" instead of "US$" for USD in en-CA. - * - * @stable ICU 61 - */ - UCURR_NARROW_SYMBOL_NAME, - - /** - * Selector for getName() indicating the formal currency symbol. - * The formal currency symbol is similar to the regular currency - * symbol, but it always takes the form used in formal settings - * such as banking; for example, "NT$" instead of "$" for TWD in zh-TW. - * - * @stable ICU 68 - */ - UCURR_FORMAL_SYMBOL_NAME, - - /** - * Selector for getName() indicating the variant currency symbol. - * The variant symbol for a currency is an alternative symbol - * that is not necessarily as widely used as the regular symbol. - * - * @stable ICU 68 - */ - UCURR_VARIANT_SYMBOL_NAME - -} UCurrNameStyle; - -#if !UCONFIG_NO_SERVICE -/** - * @stable ICU 2.6 - */ -typedef const void* UCurrRegistryKey; - -/** - * Register an (existing) ISO 4217 currency code for the given locale. - * Only the country code and the two variants EURO and PRE_EURO are - * recognized. - * @param isoCode the three-letter ISO 4217 currency code - * @param locale the locale for which to register this currency code - * @param status the in/out status code - * @return a registry key that can be used to unregister this currency code, or NULL - * if there was an error. - * @stable ICU 2.6 - */ -U_CAPI UCurrRegistryKey U_EXPORT2 -ucurr_register(const UChar* isoCode, - const char* locale, - UErrorCode* status); -/** - * Unregister the previously-registered currency definitions using the - * URegistryKey returned from ucurr_register. Key becomes invalid after - * a successful call and should not be used again. Any currency - * that might have been hidden by the original ucurr_register call is - * restored. - * @param key the registry key returned by a previous call to ucurr_register - * @param status the in/out status code, no special meanings are assigned - * @return true if the currency for this key was successfully unregistered - * @stable ICU 2.6 - */ -U_CAPI UBool U_EXPORT2 -ucurr_unregister(UCurrRegistryKey key, UErrorCode* status); -#endif /* UCONFIG_NO_SERVICE */ - -/** - * Returns the display name for the given currency in the - * given locale. For example, the display name for the USD - * currency object in the en_US locale is "$". - * @param currency null-terminated 3-letter ISO 4217 code - * @param locale locale in which to display currency - * @param nameStyle selector for which kind of name to return - * @param isChoiceFormat always set to false, or can be NULL; - * display names are static strings; - * since ICU 4.4, ChoiceFormat patterns are no longer supported - * @param len fill-in parameter to receive length of result - * @param ec error code - * @return pointer to display string of 'len' UChars. If the resource - * data contains no entry for 'currency', then 'currency' itself is - * returned. - * @stable ICU 2.6 - */ -U_CAPI const UChar* U_EXPORT2 -ucurr_getName(const UChar* currency, - const char* locale, - UCurrNameStyle nameStyle, - UBool* isChoiceFormat, - int32_t* len, - UErrorCode* ec); - -/** - * Returns the plural name for the given currency in the - * given locale. For example, the plural name for the USD - * currency object in the en_US locale is "US dollar" or "US dollars". - * @param currency null-terminated 3-letter ISO 4217 code - * @param locale locale in which to display currency - * @param isChoiceFormat always set to false, or can be NULL; - * display names are static strings; - * since ICU 4.4, ChoiceFormat patterns are no longer supported - * @param pluralCount plural count - * @param len fill-in parameter to receive length of result - * @param ec error code - * @return pointer to display string of 'len' UChars. If the resource - * data contains no entry for 'currency', then 'currency' itself is - * returned. - * @stable ICU 4.2 - */ -U_CAPI const UChar* U_EXPORT2 -ucurr_getPluralName(const UChar* currency, - const char* locale, - UBool* isChoiceFormat, - const char* pluralCount, - int32_t* len, - UErrorCode* ec); - -/** - * Returns the number of the number of fraction digits that should - * be displayed for the given currency. - * This is equivalent to ucurr_getDefaultFractionDigitsForUsage(currency,UCURR_USAGE_STANDARD,ec); - * - * Important: The number of fraction digits for a given currency is NOT - * guaranteed to be constant across versions of ICU or CLDR. For example, - * do NOT use this value as a mechanism for deciding the magnitude used - * to store currency values in a database. You should use this value for - * display purposes only. - * - * @param currency null-terminated 3-letter ISO 4217 code - * @param ec input-output error code - * @return a non-negative number of fraction digits to be - * displayed, or 0 if there is an error - * @stable ICU 3.0 - */ -U_CAPI int32_t U_EXPORT2 -ucurr_getDefaultFractionDigits(const UChar* currency, - UErrorCode* ec); - -/** - * Returns the number of the number of fraction digits that should - * be displayed for the given currency with usage. - * - * Important: The number of fraction digits for a given currency is NOT - * guaranteed to be constant across versions of ICU or CLDR. For example, - * do NOT use this value as a mechanism for deciding the magnitude used - * to store currency values in a database. You should use this value for - * display purposes only. - * - * @param currency null-terminated 3-letter ISO 4217 code - * @param usage enum usage for the currency - * @param ec input-output error code - * @return a non-negative number of fraction digits to be - * displayed, or 0 if there is an error - * @stable ICU 54 - */ -U_CAPI int32_t U_EXPORT2 -ucurr_getDefaultFractionDigitsForUsage(const UChar* currency, - const UCurrencyUsage usage, - UErrorCode* ec); - -/** - * Returns the rounding increment for the given currency, or 0.0 if no - * rounding is done by the currency. - * This is equivalent to ucurr_getRoundingIncrementForUsage(currency,UCURR_USAGE_STANDARD,ec); - * @param currency null-terminated 3-letter ISO 4217 code - * @param ec input-output error code - * @return the non-negative rounding increment, or 0.0 if none, - * or 0.0 if there is an error - * @stable ICU 3.0 - */ -U_CAPI double U_EXPORT2 -ucurr_getRoundingIncrement(const UChar* currency, - UErrorCode* ec); - -/** - * Returns the rounding increment for the given currency, or 0.0 if no - * rounding is done by the currency given usage. - * @param currency null-terminated 3-letter ISO 4217 code - * @param usage enum usage for the currency - * @param ec input-output error code - * @return the non-negative rounding increment, or 0.0 if none, - * or 0.0 if there is an error - * @stable ICU 54 - */ -U_CAPI double U_EXPORT2 -ucurr_getRoundingIncrementForUsage(const UChar* currency, - const UCurrencyUsage usage, - UErrorCode* ec); - -/** - * Selector constants for ucurr_openCurrencies(). - * - * @see ucurr_openCurrencies - * @stable ICU 3.2 - */ -typedef enum UCurrCurrencyType { - /** - * Select all ISO-4217 currency codes. - * @stable ICU 3.2 - */ - UCURR_ALL = INT32_MAX, - /** - * Select only ISO-4217 commonly used currency codes. - * These currencies can be found in common use, and they usually have - * bank notes or coins associated with the currency code. - * This does not include fund codes, precious metals and other - * various ISO-4217 codes limited to special financial products. - * @stable ICU 3.2 - */ - UCURR_COMMON = 1, - /** - * Select ISO-4217 uncommon currency codes. - * These codes respresent fund codes, precious metals and other - * various ISO-4217 codes limited to special financial products. - * A fund code is a monetary resource associated with a currency. - * @stable ICU 3.2 - */ - UCURR_UNCOMMON = 2, - /** - * Select only deprecated ISO-4217 codes. - * These codes are no longer in general public use. - * @stable ICU 3.2 - */ - UCURR_DEPRECATED = 4, - /** - * Select only non-deprecated ISO-4217 codes. - * These codes are in general public use. - * @stable ICU 3.2 - */ - UCURR_NON_DEPRECATED = 8 -} UCurrCurrencyType; - -/** - * Provides a UEnumeration object for listing ISO-4217 codes. - * @param currType You can use one of several UCurrCurrencyType values for this - * variable. You can also | (or) them together to get a specific list of - * currencies. Most people will want to use the (UCURR_COMMON|UCURR_NON_DEPRECATED) value to - * get a list of current currencies. - * @param pErrorCode Error code - * @stable ICU 3.2 - */ -U_CAPI UEnumeration * U_EXPORT2 -ucurr_openISOCurrencies(uint32_t currType, UErrorCode *pErrorCode); - -/** - * Queries if the given ISO 4217 3-letter code is available on the specified date range. - * - * Note: For checking availability of a currency on a specific date, specify the date on both 'from' and 'to' - * - * When 'from' is U_DATE_MIN and 'to' is U_DATE_MAX, this method checks if the specified currency is available any time. - * If 'from' and 'to' are same UDate value, this method checks if the specified currency is available on that date. - * - * @param isoCode - * The ISO 4217 3-letter code. - * - * @param from - * The lower bound of the date range, inclusive. When 'from' is U_DATE_MIN, check the availability - * of the currency any date before 'to' - * - * @param to - * The upper bound of the date range, inclusive. When 'to' is U_DATE_MAX, check the availability of - * the currency any date after 'from' - * - * @param errorCode - * ICU error code - * - * @return true if the given ISO 4217 3-letter code is supported on the specified date range. - * - * @stable ICU 4.8 - */ -U_CAPI UBool U_EXPORT2 -ucurr_isAvailable(const UChar* isoCode, - UDate from, - UDate to, - UErrorCode* errorCode); - -/** - * Finds the number of valid currency codes for the - * given locale and date. - * @param locale the locale for which to retrieve the - * currency count. - * @param date the date for which to retrieve the - * currency count for the given locale. - * @param ec error code - * @return the number of currency codes for the - * given locale and date. If 0, currency - * codes couldn't be found for the input - * values are invalid. - * @stable ICU 4.0 - */ -U_CAPI int32_t U_EXPORT2 -ucurr_countCurrencies(const char* locale, - UDate date, - UErrorCode* ec); - -/** - * Finds a currency code for the given locale and date - * @param locale the locale for which to retrieve a currency code. - * Currency can be specified by the "currency" keyword - * in which case it overrides the default currency code - * @param date the date for which to retrieve a currency code for - * the given locale. - * @param index the index within the available list of currency codes - * for the given locale on the given date. - * @param buff fill in buffer. Can be NULL for preflighting. - * @param buffCapacity capacity of the fill in buffer. Can be 0 for - * preflighting. If it is non-zero, the buff parameter - * must not be NULL. - * @param ec error code - * @return length of the currency string. It should always be 3. - * If 0, currency couldn't be found or the input values are - * invalid. - * @stable ICU 4.0 - */ -U_CAPI int32_t U_EXPORT2 -ucurr_forLocaleAndDate(const char* locale, - UDate date, - int32_t index, - UChar* buff, - int32_t buffCapacity, - UErrorCode* ec); - -/** - * Given a key and a locale, returns an array of string values in a preferred - * order that would make a difference. These are all and only those values where - * the open (creation) of the service with the locale formed from the input locale - * plus input keyword and that value has different behavior than creation with the - * input locale alone. - * @param key one of the keys supported by this service. For now, only - * "currency" is supported. - * @param locale the locale - * @param commonlyUsed if set to true it will return only commonly used values - * with the given locale in preferred order. Otherwise, - * it will return all the available values for the locale. - * @param status error status - * @return a string enumeration over keyword values for the given key and the locale. - * @stable ICU 4.2 - */ -U_CAPI UEnumeration* U_EXPORT2 -ucurr_getKeywordValuesForLocale(const char* key, - const char* locale, - UBool commonlyUsed, - UErrorCode* status); - -/** - * Returns the ISO 4217 numeric code for the currency. - *

Note: If the ISO 4217 numeric code is not assigned for the currency or - * the currency is unknown, this function returns 0. - * - * @param currency null-terminated 3-letter ISO 4217 code - * @return The ISO 4217 numeric code of the currency - * @stable ICU 49 - */ -U_CAPI int32_t U_EXPORT2 -ucurr_getNumericCode(const UChar* currency); - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ +#ifndef _UCURR_H_ +#define _UCURR_H_ + +#include "unicode/utypes.h" +#include "unicode/uenum.h" + +/** + * \file + * \brief C API: Encapsulates information about a currency. + * + * The ucurr API encapsulates information about a currency, as defined by + * ISO 4217. A currency is represented by a 3-character string + * containing its ISO 4217 code. This API can return various data + * necessary the proper display of a currency: + * + *

  • A display symbol, for a specific locale + *
  • The number of fraction digits to display + *
  • A rounding increment + *
+ * + * The DecimalFormat class uses these data to display + * currencies. + * @author Alan Liu + * @since ICU 2.2 + */ + +#if !UCONFIG_NO_FORMATTING + +/** + * Currency Usage used for Decimal Format + * @stable ICU 54 + */ +enum UCurrencyUsage { + /** + * a setting to specify currency usage which determines currency digit + * and rounding for standard usage, for example: "50.00 NT$" + * used as DEFAULT value + * @stable ICU 54 + */ + UCURR_USAGE_STANDARD=0, + /** + * a setting to specify currency usage which determines currency digit + * and rounding for cash usage, for example: "50 NT$" + * @stable ICU 54 + */ + UCURR_USAGE_CASH=1, +#ifndef U_HIDE_DEPRECATED_API + /** + * One higher than the last enum UCurrencyUsage constant. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UCURR_USAGE_COUNT=2 +#endif // U_HIDE_DEPRECATED_API +}; +/** Currency Usage used for Decimal Format */ +typedef enum UCurrencyUsage UCurrencyUsage; + +/** + * Finds a currency code for the given locale. + * @param locale the locale for which to retrieve a currency code. + * Currency can be specified by the "currency" keyword + * in which case it overrides the default currency code + * @param buff fill in buffer. Can be NULL for preflighting. + * @param buffCapacity capacity of the fill in buffer. Can be 0 for + * preflighting. If it is non-zero, the buff parameter + * must not be NULL. + * @param ec error code + * @return length of the currency string. It should always be 3. If 0, + * currency couldn't be found or the input values are + * invalid. + * @stable ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +ucurr_forLocale(const char* locale, + UChar* buff, + int32_t buffCapacity, + UErrorCode* ec); + +/** + * Selector constants for ucurr_getName(). + * + * @see ucurr_getName + * @stable ICU 2.6 + */ +typedef enum UCurrNameStyle { + /** + * Selector for ucurr_getName indicating a symbolic name for a + * currency, such as "$" for USD. + * @stable ICU 2.6 + */ + UCURR_SYMBOL_NAME, + + /** + * Selector for ucurr_getName indicating the long name for a + * currency, such as "US Dollar" for USD. + * @stable ICU 2.6 + */ + UCURR_LONG_NAME, + + /** + * Selector for getName() indicating the narrow currency symbol. + * The narrow currency symbol is similar to the regular currency + * symbol, but it always takes the shortest form: for example, + * "$" instead of "US$" for USD in en-CA. + * + * @stable ICU 61 + */ + UCURR_NARROW_SYMBOL_NAME, + + /** + * Selector for getName() indicating the formal currency symbol. + * The formal currency symbol is similar to the regular currency + * symbol, but it always takes the form used in formal settings + * such as banking; for example, "NT$" instead of "$" for TWD in zh-TW. + * + * @stable ICU 68 + */ + UCURR_FORMAL_SYMBOL_NAME, + + /** + * Selector for getName() indicating the variant currency symbol. + * The variant symbol for a currency is an alternative symbol + * that is not necessarily as widely used as the regular symbol. + * + * @stable ICU 68 + */ + UCURR_VARIANT_SYMBOL_NAME + +} UCurrNameStyle; + +#if !UCONFIG_NO_SERVICE +/** + * @stable ICU 2.6 + */ +typedef const void* UCurrRegistryKey; + +/** + * Register an (existing) ISO 4217 currency code for the given locale. + * Only the country code and the two variants EURO and PRE_EURO are + * recognized. + * @param isoCode the three-letter ISO 4217 currency code + * @param locale the locale for which to register this currency code + * @param status the in/out status code + * @return a registry key that can be used to unregister this currency code, or NULL + * if there was an error. + * @stable ICU 2.6 + */ +U_CAPI UCurrRegistryKey U_EXPORT2 +ucurr_register(const UChar* isoCode, + const char* locale, + UErrorCode* status); +/** + * Unregister the previously-registered currency definitions using the + * URegistryKey returned from ucurr_register. Key becomes invalid after + * a successful call and should not be used again. Any currency + * that might have been hidden by the original ucurr_register call is + * restored. + * @param key the registry key returned by a previous call to ucurr_register + * @param status the in/out status code, no special meanings are assigned + * @return true if the currency for this key was successfully unregistered + * @stable ICU 2.6 + */ +U_CAPI UBool U_EXPORT2 +ucurr_unregister(UCurrRegistryKey key, UErrorCode* status); +#endif /* UCONFIG_NO_SERVICE */ + +/** + * Returns the display name for the given currency in the + * given locale. For example, the display name for the USD + * currency object in the en_US locale is "$". + * @param currency null-terminated 3-letter ISO 4217 code + * @param locale locale in which to display currency + * @param nameStyle selector for which kind of name to return + * @param isChoiceFormat always set to false, or can be NULL; + * display names are static strings; + * since ICU 4.4, ChoiceFormat patterns are no longer supported + * @param len fill-in parameter to receive length of result + * @param ec error code + * @return pointer to display string of 'len' UChars. If the resource + * data contains no entry for 'currency', then 'currency' itself is + * returned. + * @stable ICU 2.6 + */ +U_CAPI const UChar* U_EXPORT2 +ucurr_getName(const UChar* currency, + const char* locale, + UCurrNameStyle nameStyle, + UBool* isChoiceFormat, + int32_t* len, + UErrorCode* ec); + +/** + * Returns the plural name for the given currency in the + * given locale. For example, the plural name for the USD + * currency object in the en_US locale is "US dollar" or "US dollars". + * @param currency null-terminated 3-letter ISO 4217 code + * @param locale locale in which to display currency + * @param isChoiceFormat always set to false, or can be NULL; + * display names are static strings; + * since ICU 4.4, ChoiceFormat patterns are no longer supported + * @param pluralCount plural count + * @param len fill-in parameter to receive length of result + * @param ec error code + * @return pointer to display string of 'len' UChars. If the resource + * data contains no entry for 'currency', then 'currency' itself is + * returned. + * @stable ICU 4.2 + */ +U_CAPI const UChar* U_EXPORT2 +ucurr_getPluralName(const UChar* currency, + const char* locale, + UBool* isChoiceFormat, + const char* pluralCount, + int32_t* len, + UErrorCode* ec); + +/** + * Returns the number of the number of fraction digits that should + * be displayed for the given currency. + * This is equivalent to ucurr_getDefaultFractionDigitsForUsage(currency,UCURR_USAGE_STANDARD,ec); + * + * Important: The number of fraction digits for a given currency is NOT + * guaranteed to be constant across versions of ICU or CLDR. For example, + * do NOT use this value as a mechanism for deciding the magnitude used + * to store currency values in a database. You should use this value for + * display purposes only. + * + * @param currency null-terminated 3-letter ISO 4217 code + * @param ec input-output error code + * @return a non-negative number of fraction digits to be + * displayed, or 0 if there is an error + * @stable ICU 3.0 + */ +U_CAPI int32_t U_EXPORT2 +ucurr_getDefaultFractionDigits(const UChar* currency, + UErrorCode* ec); + +/** + * Returns the number of the number of fraction digits that should + * be displayed for the given currency with usage. + * + * Important: The number of fraction digits for a given currency is NOT + * guaranteed to be constant across versions of ICU or CLDR. For example, + * do NOT use this value as a mechanism for deciding the magnitude used + * to store currency values in a database. You should use this value for + * display purposes only. + * + * @param currency null-terminated 3-letter ISO 4217 code + * @param usage enum usage for the currency + * @param ec input-output error code + * @return a non-negative number of fraction digits to be + * displayed, or 0 if there is an error + * @stable ICU 54 + */ +U_CAPI int32_t U_EXPORT2 +ucurr_getDefaultFractionDigitsForUsage(const UChar* currency, + const UCurrencyUsage usage, + UErrorCode* ec); + +/** + * Returns the rounding increment for the given currency, or 0.0 if no + * rounding is done by the currency. + * This is equivalent to ucurr_getRoundingIncrementForUsage(currency,UCURR_USAGE_STANDARD,ec); + * @param currency null-terminated 3-letter ISO 4217 code + * @param ec input-output error code + * @return the non-negative rounding increment, or 0.0 if none, + * or 0.0 if there is an error + * @stable ICU 3.0 + */ +U_CAPI double U_EXPORT2 +ucurr_getRoundingIncrement(const UChar* currency, + UErrorCode* ec); + +/** + * Returns the rounding increment for the given currency, or 0.0 if no + * rounding is done by the currency given usage. + * @param currency null-terminated 3-letter ISO 4217 code + * @param usage enum usage for the currency + * @param ec input-output error code + * @return the non-negative rounding increment, or 0.0 if none, + * or 0.0 if there is an error + * @stable ICU 54 + */ +U_CAPI double U_EXPORT2 +ucurr_getRoundingIncrementForUsage(const UChar* currency, + const UCurrencyUsage usage, + UErrorCode* ec); + +/** + * Selector constants for ucurr_openCurrencies(). + * + * @see ucurr_openCurrencies + * @stable ICU 3.2 + */ +typedef enum UCurrCurrencyType { + /** + * Select all ISO-4217 currency codes. + * @stable ICU 3.2 + */ + UCURR_ALL = INT32_MAX, + /** + * Select only ISO-4217 commonly used currency codes. + * These currencies can be found in common use, and they usually have + * bank notes or coins associated with the currency code. + * This does not include fund codes, precious metals and other + * various ISO-4217 codes limited to special financial products. + * @stable ICU 3.2 + */ + UCURR_COMMON = 1, + /** + * Select ISO-4217 uncommon currency codes. + * These codes respresent fund codes, precious metals and other + * various ISO-4217 codes limited to special financial products. + * A fund code is a monetary resource associated with a currency. + * @stable ICU 3.2 + */ + UCURR_UNCOMMON = 2, + /** + * Select only deprecated ISO-4217 codes. + * These codes are no longer in general public use. + * @stable ICU 3.2 + */ + UCURR_DEPRECATED = 4, + /** + * Select only non-deprecated ISO-4217 codes. + * These codes are in general public use. + * @stable ICU 3.2 + */ + UCURR_NON_DEPRECATED = 8 +} UCurrCurrencyType; + +/** + * Provides a UEnumeration object for listing ISO-4217 codes. + * @param currType You can use one of several UCurrCurrencyType values for this + * variable. You can also | (or) them together to get a specific list of + * currencies. Most people will want to use the (UCURR_COMMON|UCURR_NON_DEPRECATED) value to + * get a list of current currencies. + * @param pErrorCode Error code + * @stable ICU 3.2 + */ +U_CAPI UEnumeration * U_EXPORT2 +ucurr_openISOCurrencies(uint32_t currType, UErrorCode *pErrorCode); + +/** + * Queries if the given ISO 4217 3-letter code is available on the specified date range. + * + * Note: For checking availability of a currency on a specific date, specify the date on both 'from' and 'to' + * + * When 'from' is U_DATE_MIN and 'to' is U_DATE_MAX, this method checks if the specified currency is available any time. + * If 'from' and 'to' are same UDate value, this method checks if the specified currency is available on that date. + * + * @param isoCode + * The ISO 4217 3-letter code. + * + * @param from + * The lower bound of the date range, inclusive. When 'from' is U_DATE_MIN, check the availability + * of the currency any date before 'to' + * + * @param to + * The upper bound of the date range, inclusive. When 'to' is U_DATE_MAX, check the availability of + * the currency any date after 'from' + * + * @param errorCode + * ICU error code + * + * @return true if the given ISO 4217 3-letter code is supported on the specified date range. + * + * @stable ICU 4.8 + */ +U_CAPI UBool U_EXPORT2 +ucurr_isAvailable(const UChar* isoCode, + UDate from, + UDate to, + UErrorCode* errorCode); + +/** + * Finds the number of valid currency codes for the + * given locale and date. + * @param locale the locale for which to retrieve the + * currency count. + * @param date the date for which to retrieve the + * currency count for the given locale. + * @param ec error code + * @return the number of currency codes for the + * given locale and date. If 0, currency + * codes couldn't be found for the input + * values are invalid. + * @stable ICU 4.0 + */ +U_CAPI int32_t U_EXPORT2 +ucurr_countCurrencies(const char* locale, + UDate date, + UErrorCode* ec); + +/** + * Finds a currency code for the given locale and date + * @param locale the locale for which to retrieve a currency code. + * Currency can be specified by the "currency" keyword + * in which case it overrides the default currency code + * @param date the date for which to retrieve a currency code for + * the given locale. + * @param index the index within the available list of currency codes + * for the given locale on the given date. + * @param buff fill in buffer. Can be NULL for preflighting. + * @param buffCapacity capacity of the fill in buffer. Can be 0 for + * preflighting. If it is non-zero, the buff parameter + * must not be NULL. + * @param ec error code + * @return length of the currency string. It should always be 3. + * If 0, currency couldn't be found or the input values are + * invalid. + * @stable ICU 4.0 + */ +U_CAPI int32_t U_EXPORT2 +ucurr_forLocaleAndDate(const char* locale, + UDate date, + int32_t index, + UChar* buff, + int32_t buffCapacity, + UErrorCode* ec); + +/** + * Given a key and a locale, returns an array of string values in a preferred + * order that would make a difference. These are all and only those values where + * the open (creation) of the service with the locale formed from the input locale + * plus input keyword and that value has different behavior than creation with the + * input locale alone. + * @param key one of the keys supported by this service. For now, only + * "currency" is supported. + * @param locale the locale + * @param commonlyUsed if set to true it will return only commonly used values + * with the given locale in preferred order. Otherwise, + * it will return all the available values for the locale. + * @param status error status + * @return a string enumeration over keyword values for the given key and the locale. + * @stable ICU 4.2 + */ +U_CAPI UEnumeration* U_EXPORT2 +ucurr_getKeywordValuesForLocale(const char* key, + const char* locale, + UBool commonlyUsed, + UErrorCode* status); + +/** + * Returns the ISO 4217 numeric code for the currency. + *

Note: If the ISO 4217 numeric code is not assigned for the currency or + * the currency is unknown, this function returns 0. + * + * @param currency null-terminated 3-letter ISO 4217 code + * @return The ISO 4217 numeric code of the currency + * @stable ICU 49 + */ +U_CAPI int32_t U_EXPORT2 +ucurr_getNumericCode(const UChar* currency); + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif diff --git a/deps/icu-small/source/common/unicode/udata.h b/deps/icu-small/source/common/unicode/udata.h index c5b1adc359c0ab..4bc19790985811 100644 --- a/deps/icu-small/source/common/unicode/udata.h +++ b/deps/icu-small/source/common/unicode/udata.h @@ -1,440 +1,440 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: udata.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999oct25 -* created by: Markus W. Scherer -*/ - -#ifndef __UDATA_H__ -#define __UDATA_H__ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -U_CDECL_BEGIN - -/** - * \file - * \brief C API: Data loading interface - * - *

Information about data loading interface

- * - * This API is used to find and efficiently load data for ICU and applications - * using ICU. It provides an abstract interface that specifies a data type and - * name to find and load the data. Normally this API is used by other ICU APIs - * to load required data out of the ICU data library, but it can be used to - * load data out of other places. - * - * See the User Guide Data Management chapter. - */ - -#ifndef U_HIDE_INTERNAL_API -/** - * Character used to separate package names from tree names - * @internal ICU 3.0 - */ -#define U_TREE_SEPARATOR '-' - -/** - * String used to separate package names from tree names - * @internal ICU 3.0 - */ -#define U_TREE_SEPARATOR_STRING "-" - -/** - * Character used to separate parts of entry names - * @internal ICU 3.0 - */ -#define U_TREE_ENTRY_SEP_CHAR '/' - -/** - * String used to separate parts of entry names - * @internal ICU 3.0 - */ -#define U_TREE_ENTRY_SEP_STRING "/" - -/** - * Alias for standard ICU data - * @internal ICU 3.0 - */ -#define U_ICUDATA_ALIAS "ICUDATA" - -#endif /* U_HIDE_INTERNAL_API */ - -/** - * UDataInfo contains the properties about the requested data. - * This is meta data. - * - *

This structure may grow in the future, indicated by the - * size field.

- * - *

ICU data must be at least 8-aligned, and should be 16-aligned. - * The UDataInfo struct begins 4 bytes after the start of the data item, - * so it is 4-aligned. - * - *

The platform data property fields help determine if a data - * file can be efficiently used on a given machine. - * The particular fields are of importance only if the data - * is affected by the properties - if there is integer data - * with word sizes > 1 byte, char* text, or UChar* text.

- * - *

The implementation for the udata_open[Choice]() - * functions may reject data based on the value in isBigEndian. - * No other field is used by the udata API implementation.

- * - *

The dataFormat may be used to identify - * the kind of data, e.g. a converter table.

- * - *

The formatVersion field should be used to - * make sure that the format can be interpreted. - * It may be a good idea to check only for the one or two highest - * of the version elements to allow the data memory to - * get more or somewhat rearranged contents, for as long - * as the using code can still interpret the older contents.

- * - *

The dataVersion field is intended to be a - * common place to store the source version of the data; - * for data from the Unicode character database, this could - * reflect the Unicode version.

- * - * @stable ICU 2.0 - */ -typedef struct { - /** sizeof(UDataInfo) - * @stable ICU 2.0 */ - uint16_t size; - - /** unused, set to 0 - * @stable ICU 2.0*/ - uint16_t reservedWord; - - /* platform data properties */ - /** 0 for little-endian machine, 1 for big-endian - * @stable ICU 2.0 */ - uint8_t isBigEndian; - - /** see U_CHARSET_FAMILY values in utypes.h - * @stable ICU 2.0*/ - uint8_t charsetFamily; - - /** sizeof(UChar), one of { 1, 2, 4 } - * @stable ICU 2.0*/ - uint8_t sizeofUChar; - - /** unused, set to 0 - * @stable ICU 2.0*/ - uint8_t reservedByte; - - /** data format identifier - * @stable ICU 2.0*/ - uint8_t dataFormat[4]; - - /** versions: [0] major [1] minor [2] milli [3] micro - * @stable ICU 2.0*/ - uint8_t formatVersion[4]; - - /** versions: [0] major [1] minor [2] milli [3] micro - * @stable ICU 2.0*/ - uint8_t dataVersion[4]; -} UDataInfo; - -/* API for reading data -----------------------------------------------------*/ - -/** - * Forward declaration of the data memory type. - * @stable ICU 2.0 - */ -typedef struct UDataMemory UDataMemory; - -/** - * Callback function for udata_openChoice(). - * @param context parameter passed into udata_openChoice(). - * @param type The type of the data as passed into udata_openChoice(). - * It may be NULL. - * @param name The name of the data as passed into udata_openChoice(). - * @param pInfo A pointer to the UDataInfo structure - * of data that has been loaded and will be returned - * by udata_openChoice() if this function - * returns true. - * @return true if the current data memory is acceptable - * @stable ICU 2.0 - */ -typedef UBool U_CALLCONV -UDataMemoryIsAcceptable(void *context, - const char *type, const char *name, - const UDataInfo *pInfo); - - -/** - * Convenience function. - * This function works the same as udata_openChoice - * except that any data that matches the type and name - * is assumed to be acceptable. - * @param path Specifies an absolute path and/or a basename for the - * finding of the data in the file system. - * NULL for ICU data. - * @param type A string that specifies the type of data to be loaded. - * For example, resource bundles are loaded with type "res", - * conversion tables with type "cnv". - * This may be NULL or empty. - * @param name A string that specifies the name of the data. - * @param pErrorCode An ICU UErrorCode parameter. It must not be NULL. - * @return A pointer (handle) to a data memory object, or NULL - * if an error occurs. Call udata_getMemory() - * to get a pointer to the actual data. - * - * @see udata_openChoice - * @stable ICU 2.0 - */ -U_CAPI UDataMemory * U_EXPORT2 -udata_open(const char *path, const char *type, const char *name, - UErrorCode *pErrorCode); - -/** - * Data loading function. - * This function is used to find and load efficiently data for - * ICU and applications using ICU. - * It provides an abstract interface that allows to specify a data - * type and name to find and load the data. - * - *

The implementation depends on platform properties and user preferences - * and may involve loading shared libraries (DLLs), mapping - * files into memory, or fopen()/fread() files. - * It may also involve using static memory or database queries etc. - * Several or all data items may be combined into one entity - * (DLL, memory-mappable file).

- * - *

The data is always preceded by a header that includes - * a UDataInfo structure. - * The caller's isAcceptable() function is called to make - * sure that the data is useful. It may be called several times if it - * rejects the data and there is more than one location with data - * matching the type and name.

- * - *

If path==NULL, then ICU data is loaded. - * Otherwise, it is separated into a basename and a basename-less directory string. - * The basename is used as the data package name, and the directory is - * logically prepended to the ICU data directory string.

- * - *

For details about ICU data loading see the User Guide - * Data Management chapter. (https://unicode-org.github.io/icu/userguide/icu_data/)

- * - * @param path Specifies an absolute path and/or a basename for the - * finding of the data in the file system. - * NULL for ICU data. - * @param type A string that specifies the type of data to be loaded. - * For example, resource bundles are loaded with type "res", - * conversion tables with type "cnv". - * This may be NULL or empty. - * @param name A string that specifies the name of the data. - * @param isAcceptable This function is called to verify that loaded data - * is useful for the client code. If it returns false - * for all data items, then udata_openChoice() - * will return with an error. - * @param context Arbitrary parameter to be passed into isAcceptable. - * @param pErrorCode An ICU UErrorCode parameter. It must not be NULL. - * @return A pointer (handle) to a data memory object, or NULL - * if an error occurs. Call udata_getMemory() - * to get a pointer to the actual data. - * @stable ICU 2.0 - */ -U_CAPI UDataMemory * U_EXPORT2 -udata_openChoice(const char *path, const char *type, const char *name, - UDataMemoryIsAcceptable *isAcceptable, void *context, - UErrorCode *pErrorCode); - -/** - * Close the data memory. - * This function must be called to allow the system to - * release resources associated with this data memory. - * @param pData The pointer to data memory object - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -udata_close(UDataMemory *pData); - -/** - * Get the pointer to the actual data inside the data memory. - * The data is read-only. - * - * ICU data must be at least 8-aligned, and should be 16-aligned. - * - * @param pData The pointer to data memory object - * @stable ICU 2.0 - */ -U_CAPI const void * U_EXPORT2 -udata_getMemory(UDataMemory *pData); - -/** - * Get the information from the data memory header. - * This allows to get access to the header containing - * platform data properties etc. which is not part of - * the data itself and can therefore not be accessed - * via the pointer that udata_getMemory() returns. - * - * @param pData pointer to the data memory object - * @param pInfo pointer to a UDataInfo object; - * its size field must be set correctly, - * typically to sizeof(UDataInfo). - * - * *pInfo will be filled with the UDataInfo structure - * in the data memory object. If this structure is smaller than - * pInfo->size, then the size will be - * adjusted and only part of the structure will be filled. - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -udata_getInfo(UDataMemory *pData, UDataInfo *pInfo); - -/** - * This function bypasses the normal ICU data loading process and - * allows you to force ICU's system data to come out of a user-specified - * area in memory. - * - * ICU data must be at least 8-aligned, and should be 16-aligned. - * See https://unicode-org.github.io/icu/userguide/icudata - * - * The format of this data is that of the icu common data file, as is - * generated by the pkgdata tool with mode=common or mode=dll. - * You can read in a whole common mode file and pass the address to the start of the - * data, or (with the appropriate link options) pass in the pointer to - * the data that has been loaded from a dll by the operating system, - * as shown in this code: - * - * extern const char U_IMPORT U_ICUDATA_ENTRY_POINT []; - * // U_ICUDATA_ENTRY_POINT is same as entry point specified to pkgdata tool - * UErrorCode status = U_ZERO_ERROR; - * - * udata_setCommonData(&U_ICUDATA_ENTRY_POINT, &status); - * - * It is important that the declaration be as above. The entry point - * must not be declared as an extern void*. - * - * Starting with ICU 4.4, it is possible to set several data packages, - * one per call to this function. - * udata_open() will look for data in the multiple data packages in the order - * in which they were set. - * The position of the linked-in or default-name ICU .data package in the - * search list depends on when the first data item is loaded that is not contained - * in the already explicitly set packages. - * If data was loaded implicitly before the first call to this function - * (for example, via opening a converter, constructing a UnicodeString - * from default-codepage data, using formatting or collation APIs, etc.), - * then the default data will be first in the list. - * - * This function has no effect on application (non ICU) data. See udata_setAppData() - * for similar functionality for application data. - * - * @param data pointer to ICU common data - * @param err outgoing error status U_USING_DEFAULT_WARNING, U_UNSUPPORTED_ERROR - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -udata_setCommonData(const void *data, UErrorCode *err); - - -/** - * This function bypasses the normal ICU data loading process for application-specific - * data and allows you to force the it to come out of a user-specified - * pointer. - * - * ICU data must be at least 8-aligned, and should be 16-aligned. - * See https://unicode-org.github.io/icu/userguide/icudata - * - * The format of this data is that of the icu common data file, like 'icudt26l.dat' - * or the corresponding shared library (DLL) file. - * The application must read in or otherwise construct an image of the data and then - * pass the address of it to this function. - * - * - * Warning: setAppData will set a U_USING_DEFAULT_WARNING code if - * data with the specified path that has already been opened, or - * if setAppData with the same path has already been called. - * Any such calls to setAppData will have no effect. - * - * - * @param packageName the package name by which the application will refer - * to (open) this data - * @param data pointer to the data - * @param err outgoing error status U_USING_DEFAULT_WARNING, U_UNSUPPORTED_ERROR - * @see udata_setCommonData - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -udata_setAppData(const char *packageName, const void *data, UErrorCode *err); - -/** - * Possible settings for udata_setFileAccess() - * @see udata_setFileAccess - * @stable ICU 3.4 - */ -typedef enum UDataFileAccess { - /** ICU looks for data in single files first, then in packages. (default) @stable ICU 3.4 */ - UDATA_FILES_FIRST, - /** An alias for the default access mode. @stable ICU 3.4 */ - UDATA_DEFAULT_ACCESS = UDATA_FILES_FIRST, - /** ICU only loads data from packages, not from single files. @stable ICU 3.4 */ - UDATA_ONLY_PACKAGES, - /** ICU loads data from packages first, and only from single files - if the data cannot be found in a package. @stable ICU 3.4 */ - UDATA_PACKAGES_FIRST, - /** ICU does not access the file system for data loading. @stable ICU 3.4 */ - UDATA_NO_FILES, -#ifndef U_HIDE_DEPRECATED_API - /** - * Number of real UDataFileAccess values. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UDATA_FILE_ACCESS_COUNT -#endif // U_HIDE_DEPRECATED_API -} UDataFileAccess; - -/** - * This function may be called to control how ICU loads data. It must be called - * before any ICU data is loaded, including application data loaded with - * ures/ResourceBundle or udata APIs. This function is not multithread safe. - * The results of calling it while other threads are loading data are undefined. - * @param access The type of file access to be used - * @param status Error code. - * @see UDataFileAccess - * @stable ICU 3.4 - */ -U_CAPI void U_EXPORT2 -udata_setFileAccess(UDataFileAccess access, UErrorCode *status); - -U_CDECL_END - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUDataMemoryPointer - * "Smart pointer" class, closes a UDataMemory via udata_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUDataMemoryPointer, UDataMemory, udata_close); - -U_NAMESPACE_END - -#endif // U_SHOW_CPLUSPLUS_API - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: udata.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999oct25 +* created by: Markus W. Scherer +*/ + +#ifndef __UDATA_H__ +#define __UDATA_H__ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +U_CDECL_BEGIN + +/** + * \file + * \brief C API: Data loading interface + * + *

Information about data loading interface

+ * + * This API is used to find and efficiently load data for ICU and applications + * using ICU. It provides an abstract interface that specifies a data type and + * name to find and load the data. Normally this API is used by other ICU APIs + * to load required data out of the ICU data library, but it can be used to + * load data out of other places. + * + * See the User Guide Data Management chapter. + */ + +#ifndef U_HIDE_INTERNAL_API +/** + * Character used to separate package names from tree names + * @internal ICU 3.0 + */ +#define U_TREE_SEPARATOR '-' + +/** + * String used to separate package names from tree names + * @internal ICU 3.0 + */ +#define U_TREE_SEPARATOR_STRING "-" + +/** + * Character used to separate parts of entry names + * @internal ICU 3.0 + */ +#define U_TREE_ENTRY_SEP_CHAR '/' + +/** + * String used to separate parts of entry names + * @internal ICU 3.0 + */ +#define U_TREE_ENTRY_SEP_STRING "/" + +/** + * Alias for standard ICU data + * @internal ICU 3.0 + */ +#define U_ICUDATA_ALIAS "ICUDATA" + +#endif /* U_HIDE_INTERNAL_API */ + +/** + * UDataInfo contains the properties about the requested data. + * This is meta data. + * + *

This structure may grow in the future, indicated by the + * size field.

+ * + *

ICU data must be at least 8-aligned, and should be 16-aligned. + * The UDataInfo struct begins 4 bytes after the start of the data item, + * so it is 4-aligned. + * + *

The platform data property fields help determine if a data + * file can be efficiently used on a given machine. + * The particular fields are of importance only if the data + * is affected by the properties - if there is integer data + * with word sizes > 1 byte, char* text, or UChar* text.

+ * + *

The implementation for the udata_open[Choice]() + * functions may reject data based on the value in isBigEndian. + * No other field is used by the udata API implementation.

+ * + *

The dataFormat may be used to identify + * the kind of data, e.g. a converter table.

+ * + *

The formatVersion field should be used to + * make sure that the format can be interpreted. + * It may be a good idea to check only for the one or two highest + * of the version elements to allow the data memory to + * get more or somewhat rearranged contents, for as long + * as the using code can still interpret the older contents.

+ * + *

The dataVersion field is intended to be a + * common place to store the source version of the data; + * for data from the Unicode character database, this could + * reflect the Unicode version.

+ * + * @stable ICU 2.0 + */ +typedef struct { + /** sizeof(UDataInfo) + * @stable ICU 2.0 */ + uint16_t size; + + /** unused, set to 0 + * @stable ICU 2.0*/ + uint16_t reservedWord; + + /* platform data properties */ + /** 0 for little-endian machine, 1 for big-endian + * @stable ICU 2.0 */ + uint8_t isBigEndian; + + /** see U_CHARSET_FAMILY values in utypes.h + * @stable ICU 2.0*/ + uint8_t charsetFamily; + + /** sizeof(UChar), one of { 1, 2, 4 } + * @stable ICU 2.0*/ + uint8_t sizeofUChar; + + /** unused, set to 0 + * @stable ICU 2.0*/ + uint8_t reservedByte; + + /** data format identifier + * @stable ICU 2.0*/ + uint8_t dataFormat[4]; + + /** versions: [0] major [1] minor [2] milli [3] micro + * @stable ICU 2.0*/ + uint8_t formatVersion[4]; + + /** versions: [0] major [1] minor [2] milli [3] micro + * @stable ICU 2.0*/ + uint8_t dataVersion[4]; +} UDataInfo; + +/* API for reading data -----------------------------------------------------*/ + +/** + * Forward declaration of the data memory type. + * @stable ICU 2.0 + */ +typedef struct UDataMemory UDataMemory; + +/** + * Callback function for udata_openChoice(). + * @param context parameter passed into udata_openChoice(). + * @param type The type of the data as passed into udata_openChoice(). + * It may be NULL. + * @param name The name of the data as passed into udata_openChoice(). + * @param pInfo A pointer to the UDataInfo structure + * of data that has been loaded and will be returned + * by udata_openChoice() if this function + * returns true. + * @return true if the current data memory is acceptable + * @stable ICU 2.0 + */ +typedef UBool U_CALLCONV +UDataMemoryIsAcceptable(void *context, + const char *type, const char *name, + const UDataInfo *pInfo); + + +/** + * Convenience function. + * This function works the same as udata_openChoice + * except that any data that matches the type and name + * is assumed to be acceptable. + * @param path Specifies an absolute path and/or a basename for the + * finding of the data in the file system. + * NULL for ICU data. + * @param type A string that specifies the type of data to be loaded. + * For example, resource bundles are loaded with type "res", + * conversion tables with type "cnv". + * This may be NULL or empty. + * @param name A string that specifies the name of the data. + * @param pErrorCode An ICU UErrorCode parameter. It must not be NULL. + * @return A pointer (handle) to a data memory object, or NULL + * if an error occurs. Call udata_getMemory() + * to get a pointer to the actual data. + * + * @see udata_openChoice + * @stable ICU 2.0 + */ +U_CAPI UDataMemory * U_EXPORT2 +udata_open(const char *path, const char *type, const char *name, + UErrorCode *pErrorCode); + +/** + * Data loading function. + * This function is used to find and load efficiently data for + * ICU and applications using ICU. + * It provides an abstract interface that allows to specify a data + * type and name to find and load the data. + * + *

The implementation depends on platform properties and user preferences + * and may involve loading shared libraries (DLLs), mapping + * files into memory, or fopen()/fread() files. + * It may also involve using static memory or database queries etc. + * Several or all data items may be combined into one entity + * (DLL, memory-mappable file).

+ * + *

The data is always preceded by a header that includes + * a UDataInfo structure. + * The caller's isAcceptable() function is called to make + * sure that the data is useful. It may be called several times if it + * rejects the data and there is more than one location with data + * matching the type and name.

+ * + *

If path==NULL, then ICU data is loaded. + * Otherwise, it is separated into a basename and a basename-less directory string. + * The basename is used as the data package name, and the directory is + * logically prepended to the ICU data directory string.

+ * + *

For details about ICU data loading see the User Guide + * Data Management chapter. (https://unicode-org.github.io/icu/userguide/icu_data/)

+ * + * @param path Specifies an absolute path and/or a basename for the + * finding of the data in the file system. + * NULL for ICU data. + * @param type A string that specifies the type of data to be loaded. + * For example, resource bundles are loaded with type "res", + * conversion tables with type "cnv". + * This may be NULL or empty. + * @param name A string that specifies the name of the data. + * @param isAcceptable This function is called to verify that loaded data + * is useful for the client code. If it returns false + * for all data items, then udata_openChoice() + * will return with an error. + * @param context Arbitrary parameter to be passed into isAcceptable. + * @param pErrorCode An ICU UErrorCode parameter. It must not be NULL. + * @return A pointer (handle) to a data memory object, or NULL + * if an error occurs. Call udata_getMemory() + * to get a pointer to the actual data. + * @stable ICU 2.0 + */ +U_CAPI UDataMemory * U_EXPORT2 +udata_openChoice(const char *path, const char *type, const char *name, + UDataMemoryIsAcceptable *isAcceptable, void *context, + UErrorCode *pErrorCode); + +/** + * Close the data memory. + * This function must be called to allow the system to + * release resources associated with this data memory. + * @param pData The pointer to data memory object + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +udata_close(UDataMemory *pData); + +/** + * Get the pointer to the actual data inside the data memory. + * The data is read-only. + * + * ICU data must be at least 8-aligned, and should be 16-aligned. + * + * @param pData The pointer to data memory object + * @stable ICU 2.0 + */ +U_CAPI const void * U_EXPORT2 +udata_getMemory(UDataMemory *pData); + +/** + * Get the information from the data memory header. + * This allows to get access to the header containing + * platform data properties etc. which is not part of + * the data itself and can therefore not be accessed + * via the pointer that udata_getMemory() returns. + * + * @param pData pointer to the data memory object + * @param pInfo pointer to a UDataInfo object; + * its size field must be set correctly, + * typically to sizeof(UDataInfo). + * + * *pInfo will be filled with the UDataInfo structure + * in the data memory object. If this structure is smaller than + * pInfo->size, then the size will be + * adjusted and only part of the structure will be filled. + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +udata_getInfo(UDataMemory *pData, UDataInfo *pInfo); + +/** + * This function bypasses the normal ICU data loading process and + * allows you to force ICU's system data to come out of a user-specified + * area in memory. + * + * ICU data must be at least 8-aligned, and should be 16-aligned. + * See https://unicode-org.github.io/icu/userguide/icu_data + * + * The format of this data is that of the icu common data file, as is + * generated by the pkgdata tool with mode=common or mode=dll. + * You can read in a whole common mode file and pass the address to the start of the + * data, or (with the appropriate link options) pass in the pointer to + * the data that has been loaded from a dll by the operating system, + * as shown in this code: + * + * extern const char U_IMPORT U_ICUDATA_ENTRY_POINT []; + * // U_ICUDATA_ENTRY_POINT is same as entry point specified to pkgdata tool + * UErrorCode status = U_ZERO_ERROR; + * + * udata_setCommonData(&U_ICUDATA_ENTRY_POINT, &status); + * + * It is important that the declaration be as above. The entry point + * must not be declared as an extern void*. + * + * Starting with ICU 4.4, it is possible to set several data packages, + * one per call to this function. + * udata_open() will look for data in the multiple data packages in the order + * in which they were set. + * The position of the linked-in or default-name ICU .data package in the + * search list depends on when the first data item is loaded that is not contained + * in the already explicitly set packages. + * If data was loaded implicitly before the first call to this function + * (for example, via opening a converter, constructing a UnicodeString + * from default-codepage data, using formatting or collation APIs, etc.), + * then the default data will be first in the list. + * + * This function has no effect on application (non ICU) data. See udata_setAppData() + * for similar functionality for application data. + * + * @param data pointer to ICU common data + * @param err outgoing error status U_USING_DEFAULT_WARNING, U_UNSUPPORTED_ERROR + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +udata_setCommonData(const void *data, UErrorCode *err); + + +/** + * This function bypasses the normal ICU data loading process for application-specific + * data and allows you to force the it to come out of a user-specified + * pointer. + * + * ICU data must be at least 8-aligned, and should be 16-aligned. + * See https://unicode-org.github.io/icu/userguide/icu_data + * + * The format of this data is that of the icu common data file, like 'icudt26l.dat' + * or the corresponding shared library (DLL) file. + * The application must read in or otherwise construct an image of the data and then + * pass the address of it to this function. + * + * + * Warning: setAppData will set a U_USING_DEFAULT_WARNING code if + * data with the specified path that has already been opened, or + * if setAppData with the same path has already been called. + * Any such calls to setAppData will have no effect. + * + * + * @param packageName the package name by which the application will refer + * to (open) this data + * @param data pointer to the data + * @param err outgoing error status U_USING_DEFAULT_WARNING, U_UNSUPPORTED_ERROR + * @see udata_setCommonData + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +udata_setAppData(const char *packageName, const void *data, UErrorCode *err); + +/** + * Possible settings for udata_setFileAccess() + * @see udata_setFileAccess + * @stable ICU 3.4 + */ +typedef enum UDataFileAccess { + /** ICU looks for data in single files first, then in packages. (default) @stable ICU 3.4 */ + UDATA_FILES_FIRST, + /** An alias for the default access mode. @stable ICU 3.4 */ + UDATA_DEFAULT_ACCESS = UDATA_FILES_FIRST, + /** ICU only loads data from packages, not from single files. @stable ICU 3.4 */ + UDATA_ONLY_PACKAGES, + /** ICU loads data from packages first, and only from single files + if the data cannot be found in a package. @stable ICU 3.4 */ + UDATA_PACKAGES_FIRST, + /** ICU does not access the file system for data loading. @stable ICU 3.4 */ + UDATA_NO_FILES, +#ifndef U_HIDE_DEPRECATED_API + /** + * Number of real UDataFileAccess values. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UDATA_FILE_ACCESS_COUNT +#endif // U_HIDE_DEPRECATED_API +} UDataFileAccess; + +/** + * This function may be called to control how ICU loads data. It must be called + * before any ICU data is loaded, including application data loaded with + * ures/ResourceBundle or udata APIs. This function is not multithread safe. + * The results of calling it while other threads are loading data are undefined. + * @param access The type of file access to be used + * @param status Error code. + * @see UDataFileAccess + * @stable ICU 3.4 + */ +U_CAPI void U_EXPORT2 +udata_setFileAccess(UDataFileAccess access, UErrorCode *status); + +U_CDECL_END + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUDataMemoryPointer + * "Smart pointer" class, closes a UDataMemory via udata_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUDataMemoryPointer, UDataMemory, udata_close); + +U_NAMESPACE_END + +#endif // U_SHOW_CPLUSPLUS_API + +#endif diff --git a/deps/icu-small/source/common/unicode/udisplaycontext.h b/deps/icu-small/source/common/unicode/udisplaycontext.h index 6e1421798054f6..dbce02697bf06a 100644 --- a/deps/icu-small/source/common/unicode/udisplaycontext.h +++ b/deps/icu-small/source/common/unicode/udisplaycontext.h @@ -1,173 +1,173 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -***************************************************************************************** -* Copyright (C) 2014-2016, International Business Machines -* Corporation and others. All Rights Reserved. -***************************************************************************************** -*/ - -#ifndef UDISPLAYCONTEXT_H -#define UDISPLAYCONTEXT_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -/** - * \file - * \brief C API: Display context types (enum values) - */ - -/** - * Display context types, for getting values of a particular setting. - * Note, the specific numeric values are internal and may change. - * @stable ICU 51 - */ -enum UDisplayContextType { - /** - * Type to retrieve the dialect handling setting, e.g. - * UDISPCTX_STANDARD_NAMES or UDISPCTX_DIALECT_NAMES. - * @stable ICU 51 - */ - UDISPCTX_TYPE_DIALECT_HANDLING = 0, - /** - * Type to retrieve the capitalization context setting, e.g. - * UDISPCTX_CAPITALIZATION_NONE, UDISPCTX_CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, - * UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, etc. - * @stable ICU 51 - */ - UDISPCTX_TYPE_CAPITALIZATION = 1, - /** - * Type to retrieve the display length setting, e.g. - * UDISPCTX_LENGTH_FULL, UDISPCTX_LENGTH_SHORT. - * @stable ICU 54 - */ - UDISPCTX_TYPE_DISPLAY_LENGTH = 2, - /** - * Type to retrieve the substitute handling setting, e.g. - * UDISPCTX_SUBSTITUTE, UDISPCTX_NO_SUBSTITUTE. - * @stable ICU 58 - */ - UDISPCTX_TYPE_SUBSTITUTE_HANDLING = 3 -}; -/** -* @stable ICU 51 -*/ -typedef enum UDisplayContextType UDisplayContextType; - -/** - * Display context settings. - * Note, the specific numeric values are internal and may change. - * @stable ICU 51 - */ -enum UDisplayContext { - /** - * ================================ - * DIALECT_HANDLING can be set to one of UDISPCTX_STANDARD_NAMES or - * UDISPCTX_DIALECT_NAMES. Use UDisplayContextType UDISPCTX_TYPE_DIALECT_HANDLING - * to get the value. - */ - /** - * A possible setting for DIALECT_HANDLING: - * use standard names when generating a locale name, - * e.g. en_GB displays as 'English (United Kingdom)'. - * @stable ICU 51 - */ - UDISPCTX_STANDARD_NAMES = (UDISPCTX_TYPE_DIALECT_HANDLING<<8) + 0, - /** - * A possible setting for DIALECT_HANDLING: - * use dialect names, when generating a locale name, - * e.g. en_GB displays as 'British English'. - * @stable ICU 51 - */ - UDISPCTX_DIALECT_NAMES = (UDISPCTX_TYPE_DIALECT_HANDLING<<8) + 1, - /** - * ================================ - * CAPITALIZATION can be set to one of UDISPCTX_CAPITALIZATION_NONE, - * UDISPCTX_CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, - * UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, - * UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU, or - * UDISPCTX_CAPITALIZATION_FOR_STANDALONE. - * Use UDisplayContextType UDISPCTX_TYPE_CAPITALIZATION to get the value. - */ - /** - * The capitalization context to be used is unknown (this is the default value). - * @stable ICU 51 - */ - UDISPCTX_CAPITALIZATION_NONE = (UDISPCTX_TYPE_CAPITALIZATION<<8) + 0, - /** - * The capitalization context if a date, date symbol or display name is to be - * formatted with capitalization appropriate for the middle of a sentence. - * @stable ICU 51 - */ - UDISPCTX_CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE = (UDISPCTX_TYPE_CAPITALIZATION<<8) + 1, - /** - * The capitalization context if a date, date symbol or display name is to be - * formatted with capitalization appropriate for the beginning of a sentence. - * @stable ICU 51 - */ - UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE = (UDISPCTX_TYPE_CAPITALIZATION<<8) + 2, - /** - * The capitalization context if a date, date symbol or display name is to be - * formatted with capitalization appropriate for a user-interface list or menu item. - * @stable ICU 51 - */ - UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU = (UDISPCTX_TYPE_CAPITALIZATION<<8) + 3, - /** - * The capitalization context if a date, date symbol or display name is to be - * formatted with capitalization appropriate for stand-alone usage such as an - * isolated name on a calendar page. - * @stable ICU 51 - */ - UDISPCTX_CAPITALIZATION_FOR_STANDALONE = (UDISPCTX_TYPE_CAPITALIZATION<<8) + 4, - /** - * ================================ - * DISPLAY_LENGTH can be set to one of UDISPCTX_LENGTH_FULL or - * UDISPCTX_LENGTH_SHORT. Use UDisplayContextType UDISPCTX_TYPE_DISPLAY_LENGTH - * to get the value. - */ - /** - * A possible setting for DISPLAY_LENGTH: - * use full names when generating a locale name, - * e.g. "United States" for US. - * @stable ICU 54 - */ - UDISPCTX_LENGTH_FULL = (UDISPCTX_TYPE_DISPLAY_LENGTH<<8) + 0, - /** - * A possible setting for DISPLAY_LENGTH: - * use short names when generating a locale name, - * e.g. "U.S." for US. - * @stable ICU 54 - */ - UDISPCTX_LENGTH_SHORT = (UDISPCTX_TYPE_DISPLAY_LENGTH<<8) + 1, - /** - * ================================ - * SUBSTITUTE_HANDLING can be set to one of UDISPCTX_SUBSTITUTE or - * UDISPCTX_NO_SUBSTITUTE. Use UDisplayContextType UDISPCTX_TYPE_SUBSTITUTE_HANDLING - * to get the value. - */ - /** - * A possible setting for SUBSTITUTE_HANDLING: - * Returns a fallback value (e.g., the input code) when no data is available. - * This is the default value. - * @stable ICU 58 - */ - UDISPCTX_SUBSTITUTE = (UDISPCTX_TYPE_SUBSTITUTE_HANDLING<<8) + 0, - /** - * A possible setting for SUBSTITUTE_HANDLING: - * Returns a null value with error code set to U_ILLEGAL_ARGUMENT_ERROR when no - * data is available. - * @stable ICU 58 - */ - UDISPCTX_NO_SUBSTITUTE = (UDISPCTX_TYPE_SUBSTITUTE_HANDLING<<8) + 1 - -}; -/** -* @stable ICU 51 -*/ -typedef enum UDisplayContext UDisplayContext; - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +***************************************************************************************** +* Copyright (C) 2014-2016, International Business Machines +* Corporation and others. All Rights Reserved. +***************************************************************************************** +*/ + +#ifndef UDISPLAYCONTEXT_H +#define UDISPLAYCONTEXT_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +/** + * \file + * \brief C API: Display context types (enum values) + */ + +/** + * Display context types, for getting values of a particular setting. + * Note, the specific numeric values are internal and may change. + * @stable ICU 51 + */ +enum UDisplayContextType { + /** + * Type to retrieve the dialect handling setting, e.g. + * UDISPCTX_STANDARD_NAMES or UDISPCTX_DIALECT_NAMES. + * @stable ICU 51 + */ + UDISPCTX_TYPE_DIALECT_HANDLING = 0, + /** + * Type to retrieve the capitalization context setting, e.g. + * UDISPCTX_CAPITALIZATION_NONE, UDISPCTX_CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, + * UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, etc. + * @stable ICU 51 + */ + UDISPCTX_TYPE_CAPITALIZATION = 1, + /** + * Type to retrieve the display length setting, e.g. + * UDISPCTX_LENGTH_FULL, UDISPCTX_LENGTH_SHORT. + * @stable ICU 54 + */ + UDISPCTX_TYPE_DISPLAY_LENGTH = 2, + /** + * Type to retrieve the substitute handling setting, e.g. + * UDISPCTX_SUBSTITUTE, UDISPCTX_NO_SUBSTITUTE. + * @stable ICU 58 + */ + UDISPCTX_TYPE_SUBSTITUTE_HANDLING = 3 +}; +/** +* @stable ICU 51 +*/ +typedef enum UDisplayContextType UDisplayContextType; + +/** + * Display context settings. + * Note, the specific numeric values are internal and may change. + * @stable ICU 51 + */ +enum UDisplayContext { + /** + * ================================ + * DIALECT_HANDLING can be set to one of UDISPCTX_STANDARD_NAMES or + * UDISPCTX_DIALECT_NAMES. Use UDisplayContextType UDISPCTX_TYPE_DIALECT_HANDLING + * to get the value. + */ + /** + * A possible setting for DIALECT_HANDLING: + * use standard names when generating a locale name, + * e.g. en_GB displays as 'English (United Kingdom)'. + * @stable ICU 51 + */ + UDISPCTX_STANDARD_NAMES = (UDISPCTX_TYPE_DIALECT_HANDLING<<8) + 0, + /** + * A possible setting for DIALECT_HANDLING: + * use dialect names, when generating a locale name, + * e.g. en_GB displays as 'British English'. + * @stable ICU 51 + */ + UDISPCTX_DIALECT_NAMES = (UDISPCTX_TYPE_DIALECT_HANDLING<<8) + 1, + /** + * ================================ + * CAPITALIZATION can be set to one of UDISPCTX_CAPITALIZATION_NONE, + * UDISPCTX_CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, + * UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, + * UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU, or + * UDISPCTX_CAPITALIZATION_FOR_STANDALONE. + * Use UDisplayContextType UDISPCTX_TYPE_CAPITALIZATION to get the value. + */ + /** + * The capitalization context to be used is unknown (this is the default value). + * @stable ICU 51 + */ + UDISPCTX_CAPITALIZATION_NONE = (UDISPCTX_TYPE_CAPITALIZATION<<8) + 0, + /** + * The capitalization context if a date, date symbol or display name is to be + * formatted with capitalization appropriate for the middle of a sentence. + * @stable ICU 51 + */ + UDISPCTX_CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE = (UDISPCTX_TYPE_CAPITALIZATION<<8) + 1, + /** + * The capitalization context if a date, date symbol or display name is to be + * formatted with capitalization appropriate for the beginning of a sentence. + * @stable ICU 51 + */ + UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE = (UDISPCTX_TYPE_CAPITALIZATION<<8) + 2, + /** + * The capitalization context if a date, date symbol or display name is to be + * formatted with capitalization appropriate for a user-interface list or menu item. + * @stable ICU 51 + */ + UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU = (UDISPCTX_TYPE_CAPITALIZATION<<8) + 3, + /** + * The capitalization context if a date, date symbol or display name is to be + * formatted with capitalization appropriate for stand-alone usage such as an + * isolated name on a calendar page. + * @stable ICU 51 + */ + UDISPCTX_CAPITALIZATION_FOR_STANDALONE = (UDISPCTX_TYPE_CAPITALIZATION<<8) + 4, + /** + * ================================ + * DISPLAY_LENGTH can be set to one of UDISPCTX_LENGTH_FULL or + * UDISPCTX_LENGTH_SHORT. Use UDisplayContextType UDISPCTX_TYPE_DISPLAY_LENGTH + * to get the value. + */ + /** + * A possible setting for DISPLAY_LENGTH: + * use full names when generating a locale name, + * e.g. "United States" for US. + * @stable ICU 54 + */ + UDISPCTX_LENGTH_FULL = (UDISPCTX_TYPE_DISPLAY_LENGTH<<8) + 0, + /** + * A possible setting for DISPLAY_LENGTH: + * use short names when generating a locale name, + * e.g. "U.S." for US. + * @stable ICU 54 + */ + UDISPCTX_LENGTH_SHORT = (UDISPCTX_TYPE_DISPLAY_LENGTH<<8) + 1, + /** + * ================================ + * SUBSTITUTE_HANDLING can be set to one of UDISPCTX_SUBSTITUTE or + * UDISPCTX_NO_SUBSTITUTE. Use UDisplayContextType UDISPCTX_TYPE_SUBSTITUTE_HANDLING + * to get the value. + */ + /** + * A possible setting for SUBSTITUTE_HANDLING: + * Returns a fallback value (e.g., the input code) when no data is available. + * This is the default value. + * @stable ICU 58 + */ + UDISPCTX_SUBSTITUTE = (UDISPCTX_TYPE_SUBSTITUTE_HANDLING<<8) + 0, + /** + * A possible setting for SUBSTITUTE_HANDLING: + * Returns a null value with error code set to U_ILLEGAL_ARGUMENT_ERROR when no + * data is available. + * @stable ICU 58 + */ + UDISPCTX_NO_SUBSTITUTE = (UDISPCTX_TYPE_SUBSTITUTE_HANDLING<<8) + 1 + +}; +/** +* @stable ICU 51 +*/ +typedef enum UDisplayContext UDisplayContext; + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif diff --git a/deps/icu-small/source/common/unicode/uenum.h b/deps/icu-small/source/common/unicode/uenum.h index d9c893e06d92b2..5d697896715fd4 100644 --- a/deps/icu-small/source/common/unicode/uenum.h +++ b/deps/icu-small/source/common/unicode/uenum.h @@ -1,209 +1,209 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2013, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uenum.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:2 -* -* created on: 2002jul08 -* created by: Vladimir Weinstein -*/ - -#ifndef __UENUM_H -#define __UENUM_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" - -U_NAMESPACE_BEGIN -class StringEnumeration; -U_NAMESPACE_END -#endif // U_SHOW_CPLUSPLUS_API - -/** - * \file - * \brief C API: String Enumeration - */ - -/** - * An enumeration object. - * For usage in C programs. - * @stable ICU 2.2 - */ -struct UEnumeration; -/** structure representing an enumeration object instance @stable ICU 2.2 */ -typedef struct UEnumeration UEnumeration; - -/** - * Disposes of resources in use by the iterator. If en is NULL, - * does nothing. After this call, any char* or UChar* pointer - * returned by uenum_unext() or uenum_next() is invalid. - * @param en UEnumeration structure pointer - * @stable ICU 2.2 - */ -U_CAPI void U_EXPORT2 -uenum_close(UEnumeration* en); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUEnumerationPointer - * "Smart pointer" class, closes a UEnumeration via uenum_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUEnumerationPointer, UEnumeration, uenum_close); - -U_NAMESPACE_END - -#endif - -/** - * Returns the number of elements that the iterator traverses. If - * the iterator is out-of-sync with its service, status is set to - * U_ENUM_OUT_OF_SYNC_ERROR. - * This is a convenience function. It can end up being very - * expensive as all the items might have to be pre-fetched (depending - * on the type of data being traversed). Use with caution and only - * when necessary. - * @param en UEnumeration structure pointer - * @param status error code, can be U_ENUM_OUT_OF_SYNC_ERROR if the - * iterator is out of sync. - * @return number of elements in the iterator - * @stable ICU 2.2 - */ -U_CAPI int32_t U_EXPORT2 -uenum_count(UEnumeration* en, UErrorCode* status); - -/** - * Returns the next element in the iterator's list. If there are - * no more elements, returns NULL. If the iterator is out-of-sync - * with its service, status is set to U_ENUM_OUT_OF_SYNC_ERROR and - * NULL is returned. If the native service string is a char* string, - * it is converted to UChar* with the invariant converter. - * The result is terminated by (UChar)0. - * @param en the iterator object - * @param resultLength pointer to receive the length of the result - * (not including the terminating \\0). - * If the pointer is NULL it is ignored. - * @param status the error code, set to U_ENUM_OUT_OF_SYNC_ERROR if - * the iterator is out of sync with its service. - * @return a pointer to the string. The string will be - * zero-terminated. The return pointer is owned by this iterator - * and must not be deleted by the caller. The pointer is valid - * until the next call to any uenum_... method, including - * uenum_next() or uenum_unext(). When all strings have been - * traversed, returns NULL. - * @stable ICU 2.2 - */ -U_CAPI const UChar* U_EXPORT2 -uenum_unext(UEnumeration* en, - int32_t* resultLength, - UErrorCode* status); - -/** - * Returns the next element in the iterator's list. If there are - * no more elements, returns NULL. If the iterator is out-of-sync - * with its service, status is set to U_ENUM_OUT_OF_SYNC_ERROR and - * NULL is returned. If the native service string is a UChar* - * string, it is converted to char* with the invariant converter. - * The result is terminated by (char)0. If the conversion fails - * (because a character cannot be converted) then status is set to - * U_INVARIANT_CONVERSION_ERROR and the return value is undefined - * (but non-NULL). - * @param en the iterator object - * @param resultLength pointer to receive the length of the result - * (not including the terminating \\0). - * If the pointer is NULL it is ignored. - * @param status the error code, set to U_ENUM_OUT_OF_SYNC_ERROR if - * the iterator is out of sync with its service. Set to - * U_INVARIANT_CONVERSION_ERROR if the underlying native string is - * UChar* and conversion to char* with the invariant converter - * fails. This error pertains only to current string, so iteration - * might be able to continue successfully. - * @return a pointer to the string. The string will be - * zero-terminated. The return pointer is owned by this iterator - * and must not be deleted by the caller. The pointer is valid - * until the next call to any uenum_... method, including - * uenum_next() or uenum_unext(). When all strings have been - * traversed, returns NULL. - * @stable ICU 2.2 - */ -U_CAPI const char* U_EXPORT2 -uenum_next(UEnumeration* en, - int32_t* resultLength, - UErrorCode* status); - -/** - * Resets the iterator to the current list of service IDs. This - * re-establishes sync with the service and rewinds the iterator - * to start at the first element. - * @param en the iterator object - * @param status the error code, set to U_ENUM_OUT_OF_SYNC_ERROR if - * the iterator is out of sync with its service. - * @stable ICU 2.2 - */ -U_CAPI void U_EXPORT2 -uenum_reset(UEnumeration* en, UErrorCode* status); - -#if U_SHOW_CPLUSPLUS_API - -/** - * Given a StringEnumeration, wrap it in a UEnumeration. The - * StringEnumeration is adopted; after this call, the caller must not - * delete it (regardless of error status). - * @param adopted the C++ StringEnumeration to be wrapped in a UEnumeration. - * @param ec the error code. - * @return a UEnumeration wrapping the adopted StringEnumeration. - * @stable ICU 4.2 - */ -U_CAPI UEnumeration* U_EXPORT2 -uenum_openFromStringEnumeration(icu::StringEnumeration* adopted, UErrorCode* ec); - -#endif - -/** - * Given an array of const UChar* strings, return a UEnumeration. String pointers from 0..count-1 must not be null. - * Do not free or modify either the string array or the characters it points to until this object has been destroyed with uenum_close. - * \snippet test/cintltst/uenumtst.c uenum_openUCharStringsEnumeration - * @param strings array of const UChar* strings (each null terminated). All storage is owned by the caller. - * @param count length of the array - * @param ec error code - * @return the new UEnumeration object. Caller is responsible for calling uenum_close to free memory. - * @see uenum_close - * @stable ICU 50 - */ -U_CAPI UEnumeration* U_EXPORT2 -uenum_openUCharStringsEnumeration(const UChar* const strings[], int32_t count, - UErrorCode* ec); - -/** - * Given an array of const char* strings (invariant chars only), return a UEnumeration. String pointers from 0..count-1 must not be null. - * Do not free or modify either the string array or the characters it points to until this object has been destroyed with uenum_close. - * \snippet test/cintltst/uenumtst.c uenum_openCharStringsEnumeration - * @param strings array of char* strings (each null terminated). All storage is owned by the caller. - * @param count length of the array - * @param ec error code - * @return the new UEnumeration object. Caller is responsible for calling uenum_close to free memory - * @see uenum_close - * @stable ICU 50 - */ -U_CAPI UEnumeration* U_EXPORT2 -uenum_openCharStringsEnumeration(const char* const strings[], int32_t count, - UErrorCode* ec); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2013, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uenum.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:2 +* +* created on: 2002jul08 +* created by: Vladimir Weinstein +*/ + +#ifndef __UENUM_H +#define __UENUM_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" + +U_NAMESPACE_BEGIN +class StringEnumeration; +U_NAMESPACE_END +#endif // U_SHOW_CPLUSPLUS_API + +/** + * \file + * \brief C API: String Enumeration + */ + +/** + * An enumeration object. + * For usage in C programs. + * @stable ICU 2.2 + */ +struct UEnumeration; +/** structure representing an enumeration object instance @stable ICU 2.2 */ +typedef struct UEnumeration UEnumeration; + +/** + * Disposes of resources in use by the iterator. If en is NULL, + * does nothing. After this call, any char* or UChar* pointer + * returned by uenum_unext() or uenum_next() is invalid. + * @param en UEnumeration structure pointer + * @stable ICU 2.2 + */ +U_CAPI void U_EXPORT2 +uenum_close(UEnumeration* en); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUEnumerationPointer + * "Smart pointer" class, closes a UEnumeration via uenum_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUEnumerationPointer, UEnumeration, uenum_close); + +U_NAMESPACE_END + +#endif + +/** + * Returns the number of elements that the iterator traverses. If + * the iterator is out-of-sync with its service, status is set to + * U_ENUM_OUT_OF_SYNC_ERROR. + * This is a convenience function. It can end up being very + * expensive as all the items might have to be pre-fetched (depending + * on the type of data being traversed). Use with caution and only + * when necessary. + * @param en UEnumeration structure pointer + * @param status error code, can be U_ENUM_OUT_OF_SYNC_ERROR if the + * iterator is out of sync. + * @return number of elements in the iterator + * @stable ICU 2.2 + */ +U_CAPI int32_t U_EXPORT2 +uenum_count(UEnumeration* en, UErrorCode* status); + +/** + * Returns the next element in the iterator's list. If there are + * no more elements, returns NULL. If the iterator is out-of-sync + * with its service, status is set to U_ENUM_OUT_OF_SYNC_ERROR and + * NULL is returned. If the native service string is a char* string, + * it is converted to UChar* with the invariant converter. + * The result is terminated by (UChar)0. + * @param en the iterator object + * @param resultLength pointer to receive the length of the result + * (not including the terminating \\0). + * If the pointer is NULL it is ignored. + * @param status the error code, set to U_ENUM_OUT_OF_SYNC_ERROR if + * the iterator is out of sync with its service. + * @return a pointer to the string. The string will be + * zero-terminated. The return pointer is owned by this iterator + * and must not be deleted by the caller. The pointer is valid + * until the next call to any uenum_... method, including + * uenum_next() or uenum_unext(). When all strings have been + * traversed, returns NULL. + * @stable ICU 2.2 + */ +U_CAPI const UChar* U_EXPORT2 +uenum_unext(UEnumeration* en, + int32_t* resultLength, + UErrorCode* status); + +/** + * Returns the next element in the iterator's list. If there are + * no more elements, returns NULL. If the iterator is out-of-sync + * with its service, status is set to U_ENUM_OUT_OF_SYNC_ERROR and + * NULL is returned. If the native service string is a UChar* + * string, it is converted to char* with the invariant converter. + * The result is terminated by (char)0. If the conversion fails + * (because a character cannot be converted) then status is set to + * U_INVARIANT_CONVERSION_ERROR and the return value is undefined + * (but non-NULL). + * @param en the iterator object + * @param resultLength pointer to receive the length of the result + * (not including the terminating \\0). + * If the pointer is NULL it is ignored. + * @param status the error code, set to U_ENUM_OUT_OF_SYNC_ERROR if + * the iterator is out of sync with its service. Set to + * U_INVARIANT_CONVERSION_ERROR if the underlying native string is + * UChar* and conversion to char* with the invariant converter + * fails. This error pertains only to current string, so iteration + * might be able to continue successfully. + * @return a pointer to the string. The string will be + * zero-terminated. The return pointer is owned by this iterator + * and must not be deleted by the caller. The pointer is valid + * until the next call to any uenum_... method, including + * uenum_next() or uenum_unext(). When all strings have been + * traversed, returns NULL. + * @stable ICU 2.2 + */ +U_CAPI const char* U_EXPORT2 +uenum_next(UEnumeration* en, + int32_t* resultLength, + UErrorCode* status); + +/** + * Resets the iterator to the current list of service IDs. This + * re-establishes sync with the service and rewinds the iterator + * to start at the first element. + * @param en the iterator object + * @param status the error code, set to U_ENUM_OUT_OF_SYNC_ERROR if + * the iterator is out of sync with its service. + * @stable ICU 2.2 + */ +U_CAPI void U_EXPORT2 +uenum_reset(UEnumeration* en, UErrorCode* status); + +#if U_SHOW_CPLUSPLUS_API + +/** + * Given a StringEnumeration, wrap it in a UEnumeration. The + * StringEnumeration is adopted; after this call, the caller must not + * delete it (regardless of error status). + * @param adopted the C++ StringEnumeration to be wrapped in a UEnumeration. + * @param ec the error code. + * @return a UEnumeration wrapping the adopted StringEnumeration. + * @stable ICU 4.2 + */ +U_CAPI UEnumeration* U_EXPORT2 +uenum_openFromStringEnumeration(icu::StringEnumeration* adopted, UErrorCode* ec); + +#endif + +/** + * Given an array of const UChar* strings, return a UEnumeration. String pointers from 0..count-1 must not be null. + * Do not free or modify either the string array or the characters it points to until this object has been destroyed with uenum_close. + * \snippet test/cintltst/uenumtst.c uenum_openUCharStringsEnumeration + * @param strings array of const UChar* strings (each null terminated). All storage is owned by the caller. + * @param count length of the array + * @param ec error code + * @return the new UEnumeration object. Caller is responsible for calling uenum_close to free memory. + * @see uenum_close + * @stable ICU 50 + */ +U_CAPI UEnumeration* U_EXPORT2 +uenum_openUCharStringsEnumeration(const UChar* const strings[], int32_t count, + UErrorCode* ec); + +/** + * Given an array of const char* strings (invariant chars only), return a UEnumeration. String pointers from 0..count-1 must not be null. + * Do not free or modify either the string array or the characters it points to until this object has been destroyed with uenum_close. + * \snippet test/cintltst/uenumtst.c uenum_openCharStringsEnumeration + * @param strings array of char* strings (each null terminated). All storage is owned by the caller. + * @param count length of the array + * @param ec error code + * @return the new UEnumeration object. Caller is responsible for calling uenum_close to free memory + * @see uenum_close + * @stable ICU 50 + */ +U_CAPI UEnumeration* U_EXPORT2 +uenum_openCharStringsEnumeration(const char* const strings[], int32_t count, + UErrorCode* ec); + +#endif diff --git a/deps/icu-small/source/common/unicode/uidna.h b/deps/icu-small/source/common/unicode/uidna.h index 24a81ceaddf58d..732adedd86e8a8 100644 --- a/deps/icu-small/source/common/unicode/uidna.h +++ b/deps/icu-small/source/common/unicode/uidna.h @@ -1,776 +1,776 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ******************************************************************************* - * - * Copyright (C) 2003-2014, International Business Machines - * Corporation and others. All Rights Reserved. - * - ******************************************************************************* - * file name: uidna.h - * encoding: UTF-8 - * tab size: 8 (not used) - * indentation:4 - * - * created on: 2003feb1 - * created by: Ram Viswanadha - */ - -#ifndef __UIDNA_H__ -#define __UIDNA_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_IDNA - -#include -#include "unicode/parseerr.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -/** - * \file - * \brief C API: Internationalizing Domain Names in Applications (IDNA) - * - * IDNA2008 is implemented according to UTS #46, see the IDNA C++ class in idna.h. - * - * The C API functions which do take a UIDNA * service object pointer - * implement UTS #46 and IDNA2008. - * - * IDNA2003 is obsolete. - * The C API functions which do not take a service object pointer - * implement IDNA2003. They are all deprecated. - */ - -/* - * IDNA option bit set values. - */ -enum { - /** - * Default options value: None of the other options are set. - * For use in static worker and factory methods. - * @stable ICU 2.6 - */ - UIDNA_DEFAULT=0, -#ifndef U_HIDE_DEPRECATED_API - /** - * Option to allow unassigned code points in domain names and labels. - * For use in static worker and factory methods. - *

This option is ignored by the UTS46 implementation. - * (UTS #46 disallows unassigned code points.) - * @deprecated ICU 55 Use UTS #46 instead via uidna_openUTS46() or class IDNA. - */ - UIDNA_ALLOW_UNASSIGNED=1, -#endif /* U_HIDE_DEPRECATED_API */ - /** - * Option to check whether the input conforms to the STD3 ASCII rules, - * for example the restriction of labels to LDH characters - * (ASCII Letters, Digits and Hyphen-Minus). - * For use in static worker and factory methods. - * @stable ICU 2.6 - */ - UIDNA_USE_STD3_RULES=2, - /** - * IDNA option to check for whether the input conforms to the BiDi rules. - * For use in static worker and factory methods. - *

This option is ignored by the IDNA2003 implementation. - * (IDNA2003 always performs a BiDi check.) - * @stable ICU 4.6 - */ - UIDNA_CHECK_BIDI=4, - /** - * IDNA option to check for whether the input conforms to the CONTEXTJ rules. - * For use in static worker and factory methods. - *

This option is ignored by the IDNA2003 implementation. - * (The CONTEXTJ check is new in IDNA2008.) - * @stable ICU 4.6 - */ - UIDNA_CHECK_CONTEXTJ=8, - /** - * IDNA option for nontransitional processing in ToASCII(). - * For use in static worker and factory methods. - *

By default, ToASCII() uses transitional processing. - *

This option is ignored by the IDNA2003 implementation. - * (This is only relevant for compatibility of newer IDNA implementations with IDNA2003.) - * @stable ICU 4.6 - */ - UIDNA_NONTRANSITIONAL_TO_ASCII=0x10, - /** - * IDNA option for nontransitional processing in ToUnicode(). - * For use in static worker and factory methods. - *

By default, ToUnicode() uses transitional processing. - *

This option is ignored by the IDNA2003 implementation. - * (This is only relevant for compatibility of newer IDNA implementations with IDNA2003.) - * @stable ICU 4.6 - */ - UIDNA_NONTRANSITIONAL_TO_UNICODE=0x20, - /** - * IDNA option to check for whether the input conforms to the CONTEXTO rules. - * For use in static worker and factory methods. - *

This option is ignored by the IDNA2003 implementation. - * (The CONTEXTO check is new in IDNA2008.) - *

This is for use by registries for IDNA2008 conformance. - * UTS #46 does not require the CONTEXTO check. - * @stable ICU 49 - */ - UIDNA_CHECK_CONTEXTO=0x40 -}; - -/** - * Opaque C service object type for the new IDNA API. - * @stable ICU 4.6 - */ -struct UIDNA; -typedef struct UIDNA UIDNA; /**< C typedef for struct UIDNA. @stable ICU 4.6 */ - -/** - * Returns a UIDNA instance which implements UTS #46. - * Returns an unmodifiable instance, owned by the caller. - * Cache it for multiple operations, and uidna_close() it when done. - * The instance is thread-safe, that is, it can be used concurrently. - * - * For details about the UTS #46 implementation see the IDNA C++ class in idna.h. - * - * @param options Bit set to modify the processing and error checking. - * See option bit set values in uidna.h. - * @param pErrorCode Standard 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.) - * @return the UTS #46 UIDNA instance, if successful - * @stable ICU 4.6 - */ -U_CAPI UIDNA * U_EXPORT2 -uidna_openUTS46(uint32_t options, UErrorCode *pErrorCode); - -/** - * Closes a UIDNA instance. - * @param idna UIDNA instance to be closed - * @stable ICU 4.6 - */ -U_CAPI void U_EXPORT2 -uidna_close(UIDNA *idna); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUIDNAPointer - * "Smart pointer" class, closes a UIDNA via uidna_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.6 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUIDNAPointer, UIDNA, uidna_close); - -U_NAMESPACE_END - -#endif - -/** - * Output container for IDNA processing errors. - * Initialize with UIDNA_INFO_INITIALIZER: - * \code - * UIDNAInfo info = UIDNA_INFO_INITIALIZER; - * int32_t length = uidna_nameToASCII(..., &info, &errorCode); - * if(U_SUCCESS(errorCode) && info.errors!=0) { ... } - * \endcode - * @stable ICU 4.6 - */ -typedef struct UIDNAInfo { - /** sizeof(UIDNAInfo) @stable ICU 4.6 */ - int16_t size; - /** - * Set to true if transitional and nontransitional processing produce different results. - * For details see C++ IDNAInfo::isTransitionalDifferent(). - * @stable ICU 4.6 - */ - UBool isTransitionalDifferent; - UBool reservedB3; /**< Reserved field, do not use. @internal */ - /** - * Bit set indicating IDNA processing errors. 0 if no errors. - * See UIDNA_ERROR_... constants. - * @stable ICU 4.6 - */ - uint32_t errors; - int32_t reservedI2; /**< Reserved field, do not use. @internal */ - int32_t reservedI3; /**< Reserved field, do not use. @internal */ -} UIDNAInfo; - -/** - * Static initializer for a UIDNAInfo struct. - * @stable ICU 4.6 - */ -#define UIDNA_INFO_INITIALIZER { \ - (int16_t)sizeof(UIDNAInfo), \ - false, false, \ - 0, 0, 0 } - -/** - * Converts a single domain name label into its ASCII form for DNS lookup. - * If any processing step fails, then pInfo->errors will be non-zero and - * the result might not be an ASCII string. - * The label might be modified according to the types of errors. - * Labels with severe errors will be left in (or turned into) their Unicode form. - * - * The UErrorCode indicates an error only in exceptional cases, - * such as a U_MEMORY_ALLOCATION_ERROR. - * - * @param idna UIDNA instance - * @param label Input domain name label - * @param length Label length, or -1 if NUL-terminated - * @param dest Destination string buffer - * @param capacity Destination buffer capacity - * @param pInfo Output container of IDNA processing details. - * @param pErrorCode Standard 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.) - * @return destination string length - * @stable ICU 4.6 - */ -U_CAPI int32_t U_EXPORT2 -uidna_labelToASCII(const UIDNA *idna, - const UChar *label, int32_t length, - UChar *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode); - -/** - * Converts a single domain name label into its Unicode form for human-readable display. - * If any processing step fails, then pInfo->errors will be non-zero. - * The label might be modified according to the types of errors. - * - * The UErrorCode indicates an error only in exceptional cases, - * such as a U_MEMORY_ALLOCATION_ERROR. - * - * @param idna UIDNA instance - * @param label Input domain name label - * @param length Label length, or -1 if NUL-terminated - * @param dest Destination string buffer - * @param capacity Destination buffer capacity - * @param pInfo Output container of IDNA processing details. - * @param pErrorCode Standard 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.) - * @return destination string length - * @stable ICU 4.6 - */ -U_CAPI int32_t U_EXPORT2 -uidna_labelToUnicode(const UIDNA *idna, - const UChar *label, int32_t length, - UChar *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode); - -/** - * Converts a whole domain name into its ASCII form for DNS lookup. - * If any processing step fails, then pInfo->errors will be non-zero and - * the result might not be an ASCII string. - * The domain name might be modified according to the types of errors. - * Labels with severe errors will be left in (or turned into) their Unicode form. - * - * The UErrorCode indicates an error only in exceptional cases, - * such as a U_MEMORY_ALLOCATION_ERROR. - * - * @param idna UIDNA instance - * @param name Input domain name - * @param length Domain name length, or -1 if NUL-terminated - * @param dest Destination string buffer - * @param capacity Destination buffer capacity - * @param pInfo Output container of IDNA processing details. - * @param pErrorCode Standard 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.) - * @return destination string length - * @stable ICU 4.6 - */ -U_CAPI int32_t U_EXPORT2 -uidna_nameToASCII(const UIDNA *idna, - const UChar *name, int32_t length, - UChar *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode); - -/** - * Converts a whole domain name into its Unicode form for human-readable display. - * If any processing step fails, then pInfo->errors will be non-zero. - * The domain name might be modified according to the types of errors. - * - * The UErrorCode indicates an error only in exceptional cases, - * such as a U_MEMORY_ALLOCATION_ERROR. - * - * @param idna UIDNA instance - * @param name Input domain name - * @param length Domain name length, or -1 if NUL-terminated - * @param dest Destination string buffer - * @param capacity Destination buffer capacity - * @param pInfo Output container of IDNA processing details. - * @param pErrorCode Standard 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.) - * @return destination string length - * @stable ICU 4.6 - */ -U_CAPI int32_t U_EXPORT2 -uidna_nameToUnicode(const UIDNA *idna, - const UChar *name, int32_t length, - UChar *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode); - -/* UTF-8 versions of the processing methods --------------------------------- */ - -/** - * Converts a single domain name label into its ASCII form for DNS lookup. - * UTF-8 version of uidna_labelToASCII(), same behavior. - * - * @param idna UIDNA instance - * @param label Input domain name label - * @param length Label length, or -1 if NUL-terminated - * @param dest Destination string buffer - * @param capacity Destination buffer capacity - * @param pInfo Output container of IDNA processing details. - * @param pErrorCode Standard 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.) - * @return destination string length - * @stable ICU 4.6 - */ -U_CAPI int32_t U_EXPORT2 -uidna_labelToASCII_UTF8(const UIDNA *idna, - const char *label, int32_t length, - char *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode); - -/** - * Converts a single domain name label into its Unicode form for human-readable display. - * UTF-8 version of uidna_labelToUnicode(), same behavior. - * - * @param idna UIDNA instance - * @param label Input domain name label - * @param length Label length, or -1 if NUL-terminated - * @param dest Destination string buffer - * @param capacity Destination buffer capacity - * @param pInfo Output container of IDNA processing details. - * @param pErrorCode Standard 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.) - * @return destination string length - * @stable ICU 4.6 - */ -U_CAPI int32_t U_EXPORT2 -uidna_labelToUnicodeUTF8(const UIDNA *idna, - const char *label, int32_t length, - char *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode); - -/** - * Converts a whole domain name into its ASCII form for DNS lookup. - * UTF-8 version of uidna_nameToASCII(), same behavior. - * - * @param idna UIDNA instance - * @param name Input domain name - * @param length Domain name length, or -1 if NUL-terminated - * @param dest Destination string buffer - * @param capacity Destination buffer capacity - * @param pInfo Output container of IDNA processing details. - * @param pErrorCode Standard 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.) - * @return destination string length - * @stable ICU 4.6 - */ -U_CAPI int32_t U_EXPORT2 -uidna_nameToASCII_UTF8(const UIDNA *idna, - const char *name, int32_t length, - char *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode); - -/** - * Converts a whole domain name into its Unicode form for human-readable display. - * UTF-8 version of uidna_nameToUnicode(), same behavior. - * - * @param idna UIDNA instance - * @param name Input domain name - * @param length Domain name length, or -1 if NUL-terminated - * @param dest Destination string buffer - * @param capacity Destination buffer capacity - * @param pInfo Output container of IDNA processing details. - * @param pErrorCode Standard 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.) - * @return destination string length - * @stable ICU 4.6 - */ -U_CAPI int32_t U_EXPORT2 -uidna_nameToUnicodeUTF8(const UIDNA *idna, - const char *name, int32_t length, - char *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode); - -/* - * IDNA error bit set values. - * When a domain name or label fails a processing step or does not meet the - * validity criteria, then one or more of these error bits are set. - */ -enum { - /** - * A non-final domain name label (or the whole domain name) is empty. - * @stable ICU 4.6 - */ - UIDNA_ERROR_EMPTY_LABEL=1, - /** - * A domain name label is longer than 63 bytes. - * (See STD13/RFC1034 3.1. Name space specifications and terminology.) - * This is only checked in ToASCII operations, and only if the output label is all-ASCII. - * @stable ICU 4.6 - */ - UIDNA_ERROR_LABEL_TOO_LONG=2, - /** - * A domain name is longer than 255 bytes in its storage form. - * (See STD13/RFC1034 3.1. Name space specifications and terminology.) - * This is only checked in ToASCII operations, and only if the output domain name is all-ASCII. - * @stable ICU 4.6 - */ - UIDNA_ERROR_DOMAIN_NAME_TOO_LONG=4, - /** - * A label starts with a hyphen-minus ('-'). - * @stable ICU 4.6 - */ - UIDNA_ERROR_LEADING_HYPHEN=8, - /** - * A label ends with a hyphen-minus ('-'). - * @stable ICU 4.6 - */ - UIDNA_ERROR_TRAILING_HYPHEN=0x10, - /** - * A label contains hyphen-minus ('-') in the third and fourth positions. - * @stable ICU 4.6 - */ - UIDNA_ERROR_HYPHEN_3_4=0x20, - /** - * A label starts with a combining mark. - * @stable ICU 4.6 - */ - UIDNA_ERROR_LEADING_COMBINING_MARK=0x40, - /** - * A label or domain name contains disallowed characters. - * @stable ICU 4.6 - */ - UIDNA_ERROR_DISALLOWED=0x80, - /** - * A label starts with "xn--" but does not contain valid Punycode. - * That is, an xn-- label failed Punycode decoding. - * @stable ICU 4.6 - */ - UIDNA_ERROR_PUNYCODE=0x100, - /** - * A label contains a dot=full stop. - * This can occur in an input string for a single-label function. - * @stable ICU 4.6 - */ - UIDNA_ERROR_LABEL_HAS_DOT=0x200, - /** - * An ACE label does not contain a valid label string. - * The label was successfully ACE (Punycode) decoded but the resulting - * string had severe validation errors. For example, - * it might contain characters that are not allowed in ACE labels, - * or it might not be normalized. - * @stable ICU 4.6 - */ - UIDNA_ERROR_INVALID_ACE_LABEL=0x400, - /** - * A label does not meet the IDNA BiDi requirements (for right-to-left characters). - * @stable ICU 4.6 - */ - UIDNA_ERROR_BIDI=0x800, - /** - * A label does not meet the IDNA CONTEXTJ requirements. - * @stable ICU 4.6 - */ - UIDNA_ERROR_CONTEXTJ=0x1000, - /** - * A label does not meet the IDNA CONTEXTO requirements for punctuation characters. - * Some punctuation characters "Would otherwise have been DISALLOWED" - * but are allowed in certain contexts. (RFC 5892) - * @stable ICU 49 - */ - UIDNA_ERROR_CONTEXTO_PUNCTUATION=0x2000, - /** - * A label does not meet the IDNA CONTEXTO requirements for digits. - * Arabic-Indic Digits (U+066x) must not be mixed with Extended Arabic-Indic Digits (U+06Fx). - * @stable ICU 49 - */ - UIDNA_ERROR_CONTEXTO_DIGITS=0x4000 -}; - -#ifndef U_HIDE_DEPRECATED_API - -/* IDNA2003 API ------------------------------------------------------------- */ - -/** - * IDNA2003: This function implements the ToASCII operation as defined in the IDNA RFC. - * This operation is done on single labels before sending it to something that expects - * ASCII names. A label is an individual part of a domain name. Labels are usually - * separated by dots; e.g. "www.example.com" is composed of 3 labels "www","example", and "com". - * - * IDNA2003 API Overview: - * - * The uidna_ API implements the IDNA protocol as defined in the IDNA RFC - * (http://www.ietf.org/rfc/rfc3490.txt). - * The RFC defines 2 operations: ToASCII and ToUnicode. Domain name labels - * containing non-ASCII code points are processed by the - * ToASCII operation before passing it to resolver libraries. Domain names - * that are obtained from resolver libraries are processed by the - * ToUnicode operation before displaying the domain name to the user. - * IDNA requires that implementations process input strings with Nameprep - * (http://www.ietf.org/rfc/rfc3491.txt), - * which is a profile of Stringprep (http://www.ietf.org/rfc/rfc3454.txt), - * and then with Punycode (http://www.ietf.org/rfc/rfc3492.txt). - * Implementations of IDNA MUST fully implement Nameprep and Punycode; - * neither Nameprep nor Punycode are optional. - * The input and output of ToASCII and ToUnicode operations are Unicode - * and are designed to be chainable, i.e., applying ToASCII or ToUnicode operations - * multiple times to an input string will yield the same result as applying the operation - * once. - * ToUnicode(ToUnicode(ToUnicode...(ToUnicode(string)))) == ToUnicode(string) - * ToASCII(ToASCII(ToASCII...(ToASCII(string))) == ToASCII(string). - * - * @param src Input UChar array containing label in Unicode. - * @param srcLength Number of UChars in src, or -1 if NUL-terminated. - * @param dest Output UChar array with ASCII (ACE encoded) label. - * @param destCapacity Size of dest. - * @param options A bit set of options: - * - * - UIDNA_DEFAULT Use default options, i.e., do not process unassigned code points - * and do not use STD3 ASCII rules - * If unassigned code points are found the operation fails with - * U_UNASSIGNED_ERROR error code. - * - * - UIDNA_ALLOW_UNASSIGNED Unassigned values can be converted to ASCII for query operations - * If this option is set, the unassigned code points are in the input - * are treated as normal Unicode code points. - * - * - UIDNA_USE_STD3_RULES Use STD3 ASCII rules for host name syntax restrictions - * If this option is set and the input does not satisfy STD3 rules, - * the operation will fail with U_IDNA_STD3_ASCII_RULES_ERROR - * - * @param parseError Pointer to UParseError struct to receive information on position - * of error if an error is encountered. Can be NULL. - * @param status ICU in/out error code parameter. - * U_INVALID_CHAR_FOUND if src contains - * unmatched single surrogates. - * U_INDEX_OUTOFBOUNDS_ERROR if src contains - * too many code points. - * U_BUFFER_OVERFLOW_ERROR if destCapacity is not enough - * @return The length of the result string, if successful - or in case of a buffer overflow, - * in which case it will be greater than destCapacity. - * @deprecated ICU 55 Use UTS #46 instead via uidna_openUTS46() or class IDNA. - */ -U_DEPRECATED int32_t U_EXPORT2 -uidna_toASCII(const UChar* src, int32_t srcLength, - UChar* dest, int32_t destCapacity, - int32_t options, - UParseError* parseError, - UErrorCode* status); - - -/** - * IDNA2003: This function implements the ToUnicode operation as defined in the IDNA RFC. - * This operation is done on single labels before sending it to something that expects - * Unicode names. A label is an individual part of a domain name. Labels are usually - * separated by dots; for e.g. "www.example.com" is composed of 3 labels "www","example", and "com". - * - * @param src Input UChar array containing ASCII (ACE encoded) label. - * @param srcLength Number of UChars in src, or -1 if NUL-terminated. - * @param dest Output Converted UChar array containing Unicode equivalent of label. - * @param destCapacity Size of dest. - * @param options A bit set of options: - * - * - UIDNA_DEFAULT Use default options, i.e., do not process unassigned code points - * and do not use STD3 ASCII rules - * If unassigned code points are found the operation fails with - * U_UNASSIGNED_ERROR error code. - * - * - UIDNA_ALLOW_UNASSIGNED Unassigned values can be converted to ASCII for query operations - * If this option is set, the unassigned code points are in the input - * are treated as normal Unicode code points. Note: This option is - * required on toUnicode operation because the RFC mandates - * verification of decoded ACE input by applying toASCII and comparing - * its output with source - * - * - UIDNA_USE_STD3_RULES Use STD3 ASCII rules for host name syntax restrictions - * If this option is set and the input does not satisfy STD3 rules, - * the operation will fail with U_IDNA_STD3_ASCII_RULES_ERROR - * - * @param parseError Pointer to UParseError struct to receive information on position - * of error if an error is encountered. Can be NULL. - * @param status ICU in/out error code parameter. - * U_INVALID_CHAR_FOUND if src contains - * unmatched single surrogates. - * U_INDEX_OUTOFBOUNDS_ERROR if src contains - * too many code points. - * U_BUFFER_OVERFLOW_ERROR if destCapacity is not enough - * @return The length of the result string, if successful - or in case of a buffer overflow, - * in which case it will be greater than destCapacity. - * @deprecated ICU 55 Use UTS #46 instead via uidna_openUTS46() or class IDNA. - */ -U_DEPRECATED int32_t U_EXPORT2 -uidna_toUnicode(const UChar* src, int32_t srcLength, - UChar* dest, int32_t destCapacity, - int32_t options, - UParseError* parseError, - UErrorCode* status); - - -/** - * IDNA2003: Convenience function that implements the IDNToASCII operation as defined in the IDNA RFC. - * This operation is done on complete domain names, e.g: "www.example.com". - * It is important to note that this operation can fail. If it fails, then the input - * domain name cannot be used as an Internationalized Domain Name and the application - * should have methods defined to deal with the failure. - * - * Note: IDNA RFC specifies that a conformant application should divide a domain name - * into separate labels, decide whether to apply allowUnassigned and useSTD3ASCIIRules on each, - * and then convert. This function does not offer that level of granularity. The options once - * set will apply to all labels in the domain name - * - * @param src Input UChar array containing IDN in Unicode. - * @param srcLength Number of UChars in src, or -1 if NUL-terminated. - * @param dest Output UChar array with ASCII (ACE encoded) IDN. - * @param destCapacity Size of dest. - * @param options A bit set of options: - * - * - UIDNA_DEFAULT Use default options, i.e., do not process unassigned code points - * and do not use STD3 ASCII rules - * If unassigned code points are found the operation fails with - * U_UNASSIGNED_CODE_POINT_FOUND error code. - * - * - UIDNA_ALLOW_UNASSIGNED Unassigned values can be converted to ASCII for query operations - * If this option is set, the unassigned code points are in the input - * are treated as normal Unicode code points. - * - * - UIDNA_USE_STD3_RULES Use STD3 ASCII rules for host name syntax restrictions - * If this option is set and the input does not satisfy STD3 rules, - * the operation will fail with U_IDNA_STD3_ASCII_RULES_ERROR - * - * @param parseError Pointer to UParseError struct to receive information on position - * of error if an error is encountered. Can be NULL. - * @param status ICU in/out error code parameter. - * U_INVALID_CHAR_FOUND if src contains - * unmatched single surrogates. - * U_INDEX_OUTOFBOUNDS_ERROR if src contains - * too many code points. - * U_BUFFER_OVERFLOW_ERROR if destCapacity is not enough - * @return The length of the result string, if successful - or in case of a buffer overflow, - * in which case it will be greater than destCapacity. - * @deprecated ICU 55 Use UTS #46 instead via uidna_openUTS46() or class IDNA. - */ -U_DEPRECATED int32_t U_EXPORT2 -uidna_IDNToASCII( const UChar* src, int32_t srcLength, - UChar* dest, int32_t destCapacity, - int32_t options, - UParseError* parseError, - UErrorCode* status); - -/** - * IDNA2003: Convenience function that implements the IDNToUnicode operation as defined in the IDNA RFC. - * This operation is done on complete domain names, e.g: "www.example.com". - * - * Note: IDNA RFC specifies that a conformant application should divide a domain name - * into separate labels, decide whether to apply allowUnassigned and useSTD3ASCIIRules on each, - * and then convert. This function does not offer that level of granularity. The options once - * set will apply to all labels in the domain name - * - * @param src Input UChar array containing IDN in ASCII (ACE encoded) form. - * @param srcLength Number of UChars in src, or -1 if NUL-terminated. - * @param dest Output UChar array containing Unicode equivalent of source IDN. - * @param destCapacity Size of dest. - * @param options A bit set of options: - * - * - UIDNA_DEFAULT Use default options, i.e., do not process unassigned code points - * and do not use STD3 ASCII rules - * If unassigned code points are found the operation fails with - * U_UNASSIGNED_CODE_POINT_FOUND error code. - * - * - UIDNA_ALLOW_UNASSIGNED Unassigned values can be converted to ASCII for query operations - * If this option is set, the unassigned code points are in the input - * are treated as normal Unicode code points. - * - * - UIDNA_USE_STD3_RULES Use STD3 ASCII rules for host name syntax restrictions - * If this option is set and the input does not satisfy STD3 rules, - * the operation will fail with U_IDNA_STD3_ASCII_RULES_ERROR - * - * @param parseError Pointer to UParseError struct to receive information on position - * of error if an error is encountered. Can be NULL. - * @param status ICU in/out error code parameter. - * U_INVALID_CHAR_FOUND if src contains - * unmatched single surrogates. - * U_INDEX_OUTOFBOUNDS_ERROR if src contains - * too many code points. - * U_BUFFER_OVERFLOW_ERROR if destCapacity is not enough - * @return The length of the result string, if successful - or in case of a buffer overflow, - * in which case it will be greater than destCapacity. - * @deprecated ICU 55 Use UTS #46 instead via uidna_openUTS46() or class IDNA. - */ -U_DEPRECATED int32_t U_EXPORT2 -uidna_IDNToUnicode( const UChar* src, int32_t srcLength, - UChar* dest, int32_t destCapacity, - int32_t options, - UParseError* parseError, - UErrorCode* status); - -/** - * IDNA2003: Compare two IDN strings for equivalence. - * This function splits the domain names into labels and compares them. - * According to IDN RFC, whenever two labels are compared, they are - * considered equal if and only if their ASCII forms (obtained by - * applying toASCII) match using an case-insensitive ASCII comparison. - * Two domain names are considered a match if and only if all labels - * match regardless of whether label separators match. - * - * @param s1 First source string. - * @param length1 Length of first source string, or -1 if NUL-terminated. - * - * @param s2 Second source string. - * @param length2 Length of second source string, or -1 if NUL-terminated. - * @param options A bit set of options: - * - * - UIDNA_DEFAULT Use default options, i.e., do not process unassigned code points - * and do not use STD3 ASCII rules - * If unassigned code points are found the operation fails with - * U_UNASSIGNED_CODE_POINT_FOUND error code. - * - * - UIDNA_ALLOW_UNASSIGNED Unassigned values can be converted to ASCII for query operations - * If this option is set, the unassigned code points are in the input - * are treated as normal Unicode code points. - * - * - UIDNA_USE_STD3_RULES Use STD3 ASCII rules for host name syntax restrictions - * If this option is set and the input does not satisfy STD3 rules, - * the operation will fail with U_IDNA_STD3_ASCII_RULES_ERROR - * - * @param status ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return <0 or 0 or >0 as usual for string comparisons - * @deprecated ICU 55 Use UTS #46 instead via uidna_openUTS46() or class IDNA. - */ -U_DEPRECATED int32_t U_EXPORT2 -uidna_compare( const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - int32_t options, - UErrorCode* status); - -#endif /* U_HIDE_DEPRECATED_API */ - -#endif /* #if !UCONFIG_NO_IDNA */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************************* + * + * Copyright (C) 2003-2014, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: uidna.h + * encoding: UTF-8 + * tab size: 8 (not used) + * indentation:4 + * + * created on: 2003feb1 + * created by: Ram Viswanadha + */ + +#ifndef __UIDNA_H__ +#define __UIDNA_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_IDNA + +#include +#include "unicode/parseerr.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +/** + * \file + * \brief C API: Internationalizing Domain Names in Applications (IDNA) + * + * IDNA2008 is implemented according to UTS #46, see the IDNA C++ class in idna.h. + * + * The C API functions which do take a UIDNA * service object pointer + * implement UTS #46 and IDNA2008. + * + * IDNA2003 is obsolete. + * The C API functions which do not take a service object pointer + * implement IDNA2003. They are all deprecated. + */ + +/* + * IDNA option bit set values. + */ +enum { + /** + * Default options value: None of the other options are set. + * For use in static worker and factory methods. + * @stable ICU 2.6 + */ + UIDNA_DEFAULT=0, +#ifndef U_HIDE_DEPRECATED_API + /** + * Option to allow unassigned code points in domain names and labels. + * For use in static worker and factory methods. + *

This option is ignored by the UTS46 implementation. + * (UTS #46 disallows unassigned code points.) + * @deprecated ICU 55 Use UTS #46 instead via uidna_openUTS46() or class IDNA. + */ + UIDNA_ALLOW_UNASSIGNED=1, +#endif /* U_HIDE_DEPRECATED_API */ + /** + * Option to check whether the input conforms to the STD3 ASCII rules, + * for example the restriction of labels to LDH characters + * (ASCII Letters, Digits and Hyphen-Minus). + * For use in static worker and factory methods. + * @stable ICU 2.6 + */ + UIDNA_USE_STD3_RULES=2, + /** + * IDNA option to check for whether the input conforms to the BiDi rules. + * For use in static worker and factory methods. + *

This option is ignored by the IDNA2003 implementation. + * (IDNA2003 always performs a BiDi check.) + * @stable ICU 4.6 + */ + UIDNA_CHECK_BIDI=4, + /** + * IDNA option to check for whether the input conforms to the CONTEXTJ rules. + * For use in static worker and factory methods. + *

This option is ignored by the IDNA2003 implementation. + * (The CONTEXTJ check is new in IDNA2008.) + * @stable ICU 4.6 + */ + UIDNA_CHECK_CONTEXTJ=8, + /** + * IDNA option for nontransitional processing in ToASCII(). + * For use in static worker and factory methods. + *

By default, ToASCII() uses transitional processing. + *

This option is ignored by the IDNA2003 implementation. + * (This is only relevant for compatibility of newer IDNA implementations with IDNA2003.) + * @stable ICU 4.6 + */ + UIDNA_NONTRANSITIONAL_TO_ASCII=0x10, + /** + * IDNA option for nontransitional processing in ToUnicode(). + * For use in static worker and factory methods. + *

By default, ToUnicode() uses transitional processing. + *

This option is ignored by the IDNA2003 implementation. + * (This is only relevant for compatibility of newer IDNA implementations with IDNA2003.) + * @stable ICU 4.6 + */ + UIDNA_NONTRANSITIONAL_TO_UNICODE=0x20, + /** + * IDNA option to check for whether the input conforms to the CONTEXTO rules. + * For use in static worker and factory methods. + *

This option is ignored by the IDNA2003 implementation. + * (The CONTEXTO check is new in IDNA2008.) + *

This is for use by registries for IDNA2008 conformance. + * UTS #46 does not require the CONTEXTO check. + * @stable ICU 49 + */ + UIDNA_CHECK_CONTEXTO=0x40 +}; + +/** + * Opaque C service object type for the new IDNA API. + * @stable ICU 4.6 + */ +struct UIDNA; +typedef struct UIDNA UIDNA; /**< C typedef for struct UIDNA. @stable ICU 4.6 */ + +/** + * Returns a UIDNA instance which implements UTS #46. + * Returns an unmodifiable instance, owned by the caller. + * Cache it for multiple operations, and uidna_close() it when done. + * The instance is thread-safe, that is, it can be used concurrently. + * + * For details about the UTS #46 implementation see the IDNA C++ class in idna.h. + * + * @param options Bit set to modify the processing and error checking. + * See option bit set values in uidna.h. + * @param pErrorCode Standard 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.) + * @return the UTS #46 UIDNA instance, if successful + * @stable ICU 4.6 + */ +U_CAPI UIDNA * U_EXPORT2 +uidna_openUTS46(uint32_t options, UErrorCode *pErrorCode); + +/** + * Closes a UIDNA instance. + * @param idna UIDNA instance to be closed + * @stable ICU 4.6 + */ +U_CAPI void U_EXPORT2 +uidna_close(UIDNA *idna); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUIDNAPointer + * "Smart pointer" class, closes a UIDNA via uidna_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.6 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUIDNAPointer, UIDNA, uidna_close); + +U_NAMESPACE_END + +#endif + +/** + * Output container for IDNA processing errors. + * Initialize with UIDNA_INFO_INITIALIZER: + * \code + * UIDNAInfo info = UIDNA_INFO_INITIALIZER; + * int32_t length = uidna_nameToASCII(..., &info, &errorCode); + * if(U_SUCCESS(errorCode) && info.errors!=0) { ... } + * \endcode + * @stable ICU 4.6 + */ +typedef struct UIDNAInfo { + /** sizeof(UIDNAInfo) @stable ICU 4.6 */ + int16_t size; + /** + * Set to true if transitional and nontransitional processing produce different results. + * For details see C++ IDNAInfo::isTransitionalDifferent(). + * @stable ICU 4.6 + */ + UBool isTransitionalDifferent; + UBool reservedB3; /**< Reserved field, do not use. @internal */ + /** + * Bit set indicating IDNA processing errors. 0 if no errors. + * See UIDNA_ERROR_... constants. + * @stable ICU 4.6 + */ + uint32_t errors; + int32_t reservedI2; /**< Reserved field, do not use. @internal */ + int32_t reservedI3; /**< Reserved field, do not use. @internal */ +} UIDNAInfo; + +/** + * Static initializer for a UIDNAInfo struct. + * @stable ICU 4.6 + */ +#define UIDNA_INFO_INITIALIZER { \ + (int16_t)sizeof(UIDNAInfo), \ + false, false, \ + 0, 0, 0 } + +/** + * Converts a single domain name label into its ASCII form for DNS lookup. + * If any processing step fails, then pInfo->errors will be non-zero and + * the result might not be an ASCII string. + * The label might be modified according to the types of errors. + * Labels with severe errors will be left in (or turned into) their Unicode form. + * + * The UErrorCode indicates an error only in exceptional cases, + * such as a U_MEMORY_ALLOCATION_ERROR. + * + * @param idna UIDNA instance + * @param label Input domain name label + * @param length Label length, or -1 if NUL-terminated + * @param dest Destination string buffer + * @param capacity Destination buffer capacity + * @param pInfo Output container of IDNA processing details. + * @param pErrorCode Standard 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.) + * @return destination string length + * @stable ICU 4.6 + */ +U_CAPI int32_t U_EXPORT2 +uidna_labelToASCII(const UIDNA *idna, + const UChar *label, int32_t length, + UChar *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode); + +/** + * Converts a single domain name label into its Unicode form for human-readable display. + * If any processing step fails, then pInfo->errors will be non-zero. + * The label might be modified according to the types of errors. + * + * The UErrorCode indicates an error only in exceptional cases, + * such as a U_MEMORY_ALLOCATION_ERROR. + * + * @param idna UIDNA instance + * @param label Input domain name label + * @param length Label length, or -1 if NUL-terminated + * @param dest Destination string buffer + * @param capacity Destination buffer capacity + * @param pInfo Output container of IDNA processing details. + * @param pErrorCode Standard 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.) + * @return destination string length + * @stable ICU 4.6 + */ +U_CAPI int32_t U_EXPORT2 +uidna_labelToUnicode(const UIDNA *idna, + const UChar *label, int32_t length, + UChar *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode); + +/** + * Converts a whole domain name into its ASCII form for DNS lookup. + * If any processing step fails, then pInfo->errors will be non-zero and + * the result might not be an ASCII string. + * The domain name might be modified according to the types of errors. + * Labels with severe errors will be left in (or turned into) their Unicode form. + * + * The UErrorCode indicates an error only in exceptional cases, + * such as a U_MEMORY_ALLOCATION_ERROR. + * + * @param idna UIDNA instance + * @param name Input domain name + * @param length Domain name length, or -1 if NUL-terminated + * @param dest Destination string buffer + * @param capacity Destination buffer capacity + * @param pInfo Output container of IDNA processing details. + * @param pErrorCode Standard 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.) + * @return destination string length + * @stable ICU 4.6 + */ +U_CAPI int32_t U_EXPORT2 +uidna_nameToASCII(const UIDNA *idna, + const UChar *name, int32_t length, + UChar *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode); + +/** + * Converts a whole domain name into its Unicode form for human-readable display. + * If any processing step fails, then pInfo->errors will be non-zero. + * The domain name might be modified according to the types of errors. + * + * The UErrorCode indicates an error only in exceptional cases, + * such as a U_MEMORY_ALLOCATION_ERROR. + * + * @param idna UIDNA instance + * @param name Input domain name + * @param length Domain name length, or -1 if NUL-terminated + * @param dest Destination string buffer + * @param capacity Destination buffer capacity + * @param pInfo Output container of IDNA processing details. + * @param pErrorCode Standard 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.) + * @return destination string length + * @stable ICU 4.6 + */ +U_CAPI int32_t U_EXPORT2 +uidna_nameToUnicode(const UIDNA *idna, + const UChar *name, int32_t length, + UChar *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode); + +/* UTF-8 versions of the processing methods --------------------------------- */ + +/** + * Converts a single domain name label into its ASCII form for DNS lookup. + * UTF-8 version of uidna_labelToASCII(), same behavior. + * + * @param idna UIDNA instance + * @param label Input domain name label + * @param length Label length, or -1 if NUL-terminated + * @param dest Destination string buffer + * @param capacity Destination buffer capacity + * @param pInfo Output container of IDNA processing details. + * @param pErrorCode Standard 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.) + * @return destination string length + * @stable ICU 4.6 + */ +U_CAPI int32_t U_EXPORT2 +uidna_labelToASCII_UTF8(const UIDNA *idna, + const char *label, int32_t length, + char *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode); + +/** + * Converts a single domain name label into its Unicode form for human-readable display. + * UTF-8 version of uidna_labelToUnicode(), same behavior. + * + * @param idna UIDNA instance + * @param label Input domain name label + * @param length Label length, or -1 if NUL-terminated + * @param dest Destination string buffer + * @param capacity Destination buffer capacity + * @param pInfo Output container of IDNA processing details. + * @param pErrorCode Standard 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.) + * @return destination string length + * @stable ICU 4.6 + */ +U_CAPI int32_t U_EXPORT2 +uidna_labelToUnicodeUTF8(const UIDNA *idna, + const char *label, int32_t length, + char *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode); + +/** + * Converts a whole domain name into its ASCII form for DNS lookup. + * UTF-8 version of uidna_nameToASCII(), same behavior. + * + * @param idna UIDNA instance + * @param name Input domain name + * @param length Domain name length, or -1 if NUL-terminated + * @param dest Destination string buffer + * @param capacity Destination buffer capacity + * @param pInfo Output container of IDNA processing details. + * @param pErrorCode Standard 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.) + * @return destination string length + * @stable ICU 4.6 + */ +U_CAPI int32_t U_EXPORT2 +uidna_nameToASCII_UTF8(const UIDNA *idna, + const char *name, int32_t length, + char *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode); + +/** + * Converts a whole domain name into its Unicode form for human-readable display. + * UTF-8 version of uidna_nameToUnicode(), same behavior. + * + * @param idna UIDNA instance + * @param name Input domain name + * @param length Domain name length, or -1 if NUL-terminated + * @param dest Destination string buffer + * @param capacity Destination buffer capacity + * @param pInfo Output container of IDNA processing details. + * @param pErrorCode Standard 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.) + * @return destination string length + * @stable ICU 4.6 + */ +U_CAPI int32_t U_EXPORT2 +uidna_nameToUnicodeUTF8(const UIDNA *idna, + const char *name, int32_t length, + char *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode); + +/* + * IDNA error bit set values. + * When a domain name or label fails a processing step or does not meet the + * validity criteria, then one or more of these error bits are set. + */ +enum { + /** + * A non-final domain name label (or the whole domain name) is empty. + * @stable ICU 4.6 + */ + UIDNA_ERROR_EMPTY_LABEL=1, + /** + * A domain name label is longer than 63 bytes. + * (See STD13/RFC1034 3.1. Name space specifications and terminology.) + * This is only checked in ToASCII operations, and only if the output label is all-ASCII. + * @stable ICU 4.6 + */ + UIDNA_ERROR_LABEL_TOO_LONG=2, + /** + * A domain name is longer than 255 bytes in its storage form. + * (See STD13/RFC1034 3.1. Name space specifications and terminology.) + * This is only checked in ToASCII operations, and only if the output domain name is all-ASCII. + * @stable ICU 4.6 + */ + UIDNA_ERROR_DOMAIN_NAME_TOO_LONG=4, + /** + * A label starts with a hyphen-minus ('-'). + * @stable ICU 4.6 + */ + UIDNA_ERROR_LEADING_HYPHEN=8, + /** + * A label ends with a hyphen-minus ('-'). + * @stable ICU 4.6 + */ + UIDNA_ERROR_TRAILING_HYPHEN=0x10, + /** + * A label contains hyphen-minus ('-') in the third and fourth positions. + * @stable ICU 4.6 + */ + UIDNA_ERROR_HYPHEN_3_4=0x20, + /** + * A label starts with a combining mark. + * @stable ICU 4.6 + */ + UIDNA_ERROR_LEADING_COMBINING_MARK=0x40, + /** + * A label or domain name contains disallowed characters. + * @stable ICU 4.6 + */ + UIDNA_ERROR_DISALLOWED=0x80, + /** + * A label starts with "xn--" but does not contain valid Punycode. + * That is, an xn-- label failed Punycode decoding. + * @stable ICU 4.6 + */ + UIDNA_ERROR_PUNYCODE=0x100, + /** + * A label contains a dot=full stop. + * This can occur in an input string for a single-label function. + * @stable ICU 4.6 + */ + UIDNA_ERROR_LABEL_HAS_DOT=0x200, + /** + * An ACE label does not contain a valid label string. + * The label was successfully ACE (Punycode) decoded but the resulting + * string had severe validation errors. For example, + * it might contain characters that are not allowed in ACE labels, + * or it might not be normalized. + * @stable ICU 4.6 + */ + UIDNA_ERROR_INVALID_ACE_LABEL=0x400, + /** + * A label does not meet the IDNA BiDi requirements (for right-to-left characters). + * @stable ICU 4.6 + */ + UIDNA_ERROR_BIDI=0x800, + /** + * A label does not meet the IDNA CONTEXTJ requirements. + * @stable ICU 4.6 + */ + UIDNA_ERROR_CONTEXTJ=0x1000, + /** + * A label does not meet the IDNA CONTEXTO requirements for punctuation characters. + * Some punctuation characters "Would otherwise have been DISALLOWED" + * but are allowed in certain contexts. (RFC 5892) + * @stable ICU 49 + */ + UIDNA_ERROR_CONTEXTO_PUNCTUATION=0x2000, + /** + * A label does not meet the IDNA CONTEXTO requirements for digits. + * Arabic-Indic Digits (U+066x) must not be mixed with Extended Arabic-Indic Digits (U+06Fx). + * @stable ICU 49 + */ + UIDNA_ERROR_CONTEXTO_DIGITS=0x4000 +}; + +#ifndef U_HIDE_DEPRECATED_API + +/* IDNA2003 API ------------------------------------------------------------- */ + +/** + * IDNA2003: This function implements the ToASCII operation as defined in the IDNA RFC. + * This operation is done on single labels before sending it to something that expects + * ASCII names. A label is an individual part of a domain name. Labels are usually + * separated by dots; e.g. "www.example.com" is composed of 3 labels "www","example", and "com". + * + * IDNA2003 API Overview: + * + * The uidna_ API implements the IDNA protocol as defined in the IDNA RFC + * (http://www.ietf.org/rfc/rfc3490.txt). + * The RFC defines 2 operations: ToASCII and ToUnicode. Domain name labels + * containing non-ASCII code points are processed by the + * ToASCII operation before passing it to resolver libraries. Domain names + * that are obtained from resolver libraries are processed by the + * ToUnicode operation before displaying the domain name to the user. + * IDNA requires that implementations process input strings with Nameprep + * (http://www.ietf.org/rfc/rfc3491.txt), + * which is a profile of Stringprep (http://www.ietf.org/rfc/rfc3454.txt), + * and then with Punycode (http://www.ietf.org/rfc/rfc3492.txt). + * Implementations of IDNA MUST fully implement Nameprep and Punycode; + * neither Nameprep nor Punycode are optional. + * The input and output of ToASCII and ToUnicode operations are Unicode + * and are designed to be chainable, i.e., applying ToASCII or ToUnicode operations + * multiple times to an input string will yield the same result as applying the operation + * once. + * ToUnicode(ToUnicode(ToUnicode...(ToUnicode(string)))) == ToUnicode(string) + * ToASCII(ToASCII(ToASCII...(ToASCII(string))) == ToASCII(string). + * + * @param src Input UChar array containing label in Unicode. + * @param srcLength Number of UChars in src, or -1 if NUL-terminated. + * @param dest Output UChar array with ASCII (ACE encoded) label. + * @param destCapacity Size of dest. + * @param options A bit set of options: + * + * - UIDNA_DEFAULT Use default options, i.e., do not process unassigned code points + * and do not use STD3 ASCII rules + * If unassigned code points are found the operation fails with + * U_UNASSIGNED_ERROR error code. + * + * - UIDNA_ALLOW_UNASSIGNED Unassigned values can be converted to ASCII for query operations + * If this option is set, the unassigned code points are in the input + * are treated as normal Unicode code points. + * + * - UIDNA_USE_STD3_RULES Use STD3 ASCII rules for host name syntax restrictions + * If this option is set and the input does not satisfy STD3 rules, + * the operation will fail with U_IDNA_STD3_ASCII_RULES_ERROR + * + * @param parseError Pointer to UParseError struct to receive information on position + * of error if an error is encountered. Can be NULL. + * @param status ICU in/out error code parameter. + * U_INVALID_CHAR_FOUND if src contains + * unmatched single surrogates. + * U_INDEX_OUTOFBOUNDS_ERROR if src contains + * too many code points. + * U_BUFFER_OVERFLOW_ERROR if destCapacity is not enough + * @return The length of the result string, if successful - or in case of a buffer overflow, + * in which case it will be greater than destCapacity. + * @deprecated ICU 55 Use UTS #46 instead via uidna_openUTS46() or class IDNA. + */ +U_DEPRECATED int32_t U_EXPORT2 +uidna_toASCII(const UChar* src, int32_t srcLength, + UChar* dest, int32_t destCapacity, + int32_t options, + UParseError* parseError, + UErrorCode* status); + + +/** + * IDNA2003: This function implements the ToUnicode operation as defined in the IDNA RFC. + * This operation is done on single labels before sending it to something that expects + * Unicode names. A label is an individual part of a domain name. Labels are usually + * separated by dots; for e.g. "www.example.com" is composed of 3 labels "www","example", and "com". + * + * @param src Input UChar array containing ASCII (ACE encoded) label. + * @param srcLength Number of UChars in src, or -1 if NUL-terminated. + * @param dest Output Converted UChar array containing Unicode equivalent of label. + * @param destCapacity Size of dest. + * @param options A bit set of options: + * + * - UIDNA_DEFAULT Use default options, i.e., do not process unassigned code points + * and do not use STD3 ASCII rules + * If unassigned code points are found the operation fails with + * U_UNASSIGNED_ERROR error code. + * + * - UIDNA_ALLOW_UNASSIGNED Unassigned values can be converted to ASCII for query operations + * If this option is set, the unassigned code points are in the input + * are treated as normal Unicode code points. Note: This option is + * required on toUnicode operation because the RFC mandates + * verification of decoded ACE input by applying toASCII and comparing + * its output with source + * + * - UIDNA_USE_STD3_RULES Use STD3 ASCII rules for host name syntax restrictions + * If this option is set and the input does not satisfy STD3 rules, + * the operation will fail with U_IDNA_STD3_ASCII_RULES_ERROR + * + * @param parseError Pointer to UParseError struct to receive information on position + * of error if an error is encountered. Can be NULL. + * @param status ICU in/out error code parameter. + * U_INVALID_CHAR_FOUND if src contains + * unmatched single surrogates. + * U_INDEX_OUTOFBOUNDS_ERROR if src contains + * too many code points. + * U_BUFFER_OVERFLOW_ERROR if destCapacity is not enough + * @return The length of the result string, if successful - or in case of a buffer overflow, + * in which case it will be greater than destCapacity. + * @deprecated ICU 55 Use UTS #46 instead via uidna_openUTS46() or class IDNA. + */ +U_DEPRECATED int32_t U_EXPORT2 +uidna_toUnicode(const UChar* src, int32_t srcLength, + UChar* dest, int32_t destCapacity, + int32_t options, + UParseError* parseError, + UErrorCode* status); + + +/** + * IDNA2003: Convenience function that implements the IDNToASCII operation as defined in the IDNA RFC. + * This operation is done on complete domain names, e.g: "www.example.com". + * It is important to note that this operation can fail. If it fails, then the input + * domain name cannot be used as an Internationalized Domain Name and the application + * should have methods defined to deal with the failure. + * + * Note: IDNA RFC specifies that a conformant application should divide a domain name + * into separate labels, decide whether to apply allowUnassigned and useSTD3ASCIIRules on each, + * and then convert. This function does not offer that level of granularity. The options once + * set will apply to all labels in the domain name + * + * @param src Input UChar array containing IDN in Unicode. + * @param srcLength Number of UChars in src, or -1 if NUL-terminated. + * @param dest Output UChar array with ASCII (ACE encoded) IDN. + * @param destCapacity Size of dest. + * @param options A bit set of options: + * + * - UIDNA_DEFAULT Use default options, i.e., do not process unassigned code points + * and do not use STD3 ASCII rules + * If unassigned code points are found the operation fails with + * U_UNASSIGNED_CODE_POINT_FOUND error code. + * + * - UIDNA_ALLOW_UNASSIGNED Unassigned values can be converted to ASCII for query operations + * If this option is set, the unassigned code points are in the input + * are treated as normal Unicode code points. + * + * - UIDNA_USE_STD3_RULES Use STD3 ASCII rules for host name syntax restrictions + * If this option is set and the input does not satisfy STD3 rules, + * the operation will fail with U_IDNA_STD3_ASCII_RULES_ERROR + * + * @param parseError Pointer to UParseError struct to receive information on position + * of error if an error is encountered. Can be NULL. + * @param status ICU in/out error code parameter. + * U_INVALID_CHAR_FOUND if src contains + * unmatched single surrogates. + * U_INDEX_OUTOFBOUNDS_ERROR if src contains + * too many code points. + * U_BUFFER_OVERFLOW_ERROR if destCapacity is not enough + * @return The length of the result string, if successful - or in case of a buffer overflow, + * in which case it will be greater than destCapacity. + * @deprecated ICU 55 Use UTS #46 instead via uidna_openUTS46() or class IDNA. + */ +U_DEPRECATED int32_t U_EXPORT2 +uidna_IDNToASCII( const UChar* src, int32_t srcLength, + UChar* dest, int32_t destCapacity, + int32_t options, + UParseError* parseError, + UErrorCode* status); + +/** + * IDNA2003: Convenience function that implements the IDNToUnicode operation as defined in the IDNA RFC. + * This operation is done on complete domain names, e.g: "www.example.com". + * + * Note: IDNA RFC specifies that a conformant application should divide a domain name + * into separate labels, decide whether to apply allowUnassigned and useSTD3ASCIIRules on each, + * and then convert. This function does not offer that level of granularity. The options once + * set will apply to all labels in the domain name + * + * @param src Input UChar array containing IDN in ASCII (ACE encoded) form. + * @param srcLength Number of UChars in src, or -1 if NUL-terminated. + * @param dest Output UChar array containing Unicode equivalent of source IDN. + * @param destCapacity Size of dest. + * @param options A bit set of options: + * + * - UIDNA_DEFAULT Use default options, i.e., do not process unassigned code points + * and do not use STD3 ASCII rules + * If unassigned code points are found the operation fails with + * U_UNASSIGNED_CODE_POINT_FOUND error code. + * + * - UIDNA_ALLOW_UNASSIGNED Unassigned values can be converted to ASCII for query operations + * If this option is set, the unassigned code points are in the input + * are treated as normal Unicode code points. + * + * - UIDNA_USE_STD3_RULES Use STD3 ASCII rules for host name syntax restrictions + * If this option is set and the input does not satisfy STD3 rules, + * the operation will fail with U_IDNA_STD3_ASCII_RULES_ERROR + * + * @param parseError Pointer to UParseError struct to receive information on position + * of error if an error is encountered. Can be NULL. + * @param status ICU in/out error code parameter. + * U_INVALID_CHAR_FOUND if src contains + * unmatched single surrogates. + * U_INDEX_OUTOFBOUNDS_ERROR if src contains + * too many code points. + * U_BUFFER_OVERFLOW_ERROR if destCapacity is not enough + * @return The length of the result string, if successful - or in case of a buffer overflow, + * in which case it will be greater than destCapacity. + * @deprecated ICU 55 Use UTS #46 instead via uidna_openUTS46() or class IDNA. + */ +U_DEPRECATED int32_t U_EXPORT2 +uidna_IDNToUnicode( const UChar* src, int32_t srcLength, + UChar* dest, int32_t destCapacity, + int32_t options, + UParseError* parseError, + UErrorCode* status); + +/** + * IDNA2003: Compare two IDN strings for equivalence. + * This function splits the domain names into labels and compares them. + * According to IDN RFC, whenever two labels are compared, they are + * considered equal if and only if their ASCII forms (obtained by + * applying toASCII) match using an case-insensitive ASCII comparison. + * Two domain names are considered a match if and only if all labels + * match regardless of whether label separators match. + * + * @param s1 First source string. + * @param length1 Length of first source string, or -1 if NUL-terminated. + * + * @param s2 Second source string. + * @param length2 Length of second source string, or -1 if NUL-terminated. + * @param options A bit set of options: + * + * - UIDNA_DEFAULT Use default options, i.e., do not process unassigned code points + * and do not use STD3 ASCII rules + * If unassigned code points are found the operation fails with + * U_UNASSIGNED_CODE_POINT_FOUND error code. + * + * - UIDNA_ALLOW_UNASSIGNED Unassigned values can be converted to ASCII for query operations + * If this option is set, the unassigned code points are in the input + * are treated as normal Unicode code points. + * + * - UIDNA_USE_STD3_RULES Use STD3 ASCII rules for host name syntax restrictions + * If this option is set and the input does not satisfy STD3 rules, + * the operation will fail with U_IDNA_STD3_ASCII_RULES_ERROR + * + * @param status ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return <0 or 0 or >0 as usual for string comparisons + * @deprecated ICU 55 Use UTS #46 instead via uidna_openUTS46() or class IDNA. + */ +U_DEPRECATED int32_t U_EXPORT2 +uidna_compare( const UChar *s1, int32_t length1, + const UChar *s2, int32_t length2, + int32_t options, + UErrorCode* status); + +#endif /* U_HIDE_DEPRECATED_API */ + +#endif /* #if !UCONFIG_NO_IDNA */ + +#endif diff --git a/deps/icu-small/source/common/unicode/uiter.h b/deps/icu-small/source/common/unicode/uiter.h index be232c774dc614..84913a74e29b10 100644 --- a/deps/icu-small/source/common/unicode/uiter.h +++ b/deps/icu-small/source/common/unicode/uiter.h @@ -1,709 +1,709 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2011 International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uiter.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002jan18 -* created by: Markus W. Scherer -*/ - -#ifndef __UITER_H__ -#define __UITER_H__ - -/** - * \file - * \brief C API: Unicode Character Iteration - * - * @see UCharIterator - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - U_NAMESPACE_BEGIN - - class CharacterIterator; - class Replaceable; - - U_NAMESPACE_END -#endif - -U_CDECL_BEGIN - -struct UCharIterator; -typedef struct UCharIterator UCharIterator; /**< C typedef for struct UCharIterator. @stable ICU 2.1 */ - -/** - * Origin constants for UCharIterator.getIndex() and UCharIterator.move(). - * @see UCharIteratorMove - * @see UCharIterator - * @stable ICU 2.1 - */ -typedef enum UCharIteratorOrigin { - UITER_START, UITER_CURRENT, UITER_LIMIT, UITER_ZERO, UITER_LENGTH -} UCharIteratorOrigin; - -/** Constants for UCharIterator. @stable ICU 2.6 */ -enum { - /** - * Constant value that may be returned by UCharIteratorMove - * indicating that the final UTF-16 index is not known, but that the move succeeded. - * This can occur when moving relative to limit or length, or - * when moving relative to the current index after a setState() - * when the current UTF-16 index is not known. - * - * It would be very inefficient to have to count from the beginning of the text - * just to get the current/limit/length index after moving relative to it. - * The actual index can be determined with getIndex(UITER_CURRENT) - * which will count the UChars if necessary. - * - * @stable ICU 2.6 - */ - UITER_UNKNOWN_INDEX=-2 -}; - - -/** - * Constant for UCharIterator getState() indicating an error or - * an unknown state. - * Returned by uiter_getState()/UCharIteratorGetState - * when an error occurs. - * Also, some UCharIterator implementations may not be able to return - * a valid state for each position. This will be clearly documented - * for each such iterator (none of the public ones here). - * - * @stable ICU 2.6 - */ -#define UITER_NO_STATE ((uint32_t)0xffffffff) - -/** - * Function type declaration for UCharIterator.getIndex(). - * - * Gets the current position, or the start or limit of the - * iteration range. - * - * This function may perform slowly for UITER_CURRENT after setState() was called, - * or for UITER_LENGTH, because an iterator implementation may have to count - * UChars if the underlying storage is not UTF-16. - * - * @param iter the UCharIterator structure ("this pointer") - * @param origin get the 0, start, limit, length, or current index - * @return the requested index, or U_SENTINEL in an error condition - * - * @see UCharIteratorOrigin - * @see UCharIterator - * @stable ICU 2.1 - */ -typedef int32_t U_CALLCONV -UCharIteratorGetIndex(UCharIterator *iter, UCharIteratorOrigin origin); - -/** - * Function type declaration for UCharIterator.move(). - * - * Use iter->move(iter, index, UITER_ZERO) like CharacterIterator::setIndex(index). - * - * Moves the current position relative to the start or limit of the - * iteration range, or relative to the current position itself. - * The movement is expressed in numbers of code units forward - * or backward by specifying a positive or negative delta. - * Out of bounds movement will be pinned to the start or limit. - * - * This function may perform slowly for moving relative to UITER_LENGTH - * because an iterator implementation may have to count the rest of the - * UChars if the native storage is not UTF-16. - * - * When moving relative to the limit or length, or - * relative to the current position after setState() was called, - * move() may return UITER_UNKNOWN_INDEX (-2) to avoid an inefficient - * determination of the actual UTF-16 index. - * The actual index can be determined with getIndex(UITER_CURRENT) - * which will count the UChars if necessary. - * See UITER_UNKNOWN_INDEX for details. - * - * @param iter the UCharIterator structure ("this pointer") - * @param delta can be positive, zero, or negative - * @param origin move relative to the 0, start, limit, length, or current index - * @return the new index, or U_SENTINEL on an error condition, - * or UITER_UNKNOWN_INDEX when the index is not known. - * - * @see UCharIteratorOrigin - * @see UCharIterator - * @see UITER_UNKNOWN_INDEX - * @stable ICU 2.1 - */ -typedef int32_t U_CALLCONV -UCharIteratorMove(UCharIterator *iter, int32_t delta, UCharIteratorOrigin origin); - -/** - * Function type declaration for UCharIterator.hasNext(). - * - * Check if current() and next() can still - * return another code unit. - * - * @param iter the UCharIterator structure ("this pointer") - * @return boolean value for whether current() and next() can still return another code unit - * - * @see UCharIterator - * @stable ICU 2.1 - */ -typedef UBool U_CALLCONV -UCharIteratorHasNext(UCharIterator *iter); - -/** - * Function type declaration for UCharIterator.hasPrevious(). - * - * Check if previous() can still return another code unit. - * - * @param iter the UCharIterator structure ("this pointer") - * @return boolean value for whether previous() can still return another code unit - * - * @see UCharIterator - * @stable ICU 2.1 - */ -typedef UBool U_CALLCONV -UCharIteratorHasPrevious(UCharIterator *iter); - -/** - * Function type declaration for UCharIterator.current(). - * - * Return the code unit at the current position, - * or U_SENTINEL if there is none (index is at the limit). - * - * @param iter the UCharIterator structure ("this pointer") - * @return the current code unit - * - * @see UCharIterator - * @stable ICU 2.1 - */ -typedef UChar32 U_CALLCONV -UCharIteratorCurrent(UCharIterator *iter); - -/** - * Function type declaration for UCharIterator.next(). - * - * Return the code unit at the current index and increment - * the index (post-increment, like s[i++]), - * or return U_SENTINEL if there is none (index is at the limit). - * - * @param iter the UCharIterator structure ("this pointer") - * @return the current code unit (and post-increment the current index) - * - * @see UCharIterator - * @stable ICU 2.1 - */ -typedef UChar32 U_CALLCONV -UCharIteratorNext(UCharIterator *iter); - -/** - * Function type declaration for UCharIterator.previous(). - * - * Decrement the index and return the code unit from there - * (pre-decrement, like s[--i]), - * or return U_SENTINEL if there is none (index is at the start). - * - * @param iter the UCharIterator structure ("this pointer") - * @return the previous code unit (after pre-decrementing the current index) - * - * @see UCharIterator - * @stable ICU 2.1 - */ -typedef UChar32 U_CALLCONV -UCharIteratorPrevious(UCharIterator *iter); - -/** - * Function type declaration for UCharIterator.reservedFn(). - * Reserved for future use. - * - * @param iter the UCharIterator structure ("this pointer") - * @param something some integer argument - * @return some integer - * - * @see UCharIterator - * @stable ICU 2.1 - */ -typedef int32_t U_CALLCONV -UCharIteratorReserved(UCharIterator *iter, int32_t something); - -/** - * Function type declaration for UCharIterator.getState(). - * - * Get the "state" of the iterator in the form of a single 32-bit word. - * It is recommended that the state value be calculated to be as small as - * is feasible. For strings with limited lengths, fewer than 32 bits may - * be sufficient. - * - * This is used together with setState()/UCharIteratorSetState - * to save and restore the iterator position more efficiently than with - * getIndex()/move(). - * - * The iterator state is defined as a uint32_t value because it is designed - * for use in ucol_nextSortKeyPart() which provides 32 bits to store the state - * of the character iterator. - * - * With some UCharIterator implementations (e.g., UTF-8), - * getting and setting the UTF-16 index with existing functions - * (getIndex(UITER_CURRENT) followed by move(pos, UITER_ZERO)) is possible but - * relatively slow because the iterator has to "walk" from a known index - * to the requested one. - * This takes more time the farther it needs to go. - * - * An opaque state value allows an iterator implementation to provide - * an internal index (UTF-8: the source byte array index) for - * fast, constant-time restoration. - * - * After calling setState(), a getIndex(UITER_CURRENT) may be slow because - * the UTF-16 index may not be restored as well, but the iterator can deliver - * the correct text contents and move relative to the current position - * without performance degradation. - * - * Some UCharIterator implementations may not be able to return - * a valid state for each position, in which case they return UITER_NO_STATE instead. - * This will be clearly documented for each such iterator (none of the public ones here). - * - * @param iter the UCharIterator structure ("this pointer") - * @return the state word - * - * @see UCharIterator - * @see UCharIteratorSetState - * @see UITER_NO_STATE - * @stable ICU 2.6 - */ -typedef uint32_t U_CALLCONV -UCharIteratorGetState(const UCharIterator *iter); - -/** - * Function type declaration for UCharIterator.setState(). - * - * Restore the "state" of the iterator using a state word from a getState() call. - * The iterator object need not be the same one as for which getState() was called, - * but it must be of the same type (set up using the same uiter_setXYZ function) - * and it must iterate over the same string - * (binary identical regardless of memory address). - * For more about the state word see UCharIteratorGetState. - * - * After calling setState(), a getIndex(UITER_CURRENT) may be slow because - * the UTF-16 index may not be restored as well, but the iterator can deliver - * the correct text contents and move relative to the current position - * without performance degradation. - * - * @param iter the UCharIterator structure ("this pointer") - * @param state the state word from a getState() call - * on a same-type, same-string iterator - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * - * @see UCharIterator - * @see UCharIteratorGetState - * @stable ICU 2.6 - */ -typedef void U_CALLCONV -UCharIteratorSetState(UCharIterator *iter, uint32_t state, UErrorCode *pErrorCode); - - -/** - * C API for code unit iteration. - * This can be used as a C wrapper around - * CharacterIterator, Replaceable, or implemented using simple strings, etc. - * - * There are two roles for using UCharIterator: - * - * A "provider" sets the necessary function pointers and controls the "protected" - * fields of the UCharIterator structure. A "provider" passes a UCharIterator - * into C APIs that need a UCharIterator as an abstract, flexible string interface. - * - * Implementations of such C APIs are "callers" of UCharIterator functions; - * they only use the "public" function pointers and never access the "protected" - * fields directly. - * - * The current() and next() functions only check the current index against the - * limit, and previous() only checks the current index against the start, - * to see if the iterator already reached the end of the iteration range. - * - * The assumption - in all iterators - is that the index is moved via the API, - * which means it won't go out of bounds, or the index is modified by - * user code that knows enough about the iterator implementation to set valid - * index values. - * - * UCharIterator functions return code unit values 0..0xffff, - * or U_SENTINEL if the iteration bounds are reached. - * - * @stable ICU 2.1 - */ -struct UCharIterator { - /** - * (protected) Pointer to string or wrapped object or similar. - * Not used by caller. - * @stable ICU 2.1 - */ - const void *context; - - /** - * (protected) Length of string or similar. - * Not used by caller. - * @stable ICU 2.1 - */ - int32_t length; - - /** - * (protected) Start index or similar. - * Not used by caller. - * @stable ICU 2.1 - */ - int32_t start; - - /** - * (protected) Current index or similar. - * Not used by caller. - * @stable ICU 2.1 - */ - int32_t index; - - /** - * (protected) Limit index or similar. - * Not used by caller. - * @stable ICU 2.1 - */ - int32_t limit; - - /** - * (protected) Used by UTF-8 iterators and possibly others. - * @stable ICU 2.1 - */ - int32_t reservedField; - - /** - * (public) Returns the current position or the - * start or limit index of the iteration range. - * - * @see UCharIteratorGetIndex - * @stable ICU 2.1 - */ - UCharIteratorGetIndex *getIndex; - - /** - * (public) Moves the current position relative to the start or limit of the - * iteration range, or relative to the current position itself. - * The movement is expressed in numbers of code units forward - * or backward by specifying a positive or negative delta. - * - * @see UCharIteratorMove - * @stable ICU 2.1 - */ - UCharIteratorMove *move; - - /** - * (public) Check if current() and next() can still - * return another code unit. - * - * @see UCharIteratorHasNext - * @stable ICU 2.1 - */ - UCharIteratorHasNext *hasNext; - - /** - * (public) Check if previous() can still return another code unit. - * - * @see UCharIteratorHasPrevious - * @stable ICU 2.1 - */ - UCharIteratorHasPrevious *hasPrevious; - - /** - * (public) Return the code unit at the current position, - * or U_SENTINEL if there is none (index is at the limit). - * - * @see UCharIteratorCurrent - * @stable ICU 2.1 - */ - UCharIteratorCurrent *current; - - /** - * (public) Return the code unit at the current index and increment - * the index (post-increment, like s[i++]), - * or return U_SENTINEL if there is none (index is at the limit). - * - * @see UCharIteratorNext - * @stable ICU 2.1 - */ - UCharIteratorNext *next; - - /** - * (public) Decrement the index and return the code unit from there - * (pre-decrement, like s[--i]), - * or return U_SENTINEL if there is none (index is at the start). - * - * @see UCharIteratorPrevious - * @stable ICU 2.1 - */ - UCharIteratorPrevious *previous; - - /** - * (public) Reserved for future use. Currently NULL. - * - * @see UCharIteratorReserved - * @stable ICU 2.1 - */ - UCharIteratorReserved *reservedFn; - - /** - * (public) Return the state of the iterator, to be restored later with setState(). - * This function pointer is NULL if the iterator does not implement it. - * - * @see UCharIteratorGet - * @stable ICU 2.6 - */ - UCharIteratorGetState *getState; - - /** - * (public) Restore the iterator state from the state word from a call - * to getState(). - * This function pointer is NULL if the iterator does not implement it. - * - * @see UCharIteratorSet - * @stable ICU 2.6 - */ - UCharIteratorSetState *setState; -}; - -/** - * Helper function for UCharIterator to get the code point - * at the current index. - * - * Return the code point that includes the code unit at the current position, - * or U_SENTINEL if there is none (index is at the limit). - * If the current code unit is a lead or trail surrogate, - * then the following or preceding surrogate is used to form - * the code point value. - * - * @param iter the UCharIterator structure ("this pointer") - * @return the current code point - * - * @see UCharIterator - * @see U16_GET - * @see UnicodeString::char32At() - * @stable ICU 2.1 - */ -U_CAPI UChar32 U_EXPORT2 -uiter_current32(UCharIterator *iter); - -/** - * Helper function for UCharIterator to get the next code point. - * - * Return the code point at the current index and increment - * the index (post-increment, like s[i++]), - * or return U_SENTINEL if there is none (index is at the limit). - * - * @param iter the UCharIterator structure ("this pointer") - * @return the current code point (and post-increment the current index) - * - * @see UCharIterator - * @see U16_NEXT - * @stable ICU 2.1 - */ -U_CAPI UChar32 U_EXPORT2 -uiter_next32(UCharIterator *iter); - -/** - * Helper function for UCharIterator to get the previous code point. - * - * Decrement the index and return the code point from there - * (pre-decrement, like s[--i]), - * or return U_SENTINEL if there is none (index is at the start). - * - * @param iter the UCharIterator structure ("this pointer") - * @return the previous code point (after pre-decrementing the current index) - * - * @see UCharIterator - * @see U16_PREV - * @stable ICU 2.1 - */ -U_CAPI UChar32 U_EXPORT2 -uiter_previous32(UCharIterator *iter); - -/** - * Get the "state" of the iterator in the form of a single 32-bit word. - * This is a convenience function that calls iter->getState(iter) - * if iter->getState is not NULL; - * if it is NULL or any other error occurs, then UITER_NO_STATE is returned. - * - * Some UCharIterator implementations may not be able to return - * a valid state for each position, in which case they return UITER_NO_STATE instead. - * This will be clearly documented for each such iterator (none of the public ones here). - * - * @param iter the UCharIterator structure ("this pointer") - * @return the state word - * - * @see UCharIterator - * @see UCharIteratorGetState - * @see UITER_NO_STATE - * @stable ICU 2.6 - */ -U_CAPI uint32_t U_EXPORT2 -uiter_getState(const UCharIterator *iter); - -/** - * Restore the "state" of the iterator using a state word from a getState() call. - * This is a convenience function that calls iter->setState(iter, state, pErrorCode) - * if iter->setState is not NULL; if it is NULL, then U_UNSUPPORTED_ERROR is set. - * - * @param iter the UCharIterator structure ("this pointer") - * @param state the state word from a getState() call - * on a same-type, same-string iterator - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * - * @see UCharIterator - * @see UCharIteratorSetState - * @stable ICU 2.6 - */ -U_CAPI void U_EXPORT2 -uiter_setState(UCharIterator *iter, uint32_t state, UErrorCode *pErrorCode); - -/** - * Set up a UCharIterator to iterate over a string. - * - * Sets the UCharIterator function pointers for iteration over the string s - * with iteration boundaries start=index=0 and length=limit=string length. - * The "provider" may set the start, index, and limit values at any time - * within the range 0..length. - * The length field will be ignored. - * - * The string pointer s is set into UCharIterator.context without copying - * or reallocating the string contents. - * - * getState() simply returns the current index. - * move() will always return the final index. - * - * @param iter UCharIterator structure to be set for iteration - * @param s String to iterate over - * @param length Length of s, or -1 if NUL-terminated - * - * @see UCharIterator - * @stable ICU 2.1 - */ -U_CAPI void U_EXPORT2 -uiter_setString(UCharIterator *iter, const UChar *s, int32_t length); - -/** - * Set up a UCharIterator to iterate over a UTF-16BE string - * (byte vector with a big-endian pair of bytes per UChar). - * - * Everything works just like with a normal UChar iterator (uiter_setString), - * except that UChars are assembled from byte pairs, - * and that the length argument here indicates an even number of bytes. - * - * getState() simply returns the current index. - * move() will always return the final index. - * - * @param iter UCharIterator structure to be set for iteration - * @param s UTF-16BE string to iterate over - * @param length Length of s as an even number of bytes, or -1 if NUL-terminated - * (NUL means pair of 0 bytes at even index from s) - * - * @see UCharIterator - * @see uiter_setString - * @stable ICU 2.6 - */ -U_CAPI void U_EXPORT2 -uiter_setUTF16BE(UCharIterator *iter, const char *s, int32_t length); - -/** - * Set up a UCharIterator to iterate over a UTF-8 string. - * - * Sets the UCharIterator function pointers for iteration over the UTF-8 string s - * with UTF-8 iteration boundaries 0 and length. - * The implementation counts the UTF-16 index on the fly and - * lazily evaluates the UTF-16 length of the text. - * - * The start field is used as the UTF-8 offset, the limit field as the UTF-8 length. - * When the reservedField is not 0, then it contains a supplementary code point - * and the UTF-16 index is between the two corresponding surrogates. - * At that point, the UTF-8 index is behind that code point. - * - * The UTF-8 string pointer s is set into UCharIterator.context without copying - * or reallocating the string contents. - * - * getState() returns a state value consisting of - * - the current UTF-8 source byte index (bits 31..1) - * - a flag (bit 0) that indicates whether the UChar position is in the middle - * of a surrogate pair - * (from a 4-byte UTF-8 sequence for the corresponding supplementary code point) - * - * getState() cannot also encode the UTF-16 index in the state value. - * move(relative to limit or length), or - * move(relative to current) after setState(), may return UITER_UNKNOWN_INDEX. - * - * @param iter UCharIterator structure to be set for iteration - * @param s UTF-8 string to iterate over - * @param length Length of s in bytes, or -1 if NUL-terminated - * - * @see UCharIterator - * @stable ICU 2.6 - */ -U_CAPI void U_EXPORT2 -uiter_setUTF8(UCharIterator *iter, const char *s, int32_t length); - -#if U_SHOW_CPLUSPLUS_API - -/** - * Set up a UCharIterator to wrap around a C++ CharacterIterator. - * - * Sets the UCharIterator function pointers for iteration using the - * CharacterIterator charIter. - * - * The CharacterIterator pointer charIter is set into UCharIterator.context - * without copying or cloning the CharacterIterator object. - * The other "protected" UCharIterator fields are set to 0 and will be ignored. - * The iteration index and boundaries are controlled by the CharacterIterator. - * - * getState() simply returns the current index. - * move() will always return the final index. - * - * @param iter UCharIterator structure to be set for iteration - * @param charIter CharacterIterator to wrap - * - * @see UCharIterator - * @stable ICU 2.1 - */ -U_CAPI void U_EXPORT2 -uiter_setCharacterIterator(UCharIterator *iter, icu::CharacterIterator *charIter); - -/** - * Set up a UCharIterator to iterate over a C++ Replaceable. - * - * Sets the UCharIterator function pointers for iteration over the - * Replaceable rep with iteration boundaries start=index=0 and - * length=limit=rep->length(). - * The "provider" may set the start, index, and limit values at any time - * within the range 0..length=rep->length(). - * The length field will be ignored. - * - * The Replaceable pointer rep is set into UCharIterator.context without copying - * or cloning/reallocating the Replaceable object. - * - * getState() simply returns the current index. - * move() will always return the final index. - * - * @param iter UCharIterator structure to be set for iteration - * @param rep Replaceable to iterate over - * - * @see UCharIterator - * @stable ICU 2.1 - */ -U_CAPI void U_EXPORT2 -uiter_setReplaceable(UCharIterator *iter, const icu::Replaceable *rep); - -#endif - -U_CDECL_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2011 International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uiter.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002jan18 +* created by: Markus W. Scherer +*/ + +#ifndef __UITER_H__ +#define __UITER_H__ + +/** + * \file + * \brief C API: Unicode Character Iteration + * + * @see UCharIterator + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + U_NAMESPACE_BEGIN + + class CharacterIterator; + class Replaceable; + + U_NAMESPACE_END +#endif + +U_CDECL_BEGIN + +struct UCharIterator; +typedef struct UCharIterator UCharIterator; /**< C typedef for struct UCharIterator. @stable ICU 2.1 */ + +/** + * Origin constants for UCharIterator.getIndex() and UCharIterator.move(). + * @see UCharIteratorMove + * @see UCharIterator + * @stable ICU 2.1 + */ +typedef enum UCharIteratorOrigin { + UITER_START, UITER_CURRENT, UITER_LIMIT, UITER_ZERO, UITER_LENGTH +} UCharIteratorOrigin; + +/** Constants for UCharIterator. @stable ICU 2.6 */ +enum { + /** + * Constant value that may be returned by UCharIteratorMove + * indicating that the final UTF-16 index is not known, but that the move succeeded. + * This can occur when moving relative to limit or length, or + * when moving relative to the current index after a setState() + * when the current UTF-16 index is not known. + * + * It would be very inefficient to have to count from the beginning of the text + * just to get the current/limit/length index after moving relative to it. + * The actual index can be determined with getIndex(UITER_CURRENT) + * which will count the UChars if necessary. + * + * @stable ICU 2.6 + */ + UITER_UNKNOWN_INDEX=-2 +}; + + +/** + * Constant for UCharIterator getState() indicating an error or + * an unknown state. + * Returned by uiter_getState()/UCharIteratorGetState + * when an error occurs. + * Also, some UCharIterator implementations may not be able to return + * a valid state for each position. This will be clearly documented + * for each such iterator (none of the public ones here). + * + * @stable ICU 2.6 + */ +#define UITER_NO_STATE ((uint32_t)0xffffffff) + +/** + * Function type declaration for UCharIterator.getIndex(). + * + * Gets the current position, or the start or limit of the + * iteration range. + * + * This function may perform slowly for UITER_CURRENT after setState() was called, + * or for UITER_LENGTH, because an iterator implementation may have to count + * UChars if the underlying storage is not UTF-16. + * + * @param iter the UCharIterator structure ("this pointer") + * @param origin get the 0, start, limit, length, or current index + * @return the requested index, or U_SENTINEL in an error condition + * + * @see UCharIteratorOrigin + * @see UCharIterator + * @stable ICU 2.1 + */ +typedef int32_t U_CALLCONV +UCharIteratorGetIndex(UCharIterator *iter, UCharIteratorOrigin origin); + +/** + * Function type declaration for UCharIterator.move(). + * + * Use iter->move(iter, index, UITER_ZERO) like CharacterIterator::setIndex(index). + * + * Moves the current position relative to the start or limit of the + * iteration range, or relative to the current position itself. + * The movement is expressed in numbers of code units forward + * or backward by specifying a positive or negative delta. + * Out of bounds movement will be pinned to the start or limit. + * + * This function may perform slowly for moving relative to UITER_LENGTH + * because an iterator implementation may have to count the rest of the + * UChars if the native storage is not UTF-16. + * + * When moving relative to the limit or length, or + * relative to the current position after setState() was called, + * move() may return UITER_UNKNOWN_INDEX (-2) to avoid an inefficient + * determination of the actual UTF-16 index. + * The actual index can be determined with getIndex(UITER_CURRENT) + * which will count the UChars if necessary. + * See UITER_UNKNOWN_INDEX for details. + * + * @param iter the UCharIterator structure ("this pointer") + * @param delta can be positive, zero, or negative + * @param origin move relative to the 0, start, limit, length, or current index + * @return the new index, or U_SENTINEL on an error condition, + * or UITER_UNKNOWN_INDEX when the index is not known. + * + * @see UCharIteratorOrigin + * @see UCharIterator + * @see UITER_UNKNOWN_INDEX + * @stable ICU 2.1 + */ +typedef int32_t U_CALLCONV +UCharIteratorMove(UCharIterator *iter, int32_t delta, UCharIteratorOrigin origin); + +/** + * Function type declaration for UCharIterator.hasNext(). + * + * Check if current() and next() can still + * return another code unit. + * + * @param iter the UCharIterator structure ("this pointer") + * @return boolean value for whether current() and next() can still return another code unit + * + * @see UCharIterator + * @stable ICU 2.1 + */ +typedef UBool U_CALLCONV +UCharIteratorHasNext(UCharIterator *iter); + +/** + * Function type declaration for UCharIterator.hasPrevious(). + * + * Check if previous() can still return another code unit. + * + * @param iter the UCharIterator structure ("this pointer") + * @return boolean value for whether previous() can still return another code unit + * + * @see UCharIterator + * @stable ICU 2.1 + */ +typedef UBool U_CALLCONV +UCharIteratorHasPrevious(UCharIterator *iter); + +/** + * Function type declaration for UCharIterator.current(). + * + * Return the code unit at the current position, + * or U_SENTINEL if there is none (index is at the limit). + * + * @param iter the UCharIterator structure ("this pointer") + * @return the current code unit + * + * @see UCharIterator + * @stable ICU 2.1 + */ +typedef UChar32 U_CALLCONV +UCharIteratorCurrent(UCharIterator *iter); + +/** + * Function type declaration for UCharIterator.next(). + * + * Return the code unit at the current index and increment + * the index (post-increment, like s[i++]), + * or return U_SENTINEL if there is none (index is at the limit). + * + * @param iter the UCharIterator structure ("this pointer") + * @return the current code unit (and post-increment the current index) + * + * @see UCharIterator + * @stable ICU 2.1 + */ +typedef UChar32 U_CALLCONV +UCharIteratorNext(UCharIterator *iter); + +/** + * Function type declaration for UCharIterator.previous(). + * + * Decrement the index and return the code unit from there + * (pre-decrement, like s[--i]), + * or return U_SENTINEL if there is none (index is at the start). + * + * @param iter the UCharIterator structure ("this pointer") + * @return the previous code unit (after pre-decrementing the current index) + * + * @see UCharIterator + * @stable ICU 2.1 + */ +typedef UChar32 U_CALLCONV +UCharIteratorPrevious(UCharIterator *iter); + +/** + * Function type declaration for UCharIterator.reservedFn(). + * Reserved for future use. + * + * @param iter the UCharIterator structure ("this pointer") + * @param something some integer argument + * @return some integer + * + * @see UCharIterator + * @stable ICU 2.1 + */ +typedef int32_t U_CALLCONV +UCharIteratorReserved(UCharIterator *iter, int32_t something); + +/** + * Function type declaration for UCharIterator.getState(). + * + * Get the "state" of the iterator in the form of a single 32-bit word. + * It is recommended that the state value be calculated to be as small as + * is feasible. For strings with limited lengths, fewer than 32 bits may + * be sufficient. + * + * This is used together with setState()/UCharIteratorSetState + * to save and restore the iterator position more efficiently than with + * getIndex()/move(). + * + * The iterator state is defined as a uint32_t value because it is designed + * for use in ucol_nextSortKeyPart() which provides 32 bits to store the state + * of the character iterator. + * + * With some UCharIterator implementations (e.g., UTF-8), + * getting and setting the UTF-16 index with existing functions + * (getIndex(UITER_CURRENT) followed by move(pos, UITER_ZERO)) is possible but + * relatively slow because the iterator has to "walk" from a known index + * to the requested one. + * This takes more time the farther it needs to go. + * + * An opaque state value allows an iterator implementation to provide + * an internal index (UTF-8: the source byte array index) for + * fast, constant-time restoration. + * + * After calling setState(), a getIndex(UITER_CURRENT) may be slow because + * the UTF-16 index may not be restored as well, but the iterator can deliver + * the correct text contents and move relative to the current position + * without performance degradation. + * + * Some UCharIterator implementations may not be able to return + * a valid state for each position, in which case they return UITER_NO_STATE instead. + * This will be clearly documented for each such iterator (none of the public ones here). + * + * @param iter the UCharIterator structure ("this pointer") + * @return the state word + * + * @see UCharIterator + * @see UCharIteratorSetState + * @see UITER_NO_STATE + * @stable ICU 2.6 + */ +typedef uint32_t U_CALLCONV +UCharIteratorGetState(const UCharIterator *iter); + +/** + * Function type declaration for UCharIterator.setState(). + * + * Restore the "state" of the iterator using a state word from a getState() call. + * The iterator object need not be the same one as for which getState() was called, + * but it must be of the same type (set up using the same uiter_setXYZ function) + * and it must iterate over the same string + * (binary identical regardless of memory address). + * For more about the state word see UCharIteratorGetState. + * + * After calling setState(), a getIndex(UITER_CURRENT) may be slow because + * the UTF-16 index may not be restored as well, but the iterator can deliver + * the correct text contents and move relative to the current position + * without performance degradation. + * + * @param iter the UCharIterator structure ("this pointer") + * @param state the state word from a getState() call + * on a same-type, same-string iterator + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * + * @see UCharIterator + * @see UCharIteratorGetState + * @stable ICU 2.6 + */ +typedef void U_CALLCONV +UCharIteratorSetState(UCharIterator *iter, uint32_t state, UErrorCode *pErrorCode); + + +/** + * C API for code unit iteration. + * This can be used as a C wrapper around + * CharacterIterator, Replaceable, or implemented using simple strings, etc. + * + * There are two roles for using UCharIterator: + * + * A "provider" sets the necessary function pointers and controls the "protected" + * fields of the UCharIterator structure. A "provider" passes a UCharIterator + * into C APIs that need a UCharIterator as an abstract, flexible string interface. + * + * Implementations of such C APIs are "callers" of UCharIterator functions; + * they only use the "public" function pointers and never access the "protected" + * fields directly. + * + * The current() and next() functions only check the current index against the + * limit, and previous() only checks the current index against the start, + * to see if the iterator already reached the end of the iteration range. + * + * The assumption - in all iterators - is that the index is moved via the API, + * which means it won't go out of bounds, or the index is modified by + * user code that knows enough about the iterator implementation to set valid + * index values. + * + * UCharIterator functions return code unit values 0..0xffff, + * or U_SENTINEL if the iteration bounds are reached. + * + * @stable ICU 2.1 + */ +struct UCharIterator { + /** + * (protected) Pointer to string or wrapped object or similar. + * Not used by caller. + * @stable ICU 2.1 + */ + const void *context; + + /** + * (protected) Length of string or similar. + * Not used by caller. + * @stable ICU 2.1 + */ + int32_t length; + + /** + * (protected) Start index or similar. + * Not used by caller. + * @stable ICU 2.1 + */ + int32_t start; + + /** + * (protected) Current index or similar. + * Not used by caller. + * @stable ICU 2.1 + */ + int32_t index; + + /** + * (protected) Limit index or similar. + * Not used by caller. + * @stable ICU 2.1 + */ + int32_t limit; + + /** + * (protected) Used by UTF-8 iterators and possibly others. + * @stable ICU 2.1 + */ + int32_t reservedField; + + /** + * (public) Returns the current position or the + * start or limit index of the iteration range. + * + * @see UCharIteratorGetIndex + * @stable ICU 2.1 + */ + UCharIteratorGetIndex *getIndex; + + /** + * (public) Moves the current position relative to the start or limit of the + * iteration range, or relative to the current position itself. + * The movement is expressed in numbers of code units forward + * or backward by specifying a positive or negative delta. + * + * @see UCharIteratorMove + * @stable ICU 2.1 + */ + UCharIteratorMove *move; + + /** + * (public) Check if current() and next() can still + * return another code unit. + * + * @see UCharIteratorHasNext + * @stable ICU 2.1 + */ + UCharIteratorHasNext *hasNext; + + /** + * (public) Check if previous() can still return another code unit. + * + * @see UCharIteratorHasPrevious + * @stable ICU 2.1 + */ + UCharIteratorHasPrevious *hasPrevious; + + /** + * (public) Return the code unit at the current position, + * or U_SENTINEL if there is none (index is at the limit). + * + * @see UCharIteratorCurrent + * @stable ICU 2.1 + */ + UCharIteratorCurrent *current; + + /** + * (public) Return the code unit at the current index and increment + * the index (post-increment, like s[i++]), + * or return U_SENTINEL if there is none (index is at the limit). + * + * @see UCharIteratorNext + * @stable ICU 2.1 + */ + UCharIteratorNext *next; + + /** + * (public) Decrement the index and return the code unit from there + * (pre-decrement, like s[--i]), + * or return U_SENTINEL if there is none (index is at the start). + * + * @see UCharIteratorPrevious + * @stable ICU 2.1 + */ + UCharIteratorPrevious *previous; + + /** + * (public) Reserved for future use. Currently NULL. + * + * @see UCharIteratorReserved + * @stable ICU 2.1 + */ + UCharIteratorReserved *reservedFn; + + /** + * (public) Return the state of the iterator, to be restored later with setState(). + * This function pointer is NULL if the iterator does not implement it. + * + * @see UCharIteratorGet + * @stable ICU 2.6 + */ + UCharIteratorGetState *getState; + + /** + * (public) Restore the iterator state from the state word from a call + * to getState(). + * This function pointer is NULL if the iterator does not implement it. + * + * @see UCharIteratorSet + * @stable ICU 2.6 + */ + UCharIteratorSetState *setState; +}; + +/** + * Helper function for UCharIterator to get the code point + * at the current index. + * + * Return the code point that includes the code unit at the current position, + * or U_SENTINEL if there is none (index is at the limit). + * If the current code unit is a lead or trail surrogate, + * then the following or preceding surrogate is used to form + * the code point value. + * + * @param iter the UCharIterator structure ("this pointer") + * @return the current code point + * + * @see UCharIterator + * @see U16_GET + * @see UnicodeString::char32At() + * @stable ICU 2.1 + */ +U_CAPI UChar32 U_EXPORT2 +uiter_current32(UCharIterator *iter); + +/** + * Helper function for UCharIterator to get the next code point. + * + * Return the code point at the current index and increment + * the index (post-increment, like s[i++]), + * or return U_SENTINEL if there is none (index is at the limit). + * + * @param iter the UCharIterator structure ("this pointer") + * @return the current code point (and post-increment the current index) + * + * @see UCharIterator + * @see U16_NEXT + * @stable ICU 2.1 + */ +U_CAPI UChar32 U_EXPORT2 +uiter_next32(UCharIterator *iter); + +/** + * Helper function for UCharIterator to get the previous code point. + * + * Decrement the index and return the code point from there + * (pre-decrement, like s[--i]), + * or return U_SENTINEL if there is none (index is at the start). + * + * @param iter the UCharIterator structure ("this pointer") + * @return the previous code point (after pre-decrementing the current index) + * + * @see UCharIterator + * @see U16_PREV + * @stable ICU 2.1 + */ +U_CAPI UChar32 U_EXPORT2 +uiter_previous32(UCharIterator *iter); + +/** + * Get the "state" of the iterator in the form of a single 32-bit word. + * This is a convenience function that calls iter->getState(iter) + * if iter->getState is not NULL; + * if it is NULL or any other error occurs, then UITER_NO_STATE is returned. + * + * Some UCharIterator implementations may not be able to return + * a valid state for each position, in which case they return UITER_NO_STATE instead. + * This will be clearly documented for each such iterator (none of the public ones here). + * + * @param iter the UCharIterator structure ("this pointer") + * @return the state word + * + * @see UCharIterator + * @see UCharIteratorGetState + * @see UITER_NO_STATE + * @stable ICU 2.6 + */ +U_CAPI uint32_t U_EXPORT2 +uiter_getState(const UCharIterator *iter); + +/** + * Restore the "state" of the iterator using a state word from a getState() call. + * This is a convenience function that calls iter->setState(iter, state, pErrorCode) + * if iter->setState is not NULL; if it is NULL, then U_UNSUPPORTED_ERROR is set. + * + * @param iter the UCharIterator structure ("this pointer") + * @param state the state word from a getState() call + * on a same-type, same-string iterator + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * + * @see UCharIterator + * @see UCharIteratorSetState + * @stable ICU 2.6 + */ +U_CAPI void U_EXPORT2 +uiter_setState(UCharIterator *iter, uint32_t state, UErrorCode *pErrorCode); + +/** + * Set up a UCharIterator to iterate over a string. + * + * Sets the UCharIterator function pointers for iteration over the string s + * with iteration boundaries start=index=0 and length=limit=string length. + * The "provider" may set the start, index, and limit values at any time + * within the range 0..length. + * The length field will be ignored. + * + * The string pointer s is set into UCharIterator.context without copying + * or reallocating the string contents. + * + * getState() simply returns the current index. + * move() will always return the final index. + * + * @param iter UCharIterator structure to be set for iteration + * @param s String to iterate over + * @param length Length of s, or -1 if NUL-terminated + * + * @see UCharIterator + * @stable ICU 2.1 + */ +U_CAPI void U_EXPORT2 +uiter_setString(UCharIterator *iter, const UChar *s, int32_t length); + +/** + * Set up a UCharIterator to iterate over a UTF-16BE string + * (byte vector with a big-endian pair of bytes per UChar). + * + * Everything works just like with a normal UChar iterator (uiter_setString), + * except that UChars are assembled from byte pairs, + * and that the length argument here indicates an even number of bytes. + * + * getState() simply returns the current index. + * move() will always return the final index. + * + * @param iter UCharIterator structure to be set for iteration + * @param s UTF-16BE string to iterate over + * @param length Length of s as an even number of bytes, or -1 if NUL-terminated + * (NUL means pair of 0 bytes at even index from s) + * + * @see UCharIterator + * @see uiter_setString + * @stable ICU 2.6 + */ +U_CAPI void U_EXPORT2 +uiter_setUTF16BE(UCharIterator *iter, const char *s, int32_t length); + +/** + * Set up a UCharIterator to iterate over a UTF-8 string. + * + * Sets the UCharIterator function pointers for iteration over the UTF-8 string s + * with UTF-8 iteration boundaries 0 and length. + * The implementation counts the UTF-16 index on the fly and + * lazily evaluates the UTF-16 length of the text. + * + * The start field is used as the UTF-8 offset, the limit field as the UTF-8 length. + * When the reservedField is not 0, then it contains a supplementary code point + * and the UTF-16 index is between the two corresponding surrogates. + * At that point, the UTF-8 index is behind that code point. + * + * The UTF-8 string pointer s is set into UCharIterator.context without copying + * or reallocating the string contents. + * + * getState() returns a state value consisting of + * - the current UTF-8 source byte index (bits 31..1) + * - a flag (bit 0) that indicates whether the UChar position is in the middle + * of a surrogate pair + * (from a 4-byte UTF-8 sequence for the corresponding supplementary code point) + * + * getState() cannot also encode the UTF-16 index in the state value. + * move(relative to limit or length), or + * move(relative to current) after setState(), may return UITER_UNKNOWN_INDEX. + * + * @param iter UCharIterator structure to be set for iteration + * @param s UTF-8 string to iterate over + * @param length Length of s in bytes, or -1 if NUL-terminated + * + * @see UCharIterator + * @stable ICU 2.6 + */ +U_CAPI void U_EXPORT2 +uiter_setUTF8(UCharIterator *iter, const char *s, int32_t length); + +#if U_SHOW_CPLUSPLUS_API + +/** + * Set up a UCharIterator to wrap around a C++ CharacterIterator. + * + * Sets the UCharIterator function pointers for iteration using the + * CharacterIterator charIter. + * + * The CharacterIterator pointer charIter is set into UCharIterator.context + * without copying or cloning the CharacterIterator object. + * The other "protected" UCharIterator fields are set to 0 and will be ignored. + * The iteration index and boundaries are controlled by the CharacterIterator. + * + * getState() simply returns the current index. + * move() will always return the final index. + * + * @param iter UCharIterator structure to be set for iteration + * @param charIter CharacterIterator to wrap + * + * @see UCharIterator + * @stable ICU 2.1 + */ +U_CAPI void U_EXPORT2 +uiter_setCharacterIterator(UCharIterator *iter, icu::CharacterIterator *charIter); + +/** + * Set up a UCharIterator to iterate over a C++ Replaceable. + * + * Sets the UCharIterator function pointers for iteration over the + * Replaceable rep with iteration boundaries start=index=0 and + * length=limit=rep->length(). + * The "provider" may set the start, index, and limit values at any time + * within the range 0..length=rep->length(). + * The length field will be ignored. + * + * The Replaceable pointer rep is set into UCharIterator.context without copying + * or cloning/reallocating the Replaceable object. + * + * getState() simply returns the current index. + * move() will always return the final index. + * + * @param iter UCharIterator structure to be set for iteration + * @param rep Replaceable to iterate over + * + * @see UCharIterator + * @stable ICU 2.1 + */ +U_CAPI void U_EXPORT2 +uiter_setReplaceable(UCharIterator *iter, const icu::Replaceable *rep); + +#endif + +U_CDECL_END + +#endif diff --git a/deps/icu-small/source/common/unicode/uldnames.h b/deps/icu-small/source/common/unicode/uldnames.h index 47b047ece97b80..da5b08e82ec9bc 100644 --- a/deps/icu-small/source/common/unicode/uldnames.h +++ b/deps/icu-small/source/common/unicode/uldnames.h @@ -1,307 +1,307 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#ifndef __ULDNAMES_H__ -#define __ULDNAMES_H__ - -/** - * \file - * \brief C API: Provides display names of Locale ids and their components. - */ - -#include "unicode/utypes.h" -#include "unicode/uscript.h" -#include "unicode/udisplaycontext.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -/** - * Enum used in LocaleDisplayNames::createInstance. - * @stable ICU 4.4 - */ -typedef enum { - /** - * Use standard names when generating a locale name, - * e.g. en_GB displays as 'English (United Kingdom)'. - * @stable ICU 4.4 - */ - ULDN_STANDARD_NAMES = 0, - /** - * Use dialect names, when generating a locale name, - * e.g. en_GB displays as 'British English'. - * @stable ICU 4.4 - */ - ULDN_DIALECT_NAMES -} UDialectHandling; - -/** - * Opaque C service object type for the locale display names API - * @stable ICU 4.4 - */ -struct ULocaleDisplayNames; - -/** - * C typedef for struct ULocaleDisplayNames. - * @stable ICU 4.4 - */ -typedef struct ULocaleDisplayNames ULocaleDisplayNames; - -#if !UCONFIG_NO_FORMATTING - -/** - * Returns an instance of LocaleDisplayNames that returns names - * formatted for the provided locale, using the provided - * dialectHandling. The usual value for dialectHandling is - * ULOC_STANDARD_NAMES. - * - * @param locale the display locale - * @param dialectHandling how to select names for locales - * @return a ULocaleDisplayNames instance - * @param pErrorCode the status code - * @stable ICU 4.4 - */ -U_CAPI ULocaleDisplayNames * U_EXPORT2 -uldn_open(const char * locale, - UDialectHandling dialectHandling, - UErrorCode *pErrorCode); - -/** - * Closes a ULocaleDisplayNames instance obtained from uldn_open(). - * @param ldn the ULocaleDisplayNames instance to be closed - * @stable ICU 4.4 - */ -U_CAPI void U_EXPORT2 -uldn_close(ULocaleDisplayNames *ldn); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalULocaleDisplayNamesPointer - * "Smart pointer" class, closes a ULocaleDisplayNames via uldn_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalULocaleDisplayNamesPointer, ULocaleDisplayNames, uldn_close); - -U_NAMESPACE_END - -#endif - -/* getters for state */ - -/** - * Returns the locale used to determine the display names. This is - * not necessarily the same locale passed to {@link #uldn_open}. - * @param ldn the LocaleDisplayNames instance - * @return the display locale - * @stable ICU 4.4 - */ -U_CAPI const char * U_EXPORT2 -uldn_getLocale(const ULocaleDisplayNames *ldn); - -/** - * Returns the dialect handling used in the display names. - * @param ldn the LocaleDisplayNames instance - * @return the dialect handling enum - * @stable ICU 4.4 - */ -U_CAPI UDialectHandling U_EXPORT2 -uldn_getDialectHandling(const ULocaleDisplayNames *ldn); - -/* names for entire locales */ - -/** - * Returns the display name of the provided locale. - * @param ldn the LocaleDisplayNames instance - * @param locale the locale whose display name to return - * @param result receives the display name - * @param maxResultSize the size of the result buffer - * @param pErrorCode the status code - * @return the actual buffer size needed for the display name. If it's - * greater than maxResultSize, the returned name will be truncated. - * @stable ICU 4.4 - */ -U_CAPI int32_t U_EXPORT2 -uldn_localeDisplayName(const ULocaleDisplayNames *ldn, - const char *locale, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode); - -/* names for components of a locale */ - -/** - * Returns the display name of the provided language code. - * @param ldn the LocaleDisplayNames instance - * @param lang the language code whose display name to return - * @param result receives the display name - * @param maxResultSize the size of the result buffer - * @param pErrorCode the status code - * @return the actual buffer size needed for the display name. If it's - * greater than maxResultSize, the returned name will be truncated. - * @stable ICU 4.4 - */ -U_CAPI int32_t U_EXPORT2 -uldn_languageDisplayName(const ULocaleDisplayNames *ldn, - const char *lang, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode); - -/** - * Returns the display name of the provided script. - * @param ldn the LocaleDisplayNames instance - * @param script the script whose display name to return - * @param result receives the display name - * @param maxResultSize the size of the result buffer - * @param pErrorCode the status code - * @return the actual buffer size needed for the display name. If it's - * greater than maxResultSize, the returned name will be truncated. - * @stable ICU 4.4 - */ -U_CAPI int32_t U_EXPORT2 -uldn_scriptDisplayName(const ULocaleDisplayNames *ldn, - const char *script, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode); - -/** - * Returns the display name of the provided script code. - * @param ldn the LocaleDisplayNames instance - * @param scriptCode the script code whose display name to return - * @param result receives the display name - * @param maxResultSize the size of the result buffer - * @param pErrorCode the status code - * @return the actual buffer size needed for the display name. If it's - * greater than maxResultSize, the returned name will be truncated. - * @stable ICU 4.4 - */ -U_CAPI int32_t U_EXPORT2 -uldn_scriptCodeDisplayName(const ULocaleDisplayNames *ldn, - UScriptCode scriptCode, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode); - -/** - * Returns the display name of the provided region code. - * @param ldn the LocaleDisplayNames instance - * @param region the region code whose display name to return - * @param result receives the display name - * @param maxResultSize the size of the result buffer - * @param pErrorCode the status code - * @return the actual buffer size needed for the display name. If it's - * greater than maxResultSize, the returned name will be truncated. - * @stable ICU 4.4 - */ -U_CAPI int32_t U_EXPORT2 -uldn_regionDisplayName(const ULocaleDisplayNames *ldn, - const char *region, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode); - -/** - * Returns the display name of the provided variant - * @param ldn the LocaleDisplayNames instance - * @param variant the variant whose display name to return - * @param result receives the display name - * @param maxResultSize the size of the result buffer - * @param pErrorCode the status code - * @return the actual buffer size needed for the display name. If it's - * greater than maxResultSize, the returned name will be truncated. - * @stable ICU 4.4 - */ -U_CAPI int32_t U_EXPORT2 -uldn_variantDisplayName(const ULocaleDisplayNames *ldn, - const char *variant, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode); - -/** - * Returns the display name of the provided locale key - * @param ldn the LocaleDisplayNames instance - * @param key the locale key whose display name to return - * @param result receives the display name - * @param maxResultSize the size of the result buffer - * @param pErrorCode the status code - * @return the actual buffer size needed for the display name. If it's - * greater than maxResultSize, the returned name will be truncated. - * @stable ICU 4.4 - */ -U_CAPI int32_t U_EXPORT2 -uldn_keyDisplayName(const ULocaleDisplayNames *ldn, - const char *key, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode); - -/** - * Returns the display name of the provided value (used with the provided key). - * @param ldn the LocaleDisplayNames instance - * @param key the locale key - * @param value the locale key's value - * @param result receives the display name - * @param maxResultSize the size of the result buffer - * @param pErrorCode the status code - * @return the actual buffer size needed for the display name. If it's - * greater than maxResultSize, the returned name will be truncated. - * @stable ICU 4.4 - */ -U_CAPI int32_t U_EXPORT2 -uldn_keyValueDisplayName(const ULocaleDisplayNames *ldn, - const char *key, - const char *value, - UChar *result, - int32_t maxResultSize, - UErrorCode *pErrorCode); - -/** -* Returns an instance of LocaleDisplayNames that returns names formatted -* for the provided locale, using the provided UDisplayContext settings. -* -* @param locale The display locale -* @param contexts List of one or more context settings (e.g. for dialect -* handling, capitalization, etc. -* @param length Number of items in the contexts list -* @param pErrorCode Pointer to UErrorCode input/output status. If at entry this indicates -* a failure status, the function will do nothing; otherwise this will be -* updated with any new status from the function. -* @return a ULocaleDisplayNames instance -* @stable ICU 51 -*/ -U_CAPI ULocaleDisplayNames * U_EXPORT2 -uldn_openForContext(const char * locale, UDisplayContext *contexts, - int32_t length, UErrorCode *pErrorCode); - -/** -* Returns the UDisplayContext value for the specified UDisplayContextType. -* @param ldn the ULocaleDisplayNames instance -* @param type the UDisplayContextType whose value to return -* @param pErrorCode Pointer to UErrorCode input/output status. If at entry this indicates -* a failure status, the function will do nothing; otherwise this will be -* updated with any new status from the function. -* @return the UDisplayContextValue for the specified type. -* @stable ICU 51 -*/ -U_CAPI UDisplayContext U_EXPORT2 -uldn_getContext(const ULocaleDisplayNames *ldn, UDisplayContextType type, - UErrorCode *pErrorCode); - -#endif /* !UCONFIG_NO_FORMATTING */ -#endif /* __ULDNAMES_H__ */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +*/ + +#ifndef __ULDNAMES_H__ +#define __ULDNAMES_H__ + +/** + * \file + * \brief C API: Provides display names of Locale ids and their components. + */ + +#include "unicode/utypes.h" +#include "unicode/uscript.h" +#include "unicode/udisplaycontext.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +/** + * Enum used in LocaleDisplayNames::createInstance. + * @stable ICU 4.4 + */ +typedef enum { + /** + * Use standard names when generating a locale name, + * e.g. en_GB displays as 'English (United Kingdom)'. + * @stable ICU 4.4 + */ + ULDN_STANDARD_NAMES = 0, + /** + * Use dialect names, when generating a locale name, + * e.g. en_GB displays as 'British English'. + * @stable ICU 4.4 + */ + ULDN_DIALECT_NAMES +} UDialectHandling; + +/** + * Opaque C service object type for the locale display names API + * @stable ICU 4.4 + */ +struct ULocaleDisplayNames; + +/** + * C typedef for struct ULocaleDisplayNames. + * @stable ICU 4.4 + */ +typedef struct ULocaleDisplayNames ULocaleDisplayNames; + +#if !UCONFIG_NO_FORMATTING + +/** + * Returns an instance of LocaleDisplayNames that returns names + * formatted for the provided locale, using the provided + * dialectHandling. The usual value for dialectHandling is + * ULOC_STANDARD_NAMES. + * + * @param locale the display locale + * @param dialectHandling how to select names for locales + * @return a ULocaleDisplayNames instance + * @param pErrorCode the status code + * @stable ICU 4.4 + */ +U_CAPI ULocaleDisplayNames * U_EXPORT2 +uldn_open(const char * locale, + UDialectHandling dialectHandling, + UErrorCode *pErrorCode); + +/** + * Closes a ULocaleDisplayNames instance obtained from uldn_open(). + * @param ldn the ULocaleDisplayNames instance to be closed + * @stable ICU 4.4 + */ +U_CAPI void U_EXPORT2 +uldn_close(ULocaleDisplayNames *ldn); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalULocaleDisplayNamesPointer + * "Smart pointer" class, closes a ULocaleDisplayNames via uldn_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalULocaleDisplayNamesPointer, ULocaleDisplayNames, uldn_close); + +U_NAMESPACE_END + +#endif + +/* getters for state */ + +/** + * Returns the locale used to determine the display names. This is + * not necessarily the same locale passed to {@link #uldn_open}. + * @param ldn the LocaleDisplayNames instance + * @return the display locale + * @stable ICU 4.4 + */ +U_CAPI const char * U_EXPORT2 +uldn_getLocale(const ULocaleDisplayNames *ldn); + +/** + * Returns the dialect handling used in the display names. + * @param ldn the LocaleDisplayNames instance + * @return the dialect handling enum + * @stable ICU 4.4 + */ +U_CAPI UDialectHandling U_EXPORT2 +uldn_getDialectHandling(const ULocaleDisplayNames *ldn); + +/* names for entire locales */ + +/** + * Returns the display name of the provided locale. + * @param ldn the LocaleDisplayNames instance + * @param locale the locale whose display name to return + * @param result receives the display name + * @param maxResultSize the size of the result buffer + * @param pErrorCode the status code + * @return the actual buffer size needed for the display name. If it's + * greater than maxResultSize, the returned name will be truncated. + * @stable ICU 4.4 + */ +U_CAPI int32_t U_EXPORT2 +uldn_localeDisplayName(const ULocaleDisplayNames *ldn, + const char *locale, + UChar *result, + int32_t maxResultSize, + UErrorCode *pErrorCode); + +/* names for components of a locale */ + +/** + * Returns the display name of the provided language code. + * @param ldn the LocaleDisplayNames instance + * @param lang the language code whose display name to return + * @param result receives the display name + * @param maxResultSize the size of the result buffer + * @param pErrorCode the status code + * @return the actual buffer size needed for the display name. If it's + * greater than maxResultSize, the returned name will be truncated. + * @stable ICU 4.4 + */ +U_CAPI int32_t U_EXPORT2 +uldn_languageDisplayName(const ULocaleDisplayNames *ldn, + const char *lang, + UChar *result, + int32_t maxResultSize, + UErrorCode *pErrorCode); + +/** + * Returns the display name of the provided script. + * @param ldn the LocaleDisplayNames instance + * @param script the script whose display name to return + * @param result receives the display name + * @param maxResultSize the size of the result buffer + * @param pErrorCode the status code + * @return the actual buffer size needed for the display name. If it's + * greater than maxResultSize, the returned name will be truncated. + * @stable ICU 4.4 + */ +U_CAPI int32_t U_EXPORT2 +uldn_scriptDisplayName(const ULocaleDisplayNames *ldn, + const char *script, + UChar *result, + int32_t maxResultSize, + UErrorCode *pErrorCode); + +/** + * Returns the display name of the provided script code. + * @param ldn the LocaleDisplayNames instance + * @param scriptCode the script code whose display name to return + * @param result receives the display name + * @param maxResultSize the size of the result buffer + * @param pErrorCode the status code + * @return the actual buffer size needed for the display name. If it's + * greater than maxResultSize, the returned name will be truncated. + * @stable ICU 4.4 + */ +U_CAPI int32_t U_EXPORT2 +uldn_scriptCodeDisplayName(const ULocaleDisplayNames *ldn, + UScriptCode scriptCode, + UChar *result, + int32_t maxResultSize, + UErrorCode *pErrorCode); + +/** + * Returns the display name of the provided region code. + * @param ldn the LocaleDisplayNames instance + * @param region the region code whose display name to return + * @param result receives the display name + * @param maxResultSize the size of the result buffer + * @param pErrorCode the status code + * @return the actual buffer size needed for the display name. If it's + * greater than maxResultSize, the returned name will be truncated. + * @stable ICU 4.4 + */ +U_CAPI int32_t U_EXPORT2 +uldn_regionDisplayName(const ULocaleDisplayNames *ldn, + const char *region, + UChar *result, + int32_t maxResultSize, + UErrorCode *pErrorCode); + +/** + * Returns the display name of the provided variant + * @param ldn the LocaleDisplayNames instance + * @param variant the variant whose display name to return + * @param result receives the display name + * @param maxResultSize the size of the result buffer + * @param pErrorCode the status code + * @return the actual buffer size needed for the display name. If it's + * greater than maxResultSize, the returned name will be truncated. + * @stable ICU 4.4 + */ +U_CAPI int32_t U_EXPORT2 +uldn_variantDisplayName(const ULocaleDisplayNames *ldn, + const char *variant, + UChar *result, + int32_t maxResultSize, + UErrorCode *pErrorCode); + +/** + * Returns the display name of the provided locale key + * @param ldn the LocaleDisplayNames instance + * @param key the locale key whose display name to return + * @param result receives the display name + * @param maxResultSize the size of the result buffer + * @param pErrorCode the status code + * @return the actual buffer size needed for the display name. If it's + * greater than maxResultSize, the returned name will be truncated. + * @stable ICU 4.4 + */ +U_CAPI int32_t U_EXPORT2 +uldn_keyDisplayName(const ULocaleDisplayNames *ldn, + const char *key, + UChar *result, + int32_t maxResultSize, + UErrorCode *pErrorCode); + +/** + * Returns the display name of the provided value (used with the provided key). + * @param ldn the LocaleDisplayNames instance + * @param key the locale key + * @param value the locale key's value + * @param result receives the display name + * @param maxResultSize the size of the result buffer + * @param pErrorCode the status code + * @return the actual buffer size needed for the display name. If it's + * greater than maxResultSize, the returned name will be truncated. + * @stable ICU 4.4 + */ +U_CAPI int32_t U_EXPORT2 +uldn_keyValueDisplayName(const ULocaleDisplayNames *ldn, + const char *key, + const char *value, + UChar *result, + int32_t maxResultSize, + UErrorCode *pErrorCode); + +/** +* Returns an instance of LocaleDisplayNames that returns names formatted +* for the provided locale, using the provided UDisplayContext settings. +* +* @param locale The display locale +* @param contexts List of one or more context settings (e.g. for dialect +* handling, capitalization, etc. +* @param length Number of items in the contexts list +* @param pErrorCode Pointer to UErrorCode input/output status. If at entry this indicates +* a failure status, the function will do nothing; otherwise this will be +* updated with any new status from the function. +* @return a ULocaleDisplayNames instance +* @stable ICU 51 +*/ +U_CAPI ULocaleDisplayNames * U_EXPORT2 +uldn_openForContext(const char * locale, UDisplayContext *contexts, + int32_t length, UErrorCode *pErrorCode); + +/** +* Returns the UDisplayContext value for the specified UDisplayContextType. +* @param ldn the ULocaleDisplayNames instance +* @param type the UDisplayContextType whose value to return +* @param pErrorCode Pointer to UErrorCode input/output status. If at entry this indicates +* a failure status, the function will do nothing; otherwise this will be +* updated with any new status from the function. +* @return the UDisplayContextValue for the specified type. +* @stable ICU 51 +*/ +U_CAPI UDisplayContext U_EXPORT2 +uldn_getContext(const ULocaleDisplayNames *ldn, UDisplayContextType type, + UErrorCode *pErrorCode); + +#endif /* !UCONFIG_NO_FORMATTING */ +#endif /* __ULDNAMES_H__ */ diff --git a/deps/icu-small/source/common/unicode/uloc.h b/deps/icu-small/source/common/unicode/uloc.h index 21179c1b628e1c..f118078071bd3b 100644 --- a/deps/icu-small/source/common/unicode/uloc.h +++ b/deps/icu-small/source/common/unicode/uloc.h @@ -1,1393 +1,1393 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1997-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* File ULOC.H -* -* Modification History: -* -* Date Name Description -* 04/01/97 aliu Creation. -* 08/22/98 stephen JDK 1.2 sync. -* 12/08/98 rtg New C API for Locale -* 03/30/99 damiba overhaul -* 03/31/99 helena Javadoc for uloc functions. -* 04/15/99 Madhu Updated Javadoc -******************************************************************************** -*/ - -#ifndef ULOC_H -#define ULOC_H - -#include "unicode/utypes.h" -#include "unicode/uenum.h" - -/** - * \file - * \brief C API: Locale ID functionality similar to C++ class Locale - * - *

ULoc C API for Locale

- * A Locale represents a specific geographical, political, - * or cultural region. An operation that requires a Locale to perform - * its task is called locale-sensitive and uses the Locale - * to tailor information for the user. For example, displaying a number - * is a locale-sensitive operation--the number should be formatted - * according to the customs/conventions of the user's native country, - * region, or culture. In the C APIs, a locales is simply a const char string. - * - *

- * You create a Locale with one of the three options listed below. - * Each of the component is separated by '_' in the locale string. - * \htmlonly

\endhtmlonly - *
- * \code
- *       newLanguage
- * 
- *       newLanguage + newCountry
- * 
- *       newLanguage + newCountry + newVariant
- * \endcode
- * 
- * \htmlonly
\endhtmlonly - * The first option is a valid ISO - * Language Code. These codes are the lower-case two-letter - * codes as defined by ISO-639. - * You can find a full list of these codes at a number of sites, such as: - *
- * http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt - * - *

- * The second option includes an additional ISO Country - * Code. These codes are the upper-case two-letter codes - * as defined by ISO-3166. - * You can find a full list of these codes at a number of sites, such as: - *
- * http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html - * - *

- * The third option requires another additional information--the - * Variant. - * The Variant codes are vendor and browser-specific. - * For example, use WIN for Windows, MAC for Macintosh, and POSIX for POSIX. - * Where there are two variants, separate them with an underscore, and - * put the most important one first. For - * example, a Traditional Spanish collation might be referenced, with - * "ES", "ES", "Traditional_WIN". - * - *

- * Because a Locale is just an identifier for a region, - * no validity check is performed when you specify a Locale. - * If you want to see whether particular resources are available for the - * Locale you asked for, you must query those resources. For - * example, ask the UNumberFormat for the locales it supports - * using its getAvailable method. - *
Note: When you ask for a resource for a particular - * locale, you get back the best available match, not necessarily - * precisely what you asked for. For more information, look at - * UResourceBundle. - * - *

- * The Locale provides a number of convenient constants - * that you can use to specify the commonly used - * locales. For example, the following refers to a locale - * for the United States: - * \htmlonly

\endhtmlonly - *
- * \code
- *       ULOC_US
- * \endcode
- * 
- * \htmlonly
\endhtmlonly - * - *

- * Once you've specified a locale you can query it for information about - * itself. Use uloc_getCountry to get the ISO Country Code and - * uloc_getLanguage to get the ISO Language Code. You can - * use uloc_getDisplayCountry to get the - * name of the country suitable for displaying to the user. Similarly, - * you can use uloc_getDisplayLanguage to get the name of - * the language suitable for displaying to the user. Interestingly, - * the uloc_getDisplayXXX methods are themselves locale-sensitive - * and have two versions: one that uses the default locale and one - * that takes a locale as an argument and displays the name or country in - * a language appropriate to that locale. - * - *

- * The ICU provides a number of services that perform locale-sensitive - * operations. For example, the unum_xxx functions format - * numbers, currency, or percentages in a locale-sensitive manner. - *

- * \htmlonly
\endhtmlonly - *
- * \code
- *     UErrorCode success = U_ZERO_ERROR;
- *     UNumberFormat *nf;
- *     const char* myLocale = "fr_FR";
- * 
- *     nf = unum_open( UNUM_DEFAULT, NULL, success );          
- *     unum_close(nf);
- *     nf = unum_open( UNUM_CURRENCY, NULL, success );
- *     unum_close(nf);
- *     nf = unum_open( UNUM_PERCENT, NULL, success );   
- *     unum_close(nf);
- * \endcode
- * 
- * \htmlonly
\endhtmlonly - * Each of these methods has two variants; one with an explicit locale - * and one without; the latter using the default locale. - * \htmlonly
\endhtmlonly - *
- * \code 
- * 
- *     nf = unum_open( UNUM_DEFAULT, myLocale, success );          
- *     unum_close(nf);
- *     nf = unum_open( UNUM_CURRENCY, myLocale, success );
- *     unum_close(nf);
- *     nf = unum_open( UNUM_PERCENT, myLocale, success );   
- *     unum_close(nf);
- * \endcode
- * 
- * \htmlonly
\endhtmlonly - * A Locale is the mechanism for identifying the kind of services - * (UNumberFormat) that you would like to get. The locale is - * just a mechanism for identifying these services. - * - *

- * Each international service that performs locale-sensitive operations - * allows you - * to get all the available objects of that type. You can sift - * through these objects by language, country, or variant, - * and use the display names to present a menu to the user. - * For example, you can create a menu of all the collation objects - * suitable for a given language. Such classes implement these - * three class methods: - * \htmlonly

\endhtmlonly - *
- * \code
- *       const char* uloc_getAvailable(int32_t index);
- *       int32_t uloc_countAvailable();
- *       int32_t
- *       uloc_getDisplayName(const char* localeID,
- *                 const char* inLocaleID, 
- *                 UChar* result,
- *                 int32_t maxResultSize,
- *                  UErrorCode* err);
- * 
- * \endcode
- * 
- * \htmlonly
\endhtmlonly - *

- * Concerning POSIX/RFC1766 Locale IDs, - * the getLanguage/getCountry/getVariant/getName functions do understand - * the POSIX type form of language_COUNTRY.ENCODING\@VARIANT - * and if there is not an ICU-stype variant, uloc_getVariant() for example - * will return the one listed after the \@at sign. As well, the hyphen - * "-" is recognized as a country/variant separator similarly to RFC1766. - * So for example, "en-us" will be interpreted as en_US. - * As a result, uloc_getName() is far from a no-op, and will have the - * effect of converting POSIX/RFC1766 IDs into ICU form, although it does - * NOT map any of the actual codes (i.e. russian->ru) in any way. - * Applications should call uloc_getName() at the point where a locale ID - * is coming from an external source (user entry, OS, web browser) - * and pass the resulting string to other ICU functions. For example, - * don't use de-de\@EURO as an argument to resourcebundle. - * - * @see UResourceBundle - */ - -/** Useful constant for this language. @stable ICU 2.0 */ -#define ULOC_CHINESE "zh" -/** Useful constant for this language. @stable ICU 2.0 */ -#define ULOC_ENGLISH "en" -/** Useful constant for this language. @stable ICU 2.0 */ -#define ULOC_FRENCH "fr" -/** Useful constant for this language. @stable ICU 2.0 */ -#define ULOC_GERMAN "de" -/** Useful constant for this language. @stable ICU 2.0 */ -#define ULOC_ITALIAN "it" -/** Useful constant for this language. @stable ICU 2.0 */ -#define ULOC_JAPANESE "ja" -/** Useful constant for this language. @stable ICU 2.0 */ -#define ULOC_KOREAN "ko" -/** Useful constant for this language. @stable ICU 2.0 */ -#define ULOC_SIMPLIFIED_CHINESE "zh_CN" -/** Useful constant for this language. @stable ICU 2.0 */ -#define ULOC_TRADITIONAL_CHINESE "zh_TW" - -/** Useful constant for this country/region. @stable ICU 2.0 */ -#define ULOC_CANADA "en_CA" -/** Useful constant for this country/region. @stable ICU 2.0 */ -#define ULOC_CANADA_FRENCH "fr_CA" -/** Useful constant for this country/region. @stable ICU 2.0 */ -#define ULOC_CHINA "zh_CN" -/** Useful constant for this country/region. @stable ICU 2.0 */ -#define ULOC_PRC "zh_CN" -/** Useful constant for this country/region. @stable ICU 2.0 */ -#define ULOC_FRANCE "fr_FR" -/** Useful constant for this country/region. @stable ICU 2.0 */ -#define ULOC_GERMANY "de_DE" -/** Useful constant for this country/region. @stable ICU 2.0 */ -#define ULOC_ITALY "it_IT" -/** Useful constant for this country/region. @stable ICU 2.0 */ -#define ULOC_JAPAN "ja_JP" -/** Useful constant for this country/region. @stable ICU 2.0 */ -#define ULOC_KOREA "ko_KR" -/** Useful constant for this country/region. @stable ICU 2.0 */ -#define ULOC_TAIWAN "zh_TW" -/** Useful constant for this country/region. @stable ICU 2.0 */ -#define ULOC_UK "en_GB" -/** Useful constant for this country/region. @stable ICU 2.0 */ -#define ULOC_US "en_US" - -/** - * Useful constant for the maximum size of the language part of a locale ID. - * (including the terminating NULL). - * @stable ICU 2.0 - */ -#define ULOC_LANG_CAPACITY 12 - -/** - * Useful constant for the maximum size of the country part of a locale ID - * (including the terminating NULL). - * @stable ICU 2.0 - */ -#define ULOC_COUNTRY_CAPACITY 4 -/** - * Useful constant for the maximum size of the whole locale ID - * (including the terminating NULL and all keywords). - * @stable ICU 2.0 - */ -#define ULOC_FULLNAME_CAPACITY 157 - -/** - * Useful constant for the maximum size of the script part of a locale ID - * (including the terminating NULL). - * @stable ICU 2.8 - */ -#define ULOC_SCRIPT_CAPACITY 6 - -/** - * Useful constant for the maximum size of keywords in a locale - * @stable ICU 2.8 - */ -#define ULOC_KEYWORDS_CAPACITY 96 - -/** - * Useful constant for the maximum total size of keywords and their values in a locale - * @stable ICU 2.8 - */ -#define ULOC_KEYWORD_AND_VALUES_CAPACITY 100 - -/** - * Invariant character separating keywords from the locale string - * @stable ICU 2.8 - */ -#define ULOC_KEYWORD_SEPARATOR '@' - -/** - * Unicode code point for '@' separating keywords from the locale string. - * @see ULOC_KEYWORD_SEPARATOR - * @stable ICU 4.6 - */ -#define ULOC_KEYWORD_SEPARATOR_UNICODE 0x40 - -/** - * Invariant character for assigning value to a keyword - * @stable ICU 2.8 - */ -#define ULOC_KEYWORD_ASSIGN '=' - -/** - * Unicode code point for '=' for assigning value to a keyword. - * @see ULOC_KEYWORD_ASSIGN - * @stable ICU 4.6 - */ -#define ULOC_KEYWORD_ASSIGN_UNICODE 0x3D - -/** - * Invariant character separating keywords - * @stable ICU 2.8 - */ -#define ULOC_KEYWORD_ITEM_SEPARATOR ';' - -/** - * Unicode code point for ';' separating keywords - * @see ULOC_KEYWORD_ITEM_SEPARATOR - * @stable ICU 4.6 - */ -#define ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE 0x3B - -/** - * Constants for *_getLocale() - * Allow user to select whether she wants information on - * requested, valid or actual locale. - * For example, a collator for "en_US_CALIFORNIA" was - * requested. In the current state of ICU (2.0), - * the requested locale is "en_US_CALIFORNIA", - * the valid locale is "en_US" (most specific locale supported by ICU) - * and the actual locale is "root" (the collation data comes unmodified - * from the UCA) - * The locale is considered supported by ICU if there is a core ICU bundle - * for that locale (although it may be empty). - * @stable ICU 2.1 - */ -typedef enum { - /** This is locale the data actually comes from - * @stable ICU 2.1 - */ - ULOC_ACTUAL_LOCALE = 0, - /** This is the most specific locale supported by ICU - * @stable ICU 2.1 - */ - ULOC_VALID_LOCALE = 1, - -#ifndef U_HIDE_DEPRECATED_API - /** This is the requested locale - * @deprecated ICU 2.8 - */ - ULOC_REQUESTED_LOCALE = 2, - - /** - * One more than the highest normal ULocDataLocaleType value. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - ULOC_DATA_LOCALE_TYPE_LIMIT = 3 -#endif // U_HIDE_DEPRECATED_API -} ULocDataLocaleType; - -#ifndef U_HIDE_SYSTEM_API -/** - * Gets ICU's default locale. - * The returned string is a snapshot in time, and will remain valid - * and unchanged even when uloc_setDefault() is called. - * The returned storage is owned by ICU, and must not be altered or deleted - * by the caller. - * - * @return the ICU default locale - * @system - * @stable ICU 2.0 - */ -U_CAPI const char* U_EXPORT2 -uloc_getDefault(void); - -/** - * Sets ICU's default locale. - * By default (without calling this function), ICU's default locale will be based - * on information obtained from the underlying system environment. - *

- * Changes to ICU's default locale do not propagate back to the - * system environment. - *

- * Changes to ICU's default locale to not affect any ICU services that - * may already be open based on the previous default locale value. - * - * @param localeID the new ICU default locale. A value of NULL will try to get - * the system's default locale. - * @param status the error information if the setting of default locale fails - * @system - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -uloc_setDefault(const char* localeID, - UErrorCode* status); -#endif /* U_HIDE_SYSTEM_API */ - -/** - * Gets the language code for the specified locale. - * - * @param localeID the locale to get the ISO language code with - * @param language the language code for localeID - * @param languageCapacity the size of the language buffer to store the - * language code with - * @param err error information if retrieving the language code failed - * @return the actual buffer size needed for the language code. If it's greater - * than languageCapacity, the returned language code will be truncated. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getLanguage(const char* localeID, - char* language, - int32_t languageCapacity, - UErrorCode* err); - -/** - * Gets the script code for the specified locale. - * - * @param localeID the locale to get the ISO language code with - * @param script the language code for localeID - * @param scriptCapacity the size of the language buffer to store the - * language code with - * @param err error information if retrieving the language code failed - * @return the actual buffer size needed for the language code. If it's greater - * than scriptCapacity, the returned language code will be truncated. - * @stable ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getScript(const char* localeID, - char* script, - int32_t scriptCapacity, - UErrorCode* err); - -/** - * Gets the country code for the specified locale. - * - * @param localeID the locale to get the country code with - * @param country the country code for localeID - * @param countryCapacity the size of the country buffer to store the - * country code with - * @param err error information if retrieving the country code failed - * @return the actual buffer size needed for the country code. If it's greater - * than countryCapacity, the returned country code will be truncated. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getCountry(const char* localeID, - char* country, - int32_t countryCapacity, - UErrorCode* err); - -/** - * Gets the variant code for the specified locale. - * - * @param localeID the locale to get the variant code with - * @param variant the variant code for localeID - * @param variantCapacity the size of the variant buffer to store the - * variant code with - * @param err error information if retrieving the variant code failed - * @return the actual buffer size needed for the variant code. If it's greater - * than variantCapacity, the returned variant code will be truncated. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getVariant(const char* localeID, - char* variant, - int32_t variantCapacity, - UErrorCode* err); - - -/** - * Gets the full name for the specified locale. - * Note: This has the effect of 'canonicalizing' the ICU locale ID to - * a certain extent. Upper and lower case are set as needed. - * It does NOT map aliased names in any way. - * See the top of this header file. - * This API supports preflighting. - * - * @param localeID the locale to get the full name with - * @param name fill in buffer for the name without keywords. - * @param nameCapacity capacity of the fill in buffer. - * @param err error information if retrieving the full name failed - * @return the actual buffer size needed for the full name. If it's greater - * than nameCapacity, the returned full name will be truncated. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getName(const char* localeID, - char* name, - int32_t nameCapacity, - UErrorCode* err); - -/** - * Gets the full name for the specified locale. - * Note: This has the effect of 'canonicalizing' the string to - * a certain extent. Upper and lower case are set as needed, - * and if the components were in 'POSIX' format they are changed to - * ICU format. It does NOT map aliased names in any way. - * See the top of this header file. - * - * @param localeID the locale to get the full name with - * @param name the full name for localeID - * @param nameCapacity the size of the name buffer to store the - * full name with - * @param err error information if retrieving the full name failed - * @return the actual buffer size needed for the full name. If it's greater - * than nameCapacity, the returned full name will be truncated. - * @stable ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -uloc_canonicalize(const char* localeID, - char* name, - int32_t nameCapacity, - UErrorCode* err); - -/** - * Gets the ISO language code for the specified locale. - * - * @param localeID the locale to get the ISO language code with - * @return language the ISO language code for localeID - * @stable ICU 2.0 - */ -U_CAPI const char* U_EXPORT2 -uloc_getISO3Language(const char* localeID); - - -/** - * Gets the ISO country code for the specified locale. - * - * @param localeID the locale to get the ISO country code with - * @return country the ISO country code for localeID - * @stable ICU 2.0 - */ -U_CAPI const char* U_EXPORT2 -uloc_getISO3Country(const char* localeID); - -/** - * Gets the Win32 LCID value for the specified locale. - * If the ICU locale is not recognized by Windows, 0 will be returned. - * - * LCIDs were deprecated with Windows Vista and Microsoft recommends - * that developers use BCP47 style tags instead (uloc_toLanguageTag). - * - * @param localeID the locale to get the Win32 LCID value with - * @return country the Win32 LCID for localeID - * @stable ICU 2.0 - */ -U_CAPI uint32_t U_EXPORT2 -uloc_getLCID(const char* localeID); - -/** - * Gets the language name suitable for display for the specified locale. - * - * @param locale the locale to get the ISO language code with - * @param displayLocale Specifies the locale to be used to display the name. In - * other words, if the locale's language code is "en", passing - * Locale::getFrench() for inLocale would result in "Anglais", - * while passing Locale::getGerman() for inLocale would result - * in "Englisch". - * @param language the displayable language code for localeID - * @param languageCapacity the size of the language buffer to store the - * displayable language code with. - * @param status error information if retrieving the displayable language code - * failed. U_USING_DEFAULT_WARNING indicates that no data was - * found from the locale resources and a case canonicalized - * language code is placed into language as fallback. - * @return the actual buffer size needed for the displayable language code. If - * it's greater than languageCapacity, the returned language - * code will be truncated. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayLanguage(const char* locale, - const char* displayLocale, - UChar* language, - int32_t languageCapacity, - UErrorCode* status); - -/** - * Gets the script name suitable for display for the specified locale. - * - * @param locale the locale to get the displayable script code with. NULL may be - * used to specify the default. - * @param displayLocale Specifies the locale to be used to display the name. In - * other words, if the locale's language code is "en", passing - * Locale::getFrench() for inLocale would result in "", while - * passing Locale::getGerman() for inLocale would result in "". - * NULL may be used to specify the default. - * @param script the displayable script for the localeID. - * @param scriptCapacity the size of the script buffer to store the displayable - * script code with. - * @param status error information if retrieving the displayable script code - * failed. U_USING_DEFAULT_WARNING indicates that no data was - * found from the locale resources and a case canonicalized - * script code is placed into script as fallback. - * @return the actual buffer size needed for the displayable script code. If - * it's greater than scriptCapacity, the returned displayable - * script code will be truncated. - * @stable ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayScript(const char* locale, - const char* displayLocale, - UChar* script, - int32_t scriptCapacity, - UErrorCode* status); - -/** - * Gets the country name suitable for display for the specified locale. - * Warning: this is for the region part of a valid locale ID; it cannot just be - * the region code (like "FR"). To get the display name for a region alone, or - * for other options, use ULocaleDisplayNames instead. - * - * @param locale the locale to get the displayable country code with. NULL may - * be used to specify the default. - * @param displayLocale Specifies the locale to be used to display the name. In - * other words, if the locale's language code is "en", passing - * Locale::getFrench() for inLocale would result in "Anglais", - * while passing Locale::getGerman() for inLocale would result - * in "Englisch". NULL may be used to specify the default. - * @param country the displayable country code for localeID. - * @param countryCapacity the size of the country buffer to store the - * displayable country code with. - * @param status error information if retrieving the displayable country code - * failed. U_USING_DEFAULT_WARNING indicates that no data was - * found from the locale resources and a case canonicalized - * country code is placed into country as fallback. - * @return the actual buffer size needed for the displayable country code. If - * it's greater than countryCapacity, the returned displayable - * country code will be truncated. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayCountry(const char* locale, - const char* displayLocale, - UChar* country, - int32_t countryCapacity, - UErrorCode* status); - - -/** - * Gets the variant name suitable for display for the specified locale. - * - * @param locale the locale to get the displayable variant code with. NULL may - * be used to specify the default. - * @param displayLocale Specifies the locale to be used to display the name. In - * other words, if the locale's language code is "en", passing - * Locale::getFrench() for inLocale would result in "Anglais", - * while passing Locale::getGerman() for inLocale would result - * in "Englisch". NULL may be used to specify the default. - * @param variant the displayable variant code for localeID. - * @param variantCapacity the size of the variant buffer to store the - * displayable variant code with. - * @param status error information if retrieving the displayable variant code - * failed. U_USING_DEFAULT_WARNING indicates that no data was - * found from the locale resources and a case canonicalized - * variant code is placed into variant as fallback. - * @return the actual buffer size needed for the displayable variant code. If - * it's greater than variantCapacity, the returned displayable - * variant code will be truncated. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayVariant(const char* locale, - const char* displayLocale, - UChar* variant, - int32_t variantCapacity, - UErrorCode* status); - -/** - * Gets the keyword name suitable for display for the specified locale. E.g: - * for the locale string de_DE\@collation=PHONEBOOK, this API gets the display - * string for the keyword collation. - * Usage: - * - * UErrorCode status = U_ZERO_ERROR; - * const char* keyword =NULL; - * int32_t keywordLen = 0; - * int32_t keywordCount = 0; - * UChar displayKeyword[256]; - * int32_t displayKeywordLen = 0; - * UEnumeration* keywordEnum = uloc_openKeywords("de_DE@collation=PHONEBOOK;calendar=TRADITIONAL", &status); - * for(keywordCount = uenum_count(keywordEnum, &status); keywordCount > 0 ; keywordCount--){ - * if(U_FAILURE(status)){ - * ...something went wrong so handle the error... - * break; - * } - * // the uenum_next returns NUL terminated string - * keyword = uenum_next(keywordEnum, &keywordLen, &status); - * displayKeywordLen = uloc_getDisplayKeyword(keyword, "en_US", displayKeyword, 256); - * ... do something interesting ..... - * } - * uenum_close(keywordEnum); - * - * @param keyword The keyword whose display string needs to be returned. - * @param displayLocale Specifies the locale to be used to display the name. In other words, - * if the locale's language code is "en", passing Locale::getFrench() for - * inLocale would result in "Anglais", while passing Locale::getGerman() - * for inLocale would result in "Englisch". NULL may be used to specify the default. - * @param dest the buffer to which the displayable keyword should be written. - * @param destCapacity The size of the buffer (number of UChars). If it is 0, then - * dest may be NULL and the function will only return the length of the - * result without writing any of the result string (pre-flighting). - * @param status error information if retrieving the displayable string failed. - * Should not be NULL and should not indicate failure on entry. - * U_USING_DEFAULT_WARNING indicates that no data was found from the locale - * resources and the keyword is placed into dest as fallback. - * @return the actual buffer size needed for the displayable variant code. - * @see #uloc_openKeywords - * @stable ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayKeyword(const char* keyword, - const char* displayLocale, - UChar* dest, - int32_t destCapacity, - UErrorCode* status); -/** - * Gets the value of the keyword suitable for display for the specified locale. - * E.g: for the locale string de_DE\@collation=PHONEBOOK, this API gets the display - * string for PHONEBOOK, in the display locale, when "collation" is specified as the keyword. - * - * @param locale The locale to get the displayable variant code with. NULL may be used to specify the default. - * @param keyword The keyword for whose value should be used. - * @param displayLocale Specifies the locale to be used to display the name. In other words, - * if the locale's language code is "en", passing Locale::getFrench() for - * inLocale would result in "Anglais", while passing Locale::getGerman() - * for inLocale would result in "Englisch". NULL may be used to specify the default. - * @param dest the buffer to which the displayable keyword should be written. - * @param destCapacity The size of the buffer (number of UChars). If it is 0, then - * dest may be NULL and the function will only return the length of the - * result without writing any of the result string (pre-flighting). - * @param status error information if retrieving the displayable string failed. - * Should not be NULL and must not indicate failure on entry. - * U_USING_DEFAULT_WARNING indicates that no data was found from the locale - * resources and the value of the keyword is placed into dest as fallback. - * @return the actual buffer size needed for the displayable variant code. - * @stable ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayKeywordValue( const char* locale, - const char* keyword, - const char* displayLocale, - UChar* dest, - int32_t destCapacity, - UErrorCode* status); -/** - * Gets the full name suitable for display for the specified locale. - * - * @param localeID the locale to get the displayable name with. NULL may be used to specify the default. - * @param inLocaleID Specifies the locale to be used to display the name. In other words, - * if the locale's language code is "en", passing Locale::getFrench() for - * inLocale would result in "Anglais", while passing Locale::getGerman() - * for inLocale would result in "Englisch". NULL may be used to specify the default. - * @param result the displayable name for localeID - * @param maxResultSize the size of the name buffer to store the - * displayable full name with - * @param err error information if retrieving the displayable name failed - * @return the actual buffer size needed for the displayable name. If it's greater - * than maxResultSize, the returned displayable name will be truncated. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayName(const char* localeID, - const char* inLocaleID, - UChar* result, - int32_t maxResultSize, - UErrorCode* err); - - -/** - * Gets the specified locale from a list of available locales. - * - * This method corresponds to uloc_openAvailableByType called with the - * ULOC_AVAILABLE_DEFAULT type argument. - * - * The return value is a pointer to an item of a locale name array. Both this - * array and the pointers it contains are owned by ICU and should not be - * deleted or written through by the caller. The locale name is terminated by - * a null pointer. - * - * @param n the specific locale name index of the available locale list; - * should not exceed the number returned by uloc_countAvailable. - * @return a specified locale name of all available locales - * @stable ICU 2.0 - */ -U_CAPI const char* U_EXPORT2 -uloc_getAvailable(int32_t n); - -/** - * Gets the size of the all available locale list. - * - * @return the size of the locale list - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 uloc_countAvailable(void); - -/** - * Types for uloc_getAvailableByType and uloc_countAvailableByType. - * - * @stable ICU 65 - */ -typedef enum ULocAvailableType { - /** - * Locales that return data when passed to ICU APIs, - * but not including legacy or alias locales. - * - * @stable ICU 65 - */ - ULOC_AVAILABLE_DEFAULT, - - /** - * Legacy or alias locales that return data when passed to ICU APIs. - * Examples of supported legacy or alias locales: - * - * - iw (alias to he) - * - mo (alias to ro) - * - zh_CN (alias to zh_Hans_CN) - * - sr_BA (alias to sr_Cyrl_BA) - * - ars (alias to ar_SA) - * - * The locales in this set are disjoint from the ones in - * ULOC_AVAILABLE_DEFAULT. To get both sets at the same time, use - * ULOC_AVAILABLE_WITH_LEGACY_ALIASES. - * - * @stable ICU 65 - */ - ULOC_AVAILABLE_ONLY_LEGACY_ALIASES, - - /** - * The union of the locales in ULOC_AVAILABLE_DEFAULT and - * ULOC_AVAILABLE_ONLY_LEGACY_ALIAS. - * - * @stable ICU 65 - */ - ULOC_AVAILABLE_WITH_LEGACY_ALIASES, - -#ifndef U_HIDE_INTERNAL_API - /** - * @internal - */ - ULOC_AVAILABLE_COUNT -#endif /* U_HIDE_INTERNAL_API */ -} ULocAvailableType; - -/** - * Gets a list of available locales according to the type argument, allowing - * the user to access different sets of supported locales in ICU. - * - * The returned UEnumeration must be closed by the caller. - * - * @param type Type choice from ULocAvailableType. - * @param status Set if an error occurred. - * @return a UEnumeration owned by the caller, or nullptr on failure. - * @stable ICU 65 - */ -U_CAPI UEnumeration* U_EXPORT2 -uloc_openAvailableByType(ULocAvailableType type, UErrorCode* status); - -/** - * - * Gets a list of all available 2-letter language codes defined in ISO 639, - * plus additional 3-letter codes determined to be useful for locale generation as - * defined by Unicode CLDR. This is a pointer - * to an array of pointers to arrays of char. All of these pointers are owned - * by ICU-- do not delete them, and do not write through them. The array is - * terminated with a null pointer. - * @return a list of all available language codes - * @stable ICU 2.0 - */ -U_CAPI const char* const* U_EXPORT2 -uloc_getISOLanguages(void); - -/** - * - * Gets a list of all available 2-letter country codes defined in ISO 639. This is a - * pointer to an array of pointers to arrays of char. All of these pointers are - * owned by ICU-- do not delete them, and do not write through them. The array is - * terminated with a null pointer. - * @return a list of all available country codes - * @stable ICU 2.0 - */ -U_CAPI const char* const* U_EXPORT2 -uloc_getISOCountries(void); - -/** - * Truncate the locale ID string to get the parent locale ID. - * Copies the part of the string before the last underscore. - * The parent locale ID will be an empty string if there is no - * underscore, or if there is only one underscore at localeID[0]. - * - * @param localeID Input locale ID string. - * @param parent Output string buffer for the parent locale ID. - * @param parentCapacity Size of the output buffer. - * @param err A UErrorCode value. - * @return The length of the parent locale ID. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getParent(const char* localeID, - char* parent, - int32_t parentCapacity, - UErrorCode* err); - - - - -/** - * Gets the full name for the specified locale, like uloc_getName(), - * but without keywords. - * - * Note: This has the effect of 'canonicalizing' the string to - * a certain extent. Upper and lower case are set as needed, - * and if the components were in 'POSIX' format they are changed to - * ICU format. It does NOT map aliased names in any way. - * See the top of this header file. - * - * This API strips off the keyword part, so "de_DE\@collation=phonebook" - * will become "de_DE". - * This API supports preflighting. - * - * @param localeID the locale to get the full name with - * @param name fill in buffer for the name without keywords. - * @param nameCapacity capacity of the fill in buffer. - * @param err error information if retrieving the full name failed - * @return the actual buffer size needed for the full name. If it's greater - * than nameCapacity, the returned full name will be truncated. - * @stable ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getBaseName(const char* localeID, - char* name, - int32_t nameCapacity, - UErrorCode* err); - -/** - * Gets an enumeration of keywords for the specified locale. Enumeration - * must get disposed of by the client using uenum_close function. - * - * @param localeID the locale to get the variant code with - * @param status error information if retrieving the keywords failed - * @return enumeration of keywords or NULL if there are no keywords. - * @stable ICU 2.8 - */ -U_CAPI UEnumeration* U_EXPORT2 -uloc_openKeywords(const char* localeID, - UErrorCode* status); - -/** - * Get the value for a keyword. Locale name does not need to be normalized. - * - * @param localeID locale name containing the keyword ("de_DE@currency=EURO;collation=PHONEBOOK") - * @param keywordName name of the keyword for which we want the value; must not be - * NULL or empty, and must consist only of [A-Za-z0-9]. Case insensitive. - * @param buffer receiving buffer - * @param bufferCapacity capacity of receiving buffer - * @param status containing error code: e.g. buffer not big enough or ill-formed localeID - * or keywordName parameters. - * @return the length of keyword value - * @stable ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getKeywordValue(const char* localeID, - const char* keywordName, - char* buffer, int32_t bufferCapacity, - UErrorCode* status); - - -/** - * Sets or removes the value of the specified keyword. - * - * For removing all keywords, use uloc_getBaseName(). - * - * NOTE: Unlike almost every other ICU function which takes a - * buffer, this function will NOT truncate the output text, and will - * not update the buffer with unterminated text setting a status of - * U_STRING_NOT_TERMINATED_WARNING. If a BUFFER_OVERFLOW_ERROR is received, - * it means a terminated version of the updated locale ID would not fit - * in the buffer, and the original buffer is untouched. This is done to - * prevent incorrect or possibly even malformed locales from being generated - * and used. - * - * @param keywordName name of the keyword to be set; must not be - * NULL or empty, and must consist only of [A-Za-z0-9]. Case insensitive. - * @param keywordValue value of the keyword to be set. If 0-length or - * NULL, will result in the keyword being removed; no error is given if - * that keyword does not exist. Otherwise, must consist only of - * [A-Za-z0-9] and [/_+-]. - * @param buffer input buffer containing well-formed locale ID to be - * modified. - * @param bufferCapacity capacity of receiving buffer - * @param status containing error code: e.g. buffer not big enough - * or ill-formed keywordName or keywordValue parameters, or ill-formed - * locale ID in buffer on input. - * @return the length needed for the buffer - * @see uloc_getKeywordValue - * @stable ICU 3.2 - */ -U_CAPI int32_t U_EXPORT2 -uloc_setKeywordValue(const char* keywordName, - const char* keywordValue, - char* buffer, int32_t bufferCapacity, - UErrorCode* status); - -/** - * Returns whether the locale's script is written right-to-left. - * If there is no script subtag, then the likely script is used, see uloc_addLikelySubtags(). - * If no likely script is known, then false is returned. - * - * A script is right-to-left according to the CLDR script metadata - * which corresponds to whether the script's letters have Bidi_Class=R or AL. - * - * Returns true for "ar" and "en-Hebr", false for "zh" and "fa-Cyrl". - * - * @param locale input locale ID - * @return true if the locale's script is written right-to-left - * @stable ICU 54 - */ -U_CAPI UBool U_EXPORT2 -uloc_isRightToLeft(const char *locale); - -/** - * enums for the return value for the character and line orientation - * functions. - * @stable ICU 4.0 - */ -typedef enum { - ULOC_LAYOUT_LTR = 0, /* left-to-right. */ - ULOC_LAYOUT_RTL = 1, /* right-to-left. */ - ULOC_LAYOUT_TTB = 2, /* top-to-bottom. */ - ULOC_LAYOUT_BTT = 3, /* bottom-to-top. */ - ULOC_LAYOUT_UNKNOWN -} ULayoutType; - -/** - * Get the layout character orientation for the specified locale. - * - * @param localeId locale name - * @param status Error status - * @return an enum indicating the layout orientation for characters. - * @stable ICU 4.0 - */ -U_CAPI ULayoutType U_EXPORT2 -uloc_getCharacterOrientation(const char* localeId, - UErrorCode *status); - -/** - * Get the layout line orientation for the specified locale. - * - * @param localeId locale name - * @param status Error status - * @return an enum indicating the layout orientation for lines. - * @stable ICU 4.0 - */ -U_CAPI ULayoutType U_EXPORT2 -uloc_getLineOrientation(const char* localeId, - UErrorCode *status); - -/** - * Output values which uloc_acceptLanguage() writes to the 'outResult' parameter. - * - * @see uloc_acceptLanguageFromHTTP - * @see uloc_acceptLanguage - * @stable ICU 3.2 - */ -typedef enum { - /** - * No exact match was found. - * @stable ICU 3.2 - */ - ULOC_ACCEPT_FAILED = 0, - /** - * An exact match was found. - * @stable ICU 3.2 - */ - ULOC_ACCEPT_VALID = 1, - /** - * A fallback was found. For example, the Accept-Language list includes 'ja_JP' - * and is matched with available locale 'ja'. - * @stable ICU 3.2 - */ - ULOC_ACCEPT_FALLBACK = 2 /* */ -} UAcceptResult; - -/** - * Based on a HTTP header from a web browser and a list of available locales, - * determine an acceptable locale for the user. - * - * This is a thin wrapper over C++ class LocaleMatcher. - * - * @param result - buffer to accept the result locale - * @param resultAvailable the size of the result buffer. - * @param outResult - An out parameter that contains the fallback status - * @param httpAcceptLanguage - "Accept-Language:" header as per HTTP. - * @param availableLocales - list of available locales to match - * @param status 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.) - * @return length needed for the locale. - * @stable ICU 3.2 - */ -U_CAPI int32_t U_EXPORT2 -uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable, - UAcceptResult *outResult, - const char *httpAcceptLanguage, - UEnumeration* availableLocales, - UErrorCode *status); - -/** - * Based on a list of available locales, - * determine an acceptable locale for the user. - * - * This is a thin wrapper over C++ class LocaleMatcher. - * - * @param result - buffer to accept the result locale - * @param resultAvailable the size of the result buffer. - * @param outResult - An out parameter that contains the fallback status - * @param acceptList - list of acceptable languages - * @param acceptListCount - count of acceptList items - * @param availableLocales - list of available locales to match - * @param status 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.) - * @return length needed for the locale. - * @stable ICU 3.2 - */ -U_CAPI int32_t U_EXPORT2 -uloc_acceptLanguage(char *result, int32_t resultAvailable, - UAcceptResult *outResult, const char **acceptList, - int32_t acceptListCount, - UEnumeration* availableLocales, - UErrorCode *status); - - -/** - * Gets the ICU locale ID for the specified Win32 LCID value. - * - * @param hostID the Win32 LCID to translate - * @param locale the output buffer for the ICU locale ID, which will be NUL-terminated - * if there is room. - * @param localeCapacity the size of the output buffer - * @param status an error is returned if the LCID is unrecognized or the output buffer - * is too small - * @return actual the actual size of the locale ID, not including NUL-termination - * @stable ICU 3.8 - */ -U_CAPI int32_t U_EXPORT2 -uloc_getLocaleForLCID(uint32_t hostID, char *locale, int32_t localeCapacity, - UErrorCode *status); - - -/** - * Add the likely subtags for a provided locale ID, per the algorithm described - * in the following CLDR technical report: - * - * http://www.unicode.org/reports/tr35/#Likely_Subtags - * - * If localeID is already in the maximal form, or there is no data available - * for maximization, it will be copied to the output buffer. For example, - * "und-Zzzz" cannot be maximized, since there is no reasonable maximization. - * - * Examples: - * - * "en" maximizes to "en_Latn_US" - * - * "de" maximizes to "de_Latn_US" - * - * "sr" maximizes to "sr_Cyrl_RS" - * - * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.) - * - * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.) - * - * @param localeID The locale to maximize - * @param maximizedLocaleID The maximized locale - * @param maximizedLocaleIDCapacity The capacity of the maximizedLocaleID buffer - * @param err Error information if maximizing the locale failed. If the length - * of the localeID and the null-terminator is greater than the maximum allowed size, - * or the localeId is not well-formed, the error code is U_ILLEGAL_ARGUMENT_ERROR. - * @return The actual buffer size needed for the maximized locale. If it's - * greater than maximizedLocaleIDCapacity, the returned ID will be truncated. - * On error, the return value is -1. - * @stable ICU 4.0 - */ -U_CAPI int32_t U_EXPORT2 -uloc_addLikelySubtags(const char* localeID, - char* maximizedLocaleID, - int32_t maximizedLocaleIDCapacity, - UErrorCode* err); - - -/** - * Minimize the subtags for a provided locale ID, per the algorithm described - * in the following CLDR technical report: - * - * http://www.unicode.org/reports/tr35/#Likely_Subtags - * - * If localeID is already in the minimal form, or there is no data available - * for minimization, it will be copied to the output buffer. Since the - * minimization algorithm relies on proper maximization, see the comments - * for uloc_addLikelySubtags for reasons why there might not be any data. - * - * Examples: - * - * "en_Latn_US" minimizes to "en" - * - * "de_Latn_US" minimizes to "de" - * - * "sr_Cyrl_RS" minimizes to "sr" - * - * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the - * script, and minimizing to "zh" would imply "zh_Hans_CN".) - * - * @param localeID The locale to minimize - * @param minimizedLocaleID The minimized locale - * @param minimizedLocaleIDCapacity The capacity of the minimizedLocaleID buffer - * @param err Error information if minimizing the locale failed. If the length - * of the localeID and the null-terminator is greater than the maximum allowed size, - * or the localeId is not well-formed, the error code is U_ILLEGAL_ARGUMENT_ERROR. - * @return The actual buffer size needed for the minimized locale. If it's - * greater than minimizedLocaleIDCapacity, the returned ID will be truncated. - * On error, the return value is -1. - * @stable ICU 4.0 - */ -U_CAPI int32_t U_EXPORT2 -uloc_minimizeSubtags(const char* localeID, - char* minimizedLocaleID, - int32_t minimizedLocaleIDCapacity, - UErrorCode* err); - -/** - * Returns a locale ID for the specified BCP47 language tag string. - * If the specified language tag contains any ill-formed subtags, - * the first such subtag and all following subtags are ignored. - *

- * This implements the 'Language-Tag' production of BCP 47, and so - * supports legacy language tags (marked as “Type: grandfathered” in BCP 47) - * (regular and irregular) as well as private use language tags. - * - * Private use tags are represented as 'x-whatever', - * and legacy tags are converted to their canonical replacements where they exist. - * - * Note that a few legacy tags have no modern replacement; - * these will be converted using the fallback described in - * the first paragraph, so some information might be lost. - * - * @param langtag the input BCP47 language tag. - * @param localeID the output buffer receiving a locale ID for the - * specified BCP47 language tag. - * @param localeIDCapacity the size of the locale ID output buffer. - * @param parsedLength if not NULL, successfully parsed length - * for the input language tag is set. - * @param err error information if receiving the locald ID - * failed. - * @return the length of the locale ID. - * @stable ICU 4.2 - */ -U_CAPI int32_t U_EXPORT2 -uloc_forLanguageTag(const char* langtag, - char* localeID, - int32_t localeIDCapacity, - int32_t* parsedLength, - UErrorCode* err); - -/** - * Returns a well-formed language tag for this locale ID. - *

- * Note: When strict is false, any locale - * fields which do not satisfy the BCP47 syntax requirement will - * be omitted from the result. When strict is - * true, this function sets U_ILLEGAL_ARGUMENT_ERROR to the - * err if any locale fields do not satisfy the - * BCP47 syntax requirement. - * @param localeID the input locale ID - * @param langtag the output buffer receiving BCP47 language - * tag for the locale ID. - * @param langtagCapacity the size of the BCP47 language tag - * output buffer. - * @param strict boolean value indicating if the function returns - * an error for an ill-formed input locale ID. - * @param err error information if receiving the language - * tag failed. - * @return The length of the BCP47 language tag. - * @stable ICU 4.2 - */ -U_CAPI int32_t U_EXPORT2 -uloc_toLanguageTag(const char* localeID, - char* langtag, - int32_t langtagCapacity, - UBool strict, - UErrorCode* err); - -/** - * Converts the specified keyword (legacy key, or BCP 47 Unicode locale - * extension key) to the equivalent BCP 47 Unicode locale extension key. - * For example, BCP 47 Unicode locale extension key "co" is returned for - * the input keyword "collation". - *

- * When the specified keyword is unknown, but satisfies the BCP syntax, - * then the pointer to the input keyword itself will be returned. - * For example, - * uloc_toUnicodeLocaleKey("ZZ") returns "ZZ". - * - * @param keyword the input locale keyword (either legacy key - * such as "collation" or BCP 47 Unicode locale extension - * key such as "co"). - * @return the well-formed BCP 47 Unicode locale extension key, - * or NULL if the specified locale keyword cannot be - * mapped to a well-formed BCP 47 Unicode locale extension - * key. - * @see uloc_toLegacyKey - * @stable ICU 54 - */ -U_CAPI const char* U_EXPORT2 -uloc_toUnicodeLocaleKey(const char* keyword); - -/** - * Converts the specified keyword value (legacy type, or BCP 47 - * Unicode locale extension type) to the well-formed BCP 47 Unicode locale - * extension type for the specified keyword (category). For example, BCP 47 - * Unicode locale extension type "phonebk" is returned for the input - * keyword value "phonebook", with the keyword "collation" (or "co"). - *

- * When the specified keyword is not recognized, but the specified value - * satisfies the syntax of the BCP 47 Unicode locale extension type, - * or when the specified keyword allows 'variable' type and the specified - * value satisfies the syntax, then the pointer to the input type value itself - * will be returned. - * For example, - * uloc_toUnicodeLocaleType("Foo", "Bar") returns "Bar", - * uloc_toUnicodeLocaleType("variableTop", "00A4") returns "00A4". - * - * @param keyword the locale keyword (either legacy key such as - * "collation" or BCP 47 Unicode locale extension - * key such as "co"). - * @param value the locale keyword value (either legacy type - * such as "phonebook" or BCP 47 Unicode locale extension - * type such as "phonebk"). - * @return the well-formed BCP47 Unicode locale extension type, - * or NULL if the locale keyword value cannot be mapped to - * a well-formed BCP 47 Unicode locale extension type. - * @see uloc_toLegacyType - * @stable ICU 54 - */ -U_CAPI const char* U_EXPORT2 -uloc_toUnicodeLocaleType(const char* keyword, const char* value); - -/** - * Converts the specified keyword (BCP 47 Unicode locale extension key, or - * legacy key) to the legacy key. For example, legacy key "collation" is - * returned for the input BCP 47 Unicode locale extension key "co". - * - * @param keyword the input locale keyword (either BCP 47 Unicode locale - * extension key or legacy key). - * @return the well-formed legacy key, or NULL if the specified - * keyword cannot be mapped to a well-formed legacy key. - * @see toUnicodeLocaleKey - * @stable ICU 54 - */ -U_CAPI const char* U_EXPORT2 -uloc_toLegacyKey(const char* keyword); - -/** - * Converts the specified keyword value (BCP 47 Unicode locale extension type, - * or legacy type or type alias) to the canonical legacy type. For example, - * the legacy type "phonebook" is returned for the input BCP 47 Unicode - * locale extension type "phonebk" with the keyword "collation" (or "co"). - *

- * When the specified keyword is not recognized, but the specified value - * satisfies the syntax of legacy key, or when the specified keyword - * allows 'variable' type and the specified value satisfies the syntax, - * then the pointer to the input type value itself will be returned. - * For example, - * uloc_toLegacyType("Foo", "Bar") returns "Bar", - * uloc_toLegacyType("vt", "00A4") returns "00A4". - * - * @param keyword the locale keyword (either legacy keyword such as - * "collation" or BCP 47 Unicode locale extension - * key such as "co"). - * @param value the locale keyword value (either BCP 47 Unicode locale - * extension type such as "phonebk" or legacy keyword value - * such as "phonebook"). - * @return the well-formed legacy type, or NULL if the specified - * keyword value cannot be mapped to a well-formed legacy - * type. - * @see toUnicodeLocaleType - * @stable ICU 54 - */ -U_CAPI const char* U_EXPORT2 -uloc_toLegacyType(const char* keyword, const char* value); - -#endif /*_ULOC*/ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1997-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* File ULOC.H +* +* Modification History: +* +* Date Name Description +* 04/01/97 aliu Creation. +* 08/22/98 stephen JDK 1.2 sync. +* 12/08/98 rtg New C API for Locale +* 03/30/99 damiba overhaul +* 03/31/99 helena Javadoc for uloc functions. +* 04/15/99 Madhu Updated Javadoc +******************************************************************************** +*/ + +#ifndef ULOC_H +#define ULOC_H + +#include "unicode/utypes.h" +#include "unicode/uenum.h" + +/** + * \file + * \brief C API: Locale ID functionality similar to C++ class Locale + * + *

ULoc C API for Locale

+ * A Locale represents a specific geographical, political, + * or cultural region. An operation that requires a Locale to perform + * its task is called locale-sensitive and uses the Locale + * to tailor information for the user. For example, displaying a number + * is a locale-sensitive operation--the number should be formatted + * according to the customs/conventions of the user's native country, + * region, or culture. In the C APIs, a locales is simply a const char string. + * + *

+ * You create a Locale with one of the three options listed below. + * Each of the component is separated by '_' in the locale string. + * \htmlonly

\endhtmlonly + *
+ * \code
+ *       newLanguage
+ * 
+ *       newLanguage + newCountry
+ * 
+ *       newLanguage + newCountry + newVariant
+ * \endcode
+ * 
+ * \htmlonly
\endhtmlonly + * The first option is a valid ISO + * Language Code. These codes are the lower-case two-letter + * codes as defined by ISO-639. + * You can find a full list of these codes at a number of sites, such as: + *
+ * http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt + * + *

+ * The second option includes an additional ISO Country + * Code. These codes are the upper-case two-letter codes + * as defined by ISO-3166. + * You can find a full list of these codes at a number of sites, such as: + *
+ * http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html + * + *

+ * The third option requires another additional information--the + * Variant. + * The Variant codes are vendor and browser-specific. + * For example, use WIN for Windows, MAC for Macintosh, and POSIX for POSIX. + * Where there are two variants, separate them with an underscore, and + * put the most important one first. For + * example, a Traditional Spanish collation might be referenced, with + * "ES", "ES", "Traditional_WIN". + * + *

+ * Because a Locale is just an identifier for a region, + * no validity check is performed when you specify a Locale. + * If you want to see whether particular resources are available for the + * Locale you asked for, you must query those resources. For + * example, ask the UNumberFormat for the locales it supports + * using its getAvailable method. + *
Note: When you ask for a resource for a particular + * locale, you get back the best available match, not necessarily + * precisely what you asked for. For more information, look at + * UResourceBundle. + * + *

+ * The Locale provides a number of convenient constants + * that you can use to specify the commonly used + * locales. For example, the following refers to a locale + * for the United States: + * \htmlonly

\endhtmlonly + *
+ * \code
+ *       ULOC_US
+ * \endcode
+ * 
+ * \htmlonly
\endhtmlonly + * + *

+ * Once you've specified a locale you can query it for information about + * itself. Use uloc_getCountry to get the ISO Country Code and + * uloc_getLanguage to get the ISO Language Code. You can + * use uloc_getDisplayCountry to get the + * name of the country suitable for displaying to the user. Similarly, + * you can use uloc_getDisplayLanguage to get the name of + * the language suitable for displaying to the user. Interestingly, + * the uloc_getDisplayXXX methods are themselves locale-sensitive + * and have two versions: one that uses the default locale and one + * that takes a locale as an argument and displays the name or country in + * a language appropriate to that locale. + * + *

+ * The ICU provides a number of services that perform locale-sensitive + * operations. For example, the unum_xxx functions format + * numbers, currency, or percentages in a locale-sensitive manner. + *

+ * \htmlonly
\endhtmlonly + *
+ * \code
+ *     UErrorCode success = U_ZERO_ERROR;
+ *     UNumberFormat *nf;
+ *     const char* myLocale = "fr_FR";
+ * 
+ *     nf = unum_open( UNUM_DEFAULT, NULL, success );          
+ *     unum_close(nf);
+ *     nf = unum_open( UNUM_CURRENCY, NULL, success );
+ *     unum_close(nf);
+ *     nf = unum_open( UNUM_PERCENT, NULL, success );   
+ *     unum_close(nf);
+ * \endcode
+ * 
+ * \htmlonly
\endhtmlonly + * Each of these methods has two variants; one with an explicit locale + * and one without; the latter using the default locale. + * \htmlonly
\endhtmlonly + *
+ * \code 
+ * 
+ *     nf = unum_open( UNUM_DEFAULT, myLocale, success );          
+ *     unum_close(nf);
+ *     nf = unum_open( UNUM_CURRENCY, myLocale, success );
+ *     unum_close(nf);
+ *     nf = unum_open( UNUM_PERCENT, myLocale, success );   
+ *     unum_close(nf);
+ * \endcode
+ * 
+ * \htmlonly
\endhtmlonly + * A Locale is the mechanism for identifying the kind of services + * (UNumberFormat) that you would like to get. The locale is + * just a mechanism for identifying these services. + * + *

+ * Each international service that performs locale-sensitive operations + * allows you + * to get all the available objects of that type. You can sift + * through these objects by language, country, or variant, + * and use the display names to present a menu to the user. + * For example, you can create a menu of all the collation objects + * suitable for a given language. Such classes implement these + * three class methods: + * \htmlonly

\endhtmlonly + *
+ * \code
+ *       const char* uloc_getAvailable(int32_t index);
+ *       int32_t uloc_countAvailable();
+ *       int32_t
+ *       uloc_getDisplayName(const char* localeID,
+ *                 const char* inLocaleID, 
+ *                 UChar* result,
+ *                 int32_t maxResultSize,
+ *                  UErrorCode* err);
+ * 
+ * \endcode
+ * 
+ * \htmlonly
\endhtmlonly + *

+ * Concerning POSIX/RFC1766 Locale IDs, + * the getLanguage/getCountry/getVariant/getName functions do understand + * the POSIX type form of language_COUNTRY.ENCODING\@VARIANT + * and if there is not an ICU-stype variant, uloc_getVariant() for example + * will return the one listed after the \@at sign. As well, the hyphen + * "-" is recognized as a country/variant separator similarly to RFC1766. + * So for example, "en-us" will be interpreted as en_US. + * As a result, uloc_getName() is far from a no-op, and will have the + * effect of converting POSIX/RFC1766 IDs into ICU form, although it does + * NOT map any of the actual codes (i.e. russian->ru) in any way. + * Applications should call uloc_getName() at the point where a locale ID + * is coming from an external source (user entry, OS, web browser) + * and pass the resulting string to other ICU functions. For example, + * don't use de-de\@EURO as an argument to resourcebundle. + * + * @see UResourceBundle + */ + +/** Useful constant for this language. @stable ICU 2.0 */ +#define ULOC_CHINESE "zh" +/** Useful constant for this language. @stable ICU 2.0 */ +#define ULOC_ENGLISH "en" +/** Useful constant for this language. @stable ICU 2.0 */ +#define ULOC_FRENCH "fr" +/** Useful constant for this language. @stable ICU 2.0 */ +#define ULOC_GERMAN "de" +/** Useful constant for this language. @stable ICU 2.0 */ +#define ULOC_ITALIAN "it" +/** Useful constant for this language. @stable ICU 2.0 */ +#define ULOC_JAPANESE "ja" +/** Useful constant for this language. @stable ICU 2.0 */ +#define ULOC_KOREAN "ko" +/** Useful constant for this language. @stable ICU 2.0 */ +#define ULOC_SIMPLIFIED_CHINESE "zh_CN" +/** Useful constant for this language. @stable ICU 2.0 */ +#define ULOC_TRADITIONAL_CHINESE "zh_TW" + +/** Useful constant for this country/region. @stable ICU 2.0 */ +#define ULOC_CANADA "en_CA" +/** Useful constant for this country/region. @stable ICU 2.0 */ +#define ULOC_CANADA_FRENCH "fr_CA" +/** Useful constant for this country/region. @stable ICU 2.0 */ +#define ULOC_CHINA "zh_CN" +/** Useful constant for this country/region. @stable ICU 2.0 */ +#define ULOC_PRC "zh_CN" +/** Useful constant for this country/region. @stable ICU 2.0 */ +#define ULOC_FRANCE "fr_FR" +/** Useful constant for this country/region. @stable ICU 2.0 */ +#define ULOC_GERMANY "de_DE" +/** Useful constant for this country/region. @stable ICU 2.0 */ +#define ULOC_ITALY "it_IT" +/** Useful constant for this country/region. @stable ICU 2.0 */ +#define ULOC_JAPAN "ja_JP" +/** Useful constant for this country/region. @stable ICU 2.0 */ +#define ULOC_KOREA "ko_KR" +/** Useful constant for this country/region. @stable ICU 2.0 */ +#define ULOC_TAIWAN "zh_TW" +/** Useful constant for this country/region. @stable ICU 2.0 */ +#define ULOC_UK "en_GB" +/** Useful constant for this country/region. @stable ICU 2.0 */ +#define ULOC_US "en_US" + +/** + * Useful constant for the maximum size of the language part of a locale ID. + * (including the terminating NULL). + * @stable ICU 2.0 + */ +#define ULOC_LANG_CAPACITY 12 + +/** + * Useful constant for the maximum size of the country part of a locale ID + * (including the terminating NULL). + * @stable ICU 2.0 + */ +#define ULOC_COUNTRY_CAPACITY 4 +/** + * Useful constant for the maximum size of the whole locale ID + * (including the terminating NULL and all keywords). + * @stable ICU 2.0 + */ +#define ULOC_FULLNAME_CAPACITY 157 + +/** + * Useful constant for the maximum size of the script part of a locale ID + * (including the terminating NULL). + * @stable ICU 2.8 + */ +#define ULOC_SCRIPT_CAPACITY 6 + +/** + * Useful constant for the maximum size of keywords in a locale + * @stable ICU 2.8 + */ +#define ULOC_KEYWORDS_CAPACITY 96 + +/** + * Useful constant for the maximum total size of keywords and their values in a locale + * @stable ICU 2.8 + */ +#define ULOC_KEYWORD_AND_VALUES_CAPACITY 100 + +/** + * Invariant character separating keywords from the locale string + * @stable ICU 2.8 + */ +#define ULOC_KEYWORD_SEPARATOR '@' + +/** + * Unicode code point for '@' separating keywords from the locale string. + * @see ULOC_KEYWORD_SEPARATOR + * @stable ICU 4.6 + */ +#define ULOC_KEYWORD_SEPARATOR_UNICODE 0x40 + +/** + * Invariant character for assigning value to a keyword + * @stable ICU 2.8 + */ +#define ULOC_KEYWORD_ASSIGN '=' + +/** + * Unicode code point for '=' for assigning value to a keyword. + * @see ULOC_KEYWORD_ASSIGN + * @stable ICU 4.6 + */ +#define ULOC_KEYWORD_ASSIGN_UNICODE 0x3D + +/** + * Invariant character separating keywords + * @stable ICU 2.8 + */ +#define ULOC_KEYWORD_ITEM_SEPARATOR ';' + +/** + * Unicode code point for ';' separating keywords + * @see ULOC_KEYWORD_ITEM_SEPARATOR + * @stable ICU 4.6 + */ +#define ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE 0x3B + +/** + * Constants for *_getLocale() + * Allow user to select whether she wants information on + * requested, valid or actual locale. + * For example, a collator for "en_US_CALIFORNIA" was + * requested. In the current state of ICU (2.0), + * the requested locale is "en_US_CALIFORNIA", + * the valid locale is "en_US" (most specific locale supported by ICU) + * and the actual locale is "root" (the collation data comes unmodified + * from the UCA) + * The locale is considered supported by ICU if there is a core ICU bundle + * for that locale (although it may be empty). + * @stable ICU 2.1 + */ +typedef enum { + /** This is locale the data actually comes from + * @stable ICU 2.1 + */ + ULOC_ACTUAL_LOCALE = 0, + /** This is the most specific locale supported by ICU + * @stable ICU 2.1 + */ + ULOC_VALID_LOCALE = 1, + +#ifndef U_HIDE_DEPRECATED_API + /** This is the requested locale + * @deprecated ICU 2.8 + */ + ULOC_REQUESTED_LOCALE = 2, + + /** + * One more than the highest normal ULocDataLocaleType value. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + ULOC_DATA_LOCALE_TYPE_LIMIT = 3 +#endif // U_HIDE_DEPRECATED_API +} ULocDataLocaleType; + +#ifndef U_HIDE_SYSTEM_API +/** + * Gets ICU's default locale. + * The returned string is a snapshot in time, and will remain valid + * and unchanged even when uloc_setDefault() is called. + * The returned storage is owned by ICU, and must not be altered or deleted + * by the caller. + * + * @return the ICU default locale + * @system + * @stable ICU 2.0 + */ +U_CAPI const char* U_EXPORT2 +uloc_getDefault(void); + +/** + * Sets ICU's default locale. + * By default (without calling this function), ICU's default locale will be based + * on information obtained from the underlying system environment. + *

+ * Changes to ICU's default locale do not propagate back to the + * system environment. + *

+ * Changes to ICU's default locale to not affect any ICU services that + * may already be open based on the previous default locale value. + * + * @param localeID the new ICU default locale. A value of NULL will try to get + * the system's default locale. + * @param status the error information if the setting of default locale fails + * @system + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +uloc_setDefault(const char* localeID, + UErrorCode* status); +#endif /* U_HIDE_SYSTEM_API */ + +/** + * Gets the language code for the specified locale. + * + * @param localeID the locale to get the ISO language code with + * @param language the language code for localeID + * @param languageCapacity the size of the language buffer to store the + * language code with + * @param err error information if retrieving the language code failed + * @return the actual buffer size needed for the language code. If it's greater + * than languageCapacity, the returned language code will be truncated. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getLanguage(const char* localeID, + char* language, + int32_t languageCapacity, + UErrorCode* err); + +/** + * Gets the script code for the specified locale. + * + * @param localeID the locale to get the ISO language code with + * @param script the language code for localeID + * @param scriptCapacity the size of the language buffer to store the + * language code with + * @param err error information if retrieving the language code failed + * @return the actual buffer size needed for the language code. If it's greater + * than scriptCapacity, the returned language code will be truncated. + * @stable ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getScript(const char* localeID, + char* script, + int32_t scriptCapacity, + UErrorCode* err); + +/** + * Gets the country code for the specified locale. + * + * @param localeID the locale to get the country code with + * @param country the country code for localeID + * @param countryCapacity the size of the country buffer to store the + * country code with + * @param err error information if retrieving the country code failed + * @return the actual buffer size needed for the country code. If it's greater + * than countryCapacity, the returned country code will be truncated. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getCountry(const char* localeID, + char* country, + int32_t countryCapacity, + UErrorCode* err); + +/** + * Gets the variant code for the specified locale. + * + * @param localeID the locale to get the variant code with + * @param variant the variant code for localeID + * @param variantCapacity the size of the variant buffer to store the + * variant code with + * @param err error information if retrieving the variant code failed + * @return the actual buffer size needed for the variant code. If it's greater + * than variantCapacity, the returned variant code will be truncated. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getVariant(const char* localeID, + char* variant, + int32_t variantCapacity, + UErrorCode* err); + + +/** + * Gets the full name for the specified locale. + * Note: This has the effect of 'canonicalizing' the ICU locale ID to + * a certain extent. Upper and lower case are set as needed. + * It does NOT map aliased names in any way. + * See the top of this header file. + * This API supports preflighting. + * + * @param localeID the locale to get the full name with + * @param name fill in buffer for the name without keywords. + * @param nameCapacity capacity of the fill in buffer. + * @param err error information if retrieving the full name failed + * @return the actual buffer size needed for the full name. If it's greater + * than nameCapacity, the returned full name will be truncated. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getName(const char* localeID, + char* name, + int32_t nameCapacity, + UErrorCode* err); + +/** + * Gets the full name for the specified locale. + * Note: This has the effect of 'canonicalizing' the string to + * a certain extent. Upper and lower case are set as needed, + * and if the components were in 'POSIX' format they are changed to + * ICU format. It does NOT map aliased names in any way. + * See the top of this header file. + * + * @param localeID the locale to get the full name with + * @param name the full name for localeID + * @param nameCapacity the size of the name buffer to store the + * full name with + * @param err error information if retrieving the full name failed + * @return the actual buffer size needed for the full name. If it's greater + * than nameCapacity, the returned full name will be truncated. + * @stable ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +uloc_canonicalize(const char* localeID, + char* name, + int32_t nameCapacity, + UErrorCode* err); + +/** + * Gets the ISO language code for the specified locale. + * + * @param localeID the locale to get the ISO language code with + * @return language the ISO language code for localeID + * @stable ICU 2.0 + */ +U_CAPI const char* U_EXPORT2 +uloc_getISO3Language(const char* localeID); + + +/** + * Gets the ISO country code for the specified locale. + * + * @param localeID the locale to get the ISO country code with + * @return country the ISO country code for localeID + * @stable ICU 2.0 + */ +U_CAPI const char* U_EXPORT2 +uloc_getISO3Country(const char* localeID); + +/** + * Gets the Win32 LCID value for the specified locale. + * If the ICU locale is not recognized by Windows, 0 will be returned. + * + * LCIDs were deprecated with Windows Vista and Microsoft recommends + * that developers use BCP47 style tags instead (uloc_toLanguageTag). + * + * @param localeID the locale to get the Win32 LCID value with + * @return country the Win32 LCID for localeID + * @stable ICU 2.0 + */ +U_CAPI uint32_t U_EXPORT2 +uloc_getLCID(const char* localeID); + +/** + * Gets the language name suitable for display for the specified locale. + * + * @param locale the locale to get the ISO language code with + * @param displayLocale Specifies the locale to be used to display the name. In + * other words, if the locale's language code is "en", passing + * Locale::getFrench() for inLocale would result in "Anglais", + * while passing Locale::getGerman() for inLocale would result + * in "Englisch". + * @param language the displayable language code for localeID + * @param languageCapacity the size of the language buffer to store the + * displayable language code with. + * @param status error information if retrieving the displayable language code + * failed. U_USING_DEFAULT_WARNING indicates that no data was + * found from the locale resources and a case canonicalized + * language code is placed into language as fallback. + * @return the actual buffer size needed for the displayable language code. If + * it's greater than languageCapacity, the returned language + * code will be truncated. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayLanguage(const char* locale, + const char* displayLocale, + UChar* language, + int32_t languageCapacity, + UErrorCode* status); + +/** + * Gets the script name suitable for display for the specified locale. + * + * @param locale the locale to get the displayable script code with. NULL may be + * used to specify the default. + * @param displayLocale Specifies the locale to be used to display the name. In + * other words, if the locale's language code is "en", passing + * Locale::getFrench() for inLocale would result in "", while + * passing Locale::getGerman() for inLocale would result in "". + * NULL may be used to specify the default. + * @param script the displayable script for the localeID. + * @param scriptCapacity the size of the script buffer to store the displayable + * script code with. + * @param status error information if retrieving the displayable script code + * failed. U_USING_DEFAULT_WARNING indicates that no data was + * found from the locale resources and a case canonicalized + * script code is placed into script as fallback. + * @return the actual buffer size needed for the displayable script code. If + * it's greater than scriptCapacity, the returned displayable + * script code will be truncated. + * @stable ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayScript(const char* locale, + const char* displayLocale, + UChar* script, + int32_t scriptCapacity, + UErrorCode* status); + +/** + * Gets the country name suitable for display for the specified locale. + * Warning: this is for the region part of a valid locale ID; it cannot just be + * the region code (like "FR"). To get the display name for a region alone, or + * for other options, use ULocaleDisplayNames instead. + * + * @param locale the locale to get the displayable country code with. NULL may + * be used to specify the default. + * @param displayLocale Specifies the locale to be used to display the name. In + * other words, if the locale's language code is "en", passing + * Locale::getFrench() for inLocale would result in "Anglais", + * while passing Locale::getGerman() for inLocale would result + * in "Englisch". NULL may be used to specify the default. + * @param country the displayable country code for localeID. + * @param countryCapacity the size of the country buffer to store the + * displayable country code with. + * @param status error information if retrieving the displayable country code + * failed. U_USING_DEFAULT_WARNING indicates that no data was + * found from the locale resources and a case canonicalized + * country code is placed into country as fallback. + * @return the actual buffer size needed for the displayable country code. If + * it's greater than countryCapacity, the returned displayable + * country code will be truncated. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayCountry(const char* locale, + const char* displayLocale, + UChar* country, + int32_t countryCapacity, + UErrorCode* status); + + +/** + * Gets the variant name suitable for display for the specified locale. + * + * @param locale the locale to get the displayable variant code with. NULL may + * be used to specify the default. + * @param displayLocale Specifies the locale to be used to display the name. In + * other words, if the locale's language code is "en", passing + * Locale::getFrench() for inLocale would result in "Anglais", + * while passing Locale::getGerman() for inLocale would result + * in "Englisch". NULL may be used to specify the default. + * @param variant the displayable variant code for localeID. + * @param variantCapacity the size of the variant buffer to store the + * displayable variant code with. + * @param status error information if retrieving the displayable variant code + * failed. U_USING_DEFAULT_WARNING indicates that no data was + * found from the locale resources and a case canonicalized + * variant code is placed into variant as fallback. + * @return the actual buffer size needed for the displayable variant code. If + * it's greater than variantCapacity, the returned displayable + * variant code will be truncated. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayVariant(const char* locale, + const char* displayLocale, + UChar* variant, + int32_t variantCapacity, + UErrorCode* status); + +/** + * Gets the keyword name suitable for display for the specified locale. E.g: + * for the locale string de_DE\@collation=PHONEBOOK, this API gets the display + * string for the keyword collation. + * Usage: + * + * UErrorCode status = U_ZERO_ERROR; + * const char* keyword =NULL; + * int32_t keywordLen = 0; + * int32_t keywordCount = 0; + * UChar displayKeyword[256]; + * int32_t displayKeywordLen = 0; + * UEnumeration* keywordEnum = uloc_openKeywords("de_DE@collation=PHONEBOOK;calendar=TRADITIONAL", &status); + * for(keywordCount = uenum_count(keywordEnum, &status); keywordCount > 0 ; keywordCount--){ + * if(U_FAILURE(status)){ + * ...something went wrong so handle the error... + * break; + * } + * // the uenum_next returns NUL terminated string + * keyword = uenum_next(keywordEnum, &keywordLen, &status); + * displayKeywordLen = uloc_getDisplayKeyword(keyword, "en_US", displayKeyword, 256); + * ... do something interesting ..... + * } + * uenum_close(keywordEnum); + * + * @param keyword The keyword whose display string needs to be returned. + * @param displayLocale Specifies the locale to be used to display the name. In other words, + * if the locale's language code is "en", passing Locale::getFrench() for + * inLocale would result in "Anglais", while passing Locale::getGerman() + * for inLocale would result in "Englisch". NULL may be used to specify the default. + * @param dest the buffer to which the displayable keyword should be written. + * @param destCapacity The size of the buffer (number of UChars). If it is 0, then + * dest may be NULL and the function will only return the length of the + * result without writing any of the result string (pre-flighting). + * @param status error information if retrieving the displayable string failed. + * Should not be NULL and should not indicate failure on entry. + * U_USING_DEFAULT_WARNING indicates that no data was found from the locale + * resources and the keyword is placed into dest as fallback. + * @return the actual buffer size needed for the displayable variant code. + * @see #uloc_openKeywords + * @stable ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayKeyword(const char* keyword, + const char* displayLocale, + UChar* dest, + int32_t destCapacity, + UErrorCode* status); +/** + * Gets the value of the keyword suitable for display for the specified locale. + * E.g: for the locale string de_DE\@collation=PHONEBOOK, this API gets the display + * string for PHONEBOOK, in the display locale, when "collation" is specified as the keyword. + * + * @param locale The locale to get the displayable variant code with. NULL may be used to specify the default. + * @param keyword The keyword for whose value should be used. + * @param displayLocale Specifies the locale to be used to display the name. In other words, + * if the locale's language code is "en", passing Locale::getFrench() for + * inLocale would result in "Anglais", while passing Locale::getGerman() + * for inLocale would result in "Englisch". NULL may be used to specify the default. + * @param dest the buffer to which the displayable keyword should be written. + * @param destCapacity The size of the buffer (number of UChars). If it is 0, then + * dest may be NULL and the function will only return the length of the + * result without writing any of the result string (pre-flighting). + * @param status error information if retrieving the displayable string failed. + * Should not be NULL and must not indicate failure on entry. + * U_USING_DEFAULT_WARNING indicates that no data was found from the locale + * resources and the value of the keyword is placed into dest as fallback. + * @return the actual buffer size needed for the displayable variant code. + * @stable ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayKeywordValue( const char* locale, + const char* keyword, + const char* displayLocale, + UChar* dest, + int32_t destCapacity, + UErrorCode* status); +/** + * Gets the full name suitable for display for the specified locale. + * + * @param localeID the locale to get the displayable name with. NULL may be used to specify the default. + * @param inLocaleID Specifies the locale to be used to display the name. In other words, + * if the locale's language code is "en", passing Locale::getFrench() for + * inLocale would result in "Anglais", while passing Locale::getGerman() + * for inLocale would result in "Englisch". NULL may be used to specify the default. + * @param result the displayable name for localeID + * @param maxResultSize the size of the name buffer to store the + * displayable full name with + * @param err error information if retrieving the displayable name failed + * @return the actual buffer size needed for the displayable name. If it's greater + * than maxResultSize, the returned displayable name will be truncated. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getDisplayName(const char* localeID, + const char* inLocaleID, + UChar* result, + int32_t maxResultSize, + UErrorCode* err); + + +/** + * Gets the specified locale from a list of available locales. + * + * This method corresponds to uloc_openAvailableByType called with the + * ULOC_AVAILABLE_DEFAULT type argument. + * + * The return value is a pointer to an item of a locale name array. Both this + * array and the pointers it contains are owned by ICU and should not be + * deleted or written through by the caller. The locale name is terminated by + * a null pointer. + * + * @param n the specific locale name index of the available locale list; + * should not exceed the number returned by uloc_countAvailable. + * @return a specified locale name of all available locales + * @stable ICU 2.0 + */ +U_CAPI const char* U_EXPORT2 +uloc_getAvailable(int32_t n); + +/** + * Gets the size of the all available locale list. + * + * @return the size of the locale list + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 uloc_countAvailable(void); + +/** + * Types for uloc_getAvailableByType and uloc_countAvailableByType. + * + * @stable ICU 65 + */ +typedef enum ULocAvailableType { + /** + * Locales that return data when passed to ICU APIs, + * but not including legacy or alias locales. + * + * @stable ICU 65 + */ + ULOC_AVAILABLE_DEFAULT, + + /** + * Legacy or alias locales that return data when passed to ICU APIs. + * Examples of supported legacy or alias locales: + * + * - iw (alias to he) + * - mo (alias to ro) + * - zh_CN (alias to zh_Hans_CN) + * - sr_BA (alias to sr_Cyrl_BA) + * - ars (alias to ar_SA) + * + * The locales in this set are disjoint from the ones in + * ULOC_AVAILABLE_DEFAULT. To get both sets at the same time, use + * ULOC_AVAILABLE_WITH_LEGACY_ALIASES. + * + * @stable ICU 65 + */ + ULOC_AVAILABLE_ONLY_LEGACY_ALIASES, + + /** + * The union of the locales in ULOC_AVAILABLE_DEFAULT and + * ULOC_AVAILABLE_ONLY_LEGACY_ALIAS. + * + * @stable ICU 65 + */ + ULOC_AVAILABLE_WITH_LEGACY_ALIASES, + +#ifndef U_HIDE_INTERNAL_API + /** + * @internal + */ + ULOC_AVAILABLE_COUNT +#endif /* U_HIDE_INTERNAL_API */ +} ULocAvailableType; + +/** + * Gets a list of available locales according to the type argument, allowing + * the user to access different sets of supported locales in ICU. + * + * The returned UEnumeration must be closed by the caller. + * + * @param type Type choice from ULocAvailableType. + * @param status Set if an error occurred. + * @return a UEnumeration owned by the caller, or nullptr on failure. + * @stable ICU 65 + */ +U_CAPI UEnumeration* U_EXPORT2 +uloc_openAvailableByType(ULocAvailableType type, UErrorCode* status); + +/** + * + * Gets a list of all available 2-letter language codes defined in ISO 639, + * plus additional 3-letter codes determined to be useful for locale generation as + * defined by Unicode CLDR. This is a pointer + * to an array of pointers to arrays of char. All of these pointers are owned + * by ICU-- do not delete them, and do not write through them. The array is + * terminated with a null pointer. + * @return a list of all available language codes + * @stable ICU 2.0 + */ +U_CAPI const char* const* U_EXPORT2 +uloc_getISOLanguages(void); + +/** + * + * Gets a list of all available 2-letter country codes defined in ISO 639. This is a + * pointer to an array of pointers to arrays of char. All of these pointers are + * owned by ICU-- do not delete them, and do not write through them. The array is + * terminated with a null pointer. + * @return a list of all available country codes + * @stable ICU 2.0 + */ +U_CAPI const char* const* U_EXPORT2 +uloc_getISOCountries(void); + +/** + * Truncate the locale ID string to get the parent locale ID. + * Copies the part of the string before the last underscore. + * The parent locale ID will be an empty string if there is no + * underscore, or if there is only one underscore at localeID[0]. + * + * @param localeID Input locale ID string. + * @param parent Output string buffer for the parent locale ID. + * @param parentCapacity Size of the output buffer. + * @param err A UErrorCode value. + * @return The length of the parent locale ID. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getParent(const char* localeID, + char* parent, + int32_t parentCapacity, + UErrorCode* err); + + + + +/** + * Gets the full name for the specified locale, like uloc_getName(), + * but without keywords. + * + * Note: This has the effect of 'canonicalizing' the string to + * a certain extent. Upper and lower case are set as needed, + * and if the components were in 'POSIX' format they are changed to + * ICU format. It does NOT map aliased names in any way. + * See the top of this header file. + * + * This API strips off the keyword part, so "de_DE\@collation=phonebook" + * will become "de_DE". + * This API supports preflighting. + * + * @param localeID the locale to get the full name with + * @param name fill in buffer for the name without keywords. + * @param nameCapacity capacity of the fill in buffer. + * @param err error information if retrieving the full name failed + * @return the actual buffer size needed for the full name. If it's greater + * than nameCapacity, the returned full name will be truncated. + * @stable ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getBaseName(const char* localeID, + char* name, + int32_t nameCapacity, + UErrorCode* err); + +/** + * Gets an enumeration of keywords for the specified locale. Enumeration + * must get disposed of by the client using uenum_close function. + * + * @param localeID the locale to get the variant code with + * @param status error information if retrieving the keywords failed + * @return enumeration of keywords or NULL if there are no keywords. + * @stable ICU 2.8 + */ +U_CAPI UEnumeration* U_EXPORT2 +uloc_openKeywords(const char* localeID, + UErrorCode* status); + +/** + * Get the value for a keyword. Locale name does not need to be normalized. + * + * @param localeID locale name containing the keyword ("de_DE@currency=EURO;collation=PHONEBOOK") + * @param keywordName name of the keyword for which we want the value; must not be + * NULL or empty, and must consist only of [A-Za-z0-9]. Case insensitive. + * @param buffer receiving buffer + * @param bufferCapacity capacity of receiving buffer + * @param status containing error code: e.g. buffer not big enough or ill-formed localeID + * or keywordName parameters. + * @return the length of keyword value + * @stable ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getKeywordValue(const char* localeID, + const char* keywordName, + char* buffer, int32_t bufferCapacity, + UErrorCode* status); + + +/** + * Sets or removes the value of the specified keyword. + * + * For removing all keywords, use uloc_getBaseName(). + * + * NOTE: Unlike almost every other ICU function which takes a + * buffer, this function will NOT truncate the output text, and will + * not update the buffer with unterminated text setting a status of + * U_STRING_NOT_TERMINATED_WARNING. If a BUFFER_OVERFLOW_ERROR is received, + * it means a terminated version of the updated locale ID would not fit + * in the buffer, and the original buffer is untouched. This is done to + * prevent incorrect or possibly even malformed locales from being generated + * and used. + * + * @param keywordName name of the keyword to be set; must not be + * NULL or empty, and must consist only of [A-Za-z0-9]. Case insensitive. + * @param keywordValue value of the keyword to be set. If 0-length or + * NULL, will result in the keyword being removed; no error is given if + * that keyword does not exist. Otherwise, must consist only of + * [A-Za-z0-9] and [/_+-]. + * @param buffer input buffer containing well-formed locale ID to be + * modified. + * @param bufferCapacity capacity of receiving buffer + * @param status containing error code: e.g. buffer not big enough + * or ill-formed keywordName or keywordValue parameters, or ill-formed + * locale ID in buffer on input. + * @return the length needed for the buffer + * @see uloc_getKeywordValue + * @stable ICU 3.2 + */ +U_CAPI int32_t U_EXPORT2 +uloc_setKeywordValue(const char* keywordName, + const char* keywordValue, + char* buffer, int32_t bufferCapacity, + UErrorCode* status); + +/** + * Returns whether the locale's script is written right-to-left. + * If there is no script subtag, then the likely script is used, see uloc_addLikelySubtags(). + * If no likely script is known, then false is returned. + * + * A script is right-to-left according to the CLDR script metadata + * which corresponds to whether the script's letters have Bidi_Class=R or AL. + * + * Returns true for "ar" and "en-Hebr", false for "zh" and "fa-Cyrl". + * + * @param locale input locale ID + * @return true if the locale's script is written right-to-left + * @stable ICU 54 + */ +U_CAPI UBool U_EXPORT2 +uloc_isRightToLeft(const char *locale); + +/** + * enums for the return value for the character and line orientation + * functions. + * @stable ICU 4.0 + */ +typedef enum { + ULOC_LAYOUT_LTR = 0, /* left-to-right. */ + ULOC_LAYOUT_RTL = 1, /* right-to-left. */ + ULOC_LAYOUT_TTB = 2, /* top-to-bottom. */ + ULOC_LAYOUT_BTT = 3, /* bottom-to-top. */ + ULOC_LAYOUT_UNKNOWN +} ULayoutType; + +/** + * Get the layout character orientation for the specified locale. + * + * @param localeId locale name + * @param status Error status + * @return an enum indicating the layout orientation for characters. + * @stable ICU 4.0 + */ +U_CAPI ULayoutType U_EXPORT2 +uloc_getCharacterOrientation(const char* localeId, + UErrorCode *status); + +/** + * Get the layout line orientation for the specified locale. + * + * @param localeId locale name + * @param status Error status + * @return an enum indicating the layout orientation for lines. + * @stable ICU 4.0 + */ +U_CAPI ULayoutType U_EXPORT2 +uloc_getLineOrientation(const char* localeId, + UErrorCode *status); + +/** + * Output values which uloc_acceptLanguage() writes to the 'outResult' parameter. + * + * @see uloc_acceptLanguageFromHTTP + * @see uloc_acceptLanguage + * @stable ICU 3.2 + */ +typedef enum { + /** + * No exact match was found. + * @stable ICU 3.2 + */ + ULOC_ACCEPT_FAILED = 0, + /** + * An exact match was found. + * @stable ICU 3.2 + */ + ULOC_ACCEPT_VALID = 1, + /** + * A fallback was found. For example, the Accept-Language list includes 'ja_JP' + * and is matched with available locale 'ja'. + * @stable ICU 3.2 + */ + ULOC_ACCEPT_FALLBACK = 2 /* */ +} UAcceptResult; + +/** + * Based on a HTTP header from a web browser and a list of available locales, + * determine an acceptable locale for the user. + * + * This is a thin wrapper over C++ class LocaleMatcher. + * + * @param result - buffer to accept the result locale + * @param resultAvailable the size of the result buffer. + * @param outResult - An out parameter that contains the fallback status + * @param httpAcceptLanguage - "Accept-Language:" header as per HTTP. + * @param availableLocales - list of available locales to match + * @param status 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.) + * @return length needed for the locale. + * @stable ICU 3.2 + */ +U_CAPI int32_t U_EXPORT2 +uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable, + UAcceptResult *outResult, + const char *httpAcceptLanguage, + UEnumeration* availableLocales, + UErrorCode *status); + +/** + * Based on a list of available locales, + * determine an acceptable locale for the user. + * + * This is a thin wrapper over C++ class LocaleMatcher. + * + * @param result - buffer to accept the result locale + * @param resultAvailable the size of the result buffer. + * @param outResult - An out parameter that contains the fallback status + * @param acceptList - list of acceptable languages + * @param acceptListCount - count of acceptList items + * @param availableLocales - list of available locales to match + * @param status 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.) + * @return length needed for the locale. + * @stable ICU 3.2 + */ +U_CAPI int32_t U_EXPORT2 +uloc_acceptLanguage(char *result, int32_t resultAvailable, + UAcceptResult *outResult, const char **acceptList, + int32_t acceptListCount, + UEnumeration* availableLocales, + UErrorCode *status); + + +/** + * Gets the ICU locale ID for the specified Win32 LCID value. + * + * @param hostID the Win32 LCID to translate + * @param locale the output buffer for the ICU locale ID, which will be NUL-terminated + * if there is room. + * @param localeCapacity the size of the output buffer + * @param status an error is returned if the LCID is unrecognized or the output buffer + * is too small + * @return actual the actual size of the locale ID, not including NUL-termination + * @stable ICU 3.8 + */ +U_CAPI int32_t U_EXPORT2 +uloc_getLocaleForLCID(uint32_t hostID, char *locale, int32_t localeCapacity, + UErrorCode *status); + + +/** + * Add the likely subtags for a provided locale ID, per the algorithm described + * in the following CLDR technical report: + * + * http://www.unicode.org/reports/tr35/#Likely_Subtags + * + * If localeID is already in the maximal form, or there is no data available + * for maximization, it will be copied to the output buffer. For example, + * "und-Zzzz" cannot be maximized, since there is no reasonable maximization. + * + * Examples: + * + * "en" maximizes to "en_Latn_US" + * + * "de" maximizes to "de_Latn_US" + * + * "sr" maximizes to "sr_Cyrl_RS" + * + * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.) + * + * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.) + * + * @param localeID The locale to maximize + * @param maximizedLocaleID The maximized locale + * @param maximizedLocaleIDCapacity The capacity of the maximizedLocaleID buffer + * @param err Error information if maximizing the locale failed. If the length + * of the localeID and the null-terminator is greater than the maximum allowed size, + * or the localeId is not well-formed, the error code is U_ILLEGAL_ARGUMENT_ERROR. + * @return The actual buffer size needed for the maximized locale. If it's + * greater than maximizedLocaleIDCapacity, the returned ID will be truncated. + * On error, the return value is -1. + * @stable ICU 4.0 + */ +U_CAPI int32_t U_EXPORT2 +uloc_addLikelySubtags(const char* localeID, + char* maximizedLocaleID, + int32_t maximizedLocaleIDCapacity, + UErrorCode* err); + + +/** + * Minimize the subtags for a provided locale ID, per the algorithm described + * in the following CLDR technical report: + * + * http://www.unicode.org/reports/tr35/#Likely_Subtags + * + * If localeID is already in the minimal form, or there is no data available + * for minimization, it will be copied to the output buffer. Since the + * minimization algorithm relies on proper maximization, see the comments + * for uloc_addLikelySubtags for reasons why there might not be any data. + * + * Examples: + * + * "en_Latn_US" minimizes to "en" + * + * "de_Latn_US" minimizes to "de" + * + * "sr_Cyrl_RS" minimizes to "sr" + * + * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the + * script, and minimizing to "zh" would imply "zh_Hans_CN".) + * + * @param localeID The locale to minimize + * @param minimizedLocaleID The minimized locale + * @param minimizedLocaleIDCapacity The capacity of the minimizedLocaleID buffer + * @param err Error information if minimizing the locale failed. If the length + * of the localeID and the null-terminator is greater than the maximum allowed size, + * or the localeId is not well-formed, the error code is U_ILLEGAL_ARGUMENT_ERROR. + * @return The actual buffer size needed for the minimized locale. If it's + * greater than minimizedLocaleIDCapacity, the returned ID will be truncated. + * On error, the return value is -1. + * @stable ICU 4.0 + */ +U_CAPI int32_t U_EXPORT2 +uloc_minimizeSubtags(const char* localeID, + char* minimizedLocaleID, + int32_t minimizedLocaleIDCapacity, + UErrorCode* err); + +/** + * Returns a locale ID for the specified BCP47 language tag string. + * If the specified language tag contains any ill-formed subtags, + * the first such subtag and all following subtags are ignored. + *

+ * This implements the 'Language-Tag' production of BCP 47, and so + * supports legacy language tags (marked as “Type: grandfathered” in BCP 47) + * (regular and irregular) as well as private use language tags. + * + * Private use tags are represented as 'x-whatever', + * and legacy tags are converted to their canonical replacements where they exist. + * + * Note that a few legacy tags have no modern replacement; + * these will be converted using the fallback described in + * the first paragraph, so some information might be lost. + * + * @param langtag the input BCP47 language tag. + * @param localeID the output buffer receiving a locale ID for the + * specified BCP47 language tag. + * @param localeIDCapacity the size of the locale ID output buffer. + * @param parsedLength if not NULL, successfully parsed length + * for the input language tag is set. + * @param err error information if receiving the locald ID + * failed. + * @return the length of the locale ID. + * @stable ICU 4.2 + */ +U_CAPI int32_t U_EXPORT2 +uloc_forLanguageTag(const char* langtag, + char* localeID, + int32_t localeIDCapacity, + int32_t* parsedLength, + UErrorCode* err); + +/** + * Returns a well-formed language tag for this locale ID. + *

+ * Note: When strict is false, any locale + * fields which do not satisfy the BCP47 syntax requirement will + * be omitted from the result. When strict is + * true, this function sets U_ILLEGAL_ARGUMENT_ERROR to the + * err if any locale fields do not satisfy the + * BCP47 syntax requirement. + * @param localeID the input locale ID + * @param langtag the output buffer receiving BCP47 language + * tag for the locale ID. + * @param langtagCapacity the size of the BCP47 language tag + * output buffer. + * @param strict boolean value indicating if the function returns + * an error for an ill-formed input locale ID. + * @param err error information if receiving the language + * tag failed. + * @return The length of the BCP47 language tag. + * @stable ICU 4.2 + */ +U_CAPI int32_t U_EXPORT2 +uloc_toLanguageTag(const char* localeID, + char* langtag, + int32_t langtagCapacity, + UBool strict, + UErrorCode* err); + +/** + * Converts the specified keyword (legacy key, or BCP 47 Unicode locale + * extension key) to the equivalent BCP 47 Unicode locale extension key. + * For example, BCP 47 Unicode locale extension key "co" is returned for + * the input keyword "collation". + *

+ * When the specified keyword is unknown, but satisfies the BCP syntax, + * then the pointer to the input keyword itself will be returned. + * For example, + * uloc_toUnicodeLocaleKey("ZZ") returns "ZZ". + * + * @param keyword the input locale keyword (either legacy key + * such as "collation" or BCP 47 Unicode locale extension + * key such as "co"). + * @return the well-formed BCP 47 Unicode locale extension key, + * or NULL if the specified locale keyword cannot be + * mapped to a well-formed BCP 47 Unicode locale extension + * key. + * @see uloc_toLegacyKey + * @stable ICU 54 + */ +U_CAPI const char* U_EXPORT2 +uloc_toUnicodeLocaleKey(const char* keyword); + +/** + * Converts the specified keyword value (legacy type, or BCP 47 + * Unicode locale extension type) to the well-formed BCP 47 Unicode locale + * extension type for the specified keyword (category). For example, BCP 47 + * Unicode locale extension type "phonebk" is returned for the input + * keyword value "phonebook", with the keyword "collation" (or "co"). + *

+ * When the specified keyword is not recognized, but the specified value + * satisfies the syntax of the BCP 47 Unicode locale extension type, + * or when the specified keyword allows 'variable' type and the specified + * value satisfies the syntax, then the pointer to the input type value itself + * will be returned. + * For example, + * uloc_toUnicodeLocaleType("Foo", "Bar") returns "Bar", + * uloc_toUnicodeLocaleType("variableTop", "00A4") returns "00A4". + * + * @param keyword the locale keyword (either legacy key such as + * "collation" or BCP 47 Unicode locale extension + * key such as "co"). + * @param value the locale keyword value (either legacy type + * such as "phonebook" or BCP 47 Unicode locale extension + * type such as "phonebk"). + * @return the well-formed BCP47 Unicode locale extension type, + * or NULL if the locale keyword value cannot be mapped to + * a well-formed BCP 47 Unicode locale extension type. + * @see uloc_toLegacyType + * @stable ICU 54 + */ +U_CAPI const char* U_EXPORT2 +uloc_toUnicodeLocaleType(const char* keyword, const char* value); + +/** + * Converts the specified keyword (BCP 47 Unicode locale extension key, or + * legacy key) to the legacy key. For example, legacy key "collation" is + * returned for the input BCP 47 Unicode locale extension key "co". + * + * @param keyword the input locale keyword (either BCP 47 Unicode locale + * extension key or legacy key). + * @return the well-formed legacy key, or NULL if the specified + * keyword cannot be mapped to a well-formed legacy key. + * @see toUnicodeLocaleKey + * @stable ICU 54 + */ +U_CAPI const char* U_EXPORT2 +uloc_toLegacyKey(const char* keyword); + +/** + * Converts the specified keyword value (BCP 47 Unicode locale extension type, + * or legacy type or type alias) to the canonical legacy type. For example, + * the legacy type "phonebook" is returned for the input BCP 47 Unicode + * locale extension type "phonebk" with the keyword "collation" (or "co"). + *

+ * When the specified keyword is not recognized, but the specified value + * satisfies the syntax of legacy key, or when the specified keyword + * allows 'variable' type and the specified value satisfies the syntax, + * then the pointer to the input type value itself will be returned. + * For example, + * uloc_toLegacyType("Foo", "Bar") returns "Bar", + * uloc_toLegacyType("vt", "00A4") returns "00A4". + * + * @param keyword the locale keyword (either legacy keyword such as + * "collation" or BCP 47 Unicode locale extension + * key such as "co"). + * @param value the locale keyword value (either BCP 47 Unicode locale + * extension type such as "phonebk" or legacy keyword value + * such as "phonebook"). + * @return the well-formed legacy type, or NULL if the specified + * keyword value cannot be mapped to a well-formed legacy + * type. + * @see toUnicodeLocaleType + * @stable ICU 54 + */ +U_CAPI const char* U_EXPORT2 +uloc_toLegacyType(const char* keyword, const char* value); + +#endif /*_ULOC*/ diff --git a/deps/icu-small/source/common/unicode/umachine.h b/deps/icu-small/source/common/unicode/umachine.h index 66406062726f74..f08511bfa8e66b 100644 --- a/deps/icu-small/source/common/unicode/umachine.h +++ b/deps/icu-small/source/common/unicode/umachine.h @@ -1,485 +1,459 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: umachine.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999sep13 -* created by: Markus W. Scherer -* -* This file defines basic types and constants for ICU to be -* platform-independent. umachine.h and utf.h are included into -* utypes.h to provide all the general definitions for ICU. -* All of these definitions used to be in utypes.h before -* the UTF-handling macros made this unmaintainable. -*/ - -#ifndef __UMACHINE_H__ -#define __UMACHINE_H__ - - -/** - * \file - * \brief Basic types and constants for UTF - * - *

Basic types and constants for UTF

- * This file defines basic types and constants for utf.h to be - * platform-independent. umachine.h and utf.h are included into - * utypes.h to provide all the general definitions for ICU. - * All of these definitions used to be in utypes.h before - * the UTF-handling macros made this unmaintainable. - * - */ -/*==========================================================================*/ -/* Include platform-dependent definitions */ -/* which are contained in the platform-specific file platform.h */ -/*==========================================================================*/ - -#include "unicode/ptypes.h" /* platform.h is included in ptypes.h */ - -/* - * ANSI C headers: - * stddef.h defines wchar_t - */ -#include -#include - -/*==========================================================================*/ -/* For C wrappers, we use the symbol U_CAPI. */ -/* This works properly if the includer is C or C++. */ -/* Functions are declared U_CAPI return-type U_EXPORT2 function-name()... */ -/*==========================================================================*/ - -/** - * \def U_CFUNC - * This is used in a declaration of a library private ICU C function. - * @stable ICU 2.4 - */ - -/** - * \def U_CDECL_BEGIN - * This is used to begin a declaration of a library private ICU C API. - * @stable ICU 2.4 - */ - -/** - * \def U_CDECL_END - * This is used to end a declaration of a library private ICU C API - * @stable ICU 2.4 - */ - -#ifdef __cplusplus -# define U_CFUNC extern "C" -# define U_CDECL_BEGIN extern "C" { -# define U_CDECL_END } -#else -# define U_CFUNC extern -# define U_CDECL_BEGIN -# define U_CDECL_END -#endif - -#ifndef U_ATTRIBUTE_DEPRECATED -/** - * \def U_ATTRIBUTE_DEPRECATED - * This is used for GCC specific attributes - * @internal - */ -#if U_GCC_MAJOR_MINOR >= 302 -# define U_ATTRIBUTE_DEPRECATED __attribute__ ((deprecated)) -/** - * \def U_ATTRIBUTE_DEPRECATED - * This is used for Visual C++ specific attributes - * @internal - */ -#elif defined(_MSC_VER) && (_MSC_VER >= 1400) -# define U_ATTRIBUTE_DEPRECATED __declspec(deprecated) -#else -# define U_ATTRIBUTE_DEPRECATED -#endif -#endif - -/** This is used to declare a function as a public ICU C API @stable ICU 2.0*/ -#define U_CAPI U_CFUNC U_EXPORT -/** Obsolete/same as U_CAPI; was used to declare a function as a stable public ICU C API*/ -#define U_STABLE U_CAPI -/** Obsolete/same as U_CAPI; was used to declare a function as a draft public ICU C API */ -#define U_DRAFT U_CAPI -/** This is used to declare a function as a deprecated public ICU C API */ -#define U_DEPRECATED U_CAPI U_ATTRIBUTE_DEPRECATED -/** Obsolete/same as U_CAPI; was used to declare a function as an obsolete public ICU C API */ -#define U_OBSOLETE U_CAPI -/** Obsolete/same as U_CAPI; was used to declare a function as an internal ICU C API */ -#define U_INTERNAL U_CAPI - -/** - * \def U_OVERRIDE - * Defined to the C++11 "override" keyword if available. - * Denotes a class or member which is an override of the base class. - * May result in an error if it applied to something not an override. - * @internal - */ -#ifndef U_OVERRIDE -#define U_OVERRIDE override -#endif - -/** - * \def U_FINAL - * Defined to the C++11 "final" keyword if available. - * Denotes a class or member which may not be overridden in subclasses. - * May result in an error if subclasses attempt to override. - * @internal - */ -#if !defined(U_FINAL) || defined(U_IN_DOXYGEN) -#define U_FINAL final -#endif - -// Before ICU 65, function-like, multi-statement ICU macros were just defined as -// series of statements wrapped in { } blocks and the caller could choose to -// either treat them as if they were actual functions and end the invocation -// with a trailing ; creating an empty statement after the block or else omit -// this trailing ; using the knowledge that the macro would expand to { }. -// -// But doing so doesn't work well with macros that look like functions and -// compiler warnings about empty statements (ICU-20601) and ICU 65 therefore -// switches to the standard solution of wrapping such macros in do { } while. -// -// This will however break existing code that depends on being able to invoke -// these macros without a trailing ; so to be able to remain compatible with -// such code the wrapper is itself defined as macros so that it's possible to -// build ICU 65 and later with the old macro behaviour, like this: -// -// export CPPFLAGS='-DUPRV_BLOCK_MACRO_BEGIN="" -DUPRV_BLOCK_MACRO_END=""' -// runConfigureICU ... -// - -/** - * \def UPRV_BLOCK_MACRO_BEGIN - * Defined as the "do" keyword by default. - * @internal - */ -#ifndef UPRV_BLOCK_MACRO_BEGIN -#define UPRV_BLOCK_MACRO_BEGIN do -#endif - -/** - * \def UPRV_BLOCK_MACRO_END - * Defined as "while (false)" by default. - * @internal - */ -#ifndef UPRV_BLOCK_MACRO_END -#define UPRV_BLOCK_MACRO_END while (false) -#endif - -/*==========================================================================*/ -/* limits for int32_t etc., like in POSIX inttypes.h */ -/*==========================================================================*/ - -#ifndef INT8_MIN -/** The smallest value an 8 bit signed integer can hold @stable ICU 2.0 */ -# define INT8_MIN ((int8_t)(-128)) -#endif -#ifndef INT16_MIN -/** The smallest value a 16 bit signed integer can hold @stable ICU 2.0 */ -# define INT16_MIN ((int16_t)(-32767-1)) -#endif -#ifndef INT32_MIN -/** The smallest value a 32 bit signed integer can hold @stable ICU 2.0 */ -# define INT32_MIN ((int32_t)(-2147483647-1)) -#endif - -#ifndef INT8_MAX -/** The largest value an 8 bit signed integer can hold @stable ICU 2.0 */ -# define INT8_MAX ((int8_t)(127)) -#endif -#ifndef INT16_MAX -/** The largest value a 16 bit signed integer can hold @stable ICU 2.0 */ -# define INT16_MAX ((int16_t)(32767)) -#endif -#ifndef INT32_MAX -/** The largest value a 32 bit signed integer can hold @stable ICU 2.0 */ -# define INT32_MAX ((int32_t)(2147483647)) -#endif - -#ifndef UINT8_MAX -/** The largest value an 8 bit unsigned integer can hold @stable ICU 2.0 */ -# define UINT8_MAX ((uint8_t)(255U)) -#endif -#ifndef UINT16_MAX -/** The largest value a 16 bit unsigned integer can hold @stable ICU 2.0 */ -# define UINT16_MAX ((uint16_t)(65535U)) -#endif -#ifndef UINT32_MAX -/** The largest value a 32 bit unsigned integer can hold @stable ICU 2.0 */ -# define UINT32_MAX ((uint32_t)(4294967295U)) -#endif - -#if defined(U_INT64_T_UNAVAILABLE) -# error int64_t is required for decimal format and rule-based number format. -#else -# ifndef INT64_C -/** - * Provides a platform independent way to specify a signed 64-bit integer constant. - * note: may be wrong for some 64 bit platforms - ensure your compiler provides INT64_C - * @stable ICU 2.8 - */ -# define INT64_C(c) c ## LL -# endif -# ifndef UINT64_C -/** - * Provides a platform independent way to specify an unsigned 64-bit integer constant. - * note: may be wrong for some 64 bit platforms - ensure your compiler provides UINT64_C - * @stable ICU 2.8 - */ -# define UINT64_C(c) c ## ULL -# endif -# ifndef U_INT64_MIN -/** The smallest value a 64 bit signed integer can hold @stable ICU 2.8 */ -# define U_INT64_MIN ((int64_t)(INT64_C(-9223372036854775807)-1)) -# endif -# ifndef U_INT64_MAX -/** The largest value a 64 bit signed integer can hold @stable ICU 2.8 */ -# define U_INT64_MAX ((int64_t)(INT64_C(9223372036854775807))) -# endif -# ifndef U_UINT64_MAX -/** The largest value a 64 bit unsigned integer can hold @stable ICU 2.8 */ -# define U_UINT64_MAX ((uint64_t)(UINT64_C(18446744073709551615))) -# endif -#endif - -/*==========================================================================*/ -/* Boolean data type */ -/*==========================================================================*/ - -/** - * The ICU boolean type, a signed-byte integer. - * ICU-specific for historical reasons: The C and C++ standards used to not define type bool. - * Also provides a fixed type definition, as opposed to - * type bool whose details (e.g., sizeof) may vary by compiler and between C and C++. - * - * @stable ICU 2.0 - */ -typedef int8_t UBool; - -/** - * \def U_DEFINE_FALSE_AND_TRUE - * Normally turns off defining macros FALSE=0 & TRUE=1 in public ICU headers. - * These obsolete macros sometimes break compilation of other code that - * defines enum constants or similar with these names. - * C++ has long defined bool/false/true. - * C99 also added definitions for these, although as macros; see stdbool.h. - * - * You may transitionally define U_DEFINE_FALSE_AND_TRUE=1 if you need time to migrate code. - * - * @internal ICU 68 - */ -#ifdef U_DEFINE_FALSE_AND_TRUE - // Use the predefined value. -#else - // Default to avoiding collision with non-macro definitions of FALSE & TRUE. -# define U_DEFINE_FALSE_AND_TRUE 0 -#endif - -#if U_DEFINE_FALSE_AND_TRUE || defined(U_IN_DOXYGEN) -#ifndef TRUE -/** - * The TRUE value of a UBool. - * - * @deprecated ICU 68 Use standard "true" instead. - */ -# define TRUE 1 -#endif -#ifndef FALSE -/** - * The FALSE value of a UBool. - * - * @deprecated ICU 68 Use standard "false" instead. - */ -# define FALSE 0 -#endif -#endif // U_DEFINE_FALSE_AND_TRUE - -/*==========================================================================*/ -/* Unicode data types */ -/*==========================================================================*/ - -/* wchar_t-related definitions -------------------------------------------- */ - -/* - * \def U_WCHAR_IS_UTF16 - * Defined if wchar_t uses UTF-16. - * - * @stable ICU 2.0 - */ -/* - * \def U_WCHAR_IS_UTF32 - * Defined if wchar_t uses UTF-32. - * - * @stable ICU 2.0 - */ -#if !defined(U_WCHAR_IS_UTF16) && !defined(U_WCHAR_IS_UTF32) -# ifdef __STDC_ISO_10646__ -# if (U_SIZEOF_WCHAR_T==2) -# define U_WCHAR_IS_UTF16 -# elif (U_SIZEOF_WCHAR_T==4) -# define U_WCHAR_IS_UTF32 -# endif -# elif defined __UCS2__ -# if (U_PF_OS390 <= U_PLATFORM && U_PLATFORM <= U_PF_OS400) && (U_SIZEOF_WCHAR_T==2) -# define U_WCHAR_IS_UTF16 -# endif -# elif defined(__UCS4__) || (U_PLATFORM == U_PF_OS400 && defined(__UTF32__)) -# if (U_SIZEOF_WCHAR_T==4) -# define U_WCHAR_IS_UTF32 -# endif -# elif U_PLATFORM_IS_DARWIN_BASED || (U_SIZEOF_WCHAR_T==4 && U_PLATFORM_IS_LINUX_BASED) -# define U_WCHAR_IS_UTF32 -# elif U_PLATFORM_HAS_WIN32_API -# define U_WCHAR_IS_UTF16 -# endif -#endif - -/* UChar and UChar32 definitions -------------------------------------------- */ - -/** Number of bytes in a UChar. @stable ICU 2.0 */ -#define U_SIZEOF_UCHAR 2 - -/** - * \def U_CHAR16_IS_TYPEDEF - * If 1, then char16_t is a typedef and not a real type (yet) - * @internal - */ -#if (U_PLATFORM == U_PF_AIX) && defined(__cplusplus) &&(U_CPLUSPLUS_VERSION < 11) -// for AIX, uchar.h needs to be included -# include -# define U_CHAR16_IS_TYPEDEF 1 -#elif defined(_MSC_VER) && (_MSC_VER < 1900) -// Versions of Visual Studio/MSVC below 2015 do not support char16_t as a real type, -// and instead use a typedef. https://msdn.microsoft.com/library/bb531344.aspx -# define U_CHAR16_IS_TYPEDEF 1 -#else -# define U_CHAR16_IS_TYPEDEF 0 -#endif - - -/** - * \var UChar - * - * The base type for UTF-16 code units and pointers. - * Unsigned 16-bit integer. - * Starting with ICU 59, C++ API uses char16_t directly, while C API continues to use UChar. - * - * UChar is configurable by defining the macro UCHAR_TYPE - * on the preprocessor or compiler command line: - * -DUCHAR_TYPE=uint16_t or -DUCHAR_TYPE=wchar_t (if U_SIZEOF_WCHAR_T==2) etc. - * (The UCHAR_TYPE can also be \#defined earlier in this file, for outside the ICU library code.) - * This is for transitional use from application code that uses uint16_t or wchar_t for UTF-16. - * - * The default is UChar=char16_t. - * - * C++11 defines char16_t as bit-compatible with uint16_t, but as a distinct type. - * - * In C, char16_t is a simple typedef of uint_least16_t. - * ICU requires uint_least16_t=uint16_t for data memory mapping. - * On macOS, char16_t is not available because the uchar.h standard header is missing. - * - * @stable ICU 4.4 - */ - -#if 1 - // #if 1 is normal. UChar defaults to char16_t in C++. - // For configuration testing of UChar=uint16_t temporarily change this to #if 0. - // The intltest Makefile #defines UCHAR_TYPE=char16_t, - // so we only #define it to uint16_t if it is undefined so far. -#elif !defined(UCHAR_TYPE) -# define UCHAR_TYPE uint16_t -#endif - -#if defined(U_COMBINED_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) || \ - defined(U_I18N_IMPLEMENTATION) || defined(U_IO_IMPLEMENTATION) - // Inside the ICU library code, never configurable. - typedef char16_t UChar; -#elif defined(UCHAR_TYPE) - typedef UCHAR_TYPE UChar; -#elif (U_CPLUSPLUS_VERSION >= 11) - typedef char16_t UChar; -#else - typedef uint16_t UChar; -#endif - -/** - * \var OldUChar - * Default ICU 58 definition of UChar. - * A base type for UTF-16 code units and pointers. - * Unsigned 16-bit integer. - * - * Define OldUChar to be wchar_t if that is 16 bits wide. - * If wchar_t is not 16 bits wide, then define UChar to be uint16_t. - * - * This makes the definition of OldUChar platform-dependent - * but allows direct string type compatibility with platforms with - * 16-bit wchar_t types. - * - * This is how UChar was defined in ICU 58, for transition convenience. - * Exception: ICU 58 UChar was defined to UCHAR_TYPE if that macro was defined. - * The current UChar responds to UCHAR_TYPE but OldUChar does not. - * - * @stable ICU 59 - */ -#if U_SIZEOF_WCHAR_T==2 - typedef wchar_t OldUChar; -#elif defined(__CHAR16_TYPE__) - typedef __CHAR16_TYPE__ OldUChar; -#else - typedef uint16_t OldUChar; -#endif - -/** - * Define UChar32 as a type for single Unicode code points. - * UChar32 is a signed 32-bit integer (same as int32_t). - * - * The Unicode code point range is 0..0x10ffff. - * All other values (negative or >=0x110000) are illegal as Unicode code points. - * They may be used as sentinel values to indicate "done", "error" - * or similar non-code point conditions. - * - * Before ICU 2.4 (Jitterbug 2146), UChar32 was defined - * to be wchar_t if that is 32 bits wide (wchar_t may be signed or unsigned) - * or else to be uint32_t. - * That is, the definition of UChar32 was platform-dependent. - * - * @see U_SENTINEL - * @stable ICU 2.4 - */ -typedef int32_t UChar32; - -/** - * This value is intended for sentinel values for APIs that - * (take or) return single code points (UChar32). - * It is outside of the Unicode code point range 0..0x10ffff. - * - * For example, a "done" or "error" value in a new API - * could be indicated with U_SENTINEL. - * - * ICU APIs designed before ICU 2.4 usually define service-specific "done" - * values, mostly 0xffff. - * Those may need to be distinguished from - * actual U+ffff text contents by calling functions like - * CharacterIterator::hasNext() or UnicodeString::length(). - * - * @return -1 - * @see UChar32 - * @stable ICU 2.4 - */ -#define U_SENTINEL (-1) - -#include "unicode/urename.h" - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: umachine.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999sep13 +* created by: Markus W. Scherer +* +* This file defines basic types and constants for ICU to be +* platform-independent. umachine.h and utf.h are included into +* utypes.h to provide all the general definitions for ICU. +* All of these definitions used to be in utypes.h before +* the UTF-handling macros made this unmaintainable. +*/ + +#ifndef __UMACHINE_H__ +#define __UMACHINE_H__ + + +/** + * \file + * \brief Basic types and constants for UTF + * + *

Basic types and constants for UTF

+ * This file defines basic types and constants for utf.h to be + * platform-independent. umachine.h and utf.h are included into + * utypes.h to provide all the general definitions for ICU. + * All of these definitions used to be in utypes.h before + * the UTF-handling macros made this unmaintainable. + * + */ +/*==========================================================================*/ +/* Include platform-dependent definitions */ +/* which are contained in the platform-specific file platform.h */ +/*==========================================================================*/ + +#include "unicode/ptypes.h" /* platform.h is included in ptypes.h */ + +/* + * ANSI C headers: + * stddef.h defines wchar_t + */ +#include +#include + +/*==========================================================================*/ +/* For C wrappers, we use the symbol U_CAPI. */ +/* This works properly if the includer is C or C++. */ +/* Functions are declared U_CAPI return-type U_EXPORT2 function-name()... */ +/*==========================================================================*/ + +/** + * \def U_CFUNC + * This is used in a declaration of a library private ICU C function. + * @stable ICU 2.4 + */ + +/** + * \def U_CDECL_BEGIN + * This is used to begin a declaration of a library private ICU C API. + * @stable ICU 2.4 + */ + +/** + * \def U_CDECL_END + * This is used to end a declaration of a library private ICU C API + * @stable ICU 2.4 + */ + +#ifdef __cplusplus +# define U_CFUNC extern "C" +# define U_CDECL_BEGIN extern "C" { +# define U_CDECL_END } +#else +# define U_CFUNC extern +# define U_CDECL_BEGIN +# define U_CDECL_END +#endif + +#ifndef U_ATTRIBUTE_DEPRECATED +/** + * \def U_ATTRIBUTE_DEPRECATED + * This is used for GCC specific attributes + * @internal + */ +#if U_GCC_MAJOR_MINOR >= 302 +# define U_ATTRIBUTE_DEPRECATED __attribute__ ((deprecated)) +/** + * \def U_ATTRIBUTE_DEPRECATED + * This is used for Visual C++ specific attributes + * @internal + */ +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) +# define U_ATTRIBUTE_DEPRECATED __declspec(deprecated) +#else +# define U_ATTRIBUTE_DEPRECATED +#endif +#endif + +/** This is used to declare a function as a public ICU C API @stable ICU 2.0*/ +#define U_CAPI U_CFUNC U_EXPORT +/** Obsolete/same as U_CAPI; was used to declare a function as a stable public ICU C API*/ +#define U_STABLE U_CAPI +/** Obsolete/same as U_CAPI; was used to declare a function as a draft public ICU C API */ +#define U_DRAFT U_CAPI +/** This is used to declare a function as a deprecated public ICU C API */ +#define U_DEPRECATED U_CAPI U_ATTRIBUTE_DEPRECATED +/** Obsolete/same as U_CAPI; was used to declare a function as an obsolete public ICU C API */ +#define U_OBSOLETE U_CAPI +/** Obsolete/same as U_CAPI; was used to declare a function as an internal ICU C API */ +#define U_INTERNAL U_CAPI + +// Before ICU 65, function-like, multi-statement ICU macros were just defined as +// series of statements wrapped in { } blocks and the caller could choose to +// either treat them as if they were actual functions and end the invocation +// with a trailing ; creating an empty statement after the block or else omit +// this trailing ; using the knowledge that the macro would expand to { }. +// +// But doing so doesn't work well with macros that look like functions and +// compiler warnings about empty statements (ICU-20601) and ICU 65 therefore +// switches to the standard solution of wrapping such macros in do { } while. +// +// This will however break existing code that depends on being able to invoke +// these macros without a trailing ; so to be able to remain compatible with +// such code the wrapper is itself defined as macros so that it's possible to +// build ICU 65 and later with the old macro behaviour, like this: +// +// export CPPFLAGS='-DUPRV_BLOCK_MACRO_BEGIN="" -DUPRV_BLOCK_MACRO_END=""' +// runConfigureICU ... +// + +/** + * \def UPRV_BLOCK_MACRO_BEGIN + * Defined as the "do" keyword by default. + * @internal + */ +#ifndef UPRV_BLOCK_MACRO_BEGIN +#define UPRV_BLOCK_MACRO_BEGIN do +#endif + +/** + * \def UPRV_BLOCK_MACRO_END + * Defined as "while (false)" by default. + * @internal + */ +#ifndef UPRV_BLOCK_MACRO_END +#define UPRV_BLOCK_MACRO_END while (false) +#endif + +/*==========================================================================*/ +/* limits for int32_t etc., like in POSIX inttypes.h */ +/*==========================================================================*/ + +#ifndef INT8_MIN +/** The smallest value an 8 bit signed integer can hold @stable ICU 2.0 */ +# define INT8_MIN ((int8_t)(-128)) +#endif +#ifndef INT16_MIN +/** The smallest value a 16 bit signed integer can hold @stable ICU 2.0 */ +# define INT16_MIN ((int16_t)(-32767-1)) +#endif +#ifndef INT32_MIN +/** The smallest value a 32 bit signed integer can hold @stable ICU 2.0 */ +# define INT32_MIN ((int32_t)(-2147483647-1)) +#endif + +#ifndef INT8_MAX +/** The largest value an 8 bit signed integer can hold @stable ICU 2.0 */ +# define INT8_MAX ((int8_t)(127)) +#endif +#ifndef INT16_MAX +/** The largest value a 16 bit signed integer can hold @stable ICU 2.0 */ +# define INT16_MAX ((int16_t)(32767)) +#endif +#ifndef INT32_MAX +/** The largest value a 32 bit signed integer can hold @stable ICU 2.0 */ +# define INT32_MAX ((int32_t)(2147483647)) +#endif + +#ifndef UINT8_MAX +/** The largest value an 8 bit unsigned integer can hold @stable ICU 2.0 */ +# define UINT8_MAX ((uint8_t)(255U)) +#endif +#ifndef UINT16_MAX +/** The largest value a 16 bit unsigned integer can hold @stable ICU 2.0 */ +# define UINT16_MAX ((uint16_t)(65535U)) +#endif +#ifndef UINT32_MAX +/** The largest value a 32 bit unsigned integer can hold @stable ICU 2.0 */ +# define UINT32_MAX ((uint32_t)(4294967295U)) +#endif + +#if defined(U_INT64_T_UNAVAILABLE) +# error int64_t is required for decimal format and rule-based number format. +#else +# ifndef INT64_C +/** + * Provides a platform independent way to specify a signed 64-bit integer constant. + * note: may be wrong for some 64 bit platforms - ensure your compiler provides INT64_C + * @stable ICU 2.8 + */ +# define INT64_C(c) c ## LL +# endif +# ifndef UINT64_C +/** + * Provides a platform independent way to specify an unsigned 64-bit integer constant. + * note: may be wrong for some 64 bit platforms - ensure your compiler provides UINT64_C + * @stable ICU 2.8 + */ +# define UINT64_C(c) c ## ULL +# endif +# ifndef U_INT64_MIN +/** The smallest value a 64 bit signed integer can hold @stable ICU 2.8 */ +# define U_INT64_MIN ((int64_t)(INT64_C(-9223372036854775807)-1)) +# endif +# ifndef U_INT64_MAX +/** The largest value a 64 bit signed integer can hold @stable ICU 2.8 */ +# define U_INT64_MAX ((int64_t)(INT64_C(9223372036854775807))) +# endif +# ifndef U_UINT64_MAX +/** The largest value a 64 bit unsigned integer can hold @stable ICU 2.8 */ +# define U_UINT64_MAX ((uint64_t)(UINT64_C(18446744073709551615))) +# endif +#endif + +/*==========================================================================*/ +/* Boolean data type */ +/*==========================================================================*/ + +/** + * The ICU boolean type, a signed-byte integer. + * ICU-specific for historical reasons: The C and C++ standards used to not define type bool. + * Also provides a fixed type definition, as opposed to + * type bool whose details (e.g., sizeof) may vary by compiler and between C and C++. + * + * @stable ICU 2.0 + */ +typedef int8_t UBool; + +/** + * \def U_DEFINE_FALSE_AND_TRUE + * Normally turns off defining macros FALSE=0 & TRUE=1 in public ICU headers. + * These obsolete macros sometimes break compilation of other code that + * defines enum constants or similar with these names. + * C++ has long defined bool/false/true. + * C99 also added definitions for these, although as macros; see stdbool.h. + * + * You may transitionally define U_DEFINE_FALSE_AND_TRUE=1 if you need time to migrate code. + * + * @internal ICU 68 + */ +#ifdef U_DEFINE_FALSE_AND_TRUE + // Use the predefined value. +#else + // Default to avoiding collision with non-macro definitions of FALSE & TRUE. +# define U_DEFINE_FALSE_AND_TRUE 0 +#endif + +#if U_DEFINE_FALSE_AND_TRUE || defined(U_IN_DOXYGEN) +#ifndef TRUE +/** + * The TRUE value of a UBool. + * + * @deprecated ICU 68 Use standard "true" instead. + */ +# define TRUE 1 +#endif +#ifndef FALSE +/** + * The FALSE value of a UBool. + * + * @deprecated ICU 68 Use standard "false" instead. + */ +# define FALSE 0 +#endif +#endif // U_DEFINE_FALSE_AND_TRUE + +/*==========================================================================*/ +/* Unicode data types */ +/*==========================================================================*/ + +/* wchar_t-related definitions -------------------------------------------- */ + +/* + * \def U_WCHAR_IS_UTF16 + * Defined if wchar_t uses UTF-16. + * + * @stable ICU 2.0 + */ +/* + * \def U_WCHAR_IS_UTF32 + * Defined if wchar_t uses UTF-32. + * + * @stable ICU 2.0 + */ +#if !defined(U_WCHAR_IS_UTF16) && !defined(U_WCHAR_IS_UTF32) +# ifdef __STDC_ISO_10646__ +# if (U_SIZEOF_WCHAR_T==2) +# define U_WCHAR_IS_UTF16 +# elif (U_SIZEOF_WCHAR_T==4) +# define U_WCHAR_IS_UTF32 +# endif +# elif defined __UCS2__ +# if (U_PF_OS390 <= U_PLATFORM && U_PLATFORM <= U_PF_OS400) && (U_SIZEOF_WCHAR_T==2) +# define U_WCHAR_IS_UTF16 +# endif +# elif defined(__UCS4__) || (U_PLATFORM == U_PF_OS400 && defined(__UTF32__)) +# if (U_SIZEOF_WCHAR_T==4) +# define U_WCHAR_IS_UTF32 +# endif +# elif U_PLATFORM_IS_DARWIN_BASED || (U_SIZEOF_WCHAR_T==4 && U_PLATFORM_IS_LINUX_BASED) +# define U_WCHAR_IS_UTF32 +# elif U_PLATFORM_HAS_WIN32_API +# define U_WCHAR_IS_UTF16 +# endif +#endif + +/* UChar and UChar32 definitions -------------------------------------------- */ + +/** Number of bytes in a UChar (always 2). @stable ICU 2.0 */ +#define U_SIZEOF_UCHAR 2 + +/** + * \def U_CHAR16_IS_TYPEDEF + * If 1, then char16_t is a typedef and not a real type (yet) + * @internal + */ +#if defined(_MSC_VER) && (_MSC_VER < 1900) +// Versions of Visual Studio/MSVC below 2015 do not support char16_t as a real type, +// and instead use a typedef. https://msdn.microsoft.com/library/bb531344.aspx +# define U_CHAR16_IS_TYPEDEF 1 +#else +# define U_CHAR16_IS_TYPEDEF 0 +#endif + + +/** + * \var UChar + * + * The base type for UTF-16 code units and pointers. + * Unsigned 16-bit integer. + * Starting with ICU 59, C++ API uses char16_t directly, while C API continues to use UChar. + * + * UChar is configurable by defining the macro UCHAR_TYPE + * on the preprocessor or compiler command line: + * -DUCHAR_TYPE=uint16_t or -DUCHAR_TYPE=wchar_t (if U_SIZEOF_WCHAR_T==2) etc. + * (The UCHAR_TYPE can also be \#defined earlier in this file, for outside the ICU library code.) + * This is for transitional use from application code that uses uint16_t or wchar_t for UTF-16. + * + * The default is UChar=char16_t. + * + * C++11 defines char16_t as bit-compatible with uint16_t, but as a distinct type. + * + * In C, char16_t is a simple typedef of uint_least16_t. + * ICU requires uint_least16_t=uint16_t for data memory mapping. + * On macOS, char16_t is not available because the uchar.h standard header is missing. + * + * @stable ICU 4.4 + */ + +#if 1 + // #if 1 is normal. UChar defaults to char16_t in C++. + // For configuration testing of UChar=uint16_t temporarily change this to #if 0. + // The intltest Makefile #defines UCHAR_TYPE=char16_t, + // so we only #define it to uint16_t if it is undefined so far. +#elif !defined(UCHAR_TYPE) +# define UCHAR_TYPE uint16_t +#endif + +#if defined(U_COMBINED_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) || \ + defined(U_I18N_IMPLEMENTATION) || defined(U_IO_IMPLEMENTATION) + // Inside the ICU library code, never configurable. + typedef char16_t UChar; +#elif defined(UCHAR_TYPE) + typedef UCHAR_TYPE UChar; +#elif U_CPLUSPLUS_VERSION != 0 + typedef char16_t UChar; // C++ +#else + typedef uint16_t UChar; // C +#endif + +/** + * \var OldUChar + * Default ICU 58 definition of UChar. + * A base type for UTF-16 code units and pointers. + * Unsigned 16-bit integer. + * + * Define OldUChar to be wchar_t if that is 16 bits wide. + * If wchar_t is not 16 bits wide, then define UChar to be uint16_t. + * + * This makes the definition of OldUChar platform-dependent + * but allows direct string type compatibility with platforms with + * 16-bit wchar_t types. + * + * This is how UChar was defined in ICU 58, for transition convenience. + * Exception: ICU 58 UChar was defined to UCHAR_TYPE if that macro was defined. + * The current UChar responds to UCHAR_TYPE but OldUChar does not. + * + * @stable ICU 59 + */ +#if U_SIZEOF_WCHAR_T==2 + typedef wchar_t OldUChar; +#elif defined(__CHAR16_TYPE__) + typedef __CHAR16_TYPE__ OldUChar; +#else + typedef uint16_t OldUChar; +#endif + +/** + * Define UChar32 as a type for single Unicode code points. + * UChar32 is a signed 32-bit integer (same as int32_t). + * + * The Unicode code point range is 0..0x10ffff. + * All other values (negative or >=0x110000) are illegal as Unicode code points. + * They may be used as sentinel values to indicate "done", "error" + * or similar non-code point conditions. + * + * Before ICU 2.4 (Jitterbug 2146), UChar32 was defined + * to be wchar_t if that is 32 bits wide (wchar_t may be signed or unsigned) + * or else to be uint32_t. + * That is, the definition of UChar32 was platform-dependent. + * + * @see U_SENTINEL + * @stable ICU 2.4 + */ +typedef int32_t UChar32; + +/** + * This value is intended for sentinel values for APIs that + * (take or) return single code points (UChar32). + * It is outside of the Unicode code point range 0..0x10ffff. + * + * For example, a "done" or "error" value in a new API + * could be indicated with U_SENTINEL. + * + * ICU APIs designed before ICU 2.4 usually define service-specific "done" + * values, mostly 0xffff. + * Those may need to be distinguished from + * actual U+ffff text contents by calling functions like + * CharacterIterator::hasNext() or UnicodeString::length(). + * + * @return -1 + * @see UChar32 + * @stable ICU 2.4 + */ +#define U_SENTINEL (-1) + +#include "unicode/urename.h" + +#endif diff --git a/deps/icu-small/source/common/unicode/umisc.h b/deps/icu-small/source/common/unicode/umisc.h index 4e9dda7450bdc4..06f62b0be14497 100644 --- a/deps/icu-small/source/common/unicode/umisc.h +++ b/deps/icu-small/source/common/unicode/umisc.h @@ -1,62 +1,62 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2006, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: umisc.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999oct15 -* created by: Markus W. Scherer -*/ - -#ifndef UMISC_H -#define UMISC_H - -#include "unicode/utypes.h" - -/** - * \file - * \brief C API: Miscellaneous definitions - * - * This file contains miscellaneous definitions for the C APIs. - */ - -U_CDECL_BEGIN - -/** A struct representing a range of text containing a specific field - * @stable ICU 2.0 - */ -typedef struct UFieldPosition { - /** - * The field - * @stable ICU 2.0 - */ - int32_t field; - /** - * The start of the text range containing field - * @stable ICU 2.0 - */ - int32_t beginIndex; - /** - * The limit of the text range containing field - * @stable ICU 2.0 - */ - int32_t endIndex; -} UFieldPosition; - -#if !UCONFIG_NO_SERVICE -/** - * Opaque type returned by registerInstance, registerFactory and unregister for service registration. - * @stable ICU 2.6 - */ -typedef const void* URegistryKey; -#endif - -U_CDECL_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2006, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: umisc.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999oct15 +* created by: Markus W. Scherer +*/ + +#ifndef UMISC_H +#define UMISC_H + +#include "unicode/utypes.h" + +/** + * \file + * \brief C API: Miscellaneous definitions + * + * This file contains miscellaneous definitions for the C APIs. + */ + +U_CDECL_BEGIN + +/** A struct representing a range of text containing a specific field + * @stable ICU 2.0 + */ +typedef struct UFieldPosition { + /** + * The field + * @stable ICU 2.0 + */ + int32_t field; + /** + * The start of the text range containing field + * @stable ICU 2.0 + */ + int32_t beginIndex; + /** + * The limit of the text range containing field + * @stable ICU 2.0 + */ + int32_t endIndex; +} UFieldPosition; + +#if !UCONFIG_NO_SERVICE +/** + * Opaque type returned by registerInstance, registerFactory and unregister for service registration. + * @stable ICU 2.6 + */ +typedef const void* URegistryKey; +#endif + +U_CDECL_END + +#endif diff --git a/deps/icu-small/source/common/unicode/umutablecptrie.h b/deps/icu-small/source/common/unicode/umutablecptrie.h index d60fd618191c48..d1079ee3d258b1 100644 --- a/deps/icu-small/source/common/unicode/umutablecptrie.h +++ b/deps/icu-small/source/common/unicode/umutablecptrie.h @@ -1,240 +1,240 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// umutablecptrie.h (split out of ucptrie.h) -// created: 2018jan24 Markus W. Scherer - -#ifndef __UMUTABLECPTRIE_H__ -#define __UMUTABLECPTRIE_H__ - -#include "unicode/utypes.h" - -#include "unicode/ucpmap.h" -#include "unicode/ucptrie.h" -#include "unicode/utf8.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -U_CDECL_BEGIN - -/** - * \file - * \brief C API: This file defines a mutable Unicode code point trie. - * - * @see UCPTrie - * @see UMutableCPTrie - */ - -/** - * Mutable Unicode code point trie. - * Fast map from Unicode code points (U+0000..U+10FFFF) to 32-bit integer values. - * For details see https://icu.unicode.org/design/struct/utrie - * - * Setting values (especially ranges) and lookup is fast. - * The mutable trie is only somewhat space-efficient. - * It builds a compacted, immutable UCPTrie. - * - * This trie can be modified while iterating over its contents. - * For example, it is possible to merge its values with those from another - * set of ranges (e.g., another mutable or immutable trie): - * Iterate over those source ranges; for each of them iterate over this trie; - * add the source value into the value of each trie range. - * - * @see UCPTrie - * @see umutablecptrie_buildImmutable - * @stable ICU 63 - */ -typedef struct UMutableCPTrie UMutableCPTrie; - -/** - * Creates a mutable trie that initially maps each Unicode code point to the same value. - * It uses 32-bit data values until umutablecptrie_buildImmutable() is called. - * umutablecptrie_buildImmutable() takes a valueWidth parameter which - * determines the number of bits in the data value in the resulting UCPTrie. - * You must umutablecptrie_close() the trie once you are done using it. - * - * @param initialValue the initial value that is set for all code points - * @param errorValue the value for out-of-range code points and ill-formed UTF-8/16 - * @param pErrorCode an in/out ICU UErrorCode - * @return the trie - * @stable ICU 63 - */ -U_CAPI UMutableCPTrie * U_EXPORT2 -umutablecptrie_open(uint32_t initialValue, uint32_t errorValue, UErrorCode *pErrorCode); - -/** - * Clones a mutable trie. - * You must umutablecptrie_close() the clone once you are done using it. - * - * @param other the trie to clone - * @param pErrorCode an in/out ICU UErrorCode - * @return the trie clone - * @stable ICU 63 - */ -U_CAPI UMutableCPTrie * U_EXPORT2 -umutablecptrie_clone(const UMutableCPTrie *other, UErrorCode *pErrorCode); - -/** - * Closes a mutable trie and releases associated memory. - * - * @param trie the trie - * @stable ICU 63 - */ -U_CAPI void U_EXPORT2 -umutablecptrie_close(UMutableCPTrie *trie); - -/** - * Creates a mutable trie with the same contents as the UCPMap. - * You must umutablecptrie_close() the mutable trie once you are done using it. - * - * @param map the source map - * @param pErrorCode an in/out ICU UErrorCode - * @return the mutable trie - * @stable ICU 63 - */ -U_CAPI UMutableCPTrie * U_EXPORT2 -umutablecptrie_fromUCPMap(const UCPMap *map, UErrorCode *pErrorCode); - -/** - * Creates a mutable trie with the same contents as the immutable one. - * You must umutablecptrie_close() the mutable trie once you are done using it. - * - * @param trie the immutable trie - * @param pErrorCode an in/out ICU UErrorCode - * @return the mutable trie - * @stable ICU 63 - */ -U_CAPI UMutableCPTrie * U_EXPORT2 -umutablecptrie_fromUCPTrie(const UCPTrie *trie, UErrorCode *pErrorCode); - -/** - * Returns the value for a code point as stored in the trie. - * - * @param trie the trie - * @param c the code point - * @return the value - * @stable ICU 63 - */ -U_CAPI uint32_t U_EXPORT2 -umutablecptrie_get(const UMutableCPTrie *trie, UChar32 c); - -/** - * Returns the last code point such that all those from start to there have the same value. - * Can be used to efficiently iterate over all same-value ranges in a trie. - * (This is normally faster than iterating over code points and get()ting each value, - * but much slower than a data structure that stores ranges directly.) - * - * The trie can be modified between calls to this function. - * - * If the UCPMapValueFilter function pointer is not NULL, then - * the value to be delivered is passed through that function, and the return value is the end - * of the range where all values are modified to the same actual value. - * The value is unchanged if that function pointer is NULL. - * - * See the same-signature ucptrie_getRange() for a code sample. - * - * @param trie the trie - * @param start range start - * @param option defines whether surrogates are treated normally, - * or as having the surrogateValue; usually UCPMAP_RANGE_NORMAL - * @param surrogateValue value for surrogates; ignored if option==UCPMAP_RANGE_NORMAL - * @param filter a pointer to a function that may modify the trie data value, - * or NULL if the values from the trie are to be used unmodified - * @param context an opaque pointer that is passed on to the filter function - * @param pValue if not NULL, receives the value that every code point start..end has; - * may have been modified by filter(context, trie value) - * if that function pointer is not NULL - * @return the range end code point, or -1 if start is not a valid code point - * @stable ICU 63 - */ -U_CAPI UChar32 U_EXPORT2 -umutablecptrie_getRange(const UMutableCPTrie *trie, UChar32 start, - UCPMapRangeOption option, uint32_t surrogateValue, - UCPMapValueFilter *filter, const void *context, uint32_t *pValue); - -/** - * Sets a value for a code point. - * - * @param trie the trie - * @param c the code point - * @param value the value - * @param pErrorCode an in/out ICU UErrorCode - * @stable ICU 63 - */ -U_CAPI void U_EXPORT2 -umutablecptrie_set(UMutableCPTrie *trie, UChar32 c, uint32_t value, UErrorCode *pErrorCode); - -/** - * Sets a value for each code point [start..end]. - * Faster and more space-efficient than setting the value for each code point separately. - * - * @param trie the trie - * @param start the first code point to get the value - * @param end the last code point to get the value (inclusive) - * @param value the value - * @param pErrorCode an in/out ICU UErrorCode - * @stable ICU 63 - */ -U_CAPI void U_EXPORT2 -umutablecptrie_setRange(UMutableCPTrie *trie, - UChar32 start, UChar32 end, - uint32_t value, UErrorCode *pErrorCode); - -/** - * Compacts the data and builds an immutable UCPTrie according to the parameters. - * After this, the mutable trie will be empty. - * - * The mutable trie stores 32-bit values until buildImmutable() is called. - * If values shorter than 32 bits are to be stored in the immutable trie, - * then the upper bits are discarded. - * For example, when the mutable trie contains values 0x81, -0x7f, and 0xa581, - * and the value width is 8 bits, then each of these is stored as 0x81 - * and the immutable trie will return that as an unsigned value. - * (Some implementations may want to make productive temporary use of the upper bits - * until buildImmutable() discards them.) - * - * Not every possible set of mappings can be built into a UCPTrie, - * because of limitations resulting from speed and space optimizations. - * Every Unicode assigned character can be mapped to a unique value. - * Typical data yields data structures far smaller than the limitations. - * - * It is possible to construct extremely unusual mappings that exceed the data structure limits. - * In such a case this function will fail with a U_INDEX_OUTOFBOUNDS_ERROR. - * - * @param trie the trie trie - * @param type selects the trie type - * @param valueWidth selects the number of bits in a trie data value; if smaller than 32 bits, - * then the values stored in the trie will be truncated first - * @param pErrorCode an in/out ICU UErrorCode - * - * @see umutablecptrie_fromUCPTrie - * @stable ICU 63 - */ -U_CAPI UCPTrie * U_EXPORT2 -umutablecptrie_buildImmutable(UMutableCPTrie *trie, UCPTrieType type, UCPTrieValueWidth valueWidth, - UErrorCode *pErrorCode); - -U_CDECL_END - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUMutableCPTriePointer - * "Smart pointer" class, closes a UMutableCPTrie via umutablecptrie_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 63 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUMutableCPTriePointer, UMutableCPTrie, umutablecptrie_close); - -U_NAMESPACE_END - -#endif - -#endif +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// umutablecptrie.h (split out of ucptrie.h) +// created: 2018jan24 Markus W. Scherer + +#ifndef __UMUTABLECPTRIE_H__ +#define __UMUTABLECPTRIE_H__ + +#include "unicode/utypes.h" + +#include "unicode/ucpmap.h" +#include "unicode/ucptrie.h" +#include "unicode/utf8.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +U_CDECL_BEGIN + +/** + * \file + * \brief C API: This file defines a mutable Unicode code point trie. + * + * @see UCPTrie + * @see UMutableCPTrie + */ + +/** + * Mutable Unicode code point trie. + * Fast map from Unicode code points (U+0000..U+10FFFF) to 32-bit integer values. + * For details see https://icu.unicode.org/design/struct/utrie + * + * Setting values (especially ranges) and lookup is fast. + * The mutable trie is only somewhat space-efficient. + * It builds a compacted, immutable UCPTrie. + * + * This trie can be modified while iterating over its contents. + * For example, it is possible to merge its values with those from another + * set of ranges (e.g., another mutable or immutable trie): + * Iterate over those source ranges; for each of them iterate over this trie; + * add the source value into the value of each trie range. + * + * @see UCPTrie + * @see umutablecptrie_buildImmutable + * @stable ICU 63 + */ +typedef struct UMutableCPTrie UMutableCPTrie; + +/** + * Creates a mutable trie that initially maps each Unicode code point to the same value. + * It uses 32-bit data values until umutablecptrie_buildImmutable() is called. + * umutablecptrie_buildImmutable() takes a valueWidth parameter which + * determines the number of bits in the data value in the resulting UCPTrie. + * You must umutablecptrie_close() the trie once you are done using it. + * + * @param initialValue the initial value that is set for all code points + * @param errorValue the value for out-of-range code points and ill-formed UTF-8/16 + * @param pErrorCode an in/out ICU UErrorCode + * @return the trie + * @stable ICU 63 + */ +U_CAPI UMutableCPTrie * U_EXPORT2 +umutablecptrie_open(uint32_t initialValue, uint32_t errorValue, UErrorCode *pErrorCode); + +/** + * Clones a mutable trie. + * You must umutablecptrie_close() the clone once you are done using it. + * + * @param other the trie to clone + * @param pErrorCode an in/out ICU UErrorCode + * @return the trie clone + * @stable ICU 63 + */ +U_CAPI UMutableCPTrie * U_EXPORT2 +umutablecptrie_clone(const UMutableCPTrie *other, UErrorCode *pErrorCode); + +/** + * Closes a mutable trie and releases associated memory. + * + * @param trie the trie + * @stable ICU 63 + */ +U_CAPI void U_EXPORT2 +umutablecptrie_close(UMutableCPTrie *trie); + +/** + * Creates a mutable trie with the same contents as the UCPMap. + * You must umutablecptrie_close() the mutable trie once you are done using it. + * + * @param map the source map + * @param pErrorCode an in/out ICU UErrorCode + * @return the mutable trie + * @stable ICU 63 + */ +U_CAPI UMutableCPTrie * U_EXPORT2 +umutablecptrie_fromUCPMap(const UCPMap *map, UErrorCode *pErrorCode); + +/** + * Creates a mutable trie with the same contents as the immutable one. + * You must umutablecptrie_close() the mutable trie once you are done using it. + * + * @param trie the immutable trie + * @param pErrorCode an in/out ICU UErrorCode + * @return the mutable trie + * @stable ICU 63 + */ +U_CAPI UMutableCPTrie * U_EXPORT2 +umutablecptrie_fromUCPTrie(const UCPTrie *trie, UErrorCode *pErrorCode); + +/** + * Returns the value for a code point as stored in the trie. + * + * @param trie the trie + * @param c the code point + * @return the value + * @stable ICU 63 + */ +U_CAPI uint32_t U_EXPORT2 +umutablecptrie_get(const UMutableCPTrie *trie, UChar32 c); + +/** + * Returns the last code point such that all those from start to there have the same value. + * Can be used to efficiently iterate over all same-value ranges in a trie. + * (This is normally faster than iterating over code points and get()ting each value, + * but much slower than a data structure that stores ranges directly.) + * + * The trie can be modified between calls to this function. + * + * If the UCPMapValueFilter function pointer is not NULL, then + * the value to be delivered is passed through that function, and the return value is the end + * of the range where all values are modified to the same actual value. + * The value is unchanged if that function pointer is NULL. + * + * See the same-signature ucptrie_getRange() for a code sample. + * + * @param trie the trie + * @param start range start + * @param option defines whether surrogates are treated normally, + * or as having the surrogateValue; usually UCPMAP_RANGE_NORMAL + * @param surrogateValue value for surrogates; ignored if option==UCPMAP_RANGE_NORMAL + * @param filter a pointer to a function that may modify the trie data value, + * or NULL if the values from the trie are to be used unmodified + * @param context an opaque pointer that is passed on to the filter function + * @param pValue if not NULL, receives the value that every code point start..end has; + * may have been modified by filter(context, trie value) + * if that function pointer is not NULL + * @return the range end code point, or -1 if start is not a valid code point + * @stable ICU 63 + */ +U_CAPI UChar32 U_EXPORT2 +umutablecptrie_getRange(const UMutableCPTrie *trie, UChar32 start, + UCPMapRangeOption option, uint32_t surrogateValue, + UCPMapValueFilter *filter, const void *context, uint32_t *pValue); + +/** + * Sets a value for a code point. + * + * @param trie the trie + * @param c the code point + * @param value the value + * @param pErrorCode an in/out ICU UErrorCode + * @stable ICU 63 + */ +U_CAPI void U_EXPORT2 +umutablecptrie_set(UMutableCPTrie *trie, UChar32 c, uint32_t value, UErrorCode *pErrorCode); + +/** + * Sets a value for each code point [start..end]. + * Faster and more space-efficient than setting the value for each code point separately. + * + * @param trie the trie + * @param start the first code point to get the value + * @param end the last code point to get the value (inclusive) + * @param value the value + * @param pErrorCode an in/out ICU UErrorCode + * @stable ICU 63 + */ +U_CAPI void U_EXPORT2 +umutablecptrie_setRange(UMutableCPTrie *trie, + UChar32 start, UChar32 end, + uint32_t value, UErrorCode *pErrorCode); + +/** + * Compacts the data and builds an immutable UCPTrie according to the parameters. + * After this, the mutable trie will be empty. + * + * The mutable trie stores 32-bit values until buildImmutable() is called. + * If values shorter than 32 bits are to be stored in the immutable trie, + * then the upper bits are discarded. + * For example, when the mutable trie contains values 0x81, -0x7f, and 0xa581, + * and the value width is 8 bits, then each of these is stored as 0x81 + * and the immutable trie will return that as an unsigned value. + * (Some implementations may want to make productive temporary use of the upper bits + * until buildImmutable() discards them.) + * + * Not every possible set of mappings can be built into a UCPTrie, + * because of limitations resulting from speed and space optimizations. + * Every Unicode assigned character can be mapped to a unique value. + * Typical data yields data structures far smaller than the limitations. + * + * It is possible to construct extremely unusual mappings that exceed the data structure limits. + * In such a case this function will fail with a U_INDEX_OUTOFBOUNDS_ERROR. + * + * @param trie the trie trie + * @param type selects the trie type + * @param valueWidth selects the number of bits in a trie data value; if smaller than 32 bits, + * then the values stored in the trie will be truncated first + * @param pErrorCode an in/out ICU UErrorCode + * + * @see umutablecptrie_fromUCPTrie + * @stable ICU 63 + */ +U_CAPI UCPTrie * U_EXPORT2 +umutablecptrie_buildImmutable(UMutableCPTrie *trie, UCPTrieType type, UCPTrieValueWidth valueWidth, + UErrorCode *pErrorCode); + +U_CDECL_END + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUMutableCPTriePointer + * "Smart pointer" class, closes a UMutableCPTrie via umutablecptrie_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 63 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUMutableCPTriePointer, UMutableCPTrie, umutablecptrie_close); + +U_NAMESPACE_END + +#endif + +#endif diff --git a/deps/icu-small/source/common/unicode/unifilt.h b/deps/icu-small/source/common/unicode/unifilt.h index 0fcaf4b789c4c2..b8e931b4a022ef 100644 --- a/deps/icu-small/source/common/unicode/unifilt.h +++ b/deps/icu-small/source/common/unicode/unifilt.h @@ -1,136 +1,136 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2010, International Business Machines Corporation and others. -* All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/17/99 aliu Creation. -********************************************************************** -*/ -#ifndef UNIFILT_H -#define UNIFILT_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/unifunct.h" -#include "unicode/unimatch.h" - -/** - * \file - * \brief C++ API: Unicode Filter - */ - -U_NAMESPACE_BEGIN - -/** - * U_ETHER is used to represent character values for positions outside - * a range. For example, transliterator uses this to represent - * characters outside the range contextStart..contextLimit-1. This - * allows explicit matching by rules and UnicodeSets of text outside a - * defined range. - * @stable ICU 3.0 - */ -#define U_ETHER ((char16_t)0xFFFF) - -/** - * - * UnicodeFilter defines a protocol for selecting a - * subset of the full range (U+0000 to U+10FFFF) of Unicode characters. - * Currently, filters are used in conjunction with classes like - * {@link Transliterator} to only process selected characters through a - * transformation. - * - *

Note: UnicodeFilter currently stubs out two pure virtual methods - * of its base class, UnicodeMatcher. These methods are toPattern() - * and matchesIndexValue(). This is done so that filter classes that - * are not actually used as matchers -- specifically, those in the - * UnicodeFilterLogic component, and those in tests -- can continue to - * work without defining these methods. As long as a filter is not - * used in an RBT during real transliteration, these methods will not - * be called. However, this breaks the UnicodeMatcher base class - * protocol, and it is not a correct solution. - * - *

In the future we may revisit the UnicodeMatcher / UnicodeFilter - * hierarchy and either redesign it, or simply remove the stubs in - * UnicodeFilter and force subclasses to implement the full - * UnicodeMatcher protocol. - * - * @see UnicodeFilterLogic - * @stable ICU 2.0 - */ -class U_COMMON_API UnicodeFilter : public UnicodeFunctor, public UnicodeMatcher { - -public: - /** - * Destructor - * @stable ICU 2.0 - */ - virtual ~UnicodeFilter(); - - /** - * Clones this object polymorphically. - * The caller owns the result and should delete it when done. - * @return clone, or nullptr if an error occurred - * @stable ICU 2.4 - */ - virtual UnicodeFilter* clone() const override = 0; - - /** - * Returns true for characters that are in the selected - * subset. In other words, if a character is to be - * filtered, then contains() returns - * false. - * @stable ICU 2.0 - */ - virtual UBool contains(UChar32 c) const = 0; - - /** - * UnicodeFunctor API. Cast 'this' to a UnicodeMatcher* pointer - * and return the pointer. - * @stable ICU 2.4 - */ - virtual UnicodeMatcher* toMatcher() const override; - - /** - * Implement UnicodeMatcher API. - * @stable ICU 2.4 - */ - virtual UMatchDegree matches(const Replaceable& text, - int32_t& offset, - int32_t limit, - UBool incremental) override; - - /** - * UnicodeFunctor API. Nothing to do. - * @stable ICU 2.4 - */ - virtual void setData(const TransliterationRuleData*) override; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - * - * @stable ICU 2.2 - */ - static UClassID U_EXPORT2 getStaticClassID(); - -protected: - - /* - * Since this class has pure virtual functions, - * a constructor can't be used. - * @stable ICU 2.0 - */ -/* UnicodeFilter();*/ -}; - -/*inline UnicodeFilter::UnicodeFilter() {}*/ - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2010, International Business Machines Corporation and others. +* All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/17/99 aliu Creation. +********************************************************************** +*/ +#ifndef UNIFILT_H +#define UNIFILT_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/unifunct.h" +#include "unicode/unimatch.h" + +/** + * \file + * \brief C++ API: Unicode Filter + */ + +U_NAMESPACE_BEGIN + +/** + * U_ETHER is used to represent character values for positions outside + * a range. For example, transliterator uses this to represent + * characters outside the range contextStart..contextLimit-1. This + * allows explicit matching by rules and UnicodeSets of text outside a + * defined range. + * @stable ICU 3.0 + */ +#define U_ETHER ((char16_t)0xFFFF) + +/** + * + * UnicodeFilter defines a protocol for selecting a + * subset of the full range (U+0000 to U+10FFFF) of Unicode characters. + * Currently, filters are used in conjunction with classes like + * {@link Transliterator} to only process selected characters through a + * transformation. + * + *

Note: UnicodeFilter currently stubs out two pure virtual methods + * of its base class, UnicodeMatcher. These methods are toPattern() + * and matchesIndexValue(). This is done so that filter classes that + * are not actually used as matchers -- specifically, those in the + * UnicodeFilterLogic component, and those in tests -- can continue to + * work without defining these methods. As long as a filter is not + * used in an RBT during real transliteration, these methods will not + * be called. However, this breaks the UnicodeMatcher base class + * protocol, and it is not a correct solution. + * + *

In the future we may revisit the UnicodeMatcher / UnicodeFilter + * hierarchy and either redesign it, or simply remove the stubs in + * UnicodeFilter and force subclasses to implement the full + * UnicodeMatcher protocol. + * + * @see UnicodeFilterLogic + * @stable ICU 2.0 + */ +class U_COMMON_API UnicodeFilter : public UnicodeFunctor, public UnicodeMatcher { + +public: + /** + * Destructor + * @stable ICU 2.0 + */ + virtual ~UnicodeFilter(); + + /** + * Clones this object polymorphically. + * The caller owns the result and should delete it when done. + * @return clone, or nullptr if an error occurred + * @stable ICU 2.4 + */ + virtual UnicodeFilter* clone() const override = 0; + + /** + * Returns true for characters that are in the selected + * subset. In other words, if a character is to be + * filtered, then contains() returns + * false. + * @stable ICU 2.0 + */ + virtual UBool contains(UChar32 c) const = 0; + + /** + * UnicodeFunctor API. Cast 'this' to a UnicodeMatcher* pointer + * and return the pointer. + * @stable ICU 2.4 + */ + virtual UnicodeMatcher* toMatcher() const override; + + /** + * Implement UnicodeMatcher API. + * @stable ICU 2.4 + */ + virtual UMatchDegree matches(const Replaceable& text, + int32_t& offset, + int32_t limit, + UBool incremental) override; + + /** + * UnicodeFunctor API. Nothing to do. + * @stable ICU 2.4 + */ + virtual void setData(const TransliterationRuleData*) override; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + * + * @stable ICU 2.2 + */ + static UClassID U_EXPORT2 getStaticClassID(); + +protected: + + /* + * Since this class has pure virtual functions, + * a constructor can't be used. + * @stable ICU 2.0 + */ +/* UnicodeFilter();*/ +}; + +/*inline UnicodeFilter::UnicodeFilter() {}*/ + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/unifunct.h b/deps/icu-small/source/common/unicode/unifunct.h index 8751302494bcdb..f79cb773fdd233 100644 --- a/deps/icu-small/source/common/unicode/unifunct.h +++ b/deps/icu-small/source/common/unicode/unifunct.h @@ -1,132 +1,132 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2005, International Business Machines Corporation -* and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 01/14/2002 aliu Creation. -********************************************************************** -*/ -#ifndef UNIFUNCT_H -#define UNIFUNCT_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/uobject.h" - -/** - * \file - * \brief C++ API: Unicode Functor - */ - -U_NAMESPACE_BEGIN - -class UnicodeMatcher; -class UnicodeReplacer; -class TransliterationRuleData; - -/** - * UnicodeFunctor is an abstract base class for objects - * that perform match and/or replace operations on Unicode strings. - * @author Alan Liu - * @stable ICU 2.4 - */ -class U_COMMON_API UnicodeFunctor : public UObject { - -public: - - /** - * Destructor - * @stable ICU 2.4 - */ - virtual ~UnicodeFunctor(); - - /** - * Return a copy of this object. All UnicodeFunctor objects - * have to support cloning in order to allow classes using - * UnicodeFunctor to implement cloning. - * @stable ICU 2.4 - */ - virtual UnicodeFunctor* clone() const = 0; - - /** - * Cast 'this' to a UnicodeMatcher* pointer and return the - * pointer, or null if this is not a UnicodeMatcher*. Subclasses - * that mix in UnicodeMatcher as a base class must override this. - * This protocol is required because a pointer to a UnicodeFunctor - * cannot be cast to a pointer to a UnicodeMatcher, since - * UnicodeMatcher is a mixin that does not derive from - * UnicodeFunctor. - * @stable ICU 2.4 - */ - virtual UnicodeMatcher* toMatcher() const; - - /** - * Cast 'this' to a UnicodeReplacer* pointer and return the - * pointer, or null if this is not a UnicodeReplacer*. Subclasses - * that mix in UnicodeReplacer as a base class must override this. - * This protocol is required because a pointer to a UnicodeFunctor - * cannot be cast to a pointer to a UnicodeReplacer, since - * UnicodeReplacer is a mixin that does not derive from - * UnicodeFunctor. - * @stable ICU 2.4 - */ - virtual UnicodeReplacer* toReplacer() const; - - /** - * Return the class ID for this class. This is useful only for - * comparing to a return value from getDynamicClassID(). - * @return The class ID for all objects of this class. - * @stable ICU 2.0 - */ - static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * Returns a unique class ID polymorphically. This method - * is to implement a simple version of RTTI, since not all C++ - * compilers support genuine RTTI. Polymorphic operator==() and - * clone() methods call this method. - * - *

Concrete subclasses of UnicodeFunctor should use the macro - * UOBJECT_DEFINE_RTTI_IMPLEMENTATION from uobject.h to - * provide definitions getStaticClassID and getDynamicClassID. - * - * @return The class ID for this object. All objects of a given - * class have the same class ID. Objects of other classes have - * different class IDs. - * @stable ICU 2.4 - */ - virtual UClassID getDynamicClassID(void) const override = 0; - - /** - * Set the data object associated with this functor. The data - * object provides context for functor-to-standin mapping. This - * method is required when assigning a functor to a different data - * object. This function MAY GO AWAY later if the architecture is - * changed to pass data object pointers through the API. - * @internal ICU 2.1 - */ - virtual void setData(const TransliterationRuleData*) = 0; - -protected: - - /** - * Since this class has pure virtual functions, - * a constructor can't be used. - * @stable ICU 2.0 - */ - /*UnicodeFunctor();*/ - -}; - -/*inline UnicodeFunctor::UnicodeFunctor() {}*/ - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2005, International Business Machines Corporation +* and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 01/14/2002 aliu Creation. +********************************************************************** +*/ +#ifndef UNIFUNCT_H +#define UNIFUNCT_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/uobject.h" + +/** + * \file + * \brief C++ API: Unicode Functor + */ + +U_NAMESPACE_BEGIN + +class UnicodeMatcher; +class UnicodeReplacer; +class TransliterationRuleData; + +/** + * UnicodeFunctor is an abstract base class for objects + * that perform match and/or replace operations on Unicode strings. + * @author Alan Liu + * @stable ICU 2.4 + */ +class U_COMMON_API UnicodeFunctor : public UObject { + +public: + + /** + * Destructor + * @stable ICU 2.4 + */ + virtual ~UnicodeFunctor(); + + /** + * Return a copy of this object. All UnicodeFunctor objects + * have to support cloning in order to allow classes using + * UnicodeFunctor to implement cloning. + * @stable ICU 2.4 + */ + virtual UnicodeFunctor* clone() const = 0; + + /** + * Cast 'this' to a UnicodeMatcher* pointer and return the + * pointer, or null if this is not a UnicodeMatcher*. Subclasses + * that mix in UnicodeMatcher as a base class must override this. + * This protocol is required because a pointer to a UnicodeFunctor + * cannot be cast to a pointer to a UnicodeMatcher, since + * UnicodeMatcher is a mixin that does not derive from + * UnicodeFunctor. + * @stable ICU 2.4 + */ + virtual UnicodeMatcher* toMatcher() const; + + /** + * Cast 'this' to a UnicodeReplacer* pointer and return the + * pointer, or null if this is not a UnicodeReplacer*. Subclasses + * that mix in UnicodeReplacer as a base class must override this. + * This protocol is required because a pointer to a UnicodeFunctor + * cannot be cast to a pointer to a UnicodeReplacer, since + * UnicodeReplacer is a mixin that does not derive from + * UnicodeFunctor. + * @stable ICU 2.4 + */ + virtual UnicodeReplacer* toReplacer() const; + + /** + * Return the class ID for this class. This is useful only for + * comparing to a return value from getDynamicClassID(). + * @return The class ID for all objects of this class. + * @stable ICU 2.0 + */ + static UClassID U_EXPORT2 getStaticClassID(void); + + /** + * Returns a unique class ID polymorphically. This method + * is to implement a simple version of RTTI, since not all C++ + * compilers support genuine RTTI. Polymorphic operator==() and + * clone() methods call this method. + * + *

Concrete subclasses of UnicodeFunctor should use the macro + * UOBJECT_DEFINE_RTTI_IMPLEMENTATION from uobject.h to + * provide definitions getStaticClassID and getDynamicClassID. + * + * @return The class ID for this object. All objects of a given + * class have the same class ID. Objects of other classes have + * different class IDs. + * @stable ICU 2.4 + */ + virtual UClassID getDynamicClassID(void) const override = 0; + + /** + * Set the data object associated with this functor. The data + * object provides context for functor-to-standin mapping. This + * method is required when assigning a functor to a different data + * object. This function MAY GO AWAY later if the architecture is + * changed to pass data object pointers through the API. + * @internal ICU 2.1 + */ + virtual void setData(const TransliterationRuleData*) = 0; + +protected: + + /** + * Since this class has pure virtual functions, + * a constructor can't be used. + * @stable ICU 2.0 + */ + /*UnicodeFunctor();*/ + +}; + +/*inline UnicodeFunctor::UnicodeFunctor() {}*/ + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/unimatch.h b/deps/icu-small/source/common/unicode/unimatch.h index 302332f4558c3e..bdcf7c9d8700cd 100644 --- a/deps/icu-small/source/common/unicode/unimatch.h +++ b/deps/icu-small/source/common/unicode/unimatch.h @@ -1,168 +1,168 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -* Copyright (C) 2001-2005, International Business Machines Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 07/18/01 aliu Creation. -********************************************************************** -*/ -#ifndef UNIMATCH_H -#define UNIMATCH_H - -#include "unicode/utypes.h" - -/** - * \file - * \brief C++ API: Unicode Matcher - */ - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -class Replaceable; -class UnicodeString; -class UnicodeSet; - -/** - * Constants returned by UnicodeMatcher::matches() - * indicating the degree of match. - * @stable ICU 2.4 - */ -enum UMatchDegree { - /** - * Constant returned by matches() indicating a - * mismatch between the text and this matcher. The text contains - * a character which does not match, or the text does not contain - * all desired characters for a non-incremental match. - * @stable ICU 2.4 - */ - U_MISMATCH, - - /** - * Constant returned by matches() indicating a - * partial match between the text and this matcher. This value is - * only returned for incremental match operations. All characters - * of the text match, but more characters are required for a - * complete match. Alternatively, for variable-length matchers, - * all characters of the text match, and if more characters were - * supplied at limit, they might also match. - * @stable ICU 2.4 - */ - U_PARTIAL_MATCH, - - /** - * Constant returned by matches() indicating a - * complete match between the text and this matcher. For an - * incremental variable-length match, this value is returned if - * the given text matches, and it is known that additional - * characters would not alter the extent of the match. - * @stable ICU 2.4 - */ - U_MATCH -}; - -/** - * UnicodeMatcher defines a protocol for objects that can - * match a range of characters in a Replaceable string. - * @stable ICU 2.4 - */ -class U_COMMON_API UnicodeMatcher /* not : public UObject because this is an interface/mixin class */ { - -public: - /** - * Destructor. - * @stable ICU 2.4 - */ - virtual ~UnicodeMatcher(); - - /** - * Return a UMatchDegree value indicating the degree of match for - * the given text at the given offset. Zero, one, or more - * characters may be matched. - * - * Matching in the forward direction is indicated by limit > - * offset. Characters from offset forwards to limit-1 will be - * considered for matching. - * - * Matching in the reverse direction is indicated by limit < - * offset. Characters from offset backwards to limit+1 will be - * considered for matching. - * - * If limit == offset then the only match possible is a zero - * character match (which subclasses may implement if desired). - * - * As a side effect, advance the offset parameter to the limit of - * the matched substring. In the forward direction, this will be - * the index of the last matched character plus one. In the - * reverse direction, this will be the index of the last matched - * character minus one. - * - *

Note: This method is not const because some classes may - * modify their state as the result of a match. - * - * @param text the text to be matched - * @param offset on input, the index into text at which to begin - * matching. On output, the limit of the matched text. The - * number of matched characters is the output value of offset - * minus the input value. Offset should always point to the - * HIGH SURROGATE (leading code unit) of a pair of surrogates, - * both on entry and upon return. - * @param limit the limit index of text to be matched. Greater - * than offset for a forward direction match, less than offset for - * a backward direction match. The last character to be - * considered for matching will be text.charAt(limit-1) in the - * forward direction or text.charAt(limit+1) in the backward - * direction. - * @param incremental if true, then assume further characters may - * be inserted at limit and check for partial matching. Otherwise - * assume the text as given is complete. - * @return a match degree value indicating a full match, a partial - * match, or a mismatch. If incremental is false then - * U_PARTIAL_MATCH should never be returned. - * @stable ICU 2.4 - */ - virtual UMatchDegree matches(const Replaceable& text, - int32_t& offset, - int32_t limit, - UBool incremental) = 0; - - /** - * Returns a string representation of this matcher. If the result of - * calling this function is passed to the appropriate parser, it - * will produce another matcher that is equal to this one. - * @param result the string to receive the pattern. Previous - * contents will be deleted. - * @param escapeUnprintable if true then convert unprintable - * character to their hex escape representations, \\uxxxx or - * \\Uxxxxxxxx. Unprintable characters are those other than - * U+000A, U+0020..U+007E. - * @stable ICU 2.4 - */ - virtual UnicodeString& toPattern(UnicodeString& result, - UBool escapeUnprintable = false) const = 0; - - /** - * Returns true if this matcher will match a character c, where c - * & 0xFF == v, at offset, in the forward direction (with limit > - * offset). This is used by RuleBasedTransliterator for - * indexing. - * @stable ICU 2.4 - */ - virtual UBool matchesIndexValue(uint8_t v) const = 0; - - /** - * Union the set of all characters that may be matched by this object - * into the given set. - * @param toUnionTo the set into which to union the source characters - * @stable ICU 2.4 - */ - virtual void addMatchSetTo(UnicodeSet& toUnionTo) const = 0; -}; - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +* Copyright (C) 2001-2005, International Business Machines Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 07/18/01 aliu Creation. +********************************************************************** +*/ +#ifndef UNIMATCH_H +#define UNIMATCH_H + +#include "unicode/utypes.h" + +/** + * \file + * \brief C++ API: Unicode Matcher + */ + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +class Replaceable; +class UnicodeString; +class UnicodeSet; + +/** + * Constants returned by UnicodeMatcher::matches() + * indicating the degree of match. + * @stable ICU 2.4 + */ +enum UMatchDegree { + /** + * Constant returned by matches() indicating a + * mismatch between the text and this matcher. The text contains + * a character which does not match, or the text does not contain + * all desired characters for a non-incremental match. + * @stable ICU 2.4 + */ + U_MISMATCH, + + /** + * Constant returned by matches() indicating a + * partial match between the text and this matcher. This value is + * only returned for incremental match operations. All characters + * of the text match, but more characters are required for a + * complete match. Alternatively, for variable-length matchers, + * all characters of the text match, and if more characters were + * supplied at limit, they might also match. + * @stable ICU 2.4 + */ + U_PARTIAL_MATCH, + + /** + * Constant returned by matches() indicating a + * complete match between the text and this matcher. For an + * incremental variable-length match, this value is returned if + * the given text matches, and it is known that additional + * characters would not alter the extent of the match. + * @stable ICU 2.4 + */ + U_MATCH +}; + +/** + * UnicodeMatcher defines a protocol for objects that can + * match a range of characters in a Replaceable string. + * @stable ICU 2.4 + */ +class U_COMMON_API UnicodeMatcher /* not : public UObject because this is an interface/mixin class */ { + +public: + /** + * Destructor. + * @stable ICU 2.4 + */ + virtual ~UnicodeMatcher(); + + /** + * Return a UMatchDegree value indicating the degree of match for + * the given text at the given offset. Zero, one, or more + * characters may be matched. + * + * Matching in the forward direction is indicated by limit > + * offset. Characters from offset forwards to limit-1 will be + * considered for matching. + * + * Matching in the reverse direction is indicated by limit < + * offset. Characters from offset backwards to limit+1 will be + * considered for matching. + * + * If limit == offset then the only match possible is a zero + * character match (which subclasses may implement if desired). + * + * As a side effect, advance the offset parameter to the limit of + * the matched substring. In the forward direction, this will be + * the index of the last matched character plus one. In the + * reverse direction, this will be the index of the last matched + * character minus one. + * + *

Note: This method is not const because some classes may + * modify their state as the result of a match. + * + * @param text the text to be matched + * @param offset on input, the index into text at which to begin + * matching. On output, the limit of the matched text. The + * number of matched characters is the output value of offset + * minus the input value. Offset should always point to the + * HIGH SURROGATE (leading code unit) of a pair of surrogates, + * both on entry and upon return. + * @param limit the limit index of text to be matched. Greater + * than offset for a forward direction match, less than offset for + * a backward direction match. The last character to be + * considered for matching will be text.charAt(limit-1) in the + * forward direction or text.charAt(limit+1) in the backward + * direction. + * @param incremental if true, then assume further characters may + * be inserted at limit and check for partial matching. Otherwise + * assume the text as given is complete. + * @return a match degree value indicating a full match, a partial + * match, or a mismatch. If incremental is false then + * U_PARTIAL_MATCH should never be returned. + * @stable ICU 2.4 + */ + virtual UMatchDegree matches(const Replaceable& text, + int32_t& offset, + int32_t limit, + UBool incremental) = 0; + + /** + * Returns a string representation of this matcher. If the result of + * calling this function is passed to the appropriate parser, it + * will produce another matcher that is equal to this one. + * @param result the string to receive the pattern. Previous + * contents will be deleted. + * @param escapeUnprintable if true then convert unprintable + * character to their hex escape representations, \\uxxxx or + * \\Uxxxxxxxx. Unprintable characters are those other than + * U+000A, U+0020..U+007E. + * @stable ICU 2.4 + */ + virtual UnicodeString& toPattern(UnicodeString& result, + UBool escapeUnprintable = false) const = 0; + + /** + * Returns true if this matcher will match a character c, where c + * & 0xFF == v, at offset, in the forward direction (with limit > + * offset). This is used by RuleBasedTransliterator for + * indexing. + * @stable ICU 2.4 + */ + virtual UBool matchesIndexValue(uint8_t v) const = 0; + + /** + * Union the set of all characters that may be matched by this object + * into the given set. + * @param toUnionTo the set into which to union the source characters + * @stable ICU 2.4 + */ + virtual void addMatchSetTo(UnicodeSet& toUnionTo) const = 0; +}; + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/uniset.h b/deps/icu-small/source/common/unicode/uniset.h index 33e35c4def8c80..9d530baa562cd1 100644 --- a/deps/icu-small/source/common/unicode/uniset.h +++ b/deps/icu-small/source/common/unicode/uniset.h @@ -1,1780 +1,1793 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -*************************************************************************** -* Copyright (C) 1999-2016, International Business Machines Corporation -* and others. All Rights Reserved. -*************************************************************************** -* Date Name Description -* 10/20/99 alan Creation. -*************************************************************************** -*/ - -#ifndef UNICODESET_H -#define UNICODESET_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/ucpmap.h" -#include "unicode/unifilt.h" -#include "unicode/unistr.h" -#include "unicode/uset.h" - -/** - * \file - * \brief C++ API: Unicode Set - */ - -U_NAMESPACE_BEGIN - -// Forward Declarations. -class BMPSet; -class ParsePosition; -class RBBIRuleScanner; -class SymbolTable; -class UnicodeSetStringSpan; -class UVector; -class RuleCharacterIterator; - -/** - * A mutable set of Unicode characters and multicharacter strings. Objects of this class - * represent character classes used in regular expressions. - * A character specifies a subset of Unicode code points. Legal - * code points are U+0000 to U+10FFFF, inclusive. - * - *

The UnicodeSet class is not designed to be subclassed. - * - *

UnicodeSet supports two APIs. The first is the - * operand API that allows the caller to modify the value of - * a UnicodeSet object. It conforms to Java 2's - * java.util.Set interface, although - * UnicodeSet does not actually implement that - * interface. All methods of Set are supported, with the - * modification that they take a character range or single character - * instead of an Object, and they take a - * UnicodeSet instead of a Collection. The - * operand API may be thought of in terms of boolean logic: a boolean - * OR is implemented by add, a boolean AND is implemented - * by retain, a boolean XOR is implemented by - * complement taking an argument, and a boolean NOT is - * implemented by complement with no argument. In terms - * of traditional set theory function names, add is a - * union, retain is an intersection, remove - * is an asymmetric difference, and complement with no - * argument is a set complement with respect to the superset range - * MIN_VALUE-MAX_VALUE - * - *

The second API is the - * applyPattern()/toPattern() API from the - * java.text.Format-derived classes. Unlike the - * methods that add characters, add categories, and control the logic - * of the set, the method applyPattern() sets all - * attributes of a UnicodeSet at once, based on a - * string pattern. - * - *

Pattern syntax

- * - * Patterns are accepted by the constructors and the - * applyPattern() methods and returned by the - * toPattern() method. These patterns follow a syntax - * similar to that employed by version 8 regular expression character - * classes. Here are some simple examples: - * - * \htmlonly
\endhtmlonly - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
[]No characters
[a]The character 'a'
[ae]The characters 'a' and 'e'
[a-e]The characters 'a' through 'e' inclusive, in Unicode code - * point order
[\\u4E01]The character U+4E01
[a{ab}{ac}]The character 'a' and the multicharacter strings "ab" and - * "ac"
[\\p{Lu}]All characters in the general category Uppercase Letter
- * \htmlonly
\endhtmlonly - * - * Any character may be preceded by a backslash in order to remove any special - * meaning. White space characters, as defined by UCharacter.isWhitespace(), are - * ignored, unless they are escaped. - * - *

Property patterns specify a set of characters having a certain - * property as defined by the Unicode standard. Both the POSIX-like - * "[:Lu:]" and the Perl-like syntax "\\p{Lu}" are recognized. For a - * complete list of supported property patterns, see the User's Guide - * for UnicodeSet at - * - * https://unicode-org.github.io/icu/userguide/strings/unicodeset. - * Actual determination of property data is defined by the underlying - * Unicode database as implemented by UCharacter. - * - *

Patterns specify individual characters, ranges of characters, and - * Unicode property sets. When elements are concatenated, they - * specify their union. To complement a set, place a '^' immediately - * after the opening '['. Property patterns are inverted by modifying - * their delimiters; "[:^foo]" and "\\P{foo}". In any other location, - * '^' has no special meaning. - * - *

Since ICU 70, "[^...]", "[:^foo]", "\\P{foo}", and "[:binaryProperty=No:]" - * perform a “code point complement” (all code points minus the original set), - * removing all multicharacter strings, - * equivalent to .complement().removeAllStrings(). - * The complement() API function continues to perform a - * symmetric difference with all code points and thus retains all multicharacter strings. - * - *

Ranges are indicated by placing two a '-' between two - * characters, as in "a-z". This specifies the range of all - * characters from the left to the right, in Unicode order. If the - * left character is greater than or equal to the - * right character it is a syntax error. If a '-' occurs as the first - * character after the opening '[' or '[^', or if it occurs as the - * last character before the closing ']', then it is taken as a - * literal. Thus "[a\-b]", "[-ab]", and "[ab-]" all indicate the same - * set of three characters, 'a', 'b', and '-'. - * - *

Sets may be intersected using the '&' operator or the asymmetric - * set difference may be taken using the '-' operator, for example, - * "[[:L:]&[\\u0000-\\u0FFF]]" indicates the set of all Unicode letters - * with values less than 4096. Operators ('&' and '|') have equal - * precedence and bind left-to-right. Thus - * "[[:L:]-[a-z]-[\\u0100-\\u01FF]]" is equivalent to - * "[[[:L:]-[a-z]]-[\\u0100-\\u01FF]]". This only really matters for - * difference; intersection is commutative. - * - * - *
[a]The set containing 'a' - *
[a-z]The set containing 'a' - * through 'z' and all letters in between, in Unicode order - *
[^a-z]The set containing - * all characters but 'a' through 'z', - * that is, U+0000 through 'a'-1 and 'z'+1 through U+10FFFF - *
[[pat1][pat2]] - * The union of sets specified by pat1 and pat2 - *
[[pat1]&[pat2]] - * The intersection of sets specified by pat1 and pat2 - *
[[pat1]-[pat2]] - * The asymmetric difference of sets specified by pat1 and - * pat2 - *
[:Lu:] or \\p{Lu} - * The set of characters having the specified - * Unicode property; in - * this case, Unicode uppercase letters - *
[:^Lu:] or \\P{Lu} - * The set of characters not having the given - * Unicode property - *
- * - *

Formal syntax

- * - * \htmlonly
\endhtmlonly - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
pattern :=  ('[' '^'? item* ']') | - * property
item :=  char | (char '-' char) | pattern-expr
- *
pattern-expr :=  pattern | pattern-expr pattern | - * pattern-expr op pattern
- *
op :=  '&' | '-'
- *
special :=  '[' | ']' | '-'
- *
char :=  any character that is not special
- * | ('\'
any character)
- * | ('\\u' hex hex hex hex)
- *
hex :=  '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' |
- *     'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
property :=  a Unicode property set pattern
- *
- * - * - * - * - *
Legend: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
a := b  a may be replaced by b
a?zero or one instance of a
- *
a*one or more instances of a
- *
a | beither a or b
- *
'a'the literal string between the quotes
- *
- * \htmlonly
\endhtmlonly - * - *

Note: - * - Most UnicodeSet methods do not take a UErrorCode parameter because - * there are usually very few opportunities for failure other than a shortage - * of memory, error codes in low-level C++ string methods would be inconvenient, - * and the error code as the last parameter (ICU convention) would prevent - * the use of default parameter values. - * Instead, such methods set the UnicodeSet into a "bogus" state - * (see isBogus()) if an error occurs. - * - * @author Alan Liu - * @stable ICU 2.0 - */ -class U_COMMON_API UnicodeSet U_FINAL : public UnicodeFilter { -private: - /** - * Enough for sets with few ranges. - * For example, White_Space has 10 ranges, list length 21. - */ - static constexpr int32_t INITIAL_CAPACITY = 25; - // fFlags constant - static constexpr uint8_t kIsBogus = 1; // This set is bogus (i.e. not valid) - - UChar32* list = stackList; // MUST be terminated with HIGH - int32_t capacity = INITIAL_CAPACITY; // capacity of list - int32_t len = 1; // length of list used; 1 <= len <= capacity - uint8_t fFlags = 0; // Bit flag (see constants above) - - BMPSet *bmpSet = nullptr; // The set is frozen iff either bmpSet or stringSpan is not NULL. - UChar32* buffer = nullptr; // internal buffer, may be NULL - int32_t bufferCapacity = 0; // capacity of buffer - - /** - * The pattern representation of this set. This may not be the - * most economical pattern. It is the pattern supplied to - * applyPattern(), with variables substituted and whitespace - * removed. For sets constructed without applyPattern(), or - * modified using the non-pattern API, this string will be empty, - * indicating that toPattern() must generate a pattern - * representation from the inversion list. - */ - char16_t *pat = nullptr; - int32_t patLen = 0; - - UVector* strings = nullptr; // maintained in sorted order - UnicodeSetStringSpan *stringSpan = nullptr; - - /** - * Initial list array. - * Avoids some heap allocations, and list is never nullptr. - * Increases the object size a bit. - */ - UChar32 stackList[INITIAL_CAPACITY]; - -public: - /** - * Determine if this object contains a valid set. - * A bogus set has no value. It is different from an empty set. - * It can be used to indicate that no set value is available. - * - * @return true if the set is bogus/invalid, false otherwise - * @see setToBogus() - * @stable ICU 4.0 - */ - inline UBool isBogus(void) const; - - /** - * Make this UnicodeSet object invalid. - * The string will test true with isBogus(). - * - * A bogus set has no value. It is different from an empty set. - * It can be used to indicate that no set value is available. - * - * This utility function is used throughout the UnicodeSet - * implementation to indicate that a UnicodeSet operation failed, - * and may be used in other functions, - * especially but not exclusively when such functions do not - * take a UErrorCode for simplicity. - * - * @see isBogus() - * @stable ICU 4.0 - */ - void setToBogus(); - -public: - - enum { - /** - * Minimum value that can be stored in a UnicodeSet. - * @stable ICU 2.4 - */ - MIN_VALUE = 0, - - /** - * Maximum value that can be stored in a UnicodeSet. - * @stable ICU 2.4 - */ - MAX_VALUE = 0x10ffff - }; - - //---------------------------------------------------------------- - // Constructors &c - //---------------------------------------------------------------- - -public: - - /** - * Constructs an empty set. - * @stable ICU 2.0 - */ - UnicodeSet(); - - /** - * Constructs a set containing the given range. If end < - * start then an empty set is created. - * - * @param start first character, inclusive, of range - * @param end last character, inclusive, of range - * @stable ICU 2.4 - */ - UnicodeSet(UChar32 start, UChar32 end); - -#ifndef U_HIDE_INTERNAL_API - /** - * @internal - */ - enum ESerialization { - kSerialized /* result of serialize() */ - }; - - /** - * Constructs a set from the output of serialize(). - * - * @param buffer the 16 bit array - * @param bufferLen the original length returned from serialize() - * @param serialization the value 'kSerialized' - * @param status error code - * - * @internal - */ - UnicodeSet(const uint16_t buffer[], int32_t bufferLen, - ESerialization serialization, UErrorCode &status); -#endif /* U_HIDE_INTERNAL_API */ - - /** - * Constructs a set from the given pattern. See the class - * description for the syntax of the pattern language. - * @param pattern a string specifying what characters are in the set - * @param status returns U_ILLEGAL_ARGUMENT_ERROR if the pattern - * contains a syntax error. - * @stable ICU 2.0 - */ - UnicodeSet(const UnicodeString& pattern, - UErrorCode& status); - -#ifndef U_HIDE_INTERNAL_API - /** - * Constructs a set from the given pattern. See the class - * description for the syntax of the pattern language. - * @param pattern a string specifying what characters are in the set - * @param options bitmask for options to apply to the pattern. - * Valid options are USET_IGNORE_SPACE and USET_CASE_INSENSITIVE. - * @param symbols a symbol table mapping variable names to values - * and stand-in characters to UnicodeSets; may be NULL - * @param status returns U_ILLEGAL_ARGUMENT_ERROR if the pattern - * contains a syntax error. - * @internal - */ - UnicodeSet(const UnicodeString& pattern, - uint32_t options, - const SymbolTable* symbols, - UErrorCode& status); -#endif /* U_HIDE_INTERNAL_API */ - - /** - * Constructs a set from the given pattern. See the class description - * for the syntax of the pattern language. - * @param pattern a string specifying what characters are in the set - * @param pos on input, the position in pattern at which to start parsing. - * On output, the position after the last character parsed. - * @param options bitmask for options to apply to the pattern. - * Valid options are USET_IGNORE_SPACE and USET_CASE_INSENSITIVE. - * @param symbols a symbol table mapping variable names to values - * and stand-in characters to UnicodeSets; may be NULL - * @param status input-output error code - * @stable ICU 2.8 - */ - UnicodeSet(const UnicodeString& pattern, ParsePosition& pos, - uint32_t options, - const SymbolTable* symbols, - UErrorCode& status); - - /** - * Constructs a set that is identical to the given UnicodeSet. - * @stable ICU 2.0 - */ - UnicodeSet(const UnicodeSet& o); - - /** - * Destructs the set. - * @stable ICU 2.0 - */ - virtual ~UnicodeSet(); - - /** - * Assigns this object to be a copy of another. - * A frozen set will not be modified. - * @stable ICU 2.0 - */ - UnicodeSet& operator=(const UnicodeSet& o); - - /** - * Compares the specified object with this set for equality. Returns - * true if the two sets - * have the same size, and every member of the specified set is - * contained in this set (or equivalently, every member of this set is - * contained in the specified set). - * - * @param o set to be compared for equality with this set. - * @return true if the specified set is equal to this set. - * @stable ICU 2.0 - */ - virtual bool operator==(const UnicodeSet& o) const; - - /** - * Compares the specified object with this set for equality. Returns - * true if the specified set is not equal to this set. - * @stable ICU 2.0 - */ - inline bool operator!=(const UnicodeSet& o) const; - - /** - * Returns a copy of this object. All UnicodeFunctor objects have - * to support cloning in order to allow classes using - * UnicodeFunctors, such as Transliterator, to implement cloning. - * If this set is frozen, then the clone will be frozen as well. - * Use cloneAsThawed() for a mutable clone of a frozen set. - * @see cloneAsThawed - * @stable ICU 2.0 - */ - virtual UnicodeSet* clone() const override; - - /** - * Returns the hash code value for this set. - * - * @return the hash code value for this set. - * @see Object#hashCode() - * @stable ICU 2.0 - */ - virtual int32_t hashCode(void) const; - - /** - * Get a UnicodeSet pointer from a USet - * - * @param uset a USet (the ICU plain C type for UnicodeSet) - * @return the corresponding UnicodeSet pointer. - * - * @stable ICU 4.2 - */ - inline static UnicodeSet *fromUSet(USet *uset); - - /** - * Get a UnicodeSet pointer from a const USet - * - * @param uset a const USet (the ICU plain C type for UnicodeSet) - * @return the corresponding UnicodeSet pointer. - * - * @stable ICU 4.2 - */ - inline static const UnicodeSet *fromUSet(const USet *uset); - - /** - * Produce a USet * pointer for this UnicodeSet. - * USet is the plain C type for UnicodeSet - * - * @return a USet pointer for this UnicodeSet - * @stable ICU 4.2 - */ - inline USet *toUSet(); - - - /** - * Produce a const USet * pointer for this UnicodeSet. - * USet is the plain C type for UnicodeSet - * - * @return a const USet pointer for this UnicodeSet - * @stable ICU 4.2 - */ - inline const USet * toUSet() const; - - - //---------------------------------------------------------------- - // Freezable API - //---------------------------------------------------------------- - - /** - * Determines whether the set has been frozen (made immutable) or not. - * See the ICU4J Freezable interface for details. - * @return true/false for whether the set has been frozen - * @see freeze - * @see cloneAsThawed - * @stable ICU 3.8 - */ - inline UBool isFrozen() const; - - /** - * Freeze the set (make it immutable). - * Once frozen, it cannot be unfrozen and is therefore thread-safe - * until it is deleted. - * See the ICU4J Freezable interface for details. - * Freezing the set may also make some operations faster, for example - * contains() and span(). - * A frozen set will not be modified. (It remains frozen.) - * @return this set. - * @see isFrozen - * @see cloneAsThawed - * @stable ICU 3.8 - */ - UnicodeSet *freeze(); - - /** - * Clone the set and make the clone mutable. - * See the ICU4J Freezable interface for details. - * @return the mutable clone - * @see freeze - * @see isFrozen - * @stable ICU 3.8 - */ - UnicodeSet *cloneAsThawed() const; - - //---------------------------------------------------------------- - // Public API - //---------------------------------------------------------------- - - /** - * Make this object represent the range `start - end`. - * If `start > end` then this object is set to an empty range. - * A frozen set will not be modified. - * - * @param start first character in the set, inclusive - * @param end last character in the set, inclusive - * @stable ICU 2.4 - */ - UnicodeSet& set(UChar32 start, UChar32 end); - - /** - * Return true if the given position, in the given pattern, appears - * to be the start of a UnicodeSet pattern. - * @stable ICU 2.4 - */ - static UBool resemblesPattern(const UnicodeString& pattern, - int32_t pos); - - /** - * Modifies this set to represent the set specified by the given - * pattern, ignoring Unicode Pattern_White_Space characters. - * See the class description for the syntax of the pattern language. - * A frozen set will not be modified. - * @param pattern a string specifying what characters are in the set - * @param status returns U_ILLEGAL_ARGUMENT_ERROR if the pattern - * contains a syntax error. - * Empties the set passed before applying the pattern. - * @return a reference to this - * @stable ICU 2.0 - */ - UnicodeSet& applyPattern(const UnicodeString& pattern, - UErrorCode& status); - -#ifndef U_HIDE_INTERNAL_API - /** - * Modifies this set to represent the set specified by the given - * pattern, optionally ignoring Unicode Pattern_White_Space characters. - * See the class description for the syntax of the pattern language. - * A frozen set will not be modified. - * @param pattern a string specifying what characters are in the set - * @param options bitmask for options to apply to the pattern. - * Valid options are USET_IGNORE_SPACE and USET_CASE_INSENSITIVE. - * @param symbols a symbol table mapping variable names to - * values and stand-ins to UnicodeSets; may be NULL - * @param status returns U_ILLEGAL_ARGUMENT_ERROR if the pattern - * contains a syntax error. - * Empties the set passed before applying the pattern. - * @return a reference to this - * @internal - */ - UnicodeSet& applyPattern(const UnicodeString& pattern, - uint32_t options, - const SymbolTable* symbols, - UErrorCode& status); -#endif /* U_HIDE_INTERNAL_API */ - - /** - * Parses the given pattern, starting at the given position. The - * character at pattern.charAt(pos.getIndex()) must be '[', or the - * parse fails. Parsing continues until the corresponding closing - * ']'. If a syntax error is encountered between the opening and - * closing brace, the parse fails. Upon return from a successful - * parse, the ParsePosition is updated to point to the character - * following the closing ']', and a StringBuffer containing a - * pairs list for the parsed pattern is returned. This method calls - * itself recursively to parse embedded subpatterns. - * Empties the set passed before applying the pattern. - * A frozen set will not be modified. - * - * @param pattern the string containing the pattern to be parsed. - * The portion of the string from pos.getIndex(), which must be a - * '[', to the corresponding closing ']', is parsed. - * @param pos upon entry, the position at which to being parsing. - * The character at pattern.charAt(pos.getIndex()) must be a '['. - * Upon return from a successful parse, pos.getIndex() is either - * the character after the closing ']' of the parsed pattern, or - * pattern.length() if the closing ']' is the last character of - * the pattern string. - * @param options bitmask for options to apply to the pattern. - * Valid options are USET_IGNORE_SPACE and USET_CASE_INSENSITIVE. - * @param symbols a symbol table mapping variable names to - * values and stand-ins to UnicodeSets; may be NULL - * @param status returns U_ILLEGAL_ARGUMENT_ERROR if the pattern - * contains a syntax error. - * @return a reference to this - * @stable ICU 2.8 - */ - UnicodeSet& applyPattern(const UnicodeString& pattern, - ParsePosition& pos, - uint32_t options, - const SymbolTable* symbols, - UErrorCode& status); - - /** - * Returns a string representation of this set. If the result of - * calling this function is passed to a UnicodeSet constructor, it - * will produce another set that is equal to this one. - * A frozen set will not be modified. - * @param result the string to receive the rules. Previous - * contents will be deleted. - * @param escapeUnprintable if true then convert unprintable - * character to their hex escape representations, \\uxxxx or - * \\Uxxxxxxxx. Unprintable characters are those other than - * U+000A, U+0020..U+007E. - * @stable ICU 2.0 - */ - virtual UnicodeString& toPattern(UnicodeString& result, - UBool escapeUnprintable = false) const override; - - /** - * Modifies this set to contain those code points which have the given value - * for the given binary or enumerated property, as returned by - * u_getIntPropertyValue. Prior contents of this set are lost. - * A frozen set will not be modified. - * - * @param prop a property in the range UCHAR_BIN_START..UCHAR_BIN_LIMIT-1 - * or UCHAR_INT_START..UCHAR_INT_LIMIT-1 - * or UCHAR_MASK_START..UCHAR_MASK_LIMIT-1. - * - * @param value a value in the range u_getIntPropertyMinValue(prop).. - * u_getIntPropertyMaxValue(prop), with one exception. If prop is - * UCHAR_GENERAL_CATEGORY_MASK, then value should not be a UCharCategory, but - * rather a mask value produced by U_GET_GC_MASK(). This allows grouped - * categories such as [:L:] to be represented. - * - * @param ec error code input/output parameter - * - * @return a reference to this set - * - * @stable ICU 2.4 - */ - UnicodeSet& applyIntPropertyValue(UProperty prop, - int32_t value, - UErrorCode& ec); - - /** - * Modifies this set to contain those code points which have the - * given value for the given property. Prior contents of this - * set are lost. - * A frozen set will not be modified. - * - * @param prop a property alias, either short or long. The name is matched - * loosely. See PropertyAliases.txt for names and a description of loose - * matching. If the value string is empty, then this string is interpreted - * as either a General_Category value alias, a Script value alias, a binary - * property alias, or a special ID. Special IDs are matched loosely and - * correspond to the following sets: - * - * "ANY" = [\\u0000-\\U0010FFFF], - * "ASCII" = [\\u0000-\\u007F], - * "Assigned" = [:^Cn:]. - * - * @param value a value alias, either short or long. The name is matched - * loosely. See PropertyValueAliases.txt for names and a description of - * loose matching. In addition to aliases listed, numeric values and - * canonical combining classes may be expressed numerically, e.g., ("nv", - * "0.5") or ("ccc", "220"). The value string may also be empty. - * - * @param ec error code input/output parameter - * - * @return a reference to this set - * - * @stable ICU 2.4 - */ - UnicodeSet& applyPropertyAlias(const UnicodeString& prop, - const UnicodeString& value, - UErrorCode& ec); - - /** - * Returns the number of elements in this set (its cardinality). - * Note than the elements of a set may include both individual - * codepoints and strings. - * - * This is slower than getRangeCount() because - * it counts the code points of all ranges. - * - * @return the number of elements in this set (its cardinality). - * @stable ICU 2.0 - * @see getRangeCount - */ - virtual int32_t size(void) const; - - /** - * Returns true if this set contains no elements. - * - * @return true if this set contains no elements. - * @stable ICU 2.0 - */ - virtual UBool isEmpty(void) const; - - /** - * @return true if this set contains multi-character strings or the empty string. - * @stable ICU 70 - */ - UBool hasStrings() const; - - /** - * Returns true if this set contains the given character. - * This function works faster with a frozen set. - * @param c character to be checked for containment - * @return true if the test condition is met - * @stable ICU 2.0 - */ - virtual UBool contains(UChar32 c) const override; - - /** - * Returns true if this set contains every character - * of the given range. - * @param start first character, inclusive, of the range - * @param end last character, inclusive, of the range - * @return true if the test condition is met - * @stable ICU 2.0 - */ - virtual UBool contains(UChar32 start, UChar32 end) const; - - /** - * Returns true if this set contains the given - * multicharacter string. - * @param s string to be checked for containment - * @return true if this set contains the specified string - * @stable ICU 2.4 - */ - UBool contains(const UnicodeString& s) const; - - /** - * Returns true if this set contains all the characters and strings - * of the given set. - * @param c set to be checked for containment - * @return true if the test condition is met - * @stable ICU 2.4 - */ - virtual UBool containsAll(const UnicodeSet& c) const; - - /** - * Returns true if this set contains all the characters - * of the given string. - * @param s string containing characters to be checked for containment - * @return true if the test condition is met - * @stable ICU 2.4 - */ - UBool containsAll(const UnicodeString& s) const; - - /** - * Returns true if this set contains none of the characters - * of the given range. - * @param start first character, inclusive, of the range - * @param end last character, inclusive, of the range - * @return true if the test condition is met - * @stable ICU 2.4 - */ - UBool containsNone(UChar32 start, UChar32 end) const; - - /** - * Returns true if this set contains none of the characters and strings - * of the given set. - * @param c set to be checked for containment - * @return true if the test condition is met - * @stable ICU 2.4 - */ - UBool containsNone(const UnicodeSet& c) const; - - /** - * Returns true if this set contains none of the characters - * of the given string. - * @param s string containing characters to be checked for containment - * @return true if the test condition is met - * @stable ICU 2.4 - */ - UBool containsNone(const UnicodeString& s) const; - - /** - * Returns true if this set contains one or more of the characters - * in the given range. - * @param start first character, inclusive, of the range - * @param end last character, inclusive, of the range - * @return true if the condition is met - * @stable ICU 2.4 - */ - inline UBool containsSome(UChar32 start, UChar32 end) const; - - /** - * Returns true if this set contains one or more of the characters - * and strings of the given set. - * @param s The set to be checked for containment - * @return true if the condition is met - * @stable ICU 2.4 - */ - inline UBool containsSome(const UnicodeSet& s) const; - - /** - * Returns true if this set contains one or more of the characters - * of the given string. - * @param s string containing characters to be checked for containment - * @return true if the condition is met - * @stable ICU 2.4 - */ - inline UBool containsSome(const UnicodeString& s) const; - - /** - * Returns the length of the initial substring of the input string which - * consists only of characters and strings that are contained in this set - * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), - * or only of characters and strings that are not contained - * in this set (USET_SPAN_NOT_CONTAINED). - * See USetSpanCondition for details. - * Similar to the strspn() C library function. - * Unpaired surrogates are treated according to contains() of their surrogate code points. - * This function works faster with a frozen set and with a non-negative string length argument. - * @param s start of the string - * @param length of the string; can be -1 for NUL-terminated - * @param spanCondition specifies the containment condition - * @return the length of the initial substring according to the spanCondition; - * 0 if the start of the string does not fit the spanCondition - * @stable ICU 3.8 - * @see USetSpanCondition - */ - int32_t span(const char16_t *s, int32_t length, USetSpanCondition spanCondition) const; - - /** - * Returns the end of the substring of the input string according to the USetSpanCondition. - * Same as start+span(s.getBuffer()+start, s.length()-start, spanCondition) - * after pinning start to 0<=start<=s.length(). - * @param s the string - * @param start the start index in the string for the span operation - * @param spanCondition specifies the containment condition - * @return the exclusive end of the substring according to the spanCondition; - * the substring s.tempSubStringBetween(start, end) fulfills the spanCondition - * @stable ICU 4.4 - * @see USetSpanCondition - */ - inline int32_t span(const UnicodeString &s, int32_t start, USetSpanCondition spanCondition) const; - - /** - * Returns the start of the trailing substring of the input string which - * consists only of characters and strings that are contained in this set - * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), - * or only of characters and strings that are not contained - * in this set (USET_SPAN_NOT_CONTAINED). - * See USetSpanCondition for details. - * Unpaired surrogates are treated according to contains() of their surrogate code points. - * This function works faster with a frozen set and with a non-negative string length argument. - * @param s start of the string - * @param length of the string; can be -1 for NUL-terminated - * @param spanCondition specifies the containment condition - * @return the start of the trailing substring according to the spanCondition; - * the string length if the end of the string does not fit the spanCondition - * @stable ICU 3.8 - * @see USetSpanCondition - */ - int32_t spanBack(const char16_t *s, int32_t length, USetSpanCondition spanCondition) const; - - /** - * Returns the start of the substring of the input string according to the USetSpanCondition. - * Same as spanBack(s.getBuffer(), limit, spanCondition) - * after pinning limit to 0<=end<=s.length(). - * @param s the string - * @param limit the exclusive-end index in the string for the span operation - * (use s.length() or INT32_MAX for spanning back from the end of the string) - * @param spanCondition specifies the containment condition - * @return the start of the substring according to the spanCondition; - * the substring s.tempSubStringBetween(start, limit) fulfills the spanCondition - * @stable ICU 4.4 - * @see USetSpanCondition - */ - inline int32_t spanBack(const UnicodeString &s, int32_t limit, USetSpanCondition spanCondition) const; - - /** - * Returns the length of the initial substring of the input string which - * consists only of characters and strings that are contained in this set - * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), - * or only of characters and strings that are not contained - * in this set (USET_SPAN_NOT_CONTAINED). - * See USetSpanCondition for details. - * Similar to the strspn() C library function. - * Malformed byte sequences are treated according to contains(0xfffd). - * This function works faster with a frozen set and with a non-negative string length argument. - * @param s start of the string (UTF-8) - * @param length of the string; can be -1 for NUL-terminated - * @param spanCondition specifies the containment condition - * @return the length of the initial substring according to the spanCondition; - * 0 if the start of the string does not fit the spanCondition - * @stable ICU 3.8 - * @see USetSpanCondition - */ - int32_t spanUTF8(const char *s, int32_t length, USetSpanCondition spanCondition) const; - - /** - * Returns the start of the trailing substring of the input string which - * consists only of characters and strings that are contained in this set - * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), - * or only of characters and strings that are not contained - * in this set (USET_SPAN_NOT_CONTAINED). - * See USetSpanCondition for details. - * Malformed byte sequences are treated according to contains(0xfffd). - * This function works faster with a frozen set and with a non-negative string length argument. - * @param s start of the string (UTF-8) - * @param length of the string; can be -1 for NUL-terminated - * @param spanCondition specifies the containment condition - * @return the start of the trailing substring according to the spanCondition; - * the string length if the end of the string does not fit the spanCondition - * @stable ICU 3.8 - * @see USetSpanCondition - */ - int32_t spanBackUTF8(const char *s, int32_t length, USetSpanCondition spanCondition) const; - - /** - * Implement UnicodeMatcher::matches() - * @stable ICU 2.4 - */ - virtual UMatchDegree matches(const Replaceable& text, - int32_t& offset, - int32_t limit, - UBool incremental) override; - -private: - /** - * Returns the longest match for s in text at the given position. - * If limit > start then match forward from start+1 to limit - * matching all characters except s.charAt(0). If limit < start, - * go backward starting from start-1 matching all characters - * except s.charAt(s.length()-1). This method assumes that the - * first character, text.charAt(start), matches s, so it does not - * check it. - * @param text the text to match - * @param start the first character to match. In the forward - * direction, text.charAt(start) is matched against s.charAt(0). - * In the reverse direction, it is matched against - * s.charAt(s.length()-1). - * @param limit the limit offset for matching, either last+1 in - * the forward direction, or last-1 in the reverse direction, - * where last is the index of the last character to match. - * @param s - * @return If part of s matches up to the limit, return |limit - - * start|. If all of s matches before reaching the limit, return - * s.length(). If there is a mismatch between s and text, return - * 0 - */ - static int32_t matchRest(const Replaceable& text, - int32_t start, int32_t limit, - const UnicodeString& s); - - /** - * Returns the smallest value i such that c < list[i]. Caller - * must ensure that c is a legal value or this method will enter - * an infinite loop. This method performs a binary search. - * @param c a character in the range MIN_VALUE..MAX_VALUE - * inclusive - * @return the smallest integer i in the range 0..len-1, - * inclusive, such that c < list[i] - */ - int32_t findCodePoint(UChar32 c) const; - -public: - - /** - * Implementation of UnicodeMatcher API. Union the set of all - * characters that may be matched by this object into the given - * set. - * @param toUnionTo the set into which to union the source characters - * @stable ICU 2.4 - */ - virtual void addMatchSetTo(UnicodeSet& toUnionTo) const override; - - /** - * Returns the index of the given character within this set, where - * the set is ordered by ascending code point. If the character - * is not in this set, return -1. The inverse of this method is - * charAt(). - * @return an index from 0..size()-1, or -1 - * @stable ICU 2.4 - */ - int32_t indexOf(UChar32 c) const; - - /** - * Returns the character at the given index within this set, where - * the set is ordered by ascending code point. If the index is - * out of range for characters, returns (UChar32)-1. - * The inverse of this method is indexOf(). - * - * For iteration, this is slower than UnicodeSetIterator or - * getRangeCount()/getRangeStart()/getRangeEnd(), - * because for each call it skips linearly over index - * characters in the ranges. - * - * @param index an index from 0..size()-1 - * @return the character at the given index, or (UChar32)-1. - * @stable ICU 2.4 - */ - UChar32 charAt(int32_t index) const; - - /** - * Adds the specified range to this set if it is not already - * present. If this set already contains the specified range, - * the call leaves this set unchanged. If start > end - * then an empty range is added, leaving the set unchanged. - * This is equivalent to a boolean logic OR, or a set UNION. - * A frozen set will not be modified. - * - * @param start first character, inclusive, of range to be added - * to this set. - * @param end last character, inclusive, of range to be added - * to this set. - * @stable ICU 2.0 - */ - virtual UnicodeSet& add(UChar32 start, UChar32 end); - - /** - * Adds the specified character to this set if it is not already - * present. If this set already contains the specified character, - * the call leaves this set unchanged. - * A frozen set will not be modified. - * - * @param c the character (code point) - * @return this object, for chaining - * @stable ICU 2.0 - */ - UnicodeSet& add(UChar32 c); - - /** - * Adds the specified multicharacter to this set if it is not already - * present. If this set already contains the multicharacter, - * the call leaves this set unchanged. - * Thus "ch" => {"ch"} - * A frozen set will not be modified. - * - * @param s the source string - * @return this object, for chaining - * @stable ICU 2.4 - */ - UnicodeSet& add(const UnicodeString& s); - - private: - /** - * @return a code point IF the string consists of a single one. - * otherwise returns -1. - * @param s string to test - */ - static int32_t getSingleCP(const UnicodeString& s); - - void _add(const UnicodeString& s); - - public: - /** - * Adds each of the characters in this string to the set. Note: "ch" => {"c", "h"} - * If this set already contains any particular character, it has no effect on that character. - * A frozen set will not be modified. - * @param s the source string - * @return this object, for chaining - * @stable ICU 2.4 - */ - UnicodeSet& addAll(const UnicodeString& s); - - /** - * Retains EACH of the characters in this string. Note: "ch" == {"c", "h"} - * A frozen set will not be modified. - * @param s the source string - * @return this object, for chaining - * @stable ICU 2.4 - */ - UnicodeSet& retainAll(const UnicodeString& s); - - /** - * Complement EACH of the characters in this string. Note: "ch" == {"c", "h"} - * A frozen set will not be modified. - * @param s the source string - * @return this object, for chaining - * @stable ICU 2.4 - */ - UnicodeSet& complementAll(const UnicodeString& s); - - /** - * Remove EACH of the characters in this string. Note: "ch" == {"c", "h"} - * A frozen set will not be modified. - * @param s the source string - * @return this object, for chaining - * @stable ICU 2.4 - */ - UnicodeSet& removeAll(const UnicodeString& s); - - /** - * Makes a set from a multicharacter string. Thus "ch" => {"ch"} - * - * @param s the source string - * @return a newly created set containing the given string. - * The caller owns the return object and is responsible for deleting it. - * @stable ICU 2.4 - */ - static UnicodeSet* U_EXPORT2 createFrom(const UnicodeString& s); - - - /** - * Makes a set from each of the characters in the string. Thus "ch" => {"c", "h"} - * @param s the source string - * @return a newly created set containing the given characters - * The caller owns the return object and is responsible for deleting it. - * @stable ICU 2.4 - */ - static UnicodeSet* U_EXPORT2 createFromAll(const UnicodeString& s); - - /** - * Retain only the elements in this set that are contained in the - * specified range. If start > end then an empty range is - * retained, leaving the set empty. This is equivalent to - * a boolean logic AND, or a set INTERSECTION. - * A frozen set will not be modified. - * - * @param start first character, inclusive, of range - * @param end last character, inclusive, of range - * @stable ICU 2.0 - */ - virtual UnicodeSet& retain(UChar32 start, UChar32 end); - - - /** - * Retain the specified character from this set if it is present. - * A frozen set will not be modified. - * - * @param c the character (code point) - * @return this object, for chaining - * @stable ICU 2.0 - */ - UnicodeSet& retain(UChar32 c); - - /** - * Retains only the specified string from this set if it is present. - * Upon return this set will be empty if it did not contain s, or - * will only contain s if it did contain s. - * A frozen set will not be modified. - * - * @param s the source string - * @return this object, for chaining - * @stable ICU 69 - */ - UnicodeSet& retain(const UnicodeString &s); - - /** - * Removes the specified range from this set if it is present. - * The set will not contain the specified range once the call - * returns. If start > end then an empty range is - * removed, leaving the set unchanged. - * A frozen set will not be modified. - * - * @param start first character, inclusive, of range to be removed - * from this set. - * @param end last character, inclusive, of range to be removed - * from this set. - * @stable ICU 2.0 - */ - virtual UnicodeSet& remove(UChar32 start, UChar32 end); - - /** - * Removes the specified character from this set if it is present. - * The set will not contain the specified range once the call - * returns. - * A frozen set will not be modified. - * - * @param c the character (code point) - * @return this object, for chaining - * @stable ICU 2.0 - */ - UnicodeSet& remove(UChar32 c); - - /** - * Removes the specified string from this set if it is present. - * The set will not contain the specified character once the call - * returns. - * A frozen set will not be modified. - * @param s the source string - * @return this object, for chaining - * @stable ICU 2.4 - */ - UnicodeSet& remove(const UnicodeString& s); - - /** - * This is equivalent to - * complement(MIN_VALUE, MAX_VALUE). - * - * Note: This performs a symmetric difference with all code points - * and thus retains all multicharacter strings. - * In order to achieve a “code point complement” (all code points minus this set), - * the easiest is to .complement().removeAllStrings(). - * - * A frozen set will not be modified. - * @stable ICU 2.0 - */ - virtual UnicodeSet& complement(); - - /** - * Complements the specified range in this set. Any character in - * the range will be removed if it is in this set, or will be - * added if it is not in this set. If start > end - * then an empty range is complemented, leaving the set unchanged. - * This is equivalent to a boolean logic XOR. - * A frozen set will not be modified. - * - * @param start first character, inclusive, of range - * @param end last character, inclusive, of range - * @stable ICU 2.0 - */ - virtual UnicodeSet& complement(UChar32 start, UChar32 end); - - /** - * Complements the specified character in this set. The character - * will be removed if it is in this set, or will be added if it is - * not in this set. - * A frozen set will not be modified. - * - * @param c the character (code point) - * @return this object, for chaining - * @stable ICU 2.0 - */ - UnicodeSet& complement(UChar32 c); - - /** - * Complement the specified string in this set. - * The string will be removed if it is in this set, or will be added if it is not in this set. - * A frozen set will not be modified. - * - * @param s the string to complement - * @return this object, for chaining - * @stable ICU 2.4 - */ - UnicodeSet& complement(const UnicodeString& s); - - /** - * Adds all of the elements in the specified set to this set if - * they're not already present. This operation effectively - * modifies this set so that its value is the union of the two - * sets. The behavior of this operation is unspecified if the specified - * collection is modified while the operation is in progress. - * A frozen set will not be modified. - * - * @param c set whose elements are to be added to this set. - * @see #add(UChar32, UChar32) - * @stable ICU 2.0 - */ - virtual UnicodeSet& addAll(const UnicodeSet& c); - - /** - * Retains only the elements in this set that are contained in the - * specified set. In other words, removes from this set all of - * its elements that are not contained in the specified set. This - * operation effectively modifies this set so that its value is - * the intersection of the two sets. - * A frozen set will not be modified. - * - * @param c set that defines which elements this set will retain. - * @stable ICU 2.0 - */ - virtual UnicodeSet& retainAll(const UnicodeSet& c); - - /** - * Removes from this set all of its elements that are contained in the - * specified set. This operation effectively modifies this - * set so that its value is the asymmetric set difference of - * the two sets. - * A frozen set will not be modified. - * - * @param c set that defines which elements will be removed from - * this set. - * @stable ICU 2.0 - */ - virtual UnicodeSet& removeAll(const UnicodeSet& c); - - /** - * Complements in this set all elements contained in the specified - * set. Any character in the other set will be removed if it is - * in this set, or will be added if it is not in this set. - * A frozen set will not be modified. - * - * @param c set that defines which elements will be xor'ed from - * this set. - * @stable ICU 2.4 - */ - virtual UnicodeSet& complementAll(const UnicodeSet& c); - - /** - * Removes all of the elements from this set. This set will be - * empty after this call returns. - * A frozen set will not be modified. - * @stable ICU 2.0 - */ - virtual UnicodeSet& clear(void); - - /** - * Close this set over the given attribute. For the attribute - * USET_CASE, the result is to modify this set so that: - * - * 1. For each character or string 'a' in this set, all strings or - * characters 'b' such that foldCase(a) == foldCase(b) are added - * to this set. - * - * 2. For each string 'e' in the resulting set, if e != - * foldCase(e), 'e' will be removed. - * - * Example: [aq\\u00DF{Bc}{bC}{Fi}] => [aAqQ\\u00DF\\uFB01{ss}{bc}{fi}] - * - * (Here foldCase(x) refers to the operation u_strFoldCase, and a - * == b denotes that the contents are the same, not pointer - * comparison.) - * - * A frozen set will not be modified. - * - * @param attribute bitmask for attributes to close over. - * Currently only the USET_CASE bit is supported. Any undefined bits - * are ignored. - * @return a reference to this set. - * @stable ICU 4.2 - */ - UnicodeSet& closeOver(int32_t attribute); - - /** - * Remove all strings from this set. - * - * @return a reference to this set. - * @stable ICU 4.2 - */ - virtual UnicodeSet &removeAllStrings(); - - /** - * Iteration method that returns the number of ranges contained in - * this set. - * @see #getRangeStart - * @see #getRangeEnd - * @stable ICU 2.4 - */ - virtual int32_t getRangeCount(void) const; - - /** - * Iteration method that returns the first character in the - * specified range of this set. - * @see #getRangeCount - * @see #getRangeEnd - * @stable ICU 2.4 - */ - virtual UChar32 getRangeStart(int32_t index) const; - - /** - * Iteration method that returns the last character in the - * specified range of this set. - * @see #getRangeStart - * @see #getRangeEnd - * @stable ICU 2.4 - */ - virtual UChar32 getRangeEnd(int32_t index) const; - - /** - * Serializes this set into an array of 16-bit integers. Serialization - * (currently) only records the characters in the set; multicharacter - * strings are ignored. - * - * The array has following format (each line is one 16-bit - * integer): - * - * length = (n+2*m) | (m!=0?0x8000:0) - * bmpLength = n; present if m!=0 - * bmp[0] - * bmp[1] - * ... - * bmp[n-1] - * supp-high[0] - * supp-low[0] - * supp-high[1] - * supp-low[1] - * ... - * supp-high[m-1] - * supp-low[m-1] - * - * The array starts with a header. After the header are n bmp - * code points, then m supplementary code points. Either n or m - * or both may be zero. n+2*m is always <= 0x7FFF. - * - * If there are no supplementary characters (if m==0) then the - * header is one 16-bit integer, 'length', with value n. - * - * If there are supplementary characters (if m!=0) then the header - * is two 16-bit integers. The first, 'length', has value - * (n+2*m)|0x8000. The second, 'bmpLength', has value n. - * - * After the header the code points are stored in ascending order. - * Supplementary code points are stored as most significant 16 - * bits followed by least significant 16 bits. - * - * @param dest pointer to buffer of destCapacity 16-bit integers. - * May be NULL only if destCapacity is zero. - * @param destCapacity size of dest, or zero. Must not be negative. - * @param ec error code. Will be set to U_INDEX_OUTOFBOUNDS_ERROR - * if n+2*m > 0x7FFF. Will be set to U_BUFFER_OVERFLOW_ERROR if - * n+2*m+(m!=0?2:1) > destCapacity. - * @return the total length of the serialized format, including - * the header, that is, n+2*m+(m!=0?2:1), or 0 on error other - * than U_BUFFER_OVERFLOW_ERROR. - * @stable ICU 2.4 - */ - int32_t serialize(uint16_t *dest, int32_t destCapacity, UErrorCode& ec) const; - - /** - * Reallocate this objects internal structures to take up the least - * possible space, without changing this object's value. - * A frozen set will not be modified. - * @stable ICU 2.4 - */ - virtual UnicodeSet& compact(); - - /** - * Return the class ID for this class. This is useful only for - * comparing to a return value from getDynamicClassID(). For example: - *

-     * .      Base* polymorphic_pointer = createPolymorphicObject();
-     * .      if (polymorphic_pointer->getDynamicClassID() ==
-     * .          Derived::getStaticClassID()) ...
-     * 
- * @return The class ID for all objects of this class. - * @stable ICU 2.0 - */ - static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * Implement UnicodeFunctor API. - * - * @return The class ID for this object. All objects of a given - * class have the same class ID. Objects of other classes have - * different class IDs. - * @stable ICU 2.4 - */ - virtual UClassID getDynamicClassID(void) const override; - -private: - - // Private API for the USet API - - friend class USetAccess; - - const UnicodeString* getString(int32_t index) const; - - //---------------------------------------------------------------- - // RuleBasedTransliterator support - //---------------------------------------------------------------- - -private: - - /** - * Returns true if this set contains any character whose low byte - * is the given value. This is used by RuleBasedTransliterator for - * indexing. - */ - virtual UBool matchesIndexValue(uint8_t v) const override; - -private: - friend class RBBIRuleScanner; - - //---------------------------------------------------------------- - // Implementation: Clone as thawed (see ICU4J Freezable) - //---------------------------------------------------------------- - - UnicodeSet(const UnicodeSet& o, UBool /* asThawed */); - UnicodeSet& copyFrom(const UnicodeSet& o, UBool asThawed); - - //---------------------------------------------------------------- - // Implementation: Pattern parsing - //---------------------------------------------------------------- - - void applyPatternIgnoreSpace(const UnicodeString& pattern, - ParsePosition& pos, - const SymbolTable* symbols, - UErrorCode& status); - - void applyPattern(RuleCharacterIterator& chars, - const SymbolTable* symbols, - UnicodeString& rebuiltPat, - uint32_t options, - UnicodeSet& (UnicodeSet::*caseClosure)(int32_t attribute), - int32_t depth, - UErrorCode& ec); - - //---------------------------------------------------------------- - // Implementation: Utility methods - //---------------------------------------------------------------- - - static int32_t nextCapacity(int32_t minCapacity); - - bool ensureCapacity(int32_t newLen); - - bool ensureBufferCapacity(int32_t newLen); - - void swapBuffers(void); - - UBool allocateStrings(UErrorCode &status); - int32_t stringsSize() const; - UBool stringsContains(const UnicodeString &s) const; - - UnicodeString& _toPattern(UnicodeString& result, - UBool escapeUnprintable) const; - - UnicodeString& _generatePattern(UnicodeString& result, - UBool escapeUnprintable) const; - - static void _appendToPat(UnicodeString& buf, const UnicodeString& s, UBool escapeUnprintable); - - static void _appendToPat(UnicodeString& buf, UChar32 c, UBool escapeUnprintable); - - static void _appendToPat(UnicodeString &result, UChar32 start, UChar32 end, - UBool escapeUnprintable); - - //---------------------------------------------------------------- - // Implementation: Fundamental operators - //---------------------------------------------------------------- - - void exclusiveOr(const UChar32* other, int32_t otherLen, int8_t polarity); - - void add(const UChar32* other, int32_t otherLen, int8_t polarity); - - void retain(const UChar32* other, int32_t otherLen, int8_t polarity); - - /** - * Return true if the given position, in the given pattern, appears - * to be the start of a property set pattern [:foo:], \\p{foo}, or - * \\P{foo}, or \\N{name}. - */ - static UBool resemblesPropertyPattern(const UnicodeString& pattern, - int32_t pos); - - static UBool resemblesPropertyPattern(RuleCharacterIterator& chars, - int32_t iterOpts); - - /** - * Parse the given property pattern at the given parse position - * and set this UnicodeSet to the result. - * - * The original design document is out of date, but still useful. - * Ignore the property and value names: - * https://htmlpreview.github.io/?https://github.com/unicode-org/icu-docs/blob/main/design/unicodeset_properties.html - * - * Recognized syntax: - * - * [:foo:] [:^foo:] - white space not allowed within "[:" or ":]" - * \\p{foo} \\P{foo} - white space not allowed within "\\p" or "\\P" - * \\N{name} - white space not allowed within "\\N" - * - * Other than the above restrictions, Unicode Pattern_White_Space characters are ignored. - * Case is ignored except in "\\p" and "\\P" and "\\N". In 'name' leading - * and trailing space is deleted, and internal runs of whitespace - * are collapsed to a single space. - * - * We support binary properties, enumerated properties, and the - * following non-enumerated properties: - * - * Numeric_Value - * Name - * Unicode_1_Name - * - * @param pattern the pattern string - * @param ppos on entry, the position at which to begin parsing. - * This should be one of the locations marked '^': - * - * [:blah:] \\p{blah} \\P{blah} \\N{name} - * ^ % ^ % ^ % ^ % - * - * On return, the position after the last character parsed, that is, - * the locations marked '%'. If the parse fails, ppos is returned - * unchanged. - * @param ec status - * @return a reference to this. - */ - UnicodeSet& applyPropertyPattern(const UnicodeString& pattern, - ParsePosition& ppos, - UErrorCode &ec); - - void applyPropertyPattern(RuleCharacterIterator& chars, - UnicodeString& rebuiltPat, - UErrorCode& ec); - - /** - * A filter that returns true if the given code point should be - * included in the UnicodeSet being constructed. - */ - typedef UBool (*Filter)(UChar32 codePoint, void* context); - - /** - * Given a filter, set this UnicodeSet to the code points - * contained by that filter. The filter MUST be - * property-conformant. That is, if it returns value v for one - * code point, then it must return v for all affiliated code - * points, as defined by the inclusions list. See - * getInclusions(). - * src is a UPropertySource value. - */ - void applyFilter(Filter filter, - void* context, - const UnicodeSet* inclusions, - UErrorCode &status); - - /** - * Set the new pattern to cache. - */ - void setPattern(const UnicodeString& newPat) { - setPattern(newPat.getBuffer(), newPat.length()); - } - void setPattern(const char16_t *newPat, int32_t newPatLen); - /** - * Release existing cached pattern. - */ - void releasePattern(); - - friend class UnicodeSetIterator; -}; - - - -inline bool UnicodeSet::operator!=(const UnicodeSet& o) const { - return !operator==(o); -} - -inline UBool UnicodeSet::isFrozen() const { - return (UBool)(bmpSet!=NULL || stringSpan!=NULL); -} - -inline UBool UnicodeSet::containsSome(UChar32 start, UChar32 end) const { - return !containsNone(start, end); -} - -inline UBool UnicodeSet::containsSome(const UnicodeSet& s) const { - return !containsNone(s); -} - -inline UBool UnicodeSet::containsSome(const UnicodeString& s) const { - return !containsNone(s); -} - -inline UBool UnicodeSet::isBogus() const { - return (UBool)(fFlags & kIsBogus); -} - -inline UnicodeSet *UnicodeSet::fromUSet(USet *uset) { - return reinterpret_cast(uset); -} - -inline const UnicodeSet *UnicodeSet::fromUSet(const USet *uset) { - return reinterpret_cast(uset); -} - -inline USet *UnicodeSet::toUSet() { - return reinterpret_cast(this); -} - -inline const USet *UnicodeSet::toUSet() const { - return reinterpret_cast(this); -} - -inline int32_t UnicodeSet::span(const UnicodeString &s, int32_t start, USetSpanCondition spanCondition) const { - int32_t sLength=s.length(); - if(start<0) { - start=0; - } else if(start>sLength) { - start=sLength; - } - return start+span(s.getBuffer()+start, sLength-start, spanCondition); -} - -inline int32_t UnicodeSet::spanBack(const UnicodeString &s, int32_t limit, USetSpanCondition spanCondition) const { - int32_t sLength=s.length(); - if(limit<0) { - limit=0; - } else if(limit>sLength) { - limit=sLength; - } - return spanBack(s.getBuffer(), limit, spanCondition); -} - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +*************************************************************************** +* Copyright (C) 1999-2016, International Business Machines Corporation +* and others. All Rights Reserved. +*************************************************************************** +* Date Name Description +* 10/20/99 alan Creation. +*************************************************************************** +*/ + +#ifndef UNICODESET_H +#define UNICODESET_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/ucpmap.h" +#include "unicode/unifilt.h" +#include "unicode/unistr.h" +#include "unicode/uset.h" + +/** + * \file + * \brief C++ API: Unicode Set + */ + +U_NAMESPACE_BEGIN + +// Forward Declarations. +class BMPSet; +class ParsePosition; +class RBBIRuleScanner; +class SymbolTable; +class UnicodeSetStringSpan; +class UVector; +class RuleCharacterIterator; + +/** + * A mutable set of Unicode characters and multicharacter strings. Objects of this class + * represent character classes used in regular expressions. + * A character specifies a subset of Unicode code points. Legal + * code points are U+0000 to U+10FFFF, inclusive. + * + *

The UnicodeSet class is not designed to be subclassed. + * + *

UnicodeSet supports two APIs. The first is the + * operand API that allows the caller to modify the value of + * a UnicodeSet object. It conforms to Java 2's + * java.util.Set interface, although + * UnicodeSet does not actually implement that + * interface. All methods of Set are supported, with the + * modification that they take a character range or single character + * instead of an Object, and they take a + * UnicodeSet instead of a Collection. The + * operand API may be thought of in terms of boolean logic: a boolean + * OR is implemented by add, a boolean AND is implemented + * by retain, a boolean XOR is implemented by + * complement taking an argument, and a boolean NOT is + * implemented by complement with no argument. In terms + * of traditional set theory function names, add is a + * union, retain is an intersection, remove + * is an asymmetric difference, and complement with no + * argument is a set complement with respect to the superset range + * MIN_VALUE-MAX_VALUE + * + *

The second API is the + * applyPattern()/toPattern() API from the + * java.text.Format-derived classes. Unlike the + * methods that add characters, add categories, and control the logic + * of the set, the method applyPattern() sets all + * attributes of a UnicodeSet at once, based on a + * string pattern. + * + *

Pattern syntax

+ * + * Patterns are accepted by the constructors and the + * applyPattern() methods and returned by the + * toPattern() method. These patterns follow a syntax + * similar to that employed by version 8 regular expression character + * classes. Here are some simple examples: + * + * \htmlonly
\endhtmlonly + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
[]No characters
[a]The character 'a'
[ae]The characters 'a' and 'e'
[a-e]The characters 'a' through 'e' inclusive, in Unicode code + * point order
[\\u4E01]The character U+4E01
[a{ab}{ac}]The character 'a' and the multicharacter strings "ab" and + * "ac"
[\\p{Lu}]All characters in the general category Uppercase Letter
+ * \htmlonly
\endhtmlonly + * + * Any character may be preceded by a backslash in order to remove any special + * meaning. White space characters, as defined by UCharacter.isWhitespace(), are + * ignored, unless they are escaped. + * + *

Property patterns specify a set of characters having a certain + * property as defined by the Unicode standard. Both the POSIX-like + * "[:Lu:]" and the Perl-like syntax "\\p{Lu}" are recognized. For a + * complete list of supported property patterns, see the User's Guide + * for UnicodeSet at + * + * https://unicode-org.github.io/icu/userguide/strings/unicodeset. + * Actual determination of property data is defined by the underlying + * Unicode database as implemented by UCharacter. + * + *

Patterns specify individual characters, ranges of characters, and + * Unicode property sets. When elements are concatenated, they + * specify their union. To complement a set, place a '^' immediately + * after the opening '['. Property patterns are inverted by modifying + * their delimiters; "[:^foo]" and "\\P{foo}". In any other location, + * '^' has no special meaning. + * + *

Since ICU 70, "[^...]", "[:^foo]", "\\P{foo}", and "[:binaryProperty=No:]" + * perform a “code point complement” (all code points minus the original set), + * removing all multicharacter strings, + * equivalent to .complement().removeAllStrings(). + * The complement() API function continues to perform a + * symmetric difference with all code points and thus retains all multicharacter strings. + * + *

Ranges are indicated by placing two a '-' between two + * characters, as in "a-z". This specifies the range of all + * characters from the left to the right, in Unicode order. If the + * left character is greater than or equal to the + * right character it is a syntax error. If a '-' occurs as the first + * character after the opening '[' or '[^', or if it occurs as the + * last character before the closing ']', then it is taken as a + * literal. Thus "[a\-b]", "[-ab]", and "[ab-]" all indicate the same + * set of three characters, 'a', 'b', and '-'. + * + *

Sets may be intersected using the '&' operator or the asymmetric + * set difference may be taken using the '-' operator, for example, + * "[[:L:]&[\\u0000-\\u0FFF]]" indicates the set of all Unicode letters + * with values less than 4096. Operators ('&' and '|') have equal + * precedence and bind left-to-right. Thus + * "[[:L:]-[a-z]-[\\u0100-\\u01FF]]" is equivalent to + * "[[[:L:]-[a-z]]-[\\u0100-\\u01FF]]". This only really matters for + * difference; intersection is commutative. + * + * + *
[a]The set containing 'a' + *
[a-z]The set containing 'a' + * through 'z' and all letters in between, in Unicode order + *
[^a-z]The set containing + * all characters but 'a' through 'z', + * that is, U+0000 through 'a'-1 and 'z'+1 through U+10FFFF + *
[[pat1][pat2]] + * The union of sets specified by pat1 and pat2 + *
[[pat1]&[pat2]] + * The intersection of sets specified by pat1 and pat2 + *
[[pat1]-[pat2]] + * The asymmetric difference of sets specified by pat1 and + * pat2 + *
[:Lu:] or \\p{Lu} + * The set of characters having the specified + * Unicode property; in + * this case, Unicode uppercase letters + *
[:^Lu:] or \\P{Lu} + * The set of characters not having the given + * Unicode property + *
+ * + *

Formal syntax

+ * + * \htmlonly
\endhtmlonly + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
pattern :=  ('[' '^'? item* ']') | + * property
item :=  char | (char '-' char) | pattern-expr
+ *
pattern-expr :=  pattern | pattern-expr pattern | + * pattern-expr op pattern
+ *
op :=  '&' | '-'
+ *
special :=  '[' | ']' | '-'
+ *
char :=  any character that is not special
+ * | ('\'
any character)
+ * | ('\\u' hex hex hex hex)
+ *
hex :=  '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' |
+ *     'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
property :=  a Unicode property set pattern
+ *
+ * + * + * + * + *
Legend: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
a := b  a may be replaced by b
a?zero or one instance of a
+ *
a*one or more instances of a
+ *
a | beither a or b
+ *
'a'the literal string between the quotes
+ *
+ * \htmlonly
\endhtmlonly + * + *

Note: + * - Most UnicodeSet methods do not take a UErrorCode parameter because + * there are usually very few opportunities for failure other than a shortage + * of memory, error codes in low-level C++ string methods would be inconvenient, + * and the error code as the last parameter (ICU convention) would prevent + * the use of default parameter values. + * Instead, such methods set the UnicodeSet into a "bogus" state + * (see isBogus()) if an error occurs. + * + * @author Alan Liu + * @stable ICU 2.0 + */ +class U_COMMON_API UnicodeSet final : public UnicodeFilter { +private: + /** + * Enough for sets with few ranges. + * For example, White_Space has 10 ranges, list length 21. + */ + static constexpr int32_t INITIAL_CAPACITY = 25; + // fFlags constant + static constexpr uint8_t kIsBogus = 1; // This set is bogus (i.e. not valid) + + UChar32* list = stackList; // MUST be terminated with HIGH + int32_t capacity = INITIAL_CAPACITY; // capacity of list + int32_t len = 1; // length of list used; 1 <= len <= capacity + uint8_t fFlags = 0; // Bit flag (see constants above) + + BMPSet *bmpSet = nullptr; // The set is frozen iff either bmpSet or stringSpan is not nullptr. + UChar32* buffer = nullptr; // internal buffer, may be nullptr + int32_t bufferCapacity = 0; // capacity of buffer + + /** + * The pattern representation of this set. This may not be the + * most economical pattern. It is the pattern supplied to + * applyPattern(), with variables substituted and whitespace + * removed. For sets constructed without applyPattern(), or + * modified using the non-pattern API, this string will be empty, + * indicating that toPattern() must generate a pattern + * representation from the inversion list. + */ + char16_t *pat = nullptr; + int32_t patLen = 0; + + UVector* strings = nullptr; // maintained in sorted order + UnicodeSetStringSpan *stringSpan = nullptr; + + /** + * Initial list array. + * Avoids some heap allocations, and list is never nullptr. + * Increases the object size a bit. + */ + UChar32 stackList[INITIAL_CAPACITY]; + +public: + /** + * Determine if this object contains a valid set. + * A bogus set has no value. It is different from an empty set. + * It can be used to indicate that no set value is available. + * + * @return true if the set is bogus/invalid, false otherwise + * @see setToBogus() + * @stable ICU 4.0 + */ + inline UBool isBogus(void) const; + + /** + * Make this UnicodeSet object invalid. + * The string will test true with isBogus(). + * + * A bogus set has no value. It is different from an empty set. + * It can be used to indicate that no set value is available. + * + * This utility function is used throughout the UnicodeSet + * implementation to indicate that a UnicodeSet operation failed, + * and may be used in other functions, + * especially but not exclusively when such functions do not + * take a UErrorCode for simplicity. + * + * @see isBogus() + * @stable ICU 4.0 + */ + void setToBogus(); + +public: + + enum { + /** + * Minimum value that can be stored in a UnicodeSet. + * @stable ICU 2.4 + */ + MIN_VALUE = 0, + + /** + * Maximum value that can be stored in a UnicodeSet. + * @stable ICU 2.4 + */ + MAX_VALUE = 0x10ffff + }; + + //---------------------------------------------------------------- + // Constructors &c + //---------------------------------------------------------------- + +public: + + /** + * Constructs an empty set. + * @stable ICU 2.0 + */ + UnicodeSet(); + + /** + * Constructs a set containing the given range. If end < + * start then an empty set is created. + * + * @param start first character, inclusive, of range + * @param end last character, inclusive, of range + * @stable ICU 2.4 + */ + UnicodeSet(UChar32 start, UChar32 end); + +#ifndef U_HIDE_INTERNAL_API + /** + * @internal + */ + enum ESerialization { + kSerialized /* result of serialize() */ + }; + + /** + * Constructs a set from the output of serialize(). + * + * @param buffer the 16 bit array + * @param bufferLen the original length returned from serialize() + * @param serialization the value 'kSerialized' + * @param status error code + * + * @internal + */ + UnicodeSet(const uint16_t buffer[], int32_t bufferLen, + ESerialization serialization, UErrorCode &status); +#endif /* U_HIDE_INTERNAL_API */ + + /** + * Constructs a set from the given pattern. See the class + * description for the syntax of the pattern language. + * @param pattern a string specifying what characters are in the set + * @param status returns U_ILLEGAL_ARGUMENT_ERROR if the pattern + * contains a syntax error. + * @stable ICU 2.0 + */ + UnicodeSet(const UnicodeString& pattern, + UErrorCode& status); + +#ifndef U_HIDE_INTERNAL_API + /** + * Constructs a set from the given pattern. See the class + * description for the syntax of the pattern language. + * @param pattern a string specifying what characters are in the set + * @param options bitmask for options to apply to the pattern. + * Valid options are USET_IGNORE_SPACE and + * at most one of USET_CASE_INSENSITIVE, USET_ADD_CASE_MAPPINGS, USET_SIMPLE_CASE_INSENSITIVE. + * These case options are mutually exclusive. + * @param symbols a symbol table mapping variable names to values + * and stand-in characters to UnicodeSets; may be nullptr + * @param status returns U_ILLEGAL_ARGUMENT_ERROR if the pattern + * contains a syntax error. + * @internal + */ + UnicodeSet(const UnicodeString& pattern, + uint32_t options, + const SymbolTable* symbols, + UErrorCode& status); +#endif /* U_HIDE_INTERNAL_API */ + + /** + * Constructs a set from the given pattern. See the class description + * for the syntax of the pattern language. + * @param pattern a string specifying what characters are in the set + * @param pos on input, the position in pattern at which to start parsing. + * On output, the position after the last character parsed. + * @param options bitmask for options to apply to the pattern. + * Valid options are USET_IGNORE_SPACE and + * at most one of USET_CASE_INSENSITIVE, USET_ADD_CASE_MAPPINGS, USET_SIMPLE_CASE_INSENSITIVE. + * These case options are mutually exclusive. + * @param symbols a symbol table mapping variable names to values + * and stand-in characters to UnicodeSets; may be nullptr + * @param status input-output error code + * @stable ICU 2.8 + */ + UnicodeSet(const UnicodeString& pattern, ParsePosition& pos, + uint32_t options, + const SymbolTable* symbols, + UErrorCode& status); + + /** + * Constructs a set that is identical to the given UnicodeSet. + * @stable ICU 2.0 + */ + UnicodeSet(const UnicodeSet& o); + + /** + * Destructs the set. + * @stable ICU 2.0 + */ + virtual ~UnicodeSet(); + + /** + * Assigns this object to be a copy of another. + * A frozen set will not be modified. + * @stable ICU 2.0 + */ + UnicodeSet& operator=(const UnicodeSet& o); + + /** + * Compares the specified object with this set for equality. Returns + * true if the two sets + * have the same size, and every member of the specified set is + * contained in this set (or equivalently, every member of this set is + * contained in the specified set). + * + * @param o set to be compared for equality with this set. + * @return true if the specified set is equal to this set. + * @stable ICU 2.0 + */ + virtual bool operator==(const UnicodeSet& o) const; + + /** + * Compares the specified object with this set for equality. Returns + * true if the specified set is not equal to this set. + * @stable ICU 2.0 + */ + inline bool operator!=(const UnicodeSet& o) const; + + /** + * Returns a copy of this object. All UnicodeFunctor objects have + * to support cloning in order to allow classes using + * UnicodeFunctors, such as Transliterator, to implement cloning. + * If this set is frozen, then the clone will be frozen as well. + * Use cloneAsThawed() for a mutable clone of a frozen set. + * @see cloneAsThawed + * @stable ICU 2.0 + */ + virtual UnicodeSet* clone() const override; + + /** + * Returns the hash code value for this set. + * + * @return the hash code value for this set. + * @see Object#hashCode() + * @stable ICU 2.0 + */ + virtual int32_t hashCode(void) const; + + /** + * Get a UnicodeSet pointer from a USet + * + * @param uset a USet (the ICU plain C type for UnicodeSet) + * @return the corresponding UnicodeSet pointer. + * + * @stable ICU 4.2 + */ + inline static UnicodeSet *fromUSet(USet *uset); + + /** + * Get a UnicodeSet pointer from a const USet + * + * @param uset a const USet (the ICU plain C type for UnicodeSet) + * @return the corresponding UnicodeSet pointer. + * + * @stable ICU 4.2 + */ + inline static const UnicodeSet *fromUSet(const USet *uset); + + /** + * Produce a USet * pointer for this UnicodeSet. + * USet is the plain C type for UnicodeSet + * + * @return a USet pointer for this UnicodeSet + * @stable ICU 4.2 + */ + inline USet *toUSet(); + + + /** + * Produce a const USet * pointer for this UnicodeSet. + * USet is the plain C type for UnicodeSet + * + * @return a const USet pointer for this UnicodeSet + * @stable ICU 4.2 + */ + inline const USet * toUSet() const; + + + //---------------------------------------------------------------- + // Freezable API + //---------------------------------------------------------------- + + /** + * Determines whether the set has been frozen (made immutable) or not. + * See the ICU4J Freezable interface for details. + * @return true/false for whether the set has been frozen + * @see freeze + * @see cloneAsThawed + * @stable ICU 3.8 + */ + inline UBool isFrozen() const; + + /** + * Freeze the set (make it immutable). + * Once frozen, it cannot be unfrozen and is therefore thread-safe + * until it is deleted. + * See the ICU4J Freezable interface for details. + * Freezing the set may also make some operations faster, for example + * contains() and span(). + * A frozen set will not be modified. (It remains frozen.) + * @return this set. + * @see isFrozen + * @see cloneAsThawed + * @stable ICU 3.8 + */ + UnicodeSet *freeze(); + + /** + * Clone the set and make the clone mutable. + * See the ICU4J Freezable interface for details. + * @return the mutable clone + * @see freeze + * @see isFrozen + * @stable ICU 3.8 + */ + UnicodeSet *cloneAsThawed() const; + + //---------------------------------------------------------------- + // Public API + //---------------------------------------------------------------- + + /** + * Make this object represent the range `start - end`. + * If `start > end` then this object is set to an empty range. + * A frozen set will not be modified. + * + * @param start first character in the set, inclusive + * @param end last character in the set, inclusive + * @stable ICU 2.4 + */ + UnicodeSet& set(UChar32 start, UChar32 end); + + /** + * Return true if the given position, in the given pattern, appears + * to be the start of a UnicodeSet pattern. + * @stable ICU 2.4 + */ + static UBool resemblesPattern(const UnicodeString& pattern, + int32_t pos); + + /** + * Modifies this set to represent the set specified by the given + * pattern, ignoring Unicode Pattern_White_Space characters. + * See the class description for the syntax of the pattern language. + * A frozen set will not be modified. + * @param pattern a string specifying what characters are in the set + * @param status returns U_ILLEGAL_ARGUMENT_ERROR if the pattern + * contains a syntax error. + * Empties the set passed before applying the pattern. + * @return a reference to this + * @stable ICU 2.0 + */ + UnicodeSet& applyPattern(const UnicodeString& pattern, + UErrorCode& status); + +#ifndef U_HIDE_INTERNAL_API + /** + * Modifies this set to represent the set specified by the given + * pattern, optionally ignoring Unicode Pattern_White_Space characters. + * See the class description for the syntax of the pattern language. + * A frozen set will not be modified. + * @param pattern a string specifying what characters are in the set + * @param options bitmask for options to apply to the pattern. + * Valid options are USET_IGNORE_SPACE and + * at most one of USET_CASE_INSENSITIVE, USET_ADD_CASE_MAPPINGS, USET_SIMPLE_CASE_INSENSITIVE. + * These case options are mutually exclusive. + * @param symbols a symbol table mapping variable names to + * values and stand-ins to UnicodeSets; may be nullptr + * @param status returns U_ILLEGAL_ARGUMENT_ERROR if the pattern + * contains a syntax error. + * Empties the set passed before applying the pattern. + * @return a reference to this + * @internal + */ + UnicodeSet& applyPattern(const UnicodeString& pattern, + uint32_t options, + const SymbolTable* symbols, + UErrorCode& status); +#endif /* U_HIDE_INTERNAL_API */ + + /** + * Parses the given pattern, starting at the given position. The + * character at pattern.charAt(pos.getIndex()) must be '[', or the + * parse fails. Parsing continues until the corresponding closing + * ']'. If a syntax error is encountered between the opening and + * closing brace, the parse fails. Upon return from a successful + * parse, the ParsePosition is updated to point to the character + * following the closing ']', and a StringBuffer containing a + * pairs list for the parsed pattern is returned. This method calls + * itself recursively to parse embedded subpatterns. + * Empties the set passed before applying the pattern. + * A frozen set will not be modified. + * + * @param pattern the string containing the pattern to be parsed. + * The portion of the string from pos.getIndex(), which must be a + * '[', to the corresponding closing ']', is parsed. + * @param pos upon entry, the position at which to being parsing. + * The character at pattern.charAt(pos.getIndex()) must be a '['. + * Upon return from a successful parse, pos.getIndex() is either + * the character after the closing ']' of the parsed pattern, or + * pattern.length() if the closing ']' is the last character of + * the pattern string. + * @param options bitmask for options to apply to the pattern. + * Valid options are USET_IGNORE_SPACE and + * at most one of USET_CASE_INSENSITIVE, USET_ADD_CASE_MAPPINGS, USET_SIMPLE_CASE_INSENSITIVE. + * These case options are mutually exclusive. + * @param symbols a symbol table mapping variable names to + * values and stand-ins to UnicodeSets; may be nullptr + * @param status returns U_ILLEGAL_ARGUMENT_ERROR if the pattern + * contains a syntax error. + * @return a reference to this + * @stable ICU 2.8 + */ + UnicodeSet& applyPattern(const UnicodeString& pattern, + ParsePosition& pos, + uint32_t options, + const SymbolTable* symbols, + UErrorCode& status); + + /** + * Returns a string representation of this set. If the result of + * calling this function is passed to a UnicodeSet constructor, it + * will produce another set that is equal to this one. + * A frozen set will not be modified. + * @param result the string to receive the rules. Previous + * contents will be deleted. + * @param escapeUnprintable if true then convert unprintable + * character to their hex escape representations, \\uxxxx or + * \\Uxxxxxxxx. Unprintable characters are those other than + * U+000A, U+0020..U+007E. + * @stable ICU 2.0 + */ + virtual UnicodeString& toPattern(UnicodeString& result, + UBool escapeUnprintable = false) const override; + + /** + * Modifies this set to contain those code points which have the given value + * for the given binary or enumerated property, as returned by + * u_getIntPropertyValue. Prior contents of this set are lost. + * A frozen set will not be modified. + * + * @param prop a property in the range UCHAR_BIN_START..UCHAR_BIN_LIMIT-1 + * or UCHAR_INT_START..UCHAR_INT_LIMIT-1 + * or UCHAR_MASK_START..UCHAR_MASK_LIMIT-1. + * + * @param value a value in the range u_getIntPropertyMinValue(prop).. + * u_getIntPropertyMaxValue(prop), with one exception. If prop is + * UCHAR_GENERAL_CATEGORY_MASK, then value should not be a UCharCategory, but + * rather a mask value produced by U_GET_GC_MASK(). This allows grouped + * categories such as [:L:] to be represented. + * + * @param ec error code input/output parameter + * + * @return a reference to this set + * + * @stable ICU 2.4 + */ + UnicodeSet& applyIntPropertyValue(UProperty prop, + int32_t value, + UErrorCode& ec); + + /** + * Modifies this set to contain those code points which have the + * given value for the given property. Prior contents of this + * set are lost. + * A frozen set will not be modified. + * + * @param prop a property alias, either short or long. The name is matched + * loosely. See PropertyAliases.txt for names and a description of loose + * matching. If the value string is empty, then this string is interpreted + * as either a General_Category value alias, a Script value alias, a binary + * property alias, or a special ID. Special IDs are matched loosely and + * correspond to the following sets: + * + * "ANY" = [\\u0000-\\U0010FFFF], + * "ASCII" = [\\u0000-\\u007F], + * "Assigned" = [:^Cn:]. + * + * @param value a value alias, either short or long. The name is matched + * loosely. See PropertyValueAliases.txt for names and a description of + * loose matching. In addition to aliases listed, numeric values and + * canonical combining classes may be expressed numerically, e.g., ("nv", + * "0.5") or ("ccc", "220"). The value string may also be empty. + * + * @param ec error code input/output parameter + * + * @return a reference to this set + * + * @stable ICU 2.4 + */ + UnicodeSet& applyPropertyAlias(const UnicodeString& prop, + const UnicodeString& value, + UErrorCode& ec); + + /** + * Returns the number of elements in this set (its cardinality). + * Note than the elements of a set may include both individual + * codepoints and strings. + * + * This is slower than getRangeCount() because + * it counts the code points of all ranges. + * + * @return the number of elements in this set (its cardinality). + * @stable ICU 2.0 + * @see getRangeCount + */ + virtual int32_t size(void) const; + + /** + * Returns true if this set contains no elements. + * + * @return true if this set contains no elements. + * @stable ICU 2.0 + */ + virtual UBool isEmpty(void) const; + + /** + * @return true if this set contains multi-character strings or the empty string. + * @stable ICU 70 + */ + UBool hasStrings() const; + + /** + * Returns true if this set contains the given character. + * This function works faster with a frozen set. + * @param c character to be checked for containment + * @return true if the test condition is met + * @stable ICU 2.0 + */ + virtual UBool contains(UChar32 c) const override; + + /** + * Returns true if this set contains every character + * of the given range. + * @param start first character, inclusive, of the range + * @param end last character, inclusive, of the range + * @return true if the test condition is met + * @stable ICU 2.0 + */ + virtual UBool contains(UChar32 start, UChar32 end) const; + + /** + * Returns true if this set contains the given + * multicharacter string. + * @param s string to be checked for containment + * @return true if this set contains the specified string + * @stable ICU 2.4 + */ + UBool contains(const UnicodeString& s) const; + + /** + * Returns true if this set contains all the characters and strings + * of the given set. + * @param c set to be checked for containment + * @return true if the test condition is met + * @stable ICU 2.4 + */ + virtual UBool containsAll(const UnicodeSet& c) const; + + /** + * Returns true if this set contains all the characters + * of the given string. + * @param s string containing characters to be checked for containment + * @return true if the test condition is met + * @stable ICU 2.4 + */ + UBool containsAll(const UnicodeString& s) const; + + /** + * Returns true if this set contains none of the characters + * of the given range. + * @param start first character, inclusive, of the range + * @param end last character, inclusive, of the range + * @return true if the test condition is met + * @stable ICU 2.4 + */ + UBool containsNone(UChar32 start, UChar32 end) const; + + /** + * Returns true if this set contains none of the characters and strings + * of the given set. + * @param c set to be checked for containment + * @return true if the test condition is met + * @stable ICU 2.4 + */ + UBool containsNone(const UnicodeSet& c) const; + + /** + * Returns true if this set contains none of the characters + * of the given string. + * @param s string containing characters to be checked for containment + * @return true if the test condition is met + * @stable ICU 2.4 + */ + UBool containsNone(const UnicodeString& s) const; + + /** + * Returns true if this set contains one or more of the characters + * in the given range. + * @param start first character, inclusive, of the range + * @param end last character, inclusive, of the range + * @return true if the condition is met + * @stable ICU 2.4 + */ + inline UBool containsSome(UChar32 start, UChar32 end) const; + + /** + * Returns true if this set contains one or more of the characters + * and strings of the given set. + * @param s The set to be checked for containment + * @return true if the condition is met + * @stable ICU 2.4 + */ + inline UBool containsSome(const UnicodeSet& s) const; + + /** + * Returns true if this set contains one or more of the characters + * of the given string. + * @param s string containing characters to be checked for containment + * @return true if the condition is met + * @stable ICU 2.4 + */ + inline UBool containsSome(const UnicodeString& s) const; + + /** + * Returns the length of the initial substring of the input string which + * consists only of characters and strings that are contained in this set + * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), + * or only of characters and strings that are not contained + * in this set (USET_SPAN_NOT_CONTAINED). + * See USetSpanCondition for details. + * Similar to the strspn() C library function. + * Unpaired surrogates are treated according to contains() of their surrogate code points. + * This function works faster with a frozen set and with a non-negative string length argument. + * @param s start of the string + * @param length of the string; can be -1 for NUL-terminated + * @param spanCondition specifies the containment condition + * @return the length of the initial substring according to the spanCondition; + * 0 if the start of the string does not fit the spanCondition + * @stable ICU 3.8 + * @see USetSpanCondition + */ + int32_t span(const char16_t *s, int32_t length, USetSpanCondition spanCondition) const; + + /** + * Returns the end of the substring of the input string according to the USetSpanCondition. + * Same as start+span(s.getBuffer()+start, s.length()-start, spanCondition) + * after pinning start to 0<=start<=s.length(). + * @param s the string + * @param start the start index in the string for the span operation + * @param spanCondition specifies the containment condition + * @return the exclusive end of the substring according to the spanCondition; + * the substring s.tempSubStringBetween(start, end) fulfills the spanCondition + * @stable ICU 4.4 + * @see USetSpanCondition + */ + inline int32_t span(const UnicodeString &s, int32_t start, USetSpanCondition spanCondition) const; + + /** + * Returns the start of the trailing substring of the input string which + * consists only of characters and strings that are contained in this set + * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), + * or only of characters and strings that are not contained + * in this set (USET_SPAN_NOT_CONTAINED). + * See USetSpanCondition for details. + * Unpaired surrogates are treated according to contains() of their surrogate code points. + * This function works faster with a frozen set and with a non-negative string length argument. + * @param s start of the string + * @param length of the string; can be -1 for NUL-terminated + * @param spanCondition specifies the containment condition + * @return the start of the trailing substring according to the spanCondition; + * the string length if the end of the string does not fit the spanCondition + * @stable ICU 3.8 + * @see USetSpanCondition + */ + int32_t spanBack(const char16_t *s, int32_t length, USetSpanCondition spanCondition) const; + + /** + * Returns the start of the substring of the input string according to the USetSpanCondition. + * Same as spanBack(s.getBuffer(), limit, spanCondition) + * after pinning limit to 0<=end<=s.length(). + * @param s the string + * @param limit the exclusive-end index in the string for the span operation + * (use s.length() or INT32_MAX for spanning back from the end of the string) + * @param spanCondition specifies the containment condition + * @return the start of the substring according to the spanCondition; + * the substring s.tempSubStringBetween(start, limit) fulfills the spanCondition + * @stable ICU 4.4 + * @see USetSpanCondition + */ + inline int32_t spanBack(const UnicodeString &s, int32_t limit, USetSpanCondition spanCondition) const; + + /** + * Returns the length of the initial substring of the input string which + * consists only of characters and strings that are contained in this set + * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), + * or only of characters and strings that are not contained + * in this set (USET_SPAN_NOT_CONTAINED). + * See USetSpanCondition for details. + * Similar to the strspn() C library function. + * Malformed byte sequences are treated according to contains(0xfffd). + * This function works faster with a frozen set and with a non-negative string length argument. + * @param s start of the string (UTF-8) + * @param length of the string; can be -1 for NUL-terminated + * @param spanCondition specifies the containment condition + * @return the length of the initial substring according to the spanCondition; + * 0 if the start of the string does not fit the spanCondition + * @stable ICU 3.8 + * @see USetSpanCondition + */ + int32_t spanUTF8(const char *s, int32_t length, USetSpanCondition spanCondition) const; + + /** + * Returns the start of the trailing substring of the input string which + * consists only of characters and strings that are contained in this set + * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), + * or only of characters and strings that are not contained + * in this set (USET_SPAN_NOT_CONTAINED). + * See USetSpanCondition for details. + * Malformed byte sequences are treated according to contains(0xfffd). + * This function works faster with a frozen set and with a non-negative string length argument. + * @param s start of the string (UTF-8) + * @param length of the string; can be -1 for NUL-terminated + * @param spanCondition specifies the containment condition + * @return the start of the trailing substring according to the spanCondition; + * the string length if the end of the string does not fit the spanCondition + * @stable ICU 3.8 + * @see USetSpanCondition + */ + int32_t spanBackUTF8(const char *s, int32_t length, USetSpanCondition spanCondition) const; + + /** + * Implement UnicodeMatcher::matches() + * @stable ICU 2.4 + */ + virtual UMatchDegree matches(const Replaceable& text, + int32_t& offset, + int32_t limit, + UBool incremental) override; + +private: + /** + * Returns the longest match for s in text at the given position. + * If limit > start then match forward from start+1 to limit + * matching all characters except s.charAt(0). If limit < start, + * go backward starting from start-1 matching all characters + * except s.charAt(s.length()-1). This method assumes that the + * first character, text.charAt(start), matches s, so it does not + * check it. + * @param text the text to match + * @param start the first character to match. In the forward + * direction, text.charAt(start) is matched against s.charAt(0). + * In the reverse direction, it is matched against + * s.charAt(s.length()-1). + * @param limit the limit offset for matching, either last+1 in + * the forward direction, or last-1 in the reverse direction, + * where last is the index of the last character to match. + * @param s + * @return If part of s matches up to the limit, return |limit - + * start|. If all of s matches before reaching the limit, return + * s.length(). If there is a mismatch between s and text, return + * 0 + */ + static int32_t matchRest(const Replaceable& text, + int32_t start, int32_t limit, + const UnicodeString& s); + + /** + * Returns the smallest value i such that c < list[i]. Caller + * must ensure that c is a legal value or this method will enter + * an infinite loop. This method performs a binary search. + * @param c a character in the range MIN_VALUE..MAX_VALUE + * inclusive + * @return the smallest integer i in the range 0..len-1, + * inclusive, such that c < list[i] + */ + int32_t findCodePoint(UChar32 c) const; + +public: + + /** + * Implementation of UnicodeMatcher API. Union the set of all + * characters that may be matched by this object into the given + * set. + * @param toUnionTo the set into which to union the source characters + * @stable ICU 2.4 + */ + virtual void addMatchSetTo(UnicodeSet& toUnionTo) const override; + + /** + * Returns the index of the given character within this set, where + * the set is ordered by ascending code point. If the character + * is not in this set, return -1. The inverse of this method is + * charAt(). + * @return an index from 0..size()-1, or -1 + * @stable ICU 2.4 + */ + int32_t indexOf(UChar32 c) const; + + /** + * Returns the character at the given index within this set, where + * the set is ordered by ascending code point. If the index is + * out of range for characters, returns (UChar32)-1. + * The inverse of this method is indexOf(). + * + * For iteration, this is slower than UnicodeSetIterator or + * getRangeCount()/getRangeStart()/getRangeEnd(), + * because for each call it skips linearly over index + * characters in the ranges. + * + * @param index an index from 0..size()-1 + * @return the character at the given index, or (UChar32)-1. + * @stable ICU 2.4 + */ + UChar32 charAt(int32_t index) const; + + /** + * Adds the specified range to this set if it is not already + * present. If this set already contains the specified range, + * the call leaves this set unchanged. If start > end + * then an empty range is added, leaving the set unchanged. + * This is equivalent to a boolean logic OR, or a set UNION. + * A frozen set will not be modified. + * + * @param start first character, inclusive, of range to be added + * to this set. + * @param end last character, inclusive, of range to be added + * to this set. + * @stable ICU 2.0 + */ + virtual UnicodeSet& add(UChar32 start, UChar32 end); + + /** + * Adds the specified character to this set if it is not already + * present. If this set already contains the specified character, + * the call leaves this set unchanged. + * A frozen set will not be modified. + * + * @param c the character (code point) + * @return this object, for chaining + * @stable ICU 2.0 + */ + UnicodeSet& add(UChar32 c); + + /** + * Adds the specified multicharacter to this set if it is not already + * present. If this set already contains the multicharacter, + * the call leaves this set unchanged. + * Thus "ch" => {"ch"} + * A frozen set will not be modified. + * + * @param s the source string + * @return this object, for chaining + * @stable ICU 2.4 + */ + UnicodeSet& add(const UnicodeString& s); + + private: + /** + * @return a code point IF the string consists of a single one. + * otherwise returns -1. + * @param s string to test + */ + static int32_t getSingleCP(const UnicodeString& s); + + void _add(const UnicodeString& s); + + public: + /** + * Adds each of the characters in this string to the set. Note: "ch" => {"c", "h"} + * If this set already contains any particular character, it has no effect on that character. + * A frozen set will not be modified. + * @param s the source string + * @return this object, for chaining + * @stable ICU 2.4 + */ + UnicodeSet& addAll(const UnicodeString& s); + + /** + * Retains EACH of the characters in this string. Note: "ch" == {"c", "h"} + * A frozen set will not be modified. + * @param s the source string + * @return this object, for chaining + * @stable ICU 2.4 + */ + UnicodeSet& retainAll(const UnicodeString& s); + + /** + * Complement EACH of the characters in this string. Note: "ch" == {"c", "h"} + * A frozen set will not be modified. + * @param s the source string + * @return this object, for chaining + * @stable ICU 2.4 + */ + UnicodeSet& complementAll(const UnicodeString& s); + + /** + * Remove EACH of the characters in this string. Note: "ch" == {"c", "h"} + * A frozen set will not be modified. + * @param s the source string + * @return this object, for chaining + * @stable ICU 2.4 + */ + UnicodeSet& removeAll(const UnicodeString& s); + + /** + * Makes a set from a multicharacter string. Thus "ch" => {"ch"} + * + * @param s the source string + * @return a newly created set containing the given string. + * The caller owns the return object and is responsible for deleting it. + * @stable ICU 2.4 + */ + static UnicodeSet* U_EXPORT2 createFrom(const UnicodeString& s); + + + /** + * Makes a set from each of the characters in the string. Thus "ch" => {"c", "h"} + * @param s the source string + * @return a newly created set containing the given characters + * The caller owns the return object and is responsible for deleting it. + * @stable ICU 2.4 + */ + static UnicodeSet* U_EXPORT2 createFromAll(const UnicodeString& s); + + /** + * Retain only the elements in this set that are contained in the + * specified range. If start > end then an empty range is + * retained, leaving the set empty. This is equivalent to + * a boolean logic AND, or a set INTERSECTION. + * A frozen set will not be modified. + * + * @param start first character, inclusive, of range + * @param end last character, inclusive, of range + * @stable ICU 2.0 + */ + virtual UnicodeSet& retain(UChar32 start, UChar32 end); + + + /** + * Retain the specified character from this set if it is present. + * A frozen set will not be modified. + * + * @param c the character (code point) + * @return this object, for chaining + * @stable ICU 2.0 + */ + UnicodeSet& retain(UChar32 c); + + /** + * Retains only the specified string from this set if it is present. + * Upon return this set will be empty if it did not contain s, or + * will only contain s if it did contain s. + * A frozen set will not be modified. + * + * @param s the source string + * @return this object, for chaining + * @stable ICU 69 + */ + UnicodeSet& retain(const UnicodeString &s); + + /** + * Removes the specified range from this set if it is present. + * The set will not contain the specified range once the call + * returns. If start > end then an empty range is + * removed, leaving the set unchanged. + * A frozen set will not be modified. + * + * @param start first character, inclusive, of range to be removed + * from this set. + * @param end last character, inclusive, of range to be removed + * from this set. + * @stable ICU 2.0 + */ + virtual UnicodeSet& remove(UChar32 start, UChar32 end); + + /** + * Removes the specified character from this set if it is present. + * The set will not contain the specified range once the call + * returns. + * A frozen set will not be modified. + * + * @param c the character (code point) + * @return this object, for chaining + * @stable ICU 2.0 + */ + UnicodeSet& remove(UChar32 c); + + /** + * Removes the specified string from this set if it is present. + * The set will not contain the specified character once the call + * returns. + * A frozen set will not be modified. + * @param s the source string + * @return this object, for chaining + * @stable ICU 2.4 + */ + UnicodeSet& remove(const UnicodeString& s); + + /** + * This is equivalent to + * complement(MIN_VALUE, MAX_VALUE). + * + * Note: This performs a symmetric difference with all code points + * and thus retains all multicharacter strings. + * In order to achieve a “code point complement” (all code points minus this set), + * the easiest is to .complement().removeAllStrings(). + * + * A frozen set will not be modified. + * @stable ICU 2.0 + */ + virtual UnicodeSet& complement(); + + /** + * Complements the specified range in this set. Any character in + * the range will be removed if it is in this set, or will be + * added if it is not in this set. If start > end + * then an empty range is complemented, leaving the set unchanged. + * This is equivalent to a boolean logic XOR. + * A frozen set will not be modified. + * + * @param start first character, inclusive, of range + * @param end last character, inclusive, of range + * @stable ICU 2.0 + */ + virtual UnicodeSet& complement(UChar32 start, UChar32 end); + + /** + * Complements the specified character in this set. The character + * will be removed if it is in this set, or will be added if it is + * not in this set. + * A frozen set will not be modified. + * + * @param c the character (code point) + * @return this object, for chaining + * @stable ICU 2.0 + */ + UnicodeSet& complement(UChar32 c); + + /** + * Complement the specified string in this set. + * The string will be removed if it is in this set, or will be added if it is not in this set. + * A frozen set will not be modified. + * + * @param s the string to complement + * @return this object, for chaining + * @stable ICU 2.4 + */ + UnicodeSet& complement(const UnicodeString& s); + + /** + * Adds all of the elements in the specified set to this set if + * they're not already present. This operation effectively + * modifies this set so that its value is the union of the two + * sets. The behavior of this operation is unspecified if the specified + * collection is modified while the operation is in progress. + * A frozen set will not be modified. + * + * @param c set whose elements are to be added to this set. + * @see #add(UChar32, UChar32) + * @stable ICU 2.0 + */ + virtual UnicodeSet& addAll(const UnicodeSet& c); + + /** + * Retains only the elements in this set that are contained in the + * specified set. In other words, removes from this set all of + * its elements that are not contained in the specified set. This + * operation effectively modifies this set so that its value is + * the intersection of the two sets. + * A frozen set will not be modified. + * + * @param c set that defines which elements this set will retain. + * @stable ICU 2.0 + */ + virtual UnicodeSet& retainAll(const UnicodeSet& c); + + /** + * Removes from this set all of its elements that are contained in the + * specified set. This operation effectively modifies this + * set so that its value is the asymmetric set difference of + * the two sets. + * A frozen set will not be modified. + * + * @param c set that defines which elements will be removed from + * this set. + * @stable ICU 2.0 + */ + virtual UnicodeSet& removeAll(const UnicodeSet& c); + + /** + * Complements in this set all elements contained in the specified + * set. Any character in the other set will be removed if it is + * in this set, or will be added if it is not in this set. + * A frozen set will not be modified. + * + * @param c set that defines which elements will be xor'ed from + * this set. + * @stable ICU 2.4 + */ + virtual UnicodeSet& complementAll(const UnicodeSet& c); + + /** + * Removes all of the elements from this set. This set will be + * empty after this call returns. + * A frozen set will not be modified. + * @stable ICU 2.0 + */ + virtual UnicodeSet& clear(void); + + /** + * Close this set over the given attribute. For the attribute + * USET_CASE_INSENSITIVE, the result is to modify this set so that: + * + * 1. For each character or string 'a' in this set, all strings or + * characters 'b' such that foldCase(a) == foldCase(b) are added + * to this set. + * + * 2. For each string 'e' in the resulting set, if e != + * foldCase(e), 'e' will be removed. + * + * Example: [aq\\u00DF{Bc}{bC}{Fi}] => [aAqQ\\u00DF\\uFB01{ss}{bc}{fi}] + * + * (Here foldCase(x) refers to the operation u_strFoldCase, and a + * == b denotes that the contents are the same, not pointer + * comparison.) + * + * A frozen set will not be modified. + * + * @param attribute bitmask for attributes to close over. + * Valid options: + * At most one of USET_CASE_INSENSITIVE, USET_ADD_CASE_MAPPINGS, USET_SIMPLE_CASE_INSENSITIVE. + * These case options are mutually exclusive. + * Unrelated options bits are ignored. + * @return a reference to this set. + * @stable ICU 4.2 + */ + UnicodeSet& closeOver(int32_t attribute); + + /** + * Remove all strings from this set. + * + * @return a reference to this set. + * @stable ICU 4.2 + */ + virtual UnicodeSet &removeAllStrings(); + + /** + * Iteration method that returns the number of ranges contained in + * this set. + * @see #getRangeStart + * @see #getRangeEnd + * @stable ICU 2.4 + */ + virtual int32_t getRangeCount(void) const; + + /** + * Iteration method that returns the first character in the + * specified range of this set. + * @see #getRangeCount + * @see #getRangeEnd + * @stable ICU 2.4 + */ + virtual UChar32 getRangeStart(int32_t index) const; + + /** + * Iteration method that returns the last character in the + * specified range of this set. + * @see #getRangeStart + * @see #getRangeEnd + * @stable ICU 2.4 + */ + virtual UChar32 getRangeEnd(int32_t index) const; + + /** + * Serializes this set into an array of 16-bit integers. Serialization + * (currently) only records the characters in the set; multicharacter + * strings are ignored. + * + * The array has following format (each line is one 16-bit + * integer): + * + * length = (n+2*m) | (m!=0?0x8000:0) + * bmpLength = n; present if m!=0 + * bmp[0] + * bmp[1] + * ... + * bmp[n-1] + * supp-high[0] + * supp-low[0] + * supp-high[1] + * supp-low[1] + * ... + * supp-high[m-1] + * supp-low[m-1] + * + * The array starts with a header. After the header are n bmp + * code points, then m supplementary code points. Either n or m + * or both may be zero. n+2*m is always <= 0x7FFF. + * + * If there are no supplementary characters (if m==0) then the + * header is one 16-bit integer, 'length', with value n. + * + * If there are supplementary characters (if m!=0) then the header + * is two 16-bit integers. The first, 'length', has value + * (n+2*m)|0x8000. The second, 'bmpLength', has value n. + * + * After the header the code points are stored in ascending order. + * Supplementary code points are stored as most significant 16 + * bits followed by least significant 16 bits. + * + * @param dest pointer to buffer of destCapacity 16-bit integers. + * May be nullptr only if destCapacity is zero. + * @param destCapacity size of dest, or zero. Must not be negative. + * @param ec error code. Will be set to U_INDEX_OUTOFBOUNDS_ERROR + * if n+2*m > 0x7FFF. Will be set to U_BUFFER_OVERFLOW_ERROR if + * n+2*m+(m!=0?2:1) > destCapacity. + * @return the total length of the serialized format, including + * the header, that is, n+2*m+(m!=0?2:1), or 0 on error other + * than U_BUFFER_OVERFLOW_ERROR. + * @stable ICU 2.4 + */ + int32_t serialize(uint16_t *dest, int32_t destCapacity, UErrorCode& ec) const; + + /** + * Reallocate this objects internal structures to take up the least + * possible space, without changing this object's value. + * A frozen set will not be modified. + * @stable ICU 2.4 + */ + virtual UnicodeSet& compact(); + + /** + * Return the class ID for this class. This is useful only for + * comparing to a return value from getDynamicClassID(). For example: + *

+     * .      Base* polymorphic_pointer = createPolymorphicObject();
+     * .      if (polymorphic_pointer->getDynamicClassID() ==
+     * .          Derived::getStaticClassID()) ...
+     * 
+ * @return The class ID for all objects of this class. + * @stable ICU 2.0 + */ + static UClassID U_EXPORT2 getStaticClassID(void); + + /** + * Implement UnicodeFunctor API. + * + * @return The class ID for this object. All objects of a given + * class have the same class ID. Objects of other classes have + * different class IDs. + * @stable ICU 2.4 + */ + virtual UClassID getDynamicClassID(void) const override; + +private: + + // Private API for the USet API + + friend class USetAccess; + + const UnicodeString* getString(int32_t index) const; + + //---------------------------------------------------------------- + // RuleBasedTransliterator support + //---------------------------------------------------------------- + +private: + + /** + * Returns true if this set contains any character whose low byte + * is the given value. This is used by RuleBasedTransliterator for + * indexing. + */ + virtual UBool matchesIndexValue(uint8_t v) const override; + +private: + friend class RBBIRuleScanner; + + //---------------------------------------------------------------- + // Implementation: Clone as thawed (see ICU4J Freezable) + //---------------------------------------------------------------- + + UnicodeSet(const UnicodeSet& o, UBool /* asThawed */); + UnicodeSet& copyFrom(const UnicodeSet& o, UBool asThawed); + + //---------------------------------------------------------------- + // Implementation: Pattern parsing + //---------------------------------------------------------------- + + void applyPatternIgnoreSpace(const UnicodeString& pattern, + ParsePosition& pos, + const SymbolTable* symbols, + UErrorCode& status); + + void applyPattern(RuleCharacterIterator& chars, + const SymbolTable* symbols, + UnicodeString& rebuiltPat, + uint32_t options, + UnicodeSet& (UnicodeSet::*caseClosure)(int32_t attribute), + int32_t depth, + UErrorCode& ec); + + void closeOverCaseInsensitive(bool simple); + void closeOverAddCaseMappings(); + + //---------------------------------------------------------------- + // Implementation: Utility methods + //---------------------------------------------------------------- + + static int32_t nextCapacity(int32_t minCapacity); + + bool ensureCapacity(int32_t newLen); + + bool ensureBufferCapacity(int32_t newLen); + + void swapBuffers(void); + + UBool allocateStrings(UErrorCode &status); + int32_t stringsSize() const; + UBool stringsContains(const UnicodeString &s) const; + + UnicodeString& _toPattern(UnicodeString& result, + UBool escapeUnprintable) const; + + UnicodeString& _generatePattern(UnicodeString& result, + UBool escapeUnprintable) const; + + static void _appendToPat(UnicodeString& buf, const UnicodeString& s, UBool escapeUnprintable); + + static void _appendToPat(UnicodeString& buf, UChar32 c, UBool escapeUnprintable); + + static void _appendToPat(UnicodeString &result, UChar32 start, UChar32 end, + UBool escapeUnprintable); + + //---------------------------------------------------------------- + // Implementation: Fundamental operators + //---------------------------------------------------------------- + + void exclusiveOr(const UChar32* other, int32_t otherLen, int8_t polarity); + + void add(const UChar32* other, int32_t otherLen, int8_t polarity); + + void retain(const UChar32* other, int32_t otherLen, int8_t polarity); + + /** + * Return true if the given position, in the given pattern, appears + * to be the start of a property set pattern [:foo:], \\p{foo}, or + * \\P{foo}, or \\N{name}. + */ + static UBool resemblesPropertyPattern(const UnicodeString& pattern, + int32_t pos); + + static UBool resemblesPropertyPattern(RuleCharacterIterator& chars, + int32_t iterOpts); + + /** + * Parse the given property pattern at the given parse position + * and set this UnicodeSet to the result. + * + * The original design document is out of date, but still useful. + * Ignore the property and value names: + * https://htmlpreview.github.io/?https://github.com/unicode-org/icu-docs/blob/main/design/unicodeset_properties.html + * + * Recognized syntax: + * + * [:foo:] [:^foo:] - white space not allowed within "[:" or ":]" + * \\p{foo} \\P{foo} - white space not allowed within "\\p" or "\\P" + * \\N{name} - white space not allowed within "\\N" + * + * Other than the above restrictions, Unicode Pattern_White_Space characters are ignored. + * Case is ignored except in "\\p" and "\\P" and "\\N". In 'name' leading + * and trailing space is deleted, and internal runs of whitespace + * are collapsed to a single space. + * + * We support binary properties, enumerated properties, and the + * following non-enumerated properties: + * + * Numeric_Value + * Name + * Unicode_1_Name + * + * @param pattern the pattern string + * @param ppos on entry, the position at which to begin parsing. + * This should be one of the locations marked '^': + * + * [:blah:] \\p{blah} \\P{blah} \\N{name} + * ^ % ^ % ^ % ^ % + * + * On return, the position after the last character parsed, that is, + * the locations marked '%'. If the parse fails, ppos is returned + * unchanged. + * @param ec status + * @return a reference to this. + */ + UnicodeSet& applyPropertyPattern(const UnicodeString& pattern, + ParsePosition& ppos, + UErrorCode &ec); + + void applyPropertyPattern(RuleCharacterIterator& chars, + UnicodeString& rebuiltPat, + UErrorCode& ec); + + /** + * A filter that returns true if the given code point should be + * included in the UnicodeSet being constructed. + */ + typedef UBool (*Filter)(UChar32 codePoint, void* context); + + /** + * Given a filter, set this UnicodeSet to the code points + * contained by that filter. The filter MUST be + * property-conformant. That is, if it returns value v for one + * code point, then it must return v for all affiliated code + * points, as defined by the inclusions list. See + * getInclusions(). + * src is a UPropertySource value. + */ + void applyFilter(Filter filter, + void* context, + const UnicodeSet* inclusions, + UErrorCode &status); + + /** + * Set the new pattern to cache. + */ + void setPattern(const UnicodeString& newPat) { + setPattern(newPat.getBuffer(), newPat.length()); + } + void setPattern(const char16_t *newPat, int32_t newPatLen); + /** + * Release existing cached pattern. + */ + void releasePattern(); + + friend class UnicodeSetIterator; +}; + + + +inline bool UnicodeSet::operator!=(const UnicodeSet& o) const { + return !operator==(o); +} + +inline UBool UnicodeSet::isFrozen() const { + return (UBool)(bmpSet!=nullptr || stringSpan!=nullptr); +} + +inline UBool UnicodeSet::containsSome(UChar32 start, UChar32 end) const { + return !containsNone(start, end); +} + +inline UBool UnicodeSet::containsSome(const UnicodeSet& s) const { + return !containsNone(s); +} + +inline UBool UnicodeSet::containsSome(const UnicodeString& s) const { + return !containsNone(s); +} + +inline UBool UnicodeSet::isBogus() const { + return (UBool)(fFlags & kIsBogus); +} + +inline UnicodeSet *UnicodeSet::fromUSet(USet *uset) { + return reinterpret_cast(uset); +} + +inline const UnicodeSet *UnicodeSet::fromUSet(const USet *uset) { + return reinterpret_cast(uset); +} + +inline USet *UnicodeSet::toUSet() { + return reinterpret_cast(this); +} + +inline const USet *UnicodeSet::toUSet() const { + return reinterpret_cast(this); +} + +inline int32_t UnicodeSet::span(const UnicodeString &s, int32_t start, USetSpanCondition spanCondition) const { + int32_t sLength=s.length(); + if(start<0) { + start=0; + } else if(start>sLength) { + start=sLength; + } + return start+span(s.getBuffer()+start, sLength-start, spanCondition); +} + +inline int32_t UnicodeSet::spanBack(const UnicodeString &s, int32_t limit, USetSpanCondition spanCondition) const { + int32_t sLength=s.length(); + if(limit<0) { + limit=0; + } else if(limit>sLength) { + limit=sLength; + } + return spanBack(s.getBuffer(), limit, spanCondition); +} + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/unistr.h b/deps/icu-small/source/common/unicode/unistr.h index b3c99481079388..db242d5d49044c 100644 --- a/deps/icu-small/source/common/unicode/unistr.h +++ b/deps/icu-small/source/common/unicode/unistr.h @@ -1,4757 +1,4784 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1998-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* File unistr.h -* -* Modification History: -* -* Date Name Description -* 09/25/98 stephen Creation. -* 11/11/98 stephen Changed per 11/9 code review. -* 04/20/99 stephen Overhauled per 4/16 code review. -* 11/18/99 aliu Made to inherit from Replaceable. Added method -* handleReplaceBetween(); other methods unchanged. -* 06/25/01 grhoten Remove dependency on iostream. -****************************************************************************** -*/ - -#ifndef UNISTR_H -#define UNISTR_H - -/** - * \file - * \brief C++ API: Unicode String - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include -#include "unicode/char16ptr.h" -#include "unicode/rep.h" -#include "unicode/std_string.h" -#include "unicode/stringpiece.h" -#include "unicode/bytestream.h" - -struct UConverter; // unicode/ucnv.h - -#ifndef USTRING_H -/** - * \ingroup ustring_ustrlen - * @param s Pointer to sequence of UChars. - * @return Length of sequence. - */ -U_CAPI int32_t U_EXPORT2 u_strlen(const UChar *s); -#endif - -U_NAMESPACE_BEGIN - -#if !UCONFIG_NO_BREAK_ITERATION -class BreakIterator; // unicode/brkiter.h -#endif -class Edits; - -U_NAMESPACE_END - -// Not #ifndef U_HIDE_INTERNAL_API because UnicodeString needs the UStringCaseMapper. -/** - * Internal string case mapping function type. - * All error checking must be done. - * src and dest must not overlap. - * @internal - */ -typedef int32_t U_CALLCONV -UStringCaseMapper(int32_t caseLocale, uint32_t options, -#if !UCONFIG_NO_BREAK_ITERATION - icu::BreakIterator *iter, -#endif - char16_t *dest, int32_t destCapacity, - const char16_t *src, int32_t srcLength, - icu::Edits *edits, - UErrorCode &errorCode); - -U_NAMESPACE_BEGIN - -class Locale; // unicode/locid.h -class StringCharacterIterator; -class UnicodeStringAppendable; // unicode/appendable.h - -/* The include has been moved to unicode/ustream.h */ - -/** - * Constant to be used in the UnicodeString(char *, int32_t, EInvariant) constructor - * which constructs a Unicode string from an invariant-character char * string. - * About invariant characters see utypes.h. - * This constructor has no runtime dependency on conversion code and is - * therefore recommended over ones taking a charset name string - * (where the empty string "" indicates invariant-character conversion). - * - * @stable ICU 3.2 - */ -#define US_INV icu::UnicodeString::kInvariant - -/** - * Unicode String literals in C++. - * - * Note: these macros are not recommended for new code. - * Prior to the availability of C++11 and u"unicode string literals", - * these macros were provided for portability and efficiency when - * initializing UnicodeStrings from literals. - * - * They work only for strings that contain "invariant characters", i.e., - * only latin letters, digits, and some punctuation. - * See utypes.h for details. - * - * The string parameter must be a C string literal. - * The length of the string, not including the terminating - * `NUL`, must be specified as a constant. - * @stable ICU 2.0 - */ -#if !U_CHAR16_IS_TYPEDEF -# define UNICODE_STRING(cs, _length) icu::UnicodeString(true, u ## cs, _length) -#else -# define UNICODE_STRING(cs, _length) icu::UnicodeString(true, (const char16_t*)u ## cs, _length) -#endif - -/** - * Unicode String literals in C++. - * Dependent on the platform properties, different UnicodeString - * constructors should be used to create a UnicodeString object from - * a string literal. - * The macros are defined for improved performance. - * They work only for strings that contain "invariant characters", i.e., - * only latin letters, digits, and some punctuation. - * See utypes.h for details. - * - * The string parameter must be a C string literal. - * @stable ICU 2.0 - */ -#define UNICODE_STRING_SIMPLE(cs) UNICODE_STRING(cs, -1) - -/** - * \def UNISTR_FROM_CHAR_EXPLICIT - * This can be defined to be empty or "explicit". - * If explicit, then the UnicodeString(char16_t) and UnicodeString(UChar32) - * constructors are marked as explicit, preventing their inadvertent use. - * @stable ICU 49 - */ -#ifndef UNISTR_FROM_CHAR_EXPLICIT -# if defined(U_COMBINED_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) || defined(U_I18N_IMPLEMENTATION) || defined(U_IO_IMPLEMENTATION) - // Auto-"explicit" in ICU library code. -# define UNISTR_FROM_CHAR_EXPLICIT explicit -# else - // Empty by default for source code compatibility. -# define UNISTR_FROM_CHAR_EXPLICIT -# endif -#endif - -/** - * \def UNISTR_FROM_STRING_EXPLICIT - * This can be defined to be empty or "explicit". - * If explicit, then the UnicodeString(const char *) and UnicodeString(const char16_t *) - * constructors are marked as explicit, preventing their inadvertent use. - * - * In particular, this helps prevent accidentally depending on ICU conversion code - * by passing a string literal into an API with a const UnicodeString & parameter. - * @stable ICU 49 - */ -#ifndef UNISTR_FROM_STRING_EXPLICIT -# if defined(U_COMBINED_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) || defined(U_I18N_IMPLEMENTATION) || defined(U_IO_IMPLEMENTATION) - // Auto-"explicit" in ICU library code. -# define UNISTR_FROM_STRING_EXPLICIT explicit -# else - // Empty by default for source code compatibility. -# define UNISTR_FROM_STRING_EXPLICIT -# endif -#endif - -/** - * \def UNISTR_OBJECT_SIZE - * Desired sizeof(UnicodeString) in bytes. - * It should be a multiple of sizeof(pointer) to avoid unusable space for padding. - * The object size may want to be a multiple of 16 bytes, - * which is a common granularity for heap allocation. - * - * Any space inside the object beyond sizeof(vtable pointer) + 2 - * is available for storing short strings inside the object. - * The bigger the object, the longer a string that can be stored inside the object, - * without additional heap allocation. - * - * Depending on a platform's pointer size, pointer alignment requirements, - * and struct padding, the compiler will usually round up sizeof(UnicodeString) - * to 4 * sizeof(pointer) (or 3 * sizeof(pointer) for P128 data models), - * to hold the fields for heap-allocated strings. - * Such a minimum size also ensures that the object is easily large enough - * to hold at least 2 char16_ts, for one supplementary code point (U16_MAX_LENGTH). - * - * sizeof(UnicodeString) >= 48 should work for all known platforms. - * - * For example, on a 64-bit machine where sizeof(vtable pointer) is 8, - * sizeof(UnicodeString) = 64 would leave space for - * (64 - sizeof(vtable pointer) - 2) / U_SIZEOF_UCHAR = (64 - 8 - 2) / 2 = 27 - * char16_ts stored inside the object. - * - * The minimum object size on a 64-bit machine would be - * 4 * sizeof(pointer) = 4 * 8 = 32 bytes, - * and the internal buffer would hold up to 11 char16_ts in that case. - * - * @see U16_MAX_LENGTH - * @stable ICU 56 - */ -#ifndef UNISTR_OBJECT_SIZE -# define UNISTR_OBJECT_SIZE 64 -#endif - -/** - * UnicodeString is a string class that stores Unicode characters directly and provides - * similar functionality as the Java String and StringBuffer/StringBuilder classes. - * It is a concrete implementation of the abstract class Replaceable (for transliteration). - * - * The UnicodeString equivalent of std::string’s clear() is remove(). - * - * A UnicodeString may "alias" an external array of characters - * (that is, point to it, rather than own the array) - * whose lifetime must then at least match the lifetime of the aliasing object. - * This aliasing may be preserved when returning a UnicodeString by value, - * depending on the compiler and the function implementation, - * via Return Value Optimization (RVO) or the move assignment operator. - * (However, the copy assignment operator does not preserve aliasing.) - * For details see the description of storage models at the end of the class API docs - * and in the User Guide chapter linked from there. - * - * The UnicodeString class is not suitable for subclassing. - * - * For an overview of Unicode strings in C and C++ see the - * [User Guide Strings chapter](https://unicode-org.github.io/icu/userguide/strings#strings-in-cc). - * - * In ICU, a Unicode string consists of 16-bit Unicode *code units*. - * A Unicode character may be stored with either one code unit - * (the most common case) or with a matched pair of special code units - * ("surrogates"). The data type for code units is char16_t. - * For single-character handling, a Unicode character code *point* is a value - * in the range 0..0x10ffff. ICU uses the UChar32 type for code points. - * - * Indexes and offsets into and lengths of strings always count code units, not code points. - * This is the same as with multi-byte char* strings in traditional string handling. - * Operations on partial strings typically do not test for code point boundaries. - * If necessary, the user needs to take care of such boundaries by testing for the code unit - * values or by using functions like - * UnicodeString::getChar32Start() and UnicodeString::getChar32Limit() - * (or, in C, the equivalent macros U16_SET_CP_START() and U16_SET_CP_LIMIT(), see utf.h). - * - * UnicodeString methods are more lenient with regard to input parameter values - * than other ICU APIs. In particular: - * - If indexes are out of bounds for a UnicodeString object - * (< 0 or > length()) then they are "pinned" to the nearest boundary. - * - If the buffer passed to an insert/append/replace operation is owned by the - * target object, e.g., calling str.append(str), an extra copy may take place - * to ensure safety. - * - If primitive string pointer values (e.g., const char16_t * or char *) - * for input strings are NULL, then those input string parameters are treated - * as if they pointed to an empty string. - * However, this is *not* the case for char * parameters for charset names - * or other IDs. - * - Most UnicodeString methods do not take a UErrorCode parameter because - * there are usually very few opportunities for failure other than a shortage - * of memory, error codes in low-level C++ string methods would be inconvenient, - * and the error code as the last parameter (ICU convention) would prevent - * the use of default parameter values. - * Instead, such methods set the UnicodeString into a "bogus" state - * (see isBogus()) if an error occurs. - * - * In string comparisons, two UnicodeString objects that are both "bogus" - * compare equal (to be transitive and prevent endless loops in sorting), - * and a "bogus" string compares less than any non-"bogus" one. - * - * Const UnicodeString methods are thread-safe. Multiple threads can use - * const methods on the same UnicodeString object simultaneously, - * but non-const methods must not be called concurrently (in multiple threads) - * with any other (const or non-const) methods. - * - * Similarly, const UnicodeString & parameters are thread-safe. - * One object may be passed in as such a parameter concurrently in multiple threads. - * This includes the const UnicodeString & parameters for - * copy construction, assignment, and cloning. - * - * UnicodeString uses several storage methods. - * String contents can be stored inside the UnicodeString object itself, - * in an allocated and shared buffer, or in an outside buffer that is "aliased". - * Most of this is done transparently, but careful aliasing in particular provides - * significant performance improvements. - * Also, the internal buffer is accessible via special functions. - * For details see the - * [User Guide Strings chapter](https://unicode-org.github.io/icu/userguide/strings#maximizing-performance-with-the-unicodestring-storage-model). - * - * @see utf.h - * @see CharacterIterator - * @stable ICU 2.0 - */ -class U_COMMON_API UnicodeString : public Replaceable -{ -public: - - /** - * Constant to be used in the UnicodeString(char *, int32_t, EInvariant) constructor - * which constructs a Unicode string from an invariant-character char * string. - * Use the macro US_INV instead of the full qualification for this value. - * - * @see US_INV - * @stable ICU 3.2 - */ - enum EInvariant { - /** - * @see EInvariant - * @stable ICU 3.2 - */ - kInvariant - }; - - //======================================== - // Read-only operations - //======================================== - - /* Comparison - bitwise only - for international comparison use collation */ - - /** - * Equality operator. Performs only bitwise comparison. - * @param text The UnicodeString to compare to this one. - * @return true if `text` contains the same characters as this one, - * false otherwise. - * @stable ICU 2.0 - */ - inline bool operator== (const UnicodeString& text) const; - - /** - * Inequality operator. Performs only bitwise comparison. - * @param text The UnicodeString to compare to this one. - * @return false if `text` contains the same characters as this one, - * true otherwise. - * @stable ICU 2.0 - */ - inline bool operator!= (const UnicodeString& text) const; - - /** - * Greater than operator. Performs only bitwise comparison. - * @param text The UnicodeString to compare to this one. - * @return true if the characters in this are bitwise - * greater than the characters in `text`, false otherwise - * @stable ICU 2.0 - */ - inline UBool operator> (const UnicodeString& text) const; - - /** - * Less than operator. Performs only bitwise comparison. - * @param text The UnicodeString to compare to this one. - * @return true if the characters in this are bitwise - * less than the characters in `text`, false otherwise - * @stable ICU 2.0 - */ - inline UBool operator< (const UnicodeString& text) const; - - /** - * Greater than or equal operator. Performs only bitwise comparison. - * @param text The UnicodeString to compare to this one. - * @return true if the characters in this are bitwise - * greater than or equal to the characters in `text`, false otherwise - * @stable ICU 2.0 - */ - inline UBool operator>= (const UnicodeString& text) const; - - /** - * Less than or equal operator. Performs only bitwise comparison. - * @param text The UnicodeString to compare to this one. - * @return true if the characters in this are bitwise - * less than or equal to the characters in `text`, false otherwise - * @stable ICU 2.0 - */ - inline UBool operator<= (const UnicodeString& text) const; - - /** - * Compare the characters bitwise in this UnicodeString to - * the characters in `text`. - * @param text The UnicodeString to compare to this one. - * @return The result of bitwise character comparison: 0 if this - * contains the same characters as `text`, -1 if the characters in - * this are bitwise less than the characters in `text`, +1 if the - * characters in this are bitwise greater than the characters - * in `text`. - * @stable ICU 2.0 - */ - inline int8_t compare(const UnicodeString& text) const; - - /** - * Compare the characters bitwise in the range - * [`start`, `start + length`) with the characters - * in the **entire string** `text`. - * (The parameters "start" and "length" are not applied to the other text "text".) - * @param start the offset at which the compare operation begins - * @param length the number of characters of text to compare. - * @param text the other text to be compared against this string. - * @return The result of bitwise character comparison: 0 if this - * contains the same characters as `text`, -1 if the characters in - * this are bitwise less than the characters in `text`, +1 if the - * characters in this are bitwise greater than the characters - * in `text`. - * @stable ICU 2.0 - */ - inline int8_t compare(int32_t start, - int32_t length, - const UnicodeString& text) const; - - /** - * Compare the characters bitwise in the range - * [`start`, `start + length`) with the characters - * in `srcText` in the range - * [`srcStart`, `srcStart + srcLength`). - * @param start the offset at which the compare operation begins - * @param length the number of characters in this to compare. - * @param srcText the text to be compared - * @param srcStart the offset into `srcText` to start comparison - * @param srcLength the number of characters in `src` to compare - * @return The result of bitwise character comparison: 0 if this - * contains the same characters as `srcText`, -1 if the characters in - * this are bitwise less than the characters in `srcText`, +1 if the - * characters in this are bitwise greater than the characters - * in `srcText`. - * @stable ICU 2.0 - */ - inline int8_t compare(int32_t start, - int32_t length, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) const; - - /** - * Compare the characters bitwise in this UnicodeString with the first - * `srcLength` characters in `srcChars`. - * @param srcChars The characters to compare to this UnicodeString. - * @param srcLength the number of characters in `srcChars` to compare - * @return The result of bitwise character comparison: 0 if this - * contains the same characters as `srcChars`, -1 if the characters in - * this are bitwise less than the characters in `srcChars`, +1 if the - * characters in this are bitwise greater than the characters - * in `srcChars`. - * @stable ICU 2.0 - */ - inline int8_t compare(ConstChar16Ptr srcChars, - int32_t srcLength) const; - - /** - * Compare the characters bitwise in the range - * [`start`, `start + length`) with the first - * `length` characters in `srcChars` - * @param start the offset at which the compare operation begins - * @param length the number of characters to compare. - * @param srcChars the characters to be compared - * @return The result of bitwise character comparison: 0 if this - * contains the same characters as `srcChars`, -1 if the characters in - * this are bitwise less than the characters in `srcChars`, +1 if the - * characters in this are bitwise greater than the characters - * in `srcChars`. - * @stable ICU 2.0 - */ - inline int8_t compare(int32_t start, - int32_t length, - const char16_t *srcChars) const; - - /** - * Compare the characters bitwise in the range - * [`start`, `start + length`) with the characters - * in `srcChars` in the range - * [`srcStart`, `srcStart + srcLength`). - * @param start the offset at which the compare operation begins - * @param length the number of characters in this to compare - * @param srcChars the characters to be compared - * @param srcStart the offset into `srcChars` to start comparison - * @param srcLength the number of characters in `srcChars` to compare - * @return The result of bitwise character comparison: 0 if this - * contains the same characters as `srcChars`, -1 if the characters in - * this are bitwise less than the characters in `srcChars`, +1 if the - * characters in this are bitwise greater than the characters - * in `srcChars`. - * @stable ICU 2.0 - */ - inline int8_t compare(int32_t start, - int32_t length, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength) const; - - /** - * Compare the characters bitwise in the range - * [`start`, `limit`) with the characters - * in `srcText` in the range - * [`srcStart`, `srcLimit`). - * @param start the offset at which the compare operation begins - * @param limit the offset immediately following the compare operation - * @param srcText the text to be compared - * @param srcStart the offset into `srcText` to start comparison - * @param srcLimit the offset into `srcText` to limit comparison - * @return The result of bitwise character comparison: 0 if this - * contains the same characters as `srcText`, -1 if the characters in - * this are bitwise less than the characters in `srcText`, +1 if the - * characters in this are bitwise greater than the characters - * in `srcText`. - * @stable ICU 2.0 - */ - inline int8_t compareBetween(int32_t start, - int32_t limit, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLimit) const; - - /** - * Compare two Unicode strings in code point order. - * The result may be different from the results of compare(), operator<, etc. - * if supplementary characters are present: - * - * In UTF-16, supplementary characters (with code points U+10000 and above) are - * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, - * which means that they compare as less than some other BMP characters like U+feff. - * This function compares Unicode strings in code point order. - * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. - * - * @param text Another string to compare this one to. - * @return a negative/zero/positive integer corresponding to whether - * this string is less than/equal to/greater than the second one - * in code point order - * @stable ICU 2.0 - */ - inline int8_t compareCodePointOrder(const UnicodeString& text) const; - - /** - * Compare two Unicode strings in code point order. - * The result may be different from the results of compare(), operator<, etc. - * if supplementary characters are present: - * - * In UTF-16, supplementary characters (with code points U+10000 and above) are - * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, - * which means that they compare as less than some other BMP characters like U+feff. - * This function compares Unicode strings in code point order. - * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. - * - * @param start The start offset in this string at which the compare operation begins. - * @param length The number of code units from this string to compare. - * @param srcText Another string to compare this one to. - * @return a negative/zero/positive integer corresponding to whether - * this string is less than/equal to/greater than the second one - * in code point order - * @stable ICU 2.0 - */ - inline int8_t compareCodePointOrder(int32_t start, - int32_t length, - const UnicodeString& srcText) const; - - /** - * Compare two Unicode strings in code point order. - * The result may be different from the results of compare(), operator<, etc. - * if supplementary characters are present: - * - * In UTF-16, supplementary characters (with code points U+10000 and above) are - * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, - * which means that they compare as less than some other BMP characters like U+feff. - * This function compares Unicode strings in code point order. - * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. - * - * @param start The start offset in this string at which the compare operation begins. - * @param length The number of code units from this string to compare. - * @param srcText Another string to compare this one to. - * @param srcStart The start offset in that string at which the compare operation begins. - * @param srcLength The number of code units from that string to compare. - * @return a negative/zero/positive integer corresponding to whether - * this string is less than/equal to/greater than the second one - * in code point order - * @stable ICU 2.0 - */ - inline int8_t compareCodePointOrder(int32_t start, - int32_t length, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) const; - - /** - * Compare two Unicode strings in code point order. - * The result may be different from the results of compare(), operator<, etc. - * if supplementary characters are present: - * - * In UTF-16, supplementary characters (with code points U+10000 and above) are - * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, - * which means that they compare as less than some other BMP characters like U+feff. - * This function compares Unicode strings in code point order. - * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. - * - * @param srcChars A pointer to another string to compare this one to. - * @param srcLength The number of code units from that string to compare. - * @return a negative/zero/positive integer corresponding to whether - * this string is less than/equal to/greater than the second one - * in code point order - * @stable ICU 2.0 - */ - inline int8_t compareCodePointOrder(ConstChar16Ptr srcChars, - int32_t srcLength) const; - - /** - * Compare two Unicode strings in code point order. - * The result may be different from the results of compare(), operator<, etc. - * if supplementary characters are present: - * - * In UTF-16, supplementary characters (with code points U+10000 and above) are - * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, - * which means that they compare as less than some other BMP characters like U+feff. - * This function compares Unicode strings in code point order. - * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. - * - * @param start The start offset in this string at which the compare operation begins. - * @param length The number of code units from this string to compare. - * @param srcChars A pointer to another string to compare this one to. - * @return a negative/zero/positive integer corresponding to whether - * this string is less than/equal to/greater than the second one - * in code point order - * @stable ICU 2.0 - */ - inline int8_t compareCodePointOrder(int32_t start, - int32_t length, - const char16_t *srcChars) const; - - /** - * Compare two Unicode strings in code point order. - * The result may be different from the results of compare(), operator<, etc. - * if supplementary characters are present: - * - * In UTF-16, supplementary characters (with code points U+10000 and above) are - * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, - * which means that they compare as less than some other BMP characters like U+feff. - * This function compares Unicode strings in code point order. - * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. - * - * @param start The start offset in this string at which the compare operation begins. - * @param length The number of code units from this string to compare. - * @param srcChars A pointer to another string to compare this one to. - * @param srcStart The start offset in that string at which the compare operation begins. - * @param srcLength The number of code units from that string to compare. - * @return a negative/zero/positive integer corresponding to whether - * this string is less than/equal to/greater than the second one - * in code point order - * @stable ICU 2.0 - */ - inline int8_t compareCodePointOrder(int32_t start, - int32_t length, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength) const; - - /** - * Compare two Unicode strings in code point order. - * The result may be different from the results of compare(), operator<, etc. - * if supplementary characters are present: - * - * In UTF-16, supplementary characters (with code points U+10000 and above) are - * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, - * which means that they compare as less than some other BMP characters like U+feff. - * This function compares Unicode strings in code point order. - * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. - * - * @param start The start offset in this string at which the compare operation begins. - * @param limit The offset after the last code unit from this string to compare. - * @param srcText Another string to compare this one to. - * @param srcStart The start offset in that string at which the compare operation begins. - * @param srcLimit The offset after the last code unit from that string to compare. - * @return a negative/zero/positive integer corresponding to whether - * this string is less than/equal to/greater than the second one - * in code point order - * @stable ICU 2.0 - */ - inline int8_t compareCodePointOrderBetween(int32_t start, - int32_t limit, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLimit) const; - - /** - * Compare two strings case-insensitively using full case folding. - * This is equivalent to this->foldCase(options).compare(text.foldCase(options)). - * - * @param text Another string to compare this one to. - * @param options A bit set of options: - * - U_FOLD_CASE_DEFAULT or 0 is used for default options: - * Comparison in code unit order with default case folding. - * - * - U_COMPARE_CODE_POINT_ORDER - * Set to choose code point order instead of code unit order - * (see u_strCompare for details). - * - * - U_FOLD_CASE_EXCLUDE_SPECIAL_I - * - * @return A negative, zero, or positive integer indicating the comparison result. - * @stable ICU 2.0 - */ - inline int8_t caseCompare(const UnicodeString& text, uint32_t options) const; - - /** - * Compare two strings case-insensitively using full case folding. - * This is equivalent to this->foldCase(options).compare(srcText.foldCase(options)). - * - * @param start The start offset in this string at which the compare operation begins. - * @param length The number of code units from this string to compare. - * @param srcText Another string to compare this one to. - * @param options A bit set of options: - * - U_FOLD_CASE_DEFAULT or 0 is used for default options: - * Comparison in code unit order with default case folding. - * - * - U_COMPARE_CODE_POINT_ORDER - * Set to choose code point order instead of code unit order - * (see u_strCompare for details). - * - * - U_FOLD_CASE_EXCLUDE_SPECIAL_I - * - * @return A negative, zero, or positive integer indicating the comparison result. - * @stable ICU 2.0 - */ - inline int8_t caseCompare(int32_t start, - int32_t length, - const UnicodeString& srcText, - uint32_t options) const; - - /** - * Compare two strings case-insensitively using full case folding. - * This is equivalent to this->foldCase(options).compare(srcText.foldCase(options)). - * - * @param start The start offset in this string at which the compare operation begins. - * @param length The number of code units from this string to compare. - * @param srcText Another string to compare this one to. - * @param srcStart The start offset in that string at which the compare operation begins. - * @param srcLength The number of code units from that string to compare. - * @param options A bit set of options: - * - U_FOLD_CASE_DEFAULT or 0 is used for default options: - * Comparison in code unit order with default case folding. - * - * - U_COMPARE_CODE_POINT_ORDER - * Set to choose code point order instead of code unit order - * (see u_strCompare for details). - * - * - U_FOLD_CASE_EXCLUDE_SPECIAL_I - * - * @return A negative, zero, or positive integer indicating the comparison result. - * @stable ICU 2.0 - */ - inline int8_t caseCompare(int32_t start, - int32_t length, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength, - uint32_t options) const; - - /** - * Compare two strings case-insensitively using full case folding. - * This is equivalent to this->foldCase(options).compare(srcChars.foldCase(options)). - * - * @param srcChars A pointer to another string to compare this one to. - * @param srcLength The number of code units from that string to compare. - * @param options A bit set of options: - * - U_FOLD_CASE_DEFAULT or 0 is used for default options: - * Comparison in code unit order with default case folding. - * - * - U_COMPARE_CODE_POINT_ORDER - * Set to choose code point order instead of code unit order - * (see u_strCompare for details). - * - * - U_FOLD_CASE_EXCLUDE_SPECIAL_I - * - * @return A negative, zero, or positive integer indicating the comparison result. - * @stable ICU 2.0 - */ - inline int8_t caseCompare(ConstChar16Ptr srcChars, - int32_t srcLength, - uint32_t options) const; - - /** - * Compare two strings case-insensitively using full case folding. - * This is equivalent to this->foldCase(options).compare(srcChars.foldCase(options)). - * - * @param start The start offset in this string at which the compare operation begins. - * @param length The number of code units from this string to compare. - * @param srcChars A pointer to another string to compare this one to. - * @param options A bit set of options: - * - U_FOLD_CASE_DEFAULT or 0 is used for default options: - * Comparison in code unit order with default case folding. - * - * - U_COMPARE_CODE_POINT_ORDER - * Set to choose code point order instead of code unit order - * (see u_strCompare for details). - * - * - U_FOLD_CASE_EXCLUDE_SPECIAL_I - * - * @return A negative, zero, or positive integer indicating the comparison result. - * @stable ICU 2.0 - */ - inline int8_t caseCompare(int32_t start, - int32_t length, - const char16_t *srcChars, - uint32_t options) const; - - /** - * Compare two strings case-insensitively using full case folding. - * This is equivalent to this->foldCase(options).compare(srcChars.foldCase(options)). - * - * @param start The start offset in this string at which the compare operation begins. - * @param length The number of code units from this string to compare. - * @param srcChars A pointer to another string to compare this one to. - * @param srcStart The start offset in that string at which the compare operation begins. - * @param srcLength The number of code units from that string to compare. - * @param options A bit set of options: - * - U_FOLD_CASE_DEFAULT or 0 is used for default options: - * Comparison in code unit order with default case folding. - * - * - U_COMPARE_CODE_POINT_ORDER - * Set to choose code point order instead of code unit order - * (see u_strCompare for details). - * - * - U_FOLD_CASE_EXCLUDE_SPECIAL_I - * - * @return A negative, zero, or positive integer indicating the comparison result. - * @stable ICU 2.0 - */ - inline int8_t caseCompare(int32_t start, - int32_t length, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength, - uint32_t options) const; - - /** - * Compare two strings case-insensitively using full case folding. - * This is equivalent to this->foldCase(options).compareBetween(text.foldCase(options)). - * - * @param start The start offset in this string at which the compare operation begins. - * @param limit The offset after the last code unit from this string to compare. - * @param srcText Another string to compare this one to. - * @param srcStart The start offset in that string at which the compare operation begins. - * @param srcLimit The offset after the last code unit from that string to compare. - * @param options A bit set of options: - * - U_FOLD_CASE_DEFAULT or 0 is used for default options: - * Comparison in code unit order with default case folding. - * - * - U_COMPARE_CODE_POINT_ORDER - * Set to choose code point order instead of code unit order - * (see u_strCompare for details). - * - * - U_FOLD_CASE_EXCLUDE_SPECIAL_I - * - * @return A negative, zero, or positive integer indicating the comparison result. - * @stable ICU 2.0 - */ - inline int8_t caseCompareBetween(int32_t start, - int32_t limit, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLimit, - uint32_t options) const; - - /** - * Determine if this starts with the characters in `text` - * @param text The text to match. - * @return true if this starts with the characters in `text`, - * false otherwise - * @stable ICU 2.0 - */ - inline UBool startsWith(const UnicodeString& text) const; - - /** - * Determine if this starts with the characters in `srcText` - * in the range [`srcStart`, `srcStart + srcLength`). - * @param srcText The text to match. - * @param srcStart the offset into `srcText` to start matching - * @param srcLength the number of characters in `srcText` to match - * @return true if this starts with the characters in `text`, - * false otherwise - * @stable ICU 2.0 - */ - inline UBool startsWith(const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) const; - - /** - * Determine if this starts with the characters in `srcChars` - * @param srcChars The characters to match. - * @param srcLength the number of characters in `srcChars` - * @return true if this starts with the characters in `srcChars`, - * false otherwise - * @stable ICU 2.0 - */ - inline UBool startsWith(ConstChar16Ptr srcChars, - int32_t srcLength) const; - - /** - * Determine if this ends with the characters in `srcChars` - * in the range [`srcStart`, `srcStart + srcLength`). - * @param srcChars The characters to match. - * @param srcStart the offset into `srcText` to start matching - * @param srcLength the number of characters in `srcChars` to match - * @return true if this ends with the characters in `srcChars`, false otherwise - * @stable ICU 2.0 - */ - inline UBool startsWith(const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength) const; - - /** - * Determine if this ends with the characters in `text` - * @param text The text to match. - * @return true if this ends with the characters in `text`, - * false otherwise - * @stable ICU 2.0 - */ - inline UBool endsWith(const UnicodeString& text) const; - - /** - * Determine if this ends with the characters in `srcText` - * in the range [`srcStart`, `srcStart + srcLength`). - * @param srcText The text to match. - * @param srcStart the offset into `srcText` to start matching - * @param srcLength the number of characters in `srcText` to match - * @return true if this ends with the characters in `text`, - * false otherwise - * @stable ICU 2.0 - */ - inline UBool endsWith(const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) const; - - /** - * Determine if this ends with the characters in `srcChars` - * @param srcChars The characters to match. - * @param srcLength the number of characters in `srcChars` - * @return true if this ends with the characters in `srcChars`, - * false otherwise - * @stable ICU 2.0 - */ - inline UBool endsWith(ConstChar16Ptr srcChars, - int32_t srcLength) const; - - /** - * Determine if this ends with the characters in `srcChars` - * in the range [`srcStart`, `srcStart + srcLength`). - * @param srcChars The characters to match. - * @param srcStart the offset into `srcText` to start matching - * @param srcLength the number of characters in `srcChars` to match - * @return true if this ends with the characters in `srcChars`, - * false otherwise - * @stable ICU 2.0 - */ - inline UBool endsWith(const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength) const; - - - /* Searching - bitwise only */ - - /** - * Locate in this the first occurrence of the characters in `text`, - * using bitwise comparison. - * @param text The text to search for. - * @return The offset into this of the start of `text`, - * or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t indexOf(const UnicodeString& text) const; - - /** - * Locate in this the first occurrence of the characters in `text` - * starting at offset `start`, using bitwise comparison. - * @param text The text to search for. - * @param start The offset at which searching will start. - * @return The offset into this of the start of `text`, - * or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t indexOf(const UnicodeString& text, - int32_t start) const; - - /** - * Locate in this the first occurrence in the range - * [`start`, `start + length`) of the characters - * in `text`, using bitwise comparison. - * @param text The text to search for. - * @param start The offset at which searching will start. - * @param length The number of characters to search - * @return The offset into this of the start of `text`, - * or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t indexOf(const UnicodeString& text, - int32_t start, - int32_t length) const; - - /** - * Locate in this the first occurrence in the range - * [`start`, `start + length`) of the characters - * in `srcText` in the range - * [`srcStart`, `srcStart + srcLength`), - * using bitwise comparison. - * @param srcText The text to search for. - * @param srcStart the offset into `srcText` at which - * to start matching - * @param srcLength the number of characters in `srcText` to match - * @param start the offset into this at which to start matching - * @param length the number of characters in this to search - * @return The offset into this of the start of `text`, - * or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t indexOf(const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength, - int32_t start, - int32_t length) const; - - /** - * Locate in this the first occurrence of the characters in - * `srcChars` - * starting at offset `start`, using bitwise comparison. - * @param srcChars The text to search for. - * @param srcLength the number of characters in `srcChars` to match - * @param start the offset into this at which to start matching - * @return The offset into this of the start of `text`, - * or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t indexOf(const char16_t *srcChars, - int32_t srcLength, - int32_t start) const; - - /** - * Locate in this the first occurrence in the range - * [`start`, `start + length`) of the characters - * in `srcChars`, using bitwise comparison. - * @param srcChars The text to search for. - * @param srcLength the number of characters in `srcChars` - * @param start The offset at which searching will start. - * @param length The number of characters to search - * @return The offset into this of the start of `srcChars`, - * or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t indexOf(ConstChar16Ptr srcChars, - int32_t srcLength, - int32_t start, - int32_t length) const; - - /** - * Locate in this the first occurrence in the range - * [`start`, `start + length`) of the characters - * in `srcChars` in the range - * [`srcStart`, `srcStart + srcLength`), - * using bitwise comparison. - * @param srcChars The text to search for. - * @param srcStart the offset into `srcChars` at which - * to start matching - * @param srcLength the number of characters in `srcChars` to match - * @param start the offset into this at which to start matching - * @param length the number of characters in this to search - * @return The offset into this of the start of `text`, - * or -1 if not found. - * @stable ICU 2.0 - */ - int32_t indexOf(const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength, - int32_t start, - int32_t length) const; - - /** - * Locate in this the first occurrence of the BMP code point `c`, - * using bitwise comparison. - * @param c The code unit to search for. - * @return The offset into this of `c`, or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t indexOf(char16_t c) const; - - /** - * Locate in this the first occurrence of the code point `c`, - * using bitwise comparison. - * - * @param c The code point to search for. - * @return The offset into this of `c`, or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t indexOf(UChar32 c) const; - - /** - * Locate in this the first occurrence of the BMP code point `c`, - * starting at offset `start`, using bitwise comparison. - * @param c The code unit to search for. - * @param start The offset at which searching will start. - * @return The offset into this of `c`, or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t indexOf(char16_t c, - int32_t start) const; - - /** - * Locate in this the first occurrence of the code point `c` - * starting at offset `start`, using bitwise comparison. - * - * @param c The code point to search for. - * @param start The offset at which searching will start. - * @return The offset into this of `c`, or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t indexOf(UChar32 c, - int32_t start) const; - - /** - * Locate in this the first occurrence of the BMP code point `c` - * in the range [`start`, `start + length`), - * using bitwise comparison. - * @param c The code unit to search for. - * @param start the offset into this at which to start matching - * @param length the number of characters in this to search - * @return The offset into this of `c`, or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t indexOf(char16_t c, - int32_t start, - int32_t length) const; - - /** - * Locate in this the first occurrence of the code point `c` - * in the range [`start`, `start + length`), - * using bitwise comparison. - * - * @param c The code point to search for. - * @param start the offset into this at which to start matching - * @param length the number of characters in this to search - * @return The offset into this of `c`, or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t indexOf(UChar32 c, - int32_t start, - int32_t length) const; - - /** - * Locate in this the last occurrence of the characters in `text`, - * using bitwise comparison. - * @param text The text to search for. - * @return The offset into this of the start of `text`, - * or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t lastIndexOf(const UnicodeString& text) const; - - /** - * Locate in this the last occurrence of the characters in `text` - * starting at offset `start`, using bitwise comparison. - * @param text The text to search for. - * @param start The offset at which searching will start. - * @return The offset into this of the start of `text`, - * or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t lastIndexOf(const UnicodeString& text, - int32_t start) const; - - /** - * Locate in this the last occurrence in the range - * [`start`, `start + length`) of the characters - * in `text`, using bitwise comparison. - * @param text The text to search for. - * @param start The offset at which searching will start. - * @param length The number of characters to search - * @return The offset into this of the start of `text`, - * or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t lastIndexOf(const UnicodeString& text, - int32_t start, - int32_t length) const; - - /** - * Locate in this the last occurrence in the range - * [`start`, `start + length`) of the characters - * in `srcText` in the range - * [`srcStart`, `srcStart + srcLength`), - * using bitwise comparison. - * @param srcText The text to search for. - * @param srcStart the offset into `srcText` at which - * to start matching - * @param srcLength the number of characters in `srcText` to match - * @param start the offset into this at which to start matching - * @param length the number of characters in this to search - * @return The offset into this of the start of `text`, - * or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t lastIndexOf(const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength, - int32_t start, - int32_t length) const; - - /** - * Locate in this the last occurrence of the characters in `srcChars` - * starting at offset `start`, using bitwise comparison. - * @param srcChars The text to search for. - * @param srcLength the number of characters in `srcChars` to match - * @param start the offset into this at which to start matching - * @return The offset into this of the start of `text`, - * or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t lastIndexOf(const char16_t *srcChars, - int32_t srcLength, - int32_t start) const; - - /** - * Locate in this the last occurrence in the range - * [`start`, `start + length`) of the characters - * in `srcChars`, using bitwise comparison. - * @param srcChars The text to search for. - * @param srcLength the number of characters in `srcChars` - * @param start The offset at which searching will start. - * @param length The number of characters to search - * @return The offset into this of the start of `srcChars`, - * or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t lastIndexOf(ConstChar16Ptr srcChars, - int32_t srcLength, - int32_t start, - int32_t length) const; - - /** - * Locate in this the last occurrence in the range - * [`start`, `start + length`) of the characters - * in `srcChars` in the range - * [`srcStart`, `srcStart + srcLength`), - * using bitwise comparison. - * @param srcChars The text to search for. - * @param srcStart the offset into `srcChars` at which - * to start matching - * @param srcLength the number of characters in `srcChars` to match - * @param start the offset into this at which to start matching - * @param length the number of characters in this to search - * @return The offset into this of the start of `text`, - * or -1 if not found. - * @stable ICU 2.0 - */ - int32_t lastIndexOf(const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength, - int32_t start, - int32_t length) const; - - /** - * Locate in this the last occurrence of the BMP code point `c`, - * using bitwise comparison. - * @param c The code unit to search for. - * @return The offset into this of `c`, or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t lastIndexOf(char16_t c) const; - - /** - * Locate in this the last occurrence of the code point `c`, - * using bitwise comparison. - * - * @param c The code point to search for. - * @return The offset into this of `c`, or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t lastIndexOf(UChar32 c) const; - - /** - * Locate in this the last occurrence of the BMP code point `c` - * starting at offset `start`, using bitwise comparison. - * @param c The code unit to search for. - * @param start The offset at which searching will start. - * @return The offset into this of `c`, or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t lastIndexOf(char16_t c, - int32_t start) const; - - /** - * Locate in this the last occurrence of the code point `c` - * starting at offset `start`, using bitwise comparison. - * - * @param c The code point to search for. - * @param start The offset at which searching will start. - * @return The offset into this of `c`, or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t lastIndexOf(UChar32 c, - int32_t start) const; - - /** - * Locate in this the last occurrence of the BMP code point `c` - * in the range [`start`, `start + length`), - * using bitwise comparison. - * @param c The code unit to search for. - * @param start the offset into this at which to start matching - * @param length the number of characters in this to search - * @return The offset into this of `c`, or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t lastIndexOf(char16_t c, - int32_t start, - int32_t length) const; - - /** - * Locate in this the last occurrence of the code point `c` - * in the range [`start`, `start + length`), - * using bitwise comparison. - * - * @param c The code point to search for. - * @param start the offset into this at which to start matching - * @param length the number of characters in this to search - * @return The offset into this of `c`, or -1 if not found. - * @stable ICU 2.0 - */ - inline int32_t lastIndexOf(UChar32 c, - int32_t start, - int32_t length) const; - - - /* Character access */ - - /** - * Return the code unit at offset `offset`. - * If the offset is not valid (0..length()-1) then U+ffff is returned. - * @param offset a valid offset into the text - * @return the code unit at offset `offset` - * or 0xffff if the offset is not valid for this string - * @stable ICU 2.0 - */ - inline char16_t charAt(int32_t offset) const; - - /** - * Return the code unit at offset `offset`. - * If the offset is not valid (0..length()-1) then U+ffff is returned. - * @param offset a valid offset into the text - * @return the code unit at offset `offset` - * @stable ICU 2.0 - */ - inline char16_t operator[] (int32_t offset) const; - - /** - * Return the code point that contains the code unit - * at offset `offset`. - * If the offset is not valid (0..length()-1) then U+ffff is returned. - * @param offset a valid offset into the text - * that indicates the text offset of any of the code units - * that will be assembled into a code point (21-bit value) and returned - * @return the code point of text at `offset` - * or 0xffff if the offset is not valid for this string - * @stable ICU 2.0 - */ - UChar32 char32At(int32_t offset) const; - - /** - * Adjust a random-access offset so that - * it points to the beginning of a Unicode character. - * The offset that is passed in points to - * any code unit of a code point, - * while the returned offset will point to the first code unit - * of the same code point. - * In UTF-16, if the input offset points to a second surrogate - * of a surrogate pair, then the returned offset will point - * to the first surrogate. - * @param offset a valid offset into one code point of the text - * @return offset of the first code unit of the same code point - * @see U16_SET_CP_START - * @stable ICU 2.0 - */ - int32_t getChar32Start(int32_t offset) const; - - /** - * Adjust a random-access offset so that - * it points behind a Unicode character. - * The offset that is passed in points behind - * any code unit of a code point, - * while the returned offset will point behind the last code unit - * of the same code point. - * In UTF-16, if the input offset points behind the first surrogate - * (i.e., to the second surrogate) - * of a surrogate pair, then the returned offset will point - * behind the second surrogate (i.e., to the first surrogate). - * @param offset a valid offset after any code unit of a code point of the text - * @return offset of the first code unit after the same code point - * @see U16_SET_CP_LIMIT - * @stable ICU 2.0 - */ - int32_t getChar32Limit(int32_t offset) const; - - /** - * Move the code unit index along the string by delta code points. - * Interpret the input index as a code unit-based offset into the string, - * move the index forward or backward by delta code points, and - * return the resulting index. - * The input index should point to the first code unit of a code point, - * if there is more than one. - * - * Both input and output indexes are code unit-based as for all - * string indexes/offsets in ICU (and other libraries, like MBCS char*). - * If delta<0 then the index is moved backward (toward the start of the string). - * If delta>0 then the index is moved forward (toward the end of the string). - * - * This behaves like CharacterIterator::move32(delta, kCurrent). - * - * Behavior for out-of-bounds indexes: - * `moveIndex32` pins the input index to 0..length(), i.e., - * if the input index<0 then it is pinned to 0; - * if it is index>length() then it is pinned to length(). - * Afterwards, the index is moved by `delta` code points - * forward or backward, - * but no further backward than to 0 and no further forward than to length(). - * The resulting index return value will be in between 0 and length(), inclusively. - * - * Examples: - * \code - * // s has code points 'a' U+10000 'b' U+10ffff U+2029 - * UnicodeString s(u"a\U00010000b\U0010ffff\u2029"); - * - * // initial index: position of U+10000 - * int32_t index=1; - * - * // the following examples will all result in index==4, position of U+10ffff - * - * // skip 2 code points from some position in the string - * index=s.moveIndex32(index, 2); // skips U+10000 and 'b' - * - * // go to the 3rd code point from the start of s (0-based) - * index=s.moveIndex32(0, 3); // skips 'a', U+10000, and 'b' - * - * // go to the next-to-last code point of s - * index=s.moveIndex32(s.length(), -2); // backward-skips U+2029 and U+10ffff - * \endcode - * - * @param index input code unit index - * @param delta (signed) code point count to move the index forward or backward - * in the string - * @return the resulting code unit index - * @stable ICU 2.0 - */ - int32_t moveIndex32(int32_t index, int32_t delta) const; - - /* Substring extraction */ - - /** - * Copy the characters in the range - * [`start`, `start + length`) into the array `dst`, - * beginning at `dstStart`. - * If the string aliases to `dst` itself as an external buffer, - * then extract() will not copy the contents. - * - * @param start offset of first character which will be copied into the array - * @param length the number of characters to extract - * @param dst array in which to copy characters. The length of `dst` - * must be at least (`dstStart + length`). - * @param dstStart the offset in `dst` where the first character - * will be extracted - * @stable ICU 2.0 - */ - inline void extract(int32_t start, - int32_t length, - Char16Ptr dst, - int32_t dstStart = 0) const; - - /** - * Copy the contents of the string into dest. - * This is a convenience function that - * checks if there is enough space in dest, - * extracts the entire string if possible, - * and NUL-terminates dest if possible. - * - * If the string fits into dest but cannot be NUL-terminated - * (length()==destCapacity) then the error code is set to U_STRING_NOT_TERMINATED_WARNING. - * If the string itself does not fit into dest - * (length()>destCapacity) then the error code is set to U_BUFFER_OVERFLOW_ERROR. - * - * If the string aliases to `dest` itself as an external buffer, - * then extract() will not copy the contents. - * - * @param dest Destination string buffer. - * @param destCapacity Number of char16_ts available at dest. - * @param errorCode ICU error code. - * @return length() - * @stable ICU 2.0 - */ - int32_t - extract(Char16Ptr dest, int32_t destCapacity, - UErrorCode &errorCode) const; - - /** - * Copy the characters in the range - * [`start`, `start + length`) into the UnicodeString - * `target`. - * @param start offset of first character which will be copied - * @param length the number of characters to extract - * @param target UnicodeString into which to copy characters. - * @stable ICU 2.0 - */ - inline void extract(int32_t start, - int32_t length, - UnicodeString& target) const; - - /** - * Copy the characters in the range [`start`, `limit`) - * into the array `dst`, beginning at `dstStart`. - * @param start offset of first character which will be copied into the array - * @param limit offset immediately following the last character to be copied - * @param dst array in which to copy characters. The length of `dst` - * must be at least (`dstStart + (limit - start)`). - * @param dstStart the offset in `dst` where the first character - * will be extracted - * @stable ICU 2.0 - */ - inline void extractBetween(int32_t start, - int32_t limit, - char16_t *dst, - int32_t dstStart = 0) const; - - /** - * Copy the characters in the range [`start`, `limit`) - * into the UnicodeString `target`. Replaceable API. - * @param start offset of first character which will be copied - * @param limit offset immediately following the last character to be copied - * @param target UnicodeString into which to copy characters. - * @stable ICU 2.0 - */ - virtual void extractBetween(int32_t start, - int32_t limit, - UnicodeString& target) const override; - - /** - * Copy the characters in the range - * [`start`, `start + startLength`) into an array of characters. - * All characters must be invariant (see utypes.h). - * Use US_INV as the last, signature-distinguishing parameter. - * - * This function does not write any more than `targetCapacity` - * characters but returns the length of the entire output string - * so that one can allocate a larger buffer and call the function again - * if necessary. - * The output string is NUL-terminated if possible. - * - * @param start offset of first character which will be copied - * @param startLength the number of characters to extract - * @param target the target buffer for extraction, can be NULL - * if targetLength is 0 - * @param targetCapacity the length of the target buffer - * @param inv Signature-distinguishing parameter, use US_INV. - * @return the output string length, not including the terminating NUL - * @stable ICU 3.2 - */ - int32_t extract(int32_t start, - int32_t startLength, - char *target, - int32_t targetCapacity, - enum EInvariant inv) const; - -#if U_CHARSET_IS_UTF8 || !UCONFIG_NO_CONVERSION - - /** - * Copy the characters in the range - * [`start`, `start + length`) into an array of characters - * in the platform's default codepage. - * This function does not write any more than `targetLength` - * characters but returns the length of the entire output string - * so that one can allocate a larger buffer and call the function again - * if necessary. - * The output string is NUL-terminated if possible. - * - * @param start offset of first character which will be copied - * @param startLength the number of characters to extract - * @param target the target buffer for extraction - * @param targetLength the length of the target buffer - * If `target` is NULL, then the number of bytes required for - * `target` is returned. - * @return the output string length, not including the terminating NUL - * @stable ICU 2.0 - */ - int32_t extract(int32_t start, - int32_t startLength, - char *target, - uint32_t targetLength) const; - -#endif - -#if !UCONFIG_NO_CONVERSION - - /** - * Copy the characters in the range - * [`start`, `start + length`) into an array of characters - * in a specified codepage. - * The output string is NUL-terminated. - * - * Recommendation: For invariant-character strings use - * extract(int32_t start, int32_t length, char *target, int32_t targetCapacity, enum EInvariant inv) const - * because it avoids object code dependencies of UnicodeString on - * the conversion code. - * - * @param start offset of first character which will be copied - * @param startLength the number of characters to extract - * @param target the target buffer for extraction - * @param codepage the desired codepage for the characters. 0 has - * the special meaning of the default codepage - * If `codepage` is an empty string (`""`), - * then a simple conversion is performed on the codepage-invariant - * subset ("invariant characters") of the platform encoding. See utypes.h. - * If `target` is NULL, then the number of bytes required for - * `target` is returned. It is assumed that the target is big enough - * to fit all of the characters. - * @return the output string length, not including the terminating NUL - * @stable ICU 2.0 - */ - inline int32_t extract(int32_t start, - int32_t startLength, - char *target, - const char *codepage = 0) const; - - /** - * Copy the characters in the range - * [`start`, `start + length`) into an array of characters - * in a specified codepage. - * This function does not write any more than `targetLength` - * characters but returns the length of the entire output string - * so that one can allocate a larger buffer and call the function again - * if necessary. - * The output string is NUL-terminated if possible. - * - * Recommendation: For invariant-character strings use - * extract(int32_t start, int32_t length, char *target, int32_t targetCapacity, enum EInvariant inv) const - * because it avoids object code dependencies of UnicodeString on - * the conversion code. - * - * @param start offset of first character which will be copied - * @param startLength the number of characters to extract - * @param target the target buffer for extraction - * @param targetLength the length of the target buffer - * @param codepage the desired codepage for the characters. 0 has - * the special meaning of the default codepage - * If `codepage` is an empty string (`""`), - * then a simple conversion is performed on the codepage-invariant - * subset ("invariant characters") of the platform encoding. See utypes.h. - * If `target` is NULL, then the number of bytes required for - * `target` is returned. - * @return the output string length, not including the terminating NUL - * @stable ICU 2.0 - */ - int32_t extract(int32_t start, - int32_t startLength, - char *target, - uint32_t targetLength, - const char *codepage) const; - - /** - * Convert the UnicodeString into a codepage string using an existing UConverter. - * The output string is NUL-terminated if possible. - * - * This function avoids the overhead of opening and closing a converter if - * multiple strings are extracted. - * - * @param dest destination string buffer, can be NULL if destCapacity==0 - * @param destCapacity the number of chars available at dest - * @param cnv the converter object to be used (ucnv_resetFromUnicode() will be called), - * or NULL for the default converter - * @param errorCode normal ICU error code - * @return the length of the output string, not counting the terminating NUL; - * if the length is greater than destCapacity, then the string will not fit - * and a buffer of the indicated length would need to be passed in - * @stable ICU 2.0 - */ - int32_t extract(char *dest, int32_t destCapacity, - UConverter *cnv, - UErrorCode &errorCode) const; - -#endif - - /** - * Create a temporary substring for the specified range. - * Unlike the substring constructor and setTo() functions, - * the object returned here will be a read-only alias (using getBuffer()) - * rather than copying the text. - * As a result, this substring operation is much faster but requires - * that the original string not be modified or deleted during the lifetime - * of the returned substring object. - * @param start offset of the first character visible in the substring - * @param length length of the substring - * @return a read-only alias UnicodeString object for the substring - * @stable ICU 4.4 - */ - UnicodeString tempSubString(int32_t start=0, int32_t length=INT32_MAX) const; - - /** - * Create a temporary substring for the specified range. - * Same as tempSubString(start, length) except that the substring range - * is specified as a (start, limit) pair (with an exclusive limit index) - * rather than a (start, length) pair. - * @param start offset of the first character visible in the substring - * @param limit offset immediately following the last character visible in the substring - * @return a read-only alias UnicodeString object for the substring - * @stable ICU 4.4 - */ - inline UnicodeString tempSubStringBetween(int32_t start, int32_t limit=INT32_MAX) const; - - /** - * Convert the UnicodeString to UTF-8 and write the result - * to a ByteSink. This is called by toUTF8String(). - * Unpaired surrogates are replaced with U+FFFD. - * Calls u_strToUTF8WithSub(). - * - * @param sink A ByteSink to which the UTF-8 version of the string is written. - * sink.Flush() is called at the end. - * @stable ICU 4.2 - * @see toUTF8String - */ - void toUTF8(ByteSink &sink) const; - - /** - * Convert the UnicodeString to UTF-8 and append the result - * to a standard string. - * Unpaired surrogates are replaced with U+FFFD. - * Calls toUTF8(). - * - * @param result A standard string (or a compatible object) - * to which the UTF-8 version of the string is appended. - * @return The string object. - * @stable ICU 4.2 - * @see toUTF8 - */ - template - StringClass &toUTF8String(StringClass &result) const { - StringByteSink sbs(&result, length()); - toUTF8(sbs); - return result; - } - - /** - * Convert the UnicodeString to UTF-32. - * Unpaired surrogates are replaced with U+FFFD. - * Calls u_strToUTF32WithSub(). - * - * @param utf32 destination string buffer, can be NULL if capacity==0 - * @param capacity the number of UChar32s available at utf32 - * @param errorCode Standard 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.) - * @return The length of the UTF-32 string. - * @see fromUTF32 - * @stable ICU 4.2 - */ - int32_t toUTF32(UChar32 *utf32, int32_t capacity, UErrorCode &errorCode) const; - - /* Length operations */ - - /** - * Return the length of the UnicodeString object. - * The length is the number of char16_t code units are in the UnicodeString. - * If you want the number of code points, please use countChar32(). - * @return the length of the UnicodeString object - * @see countChar32 - * @stable ICU 2.0 - */ - inline int32_t length(void) const; - - /** - * Count Unicode code points in the length char16_t code units of the string. - * A code point may occupy either one or two char16_t code units. - * Counting code points involves reading all code units. - * - * This functions is basically the inverse of moveIndex32(). - * - * @param start the index of the first code unit to check - * @param length the number of char16_t code units to check - * @return the number of code points in the specified code units - * @see length - * @stable ICU 2.0 - */ - int32_t - countChar32(int32_t start=0, int32_t length=INT32_MAX) const; - - /** - * Check if the length char16_t code units of the string - * contain more Unicode code points than a certain number. - * This is more efficient than counting all code points in this part of the string - * and comparing that number with a threshold. - * This function may not need to scan the string at all if the length - * falls within a certain range, and - * never needs to count more than 'number+1' code points. - * Logically equivalent to (countChar32(start, length)>number). - * A Unicode code point may occupy either one or two char16_t code units. - * - * @param start the index of the first code unit to check (0 for the entire string) - * @param length the number of char16_t code units to check - * (use INT32_MAX for the entire string; remember that start/length - * values are pinned) - * @param number The number of code points in the (sub)string is compared against - * the 'number' parameter. - * @return Boolean value for whether the string contains more Unicode code points - * than 'number'. Same as (u_countChar32(s, length)>number). - * @see countChar32 - * @see u_strHasMoreChar32Than - * @stable ICU 2.4 - */ - UBool - hasMoreChar32Than(int32_t start, int32_t length, int32_t number) const; - - /** - * Determine if this string is empty. - * @return true if this string contains 0 characters, false otherwise. - * @stable ICU 2.0 - */ - inline UBool isEmpty(void) const; - - /** - * Return the capacity of the internal buffer of the UnicodeString object. - * This is useful together with the getBuffer functions. - * See there for details. - * - * @return the number of char16_ts available in the internal buffer - * @see getBuffer - * @stable ICU 2.0 - */ - inline int32_t getCapacity(void) const; - - /* Other operations */ - - /** - * Generate a hash code for this object. - * @return The hash code of this UnicodeString. - * @stable ICU 2.0 - */ - inline int32_t hashCode(void) const; - - /** - * Determine if this object contains a valid string. - * A bogus string has no value. It is different from an empty string, - * although in both cases isEmpty() returns true and length() returns 0. - * setToBogus() and isBogus() can be used to indicate that no string value is available. - * For a bogus string, getBuffer() and getTerminatedBuffer() return NULL, and - * length() returns 0. - * - * @return true if the string is bogus/invalid, false otherwise - * @see setToBogus() - * @stable ICU 2.0 - */ - inline UBool isBogus(void) const; - - - //======================================== - // Write operations - //======================================== - - /* Assignment operations */ - - /** - * Assignment operator. Replace the characters in this UnicodeString - * with the characters from `srcText`. - * - * Starting with ICU 2.4, the assignment operator and the copy constructor - * allocate a new buffer and copy the buffer contents even for readonly aliases. - * By contrast, the fastCopyFrom() function implements the old, - * more efficient but less safe behavior - * of making this string also a readonly alias to the same buffer. - * - * If the source object has an "open" buffer from getBuffer(minCapacity), - * then the copy is an empty string. - * - * @param srcText The text containing the characters to replace - * @return a reference to this - * @stable ICU 2.0 - * @see fastCopyFrom - */ - UnicodeString &operator=(const UnicodeString &srcText); - - /** - * Almost the same as the assignment operator. - * Replace the characters in this UnicodeString - * with the characters from `srcText`. - * - * This function works the same as the assignment operator - * for all strings except for ones that are readonly aliases. - * - * Starting with ICU 2.4, the assignment operator and the copy constructor - * allocate a new buffer and copy the buffer contents even for readonly aliases. - * This function implements the old, more efficient but less safe behavior - * of making this string also a readonly alias to the same buffer. - * - * The fastCopyFrom function must be used only if it is known that the lifetime of - * this UnicodeString does not exceed the lifetime of the aliased buffer - * including its contents, for example for strings from resource bundles - * or aliases to string constants. - * - * If the source object has an "open" buffer from getBuffer(minCapacity), - * then the copy is an empty string. - * - * @param src The text containing the characters to replace. - * @return a reference to this - * @stable ICU 2.4 - */ - UnicodeString &fastCopyFrom(const UnicodeString &src); - - /** - * 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 - * @return *this - * @stable ICU 56 - */ - UnicodeString &operator=(UnicodeString &&src) U_NOEXCEPT; - - /** - * Swap strings. - * @param other other string - * @stable ICU 56 - */ - void swap(UnicodeString &other) U_NOEXCEPT; - - /** - * Non-member UnicodeString swap function. - * @param s1 will get s2's contents and state - * @param s2 will get s1's contents and state - * @stable ICU 56 - */ - friend inline void U_EXPORT2 - swap(UnicodeString &s1, UnicodeString &s2) U_NOEXCEPT { - s1.swap(s2); - } - - /** - * Assignment operator. Replace the characters in this UnicodeString - * with the code unit `ch`. - * @param ch the code unit to replace - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& operator= (char16_t ch); - - /** - * Assignment operator. Replace the characters in this UnicodeString - * with the code point `ch`. - * @param ch the code point to replace - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& operator= (UChar32 ch); - - /** - * Set the text in the UnicodeString object to the characters - * in `srcText` in the range - * [`srcStart`, `srcText.length()`). - * `srcText` is not modified. - * @param srcText the source for the new characters - * @param srcStart the offset into `srcText` where new characters - * will be obtained - * @return a reference to this - * @stable ICU 2.2 - */ - inline UnicodeString& setTo(const UnicodeString& srcText, - int32_t srcStart); - - /** - * Set the text in the UnicodeString object to the characters - * in `srcText` in the range - * [`srcStart`, `srcStart + srcLength`). - * `srcText` is not modified. - * @param srcText the source for the new characters - * @param srcStart the offset into `srcText` where new characters - * will be obtained - * @param srcLength the number of characters in `srcText` in the - * replace string. - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& setTo(const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength); - - /** - * Set the text in the UnicodeString object to the characters in - * `srcText`. - * `srcText` is not modified. - * @param srcText the source for the new characters - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& setTo(const UnicodeString& srcText); - - /** - * Set the characters in the UnicodeString object to the characters - * in `srcChars`. `srcChars` is not modified. - * @param srcChars the source for the new characters - * @param srcLength the number of Unicode characters in srcChars. - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& setTo(const char16_t *srcChars, - int32_t srcLength); - - /** - * Set the characters in the UnicodeString object to the code unit - * `srcChar`. - * @param srcChar the code unit which becomes the UnicodeString's character - * content - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& setTo(char16_t srcChar); - - /** - * Set the characters in the UnicodeString object to the code point - * `srcChar`. - * @param srcChar the code point which becomes the UnicodeString's character - * content - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& setTo(UChar32 srcChar); - - /** - * Aliasing setTo() function, analogous to the readonly-aliasing char16_t* constructor. - * The text will be used for the UnicodeString object, but - * it will not be released when the UnicodeString is destroyed. - * This has copy-on-write semantics: - * When the string is modified, then the buffer is first copied into - * newly allocated memory. - * The aliased buffer is never modified. - * - * In an assignment to another UnicodeString, when using the copy constructor - * or the assignment operator, the text will be copied. - * When using fastCopyFrom(), the text will be aliased again, - * so that both strings then alias the same readonly-text. - * - * @param isTerminated specifies if `text` is `NUL`-terminated. - * This must be true if `textLength==-1`. - * @param text The characters to alias for the UnicodeString. - * @param textLength The number of Unicode characters in `text` to alias. - * If -1, then this constructor will determine the length - * by calling `u_strlen()`. - * @return a reference to this - * @stable ICU 2.0 - */ - UnicodeString &setTo(UBool isTerminated, - ConstChar16Ptr text, - int32_t textLength); - - /** - * Aliasing setTo() function, analogous to the writable-aliasing char16_t* constructor. - * The text will be used for the UnicodeString object, but - * it will not be released when the UnicodeString is destroyed. - * This has write-through semantics: - * For as long as the capacity of the buffer is sufficient, write operations - * will directly affect the buffer. When more capacity is necessary, then - * a new buffer will be allocated and the contents copied as with regularly - * constructed strings. - * In an assignment to another UnicodeString, the buffer will be copied. - * The extract(Char16Ptr dst) function detects whether the dst pointer is the same - * as the string buffer itself and will in this case not copy the contents. - * - * @param buffer The characters to alias for the UnicodeString. - * @param buffLength The number of Unicode characters in `buffer` to alias. - * @param buffCapacity The size of `buffer` in char16_ts. - * @return a reference to this - * @stable ICU 2.0 - */ - UnicodeString &setTo(char16_t *buffer, - int32_t buffLength, - int32_t buffCapacity); - - /** - * Make this UnicodeString object invalid. - * The string will test true with isBogus(). - * - * A bogus string has no value. It is different from an empty string. - * It can be used to indicate that no string value is available. - * getBuffer() and getTerminatedBuffer() return NULL, and - * length() returns 0. - * - * This utility function is used throughout the UnicodeString - * implementation to indicate that a UnicodeString operation failed, - * and may be used in other functions, - * especially but not exclusively when such functions do not - * take a UErrorCode for simplicity. - * - * The following methods, and no others, will clear a string object's bogus flag: - * - remove() - * - remove(0, INT32_MAX) - * - truncate(0) - * - operator=() (assignment operator) - * - setTo(...) - * - * The simplest ways to turn a bogus string into an empty one - * is to use the remove() function. - * Examples for other functions that are equivalent to "set to empty string": - * \code - * if(s.isBogus()) { - * s.remove(); // set to an empty string (remove all), or - * s.remove(0, INT32_MAX); // set to an empty string (remove all), or - * s.truncate(0); // set to an empty string (complete truncation), or - * s=UnicodeString(); // assign an empty string, or - * s.setTo((UChar32)-1); // set to a pseudo code point that is out of range, or - * s.setTo(u"", 0); // set to an empty C Unicode string - * } - * \endcode - * - * @see isBogus() - * @stable ICU 2.0 - */ - void setToBogus(); - - /** - * Set the character at the specified offset to the specified character. - * @param offset A valid offset into the text of the character to set - * @param ch The new character - * @return A reference to this - * @stable ICU 2.0 - */ - UnicodeString& setCharAt(int32_t offset, - char16_t ch); - - - /* Append operations */ - - /** - * Append operator. Append the code unit `ch` to the UnicodeString - * object. - * @param ch the code unit to be appended - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& operator+= (char16_t ch); - - /** - * Append operator. Append the code point `ch` to the UnicodeString - * object. - * @param ch the code point to be appended - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& operator+= (UChar32 ch); - - /** - * Append operator. Append the characters in `srcText` to the - * UnicodeString object. `srcText` is not modified. - * @param srcText the source for the new characters - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& operator+= (const UnicodeString& srcText); - - /** - * Append the characters - * in `srcText` in the range - * [`srcStart`, `srcStart + srcLength`) to the - * UnicodeString object at offset `start`. `srcText` - * is not modified. - * @param srcText the source for the new characters - * @param srcStart the offset into `srcText` where new characters - * will be obtained - * @param srcLength the number of characters in `srcText` in - * the append string - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& append(const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength); - - /** - * Append the characters in `srcText` to the UnicodeString object. - * `srcText` is not modified. - * @param srcText the source for the new characters - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& append(const UnicodeString& srcText); - - /** - * Append the characters in `srcChars` in the range - * [`srcStart`, `srcStart + srcLength`) to the UnicodeString - * object at offset - * `start`. `srcChars` is not modified. - * @param srcChars the source for the new characters - * @param srcStart the offset into `srcChars` where new characters - * will be obtained - * @param srcLength the number of characters in `srcChars` in - * the append string; can be -1 if `srcChars` is NUL-terminated - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& append(const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength); - - /** - * Append the characters in `srcChars` to the UnicodeString object - * at offset `start`. `srcChars` is not modified. - * @param srcChars the source for the new characters - * @param srcLength the number of Unicode characters in `srcChars`; - * can be -1 if `srcChars` is NUL-terminated - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& append(ConstChar16Ptr srcChars, - int32_t srcLength); - - /** - * Append the code unit `srcChar` to the UnicodeString object. - * @param srcChar the code unit to append - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& append(char16_t srcChar); - - /** - * Append the code point `srcChar` to the UnicodeString object. - * @param srcChar the code point to append - * @return a reference to this - * @stable ICU 2.0 - */ - UnicodeString& append(UChar32 srcChar); - - - /* Insert operations */ - - /** - * Insert the characters in `srcText` in the range - * [`srcStart`, `srcStart + srcLength`) into the UnicodeString - * object at offset `start`. `srcText` is not modified. - * @param start the offset where the insertion begins - * @param srcText the source for the new characters - * @param srcStart the offset into `srcText` where new characters - * will be obtained - * @param srcLength the number of characters in `srcText` in - * the insert string - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& insert(int32_t start, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength); - - /** - * Insert the characters in `srcText` into the UnicodeString object - * at offset `start`. `srcText` is not modified. - * @param start the offset where the insertion begins - * @param srcText the source for the new characters - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& insert(int32_t start, - const UnicodeString& srcText); - - /** - * Insert the characters in `srcChars` in the range - * [`srcStart`, `srcStart + srcLength`) into the UnicodeString - * object at offset `start`. `srcChars` is not modified. - * @param start the offset at which the insertion begins - * @param srcChars the source for the new characters - * @param srcStart the offset into `srcChars` where new characters - * will be obtained - * @param srcLength the number of characters in `srcChars` - * in the insert string - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& insert(int32_t start, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength); - - /** - * Insert the characters in `srcChars` into the UnicodeString object - * at offset `start`. `srcChars` is not modified. - * @param start the offset where the insertion begins - * @param srcChars the source for the new characters - * @param srcLength the number of Unicode characters in srcChars. - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& insert(int32_t start, - ConstChar16Ptr srcChars, - int32_t srcLength); - - /** - * Insert the code unit `srcChar` into the UnicodeString object at - * offset `start`. - * @param start the offset at which the insertion occurs - * @param srcChar the code unit to insert - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& insert(int32_t start, - char16_t srcChar); - - /** - * Insert the code point `srcChar` into the UnicodeString object at - * offset `start`. - * @param start the offset at which the insertion occurs - * @param srcChar the code point to insert - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& insert(int32_t start, - UChar32 srcChar); - - - /* Replace operations */ - - /** - * Replace the characters in the range - * [`start`, `start + length`) with the characters in - * `srcText` in the range - * [`srcStart`, `srcStart + srcLength`). - * `srcText` is not modified. - * @param start the offset at which the replace operation begins - * @param length the number of characters to replace. The character at - * `start + length` is not modified. - * @param srcText the source for the new characters - * @param srcStart the offset into `srcText` where new characters - * will be obtained - * @param srcLength the number of characters in `srcText` in - * the replace string - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& replace(int32_t start, - int32_t length, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength); - - /** - * Replace the characters in the range - * [`start`, `start + length`) - * with the characters in `srcText`. `srcText` is - * not modified. - * @param start the offset at which the replace operation begins - * @param length the number of characters to replace. The character at - * `start + length` is not modified. - * @param srcText the source for the new characters - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& replace(int32_t start, - int32_t length, - const UnicodeString& srcText); - - /** - * Replace the characters in the range - * [`start`, `start + length`) with the characters in - * `srcChars` in the range - * [`srcStart`, `srcStart + srcLength`). `srcChars` - * is not modified. - * @param start the offset at which the replace operation begins - * @param length the number of characters to replace. The character at - * `start + length` is not modified. - * @param srcChars the source for the new characters - * @param srcStart the offset into `srcChars` where new characters - * will be obtained - * @param srcLength the number of characters in `srcChars` - * in the replace string - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& replace(int32_t start, - int32_t length, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength); - - /** - * Replace the characters in the range - * [`start`, `start + length`) with the characters in - * `srcChars`. `srcChars` is not modified. - * @param start the offset at which the replace operation begins - * @param length number of characters to replace. The character at - * `start + length` is not modified. - * @param srcChars the source for the new characters - * @param srcLength the number of Unicode characters in srcChars - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& replace(int32_t start, - int32_t length, - ConstChar16Ptr srcChars, - int32_t srcLength); - - /** - * Replace the characters in the range - * [`start`, `start + length`) with the code unit - * `srcChar`. - * @param start the offset at which the replace operation begins - * @param length the number of characters to replace. The character at - * `start + length` is not modified. - * @param srcChar the new code unit - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& replace(int32_t start, - int32_t length, - char16_t srcChar); - - /** - * Replace the characters in the range - * [`start`, `start + length`) with the code point - * `srcChar`. - * @param start the offset at which the replace operation begins - * @param length the number of characters to replace. The character at - * `start + length` is not modified. - * @param srcChar the new code point - * @return a reference to this - * @stable ICU 2.0 - */ - UnicodeString& replace(int32_t start, int32_t length, UChar32 srcChar); - - /** - * Replace the characters in the range [`start`, `limit`) - * with the characters in `srcText`. `srcText` is not modified. - * @param start the offset at which the replace operation begins - * @param limit the offset immediately following the replace range - * @param srcText the source for the new characters - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& replaceBetween(int32_t start, - int32_t limit, - const UnicodeString& srcText); - - /** - * Replace the characters in the range [`start`, `limit`) - * with the characters in `srcText` in the range - * [`srcStart`, `srcLimit`). `srcText` is not modified. - * @param start the offset at which the replace operation begins - * @param limit the offset immediately following the replace range - * @param srcText the source for the new characters - * @param srcStart the offset into `srcChars` where new characters - * will be obtained - * @param srcLimit the offset immediately following the range to copy - * in `srcText` - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& replaceBetween(int32_t start, - int32_t limit, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLimit); - - /** - * Replace a substring of this object with the given text. - * @param start the beginning index, inclusive; `0 <= start <= limit`. - * @param limit the ending index, exclusive; `start <= limit <= length()`. - * @param text the text to replace characters `start` to `limit - 1` - * @stable ICU 2.0 - */ - virtual void handleReplaceBetween(int32_t start, - int32_t limit, - const UnicodeString& text) override; - - /** - * Replaceable API - * @return true if it has MetaData - * @stable ICU 2.4 - */ - virtual UBool hasMetaData() const override; - - /** - * Copy a substring of this object, retaining attribute (out-of-band) - * information. This method is used to duplicate or reorder substrings. - * The destination index must not overlap the source range. - * - * @param start the beginning index, inclusive; `0 <= start <= limit`. - * @param limit the ending index, exclusive; `start <= limit <= length()`. - * @param dest the destination index. The characters from - * `start..limit-1` will be copied to `dest`. - * Implementations of this method may assume that `dest <= start || - * dest >= limit`. - * @stable ICU 2.0 - */ - virtual void copy(int32_t start, int32_t limit, int32_t dest) override; - - /* Search and replace operations */ - - /** - * Replace all occurrences of characters in oldText with the characters - * in newText - * @param oldText the text containing the search text - * @param newText the text containing the replacement text - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& findAndReplace(const UnicodeString& oldText, - const UnicodeString& newText); - - /** - * Replace all occurrences of characters in oldText with characters - * in newText - * in the range [`start`, `start + length`). - * @param start the start of the range in which replace will performed - * @param length the length of the range in which replace will be performed - * @param oldText the text containing the search text - * @param newText the text containing the replacement text - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& findAndReplace(int32_t start, - int32_t length, - const UnicodeString& oldText, - const UnicodeString& newText); - - /** - * Replace all occurrences of characters in oldText in the range - * [`oldStart`, `oldStart + oldLength`) with the characters - * in newText in the range - * [`newStart`, `newStart + newLength`) - * in the range [`start`, `start + length`). - * @param start the start of the range in which replace will performed - * @param length the length of the range in which replace will be performed - * @param oldText the text containing the search text - * @param oldStart the start of the search range in `oldText` - * @param oldLength the length of the search range in `oldText` - * @param newText the text containing the replacement text - * @param newStart the start of the replacement range in `newText` - * @param newLength the length of the replacement range in `newText` - * @return a reference to this - * @stable ICU 2.0 - */ - UnicodeString& findAndReplace(int32_t start, - int32_t length, - const UnicodeString& oldText, - int32_t oldStart, - int32_t oldLength, - const UnicodeString& newText, - int32_t newStart, - int32_t newLength); - - - /* Remove operations */ - - /** - * Removes all characters from the UnicodeString object and clears the bogus flag. - * This is the UnicodeString equivalent of std::string’s clear(). - * - * @return a reference to this - * @see setToBogus - * @stable ICU 2.0 - */ - inline UnicodeString& remove(); - - /** - * Remove the characters in the range - * [`start`, `start + length`) from the UnicodeString object. - * @param start the offset of the first character to remove - * @param length the number of characters to remove - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& remove(int32_t start, - int32_t length = (int32_t)INT32_MAX); - - /** - * Remove the characters in the range - * [`start`, `limit`) from the UnicodeString object. - * @param start the offset of the first character to remove - * @param limit the offset immediately following the range to remove - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& removeBetween(int32_t start, - int32_t limit = (int32_t)INT32_MAX); - - /** - * Retain only the characters in the range - * [`start`, `limit`) from the UnicodeString object. - * Removes characters before `start` and at and after `limit`. - * @param start the offset of the first character to retain - * @param limit the offset immediately following the range to retain - * @return a reference to this - * @stable ICU 4.4 - */ - inline UnicodeString &retainBetween(int32_t start, int32_t limit = INT32_MAX); - - /* Length operations */ - - /** - * Pad the start of this UnicodeString with the character `padChar`. - * If the length of this UnicodeString is less than targetLength, - * length() - targetLength copies of padChar will be added to the - * beginning of this UnicodeString. - * @param targetLength the desired length of the string - * @param padChar the character to use for padding. Defaults to - * space (U+0020) - * @return true if the text was padded, false otherwise. - * @stable ICU 2.0 - */ - UBool padLeading(int32_t targetLength, - char16_t padChar = 0x0020); - - /** - * Pad the end of this UnicodeString with the character `padChar`. - * If the length of this UnicodeString is less than targetLength, - * length() - targetLength copies of padChar will be added to the - * end of this UnicodeString. - * @param targetLength the desired length of the string - * @param padChar the character to use for padding. Defaults to - * space (U+0020) - * @return true if the text was padded, false otherwise. - * @stable ICU 2.0 - */ - UBool padTrailing(int32_t targetLength, - char16_t padChar = 0x0020); - - /** - * Truncate this UnicodeString to the `targetLength`. - * @param targetLength the desired length of this UnicodeString. - * @return true if the text was truncated, false otherwise - * @stable ICU 2.0 - */ - inline UBool truncate(int32_t targetLength); - - /** - * Trims leading and trailing whitespace from this UnicodeString. - * @return a reference to this - * @stable ICU 2.0 - */ - UnicodeString& trim(void); - - - /* Miscellaneous operations */ - - /** - * Reverse this UnicodeString in place. - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& reverse(void); - - /** - * Reverse the range [`start`, `start + length`) in - * this UnicodeString. - * @param start the start of the range to reverse - * @param length the number of characters to to reverse - * @return a reference to this - * @stable ICU 2.0 - */ - inline UnicodeString& reverse(int32_t start, - int32_t length); - - /** - * Convert the characters in this to UPPER CASE following the conventions of - * the default locale. - * @return A reference to this. - * @stable ICU 2.0 - */ - UnicodeString& toUpper(void); - - /** - * Convert the characters in this to UPPER CASE following the conventions of - * a specific locale. - * @param locale The locale containing the conventions to use. - * @return A reference to this. - * @stable ICU 2.0 - */ - UnicodeString& toUpper(const Locale& locale); - - /** - * Convert the characters in this to lower case following the conventions of - * the default locale. - * @return A reference to this. - * @stable ICU 2.0 - */ - UnicodeString& toLower(void); - - /** - * Convert the characters in this to lower case following the conventions of - * a specific locale. - * @param locale The locale containing the conventions to use. - * @return A reference to this. - * @stable ICU 2.0 - */ - UnicodeString& toLower(const Locale& locale); - -#if !UCONFIG_NO_BREAK_ITERATION - - /** - * Titlecase this string, convenience function using the default locale. - * - * Casing is locale-dependent and context-sensitive. - * Titlecasing uses a break iterator to find the first characters of words - * that are to be titlecased. It titlecases those characters and lowercases - * all others. - * - * The titlecase break iterator can be provided to customize for arbitrary - * styles, using rules and dictionaries beyond the standard iterators. - * It may be more efficient to always provide an iterator to avoid - * opening and closing one for each string. - * The standard titlecase iterator for the root locale implements the - * algorithm of Unicode TR 21. - * - * This function uses only the setText(), first() and next() methods of the - * provided break iterator. - * - * @param titleIter A break iterator to find the first characters of words - * that are to be titlecased. - * If none is provided (0), then a standard titlecase - * break iterator is opened. - * Otherwise the provided iterator is set to the string's text. - * @return A reference to this. - * @stable ICU 2.1 - */ - UnicodeString &toTitle(BreakIterator *titleIter); - - /** - * Titlecase this string. - * - * Casing is locale-dependent and context-sensitive. - * Titlecasing uses a break iterator to find the first characters of words - * that are to be titlecased. It titlecases those characters and lowercases - * all others. - * - * The titlecase break iterator can be provided to customize for arbitrary - * styles, using rules and dictionaries beyond the standard iterators. - * It may be more efficient to always provide an iterator to avoid - * opening and closing one for each string. - * The standard titlecase iterator for the root locale implements the - * algorithm of Unicode TR 21. - * - * This function uses only the setText(), first() and next() methods of the - * provided break iterator. - * - * @param titleIter A break iterator to find the first characters of words - * that are to be titlecased. - * If none is provided (0), then a standard titlecase - * break iterator is opened. - * Otherwise the provided iterator is set to the string's text. - * @param locale The locale to consider. - * @return A reference to this. - * @stable ICU 2.1 - */ - UnicodeString &toTitle(BreakIterator *titleIter, const Locale &locale); - - /** - * Titlecase this string, with options. - * - * Casing is locale-dependent and context-sensitive. - * Titlecasing uses a break iterator to find the first characters of words - * that are to be titlecased. It titlecases those characters and lowercases - * all others. (This can be modified with options.) - * - * The titlecase break iterator can be provided to customize for arbitrary - * styles, using rules and dictionaries beyond the standard iterators. - * It may be more efficient to always provide an iterator to avoid - * opening and closing one for each string. - * The standard titlecase iterator for the root locale implements the - * algorithm of Unicode TR 21. - * - * This function uses only the setText(), first() and next() methods of the - * provided break iterator. - * - * @param titleIter A break iterator to find the first characters of words - * that are to be titlecased. - * If none is provided (0), then a standard titlecase - * break iterator is opened. - * Otherwise the provided iterator is set to the string's text. - * @param locale The locale to consider. - * @param options Options bit set, usually 0. See U_TITLECASE_NO_LOWERCASE, - * U_TITLECASE_NO_BREAK_ADJUSTMENT, U_TITLECASE_ADJUST_TO_CASED, - * U_TITLECASE_WHOLE_STRING, U_TITLECASE_SENTENCES. - * @return A reference to this. - * @stable ICU 3.8 - */ - UnicodeString &toTitle(BreakIterator *titleIter, const Locale &locale, uint32_t options); - -#endif - - /** - * Case-folds the characters in this string. - * - * Case-folding is locale-independent and not context-sensitive, - * but there is an option for whether to include or exclude mappings for dotted I - * and dotless i that are marked with 'T' in CaseFolding.txt. - * - * The result may be longer or shorter than the original. - * - * @param options Either U_FOLD_CASE_DEFAULT or U_FOLD_CASE_EXCLUDE_SPECIAL_I - * @return A reference to this. - * @stable ICU 2.0 - */ - UnicodeString &foldCase(uint32_t options=0 /*U_FOLD_CASE_DEFAULT*/); - - //======================================== - // Access to the internal buffer - //======================================== - - /** - * Get a read/write pointer to the internal buffer. - * The buffer is guaranteed to be large enough for at least minCapacity char16_ts, - * writable, and is still owned by the UnicodeString object. - * Calls to getBuffer(minCapacity) must not be nested, and - * must be matched with calls to releaseBuffer(newLength). - * If the string buffer was read-only or shared, - * then it will be reallocated and copied. - * - * An attempted nested call will return 0, and will not further modify the - * state of the UnicodeString object. - * It also returns 0 if the string is bogus. - * - * The actual capacity of the string buffer may be larger than minCapacity. - * getCapacity() returns the actual capacity. - * For many operations, the full capacity should be used to avoid reallocations. - * - * While the buffer is "open" between getBuffer(minCapacity) - * and releaseBuffer(newLength), the following applies: - * - The string length is set to 0. - * - Any read API call on the UnicodeString object will behave like on a 0-length string. - * - Any write API call on the UnicodeString object is disallowed and will have no effect. - * - You can read from and write to the returned buffer. - * - The previous string contents will still be in the buffer; - * if you want to use it, then you need to call length() before getBuffer(minCapacity). - * If the length() was greater than minCapacity, then any contents after minCapacity - * may be lost. - * The buffer contents is not NUL-terminated by getBuffer(). - * If length() < getCapacity() then you can terminate it by writing a NUL - * at index length(). - * - You must call releaseBuffer(newLength) before and in order to - * return to normal UnicodeString operation. - * - * @param minCapacity the minimum number of char16_ts that are to be available - * in the buffer, starting at the returned pointer; - * default to the current string capacity if minCapacity==-1 - * @return a writable pointer to the internal string buffer, - * or nullptr if an error occurs (nested calls, out of memory) - * - * @see releaseBuffer - * @see getTerminatedBuffer() - * @stable ICU 2.0 - */ - char16_t *getBuffer(int32_t minCapacity); - - /** - * Release a read/write buffer on a UnicodeString object with an - * "open" getBuffer(minCapacity). - * This function must be called in a matched pair with getBuffer(minCapacity). - * releaseBuffer(newLength) must be called if and only if a getBuffer(minCapacity) is "open". - * - * It will set the string length to newLength, at most to the current capacity. - * If newLength==-1 then it will set the length according to the - * first NUL in the buffer, or to the capacity if there is no NUL. - * - * After calling releaseBuffer(newLength) the UnicodeString is back to normal operation. - * - * @param newLength the new length of the UnicodeString object; - * defaults to the current capacity if newLength is greater than that; - * if newLength==-1, it defaults to u_strlen(buffer) but not more than - * the current capacity of the string - * - * @see getBuffer(int32_t minCapacity) - * @stable ICU 2.0 - */ - void releaseBuffer(int32_t newLength=-1); - - /** - * Get a read-only pointer to the internal buffer. - * This can be called at any time on a valid UnicodeString. - * - * It returns 0 if the string is bogus, or - * during an "open" getBuffer(minCapacity). - * - * It can be called as many times as desired. - * The pointer that it returns will remain valid until the UnicodeString object is modified, - * at which time the pointer is semantically invalidated and must not be used any more. - * - * The capacity of the buffer can be determined with getCapacity(). - * The part after length() may or may not be initialized and valid, - * depending on the history of the UnicodeString object. - * - * The buffer contents is (probably) not NUL-terminated. - * You can check if it is with - * `(s.length() < s.getCapacity() && buffer[s.length()]==0)`. - * (See getTerminatedBuffer().) - * - * The buffer may reside in read-only memory. Its contents must not - * be modified. - * - * @return a read-only pointer to the internal string buffer, - * or nullptr if the string is empty or bogus - * - * @see getBuffer(int32_t minCapacity) - * @see getTerminatedBuffer() - * @stable ICU 2.0 - */ - inline const char16_t *getBuffer() const; - - /** - * Get a read-only pointer to the internal buffer, - * making sure that it is NUL-terminated. - * This can be called at any time on a valid UnicodeString. - * - * It returns 0 if the string is bogus, or - * during an "open" getBuffer(minCapacity), or if the buffer cannot - * be NUL-terminated (because memory allocation failed). - * - * It can be called as many times as desired. - * The pointer that it returns will remain valid until the UnicodeString object is modified, - * at which time the pointer is semantically invalidated and must not be used any more. - * - * The capacity of the buffer can be determined with getCapacity(). - * The part after length()+1 may or may not be initialized and valid, - * depending on the history of the UnicodeString object. - * - * The buffer contents is guaranteed to be NUL-terminated. - * getTerminatedBuffer() may reallocate the buffer if a terminating NUL - * is written. - * For this reason, this function is not const, unlike getBuffer(). - * Note that a UnicodeString may also contain NUL characters as part of its contents. - * - * The buffer may reside in read-only memory. Its contents must not - * be modified. - * - * @return a read-only pointer to the internal string buffer, - * or 0 if the string is empty or bogus - * - * @see getBuffer(int32_t minCapacity) - * @see getBuffer() - * @stable ICU 2.2 - */ - const char16_t *getTerminatedBuffer(); - - //======================================== - // Constructors - //======================================== - - /** Construct an empty UnicodeString. - * @stable ICU 2.0 - */ - inline UnicodeString(); - - /** - * Construct a UnicodeString with capacity to hold `capacity` char16_ts - * @param capacity the number of char16_ts this UnicodeString should hold - * before a resize is necessary; if count is greater than 0 and count - * code points c take up more space than capacity, then capacity is adjusted - * accordingly. - * @param c is used to initially fill the string - * @param count specifies how many code points c are to be written in the - * string - * @stable ICU 2.0 - */ - UnicodeString(int32_t capacity, UChar32 c, int32_t count); - - /** - * Single char16_t (code unit) constructor. - * - * It is recommended to mark this constructor "explicit" by - * `-DUNISTR_FROM_CHAR_EXPLICIT=explicit` - * on the compiler command line or similar. - * @param ch the character to place in the UnicodeString - * @stable ICU 2.0 - */ - UNISTR_FROM_CHAR_EXPLICIT UnicodeString(char16_t ch); - - /** - * Single UChar32 (code point) constructor. - * - * It is recommended to mark this constructor "explicit" by - * `-DUNISTR_FROM_CHAR_EXPLICIT=explicit` - * on the compiler command line or similar. - * @param ch the character to place in the UnicodeString - * @stable ICU 2.0 - */ - UNISTR_FROM_CHAR_EXPLICIT UnicodeString(UChar32 ch); - - /** - * char16_t* constructor. - * - * It is recommended to mark this constructor "explicit" by - * `-DUNISTR_FROM_STRING_EXPLICIT=explicit` - * on the compiler command line or similar. - * @param text The characters to place in the UnicodeString. `text` - * must be NULL (U+0000) terminated. - * @stable ICU 2.0 - */ - UNISTR_FROM_STRING_EXPLICIT UnicodeString(const char16_t *text); - -#if !U_CHAR16_IS_TYPEDEF - /** - * uint16_t * constructor. - * Delegates to UnicodeString(const char16_t *). - * - * It is recommended to mark this constructor "explicit" by - * `-DUNISTR_FROM_STRING_EXPLICIT=explicit` - * on the compiler command line or similar. - * @param text NUL-terminated UTF-16 string - * @stable ICU 59 - */ - UNISTR_FROM_STRING_EXPLICIT UnicodeString(const uint16_t *text) : - UnicodeString(ConstChar16Ptr(text)) {} -#endif - -#if U_SIZEOF_WCHAR_T==2 || defined(U_IN_DOXYGEN) - /** - * wchar_t * constructor. - * (Only defined if U_SIZEOF_WCHAR_T==2.) - * Delegates to UnicodeString(const char16_t *). - * - * It is recommended to mark this constructor "explicit" by - * `-DUNISTR_FROM_STRING_EXPLICIT=explicit` - * on the compiler command line or similar. - * @param text NUL-terminated UTF-16 string - * @stable ICU 59 - */ - UNISTR_FROM_STRING_EXPLICIT UnicodeString(const wchar_t *text) : - UnicodeString(ConstChar16Ptr(text)) {} -#endif - - /** - * nullptr_t constructor. - * Effectively the same as the default constructor, makes an empty string object. - * - * It is recommended to mark this constructor "explicit" by - * `-DUNISTR_FROM_STRING_EXPLICIT=explicit` - * on the compiler command line or similar. - * @param text nullptr - * @stable ICU 59 - */ - UNISTR_FROM_STRING_EXPLICIT inline UnicodeString(const std::nullptr_t text); - - /** - * char16_t* constructor. - * @param text The characters to place in the UnicodeString. - * @param textLength The number of Unicode characters in `text` - * to copy. - * @stable ICU 2.0 - */ - UnicodeString(const char16_t *text, - int32_t textLength); - -#if !U_CHAR16_IS_TYPEDEF - /** - * uint16_t * constructor. - * Delegates to UnicodeString(const char16_t *, int32_t). - * @param text UTF-16 string - * @param textLength string length - * @stable ICU 59 - */ - UnicodeString(const uint16_t *text, int32_t textLength) : - UnicodeString(ConstChar16Ptr(text), textLength) {} -#endif - -#if U_SIZEOF_WCHAR_T==2 || defined(U_IN_DOXYGEN) - /** - * wchar_t * constructor. - * (Only defined if U_SIZEOF_WCHAR_T==2.) - * Delegates to UnicodeString(const char16_t *, int32_t). - * @param text NUL-terminated UTF-16 string - * @param textLength string length - * @stable ICU 59 - */ - UnicodeString(const wchar_t *text, int32_t textLength) : - UnicodeString(ConstChar16Ptr(text), textLength) {} -#endif - - /** - * nullptr_t constructor. - * Effectively the same as the default constructor, makes an empty string object. - * @param text nullptr - * @param textLength ignored - * @stable ICU 59 - */ - inline UnicodeString(const std::nullptr_t text, int32_t textLength); - - /** - * Readonly-aliasing char16_t* constructor. - * The text will be used for the UnicodeString object, but - * it will not be released when the UnicodeString is destroyed. - * This has copy-on-write semantics: - * When the string is modified, then the buffer is first copied into - * newly allocated memory. - * The aliased buffer is never modified. - * - * In an assignment to another UnicodeString, when using the copy constructor - * or the assignment operator, the text will be copied. - * When using fastCopyFrom(), the text will be aliased again, - * so that both strings then alias the same readonly-text. - * - * @param isTerminated specifies if `text` is `NUL`-terminated. - * This must be true if `textLength==-1`. - * @param text The characters to alias for the UnicodeString. - * @param textLength The number of Unicode characters in `text` to alias. - * If -1, then this constructor will determine the length - * by calling `u_strlen()`. - * @stable ICU 2.0 - */ - UnicodeString(UBool isTerminated, - ConstChar16Ptr text, - int32_t textLength); - - /** - * Writable-aliasing char16_t* constructor. - * The text will be used for the UnicodeString object, but - * it will not be released when the UnicodeString is destroyed. - * This has write-through semantics: - * For as long as the capacity of the buffer is sufficient, write operations - * will directly affect the buffer. When more capacity is necessary, then - * a new buffer will be allocated and the contents copied as with regularly - * constructed strings. - * In an assignment to another UnicodeString, the buffer will be copied. - * The extract(Char16Ptr dst) function detects whether the dst pointer is the same - * as the string buffer itself and will in this case not copy the contents. - * - * @param buffer The characters to alias for the UnicodeString. - * @param buffLength The number of Unicode characters in `buffer` to alias. - * @param buffCapacity The size of `buffer` in char16_ts. - * @stable ICU 2.0 - */ - UnicodeString(char16_t *buffer, int32_t buffLength, int32_t buffCapacity); - -#if !U_CHAR16_IS_TYPEDEF - /** - * Writable-aliasing uint16_t * constructor. - * Delegates to UnicodeString(const char16_t *, int32_t, int32_t). - * @param buffer writable buffer of/for UTF-16 text - * @param buffLength length of the current buffer contents - * @param buffCapacity buffer capacity - * @stable ICU 59 - */ - UnicodeString(uint16_t *buffer, int32_t buffLength, int32_t buffCapacity) : - UnicodeString(Char16Ptr(buffer), buffLength, buffCapacity) {} -#endif - -#if U_SIZEOF_WCHAR_T==2 || defined(U_IN_DOXYGEN) - /** - * Writable-aliasing wchar_t * constructor. - * (Only defined if U_SIZEOF_WCHAR_T==2.) - * Delegates to UnicodeString(const char16_t *, int32_t, int32_t). - * @param buffer writable buffer of/for UTF-16 text - * @param buffLength length of the current buffer contents - * @param buffCapacity buffer capacity - * @stable ICU 59 - */ - UnicodeString(wchar_t *buffer, int32_t buffLength, int32_t buffCapacity) : - UnicodeString(Char16Ptr(buffer), buffLength, buffCapacity) {} -#endif - - /** - * Writable-aliasing nullptr_t constructor. - * Effectively the same as the default constructor, makes an empty string object. - * @param buffer nullptr - * @param buffLength ignored - * @param buffCapacity ignored - * @stable ICU 59 - */ - inline UnicodeString(std::nullptr_t buffer, int32_t buffLength, int32_t buffCapacity); - -#if U_CHARSET_IS_UTF8 || !UCONFIG_NO_CONVERSION - - /** - * char* constructor. - * Uses the default converter (and thus depends on the ICU conversion code) - * unless U_CHARSET_IS_UTF8 is set to 1. - * - * For ASCII (really "invariant character") strings it is more efficient to use - * the constructor that takes a US_INV (for its enum EInvariant). - * For ASCII (invariant-character) string literals, see UNICODE_STRING and - * UNICODE_STRING_SIMPLE. - * - * It is recommended to mark this constructor "explicit" by - * `-DUNISTR_FROM_STRING_EXPLICIT=explicit` - * on the compiler command line or similar. - * @param codepageData an array of bytes, null-terminated, - * in the platform's default codepage. - * @stable ICU 2.0 - * @see UNICODE_STRING - * @see UNICODE_STRING_SIMPLE - */ - UNISTR_FROM_STRING_EXPLICIT UnicodeString(const char *codepageData); - - /** - * char* constructor. - * Uses the default converter (and thus depends on the ICU conversion code) - * unless U_CHARSET_IS_UTF8 is set to 1. - * @param codepageData an array of bytes in the platform's default codepage. - * @param dataLength The number of bytes in `codepageData`. - * @stable ICU 2.0 - */ - UnicodeString(const char *codepageData, int32_t dataLength); - -#endif - -#if !UCONFIG_NO_CONVERSION - - /** - * char* constructor. - * @param codepageData an array of bytes, null-terminated - * @param codepage the encoding of `codepageData`. The special - * value 0 for `codepage` indicates that the text is in the - * platform's default codepage. - * - * If `codepage` is an empty string (`""`), - * then a simple conversion is performed on the codepage-invariant - * subset ("invariant characters") of the platform encoding. See utypes.h. - * Recommendation: For invariant-character strings use the constructor - * UnicodeString(const char *src, int32_t length, enum EInvariant inv) - * because it avoids object code dependencies of UnicodeString on - * the conversion code. - * - * @stable ICU 2.0 - */ - UnicodeString(const char *codepageData, const char *codepage); - - /** - * char* constructor. - * @param codepageData an array of bytes. - * @param dataLength The number of bytes in `codepageData`. - * @param codepage the encoding of `codepageData`. The special - * value 0 for `codepage` indicates that the text is in the - * platform's default codepage. - * If `codepage` is an empty string (`""`), - * then a simple conversion is performed on the codepage-invariant - * subset ("invariant characters") of the platform encoding. See utypes.h. - * Recommendation: For invariant-character strings use the constructor - * UnicodeString(const char *src, int32_t length, enum EInvariant inv) - * because it avoids object code dependencies of UnicodeString on - * the conversion code. - * - * @stable ICU 2.0 - */ - UnicodeString(const char *codepageData, int32_t dataLength, const char *codepage); - - /** - * char * / UConverter constructor. - * This constructor uses an existing UConverter object to - * convert the codepage string to Unicode and construct a UnicodeString - * from that. - * - * The converter is reset at first. - * If the error code indicates a failure before this constructor is called, - * or if an error occurs during conversion or construction, - * then the string will be bogus. - * - * This function avoids the overhead of opening and closing a converter if - * multiple strings are constructed. - * - * @param src input codepage string - * @param srcLength length of the input string, can be -1 for NUL-terminated strings - * @param cnv converter object (ucnv_resetToUnicode() will be called), - * can be NULL for the default converter - * @param errorCode normal ICU error code - * @stable ICU 2.0 - */ - UnicodeString( - const char *src, int32_t srcLength, - UConverter *cnv, - UErrorCode &errorCode); - -#endif - - /** - * Constructs a Unicode string from an invariant-character char * string. - * About invariant characters see utypes.h. - * This constructor has no runtime dependency on conversion code and is - * therefore recommended over ones taking a charset name string - * (where the empty string "" indicates invariant-character conversion). - * - * Use the macro US_INV as the third, signature-distinguishing parameter. - * - * For example: - * \code - * void fn(const char *s) { - * UnicodeString ustr(s, -1, US_INV); - * // use ustr ... - * } - * \endcode - * @param src String using only invariant characters. - * @param textLength Length of src, or -1 if NUL-terminated. - * @param inv Signature-distinguishing parameter, use US_INV. - * - * @see US_INV - * @stable ICU 3.2 - */ - UnicodeString(const char *src, int32_t textLength, enum EInvariant inv); - - - /** - * Copy constructor. - * - * Starting with ICU 2.4, the assignment operator and the copy constructor - * allocate a new buffer and copy the buffer contents even for readonly aliases. - * By contrast, the fastCopyFrom() function implements the old, - * more efficient but less safe behavior - * of making this string also a readonly alias to the same buffer. - * - * If the source object has an "open" buffer from getBuffer(minCapacity), - * then the copy is an empty string. - * - * @param that The UnicodeString object to copy. - * @stable ICU 2.0 - * @see fastCopyFrom - */ - UnicodeString(const UnicodeString& that); - - /** - * 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 - */ - UnicodeString(UnicodeString &&src) U_NOEXCEPT; - - /** - * 'Substring' constructor from tail of source string. - * @param src The UnicodeString object to copy. - * @param srcStart The offset into `src` at which to start copying. - * @stable ICU 2.2 - */ - UnicodeString(const UnicodeString& src, int32_t srcStart); - - /** - * 'Substring' constructor from subrange of source string. - * @param src The UnicodeString object to copy. - * @param srcStart The offset into `src` at which to start copying. - * @param srcLength The number of characters from `src` to copy. - * @stable ICU 2.2 - */ - UnicodeString(const UnicodeString& src, int32_t srcStart, int32_t srcLength); - - /** - * Clone this object, an instance of a subclass of Replaceable. - * Clones can be used concurrently in multiple threads. - * If a subclass does not implement clone(), or if an error occurs, - * then NULL is returned. - * The caller must delete the clone. - * - * @return a clone of this object - * - * @see Replaceable::clone - * @see getDynamicClassID - * @stable ICU 2.6 - */ - virtual UnicodeString *clone() const override; - - /** Destructor. - * @stable ICU 2.0 - */ - virtual ~UnicodeString(); - - /** - * Create a UnicodeString from a UTF-8 string. - * Illegal input is replaced with U+FFFD. Otherwise, errors result in a bogus string. - * Calls u_strFromUTF8WithSub(). - * - * @param utf8 UTF-8 input string. - * Note that a StringPiece can be implicitly constructed - * from a std::string or a NUL-terminated const char * string. - * @return A UnicodeString with equivalent UTF-16 contents. - * @see toUTF8 - * @see toUTF8String - * @stable ICU 4.2 - */ - static UnicodeString fromUTF8(StringPiece utf8); - - /** - * Create a UnicodeString from a UTF-32 string. - * Illegal input is replaced with U+FFFD. Otherwise, errors result in a bogus string. - * Calls u_strFromUTF32WithSub(). - * - * @param utf32 UTF-32 input string. Must not be NULL. - * @param length Length of the input string, or -1 if NUL-terminated. - * @return A UnicodeString with equivalent UTF-16 contents. - * @see toUTF32 - * @stable ICU 4.2 - */ - static UnicodeString fromUTF32(const UChar32 *utf32, int32_t length); - - /* Miscellaneous operations */ - - /** - * Unescape a string of characters and return a string containing - * the result. The following escape sequences are recognized: - * - * \\uhhhh 4 hex digits; h in [0-9A-Fa-f] - * \\Uhhhhhhhh 8 hex digits - * \\xhh 1-2 hex digits - * \\ooo 1-3 octal digits; o in [0-7] - * \\cX control-X; X is masked with 0x1F - * - * as well as the standard ANSI C escapes: - * - * \\a => U+0007, \\b => U+0008, \\t => U+0009, \\n => U+000A, - * \\v => U+000B, \\f => U+000C, \\r => U+000D, \\e => U+001B, - * \\" => U+0022, \\' => U+0027, \\? => U+003F, \\\\ => U+005C - * - * Anything else following a backslash is generically escaped. For - * example, "[a\\-z]" returns "[a-z]". - * - * If an escape sequence is ill-formed, this method returns an empty - * string. An example of an ill-formed sequence is "\\u" followed by - * fewer than 4 hex digits. - * - * This function is similar to u_unescape() but not identical to it. - * The latter takes a source char*, so it does escape recognition - * and also invariant conversion. - * - * @return a string with backslash escapes interpreted, or an - * empty string on error. - * @see UnicodeString#unescapeAt() - * @see u_unescape() - * @see u_unescapeAt() - * @stable ICU 2.0 - */ - UnicodeString unescape() const; - - /** - * Unescape a single escape sequence and return the represented - * character. See unescape() for a listing of the recognized escape - * sequences. The character at offset-1 is assumed (without - * checking) to be a backslash. If the escape sequence is - * ill-formed, or the offset is out of range, U_SENTINEL=-1 is - * returned. - * - * @param offset an input output parameter. On input, it is the - * offset into this string where the escape sequence is located, - * after the initial backslash. On output, it is advanced after the - * last character parsed. On error, it is not advanced at all. - * @return the character represented by the escape sequence at - * offset, or U_SENTINEL=-1 on error. - * @see UnicodeString#unescape() - * @see u_unescape() - * @see u_unescapeAt() - * @stable ICU 2.0 - */ - UChar32 unescapeAt(int32_t &offset) const; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - * - * @stable ICU 2.2 - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - * - * @stable ICU 2.2 - */ - virtual UClassID getDynamicClassID() const override; - - //======================================== - // Implementation methods - //======================================== - -protected: - /** - * Implement Replaceable::getLength() (see jitterbug 1027). - * @stable ICU 2.4 - */ - virtual int32_t getLength() const override; - - /** - * The change in Replaceable to use virtual getCharAt() allows - * UnicodeString::charAt() to be inline again (see jitterbug 709). - * @stable ICU 2.4 - */ - virtual char16_t getCharAt(int32_t offset) const override; - - /** - * The change in Replaceable to use virtual getChar32At() allows - * UnicodeString::char32At() to be inline again (see jitterbug 709). - * @stable ICU 2.4 - */ - virtual UChar32 getChar32At(int32_t offset) const override; - -private: - // For char* constructors. Could be made public. - UnicodeString &setToUTF8(StringPiece utf8); - // For extract(char*). - // We could make a toUTF8(target, capacity, errorCode) public but not - // this version: New API will be cleaner if we make callers create substrings - // rather than having start+length on every method, - // and it should take a UErrorCode&. - int32_t - toUTF8(int32_t start, int32_t len, - char *target, int32_t capacity) const; - - /** - * Internal string contents comparison, called by operator==. - * Requires: this & text not bogus and have same lengths. - */ - UBool doEquals(const UnicodeString &text, int32_t len) const; - - inline int8_t - doCompare(int32_t start, - int32_t length, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) const; - - int8_t doCompare(int32_t start, - int32_t length, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength) const; - - inline int8_t - doCompareCodePointOrder(int32_t start, - int32_t length, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) const; - - int8_t doCompareCodePointOrder(int32_t start, - int32_t length, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength) const; - - inline int8_t - doCaseCompare(int32_t start, - int32_t length, - const UnicodeString &srcText, - int32_t srcStart, - int32_t srcLength, - uint32_t options) const; - - int8_t - doCaseCompare(int32_t start, - int32_t length, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength, - uint32_t options) const; - - int32_t doIndexOf(char16_t c, - int32_t start, - int32_t length) const; - - int32_t doIndexOf(UChar32 c, - int32_t start, - int32_t length) const; - - int32_t doLastIndexOf(char16_t c, - int32_t start, - int32_t length) const; - - int32_t doLastIndexOf(UChar32 c, - int32_t start, - int32_t length) const; - - void doExtract(int32_t start, - int32_t length, - char16_t *dst, - int32_t dstStart) const; - - inline void doExtract(int32_t start, - int32_t length, - UnicodeString& target) const; - - inline char16_t doCharAt(int32_t offset) const; - - UnicodeString& doReplace(int32_t start, - int32_t length, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength); - - UnicodeString& doReplace(int32_t start, - int32_t length, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength); - - UnicodeString& doAppend(const UnicodeString& src, int32_t srcStart, int32_t srcLength); - UnicodeString& doAppend(const char16_t *srcChars, int32_t srcStart, int32_t srcLength); - - UnicodeString& doReverse(int32_t start, - int32_t length); - - // calculate hash code - int32_t doHashCode(void) const; - - // get pointer to start of array - // these do not check for kOpenGetBuffer, unlike the public getBuffer() function - inline char16_t* getArrayStart(void); - inline const char16_t* getArrayStart(void) const; - - inline UBool hasShortLength() const; - inline int32_t getShortLength() const; - - // A UnicodeString object (not necessarily its current buffer) - // is writable unless it isBogus() or it has an "open" getBuffer(minCapacity). - inline UBool isWritable() const; - - // Is the current buffer writable? - inline UBool isBufferWritable() const; - - // None of the following does releaseArray(). - inline void setZeroLength(); - inline void setShortLength(int32_t len); - inline void setLength(int32_t len); - inline void setToEmpty(); - inline void setArray(char16_t *array, int32_t len, int32_t capacity); // sets length but not flags - - // allocate the array; result may be the stack buffer - // sets refCount to 1 if appropriate - // sets fArray, fCapacity, and flags - // sets length to 0 - // returns boolean for success or failure - UBool allocate(int32_t capacity); - - // release the array if owned - void releaseArray(void); - - // turn a bogus string into an empty one - void unBogus(); - - // implements assignment operator, copy constructor, and fastCopyFrom() - UnicodeString ©From(const UnicodeString &src, UBool fastCopy=false); - - // Copies just the fields without memory management. - void copyFieldsFrom(UnicodeString &src, UBool setSrcToBogus) U_NOEXCEPT; - - // Pin start and limit to acceptable values. - inline void pinIndex(int32_t& start) const; - inline void pinIndices(int32_t& start, - int32_t& length) const; - -#if !UCONFIG_NO_CONVERSION - - /* Internal extract() using UConverter. */ - int32_t doExtract(int32_t start, int32_t length, - char *dest, int32_t destCapacity, - UConverter *cnv, - UErrorCode &errorCode) const; - - /* - * Real constructor for converting from codepage data. - * It assumes that it is called with !fRefCounted. - * - * If `codepage==0`, then the default converter - * is used for the platform encoding. - * If `codepage` is an empty string (`""`), - * then a simple conversion is performed on the codepage-invariant - * subset ("invariant characters") of the platform encoding. See utypes.h. - */ - void doCodepageCreate(const char *codepageData, - int32_t dataLength, - const char *codepage); - - /* - * Worker function for creating a UnicodeString from - * a codepage string using a UConverter. - */ - void - doCodepageCreate(const char *codepageData, - int32_t dataLength, - UConverter *converter, - UErrorCode &status); - -#endif - - /* - * This function is called when write access to the array - * is necessary. - * - * We need to make a copy of the array if - * the buffer is read-only, or - * the buffer is refCounted (shared), and refCount>1, or - * the buffer is too small. - * - * Return false if memory could not be allocated. - */ - UBool cloneArrayIfNeeded(int32_t newCapacity = -1, - int32_t growCapacity = -1, - UBool doCopyArray = true, - int32_t **pBufferToDelete = 0, - UBool forceClone = false); - - /** - * Common function for UnicodeString case mappings. - * The stringCaseMapper has the same type UStringCaseMapper - * as in ustr_imp.h for ustrcase_map(). - */ - UnicodeString & - caseMap(int32_t caseLocale, uint32_t options, -#if !UCONFIG_NO_BREAK_ITERATION - BreakIterator *iter, -#endif - UStringCaseMapper *stringCaseMapper); - - // ref counting - void addRef(void); - int32_t removeRef(void); - int32_t refCount(void) const; - - // constants - enum { - /** - * Size of stack buffer for short strings. - * Must be at least U16_MAX_LENGTH for the single-code point constructor to work. - * @see UNISTR_OBJECT_SIZE - */ - US_STACKBUF_SIZE=(int32_t)(UNISTR_OBJECT_SIZE-sizeof(void *)-2)/U_SIZEOF_UCHAR, - kInvalidUChar=0xffff, // U+FFFF returned by charAt(invalid index) - kInvalidHashCode=0, // invalid hash code - kEmptyHashCode=1, // hash code for empty string - - // bit flag values for fLengthAndFlags - kIsBogus=1, // this string is bogus, i.e., not valid or NULL - kUsingStackBuffer=2,// using fUnion.fStackFields instead of fUnion.fFields - kRefCounted=4, // there is a refCount field before the characters in fArray - kBufferIsReadonly=8,// do not write to this buffer - kOpenGetBuffer=16, // getBuffer(minCapacity) was called (is "open"), - // and releaseBuffer(newLength) must be called - kAllStorageFlags=0x1f, - - kLengthShift=5, // remaining 11 bits for non-negative short length, or negative if long - kLength1=1<127; else undefined - int32_t fCapacity; // capacity of fArray (in char16_ts) - // array pointer last to minimize padding for machines with P128 data model - // or pointer sizes that are not a power of 2 - char16_t *fArray; // the Unicode data - } fFields; - } fUnion; -}; - -/** - * Create a new UnicodeString with the concatenation of two others. - * - * @param s1 The first string to be copied to the new one. - * @param s2 The second string to be copied to the new one, after s1. - * @return UnicodeString(s1).append(s2) - * @stable ICU 2.8 - */ -U_COMMON_API UnicodeString U_EXPORT2 -operator+ (const UnicodeString &s1, const UnicodeString &s2); - -//======================================== -// Inline members -//======================================== - -//======================================== -// Privates -//======================================== - -inline void -UnicodeString::pinIndex(int32_t& start) const -{ - // pin index - if(start < 0) { - start = 0; - } else if(start > length()) { - start = length(); - } -} - -inline void -UnicodeString::pinIndices(int32_t& start, - int32_t& _length) const -{ - // pin indices - int32_t len = length(); - if(start < 0) { - start = 0; - } else if(start > len) { - start = len; - } - if(_length < 0) { - _length = 0; - } else if(_length > (len - start)) { - _length = (len - start); - } -} - -inline char16_t* -UnicodeString::getArrayStart() { - return (fUnion.fFields.fLengthAndFlags&kUsingStackBuffer) ? - fUnion.fStackFields.fBuffer : fUnion.fFields.fArray; -} - -inline const char16_t* -UnicodeString::getArrayStart() const { - return (fUnion.fFields.fLengthAndFlags&kUsingStackBuffer) ? - fUnion.fStackFields.fBuffer : fUnion.fFields.fArray; -} - -//======================================== -// Default constructor -//======================================== - -inline -UnicodeString::UnicodeString() { - fUnion.fStackFields.fLengthAndFlags=kShortString; -} - -inline UnicodeString::UnicodeString(const std::nullptr_t /*text*/) { - fUnion.fStackFields.fLengthAndFlags=kShortString; -} - -inline UnicodeString::UnicodeString(const std::nullptr_t /*text*/, int32_t /*length*/) { - fUnion.fStackFields.fLengthAndFlags=kShortString; -} - -inline UnicodeString::UnicodeString(std::nullptr_t /*buffer*/, int32_t /*buffLength*/, int32_t /*buffCapacity*/) { - fUnion.fStackFields.fLengthAndFlags=kShortString; -} - -//======================================== -// Read-only implementation methods -//======================================== -inline UBool -UnicodeString::hasShortLength() const { - return fUnion.fFields.fLengthAndFlags>=0; -} - -inline int32_t -UnicodeString::getShortLength() const { - // fLengthAndFlags must be non-negative -> short length >= 0 - // and arithmetic or logical shift does not matter. - return fUnion.fFields.fLengthAndFlags>>kLengthShift; -} - -inline int32_t -UnicodeString::length() const { - return hasShortLength() ? getShortLength() : fUnion.fFields.fLength; -} - -inline int32_t -UnicodeString::getCapacity() const { - return (fUnion.fFields.fLengthAndFlags&kUsingStackBuffer) ? - US_STACKBUF_SIZE : fUnion.fFields.fCapacity; -} - -inline int32_t -UnicodeString::hashCode() const -{ return doHashCode(); } - -inline UBool -UnicodeString::isBogus() const -{ return (UBool)(fUnion.fFields.fLengthAndFlags & kIsBogus); } - -inline UBool -UnicodeString::isWritable() const -{ return (UBool)!(fUnion.fFields.fLengthAndFlags&(kOpenGetBuffer|kIsBogus)); } - -inline UBool -UnicodeString::isBufferWritable() const -{ - return (UBool)( - !(fUnion.fFields.fLengthAndFlags&(kOpenGetBuffer|kIsBogus|kBufferIsReadonly)) && - (!(fUnion.fFields.fLengthAndFlags&kRefCounted) || refCount()==1)); -} - -inline const char16_t * -UnicodeString::getBuffer() const { - if(fUnion.fFields.fLengthAndFlags&(kIsBogus|kOpenGetBuffer)) { - return nullptr; - } else if(fUnion.fFields.fLengthAndFlags&kUsingStackBuffer) { - return fUnion.fStackFields.fBuffer; - } else { - return fUnion.fFields.fArray; - } -} - -//======================================== -// Read-only alias methods -//======================================== -inline int8_t -UnicodeString::doCompare(int32_t start, - int32_t thisLength, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) const -{ - if(srcText.isBogus()) { - return (int8_t)!isBogus(); // 0 if both are bogus, 1 otherwise - } else { - srcText.pinIndices(srcStart, srcLength); - return doCompare(start, thisLength, srcText.getArrayStart(), srcStart, srcLength); - } -} - -inline bool -UnicodeString::operator== (const UnicodeString& text) const -{ - if(isBogus()) { - return text.isBogus(); - } else { - int32_t len = length(), textLength = text.length(); - return !text.isBogus() && len == textLength && doEquals(text, len); - } -} - -inline bool -UnicodeString::operator!= (const UnicodeString& text) const -{ return (! operator==(text)); } - -inline UBool -UnicodeString::operator> (const UnicodeString& text) const -{ return doCompare(0, length(), text, 0, text.length()) == 1; } - -inline UBool -UnicodeString::operator< (const UnicodeString& text) const -{ return doCompare(0, length(), text, 0, text.length()) == -1; } - -inline UBool -UnicodeString::operator>= (const UnicodeString& text) const -{ return doCompare(0, length(), text, 0, text.length()) != -1; } - -inline UBool -UnicodeString::operator<= (const UnicodeString& text) const -{ return doCompare(0, length(), text, 0, text.length()) != 1; } - -inline int8_t -UnicodeString::compare(const UnicodeString& text) const -{ return doCompare(0, length(), text, 0, text.length()); } - -inline int8_t -UnicodeString::compare(int32_t start, - int32_t _length, - const UnicodeString& srcText) const -{ return doCompare(start, _length, srcText, 0, srcText.length()); } - -inline int8_t -UnicodeString::compare(ConstChar16Ptr srcChars, - int32_t srcLength) const -{ return doCompare(0, length(), srcChars, 0, srcLength); } - -inline int8_t -UnicodeString::compare(int32_t start, - int32_t _length, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) const -{ return doCompare(start, _length, srcText, srcStart, srcLength); } - -inline int8_t -UnicodeString::compare(int32_t start, - int32_t _length, - const char16_t *srcChars) const -{ return doCompare(start, _length, srcChars, 0, _length); } - -inline int8_t -UnicodeString::compare(int32_t start, - int32_t _length, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength) const -{ return doCompare(start, _length, srcChars, srcStart, srcLength); } - -inline int8_t -UnicodeString::compareBetween(int32_t start, - int32_t limit, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLimit) const -{ return doCompare(start, limit - start, - srcText, srcStart, srcLimit - srcStart); } - -inline int8_t -UnicodeString::doCompareCodePointOrder(int32_t start, - int32_t thisLength, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) const -{ - if(srcText.isBogus()) { - return (int8_t)!isBogus(); // 0 if both are bogus, 1 otherwise - } else { - srcText.pinIndices(srcStart, srcLength); - return doCompareCodePointOrder(start, thisLength, srcText.getArrayStart(), srcStart, srcLength); - } -} - -inline int8_t -UnicodeString::compareCodePointOrder(const UnicodeString& text) const -{ return doCompareCodePointOrder(0, length(), text, 0, text.length()); } - -inline int8_t -UnicodeString::compareCodePointOrder(int32_t start, - int32_t _length, - const UnicodeString& srcText) const -{ return doCompareCodePointOrder(start, _length, srcText, 0, srcText.length()); } - -inline int8_t -UnicodeString::compareCodePointOrder(ConstChar16Ptr srcChars, - int32_t srcLength) const -{ return doCompareCodePointOrder(0, length(), srcChars, 0, srcLength); } - -inline int8_t -UnicodeString::compareCodePointOrder(int32_t start, - int32_t _length, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) const -{ return doCompareCodePointOrder(start, _length, srcText, srcStart, srcLength); } - -inline int8_t -UnicodeString::compareCodePointOrder(int32_t start, - int32_t _length, - const char16_t *srcChars) const -{ return doCompareCodePointOrder(start, _length, srcChars, 0, _length); } - -inline int8_t -UnicodeString::compareCodePointOrder(int32_t start, - int32_t _length, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength) const -{ return doCompareCodePointOrder(start, _length, srcChars, srcStart, srcLength); } - -inline int8_t -UnicodeString::compareCodePointOrderBetween(int32_t start, - int32_t limit, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLimit) const -{ return doCompareCodePointOrder(start, limit - start, - srcText, srcStart, srcLimit - srcStart); } - -inline int8_t -UnicodeString::doCaseCompare(int32_t start, - int32_t thisLength, - const UnicodeString &srcText, - int32_t srcStart, - int32_t srcLength, - uint32_t options) const -{ - if(srcText.isBogus()) { - return (int8_t)!isBogus(); // 0 if both are bogus, 1 otherwise - } else { - srcText.pinIndices(srcStart, srcLength); - return doCaseCompare(start, thisLength, srcText.getArrayStart(), srcStart, srcLength, options); - } -} - -inline int8_t -UnicodeString::caseCompare(const UnicodeString &text, uint32_t options) const { - return doCaseCompare(0, length(), text, 0, text.length(), options); -} - -inline int8_t -UnicodeString::caseCompare(int32_t start, - int32_t _length, - const UnicodeString &srcText, - uint32_t options) const { - return doCaseCompare(start, _length, srcText, 0, srcText.length(), options); -} - -inline int8_t -UnicodeString::caseCompare(ConstChar16Ptr srcChars, - int32_t srcLength, - uint32_t options) const { - return doCaseCompare(0, length(), srcChars, 0, srcLength, options); -} - -inline int8_t -UnicodeString::caseCompare(int32_t start, - int32_t _length, - const UnicodeString &srcText, - int32_t srcStart, - int32_t srcLength, - uint32_t options) const { - return doCaseCompare(start, _length, srcText, srcStart, srcLength, options); -} - -inline int8_t -UnicodeString::caseCompare(int32_t start, - int32_t _length, - const char16_t *srcChars, - uint32_t options) const { - return doCaseCompare(start, _length, srcChars, 0, _length, options); -} - -inline int8_t -UnicodeString::caseCompare(int32_t start, - int32_t _length, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength, - uint32_t options) const { - return doCaseCompare(start, _length, srcChars, srcStart, srcLength, options); -} - -inline int8_t -UnicodeString::caseCompareBetween(int32_t start, - int32_t limit, - const UnicodeString &srcText, - int32_t srcStart, - int32_t srcLimit, - uint32_t options) const { - return doCaseCompare(start, limit - start, srcText, srcStart, srcLimit - srcStart, options); -} - -inline int32_t -UnicodeString::indexOf(const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength, - int32_t start, - int32_t _length) const -{ - if(!srcText.isBogus()) { - srcText.pinIndices(srcStart, srcLength); - if(srcLength > 0) { - return indexOf(srcText.getArrayStart(), srcStart, srcLength, start, _length); - } - } - return -1; -} - -inline int32_t -UnicodeString::indexOf(const UnicodeString& text) const -{ return indexOf(text, 0, text.length(), 0, length()); } - -inline int32_t -UnicodeString::indexOf(const UnicodeString& text, - int32_t start) const { - pinIndex(start); - return indexOf(text, 0, text.length(), start, length() - start); -} - -inline int32_t -UnicodeString::indexOf(const UnicodeString& text, - int32_t start, - int32_t _length) const -{ return indexOf(text, 0, text.length(), start, _length); } - -inline int32_t -UnicodeString::indexOf(const char16_t *srcChars, - int32_t srcLength, - int32_t start) const { - pinIndex(start); - return indexOf(srcChars, 0, srcLength, start, length() - start); -} - -inline int32_t -UnicodeString::indexOf(ConstChar16Ptr srcChars, - int32_t srcLength, - int32_t start, - int32_t _length) const -{ return indexOf(srcChars, 0, srcLength, start, _length); } - -inline int32_t -UnicodeString::indexOf(char16_t c, - int32_t start, - int32_t _length) const -{ return doIndexOf(c, start, _length); } - -inline int32_t -UnicodeString::indexOf(UChar32 c, - int32_t start, - int32_t _length) const -{ return doIndexOf(c, start, _length); } - -inline int32_t -UnicodeString::indexOf(char16_t c) const -{ return doIndexOf(c, 0, length()); } - -inline int32_t -UnicodeString::indexOf(UChar32 c) const -{ return indexOf(c, 0, length()); } - -inline int32_t -UnicodeString::indexOf(char16_t c, - int32_t start) const { - pinIndex(start); - return doIndexOf(c, start, length() - start); -} - -inline int32_t -UnicodeString::indexOf(UChar32 c, - int32_t start) const { - pinIndex(start); - return indexOf(c, start, length() - start); -} - -inline int32_t -UnicodeString::lastIndexOf(ConstChar16Ptr srcChars, - int32_t srcLength, - int32_t start, - int32_t _length) const -{ return lastIndexOf(srcChars, 0, srcLength, start, _length); } - -inline int32_t -UnicodeString::lastIndexOf(const char16_t *srcChars, - int32_t srcLength, - int32_t start) const { - pinIndex(start); - return lastIndexOf(srcChars, 0, srcLength, start, length() - start); -} - -inline int32_t -UnicodeString::lastIndexOf(const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength, - int32_t start, - int32_t _length) const -{ - if(!srcText.isBogus()) { - srcText.pinIndices(srcStart, srcLength); - if(srcLength > 0) { - return lastIndexOf(srcText.getArrayStart(), srcStart, srcLength, start, _length); - } - } - return -1; -} - -inline int32_t -UnicodeString::lastIndexOf(const UnicodeString& text, - int32_t start, - int32_t _length) const -{ return lastIndexOf(text, 0, text.length(), start, _length); } - -inline int32_t -UnicodeString::lastIndexOf(const UnicodeString& text, - int32_t start) const { - pinIndex(start); - return lastIndexOf(text, 0, text.length(), start, length() - start); -} - -inline int32_t -UnicodeString::lastIndexOf(const UnicodeString& text) const -{ return lastIndexOf(text, 0, text.length(), 0, length()); } - -inline int32_t -UnicodeString::lastIndexOf(char16_t c, - int32_t start, - int32_t _length) const -{ return doLastIndexOf(c, start, _length); } - -inline int32_t -UnicodeString::lastIndexOf(UChar32 c, - int32_t start, - int32_t _length) const { - return doLastIndexOf(c, start, _length); -} - -inline int32_t -UnicodeString::lastIndexOf(char16_t c) const -{ return doLastIndexOf(c, 0, length()); } - -inline int32_t -UnicodeString::lastIndexOf(UChar32 c) const { - return lastIndexOf(c, 0, length()); -} - -inline int32_t -UnicodeString::lastIndexOf(char16_t c, - int32_t start) const { - pinIndex(start); - return doLastIndexOf(c, start, length() - start); -} - -inline int32_t -UnicodeString::lastIndexOf(UChar32 c, - int32_t start) const { - pinIndex(start); - return lastIndexOf(c, start, length() - start); -} - -inline UBool -UnicodeString::startsWith(const UnicodeString& text) const -{ return compare(0, text.length(), text, 0, text.length()) == 0; } - -inline UBool -UnicodeString::startsWith(const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) const -{ return doCompare(0, srcLength, srcText, srcStart, srcLength) == 0; } - -inline UBool -UnicodeString::startsWith(ConstChar16Ptr srcChars, int32_t srcLength) const { - if(srcLength < 0) { - srcLength = u_strlen(toUCharPtr(srcChars)); - } - return doCompare(0, srcLength, srcChars, 0, srcLength) == 0; -} - -inline UBool -UnicodeString::startsWith(const char16_t *srcChars, int32_t srcStart, int32_t srcLength) const { - if(srcLength < 0) { - srcLength = u_strlen(toUCharPtr(srcChars)); - } - return doCompare(0, srcLength, srcChars, srcStart, srcLength) == 0; -} - -inline UBool -UnicodeString::endsWith(const UnicodeString& text) const -{ return doCompare(length() - text.length(), text.length(), - text, 0, text.length()) == 0; } - -inline UBool -UnicodeString::endsWith(const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) const { - srcText.pinIndices(srcStart, srcLength); - return doCompare(length() - srcLength, srcLength, - srcText, srcStart, srcLength) == 0; -} - -inline UBool -UnicodeString::endsWith(ConstChar16Ptr srcChars, - int32_t srcLength) const { - if(srcLength < 0) { - srcLength = u_strlen(toUCharPtr(srcChars)); - } - return doCompare(length() - srcLength, srcLength, - srcChars, 0, srcLength) == 0; -} - -inline UBool -UnicodeString::endsWith(const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength) const { - if(srcLength < 0) { - srcLength = u_strlen(toUCharPtr(srcChars + srcStart)); - } - return doCompare(length() - srcLength, srcLength, - srcChars, srcStart, srcLength) == 0; -} - -//======================================== -// replace -//======================================== -inline UnicodeString& -UnicodeString::replace(int32_t start, - int32_t _length, - const UnicodeString& srcText) -{ return doReplace(start, _length, srcText, 0, srcText.length()); } - -inline UnicodeString& -UnicodeString::replace(int32_t start, - int32_t _length, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) -{ return doReplace(start, _length, srcText, srcStart, srcLength); } - -inline UnicodeString& -UnicodeString::replace(int32_t start, - int32_t _length, - ConstChar16Ptr srcChars, - int32_t srcLength) -{ return doReplace(start, _length, srcChars, 0, srcLength); } - -inline UnicodeString& -UnicodeString::replace(int32_t start, - int32_t _length, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength) -{ return doReplace(start, _length, srcChars, srcStart, srcLength); } - -inline UnicodeString& -UnicodeString::replace(int32_t start, - int32_t _length, - char16_t srcChar) -{ return doReplace(start, _length, &srcChar, 0, 1); } - -inline UnicodeString& -UnicodeString::replaceBetween(int32_t start, - int32_t limit, - const UnicodeString& srcText) -{ return doReplace(start, limit - start, srcText, 0, srcText.length()); } - -inline UnicodeString& -UnicodeString::replaceBetween(int32_t start, - int32_t limit, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLimit) -{ return doReplace(start, limit - start, srcText, srcStart, srcLimit - srcStart); } - -inline UnicodeString& -UnicodeString::findAndReplace(const UnicodeString& oldText, - const UnicodeString& newText) -{ return findAndReplace(0, length(), oldText, 0, oldText.length(), - newText, 0, newText.length()); } - -inline UnicodeString& -UnicodeString::findAndReplace(int32_t start, - int32_t _length, - const UnicodeString& oldText, - const UnicodeString& newText) -{ return findAndReplace(start, _length, oldText, 0, oldText.length(), - newText, 0, newText.length()); } - -// ============================ -// extract -// ============================ -inline void -UnicodeString::doExtract(int32_t start, - int32_t _length, - UnicodeString& target) const -{ target.replace(0, target.length(), *this, start, _length); } - -inline void -UnicodeString::extract(int32_t start, - int32_t _length, - Char16Ptr target, - int32_t targetStart) const -{ doExtract(start, _length, target, targetStart); } - -inline void -UnicodeString::extract(int32_t start, - int32_t _length, - UnicodeString& target) const -{ doExtract(start, _length, target); } - -#if !UCONFIG_NO_CONVERSION - -inline int32_t -UnicodeString::extract(int32_t start, - int32_t _length, - char *dst, - const char *codepage) const - -{ - // This dstSize value will be checked explicitly - return extract(start, _length, dst, dst!=0 ? 0xffffffff : 0, codepage); -} - -#endif - -inline void -UnicodeString::extractBetween(int32_t start, - int32_t limit, - char16_t *dst, - int32_t dstStart) const { - pinIndex(start); - pinIndex(limit); - doExtract(start, limit - start, dst, dstStart); -} - -inline UnicodeString -UnicodeString::tempSubStringBetween(int32_t start, int32_t limit) const { - return tempSubString(start, limit - start); -} - -inline char16_t -UnicodeString::doCharAt(int32_t offset) const -{ - if((uint32_t)offset < (uint32_t)length()) { - return getArrayStart()[offset]; - } else { - return kInvalidUChar; - } -} - -inline char16_t -UnicodeString::charAt(int32_t offset) const -{ return doCharAt(offset); } - -inline char16_t -UnicodeString::operator[] (int32_t offset) const -{ return doCharAt(offset); } - -inline UBool -UnicodeString::isEmpty() const { - // Arithmetic or logical right shift does not matter: only testing for 0. - return (fUnion.fFields.fLengthAndFlags>>kLengthShift) == 0; -} - -//======================================== -// Write implementation methods -//======================================== -inline void -UnicodeString::setZeroLength() { - fUnion.fFields.fLengthAndFlags &= kAllStorageFlags; -} - -inline void -UnicodeString::setShortLength(int32_t len) { - // requires 0 <= len <= kMaxShortLength - fUnion.fFields.fLengthAndFlags = - (int16_t)((fUnion.fFields.fLengthAndFlags & kAllStorageFlags) | (len << kLengthShift)); -} - -inline void -UnicodeString::setLength(int32_t len) { - if(len <= kMaxShortLength) { - setShortLength(len); - } else { - fUnion.fFields.fLengthAndFlags |= kLengthIsLarge; - fUnion.fFields.fLength = len; - } -} - -inline void -UnicodeString::setToEmpty() { - fUnion.fFields.fLengthAndFlags = kShortString; -} - -inline void -UnicodeString::setArray(char16_t *array, int32_t len, int32_t capacity) { - setLength(len); - fUnion.fFields.fArray = array; - fUnion.fFields.fCapacity = capacity; -} - -inline UnicodeString& -UnicodeString::operator= (char16_t ch) -{ return doReplace(0, length(), &ch, 0, 1); } - -inline UnicodeString& -UnicodeString::operator= (UChar32 ch) -{ return replace(0, length(), ch); } - -inline UnicodeString& -UnicodeString::setTo(const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) -{ - unBogus(); - return doReplace(0, length(), srcText, srcStart, srcLength); -} - -inline UnicodeString& -UnicodeString::setTo(const UnicodeString& srcText, - int32_t srcStart) -{ - unBogus(); - srcText.pinIndex(srcStart); - return doReplace(0, length(), srcText, srcStart, srcText.length() - srcStart); -} - -inline UnicodeString& -UnicodeString::setTo(const UnicodeString& srcText) -{ - return copyFrom(srcText); -} - -inline UnicodeString& -UnicodeString::setTo(const char16_t *srcChars, - int32_t srcLength) -{ - unBogus(); - return doReplace(0, length(), srcChars, 0, srcLength); -} - -inline UnicodeString& -UnicodeString::setTo(char16_t srcChar) -{ - unBogus(); - return doReplace(0, length(), &srcChar, 0, 1); -} - -inline UnicodeString& -UnicodeString::setTo(UChar32 srcChar) -{ - unBogus(); - return replace(0, length(), srcChar); -} - -inline UnicodeString& -UnicodeString::append(const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) -{ return doAppend(srcText, srcStart, srcLength); } - -inline UnicodeString& -UnicodeString::append(const UnicodeString& srcText) -{ return doAppend(srcText, 0, srcText.length()); } - -inline UnicodeString& -UnicodeString::append(const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength) -{ return doAppend(srcChars, srcStart, srcLength); } - -inline UnicodeString& -UnicodeString::append(ConstChar16Ptr srcChars, - int32_t srcLength) -{ return doAppend(srcChars, 0, srcLength); } - -inline UnicodeString& -UnicodeString::append(char16_t srcChar) -{ return doAppend(&srcChar, 0, 1); } - -inline UnicodeString& -UnicodeString::operator+= (char16_t ch) -{ return doAppend(&ch, 0, 1); } - -inline UnicodeString& -UnicodeString::operator+= (UChar32 ch) { - return append(ch); -} - -inline UnicodeString& -UnicodeString::operator+= (const UnicodeString& srcText) -{ return doAppend(srcText, 0, srcText.length()); } - -inline UnicodeString& -UnicodeString::insert(int32_t start, - const UnicodeString& srcText, - int32_t srcStart, - int32_t srcLength) -{ return doReplace(start, 0, srcText, srcStart, srcLength); } - -inline UnicodeString& -UnicodeString::insert(int32_t start, - const UnicodeString& srcText) -{ return doReplace(start, 0, srcText, 0, srcText.length()); } - -inline UnicodeString& -UnicodeString::insert(int32_t start, - const char16_t *srcChars, - int32_t srcStart, - int32_t srcLength) -{ return doReplace(start, 0, srcChars, srcStart, srcLength); } - -inline UnicodeString& -UnicodeString::insert(int32_t start, - ConstChar16Ptr srcChars, - int32_t srcLength) -{ return doReplace(start, 0, srcChars, 0, srcLength); } - -inline UnicodeString& -UnicodeString::insert(int32_t start, - char16_t srcChar) -{ return doReplace(start, 0, &srcChar, 0, 1); } - -inline UnicodeString& -UnicodeString::insert(int32_t start, - UChar32 srcChar) -{ return replace(start, 0, srcChar); } - - -inline UnicodeString& -UnicodeString::remove() -{ - // remove() of a bogus string makes the string empty and non-bogus - if(isBogus()) { - setToEmpty(); - } else { - setZeroLength(); - } - return *this; -} - -inline UnicodeString& -UnicodeString::remove(int32_t start, - int32_t _length) -{ - if(start <= 0 && _length == INT32_MAX) { - // remove(guaranteed everything) of a bogus string makes the string empty and non-bogus - return remove(); - } - return doReplace(start, _length, NULL, 0, 0); -} - -inline UnicodeString& -UnicodeString::removeBetween(int32_t start, - int32_t limit) -{ return doReplace(start, limit - start, NULL, 0, 0); } - -inline UnicodeString & -UnicodeString::retainBetween(int32_t start, int32_t limit) { - truncate(limit); - return doReplace(0, start, NULL, 0, 0); -} - -inline UBool -UnicodeString::truncate(int32_t targetLength) -{ - if(isBogus() && targetLength == 0) { - // truncate(0) of a bogus string makes the string empty and non-bogus - unBogus(); - return false; - } else if((uint32_t)targetLength < (uint32_t)length()) { - setLength(targetLength); - return true; - } else { - return false; - } -} - -inline UnicodeString& -UnicodeString::reverse() -{ return doReverse(0, length()); } - -inline UnicodeString& -UnicodeString::reverse(int32_t start, - int32_t _length) -{ return doReverse(start, _length); } - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1998-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* File unistr.h +* +* Modification History: +* +* Date Name Description +* 09/25/98 stephen Creation. +* 11/11/98 stephen Changed per 11/9 code review. +* 04/20/99 stephen Overhauled per 4/16 code review. +* 11/18/99 aliu Made to inherit from Replaceable. Added method +* handleReplaceBetween(); other methods unchanged. +* 06/25/01 grhoten Remove dependency on iostream. +****************************************************************************** +*/ + +#ifndef UNISTR_H +#define UNISTR_H + +/** + * \file + * \brief C++ API: Unicode String + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include +#include "unicode/char16ptr.h" +#include "unicode/rep.h" +#include "unicode/std_string.h" +#include "unicode/stringpiece.h" +#include "unicode/bytestream.h" + +struct UConverter; // unicode/ucnv.h + +#ifndef USTRING_H +/** + * \ingroup ustring_ustrlen + * @param s Pointer to sequence of UChars. + * @return Length of sequence. + */ +U_CAPI int32_t U_EXPORT2 u_strlen(const UChar *s); +#endif + +U_NAMESPACE_BEGIN + +#if !UCONFIG_NO_BREAK_ITERATION +class BreakIterator; // unicode/brkiter.h +#endif +class Edits; + +U_NAMESPACE_END + +// Not #ifndef U_HIDE_INTERNAL_API because UnicodeString needs the UStringCaseMapper. +/** + * Internal string case mapping function type. + * All error checking must be done. + * src and dest must not overlap. + * @internal + */ +typedef int32_t U_CALLCONV +UStringCaseMapper(int32_t caseLocale, uint32_t options, +#if !UCONFIG_NO_BREAK_ITERATION + icu::BreakIterator *iter, +#endif + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + icu::Edits *edits, + UErrorCode &errorCode); + +U_NAMESPACE_BEGIN + +class Locale; // unicode/locid.h +class StringCharacterIterator; +class UnicodeStringAppendable; // unicode/appendable.h + +/* The include has been moved to unicode/ustream.h */ + +/** + * Constant to be used in the UnicodeString(char *, int32_t, EInvariant) constructor + * which constructs a Unicode string from an invariant-character char * string. + * About invariant characters see utypes.h. + * This constructor has no runtime dependency on conversion code and is + * therefore recommended over ones taking a charset name string + * (where the empty string "" indicates invariant-character conversion). + * + * @stable ICU 3.2 + */ +#define US_INV icu::UnicodeString::kInvariant + +/** + * Unicode String literals in C++. + * + * Note: these macros are not recommended for new code. + * Prior to the availability of C++11 and u"unicode string literals", + * these macros were provided for portability and efficiency when + * initializing UnicodeStrings from literals. + * + * They work only for strings that contain "invariant characters", i.e., + * only latin letters, digits, and some punctuation. + * See utypes.h for details. + * + * The string parameter must be a C string literal. + * The length of the string, not including the terminating + * `NUL`, must be specified as a constant. + * @stable ICU 2.0 + */ +#if !U_CHAR16_IS_TYPEDEF +# define UNICODE_STRING(cs, _length) icu::UnicodeString(true, u ## cs, _length) +#else +# define UNICODE_STRING(cs, _length) icu::UnicodeString(true, (const char16_t*)u ## cs, _length) +#endif + +/** + * Unicode String literals in C++. + * Dependent on the platform properties, different UnicodeString + * constructors should be used to create a UnicodeString object from + * a string literal. + * The macros are defined for improved performance. + * They work only for strings that contain "invariant characters", i.e., + * only latin letters, digits, and some punctuation. + * See utypes.h for details. + * + * The string parameter must be a C string literal. + * @stable ICU 2.0 + */ +#define UNICODE_STRING_SIMPLE(cs) UNICODE_STRING(cs, -1) + +/** + * \def UNISTR_FROM_CHAR_EXPLICIT + * This can be defined to be empty or "explicit". + * If explicit, then the UnicodeString(char16_t) and UnicodeString(UChar32) + * constructors are marked as explicit, preventing their inadvertent use. + * @stable ICU 49 + */ +#ifndef UNISTR_FROM_CHAR_EXPLICIT +# if defined(U_COMBINED_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) || defined(U_I18N_IMPLEMENTATION) || defined(U_IO_IMPLEMENTATION) + // Auto-"explicit" in ICU library code. +# define UNISTR_FROM_CHAR_EXPLICIT explicit +# else + // Empty by default for source code compatibility. +# define UNISTR_FROM_CHAR_EXPLICIT +# endif +#endif + +/** + * \def UNISTR_FROM_STRING_EXPLICIT + * This can be defined to be empty or "explicit". + * If explicit, then the UnicodeString(const char *) and UnicodeString(const char16_t *) + * constructors are marked as explicit, preventing their inadvertent use. + * + * In particular, this helps prevent accidentally depending on ICU conversion code + * by passing a string literal into an API with a const UnicodeString & parameter. + * @stable ICU 49 + */ +#ifndef UNISTR_FROM_STRING_EXPLICIT +# if defined(U_COMBINED_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) || defined(U_I18N_IMPLEMENTATION) || defined(U_IO_IMPLEMENTATION) + // Auto-"explicit" in ICU library code. +# define UNISTR_FROM_STRING_EXPLICIT explicit +# else + // Empty by default for source code compatibility. +# define UNISTR_FROM_STRING_EXPLICIT +# endif +#endif + +/** + * \def UNISTR_OBJECT_SIZE + * Desired sizeof(UnicodeString) in bytes. + * It should be a multiple of sizeof(pointer) to avoid unusable space for padding. + * The object size may want to be a multiple of 16 bytes, + * which is a common granularity for heap allocation. + * + * Any space inside the object beyond sizeof(vtable pointer) + 2 + * is available for storing short strings inside the object. + * The bigger the object, the longer a string that can be stored inside the object, + * without additional heap allocation. + * + * Depending on a platform's pointer size, pointer alignment requirements, + * and struct padding, the compiler will usually round up sizeof(UnicodeString) + * to 4 * sizeof(pointer) (or 3 * sizeof(pointer) for P128 data models), + * to hold the fields for heap-allocated strings. + * Such a minimum size also ensures that the object is easily large enough + * to hold at least 2 char16_ts, for one supplementary code point (U16_MAX_LENGTH). + * + * sizeof(UnicodeString) >= 48 should work for all known platforms. + * + * For example, on a 64-bit machine where sizeof(vtable pointer) is 8, + * sizeof(UnicodeString) = 64 would leave space for + * (64 - sizeof(vtable pointer) - 2) / U_SIZEOF_UCHAR = (64 - 8 - 2) / 2 = 27 + * char16_ts stored inside the object. + * + * The minimum object size on a 64-bit machine would be + * 4 * sizeof(pointer) = 4 * 8 = 32 bytes, + * and the internal buffer would hold up to 11 char16_ts in that case. + * + * @see U16_MAX_LENGTH + * @stable ICU 56 + */ +#ifndef UNISTR_OBJECT_SIZE +# define UNISTR_OBJECT_SIZE 64 +#endif + +/** + * UnicodeString is a string class that stores Unicode characters directly and provides + * similar functionality as the Java String and StringBuffer/StringBuilder classes. + * It is a concrete implementation of the abstract class Replaceable (for transliteration). + * + * The UnicodeString equivalent of std::string’s clear() is remove(). + * + * A UnicodeString may "alias" an external array of characters + * (that is, point to it, rather than own the array) + * whose lifetime must then at least match the lifetime of the aliasing object. + * This aliasing may be preserved when returning a UnicodeString by value, + * depending on the compiler and the function implementation, + * via Return Value Optimization (RVO) or the move assignment operator. + * (However, the copy assignment operator does not preserve aliasing.) + * For details see the description of storage models at the end of the class API docs + * and in the User Guide chapter linked from there. + * + * The UnicodeString class is not suitable for subclassing. + * + * For an overview of Unicode strings in C and C++ see the + * [User Guide Strings chapter](https://unicode-org.github.io/icu/userguide/strings#strings-in-cc). + * + * In ICU, a Unicode string consists of 16-bit Unicode *code units*. + * A Unicode character may be stored with either one code unit + * (the most common case) or with a matched pair of special code units + * ("surrogates"). The data type for code units is char16_t. + * For single-character handling, a Unicode character code *point* is a value + * in the range 0..0x10ffff. ICU uses the UChar32 type for code points. + * + * Indexes and offsets into and lengths of strings always count code units, not code points. + * This is the same as with multi-byte char* strings in traditional string handling. + * Operations on partial strings typically do not test for code point boundaries. + * If necessary, the user needs to take care of such boundaries by testing for the code unit + * values or by using functions like + * UnicodeString::getChar32Start() and UnicodeString::getChar32Limit() + * (or, in C, the equivalent macros U16_SET_CP_START() and U16_SET_CP_LIMIT(), see utf.h). + * + * UnicodeString methods are more lenient with regard to input parameter values + * than other ICU APIs. In particular: + * - If indexes are out of bounds for a UnicodeString object + * (< 0 or > length()) then they are "pinned" to the nearest boundary. + * - If the buffer passed to an insert/append/replace operation is owned by the + * target object, e.g., calling str.append(str), an extra copy may take place + * to ensure safety. + * - If primitive string pointer values (e.g., const char16_t * or char *) + * for input strings are nullptr, then those input string parameters are treated + * as if they pointed to an empty string. + * However, this is *not* the case for char * parameters for charset names + * or other IDs. + * - Most UnicodeString methods do not take a UErrorCode parameter because + * there are usually very few opportunities for failure other than a shortage + * of memory, error codes in low-level C++ string methods would be inconvenient, + * and the error code as the last parameter (ICU convention) would prevent + * the use of default parameter values. + * Instead, such methods set the UnicodeString into a "bogus" state + * (see isBogus()) if an error occurs. + * + * In string comparisons, two UnicodeString objects that are both "bogus" + * compare equal (to be transitive and prevent endless loops in sorting), + * and a "bogus" string compares less than any non-"bogus" one. + * + * Const UnicodeString methods are thread-safe. Multiple threads can use + * const methods on the same UnicodeString object simultaneously, + * but non-const methods must not be called concurrently (in multiple threads) + * with any other (const or non-const) methods. + * + * Similarly, const UnicodeString & parameters are thread-safe. + * One object may be passed in as such a parameter concurrently in multiple threads. + * This includes the const UnicodeString & parameters for + * copy construction, assignment, and cloning. + * + * UnicodeString uses several storage methods. + * String contents can be stored inside the UnicodeString object itself, + * in an allocated and shared buffer, or in an outside buffer that is "aliased". + * Most of this is done transparently, but careful aliasing in particular provides + * significant performance improvements. + * Also, the internal buffer is accessible via special functions. + * For details see the + * [User Guide Strings chapter](https://unicode-org.github.io/icu/userguide/strings#maximizing-performance-with-the-unicodestring-storage-model). + * + * @see utf.h + * @see CharacterIterator + * @stable ICU 2.0 + */ +class U_COMMON_API UnicodeString : public Replaceable +{ +public: + + /** + * Constant to be used in the UnicodeString(char *, int32_t, EInvariant) constructor + * which constructs a Unicode string from an invariant-character char * string. + * Use the macro US_INV instead of the full qualification for this value. + * + * @see US_INV + * @stable ICU 3.2 + */ + enum EInvariant { + /** + * @see EInvariant + * @stable ICU 3.2 + */ + kInvariant + }; + + //======================================== + // Read-only operations + //======================================== + + /* Comparison - bitwise only - for international comparison use collation */ + + /** + * Equality operator. Performs only bitwise comparison. + * @param text The UnicodeString to compare to this one. + * @return true if `text` contains the same characters as this one, + * false otherwise. + * @stable ICU 2.0 + */ + inline bool operator== (const UnicodeString& text) const; + + /** + * Inequality operator. Performs only bitwise comparison. + * @param text The UnicodeString to compare to this one. + * @return false if `text` contains the same characters as this one, + * true otherwise. + * @stable ICU 2.0 + */ + inline bool operator!= (const UnicodeString& text) const; + + /** + * Greater than operator. Performs only bitwise comparison. + * @param text The UnicodeString to compare to this one. + * @return true if the characters in this are bitwise + * greater than the characters in `text`, false otherwise + * @stable ICU 2.0 + */ + inline UBool operator> (const UnicodeString& text) const; + + /** + * Less than operator. Performs only bitwise comparison. + * @param text The UnicodeString to compare to this one. + * @return true if the characters in this are bitwise + * less than the characters in `text`, false otherwise + * @stable ICU 2.0 + */ + inline UBool operator< (const UnicodeString& text) const; + + /** + * Greater than or equal operator. Performs only bitwise comparison. + * @param text The UnicodeString to compare to this one. + * @return true if the characters in this are bitwise + * greater than or equal to the characters in `text`, false otherwise + * @stable ICU 2.0 + */ + inline UBool operator>= (const UnicodeString& text) const; + + /** + * Less than or equal operator. Performs only bitwise comparison. + * @param text The UnicodeString to compare to this one. + * @return true if the characters in this are bitwise + * less than or equal to the characters in `text`, false otherwise + * @stable ICU 2.0 + */ + inline UBool operator<= (const UnicodeString& text) const; + + /** + * Compare the characters bitwise in this UnicodeString to + * the characters in `text`. + * @param text The UnicodeString to compare to this one. + * @return The result of bitwise character comparison: 0 if this + * contains the same characters as `text`, -1 if the characters in + * this are bitwise less than the characters in `text`, +1 if the + * characters in this are bitwise greater than the characters + * in `text`. + * @stable ICU 2.0 + */ + inline int8_t compare(const UnicodeString& text) const; + + /** + * Compare the characters bitwise in the range + * [`start`, `start + length`) with the characters + * in the **entire string** `text`. + * (The parameters "start" and "length" are not applied to the other text "text".) + * @param start the offset at which the compare operation begins + * @param length the number of characters of text to compare. + * @param text the other text to be compared against this string. + * @return The result of bitwise character comparison: 0 if this + * contains the same characters as `text`, -1 if the characters in + * this are bitwise less than the characters in `text`, +1 if the + * characters in this are bitwise greater than the characters + * in `text`. + * @stable ICU 2.0 + */ + inline int8_t compare(int32_t start, + int32_t length, + const UnicodeString& text) const; + + /** + * Compare the characters bitwise in the range + * [`start`, `start + length`) with the characters + * in `srcText` in the range + * [`srcStart`, `srcStart + srcLength`). + * @param start the offset at which the compare operation begins + * @param length the number of characters in this to compare. + * @param srcText the text to be compared + * @param srcStart the offset into `srcText` to start comparison + * @param srcLength the number of characters in `src` to compare + * @return The result of bitwise character comparison: 0 if this + * contains the same characters as `srcText`, -1 if the characters in + * this are bitwise less than the characters in `srcText`, +1 if the + * characters in this are bitwise greater than the characters + * in `srcText`. + * @stable ICU 2.0 + */ + inline int8_t compare(int32_t start, + int32_t length, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const; + + /** + * Compare the characters bitwise in this UnicodeString with the first + * `srcLength` characters in `srcChars`. + * @param srcChars The characters to compare to this UnicodeString. + * @param srcLength the number of characters in `srcChars` to compare + * @return The result of bitwise character comparison: 0 if this + * contains the same characters as `srcChars`, -1 if the characters in + * this are bitwise less than the characters in `srcChars`, +1 if the + * characters in this are bitwise greater than the characters + * in `srcChars`. + * @stable ICU 2.0 + */ + inline int8_t compare(ConstChar16Ptr srcChars, + int32_t srcLength) const; + + /** + * Compare the characters bitwise in the range + * [`start`, `start + length`) with the first + * `length` characters in `srcChars` + * @param start the offset at which the compare operation begins + * @param length the number of characters to compare. + * @param srcChars the characters to be compared + * @return The result of bitwise character comparison: 0 if this + * contains the same characters as `srcChars`, -1 if the characters in + * this are bitwise less than the characters in `srcChars`, +1 if the + * characters in this are bitwise greater than the characters + * in `srcChars`. + * @stable ICU 2.0 + */ + inline int8_t compare(int32_t start, + int32_t length, + const char16_t *srcChars) const; + + /** + * Compare the characters bitwise in the range + * [`start`, `start + length`) with the characters + * in `srcChars` in the range + * [`srcStart`, `srcStart + srcLength`). + * @param start the offset at which the compare operation begins + * @param length the number of characters in this to compare + * @param srcChars the characters to be compared + * @param srcStart the offset into `srcChars` to start comparison + * @param srcLength the number of characters in `srcChars` to compare + * @return The result of bitwise character comparison: 0 if this + * contains the same characters as `srcChars`, -1 if the characters in + * this are bitwise less than the characters in `srcChars`, +1 if the + * characters in this are bitwise greater than the characters + * in `srcChars`. + * @stable ICU 2.0 + */ + inline int8_t compare(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const; + + /** + * Compare the characters bitwise in the range + * [`start`, `limit`) with the characters + * in `srcText` in the range + * [`srcStart`, `srcLimit`). + * @param start the offset at which the compare operation begins + * @param limit the offset immediately following the compare operation + * @param srcText the text to be compared + * @param srcStart the offset into `srcText` to start comparison + * @param srcLimit the offset into `srcText` to limit comparison + * @return The result of bitwise character comparison: 0 if this + * contains the same characters as `srcText`, -1 if the characters in + * this are bitwise less than the characters in `srcText`, +1 if the + * characters in this are bitwise greater than the characters + * in `srcText`. + * @stable ICU 2.0 + */ + inline int8_t compareBetween(int32_t start, + int32_t limit, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLimit) const; + + /** + * Compare two Unicode strings in code point order. + * The result may be different from the results of compare(), operator<, etc. + * if supplementary characters are present: + * + * In UTF-16, supplementary characters (with code points U+10000 and above) are + * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, + * which means that they compare as less than some other BMP characters like U+feff. + * This function compares Unicode strings in code point order. + * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. + * + * @param text Another string to compare this one to. + * @return a negative/zero/positive integer corresponding to whether + * this string is less than/equal to/greater than the second one + * in code point order + * @stable ICU 2.0 + */ + inline int8_t compareCodePointOrder(const UnicodeString& text) const; + + /** + * Compare two Unicode strings in code point order. + * The result may be different from the results of compare(), operator<, etc. + * if supplementary characters are present: + * + * In UTF-16, supplementary characters (with code points U+10000 and above) are + * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, + * which means that they compare as less than some other BMP characters like U+feff. + * This function compares Unicode strings in code point order. + * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. + * + * @param start The start offset in this string at which the compare operation begins. + * @param length The number of code units from this string to compare. + * @param srcText Another string to compare this one to. + * @return a negative/zero/positive integer corresponding to whether + * this string is less than/equal to/greater than the second one + * in code point order + * @stable ICU 2.0 + */ + inline int8_t compareCodePointOrder(int32_t start, + int32_t length, + const UnicodeString& srcText) const; + + /** + * Compare two Unicode strings in code point order. + * The result may be different from the results of compare(), operator<, etc. + * if supplementary characters are present: + * + * In UTF-16, supplementary characters (with code points U+10000 and above) are + * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, + * which means that they compare as less than some other BMP characters like U+feff. + * This function compares Unicode strings in code point order. + * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. + * + * @param start The start offset in this string at which the compare operation begins. + * @param length The number of code units from this string to compare. + * @param srcText Another string to compare this one to. + * @param srcStart The start offset in that string at which the compare operation begins. + * @param srcLength The number of code units from that string to compare. + * @return a negative/zero/positive integer corresponding to whether + * this string is less than/equal to/greater than the second one + * in code point order + * @stable ICU 2.0 + */ + inline int8_t compareCodePointOrder(int32_t start, + int32_t length, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const; + + /** + * Compare two Unicode strings in code point order. + * The result may be different from the results of compare(), operator<, etc. + * if supplementary characters are present: + * + * In UTF-16, supplementary characters (with code points U+10000 and above) are + * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, + * which means that they compare as less than some other BMP characters like U+feff. + * This function compares Unicode strings in code point order. + * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. + * + * @param srcChars A pointer to another string to compare this one to. + * @param srcLength The number of code units from that string to compare. + * @return a negative/zero/positive integer corresponding to whether + * this string is less than/equal to/greater than the second one + * in code point order + * @stable ICU 2.0 + */ + inline int8_t compareCodePointOrder(ConstChar16Ptr srcChars, + int32_t srcLength) const; + + /** + * Compare two Unicode strings in code point order. + * The result may be different from the results of compare(), operator<, etc. + * if supplementary characters are present: + * + * In UTF-16, supplementary characters (with code points U+10000 and above) are + * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, + * which means that they compare as less than some other BMP characters like U+feff. + * This function compares Unicode strings in code point order. + * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. + * + * @param start The start offset in this string at which the compare operation begins. + * @param length The number of code units from this string to compare. + * @param srcChars A pointer to another string to compare this one to. + * @return a negative/zero/positive integer corresponding to whether + * this string is less than/equal to/greater than the second one + * in code point order + * @stable ICU 2.0 + */ + inline int8_t compareCodePointOrder(int32_t start, + int32_t length, + const char16_t *srcChars) const; + + /** + * Compare two Unicode strings in code point order. + * The result may be different from the results of compare(), operator<, etc. + * if supplementary characters are present: + * + * In UTF-16, supplementary characters (with code points U+10000 and above) are + * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, + * which means that they compare as less than some other BMP characters like U+feff. + * This function compares Unicode strings in code point order. + * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. + * + * @param start The start offset in this string at which the compare operation begins. + * @param length The number of code units from this string to compare. + * @param srcChars A pointer to another string to compare this one to. + * @param srcStart The start offset in that string at which the compare operation begins. + * @param srcLength The number of code units from that string to compare. + * @return a negative/zero/positive integer corresponding to whether + * this string is less than/equal to/greater than the second one + * in code point order + * @stable ICU 2.0 + */ + inline int8_t compareCodePointOrder(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const; + + /** + * Compare two Unicode strings in code point order. + * The result may be different from the results of compare(), operator<, etc. + * if supplementary characters are present: + * + * In UTF-16, supplementary characters (with code points U+10000 and above) are + * stored with pairs of surrogate code units. These have values from 0xd800 to 0xdfff, + * which means that they compare as less than some other BMP characters like U+feff. + * This function compares Unicode strings in code point order. + * If either of the UTF-16 strings is malformed (i.e., it contains unpaired surrogates), then the result is not defined. + * + * @param start The start offset in this string at which the compare operation begins. + * @param limit The offset after the last code unit from this string to compare. + * @param srcText Another string to compare this one to. + * @param srcStart The start offset in that string at which the compare operation begins. + * @param srcLimit The offset after the last code unit from that string to compare. + * @return a negative/zero/positive integer corresponding to whether + * this string is less than/equal to/greater than the second one + * in code point order + * @stable ICU 2.0 + */ + inline int8_t compareCodePointOrderBetween(int32_t start, + int32_t limit, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLimit) const; + + /** + * Compare two strings case-insensitively using full case folding. + * This is equivalent to this->foldCase(options).compare(text.foldCase(options)). + * + * @param text Another string to compare this one to. + * @param options A bit set of options: + * - U_FOLD_CASE_DEFAULT or 0 is used for default options: + * Comparison in code unit order with default case folding. + * + * - U_COMPARE_CODE_POINT_ORDER + * Set to choose code point order instead of code unit order + * (see u_strCompare for details). + * + * - U_FOLD_CASE_EXCLUDE_SPECIAL_I + * + * @return A negative, zero, or positive integer indicating the comparison result. + * @stable ICU 2.0 + */ + inline int8_t caseCompare(const UnicodeString& text, uint32_t options) const; + + /** + * Compare two strings case-insensitively using full case folding. + * This is equivalent to this->foldCase(options).compare(srcText.foldCase(options)). + * + * @param start The start offset in this string at which the compare operation begins. + * @param length The number of code units from this string to compare. + * @param srcText Another string to compare this one to. + * @param options A bit set of options: + * - U_FOLD_CASE_DEFAULT or 0 is used for default options: + * Comparison in code unit order with default case folding. + * + * - U_COMPARE_CODE_POINT_ORDER + * Set to choose code point order instead of code unit order + * (see u_strCompare for details). + * + * - U_FOLD_CASE_EXCLUDE_SPECIAL_I + * + * @return A negative, zero, or positive integer indicating the comparison result. + * @stable ICU 2.0 + */ + inline int8_t caseCompare(int32_t start, + int32_t length, + const UnicodeString& srcText, + uint32_t options) const; + + /** + * Compare two strings case-insensitively using full case folding. + * This is equivalent to this->foldCase(options).compare(srcText.foldCase(options)). + * + * @param start The start offset in this string at which the compare operation begins. + * @param length The number of code units from this string to compare. + * @param srcText Another string to compare this one to. + * @param srcStart The start offset in that string at which the compare operation begins. + * @param srcLength The number of code units from that string to compare. + * @param options A bit set of options: + * - U_FOLD_CASE_DEFAULT or 0 is used for default options: + * Comparison in code unit order with default case folding. + * + * - U_COMPARE_CODE_POINT_ORDER + * Set to choose code point order instead of code unit order + * (see u_strCompare for details). + * + * - U_FOLD_CASE_EXCLUDE_SPECIAL_I + * + * @return A negative, zero, or positive integer indicating the comparison result. + * @stable ICU 2.0 + */ + inline int8_t caseCompare(int32_t start, + int32_t length, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength, + uint32_t options) const; + + /** + * Compare two strings case-insensitively using full case folding. + * This is equivalent to this->foldCase(options).compare(srcChars.foldCase(options)). + * + * @param srcChars A pointer to another string to compare this one to. + * @param srcLength The number of code units from that string to compare. + * @param options A bit set of options: + * - U_FOLD_CASE_DEFAULT or 0 is used for default options: + * Comparison in code unit order with default case folding. + * + * - U_COMPARE_CODE_POINT_ORDER + * Set to choose code point order instead of code unit order + * (see u_strCompare for details). + * + * - U_FOLD_CASE_EXCLUDE_SPECIAL_I + * + * @return A negative, zero, or positive integer indicating the comparison result. + * @stable ICU 2.0 + */ + inline int8_t caseCompare(ConstChar16Ptr srcChars, + int32_t srcLength, + uint32_t options) const; + + /** + * Compare two strings case-insensitively using full case folding. + * This is equivalent to this->foldCase(options).compare(srcChars.foldCase(options)). + * + * @param start The start offset in this string at which the compare operation begins. + * @param length The number of code units from this string to compare. + * @param srcChars A pointer to another string to compare this one to. + * @param options A bit set of options: + * - U_FOLD_CASE_DEFAULT or 0 is used for default options: + * Comparison in code unit order with default case folding. + * + * - U_COMPARE_CODE_POINT_ORDER + * Set to choose code point order instead of code unit order + * (see u_strCompare for details). + * + * - U_FOLD_CASE_EXCLUDE_SPECIAL_I + * + * @return A negative, zero, or positive integer indicating the comparison result. + * @stable ICU 2.0 + */ + inline int8_t caseCompare(int32_t start, + int32_t length, + const char16_t *srcChars, + uint32_t options) const; + + /** + * Compare two strings case-insensitively using full case folding. + * This is equivalent to this->foldCase(options).compare(srcChars.foldCase(options)). + * + * @param start The start offset in this string at which the compare operation begins. + * @param length The number of code units from this string to compare. + * @param srcChars A pointer to another string to compare this one to. + * @param srcStart The start offset in that string at which the compare operation begins. + * @param srcLength The number of code units from that string to compare. + * @param options A bit set of options: + * - U_FOLD_CASE_DEFAULT or 0 is used for default options: + * Comparison in code unit order with default case folding. + * + * - U_COMPARE_CODE_POINT_ORDER + * Set to choose code point order instead of code unit order + * (see u_strCompare for details). + * + * - U_FOLD_CASE_EXCLUDE_SPECIAL_I + * + * @return A negative, zero, or positive integer indicating the comparison result. + * @stable ICU 2.0 + */ + inline int8_t caseCompare(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength, + uint32_t options) const; + + /** + * Compare two strings case-insensitively using full case folding. + * This is equivalent to this->foldCase(options).compareBetween(text.foldCase(options)). + * + * @param start The start offset in this string at which the compare operation begins. + * @param limit The offset after the last code unit from this string to compare. + * @param srcText Another string to compare this one to. + * @param srcStart The start offset in that string at which the compare operation begins. + * @param srcLimit The offset after the last code unit from that string to compare. + * @param options A bit set of options: + * - U_FOLD_CASE_DEFAULT or 0 is used for default options: + * Comparison in code unit order with default case folding. + * + * - U_COMPARE_CODE_POINT_ORDER + * Set to choose code point order instead of code unit order + * (see u_strCompare for details). + * + * - U_FOLD_CASE_EXCLUDE_SPECIAL_I + * + * @return A negative, zero, or positive integer indicating the comparison result. + * @stable ICU 2.0 + */ + inline int8_t caseCompareBetween(int32_t start, + int32_t limit, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLimit, + uint32_t options) const; + + /** + * Determine if this starts with the characters in `text` + * @param text The text to match. + * @return true if this starts with the characters in `text`, + * false otherwise + * @stable ICU 2.0 + */ + inline UBool startsWith(const UnicodeString& text) const; + + /** + * Determine if this starts with the characters in `srcText` + * in the range [`srcStart`, `srcStart + srcLength`). + * @param srcText The text to match. + * @param srcStart the offset into `srcText` to start matching + * @param srcLength the number of characters in `srcText` to match + * @return true if this starts with the characters in `text`, + * false otherwise + * @stable ICU 2.0 + */ + inline UBool startsWith(const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const; + + /** + * Determine if this starts with the characters in `srcChars` + * @param srcChars The characters to match. + * @param srcLength the number of characters in `srcChars` + * @return true if this starts with the characters in `srcChars`, + * false otherwise + * @stable ICU 2.0 + */ + inline UBool startsWith(ConstChar16Ptr srcChars, + int32_t srcLength) const; + + /** + * Determine if this ends with the characters in `srcChars` + * in the range [`srcStart`, `srcStart + srcLength`). + * @param srcChars The characters to match. + * @param srcStart the offset into `srcText` to start matching + * @param srcLength the number of characters in `srcChars` to match + * @return true if this ends with the characters in `srcChars`, false otherwise + * @stable ICU 2.0 + */ + inline UBool startsWith(const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const; + + /** + * Determine if this ends with the characters in `text` + * @param text The text to match. + * @return true if this ends with the characters in `text`, + * false otherwise + * @stable ICU 2.0 + */ + inline UBool endsWith(const UnicodeString& text) const; + + /** + * Determine if this ends with the characters in `srcText` + * in the range [`srcStart`, `srcStart + srcLength`). + * @param srcText The text to match. + * @param srcStart the offset into `srcText` to start matching + * @param srcLength the number of characters in `srcText` to match + * @return true if this ends with the characters in `text`, + * false otherwise + * @stable ICU 2.0 + */ + inline UBool endsWith(const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const; + + /** + * Determine if this ends with the characters in `srcChars` + * @param srcChars The characters to match. + * @param srcLength the number of characters in `srcChars` + * @return true if this ends with the characters in `srcChars`, + * false otherwise + * @stable ICU 2.0 + */ + inline UBool endsWith(ConstChar16Ptr srcChars, + int32_t srcLength) const; + + /** + * Determine if this ends with the characters in `srcChars` + * in the range [`srcStart`, `srcStart + srcLength`). + * @param srcChars The characters to match. + * @param srcStart the offset into `srcText` to start matching + * @param srcLength the number of characters in `srcChars` to match + * @return true if this ends with the characters in `srcChars`, + * false otherwise + * @stable ICU 2.0 + */ + inline UBool endsWith(const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const; + + + /* Searching - bitwise only */ + + /** + * Locate in this the first occurrence of the characters in `text`, + * using bitwise comparison. + * @param text The text to search for. + * @return The offset into this of the start of `text`, + * or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t indexOf(const UnicodeString& text) const; + + /** + * Locate in this the first occurrence of the characters in `text` + * starting at offset `start`, using bitwise comparison. + * @param text The text to search for. + * @param start The offset at which searching will start. + * @return The offset into this of the start of `text`, + * or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t indexOf(const UnicodeString& text, + int32_t start) const; + + /** + * Locate in this the first occurrence in the range + * [`start`, `start + length`) of the characters + * in `text`, using bitwise comparison. + * @param text The text to search for. + * @param start The offset at which searching will start. + * @param length The number of characters to search + * @return The offset into this of the start of `text`, + * or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t indexOf(const UnicodeString& text, + int32_t start, + int32_t length) const; + + /** + * Locate in this the first occurrence in the range + * [`start`, `start + length`) of the characters + * in `srcText` in the range + * [`srcStart`, `srcStart + srcLength`), + * using bitwise comparison. + * @param srcText The text to search for. + * @param srcStart the offset into `srcText` at which + * to start matching + * @param srcLength the number of characters in `srcText` to match + * @param start the offset into this at which to start matching + * @param length the number of characters in this to search + * @return The offset into this of the start of `text`, + * or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t indexOf(const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength, + int32_t start, + int32_t length) const; + + /** + * Locate in this the first occurrence of the characters in + * `srcChars` + * starting at offset `start`, using bitwise comparison. + * @param srcChars The text to search for. + * @param srcLength the number of characters in `srcChars` to match + * @param start the offset into this at which to start matching + * @return The offset into this of the start of `text`, + * or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t indexOf(const char16_t *srcChars, + int32_t srcLength, + int32_t start) const; + + /** + * Locate in this the first occurrence in the range + * [`start`, `start + length`) of the characters + * in `srcChars`, using bitwise comparison. + * @param srcChars The text to search for. + * @param srcLength the number of characters in `srcChars` + * @param start The offset at which searching will start. + * @param length The number of characters to search + * @return The offset into this of the start of `srcChars`, + * or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t indexOf(ConstChar16Ptr srcChars, + int32_t srcLength, + int32_t start, + int32_t length) const; + + /** + * Locate in this the first occurrence in the range + * [`start`, `start + length`) of the characters + * in `srcChars` in the range + * [`srcStart`, `srcStart + srcLength`), + * using bitwise comparison. + * @param srcChars The text to search for. + * @param srcStart the offset into `srcChars` at which + * to start matching + * @param srcLength the number of characters in `srcChars` to match + * @param start the offset into this at which to start matching + * @param length the number of characters in this to search + * @return The offset into this of the start of `text`, + * or -1 if not found. + * @stable ICU 2.0 + */ + int32_t indexOf(const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength, + int32_t start, + int32_t length) const; + + /** + * Locate in this the first occurrence of the BMP code point `c`, + * using bitwise comparison. + * @param c The code unit to search for. + * @return The offset into this of `c`, or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t indexOf(char16_t c) const; + + /** + * Locate in this the first occurrence of the code point `c`, + * using bitwise comparison. + * + * @param c The code point to search for. + * @return The offset into this of `c`, or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t indexOf(UChar32 c) const; + + /** + * Locate in this the first occurrence of the BMP code point `c`, + * starting at offset `start`, using bitwise comparison. + * @param c The code unit to search for. + * @param start The offset at which searching will start. + * @return The offset into this of `c`, or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t indexOf(char16_t c, + int32_t start) const; + + /** + * Locate in this the first occurrence of the code point `c` + * starting at offset `start`, using bitwise comparison. + * + * @param c The code point to search for. + * @param start The offset at which searching will start. + * @return The offset into this of `c`, or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t indexOf(UChar32 c, + int32_t start) const; + + /** + * Locate in this the first occurrence of the BMP code point `c` + * in the range [`start`, `start + length`), + * using bitwise comparison. + * @param c The code unit to search for. + * @param start the offset into this at which to start matching + * @param length the number of characters in this to search + * @return The offset into this of `c`, or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t indexOf(char16_t c, + int32_t start, + int32_t length) const; + + /** + * Locate in this the first occurrence of the code point `c` + * in the range [`start`, `start + length`), + * using bitwise comparison. + * + * @param c The code point to search for. + * @param start the offset into this at which to start matching + * @param length the number of characters in this to search + * @return The offset into this of `c`, or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t indexOf(UChar32 c, + int32_t start, + int32_t length) const; + + /** + * Locate in this the last occurrence of the characters in `text`, + * using bitwise comparison. + * @param text The text to search for. + * @return The offset into this of the start of `text`, + * or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t lastIndexOf(const UnicodeString& text) const; + + /** + * Locate in this the last occurrence of the characters in `text` + * starting at offset `start`, using bitwise comparison. + * @param text The text to search for. + * @param start The offset at which searching will start. + * @return The offset into this of the start of `text`, + * or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t lastIndexOf(const UnicodeString& text, + int32_t start) const; + + /** + * Locate in this the last occurrence in the range + * [`start`, `start + length`) of the characters + * in `text`, using bitwise comparison. + * @param text The text to search for. + * @param start The offset at which searching will start. + * @param length The number of characters to search + * @return The offset into this of the start of `text`, + * or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t lastIndexOf(const UnicodeString& text, + int32_t start, + int32_t length) const; + + /** + * Locate in this the last occurrence in the range + * [`start`, `start + length`) of the characters + * in `srcText` in the range + * [`srcStart`, `srcStart + srcLength`), + * using bitwise comparison. + * @param srcText The text to search for. + * @param srcStart the offset into `srcText` at which + * to start matching + * @param srcLength the number of characters in `srcText` to match + * @param start the offset into this at which to start matching + * @param length the number of characters in this to search + * @return The offset into this of the start of `text`, + * or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t lastIndexOf(const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength, + int32_t start, + int32_t length) const; + + /** + * Locate in this the last occurrence of the characters in `srcChars` + * starting at offset `start`, using bitwise comparison. + * @param srcChars The text to search for. + * @param srcLength the number of characters in `srcChars` to match + * @param start the offset into this at which to start matching + * @return The offset into this of the start of `text`, + * or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t lastIndexOf(const char16_t *srcChars, + int32_t srcLength, + int32_t start) const; + + /** + * Locate in this the last occurrence in the range + * [`start`, `start + length`) of the characters + * in `srcChars`, using bitwise comparison. + * @param srcChars The text to search for. + * @param srcLength the number of characters in `srcChars` + * @param start The offset at which searching will start. + * @param length The number of characters to search + * @return The offset into this of the start of `srcChars`, + * or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t lastIndexOf(ConstChar16Ptr srcChars, + int32_t srcLength, + int32_t start, + int32_t length) const; + + /** + * Locate in this the last occurrence in the range + * [`start`, `start + length`) of the characters + * in `srcChars` in the range + * [`srcStart`, `srcStart + srcLength`), + * using bitwise comparison. + * @param srcChars The text to search for. + * @param srcStart the offset into `srcChars` at which + * to start matching + * @param srcLength the number of characters in `srcChars` to match + * @param start the offset into this at which to start matching + * @param length the number of characters in this to search + * @return The offset into this of the start of `text`, + * or -1 if not found. + * @stable ICU 2.0 + */ + int32_t lastIndexOf(const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength, + int32_t start, + int32_t length) const; + + /** + * Locate in this the last occurrence of the BMP code point `c`, + * using bitwise comparison. + * @param c The code unit to search for. + * @return The offset into this of `c`, or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t lastIndexOf(char16_t c) const; + + /** + * Locate in this the last occurrence of the code point `c`, + * using bitwise comparison. + * + * @param c The code point to search for. + * @return The offset into this of `c`, or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t lastIndexOf(UChar32 c) const; + + /** + * Locate in this the last occurrence of the BMP code point `c` + * starting at offset `start`, using bitwise comparison. + * @param c The code unit to search for. + * @param start The offset at which searching will start. + * @return The offset into this of `c`, or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t lastIndexOf(char16_t c, + int32_t start) const; + + /** + * Locate in this the last occurrence of the code point `c` + * starting at offset `start`, using bitwise comparison. + * + * @param c The code point to search for. + * @param start The offset at which searching will start. + * @return The offset into this of `c`, or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t lastIndexOf(UChar32 c, + int32_t start) const; + + /** + * Locate in this the last occurrence of the BMP code point `c` + * in the range [`start`, `start + length`), + * using bitwise comparison. + * @param c The code unit to search for. + * @param start the offset into this at which to start matching + * @param length the number of characters in this to search + * @return The offset into this of `c`, or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t lastIndexOf(char16_t c, + int32_t start, + int32_t length) const; + + /** + * Locate in this the last occurrence of the code point `c` + * in the range [`start`, `start + length`), + * using bitwise comparison. + * + * @param c The code point to search for. + * @param start the offset into this at which to start matching + * @param length the number of characters in this to search + * @return The offset into this of `c`, or -1 if not found. + * @stable ICU 2.0 + */ + inline int32_t lastIndexOf(UChar32 c, + int32_t start, + int32_t length) const; + + + /* Character access */ + + /** + * Return the code unit at offset `offset`. + * If the offset is not valid (0..length()-1) then U+ffff is returned. + * @param offset a valid offset into the text + * @return the code unit at offset `offset` + * or 0xffff if the offset is not valid for this string + * @stable ICU 2.0 + */ + inline char16_t charAt(int32_t offset) const; + + /** + * Return the code unit at offset `offset`. + * If the offset is not valid (0..length()-1) then U+ffff is returned. + * @param offset a valid offset into the text + * @return the code unit at offset `offset` + * @stable ICU 2.0 + */ + inline char16_t operator[] (int32_t offset) const; + + /** + * Return the code point that contains the code unit + * at offset `offset`. + * If the offset is not valid (0..length()-1) then U+ffff is returned. + * @param offset a valid offset into the text + * that indicates the text offset of any of the code units + * that will be assembled into a code point (21-bit value) and returned + * @return the code point of text at `offset` + * or 0xffff if the offset is not valid for this string + * @stable ICU 2.0 + */ + UChar32 char32At(int32_t offset) const; + + /** + * Adjust a random-access offset so that + * it points to the beginning of a Unicode character. + * The offset that is passed in points to + * any code unit of a code point, + * while the returned offset will point to the first code unit + * of the same code point. + * In UTF-16, if the input offset points to a second surrogate + * of a surrogate pair, then the returned offset will point + * to the first surrogate. + * @param offset a valid offset into one code point of the text + * @return offset of the first code unit of the same code point + * @see U16_SET_CP_START + * @stable ICU 2.0 + */ + int32_t getChar32Start(int32_t offset) const; + + /** + * Adjust a random-access offset so that + * it points behind a Unicode character. + * The offset that is passed in points behind + * any code unit of a code point, + * while the returned offset will point behind the last code unit + * of the same code point. + * In UTF-16, if the input offset points behind the first surrogate + * (i.e., to the second surrogate) + * of a surrogate pair, then the returned offset will point + * behind the second surrogate (i.e., to the first surrogate). + * @param offset a valid offset after any code unit of a code point of the text + * @return offset of the first code unit after the same code point + * @see U16_SET_CP_LIMIT + * @stable ICU 2.0 + */ + int32_t getChar32Limit(int32_t offset) const; + + /** + * Move the code unit index along the string by delta code points. + * Interpret the input index as a code unit-based offset into the string, + * move the index forward or backward by delta code points, and + * return the resulting index. + * The input index should point to the first code unit of a code point, + * if there is more than one. + * + * Both input and output indexes are code unit-based as for all + * string indexes/offsets in ICU (and other libraries, like MBCS char*). + * If delta<0 then the index is moved backward (toward the start of the string). + * If delta>0 then the index is moved forward (toward the end of the string). + * + * This behaves like CharacterIterator::move32(delta, kCurrent). + * + * Behavior for out-of-bounds indexes: + * `moveIndex32` pins the input index to 0..length(), i.e., + * if the input index<0 then it is pinned to 0; + * if it is index>length() then it is pinned to length(). + * Afterwards, the index is moved by `delta` code points + * forward or backward, + * but no further backward than to 0 and no further forward than to length(). + * The resulting index return value will be in between 0 and length(), inclusively. + * + * Examples: + * \code + * // s has code points 'a' U+10000 'b' U+10ffff U+2029 + * UnicodeString s(u"a\U00010000b\U0010ffff\u2029"); + * + * // initial index: position of U+10000 + * int32_t index=1; + * + * // the following examples will all result in index==4, position of U+10ffff + * + * // skip 2 code points from some position in the string + * index=s.moveIndex32(index, 2); // skips U+10000 and 'b' + * + * // go to the 3rd code point from the start of s (0-based) + * index=s.moveIndex32(0, 3); // skips 'a', U+10000, and 'b' + * + * // go to the next-to-last code point of s + * index=s.moveIndex32(s.length(), -2); // backward-skips U+2029 and U+10ffff + * \endcode + * + * @param index input code unit index + * @param delta (signed) code point count to move the index forward or backward + * in the string + * @return the resulting code unit index + * @stable ICU 2.0 + */ + int32_t moveIndex32(int32_t index, int32_t delta) const; + + /* Substring extraction */ + + /** + * Copy the characters in the range + * [`start`, `start + length`) into the array `dst`, + * beginning at `dstStart`. + * If the string aliases to `dst` itself as an external buffer, + * then extract() will not copy the contents. + * + * @param start offset of first character which will be copied into the array + * @param length the number of characters to extract + * @param dst array in which to copy characters. The length of `dst` + * must be at least (`dstStart + length`). + * @param dstStart the offset in `dst` where the first character + * will be extracted + * @stable ICU 2.0 + */ + inline void extract(int32_t start, + int32_t length, + Char16Ptr dst, + int32_t dstStart = 0) const; + + /** + * Copy the contents of the string into dest. + * This is a convenience function that + * checks if there is enough space in dest, + * extracts the entire string if possible, + * and NUL-terminates dest if possible. + * + * If the string fits into dest but cannot be NUL-terminated + * (length()==destCapacity) then the error code is set to U_STRING_NOT_TERMINATED_WARNING. + * If the string itself does not fit into dest + * (length()>destCapacity) then the error code is set to U_BUFFER_OVERFLOW_ERROR. + * + * If the string aliases to `dest` itself as an external buffer, + * then extract() will not copy the contents. + * + * @param dest Destination string buffer. + * @param destCapacity Number of char16_ts available at dest. + * @param errorCode ICU error code. + * @return length() + * @stable ICU 2.0 + */ + int32_t + extract(Char16Ptr dest, int32_t destCapacity, + UErrorCode &errorCode) const; + + /** + * Copy the characters in the range + * [`start`, `start + length`) into the UnicodeString + * `target`. + * @param start offset of first character which will be copied + * @param length the number of characters to extract + * @param target UnicodeString into which to copy characters. + * @stable ICU 2.0 + */ + inline void extract(int32_t start, + int32_t length, + UnicodeString& target) const; + + /** + * Copy the characters in the range [`start`, `limit`) + * into the array `dst`, beginning at `dstStart`. + * @param start offset of first character which will be copied into the array + * @param limit offset immediately following the last character to be copied + * @param dst array in which to copy characters. The length of `dst` + * must be at least (`dstStart + (limit - start)`). + * @param dstStart the offset in `dst` where the first character + * will be extracted + * @stable ICU 2.0 + */ + inline void extractBetween(int32_t start, + int32_t limit, + char16_t *dst, + int32_t dstStart = 0) const; + + /** + * Copy the characters in the range [`start`, `limit`) + * into the UnicodeString `target`. Replaceable API. + * @param start offset of first character which will be copied + * @param limit offset immediately following the last character to be copied + * @param target UnicodeString into which to copy characters. + * @stable ICU 2.0 + */ + virtual void extractBetween(int32_t start, + int32_t limit, + UnicodeString& target) const override; + + /** + * Copy the characters in the range + * [`start`, `start + startLength`) into an array of characters. + * All characters must be invariant (see utypes.h). + * Use US_INV as the last, signature-distinguishing parameter. + * + * This function does not write any more than `targetCapacity` + * characters but returns the length of the entire output string + * so that one can allocate a larger buffer and call the function again + * if necessary. + * The output string is NUL-terminated if possible. + * + * @param start offset of first character which will be copied + * @param startLength the number of characters to extract + * @param target the target buffer for extraction, can be nullptr + * if targetLength is 0 + * @param targetCapacity the length of the target buffer + * @param inv Signature-distinguishing parameter, use US_INV. + * @return the output string length, not including the terminating NUL + * @stable ICU 3.2 + */ + int32_t extract(int32_t start, + int32_t startLength, + char *target, + int32_t targetCapacity, + enum EInvariant inv) const; + +#if U_CHARSET_IS_UTF8 || !UCONFIG_NO_CONVERSION + + /** + * Copy the characters in the range + * [`start`, `start + length`) into an array of characters + * in the platform's default codepage. + * This function does not write any more than `targetLength` + * characters but returns the length of the entire output string + * so that one can allocate a larger buffer and call the function again + * if necessary. + * The output string is NUL-terminated if possible. + * + * @param start offset of first character which will be copied + * @param startLength the number of characters to extract + * @param target the target buffer for extraction + * @param targetLength the length of the target buffer + * If `target` is nullptr, then the number of bytes required for + * `target` is returned. + * @return the output string length, not including the terminating NUL + * @stable ICU 2.0 + */ + int32_t extract(int32_t start, + int32_t startLength, + char *target, + uint32_t targetLength) const; + +#endif + +#if !UCONFIG_NO_CONVERSION + + /** + * Copy the characters in the range + * [`start`, `start + length`) into an array of characters + * in a specified codepage. + * The output string is NUL-terminated. + * + * Recommendation: For invariant-character strings use + * extract(int32_t start, int32_t length, char *target, int32_t targetCapacity, enum EInvariant inv) const + * because it avoids object code dependencies of UnicodeString on + * the conversion code. + * + * @param start offset of first character which will be copied + * @param startLength the number of characters to extract + * @param target the target buffer for extraction + * @param codepage the desired codepage for the characters. 0 has + * the special meaning of the default codepage + * If `codepage` is an empty string (`""`), + * then a simple conversion is performed on the codepage-invariant + * subset ("invariant characters") of the platform encoding. See utypes.h. + * If `target` is nullptr, then the number of bytes required for + * `target` is returned. It is assumed that the target is big enough + * to fit all of the characters. + * @return the output string length, not including the terminating NUL + * @stable ICU 2.0 + */ + inline int32_t extract(int32_t start, + int32_t startLength, + char *target, + const char *codepage = 0) const; + + /** + * Copy the characters in the range + * [`start`, `start + length`) into an array of characters + * in a specified codepage. + * This function does not write any more than `targetLength` + * characters but returns the length of the entire output string + * so that one can allocate a larger buffer and call the function again + * if necessary. + * The output string is NUL-terminated if possible. + * + * Recommendation: For invariant-character strings use + * extract(int32_t start, int32_t length, char *target, int32_t targetCapacity, enum EInvariant inv) const + * because it avoids object code dependencies of UnicodeString on + * the conversion code. + * + * @param start offset of first character which will be copied + * @param startLength the number of characters to extract + * @param target the target buffer for extraction + * @param targetLength the length of the target buffer + * @param codepage the desired codepage for the characters. 0 has + * the special meaning of the default codepage + * If `codepage` is an empty string (`""`), + * then a simple conversion is performed on the codepage-invariant + * subset ("invariant characters") of the platform encoding. See utypes.h. + * If `target` is nullptr, then the number of bytes required for + * `target` is returned. + * @return the output string length, not including the terminating NUL + * @stable ICU 2.0 + */ + int32_t extract(int32_t start, + int32_t startLength, + char *target, + uint32_t targetLength, + const char *codepage) const; + + /** + * Convert the UnicodeString into a codepage string using an existing UConverter. + * The output string is NUL-terminated if possible. + * + * This function avoids the overhead of opening and closing a converter if + * multiple strings are extracted. + * + * @param dest destination string buffer, can be nullptr if destCapacity==0 + * @param destCapacity the number of chars available at dest + * @param cnv the converter object to be used (ucnv_resetFromUnicode() will be called), + * or nullptr for the default converter + * @param errorCode normal ICU error code + * @return the length of the output string, not counting the terminating NUL; + * if the length is greater than destCapacity, then the string will not fit + * and a buffer of the indicated length would need to be passed in + * @stable ICU 2.0 + */ + int32_t extract(char *dest, int32_t destCapacity, + UConverter *cnv, + UErrorCode &errorCode) const; + +#endif + + /** + * Create a temporary substring for the specified range. + * Unlike the substring constructor and setTo() functions, + * the object returned here will be a read-only alias (using getBuffer()) + * rather than copying the text. + * As a result, this substring operation is much faster but requires + * that the original string not be modified or deleted during the lifetime + * of the returned substring object. + * @param start offset of the first character visible in the substring + * @param length length of the substring + * @return a read-only alias UnicodeString object for the substring + * @stable ICU 4.4 + */ + UnicodeString tempSubString(int32_t start=0, int32_t length=INT32_MAX) const; + + /** + * Create a temporary substring for the specified range. + * Same as tempSubString(start, length) except that the substring range + * is specified as a (start, limit) pair (with an exclusive limit index) + * rather than a (start, length) pair. + * @param start offset of the first character visible in the substring + * @param limit offset immediately following the last character visible in the substring + * @return a read-only alias UnicodeString object for the substring + * @stable ICU 4.4 + */ + inline UnicodeString tempSubStringBetween(int32_t start, int32_t limit=INT32_MAX) const; + + /** + * Convert the UnicodeString to UTF-8 and write the result + * to a ByteSink. This is called by toUTF8String(). + * Unpaired surrogates are replaced with U+FFFD. + * Calls u_strToUTF8WithSub(). + * + * @param sink A ByteSink to which the UTF-8 version of the string is written. + * sink.Flush() is called at the end. + * @stable ICU 4.2 + * @see toUTF8String + */ + void toUTF8(ByteSink &sink) const; + + /** + * Convert the UnicodeString to UTF-8 and append the result + * to a standard string. + * Unpaired surrogates are replaced with U+FFFD. + * Calls toUTF8(). + * + * @param result A standard string (or a compatible object) + * to which the UTF-8 version of the string is appended. + * @return The string object. + * @stable ICU 4.2 + * @see toUTF8 + */ + template + StringClass &toUTF8String(StringClass &result) const { + StringByteSink sbs(&result, length()); + toUTF8(sbs); + return result; + } + + /** + * Convert the UnicodeString to UTF-32. + * Unpaired surrogates are replaced with U+FFFD. + * Calls u_strToUTF32WithSub(). + * + * @param utf32 destination string buffer, can be nullptr if capacity==0 + * @param capacity the number of UChar32s available at utf32 + * @param errorCode Standard 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.) + * @return The length of the UTF-32 string. + * @see fromUTF32 + * @stable ICU 4.2 + */ + int32_t toUTF32(UChar32 *utf32, int32_t capacity, UErrorCode &errorCode) const; + + /* Length operations */ + + /** + * Return the length of the UnicodeString object. + * The length is the number of char16_t code units are in the UnicodeString. + * If you want the number of code points, please use countChar32(). + * @return the length of the UnicodeString object + * @see countChar32 + * @stable ICU 2.0 + */ + inline int32_t length(void) const; + + /** + * Count Unicode code points in the length char16_t code units of the string. + * A code point may occupy either one or two char16_t code units. + * Counting code points involves reading all code units. + * + * This functions is basically the inverse of moveIndex32(). + * + * @param start the index of the first code unit to check + * @param length the number of char16_t code units to check + * @return the number of code points in the specified code units + * @see length + * @stable ICU 2.0 + */ + int32_t + countChar32(int32_t start=0, int32_t length=INT32_MAX) const; + + /** + * Check if the length char16_t code units of the string + * contain more Unicode code points than a certain number. + * This is more efficient than counting all code points in this part of the string + * and comparing that number with a threshold. + * This function may not need to scan the string at all if the length + * falls within a certain range, and + * never needs to count more than 'number+1' code points. + * Logically equivalent to (countChar32(start, length)>number). + * A Unicode code point may occupy either one or two char16_t code units. + * + * @param start the index of the first code unit to check (0 for the entire string) + * @param length the number of char16_t code units to check + * (use INT32_MAX for the entire string; remember that start/length + * values are pinned) + * @param number The number of code points in the (sub)string is compared against + * the 'number' parameter. + * @return Boolean value for whether the string contains more Unicode code points + * than 'number'. Same as (u_countChar32(s, length)>number). + * @see countChar32 + * @see u_strHasMoreChar32Than + * @stable ICU 2.4 + */ + UBool + hasMoreChar32Than(int32_t start, int32_t length, int32_t number) const; + + /** + * Determine if this string is empty. + * @return true if this string contains 0 characters, false otherwise. + * @stable ICU 2.0 + */ + inline UBool isEmpty(void) const; + + /** + * Return the capacity of the internal buffer of the UnicodeString object. + * This is useful together with the getBuffer functions. + * See there for details. + * + * @return the number of char16_ts available in the internal buffer + * @see getBuffer + * @stable ICU 2.0 + */ + inline int32_t getCapacity(void) const; + + /* Other operations */ + + /** + * Generate a hash code for this object. + * @return The hash code of this UnicodeString. + * @stable ICU 2.0 + */ + inline int32_t hashCode(void) const; + + /** + * Determine if this object contains a valid string. + * A bogus string has no value. It is different from an empty string, + * although in both cases isEmpty() returns true and length() returns 0. + * setToBogus() and isBogus() can be used to indicate that no string value is available. + * For a bogus string, getBuffer() and getTerminatedBuffer() return nullptr, and + * length() returns 0. + * + * @return true if the string is bogus/invalid, false otherwise + * @see setToBogus() + * @stable ICU 2.0 + */ + inline UBool isBogus(void) const; + + + //======================================== + // Write operations + //======================================== + + /* Assignment operations */ + + /** + * Assignment operator. Replace the characters in this UnicodeString + * with the characters from `srcText`. + * + * Starting with ICU 2.4, the assignment operator and the copy constructor + * allocate a new buffer and copy the buffer contents even for readonly aliases. + * By contrast, the fastCopyFrom() function implements the old, + * more efficient but less safe behavior + * of making this string also a readonly alias to the same buffer. + * + * If the source object has an "open" buffer from getBuffer(minCapacity), + * then the copy is an empty string. + * + * @param srcText The text containing the characters to replace + * @return a reference to this + * @stable ICU 2.0 + * @see fastCopyFrom + */ + UnicodeString &operator=(const UnicodeString &srcText); + + /** + * Almost the same as the assignment operator. + * Replace the characters in this UnicodeString + * with the characters from `srcText`. + * + * This function works the same as the assignment operator + * for all strings except for ones that are readonly aliases. + * + * Starting with ICU 2.4, the assignment operator and the copy constructor + * allocate a new buffer and copy the buffer contents even for readonly aliases. + * This function implements the old, more efficient but less safe behavior + * of making this string also a readonly alias to the same buffer. + * + * The fastCopyFrom function must be used only if it is known that the lifetime of + * this UnicodeString does not exceed the lifetime of the aliased buffer + * including its contents, for example for strings from resource bundles + * or aliases to string constants. + * + * If the source object has an "open" buffer from getBuffer(minCapacity), + * then the copy is an empty string. + * + * @param src The text containing the characters to replace. + * @return a reference to this + * @stable ICU 2.4 + */ + UnicodeString &fastCopyFrom(const UnicodeString &src); + + /** + * 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 + * @return *this + * @stable ICU 56 + */ + UnicodeString &operator=(UnicodeString &&src) noexcept; + + /** + * Swap strings. + * @param other other string + * @stable ICU 56 + */ + void swap(UnicodeString &other) noexcept; + + /** + * Non-member UnicodeString swap function. + * @param s1 will get s2's contents and state + * @param s2 will get s1's contents and state + * @stable ICU 56 + */ + friend inline void U_EXPORT2 + swap(UnicodeString &s1, UnicodeString &s2) noexcept { + s1.swap(s2); + } + + /** + * Assignment operator. Replace the characters in this UnicodeString + * with the code unit `ch`. + * @param ch the code unit to replace + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& operator= (char16_t ch); + + /** + * Assignment operator. Replace the characters in this UnicodeString + * with the code point `ch`. + * @param ch the code point to replace + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& operator= (UChar32 ch); + + /** + * Set the text in the UnicodeString object to the characters + * in `srcText` in the range + * [`srcStart`, `srcText.length()`). + * `srcText` is not modified. + * @param srcText the source for the new characters + * @param srcStart the offset into `srcText` where new characters + * will be obtained + * @return a reference to this + * @stable ICU 2.2 + */ + inline UnicodeString& setTo(const UnicodeString& srcText, + int32_t srcStart); + + /** + * Set the text in the UnicodeString object to the characters + * in `srcText` in the range + * [`srcStart`, `srcStart + srcLength`). + * `srcText` is not modified. + * @param srcText the source for the new characters + * @param srcStart the offset into `srcText` where new characters + * will be obtained + * @param srcLength the number of characters in `srcText` in the + * replace string. + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& setTo(const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength); + + /** + * Set the text in the UnicodeString object to the characters in + * `srcText`. + * `srcText` is not modified. + * @param srcText the source for the new characters + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& setTo(const UnicodeString& srcText); + + /** + * Set the characters in the UnicodeString object to the characters + * in `srcChars`. `srcChars` is not modified. + * @param srcChars the source for the new characters + * @param srcLength the number of Unicode characters in srcChars. + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& setTo(const char16_t *srcChars, + int32_t srcLength); + + /** + * Set the characters in the UnicodeString object to the code unit + * `srcChar`. + * @param srcChar the code unit which becomes the UnicodeString's character + * content + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& setTo(char16_t srcChar); + + /** + * Set the characters in the UnicodeString object to the code point + * `srcChar`. + * @param srcChar the code point which becomes the UnicodeString's character + * content + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& setTo(UChar32 srcChar); + + /** + * Aliasing setTo() function, analogous to the readonly-aliasing char16_t* constructor. + * The text will be used for the UnicodeString object, but + * it will not be released when the UnicodeString is destroyed. + * This has copy-on-write semantics: + * When the string is modified, then the buffer is first copied into + * newly allocated memory. + * The aliased buffer is never modified. + * + * In an assignment to another UnicodeString, when using the copy constructor + * or the assignment operator, the text will be copied. + * When using fastCopyFrom(), the text will be aliased again, + * so that both strings then alias the same readonly-text. + * + * @param isTerminated specifies if `text` is `NUL`-terminated. + * This must be true if `textLength==-1`. + * @param text The characters to alias for the UnicodeString. + * @param textLength The number of Unicode characters in `text` to alias. + * If -1, then this constructor will determine the length + * by calling `u_strlen()`. + * @return a reference to this + * @stable ICU 2.0 + */ + UnicodeString &setTo(UBool isTerminated, + ConstChar16Ptr text, + int32_t textLength); + + /** + * Aliasing setTo() function, analogous to the writable-aliasing char16_t* constructor. + * The text will be used for the UnicodeString object, but + * it will not be released when the UnicodeString is destroyed. + * This has write-through semantics: + * For as long as the capacity of the buffer is sufficient, write operations + * will directly affect the buffer. When more capacity is necessary, then + * a new buffer will be allocated and the contents copied as with regularly + * constructed strings. + * In an assignment to another UnicodeString, the buffer will be copied. + * The extract(Char16Ptr dst) function detects whether the dst pointer is the same + * as the string buffer itself and will in this case not copy the contents. + * + * @param buffer The characters to alias for the UnicodeString. + * @param buffLength The number of Unicode characters in `buffer` to alias. + * @param buffCapacity The size of `buffer` in char16_ts. + * @return a reference to this + * @stable ICU 2.0 + */ + UnicodeString &setTo(char16_t *buffer, + int32_t buffLength, + int32_t buffCapacity); + + /** + * Make this UnicodeString object invalid. + * The string will test true with isBogus(). + * + * A bogus string has no value. It is different from an empty string. + * It can be used to indicate that no string value is available. + * getBuffer() and getTerminatedBuffer() return nullptr, and + * length() returns 0. + * + * This utility function is used throughout the UnicodeString + * implementation to indicate that a UnicodeString operation failed, + * and may be used in other functions, + * especially but not exclusively when such functions do not + * take a UErrorCode for simplicity. + * + * The following methods, and no others, will clear a string object's bogus flag: + * - remove() + * - remove(0, INT32_MAX) + * - truncate(0) + * - operator=() (assignment operator) + * - setTo(...) + * + * The simplest ways to turn a bogus string into an empty one + * is to use the remove() function. + * Examples for other functions that are equivalent to "set to empty string": + * \code + * if(s.isBogus()) { + * s.remove(); // set to an empty string (remove all), or + * s.remove(0, INT32_MAX); // set to an empty string (remove all), or + * s.truncate(0); // set to an empty string (complete truncation), or + * s=UnicodeString(); // assign an empty string, or + * s.setTo((UChar32)-1); // set to a pseudo code point that is out of range, or + * s.setTo(u"", 0); // set to an empty C Unicode string + * } + * \endcode + * + * @see isBogus() + * @stable ICU 2.0 + */ + void setToBogus(); + + /** + * Set the character at the specified offset to the specified character. + * @param offset A valid offset into the text of the character to set + * @param ch The new character + * @return A reference to this + * @stable ICU 2.0 + */ + UnicodeString& setCharAt(int32_t offset, + char16_t ch); + + + /* Append operations */ + + /** + * Append operator. Append the code unit `ch` to the UnicodeString + * object. + * @param ch the code unit to be appended + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& operator+= (char16_t ch); + + /** + * Append operator. Append the code point `ch` to the UnicodeString + * object. + * @param ch the code point to be appended + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& operator+= (UChar32 ch); + + /** + * Append operator. Append the characters in `srcText` to the + * UnicodeString object. `srcText` is not modified. + * @param srcText the source for the new characters + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& operator+= (const UnicodeString& srcText); + + /** + * Append the characters + * in `srcText` in the range + * [`srcStart`, `srcStart + srcLength`) to the + * UnicodeString object at offset `start`. `srcText` + * is not modified. + * @param srcText the source for the new characters + * @param srcStart the offset into `srcText` where new characters + * will be obtained + * @param srcLength the number of characters in `srcText` in + * the append string + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& append(const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength); + + /** + * Append the characters in `srcText` to the UnicodeString object. + * `srcText` is not modified. + * @param srcText the source for the new characters + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& append(const UnicodeString& srcText); + + /** + * Append the characters in `srcChars` in the range + * [`srcStart`, `srcStart + srcLength`) to the UnicodeString + * object at offset + * `start`. `srcChars` is not modified. + * @param srcChars the source for the new characters + * @param srcStart the offset into `srcChars` where new characters + * will be obtained + * @param srcLength the number of characters in `srcChars` in + * the append string; can be -1 if `srcChars` is NUL-terminated + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& append(const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength); + + /** + * Append the characters in `srcChars` to the UnicodeString object + * at offset `start`. `srcChars` is not modified. + * @param srcChars the source for the new characters + * @param srcLength the number of Unicode characters in `srcChars`; + * can be -1 if `srcChars` is NUL-terminated + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& append(ConstChar16Ptr srcChars, + int32_t srcLength); + + /** + * Append the code unit `srcChar` to the UnicodeString object. + * @param srcChar the code unit to append + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& append(char16_t srcChar); + + /** + * Append the code point `srcChar` to the UnicodeString object. + * @param srcChar the code point to append + * @return a reference to this + * @stable ICU 2.0 + */ + UnicodeString& append(UChar32 srcChar); + + + /* Insert operations */ + + /** + * Insert the characters in `srcText` in the range + * [`srcStart`, `srcStart + srcLength`) into the UnicodeString + * object at offset `start`. `srcText` is not modified. + * @param start the offset where the insertion begins + * @param srcText the source for the new characters + * @param srcStart the offset into `srcText` where new characters + * will be obtained + * @param srcLength the number of characters in `srcText` in + * the insert string + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& insert(int32_t start, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength); + + /** + * Insert the characters in `srcText` into the UnicodeString object + * at offset `start`. `srcText` is not modified. + * @param start the offset where the insertion begins + * @param srcText the source for the new characters + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& insert(int32_t start, + const UnicodeString& srcText); + + /** + * Insert the characters in `srcChars` in the range + * [`srcStart`, `srcStart + srcLength`) into the UnicodeString + * object at offset `start`. `srcChars` is not modified. + * @param start the offset at which the insertion begins + * @param srcChars the source for the new characters + * @param srcStart the offset into `srcChars` where new characters + * will be obtained + * @param srcLength the number of characters in `srcChars` + * in the insert string + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& insert(int32_t start, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength); + + /** + * Insert the characters in `srcChars` into the UnicodeString object + * at offset `start`. `srcChars` is not modified. + * @param start the offset where the insertion begins + * @param srcChars the source for the new characters + * @param srcLength the number of Unicode characters in srcChars. + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& insert(int32_t start, + ConstChar16Ptr srcChars, + int32_t srcLength); + + /** + * Insert the code unit `srcChar` into the UnicodeString object at + * offset `start`. + * @param start the offset at which the insertion occurs + * @param srcChar the code unit to insert + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& insert(int32_t start, + char16_t srcChar); + + /** + * Insert the code point `srcChar` into the UnicodeString object at + * offset `start`. + * @param start the offset at which the insertion occurs + * @param srcChar the code point to insert + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& insert(int32_t start, + UChar32 srcChar); + + + /* Replace operations */ + + /** + * Replace the characters in the range + * [`start`, `start + length`) with the characters in + * `srcText` in the range + * [`srcStart`, `srcStart + srcLength`). + * `srcText` is not modified. + * @param start the offset at which the replace operation begins + * @param length the number of characters to replace. The character at + * `start + length` is not modified. + * @param srcText the source for the new characters + * @param srcStart the offset into `srcText` where new characters + * will be obtained + * @param srcLength the number of characters in `srcText` in + * the replace string + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& replace(int32_t start, + int32_t length, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength); + + /** + * Replace the characters in the range + * [`start`, `start + length`) + * with the characters in `srcText`. `srcText` is + * not modified. + * @param start the offset at which the replace operation begins + * @param length the number of characters to replace. The character at + * `start + length` is not modified. + * @param srcText the source for the new characters + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& replace(int32_t start, + int32_t length, + const UnicodeString& srcText); + + /** + * Replace the characters in the range + * [`start`, `start + length`) with the characters in + * `srcChars` in the range + * [`srcStart`, `srcStart + srcLength`). `srcChars` + * is not modified. + * @param start the offset at which the replace operation begins + * @param length the number of characters to replace. The character at + * `start + length` is not modified. + * @param srcChars the source for the new characters + * @param srcStart the offset into `srcChars` where new characters + * will be obtained + * @param srcLength the number of characters in `srcChars` + * in the replace string + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& replace(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength); + + /** + * Replace the characters in the range + * [`start`, `start + length`) with the characters in + * `srcChars`. `srcChars` is not modified. + * @param start the offset at which the replace operation begins + * @param length number of characters to replace. The character at + * `start + length` is not modified. + * @param srcChars the source for the new characters + * @param srcLength the number of Unicode characters in srcChars + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& replace(int32_t start, + int32_t length, + ConstChar16Ptr srcChars, + int32_t srcLength); + + /** + * Replace the characters in the range + * [`start`, `start + length`) with the code unit + * `srcChar`. + * @param start the offset at which the replace operation begins + * @param length the number of characters to replace. The character at + * `start + length` is not modified. + * @param srcChar the new code unit + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& replace(int32_t start, + int32_t length, + char16_t srcChar); + + /** + * Replace the characters in the range + * [`start`, `start + length`) with the code point + * `srcChar`. + * @param start the offset at which the replace operation begins + * @param length the number of characters to replace. The character at + * `start + length` is not modified. + * @param srcChar the new code point + * @return a reference to this + * @stable ICU 2.0 + */ + UnicodeString& replace(int32_t start, int32_t length, UChar32 srcChar); + + /** + * Replace the characters in the range [`start`, `limit`) + * with the characters in `srcText`. `srcText` is not modified. + * @param start the offset at which the replace operation begins + * @param limit the offset immediately following the replace range + * @param srcText the source for the new characters + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& replaceBetween(int32_t start, + int32_t limit, + const UnicodeString& srcText); + + /** + * Replace the characters in the range [`start`, `limit`) + * with the characters in `srcText` in the range + * [`srcStart`, `srcLimit`). `srcText` is not modified. + * @param start the offset at which the replace operation begins + * @param limit the offset immediately following the replace range + * @param srcText the source for the new characters + * @param srcStart the offset into `srcChars` where new characters + * will be obtained + * @param srcLimit the offset immediately following the range to copy + * in `srcText` + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& replaceBetween(int32_t start, + int32_t limit, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLimit); + + /** + * Replace a substring of this object with the given text. + * @param start the beginning index, inclusive; `0 <= start <= limit`. + * @param limit the ending index, exclusive; `start <= limit <= length()`. + * @param text the text to replace characters `start` to `limit - 1` + * @stable ICU 2.0 + */ + virtual void handleReplaceBetween(int32_t start, + int32_t limit, + const UnicodeString& text) override; + + /** + * Replaceable API + * @return true if it has MetaData + * @stable ICU 2.4 + */ + virtual UBool hasMetaData() const override; + + /** + * Copy a substring of this object, retaining attribute (out-of-band) + * information. This method is used to duplicate or reorder substrings. + * The destination index must not overlap the source range. + * + * @param start the beginning index, inclusive; `0 <= start <= limit`. + * @param limit the ending index, exclusive; `start <= limit <= length()`. + * @param dest the destination index. The characters from + * `start..limit-1` will be copied to `dest`. + * Implementations of this method may assume that `dest <= start || + * dest >= limit`. + * @stable ICU 2.0 + */ + virtual void copy(int32_t start, int32_t limit, int32_t dest) override; + + /* Search and replace operations */ + + /** + * Replace all occurrences of characters in oldText with the characters + * in newText + * @param oldText the text containing the search text + * @param newText the text containing the replacement text + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& findAndReplace(const UnicodeString& oldText, + const UnicodeString& newText); + + /** + * Replace all occurrences of characters in oldText with characters + * in newText + * in the range [`start`, `start + length`). + * @param start the start of the range in which replace will performed + * @param length the length of the range in which replace will be performed + * @param oldText the text containing the search text + * @param newText the text containing the replacement text + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& findAndReplace(int32_t start, + int32_t length, + const UnicodeString& oldText, + const UnicodeString& newText); + + /** + * Replace all occurrences of characters in oldText in the range + * [`oldStart`, `oldStart + oldLength`) with the characters + * in newText in the range + * [`newStart`, `newStart + newLength`) + * in the range [`start`, `start + length`). + * @param start the start of the range in which replace will performed + * @param length the length of the range in which replace will be performed + * @param oldText the text containing the search text + * @param oldStart the start of the search range in `oldText` + * @param oldLength the length of the search range in `oldText` + * @param newText the text containing the replacement text + * @param newStart the start of the replacement range in `newText` + * @param newLength the length of the replacement range in `newText` + * @return a reference to this + * @stable ICU 2.0 + */ + UnicodeString& findAndReplace(int32_t start, + int32_t length, + const UnicodeString& oldText, + int32_t oldStart, + int32_t oldLength, + const UnicodeString& newText, + int32_t newStart, + int32_t newLength); + + + /* Remove operations */ + + /** + * Removes all characters from the UnicodeString object and clears the bogus flag. + * This is the UnicodeString equivalent of std::string’s clear(). + * + * @return a reference to this + * @see setToBogus + * @stable ICU 2.0 + */ + inline UnicodeString& remove(); + + /** + * Remove the characters in the range + * [`start`, `start + length`) from the UnicodeString object. + * @param start the offset of the first character to remove + * @param length the number of characters to remove + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& remove(int32_t start, + int32_t length = (int32_t)INT32_MAX); + + /** + * Remove the characters in the range + * [`start`, `limit`) from the UnicodeString object. + * @param start the offset of the first character to remove + * @param limit the offset immediately following the range to remove + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& removeBetween(int32_t start, + int32_t limit = (int32_t)INT32_MAX); + + /** + * Retain only the characters in the range + * [`start`, `limit`) from the UnicodeString object. + * Removes characters before `start` and at and after `limit`. + * @param start the offset of the first character to retain + * @param limit the offset immediately following the range to retain + * @return a reference to this + * @stable ICU 4.4 + */ + inline UnicodeString &retainBetween(int32_t start, int32_t limit = INT32_MAX); + + /* Length operations */ + + /** + * Pad the start of this UnicodeString with the character `padChar`. + * If the length of this UnicodeString is less than targetLength, + * length() - targetLength copies of padChar will be added to the + * beginning of this UnicodeString. + * @param targetLength the desired length of the string + * @param padChar the character to use for padding. Defaults to + * space (U+0020) + * @return true if the text was padded, false otherwise. + * @stable ICU 2.0 + */ + UBool padLeading(int32_t targetLength, + char16_t padChar = 0x0020); + + /** + * Pad the end of this UnicodeString with the character `padChar`. + * If the length of this UnicodeString is less than targetLength, + * length() - targetLength copies of padChar will be added to the + * end of this UnicodeString. + * @param targetLength the desired length of the string + * @param padChar the character to use for padding. Defaults to + * space (U+0020) + * @return true if the text was padded, false otherwise. + * @stable ICU 2.0 + */ + UBool padTrailing(int32_t targetLength, + char16_t padChar = 0x0020); + + /** + * Truncate this UnicodeString to the `targetLength`. + * @param targetLength the desired length of this UnicodeString. + * @return true if the text was truncated, false otherwise + * @stable ICU 2.0 + */ + inline UBool truncate(int32_t targetLength); + + /** + * Trims leading and trailing whitespace from this UnicodeString. + * @return a reference to this + * @stable ICU 2.0 + */ + UnicodeString& trim(void); + + + /* Miscellaneous operations */ + + /** + * Reverse this UnicodeString in place. + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& reverse(void); + + /** + * Reverse the range [`start`, `start + length`) in + * this UnicodeString. + * @param start the start of the range to reverse + * @param length the number of characters to to reverse + * @return a reference to this + * @stable ICU 2.0 + */ + inline UnicodeString& reverse(int32_t start, + int32_t length); + + /** + * Convert the characters in this to UPPER CASE following the conventions of + * the default locale. + * @return A reference to this. + * @stable ICU 2.0 + */ + UnicodeString& toUpper(void); + + /** + * Convert the characters in this to UPPER CASE following the conventions of + * a specific locale. + * @param locale The locale containing the conventions to use. + * @return A reference to this. + * @stable ICU 2.0 + */ + UnicodeString& toUpper(const Locale& locale); + + /** + * Convert the characters in this to lower case following the conventions of + * the default locale. + * @return A reference to this. + * @stable ICU 2.0 + */ + UnicodeString& toLower(void); + + /** + * Convert the characters in this to lower case following the conventions of + * a specific locale. + * @param locale The locale containing the conventions to use. + * @return A reference to this. + * @stable ICU 2.0 + */ + UnicodeString& toLower(const Locale& locale); + +#if !UCONFIG_NO_BREAK_ITERATION + + /** + * Titlecase this string, convenience function using the default locale. + * + * Casing is locale-dependent and context-sensitive. + * Titlecasing uses a break iterator to find the first characters of words + * that are to be titlecased. It titlecases those characters and lowercases + * all others. + * + * The titlecase break iterator can be provided to customize for arbitrary + * styles, using rules and dictionaries beyond the standard iterators. + * It may be more efficient to always provide an iterator to avoid + * opening and closing one for each string. + * The standard titlecase iterator for the root locale implements the + * algorithm of Unicode TR 21. + * + * This function uses only the setText(), first() and next() methods of the + * provided break iterator. + * + * @param titleIter A break iterator to find the first characters of words + * that are to be titlecased. + * If none is provided (0), then a standard titlecase + * break iterator is opened. + * Otherwise the provided iterator is set to the string's text. + * @return A reference to this. + * @stable ICU 2.1 + */ + UnicodeString &toTitle(BreakIterator *titleIter); + + /** + * Titlecase this string. + * + * Casing is locale-dependent and context-sensitive. + * Titlecasing uses a break iterator to find the first characters of words + * that are to be titlecased. It titlecases those characters and lowercases + * all others. + * + * The titlecase break iterator can be provided to customize for arbitrary + * styles, using rules and dictionaries beyond the standard iterators. + * It may be more efficient to always provide an iterator to avoid + * opening and closing one for each string. + * The standard titlecase iterator for the root locale implements the + * algorithm of Unicode TR 21. + * + * This function uses only the setText(), first() and next() methods of the + * provided break iterator. + * + * @param titleIter A break iterator to find the first characters of words + * that are to be titlecased. + * If none is provided (0), then a standard titlecase + * break iterator is opened. + * Otherwise the provided iterator is set to the string's text. + * @param locale The locale to consider. + * @return A reference to this. + * @stable ICU 2.1 + */ + UnicodeString &toTitle(BreakIterator *titleIter, const Locale &locale); + + /** + * Titlecase this string, with options. + * + * Casing is locale-dependent and context-sensitive. + * Titlecasing uses a break iterator to find the first characters of words + * that are to be titlecased. It titlecases those characters and lowercases + * all others. (This can be modified with options.) + * + * The titlecase break iterator can be provided to customize for arbitrary + * styles, using rules and dictionaries beyond the standard iterators. + * It may be more efficient to always provide an iterator to avoid + * opening and closing one for each string. + * The standard titlecase iterator for the root locale implements the + * algorithm of Unicode TR 21. + * + * This function uses only the setText(), first() and next() methods of the + * provided break iterator. + * + * @param titleIter A break iterator to find the first characters of words + * that are to be titlecased. + * If none is provided (0), then a standard titlecase + * break iterator is opened. + * Otherwise the provided iterator is set to the string's text. + * @param locale The locale to consider. + * @param options Options bit set, usually 0. See U_TITLECASE_NO_LOWERCASE, + * U_TITLECASE_NO_BREAK_ADJUSTMENT, U_TITLECASE_ADJUST_TO_CASED, + * U_TITLECASE_WHOLE_STRING, U_TITLECASE_SENTENCES. + * @return A reference to this. + * @stable ICU 3.8 + */ + UnicodeString &toTitle(BreakIterator *titleIter, const Locale &locale, uint32_t options); + +#endif + + /** + * Case-folds the characters in this string. + * + * Case-folding is locale-independent and not context-sensitive, + * but there is an option for whether to include or exclude mappings for dotted I + * and dotless i that are marked with 'T' in CaseFolding.txt. + * + * The result may be longer or shorter than the original. + * + * @param options Either U_FOLD_CASE_DEFAULT or U_FOLD_CASE_EXCLUDE_SPECIAL_I + * @return A reference to this. + * @stable ICU 2.0 + */ + UnicodeString &foldCase(uint32_t options=0 /*U_FOLD_CASE_DEFAULT*/); + + //======================================== + // Access to the internal buffer + //======================================== + + /** + * Get a read/write pointer to the internal buffer. + * The buffer is guaranteed to be large enough for at least minCapacity char16_ts, + * writable, and is still owned by the UnicodeString object. + * Calls to getBuffer(minCapacity) must not be nested, and + * must be matched with calls to releaseBuffer(newLength). + * If the string buffer was read-only or shared, + * then it will be reallocated and copied. + * + * An attempted nested call will return 0, and will not further modify the + * state of the UnicodeString object. + * It also returns 0 if the string is bogus. + * + * The actual capacity of the string buffer may be larger than minCapacity. + * getCapacity() returns the actual capacity. + * For many operations, the full capacity should be used to avoid reallocations. + * + * While the buffer is "open" between getBuffer(minCapacity) + * and releaseBuffer(newLength), the following applies: + * - The string length is set to 0. + * - Any read API call on the UnicodeString object will behave like on a 0-length string. + * - Any write API call on the UnicodeString object is disallowed and will have no effect. + * - You can read from and write to the returned buffer. + * - The previous string contents will still be in the buffer; + * if you want to use it, then you need to call length() before getBuffer(minCapacity). + * If the length() was greater than minCapacity, then any contents after minCapacity + * may be lost. + * The buffer contents is not NUL-terminated by getBuffer(). + * If length() < getCapacity() then you can terminate it by writing a NUL + * at index length(). + * - You must call releaseBuffer(newLength) before and in order to + * return to normal UnicodeString operation. + * + * @param minCapacity the minimum number of char16_ts that are to be available + * in the buffer, starting at the returned pointer; + * default to the current string capacity if minCapacity==-1 + * @return a writable pointer to the internal string buffer, + * or nullptr if an error occurs (nested calls, out of memory) + * + * @see releaseBuffer + * @see getTerminatedBuffer() + * @stable ICU 2.0 + */ + char16_t *getBuffer(int32_t minCapacity); + + /** + * Release a read/write buffer on a UnicodeString object with an + * "open" getBuffer(minCapacity). + * This function must be called in a matched pair with getBuffer(minCapacity). + * releaseBuffer(newLength) must be called if and only if a getBuffer(minCapacity) is "open". + * + * It will set the string length to newLength, at most to the current capacity. + * If newLength==-1 then it will set the length according to the + * first NUL in the buffer, or to the capacity if there is no NUL. + * + * After calling releaseBuffer(newLength) the UnicodeString is back to normal operation. + * + * @param newLength the new length of the UnicodeString object; + * defaults to the current capacity if newLength is greater than that; + * if newLength==-1, it defaults to u_strlen(buffer) but not more than + * the current capacity of the string + * + * @see getBuffer(int32_t minCapacity) + * @stable ICU 2.0 + */ + void releaseBuffer(int32_t newLength=-1); + + /** + * Get a read-only pointer to the internal buffer. + * This can be called at any time on a valid UnicodeString. + * + * It returns 0 if the string is bogus, or + * during an "open" getBuffer(minCapacity). + * + * It can be called as many times as desired. + * The pointer that it returns will remain valid until the UnicodeString object is modified, + * at which time the pointer is semantically invalidated and must not be used any more. + * + * The capacity of the buffer can be determined with getCapacity(). + * The part after length() may or may not be initialized and valid, + * depending on the history of the UnicodeString object. + * + * The buffer contents is (probably) not NUL-terminated. + * You can check if it is with + * `(s.length() < s.getCapacity() && buffer[s.length()]==0)`. + * (See getTerminatedBuffer().) + * + * The buffer may reside in read-only memory. Its contents must not + * be modified. + * + * @return a read-only pointer to the internal string buffer, + * or nullptr if the string is empty or bogus + * + * @see getBuffer(int32_t minCapacity) + * @see getTerminatedBuffer() + * @stable ICU 2.0 + */ + inline const char16_t *getBuffer() const; + + /** + * Get a read-only pointer to the internal buffer, + * making sure that it is NUL-terminated. + * This can be called at any time on a valid UnicodeString. + * + * It returns 0 if the string is bogus, or + * during an "open" getBuffer(minCapacity), or if the buffer cannot + * be NUL-terminated (because memory allocation failed). + * + * It can be called as many times as desired. + * The pointer that it returns will remain valid until the UnicodeString object is modified, + * at which time the pointer is semantically invalidated and must not be used any more. + * + * The capacity of the buffer can be determined with getCapacity(). + * The part after length()+1 may or may not be initialized and valid, + * depending on the history of the UnicodeString object. + * + * The buffer contents is guaranteed to be NUL-terminated. + * getTerminatedBuffer() may reallocate the buffer if a terminating NUL + * is written. + * For this reason, this function is not const, unlike getBuffer(). + * Note that a UnicodeString may also contain NUL characters as part of its contents. + * + * The buffer may reside in read-only memory. Its contents must not + * be modified. + * + * @return a read-only pointer to the internal string buffer, + * or 0 if the string is empty or bogus + * + * @see getBuffer(int32_t minCapacity) + * @see getBuffer() + * @stable ICU 2.2 + */ + const char16_t *getTerminatedBuffer(); + + //======================================== + // Constructors + //======================================== + + /** Construct an empty UnicodeString. + * @stable ICU 2.0 + */ + inline UnicodeString(); + + /** + * Construct a UnicodeString with capacity to hold `capacity` char16_ts + * @param capacity the number of char16_ts this UnicodeString should hold + * before a resize is necessary; if count is greater than 0 and count + * code points c take up more space than capacity, then capacity is adjusted + * accordingly. + * @param c is used to initially fill the string + * @param count specifies how many code points c are to be written in the + * string + * @stable ICU 2.0 + */ + UnicodeString(int32_t capacity, UChar32 c, int32_t count); + + /** + * Single char16_t (code unit) constructor. + * + * It is recommended to mark this constructor "explicit" by + * `-DUNISTR_FROM_CHAR_EXPLICIT=explicit` + * on the compiler command line or similar. + * @param ch the character to place in the UnicodeString + * @stable ICU 2.0 + */ + UNISTR_FROM_CHAR_EXPLICIT UnicodeString(char16_t ch); + + /** + * Single UChar32 (code point) constructor. + * + * It is recommended to mark this constructor "explicit" by + * `-DUNISTR_FROM_CHAR_EXPLICIT=explicit` + * on the compiler command line or similar. + * @param ch the character to place in the UnicodeString + * @stable ICU 2.0 + */ + UNISTR_FROM_CHAR_EXPLICIT UnicodeString(UChar32 ch); + + /** + * char16_t* constructor. + * + * It is recommended to mark this constructor "explicit" by + * `-DUNISTR_FROM_STRING_EXPLICIT=explicit` + * on the compiler command line or similar. + * @param text The characters to place in the UnicodeString. `text` + * must be NUL (U+0000) terminated. + * @stable ICU 2.0 + */ + UNISTR_FROM_STRING_EXPLICIT UnicodeString(const char16_t *text); + +#if !U_CHAR16_IS_TYPEDEF + /** + * uint16_t * constructor. + * Delegates to UnicodeString(const char16_t *). + * + * It is recommended to mark this constructor "explicit" by + * `-DUNISTR_FROM_STRING_EXPLICIT=explicit` + * on the compiler command line or similar. + * @param text NUL-terminated UTF-16 string + * @stable ICU 59 + */ + UNISTR_FROM_STRING_EXPLICIT UnicodeString(const uint16_t *text) : + UnicodeString(ConstChar16Ptr(text)) {} +#endif + +#if U_SIZEOF_WCHAR_T==2 || defined(U_IN_DOXYGEN) + /** + * wchar_t * constructor. + * (Only defined if U_SIZEOF_WCHAR_T==2.) + * Delegates to UnicodeString(const char16_t *). + * + * It is recommended to mark this constructor "explicit" by + * `-DUNISTR_FROM_STRING_EXPLICIT=explicit` + * on the compiler command line or similar. + * @param text NUL-terminated UTF-16 string + * @stable ICU 59 + */ + UNISTR_FROM_STRING_EXPLICIT UnicodeString(const wchar_t *text) : + UnicodeString(ConstChar16Ptr(text)) {} +#endif + + /** + * nullptr_t constructor. + * Effectively the same as the default constructor, makes an empty string object. + * + * It is recommended to mark this constructor "explicit" by + * `-DUNISTR_FROM_STRING_EXPLICIT=explicit` + * on the compiler command line or similar. + * @param text nullptr + * @stable ICU 59 + */ + UNISTR_FROM_STRING_EXPLICIT inline UnicodeString(const std::nullptr_t text); + + /** + * char16_t* constructor. + * @param text The characters to place in the UnicodeString. + * @param textLength The number of Unicode characters in `text` + * to copy. + * @stable ICU 2.0 + */ + UnicodeString(const char16_t *text, + int32_t textLength); + +#if !U_CHAR16_IS_TYPEDEF + /** + * uint16_t * constructor. + * Delegates to UnicodeString(const char16_t *, int32_t). + * @param text UTF-16 string + * @param textLength string length + * @stable ICU 59 + */ + UnicodeString(const uint16_t *text, int32_t textLength) : + UnicodeString(ConstChar16Ptr(text), textLength) {} +#endif + +#if U_SIZEOF_WCHAR_T==2 || defined(U_IN_DOXYGEN) + /** + * wchar_t * constructor. + * (Only defined if U_SIZEOF_WCHAR_T==2.) + * Delegates to UnicodeString(const char16_t *, int32_t). + * @param text NUL-terminated UTF-16 string + * @param textLength string length + * @stable ICU 59 + */ + UnicodeString(const wchar_t *text, int32_t textLength) : + UnicodeString(ConstChar16Ptr(text), textLength) {} +#endif + + /** + * nullptr_t constructor. + * Effectively the same as the default constructor, makes an empty string object. + * @param text nullptr + * @param textLength ignored + * @stable ICU 59 + */ + inline UnicodeString(const std::nullptr_t text, int32_t textLength); + + /** + * Readonly-aliasing char16_t* constructor. + * The text will be used for the UnicodeString object, but + * it will not be released when the UnicodeString is destroyed. + * This has copy-on-write semantics: + * When the string is modified, then the buffer is first copied into + * newly allocated memory. + * The aliased buffer is never modified. + * + * In an assignment to another UnicodeString, when using the copy constructor + * or the assignment operator, the text will be copied. + * When using fastCopyFrom(), the text will be aliased again, + * so that both strings then alias the same readonly-text. + * + * @param isTerminated specifies if `text` is `NUL`-terminated. + * This must be true if `textLength==-1`. + * @param text The characters to alias for the UnicodeString. + * @param textLength The number of Unicode characters in `text` to alias. + * If -1, then this constructor will determine the length + * by calling `u_strlen()`. + * @stable ICU 2.0 + */ + UnicodeString(UBool isTerminated, + ConstChar16Ptr text, + int32_t textLength); + + /** + * Writable-aliasing char16_t* constructor. + * The text will be used for the UnicodeString object, but + * it will not be released when the UnicodeString is destroyed. + * This has write-through semantics: + * For as long as the capacity of the buffer is sufficient, write operations + * will directly affect the buffer. When more capacity is necessary, then + * a new buffer will be allocated and the contents copied as with regularly + * constructed strings. + * In an assignment to another UnicodeString, the buffer will be copied. + * The extract(Char16Ptr dst) function detects whether the dst pointer is the same + * as the string buffer itself and will in this case not copy the contents. + * + * @param buffer The characters to alias for the UnicodeString. + * @param buffLength The number of Unicode characters in `buffer` to alias. + * @param buffCapacity The size of `buffer` in char16_ts. + * @stable ICU 2.0 + */ + UnicodeString(char16_t *buffer, int32_t buffLength, int32_t buffCapacity); + +#if !U_CHAR16_IS_TYPEDEF + /** + * Writable-aliasing uint16_t * constructor. + * Delegates to UnicodeString(const char16_t *, int32_t, int32_t). + * @param buffer writable buffer of/for UTF-16 text + * @param buffLength length of the current buffer contents + * @param buffCapacity buffer capacity + * @stable ICU 59 + */ + UnicodeString(uint16_t *buffer, int32_t buffLength, int32_t buffCapacity) : + UnicodeString(Char16Ptr(buffer), buffLength, buffCapacity) {} +#endif + +#if U_SIZEOF_WCHAR_T==2 || defined(U_IN_DOXYGEN) + /** + * Writable-aliasing wchar_t * constructor. + * (Only defined if U_SIZEOF_WCHAR_T==2.) + * Delegates to UnicodeString(const char16_t *, int32_t, int32_t). + * @param buffer writable buffer of/for UTF-16 text + * @param buffLength length of the current buffer contents + * @param buffCapacity buffer capacity + * @stable ICU 59 + */ + UnicodeString(wchar_t *buffer, int32_t buffLength, int32_t buffCapacity) : + UnicodeString(Char16Ptr(buffer), buffLength, buffCapacity) {} +#endif + + /** + * Writable-aliasing nullptr_t constructor. + * Effectively the same as the default constructor, makes an empty string object. + * @param buffer nullptr + * @param buffLength ignored + * @param buffCapacity ignored + * @stable ICU 59 + */ + inline UnicodeString(std::nullptr_t buffer, int32_t buffLength, int32_t buffCapacity); + +#if U_CHARSET_IS_UTF8 || !UCONFIG_NO_CONVERSION + + /** + * char* constructor. + * Uses the default converter (and thus depends on the ICU conversion code) + * unless U_CHARSET_IS_UTF8 is set to 1. + * + * For ASCII (really "invariant character") strings it is more efficient to use + * the constructor that takes a US_INV (for its enum EInvariant). + * For ASCII (invariant-character) string literals, see UNICODE_STRING and + * UNICODE_STRING_SIMPLE. + * + * It is recommended to mark this constructor "explicit" by + * `-DUNISTR_FROM_STRING_EXPLICIT=explicit` + * on the compiler command line or similar. + * @param codepageData an array of bytes, null-terminated, + * in the platform's default codepage. + * @stable ICU 2.0 + * @see UNICODE_STRING + * @see UNICODE_STRING_SIMPLE + */ + UNISTR_FROM_STRING_EXPLICIT UnicodeString(const char *codepageData); + + /** + * char* constructor. + * Uses the default converter (and thus depends on the ICU conversion code) + * unless U_CHARSET_IS_UTF8 is set to 1. + * @param codepageData an array of bytes in the platform's default codepage. + * @param dataLength The number of bytes in `codepageData`. + * @stable ICU 2.0 + */ + UnicodeString(const char *codepageData, int32_t dataLength); + +#endif + +#if !UCONFIG_NO_CONVERSION + + /** + * char* constructor. + * @param codepageData an array of bytes, null-terminated + * @param codepage the encoding of `codepageData`. The special + * value 0 for `codepage` indicates that the text is in the + * platform's default codepage. + * + * If `codepage` is an empty string (`""`), + * then a simple conversion is performed on the codepage-invariant + * subset ("invariant characters") of the platform encoding. See utypes.h. + * Recommendation: For invariant-character strings use the constructor + * UnicodeString(const char *src, int32_t length, enum EInvariant inv) + * because it avoids object code dependencies of UnicodeString on + * the conversion code. + * + * @stable ICU 2.0 + */ + UnicodeString(const char *codepageData, const char *codepage); + + /** + * char* constructor. + * @param codepageData an array of bytes. + * @param dataLength The number of bytes in `codepageData`. + * @param codepage the encoding of `codepageData`. The special + * value 0 for `codepage` indicates that the text is in the + * platform's default codepage. + * If `codepage` is an empty string (`""`), + * then a simple conversion is performed on the codepage-invariant + * subset ("invariant characters") of the platform encoding. See utypes.h. + * Recommendation: For invariant-character strings use the constructor + * UnicodeString(const char *src, int32_t length, enum EInvariant inv) + * because it avoids object code dependencies of UnicodeString on + * the conversion code. + * + * @stable ICU 2.0 + */ + UnicodeString(const char *codepageData, int32_t dataLength, const char *codepage); + + /** + * char * / UConverter constructor. + * This constructor uses an existing UConverter object to + * convert the codepage string to Unicode and construct a UnicodeString + * from that. + * + * The converter is reset at first. + * If the error code indicates a failure before this constructor is called, + * or if an error occurs during conversion or construction, + * then the string will be bogus. + * + * This function avoids the overhead of opening and closing a converter if + * multiple strings are constructed. + * + * @param src input codepage string + * @param srcLength length of the input string, can be -1 for NUL-terminated strings + * @param cnv converter object (ucnv_resetToUnicode() will be called), + * can be nullptr for the default converter + * @param errorCode normal ICU error code + * @stable ICU 2.0 + */ + UnicodeString( + const char *src, int32_t srcLength, + UConverter *cnv, + UErrorCode &errorCode); + +#endif + + /** + * Constructs a Unicode string from an invariant-character char * string. + * About invariant characters see utypes.h. + * This constructor has no runtime dependency on conversion code and is + * therefore recommended over ones taking a charset name string + * (where the empty string "" indicates invariant-character conversion). + * + * Use the macro US_INV as the third, signature-distinguishing parameter. + * + * For example: + * \code + * void fn(const char *s) { + * UnicodeString ustr(s, -1, US_INV); + * // use ustr ... + * } + * \endcode + * @param src String using only invariant characters. + * @param textLength Length of src, or -1 if NUL-terminated. + * @param inv Signature-distinguishing parameter, use US_INV. + * + * @see US_INV + * @stable ICU 3.2 + */ + UnicodeString(const char *src, int32_t textLength, enum EInvariant inv); + + + /** + * Copy constructor. + * + * Starting with ICU 2.4, the assignment operator and the copy constructor + * allocate a new buffer and copy the buffer contents even for readonly aliases. + * By contrast, the fastCopyFrom() function implements the old, + * more efficient but less safe behavior + * of making this string also a readonly alias to the same buffer. + * + * If the source object has an "open" buffer from getBuffer(minCapacity), + * then the copy is an empty string. + * + * @param that The UnicodeString object to copy. + * @stable ICU 2.0 + * @see fastCopyFrom + */ + UnicodeString(const UnicodeString& that); + + /** + * 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 + */ + UnicodeString(UnicodeString &&src) noexcept; + + /** + * 'Substring' constructor from tail of source string. + * @param src The UnicodeString object to copy. + * @param srcStart The offset into `src` at which to start copying. + * @stable ICU 2.2 + */ + UnicodeString(const UnicodeString& src, int32_t srcStart); + + /** + * 'Substring' constructor from subrange of source string. + * @param src The UnicodeString object to copy. + * @param srcStart The offset into `src` at which to start copying. + * @param srcLength The number of characters from `src` to copy. + * @stable ICU 2.2 + */ + UnicodeString(const UnicodeString& src, int32_t srcStart, int32_t srcLength); + + /** + * Clone this object, an instance of a subclass of Replaceable. + * Clones can be used concurrently in multiple threads. + * If a subclass does not implement clone(), or if an error occurs, + * then nullptr is returned. + * The caller must delete the clone. + * + * @return a clone of this object + * + * @see Replaceable::clone + * @see getDynamicClassID + * @stable ICU 2.6 + */ + virtual UnicodeString *clone() const override; + + /** Destructor. + * @stable ICU 2.0 + */ + virtual ~UnicodeString(); + + /** + * Create a UnicodeString from a UTF-8 string. + * Illegal input is replaced with U+FFFD. Otherwise, errors result in a bogus string. + * Calls u_strFromUTF8WithSub(). + * + * @param utf8 UTF-8 input string. + * Note that a StringPiece can be implicitly constructed + * from a std::string or a NUL-terminated const char * string. + * @return A UnicodeString with equivalent UTF-16 contents. + * @see toUTF8 + * @see toUTF8String + * @stable ICU 4.2 + */ + static UnicodeString fromUTF8(StringPiece utf8); + + /** + * Create a UnicodeString from a UTF-32 string. + * Illegal input is replaced with U+FFFD. Otherwise, errors result in a bogus string. + * Calls u_strFromUTF32WithSub(). + * + * @param utf32 UTF-32 input string. Must not be nullptr. + * @param length Length of the input string, or -1 if NUL-terminated. + * @return A UnicodeString with equivalent UTF-16 contents. + * @see toUTF32 + * @stable ICU 4.2 + */ + static UnicodeString fromUTF32(const UChar32 *utf32, int32_t length); + + /* Miscellaneous operations */ + + /** + * Unescape a string of characters and return a string containing + * the result. The following escape sequences are recognized: + * + * \\uhhhh 4 hex digits; h in [0-9A-Fa-f] + * \\Uhhhhhhhh 8 hex digits + * \\xhh 1-2 hex digits + * \\ooo 1-3 octal digits; o in [0-7] + * \\cX control-X; X is masked with 0x1F + * + * as well as the standard ANSI C escapes: + * + * \\a => U+0007, \\b => U+0008, \\t => U+0009, \\n => U+000A, + * \\v => U+000B, \\f => U+000C, \\r => U+000D, \\e => U+001B, + * \\" => U+0022, \\' => U+0027, \\? => U+003F, \\\\ => U+005C + * + * Anything else following a backslash is generically escaped. For + * example, "[a\\-z]" returns "[a-z]". + * + * If an escape sequence is ill-formed, this method returns an empty + * string. An example of an ill-formed sequence is "\\u" followed by + * fewer than 4 hex digits. + * + * This function is similar to u_unescape() but not identical to it. + * The latter takes a source char*, so it does escape recognition + * and also invariant conversion. + * + * @return a string with backslash escapes interpreted, or an + * empty string on error. + * @see UnicodeString#unescapeAt() + * @see u_unescape() + * @see u_unescapeAt() + * @stable ICU 2.0 + */ + UnicodeString unescape() const; + + /** + * Unescape a single escape sequence and return the represented + * character. See unescape() for a listing of the recognized escape + * sequences. The character at offset-1 is assumed (without + * checking) to be a backslash. If the escape sequence is + * ill-formed, or the offset is out of range, U_SENTINEL=-1 is + * returned. + * + * @param offset an input output parameter. On input, it is the + * offset into this string where the escape sequence is located, + * after the initial backslash. On output, it is advanced after the + * last character parsed. On error, it is not advanced at all. + * @return the character represented by the escape sequence at + * offset, or U_SENTINEL=-1 on error. + * @see UnicodeString#unescape() + * @see u_unescape() + * @see u_unescapeAt() + * @stable ICU 2.0 + */ + UChar32 unescapeAt(int32_t &offset) const; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + * + * @stable ICU 2.2 + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + * + * @stable ICU 2.2 + */ + virtual UClassID getDynamicClassID() const override; + + //======================================== + // Implementation methods + //======================================== + +protected: + /** + * Implement Replaceable::getLength() (see jitterbug 1027). + * @stable ICU 2.4 + */ + virtual int32_t getLength() const override; + + /** + * The change in Replaceable to use virtual getCharAt() allows + * UnicodeString::charAt() to be inline again (see jitterbug 709). + * @stable ICU 2.4 + */ + virtual char16_t getCharAt(int32_t offset) const override; + + /** + * The change in Replaceable to use virtual getChar32At() allows + * UnicodeString::char32At() to be inline again (see jitterbug 709). + * @stable ICU 2.4 + */ + virtual UChar32 getChar32At(int32_t offset) const override; + +private: + // For char* constructors. Could be made public. + UnicodeString &setToUTF8(StringPiece utf8); + // For extract(char*). + // We could make a toUTF8(target, capacity, errorCode) public but not + // this version: New API will be cleaner if we make callers create substrings + // rather than having start+length on every method, + // and it should take a UErrorCode&. + int32_t + toUTF8(int32_t start, int32_t len, + char *target, int32_t capacity) const; + + /** + * Internal string contents comparison, called by operator==. + * Requires: this & text not bogus and have same lengths. + */ + UBool doEquals(const UnicodeString &text, int32_t len) const; + + inline UBool + doEqualsSubstring(int32_t start, + int32_t length, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const; + + UBool doEqualsSubstring(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const; + + inline int8_t + doCompare(int32_t start, + int32_t length, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const; + + int8_t doCompare(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const; + + inline int8_t + doCompareCodePointOrder(int32_t start, + int32_t length, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const; + + int8_t doCompareCodePointOrder(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const; + + inline int8_t + doCaseCompare(int32_t start, + int32_t length, + const UnicodeString &srcText, + int32_t srcStart, + int32_t srcLength, + uint32_t options) const; + + int8_t + doCaseCompare(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength, + uint32_t options) const; + + int32_t doIndexOf(char16_t c, + int32_t start, + int32_t length) const; + + int32_t doIndexOf(UChar32 c, + int32_t start, + int32_t length) const; + + int32_t doLastIndexOf(char16_t c, + int32_t start, + int32_t length) const; + + int32_t doLastIndexOf(UChar32 c, + int32_t start, + int32_t length) const; + + void doExtract(int32_t start, + int32_t length, + char16_t *dst, + int32_t dstStart) const; + + inline void doExtract(int32_t start, + int32_t length, + UnicodeString& target) const; + + inline char16_t doCharAt(int32_t offset) const; + + UnicodeString& doReplace(int32_t start, + int32_t length, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength); + + UnicodeString& doReplace(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength); + + UnicodeString& doAppend(const UnicodeString& src, int32_t srcStart, int32_t srcLength); + UnicodeString& doAppend(const char16_t *srcChars, int32_t srcStart, int32_t srcLength); + + UnicodeString& doReverse(int32_t start, + int32_t length); + + // calculate hash code + int32_t doHashCode(void) const; + + // get pointer to start of array + // these do not check for kOpenGetBuffer, unlike the public getBuffer() function + inline char16_t* getArrayStart(void); + inline const char16_t* getArrayStart(void) const; + + inline UBool hasShortLength() const; + inline int32_t getShortLength() const; + + // A UnicodeString object (not necessarily its current buffer) + // is writable unless it isBogus() or it has an "open" getBuffer(minCapacity). + inline UBool isWritable() const; + + // Is the current buffer writable? + inline UBool isBufferWritable() const; + + // None of the following does releaseArray(). + inline void setZeroLength(); + inline void setShortLength(int32_t len); + inline void setLength(int32_t len); + inline void setToEmpty(); + inline void setArray(char16_t *array, int32_t len, int32_t capacity); // sets length but not flags + + // allocate the array; result may be the stack buffer + // sets refCount to 1 if appropriate + // sets fArray, fCapacity, and flags + // sets length to 0 + // returns boolean for success or failure + UBool allocate(int32_t capacity); + + // release the array if owned + void releaseArray(void); + + // turn a bogus string into an empty one + void unBogus(); + + // implements assignment operator, copy constructor, and fastCopyFrom() + UnicodeString ©From(const UnicodeString &src, UBool fastCopy=false); + + // Copies just the fields without memory management. + void copyFieldsFrom(UnicodeString &src, UBool setSrcToBogus) noexcept; + + // Pin start and limit to acceptable values. + inline void pinIndex(int32_t& start) const; + inline void pinIndices(int32_t& start, + int32_t& length) const; + +#if !UCONFIG_NO_CONVERSION + + /* Internal extract() using UConverter. */ + int32_t doExtract(int32_t start, int32_t length, + char *dest, int32_t destCapacity, + UConverter *cnv, + UErrorCode &errorCode) const; + + /* + * Real constructor for converting from codepage data. + * It assumes that it is called with !fRefCounted. + * + * If `codepage==0`, then the default converter + * is used for the platform encoding. + * If `codepage` is an empty string (`""`), + * then a simple conversion is performed on the codepage-invariant + * subset ("invariant characters") of the platform encoding. See utypes.h. + */ + void doCodepageCreate(const char *codepageData, + int32_t dataLength, + const char *codepage); + + /* + * Worker function for creating a UnicodeString from + * a codepage string using a UConverter. + */ + void + doCodepageCreate(const char *codepageData, + int32_t dataLength, + UConverter *converter, + UErrorCode &status); + +#endif + + /* + * This function is called when write access to the array + * is necessary. + * + * We need to make a copy of the array if + * the buffer is read-only, or + * the buffer is refCounted (shared), and refCount>1, or + * the buffer is too small. + * + * Return false if memory could not be allocated. + */ + UBool cloneArrayIfNeeded(int32_t newCapacity = -1, + int32_t growCapacity = -1, + UBool doCopyArray = true, + int32_t **pBufferToDelete = 0, + UBool forceClone = false); + + /** + * Common function for UnicodeString case mappings. + * The stringCaseMapper has the same type UStringCaseMapper + * as in ustr_imp.h for ustrcase_map(). + */ + UnicodeString & + caseMap(int32_t caseLocale, uint32_t options, +#if !UCONFIG_NO_BREAK_ITERATION + BreakIterator *iter, +#endif + UStringCaseMapper *stringCaseMapper); + + // ref counting + void addRef(void); + int32_t removeRef(void); + int32_t refCount(void) const; + + // constants + enum { + /** + * Size of stack buffer for short strings. + * Must be at least U16_MAX_LENGTH for the single-code point constructor to work. + * @see UNISTR_OBJECT_SIZE + */ + US_STACKBUF_SIZE=(int32_t)(UNISTR_OBJECT_SIZE-sizeof(void *)-2)/U_SIZEOF_UCHAR, + kInvalidUChar=0xffff, // U+FFFF returned by charAt(invalid index) + kInvalidHashCode=0, // invalid hash code + kEmptyHashCode=1, // hash code for empty string + + // bit flag values for fLengthAndFlags + kIsBogus=1, // this string is bogus, i.e., not valid or nullptr + kUsingStackBuffer=2,// using fUnion.fStackFields instead of fUnion.fFields + kRefCounted=4, // there is a refCount field before the characters in fArray + kBufferIsReadonly=8,// do not write to this buffer + kOpenGetBuffer=16, // getBuffer(minCapacity) was called (is "open"), + // and releaseBuffer(newLength) must be called + kAllStorageFlags=0x1f, + + kLengthShift=5, // remaining 11 bits for non-negative short length, or negative if long + kLength1=1<127; else undefined + int32_t fCapacity; // capacity of fArray (in char16_ts) + // array pointer last to minimize padding for machines with P128 data model + // or pointer sizes that are not a power of 2 + char16_t *fArray; // the Unicode data + } fFields; + } fUnion; +}; + +/** + * Create a new UnicodeString with the concatenation of two others. + * + * @param s1 The first string to be copied to the new one. + * @param s2 The second string to be copied to the new one, after s1. + * @return UnicodeString(s1).append(s2) + * @stable ICU 2.8 + */ +U_COMMON_API UnicodeString U_EXPORT2 +operator+ (const UnicodeString &s1, const UnicodeString &s2); + +//======================================== +// Inline members +//======================================== + +//======================================== +// Privates +//======================================== + +inline void +UnicodeString::pinIndex(int32_t& start) const +{ + // pin index + if(start < 0) { + start = 0; + } else if(start > length()) { + start = length(); + } +} + +inline void +UnicodeString::pinIndices(int32_t& start, + int32_t& _length) const +{ + // pin indices + int32_t len = length(); + if(start < 0) { + start = 0; + } else if(start > len) { + start = len; + } + if(_length < 0) { + _length = 0; + } else if(_length > (len - start)) { + _length = (len - start); + } +} + +inline char16_t* +UnicodeString::getArrayStart() { + return (fUnion.fFields.fLengthAndFlags&kUsingStackBuffer) ? + fUnion.fStackFields.fBuffer : fUnion.fFields.fArray; +} + +inline const char16_t* +UnicodeString::getArrayStart() const { + return (fUnion.fFields.fLengthAndFlags&kUsingStackBuffer) ? + fUnion.fStackFields.fBuffer : fUnion.fFields.fArray; +} + +//======================================== +// Default constructor +//======================================== + +inline +UnicodeString::UnicodeString() { + fUnion.fStackFields.fLengthAndFlags=kShortString; +} + +inline UnicodeString::UnicodeString(const std::nullptr_t /*text*/) { + fUnion.fStackFields.fLengthAndFlags=kShortString; +} + +inline UnicodeString::UnicodeString(const std::nullptr_t /*text*/, int32_t /*length*/) { + fUnion.fStackFields.fLengthAndFlags=kShortString; +} + +inline UnicodeString::UnicodeString(std::nullptr_t /*buffer*/, int32_t /*buffLength*/, int32_t /*buffCapacity*/) { + fUnion.fStackFields.fLengthAndFlags=kShortString; +} + +//======================================== +// Read-only implementation methods +//======================================== +inline UBool +UnicodeString::hasShortLength() const { + return fUnion.fFields.fLengthAndFlags>=0; +} + +inline int32_t +UnicodeString::getShortLength() const { + // fLengthAndFlags must be non-negative -> short length >= 0 + // and arithmetic or logical shift does not matter. + return fUnion.fFields.fLengthAndFlags>>kLengthShift; +} + +inline int32_t +UnicodeString::length() const { + return hasShortLength() ? getShortLength() : fUnion.fFields.fLength; +} + +inline int32_t +UnicodeString::getCapacity() const { + return (fUnion.fFields.fLengthAndFlags&kUsingStackBuffer) ? + US_STACKBUF_SIZE : fUnion.fFields.fCapacity; +} + +inline int32_t +UnicodeString::hashCode() const +{ return doHashCode(); } + +inline UBool +UnicodeString::isBogus() const +{ return (UBool)(fUnion.fFields.fLengthAndFlags & kIsBogus); } + +inline UBool +UnicodeString::isWritable() const +{ return (UBool)!(fUnion.fFields.fLengthAndFlags&(kOpenGetBuffer|kIsBogus)); } + +inline UBool +UnicodeString::isBufferWritable() const +{ + return (UBool)( + !(fUnion.fFields.fLengthAndFlags&(kOpenGetBuffer|kIsBogus|kBufferIsReadonly)) && + (!(fUnion.fFields.fLengthAndFlags&kRefCounted) || refCount()==1)); +} + +inline const char16_t * +UnicodeString::getBuffer() const { + if(fUnion.fFields.fLengthAndFlags&(kIsBogus|kOpenGetBuffer)) { + return nullptr; + } else if(fUnion.fFields.fLengthAndFlags&kUsingStackBuffer) { + return fUnion.fStackFields.fBuffer; + } else { + return fUnion.fFields.fArray; + } +} + +//======================================== +// Read-only alias methods +//======================================== +inline int8_t +UnicodeString::doCompare(int32_t start, + int32_t thisLength, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const +{ + if(srcText.isBogus()) { + return (int8_t)!isBogus(); // 0 if both are bogus, 1 otherwise + } else { + srcText.pinIndices(srcStart, srcLength); + return doCompare(start, thisLength, srcText.getArrayStart(), srcStart, srcLength); + } +} + +inline UBool +UnicodeString::doEqualsSubstring(int32_t start, + int32_t thisLength, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const +{ + if(srcText.isBogus()) { + return isBogus(); + } else { + srcText.pinIndices(srcStart, srcLength); + return !isBogus() && doEqualsSubstring(start, thisLength, srcText.getArrayStart(), srcStart, srcLength); + } +} + +inline bool +UnicodeString::operator== (const UnicodeString& text) const +{ + if(isBogus()) { + return text.isBogus(); + } else { + int32_t len = length(), textLength = text.length(); + return !text.isBogus() && len == textLength && doEquals(text, len); + } +} + +inline bool +UnicodeString::operator!= (const UnicodeString& text) const +{ return (! operator==(text)); } + +inline UBool +UnicodeString::operator> (const UnicodeString& text) const +{ return doCompare(0, length(), text, 0, text.length()) == 1; } + +inline UBool +UnicodeString::operator< (const UnicodeString& text) const +{ return doCompare(0, length(), text, 0, text.length()) == -1; } + +inline UBool +UnicodeString::operator>= (const UnicodeString& text) const +{ return doCompare(0, length(), text, 0, text.length()) != -1; } + +inline UBool +UnicodeString::operator<= (const UnicodeString& text) const +{ return doCompare(0, length(), text, 0, text.length()) != 1; } + +inline int8_t +UnicodeString::compare(const UnicodeString& text) const +{ return doCompare(0, length(), text, 0, text.length()); } + +inline int8_t +UnicodeString::compare(int32_t start, + int32_t _length, + const UnicodeString& srcText) const +{ return doCompare(start, _length, srcText, 0, srcText.length()); } + +inline int8_t +UnicodeString::compare(ConstChar16Ptr srcChars, + int32_t srcLength) const +{ return doCompare(0, length(), srcChars, 0, srcLength); } + +inline int8_t +UnicodeString::compare(int32_t start, + int32_t _length, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const +{ return doCompare(start, _length, srcText, srcStart, srcLength); } + +inline int8_t +UnicodeString::compare(int32_t start, + int32_t _length, + const char16_t *srcChars) const +{ return doCompare(start, _length, srcChars, 0, _length); } + +inline int8_t +UnicodeString::compare(int32_t start, + int32_t _length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const +{ return doCompare(start, _length, srcChars, srcStart, srcLength); } + +inline int8_t +UnicodeString::compareBetween(int32_t start, + int32_t limit, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLimit) const +{ return doCompare(start, limit - start, + srcText, srcStart, srcLimit - srcStart); } + +inline int8_t +UnicodeString::doCompareCodePointOrder(int32_t start, + int32_t thisLength, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const +{ + if(srcText.isBogus()) { + return (int8_t)!isBogus(); // 0 if both are bogus, 1 otherwise + } else { + srcText.pinIndices(srcStart, srcLength); + return doCompareCodePointOrder(start, thisLength, srcText.getArrayStart(), srcStart, srcLength); + } +} + +inline int8_t +UnicodeString::compareCodePointOrder(const UnicodeString& text) const +{ return doCompareCodePointOrder(0, length(), text, 0, text.length()); } + +inline int8_t +UnicodeString::compareCodePointOrder(int32_t start, + int32_t _length, + const UnicodeString& srcText) const +{ return doCompareCodePointOrder(start, _length, srcText, 0, srcText.length()); } + +inline int8_t +UnicodeString::compareCodePointOrder(ConstChar16Ptr srcChars, + int32_t srcLength) const +{ return doCompareCodePointOrder(0, length(), srcChars, 0, srcLength); } + +inline int8_t +UnicodeString::compareCodePointOrder(int32_t start, + int32_t _length, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const +{ return doCompareCodePointOrder(start, _length, srcText, srcStart, srcLength); } + +inline int8_t +UnicodeString::compareCodePointOrder(int32_t start, + int32_t _length, + const char16_t *srcChars) const +{ return doCompareCodePointOrder(start, _length, srcChars, 0, _length); } + +inline int8_t +UnicodeString::compareCodePointOrder(int32_t start, + int32_t _length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const +{ return doCompareCodePointOrder(start, _length, srcChars, srcStart, srcLength); } + +inline int8_t +UnicodeString::compareCodePointOrderBetween(int32_t start, + int32_t limit, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLimit) const +{ return doCompareCodePointOrder(start, limit - start, + srcText, srcStart, srcLimit - srcStart); } + +inline int8_t +UnicodeString::doCaseCompare(int32_t start, + int32_t thisLength, + const UnicodeString &srcText, + int32_t srcStart, + int32_t srcLength, + uint32_t options) const +{ + if(srcText.isBogus()) { + return (int8_t)!isBogus(); // 0 if both are bogus, 1 otherwise + } else { + srcText.pinIndices(srcStart, srcLength); + return doCaseCompare(start, thisLength, srcText.getArrayStart(), srcStart, srcLength, options); + } +} + +inline int8_t +UnicodeString::caseCompare(const UnicodeString &text, uint32_t options) const { + return doCaseCompare(0, length(), text, 0, text.length(), options); +} + +inline int8_t +UnicodeString::caseCompare(int32_t start, + int32_t _length, + const UnicodeString &srcText, + uint32_t options) const { + return doCaseCompare(start, _length, srcText, 0, srcText.length(), options); +} + +inline int8_t +UnicodeString::caseCompare(ConstChar16Ptr srcChars, + int32_t srcLength, + uint32_t options) const { + return doCaseCompare(0, length(), srcChars, 0, srcLength, options); +} + +inline int8_t +UnicodeString::caseCompare(int32_t start, + int32_t _length, + const UnicodeString &srcText, + int32_t srcStart, + int32_t srcLength, + uint32_t options) const { + return doCaseCompare(start, _length, srcText, srcStart, srcLength, options); +} + +inline int8_t +UnicodeString::caseCompare(int32_t start, + int32_t _length, + const char16_t *srcChars, + uint32_t options) const { + return doCaseCompare(start, _length, srcChars, 0, _length, options); +} + +inline int8_t +UnicodeString::caseCompare(int32_t start, + int32_t _length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength, + uint32_t options) const { + return doCaseCompare(start, _length, srcChars, srcStart, srcLength, options); +} + +inline int8_t +UnicodeString::caseCompareBetween(int32_t start, + int32_t limit, + const UnicodeString &srcText, + int32_t srcStart, + int32_t srcLimit, + uint32_t options) const { + return doCaseCompare(start, limit - start, srcText, srcStart, srcLimit - srcStart, options); +} + +inline int32_t +UnicodeString::indexOf(const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength, + int32_t start, + int32_t _length) const +{ + if(!srcText.isBogus()) { + srcText.pinIndices(srcStart, srcLength); + if(srcLength > 0) { + return indexOf(srcText.getArrayStart(), srcStart, srcLength, start, _length); + } + } + return -1; +} + +inline int32_t +UnicodeString::indexOf(const UnicodeString& text) const +{ return indexOf(text, 0, text.length(), 0, length()); } + +inline int32_t +UnicodeString::indexOf(const UnicodeString& text, + int32_t start) const { + pinIndex(start); + return indexOf(text, 0, text.length(), start, length() - start); +} + +inline int32_t +UnicodeString::indexOf(const UnicodeString& text, + int32_t start, + int32_t _length) const +{ return indexOf(text, 0, text.length(), start, _length); } + +inline int32_t +UnicodeString::indexOf(const char16_t *srcChars, + int32_t srcLength, + int32_t start) const { + pinIndex(start); + return indexOf(srcChars, 0, srcLength, start, length() - start); +} + +inline int32_t +UnicodeString::indexOf(ConstChar16Ptr srcChars, + int32_t srcLength, + int32_t start, + int32_t _length) const +{ return indexOf(srcChars, 0, srcLength, start, _length); } + +inline int32_t +UnicodeString::indexOf(char16_t c, + int32_t start, + int32_t _length) const +{ return doIndexOf(c, start, _length); } + +inline int32_t +UnicodeString::indexOf(UChar32 c, + int32_t start, + int32_t _length) const +{ return doIndexOf(c, start, _length); } + +inline int32_t +UnicodeString::indexOf(char16_t c) const +{ return doIndexOf(c, 0, length()); } + +inline int32_t +UnicodeString::indexOf(UChar32 c) const +{ return indexOf(c, 0, length()); } + +inline int32_t +UnicodeString::indexOf(char16_t c, + int32_t start) const { + pinIndex(start); + return doIndexOf(c, start, length() - start); +} + +inline int32_t +UnicodeString::indexOf(UChar32 c, + int32_t start) const { + pinIndex(start); + return indexOf(c, start, length() - start); +} + +inline int32_t +UnicodeString::lastIndexOf(ConstChar16Ptr srcChars, + int32_t srcLength, + int32_t start, + int32_t _length) const +{ return lastIndexOf(srcChars, 0, srcLength, start, _length); } + +inline int32_t +UnicodeString::lastIndexOf(const char16_t *srcChars, + int32_t srcLength, + int32_t start) const { + pinIndex(start); + return lastIndexOf(srcChars, 0, srcLength, start, length() - start); +} + +inline int32_t +UnicodeString::lastIndexOf(const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength, + int32_t start, + int32_t _length) const +{ + if(!srcText.isBogus()) { + srcText.pinIndices(srcStart, srcLength); + if(srcLength > 0) { + return lastIndexOf(srcText.getArrayStart(), srcStart, srcLength, start, _length); + } + } + return -1; +} + +inline int32_t +UnicodeString::lastIndexOf(const UnicodeString& text, + int32_t start, + int32_t _length) const +{ return lastIndexOf(text, 0, text.length(), start, _length); } + +inline int32_t +UnicodeString::lastIndexOf(const UnicodeString& text, + int32_t start) const { + pinIndex(start); + return lastIndexOf(text, 0, text.length(), start, length() - start); +} + +inline int32_t +UnicodeString::lastIndexOf(const UnicodeString& text) const +{ return lastIndexOf(text, 0, text.length(), 0, length()); } + +inline int32_t +UnicodeString::lastIndexOf(char16_t c, + int32_t start, + int32_t _length) const +{ return doLastIndexOf(c, start, _length); } + +inline int32_t +UnicodeString::lastIndexOf(UChar32 c, + int32_t start, + int32_t _length) const { + return doLastIndexOf(c, start, _length); +} + +inline int32_t +UnicodeString::lastIndexOf(char16_t c) const +{ return doLastIndexOf(c, 0, length()); } + +inline int32_t +UnicodeString::lastIndexOf(UChar32 c) const { + return lastIndexOf(c, 0, length()); +} + +inline int32_t +UnicodeString::lastIndexOf(char16_t c, + int32_t start) const { + pinIndex(start); + return doLastIndexOf(c, start, length() - start); +} + +inline int32_t +UnicodeString::lastIndexOf(UChar32 c, + int32_t start) const { + pinIndex(start); + return lastIndexOf(c, start, length() - start); +} + +inline UBool +UnicodeString::startsWith(const UnicodeString& text) const +{ return doEqualsSubstring(0, text.length(), text, 0, text.length()); } + +inline UBool +UnicodeString::startsWith(const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const +{ return doEqualsSubstring(0, srcLength, srcText, srcStart, srcLength); } + +inline UBool +UnicodeString::startsWith(ConstChar16Ptr srcChars, int32_t srcLength) const { + if(srcLength < 0) { + srcLength = u_strlen(toUCharPtr(srcChars)); + } + return doEqualsSubstring(0, srcLength, srcChars, 0, srcLength); +} + +inline UBool +UnicodeString::startsWith(const char16_t *srcChars, int32_t srcStart, int32_t srcLength) const { + if(srcLength < 0) { + srcLength = u_strlen(toUCharPtr(srcChars)); + } + return doEqualsSubstring(0, srcLength, srcChars, srcStart, srcLength); +} + +inline UBool +UnicodeString::endsWith(const UnicodeString& text) const +{ return doEqualsSubstring(length() - text.length(), text.length(), + text, 0, text.length()); } + +inline UBool +UnicodeString::endsWith(const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) const { + srcText.pinIndices(srcStart, srcLength); + return doEqualsSubstring(length() - srcLength, srcLength, + srcText, srcStart, srcLength); +} + +inline UBool +UnicodeString::endsWith(ConstChar16Ptr srcChars, + int32_t srcLength) const { + if(srcLength < 0) { + srcLength = u_strlen(toUCharPtr(srcChars)); + } + return doEqualsSubstring(length() - srcLength, srcLength, srcChars, 0, srcLength); +} + +inline UBool +UnicodeString::endsWith(const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const { + if(srcLength < 0) { + srcLength = u_strlen(toUCharPtr(srcChars + srcStart)); + } + return doEqualsSubstring(length() - srcLength, srcLength, + srcChars, srcStart, srcLength); +} + +//======================================== +// replace +//======================================== +inline UnicodeString& +UnicodeString::replace(int32_t start, + int32_t _length, + const UnicodeString& srcText) +{ return doReplace(start, _length, srcText, 0, srcText.length()); } + +inline UnicodeString& +UnicodeString::replace(int32_t start, + int32_t _length, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) +{ return doReplace(start, _length, srcText, srcStart, srcLength); } + +inline UnicodeString& +UnicodeString::replace(int32_t start, + int32_t _length, + ConstChar16Ptr srcChars, + int32_t srcLength) +{ return doReplace(start, _length, srcChars, 0, srcLength); } + +inline UnicodeString& +UnicodeString::replace(int32_t start, + int32_t _length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) +{ return doReplace(start, _length, srcChars, srcStart, srcLength); } + +inline UnicodeString& +UnicodeString::replace(int32_t start, + int32_t _length, + char16_t srcChar) +{ return doReplace(start, _length, &srcChar, 0, 1); } + +inline UnicodeString& +UnicodeString::replaceBetween(int32_t start, + int32_t limit, + const UnicodeString& srcText) +{ return doReplace(start, limit - start, srcText, 0, srcText.length()); } + +inline UnicodeString& +UnicodeString::replaceBetween(int32_t start, + int32_t limit, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLimit) +{ return doReplace(start, limit - start, srcText, srcStart, srcLimit - srcStart); } + +inline UnicodeString& +UnicodeString::findAndReplace(const UnicodeString& oldText, + const UnicodeString& newText) +{ return findAndReplace(0, length(), oldText, 0, oldText.length(), + newText, 0, newText.length()); } + +inline UnicodeString& +UnicodeString::findAndReplace(int32_t start, + int32_t _length, + const UnicodeString& oldText, + const UnicodeString& newText) +{ return findAndReplace(start, _length, oldText, 0, oldText.length(), + newText, 0, newText.length()); } + +// ============================ +// extract +// ============================ +inline void +UnicodeString::doExtract(int32_t start, + int32_t _length, + UnicodeString& target) const +{ target.replace(0, target.length(), *this, start, _length); } + +inline void +UnicodeString::extract(int32_t start, + int32_t _length, + Char16Ptr target, + int32_t targetStart) const +{ doExtract(start, _length, target, targetStart); } + +inline void +UnicodeString::extract(int32_t start, + int32_t _length, + UnicodeString& target) const +{ doExtract(start, _length, target); } + +#if !UCONFIG_NO_CONVERSION + +inline int32_t +UnicodeString::extract(int32_t start, + int32_t _length, + char *dst, + const char *codepage) const + +{ + // This dstSize value will be checked explicitly + return extract(start, _length, dst, dst!=0 ? 0xffffffff : 0, codepage); +} + +#endif + +inline void +UnicodeString::extractBetween(int32_t start, + int32_t limit, + char16_t *dst, + int32_t dstStart) const { + pinIndex(start); + pinIndex(limit); + doExtract(start, limit - start, dst, dstStart); +} + +inline UnicodeString +UnicodeString::tempSubStringBetween(int32_t start, int32_t limit) const { + return tempSubString(start, limit - start); +} + +inline char16_t +UnicodeString::doCharAt(int32_t offset) const +{ + if((uint32_t)offset < (uint32_t)length()) { + return getArrayStart()[offset]; + } else { + return kInvalidUChar; + } +} + +inline char16_t +UnicodeString::charAt(int32_t offset) const +{ return doCharAt(offset); } + +inline char16_t +UnicodeString::operator[] (int32_t offset) const +{ return doCharAt(offset); } + +inline UBool +UnicodeString::isEmpty() const { + // Arithmetic or logical right shift does not matter: only testing for 0. + return (fUnion.fFields.fLengthAndFlags>>kLengthShift) == 0; +} + +//======================================== +// Write implementation methods +//======================================== +inline void +UnicodeString::setZeroLength() { + fUnion.fFields.fLengthAndFlags &= kAllStorageFlags; +} + +inline void +UnicodeString::setShortLength(int32_t len) { + // requires 0 <= len <= kMaxShortLength + fUnion.fFields.fLengthAndFlags = + (int16_t)((fUnion.fFields.fLengthAndFlags & kAllStorageFlags) | (len << kLengthShift)); +} + +inline void +UnicodeString::setLength(int32_t len) { + if(len <= kMaxShortLength) { + setShortLength(len); + } else { + fUnion.fFields.fLengthAndFlags |= kLengthIsLarge; + fUnion.fFields.fLength = len; + } +} + +inline void +UnicodeString::setToEmpty() { + fUnion.fFields.fLengthAndFlags = kShortString; +} + +inline void +UnicodeString::setArray(char16_t *array, int32_t len, int32_t capacity) { + setLength(len); + fUnion.fFields.fArray = array; + fUnion.fFields.fCapacity = capacity; +} + +inline UnicodeString& +UnicodeString::operator= (char16_t ch) +{ return doReplace(0, length(), &ch, 0, 1); } + +inline UnicodeString& +UnicodeString::operator= (UChar32 ch) +{ return replace(0, length(), ch); } + +inline UnicodeString& +UnicodeString::setTo(const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) +{ + unBogus(); + return doReplace(0, length(), srcText, srcStart, srcLength); +} + +inline UnicodeString& +UnicodeString::setTo(const UnicodeString& srcText, + int32_t srcStart) +{ + unBogus(); + srcText.pinIndex(srcStart); + return doReplace(0, length(), srcText, srcStart, srcText.length() - srcStart); +} + +inline UnicodeString& +UnicodeString::setTo(const UnicodeString& srcText) +{ + return copyFrom(srcText); +} + +inline UnicodeString& +UnicodeString::setTo(const char16_t *srcChars, + int32_t srcLength) +{ + unBogus(); + return doReplace(0, length(), srcChars, 0, srcLength); +} + +inline UnicodeString& +UnicodeString::setTo(char16_t srcChar) +{ + unBogus(); + return doReplace(0, length(), &srcChar, 0, 1); +} + +inline UnicodeString& +UnicodeString::setTo(UChar32 srcChar) +{ + unBogus(); + return replace(0, length(), srcChar); +} + +inline UnicodeString& +UnicodeString::append(const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) +{ return doAppend(srcText, srcStart, srcLength); } + +inline UnicodeString& +UnicodeString::append(const UnicodeString& srcText) +{ return doAppend(srcText, 0, srcText.length()); } + +inline UnicodeString& +UnicodeString::append(const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) +{ return doAppend(srcChars, srcStart, srcLength); } + +inline UnicodeString& +UnicodeString::append(ConstChar16Ptr srcChars, + int32_t srcLength) +{ return doAppend(srcChars, 0, srcLength); } + +inline UnicodeString& +UnicodeString::append(char16_t srcChar) +{ return doAppend(&srcChar, 0, 1); } + +inline UnicodeString& +UnicodeString::operator+= (char16_t ch) +{ return doAppend(&ch, 0, 1); } + +inline UnicodeString& +UnicodeString::operator+= (UChar32 ch) { + return append(ch); +} + +inline UnicodeString& +UnicodeString::operator+= (const UnicodeString& srcText) +{ return doAppend(srcText, 0, srcText.length()); } + +inline UnicodeString& +UnicodeString::insert(int32_t start, + const UnicodeString& srcText, + int32_t srcStart, + int32_t srcLength) +{ return doReplace(start, 0, srcText, srcStart, srcLength); } + +inline UnicodeString& +UnicodeString::insert(int32_t start, + const UnicodeString& srcText) +{ return doReplace(start, 0, srcText, 0, srcText.length()); } + +inline UnicodeString& +UnicodeString::insert(int32_t start, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) +{ return doReplace(start, 0, srcChars, srcStart, srcLength); } + +inline UnicodeString& +UnicodeString::insert(int32_t start, + ConstChar16Ptr srcChars, + int32_t srcLength) +{ return doReplace(start, 0, srcChars, 0, srcLength); } + +inline UnicodeString& +UnicodeString::insert(int32_t start, + char16_t srcChar) +{ return doReplace(start, 0, &srcChar, 0, 1); } + +inline UnicodeString& +UnicodeString::insert(int32_t start, + UChar32 srcChar) +{ return replace(start, 0, srcChar); } + + +inline UnicodeString& +UnicodeString::remove() +{ + // remove() of a bogus string makes the string empty and non-bogus + if(isBogus()) { + setToEmpty(); + } else { + setZeroLength(); + } + return *this; +} + +inline UnicodeString& +UnicodeString::remove(int32_t start, + int32_t _length) +{ + if(start <= 0 && _length == INT32_MAX) { + // remove(guaranteed everything) of a bogus string makes the string empty and non-bogus + return remove(); + } + return doReplace(start, _length, nullptr, 0, 0); +} + +inline UnicodeString& +UnicodeString::removeBetween(int32_t start, + int32_t limit) +{ return doReplace(start, limit - start, nullptr, 0, 0); } + +inline UnicodeString & +UnicodeString::retainBetween(int32_t start, int32_t limit) { + truncate(limit); + return doReplace(0, start, nullptr, 0, 0); +} + +inline UBool +UnicodeString::truncate(int32_t targetLength) +{ + if(isBogus() && targetLength == 0) { + // truncate(0) of a bogus string makes the string empty and non-bogus + unBogus(); + return false; + } else if((uint32_t)targetLength < (uint32_t)length()) { + setLength(targetLength); + return true; + } else { + return false; + } +} + +inline UnicodeString& +UnicodeString::reverse() +{ return doReverse(0, length()); } + +inline UnicodeString& +UnicodeString::reverse(int32_t start, + int32_t _length) +{ return doReverse(start, _length); } + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/unorm.h b/deps/icu-small/source/common/unicode/unorm.h index 38fb8951557c0f..5084a8868bb42d 100644 --- a/deps/icu-small/source/common/unicode/unorm.h +++ b/deps/icu-small/source/common/unicode/unorm.h @@ -1,476 +1,476 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (c) 1996-2016, International Business Machines Corporation -* and others. All Rights Reserved. -******************************************************************************* -* File unorm.h -* -* Created by: Vladimir Weinstein 12052000 -* -* Modification history : -* -* Date Name Description -* 02/01/01 synwee Added normalization quickcheck enum and method. -*/ -#ifndef UNORM_H -#define UNORM_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_NORMALIZATION - -#include "unicode/uiter.h" -#include "unicode/unorm2.h" - -/** - * \file - * \brief C API: Unicode Normalization - * - * Old Unicode normalization API. - * - * This API has been replaced by the unorm2.h API and is only available - * for backward compatibility. The functions here simply delegate to the - * unorm2.h functions, for example unorm2_getInstance() and unorm2_normalize(). - * There is one exception: The new API does not provide a replacement for unorm_compare(). - * Its declaration has been moved to unorm2.h. - * - * unorm_normalize transforms Unicode text into an equivalent composed or - * decomposed form, allowing for easier sorting and searching of text. - * unorm_normalize supports the standard normalization forms described in - * - * Unicode Standard Annex #15: Unicode Normalization Forms. - * - * Characters with accents or other adornments can be encoded in - * several different ways in Unicode. For example, take the character A-acute. - * In Unicode, this can be encoded as a single character (the - * "composed" form): - * - * \code - * 00C1 LATIN CAPITAL LETTER A WITH ACUTE - * \endcode - * - * or as two separate characters (the "decomposed" form): - * - * \code - * 0041 LATIN CAPITAL LETTER A - * 0301 COMBINING ACUTE ACCENT - * \endcode - * - * To a user of your program, however, both of these sequences should be - * treated as the same "user-level" character "A with acute accent". When you are searching or - * comparing text, you must ensure that these two sequences are treated - * equivalently. In addition, you must handle characters with more than one - * accent. Sometimes the order of a character's combining accents is - * significant, while in other cases accent sequences in different orders are - * really equivalent. - * - * Similarly, the string "ffi" can be encoded as three separate letters: - * - * \code - * 0066 LATIN SMALL LETTER F - * 0066 LATIN SMALL LETTER F - * 0069 LATIN SMALL LETTER I - * \endcode - * - * or as the single character - * - * \code - * FB03 LATIN SMALL LIGATURE FFI - * \endcode - * - * The ffi ligature is not a distinct semantic character, and strictly speaking - * it shouldn't be in Unicode at all, but it was included for compatibility - * with existing character sets that already provided it. The Unicode standard - * identifies such characters by giving them "compatibility" decompositions - * into the corresponding semantic characters. When sorting and searching, you - * will often want to use these mappings. - * - * unorm_normalize helps solve these problems by transforming text into the - * canonical composed and decomposed forms as shown in the first example above. - * In addition, you can have it perform compatibility decompositions so that - * you can treat compatibility characters the same as their equivalents. - * Finally, unorm_normalize rearranges accents into the proper canonical - * order, so that you do not have to worry about accent rearrangement on your - * own. - * - * Form FCD, "Fast C or D", is also designed for collation. - * It allows to work on strings that are not necessarily normalized - * with an algorithm (like in collation) that works under "canonical closure", i.e., it treats precomposed - * characters and their decomposed equivalents the same. - * - * It is not a normalization form because it does not provide for uniqueness of representation. Multiple strings - * may be canonically equivalent (their NFDs are identical) and may all conform to FCD without being identical - * themselves. - * - * The form is defined such that the "raw decomposition", the recursive canonical decomposition of each character, - * results in a string that is canonically ordered. This means that precomposed characters are allowed for as long - * as their decompositions do not need canonical reordering. - * - * Its advantage for a process like collation is that all NFD and most NFC texts - and many unnormalized texts - - * already conform to FCD and do not need to be normalized (NFD) for such a process. The FCD quick check will - * return UNORM_YES for most strings in practice. - * - * unorm_normalize(UNORM_FCD) may be implemented with UNORM_NFD. - * - * For more details on FCD see the collation design document: - * https://htmlpreview.github.io/?https://github.com/unicode-org/icu-docs/blob/main/design/collation/ICU_collation_design.htm - * - * ICU collation performs either NFD or FCD normalization automatically if normalization - * is turned on for the collator object. - * Beyond collation and string search, normalized strings may be useful for string equivalence comparisons, - * transliteration/transcription, unique representations, etc. - * - * The W3C generally recommends to exchange texts in NFC. - * Note also that most legacy character encodings use only precomposed forms and often do not - * encode any combining marks by themselves. For conversion to such character encodings the - * Unicode text needs to be normalized to NFC. - * For more usage examples, see the Unicode Standard Annex. - */ - -// Do not conditionalize the following enum with #ifndef U_HIDE_DEPRECATED_API, -// it is needed for layout of Normalizer object. -#ifndef U_FORCE_HIDE_DEPRECATED_API - -/** - * Constants for normalization modes. - * @deprecated ICU 56 Use unorm2.h instead. - */ -typedef enum { - /** No decomposition/composition. @deprecated ICU 56 Use unorm2.h instead. */ - UNORM_NONE = 1, - /** Canonical decomposition. @deprecated ICU 56 Use unorm2.h instead. */ - UNORM_NFD = 2, - /** Compatibility decomposition. @deprecated ICU 56 Use unorm2.h instead. */ - UNORM_NFKD = 3, - /** Canonical decomposition followed by canonical composition. @deprecated ICU 56 Use unorm2.h instead. */ - UNORM_NFC = 4, - /** Default normalization. @deprecated ICU 56 Use unorm2.h instead. */ - UNORM_DEFAULT = UNORM_NFC, - /** Compatibility decomposition followed by canonical composition. @deprecated ICU 56 Use unorm2.h instead. */ - UNORM_NFKC =5, - /** "Fast C or D" form. @deprecated ICU 56 Use unorm2.h instead. */ - UNORM_FCD = 6, - - /** One more than the highest normalization mode constant. @deprecated ICU 56 Use unorm2.h instead. */ - UNORM_MODE_COUNT -} UNormalizationMode; - -#endif // U_FORCE_HIDE_DEPRECATED_API - -#ifndef U_HIDE_DEPRECATED_API - -/** - * Constants for options flags for normalization. - * Use 0 for default options, - * including normalization according to the Unicode version - * that is currently supported by ICU (see u_getUnicodeVersion). - * @deprecated ICU 56 Use unorm2.h instead. - */ -enum { - /** - * Options bit set value to select Unicode 3.2 normalization - * (except NormalizationCorrections). - * At most one Unicode version can be selected at a time. - * @deprecated ICU 56 Use unorm2.h instead. - */ - UNORM_UNICODE_3_2=0x20 -}; - -/** - * Lowest-order bit number of unorm_compare() options bits corresponding to - * normalization options bits. - * - * The options parameter for unorm_compare() uses most bits for - * itself and for various comparison and folding flags. - * The most significant bits, however, are shifted down and passed on - * to the normalization implementation. - * (That is, from unorm_compare(..., options, ...), - * options>>UNORM_COMPARE_NORM_OPTIONS_SHIFT will be passed on to the - * internal normalization functions.) - * - * @see unorm_compare - * @deprecated ICU 56 Use unorm2.h instead. - */ -#define UNORM_COMPARE_NORM_OPTIONS_SHIFT 20 - -/** - * Normalize a string. - * The string will be normalized according the specified normalization mode - * and options. - * The source and result buffers must not be the same, nor overlap. - * - * @param source The string to normalize. - * @param sourceLength The length of source, or -1 if NUL-terminated. - * @param mode The normalization mode; one of UNORM_NONE, - * UNORM_NFD, UNORM_NFC, UNORM_NFKC, UNORM_NFKD, UNORM_DEFAULT. - * @param options The normalization options, ORed together (0 for no options). - * @param result A pointer to a buffer to receive the result string. - * The result string is NUL-terminated if possible. - * @param resultLength The maximum size of result. - * @param status A pointer to a UErrorCode to receive any errors. - * @return The total buffer size needed; if greater than resultLength, - * the output was truncated, and the error code is set to U_BUFFER_OVERFLOW_ERROR. - * @deprecated ICU 56 Use unorm2.h instead. - */ -U_DEPRECATED int32_t U_EXPORT2 -unorm_normalize(const UChar *source, int32_t sourceLength, - UNormalizationMode mode, int32_t options, - UChar *result, int32_t resultLength, - UErrorCode *status); - -/** - * Performing quick check on a string, to quickly determine if the string is - * in a particular normalization format. - * Three types of result can be returned UNORM_YES, UNORM_NO or - * UNORM_MAYBE. Result UNORM_YES indicates that the argument - * string is in the desired normalized format, UNORM_NO determines that - * argument string is not in the desired normalized format. A - * UNORM_MAYBE result indicates that a more thorough check is required, - * the user may have to put the string in its normalized form and compare the - * results. - * - * @param source string for determining if it is in a normalized format - * @param sourcelength length of source to test, or -1 if NUL-terminated - * @param mode which normalization form to test for - * @param status a pointer to a UErrorCode to receive any errors - * @return UNORM_YES, UNORM_NO or UNORM_MAYBE - * - * @see unorm_isNormalized - * @deprecated ICU 56 Use unorm2.h instead. - */ -U_DEPRECATED UNormalizationCheckResult U_EXPORT2 -unorm_quickCheck(const UChar *source, int32_t sourcelength, - UNormalizationMode mode, - UErrorCode *status); - -/** - * Performing quick check on a string; same as unorm_quickCheck but - * takes an extra options parameter like most normalization functions. - * - * @param src String that is to be tested if it is in a normalization format. - * @param srcLength Length of source to test, or -1 if NUL-terminated. - * @param mode Which normalization form to test for. - * @param options The normalization options, ORed together (0 for no options). - * @param pErrorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return UNORM_YES, UNORM_NO or UNORM_MAYBE - * - * @see unorm_quickCheck - * @see unorm_isNormalized - * @deprecated ICU 56 Use unorm2.h instead. - */ -U_DEPRECATED UNormalizationCheckResult U_EXPORT2 -unorm_quickCheckWithOptions(const UChar *src, int32_t srcLength, - UNormalizationMode mode, int32_t options, - UErrorCode *pErrorCode); - -/** - * Test if a string is in a given normalization form. - * This is semantically equivalent to source.equals(normalize(source, mode)) . - * - * Unlike unorm_quickCheck(), this function returns a definitive result, - * never a "maybe". - * For NFD, NFKD, and FCD, both functions work exactly the same. - * For NFC and NFKC where quickCheck may return "maybe", this function will - * perform further tests to arrive at a true/false result. - * - * @param src String that is to be tested if it is in a normalization format. - * @param srcLength Length of source to test, or -1 if NUL-terminated. - * @param mode Which normalization form to test for. - * @param pErrorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return Boolean value indicating whether the source string is in the - * "mode" normalization form. - * - * @see unorm_quickCheck - * @deprecated ICU 56 Use unorm2.h instead. - */ -U_DEPRECATED UBool U_EXPORT2 -unorm_isNormalized(const UChar *src, int32_t srcLength, - UNormalizationMode mode, - UErrorCode *pErrorCode); - -/** - * Test if a string is in a given normalization form; same as unorm_isNormalized but - * takes an extra options parameter like most normalization functions. - * - * @param src String that is to be tested if it is in a normalization format. - * @param srcLength Length of source to test, or -1 if NUL-terminated. - * @param mode Which normalization form to test for. - * @param options The normalization options, ORed together (0 for no options). - * @param pErrorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return Boolean value indicating whether the source string is in the - * "mode/options" normalization form. - * - * @see unorm_quickCheck - * @see unorm_isNormalized - * @deprecated ICU 56 Use unorm2.h instead. - */ -U_DEPRECATED UBool U_EXPORT2 -unorm_isNormalizedWithOptions(const UChar *src, int32_t srcLength, - UNormalizationMode mode, int32_t options, - UErrorCode *pErrorCode); - -/** - * Iterative normalization forward. - * This function (together with unorm_previous) is somewhat - * similar to the C++ Normalizer class (see its non-static functions). - * - * Iterative normalization is useful when only a small portion of a longer - * string/text needs to be processed. - * - * For example, the likelihood may be high that processing the first 10% of some - * text will be sufficient to find certain data. - * Another example: When one wants to concatenate two normalized strings and get a - * normalized result, it is much more efficient to normalize just a small part of - * the result around the concatenation place instead of re-normalizing everything. - * - * The input text is an instance of the C character iteration API UCharIterator. - * It may wrap around a simple string, a CharacterIterator, a Replaceable, or any - * other kind of text object. - * - * If a buffer overflow occurs, then the caller needs to reset the iterator to the - * old index and call the function again with a larger buffer - if the caller cares - * for the actual output. - * Regardless of the output buffer, the iterator will always be moved to the next - * normalization boundary. - * - * This function (like unorm_previous) serves two purposes: - * - * 1) To find the next boundary so that the normalization of the part of the text - * from the current position to that boundary does not affect and is not affected - * by the part of the text beyond that boundary. - * - * 2) To normalize the text up to the boundary. - * - * The second step is optional, per the doNormalize parameter. - * It is omitted for operations like string concatenation, where the two adjacent - * string ends need to be normalized together. - * In such a case, the output buffer will just contain a copy of the text up to the - * boundary. - * - * pNeededToNormalize is an output-only parameter. Its output value is only defined - * if normalization was requested (doNormalize) and successful (especially, no - * buffer overflow). - * It is useful for operations like a normalizing transliterator, where one would - * not want to replace a piece of text if it is not modified. - * - * If doNormalize==true and pNeededToNormalize!=NULL then *pNeeded... is set true - * if the normalization was necessary. - * - * If doNormalize==false then *pNeededToNormalize will be set to false. - * - * If the buffer overflows, then *pNeededToNormalize will be undefined; - * essentially, whenever U_FAILURE is true (like in buffer overflows), this result - * will be undefined. - * - * @param src The input text in the form of a C character iterator. - * @param dest The output buffer; can be NULL if destCapacity==0 for pure preflighting. - * @param destCapacity The number of UChars that fit into dest. - * @param mode The normalization mode. - * @param options The normalization options, ORed together (0 for no options). - * @param doNormalize Indicates if the source text up to the next boundary - * is to be normalized (true) or just copied (false). - * @param pNeededToNormalize Output flag indicating if the normalization resulted in - * different text from the input. - * Not defined if an error occurs including buffer overflow. - * Always false if !doNormalize. - * @param pErrorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return Length of output (number of UChars) when successful or buffer overflow. - * - * @see unorm_previous - * @see unorm_normalize - * - * @deprecated ICU 56 Use unorm2.h instead. - */ -U_DEPRECATED int32_t U_EXPORT2 -unorm_next(UCharIterator *src, - UChar *dest, int32_t destCapacity, - UNormalizationMode mode, int32_t options, - UBool doNormalize, UBool *pNeededToNormalize, - UErrorCode *pErrorCode); - -/** - * Iterative normalization backward. - * This function (together with unorm_next) is somewhat - * similar to the C++ Normalizer class (see its non-static functions). - * For all details see unorm_next. - * - * @param src The input text in the form of a C character iterator. - * @param dest The output buffer; can be NULL if destCapacity==0 for pure preflighting. - * @param destCapacity The number of UChars that fit into dest. - * @param mode The normalization mode. - * @param options The normalization options, ORed together (0 for no options). - * @param doNormalize Indicates if the source text up to the next boundary - * is to be normalized (true) or just copied (false). - * @param pNeededToNormalize Output flag indicating if the normalization resulted in - * different text from the input. - * Not defined if an error occurs including buffer overflow. - * Always false if !doNormalize. - * @param pErrorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return Length of output (number of UChars) when successful or buffer overflow. - * - * @see unorm_next - * @see unorm_normalize - * - * @deprecated ICU 56 Use unorm2.h instead. - */ -U_DEPRECATED int32_t U_EXPORT2 -unorm_previous(UCharIterator *src, - UChar *dest, int32_t destCapacity, - UNormalizationMode mode, int32_t options, - UBool doNormalize, UBool *pNeededToNormalize, - UErrorCode *pErrorCode); - -/** - * Concatenate normalized strings, making sure that the result is normalized as well. - * - * If both the left and the right strings are in - * the normalization form according to "mode/options", - * then the result will be - * - * \code - * dest=normalize(left+right, mode, options) - * \endcode - * - * With the input strings already being normalized, - * this function will use unorm_next() and unorm_previous() - * to find the adjacent end pieces of the input strings. - * Only the concatenation of these end pieces will be normalized and - * then concatenated with the remaining parts of the input strings. - * - * It is allowed to have dest==left to avoid copying the entire left string. - * - * @param left Left source string, may be same as dest. - * @param leftLength Length of left source string, or -1 if NUL-terminated. - * @param right Right source string. Must not be the same as dest, nor overlap. - * @param rightLength Length of right source string, or -1 if NUL-terminated. - * @param dest The output buffer; can be NULL if destCapacity==0 for pure preflighting. - * @param destCapacity The number of UChars that fit into dest. - * @param mode The normalization mode. - * @param options The normalization options, ORed together (0 for no options). - * @param pErrorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return Length of output (number of UChars) when successful or buffer overflow. - * - * @see unorm_normalize - * @see unorm_next - * @see unorm_previous - * - * @deprecated ICU 56 Use unorm2.h instead. - */ -U_DEPRECATED int32_t U_EXPORT2 -unorm_concatenate(const UChar *left, int32_t leftLength, - const UChar *right, int32_t rightLength, - UChar *dest, int32_t destCapacity, - UNormalizationMode mode, int32_t options, - UErrorCode *pErrorCode); - -#endif /* U_HIDE_DEPRECATED_API */ -#endif /* #if !UCONFIG_NO_NORMALIZATION */ -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (c) 1996-2016, International Business Machines Corporation +* and others. All Rights Reserved. +******************************************************************************* +* File unorm.h +* +* Created by: Vladimir Weinstein 12052000 +* +* Modification history : +* +* Date Name Description +* 02/01/01 synwee Added normalization quickcheck enum and method. +*/ +#ifndef UNORM_H +#define UNORM_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_NORMALIZATION + +#include "unicode/uiter.h" +#include "unicode/unorm2.h" + +/** + * \file + * \brief C API: Unicode Normalization + * + * Old Unicode normalization API. + * + * This API has been replaced by the unorm2.h API and is only available + * for backward compatibility. The functions here simply delegate to the + * unorm2.h functions, for example unorm2_getInstance() and unorm2_normalize(). + * There is one exception: The new API does not provide a replacement for unorm_compare(). + * Its declaration has been moved to unorm2.h. + * + * unorm_normalize transforms Unicode text into an equivalent composed or + * decomposed form, allowing for easier sorting and searching of text. + * unorm_normalize supports the standard normalization forms described in + * + * Unicode Standard Annex #15: Unicode Normalization Forms. + * + * Characters with accents or other adornments can be encoded in + * several different ways in Unicode. For example, take the character A-acute. + * In Unicode, this can be encoded as a single character (the + * "composed" form): + * + * \code + * 00C1 LATIN CAPITAL LETTER A WITH ACUTE + * \endcode + * + * or as two separate characters (the "decomposed" form): + * + * \code + * 0041 LATIN CAPITAL LETTER A + * 0301 COMBINING ACUTE ACCENT + * \endcode + * + * To a user of your program, however, both of these sequences should be + * treated as the same "user-level" character "A with acute accent". When you are searching or + * comparing text, you must ensure that these two sequences are treated + * equivalently. In addition, you must handle characters with more than one + * accent. Sometimes the order of a character's combining accents is + * significant, while in other cases accent sequences in different orders are + * really equivalent. + * + * Similarly, the string "ffi" can be encoded as three separate letters: + * + * \code + * 0066 LATIN SMALL LETTER F + * 0066 LATIN SMALL LETTER F + * 0069 LATIN SMALL LETTER I + * \endcode + * + * or as the single character + * + * \code + * FB03 LATIN SMALL LIGATURE FFI + * \endcode + * + * The ffi ligature is not a distinct semantic character, and strictly speaking + * it shouldn't be in Unicode at all, but it was included for compatibility + * with existing character sets that already provided it. The Unicode standard + * identifies such characters by giving them "compatibility" decompositions + * into the corresponding semantic characters. When sorting and searching, you + * will often want to use these mappings. + * + * unorm_normalize helps solve these problems by transforming text into the + * canonical composed and decomposed forms as shown in the first example above. + * In addition, you can have it perform compatibility decompositions so that + * you can treat compatibility characters the same as their equivalents. + * Finally, unorm_normalize rearranges accents into the proper canonical + * order, so that you do not have to worry about accent rearrangement on your + * own. + * + * Form FCD, "Fast C or D", is also designed for collation. + * It allows to work on strings that are not necessarily normalized + * with an algorithm (like in collation) that works under "canonical closure", i.e., it treats precomposed + * characters and their decomposed equivalents the same. + * + * It is not a normalization form because it does not provide for uniqueness of representation. Multiple strings + * may be canonically equivalent (their NFDs are identical) and may all conform to FCD without being identical + * themselves. + * + * The form is defined such that the "raw decomposition", the recursive canonical decomposition of each character, + * results in a string that is canonically ordered. This means that precomposed characters are allowed for as long + * as their decompositions do not need canonical reordering. + * + * Its advantage for a process like collation is that all NFD and most NFC texts - and many unnormalized texts - + * already conform to FCD and do not need to be normalized (NFD) for such a process. The FCD quick check will + * return UNORM_YES for most strings in practice. + * + * unorm_normalize(UNORM_FCD) may be implemented with UNORM_NFD. + * + * For more details on FCD see the collation design document: + * https://htmlpreview.github.io/?https://github.com/unicode-org/icu-docs/blob/main/design/collation/ICU_collation_design.htm + * + * ICU collation performs either NFD or FCD normalization automatically if normalization + * is turned on for the collator object. + * Beyond collation and string search, normalized strings may be useful for string equivalence comparisons, + * transliteration/transcription, unique representations, etc. + * + * The W3C generally recommends to exchange texts in NFC. + * Note also that most legacy character encodings use only precomposed forms and often do not + * encode any combining marks by themselves. For conversion to such character encodings the + * Unicode text needs to be normalized to NFC. + * For more usage examples, see the Unicode Standard Annex. + */ + +// Do not conditionalize the following enum with #ifndef U_HIDE_DEPRECATED_API, +// it is needed for layout of Normalizer object. +#ifndef U_FORCE_HIDE_DEPRECATED_API + +/** + * Constants for normalization modes. + * @deprecated ICU 56 Use unorm2.h instead. + */ +typedef enum { + /** No decomposition/composition. @deprecated ICU 56 Use unorm2.h instead. */ + UNORM_NONE = 1, + /** Canonical decomposition. @deprecated ICU 56 Use unorm2.h instead. */ + UNORM_NFD = 2, + /** Compatibility decomposition. @deprecated ICU 56 Use unorm2.h instead. */ + UNORM_NFKD = 3, + /** Canonical decomposition followed by canonical composition. @deprecated ICU 56 Use unorm2.h instead. */ + UNORM_NFC = 4, + /** Default normalization. @deprecated ICU 56 Use unorm2.h instead. */ + UNORM_DEFAULT = UNORM_NFC, + /** Compatibility decomposition followed by canonical composition. @deprecated ICU 56 Use unorm2.h instead. */ + UNORM_NFKC =5, + /** "Fast C or D" form. @deprecated ICU 56 Use unorm2.h instead. */ + UNORM_FCD = 6, + + /** One more than the highest normalization mode constant. @deprecated ICU 56 Use unorm2.h instead. */ + UNORM_MODE_COUNT +} UNormalizationMode; + +#endif // U_FORCE_HIDE_DEPRECATED_API + +#ifndef U_HIDE_DEPRECATED_API + +/** + * Constants for options flags for normalization. + * Use 0 for default options, + * including normalization according to the Unicode version + * that is currently supported by ICU (see u_getUnicodeVersion). + * @deprecated ICU 56 Use unorm2.h instead. + */ +enum { + /** + * Options bit set value to select Unicode 3.2 normalization + * (except NormalizationCorrections). + * At most one Unicode version can be selected at a time. + * @deprecated ICU 56 Use unorm2.h instead. + */ + UNORM_UNICODE_3_2=0x20 +}; + +/** + * Lowest-order bit number of unorm_compare() options bits corresponding to + * normalization options bits. + * + * The options parameter for unorm_compare() uses most bits for + * itself and for various comparison and folding flags. + * The most significant bits, however, are shifted down and passed on + * to the normalization implementation. + * (That is, from unorm_compare(..., options, ...), + * options>>UNORM_COMPARE_NORM_OPTIONS_SHIFT will be passed on to the + * internal normalization functions.) + * + * @see unorm_compare + * @deprecated ICU 56 Use unorm2.h instead. + */ +#define UNORM_COMPARE_NORM_OPTIONS_SHIFT 20 + +/** + * Normalize a string. + * The string will be normalized according the specified normalization mode + * and options. + * The source and result buffers must not be the same, nor overlap. + * + * @param source The string to normalize. + * @param sourceLength The length of source, or -1 if NUL-terminated. + * @param mode The normalization mode; one of UNORM_NONE, + * UNORM_NFD, UNORM_NFC, UNORM_NFKC, UNORM_NFKD, UNORM_DEFAULT. + * @param options The normalization options, ORed together (0 for no options). + * @param result A pointer to a buffer to receive the result string. + * The result string is NUL-terminated if possible. + * @param resultLength The maximum size of result. + * @param status A pointer to a UErrorCode to receive any errors. + * @return The total buffer size needed; if greater than resultLength, + * the output was truncated, and the error code is set to U_BUFFER_OVERFLOW_ERROR. + * @deprecated ICU 56 Use unorm2.h instead. + */ +U_DEPRECATED int32_t U_EXPORT2 +unorm_normalize(const UChar *source, int32_t sourceLength, + UNormalizationMode mode, int32_t options, + UChar *result, int32_t resultLength, + UErrorCode *status); + +/** + * Performing quick check on a string, to quickly determine if the string is + * in a particular normalization format. + * Three types of result can be returned UNORM_YES, UNORM_NO or + * UNORM_MAYBE. Result UNORM_YES indicates that the argument + * string is in the desired normalized format, UNORM_NO determines that + * argument string is not in the desired normalized format. A + * UNORM_MAYBE result indicates that a more thorough check is required, + * the user may have to put the string in its normalized form and compare the + * results. + * + * @param source string for determining if it is in a normalized format + * @param sourcelength length of source to test, or -1 if NUL-terminated + * @param mode which normalization form to test for + * @param status a pointer to a UErrorCode to receive any errors + * @return UNORM_YES, UNORM_NO or UNORM_MAYBE + * + * @see unorm_isNormalized + * @deprecated ICU 56 Use unorm2.h instead. + */ +U_DEPRECATED UNormalizationCheckResult U_EXPORT2 +unorm_quickCheck(const UChar *source, int32_t sourcelength, + UNormalizationMode mode, + UErrorCode *status); + +/** + * Performing quick check on a string; same as unorm_quickCheck but + * takes an extra options parameter like most normalization functions. + * + * @param src String that is to be tested if it is in a normalization format. + * @param srcLength Length of source to test, or -1 if NUL-terminated. + * @param mode Which normalization form to test for. + * @param options The normalization options, ORed together (0 for no options). + * @param pErrorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return UNORM_YES, UNORM_NO or UNORM_MAYBE + * + * @see unorm_quickCheck + * @see unorm_isNormalized + * @deprecated ICU 56 Use unorm2.h instead. + */ +U_DEPRECATED UNormalizationCheckResult U_EXPORT2 +unorm_quickCheckWithOptions(const UChar *src, int32_t srcLength, + UNormalizationMode mode, int32_t options, + UErrorCode *pErrorCode); + +/** + * Test if a string is in a given normalization form. + * This is semantically equivalent to source.equals(normalize(source, mode)) . + * + * Unlike unorm_quickCheck(), this function returns a definitive result, + * never a "maybe". + * For NFD, NFKD, and FCD, both functions work exactly the same. + * For NFC and NFKC where quickCheck may return "maybe", this function will + * perform further tests to arrive at a true/false result. + * + * @param src String that is to be tested if it is in a normalization format. + * @param srcLength Length of source to test, or -1 if NUL-terminated. + * @param mode Which normalization form to test for. + * @param pErrorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return Boolean value indicating whether the source string is in the + * "mode" normalization form. + * + * @see unorm_quickCheck + * @deprecated ICU 56 Use unorm2.h instead. + */ +U_DEPRECATED UBool U_EXPORT2 +unorm_isNormalized(const UChar *src, int32_t srcLength, + UNormalizationMode mode, + UErrorCode *pErrorCode); + +/** + * Test if a string is in a given normalization form; same as unorm_isNormalized but + * takes an extra options parameter like most normalization functions. + * + * @param src String that is to be tested if it is in a normalization format. + * @param srcLength Length of source to test, or -1 if NUL-terminated. + * @param mode Which normalization form to test for. + * @param options The normalization options, ORed together (0 for no options). + * @param pErrorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return Boolean value indicating whether the source string is in the + * "mode/options" normalization form. + * + * @see unorm_quickCheck + * @see unorm_isNormalized + * @deprecated ICU 56 Use unorm2.h instead. + */ +U_DEPRECATED UBool U_EXPORT2 +unorm_isNormalizedWithOptions(const UChar *src, int32_t srcLength, + UNormalizationMode mode, int32_t options, + UErrorCode *pErrorCode); + +/** + * Iterative normalization forward. + * This function (together with unorm_previous) is somewhat + * similar to the C++ Normalizer class (see its non-static functions). + * + * Iterative normalization is useful when only a small portion of a longer + * string/text needs to be processed. + * + * For example, the likelihood may be high that processing the first 10% of some + * text will be sufficient to find certain data. + * Another example: When one wants to concatenate two normalized strings and get a + * normalized result, it is much more efficient to normalize just a small part of + * the result around the concatenation place instead of re-normalizing everything. + * + * The input text is an instance of the C character iteration API UCharIterator. + * It may wrap around a simple string, a CharacterIterator, a Replaceable, or any + * other kind of text object. + * + * If a buffer overflow occurs, then the caller needs to reset the iterator to the + * old index and call the function again with a larger buffer - if the caller cares + * for the actual output. + * Regardless of the output buffer, the iterator will always be moved to the next + * normalization boundary. + * + * This function (like unorm_previous) serves two purposes: + * + * 1) To find the next boundary so that the normalization of the part of the text + * from the current position to that boundary does not affect and is not affected + * by the part of the text beyond that boundary. + * + * 2) To normalize the text up to the boundary. + * + * The second step is optional, per the doNormalize parameter. + * It is omitted for operations like string concatenation, where the two adjacent + * string ends need to be normalized together. + * In such a case, the output buffer will just contain a copy of the text up to the + * boundary. + * + * pNeededToNormalize is an output-only parameter. Its output value is only defined + * if normalization was requested (doNormalize) and successful (especially, no + * buffer overflow). + * It is useful for operations like a normalizing transliterator, where one would + * not want to replace a piece of text if it is not modified. + * + * If doNormalize==true and pNeededToNormalize!=NULL then *pNeeded... is set true + * if the normalization was necessary. + * + * If doNormalize==false then *pNeededToNormalize will be set to false. + * + * If the buffer overflows, then *pNeededToNormalize will be undefined; + * essentially, whenever U_FAILURE is true (like in buffer overflows), this result + * will be undefined. + * + * @param src The input text in the form of a C character iterator. + * @param dest The output buffer; can be NULL if destCapacity==0 for pure preflighting. + * @param destCapacity The number of UChars that fit into dest. + * @param mode The normalization mode. + * @param options The normalization options, ORed together (0 for no options). + * @param doNormalize Indicates if the source text up to the next boundary + * is to be normalized (true) or just copied (false). + * @param pNeededToNormalize Output flag indicating if the normalization resulted in + * different text from the input. + * Not defined if an error occurs including buffer overflow. + * Always false if !doNormalize. + * @param pErrorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return Length of output (number of UChars) when successful or buffer overflow. + * + * @see unorm_previous + * @see unorm_normalize + * + * @deprecated ICU 56 Use unorm2.h instead. + */ +U_DEPRECATED int32_t U_EXPORT2 +unorm_next(UCharIterator *src, + UChar *dest, int32_t destCapacity, + UNormalizationMode mode, int32_t options, + UBool doNormalize, UBool *pNeededToNormalize, + UErrorCode *pErrorCode); + +/** + * Iterative normalization backward. + * This function (together with unorm_next) is somewhat + * similar to the C++ Normalizer class (see its non-static functions). + * For all details see unorm_next. + * + * @param src The input text in the form of a C character iterator. + * @param dest The output buffer; can be NULL if destCapacity==0 for pure preflighting. + * @param destCapacity The number of UChars that fit into dest. + * @param mode The normalization mode. + * @param options The normalization options, ORed together (0 for no options). + * @param doNormalize Indicates if the source text up to the next boundary + * is to be normalized (true) or just copied (false). + * @param pNeededToNormalize Output flag indicating if the normalization resulted in + * different text from the input. + * Not defined if an error occurs including buffer overflow. + * Always false if !doNormalize. + * @param pErrorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return Length of output (number of UChars) when successful or buffer overflow. + * + * @see unorm_next + * @see unorm_normalize + * + * @deprecated ICU 56 Use unorm2.h instead. + */ +U_DEPRECATED int32_t U_EXPORT2 +unorm_previous(UCharIterator *src, + UChar *dest, int32_t destCapacity, + UNormalizationMode mode, int32_t options, + UBool doNormalize, UBool *pNeededToNormalize, + UErrorCode *pErrorCode); + +/** + * Concatenate normalized strings, making sure that the result is normalized as well. + * + * If both the left and the right strings are in + * the normalization form according to "mode/options", + * then the result will be + * + * \code + * dest=normalize(left+right, mode, options) + * \endcode + * + * With the input strings already being normalized, + * this function will use unorm_next() and unorm_previous() + * to find the adjacent end pieces of the input strings. + * Only the concatenation of these end pieces will be normalized and + * then concatenated with the remaining parts of the input strings. + * + * It is allowed to have dest==left to avoid copying the entire left string. + * + * @param left Left source string, may be same as dest. + * @param leftLength Length of left source string, or -1 if NUL-terminated. + * @param right Right source string. Must not be the same as dest, nor overlap. + * @param rightLength Length of right source string, or -1 if NUL-terminated. + * @param dest The output buffer; can be NULL if destCapacity==0 for pure preflighting. + * @param destCapacity The number of UChars that fit into dest. + * @param mode The normalization mode. + * @param options The normalization options, ORed together (0 for no options). + * @param pErrorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return Length of output (number of UChars) when successful or buffer overflow. + * + * @see unorm_normalize + * @see unorm_next + * @see unorm_previous + * + * @deprecated ICU 56 Use unorm2.h instead. + */ +U_DEPRECATED int32_t U_EXPORT2 +unorm_concatenate(const UChar *left, int32_t leftLength, + const UChar *right, int32_t rightLength, + UChar *dest, int32_t destCapacity, + UNormalizationMode mode, int32_t options, + UErrorCode *pErrorCode); + +#endif /* U_HIDE_DEPRECATED_API */ +#endif /* #if !UCONFIG_NO_NORMALIZATION */ +#endif diff --git a/deps/icu-small/source/common/unicode/unorm2.h b/deps/icu-small/source/common/unicode/unorm2.h index 24417b7103c12e..42c672ca5a0c1a 100644 --- a/deps/icu-small/source/common/unicode/unorm2.h +++ b/deps/icu-small/source/common/unicode/unorm2.h @@ -1,606 +1,606 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2009-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: unorm2.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2009dec15 -* created by: Markus W. Scherer -*/ - -#ifndef __UNORM2_H__ -#define __UNORM2_H__ - -/** - * \file - * \brief C API: New API for Unicode Normalization. - * - * Unicode normalization functionality for standard Unicode normalization or - * for using custom mapping tables. - * All instances of UNormalizer2 are unmodifiable/immutable. - * Instances returned by unorm2_getInstance() are singletons that must not be deleted by the caller. - * For more details see the Normalizer2 C++ class. - */ - -#include "unicode/utypes.h" -#include "unicode/stringoptions.h" -#include "unicode/uset.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -/** - * Constants for normalization modes. - * For details about standard Unicode normalization forms - * and about the algorithms which are also used with custom mapping tables - * see http://www.unicode.org/unicode/reports/tr15/ - * @stable ICU 4.4 - */ -typedef enum { - /** - * Decomposition followed by composition. - * Same as standard NFC when using an "nfc" instance. - * Same as standard NFKC when using an "nfkc" instance. - * For details about standard Unicode normalization forms - * see http://www.unicode.org/unicode/reports/tr15/ - * @stable ICU 4.4 - */ - UNORM2_COMPOSE, - /** - * Map, and reorder canonically. - * Same as standard NFD when using an "nfc" instance. - * Same as standard NFKD when using an "nfkc" instance. - * For details about standard Unicode normalization forms - * see http://www.unicode.org/unicode/reports/tr15/ - * @stable ICU 4.4 - */ - UNORM2_DECOMPOSE, - /** - * "Fast C or D" form. - * If a string is in this form, then further decomposition without reordering - * would yield the same form as DECOMPOSE. - * Text in "Fast C or D" form can be processed efficiently with data tables - * that are "canonically closed", that is, that provide equivalent data for - * equivalent text, without having to be fully normalized. - * Not a standard Unicode normalization form. - * Not a unique form: Different FCD strings can be canonically equivalent. - * For details see http://www.unicode.org/notes/tn5/#FCD - * @stable ICU 4.4 - */ - UNORM2_FCD, - /** - * Compose only contiguously. - * Also known as "FCC" or "Fast C Contiguous". - * The result will often but not always be in NFC. - * The result will conform to FCD which is useful for processing. - * Not a standard Unicode normalization form. - * For details see http://www.unicode.org/notes/tn5/#FCC - * @stable ICU 4.4 - */ - UNORM2_COMPOSE_CONTIGUOUS -} UNormalization2Mode; - -/** - * Result values for normalization quick check functions. - * For details see http://www.unicode.org/reports/tr15/#Detecting_Normalization_Forms - * @stable ICU 2.0 - */ -typedef enum UNormalizationCheckResult { - /** - * The input string is not in the normalization form. - * @stable ICU 2.0 - */ - UNORM_NO, - /** - * The input string is in the normalization form. - * @stable ICU 2.0 - */ - UNORM_YES, - /** - * The input string may or may not be in the normalization form. - * This value is only returned for composition forms like NFC and FCC, - * when a backward-combining character is found for which the surrounding text - * would have to be analyzed further. - * @stable ICU 2.0 - */ - UNORM_MAYBE -} UNormalizationCheckResult; - -/** - * Opaque C service object type for the new normalization API. - * @stable ICU 4.4 - */ -struct UNormalizer2; -typedef struct UNormalizer2 UNormalizer2; /**< C typedef for struct UNormalizer2. @stable ICU 4.4 */ - -#if !UCONFIG_NO_NORMALIZATION - -/** - * Returns a UNormalizer2 instance for Unicode NFC normalization. - * Same as unorm2_getInstance(NULL, "nfc", UNORM2_COMPOSE, pErrorCode). - * Returns an unmodifiable singleton instance. Do not delete it. - * @param pErrorCode Standard 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.) - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ -U_CAPI const UNormalizer2 * U_EXPORT2 -unorm2_getNFCInstance(UErrorCode *pErrorCode); - -/** - * Returns a UNormalizer2 instance for Unicode NFD normalization. - * Same as unorm2_getInstance(NULL, "nfc", UNORM2_DECOMPOSE, pErrorCode). - * Returns an unmodifiable singleton instance. Do not delete it. - * @param pErrorCode Standard 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.) - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ -U_CAPI const UNormalizer2 * U_EXPORT2 -unorm2_getNFDInstance(UErrorCode *pErrorCode); - -/** - * Returns a UNormalizer2 instance for Unicode NFKC normalization. - * Same as unorm2_getInstance(NULL, "nfkc", UNORM2_COMPOSE, pErrorCode). - * Returns an unmodifiable singleton instance. Do not delete it. - * @param pErrorCode Standard 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.) - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ -U_CAPI const UNormalizer2 * U_EXPORT2 -unorm2_getNFKCInstance(UErrorCode *pErrorCode); - -/** - * Returns a UNormalizer2 instance for Unicode NFKD normalization. - * Same as unorm2_getInstance(NULL, "nfkc", UNORM2_DECOMPOSE, pErrorCode). - * Returns an unmodifiable singleton instance. Do not delete it. - * @param pErrorCode Standard 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.) - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ -U_CAPI const UNormalizer2 * U_EXPORT2 -unorm2_getNFKDInstance(UErrorCode *pErrorCode); - -/** - * Returns a UNormalizer2 instance for Unicode NFKC_Casefold normalization. - * Same as unorm2_getInstance(NULL, "nfkc_cf", UNORM2_COMPOSE, pErrorCode). - * Returns an unmodifiable singleton instance. Do not delete it. - * @param pErrorCode Standard 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.) - * @return the requested Normalizer2, if successful - * @stable ICU 49 - */ -U_CAPI const UNormalizer2 * U_EXPORT2 -unorm2_getNFKCCasefoldInstance(UErrorCode *pErrorCode); - -/** - * Returns a UNormalizer2 instance which uses the specified data file - * (packageName/name similar to ucnv_openPackage() and ures_open()/ResourceBundle) - * and which composes or decomposes text according to the specified mode. - * Returns an unmodifiable singleton instance. Do not delete it. - * - * Use packageName=NULL for data files that are part of ICU's own data. - * Use name="nfc" and UNORM2_COMPOSE/UNORM2_DECOMPOSE for Unicode standard NFC/NFD. - * Use name="nfkc" and UNORM2_COMPOSE/UNORM2_DECOMPOSE for Unicode standard NFKC/NFKD. - * Use name="nfkc_cf" and UNORM2_COMPOSE for Unicode standard NFKC_CF=NFKC_Casefold. - * - * @param packageName NULL for ICU built-in data, otherwise application data package name - * @param name "nfc" or "nfkc" or "nfkc_cf" or name of custom data file - * @param mode normalization mode (compose or decompose etc.) - * @param pErrorCode Standard 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.) - * @return the requested UNormalizer2, if successful - * @stable ICU 4.4 - */ -U_CAPI const UNormalizer2 * U_EXPORT2 -unorm2_getInstance(const char *packageName, - const char *name, - UNormalization2Mode mode, - UErrorCode *pErrorCode); - -/** - * Constructs a filtered normalizer wrapping any UNormalizer2 instance - * and a filter set. - * Both are aliased and must not be modified or deleted while this object - * is used. - * The filter set should be frozen; otherwise the performance will suffer greatly. - * @param norm2 wrapped UNormalizer2 instance - * @param filterSet USet which determines the characters to be normalized - * @param pErrorCode Standard 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.) - * @return the requested UNormalizer2, if successful - * @stable ICU 4.4 - */ -U_CAPI UNormalizer2 * U_EXPORT2 -unorm2_openFiltered(const UNormalizer2 *norm2, const USet *filterSet, UErrorCode *pErrorCode); - -/** - * Closes a UNormalizer2 instance from unorm2_openFiltered(). - * Do not close instances from unorm2_getInstance()! - * @param norm2 UNormalizer2 instance to be closed - * @stable ICU 4.4 - */ -U_CAPI void U_EXPORT2 -unorm2_close(UNormalizer2 *norm2); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUNormalizer2Pointer - * "Smart pointer" class, closes a UNormalizer2 via unorm2_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUNormalizer2Pointer, UNormalizer2, unorm2_close); - -U_NAMESPACE_END - -#endif - -/** - * Writes the normalized form of the source string to the destination string - * (replacing its contents) and returns the length of the destination string. - * The source and destination strings must be different buffers. - * @param norm2 UNormalizer2 instance - * @param src source string - * @param length length of the source string, or -1 if NUL-terminated - * @param dest destination string; its contents is replaced with normalized src - * @param capacity number of UChars that can be written to dest - * @param pErrorCode Standard 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.) - * @return dest - * @stable ICU 4.4 - */ -U_CAPI int32_t U_EXPORT2 -unorm2_normalize(const UNormalizer2 *norm2, - const UChar *src, int32_t length, - UChar *dest, int32_t capacity, - UErrorCode *pErrorCode); -/** - * Appends the normalized form of the second string to the first string - * (merging them at the boundary) and returns the length of the first string. - * The result is normalized if the first string was normalized. - * The first and second strings must be different buffers. - * @param norm2 UNormalizer2 instance - * @param first string, should be normalized - * @param firstLength length of the first string, or -1 if NUL-terminated - * @param firstCapacity number of UChars that can be written to first - * @param second string, will be normalized - * @param secondLength length of the source string, or -1 if NUL-terminated - * @param pErrorCode Standard 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.) - * @return first - * @stable ICU 4.4 - */ -U_CAPI int32_t U_EXPORT2 -unorm2_normalizeSecondAndAppend(const UNormalizer2 *norm2, - UChar *first, int32_t firstLength, int32_t firstCapacity, - const UChar *second, int32_t secondLength, - UErrorCode *pErrorCode); -/** - * Appends the second string to the first string - * (merging them at the boundary) and returns the length of the first string. - * The result is normalized if both the strings were normalized. - * The first and second strings must be different buffers. - * @param norm2 UNormalizer2 instance - * @param first string, should be normalized - * @param firstLength length of the first string, or -1 if NUL-terminated - * @param firstCapacity number of UChars that can be written to first - * @param second string, should be normalized - * @param secondLength length of the source string, or -1 if NUL-terminated - * @param pErrorCode Standard 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.) - * @return first - * @stable ICU 4.4 - */ -U_CAPI int32_t U_EXPORT2 -unorm2_append(const UNormalizer2 *norm2, - UChar *first, int32_t firstLength, int32_t firstCapacity, - const UChar *second, int32_t secondLength, - UErrorCode *pErrorCode); - -/** - * Gets the decomposition mapping of c. - * Roughly equivalent to normalizing the String form of c - * on a UNORM2_DECOMPOSE UNormalizer2 instance, but much faster, and except that this function - * returns a negative value and does not write a string - * if c does not have a decomposition mapping in this instance's data. - * This function is independent of the mode of the UNormalizer2. - * @param norm2 UNormalizer2 instance - * @param c code point - * @param decomposition String buffer which will be set to c's - * decomposition mapping, if there is one. - * @param capacity number of UChars that can be written to decomposition - * @param pErrorCode Standard 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.) - * @return the non-negative length of c's decomposition, if there is one; otherwise a negative value - * @stable ICU 4.6 - */ -U_CAPI int32_t U_EXPORT2 -unorm2_getDecomposition(const UNormalizer2 *norm2, - UChar32 c, UChar *decomposition, int32_t capacity, - UErrorCode *pErrorCode); - -/** - * Gets the raw decomposition mapping of c. - * - * This is similar to the unorm2_getDecomposition() function but returns the - * raw decomposition mapping as specified in UnicodeData.txt or - * (for custom data) in the mapping files processed by the gennorm2 tool. - * By contrast, unorm2_getDecomposition() returns the processed, - * recursively-decomposed version of this mapping. - * - * When used on a standard NFKC Normalizer2 instance, - * unorm2_getRawDecomposition() returns the Unicode Decomposition_Mapping (dm) property. - * - * When used on a standard NFC Normalizer2 instance, - * it returns the Decomposition_Mapping only if the Decomposition_Type (dt) is Canonical (Can); - * in this case, the result contains either one or two code points (=1..4 UChars). - * - * This function is independent of the mode of the UNormalizer2. - * @param norm2 UNormalizer2 instance - * @param c code point - * @param decomposition String buffer which will be set to c's - * raw decomposition mapping, if there is one. - * @param capacity number of UChars that can be written to decomposition - * @param pErrorCode Standard 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.) - * @return the non-negative length of c's raw decomposition, if there is one; otherwise a negative value - * @stable ICU 49 - */ -U_CAPI int32_t U_EXPORT2 -unorm2_getRawDecomposition(const UNormalizer2 *norm2, - UChar32 c, UChar *decomposition, int32_t capacity, - UErrorCode *pErrorCode); - -/** - * Performs pairwise composition of a & b and returns the composite if there is one. - * - * Returns a composite code point c only if c has a two-way mapping to a+b. - * In standard Unicode normalization, this means that - * c has a canonical decomposition to a+b - * and c does not have the Full_Composition_Exclusion property. - * - * This function is independent of the mode of the UNormalizer2. - * @param norm2 UNormalizer2 instance - * @param a A (normalization starter) code point. - * @param b Another code point. - * @return The non-negative composite code point if there is one; otherwise a negative value. - * @stable ICU 49 - */ -U_CAPI UChar32 U_EXPORT2 -unorm2_composePair(const UNormalizer2 *norm2, UChar32 a, UChar32 b); - -/** - * Gets the combining class of c. - * The default implementation returns 0 - * but all standard implementations return the Unicode Canonical_Combining_Class value. - * @param norm2 UNormalizer2 instance - * @param c code point - * @return c's combining class - * @stable ICU 49 - */ -U_CAPI uint8_t U_EXPORT2 -unorm2_getCombiningClass(const UNormalizer2 *norm2, UChar32 c); - -/** - * Tests if the string is normalized. - * Internally, in cases where the quickCheck() method would return "maybe" - * (which is only possible for the two COMPOSE modes) this method - * resolves to "yes" or "no" to provide a definitive result, - * at the cost of doing more work in those cases. - * @param norm2 UNormalizer2 instance - * @param s input string - * @param length length of the string, or -1 if NUL-terminated - * @param pErrorCode Standard 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.) - * @return true if s is normalized - * @stable ICU 4.4 - */ -U_CAPI UBool U_EXPORT2 -unorm2_isNormalized(const UNormalizer2 *norm2, - const UChar *s, int32_t length, - UErrorCode *pErrorCode); - -/** - * Tests if the string is normalized. - * For the two COMPOSE modes, the result could be "maybe" in cases that - * would take a little more work to resolve definitively. - * Use spanQuickCheckYes() and normalizeSecondAndAppend() for a faster - * combination of quick check + normalization, to avoid - * re-checking the "yes" prefix. - * @param norm2 UNormalizer2 instance - * @param s input string - * @param length length of the string, or -1 if NUL-terminated - * @param pErrorCode Standard 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.) - * @return UNormalizationCheckResult - * @stable ICU 4.4 - */ -U_CAPI UNormalizationCheckResult U_EXPORT2 -unorm2_quickCheck(const UNormalizer2 *norm2, - const UChar *s, int32_t length, - UErrorCode *pErrorCode); - -/** - * Returns the end of the normalized substring of the input string. - * In other words, with end=spanQuickCheckYes(s, ec); - * the substring UnicodeString(s, 0, end) - * will pass the quick check with a "yes" result. - * - * The returned end index is usually one or more characters before the - * "no" or "maybe" character: The end index is at a normalization boundary. - * (See the class documentation for more about normalization boundaries.) - * - * When the goal is a normalized string and most input strings are expected - * to be normalized already, then call this method, - * and if it returns a prefix shorter than the input string, - * copy that prefix and use normalizeSecondAndAppend() for the remainder. - * @param norm2 UNormalizer2 instance - * @param s input string - * @param length length of the string, or -1 if NUL-terminated - * @param pErrorCode Standard 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.) - * @return "yes" span end index - * @stable ICU 4.4 - */ -U_CAPI int32_t U_EXPORT2 -unorm2_spanQuickCheckYes(const UNormalizer2 *norm2, - const UChar *s, int32_t length, - UErrorCode *pErrorCode); - -/** - * Tests if the character always has a normalization boundary before it, - * regardless of context. - * For details see the Normalizer2 base class documentation. - * @param norm2 UNormalizer2 instance - * @param c character to test - * @return true if c has a normalization boundary before it - * @stable ICU 4.4 - */ -U_CAPI UBool U_EXPORT2 -unorm2_hasBoundaryBefore(const UNormalizer2 *norm2, UChar32 c); - -/** - * Tests if the character always has a normalization boundary after it, - * regardless of context. - * For details see the Normalizer2 base class documentation. - * @param norm2 UNormalizer2 instance - * @param c character to test - * @return true if c has a normalization boundary after it - * @stable ICU 4.4 - */ -U_CAPI UBool U_EXPORT2 -unorm2_hasBoundaryAfter(const UNormalizer2 *norm2, UChar32 c); - -/** - * Tests if the character is normalization-inert. - * For details see the Normalizer2 base class documentation. - * @param norm2 UNormalizer2 instance - * @param c character to test - * @return true if c is normalization-inert - * @stable ICU 4.4 - */ -U_CAPI UBool U_EXPORT2 -unorm2_isInert(const UNormalizer2 *norm2, UChar32 c); - -/** - * Compares two strings for canonical equivalence. - * Further options include case-insensitive comparison and - * code point order (as opposed to code unit order). - * - * Canonical equivalence between two strings is defined as their normalized - * forms (NFD or NFC) being identical. - * This function compares strings incrementally instead of normalizing - * (and optionally case-folding) both strings entirely, - * improving performance significantly. - * - * Bulk normalization is only necessary if the strings do not fulfill the FCD - * conditions. Only in this case, and only if the strings are relatively long, - * is memory allocated temporarily. - * For FCD strings and short non-FCD strings there is no memory allocation. - * - * Semantically, this is equivalent to - * strcmp[CodePointOrder](NFD(foldCase(NFD(s1))), NFD(foldCase(NFD(s2)))) - * where code point order and foldCase are all optional. - * - * UAX 21 2.5 Caseless Matching specifies that for a canonical caseless match - * the case folding must be performed first, then the normalization. - * - * @param s1 First source string. - * @param length1 Length of first source string, or -1 if NUL-terminated. - * - * @param s2 Second source string. - * @param length2 Length of second source string, or -1 if NUL-terminated. - * - * @param options A bit set of options: - * - U_FOLD_CASE_DEFAULT or 0 is used for default options: - * Case-sensitive comparison in code unit order, and the input strings - * are quick-checked for FCD. - * - * - UNORM_INPUT_IS_FCD - * Set if the caller knows that both s1 and s2 fulfill the FCD conditions. - * If not set, the function will quickCheck for FCD - * and normalize if necessary. - * - * - U_COMPARE_CODE_POINT_ORDER - * Set to choose code point order instead of code unit order - * (see u_strCompare for details). - * - * - U_COMPARE_IGNORE_CASE - * Set to compare strings case-insensitively using case folding, - * instead of case-sensitively. - * If set, then the following case folding options are used. - * - * - Options as used with case-insensitive comparisons, currently: - * - * - U_FOLD_CASE_EXCLUDE_SPECIAL_I - * (see u_strCaseCompare for details) - * - * - regular normalization options shifted left by UNORM_COMPARE_NORM_OPTIONS_SHIFT - * - * @param pErrorCode ICU error code in/out parameter. - * Must fulfill U_SUCCESS before the function call. - * @return <0 or 0 or >0 as usual for string comparisons - * - * @see unorm_normalize - * @see UNORM_FCD - * @see u_strCompare - * @see u_strCaseCompare - * - * @stable ICU 2.2 - */ -U_CAPI int32_t U_EXPORT2 -unorm_compare(const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - uint32_t options, - UErrorCode *pErrorCode); - -#endif /* !UCONFIG_NO_NORMALIZATION */ -#endif /* __UNORM2_H__ */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2009-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: unorm2.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2009dec15 +* created by: Markus W. Scherer +*/ + +#ifndef __UNORM2_H__ +#define __UNORM2_H__ + +/** + * \file + * \brief C API: New API for Unicode Normalization. + * + * Unicode normalization functionality for standard Unicode normalization or + * for using custom mapping tables. + * All instances of UNormalizer2 are unmodifiable/immutable. + * Instances returned by unorm2_getInstance() are singletons that must not be deleted by the caller. + * For more details see the Normalizer2 C++ class. + */ + +#include "unicode/utypes.h" +#include "unicode/stringoptions.h" +#include "unicode/uset.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +/** + * Constants for normalization modes. + * For details about standard Unicode normalization forms + * and about the algorithms which are also used with custom mapping tables + * see http://www.unicode.org/unicode/reports/tr15/ + * @stable ICU 4.4 + */ +typedef enum { + /** + * Decomposition followed by composition. + * Same as standard NFC when using an "nfc" instance. + * Same as standard NFKC when using an "nfkc" instance. + * For details about standard Unicode normalization forms + * see http://www.unicode.org/unicode/reports/tr15/ + * @stable ICU 4.4 + */ + UNORM2_COMPOSE, + /** + * Map, and reorder canonically. + * Same as standard NFD when using an "nfc" instance. + * Same as standard NFKD when using an "nfkc" instance. + * For details about standard Unicode normalization forms + * see http://www.unicode.org/unicode/reports/tr15/ + * @stable ICU 4.4 + */ + UNORM2_DECOMPOSE, + /** + * "Fast C or D" form. + * If a string is in this form, then further decomposition without reordering + * would yield the same form as DECOMPOSE. + * Text in "Fast C or D" form can be processed efficiently with data tables + * that are "canonically closed", that is, that provide equivalent data for + * equivalent text, without having to be fully normalized. + * Not a standard Unicode normalization form. + * Not a unique form: Different FCD strings can be canonically equivalent. + * For details see http://www.unicode.org/notes/tn5/#FCD + * @stable ICU 4.4 + */ + UNORM2_FCD, + /** + * Compose only contiguously. + * Also known as "FCC" or "Fast C Contiguous". + * The result will often but not always be in NFC. + * The result will conform to FCD which is useful for processing. + * Not a standard Unicode normalization form. + * For details see http://www.unicode.org/notes/tn5/#FCC + * @stable ICU 4.4 + */ + UNORM2_COMPOSE_CONTIGUOUS +} UNormalization2Mode; + +/** + * Result values for normalization quick check functions. + * For details see http://www.unicode.org/reports/tr15/#Detecting_Normalization_Forms + * @stable ICU 2.0 + */ +typedef enum UNormalizationCheckResult { + /** + * The input string is not in the normalization form. + * @stable ICU 2.0 + */ + UNORM_NO, + /** + * The input string is in the normalization form. + * @stable ICU 2.0 + */ + UNORM_YES, + /** + * The input string may or may not be in the normalization form. + * This value is only returned for composition forms like NFC and FCC, + * when a backward-combining character is found for which the surrounding text + * would have to be analyzed further. + * @stable ICU 2.0 + */ + UNORM_MAYBE +} UNormalizationCheckResult; + +/** + * Opaque C service object type for the new normalization API. + * @stable ICU 4.4 + */ +struct UNormalizer2; +typedef struct UNormalizer2 UNormalizer2; /**< C typedef for struct UNormalizer2. @stable ICU 4.4 */ + +#if !UCONFIG_NO_NORMALIZATION + +/** + * Returns a UNormalizer2 instance for Unicode NFC normalization. + * Same as unorm2_getInstance(NULL, "nfc", UNORM2_COMPOSE, pErrorCode). + * Returns an unmodifiable singleton instance. Do not delete it. + * @param pErrorCode Standard 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.) + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ +U_CAPI const UNormalizer2 * U_EXPORT2 +unorm2_getNFCInstance(UErrorCode *pErrorCode); + +/** + * Returns a UNormalizer2 instance for Unicode NFD normalization. + * Same as unorm2_getInstance(NULL, "nfc", UNORM2_DECOMPOSE, pErrorCode). + * Returns an unmodifiable singleton instance. Do not delete it. + * @param pErrorCode Standard 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.) + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ +U_CAPI const UNormalizer2 * U_EXPORT2 +unorm2_getNFDInstance(UErrorCode *pErrorCode); + +/** + * Returns a UNormalizer2 instance for Unicode NFKC normalization. + * Same as unorm2_getInstance(NULL, "nfkc", UNORM2_COMPOSE, pErrorCode). + * Returns an unmodifiable singleton instance. Do not delete it. + * @param pErrorCode Standard 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.) + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ +U_CAPI const UNormalizer2 * U_EXPORT2 +unorm2_getNFKCInstance(UErrorCode *pErrorCode); + +/** + * Returns a UNormalizer2 instance for Unicode NFKD normalization. + * Same as unorm2_getInstance(NULL, "nfkc", UNORM2_DECOMPOSE, pErrorCode). + * Returns an unmodifiable singleton instance. Do not delete it. + * @param pErrorCode Standard 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.) + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ +U_CAPI const UNormalizer2 * U_EXPORT2 +unorm2_getNFKDInstance(UErrorCode *pErrorCode); + +/** + * Returns a UNormalizer2 instance for Unicode NFKC_Casefold normalization. + * Same as unorm2_getInstance(NULL, "nfkc_cf", UNORM2_COMPOSE, pErrorCode). + * Returns an unmodifiable singleton instance. Do not delete it. + * @param pErrorCode Standard 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.) + * @return the requested Normalizer2, if successful + * @stable ICU 49 + */ +U_CAPI const UNormalizer2 * U_EXPORT2 +unorm2_getNFKCCasefoldInstance(UErrorCode *pErrorCode); + +/** + * Returns a UNormalizer2 instance which uses the specified data file + * (packageName/name similar to ucnv_openPackage() and ures_open()/ResourceBundle) + * and which composes or decomposes text according to the specified mode. + * Returns an unmodifiable singleton instance. Do not delete it. + * + * Use packageName=NULL for data files that are part of ICU's own data. + * Use name="nfc" and UNORM2_COMPOSE/UNORM2_DECOMPOSE for Unicode standard NFC/NFD. + * Use name="nfkc" and UNORM2_COMPOSE/UNORM2_DECOMPOSE for Unicode standard NFKC/NFKD. + * Use name="nfkc_cf" and UNORM2_COMPOSE for Unicode standard NFKC_CF=NFKC_Casefold. + * + * @param packageName NULL for ICU built-in data, otherwise application data package name + * @param name "nfc" or "nfkc" or "nfkc_cf" or name of custom data file + * @param mode normalization mode (compose or decompose etc.) + * @param pErrorCode Standard 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.) + * @return the requested UNormalizer2, if successful + * @stable ICU 4.4 + */ +U_CAPI const UNormalizer2 * U_EXPORT2 +unorm2_getInstance(const char *packageName, + const char *name, + UNormalization2Mode mode, + UErrorCode *pErrorCode); + +/** + * Constructs a filtered normalizer wrapping any UNormalizer2 instance + * and a filter set. + * Both are aliased and must not be modified or deleted while this object + * is used. + * The filter set should be frozen; otherwise the performance will suffer greatly. + * @param norm2 wrapped UNormalizer2 instance + * @param filterSet USet which determines the characters to be normalized + * @param pErrorCode Standard 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.) + * @return the requested UNormalizer2, if successful + * @stable ICU 4.4 + */ +U_CAPI UNormalizer2 * U_EXPORT2 +unorm2_openFiltered(const UNormalizer2 *norm2, const USet *filterSet, UErrorCode *pErrorCode); + +/** + * Closes a UNormalizer2 instance from unorm2_openFiltered(). + * Do not close instances from unorm2_getInstance()! + * @param norm2 UNormalizer2 instance to be closed + * @stable ICU 4.4 + */ +U_CAPI void U_EXPORT2 +unorm2_close(UNormalizer2 *norm2); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUNormalizer2Pointer + * "Smart pointer" class, closes a UNormalizer2 via unorm2_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUNormalizer2Pointer, UNormalizer2, unorm2_close); + +U_NAMESPACE_END + +#endif + +/** + * Writes the normalized form of the source string to the destination string + * (replacing its contents) and returns the length of the destination string. + * The source and destination strings must be different buffers. + * @param norm2 UNormalizer2 instance + * @param src source string + * @param length length of the source string, or -1 if NUL-terminated + * @param dest destination string; its contents is replaced with normalized src + * @param capacity number of UChars that can be written to dest + * @param pErrorCode Standard 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.) + * @return dest + * @stable ICU 4.4 + */ +U_CAPI int32_t U_EXPORT2 +unorm2_normalize(const UNormalizer2 *norm2, + const UChar *src, int32_t length, + UChar *dest, int32_t capacity, + UErrorCode *pErrorCode); +/** + * Appends the normalized form of the second string to the first string + * (merging them at the boundary) and returns the length of the first string. + * The result is normalized if the first string was normalized. + * The first and second strings must be different buffers. + * @param norm2 UNormalizer2 instance + * @param first string, should be normalized + * @param firstLength length of the first string, or -1 if NUL-terminated + * @param firstCapacity number of UChars that can be written to first + * @param second string, will be normalized + * @param secondLength length of the source string, or -1 if NUL-terminated + * @param pErrorCode Standard 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.) + * @return first + * @stable ICU 4.4 + */ +U_CAPI int32_t U_EXPORT2 +unorm2_normalizeSecondAndAppend(const UNormalizer2 *norm2, + UChar *first, int32_t firstLength, int32_t firstCapacity, + const UChar *second, int32_t secondLength, + UErrorCode *pErrorCode); +/** + * Appends the second string to the first string + * (merging them at the boundary) and returns the length of the first string. + * The result is normalized if both the strings were normalized. + * The first and second strings must be different buffers. + * @param norm2 UNormalizer2 instance + * @param first string, should be normalized + * @param firstLength length of the first string, or -1 if NUL-terminated + * @param firstCapacity number of UChars that can be written to first + * @param second string, should be normalized + * @param secondLength length of the source string, or -1 if NUL-terminated + * @param pErrorCode Standard 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.) + * @return first + * @stable ICU 4.4 + */ +U_CAPI int32_t U_EXPORT2 +unorm2_append(const UNormalizer2 *norm2, + UChar *first, int32_t firstLength, int32_t firstCapacity, + const UChar *second, int32_t secondLength, + UErrorCode *pErrorCode); + +/** + * Gets the decomposition mapping of c. + * Roughly equivalent to normalizing the String form of c + * on a UNORM2_DECOMPOSE UNormalizer2 instance, but much faster, and except that this function + * returns a negative value and does not write a string + * if c does not have a decomposition mapping in this instance's data. + * This function is independent of the mode of the UNormalizer2. + * @param norm2 UNormalizer2 instance + * @param c code point + * @param decomposition String buffer which will be set to c's + * decomposition mapping, if there is one. + * @param capacity number of UChars that can be written to decomposition + * @param pErrorCode Standard 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.) + * @return the non-negative length of c's decomposition, if there is one; otherwise a negative value + * @stable ICU 4.6 + */ +U_CAPI int32_t U_EXPORT2 +unorm2_getDecomposition(const UNormalizer2 *norm2, + UChar32 c, UChar *decomposition, int32_t capacity, + UErrorCode *pErrorCode); + +/** + * Gets the raw decomposition mapping of c. + * + * This is similar to the unorm2_getDecomposition() function but returns the + * raw decomposition mapping as specified in UnicodeData.txt or + * (for custom data) in the mapping files processed by the gennorm2 tool. + * By contrast, unorm2_getDecomposition() returns the processed, + * recursively-decomposed version of this mapping. + * + * When used on a standard NFKC Normalizer2 instance, + * unorm2_getRawDecomposition() returns the Unicode Decomposition_Mapping (dm) property. + * + * When used on a standard NFC Normalizer2 instance, + * it returns the Decomposition_Mapping only if the Decomposition_Type (dt) is Canonical (Can); + * in this case, the result contains either one or two code points (=1..4 UChars). + * + * This function is independent of the mode of the UNormalizer2. + * @param norm2 UNormalizer2 instance + * @param c code point + * @param decomposition String buffer which will be set to c's + * raw decomposition mapping, if there is one. + * @param capacity number of UChars that can be written to decomposition + * @param pErrorCode Standard 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.) + * @return the non-negative length of c's raw decomposition, if there is one; otherwise a negative value + * @stable ICU 49 + */ +U_CAPI int32_t U_EXPORT2 +unorm2_getRawDecomposition(const UNormalizer2 *norm2, + UChar32 c, UChar *decomposition, int32_t capacity, + UErrorCode *pErrorCode); + +/** + * Performs pairwise composition of a & b and returns the composite if there is one. + * + * Returns a composite code point c only if c has a two-way mapping to a+b. + * In standard Unicode normalization, this means that + * c has a canonical decomposition to a+b + * and c does not have the Full_Composition_Exclusion property. + * + * This function is independent of the mode of the UNormalizer2. + * @param norm2 UNormalizer2 instance + * @param a A (normalization starter) code point. + * @param b Another code point. + * @return The non-negative composite code point if there is one; otherwise a negative value. + * @stable ICU 49 + */ +U_CAPI UChar32 U_EXPORT2 +unorm2_composePair(const UNormalizer2 *norm2, UChar32 a, UChar32 b); + +/** + * Gets the combining class of c. + * The default implementation returns 0 + * but all standard implementations return the Unicode Canonical_Combining_Class value. + * @param norm2 UNormalizer2 instance + * @param c code point + * @return c's combining class + * @stable ICU 49 + */ +U_CAPI uint8_t U_EXPORT2 +unorm2_getCombiningClass(const UNormalizer2 *norm2, UChar32 c); + +/** + * Tests if the string is normalized. + * Internally, in cases where the quickCheck() method would return "maybe" + * (which is only possible for the two COMPOSE modes) this method + * resolves to "yes" or "no" to provide a definitive result, + * at the cost of doing more work in those cases. + * @param norm2 UNormalizer2 instance + * @param s input string + * @param length length of the string, or -1 if NUL-terminated + * @param pErrorCode Standard 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.) + * @return true if s is normalized + * @stable ICU 4.4 + */ +U_CAPI UBool U_EXPORT2 +unorm2_isNormalized(const UNormalizer2 *norm2, + const UChar *s, int32_t length, + UErrorCode *pErrorCode); + +/** + * Tests if the string is normalized. + * For the two COMPOSE modes, the result could be "maybe" in cases that + * would take a little more work to resolve definitively. + * Use spanQuickCheckYes() and normalizeSecondAndAppend() for a faster + * combination of quick check + normalization, to avoid + * re-checking the "yes" prefix. + * @param norm2 UNormalizer2 instance + * @param s input string + * @param length length of the string, or -1 if NUL-terminated + * @param pErrorCode Standard 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.) + * @return UNormalizationCheckResult + * @stable ICU 4.4 + */ +U_CAPI UNormalizationCheckResult U_EXPORT2 +unorm2_quickCheck(const UNormalizer2 *norm2, + const UChar *s, int32_t length, + UErrorCode *pErrorCode); + +/** + * Returns the end of the normalized substring of the input string. + * In other words, with end=spanQuickCheckYes(s, ec); + * the substring UnicodeString(s, 0, end) + * will pass the quick check with a "yes" result. + * + * The returned end index is usually one or more characters before the + * "no" or "maybe" character: The end index is at a normalization boundary. + * (See the class documentation for more about normalization boundaries.) + * + * When the goal is a normalized string and most input strings are expected + * to be normalized already, then call this method, + * and if it returns a prefix shorter than the input string, + * copy that prefix and use normalizeSecondAndAppend() for the remainder. + * @param norm2 UNormalizer2 instance + * @param s input string + * @param length length of the string, or -1 if NUL-terminated + * @param pErrorCode Standard 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.) + * @return "yes" span end index + * @stable ICU 4.4 + */ +U_CAPI int32_t U_EXPORT2 +unorm2_spanQuickCheckYes(const UNormalizer2 *norm2, + const UChar *s, int32_t length, + UErrorCode *pErrorCode); + +/** + * Tests if the character always has a normalization boundary before it, + * regardless of context. + * For details see the Normalizer2 base class documentation. + * @param norm2 UNormalizer2 instance + * @param c character to test + * @return true if c has a normalization boundary before it + * @stable ICU 4.4 + */ +U_CAPI UBool U_EXPORT2 +unorm2_hasBoundaryBefore(const UNormalizer2 *norm2, UChar32 c); + +/** + * Tests if the character always has a normalization boundary after it, + * regardless of context. + * For details see the Normalizer2 base class documentation. + * @param norm2 UNormalizer2 instance + * @param c character to test + * @return true if c has a normalization boundary after it + * @stable ICU 4.4 + */ +U_CAPI UBool U_EXPORT2 +unorm2_hasBoundaryAfter(const UNormalizer2 *norm2, UChar32 c); + +/** + * Tests if the character is normalization-inert. + * For details see the Normalizer2 base class documentation. + * @param norm2 UNormalizer2 instance + * @param c character to test + * @return true if c is normalization-inert + * @stable ICU 4.4 + */ +U_CAPI UBool U_EXPORT2 +unorm2_isInert(const UNormalizer2 *norm2, UChar32 c); + +/** + * Compares two strings for canonical equivalence. + * Further options include case-insensitive comparison and + * code point order (as opposed to code unit order). + * + * Canonical equivalence between two strings is defined as their normalized + * forms (NFD or NFC) being identical. + * This function compares strings incrementally instead of normalizing + * (and optionally case-folding) both strings entirely, + * improving performance significantly. + * + * Bulk normalization is only necessary if the strings do not fulfill the FCD + * conditions. Only in this case, and only if the strings are relatively long, + * is memory allocated temporarily. + * For FCD strings and short non-FCD strings there is no memory allocation. + * + * Semantically, this is equivalent to + * strcmp[CodePointOrder](NFD(foldCase(NFD(s1))), NFD(foldCase(NFD(s2)))) + * where code point order and foldCase are all optional. + * + * UAX 21 2.5 Caseless Matching specifies that for a canonical caseless match + * the case folding must be performed first, then the normalization. + * + * @param s1 First source string. + * @param length1 Length of first source string, or -1 if NUL-terminated. + * + * @param s2 Second source string. + * @param length2 Length of second source string, or -1 if NUL-terminated. + * + * @param options A bit set of options: + * - U_FOLD_CASE_DEFAULT or 0 is used for default options: + * Case-sensitive comparison in code unit order, and the input strings + * are quick-checked for FCD. + * + * - UNORM_INPUT_IS_FCD + * Set if the caller knows that both s1 and s2 fulfill the FCD conditions. + * If not set, the function will quickCheck for FCD + * and normalize if necessary. + * + * - U_COMPARE_CODE_POINT_ORDER + * Set to choose code point order instead of code unit order + * (see u_strCompare for details). + * + * - U_COMPARE_IGNORE_CASE + * Set to compare strings case-insensitively using case folding, + * instead of case-sensitively. + * If set, then the following case folding options are used. + * + * - Options as used with case-insensitive comparisons, currently: + * + * - U_FOLD_CASE_EXCLUDE_SPECIAL_I + * (see u_strCaseCompare for details) + * + * - regular normalization options shifted left by UNORM_COMPARE_NORM_OPTIONS_SHIFT + * + * @param pErrorCode ICU error code in/out parameter. + * Must fulfill U_SUCCESS before the function call. + * @return <0 or 0 or >0 as usual for string comparisons + * + * @see unorm_normalize + * @see UNORM_FCD + * @see u_strCompare + * @see u_strCaseCompare + * + * @stable ICU 2.2 + */ +U_CAPI int32_t U_EXPORT2 +unorm_compare(const UChar *s1, int32_t length1, + const UChar *s2, int32_t length2, + uint32_t options, + UErrorCode *pErrorCode); + +#endif /* !UCONFIG_NO_NORMALIZATION */ +#endif /* __UNORM2_H__ */ diff --git a/deps/icu-small/source/common/unicode/uobject.h b/deps/icu-small/source/common/unicode/uobject.h index 25a8330f9ac9d5..4620e58c905093 100644 --- a/deps/icu-small/source/common/unicode/uobject.h +++ b/deps/icu-small/source/common/unicode/uobject.h @@ -1,324 +1,324 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2002-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: uobject.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002jun26 -* created by: Markus W. Scherer -*/ - -#ifndef __UOBJECT_H__ -#define __UOBJECT_H__ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/platform.h" - -/** - * \file - * \brief C++ API: Common ICU base class UObject. - */ - -/** - * \def U_NO_THROW - * Since ICU 64, use U_NOEXCEPT instead. - * - * Previously, define this to define the throw() specification so - * certain functions do not throw any exceptions - * - * UMemory operator new methods should have the throw() specification - * appended to them, so that the compiler adds the additional NULL check - * before calling constructors. Without, if operator new returns NULL the - * constructor is still called, and if the constructor references member - * data, (which it typically does), the result is a segmentation violation. - * - * @stable ICU 4.2. Since ICU 64, Use U_NOEXCEPT instead. See ICU-20422. - */ -#ifndef U_NO_THROW -#define U_NO_THROW U_NOEXCEPT -#endif - -/*===========================================================================*/ -/* UClassID-based RTTI */ -/*===========================================================================*/ - -/** - * UClassID is used to identify classes without using the compiler's RTTI. - * This was used before C++ compilers consistently supported RTTI. - * ICU 4.6 requires compiler RTTI to be turned on. - * - * Each class hierarchy which needs - * to implement polymorphic clone() or operator==() defines two methods, - * described in detail below. UClassID values can be compared using - * operator==(). Nothing else should be done with them. - * - * \par - * In class hierarchies that implement "poor man's RTTI", - * each concrete subclass implements getDynamicClassID() in the same way: - * - * \code - * class Derived { - * public: - * virtual UClassID getDynamicClassID() const - * { return Derived::getStaticClassID(); } - * } - * \endcode - * - * Each concrete class implements getStaticClassID() as well, which allows - * clients to test for a specific type. - * - * \code - * class Derived { - * public: - * static UClassID U_EXPORT2 getStaticClassID(); - * private: - * static char fgClassID; - * } - * - * // In Derived.cpp: - * UClassID Derived::getStaticClassID() - * { return (UClassID)&Derived::fgClassID; } - * char Derived::fgClassID = 0; // Value is irrelevant - * \endcode - * @stable ICU 2.0 - */ -typedef void* UClassID; - -U_NAMESPACE_BEGIN - -/** - * UMemory is the common ICU base class. - * All other ICU C++ classes are derived from UMemory (starting with ICU 2.4). - * - * This is primarily to make it possible and simple to override the - * C++ memory management by adding new/delete operators to this base class. - * - * To override ALL ICU memory management, including that from plain C code, - * replace the allocation functions declared in cmemory.h - * - * UMemory does not contain any virtual functions. - * Common "boilerplate" functions are defined in UObject. - * - * @stable ICU 2.4 - */ -class U_COMMON_API UMemory { -public: - -/* test versions for debugging shaper heap memory problems */ -#ifdef SHAPER_MEMORY_DEBUG - static void * NewArray(int size, int count); - static void * GrowArray(void * array, int newSize ); - static void FreeArray(void * array ); -#endif - -#if U_OVERRIDE_CXX_ALLOCATION - /** - * Override for ICU4C C++ memory management. - * simple, non-class types are allocated using the macros in common/cmemory.h - * (uprv_malloc(), uprv_free(), uprv_realloc()); - * they or something else could be used here to implement C++ new/delete - * for ICU4C C++ classes - * @stable ICU 2.4 - */ - static void * U_EXPORT2 operator new(size_t size) U_NOEXCEPT; - - /** - * Override for ICU4C C++ memory management. - * See new(). - * @stable ICU 2.4 - */ - static void * U_EXPORT2 operator new[](size_t size) U_NOEXCEPT; - - /** - * Override for ICU4C C++ memory management. - * simple, non-class types are allocated using the macros in common/cmemory.h - * (uprv_malloc(), uprv_free(), uprv_realloc()); - * they or something else could be used here to implement C++ new/delete - * for ICU4C C++ classes - * @stable ICU 2.4 - */ - static void U_EXPORT2 operator delete(void *p) U_NOEXCEPT; - - /** - * Override for ICU4C C++ memory management. - * See delete(). - * @stable ICU 2.4 - */ - static void U_EXPORT2 operator delete[](void *p) U_NOEXCEPT; - -#if U_HAVE_PLACEMENT_NEW - /** - * Override for ICU4C C++ memory management for STL. - * See new(). - * @stable ICU 2.6 - */ - static inline void * U_EXPORT2 operator new(size_t, void *ptr) U_NOEXCEPT { return ptr; } - - /** - * Override for ICU4C C++ memory management for STL. - * See delete(). - * @stable ICU 2.6 - */ - static inline void U_EXPORT2 operator delete(void *, void *) U_NOEXCEPT {} -#endif /* U_HAVE_PLACEMENT_NEW */ -#if U_HAVE_DEBUG_LOCATION_NEW - /** - * This method overrides the MFC debug version of the operator new - * - * @param size The requested memory size - * @param file The file where the allocation was requested - * @param line The line where the allocation was requested - */ - static void * U_EXPORT2 operator new(size_t size, const char* file, int line) U_NOEXCEPT; - /** - * This method provides a matching delete for the MFC debug new - * - * @param p The pointer to the allocated memory - * @param file The file where the allocation was requested - * @param line The line where the allocation was requested - */ - static void U_EXPORT2 operator delete(void* p, const char* file, int line) U_NOEXCEPT; -#endif /* U_HAVE_DEBUG_LOCATION_NEW */ -#endif /* U_OVERRIDE_CXX_ALLOCATION */ - - /* - * Assignment operator not declared. The compiler will provide one - * which does nothing since this class does not contain any data members. - * API/code coverage may show the assignment operator as present and - * untested - ignore. - * Subclasses need this assignment operator if they use compiler-provided - * assignment operators of their own. An alternative to not declaring one - * here would be to declare and empty-implement a protected or public one. - UMemory &UMemory::operator=(const UMemory &); - */ -}; - -/** - * UObject is the common ICU "boilerplate" class. - * UObject inherits UMemory (starting with ICU 2.4), - * and all other public ICU C++ classes - * are derived from UObject (starting with ICU 2.2). - * - * UObject contains common virtual functions, in particular a virtual destructor. - * - * The clone() function is not available in UObject because it is not - * implemented by all ICU classes. - * Many ICU services provide a clone() function for their class trees, - * defined on the service's C++ base class - * (which itself is a subclass of UObject). - * - * @stable ICU 2.2 - */ -class U_COMMON_API UObject : public UMemory { -public: - /** - * Destructor. - * - * @stable ICU 2.2 - */ - virtual ~UObject(); - - /** - * ICU4C "poor man's RTTI", returns a UClassID for the actual ICU class. - * The base class implementation returns a dummy value. - * - * Use compiler RTTI rather than ICU's "poor man's RTTI". - * Since ICU 4.6, new ICU C++ class hierarchies do not implement "poor man's RTTI". - * - * @stable ICU 2.2 - */ - virtual UClassID getDynamicClassID() const; - -protected: - // the following functions are protected to prevent instantiation and - // direct use of UObject itself - - // default constructor - // inline UObject() {} - - // copy constructor - // inline UObject(const UObject &other) {} - -#if 0 - // TODO Sometime in the future. Implement operator==(). - // (This comment inserted in 2.2) - // some or all of the following "boilerplate" functions may be made public - // in a future ICU4C release when all subclasses implement them - - // assignment operator - // (not virtual, see "Taligent's Guide to Designing Programs" pp.73..74) - // commented out because the implementation is the same as a compiler's default - // UObject &operator=(const UObject &other) { return *this; } - - // comparison operators - virtual inline bool operator==(const UObject &other) const { return this==&other; } - inline bool operator!=(const UObject &other) const { return !operator==(other); } - - // clone() commented out from the base class: - // some compilers do not support co-variant return types - // (i.e., subclasses would have to return UObject * as well, instead of SubClass *) - // see also UObject class documentation. - // virtual UObject *clone() const; -#endif - - /* - * Assignment operator not declared. The compiler will provide one - * which does nothing since this class does not contain any data members. - * API/code coverage may show the assignment operator as present and - * untested - ignore. - * Subclasses need this assignment operator if they use compiler-provided - * assignment operators of their own. An alternative to not declaring one - * here would be to declare and empty-implement a protected or public one. - UObject &UObject::operator=(const UObject &); - */ -}; - -#ifndef U_HIDE_INTERNAL_API -/** - * This is a simple macro to add ICU RTTI to an ICU object implementation. - * This does not go into the header. This should only be used in *.cpp files. - * - * @param myClass The name of the class that needs RTTI defined. - * @internal - */ -#define UOBJECT_DEFINE_RTTI_IMPLEMENTATION(myClass) \ - UClassID U_EXPORT2 myClass::getStaticClassID() { \ - static char classID = 0; \ - return (UClassID)&classID; \ - } \ - UClassID myClass::getDynamicClassID() const \ - { return myClass::getStaticClassID(); } - - -/** - * This macro adds ICU RTTI to an ICU abstract class implementation. - * This macro should be invoked in *.cpp files. The corresponding - * header should declare getStaticClassID. - * - * @param myClass The name of the class that needs RTTI defined. - * @internal - */ -#define UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(myClass) \ - UClassID U_EXPORT2 myClass::getStaticClassID() { \ - static char classID = 0; \ - return (UClassID)&classID; \ - } - -#endif /* U_HIDE_INTERNAL_API */ - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2002-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: uobject.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002jun26 +* created by: Markus W. Scherer +*/ + +#ifndef __UOBJECT_H__ +#define __UOBJECT_H__ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/platform.h" + +/** + * \file + * \brief C++ API: Common ICU base class UObject. + */ + +/** + * \def U_NO_THROW + * Since ICU 64, use noexcept instead. + * + * Previously, define this to define the throw() specification so + * certain functions do not throw any exceptions + * + * UMemory operator new methods should have the throw() specification + * appended to them, so that the compiler adds the additional nullptr check + * before calling constructors. Without, if operator new returns nullptr the + * constructor is still called, and if the constructor references member + * data, (which it typically does), the result is a segmentation violation. + * + * @stable ICU 4.2. Since ICU 64, Use noexcept instead. See ICU-20422. + */ +#ifndef U_NO_THROW +#define U_NO_THROW noexcept +#endif + +/*===========================================================================*/ +/* UClassID-based RTTI */ +/*===========================================================================*/ + +/** + * UClassID is used to identify classes without using the compiler's RTTI. + * This was used before C++ compilers consistently supported RTTI. + * ICU 4.6 requires compiler RTTI to be turned on. + * + * Each class hierarchy which needs + * to implement polymorphic clone() or operator==() defines two methods, + * described in detail below. UClassID values can be compared using + * operator==(). Nothing else should be done with them. + * + * \par + * In class hierarchies that implement "poor man's RTTI", + * each concrete subclass implements getDynamicClassID() in the same way: + * + * \code + * class Derived { + * public: + * virtual UClassID getDynamicClassID() const + * { return Derived::getStaticClassID(); } + * } + * \endcode + * + * Each concrete class implements getStaticClassID() as well, which allows + * clients to test for a specific type. + * + * \code + * class Derived { + * public: + * static UClassID U_EXPORT2 getStaticClassID(); + * private: + * static char fgClassID; + * } + * + * // In Derived.cpp: + * UClassID Derived::getStaticClassID() + * { return (UClassID)&Derived::fgClassID; } + * char Derived::fgClassID = 0; // Value is irrelevant + * \endcode + * @stable ICU 2.0 + */ +typedef void* UClassID; + +U_NAMESPACE_BEGIN + +/** + * UMemory is the common ICU base class. + * All other ICU C++ classes are derived from UMemory (starting with ICU 2.4). + * + * This is primarily to make it possible and simple to override the + * C++ memory management by adding new/delete operators to this base class. + * + * To override ALL ICU memory management, including that from plain C code, + * replace the allocation functions declared in cmemory.h + * + * UMemory does not contain any virtual functions. + * Common "boilerplate" functions are defined in UObject. + * + * @stable ICU 2.4 + */ +class U_COMMON_API UMemory { +public: + +/* test versions for debugging shaper heap memory problems */ +#ifdef SHAPER_MEMORY_DEBUG + static void * NewArray(int size, int count); + static void * GrowArray(void * array, int newSize ); + static void FreeArray(void * array ); +#endif + +#if U_OVERRIDE_CXX_ALLOCATION + /** + * Override for ICU4C C++ memory management. + * simple, non-class types are allocated using the macros in common/cmemory.h + * (uprv_malloc(), uprv_free(), uprv_realloc()); + * they or something else could be used here to implement C++ new/delete + * for ICU4C C++ classes + * @stable ICU 2.4 + */ + static void * U_EXPORT2 operator new(size_t size) noexcept; + + /** + * Override for ICU4C C++ memory management. + * See new(). + * @stable ICU 2.4 + */ + static void * U_EXPORT2 operator new[](size_t size) noexcept; + + /** + * Override for ICU4C C++ memory management. + * simple, non-class types are allocated using the macros in common/cmemory.h + * (uprv_malloc(), uprv_free(), uprv_realloc()); + * they or something else could be used here to implement C++ new/delete + * for ICU4C C++ classes + * @stable ICU 2.4 + */ + static void U_EXPORT2 operator delete(void *p) noexcept; + + /** + * Override for ICU4C C++ memory management. + * See delete(). + * @stable ICU 2.4 + */ + static void U_EXPORT2 operator delete[](void *p) noexcept; + +#if U_HAVE_PLACEMENT_NEW + /** + * Override for ICU4C C++ memory management for STL. + * See new(). + * @stable ICU 2.6 + */ + static inline void * U_EXPORT2 operator new(size_t, void *ptr) noexcept { return ptr; } + + /** + * Override for ICU4C C++ memory management for STL. + * See delete(). + * @stable ICU 2.6 + */ + static inline void U_EXPORT2 operator delete(void *, void *) noexcept {} +#endif /* U_HAVE_PLACEMENT_NEW */ +#if U_HAVE_DEBUG_LOCATION_NEW + /** + * This method overrides the MFC debug version of the operator new + * + * @param size The requested memory size + * @param file The file where the allocation was requested + * @param line The line where the allocation was requested + */ + static void * U_EXPORT2 operator new(size_t size, const char* file, int line) noexcept; + /** + * This method provides a matching delete for the MFC debug new + * + * @param p The pointer to the allocated memory + * @param file The file where the allocation was requested + * @param line The line where the allocation was requested + */ + static void U_EXPORT2 operator delete(void* p, const char* file, int line) noexcept; +#endif /* U_HAVE_DEBUG_LOCATION_NEW */ +#endif /* U_OVERRIDE_CXX_ALLOCATION */ + + /* + * Assignment operator not declared. The compiler will provide one + * which does nothing since this class does not contain any data members. + * API/code coverage may show the assignment operator as present and + * untested - ignore. + * Subclasses need this assignment operator if they use compiler-provided + * assignment operators of their own. An alternative to not declaring one + * here would be to declare and empty-implement a protected or public one. + UMemory &UMemory::operator=(const UMemory &); + */ +}; + +/** + * UObject is the common ICU "boilerplate" class. + * UObject inherits UMemory (starting with ICU 2.4), + * and all other public ICU C++ classes + * are derived from UObject (starting with ICU 2.2). + * + * UObject contains common virtual functions, in particular a virtual destructor. + * + * The clone() function is not available in UObject because it is not + * implemented by all ICU classes. + * Many ICU services provide a clone() function for their class trees, + * defined on the service's C++ base class + * (which itself is a subclass of UObject). + * + * @stable ICU 2.2 + */ +class U_COMMON_API UObject : public UMemory { +public: + /** + * Destructor. + * + * @stable ICU 2.2 + */ + virtual ~UObject(); + + /** + * ICU4C "poor man's RTTI", returns a UClassID for the actual ICU class. + * The base class implementation returns a dummy value. + * + * Use compiler RTTI rather than ICU's "poor man's RTTI". + * Since ICU 4.6, new ICU C++ class hierarchies do not implement "poor man's RTTI". + * + * @stable ICU 2.2 + */ + virtual UClassID getDynamicClassID() const; + +protected: + // the following functions are protected to prevent instantiation and + // direct use of UObject itself + + // default constructor + // inline UObject() {} + + // copy constructor + // inline UObject(const UObject &other) {} + +#if 0 + // TODO Sometime in the future. Implement operator==(). + // (This comment inserted in 2.2) + // some or all of the following "boilerplate" functions may be made public + // in a future ICU4C release when all subclasses implement them + + // assignment operator + // (not virtual, see "Taligent's Guide to Designing Programs" pp.73..74) + // commented out because the implementation is the same as a compiler's default + // UObject &operator=(const UObject &other) { return *this; } + + // comparison operators + virtual inline bool operator==(const UObject &other) const { return this==&other; } + inline bool operator!=(const UObject &other) const { return !operator==(other); } + + // clone() commented out from the base class: + // some compilers do not support co-variant return types + // (i.e., subclasses would have to return UObject * as well, instead of SubClass *) + // see also UObject class documentation. + // virtual UObject *clone() const; +#endif + + /* + * Assignment operator not declared. The compiler will provide one + * which does nothing since this class does not contain any data members. + * API/code coverage may show the assignment operator as present and + * untested - ignore. + * Subclasses need this assignment operator if they use compiler-provided + * assignment operators of their own. An alternative to not declaring one + * here would be to declare and empty-implement a protected or public one. + UObject &UObject::operator=(const UObject &); + */ +}; + +#ifndef U_HIDE_INTERNAL_API +/** + * This is a simple macro to add ICU RTTI to an ICU object implementation. + * This does not go into the header. This should only be used in *.cpp files. + * + * @param myClass The name of the class that needs RTTI defined. + * @internal + */ +#define UOBJECT_DEFINE_RTTI_IMPLEMENTATION(myClass) \ + UClassID U_EXPORT2 myClass::getStaticClassID() { \ + static char classID = 0; \ + return (UClassID)&classID; \ + } \ + UClassID myClass::getDynamicClassID() const \ + { return myClass::getStaticClassID(); } + + +/** + * This macro adds ICU RTTI to an ICU abstract class implementation. + * This macro should be invoked in *.cpp files. The corresponding + * header should declare getStaticClassID. + * + * @param myClass The name of the class that needs RTTI defined. + * @internal + */ +#define UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(myClass) \ + UClassID U_EXPORT2 myClass::getStaticClassID() { \ + static char classID = 0; \ + return (UClassID)&classID; \ + } + +#endif /* U_HIDE_INTERNAL_API */ + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/urename.h b/deps/icu-small/source/common/unicode/urename.h index d3e23b8fa72307..fc8a910c32b5a5 100644 --- a/deps/icu-small/source/common/unicode/urename.h +++ b/deps/icu-small/source/common/unicode/urename.h @@ -1,1960 +1,1975 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2002-2016, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* -* file name: urename.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* Created by: Perl script tools/genren.pl written by Vladimir Weinstein -* -* Contains data for renaming ICU exports. -* Gets included by umachine.h -* -* THIS FILE IS MACHINE-GENERATED, DON'T PLAY WITH IT IF YOU DON'T KNOW WHAT -* YOU ARE DOING, OTHERWISE VERY BAD THINGS WILL HAPPEN! -*/ - -#ifndef URENAME_H -#define URENAME_H - -/* U_DISABLE_RENAMING can be defined in the following ways: - * - when running configure, e.g. - * runConfigureICU Linux --disable-renaming - * - by changing the default setting of U_DISABLE_RENAMING in uconfig.h - */ - -#include "unicode/uconfig.h" - -#if !U_DISABLE_RENAMING - -// Disable Renaming for Visual Studio's IntelliSense feature, so that 'Go-to-Definition' (F12) will work. -#if !(defined(_MSC_VER) && defined(__INTELLISENSE__)) - -/* We need the U_ICU_ENTRY_POINT_RENAME definition. There's a default one in unicode/uvernum.h we can use, but we will give - the platform a chance to define it first. - Normally (if utypes.h or umachine.h was included first) this will not be necessary as it will already be defined. - */ - -#ifndef U_ICU_ENTRY_POINT_RENAME -#include "unicode/umachine.h" -#endif - -/* If we still don't have U_ICU_ENTRY_POINT_RENAME use the default. */ -#ifndef U_ICU_ENTRY_POINT_RENAME -#include "unicode/uvernum.h" -#endif - -/* Error out before the following defines cause very strange and unexpected code breakage */ -#ifndef U_ICU_ENTRY_POINT_RENAME -#error U_ICU_ENTRY_POINT_RENAME is not defined - cannot continue. Consider defining U_DISABLE_RENAMING if renaming should not be used. -#endif - - -/* C exports renaming data */ - -#define CreateLSTMBreakEngine U_ICU_ENTRY_POINT_RENAME(CreateLSTMBreakEngine) -#define CreateLSTMData U_ICU_ENTRY_POINT_RENAME(CreateLSTMData) -#define CreateLSTMDataForScript U_ICU_ENTRY_POINT_RENAME(CreateLSTMDataForScript) -#define DeleteLSTMData U_ICU_ENTRY_POINT_RENAME(DeleteLSTMData) -#define LSTMDataName U_ICU_ENTRY_POINT_RENAME(LSTMDataName) -#define T_CString_int64ToString U_ICU_ENTRY_POINT_RENAME(T_CString_int64ToString) -#define T_CString_integerToString U_ICU_ENTRY_POINT_RENAME(T_CString_integerToString) -#define T_CString_stringToInteger U_ICU_ENTRY_POINT_RENAME(T_CString_stringToInteger) -#define T_CString_toLowerCase U_ICU_ENTRY_POINT_RENAME(T_CString_toLowerCase) -#define T_CString_toUpperCase U_ICU_ENTRY_POINT_RENAME(T_CString_toUpperCase) -#define UCNV_FROM_U_CALLBACK_ESCAPE U_ICU_ENTRY_POINT_RENAME(UCNV_FROM_U_CALLBACK_ESCAPE) -#define UCNV_FROM_U_CALLBACK_SKIP U_ICU_ENTRY_POINT_RENAME(UCNV_FROM_U_CALLBACK_SKIP) -#define UCNV_FROM_U_CALLBACK_STOP U_ICU_ENTRY_POINT_RENAME(UCNV_FROM_U_CALLBACK_STOP) -#define UCNV_FROM_U_CALLBACK_SUBSTITUTE U_ICU_ENTRY_POINT_RENAME(UCNV_FROM_U_CALLBACK_SUBSTITUTE) -#define UCNV_TO_U_CALLBACK_ESCAPE U_ICU_ENTRY_POINT_RENAME(UCNV_TO_U_CALLBACK_ESCAPE) -#define UCNV_TO_U_CALLBACK_SKIP U_ICU_ENTRY_POINT_RENAME(UCNV_TO_U_CALLBACK_SKIP) -#define UCNV_TO_U_CALLBACK_STOP U_ICU_ENTRY_POINT_RENAME(UCNV_TO_U_CALLBACK_STOP) -#define UCNV_TO_U_CALLBACK_SUBSTITUTE U_ICU_ENTRY_POINT_RENAME(UCNV_TO_U_CALLBACK_SUBSTITUTE) -#define UDataMemory_createNewInstance U_ICU_ENTRY_POINT_RENAME(UDataMemory_createNewInstance) -#define UDataMemory_init U_ICU_ENTRY_POINT_RENAME(UDataMemory_init) -#define UDataMemory_isLoaded U_ICU_ENTRY_POINT_RENAME(UDataMemory_isLoaded) -#define UDataMemory_normalizeDataPointer U_ICU_ENTRY_POINT_RENAME(UDataMemory_normalizeDataPointer) -#define UDataMemory_setData U_ICU_ENTRY_POINT_RENAME(UDataMemory_setData) -#define UDatamemory_assign U_ICU_ENTRY_POINT_RENAME(UDatamemory_assign) -#define _ASCIIData U_ICU_ENTRY_POINT_RENAME(_ASCIIData) -#define _Bocu1Data U_ICU_ENTRY_POINT_RENAME(_Bocu1Data) -#define _CESU8Data U_ICU_ENTRY_POINT_RENAME(_CESU8Data) -#define _CompoundTextData U_ICU_ENTRY_POINT_RENAME(_CompoundTextData) -#define _HZData U_ICU_ENTRY_POINT_RENAME(_HZData) -#define _IMAPData U_ICU_ENTRY_POINT_RENAME(_IMAPData) -#define _ISCIIData U_ICU_ENTRY_POINT_RENAME(_ISCIIData) -#define _ISO2022Data U_ICU_ENTRY_POINT_RENAME(_ISO2022Data) -#define _LMBCSData1 U_ICU_ENTRY_POINT_RENAME(_LMBCSData1) -#define _LMBCSData11 U_ICU_ENTRY_POINT_RENAME(_LMBCSData11) -#define _LMBCSData16 U_ICU_ENTRY_POINT_RENAME(_LMBCSData16) -#define _LMBCSData17 U_ICU_ENTRY_POINT_RENAME(_LMBCSData17) -#define _LMBCSData18 U_ICU_ENTRY_POINT_RENAME(_LMBCSData18) -#define _LMBCSData19 U_ICU_ENTRY_POINT_RENAME(_LMBCSData19) -#define _LMBCSData2 U_ICU_ENTRY_POINT_RENAME(_LMBCSData2) -#define _LMBCSData3 U_ICU_ENTRY_POINT_RENAME(_LMBCSData3) -#define _LMBCSData4 U_ICU_ENTRY_POINT_RENAME(_LMBCSData4) -#define _LMBCSData5 U_ICU_ENTRY_POINT_RENAME(_LMBCSData5) -#define _LMBCSData6 U_ICU_ENTRY_POINT_RENAME(_LMBCSData6) -#define _LMBCSData8 U_ICU_ENTRY_POINT_RENAME(_LMBCSData8) -#define _Latin1Data U_ICU_ENTRY_POINT_RENAME(_Latin1Data) -#define _MBCSData U_ICU_ENTRY_POINT_RENAME(_MBCSData) -#define _SCSUData U_ICU_ENTRY_POINT_RENAME(_SCSUData) -#define _UTF16BEData U_ICU_ENTRY_POINT_RENAME(_UTF16BEData) -#define _UTF16Data U_ICU_ENTRY_POINT_RENAME(_UTF16Data) -#define _UTF16LEData U_ICU_ENTRY_POINT_RENAME(_UTF16LEData) -#define _UTF16v2Data U_ICU_ENTRY_POINT_RENAME(_UTF16v2Data) -#define _UTF32BEData U_ICU_ENTRY_POINT_RENAME(_UTF32BEData) -#define _UTF32Data U_ICU_ENTRY_POINT_RENAME(_UTF32Data) -#define _UTF32LEData U_ICU_ENTRY_POINT_RENAME(_UTF32LEData) -#define _UTF7Data U_ICU_ENTRY_POINT_RENAME(_UTF7Data) -#define _UTF8Data U_ICU_ENTRY_POINT_RENAME(_UTF8Data) -#define _isUnicodeLocaleTypeSubtag U_ICU_ENTRY_POINT_RENAME(_isUnicodeLocaleTypeSubtag) -#define allowedHourFormatsCleanup U_ICU_ENTRY_POINT_RENAME(allowedHourFormatsCleanup) -#define cmemory_cleanup U_ICU_ENTRY_POINT_RENAME(cmemory_cleanup) -#define dayPeriodRulesCleanup U_ICU_ENTRY_POINT_RENAME(dayPeriodRulesCleanup) -#define deleteAllowedHourFormats U_ICU_ENTRY_POINT_RENAME(deleteAllowedHourFormats) -#define gTimeZoneFilesInitOnce U_ICU_ENTRY_POINT_RENAME(gTimeZoneFilesInitOnce) -#define initNumsysNames U_ICU_ENTRY_POINT_RENAME(initNumsysNames) -#define izrule_clone U_ICU_ENTRY_POINT_RENAME(izrule_clone) -#define izrule_close U_ICU_ENTRY_POINT_RENAME(izrule_close) -#define izrule_equals U_ICU_ENTRY_POINT_RENAME(izrule_equals) -#define izrule_getDSTSavings U_ICU_ENTRY_POINT_RENAME(izrule_getDSTSavings) -#define izrule_getDynamicClassID U_ICU_ENTRY_POINT_RENAME(izrule_getDynamicClassID) -#define izrule_getFinalStart U_ICU_ENTRY_POINT_RENAME(izrule_getFinalStart) -#define izrule_getFirstStart U_ICU_ENTRY_POINT_RENAME(izrule_getFirstStart) -#define izrule_getName U_ICU_ENTRY_POINT_RENAME(izrule_getName) -#define izrule_getNextStart U_ICU_ENTRY_POINT_RENAME(izrule_getNextStart) -#define izrule_getPreviousStart U_ICU_ENTRY_POINT_RENAME(izrule_getPreviousStart) -#define izrule_getRawOffset U_ICU_ENTRY_POINT_RENAME(izrule_getRawOffset) -#define izrule_getStaticClassID U_ICU_ENTRY_POINT_RENAME(izrule_getStaticClassID) -#define izrule_isEquivalentTo U_ICU_ENTRY_POINT_RENAME(izrule_isEquivalentTo) -#define izrule_open U_ICU_ENTRY_POINT_RENAME(izrule_open) -#define locale_getKeywordsStart U_ICU_ENTRY_POINT_RENAME(locale_getKeywordsStart) -#define locale_get_default U_ICU_ENTRY_POINT_RENAME(locale_get_default) -#define locale_set_default U_ICU_ENTRY_POINT_RENAME(locale_set_default) -#define numSysCleanup U_ICU_ENTRY_POINT_RENAME(numSysCleanup) -#define rbbi_cleanup U_ICU_ENTRY_POINT_RENAME(rbbi_cleanup) -#define pl_addFontRun U_ICU_ENTRY_POINT_RENAME(pl_addFontRun) -#define pl_addLocaleRun U_ICU_ENTRY_POINT_RENAME(pl_addLocaleRun) -#define pl_addValueRun U_ICU_ENTRY_POINT_RENAME(pl_addValueRun) -#define pl_close U_ICU_ENTRY_POINT_RENAME(pl_close) -#define pl_closeFontRuns U_ICU_ENTRY_POINT_RENAME(pl_closeFontRuns) -#define pl_closeLine U_ICU_ENTRY_POINT_RENAME(pl_closeLine) -#define pl_closeLocaleRuns U_ICU_ENTRY_POINT_RENAME(pl_closeLocaleRuns) -#define pl_closeValueRuns U_ICU_ENTRY_POINT_RENAME(pl_closeValueRuns) -#define pl_countLineRuns U_ICU_ENTRY_POINT_RENAME(pl_countLineRuns) -#define pl_create U_ICU_ENTRY_POINT_RENAME(pl_create) -#define pl_getAscent U_ICU_ENTRY_POINT_RENAME(pl_getAscent) -#define pl_getDescent U_ICU_ENTRY_POINT_RENAME(pl_getDescent) -#define pl_getFontRunCount U_ICU_ENTRY_POINT_RENAME(pl_getFontRunCount) -#define pl_getFontRunFont U_ICU_ENTRY_POINT_RENAME(pl_getFontRunFont) -#define pl_getFontRunLastLimit U_ICU_ENTRY_POINT_RENAME(pl_getFontRunLastLimit) -#define pl_getFontRunLimit U_ICU_ENTRY_POINT_RENAME(pl_getFontRunLimit) -#define pl_getLeading U_ICU_ENTRY_POINT_RENAME(pl_getLeading) -#define pl_getLineAscent U_ICU_ENTRY_POINT_RENAME(pl_getLineAscent) -#define pl_getLineDescent U_ICU_ENTRY_POINT_RENAME(pl_getLineDescent) -#define pl_getLineLeading U_ICU_ENTRY_POINT_RENAME(pl_getLineLeading) -#define pl_getLineVisualRun U_ICU_ENTRY_POINT_RENAME(pl_getLineVisualRun) -#define pl_getLineWidth U_ICU_ENTRY_POINT_RENAME(pl_getLineWidth) -#define pl_getLocaleRunCount U_ICU_ENTRY_POINT_RENAME(pl_getLocaleRunCount) -#define pl_getLocaleRunLastLimit U_ICU_ENTRY_POINT_RENAME(pl_getLocaleRunLastLimit) -#define pl_getLocaleRunLimit U_ICU_ENTRY_POINT_RENAME(pl_getLocaleRunLimit) -#define pl_getLocaleRunLocale U_ICU_ENTRY_POINT_RENAME(pl_getLocaleRunLocale) -#define pl_getParagraphLevel U_ICU_ENTRY_POINT_RENAME(pl_getParagraphLevel) -#define pl_getTextDirection U_ICU_ENTRY_POINT_RENAME(pl_getTextDirection) -#define pl_getValueRunCount U_ICU_ENTRY_POINT_RENAME(pl_getValueRunCount) -#define pl_getValueRunLastLimit U_ICU_ENTRY_POINT_RENAME(pl_getValueRunLastLimit) -#define pl_getValueRunLimit U_ICU_ENTRY_POINT_RENAME(pl_getValueRunLimit) -#define pl_getValueRunValue U_ICU_ENTRY_POINT_RENAME(pl_getValueRunValue) -#define pl_getVisualRunAscent U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunAscent) -#define pl_getVisualRunDescent U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunDescent) -#define pl_getVisualRunDirection U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunDirection) -#define pl_getVisualRunFont U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunFont) -#define pl_getVisualRunGlyphCount U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunGlyphCount) -#define pl_getVisualRunGlyphToCharMap U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunGlyphToCharMap) -#define pl_getVisualRunGlyphs U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunGlyphs) -#define pl_getVisualRunLeading U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunLeading) -#define pl_getVisualRunPositions U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunPositions) -#define pl_isComplex U_ICU_ENTRY_POINT_RENAME(pl_isComplex) -#define pl_nextLine U_ICU_ENTRY_POINT_RENAME(pl_nextLine) -#define pl_openEmptyFontRuns U_ICU_ENTRY_POINT_RENAME(pl_openEmptyFontRuns) -#define pl_openEmptyLocaleRuns U_ICU_ENTRY_POINT_RENAME(pl_openEmptyLocaleRuns) -#define pl_openEmptyValueRuns U_ICU_ENTRY_POINT_RENAME(pl_openEmptyValueRuns) -#define pl_openFontRuns U_ICU_ENTRY_POINT_RENAME(pl_openFontRuns) -#define pl_openLocaleRuns U_ICU_ENTRY_POINT_RENAME(pl_openLocaleRuns) -#define pl_openValueRuns U_ICU_ENTRY_POINT_RENAME(pl_openValueRuns) -#define pl_reflow U_ICU_ENTRY_POINT_RENAME(pl_reflow) -#define pl_resetFontRuns U_ICU_ENTRY_POINT_RENAME(pl_resetFontRuns) -#define pl_resetLocaleRuns U_ICU_ENTRY_POINT_RENAME(pl_resetLocaleRuns) -#define pl_resetValueRuns U_ICU_ENTRY_POINT_RENAME(pl_resetValueRuns) -#define res_countArrayItems U_ICU_ENTRY_POINT_RENAME(res_countArrayItems) -#define res_findResource U_ICU_ENTRY_POINT_RENAME(res_findResource) -#define res_getAlias U_ICU_ENTRY_POINT_RENAME(res_getAlias) -#define res_getArrayItem U_ICU_ENTRY_POINT_RENAME(res_getArrayItem) -#define res_getBinaryNoTrace U_ICU_ENTRY_POINT_RENAME(res_getBinaryNoTrace) -#define res_getIntVectorNoTrace U_ICU_ENTRY_POINT_RENAME(res_getIntVectorNoTrace) -#define res_getPublicType U_ICU_ENTRY_POINT_RENAME(res_getPublicType) -#define res_getResource U_ICU_ENTRY_POINT_RENAME(res_getResource) -#define res_getStringNoTrace U_ICU_ENTRY_POINT_RENAME(res_getStringNoTrace) -#define res_getTableItemByIndex U_ICU_ENTRY_POINT_RENAME(res_getTableItemByIndex) -#define res_getTableItemByKey U_ICU_ENTRY_POINT_RENAME(res_getTableItemByKey) -#define res_load U_ICU_ENTRY_POINT_RENAME(res_load) -#define res_read U_ICU_ENTRY_POINT_RENAME(res_read) -#define res_unload U_ICU_ENTRY_POINT_RENAME(res_unload) -#define u_UCharsToChars U_ICU_ENTRY_POINT_RENAME(u_UCharsToChars) -#define u_asciiToUpper U_ICU_ENTRY_POINT_RENAME(u_asciiToUpper) -#define u_austrcpy U_ICU_ENTRY_POINT_RENAME(u_austrcpy) -#define u_austrncpy U_ICU_ENTRY_POINT_RENAME(u_austrncpy) -#define u_caseInsensitivePrefixMatch U_ICU_ENTRY_POINT_RENAME(u_caseInsensitivePrefixMatch) -#define u_catclose U_ICU_ENTRY_POINT_RENAME(u_catclose) -#define u_catgets U_ICU_ENTRY_POINT_RENAME(u_catgets) -#define u_catopen U_ICU_ENTRY_POINT_RENAME(u_catopen) -#define u_charAge U_ICU_ENTRY_POINT_RENAME(u_charAge) -#define u_charDigitValue U_ICU_ENTRY_POINT_RENAME(u_charDigitValue) -#define u_charDirection U_ICU_ENTRY_POINT_RENAME(u_charDirection) -#define u_charFromName U_ICU_ENTRY_POINT_RENAME(u_charFromName) -#define u_charMirror U_ICU_ENTRY_POINT_RENAME(u_charMirror) -#define u_charName U_ICU_ENTRY_POINT_RENAME(u_charName) -#define u_charType U_ICU_ENTRY_POINT_RENAME(u_charType) -#define u_charsToUChars U_ICU_ENTRY_POINT_RENAME(u_charsToUChars) -#define u_cleanup U_ICU_ENTRY_POINT_RENAME(u_cleanup) -#define u_countChar32 U_ICU_ENTRY_POINT_RENAME(u_countChar32) -#define u_digit U_ICU_ENTRY_POINT_RENAME(u_digit) -#define u_enumCharNames U_ICU_ENTRY_POINT_RENAME(u_enumCharNames) -#define u_enumCharTypes U_ICU_ENTRY_POINT_RENAME(u_enumCharTypes) -#define u_errorName U_ICU_ENTRY_POINT_RENAME(u_errorName) -#define u_fadopt U_ICU_ENTRY_POINT_RENAME(u_fadopt) -#define u_fclose U_ICU_ENTRY_POINT_RENAME(u_fclose) -#define u_feof U_ICU_ENTRY_POINT_RENAME(u_feof) -#define u_fflush U_ICU_ENTRY_POINT_RENAME(u_fflush) -#define u_fgetConverter U_ICU_ENTRY_POINT_RENAME(u_fgetConverter) -#define u_fgetNumberFormat U_ICU_ENTRY_POINT_RENAME(u_fgetNumberFormat) -#define u_fgetc U_ICU_ENTRY_POINT_RENAME(u_fgetc) -#define u_fgetcodepage U_ICU_ENTRY_POINT_RENAME(u_fgetcodepage) -#define u_fgetcx U_ICU_ENTRY_POINT_RENAME(u_fgetcx) -#define u_fgetfile U_ICU_ENTRY_POINT_RENAME(u_fgetfile) -#define u_fgetlocale U_ICU_ENTRY_POINT_RENAME(u_fgetlocale) -#define u_fgets U_ICU_ENTRY_POINT_RENAME(u_fgets) -#define u_file_read U_ICU_ENTRY_POINT_RENAME(u_file_read) -#define u_file_write U_ICU_ENTRY_POINT_RENAME(u_file_write) -#define u_file_write_flush U_ICU_ENTRY_POINT_RENAME(u_file_write_flush) -#define u_finit U_ICU_ENTRY_POINT_RENAME(u_finit) -#define u_flushDefaultConverter U_ICU_ENTRY_POINT_RENAME(u_flushDefaultConverter) -#define u_foldCase U_ICU_ENTRY_POINT_RENAME(u_foldCase) -#define u_fopen U_ICU_ENTRY_POINT_RENAME(u_fopen) -#define u_fopen_u U_ICU_ENTRY_POINT_RENAME(u_fopen_u) -#define u_forDigit U_ICU_ENTRY_POINT_RENAME(u_forDigit) -#define u_formatMessage U_ICU_ENTRY_POINT_RENAME(u_formatMessage) -#define u_formatMessageWithError U_ICU_ENTRY_POINT_RENAME(u_formatMessageWithError) -#define u_fprintf U_ICU_ENTRY_POINT_RENAME(u_fprintf) -#define u_fprintf_u U_ICU_ENTRY_POINT_RENAME(u_fprintf_u) -#define u_fputc U_ICU_ENTRY_POINT_RENAME(u_fputc) -#define u_fputs U_ICU_ENTRY_POINT_RENAME(u_fputs) -#define u_frewind U_ICU_ENTRY_POINT_RENAME(u_frewind) -#define u_fscanf U_ICU_ENTRY_POINT_RENAME(u_fscanf) -#define u_fscanf_u U_ICU_ENTRY_POINT_RENAME(u_fscanf_u) -#define u_fsetcodepage U_ICU_ENTRY_POINT_RENAME(u_fsetcodepage) -#define u_fsetlocale U_ICU_ENTRY_POINT_RENAME(u_fsetlocale) -#define u_fsettransliterator U_ICU_ENTRY_POINT_RENAME(u_fsettransliterator) -#define u_fstropen U_ICU_ENTRY_POINT_RENAME(u_fstropen) -#define u_fungetc U_ICU_ENTRY_POINT_RENAME(u_fungetc) -#define u_getBidiPairedBracket U_ICU_ENTRY_POINT_RENAME(u_getBidiPairedBracket) -#define u_getBinaryPropertySet U_ICU_ENTRY_POINT_RENAME(u_getBinaryPropertySet) -#define u_getCombiningClass U_ICU_ENTRY_POINT_RENAME(u_getCombiningClass) -#define u_getDataDirectory U_ICU_ENTRY_POINT_RENAME(u_getDataDirectory) -#define u_getDataVersion U_ICU_ENTRY_POINT_RENAME(u_getDataVersion) -#define u_getDefaultConverter U_ICU_ENTRY_POINT_RENAME(u_getDefaultConverter) -#define u_getFC_NFKC_Closure U_ICU_ENTRY_POINT_RENAME(u_getFC_NFKC_Closure) -#define u_getISOComment U_ICU_ENTRY_POINT_RENAME(u_getISOComment) -#define u_getIntPropertyMap U_ICU_ENTRY_POINT_RENAME(u_getIntPropertyMap) -#define u_getIntPropertyMaxValue U_ICU_ENTRY_POINT_RENAME(u_getIntPropertyMaxValue) -#define u_getIntPropertyMinValue U_ICU_ENTRY_POINT_RENAME(u_getIntPropertyMinValue) -#define u_getIntPropertyValue U_ICU_ENTRY_POINT_RENAME(u_getIntPropertyValue) -#define u_getMainProperties U_ICU_ENTRY_POINT_RENAME(u_getMainProperties) -#define u_getNumericValue U_ICU_ENTRY_POINT_RENAME(u_getNumericValue) -#define u_getPropertyEnum U_ICU_ENTRY_POINT_RENAME(u_getPropertyEnum) -#define u_getPropertyName U_ICU_ENTRY_POINT_RENAME(u_getPropertyName) -#define u_getPropertyValueEnum U_ICU_ENTRY_POINT_RENAME(u_getPropertyValueEnum) -#define u_getPropertyValueName U_ICU_ENTRY_POINT_RENAME(u_getPropertyValueName) -#define u_getTimeZoneFilesDirectory U_ICU_ENTRY_POINT_RENAME(u_getTimeZoneFilesDirectory) -#define u_getUnicodeProperties U_ICU_ENTRY_POINT_RENAME(u_getUnicodeProperties) -#define u_getUnicodeVersion U_ICU_ENTRY_POINT_RENAME(u_getUnicodeVersion) -#define u_getVersion U_ICU_ENTRY_POINT_RENAME(u_getVersion) -#define u_get_stdout U_ICU_ENTRY_POINT_RENAME(u_get_stdout) -#define u_hasBinaryProperty U_ICU_ENTRY_POINT_RENAME(u_hasBinaryProperty) -#define u_init U_ICU_ENTRY_POINT_RENAME(u_init) -#define u_isIDIgnorable U_ICU_ENTRY_POINT_RENAME(u_isIDIgnorable) -#define u_isIDPart U_ICU_ENTRY_POINT_RENAME(u_isIDPart) -#define u_isIDStart U_ICU_ENTRY_POINT_RENAME(u_isIDStart) -#define u_isISOControl U_ICU_ENTRY_POINT_RENAME(u_isISOControl) -#define u_isJavaIDPart U_ICU_ENTRY_POINT_RENAME(u_isJavaIDPart) -#define u_isJavaIDStart U_ICU_ENTRY_POINT_RENAME(u_isJavaIDStart) -#define u_isJavaSpaceChar U_ICU_ENTRY_POINT_RENAME(u_isJavaSpaceChar) -#define u_isMirrored U_ICU_ENTRY_POINT_RENAME(u_isMirrored) -#define u_isUAlphabetic U_ICU_ENTRY_POINT_RENAME(u_isUAlphabetic) -#define u_isULowercase U_ICU_ENTRY_POINT_RENAME(u_isULowercase) -#define u_isUUppercase U_ICU_ENTRY_POINT_RENAME(u_isUUppercase) -#define u_isUWhiteSpace U_ICU_ENTRY_POINT_RENAME(u_isUWhiteSpace) -#define u_isWhitespace U_ICU_ENTRY_POINT_RENAME(u_isWhitespace) -#define u_isalnum U_ICU_ENTRY_POINT_RENAME(u_isalnum) -#define u_isalnumPOSIX U_ICU_ENTRY_POINT_RENAME(u_isalnumPOSIX) -#define u_isalpha U_ICU_ENTRY_POINT_RENAME(u_isalpha) -#define u_isbase U_ICU_ENTRY_POINT_RENAME(u_isbase) -#define u_isblank U_ICU_ENTRY_POINT_RENAME(u_isblank) -#define u_iscntrl U_ICU_ENTRY_POINT_RENAME(u_iscntrl) -#define u_isdefined U_ICU_ENTRY_POINT_RENAME(u_isdefined) -#define u_isdigit U_ICU_ENTRY_POINT_RENAME(u_isdigit) -#define u_isgraph U_ICU_ENTRY_POINT_RENAME(u_isgraph) -#define u_isgraphPOSIX U_ICU_ENTRY_POINT_RENAME(u_isgraphPOSIX) -#define u_islower U_ICU_ENTRY_POINT_RENAME(u_islower) -#define u_isprint U_ICU_ENTRY_POINT_RENAME(u_isprint) -#define u_isprintPOSIX U_ICU_ENTRY_POINT_RENAME(u_isprintPOSIX) -#define u_ispunct U_ICU_ENTRY_POINT_RENAME(u_ispunct) -#define u_isspace U_ICU_ENTRY_POINT_RENAME(u_isspace) -#define u_istitle U_ICU_ENTRY_POINT_RENAME(u_istitle) -#define u_isupper U_ICU_ENTRY_POINT_RENAME(u_isupper) -#define u_isxdigit U_ICU_ENTRY_POINT_RENAME(u_isxdigit) -#define u_locbund_close U_ICU_ENTRY_POINT_RENAME(u_locbund_close) -#define u_locbund_getNumberFormat U_ICU_ENTRY_POINT_RENAME(u_locbund_getNumberFormat) -#define u_locbund_init U_ICU_ENTRY_POINT_RENAME(u_locbund_init) -#define u_memcasecmp U_ICU_ENTRY_POINT_RENAME(u_memcasecmp) -#define u_memchr U_ICU_ENTRY_POINT_RENAME(u_memchr) -#define u_memchr32 U_ICU_ENTRY_POINT_RENAME(u_memchr32) -#define u_memcmp U_ICU_ENTRY_POINT_RENAME(u_memcmp) -#define u_memcmpCodePointOrder U_ICU_ENTRY_POINT_RENAME(u_memcmpCodePointOrder) -#define u_memcpy U_ICU_ENTRY_POINT_RENAME(u_memcpy) -#define u_memmove U_ICU_ENTRY_POINT_RENAME(u_memmove) -#define u_memrchr U_ICU_ENTRY_POINT_RENAME(u_memrchr) -#define u_memrchr32 U_ICU_ENTRY_POINT_RENAME(u_memrchr32) -#define u_memset U_ICU_ENTRY_POINT_RENAME(u_memset) -#define u_parseMessage U_ICU_ENTRY_POINT_RENAME(u_parseMessage) -#define u_parseMessageWithError U_ICU_ENTRY_POINT_RENAME(u_parseMessageWithError) -#define u_printf U_ICU_ENTRY_POINT_RENAME(u_printf) -#define u_printf_parse U_ICU_ENTRY_POINT_RENAME(u_printf_parse) -#define u_printf_u U_ICU_ENTRY_POINT_RENAME(u_printf_u) -#define u_releaseDefaultConverter U_ICU_ENTRY_POINT_RENAME(u_releaseDefaultConverter) -#define u_scanf_parse U_ICU_ENTRY_POINT_RENAME(u_scanf_parse) -#define u_setAtomicIncDecFunctions U_ICU_ENTRY_POINT_RENAME(u_setAtomicIncDecFunctions) -#define u_setDataDirectory U_ICU_ENTRY_POINT_RENAME(u_setDataDirectory) -#define u_setMemoryFunctions U_ICU_ENTRY_POINT_RENAME(u_setMemoryFunctions) -#define u_setMutexFunctions U_ICU_ENTRY_POINT_RENAME(u_setMutexFunctions) -#define u_setTimeZoneFilesDirectory U_ICU_ENTRY_POINT_RENAME(u_setTimeZoneFilesDirectory) -#define u_shapeArabic U_ICU_ENTRY_POINT_RENAME(u_shapeArabic) -#define u_snprintf U_ICU_ENTRY_POINT_RENAME(u_snprintf) -#define u_snprintf_u U_ICU_ENTRY_POINT_RENAME(u_snprintf_u) -#define u_sprintf U_ICU_ENTRY_POINT_RENAME(u_sprintf) -#define u_sprintf_u U_ICU_ENTRY_POINT_RENAME(u_sprintf_u) -#define u_sscanf U_ICU_ENTRY_POINT_RENAME(u_sscanf) -#define u_sscanf_u U_ICU_ENTRY_POINT_RENAME(u_sscanf_u) -#define u_strCaseCompare U_ICU_ENTRY_POINT_RENAME(u_strCaseCompare) -#define u_strCompare U_ICU_ENTRY_POINT_RENAME(u_strCompare) -#define u_strCompareIter U_ICU_ENTRY_POINT_RENAME(u_strCompareIter) -#define u_strFindFirst U_ICU_ENTRY_POINT_RENAME(u_strFindFirst) -#define u_strFindLast U_ICU_ENTRY_POINT_RENAME(u_strFindLast) -#define u_strFoldCase U_ICU_ENTRY_POINT_RENAME(u_strFoldCase) -#define u_strFromJavaModifiedUTF8WithSub U_ICU_ENTRY_POINT_RENAME(u_strFromJavaModifiedUTF8WithSub) -#define u_strFromPunycode U_ICU_ENTRY_POINT_RENAME(u_strFromPunycode) -#define u_strFromUTF32 U_ICU_ENTRY_POINT_RENAME(u_strFromUTF32) -#define u_strFromUTF32WithSub U_ICU_ENTRY_POINT_RENAME(u_strFromUTF32WithSub) -#define u_strFromUTF8 U_ICU_ENTRY_POINT_RENAME(u_strFromUTF8) -#define u_strFromUTF8Lenient U_ICU_ENTRY_POINT_RENAME(u_strFromUTF8Lenient) -#define u_strFromUTF8WithSub U_ICU_ENTRY_POINT_RENAME(u_strFromUTF8WithSub) -#define u_strFromWCS U_ICU_ENTRY_POINT_RENAME(u_strFromWCS) -#define u_strHasMoreChar32Than U_ICU_ENTRY_POINT_RENAME(u_strHasMoreChar32Than) -#define u_strToJavaModifiedUTF8 U_ICU_ENTRY_POINT_RENAME(u_strToJavaModifiedUTF8) -#define u_strToLower U_ICU_ENTRY_POINT_RENAME(u_strToLower) -#define u_strToPunycode U_ICU_ENTRY_POINT_RENAME(u_strToPunycode) -#define u_strToTitle U_ICU_ENTRY_POINT_RENAME(u_strToTitle) -#define u_strToUTF32 U_ICU_ENTRY_POINT_RENAME(u_strToUTF32) -#define u_strToUTF32WithSub U_ICU_ENTRY_POINT_RENAME(u_strToUTF32WithSub) -#define u_strToUTF8 U_ICU_ENTRY_POINT_RENAME(u_strToUTF8) -#define u_strToUTF8WithSub U_ICU_ENTRY_POINT_RENAME(u_strToUTF8WithSub) -#define u_strToUpper U_ICU_ENTRY_POINT_RENAME(u_strToUpper) -#define u_strToWCS U_ICU_ENTRY_POINT_RENAME(u_strToWCS) -#define u_strcasecmp U_ICU_ENTRY_POINT_RENAME(u_strcasecmp) -#define u_strcat U_ICU_ENTRY_POINT_RENAME(u_strcat) -#define u_strchr U_ICU_ENTRY_POINT_RENAME(u_strchr) -#define u_strchr32 U_ICU_ENTRY_POINT_RENAME(u_strchr32) -#define u_strcmp U_ICU_ENTRY_POINT_RENAME(u_strcmp) -#define u_strcmpCodePointOrder U_ICU_ENTRY_POINT_RENAME(u_strcmpCodePointOrder) -#define u_strcmpFold U_ICU_ENTRY_POINT_RENAME(u_strcmpFold) -#define u_strcpy U_ICU_ENTRY_POINT_RENAME(u_strcpy) -#define u_strcspn U_ICU_ENTRY_POINT_RENAME(u_strcspn) -#define u_stringHasBinaryProperty U_ICU_ENTRY_POINT_RENAME(u_stringHasBinaryProperty) -#define u_strlen U_ICU_ENTRY_POINT_RENAME(u_strlen) -#define u_strncasecmp U_ICU_ENTRY_POINT_RENAME(u_strncasecmp) -#define u_strncat U_ICU_ENTRY_POINT_RENAME(u_strncat) -#define u_strncmp U_ICU_ENTRY_POINT_RENAME(u_strncmp) -#define u_strncmpCodePointOrder U_ICU_ENTRY_POINT_RENAME(u_strncmpCodePointOrder) -#define u_strncpy U_ICU_ENTRY_POINT_RENAME(u_strncpy) -#define u_strpbrk U_ICU_ENTRY_POINT_RENAME(u_strpbrk) -#define u_strrchr U_ICU_ENTRY_POINT_RENAME(u_strrchr) -#define u_strrchr32 U_ICU_ENTRY_POINT_RENAME(u_strrchr32) -#define u_strrstr U_ICU_ENTRY_POINT_RENAME(u_strrstr) -#define u_strspn U_ICU_ENTRY_POINT_RENAME(u_strspn) -#define u_strstr U_ICU_ENTRY_POINT_RENAME(u_strstr) -#define u_strtok_r U_ICU_ENTRY_POINT_RENAME(u_strtok_r) -#define u_terminateChars U_ICU_ENTRY_POINT_RENAME(u_terminateChars) -#define u_terminateUChar32s U_ICU_ENTRY_POINT_RENAME(u_terminateUChar32s) -#define u_terminateUChars U_ICU_ENTRY_POINT_RENAME(u_terminateUChars) -#define u_terminateWChars U_ICU_ENTRY_POINT_RENAME(u_terminateWChars) -#define u_tolower U_ICU_ENTRY_POINT_RENAME(u_tolower) -#define u_totitle U_ICU_ENTRY_POINT_RENAME(u_totitle) -#define u_toupper U_ICU_ENTRY_POINT_RENAME(u_toupper) -#define u_uastrcpy U_ICU_ENTRY_POINT_RENAME(u_uastrcpy) -#define u_uastrncpy U_ICU_ENTRY_POINT_RENAME(u_uastrncpy) -#define u_unescape U_ICU_ENTRY_POINT_RENAME(u_unescape) -#define u_unescapeAt U_ICU_ENTRY_POINT_RENAME(u_unescapeAt) -#define u_versionFromString U_ICU_ENTRY_POINT_RENAME(u_versionFromString) -#define u_versionFromUString U_ICU_ENTRY_POINT_RENAME(u_versionFromUString) -#define u_versionToString U_ICU_ENTRY_POINT_RENAME(u_versionToString) -#define u_vformatMessage U_ICU_ENTRY_POINT_RENAME(u_vformatMessage) -#define u_vformatMessageWithError U_ICU_ENTRY_POINT_RENAME(u_vformatMessageWithError) -#define u_vfprintf U_ICU_ENTRY_POINT_RENAME(u_vfprintf) -#define u_vfprintf_u U_ICU_ENTRY_POINT_RENAME(u_vfprintf_u) -#define u_vfscanf U_ICU_ENTRY_POINT_RENAME(u_vfscanf) -#define u_vfscanf_u U_ICU_ENTRY_POINT_RENAME(u_vfscanf_u) -#define u_vparseMessage U_ICU_ENTRY_POINT_RENAME(u_vparseMessage) -#define u_vparseMessageWithError U_ICU_ENTRY_POINT_RENAME(u_vparseMessageWithError) -#define u_vsnprintf U_ICU_ENTRY_POINT_RENAME(u_vsnprintf) -#define u_vsnprintf_u U_ICU_ENTRY_POINT_RENAME(u_vsnprintf_u) -#define u_vsprintf U_ICU_ENTRY_POINT_RENAME(u_vsprintf) -#define u_vsprintf_u U_ICU_ENTRY_POINT_RENAME(u_vsprintf_u) -#define u_vsscanf U_ICU_ENTRY_POINT_RENAME(u_vsscanf) -#define u_vsscanf_u U_ICU_ENTRY_POINT_RENAME(u_vsscanf_u) -#define u_writeIdenticalLevelRun U_ICU_ENTRY_POINT_RENAME(u_writeIdenticalLevelRun) -#define ubidi_addPropertyStarts U_ICU_ENTRY_POINT_RENAME(ubidi_addPropertyStarts) -#define ubidi_close U_ICU_ENTRY_POINT_RENAME(ubidi_close) -#define ubidi_countParagraphs U_ICU_ENTRY_POINT_RENAME(ubidi_countParagraphs) -#define ubidi_countRuns U_ICU_ENTRY_POINT_RENAME(ubidi_countRuns) -#define ubidi_getBaseDirection U_ICU_ENTRY_POINT_RENAME(ubidi_getBaseDirection) -#define ubidi_getClass U_ICU_ENTRY_POINT_RENAME(ubidi_getClass) -#define ubidi_getClassCallback U_ICU_ENTRY_POINT_RENAME(ubidi_getClassCallback) -#define ubidi_getCustomizedClass U_ICU_ENTRY_POINT_RENAME(ubidi_getCustomizedClass) -#define ubidi_getDirection U_ICU_ENTRY_POINT_RENAME(ubidi_getDirection) -#define ubidi_getJoiningGroup U_ICU_ENTRY_POINT_RENAME(ubidi_getJoiningGroup) -#define ubidi_getJoiningType U_ICU_ENTRY_POINT_RENAME(ubidi_getJoiningType) -#define ubidi_getLength U_ICU_ENTRY_POINT_RENAME(ubidi_getLength) -#define ubidi_getLevelAt U_ICU_ENTRY_POINT_RENAME(ubidi_getLevelAt) -#define ubidi_getLevels U_ICU_ENTRY_POINT_RENAME(ubidi_getLevels) -#define ubidi_getLogicalIndex U_ICU_ENTRY_POINT_RENAME(ubidi_getLogicalIndex) -#define ubidi_getLogicalMap U_ICU_ENTRY_POINT_RENAME(ubidi_getLogicalMap) -#define ubidi_getLogicalRun U_ICU_ENTRY_POINT_RENAME(ubidi_getLogicalRun) -#define ubidi_getMaxValue U_ICU_ENTRY_POINT_RENAME(ubidi_getMaxValue) -#define ubidi_getMemory U_ICU_ENTRY_POINT_RENAME(ubidi_getMemory) -#define ubidi_getMirror U_ICU_ENTRY_POINT_RENAME(ubidi_getMirror) -#define ubidi_getPairedBracket U_ICU_ENTRY_POINT_RENAME(ubidi_getPairedBracket) -#define ubidi_getPairedBracketType U_ICU_ENTRY_POINT_RENAME(ubidi_getPairedBracketType) -#define ubidi_getParaLevel U_ICU_ENTRY_POINT_RENAME(ubidi_getParaLevel) -#define ubidi_getParaLevelAtIndex U_ICU_ENTRY_POINT_RENAME(ubidi_getParaLevelAtIndex) -#define ubidi_getParagraph U_ICU_ENTRY_POINT_RENAME(ubidi_getParagraph) -#define ubidi_getParagraphByIndex U_ICU_ENTRY_POINT_RENAME(ubidi_getParagraphByIndex) -#define ubidi_getProcessedLength U_ICU_ENTRY_POINT_RENAME(ubidi_getProcessedLength) -#define ubidi_getReorderingMode U_ICU_ENTRY_POINT_RENAME(ubidi_getReorderingMode) -#define ubidi_getReorderingOptions U_ICU_ENTRY_POINT_RENAME(ubidi_getReorderingOptions) -#define ubidi_getResultLength U_ICU_ENTRY_POINT_RENAME(ubidi_getResultLength) -#define ubidi_getRuns U_ICU_ENTRY_POINT_RENAME(ubidi_getRuns) -#define ubidi_getText U_ICU_ENTRY_POINT_RENAME(ubidi_getText) -#define ubidi_getVisualIndex U_ICU_ENTRY_POINT_RENAME(ubidi_getVisualIndex) -#define ubidi_getVisualMap U_ICU_ENTRY_POINT_RENAME(ubidi_getVisualMap) -#define ubidi_getVisualRun U_ICU_ENTRY_POINT_RENAME(ubidi_getVisualRun) -#define ubidi_invertMap U_ICU_ENTRY_POINT_RENAME(ubidi_invertMap) -#define ubidi_isBidiControl U_ICU_ENTRY_POINT_RENAME(ubidi_isBidiControl) -#define ubidi_isInverse U_ICU_ENTRY_POINT_RENAME(ubidi_isInverse) -#define ubidi_isJoinControl U_ICU_ENTRY_POINT_RENAME(ubidi_isJoinControl) -#define ubidi_isMirrored U_ICU_ENTRY_POINT_RENAME(ubidi_isMirrored) -#define ubidi_isOrderParagraphsLTR U_ICU_ENTRY_POINT_RENAME(ubidi_isOrderParagraphsLTR) -#define ubidi_open U_ICU_ENTRY_POINT_RENAME(ubidi_open) -#define ubidi_openSized U_ICU_ENTRY_POINT_RENAME(ubidi_openSized) -#define ubidi_orderParagraphsLTR U_ICU_ENTRY_POINT_RENAME(ubidi_orderParagraphsLTR) -#define ubidi_reorderLogical U_ICU_ENTRY_POINT_RENAME(ubidi_reorderLogical) -#define ubidi_reorderVisual U_ICU_ENTRY_POINT_RENAME(ubidi_reorderVisual) -#define ubidi_setClassCallback U_ICU_ENTRY_POINT_RENAME(ubidi_setClassCallback) -#define ubidi_setContext U_ICU_ENTRY_POINT_RENAME(ubidi_setContext) -#define ubidi_setInverse U_ICU_ENTRY_POINT_RENAME(ubidi_setInverse) -#define ubidi_setLine U_ICU_ENTRY_POINT_RENAME(ubidi_setLine) -#define ubidi_setPara U_ICU_ENTRY_POINT_RENAME(ubidi_setPara) -#define ubidi_setReorderingMode U_ICU_ENTRY_POINT_RENAME(ubidi_setReorderingMode) -#define ubidi_setReorderingOptions U_ICU_ENTRY_POINT_RENAME(ubidi_setReorderingOptions) -#define ubidi_writeReordered U_ICU_ENTRY_POINT_RENAME(ubidi_writeReordered) -#define ubidi_writeReverse U_ICU_ENTRY_POINT_RENAME(ubidi_writeReverse) -#define ubiditransform_close U_ICU_ENTRY_POINT_RENAME(ubiditransform_close) -#define ubiditransform_open U_ICU_ENTRY_POINT_RENAME(ubiditransform_open) -#define ubiditransform_transform U_ICU_ENTRY_POINT_RENAME(ubiditransform_transform) -#define ublock_getCode U_ICU_ENTRY_POINT_RENAME(ublock_getCode) -#define ubrk_clone U_ICU_ENTRY_POINT_RENAME(ubrk_clone) -#define ubrk_close U_ICU_ENTRY_POINT_RENAME(ubrk_close) -#define ubrk_countAvailable U_ICU_ENTRY_POINT_RENAME(ubrk_countAvailable) -#define ubrk_current U_ICU_ENTRY_POINT_RENAME(ubrk_current) -#define ubrk_first U_ICU_ENTRY_POINT_RENAME(ubrk_first) -#define ubrk_following U_ICU_ENTRY_POINT_RENAME(ubrk_following) -#define ubrk_getAvailable U_ICU_ENTRY_POINT_RENAME(ubrk_getAvailable) -#define ubrk_getBinaryRules U_ICU_ENTRY_POINT_RENAME(ubrk_getBinaryRules) -#define ubrk_getLocaleByType U_ICU_ENTRY_POINT_RENAME(ubrk_getLocaleByType) -#define ubrk_getRuleStatus U_ICU_ENTRY_POINT_RENAME(ubrk_getRuleStatus) -#define ubrk_getRuleStatusVec U_ICU_ENTRY_POINT_RENAME(ubrk_getRuleStatusVec) -#define ubrk_isBoundary U_ICU_ENTRY_POINT_RENAME(ubrk_isBoundary) -#define ubrk_last U_ICU_ENTRY_POINT_RENAME(ubrk_last) -#define ubrk_next U_ICU_ENTRY_POINT_RENAME(ubrk_next) -#define ubrk_open U_ICU_ENTRY_POINT_RENAME(ubrk_open) -#define ubrk_openBinaryRules U_ICU_ENTRY_POINT_RENAME(ubrk_openBinaryRules) -#define ubrk_openRules U_ICU_ENTRY_POINT_RENAME(ubrk_openRules) -#define ubrk_preceding U_ICU_ENTRY_POINT_RENAME(ubrk_preceding) -#define ubrk_previous U_ICU_ENTRY_POINT_RENAME(ubrk_previous) -#define ubrk_refreshUText U_ICU_ENTRY_POINT_RENAME(ubrk_refreshUText) -#define ubrk_safeClone U_ICU_ENTRY_POINT_RENAME(ubrk_safeClone) -#define ubrk_setText U_ICU_ENTRY_POINT_RENAME(ubrk_setText) -#define ubrk_setUText U_ICU_ENTRY_POINT_RENAME(ubrk_setUText) -#define ubrk_swap U_ICU_ENTRY_POINT_RENAME(ubrk_swap) -#define ucache_compareKeys U_ICU_ENTRY_POINT_RENAME(ucache_compareKeys) -#define ucache_deleteKey U_ICU_ENTRY_POINT_RENAME(ucache_deleteKey) -#define ucache_hashKeys U_ICU_ENTRY_POINT_RENAME(ucache_hashKeys) -#define ucal_add U_ICU_ENTRY_POINT_RENAME(ucal_add) -#define ucal_clear U_ICU_ENTRY_POINT_RENAME(ucal_clear) -#define ucal_clearField U_ICU_ENTRY_POINT_RENAME(ucal_clearField) -#define ucal_clone U_ICU_ENTRY_POINT_RENAME(ucal_clone) -#define ucal_close U_ICU_ENTRY_POINT_RENAME(ucal_close) -#define ucal_countAvailable U_ICU_ENTRY_POINT_RENAME(ucal_countAvailable) -#define ucal_equivalentTo U_ICU_ENTRY_POINT_RENAME(ucal_equivalentTo) -#define ucal_get U_ICU_ENTRY_POINT_RENAME(ucal_get) -#define ucal_getAttribute U_ICU_ENTRY_POINT_RENAME(ucal_getAttribute) -#define ucal_getAvailable U_ICU_ENTRY_POINT_RENAME(ucal_getAvailable) -#define ucal_getCanonicalTimeZoneID U_ICU_ENTRY_POINT_RENAME(ucal_getCanonicalTimeZoneID) -#define ucal_getDSTSavings U_ICU_ENTRY_POINT_RENAME(ucal_getDSTSavings) -#define ucal_getDayOfWeekType U_ICU_ENTRY_POINT_RENAME(ucal_getDayOfWeekType) -#define ucal_getDefaultTimeZone U_ICU_ENTRY_POINT_RENAME(ucal_getDefaultTimeZone) -#define ucal_getFieldDifference U_ICU_ENTRY_POINT_RENAME(ucal_getFieldDifference) -#define ucal_getGregorianChange U_ICU_ENTRY_POINT_RENAME(ucal_getGregorianChange) -#define ucal_getHostTimeZone U_ICU_ENTRY_POINT_RENAME(ucal_getHostTimeZone) -#define ucal_getKeywordValuesForLocale U_ICU_ENTRY_POINT_RENAME(ucal_getKeywordValuesForLocale) -#define ucal_getLimit U_ICU_ENTRY_POINT_RENAME(ucal_getLimit) -#define ucal_getLocaleByType U_ICU_ENTRY_POINT_RENAME(ucal_getLocaleByType) -#define ucal_getMillis U_ICU_ENTRY_POINT_RENAME(ucal_getMillis) -#define ucal_getNow U_ICU_ENTRY_POINT_RENAME(ucal_getNow) -#define ucal_getTZDataVersion U_ICU_ENTRY_POINT_RENAME(ucal_getTZDataVersion) -#define ucal_getTimeZoneDisplayName U_ICU_ENTRY_POINT_RENAME(ucal_getTimeZoneDisplayName) -#define ucal_getTimeZoneID U_ICU_ENTRY_POINT_RENAME(ucal_getTimeZoneID) -#define ucal_getTimeZoneIDForWindowsID U_ICU_ENTRY_POINT_RENAME(ucal_getTimeZoneIDForWindowsID) -#define ucal_getTimeZoneOffsetFromLocal U_ICU_ENTRY_POINT_RENAME(ucal_getTimeZoneOffsetFromLocal) -#define ucal_getTimeZoneTransitionDate U_ICU_ENTRY_POINT_RENAME(ucal_getTimeZoneTransitionDate) -#define ucal_getType U_ICU_ENTRY_POINT_RENAME(ucal_getType) -#define ucal_getWeekendTransition U_ICU_ENTRY_POINT_RENAME(ucal_getWeekendTransition) -#define ucal_getWindowsTimeZoneID U_ICU_ENTRY_POINT_RENAME(ucal_getWindowsTimeZoneID) -#define ucal_inDaylightTime U_ICU_ENTRY_POINT_RENAME(ucal_inDaylightTime) -#define ucal_isSet U_ICU_ENTRY_POINT_RENAME(ucal_isSet) -#define ucal_isWeekend U_ICU_ENTRY_POINT_RENAME(ucal_isWeekend) -#define ucal_open U_ICU_ENTRY_POINT_RENAME(ucal_open) -#define ucal_openCountryTimeZones U_ICU_ENTRY_POINT_RENAME(ucal_openCountryTimeZones) -#define ucal_openTimeZoneIDEnumeration U_ICU_ENTRY_POINT_RENAME(ucal_openTimeZoneIDEnumeration) -#define ucal_openTimeZones U_ICU_ENTRY_POINT_RENAME(ucal_openTimeZones) -#define ucal_roll U_ICU_ENTRY_POINT_RENAME(ucal_roll) -#define ucal_set U_ICU_ENTRY_POINT_RENAME(ucal_set) -#define ucal_setAttribute U_ICU_ENTRY_POINT_RENAME(ucal_setAttribute) -#define ucal_setDate U_ICU_ENTRY_POINT_RENAME(ucal_setDate) -#define ucal_setDateTime U_ICU_ENTRY_POINT_RENAME(ucal_setDateTime) -#define ucal_setDefaultTimeZone U_ICU_ENTRY_POINT_RENAME(ucal_setDefaultTimeZone) -#define ucal_setGregorianChange U_ICU_ENTRY_POINT_RENAME(ucal_setGregorianChange) -#define ucal_setMillis U_ICU_ENTRY_POINT_RENAME(ucal_setMillis) -#define ucal_setTimeZone U_ICU_ENTRY_POINT_RENAME(ucal_setTimeZone) -#define ucase_addCaseClosure U_ICU_ENTRY_POINT_RENAME(ucase_addCaseClosure) -#define ucase_addPropertyStarts U_ICU_ENTRY_POINT_RENAME(ucase_addPropertyStarts) -#define ucase_addStringCaseClosure U_ICU_ENTRY_POINT_RENAME(ucase_addStringCaseClosure) -#define ucase_fold U_ICU_ENTRY_POINT_RENAME(ucase_fold) -#define ucase_getCaseLocale U_ICU_ENTRY_POINT_RENAME(ucase_getCaseLocale) -#define ucase_getSingleton U_ICU_ENTRY_POINT_RENAME(ucase_getSingleton) -#define ucase_getTrie U_ICU_ENTRY_POINT_RENAME(ucase_getTrie) -#define ucase_getType U_ICU_ENTRY_POINT_RENAME(ucase_getType) -#define ucase_getTypeOrIgnorable U_ICU_ENTRY_POINT_RENAME(ucase_getTypeOrIgnorable) -#define ucase_hasBinaryProperty U_ICU_ENTRY_POINT_RENAME(ucase_hasBinaryProperty) -#define ucase_isCaseSensitive U_ICU_ENTRY_POINT_RENAME(ucase_isCaseSensitive) -#define ucase_isSoftDotted U_ICU_ENTRY_POINT_RENAME(ucase_isSoftDotted) -#define ucase_toFullFolding U_ICU_ENTRY_POINT_RENAME(ucase_toFullFolding) -#define ucase_toFullLower U_ICU_ENTRY_POINT_RENAME(ucase_toFullLower) -#define ucase_toFullTitle U_ICU_ENTRY_POINT_RENAME(ucase_toFullTitle) -#define ucase_toFullUpper U_ICU_ENTRY_POINT_RENAME(ucase_toFullUpper) -#define ucase_tolower U_ICU_ENTRY_POINT_RENAME(ucase_tolower) -#define ucase_totitle U_ICU_ENTRY_POINT_RENAME(ucase_totitle) -#define ucase_toupper U_ICU_ENTRY_POINT_RENAME(ucase_toupper) -#define ucasemap_close U_ICU_ENTRY_POINT_RENAME(ucasemap_close) -#define ucasemap_getBreakIterator U_ICU_ENTRY_POINT_RENAME(ucasemap_getBreakIterator) -#define ucasemap_getLocale U_ICU_ENTRY_POINT_RENAME(ucasemap_getLocale) -#define ucasemap_getOptions U_ICU_ENTRY_POINT_RENAME(ucasemap_getOptions) -#define ucasemap_internalUTF8ToTitle U_ICU_ENTRY_POINT_RENAME(ucasemap_internalUTF8ToTitle) -#define ucasemap_open U_ICU_ENTRY_POINT_RENAME(ucasemap_open) -#define ucasemap_setBreakIterator U_ICU_ENTRY_POINT_RENAME(ucasemap_setBreakIterator) -#define ucasemap_setLocale U_ICU_ENTRY_POINT_RENAME(ucasemap_setLocale) -#define ucasemap_setOptions U_ICU_ENTRY_POINT_RENAME(ucasemap_setOptions) -#define ucasemap_toTitle U_ICU_ENTRY_POINT_RENAME(ucasemap_toTitle) -#define ucasemap_utf8FoldCase U_ICU_ENTRY_POINT_RENAME(ucasemap_utf8FoldCase) -#define ucasemap_utf8ToLower U_ICU_ENTRY_POINT_RENAME(ucasemap_utf8ToLower) -#define ucasemap_utf8ToTitle U_ICU_ENTRY_POINT_RENAME(ucasemap_utf8ToTitle) -#define ucasemap_utf8ToUpper U_ICU_ENTRY_POINT_RENAME(ucasemap_utf8ToUpper) -#define ucfpos_close U_ICU_ENTRY_POINT_RENAME(ucfpos_close) -#define ucfpos_constrainCategory U_ICU_ENTRY_POINT_RENAME(ucfpos_constrainCategory) -#define ucfpos_constrainField U_ICU_ENTRY_POINT_RENAME(ucfpos_constrainField) -#define ucfpos_getCategory U_ICU_ENTRY_POINT_RENAME(ucfpos_getCategory) -#define ucfpos_getField U_ICU_ENTRY_POINT_RENAME(ucfpos_getField) -#define ucfpos_getIndexes U_ICU_ENTRY_POINT_RENAME(ucfpos_getIndexes) -#define ucfpos_getInt64IterationContext U_ICU_ENTRY_POINT_RENAME(ucfpos_getInt64IterationContext) -#define ucfpos_matchesField U_ICU_ENTRY_POINT_RENAME(ucfpos_matchesField) -#define ucfpos_open U_ICU_ENTRY_POINT_RENAME(ucfpos_open) -#define ucfpos_reset U_ICU_ENTRY_POINT_RENAME(ucfpos_reset) -#define ucfpos_setInt64IterationContext U_ICU_ENTRY_POINT_RENAME(ucfpos_setInt64IterationContext) -#define ucfpos_setState U_ICU_ENTRY_POINT_RENAME(ucfpos_setState) -#define uchar_addPropertyStarts U_ICU_ENTRY_POINT_RENAME(uchar_addPropertyStarts) -#define uchar_swapNames U_ICU_ENTRY_POINT_RENAME(uchar_swapNames) -#define ucln_cleanupOne U_ICU_ENTRY_POINT_RENAME(ucln_cleanupOne) -#define ucln_common_registerCleanup U_ICU_ENTRY_POINT_RENAME(ucln_common_registerCleanup) -#define ucln_i18n_registerCleanup U_ICU_ENTRY_POINT_RENAME(ucln_i18n_registerCleanup) -#define ucln_io_registerCleanup U_ICU_ENTRY_POINT_RENAME(ucln_io_registerCleanup) -#define ucln_lib_cleanup U_ICU_ENTRY_POINT_RENAME(ucln_lib_cleanup) -#define ucln_registerCleanup U_ICU_ENTRY_POINT_RENAME(ucln_registerCleanup) -#define ucnv_MBCSFromUChar32 U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSFromUChar32) -#define ucnv_MBCSFromUnicodeWithOffsets U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSFromUnicodeWithOffsets) -#define ucnv_MBCSGetFilteredUnicodeSetForUnicode U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSGetFilteredUnicodeSetForUnicode) -#define ucnv_MBCSGetType U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSGetType) -#define ucnv_MBCSGetUnicodeSetForUnicode U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSGetUnicodeSetForUnicode) -#define ucnv_MBCSIsLeadByte U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSIsLeadByte) -#define ucnv_MBCSSimpleGetNextUChar U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSSimpleGetNextUChar) -#define ucnv_MBCSToUnicodeWithOffsets U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSToUnicodeWithOffsets) -#define ucnv_bld_countAvailableConverters U_ICU_ENTRY_POINT_RENAME(ucnv_bld_countAvailableConverters) -#define ucnv_bld_getAvailableConverter U_ICU_ENTRY_POINT_RENAME(ucnv_bld_getAvailableConverter) -#define ucnv_canCreateConverter U_ICU_ENTRY_POINT_RENAME(ucnv_canCreateConverter) -#define ucnv_cbFromUWriteBytes U_ICU_ENTRY_POINT_RENAME(ucnv_cbFromUWriteBytes) -#define ucnv_cbFromUWriteSub U_ICU_ENTRY_POINT_RENAME(ucnv_cbFromUWriteSub) -#define ucnv_cbFromUWriteUChars U_ICU_ENTRY_POINT_RENAME(ucnv_cbFromUWriteUChars) -#define ucnv_cbToUWriteSub U_ICU_ENTRY_POINT_RENAME(ucnv_cbToUWriteSub) -#define ucnv_cbToUWriteUChars U_ICU_ENTRY_POINT_RENAME(ucnv_cbToUWriteUChars) -#define ucnv_clone U_ICU_ENTRY_POINT_RENAME(ucnv_clone) -#define ucnv_close U_ICU_ENTRY_POINT_RENAME(ucnv_close) -#define ucnv_compareNames U_ICU_ENTRY_POINT_RENAME(ucnv_compareNames) -#define ucnv_convert U_ICU_ENTRY_POINT_RENAME(ucnv_convert) -#define ucnv_convertEx U_ICU_ENTRY_POINT_RENAME(ucnv_convertEx) -#define ucnv_countAliases U_ICU_ENTRY_POINT_RENAME(ucnv_countAliases) -#define ucnv_countAvailable U_ICU_ENTRY_POINT_RENAME(ucnv_countAvailable) -#define ucnv_countStandards U_ICU_ENTRY_POINT_RENAME(ucnv_countStandards) -#define ucnv_createAlgorithmicConverter U_ICU_ENTRY_POINT_RENAME(ucnv_createAlgorithmicConverter) -#define ucnv_createConverter U_ICU_ENTRY_POINT_RENAME(ucnv_createConverter) -#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) -#define ucnv_extInitialMatchFromU U_ICU_ENTRY_POINT_RENAME(ucnv_extInitialMatchFromU) -#define ucnv_extInitialMatchToU U_ICU_ENTRY_POINT_RENAME(ucnv_extInitialMatchToU) -#define ucnv_extSimpleMatchFromU U_ICU_ENTRY_POINT_RENAME(ucnv_extSimpleMatchFromU) -#define ucnv_extSimpleMatchToU U_ICU_ENTRY_POINT_RENAME(ucnv_extSimpleMatchToU) -#define ucnv_fixFileSeparator U_ICU_ENTRY_POINT_RENAME(ucnv_fixFileSeparator) -#define ucnv_flushCache U_ICU_ENTRY_POINT_RENAME(ucnv_flushCache) -#define ucnv_fromAlgorithmic U_ICU_ENTRY_POINT_RENAME(ucnv_fromAlgorithmic) -#define ucnv_fromUChars U_ICU_ENTRY_POINT_RENAME(ucnv_fromUChars) -#define ucnv_fromUCountPending U_ICU_ENTRY_POINT_RENAME(ucnv_fromUCountPending) -#define ucnv_fromUWriteBytes U_ICU_ENTRY_POINT_RENAME(ucnv_fromUWriteBytes) -#define ucnv_fromUnicode U_ICU_ENTRY_POINT_RENAME(ucnv_fromUnicode) -#define ucnv_fromUnicode_UTF8 U_ICU_ENTRY_POINT_RENAME(ucnv_fromUnicode_UTF8) -#define ucnv_fromUnicode_UTF8_OFFSETS_LOGIC U_ICU_ENTRY_POINT_RENAME(ucnv_fromUnicode_UTF8_OFFSETS_LOGIC) -#define ucnv_getAlias U_ICU_ENTRY_POINT_RENAME(ucnv_getAlias) -#define ucnv_getAliases U_ICU_ENTRY_POINT_RENAME(ucnv_getAliases) -#define ucnv_getAvailableName U_ICU_ENTRY_POINT_RENAME(ucnv_getAvailableName) -#define ucnv_getCCSID U_ICU_ENTRY_POINT_RENAME(ucnv_getCCSID) -#define ucnv_getCanonicalName U_ICU_ENTRY_POINT_RENAME(ucnv_getCanonicalName) -#define ucnv_getCompleteUnicodeSet U_ICU_ENTRY_POINT_RENAME(ucnv_getCompleteUnicodeSet) -#define ucnv_getDefaultName U_ICU_ENTRY_POINT_RENAME(ucnv_getDefaultName) -#define ucnv_getDisplayName U_ICU_ENTRY_POINT_RENAME(ucnv_getDisplayName) -#define ucnv_getFromUCallBack U_ICU_ENTRY_POINT_RENAME(ucnv_getFromUCallBack) -#define ucnv_getInvalidChars U_ICU_ENTRY_POINT_RENAME(ucnv_getInvalidChars) -#define ucnv_getInvalidUChars U_ICU_ENTRY_POINT_RENAME(ucnv_getInvalidUChars) -#define ucnv_getMaxCharSize U_ICU_ENTRY_POINT_RENAME(ucnv_getMaxCharSize) -#define ucnv_getMinCharSize U_ICU_ENTRY_POINT_RENAME(ucnv_getMinCharSize) -#define ucnv_getName U_ICU_ENTRY_POINT_RENAME(ucnv_getName) -#define ucnv_getNextUChar U_ICU_ENTRY_POINT_RENAME(ucnv_getNextUChar) -#define ucnv_getNonSurrogateUnicodeSet U_ICU_ENTRY_POINT_RENAME(ucnv_getNonSurrogateUnicodeSet) -#define ucnv_getPlatform U_ICU_ENTRY_POINT_RENAME(ucnv_getPlatform) -#define ucnv_getStandard U_ICU_ENTRY_POINT_RENAME(ucnv_getStandard) -#define ucnv_getStandardName U_ICU_ENTRY_POINT_RENAME(ucnv_getStandardName) -#define ucnv_getStarters U_ICU_ENTRY_POINT_RENAME(ucnv_getStarters) -#define ucnv_getSubstChars U_ICU_ENTRY_POINT_RENAME(ucnv_getSubstChars) -#define ucnv_getToUCallBack U_ICU_ENTRY_POINT_RENAME(ucnv_getToUCallBack) -#define ucnv_getType U_ICU_ENTRY_POINT_RENAME(ucnv_getType) -#define ucnv_getUnicodeSet U_ICU_ENTRY_POINT_RENAME(ucnv_getUnicodeSet) -#define ucnv_incrementRefCount U_ICU_ENTRY_POINT_RENAME(ucnv_incrementRefCount) -#define ucnv_io_countKnownConverters U_ICU_ENTRY_POINT_RENAME(ucnv_io_countKnownConverters) -#define ucnv_io_getConverterName U_ICU_ENTRY_POINT_RENAME(ucnv_io_getConverterName) -#define ucnv_io_stripASCIIForCompare U_ICU_ENTRY_POINT_RENAME(ucnv_io_stripASCIIForCompare) -#define ucnv_io_stripEBCDICForCompare U_ICU_ENTRY_POINT_RENAME(ucnv_io_stripEBCDICForCompare) -#define ucnv_isAmbiguous U_ICU_ENTRY_POINT_RENAME(ucnv_isAmbiguous) -#define ucnv_isFixedWidth U_ICU_ENTRY_POINT_RENAME(ucnv_isFixedWidth) -#define ucnv_load U_ICU_ENTRY_POINT_RENAME(ucnv_load) -#define ucnv_loadSharedData U_ICU_ENTRY_POINT_RENAME(ucnv_loadSharedData) -#define ucnv_open U_ICU_ENTRY_POINT_RENAME(ucnv_open) -#define ucnv_openAllNames U_ICU_ENTRY_POINT_RENAME(ucnv_openAllNames) -#define ucnv_openCCSID U_ICU_ENTRY_POINT_RENAME(ucnv_openCCSID) -#define ucnv_openPackage U_ICU_ENTRY_POINT_RENAME(ucnv_openPackage) -#define ucnv_openStandardNames U_ICU_ENTRY_POINT_RENAME(ucnv_openStandardNames) -#define ucnv_openU U_ICU_ENTRY_POINT_RENAME(ucnv_openU) -#define ucnv_reset U_ICU_ENTRY_POINT_RENAME(ucnv_reset) -#define ucnv_resetFromUnicode U_ICU_ENTRY_POINT_RENAME(ucnv_resetFromUnicode) -#define ucnv_resetToUnicode U_ICU_ENTRY_POINT_RENAME(ucnv_resetToUnicode) -#define ucnv_safeClone U_ICU_ENTRY_POINT_RENAME(ucnv_safeClone) -#define ucnv_setDefaultName U_ICU_ENTRY_POINT_RENAME(ucnv_setDefaultName) -#define ucnv_setFallback U_ICU_ENTRY_POINT_RENAME(ucnv_setFallback) -#define ucnv_setFromUCallBack U_ICU_ENTRY_POINT_RENAME(ucnv_setFromUCallBack) -#define ucnv_setSubstChars U_ICU_ENTRY_POINT_RENAME(ucnv_setSubstChars) -#define ucnv_setSubstString U_ICU_ENTRY_POINT_RENAME(ucnv_setSubstString) -#define ucnv_setToUCallBack U_ICU_ENTRY_POINT_RENAME(ucnv_setToUCallBack) -#define ucnv_swap U_ICU_ENTRY_POINT_RENAME(ucnv_swap) -#define ucnv_swapAliases U_ICU_ENTRY_POINT_RENAME(ucnv_swapAliases) -#define ucnv_toAlgorithmic U_ICU_ENTRY_POINT_RENAME(ucnv_toAlgorithmic) -#define ucnv_toUChars U_ICU_ENTRY_POINT_RENAME(ucnv_toUChars) -#define ucnv_toUCountPending U_ICU_ENTRY_POINT_RENAME(ucnv_toUCountPending) -#define ucnv_toUWriteCodePoint U_ICU_ENTRY_POINT_RENAME(ucnv_toUWriteCodePoint) -#define ucnv_toUWriteUChars U_ICU_ENTRY_POINT_RENAME(ucnv_toUWriteUChars) -#define ucnv_toUnicode U_ICU_ENTRY_POINT_RENAME(ucnv_toUnicode) -#define ucnv_unload U_ICU_ENTRY_POINT_RENAME(ucnv_unload) -#define ucnv_unloadSharedDataIfReady U_ICU_ENTRY_POINT_RENAME(ucnv_unloadSharedDataIfReady) -#define ucnv_usesFallback U_ICU_ENTRY_POINT_RENAME(ucnv_usesFallback) -#define ucnvsel_close U_ICU_ENTRY_POINT_RENAME(ucnvsel_close) -#define ucnvsel_open U_ICU_ENTRY_POINT_RENAME(ucnvsel_open) -#define ucnvsel_openFromSerialized U_ICU_ENTRY_POINT_RENAME(ucnvsel_openFromSerialized) -#define ucnvsel_selectForString U_ICU_ENTRY_POINT_RENAME(ucnvsel_selectForString) -#define ucnvsel_selectForUTF8 U_ICU_ENTRY_POINT_RENAME(ucnvsel_selectForUTF8) -#define ucnvsel_serialize U_ICU_ENTRY_POINT_RENAME(ucnvsel_serialize) -#define ucol_clone U_ICU_ENTRY_POINT_RENAME(ucol_clone) -#define ucol_cloneBinary U_ICU_ENTRY_POINT_RENAME(ucol_cloneBinary) -#define ucol_close U_ICU_ENTRY_POINT_RENAME(ucol_close) -#define ucol_closeElements U_ICU_ENTRY_POINT_RENAME(ucol_closeElements) -#define ucol_countAvailable U_ICU_ENTRY_POINT_RENAME(ucol_countAvailable) -#define ucol_equal U_ICU_ENTRY_POINT_RENAME(ucol_equal) -#define ucol_equals U_ICU_ENTRY_POINT_RENAME(ucol_equals) -#define ucol_getAttribute U_ICU_ENTRY_POINT_RENAME(ucol_getAttribute) -#define ucol_getAvailable U_ICU_ENTRY_POINT_RENAME(ucol_getAvailable) -#define ucol_getBound U_ICU_ENTRY_POINT_RENAME(ucol_getBound) -#define ucol_getContractions U_ICU_ENTRY_POINT_RENAME(ucol_getContractions) -#define ucol_getContractionsAndExpansions U_ICU_ENTRY_POINT_RENAME(ucol_getContractionsAndExpansions) -#define ucol_getDisplayName U_ICU_ENTRY_POINT_RENAME(ucol_getDisplayName) -#define ucol_getEquivalentReorderCodes U_ICU_ENTRY_POINT_RENAME(ucol_getEquivalentReorderCodes) -#define ucol_getFunctionalEquivalent U_ICU_ENTRY_POINT_RENAME(ucol_getFunctionalEquivalent) -#define ucol_getKeywordValues U_ICU_ENTRY_POINT_RENAME(ucol_getKeywordValues) -#define ucol_getKeywordValuesForLocale U_ICU_ENTRY_POINT_RENAME(ucol_getKeywordValuesForLocale) -#define ucol_getKeywords U_ICU_ENTRY_POINT_RENAME(ucol_getKeywords) -#define ucol_getLocale U_ICU_ENTRY_POINT_RENAME(ucol_getLocale) -#define ucol_getLocaleByType U_ICU_ENTRY_POINT_RENAME(ucol_getLocaleByType) -#define ucol_getMaxExpansion U_ICU_ENTRY_POINT_RENAME(ucol_getMaxExpansion) -#define ucol_getMaxVariable U_ICU_ENTRY_POINT_RENAME(ucol_getMaxVariable) -#define ucol_getOffset U_ICU_ENTRY_POINT_RENAME(ucol_getOffset) -#define ucol_getReorderCodes U_ICU_ENTRY_POINT_RENAME(ucol_getReorderCodes) -#define ucol_getRules U_ICU_ENTRY_POINT_RENAME(ucol_getRules) -#define ucol_getRulesEx U_ICU_ENTRY_POINT_RENAME(ucol_getRulesEx) -#define ucol_getShortDefinitionString U_ICU_ENTRY_POINT_RENAME(ucol_getShortDefinitionString) -#define ucol_getSortKey U_ICU_ENTRY_POINT_RENAME(ucol_getSortKey) -#define ucol_getStrength U_ICU_ENTRY_POINT_RENAME(ucol_getStrength) -#define ucol_getTailoredSet U_ICU_ENTRY_POINT_RENAME(ucol_getTailoredSet) -#define ucol_getUCAVersion U_ICU_ENTRY_POINT_RENAME(ucol_getUCAVersion) -#define ucol_getUnsafeSet U_ICU_ENTRY_POINT_RENAME(ucol_getUnsafeSet) -#define ucol_getVariableTop U_ICU_ENTRY_POINT_RENAME(ucol_getVariableTop) -#define ucol_getVersion U_ICU_ENTRY_POINT_RENAME(ucol_getVersion) -#define ucol_greater U_ICU_ENTRY_POINT_RENAME(ucol_greater) -#define ucol_greaterOrEqual U_ICU_ENTRY_POINT_RENAME(ucol_greaterOrEqual) -#define ucol_keyHashCode U_ICU_ENTRY_POINT_RENAME(ucol_keyHashCode) -#define ucol_looksLikeCollationBinary U_ICU_ENTRY_POINT_RENAME(ucol_looksLikeCollationBinary) -#define ucol_mergeSortkeys U_ICU_ENTRY_POINT_RENAME(ucol_mergeSortkeys) -#define ucol_next U_ICU_ENTRY_POINT_RENAME(ucol_next) -#define ucol_nextSortKeyPart U_ICU_ENTRY_POINT_RENAME(ucol_nextSortKeyPart) -#define ucol_normalizeShortDefinitionString U_ICU_ENTRY_POINT_RENAME(ucol_normalizeShortDefinitionString) -#define ucol_open U_ICU_ENTRY_POINT_RENAME(ucol_open) -#define ucol_openAvailableLocales U_ICU_ENTRY_POINT_RENAME(ucol_openAvailableLocales) -#define ucol_openBinary U_ICU_ENTRY_POINT_RENAME(ucol_openBinary) -#define ucol_openElements U_ICU_ENTRY_POINT_RENAME(ucol_openElements) -#define ucol_openFromShortString U_ICU_ENTRY_POINT_RENAME(ucol_openFromShortString) -#define ucol_openRules U_ICU_ENTRY_POINT_RENAME(ucol_openRules) -#define ucol_prepareShortStringOpen U_ICU_ENTRY_POINT_RENAME(ucol_prepareShortStringOpen) -#define ucol_previous U_ICU_ENTRY_POINT_RENAME(ucol_previous) -#define ucol_primaryOrder U_ICU_ENTRY_POINT_RENAME(ucol_primaryOrder) -#define ucol_reset U_ICU_ENTRY_POINT_RENAME(ucol_reset) -#define ucol_restoreVariableTop U_ICU_ENTRY_POINT_RENAME(ucol_restoreVariableTop) -#define ucol_safeClone U_ICU_ENTRY_POINT_RENAME(ucol_safeClone) -#define ucol_secondaryOrder U_ICU_ENTRY_POINT_RENAME(ucol_secondaryOrder) -#define ucol_setAttribute U_ICU_ENTRY_POINT_RENAME(ucol_setAttribute) -#define ucol_setMaxVariable U_ICU_ENTRY_POINT_RENAME(ucol_setMaxVariable) -#define ucol_setOffset U_ICU_ENTRY_POINT_RENAME(ucol_setOffset) -#define ucol_setReorderCodes U_ICU_ENTRY_POINT_RENAME(ucol_setReorderCodes) -#define ucol_setStrength U_ICU_ENTRY_POINT_RENAME(ucol_setStrength) -#define ucol_setText U_ICU_ENTRY_POINT_RENAME(ucol_setText) -#define ucol_setVariableTop U_ICU_ENTRY_POINT_RENAME(ucol_setVariableTop) -#define ucol_strcoll U_ICU_ENTRY_POINT_RENAME(ucol_strcoll) -#define ucol_strcollIter U_ICU_ENTRY_POINT_RENAME(ucol_strcollIter) -#define ucol_strcollUTF8 U_ICU_ENTRY_POINT_RENAME(ucol_strcollUTF8) -#define ucol_swap U_ICU_ENTRY_POINT_RENAME(ucol_swap) -#define ucol_swapInverseUCA U_ICU_ENTRY_POINT_RENAME(ucol_swapInverseUCA) -#define ucol_tertiaryOrder U_ICU_ENTRY_POINT_RENAME(ucol_tertiaryOrder) -#define ucpmap_get U_ICU_ENTRY_POINT_RENAME(ucpmap_get) -#define ucpmap_getRange U_ICU_ENTRY_POINT_RENAME(ucpmap_getRange) -#define ucptrie_close U_ICU_ENTRY_POINT_RENAME(ucptrie_close) -#define ucptrie_get U_ICU_ENTRY_POINT_RENAME(ucptrie_get) -#define ucptrie_getRange U_ICU_ENTRY_POINT_RENAME(ucptrie_getRange) -#define ucptrie_getType U_ICU_ENTRY_POINT_RENAME(ucptrie_getType) -#define ucptrie_getValueWidth U_ICU_ENTRY_POINT_RENAME(ucptrie_getValueWidth) -#define ucptrie_internalGetRange U_ICU_ENTRY_POINT_RENAME(ucptrie_internalGetRange) -#define ucptrie_internalSmallIndex U_ICU_ENTRY_POINT_RENAME(ucptrie_internalSmallIndex) -#define ucptrie_internalSmallU8Index U_ICU_ENTRY_POINT_RENAME(ucptrie_internalSmallU8Index) -#define ucptrie_internalU8PrevIndex U_ICU_ENTRY_POINT_RENAME(ucptrie_internalU8PrevIndex) -#define ucptrie_openFromBinary U_ICU_ENTRY_POINT_RENAME(ucptrie_openFromBinary) -#define ucptrie_swap U_ICU_ENTRY_POINT_RENAME(ucptrie_swap) -#define ucptrie_toBinary U_ICU_ENTRY_POINT_RENAME(ucptrie_toBinary) -#define ucsdet_close U_ICU_ENTRY_POINT_RENAME(ucsdet_close) -#define ucsdet_detect U_ICU_ENTRY_POINT_RENAME(ucsdet_detect) -#define ucsdet_detectAll U_ICU_ENTRY_POINT_RENAME(ucsdet_detectAll) -#define ucsdet_enableInputFilter U_ICU_ENTRY_POINT_RENAME(ucsdet_enableInputFilter) -#define ucsdet_getAllDetectableCharsets U_ICU_ENTRY_POINT_RENAME(ucsdet_getAllDetectableCharsets) -#define ucsdet_getConfidence U_ICU_ENTRY_POINT_RENAME(ucsdet_getConfidence) -#define ucsdet_getDetectableCharsets U_ICU_ENTRY_POINT_RENAME(ucsdet_getDetectableCharsets) -#define ucsdet_getLanguage U_ICU_ENTRY_POINT_RENAME(ucsdet_getLanguage) -#define ucsdet_getName U_ICU_ENTRY_POINT_RENAME(ucsdet_getName) -#define ucsdet_getUChars U_ICU_ENTRY_POINT_RENAME(ucsdet_getUChars) -#define ucsdet_isInputFilterEnabled U_ICU_ENTRY_POINT_RENAME(ucsdet_isInputFilterEnabled) -#define ucsdet_open U_ICU_ENTRY_POINT_RENAME(ucsdet_open) -#define ucsdet_setDeclaredEncoding U_ICU_ENTRY_POINT_RENAME(ucsdet_setDeclaredEncoding) -#define ucsdet_setDetectableCharset U_ICU_ENTRY_POINT_RENAME(ucsdet_setDetectableCharset) -#define ucsdet_setText U_ICU_ENTRY_POINT_RENAME(ucsdet_setText) -#define ucurr_countCurrencies U_ICU_ENTRY_POINT_RENAME(ucurr_countCurrencies) -#define ucurr_forLocale U_ICU_ENTRY_POINT_RENAME(ucurr_forLocale) -#define ucurr_forLocaleAndDate U_ICU_ENTRY_POINT_RENAME(ucurr_forLocaleAndDate) -#define ucurr_getDefaultFractionDigits U_ICU_ENTRY_POINT_RENAME(ucurr_getDefaultFractionDigits) -#define ucurr_getDefaultFractionDigitsForUsage U_ICU_ENTRY_POINT_RENAME(ucurr_getDefaultFractionDigitsForUsage) -#define ucurr_getKeywordValuesForLocale U_ICU_ENTRY_POINT_RENAME(ucurr_getKeywordValuesForLocale) -#define ucurr_getName U_ICU_ENTRY_POINT_RENAME(ucurr_getName) -#define ucurr_getNumericCode U_ICU_ENTRY_POINT_RENAME(ucurr_getNumericCode) -#define ucurr_getPluralName U_ICU_ENTRY_POINT_RENAME(ucurr_getPluralName) -#define ucurr_getRoundingIncrement U_ICU_ENTRY_POINT_RENAME(ucurr_getRoundingIncrement) -#define ucurr_getRoundingIncrementForUsage U_ICU_ENTRY_POINT_RENAME(ucurr_getRoundingIncrementForUsage) -#define ucurr_isAvailable U_ICU_ENTRY_POINT_RENAME(ucurr_isAvailable) -#define ucurr_openISOCurrencies U_ICU_ENTRY_POINT_RENAME(ucurr_openISOCurrencies) -#define ucurr_register U_ICU_ENTRY_POINT_RENAME(ucurr_register) -#define ucurr_unregister U_ICU_ENTRY_POINT_RENAME(ucurr_unregister) -#define udat_adoptNumberFormat U_ICU_ENTRY_POINT_RENAME(udat_adoptNumberFormat) -#define udat_adoptNumberFormatForFields U_ICU_ENTRY_POINT_RENAME(udat_adoptNumberFormatForFields) -#define udat_applyPattern U_ICU_ENTRY_POINT_RENAME(udat_applyPattern) -#define udat_applyPatternRelative U_ICU_ENTRY_POINT_RENAME(udat_applyPatternRelative) -#define udat_clone U_ICU_ENTRY_POINT_RENAME(udat_clone) -#define udat_close U_ICU_ENTRY_POINT_RENAME(udat_close) -#define udat_countAvailable U_ICU_ENTRY_POINT_RENAME(udat_countAvailable) -#define udat_countSymbols U_ICU_ENTRY_POINT_RENAME(udat_countSymbols) -#define udat_format U_ICU_ENTRY_POINT_RENAME(udat_format) -#define udat_formatCalendar U_ICU_ENTRY_POINT_RENAME(udat_formatCalendar) -#define udat_formatCalendarForFields U_ICU_ENTRY_POINT_RENAME(udat_formatCalendarForFields) -#define udat_formatForFields U_ICU_ENTRY_POINT_RENAME(udat_formatForFields) -#define udat_get2DigitYearStart U_ICU_ENTRY_POINT_RENAME(udat_get2DigitYearStart) -#define udat_getAvailable U_ICU_ENTRY_POINT_RENAME(udat_getAvailable) -#define udat_getBooleanAttribute U_ICU_ENTRY_POINT_RENAME(udat_getBooleanAttribute) -#define udat_getCalendar U_ICU_ENTRY_POINT_RENAME(udat_getCalendar) -#define udat_getContext U_ICU_ENTRY_POINT_RENAME(udat_getContext) -#define udat_getLocaleByType U_ICU_ENTRY_POINT_RENAME(udat_getLocaleByType) -#define udat_getNumberFormat U_ICU_ENTRY_POINT_RENAME(udat_getNumberFormat) -#define udat_getNumberFormatForField U_ICU_ENTRY_POINT_RENAME(udat_getNumberFormatForField) -#define udat_getSymbols U_ICU_ENTRY_POINT_RENAME(udat_getSymbols) -#define udat_isLenient U_ICU_ENTRY_POINT_RENAME(udat_isLenient) -#define udat_open U_ICU_ENTRY_POINT_RENAME(udat_open) -#define udat_parse U_ICU_ENTRY_POINT_RENAME(udat_parse) -#define udat_parseCalendar U_ICU_ENTRY_POINT_RENAME(udat_parseCalendar) -#define udat_registerOpener U_ICU_ENTRY_POINT_RENAME(udat_registerOpener) -#define udat_set2DigitYearStart U_ICU_ENTRY_POINT_RENAME(udat_set2DigitYearStart) -#define udat_setBooleanAttribute U_ICU_ENTRY_POINT_RENAME(udat_setBooleanAttribute) -#define udat_setCalendar U_ICU_ENTRY_POINT_RENAME(udat_setCalendar) -#define udat_setContext U_ICU_ENTRY_POINT_RENAME(udat_setContext) -#define udat_setLenient U_ICU_ENTRY_POINT_RENAME(udat_setLenient) -#define udat_setNumberFormat U_ICU_ENTRY_POINT_RENAME(udat_setNumberFormat) -#define udat_setSymbols U_ICU_ENTRY_POINT_RENAME(udat_setSymbols) -#define udat_toCalendarDateField U_ICU_ENTRY_POINT_RENAME(udat_toCalendarDateField) -#define udat_toPattern U_ICU_ENTRY_POINT_RENAME(udat_toPattern) -#define udat_toPatternRelativeDate U_ICU_ENTRY_POINT_RENAME(udat_toPatternRelativeDate) -#define udat_toPatternRelativeTime U_ICU_ENTRY_POINT_RENAME(udat_toPatternRelativeTime) -#define udat_unregisterOpener U_ICU_ENTRY_POINT_RENAME(udat_unregisterOpener) -#define udata_checkCommonData U_ICU_ENTRY_POINT_RENAME(udata_checkCommonData) -#define udata_close U_ICU_ENTRY_POINT_RENAME(udata_close) -#define udata_closeSwapper U_ICU_ENTRY_POINT_RENAME(udata_closeSwapper) -#define udata_getHeaderSize U_ICU_ENTRY_POINT_RENAME(udata_getHeaderSize) -#define udata_getInfo U_ICU_ENTRY_POINT_RENAME(udata_getInfo) -#define udata_getInfoSize U_ICU_ENTRY_POINT_RENAME(udata_getInfoSize) -#define udata_getLength U_ICU_ENTRY_POINT_RENAME(udata_getLength) -#define udata_getMemory U_ICU_ENTRY_POINT_RENAME(udata_getMemory) -#define udata_getRawMemory U_ICU_ENTRY_POINT_RENAME(udata_getRawMemory) -#define udata_open U_ICU_ENTRY_POINT_RENAME(udata_open) -#define udata_openChoice U_ICU_ENTRY_POINT_RENAME(udata_openChoice) -#define udata_openSwapper U_ICU_ENTRY_POINT_RENAME(udata_openSwapper) -#define udata_openSwapperForInputData U_ICU_ENTRY_POINT_RENAME(udata_openSwapperForInputData) -#define udata_printError U_ICU_ENTRY_POINT_RENAME(udata_printError) -#define udata_readInt16 U_ICU_ENTRY_POINT_RENAME(udata_readInt16) -#define udata_readInt32 U_ICU_ENTRY_POINT_RENAME(udata_readInt32) -#define udata_setAppData U_ICU_ENTRY_POINT_RENAME(udata_setAppData) -#define udata_setCommonData U_ICU_ENTRY_POINT_RENAME(udata_setCommonData) -#define udata_setFileAccess U_ICU_ENTRY_POINT_RENAME(udata_setFileAccess) -#define udata_swapDataHeader U_ICU_ENTRY_POINT_RENAME(udata_swapDataHeader) -#define udata_swapInvStringBlock U_ICU_ENTRY_POINT_RENAME(udata_swapInvStringBlock) -#define udatpg_addPattern U_ICU_ENTRY_POINT_RENAME(udatpg_addPattern) -#define udatpg_clone U_ICU_ENTRY_POINT_RENAME(udatpg_clone) -#define udatpg_close U_ICU_ENTRY_POINT_RENAME(udatpg_close) -#define udatpg_getAppendItemFormat U_ICU_ENTRY_POINT_RENAME(udatpg_getAppendItemFormat) -#define udatpg_getAppendItemName U_ICU_ENTRY_POINT_RENAME(udatpg_getAppendItemName) -#define udatpg_getBaseSkeleton U_ICU_ENTRY_POINT_RENAME(udatpg_getBaseSkeleton) -#define udatpg_getBestPattern U_ICU_ENTRY_POINT_RENAME(udatpg_getBestPattern) -#define udatpg_getBestPatternWithOptions U_ICU_ENTRY_POINT_RENAME(udatpg_getBestPatternWithOptions) -#define udatpg_getDateTimeFormat U_ICU_ENTRY_POINT_RENAME(udatpg_getDateTimeFormat) -#define udatpg_getDateTimeFormatForStyle U_ICU_ENTRY_POINT_RENAME(udatpg_getDateTimeFormatForStyle) -#define udatpg_getDecimal U_ICU_ENTRY_POINT_RENAME(udatpg_getDecimal) -#define udatpg_getDefaultHourCycle U_ICU_ENTRY_POINT_RENAME(udatpg_getDefaultHourCycle) -#define udatpg_getFieldDisplayName U_ICU_ENTRY_POINT_RENAME(udatpg_getFieldDisplayName) -#define udatpg_getPatternForSkeleton U_ICU_ENTRY_POINT_RENAME(udatpg_getPatternForSkeleton) -#define udatpg_getSkeleton U_ICU_ENTRY_POINT_RENAME(udatpg_getSkeleton) -#define udatpg_open U_ICU_ENTRY_POINT_RENAME(udatpg_open) -#define udatpg_openBaseSkeletons U_ICU_ENTRY_POINT_RENAME(udatpg_openBaseSkeletons) -#define udatpg_openEmpty U_ICU_ENTRY_POINT_RENAME(udatpg_openEmpty) -#define udatpg_openSkeletons U_ICU_ENTRY_POINT_RENAME(udatpg_openSkeletons) -#define udatpg_replaceFieldTypes U_ICU_ENTRY_POINT_RENAME(udatpg_replaceFieldTypes) -#define udatpg_replaceFieldTypesWithOptions U_ICU_ENTRY_POINT_RENAME(udatpg_replaceFieldTypesWithOptions) -#define udatpg_setAppendItemFormat U_ICU_ENTRY_POINT_RENAME(udatpg_setAppendItemFormat) -#define udatpg_setAppendItemName U_ICU_ENTRY_POINT_RENAME(udatpg_setAppendItemName) -#define udatpg_setDateTimeFormat U_ICU_ENTRY_POINT_RENAME(udatpg_setDateTimeFormat) -#define udatpg_setDateTimeFormatForStyle U_ICU_ENTRY_POINT_RENAME(udatpg_setDateTimeFormatForStyle) -#define udatpg_setDecimal U_ICU_ENTRY_POINT_RENAME(udatpg_setDecimal) -#define udict_swap U_ICU_ENTRY_POINT_RENAME(udict_swap) -#define udispopt_fromGrammaticalCaseIdentifier U_ICU_ENTRY_POINT_RENAME(udispopt_fromGrammaticalCaseIdentifier) -#define udispopt_fromNounClassIdentifier U_ICU_ENTRY_POINT_RENAME(udispopt_fromNounClassIdentifier) -#define udispopt_fromPluralCategoryIdentifier U_ICU_ENTRY_POINT_RENAME(udispopt_fromPluralCategoryIdentifier) -#define udispopt_getGrammaticalCaseIdentifier U_ICU_ENTRY_POINT_RENAME(udispopt_getGrammaticalCaseIdentifier) -#define udispopt_getNounClassIdentifier U_ICU_ENTRY_POINT_RENAME(udispopt_getNounClassIdentifier) -#define udispopt_getPluralCategoryIdentifier U_ICU_ENTRY_POINT_RENAME(udispopt_getPluralCategoryIdentifier) -#define udtitvfmt_close U_ICU_ENTRY_POINT_RENAME(udtitvfmt_close) -#define udtitvfmt_closeResult U_ICU_ENTRY_POINT_RENAME(udtitvfmt_closeResult) -#define udtitvfmt_format U_ICU_ENTRY_POINT_RENAME(udtitvfmt_format) -#define udtitvfmt_formatCalendarToResult U_ICU_ENTRY_POINT_RENAME(udtitvfmt_formatCalendarToResult) -#define udtitvfmt_formatToResult U_ICU_ENTRY_POINT_RENAME(udtitvfmt_formatToResult) -#define udtitvfmt_getContext U_ICU_ENTRY_POINT_RENAME(udtitvfmt_getContext) -#define udtitvfmt_open U_ICU_ENTRY_POINT_RENAME(udtitvfmt_open) -#define udtitvfmt_openResult U_ICU_ENTRY_POINT_RENAME(udtitvfmt_openResult) -#define udtitvfmt_resultAsValue U_ICU_ENTRY_POINT_RENAME(udtitvfmt_resultAsValue) -#define udtitvfmt_setContext U_ICU_ENTRY_POINT_RENAME(udtitvfmt_setContext) -#define uenum_close U_ICU_ENTRY_POINT_RENAME(uenum_close) -#define uenum_count U_ICU_ENTRY_POINT_RENAME(uenum_count) -#define uenum_next U_ICU_ENTRY_POINT_RENAME(uenum_next) -#define uenum_nextDefault U_ICU_ENTRY_POINT_RENAME(uenum_nextDefault) -#define uenum_openCharStringsEnumeration U_ICU_ENTRY_POINT_RENAME(uenum_openCharStringsEnumeration) -#define uenum_openFromStringEnumeration U_ICU_ENTRY_POINT_RENAME(uenum_openFromStringEnumeration) -#define uenum_openUCharStringsEnumeration U_ICU_ENTRY_POINT_RENAME(uenum_openUCharStringsEnumeration) -#define uenum_reset U_ICU_ENTRY_POINT_RENAME(uenum_reset) -#define uenum_unext U_ICU_ENTRY_POINT_RENAME(uenum_unext) -#define uenum_unextDefault U_ICU_ENTRY_POINT_RENAME(uenum_unextDefault) -#define ufieldpositer_close U_ICU_ENTRY_POINT_RENAME(ufieldpositer_close) -#define ufieldpositer_next U_ICU_ENTRY_POINT_RENAME(ufieldpositer_next) -#define ufieldpositer_open U_ICU_ENTRY_POINT_RENAME(ufieldpositer_open) -#define ufile_getch U_ICU_ENTRY_POINT_RENAME(ufile_getch) -#define ufile_getch32 U_ICU_ENTRY_POINT_RENAME(ufile_getch32) -#define ufmt_close U_ICU_ENTRY_POINT_RENAME(ufmt_close) -#define ufmt_getArrayItemByIndex U_ICU_ENTRY_POINT_RENAME(ufmt_getArrayItemByIndex) -#define ufmt_getArrayLength U_ICU_ENTRY_POINT_RENAME(ufmt_getArrayLength) -#define ufmt_getDate U_ICU_ENTRY_POINT_RENAME(ufmt_getDate) -#define ufmt_getDecNumChars U_ICU_ENTRY_POINT_RENAME(ufmt_getDecNumChars) -#define ufmt_getDouble U_ICU_ENTRY_POINT_RENAME(ufmt_getDouble) -#define ufmt_getInt64 U_ICU_ENTRY_POINT_RENAME(ufmt_getInt64) -#define ufmt_getLong U_ICU_ENTRY_POINT_RENAME(ufmt_getLong) -#define ufmt_getObject U_ICU_ENTRY_POINT_RENAME(ufmt_getObject) -#define ufmt_getType U_ICU_ENTRY_POINT_RENAME(ufmt_getType) -#define ufmt_getUChars U_ICU_ENTRY_POINT_RENAME(ufmt_getUChars) -#define ufmt_isNumeric U_ICU_ENTRY_POINT_RENAME(ufmt_isNumeric) -#define ufmt_open U_ICU_ENTRY_POINT_RENAME(ufmt_open) -#define ufmtval_getString U_ICU_ENTRY_POINT_RENAME(ufmtval_getString) -#define ufmtval_nextPosition U_ICU_ENTRY_POINT_RENAME(ufmtval_nextPosition) -#define ugender_getInstance U_ICU_ENTRY_POINT_RENAME(ugender_getInstance) -#define ugender_getListGender U_ICU_ENTRY_POINT_RENAME(ugender_getListGender) -#define uhash_close U_ICU_ENTRY_POINT_RENAME(uhash_close) -#define uhash_compareCaselessUnicodeString U_ICU_ENTRY_POINT_RENAME(uhash_compareCaselessUnicodeString) -#define uhash_compareChars U_ICU_ENTRY_POINT_RENAME(uhash_compareChars) -#define uhash_compareIChars U_ICU_ENTRY_POINT_RENAME(uhash_compareIChars) -#define uhash_compareLong U_ICU_ENTRY_POINT_RENAME(uhash_compareLong) -#define uhash_compareScriptSet U_ICU_ENTRY_POINT_RENAME(uhash_compareScriptSet) -#define uhash_compareUChars U_ICU_ENTRY_POINT_RENAME(uhash_compareUChars) -#define uhash_compareUnicodeString U_ICU_ENTRY_POINT_RENAME(uhash_compareUnicodeString) -#define uhash_containsKey U_ICU_ENTRY_POINT_RENAME(uhash_containsKey) -#define uhash_count U_ICU_ENTRY_POINT_RENAME(uhash_count) -#define uhash_deleteHashtable U_ICU_ENTRY_POINT_RENAME(uhash_deleteHashtable) -#define uhash_deleteScriptSet U_ICU_ENTRY_POINT_RENAME(uhash_deleteScriptSet) -#define uhash_equals U_ICU_ENTRY_POINT_RENAME(uhash_equals) -#define uhash_equalsScriptSet U_ICU_ENTRY_POINT_RENAME(uhash_equalsScriptSet) -#define uhash_find U_ICU_ENTRY_POINT_RENAME(uhash_find) -#define uhash_get U_ICU_ENTRY_POINT_RENAME(uhash_get) -#define uhash_geti U_ICU_ENTRY_POINT_RENAME(uhash_geti) -#define uhash_getiAndFound U_ICU_ENTRY_POINT_RENAME(uhash_getiAndFound) -#define uhash_hashCaselessUnicodeString U_ICU_ENTRY_POINT_RENAME(uhash_hashCaselessUnicodeString) -#define uhash_hashChars U_ICU_ENTRY_POINT_RENAME(uhash_hashChars) -#define uhash_hashIChars U_ICU_ENTRY_POINT_RENAME(uhash_hashIChars) -#define uhash_hashLong U_ICU_ENTRY_POINT_RENAME(uhash_hashLong) -#define uhash_hashScriptSet U_ICU_ENTRY_POINT_RENAME(uhash_hashScriptSet) -#define uhash_hashUChars U_ICU_ENTRY_POINT_RENAME(uhash_hashUChars) -#define uhash_hashUnicodeString U_ICU_ENTRY_POINT_RENAME(uhash_hashUnicodeString) -#define uhash_icontainsKey U_ICU_ENTRY_POINT_RENAME(uhash_icontainsKey) -#define uhash_iget U_ICU_ENTRY_POINT_RENAME(uhash_iget) -#define uhash_igeti U_ICU_ENTRY_POINT_RENAME(uhash_igeti) -#define uhash_igetiAndFound U_ICU_ENTRY_POINT_RENAME(uhash_igetiAndFound) -#define uhash_init U_ICU_ENTRY_POINT_RENAME(uhash_init) -#define uhash_initSize U_ICU_ENTRY_POINT_RENAME(uhash_initSize) -#define uhash_iput U_ICU_ENTRY_POINT_RENAME(uhash_iput) -#define uhash_iputi U_ICU_ENTRY_POINT_RENAME(uhash_iputi) -#define uhash_iputiAllowZero U_ICU_ENTRY_POINT_RENAME(uhash_iputiAllowZero) -#define uhash_iremove U_ICU_ENTRY_POINT_RENAME(uhash_iremove) -#define uhash_iremovei U_ICU_ENTRY_POINT_RENAME(uhash_iremovei) -#define uhash_nextElement U_ICU_ENTRY_POINT_RENAME(uhash_nextElement) -#define uhash_open U_ICU_ENTRY_POINT_RENAME(uhash_open) -#define uhash_openSize U_ICU_ENTRY_POINT_RENAME(uhash_openSize) -#define uhash_put U_ICU_ENTRY_POINT_RENAME(uhash_put) -#define uhash_puti U_ICU_ENTRY_POINT_RENAME(uhash_puti) -#define uhash_putiAllowZero U_ICU_ENTRY_POINT_RENAME(uhash_putiAllowZero) -#define uhash_remove U_ICU_ENTRY_POINT_RENAME(uhash_remove) -#define uhash_removeAll U_ICU_ENTRY_POINT_RENAME(uhash_removeAll) -#define uhash_removeElement U_ICU_ENTRY_POINT_RENAME(uhash_removeElement) -#define uhash_removei U_ICU_ENTRY_POINT_RENAME(uhash_removei) -#define uhash_setKeyComparator U_ICU_ENTRY_POINT_RENAME(uhash_setKeyComparator) -#define uhash_setKeyDeleter U_ICU_ENTRY_POINT_RENAME(uhash_setKeyDeleter) -#define uhash_setKeyHasher U_ICU_ENTRY_POINT_RENAME(uhash_setKeyHasher) -#define uhash_setResizePolicy U_ICU_ENTRY_POINT_RENAME(uhash_setResizePolicy) -#define uhash_setValueComparator U_ICU_ENTRY_POINT_RENAME(uhash_setValueComparator) -#define uhash_setValueDeleter U_ICU_ENTRY_POINT_RENAME(uhash_setValueDeleter) -#define uidna_IDNToASCII U_ICU_ENTRY_POINT_RENAME(uidna_IDNToASCII) -#define uidna_IDNToUnicode U_ICU_ENTRY_POINT_RENAME(uidna_IDNToUnicode) -#define uidna_close U_ICU_ENTRY_POINT_RENAME(uidna_close) -#define uidna_compare U_ICU_ENTRY_POINT_RENAME(uidna_compare) -#define uidna_labelToASCII U_ICU_ENTRY_POINT_RENAME(uidna_labelToASCII) -#define uidna_labelToASCII_UTF8 U_ICU_ENTRY_POINT_RENAME(uidna_labelToASCII_UTF8) -#define uidna_labelToUnicode U_ICU_ENTRY_POINT_RENAME(uidna_labelToUnicode) -#define uidna_labelToUnicodeUTF8 U_ICU_ENTRY_POINT_RENAME(uidna_labelToUnicodeUTF8) -#define uidna_nameToASCII U_ICU_ENTRY_POINT_RENAME(uidna_nameToASCII) -#define uidna_nameToASCII_UTF8 U_ICU_ENTRY_POINT_RENAME(uidna_nameToASCII_UTF8) -#define uidna_nameToUnicode U_ICU_ENTRY_POINT_RENAME(uidna_nameToUnicode) -#define uidna_nameToUnicodeUTF8 U_ICU_ENTRY_POINT_RENAME(uidna_nameToUnicodeUTF8) -#define uidna_openUTS46 U_ICU_ENTRY_POINT_RENAME(uidna_openUTS46) -#define uidna_toASCII U_ICU_ENTRY_POINT_RENAME(uidna_toASCII) -#define uidna_toUnicode U_ICU_ENTRY_POINT_RENAME(uidna_toUnicode) -#define uiter_current32 U_ICU_ENTRY_POINT_RENAME(uiter_current32) -#define uiter_getState U_ICU_ENTRY_POINT_RENAME(uiter_getState) -#define uiter_next32 U_ICU_ENTRY_POINT_RENAME(uiter_next32) -#define uiter_previous32 U_ICU_ENTRY_POINT_RENAME(uiter_previous32) -#define uiter_setCharacterIterator U_ICU_ENTRY_POINT_RENAME(uiter_setCharacterIterator) -#define uiter_setReplaceable U_ICU_ENTRY_POINT_RENAME(uiter_setReplaceable) -#define uiter_setState U_ICU_ENTRY_POINT_RENAME(uiter_setState) -#define uiter_setString U_ICU_ENTRY_POINT_RENAME(uiter_setString) -#define uiter_setUTF16BE U_ICU_ENTRY_POINT_RENAME(uiter_setUTF16BE) -#define uiter_setUTF8 U_ICU_ENTRY_POINT_RENAME(uiter_setUTF8) -#define uldn_close U_ICU_ENTRY_POINT_RENAME(uldn_close) -#define uldn_getContext U_ICU_ENTRY_POINT_RENAME(uldn_getContext) -#define uldn_getDialectHandling U_ICU_ENTRY_POINT_RENAME(uldn_getDialectHandling) -#define uldn_getLocale U_ICU_ENTRY_POINT_RENAME(uldn_getLocale) -#define uldn_keyDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_keyDisplayName) -#define uldn_keyValueDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_keyValueDisplayName) -#define uldn_languageDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_languageDisplayName) -#define uldn_localeDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_localeDisplayName) -#define uldn_open U_ICU_ENTRY_POINT_RENAME(uldn_open) -#define uldn_openForContext U_ICU_ENTRY_POINT_RENAME(uldn_openForContext) -#define uldn_regionDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_regionDisplayName) -#define uldn_scriptCodeDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_scriptCodeDisplayName) -#define uldn_scriptDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_scriptDisplayName) -#define uldn_variantDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_variantDisplayName) -#define ulist_addItemBeginList U_ICU_ENTRY_POINT_RENAME(ulist_addItemBeginList) -#define ulist_addItemEndList U_ICU_ENTRY_POINT_RENAME(ulist_addItemEndList) -#define ulist_close_keyword_values_iterator U_ICU_ENTRY_POINT_RENAME(ulist_close_keyword_values_iterator) -#define ulist_containsString U_ICU_ENTRY_POINT_RENAME(ulist_containsString) -#define ulist_count_keyword_values U_ICU_ENTRY_POINT_RENAME(ulist_count_keyword_values) -#define ulist_createEmptyList U_ICU_ENTRY_POINT_RENAME(ulist_createEmptyList) -#define ulist_deleteList U_ICU_ENTRY_POINT_RENAME(ulist_deleteList) -#define ulist_getListFromEnum U_ICU_ENTRY_POINT_RENAME(ulist_getListFromEnum) -#define ulist_getListSize U_ICU_ENTRY_POINT_RENAME(ulist_getListSize) -#define ulist_getNext U_ICU_ENTRY_POINT_RENAME(ulist_getNext) -#define ulist_next_keyword_value U_ICU_ENTRY_POINT_RENAME(ulist_next_keyword_value) -#define ulist_removeString U_ICU_ENTRY_POINT_RENAME(ulist_removeString) -#define ulist_resetList U_ICU_ENTRY_POINT_RENAME(ulist_resetList) -#define ulist_reset_keyword_values_iterator U_ICU_ENTRY_POINT_RENAME(ulist_reset_keyword_values_iterator) -#define ulistfmt_close U_ICU_ENTRY_POINT_RENAME(ulistfmt_close) -#define ulistfmt_closeResult U_ICU_ENTRY_POINT_RENAME(ulistfmt_closeResult) -#define ulistfmt_format U_ICU_ENTRY_POINT_RENAME(ulistfmt_format) -#define ulistfmt_formatStringsToResult U_ICU_ENTRY_POINT_RENAME(ulistfmt_formatStringsToResult) -#define ulistfmt_open U_ICU_ENTRY_POINT_RENAME(ulistfmt_open) -#define ulistfmt_openForType U_ICU_ENTRY_POINT_RENAME(ulistfmt_openForType) -#define ulistfmt_openResult U_ICU_ENTRY_POINT_RENAME(ulistfmt_openResult) -#define ulistfmt_resultAsValue U_ICU_ENTRY_POINT_RENAME(ulistfmt_resultAsValue) -#define uloc_acceptLanguage U_ICU_ENTRY_POINT_RENAME(uloc_acceptLanguage) -#define uloc_acceptLanguageFromHTTP U_ICU_ENTRY_POINT_RENAME(uloc_acceptLanguageFromHTTP) -#define uloc_addLikelySubtags U_ICU_ENTRY_POINT_RENAME(uloc_addLikelySubtags) -#define uloc_canonicalize U_ICU_ENTRY_POINT_RENAME(uloc_canonicalize) -#define uloc_countAvailable U_ICU_ENTRY_POINT_RENAME(uloc_countAvailable) -#define uloc_forLanguageTag U_ICU_ENTRY_POINT_RENAME(uloc_forLanguageTag) -#define uloc_getAvailable U_ICU_ENTRY_POINT_RENAME(uloc_getAvailable) -#define uloc_getBaseName U_ICU_ENTRY_POINT_RENAME(uloc_getBaseName) -#define uloc_getCharacterOrientation U_ICU_ENTRY_POINT_RENAME(uloc_getCharacterOrientation) -#define uloc_getCountry U_ICU_ENTRY_POINT_RENAME(uloc_getCountry) -#define uloc_getCurrentCountryID U_ICU_ENTRY_POINT_RENAME(uloc_getCurrentCountryID) -#define uloc_getCurrentLanguageID U_ICU_ENTRY_POINT_RENAME(uloc_getCurrentLanguageID) -#define uloc_getDefault U_ICU_ENTRY_POINT_RENAME(uloc_getDefault) -#define uloc_getDisplayCountry U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayCountry) -#define uloc_getDisplayKeyword U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayKeyword) -#define uloc_getDisplayKeywordValue U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayKeywordValue) -#define uloc_getDisplayLanguage U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayLanguage) -#define uloc_getDisplayName U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayName) -#define uloc_getDisplayScript U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayScript) -#define uloc_getDisplayVariant U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayVariant) -#define uloc_getISO3Country U_ICU_ENTRY_POINT_RENAME(uloc_getISO3Country) -#define uloc_getISO3Language U_ICU_ENTRY_POINT_RENAME(uloc_getISO3Language) -#define uloc_getISOCountries U_ICU_ENTRY_POINT_RENAME(uloc_getISOCountries) -#define uloc_getISOLanguages U_ICU_ENTRY_POINT_RENAME(uloc_getISOLanguages) -#define uloc_getKeywordValue U_ICU_ENTRY_POINT_RENAME(uloc_getKeywordValue) -#define uloc_getLCID U_ICU_ENTRY_POINT_RENAME(uloc_getLCID) -#define uloc_getLanguage U_ICU_ENTRY_POINT_RENAME(uloc_getLanguage) -#define uloc_getLineOrientation U_ICU_ENTRY_POINT_RENAME(uloc_getLineOrientation) -#define uloc_getLocaleForLCID U_ICU_ENTRY_POINT_RENAME(uloc_getLocaleForLCID) -#define uloc_getName U_ICU_ENTRY_POINT_RENAME(uloc_getName) -#define uloc_getParent U_ICU_ENTRY_POINT_RENAME(uloc_getParent) -#define uloc_getScript U_ICU_ENTRY_POINT_RENAME(uloc_getScript) -#define uloc_getTableStringWithFallback U_ICU_ENTRY_POINT_RENAME(uloc_getTableStringWithFallback) -#define uloc_getVariant U_ICU_ENTRY_POINT_RENAME(uloc_getVariant) -#define uloc_isRightToLeft U_ICU_ENTRY_POINT_RENAME(uloc_isRightToLeft) -#define uloc_minimizeSubtags U_ICU_ENTRY_POINT_RENAME(uloc_minimizeSubtags) -#define uloc_openAvailableByType U_ICU_ENTRY_POINT_RENAME(uloc_openAvailableByType) -#define uloc_openKeywordList U_ICU_ENTRY_POINT_RENAME(uloc_openKeywordList) -#define uloc_openKeywords U_ICU_ENTRY_POINT_RENAME(uloc_openKeywords) -#define uloc_setDefault U_ICU_ENTRY_POINT_RENAME(uloc_setDefault) -#define uloc_setKeywordValue U_ICU_ENTRY_POINT_RENAME(uloc_setKeywordValue) -#define uloc_toLanguageTag U_ICU_ENTRY_POINT_RENAME(uloc_toLanguageTag) -#define uloc_toLegacyKey U_ICU_ENTRY_POINT_RENAME(uloc_toLegacyKey) -#define uloc_toLegacyType U_ICU_ENTRY_POINT_RENAME(uloc_toLegacyType) -#define uloc_toUnicodeLocaleKey U_ICU_ENTRY_POINT_RENAME(uloc_toUnicodeLocaleKey) -#define uloc_toUnicodeLocaleType U_ICU_ENTRY_POINT_RENAME(uloc_toUnicodeLocaleType) -#define ulocdata_close U_ICU_ENTRY_POINT_RENAME(ulocdata_close) -#define ulocdata_getCLDRVersion U_ICU_ENTRY_POINT_RENAME(ulocdata_getCLDRVersion) -#define ulocdata_getDelimiter U_ICU_ENTRY_POINT_RENAME(ulocdata_getDelimiter) -#define ulocdata_getExemplarSet U_ICU_ENTRY_POINT_RENAME(ulocdata_getExemplarSet) -#define ulocdata_getLocaleDisplayPattern U_ICU_ENTRY_POINT_RENAME(ulocdata_getLocaleDisplayPattern) -#define ulocdata_getLocaleSeparator U_ICU_ENTRY_POINT_RENAME(ulocdata_getLocaleSeparator) -#define ulocdata_getMeasurementSystem U_ICU_ENTRY_POINT_RENAME(ulocdata_getMeasurementSystem) -#define ulocdata_getNoSubstitute U_ICU_ENTRY_POINT_RENAME(ulocdata_getNoSubstitute) -#define ulocdata_getPaperSize U_ICU_ENTRY_POINT_RENAME(ulocdata_getPaperSize) -#define ulocdata_open U_ICU_ENTRY_POINT_RENAME(ulocdata_open) -#define ulocdata_setNoSubstitute U_ICU_ENTRY_POINT_RENAME(ulocdata_setNoSubstitute) -#define ulocimp_addLikelySubtags U_ICU_ENTRY_POINT_RENAME(ulocimp_addLikelySubtags) -#define ulocimp_canonicalize U_ICU_ENTRY_POINT_RENAME(ulocimp_canonicalize) -#define ulocimp_forLanguageTag U_ICU_ENTRY_POINT_RENAME(ulocimp_forLanguageTag) -#define ulocimp_getBaseName U_ICU_ENTRY_POINT_RENAME(ulocimp_getBaseName) -#define ulocimp_getCountry U_ICU_ENTRY_POINT_RENAME(ulocimp_getCountry) -#define ulocimp_getKeywordValue U_ICU_ENTRY_POINT_RENAME(ulocimp_getKeywordValue) -#define ulocimp_getKeywords U_ICU_ENTRY_POINT_RENAME(ulocimp_getKeywords) -#define ulocimp_getKnownCanonicalizedLocaleForTest U_ICU_ENTRY_POINT_RENAME(ulocimp_getKnownCanonicalizedLocaleForTest) -#define ulocimp_getLanguage U_ICU_ENTRY_POINT_RENAME(ulocimp_getLanguage) -#define ulocimp_getName U_ICU_ENTRY_POINT_RENAME(ulocimp_getName) -#define ulocimp_getRegionForSupplementalData U_ICU_ENTRY_POINT_RENAME(ulocimp_getRegionForSupplementalData) -#define ulocimp_getScript U_ICU_ENTRY_POINT_RENAME(ulocimp_getScript) -#define ulocimp_isCanonicalizedLocaleForTest U_ICU_ENTRY_POINT_RENAME(ulocimp_isCanonicalizedLocaleForTest) -#define ulocimp_minimizeSubtags U_ICU_ENTRY_POINT_RENAME(ulocimp_minimizeSubtags) -#define ulocimp_toBcpKey U_ICU_ENTRY_POINT_RENAME(ulocimp_toBcpKey) -#define ulocimp_toBcpType U_ICU_ENTRY_POINT_RENAME(ulocimp_toBcpType) -#define ulocimp_toLanguageTag U_ICU_ENTRY_POINT_RENAME(ulocimp_toLanguageTag) -#define ulocimp_toLegacyKey U_ICU_ENTRY_POINT_RENAME(ulocimp_toLegacyKey) -#define ulocimp_toLegacyType U_ICU_ENTRY_POINT_RENAME(ulocimp_toLegacyType) -#define ultag_getTKeyStart U_ICU_ENTRY_POINT_RENAME(ultag_getTKeyStart) -#define ultag_isExtensionSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isExtensionSubtags) -#define ultag_isLanguageSubtag U_ICU_ENTRY_POINT_RENAME(ultag_isLanguageSubtag) -#define ultag_isPrivateuseValueSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isPrivateuseValueSubtags) -#define ultag_isRegionSubtag U_ICU_ENTRY_POINT_RENAME(ultag_isRegionSubtag) -#define ultag_isScriptSubtag U_ICU_ENTRY_POINT_RENAME(ultag_isScriptSubtag) -#define ultag_isTransformedExtensionSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isTransformedExtensionSubtags) -#define ultag_isUnicodeExtensionSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeExtensionSubtags) -#define ultag_isUnicodeLocaleAttribute U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleAttribute) -#define ultag_isUnicodeLocaleAttributes U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleAttributes) -#define ultag_isUnicodeLocaleKey U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleKey) -#define ultag_isUnicodeLocaleType U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleType) -#define ultag_isVariantSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isVariantSubtags) -#define umeas_getPrefixBase U_ICU_ENTRY_POINT_RENAME(umeas_getPrefixBase) -#define umeas_getPrefixPower U_ICU_ENTRY_POINT_RENAME(umeas_getPrefixPower) -#define umsg_applyPattern U_ICU_ENTRY_POINT_RENAME(umsg_applyPattern) -#define umsg_autoQuoteApostrophe U_ICU_ENTRY_POINT_RENAME(umsg_autoQuoteApostrophe) -#define umsg_clone U_ICU_ENTRY_POINT_RENAME(umsg_clone) -#define umsg_close U_ICU_ENTRY_POINT_RENAME(umsg_close) -#define umsg_format U_ICU_ENTRY_POINT_RENAME(umsg_format) -#define umsg_getLocale U_ICU_ENTRY_POINT_RENAME(umsg_getLocale) -#define umsg_open U_ICU_ENTRY_POINT_RENAME(umsg_open) -#define umsg_parse U_ICU_ENTRY_POINT_RENAME(umsg_parse) -#define umsg_setLocale U_ICU_ENTRY_POINT_RENAME(umsg_setLocale) -#define umsg_toPattern U_ICU_ENTRY_POINT_RENAME(umsg_toPattern) -#define umsg_vformat U_ICU_ENTRY_POINT_RENAME(umsg_vformat) -#define umsg_vparse U_ICU_ENTRY_POINT_RENAME(umsg_vparse) -#define umtx_lock U_ICU_ENTRY_POINT_RENAME(umtx_lock) -#define umtx_unlock U_ICU_ENTRY_POINT_RENAME(umtx_unlock) -#define umutablecptrie_buildImmutable U_ICU_ENTRY_POINT_RENAME(umutablecptrie_buildImmutable) -#define umutablecptrie_clone U_ICU_ENTRY_POINT_RENAME(umutablecptrie_clone) -#define umutablecptrie_close U_ICU_ENTRY_POINT_RENAME(umutablecptrie_close) -#define umutablecptrie_fromUCPMap U_ICU_ENTRY_POINT_RENAME(umutablecptrie_fromUCPMap) -#define umutablecptrie_fromUCPTrie U_ICU_ENTRY_POINT_RENAME(umutablecptrie_fromUCPTrie) -#define umutablecptrie_get U_ICU_ENTRY_POINT_RENAME(umutablecptrie_get) -#define umutablecptrie_getRange U_ICU_ENTRY_POINT_RENAME(umutablecptrie_getRange) -#define umutablecptrie_open U_ICU_ENTRY_POINT_RENAME(umutablecptrie_open) -#define umutablecptrie_set U_ICU_ENTRY_POINT_RENAME(umutablecptrie_set) -#define umutablecptrie_setRange U_ICU_ENTRY_POINT_RENAME(umutablecptrie_setRange) -#define uniset_getUnicode32Instance U_ICU_ENTRY_POINT_RENAME(uniset_getUnicode32Instance) -#define unorm2_append U_ICU_ENTRY_POINT_RENAME(unorm2_append) -#define unorm2_close U_ICU_ENTRY_POINT_RENAME(unorm2_close) -#define unorm2_composePair U_ICU_ENTRY_POINT_RENAME(unorm2_composePair) -#define unorm2_getCombiningClass U_ICU_ENTRY_POINT_RENAME(unorm2_getCombiningClass) -#define unorm2_getDecomposition U_ICU_ENTRY_POINT_RENAME(unorm2_getDecomposition) -#define unorm2_getInstance U_ICU_ENTRY_POINT_RENAME(unorm2_getInstance) -#define unorm2_getNFCInstance U_ICU_ENTRY_POINT_RENAME(unorm2_getNFCInstance) -#define unorm2_getNFDInstance U_ICU_ENTRY_POINT_RENAME(unorm2_getNFDInstance) -#define unorm2_getNFKCCasefoldInstance U_ICU_ENTRY_POINT_RENAME(unorm2_getNFKCCasefoldInstance) -#define unorm2_getNFKCInstance U_ICU_ENTRY_POINT_RENAME(unorm2_getNFKCInstance) -#define unorm2_getNFKDInstance U_ICU_ENTRY_POINT_RENAME(unorm2_getNFKDInstance) -#define unorm2_getRawDecomposition U_ICU_ENTRY_POINT_RENAME(unorm2_getRawDecomposition) -#define unorm2_hasBoundaryAfter U_ICU_ENTRY_POINT_RENAME(unorm2_hasBoundaryAfter) -#define unorm2_hasBoundaryBefore U_ICU_ENTRY_POINT_RENAME(unorm2_hasBoundaryBefore) -#define unorm2_isInert U_ICU_ENTRY_POINT_RENAME(unorm2_isInert) -#define unorm2_isNormalized U_ICU_ENTRY_POINT_RENAME(unorm2_isNormalized) -#define unorm2_normalize U_ICU_ENTRY_POINT_RENAME(unorm2_normalize) -#define unorm2_normalizeSecondAndAppend U_ICU_ENTRY_POINT_RENAME(unorm2_normalizeSecondAndAppend) -#define unorm2_openFiltered U_ICU_ENTRY_POINT_RENAME(unorm2_openFiltered) -#define unorm2_quickCheck U_ICU_ENTRY_POINT_RENAME(unorm2_quickCheck) -#define unorm2_spanQuickCheckYes U_ICU_ENTRY_POINT_RENAME(unorm2_spanQuickCheckYes) -#define unorm2_swap U_ICU_ENTRY_POINT_RENAME(unorm2_swap) -#define unorm_compare U_ICU_ENTRY_POINT_RENAME(unorm_compare) -#define unorm_concatenate U_ICU_ENTRY_POINT_RENAME(unorm_concatenate) -#define unorm_getFCD16 U_ICU_ENTRY_POINT_RENAME(unorm_getFCD16) -#define unorm_getQuickCheck U_ICU_ENTRY_POINT_RENAME(unorm_getQuickCheck) -#define unorm_isNormalized U_ICU_ENTRY_POINT_RENAME(unorm_isNormalized) -#define unorm_isNormalizedWithOptions U_ICU_ENTRY_POINT_RENAME(unorm_isNormalizedWithOptions) -#define unorm_next U_ICU_ENTRY_POINT_RENAME(unorm_next) -#define unorm_normalize U_ICU_ENTRY_POINT_RENAME(unorm_normalize) -#define unorm_previous U_ICU_ENTRY_POINT_RENAME(unorm_previous) -#define unorm_quickCheck U_ICU_ENTRY_POINT_RENAME(unorm_quickCheck) -#define unorm_quickCheckWithOptions U_ICU_ENTRY_POINT_RENAME(unorm_quickCheckWithOptions) -#define unum_applyPattern U_ICU_ENTRY_POINT_RENAME(unum_applyPattern) -#define unum_clone U_ICU_ENTRY_POINT_RENAME(unum_clone) -#define unum_close U_ICU_ENTRY_POINT_RENAME(unum_close) -#define unum_countAvailable U_ICU_ENTRY_POINT_RENAME(unum_countAvailable) -#define unum_format U_ICU_ENTRY_POINT_RENAME(unum_format) -#define unum_formatDecimal U_ICU_ENTRY_POINT_RENAME(unum_formatDecimal) -#define unum_formatDouble U_ICU_ENTRY_POINT_RENAME(unum_formatDouble) -#define unum_formatDoubleCurrency U_ICU_ENTRY_POINT_RENAME(unum_formatDoubleCurrency) -#define unum_formatDoubleForFields U_ICU_ENTRY_POINT_RENAME(unum_formatDoubleForFields) -#define unum_formatInt64 U_ICU_ENTRY_POINT_RENAME(unum_formatInt64) -#define unum_formatUFormattable U_ICU_ENTRY_POINT_RENAME(unum_formatUFormattable) -#define unum_getAttribute U_ICU_ENTRY_POINT_RENAME(unum_getAttribute) -#define unum_getAvailable U_ICU_ENTRY_POINT_RENAME(unum_getAvailable) -#define unum_getContext U_ICU_ENTRY_POINT_RENAME(unum_getContext) -#define unum_getDoubleAttribute U_ICU_ENTRY_POINT_RENAME(unum_getDoubleAttribute) -#define unum_getLocaleByType U_ICU_ENTRY_POINT_RENAME(unum_getLocaleByType) -#define unum_getSymbol U_ICU_ENTRY_POINT_RENAME(unum_getSymbol) -#define unum_getTextAttribute U_ICU_ENTRY_POINT_RENAME(unum_getTextAttribute) -#define unum_hasAttribute U_ICU_ENTRY_POINT_RENAME(unum_hasAttribute) -#define unum_open U_ICU_ENTRY_POINT_RENAME(unum_open) -#define unum_parse U_ICU_ENTRY_POINT_RENAME(unum_parse) -#define unum_parseDecimal U_ICU_ENTRY_POINT_RENAME(unum_parseDecimal) -#define unum_parseDouble U_ICU_ENTRY_POINT_RENAME(unum_parseDouble) -#define unum_parseDoubleCurrency U_ICU_ENTRY_POINT_RENAME(unum_parseDoubleCurrency) -#define unum_parseInt64 U_ICU_ENTRY_POINT_RENAME(unum_parseInt64) -#define unum_parseToUFormattable U_ICU_ENTRY_POINT_RENAME(unum_parseToUFormattable) -#define unum_setAttribute U_ICU_ENTRY_POINT_RENAME(unum_setAttribute) -#define unum_setContext U_ICU_ENTRY_POINT_RENAME(unum_setContext) -#define unum_setDoubleAttribute U_ICU_ENTRY_POINT_RENAME(unum_setDoubleAttribute) -#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_openForSkeletonAndLocaleWithError U_ICU_ENTRY_POINT_RENAME(unumf_openForSkeletonAndLocaleWithError) -#define unumf_openResult U_ICU_ENTRY_POINT_RENAME(unumf_openResult) -#define unumf_resultAsValue U_ICU_ENTRY_POINT_RENAME(unumf_resultAsValue) -#define unumf_resultGetAllFieldPositions U_ICU_ENTRY_POINT_RENAME(unumf_resultGetAllFieldPositions) -#define unumf_resultNextFieldPosition U_ICU_ENTRY_POINT_RENAME(unumf_resultNextFieldPosition) -#define unumf_resultToDecimalNumber U_ICU_ENTRY_POINT_RENAME(unumf_resultToDecimalNumber) -#define unumf_resultToString U_ICU_ENTRY_POINT_RENAME(unumf_resultToString) -#define unumrf_close U_ICU_ENTRY_POINT_RENAME(unumrf_close) -#define unumrf_closeResult U_ICU_ENTRY_POINT_RENAME(unumrf_closeResult) -#define unumrf_formatDecimalRange U_ICU_ENTRY_POINT_RENAME(unumrf_formatDecimalRange) -#define unumrf_formatDoubleRange U_ICU_ENTRY_POINT_RENAME(unumrf_formatDoubleRange) -#define unumrf_openForSkeletonWithCollapseAndIdentityFallback U_ICU_ENTRY_POINT_RENAME(unumrf_openForSkeletonWithCollapseAndIdentityFallback) -#define unumrf_openResult U_ICU_ENTRY_POINT_RENAME(unumrf_openResult) -#define unumrf_resultAsValue U_ICU_ENTRY_POINT_RENAME(unumrf_resultAsValue) -#define unumrf_resultGetFirstDecimalNumber U_ICU_ENTRY_POINT_RENAME(unumrf_resultGetFirstDecimalNumber) -#define unumrf_resultGetIdentityResult U_ICU_ENTRY_POINT_RENAME(unumrf_resultGetIdentityResult) -#define unumrf_resultGetSecondDecimalNumber U_ICU_ENTRY_POINT_RENAME(unumrf_resultGetSecondDecimalNumber) -#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) -#define unumsys_getRadix U_ICU_ENTRY_POINT_RENAME(unumsys_getRadix) -#define unumsys_isAlgorithmic U_ICU_ENTRY_POINT_RENAME(unumsys_isAlgorithmic) -#define unumsys_open U_ICU_ENTRY_POINT_RENAME(unumsys_open) -#define unumsys_openAvailableNames U_ICU_ENTRY_POINT_RENAME(unumsys_openAvailableNames) -#define unumsys_openByName U_ICU_ENTRY_POINT_RENAME(unumsys_openByName) -#define uplrules_close U_ICU_ENTRY_POINT_RENAME(uplrules_close) -#define uplrules_getKeywords U_ICU_ENTRY_POINT_RENAME(uplrules_getKeywords) -#define uplrules_open U_ICU_ENTRY_POINT_RENAME(uplrules_open) -#define uplrules_openForType U_ICU_ENTRY_POINT_RENAME(uplrules_openForType) -#define uplrules_select U_ICU_ENTRY_POINT_RENAME(uplrules_select) -#define uplrules_selectForRange U_ICU_ENTRY_POINT_RENAME(uplrules_selectForRange) -#define uplrules_selectFormatted U_ICU_ENTRY_POINT_RENAME(uplrules_selectFormatted) -#define uplrules_selectWithFormat U_ICU_ENTRY_POINT_RENAME(uplrules_selectWithFormat) -#define uplug_closeLibrary U_ICU_ENTRY_POINT_RENAME(uplug_closeLibrary) -#define uplug_findLibrary U_ICU_ENTRY_POINT_RENAME(uplug_findLibrary) -#define uplug_getConfiguration U_ICU_ENTRY_POINT_RENAME(uplug_getConfiguration) -#define uplug_getContext U_ICU_ENTRY_POINT_RENAME(uplug_getContext) -#define uplug_getCurrentLevel U_ICU_ENTRY_POINT_RENAME(uplug_getCurrentLevel) -#define uplug_getLibrary U_ICU_ENTRY_POINT_RENAME(uplug_getLibrary) -#define uplug_getLibraryName U_ICU_ENTRY_POINT_RENAME(uplug_getLibraryName) -#define uplug_getPlugInternal U_ICU_ENTRY_POINT_RENAME(uplug_getPlugInternal) -#define uplug_getPlugLevel U_ICU_ENTRY_POINT_RENAME(uplug_getPlugLevel) -#define uplug_getPlugLoadStatus U_ICU_ENTRY_POINT_RENAME(uplug_getPlugLoadStatus) -#define uplug_getPlugName U_ICU_ENTRY_POINT_RENAME(uplug_getPlugName) -#define uplug_getPluginFile U_ICU_ENTRY_POINT_RENAME(uplug_getPluginFile) -#define uplug_getSymbolName U_ICU_ENTRY_POINT_RENAME(uplug_getSymbolName) -#define uplug_init U_ICU_ENTRY_POINT_RENAME(uplug_init) -#define uplug_loadPlugFromEntrypoint U_ICU_ENTRY_POINT_RENAME(uplug_loadPlugFromEntrypoint) -#define uplug_loadPlugFromLibrary U_ICU_ENTRY_POINT_RENAME(uplug_loadPlugFromLibrary) -#define uplug_nextPlug U_ICU_ENTRY_POINT_RENAME(uplug_nextPlug) -#define uplug_openLibrary U_ICU_ENTRY_POINT_RENAME(uplug_openLibrary) -#define uplug_removePlug U_ICU_ENTRY_POINT_RENAME(uplug_removePlug) -#define uplug_setContext U_ICU_ENTRY_POINT_RENAME(uplug_setContext) -#define uplug_setPlugLevel U_ICU_ENTRY_POINT_RENAME(uplug_setPlugLevel) -#define uplug_setPlugName U_ICU_ENTRY_POINT_RENAME(uplug_setPlugName) -#define uplug_setPlugNoUnload U_ICU_ENTRY_POINT_RENAME(uplug_setPlugNoUnload) -#define uprops_addPropertyStarts U_ICU_ENTRY_POINT_RENAME(uprops_addPropertyStarts) -#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) -#define uprv_calloc U_ICU_ENTRY_POINT_RENAME(uprv_calloc) -#define uprv_ceil U_ICU_ENTRY_POINT_RENAME(uprv_ceil) -#define uprv_compareASCIIPropertyNames U_ICU_ENTRY_POINT_RENAME(uprv_compareASCIIPropertyNames) -#define uprv_compareEBCDICPropertyNames U_ICU_ENTRY_POINT_RENAME(uprv_compareEBCDICPropertyNames) -#define uprv_compareInvAscii U_ICU_ENTRY_POINT_RENAME(uprv_compareInvAscii) -#define uprv_compareInvEbcdic U_ICU_ENTRY_POINT_RENAME(uprv_compareInvEbcdic) -#define uprv_compareInvEbcdicAsAscii U_ICU_ENTRY_POINT_RENAME(uprv_compareInvEbcdicAsAscii) -#define uprv_convertToLCID U_ICU_ENTRY_POINT_RENAME(uprv_convertToLCID) -#define uprv_convertToLCIDPlatform U_ICU_ENTRY_POINT_RENAME(uprv_convertToLCIDPlatform) -#define uprv_convertToPosix U_ICU_ENTRY_POINT_RENAME(uprv_convertToPosix) -#define uprv_copyAscii U_ICU_ENTRY_POINT_RENAME(uprv_copyAscii) -#define uprv_copyEbcdic U_ICU_ENTRY_POINT_RENAME(uprv_copyEbcdic) -#define uprv_decContextClearStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextClearStatus) -#define uprv_decContextDefault U_ICU_ENTRY_POINT_RENAME(uprv_decContextDefault) -#define uprv_decContextGetRounding U_ICU_ENTRY_POINT_RENAME(uprv_decContextGetRounding) -#define uprv_decContextGetStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextGetStatus) -#define uprv_decContextRestoreStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextRestoreStatus) -#define uprv_decContextSaveStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextSaveStatus) -#define uprv_decContextSetRounding U_ICU_ENTRY_POINT_RENAME(uprv_decContextSetRounding) -#define uprv_decContextSetStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextSetStatus) -#define uprv_decContextSetStatusFromString U_ICU_ENTRY_POINT_RENAME(uprv_decContextSetStatusFromString) -#define uprv_decContextSetStatusFromStringQuiet U_ICU_ENTRY_POINT_RENAME(uprv_decContextSetStatusFromStringQuiet) -#define uprv_decContextSetStatusQuiet U_ICU_ENTRY_POINT_RENAME(uprv_decContextSetStatusQuiet) -#define uprv_decContextStatusToString U_ICU_ENTRY_POINT_RENAME(uprv_decContextStatusToString) -#define uprv_decContextTestSavedStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextTestSavedStatus) -#define uprv_decContextTestStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextTestStatus) -#define uprv_decContextZeroStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextZeroStatus) -#define uprv_decNumberAbs U_ICU_ENTRY_POINT_RENAME(uprv_decNumberAbs) -#define uprv_decNumberAdd U_ICU_ENTRY_POINT_RENAME(uprv_decNumberAdd) -#define uprv_decNumberAnd U_ICU_ENTRY_POINT_RENAME(uprv_decNumberAnd) -#define uprv_decNumberClassToString U_ICU_ENTRY_POINT_RENAME(uprv_decNumberClassToString) -#define uprv_decNumberCompare U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCompare) -#define uprv_decNumberCompareSignal U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCompareSignal) -#define uprv_decNumberCompareTotal U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCompareTotal) -#define uprv_decNumberCompareTotalMag U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCompareTotalMag) -#define uprv_decNumberCopy U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCopy) -#define uprv_decNumberCopyAbs U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCopyAbs) -#define uprv_decNumberCopyNegate U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCopyNegate) -#define uprv_decNumberCopySign U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCopySign) -#define uprv_decNumberDivide U_ICU_ENTRY_POINT_RENAME(uprv_decNumberDivide) -#define uprv_decNumberDivideInteger U_ICU_ENTRY_POINT_RENAME(uprv_decNumberDivideInteger) -#define uprv_decNumberExp U_ICU_ENTRY_POINT_RENAME(uprv_decNumberExp) -#define uprv_decNumberFMA U_ICU_ENTRY_POINT_RENAME(uprv_decNumberFMA) -#define uprv_decNumberFromInt32 U_ICU_ENTRY_POINT_RENAME(uprv_decNumberFromInt32) -#define uprv_decNumberFromString U_ICU_ENTRY_POINT_RENAME(uprv_decNumberFromString) -#define uprv_decNumberFromUInt32 U_ICU_ENTRY_POINT_RENAME(uprv_decNumberFromUInt32) -#define uprv_decNumberGetBCD U_ICU_ENTRY_POINT_RENAME(uprv_decNumberGetBCD) -#define uprv_decNumberInvert U_ICU_ENTRY_POINT_RENAME(uprv_decNumberInvert) -#define uprv_decNumberIsNormal U_ICU_ENTRY_POINT_RENAME(uprv_decNumberIsNormal) -#define uprv_decNumberIsSubnormal U_ICU_ENTRY_POINT_RENAME(uprv_decNumberIsSubnormal) -#define uprv_decNumberLn U_ICU_ENTRY_POINT_RENAME(uprv_decNumberLn) -#define uprv_decNumberLog10 U_ICU_ENTRY_POINT_RENAME(uprv_decNumberLog10) -#define uprv_decNumberLogB U_ICU_ENTRY_POINT_RENAME(uprv_decNumberLogB) -#define uprv_decNumberMax U_ICU_ENTRY_POINT_RENAME(uprv_decNumberMax) -#define uprv_decNumberMaxMag U_ICU_ENTRY_POINT_RENAME(uprv_decNumberMaxMag) -#define uprv_decNumberMin U_ICU_ENTRY_POINT_RENAME(uprv_decNumberMin) -#define uprv_decNumberMinMag U_ICU_ENTRY_POINT_RENAME(uprv_decNumberMinMag) -#define uprv_decNumberMinus U_ICU_ENTRY_POINT_RENAME(uprv_decNumberMinus) -#define uprv_decNumberMultiply U_ICU_ENTRY_POINT_RENAME(uprv_decNumberMultiply) -#define uprv_decNumberNextMinus U_ICU_ENTRY_POINT_RENAME(uprv_decNumberNextMinus) -#define uprv_decNumberNextPlus U_ICU_ENTRY_POINT_RENAME(uprv_decNumberNextPlus) -#define uprv_decNumberNextToward U_ICU_ENTRY_POINT_RENAME(uprv_decNumberNextToward) -#define uprv_decNumberNormalize U_ICU_ENTRY_POINT_RENAME(uprv_decNumberNormalize) -#define uprv_decNumberOr U_ICU_ENTRY_POINT_RENAME(uprv_decNumberOr) -#define uprv_decNumberPlus U_ICU_ENTRY_POINT_RENAME(uprv_decNumberPlus) -#define uprv_decNumberPower U_ICU_ENTRY_POINT_RENAME(uprv_decNumberPower) -#define uprv_decNumberQuantize U_ICU_ENTRY_POINT_RENAME(uprv_decNumberQuantize) -#define uprv_decNumberReduce U_ICU_ENTRY_POINT_RENAME(uprv_decNumberReduce) -#define uprv_decNumberRemainder U_ICU_ENTRY_POINT_RENAME(uprv_decNumberRemainder) -#define uprv_decNumberRemainderNear U_ICU_ENTRY_POINT_RENAME(uprv_decNumberRemainderNear) -#define uprv_decNumberRescale U_ICU_ENTRY_POINT_RENAME(uprv_decNumberRescale) -#define uprv_decNumberRotate U_ICU_ENTRY_POINT_RENAME(uprv_decNumberRotate) -#define uprv_decNumberSameQuantum U_ICU_ENTRY_POINT_RENAME(uprv_decNumberSameQuantum) -#define uprv_decNumberScaleB U_ICU_ENTRY_POINT_RENAME(uprv_decNumberScaleB) -#define uprv_decNumberSetBCD U_ICU_ENTRY_POINT_RENAME(uprv_decNumberSetBCD) -#define uprv_decNumberShift U_ICU_ENTRY_POINT_RENAME(uprv_decNumberShift) -#define uprv_decNumberSquareRoot U_ICU_ENTRY_POINT_RENAME(uprv_decNumberSquareRoot) -#define uprv_decNumberSubtract U_ICU_ENTRY_POINT_RENAME(uprv_decNumberSubtract) -#define uprv_decNumberToEngString U_ICU_ENTRY_POINT_RENAME(uprv_decNumberToEngString) -#define uprv_decNumberToInt32 U_ICU_ENTRY_POINT_RENAME(uprv_decNumberToInt32) -#define uprv_decNumberToIntegralExact U_ICU_ENTRY_POINT_RENAME(uprv_decNumberToIntegralExact) -#define uprv_decNumberToIntegralValue U_ICU_ENTRY_POINT_RENAME(uprv_decNumberToIntegralValue) -#define uprv_decNumberToString U_ICU_ENTRY_POINT_RENAME(uprv_decNumberToString) -#define uprv_decNumberToUInt32 U_ICU_ENTRY_POINT_RENAME(uprv_decNumberToUInt32) -#define uprv_decNumberTrim U_ICU_ENTRY_POINT_RENAME(uprv_decNumberTrim) -#define uprv_decNumberVersion U_ICU_ENTRY_POINT_RENAME(uprv_decNumberVersion) -#define uprv_decNumberXor U_ICU_ENTRY_POINT_RENAME(uprv_decNumberXor) -#define uprv_decNumberZero U_ICU_ENTRY_POINT_RENAME(uprv_decNumberZero) -#define uprv_deleteConditionalCE32 U_ICU_ENTRY_POINT_RENAME(uprv_deleteConditionalCE32) -#define uprv_deleteUObject U_ICU_ENTRY_POINT_RENAME(uprv_deleteUObject) -#define uprv_dl_close U_ICU_ENTRY_POINT_RENAME(uprv_dl_close) -#define uprv_dl_open U_ICU_ENTRY_POINT_RENAME(uprv_dl_open) -#define uprv_dlsym_func U_ICU_ENTRY_POINT_RENAME(uprv_dlsym_func) -#define uprv_eastrncpy U_ICU_ENTRY_POINT_RENAME(uprv_eastrncpy) -#define uprv_ebcdicFromAscii U_ICU_ENTRY_POINT_RENAME(uprv_ebcdicFromAscii) -#define uprv_ebcdicToAscii U_ICU_ENTRY_POINT_RENAME(uprv_ebcdicToAscii) -#define uprv_ebcdicToLowercaseAscii U_ICU_ENTRY_POINT_RENAME(uprv_ebcdicToLowercaseAscii) -#define uprv_ebcdictolower U_ICU_ENTRY_POINT_RENAME(uprv_ebcdictolower) -#define uprv_fabs U_ICU_ENTRY_POINT_RENAME(uprv_fabs) -#define uprv_floor U_ICU_ENTRY_POINT_RENAME(uprv_floor) -#define uprv_fmax U_ICU_ENTRY_POINT_RENAME(uprv_fmax) -#define uprv_fmin U_ICU_ENTRY_POINT_RENAME(uprv_fmin) -#define uprv_fmod U_ICU_ENTRY_POINT_RENAME(uprv_fmod) -#define uprv_free U_ICU_ENTRY_POINT_RENAME(uprv_free) -#define uprv_getCharNameCharacters U_ICU_ENTRY_POINT_RENAME(uprv_getCharNameCharacters) -#define uprv_getDefaultLocaleID U_ICU_ENTRY_POINT_RENAME(uprv_getDefaultLocaleID) -#define uprv_getInfinity U_ICU_ENTRY_POINT_RENAME(uprv_getInfinity) -#define uprv_getMaxCharNameLength U_ICU_ENTRY_POINT_RENAME(uprv_getMaxCharNameLength) -#define uprv_getMaxValues U_ICU_ENTRY_POINT_RENAME(uprv_getMaxValues) -#define uprv_getNaN U_ICU_ENTRY_POINT_RENAME(uprv_getNaN) -#define uprv_getRawUTCtime U_ICU_ENTRY_POINT_RENAME(uprv_getRawUTCtime) -#define uprv_getStaticCurrencyName U_ICU_ENTRY_POINT_RENAME(uprv_getStaticCurrencyName) -#define uprv_getUTCtime U_ICU_ENTRY_POINT_RENAME(uprv_getUTCtime) -#define uprv_int32Comparator U_ICU_ENTRY_POINT_RENAME(uprv_int32Comparator) -#define uprv_isASCIILetter U_ICU_ENTRY_POINT_RENAME(uprv_isASCIILetter) -#define uprv_isEbcdicAtSign U_ICU_ENTRY_POINT_RENAME(uprv_isEbcdicAtSign) -#define uprv_isInfinite U_ICU_ENTRY_POINT_RENAME(uprv_isInfinite) -#define uprv_isInvariantString U_ICU_ENTRY_POINT_RENAME(uprv_isInvariantString) -#define uprv_isInvariantUString U_ICU_ENTRY_POINT_RENAME(uprv_isInvariantUString) -#define uprv_isNaN U_ICU_ENTRY_POINT_RENAME(uprv_isNaN) -#define uprv_isNegativeInfinity U_ICU_ENTRY_POINT_RENAME(uprv_isNegativeInfinity) -#define uprv_isPositiveInfinity U_ICU_ENTRY_POINT_RENAME(uprv_isPositiveInfinity) -#define uprv_itou U_ICU_ENTRY_POINT_RENAME(uprv_itou) -#define uprv_log U_ICU_ENTRY_POINT_RENAME(uprv_log) -#define uprv_malloc U_ICU_ENTRY_POINT_RENAME(uprv_malloc) -#define uprv_mapFile U_ICU_ENTRY_POINT_RENAME(uprv_mapFile) -#define uprv_max U_ICU_ENTRY_POINT_RENAME(uprv_max) -#define uprv_maxMantissa U_ICU_ENTRY_POINT_RENAME(uprv_maxMantissa) -#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) -#define uprv_pow10 U_ICU_ENTRY_POINT_RENAME(uprv_pow10) -#define uprv_realloc U_ICU_ENTRY_POINT_RENAME(uprv_realloc) -#define uprv_round U_ICU_ENTRY_POINT_RENAME(uprv_round) -#define uprv_sortArray U_ICU_ENTRY_POINT_RENAME(uprv_sortArray) -#define uprv_stableBinarySearch U_ICU_ENTRY_POINT_RENAME(uprv_stableBinarySearch) -#define uprv_strCompare U_ICU_ENTRY_POINT_RENAME(uprv_strCompare) -#define uprv_strdup U_ICU_ENTRY_POINT_RENAME(uprv_strdup) -#define uprv_stricmp U_ICU_ENTRY_POINT_RENAME(uprv_stricmp) -#define uprv_strndup U_ICU_ENTRY_POINT_RENAME(uprv_strndup) -#define uprv_strnicmp U_ICU_ENTRY_POINT_RENAME(uprv_strnicmp) -#define uprv_syntaxError U_ICU_ENTRY_POINT_RENAME(uprv_syntaxError) -#define uprv_timezone U_ICU_ENTRY_POINT_RENAME(uprv_timezone) -#define uprv_toupper U_ICU_ENTRY_POINT_RENAME(uprv_toupper) -#define uprv_trunc U_ICU_ENTRY_POINT_RENAME(uprv_trunc) -#define uprv_tzname U_ICU_ENTRY_POINT_RENAME(uprv_tzname) -#define uprv_tzname_clear_cache U_ICU_ENTRY_POINT_RENAME(uprv_tzname_clear_cache) -#define uprv_tzset U_ICU_ENTRY_POINT_RENAME(uprv_tzset) -#define uprv_uint16Comparator U_ICU_ENTRY_POINT_RENAME(uprv_uint16Comparator) -#define uprv_uint32Comparator U_ICU_ENTRY_POINT_RENAME(uprv_uint32Comparator) -#define uprv_unmapFile U_ICU_ENTRY_POINT_RENAME(uprv_unmapFile) -#define upvec_cloneArray U_ICU_ENTRY_POINT_RENAME(upvec_cloneArray) -#define upvec_close U_ICU_ENTRY_POINT_RENAME(upvec_close) -#define upvec_compact U_ICU_ENTRY_POINT_RENAME(upvec_compact) -#define upvec_compactToUTrie2Handler U_ICU_ENTRY_POINT_RENAME(upvec_compactToUTrie2Handler) -#define upvec_compactToUTrie2WithRowIndexes U_ICU_ENTRY_POINT_RENAME(upvec_compactToUTrie2WithRowIndexes) -#define upvec_getArray U_ICU_ENTRY_POINT_RENAME(upvec_getArray) -#define upvec_getRow U_ICU_ENTRY_POINT_RENAME(upvec_getRow) -#define upvec_getValue U_ICU_ENTRY_POINT_RENAME(upvec_getValue) -#define upvec_open U_ICU_ENTRY_POINT_RENAME(upvec_open) -#define upvec_setValue U_ICU_ENTRY_POINT_RENAME(upvec_setValue) -#define uregex_appendReplacement U_ICU_ENTRY_POINT_RENAME(uregex_appendReplacement) -#define uregex_appendReplacementUText U_ICU_ENTRY_POINT_RENAME(uregex_appendReplacementUText) -#define uregex_appendTail U_ICU_ENTRY_POINT_RENAME(uregex_appendTail) -#define uregex_appendTailUText U_ICU_ENTRY_POINT_RENAME(uregex_appendTailUText) -#define uregex_clone U_ICU_ENTRY_POINT_RENAME(uregex_clone) -#define uregex_close U_ICU_ENTRY_POINT_RENAME(uregex_close) -#define uregex_end U_ICU_ENTRY_POINT_RENAME(uregex_end) -#define uregex_end64 U_ICU_ENTRY_POINT_RENAME(uregex_end64) -#define uregex_find U_ICU_ENTRY_POINT_RENAME(uregex_find) -#define uregex_find64 U_ICU_ENTRY_POINT_RENAME(uregex_find64) -#define uregex_findNext U_ICU_ENTRY_POINT_RENAME(uregex_findNext) -#define uregex_flags U_ICU_ENTRY_POINT_RENAME(uregex_flags) -#define uregex_getFindProgressCallback U_ICU_ENTRY_POINT_RENAME(uregex_getFindProgressCallback) -#define uregex_getMatchCallback U_ICU_ENTRY_POINT_RENAME(uregex_getMatchCallback) -#define uregex_getStackLimit U_ICU_ENTRY_POINT_RENAME(uregex_getStackLimit) -#define uregex_getText U_ICU_ENTRY_POINT_RENAME(uregex_getText) -#define uregex_getTimeLimit U_ICU_ENTRY_POINT_RENAME(uregex_getTimeLimit) -#define uregex_getUText U_ICU_ENTRY_POINT_RENAME(uregex_getUText) -#define uregex_group U_ICU_ENTRY_POINT_RENAME(uregex_group) -#define uregex_groupCount U_ICU_ENTRY_POINT_RENAME(uregex_groupCount) -#define uregex_groupNumberFromCName U_ICU_ENTRY_POINT_RENAME(uregex_groupNumberFromCName) -#define uregex_groupNumberFromName U_ICU_ENTRY_POINT_RENAME(uregex_groupNumberFromName) -#define uregex_groupUText U_ICU_ENTRY_POINT_RENAME(uregex_groupUText) -#define uregex_hasAnchoringBounds U_ICU_ENTRY_POINT_RENAME(uregex_hasAnchoringBounds) -#define uregex_hasTransparentBounds U_ICU_ENTRY_POINT_RENAME(uregex_hasTransparentBounds) -#define uregex_hitEnd U_ICU_ENTRY_POINT_RENAME(uregex_hitEnd) -#define uregex_lookingAt U_ICU_ENTRY_POINT_RENAME(uregex_lookingAt) -#define uregex_lookingAt64 U_ICU_ENTRY_POINT_RENAME(uregex_lookingAt64) -#define uregex_matches U_ICU_ENTRY_POINT_RENAME(uregex_matches) -#define uregex_matches64 U_ICU_ENTRY_POINT_RENAME(uregex_matches64) -#define uregex_open U_ICU_ENTRY_POINT_RENAME(uregex_open) -#define uregex_openC U_ICU_ENTRY_POINT_RENAME(uregex_openC) -#define uregex_openUText U_ICU_ENTRY_POINT_RENAME(uregex_openUText) -#define uregex_pattern U_ICU_ENTRY_POINT_RENAME(uregex_pattern) -#define uregex_patternUText U_ICU_ENTRY_POINT_RENAME(uregex_patternUText) -#define uregex_refreshUText U_ICU_ENTRY_POINT_RENAME(uregex_refreshUText) -#define uregex_regionEnd U_ICU_ENTRY_POINT_RENAME(uregex_regionEnd) -#define uregex_regionEnd64 U_ICU_ENTRY_POINT_RENAME(uregex_regionEnd64) -#define uregex_regionStart U_ICU_ENTRY_POINT_RENAME(uregex_regionStart) -#define uregex_regionStart64 U_ICU_ENTRY_POINT_RENAME(uregex_regionStart64) -#define uregex_replaceAll U_ICU_ENTRY_POINT_RENAME(uregex_replaceAll) -#define uregex_replaceAllUText U_ICU_ENTRY_POINT_RENAME(uregex_replaceAllUText) -#define uregex_replaceFirst U_ICU_ENTRY_POINT_RENAME(uregex_replaceFirst) -#define uregex_replaceFirstUText U_ICU_ENTRY_POINT_RENAME(uregex_replaceFirstUText) -#define uregex_requireEnd U_ICU_ENTRY_POINT_RENAME(uregex_requireEnd) -#define uregex_reset U_ICU_ENTRY_POINT_RENAME(uregex_reset) -#define uregex_reset64 U_ICU_ENTRY_POINT_RENAME(uregex_reset64) -#define uregex_setFindProgressCallback U_ICU_ENTRY_POINT_RENAME(uregex_setFindProgressCallback) -#define uregex_setMatchCallback U_ICU_ENTRY_POINT_RENAME(uregex_setMatchCallback) -#define uregex_setRegion U_ICU_ENTRY_POINT_RENAME(uregex_setRegion) -#define uregex_setRegion64 U_ICU_ENTRY_POINT_RENAME(uregex_setRegion64) -#define uregex_setRegionAndStart U_ICU_ENTRY_POINT_RENAME(uregex_setRegionAndStart) -#define uregex_setStackLimit U_ICU_ENTRY_POINT_RENAME(uregex_setStackLimit) -#define uregex_setText U_ICU_ENTRY_POINT_RENAME(uregex_setText) -#define uregex_setTimeLimit U_ICU_ENTRY_POINT_RENAME(uregex_setTimeLimit) -#define uregex_setUText U_ICU_ENTRY_POINT_RENAME(uregex_setUText) -#define uregex_split U_ICU_ENTRY_POINT_RENAME(uregex_split) -#define uregex_splitUText U_ICU_ENTRY_POINT_RENAME(uregex_splitUText) -#define uregex_start U_ICU_ENTRY_POINT_RENAME(uregex_start) -#define uregex_start64 U_ICU_ENTRY_POINT_RENAME(uregex_start64) -#define uregex_ucstr_unescape_charAt U_ICU_ENTRY_POINT_RENAME(uregex_ucstr_unescape_charAt) -#define uregex_useAnchoringBounds U_ICU_ENTRY_POINT_RENAME(uregex_useAnchoringBounds) -#define uregex_useTransparentBounds U_ICU_ENTRY_POINT_RENAME(uregex_useTransparentBounds) -#define uregex_utext_unescape_charAt U_ICU_ENTRY_POINT_RENAME(uregex_utext_unescape_charAt) -#define uregion_areEqual U_ICU_ENTRY_POINT_RENAME(uregion_areEqual) -#define uregion_contains U_ICU_ENTRY_POINT_RENAME(uregion_contains) -#define uregion_getAvailable U_ICU_ENTRY_POINT_RENAME(uregion_getAvailable) -#define uregion_getContainedRegions U_ICU_ENTRY_POINT_RENAME(uregion_getContainedRegions) -#define uregion_getContainedRegionsOfType U_ICU_ENTRY_POINT_RENAME(uregion_getContainedRegionsOfType) -#define uregion_getContainingRegion U_ICU_ENTRY_POINT_RENAME(uregion_getContainingRegion) -#define uregion_getContainingRegionOfType U_ICU_ENTRY_POINT_RENAME(uregion_getContainingRegionOfType) -#define uregion_getNumericCode U_ICU_ENTRY_POINT_RENAME(uregion_getNumericCode) -#define uregion_getPreferredValues U_ICU_ENTRY_POINT_RENAME(uregion_getPreferredValues) -#define uregion_getRegionCode U_ICU_ENTRY_POINT_RENAME(uregion_getRegionCode) -#define uregion_getRegionFromCode U_ICU_ENTRY_POINT_RENAME(uregion_getRegionFromCode) -#define uregion_getRegionFromNumericCode U_ICU_ENTRY_POINT_RENAME(uregion_getRegionFromNumericCode) -#define uregion_getType U_ICU_ENTRY_POINT_RENAME(uregion_getType) -#define ureldatefmt_close U_ICU_ENTRY_POINT_RENAME(ureldatefmt_close) -#define ureldatefmt_closeResult U_ICU_ENTRY_POINT_RENAME(ureldatefmt_closeResult) -#define ureldatefmt_combineDateAndTime U_ICU_ENTRY_POINT_RENAME(ureldatefmt_combineDateAndTime) -#define ureldatefmt_format U_ICU_ENTRY_POINT_RENAME(ureldatefmt_format) -#define ureldatefmt_formatNumeric U_ICU_ENTRY_POINT_RENAME(ureldatefmt_formatNumeric) -#define ureldatefmt_formatNumericToResult U_ICU_ENTRY_POINT_RENAME(ureldatefmt_formatNumericToResult) -#define ureldatefmt_formatToResult U_ICU_ENTRY_POINT_RENAME(ureldatefmt_formatToResult) -#define ureldatefmt_open U_ICU_ENTRY_POINT_RENAME(ureldatefmt_open) -#define ureldatefmt_openResult U_ICU_ENTRY_POINT_RENAME(ureldatefmt_openResult) -#define ureldatefmt_resultAsValue U_ICU_ENTRY_POINT_RENAME(ureldatefmt_resultAsValue) -#define ures_close U_ICU_ENTRY_POINT_RENAME(ures_close) -#define ures_copyResb U_ICU_ENTRY_POINT_RENAME(ures_copyResb) -#define ures_countArrayItems U_ICU_ENTRY_POINT_RENAME(ures_countArrayItems) -#define ures_findResource U_ICU_ENTRY_POINT_RENAME(ures_findResource) -#define ures_findSubResource U_ICU_ENTRY_POINT_RENAME(ures_findSubResource) -#define ures_getAllChildrenWithFallback U_ICU_ENTRY_POINT_RENAME(ures_getAllChildrenWithFallback) -#define ures_getAllItemsWithFallback U_ICU_ENTRY_POINT_RENAME(ures_getAllItemsWithFallback) -#define ures_getBinary U_ICU_ENTRY_POINT_RENAME(ures_getBinary) -#define ures_getByIndex U_ICU_ENTRY_POINT_RENAME(ures_getByIndex) -#define ures_getByKey U_ICU_ENTRY_POINT_RENAME(ures_getByKey) -#define ures_getByKeyWithFallback U_ICU_ENTRY_POINT_RENAME(ures_getByKeyWithFallback) -#define ures_getFunctionalEquivalent U_ICU_ENTRY_POINT_RENAME(ures_getFunctionalEquivalent) -#define ures_getInt U_ICU_ENTRY_POINT_RENAME(ures_getInt) -#define ures_getIntVector U_ICU_ENTRY_POINT_RENAME(ures_getIntVector) -#define ures_getKey U_ICU_ENTRY_POINT_RENAME(ures_getKey) -#define ures_getKeywordValues U_ICU_ENTRY_POINT_RENAME(ures_getKeywordValues) -#define ures_getLocale U_ICU_ENTRY_POINT_RENAME(ures_getLocale) -#define ures_getLocaleByType U_ICU_ENTRY_POINT_RENAME(ures_getLocaleByType) -#define ures_getLocaleInternal U_ICU_ENTRY_POINT_RENAME(ures_getLocaleInternal) -#define ures_getName U_ICU_ENTRY_POINT_RENAME(ures_getName) -#define ures_getNextResource U_ICU_ENTRY_POINT_RENAME(ures_getNextResource) -#define ures_getNextString U_ICU_ENTRY_POINT_RENAME(ures_getNextString) -#define ures_getSize U_ICU_ENTRY_POINT_RENAME(ures_getSize) -#define ures_getString U_ICU_ENTRY_POINT_RENAME(ures_getString) -#define ures_getStringByIndex U_ICU_ENTRY_POINT_RENAME(ures_getStringByIndex) -#define ures_getStringByKey U_ICU_ENTRY_POINT_RENAME(ures_getStringByKey) -#define ures_getStringByKeyWithFallback U_ICU_ENTRY_POINT_RENAME(ures_getStringByKeyWithFallback) -#define ures_getType U_ICU_ENTRY_POINT_RENAME(ures_getType) -#define ures_getUInt U_ICU_ENTRY_POINT_RENAME(ures_getUInt) -#define ures_getUTF8String U_ICU_ENTRY_POINT_RENAME(ures_getUTF8String) -#define ures_getUTF8StringByIndex U_ICU_ENTRY_POINT_RENAME(ures_getUTF8StringByIndex) -#define ures_getUTF8StringByKey U_ICU_ENTRY_POINT_RENAME(ures_getUTF8StringByKey) -#define ures_getValueWithFallback U_ICU_ENTRY_POINT_RENAME(ures_getValueWithFallback) -#define ures_getVersion U_ICU_ENTRY_POINT_RENAME(ures_getVersion) -#define ures_getVersionByKey U_ICU_ENTRY_POINT_RENAME(ures_getVersionByKey) -#define ures_getVersionNumber U_ICU_ENTRY_POINT_RENAME(ures_getVersionNumber) -#define ures_getVersionNumberInternal U_ICU_ENTRY_POINT_RENAME(ures_getVersionNumberInternal) -#define ures_hasNext U_ICU_ENTRY_POINT_RENAME(ures_hasNext) -#define ures_initStackObject U_ICU_ENTRY_POINT_RENAME(ures_initStackObject) -#define ures_open U_ICU_ENTRY_POINT_RENAME(ures_open) -#define ures_openAvailableLocales U_ICU_ENTRY_POINT_RENAME(ures_openAvailableLocales) -#define ures_openDirect U_ICU_ENTRY_POINT_RENAME(ures_openDirect) -#define ures_openDirectFillIn U_ICU_ENTRY_POINT_RENAME(ures_openDirectFillIn) -#define ures_openFillIn U_ICU_ENTRY_POINT_RENAME(ures_openFillIn) -#define ures_openNoDefault U_ICU_ENTRY_POINT_RENAME(ures_openNoDefault) -#define ures_openU U_ICU_ENTRY_POINT_RENAME(ures_openU) -#define ures_resetIterator U_ICU_ENTRY_POINT_RENAME(ures_resetIterator) -#define ures_swap U_ICU_ENTRY_POINT_RENAME(ures_swap) -#define uscript_breaksBetweenLetters U_ICU_ENTRY_POINT_RENAME(uscript_breaksBetweenLetters) -#define uscript_closeRun U_ICU_ENTRY_POINT_RENAME(uscript_closeRun) -#define uscript_getCode U_ICU_ENTRY_POINT_RENAME(uscript_getCode) -#define uscript_getName U_ICU_ENTRY_POINT_RENAME(uscript_getName) -#define uscript_getSampleString U_ICU_ENTRY_POINT_RENAME(uscript_getSampleString) -#define uscript_getSampleUnicodeString U_ICU_ENTRY_POINT_RENAME(uscript_getSampleUnicodeString) -#define uscript_getScript U_ICU_ENTRY_POINT_RENAME(uscript_getScript) -#define uscript_getScriptExtensions U_ICU_ENTRY_POINT_RENAME(uscript_getScriptExtensions) -#define uscript_getShortName U_ICU_ENTRY_POINT_RENAME(uscript_getShortName) -#define uscript_getUsage U_ICU_ENTRY_POINT_RENAME(uscript_getUsage) -#define uscript_hasScript U_ICU_ENTRY_POINT_RENAME(uscript_hasScript) -#define uscript_isCased U_ICU_ENTRY_POINT_RENAME(uscript_isCased) -#define uscript_isRightToLeft U_ICU_ENTRY_POINT_RENAME(uscript_isRightToLeft) -#define uscript_nextRun U_ICU_ENTRY_POINT_RENAME(uscript_nextRun) -#define uscript_openRun U_ICU_ENTRY_POINT_RENAME(uscript_openRun) -#define uscript_resetRun U_ICU_ENTRY_POINT_RENAME(uscript_resetRun) -#define uscript_setRunText U_ICU_ENTRY_POINT_RENAME(uscript_setRunText) -#define usearch_close U_ICU_ENTRY_POINT_RENAME(usearch_close) -#define usearch_first U_ICU_ENTRY_POINT_RENAME(usearch_first) -#define usearch_following U_ICU_ENTRY_POINT_RENAME(usearch_following) -#define usearch_getAttribute U_ICU_ENTRY_POINT_RENAME(usearch_getAttribute) -#define usearch_getBreakIterator U_ICU_ENTRY_POINT_RENAME(usearch_getBreakIterator) -#define usearch_getCollator U_ICU_ENTRY_POINT_RENAME(usearch_getCollator) -#define usearch_getMatchedLength U_ICU_ENTRY_POINT_RENAME(usearch_getMatchedLength) -#define usearch_getMatchedStart U_ICU_ENTRY_POINT_RENAME(usearch_getMatchedStart) -#define usearch_getMatchedText U_ICU_ENTRY_POINT_RENAME(usearch_getMatchedText) -#define usearch_getOffset U_ICU_ENTRY_POINT_RENAME(usearch_getOffset) -#define usearch_getPattern U_ICU_ENTRY_POINT_RENAME(usearch_getPattern) -#define usearch_getText U_ICU_ENTRY_POINT_RENAME(usearch_getText) -#define usearch_handleNextCanonical U_ICU_ENTRY_POINT_RENAME(usearch_handleNextCanonical) -#define usearch_handleNextExact U_ICU_ENTRY_POINT_RENAME(usearch_handleNextExact) -#define usearch_handlePreviousCanonical U_ICU_ENTRY_POINT_RENAME(usearch_handlePreviousCanonical) -#define usearch_handlePreviousExact U_ICU_ENTRY_POINT_RENAME(usearch_handlePreviousExact) -#define usearch_last U_ICU_ENTRY_POINT_RENAME(usearch_last) -#define usearch_next U_ICU_ENTRY_POINT_RENAME(usearch_next) -#define usearch_open U_ICU_ENTRY_POINT_RENAME(usearch_open) -#define usearch_openFromCollator U_ICU_ENTRY_POINT_RENAME(usearch_openFromCollator) -#define usearch_preceding U_ICU_ENTRY_POINT_RENAME(usearch_preceding) -#define usearch_previous U_ICU_ENTRY_POINT_RENAME(usearch_previous) -#define usearch_reset U_ICU_ENTRY_POINT_RENAME(usearch_reset) -#define usearch_search U_ICU_ENTRY_POINT_RENAME(usearch_search) -#define usearch_searchBackwards U_ICU_ENTRY_POINT_RENAME(usearch_searchBackwards) -#define usearch_setAttribute U_ICU_ENTRY_POINT_RENAME(usearch_setAttribute) -#define usearch_setBreakIterator U_ICU_ENTRY_POINT_RENAME(usearch_setBreakIterator) -#define usearch_setCollator U_ICU_ENTRY_POINT_RENAME(usearch_setCollator) -#define usearch_setOffset U_ICU_ENTRY_POINT_RENAME(usearch_setOffset) -#define usearch_setPattern U_ICU_ENTRY_POINT_RENAME(usearch_setPattern) -#define usearch_setText U_ICU_ENTRY_POINT_RENAME(usearch_setText) -#define uset_add U_ICU_ENTRY_POINT_RENAME(uset_add) -#define uset_addAll U_ICU_ENTRY_POINT_RENAME(uset_addAll) -#define uset_addAllCodePoints U_ICU_ENTRY_POINT_RENAME(uset_addAllCodePoints) -#define uset_addRange U_ICU_ENTRY_POINT_RENAME(uset_addRange) -#define uset_addString U_ICU_ENTRY_POINT_RENAME(uset_addString) -#define uset_applyIntPropertyValue U_ICU_ENTRY_POINT_RENAME(uset_applyIntPropertyValue) -#define uset_applyPattern U_ICU_ENTRY_POINT_RENAME(uset_applyPattern) -#define uset_applyPropertyAlias U_ICU_ENTRY_POINT_RENAME(uset_applyPropertyAlias) -#define uset_charAt U_ICU_ENTRY_POINT_RENAME(uset_charAt) -#define uset_clear U_ICU_ENTRY_POINT_RENAME(uset_clear) -#define uset_clone U_ICU_ENTRY_POINT_RENAME(uset_clone) -#define uset_cloneAsThawed U_ICU_ENTRY_POINT_RENAME(uset_cloneAsThawed) -#define uset_close U_ICU_ENTRY_POINT_RENAME(uset_close) -#define uset_closeOver U_ICU_ENTRY_POINT_RENAME(uset_closeOver) -#define uset_compact U_ICU_ENTRY_POINT_RENAME(uset_compact) -#define uset_complement U_ICU_ENTRY_POINT_RENAME(uset_complement) -#define uset_complementAll U_ICU_ENTRY_POINT_RENAME(uset_complementAll) -#define uset_complementAllCodePoints U_ICU_ENTRY_POINT_RENAME(uset_complementAllCodePoints) -#define uset_complementRange U_ICU_ENTRY_POINT_RENAME(uset_complementRange) -#define uset_complementString U_ICU_ENTRY_POINT_RENAME(uset_complementString) -#define uset_contains U_ICU_ENTRY_POINT_RENAME(uset_contains) -#define uset_containsAll U_ICU_ENTRY_POINT_RENAME(uset_containsAll) -#define uset_containsAllCodePoints U_ICU_ENTRY_POINT_RENAME(uset_containsAllCodePoints) -#define uset_containsNone U_ICU_ENTRY_POINT_RENAME(uset_containsNone) -#define uset_containsRange U_ICU_ENTRY_POINT_RENAME(uset_containsRange) -#define uset_containsSome U_ICU_ENTRY_POINT_RENAME(uset_containsSome) -#define uset_containsString U_ICU_ENTRY_POINT_RENAME(uset_containsString) -#define uset_equals U_ICU_ENTRY_POINT_RENAME(uset_equals) -#define uset_freeze U_ICU_ENTRY_POINT_RENAME(uset_freeze) -#define uset_getItem U_ICU_ENTRY_POINT_RENAME(uset_getItem) -#define uset_getItemCount U_ICU_ENTRY_POINT_RENAME(uset_getItemCount) -#define uset_getRangeCount U_ICU_ENTRY_POINT_RENAME(uset_getRangeCount) -#define uset_getSerializedRange U_ICU_ENTRY_POINT_RENAME(uset_getSerializedRange) -#define uset_getSerializedRangeCount U_ICU_ENTRY_POINT_RENAME(uset_getSerializedRangeCount) -#define uset_getSerializedSet U_ICU_ENTRY_POINT_RENAME(uset_getSerializedSet) -#define uset_hasStrings U_ICU_ENTRY_POINT_RENAME(uset_hasStrings) -#define uset_indexOf U_ICU_ENTRY_POINT_RENAME(uset_indexOf) -#define uset_isEmpty U_ICU_ENTRY_POINT_RENAME(uset_isEmpty) -#define uset_isFrozen U_ICU_ENTRY_POINT_RENAME(uset_isFrozen) -#define uset_open U_ICU_ENTRY_POINT_RENAME(uset_open) -#define uset_openEmpty U_ICU_ENTRY_POINT_RENAME(uset_openEmpty) -#define uset_openPattern U_ICU_ENTRY_POINT_RENAME(uset_openPattern) -#define uset_openPatternOptions U_ICU_ENTRY_POINT_RENAME(uset_openPatternOptions) -#define uset_remove U_ICU_ENTRY_POINT_RENAME(uset_remove) -#define uset_removeAll U_ICU_ENTRY_POINT_RENAME(uset_removeAll) -#define uset_removeAllCodePoints U_ICU_ENTRY_POINT_RENAME(uset_removeAllCodePoints) -#define uset_removeAllStrings U_ICU_ENTRY_POINT_RENAME(uset_removeAllStrings) -#define uset_removeRange U_ICU_ENTRY_POINT_RENAME(uset_removeRange) -#define uset_removeString U_ICU_ENTRY_POINT_RENAME(uset_removeString) -#define uset_resemblesPattern U_ICU_ENTRY_POINT_RENAME(uset_resemblesPattern) -#define uset_retain U_ICU_ENTRY_POINT_RENAME(uset_retain) -#define uset_retainAll U_ICU_ENTRY_POINT_RENAME(uset_retainAll) -#define uset_retainAllCodePoints U_ICU_ENTRY_POINT_RENAME(uset_retainAllCodePoints) -#define uset_retainString U_ICU_ENTRY_POINT_RENAME(uset_retainString) -#define uset_serialize U_ICU_ENTRY_POINT_RENAME(uset_serialize) -#define uset_serializedContains U_ICU_ENTRY_POINT_RENAME(uset_serializedContains) -#define uset_set U_ICU_ENTRY_POINT_RENAME(uset_set) -#define uset_setSerializedToOne U_ICU_ENTRY_POINT_RENAME(uset_setSerializedToOne) -#define uset_size U_ICU_ENTRY_POINT_RENAME(uset_size) -#define uset_span U_ICU_ENTRY_POINT_RENAME(uset_span) -#define uset_spanBack U_ICU_ENTRY_POINT_RENAME(uset_spanBack) -#define uset_spanBackUTF8 U_ICU_ENTRY_POINT_RENAME(uset_spanBackUTF8) -#define uset_spanUTF8 U_ICU_ENTRY_POINT_RENAME(uset_spanUTF8) -#define uset_toPattern U_ICU_ENTRY_POINT_RENAME(uset_toPattern) -#define uspoof_areConfusable U_ICU_ENTRY_POINT_RENAME(uspoof_areConfusable) -#define uspoof_areConfusableUTF8 U_ICU_ENTRY_POINT_RENAME(uspoof_areConfusableUTF8) -#define uspoof_areConfusableUnicodeString U_ICU_ENTRY_POINT_RENAME(uspoof_areConfusableUnicodeString) -#define uspoof_check U_ICU_ENTRY_POINT_RENAME(uspoof_check) -#define uspoof_check2 U_ICU_ENTRY_POINT_RENAME(uspoof_check2) -#define uspoof_check2UTF8 U_ICU_ENTRY_POINT_RENAME(uspoof_check2UTF8) -#define uspoof_check2UnicodeString U_ICU_ENTRY_POINT_RENAME(uspoof_check2UnicodeString) -#define uspoof_checkUTF8 U_ICU_ENTRY_POINT_RENAME(uspoof_checkUTF8) -#define uspoof_checkUnicodeString U_ICU_ENTRY_POINT_RENAME(uspoof_checkUnicodeString) -#define uspoof_clone U_ICU_ENTRY_POINT_RENAME(uspoof_clone) -#define uspoof_close U_ICU_ENTRY_POINT_RENAME(uspoof_close) -#define uspoof_closeCheckResult U_ICU_ENTRY_POINT_RENAME(uspoof_closeCheckResult) -#define uspoof_getAllowedChars U_ICU_ENTRY_POINT_RENAME(uspoof_getAllowedChars) -#define uspoof_getAllowedLocales U_ICU_ENTRY_POINT_RENAME(uspoof_getAllowedLocales) -#define uspoof_getAllowedUnicodeSet U_ICU_ENTRY_POINT_RENAME(uspoof_getAllowedUnicodeSet) -#define uspoof_getCheckResultChecks U_ICU_ENTRY_POINT_RENAME(uspoof_getCheckResultChecks) -#define uspoof_getCheckResultNumerics U_ICU_ENTRY_POINT_RENAME(uspoof_getCheckResultNumerics) -#define uspoof_getCheckResultRestrictionLevel U_ICU_ENTRY_POINT_RENAME(uspoof_getCheckResultRestrictionLevel) -#define uspoof_getChecks U_ICU_ENTRY_POINT_RENAME(uspoof_getChecks) -#define uspoof_getInclusionSet U_ICU_ENTRY_POINT_RENAME(uspoof_getInclusionSet) -#define uspoof_getInclusionUnicodeSet U_ICU_ENTRY_POINT_RENAME(uspoof_getInclusionUnicodeSet) -#define uspoof_getRecommendedSet U_ICU_ENTRY_POINT_RENAME(uspoof_getRecommendedSet) -#define uspoof_getRecommendedUnicodeSet U_ICU_ENTRY_POINT_RENAME(uspoof_getRecommendedUnicodeSet) -#define uspoof_getRestrictionLevel U_ICU_ENTRY_POINT_RENAME(uspoof_getRestrictionLevel) -#define uspoof_getSkeleton U_ICU_ENTRY_POINT_RENAME(uspoof_getSkeleton) -#define uspoof_getSkeletonUTF8 U_ICU_ENTRY_POINT_RENAME(uspoof_getSkeletonUTF8) -#define uspoof_getSkeletonUnicodeString U_ICU_ENTRY_POINT_RENAME(uspoof_getSkeletonUnicodeString) -#define uspoof_internalInitStatics U_ICU_ENTRY_POINT_RENAME(uspoof_internalInitStatics) -#define uspoof_open U_ICU_ENTRY_POINT_RENAME(uspoof_open) -#define uspoof_openCheckResult U_ICU_ENTRY_POINT_RENAME(uspoof_openCheckResult) -#define uspoof_openFromSerialized U_ICU_ENTRY_POINT_RENAME(uspoof_openFromSerialized) -#define uspoof_openFromSource U_ICU_ENTRY_POINT_RENAME(uspoof_openFromSource) -#define uspoof_serialize U_ICU_ENTRY_POINT_RENAME(uspoof_serialize) -#define uspoof_setAllowedChars U_ICU_ENTRY_POINT_RENAME(uspoof_setAllowedChars) -#define uspoof_setAllowedLocales U_ICU_ENTRY_POINT_RENAME(uspoof_setAllowedLocales) -#define uspoof_setAllowedUnicodeSet U_ICU_ENTRY_POINT_RENAME(uspoof_setAllowedUnicodeSet) -#define uspoof_setChecks U_ICU_ENTRY_POINT_RENAME(uspoof_setChecks) -#define uspoof_setRestrictionLevel U_ICU_ENTRY_POINT_RENAME(uspoof_setRestrictionLevel) -#define uspoof_swap U_ICU_ENTRY_POINT_RENAME(uspoof_swap) -#define usprep_close U_ICU_ENTRY_POINT_RENAME(usprep_close) -#define usprep_open U_ICU_ENTRY_POINT_RENAME(usprep_open) -#define usprep_openByType U_ICU_ENTRY_POINT_RENAME(usprep_openByType) -#define usprep_prepare U_ICU_ENTRY_POINT_RENAME(usprep_prepare) -#define usprep_swap U_ICU_ENTRY_POINT_RENAME(usprep_swap) -#define ustr_hashCharsN U_ICU_ENTRY_POINT_RENAME(ustr_hashCharsN) -#define ustr_hashICharsN U_ICU_ENTRY_POINT_RENAME(ustr_hashICharsN) -#define ustr_hashUCharsN U_ICU_ENTRY_POINT_RENAME(ustr_hashUCharsN) -#define ustrcase_getCaseLocale U_ICU_ENTRY_POINT_RENAME(ustrcase_getCaseLocale) -#define ustrcase_getTitleBreakIterator U_ICU_ENTRY_POINT_RENAME(ustrcase_getTitleBreakIterator) -#define ustrcase_internalFold U_ICU_ENTRY_POINT_RENAME(ustrcase_internalFold) -#define ustrcase_internalToLower U_ICU_ENTRY_POINT_RENAME(ustrcase_internalToLower) -#define ustrcase_internalToTitle U_ICU_ENTRY_POINT_RENAME(ustrcase_internalToTitle) -#define ustrcase_internalToUpper U_ICU_ENTRY_POINT_RENAME(ustrcase_internalToUpper) -#define ustrcase_map U_ICU_ENTRY_POINT_RENAME(ustrcase_map) -#define ustrcase_mapWithOverlap U_ICU_ENTRY_POINT_RENAME(ustrcase_mapWithOverlap) -#define utext_char32At U_ICU_ENTRY_POINT_RENAME(utext_char32At) -#define utext_clone U_ICU_ENTRY_POINT_RENAME(utext_clone) -#define utext_close U_ICU_ENTRY_POINT_RENAME(utext_close) -#define utext_copy U_ICU_ENTRY_POINT_RENAME(utext_copy) -#define utext_current32 U_ICU_ENTRY_POINT_RENAME(utext_current32) -#define utext_equals U_ICU_ENTRY_POINT_RENAME(utext_equals) -#define utext_extract U_ICU_ENTRY_POINT_RENAME(utext_extract) -#define utext_freeze U_ICU_ENTRY_POINT_RENAME(utext_freeze) -#define utext_getNativeIndex U_ICU_ENTRY_POINT_RENAME(utext_getNativeIndex) -#define utext_getPreviousNativeIndex U_ICU_ENTRY_POINT_RENAME(utext_getPreviousNativeIndex) -#define utext_hasMetaData U_ICU_ENTRY_POINT_RENAME(utext_hasMetaData) -#define utext_isLengthExpensive U_ICU_ENTRY_POINT_RENAME(utext_isLengthExpensive) -#define utext_isWritable U_ICU_ENTRY_POINT_RENAME(utext_isWritable) -#define utext_moveIndex32 U_ICU_ENTRY_POINT_RENAME(utext_moveIndex32) -#define utext_nativeLength U_ICU_ENTRY_POINT_RENAME(utext_nativeLength) -#define utext_next32 U_ICU_ENTRY_POINT_RENAME(utext_next32) -#define utext_next32From U_ICU_ENTRY_POINT_RENAME(utext_next32From) -#define utext_openCharacterIterator U_ICU_ENTRY_POINT_RENAME(utext_openCharacterIterator) -#define utext_openConstUnicodeString U_ICU_ENTRY_POINT_RENAME(utext_openConstUnicodeString) -#define utext_openReplaceable U_ICU_ENTRY_POINT_RENAME(utext_openReplaceable) -#define utext_openUChars U_ICU_ENTRY_POINT_RENAME(utext_openUChars) -#define utext_openUTF8 U_ICU_ENTRY_POINT_RENAME(utext_openUTF8) -#define utext_openUnicodeString U_ICU_ENTRY_POINT_RENAME(utext_openUnicodeString) -#define utext_previous32 U_ICU_ENTRY_POINT_RENAME(utext_previous32) -#define utext_previous32From U_ICU_ENTRY_POINT_RENAME(utext_previous32From) -#define utext_replace U_ICU_ENTRY_POINT_RENAME(utext_replace) -#define utext_setNativeIndex U_ICU_ENTRY_POINT_RENAME(utext_setNativeIndex) -#define utext_setup U_ICU_ENTRY_POINT_RENAME(utext_setup) -#define utf8_appendCharSafeBody U_ICU_ENTRY_POINT_RENAME(utf8_appendCharSafeBody) -#define utf8_back1SafeBody U_ICU_ENTRY_POINT_RENAME(utf8_back1SafeBody) -#define utf8_countTrailBytes U_ICU_ENTRY_POINT_RENAME(utf8_countTrailBytes) -#define utf8_nextCharSafeBody U_ICU_ENTRY_POINT_RENAME(utf8_nextCharSafeBody) -#define utf8_prevCharSafeBody U_ICU_ENTRY_POINT_RENAME(utf8_prevCharSafeBody) -#define utmscale_fromInt64 U_ICU_ENTRY_POINT_RENAME(utmscale_fromInt64) -#define utmscale_getTimeScaleValue U_ICU_ENTRY_POINT_RENAME(utmscale_getTimeScaleValue) -#define utmscale_toInt64 U_ICU_ENTRY_POINT_RENAME(utmscale_toInt64) -#define utrace_cleanup U_ICU_ENTRY_POINT_RENAME(utrace_cleanup) -#define utrace_data U_ICU_ENTRY_POINT_RENAME(utrace_data) -#define utrace_entry U_ICU_ENTRY_POINT_RENAME(utrace_entry) -#define utrace_exit U_ICU_ENTRY_POINT_RENAME(utrace_exit) -#define utrace_format U_ICU_ENTRY_POINT_RENAME(utrace_format) -#define utrace_functionName U_ICU_ENTRY_POINT_RENAME(utrace_functionName) -#define utrace_getFunctions U_ICU_ENTRY_POINT_RENAME(utrace_getFunctions) -#define utrace_getLevel U_ICU_ENTRY_POINT_RENAME(utrace_getLevel) -#define utrace_setFunctions U_ICU_ENTRY_POINT_RENAME(utrace_setFunctions) -#define utrace_setLevel U_ICU_ENTRY_POINT_RENAME(utrace_setLevel) -#define utrace_vformat U_ICU_ENTRY_POINT_RENAME(utrace_vformat) -#define utrans_clone U_ICU_ENTRY_POINT_RENAME(utrans_clone) -#define utrans_close U_ICU_ENTRY_POINT_RENAME(utrans_close) -#define utrans_countAvailableIDs U_ICU_ENTRY_POINT_RENAME(utrans_countAvailableIDs) -#define utrans_getAvailableID U_ICU_ENTRY_POINT_RENAME(utrans_getAvailableID) -#define utrans_getID U_ICU_ENTRY_POINT_RENAME(utrans_getID) -#define utrans_getSourceSet U_ICU_ENTRY_POINT_RENAME(utrans_getSourceSet) -#define utrans_getUnicodeID U_ICU_ENTRY_POINT_RENAME(utrans_getUnicodeID) -#define utrans_open U_ICU_ENTRY_POINT_RENAME(utrans_open) -#define utrans_openIDs U_ICU_ENTRY_POINT_RENAME(utrans_openIDs) -#define utrans_openInverse U_ICU_ENTRY_POINT_RENAME(utrans_openInverse) -#define utrans_openU U_ICU_ENTRY_POINT_RENAME(utrans_openU) -#define utrans_register U_ICU_ENTRY_POINT_RENAME(utrans_register) -#define utrans_rep_caseContextIterator U_ICU_ENTRY_POINT_RENAME(utrans_rep_caseContextIterator) -#define utrans_setFilter U_ICU_ENTRY_POINT_RENAME(utrans_setFilter) -#define utrans_stripRules U_ICU_ENTRY_POINT_RENAME(utrans_stripRules) -#define utrans_toRules U_ICU_ENTRY_POINT_RENAME(utrans_toRules) -#define utrans_trans U_ICU_ENTRY_POINT_RENAME(utrans_trans) -#define utrans_transIncremental U_ICU_ENTRY_POINT_RENAME(utrans_transIncremental) -#define utrans_transIncrementalUChars U_ICU_ENTRY_POINT_RENAME(utrans_transIncrementalUChars) -#define utrans_transUChars U_ICU_ENTRY_POINT_RENAME(utrans_transUChars) -#define utrans_transliterator_cleanup U_ICU_ENTRY_POINT_RENAME(utrans_transliterator_cleanup) -#define utrans_unregister U_ICU_ENTRY_POINT_RENAME(utrans_unregister) -#define utrans_unregisterID U_ICU_ENTRY_POINT_RENAME(utrans_unregisterID) -#define utrie2_clone U_ICU_ENTRY_POINT_RENAME(utrie2_clone) -#define utrie2_cloneAsThawed U_ICU_ENTRY_POINT_RENAME(utrie2_cloneAsThawed) -#define utrie2_close U_ICU_ENTRY_POINT_RENAME(utrie2_close) -#define utrie2_enum U_ICU_ENTRY_POINT_RENAME(utrie2_enum) -#define utrie2_enumForLeadSurrogate U_ICU_ENTRY_POINT_RENAME(utrie2_enumForLeadSurrogate) -#define utrie2_freeze U_ICU_ENTRY_POINT_RENAME(utrie2_freeze) -#define utrie2_fromUTrie U_ICU_ENTRY_POINT_RENAME(utrie2_fromUTrie) -#define utrie2_get32 U_ICU_ENTRY_POINT_RENAME(utrie2_get32) -#define utrie2_get32FromLeadSurrogateCodeUnit U_ICU_ENTRY_POINT_RENAME(utrie2_get32FromLeadSurrogateCodeUnit) -#define utrie2_internalU8NextIndex U_ICU_ENTRY_POINT_RENAME(utrie2_internalU8NextIndex) -#define utrie2_internalU8PrevIndex U_ICU_ENTRY_POINT_RENAME(utrie2_internalU8PrevIndex) -#define utrie2_isFrozen U_ICU_ENTRY_POINT_RENAME(utrie2_isFrozen) -#define utrie2_open U_ICU_ENTRY_POINT_RENAME(utrie2_open) -#define utrie2_openDummy U_ICU_ENTRY_POINT_RENAME(utrie2_openDummy) -#define utrie2_openFromSerialized U_ICU_ENTRY_POINT_RENAME(utrie2_openFromSerialized) -#define utrie2_serialize U_ICU_ENTRY_POINT_RENAME(utrie2_serialize) -#define utrie2_set32 U_ICU_ENTRY_POINT_RENAME(utrie2_set32) -#define utrie2_set32ForLeadSurrogateCodeUnit U_ICU_ENTRY_POINT_RENAME(utrie2_set32ForLeadSurrogateCodeUnit) -#define utrie2_setRange32 U_ICU_ENTRY_POINT_RENAME(utrie2_setRange32) -#define utrie2_swap U_ICU_ENTRY_POINT_RENAME(utrie2_swap) -#define utrie_clone U_ICU_ENTRY_POINT_RENAME(utrie_clone) -#define utrie_close U_ICU_ENTRY_POINT_RENAME(utrie_close) -#define utrie_defaultGetFoldingOffset U_ICU_ENTRY_POINT_RENAME(utrie_defaultGetFoldingOffset) -#define utrie_enum U_ICU_ENTRY_POINT_RENAME(utrie_enum) -#define utrie_get32 U_ICU_ENTRY_POINT_RENAME(utrie_get32) -#define utrie_getData U_ICU_ENTRY_POINT_RENAME(utrie_getData) -#define utrie_open U_ICU_ENTRY_POINT_RENAME(utrie_open) -#define utrie_serialize U_ICU_ENTRY_POINT_RENAME(utrie_serialize) -#define utrie_set32 U_ICU_ENTRY_POINT_RENAME(utrie_set32) -#define utrie_setRange32 U_ICU_ENTRY_POINT_RENAME(utrie_setRange32) -#define utrie_swap U_ICU_ENTRY_POINT_RENAME(utrie_swap) -#define utrie_swapAnyVersion U_ICU_ENTRY_POINT_RENAME(utrie_swapAnyVersion) -#define utrie_unserialize U_ICU_ENTRY_POINT_RENAME(utrie_unserialize) -#define utrie_unserializeDummy U_ICU_ENTRY_POINT_RENAME(utrie_unserializeDummy) -#define vzone_clone U_ICU_ENTRY_POINT_RENAME(vzone_clone) -#define vzone_close U_ICU_ENTRY_POINT_RENAME(vzone_close) -#define vzone_countTransitionRules U_ICU_ENTRY_POINT_RENAME(vzone_countTransitionRules) -#define vzone_equals U_ICU_ENTRY_POINT_RENAME(vzone_equals) -#define vzone_getDynamicClassID U_ICU_ENTRY_POINT_RENAME(vzone_getDynamicClassID) -#define vzone_getLastModified U_ICU_ENTRY_POINT_RENAME(vzone_getLastModified) -#define vzone_getNextTransition U_ICU_ENTRY_POINT_RENAME(vzone_getNextTransition) -#define vzone_getOffset U_ICU_ENTRY_POINT_RENAME(vzone_getOffset) -#define vzone_getOffset2 U_ICU_ENTRY_POINT_RENAME(vzone_getOffset2) -#define vzone_getOffset3 U_ICU_ENTRY_POINT_RENAME(vzone_getOffset3) -#define vzone_getPreviousTransition U_ICU_ENTRY_POINT_RENAME(vzone_getPreviousTransition) -#define vzone_getRawOffset U_ICU_ENTRY_POINT_RENAME(vzone_getRawOffset) -#define vzone_getStaticClassID U_ICU_ENTRY_POINT_RENAME(vzone_getStaticClassID) -#define vzone_getTZURL U_ICU_ENTRY_POINT_RENAME(vzone_getTZURL) -#define vzone_hasSameRules U_ICU_ENTRY_POINT_RENAME(vzone_hasSameRules) -#define vzone_inDaylightTime U_ICU_ENTRY_POINT_RENAME(vzone_inDaylightTime) -#define vzone_openData U_ICU_ENTRY_POINT_RENAME(vzone_openData) -#define vzone_openID U_ICU_ENTRY_POINT_RENAME(vzone_openID) -#define vzone_setLastModified U_ICU_ENTRY_POINT_RENAME(vzone_setLastModified) -#define vzone_setRawOffset U_ICU_ENTRY_POINT_RENAME(vzone_setRawOffset) -#define vzone_setTZURL U_ICU_ENTRY_POINT_RENAME(vzone_setTZURL) -#define vzone_useDaylightTime U_ICU_ENTRY_POINT_RENAME(vzone_useDaylightTime) -#define vzone_write U_ICU_ENTRY_POINT_RENAME(vzone_write) -#define vzone_writeFromStart U_ICU_ENTRY_POINT_RENAME(vzone_writeFromStart) -#define vzone_writeSimple U_ICU_ENTRY_POINT_RENAME(vzone_writeSimple) -#define zrule_close U_ICU_ENTRY_POINT_RENAME(zrule_close) -#define zrule_equals U_ICU_ENTRY_POINT_RENAME(zrule_equals) -#define zrule_getDSTSavings U_ICU_ENTRY_POINT_RENAME(zrule_getDSTSavings) -#define zrule_getName U_ICU_ENTRY_POINT_RENAME(zrule_getName) -#define zrule_getRawOffset U_ICU_ENTRY_POINT_RENAME(zrule_getRawOffset) -#define zrule_isEquivalentTo U_ICU_ENTRY_POINT_RENAME(zrule_isEquivalentTo) -#define ztrans_adoptFrom U_ICU_ENTRY_POINT_RENAME(ztrans_adoptFrom) -#define ztrans_adoptTo U_ICU_ENTRY_POINT_RENAME(ztrans_adoptTo) -#define ztrans_clone U_ICU_ENTRY_POINT_RENAME(ztrans_clone) -#define ztrans_close U_ICU_ENTRY_POINT_RENAME(ztrans_close) -#define ztrans_equals U_ICU_ENTRY_POINT_RENAME(ztrans_equals) -#define ztrans_getDynamicClassID U_ICU_ENTRY_POINT_RENAME(ztrans_getDynamicClassID) -#define ztrans_getFrom U_ICU_ENTRY_POINT_RENAME(ztrans_getFrom) -#define ztrans_getStaticClassID U_ICU_ENTRY_POINT_RENAME(ztrans_getStaticClassID) -#define ztrans_getTime U_ICU_ENTRY_POINT_RENAME(ztrans_getTime) -#define ztrans_getTo U_ICU_ENTRY_POINT_RENAME(ztrans_getTo) -#define ztrans_open U_ICU_ENTRY_POINT_RENAME(ztrans_open) -#define ztrans_openEmpty U_ICU_ENTRY_POINT_RENAME(ztrans_openEmpty) -#define ztrans_setFrom U_ICU_ENTRY_POINT_RENAME(ztrans_setFrom) -#define ztrans_setTime U_ICU_ENTRY_POINT_RENAME(ztrans_setTime) -#define ztrans_setTo U_ICU_ENTRY_POINT_RENAME(ztrans_setTo) - -#endif /* !(defined(_MSC_VER) && defined(__INTELLISENSE__)) */ -#endif /* U_DISABLE_RENAMING */ -#endif /* URENAME_H */ - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2002-2016, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* +* file name: urename.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* Created by: Perl script tools/genren.pl written by Vladimir Weinstein +* +* Contains data for renaming ICU exports. +* Gets included by umachine.h +* +* THIS FILE IS MACHINE-GENERATED, DON'T PLAY WITH IT IF YOU DON'T KNOW WHAT +* YOU ARE DOING, OTHERWISE VERY BAD THINGS WILL HAPPEN! +*/ + +#ifndef URENAME_H +#define URENAME_H + +/* U_DISABLE_RENAMING can be defined in the following ways: + * - when running configure, e.g. + * runConfigureICU Linux --disable-renaming + * - by changing the default setting of U_DISABLE_RENAMING in uconfig.h + */ + +#include "unicode/uconfig.h" + +#if !U_DISABLE_RENAMING + +// Disable Renaming for Visual Studio's IntelliSense feature, so that 'Go-to-Definition' (F12) will work. +#if !(defined(_MSC_VER) && defined(__INTELLISENSE__)) + +/* We need the U_ICU_ENTRY_POINT_RENAME definition. There's a default one in unicode/uvernum.h we can use, but we will give + the platform a chance to define it first. + Normally (if utypes.h or umachine.h was included first) this will not be necessary as it will already be defined. + */ + +#ifndef U_ICU_ENTRY_POINT_RENAME +#include "unicode/umachine.h" +#endif + +/* If we still don't have U_ICU_ENTRY_POINT_RENAME use the default. */ +#ifndef U_ICU_ENTRY_POINT_RENAME +#include "unicode/uvernum.h" +#endif + +/* Error out before the following defines cause very strange and unexpected code breakage */ +#ifndef U_ICU_ENTRY_POINT_RENAME +#error U_ICU_ENTRY_POINT_RENAME is not defined - cannot continue. Consider defining U_DISABLE_RENAMING if renaming should not be used. +#endif + + +/* C exports renaming data */ + +#define CreateLSTMBreakEngine U_ICU_ENTRY_POINT_RENAME(CreateLSTMBreakEngine) +#define CreateLSTMData U_ICU_ENTRY_POINT_RENAME(CreateLSTMData) +#define CreateLSTMDataForScript U_ICU_ENTRY_POINT_RENAME(CreateLSTMDataForScript) +#define DeleteLSTMData U_ICU_ENTRY_POINT_RENAME(DeleteLSTMData) +#define LSTMDataName U_ICU_ENTRY_POINT_RENAME(LSTMDataName) +#define T_CString_int64ToString U_ICU_ENTRY_POINT_RENAME(T_CString_int64ToString) +#define T_CString_integerToString U_ICU_ENTRY_POINT_RENAME(T_CString_integerToString) +#define T_CString_stringToInteger U_ICU_ENTRY_POINT_RENAME(T_CString_stringToInteger) +#define T_CString_toLowerCase U_ICU_ENTRY_POINT_RENAME(T_CString_toLowerCase) +#define T_CString_toUpperCase U_ICU_ENTRY_POINT_RENAME(T_CString_toUpperCase) +#define UCNV_FROM_U_CALLBACK_ESCAPE U_ICU_ENTRY_POINT_RENAME(UCNV_FROM_U_CALLBACK_ESCAPE) +#define UCNV_FROM_U_CALLBACK_SKIP U_ICU_ENTRY_POINT_RENAME(UCNV_FROM_U_CALLBACK_SKIP) +#define UCNV_FROM_U_CALLBACK_STOP U_ICU_ENTRY_POINT_RENAME(UCNV_FROM_U_CALLBACK_STOP) +#define UCNV_FROM_U_CALLBACK_SUBSTITUTE U_ICU_ENTRY_POINT_RENAME(UCNV_FROM_U_CALLBACK_SUBSTITUTE) +#define UCNV_TO_U_CALLBACK_ESCAPE U_ICU_ENTRY_POINT_RENAME(UCNV_TO_U_CALLBACK_ESCAPE) +#define UCNV_TO_U_CALLBACK_SKIP U_ICU_ENTRY_POINT_RENAME(UCNV_TO_U_CALLBACK_SKIP) +#define UCNV_TO_U_CALLBACK_STOP U_ICU_ENTRY_POINT_RENAME(UCNV_TO_U_CALLBACK_STOP) +#define UCNV_TO_U_CALLBACK_SUBSTITUTE U_ICU_ENTRY_POINT_RENAME(UCNV_TO_U_CALLBACK_SUBSTITUTE) +#define UDataMemory_createNewInstance U_ICU_ENTRY_POINT_RENAME(UDataMemory_createNewInstance) +#define UDataMemory_init U_ICU_ENTRY_POINT_RENAME(UDataMemory_init) +#define UDataMemory_isLoaded U_ICU_ENTRY_POINT_RENAME(UDataMemory_isLoaded) +#define UDataMemory_normalizeDataPointer U_ICU_ENTRY_POINT_RENAME(UDataMemory_normalizeDataPointer) +#define UDataMemory_setData U_ICU_ENTRY_POINT_RENAME(UDataMemory_setData) +#define UDatamemory_assign U_ICU_ENTRY_POINT_RENAME(UDatamemory_assign) +#define _ASCIIData U_ICU_ENTRY_POINT_RENAME(_ASCIIData) +#define _Bocu1Data U_ICU_ENTRY_POINT_RENAME(_Bocu1Data) +#define _CESU8Data U_ICU_ENTRY_POINT_RENAME(_CESU8Data) +#define _CompoundTextData U_ICU_ENTRY_POINT_RENAME(_CompoundTextData) +#define _HZData U_ICU_ENTRY_POINT_RENAME(_HZData) +#define _IMAPData U_ICU_ENTRY_POINT_RENAME(_IMAPData) +#define _ISCIIData U_ICU_ENTRY_POINT_RENAME(_ISCIIData) +#define _ISO2022Data U_ICU_ENTRY_POINT_RENAME(_ISO2022Data) +#define _LMBCSData1 U_ICU_ENTRY_POINT_RENAME(_LMBCSData1) +#define _LMBCSData11 U_ICU_ENTRY_POINT_RENAME(_LMBCSData11) +#define _LMBCSData16 U_ICU_ENTRY_POINT_RENAME(_LMBCSData16) +#define _LMBCSData17 U_ICU_ENTRY_POINT_RENAME(_LMBCSData17) +#define _LMBCSData18 U_ICU_ENTRY_POINT_RENAME(_LMBCSData18) +#define _LMBCSData19 U_ICU_ENTRY_POINT_RENAME(_LMBCSData19) +#define _LMBCSData2 U_ICU_ENTRY_POINT_RENAME(_LMBCSData2) +#define _LMBCSData3 U_ICU_ENTRY_POINT_RENAME(_LMBCSData3) +#define _LMBCSData4 U_ICU_ENTRY_POINT_RENAME(_LMBCSData4) +#define _LMBCSData5 U_ICU_ENTRY_POINT_RENAME(_LMBCSData5) +#define _LMBCSData6 U_ICU_ENTRY_POINT_RENAME(_LMBCSData6) +#define _LMBCSData8 U_ICU_ENTRY_POINT_RENAME(_LMBCSData8) +#define _Latin1Data U_ICU_ENTRY_POINT_RENAME(_Latin1Data) +#define _MBCSData U_ICU_ENTRY_POINT_RENAME(_MBCSData) +#define _SCSUData U_ICU_ENTRY_POINT_RENAME(_SCSUData) +#define _UTF16BEData U_ICU_ENTRY_POINT_RENAME(_UTF16BEData) +#define _UTF16Data U_ICU_ENTRY_POINT_RENAME(_UTF16Data) +#define _UTF16LEData U_ICU_ENTRY_POINT_RENAME(_UTF16LEData) +#define _UTF16v2Data U_ICU_ENTRY_POINT_RENAME(_UTF16v2Data) +#define _UTF32BEData U_ICU_ENTRY_POINT_RENAME(_UTF32BEData) +#define _UTF32Data U_ICU_ENTRY_POINT_RENAME(_UTF32Data) +#define _UTF32LEData U_ICU_ENTRY_POINT_RENAME(_UTF32LEData) +#define _UTF7Data U_ICU_ENTRY_POINT_RENAME(_UTF7Data) +#define _UTF8Data U_ICU_ENTRY_POINT_RENAME(_UTF8Data) +#define _isUnicodeLocaleTypeSubtag U_ICU_ENTRY_POINT_RENAME(_isUnicodeLocaleTypeSubtag) +#define allowedHourFormatsCleanup U_ICU_ENTRY_POINT_RENAME(allowedHourFormatsCleanup) +#define cmemory_cleanup U_ICU_ENTRY_POINT_RENAME(cmemory_cleanup) +#define dayPeriodRulesCleanup U_ICU_ENTRY_POINT_RENAME(dayPeriodRulesCleanup) +#define deleteAllowedHourFormats U_ICU_ENTRY_POINT_RENAME(deleteAllowedHourFormats) +#define gTimeZoneFilesInitOnce U_ICU_ENTRY_POINT_RENAME(gTimeZoneFilesInitOnce) +#define initNumsysNames U_ICU_ENTRY_POINT_RENAME(initNumsysNames) +#define izrule_clone U_ICU_ENTRY_POINT_RENAME(izrule_clone) +#define izrule_close U_ICU_ENTRY_POINT_RENAME(izrule_close) +#define izrule_equals U_ICU_ENTRY_POINT_RENAME(izrule_equals) +#define izrule_getDSTSavings U_ICU_ENTRY_POINT_RENAME(izrule_getDSTSavings) +#define izrule_getDynamicClassID U_ICU_ENTRY_POINT_RENAME(izrule_getDynamicClassID) +#define izrule_getFinalStart U_ICU_ENTRY_POINT_RENAME(izrule_getFinalStart) +#define izrule_getFirstStart U_ICU_ENTRY_POINT_RENAME(izrule_getFirstStart) +#define izrule_getName U_ICU_ENTRY_POINT_RENAME(izrule_getName) +#define izrule_getNextStart U_ICU_ENTRY_POINT_RENAME(izrule_getNextStart) +#define izrule_getPreviousStart U_ICU_ENTRY_POINT_RENAME(izrule_getPreviousStart) +#define izrule_getRawOffset U_ICU_ENTRY_POINT_RENAME(izrule_getRawOffset) +#define izrule_getStaticClassID U_ICU_ENTRY_POINT_RENAME(izrule_getStaticClassID) +#define izrule_isEquivalentTo U_ICU_ENTRY_POINT_RENAME(izrule_isEquivalentTo) +#define izrule_open U_ICU_ENTRY_POINT_RENAME(izrule_open) +#define locale_getKeywordsStart U_ICU_ENTRY_POINT_RENAME(locale_getKeywordsStart) +#define locale_get_default U_ICU_ENTRY_POINT_RENAME(locale_get_default) +#define locale_set_default U_ICU_ENTRY_POINT_RENAME(locale_set_default) +#define numSysCleanup U_ICU_ENTRY_POINT_RENAME(numSysCleanup) +#define rbbi_cleanup U_ICU_ENTRY_POINT_RENAME(rbbi_cleanup) +#define pl_addFontRun U_ICU_ENTRY_POINT_RENAME(pl_addFontRun) +#define pl_addLocaleRun U_ICU_ENTRY_POINT_RENAME(pl_addLocaleRun) +#define pl_addValueRun U_ICU_ENTRY_POINT_RENAME(pl_addValueRun) +#define pl_close U_ICU_ENTRY_POINT_RENAME(pl_close) +#define pl_closeFontRuns U_ICU_ENTRY_POINT_RENAME(pl_closeFontRuns) +#define pl_closeLine U_ICU_ENTRY_POINT_RENAME(pl_closeLine) +#define pl_closeLocaleRuns U_ICU_ENTRY_POINT_RENAME(pl_closeLocaleRuns) +#define pl_closeValueRuns U_ICU_ENTRY_POINT_RENAME(pl_closeValueRuns) +#define pl_countLineRuns U_ICU_ENTRY_POINT_RENAME(pl_countLineRuns) +#define pl_create U_ICU_ENTRY_POINT_RENAME(pl_create) +#define pl_getAscent U_ICU_ENTRY_POINT_RENAME(pl_getAscent) +#define pl_getDescent U_ICU_ENTRY_POINT_RENAME(pl_getDescent) +#define pl_getFontRunCount U_ICU_ENTRY_POINT_RENAME(pl_getFontRunCount) +#define pl_getFontRunFont U_ICU_ENTRY_POINT_RENAME(pl_getFontRunFont) +#define pl_getFontRunLastLimit U_ICU_ENTRY_POINT_RENAME(pl_getFontRunLastLimit) +#define pl_getFontRunLimit U_ICU_ENTRY_POINT_RENAME(pl_getFontRunLimit) +#define pl_getLeading U_ICU_ENTRY_POINT_RENAME(pl_getLeading) +#define pl_getLineAscent U_ICU_ENTRY_POINT_RENAME(pl_getLineAscent) +#define pl_getLineDescent U_ICU_ENTRY_POINT_RENAME(pl_getLineDescent) +#define pl_getLineLeading U_ICU_ENTRY_POINT_RENAME(pl_getLineLeading) +#define pl_getLineVisualRun U_ICU_ENTRY_POINT_RENAME(pl_getLineVisualRun) +#define pl_getLineWidth U_ICU_ENTRY_POINT_RENAME(pl_getLineWidth) +#define pl_getLocaleRunCount U_ICU_ENTRY_POINT_RENAME(pl_getLocaleRunCount) +#define pl_getLocaleRunLastLimit U_ICU_ENTRY_POINT_RENAME(pl_getLocaleRunLastLimit) +#define pl_getLocaleRunLimit U_ICU_ENTRY_POINT_RENAME(pl_getLocaleRunLimit) +#define pl_getLocaleRunLocale U_ICU_ENTRY_POINT_RENAME(pl_getLocaleRunLocale) +#define pl_getParagraphLevel U_ICU_ENTRY_POINT_RENAME(pl_getParagraphLevel) +#define pl_getTextDirection U_ICU_ENTRY_POINT_RENAME(pl_getTextDirection) +#define pl_getValueRunCount U_ICU_ENTRY_POINT_RENAME(pl_getValueRunCount) +#define pl_getValueRunLastLimit U_ICU_ENTRY_POINT_RENAME(pl_getValueRunLastLimit) +#define pl_getValueRunLimit U_ICU_ENTRY_POINT_RENAME(pl_getValueRunLimit) +#define pl_getValueRunValue U_ICU_ENTRY_POINT_RENAME(pl_getValueRunValue) +#define pl_getVisualRunAscent U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunAscent) +#define pl_getVisualRunDescent U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunDescent) +#define pl_getVisualRunDirection U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunDirection) +#define pl_getVisualRunFont U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunFont) +#define pl_getVisualRunGlyphCount U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunGlyphCount) +#define pl_getVisualRunGlyphToCharMap U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunGlyphToCharMap) +#define pl_getVisualRunGlyphs U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunGlyphs) +#define pl_getVisualRunLeading U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunLeading) +#define pl_getVisualRunPositions U_ICU_ENTRY_POINT_RENAME(pl_getVisualRunPositions) +#define pl_isComplex U_ICU_ENTRY_POINT_RENAME(pl_isComplex) +#define pl_nextLine U_ICU_ENTRY_POINT_RENAME(pl_nextLine) +#define pl_openEmptyFontRuns U_ICU_ENTRY_POINT_RENAME(pl_openEmptyFontRuns) +#define pl_openEmptyLocaleRuns U_ICU_ENTRY_POINT_RENAME(pl_openEmptyLocaleRuns) +#define pl_openEmptyValueRuns U_ICU_ENTRY_POINT_RENAME(pl_openEmptyValueRuns) +#define pl_openFontRuns U_ICU_ENTRY_POINT_RENAME(pl_openFontRuns) +#define pl_openLocaleRuns U_ICU_ENTRY_POINT_RENAME(pl_openLocaleRuns) +#define pl_openValueRuns U_ICU_ENTRY_POINT_RENAME(pl_openValueRuns) +#define pl_reflow U_ICU_ENTRY_POINT_RENAME(pl_reflow) +#define pl_resetFontRuns U_ICU_ENTRY_POINT_RENAME(pl_resetFontRuns) +#define pl_resetLocaleRuns U_ICU_ENTRY_POINT_RENAME(pl_resetLocaleRuns) +#define pl_resetValueRuns U_ICU_ENTRY_POINT_RENAME(pl_resetValueRuns) +#define res_countArrayItems U_ICU_ENTRY_POINT_RENAME(res_countArrayItems) +#define res_findResource U_ICU_ENTRY_POINT_RENAME(res_findResource) +#define res_getAlias U_ICU_ENTRY_POINT_RENAME(res_getAlias) +#define res_getArrayItem U_ICU_ENTRY_POINT_RENAME(res_getArrayItem) +#define res_getBinaryNoTrace U_ICU_ENTRY_POINT_RENAME(res_getBinaryNoTrace) +#define res_getIntVectorNoTrace U_ICU_ENTRY_POINT_RENAME(res_getIntVectorNoTrace) +#define res_getPublicType U_ICU_ENTRY_POINT_RENAME(res_getPublicType) +#define res_getResource U_ICU_ENTRY_POINT_RENAME(res_getResource) +#define res_getStringNoTrace U_ICU_ENTRY_POINT_RENAME(res_getStringNoTrace) +#define res_getTableItemByIndex U_ICU_ENTRY_POINT_RENAME(res_getTableItemByIndex) +#define res_getTableItemByKey U_ICU_ENTRY_POINT_RENAME(res_getTableItemByKey) +#define res_load U_ICU_ENTRY_POINT_RENAME(res_load) +#define res_read U_ICU_ENTRY_POINT_RENAME(res_read) +#define res_unload U_ICU_ENTRY_POINT_RENAME(res_unload) +#define u_UCharsToChars U_ICU_ENTRY_POINT_RENAME(u_UCharsToChars) +#define u_asciiToUpper U_ICU_ENTRY_POINT_RENAME(u_asciiToUpper) +#define u_austrcpy U_ICU_ENTRY_POINT_RENAME(u_austrcpy) +#define u_austrncpy U_ICU_ENTRY_POINT_RENAME(u_austrncpy) +#define u_caseInsensitivePrefixMatch U_ICU_ENTRY_POINT_RENAME(u_caseInsensitivePrefixMatch) +#define u_catclose U_ICU_ENTRY_POINT_RENAME(u_catclose) +#define u_catgets U_ICU_ENTRY_POINT_RENAME(u_catgets) +#define u_catopen U_ICU_ENTRY_POINT_RENAME(u_catopen) +#define u_charAge U_ICU_ENTRY_POINT_RENAME(u_charAge) +#define u_charDigitValue U_ICU_ENTRY_POINT_RENAME(u_charDigitValue) +#define u_charDirection U_ICU_ENTRY_POINT_RENAME(u_charDirection) +#define u_charFromName U_ICU_ENTRY_POINT_RENAME(u_charFromName) +#define u_charMirror U_ICU_ENTRY_POINT_RENAME(u_charMirror) +#define u_charName U_ICU_ENTRY_POINT_RENAME(u_charName) +#define u_charType U_ICU_ENTRY_POINT_RENAME(u_charType) +#define u_charsToUChars U_ICU_ENTRY_POINT_RENAME(u_charsToUChars) +#define u_cleanup U_ICU_ENTRY_POINT_RENAME(u_cleanup) +#define u_countChar32 U_ICU_ENTRY_POINT_RENAME(u_countChar32) +#define u_digit U_ICU_ENTRY_POINT_RENAME(u_digit) +#define u_enumCharNames U_ICU_ENTRY_POINT_RENAME(u_enumCharNames) +#define u_enumCharTypes U_ICU_ENTRY_POINT_RENAME(u_enumCharTypes) +#define u_errorName U_ICU_ENTRY_POINT_RENAME(u_errorName) +#define u_fadopt U_ICU_ENTRY_POINT_RENAME(u_fadopt) +#define u_fclose U_ICU_ENTRY_POINT_RENAME(u_fclose) +#define u_feof U_ICU_ENTRY_POINT_RENAME(u_feof) +#define u_fflush U_ICU_ENTRY_POINT_RENAME(u_fflush) +#define u_fgetConverter U_ICU_ENTRY_POINT_RENAME(u_fgetConverter) +#define u_fgetNumberFormat U_ICU_ENTRY_POINT_RENAME(u_fgetNumberFormat) +#define u_fgetc U_ICU_ENTRY_POINT_RENAME(u_fgetc) +#define u_fgetcodepage U_ICU_ENTRY_POINT_RENAME(u_fgetcodepage) +#define u_fgetcx U_ICU_ENTRY_POINT_RENAME(u_fgetcx) +#define u_fgetfile U_ICU_ENTRY_POINT_RENAME(u_fgetfile) +#define u_fgetlocale U_ICU_ENTRY_POINT_RENAME(u_fgetlocale) +#define u_fgets U_ICU_ENTRY_POINT_RENAME(u_fgets) +#define u_file_read U_ICU_ENTRY_POINT_RENAME(u_file_read) +#define u_file_write U_ICU_ENTRY_POINT_RENAME(u_file_write) +#define u_file_write_flush U_ICU_ENTRY_POINT_RENAME(u_file_write_flush) +#define u_finit U_ICU_ENTRY_POINT_RENAME(u_finit) +#define u_flushDefaultConverter U_ICU_ENTRY_POINT_RENAME(u_flushDefaultConverter) +#define u_foldCase U_ICU_ENTRY_POINT_RENAME(u_foldCase) +#define u_fopen U_ICU_ENTRY_POINT_RENAME(u_fopen) +#define u_fopen_u U_ICU_ENTRY_POINT_RENAME(u_fopen_u) +#define u_forDigit U_ICU_ENTRY_POINT_RENAME(u_forDigit) +#define u_formatMessage U_ICU_ENTRY_POINT_RENAME(u_formatMessage) +#define u_formatMessageWithError U_ICU_ENTRY_POINT_RENAME(u_formatMessageWithError) +#define u_fprintf U_ICU_ENTRY_POINT_RENAME(u_fprintf) +#define u_fprintf_u U_ICU_ENTRY_POINT_RENAME(u_fprintf_u) +#define u_fputc U_ICU_ENTRY_POINT_RENAME(u_fputc) +#define u_fputs U_ICU_ENTRY_POINT_RENAME(u_fputs) +#define u_frewind U_ICU_ENTRY_POINT_RENAME(u_frewind) +#define u_fscanf U_ICU_ENTRY_POINT_RENAME(u_fscanf) +#define u_fscanf_u U_ICU_ENTRY_POINT_RENAME(u_fscanf_u) +#define u_fsetcodepage U_ICU_ENTRY_POINT_RENAME(u_fsetcodepage) +#define u_fsetlocale U_ICU_ENTRY_POINT_RENAME(u_fsetlocale) +#define u_fsettransliterator U_ICU_ENTRY_POINT_RENAME(u_fsettransliterator) +#define u_fstropen U_ICU_ENTRY_POINT_RENAME(u_fstropen) +#define u_fungetc U_ICU_ENTRY_POINT_RENAME(u_fungetc) +#define u_getBidiPairedBracket U_ICU_ENTRY_POINT_RENAME(u_getBidiPairedBracket) +#define u_getBinaryPropertySet U_ICU_ENTRY_POINT_RENAME(u_getBinaryPropertySet) +#define u_getCombiningClass U_ICU_ENTRY_POINT_RENAME(u_getCombiningClass) +#define u_getDataDirectory U_ICU_ENTRY_POINT_RENAME(u_getDataDirectory) +#define u_getDataVersion U_ICU_ENTRY_POINT_RENAME(u_getDataVersion) +#define u_getDefaultConverter U_ICU_ENTRY_POINT_RENAME(u_getDefaultConverter) +#define u_getFC_NFKC_Closure U_ICU_ENTRY_POINT_RENAME(u_getFC_NFKC_Closure) +#define u_getISOComment U_ICU_ENTRY_POINT_RENAME(u_getISOComment) +#define u_getIntPropertyMap U_ICU_ENTRY_POINT_RENAME(u_getIntPropertyMap) +#define u_getIntPropertyMaxValue U_ICU_ENTRY_POINT_RENAME(u_getIntPropertyMaxValue) +#define u_getIntPropertyMinValue U_ICU_ENTRY_POINT_RENAME(u_getIntPropertyMinValue) +#define u_getIntPropertyValue U_ICU_ENTRY_POINT_RENAME(u_getIntPropertyValue) +#define u_getMainProperties U_ICU_ENTRY_POINT_RENAME(u_getMainProperties) +#define u_getNumericValue U_ICU_ENTRY_POINT_RENAME(u_getNumericValue) +#define u_getPropertyEnum U_ICU_ENTRY_POINT_RENAME(u_getPropertyEnum) +#define u_getPropertyName U_ICU_ENTRY_POINT_RENAME(u_getPropertyName) +#define u_getPropertyValueEnum U_ICU_ENTRY_POINT_RENAME(u_getPropertyValueEnum) +#define u_getPropertyValueName U_ICU_ENTRY_POINT_RENAME(u_getPropertyValueName) +#define u_getTimeZoneFilesDirectory U_ICU_ENTRY_POINT_RENAME(u_getTimeZoneFilesDirectory) +#define u_getUnicodeProperties U_ICU_ENTRY_POINT_RENAME(u_getUnicodeProperties) +#define u_getUnicodeVersion U_ICU_ENTRY_POINT_RENAME(u_getUnicodeVersion) +#define u_getVersion U_ICU_ENTRY_POINT_RENAME(u_getVersion) +#define u_get_stdout U_ICU_ENTRY_POINT_RENAME(u_get_stdout) +#define u_hasBinaryProperty U_ICU_ENTRY_POINT_RENAME(u_hasBinaryProperty) +#define u_init U_ICU_ENTRY_POINT_RENAME(u_init) +#define u_isIDIgnorable U_ICU_ENTRY_POINT_RENAME(u_isIDIgnorable) +#define u_isIDPart U_ICU_ENTRY_POINT_RENAME(u_isIDPart) +#define u_isIDStart U_ICU_ENTRY_POINT_RENAME(u_isIDStart) +#define u_isISOControl U_ICU_ENTRY_POINT_RENAME(u_isISOControl) +#define u_isJavaIDPart U_ICU_ENTRY_POINT_RENAME(u_isJavaIDPart) +#define u_isJavaIDStart U_ICU_ENTRY_POINT_RENAME(u_isJavaIDStart) +#define u_isJavaSpaceChar U_ICU_ENTRY_POINT_RENAME(u_isJavaSpaceChar) +#define u_isMirrored U_ICU_ENTRY_POINT_RENAME(u_isMirrored) +#define u_isUAlphabetic U_ICU_ENTRY_POINT_RENAME(u_isUAlphabetic) +#define u_isULowercase U_ICU_ENTRY_POINT_RENAME(u_isULowercase) +#define u_isUUppercase U_ICU_ENTRY_POINT_RENAME(u_isUUppercase) +#define u_isUWhiteSpace U_ICU_ENTRY_POINT_RENAME(u_isUWhiteSpace) +#define u_isWhitespace U_ICU_ENTRY_POINT_RENAME(u_isWhitespace) +#define u_isalnum U_ICU_ENTRY_POINT_RENAME(u_isalnum) +#define u_isalnumPOSIX U_ICU_ENTRY_POINT_RENAME(u_isalnumPOSIX) +#define u_isalpha U_ICU_ENTRY_POINT_RENAME(u_isalpha) +#define u_isbase U_ICU_ENTRY_POINT_RENAME(u_isbase) +#define u_isblank U_ICU_ENTRY_POINT_RENAME(u_isblank) +#define u_iscntrl U_ICU_ENTRY_POINT_RENAME(u_iscntrl) +#define u_isdefined U_ICU_ENTRY_POINT_RENAME(u_isdefined) +#define u_isdigit U_ICU_ENTRY_POINT_RENAME(u_isdigit) +#define u_isgraph U_ICU_ENTRY_POINT_RENAME(u_isgraph) +#define u_isgraphPOSIX U_ICU_ENTRY_POINT_RENAME(u_isgraphPOSIX) +#define u_islower U_ICU_ENTRY_POINT_RENAME(u_islower) +#define u_isprint U_ICU_ENTRY_POINT_RENAME(u_isprint) +#define u_isprintPOSIX U_ICU_ENTRY_POINT_RENAME(u_isprintPOSIX) +#define u_ispunct U_ICU_ENTRY_POINT_RENAME(u_ispunct) +#define u_isspace U_ICU_ENTRY_POINT_RENAME(u_isspace) +#define u_istitle U_ICU_ENTRY_POINT_RENAME(u_istitle) +#define u_isupper U_ICU_ENTRY_POINT_RENAME(u_isupper) +#define u_isxdigit U_ICU_ENTRY_POINT_RENAME(u_isxdigit) +#define u_locbund_close U_ICU_ENTRY_POINT_RENAME(u_locbund_close) +#define u_locbund_getNumberFormat U_ICU_ENTRY_POINT_RENAME(u_locbund_getNumberFormat) +#define u_locbund_init U_ICU_ENTRY_POINT_RENAME(u_locbund_init) +#define u_memcasecmp U_ICU_ENTRY_POINT_RENAME(u_memcasecmp) +#define u_memchr U_ICU_ENTRY_POINT_RENAME(u_memchr) +#define u_memchr32 U_ICU_ENTRY_POINT_RENAME(u_memchr32) +#define u_memcmp U_ICU_ENTRY_POINT_RENAME(u_memcmp) +#define u_memcmpCodePointOrder U_ICU_ENTRY_POINT_RENAME(u_memcmpCodePointOrder) +#define u_memcpy U_ICU_ENTRY_POINT_RENAME(u_memcpy) +#define u_memmove U_ICU_ENTRY_POINT_RENAME(u_memmove) +#define u_memrchr U_ICU_ENTRY_POINT_RENAME(u_memrchr) +#define u_memrchr32 U_ICU_ENTRY_POINT_RENAME(u_memrchr32) +#define u_memset U_ICU_ENTRY_POINT_RENAME(u_memset) +#define u_parseMessage U_ICU_ENTRY_POINT_RENAME(u_parseMessage) +#define u_parseMessageWithError U_ICU_ENTRY_POINT_RENAME(u_parseMessageWithError) +#define u_printf U_ICU_ENTRY_POINT_RENAME(u_printf) +#define u_printf_parse U_ICU_ENTRY_POINT_RENAME(u_printf_parse) +#define u_printf_u U_ICU_ENTRY_POINT_RENAME(u_printf_u) +#define u_releaseDefaultConverter U_ICU_ENTRY_POINT_RENAME(u_releaseDefaultConverter) +#define u_scanf_parse U_ICU_ENTRY_POINT_RENAME(u_scanf_parse) +#define u_setAtomicIncDecFunctions U_ICU_ENTRY_POINT_RENAME(u_setAtomicIncDecFunctions) +#define u_setDataDirectory U_ICU_ENTRY_POINT_RENAME(u_setDataDirectory) +#define u_setMemoryFunctions U_ICU_ENTRY_POINT_RENAME(u_setMemoryFunctions) +#define u_setMutexFunctions U_ICU_ENTRY_POINT_RENAME(u_setMutexFunctions) +#define u_setTimeZoneFilesDirectory U_ICU_ENTRY_POINT_RENAME(u_setTimeZoneFilesDirectory) +#define u_shapeArabic U_ICU_ENTRY_POINT_RENAME(u_shapeArabic) +#define u_snprintf U_ICU_ENTRY_POINT_RENAME(u_snprintf) +#define u_snprintf_u U_ICU_ENTRY_POINT_RENAME(u_snprintf_u) +#define u_sprintf U_ICU_ENTRY_POINT_RENAME(u_sprintf) +#define u_sprintf_u U_ICU_ENTRY_POINT_RENAME(u_sprintf_u) +#define u_sscanf U_ICU_ENTRY_POINT_RENAME(u_sscanf) +#define u_sscanf_u U_ICU_ENTRY_POINT_RENAME(u_sscanf_u) +#define u_strCaseCompare U_ICU_ENTRY_POINT_RENAME(u_strCaseCompare) +#define u_strCompare U_ICU_ENTRY_POINT_RENAME(u_strCompare) +#define u_strCompareIter U_ICU_ENTRY_POINT_RENAME(u_strCompareIter) +#define u_strFindFirst U_ICU_ENTRY_POINT_RENAME(u_strFindFirst) +#define u_strFindLast U_ICU_ENTRY_POINT_RENAME(u_strFindLast) +#define u_strFoldCase U_ICU_ENTRY_POINT_RENAME(u_strFoldCase) +#define u_strFromJavaModifiedUTF8WithSub U_ICU_ENTRY_POINT_RENAME(u_strFromJavaModifiedUTF8WithSub) +#define u_strFromPunycode U_ICU_ENTRY_POINT_RENAME(u_strFromPunycode) +#define u_strFromUTF32 U_ICU_ENTRY_POINT_RENAME(u_strFromUTF32) +#define u_strFromUTF32WithSub U_ICU_ENTRY_POINT_RENAME(u_strFromUTF32WithSub) +#define u_strFromUTF8 U_ICU_ENTRY_POINT_RENAME(u_strFromUTF8) +#define u_strFromUTF8Lenient U_ICU_ENTRY_POINT_RENAME(u_strFromUTF8Lenient) +#define u_strFromUTF8WithSub U_ICU_ENTRY_POINT_RENAME(u_strFromUTF8WithSub) +#define u_strFromWCS U_ICU_ENTRY_POINT_RENAME(u_strFromWCS) +#define u_strHasMoreChar32Than U_ICU_ENTRY_POINT_RENAME(u_strHasMoreChar32Than) +#define u_strToJavaModifiedUTF8 U_ICU_ENTRY_POINT_RENAME(u_strToJavaModifiedUTF8) +#define u_strToLower U_ICU_ENTRY_POINT_RENAME(u_strToLower) +#define u_strToPunycode U_ICU_ENTRY_POINT_RENAME(u_strToPunycode) +#define u_strToTitle U_ICU_ENTRY_POINT_RENAME(u_strToTitle) +#define u_strToUTF32 U_ICU_ENTRY_POINT_RENAME(u_strToUTF32) +#define u_strToUTF32WithSub U_ICU_ENTRY_POINT_RENAME(u_strToUTF32WithSub) +#define u_strToUTF8 U_ICU_ENTRY_POINT_RENAME(u_strToUTF8) +#define u_strToUTF8WithSub U_ICU_ENTRY_POINT_RENAME(u_strToUTF8WithSub) +#define u_strToUpper U_ICU_ENTRY_POINT_RENAME(u_strToUpper) +#define u_strToWCS U_ICU_ENTRY_POINT_RENAME(u_strToWCS) +#define u_strcasecmp U_ICU_ENTRY_POINT_RENAME(u_strcasecmp) +#define u_strcat U_ICU_ENTRY_POINT_RENAME(u_strcat) +#define u_strchr U_ICU_ENTRY_POINT_RENAME(u_strchr) +#define u_strchr32 U_ICU_ENTRY_POINT_RENAME(u_strchr32) +#define u_strcmp U_ICU_ENTRY_POINT_RENAME(u_strcmp) +#define u_strcmpCodePointOrder U_ICU_ENTRY_POINT_RENAME(u_strcmpCodePointOrder) +#define u_strcmpFold U_ICU_ENTRY_POINT_RENAME(u_strcmpFold) +#define u_strcpy U_ICU_ENTRY_POINT_RENAME(u_strcpy) +#define u_strcspn U_ICU_ENTRY_POINT_RENAME(u_strcspn) +#define u_stringHasBinaryProperty U_ICU_ENTRY_POINT_RENAME(u_stringHasBinaryProperty) +#define u_strlen U_ICU_ENTRY_POINT_RENAME(u_strlen) +#define u_strncasecmp U_ICU_ENTRY_POINT_RENAME(u_strncasecmp) +#define u_strncat U_ICU_ENTRY_POINT_RENAME(u_strncat) +#define u_strncmp U_ICU_ENTRY_POINT_RENAME(u_strncmp) +#define u_strncmpCodePointOrder U_ICU_ENTRY_POINT_RENAME(u_strncmpCodePointOrder) +#define u_strncpy U_ICU_ENTRY_POINT_RENAME(u_strncpy) +#define u_strpbrk U_ICU_ENTRY_POINT_RENAME(u_strpbrk) +#define u_strrchr U_ICU_ENTRY_POINT_RENAME(u_strrchr) +#define u_strrchr32 U_ICU_ENTRY_POINT_RENAME(u_strrchr32) +#define u_strrstr U_ICU_ENTRY_POINT_RENAME(u_strrstr) +#define u_strspn U_ICU_ENTRY_POINT_RENAME(u_strspn) +#define u_strstr U_ICU_ENTRY_POINT_RENAME(u_strstr) +#define u_strtok_r U_ICU_ENTRY_POINT_RENAME(u_strtok_r) +#define u_terminateChars U_ICU_ENTRY_POINT_RENAME(u_terminateChars) +#define u_terminateUChar32s U_ICU_ENTRY_POINT_RENAME(u_terminateUChar32s) +#define u_terminateUChars U_ICU_ENTRY_POINT_RENAME(u_terminateUChars) +#define u_terminateWChars U_ICU_ENTRY_POINT_RENAME(u_terminateWChars) +#define u_tolower U_ICU_ENTRY_POINT_RENAME(u_tolower) +#define u_totitle U_ICU_ENTRY_POINT_RENAME(u_totitle) +#define u_toupper U_ICU_ENTRY_POINT_RENAME(u_toupper) +#define u_uastrcpy U_ICU_ENTRY_POINT_RENAME(u_uastrcpy) +#define u_uastrncpy U_ICU_ENTRY_POINT_RENAME(u_uastrncpy) +#define u_unescape U_ICU_ENTRY_POINT_RENAME(u_unescape) +#define u_unescapeAt U_ICU_ENTRY_POINT_RENAME(u_unescapeAt) +#define u_versionFromString U_ICU_ENTRY_POINT_RENAME(u_versionFromString) +#define u_versionFromUString U_ICU_ENTRY_POINT_RENAME(u_versionFromUString) +#define u_versionToString U_ICU_ENTRY_POINT_RENAME(u_versionToString) +#define u_vformatMessage U_ICU_ENTRY_POINT_RENAME(u_vformatMessage) +#define u_vformatMessageWithError U_ICU_ENTRY_POINT_RENAME(u_vformatMessageWithError) +#define u_vfprintf U_ICU_ENTRY_POINT_RENAME(u_vfprintf) +#define u_vfprintf_u U_ICU_ENTRY_POINT_RENAME(u_vfprintf_u) +#define u_vfscanf U_ICU_ENTRY_POINT_RENAME(u_vfscanf) +#define u_vfscanf_u U_ICU_ENTRY_POINT_RENAME(u_vfscanf_u) +#define u_vparseMessage U_ICU_ENTRY_POINT_RENAME(u_vparseMessage) +#define u_vparseMessageWithError U_ICU_ENTRY_POINT_RENAME(u_vparseMessageWithError) +#define u_vsnprintf U_ICU_ENTRY_POINT_RENAME(u_vsnprintf) +#define u_vsnprintf_u U_ICU_ENTRY_POINT_RENAME(u_vsnprintf_u) +#define u_vsprintf U_ICU_ENTRY_POINT_RENAME(u_vsprintf) +#define u_vsprintf_u U_ICU_ENTRY_POINT_RENAME(u_vsprintf_u) +#define u_vsscanf U_ICU_ENTRY_POINT_RENAME(u_vsscanf) +#define u_vsscanf_u U_ICU_ENTRY_POINT_RENAME(u_vsscanf_u) +#define u_writeIdenticalLevelRun U_ICU_ENTRY_POINT_RENAME(u_writeIdenticalLevelRun) +#define ubidi_addPropertyStarts U_ICU_ENTRY_POINT_RENAME(ubidi_addPropertyStarts) +#define ubidi_close U_ICU_ENTRY_POINT_RENAME(ubidi_close) +#define ubidi_countParagraphs U_ICU_ENTRY_POINT_RENAME(ubidi_countParagraphs) +#define ubidi_countRuns U_ICU_ENTRY_POINT_RENAME(ubidi_countRuns) +#define ubidi_getBaseDirection U_ICU_ENTRY_POINT_RENAME(ubidi_getBaseDirection) +#define ubidi_getClass U_ICU_ENTRY_POINT_RENAME(ubidi_getClass) +#define ubidi_getClassCallback U_ICU_ENTRY_POINT_RENAME(ubidi_getClassCallback) +#define ubidi_getCustomizedClass U_ICU_ENTRY_POINT_RENAME(ubidi_getCustomizedClass) +#define ubidi_getDirection U_ICU_ENTRY_POINT_RENAME(ubidi_getDirection) +#define ubidi_getJoiningGroup U_ICU_ENTRY_POINT_RENAME(ubidi_getJoiningGroup) +#define ubidi_getJoiningType U_ICU_ENTRY_POINT_RENAME(ubidi_getJoiningType) +#define ubidi_getLength U_ICU_ENTRY_POINT_RENAME(ubidi_getLength) +#define ubidi_getLevelAt U_ICU_ENTRY_POINT_RENAME(ubidi_getLevelAt) +#define ubidi_getLevels U_ICU_ENTRY_POINT_RENAME(ubidi_getLevels) +#define ubidi_getLogicalIndex U_ICU_ENTRY_POINT_RENAME(ubidi_getLogicalIndex) +#define ubidi_getLogicalMap U_ICU_ENTRY_POINT_RENAME(ubidi_getLogicalMap) +#define ubidi_getLogicalRun U_ICU_ENTRY_POINT_RENAME(ubidi_getLogicalRun) +#define ubidi_getMaxValue U_ICU_ENTRY_POINT_RENAME(ubidi_getMaxValue) +#define ubidi_getMemory U_ICU_ENTRY_POINT_RENAME(ubidi_getMemory) +#define ubidi_getMirror U_ICU_ENTRY_POINT_RENAME(ubidi_getMirror) +#define ubidi_getPairedBracket U_ICU_ENTRY_POINT_RENAME(ubidi_getPairedBracket) +#define ubidi_getPairedBracketType U_ICU_ENTRY_POINT_RENAME(ubidi_getPairedBracketType) +#define ubidi_getParaLevel U_ICU_ENTRY_POINT_RENAME(ubidi_getParaLevel) +#define ubidi_getParaLevelAtIndex U_ICU_ENTRY_POINT_RENAME(ubidi_getParaLevelAtIndex) +#define ubidi_getParagraph U_ICU_ENTRY_POINT_RENAME(ubidi_getParagraph) +#define ubidi_getParagraphByIndex U_ICU_ENTRY_POINT_RENAME(ubidi_getParagraphByIndex) +#define ubidi_getProcessedLength U_ICU_ENTRY_POINT_RENAME(ubidi_getProcessedLength) +#define ubidi_getReorderingMode U_ICU_ENTRY_POINT_RENAME(ubidi_getReorderingMode) +#define ubidi_getReorderingOptions U_ICU_ENTRY_POINT_RENAME(ubidi_getReorderingOptions) +#define ubidi_getResultLength U_ICU_ENTRY_POINT_RENAME(ubidi_getResultLength) +#define ubidi_getRuns U_ICU_ENTRY_POINT_RENAME(ubidi_getRuns) +#define ubidi_getText U_ICU_ENTRY_POINT_RENAME(ubidi_getText) +#define ubidi_getVisualIndex U_ICU_ENTRY_POINT_RENAME(ubidi_getVisualIndex) +#define ubidi_getVisualMap U_ICU_ENTRY_POINT_RENAME(ubidi_getVisualMap) +#define ubidi_getVisualRun U_ICU_ENTRY_POINT_RENAME(ubidi_getVisualRun) +#define ubidi_invertMap U_ICU_ENTRY_POINT_RENAME(ubidi_invertMap) +#define ubidi_isBidiControl U_ICU_ENTRY_POINT_RENAME(ubidi_isBidiControl) +#define ubidi_isInverse U_ICU_ENTRY_POINT_RENAME(ubidi_isInverse) +#define ubidi_isJoinControl U_ICU_ENTRY_POINT_RENAME(ubidi_isJoinControl) +#define ubidi_isMirrored U_ICU_ENTRY_POINT_RENAME(ubidi_isMirrored) +#define ubidi_isOrderParagraphsLTR U_ICU_ENTRY_POINT_RENAME(ubidi_isOrderParagraphsLTR) +#define ubidi_open U_ICU_ENTRY_POINT_RENAME(ubidi_open) +#define ubidi_openSized U_ICU_ENTRY_POINT_RENAME(ubidi_openSized) +#define ubidi_orderParagraphsLTR U_ICU_ENTRY_POINT_RENAME(ubidi_orderParagraphsLTR) +#define ubidi_reorderLogical U_ICU_ENTRY_POINT_RENAME(ubidi_reorderLogical) +#define ubidi_reorderVisual U_ICU_ENTRY_POINT_RENAME(ubidi_reorderVisual) +#define ubidi_setClassCallback U_ICU_ENTRY_POINT_RENAME(ubidi_setClassCallback) +#define ubidi_setContext U_ICU_ENTRY_POINT_RENAME(ubidi_setContext) +#define ubidi_setInverse U_ICU_ENTRY_POINT_RENAME(ubidi_setInverse) +#define ubidi_setLine U_ICU_ENTRY_POINT_RENAME(ubidi_setLine) +#define ubidi_setPara U_ICU_ENTRY_POINT_RENAME(ubidi_setPara) +#define ubidi_setReorderingMode U_ICU_ENTRY_POINT_RENAME(ubidi_setReorderingMode) +#define ubidi_setReorderingOptions U_ICU_ENTRY_POINT_RENAME(ubidi_setReorderingOptions) +#define ubidi_writeReordered U_ICU_ENTRY_POINT_RENAME(ubidi_writeReordered) +#define ubidi_writeReverse U_ICU_ENTRY_POINT_RENAME(ubidi_writeReverse) +#define ubiditransform_close U_ICU_ENTRY_POINT_RENAME(ubiditransform_close) +#define ubiditransform_open U_ICU_ENTRY_POINT_RENAME(ubiditransform_open) +#define ubiditransform_transform U_ICU_ENTRY_POINT_RENAME(ubiditransform_transform) +#define ublock_getCode U_ICU_ENTRY_POINT_RENAME(ublock_getCode) +#define ubrk_clone U_ICU_ENTRY_POINT_RENAME(ubrk_clone) +#define ubrk_close U_ICU_ENTRY_POINT_RENAME(ubrk_close) +#define ubrk_countAvailable U_ICU_ENTRY_POINT_RENAME(ubrk_countAvailable) +#define ubrk_current U_ICU_ENTRY_POINT_RENAME(ubrk_current) +#define ubrk_first U_ICU_ENTRY_POINT_RENAME(ubrk_first) +#define ubrk_following U_ICU_ENTRY_POINT_RENAME(ubrk_following) +#define ubrk_getAvailable U_ICU_ENTRY_POINT_RENAME(ubrk_getAvailable) +#define ubrk_getBinaryRules U_ICU_ENTRY_POINT_RENAME(ubrk_getBinaryRules) +#define ubrk_getLocaleByType U_ICU_ENTRY_POINT_RENAME(ubrk_getLocaleByType) +#define ubrk_getRuleStatus U_ICU_ENTRY_POINT_RENAME(ubrk_getRuleStatus) +#define ubrk_getRuleStatusVec U_ICU_ENTRY_POINT_RENAME(ubrk_getRuleStatusVec) +#define ubrk_isBoundary U_ICU_ENTRY_POINT_RENAME(ubrk_isBoundary) +#define ubrk_last U_ICU_ENTRY_POINT_RENAME(ubrk_last) +#define ubrk_next U_ICU_ENTRY_POINT_RENAME(ubrk_next) +#define ubrk_open U_ICU_ENTRY_POINT_RENAME(ubrk_open) +#define ubrk_openBinaryRules U_ICU_ENTRY_POINT_RENAME(ubrk_openBinaryRules) +#define ubrk_openRules U_ICU_ENTRY_POINT_RENAME(ubrk_openRules) +#define ubrk_preceding U_ICU_ENTRY_POINT_RENAME(ubrk_preceding) +#define ubrk_previous U_ICU_ENTRY_POINT_RENAME(ubrk_previous) +#define ubrk_refreshUText U_ICU_ENTRY_POINT_RENAME(ubrk_refreshUText) +#define ubrk_safeClone U_ICU_ENTRY_POINT_RENAME(ubrk_safeClone) +#define ubrk_setText U_ICU_ENTRY_POINT_RENAME(ubrk_setText) +#define ubrk_setUText U_ICU_ENTRY_POINT_RENAME(ubrk_setUText) +#define ubrk_swap U_ICU_ENTRY_POINT_RENAME(ubrk_swap) +#define ucache_compareKeys U_ICU_ENTRY_POINT_RENAME(ucache_compareKeys) +#define ucache_deleteKey U_ICU_ENTRY_POINT_RENAME(ucache_deleteKey) +#define ucache_hashKeys U_ICU_ENTRY_POINT_RENAME(ucache_hashKeys) +#define ucal_add U_ICU_ENTRY_POINT_RENAME(ucal_add) +#define ucal_clear U_ICU_ENTRY_POINT_RENAME(ucal_clear) +#define ucal_clearField U_ICU_ENTRY_POINT_RENAME(ucal_clearField) +#define ucal_clone U_ICU_ENTRY_POINT_RENAME(ucal_clone) +#define ucal_close U_ICU_ENTRY_POINT_RENAME(ucal_close) +#define ucal_countAvailable U_ICU_ENTRY_POINT_RENAME(ucal_countAvailable) +#define ucal_equivalentTo U_ICU_ENTRY_POINT_RENAME(ucal_equivalentTo) +#define ucal_get U_ICU_ENTRY_POINT_RENAME(ucal_get) +#define ucal_getAttribute U_ICU_ENTRY_POINT_RENAME(ucal_getAttribute) +#define ucal_getAvailable U_ICU_ENTRY_POINT_RENAME(ucal_getAvailable) +#define ucal_getCanonicalTimeZoneID U_ICU_ENTRY_POINT_RENAME(ucal_getCanonicalTimeZoneID) +#define ucal_getDSTSavings U_ICU_ENTRY_POINT_RENAME(ucal_getDSTSavings) +#define ucal_getDayOfWeekType U_ICU_ENTRY_POINT_RENAME(ucal_getDayOfWeekType) +#define ucal_getDefaultTimeZone U_ICU_ENTRY_POINT_RENAME(ucal_getDefaultTimeZone) +#define ucal_getFieldDifference U_ICU_ENTRY_POINT_RENAME(ucal_getFieldDifference) +#define ucal_getGregorianChange U_ICU_ENTRY_POINT_RENAME(ucal_getGregorianChange) +#define ucal_getHostTimeZone U_ICU_ENTRY_POINT_RENAME(ucal_getHostTimeZone) +#define ucal_getKeywordValuesForLocale U_ICU_ENTRY_POINT_RENAME(ucal_getKeywordValuesForLocale) +#define ucal_getLimit U_ICU_ENTRY_POINT_RENAME(ucal_getLimit) +#define ucal_getLocaleByType U_ICU_ENTRY_POINT_RENAME(ucal_getLocaleByType) +#define ucal_getMillis U_ICU_ENTRY_POINT_RENAME(ucal_getMillis) +#define ucal_getNow U_ICU_ENTRY_POINT_RENAME(ucal_getNow) +#define ucal_getTZDataVersion U_ICU_ENTRY_POINT_RENAME(ucal_getTZDataVersion) +#define ucal_getTimeZoneDisplayName U_ICU_ENTRY_POINT_RENAME(ucal_getTimeZoneDisplayName) +#define ucal_getTimeZoneID U_ICU_ENTRY_POINT_RENAME(ucal_getTimeZoneID) +#define ucal_getTimeZoneIDForWindowsID U_ICU_ENTRY_POINT_RENAME(ucal_getTimeZoneIDForWindowsID) +#define ucal_getTimeZoneOffsetFromLocal U_ICU_ENTRY_POINT_RENAME(ucal_getTimeZoneOffsetFromLocal) +#define ucal_getTimeZoneTransitionDate U_ICU_ENTRY_POINT_RENAME(ucal_getTimeZoneTransitionDate) +#define ucal_getType U_ICU_ENTRY_POINT_RENAME(ucal_getType) +#define ucal_getWeekendTransition U_ICU_ENTRY_POINT_RENAME(ucal_getWeekendTransition) +#define ucal_getWindowsTimeZoneID U_ICU_ENTRY_POINT_RENAME(ucal_getWindowsTimeZoneID) +#define ucal_inDaylightTime U_ICU_ENTRY_POINT_RENAME(ucal_inDaylightTime) +#define ucal_isSet U_ICU_ENTRY_POINT_RENAME(ucal_isSet) +#define ucal_isWeekend U_ICU_ENTRY_POINT_RENAME(ucal_isWeekend) +#define ucal_open U_ICU_ENTRY_POINT_RENAME(ucal_open) +#define ucal_openCountryTimeZones U_ICU_ENTRY_POINT_RENAME(ucal_openCountryTimeZones) +#define ucal_openTimeZoneIDEnumeration U_ICU_ENTRY_POINT_RENAME(ucal_openTimeZoneIDEnumeration) +#define ucal_openTimeZones U_ICU_ENTRY_POINT_RENAME(ucal_openTimeZones) +#define ucal_roll U_ICU_ENTRY_POINT_RENAME(ucal_roll) +#define ucal_set U_ICU_ENTRY_POINT_RENAME(ucal_set) +#define ucal_setAttribute U_ICU_ENTRY_POINT_RENAME(ucal_setAttribute) +#define ucal_setDate U_ICU_ENTRY_POINT_RENAME(ucal_setDate) +#define ucal_setDateTime U_ICU_ENTRY_POINT_RENAME(ucal_setDateTime) +#define ucal_setDefaultTimeZone U_ICU_ENTRY_POINT_RENAME(ucal_setDefaultTimeZone) +#define ucal_setGregorianChange U_ICU_ENTRY_POINT_RENAME(ucal_setGregorianChange) +#define ucal_setMillis U_ICU_ENTRY_POINT_RENAME(ucal_setMillis) +#define ucal_setTimeZone U_ICU_ENTRY_POINT_RENAME(ucal_setTimeZone) +#define ucase_addCaseClosure U_ICU_ENTRY_POINT_RENAME(ucase_addCaseClosure) +#define ucase_addPropertyStarts U_ICU_ENTRY_POINT_RENAME(ucase_addPropertyStarts) +#define ucase_addSimpleCaseClosure U_ICU_ENTRY_POINT_RENAME(ucase_addSimpleCaseClosure) +#define ucase_addStringCaseClosure U_ICU_ENTRY_POINT_RENAME(ucase_addStringCaseClosure) +#define ucase_fold U_ICU_ENTRY_POINT_RENAME(ucase_fold) +#define ucase_getCaseLocale U_ICU_ENTRY_POINT_RENAME(ucase_getCaseLocale) +#define ucase_getSingleton U_ICU_ENTRY_POINT_RENAME(ucase_getSingleton) +#define ucase_getTrie U_ICU_ENTRY_POINT_RENAME(ucase_getTrie) +#define ucase_getType U_ICU_ENTRY_POINT_RENAME(ucase_getType) +#define ucase_getTypeOrIgnorable U_ICU_ENTRY_POINT_RENAME(ucase_getTypeOrIgnorable) +#define ucase_hasBinaryProperty U_ICU_ENTRY_POINT_RENAME(ucase_hasBinaryProperty) +#define ucase_isCaseSensitive U_ICU_ENTRY_POINT_RENAME(ucase_isCaseSensitive) +#define ucase_isSoftDotted U_ICU_ENTRY_POINT_RENAME(ucase_isSoftDotted) +#define ucase_toFullFolding U_ICU_ENTRY_POINT_RENAME(ucase_toFullFolding) +#define ucase_toFullLower U_ICU_ENTRY_POINT_RENAME(ucase_toFullLower) +#define ucase_toFullTitle U_ICU_ENTRY_POINT_RENAME(ucase_toFullTitle) +#define ucase_toFullUpper U_ICU_ENTRY_POINT_RENAME(ucase_toFullUpper) +#define ucase_tolower U_ICU_ENTRY_POINT_RENAME(ucase_tolower) +#define ucase_totitle U_ICU_ENTRY_POINT_RENAME(ucase_totitle) +#define ucase_toupper U_ICU_ENTRY_POINT_RENAME(ucase_toupper) +#define ucasemap_close U_ICU_ENTRY_POINT_RENAME(ucasemap_close) +#define ucasemap_getBreakIterator U_ICU_ENTRY_POINT_RENAME(ucasemap_getBreakIterator) +#define ucasemap_getLocale U_ICU_ENTRY_POINT_RENAME(ucasemap_getLocale) +#define ucasemap_getOptions U_ICU_ENTRY_POINT_RENAME(ucasemap_getOptions) +#define ucasemap_internalUTF8ToTitle U_ICU_ENTRY_POINT_RENAME(ucasemap_internalUTF8ToTitle) +#define ucasemap_open U_ICU_ENTRY_POINT_RENAME(ucasemap_open) +#define ucasemap_setBreakIterator U_ICU_ENTRY_POINT_RENAME(ucasemap_setBreakIterator) +#define ucasemap_setLocale U_ICU_ENTRY_POINT_RENAME(ucasemap_setLocale) +#define ucasemap_setOptions U_ICU_ENTRY_POINT_RENAME(ucasemap_setOptions) +#define ucasemap_toTitle U_ICU_ENTRY_POINT_RENAME(ucasemap_toTitle) +#define ucasemap_utf8FoldCase U_ICU_ENTRY_POINT_RENAME(ucasemap_utf8FoldCase) +#define ucasemap_utf8ToLower U_ICU_ENTRY_POINT_RENAME(ucasemap_utf8ToLower) +#define ucasemap_utf8ToTitle U_ICU_ENTRY_POINT_RENAME(ucasemap_utf8ToTitle) +#define ucasemap_utf8ToUpper U_ICU_ENTRY_POINT_RENAME(ucasemap_utf8ToUpper) +#define ucfpos_close U_ICU_ENTRY_POINT_RENAME(ucfpos_close) +#define ucfpos_constrainCategory U_ICU_ENTRY_POINT_RENAME(ucfpos_constrainCategory) +#define ucfpos_constrainField U_ICU_ENTRY_POINT_RENAME(ucfpos_constrainField) +#define ucfpos_getCategory U_ICU_ENTRY_POINT_RENAME(ucfpos_getCategory) +#define ucfpos_getField U_ICU_ENTRY_POINT_RENAME(ucfpos_getField) +#define ucfpos_getIndexes U_ICU_ENTRY_POINT_RENAME(ucfpos_getIndexes) +#define ucfpos_getInt64IterationContext U_ICU_ENTRY_POINT_RENAME(ucfpos_getInt64IterationContext) +#define ucfpos_matchesField U_ICU_ENTRY_POINT_RENAME(ucfpos_matchesField) +#define ucfpos_open U_ICU_ENTRY_POINT_RENAME(ucfpos_open) +#define ucfpos_reset U_ICU_ENTRY_POINT_RENAME(ucfpos_reset) +#define ucfpos_setInt64IterationContext U_ICU_ENTRY_POINT_RENAME(ucfpos_setInt64IterationContext) +#define ucfpos_setState U_ICU_ENTRY_POINT_RENAME(ucfpos_setState) +#define uchar_addPropertyStarts U_ICU_ENTRY_POINT_RENAME(uchar_addPropertyStarts) +#define uchar_swapNames U_ICU_ENTRY_POINT_RENAME(uchar_swapNames) +#define ucln_cleanupOne U_ICU_ENTRY_POINT_RENAME(ucln_cleanupOne) +#define ucln_common_registerCleanup U_ICU_ENTRY_POINT_RENAME(ucln_common_registerCleanup) +#define ucln_i18n_registerCleanup U_ICU_ENTRY_POINT_RENAME(ucln_i18n_registerCleanup) +#define ucln_io_registerCleanup U_ICU_ENTRY_POINT_RENAME(ucln_io_registerCleanup) +#define ucln_lib_cleanup U_ICU_ENTRY_POINT_RENAME(ucln_lib_cleanup) +#define ucln_registerCleanup U_ICU_ENTRY_POINT_RENAME(ucln_registerCleanup) +#define ucnv_MBCSFromUChar32 U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSFromUChar32) +#define ucnv_MBCSFromUnicodeWithOffsets U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSFromUnicodeWithOffsets) +#define ucnv_MBCSGetFilteredUnicodeSetForUnicode U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSGetFilteredUnicodeSetForUnicode) +#define ucnv_MBCSGetType U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSGetType) +#define ucnv_MBCSGetUnicodeSetForUnicode U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSGetUnicodeSetForUnicode) +#define ucnv_MBCSIsLeadByte U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSIsLeadByte) +#define ucnv_MBCSSimpleGetNextUChar U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSSimpleGetNextUChar) +#define ucnv_MBCSToUnicodeWithOffsets U_ICU_ENTRY_POINT_RENAME(ucnv_MBCSToUnicodeWithOffsets) +#define ucnv_bld_countAvailableConverters U_ICU_ENTRY_POINT_RENAME(ucnv_bld_countAvailableConverters) +#define ucnv_bld_getAvailableConverter U_ICU_ENTRY_POINT_RENAME(ucnv_bld_getAvailableConverter) +#define ucnv_canCreateConverter U_ICU_ENTRY_POINT_RENAME(ucnv_canCreateConverter) +#define ucnv_cbFromUWriteBytes U_ICU_ENTRY_POINT_RENAME(ucnv_cbFromUWriteBytes) +#define ucnv_cbFromUWriteSub U_ICU_ENTRY_POINT_RENAME(ucnv_cbFromUWriteSub) +#define ucnv_cbFromUWriteUChars U_ICU_ENTRY_POINT_RENAME(ucnv_cbFromUWriteUChars) +#define ucnv_cbToUWriteSub U_ICU_ENTRY_POINT_RENAME(ucnv_cbToUWriteSub) +#define ucnv_cbToUWriteUChars U_ICU_ENTRY_POINT_RENAME(ucnv_cbToUWriteUChars) +#define ucnv_clone U_ICU_ENTRY_POINT_RENAME(ucnv_clone) +#define ucnv_close U_ICU_ENTRY_POINT_RENAME(ucnv_close) +#define ucnv_compareNames U_ICU_ENTRY_POINT_RENAME(ucnv_compareNames) +#define ucnv_convert U_ICU_ENTRY_POINT_RENAME(ucnv_convert) +#define ucnv_convertEx U_ICU_ENTRY_POINT_RENAME(ucnv_convertEx) +#define ucnv_countAliases U_ICU_ENTRY_POINT_RENAME(ucnv_countAliases) +#define ucnv_countAvailable U_ICU_ENTRY_POINT_RENAME(ucnv_countAvailable) +#define ucnv_countStandards U_ICU_ENTRY_POINT_RENAME(ucnv_countStandards) +#define ucnv_createAlgorithmicConverter U_ICU_ENTRY_POINT_RENAME(ucnv_createAlgorithmicConverter) +#define ucnv_createConverter U_ICU_ENTRY_POINT_RENAME(ucnv_createConverter) +#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) +#define ucnv_extInitialMatchFromU U_ICU_ENTRY_POINT_RENAME(ucnv_extInitialMatchFromU) +#define ucnv_extInitialMatchToU U_ICU_ENTRY_POINT_RENAME(ucnv_extInitialMatchToU) +#define ucnv_extSimpleMatchFromU U_ICU_ENTRY_POINT_RENAME(ucnv_extSimpleMatchFromU) +#define ucnv_extSimpleMatchToU U_ICU_ENTRY_POINT_RENAME(ucnv_extSimpleMatchToU) +#define ucnv_fixFileSeparator U_ICU_ENTRY_POINT_RENAME(ucnv_fixFileSeparator) +#define ucnv_flushCache U_ICU_ENTRY_POINT_RENAME(ucnv_flushCache) +#define ucnv_fromAlgorithmic U_ICU_ENTRY_POINT_RENAME(ucnv_fromAlgorithmic) +#define ucnv_fromUChars U_ICU_ENTRY_POINT_RENAME(ucnv_fromUChars) +#define ucnv_fromUCountPending U_ICU_ENTRY_POINT_RENAME(ucnv_fromUCountPending) +#define ucnv_fromUWriteBytes U_ICU_ENTRY_POINT_RENAME(ucnv_fromUWriteBytes) +#define ucnv_fromUnicode U_ICU_ENTRY_POINT_RENAME(ucnv_fromUnicode) +#define ucnv_fromUnicode_UTF8 U_ICU_ENTRY_POINT_RENAME(ucnv_fromUnicode_UTF8) +#define ucnv_fromUnicode_UTF8_OFFSETS_LOGIC U_ICU_ENTRY_POINT_RENAME(ucnv_fromUnicode_UTF8_OFFSETS_LOGIC) +#define ucnv_getAlias U_ICU_ENTRY_POINT_RENAME(ucnv_getAlias) +#define ucnv_getAliases U_ICU_ENTRY_POINT_RENAME(ucnv_getAliases) +#define ucnv_getAvailableName U_ICU_ENTRY_POINT_RENAME(ucnv_getAvailableName) +#define ucnv_getCCSID U_ICU_ENTRY_POINT_RENAME(ucnv_getCCSID) +#define ucnv_getCanonicalName U_ICU_ENTRY_POINT_RENAME(ucnv_getCanonicalName) +#define ucnv_getCompleteUnicodeSet U_ICU_ENTRY_POINT_RENAME(ucnv_getCompleteUnicodeSet) +#define ucnv_getDefaultName U_ICU_ENTRY_POINT_RENAME(ucnv_getDefaultName) +#define ucnv_getDisplayName U_ICU_ENTRY_POINT_RENAME(ucnv_getDisplayName) +#define ucnv_getFromUCallBack U_ICU_ENTRY_POINT_RENAME(ucnv_getFromUCallBack) +#define ucnv_getInvalidChars U_ICU_ENTRY_POINT_RENAME(ucnv_getInvalidChars) +#define ucnv_getInvalidUChars U_ICU_ENTRY_POINT_RENAME(ucnv_getInvalidUChars) +#define ucnv_getMaxCharSize U_ICU_ENTRY_POINT_RENAME(ucnv_getMaxCharSize) +#define ucnv_getMinCharSize U_ICU_ENTRY_POINT_RENAME(ucnv_getMinCharSize) +#define ucnv_getName U_ICU_ENTRY_POINT_RENAME(ucnv_getName) +#define ucnv_getNextUChar U_ICU_ENTRY_POINT_RENAME(ucnv_getNextUChar) +#define ucnv_getNonSurrogateUnicodeSet U_ICU_ENTRY_POINT_RENAME(ucnv_getNonSurrogateUnicodeSet) +#define ucnv_getPlatform U_ICU_ENTRY_POINT_RENAME(ucnv_getPlatform) +#define ucnv_getStandard U_ICU_ENTRY_POINT_RENAME(ucnv_getStandard) +#define ucnv_getStandardName U_ICU_ENTRY_POINT_RENAME(ucnv_getStandardName) +#define ucnv_getStarters U_ICU_ENTRY_POINT_RENAME(ucnv_getStarters) +#define ucnv_getSubstChars U_ICU_ENTRY_POINT_RENAME(ucnv_getSubstChars) +#define ucnv_getToUCallBack U_ICU_ENTRY_POINT_RENAME(ucnv_getToUCallBack) +#define ucnv_getType U_ICU_ENTRY_POINT_RENAME(ucnv_getType) +#define ucnv_getUnicodeSet U_ICU_ENTRY_POINT_RENAME(ucnv_getUnicodeSet) +#define ucnv_incrementRefCount U_ICU_ENTRY_POINT_RENAME(ucnv_incrementRefCount) +#define ucnv_io_countKnownConverters U_ICU_ENTRY_POINT_RENAME(ucnv_io_countKnownConverters) +#define ucnv_io_getConverterName U_ICU_ENTRY_POINT_RENAME(ucnv_io_getConverterName) +#define ucnv_io_stripASCIIForCompare U_ICU_ENTRY_POINT_RENAME(ucnv_io_stripASCIIForCompare) +#define ucnv_io_stripEBCDICForCompare U_ICU_ENTRY_POINT_RENAME(ucnv_io_stripEBCDICForCompare) +#define ucnv_isAmbiguous U_ICU_ENTRY_POINT_RENAME(ucnv_isAmbiguous) +#define ucnv_isFixedWidth U_ICU_ENTRY_POINT_RENAME(ucnv_isFixedWidth) +#define ucnv_load U_ICU_ENTRY_POINT_RENAME(ucnv_load) +#define ucnv_loadSharedData U_ICU_ENTRY_POINT_RENAME(ucnv_loadSharedData) +#define ucnv_open U_ICU_ENTRY_POINT_RENAME(ucnv_open) +#define ucnv_openAllNames U_ICU_ENTRY_POINT_RENAME(ucnv_openAllNames) +#define ucnv_openCCSID U_ICU_ENTRY_POINT_RENAME(ucnv_openCCSID) +#define ucnv_openPackage U_ICU_ENTRY_POINT_RENAME(ucnv_openPackage) +#define ucnv_openStandardNames U_ICU_ENTRY_POINT_RENAME(ucnv_openStandardNames) +#define ucnv_openU U_ICU_ENTRY_POINT_RENAME(ucnv_openU) +#define ucnv_reset U_ICU_ENTRY_POINT_RENAME(ucnv_reset) +#define ucnv_resetFromUnicode U_ICU_ENTRY_POINT_RENAME(ucnv_resetFromUnicode) +#define ucnv_resetToUnicode U_ICU_ENTRY_POINT_RENAME(ucnv_resetToUnicode) +#define ucnv_safeClone U_ICU_ENTRY_POINT_RENAME(ucnv_safeClone) +#define ucnv_setDefaultName U_ICU_ENTRY_POINT_RENAME(ucnv_setDefaultName) +#define ucnv_setFallback U_ICU_ENTRY_POINT_RENAME(ucnv_setFallback) +#define ucnv_setFromUCallBack U_ICU_ENTRY_POINT_RENAME(ucnv_setFromUCallBack) +#define ucnv_setSubstChars U_ICU_ENTRY_POINT_RENAME(ucnv_setSubstChars) +#define ucnv_setSubstString U_ICU_ENTRY_POINT_RENAME(ucnv_setSubstString) +#define ucnv_setToUCallBack U_ICU_ENTRY_POINT_RENAME(ucnv_setToUCallBack) +#define ucnv_swap U_ICU_ENTRY_POINT_RENAME(ucnv_swap) +#define ucnv_swapAliases U_ICU_ENTRY_POINT_RENAME(ucnv_swapAliases) +#define ucnv_toAlgorithmic U_ICU_ENTRY_POINT_RENAME(ucnv_toAlgorithmic) +#define ucnv_toUChars U_ICU_ENTRY_POINT_RENAME(ucnv_toUChars) +#define ucnv_toUCountPending U_ICU_ENTRY_POINT_RENAME(ucnv_toUCountPending) +#define ucnv_toUWriteCodePoint U_ICU_ENTRY_POINT_RENAME(ucnv_toUWriteCodePoint) +#define ucnv_toUWriteUChars U_ICU_ENTRY_POINT_RENAME(ucnv_toUWriteUChars) +#define ucnv_toUnicode U_ICU_ENTRY_POINT_RENAME(ucnv_toUnicode) +#define ucnv_unload U_ICU_ENTRY_POINT_RENAME(ucnv_unload) +#define ucnv_unloadSharedDataIfReady U_ICU_ENTRY_POINT_RENAME(ucnv_unloadSharedDataIfReady) +#define ucnv_usesFallback U_ICU_ENTRY_POINT_RENAME(ucnv_usesFallback) +#define ucnvsel_close U_ICU_ENTRY_POINT_RENAME(ucnvsel_close) +#define ucnvsel_open U_ICU_ENTRY_POINT_RENAME(ucnvsel_open) +#define ucnvsel_openFromSerialized U_ICU_ENTRY_POINT_RENAME(ucnvsel_openFromSerialized) +#define ucnvsel_selectForString U_ICU_ENTRY_POINT_RENAME(ucnvsel_selectForString) +#define ucnvsel_selectForUTF8 U_ICU_ENTRY_POINT_RENAME(ucnvsel_selectForUTF8) +#define ucnvsel_serialize U_ICU_ENTRY_POINT_RENAME(ucnvsel_serialize) +#define ucol_clone U_ICU_ENTRY_POINT_RENAME(ucol_clone) +#define ucol_cloneBinary U_ICU_ENTRY_POINT_RENAME(ucol_cloneBinary) +#define ucol_close U_ICU_ENTRY_POINT_RENAME(ucol_close) +#define ucol_closeElements U_ICU_ENTRY_POINT_RENAME(ucol_closeElements) +#define ucol_countAvailable U_ICU_ENTRY_POINT_RENAME(ucol_countAvailable) +#define ucol_equal U_ICU_ENTRY_POINT_RENAME(ucol_equal) +#define ucol_equals U_ICU_ENTRY_POINT_RENAME(ucol_equals) +#define ucol_getAttribute U_ICU_ENTRY_POINT_RENAME(ucol_getAttribute) +#define ucol_getAvailable U_ICU_ENTRY_POINT_RENAME(ucol_getAvailable) +#define ucol_getBound U_ICU_ENTRY_POINT_RENAME(ucol_getBound) +#define ucol_getContractions U_ICU_ENTRY_POINT_RENAME(ucol_getContractions) +#define ucol_getContractionsAndExpansions U_ICU_ENTRY_POINT_RENAME(ucol_getContractionsAndExpansions) +#define ucol_getDisplayName U_ICU_ENTRY_POINT_RENAME(ucol_getDisplayName) +#define ucol_getEquivalentReorderCodes U_ICU_ENTRY_POINT_RENAME(ucol_getEquivalentReorderCodes) +#define ucol_getFunctionalEquivalent U_ICU_ENTRY_POINT_RENAME(ucol_getFunctionalEquivalent) +#define ucol_getKeywordValues U_ICU_ENTRY_POINT_RENAME(ucol_getKeywordValues) +#define ucol_getKeywordValuesForLocale U_ICU_ENTRY_POINT_RENAME(ucol_getKeywordValuesForLocale) +#define ucol_getKeywords U_ICU_ENTRY_POINT_RENAME(ucol_getKeywords) +#define ucol_getLocale U_ICU_ENTRY_POINT_RENAME(ucol_getLocale) +#define ucol_getLocaleByType U_ICU_ENTRY_POINT_RENAME(ucol_getLocaleByType) +#define ucol_getMaxExpansion U_ICU_ENTRY_POINT_RENAME(ucol_getMaxExpansion) +#define ucol_getMaxVariable U_ICU_ENTRY_POINT_RENAME(ucol_getMaxVariable) +#define ucol_getOffset U_ICU_ENTRY_POINT_RENAME(ucol_getOffset) +#define ucol_getReorderCodes U_ICU_ENTRY_POINT_RENAME(ucol_getReorderCodes) +#define ucol_getRules U_ICU_ENTRY_POINT_RENAME(ucol_getRules) +#define ucol_getRulesEx U_ICU_ENTRY_POINT_RENAME(ucol_getRulesEx) +#define ucol_getShortDefinitionString U_ICU_ENTRY_POINT_RENAME(ucol_getShortDefinitionString) +#define ucol_getSortKey U_ICU_ENTRY_POINT_RENAME(ucol_getSortKey) +#define ucol_getStrength U_ICU_ENTRY_POINT_RENAME(ucol_getStrength) +#define ucol_getTailoredSet U_ICU_ENTRY_POINT_RENAME(ucol_getTailoredSet) +#define ucol_getUCAVersion U_ICU_ENTRY_POINT_RENAME(ucol_getUCAVersion) +#define ucol_getUnsafeSet U_ICU_ENTRY_POINT_RENAME(ucol_getUnsafeSet) +#define ucol_getVariableTop U_ICU_ENTRY_POINT_RENAME(ucol_getVariableTop) +#define ucol_getVersion U_ICU_ENTRY_POINT_RENAME(ucol_getVersion) +#define ucol_greater U_ICU_ENTRY_POINT_RENAME(ucol_greater) +#define ucol_greaterOrEqual U_ICU_ENTRY_POINT_RENAME(ucol_greaterOrEqual) +#define ucol_keyHashCode U_ICU_ENTRY_POINT_RENAME(ucol_keyHashCode) +#define ucol_looksLikeCollationBinary U_ICU_ENTRY_POINT_RENAME(ucol_looksLikeCollationBinary) +#define ucol_mergeSortkeys U_ICU_ENTRY_POINT_RENAME(ucol_mergeSortkeys) +#define ucol_next U_ICU_ENTRY_POINT_RENAME(ucol_next) +#define ucol_nextSortKeyPart U_ICU_ENTRY_POINT_RENAME(ucol_nextSortKeyPart) +#define ucol_normalizeShortDefinitionString U_ICU_ENTRY_POINT_RENAME(ucol_normalizeShortDefinitionString) +#define ucol_open U_ICU_ENTRY_POINT_RENAME(ucol_open) +#define ucol_openAvailableLocales U_ICU_ENTRY_POINT_RENAME(ucol_openAvailableLocales) +#define ucol_openBinary U_ICU_ENTRY_POINT_RENAME(ucol_openBinary) +#define ucol_openElements U_ICU_ENTRY_POINT_RENAME(ucol_openElements) +#define ucol_openFromShortString U_ICU_ENTRY_POINT_RENAME(ucol_openFromShortString) +#define ucol_openRules U_ICU_ENTRY_POINT_RENAME(ucol_openRules) +#define ucol_prepareShortStringOpen U_ICU_ENTRY_POINT_RENAME(ucol_prepareShortStringOpen) +#define ucol_previous U_ICU_ENTRY_POINT_RENAME(ucol_previous) +#define ucol_primaryOrder U_ICU_ENTRY_POINT_RENAME(ucol_primaryOrder) +#define ucol_reset U_ICU_ENTRY_POINT_RENAME(ucol_reset) +#define ucol_restoreVariableTop U_ICU_ENTRY_POINT_RENAME(ucol_restoreVariableTop) +#define ucol_safeClone U_ICU_ENTRY_POINT_RENAME(ucol_safeClone) +#define ucol_secondaryOrder U_ICU_ENTRY_POINT_RENAME(ucol_secondaryOrder) +#define ucol_setAttribute U_ICU_ENTRY_POINT_RENAME(ucol_setAttribute) +#define ucol_setMaxVariable U_ICU_ENTRY_POINT_RENAME(ucol_setMaxVariable) +#define ucol_setOffset U_ICU_ENTRY_POINT_RENAME(ucol_setOffset) +#define ucol_setReorderCodes U_ICU_ENTRY_POINT_RENAME(ucol_setReorderCodes) +#define ucol_setStrength U_ICU_ENTRY_POINT_RENAME(ucol_setStrength) +#define ucol_setText U_ICU_ENTRY_POINT_RENAME(ucol_setText) +#define ucol_setVariableTop U_ICU_ENTRY_POINT_RENAME(ucol_setVariableTop) +#define ucol_strcoll U_ICU_ENTRY_POINT_RENAME(ucol_strcoll) +#define ucol_strcollIter U_ICU_ENTRY_POINT_RENAME(ucol_strcollIter) +#define ucol_strcollUTF8 U_ICU_ENTRY_POINT_RENAME(ucol_strcollUTF8) +#define ucol_swap U_ICU_ENTRY_POINT_RENAME(ucol_swap) +#define ucol_swapInverseUCA U_ICU_ENTRY_POINT_RENAME(ucol_swapInverseUCA) +#define ucol_tertiaryOrder U_ICU_ENTRY_POINT_RENAME(ucol_tertiaryOrder) +#define ucpmap_get U_ICU_ENTRY_POINT_RENAME(ucpmap_get) +#define ucpmap_getRange U_ICU_ENTRY_POINT_RENAME(ucpmap_getRange) +#define ucptrie_close U_ICU_ENTRY_POINT_RENAME(ucptrie_close) +#define ucptrie_get U_ICU_ENTRY_POINT_RENAME(ucptrie_get) +#define ucptrie_getRange U_ICU_ENTRY_POINT_RENAME(ucptrie_getRange) +#define ucptrie_getType U_ICU_ENTRY_POINT_RENAME(ucptrie_getType) +#define ucptrie_getValueWidth U_ICU_ENTRY_POINT_RENAME(ucptrie_getValueWidth) +#define ucptrie_internalGetRange U_ICU_ENTRY_POINT_RENAME(ucptrie_internalGetRange) +#define ucptrie_internalSmallIndex U_ICU_ENTRY_POINT_RENAME(ucptrie_internalSmallIndex) +#define ucptrie_internalSmallU8Index U_ICU_ENTRY_POINT_RENAME(ucptrie_internalSmallU8Index) +#define ucptrie_internalU8PrevIndex U_ICU_ENTRY_POINT_RENAME(ucptrie_internalU8PrevIndex) +#define ucptrie_openFromBinary U_ICU_ENTRY_POINT_RENAME(ucptrie_openFromBinary) +#define ucptrie_swap U_ICU_ENTRY_POINT_RENAME(ucptrie_swap) +#define ucptrie_toBinary U_ICU_ENTRY_POINT_RENAME(ucptrie_toBinary) +#define ucsdet_close U_ICU_ENTRY_POINT_RENAME(ucsdet_close) +#define ucsdet_detect U_ICU_ENTRY_POINT_RENAME(ucsdet_detect) +#define ucsdet_detectAll U_ICU_ENTRY_POINT_RENAME(ucsdet_detectAll) +#define ucsdet_enableInputFilter U_ICU_ENTRY_POINT_RENAME(ucsdet_enableInputFilter) +#define ucsdet_getAllDetectableCharsets U_ICU_ENTRY_POINT_RENAME(ucsdet_getAllDetectableCharsets) +#define ucsdet_getConfidence U_ICU_ENTRY_POINT_RENAME(ucsdet_getConfidence) +#define ucsdet_getDetectableCharsets U_ICU_ENTRY_POINT_RENAME(ucsdet_getDetectableCharsets) +#define ucsdet_getLanguage U_ICU_ENTRY_POINT_RENAME(ucsdet_getLanguage) +#define ucsdet_getName U_ICU_ENTRY_POINT_RENAME(ucsdet_getName) +#define ucsdet_getUChars U_ICU_ENTRY_POINT_RENAME(ucsdet_getUChars) +#define ucsdet_isInputFilterEnabled U_ICU_ENTRY_POINT_RENAME(ucsdet_isInputFilterEnabled) +#define ucsdet_open U_ICU_ENTRY_POINT_RENAME(ucsdet_open) +#define ucsdet_setDeclaredEncoding U_ICU_ENTRY_POINT_RENAME(ucsdet_setDeclaredEncoding) +#define ucsdet_setDetectableCharset U_ICU_ENTRY_POINT_RENAME(ucsdet_setDetectableCharset) +#define ucsdet_setText U_ICU_ENTRY_POINT_RENAME(ucsdet_setText) +#define ucurr_countCurrencies U_ICU_ENTRY_POINT_RENAME(ucurr_countCurrencies) +#define ucurr_forLocale U_ICU_ENTRY_POINT_RENAME(ucurr_forLocale) +#define ucurr_forLocaleAndDate U_ICU_ENTRY_POINT_RENAME(ucurr_forLocaleAndDate) +#define ucurr_getDefaultFractionDigits U_ICU_ENTRY_POINT_RENAME(ucurr_getDefaultFractionDigits) +#define ucurr_getDefaultFractionDigitsForUsage U_ICU_ENTRY_POINT_RENAME(ucurr_getDefaultFractionDigitsForUsage) +#define ucurr_getKeywordValuesForLocale U_ICU_ENTRY_POINT_RENAME(ucurr_getKeywordValuesForLocale) +#define ucurr_getName U_ICU_ENTRY_POINT_RENAME(ucurr_getName) +#define ucurr_getNumericCode U_ICU_ENTRY_POINT_RENAME(ucurr_getNumericCode) +#define ucurr_getPluralName U_ICU_ENTRY_POINT_RENAME(ucurr_getPluralName) +#define ucurr_getRoundingIncrement U_ICU_ENTRY_POINT_RENAME(ucurr_getRoundingIncrement) +#define ucurr_getRoundingIncrementForUsage U_ICU_ENTRY_POINT_RENAME(ucurr_getRoundingIncrementForUsage) +#define ucurr_isAvailable U_ICU_ENTRY_POINT_RENAME(ucurr_isAvailable) +#define ucurr_openISOCurrencies U_ICU_ENTRY_POINT_RENAME(ucurr_openISOCurrencies) +#define ucurr_register U_ICU_ENTRY_POINT_RENAME(ucurr_register) +#define ucurr_unregister U_ICU_ENTRY_POINT_RENAME(ucurr_unregister) +#define udat_adoptNumberFormat U_ICU_ENTRY_POINT_RENAME(udat_adoptNumberFormat) +#define udat_adoptNumberFormatForFields U_ICU_ENTRY_POINT_RENAME(udat_adoptNumberFormatForFields) +#define udat_applyPattern U_ICU_ENTRY_POINT_RENAME(udat_applyPattern) +#define udat_applyPatternRelative U_ICU_ENTRY_POINT_RENAME(udat_applyPatternRelative) +#define udat_clone U_ICU_ENTRY_POINT_RENAME(udat_clone) +#define udat_close U_ICU_ENTRY_POINT_RENAME(udat_close) +#define udat_countAvailable U_ICU_ENTRY_POINT_RENAME(udat_countAvailable) +#define udat_countSymbols U_ICU_ENTRY_POINT_RENAME(udat_countSymbols) +#define udat_format U_ICU_ENTRY_POINT_RENAME(udat_format) +#define udat_formatCalendar U_ICU_ENTRY_POINT_RENAME(udat_formatCalendar) +#define udat_formatCalendarForFields U_ICU_ENTRY_POINT_RENAME(udat_formatCalendarForFields) +#define udat_formatForFields U_ICU_ENTRY_POINT_RENAME(udat_formatForFields) +#define udat_get2DigitYearStart U_ICU_ENTRY_POINT_RENAME(udat_get2DigitYearStart) +#define udat_getAvailable U_ICU_ENTRY_POINT_RENAME(udat_getAvailable) +#define udat_getBooleanAttribute U_ICU_ENTRY_POINT_RENAME(udat_getBooleanAttribute) +#define udat_getCalendar U_ICU_ENTRY_POINT_RENAME(udat_getCalendar) +#define udat_getContext U_ICU_ENTRY_POINT_RENAME(udat_getContext) +#define udat_getLocaleByType U_ICU_ENTRY_POINT_RENAME(udat_getLocaleByType) +#define udat_getNumberFormat U_ICU_ENTRY_POINT_RENAME(udat_getNumberFormat) +#define udat_getNumberFormatForField U_ICU_ENTRY_POINT_RENAME(udat_getNumberFormatForField) +#define udat_getSymbols U_ICU_ENTRY_POINT_RENAME(udat_getSymbols) +#define udat_isLenient U_ICU_ENTRY_POINT_RENAME(udat_isLenient) +#define udat_open U_ICU_ENTRY_POINT_RENAME(udat_open) +#define udat_parse U_ICU_ENTRY_POINT_RENAME(udat_parse) +#define udat_parseCalendar U_ICU_ENTRY_POINT_RENAME(udat_parseCalendar) +#define udat_registerOpener U_ICU_ENTRY_POINT_RENAME(udat_registerOpener) +#define udat_set2DigitYearStart U_ICU_ENTRY_POINT_RENAME(udat_set2DigitYearStart) +#define udat_setBooleanAttribute U_ICU_ENTRY_POINT_RENAME(udat_setBooleanAttribute) +#define udat_setCalendar U_ICU_ENTRY_POINT_RENAME(udat_setCalendar) +#define udat_setContext U_ICU_ENTRY_POINT_RENAME(udat_setContext) +#define udat_setLenient U_ICU_ENTRY_POINT_RENAME(udat_setLenient) +#define udat_setNumberFormat U_ICU_ENTRY_POINT_RENAME(udat_setNumberFormat) +#define udat_setSymbols U_ICU_ENTRY_POINT_RENAME(udat_setSymbols) +#define udat_toCalendarDateField U_ICU_ENTRY_POINT_RENAME(udat_toCalendarDateField) +#define udat_toPattern U_ICU_ENTRY_POINT_RENAME(udat_toPattern) +#define udat_toPatternRelativeDate U_ICU_ENTRY_POINT_RENAME(udat_toPatternRelativeDate) +#define udat_toPatternRelativeTime U_ICU_ENTRY_POINT_RENAME(udat_toPatternRelativeTime) +#define udat_unregisterOpener U_ICU_ENTRY_POINT_RENAME(udat_unregisterOpener) +#define udata_checkCommonData U_ICU_ENTRY_POINT_RENAME(udata_checkCommonData) +#define udata_close U_ICU_ENTRY_POINT_RENAME(udata_close) +#define udata_closeSwapper U_ICU_ENTRY_POINT_RENAME(udata_closeSwapper) +#define udata_getHeaderSize U_ICU_ENTRY_POINT_RENAME(udata_getHeaderSize) +#define udata_getInfo U_ICU_ENTRY_POINT_RENAME(udata_getInfo) +#define udata_getInfoSize U_ICU_ENTRY_POINT_RENAME(udata_getInfoSize) +#define udata_getLength U_ICU_ENTRY_POINT_RENAME(udata_getLength) +#define udata_getMemory U_ICU_ENTRY_POINT_RENAME(udata_getMemory) +#define udata_getRawMemory U_ICU_ENTRY_POINT_RENAME(udata_getRawMemory) +#define udata_open U_ICU_ENTRY_POINT_RENAME(udata_open) +#define udata_openChoice U_ICU_ENTRY_POINT_RENAME(udata_openChoice) +#define udata_openSwapper U_ICU_ENTRY_POINT_RENAME(udata_openSwapper) +#define udata_openSwapperForInputData U_ICU_ENTRY_POINT_RENAME(udata_openSwapperForInputData) +#define udata_printError U_ICU_ENTRY_POINT_RENAME(udata_printError) +#define udata_readInt16 U_ICU_ENTRY_POINT_RENAME(udata_readInt16) +#define udata_readInt32 U_ICU_ENTRY_POINT_RENAME(udata_readInt32) +#define udata_setAppData U_ICU_ENTRY_POINT_RENAME(udata_setAppData) +#define udata_setCommonData U_ICU_ENTRY_POINT_RENAME(udata_setCommonData) +#define udata_setFileAccess U_ICU_ENTRY_POINT_RENAME(udata_setFileAccess) +#define udata_swapDataHeader U_ICU_ENTRY_POINT_RENAME(udata_swapDataHeader) +#define udata_swapInvStringBlock U_ICU_ENTRY_POINT_RENAME(udata_swapInvStringBlock) +#define udatpg_addPattern U_ICU_ENTRY_POINT_RENAME(udatpg_addPattern) +#define udatpg_clone U_ICU_ENTRY_POINT_RENAME(udatpg_clone) +#define udatpg_close U_ICU_ENTRY_POINT_RENAME(udatpg_close) +#define udatpg_getAppendItemFormat U_ICU_ENTRY_POINT_RENAME(udatpg_getAppendItemFormat) +#define udatpg_getAppendItemName U_ICU_ENTRY_POINT_RENAME(udatpg_getAppendItemName) +#define udatpg_getBaseSkeleton U_ICU_ENTRY_POINT_RENAME(udatpg_getBaseSkeleton) +#define udatpg_getBestPattern U_ICU_ENTRY_POINT_RENAME(udatpg_getBestPattern) +#define udatpg_getBestPatternWithOptions U_ICU_ENTRY_POINT_RENAME(udatpg_getBestPatternWithOptions) +#define udatpg_getDateTimeFormat U_ICU_ENTRY_POINT_RENAME(udatpg_getDateTimeFormat) +#define udatpg_getDateTimeFormatForStyle U_ICU_ENTRY_POINT_RENAME(udatpg_getDateTimeFormatForStyle) +#define udatpg_getDecimal U_ICU_ENTRY_POINT_RENAME(udatpg_getDecimal) +#define udatpg_getDefaultHourCycle U_ICU_ENTRY_POINT_RENAME(udatpg_getDefaultHourCycle) +#define udatpg_getFieldDisplayName U_ICU_ENTRY_POINT_RENAME(udatpg_getFieldDisplayName) +#define udatpg_getPatternForSkeleton U_ICU_ENTRY_POINT_RENAME(udatpg_getPatternForSkeleton) +#define udatpg_getSkeleton U_ICU_ENTRY_POINT_RENAME(udatpg_getSkeleton) +#define udatpg_open U_ICU_ENTRY_POINT_RENAME(udatpg_open) +#define udatpg_openBaseSkeletons U_ICU_ENTRY_POINT_RENAME(udatpg_openBaseSkeletons) +#define udatpg_openEmpty U_ICU_ENTRY_POINT_RENAME(udatpg_openEmpty) +#define udatpg_openSkeletons U_ICU_ENTRY_POINT_RENAME(udatpg_openSkeletons) +#define udatpg_replaceFieldTypes U_ICU_ENTRY_POINT_RENAME(udatpg_replaceFieldTypes) +#define udatpg_replaceFieldTypesWithOptions U_ICU_ENTRY_POINT_RENAME(udatpg_replaceFieldTypesWithOptions) +#define udatpg_setAppendItemFormat U_ICU_ENTRY_POINT_RENAME(udatpg_setAppendItemFormat) +#define udatpg_setAppendItemName U_ICU_ENTRY_POINT_RENAME(udatpg_setAppendItemName) +#define udatpg_setDateTimeFormat U_ICU_ENTRY_POINT_RENAME(udatpg_setDateTimeFormat) +#define udatpg_setDateTimeFormatForStyle U_ICU_ENTRY_POINT_RENAME(udatpg_setDateTimeFormatForStyle) +#define udatpg_setDecimal U_ICU_ENTRY_POINT_RENAME(udatpg_setDecimal) +#define udict_swap U_ICU_ENTRY_POINT_RENAME(udict_swap) +#define udispopt_fromGrammaticalCaseIdentifier U_ICU_ENTRY_POINT_RENAME(udispopt_fromGrammaticalCaseIdentifier) +#define udispopt_fromNounClassIdentifier U_ICU_ENTRY_POINT_RENAME(udispopt_fromNounClassIdentifier) +#define udispopt_fromPluralCategoryIdentifier U_ICU_ENTRY_POINT_RENAME(udispopt_fromPluralCategoryIdentifier) +#define udispopt_getGrammaticalCaseIdentifier U_ICU_ENTRY_POINT_RENAME(udispopt_getGrammaticalCaseIdentifier) +#define udispopt_getNounClassIdentifier U_ICU_ENTRY_POINT_RENAME(udispopt_getNounClassIdentifier) +#define udispopt_getPluralCategoryIdentifier U_ICU_ENTRY_POINT_RENAME(udispopt_getPluralCategoryIdentifier) +#define udtitvfmt_close U_ICU_ENTRY_POINT_RENAME(udtitvfmt_close) +#define udtitvfmt_closeResult U_ICU_ENTRY_POINT_RENAME(udtitvfmt_closeResult) +#define udtitvfmt_format U_ICU_ENTRY_POINT_RENAME(udtitvfmt_format) +#define udtitvfmt_formatCalendarToResult U_ICU_ENTRY_POINT_RENAME(udtitvfmt_formatCalendarToResult) +#define udtitvfmt_formatToResult U_ICU_ENTRY_POINT_RENAME(udtitvfmt_formatToResult) +#define udtitvfmt_getContext U_ICU_ENTRY_POINT_RENAME(udtitvfmt_getContext) +#define udtitvfmt_open U_ICU_ENTRY_POINT_RENAME(udtitvfmt_open) +#define udtitvfmt_openResult U_ICU_ENTRY_POINT_RENAME(udtitvfmt_openResult) +#define udtitvfmt_resultAsValue U_ICU_ENTRY_POINT_RENAME(udtitvfmt_resultAsValue) +#define udtitvfmt_setContext U_ICU_ENTRY_POINT_RENAME(udtitvfmt_setContext) +#define uenum_close U_ICU_ENTRY_POINT_RENAME(uenum_close) +#define uenum_count U_ICU_ENTRY_POINT_RENAME(uenum_count) +#define uenum_next U_ICU_ENTRY_POINT_RENAME(uenum_next) +#define uenum_nextDefault U_ICU_ENTRY_POINT_RENAME(uenum_nextDefault) +#define uenum_openCharStringsEnumeration U_ICU_ENTRY_POINT_RENAME(uenum_openCharStringsEnumeration) +#define uenum_openFromStringEnumeration U_ICU_ENTRY_POINT_RENAME(uenum_openFromStringEnumeration) +#define uenum_openUCharStringsEnumeration U_ICU_ENTRY_POINT_RENAME(uenum_openUCharStringsEnumeration) +#define uenum_reset U_ICU_ENTRY_POINT_RENAME(uenum_reset) +#define uenum_unext U_ICU_ENTRY_POINT_RENAME(uenum_unext) +#define uenum_unextDefault U_ICU_ENTRY_POINT_RENAME(uenum_unextDefault) +#define ufieldpositer_close U_ICU_ENTRY_POINT_RENAME(ufieldpositer_close) +#define ufieldpositer_next U_ICU_ENTRY_POINT_RENAME(ufieldpositer_next) +#define ufieldpositer_open U_ICU_ENTRY_POINT_RENAME(ufieldpositer_open) +#define ufile_getch U_ICU_ENTRY_POINT_RENAME(ufile_getch) +#define ufile_getch32 U_ICU_ENTRY_POINT_RENAME(ufile_getch32) +#define ufmt_close U_ICU_ENTRY_POINT_RENAME(ufmt_close) +#define ufmt_getArrayItemByIndex U_ICU_ENTRY_POINT_RENAME(ufmt_getArrayItemByIndex) +#define ufmt_getArrayLength U_ICU_ENTRY_POINT_RENAME(ufmt_getArrayLength) +#define ufmt_getDate U_ICU_ENTRY_POINT_RENAME(ufmt_getDate) +#define ufmt_getDecNumChars U_ICU_ENTRY_POINT_RENAME(ufmt_getDecNumChars) +#define ufmt_getDouble U_ICU_ENTRY_POINT_RENAME(ufmt_getDouble) +#define ufmt_getInt64 U_ICU_ENTRY_POINT_RENAME(ufmt_getInt64) +#define ufmt_getLong U_ICU_ENTRY_POINT_RENAME(ufmt_getLong) +#define ufmt_getObject U_ICU_ENTRY_POINT_RENAME(ufmt_getObject) +#define ufmt_getType U_ICU_ENTRY_POINT_RENAME(ufmt_getType) +#define ufmt_getUChars U_ICU_ENTRY_POINT_RENAME(ufmt_getUChars) +#define ufmt_isNumeric U_ICU_ENTRY_POINT_RENAME(ufmt_isNumeric) +#define ufmt_open U_ICU_ENTRY_POINT_RENAME(ufmt_open) +#define ufmtval_getString U_ICU_ENTRY_POINT_RENAME(ufmtval_getString) +#define ufmtval_nextPosition U_ICU_ENTRY_POINT_RENAME(ufmtval_nextPosition) +#define ugender_getInstance U_ICU_ENTRY_POINT_RENAME(ugender_getInstance) +#define ugender_getListGender U_ICU_ENTRY_POINT_RENAME(ugender_getListGender) +#define uhash_close U_ICU_ENTRY_POINT_RENAME(uhash_close) +#define uhash_compareCaselessUnicodeString U_ICU_ENTRY_POINT_RENAME(uhash_compareCaselessUnicodeString) +#define uhash_compareChars U_ICU_ENTRY_POINT_RENAME(uhash_compareChars) +#define uhash_compareIChars U_ICU_ENTRY_POINT_RENAME(uhash_compareIChars) +#define uhash_compareLong U_ICU_ENTRY_POINT_RENAME(uhash_compareLong) +#define uhash_compareScriptSet U_ICU_ENTRY_POINT_RENAME(uhash_compareScriptSet) +#define uhash_compareUChars U_ICU_ENTRY_POINT_RENAME(uhash_compareUChars) +#define uhash_compareUnicodeString U_ICU_ENTRY_POINT_RENAME(uhash_compareUnicodeString) +#define uhash_containsKey U_ICU_ENTRY_POINT_RENAME(uhash_containsKey) +#define uhash_count U_ICU_ENTRY_POINT_RENAME(uhash_count) +#define uhash_deleteHashtable U_ICU_ENTRY_POINT_RENAME(uhash_deleteHashtable) +#define uhash_deleteScriptSet U_ICU_ENTRY_POINT_RENAME(uhash_deleteScriptSet) +#define uhash_equals U_ICU_ENTRY_POINT_RENAME(uhash_equals) +#define uhash_equalsScriptSet U_ICU_ENTRY_POINT_RENAME(uhash_equalsScriptSet) +#define uhash_find U_ICU_ENTRY_POINT_RENAME(uhash_find) +#define uhash_get U_ICU_ENTRY_POINT_RENAME(uhash_get) +#define uhash_geti U_ICU_ENTRY_POINT_RENAME(uhash_geti) +#define uhash_getiAndFound U_ICU_ENTRY_POINT_RENAME(uhash_getiAndFound) +#define uhash_hashCaselessUnicodeString U_ICU_ENTRY_POINT_RENAME(uhash_hashCaselessUnicodeString) +#define uhash_hashChars U_ICU_ENTRY_POINT_RENAME(uhash_hashChars) +#define uhash_hashIChars U_ICU_ENTRY_POINT_RENAME(uhash_hashIChars) +#define uhash_hashLong U_ICU_ENTRY_POINT_RENAME(uhash_hashLong) +#define uhash_hashScriptSet U_ICU_ENTRY_POINT_RENAME(uhash_hashScriptSet) +#define uhash_hashUChars U_ICU_ENTRY_POINT_RENAME(uhash_hashUChars) +#define uhash_hashUnicodeString U_ICU_ENTRY_POINT_RENAME(uhash_hashUnicodeString) +#define uhash_icontainsKey U_ICU_ENTRY_POINT_RENAME(uhash_icontainsKey) +#define uhash_iget U_ICU_ENTRY_POINT_RENAME(uhash_iget) +#define uhash_igeti U_ICU_ENTRY_POINT_RENAME(uhash_igeti) +#define uhash_igetiAndFound U_ICU_ENTRY_POINT_RENAME(uhash_igetiAndFound) +#define uhash_init U_ICU_ENTRY_POINT_RENAME(uhash_init) +#define uhash_initSize U_ICU_ENTRY_POINT_RENAME(uhash_initSize) +#define uhash_iput U_ICU_ENTRY_POINT_RENAME(uhash_iput) +#define uhash_iputi U_ICU_ENTRY_POINT_RENAME(uhash_iputi) +#define uhash_iputiAllowZero U_ICU_ENTRY_POINT_RENAME(uhash_iputiAllowZero) +#define uhash_iremove U_ICU_ENTRY_POINT_RENAME(uhash_iremove) +#define uhash_iremovei U_ICU_ENTRY_POINT_RENAME(uhash_iremovei) +#define uhash_nextElement U_ICU_ENTRY_POINT_RENAME(uhash_nextElement) +#define uhash_open U_ICU_ENTRY_POINT_RENAME(uhash_open) +#define uhash_openSize U_ICU_ENTRY_POINT_RENAME(uhash_openSize) +#define uhash_put U_ICU_ENTRY_POINT_RENAME(uhash_put) +#define uhash_puti U_ICU_ENTRY_POINT_RENAME(uhash_puti) +#define uhash_putiAllowZero U_ICU_ENTRY_POINT_RENAME(uhash_putiAllowZero) +#define uhash_remove U_ICU_ENTRY_POINT_RENAME(uhash_remove) +#define uhash_removeAll U_ICU_ENTRY_POINT_RENAME(uhash_removeAll) +#define uhash_removeElement U_ICU_ENTRY_POINT_RENAME(uhash_removeElement) +#define uhash_removei U_ICU_ENTRY_POINT_RENAME(uhash_removei) +#define uhash_setKeyComparator U_ICU_ENTRY_POINT_RENAME(uhash_setKeyComparator) +#define uhash_setKeyDeleter U_ICU_ENTRY_POINT_RENAME(uhash_setKeyDeleter) +#define uhash_setKeyHasher U_ICU_ENTRY_POINT_RENAME(uhash_setKeyHasher) +#define uhash_setResizePolicy U_ICU_ENTRY_POINT_RENAME(uhash_setResizePolicy) +#define uhash_setValueComparator U_ICU_ENTRY_POINT_RENAME(uhash_setValueComparator) +#define uhash_setValueDeleter U_ICU_ENTRY_POINT_RENAME(uhash_setValueDeleter) +#define uidna_IDNToASCII U_ICU_ENTRY_POINT_RENAME(uidna_IDNToASCII) +#define uidna_IDNToUnicode U_ICU_ENTRY_POINT_RENAME(uidna_IDNToUnicode) +#define uidna_close U_ICU_ENTRY_POINT_RENAME(uidna_close) +#define uidna_compare U_ICU_ENTRY_POINT_RENAME(uidna_compare) +#define uidna_labelToASCII U_ICU_ENTRY_POINT_RENAME(uidna_labelToASCII) +#define uidna_labelToASCII_UTF8 U_ICU_ENTRY_POINT_RENAME(uidna_labelToASCII_UTF8) +#define uidna_labelToUnicode U_ICU_ENTRY_POINT_RENAME(uidna_labelToUnicode) +#define uidna_labelToUnicodeUTF8 U_ICU_ENTRY_POINT_RENAME(uidna_labelToUnicodeUTF8) +#define uidna_nameToASCII U_ICU_ENTRY_POINT_RENAME(uidna_nameToASCII) +#define uidna_nameToASCII_UTF8 U_ICU_ENTRY_POINT_RENAME(uidna_nameToASCII_UTF8) +#define uidna_nameToUnicode U_ICU_ENTRY_POINT_RENAME(uidna_nameToUnicode) +#define uidna_nameToUnicodeUTF8 U_ICU_ENTRY_POINT_RENAME(uidna_nameToUnicodeUTF8) +#define uidna_openUTS46 U_ICU_ENTRY_POINT_RENAME(uidna_openUTS46) +#define uidna_toASCII U_ICU_ENTRY_POINT_RENAME(uidna_toASCII) +#define uidna_toUnicode U_ICU_ENTRY_POINT_RENAME(uidna_toUnicode) +#define uiter_current32 U_ICU_ENTRY_POINT_RENAME(uiter_current32) +#define uiter_getState U_ICU_ENTRY_POINT_RENAME(uiter_getState) +#define uiter_next32 U_ICU_ENTRY_POINT_RENAME(uiter_next32) +#define uiter_previous32 U_ICU_ENTRY_POINT_RENAME(uiter_previous32) +#define uiter_setCharacterIterator U_ICU_ENTRY_POINT_RENAME(uiter_setCharacterIterator) +#define uiter_setReplaceable U_ICU_ENTRY_POINT_RENAME(uiter_setReplaceable) +#define uiter_setState U_ICU_ENTRY_POINT_RENAME(uiter_setState) +#define uiter_setString U_ICU_ENTRY_POINT_RENAME(uiter_setString) +#define uiter_setUTF16BE U_ICU_ENTRY_POINT_RENAME(uiter_setUTF16BE) +#define uiter_setUTF8 U_ICU_ENTRY_POINT_RENAME(uiter_setUTF8) +#define uldn_close U_ICU_ENTRY_POINT_RENAME(uldn_close) +#define uldn_getContext U_ICU_ENTRY_POINT_RENAME(uldn_getContext) +#define uldn_getDialectHandling U_ICU_ENTRY_POINT_RENAME(uldn_getDialectHandling) +#define uldn_getLocale U_ICU_ENTRY_POINT_RENAME(uldn_getLocale) +#define uldn_keyDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_keyDisplayName) +#define uldn_keyValueDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_keyValueDisplayName) +#define uldn_languageDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_languageDisplayName) +#define uldn_localeDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_localeDisplayName) +#define uldn_open U_ICU_ENTRY_POINT_RENAME(uldn_open) +#define uldn_openForContext U_ICU_ENTRY_POINT_RENAME(uldn_openForContext) +#define uldn_regionDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_regionDisplayName) +#define uldn_scriptCodeDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_scriptCodeDisplayName) +#define uldn_scriptDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_scriptDisplayName) +#define uldn_variantDisplayName U_ICU_ENTRY_POINT_RENAME(uldn_variantDisplayName) +#define ulist_addItemBeginList U_ICU_ENTRY_POINT_RENAME(ulist_addItemBeginList) +#define ulist_addItemEndList U_ICU_ENTRY_POINT_RENAME(ulist_addItemEndList) +#define ulist_close_keyword_values_iterator U_ICU_ENTRY_POINT_RENAME(ulist_close_keyword_values_iterator) +#define ulist_containsString U_ICU_ENTRY_POINT_RENAME(ulist_containsString) +#define ulist_count_keyword_values U_ICU_ENTRY_POINT_RENAME(ulist_count_keyword_values) +#define ulist_createEmptyList U_ICU_ENTRY_POINT_RENAME(ulist_createEmptyList) +#define ulist_deleteList U_ICU_ENTRY_POINT_RENAME(ulist_deleteList) +#define ulist_getListFromEnum U_ICU_ENTRY_POINT_RENAME(ulist_getListFromEnum) +#define ulist_getListSize U_ICU_ENTRY_POINT_RENAME(ulist_getListSize) +#define ulist_getNext U_ICU_ENTRY_POINT_RENAME(ulist_getNext) +#define ulist_next_keyword_value U_ICU_ENTRY_POINT_RENAME(ulist_next_keyword_value) +#define ulist_removeString U_ICU_ENTRY_POINT_RENAME(ulist_removeString) +#define ulist_resetList U_ICU_ENTRY_POINT_RENAME(ulist_resetList) +#define ulist_reset_keyword_values_iterator U_ICU_ENTRY_POINT_RENAME(ulist_reset_keyword_values_iterator) +#define ulistfmt_close U_ICU_ENTRY_POINT_RENAME(ulistfmt_close) +#define ulistfmt_closeResult U_ICU_ENTRY_POINT_RENAME(ulistfmt_closeResult) +#define ulistfmt_format U_ICU_ENTRY_POINT_RENAME(ulistfmt_format) +#define ulistfmt_formatStringsToResult U_ICU_ENTRY_POINT_RENAME(ulistfmt_formatStringsToResult) +#define ulistfmt_open U_ICU_ENTRY_POINT_RENAME(ulistfmt_open) +#define ulistfmt_openForType U_ICU_ENTRY_POINT_RENAME(ulistfmt_openForType) +#define ulistfmt_openResult U_ICU_ENTRY_POINT_RENAME(ulistfmt_openResult) +#define ulistfmt_resultAsValue U_ICU_ENTRY_POINT_RENAME(ulistfmt_resultAsValue) +#define uloc_acceptLanguage U_ICU_ENTRY_POINT_RENAME(uloc_acceptLanguage) +#define uloc_acceptLanguageFromHTTP U_ICU_ENTRY_POINT_RENAME(uloc_acceptLanguageFromHTTP) +#define uloc_addLikelySubtags U_ICU_ENTRY_POINT_RENAME(uloc_addLikelySubtags) +#define uloc_canonicalize U_ICU_ENTRY_POINT_RENAME(uloc_canonicalize) +#define uloc_countAvailable U_ICU_ENTRY_POINT_RENAME(uloc_countAvailable) +#define uloc_forLanguageTag U_ICU_ENTRY_POINT_RENAME(uloc_forLanguageTag) +#define uloc_getAvailable U_ICU_ENTRY_POINT_RENAME(uloc_getAvailable) +#define uloc_getBaseName U_ICU_ENTRY_POINT_RENAME(uloc_getBaseName) +#define uloc_getCharacterOrientation U_ICU_ENTRY_POINT_RENAME(uloc_getCharacterOrientation) +#define uloc_getCountry U_ICU_ENTRY_POINT_RENAME(uloc_getCountry) +#define uloc_getCurrentCountryID U_ICU_ENTRY_POINT_RENAME(uloc_getCurrentCountryID) +#define uloc_getCurrentLanguageID U_ICU_ENTRY_POINT_RENAME(uloc_getCurrentLanguageID) +#define uloc_getDefault U_ICU_ENTRY_POINT_RENAME(uloc_getDefault) +#define uloc_getDisplayCountry U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayCountry) +#define uloc_getDisplayKeyword U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayKeyword) +#define uloc_getDisplayKeywordValue U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayKeywordValue) +#define uloc_getDisplayLanguage U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayLanguage) +#define uloc_getDisplayName U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayName) +#define uloc_getDisplayScript U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayScript) +#define uloc_getDisplayVariant U_ICU_ENTRY_POINT_RENAME(uloc_getDisplayVariant) +#define uloc_getISO3Country U_ICU_ENTRY_POINT_RENAME(uloc_getISO3Country) +#define uloc_getISO3Language U_ICU_ENTRY_POINT_RENAME(uloc_getISO3Language) +#define uloc_getISOCountries U_ICU_ENTRY_POINT_RENAME(uloc_getISOCountries) +#define uloc_getISOLanguages U_ICU_ENTRY_POINT_RENAME(uloc_getISOLanguages) +#define uloc_getKeywordValue U_ICU_ENTRY_POINT_RENAME(uloc_getKeywordValue) +#define uloc_getLCID U_ICU_ENTRY_POINT_RENAME(uloc_getLCID) +#define uloc_getLanguage U_ICU_ENTRY_POINT_RENAME(uloc_getLanguage) +#define uloc_getLineOrientation U_ICU_ENTRY_POINT_RENAME(uloc_getLineOrientation) +#define uloc_getLocaleForLCID U_ICU_ENTRY_POINT_RENAME(uloc_getLocaleForLCID) +#define uloc_getName U_ICU_ENTRY_POINT_RENAME(uloc_getName) +#define uloc_getParent U_ICU_ENTRY_POINT_RENAME(uloc_getParent) +#define uloc_getScript U_ICU_ENTRY_POINT_RENAME(uloc_getScript) +#define uloc_getTableStringWithFallback U_ICU_ENTRY_POINT_RENAME(uloc_getTableStringWithFallback) +#define uloc_getVariant U_ICU_ENTRY_POINT_RENAME(uloc_getVariant) +#define uloc_isRightToLeft U_ICU_ENTRY_POINT_RENAME(uloc_isRightToLeft) +#define uloc_minimizeSubtags U_ICU_ENTRY_POINT_RENAME(uloc_minimizeSubtags) +#define uloc_openAvailableByType U_ICU_ENTRY_POINT_RENAME(uloc_openAvailableByType) +#define uloc_openKeywordList U_ICU_ENTRY_POINT_RENAME(uloc_openKeywordList) +#define uloc_openKeywords U_ICU_ENTRY_POINT_RENAME(uloc_openKeywords) +#define uloc_setDefault U_ICU_ENTRY_POINT_RENAME(uloc_setDefault) +#define uloc_setKeywordValue U_ICU_ENTRY_POINT_RENAME(uloc_setKeywordValue) +#define uloc_toLanguageTag U_ICU_ENTRY_POINT_RENAME(uloc_toLanguageTag) +#define uloc_toLegacyKey U_ICU_ENTRY_POINT_RENAME(uloc_toLegacyKey) +#define uloc_toLegacyType U_ICU_ENTRY_POINT_RENAME(uloc_toLegacyType) +#define uloc_toUnicodeLocaleKey U_ICU_ENTRY_POINT_RENAME(uloc_toUnicodeLocaleKey) +#define uloc_toUnicodeLocaleType U_ICU_ENTRY_POINT_RENAME(uloc_toUnicodeLocaleType) +#define ulocdata_close U_ICU_ENTRY_POINT_RENAME(ulocdata_close) +#define ulocdata_getCLDRVersion U_ICU_ENTRY_POINT_RENAME(ulocdata_getCLDRVersion) +#define ulocdata_getDelimiter U_ICU_ENTRY_POINT_RENAME(ulocdata_getDelimiter) +#define ulocdata_getExemplarSet U_ICU_ENTRY_POINT_RENAME(ulocdata_getExemplarSet) +#define ulocdata_getLocaleDisplayPattern U_ICU_ENTRY_POINT_RENAME(ulocdata_getLocaleDisplayPattern) +#define ulocdata_getLocaleSeparator U_ICU_ENTRY_POINT_RENAME(ulocdata_getLocaleSeparator) +#define ulocdata_getMeasurementSystem U_ICU_ENTRY_POINT_RENAME(ulocdata_getMeasurementSystem) +#define ulocdata_getNoSubstitute U_ICU_ENTRY_POINT_RENAME(ulocdata_getNoSubstitute) +#define ulocdata_getPaperSize U_ICU_ENTRY_POINT_RENAME(ulocdata_getPaperSize) +#define ulocdata_open U_ICU_ENTRY_POINT_RENAME(ulocdata_open) +#define ulocdata_setNoSubstitute U_ICU_ENTRY_POINT_RENAME(ulocdata_setNoSubstitute) +#define ulocimp_addLikelySubtags U_ICU_ENTRY_POINT_RENAME(ulocimp_addLikelySubtags) +#define ulocimp_canonicalize U_ICU_ENTRY_POINT_RENAME(ulocimp_canonicalize) +#define ulocimp_forLanguageTag U_ICU_ENTRY_POINT_RENAME(ulocimp_forLanguageTag) +#define ulocimp_getBaseName U_ICU_ENTRY_POINT_RENAME(ulocimp_getBaseName) +#define ulocimp_getCountry U_ICU_ENTRY_POINT_RENAME(ulocimp_getCountry) +#define ulocimp_getKeywordValue U_ICU_ENTRY_POINT_RENAME(ulocimp_getKeywordValue) +#define ulocimp_getKeywords U_ICU_ENTRY_POINT_RENAME(ulocimp_getKeywords) +#define ulocimp_getKnownCanonicalizedLocaleForTest U_ICU_ENTRY_POINT_RENAME(ulocimp_getKnownCanonicalizedLocaleForTest) +#define ulocimp_getLanguage U_ICU_ENTRY_POINT_RENAME(ulocimp_getLanguage) +#define ulocimp_getName U_ICU_ENTRY_POINT_RENAME(ulocimp_getName) +#define ulocimp_getRegionForSupplementalData U_ICU_ENTRY_POINT_RENAME(ulocimp_getRegionForSupplementalData) +#define ulocimp_getScript U_ICU_ENTRY_POINT_RENAME(ulocimp_getScript) +#define ulocimp_isCanonicalizedLocaleForTest U_ICU_ENTRY_POINT_RENAME(ulocimp_isCanonicalizedLocaleForTest) +#define ulocimp_minimizeSubtags U_ICU_ENTRY_POINT_RENAME(ulocimp_minimizeSubtags) +#define ulocimp_toBcpKey U_ICU_ENTRY_POINT_RENAME(ulocimp_toBcpKey) +#define ulocimp_toBcpType U_ICU_ENTRY_POINT_RENAME(ulocimp_toBcpType) +#define ulocimp_toLanguageTag U_ICU_ENTRY_POINT_RENAME(ulocimp_toLanguageTag) +#define ulocimp_toLegacyKey U_ICU_ENTRY_POINT_RENAME(ulocimp_toLegacyKey) +#define ulocimp_toLegacyType U_ICU_ENTRY_POINT_RENAME(ulocimp_toLegacyType) +#define ultag_getTKeyStart U_ICU_ENTRY_POINT_RENAME(ultag_getTKeyStart) +#define ultag_isExtensionSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isExtensionSubtags) +#define ultag_isLanguageSubtag U_ICU_ENTRY_POINT_RENAME(ultag_isLanguageSubtag) +#define ultag_isPrivateuseValueSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isPrivateuseValueSubtags) +#define ultag_isRegionSubtag U_ICU_ENTRY_POINT_RENAME(ultag_isRegionSubtag) +#define ultag_isScriptSubtag U_ICU_ENTRY_POINT_RENAME(ultag_isScriptSubtag) +#define ultag_isTransformedExtensionSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isTransformedExtensionSubtags) +#define ultag_isUnicodeExtensionSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeExtensionSubtags) +#define ultag_isUnicodeLocaleAttribute U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleAttribute) +#define ultag_isUnicodeLocaleAttributes U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleAttributes) +#define ultag_isUnicodeLocaleKey U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleKey) +#define ultag_isUnicodeLocaleType U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleType) +#define ultag_isVariantSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isVariantSubtags) +#define umeas_getPrefixBase U_ICU_ENTRY_POINT_RENAME(umeas_getPrefixBase) +#define umeas_getPrefixPower U_ICU_ENTRY_POINT_RENAME(umeas_getPrefixPower) +#define umsg_applyPattern U_ICU_ENTRY_POINT_RENAME(umsg_applyPattern) +#define umsg_autoQuoteApostrophe U_ICU_ENTRY_POINT_RENAME(umsg_autoQuoteApostrophe) +#define umsg_clone U_ICU_ENTRY_POINT_RENAME(umsg_clone) +#define umsg_close U_ICU_ENTRY_POINT_RENAME(umsg_close) +#define umsg_format U_ICU_ENTRY_POINT_RENAME(umsg_format) +#define umsg_getLocale U_ICU_ENTRY_POINT_RENAME(umsg_getLocale) +#define umsg_open U_ICU_ENTRY_POINT_RENAME(umsg_open) +#define umsg_parse U_ICU_ENTRY_POINT_RENAME(umsg_parse) +#define umsg_setLocale U_ICU_ENTRY_POINT_RENAME(umsg_setLocale) +#define umsg_toPattern U_ICU_ENTRY_POINT_RENAME(umsg_toPattern) +#define umsg_vformat U_ICU_ENTRY_POINT_RENAME(umsg_vformat) +#define umsg_vparse U_ICU_ENTRY_POINT_RENAME(umsg_vparse) +#define umtx_lock U_ICU_ENTRY_POINT_RENAME(umtx_lock) +#define umtx_unlock U_ICU_ENTRY_POINT_RENAME(umtx_unlock) +#define umutablecptrie_buildImmutable U_ICU_ENTRY_POINT_RENAME(umutablecptrie_buildImmutable) +#define umutablecptrie_clone U_ICU_ENTRY_POINT_RENAME(umutablecptrie_clone) +#define umutablecptrie_close U_ICU_ENTRY_POINT_RENAME(umutablecptrie_close) +#define umutablecptrie_fromUCPMap U_ICU_ENTRY_POINT_RENAME(umutablecptrie_fromUCPMap) +#define umutablecptrie_fromUCPTrie U_ICU_ENTRY_POINT_RENAME(umutablecptrie_fromUCPTrie) +#define umutablecptrie_get U_ICU_ENTRY_POINT_RENAME(umutablecptrie_get) +#define umutablecptrie_getRange U_ICU_ENTRY_POINT_RENAME(umutablecptrie_getRange) +#define umutablecptrie_open U_ICU_ENTRY_POINT_RENAME(umutablecptrie_open) +#define umutablecptrie_set U_ICU_ENTRY_POINT_RENAME(umutablecptrie_set) +#define umutablecptrie_setRange U_ICU_ENTRY_POINT_RENAME(umutablecptrie_setRange) +#define uniset_getUnicode32Instance U_ICU_ENTRY_POINT_RENAME(uniset_getUnicode32Instance) +#define unorm2_append U_ICU_ENTRY_POINT_RENAME(unorm2_append) +#define unorm2_close U_ICU_ENTRY_POINT_RENAME(unorm2_close) +#define unorm2_composePair U_ICU_ENTRY_POINT_RENAME(unorm2_composePair) +#define unorm2_getCombiningClass U_ICU_ENTRY_POINT_RENAME(unorm2_getCombiningClass) +#define unorm2_getDecomposition U_ICU_ENTRY_POINT_RENAME(unorm2_getDecomposition) +#define unorm2_getInstance U_ICU_ENTRY_POINT_RENAME(unorm2_getInstance) +#define unorm2_getNFCInstance U_ICU_ENTRY_POINT_RENAME(unorm2_getNFCInstance) +#define unorm2_getNFDInstance U_ICU_ENTRY_POINT_RENAME(unorm2_getNFDInstance) +#define unorm2_getNFKCCasefoldInstance U_ICU_ENTRY_POINT_RENAME(unorm2_getNFKCCasefoldInstance) +#define unorm2_getNFKCInstance U_ICU_ENTRY_POINT_RENAME(unorm2_getNFKCInstance) +#define unorm2_getNFKDInstance U_ICU_ENTRY_POINT_RENAME(unorm2_getNFKDInstance) +#define unorm2_getRawDecomposition U_ICU_ENTRY_POINT_RENAME(unorm2_getRawDecomposition) +#define unorm2_hasBoundaryAfter U_ICU_ENTRY_POINT_RENAME(unorm2_hasBoundaryAfter) +#define unorm2_hasBoundaryBefore U_ICU_ENTRY_POINT_RENAME(unorm2_hasBoundaryBefore) +#define unorm2_isInert U_ICU_ENTRY_POINT_RENAME(unorm2_isInert) +#define unorm2_isNormalized U_ICU_ENTRY_POINT_RENAME(unorm2_isNormalized) +#define unorm2_normalize U_ICU_ENTRY_POINT_RENAME(unorm2_normalize) +#define unorm2_normalizeSecondAndAppend U_ICU_ENTRY_POINT_RENAME(unorm2_normalizeSecondAndAppend) +#define unorm2_openFiltered U_ICU_ENTRY_POINT_RENAME(unorm2_openFiltered) +#define unorm2_quickCheck U_ICU_ENTRY_POINT_RENAME(unorm2_quickCheck) +#define unorm2_spanQuickCheckYes U_ICU_ENTRY_POINT_RENAME(unorm2_spanQuickCheckYes) +#define unorm2_swap U_ICU_ENTRY_POINT_RENAME(unorm2_swap) +#define unorm_compare U_ICU_ENTRY_POINT_RENAME(unorm_compare) +#define unorm_concatenate U_ICU_ENTRY_POINT_RENAME(unorm_concatenate) +#define unorm_getFCD16 U_ICU_ENTRY_POINT_RENAME(unorm_getFCD16) +#define unorm_getQuickCheck U_ICU_ENTRY_POINT_RENAME(unorm_getQuickCheck) +#define unorm_isNormalized U_ICU_ENTRY_POINT_RENAME(unorm_isNormalized) +#define unorm_isNormalizedWithOptions U_ICU_ENTRY_POINT_RENAME(unorm_isNormalizedWithOptions) +#define unorm_next U_ICU_ENTRY_POINT_RENAME(unorm_next) +#define unorm_normalize U_ICU_ENTRY_POINT_RENAME(unorm_normalize) +#define unorm_previous U_ICU_ENTRY_POINT_RENAME(unorm_previous) +#define unorm_quickCheck U_ICU_ENTRY_POINT_RENAME(unorm_quickCheck) +#define unorm_quickCheckWithOptions U_ICU_ENTRY_POINT_RENAME(unorm_quickCheckWithOptions) +#define unum_applyPattern U_ICU_ENTRY_POINT_RENAME(unum_applyPattern) +#define unum_clone U_ICU_ENTRY_POINT_RENAME(unum_clone) +#define unum_close U_ICU_ENTRY_POINT_RENAME(unum_close) +#define unum_countAvailable U_ICU_ENTRY_POINT_RENAME(unum_countAvailable) +#define unum_format U_ICU_ENTRY_POINT_RENAME(unum_format) +#define unum_formatDecimal U_ICU_ENTRY_POINT_RENAME(unum_formatDecimal) +#define unum_formatDouble U_ICU_ENTRY_POINT_RENAME(unum_formatDouble) +#define unum_formatDoubleCurrency U_ICU_ENTRY_POINT_RENAME(unum_formatDoubleCurrency) +#define unum_formatDoubleForFields U_ICU_ENTRY_POINT_RENAME(unum_formatDoubleForFields) +#define unum_formatInt64 U_ICU_ENTRY_POINT_RENAME(unum_formatInt64) +#define unum_formatUFormattable U_ICU_ENTRY_POINT_RENAME(unum_formatUFormattable) +#define unum_getAttribute U_ICU_ENTRY_POINT_RENAME(unum_getAttribute) +#define unum_getAvailable U_ICU_ENTRY_POINT_RENAME(unum_getAvailable) +#define unum_getContext U_ICU_ENTRY_POINT_RENAME(unum_getContext) +#define unum_getDoubleAttribute U_ICU_ENTRY_POINT_RENAME(unum_getDoubleAttribute) +#define unum_getLocaleByType U_ICU_ENTRY_POINT_RENAME(unum_getLocaleByType) +#define unum_getSymbol U_ICU_ENTRY_POINT_RENAME(unum_getSymbol) +#define unum_getTextAttribute U_ICU_ENTRY_POINT_RENAME(unum_getTextAttribute) +#define unum_hasAttribute U_ICU_ENTRY_POINT_RENAME(unum_hasAttribute) +#define unum_open U_ICU_ENTRY_POINT_RENAME(unum_open) +#define unum_parse U_ICU_ENTRY_POINT_RENAME(unum_parse) +#define unum_parseDecimal U_ICU_ENTRY_POINT_RENAME(unum_parseDecimal) +#define unum_parseDouble U_ICU_ENTRY_POINT_RENAME(unum_parseDouble) +#define unum_parseDoubleCurrency U_ICU_ENTRY_POINT_RENAME(unum_parseDoubleCurrency) +#define unum_parseInt64 U_ICU_ENTRY_POINT_RENAME(unum_parseInt64) +#define unum_parseToUFormattable U_ICU_ENTRY_POINT_RENAME(unum_parseToUFormattable) +#define unum_setAttribute U_ICU_ENTRY_POINT_RENAME(unum_setAttribute) +#define unum_setContext U_ICU_ENTRY_POINT_RENAME(unum_setContext) +#define unum_setDoubleAttribute U_ICU_ENTRY_POINT_RENAME(unum_setDoubleAttribute) +#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_openForSkeletonAndLocaleWithError U_ICU_ENTRY_POINT_RENAME(unumf_openForSkeletonAndLocaleWithError) +#define unumf_openResult U_ICU_ENTRY_POINT_RENAME(unumf_openResult) +#define unumf_resultAsValue U_ICU_ENTRY_POINT_RENAME(unumf_resultAsValue) +#define unumf_resultGetAllFieldPositions U_ICU_ENTRY_POINT_RENAME(unumf_resultGetAllFieldPositions) +#define unumf_resultNextFieldPosition U_ICU_ENTRY_POINT_RENAME(unumf_resultNextFieldPosition) +#define unumf_resultToDecimalNumber U_ICU_ENTRY_POINT_RENAME(unumf_resultToDecimalNumber) +#define unumf_resultToString U_ICU_ENTRY_POINT_RENAME(unumf_resultToString) +#define unumrf_close U_ICU_ENTRY_POINT_RENAME(unumrf_close) +#define unumrf_closeResult U_ICU_ENTRY_POINT_RENAME(unumrf_closeResult) +#define unumrf_formatDecimalRange U_ICU_ENTRY_POINT_RENAME(unumrf_formatDecimalRange) +#define unumrf_formatDoubleRange U_ICU_ENTRY_POINT_RENAME(unumrf_formatDoubleRange) +#define unumrf_openForSkeletonWithCollapseAndIdentityFallback U_ICU_ENTRY_POINT_RENAME(unumrf_openForSkeletonWithCollapseAndIdentityFallback) +#define unumrf_openResult U_ICU_ENTRY_POINT_RENAME(unumrf_openResult) +#define unumrf_resultAsValue U_ICU_ENTRY_POINT_RENAME(unumrf_resultAsValue) +#define unumrf_resultGetFirstDecimalNumber U_ICU_ENTRY_POINT_RENAME(unumrf_resultGetFirstDecimalNumber) +#define unumrf_resultGetIdentityResult U_ICU_ENTRY_POINT_RENAME(unumrf_resultGetIdentityResult) +#define unumrf_resultGetSecondDecimalNumber U_ICU_ENTRY_POINT_RENAME(unumrf_resultGetSecondDecimalNumber) +#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) +#define unumsys_getRadix U_ICU_ENTRY_POINT_RENAME(unumsys_getRadix) +#define unumsys_isAlgorithmic U_ICU_ENTRY_POINT_RENAME(unumsys_isAlgorithmic) +#define unumsys_open U_ICU_ENTRY_POINT_RENAME(unumsys_open) +#define unumsys_openAvailableNames U_ICU_ENTRY_POINT_RENAME(unumsys_openAvailableNames) +#define unumsys_openByName U_ICU_ENTRY_POINT_RENAME(unumsys_openByName) +#define uplrules_close U_ICU_ENTRY_POINT_RENAME(uplrules_close) +#define uplrules_getKeywords U_ICU_ENTRY_POINT_RENAME(uplrules_getKeywords) +#define uplrules_open U_ICU_ENTRY_POINT_RENAME(uplrules_open) +#define uplrules_openForType U_ICU_ENTRY_POINT_RENAME(uplrules_openForType) +#define uplrules_select U_ICU_ENTRY_POINT_RENAME(uplrules_select) +#define uplrules_selectForRange U_ICU_ENTRY_POINT_RENAME(uplrules_selectForRange) +#define uplrules_selectFormatted U_ICU_ENTRY_POINT_RENAME(uplrules_selectFormatted) +#define uplrules_selectWithFormat U_ICU_ENTRY_POINT_RENAME(uplrules_selectWithFormat) +#define uplug_closeLibrary U_ICU_ENTRY_POINT_RENAME(uplug_closeLibrary) +#define uplug_findLibrary U_ICU_ENTRY_POINT_RENAME(uplug_findLibrary) +#define uplug_getConfiguration U_ICU_ENTRY_POINT_RENAME(uplug_getConfiguration) +#define uplug_getContext U_ICU_ENTRY_POINT_RENAME(uplug_getContext) +#define uplug_getCurrentLevel U_ICU_ENTRY_POINT_RENAME(uplug_getCurrentLevel) +#define uplug_getLibrary U_ICU_ENTRY_POINT_RENAME(uplug_getLibrary) +#define uplug_getLibraryName U_ICU_ENTRY_POINT_RENAME(uplug_getLibraryName) +#define uplug_getPlugInternal U_ICU_ENTRY_POINT_RENAME(uplug_getPlugInternal) +#define uplug_getPlugLevel U_ICU_ENTRY_POINT_RENAME(uplug_getPlugLevel) +#define uplug_getPlugLoadStatus U_ICU_ENTRY_POINT_RENAME(uplug_getPlugLoadStatus) +#define uplug_getPlugName U_ICU_ENTRY_POINT_RENAME(uplug_getPlugName) +#define uplug_getPluginFile U_ICU_ENTRY_POINT_RENAME(uplug_getPluginFile) +#define uplug_getSymbolName U_ICU_ENTRY_POINT_RENAME(uplug_getSymbolName) +#define uplug_init U_ICU_ENTRY_POINT_RENAME(uplug_init) +#define uplug_loadPlugFromEntrypoint U_ICU_ENTRY_POINT_RENAME(uplug_loadPlugFromEntrypoint) +#define uplug_loadPlugFromLibrary U_ICU_ENTRY_POINT_RENAME(uplug_loadPlugFromLibrary) +#define uplug_nextPlug U_ICU_ENTRY_POINT_RENAME(uplug_nextPlug) +#define uplug_openLibrary U_ICU_ENTRY_POINT_RENAME(uplug_openLibrary) +#define uplug_removePlug U_ICU_ENTRY_POINT_RENAME(uplug_removePlug) +#define uplug_setContext U_ICU_ENTRY_POINT_RENAME(uplug_setContext) +#define uplug_setPlugLevel U_ICU_ENTRY_POINT_RENAME(uplug_setPlugLevel) +#define uplug_setPlugName U_ICU_ENTRY_POINT_RENAME(uplug_setPlugName) +#define uplug_setPlugNoUnload U_ICU_ENTRY_POINT_RENAME(uplug_setPlugNoUnload) +#define uprops_addPropertyStarts U_ICU_ENTRY_POINT_RENAME(uprops_addPropertyStarts) +#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) +#define uprv_calloc U_ICU_ENTRY_POINT_RENAME(uprv_calloc) +#define uprv_ceil U_ICU_ENTRY_POINT_RENAME(uprv_ceil) +#define uprv_compareASCIIPropertyNames U_ICU_ENTRY_POINT_RENAME(uprv_compareASCIIPropertyNames) +#define uprv_compareEBCDICPropertyNames U_ICU_ENTRY_POINT_RENAME(uprv_compareEBCDICPropertyNames) +#define uprv_compareInvAscii U_ICU_ENTRY_POINT_RENAME(uprv_compareInvAscii) +#define uprv_compareInvEbcdic U_ICU_ENTRY_POINT_RENAME(uprv_compareInvEbcdic) +#define uprv_compareInvEbcdicAsAscii U_ICU_ENTRY_POINT_RENAME(uprv_compareInvEbcdicAsAscii) +#define uprv_convertToLCID U_ICU_ENTRY_POINT_RENAME(uprv_convertToLCID) +#define uprv_convertToLCIDPlatform U_ICU_ENTRY_POINT_RENAME(uprv_convertToLCIDPlatform) +#define uprv_convertToPosix U_ICU_ENTRY_POINT_RENAME(uprv_convertToPosix) +#define uprv_copyAscii U_ICU_ENTRY_POINT_RENAME(uprv_copyAscii) +#define uprv_copyEbcdic U_ICU_ENTRY_POINT_RENAME(uprv_copyEbcdic) +#define uprv_decContextClearStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextClearStatus) +#define uprv_decContextDefault U_ICU_ENTRY_POINT_RENAME(uprv_decContextDefault) +#define uprv_decContextGetRounding U_ICU_ENTRY_POINT_RENAME(uprv_decContextGetRounding) +#define uprv_decContextGetStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextGetStatus) +#define uprv_decContextRestoreStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextRestoreStatus) +#define uprv_decContextSaveStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextSaveStatus) +#define uprv_decContextSetRounding U_ICU_ENTRY_POINT_RENAME(uprv_decContextSetRounding) +#define uprv_decContextSetStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextSetStatus) +#define uprv_decContextSetStatusFromString U_ICU_ENTRY_POINT_RENAME(uprv_decContextSetStatusFromString) +#define uprv_decContextSetStatusFromStringQuiet U_ICU_ENTRY_POINT_RENAME(uprv_decContextSetStatusFromStringQuiet) +#define uprv_decContextSetStatusQuiet U_ICU_ENTRY_POINT_RENAME(uprv_decContextSetStatusQuiet) +#define uprv_decContextStatusToString U_ICU_ENTRY_POINT_RENAME(uprv_decContextStatusToString) +#define uprv_decContextTestSavedStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextTestSavedStatus) +#define uprv_decContextTestStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextTestStatus) +#define uprv_decContextZeroStatus U_ICU_ENTRY_POINT_RENAME(uprv_decContextZeroStatus) +#define uprv_decNumberAbs U_ICU_ENTRY_POINT_RENAME(uprv_decNumberAbs) +#define uprv_decNumberAdd U_ICU_ENTRY_POINT_RENAME(uprv_decNumberAdd) +#define uprv_decNumberAnd U_ICU_ENTRY_POINT_RENAME(uprv_decNumberAnd) +#define uprv_decNumberClassToString U_ICU_ENTRY_POINT_RENAME(uprv_decNumberClassToString) +#define uprv_decNumberCompare U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCompare) +#define uprv_decNumberCompareSignal U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCompareSignal) +#define uprv_decNumberCompareTotal U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCompareTotal) +#define uprv_decNumberCompareTotalMag U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCompareTotalMag) +#define uprv_decNumberCopy U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCopy) +#define uprv_decNumberCopyAbs U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCopyAbs) +#define uprv_decNumberCopyNegate U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCopyNegate) +#define uprv_decNumberCopySign U_ICU_ENTRY_POINT_RENAME(uprv_decNumberCopySign) +#define uprv_decNumberDivide U_ICU_ENTRY_POINT_RENAME(uprv_decNumberDivide) +#define uprv_decNumberDivideInteger U_ICU_ENTRY_POINT_RENAME(uprv_decNumberDivideInteger) +#define uprv_decNumberExp U_ICU_ENTRY_POINT_RENAME(uprv_decNumberExp) +#define uprv_decNumberFMA U_ICU_ENTRY_POINT_RENAME(uprv_decNumberFMA) +#define uprv_decNumberFromInt32 U_ICU_ENTRY_POINT_RENAME(uprv_decNumberFromInt32) +#define uprv_decNumberFromString U_ICU_ENTRY_POINT_RENAME(uprv_decNumberFromString) +#define uprv_decNumberFromUInt32 U_ICU_ENTRY_POINT_RENAME(uprv_decNumberFromUInt32) +#define uprv_decNumberGetBCD U_ICU_ENTRY_POINT_RENAME(uprv_decNumberGetBCD) +#define uprv_decNumberInvert U_ICU_ENTRY_POINT_RENAME(uprv_decNumberInvert) +#define uprv_decNumberIsNormal U_ICU_ENTRY_POINT_RENAME(uprv_decNumberIsNormal) +#define uprv_decNumberIsSubnormal U_ICU_ENTRY_POINT_RENAME(uprv_decNumberIsSubnormal) +#define uprv_decNumberLn U_ICU_ENTRY_POINT_RENAME(uprv_decNumberLn) +#define uprv_decNumberLog10 U_ICU_ENTRY_POINT_RENAME(uprv_decNumberLog10) +#define uprv_decNumberLogB U_ICU_ENTRY_POINT_RENAME(uprv_decNumberLogB) +#define uprv_decNumberMax U_ICU_ENTRY_POINT_RENAME(uprv_decNumberMax) +#define uprv_decNumberMaxMag U_ICU_ENTRY_POINT_RENAME(uprv_decNumberMaxMag) +#define uprv_decNumberMin U_ICU_ENTRY_POINT_RENAME(uprv_decNumberMin) +#define uprv_decNumberMinMag U_ICU_ENTRY_POINT_RENAME(uprv_decNumberMinMag) +#define uprv_decNumberMinus U_ICU_ENTRY_POINT_RENAME(uprv_decNumberMinus) +#define uprv_decNumberMultiply U_ICU_ENTRY_POINT_RENAME(uprv_decNumberMultiply) +#define uprv_decNumberNextMinus U_ICU_ENTRY_POINT_RENAME(uprv_decNumberNextMinus) +#define uprv_decNumberNextPlus U_ICU_ENTRY_POINT_RENAME(uprv_decNumberNextPlus) +#define uprv_decNumberNextToward U_ICU_ENTRY_POINT_RENAME(uprv_decNumberNextToward) +#define uprv_decNumberNormalize U_ICU_ENTRY_POINT_RENAME(uprv_decNumberNormalize) +#define uprv_decNumberOr U_ICU_ENTRY_POINT_RENAME(uprv_decNumberOr) +#define uprv_decNumberPlus U_ICU_ENTRY_POINT_RENAME(uprv_decNumberPlus) +#define uprv_decNumberPower U_ICU_ENTRY_POINT_RENAME(uprv_decNumberPower) +#define uprv_decNumberQuantize U_ICU_ENTRY_POINT_RENAME(uprv_decNumberQuantize) +#define uprv_decNumberReduce U_ICU_ENTRY_POINT_RENAME(uprv_decNumberReduce) +#define uprv_decNumberRemainder U_ICU_ENTRY_POINT_RENAME(uprv_decNumberRemainder) +#define uprv_decNumberRemainderNear U_ICU_ENTRY_POINT_RENAME(uprv_decNumberRemainderNear) +#define uprv_decNumberRescale U_ICU_ENTRY_POINT_RENAME(uprv_decNumberRescale) +#define uprv_decNumberRotate U_ICU_ENTRY_POINT_RENAME(uprv_decNumberRotate) +#define uprv_decNumberSameQuantum U_ICU_ENTRY_POINT_RENAME(uprv_decNumberSameQuantum) +#define uprv_decNumberScaleB U_ICU_ENTRY_POINT_RENAME(uprv_decNumberScaleB) +#define uprv_decNumberSetBCD U_ICU_ENTRY_POINT_RENAME(uprv_decNumberSetBCD) +#define uprv_decNumberShift U_ICU_ENTRY_POINT_RENAME(uprv_decNumberShift) +#define uprv_decNumberSquareRoot U_ICU_ENTRY_POINT_RENAME(uprv_decNumberSquareRoot) +#define uprv_decNumberSubtract U_ICU_ENTRY_POINT_RENAME(uprv_decNumberSubtract) +#define uprv_decNumberToEngString U_ICU_ENTRY_POINT_RENAME(uprv_decNumberToEngString) +#define uprv_decNumberToInt32 U_ICU_ENTRY_POINT_RENAME(uprv_decNumberToInt32) +#define uprv_decNumberToIntegralExact U_ICU_ENTRY_POINT_RENAME(uprv_decNumberToIntegralExact) +#define uprv_decNumberToIntegralValue U_ICU_ENTRY_POINT_RENAME(uprv_decNumberToIntegralValue) +#define uprv_decNumberToString U_ICU_ENTRY_POINT_RENAME(uprv_decNumberToString) +#define uprv_decNumberToUInt32 U_ICU_ENTRY_POINT_RENAME(uprv_decNumberToUInt32) +#define uprv_decNumberTrim U_ICU_ENTRY_POINT_RENAME(uprv_decNumberTrim) +#define uprv_decNumberVersion U_ICU_ENTRY_POINT_RENAME(uprv_decNumberVersion) +#define uprv_decNumberXor U_ICU_ENTRY_POINT_RENAME(uprv_decNumberXor) +#define uprv_decNumberZero U_ICU_ENTRY_POINT_RENAME(uprv_decNumberZero) +#define uprv_deleteConditionalCE32 U_ICU_ENTRY_POINT_RENAME(uprv_deleteConditionalCE32) +#define uprv_deleteUObject U_ICU_ENTRY_POINT_RENAME(uprv_deleteUObject) +#define uprv_dl_close U_ICU_ENTRY_POINT_RENAME(uprv_dl_close) +#define uprv_dl_open U_ICU_ENTRY_POINT_RENAME(uprv_dl_open) +#define uprv_dlsym_func U_ICU_ENTRY_POINT_RENAME(uprv_dlsym_func) +#define uprv_eastrncpy U_ICU_ENTRY_POINT_RENAME(uprv_eastrncpy) +#define uprv_ebcdicFromAscii U_ICU_ENTRY_POINT_RENAME(uprv_ebcdicFromAscii) +#define uprv_ebcdicToAscii U_ICU_ENTRY_POINT_RENAME(uprv_ebcdicToAscii) +#define uprv_ebcdicToLowercaseAscii U_ICU_ENTRY_POINT_RENAME(uprv_ebcdicToLowercaseAscii) +#define uprv_ebcdictolower U_ICU_ENTRY_POINT_RENAME(uprv_ebcdictolower) +#define uprv_fabs U_ICU_ENTRY_POINT_RENAME(uprv_fabs) +#define uprv_floor U_ICU_ENTRY_POINT_RENAME(uprv_floor) +#define uprv_fmax U_ICU_ENTRY_POINT_RENAME(uprv_fmax) +#define uprv_fmin U_ICU_ENTRY_POINT_RENAME(uprv_fmin) +#define uprv_fmod U_ICU_ENTRY_POINT_RENAME(uprv_fmod) +#define uprv_free U_ICU_ENTRY_POINT_RENAME(uprv_free) +#define uprv_getCharNameCharacters U_ICU_ENTRY_POINT_RENAME(uprv_getCharNameCharacters) +#define uprv_getDefaultLocaleID U_ICU_ENTRY_POINT_RENAME(uprv_getDefaultLocaleID) +#define uprv_getInfinity U_ICU_ENTRY_POINT_RENAME(uprv_getInfinity) +#define uprv_getMaxCharNameLength U_ICU_ENTRY_POINT_RENAME(uprv_getMaxCharNameLength) +#define uprv_getMaxValues U_ICU_ENTRY_POINT_RENAME(uprv_getMaxValues) +#define uprv_getNaN U_ICU_ENTRY_POINT_RENAME(uprv_getNaN) +#define uprv_getRawUTCtime U_ICU_ENTRY_POINT_RENAME(uprv_getRawUTCtime) +#define uprv_getStaticCurrencyName U_ICU_ENTRY_POINT_RENAME(uprv_getStaticCurrencyName) +#define uprv_getUTCtime U_ICU_ENTRY_POINT_RENAME(uprv_getUTCtime) +#define uprv_int32Comparator U_ICU_ENTRY_POINT_RENAME(uprv_int32Comparator) +#define uprv_isASCIILetter U_ICU_ENTRY_POINT_RENAME(uprv_isASCIILetter) +#define uprv_isEbcdicAtSign U_ICU_ENTRY_POINT_RENAME(uprv_isEbcdicAtSign) +#define uprv_isInfinite U_ICU_ENTRY_POINT_RENAME(uprv_isInfinite) +#define uprv_isInvariantString U_ICU_ENTRY_POINT_RENAME(uprv_isInvariantString) +#define uprv_isInvariantUString U_ICU_ENTRY_POINT_RENAME(uprv_isInvariantUString) +#define uprv_isNaN U_ICU_ENTRY_POINT_RENAME(uprv_isNaN) +#define uprv_isNegativeInfinity U_ICU_ENTRY_POINT_RENAME(uprv_isNegativeInfinity) +#define uprv_isPositiveInfinity U_ICU_ENTRY_POINT_RENAME(uprv_isPositiveInfinity) +#define uprv_itou U_ICU_ENTRY_POINT_RENAME(uprv_itou) +#define uprv_log U_ICU_ENTRY_POINT_RENAME(uprv_log) +#define uprv_malloc U_ICU_ENTRY_POINT_RENAME(uprv_malloc) +#define uprv_mapFile U_ICU_ENTRY_POINT_RENAME(uprv_mapFile) +#define uprv_max U_ICU_ENTRY_POINT_RENAME(uprv_max) +#define uprv_maxMantissa U_ICU_ENTRY_POINT_RENAME(uprv_maxMantissa) +#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) +#define uprv_pow10 U_ICU_ENTRY_POINT_RENAME(uprv_pow10) +#define uprv_realloc U_ICU_ENTRY_POINT_RENAME(uprv_realloc) +#define uprv_round U_ICU_ENTRY_POINT_RENAME(uprv_round) +#define uprv_sortArray U_ICU_ENTRY_POINT_RENAME(uprv_sortArray) +#define uprv_stableBinarySearch U_ICU_ENTRY_POINT_RENAME(uprv_stableBinarySearch) +#define uprv_strCompare U_ICU_ENTRY_POINT_RENAME(uprv_strCompare) +#define uprv_strdup U_ICU_ENTRY_POINT_RENAME(uprv_strdup) +#define uprv_stricmp U_ICU_ENTRY_POINT_RENAME(uprv_stricmp) +#define uprv_strndup U_ICU_ENTRY_POINT_RENAME(uprv_strndup) +#define uprv_strnicmp U_ICU_ENTRY_POINT_RENAME(uprv_strnicmp) +#define uprv_syntaxError U_ICU_ENTRY_POINT_RENAME(uprv_syntaxError) +#define uprv_timezone U_ICU_ENTRY_POINT_RENAME(uprv_timezone) +#define uprv_toupper U_ICU_ENTRY_POINT_RENAME(uprv_toupper) +#define uprv_trunc U_ICU_ENTRY_POINT_RENAME(uprv_trunc) +#define uprv_tzname U_ICU_ENTRY_POINT_RENAME(uprv_tzname) +#define uprv_tzname_clear_cache U_ICU_ENTRY_POINT_RENAME(uprv_tzname_clear_cache) +#define uprv_tzset U_ICU_ENTRY_POINT_RENAME(uprv_tzset) +#define uprv_uint16Comparator U_ICU_ENTRY_POINT_RENAME(uprv_uint16Comparator) +#define uprv_uint32Comparator U_ICU_ENTRY_POINT_RENAME(uprv_uint32Comparator) +#define uprv_unmapFile U_ICU_ENTRY_POINT_RENAME(uprv_unmapFile) +#define upvec_cloneArray U_ICU_ENTRY_POINT_RENAME(upvec_cloneArray) +#define upvec_close U_ICU_ENTRY_POINT_RENAME(upvec_close) +#define upvec_compact U_ICU_ENTRY_POINT_RENAME(upvec_compact) +#define upvec_compactToUTrie2Handler U_ICU_ENTRY_POINT_RENAME(upvec_compactToUTrie2Handler) +#define upvec_compactToUTrie2WithRowIndexes U_ICU_ENTRY_POINT_RENAME(upvec_compactToUTrie2WithRowIndexes) +#define upvec_getArray U_ICU_ENTRY_POINT_RENAME(upvec_getArray) +#define upvec_getRow U_ICU_ENTRY_POINT_RENAME(upvec_getRow) +#define upvec_getValue U_ICU_ENTRY_POINT_RENAME(upvec_getValue) +#define upvec_open U_ICU_ENTRY_POINT_RENAME(upvec_open) +#define upvec_setValue U_ICU_ENTRY_POINT_RENAME(upvec_setValue) +#define uregex_appendReplacement U_ICU_ENTRY_POINT_RENAME(uregex_appendReplacement) +#define uregex_appendReplacementUText U_ICU_ENTRY_POINT_RENAME(uregex_appendReplacementUText) +#define uregex_appendTail U_ICU_ENTRY_POINT_RENAME(uregex_appendTail) +#define uregex_appendTailUText U_ICU_ENTRY_POINT_RENAME(uregex_appendTailUText) +#define uregex_clone U_ICU_ENTRY_POINT_RENAME(uregex_clone) +#define uregex_close U_ICU_ENTRY_POINT_RENAME(uregex_close) +#define uregex_end U_ICU_ENTRY_POINT_RENAME(uregex_end) +#define uregex_end64 U_ICU_ENTRY_POINT_RENAME(uregex_end64) +#define uregex_find U_ICU_ENTRY_POINT_RENAME(uregex_find) +#define uregex_find64 U_ICU_ENTRY_POINT_RENAME(uregex_find64) +#define uregex_findNext U_ICU_ENTRY_POINT_RENAME(uregex_findNext) +#define uregex_flags U_ICU_ENTRY_POINT_RENAME(uregex_flags) +#define uregex_getFindProgressCallback U_ICU_ENTRY_POINT_RENAME(uregex_getFindProgressCallback) +#define uregex_getMatchCallback U_ICU_ENTRY_POINT_RENAME(uregex_getMatchCallback) +#define uregex_getStackLimit U_ICU_ENTRY_POINT_RENAME(uregex_getStackLimit) +#define uregex_getText U_ICU_ENTRY_POINT_RENAME(uregex_getText) +#define uregex_getTimeLimit U_ICU_ENTRY_POINT_RENAME(uregex_getTimeLimit) +#define uregex_getUText U_ICU_ENTRY_POINT_RENAME(uregex_getUText) +#define uregex_group U_ICU_ENTRY_POINT_RENAME(uregex_group) +#define uregex_groupCount U_ICU_ENTRY_POINT_RENAME(uregex_groupCount) +#define uregex_groupNumberFromCName U_ICU_ENTRY_POINT_RENAME(uregex_groupNumberFromCName) +#define uregex_groupNumberFromName U_ICU_ENTRY_POINT_RENAME(uregex_groupNumberFromName) +#define uregex_groupUText U_ICU_ENTRY_POINT_RENAME(uregex_groupUText) +#define uregex_hasAnchoringBounds U_ICU_ENTRY_POINT_RENAME(uregex_hasAnchoringBounds) +#define uregex_hasTransparentBounds U_ICU_ENTRY_POINT_RENAME(uregex_hasTransparentBounds) +#define uregex_hitEnd U_ICU_ENTRY_POINT_RENAME(uregex_hitEnd) +#define uregex_lookingAt U_ICU_ENTRY_POINT_RENAME(uregex_lookingAt) +#define uregex_lookingAt64 U_ICU_ENTRY_POINT_RENAME(uregex_lookingAt64) +#define uregex_matches U_ICU_ENTRY_POINT_RENAME(uregex_matches) +#define uregex_matches64 U_ICU_ENTRY_POINT_RENAME(uregex_matches64) +#define uregex_open U_ICU_ENTRY_POINT_RENAME(uregex_open) +#define uregex_openC U_ICU_ENTRY_POINT_RENAME(uregex_openC) +#define uregex_openUText U_ICU_ENTRY_POINT_RENAME(uregex_openUText) +#define uregex_pattern U_ICU_ENTRY_POINT_RENAME(uregex_pattern) +#define uregex_patternUText U_ICU_ENTRY_POINT_RENAME(uregex_patternUText) +#define uregex_refreshUText U_ICU_ENTRY_POINT_RENAME(uregex_refreshUText) +#define uregex_regionEnd U_ICU_ENTRY_POINT_RENAME(uregex_regionEnd) +#define uregex_regionEnd64 U_ICU_ENTRY_POINT_RENAME(uregex_regionEnd64) +#define uregex_regionStart U_ICU_ENTRY_POINT_RENAME(uregex_regionStart) +#define uregex_regionStart64 U_ICU_ENTRY_POINT_RENAME(uregex_regionStart64) +#define uregex_replaceAll U_ICU_ENTRY_POINT_RENAME(uregex_replaceAll) +#define uregex_replaceAllUText U_ICU_ENTRY_POINT_RENAME(uregex_replaceAllUText) +#define uregex_replaceFirst U_ICU_ENTRY_POINT_RENAME(uregex_replaceFirst) +#define uregex_replaceFirstUText U_ICU_ENTRY_POINT_RENAME(uregex_replaceFirstUText) +#define uregex_requireEnd U_ICU_ENTRY_POINT_RENAME(uregex_requireEnd) +#define uregex_reset U_ICU_ENTRY_POINT_RENAME(uregex_reset) +#define uregex_reset64 U_ICU_ENTRY_POINT_RENAME(uregex_reset64) +#define uregex_setFindProgressCallback U_ICU_ENTRY_POINT_RENAME(uregex_setFindProgressCallback) +#define uregex_setMatchCallback U_ICU_ENTRY_POINT_RENAME(uregex_setMatchCallback) +#define uregex_setRegion U_ICU_ENTRY_POINT_RENAME(uregex_setRegion) +#define uregex_setRegion64 U_ICU_ENTRY_POINT_RENAME(uregex_setRegion64) +#define uregex_setRegionAndStart U_ICU_ENTRY_POINT_RENAME(uregex_setRegionAndStart) +#define uregex_setStackLimit U_ICU_ENTRY_POINT_RENAME(uregex_setStackLimit) +#define uregex_setText U_ICU_ENTRY_POINT_RENAME(uregex_setText) +#define uregex_setTimeLimit U_ICU_ENTRY_POINT_RENAME(uregex_setTimeLimit) +#define uregex_setUText U_ICU_ENTRY_POINT_RENAME(uregex_setUText) +#define uregex_split U_ICU_ENTRY_POINT_RENAME(uregex_split) +#define uregex_splitUText U_ICU_ENTRY_POINT_RENAME(uregex_splitUText) +#define uregex_start U_ICU_ENTRY_POINT_RENAME(uregex_start) +#define uregex_start64 U_ICU_ENTRY_POINT_RENAME(uregex_start64) +#define uregex_ucstr_unescape_charAt U_ICU_ENTRY_POINT_RENAME(uregex_ucstr_unescape_charAt) +#define uregex_useAnchoringBounds U_ICU_ENTRY_POINT_RENAME(uregex_useAnchoringBounds) +#define uregex_useTransparentBounds U_ICU_ENTRY_POINT_RENAME(uregex_useTransparentBounds) +#define uregex_utext_unescape_charAt U_ICU_ENTRY_POINT_RENAME(uregex_utext_unescape_charAt) +#define uregion_areEqual U_ICU_ENTRY_POINT_RENAME(uregion_areEqual) +#define uregion_contains U_ICU_ENTRY_POINT_RENAME(uregion_contains) +#define uregion_getAvailable U_ICU_ENTRY_POINT_RENAME(uregion_getAvailable) +#define uregion_getContainedRegions U_ICU_ENTRY_POINT_RENAME(uregion_getContainedRegions) +#define uregion_getContainedRegionsOfType U_ICU_ENTRY_POINT_RENAME(uregion_getContainedRegionsOfType) +#define uregion_getContainingRegion U_ICU_ENTRY_POINT_RENAME(uregion_getContainingRegion) +#define uregion_getContainingRegionOfType U_ICU_ENTRY_POINT_RENAME(uregion_getContainingRegionOfType) +#define uregion_getNumericCode U_ICU_ENTRY_POINT_RENAME(uregion_getNumericCode) +#define uregion_getPreferredValues U_ICU_ENTRY_POINT_RENAME(uregion_getPreferredValues) +#define uregion_getRegionCode U_ICU_ENTRY_POINT_RENAME(uregion_getRegionCode) +#define uregion_getRegionFromCode U_ICU_ENTRY_POINT_RENAME(uregion_getRegionFromCode) +#define uregion_getRegionFromNumericCode U_ICU_ENTRY_POINT_RENAME(uregion_getRegionFromNumericCode) +#define uregion_getType U_ICU_ENTRY_POINT_RENAME(uregion_getType) +#define ureldatefmt_close U_ICU_ENTRY_POINT_RENAME(ureldatefmt_close) +#define ureldatefmt_closeResult U_ICU_ENTRY_POINT_RENAME(ureldatefmt_closeResult) +#define ureldatefmt_combineDateAndTime U_ICU_ENTRY_POINT_RENAME(ureldatefmt_combineDateAndTime) +#define ureldatefmt_format U_ICU_ENTRY_POINT_RENAME(ureldatefmt_format) +#define ureldatefmt_formatNumeric U_ICU_ENTRY_POINT_RENAME(ureldatefmt_formatNumeric) +#define ureldatefmt_formatNumericToResult U_ICU_ENTRY_POINT_RENAME(ureldatefmt_formatNumericToResult) +#define ureldatefmt_formatToResult U_ICU_ENTRY_POINT_RENAME(ureldatefmt_formatToResult) +#define ureldatefmt_open U_ICU_ENTRY_POINT_RENAME(ureldatefmt_open) +#define ureldatefmt_openResult U_ICU_ENTRY_POINT_RENAME(ureldatefmt_openResult) +#define ureldatefmt_resultAsValue U_ICU_ENTRY_POINT_RENAME(ureldatefmt_resultAsValue) +#define ures_close U_ICU_ENTRY_POINT_RENAME(ures_close) +#define ures_copyResb U_ICU_ENTRY_POINT_RENAME(ures_copyResb) +#define ures_countArrayItems U_ICU_ENTRY_POINT_RENAME(ures_countArrayItems) +#define ures_findResource U_ICU_ENTRY_POINT_RENAME(ures_findResource) +#define ures_findSubResource U_ICU_ENTRY_POINT_RENAME(ures_findSubResource) +#define ures_getAllChildrenWithFallback U_ICU_ENTRY_POINT_RENAME(ures_getAllChildrenWithFallback) +#define ures_getAllItemsWithFallback U_ICU_ENTRY_POINT_RENAME(ures_getAllItemsWithFallback) +#define ures_getBinary U_ICU_ENTRY_POINT_RENAME(ures_getBinary) +#define ures_getByIndex U_ICU_ENTRY_POINT_RENAME(ures_getByIndex) +#define ures_getByKey U_ICU_ENTRY_POINT_RENAME(ures_getByKey) +#define ures_getByKeyWithFallback U_ICU_ENTRY_POINT_RENAME(ures_getByKeyWithFallback) +#define ures_getFunctionalEquivalent U_ICU_ENTRY_POINT_RENAME(ures_getFunctionalEquivalent) +#define ures_getInt U_ICU_ENTRY_POINT_RENAME(ures_getInt) +#define ures_getIntVector U_ICU_ENTRY_POINT_RENAME(ures_getIntVector) +#define ures_getKey U_ICU_ENTRY_POINT_RENAME(ures_getKey) +#define ures_getKeywordValues U_ICU_ENTRY_POINT_RENAME(ures_getKeywordValues) +#define ures_getLocale U_ICU_ENTRY_POINT_RENAME(ures_getLocale) +#define ures_getLocaleByType U_ICU_ENTRY_POINT_RENAME(ures_getLocaleByType) +#define ures_getLocaleInternal U_ICU_ENTRY_POINT_RENAME(ures_getLocaleInternal) +#define ures_getName U_ICU_ENTRY_POINT_RENAME(ures_getName) +#define ures_getNextResource U_ICU_ENTRY_POINT_RENAME(ures_getNextResource) +#define ures_getNextString U_ICU_ENTRY_POINT_RENAME(ures_getNextString) +#define ures_getSize U_ICU_ENTRY_POINT_RENAME(ures_getSize) +#define ures_getString U_ICU_ENTRY_POINT_RENAME(ures_getString) +#define ures_getStringByIndex U_ICU_ENTRY_POINT_RENAME(ures_getStringByIndex) +#define ures_getStringByKey U_ICU_ENTRY_POINT_RENAME(ures_getStringByKey) +#define ures_getStringByKeyWithFallback U_ICU_ENTRY_POINT_RENAME(ures_getStringByKeyWithFallback) +#define ures_getType U_ICU_ENTRY_POINT_RENAME(ures_getType) +#define ures_getUInt U_ICU_ENTRY_POINT_RENAME(ures_getUInt) +#define ures_getUTF8String U_ICU_ENTRY_POINT_RENAME(ures_getUTF8String) +#define ures_getUTF8StringByIndex U_ICU_ENTRY_POINT_RENAME(ures_getUTF8StringByIndex) +#define ures_getUTF8StringByKey U_ICU_ENTRY_POINT_RENAME(ures_getUTF8StringByKey) +#define ures_getValueWithFallback U_ICU_ENTRY_POINT_RENAME(ures_getValueWithFallback) +#define ures_getVersion U_ICU_ENTRY_POINT_RENAME(ures_getVersion) +#define ures_getVersionByKey U_ICU_ENTRY_POINT_RENAME(ures_getVersionByKey) +#define ures_getVersionNumber U_ICU_ENTRY_POINT_RENAME(ures_getVersionNumber) +#define ures_getVersionNumberInternal U_ICU_ENTRY_POINT_RENAME(ures_getVersionNumberInternal) +#define ures_hasNext U_ICU_ENTRY_POINT_RENAME(ures_hasNext) +#define ures_initStackObject U_ICU_ENTRY_POINT_RENAME(ures_initStackObject) +#define ures_open U_ICU_ENTRY_POINT_RENAME(ures_open) +#define ures_openAvailableLocales U_ICU_ENTRY_POINT_RENAME(ures_openAvailableLocales) +#define ures_openDirect U_ICU_ENTRY_POINT_RENAME(ures_openDirect) +#define ures_openDirectFillIn U_ICU_ENTRY_POINT_RENAME(ures_openDirectFillIn) +#define ures_openFillIn U_ICU_ENTRY_POINT_RENAME(ures_openFillIn) +#define ures_openNoDefault U_ICU_ENTRY_POINT_RENAME(ures_openNoDefault) +#define ures_openU U_ICU_ENTRY_POINT_RENAME(ures_openU) +#define ures_resetIterator U_ICU_ENTRY_POINT_RENAME(ures_resetIterator) +#define ures_swap U_ICU_ENTRY_POINT_RENAME(ures_swap) +#define uscript_breaksBetweenLetters U_ICU_ENTRY_POINT_RENAME(uscript_breaksBetweenLetters) +#define uscript_closeRun U_ICU_ENTRY_POINT_RENAME(uscript_closeRun) +#define uscript_getCode U_ICU_ENTRY_POINT_RENAME(uscript_getCode) +#define uscript_getName U_ICU_ENTRY_POINT_RENAME(uscript_getName) +#define uscript_getSampleString U_ICU_ENTRY_POINT_RENAME(uscript_getSampleString) +#define uscript_getSampleUnicodeString U_ICU_ENTRY_POINT_RENAME(uscript_getSampleUnicodeString) +#define uscript_getScript U_ICU_ENTRY_POINT_RENAME(uscript_getScript) +#define uscript_getScriptExtensions U_ICU_ENTRY_POINT_RENAME(uscript_getScriptExtensions) +#define uscript_getShortName U_ICU_ENTRY_POINT_RENAME(uscript_getShortName) +#define uscript_getUsage U_ICU_ENTRY_POINT_RENAME(uscript_getUsage) +#define uscript_hasScript U_ICU_ENTRY_POINT_RENAME(uscript_hasScript) +#define uscript_isCased U_ICU_ENTRY_POINT_RENAME(uscript_isCased) +#define uscript_isRightToLeft U_ICU_ENTRY_POINT_RENAME(uscript_isRightToLeft) +#define uscript_nextRun U_ICU_ENTRY_POINT_RENAME(uscript_nextRun) +#define uscript_openRun U_ICU_ENTRY_POINT_RENAME(uscript_openRun) +#define uscript_resetRun U_ICU_ENTRY_POINT_RENAME(uscript_resetRun) +#define uscript_setRunText U_ICU_ENTRY_POINT_RENAME(uscript_setRunText) +#define usearch_close U_ICU_ENTRY_POINT_RENAME(usearch_close) +#define usearch_first U_ICU_ENTRY_POINT_RENAME(usearch_first) +#define usearch_following U_ICU_ENTRY_POINT_RENAME(usearch_following) +#define usearch_getAttribute U_ICU_ENTRY_POINT_RENAME(usearch_getAttribute) +#define usearch_getBreakIterator U_ICU_ENTRY_POINT_RENAME(usearch_getBreakIterator) +#define usearch_getCollator U_ICU_ENTRY_POINT_RENAME(usearch_getCollator) +#define usearch_getMatchedLength U_ICU_ENTRY_POINT_RENAME(usearch_getMatchedLength) +#define usearch_getMatchedStart U_ICU_ENTRY_POINT_RENAME(usearch_getMatchedStart) +#define usearch_getMatchedText U_ICU_ENTRY_POINT_RENAME(usearch_getMatchedText) +#define usearch_getOffset U_ICU_ENTRY_POINT_RENAME(usearch_getOffset) +#define usearch_getPattern U_ICU_ENTRY_POINT_RENAME(usearch_getPattern) +#define usearch_getText U_ICU_ENTRY_POINT_RENAME(usearch_getText) +#define usearch_handleNextCanonical U_ICU_ENTRY_POINT_RENAME(usearch_handleNextCanonical) +#define usearch_handleNextExact U_ICU_ENTRY_POINT_RENAME(usearch_handleNextExact) +#define usearch_handlePreviousCanonical U_ICU_ENTRY_POINT_RENAME(usearch_handlePreviousCanonical) +#define usearch_handlePreviousExact U_ICU_ENTRY_POINT_RENAME(usearch_handlePreviousExact) +#define usearch_last U_ICU_ENTRY_POINT_RENAME(usearch_last) +#define usearch_next U_ICU_ENTRY_POINT_RENAME(usearch_next) +#define usearch_open U_ICU_ENTRY_POINT_RENAME(usearch_open) +#define usearch_openFromCollator U_ICU_ENTRY_POINT_RENAME(usearch_openFromCollator) +#define usearch_preceding U_ICU_ENTRY_POINT_RENAME(usearch_preceding) +#define usearch_previous U_ICU_ENTRY_POINT_RENAME(usearch_previous) +#define usearch_reset U_ICU_ENTRY_POINT_RENAME(usearch_reset) +#define usearch_search U_ICU_ENTRY_POINT_RENAME(usearch_search) +#define usearch_searchBackwards U_ICU_ENTRY_POINT_RENAME(usearch_searchBackwards) +#define usearch_setAttribute U_ICU_ENTRY_POINT_RENAME(usearch_setAttribute) +#define usearch_setBreakIterator U_ICU_ENTRY_POINT_RENAME(usearch_setBreakIterator) +#define usearch_setCollator U_ICU_ENTRY_POINT_RENAME(usearch_setCollator) +#define usearch_setOffset U_ICU_ENTRY_POINT_RENAME(usearch_setOffset) +#define usearch_setPattern U_ICU_ENTRY_POINT_RENAME(usearch_setPattern) +#define usearch_setText U_ICU_ENTRY_POINT_RENAME(usearch_setText) +#define uset_add U_ICU_ENTRY_POINT_RENAME(uset_add) +#define uset_addAll U_ICU_ENTRY_POINT_RENAME(uset_addAll) +#define uset_addAllCodePoints U_ICU_ENTRY_POINT_RENAME(uset_addAllCodePoints) +#define uset_addRange U_ICU_ENTRY_POINT_RENAME(uset_addRange) +#define uset_addString U_ICU_ENTRY_POINT_RENAME(uset_addString) +#define uset_applyIntPropertyValue U_ICU_ENTRY_POINT_RENAME(uset_applyIntPropertyValue) +#define uset_applyPattern U_ICU_ENTRY_POINT_RENAME(uset_applyPattern) +#define uset_applyPropertyAlias U_ICU_ENTRY_POINT_RENAME(uset_applyPropertyAlias) +#define uset_charAt U_ICU_ENTRY_POINT_RENAME(uset_charAt) +#define uset_clear U_ICU_ENTRY_POINT_RENAME(uset_clear) +#define uset_clone U_ICU_ENTRY_POINT_RENAME(uset_clone) +#define uset_cloneAsThawed U_ICU_ENTRY_POINT_RENAME(uset_cloneAsThawed) +#define uset_close U_ICU_ENTRY_POINT_RENAME(uset_close) +#define uset_closeOver U_ICU_ENTRY_POINT_RENAME(uset_closeOver) +#define uset_compact U_ICU_ENTRY_POINT_RENAME(uset_compact) +#define uset_complement U_ICU_ENTRY_POINT_RENAME(uset_complement) +#define uset_complementAll U_ICU_ENTRY_POINT_RENAME(uset_complementAll) +#define uset_complementAllCodePoints U_ICU_ENTRY_POINT_RENAME(uset_complementAllCodePoints) +#define uset_complementRange U_ICU_ENTRY_POINT_RENAME(uset_complementRange) +#define uset_complementString U_ICU_ENTRY_POINT_RENAME(uset_complementString) +#define uset_contains U_ICU_ENTRY_POINT_RENAME(uset_contains) +#define uset_containsAll U_ICU_ENTRY_POINT_RENAME(uset_containsAll) +#define uset_containsAllCodePoints U_ICU_ENTRY_POINT_RENAME(uset_containsAllCodePoints) +#define uset_containsNone U_ICU_ENTRY_POINT_RENAME(uset_containsNone) +#define uset_containsRange U_ICU_ENTRY_POINT_RENAME(uset_containsRange) +#define uset_containsSome U_ICU_ENTRY_POINT_RENAME(uset_containsSome) +#define uset_containsString U_ICU_ENTRY_POINT_RENAME(uset_containsString) +#define uset_equals U_ICU_ENTRY_POINT_RENAME(uset_equals) +#define uset_freeze U_ICU_ENTRY_POINT_RENAME(uset_freeze) +#define uset_getItem U_ICU_ENTRY_POINT_RENAME(uset_getItem) +#define uset_getItemCount U_ICU_ENTRY_POINT_RENAME(uset_getItemCount) +#define uset_getRangeCount U_ICU_ENTRY_POINT_RENAME(uset_getRangeCount) +#define uset_getSerializedRange U_ICU_ENTRY_POINT_RENAME(uset_getSerializedRange) +#define uset_getSerializedRangeCount U_ICU_ENTRY_POINT_RENAME(uset_getSerializedRangeCount) +#define uset_getSerializedSet U_ICU_ENTRY_POINT_RENAME(uset_getSerializedSet) +#define uset_hasStrings U_ICU_ENTRY_POINT_RENAME(uset_hasStrings) +#define uset_indexOf U_ICU_ENTRY_POINT_RENAME(uset_indexOf) +#define uset_isEmpty U_ICU_ENTRY_POINT_RENAME(uset_isEmpty) +#define uset_isFrozen U_ICU_ENTRY_POINT_RENAME(uset_isFrozen) +#define uset_open U_ICU_ENTRY_POINT_RENAME(uset_open) +#define uset_openEmpty U_ICU_ENTRY_POINT_RENAME(uset_openEmpty) +#define uset_openPattern U_ICU_ENTRY_POINT_RENAME(uset_openPattern) +#define uset_openPatternOptions U_ICU_ENTRY_POINT_RENAME(uset_openPatternOptions) +#define uset_remove U_ICU_ENTRY_POINT_RENAME(uset_remove) +#define uset_removeAll U_ICU_ENTRY_POINT_RENAME(uset_removeAll) +#define uset_removeAllCodePoints U_ICU_ENTRY_POINT_RENAME(uset_removeAllCodePoints) +#define uset_removeAllStrings U_ICU_ENTRY_POINT_RENAME(uset_removeAllStrings) +#define uset_removeRange U_ICU_ENTRY_POINT_RENAME(uset_removeRange) +#define uset_removeString U_ICU_ENTRY_POINT_RENAME(uset_removeString) +#define uset_resemblesPattern U_ICU_ENTRY_POINT_RENAME(uset_resemblesPattern) +#define uset_retain U_ICU_ENTRY_POINT_RENAME(uset_retain) +#define uset_retainAll U_ICU_ENTRY_POINT_RENAME(uset_retainAll) +#define uset_retainAllCodePoints U_ICU_ENTRY_POINT_RENAME(uset_retainAllCodePoints) +#define uset_retainString U_ICU_ENTRY_POINT_RENAME(uset_retainString) +#define uset_serialize U_ICU_ENTRY_POINT_RENAME(uset_serialize) +#define uset_serializedContains U_ICU_ENTRY_POINT_RENAME(uset_serializedContains) +#define uset_set U_ICU_ENTRY_POINT_RENAME(uset_set) +#define uset_setSerializedToOne U_ICU_ENTRY_POINT_RENAME(uset_setSerializedToOne) +#define uset_size U_ICU_ENTRY_POINT_RENAME(uset_size) +#define uset_span U_ICU_ENTRY_POINT_RENAME(uset_span) +#define uset_spanBack U_ICU_ENTRY_POINT_RENAME(uset_spanBack) +#define uset_spanBackUTF8 U_ICU_ENTRY_POINT_RENAME(uset_spanBackUTF8) +#define uset_spanUTF8 U_ICU_ENTRY_POINT_RENAME(uset_spanUTF8) +#define uset_toPattern U_ICU_ENTRY_POINT_RENAME(uset_toPattern) +#define usnum_close U_ICU_ENTRY_POINT_RENAME(usnum_close) +#define usnum_multiplyByPowerOfTen U_ICU_ENTRY_POINT_RENAME(usnum_multiplyByPowerOfTen) +#define usnum_openForInt64 U_ICU_ENTRY_POINT_RENAME(usnum_openForInt64) +#define usnum_roundTo U_ICU_ENTRY_POINT_RENAME(usnum_roundTo) +#define usnum_setMinimumFractionDigits U_ICU_ENTRY_POINT_RENAME(usnum_setMinimumFractionDigits) +#define usnum_setMinimumIntegerDigits U_ICU_ENTRY_POINT_RENAME(usnum_setMinimumIntegerDigits) +#define usnum_setSign U_ICU_ENTRY_POINT_RENAME(usnum_setSign) +#define usnum_setToInt64 U_ICU_ENTRY_POINT_RENAME(usnum_setToInt64) +#define usnum_truncateStart U_ICU_ENTRY_POINT_RENAME(usnum_truncateStart) +#define usnumf_close U_ICU_ENTRY_POINT_RENAME(usnumf_close) +#define usnumf_format U_ICU_ENTRY_POINT_RENAME(usnumf_format) +#define usnumf_formatInt64 U_ICU_ENTRY_POINT_RENAME(usnumf_formatInt64) +#define usnumf_openForLocale U_ICU_ENTRY_POINT_RENAME(usnumf_openForLocale) +#define usnumf_openForLocaleAndGroupingStrategy U_ICU_ENTRY_POINT_RENAME(usnumf_openForLocaleAndGroupingStrategy) +#define uspoof_areConfusable U_ICU_ENTRY_POINT_RENAME(uspoof_areConfusable) +#define uspoof_areConfusableUTF8 U_ICU_ENTRY_POINT_RENAME(uspoof_areConfusableUTF8) +#define uspoof_areConfusableUnicodeString U_ICU_ENTRY_POINT_RENAME(uspoof_areConfusableUnicodeString) +#define uspoof_check U_ICU_ENTRY_POINT_RENAME(uspoof_check) +#define uspoof_check2 U_ICU_ENTRY_POINT_RENAME(uspoof_check2) +#define uspoof_check2UTF8 U_ICU_ENTRY_POINT_RENAME(uspoof_check2UTF8) +#define uspoof_check2UnicodeString U_ICU_ENTRY_POINT_RENAME(uspoof_check2UnicodeString) +#define uspoof_checkUTF8 U_ICU_ENTRY_POINT_RENAME(uspoof_checkUTF8) +#define uspoof_checkUnicodeString U_ICU_ENTRY_POINT_RENAME(uspoof_checkUnicodeString) +#define uspoof_clone U_ICU_ENTRY_POINT_RENAME(uspoof_clone) +#define uspoof_close U_ICU_ENTRY_POINT_RENAME(uspoof_close) +#define uspoof_closeCheckResult U_ICU_ENTRY_POINT_RENAME(uspoof_closeCheckResult) +#define uspoof_getAllowedChars U_ICU_ENTRY_POINT_RENAME(uspoof_getAllowedChars) +#define uspoof_getAllowedLocales U_ICU_ENTRY_POINT_RENAME(uspoof_getAllowedLocales) +#define uspoof_getAllowedUnicodeSet U_ICU_ENTRY_POINT_RENAME(uspoof_getAllowedUnicodeSet) +#define uspoof_getCheckResultChecks U_ICU_ENTRY_POINT_RENAME(uspoof_getCheckResultChecks) +#define uspoof_getCheckResultNumerics U_ICU_ENTRY_POINT_RENAME(uspoof_getCheckResultNumerics) +#define uspoof_getCheckResultRestrictionLevel U_ICU_ENTRY_POINT_RENAME(uspoof_getCheckResultRestrictionLevel) +#define uspoof_getChecks U_ICU_ENTRY_POINT_RENAME(uspoof_getChecks) +#define uspoof_getInclusionSet U_ICU_ENTRY_POINT_RENAME(uspoof_getInclusionSet) +#define uspoof_getInclusionUnicodeSet U_ICU_ENTRY_POINT_RENAME(uspoof_getInclusionUnicodeSet) +#define uspoof_getRecommendedSet U_ICU_ENTRY_POINT_RENAME(uspoof_getRecommendedSet) +#define uspoof_getRecommendedUnicodeSet U_ICU_ENTRY_POINT_RENAME(uspoof_getRecommendedUnicodeSet) +#define uspoof_getRestrictionLevel U_ICU_ENTRY_POINT_RENAME(uspoof_getRestrictionLevel) +#define uspoof_getSkeleton U_ICU_ENTRY_POINT_RENAME(uspoof_getSkeleton) +#define uspoof_getSkeletonUTF8 U_ICU_ENTRY_POINT_RENAME(uspoof_getSkeletonUTF8) +#define uspoof_getSkeletonUnicodeString U_ICU_ENTRY_POINT_RENAME(uspoof_getSkeletonUnicodeString) +#define uspoof_internalInitStatics U_ICU_ENTRY_POINT_RENAME(uspoof_internalInitStatics) +#define uspoof_open U_ICU_ENTRY_POINT_RENAME(uspoof_open) +#define uspoof_openCheckResult U_ICU_ENTRY_POINT_RENAME(uspoof_openCheckResult) +#define uspoof_openFromSerialized U_ICU_ENTRY_POINT_RENAME(uspoof_openFromSerialized) +#define uspoof_openFromSource U_ICU_ENTRY_POINT_RENAME(uspoof_openFromSource) +#define uspoof_serialize U_ICU_ENTRY_POINT_RENAME(uspoof_serialize) +#define uspoof_setAllowedChars U_ICU_ENTRY_POINT_RENAME(uspoof_setAllowedChars) +#define uspoof_setAllowedLocales U_ICU_ENTRY_POINT_RENAME(uspoof_setAllowedLocales) +#define uspoof_setAllowedUnicodeSet U_ICU_ENTRY_POINT_RENAME(uspoof_setAllowedUnicodeSet) +#define uspoof_setChecks U_ICU_ENTRY_POINT_RENAME(uspoof_setChecks) +#define uspoof_setRestrictionLevel U_ICU_ENTRY_POINT_RENAME(uspoof_setRestrictionLevel) +#define uspoof_swap U_ICU_ENTRY_POINT_RENAME(uspoof_swap) +#define usprep_close U_ICU_ENTRY_POINT_RENAME(usprep_close) +#define usprep_open U_ICU_ENTRY_POINT_RENAME(usprep_open) +#define usprep_openByType U_ICU_ENTRY_POINT_RENAME(usprep_openByType) +#define usprep_prepare U_ICU_ENTRY_POINT_RENAME(usprep_prepare) +#define usprep_swap U_ICU_ENTRY_POINT_RENAME(usprep_swap) +#define ustr_hashCharsN U_ICU_ENTRY_POINT_RENAME(ustr_hashCharsN) +#define ustr_hashICharsN U_ICU_ENTRY_POINT_RENAME(ustr_hashICharsN) +#define ustr_hashUCharsN U_ICU_ENTRY_POINT_RENAME(ustr_hashUCharsN) +#define ustrcase_getCaseLocale U_ICU_ENTRY_POINT_RENAME(ustrcase_getCaseLocale) +#define ustrcase_getTitleBreakIterator U_ICU_ENTRY_POINT_RENAME(ustrcase_getTitleBreakIterator) +#define ustrcase_internalFold U_ICU_ENTRY_POINT_RENAME(ustrcase_internalFold) +#define ustrcase_internalToLower U_ICU_ENTRY_POINT_RENAME(ustrcase_internalToLower) +#define ustrcase_internalToTitle U_ICU_ENTRY_POINT_RENAME(ustrcase_internalToTitle) +#define ustrcase_internalToUpper U_ICU_ENTRY_POINT_RENAME(ustrcase_internalToUpper) +#define ustrcase_map U_ICU_ENTRY_POINT_RENAME(ustrcase_map) +#define ustrcase_mapWithOverlap U_ICU_ENTRY_POINT_RENAME(ustrcase_mapWithOverlap) +#define utext_char32At U_ICU_ENTRY_POINT_RENAME(utext_char32At) +#define utext_clone U_ICU_ENTRY_POINT_RENAME(utext_clone) +#define utext_close U_ICU_ENTRY_POINT_RENAME(utext_close) +#define utext_copy U_ICU_ENTRY_POINT_RENAME(utext_copy) +#define utext_current32 U_ICU_ENTRY_POINT_RENAME(utext_current32) +#define utext_equals U_ICU_ENTRY_POINT_RENAME(utext_equals) +#define utext_extract U_ICU_ENTRY_POINT_RENAME(utext_extract) +#define utext_freeze U_ICU_ENTRY_POINT_RENAME(utext_freeze) +#define utext_getNativeIndex U_ICU_ENTRY_POINT_RENAME(utext_getNativeIndex) +#define utext_getPreviousNativeIndex U_ICU_ENTRY_POINT_RENAME(utext_getPreviousNativeIndex) +#define utext_hasMetaData U_ICU_ENTRY_POINT_RENAME(utext_hasMetaData) +#define utext_isLengthExpensive U_ICU_ENTRY_POINT_RENAME(utext_isLengthExpensive) +#define utext_isWritable U_ICU_ENTRY_POINT_RENAME(utext_isWritable) +#define utext_moveIndex32 U_ICU_ENTRY_POINT_RENAME(utext_moveIndex32) +#define utext_nativeLength U_ICU_ENTRY_POINT_RENAME(utext_nativeLength) +#define utext_next32 U_ICU_ENTRY_POINT_RENAME(utext_next32) +#define utext_next32From U_ICU_ENTRY_POINT_RENAME(utext_next32From) +#define utext_openCharacterIterator U_ICU_ENTRY_POINT_RENAME(utext_openCharacterIterator) +#define utext_openConstUnicodeString U_ICU_ENTRY_POINT_RENAME(utext_openConstUnicodeString) +#define utext_openReplaceable U_ICU_ENTRY_POINT_RENAME(utext_openReplaceable) +#define utext_openUChars U_ICU_ENTRY_POINT_RENAME(utext_openUChars) +#define utext_openUTF8 U_ICU_ENTRY_POINT_RENAME(utext_openUTF8) +#define utext_openUnicodeString U_ICU_ENTRY_POINT_RENAME(utext_openUnicodeString) +#define utext_previous32 U_ICU_ENTRY_POINT_RENAME(utext_previous32) +#define utext_previous32From U_ICU_ENTRY_POINT_RENAME(utext_previous32From) +#define utext_replace U_ICU_ENTRY_POINT_RENAME(utext_replace) +#define utext_setNativeIndex U_ICU_ENTRY_POINT_RENAME(utext_setNativeIndex) +#define utext_setup U_ICU_ENTRY_POINT_RENAME(utext_setup) +#define utf8_appendCharSafeBody U_ICU_ENTRY_POINT_RENAME(utf8_appendCharSafeBody) +#define utf8_back1SafeBody U_ICU_ENTRY_POINT_RENAME(utf8_back1SafeBody) +#define utf8_countTrailBytes U_ICU_ENTRY_POINT_RENAME(utf8_countTrailBytes) +#define utf8_nextCharSafeBody U_ICU_ENTRY_POINT_RENAME(utf8_nextCharSafeBody) +#define utf8_prevCharSafeBody U_ICU_ENTRY_POINT_RENAME(utf8_prevCharSafeBody) +#define utmscale_fromInt64 U_ICU_ENTRY_POINT_RENAME(utmscale_fromInt64) +#define utmscale_getTimeScaleValue U_ICU_ENTRY_POINT_RENAME(utmscale_getTimeScaleValue) +#define utmscale_toInt64 U_ICU_ENTRY_POINT_RENAME(utmscale_toInt64) +#define utrace_cleanup U_ICU_ENTRY_POINT_RENAME(utrace_cleanup) +#define utrace_data U_ICU_ENTRY_POINT_RENAME(utrace_data) +#define utrace_entry U_ICU_ENTRY_POINT_RENAME(utrace_entry) +#define utrace_exit U_ICU_ENTRY_POINT_RENAME(utrace_exit) +#define utrace_format U_ICU_ENTRY_POINT_RENAME(utrace_format) +#define utrace_functionName U_ICU_ENTRY_POINT_RENAME(utrace_functionName) +#define utrace_getFunctions U_ICU_ENTRY_POINT_RENAME(utrace_getFunctions) +#define utrace_getLevel U_ICU_ENTRY_POINT_RENAME(utrace_getLevel) +#define utrace_setFunctions U_ICU_ENTRY_POINT_RENAME(utrace_setFunctions) +#define utrace_setLevel U_ICU_ENTRY_POINT_RENAME(utrace_setLevel) +#define utrace_vformat U_ICU_ENTRY_POINT_RENAME(utrace_vformat) +#define utrans_clone U_ICU_ENTRY_POINT_RENAME(utrans_clone) +#define utrans_close U_ICU_ENTRY_POINT_RENAME(utrans_close) +#define utrans_countAvailableIDs U_ICU_ENTRY_POINT_RENAME(utrans_countAvailableIDs) +#define utrans_getAvailableID U_ICU_ENTRY_POINT_RENAME(utrans_getAvailableID) +#define utrans_getID U_ICU_ENTRY_POINT_RENAME(utrans_getID) +#define utrans_getSourceSet U_ICU_ENTRY_POINT_RENAME(utrans_getSourceSet) +#define utrans_getUnicodeID U_ICU_ENTRY_POINT_RENAME(utrans_getUnicodeID) +#define utrans_open U_ICU_ENTRY_POINT_RENAME(utrans_open) +#define utrans_openIDs U_ICU_ENTRY_POINT_RENAME(utrans_openIDs) +#define utrans_openInverse U_ICU_ENTRY_POINT_RENAME(utrans_openInverse) +#define utrans_openU U_ICU_ENTRY_POINT_RENAME(utrans_openU) +#define utrans_register U_ICU_ENTRY_POINT_RENAME(utrans_register) +#define utrans_rep_caseContextIterator U_ICU_ENTRY_POINT_RENAME(utrans_rep_caseContextIterator) +#define utrans_setFilter U_ICU_ENTRY_POINT_RENAME(utrans_setFilter) +#define utrans_stripRules U_ICU_ENTRY_POINT_RENAME(utrans_stripRules) +#define utrans_toRules U_ICU_ENTRY_POINT_RENAME(utrans_toRules) +#define utrans_trans U_ICU_ENTRY_POINT_RENAME(utrans_trans) +#define utrans_transIncremental U_ICU_ENTRY_POINT_RENAME(utrans_transIncremental) +#define utrans_transIncrementalUChars U_ICU_ENTRY_POINT_RENAME(utrans_transIncrementalUChars) +#define utrans_transUChars U_ICU_ENTRY_POINT_RENAME(utrans_transUChars) +#define utrans_transliterator_cleanup U_ICU_ENTRY_POINT_RENAME(utrans_transliterator_cleanup) +#define utrans_unregister U_ICU_ENTRY_POINT_RENAME(utrans_unregister) +#define utrans_unregisterID U_ICU_ENTRY_POINT_RENAME(utrans_unregisterID) +#define utrie2_clone U_ICU_ENTRY_POINT_RENAME(utrie2_clone) +#define utrie2_cloneAsThawed U_ICU_ENTRY_POINT_RENAME(utrie2_cloneAsThawed) +#define utrie2_close U_ICU_ENTRY_POINT_RENAME(utrie2_close) +#define utrie2_enum U_ICU_ENTRY_POINT_RENAME(utrie2_enum) +#define utrie2_enumForLeadSurrogate U_ICU_ENTRY_POINT_RENAME(utrie2_enumForLeadSurrogate) +#define utrie2_freeze U_ICU_ENTRY_POINT_RENAME(utrie2_freeze) +#define utrie2_fromUTrie U_ICU_ENTRY_POINT_RENAME(utrie2_fromUTrie) +#define utrie2_get32 U_ICU_ENTRY_POINT_RENAME(utrie2_get32) +#define utrie2_get32FromLeadSurrogateCodeUnit U_ICU_ENTRY_POINT_RENAME(utrie2_get32FromLeadSurrogateCodeUnit) +#define utrie2_internalU8NextIndex U_ICU_ENTRY_POINT_RENAME(utrie2_internalU8NextIndex) +#define utrie2_internalU8PrevIndex U_ICU_ENTRY_POINT_RENAME(utrie2_internalU8PrevIndex) +#define utrie2_isFrozen U_ICU_ENTRY_POINT_RENAME(utrie2_isFrozen) +#define utrie2_open U_ICU_ENTRY_POINT_RENAME(utrie2_open) +#define utrie2_openDummy U_ICU_ENTRY_POINT_RENAME(utrie2_openDummy) +#define utrie2_openFromSerialized U_ICU_ENTRY_POINT_RENAME(utrie2_openFromSerialized) +#define utrie2_serialize U_ICU_ENTRY_POINT_RENAME(utrie2_serialize) +#define utrie2_set32 U_ICU_ENTRY_POINT_RENAME(utrie2_set32) +#define utrie2_set32ForLeadSurrogateCodeUnit U_ICU_ENTRY_POINT_RENAME(utrie2_set32ForLeadSurrogateCodeUnit) +#define utrie2_setRange32 U_ICU_ENTRY_POINT_RENAME(utrie2_setRange32) +#define utrie2_swap U_ICU_ENTRY_POINT_RENAME(utrie2_swap) +#define utrie_clone U_ICU_ENTRY_POINT_RENAME(utrie_clone) +#define utrie_close U_ICU_ENTRY_POINT_RENAME(utrie_close) +#define utrie_defaultGetFoldingOffset U_ICU_ENTRY_POINT_RENAME(utrie_defaultGetFoldingOffset) +#define utrie_enum U_ICU_ENTRY_POINT_RENAME(utrie_enum) +#define utrie_get32 U_ICU_ENTRY_POINT_RENAME(utrie_get32) +#define utrie_getData U_ICU_ENTRY_POINT_RENAME(utrie_getData) +#define utrie_open U_ICU_ENTRY_POINT_RENAME(utrie_open) +#define utrie_serialize U_ICU_ENTRY_POINT_RENAME(utrie_serialize) +#define utrie_set32 U_ICU_ENTRY_POINT_RENAME(utrie_set32) +#define utrie_setRange32 U_ICU_ENTRY_POINT_RENAME(utrie_setRange32) +#define utrie_swap U_ICU_ENTRY_POINT_RENAME(utrie_swap) +#define utrie_swapAnyVersion U_ICU_ENTRY_POINT_RENAME(utrie_swapAnyVersion) +#define utrie_unserialize U_ICU_ENTRY_POINT_RENAME(utrie_unserialize) +#define utrie_unserializeDummy U_ICU_ENTRY_POINT_RENAME(utrie_unserializeDummy) +#define vzone_clone U_ICU_ENTRY_POINT_RENAME(vzone_clone) +#define vzone_close U_ICU_ENTRY_POINT_RENAME(vzone_close) +#define vzone_countTransitionRules U_ICU_ENTRY_POINT_RENAME(vzone_countTransitionRules) +#define vzone_equals U_ICU_ENTRY_POINT_RENAME(vzone_equals) +#define vzone_getDynamicClassID U_ICU_ENTRY_POINT_RENAME(vzone_getDynamicClassID) +#define vzone_getLastModified U_ICU_ENTRY_POINT_RENAME(vzone_getLastModified) +#define vzone_getNextTransition U_ICU_ENTRY_POINT_RENAME(vzone_getNextTransition) +#define vzone_getOffset U_ICU_ENTRY_POINT_RENAME(vzone_getOffset) +#define vzone_getOffset2 U_ICU_ENTRY_POINT_RENAME(vzone_getOffset2) +#define vzone_getOffset3 U_ICU_ENTRY_POINT_RENAME(vzone_getOffset3) +#define vzone_getPreviousTransition U_ICU_ENTRY_POINT_RENAME(vzone_getPreviousTransition) +#define vzone_getRawOffset U_ICU_ENTRY_POINT_RENAME(vzone_getRawOffset) +#define vzone_getStaticClassID U_ICU_ENTRY_POINT_RENAME(vzone_getStaticClassID) +#define vzone_getTZURL U_ICU_ENTRY_POINT_RENAME(vzone_getTZURL) +#define vzone_hasSameRules U_ICU_ENTRY_POINT_RENAME(vzone_hasSameRules) +#define vzone_inDaylightTime U_ICU_ENTRY_POINT_RENAME(vzone_inDaylightTime) +#define vzone_openData U_ICU_ENTRY_POINT_RENAME(vzone_openData) +#define vzone_openID U_ICU_ENTRY_POINT_RENAME(vzone_openID) +#define vzone_setLastModified U_ICU_ENTRY_POINT_RENAME(vzone_setLastModified) +#define vzone_setRawOffset U_ICU_ENTRY_POINT_RENAME(vzone_setRawOffset) +#define vzone_setTZURL U_ICU_ENTRY_POINT_RENAME(vzone_setTZURL) +#define vzone_useDaylightTime U_ICU_ENTRY_POINT_RENAME(vzone_useDaylightTime) +#define vzone_write U_ICU_ENTRY_POINT_RENAME(vzone_write) +#define vzone_writeFromStart U_ICU_ENTRY_POINT_RENAME(vzone_writeFromStart) +#define vzone_writeSimple U_ICU_ENTRY_POINT_RENAME(vzone_writeSimple) +#define zrule_close U_ICU_ENTRY_POINT_RENAME(zrule_close) +#define zrule_equals U_ICU_ENTRY_POINT_RENAME(zrule_equals) +#define zrule_getDSTSavings U_ICU_ENTRY_POINT_RENAME(zrule_getDSTSavings) +#define zrule_getName U_ICU_ENTRY_POINT_RENAME(zrule_getName) +#define zrule_getRawOffset U_ICU_ENTRY_POINT_RENAME(zrule_getRawOffset) +#define zrule_isEquivalentTo U_ICU_ENTRY_POINT_RENAME(zrule_isEquivalentTo) +#define ztrans_adoptFrom U_ICU_ENTRY_POINT_RENAME(ztrans_adoptFrom) +#define ztrans_adoptTo U_ICU_ENTRY_POINT_RENAME(ztrans_adoptTo) +#define ztrans_clone U_ICU_ENTRY_POINT_RENAME(ztrans_clone) +#define ztrans_close U_ICU_ENTRY_POINT_RENAME(ztrans_close) +#define ztrans_equals U_ICU_ENTRY_POINT_RENAME(ztrans_equals) +#define ztrans_getDynamicClassID U_ICU_ENTRY_POINT_RENAME(ztrans_getDynamicClassID) +#define ztrans_getFrom U_ICU_ENTRY_POINT_RENAME(ztrans_getFrom) +#define ztrans_getStaticClassID U_ICU_ENTRY_POINT_RENAME(ztrans_getStaticClassID) +#define ztrans_getTime U_ICU_ENTRY_POINT_RENAME(ztrans_getTime) +#define ztrans_getTo U_ICU_ENTRY_POINT_RENAME(ztrans_getTo) +#define ztrans_open U_ICU_ENTRY_POINT_RENAME(ztrans_open) +#define ztrans_openEmpty U_ICU_ENTRY_POINT_RENAME(ztrans_openEmpty) +#define ztrans_setFrom U_ICU_ENTRY_POINT_RENAME(ztrans_setFrom) +#define ztrans_setTime U_ICU_ENTRY_POINT_RENAME(ztrans_setTime) +#define ztrans_setTo U_ICU_ENTRY_POINT_RENAME(ztrans_setTo) + +#endif /* !(defined(_MSC_VER) && defined(__INTELLISENSE__)) */ +#endif /* U_DISABLE_RENAMING */ +#endif /* URENAME_H */ + diff --git a/deps/icu-small/source/common/unicode/urep.h b/deps/icu-small/source/common/unicode/urep.h index 932202ddb04b1c..593eca94c4b661 100644 --- a/deps/icu-small/source/common/unicode/urep.h +++ b/deps/icu-small/source/common/unicode/urep.h @@ -1,157 +1,157 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 1997-2010, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* Date Name Description -* 06/23/00 aliu Creation. -****************************************************************************** -*/ - -#ifndef __UREP_H -#define __UREP_H - -#include "unicode/utypes.h" - -U_CDECL_BEGIN - -/******************************************************************** - * General Notes - ******************************************************************** - * TODO - * Add usage scenario - * Add test code - * Talk about pinning - * Talk about "can truncate result if out of memory" - */ - -/******************************************************************** - * Data Structures - ********************************************************************/ -/** - * \file - * \brief C API: Callbacks for UReplaceable - */ -/** - * An opaque replaceable text object. This will be manipulated only - * through the caller-supplied UReplaceableFunctor struct. Related - * to the C++ class Replaceable. - * This is currently only used in the Transliterator C API, see utrans.h . - * @stable ICU 2.0 - */ -typedef void* UReplaceable; - -/** - * A set of function pointers that transliterators use to manipulate a - * UReplaceable. The caller should supply the required functions to - * manipulate their text appropriately. Related to the C++ class - * Replaceable. - * @stable ICU 2.0 - */ -typedef struct UReplaceableCallbacks { - - /** - * Function pointer that returns the number of UChar code units in - * this text. - * - * @param rep A pointer to "this" UReplaceable object. - * @return The length of the text. - * @stable ICU 2.0 - */ - int32_t (*length)(const UReplaceable* rep); - - /** - * Function pointer that returns a UChar code units at the given - * offset into this text; 0 <= offset < n, where n is the value - * returned by (*length)(rep). See unistr.h for a description of - * charAt() vs. char32At(). - * - * @param rep A pointer to "this" UReplaceable object. - * @param offset The index at which to fetch the UChar (code unit). - * @return The UChar (code unit) at offset, or U+FFFF if the offset is out of bounds. - * @stable ICU 2.0 - */ - UChar (*charAt)(const UReplaceable* rep, - int32_t offset); - - /** - * Function pointer that returns a UChar32 code point at the given - * offset into this text. See unistr.h for a description of - * charAt() vs. char32At(). - * - * @param rep A pointer to "this" UReplaceable object. - * @param offset The index at which to fetch the UChar32 (code point). - * @return The UChar32 (code point) at offset, or U+FFFF if the offset is out of bounds. - * @stable ICU 2.0 - */ - UChar32 (*char32At)(const UReplaceable* rep, - int32_t offset); - - /** - * Function pointer that replaces text between start and limit in - * this text with the given text. Attributes (out of band info) - * should be retained. - * - * @param rep A pointer to "this" UReplaceable object. - * @param start the starting index of the text to be replaced, - * inclusive. - * @param limit the ending index of the text to be replaced, - * exclusive. - * @param text the new text to replace the UChars from - * start..limit-1. - * @param textLength the number of UChars at text, or -1 if text - * is null-terminated. - * @stable ICU 2.0 - */ - void (*replace)(UReplaceable* rep, - int32_t start, - int32_t limit, - const UChar* text, - int32_t textLength); - - /** - * Function pointer that copies the characters in the range - * [start, limit) into the array dst. - * - * @param rep A pointer to "this" UReplaceable object. - * @param start offset of first character which will be copied - * into the array - * @param limit offset immediately following the last character to - * be copied - * @param dst array in which to copy characters. The length of - * dst must be at least (limit - start). - * @stable ICU 2.1 - */ - void (*extract)(UReplaceable* rep, - int32_t start, - int32_t limit, - UChar* dst); - - /** - * Function pointer that copies text between start and limit in - * this text to another index in the text. Attributes (out of - * band info) should be retained. After this call, there will be - * (at least) two copies of the characters originally located at - * start..limit-1. - * - * @param rep A pointer to "this" UReplaceable object. - * @param start the starting index of the text to be copied, - * inclusive. - * @param limit the ending index of the text to be copied, - * exclusive. - * @param dest the index at which the copy of the UChars should be - * inserted. - * @stable ICU 2.0 - */ - void (*copy)(UReplaceable* rep, - int32_t start, - int32_t limit, - int32_t dest); - -} UReplaceableCallbacks; - -U_CDECL_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 1997-2010, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* Date Name Description +* 06/23/00 aliu Creation. +****************************************************************************** +*/ + +#ifndef __UREP_H +#define __UREP_H + +#include "unicode/utypes.h" + +U_CDECL_BEGIN + +/******************************************************************** + * General Notes + ******************************************************************** + * TODO + * Add usage scenario + * Add test code + * Talk about pinning + * Talk about "can truncate result if out of memory" + */ + +/******************************************************************** + * Data Structures + ********************************************************************/ +/** + * \file + * \brief C API: Callbacks for UReplaceable + */ +/** + * An opaque replaceable text object. This will be manipulated only + * through the caller-supplied UReplaceableFunctor struct. Related + * to the C++ class Replaceable. + * This is currently only used in the Transliterator C API, see utrans.h . + * @stable ICU 2.0 + */ +typedef void* UReplaceable; + +/** + * A set of function pointers that transliterators use to manipulate a + * UReplaceable. The caller should supply the required functions to + * manipulate their text appropriately. Related to the C++ class + * Replaceable. + * @stable ICU 2.0 + */ +typedef struct UReplaceableCallbacks { + + /** + * Function pointer that returns the number of UChar code units in + * this text. + * + * @param rep A pointer to "this" UReplaceable object. + * @return The length of the text. + * @stable ICU 2.0 + */ + int32_t (*length)(const UReplaceable* rep); + + /** + * Function pointer that returns a UChar code units at the given + * offset into this text; 0 <= offset < n, where n is the value + * returned by (*length)(rep). See unistr.h for a description of + * charAt() vs. char32At(). + * + * @param rep A pointer to "this" UReplaceable object. + * @param offset The index at which to fetch the UChar (code unit). + * @return The UChar (code unit) at offset, or U+FFFF if the offset is out of bounds. + * @stable ICU 2.0 + */ + UChar (*charAt)(const UReplaceable* rep, + int32_t offset); + + /** + * Function pointer that returns a UChar32 code point at the given + * offset into this text. See unistr.h for a description of + * charAt() vs. char32At(). + * + * @param rep A pointer to "this" UReplaceable object. + * @param offset The index at which to fetch the UChar32 (code point). + * @return The UChar32 (code point) at offset, or U+FFFF if the offset is out of bounds. + * @stable ICU 2.0 + */ + UChar32 (*char32At)(const UReplaceable* rep, + int32_t offset); + + /** + * Function pointer that replaces text between start and limit in + * this text with the given text. Attributes (out of band info) + * should be retained. + * + * @param rep A pointer to "this" UReplaceable object. + * @param start the starting index of the text to be replaced, + * inclusive. + * @param limit the ending index of the text to be replaced, + * exclusive. + * @param text the new text to replace the UChars from + * start..limit-1. + * @param textLength the number of UChars at text, or -1 if text + * is null-terminated. + * @stable ICU 2.0 + */ + void (*replace)(UReplaceable* rep, + int32_t start, + int32_t limit, + const UChar* text, + int32_t textLength); + + /** + * Function pointer that copies the characters in the range + * [start, limit) into the array dst. + * + * @param rep A pointer to "this" UReplaceable object. + * @param start offset of first character which will be copied + * into the array + * @param limit offset immediately following the last character to + * be copied + * @param dst array in which to copy characters. The length of + * dst must be at least (limit - start). + * @stable ICU 2.1 + */ + void (*extract)(UReplaceable* rep, + int32_t start, + int32_t limit, + UChar* dst); + + /** + * Function pointer that copies text between start and limit in + * this text to another index in the text. Attributes (out of + * band info) should be retained. After this call, there will be + * (at least) two copies of the characters originally located at + * start..limit-1. + * + * @param rep A pointer to "this" UReplaceable object. + * @param start the starting index of the text to be copied, + * inclusive. + * @param limit the ending index of the text to be copied, + * exclusive. + * @param dest the index at which the copy of the UChars should be + * inserted. + * @stable ICU 2.0 + */ + void (*copy)(UReplaceable* rep, + int32_t start, + int32_t limit, + int32_t dest); + +} UReplaceableCallbacks; + +U_CDECL_END + +#endif diff --git a/deps/icu-small/source/common/unicode/ures.h b/deps/icu-small/source/common/unicode/ures.h index a6c43f9537a55e..6858c5addd0e07 100644 --- a/deps/icu-small/source/common/unicode/ures.h +++ b/deps/icu-small/source/common/unicode/ures.h @@ -1,911 +1,911 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1997-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* File URES.H (formerly CRESBUND.H) -* -* Modification History: -* -* Date Name Description -* 04/01/97 aliu Creation. -* 02/22/99 damiba overhaul. -* 04/04/99 helena Fixed internal header inclusion. -* 04/15/99 Madhu Updated Javadoc -* 06/14/99 stephen Removed functions taking a filename suffix. -* 07/20/99 stephen Language-independent typedef to void* -* 11/09/99 weiv Added ures_getLocale() -* 06/24/02 weiv Added support for resource sharing -****************************************************************************** -*/ - -#ifndef URES_H -#define URES_H - -#include "unicode/utypes.h" -#include "unicode/uloc.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -/** - * \file - * \brief C API: Resource Bundle - * - *

C API: Resource Bundle

- * - * C API representing a collection of resource information pertaining to a given - * locale. A resource bundle provides a way of accessing locale- specific information in - * a data file. You create a resource bundle that manages the resources for a given - * locale and then ask it for individual resources. - *

- * Resource bundles in ICU4C are currently defined using text files which conform to the following - * BNF definition. - * More on resource bundle concepts and syntax can be found in the - * Users Guide. - *

- */ - -/** - * UResourceBundle is an opaque type for handles for resource bundles in C APIs. - * @stable ICU 2.0 - */ -struct UResourceBundle; - -/** - * @stable ICU 2.0 - */ -typedef struct UResourceBundle UResourceBundle; - -/** - * Numeric constants for types of resource items. - * @see ures_getType - * @stable ICU 2.0 - */ -typedef enum { - /** Resource type constant for "no resource". @stable ICU 2.6 */ - URES_NONE=-1, - - /** Resource type constant for 16-bit Unicode strings. @stable ICU 2.6 */ - URES_STRING=0, - - /** Resource type constant for binary data. @stable ICU 2.6 */ - URES_BINARY=1, - - /** Resource type constant for tables of key-value pairs. @stable ICU 2.6 */ - URES_TABLE=2, - - /** - * Resource type constant for aliases; - * internally stores a string which identifies the actual resource - * storing the data (can be in a different resource bundle). - * Resolved internally before delivering the actual resource through the API. - * @stable ICU 2.6 - */ - URES_ALIAS=3, - - /** - * Resource type constant for a single 28-bit integer, interpreted as - * signed or unsigned by the ures_getInt() or ures_getUInt() function. - * @see ures_getInt - * @see ures_getUInt - * @stable ICU 2.6 - */ - URES_INT=7, - - /** Resource type constant for arrays of resources. @stable ICU 2.6 */ - URES_ARRAY=8, - - /** - * Resource type constant for vectors of 32-bit integers. - * @see ures_getIntVector - * @stable ICU 2.6 - */ - URES_INT_VECTOR = 14, -#ifndef U_HIDE_DEPRECATED_API - /** @deprecated ICU 2.6 Use the URES_ constant instead. */ - RES_NONE=URES_NONE, - /** @deprecated ICU 2.6 Use the URES_ constant instead. */ - RES_STRING=URES_STRING, - /** @deprecated ICU 2.6 Use the URES_ constant instead. */ - RES_BINARY=URES_BINARY, - /** @deprecated ICU 2.6 Use the URES_ constant instead. */ - RES_TABLE=URES_TABLE, - /** @deprecated ICU 2.6 Use the URES_ constant instead. */ - RES_ALIAS=URES_ALIAS, - /** @deprecated ICU 2.6 Use the URES_ constant instead. */ - RES_INT=URES_INT, - /** @deprecated ICU 2.6 Use the URES_ constant instead. */ - RES_ARRAY=URES_ARRAY, - /** @deprecated ICU 2.6 Use the URES_ constant instead. */ - RES_INT_VECTOR=URES_INT_VECTOR, - /** @deprecated ICU 2.6 Not used. */ - RES_RESERVED=15, - - /** - * One more than the highest normal UResType value. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - URES_LIMIT = 16 -#endif // U_HIDE_DEPRECATED_API -} UResType; - -/* - * Functions to create and destroy resource bundles. - */ - -/** - * Opens a UResourceBundle, from which users can extract strings by using - * their corresponding keys. - * Note that the caller is responsible of calling ures_close on each successfully - * opened resource bundle. - * @param packageName The packageName and locale together point to an ICU udata object, - * as defined by udata_open( packageName, "res", locale, err) - * or equivalent. Typically, packageName will refer to a (.dat) file, or to - * a package registered with udata_setAppData(). Using a full file or directory - * pathname for packageName is deprecated. If NULL, ICU data will be used. - * @param locale specifies the locale for which we want to open the resource - * if NULL, the default locale will be used. If strlen(locale) == 0 - * root locale will be used. - * - * @param status fills in the outgoing error code. - * The UErrorCode err parameter is used to return status information to the user. To - * check whether the construction succeeded or not, you should check the value of - * U_SUCCESS(err). If you wish more detailed information, you can check for - * informational status results which still indicate success. U_USING_FALLBACK_WARNING - * indicates that a fall back locale was used. For example, 'de_CH' was requested, - * but nothing was found there, so 'de' was used. U_USING_DEFAULT_WARNING indicates that - * the default locale data or root locale data was used; neither the requested locale - * nor any of its fall back locales could be found. Please see the users guide for more - * information on this topic. - * @return a newly allocated resource bundle. - * @see ures_close - * @stable ICU 2.0 - */ -U_CAPI UResourceBundle* U_EXPORT2 -ures_open(const char* packageName, - const char* locale, - UErrorCode* status); - - -/** This function does not care what kind of localeID is passed in. It simply opens a bundle with - * that name. Fallback mechanism is disabled for the new bundle. If the requested bundle contains - * an %%ALIAS directive, the results are undefined. - * @param packageName The packageName and locale together point to an ICU udata object, - * as defined by udata_open( packageName, "res", locale, err) - * or equivalent. Typically, packageName will refer to a (.dat) file, or to - * a package registered with udata_setAppData(). Using a full file or directory - * pathname for packageName is deprecated. If NULL, ICU data will be used. - * @param locale specifies the locale for which we want to open the resource - * if NULL, the default locale will be used. If strlen(locale) == 0 - * root locale will be used. - * - * @param status fills in the outgoing error code. Either U_ZERO_ERROR or U_MISSING_RESOURCE_ERROR - * @return a newly allocated resource bundle or NULL if it doesn't exist. - * @see ures_close - * @stable ICU 2.0 - */ -U_CAPI UResourceBundle* U_EXPORT2 -ures_openDirect(const char* packageName, - const char* locale, - UErrorCode* status); - -/** - * Same as ures_open() but takes a const UChar *path. - * This path will be converted to char * using the default converter, - * then ures_open() is called. - * - * @param packageName The packageName and locale together point to an ICU udata object, - * as defined by udata_open( packageName, "res", locale, err) - * or equivalent. Typically, packageName will refer to a (.dat) file, or to - * a package registered with udata_setAppData(). Using a full file or directory - * pathname for packageName is deprecated. If NULL, ICU data will be used. - * @param locale specifies the locale for which we want to open the resource - * if NULL, the default locale will be used. If strlen(locale) == 0 - * root locale will be used. - * @param status fills in the outgoing error code. - * @return a newly allocated resource bundle. - * @see ures_open - * @stable ICU 2.0 - */ -U_CAPI UResourceBundle* U_EXPORT2 -ures_openU(const UChar* packageName, - const char* locale, - UErrorCode* status); - -#ifndef U_HIDE_DEPRECATED_API -/** - * Returns the number of strings/arrays in resource bundles. - * Better to use ures_getSize, as this function will be deprecated. - * - *@param resourceBundle resource bundle containing the desired strings - *@param resourceKey key tagging the resource - *@param err fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * could be a non-failing error - * e.g.: U_USING_FALLBACK_WARNING,U_USING_FALLBACK_WARNING - *@return: for Arrays: returns the number of resources in the array - * Tables: returns the number of resources in the table - * single string: returns 1 - *@see ures_getSize - * @deprecated ICU 2.8 User ures_getSize instead - */ -U_DEPRECATED int32_t U_EXPORT2 -ures_countArrayItems(const UResourceBundle* resourceBundle, - const char* resourceKey, - UErrorCode* err); -#endif /* U_HIDE_DEPRECATED_API */ - -/** - * Close a resource bundle, all pointers returned from the various ures_getXXX calls - * on this particular bundle should be considered invalid henceforth. - * - * @param resourceBundle a pointer to a resourceBundle struct. Can be NULL. - * @see ures_open - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ures_close(UResourceBundle* resourceBundle); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUResourceBundlePointer - * "Smart pointer" class, closes a UResourceBundle via ures_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUResourceBundlePointer, UResourceBundle, ures_close); - -U_NAMESPACE_END - -#endif - -#ifndef U_HIDE_DEPRECATED_API -/** - * Return the version number associated with this ResourceBundle as a string. Please - * use ures_getVersion as this function is going to be deprecated. - * - * @param resourceBundle The resource bundle for which the version is checked. - * @return A version number string as specified in the resource bundle or its parent. - * The caller does not own this string. - * @see ures_getVersion - * @deprecated ICU 2.8 Use ures_getVersion instead. - */ -U_DEPRECATED const char* U_EXPORT2 -ures_getVersionNumber(const UResourceBundle* resourceBundle); -#endif /* U_HIDE_DEPRECATED_API */ - -/** - * Return the version number associated with this ResourceBundle as an - * UVersionInfo array. - * - * @param resB The resource bundle for which the version is checked. - * @param versionInfo A UVersionInfo array that is filled with the version number - * as specified in the resource bundle or its parent. - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ures_getVersion(const UResourceBundle* resB, - UVersionInfo versionInfo); - -#ifndef U_HIDE_DEPRECATED_API -/** - * Return the name of the Locale associated with this ResourceBundle. This API allows - * you to query for the real locale of the resource. For example, if you requested - * "en_US_CALIFORNIA" and only "en_US" bundle exists, "en_US" will be returned. - * For subresources, the locale where this resource comes from will be returned. - * If fallback has occurred, getLocale will reflect this. - * - * @param resourceBundle resource bundle in question - * @param status just for catching illegal arguments - * @return A Locale name - * @deprecated ICU 2.8 Use ures_getLocaleByType instead. - */ -U_DEPRECATED const char* U_EXPORT2 -ures_getLocale(const UResourceBundle* resourceBundle, - UErrorCode* status); -#endif /* U_HIDE_DEPRECATED_API */ - -/** - * Return the name of the Locale associated with this ResourceBundle. - * You can choose between requested, valid and real locale. - * - * @param resourceBundle resource bundle in question - * @param type You can choose between requested, valid and actual - * locale. For description see the definition of - * ULocDataLocaleType in uloc.h - * @param status just for catching illegal arguments - * @return A Locale name - * @stable ICU 2.8 - */ -U_CAPI const char* U_EXPORT2 -ures_getLocaleByType(const UResourceBundle* resourceBundle, - ULocDataLocaleType type, - UErrorCode* status); - - -#ifndef U_HIDE_INTERNAL_API -/** - * Same as ures_open() but uses the fill-in parameter instead of allocating a new bundle. - * - * TODO need to revisit usefulness of this function - * and usage model for fillIn parameters without knowing sizeof(UResourceBundle) - * @param r The existing UResourceBundle to fill in. If NULL then status will be - * set to U_ILLEGAL_ARGUMENT_ERROR. - * @param packageName The packageName and locale together point to an ICU udata object, - * as defined by udata_open( packageName, "res", locale, err) - * or equivalent. Typically, packageName will refer to a (.dat) file, or to - * a package registered with udata_setAppData(). Using a full file or directory - * pathname for packageName is deprecated. If NULL, ICU data will be used. - * @param localeID specifies the locale for which we want to open the resource - * @param status The error code. - * @internal - */ -U_CAPI void U_EXPORT2 -ures_openFillIn(UResourceBundle *r, - const char* packageName, - const char* localeID, - UErrorCode* status); -#endif /* U_HIDE_INTERNAL_API */ - -/** - * Returns a string from a string resource type - * - * @param resourceBundle a string resource - * @param len fills in the length of resulting string - * @param status fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * Always check the value of status. Don't count on returning NULL. - * could be a non-failing error - * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING - * @return a pointer to a zero-terminated UChar array which lives in a memory mapped/DLL file. - * @see ures_getBinary - * @see ures_getIntVector - * @see ures_getInt - * @see ures_getUInt - * @stable ICU 2.0 - */ -U_CAPI const UChar* U_EXPORT2 -ures_getString(const UResourceBundle* resourceBundle, - int32_t* len, - UErrorCode* status); - -/** - * Returns a UTF-8 string from a string resource. - * The UTF-8 string may be returnable directly as a pointer, or - * it may need to be copied, or transformed from UTF-16 using u_strToUTF8() - * or equivalent. - * - * If forceCopy==true, then the string is always written to the dest buffer - * and dest is returned. - * - * If forceCopy==false, then the string is returned as a pointer if possible, - * without needing a dest buffer (it can be NULL). If the string needs to be - * copied or transformed, then it may be placed into dest at an arbitrary offset. - * - * If the string is to be written to dest, then U_BUFFER_OVERFLOW_ERROR and - * U_STRING_NOT_TERMINATED_WARNING are set if appropriate, as usual. - * - * If the string is transformed from UTF-16, then a conversion error may occur - * if an unpaired surrogate is encountered. If the function is successful, then - * the output UTF-8 string is always well-formed. - * - * @param resB Resource bundle. - * @param dest Destination buffer. Can be NULL only if capacity=*length==0. - * @param length Input: Capacity of destination buffer. - * Output: Actual length of the UTF-8 string, not counting the - * terminating NUL, even in case of U_BUFFER_OVERFLOW_ERROR. - * Can be NULL, meaning capacity=0 and the string length is not - * returned to the caller. - * @param forceCopy If true, then the output string will always be written to - * dest, with U_BUFFER_OVERFLOW_ERROR and - * U_STRING_NOT_TERMINATED_WARNING set if appropriate. - * If false, then the dest buffer may or may not contain a - * copy of the string. dest may or may not be modified. - * If a copy needs to be written, then the UErrorCode parameter - * indicates overflow etc. as usual. - * @param status Pointer to a standard 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.) - * @return The pointer to the UTF-8 string. It may be dest, or at some offset - * from dest (only if !forceCopy), or in unrelated memory. - * Always NUL-terminated unless the string was written to dest and - * length==capacity (in which case U_STRING_NOT_TERMINATED_WARNING is set). - * - * @see ures_getString - * @see u_strToUTF8 - * @stable ICU 3.6 - */ -U_CAPI const char * U_EXPORT2 -ures_getUTF8String(const UResourceBundle *resB, - char *dest, int32_t *length, - UBool forceCopy, - UErrorCode *status); - -/** - * Returns a binary data from a binary resource. - * - * @param resourceBundle a string resource - * @param len fills in the length of resulting byte chunk - * @param status fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * Always check the value of status. Don't count on returning NULL. - * could be a non-failing error - * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING - * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file. - * @see ures_getString - * @see ures_getIntVector - * @see ures_getInt - * @see ures_getUInt - * @stable ICU 2.0 - */ -U_CAPI const uint8_t* U_EXPORT2 -ures_getBinary(const UResourceBundle* resourceBundle, - int32_t* len, - UErrorCode* status); - -/** - * Returns a 32 bit integer array from a resource. - * - * @param resourceBundle an int vector resource - * @param len fills in the length of resulting byte chunk - * @param status fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * Always check the value of status. Don't count on returning NULL. - * could be a non-failing error - * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING - * @return a pointer to a chunk of integers which live in a memory mapped/DLL file. - * @see ures_getBinary - * @see ures_getString - * @see ures_getInt - * @see ures_getUInt - * @stable ICU 2.0 - */ -U_CAPI const int32_t* U_EXPORT2 -ures_getIntVector(const UResourceBundle* resourceBundle, - int32_t* len, - UErrorCode* status); - -/** - * Returns an unsigned integer from a resource. - * This integer is originally 28 bits. - * - * @param resourceBundle a string resource - * @param status fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * could be a non-failing error - * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING - * @return an integer value - * @see ures_getInt - * @see ures_getIntVector - * @see ures_getBinary - * @see ures_getString - * @stable ICU 2.0 - */ -U_CAPI uint32_t U_EXPORT2 -ures_getUInt(const UResourceBundle* resourceBundle, - UErrorCode *status); - -/** - * Returns a signed integer from a resource. - * This integer is originally 28 bit and the sign gets propagated. - * - * @param resourceBundle a string resource - * @param status fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * could be a non-failing error - * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING - * @return an integer value - * @see ures_getUInt - * @see ures_getIntVector - * @see ures_getBinary - * @see ures_getString - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ures_getInt(const UResourceBundle* resourceBundle, - UErrorCode *status); - -/** - * Returns the size of a resource. Size for scalar types is always 1, - * and for vector/table types is the number of child resources. - * @warning Integer array is treated as a scalar type. There are no - * APIs to access individual members of an integer array. It - * is always returned as a whole. - * @param resourceBundle a resource - * @return number of resources in a given resource. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -ures_getSize(const UResourceBundle *resourceBundle); - -/** - * Returns the type of a resource. Available types are defined in enum UResType - * - * @param resourceBundle a resource - * @return type of the given resource. - * @see UResType - * @stable ICU 2.0 - */ -U_CAPI UResType U_EXPORT2 -ures_getType(const UResourceBundle *resourceBundle); - -/** - * Returns the key associated with a given resource. Not all the resources have a key - only - * those that are members of a table. - * - * @param resourceBundle a resource - * @return a key associated to this resource, or NULL if it doesn't have a key - * @stable ICU 2.0 - */ -U_CAPI const char * U_EXPORT2 -ures_getKey(const UResourceBundle *resourceBundle); - -/* ITERATION API - This API provides means for iterating through a resource -*/ - -/** - * Resets the internal context of a resource so that iteration starts from the first element. - * - * @param resourceBundle a resource - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -ures_resetIterator(UResourceBundle *resourceBundle); - -/** - * Checks whether the given resource has another element to iterate over. - * - * @param resourceBundle a resource - * @return true if there are more elements, false if there is no more elements - * @stable ICU 2.0 - */ -U_CAPI UBool U_EXPORT2 -ures_hasNext(const UResourceBundle *resourceBundle); - -/** - * Returns the next resource in a given resource or NULL if there are no more resources - * to iterate over. Features a fill-in parameter. - * - * @param resourceBundle a resource - * @param fillIn if NULL a new UResourceBundle struct is allocated and must be closed by the caller. - * Alternatively, you can supply a struct to be filled by this function. - * @param status fills in the outgoing error code. You may still get a non NULL result even if an - * error occurred. Check status instead. - * @return a pointer to a UResourceBundle struct. If fill in param was NULL, caller must close it - * @stable ICU 2.0 - */ -U_CAPI UResourceBundle* U_EXPORT2 -ures_getNextResource(UResourceBundle *resourceBundle, - UResourceBundle *fillIn, - UErrorCode *status); - -/** - * Returns the next string in a given resource or NULL if there are no more resources - * to iterate over. - * - * @param resourceBundle a resource - * @param len fill in length of the string - * @param key fill in for key associated with this string. NULL if no key - * @param status fills in the outgoing error code. If an error occurred, we may return NULL, but don't - * count on it. Check status instead! - * @return a pointer to a zero-terminated UChar array which lives in a memory mapped/DLL file. - * @stable ICU 2.0 - */ -U_CAPI const UChar* U_EXPORT2 -ures_getNextString(UResourceBundle *resourceBundle, - int32_t* len, - const char ** key, - UErrorCode *status); - -/** - * Returns the resource in a given resource at the specified index. Features a fill-in parameter. - * - * @param resourceBundle the resource bundle from which to get a sub-resource - * @param indexR an index to the wanted resource. - * @param fillIn if NULL a new UResourceBundle struct is allocated and must be closed by the caller. - * Alternatively, you can supply a struct to be filled by this function. - * @param status fills in the outgoing error code. Don't count on NULL being returned if an error has - * occurred. Check status instead. - * @return a pointer to a UResourceBundle struct. If fill in param was NULL, caller must close it - * @stable ICU 2.0 - */ -U_CAPI UResourceBundle* U_EXPORT2 -ures_getByIndex(const UResourceBundle *resourceBundle, - int32_t indexR, - UResourceBundle *fillIn, - UErrorCode *status); - -/** - * Returns the string in a given resource at the specified index. - * - * @param resourceBundle a resource - * @param indexS an index to the wanted string. - * @param len fill in length of the string - * @param status fills in the outgoing error code. If an error occurred, we may return NULL, but don't - * count on it. Check status instead! - * @return a pointer to a zero-terminated UChar array which lives in a memory mapped/DLL file. - * @stable ICU 2.0 - */ -U_CAPI const UChar* U_EXPORT2 -ures_getStringByIndex(const UResourceBundle *resourceBundle, - int32_t indexS, - int32_t* len, - UErrorCode *status); - -/** - * Returns a UTF-8 string from a resource at the specified index. - * The UTF-8 string may be returnable directly as a pointer, or - * it may need to be copied, or transformed from UTF-16 using u_strToUTF8() - * or equivalent. - * - * If forceCopy==true, then the string is always written to the dest buffer - * and dest is returned. - * - * If forceCopy==false, then the string is returned as a pointer if possible, - * without needing a dest buffer (it can be NULL). If the string needs to be - * copied or transformed, then it may be placed into dest at an arbitrary offset. - * - * If the string is to be written to dest, then U_BUFFER_OVERFLOW_ERROR and - * U_STRING_NOT_TERMINATED_WARNING are set if appropriate, as usual. - * - * If the string is transformed from UTF-16, then a conversion error may occur - * if an unpaired surrogate is encountered. If the function is successful, then - * the output UTF-8 string is always well-formed. - * - * @param resB Resource bundle. - * @param stringIndex An index to the wanted string. - * @param dest Destination buffer. Can be NULL only if capacity=*length==0. - * @param pLength Input: Capacity of destination buffer. - * Output: Actual length of the UTF-8 string, not counting the - * terminating NUL, even in case of U_BUFFER_OVERFLOW_ERROR. - * Can be NULL, meaning capacity=0 and the string length is not - * returned to the caller. - * @param forceCopy If true, then the output string will always be written to - * dest, with U_BUFFER_OVERFLOW_ERROR and - * U_STRING_NOT_TERMINATED_WARNING set if appropriate. - * If false, then the dest buffer may or may not contain a - * copy of the string. dest may or may not be modified. - * If a copy needs to be written, then the UErrorCode parameter - * indicates overflow etc. as usual. - * @param status Pointer to a standard 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.) - * @return The pointer to the UTF-8 string. It may be dest, or at some offset - * from dest (only if !forceCopy), or in unrelated memory. - * Always NUL-terminated unless the string was written to dest and - * length==capacity (in which case U_STRING_NOT_TERMINATED_WARNING is set). - * - * @see ures_getStringByIndex - * @see u_strToUTF8 - * @stable ICU 3.6 - */ -U_CAPI const char * U_EXPORT2 -ures_getUTF8StringByIndex(const UResourceBundle *resB, - int32_t stringIndex, - char *dest, int32_t *pLength, - UBool forceCopy, - UErrorCode *status); - -/** - * Returns a resource in a given resource that has a given key. This procedure works only with table - * resources. Features a fill-in parameter. - * - * @param resourceBundle a resource - * @param key a key associated with the wanted resource - * @param fillIn if NULL a new UResourceBundle struct is allocated and must be closed by the caller. - * Alternatively, you can supply a struct to be filled by this function. - * @param status fills in the outgoing error code. - * @return a pointer to a UResourceBundle struct. If fill in param was NULL, caller must close it - * @stable ICU 2.0 - */ -U_CAPI UResourceBundle* U_EXPORT2 -ures_getByKey(const UResourceBundle *resourceBundle, - const char* key, - UResourceBundle *fillIn, - UErrorCode *status); - -/** - * Returns a string in a given resource that has a given key. This procedure works only with table - * resources. - * - * @param resB a resource - * @param key a key associated with the wanted string - * @param len fill in length of the string - * @param status fills in the outgoing error code. If an error occurred, we may return NULL, but don't - * count on it. Check status instead! - * @return a pointer to a zero-terminated UChar array which lives in a memory mapped/DLL file. - * @stable ICU 2.0 - */ -U_CAPI const UChar* U_EXPORT2 -ures_getStringByKey(const UResourceBundle *resB, - const char* key, - int32_t* len, - UErrorCode *status); - -/** - * Returns a UTF-8 string from a resource and a key. - * This function works only with table resources. - * - * The UTF-8 string may be returnable directly as a pointer, or - * it may need to be copied, or transformed from UTF-16 using u_strToUTF8() - * or equivalent. - * - * If forceCopy==true, then the string is always written to the dest buffer - * and dest is returned. - * - * If forceCopy==false, then the string is returned as a pointer if possible, - * without needing a dest buffer (it can be NULL). If the string needs to be - * copied or transformed, then it may be placed into dest at an arbitrary offset. - * - * If the string is to be written to dest, then U_BUFFER_OVERFLOW_ERROR and - * U_STRING_NOT_TERMINATED_WARNING are set if appropriate, as usual. - * - * If the string is transformed from UTF-16, then a conversion error may occur - * if an unpaired surrogate is encountered. If the function is successful, then - * the output UTF-8 string is always well-formed. - * - * @param resB Resource bundle. - * @param key A key associated with the wanted resource - * @param dest Destination buffer. Can be NULL only if capacity=*length==0. - * @param pLength Input: Capacity of destination buffer. - * Output: Actual length of the UTF-8 string, not counting the - * terminating NUL, even in case of U_BUFFER_OVERFLOW_ERROR. - * Can be NULL, meaning capacity=0 and the string length is not - * returned to the caller. - * @param forceCopy If true, then the output string will always be written to - * dest, with U_BUFFER_OVERFLOW_ERROR and - * U_STRING_NOT_TERMINATED_WARNING set if appropriate. - * If false, then the dest buffer may or may not contain a - * copy of the string. dest may or may not be modified. - * If a copy needs to be written, then the UErrorCode parameter - * indicates overflow etc. as usual. - * @param status Pointer to a standard 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.) - * @return The pointer to the UTF-8 string. It may be dest, or at some offset - * from dest (only if !forceCopy), or in unrelated memory. - * Always NUL-terminated unless the string was written to dest and - * length==capacity (in which case U_STRING_NOT_TERMINATED_WARNING is set). - * - * @see ures_getStringByKey - * @see u_strToUTF8 - * @stable ICU 3.6 - */ -U_CAPI const char * U_EXPORT2 -ures_getUTF8StringByKey(const UResourceBundle *resB, - const char *key, - char *dest, int32_t *pLength, - UBool forceCopy, - UErrorCode *status); - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/unistr.h" - -U_NAMESPACE_BEGIN -/** - * Returns the string value from a string resource bundle. - * - * @param resB a resource, should have type URES_STRING - * @param status: fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * could be a non-failing error - * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING - * @return The string value, or a bogus string if there is a failure UErrorCode. - * @stable ICU 2.0 - */ -inline UnicodeString -ures_getUnicodeString(const UResourceBundle *resB, UErrorCode* status) { - UnicodeString result; - int32_t len = 0; - const UChar *r = ures_getString(resB, &len, status); - if(U_SUCCESS(*status)) { - result.setTo(true, r, len); - } else { - result.setToBogus(); - } - return result; -} - -/** - * Returns the next string in a resource, or an empty string if there are no more resources - * to iterate over. - * Use ures_getNextString() instead to distinguish between - * the end of the iteration and a real empty string value. - * - * @param resB a resource - * @param key fill in for key associated with this string - * @param status fills in the outgoing error code - * @return The string value, or a bogus string if there is a failure UErrorCode. - * @stable ICU 2.0 - */ -inline UnicodeString -ures_getNextUnicodeString(UResourceBundle *resB, const char ** key, UErrorCode* status) { - UnicodeString result; - int32_t len = 0; - const UChar* r = ures_getNextString(resB, &len, key, status); - if(U_SUCCESS(*status)) { - result.setTo(true, r, len); - } else { - result.setToBogus(); - } - return result; -} - -/** - * Returns the string in a given resource array or table at the specified index. - * - * @param resB a resource - * @param indexS an index to the wanted string. - * @param status fills in the outgoing error code - * @return The string value, or a bogus string if there is a failure UErrorCode. - * @stable ICU 2.0 - */ -inline UnicodeString -ures_getUnicodeStringByIndex(const UResourceBundle *resB, int32_t indexS, UErrorCode* status) { - UnicodeString result; - int32_t len = 0; - const UChar* r = ures_getStringByIndex(resB, indexS, &len, status); - if(U_SUCCESS(*status)) { - result.setTo(true, r, len); - } else { - result.setToBogus(); - } - return result; -} - -/** - * Returns a string in a resource that has a given key. - * This procedure works only with table resources. - * - * @param resB a resource - * @param key a key associated with the wanted string - * @param status fills in the outgoing error code - * @return The string value, or a bogus string if there is a failure UErrorCode. - * @stable ICU 2.0 - */ -inline UnicodeString -ures_getUnicodeStringByKey(const UResourceBundle *resB, const char* key, UErrorCode* status) { - UnicodeString result; - int32_t len = 0; - const UChar* r = ures_getStringByKey(resB, key, &len, status); - if(U_SUCCESS(*status)) { - result.setTo(true, r, len); - } else { - result.setToBogus(); - } - return result; -} - -U_NAMESPACE_END - -#endif - -/** - * Create a string enumerator, owned by the caller, of all locales located within - * the specified resource tree. - * @param packageName name of the tree, such as (NULL) or U_ICUDATA_ALIAS or or "ICUDATA-coll" - * This call is similar to uloc_getAvailable(). - * @param status error code - * @stable ICU 3.2 - */ -U_CAPI UEnumeration* U_EXPORT2 -ures_openAvailableLocales(const char *packageName, UErrorCode *status); - - -#endif /*_URES*/ -/*eof*/ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1997-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* File URES.H (formerly CRESBUND.H) +* +* Modification History: +* +* Date Name Description +* 04/01/97 aliu Creation. +* 02/22/99 damiba overhaul. +* 04/04/99 helena Fixed internal header inclusion. +* 04/15/99 Madhu Updated Javadoc +* 06/14/99 stephen Removed functions taking a filename suffix. +* 07/20/99 stephen Language-independent typedef to void* +* 11/09/99 weiv Added ures_getLocale() +* 06/24/02 weiv Added support for resource sharing +****************************************************************************** +*/ + +#ifndef URES_H +#define URES_H + +#include "unicode/utypes.h" +#include "unicode/uloc.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +/** + * \file + * \brief C API: Resource Bundle + * + *

C API: Resource Bundle

+ * + * C API representing a collection of resource information pertaining to a given + * locale. A resource bundle provides a way of accessing locale- specific information in + * a data file. You create a resource bundle that manages the resources for a given + * locale and then ask it for individual resources. + *

+ * Resource bundles in ICU4C are currently defined using text files which conform to the following + * BNF definition. + * More on resource bundle concepts and syntax can be found in the + * Users Guide. + *

+ */ + +/** + * UResourceBundle is an opaque type for handles for resource bundles in C APIs. + * @stable ICU 2.0 + */ +struct UResourceBundle; + +/** + * @stable ICU 2.0 + */ +typedef struct UResourceBundle UResourceBundle; + +/** + * Numeric constants for types of resource items. + * @see ures_getType + * @stable ICU 2.0 + */ +typedef enum { + /** Resource type constant for "no resource". @stable ICU 2.6 */ + URES_NONE=-1, + + /** Resource type constant for 16-bit Unicode strings. @stable ICU 2.6 */ + URES_STRING=0, + + /** Resource type constant for binary data. @stable ICU 2.6 */ + URES_BINARY=1, + + /** Resource type constant for tables of key-value pairs. @stable ICU 2.6 */ + URES_TABLE=2, + + /** + * Resource type constant for aliases; + * internally stores a string which identifies the actual resource + * storing the data (can be in a different resource bundle). + * Resolved internally before delivering the actual resource through the API. + * @stable ICU 2.6 + */ + URES_ALIAS=3, + + /** + * Resource type constant for a single 28-bit integer, interpreted as + * signed or unsigned by the ures_getInt() or ures_getUInt() function. + * @see ures_getInt + * @see ures_getUInt + * @stable ICU 2.6 + */ + URES_INT=7, + + /** Resource type constant for arrays of resources. @stable ICU 2.6 */ + URES_ARRAY=8, + + /** + * Resource type constant for vectors of 32-bit integers. + * @see ures_getIntVector + * @stable ICU 2.6 + */ + URES_INT_VECTOR = 14, +#ifndef U_HIDE_DEPRECATED_API + /** @deprecated ICU 2.6 Use the URES_ constant instead. */ + RES_NONE=URES_NONE, + /** @deprecated ICU 2.6 Use the URES_ constant instead. */ + RES_STRING=URES_STRING, + /** @deprecated ICU 2.6 Use the URES_ constant instead. */ + RES_BINARY=URES_BINARY, + /** @deprecated ICU 2.6 Use the URES_ constant instead. */ + RES_TABLE=URES_TABLE, + /** @deprecated ICU 2.6 Use the URES_ constant instead. */ + RES_ALIAS=URES_ALIAS, + /** @deprecated ICU 2.6 Use the URES_ constant instead. */ + RES_INT=URES_INT, + /** @deprecated ICU 2.6 Use the URES_ constant instead. */ + RES_ARRAY=URES_ARRAY, + /** @deprecated ICU 2.6 Use the URES_ constant instead. */ + RES_INT_VECTOR=URES_INT_VECTOR, + /** @deprecated ICU 2.6 Not used. */ + RES_RESERVED=15, + + /** + * One more than the highest normal UResType value. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + URES_LIMIT = 16 +#endif // U_HIDE_DEPRECATED_API +} UResType; + +/* + * Functions to create and destroy resource bundles. + */ + +/** + * Opens a UResourceBundle, from which users can extract strings by using + * their corresponding keys. + * Note that the caller is responsible of calling ures_close on each successfully + * opened resource bundle. + * @param packageName The packageName and locale together point to an ICU udata object, + * as defined by udata_open( packageName, "res", locale, err) + * or equivalent. Typically, packageName will refer to a (.dat) file, or to + * a package registered with udata_setAppData(). Using a full file or directory + * pathname for packageName is deprecated. If NULL, ICU data will be used. + * @param locale specifies the locale for which we want to open the resource + * if NULL, the default locale will be used. If strlen(locale) == 0 + * root locale will be used. + * + * @param status fills in the outgoing error code. + * The UErrorCode err parameter is used to return status information to the user. To + * check whether the construction succeeded or not, you should check the value of + * U_SUCCESS(err). If you wish more detailed information, you can check for + * informational status results which still indicate success. U_USING_FALLBACK_WARNING + * indicates that a fall back locale was used. For example, 'de_CH' was requested, + * but nothing was found there, so 'de' was used. U_USING_DEFAULT_WARNING indicates that + * the default locale data or root locale data was used; neither the requested locale + * nor any of its fall back locales could be found. Please see the users guide for more + * information on this topic. + * @return a newly allocated resource bundle. + * @see ures_close + * @stable ICU 2.0 + */ +U_CAPI UResourceBundle* U_EXPORT2 +ures_open(const char* packageName, + const char* locale, + UErrorCode* status); + + +/** This function does not care what kind of localeID is passed in. It simply opens a bundle with + * that name. Fallback mechanism is disabled for the new bundle. If the requested bundle contains + * an %%ALIAS directive, the results are undefined. + * @param packageName The packageName and locale together point to an ICU udata object, + * as defined by udata_open( packageName, "res", locale, err) + * or equivalent. Typically, packageName will refer to a (.dat) file, or to + * a package registered with udata_setAppData(). Using a full file or directory + * pathname for packageName is deprecated. If NULL, ICU data will be used. + * @param locale specifies the locale for which we want to open the resource + * if NULL, the default locale will be used. If strlen(locale) == 0 + * root locale will be used. + * + * @param status fills in the outgoing error code. Either U_ZERO_ERROR or U_MISSING_RESOURCE_ERROR + * @return a newly allocated resource bundle or NULL if it doesn't exist. + * @see ures_close + * @stable ICU 2.0 + */ +U_CAPI UResourceBundle* U_EXPORT2 +ures_openDirect(const char* packageName, + const char* locale, + UErrorCode* status); + +/** + * Same as ures_open() but takes a const UChar *path. + * This path will be converted to char * using the default converter, + * then ures_open() is called. + * + * @param packageName The packageName and locale together point to an ICU udata object, + * as defined by udata_open( packageName, "res", locale, err) + * or equivalent. Typically, packageName will refer to a (.dat) file, or to + * a package registered with udata_setAppData(). Using a full file or directory + * pathname for packageName is deprecated. If NULL, ICU data will be used. + * @param locale specifies the locale for which we want to open the resource + * if NULL, the default locale will be used. If strlen(locale) == 0 + * root locale will be used. + * @param status fills in the outgoing error code. + * @return a newly allocated resource bundle. + * @see ures_open + * @stable ICU 2.0 + */ +U_CAPI UResourceBundle* U_EXPORT2 +ures_openU(const UChar* packageName, + const char* locale, + UErrorCode* status); + +#ifndef U_HIDE_DEPRECATED_API +/** + * Returns the number of strings/arrays in resource bundles. + * Better to use ures_getSize, as this function will be deprecated. + * + *@param resourceBundle resource bundle containing the desired strings + *@param resourceKey key tagging the resource + *@param err fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * could be a non-failing error + * e.g.: U_USING_FALLBACK_WARNING,U_USING_FALLBACK_WARNING + *@return: for Arrays: returns the number of resources in the array + * Tables: returns the number of resources in the table + * single string: returns 1 + *@see ures_getSize + * @deprecated ICU 2.8 User ures_getSize instead + */ +U_DEPRECATED int32_t U_EXPORT2 +ures_countArrayItems(const UResourceBundle* resourceBundle, + const char* resourceKey, + UErrorCode* err); +#endif /* U_HIDE_DEPRECATED_API */ + +/** + * Close a resource bundle, all pointers returned from the various ures_getXXX calls + * on this particular bundle should be considered invalid henceforth. + * + * @param resourceBundle a pointer to a resourceBundle struct. Can be NULL. + * @see ures_open + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ures_close(UResourceBundle* resourceBundle); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUResourceBundlePointer + * "Smart pointer" class, closes a UResourceBundle via ures_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUResourceBundlePointer, UResourceBundle, ures_close); + +U_NAMESPACE_END + +#endif + +#ifndef U_HIDE_DEPRECATED_API +/** + * Return the version number associated with this ResourceBundle as a string. Please + * use ures_getVersion as this function is going to be deprecated. + * + * @param resourceBundle The resource bundle for which the version is checked. + * @return A version number string as specified in the resource bundle or its parent. + * The caller does not own this string. + * @see ures_getVersion + * @deprecated ICU 2.8 Use ures_getVersion instead. + */ +U_DEPRECATED const char* U_EXPORT2 +ures_getVersionNumber(const UResourceBundle* resourceBundle); +#endif /* U_HIDE_DEPRECATED_API */ + +/** + * Return the version number associated with this ResourceBundle as an + * UVersionInfo array. + * + * @param resB The resource bundle for which the version is checked. + * @param versionInfo A UVersionInfo array that is filled with the version number + * as specified in the resource bundle or its parent. + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ures_getVersion(const UResourceBundle* resB, + UVersionInfo versionInfo); + +#ifndef U_HIDE_DEPRECATED_API +/** + * Return the name of the Locale associated with this ResourceBundle. This API allows + * you to query for the real locale of the resource. For example, if you requested + * "en_US_CALIFORNIA" and only "en_US" bundle exists, "en_US" will be returned. + * For subresources, the locale where this resource comes from will be returned. + * If fallback has occurred, getLocale will reflect this. + * + * @param resourceBundle resource bundle in question + * @param status just for catching illegal arguments + * @return A Locale name + * @deprecated ICU 2.8 Use ures_getLocaleByType instead. + */ +U_DEPRECATED const char* U_EXPORT2 +ures_getLocale(const UResourceBundle* resourceBundle, + UErrorCode* status); +#endif /* U_HIDE_DEPRECATED_API */ + +/** + * Return the name of the Locale associated with this ResourceBundle. + * You can choose between requested, valid and real locale. + * + * @param resourceBundle resource bundle in question + * @param type You can choose between requested, valid and actual + * locale. For description see the definition of + * ULocDataLocaleType in uloc.h + * @param status just for catching illegal arguments + * @return A Locale name + * @stable ICU 2.8 + */ +U_CAPI const char* U_EXPORT2 +ures_getLocaleByType(const UResourceBundle* resourceBundle, + ULocDataLocaleType type, + UErrorCode* status); + + +#ifndef U_HIDE_INTERNAL_API +/** + * Same as ures_open() but uses the fill-in parameter instead of allocating a new bundle. + * + * TODO need to revisit usefulness of this function + * and usage model for fillIn parameters without knowing sizeof(UResourceBundle) + * @param r The existing UResourceBundle to fill in. If NULL then status will be + * set to U_ILLEGAL_ARGUMENT_ERROR. + * @param packageName The packageName and locale together point to an ICU udata object, + * as defined by udata_open( packageName, "res", locale, err) + * or equivalent. Typically, packageName will refer to a (.dat) file, or to + * a package registered with udata_setAppData(). Using a full file or directory + * pathname for packageName is deprecated. If NULL, ICU data will be used. + * @param localeID specifies the locale for which we want to open the resource + * @param status The error code. + * @internal + */ +U_CAPI void U_EXPORT2 +ures_openFillIn(UResourceBundle *r, + const char* packageName, + const char* localeID, + UErrorCode* status); +#endif /* U_HIDE_INTERNAL_API */ + +/** + * Returns a string from a string resource type + * + * @param resourceBundle a string resource + * @param len fills in the length of resulting string + * @param status fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * Always check the value of status. Don't count on returning NULL. + * could be a non-failing error + * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING + * @return a pointer to a zero-terminated UChar array which lives in a memory mapped/DLL file. + * @see ures_getBinary + * @see ures_getIntVector + * @see ures_getInt + * @see ures_getUInt + * @stable ICU 2.0 + */ +U_CAPI const UChar* U_EXPORT2 +ures_getString(const UResourceBundle* resourceBundle, + int32_t* len, + UErrorCode* status); + +/** + * Returns a UTF-8 string from a string resource. + * The UTF-8 string may be returnable directly as a pointer, or + * it may need to be copied, or transformed from UTF-16 using u_strToUTF8() + * or equivalent. + * + * If forceCopy==true, then the string is always written to the dest buffer + * and dest is returned. + * + * If forceCopy==false, then the string is returned as a pointer if possible, + * without needing a dest buffer (it can be NULL). If the string needs to be + * copied or transformed, then it may be placed into dest at an arbitrary offset. + * + * If the string is to be written to dest, then U_BUFFER_OVERFLOW_ERROR and + * U_STRING_NOT_TERMINATED_WARNING are set if appropriate, as usual. + * + * If the string is transformed from UTF-16, then a conversion error may occur + * if an unpaired surrogate is encountered. If the function is successful, then + * the output UTF-8 string is always well-formed. + * + * @param resB Resource bundle. + * @param dest Destination buffer. Can be NULL only if capacity=*length==0. + * @param length Input: Capacity of destination buffer. + * Output: Actual length of the UTF-8 string, not counting the + * terminating NUL, even in case of U_BUFFER_OVERFLOW_ERROR. + * Can be NULL, meaning capacity=0 and the string length is not + * returned to the caller. + * @param forceCopy If true, then the output string will always be written to + * dest, with U_BUFFER_OVERFLOW_ERROR and + * U_STRING_NOT_TERMINATED_WARNING set if appropriate. + * If false, then the dest buffer may or may not contain a + * copy of the string. dest may or may not be modified. + * If a copy needs to be written, then the UErrorCode parameter + * indicates overflow etc. as usual. + * @param status Pointer to a standard 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.) + * @return The pointer to the UTF-8 string. It may be dest, or at some offset + * from dest (only if !forceCopy), or in unrelated memory. + * Always NUL-terminated unless the string was written to dest and + * length==capacity (in which case U_STRING_NOT_TERMINATED_WARNING is set). + * + * @see ures_getString + * @see u_strToUTF8 + * @stable ICU 3.6 + */ +U_CAPI const char * U_EXPORT2 +ures_getUTF8String(const UResourceBundle *resB, + char *dest, int32_t *length, + UBool forceCopy, + UErrorCode *status); + +/** + * Returns a binary data from a binary resource. + * + * @param resourceBundle a string resource + * @param len fills in the length of resulting byte chunk + * @param status fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * Always check the value of status. Don't count on returning NULL. + * could be a non-failing error + * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING + * @return a pointer to a chunk of unsigned bytes which live in a memory mapped/DLL file. + * @see ures_getString + * @see ures_getIntVector + * @see ures_getInt + * @see ures_getUInt + * @stable ICU 2.0 + */ +U_CAPI const uint8_t* U_EXPORT2 +ures_getBinary(const UResourceBundle* resourceBundle, + int32_t* len, + UErrorCode* status); + +/** + * Returns a 32 bit integer array from a resource. + * + * @param resourceBundle an int vector resource + * @param len fills in the length of resulting byte chunk + * @param status fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * Always check the value of status. Don't count on returning NULL. + * could be a non-failing error + * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING + * @return a pointer to a chunk of integers which live in a memory mapped/DLL file. + * @see ures_getBinary + * @see ures_getString + * @see ures_getInt + * @see ures_getUInt + * @stable ICU 2.0 + */ +U_CAPI const int32_t* U_EXPORT2 +ures_getIntVector(const UResourceBundle* resourceBundle, + int32_t* len, + UErrorCode* status); + +/** + * Returns an unsigned integer from a resource. + * This integer is originally 28 bits. + * + * @param resourceBundle a string resource + * @param status fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * could be a non-failing error + * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING + * @return an integer value + * @see ures_getInt + * @see ures_getIntVector + * @see ures_getBinary + * @see ures_getString + * @stable ICU 2.0 + */ +U_CAPI uint32_t U_EXPORT2 +ures_getUInt(const UResourceBundle* resourceBundle, + UErrorCode *status); + +/** + * Returns a signed integer from a resource. + * This integer is originally 28 bit and the sign gets propagated. + * + * @param resourceBundle a string resource + * @param status fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * could be a non-failing error + * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING + * @return an integer value + * @see ures_getUInt + * @see ures_getIntVector + * @see ures_getBinary + * @see ures_getString + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ures_getInt(const UResourceBundle* resourceBundle, + UErrorCode *status); + +/** + * Returns the size of a resource. Size for scalar types is always 1, + * and for vector/table types is the number of child resources. + * @warning Integer array is treated as a scalar type. There are no + * APIs to access individual members of an integer array. It + * is always returned as a whole. + * @param resourceBundle a resource + * @return number of resources in a given resource. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +ures_getSize(const UResourceBundle *resourceBundle); + +/** + * Returns the type of a resource. Available types are defined in enum UResType + * + * @param resourceBundle a resource + * @return type of the given resource. + * @see UResType + * @stable ICU 2.0 + */ +U_CAPI UResType U_EXPORT2 +ures_getType(const UResourceBundle *resourceBundle); + +/** + * Returns the key associated with a given resource. Not all the resources have a key - only + * those that are members of a table. + * + * @param resourceBundle a resource + * @return a key associated to this resource, or NULL if it doesn't have a key + * @stable ICU 2.0 + */ +U_CAPI const char * U_EXPORT2 +ures_getKey(const UResourceBundle *resourceBundle); + +/* ITERATION API + This API provides means for iterating through a resource +*/ + +/** + * Resets the internal context of a resource so that iteration starts from the first element. + * + * @param resourceBundle a resource + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +ures_resetIterator(UResourceBundle *resourceBundle); + +/** + * Checks whether the given resource has another element to iterate over. + * + * @param resourceBundle a resource + * @return true if there are more elements, false if there is no more elements + * @stable ICU 2.0 + */ +U_CAPI UBool U_EXPORT2 +ures_hasNext(const UResourceBundle *resourceBundle); + +/** + * Returns the next resource in a given resource or NULL if there are no more resources + * to iterate over. Features a fill-in parameter. + * + * @param resourceBundle a resource + * @param fillIn if NULL a new UResourceBundle struct is allocated and must be closed by the caller. + * Alternatively, you can supply a struct to be filled by this function. + * @param status fills in the outgoing error code. You may still get a non NULL result even if an + * error occurred. Check status instead. + * @return a pointer to a UResourceBundle struct. If fill in param was NULL, caller must close it + * @stable ICU 2.0 + */ +U_CAPI UResourceBundle* U_EXPORT2 +ures_getNextResource(UResourceBundle *resourceBundle, + UResourceBundle *fillIn, + UErrorCode *status); + +/** + * Returns the next string in a given resource or NULL if there are no more resources + * to iterate over. + * + * @param resourceBundle a resource + * @param len fill in length of the string + * @param key fill in for key associated with this string. NULL if no key + * @param status fills in the outgoing error code. If an error occurred, we may return NULL, but don't + * count on it. Check status instead! + * @return a pointer to a zero-terminated UChar array which lives in a memory mapped/DLL file. + * @stable ICU 2.0 + */ +U_CAPI const UChar* U_EXPORT2 +ures_getNextString(UResourceBundle *resourceBundle, + int32_t* len, + const char ** key, + UErrorCode *status); + +/** + * Returns the resource in a given resource at the specified index. Features a fill-in parameter. + * + * @param resourceBundle the resource bundle from which to get a sub-resource + * @param indexR an index to the wanted resource. + * @param fillIn if NULL a new UResourceBundle struct is allocated and must be closed by the caller. + * Alternatively, you can supply a struct to be filled by this function. + * @param status fills in the outgoing error code. Don't count on NULL being returned if an error has + * occurred. Check status instead. + * @return a pointer to a UResourceBundle struct. If fill in param was NULL, caller must close it + * @stable ICU 2.0 + */ +U_CAPI UResourceBundle* U_EXPORT2 +ures_getByIndex(const UResourceBundle *resourceBundle, + int32_t indexR, + UResourceBundle *fillIn, + UErrorCode *status); + +/** + * Returns the string in a given resource at the specified index. + * + * @param resourceBundle a resource + * @param indexS an index to the wanted string. + * @param len fill in length of the string + * @param status fills in the outgoing error code. If an error occurred, we may return NULL, but don't + * count on it. Check status instead! + * @return a pointer to a zero-terminated UChar array which lives in a memory mapped/DLL file. + * @stable ICU 2.0 + */ +U_CAPI const UChar* U_EXPORT2 +ures_getStringByIndex(const UResourceBundle *resourceBundle, + int32_t indexS, + int32_t* len, + UErrorCode *status); + +/** + * Returns a UTF-8 string from a resource at the specified index. + * The UTF-8 string may be returnable directly as a pointer, or + * it may need to be copied, or transformed from UTF-16 using u_strToUTF8() + * or equivalent. + * + * If forceCopy==true, then the string is always written to the dest buffer + * and dest is returned. + * + * If forceCopy==false, then the string is returned as a pointer if possible, + * without needing a dest buffer (it can be NULL). If the string needs to be + * copied or transformed, then it may be placed into dest at an arbitrary offset. + * + * If the string is to be written to dest, then U_BUFFER_OVERFLOW_ERROR and + * U_STRING_NOT_TERMINATED_WARNING are set if appropriate, as usual. + * + * If the string is transformed from UTF-16, then a conversion error may occur + * if an unpaired surrogate is encountered. If the function is successful, then + * the output UTF-8 string is always well-formed. + * + * @param resB Resource bundle. + * @param stringIndex An index to the wanted string. + * @param dest Destination buffer. Can be NULL only if capacity=*length==0. + * @param pLength Input: Capacity of destination buffer. + * Output: Actual length of the UTF-8 string, not counting the + * terminating NUL, even in case of U_BUFFER_OVERFLOW_ERROR. + * Can be NULL, meaning capacity=0 and the string length is not + * returned to the caller. + * @param forceCopy If true, then the output string will always be written to + * dest, with U_BUFFER_OVERFLOW_ERROR and + * U_STRING_NOT_TERMINATED_WARNING set if appropriate. + * If false, then the dest buffer may or may not contain a + * copy of the string. dest may or may not be modified. + * If a copy needs to be written, then the UErrorCode parameter + * indicates overflow etc. as usual. + * @param status Pointer to a standard 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.) + * @return The pointer to the UTF-8 string. It may be dest, or at some offset + * from dest (only if !forceCopy), or in unrelated memory. + * Always NUL-terminated unless the string was written to dest and + * length==capacity (in which case U_STRING_NOT_TERMINATED_WARNING is set). + * + * @see ures_getStringByIndex + * @see u_strToUTF8 + * @stable ICU 3.6 + */ +U_CAPI const char * U_EXPORT2 +ures_getUTF8StringByIndex(const UResourceBundle *resB, + int32_t stringIndex, + char *dest, int32_t *pLength, + UBool forceCopy, + UErrorCode *status); + +/** + * Returns a resource in a given resource that has a given key. This procedure works only with table + * resources. Features a fill-in parameter. + * + * @param resourceBundle a resource + * @param key a key associated with the wanted resource + * @param fillIn if NULL a new UResourceBundle struct is allocated and must be closed by the caller. + * Alternatively, you can supply a struct to be filled by this function. + * @param status fills in the outgoing error code. + * @return a pointer to a UResourceBundle struct. If fill in param was NULL, caller must close it + * @stable ICU 2.0 + */ +U_CAPI UResourceBundle* U_EXPORT2 +ures_getByKey(const UResourceBundle *resourceBundle, + const char* key, + UResourceBundle *fillIn, + UErrorCode *status); + +/** + * Returns a string in a given resource that has a given key. This procedure works only with table + * resources. + * + * @param resB a resource + * @param key a key associated with the wanted string + * @param len fill in length of the string + * @param status fills in the outgoing error code. If an error occurred, we may return NULL, but don't + * count on it. Check status instead! + * @return a pointer to a zero-terminated UChar array which lives in a memory mapped/DLL file. + * @stable ICU 2.0 + */ +U_CAPI const UChar* U_EXPORT2 +ures_getStringByKey(const UResourceBundle *resB, + const char* key, + int32_t* len, + UErrorCode *status); + +/** + * Returns a UTF-8 string from a resource and a key. + * This function works only with table resources. + * + * The UTF-8 string may be returnable directly as a pointer, or + * it may need to be copied, or transformed from UTF-16 using u_strToUTF8() + * or equivalent. + * + * If forceCopy==true, then the string is always written to the dest buffer + * and dest is returned. + * + * If forceCopy==false, then the string is returned as a pointer if possible, + * without needing a dest buffer (it can be NULL). If the string needs to be + * copied or transformed, then it may be placed into dest at an arbitrary offset. + * + * If the string is to be written to dest, then U_BUFFER_OVERFLOW_ERROR and + * U_STRING_NOT_TERMINATED_WARNING are set if appropriate, as usual. + * + * If the string is transformed from UTF-16, then a conversion error may occur + * if an unpaired surrogate is encountered. If the function is successful, then + * the output UTF-8 string is always well-formed. + * + * @param resB Resource bundle. + * @param key A key associated with the wanted resource + * @param dest Destination buffer. Can be NULL only if capacity=*length==0. + * @param pLength Input: Capacity of destination buffer. + * Output: Actual length of the UTF-8 string, not counting the + * terminating NUL, even in case of U_BUFFER_OVERFLOW_ERROR. + * Can be NULL, meaning capacity=0 and the string length is not + * returned to the caller. + * @param forceCopy If true, then the output string will always be written to + * dest, with U_BUFFER_OVERFLOW_ERROR and + * U_STRING_NOT_TERMINATED_WARNING set if appropriate. + * If false, then the dest buffer may or may not contain a + * copy of the string. dest may or may not be modified. + * If a copy needs to be written, then the UErrorCode parameter + * indicates overflow etc. as usual. + * @param status Pointer to a standard 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.) + * @return The pointer to the UTF-8 string. It may be dest, or at some offset + * from dest (only if !forceCopy), or in unrelated memory. + * Always NUL-terminated unless the string was written to dest and + * length==capacity (in which case U_STRING_NOT_TERMINATED_WARNING is set). + * + * @see ures_getStringByKey + * @see u_strToUTF8 + * @stable ICU 3.6 + */ +U_CAPI const char * U_EXPORT2 +ures_getUTF8StringByKey(const UResourceBundle *resB, + const char *key, + char *dest, int32_t *pLength, + UBool forceCopy, + UErrorCode *status); + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/unistr.h" + +U_NAMESPACE_BEGIN +/** + * Returns the string value from a string resource bundle. + * + * @param resB a resource, should have type URES_STRING + * @param status: fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * could be a non-failing error + * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING + * @return The string value, or a bogus string if there is a failure UErrorCode. + * @stable ICU 2.0 + */ +inline UnicodeString +ures_getUnicodeString(const UResourceBundle *resB, UErrorCode* status) { + UnicodeString result; + int32_t len = 0; + const char16_t *r = ures_getString(resB, &len, status); + if(U_SUCCESS(*status)) { + result.setTo(true, r, len); + } else { + result.setToBogus(); + } + return result; +} + +/** + * Returns the next string in a resource, or an empty string if there are no more resources + * to iterate over. + * Use ures_getNextString() instead to distinguish between + * the end of the iteration and a real empty string value. + * + * @param resB a resource + * @param key fill in for key associated with this string + * @param status fills in the outgoing error code + * @return The string value, or a bogus string if there is a failure UErrorCode. + * @stable ICU 2.0 + */ +inline UnicodeString +ures_getNextUnicodeString(UResourceBundle *resB, const char ** key, UErrorCode* status) { + UnicodeString result; + int32_t len = 0; + const char16_t* r = ures_getNextString(resB, &len, key, status); + if(U_SUCCESS(*status)) { + result.setTo(true, r, len); + } else { + result.setToBogus(); + } + return result; +} + +/** + * Returns the string in a given resource array or table at the specified index. + * + * @param resB a resource + * @param indexS an index to the wanted string. + * @param status fills in the outgoing error code + * @return The string value, or a bogus string if there is a failure UErrorCode. + * @stable ICU 2.0 + */ +inline UnicodeString +ures_getUnicodeStringByIndex(const UResourceBundle *resB, int32_t indexS, UErrorCode* status) { + UnicodeString result; + int32_t len = 0; + const char16_t* r = ures_getStringByIndex(resB, indexS, &len, status); + if(U_SUCCESS(*status)) { + result.setTo(true, r, len); + } else { + result.setToBogus(); + } + return result; +} + +/** + * Returns a string in a resource that has a given key. + * This procedure works only with table resources. + * + * @param resB a resource + * @param key a key associated with the wanted string + * @param status fills in the outgoing error code + * @return The string value, or a bogus string if there is a failure UErrorCode. + * @stable ICU 2.0 + */ +inline UnicodeString +ures_getUnicodeStringByKey(const UResourceBundle *resB, const char* key, UErrorCode* status) { + UnicodeString result; + int32_t len = 0; + const char16_t* r = ures_getStringByKey(resB, key, &len, status); + if(U_SUCCESS(*status)) { + result.setTo(true, r, len); + } else { + result.setToBogus(); + } + return result; +} + +U_NAMESPACE_END + +#endif + +/** + * Create a string enumerator, owned by the caller, of all locales located within + * the specified resource tree. + * @param packageName name of the tree, such as (NULL) or U_ICUDATA_ALIAS or or "ICUDATA-coll" + * This call is similar to uloc_getAvailable(). + * @param status error code + * @stable ICU 3.2 + */ +U_CAPI UEnumeration* U_EXPORT2 +ures_openAvailableLocales(const char *packageName, UErrorCode *status); + + +#endif /*_URES*/ +/*eof*/ diff --git a/deps/icu-small/source/common/unicode/uscript.h b/deps/icu-small/source/common/unicode/uscript.h index dc97ab2ba56c17..1c2bae38f7766e 100644 --- a/deps/icu-small/source/common/unicode/uscript.h +++ b/deps/icu-small/source/common/unicode/uscript.h @@ -1,724 +1,724 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 1997-2016, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - * - * File USCRIPT.H - * - * Modification History: - * - * Date Name Description - * 07/06/2001 Ram Creation. - ****************************************************************************** - */ - -#ifndef USCRIPT_H -#define USCRIPT_H -#include "unicode/utypes.h" - -/** - * \file - * \brief C API: Unicode Script Information - */ - -/** - * Constants for ISO 15924 script codes. - * - * The current set of script code constants supports at least all scripts - * that are encoded in the version of Unicode which ICU currently supports. - * The names of the constants are usually derived from the - * Unicode script property value aliases. - * See UAX #24 Unicode Script Property (http://www.unicode.org/reports/tr24/) - * and http://www.unicode.org/Public/UCD/latest/ucd/PropertyValueAliases.txt . - * - * In addition, constants for many ISO 15924 script codes - * are included, for use with language tags, CLDR data, and similar. - * Some of those codes are not used in the Unicode Character Database (UCD). - * For example, there are no characters that have a UCD script property value of - * Hans or Hant. All Han ideographs have the Hani script property value in Unicode. - * - * Private-use codes Qaaa..Qabx are not included, except as used in the UCD or in CLDR. - * - * Starting with ICU 55, script codes are only added when their scripts - * have been or will certainly be encoded in Unicode, - * and have been assigned Unicode script property value aliases, - * to ensure that their script names are stable and match the names of the constants. - * Script codes like Latf and Aran that are not subject to separate encoding - * may be added at any time. - * - * @stable ICU 2.2 - */ -typedef enum UScriptCode { - /* - * Note: UScriptCode constants and their ISO script code comments - * are parsed by preparseucd.py. - * It matches lines like - * USCRIPT_ = , / * * / - */ - - /** @stable ICU 2.2 */ - USCRIPT_INVALID_CODE = -1, - /** @stable ICU 2.2 */ - USCRIPT_COMMON = 0, /* Zyyy */ - /** @stable ICU 2.2 */ - USCRIPT_INHERITED = 1, /* Zinh */ /* "Code for inherited script", for non-spacing combining marks; also Qaai */ - /** @stable ICU 2.2 */ - USCRIPT_ARABIC = 2, /* Arab */ - /** @stable ICU 2.2 */ - USCRIPT_ARMENIAN = 3, /* Armn */ - /** @stable ICU 2.2 */ - USCRIPT_BENGALI = 4, /* Beng */ - /** @stable ICU 2.2 */ - USCRIPT_BOPOMOFO = 5, /* Bopo */ - /** @stable ICU 2.2 */ - USCRIPT_CHEROKEE = 6, /* Cher */ - /** @stable ICU 2.2 */ - USCRIPT_COPTIC = 7, /* Copt */ - /** @stable ICU 2.2 */ - USCRIPT_CYRILLIC = 8, /* Cyrl */ - /** @stable ICU 2.2 */ - USCRIPT_DESERET = 9, /* Dsrt */ - /** @stable ICU 2.2 */ - USCRIPT_DEVANAGARI = 10, /* Deva */ - /** @stable ICU 2.2 */ - USCRIPT_ETHIOPIC = 11, /* Ethi */ - /** @stable ICU 2.2 */ - USCRIPT_GEORGIAN = 12, /* Geor */ - /** @stable ICU 2.2 */ - USCRIPT_GOTHIC = 13, /* Goth */ - /** @stable ICU 2.2 */ - USCRIPT_GREEK = 14, /* Grek */ - /** @stable ICU 2.2 */ - USCRIPT_GUJARATI = 15, /* Gujr */ - /** @stable ICU 2.2 */ - USCRIPT_GURMUKHI = 16, /* Guru */ - /** @stable ICU 2.2 */ - USCRIPT_HAN = 17, /* Hani */ - /** @stable ICU 2.2 */ - USCRIPT_HANGUL = 18, /* Hang */ - /** @stable ICU 2.2 */ - USCRIPT_HEBREW = 19, /* Hebr */ - /** @stable ICU 2.2 */ - USCRIPT_HIRAGANA = 20, /* Hira */ - /** @stable ICU 2.2 */ - USCRIPT_KANNADA = 21, /* Knda */ - /** @stable ICU 2.2 */ - USCRIPT_KATAKANA = 22, /* Kana */ - /** @stable ICU 2.2 */ - USCRIPT_KHMER = 23, /* Khmr */ - /** @stable ICU 2.2 */ - USCRIPT_LAO = 24, /* Laoo */ - /** @stable ICU 2.2 */ - USCRIPT_LATIN = 25, /* Latn */ - /** @stable ICU 2.2 */ - USCRIPT_MALAYALAM = 26, /* Mlym */ - /** @stable ICU 2.2 */ - USCRIPT_MONGOLIAN = 27, /* Mong */ - /** @stable ICU 2.2 */ - USCRIPT_MYANMAR = 28, /* Mymr */ - /** @stable ICU 2.2 */ - USCRIPT_OGHAM = 29, /* Ogam */ - /** @stable ICU 2.2 */ - USCRIPT_OLD_ITALIC = 30, /* Ital */ - /** @stable ICU 2.2 */ - USCRIPT_ORIYA = 31, /* Orya */ - /** @stable ICU 2.2 */ - USCRIPT_RUNIC = 32, /* Runr */ - /** @stable ICU 2.2 */ - USCRIPT_SINHALA = 33, /* Sinh */ - /** @stable ICU 2.2 */ - USCRIPT_SYRIAC = 34, /* Syrc */ - /** @stable ICU 2.2 */ - USCRIPT_TAMIL = 35, /* Taml */ - /** @stable ICU 2.2 */ - USCRIPT_TELUGU = 36, /* Telu */ - /** @stable ICU 2.2 */ - USCRIPT_THAANA = 37, /* Thaa */ - /** @stable ICU 2.2 */ - USCRIPT_THAI = 38, /* Thai */ - /** @stable ICU 2.2 */ - USCRIPT_TIBETAN = 39, /* Tibt */ - /** Canadian_Aboriginal script. @stable ICU 2.6 */ - USCRIPT_CANADIAN_ABORIGINAL = 40, /* Cans */ - /** Canadian_Aboriginal script (alias). @stable ICU 2.2 */ - USCRIPT_UCAS = USCRIPT_CANADIAN_ABORIGINAL, - /** @stable ICU 2.2 */ - USCRIPT_YI = 41, /* Yiii */ - /* New scripts in Unicode 3.2 */ - /** @stable ICU 2.2 */ - USCRIPT_TAGALOG = 42, /* Tglg */ - /** @stable ICU 2.2 */ - USCRIPT_HANUNOO = 43, /* Hano */ - /** @stable ICU 2.2 */ - USCRIPT_BUHID = 44, /* Buhd */ - /** @stable ICU 2.2 */ - USCRIPT_TAGBANWA = 45, /* Tagb */ - - /* New scripts in Unicode 4 */ - /** @stable ICU 2.6 */ - USCRIPT_BRAILLE = 46, /* Brai */ - /** @stable ICU 2.6 */ - USCRIPT_CYPRIOT = 47, /* Cprt */ - /** @stable ICU 2.6 */ - USCRIPT_LIMBU = 48, /* Limb */ - /** @stable ICU 2.6 */ - USCRIPT_LINEAR_B = 49, /* Linb */ - /** @stable ICU 2.6 */ - USCRIPT_OSMANYA = 50, /* Osma */ - /** @stable ICU 2.6 */ - USCRIPT_SHAVIAN = 51, /* Shaw */ - /** @stable ICU 2.6 */ - USCRIPT_TAI_LE = 52, /* Tale */ - /** @stable ICU 2.6 */ - USCRIPT_UGARITIC = 53, /* Ugar */ - - /** New script code in Unicode 4.0.1 @stable ICU 3.0 */ - USCRIPT_KATAKANA_OR_HIRAGANA = 54,/*Hrkt */ - - /* New scripts in Unicode 4.1 */ - /** @stable ICU 3.4 */ - USCRIPT_BUGINESE = 55, /* Bugi */ - /** @stable ICU 3.4 */ - USCRIPT_GLAGOLITIC = 56, /* Glag */ - /** @stable ICU 3.4 */ - USCRIPT_KHAROSHTHI = 57, /* Khar */ - /** @stable ICU 3.4 */ - USCRIPT_SYLOTI_NAGRI = 58, /* Sylo */ - /** @stable ICU 3.4 */ - USCRIPT_NEW_TAI_LUE = 59, /* Talu */ - /** @stable ICU 3.4 */ - USCRIPT_TIFINAGH = 60, /* Tfng */ - /** @stable ICU 3.4 */ - USCRIPT_OLD_PERSIAN = 61, /* Xpeo */ - - /* New script codes from Unicode and ISO 15924 */ - /** @stable ICU 3.6 */ - USCRIPT_BALINESE = 62, /* Bali */ - /** @stable ICU 3.6 */ - USCRIPT_BATAK = 63, /* Batk */ - /** @stable ICU 3.6 */ - USCRIPT_BLISSYMBOLS = 64, /* Blis */ - /** @stable ICU 3.6 */ - USCRIPT_BRAHMI = 65, /* Brah */ - /** @stable ICU 3.6 */ - USCRIPT_CHAM = 66, /* Cham */ - /** @stable ICU 3.6 */ - USCRIPT_CIRTH = 67, /* Cirt */ - /** @stable ICU 3.6 */ - USCRIPT_OLD_CHURCH_SLAVONIC_CYRILLIC = 68, /* Cyrs */ - /** @stable ICU 3.6 */ - USCRIPT_DEMOTIC_EGYPTIAN = 69, /* Egyd */ - /** @stable ICU 3.6 */ - USCRIPT_HIERATIC_EGYPTIAN = 70, /* Egyh */ - /** @stable ICU 3.6 */ - USCRIPT_EGYPTIAN_HIEROGLYPHS = 71, /* Egyp */ - /** @stable ICU 3.6 */ - USCRIPT_KHUTSURI = 72, /* Geok */ - /** @stable ICU 3.6 */ - USCRIPT_SIMPLIFIED_HAN = 73, /* Hans */ - /** @stable ICU 3.6 */ - USCRIPT_TRADITIONAL_HAN = 74, /* Hant */ - /** @stable ICU 3.6 */ - USCRIPT_PAHAWH_HMONG = 75, /* Hmng */ - /** @stable ICU 3.6 */ - USCRIPT_OLD_HUNGARIAN = 76, /* Hung */ - /** @stable ICU 3.6 */ - USCRIPT_HARAPPAN_INDUS = 77, /* Inds */ - /** @stable ICU 3.6 */ - USCRIPT_JAVANESE = 78, /* Java */ - /** @stable ICU 3.6 */ - USCRIPT_KAYAH_LI = 79, /* Kali */ - /** @stable ICU 3.6 */ - USCRIPT_LATIN_FRAKTUR = 80, /* Latf */ - /** @stable ICU 3.6 */ - USCRIPT_LATIN_GAELIC = 81, /* Latg */ - /** @stable ICU 3.6 */ - USCRIPT_LEPCHA = 82, /* Lepc */ - /** @stable ICU 3.6 */ - USCRIPT_LINEAR_A = 83, /* Lina */ - /** @stable ICU 4.6 */ - USCRIPT_MANDAIC = 84, /* Mand */ - /** @stable ICU 3.6 */ - USCRIPT_MANDAEAN = USCRIPT_MANDAIC, - /** @stable ICU 3.6 */ - USCRIPT_MAYAN_HIEROGLYPHS = 85, /* Maya */ - /** @stable ICU 4.6 */ - USCRIPT_MEROITIC_HIEROGLYPHS = 86, /* Mero */ - /** @stable ICU 3.6 */ - USCRIPT_MEROITIC = USCRIPT_MEROITIC_HIEROGLYPHS, - /** @stable ICU 3.6 */ - USCRIPT_NKO = 87, /* Nkoo */ - /** @stable ICU 3.6 */ - USCRIPT_ORKHON = 88, /* Orkh */ - /** @stable ICU 3.6 */ - USCRIPT_OLD_PERMIC = 89, /* Perm */ - /** @stable ICU 3.6 */ - USCRIPT_PHAGS_PA = 90, /* Phag */ - /** @stable ICU 3.6 */ - USCRIPT_PHOENICIAN = 91, /* Phnx */ - /** @stable ICU 52 */ - USCRIPT_MIAO = 92, /* Plrd */ - /** @stable ICU 3.6 */ - USCRIPT_PHONETIC_POLLARD = USCRIPT_MIAO, - /** @stable ICU 3.6 */ - USCRIPT_RONGORONGO = 93, /* Roro */ - /** @stable ICU 3.6 */ - USCRIPT_SARATI = 94, /* Sara */ - /** @stable ICU 3.6 */ - USCRIPT_ESTRANGELO_SYRIAC = 95, /* Syre */ - /** @stable ICU 3.6 */ - USCRIPT_WESTERN_SYRIAC = 96, /* Syrj */ - /** @stable ICU 3.6 */ - USCRIPT_EASTERN_SYRIAC = 97, /* Syrn */ - /** @stable ICU 3.6 */ - USCRIPT_TENGWAR = 98, /* Teng */ - /** @stable ICU 3.6 */ - USCRIPT_VAI = 99, /* Vaii */ - /** @stable ICU 3.6 */ - USCRIPT_VISIBLE_SPEECH = 100,/* Visp */ - /** @stable ICU 3.6 */ - USCRIPT_CUNEIFORM = 101,/* Xsux */ - /** @stable ICU 3.6 */ - USCRIPT_UNWRITTEN_LANGUAGES = 102,/* Zxxx */ - /** @stable ICU 3.6 */ - USCRIPT_UNKNOWN = 103,/* Zzzz */ /* Unknown="Code for uncoded script", for unassigned code points */ - - /** @stable ICU 3.8 */ - USCRIPT_CARIAN = 104,/* Cari */ - /** @stable ICU 3.8 */ - USCRIPT_JAPANESE = 105,/* Jpan */ - /** @stable ICU 3.8 */ - USCRIPT_LANNA = 106,/* Lana */ - /** @stable ICU 3.8 */ - USCRIPT_LYCIAN = 107,/* Lyci */ - /** @stable ICU 3.8 */ - USCRIPT_LYDIAN = 108,/* Lydi */ - /** @stable ICU 3.8 */ - USCRIPT_OL_CHIKI = 109,/* Olck */ - /** @stable ICU 3.8 */ - USCRIPT_REJANG = 110,/* Rjng */ - /** @stable ICU 3.8 */ - USCRIPT_SAURASHTRA = 111,/* Saur */ - /** Sutton SignWriting @stable ICU 3.8 */ - USCRIPT_SIGN_WRITING = 112,/* Sgnw */ - /** @stable ICU 3.8 */ - USCRIPT_SUNDANESE = 113,/* Sund */ - /** @stable ICU 3.8 */ - USCRIPT_MOON = 114,/* Moon */ - /** @stable ICU 3.8 */ - USCRIPT_MEITEI_MAYEK = 115,/* Mtei */ - - /** @stable ICU 4.0 */ - USCRIPT_IMPERIAL_ARAMAIC = 116,/* Armi */ - /** @stable ICU 4.0 */ - USCRIPT_AVESTAN = 117,/* Avst */ - /** @stable ICU 4.0 */ - USCRIPT_CHAKMA = 118,/* Cakm */ - /** @stable ICU 4.0 */ - USCRIPT_KOREAN = 119,/* Kore */ - /** @stable ICU 4.0 */ - USCRIPT_KAITHI = 120,/* Kthi */ - /** @stable ICU 4.0 */ - USCRIPT_MANICHAEAN = 121,/* Mani */ - /** @stable ICU 4.0 */ - USCRIPT_INSCRIPTIONAL_PAHLAVI = 122,/* Phli */ - /** @stable ICU 4.0 */ - USCRIPT_PSALTER_PAHLAVI = 123,/* Phlp */ - /** @stable ICU 4.0 */ - USCRIPT_BOOK_PAHLAVI = 124,/* Phlv */ - /** @stable ICU 4.0 */ - USCRIPT_INSCRIPTIONAL_PARTHIAN = 125,/* Prti */ - /** @stable ICU 4.0 */ - USCRIPT_SAMARITAN = 126,/* Samr */ - /** @stable ICU 4.0 */ - USCRIPT_TAI_VIET = 127,/* Tavt */ - /** @stable ICU 4.0 */ - USCRIPT_MATHEMATICAL_NOTATION = 128,/* Zmth */ - /** @stable ICU 4.0 */ - USCRIPT_SYMBOLS = 129,/* Zsym */ - - /** @stable ICU 4.4 */ - USCRIPT_BAMUM = 130,/* Bamu */ - /** @stable ICU 4.4 */ - USCRIPT_LISU = 131,/* Lisu */ - /** @stable ICU 4.4 */ - USCRIPT_NAKHI_GEBA = 132,/* Nkgb */ - /** @stable ICU 4.4 */ - USCRIPT_OLD_SOUTH_ARABIAN = 133,/* Sarb */ - - /** @stable ICU 4.6 */ - USCRIPT_BASSA_VAH = 134,/* Bass */ - /** @stable ICU 54 */ - USCRIPT_DUPLOYAN = 135,/* Dupl */ -#ifndef U_HIDE_DEPRECATED_API - /** @deprecated ICU 54 Typo, use USCRIPT_DUPLOYAN */ - USCRIPT_DUPLOYAN_SHORTAND = USCRIPT_DUPLOYAN, -#endif /* U_HIDE_DEPRECATED_API */ - /** @stable ICU 4.6 */ - USCRIPT_ELBASAN = 136,/* Elba */ - /** @stable ICU 4.6 */ - USCRIPT_GRANTHA = 137,/* Gran */ - /** @stable ICU 4.6 */ - USCRIPT_KPELLE = 138,/* Kpel */ - /** @stable ICU 4.6 */ - USCRIPT_LOMA = 139,/* Loma */ - /** Mende Kikakui @stable ICU 4.6 */ - USCRIPT_MENDE = 140,/* Mend */ - /** @stable ICU 4.6 */ - USCRIPT_MEROITIC_CURSIVE = 141,/* Merc */ - /** @stable ICU 4.6 */ - USCRIPT_OLD_NORTH_ARABIAN = 142,/* Narb */ - /** @stable ICU 4.6 */ - USCRIPT_NABATAEAN = 143,/* Nbat */ - /** @stable ICU 4.6 */ - USCRIPT_PALMYRENE = 144,/* Palm */ - /** @stable ICU 54 */ - USCRIPT_KHUDAWADI = 145,/* Sind */ - /** @stable ICU 4.6 */ - USCRIPT_SINDHI = USCRIPT_KHUDAWADI, - /** @stable ICU 4.6 */ - USCRIPT_WARANG_CITI = 146,/* Wara */ - - /** @stable ICU 4.8 */ - USCRIPT_AFAKA = 147,/* Afak */ - /** @stable ICU 4.8 */ - USCRIPT_JURCHEN = 148,/* Jurc */ - /** @stable ICU 4.8 */ - USCRIPT_MRO = 149,/* Mroo */ - /** @stable ICU 4.8 */ - USCRIPT_NUSHU = 150,/* Nshu */ - /** @stable ICU 4.8 */ - USCRIPT_SHARADA = 151,/* Shrd */ - /** @stable ICU 4.8 */ - USCRIPT_SORA_SOMPENG = 152,/* Sora */ - /** @stable ICU 4.8 */ - USCRIPT_TAKRI = 153,/* Takr */ - /** @stable ICU 4.8 */ - USCRIPT_TANGUT = 154,/* Tang */ - /** @stable ICU 4.8 */ - USCRIPT_WOLEAI = 155,/* Wole */ - - /** @stable ICU 49 */ - USCRIPT_ANATOLIAN_HIEROGLYPHS = 156,/* Hluw */ - /** @stable ICU 49 */ - USCRIPT_KHOJKI = 157,/* Khoj */ - /** @stable ICU 49 */ - USCRIPT_TIRHUTA = 158,/* Tirh */ - - /** @stable ICU 52 */ - USCRIPT_CAUCASIAN_ALBANIAN = 159,/* Aghb */ - /** @stable ICU 52 */ - USCRIPT_MAHAJANI = 160,/* Mahj */ - - /** @stable ICU 54 */ - USCRIPT_AHOM = 161,/* Ahom */ - /** @stable ICU 54 */ - USCRIPT_HATRAN = 162,/* Hatr */ - /** @stable ICU 54 */ - USCRIPT_MODI = 163,/* Modi */ - /** @stable ICU 54 */ - USCRIPT_MULTANI = 164,/* Mult */ - /** @stable ICU 54 */ - USCRIPT_PAU_CIN_HAU = 165,/* Pauc */ - /** @stable ICU 54 */ - USCRIPT_SIDDHAM = 166,/* Sidd */ - - /** @stable ICU 58 */ - USCRIPT_ADLAM = 167,/* Adlm */ - /** @stable ICU 58 */ - USCRIPT_BHAIKSUKI = 168,/* Bhks */ - /** @stable ICU 58 */ - USCRIPT_MARCHEN = 169,/* Marc */ - /** @stable ICU 58 */ - USCRIPT_NEWA = 170,/* Newa */ - /** @stable ICU 58 */ - USCRIPT_OSAGE = 171,/* Osge */ - - /** @stable ICU 58 */ - USCRIPT_HAN_WITH_BOPOMOFO = 172,/* Hanb */ - /** @stable ICU 58 */ - USCRIPT_JAMO = 173,/* Jamo */ - /** @stable ICU 58 */ - USCRIPT_SYMBOLS_EMOJI = 174,/* Zsye */ - - /** @stable ICU 60 */ - USCRIPT_MASARAM_GONDI = 175,/* Gonm */ - /** @stable ICU 60 */ - USCRIPT_SOYOMBO = 176,/* Soyo */ - /** @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 */ - - /** @stable ICU 64 */ - USCRIPT_ELYMAIC = 185,/* Elym */ - /** @stable ICU 64 */ - USCRIPT_NYIAKENG_PUACHUE_HMONG = 186,/* Hmnp */ - /** @stable ICU 64 */ - USCRIPT_NANDINAGARI = 187,/* Nand */ - /** @stable ICU 64 */ - USCRIPT_WANCHO = 188,/* Wcho */ - - /** @stable ICU 66 */ - USCRIPT_CHORASMIAN = 189,/* Chrs */ - /** @stable ICU 66 */ - USCRIPT_DIVES_AKURU = 190,/* Diak */ - /** @stable ICU 66 */ - USCRIPT_KHITAN_SMALL_SCRIPT = 191,/* Kits */ - /** @stable ICU 66 */ - USCRIPT_YEZIDI = 192,/* Yezi */ - - /** @stable ICU 70 */ - USCRIPT_CYPRO_MINOAN = 193,/* Cpmn */ - /** @stable ICU 70 */ - USCRIPT_OLD_UYGHUR = 194,/* Ougr */ - /** @stable ICU 70 */ - USCRIPT_TANGSA = 195,/* Tnsa */ - /** @stable ICU 70 */ - USCRIPT_TOTO = 196,/* Toto */ - /** @stable ICU 70 */ - USCRIPT_VITHKUQI = 197,/* Vith */ - - /** @stable ICU 72 */ - USCRIPT_KAWI = 198,/* Kawi */ - /** @stable ICU 72 */ - USCRIPT_NAG_MUNDARI = 199,/* Nagm */ - -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal UScriptCode value. - * The highest value is available via u_getIntPropertyMaxValue(UCHAR_SCRIPT). - * - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - USCRIPT_CODE_LIMIT = 200 -#endif // U_HIDE_DEPRECATED_API -} UScriptCode; - -/** - * Gets the script codes associated with the given locale or ISO 15924 abbreviation or name. - * Fills in USCRIPT_MALAYALAM given "Malayam" OR "Mlym". - * Fills in USCRIPT_LATIN given "en" OR "en_US" - * If the required capacity is greater than the capacity of the destination buffer, - * then the error code is set to U_BUFFER_OVERFLOW_ERROR and the required capacity is returned. - * - *

Note: To search by short or long script alias only, use - * u_getPropertyValueEnum(UCHAR_SCRIPT, alias) instead. That does - * a fast lookup with no access of the locale data. - * - * @param nameOrAbbrOrLocale name of the script, as given in - * PropertyValueAliases.txt, or ISO 15924 code or locale - * @param fillIn the UScriptCode buffer to fill in the script code - * @param capacity the capacity (size) of UScriptCode buffer passed in. - * @param err the error status code. - * @return The number of script codes filled in the buffer passed in - * @stable ICU 2.4 - */ -U_CAPI int32_t U_EXPORT2 -uscript_getCode(const char* nameOrAbbrOrLocale,UScriptCode* fillIn,int32_t capacity,UErrorCode *err); - -/** - * Returns the long Unicode script name, if there is one. - * Otherwise returns the 4-letter ISO 15924 script code. - * Returns "Malayam" given USCRIPT_MALAYALAM. - * - * @param scriptCode UScriptCode enum - * @return long script name as given in PropertyValueAliases.txt, or the 4-letter code, - * or NULL if scriptCode is invalid - * @stable ICU 2.4 - */ -U_CAPI const char* U_EXPORT2 -uscript_getName(UScriptCode scriptCode); - -/** - * Returns the 4-letter ISO 15924 script code, - * which is the same as the short Unicode script name if Unicode has names for the script. - * Returns "Mlym" given USCRIPT_MALAYALAM. - * - * @param scriptCode UScriptCode enum - * @return short script name (4-letter code), or NULL if scriptCode is invalid - * @stable ICU 2.4 - */ -U_CAPI const char* U_EXPORT2 -uscript_getShortName(UScriptCode scriptCode); - -/** - * Gets the script code associated with the given codepoint. - * Returns USCRIPT_MALAYALAM given 0x0D02 - * @param codepoint UChar32 codepoint - * @param err the error status code. - * @return The UScriptCode, or 0 if codepoint is invalid - * @stable ICU 2.4 - */ -U_CAPI UScriptCode U_EXPORT2 -uscript_getScript(UChar32 codepoint, UErrorCode *err); - -/** - * Do the Script_Extensions of code point c contain script sc? - * If c does not have explicit Script_Extensions, then this tests whether - * c has the Script property value sc. - * - * Some characters are commonly used in multiple scripts. - * For more information, see UAX #24: http://www.unicode.org/reports/tr24/. - * @param c code point - * @param sc script code - * @return true if sc is in Script_Extensions(c) - * @stable ICU 49 - */ -U_CAPI UBool U_EXPORT2 -uscript_hasScript(UChar32 c, UScriptCode sc); - -/** - * Writes code point c's Script_Extensions as a list of UScriptCode values - * to the output scripts array and returns the number of script codes. - * - If c does have Script_Extensions, then the Script property value - * (normally Common or Inherited) is not included. - * - If c does not have Script_Extensions, then the one Script code is written to the output array. - * - If c is not a valid code point, then the one USCRIPT_UNKNOWN code is written. - * In other words, if the return value is 1, - * then the output array contains exactly c's single Script code. - * If the return value is n>=2, then the output array contains c's n Script_Extensions script codes. - * - * Some characters are commonly used in multiple scripts. - * For more information, see UAX #24: http://www.unicode.org/reports/tr24/. - * - * If there are more than capacity script codes to be written, then - * U_BUFFER_OVERFLOW_ERROR is set and the number of Script_Extensions is returned. - * (Usual ICU buffer handling behavior.) - * - * @param c code point - * @param scripts output script code array - * @param capacity capacity of the scripts array - * @param errorCode Standard 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.) - * @return number of script codes in c's Script_Extensions, or 1 for the single Script value, - * written to scripts unless U_BUFFER_OVERFLOW_ERROR indicates insufficient capacity - * @stable ICU 49 - */ -U_CAPI int32_t U_EXPORT2 -uscript_getScriptExtensions(UChar32 c, - UScriptCode *scripts, int32_t capacity, - UErrorCode *errorCode); - -/** - * Script usage constants. - * See UAX #31 Unicode Identifier and Pattern Syntax. - * http://www.unicode.org/reports/tr31/#Table_Candidate_Characters_for_Exclusion_from_Identifiers - * - * @stable ICU 51 - */ -typedef enum UScriptUsage { - /** Not encoded in Unicode. @stable ICU 51 */ - USCRIPT_USAGE_NOT_ENCODED, - /** Unknown script usage. @stable ICU 51 */ - USCRIPT_USAGE_UNKNOWN, - /** Candidate for Exclusion from Identifiers. @stable ICU 51 */ - USCRIPT_USAGE_EXCLUDED, - /** Limited Use script. @stable ICU 51 */ - USCRIPT_USAGE_LIMITED_USE, - /** Aspirational Use script. @stable ICU 51 */ - USCRIPT_USAGE_ASPIRATIONAL, - /** Recommended script. @stable ICU 51 */ - USCRIPT_USAGE_RECOMMENDED -} UScriptUsage; - -/** - * Writes the script sample character string. - * This string normally consists of one code point but might be longer. - * The string is empty if the script is not encoded. - * - * @param script script code - * @param dest output string array - * @param capacity number of UChars in the dest array - * @param pErrorCode standard ICU in/out error code, must pass U_SUCCESS() on input - * @return the string length, even if U_BUFFER_OVERFLOW_ERROR - * @stable ICU 51 - */ -U_CAPI int32_t U_EXPORT2 -uscript_getSampleString(UScriptCode script, UChar *dest, int32_t capacity, UErrorCode *pErrorCode); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN -class UnicodeString; -U_NAMESPACE_END - -/** - * Returns the script sample character string. - * This string normally consists of one code point but might be longer. - * The string is empty if the script is not encoded. - * - * @param script script code - * @return the sample character string - * @stable ICU 51 - */ -U_COMMON_API icu::UnicodeString U_EXPORT2 -uscript_getSampleUnicodeString(UScriptCode script); - -#endif - -/** - * Returns the script usage according to UAX #31 Unicode Identifier and Pattern Syntax. - * Returns USCRIPT_USAGE_NOT_ENCODED if the script is not encoded in Unicode. - * - * @param script script code - * @return script usage - * @see UScriptUsage - * @stable ICU 51 - */ -U_CAPI UScriptUsage U_EXPORT2 -uscript_getUsage(UScriptCode script); - -/** - * Returns true if the script is written right-to-left. - * For example, Arab and Hebr. - * - * @param script script code - * @return true if the script is right-to-left - * @stable ICU 51 - */ -U_CAPI UBool U_EXPORT2 -uscript_isRightToLeft(UScriptCode script); - -/** - * Returns true if the script allows line breaks between letters (excluding hyphenation). - * Such a script typically requires dictionary-based line breaking. - * For example, Hani and Thai. - * - * @param script script code - * @return true if the script allows line breaks between letters - * @stable ICU 51 - */ -U_CAPI UBool U_EXPORT2 -uscript_breaksBetweenLetters(UScriptCode script); - -/** - * Returns true if in modern (or most recent) usage of the script case distinctions are customary. - * For example, Latn and Cyrl. - * - * @param script script code - * @return true if the script is cased - * @stable ICU 51 - */ -U_CAPI UBool U_EXPORT2 -uscript_isCased(UScriptCode script); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 1997-2016, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + * + * File USCRIPT.H + * + * Modification History: + * + * Date Name Description + * 07/06/2001 Ram Creation. + ****************************************************************************** + */ + +#ifndef USCRIPT_H +#define USCRIPT_H +#include "unicode/utypes.h" + +/** + * \file + * \brief C API: Unicode Script Information + */ + +/** + * Constants for ISO 15924 script codes. + * + * The current set of script code constants supports at least all scripts + * that are encoded in the version of Unicode which ICU currently supports. + * The names of the constants are usually derived from the + * Unicode script property value aliases. + * See UAX #24 Unicode Script Property (http://www.unicode.org/reports/tr24/) + * and http://www.unicode.org/Public/UCD/latest/ucd/PropertyValueAliases.txt . + * + * In addition, constants for many ISO 15924 script codes + * are included, for use with language tags, CLDR data, and similar. + * Some of those codes are not used in the Unicode Character Database (UCD). + * For example, there are no characters that have a UCD script property value of + * Hans or Hant. All Han ideographs have the Hani script property value in Unicode. + * + * Private-use codes Qaaa..Qabx are not included, except as used in the UCD or in CLDR. + * + * Starting with ICU 55, script codes are only added when their scripts + * have been or will certainly be encoded in Unicode, + * and have been assigned Unicode script property value aliases, + * to ensure that their script names are stable and match the names of the constants. + * Script codes like Latf and Aran that are not subject to separate encoding + * may be added at any time. + * + * @stable ICU 2.2 + */ +typedef enum UScriptCode { + /* + * Note: UScriptCode constants and their ISO script code comments + * are parsed by preparseucd.py. + * It matches lines like + * USCRIPT_ = , / * * / + */ + + /** @stable ICU 2.2 */ + USCRIPT_INVALID_CODE = -1, + /** @stable ICU 2.2 */ + USCRIPT_COMMON = 0, /* Zyyy */ + /** @stable ICU 2.2 */ + USCRIPT_INHERITED = 1, /* Zinh */ /* "Code for inherited script", for non-spacing combining marks; also Qaai */ + /** @stable ICU 2.2 */ + USCRIPT_ARABIC = 2, /* Arab */ + /** @stable ICU 2.2 */ + USCRIPT_ARMENIAN = 3, /* Armn */ + /** @stable ICU 2.2 */ + USCRIPT_BENGALI = 4, /* Beng */ + /** @stable ICU 2.2 */ + USCRIPT_BOPOMOFO = 5, /* Bopo */ + /** @stable ICU 2.2 */ + USCRIPT_CHEROKEE = 6, /* Cher */ + /** @stable ICU 2.2 */ + USCRIPT_COPTIC = 7, /* Copt */ + /** @stable ICU 2.2 */ + USCRIPT_CYRILLIC = 8, /* Cyrl */ + /** @stable ICU 2.2 */ + USCRIPT_DESERET = 9, /* Dsrt */ + /** @stable ICU 2.2 */ + USCRIPT_DEVANAGARI = 10, /* Deva */ + /** @stable ICU 2.2 */ + USCRIPT_ETHIOPIC = 11, /* Ethi */ + /** @stable ICU 2.2 */ + USCRIPT_GEORGIAN = 12, /* Geor */ + /** @stable ICU 2.2 */ + USCRIPT_GOTHIC = 13, /* Goth */ + /** @stable ICU 2.2 */ + USCRIPT_GREEK = 14, /* Grek */ + /** @stable ICU 2.2 */ + USCRIPT_GUJARATI = 15, /* Gujr */ + /** @stable ICU 2.2 */ + USCRIPT_GURMUKHI = 16, /* Guru */ + /** @stable ICU 2.2 */ + USCRIPT_HAN = 17, /* Hani */ + /** @stable ICU 2.2 */ + USCRIPT_HANGUL = 18, /* Hang */ + /** @stable ICU 2.2 */ + USCRIPT_HEBREW = 19, /* Hebr */ + /** @stable ICU 2.2 */ + USCRIPT_HIRAGANA = 20, /* Hira */ + /** @stable ICU 2.2 */ + USCRIPT_KANNADA = 21, /* Knda */ + /** @stable ICU 2.2 */ + USCRIPT_KATAKANA = 22, /* Kana */ + /** @stable ICU 2.2 */ + USCRIPT_KHMER = 23, /* Khmr */ + /** @stable ICU 2.2 */ + USCRIPT_LAO = 24, /* Laoo */ + /** @stable ICU 2.2 */ + USCRIPT_LATIN = 25, /* Latn */ + /** @stable ICU 2.2 */ + USCRIPT_MALAYALAM = 26, /* Mlym */ + /** @stable ICU 2.2 */ + USCRIPT_MONGOLIAN = 27, /* Mong */ + /** @stable ICU 2.2 */ + USCRIPT_MYANMAR = 28, /* Mymr */ + /** @stable ICU 2.2 */ + USCRIPT_OGHAM = 29, /* Ogam */ + /** @stable ICU 2.2 */ + USCRIPT_OLD_ITALIC = 30, /* Ital */ + /** @stable ICU 2.2 */ + USCRIPT_ORIYA = 31, /* Orya */ + /** @stable ICU 2.2 */ + USCRIPT_RUNIC = 32, /* Runr */ + /** @stable ICU 2.2 */ + USCRIPT_SINHALA = 33, /* Sinh */ + /** @stable ICU 2.2 */ + USCRIPT_SYRIAC = 34, /* Syrc */ + /** @stable ICU 2.2 */ + USCRIPT_TAMIL = 35, /* Taml */ + /** @stable ICU 2.2 */ + USCRIPT_TELUGU = 36, /* Telu */ + /** @stable ICU 2.2 */ + USCRIPT_THAANA = 37, /* Thaa */ + /** @stable ICU 2.2 */ + USCRIPT_THAI = 38, /* Thai */ + /** @stable ICU 2.2 */ + USCRIPT_TIBETAN = 39, /* Tibt */ + /** Canadian_Aboriginal script. @stable ICU 2.6 */ + USCRIPT_CANADIAN_ABORIGINAL = 40, /* Cans */ + /** Canadian_Aboriginal script (alias). @stable ICU 2.2 */ + USCRIPT_UCAS = USCRIPT_CANADIAN_ABORIGINAL, + /** @stable ICU 2.2 */ + USCRIPT_YI = 41, /* Yiii */ + /* New scripts in Unicode 3.2 */ + /** @stable ICU 2.2 */ + USCRIPT_TAGALOG = 42, /* Tglg */ + /** @stable ICU 2.2 */ + USCRIPT_HANUNOO = 43, /* Hano */ + /** @stable ICU 2.2 */ + USCRIPT_BUHID = 44, /* Buhd */ + /** @stable ICU 2.2 */ + USCRIPT_TAGBANWA = 45, /* Tagb */ + + /* New scripts in Unicode 4 */ + /** @stable ICU 2.6 */ + USCRIPT_BRAILLE = 46, /* Brai */ + /** @stable ICU 2.6 */ + USCRIPT_CYPRIOT = 47, /* Cprt */ + /** @stable ICU 2.6 */ + USCRIPT_LIMBU = 48, /* Limb */ + /** @stable ICU 2.6 */ + USCRIPT_LINEAR_B = 49, /* Linb */ + /** @stable ICU 2.6 */ + USCRIPT_OSMANYA = 50, /* Osma */ + /** @stable ICU 2.6 */ + USCRIPT_SHAVIAN = 51, /* Shaw */ + /** @stable ICU 2.6 */ + USCRIPT_TAI_LE = 52, /* Tale */ + /** @stable ICU 2.6 */ + USCRIPT_UGARITIC = 53, /* Ugar */ + + /** New script code in Unicode 4.0.1 @stable ICU 3.0 */ + USCRIPT_KATAKANA_OR_HIRAGANA = 54,/*Hrkt */ + + /* New scripts in Unicode 4.1 */ + /** @stable ICU 3.4 */ + USCRIPT_BUGINESE = 55, /* Bugi */ + /** @stable ICU 3.4 */ + USCRIPT_GLAGOLITIC = 56, /* Glag */ + /** @stable ICU 3.4 */ + USCRIPT_KHAROSHTHI = 57, /* Khar */ + /** @stable ICU 3.4 */ + USCRIPT_SYLOTI_NAGRI = 58, /* Sylo */ + /** @stable ICU 3.4 */ + USCRIPT_NEW_TAI_LUE = 59, /* Talu */ + /** @stable ICU 3.4 */ + USCRIPT_TIFINAGH = 60, /* Tfng */ + /** @stable ICU 3.4 */ + USCRIPT_OLD_PERSIAN = 61, /* Xpeo */ + + /* New script codes from Unicode and ISO 15924 */ + /** @stable ICU 3.6 */ + USCRIPT_BALINESE = 62, /* Bali */ + /** @stable ICU 3.6 */ + USCRIPT_BATAK = 63, /* Batk */ + /** @stable ICU 3.6 */ + USCRIPT_BLISSYMBOLS = 64, /* Blis */ + /** @stable ICU 3.6 */ + USCRIPT_BRAHMI = 65, /* Brah */ + /** @stable ICU 3.6 */ + USCRIPT_CHAM = 66, /* Cham */ + /** @stable ICU 3.6 */ + USCRIPT_CIRTH = 67, /* Cirt */ + /** @stable ICU 3.6 */ + USCRIPT_OLD_CHURCH_SLAVONIC_CYRILLIC = 68, /* Cyrs */ + /** @stable ICU 3.6 */ + USCRIPT_DEMOTIC_EGYPTIAN = 69, /* Egyd */ + /** @stable ICU 3.6 */ + USCRIPT_HIERATIC_EGYPTIAN = 70, /* Egyh */ + /** @stable ICU 3.6 */ + USCRIPT_EGYPTIAN_HIEROGLYPHS = 71, /* Egyp */ + /** @stable ICU 3.6 */ + USCRIPT_KHUTSURI = 72, /* Geok */ + /** @stable ICU 3.6 */ + USCRIPT_SIMPLIFIED_HAN = 73, /* Hans */ + /** @stable ICU 3.6 */ + USCRIPT_TRADITIONAL_HAN = 74, /* Hant */ + /** @stable ICU 3.6 */ + USCRIPT_PAHAWH_HMONG = 75, /* Hmng */ + /** @stable ICU 3.6 */ + USCRIPT_OLD_HUNGARIAN = 76, /* Hung */ + /** @stable ICU 3.6 */ + USCRIPT_HARAPPAN_INDUS = 77, /* Inds */ + /** @stable ICU 3.6 */ + USCRIPT_JAVANESE = 78, /* Java */ + /** @stable ICU 3.6 */ + USCRIPT_KAYAH_LI = 79, /* Kali */ + /** @stable ICU 3.6 */ + USCRIPT_LATIN_FRAKTUR = 80, /* Latf */ + /** @stable ICU 3.6 */ + USCRIPT_LATIN_GAELIC = 81, /* Latg */ + /** @stable ICU 3.6 */ + USCRIPT_LEPCHA = 82, /* Lepc */ + /** @stable ICU 3.6 */ + USCRIPT_LINEAR_A = 83, /* Lina */ + /** @stable ICU 4.6 */ + USCRIPT_MANDAIC = 84, /* Mand */ + /** @stable ICU 3.6 */ + USCRIPT_MANDAEAN = USCRIPT_MANDAIC, + /** @stable ICU 3.6 */ + USCRIPT_MAYAN_HIEROGLYPHS = 85, /* Maya */ + /** @stable ICU 4.6 */ + USCRIPT_MEROITIC_HIEROGLYPHS = 86, /* Mero */ + /** @stable ICU 3.6 */ + USCRIPT_MEROITIC = USCRIPT_MEROITIC_HIEROGLYPHS, + /** @stable ICU 3.6 */ + USCRIPT_NKO = 87, /* Nkoo */ + /** @stable ICU 3.6 */ + USCRIPT_ORKHON = 88, /* Orkh */ + /** @stable ICU 3.6 */ + USCRIPT_OLD_PERMIC = 89, /* Perm */ + /** @stable ICU 3.6 */ + USCRIPT_PHAGS_PA = 90, /* Phag */ + /** @stable ICU 3.6 */ + USCRIPT_PHOENICIAN = 91, /* Phnx */ + /** @stable ICU 52 */ + USCRIPT_MIAO = 92, /* Plrd */ + /** @stable ICU 3.6 */ + USCRIPT_PHONETIC_POLLARD = USCRIPT_MIAO, + /** @stable ICU 3.6 */ + USCRIPT_RONGORONGO = 93, /* Roro */ + /** @stable ICU 3.6 */ + USCRIPT_SARATI = 94, /* Sara */ + /** @stable ICU 3.6 */ + USCRIPT_ESTRANGELO_SYRIAC = 95, /* Syre */ + /** @stable ICU 3.6 */ + USCRIPT_WESTERN_SYRIAC = 96, /* Syrj */ + /** @stable ICU 3.6 */ + USCRIPT_EASTERN_SYRIAC = 97, /* Syrn */ + /** @stable ICU 3.6 */ + USCRIPT_TENGWAR = 98, /* Teng */ + /** @stable ICU 3.6 */ + USCRIPT_VAI = 99, /* Vaii */ + /** @stable ICU 3.6 */ + USCRIPT_VISIBLE_SPEECH = 100,/* Visp */ + /** @stable ICU 3.6 */ + USCRIPT_CUNEIFORM = 101,/* Xsux */ + /** @stable ICU 3.6 */ + USCRIPT_UNWRITTEN_LANGUAGES = 102,/* Zxxx */ + /** @stable ICU 3.6 */ + USCRIPT_UNKNOWN = 103,/* Zzzz */ /* Unknown="Code for uncoded script", for unassigned code points */ + + /** @stable ICU 3.8 */ + USCRIPT_CARIAN = 104,/* Cari */ + /** @stable ICU 3.8 */ + USCRIPT_JAPANESE = 105,/* Jpan */ + /** @stable ICU 3.8 */ + USCRIPT_LANNA = 106,/* Lana */ + /** @stable ICU 3.8 */ + USCRIPT_LYCIAN = 107,/* Lyci */ + /** @stable ICU 3.8 */ + USCRIPT_LYDIAN = 108,/* Lydi */ + /** @stable ICU 3.8 */ + USCRIPT_OL_CHIKI = 109,/* Olck */ + /** @stable ICU 3.8 */ + USCRIPT_REJANG = 110,/* Rjng */ + /** @stable ICU 3.8 */ + USCRIPT_SAURASHTRA = 111,/* Saur */ + /** Sutton SignWriting @stable ICU 3.8 */ + USCRIPT_SIGN_WRITING = 112,/* Sgnw */ + /** @stable ICU 3.8 */ + USCRIPT_SUNDANESE = 113,/* Sund */ + /** @stable ICU 3.8 */ + USCRIPT_MOON = 114,/* Moon */ + /** @stable ICU 3.8 */ + USCRIPT_MEITEI_MAYEK = 115,/* Mtei */ + + /** @stable ICU 4.0 */ + USCRIPT_IMPERIAL_ARAMAIC = 116,/* Armi */ + /** @stable ICU 4.0 */ + USCRIPT_AVESTAN = 117,/* Avst */ + /** @stable ICU 4.0 */ + USCRIPT_CHAKMA = 118,/* Cakm */ + /** @stable ICU 4.0 */ + USCRIPT_KOREAN = 119,/* Kore */ + /** @stable ICU 4.0 */ + USCRIPT_KAITHI = 120,/* Kthi */ + /** @stable ICU 4.0 */ + USCRIPT_MANICHAEAN = 121,/* Mani */ + /** @stable ICU 4.0 */ + USCRIPT_INSCRIPTIONAL_PAHLAVI = 122,/* Phli */ + /** @stable ICU 4.0 */ + USCRIPT_PSALTER_PAHLAVI = 123,/* Phlp */ + /** @stable ICU 4.0 */ + USCRIPT_BOOK_PAHLAVI = 124,/* Phlv */ + /** @stable ICU 4.0 */ + USCRIPT_INSCRIPTIONAL_PARTHIAN = 125,/* Prti */ + /** @stable ICU 4.0 */ + USCRIPT_SAMARITAN = 126,/* Samr */ + /** @stable ICU 4.0 */ + USCRIPT_TAI_VIET = 127,/* Tavt */ + /** @stable ICU 4.0 */ + USCRIPT_MATHEMATICAL_NOTATION = 128,/* Zmth */ + /** @stable ICU 4.0 */ + USCRIPT_SYMBOLS = 129,/* Zsym */ + + /** @stable ICU 4.4 */ + USCRIPT_BAMUM = 130,/* Bamu */ + /** @stable ICU 4.4 */ + USCRIPT_LISU = 131,/* Lisu */ + /** @stable ICU 4.4 */ + USCRIPT_NAKHI_GEBA = 132,/* Nkgb */ + /** @stable ICU 4.4 */ + USCRIPT_OLD_SOUTH_ARABIAN = 133,/* Sarb */ + + /** @stable ICU 4.6 */ + USCRIPT_BASSA_VAH = 134,/* Bass */ + /** @stable ICU 54 */ + USCRIPT_DUPLOYAN = 135,/* Dupl */ +#ifndef U_HIDE_DEPRECATED_API + /** @deprecated ICU 54 Typo, use USCRIPT_DUPLOYAN */ + USCRIPT_DUPLOYAN_SHORTAND = USCRIPT_DUPLOYAN, +#endif /* U_HIDE_DEPRECATED_API */ + /** @stable ICU 4.6 */ + USCRIPT_ELBASAN = 136,/* Elba */ + /** @stable ICU 4.6 */ + USCRIPT_GRANTHA = 137,/* Gran */ + /** @stable ICU 4.6 */ + USCRIPT_KPELLE = 138,/* Kpel */ + /** @stable ICU 4.6 */ + USCRIPT_LOMA = 139,/* Loma */ + /** Mende Kikakui @stable ICU 4.6 */ + USCRIPT_MENDE = 140,/* Mend */ + /** @stable ICU 4.6 */ + USCRIPT_MEROITIC_CURSIVE = 141,/* Merc */ + /** @stable ICU 4.6 */ + USCRIPT_OLD_NORTH_ARABIAN = 142,/* Narb */ + /** @stable ICU 4.6 */ + USCRIPT_NABATAEAN = 143,/* Nbat */ + /** @stable ICU 4.6 */ + USCRIPT_PALMYRENE = 144,/* Palm */ + /** @stable ICU 54 */ + USCRIPT_KHUDAWADI = 145,/* Sind */ + /** @stable ICU 4.6 */ + USCRIPT_SINDHI = USCRIPT_KHUDAWADI, + /** @stable ICU 4.6 */ + USCRIPT_WARANG_CITI = 146,/* Wara */ + + /** @stable ICU 4.8 */ + USCRIPT_AFAKA = 147,/* Afak */ + /** @stable ICU 4.8 */ + USCRIPT_JURCHEN = 148,/* Jurc */ + /** @stable ICU 4.8 */ + USCRIPT_MRO = 149,/* Mroo */ + /** @stable ICU 4.8 */ + USCRIPT_NUSHU = 150,/* Nshu */ + /** @stable ICU 4.8 */ + USCRIPT_SHARADA = 151,/* Shrd */ + /** @stable ICU 4.8 */ + USCRIPT_SORA_SOMPENG = 152,/* Sora */ + /** @stable ICU 4.8 */ + USCRIPT_TAKRI = 153,/* Takr */ + /** @stable ICU 4.8 */ + USCRIPT_TANGUT = 154,/* Tang */ + /** @stable ICU 4.8 */ + USCRIPT_WOLEAI = 155,/* Wole */ + + /** @stable ICU 49 */ + USCRIPT_ANATOLIAN_HIEROGLYPHS = 156,/* Hluw */ + /** @stable ICU 49 */ + USCRIPT_KHOJKI = 157,/* Khoj */ + /** @stable ICU 49 */ + USCRIPT_TIRHUTA = 158,/* Tirh */ + + /** @stable ICU 52 */ + USCRIPT_CAUCASIAN_ALBANIAN = 159,/* Aghb */ + /** @stable ICU 52 */ + USCRIPT_MAHAJANI = 160,/* Mahj */ + + /** @stable ICU 54 */ + USCRIPT_AHOM = 161,/* Ahom */ + /** @stable ICU 54 */ + USCRIPT_HATRAN = 162,/* Hatr */ + /** @stable ICU 54 */ + USCRIPT_MODI = 163,/* Modi */ + /** @stable ICU 54 */ + USCRIPT_MULTANI = 164,/* Mult */ + /** @stable ICU 54 */ + USCRIPT_PAU_CIN_HAU = 165,/* Pauc */ + /** @stable ICU 54 */ + USCRIPT_SIDDHAM = 166,/* Sidd */ + + /** @stable ICU 58 */ + USCRIPT_ADLAM = 167,/* Adlm */ + /** @stable ICU 58 */ + USCRIPT_BHAIKSUKI = 168,/* Bhks */ + /** @stable ICU 58 */ + USCRIPT_MARCHEN = 169,/* Marc */ + /** @stable ICU 58 */ + USCRIPT_NEWA = 170,/* Newa */ + /** @stable ICU 58 */ + USCRIPT_OSAGE = 171,/* Osge */ + + /** @stable ICU 58 */ + USCRIPT_HAN_WITH_BOPOMOFO = 172,/* Hanb */ + /** @stable ICU 58 */ + USCRIPT_JAMO = 173,/* Jamo */ + /** @stable ICU 58 */ + USCRIPT_SYMBOLS_EMOJI = 174,/* Zsye */ + + /** @stable ICU 60 */ + USCRIPT_MASARAM_GONDI = 175,/* Gonm */ + /** @stable ICU 60 */ + USCRIPT_SOYOMBO = 176,/* Soyo */ + /** @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 */ + + /** @stable ICU 64 */ + USCRIPT_ELYMAIC = 185,/* Elym */ + /** @stable ICU 64 */ + USCRIPT_NYIAKENG_PUACHUE_HMONG = 186,/* Hmnp */ + /** @stable ICU 64 */ + USCRIPT_NANDINAGARI = 187,/* Nand */ + /** @stable ICU 64 */ + USCRIPT_WANCHO = 188,/* Wcho */ + + /** @stable ICU 66 */ + USCRIPT_CHORASMIAN = 189,/* Chrs */ + /** @stable ICU 66 */ + USCRIPT_DIVES_AKURU = 190,/* Diak */ + /** @stable ICU 66 */ + USCRIPT_KHITAN_SMALL_SCRIPT = 191,/* Kits */ + /** @stable ICU 66 */ + USCRIPT_YEZIDI = 192,/* Yezi */ + + /** @stable ICU 70 */ + USCRIPT_CYPRO_MINOAN = 193,/* Cpmn */ + /** @stable ICU 70 */ + USCRIPT_OLD_UYGHUR = 194,/* Ougr */ + /** @stable ICU 70 */ + USCRIPT_TANGSA = 195,/* Tnsa */ + /** @stable ICU 70 */ + USCRIPT_TOTO = 196,/* Toto */ + /** @stable ICU 70 */ + USCRIPT_VITHKUQI = 197,/* Vith */ + + /** @stable ICU 72 */ + USCRIPT_KAWI = 198,/* Kawi */ + /** @stable ICU 72 */ + USCRIPT_NAG_MUNDARI = 199,/* Nagm */ + +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal UScriptCode value. + * The highest value is available via u_getIntPropertyMaxValue(UCHAR_SCRIPT). + * + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + USCRIPT_CODE_LIMIT = 200 +#endif // U_HIDE_DEPRECATED_API +} UScriptCode; + +/** + * Gets the script codes associated with the given locale or ISO 15924 abbreviation or name. + * Fills in USCRIPT_MALAYALAM given "Malayam" OR "Mlym". + * Fills in USCRIPT_LATIN given "en" OR "en_US" + * If the required capacity is greater than the capacity of the destination buffer, + * then the error code is set to U_BUFFER_OVERFLOW_ERROR and the required capacity is returned. + * + *

Note: To search by short or long script alias only, use + * u_getPropertyValueEnum(UCHAR_SCRIPT, alias) instead. That does + * a fast lookup with no access of the locale data. + * + * @param nameOrAbbrOrLocale name of the script, as given in + * PropertyValueAliases.txt, or ISO 15924 code or locale + * @param fillIn the UScriptCode buffer to fill in the script code + * @param capacity the capacity (size) of UScriptCode buffer passed in. + * @param err the error status code. + * @return The number of script codes filled in the buffer passed in + * @stable ICU 2.4 + */ +U_CAPI int32_t U_EXPORT2 +uscript_getCode(const char* nameOrAbbrOrLocale,UScriptCode* fillIn,int32_t capacity,UErrorCode *err); + +/** + * Returns the long Unicode script name, if there is one. + * Otherwise returns the 4-letter ISO 15924 script code. + * Returns "Malayam" given USCRIPT_MALAYALAM. + * + * @param scriptCode UScriptCode enum + * @return long script name as given in PropertyValueAliases.txt, or the 4-letter code, + * or NULL if scriptCode is invalid + * @stable ICU 2.4 + */ +U_CAPI const char* U_EXPORT2 +uscript_getName(UScriptCode scriptCode); + +/** + * Returns the 4-letter ISO 15924 script code, + * which is the same as the short Unicode script name if Unicode has names for the script. + * Returns "Mlym" given USCRIPT_MALAYALAM. + * + * @param scriptCode UScriptCode enum + * @return short script name (4-letter code), or NULL if scriptCode is invalid + * @stable ICU 2.4 + */ +U_CAPI const char* U_EXPORT2 +uscript_getShortName(UScriptCode scriptCode); + +/** + * Gets the script code associated with the given codepoint. + * Returns USCRIPT_MALAYALAM given 0x0D02 + * @param codepoint UChar32 codepoint + * @param err the error status code. + * @return The UScriptCode, or 0 if codepoint is invalid + * @stable ICU 2.4 + */ +U_CAPI UScriptCode U_EXPORT2 +uscript_getScript(UChar32 codepoint, UErrorCode *err); + +/** + * Do the Script_Extensions of code point c contain script sc? + * If c does not have explicit Script_Extensions, then this tests whether + * c has the Script property value sc. + * + * Some characters are commonly used in multiple scripts. + * For more information, see UAX #24: http://www.unicode.org/reports/tr24/. + * @param c code point + * @param sc script code + * @return true if sc is in Script_Extensions(c) + * @stable ICU 49 + */ +U_CAPI UBool U_EXPORT2 +uscript_hasScript(UChar32 c, UScriptCode sc); + +/** + * Writes code point c's Script_Extensions as a list of UScriptCode values + * to the output scripts array and returns the number of script codes. + * - If c does have Script_Extensions, then the Script property value + * (normally Common or Inherited) is not included. + * - If c does not have Script_Extensions, then the one Script code is written to the output array. + * - If c is not a valid code point, then the one USCRIPT_UNKNOWN code is written. + * In other words, if the return value is 1, + * then the output array contains exactly c's single Script code. + * If the return value is n>=2, then the output array contains c's n Script_Extensions script codes. + * + * Some characters are commonly used in multiple scripts. + * For more information, see UAX #24: http://www.unicode.org/reports/tr24/. + * + * If there are more than capacity script codes to be written, then + * U_BUFFER_OVERFLOW_ERROR is set and the number of Script_Extensions is returned. + * (Usual ICU buffer handling behavior.) + * + * @param c code point + * @param scripts output script code array + * @param capacity capacity of the scripts array + * @param errorCode Standard 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.) + * @return number of script codes in c's Script_Extensions, or 1 for the single Script value, + * written to scripts unless U_BUFFER_OVERFLOW_ERROR indicates insufficient capacity + * @stable ICU 49 + */ +U_CAPI int32_t U_EXPORT2 +uscript_getScriptExtensions(UChar32 c, + UScriptCode *scripts, int32_t capacity, + UErrorCode *errorCode); + +/** + * Script usage constants. + * See UAX #31 Unicode Identifier and Pattern Syntax. + * http://www.unicode.org/reports/tr31/#Table_Candidate_Characters_for_Exclusion_from_Identifiers + * + * @stable ICU 51 + */ +typedef enum UScriptUsage { + /** Not encoded in Unicode. @stable ICU 51 */ + USCRIPT_USAGE_NOT_ENCODED, + /** Unknown script usage. @stable ICU 51 */ + USCRIPT_USAGE_UNKNOWN, + /** Candidate for Exclusion from Identifiers. @stable ICU 51 */ + USCRIPT_USAGE_EXCLUDED, + /** Limited Use script. @stable ICU 51 */ + USCRIPT_USAGE_LIMITED_USE, + /** Aspirational Use script. @stable ICU 51 */ + USCRIPT_USAGE_ASPIRATIONAL, + /** Recommended script. @stable ICU 51 */ + USCRIPT_USAGE_RECOMMENDED +} UScriptUsage; + +/** + * Writes the script sample character string. + * This string normally consists of one code point but might be longer. + * The string is empty if the script is not encoded. + * + * @param script script code + * @param dest output string array + * @param capacity number of UChars in the dest array + * @param pErrorCode standard ICU in/out error code, must pass U_SUCCESS() on input + * @return the string length, even if U_BUFFER_OVERFLOW_ERROR + * @stable ICU 51 + */ +U_CAPI int32_t U_EXPORT2 +uscript_getSampleString(UScriptCode script, UChar *dest, int32_t capacity, UErrorCode *pErrorCode); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN +class UnicodeString; +U_NAMESPACE_END + +/** + * Returns the script sample character string. + * This string normally consists of one code point but might be longer. + * The string is empty if the script is not encoded. + * + * @param script script code + * @return the sample character string + * @stable ICU 51 + */ +U_COMMON_API icu::UnicodeString U_EXPORT2 +uscript_getSampleUnicodeString(UScriptCode script); + +#endif + +/** + * Returns the script usage according to UAX #31 Unicode Identifier and Pattern Syntax. + * Returns USCRIPT_USAGE_NOT_ENCODED if the script is not encoded in Unicode. + * + * @param script script code + * @return script usage + * @see UScriptUsage + * @stable ICU 51 + */ +U_CAPI UScriptUsage U_EXPORT2 +uscript_getUsage(UScriptCode script); + +/** + * Returns true if the script is written right-to-left. + * For example, Arab and Hebr. + * + * @param script script code + * @return true if the script is right-to-left + * @stable ICU 51 + */ +U_CAPI UBool U_EXPORT2 +uscript_isRightToLeft(UScriptCode script); + +/** + * Returns true if the script allows line breaks between letters (excluding hyphenation). + * Such a script typically requires dictionary-based line breaking. + * For example, Hani and Thai. + * + * @param script script code + * @return true if the script allows line breaks between letters + * @stable ICU 51 + */ +U_CAPI UBool U_EXPORT2 +uscript_breaksBetweenLetters(UScriptCode script); + +/** + * Returns true if in modern (or most recent) usage of the script case distinctions are customary. + * For example, Latn and Cyrl. + * + * @param script script code + * @return true if the script is cased + * @stable ICU 51 + */ +U_CAPI UBool U_EXPORT2 +uscript_isCased(UScriptCode script); + +#endif diff --git a/deps/icu-small/source/common/unicode/uset.h b/deps/icu-small/source/common/unicode/uset.h index 5dd890e148d07e..fc1260fb7f3e8d 100644 --- a/deps/icu-small/source/common/unicode/uset.h +++ b/deps/icu-small/source/common/unicode/uset.h @@ -1,1258 +1,1290 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uset.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002mar07 -* created by: Markus W. Scherer -* -* C version of UnicodeSet. -*/ - - -/** - * \file - * \brief C API: Unicode Set - * - *

This is a C wrapper around the C++ UnicodeSet class.

- */ - -#ifndef __USET_H__ -#define __USET_H__ - -#include "unicode/utypes.h" -#include "unicode/uchar.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -#ifndef USET_DEFINED - -#ifndef U_IN_DOXYGEN -#define USET_DEFINED -#endif -/** - * USet is the C API type corresponding to C++ class UnicodeSet. - * Use the uset_* API to manipulate. Create with - * uset_open*, and destroy with uset_close. - * @stable ICU 2.4 - */ -typedef struct USet USet; -#endif - -/** - * Bitmask values to be passed to uset_openPatternOptions() or - * uset_applyPattern() taking an option parameter. - * @stable ICU 2.4 - */ -enum { - /** - * Ignore white space within patterns unless quoted or escaped. - * @stable ICU 2.4 - */ - USET_IGNORE_SPACE = 1, - - /** - * Enable case insensitive matching. E.g., "[ab]" with this flag - * will match 'a', 'A', 'b', and 'B'. "[^ab]" with this flag will - * match all except 'a', 'A', 'b', and 'B'. This performs a full - * closure over case mappings, e.g. U+017F for s. - * - * The resulting set is a superset of the input for the code points but - * not for the strings. - * It performs a case mapping closure of the code points and adds - * full case folding strings for the code points, and reduces strings of - * the original set to their full case folding equivalents. - * - * This is designed for case-insensitive matches, for example - * in regular expressions. The full code point case closure allows checking of - * an input character directly against the closure set. - * Strings are matched by comparing the case-folded form from the closure - * set with an incremental case folding of the string in question. - * - * The closure set will also contain single code points if the original - * set contained case-equivalent strings (like U+00DF for "ss" or "Ss" etc.). - * This is not necessary (that is, redundant) for the above matching method - * but results in the same closure sets regardless of whether the original - * set contained the code point or a string. - * - * @stable ICU 2.4 - */ - USET_CASE_INSENSITIVE = 2, - - /** - * Enable case insensitive matching. E.g., "[ab]" with this flag - * will match 'a', 'A', 'b', and 'B'. "[^ab]" with this flag will - * match all except 'a', 'A', 'b', and 'B'. This adds the lower-, - * title-, and uppercase mappings as well as the case folding - * of each existing element in the set. - * @stable ICU 3.2 - */ - USET_ADD_CASE_MAPPINGS = 4 -}; - -/** - * Argument values for whether span() and similar functions continue while - * the current character is contained vs. not contained in the set. - * - * The functionality is straightforward for sets with only single code points, - * without strings (which is the common case): - * - USET_SPAN_CONTAINED and USET_SPAN_SIMPLE work the same. - * - USET_SPAN_CONTAINED and USET_SPAN_SIMPLE are inverses of USET_SPAN_NOT_CONTAINED. - * - span() and spanBack() partition any string the same way when - * alternating between span(USET_SPAN_NOT_CONTAINED) and - * span(either "contained" condition). - * - Using a complemented (inverted) set and the opposite span conditions - * yields the same results. - * - * When a set contains multi-code point strings, then these statements may not - * be true, depending on the strings in the set (for example, whether they - * overlap with each other) and the string that is processed. - * For a set with strings: - * - The complement of the set contains the opposite set of code points, - * but the same set of strings. - * Therefore, complementing both the set and the span conditions - * may yield different results. - * - When starting spans at different positions in a string - * (span(s, ...) vs. span(s+1, ...)) the ends of the spans may be different - * because a set string may start before the later position. - * - span(USET_SPAN_SIMPLE) may be shorter than - * span(USET_SPAN_CONTAINED) because it will not recursively try - * all possible paths. - * For example, with a set which contains the three strings "xy", "xya" and "ax", - * span("xyax", USET_SPAN_CONTAINED) will return 4 but - * span("xyax", USET_SPAN_SIMPLE) will return 3. - * span(USET_SPAN_SIMPLE) will never be longer than - * span(USET_SPAN_CONTAINED). - * - With either "contained" condition, span() and spanBack() may partition - * a string in different ways. - * For example, with a set which contains the two strings "ab" and "ba", - * and when processing the string "aba", - * span() will yield contained/not-contained boundaries of { 0, 2, 3 } - * while spanBack() will yield boundaries of { 0, 1, 3 }. - * - * Note: If it is important to get the same boundaries whether iterating forward - * or backward through a string, then either only span() should be used and - * the boundaries cached for backward operation, or an ICU BreakIterator - * could be used. - * - * Note: Unpaired surrogates are treated like surrogate code points. - * Similarly, set strings match only on code point boundaries, - * never in the middle of a surrogate pair. - * Illegal UTF-8 sequences are treated like U+FFFD. - * When processing UTF-8 strings, malformed set strings - * (strings with unpaired surrogates which cannot be converted to UTF-8) - * are ignored. - * - * @stable ICU 3.8 - */ -typedef enum USetSpanCondition { - /** - * Continues a span() while there is no set element at the current position. - * Increments by one code point at a time. - * Stops before the first set element (character or string). - * (For code points only, this is like while contains(current)==false). - * - * When span() returns, the substring between where it started and the position - * it returned consists only of characters that are not in the set, - * and none of its strings overlap with the span. - * - * @stable ICU 3.8 - */ - USET_SPAN_NOT_CONTAINED = 0, - /** - * Spans the longest substring that is a concatenation of set elements (characters or strings). - * (For characters only, this is like while contains(current)==true). - * - * When span() returns, the substring between where it started and the position - * it returned consists only of set elements (characters or strings) that are in the set. - * - * If a set contains strings, then the span will be the longest substring for which there - * exists at least one non-overlapping concatenation of set elements (characters or strings). - * This is equivalent to a POSIX regular expression for (OR of each set element)*. - * (Java/ICU/Perl regex stops at the first match of an OR.) - * - * @stable ICU 3.8 - */ - USET_SPAN_CONTAINED = 1, - /** - * Continues a span() while there is a set element at the current position. - * Increments by the longest matching element at each position. - * (For characters only, this is like while contains(current)==true). - * - * When span() returns, the substring between where it started and the position - * it returned consists only of set elements (characters or strings) that are in the set. - * - * If a set only contains single characters, then this is the same - * as USET_SPAN_CONTAINED. - * - * If a set contains strings, then the span will be the longest substring - * with a match at each position with the longest single set element (character or string). - * - * Use this span condition together with other longest-match algorithms, - * such as ICU converters (ucnv_getUnicodeSet()). - * - * @stable ICU 3.8 - */ - USET_SPAN_SIMPLE = 2, -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the last span condition. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - USET_SPAN_CONDITION_COUNT -#endif // U_HIDE_DEPRECATED_API -} USetSpanCondition; - -enum { - /** - * Capacity of USerializedSet::staticArray. - * Enough for any single-code point set. - * Also provides padding for nice sizeof(USerializedSet). - * @stable ICU 2.4 - */ - USET_SERIALIZED_STATIC_ARRAY_CAPACITY=8 -}; - -/** - * A serialized form of a Unicode set. Limited manipulations are - * possible directly on a serialized set. See below. - * @stable ICU 2.4 - */ -typedef struct USerializedSet { - /** - * The serialized Unicode Set. - * @stable ICU 2.4 - */ - const uint16_t *array; - /** - * The length of the array that contains BMP characters. - * @stable ICU 2.4 - */ - int32_t bmpLength; - /** - * The total length of the array. - * @stable ICU 2.4 - */ - int32_t length; - /** - * A small buffer for the array to reduce memory allocations. - * @stable ICU 2.4 - */ - uint16_t staticArray[USET_SERIALIZED_STATIC_ARRAY_CAPACITY]; -} USerializedSet; - -/********************************************************************* - * USet API - *********************************************************************/ - -/** - * Create an empty USet object. - * Equivalent to uset_open(1, 0). - * @return a newly created USet. The caller must call uset_close() on - * it when done. - * @stable ICU 4.2 - */ -U_CAPI USet* U_EXPORT2 -uset_openEmpty(void); - -/** - * Creates a USet object that contains the range of characters - * start..end, inclusive. If start > end - * then an empty set is created (same as using uset_openEmpty()). - * @param start first character of the range, inclusive - * @param end last character of the range, inclusive - * @return a newly created USet. The caller must call uset_close() on - * it when done. - * @stable ICU 2.4 - */ -U_CAPI USet* U_EXPORT2 -uset_open(UChar32 start, UChar32 end); - -/** - * Creates a set from the given pattern. See the UnicodeSet class - * description for the syntax of the pattern language. - * @param pattern a string specifying what characters are in the set - * @param patternLength the length of the pattern, or -1 if null - * terminated - * @param ec the error code - * @stable ICU 2.4 - */ -U_CAPI USet* U_EXPORT2 -uset_openPattern(const UChar* pattern, int32_t patternLength, - UErrorCode* ec); - -/** - * Creates a set from the given pattern. See the UnicodeSet class - * description for the syntax of the pattern language. - * @param pattern a string specifying what characters are in the set - * @param patternLength the length of the pattern, or -1 if null - * terminated - * @param options bitmask for options to apply to the pattern. - * Valid options are USET_IGNORE_SPACE and USET_CASE_INSENSITIVE. - * @param ec the error code - * @stable ICU 2.4 - */ -U_CAPI USet* U_EXPORT2 -uset_openPatternOptions(const UChar* pattern, int32_t patternLength, - uint32_t options, - UErrorCode* ec); - -/** - * Disposes of the storage used by a USet object. This function should - * be called exactly once for objects returned by uset_open(). - * @param set the object to dispose of - * @stable ICU 2.4 - */ -U_CAPI void U_EXPORT2 -uset_close(USet* set); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUSetPointer - * "Smart pointer" class, closes a USet via uset_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUSetPointer, USet, uset_close); - -U_NAMESPACE_END - -#endif - -/** - * Returns a copy of this object. - * If this set is frozen, then the clone will be frozen as well. - * Use uset_cloneAsThawed() for a mutable clone of a frozen set. - * @param set the original set - * @return the newly allocated copy of the set - * @see uset_cloneAsThawed - * @stable ICU 3.8 - */ -U_CAPI USet * U_EXPORT2 -uset_clone(const USet *set); - -/** - * Determines whether the set has been frozen (made immutable) or not. - * See the ICU4J Freezable interface for details. - * @param set the set - * @return true/false for whether the set has been frozen - * @see uset_freeze - * @see uset_cloneAsThawed - * @stable ICU 3.8 - */ -U_CAPI UBool U_EXPORT2 -uset_isFrozen(const USet *set); - -/** - * Freeze the set (make it immutable). - * Once frozen, it cannot be unfrozen and is therefore thread-safe - * until it is deleted. - * See the ICU4J Freezable interface for details. - * Freezing the set may also make some operations faster, for example - * uset_contains() and uset_span(). - * A frozen set will not be modified. (It remains frozen.) - * @param set the set - * @return the same set, now frozen - * @see uset_isFrozen - * @see uset_cloneAsThawed - * @stable ICU 3.8 - */ -U_CAPI void U_EXPORT2 -uset_freeze(USet *set); - -/** - * Clone the set and make the clone mutable. - * See the ICU4J Freezable interface for details. - * @param set the set - * @return the mutable clone - * @see uset_freeze - * @see uset_isFrozen - * @see uset_clone - * @stable ICU 3.8 - */ -U_CAPI USet * U_EXPORT2 -uset_cloneAsThawed(const USet *set); - -/** - * Causes the USet object to represent the range start - end. - * If start > end then this USet is set to an empty range. - * A frozen set will not be modified. - * @param set the object to set to the given range - * @param start first character in the set, inclusive - * @param end last character in the set, inclusive - * @stable ICU 3.2 - */ -U_CAPI void U_EXPORT2 -uset_set(USet* set, - UChar32 start, UChar32 end); - -/** - * Modifies the set to represent the set specified by the given - * pattern. See the UnicodeSet class description for the syntax of - * the pattern language. See also the User Guide chapter about UnicodeSet. - * Empties the set passed before applying the pattern. - * A frozen set will not be modified. - * @param set The set to which the pattern is to be applied. - * @param pattern A pointer to UChar string specifying what characters are in the set. - * The character at pattern[0] must be a '['. - * @param patternLength The length of the UChar string. -1 if NUL terminated. - * @param options A bitmask for options to apply to the pattern. - * Valid options are USET_IGNORE_SPACE and USET_CASE_INSENSITIVE. - * @param status Returns an error if the pattern cannot be parsed. - * @return Upon successful parse, the value is either - * the index of the character after the closing ']' - * of the parsed pattern. - * If the status code indicates failure, then the return value - * is the index of the error in the source. - * - * @stable ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -uset_applyPattern(USet *set, - const UChar *pattern, int32_t patternLength, - uint32_t options, - UErrorCode *status); - -/** - * Modifies the set to contain those code points which have the given value - * for the given binary or enumerated property, as returned by - * u_getIntPropertyValue. Prior contents of this set are lost. - * A frozen set will not be modified. - * - * @param set the object to contain the code points defined by the property - * - * @param prop a property in the range UCHAR_BIN_START..UCHAR_BIN_LIMIT-1 - * or UCHAR_INT_START..UCHAR_INT_LIMIT-1 - * or UCHAR_MASK_START..UCHAR_MASK_LIMIT-1. - * - * @param value a value in the range u_getIntPropertyMinValue(prop).. - * u_getIntPropertyMaxValue(prop), with one exception. If prop is - * UCHAR_GENERAL_CATEGORY_MASK, then value should not be a UCharCategory, but - * rather a mask value produced by U_GET_GC_MASK(). This allows grouped - * categories such as [:L:] to be represented. - * - * @param ec error code input/output parameter - * - * @stable ICU 3.2 - */ -U_CAPI void U_EXPORT2 -uset_applyIntPropertyValue(USet* set, - UProperty prop, int32_t value, UErrorCode* ec); - -/** - * Modifies the set to contain those code points which have the - * given value for the given property. Prior contents of this - * set are lost. - * A frozen set will not be modified. - * - * @param set the object to contain the code points defined by the given - * property and value alias - * - * @param prop a string specifying a property alias, either short or long. - * The name is matched loosely. See PropertyAliases.txt for names and a - * description of loose matching. If the value string is empty, then this - * string is interpreted as either a General_Category value alias, a Script - * value alias, a binary property alias, or a special ID. Special IDs are - * matched loosely and correspond to the following sets: - * - * "ANY" = [\\u0000-\\U0010FFFF], - * "ASCII" = [\\u0000-\\u007F], - * "Assigned" = [:^Cn:]. - * - * @param propLength the length of the prop, or -1 if NULL - * - * @param value a string specifying a value alias, either short or long. - * The name is matched loosely. See PropertyValueAliases.txt for names - * and a description of loose matching. In addition to aliases listed, - * numeric values and canonical combining classes may be expressed - * numerically, e.g., ("nv", "0.5") or ("ccc", "220"). The value string - * may also be empty. - * - * @param valueLength the length of the value, or -1 if NULL - * - * @param ec error code input/output parameter - * - * @stable ICU 3.2 - */ -U_CAPI void U_EXPORT2 -uset_applyPropertyAlias(USet* set, - const UChar *prop, int32_t propLength, - const UChar *value, int32_t valueLength, - UErrorCode* ec); - -/** - * Return true if the given position, in the given pattern, appears - * to be the start of a UnicodeSet pattern. - * - * @param pattern a string specifying the pattern - * @param patternLength the length of the pattern, or -1 if NULL - * @param pos the given position - * @stable ICU 3.2 - */ -U_CAPI UBool U_EXPORT2 -uset_resemblesPattern(const UChar *pattern, int32_t patternLength, - int32_t pos); - -/** - * Returns a string representation of this set. If the result of - * calling this function is passed to a uset_openPattern(), it - * will produce another set that is equal to this one. - * @param set the set - * @param result the string to receive the rules, may be NULL - * @param resultCapacity the capacity of result, may be 0 if result is NULL - * @param escapeUnprintable if true then convert unprintable - * character to their hex escape representations, \\uxxxx or - * \\Uxxxxxxxx. Unprintable characters are those other than - * U+000A, U+0020..U+007E. - * @param ec error code. - * @return length of string, possibly larger than resultCapacity - * @stable ICU 2.4 - */ -U_CAPI int32_t U_EXPORT2 -uset_toPattern(const USet* set, - UChar* result, int32_t resultCapacity, - UBool escapeUnprintable, - UErrorCode* ec); - -/** - * Adds the given character to the given USet. After this call, - * uset_contains(set, c) will return true. - * A frozen set will not be modified. - * @param set the object to which to add the character - * @param c the character to add - * @stable ICU 2.4 - */ -U_CAPI void U_EXPORT2 -uset_add(USet* set, UChar32 c); - -/** - * Adds all of the elements in the specified set to this set if - * they're not already present. This operation effectively - * modifies this set so that its value is the union of the two - * sets. The behavior of this operation is unspecified if the specified - * collection is modified while the operation is in progress. - * A frozen set will not be modified. - * - * @param set the object to which to add the set - * @param additionalSet the source set whose elements are to be added to this set. - * @stable ICU 2.6 - */ -U_CAPI void U_EXPORT2 -uset_addAll(USet* set, const USet *additionalSet); - -/** - * Adds the given range of characters to the given USet. After this call, - * uset_contains(set, start, end) will return true. - * A frozen set will not be modified. - * @param set the object to which to add the character - * @param start the first character of the range to add, inclusive - * @param end the last character of the range to add, inclusive - * @stable ICU 2.2 - */ -U_CAPI void U_EXPORT2 -uset_addRange(USet* set, UChar32 start, UChar32 end); - -/** - * Adds the given string to the given USet. After this call, - * uset_containsString(set, str, strLen) will return true. - * A frozen set will not be modified. - * @param set the object to which to add the character - * @param str the string to add - * @param strLen the length of the string or -1 if null terminated. - * @stable ICU 2.4 - */ -U_CAPI void U_EXPORT2 -uset_addString(USet* set, const UChar* str, int32_t strLen); - -/** - * Adds each of the characters in this string to the set. Note: "ch" => {"c", "h"} - * If this set already contains any particular character, it has no effect on that character. - * A frozen set will not be modified. - * @param set the object to which to add the character - * @param str the source string - * @param strLen the length of the string or -1 if null terminated. - * @stable ICU 3.4 - */ -U_CAPI void U_EXPORT2 -uset_addAllCodePoints(USet* set, const UChar *str, int32_t strLen); - -/** - * Removes the given character from the given USet. After this call, - * uset_contains(set, c) will return false. - * A frozen set will not be modified. - * @param set the object from which to remove the character - * @param c the character to remove - * @stable ICU 2.4 - */ -U_CAPI void U_EXPORT2 -uset_remove(USet* set, UChar32 c); - -/** - * Removes the given range of characters from the given USet. After this call, - * uset_contains(set, start, end) will return false. - * A frozen set will not be modified. - * @param set the object to which to add the character - * @param start the first character of the range to remove, inclusive - * @param end the last character of the range to remove, inclusive - * @stable ICU 2.2 - */ -U_CAPI void U_EXPORT2 -uset_removeRange(USet* set, UChar32 start, UChar32 end); - -/** - * Removes the given string to the given USet. After this call, - * uset_containsString(set, str, strLen) will return false. - * A frozen set will not be modified. - * @param set the object to which to add the character - * @param str the string to remove - * @param strLen the length of the string or -1 if null terminated. - * @stable ICU 2.4 - */ -U_CAPI void U_EXPORT2 -uset_removeString(USet* set, const UChar* str, int32_t strLen); - -/** - * Removes EACH of the characters in this string. Note: "ch" == {"c", "h"} - * A frozen set will not be modified. - * - * @param set the object to be modified - * @param str the string - * @param length the length of the string, or -1 if NUL-terminated - * @stable ICU 69 - */ -U_CAPI void U_EXPORT2 -uset_removeAllCodePoints(USet *set, const UChar *str, int32_t length); - -/** - * Removes from this set all of its elements that are contained in the - * specified set. This operation effectively modifies this - * set so that its value is the asymmetric set difference of - * the two sets. - * A frozen set will not be modified. - * @param set the object from which the elements are to be removed - * @param removeSet the object that defines which elements will be - * removed from this set - * @stable ICU 3.2 - */ -U_CAPI void U_EXPORT2 -uset_removeAll(USet* set, const USet* removeSet); - -/** - * Retain only the elements in this set that are contained in the - * specified range. If start > end then an empty range is - * retained, leaving the set empty. This is equivalent to - * a boolean logic AND, or a set INTERSECTION. - * A frozen set will not be modified. - * - * @param set the object for which to retain only the specified range - * @param start first character, inclusive, of range - * @param end last character, inclusive, of range - * @stable ICU 3.2 - */ -U_CAPI void U_EXPORT2 -uset_retain(USet* set, UChar32 start, UChar32 end); - -/** - * Retains only the specified string from this set if it is present. - * Upon return this set will be empty if it did not contain s, or - * will only contain s if it did contain s. - * A frozen set will not be modified. - * - * @param set the object to be modified - * @param str the string - * @param length the length of the string, or -1 if NUL-terminated - * @stable ICU 69 - */ -U_CAPI void U_EXPORT2 -uset_retainString(USet *set, const UChar *str, int32_t length); - -/** - * Retains EACH of the characters in this string. Note: "ch" == {"c", "h"} - * A frozen set will not be modified. - * - * @param set the object to be modified - * @param str the string - * @param length the length of the string, or -1 if NUL-terminated - * @stable ICU 69 - */ -U_CAPI void U_EXPORT2 -uset_retainAllCodePoints(USet *set, const UChar *str, int32_t length); - -/** - * Retains only the elements in this set that are contained in the - * specified set. In other words, removes from this set all of - * its elements that are not contained in the specified set. This - * operation effectively modifies this set so that its value is - * the intersection of the two sets. - * A frozen set will not be modified. - * - * @param set the object on which to perform the retain - * @param retain set that defines which elements this set will retain - * @stable ICU 3.2 - */ -U_CAPI void U_EXPORT2 -uset_retainAll(USet* set, const USet* retain); - -/** - * Reallocate this objects internal structures to take up the least - * possible space, without changing this object's value. - * A frozen set will not be modified. - * - * @param set the object on which to perform the compact - * @stable ICU 3.2 - */ -U_CAPI void U_EXPORT2 -uset_compact(USet* set); - -/** - * This is equivalent to - * uset_complementRange(set, 0, 0x10FFFF). - * - * Note: This performs a symmetric difference with all code points - * and thus retains all multicharacter strings. - * In order to achieve a “code point complement” (all code points minus this set), - * the easiest is to uset_complement(set); uset_removeAllStrings(set);. - * - * A frozen set will not be modified. - * @param set the set - * @stable ICU 2.4 - */ -U_CAPI void U_EXPORT2 -uset_complement(USet* set); - -/** - * Complements the specified range in this set. Any character in - * the range will be removed if it is in this set, or will be - * added if it is not in this set. If start > end - * then an empty range is complemented, leaving the set unchanged. - * This is equivalent to a boolean logic XOR. - * A frozen set will not be modified. - * - * @param set the object to be modified - * @param start first character, inclusive, of range - * @param end last character, inclusive, of range - * @stable ICU 69 - */ -U_CAPI void U_EXPORT2 -uset_complementRange(USet *set, UChar32 start, UChar32 end); - -/** - * Complements the specified string in this set. - * The string will be removed if it is in this set, or will be added if it is not in this set. - * A frozen set will not be modified. - * - * @param set the object to be modified - * @param str the string - * @param length the length of the string, or -1 if NUL-terminated - * @stable ICU 69 - */ -U_CAPI void U_EXPORT2 -uset_complementString(USet *set, const UChar *str, int32_t length); - -/** - * Complements EACH of the characters in this string. Note: "ch" == {"c", "h"} - * A frozen set will not be modified. - * - * @param set the object to be modified - * @param str the string - * @param length the length of the string, or -1 if NUL-terminated - * @stable ICU 69 - */ -U_CAPI void U_EXPORT2 -uset_complementAllCodePoints(USet *set, const UChar *str, int32_t length); - -/** - * Complements in this set all elements contained in the specified - * set. Any character in the other set will be removed if it is - * in this set, or will be added if it is not in this set. - * A frozen set will not be modified. - * - * @param set the set with which to complement - * @param complement set that defines which elements will be xor'ed - * from this set. - * @stable ICU 3.2 - */ -U_CAPI void U_EXPORT2 -uset_complementAll(USet* set, const USet* complement); - -/** - * Removes all of the elements from this set. This set will be - * empty after this call returns. - * A frozen set will not be modified. - * @param set the set - * @stable ICU 2.4 - */ -U_CAPI void U_EXPORT2 -uset_clear(USet* set); - -/** - * Close this set over the given attribute. For the attribute - * USET_CASE, the result is to modify this set so that: - * - * 1. For each character or string 'a' in this set, all strings or - * characters 'b' such that foldCase(a) == foldCase(b) are added - * to this set. - * - * 2. For each string 'e' in the resulting set, if e != - * foldCase(e), 'e' will be removed. - * - * Example: [aq\\u00DF{Bc}{bC}{Fi}] => [aAqQ\\u00DF\\uFB01{ss}{bc}{fi}] - * - * (Here foldCase(x) refers to the operation u_strFoldCase, and a - * == b denotes that the contents are the same, not pointer - * comparison.) - * - * A frozen set will not be modified. - * - * @param set the set - * - * @param attributes bitmask for attributes to close over. - * Currently only the USET_CASE bit is supported. Any undefined bits - * are ignored. - * @stable ICU 4.2 - */ -U_CAPI void U_EXPORT2 -uset_closeOver(USet* set, int32_t attributes); - -/** - * Remove all strings from this set. - * - * @param set the set - * @stable ICU 4.2 - */ -U_CAPI void U_EXPORT2 -uset_removeAllStrings(USet* set); - -/** - * Returns true if the given USet contains no characters and no - * strings. - * @param set the set - * @return true if set is empty - * @stable ICU 2.4 - */ -U_CAPI UBool U_EXPORT2 -uset_isEmpty(const USet* set); - -/** - * @param set the set - * @return true if this set contains multi-character strings or the empty string. - * @stable ICU 70 - */ -U_CAPI UBool U_EXPORT2 -uset_hasStrings(const USet *set); - -/** - * Returns true if the given USet contains the given character. - * This function works faster with a frozen set. - * @param set the set - * @param c The codepoint to check for within the set - * @return true if set contains c - * @stable ICU 2.4 - */ -U_CAPI UBool U_EXPORT2 -uset_contains(const USet* set, UChar32 c); - -/** - * Returns true if the given USet contains all characters c - * where start <= c && c <= end. - * @param set the set - * @param start the first character of the range to test, inclusive - * @param end the last character of the range to test, inclusive - * @return true if set contains the range - * @stable ICU 2.2 - */ -U_CAPI UBool U_EXPORT2 -uset_containsRange(const USet* set, UChar32 start, UChar32 end); - -/** - * Returns true if the given USet contains the given string. - * @param set the set - * @param str the string - * @param strLen the length of the string or -1 if null terminated. - * @return true if set contains str - * @stable ICU 2.4 - */ -U_CAPI UBool U_EXPORT2 -uset_containsString(const USet* set, const UChar* str, int32_t strLen); - -/** - * Returns the index of the given character within this set, where - * the set is ordered by ascending code point. If the character - * is not in this set, return -1. The inverse of this method is - * charAt(). - * @param set the set - * @param c the character to obtain the index for - * @return an index from 0..size()-1, or -1 - * @stable ICU 3.2 - */ -U_CAPI int32_t U_EXPORT2 -uset_indexOf(const USet* set, UChar32 c); - -/** - * Returns the character at the given index within this set, where - * the set is ordered by ascending code point. If the index is - * out of range for characters, returns (UChar32)-1. - * The inverse of this method is indexOf(). - * - * For iteration, this is slower than uset_getRangeCount()/uset_getItemCount() - * with uset_getItem(), because for each call it skips linearly over index - * characters in the ranges. - * - * @param set the set - * @param charIndex an index from 0..size()-1 to obtain the char for - * @return the character at the given index, or (UChar32)-1. - * @stable ICU 3.2 - */ -U_CAPI UChar32 U_EXPORT2 -uset_charAt(const USet* set, int32_t charIndex); - -/** - * Returns the number of characters and strings contained in this set. - * The last (uset_getItemCount() - uset_getRangeCount()) items are strings. - * - * This is slower than uset_getRangeCount() and uset_getItemCount() because - * it counts the code points of all ranges. - * - * @param set the set - * @return a non-negative integer counting the characters and strings - * contained in set - * @stable ICU 2.4 - * @see uset_getRangeCount - */ -U_CAPI int32_t U_EXPORT2 -uset_size(const USet* set); - -/** - * @param set the set - * @return the number of ranges in this set. - * @stable ICU 70 - * @see uset_getItemCount - * @see uset_getItem - * @see uset_size - */ -U_CAPI int32_t U_EXPORT2 -uset_getRangeCount(const USet *set); - -/** - * Returns the number of items in this set. An item is either a range - * of characters or a single multicharacter string. - * @param set the set - * @return a non-negative integer counting the character ranges - * and/or strings contained in set - * @stable ICU 2.4 - */ -U_CAPI int32_t U_EXPORT2 -uset_getItemCount(const USet* set); - -/** - * Returns an item of this set. An item is either a range of - * characters or a single multicharacter string (which can be the empty string). - * - * If itemIndex is less than uset_getRangeCount(), then this function returns 0, - * and the range is *start..*end. - * - * If itemIndex is at least uset_getRangeCount() and less than uset_getItemCount(), then - * this function copies the string into str[strCapacity] and - * returns the length of the string (0 for the empty string). - * - * If itemIndex is out of range, then this function returns -1. - * - * Note that 0 is returned for each range as well as for the empty string. - * - * @param set the set - * @param itemIndex a non-negative integer in the range 0..uset_getItemCount(set)-1 - * @param start pointer to variable to receive first character in range, inclusive; - * can be NULL for a string item - * @param end pointer to variable to receive last character in range, inclusive; - * can be NULL for a string item - * @param str buffer to receive the string, may be NULL - * @param strCapacity capacity of str, or 0 if str is NULL - * @param ec error code; U_INDEX_OUTOFBOUNDS_ERROR if the itemIndex is out of range - * @return the length of the string (0 or >= 2), or 0 if the item is a range, - * or -1 if the itemIndex is out of range - * @stable ICU 2.4 - */ -U_CAPI int32_t U_EXPORT2 -uset_getItem(const USet* set, int32_t itemIndex, - UChar32* start, UChar32* end, - UChar* str, int32_t strCapacity, - UErrorCode* ec); - -/** - * Returns true if set1 contains all the characters and strings - * of set2. It answers the question, 'Is set1 a superset of set2?' - * @param set1 set to be checked for containment - * @param set2 set to be checked for containment - * @return true if the test condition is met - * @stable ICU 3.2 - */ -U_CAPI UBool U_EXPORT2 -uset_containsAll(const USet* set1, const USet* set2); - -/** - * Returns true if this set contains all the characters - * of the given string. This is does not check containment of grapheme - * clusters, like uset_containsString. - * @param set set of characters to be checked for containment - * @param str string containing codepoints to be checked for containment - * @param strLen the length of the string or -1 if null terminated. - * @return true if the test condition is met - * @stable ICU 3.4 - */ -U_CAPI UBool U_EXPORT2 -uset_containsAllCodePoints(const USet* set, const UChar *str, int32_t strLen); - -/** - * Returns true if set1 contains none of the characters and strings - * of set2. It answers the question, 'Is set1 a disjoint set of set2?' - * @param set1 set to be checked for containment - * @param set2 set to be checked for containment - * @return true if the test condition is met - * @stable ICU 3.2 - */ -U_CAPI UBool U_EXPORT2 -uset_containsNone(const USet* set1, const USet* set2); - -/** - * Returns true if set1 contains some of the characters and strings - * of set2. It answers the question, 'Does set1 and set2 have an intersection?' - * @param set1 set to be checked for containment - * @param set2 set to be checked for containment - * @return true if the test condition is met - * @stable ICU 3.2 - */ -U_CAPI UBool U_EXPORT2 -uset_containsSome(const USet* set1, const USet* set2); - -/** - * Returns the length of the initial substring of the input string which - * consists only of characters and strings that are contained in this set - * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), - * or only of characters and strings that are not contained - * in this set (USET_SPAN_NOT_CONTAINED). - * See USetSpanCondition for details. - * Similar to the strspn() C library function. - * Unpaired surrogates are treated according to contains() of their surrogate code points. - * This function works faster with a frozen set and with a non-negative string length argument. - * @param set the set - * @param s start of the string - * @param length of the string; can be -1 for NUL-terminated - * @param spanCondition specifies the containment condition - * @return the length of the initial substring according to the spanCondition; - * 0 if the start of the string does not fit the spanCondition - * @stable ICU 3.8 - * @see USetSpanCondition - */ -U_CAPI int32_t U_EXPORT2 -uset_span(const USet *set, const UChar *s, int32_t length, USetSpanCondition spanCondition); - -/** - * Returns the start of the trailing substring of the input string which - * consists only of characters and strings that are contained in this set - * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), - * or only of characters and strings that are not contained - * in this set (USET_SPAN_NOT_CONTAINED). - * See USetSpanCondition for details. - * Unpaired surrogates are treated according to contains() of their surrogate code points. - * This function works faster with a frozen set and with a non-negative string length argument. - * @param set the set - * @param s start of the string - * @param length of the string; can be -1 for NUL-terminated - * @param spanCondition specifies the containment condition - * @return the start of the trailing substring according to the spanCondition; - * the string length if the end of the string does not fit the spanCondition - * @stable ICU 3.8 - * @see USetSpanCondition - */ -U_CAPI int32_t U_EXPORT2 -uset_spanBack(const USet *set, const UChar *s, int32_t length, USetSpanCondition spanCondition); - -/** - * Returns the length of the initial substring of the input string which - * consists only of characters and strings that are contained in this set - * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), - * or only of characters and strings that are not contained - * in this set (USET_SPAN_NOT_CONTAINED). - * See USetSpanCondition for details. - * Similar to the strspn() C library function. - * Malformed byte sequences are treated according to contains(0xfffd). - * This function works faster with a frozen set and with a non-negative string length argument. - * @param set the set - * @param s start of the string (UTF-8) - * @param length of the string; can be -1 for NUL-terminated - * @param spanCondition specifies the containment condition - * @return the length of the initial substring according to the spanCondition; - * 0 if the start of the string does not fit the spanCondition - * @stable ICU 3.8 - * @see USetSpanCondition - */ -U_CAPI int32_t U_EXPORT2 -uset_spanUTF8(const USet *set, const char *s, int32_t length, USetSpanCondition spanCondition); - -/** - * Returns the start of the trailing substring of the input string which - * consists only of characters and strings that are contained in this set - * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), - * or only of characters and strings that are not contained - * in this set (USET_SPAN_NOT_CONTAINED). - * See USetSpanCondition for details. - * Malformed byte sequences are treated according to contains(0xfffd). - * This function works faster with a frozen set and with a non-negative string length argument. - * @param set the set - * @param s start of the string (UTF-8) - * @param length of the string; can be -1 for NUL-terminated - * @param spanCondition specifies the containment condition - * @return the start of the trailing substring according to the spanCondition; - * the string length if the end of the string does not fit the spanCondition - * @stable ICU 3.8 - * @see USetSpanCondition - */ -U_CAPI int32_t U_EXPORT2 -uset_spanBackUTF8(const USet *set, const char *s, int32_t length, USetSpanCondition spanCondition); - -/** - * Returns true if set1 contains all of the characters and strings - * of set2, and vis versa. It answers the question, 'Is set1 equal to set2?' - * @param set1 set to be checked for containment - * @param set2 set to be checked for containment - * @return true if the test condition is met - * @stable ICU 3.2 - */ -U_CAPI UBool U_EXPORT2 -uset_equals(const USet* set1, const USet* set2); - -/********************************************************************* - * Serialized set API - *********************************************************************/ - -/** - * Serializes this set into an array of 16-bit integers. Serialization - * (currently) only records the characters in the set; multicharacter - * strings are ignored. - * - * The array - * has following format (each line is one 16-bit integer): - * - * length = (n+2*m) | (m!=0?0x8000:0) - * bmpLength = n; present if m!=0 - * bmp[0] - * bmp[1] - * ... - * bmp[n-1] - * supp-high[0] - * supp-low[0] - * supp-high[1] - * supp-low[1] - * ... - * supp-high[m-1] - * supp-low[m-1] - * - * The array starts with a header. After the header are n bmp - * code points, then m supplementary code points. Either n or m - * or both may be zero. n+2*m is always <= 0x7FFF. - * - * If there are no supplementary characters (if m==0) then the - * header is one 16-bit integer, 'length', with value n. - * - * If there are supplementary characters (if m!=0) then the header - * is two 16-bit integers. The first, 'length', has value - * (n+2*m)|0x8000. The second, 'bmpLength', has value n. - * - * After the header the code points are stored in ascending order. - * Supplementary code points are stored as most significant 16 - * bits followed by least significant 16 bits. - * - * @param set the set - * @param dest pointer to buffer of destCapacity 16-bit integers. - * May be NULL only if destCapacity is zero. - * @param destCapacity size of dest, or zero. Must not be negative. - * @param pErrorCode pointer to the error code. Will be set to - * U_INDEX_OUTOFBOUNDS_ERROR if n+2*m > 0x7FFF. Will be set to - * U_BUFFER_OVERFLOW_ERROR if n+2*m+(m!=0?2:1) > destCapacity. - * @return the total length of the serialized format, including - * the header, that is, n+2*m+(m!=0?2:1), or 0 on error other - * than U_BUFFER_OVERFLOW_ERROR. - * @stable ICU 2.4 - */ -U_CAPI int32_t U_EXPORT2 -uset_serialize(const USet* set, uint16_t* dest, int32_t destCapacity, UErrorCode* pErrorCode); - -/** - * Given a serialized array, fill in the given serialized set object. - * @param fillSet pointer to result - * @param src pointer to start of array - * @param srcLength length of array - * @return true if the given array is valid, otherwise false - * @stable ICU 2.4 - */ -U_CAPI UBool U_EXPORT2 -uset_getSerializedSet(USerializedSet* fillSet, const uint16_t* src, int32_t srcLength); - -/** - * Set the USerializedSet to contain the given character (and nothing - * else). - * @param fillSet pointer to result - * @param c The codepoint to set - * @stable ICU 2.4 - */ -U_CAPI void U_EXPORT2 -uset_setSerializedToOne(USerializedSet* fillSet, UChar32 c); - -/** - * Returns true if the given USerializedSet contains the given - * character. - * @param set the serialized set - * @param c The codepoint to check for within the set - * @return true if set contains c - * @stable ICU 2.4 - */ -U_CAPI UBool U_EXPORT2 -uset_serializedContains(const USerializedSet* set, UChar32 c); - -/** - * Returns the number of disjoint ranges of characters contained in - * the given serialized set. Ignores any strings contained in the - * set. - * @param set the serialized set - * @return a non-negative integer counting the character ranges - * contained in set - * @stable ICU 2.4 - */ -U_CAPI int32_t U_EXPORT2 -uset_getSerializedRangeCount(const USerializedSet* set); - -/** - * Returns a range of characters contained in the given serialized - * set. - * @param set the serialized set - * @param rangeIndex a non-negative integer in the range 0.. - * uset_getSerializedRangeCount(set)-1 - * @param pStart pointer to variable to receive first character - * in range, inclusive - * @param pEnd pointer to variable to receive last character in range, - * inclusive - * @return true if rangeIndex is valid, otherwise false - * @stable ICU 2.4 - */ -U_CAPI UBool U_EXPORT2 -uset_getSerializedRange(const USerializedSet* set, int32_t rangeIndex, - UChar32* pStart, UChar32* pEnd); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uset.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002mar07 +* created by: Markus W. Scherer +* +* C version of UnicodeSet. +*/ + + +/** + * \file + * \brief C API: Unicode Set + * + *

This is a C wrapper around the C++ UnicodeSet class.

+ */ + +#ifndef __USET_H__ +#define __USET_H__ + +#include "unicode/utypes.h" +#include "unicode/uchar.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +#ifndef USET_DEFINED + +#ifndef U_IN_DOXYGEN +#define USET_DEFINED +#endif +/** + * USet is the C API type corresponding to C++ class UnicodeSet. + * Use the uset_* API to manipulate. Create with + * uset_open*, and destroy with uset_close. + * @stable ICU 2.4 + */ +typedef struct USet USet; +#endif + +/** + * Bitmask values to be passed to uset_openPatternOptions() or + * uset_applyPattern() taking an option parameter. + * + * Use at most one of USET_CASE_INSENSITIVE, USET_ADD_CASE_MAPPINGS, USET_SIMPLE_CASE_INSENSITIVE. + * These case options are mutually exclusive. + * + * Undefined options bits are ignored, and reserved for future use. + * + * @stable ICU 2.4 + */ +enum { + /** + * Ignore white space within patterns unless quoted or escaped. + * @stable ICU 2.4 + */ + USET_IGNORE_SPACE = 1, + + /** + * Enable case insensitive matching. E.g., "[ab]" with this flag + * will match 'a', 'A', 'b', and 'B'. "[^ab]" with this flag will + * match all except 'a', 'A', 'b', and 'B'. This performs a full + * closure over case mappings, e.g. 'ſ' (U+017F long s) for 's'. + * + * The resulting set is a superset of the input for the code points but + * not for the strings. + * It performs a case mapping closure of the code points and adds + * full case folding strings for the code points, and reduces strings of + * the original set to their full case folding equivalents. + * + * This is designed for case-insensitive matches, for example + * in regular expressions. The full code point case closure allows checking of + * an input character directly against the closure set. + * Strings are matched by comparing the case-folded form from the closure + * set with an incremental case folding of the string in question. + * + * The closure set will also contain single code points if the original + * set contained case-equivalent strings (like U+00DF for "ss" or "Ss" etc.). + * This is not necessary (that is, redundant) for the above matching method + * but results in the same closure sets regardless of whether the original + * set contained the code point or a string. + * + * @stable ICU 2.4 + */ + USET_CASE_INSENSITIVE = 2, + + /** + * Adds all case mappings for each element in the set. + * This adds the full lower-, title-, and uppercase mappings as well as the full case folding + * of each existing element in the set. + * + * Unlike the “case insensitive” options, this does not perform a closure. + * For example, it does not add 'ſ' (U+017F long s) for 's', + * 'K' (U+212A Kelvin sign) for 'k', or replace set strings by their case-folded versions. + * + * @stable ICU 3.2 + */ + USET_ADD_CASE_MAPPINGS = 4, + +#ifndef U_HIDE_DRAFT_API + /** + * Enable case insensitive matching. + * Same as USET_CASE_INSENSITIVE but using only Simple_Case_Folding (scf) mappings, + * which map each code point to one code point, + * not full Case_Folding (cf) mappings, which map some code points to multiple code points. + * + * This is designed for case-insensitive matches, for example in certain + * regular expression implementations where only Simple_Case_Folding mappings are used, + * such as in ECMAScript (JavaScript) regular expressions. + * + * @draft ICU 73 + */ + USET_SIMPLE_CASE_INSENSITIVE = 6 +#endif // U_HIDE_DRAFT_API +}; + +/** + * Argument values for whether span() and similar functions continue while + * the current character is contained vs. not contained in the set. + * + * The functionality is straightforward for sets with only single code points, + * without strings (which is the common case): + * - USET_SPAN_CONTAINED and USET_SPAN_SIMPLE work the same. + * - USET_SPAN_CONTAINED and USET_SPAN_SIMPLE are inverses of USET_SPAN_NOT_CONTAINED. + * - span() and spanBack() partition any string the same way when + * alternating between span(USET_SPAN_NOT_CONTAINED) and + * span(either "contained" condition). + * - Using a complemented (inverted) set and the opposite span conditions + * yields the same results. + * + * When a set contains multi-code point strings, then these statements may not + * be true, depending on the strings in the set (for example, whether they + * overlap with each other) and the string that is processed. + * For a set with strings: + * - The complement of the set contains the opposite set of code points, + * but the same set of strings. + * Therefore, complementing both the set and the span conditions + * may yield different results. + * - When starting spans at different positions in a string + * (span(s, ...) vs. span(s+1, ...)) the ends of the spans may be different + * because a set string may start before the later position. + * - span(USET_SPAN_SIMPLE) may be shorter than + * span(USET_SPAN_CONTAINED) because it will not recursively try + * all possible paths. + * For example, with a set which contains the three strings "xy", "xya" and "ax", + * span("xyax", USET_SPAN_CONTAINED) will return 4 but + * span("xyax", USET_SPAN_SIMPLE) will return 3. + * span(USET_SPAN_SIMPLE) will never be longer than + * span(USET_SPAN_CONTAINED). + * - With either "contained" condition, span() and spanBack() may partition + * a string in different ways. + * For example, with a set which contains the two strings "ab" and "ba", + * and when processing the string "aba", + * span() will yield contained/not-contained boundaries of { 0, 2, 3 } + * while spanBack() will yield boundaries of { 0, 1, 3 }. + * + * Note: If it is important to get the same boundaries whether iterating forward + * or backward through a string, then either only span() should be used and + * the boundaries cached for backward operation, or an ICU BreakIterator + * could be used. + * + * Note: Unpaired surrogates are treated like surrogate code points. + * Similarly, set strings match only on code point boundaries, + * never in the middle of a surrogate pair. + * Illegal UTF-8 sequences are treated like U+FFFD. + * When processing UTF-8 strings, malformed set strings + * (strings with unpaired surrogates which cannot be converted to UTF-8) + * are ignored. + * + * @stable ICU 3.8 + */ +typedef enum USetSpanCondition { + /** + * Continues a span() while there is no set element at the current position. + * Increments by one code point at a time. + * Stops before the first set element (character or string). + * (For code points only, this is like while contains(current)==false). + * + * When span() returns, the substring between where it started and the position + * it returned consists only of characters that are not in the set, + * and none of its strings overlap with the span. + * + * @stable ICU 3.8 + */ + USET_SPAN_NOT_CONTAINED = 0, + /** + * Spans the longest substring that is a concatenation of set elements (characters or strings). + * (For characters only, this is like while contains(current)==true). + * + * When span() returns, the substring between where it started and the position + * it returned consists only of set elements (characters or strings) that are in the set. + * + * If a set contains strings, then the span will be the longest substring for which there + * exists at least one non-overlapping concatenation of set elements (characters or strings). + * This is equivalent to a POSIX regular expression for (OR of each set element)*. + * (Java/ICU/Perl regex stops at the first match of an OR.) + * + * @stable ICU 3.8 + */ + USET_SPAN_CONTAINED = 1, + /** + * Continues a span() while there is a set element at the current position. + * Increments by the longest matching element at each position. + * (For characters only, this is like while contains(current)==true). + * + * When span() returns, the substring between where it started and the position + * it returned consists only of set elements (characters or strings) that are in the set. + * + * If a set only contains single characters, then this is the same + * as USET_SPAN_CONTAINED. + * + * If a set contains strings, then the span will be the longest substring + * with a match at each position with the longest single set element (character or string). + * + * Use this span condition together with other longest-match algorithms, + * such as ICU converters (ucnv_getUnicodeSet()). + * + * @stable ICU 3.8 + */ + USET_SPAN_SIMPLE = 2, +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the last span condition. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + USET_SPAN_CONDITION_COUNT +#endif // U_HIDE_DEPRECATED_API +} USetSpanCondition; + +enum { + /** + * Capacity of USerializedSet::staticArray. + * Enough for any single-code point set. + * Also provides padding for nice sizeof(USerializedSet). + * @stable ICU 2.4 + */ + USET_SERIALIZED_STATIC_ARRAY_CAPACITY=8 +}; + +/** + * A serialized form of a Unicode set. Limited manipulations are + * possible directly on a serialized set. See below. + * @stable ICU 2.4 + */ +typedef struct USerializedSet { + /** + * The serialized Unicode Set. + * @stable ICU 2.4 + */ + const uint16_t *array; + /** + * The length of the array that contains BMP characters. + * @stable ICU 2.4 + */ + int32_t bmpLength; + /** + * The total length of the array. + * @stable ICU 2.4 + */ + int32_t length; + /** + * A small buffer for the array to reduce memory allocations. + * @stable ICU 2.4 + */ + uint16_t staticArray[USET_SERIALIZED_STATIC_ARRAY_CAPACITY]; +} USerializedSet; + +/********************************************************************* + * USet API + *********************************************************************/ + +/** + * Create an empty USet object. + * Equivalent to uset_open(1, 0). + * @return a newly created USet. The caller must call uset_close() on + * it when done. + * @stable ICU 4.2 + */ +U_CAPI USet* U_EXPORT2 +uset_openEmpty(void); + +/** + * Creates a USet object that contains the range of characters + * start..end, inclusive. If start > end + * then an empty set is created (same as using uset_openEmpty()). + * @param start first character of the range, inclusive + * @param end last character of the range, inclusive + * @return a newly created USet. The caller must call uset_close() on + * it when done. + * @stable ICU 2.4 + */ +U_CAPI USet* U_EXPORT2 +uset_open(UChar32 start, UChar32 end); + +/** + * Creates a set from the given pattern. See the UnicodeSet class + * description for the syntax of the pattern language. + * @param pattern a string specifying what characters are in the set + * @param patternLength the length of the pattern, or -1 if null + * terminated + * @param ec the error code + * @stable ICU 2.4 + */ +U_CAPI USet* U_EXPORT2 +uset_openPattern(const UChar* pattern, int32_t patternLength, + UErrorCode* ec); + +/** + * Creates a set from the given pattern. See the UnicodeSet class + * description for the syntax of the pattern language. + * @param pattern a string specifying what characters are in the set + * @param patternLength the length of the pattern, or -1 if null + * terminated + * @param options bitmask for options to apply to the pattern. + * Valid options are USET_IGNORE_SPACE and + * at most one of USET_CASE_INSENSITIVE, USET_ADD_CASE_MAPPINGS, USET_SIMPLE_CASE_INSENSITIVE. + * These case options are mutually exclusive. + * @param ec the error code + * @stable ICU 2.4 + */ +U_CAPI USet* U_EXPORT2 +uset_openPatternOptions(const UChar* pattern, int32_t patternLength, + uint32_t options, + UErrorCode* ec); + +/** + * Disposes of the storage used by a USet object. This function should + * be called exactly once for objects returned by uset_open(). + * @param set the object to dispose of + * @stable ICU 2.4 + */ +U_CAPI void U_EXPORT2 +uset_close(USet* set); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUSetPointer + * "Smart pointer" class, closes a USet via uset_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUSetPointer, USet, uset_close); + +U_NAMESPACE_END + +#endif + +/** + * Returns a copy of this object. + * If this set is frozen, then the clone will be frozen as well. + * Use uset_cloneAsThawed() for a mutable clone of a frozen set. + * @param set the original set + * @return the newly allocated copy of the set + * @see uset_cloneAsThawed + * @stable ICU 3.8 + */ +U_CAPI USet * U_EXPORT2 +uset_clone(const USet *set); + +/** + * Determines whether the set has been frozen (made immutable) or not. + * See the ICU4J Freezable interface for details. + * @param set the set + * @return true/false for whether the set has been frozen + * @see uset_freeze + * @see uset_cloneAsThawed + * @stable ICU 3.8 + */ +U_CAPI UBool U_EXPORT2 +uset_isFrozen(const USet *set); + +/** + * Freeze the set (make it immutable). + * Once frozen, it cannot be unfrozen and is therefore thread-safe + * until it is deleted. + * See the ICU4J Freezable interface for details. + * Freezing the set may also make some operations faster, for example + * uset_contains() and uset_span(). + * A frozen set will not be modified. (It remains frozen.) + * @param set the set + * @return the same set, now frozen + * @see uset_isFrozen + * @see uset_cloneAsThawed + * @stable ICU 3.8 + */ +U_CAPI void U_EXPORT2 +uset_freeze(USet *set); + +/** + * Clone the set and make the clone mutable. + * See the ICU4J Freezable interface for details. + * @param set the set + * @return the mutable clone + * @see uset_freeze + * @see uset_isFrozen + * @see uset_clone + * @stable ICU 3.8 + */ +U_CAPI USet * U_EXPORT2 +uset_cloneAsThawed(const USet *set); + +/** + * Causes the USet object to represent the range start - end. + * If start > end then this USet is set to an empty range. + * A frozen set will not be modified. + * @param set the object to set to the given range + * @param start first character in the set, inclusive + * @param end last character in the set, inclusive + * @stable ICU 3.2 + */ +U_CAPI void U_EXPORT2 +uset_set(USet* set, + UChar32 start, UChar32 end); + +/** + * Modifies the set to represent the set specified by the given + * pattern. See the UnicodeSet class description for the syntax of + * the pattern language. See also the User Guide chapter about UnicodeSet. + * Empties the set passed before applying the pattern. + * A frozen set will not be modified. + * @param set The set to which the pattern is to be applied. + * @param pattern A pointer to UChar string specifying what characters are in the set. + * The character at pattern[0] must be a '['. + * @param patternLength The length of the UChar string. -1 if NUL terminated. + * @param options A bitmask for options to apply to the pattern. + * Valid options are USET_IGNORE_SPACE and + * at most one of USET_CASE_INSENSITIVE, USET_ADD_CASE_MAPPINGS, + * USET_SIMPLE_CASE_INSENSITIVE. + * These case options are mutually exclusive. + * @param status Returns an error if the pattern cannot be parsed. + * @return Upon successful parse, the value is either + * the index of the character after the closing ']' + * of the parsed pattern. + * If the status code indicates failure, then the return value + * is the index of the error in the source. + * + * @stable ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +uset_applyPattern(USet *set, + const UChar *pattern, int32_t patternLength, + uint32_t options, + UErrorCode *status); + +/** + * Modifies the set to contain those code points which have the given value + * for the given binary or enumerated property, as returned by + * u_getIntPropertyValue. Prior contents of this set are lost. + * A frozen set will not be modified. + * + * @param set the object to contain the code points defined by the property + * + * @param prop a property in the range UCHAR_BIN_START..UCHAR_BIN_LIMIT-1 + * or UCHAR_INT_START..UCHAR_INT_LIMIT-1 + * or UCHAR_MASK_START..UCHAR_MASK_LIMIT-1. + * + * @param value a value in the range u_getIntPropertyMinValue(prop).. + * u_getIntPropertyMaxValue(prop), with one exception. If prop is + * UCHAR_GENERAL_CATEGORY_MASK, then value should not be a UCharCategory, but + * rather a mask value produced by U_GET_GC_MASK(). This allows grouped + * categories such as [:L:] to be represented. + * + * @param ec error code input/output parameter + * + * @stable ICU 3.2 + */ +U_CAPI void U_EXPORT2 +uset_applyIntPropertyValue(USet* set, + UProperty prop, int32_t value, UErrorCode* ec); + +/** + * Modifies the set to contain those code points which have the + * given value for the given property. Prior contents of this + * set are lost. + * A frozen set will not be modified. + * + * @param set the object to contain the code points defined by the given + * property and value alias + * + * @param prop a string specifying a property alias, either short or long. + * The name is matched loosely. See PropertyAliases.txt for names and a + * description of loose matching. If the value string is empty, then this + * string is interpreted as either a General_Category value alias, a Script + * value alias, a binary property alias, or a special ID. Special IDs are + * matched loosely and correspond to the following sets: + * + * "ANY" = [\\u0000-\\U0010FFFF], + * "ASCII" = [\\u0000-\\u007F], + * "Assigned" = [:^Cn:]. + * + * @param propLength the length of the prop, or -1 if NULL + * + * @param value a string specifying a value alias, either short or long. + * The name is matched loosely. See PropertyValueAliases.txt for names + * and a description of loose matching. In addition to aliases listed, + * numeric values and canonical combining classes may be expressed + * numerically, e.g., ("nv", "0.5") or ("ccc", "220"). The value string + * may also be empty. + * + * @param valueLength the length of the value, or -1 if NULL + * + * @param ec error code input/output parameter + * + * @stable ICU 3.2 + */ +U_CAPI void U_EXPORT2 +uset_applyPropertyAlias(USet* set, + const UChar *prop, int32_t propLength, + const UChar *value, int32_t valueLength, + UErrorCode* ec); + +/** + * Return true if the given position, in the given pattern, appears + * to be the start of a UnicodeSet pattern. + * + * @param pattern a string specifying the pattern + * @param patternLength the length of the pattern, or -1 if NULL + * @param pos the given position + * @stable ICU 3.2 + */ +U_CAPI UBool U_EXPORT2 +uset_resemblesPattern(const UChar *pattern, int32_t patternLength, + int32_t pos); + +/** + * Returns a string representation of this set. If the result of + * calling this function is passed to a uset_openPattern(), it + * will produce another set that is equal to this one. + * @param set the set + * @param result the string to receive the rules, may be NULL + * @param resultCapacity the capacity of result, may be 0 if result is NULL + * @param escapeUnprintable if true then convert unprintable + * character to their hex escape representations, \\uxxxx or + * \\Uxxxxxxxx. Unprintable characters are those other than + * U+000A, U+0020..U+007E. + * @param ec error code. + * @return length of string, possibly larger than resultCapacity + * @stable ICU 2.4 + */ +U_CAPI int32_t U_EXPORT2 +uset_toPattern(const USet* set, + UChar* result, int32_t resultCapacity, + UBool escapeUnprintable, + UErrorCode* ec); + +/** + * Adds the given character to the given USet. After this call, + * uset_contains(set, c) will return true. + * A frozen set will not be modified. + * @param set the object to which to add the character + * @param c the character to add + * @stable ICU 2.4 + */ +U_CAPI void U_EXPORT2 +uset_add(USet* set, UChar32 c); + +/** + * Adds all of the elements in the specified set to this set if + * they're not already present. This operation effectively + * modifies this set so that its value is the union of the two + * sets. The behavior of this operation is unspecified if the specified + * collection is modified while the operation is in progress. + * A frozen set will not be modified. + * + * @param set the object to which to add the set + * @param additionalSet the source set whose elements are to be added to this set. + * @stable ICU 2.6 + */ +U_CAPI void U_EXPORT2 +uset_addAll(USet* set, const USet *additionalSet); + +/** + * Adds the given range of characters to the given USet. After this call, + * uset_contains(set, start, end) will return true. + * A frozen set will not be modified. + * @param set the object to which to add the character + * @param start the first character of the range to add, inclusive + * @param end the last character of the range to add, inclusive + * @stable ICU 2.2 + */ +U_CAPI void U_EXPORT2 +uset_addRange(USet* set, UChar32 start, UChar32 end); + +/** + * Adds the given string to the given USet. After this call, + * uset_containsString(set, str, strLen) will return true. + * A frozen set will not be modified. + * @param set the object to which to add the character + * @param str the string to add + * @param strLen the length of the string or -1 if null terminated. + * @stable ICU 2.4 + */ +U_CAPI void U_EXPORT2 +uset_addString(USet* set, const UChar* str, int32_t strLen); + +/** + * Adds each of the characters in this string to the set. Note: "ch" => {"c", "h"} + * If this set already contains any particular character, it has no effect on that character. + * A frozen set will not be modified. + * @param set the object to which to add the character + * @param str the source string + * @param strLen the length of the string or -1 if null terminated. + * @stable ICU 3.4 + */ +U_CAPI void U_EXPORT2 +uset_addAllCodePoints(USet* set, const UChar *str, int32_t strLen); + +/** + * Removes the given character from the given USet. After this call, + * uset_contains(set, c) will return false. + * A frozen set will not be modified. + * @param set the object from which to remove the character + * @param c the character to remove + * @stable ICU 2.4 + */ +U_CAPI void U_EXPORT2 +uset_remove(USet* set, UChar32 c); + +/** + * Removes the given range of characters from the given USet. After this call, + * uset_contains(set, start, end) will return false. + * A frozen set will not be modified. + * @param set the object to which to add the character + * @param start the first character of the range to remove, inclusive + * @param end the last character of the range to remove, inclusive + * @stable ICU 2.2 + */ +U_CAPI void U_EXPORT2 +uset_removeRange(USet* set, UChar32 start, UChar32 end); + +/** + * Removes the given string to the given USet. After this call, + * uset_containsString(set, str, strLen) will return false. + * A frozen set will not be modified. + * @param set the object to which to add the character + * @param str the string to remove + * @param strLen the length of the string or -1 if null terminated. + * @stable ICU 2.4 + */ +U_CAPI void U_EXPORT2 +uset_removeString(USet* set, const UChar* str, int32_t strLen); + +/** + * Removes EACH of the characters in this string. Note: "ch" == {"c", "h"} + * A frozen set will not be modified. + * + * @param set the object to be modified + * @param str the string + * @param length the length of the string, or -1 if NUL-terminated + * @stable ICU 69 + */ +U_CAPI void U_EXPORT2 +uset_removeAllCodePoints(USet *set, const UChar *str, int32_t length); + +/** + * Removes from this set all of its elements that are contained in the + * specified set. This operation effectively modifies this + * set so that its value is the asymmetric set difference of + * the two sets. + * A frozen set will not be modified. + * @param set the object from which the elements are to be removed + * @param removeSet the object that defines which elements will be + * removed from this set + * @stable ICU 3.2 + */ +U_CAPI void U_EXPORT2 +uset_removeAll(USet* set, const USet* removeSet); + +/** + * Retain only the elements in this set that are contained in the + * specified range. If start > end then an empty range is + * retained, leaving the set empty. This is equivalent to + * a boolean logic AND, or a set INTERSECTION. + * A frozen set will not be modified. + * + * @param set the object for which to retain only the specified range + * @param start first character, inclusive, of range + * @param end last character, inclusive, of range + * @stable ICU 3.2 + */ +U_CAPI void U_EXPORT2 +uset_retain(USet* set, UChar32 start, UChar32 end); + +/** + * Retains only the specified string from this set if it is present. + * Upon return this set will be empty if it did not contain s, or + * will only contain s if it did contain s. + * A frozen set will not be modified. + * + * @param set the object to be modified + * @param str the string + * @param length the length of the string, or -1 if NUL-terminated + * @stable ICU 69 + */ +U_CAPI void U_EXPORT2 +uset_retainString(USet *set, const UChar *str, int32_t length); + +/** + * Retains EACH of the characters in this string. Note: "ch" == {"c", "h"} + * A frozen set will not be modified. + * + * @param set the object to be modified + * @param str the string + * @param length the length of the string, or -1 if NUL-terminated + * @stable ICU 69 + */ +U_CAPI void U_EXPORT2 +uset_retainAllCodePoints(USet *set, const UChar *str, int32_t length); + +/** + * Retains only the elements in this set that are contained in the + * specified set. In other words, removes from this set all of + * its elements that are not contained in the specified set. This + * operation effectively modifies this set so that its value is + * the intersection of the two sets. + * A frozen set will not be modified. + * + * @param set the object on which to perform the retain + * @param retain set that defines which elements this set will retain + * @stable ICU 3.2 + */ +U_CAPI void U_EXPORT2 +uset_retainAll(USet* set, const USet* retain); + +/** + * Reallocate this objects internal structures to take up the least + * possible space, without changing this object's value. + * A frozen set will not be modified. + * + * @param set the object on which to perform the compact + * @stable ICU 3.2 + */ +U_CAPI void U_EXPORT2 +uset_compact(USet* set); + +/** + * This is equivalent to + * uset_complementRange(set, 0, 0x10FFFF). + * + * Note: This performs a symmetric difference with all code points + * and thus retains all multicharacter strings. + * In order to achieve a “code point complement” (all code points minus this set), + * the easiest is to uset_complement(set); uset_removeAllStrings(set);. + * + * A frozen set will not be modified. + * @param set the set + * @stable ICU 2.4 + */ +U_CAPI void U_EXPORT2 +uset_complement(USet* set); + +/** + * Complements the specified range in this set. Any character in + * the range will be removed if it is in this set, or will be + * added if it is not in this set. If start > end + * then an empty range is complemented, leaving the set unchanged. + * This is equivalent to a boolean logic XOR. + * A frozen set will not be modified. + * + * @param set the object to be modified + * @param start first character, inclusive, of range + * @param end last character, inclusive, of range + * @stable ICU 69 + */ +U_CAPI void U_EXPORT2 +uset_complementRange(USet *set, UChar32 start, UChar32 end); + +/** + * Complements the specified string in this set. + * The string will be removed if it is in this set, or will be added if it is not in this set. + * A frozen set will not be modified. + * + * @param set the object to be modified + * @param str the string + * @param length the length of the string, or -1 if NUL-terminated + * @stable ICU 69 + */ +U_CAPI void U_EXPORT2 +uset_complementString(USet *set, const UChar *str, int32_t length); + +/** + * Complements EACH of the characters in this string. Note: "ch" == {"c", "h"} + * A frozen set will not be modified. + * + * @param set the object to be modified + * @param str the string + * @param length the length of the string, or -1 if NUL-terminated + * @stable ICU 69 + */ +U_CAPI void U_EXPORT2 +uset_complementAllCodePoints(USet *set, const UChar *str, int32_t length); + +/** + * Complements in this set all elements contained in the specified + * set. Any character in the other set will be removed if it is + * in this set, or will be added if it is not in this set. + * A frozen set will not be modified. + * + * @param set the set with which to complement + * @param complement set that defines which elements will be xor'ed + * from this set. + * @stable ICU 3.2 + */ +U_CAPI void U_EXPORT2 +uset_complementAll(USet* set, const USet* complement); + +/** + * Removes all of the elements from this set. This set will be + * empty after this call returns. + * A frozen set will not be modified. + * @param set the set + * @stable ICU 2.4 + */ +U_CAPI void U_EXPORT2 +uset_clear(USet* set); + +/** + * Close this set over the given attribute. For the attribute + * USET_CASE_INSENSITIVE, the result is to modify this set so that: + * + * 1. For each character or string 'a' in this set, all strings or + * characters 'b' such that foldCase(a) == foldCase(b) are added + * to this set. + * + * 2. For each string 'e' in the resulting set, if e != + * foldCase(e), 'e' will be removed. + * + * Example: [aq\\u00DF{Bc}{bC}{Fi}] => [aAqQ\\u00DF\\uFB01{ss}{bc}{fi}] + * + * (Here foldCase(x) refers to the operation u_strFoldCase, and a + * == b denotes that the contents are the same, not pointer + * comparison.) + * + * A frozen set will not be modified. + * + * @param set the set + * + * @param attributes bitmask for attributes to close over. + * Valid options: + * At most one of USET_CASE_INSENSITIVE, USET_ADD_CASE_MAPPINGS, USET_SIMPLE_CASE_INSENSITIVE. + * These case options are mutually exclusive. + * Unrelated options bits are ignored. + * @stable ICU 4.2 + */ +U_CAPI void U_EXPORT2 +uset_closeOver(USet* set, int32_t attributes); + +/** + * Remove all strings from this set. + * + * @param set the set + * @stable ICU 4.2 + */ +U_CAPI void U_EXPORT2 +uset_removeAllStrings(USet* set); + +/** + * Returns true if the given USet contains no characters and no + * strings. + * @param set the set + * @return true if set is empty + * @stable ICU 2.4 + */ +U_CAPI UBool U_EXPORT2 +uset_isEmpty(const USet* set); + +/** + * @param set the set + * @return true if this set contains multi-character strings or the empty string. + * @stable ICU 70 + */ +U_CAPI UBool U_EXPORT2 +uset_hasStrings(const USet *set); + +/** + * Returns true if the given USet contains the given character. + * This function works faster with a frozen set. + * @param set the set + * @param c The codepoint to check for within the set + * @return true if set contains c + * @stable ICU 2.4 + */ +U_CAPI UBool U_EXPORT2 +uset_contains(const USet* set, UChar32 c); + +/** + * Returns true if the given USet contains all characters c + * where start <= c && c <= end. + * @param set the set + * @param start the first character of the range to test, inclusive + * @param end the last character of the range to test, inclusive + * @return true if set contains the range + * @stable ICU 2.2 + */ +U_CAPI UBool U_EXPORT2 +uset_containsRange(const USet* set, UChar32 start, UChar32 end); + +/** + * Returns true if the given USet contains the given string. + * @param set the set + * @param str the string + * @param strLen the length of the string or -1 if null terminated. + * @return true if set contains str + * @stable ICU 2.4 + */ +U_CAPI UBool U_EXPORT2 +uset_containsString(const USet* set, const UChar* str, int32_t strLen); + +/** + * Returns the index of the given character within this set, where + * the set is ordered by ascending code point. If the character + * is not in this set, return -1. The inverse of this method is + * charAt(). + * @param set the set + * @param c the character to obtain the index for + * @return an index from 0..size()-1, or -1 + * @stable ICU 3.2 + */ +U_CAPI int32_t U_EXPORT2 +uset_indexOf(const USet* set, UChar32 c); + +/** + * Returns the character at the given index within this set, where + * the set is ordered by ascending code point. If the index is + * out of range for characters, returns (UChar32)-1. + * The inverse of this method is indexOf(). + * + * For iteration, this is slower than uset_getRangeCount()/uset_getItemCount() + * with uset_getItem(), because for each call it skips linearly over index + * characters in the ranges. + * + * @param set the set + * @param charIndex an index from 0..size()-1 to obtain the char for + * @return the character at the given index, or (UChar32)-1. + * @stable ICU 3.2 + */ +U_CAPI UChar32 U_EXPORT2 +uset_charAt(const USet* set, int32_t charIndex); + +/** + * Returns the number of characters and strings contained in this set. + * The last (uset_getItemCount() - uset_getRangeCount()) items are strings. + * + * This is slower than uset_getRangeCount() and uset_getItemCount() because + * it counts the code points of all ranges. + * + * @param set the set + * @return a non-negative integer counting the characters and strings + * contained in set + * @stable ICU 2.4 + * @see uset_getRangeCount + */ +U_CAPI int32_t U_EXPORT2 +uset_size(const USet* set); + +/** + * @param set the set + * @return the number of ranges in this set. + * @stable ICU 70 + * @see uset_getItemCount + * @see uset_getItem + * @see uset_size + */ +U_CAPI int32_t U_EXPORT2 +uset_getRangeCount(const USet *set); + +/** + * Returns the number of items in this set. An item is either a range + * of characters or a single multicharacter string. + * @param set the set + * @return a non-negative integer counting the character ranges + * and/or strings contained in set + * @stable ICU 2.4 + */ +U_CAPI int32_t U_EXPORT2 +uset_getItemCount(const USet* set); + +/** + * Returns an item of this set. An item is either a range of + * characters or a single multicharacter string (which can be the empty string). + * + * If itemIndex is less than uset_getRangeCount(), then this function returns 0, + * and the range is *start..*end. + * + * If itemIndex is at least uset_getRangeCount() and less than uset_getItemCount(), then + * this function copies the string into str[strCapacity] and + * returns the length of the string (0 for the empty string). + * + * If itemIndex is out of range, then this function returns -1. + * + * Note that 0 is returned for each range as well as for the empty string. + * + * @param set the set + * @param itemIndex a non-negative integer in the range 0..uset_getItemCount(set)-1 + * @param start pointer to variable to receive first character in range, inclusive; + * can be NULL for a string item + * @param end pointer to variable to receive last character in range, inclusive; + * can be NULL for a string item + * @param str buffer to receive the string, may be NULL + * @param strCapacity capacity of str, or 0 if str is NULL + * @param ec error code; U_INDEX_OUTOFBOUNDS_ERROR if the itemIndex is out of range + * @return the length of the string (0 or >= 2), or 0 if the item is a range, + * or -1 if the itemIndex is out of range + * @stable ICU 2.4 + */ +U_CAPI int32_t U_EXPORT2 +uset_getItem(const USet* set, int32_t itemIndex, + UChar32* start, UChar32* end, + UChar* str, int32_t strCapacity, + UErrorCode* ec); + +/** + * Returns true if set1 contains all the characters and strings + * of set2. It answers the question, 'Is set1 a superset of set2?' + * @param set1 set to be checked for containment + * @param set2 set to be checked for containment + * @return true if the test condition is met + * @stable ICU 3.2 + */ +U_CAPI UBool U_EXPORT2 +uset_containsAll(const USet* set1, const USet* set2); + +/** + * Returns true if this set contains all the characters + * of the given string. This is does not check containment of grapheme + * clusters, like uset_containsString. + * @param set set of characters to be checked for containment + * @param str string containing codepoints to be checked for containment + * @param strLen the length of the string or -1 if null terminated. + * @return true if the test condition is met + * @stable ICU 3.4 + */ +U_CAPI UBool U_EXPORT2 +uset_containsAllCodePoints(const USet* set, const UChar *str, int32_t strLen); + +/** + * Returns true if set1 contains none of the characters and strings + * of set2. It answers the question, 'Is set1 a disjoint set of set2?' + * @param set1 set to be checked for containment + * @param set2 set to be checked for containment + * @return true if the test condition is met + * @stable ICU 3.2 + */ +U_CAPI UBool U_EXPORT2 +uset_containsNone(const USet* set1, const USet* set2); + +/** + * Returns true if set1 contains some of the characters and strings + * of set2. It answers the question, 'Does set1 and set2 have an intersection?' + * @param set1 set to be checked for containment + * @param set2 set to be checked for containment + * @return true if the test condition is met + * @stable ICU 3.2 + */ +U_CAPI UBool U_EXPORT2 +uset_containsSome(const USet* set1, const USet* set2); + +/** + * Returns the length of the initial substring of the input string which + * consists only of characters and strings that are contained in this set + * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), + * or only of characters and strings that are not contained + * in this set (USET_SPAN_NOT_CONTAINED). + * See USetSpanCondition for details. + * Similar to the strspn() C library function. + * Unpaired surrogates are treated according to contains() of their surrogate code points. + * This function works faster with a frozen set and with a non-negative string length argument. + * @param set the set + * @param s start of the string + * @param length of the string; can be -1 for NUL-terminated + * @param spanCondition specifies the containment condition + * @return the length of the initial substring according to the spanCondition; + * 0 if the start of the string does not fit the spanCondition + * @stable ICU 3.8 + * @see USetSpanCondition + */ +U_CAPI int32_t U_EXPORT2 +uset_span(const USet *set, const UChar *s, int32_t length, USetSpanCondition spanCondition); + +/** + * Returns the start of the trailing substring of the input string which + * consists only of characters and strings that are contained in this set + * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), + * or only of characters and strings that are not contained + * in this set (USET_SPAN_NOT_CONTAINED). + * See USetSpanCondition for details. + * Unpaired surrogates are treated according to contains() of their surrogate code points. + * This function works faster with a frozen set and with a non-negative string length argument. + * @param set the set + * @param s start of the string + * @param length of the string; can be -1 for NUL-terminated + * @param spanCondition specifies the containment condition + * @return the start of the trailing substring according to the spanCondition; + * the string length if the end of the string does not fit the spanCondition + * @stable ICU 3.8 + * @see USetSpanCondition + */ +U_CAPI int32_t U_EXPORT2 +uset_spanBack(const USet *set, const UChar *s, int32_t length, USetSpanCondition spanCondition); + +/** + * Returns the length of the initial substring of the input string which + * consists only of characters and strings that are contained in this set + * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), + * or only of characters and strings that are not contained + * in this set (USET_SPAN_NOT_CONTAINED). + * See USetSpanCondition for details. + * Similar to the strspn() C library function. + * Malformed byte sequences are treated according to contains(0xfffd). + * This function works faster with a frozen set and with a non-negative string length argument. + * @param set the set + * @param s start of the string (UTF-8) + * @param length of the string; can be -1 for NUL-terminated + * @param spanCondition specifies the containment condition + * @return the length of the initial substring according to the spanCondition; + * 0 if the start of the string does not fit the spanCondition + * @stable ICU 3.8 + * @see USetSpanCondition + */ +U_CAPI int32_t U_EXPORT2 +uset_spanUTF8(const USet *set, const char *s, int32_t length, USetSpanCondition spanCondition); + +/** + * Returns the start of the trailing substring of the input string which + * consists only of characters and strings that are contained in this set + * (USET_SPAN_CONTAINED, USET_SPAN_SIMPLE), + * or only of characters and strings that are not contained + * in this set (USET_SPAN_NOT_CONTAINED). + * See USetSpanCondition for details. + * Malformed byte sequences are treated according to contains(0xfffd). + * This function works faster with a frozen set and with a non-negative string length argument. + * @param set the set + * @param s start of the string (UTF-8) + * @param length of the string; can be -1 for NUL-terminated + * @param spanCondition specifies the containment condition + * @return the start of the trailing substring according to the spanCondition; + * the string length if the end of the string does not fit the spanCondition + * @stable ICU 3.8 + * @see USetSpanCondition + */ +U_CAPI int32_t U_EXPORT2 +uset_spanBackUTF8(const USet *set, const char *s, int32_t length, USetSpanCondition spanCondition); + +/** + * Returns true if set1 contains all of the characters and strings + * of set2, and vis versa. It answers the question, 'Is set1 equal to set2?' + * @param set1 set to be checked for containment + * @param set2 set to be checked for containment + * @return true if the test condition is met + * @stable ICU 3.2 + */ +U_CAPI UBool U_EXPORT2 +uset_equals(const USet* set1, const USet* set2); + +/********************************************************************* + * Serialized set API + *********************************************************************/ + +/** + * Serializes this set into an array of 16-bit integers. Serialization + * (currently) only records the characters in the set; multicharacter + * strings are ignored. + * + * The array + * has following format (each line is one 16-bit integer): + * + * length = (n+2*m) | (m!=0?0x8000:0) + * bmpLength = n; present if m!=0 + * bmp[0] + * bmp[1] + * ... + * bmp[n-1] + * supp-high[0] + * supp-low[0] + * supp-high[1] + * supp-low[1] + * ... + * supp-high[m-1] + * supp-low[m-1] + * + * The array starts with a header. After the header are n bmp + * code points, then m supplementary code points. Either n or m + * or both may be zero. n+2*m is always <= 0x7FFF. + * + * If there are no supplementary characters (if m==0) then the + * header is one 16-bit integer, 'length', with value n. + * + * If there are supplementary characters (if m!=0) then the header + * is two 16-bit integers. The first, 'length', has value + * (n+2*m)|0x8000. The second, 'bmpLength', has value n. + * + * After the header the code points are stored in ascending order. + * Supplementary code points are stored as most significant 16 + * bits followed by least significant 16 bits. + * + * @param set the set + * @param dest pointer to buffer of destCapacity 16-bit integers. + * May be NULL only if destCapacity is zero. + * @param destCapacity size of dest, or zero. Must not be negative. + * @param pErrorCode pointer to the error code. Will be set to + * U_INDEX_OUTOFBOUNDS_ERROR if n+2*m > 0x7FFF. Will be set to + * U_BUFFER_OVERFLOW_ERROR if n+2*m+(m!=0?2:1) > destCapacity. + * @return the total length of the serialized format, including + * the header, that is, n+2*m+(m!=0?2:1), or 0 on error other + * than U_BUFFER_OVERFLOW_ERROR. + * @stable ICU 2.4 + */ +U_CAPI int32_t U_EXPORT2 +uset_serialize(const USet* set, uint16_t* dest, int32_t destCapacity, UErrorCode* pErrorCode); + +/** + * Given a serialized array, fill in the given serialized set object. + * @param fillSet pointer to result + * @param src pointer to start of array + * @param srcLength length of array + * @return true if the given array is valid, otherwise false + * @stable ICU 2.4 + */ +U_CAPI UBool U_EXPORT2 +uset_getSerializedSet(USerializedSet* fillSet, const uint16_t* src, int32_t srcLength); + +/** + * Set the USerializedSet to contain the given character (and nothing + * else). + * @param fillSet pointer to result + * @param c The codepoint to set + * @stable ICU 2.4 + */ +U_CAPI void U_EXPORT2 +uset_setSerializedToOne(USerializedSet* fillSet, UChar32 c); + +/** + * Returns true if the given USerializedSet contains the given + * character. + * @param set the serialized set + * @param c The codepoint to check for within the set + * @return true if set contains c + * @stable ICU 2.4 + */ +U_CAPI UBool U_EXPORT2 +uset_serializedContains(const USerializedSet* set, UChar32 c); + +/** + * Returns the number of disjoint ranges of characters contained in + * the given serialized set. Ignores any strings contained in the + * set. + * @param set the serialized set + * @return a non-negative integer counting the character ranges + * contained in set + * @stable ICU 2.4 + */ +U_CAPI int32_t U_EXPORT2 +uset_getSerializedRangeCount(const USerializedSet* set); + +/** + * Returns a range of characters contained in the given serialized + * set. + * @param set the serialized set + * @param rangeIndex a non-negative integer in the range 0.. + * uset_getSerializedRangeCount(set)-1 + * @param pStart pointer to variable to receive first character + * in range, inclusive + * @param pEnd pointer to variable to receive last character in range, + * inclusive + * @return true if rangeIndex is valid, otherwise false + * @stable ICU 2.4 + */ +U_CAPI UBool U_EXPORT2 +uset_getSerializedRange(const USerializedSet* set, int32_t rangeIndex, + UChar32* pStart, UChar32* pEnd); + +#endif diff --git a/deps/icu-small/source/common/unicode/usetiter.h b/deps/icu-small/source/common/unicode/usetiter.h index 34992d94b7f7b8..0d3a13b9da7093 100644 --- a/deps/icu-small/source/common/unicode/usetiter.h +++ b/deps/icu-small/source/common/unicode/usetiter.h @@ -1,323 +1,323 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ -#ifndef USETITER_H -#define USETITER_H - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API - -#include "unicode/uobject.h" -#include "unicode/unistr.h" - -/** - * \file - * \brief C++ API: UnicodeSetIterator iterates over the contents of a UnicodeSet. - */ - -U_NAMESPACE_BEGIN - -class UnicodeSet; -class UnicodeString; - -/** - * - * UnicodeSetIterator iterates over the contents of a UnicodeSet. It - * iterates over either code points or code point ranges. After all - * code points or ranges have been returned, it returns the - * multicharacter strings of the UnicodeSet, if any. - * - * This class is not intended for public subclassing. - * - *

To iterate over code points and strings, use a loop like this: - *

- * UnicodeSetIterator it(set);
- * while (it.next()) {
- *     processItem(it.getString());
- * }
- * 
- *

Each item in the set is accessed as a string. Set elements - * consisting of single code points are returned as strings containing - * just the one code point. - * - *

To iterate over code point ranges, instead of individual code points, - * use a loop like this: - *

- * UnicodeSetIterator it(set);
- * while (it.nextRange()) {
- *   if (it.isString()) {
- *     processString(it.getString());
- *   } else {
- *     processCodepointRange(it.getCodepoint(), it.getCodepointEnd());
- *   }
- * }
- * 
- * - * To iterate over only the strings, start with skipToStrings(). - * - * @author M. Davis - * @stable ICU 2.4 - */ -class U_COMMON_API UnicodeSetIterator U_FINAL : public UObject { - /** - * Value of codepoint if the iterator points to a string. - * If codepoint == IS_STRING, then examine - * string for the current iteration result. - */ - enum { IS_STRING = -1 }; - - /** - * Current code point, or the special value IS_STRING, if - * the iterator points to a string. - */ - UChar32 codepoint; - - /** - * When iterating over ranges using nextRange(), - * codepointEnd contains the inclusive end of the - * iteration range, if codepoint != IS_STRING. If - * iterating over code points using next(), or if - * codepoint == IS_STRING, then the value of - * codepointEnd is undefined. - */ - UChar32 codepointEnd; - - /** - * If codepoint == IS_STRING, then string points - * to the current string. If codepoint != IS_STRING, the - * value of string is undefined. - */ - const UnicodeString* string; - - public: - - /** - * Create an iterator over the given set. The iterator is valid - * only so long as set is valid. - * @param set set to iterate over - * @stable ICU 2.4 - */ - UnicodeSetIterator(const UnicodeSet& set); - - /** - * Create an iterator over nothing. next() and - * nextRange() return false. This is a convenience - * constructor allowing the target to be set later. - * @stable ICU 2.4 - */ - UnicodeSetIterator(); - - /** - * Destructor. - * @stable ICU 2.4 - */ - virtual ~UnicodeSetIterator(); - - /** - * Returns true if the current element is a string. If so, the - * caller can retrieve it with getString(). If this - * method returns false, the current element is a code point or - * code point range, depending on whether next() or - * nextRange() was called. - * Elements of types string and codepoint can both be retrieved - * with the function getString(). - * Elements of type codepoint can also be retrieved with - * getCodepoint(). - * For ranges, getCodepoint() returns the starting codepoint - * of the range, and getCodepointEnd() returns the end - * of the range. - * @stable ICU 2.4 - */ - inline UBool isString() const; - - /** - * Returns the current code point, if isString() returned - * false. Otherwise returns an undefined result. - * @stable ICU 2.4 - */ - inline UChar32 getCodepoint() const; - - /** - * Returns the end of the current code point range, if - * isString() returned false and nextRange() was - * called. Otherwise returns an undefined result. - * @stable ICU 2.4 - */ - inline UChar32 getCodepointEnd() const; - - /** - * Returns the current string, if isString() returned - * true. If the current iteration item is a code point, a UnicodeString - * containing that single code point is returned. - * - * Ownership of the returned string remains with the iterator. - * The string is guaranteed to remain valid only until the iterator is - * advanced to the next item, or until the iterator is deleted. - * - * @stable ICU 2.4 - */ - const UnicodeString& getString(); - - /** - * Skips over the remaining code points/ranges, if any. - * A following call to next() or nextRange() will yield a string, if there is one. - * No-op if next() would return false, or if it would yield a string anyway. - * - * @return *this - * @stable ICU 70 - * @see UnicodeSet#strings() - */ - inline UnicodeSetIterator &skipToStrings() { - // Finish code point/range iteration. - range = endRange; - endElement = -1; - nextElement = 0; - return *this; - } - - /** - * Advances the iteration position to the next element in the set, - * which can be either a single code point or a string. - * If there are no more elements in the set, return false. - * - *

- * If isString() == true, the value is a - * string, otherwise the value is a - * single code point. Elements of either type can be retrieved - * with the function getString(), while elements of - * consisting of a single code point can be retrieved with - * getCodepoint() - * - *

The order of iteration is all code points in sorted order, - * followed by all strings sorted order. Do not mix - * calls to next() and nextRange() without - * calling reset() between them. The results of doing so - * are undefined. - * - * @return true if there was another element in the set. - * @stable ICU 2.4 - */ - UBool next(); - - /** - * Returns the next element in the set, either a code point range - * or a string. If there are no more elements in the set, return - * false. If isString() == true, the value is a - * string and can be accessed with getString(). Otherwise the value is a - * range of one or more code points from getCodepoint() to - * getCodepointeEnd() inclusive. - * - *

The order of iteration is all code points ranges in sorted - * order, followed by all strings sorted order. Ranges are - * disjoint and non-contiguous. The value returned from getString() - * is undefined unless isString() == true. Do not mix calls to - * next() and nextRange() without calling - * reset() between them. The results of doing so are - * undefined. - * - * @return true if there was another element in the set. - * @stable ICU 2.4 - */ - UBool nextRange(); - - /** - * Sets this iterator to visit the elements of the given set and - * resets it to the start of that set. The iterator is valid only - * so long as set is valid. - * @param set the set to iterate over. - * @stable ICU 2.4 - */ - void reset(const UnicodeSet& set); - - /** - * Resets this iterator to the start of the set. - * @stable ICU 2.4 - */ - void reset(); - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - * - * @stable ICU 2.4 - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - * - * @stable ICU 2.4 - */ - virtual UClassID getDynamicClassID() const override; - - // ======================= PRIVATES =========================== - -private: - - // endElement and nextElements are really UChar32's, but we keep - // them as signed int32_t's so we can do comparisons with - // endElement set to -1. Leave them as int32_t's. - /** The set - */ - const UnicodeSet* set; - /** End range - */ - int32_t endRange; - /** Range - */ - int32_t range; - /** End element - */ - int32_t endElement; - /** Next element - */ - int32_t nextElement; - /** Next string - */ - int32_t nextString; - /** String count - */ - int32_t stringCount; - - /** - * Points to the string to use when the caller asks for a - * string and the current iteration item is a code point, not a string. - */ - UnicodeString *cpString; - - /** Copy constructor. Disallowed. - */ - UnicodeSetIterator(const UnicodeSetIterator&) = delete; - - /** Assignment operator. Disallowed. - */ - UnicodeSetIterator& operator=(const UnicodeSetIterator&) = delete; - - /** Load range - */ - void loadRange(int32_t range); -}; - -inline UBool UnicodeSetIterator::isString() const { - return codepoint < 0; -} - -inline UChar32 UnicodeSetIterator::getCodepoint() const { - return codepoint; -} - -inline UChar32 UnicodeSetIterator::getCodepointEnd() const { - return codepointEnd; -} - - -U_NAMESPACE_END - -#endif /* U_SHOW_CPLUSPLUS_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ +#ifndef USETITER_H +#define USETITER_H + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API + +#include "unicode/uobject.h" +#include "unicode/unistr.h" + +/** + * \file + * \brief C++ API: UnicodeSetIterator iterates over the contents of a UnicodeSet. + */ + +U_NAMESPACE_BEGIN + +class UnicodeSet; +class UnicodeString; + +/** + * + * UnicodeSetIterator iterates over the contents of a UnicodeSet. It + * iterates over either code points or code point ranges. After all + * code points or ranges have been returned, it returns the + * multicharacter strings of the UnicodeSet, if any. + * + * This class is not intended for public subclassing. + * + *

To iterate over code points and strings, use a loop like this: + *

+ * UnicodeSetIterator it(set);
+ * while (it.next()) {
+ *     processItem(it.getString());
+ * }
+ * 
+ *

Each item in the set is accessed as a string. Set elements + * consisting of single code points are returned as strings containing + * just the one code point. + * + *

To iterate over code point ranges, instead of individual code points, + * use a loop like this: + *

+ * UnicodeSetIterator it(set);
+ * while (it.nextRange()) {
+ *   if (it.isString()) {
+ *     processString(it.getString());
+ *   } else {
+ *     processCodepointRange(it.getCodepoint(), it.getCodepointEnd());
+ *   }
+ * }
+ * 
+ * + * To iterate over only the strings, start with skipToStrings(). + * + * @author M. Davis + * @stable ICU 2.4 + */ +class U_COMMON_API UnicodeSetIterator final : public UObject { + /** + * Value of codepoint if the iterator points to a string. + * If codepoint == IS_STRING, then examine + * string for the current iteration result. + */ + enum { IS_STRING = -1 }; + + /** + * Current code point, or the special value IS_STRING, if + * the iterator points to a string. + */ + UChar32 codepoint; + + /** + * When iterating over ranges using nextRange(), + * codepointEnd contains the inclusive end of the + * iteration range, if codepoint != IS_STRING. If + * iterating over code points using next(), or if + * codepoint == IS_STRING, then the value of + * codepointEnd is undefined. + */ + UChar32 codepointEnd; + + /** + * If codepoint == IS_STRING, then string points + * to the current string. If codepoint != IS_STRING, the + * value of string is undefined. + */ + const UnicodeString* string; + + public: + + /** + * Create an iterator over the given set. The iterator is valid + * only so long as set is valid. + * @param set set to iterate over + * @stable ICU 2.4 + */ + UnicodeSetIterator(const UnicodeSet& set); + + /** + * Create an iterator over nothing. next() and + * nextRange() return false. This is a convenience + * constructor allowing the target to be set later. + * @stable ICU 2.4 + */ + UnicodeSetIterator(); + + /** + * Destructor. + * @stable ICU 2.4 + */ + virtual ~UnicodeSetIterator(); + + /** + * Returns true if the current element is a string. If so, the + * caller can retrieve it with getString(). If this + * method returns false, the current element is a code point or + * code point range, depending on whether next() or + * nextRange() was called. + * Elements of types string and codepoint can both be retrieved + * with the function getString(). + * Elements of type codepoint can also be retrieved with + * getCodepoint(). + * For ranges, getCodepoint() returns the starting codepoint + * of the range, and getCodepointEnd() returns the end + * of the range. + * @stable ICU 2.4 + */ + inline UBool isString() const; + + /** + * Returns the current code point, if isString() returned + * false. Otherwise returns an undefined result. + * @stable ICU 2.4 + */ + inline UChar32 getCodepoint() const; + + /** + * Returns the end of the current code point range, if + * isString() returned false and nextRange() was + * called. Otherwise returns an undefined result. + * @stable ICU 2.4 + */ + inline UChar32 getCodepointEnd() const; + + /** + * Returns the current string, if isString() returned + * true. If the current iteration item is a code point, a UnicodeString + * containing that single code point is returned. + * + * Ownership of the returned string remains with the iterator. + * The string is guaranteed to remain valid only until the iterator is + * advanced to the next item, or until the iterator is deleted. + * + * @stable ICU 2.4 + */ + const UnicodeString& getString(); + + /** + * Skips over the remaining code points/ranges, if any. + * A following call to next() or nextRange() will yield a string, if there is one. + * No-op if next() would return false, or if it would yield a string anyway. + * + * @return *this + * @stable ICU 70 + * @see UnicodeSet#strings() + */ + inline UnicodeSetIterator &skipToStrings() { + // Finish code point/range iteration. + range = endRange; + endElement = -1; + nextElement = 0; + return *this; + } + + /** + * Advances the iteration position to the next element in the set, + * which can be either a single code point or a string. + * If there are no more elements in the set, return false. + * + *

+ * If isString() == true, the value is a + * string, otherwise the value is a + * single code point. Elements of either type can be retrieved + * with the function getString(), while elements of + * consisting of a single code point can be retrieved with + * getCodepoint() + * + *

The order of iteration is all code points in sorted order, + * followed by all strings sorted order. Do not mix + * calls to next() and nextRange() without + * calling reset() between them. The results of doing so + * are undefined. + * + * @return true if there was another element in the set. + * @stable ICU 2.4 + */ + UBool next(); + + /** + * Returns the next element in the set, either a code point range + * or a string. If there are no more elements in the set, return + * false. If isString() == true, the value is a + * string and can be accessed with getString(). Otherwise the value is a + * range of one or more code points from getCodepoint() to + * getCodepointeEnd() inclusive. + * + *

The order of iteration is all code points ranges in sorted + * order, followed by all strings sorted order. Ranges are + * disjoint and non-contiguous. The value returned from getString() + * is undefined unless isString() == true. Do not mix calls to + * next() and nextRange() without calling + * reset() between them. The results of doing so are + * undefined. + * + * @return true if there was another element in the set. + * @stable ICU 2.4 + */ + UBool nextRange(); + + /** + * Sets this iterator to visit the elements of the given set and + * resets it to the start of that set. The iterator is valid only + * so long as set is valid. + * @param set the set to iterate over. + * @stable ICU 2.4 + */ + void reset(const UnicodeSet& set); + + /** + * Resets this iterator to the start of the set. + * @stable ICU 2.4 + */ + void reset(); + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + * + * @stable ICU 2.4 + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + * + * @stable ICU 2.4 + */ + virtual UClassID getDynamicClassID() const override; + + // ======================= PRIVATES =========================== + +private: + + // endElement and nextElements are really UChar32's, but we keep + // them as signed int32_t's so we can do comparisons with + // endElement set to -1. Leave them as int32_t's. + /** The set + */ + const UnicodeSet* set; + /** End range + */ + int32_t endRange; + /** Range + */ + int32_t range; + /** End element + */ + int32_t endElement; + /** Next element + */ + int32_t nextElement; + /** Next string + */ + int32_t nextString; + /** String count + */ + int32_t stringCount; + + /** + * Points to the string to use when the caller asks for a + * string and the current iteration item is a code point, not a string. + */ + UnicodeString *cpString; + + /** Copy constructor. Disallowed. + */ + UnicodeSetIterator(const UnicodeSetIterator&) = delete; + + /** Assignment operator. Disallowed. + */ + UnicodeSetIterator& operator=(const UnicodeSetIterator&) = delete; + + /** Load range + */ + void loadRange(int32_t range); +}; + +inline UBool UnicodeSetIterator::isString() const { + return codepoint < 0; +} + +inline UChar32 UnicodeSetIterator::getCodepoint() const { + return codepoint; +} + +inline UChar32 UnicodeSetIterator::getCodepointEnd() const { + return codepointEnd; +} + + +U_NAMESPACE_END + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/ushape.h b/deps/icu-small/source/common/unicode/ushape.h index 14371edc8f9020..cde5a067fa84bb 100644 --- a/deps/icu-small/source/common/unicode/ushape.h +++ b/deps/icu-small/source/common/unicode/ushape.h @@ -1,476 +1,476 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2000-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: ushape.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2000jun29 -* created by: Markus W. Scherer -*/ - -#ifndef __USHAPE_H__ -#define __USHAPE_H__ - -#include "unicode/utypes.h" - -/** - * \file - * \brief C API: Arabic shaping - * - */ - -/** - * Shape Arabic text on a character basis. - * - *

This function performs basic operations for "shaping" Arabic text. It is most - * useful for use with legacy data formats and legacy display technology - * (simple terminals). All operations are performed on Unicode characters.

- * - *

Text-based shaping means that some character code points in the text are - * replaced by others depending on the context. It transforms one kind of text - * into another. In comparison, modern displays for Arabic text select - * appropriate, context-dependent font glyphs for each text element, which means - * that they transform text into a glyph vector.

- * - *

Text transformations are necessary when modern display technology is not - * available or when text needs to be transformed to or from legacy formats that - * use "shaped" characters. Since the Arabic script is cursive, connecting - * adjacent letters to each other, computers select images for each letter based - * on the surrounding letters. This usually results in four images per Arabic - * letter: initial, middle, final, and isolated forms. In Unicode, on the other - * hand, letters are normally stored abstract, and a display system is expected - * to select the necessary glyphs. (This makes searching and other text - * processing easier because the same letter has only one code.) It is possible - * to mimic this with text transformations because there are characters in - * Unicode that are rendered as letters with a specific shape - * (or cursive connectivity). They were included for interoperability with - * legacy systems and codepages, and for unsophisticated display systems.

- * - *

A second kind of text transformations is supported for Arabic digits: - * For compatibility with legacy codepages that only include European digits, - * it is possible to replace one set of digits by another, changing the - * character code points. These operations can be performed for either - * Arabic-Indic Digits (U+0660...U+0669) or Eastern (Extended) Arabic-Indic - * digits (U+06f0...U+06f9).

- * - *

Some replacements may result in more or fewer characters (code points). - * By default, this means that the destination buffer may receive text with a - * length different from the source length. Some legacy systems rely on the - * length of the text to be constant. They expect extra spaces to be added - * or consumed either next to the affected character or at the end of the - * text.

- * - *

For details about the available operations, see the description of the - * U_SHAPE_... options.

- * - * @param source The input text. - * - * @param sourceLength The number of UChars in source. - * - * @param dest The destination buffer that will receive the results of the - * requested operations. It may be NULL only if - * destSize is 0. The source and destination must not - * overlap. - * - * @param destSize The size (capacity) of the destination buffer in UChars. - * If destSize is 0, then no output is produced, - * but the necessary buffer size is returned ("preflighting"). - * - * @param options This is a 32-bit set of flags that specify the operations - * that are performed on the input text. If no error occurs, - * then the result will always be written to the destination - * buffer. - * - * @param pErrorCode must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * - * @return The number of UChars written to the destination buffer. - * If an error occurred, then no output was written, or it may be - * incomplete. If U_BUFFER_OVERFLOW_ERROR is set, then - * the return value indicates the necessary destination buffer size. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_shapeArabic(const UChar *source, int32_t sourceLength, - UChar *dest, int32_t destSize, - uint32_t options, - UErrorCode *pErrorCode); - -/** - * Memory option: allow the result to have a different length than the source. - * Affects: LamAlef options - * @stable ICU 2.0 - */ -#define U_SHAPE_LENGTH_GROW_SHRINK 0 - -/** - * Memory option: allow the result to have a different length than the source. - * Affects: LamAlef options - * This option is an alias to U_SHAPE_LENGTH_GROW_SHRINK - * @stable ICU 4.2 - */ -#define U_SHAPE_LAMALEF_RESIZE 0 - -/** - * Memory option: the result must have the same length as the source. - * If more room is necessary, then try to consume spaces next to modified characters. - * @stable ICU 2.0 - */ -#define U_SHAPE_LENGTH_FIXED_SPACES_NEAR 1 - -/** - * Memory option: the result must have the same length as the source. - * If more room is necessary, then try to consume spaces next to modified characters. - * Affects: LamAlef options - * This option is an alias to U_SHAPE_LENGTH_FIXED_SPACES_NEAR - * @stable ICU 4.2 - */ -#define U_SHAPE_LAMALEF_NEAR 1 - -/** - * Memory option: the result must have the same length as the source. - * If more room is necessary, then try to consume spaces at the end of the text. - * @stable ICU 2.0 - */ -#define U_SHAPE_LENGTH_FIXED_SPACES_AT_END 2 - -/** - * Memory option: the result must have the same length as the source. - * If more room is necessary, then try to consume spaces at the end of the text. - * Affects: LamAlef options - * This option is an alias to U_SHAPE_LENGTH_FIXED_SPACES_AT_END - * @stable ICU 4.2 - */ -#define U_SHAPE_LAMALEF_END 2 - -/** - * Memory option: the result must have the same length as the source. - * If more room is necessary, then try to consume spaces at the beginning of the text. - * @stable ICU 2.0 - */ -#define U_SHAPE_LENGTH_FIXED_SPACES_AT_BEGINNING 3 - -/** - * Memory option: the result must have the same length as the source. - * If more room is necessary, then try to consume spaces at the beginning of the text. - * Affects: LamAlef options - * This option is an alias to U_SHAPE_LENGTH_FIXED_SPACES_AT_BEGINNING - * @stable ICU 4.2 - */ -#define U_SHAPE_LAMALEF_BEGIN 3 - - -/** - * Memory option: the result must have the same length as the source. - * Shaping Mode: For each LAMALEF character found, expand LAMALEF using space at end. - * If there is no space at end, use spaces at beginning of the buffer. If there - * is no space at beginning of the buffer, use spaces at the near (i.e. the space - * after the LAMALEF character). - * If there are no spaces found, an error U_NO_SPACE_AVAILABLE (as defined in utypes.h) - * will be set in pErrorCode - * - * Deshaping Mode: Perform the same function as the flag equals U_SHAPE_LAMALEF_END. - * Affects: LamAlef options - * @stable ICU 4.2 - */ -#define U_SHAPE_LAMALEF_AUTO 0x10000 - -/** Bit mask for memory options. @stable ICU 2.0 */ -#define U_SHAPE_LENGTH_MASK 0x10003 /* Changed old value 3 */ - - -/** - * Bit mask for LamAlef memory options. - * @stable ICU 4.2 - */ -#define U_SHAPE_LAMALEF_MASK 0x10003 /* updated */ - -/** Direction indicator: the source is in logical (keyboard) order. @stable ICU 2.0 */ -#define U_SHAPE_TEXT_DIRECTION_LOGICAL 0 - -/** - * Direction indicator: - * the source is in visual RTL order, - * the rightmost displayed character stored first. - * This option is an alias to U_SHAPE_TEXT_DIRECTION_LOGICAL - * @stable ICU 4.2 - */ -#define U_SHAPE_TEXT_DIRECTION_VISUAL_RTL 0 - -/** - * Direction indicator: - * the source is in visual LTR order, - * the leftmost displayed character stored first. - * @stable ICU 2.0 - */ -#define U_SHAPE_TEXT_DIRECTION_VISUAL_LTR 4 - -/** Bit mask for direction indicators. @stable ICU 2.0 */ -#define U_SHAPE_TEXT_DIRECTION_MASK 4 - - -/** Letter shaping option: do not perform letter shaping. @stable ICU 2.0 */ -#define U_SHAPE_LETTERS_NOOP 0 - -/** Letter shaping option: replace abstract letter characters by "shaped" ones. @stable ICU 2.0 */ -#define U_SHAPE_LETTERS_SHAPE 8 - -/** Letter shaping option: replace "shaped" letter characters by abstract ones. @stable ICU 2.0 */ -#define U_SHAPE_LETTERS_UNSHAPE 0x10 - -/** - * Letter shaping option: replace abstract letter characters by "shaped" ones. - * The only difference with U_SHAPE_LETTERS_SHAPE is that Tashkeel letters - * are always "shaped" into the isolated form instead of the medial form - * (selecting code points from the Arabic Presentation Forms-B block). - * @stable ICU 2.0 - */ -#define U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED 0x18 - - -/** Bit mask for letter shaping options. @stable ICU 2.0 */ -#define U_SHAPE_LETTERS_MASK 0x18 - - -/** Digit shaping option: do not perform digit shaping. @stable ICU 2.0 */ -#define U_SHAPE_DIGITS_NOOP 0 - -/** - * Digit shaping option: - * Replace European digits (U+0030...) by Arabic-Indic digits. - * @stable ICU 2.0 - */ -#define U_SHAPE_DIGITS_EN2AN 0x20 - -/** - * Digit shaping option: - * Replace Arabic-Indic digits by European digits (U+0030...). - * @stable ICU 2.0 - */ -#define U_SHAPE_DIGITS_AN2EN 0x40 - -/** - * Digit shaping option: - * Replace European digits (U+0030...) by Arabic-Indic digits if the most recent - * strongly directional character is an Arabic letter - * (u_charDirection() result U_RIGHT_TO_LEFT_ARABIC [AL]).
- * The direction of "preceding" depends on the direction indicator option. - * For the first characters, the preceding strongly directional character - * (initial state) is assumed to be not an Arabic letter - * (it is U_LEFT_TO_RIGHT [L] or U_RIGHT_TO_LEFT [R]). - * @stable ICU 2.0 - */ -#define U_SHAPE_DIGITS_ALEN2AN_INIT_LR 0x60 - -/** - * Digit shaping option: - * Replace European digits (U+0030...) by Arabic-Indic digits if the most recent - * strongly directional character is an Arabic letter - * (u_charDirection() result U_RIGHT_TO_LEFT_ARABIC [AL]).
- * The direction of "preceding" depends on the direction indicator option. - * For the first characters, the preceding strongly directional character - * (initial state) is assumed to be an Arabic letter. - * @stable ICU 2.0 - */ -#define U_SHAPE_DIGITS_ALEN2AN_INIT_AL 0x80 - -/** Not a valid option value. May be replaced by a new option. @stable ICU 2.0 */ -#define U_SHAPE_DIGITS_RESERVED 0xa0 - -/** Bit mask for digit shaping options. @stable ICU 2.0 */ -#define U_SHAPE_DIGITS_MASK 0xe0 - - -/** Digit type option: Use Arabic-Indic digits (U+0660...U+0669). @stable ICU 2.0 */ -#define U_SHAPE_DIGIT_TYPE_AN 0 - -/** Digit type option: Use Eastern (Extended) Arabic-Indic digits (U+06f0...U+06f9). @stable ICU 2.0 */ -#define U_SHAPE_DIGIT_TYPE_AN_EXTENDED 0x100 - -/** Not a valid option value. May be replaced by a new option. @stable ICU 2.0 */ -#define U_SHAPE_DIGIT_TYPE_RESERVED 0x200 - -/** Bit mask for digit type options. @stable ICU 2.0 */ -#define U_SHAPE_DIGIT_TYPE_MASK 0x300 /* I need to change this from 0x3f00 to 0x300 */ - -/** - * Tashkeel aggregation option: - * Replaces any combination of U+0651 with one of - * U+064C, U+064D, U+064E, U+064F, U+0650 with - * U+FC5E, U+FC5F, U+FC60, U+FC61, U+FC62 consecutively. - * @stable ICU 3.6 - */ -#define U_SHAPE_AGGREGATE_TASHKEEL 0x4000 -/** Tashkeel aggregation option: do not aggregate tashkeels. @stable ICU 3.6 */ -#define U_SHAPE_AGGREGATE_TASHKEEL_NOOP 0 -/** Bit mask for tashkeel aggregation. @stable ICU 3.6 */ -#define U_SHAPE_AGGREGATE_TASHKEEL_MASK 0x4000 - -/** - * Presentation form option: - * Don't replace Arabic Presentation Forms-A and Arabic Presentation Forms-B - * characters with 0+06xx characters, before shaping. - * @stable ICU 3.6 - */ -#define U_SHAPE_PRESERVE_PRESENTATION 0x8000 -/** Presentation form option: - * Replace Arabic Presentation Forms-A and Arabic Presentationo Forms-B with - * their unshaped correspondents in range 0+06xx, before shaping. - * @stable ICU 3.6 - */ -#define U_SHAPE_PRESERVE_PRESENTATION_NOOP 0 -/** Bit mask for preserve presentation form. @stable ICU 3.6 */ -#define U_SHAPE_PRESERVE_PRESENTATION_MASK 0x8000 - -/* Seen Tail option */ -/** - * Memory option: the result must have the same length as the source. - * Shaping mode: The SEEN family character will expand into two characters using space near - * the SEEN family character(i.e. the space after the character). - * If there are no spaces found, an error U_NO_SPACE_AVAILABLE (as defined in utypes.h) - * will be set in pErrorCode - * - * De-shaping mode: Any Seen character followed by Tail character will be - * replaced by one cell Seen and a space will replace the Tail. - * Affects: Seen options - * @stable ICU 4.2 - */ -#define U_SHAPE_SEEN_TWOCELL_NEAR 0x200000 - -/** - * Bit mask for Seen memory options. - * @stable ICU 4.2 - */ -#define U_SHAPE_SEEN_MASK 0x700000 - -/* YehHamza option */ -/** - * Memory option: the result must have the same length as the source. - * Shaping mode: The YEHHAMZA character will expand into two characters using space near it - * (i.e. the space after the character - * If there are no spaces found, an error U_NO_SPACE_AVAILABLE (as defined in utypes.h) - * will be set in pErrorCode - * - * De-shaping mode: Any Yeh (final or isolated) character followed by Hamza character will be - * replaced by one cell YehHamza and space will replace the Hamza. - * Affects: YehHamza options - * @stable ICU 4.2 - */ -#define U_SHAPE_YEHHAMZA_TWOCELL_NEAR 0x1000000 - - -/** - * Bit mask for YehHamza memory options. - * @stable ICU 4.2 - */ -#define U_SHAPE_YEHHAMZA_MASK 0x3800000 - -/* New Tashkeel options */ -/** - * Memory option: the result must have the same length as the source. - * Shaping mode: Tashkeel characters will be replaced by spaces. - * Spaces will be placed at beginning of the buffer - * - * De-shaping mode: N/A - * Affects: Tashkeel options - * @stable ICU 4.2 - */ -#define U_SHAPE_TASHKEEL_BEGIN 0x40000 - -/** - * Memory option: the result must have the same length as the source. - * Shaping mode: Tashkeel characters will be replaced by spaces. - * Spaces will be placed at end of the buffer - * - * De-shaping mode: N/A - * Affects: Tashkeel options - * @stable ICU 4.2 - */ -#define U_SHAPE_TASHKEEL_END 0x60000 - -/** - * Memory option: allow the result to have a different length than the source. - * Shaping mode: Tashkeel characters will be removed, buffer length will shrink. - * De-shaping mode: N/A - * - * Affect: Tashkeel options - * @stable ICU 4.2 - */ -#define U_SHAPE_TASHKEEL_RESIZE 0x80000 - -/** - * Memory option: the result must have the same length as the source. - * Shaping mode: Tashkeel characters will be replaced by Tatweel if it is connected to adjacent - * characters (i.e. shaped on Tatweel) or replaced by space if it is not connected. - * - * De-shaping mode: N/A - * Affects: YehHamza options - * @stable ICU 4.2 - */ -#define U_SHAPE_TASHKEEL_REPLACE_BY_TATWEEL 0xC0000 - -/** - * Bit mask for Tashkeel replacement with Space or Tatweel memory options. - * @stable ICU 4.2 - */ -#define U_SHAPE_TASHKEEL_MASK 0xE0000 - - -/* Space location Control options */ -/** - * This option affect the meaning of BEGIN and END options. if this option is not used the default - * for BEGIN and END will be as following: - * The Default (for both Visual LTR, Visual RTL and Logical Text) - * 1. BEGIN always refers to the start address of physical memory. - * 2. END always refers to the end address of physical memory. - * - * If this option is used it will swap the meaning of BEGIN and END only for Visual LTR text. - * - * The effect on BEGIN and END Memory Options will be as following: - * A. BEGIN For Visual LTR text: This will be the beginning (right side) of the visual text( - * corresponding to the physical memory address end for Visual LTR text, Same as END in - * default behavior) - * B. BEGIN For Logical text: Same as BEGIN in default behavior. - * C. END For Visual LTR text: This will be the end (left side) of the visual text (corresponding - * to the physical memory address beginning for Visual LTR text, Same as BEGIN in default behavior. - * D. END For Logical text: Same as END in default behavior). - * Affects: All LamAlef BEGIN, END and AUTO options. - * @stable ICU 4.2 - */ -#define U_SHAPE_SPACES_RELATIVE_TO_TEXT_BEGIN_END 0x4000000 - -/** - * Bit mask for swapping BEGIN and END for Visual LTR text - * @stable ICU 4.2 - */ -#define U_SHAPE_SPACES_RELATIVE_TO_TEXT_MASK 0x4000000 - -/** - * If this option is used, shaping will use the new Unicode code point for TAIL (i.e. 0xFE73). - * If this option is not specified (Default), old unofficial Unicode TAIL code point is used (i.e. 0x200B) - * De-shaping will not use this option as it will always search for both the new Unicode code point for the - * TAIL (i.e. 0xFE73) or the old unofficial Unicode TAIL code point (i.e. 0x200B) and de-shape the - * Seen-Family letter accordingly. - * - * Shaping Mode: Only shaping. - * De-shaping Mode: N/A. - * Affects: All Seen options - * @stable ICU 4.8 - */ -#define U_SHAPE_TAIL_NEW_UNICODE 0x8000000 - -/** - * Bit mask for new Unicode Tail option - * @stable ICU 4.8 - */ -#define U_SHAPE_TAIL_TYPE_MASK 0x8000000 - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2000-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: ushape.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2000jun29 +* created by: Markus W. Scherer +*/ + +#ifndef __USHAPE_H__ +#define __USHAPE_H__ + +#include "unicode/utypes.h" + +/** + * \file + * \brief C API: Arabic shaping + * + */ + +/** + * Shape Arabic text on a character basis. + * + *

This function performs basic operations for "shaping" Arabic text. It is most + * useful for use with legacy data formats and legacy display technology + * (simple terminals). All operations are performed on Unicode characters.

+ * + *

Text-based shaping means that some character code points in the text are + * replaced by others depending on the context. It transforms one kind of text + * into another. In comparison, modern displays for Arabic text select + * appropriate, context-dependent font glyphs for each text element, which means + * that they transform text into a glyph vector.

+ * + *

Text transformations are necessary when modern display technology is not + * available or when text needs to be transformed to or from legacy formats that + * use "shaped" characters. Since the Arabic script is cursive, connecting + * adjacent letters to each other, computers select images for each letter based + * on the surrounding letters. This usually results in four images per Arabic + * letter: initial, middle, final, and isolated forms. In Unicode, on the other + * hand, letters are normally stored abstract, and a display system is expected + * to select the necessary glyphs. (This makes searching and other text + * processing easier because the same letter has only one code.) It is possible + * to mimic this with text transformations because there are characters in + * Unicode that are rendered as letters with a specific shape + * (or cursive connectivity). They were included for interoperability with + * legacy systems and codepages, and for unsophisticated display systems.

+ * + *

A second kind of text transformations is supported for Arabic digits: + * For compatibility with legacy codepages that only include European digits, + * it is possible to replace one set of digits by another, changing the + * character code points. These operations can be performed for either + * Arabic-Indic Digits (U+0660...U+0669) or Eastern (Extended) Arabic-Indic + * digits (U+06f0...U+06f9).

+ * + *

Some replacements may result in more or fewer characters (code points). + * By default, this means that the destination buffer may receive text with a + * length different from the source length. Some legacy systems rely on the + * length of the text to be constant. They expect extra spaces to be added + * or consumed either next to the affected character or at the end of the + * text.

+ * + *

For details about the available operations, see the description of the + * U_SHAPE_... options.

+ * + * @param source The input text. + * + * @param sourceLength The number of UChars in source. + * + * @param dest The destination buffer that will receive the results of the + * requested operations. It may be NULL only if + * destSize is 0. The source and destination must not + * overlap. + * + * @param destSize The size (capacity) of the destination buffer in UChars. + * If destSize is 0, then no output is produced, + * but the necessary buffer size is returned ("preflighting"). + * + * @param options This is a 32-bit set of flags that specify the operations + * that are performed on the input text. If no error occurs, + * then the result will always be written to the destination + * buffer. + * + * @param pErrorCode must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * + * @return The number of UChars written to the destination buffer. + * If an error occurred, then no output was written, or it may be + * incomplete. If U_BUFFER_OVERFLOW_ERROR is set, then + * the return value indicates the necessary destination buffer size. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_shapeArabic(const UChar *source, int32_t sourceLength, + UChar *dest, int32_t destSize, + uint32_t options, + UErrorCode *pErrorCode); + +/** + * Memory option: allow the result to have a different length than the source. + * Affects: LamAlef options + * @stable ICU 2.0 + */ +#define U_SHAPE_LENGTH_GROW_SHRINK 0 + +/** + * Memory option: allow the result to have a different length than the source. + * Affects: LamAlef options + * This option is an alias to U_SHAPE_LENGTH_GROW_SHRINK + * @stable ICU 4.2 + */ +#define U_SHAPE_LAMALEF_RESIZE 0 + +/** + * Memory option: the result must have the same length as the source. + * If more room is necessary, then try to consume spaces next to modified characters. + * @stable ICU 2.0 + */ +#define U_SHAPE_LENGTH_FIXED_SPACES_NEAR 1 + +/** + * Memory option: the result must have the same length as the source. + * If more room is necessary, then try to consume spaces next to modified characters. + * Affects: LamAlef options + * This option is an alias to U_SHAPE_LENGTH_FIXED_SPACES_NEAR + * @stable ICU 4.2 + */ +#define U_SHAPE_LAMALEF_NEAR 1 + +/** + * Memory option: the result must have the same length as the source. + * If more room is necessary, then try to consume spaces at the end of the text. + * @stable ICU 2.0 + */ +#define U_SHAPE_LENGTH_FIXED_SPACES_AT_END 2 + +/** + * Memory option: the result must have the same length as the source. + * If more room is necessary, then try to consume spaces at the end of the text. + * Affects: LamAlef options + * This option is an alias to U_SHAPE_LENGTH_FIXED_SPACES_AT_END + * @stable ICU 4.2 + */ +#define U_SHAPE_LAMALEF_END 2 + +/** + * Memory option: the result must have the same length as the source. + * If more room is necessary, then try to consume spaces at the beginning of the text. + * @stable ICU 2.0 + */ +#define U_SHAPE_LENGTH_FIXED_SPACES_AT_BEGINNING 3 + +/** + * Memory option: the result must have the same length as the source. + * If more room is necessary, then try to consume spaces at the beginning of the text. + * Affects: LamAlef options + * This option is an alias to U_SHAPE_LENGTH_FIXED_SPACES_AT_BEGINNING + * @stable ICU 4.2 + */ +#define U_SHAPE_LAMALEF_BEGIN 3 + + +/** + * Memory option: the result must have the same length as the source. + * Shaping Mode: For each LAMALEF character found, expand LAMALEF using space at end. + * If there is no space at end, use spaces at beginning of the buffer. If there + * is no space at beginning of the buffer, use spaces at the near (i.e. the space + * after the LAMALEF character). + * If there are no spaces found, an error U_NO_SPACE_AVAILABLE (as defined in utypes.h) + * will be set in pErrorCode + * + * Deshaping Mode: Perform the same function as the flag equals U_SHAPE_LAMALEF_END. + * Affects: LamAlef options + * @stable ICU 4.2 + */ +#define U_SHAPE_LAMALEF_AUTO 0x10000 + +/** Bit mask for memory options. @stable ICU 2.0 */ +#define U_SHAPE_LENGTH_MASK 0x10003 /* Changed old value 3 */ + + +/** + * Bit mask for LamAlef memory options. + * @stable ICU 4.2 + */ +#define U_SHAPE_LAMALEF_MASK 0x10003 /* updated */ + +/** Direction indicator: the source is in logical (keyboard) order. @stable ICU 2.0 */ +#define U_SHAPE_TEXT_DIRECTION_LOGICAL 0 + +/** + * Direction indicator: + * the source is in visual RTL order, + * the rightmost displayed character stored first. + * This option is an alias to U_SHAPE_TEXT_DIRECTION_LOGICAL + * @stable ICU 4.2 + */ +#define U_SHAPE_TEXT_DIRECTION_VISUAL_RTL 0 + +/** + * Direction indicator: + * the source is in visual LTR order, + * the leftmost displayed character stored first. + * @stable ICU 2.0 + */ +#define U_SHAPE_TEXT_DIRECTION_VISUAL_LTR 4 + +/** Bit mask for direction indicators. @stable ICU 2.0 */ +#define U_SHAPE_TEXT_DIRECTION_MASK 4 + + +/** Letter shaping option: do not perform letter shaping. @stable ICU 2.0 */ +#define U_SHAPE_LETTERS_NOOP 0 + +/** Letter shaping option: replace abstract letter characters by "shaped" ones. @stable ICU 2.0 */ +#define U_SHAPE_LETTERS_SHAPE 8 + +/** Letter shaping option: replace "shaped" letter characters by abstract ones. @stable ICU 2.0 */ +#define U_SHAPE_LETTERS_UNSHAPE 0x10 + +/** + * Letter shaping option: replace abstract letter characters by "shaped" ones. + * The only difference with U_SHAPE_LETTERS_SHAPE is that Tashkeel letters + * are always "shaped" into the isolated form instead of the medial form + * (selecting code points from the Arabic Presentation Forms-B block). + * @stable ICU 2.0 + */ +#define U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED 0x18 + + +/** Bit mask for letter shaping options. @stable ICU 2.0 */ +#define U_SHAPE_LETTERS_MASK 0x18 + + +/** Digit shaping option: do not perform digit shaping. @stable ICU 2.0 */ +#define U_SHAPE_DIGITS_NOOP 0 + +/** + * Digit shaping option: + * Replace European digits (U+0030...) by Arabic-Indic digits. + * @stable ICU 2.0 + */ +#define U_SHAPE_DIGITS_EN2AN 0x20 + +/** + * Digit shaping option: + * Replace Arabic-Indic digits by European digits (U+0030...). + * @stable ICU 2.0 + */ +#define U_SHAPE_DIGITS_AN2EN 0x40 + +/** + * Digit shaping option: + * Replace European digits (U+0030...) by Arabic-Indic digits if the most recent + * strongly directional character is an Arabic letter + * (u_charDirection() result U_RIGHT_TO_LEFT_ARABIC [AL]).
+ * The direction of "preceding" depends on the direction indicator option. + * For the first characters, the preceding strongly directional character + * (initial state) is assumed to be not an Arabic letter + * (it is U_LEFT_TO_RIGHT [L] or U_RIGHT_TO_LEFT [R]). + * @stable ICU 2.0 + */ +#define U_SHAPE_DIGITS_ALEN2AN_INIT_LR 0x60 + +/** + * Digit shaping option: + * Replace European digits (U+0030...) by Arabic-Indic digits if the most recent + * strongly directional character is an Arabic letter + * (u_charDirection() result U_RIGHT_TO_LEFT_ARABIC [AL]).
+ * The direction of "preceding" depends on the direction indicator option. + * For the first characters, the preceding strongly directional character + * (initial state) is assumed to be an Arabic letter. + * @stable ICU 2.0 + */ +#define U_SHAPE_DIGITS_ALEN2AN_INIT_AL 0x80 + +/** Not a valid option value. May be replaced by a new option. @stable ICU 2.0 */ +#define U_SHAPE_DIGITS_RESERVED 0xa0 + +/** Bit mask for digit shaping options. @stable ICU 2.0 */ +#define U_SHAPE_DIGITS_MASK 0xe0 + + +/** Digit type option: Use Arabic-Indic digits (U+0660...U+0669). @stable ICU 2.0 */ +#define U_SHAPE_DIGIT_TYPE_AN 0 + +/** Digit type option: Use Eastern (Extended) Arabic-Indic digits (U+06f0...U+06f9). @stable ICU 2.0 */ +#define U_SHAPE_DIGIT_TYPE_AN_EXTENDED 0x100 + +/** Not a valid option value. May be replaced by a new option. @stable ICU 2.0 */ +#define U_SHAPE_DIGIT_TYPE_RESERVED 0x200 + +/** Bit mask for digit type options. @stable ICU 2.0 */ +#define U_SHAPE_DIGIT_TYPE_MASK 0x300 /* I need to change this from 0x3f00 to 0x300 */ + +/** + * Tashkeel aggregation option: + * Replaces any combination of U+0651 with one of + * U+064C, U+064D, U+064E, U+064F, U+0650 with + * U+FC5E, U+FC5F, U+FC60, U+FC61, U+FC62 consecutively. + * @stable ICU 3.6 + */ +#define U_SHAPE_AGGREGATE_TASHKEEL 0x4000 +/** Tashkeel aggregation option: do not aggregate tashkeels. @stable ICU 3.6 */ +#define U_SHAPE_AGGREGATE_TASHKEEL_NOOP 0 +/** Bit mask for tashkeel aggregation. @stable ICU 3.6 */ +#define U_SHAPE_AGGREGATE_TASHKEEL_MASK 0x4000 + +/** + * Presentation form option: + * Don't replace Arabic Presentation Forms-A and Arabic Presentation Forms-B + * characters with 0+06xx characters, before shaping. + * @stable ICU 3.6 + */ +#define U_SHAPE_PRESERVE_PRESENTATION 0x8000 +/** Presentation form option: + * Replace Arabic Presentation Forms-A and Arabic Presentationo Forms-B with + * their unshaped correspondents in range 0+06xx, before shaping. + * @stable ICU 3.6 + */ +#define U_SHAPE_PRESERVE_PRESENTATION_NOOP 0 +/** Bit mask for preserve presentation form. @stable ICU 3.6 */ +#define U_SHAPE_PRESERVE_PRESENTATION_MASK 0x8000 + +/* Seen Tail option */ +/** + * Memory option: the result must have the same length as the source. + * Shaping mode: The SEEN family character will expand into two characters using space near + * the SEEN family character(i.e. the space after the character). + * If there are no spaces found, an error U_NO_SPACE_AVAILABLE (as defined in utypes.h) + * will be set in pErrorCode + * + * De-shaping mode: Any Seen character followed by Tail character will be + * replaced by one cell Seen and a space will replace the Tail. + * Affects: Seen options + * @stable ICU 4.2 + */ +#define U_SHAPE_SEEN_TWOCELL_NEAR 0x200000 + +/** + * Bit mask for Seen memory options. + * @stable ICU 4.2 + */ +#define U_SHAPE_SEEN_MASK 0x700000 + +/* YehHamza option */ +/** + * Memory option: the result must have the same length as the source. + * Shaping mode: The YEHHAMZA character will expand into two characters using space near it + * (i.e. the space after the character + * If there are no spaces found, an error U_NO_SPACE_AVAILABLE (as defined in utypes.h) + * will be set in pErrorCode + * + * De-shaping mode: Any Yeh (final or isolated) character followed by Hamza character will be + * replaced by one cell YehHamza and space will replace the Hamza. + * Affects: YehHamza options + * @stable ICU 4.2 + */ +#define U_SHAPE_YEHHAMZA_TWOCELL_NEAR 0x1000000 + + +/** + * Bit mask for YehHamza memory options. + * @stable ICU 4.2 + */ +#define U_SHAPE_YEHHAMZA_MASK 0x3800000 + +/* New Tashkeel options */ +/** + * Memory option: the result must have the same length as the source. + * Shaping mode: Tashkeel characters will be replaced by spaces. + * Spaces will be placed at beginning of the buffer + * + * De-shaping mode: N/A + * Affects: Tashkeel options + * @stable ICU 4.2 + */ +#define U_SHAPE_TASHKEEL_BEGIN 0x40000 + +/** + * Memory option: the result must have the same length as the source. + * Shaping mode: Tashkeel characters will be replaced by spaces. + * Spaces will be placed at end of the buffer + * + * De-shaping mode: N/A + * Affects: Tashkeel options + * @stable ICU 4.2 + */ +#define U_SHAPE_TASHKEEL_END 0x60000 + +/** + * Memory option: allow the result to have a different length than the source. + * Shaping mode: Tashkeel characters will be removed, buffer length will shrink. + * De-shaping mode: N/A + * + * Affect: Tashkeel options + * @stable ICU 4.2 + */ +#define U_SHAPE_TASHKEEL_RESIZE 0x80000 + +/** + * Memory option: the result must have the same length as the source. + * Shaping mode: Tashkeel characters will be replaced by Tatweel if it is connected to adjacent + * characters (i.e. shaped on Tatweel) or replaced by space if it is not connected. + * + * De-shaping mode: N/A + * Affects: YehHamza options + * @stable ICU 4.2 + */ +#define U_SHAPE_TASHKEEL_REPLACE_BY_TATWEEL 0xC0000 + +/** + * Bit mask for Tashkeel replacement with Space or Tatweel memory options. + * @stable ICU 4.2 + */ +#define U_SHAPE_TASHKEEL_MASK 0xE0000 + + +/* Space location Control options */ +/** + * This option affect the meaning of BEGIN and END options. if this option is not used the default + * for BEGIN and END will be as following: + * The Default (for both Visual LTR, Visual RTL and Logical Text) + * 1. BEGIN always refers to the start address of physical memory. + * 2. END always refers to the end address of physical memory. + * + * If this option is used it will swap the meaning of BEGIN and END only for Visual LTR text. + * + * The effect on BEGIN and END Memory Options will be as following: + * A. BEGIN For Visual LTR text: This will be the beginning (right side) of the visual text( + * corresponding to the physical memory address end for Visual LTR text, Same as END in + * default behavior) + * B. BEGIN For Logical text: Same as BEGIN in default behavior. + * C. END For Visual LTR text: This will be the end (left side) of the visual text (corresponding + * to the physical memory address beginning for Visual LTR text, Same as BEGIN in default behavior. + * D. END For Logical text: Same as END in default behavior). + * Affects: All LamAlef BEGIN, END and AUTO options. + * @stable ICU 4.2 + */ +#define U_SHAPE_SPACES_RELATIVE_TO_TEXT_BEGIN_END 0x4000000 + +/** + * Bit mask for swapping BEGIN and END for Visual LTR text + * @stable ICU 4.2 + */ +#define U_SHAPE_SPACES_RELATIVE_TO_TEXT_MASK 0x4000000 + +/** + * If this option is used, shaping will use the new Unicode code point for TAIL (i.e. 0xFE73). + * If this option is not specified (Default), old unofficial Unicode TAIL code point is used (i.e. 0x200B) + * De-shaping will not use this option as it will always search for both the new Unicode code point for the + * TAIL (i.e. 0xFE73) or the old unofficial Unicode TAIL code point (i.e. 0x200B) and de-shape the + * Seen-Family letter accordingly. + * + * Shaping Mode: Only shaping. + * De-shaping Mode: N/A. + * Affects: All Seen options + * @stable ICU 4.8 + */ +#define U_SHAPE_TAIL_NEW_UNICODE 0x8000000 + +/** + * Bit mask for new Unicode Tail option + * @stable ICU 4.8 + */ +#define U_SHAPE_TAIL_TYPE_MASK 0x8000000 + +#endif diff --git a/deps/icu-small/source/common/unicode/usprep.h b/deps/icu-small/source/common/unicode/usprep.h index f8a0f58e0de27f..6324e2bc79807c 100644 --- a/deps/icu-small/source/common/unicode/usprep.h +++ b/deps/icu-small/source/common/unicode/usprep.h @@ -1,274 +1,274 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ******************************************************************************* - * - * Copyright (C) 2003-2014, International Business Machines - * Corporation and others. All Rights Reserved. - * - ******************************************************************************* - * file name: usprep.h - * encoding: UTF-8 - * tab size: 8 (not used) - * indentation:4 - * - * created on: 2003jul2 - * created by: Ram Viswanadha - */ - -#ifndef __USPREP_H__ -#define __USPREP_H__ - -/** - * \file - * \brief C API: Implements the StringPrep algorithm. - */ - -#include "unicode/utypes.h" - -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#endif // U_SHOW_CPLUSPLUS_API - -/** - * - * StringPrep API implements the StingPrep framework as described by RFC 3454. - * StringPrep prepares Unicode strings for use in network protocols. - * Profiles of StingPrep are set of rules and data according to with the - * Unicode Strings are prepared. Each profiles contains tables which describe - * how a code point should be treated. The tables are broadly classified into - *
    - *
  • Unassigned Table: Contains code points that are unassigned - * in the Unicode Version supported by StringPrep. Currently - * RFC 3454 supports Unicode 3.2.
  • - *
  • Prohibited Table: Contains code points that are prohibited from - * the output of the StringPrep processing function.
  • - *
  • Mapping Table: Contains code points that are deleted from the output or case mapped.
  • - *
- * - * The procedure for preparing Unicode strings: - *
    - *
  1. Map: For each character in the input, check if it has a mapping - * and, if so, replace it with its mapping.
  2. - *
  3. Normalize: Possibly normalize the result of step 1 using Unicode - * normalization.
  4. - *
  5. Prohibit: Check for any characters that are not allowed in the - * output. If any are found, return an error.
  6. - *
  7. Check bidi: Possibly check for right-to-left characters, and if - * any are found, make sure that the whole string satisfies the - * requirements for bidirectional strings. If the string does not - * satisfy the requirements for bidirectional strings, return an - * error.
  8. - *
- * @author Ram Viswanadha - */ -#if !UCONFIG_NO_IDNA - -#include "unicode/parseerr.h" - -/** - * The StringPrep profile - * @stable ICU 2.8 - */ -typedef struct UStringPrepProfile UStringPrepProfile; - - -/** - * Option to prohibit processing of unassigned code points in the input - * - * @see usprep_prepare - * @stable ICU 2.8 - */ -#define USPREP_DEFAULT 0x0000 - -/** - * Option to allow processing of unassigned code points in the input - * - * @see usprep_prepare - * @stable ICU 2.8 - */ -#define USPREP_ALLOW_UNASSIGNED 0x0001 - -/** - * enums for the standard stringprep profile types - * supported by usprep_openByType. - * @see usprep_openByType - * @stable ICU 4.2 - */ -typedef enum UStringPrepProfileType { - /** - * RFC3491 Nameprep - * @stable ICU 4.2 - */ - USPREP_RFC3491_NAMEPREP, - /** - * RFC3530 nfs4_cs_prep - * @stable ICU 4.2 - */ - USPREP_RFC3530_NFS4_CS_PREP, - /** - * RFC3530 nfs4_cs_prep with case insensitive option - * @stable ICU 4.2 - */ - USPREP_RFC3530_NFS4_CS_PREP_CI, - /** - * RFC3530 nfs4_cis_prep - * @stable ICU 4.2 - */ - USPREP_RFC3530_NFS4_CIS_PREP, - /** - * RFC3530 nfs4_mixed_prep for prefix - * @stable ICU 4.2 - */ - USPREP_RFC3530_NFS4_MIXED_PREP_PREFIX, - /** - * RFC3530 nfs4_mixed_prep for suffix - * @stable ICU 4.2 - */ - USPREP_RFC3530_NFS4_MIXED_PREP_SUFFIX, - /** - * RFC3722 iSCSI - * @stable ICU 4.2 - */ - USPREP_RFC3722_ISCSI, - /** - * RFC3920 XMPP Nodeprep - * @stable ICU 4.2 - */ - USPREP_RFC3920_NODEPREP, - /** - * RFC3920 XMPP Resourceprep - * @stable ICU 4.2 - */ - USPREP_RFC3920_RESOURCEPREP, - /** - * RFC4011 Policy MIB Stringprep - * @stable ICU 4.2 - */ - USPREP_RFC4011_MIB, - /** - * RFC4013 SASLprep - * @stable ICU 4.2 - */ - USPREP_RFC4013_SASLPREP, - /** - * RFC4505 trace - * @stable ICU 4.2 - */ - USPREP_RFC4505_TRACE, - /** - * RFC4518 LDAP - * @stable ICU 4.2 - */ - USPREP_RFC4518_LDAP, - /** - * RFC4518 LDAP for case ignore, numeric and stored prefix - * matching rules - * @stable ICU 4.2 - */ - USPREP_RFC4518_LDAP_CI -} UStringPrepProfileType; - -/** - * Creates a StringPrep profile from the data file. - * - * @param path string containing the full path pointing to the directory - * where the profile reside followed by the package name - * e.g. "/usr/resource/my_app/profiles/mydata" on a Unix system. - * if NULL, ICU default data files will be used. - * @param fileName name of the profile file to be opened - * @param status ICU error code in/out parameter. Must not be NULL. - * Must fulfill U_SUCCESS before the function call. - * @return Pointer to UStringPrepProfile that is opened. Should be closed by - * calling usprep_close() - * @see usprep_close() - * @stable ICU 2.8 - */ -U_CAPI UStringPrepProfile* U_EXPORT2 -usprep_open(const char* path, - const char* fileName, - UErrorCode* status); - -/** - * Creates a StringPrep profile for the specified profile type. - * - * @param type The profile type - * @param status ICU error code in/out parameter. Must not be NULL. - * Must fulfill U_SUCCESS before the function call. - * @return Pointer to UStringPrepProfile that is opened. Should be closed by - * calling usprep_close() - * @see usprep_close() - * @stable ICU 4.2 - */ -U_CAPI UStringPrepProfile* U_EXPORT2 -usprep_openByType(UStringPrepProfileType type, - UErrorCode* status); - -/** - * Closes the profile - * @param profile The profile to close - * @stable ICU 2.8 - */ -U_CAPI void U_EXPORT2 -usprep_close(UStringPrepProfile* profile); - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUStringPrepProfilePointer - * "Smart pointer" class, closes a UStringPrepProfile via usprep_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUStringPrepProfilePointer, UStringPrepProfile, usprep_close); - -U_NAMESPACE_END - -#endif - -/** - * Prepare the input buffer for use in applications with the given profile. This operation maps, normalizes(NFKC), - * checks for prohibited and BiDi characters in the order defined by RFC 3454 - * depending on the options specified in the profile. - * - * @param prep The profile to use - * @param src Pointer to UChar buffer containing the string to prepare - * @param srcLength Number of characters in the source string - * @param dest Pointer to the destination buffer to receive the output - * @param destCapacity The capacity of destination array - * @param options A bit set of options: - * - * - USPREP_DEFAULT Prohibit processing of unassigned code points in the input - * - * - USPREP_ALLOW_UNASSIGNED Treat the unassigned code points are in the input - * as normal Unicode code points. - * - * @param parseError Pointer to UParseError struct to receive information on position - * of error if an error is encountered. Can be NULL. - * @param status ICU in/out error code parameter. - * U_INVALID_CHAR_FOUND if src contains - * unmatched single surrogates. - * U_INDEX_OUTOFBOUNDS_ERROR if src contains - * too many code points. - * U_BUFFER_OVERFLOW_ERROR if destCapacity is not enough - * @return The number of UChars in the destination buffer - * @stable ICU 2.8 - */ - -U_CAPI int32_t U_EXPORT2 -usprep_prepare( const UStringPrepProfile* prep, - const UChar* src, int32_t srcLength, - UChar* dest, int32_t destCapacity, - int32_t options, - UParseError* parseError, - UErrorCode* status ); - - -#endif /* #if !UCONFIG_NO_IDNA */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************************* + * + * Copyright (C) 2003-2014, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: usprep.h + * encoding: UTF-8 + * tab size: 8 (not used) + * indentation:4 + * + * created on: 2003jul2 + * created by: Ram Viswanadha + */ + +#ifndef __USPREP_H__ +#define __USPREP_H__ + +/** + * \file + * \brief C API: Implements the StringPrep algorithm. + */ + +#include "unicode/utypes.h" + +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#endif // U_SHOW_CPLUSPLUS_API + +/** + * + * StringPrep API implements the StingPrep framework as described by RFC 3454. + * StringPrep prepares Unicode strings for use in network protocols. + * Profiles of StingPrep are set of rules and data according to with the + * Unicode Strings are prepared. Each profiles contains tables which describe + * how a code point should be treated. The tables are broadly classified into + *
    + *
  • Unassigned Table: Contains code points that are unassigned + * in the Unicode Version supported by StringPrep. Currently + * RFC 3454 supports Unicode 3.2.
  • + *
  • Prohibited Table: Contains code points that are prohibited from + * the output of the StringPrep processing function.
  • + *
  • Mapping Table: Contains code points that are deleted from the output or case mapped.
  • + *
+ * + * The procedure for preparing Unicode strings: + *
    + *
  1. Map: For each character in the input, check if it has a mapping + * and, if so, replace it with its mapping.
  2. + *
  3. Normalize: Possibly normalize the result of step 1 using Unicode + * normalization.
  4. + *
  5. Prohibit: Check for any characters that are not allowed in the + * output. If any are found, return an error.
  6. + *
  7. Check bidi: Possibly check for right-to-left characters, and if + * any are found, make sure that the whole string satisfies the + * requirements for bidirectional strings. If the string does not + * satisfy the requirements for bidirectional strings, return an + * error.
  8. + *
+ * @author Ram Viswanadha + */ +#if !UCONFIG_NO_IDNA + +#include "unicode/parseerr.h" + +/** + * The StringPrep profile + * @stable ICU 2.8 + */ +typedef struct UStringPrepProfile UStringPrepProfile; + + +/** + * Option to prohibit processing of unassigned code points in the input + * + * @see usprep_prepare + * @stable ICU 2.8 + */ +#define USPREP_DEFAULT 0x0000 + +/** + * Option to allow processing of unassigned code points in the input + * + * @see usprep_prepare + * @stable ICU 2.8 + */ +#define USPREP_ALLOW_UNASSIGNED 0x0001 + +/** + * enums for the standard stringprep profile types + * supported by usprep_openByType. + * @see usprep_openByType + * @stable ICU 4.2 + */ +typedef enum UStringPrepProfileType { + /** + * RFC3491 Nameprep + * @stable ICU 4.2 + */ + USPREP_RFC3491_NAMEPREP, + /** + * RFC3530 nfs4_cs_prep + * @stable ICU 4.2 + */ + USPREP_RFC3530_NFS4_CS_PREP, + /** + * RFC3530 nfs4_cs_prep with case insensitive option + * @stable ICU 4.2 + */ + USPREP_RFC3530_NFS4_CS_PREP_CI, + /** + * RFC3530 nfs4_cis_prep + * @stable ICU 4.2 + */ + USPREP_RFC3530_NFS4_CIS_PREP, + /** + * RFC3530 nfs4_mixed_prep for prefix + * @stable ICU 4.2 + */ + USPREP_RFC3530_NFS4_MIXED_PREP_PREFIX, + /** + * RFC3530 nfs4_mixed_prep for suffix + * @stable ICU 4.2 + */ + USPREP_RFC3530_NFS4_MIXED_PREP_SUFFIX, + /** + * RFC3722 iSCSI + * @stable ICU 4.2 + */ + USPREP_RFC3722_ISCSI, + /** + * RFC3920 XMPP Nodeprep + * @stable ICU 4.2 + */ + USPREP_RFC3920_NODEPREP, + /** + * RFC3920 XMPP Resourceprep + * @stable ICU 4.2 + */ + USPREP_RFC3920_RESOURCEPREP, + /** + * RFC4011 Policy MIB Stringprep + * @stable ICU 4.2 + */ + USPREP_RFC4011_MIB, + /** + * RFC4013 SASLprep + * @stable ICU 4.2 + */ + USPREP_RFC4013_SASLPREP, + /** + * RFC4505 trace + * @stable ICU 4.2 + */ + USPREP_RFC4505_TRACE, + /** + * RFC4518 LDAP + * @stable ICU 4.2 + */ + USPREP_RFC4518_LDAP, + /** + * RFC4518 LDAP for case ignore, numeric and stored prefix + * matching rules + * @stable ICU 4.2 + */ + USPREP_RFC4518_LDAP_CI +} UStringPrepProfileType; + +/** + * Creates a StringPrep profile from the data file. + * + * @param path string containing the full path pointing to the directory + * where the profile reside followed by the package name + * e.g. "/usr/resource/my_app/profiles/mydata" on a Unix system. + * if NULL, ICU default data files will be used. + * @param fileName name of the profile file to be opened + * @param status ICU error code in/out parameter. Must not be NULL. + * Must fulfill U_SUCCESS before the function call. + * @return Pointer to UStringPrepProfile that is opened. Should be closed by + * calling usprep_close() + * @see usprep_close() + * @stable ICU 2.8 + */ +U_CAPI UStringPrepProfile* U_EXPORT2 +usprep_open(const char* path, + const char* fileName, + UErrorCode* status); + +/** + * Creates a StringPrep profile for the specified profile type. + * + * @param type The profile type + * @param status ICU error code in/out parameter. Must not be NULL. + * Must fulfill U_SUCCESS before the function call. + * @return Pointer to UStringPrepProfile that is opened. Should be closed by + * calling usprep_close() + * @see usprep_close() + * @stable ICU 4.2 + */ +U_CAPI UStringPrepProfile* U_EXPORT2 +usprep_openByType(UStringPrepProfileType type, + UErrorCode* status); + +/** + * Closes the profile + * @param profile The profile to close + * @stable ICU 2.8 + */ +U_CAPI void U_EXPORT2 +usprep_close(UStringPrepProfile* profile); + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUStringPrepProfilePointer + * "Smart pointer" class, closes a UStringPrepProfile via usprep_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUStringPrepProfilePointer, UStringPrepProfile, usprep_close); + +U_NAMESPACE_END + +#endif + +/** + * Prepare the input buffer for use in applications with the given profile. This operation maps, normalizes(NFKC), + * checks for prohibited and BiDi characters in the order defined by RFC 3454 + * depending on the options specified in the profile. + * + * @param prep The profile to use + * @param src Pointer to UChar buffer containing the string to prepare + * @param srcLength Number of characters in the source string + * @param dest Pointer to the destination buffer to receive the output + * @param destCapacity The capacity of destination array + * @param options A bit set of options: + * + * - USPREP_DEFAULT Prohibit processing of unassigned code points in the input + * + * - USPREP_ALLOW_UNASSIGNED Treat the unassigned code points are in the input + * as normal Unicode code points. + * + * @param parseError Pointer to UParseError struct to receive information on position + * of error if an error is encountered. Can be NULL. + * @param status ICU in/out error code parameter. + * U_INVALID_CHAR_FOUND if src contains + * unmatched single surrogates. + * U_INDEX_OUTOFBOUNDS_ERROR if src contains + * too many code points. + * U_BUFFER_OVERFLOW_ERROR if destCapacity is not enough + * @return The number of UChars in the destination buffer + * @stable ICU 2.8 + */ + +U_CAPI int32_t U_EXPORT2 +usprep_prepare( const UStringPrepProfile* prep, + const UChar* src, int32_t srcLength, + UChar* dest, int32_t destCapacity, + int32_t options, + UParseError* parseError, + UErrorCode* status ); + + +#endif /* #if !UCONFIG_NO_IDNA */ + +#endif diff --git a/deps/icu-small/source/common/unicode/ustring.h b/deps/icu-small/source/common/unicode/ustring.h index 5452fbe09a8199..d637586fb1bd08 100644 --- a/deps/icu-small/source/common/unicode/ustring.h +++ b/deps/icu-small/source/common/unicode/ustring.h @@ -1,1689 +1,1685 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1998-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* File ustring.h -* -* Modification History: -* -* Date Name Description -* 12/07/98 bertrand Creation. -****************************************************************************** -*/ - -#ifndef USTRING_H -#define USTRING_H - -#include "unicode/utypes.h" -#include "unicode/putil.h" -#include "unicode/uiter.h" - -/** - * \def UBRK_TYPEDEF_UBREAK_ITERATOR - * @internal - */ - -#ifndef UBRK_TYPEDEF_UBREAK_ITERATOR -# define UBRK_TYPEDEF_UBREAK_ITERATOR -/** Simple declaration for u_strToTitle() to avoid including unicode/ubrk.h. @stable ICU 2.1*/ - typedef struct UBreakIterator UBreakIterator; -#endif - -/** - * \file - * \brief C API: Unicode string handling functions - * - * These C API functions provide general Unicode string handling. - * - * Some functions are equivalent in name, signature, and behavior to the ANSI C - * functions. (For example, they do not check for bad arguments like NULL string pointers.) - * In some cases, only the thread-safe variant of such a function is implemented here - * (see u_strtok_r()). - * - * Other functions provide more Unicode-specific functionality like locale-specific - * upper/lower-casing and string comparison in code point order. - * - * ICU uses 16-bit Unicode (UTF-16) in the form of arrays of UChar code units. - * UTF-16 encodes each Unicode code point with either one or two UChar code units. - * (This is the default form of Unicode, and a forward-compatible extension of the original, - * fixed-width form that was known as UCS-2. UTF-16 superseded UCS-2 with Unicode 2.0 - * in 1996.) - * - * Some APIs accept a 32-bit UChar32 value for a single code point. - * - * ICU also handles 16-bit Unicode text with unpaired surrogates. - * Such text is not well-formed UTF-16. - * Code-point-related functions treat unpaired surrogates as surrogate code points, - * i.e., as separate units. - * - * Although UTF-16 is a variable-width encoding form (like some legacy multi-byte encodings), - * it is much more efficient even for random access because the code unit values - * for single-unit characters vs. lead units vs. trail units are completely disjoint. - * This means that it is easy to determine character (code point) boundaries from - * random offsets in the string. - * - * Unicode (UTF-16) string processing is optimized for the single-unit case. - * Although it is important to support supplementary characters - * (which use pairs of lead/trail code units called "surrogates"), - * their occurrence is rare. Almost all characters in modern use require only - * a single UChar code unit (i.e., their code point values are <=0xffff). - * - * For more details see the User Guide Strings chapter (https://unicode-org.github.io/icu/userguide/strings/). - * For a discussion of the handling of unpaired surrogates see also - * Jitterbug 2145 and its icu mailing list proposal on 2002-sep-18. - */ - -/** - * \defgroup ustring_ustrlen String Length - * \ingroup ustring_strlen - */ -/*@{*/ -/** - * Determine the length of an array of UChar. - * - * @param s The array of UChars, NULL (U+0000) terminated. - * @return The number of UChars in chars, minus the terminator. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_strlen(const UChar *s); -/*@}*/ - -/** - * Count Unicode code points in the length UChar code units of the string. - * A code point may occupy either one or two UChar code units. - * Counting code points involves reading all code units. - * - * This functions is basically the inverse of the U16_FWD_N() macro (see utf.h). - * - * @param s The input string. - * @param length The number of UChar code units to be checked, or -1 to count all - * code points before the first NUL (U+0000). - * @return The number of code points in the specified code units. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_countChar32(const UChar *s, int32_t length); - -/** - * Check if the string contains more Unicode code points than a certain number. - * This is more efficient than counting all code points in the entire string - * and comparing that number with a threshold. - * This function may not need to scan the string at all if the length is known - * (not -1 for NUL-termination) and falls within a certain range, and - * never needs to count more than 'number+1' code points. - * Logically equivalent to (u_countChar32(s, length)>number). - * A Unicode code point may occupy either one or two UChar code units. - * - * @param s The input string. - * @param length The length of the string, or -1 if it is NUL-terminated. - * @param number The number of code points in the string is compared against - * the 'number' parameter. - * @return Boolean value for whether the string contains more Unicode code points - * than 'number'. Same as (u_countChar32(s, length)>number). - * @stable ICU 2.4 - */ -U_CAPI UBool U_EXPORT2 -u_strHasMoreChar32Than(const UChar *s, int32_t length, int32_t number); - -/** - * Concatenate two ustrings. Appends a copy of src, - * including the null terminator, to dst. The initial copied - * character from src overwrites the null terminator in dst. - * - * @param dst The destination string. - * @param src The source string. - * @return A pointer to dst. - * @stable ICU 2.0 - */ -U_CAPI UChar* U_EXPORT2 -u_strcat(UChar *dst, - const UChar *src); - -/** - * Concatenate two ustrings. - * Appends at most n characters from src to dst. - * Adds a terminating NUL. - * If src is too long, then only n-1 characters will be copied - * before the terminating NUL. - * If n<=0 then dst is not modified. - * - * @param dst The destination string. - * @param src The source string (can be NULL/invalid if n<=0). - * @param n The maximum number of characters to append; no-op if <=0. - * @return A pointer to dst. - * @stable ICU 2.0 - */ -U_CAPI UChar* U_EXPORT2 -u_strncat(UChar *dst, - const UChar *src, - int32_t n); - -/** - * Find the first occurrence of a substring in a string. - * The substring is found at code point boundaries. - * That means that if the substring begins with - * a trail surrogate or ends with a lead surrogate, - * then it is found only if these surrogates stand alone in the text. - * Otherwise, the substring edge units would be matched against - * halves of surrogate pairs. - * - * @param s The string to search (NUL-terminated). - * @param substring The substring to find (NUL-terminated). - * @return A pointer to the first occurrence of substring in s, - * or s itself if the substring is empty, - * or NULL if substring is not in s. - * @stable ICU 2.0 - * - * @see u_strrstr - * @see u_strFindFirst - * @see u_strFindLast - */ -U_CAPI UChar * U_EXPORT2 -u_strstr(const UChar *s, const UChar *substring); - -/** - * Find the first occurrence of a substring in a string. - * The substring is found at code point boundaries. - * That means that if the substring begins with - * a trail surrogate or ends with a lead surrogate, - * then it is found only if these surrogates stand alone in the text. - * Otherwise, the substring edge units would be matched against - * halves of surrogate pairs. - * - * @param s The string to search. - * @param length The length of s (number of UChars), or -1 if it is NUL-terminated. - * @param substring The substring to find (NUL-terminated). - * @param subLength The length of substring (number of UChars), or -1 if it is NUL-terminated. - * @return A pointer to the first occurrence of substring in s, - * or s itself if the substring is empty, - * or NULL if substring is not in s. - * @stable ICU 2.4 - * - * @see u_strstr - * @see u_strFindLast - */ -U_CAPI UChar * U_EXPORT2 -u_strFindFirst(const UChar *s, int32_t length, const UChar *substring, int32_t subLength); - -/** - * Find the first occurrence of a BMP code point in a string. - * A surrogate code point is found only if its match in the text is not - * part of a surrogate pair. - * A NUL character is found at the string terminator. - * - * @param s The string to search (NUL-terminated). - * @param c The BMP code point to find. - * @return A pointer to the first occurrence of c in s - * or NULL if c is not in s. - * @stable ICU 2.0 - * - * @see u_strchr32 - * @see u_memchr - * @see u_strstr - * @see u_strFindFirst - */ -U_CAPI UChar * U_EXPORT2 -u_strchr(const UChar *s, UChar c); - -/** - * Find the first occurrence of a code point in a string. - * A surrogate code point is found only if its match in the text is not - * part of a surrogate pair. - * A NUL character is found at the string terminator. - * - * @param s The string to search (NUL-terminated). - * @param c The code point to find. - * @return A pointer to the first occurrence of c in s - * or NULL if c is not in s. - * @stable ICU 2.0 - * - * @see u_strchr - * @see u_memchr32 - * @see u_strstr - * @see u_strFindFirst - */ -U_CAPI UChar * U_EXPORT2 -u_strchr32(const UChar *s, UChar32 c); - -/** - * Find the last occurrence of a substring in a string. - * The substring is found at code point boundaries. - * That means that if the substring begins with - * a trail surrogate or ends with a lead surrogate, - * then it is found only if these surrogates stand alone in the text. - * Otherwise, the substring edge units would be matched against - * halves of surrogate pairs. - * - * @param s The string to search (NUL-terminated). - * @param substring The substring to find (NUL-terminated). - * @return A pointer to the last occurrence of substring in s, - * or s itself if the substring is empty, - * or NULL if substring is not in s. - * @stable ICU 2.4 - * - * @see u_strstr - * @see u_strFindFirst - * @see u_strFindLast - */ -U_CAPI UChar * U_EXPORT2 -u_strrstr(const UChar *s, const UChar *substring); - -/** - * Find the last occurrence of a substring in a string. - * The substring is found at code point boundaries. - * That means that if the substring begins with - * a trail surrogate or ends with a lead surrogate, - * then it is found only if these surrogates stand alone in the text. - * Otherwise, the substring edge units would be matched against - * halves of surrogate pairs. - * - * @param s The string to search. - * @param length The length of s (number of UChars), or -1 if it is NUL-terminated. - * @param substring The substring to find (NUL-terminated). - * @param subLength The length of substring (number of UChars), or -1 if it is NUL-terminated. - * @return A pointer to the last occurrence of substring in s, - * or s itself if the substring is empty, - * or NULL if substring is not in s. - * @stable ICU 2.4 - * - * @see u_strstr - * @see u_strFindLast - */ -U_CAPI UChar * U_EXPORT2 -u_strFindLast(const UChar *s, int32_t length, const UChar *substring, int32_t subLength); - -/** - * Find the last occurrence of a BMP code point in a string. - * A surrogate code point is found only if its match in the text is not - * part of a surrogate pair. - * A NUL character is found at the string terminator. - * - * @param s The string to search (NUL-terminated). - * @param c The BMP code point to find. - * @return A pointer to the last occurrence of c in s - * or NULL if c is not in s. - * @stable ICU 2.4 - * - * @see u_strrchr32 - * @see u_memrchr - * @see u_strrstr - * @see u_strFindLast - */ -U_CAPI UChar * U_EXPORT2 -u_strrchr(const UChar *s, UChar c); - -/** - * Find the last occurrence of a code point in a string. - * A surrogate code point is found only if its match in the text is not - * part of a surrogate pair. - * A NUL character is found at the string terminator. - * - * @param s The string to search (NUL-terminated). - * @param c The code point to find. - * @return A pointer to the last occurrence of c in s - * or NULL if c is not in s. - * @stable ICU 2.4 - * - * @see u_strrchr - * @see u_memchr32 - * @see u_strrstr - * @see u_strFindLast - */ -U_CAPI UChar * U_EXPORT2 -u_strrchr32(const UChar *s, UChar32 c); - -/** - * Locates the first occurrence in the string string of any of the characters - * in the string matchSet. - * Works just like C's strpbrk but with Unicode. - * - * @param string The string in which to search, NUL-terminated. - * @param matchSet A NUL-terminated string defining a set of code points - * for which to search in the text string. - * @return A pointer to the character in string that matches one of the - * characters in matchSet, or NULL if no such character is found. - * @stable ICU 2.0 - */ -U_CAPI UChar * U_EXPORT2 -u_strpbrk(const UChar *string, const UChar *matchSet); - -/** - * Returns the number of consecutive characters in string, - * beginning with the first, that do not occur somewhere in matchSet. - * Works just like C's strcspn but with Unicode. - * - * @param string The string in which to search, NUL-terminated. - * @param matchSet A NUL-terminated string defining a set of code points - * for which to search in the text string. - * @return The number of initial characters in string that do not - * occur in matchSet. - * @see u_strspn - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_strcspn(const UChar *string, const UChar *matchSet); - -/** - * Returns the number of consecutive characters in string, - * beginning with the first, that occur somewhere in matchSet. - * Works just like C's strspn but with Unicode. - * - * @param string The string in which to search, NUL-terminated. - * @param matchSet A NUL-terminated string defining a set of code points - * for which to search in the text string. - * @return The number of initial characters in string that do - * occur in matchSet. - * @see u_strcspn - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_strspn(const UChar *string, const UChar *matchSet); - -/** - * The string tokenizer API allows an application to break a string into - * tokens. Unlike strtok(), the saveState (the current pointer within the - * original string) is maintained in saveState. In the first call, the - * argument src is a pointer to the string. In subsequent calls to - * return successive tokens of that string, src must be specified as - * NULL. The value saveState is set by this function to maintain the - * function's position within the string, and on each subsequent call - * you must give this argument the same variable. This function does - * handle surrogate pairs. This function is similar to the strtok_r() - * the POSIX Threads Extension (1003.1c-1995) version. - * - * @param src String containing token(s). This string will be modified. - * After the first call to u_strtok_r(), this argument must - * be NULL to get to the next token. - * @param delim Set of delimiter characters (Unicode code points). - * @param saveState The current pointer within the original string, - * which is set by this function. The saveState - * parameter should the address of a local variable of type - * UChar *. (i.e. defined "UChar *myLocalSaveState" and use - * &myLocalSaveState for this parameter). - * @return A pointer to the next token found in src, or NULL - * when there are no more tokens. - * @stable ICU 2.0 - */ -U_CAPI UChar * U_EXPORT2 -u_strtok_r(UChar *src, - const UChar *delim, - UChar **saveState); - -/** - * Compare two Unicode strings for bitwise equality (code unit order). - * - * @param s1 A string to compare. - * @param s2 A string to compare. - * @return 0 if s1 and s2 are bitwise equal; a negative - * value if s1 is bitwise less than s2,; a positive - * value if s1 is bitwise greater than s2. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_strcmp(const UChar *s1, - const UChar *s2); - -/** - * Compare two Unicode strings in code point order. - * See u_strCompare for details. - * - * @param s1 A string to compare. - * @param s2 A string to compare. - * @return a negative/zero/positive integer corresponding to whether - * the first string is less than/equal to/greater than the second one - * in code point order - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_strcmpCodePointOrder(const UChar *s1, const UChar *s2); - -/** - * Compare two Unicode strings (binary order). - * - * The comparison can be done in code unit order or in code point order. - * They differ only in UTF-16 when - * comparing supplementary code points (U+10000..U+10ffff) - * to BMP code points near the end of the BMP (i.e., U+e000..U+ffff). - * In code unit order, high BMP code points sort after supplementary code points - * because they are stored as pairs of surrogates which are at U+d800..U+dfff. - * - * This functions works with strings of different explicitly specified lengths - * unlike the ANSI C-like u_strcmp() and u_memcmp() etc. - * NUL-terminated strings are possible with length arguments of -1. - * - * @param s1 First source string. - * @param length1 Length of first source string, or -1 if NUL-terminated. - * - * @param s2 Second source string. - * @param length2 Length of second source string, or -1 if NUL-terminated. - * - * @param codePointOrder Choose between code unit order (false) - * and code point order (true). - * - * @return <0 or 0 or >0 as usual for string comparisons - * - * @stable ICU 2.2 - */ -U_CAPI int32_t U_EXPORT2 -u_strCompare(const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - UBool codePointOrder); - -/** - * Compare two Unicode strings (binary order) - * as presented by UCharIterator objects. - * Works otherwise just like u_strCompare(). - * - * Both iterators are reset to their start positions. - * When the function returns, it is undefined where the iterators - * have stopped. - * - * @param iter1 First source string iterator. - * @param iter2 Second source string iterator. - * @param codePointOrder Choose between code unit order (false) - * and code point order (true). - * - * @return <0 or 0 or >0 as usual for string comparisons - * - * @see u_strCompare - * - * @stable ICU 2.6 - */ -U_CAPI int32_t U_EXPORT2 -u_strCompareIter(UCharIterator *iter1, UCharIterator *iter2, UBool codePointOrder); - -/** - * Compare two strings case-insensitively using full case folding. - * This is equivalent to - * u_strCompare(u_strFoldCase(s1, options), - * u_strFoldCase(s2, options), - * (options&U_COMPARE_CODE_POINT_ORDER)!=0). - * - * The comparison can be done in UTF-16 code unit order or in code point order. - * They differ only when comparing supplementary code points (U+10000..U+10ffff) - * to BMP code points near the end of the BMP (i.e., U+e000..U+ffff). - * In code unit order, high BMP code points sort after supplementary code points - * because they are stored as pairs of surrogates which are at U+d800..U+dfff. - * - * This functions works with strings of different explicitly specified lengths - * unlike the ANSI C-like u_strcmp() and u_memcmp() etc. - * NUL-terminated strings are possible with length arguments of -1. - * - * @param s1 First source string. - * @param length1 Length of first source string, or -1 if NUL-terminated. - * - * @param s2 Second source string. - * @param length2 Length of second source string, or -1 if NUL-terminated. - * - * @param options A bit set of options: - * - U_FOLD_CASE_DEFAULT or 0 is used for default options: - * Comparison in code unit order with default case folding. - * - * - U_COMPARE_CODE_POINT_ORDER - * Set to choose code point order instead of code unit order - * (see u_strCompare for details). - * - * - U_FOLD_CASE_EXCLUDE_SPECIAL_I - * - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * - * @return <0 or 0 or >0 as usual for string comparisons - * - * @stable ICU 2.2 - */ -U_CAPI int32_t U_EXPORT2 -u_strCaseCompare(const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - uint32_t options, - UErrorCode *pErrorCode); - -/** - * Compare two ustrings for bitwise equality. - * Compares at most n characters. - * - * @param ucs1 A string to compare (can be NULL/invalid if n<=0). - * @param ucs2 A string to compare (can be NULL/invalid if n<=0). - * @param n The maximum number of characters to compare; always returns 0 if n<=0. - * @return 0 if s1 and s2 are bitwise equal; a negative - * value if s1 is bitwise less than s2; a positive - * value if s1 is bitwise greater than s2. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_strncmp(const UChar *ucs1, - const UChar *ucs2, - int32_t n); - -/** - * Compare two Unicode strings in code point order. - * This is different in UTF-16 from u_strncmp() if supplementary characters are present. - * For details, see u_strCompare(). - * - * @param s1 A string to compare. - * @param s2 A string to compare. - * @param n The maximum number of characters to compare. - * @return a negative/zero/positive integer corresponding to whether - * the first string is less than/equal to/greater than the second one - * in code point order - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_strncmpCodePointOrder(const UChar *s1, const UChar *s2, int32_t n); - -/** - * Compare two strings case-insensitively using full case folding. - * This is equivalent to u_strcmp(u_strFoldCase(s1, options), u_strFoldCase(s2, options)). - * - * @param s1 A string to compare. - * @param s2 A string to compare. - * @param options A bit set of options: - * - U_FOLD_CASE_DEFAULT or 0 is used for default options: - * Comparison in code unit order with default case folding. - * - * - U_COMPARE_CODE_POINT_ORDER - * Set to choose code point order instead of code unit order - * (see u_strCompare for details). - * - * - U_FOLD_CASE_EXCLUDE_SPECIAL_I - * - * @return A negative, zero, or positive integer indicating the comparison result. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_strcasecmp(const UChar *s1, const UChar *s2, uint32_t options); - -/** - * Compare two strings case-insensitively using full case folding. - * This is equivalent to u_strcmp(u_strFoldCase(s1, at most n, options), - * u_strFoldCase(s2, at most n, options)). - * - * @param s1 A string to compare. - * @param s2 A string to compare. - * @param n The maximum number of characters each string to case-fold and then compare. - * @param options A bit set of options: - * - U_FOLD_CASE_DEFAULT or 0 is used for default options: - * Comparison in code unit order with default case folding. - * - * - U_COMPARE_CODE_POINT_ORDER - * Set to choose code point order instead of code unit order - * (see u_strCompare for details). - * - * - U_FOLD_CASE_EXCLUDE_SPECIAL_I - * - * @return A negative, zero, or positive integer indicating the comparison result. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_strncasecmp(const UChar *s1, const UChar *s2, int32_t n, uint32_t options); - -/** - * Compare two strings case-insensitively using full case folding. - * This is equivalent to u_strcmp(u_strFoldCase(s1, n, options), - * u_strFoldCase(s2, n, options)). - * - * @param s1 A string to compare. - * @param s2 A string to compare. - * @param length The number of characters in each string to case-fold and then compare. - * @param options A bit set of options: - * - U_FOLD_CASE_DEFAULT or 0 is used for default options: - * Comparison in code unit order with default case folding. - * - * - U_COMPARE_CODE_POINT_ORDER - * Set to choose code point order instead of code unit order - * (see u_strCompare for details). - * - * - U_FOLD_CASE_EXCLUDE_SPECIAL_I - * - * @return A negative, zero, or positive integer indicating the comparison result. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_memcasecmp(const UChar *s1, const UChar *s2, int32_t length, uint32_t options); - -/** - * Copy a ustring. Adds a null terminator. - * - * @param dst The destination string. - * @param src The source string. - * @return A pointer to dst. - * @stable ICU 2.0 - */ -U_CAPI UChar* U_EXPORT2 -u_strcpy(UChar *dst, - const UChar *src); - -/** - * Copy a ustring. - * Copies at most n characters. The result will be null terminated - * if the length of src is less than n. - * - * @param dst The destination string. - * @param src The source string (can be NULL/invalid if n<=0). - * @param n The maximum number of characters to copy; no-op if <=0. - * @return A pointer to dst. - * @stable ICU 2.0 - */ -U_CAPI UChar* U_EXPORT2 -u_strncpy(UChar *dst, - const UChar *src, - int32_t n); - -#if !UCONFIG_NO_CONVERSION - -/** - * Copy a byte string encoded in the default codepage to a ustring. - * Adds a null terminator. - * Performs a host byte to UChar conversion - * - * @param dst The destination string. - * @param src The source string. - * @return A pointer to dst. - * @stable ICU 2.0 - */ -U_CAPI UChar* U_EXPORT2 u_uastrcpy(UChar *dst, - const char *src ); - -/** - * Copy a byte string encoded in the default codepage to a ustring. - * Copies at most n characters. The result will be null terminated - * if the length of src is less than n. - * Performs a host byte to UChar conversion - * - * @param dst The destination string. - * @param src The source string. - * @param n The maximum number of characters to copy. - * @return A pointer to dst. - * @stable ICU 2.0 - */ -U_CAPI UChar* U_EXPORT2 u_uastrncpy(UChar *dst, - const char *src, - int32_t n); - -/** - * Copy ustring to a byte string encoded in the default codepage. - * Adds a null terminator. - * Performs a UChar to host byte conversion - * - * @param dst The destination string. - * @param src The source string. - * @return A pointer to dst. - * @stable ICU 2.0 - */ -U_CAPI char* U_EXPORT2 u_austrcpy(char *dst, - const UChar *src ); - -/** - * Copy ustring to a byte string encoded in the default codepage. - * Copies at most n characters. The result will be null terminated - * if the length of src is less than n. - * Performs a UChar to host byte conversion - * - * @param dst The destination string. - * @param src The source string. - * @param n The maximum number of characters to copy. - * @return A pointer to dst. - * @stable ICU 2.0 - */ -U_CAPI char* U_EXPORT2 u_austrncpy(char *dst, - const UChar *src, - int32_t n ); - -#endif - -/** - * Synonym for memcpy(), but with UChars only. - * @param dest The destination string - * @param src The source string (can be NULL/invalid if count<=0) - * @param count The number of characters to copy; no-op if <=0 - * @return A pointer to dest - * @stable ICU 2.0 - */ -U_CAPI UChar* U_EXPORT2 -u_memcpy(UChar *dest, const UChar *src, int32_t count); - -/** - * Synonym for memmove(), but with UChars only. - * @param dest The destination string - * @param src The source string (can be NULL/invalid if count<=0) - * @param count The number of characters to move; no-op if <=0 - * @return A pointer to dest - * @stable ICU 2.0 - */ -U_CAPI UChar* U_EXPORT2 -u_memmove(UChar *dest, const UChar *src, int32_t count); - -/** - * Initialize count characters of dest to c. - * - * @param dest The destination string. - * @param c The character to initialize the string. - * @param count The maximum number of characters to set. - * @return A pointer to dest. - * @stable ICU 2.0 - */ -U_CAPI UChar* U_EXPORT2 -u_memset(UChar *dest, UChar c, int32_t count); - -/** - * Compare the first count UChars of each buffer. - * - * @param buf1 The first string to compare. - * @param buf2 The second string to compare. - * @param count The maximum number of UChars to compare. - * @return When buf1 < buf2, a negative number is returned. - * When buf1 == buf2, 0 is returned. - * When buf1 > buf2, a positive number is returned. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_memcmp(const UChar *buf1, const UChar *buf2, int32_t count); - -/** - * Compare two Unicode strings in code point order. - * This is different in UTF-16 from u_memcmp() if supplementary characters are present. - * For details, see u_strCompare(). - * - * @param s1 A string to compare. - * @param s2 A string to compare. - * @param count The maximum number of characters to compare. - * @return a negative/zero/positive integer corresponding to whether - * the first string is less than/equal to/greater than the second one - * in code point order - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_memcmpCodePointOrder(const UChar *s1, const UChar *s2, int32_t count); - -/** - * Find the first occurrence of a BMP code point in a string. - * A surrogate code point is found only if its match in the text is not - * part of a surrogate pair. - * A NUL character is found at the string terminator. - * - * @param s The string to search (contains count UChars). - * @param c The BMP code point to find. - * @param count The length of the string. - * @return A pointer to the first occurrence of c in s - * or NULL if c is not in s. - * @stable ICU 2.0 - * - * @see u_strchr - * @see u_memchr32 - * @see u_strFindFirst - */ -U_CAPI UChar* U_EXPORT2 -u_memchr(const UChar *s, UChar c, int32_t count); - -/** - * Find the first occurrence of a code point in a string. - * A surrogate code point is found only if its match in the text is not - * part of a surrogate pair. - * A NUL character is found at the string terminator. - * - * @param s The string to search (contains count UChars). - * @param c The code point to find. - * @param count The length of the string. - * @return A pointer to the first occurrence of c in s - * or NULL if c is not in s. - * @stable ICU 2.0 - * - * @see u_strchr32 - * @see u_memchr - * @see u_strFindFirst - */ -U_CAPI UChar* U_EXPORT2 -u_memchr32(const UChar *s, UChar32 c, int32_t count); - -/** - * Find the last occurrence of a BMP code point in a string. - * A surrogate code point is found only if its match in the text is not - * part of a surrogate pair. - * A NUL character is found at the string terminator. - * - * @param s The string to search (contains count UChars). - * @param c The BMP code point to find. - * @param count The length of the string. - * @return A pointer to the last occurrence of c in s - * or NULL if c is not in s. - * @stable ICU 2.4 - * - * @see u_strrchr - * @see u_memrchr32 - * @see u_strFindLast - */ -U_CAPI UChar* U_EXPORT2 -u_memrchr(const UChar *s, UChar c, int32_t count); - -/** - * Find the last occurrence of a code point in a string. - * A surrogate code point is found only if its match in the text is not - * part of a surrogate pair. - * A NUL character is found at the string terminator. - * - * @param s The string to search (contains count UChars). - * @param c The code point to find. - * @param count The length of the string. - * @return A pointer to the last occurrence of c in s - * or NULL if c is not in s. - * @stable ICU 2.4 - * - * @see u_strrchr32 - * @see u_memrchr - * @see u_strFindLast - */ -U_CAPI UChar* U_EXPORT2 -u_memrchr32(const UChar *s, UChar32 c, int32_t count); - -/** - * Unicode String literals in C. - * We need one macro to declare a variable for the string - * and to statically preinitialize it if possible, - * and a second macro to dynamically initialize such a string variable if necessary. - * - * The macros are defined for maximum performance. - * They work only for strings that contain "invariant characters", i.e., - * only latin letters, digits, and some punctuation. - * See utypes.h for details. - * - * A pair of macros for a single string must be used with the same - * parameters. - * The string parameter must be a C string literal. - * The length of the string, not including the terminating - * `NUL`, must be specified as a constant. - * The U_STRING_DECL macro should be invoked exactly once for one - * such string variable before it is used. - * - * Usage: - * - * U_STRING_DECL(ustringVar1, "Quick-Fox 2", 11); - * U_STRING_DECL(ustringVar2, "jumps 5%", 8); - * static UBool didInit=false; - * - * int32_t function() { - * if(!didInit) { - * U_STRING_INIT(ustringVar1, "Quick-Fox 2", 11); - * U_STRING_INIT(ustringVar2, "jumps 5%", 8); - * didInit=true; - * } - * return u_strcmp(ustringVar1, ustringVar2); - * } - * - * Note that the macros will NOT consistently work if their argument is another #`define`. - * The following will not work on all platforms, don't use it. - * - * #define GLUCK "Mr. Gluck" - * U_STRING_DECL(var, GLUCK, 9) - * U_STRING_INIT(var, GLUCK, 9) - * - * Instead, use the string literal "Mr. Gluck" as the argument to both macro - * calls. - * - * - * @stable ICU 2.0 - */ -#if defined(U_DECLARE_UTF16) -# define U_STRING_DECL(var, cs, length) static const UChar *var=(const UChar *)U_DECLARE_UTF16(cs) - /**@stable ICU 2.0 */ -# define U_STRING_INIT(var, cs, length) -#elif U_SIZEOF_WCHAR_T==U_SIZEOF_UCHAR && (U_CHARSET_FAMILY==U_ASCII_FAMILY || (U_SIZEOF_UCHAR == 2 && defined(U_WCHAR_IS_UTF16))) -# define U_STRING_DECL(var, cs, length) static const UChar var[(length)+1]=L ## cs - /**@stable ICU 2.0 */ -# define U_STRING_INIT(var, cs, length) -#elif U_SIZEOF_UCHAR==1 && U_CHARSET_FAMILY==U_ASCII_FAMILY -# define U_STRING_DECL(var, cs, length) static const UChar var[(length)+1]=cs - /**@stable ICU 2.0 */ -# define U_STRING_INIT(var, cs, length) -#else -# define U_STRING_DECL(var, cs, length) static UChar var[(length)+1] - /**@stable ICU 2.0 */ -# define U_STRING_INIT(var, cs, length) u_charsToUChars(cs, var, length+1) -#endif - -/** - * Unescape a string of characters and write the resulting - * Unicode characters to the destination buffer. The following escape - * sequences are recognized: - * - * \\uhhhh 4 hex digits; h in [0-9A-Fa-f] - * \\Uhhhhhhhh 8 hex digits - * \\xhh 1-2 hex digits - * \\x{h...} 1-8 hex digits - * \\ooo 1-3 octal digits; o in [0-7] - * \\cX control-X; X is masked with 0x1F - * - * as well as the standard ANSI C escapes: - * - * \\a => U+0007, \\b => U+0008, \\t => U+0009, \\n => U+000A, - * \\v => U+000B, \\f => U+000C, \\r => U+000D, \\e => U+001B, - * \\" => U+0022, \\' => U+0027, \\? => U+003F, \\\\ => U+005C - * - * Anything else following a backslash is generically escaped. For - * example, "[a\\-z]" returns "[a-z]". - * - * If an escape sequence is ill-formed, this method returns an empty - * string. An example of an ill-formed sequence is "\\u" followed by - * fewer than 4 hex digits. - * - * The above characters are recognized in the compiler's codepage, - * that is, they are coded as 'u', '\\', etc. Characters that are - * not parts of escape sequences are converted using u_charsToUChars(). - * - * This function is similar to UnicodeString::unescape() but not - * identical to it. The latter takes a source UnicodeString, so it - * does escape recognition but no conversion. - * - * @param src a zero-terminated string of invariant characters - * @param dest pointer to buffer to receive converted and unescaped - * text and, if there is room, a zero terminator. May be NULL for - * preflighting, in which case no UChars will be written, but the - * return value will still be valid. On error, an empty string is - * stored here (if possible). - * @param destCapacity the number of UChars that may be written at - * dest. Ignored if dest == NULL. - * @return the length of unescaped string. - * @see u_unescapeAt - * @see UnicodeString#unescape() - * @see UnicodeString#unescapeAt() - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_unescape(const char *src, - UChar *dest, int32_t destCapacity); - -U_CDECL_BEGIN -/** - * Callback function for u_unescapeAt() that returns a character of - * the source text given an offset and a context pointer. The context - * pointer will be whatever is passed into u_unescapeAt(). - * - * @param offset pointer to the offset that will be passed to u_unescapeAt(). - * @param context an opaque pointer passed directly into u_unescapeAt() - * @return the character represented by the escape sequence at - * offset - * @see u_unescapeAt - * @stable ICU 2.0 - */ -typedef UChar (U_CALLCONV *UNESCAPE_CHAR_AT)(int32_t offset, void *context); -U_CDECL_END - -/** - * Unescape a single sequence. The character at offset-1 is assumed - * (without checking) to be a backslash. This method takes a callback - * pointer to a function that returns the UChar at a given offset. By - * varying this callback, ICU functions are able to unescape char* - * strings, UnicodeString objects, and UFILE pointers. - * - * If offset is out of range, or if the escape sequence is ill-formed, - * (UChar32)0xFFFFFFFF is returned. See documentation of u_unescape() - * for a list of recognized sequences. - * - * @param charAt callback function that returns a UChar of the source - * text given an offset and a context pointer. - * @param offset pointer to the offset that will be passed to charAt. - * The offset value will be updated upon return to point after the - * last parsed character of the escape sequence. On error the offset - * is unchanged. - * @param length the number of characters in the source text. The - * last character of the source text is considered to be at offset - * length-1. - * @param context an opaque pointer passed directly into charAt. - * @return the character represented by the escape sequence at - * offset, or (UChar32)0xFFFFFFFF on error. - * @see u_unescape() - * @see UnicodeString#unescape() - * @see UnicodeString#unescapeAt() - * @stable ICU 2.0 - */ -U_CAPI UChar32 U_EXPORT2 -u_unescapeAt(UNESCAPE_CHAR_AT charAt, - int32_t *offset, - int32_t length, - void *context); - -/** - * Uppercase the characters in a string. - * Casing is locale-dependent and context-sensitive. - * The result may be longer or shorter than the original. - * The source string and the destination buffer are allowed to overlap. - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of UChars). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param src The original string - * @param srcLength The length of the original string. If -1, then src must be zero-terminated. - * @param locale The locale to consider, or "" for the root locale or NULL for the default locale. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return The length of the result string. It may be greater than destCapacity. In that case, - * only some of the result was written to the destination buffer. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_strToUpper(UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - const char *locale, - UErrorCode *pErrorCode); - -/** - * Lowercase the characters in a string. - * Casing is locale-dependent and context-sensitive. - * The result may be longer or shorter than the original. - * The source string and the destination buffer are allowed to overlap. - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of UChars). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param src The original string - * @param srcLength The length of the original string. If -1, then src must be zero-terminated. - * @param locale The locale to consider, or "" for the root locale or NULL for the default locale. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return The length of the result string. It may be greater than destCapacity. In that case, - * only some of the result was written to the destination buffer. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_strToLower(UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - const char *locale, - UErrorCode *pErrorCode); - -#if !UCONFIG_NO_BREAK_ITERATION - -/** - * Titlecase a string. - * Casing is locale-dependent and context-sensitive. - * Titlecasing uses a break iterator to find the first characters of words - * that are to be titlecased. It titlecases those characters and lowercases - * all others. - * - * The titlecase break iterator can be provided to customize for arbitrary - * styles, using rules and dictionaries beyond the standard iterators. - * It may be more efficient to always provide an iterator to avoid - * opening and closing one for each string. - * The standard titlecase iterator for the root locale implements the - * algorithm of Unicode TR 21. - * - * This function uses only the setText(), first() and next() methods of the - * provided break iterator. - * - * The result may be longer or shorter than the original. - * The source string and the destination buffer are allowed to overlap. - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of UChars). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param src The original string - * @param srcLength The length of the original string. If -1, then src must be zero-terminated. - * @param titleIter A break iterator to find the first characters of words - * that are to be titlecased. - * If none is provided (NULL), then a standard titlecase - * break iterator is opened. - * @param locale The locale to consider, or "" for the root locale or NULL for the default locale. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return The length of the result string. It may be greater than destCapacity. In that case, - * only some of the result was written to the destination buffer. - * @stable ICU 2.1 - */ -U_CAPI int32_t U_EXPORT2 -u_strToTitle(UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - UBreakIterator *titleIter, - const char *locale, - UErrorCode *pErrorCode); - -#endif - -/** - * Case-folds the characters in a string. - * - * Case-folding is locale-independent and not context-sensitive, - * but there is an option for whether to include or exclude mappings for dotted I - * and dotless i that are marked with 'T' in CaseFolding.txt. - * - * The result may be longer or shorter than the original. - * The source string and the destination buffer are allowed to overlap. - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of UChars). If it is 0, then - * dest may be NULL and the function will only return the length of the result - * without writing any of the result string. - * @param src The original string - * @param srcLength The length of the original string. If -1, then src must be zero-terminated. - * @param options Either U_FOLD_CASE_DEFAULT or U_FOLD_CASE_EXCLUDE_SPECIAL_I - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return The length of the result string. It may be greater than destCapacity. In that case, - * only some of the result was written to the destination buffer. - * @stable ICU 2.0 - */ -U_CAPI int32_t U_EXPORT2 -u_strFoldCase(UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - uint32_t options, - UErrorCode *pErrorCode); - -#if defined(U_WCHAR_IS_UTF16) || defined(U_WCHAR_IS_UTF32) || !UCONFIG_NO_CONVERSION -/** - * Convert a UTF-16 string to a wchar_t string. - * If it is known at compile time that wchar_t strings are in UTF-16 or UTF-32, then - * this function simply calls the fast, dedicated function for that. - * Otherwise, two conversions UTF-16 -> default charset -> wchar_t* are performed. - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of wchar_t's). If it is 0, then - * dest may be NULL and the function will only return the length of the - * result without writing any of the result string (pre-flighting). - * @param pDestLength A pointer to receive the number of units written to the destination. If - * pDestLength!=NULL then *pDestLength is always set to the - * number of output units corresponding to the transformation of - * all the input units, even in case of a buffer overflow. - * @param src The original source string - * @param srcLength The length of the original string. If -1, then src must be zero-terminated. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return The pointer to destination buffer. - * @stable ICU 2.0 - */ -U_CAPI wchar_t* U_EXPORT2 -u_strToWCS(wchar_t *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar *src, - int32_t srcLength, - UErrorCode *pErrorCode); -/** - * Convert a wchar_t string to UTF-16. - * If it is known at compile time that wchar_t strings are in UTF-16 or UTF-32, then - * this function simply calls the fast, dedicated function for that. - * Otherwise, two conversions wchar_t* -> default charset -> UTF-16 are performed. - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of UChars). If it is 0, then - * dest may be NULL and the function will only return the length of the - * result without writing any of the result string (pre-flighting). - * @param pDestLength A pointer to receive the number of units written to the destination. If - * pDestLength!=NULL then *pDestLength is always set to the - * number of output units corresponding to the transformation of - * all the input units, even in case of a buffer overflow. - * @param src The original source string - * @param srcLength The length of the original string. If -1, then src must be zero-terminated. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return The pointer to destination buffer. - * @stable ICU 2.0 - */ -U_CAPI UChar* U_EXPORT2 -u_strFromWCS(UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const wchar_t *src, - int32_t srcLength, - UErrorCode *pErrorCode); -#endif /* defined(U_WCHAR_IS_UTF16) || defined(U_WCHAR_IS_UTF32) || !UCONFIG_NO_CONVERSION */ - -/** - * Convert a UTF-16 string to UTF-8. - * If the input string is not well-formed, then the U_INVALID_CHAR_FOUND error code is set. - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of chars). If it is 0, then - * dest may be NULL and the function will only return the length of the - * result without writing any of the result string (pre-flighting). - * @param pDestLength A pointer to receive the number of units written to the destination. If - * pDestLength!=NULL then *pDestLength is always set to the - * number of output units corresponding to the transformation of - * all the input units, even in case of a buffer overflow. - * @param src The original source string - * @param srcLength The length of the original string. If -1, then src must be zero-terminated. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return The pointer to destination buffer. - * @stable ICU 2.0 - * @see u_strToUTF8WithSub - * @see u_strFromUTF8 - */ -U_CAPI char* U_EXPORT2 -u_strToUTF8(char *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar *src, - int32_t srcLength, - UErrorCode *pErrorCode); - -/** - * Convert a UTF-8 string to UTF-16. - * If the input string is not well-formed, then the U_INVALID_CHAR_FOUND error code is set. - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of UChars). If it is 0, then - * dest may be NULL and the function will only return the length of the - * result without writing any of the result string (pre-flighting). - * @param pDestLength A pointer to receive the number of units written to the destination. If - * pDestLength!=NULL then *pDestLength is always set to the - * number of output units corresponding to the transformation of - * all the input units, even in case of a buffer overflow. - * @param src The original source string - * @param srcLength The length of the original string. If -1, then src must be zero-terminated. - * @param pErrorCode Must be a valid pointer to an error code value, - * which must not indicate a failure before the function call. - * @return The pointer to destination buffer. - * @stable ICU 2.0 - * @see u_strFromUTF8WithSub - * @see u_strFromUTF8Lenient - */ -U_CAPI UChar* U_EXPORT2 -u_strFromUTF8(UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const char *src, - int32_t srcLength, - UErrorCode *pErrorCode); - -/** - * Convert a UTF-16 string to UTF-8. - * - * Same as u_strToUTF8() except for the additional subchar which is output for - * illegal input sequences, instead of stopping with the U_INVALID_CHAR_FOUND error code. - * With subchar==U_SENTINEL, this function behaves exactly like u_strToUTF8(). - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of chars). If it is 0, then - * dest may be NULL and the function will only return the length of the - * result without writing any of the result string (pre-flighting). - * @param pDestLength A pointer to receive the number of units written to the destination. If - * pDestLength!=NULL then *pDestLength is always set to the - * number of output units corresponding to the transformation of - * all the input units, even in case of a buffer overflow. - * @param src The original source string - * @param srcLength The length of the original string. If -1, then src must be zero-terminated. - * @param subchar The substitution character to use in place of an illegal input sequence, - * or U_SENTINEL if the function is to return with U_INVALID_CHAR_FOUND instead. - * A substitution character can be any valid Unicode code point (up to U+10FFFF) - * except for surrogate code points (U+D800..U+DFFF). - * The recommended value is U+FFFD "REPLACEMENT CHARACTER". - * @param pNumSubstitutions Output parameter receiving the number of substitutions if subchar>=0. - * Set to 0 if no substitutions occur or subchar<0. - * pNumSubstitutions can be NULL. - * @param pErrorCode Pointer to a standard 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.) - * @return The pointer to destination buffer. - * @see u_strToUTF8 - * @see u_strFromUTF8WithSub - * @stable ICU 3.6 - */ -U_CAPI char* U_EXPORT2 -u_strToUTF8WithSub(char *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar *src, - int32_t srcLength, - UChar32 subchar, int32_t *pNumSubstitutions, - UErrorCode *pErrorCode); - -/** - * Convert a UTF-8 string to UTF-16. - * - * Same as u_strFromUTF8() except for the additional subchar which is output for - * illegal input sequences, instead of stopping with the U_INVALID_CHAR_FOUND error code. - * With subchar==U_SENTINEL, this function behaves exactly like u_strFromUTF8(). - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of UChars). If it is 0, then - * dest may be NULL and the function will only return the length of the - * result without writing any of the result string (pre-flighting). - * @param pDestLength A pointer to receive the number of units written to the destination. If - * pDestLength!=NULL then *pDestLength is always set to the - * number of output units corresponding to the transformation of - * all the input units, even in case of a buffer overflow. - * @param src The original source string - * @param srcLength The length of the original string. If -1, then src must be zero-terminated. - * @param subchar The substitution character to use in place of an illegal input sequence, - * or U_SENTINEL if the function is to return with U_INVALID_CHAR_FOUND instead. - * A substitution character can be any valid Unicode code point (up to U+10FFFF) - * except for surrogate code points (U+D800..U+DFFF). - * The recommended value is U+FFFD "REPLACEMENT CHARACTER". - * @param pNumSubstitutions Output parameter receiving the number of substitutions if subchar>=0. - * Set to 0 if no substitutions occur or subchar<0. - * pNumSubstitutions can be NULL. - * @param pErrorCode Pointer to a standard 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.) - * @return The pointer to destination buffer. - * @see u_strFromUTF8 - * @see u_strFromUTF8Lenient - * @see u_strToUTF8WithSub - * @stable ICU 3.6 - */ -U_CAPI UChar* U_EXPORT2 -u_strFromUTF8WithSub(UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const char *src, - int32_t srcLength, - UChar32 subchar, int32_t *pNumSubstitutions, - UErrorCode *pErrorCode); - -/** - * Convert a UTF-8 string to UTF-16. - * - * Same as u_strFromUTF8() except that this function is designed to be very fast, - * which it achieves by being lenient about malformed UTF-8 sequences. - * This function is intended for use in environments where UTF-8 text is - * expected to be well-formed. - * - * Its semantics are: - * - Well-formed UTF-8 text is correctly converted to well-formed UTF-16 text. - * - The function will not read beyond the input string, nor write beyond - * the destCapacity. - * - Malformed UTF-8 results in "garbage" 16-bit Unicode strings which may not - * be well-formed UTF-16. - * The function will resynchronize to valid code point boundaries - * within a small number of code points after an illegal sequence. - * - Non-shortest forms are not detected and will result in "spoofing" output. - * - * For further performance improvement, if srcLength is given (>=0), - * then it must be destCapacity>=srcLength. - * - * There is no inverse u_strToUTF8Lenient() function because there is practically - * no performance gain from not checking that a UTF-16 string is well-formed. - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of UChars). If it is 0, then - * dest may be NULL and the function will only return the length of the - * result without writing any of the result string (pre-flighting). - * Unlike for other ICU functions, if srcLength>=0 then it - * must be destCapacity>=srcLength. - * @param pDestLength A pointer to receive the number of units written to the destination. If - * pDestLength!=NULL then *pDestLength is always set to the - * number of output units corresponding to the transformation of - * all the input units, even in case of a buffer overflow. - * Unlike for other ICU functions, if srcLength>=0 but - * destCapacity=0. - * Set to 0 if no substitutions occur or subchar<0. - * pNumSubstitutions can be NULL. - * @param pErrorCode Pointer to a standard 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.) - * @return The pointer to destination buffer. - * @see u_strToUTF32 - * @see u_strFromUTF32WithSub - * @stable ICU 4.2 - */ -U_CAPI UChar32* U_EXPORT2 -u_strToUTF32WithSub(UChar32 *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar *src, - int32_t srcLength, - UChar32 subchar, int32_t *pNumSubstitutions, - UErrorCode *pErrorCode); - -/** - * Convert a UTF-32 string to UTF-16. - * - * Same as u_strFromUTF32() except for the additional subchar which is output for - * illegal input sequences, instead of stopping with the U_INVALID_CHAR_FOUND error code. - * With subchar==U_SENTINEL, this function behaves exactly like u_strFromUTF32(). - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of UChars). If it is 0, then - * dest may be NULL and the function will only return the length of the - * result without writing any of the result string (pre-flighting). - * @param pDestLength A pointer to receive the number of units written to the destination. If - * pDestLength!=NULL then *pDestLength is always set to the - * number of output units corresponding to the transformation of - * all the input units, even in case of a buffer overflow. - * @param src The original source string - * @param srcLength The length of the original string. If -1, then src must be zero-terminated. - * @param subchar The substitution character to use in place of an illegal input sequence, - * or U_SENTINEL if the function is to return with U_INVALID_CHAR_FOUND instead. - * A substitution character can be any valid Unicode code point (up to U+10FFFF) - * except for surrogate code points (U+D800..U+DFFF). - * The recommended value is U+FFFD "REPLACEMENT CHARACTER". - * @param pNumSubstitutions Output parameter receiving the number of substitutions if subchar>=0. - * Set to 0 if no substitutions occur or subchar<0. - * pNumSubstitutions can be NULL. - * @param pErrorCode Pointer to a standard 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.) - * @return The pointer to destination buffer. - * @see u_strFromUTF32 - * @see u_strToUTF32WithSub - * @stable ICU 4.2 - */ -U_CAPI UChar* U_EXPORT2 -u_strFromUTF32WithSub(UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar32 *src, - int32_t srcLength, - UChar32 subchar, int32_t *pNumSubstitutions, - UErrorCode *pErrorCode); - -/** - * Convert a 16-bit Unicode string to Java Modified UTF-8. - * See http://java.sun.com/javase/6/docs/api/java/io/DataInput.html#modified-utf-8 - * - * This function behaves according to the documentation for Java DataOutput.writeUTF() - * except that it does not encode the output length in the destination buffer - * and does not have an output length restriction. - * See http://java.sun.com/javase/6/docs/api/java/io/DataOutput.html#writeUTF(java.lang.String) - * - * The input string need not be well-formed UTF-16. - * (Therefore there is no subchar parameter.) - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of chars). If it is 0, then - * dest may be NULL and the function will only return the length of the - * result without writing any of the result string (pre-flighting). - * @param pDestLength A pointer to receive the number of units written to the destination. If - * pDestLength!=NULL then *pDestLength is always set to the - * number of output units corresponding to the transformation of - * all the input units, even in case of a buffer overflow. - * @param src The original source string - * @param srcLength The length of the original string. If -1, then src must be zero-terminated. - * @param pErrorCode Pointer to a standard 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.) - * @return The pointer to destination buffer. - * @stable ICU 4.4 - * @see u_strToUTF8WithSub - * @see u_strFromJavaModifiedUTF8WithSub - */ -U_CAPI char* U_EXPORT2 -u_strToJavaModifiedUTF8( - char *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar *src, - int32_t srcLength, - UErrorCode *pErrorCode); - -/** - * Convert a Java Modified UTF-8 string to a 16-bit Unicode string. - * If the input string is not well-formed and no substitution char is specified, - * then the U_INVALID_CHAR_FOUND error code is set. - * - * This function behaves according to the documentation for Java DataInput.readUTF() - * except that it takes a length parameter rather than - * interpreting the first two input bytes as the length. - * See http://java.sun.com/javase/6/docs/api/java/io/DataInput.html#readUTF() - * - * The output string may not be well-formed UTF-16. - * - * @param dest A buffer for the result string. The result will be zero-terminated if - * the buffer is large enough. - * @param destCapacity The size of the buffer (number of UChars). If it is 0, then - * dest may be NULL and the function will only return the length of the - * result without writing any of the result string (pre-flighting). - * @param pDestLength A pointer to receive the number of units written to the destination. If - * pDestLength!=NULL then *pDestLength is always set to the - * number of output units corresponding to the transformation of - * all the input units, even in case of a buffer overflow. - * @param src The original source string - * @param srcLength The length of the original string. If -1, then src must be zero-terminated. - * @param subchar The substitution character to use in place of an illegal input sequence, - * or U_SENTINEL if the function is to return with U_INVALID_CHAR_FOUND instead. - * A substitution character can be any valid Unicode code point (up to U+10FFFF) - * except for surrogate code points (U+D800..U+DFFF). - * The recommended value is U+FFFD "REPLACEMENT CHARACTER". - * @param pNumSubstitutions Output parameter receiving the number of substitutions if subchar>=0. - * Set to 0 if no substitutions occur or subchar<0. - * pNumSubstitutions can be NULL. - * @param pErrorCode Pointer to a standard 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.) - * @return The pointer to destination buffer. - * @see u_strFromUTF8WithSub - * @see u_strFromUTF8Lenient - * @see u_strToJavaModifiedUTF8 - * @stable ICU 4.4 - */ -U_CAPI UChar* U_EXPORT2 -u_strFromJavaModifiedUTF8WithSub( - UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const char *src, - int32_t srcLength, - UChar32 subchar, int32_t *pNumSubstitutions, - UErrorCode *pErrorCode); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1998-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* File ustring.h +* +* Modification History: +* +* Date Name Description +* 12/07/98 bertrand Creation. +****************************************************************************** +*/ + +#ifndef USTRING_H +#define USTRING_H + +#include "unicode/utypes.h" +#include "unicode/putil.h" +#include "unicode/uiter.h" + +/** + * \def UBRK_TYPEDEF_UBREAK_ITERATOR + * @internal + */ + +#ifndef UBRK_TYPEDEF_UBREAK_ITERATOR +# define UBRK_TYPEDEF_UBREAK_ITERATOR +/** Simple declaration for u_strToTitle() to avoid including unicode/ubrk.h. @stable ICU 2.1*/ + typedef struct UBreakIterator UBreakIterator; +#endif + +/** + * \file + * \brief C API: Unicode string handling functions + * + * These C API functions provide general Unicode string handling. + * + * Some functions are equivalent in name, signature, and behavior to the ANSI C + * functions. (For example, they do not check for bad arguments like NULL string pointers.) + * In some cases, only the thread-safe variant of such a function is implemented here + * (see u_strtok_r()). + * + * Other functions provide more Unicode-specific functionality like locale-specific + * upper/lower-casing and string comparison in code point order. + * + * ICU uses 16-bit Unicode (UTF-16) in the form of arrays of UChar code units. + * UTF-16 encodes each Unicode code point with either one or two UChar code units. + * (This is the default form of Unicode, and a forward-compatible extension of the original, + * fixed-width form that was known as UCS-2. UTF-16 superseded UCS-2 with Unicode 2.0 + * in 1996.) + * + * Some APIs accept a 32-bit UChar32 value for a single code point. + * + * ICU also handles 16-bit Unicode text with unpaired surrogates. + * Such text is not well-formed UTF-16. + * Code-point-related functions treat unpaired surrogates as surrogate code points, + * i.e., as separate units. + * + * Although UTF-16 is a variable-width encoding form (like some legacy multi-byte encodings), + * it is much more efficient even for random access because the code unit values + * for single-unit characters vs. lead units vs. trail units are completely disjoint. + * This means that it is easy to determine character (code point) boundaries from + * random offsets in the string. + * + * Unicode (UTF-16) string processing is optimized for the single-unit case. + * Although it is important to support supplementary characters + * (which use pairs of lead/trail code units called "surrogates"), + * their occurrence is rare. Almost all characters in modern use require only + * a single UChar code unit (i.e., their code point values are <=0xffff). + * + * For more details see the User Guide Strings chapter (https://unicode-org.github.io/icu/userguide/strings/). + * For a discussion of the handling of unpaired surrogates see also + * Jitterbug 2145 and its icu mailing list proposal on 2002-sep-18. + */ + +/** + * \defgroup ustring_ustrlen String Length + * \ingroup ustring_strlen + */ +/*@{*/ +/** + * Determine the length of an array of UChar. + * + * @param s The array of UChars, NULL (U+0000) terminated. + * @return The number of UChars in chars, minus the terminator. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_strlen(const UChar *s); +/*@}*/ + +/** + * Count Unicode code points in the length UChar code units of the string. + * A code point may occupy either one or two UChar code units. + * Counting code points involves reading all code units. + * + * This functions is basically the inverse of the U16_FWD_N() macro (see utf.h). + * + * @param s The input string. + * @param length The number of UChar code units to be checked, or -1 to count all + * code points before the first NUL (U+0000). + * @return The number of code points in the specified code units. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_countChar32(const UChar *s, int32_t length); + +/** + * Check if the string contains more Unicode code points than a certain number. + * This is more efficient than counting all code points in the entire string + * and comparing that number with a threshold. + * This function may not need to scan the string at all if the length is known + * (not -1 for NUL-termination) and falls within a certain range, and + * never needs to count more than 'number+1' code points. + * Logically equivalent to (u_countChar32(s, length)>number). + * A Unicode code point may occupy either one or two UChar code units. + * + * @param s The input string. + * @param length The length of the string, or -1 if it is NUL-terminated. + * @param number The number of code points in the string is compared against + * the 'number' parameter. + * @return Boolean value for whether the string contains more Unicode code points + * than 'number'. Same as (u_countChar32(s, length)>number). + * @stable ICU 2.4 + */ +U_CAPI UBool U_EXPORT2 +u_strHasMoreChar32Than(const UChar *s, int32_t length, int32_t number); + +/** + * Concatenate two ustrings. Appends a copy of src, + * including the null terminator, to dst. The initial copied + * character from src overwrites the null terminator in dst. + * + * @param dst The destination string. + * @param src The source string. + * @return A pointer to dst. + * @stable ICU 2.0 + */ +U_CAPI UChar* U_EXPORT2 +u_strcat(UChar *dst, + const UChar *src); + +/** + * Concatenate two ustrings. + * Appends at most n characters from src to dst. + * Adds a terminating NUL. + * If src is too long, then only n-1 characters will be copied + * before the terminating NUL. + * If n<=0 then dst is not modified. + * + * @param dst The destination string. + * @param src The source string (can be NULL/invalid if n<=0). + * @param n The maximum number of characters to append; no-op if <=0. + * @return A pointer to dst. + * @stable ICU 2.0 + */ +U_CAPI UChar* U_EXPORT2 +u_strncat(UChar *dst, + const UChar *src, + int32_t n); + +/** + * Find the first occurrence of a substring in a string. + * The substring is found at code point boundaries. + * That means that if the substring begins with + * a trail surrogate or ends with a lead surrogate, + * then it is found only if these surrogates stand alone in the text. + * Otherwise, the substring edge units would be matched against + * halves of surrogate pairs. + * + * @param s The string to search (NUL-terminated). + * @param substring The substring to find (NUL-terminated). + * @return A pointer to the first occurrence of substring in s, + * or s itself if the substring is empty, + * or NULL if substring is not in s. + * @stable ICU 2.0 + * + * @see u_strrstr + * @see u_strFindFirst + * @see u_strFindLast + */ +U_CAPI UChar * U_EXPORT2 +u_strstr(const UChar *s, const UChar *substring); + +/** + * Find the first occurrence of a substring in a string. + * The substring is found at code point boundaries. + * That means that if the substring begins with + * a trail surrogate or ends with a lead surrogate, + * then it is found only if these surrogates stand alone in the text. + * Otherwise, the substring edge units would be matched against + * halves of surrogate pairs. + * + * @param s The string to search. + * @param length The length of s (number of UChars), or -1 if it is NUL-terminated. + * @param substring The substring to find (NUL-terminated). + * @param subLength The length of substring (number of UChars), or -1 if it is NUL-terminated. + * @return A pointer to the first occurrence of substring in s, + * or s itself if the substring is empty, + * or NULL if substring is not in s. + * @stable ICU 2.4 + * + * @see u_strstr + * @see u_strFindLast + */ +U_CAPI UChar * U_EXPORT2 +u_strFindFirst(const UChar *s, int32_t length, const UChar *substring, int32_t subLength); + +/** + * Find the first occurrence of a BMP code point in a string. + * A surrogate code point is found only if its match in the text is not + * part of a surrogate pair. + * A NUL character is found at the string terminator. + * + * @param s The string to search (NUL-terminated). + * @param c The BMP code point to find. + * @return A pointer to the first occurrence of c in s + * or NULL if c is not in s. + * @stable ICU 2.0 + * + * @see u_strchr32 + * @see u_memchr + * @see u_strstr + * @see u_strFindFirst + */ +U_CAPI UChar * U_EXPORT2 +u_strchr(const UChar *s, UChar c); + +/** + * Find the first occurrence of a code point in a string. + * A surrogate code point is found only if its match in the text is not + * part of a surrogate pair. + * A NUL character is found at the string terminator. + * + * @param s The string to search (NUL-terminated). + * @param c The code point to find. + * @return A pointer to the first occurrence of c in s + * or NULL if c is not in s. + * @stable ICU 2.0 + * + * @see u_strchr + * @see u_memchr32 + * @see u_strstr + * @see u_strFindFirst + */ +U_CAPI UChar * U_EXPORT2 +u_strchr32(const UChar *s, UChar32 c); + +/** + * Find the last occurrence of a substring in a string. + * The substring is found at code point boundaries. + * That means that if the substring begins with + * a trail surrogate or ends with a lead surrogate, + * then it is found only if these surrogates stand alone in the text. + * Otherwise, the substring edge units would be matched against + * halves of surrogate pairs. + * + * @param s The string to search (NUL-terminated). + * @param substring The substring to find (NUL-terminated). + * @return A pointer to the last occurrence of substring in s, + * or s itself if the substring is empty, + * or NULL if substring is not in s. + * @stable ICU 2.4 + * + * @see u_strstr + * @see u_strFindFirst + * @see u_strFindLast + */ +U_CAPI UChar * U_EXPORT2 +u_strrstr(const UChar *s, const UChar *substring); + +/** + * Find the last occurrence of a substring in a string. + * The substring is found at code point boundaries. + * That means that if the substring begins with + * a trail surrogate or ends with a lead surrogate, + * then it is found only if these surrogates stand alone in the text. + * Otherwise, the substring edge units would be matched against + * halves of surrogate pairs. + * + * @param s The string to search. + * @param length The length of s (number of UChars), or -1 if it is NUL-terminated. + * @param substring The substring to find (NUL-terminated). + * @param subLength The length of substring (number of UChars), or -1 if it is NUL-terminated. + * @return A pointer to the last occurrence of substring in s, + * or s itself if the substring is empty, + * or NULL if substring is not in s. + * @stable ICU 2.4 + * + * @see u_strstr + * @see u_strFindLast + */ +U_CAPI UChar * U_EXPORT2 +u_strFindLast(const UChar *s, int32_t length, const UChar *substring, int32_t subLength); + +/** + * Find the last occurrence of a BMP code point in a string. + * A surrogate code point is found only if its match in the text is not + * part of a surrogate pair. + * A NUL character is found at the string terminator. + * + * @param s The string to search (NUL-terminated). + * @param c The BMP code point to find. + * @return A pointer to the last occurrence of c in s + * or NULL if c is not in s. + * @stable ICU 2.4 + * + * @see u_strrchr32 + * @see u_memrchr + * @see u_strrstr + * @see u_strFindLast + */ +U_CAPI UChar * U_EXPORT2 +u_strrchr(const UChar *s, UChar c); + +/** + * Find the last occurrence of a code point in a string. + * A surrogate code point is found only if its match in the text is not + * part of a surrogate pair. + * A NUL character is found at the string terminator. + * + * @param s The string to search (NUL-terminated). + * @param c The code point to find. + * @return A pointer to the last occurrence of c in s + * or NULL if c is not in s. + * @stable ICU 2.4 + * + * @see u_strrchr + * @see u_memchr32 + * @see u_strrstr + * @see u_strFindLast + */ +U_CAPI UChar * U_EXPORT2 +u_strrchr32(const UChar *s, UChar32 c); + +/** + * Locates the first occurrence in the string string of any of the characters + * in the string matchSet. + * Works just like C's strpbrk but with Unicode. + * + * @param string The string in which to search, NUL-terminated. + * @param matchSet A NUL-terminated string defining a set of code points + * for which to search in the text string. + * @return A pointer to the character in string that matches one of the + * characters in matchSet, or NULL if no such character is found. + * @stable ICU 2.0 + */ +U_CAPI UChar * U_EXPORT2 +u_strpbrk(const UChar *string, const UChar *matchSet); + +/** + * Returns the number of consecutive characters in string, + * beginning with the first, that do not occur somewhere in matchSet. + * Works just like C's strcspn but with Unicode. + * + * @param string The string in which to search, NUL-terminated. + * @param matchSet A NUL-terminated string defining a set of code points + * for which to search in the text string. + * @return The number of initial characters in string that do not + * occur in matchSet. + * @see u_strspn + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_strcspn(const UChar *string, const UChar *matchSet); + +/** + * Returns the number of consecutive characters in string, + * beginning with the first, that occur somewhere in matchSet. + * Works just like C's strspn but with Unicode. + * + * @param string The string in which to search, NUL-terminated. + * @param matchSet A NUL-terminated string defining a set of code points + * for which to search in the text string. + * @return The number of initial characters in string that do + * occur in matchSet. + * @see u_strcspn + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_strspn(const UChar *string, const UChar *matchSet); + +/** + * The string tokenizer API allows an application to break a string into + * tokens. Unlike strtok(), the saveState (the current pointer within the + * original string) is maintained in saveState. In the first call, the + * argument src is a pointer to the string. In subsequent calls to + * return successive tokens of that string, src must be specified as + * NULL. The value saveState is set by this function to maintain the + * function's position within the string, and on each subsequent call + * you must give this argument the same variable. This function does + * handle surrogate pairs. This function is similar to the strtok_r() + * the POSIX Threads Extension (1003.1c-1995) version. + * + * @param src String containing token(s). This string will be modified. + * After the first call to u_strtok_r(), this argument must + * be NULL to get to the next token. + * @param delim Set of delimiter characters (Unicode code points). + * @param saveState The current pointer within the original string, + * which is set by this function. The saveState + * parameter should the address of a local variable of type + * UChar *. (i.e. defined "UChar *myLocalSaveState" and use + * &myLocalSaveState for this parameter). + * @return A pointer to the next token found in src, or NULL + * when there are no more tokens. + * @stable ICU 2.0 + */ +U_CAPI UChar * U_EXPORT2 +u_strtok_r(UChar *src, + const UChar *delim, + UChar **saveState); + +/** + * Compare two Unicode strings for bitwise equality (code unit order). + * + * @param s1 A string to compare. + * @param s2 A string to compare. + * @return 0 if s1 and s2 are bitwise equal; a negative + * value if s1 is bitwise less than s2,; a positive + * value if s1 is bitwise greater than s2. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_strcmp(const UChar *s1, + const UChar *s2); + +/** + * Compare two Unicode strings in code point order. + * See u_strCompare for details. + * + * @param s1 A string to compare. + * @param s2 A string to compare. + * @return a negative/zero/positive integer corresponding to whether + * the first string is less than/equal to/greater than the second one + * in code point order + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_strcmpCodePointOrder(const UChar *s1, const UChar *s2); + +/** + * Compare two Unicode strings (binary order). + * + * The comparison can be done in code unit order or in code point order. + * They differ only in UTF-16 when + * comparing supplementary code points (U+10000..U+10ffff) + * to BMP code points near the end of the BMP (i.e., U+e000..U+ffff). + * In code unit order, high BMP code points sort after supplementary code points + * because they are stored as pairs of surrogates which are at U+d800..U+dfff. + * + * This functions works with strings of different explicitly specified lengths + * unlike the ANSI C-like u_strcmp() and u_memcmp() etc. + * NUL-terminated strings are possible with length arguments of -1. + * + * @param s1 First source string. + * @param length1 Length of first source string, or -1 if NUL-terminated. + * + * @param s2 Second source string. + * @param length2 Length of second source string, or -1 if NUL-terminated. + * + * @param codePointOrder Choose between code unit order (false) + * and code point order (true). + * + * @return <0 or 0 or >0 as usual for string comparisons + * + * @stable ICU 2.2 + */ +U_CAPI int32_t U_EXPORT2 +u_strCompare(const UChar *s1, int32_t length1, + const UChar *s2, int32_t length2, + UBool codePointOrder); + +/** + * Compare two Unicode strings (binary order) + * as presented by UCharIterator objects. + * Works otherwise just like u_strCompare(). + * + * Both iterators are reset to their start positions. + * When the function returns, it is undefined where the iterators + * have stopped. + * + * @param iter1 First source string iterator. + * @param iter2 Second source string iterator. + * @param codePointOrder Choose between code unit order (false) + * and code point order (true). + * + * @return <0 or 0 or >0 as usual for string comparisons + * + * @see u_strCompare + * + * @stable ICU 2.6 + */ +U_CAPI int32_t U_EXPORT2 +u_strCompareIter(UCharIterator *iter1, UCharIterator *iter2, UBool codePointOrder); + +/** + * Compare two strings case-insensitively using full case folding. + * This is equivalent to + * u_strCompare(u_strFoldCase(s1, options), + * u_strFoldCase(s2, options), + * (options&U_COMPARE_CODE_POINT_ORDER)!=0). + * + * The comparison can be done in UTF-16 code unit order or in code point order. + * They differ only when comparing supplementary code points (U+10000..U+10ffff) + * to BMP code points near the end of the BMP (i.e., U+e000..U+ffff). + * In code unit order, high BMP code points sort after supplementary code points + * because they are stored as pairs of surrogates which are at U+d800..U+dfff. + * + * This functions works with strings of different explicitly specified lengths + * unlike the ANSI C-like u_strcmp() and u_memcmp() etc. + * NUL-terminated strings are possible with length arguments of -1. + * + * @param s1 First source string. + * @param length1 Length of first source string, or -1 if NUL-terminated. + * + * @param s2 Second source string. + * @param length2 Length of second source string, or -1 if NUL-terminated. + * + * @param options A bit set of options: + * - U_FOLD_CASE_DEFAULT or 0 is used for default options: + * Comparison in code unit order with default case folding. + * + * - U_COMPARE_CODE_POINT_ORDER + * Set to choose code point order instead of code unit order + * (see u_strCompare for details). + * + * - U_FOLD_CASE_EXCLUDE_SPECIAL_I + * + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * + * @return <0 or 0 or >0 as usual for string comparisons + * + * @stable ICU 2.2 + */ +U_CAPI int32_t U_EXPORT2 +u_strCaseCompare(const UChar *s1, int32_t length1, + const UChar *s2, int32_t length2, + uint32_t options, + UErrorCode *pErrorCode); + +/** + * Compare two ustrings for bitwise equality. + * Compares at most n characters. + * + * @param ucs1 A string to compare (can be NULL/invalid if n<=0). + * @param ucs2 A string to compare (can be NULL/invalid if n<=0). + * @param n The maximum number of characters to compare; always returns 0 if n<=0. + * @return 0 if s1 and s2 are bitwise equal; a negative + * value if s1 is bitwise less than s2; a positive + * value if s1 is bitwise greater than s2. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_strncmp(const UChar *ucs1, + const UChar *ucs2, + int32_t n); + +/** + * Compare two Unicode strings in code point order. + * This is different in UTF-16 from u_strncmp() if supplementary characters are present. + * For details, see u_strCompare(). + * + * @param s1 A string to compare. + * @param s2 A string to compare. + * @param n The maximum number of characters to compare. + * @return a negative/zero/positive integer corresponding to whether + * the first string is less than/equal to/greater than the second one + * in code point order + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_strncmpCodePointOrder(const UChar *s1, const UChar *s2, int32_t n); + +/** + * Compare two strings case-insensitively using full case folding. + * This is equivalent to u_strcmp(u_strFoldCase(s1, options), u_strFoldCase(s2, options)). + * + * @param s1 A string to compare. + * @param s2 A string to compare. + * @param options A bit set of options: + * - U_FOLD_CASE_DEFAULT or 0 is used for default options: + * Comparison in code unit order with default case folding. + * + * - U_COMPARE_CODE_POINT_ORDER + * Set to choose code point order instead of code unit order + * (see u_strCompare for details). + * + * - U_FOLD_CASE_EXCLUDE_SPECIAL_I + * + * @return A negative, zero, or positive integer indicating the comparison result. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_strcasecmp(const UChar *s1, const UChar *s2, uint32_t options); + +/** + * Compare two strings case-insensitively using full case folding. + * This is equivalent to u_strcmp(u_strFoldCase(s1, at most n, options), + * u_strFoldCase(s2, at most n, options)). + * + * @param s1 A string to compare. + * @param s2 A string to compare. + * @param n The maximum number of characters each string to case-fold and then compare. + * @param options A bit set of options: + * - U_FOLD_CASE_DEFAULT or 0 is used for default options: + * Comparison in code unit order with default case folding. + * + * - U_COMPARE_CODE_POINT_ORDER + * Set to choose code point order instead of code unit order + * (see u_strCompare for details). + * + * - U_FOLD_CASE_EXCLUDE_SPECIAL_I + * + * @return A negative, zero, or positive integer indicating the comparison result. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_strncasecmp(const UChar *s1, const UChar *s2, int32_t n, uint32_t options); + +/** + * Compare two strings case-insensitively using full case folding. + * This is equivalent to u_strcmp(u_strFoldCase(s1, n, options), + * u_strFoldCase(s2, n, options)). + * + * @param s1 A string to compare. + * @param s2 A string to compare. + * @param length The number of characters in each string to case-fold and then compare. + * @param options A bit set of options: + * - U_FOLD_CASE_DEFAULT or 0 is used for default options: + * Comparison in code unit order with default case folding. + * + * - U_COMPARE_CODE_POINT_ORDER + * Set to choose code point order instead of code unit order + * (see u_strCompare for details). + * + * - U_FOLD_CASE_EXCLUDE_SPECIAL_I + * + * @return A negative, zero, or positive integer indicating the comparison result. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_memcasecmp(const UChar *s1, const UChar *s2, int32_t length, uint32_t options); + +/** + * Copy a ustring. Adds a null terminator. + * + * @param dst The destination string. + * @param src The source string. + * @return A pointer to dst. + * @stable ICU 2.0 + */ +U_CAPI UChar* U_EXPORT2 +u_strcpy(UChar *dst, + const UChar *src); + +/** + * Copy a ustring. + * Copies at most n characters. The result will be null terminated + * if the length of src is less than n. + * + * @param dst The destination string. + * @param src The source string (can be NULL/invalid if n<=0). + * @param n The maximum number of characters to copy; no-op if <=0. + * @return A pointer to dst. + * @stable ICU 2.0 + */ +U_CAPI UChar* U_EXPORT2 +u_strncpy(UChar *dst, + const UChar *src, + int32_t n); + +#if !UCONFIG_NO_CONVERSION + +/** + * Copy a byte string encoded in the default codepage to a ustring. + * Adds a null terminator. + * Performs a host byte to UChar conversion + * + * @param dst The destination string. + * @param src The source string. + * @return A pointer to dst. + * @stable ICU 2.0 + */ +U_CAPI UChar* U_EXPORT2 u_uastrcpy(UChar *dst, + const char *src ); + +/** + * Copy a byte string encoded in the default codepage to a ustring. + * Copies at most n characters. The result will be null terminated + * if the length of src is less than n. + * Performs a host byte to UChar conversion + * + * @param dst The destination string. + * @param src The source string. + * @param n The maximum number of characters to copy. + * @return A pointer to dst. + * @stable ICU 2.0 + */ +U_CAPI UChar* U_EXPORT2 u_uastrncpy(UChar *dst, + const char *src, + int32_t n); + +/** + * Copy ustring to a byte string encoded in the default codepage. + * Adds a null terminator. + * Performs a UChar to host byte conversion + * + * @param dst The destination string. + * @param src The source string. + * @return A pointer to dst. + * @stable ICU 2.0 + */ +U_CAPI char* U_EXPORT2 u_austrcpy(char *dst, + const UChar *src ); + +/** + * Copy ustring to a byte string encoded in the default codepage. + * Copies at most n characters. The result will be null terminated + * if the length of src is less than n. + * Performs a UChar to host byte conversion + * + * @param dst The destination string. + * @param src The source string. + * @param n The maximum number of characters to copy. + * @return A pointer to dst. + * @stable ICU 2.0 + */ +U_CAPI char* U_EXPORT2 u_austrncpy(char *dst, + const UChar *src, + int32_t n ); + +#endif + +/** + * Synonym for memcpy(), but with UChars only. + * @param dest The destination string + * @param src The source string (can be NULL/invalid if count<=0) + * @param count The number of characters to copy; no-op if <=0 + * @return A pointer to dest + * @stable ICU 2.0 + */ +U_CAPI UChar* U_EXPORT2 +u_memcpy(UChar *dest, const UChar *src, int32_t count); + +/** + * Synonym for memmove(), but with UChars only. + * @param dest The destination string + * @param src The source string (can be NULL/invalid if count<=0) + * @param count The number of characters to move; no-op if <=0 + * @return A pointer to dest + * @stable ICU 2.0 + */ +U_CAPI UChar* U_EXPORT2 +u_memmove(UChar *dest, const UChar *src, int32_t count); + +/** + * Initialize count characters of dest to c. + * + * @param dest The destination string. + * @param c The character to initialize the string. + * @param count The maximum number of characters to set. + * @return A pointer to dest. + * @stable ICU 2.0 + */ +U_CAPI UChar* U_EXPORT2 +u_memset(UChar *dest, UChar c, int32_t count); + +/** + * Compare the first count UChars of each buffer. + * + * @param buf1 The first string to compare. + * @param buf2 The second string to compare. + * @param count The maximum number of UChars to compare. + * @return When buf1 < buf2, a negative number is returned. + * When buf1 == buf2, 0 is returned. + * When buf1 > buf2, a positive number is returned. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_memcmp(const UChar *buf1, const UChar *buf2, int32_t count); + +/** + * Compare two Unicode strings in code point order. + * This is different in UTF-16 from u_memcmp() if supplementary characters are present. + * For details, see u_strCompare(). + * + * @param s1 A string to compare. + * @param s2 A string to compare. + * @param count The maximum number of characters to compare. + * @return a negative/zero/positive integer corresponding to whether + * the first string is less than/equal to/greater than the second one + * in code point order + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_memcmpCodePointOrder(const UChar *s1, const UChar *s2, int32_t count); + +/** + * Find the first occurrence of a BMP code point in a string. + * A surrogate code point is found only if its match in the text is not + * part of a surrogate pair. + * A NUL character is found at the string terminator. + * + * @param s The string to search (contains count UChars). + * @param c The BMP code point to find. + * @param count The length of the string. + * @return A pointer to the first occurrence of c in s + * or NULL if c is not in s. + * @stable ICU 2.0 + * + * @see u_strchr + * @see u_memchr32 + * @see u_strFindFirst + */ +U_CAPI UChar* U_EXPORT2 +u_memchr(const UChar *s, UChar c, int32_t count); + +/** + * Find the first occurrence of a code point in a string. + * A surrogate code point is found only if its match in the text is not + * part of a surrogate pair. + * A NUL character is found at the string terminator. + * + * @param s The string to search (contains count UChars). + * @param c The code point to find. + * @param count The length of the string. + * @return A pointer to the first occurrence of c in s + * or NULL if c is not in s. + * @stable ICU 2.0 + * + * @see u_strchr32 + * @see u_memchr + * @see u_strFindFirst + */ +U_CAPI UChar* U_EXPORT2 +u_memchr32(const UChar *s, UChar32 c, int32_t count); + +/** + * Find the last occurrence of a BMP code point in a string. + * A surrogate code point is found only if its match in the text is not + * part of a surrogate pair. + * A NUL character is found at the string terminator. + * + * @param s The string to search (contains count UChars). + * @param c The BMP code point to find. + * @param count The length of the string. + * @return A pointer to the last occurrence of c in s + * or NULL if c is not in s. + * @stable ICU 2.4 + * + * @see u_strrchr + * @see u_memrchr32 + * @see u_strFindLast + */ +U_CAPI UChar* U_EXPORT2 +u_memrchr(const UChar *s, UChar c, int32_t count); + +/** + * Find the last occurrence of a code point in a string. + * A surrogate code point is found only if its match in the text is not + * part of a surrogate pair. + * A NUL character is found at the string terminator. + * + * @param s The string to search (contains count UChars). + * @param c The code point to find. + * @param count The length of the string. + * @return A pointer to the last occurrence of c in s + * or NULL if c is not in s. + * @stable ICU 2.4 + * + * @see u_strrchr32 + * @see u_memrchr + * @see u_strFindLast + */ +U_CAPI UChar* U_EXPORT2 +u_memrchr32(const UChar *s, UChar32 c, int32_t count); + +/** + * Unicode String literals in C. + * We need one macro to declare a variable for the string + * and to statically preinitialize it if possible, + * and a second macro to dynamically initialize such a string variable if necessary. + * + * The macros are defined for maximum performance. + * They work only for strings that contain "invariant characters", i.e., + * only latin letters, digits, and some punctuation. + * See utypes.h for details. + * + * A pair of macros for a single string must be used with the same + * parameters. + * The string parameter must be a C string literal. + * The length of the string, not including the terminating + * `NUL`, must be specified as a constant. + * The U_STRING_DECL macro should be invoked exactly once for one + * such string variable before it is used. + * + * Usage: + * + * U_STRING_DECL(ustringVar1, "Quick-Fox 2", 11); + * U_STRING_DECL(ustringVar2, "jumps 5%", 8); + * static UBool didInit=false; + * + * int32_t function() { + * if(!didInit) { + * U_STRING_INIT(ustringVar1, "Quick-Fox 2", 11); + * U_STRING_INIT(ustringVar2, "jumps 5%", 8); + * didInit=true; + * } + * return u_strcmp(ustringVar1, ustringVar2); + * } + * + * Note that the macros will NOT consistently work if their argument is another #`define`. + * The following will not work on all platforms, don't use it. + * + * #define GLUCK "Mr. Gluck" + * U_STRING_DECL(var, GLUCK, 9) + * U_STRING_INIT(var, GLUCK, 9) + * + * Instead, use the string literal "Mr. Gluck" as the argument to both macro + * calls. + * + * + * @stable ICU 2.0 + */ +#if defined(U_DECLARE_UTF16) +# define U_STRING_DECL(var, cs, length) static const UChar *var=(const UChar *)U_DECLARE_UTF16(cs) + /**@stable ICU 2.0 */ +# define U_STRING_INIT(var, cs, length) +#elif U_SIZEOF_WCHAR_T==U_SIZEOF_UCHAR && (U_CHARSET_FAMILY==U_ASCII_FAMILY || defined(U_WCHAR_IS_UTF16)) +# define U_STRING_DECL(var, cs, length) static const UChar var[(length)+1]=L ## cs + /**@stable ICU 2.0 */ +# define U_STRING_INIT(var, cs, length) +#else +# define U_STRING_DECL(var, cs, length) static UChar var[(length)+1] + /**@stable ICU 2.0 */ +# define U_STRING_INIT(var, cs, length) u_charsToUChars(cs, var, length+1) +#endif + +/** + * Unescape a string of characters and write the resulting + * Unicode characters to the destination buffer. The following escape + * sequences are recognized: + * + * \\uhhhh 4 hex digits; h in [0-9A-Fa-f] + * \\Uhhhhhhhh 8 hex digits + * \\xhh 1-2 hex digits + * \\x{h...} 1-8 hex digits + * \\ooo 1-3 octal digits; o in [0-7] + * \\cX control-X; X is masked with 0x1F + * + * as well as the standard ANSI C escapes: + * + * \\a => U+0007, \\b => U+0008, \\t => U+0009, \\n => U+000A, + * \\v => U+000B, \\f => U+000C, \\r => U+000D, \\e => U+001B, + * \\" => U+0022, \\' => U+0027, \\? => U+003F, \\\\ => U+005C + * + * Anything else following a backslash is generically escaped. For + * example, "[a\\-z]" returns "[a-z]". + * + * If an escape sequence is ill-formed, this method returns an empty + * string. An example of an ill-formed sequence is "\\u" followed by + * fewer than 4 hex digits. + * + * The above characters are recognized in the compiler's codepage, + * that is, they are coded as 'u', '\\', etc. Characters that are + * not parts of escape sequences are converted using u_charsToUChars(). + * + * This function is similar to UnicodeString::unescape() but not + * identical to it. The latter takes a source UnicodeString, so it + * does escape recognition but no conversion. + * + * @param src a zero-terminated string of invariant characters + * @param dest pointer to buffer to receive converted and unescaped + * text and, if there is room, a zero terminator. May be NULL for + * preflighting, in which case no UChars will be written, but the + * return value will still be valid. On error, an empty string is + * stored here (if possible). + * @param destCapacity the number of UChars that may be written at + * dest. Ignored if dest == NULL. + * @return the length of unescaped string. + * @see u_unescapeAt + * @see UnicodeString#unescape() + * @see UnicodeString#unescapeAt() + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_unescape(const char *src, + UChar *dest, int32_t destCapacity); + +U_CDECL_BEGIN +/** + * Callback function for u_unescapeAt() that returns a character of + * the source text given an offset and a context pointer. The context + * pointer will be whatever is passed into u_unescapeAt(). + * + * @param offset pointer to the offset that will be passed to u_unescapeAt(). + * @param context an opaque pointer passed directly into u_unescapeAt() + * @return the character represented by the escape sequence at + * offset + * @see u_unescapeAt + * @stable ICU 2.0 + */ +typedef UChar (U_CALLCONV *UNESCAPE_CHAR_AT)(int32_t offset, void *context); +U_CDECL_END + +/** + * Unescape a single sequence. The character at offset-1 is assumed + * (without checking) to be a backslash. This method takes a callback + * pointer to a function that returns the UChar at a given offset. By + * varying this callback, ICU functions are able to unescape char* + * strings, UnicodeString objects, and UFILE pointers. + * + * If offset is out of range, or if the escape sequence is ill-formed, + * (UChar32)0xFFFFFFFF is returned. See documentation of u_unescape() + * for a list of recognized sequences. + * + * @param charAt callback function that returns a UChar of the source + * text given an offset and a context pointer. + * @param offset pointer to the offset that will be passed to charAt. + * The offset value will be updated upon return to point after the + * last parsed character of the escape sequence. On error the offset + * is unchanged. + * @param length the number of characters in the source text. The + * last character of the source text is considered to be at offset + * length-1. + * @param context an opaque pointer passed directly into charAt. + * @return the character represented by the escape sequence at + * offset, or (UChar32)0xFFFFFFFF on error. + * @see u_unescape() + * @see UnicodeString#unescape() + * @see UnicodeString#unescapeAt() + * @stable ICU 2.0 + */ +U_CAPI UChar32 U_EXPORT2 +u_unescapeAt(UNESCAPE_CHAR_AT charAt, + int32_t *offset, + int32_t length, + void *context); + +/** + * Uppercase the characters in a string. + * Casing is locale-dependent and context-sensitive. + * The result may be longer or shorter than the original. + * The source string and the destination buffer are allowed to overlap. + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of UChars). If it is 0, then + * dest may be NULL and the function will only return the length of the result + * without writing any of the result string. + * @param src The original string + * @param srcLength The length of the original string. If -1, then src must be zero-terminated. + * @param locale The locale to consider, or "" for the root locale or NULL for the default locale. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return The length of the result string. It may be greater than destCapacity. In that case, + * only some of the result was written to the destination buffer. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_strToUpper(UChar *dest, int32_t destCapacity, + const UChar *src, int32_t srcLength, + const char *locale, + UErrorCode *pErrorCode); + +/** + * Lowercase the characters in a string. + * Casing is locale-dependent and context-sensitive. + * The result may be longer or shorter than the original. + * The source string and the destination buffer are allowed to overlap. + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of UChars). If it is 0, then + * dest may be NULL and the function will only return the length of the result + * without writing any of the result string. + * @param src The original string + * @param srcLength The length of the original string. If -1, then src must be zero-terminated. + * @param locale The locale to consider, or "" for the root locale or NULL for the default locale. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return The length of the result string. It may be greater than destCapacity. In that case, + * only some of the result was written to the destination buffer. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_strToLower(UChar *dest, int32_t destCapacity, + const UChar *src, int32_t srcLength, + const char *locale, + UErrorCode *pErrorCode); + +#if !UCONFIG_NO_BREAK_ITERATION + +/** + * Titlecase a string. + * Casing is locale-dependent and context-sensitive. + * Titlecasing uses a break iterator to find the first characters of words + * that are to be titlecased. It titlecases those characters and lowercases + * all others. + * + * The titlecase break iterator can be provided to customize for arbitrary + * styles, using rules and dictionaries beyond the standard iterators. + * It may be more efficient to always provide an iterator to avoid + * opening and closing one for each string. + * The standard titlecase iterator for the root locale implements the + * algorithm of Unicode TR 21. + * + * This function uses only the setText(), first() and next() methods of the + * provided break iterator. + * + * The result may be longer or shorter than the original. + * The source string and the destination buffer are allowed to overlap. + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of UChars). If it is 0, then + * dest may be NULL and the function will only return the length of the result + * without writing any of the result string. + * @param src The original string + * @param srcLength The length of the original string. If -1, then src must be zero-terminated. + * @param titleIter A break iterator to find the first characters of words + * that are to be titlecased. + * If none is provided (NULL), then a standard titlecase + * break iterator is opened. + * @param locale The locale to consider, or "" for the root locale or NULL for the default locale. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return The length of the result string. It may be greater than destCapacity. In that case, + * only some of the result was written to the destination buffer. + * @stable ICU 2.1 + */ +U_CAPI int32_t U_EXPORT2 +u_strToTitle(UChar *dest, int32_t destCapacity, + const UChar *src, int32_t srcLength, + UBreakIterator *titleIter, + const char *locale, + UErrorCode *pErrorCode); + +#endif + +/** + * Case-folds the characters in a string. + * + * Case-folding is locale-independent and not context-sensitive, + * but there is an option for whether to include or exclude mappings for dotted I + * and dotless i that are marked with 'T' in CaseFolding.txt. + * + * The result may be longer or shorter than the original. + * The source string and the destination buffer are allowed to overlap. + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of UChars). If it is 0, then + * dest may be NULL and the function will only return the length of the result + * without writing any of the result string. + * @param src The original string + * @param srcLength The length of the original string. If -1, then src must be zero-terminated. + * @param options Either U_FOLD_CASE_DEFAULT or U_FOLD_CASE_EXCLUDE_SPECIAL_I + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return The length of the result string. It may be greater than destCapacity. In that case, + * only some of the result was written to the destination buffer. + * @stable ICU 2.0 + */ +U_CAPI int32_t U_EXPORT2 +u_strFoldCase(UChar *dest, int32_t destCapacity, + const UChar *src, int32_t srcLength, + uint32_t options, + UErrorCode *pErrorCode); + +#if defined(U_WCHAR_IS_UTF16) || defined(U_WCHAR_IS_UTF32) || !UCONFIG_NO_CONVERSION +/** + * Convert a UTF-16 string to a wchar_t string. + * If it is known at compile time that wchar_t strings are in UTF-16 or UTF-32, then + * this function simply calls the fast, dedicated function for that. + * Otherwise, two conversions UTF-16 -> default charset -> wchar_t* are performed. + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of wchar_t's). If it is 0, then + * dest may be NULL and the function will only return the length of the + * result without writing any of the result string (pre-flighting). + * @param pDestLength A pointer to receive the number of units written to the destination. If + * pDestLength!=NULL then *pDestLength is always set to the + * number of output units corresponding to the transformation of + * all the input units, even in case of a buffer overflow. + * @param src The original source string + * @param srcLength The length of the original string. If -1, then src must be zero-terminated. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return The pointer to destination buffer. + * @stable ICU 2.0 + */ +U_CAPI wchar_t* U_EXPORT2 +u_strToWCS(wchar_t *dest, + int32_t destCapacity, + int32_t *pDestLength, + const UChar *src, + int32_t srcLength, + UErrorCode *pErrorCode); +/** + * Convert a wchar_t string to UTF-16. + * If it is known at compile time that wchar_t strings are in UTF-16 or UTF-32, then + * this function simply calls the fast, dedicated function for that. + * Otherwise, two conversions wchar_t* -> default charset -> UTF-16 are performed. + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of UChars). If it is 0, then + * dest may be NULL and the function will only return the length of the + * result without writing any of the result string (pre-flighting). + * @param pDestLength A pointer to receive the number of units written to the destination. If + * pDestLength!=NULL then *pDestLength is always set to the + * number of output units corresponding to the transformation of + * all the input units, even in case of a buffer overflow. + * @param src The original source string + * @param srcLength The length of the original string. If -1, then src must be zero-terminated. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return The pointer to destination buffer. + * @stable ICU 2.0 + */ +U_CAPI UChar* U_EXPORT2 +u_strFromWCS(UChar *dest, + int32_t destCapacity, + int32_t *pDestLength, + const wchar_t *src, + int32_t srcLength, + UErrorCode *pErrorCode); +#endif /* defined(U_WCHAR_IS_UTF16) || defined(U_WCHAR_IS_UTF32) || !UCONFIG_NO_CONVERSION */ + +/** + * Convert a UTF-16 string to UTF-8. + * If the input string is not well-formed, then the U_INVALID_CHAR_FOUND error code is set. + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of chars). If it is 0, then + * dest may be NULL and the function will only return the length of the + * result without writing any of the result string (pre-flighting). + * @param pDestLength A pointer to receive the number of units written to the destination. If + * pDestLength!=NULL then *pDestLength is always set to the + * number of output units corresponding to the transformation of + * all the input units, even in case of a buffer overflow. + * @param src The original source string + * @param srcLength The length of the original string. If -1, then src must be zero-terminated. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return The pointer to destination buffer. + * @stable ICU 2.0 + * @see u_strToUTF8WithSub + * @see u_strFromUTF8 + */ +U_CAPI char* U_EXPORT2 +u_strToUTF8(char *dest, + int32_t destCapacity, + int32_t *pDestLength, + const UChar *src, + int32_t srcLength, + UErrorCode *pErrorCode); + +/** + * Convert a UTF-8 string to UTF-16. + * If the input string is not well-formed, then the U_INVALID_CHAR_FOUND error code is set. + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of UChars). If it is 0, then + * dest may be NULL and the function will only return the length of the + * result without writing any of the result string (pre-flighting). + * @param pDestLength A pointer to receive the number of units written to the destination. If + * pDestLength!=NULL then *pDestLength is always set to the + * number of output units corresponding to the transformation of + * all the input units, even in case of a buffer overflow. + * @param src The original source string + * @param srcLength The length of the original string. If -1, then src must be zero-terminated. + * @param pErrorCode Must be a valid pointer to an error code value, + * which must not indicate a failure before the function call. + * @return The pointer to destination buffer. + * @stable ICU 2.0 + * @see u_strFromUTF8WithSub + * @see u_strFromUTF8Lenient + */ +U_CAPI UChar* U_EXPORT2 +u_strFromUTF8(UChar *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char *src, + int32_t srcLength, + UErrorCode *pErrorCode); + +/** + * Convert a UTF-16 string to UTF-8. + * + * Same as u_strToUTF8() except for the additional subchar which is output for + * illegal input sequences, instead of stopping with the U_INVALID_CHAR_FOUND error code. + * With subchar==U_SENTINEL, this function behaves exactly like u_strToUTF8(). + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of chars). If it is 0, then + * dest may be NULL and the function will only return the length of the + * result without writing any of the result string (pre-flighting). + * @param pDestLength A pointer to receive the number of units written to the destination. If + * pDestLength!=NULL then *pDestLength is always set to the + * number of output units corresponding to the transformation of + * all the input units, even in case of a buffer overflow. + * @param src The original source string + * @param srcLength The length of the original string. If -1, then src must be zero-terminated. + * @param subchar The substitution character to use in place of an illegal input sequence, + * or U_SENTINEL if the function is to return with U_INVALID_CHAR_FOUND instead. + * A substitution character can be any valid Unicode code point (up to U+10FFFF) + * except for surrogate code points (U+D800..U+DFFF). + * The recommended value is U+FFFD "REPLACEMENT CHARACTER". + * @param pNumSubstitutions Output parameter receiving the number of substitutions if subchar>=0. + * Set to 0 if no substitutions occur or subchar<0. + * pNumSubstitutions can be NULL. + * @param pErrorCode Pointer to a standard 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.) + * @return The pointer to destination buffer. + * @see u_strToUTF8 + * @see u_strFromUTF8WithSub + * @stable ICU 3.6 + */ +U_CAPI char* U_EXPORT2 +u_strToUTF8WithSub(char *dest, + int32_t destCapacity, + int32_t *pDestLength, + const UChar *src, + int32_t srcLength, + UChar32 subchar, int32_t *pNumSubstitutions, + UErrorCode *pErrorCode); + +/** + * Convert a UTF-8 string to UTF-16. + * + * Same as u_strFromUTF8() except for the additional subchar which is output for + * illegal input sequences, instead of stopping with the U_INVALID_CHAR_FOUND error code. + * With subchar==U_SENTINEL, this function behaves exactly like u_strFromUTF8(). + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of UChars). If it is 0, then + * dest may be NULL and the function will only return the length of the + * result without writing any of the result string (pre-flighting). + * @param pDestLength A pointer to receive the number of units written to the destination. If + * pDestLength!=NULL then *pDestLength is always set to the + * number of output units corresponding to the transformation of + * all the input units, even in case of a buffer overflow. + * @param src The original source string + * @param srcLength The length of the original string. If -1, then src must be zero-terminated. + * @param subchar The substitution character to use in place of an illegal input sequence, + * or U_SENTINEL if the function is to return with U_INVALID_CHAR_FOUND instead. + * A substitution character can be any valid Unicode code point (up to U+10FFFF) + * except for surrogate code points (U+D800..U+DFFF). + * The recommended value is U+FFFD "REPLACEMENT CHARACTER". + * @param pNumSubstitutions Output parameter receiving the number of substitutions if subchar>=0. + * Set to 0 if no substitutions occur or subchar<0. + * pNumSubstitutions can be NULL. + * @param pErrorCode Pointer to a standard 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.) + * @return The pointer to destination buffer. + * @see u_strFromUTF8 + * @see u_strFromUTF8Lenient + * @see u_strToUTF8WithSub + * @stable ICU 3.6 + */ +U_CAPI UChar* U_EXPORT2 +u_strFromUTF8WithSub(UChar *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char *src, + int32_t srcLength, + UChar32 subchar, int32_t *pNumSubstitutions, + UErrorCode *pErrorCode); + +/** + * Convert a UTF-8 string to UTF-16. + * + * Same as u_strFromUTF8() except that this function is designed to be very fast, + * which it achieves by being lenient about malformed UTF-8 sequences. + * This function is intended for use in environments where UTF-8 text is + * expected to be well-formed. + * + * Its semantics are: + * - Well-formed UTF-8 text is correctly converted to well-formed UTF-16 text. + * - The function will not read beyond the input string, nor write beyond + * the destCapacity. + * - Malformed UTF-8 results in "garbage" 16-bit Unicode strings which may not + * be well-formed UTF-16. + * The function will resynchronize to valid code point boundaries + * within a small number of code points after an illegal sequence. + * - Non-shortest forms are not detected and will result in "spoofing" output. + * + * For further performance improvement, if srcLength is given (>=0), + * then it must be destCapacity>=srcLength. + * + * There is no inverse u_strToUTF8Lenient() function because there is practically + * no performance gain from not checking that a UTF-16 string is well-formed. + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of UChars). If it is 0, then + * dest may be NULL and the function will only return the length of the + * result without writing any of the result string (pre-flighting). + * Unlike for other ICU functions, if srcLength>=0 then it + * must be destCapacity>=srcLength. + * @param pDestLength A pointer to receive the number of units written to the destination. If + * pDestLength!=NULL then *pDestLength is always set to the + * number of output units corresponding to the transformation of + * all the input units, even in case of a buffer overflow. + * Unlike for other ICU functions, if srcLength>=0 but + * destCapacity=0. + * Set to 0 if no substitutions occur or subchar<0. + * pNumSubstitutions can be NULL. + * @param pErrorCode Pointer to a standard 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.) + * @return The pointer to destination buffer. + * @see u_strToUTF32 + * @see u_strFromUTF32WithSub + * @stable ICU 4.2 + */ +U_CAPI UChar32* U_EXPORT2 +u_strToUTF32WithSub(UChar32 *dest, + int32_t destCapacity, + int32_t *pDestLength, + const UChar *src, + int32_t srcLength, + UChar32 subchar, int32_t *pNumSubstitutions, + UErrorCode *pErrorCode); + +/** + * Convert a UTF-32 string to UTF-16. + * + * Same as u_strFromUTF32() except for the additional subchar which is output for + * illegal input sequences, instead of stopping with the U_INVALID_CHAR_FOUND error code. + * With subchar==U_SENTINEL, this function behaves exactly like u_strFromUTF32(). + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of UChars). If it is 0, then + * dest may be NULL and the function will only return the length of the + * result without writing any of the result string (pre-flighting). + * @param pDestLength A pointer to receive the number of units written to the destination. If + * pDestLength!=NULL then *pDestLength is always set to the + * number of output units corresponding to the transformation of + * all the input units, even in case of a buffer overflow. + * @param src The original source string + * @param srcLength The length of the original string. If -1, then src must be zero-terminated. + * @param subchar The substitution character to use in place of an illegal input sequence, + * or U_SENTINEL if the function is to return with U_INVALID_CHAR_FOUND instead. + * A substitution character can be any valid Unicode code point (up to U+10FFFF) + * except for surrogate code points (U+D800..U+DFFF). + * The recommended value is U+FFFD "REPLACEMENT CHARACTER". + * @param pNumSubstitutions Output parameter receiving the number of substitutions if subchar>=0. + * Set to 0 if no substitutions occur or subchar<0. + * pNumSubstitutions can be NULL. + * @param pErrorCode Pointer to a standard 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.) + * @return The pointer to destination buffer. + * @see u_strFromUTF32 + * @see u_strToUTF32WithSub + * @stable ICU 4.2 + */ +U_CAPI UChar* U_EXPORT2 +u_strFromUTF32WithSub(UChar *dest, + int32_t destCapacity, + int32_t *pDestLength, + const UChar32 *src, + int32_t srcLength, + UChar32 subchar, int32_t *pNumSubstitutions, + UErrorCode *pErrorCode); + +/** + * Convert a 16-bit Unicode string to Java Modified UTF-8. + * See http://java.sun.com/javase/6/docs/api/java/io/DataInput.html#modified-utf-8 + * + * This function behaves according to the documentation for Java DataOutput.writeUTF() + * except that it does not encode the output length in the destination buffer + * and does not have an output length restriction. + * See http://java.sun.com/javase/6/docs/api/java/io/DataOutput.html#writeUTF(java.lang.String) + * + * The input string need not be well-formed UTF-16. + * (Therefore there is no subchar parameter.) + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of chars). If it is 0, then + * dest may be NULL and the function will only return the length of the + * result without writing any of the result string (pre-flighting). + * @param pDestLength A pointer to receive the number of units written to the destination. If + * pDestLength!=NULL then *pDestLength is always set to the + * number of output units corresponding to the transformation of + * all the input units, even in case of a buffer overflow. + * @param src The original source string + * @param srcLength The length of the original string. If -1, then src must be zero-terminated. + * @param pErrorCode Pointer to a standard 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.) + * @return The pointer to destination buffer. + * @stable ICU 4.4 + * @see u_strToUTF8WithSub + * @see u_strFromJavaModifiedUTF8WithSub + */ +U_CAPI char* U_EXPORT2 +u_strToJavaModifiedUTF8( + char *dest, + int32_t destCapacity, + int32_t *pDestLength, + const UChar *src, + int32_t srcLength, + UErrorCode *pErrorCode); + +/** + * Convert a Java Modified UTF-8 string to a 16-bit Unicode string. + * If the input string is not well-formed and no substitution char is specified, + * then the U_INVALID_CHAR_FOUND error code is set. + * + * This function behaves according to the documentation for Java DataInput.readUTF() + * except that it takes a length parameter rather than + * interpreting the first two input bytes as the length. + * See http://java.sun.com/javase/6/docs/api/java/io/DataInput.html#readUTF() + * + * The output string may not be well-formed UTF-16. + * + * @param dest A buffer for the result string. The result will be zero-terminated if + * the buffer is large enough. + * @param destCapacity The size of the buffer (number of UChars). If it is 0, then + * dest may be NULL and the function will only return the length of the + * result without writing any of the result string (pre-flighting). + * @param pDestLength A pointer to receive the number of units written to the destination. If + * pDestLength!=NULL then *pDestLength is always set to the + * number of output units corresponding to the transformation of + * all the input units, even in case of a buffer overflow. + * @param src The original source string + * @param srcLength The length of the original string. If -1, then src must be zero-terminated. + * @param subchar The substitution character to use in place of an illegal input sequence, + * or U_SENTINEL if the function is to return with U_INVALID_CHAR_FOUND instead. + * A substitution character can be any valid Unicode code point (up to U+10FFFF) + * except for surrogate code points (U+D800..U+DFFF). + * The recommended value is U+FFFD "REPLACEMENT CHARACTER". + * @param pNumSubstitutions Output parameter receiving the number of substitutions if subchar>=0. + * Set to 0 if no substitutions occur or subchar<0. + * pNumSubstitutions can be NULL. + * @param pErrorCode Pointer to a standard 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.) + * @return The pointer to destination buffer. + * @see u_strFromUTF8WithSub + * @see u_strFromUTF8Lenient + * @see u_strToJavaModifiedUTF8 + * @stable ICU 4.4 + */ +U_CAPI UChar* U_EXPORT2 +u_strFromJavaModifiedUTF8WithSub( + UChar *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char *src, + int32_t srcLength, + UChar32 subchar, int32_t *pNumSubstitutions, + UErrorCode *pErrorCode); + +#endif diff --git a/deps/icu-small/source/common/unicode/ustringtrie.h b/deps/icu-small/source/common/unicode/ustringtrie.h index fd85648225408c..33a7247838e28f 100644 --- a/deps/icu-small/source/common/unicode/ustringtrie.h +++ b/deps/icu-small/source/common/unicode/ustringtrie.h @@ -1,97 +1,97 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2012, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: udicttrie.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010dec17 -* created by: Markus W. Scherer -*/ - -#ifndef __USTRINGTRIE_H__ -#define __USTRINGTRIE_H__ - -/** - * \file - * \brief C API: Helper definitions for dictionary trie APIs. - */ - -#include "unicode/utypes.h" - - -/** - * Return values for BytesTrie::next(), UCharsTrie::next() and similar methods. - * @see USTRINGTRIE_MATCHES - * @see USTRINGTRIE_HAS_VALUE - * @see USTRINGTRIE_HAS_NEXT - * @stable ICU 4.8 - */ -enum UStringTrieResult { - /** - * The input unit(s) did not continue a matching string. - * Once current()/next() return USTRINGTRIE_NO_MATCH, - * all further calls to current()/next() will also return USTRINGTRIE_NO_MATCH, - * until the trie is reset to its original state or to a saved state. - * @stable ICU 4.8 - */ - USTRINGTRIE_NO_MATCH, - /** - * The input unit(s) continued a matching string - * but there is no value for the string so far. - * (It is a prefix of a longer string.) - * @stable ICU 4.8 - */ - USTRINGTRIE_NO_VALUE, - /** - * The input unit(s) continued a matching string - * and there is a value for the string so far. - * This value will be returned by getValue(). - * No further input byte/unit can continue a matching string. - * @stable ICU 4.8 - */ - USTRINGTRIE_FINAL_VALUE, - /** - * The input unit(s) continued a matching string - * and there is a value for the string so far. - * This value will be returned by getValue(). - * Another input byte/unit can continue a matching string. - * @stable ICU 4.8 - */ - USTRINGTRIE_INTERMEDIATE_VALUE -}; - -/** - * Same as (result!=USTRINGTRIE_NO_MATCH). - * @param result A result from BytesTrie::first(), UCharsTrie::next() etc. - * @return true if the input bytes/units so far are part of a matching string/byte sequence. - * @stable ICU 4.8 - */ -#define USTRINGTRIE_MATCHES(result) ((result)!=USTRINGTRIE_NO_MATCH) - -/** - * Equivalent to (result==USTRINGTRIE_INTERMEDIATE_VALUE || result==USTRINGTRIE_FINAL_VALUE) but - * this macro evaluates result exactly once. - * @param result A result from BytesTrie::first(), UCharsTrie::next() etc. - * @return true if there is a value for the input bytes/units so far. - * @see BytesTrie::getValue - * @see UCharsTrie::getValue - * @stable ICU 4.8 - */ -#define USTRINGTRIE_HAS_VALUE(result) ((result)>=USTRINGTRIE_FINAL_VALUE) - -/** - * Equivalent to (result==USTRINGTRIE_NO_VALUE || result==USTRINGTRIE_INTERMEDIATE_VALUE) but - * this macro evaluates result exactly once. - * @param result A result from BytesTrie::first(), UCharsTrie::next() etc. - * @return true if another input byte/unit can continue a matching string. - * @stable ICU 4.8 - */ -#define USTRINGTRIE_HAS_NEXT(result) ((result)&1) - -#endif /* __USTRINGTRIE_H__ */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2012, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: udicttrie.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010dec17 +* created by: Markus W. Scherer +*/ + +#ifndef __USTRINGTRIE_H__ +#define __USTRINGTRIE_H__ + +/** + * \file + * \brief C API: Helper definitions for dictionary trie APIs. + */ + +#include "unicode/utypes.h" + + +/** + * Return values for BytesTrie::next(), UCharsTrie::next() and similar methods. + * @see USTRINGTRIE_MATCHES + * @see USTRINGTRIE_HAS_VALUE + * @see USTRINGTRIE_HAS_NEXT + * @stable ICU 4.8 + */ +enum UStringTrieResult { + /** + * The input unit(s) did not continue a matching string. + * Once current()/next() return USTRINGTRIE_NO_MATCH, + * all further calls to current()/next() will also return USTRINGTRIE_NO_MATCH, + * until the trie is reset to its original state or to a saved state. + * @stable ICU 4.8 + */ + USTRINGTRIE_NO_MATCH, + /** + * The input unit(s) continued a matching string + * but there is no value for the string so far. + * (It is a prefix of a longer string.) + * @stable ICU 4.8 + */ + USTRINGTRIE_NO_VALUE, + /** + * The input unit(s) continued a matching string + * and there is a value for the string so far. + * This value will be returned by getValue(). + * No further input byte/unit can continue a matching string. + * @stable ICU 4.8 + */ + USTRINGTRIE_FINAL_VALUE, + /** + * The input unit(s) continued a matching string + * and there is a value for the string so far. + * This value will be returned by getValue(). + * Another input byte/unit can continue a matching string. + * @stable ICU 4.8 + */ + USTRINGTRIE_INTERMEDIATE_VALUE +}; + +/** + * Same as (result!=USTRINGTRIE_NO_MATCH). + * @param result A result from BytesTrie::first(), UCharsTrie::next() etc. + * @return true if the input bytes/units so far are part of a matching string/byte sequence. + * @stable ICU 4.8 + */ +#define USTRINGTRIE_MATCHES(result) ((result)!=USTRINGTRIE_NO_MATCH) + +/** + * Equivalent to (result==USTRINGTRIE_INTERMEDIATE_VALUE || result==USTRINGTRIE_FINAL_VALUE) but + * this macro evaluates result exactly once. + * @param result A result from BytesTrie::first(), UCharsTrie::next() etc. + * @return true if there is a value for the input bytes/units so far. + * @see BytesTrie::getValue + * @see UCharsTrie::getValue + * @stable ICU 4.8 + */ +#define USTRINGTRIE_HAS_VALUE(result) ((result)>=USTRINGTRIE_FINAL_VALUE) + +/** + * Equivalent to (result==USTRINGTRIE_NO_VALUE || result==USTRINGTRIE_INTERMEDIATE_VALUE) but + * this macro evaluates result exactly once. + * @param result A result from BytesTrie::first(), UCharsTrie::next() etc. + * @return true if another input byte/unit can continue a matching string. + * @stable ICU 4.8 + */ +#define USTRINGTRIE_HAS_NEXT(result) ((result)&1) + +#endif /* __USTRINGTRIE_H__ */ diff --git a/deps/icu-small/source/common/unicode/utext.h b/deps/icu-small/source/common/unicode/utext.h index c6d1e53a8cef10..10683e68f5614c 100644 --- a/deps/icu-small/source/common/unicode/utext.h +++ b/deps/icu-small/source/common/unicode/utext.h @@ -1,1603 +1,1603 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2004-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: utext.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004oct06 -* created by: Markus W. Scherer -*/ - -#ifndef __UTEXT_H__ -#define __UTEXT_H__ - -/** - * \file - * \brief C API: Abstract Unicode Text API - * - * The Text Access API provides a means to allow text that is stored in alternative - * formats to work with ICU services. ICU normally operates on text that is - * stored in UTF-16 format, in (UChar *) arrays for the C APIs or as type - * UnicodeString for C++ APIs. - * - * ICU Text Access allows other formats, such as UTF-8 or non-contiguous - * UTF-16 strings, to be placed in a UText wrapper and then passed to ICU services. - * - * There are three general classes of usage for UText: - * - * Application Level Use. This is the simplest usage - applications would - * use one of the utext_open() functions on their input text, and pass - * the resulting UText to the desired ICU service. - * - * Second is usage in ICU Services, such as break iteration, that will need to - * operate on input presented to them as a UText. These implementations - * will need to use the iteration and related UText functions to gain - * access to the actual text. - * - * The third class of UText users are "text providers." These are the - * UText implementations for the various text storage formats. An application - * or system with a unique text storage format can implement a set of - * UText provider functions for that format, which will then allow - * ICU services to operate on that format. - * - * - * Iterating over text - * - * Here is sample code for a forward iteration over the contents of a UText - * - * \code - * UChar32 c; - * UText *ut = whatever(); - * - * for (c=utext_next32From(ut, 0); c>=0; c=utext_next32(ut)) { - * // do whatever with the codepoint c here. - * } - * \endcode - * - * And here is similar code to iterate in the reverse direction, from the end - * of the text towards the beginning. - * - * \code - * UChar32 c; - * UText *ut = whatever(); - * int textLength = utext_nativeLength(ut); - * for (c=utext_previous32From(ut, textLength); c>=0; c=utext_previous32(ut)) { - * // do whatever with the codepoint c here. - * } - * \endcode - * - * Characters and Indexing - * - * Indexing into text by UText functions is nearly always in terms of the native - * indexing of the underlying text storage. The storage format could be UTF-8 - * or UTF-32, for example. When coding to the UText access API, no assumptions - * can be made regarding the size of characters, or how far an index - * may move when iterating between characters. - * - * All indices supplied to UText functions are pinned to the length of the - * text. An out-of-bounds index is not considered to be an error, but is - * adjusted to be in the range 0 <= index <= length of input text. - * - * - * When an index position is returned from a UText function, it will be - * a native index to the underlying text. In the case of multi-unit characters, - * it will always refer to the first position of the character, - * never to the interior. This is essentially the same thing as saying that - * a returned index will always point to a boundary between characters. - * - * When a native index is supplied to a UText function, all indices that - * refer to any part of a multi-unit character representation are considered - * to be equivalent. In the case of multi-unit characters, an incoming index - * will be logically normalized to refer to the start of the character. - * - * It is possible to test whether a native index is on a code point boundary - * by doing a utext_setNativeIndex() followed by a utext_getNativeIndex(). - * If the index is returned unchanged, it was on a code point boundary. If - * an adjusted index is returned, the original index referred to the - * interior of a character. - * - * Conventions for calling UText functions - * - * Most UText access functions have as their first parameter a (UText *) pointer, - * which specifies the UText to be used. Unless otherwise noted, the - * pointer must refer to a valid, open UText. Attempting to - * use a closed UText or passing a NULL pointer is a programming error and - * will produce undefined results or NULL pointer exceptions. - * - * The UText_Open family of functions can either open an existing (closed) - * UText, or heap allocate a new UText. Here is sample code for creating - * a stack-allocated UText. - * - * \code - * char *s = whatever(); // A utf-8 string - * U_ErrorCode status = U_ZERO_ERROR; - * UText ut = UTEXT_INITIALIZER; - * utext_openUTF8(ut, s, -1, &status); - * if (U_FAILURE(status)) { - * // error handling - * } else { - * // work with the UText - * } - * \endcode - * - * Any existing UText passed to an open function _must_ have been initialized, - * either by the UTEXT_INITIALIZER, or by having been originally heap-allocated - * by an open function. Passing NULL will cause the open function to - * heap-allocate and fully initialize a new UText. - * - */ - - - -#include "unicode/utypes.h" -#include "unicode/uchar.h" -#if U_SHOW_CPLUSPLUS_API -#include "unicode/localpointer.h" -#include "unicode/rep.h" -#include "unicode/unistr.h" -#include "unicode/chariter.h" -#endif - - -U_CDECL_BEGIN - -struct UText; -typedef struct UText UText; /**< C typedef for struct UText. @stable ICU 3.6 */ - - -/*************************************************************************************** - * - * C Functions for creating UText wrappers around various kinds of text strings. - * - ****************************************************************************************/ - - -/** - * Close function for UText instances. - * Cleans up, releases any resources being held by an open UText. - *

- * If the UText was originally allocated by one of the utext_open functions, - * the storage associated with the utext will also be freed. - * If the UText storage originated with the application, as it would with - * a local or static instance, the storage will not be deleted. - * - * An open UText can be reset to refer to new string by using one of the utext_open() - * functions without first closing the UText. - * - * @param ut The UText to be closed. - * @return NULL if the UText struct was deleted by the close. If the UText struct - * was originally provided by the caller to the open function, it is - * returned by this function, and may be safely used again in - * a subsequent utext_open. - * - * @stable ICU 3.4 - */ -U_CAPI UText * U_EXPORT2 -utext_close(UText *ut); - -/** - * Open a read-only UText implementation for UTF-8 strings. - * - * \htmlonly - * Any invalid UTF-8 in the input will be handled in this way: - * a sequence of bytes that has the form of a truncated, but otherwise valid, - * UTF-8 sequence will be replaced by a single unicode replacement character, \uFFFD. - * Any other illegal bytes will each be replaced by a \uFFFD. - * \endhtmlonly - * - * @param ut Pointer to a UText struct. If NULL, a new UText will be created. - * If non-NULL, must refer to an initialized UText struct, which will then - * be reset to reference the specified UTF-8 string. - * @param s A UTF-8 string. Must not be NULL. - * @param length The length of the UTF-8 string in bytes, or -1 if the string is - * zero terminated. - * @param status Errors are returned here. - * @return A pointer to the UText. If a pre-allocated UText was provided, it - * will always be used and returned. - * @stable ICU 3.4 - */ -U_CAPI UText * U_EXPORT2 -utext_openUTF8(UText *ut, const char *s, int64_t length, UErrorCode *status); - - -/** - * Open a read-only UText for UChar * string. - * - * @param ut Pointer to a UText struct. If NULL, a new UText will be created. - * If non-NULL, must refer to an initialized UText struct, which will then - * be reset to reference the specified UChar string. - * @param s A UChar (UTF-16) string - * @param length The number of UChars in the input string, or -1 if the string is - * zero terminated. - * @param status Errors are returned here. - * @return A pointer to the UText. If a pre-allocated UText was provided, it - * will always be used and returned. - * @stable ICU 3.4 - */ -U_CAPI UText * U_EXPORT2 -utext_openUChars(UText *ut, const UChar *s, int64_t length, UErrorCode *status); - - -#if U_SHOW_CPLUSPLUS_API -/** - * Open a writable UText for a non-const UnicodeString. - * - * @param ut Pointer to a UText struct. If NULL, a new UText will be created. - * If non-NULL, must refer to an initialized UText struct, which will then - * be reset to reference the specified input string. - * @param s A UnicodeString. - * @param status Errors are returned here. - * @return Pointer to the UText. If a UText was supplied as input, this - * will always be used and returned. - * @stable ICU 3.4 - */ -U_CAPI UText * U_EXPORT2 -utext_openUnicodeString(UText *ut, icu::UnicodeString *s, UErrorCode *status); - - -/** - * Open a UText for a const UnicodeString. The resulting UText will not be writable. - * - * @param ut Pointer to a UText struct. If NULL, a new UText will be created. - * If non-NULL, must refer to an initialized UText struct, which will then - * be reset to reference the specified input string. - * @param s A const UnicodeString to be wrapped. - * @param status Errors are returned here. - * @return Pointer to the UText. If a UText was supplied as input, this - * will always be used and returned. - * @stable ICU 3.4 - */ -U_CAPI UText * U_EXPORT2 -utext_openConstUnicodeString(UText *ut, const icu::UnicodeString *s, UErrorCode *status); - - -/** - * Open a writable UText implementation for an ICU Replaceable object. - * @param ut Pointer to a UText struct. If NULL, a new UText will be created. - * If non-NULL, must refer to an already existing UText, which will then - * be reset to reference the specified replaceable text. - * @param rep A Replaceable text object. - * @param status Errors are returned here. - * @return Pointer to the UText. If a UText was supplied as input, this - * will always be used and returned. - * @see Replaceable - * @stable ICU 3.4 - */ -U_CAPI UText * U_EXPORT2 -utext_openReplaceable(UText *ut, icu::Replaceable *rep, UErrorCode *status); - -/** - * Open a UText implementation over an ICU CharacterIterator. - * @param ut Pointer to a UText struct. If NULL, a new UText will be created. - * If non-NULL, must refer to an already existing UText, which will then - * be reset to reference the specified replaceable text. - * @param ci A Character Iterator. - * @param status Errors are returned here. - * @return Pointer to the UText. If a UText was supplied as input, this - * will always be used and returned. - * @see Replaceable - * @stable ICU 3.4 - */ -U_CAPI UText * U_EXPORT2 -utext_openCharacterIterator(UText *ut, icu::CharacterIterator *ci, UErrorCode *status); - -#endif - - -/** - * Clone a UText. This is much like opening a UText where the source text is itself - * another UText. - * - * A deep clone will copy both the UText data structures and the underlying text. - * The original and cloned UText will operate completely independently; modifications - * made to the text in one will not affect the other. Text providers are not - * required to support deep clones. The user of clone() must check the status return - * and be prepared to handle failures. - * - * The standard UText implementations for UTF8, UChar *, UnicodeString and - * Replaceable all support deep cloning. - * - * The UText returned from a deep clone will be writable, assuming that the text - * provider is able to support writing, even if the source UText had been made - * non-writable by means of UText_freeze(). - * - * A shallow clone replicates only the UText data structures; it does not make - * a copy of the underlying text. Shallow clones can be used as an efficient way to - * have multiple iterators active in a single text string that is not being - * modified. - * - * A shallow clone operation will not fail, barring truly exceptional conditions such - * as memory allocation failures. - * - * Shallow UText clones should be avoided if the UText functions that modify the - * text are expected to be used, either on the original or the cloned UText. - * Any such modifications can cause unpredictable behavior. Read Only - * shallow clones provide some protection against errors of this type by - * disabling text modification via the cloned UText. - * - * A shallow clone made with the readOnly parameter == false will preserve the - * utext_isWritable() state of the source object. Note, however, that - * write operations must be avoided while more than one UText exists that refer - * to the same underlying text. - * - * A UText and its clone may be safely concurrently accessed by separate threads. - * This is true for read access only with shallow clones, and for both read and - * write access with deep clones. - * It is the responsibility of the Text Provider to ensure that this thread safety - * constraint is met. - * - * @param dest A UText struct to be filled in with the result of the clone operation, - * or NULL if the clone function should heap-allocate a new UText struct. - * If non-NULL, must refer to an already existing UText, which will then - * be reset to become the clone. - * @param src The UText to be cloned. - * @param deep true to request a deep clone, false for a shallow clone. - * @param readOnly true to request that the cloned UText have read only access to the - * underlying text. - - * @param status Errors are returned here. For deep clones, U_UNSUPPORTED_ERROR - * will be returned if the text provider is unable to clone the - * original text. - * @return The newly created clone, or NULL if the clone operation failed. - * @stable ICU 3.4 - */ -U_CAPI UText * U_EXPORT2 -utext_clone(UText *dest, const UText *src, UBool deep, UBool readOnly, UErrorCode *status); - - -/** - * Compare two UText objects for equality. - * UTexts are equal if they are iterating over the same text, and - * have the same iteration position within the text. - * If either or both of the parameters are NULL, the comparison is false. - * - * @param a The first of the two UTexts to compare. - * @param b The other UText to be compared. - * @return true if the two UTexts are equal. - * @stable ICU 3.6 - */ -U_CAPI UBool U_EXPORT2 -utext_equals(const UText *a, const UText *b); - - -/***************************************************************************** - * - * Functions to work with the text represented by a UText wrapper - * - *****************************************************************************/ - -/** - * Get the length of the text. Depending on the characteristics - * of the underlying text representation, this may be expensive. - * @see utext_isLengthExpensive() - * - * - * @param ut the text to be accessed. - * @return the length of the text, expressed in native units. - * - * @stable ICU 3.4 - */ -U_CAPI int64_t U_EXPORT2 -utext_nativeLength(UText *ut); - -/** - * Return true if calculating the length of the text could be expensive. - * Finding the length of NUL terminated strings is considered to be expensive. - * - * Note that the value of this function may change - * as the result of other operations on a UText. - * Once the length of a string has been discovered, it will no longer - * be expensive to report it. - * - * @param ut the text to be accessed. - * @return true if determining the length of the text could be time consuming. - * @stable ICU 3.4 - */ -U_CAPI UBool U_EXPORT2 -utext_isLengthExpensive(const UText *ut); - -/** - * Returns the code point at the requested index, - * or U_SENTINEL (-1) if it is out of bounds. - * - * If the specified index points to the interior of a multi-unit - * character - one of the trail bytes of a UTF-8 sequence, for example - - * the complete code point will be returned. - * - * The iteration position will be set to the start of the returned code point. - * - * This function is roughly equivalent to the sequence - * utext_setNativeIndex(index); - * utext_current32(); - * (There is a subtle difference if the index is out of bounds by being less than zero - - * utext_setNativeIndex(negative value) sets the index to zero, after which utext_current() - * will return the char at zero. utext_char32At(negative index), on the other hand, will - * return the U_SENTINEL value of -1.) - * - * @param ut the text to be accessed - * @param nativeIndex the native index of the character to be accessed. If the index points - * to other than the first unit of a multi-unit character, it will be adjusted - * to the start of the character. - * @return the code point at the specified index. - * @stable ICU 3.4 - */ -U_CAPI UChar32 U_EXPORT2 -utext_char32At(UText *ut, int64_t nativeIndex); - - -/** - * - * Get the code point at the current iteration position, - * or U_SENTINEL (-1) if the iteration has reached the end of - * the input text. - * - * @param ut the text to be accessed. - * @return the Unicode code point at the current iterator position. - * @stable ICU 3.4 - */ -U_CAPI UChar32 U_EXPORT2 -utext_current32(UText *ut); - - -/** - * Get the code point at the current iteration position of the UText, and - * advance the position to the first index following the character. - * - * If the position is at the end of the text (the index following - * the last character, which is also the length of the text), - * return U_SENTINEL (-1) and do not advance the index. - * - * This is a post-increment operation. - * - * An inline macro version of this function, UTEXT_NEXT32(), - * is available for performance critical use. - * - * @param ut the text to be accessed. - * @return the Unicode code point at the iteration position. - * @see UTEXT_NEXT32 - * @stable ICU 3.4 - */ -U_CAPI UChar32 U_EXPORT2 -utext_next32(UText *ut); - - -/** - * Move the iterator position to the character (code point) whose - * index precedes the current position, and return that character. - * This is a pre-decrement operation. - * - * If the initial position is at the start of the text (index of 0) - * return U_SENTINEL (-1), and leave the position unchanged. - * - * An inline macro version of this function, UTEXT_PREVIOUS32(), - * is available for performance critical use. - * - * @param ut the text to be accessed. - * @return the previous UChar32 code point, or U_SENTINEL (-1) - * if the iteration has reached the start of the text. - * @see UTEXT_PREVIOUS32 - * @stable ICU 3.4 - */ -U_CAPI UChar32 U_EXPORT2 -utext_previous32(UText *ut); - - -/** - * Set the iteration index and return the code point at that index. - * Leave the iteration index at the start of the following code point. - * - * This function is the most efficient and convenient way to - * begin a forward iteration. The results are identical to the those - * from the sequence - * \code - * utext_setIndex(); - * utext_next32(); - * \endcode - * - * @param ut the text to be accessed. - * @param nativeIndex Iteration index, in the native units of the text provider. - * @return Code point which starts at or before index, - * or U_SENTINEL (-1) if it is out of bounds. - * @stable ICU 3.4 - */ -U_CAPI UChar32 U_EXPORT2 -utext_next32From(UText *ut, int64_t nativeIndex); - - - -/** - * Set the iteration index, and return the code point preceding the - * one specified by the initial index. Leave the iteration position - * at the start of the returned code point. - * - * This function is the most efficient and convenient way to - * begin a backwards iteration. - * - * @param ut the text to be accessed. - * @param nativeIndex Iteration index in the native units of the text provider. - * @return Code point preceding the one at the initial index, - * or U_SENTINEL (-1) if it is out of bounds. - * - * @stable ICU 3.4 - */ -U_CAPI UChar32 U_EXPORT2 -utext_previous32From(UText *ut, int64_t nativeIndex); - -/** - * Get the current iterator position, which can range from 0 to - * the length of the text. - * The position is a native index into the input text, in whatever format it - * may have (possibly UTF-8 for example), and may not always be the same as - * the corresponding UChar (UTF-16) index. - * The returned position will always be aligned to a code point boundary. - * - * @param ut the text to be accessed. - * @return the current index position, in the native units of the text provider. - * @stable ICU 3.4 - */ -U_CAPI int64_t U_EXPORT2 -utext_getNativeIndex(const UText *ut); - -/** - * Set the current iteration position to the nearest code point - * boundary at or preceding the specified index. - * The index is in the native units of the original input text. - * If the index is out of range, it will be pinned to be within - * the range of the input text. - *

- * It will usually be more efficient to begin an iteration - * using the functions utext_next32From() or utext_previous32From() - * rather than setIndex(). - *

- * Moving the index position to an adjacent character is best done - * with utext_next32(), utext_previous32() or utext_moveIndex32(). - * Attempting to do direct arithmetic on the index position is - * complicated by the fact that the size (in native units) of a - * character depends on the underlying representation of the character - * (UTF-8, UTF-16, UTF-32, arbitrary codepage), and is not - * easily knowable. - * - * @param ut the text to be accessed. - * @param nativeIndex the native unit index of the new iteration position. - * @stable ICU 3.4 - */ -U_CAPI void U_EXPORT2 -utext_setNativeIndex(UText *ut, int64_t nativeIndex); - -/** - * Move the iterator position by delta code points. The number of code points - * is a signed number; a negative delta will move the iterator backwards, - * towards the start of the text. - *

- * The index is moved by delta code points - * forward or backward, but no further backward than to 0 and - * no further forward than to utext_nativeLength(). - * The resulting index value will be in between 0 and length, inclusive. - * - * @param ut the text to be accessed. - * @param delta the signed number of code points to move the iteration position. - * @return true if the position could be moved the requested number of positions while - * staying within the range [0 - text length]. - * @stable ICU 3.4 - */ -U_CAPI UBool U_EXPORT2 -utext_moveIndex32(UText *ut, int32_t delta); - -/** - * Get the native index of the character preceding the current position. - * If the iteration position is already at the start of the text, zero - * is returned. - * The value returned is the same as that obtained from the following sequence, - * but without the side effect of changing the iteration position. - * - * \code - * UText *ut = whatever; - * ... - * utext_previous(ut) - * utext_getNativeIndex(ut); - * \endcode - * - * This function is most useful during forwards iteration, where it will get the - * native index of the character most recently returned from utext_next(). - * - * @param ut the text to be accessed - * @return the native index of the character preceding the current index position, - * or zero if the current position is at the start of the text. - * @stable ICU 3.6 - */ -U_CAPI int64_t U_EXPORT2 -utext_getPreviousNativeIndex(UText *ut); - - -/** - * - * Extract text from a UText into a UChar buffer. The range of text to be extracted - * is specified in the native indices of the UText provider. These may not necessarily - * be UTF-16 indices. - *

- * The size (number of 16 bit UChars) of the data to be extracted is returned. The - * full number of UChars is returned, even when the extracted text is truncated - * because the specified buffer size is too small. - *

- * The extracted string will (if you are a user) / must (if you are a text provider) - * be NUL-terminated if there is sufficient space in the destination buffer. This - * terminating NUL is not included in the returned length. - *

- * The iteration index is left at the position following the last extracted character. - * - * @param ut the UText from which to extract data. - * @param nativeStart the native index of the first character to extract.\ - * If the specified index is out of range, - * it will be pinned to be within 0 <= index <= textLength - * @param nativeLimit the native string index of the position following the last - * character to extract. If the specified index is out of range, - * it will be pinned to be within 0 <= index <= textLength. - * nativeLimit must be >= nativeStart. - * @param dest the UChar (UTF-16) buffer into which the extracted text is placed - * @param destCapacity The size, in UChars, of the destination buffer. May be zero - * for precomputing the required size. - * @param status receives any error status. - * U_BUFFER_OVERFLOW_ERROR: the extracted text was truncated because the - * buffer was too small. Returns number of UChars for preflighting. - * @return Number of UChars in the data to be extracted. Does not include a trailing NUL. - * - * @stable ICU 3.4 - */ -U_CAPI int32_t U_EXPORT2 -utext_extract(UText *ut, - int64_t nativeStart, int64_t nativeLimit, - UChar *dest, int32_t destCapacity, - UErrorCode *status); - - - -/************************************************************************************ - * - * #define inline versions of selected performance-critical text access functions - * Caution: do not use auto increment++ or decrement-- expressions - * as parameters to these macros. - * - * For most use, where there is no extreme performance constraint, the - * normal, non-inline functions are a better choice. The resulting code - * will be smaller, and, if the need ever arises, easier to debug. - * - * These are implemented as #defines rather than real functions - * because there is no fully portable way to do inline functions in plain C. - * - ************************************************************************************/ - -#ifndef U_HIDE_INTERNAL_API -/** - * inline version of utext_current32(), for performance-critical situations. - * - * Get the code point at the current iteration position of the UText. - * Returns U_SENTINEL (-1) if the position is at the end of the - * text. - * - * @internal ICU 4.4 technology preview - */ -#define UTEXT_CURRENT32(ut) \ - ((ut)->chunkOffset < (ut)->chunkLength && ((ut)->chunkContents)[(ut)->chunkOffset]<0xd800 ? \ - ((ut)->chunkContents)[((ut)->chunkOffset)] : utext_current32(ut)) -#endif /* U_HIDE_INTERNAL_API */ - -/** - * inline version of utext_next32(), for performance-critical situations. - * - * Get the code point at the current iteration position of the UText, and - * advance the position to the first index following the character. - * This is a post-increment operation. - * Returns U_SENTINEL (-1) if the position is at the end of the - * text. - * - * @stable ICU 3.4 - */ -#define UTEXT_NEXT32(ut) \ - ((ut)->chunkOffset < (ut)->chunkLength && ((ut)->chunkContents)[(ut)->chunkOffset]<0xd800 ? \ - ((ut)->chunkContents)[((ut)->chunkOffset)++] : utext_next32(ut)) - -/** - * inline version of utext_previous32(), for performance-critical situations. - * - * Move the iterator position to the character (code point) whose - * index precedes the current position, and return that character. - * This is a pre-decrement operation. - * Returns U_SENTINEL (-1) if the position is at the start of the text. - * - * @stable ICU 3.4 - */ -#define UTEXT_PREVIOUS32(ut) \ - ((ut)->chunkOffset > 0 && \ - (ut)->chunkContents[(ut)->chunkOffset-1] < 0xd800 ? \ - (ut)->chunkContents[--((ut)->chunkOffset)] : utext_previous32(ut)) - -/** - * inline version of utext_getNativeIndex(), for performance-critical situations. - * - * Get the current iterator position, which can range from 0 to - * the length of the text. - * The position is a native index into the input text, in whatever format it - * may have (possibly UTF-8 for example), and may not always be the same as - * the corresponding UChar (UTF-16) index. - * The returned position will always be aligned to a code point boundary. - * - * @stable ICU 3.6 - */ -#define UTEXT_GETNATIVEINDEX(ut) \ - ((ut)->chunkOffset <= (ut)->nativeIndexingLimit? \ - (ut)->chunkNativeStart+(ut)->chunkOffset : \ - (ut)->pFuncs->mapOffsetToNative(ut)) - -/** - * inline version of utext_setNativeIndex(), for performance-critical situations. - * - * Set the current iteration position to the nearest code point - * boundary at or preceding the specified index. - * The index is in the native units of the original input text. - * If the index is out of range, it will be pinned to be within - * the range of the input text. - * - * @stable ICU 3.8 - */ -#define UTEXT_SETNATIVEINDEX(ut, ix) UPRV_BLOCK_MACRO_BEGIN { \ - int64_t __offset = (ix) - (ut)->chunkNativeStart; \ - if (__offset>=0 && __offset<(int64_t)(ut)->nativeIndexingLimit && (ut)->chunkContents[__offset]<0xdc00) { \ - (ut)->chunkOffset=(int32_t)__offset; \ - } else { \ - utext_setNativeIndex((ut), (ix)); \ - } \ -} UPRV_BLOCK_MACRO_END - - - -/************************************************************************************ - * - * Functions related to writing or modifying the text. - * These will work only with modifiable UTexts. Attempting to - * modify a read-only UText will return an error status. - * - ************************************************************************************/ - - -/** - * Return true if the text can be written (modified) with utext_replace() or - * utext_copy(). For the text to be writable, the text provider must - * be of a type that supports writing and the UText must not be frozen. - * - * Attempting to modify text when utext_isWriteable() is false will fail - - * the text will not be modified, and an error will be returned from the function - * that attempted the modification. - * - * @param ut the UText to be tested. - * @return true if the text is modifiable. - * - * @see utext_freeze() - * @see utext_replace() - * @see utext_copy() - * @stable ICU 3.4 - * - */ -U_CAPI UBool U_EXPORT2 -utext_isWritable(const UText *ut); - - -/** - * Test whether there is meta data associated with the text. - * @see Replaceable::hasMetaData() - * - * @param ut The UText to be tested - * @return true if the underlying text includes meta data. - * @stable ICU 3.4 - */ -U_CAPI UBool U_EXPORT2 -utext_hasMetaData(const UText *ut); - - -/** - * Replace a range of the original text with a replacement text. - * - * Leaves the current iteration position at the position following the - * newly inserted replacement text. - * - * This function is only available on UText types that support writing, - * that is, ones where utext_isWritable() returns true. - * - * When using this function, there should be only a single UText opened onto the - * underlying native text string. Behavior after a replace operation - * on a UText is undefined for any other additional UTexts that refer to the - * modified string. - * - * @param ut the UText representing the text to be operated on. - * @param nativeStart the native index of the start of the region to be replaced - * @param nativeLimit the native index of the character following the region to be replaced. - * @param replacementText pointer to the replacement text - * @param replacementLength length of the replacement text, or -1 if the text is NUL terminated. - * @param status receives any error status. Possible errors include - * U_NO_WRITE_PERMISSION - * - * @return The signed number of (native) storage units by which - * the length of the text expanded or contracted. - * - * @stable ICU 3.4 - */ -U_CAPI int32_t U_EXPORT2 -utext_replace(UText *ut, - int64_t nativeStart, int64_t nativeLimit, - const UChar *replacementText, int32_t replacementLength, - UErrorCode *status); - - - -/** - * - * Copy or move a substring from one position to another within the text, - * while retaining any metadata associated with the text. - * This function is used to duplicate or reorder substrings. - * The destination index must not overlap the source range. - * - * The text to be copied or moved is inserted at destIndex; - * it does not replace or overwrite any existing text. - * - * The iteration position is left following the newly inserted text - * at the destination position. - * - * This function is only available on UText types that support writing, - * that is, ones where utext_isWritable() returns true. - * - * When using this function, there should be only a single UText opened onto the - * underlying native text string. Behavior after a copy operation - * on a UText is undefined in any other additional UTexts that refer to the - * modified string. - * - * @param ut The UText representing the text to be operated on. - * @param nativeStart The native index of the start of the region to be copied or moved - * @param nativeLimit The native index of the character position following the region - * to be copied. - * @param destIndex The native destination index to which the source substring is - * copied or moved. - * @param move If true, then the substring is moved, not copied/duplicated. - * @param status receives any error status. Possible errors include U_NO_WRITE_PERMISSION - * - * @stable ICU 3.4 - */ -U_CAPI void U_EXPORT2 -utext_copy(UText *ut, - int64_t nativeStart, int64_t nativeLimit, - int64_t destIndex, - UBool move, - UErrorCode *status); - - -/** - *

- * Freeze a UText. This prevents any modification to the underlying text itself - * by means of functions operating on this UText. - *

- *

- * Once frozen, a UText can not be unfrozen. The intent is to ensure - * that a the text underlying a frozen UText wrapper cannot be modified via that UText. - *

- *

- * Caution: freezing a UText will disable changes made via the specific - * frozen UText wrapper only; it will not have any effect on the ability to - * directly modify the text by bypassing the UText. Any such backdoor modifications - * are always an error while UText access is occurring because the underlying - * text can get out of sync with UText's buffering. - *

- * - * @param ut The UText to be frozen. - * @see utext_isWritable() - * @stable ICU 3.6 - */ -U_CAPI void U_EXPORT2 -utext_freeze(UText *ut); - - -/** - * UText provider properties (bit field indexes). - * - * @see UText - * @stable ICU 3.4 - */ -enum { - /** - * It is potentially time consuming for the provider to determine the length of the text. - * @stable ICU 3.4 - */ - UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE = 1, - /** - * Text chunks remain valid and usable until the text object is modified or - * deleted, not just until the next time the access() function is called - * (which is the default). - * @stable ICU 3.4 - */ - UTEXT_PROVIDER_STABLE_CHUNKS = 2, - /** - * The provider supports modifying the text via the replace() and copy() - * functions. - * @see Replaceable - * @stable ICU 3.4 - */ - UTEXT_PROVIDER_WRITABLE = 3, - /** - * There is meta data associated with the text. - * @see Replaceable::hasMetaData() - * @stable ICU 3.4 - */ - UTEXT_PROVIDER_HAS_META_DATA = 4, - /** - * Text provider owns the text storage. - * Generally occurs as the result of a deep clone of the UText. - * When closing the UText, the associated text must - * also be closed/deleted/freed/ whatever is appropriate. - * @stable ICU 3.6 - */ - UTEXT_PROVIDER_OWNS_TEXT = 5 -}; - -/** - * Function type declaration for UText.clone(). - * - * clone a UText. Much like opening a UText where the source text is itself - * another UText. - * - * A deep clone will copy both the UText data structures and the underlying text. - * The original and cloned UText will operate completely independently; modifications - * made to the text in one will not effect the other. Text providers are not - * required to support deep clones. The user of clone() must check the status return - * and be prepared to handle failures. - * - * A shallow clone replicates only the UText data structures; it does not make - * a copy of the underlying text. Shallow clones can be used as an efficient way to - * have multiple iterators active in a single text string that is not being - * modified. - * - * A shallow clone operation must not fail except for truly exceptional conditions such - * as memory allocation failures. - * - * A UText and its clone may be safely concurrently accessed by separate threads. - * This is true for both shallow and deep clones. - * It is the responsibility of the Text Provider to ensure that this thread safety - * constraint is met. - - * - * @param dest A UText struct to be filled in with the result of the clone operation, - * or NULL if the clone function should heap-allocate a new UText struct. - * @param src The UText to be cloned. - * @param deep true to request a deep clone, false for a shallow clone. - * @param status Errors are returned here. For deep clones, U_UNSUPPORTED_ERROR - * should be returned if the text provider is unable to clone the - * original text. - * @return The newly created clone, or NULL if the clone operation failed. - * - * @stable ICU 3.4 - */ -typedef UText * U_CALLCONV -UTextClone(UText *dest, const UText *src, UBool deep, UErrorCode *status); - - -/** - * Function type declaration for UText.nativeLength(). - * - * @param ut the UText to get the length of. - * @return the length, in the native units of the original text string. - * @see UText - * @stable ICU 3.4 - */ -typedef int64_t U_CALLCONV -UTextNativeLength(UText *ut); - -/** - * Function type declaration for UText.access(). Get the description of the text chunk - * containing the text at a requested native index. The UText's iteration - * position will be left at the requested index. If the index is out - * of bounds, the iteration position will be left at the start or end - * of the string, as appropriate. - * - * Chunks must begin and end on code point boundaries. A single code point - * comprised of multiple storage units must never span a chunk boundary. - * - * - * @param ut the UText being accessed. - * @param nativeIndex Requested index of the text to be accessed. - * @param forward If true, then the returned chunk must contain text - * starting from the index, so that start<=index - * The size (number of 16 bit UChars) in the data to be extracted is returned. The - * full amount is returned, even when the specified buffer size is smaller. - *

- * The extracted string will (if you are a user) / must (if you are a text provider) - * be NUL-terminated if there is sufficient space in the destination buffer. - * - * @param ut the UText from which to extract data. - * @param nativeStart the native index of the first character to extract. - * @param nativeLimit the native string index of the position following the last - * character to extract. - * @param dest the UChar (UTF-16) buffer into which the extracted text is placed - * @param destCapacity The size, in UChars, of the destination buffer. May be zero - * for precomputing the required size. - * @param status receives any error status. - * If U_BUFFER_OVERFLOW_ERROR: Returns number of UChars for - * preflighting. - * @return Number of UChars in the data. Does not include a trailing NUL. - * - * @stable ICU 3.4 - */ -typedef int32_t U_CALLCONV -UTextExtract(UText *ut, - int64_t nativeStart, int64_t nativeLimit, - UChar *dest, int32_t destCapacity, - UErrorCode *status); - -/** - * Function type declaration for UText.replace(). - * - * Replace a range of the original text with a replacement text. - * - * Leaves the current iteration position at the position following the - * newly inserted replacement text. - * - * This function need only be implemented on UText types that support writing. - * - * When using this function, there should be only a single UText opened onto the - * underlying native text string. The function is responsible for updating the - * text chunk within the UText to reflect the updated iteration position, - * taking into account any changes to the underlying string's structure caused - * by the replace operation. - * - * @param ut the UText representing the text to be operated on. - * @param nativeStart the index of the start of the region to be replaced - * @param nativeLimit the index of the character following the region to be replaced. - * @param replacementText pointer to the replacement text - * @param replacmentLength length of the replacement text in UChars, or -1 if the text is NUL terminated. - * @param status receives any error status. Possible errors include - * U_NO_WRITE_PERMISSION - * - * @return The signed number of (native) storage units by which - * the length of the text expanded or contracted. - * - * @stable ICU 3.4 - */ -typedef int32_t U_CALLCONV -UTextReplace(UText *ut, - int64_t nativeStart, int64_t nativeLimit, - const UChar *replacementText, int32_t replacmentLength, - UErrorCode *status); - -/** - * Function type declaration for UText.copy(). - * - * Copy or move a substring from one position to another within the text, - * while retaining any metadata associated with the text. - * This function is used to duplicate or reorder substrings. - * The destination index must not overlap the source range. - * - * The text to be copied or moved is inserted at destIndex; - * it does not replace or overwrite any existing text. - * - * This function need only be implemented for UText types that support writing. - * - * When using this function, there should be only a single UText opened onto the - * underlying native text string. The function is responsible for updating the - * text chunk within the UText to reflect the updated iteration position, - * taking into account any changes to the underlying string's structure caused - * by the replace operation. - * - * @param ut The UText representing the text to be operated on. - * @param nativeStart The index of the start of the region to be copied or moved - * @param nativeLimit The index of the character following the region to be replaced. - * @param nativeDest The destination index to which the source substring is copied or moved. - * @param move If true, then the substring is moved, not copied/duplicated. - * @param status receives any error status. Possible errors include U_NO_WRITE_PERMISSION - * - * @stable ICU 3.4 - */ -typedef void U_CALLCONV -UTextCopy(UText *ut, - int64_t nativeStart, int64_t nativeLimit, - int64_t nativeDest, - UBool move, - UErrorCode *status); - -/** - * Function type declaration for UText.mapOffsetToNative(). - * Map from the current UChar offset within the current text chunk to - * the corresponding native index in the original source text. - * - * This is required only for text providers that do not use native UTF-16 indexes. - * - * @param ut the UText. - * @return Absolute (native) index corresponding to chunkOffset in the current chunk. - * The returned native index should always be to a code point boundary. - * - * @stable ICU 3.4 - */ -typedef int64_t U_CALLCONV -UTextMapOffsetToNative(const UText *ut); - -/** - * Function type declaration for UText.mapIndexToUTF16(). - * Map from a native index to a UChar offset within a text chunk. - * Behavior is undefined if the native index does not fall within the - * current chunk. - * - * This function is required only for text providers that do not use native UTF-16 indexes. - * - * @param ut The UText containing the text chunk. - * @param nativeIndex Absolute (native) text index, chunk->start<=index<=chunk->limit. - * @return Chunk-relative UTF-16 offset corresponding to the specified native - * index. - * - * @stable ICU 3.4 - */ -typedef int32_t U_CALLCONV -UTextMapNativeIndexToUTF16(const UText *ut, int64_t nativeIndex); - - -/** - * Function type declaration for UText.utextClose(). - * - * A Text Provider close function is only required for provider types that make - * allocations in their open function (or other functions) that must be - * cleaned when the UText is closed. - * - * The allocation of the UText struct itself and any "extra" storage - * associated with the UText is handled by the common UText implementation - * and does not require provider specific cleanup in a close function. - * - * Most UText provider implementations do not need to implement this function. - * - * @param ut A UText object to be closed. - * - * @stable ICU 3.4 - */ -typedef void U_CALLCONV -UTextClose(UText *ut); - - -/** - * (public) Function dispatch table for UText. - * Conceptually very much like a C++ Virtual Function Table. - * This struct defines the organization of the table. - * Each text provider implementation must provide an - * actual table that is initialized with the appropriate functions - * for the type of text being handled. - * @stable ICU 3.6 - */ -struct UTextFuncs { - /** - * (public) Function table size, sizeof(UTextFuncs) - * Intended for use should the table grow to accommodate added - * functions in the future, to allow tests for older format - * function tables that do not contain the extensions. - * - * Fields are placed for optimal alignment on - * 32/64/128-bit-pointer machines, by normally grouping together - * 4 32-bit fields, - * 4 pointers, - * 2 64-bit fields - * in sequence. - * @stable ICU 3.6 - */ - int32_t tableSize; - - /** - * (private) Alignment padding. - * Do not use, reserved for use by the UText framework only. - * @internal - */ - int32_t reserved1, /** @internal */ reserved2, /** @internal */ reserved3; - - - /** - * (public) Function pointer for UTextClone - * - * @see UTextClone - * @stable ICU 3.6 - */ - UTextClone *clone; - - /** - * (public) function pointer for UTextLength - * May be expensive to compute! - * - * @see UTextLength - * @stable ICU 3.6 - */ - UTextNativeLength *nativeLength; - - /** - * (public) Function pointer for UTextAccess. - * - * @see UTextAccess - * @stable ICU 3.6 - */ - UTextAccess *access; - - /** - * (public) Function pointer for UTextExtract. - * - * @see UTextExtract - * @stable ICU 3.6 - */ - UTextExtract *extract; - - /** - * (public) Function pointer for UTextReplace. - * - * @see UTextReplace - * @stable ICU 3.6 - */ - UTextReplace *replace; - - /** - * (public) Function pointer for UTextCopy. - * - * @see UTextCopy - * @stable ICU 3.6 - */ - UTextCopy *copy; - - /** - * (public) Function pointer for UTextMapOffsetToNative. - * - * @see UTextMapOffsetToNative - * @stable ICU 3.6 - */ - UTextMapOffsetToNative *mapOffsetToNative; - - /** - * (public) Function pointer for UTextMapNativeIndexToUTF16. - * - * @see UTextMapNativeIndexToUTF16 - * @stable ICU 3.6 - */ - UTextMapNativeIndexToUTF16 *mapNativeIndexToUTF16; - - /** - * (public) Function pointer for UTextClose. - * - * @see UTextClose - * @stable ICU 3.6 - */ - UTextClose *close; - - /** - * (private) Spare function pointer - * @internal - */ - UTextClose *spare1; - - /** - * (private) Spare function pointer - * @internal - */ - UTextClose *spare2; - - /** - * (private) Spare function pointer - * @internal - */ - UTextClose *spare3; - -}; -/** - * Function dispatch table for UText - * @see UTextFuncs - */ -typedef struct UTextFuncs UTextFuncs; - - /** - * UText struct. Provides the interface between the generic UText access code - * and the UText provider code that works on specific kinds of - * text (UTF-8, noncontiguous UTF-16, whatever.) - * - * Applications that are using predefined types of text providers - * to pass text data to ICU services will have no need to view the - * internals of the UText structs that they open. - * - * @stable ICU 3.6 - */ -struct UText { - /** - * (private) Magic. Used to help detect when UText functions are handed - * invalid or uninitialized UText structs. - * utext_openXYZ() functions take an initialized, - * but not necessarily open, UText struct as an - * optional fill-in parameter. This magic field - * is used to check for that initialization. - * Text provider close functions must NOT clear - * the magic field because that would prevent - * reuse of the UText struct. - * @internal - */ - uint32_t magic; - - - /** - * (private) Flags for managing the allocation and freeing of - * memory associated with this UText. - * @internal - */ - int32_t flags; - - - /** - * Text provider properties. This set of flags is maintained by the - * text provider implementation. - * @stable ICU 3.4 - */ - int32_t providerProperties; - - /** - * (public) sizeOfStruct=sizeof(UText) - * Allows possible backward compatible extension. - * - * @stable ICU 3.4 - */ - int32_t sizeOfStruct; - - /* ------ 16 byte alignment boundary ----------- */ - - - /** - * (protected) Native index of the first character position following - * the current chunk. - * @stable ICU 3.6 - */ - int64_t chunkNativeLimit; - - /** - * (protected) Size in bytes of the extra space (pExtra). - * @stable ICU 3.4 - */ - int32_t extraSize; - - /** - * (protected) The highest chunk offset where native indexing and - * chunk (UTF-16) indexing correspond. For UTF-16 sources, value - * will be equal to chunkLength. - * - * @stable ICU 3.6 - */ - int32_t nativeIndexingLimit; - - /* ---- 16 byte alignment boundary------ */ - - /** - * (protected) Native index of the first character in the text chunk. - * @stable ICU 3.6 - */ - int64_t chunkNativeStart; - - /** - * (protected) Current iteration position within the text chunk (UTF-16 buffer). - * This is the index to the character that will be returned by utext_next32(). - * @stable ICU 3.6 - */ - int32_t chunkOffset; - - /** - * (protected) Length the text chunk (UTF-16 buffer), in UChars. - * @stable ICU 3.6 - */ - int32_t chunkLength; - - /* ---- 16 byte alignment boundary-- */ - - - /** - * (protected) pointer to a chunk of text in UTF-16 format. - * May refer either to original storage of the source of the text, or - * if conversion was required, to a buffer owned by the UText. - * @stable ICU 3.6 - */ - const UChar *chunkContents; - - /** - * (public) Pointer to Dispatch table for accessing functions for this UText. - * @stable ICU 3.6 - */ - const UTextFuncs *pFuncs; - - /** - * (protected) Pointer to additional space requested by the - * text provider during the utext_open operation. - * @stable ICU 3.4 - */ - void *pExtra; - - /** - * (protected) Pointer to string or text-containing object or similar. - * This is the source of the text that this UText is wrapping, in a format - * that is known to the text provider functions. - * @stable ICU 3.4 - */ - const void *context; - - /* --- 16 byte alignment boundary--- */ - - /** - * (protected) Pointer fields available for use by the text provider. - * Not used by UText common code. - * @stable ICU 3.6 - */ - const void *p; - /** - * (protected) Pointer fields available for use by the text provider. - * Not used by UText common code. - * @stable ICU 3.6 - */ - const void *q; - /** - * (protected) Pointer fields available for use by the text provider. - * Not used by UText common code. - * @stable ICU 3.6 - */ - const void *r; - - /** - * Private field reserved for future use by the UText framework - * itself. This is not to be touched by the text providers. - * @internal ICU 3.4 - */ - void *privP; - - - /* --- 16 byte alignment boundary--- */ - - - /** - * (protected) Integer field reserved for use by the text provider. - * Not used by the UText framework, or by the client (user) of the UText. - * @stable ICU 3.4 - */ - int64_t a; - - /** - * (protected) Integer field reserved for use by the text provider. - * Not used by the UText framework, or by the client (user) of the UText. - * @stable ICU 3.4 - */ - int32_t b; - - /** - * (protected) Integer field reserved for use by the text provider. - * Not used by the UText framework, or by the client (user) of the UText. - * @stable ICU 3.4 - */ - int32_t c; - - /* ---- 16 byte alignment boundary---- */ - - - /** - * Private field reserved for future use by the UText framework - * itself. This is not to be touched by the text providers. - * @internal ICU 3.4 - */ - int64_t privA; - /** - * Private field reserved for future use by the UText framework - * itself. This is not to be touched by the text providers. - * @internal ICU 3.4 - */ - int32_t privB; - /** - * Private field reserved for future use by the UText framework - * itself. This is not to be touched by the text providers. - * @internal ICU 3.4 - */ - int32_t privC; -}; - - -/** - * Common function for use by Text Provider implementations to allocate and/or initialize - * a new UText struct. To be called in the implementation of utext_open() functions. - * If the supplied UText parameter is null, a new UText struct will be allocated on the heap. - * If the supplied UText is already open, the provider's close function will be called - * so that the struct can be reused by the open that is in progress. - * - * @param ut pointer to a UText struct to be re-used, or null if a new UText - * should be allocated. - * @param extraSpace The amount of additional space to be allocated as part - * of this UText, for use by types of providers that require - * additional storage. - * @param status Errors are returned here. - * @return pointer to the UText, allocated if necessary, with extra space set up if requested. - * @stable ICU 3.4 - */ -U_CAPI UText * U_EXPORT2 -utext_setup(UText *ut, int32_t extraSpace, UErrorCode *status); - -// do not use #ifndef U_HIDE_INTERNAL_API around the following! -/** - * @internal - * Value used to help identify correctly initialized UText structs. - * Note: must be publicly visible so that UTEXT_INITIALIZER can access it. - */ -enum { - UTEXT_MAGIC = 0x345ad82c -}; - -/** - * initializer to be used with local (stack) instances of a UText - * struct. UText structs must be initialized before passing - * them to one of the utext_open functions. - * - * @stable ICU 3.6 - */ -#define UTEXT_INITIALIZER { \ - UTEXT_MAGIC, /* magic */ \ - 0, /* flags */ \ - 0, /* providerProps */ \ - sizeof(UText), /* sizeOfStruct */ \ - 0, /* chunkNativeLimit */ \ - 0, /* extraSize */ \ - 0, /* nativeIndexingLimit */ \ - 0, /* chunkNativeStart */ \ - 0, /* chunkOffset */ \ - 0, /* chunkLength */ \ - NULL, /* chunkContents */ \ - NULL, /* pFuncs */ \ - NULL, /* pExtra */ \ - NULL, /* context */ \ - NULL, NULL, NULL, /* p, q, r */ \ - NULL, /* privP */ \ - 0, 0, 0, /* a, b, c */ \ - 0, 0, 0 /* privA,B,C, */ \ - } - - -U_CDECL_END - - -#if U_SHOW_CPLUSPLUS_API - -U_NAMESPACE_BEGIN - -/** - * \class LocalUTextPointer - * "Smart pointer" class, closes a UText via utext_close(). - * For most methods see the LocalPointerBase base class. - * - * @see LocalPointerBase - * @see LocalPointer - * @stable ICU 4.4 - */ -U_DEFINE_LOCAL_OPEN_POINTER(LocalUTextPointer, UText, utext_close); - -U_NAMESPACE_END - -#endif - - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2004-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: utext.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004oct06 +* created by: Markus W. Scherer +*/ + +#ifndef __UTEXT_H__ +#define __UTEXT_H__ + +/** + * \file + * \brief C API: Abstract Unicode Text API + * + * The Text Access API provides a means to allow text that is stored in alternative + * formats to work with ICU services. ICU normally operates on text that is + * stored in UTF-16 format, in (UChar *) arrays for the C APIs or as type + * UnicodeString for C++ APIs. + * + * ICU Text Access allows other formats, such as UTF-8 or non-contiguous + * UTF-16 strings, to be placed in a UText wrapper and then passed to ICU services. + * + * There are three general classes of usage for UText: + * + * Application Level Use. This is the simplest usage - applications would + * use one of the utext_open() functions on their input text, and pass + * the resulting UText to the desired ICU service. + * + * Second is usage in ICU Services, such as break iteration, that will need to + * operate on input presented to them as a UText. These implementations + * will need to use the iteration and related UText functions to gain + * access to the actual text. + * + * The third class of UText users are "text providers." These are the + * UText implementations for the various text storage formats. An application + * or system with a unique text storage format can implement a set of + * UText provider functions for that format, which will then allow + * ICU services to operate on that format. + * + * + * Iterating over text + * + * Here is sample code for a forward iteration over the contents of a UText + * + * \code + * UChar32 c; + * UText *ut = whatever(); + * + * for (c=utext_next32From(ut, 0); c>=0; c=utext_next32(ut)) { + * // do whatever with the codepoint c here. + * } + * \endcode + * + * And here is similar code to iterate in the reverse direction, from the end + * of the text towards the beginning. + * + * \code + * UChar32 c; + * UText *ut = whatever(); + * int textLength = utext_nativeLength(ut); + * for (c=utext_previous32From(ut, textLength); c>=0; c=utext_previous32(ut)) { + * // do whatever with the codepoint c here. + * } + * \endcode + * + * Characters and Indexing + * + * Indexing into text by UText functions is nearly always in terms of the native + * indexing of the underlying text storage. The storage format could be UTF-8 + * or UTF-32, for example. When coding to the UText access API, no assumptions + * can be made regarding the size of characters, or how far an index + * may move when iterating between characters. + * + * All indices supplied to UText functions are pinned to the length of the + * text. An out-of-bounds index is not considered to be an error, but is + * adjusted to be in the range 0 <= index <= length of input text. + * + * + * When an index position is returned from a UText function, it will be + * a native index to the underlying text. In the case of multi-unit characters, + * it will always refer to the first position of the character, + * never to the interior. This is essentially the same thing as saying that + * a returned index will always point to a boundary between characters. + * + * When a native index is supplied to a UText function, all indices that + * refer to any part of a multi-unit character representation are considered + * to be equivalent. In the case of multi-unit characters, an incoming index + * will be logically normalized to refer to the start of the character. + * + * It is possible to test whether a native index is on a code point boundary + * by doing a utext_setNativeIndex() followed by a utext_getNativeIndex(). + * If the index is returned unchanged, it was on a code point boundary. If + * an adjusted index is returned, the original index referred to the + * interior of a character. + * + * Conventions for calling UText functions + * + * Most UText access functions have as their first parameter a (UText *) pointer, + * which specifies the UText to be used. Unless otherwise noted, the + * pointer must refer to a valid, open UText. Attempting to + * use a closed UText or passing a NULL pointer is a programming error and + * will produce undefined results or NULL pointer exceptions. + * + * The UText_Open family of functions can either open an existing (closed) + * UText, or heap allocate a new UText. Here is sample code for creating + * a stack-allocated UText. + * + * \code + * char *s = whatever(); // A utf-8 string + * U_ErrorCode status = U_ZERO_ERROR; + * UText ut = UTEXT_INITIALIZER; + * utext_openUTF8(ut, s, -1, &status); + * if (U_FAILURE(status)) { + * // error handling + * } else { + * // work with the UText + * } + * \endcode + * + * Any existing UText passed to an open function _must_ have been initialized, + * either by the UTEXT_INITIALIZER, or by having been originally heap-allocated + * by an open function. Passing NULL will cause the open function to + * heap-allocate and fully initialize a new UText. + * + */ + + + +#include "unicode/utypes.h" +#include "unicode/uchar.h" +#if U_SHOW_CPLUSPLUS_API +#include "unicode/localpointer.h" +#include "unicode/rep.h" +#include "unicode/unistr.h" +#include "unicode/chariter.h" +#endif + + +U_CDECL_BEGIN + +struct UText; +typedef struct UText UText; /**< C typedef for struct UText. @stable ICU 3.6 */ + + +/*************************************************************************************** + * + * C Functions for creating UText wrappers around various kinds of text strings. + * + ****************************************************************************************/ + + +/** + * Close function for UText instances. + * Cleans up, releases any resources being held by an open UText. + *

+ * If the UText was originally allocated by one of the utext_open functions, + * the storage associated with the utext will also be freed. + * If the UText storage originated with the application, as it would with + * a local or static instance, the storage will not be deleted. + * + * An open UText can be reset to refer to new string by using one of the utext_open() + * functions without first closing the UText. + * + * @param ut The UText to be closed. + * @return NULL if the UText struct was deleted by the close. If the UText struct + * was originally provided by the caller to the open function, it is + * returned by this function, and may be safely used again in + * a subsequent utext_open. + * + * @stable ICU 3.4 + */ +U_CAPI UText * U_EXPORT2 +utext_close(UText *ut); + +/** + * Open a read-only UText implementation for UTF-8 strings. + * + * \htmlonly + * Any invalid UTF-8 in the input will be handled in this way: + * a sequence of bytes that has the form of a truncated, but otherwise valid, + * UTF-8 sequence will be replaced by a single unicode replacement character, \uFFFD. + * Any other illegal bytes will each be replaced by a \uFFFD. + * \endhtmlonly + * + * @param ut Pointer to a UText struct. If NULL, a new UText will be created. + * If non-NULL, must refer to an initialized UText struct, which will then + * be reset to reference the specified UTF-8 string. + * @param s A UTF-8 string. Must not be NULL. + * @param length The length of the UTF-8 string in bytes, or -1 if the string is + * zero terminated. + * @param status Errors are returned here. + * @return A pointer to the UText. If a pre-allocated UText was provided, it + * will always be used and returned. + * @stable ICU 3.4 + */ +U_CAPI UText * U_EXPORT2 +utext_openUTF8(UText *ut, const char *s, int64_t length, UErrorCode *status); + + +/** + * Open a read-only UText for UChar * string. + * + * @param ut Pointer to a UText struct. If NULL, a new UText will be created. + * If non-NULL, must refer to an initialized UText struct, which will then + * be reset to reference the specified UChar string. + * @param s A UChar (UTF-16) string + * @param length The number of UChars in the input string, or -1 if the string is + * zero terminated. + * @param status Errors are returned here. + * @return A pointer to the UText. If a pre-allocated UText was provided, it + * will always be used and returned. + * @stable ICU 3.4 + */ +U_CAPI UText * U_EXPORT2 +utext_openUChars(UText *ut, const UChar *s, int64_t length, UErrorCode *status); + + +#if U_SHOW_CPLUSPLUS_API +/** + * Open a writable UText for a non-const UnicodeString. + * + * @param ut Pointer to a UText struct. If nullptr, a new UText will be created. + * If non-nullptr, must refer to an initialized UText struct, which will then + * be reset to reference the specified input string. + * @param s A UnicodeString. + * @param status Errors are returned here. + * @return Pointer to the UText. If a UText was supplied as input, this + * will always be used and returned. + * @stable ICU 3.4 + */ +U_CAPI UText * U_EXPORT2 +utext_openUnicodeString(UText *ut, icu::UnicodeString *s, UErrorCode *status); + + +/** + * Open a UText for a const UnicodeString. The resulting UText will not be writable. + * + * @param ut Pointer to a UText struct. If nullptr, a new UText will be created. + * If non-nullptr, must refer to an initialized UText struct, which will then + * be reset to reference the specified input string. + * @param s A const UnicodeString to be wrapped. + * @param status Errors are returned here. + * @return Pointer to the UText. If a UText was supplied as input, this + * will always be used and returned. + * @stable ICU 3.4 + */ +U_CAPI UText * U_EXPORT2 +utext_openConstUnicodeString(UText *ut, const icu::UnicodeString *s, UErrorCode *status); + + +/** + * Open a writable UText implementation for an ICU Replaceable object. + * @param ut Pointer to a UText struct. If nullptr, a new UText will be created. + * If non-nullptr, must refer to an already existing UText, which will then + * be reset to reference the specified replaceable text. + * @param rep A Replaceable text object. + * @param status Errors are returned here. + * @return Pointer to the UText. If a UText was supplied as input, this + * will always be used and returned. + * @see Replaceable + * @stable ICU 3.4 + */ +U_CAPI UText * U_EXPORT2 +utext_openReplaceable(UText *ut, icu::Replaceable *rep, UErrorCode *status); + +/** + * Open a UText implementation over an ICU CharacterIterator. + * @param ut Pointer to a UText struct. If nullptr, a new UText will be created. + * If non-nullptr, must refer to an already existing UText, which will then + * be reset to reference the specified replaceable text. + * @param ci A Character Iterator. + * @param status Errors are returned here. + * @return Pointer to the UText. If a UText was supplied as input, this + * will always be used and returned. + * @see Replaceable + * @stable ICU 3.4 + */ +U_CAPI UText * U_EXPORT2 +utext_openCharacterIterator(UText *ut, icu::CharacterIterator *ci, UErrorCode *status); + +#endif + + +/** + * Clone a UText. This is much like opening a UText where the source text is itself + * another UText. + * + * A deep clone will copy both the UText data structures and the underlying text. + * The original and cloned UText will operate completely independently; modifications + * made to the text in one will not affect the other. Text providers are not + * required to support deep clones. The user of clone() must check the status return + * and be prepared to handle failures. + * + * The standard UText implementations for UTF8, UChar *, UnicodeString and + * Replaceable all support deep cloning. + * + * The UText returned from a deep clone will be writable, assuming that the text + * provider is able to support writing, even if the source UText had been made + * non-writable by means of UText_freeze(). + * + * A shallow clone replicates only the UText data structures; it does not make + * a copy of the underlying text. Shallow clones can be used as an efficient way to + * have multiple iterators active in a single text string that is not being + * modified. + * + * A shallow clone operation will not fail, barring truly exceptional conditions such + * as memory allocation failures. + * + * Shallow UText clones should be avoided if the UText functions that modify the + * text are expected to be used, either on the original or the cloned UText. + * Any such modifications can cause unpredictable behavior. Read Only + * shallow clones provide some protection against errors of this type by + * disabling text modification via the cloned UText. + * + * A shallow clone made with the readOnly parameter == false will preserve the + * utext_isWritable() state of the source object. Note, however, that + * write operations must be avoided while more than one UText exists that refer + * to the same underlying text. + * + * A UText and its clone may be safely concurrently accessed by separate threads. + * This is true for read access only with shallow clones, and for both read and + * write access with deep clones. + * It is the responsibility of the Text Provider to ensure that this thread safety + * constraint is met. + * + * @param dest A UText struct to be filled in with the result of the clone operation, + * or NULL if the clone function should heap-allocate a new UText struct. + * If non-NULL, must refer to an already existing UText, which will then + * be reset to become the clone. + * @param src The UText to be cloned. + * @param deep true to request a deep clone, false for a shallow clone. + * @param readOnly true to request that the cloned UText have read only access to the + * underlying text. + + * @param status Errors are returned here. For deep clones, U_UNSUPPORTED_ERROR + * will be returned if the text provider is unable to clone the + * original text. + * @return The newly created clone, or NULL if the clone operation failed. + * @stable ICU 3.4 + */ +U_CAPI UText * U_EXPORT2 +utext_clone(UText *dest, const UText *src, UBool deep, UBool readOnly, UErrorCode *status); + + +/** + * Compare two UText objects for equality. + * UTexts are equal if they are iterating over the same text, and + * have the same iteration position within the text. + * If either or both of the parameters are NULL, the comparison is false. + * + * @param a The first of the two UTexts to compare. + * @param b The other UText to be compared. + * @return true if the two UTexts are equal. + * @stable ICU 3.6 + */ +U_CAPI UBool U_EXPORT2 +utext_equals(const UText *a, const UText *b); + + +/***************************************************************************** + * + * Functions to work with the text represented by a UText wrapper + * + *****************************************************************************/ + +/** + * Get the length of the text. Depending on the characteristics + * of the underlying text representation, this may be expensive. + * @see utext_isLengthExpensive() + * + * + * @param ut the text to be accessed. + * @return the length of the text, expressed in native units. + * + * @stable ICU 3.4 + */ +U_CAPI int64_t U_EXPORT2 +utext_nativeLength(UText *ut); + +/** + * Return true if calculating the length of the text could be expensive. + * Finding the length of NUL terminated strings is considered to be expensive. + * + * Note that the value of this function may change + * as the result of other operations on a UText. + * Once the length of a string has been discovered, it will no longer + * be expensive to report it. + * + * @param ut the text to be accessed. + * @return true if determining the length of the text could be time consuming. + * @stable ICU 3.4 + */ +U_CAPI UBool U_EXPORT2 +utext_isLengthExpensive(const UText *ut); + +/** + * Returns the code point at the requested index, + * or U_SENTINEL (-1) if it is out of bounds. + * + * If the specified index points to the interior of a multi-unit + * character - one of the trail bytes of a UTF-8 sequence, for example - + * the complete code point will be returned. + * + * The iteration position will be set to the start of the returned code point. + * + * This function is roughly equivalent to the sequence + * utext_setNativeIndex(index); + * utext_current32(); + * (There is a subtle difference if the index is out of bounds by being less than zero - + * utext_setNativeIndex(negative value) sets the index to zero, after which utext_current() + * will return the char at zero. utext_char32At(negative index), on the other hand, will + * return the U_SENTINEL value of -1.) + * + * @param ut the text to be accessed + * @param nativeIndex the native index of the character to be accessed. If the index points + * to other than the first unit of a multi-unit character, it will be adjusted + * to the start of the character. + * @return the code point at the specified index. + * @stable ICU 3.4 + */ +U_CAPI UChar32 U_EXPORT2 +utext_char32At(UText *ut, int64_t nativeIndex); + + +/** + * + * Get the code point at the current iteration position, + * or U_SENTINEL (-1) if the iteration has reached the end of + * the input text. + * + * @param ut the text to be accessed. + * @return the Unicode code point at the current iterator position. + * @stable ICU 3.4 + */ +U_CAPI UChar32 U_EXPORT2 +utext_current32(UText *ut); + + +/** + * Get the code point at the current iteration position of the UText, and + * advance the position to the first index following the character. + * + * If the position is at the end of the text (the index following + * the last character, which is also the length of the text), + * return U_SENTINEL (-1) and do not advance the index. + * + * This is a post-increment operation. + * + * An inline macro version of this function, UTEXT_NEXT32(), + * is available for performance critical use. + * + * @param ut the text to be accessed. + * @return the Unicode code point at the iteration position. + * @see UTEXT_NEXT32 + * @stable ICU 3.4 + */ +U_CAPI UChar32 U_EXPORT2 +utext_next32(UText *ut); + + +/** + * Move the iterator position to the character (code point) whose + * index precedes the current position, and return that character. + * This is a pre-decrement operation. + * + * If the initial position is at the start of the text (index of 0) + * return U_SENTINEL (-1), and leave the position unchanged. + * + * An inline macro version of this function, UTEXT_PREVIOUS32(), + * is available for performance critical use. + * + * @param ut the text to be accessed. + * @return the previous UChar32 code point, or U_SENTINEL (-1) + * if the iteration has reached the start of the text. + * @see UTEXT_PREVIOUS32 + * @stable ICU 3.4 + */ +U_CAPI UChar32 U_EXPORT2 +utext_previous32(UText *ut); + + +/** + * Set the iteration index and return the code point at that index. + * Leave the iteration index at the start of the following code point. + * + * This function is the most efficient and convenient way to + * begin a forward iteration. The results are identical to the those + * from the sequence + * \code + * utext_setIndex(); + * utext_next32(); + * \endcode + * + * @param ut the text to be accessed. + * @param nativeIndex Iteration index, in the native units of the text provider. + * @return Code point which starts at or before index, + * or U_SENTINEL (-1) if it is out of bounds. + * @stable ICU 3.4 + */ +U_CAPI UChar32 U_EXPORT2 +utext_next32From(UText *ut, int64_t nativeIndex); + + + +/** + * Set the iteration index, and return the code point preceding the + * one specified by the initial index. Leave the iteration position + * at the start of the returned code point. + * + * This function is the most efficient and convenient way to + * begin a backwards iteration. + * + * @param ut the text to be accessed. + * @param nativeIndex Iteration index in the native units of the text provider. + * @return Code point preceding the one at the initial index, + * or U_SENTINEL (-1) if it is out of bounds. + * + * @stable ICU 3.4 + */ +U_CAPI UChar32 U_EXPORT2 +utext_previous32From(UText *ut, int64_t nativeIndex); + +/** + * Get the current iterator position, which can range from 0 to + * the length of the text. + * The position is a native index into the input text, in whatever format it + * may have (possibly UTF-8 for example), and may not always be the same as + * the corresponding UChar (UTF-16) index. + * The returned position will always be aligned to a code point boundary. + * + * @param ut the text to be accessed. + * @return the current index position, in the native units of the text provider. + * @stable ICU 3.4 + */ +U_CAPI int64_t U_EXPORT2 +utext_getNativeIndex(const UText *ut); + +/** + * Set the current iteration position to the nearest code point + * boundary at or preceding the specified index. + * The index is in the native units of the original input text. + * If the index is out of range, it will be pinned to be within + * the range of the input text. + *

+ * It will usually be more efficient to begin an iteration + * using the functions utext_next32From() or utext_previous32From() + * rather than setIndex(). + *

+ * Moving the index position to an adjacent character is best done + * with utext_next32(), utext_previous32() or utext_moveIndex32(). + * Attempting to do direct arithmetic on the index position is + * complicated by the fact that the size (in native units) of a + * character depends on the underlying representation of the character + * (UTF-8, UTF-16, UTF-32, arbitrary codepage), and is not + * easily knowable. + * + * @param ut the text to be accessed. + * @param nativeIndex the native unit index of the new iteration position. + * @stable ICU 3.4 + */ +U_CAPI void U_EXPORT2 +utext_setNativeIndex(UText *ut, int64_t nativeIndex); + +/** + * Move the iterator position by delta code points. The number of code points + * is a signed number; a negative delta will move the iterator backwards, + * towards the start of the text. + *

+ * The index is moved by delta code points + * forward or backward, but no further backward than to 0 and + * no further forward than to utext_nativeLength(). + * The resulting index value will be in between 0 and length, inclusive. + * + * @param ut the text to be accessed. + * @param delta the signed number of code points to move the iteration position. + * @return true if the position could be moved the requested number of positions while + * staying within the range [0 - text length]. + * @stable ICU 3.4 + */ +U_CAPI UBool U_EXPORT2 +utext_moveIndex32(UText *ut, int32_t delta); + +/** + * Get the native index of the character preceding the current position. + * If the iteration position is already at the start of the text, zero + * is returned. + * The value returned is the same as that obtained from the following sequence, + * but without the side effect of changing the iteration position. + * + * \code + * UText *ut = whatever; + * ... + * utext_previous(ut) + * utext_getNativeIndex(ut); + * \endcode + * + * This function is most useful during forwards iteration, where it will get the + * native index of the character most recently returned from utext_next(). + * + * @param ut the text to be accessed + * @return the native index of the character preceding the current index position, + * or zero if the current position is at the start of the text. + * @stable ICU 3.6 + */ +U_CAPI int64_t U_EXPORT2 +utext_getPreviousNativeIndex(UText *ut); + + +/** + * + * Extract text from a UText into a UChar buffer. The range of text to be extracted + * is specified in the native indices of the UText provider. These may not necessarily + * be UTF-16 indices. + *

+ * The size (number of 16 bit UChars) of the data to be extracted is returned. The + * full number of UChars is returned, even when the extracted text is truncated + * because the specified buffer size is too small. + *

+ * The extracted string will (if you are a user) / must (if you are a text provider) + * be NUL-terminated if there is sufficient space in the destination buffer. This + * terminating NUL is not included in the returned length. + *

+ * The iteration index is left at the position following the last extracted character. + * + * @param ut the UText from which to extract data. + * @param nativeStart the native index of the first character to extract.\ + * If the specified index is out of range, + * it will be pinned to be within 0 <= index <= textLength + * @param nativeLimit the native string index of the position following the last + * character to extract. If the specified index is out of range, + * it will be pinned to be within 0 <= index <= textLength. + * nativeLimit must be >= nativeStart. + * @param dest the UChar (UTF-16) buffer into which the extracted text is placed + * @param destCapacity The size, in UChars, of the destination buffer. May be zero + * for precomputing the required size. + * @param status receives any error status. + * U_BUFFER_OVERFLOW_ERROR: the extracted text was truncated because the + * buffer was too small. Returns number of UChars for preflighting. + * @return Number of UChars in the data to be extracted. Does not include a trailing NUL. + * + * @stable ICU 3.4 + */ +U_CAPI int32_t U_EXPORT2 +utext_extract(UText *ut, + int64_t nativeStart, int64_t nativeLimit, + UChar *dest, int32_t destCapacity, + UErrorCode *status); + + + +/************************************************************************************ + * + * #define inline versions of selected performance-critical text access functions + * Caution: do not use auto increment++ or decrement-- expressions + * as parameters to these macros. + * + * For most use, where there is no extreme performance constraint, the + * normal, non-inline functions are a better choice. The resulting code + * will be smaller, and, if the need ever arises, easier to debug. + * + * These are implemented as #defines rather than real functions + * because there is no fully portable way to do inline functions in plain C. + * + ************************************************************************************/ + +#ifndef U_HIDE_INTERNAL_API +/** + * inline version of utext_current32(), for performance-critical situations. + * + * Get the code point at the current iteration position of the UText. + * Returns U_SENTINEL (-1) if the position is at the end of the + * text. + * + * @internal ICU 4.4 technology preview + */ +#define UTEXT_CURRENT32(ut) \ + ((ut)->chunkOffset < (ut)->chunkLength && ((ut)->chunkContents)[(ut)->chunkOffset]<0xd800 ? \ + ((ut)->chunkContents)[((ut)->chunkOffset)] : utext_current32(ut)) +#endif /* U_HIDE_INTERNAL_API */ + +/** + * inline version of utext_next32(), for performance-critical situations. + * + * Get the code point at the current iteration position of the UText, and + * advance the position to the first index following the character. + * This is a post-increment operation. + * Returns U_SENTINEL (-1) if the position is at the end of the + * text. + * + * @stable ICU 3.4 + */ +#define UTEXT_NEXT32(ut) \ + ((ut)->chunkOffset < (ut)->chunkLength && ((ut)->chunkContents)[(ut)->chunkOffset]<0xd800 ? \ + ((ut)->chunkContents)[((ut)->chunkOffset)++] : utext_next32(ut)) + +/** + * inline version of utext_previous32(), for performance-critical situations. + * + * Move the iterator position to the character (code point) whose + * index precedes the current position, and return that character. + * This is a pre-decrement operation. + * Returns U_SENTINEL (-1) if the position is at the start of the text. + * + * @stable ICU 3.4 + */ +#define UTEXT_PREVIOUS32(ut) \ + ((ut)->chunkOffset > 0 && \ + (ut)->chunkContents[(ut)->chunkOffset-1] < 0xd800 ? \ + (ut)->chunkContents[--((ut)->chunkOffset)] : utext_previous32(ut)) + +/** + * inline version of utext_getNativeIndex(), for performance-critical situations. + * + * Get the current iterator position, which can range from 0 to + * the length of the text. + * The position is a native index into the input text, in whatever format it + * may have (possibly UTF-8 for example), and may not always be the same as + * the corresponding UChar (UTF-16) index. + * The returned position will always be aligned to a code point boundary. + * + * @stable ICU 3.6 + */ +#define UTEXT_GETNATIVEINDEX(ut) \ + ((ut)->chunkOffset <= (ut)->nativeIndexingLimit? \ + (ut)->chunkNativeStart+(ut)->chunkOffset : \ + (ut)->pFuncs->mapOffsetToNative(ut)) + +/** + * inline version of utext_setNativeIndex(), for performance-critical situations. + * + * Set the current iteration position to the nearest code point + * boundary at or preceding the specified index. + * The index is in the native units of the original input text. + * If the index is out of range, it will be pinned to be within + * the range of the input text. + * + * @stable ICU 3.8 + */ +#define UTEXT_SETNATIVEINDEX(ut, ix) UPRV_BLOCK_MACRO_BEGIN { \ + int64_t __offset = (ix) - (ut)->chunkNativeStart; \ + if (__offset>=0 && __offset<(int64_t)(ut)->nativeIndexingLimit && (ut)->chunkContents[__offset]<0xdc00) { \ + (ut)->chunkOffset=(int32_t)__offset; \ + } else { \ + utext_setNativeIndex((ut), (ix)); \ + } \ +} UPRV_BLOCK_MACRO_END + + + +/************************************************************************************ + * + * Functions related to writing or modifying the text. + * These will work only with modifiable UTexts. Attempting to + * modify a read-only UText will return an error status. + * + ************************************************************************************/ + + +/** + * Return true if the text can be written (modified) with utext_replace() or + * utext_copy(). For the text to be writable, the text provider must + * be of a type that supports writing and the UText must not be frozen. + * + * Attempting to modify text when utext_isWriteable() is false will fail - + * the text will not be modified, and an error will be returned from the function + * that attempted the modification. + * + * @param ut the UText to be tested. + * @return true if the text is modifiable. + * + * @see utext_freeze() + * @see utext_replace() + * @see utext_copy() + * @stable ICU 3.4 + * + */ +U_CAPI UBool U_EXPORT2 +utext_isWritable(const UText *ut); + + +/** + * Test whether there is meta data associated with the text. + * @see Replaceable::hasMetaData() + * + * @param ut The UText to be tested + * @return true if the underlying text includes meta data. + * @stable ICU 3.4 + */ +U_CAPI UBool U_EXPORT2 +utext_hasMetaData(const UText *ut); + + +/** + * Replace a range of the original text with a replacement text. + * + * Leaves the current iteration position at the position following the + * newly inserted replacement text. + * + * This function is only available on UText types that support writing, + * that is, ones where utext_isWritable() returns true. + * + * When using this function, there should be only a single UText opened onto the + * underlying native text string. Behavior after a replace operation + * on a UText is undefined for any other additional UTexts that refer to the + * modified string. + * + * @param ut the UText representing the text to be operated on. + * @param nativeStart the native index of the start of the region to be replaced + * @param nativeLimit the native index of the character following the region to be replaced. + * @param replacementText pointer to the replacement text + * @param replacementLength length of the replacement text, or -1 if the text is NUL terminated. + * @param status receives any error status. Possible errors include + * U_NO_WRITE_PERMISSION + * + * @return The signed number of (native) storage units by which + * the length of the text expanded or contracted. + * + * @stable ICU 3.4 + */ +U_CAPI int32_t U_EXPORT2 +utext_replace(UText *ut, + int64_t nativeStart, int64_t nativeLimit, + const UChar *replacementText, int32_t replacementLength, + UErrorCode *status); + + + +/** + * + * Copy or move a substring from one position to another within the text, + * while retaining any metadata associated with the text. + * This function is used to duplicate or reorder substrings. + * The destination index must not overlap the source range. + * + * The text to be copied or moved is inserted at destIndex; + * it does not replace or overwrite any existing text. + * + * The iteration position is left following the newly inserted text + * at the destination position. + * + * This function is only available on UText types that support writing, + * that is, ones where utext_isWritable() returns true. + * + * When using this function, there should be only a single UText opened onto the + * underlying native text string. Behavior after a copy operation + * on a UText is undefined in any other additional UTexts that refer to the + * modified string. + * + * @param ut The UText representing the text to be operated on. + * @param nativeStart The native index of the start of the region to be copied or moved + * @param nativeLimit The native index of the character position following the region + * to be copied. + * @param destIndex The native destination index to which the source substring is + * copied or moved. + * @param move If true, then the substring is moved, not copied/duplicated. + * @param status receives any error status. Possible errors include U_NO_WRITE_PERMISSION + * + * @stable ICU 3.4 + */ +U_CAPI void U_EXPORT2 +utext_copy(UText *ut, + int64_t nativeStart, int64_t nativeLimit, + int64_t destIndex, + UBool move, + UErrorCode *status); + + +/** + *

+ * Freeze a UText. This prevents any modification to the underlying text itself + * by means of functions operating on this UText. + *

+ *

+ * Once frozen, a UText can not be unfrozen. The intent is to ensure + * that a the text underlying a frozen UText wrapper cannot be modified via that UText. + *

+ *

+ * Caution: freezing a UText will disable changes made via the specific + * frozen UText wrapper only; it will not have any effect on the ability to + * directly modify the text by bypassing the UText. Any such backdoor modifications + * are always an error while UText access is occurring because the underlying + * text can get out of sync with UText's buffering. + *

+ * + * @param ut The UText to be frozen. + * @see utext_isWritable() + * @stable ICU 3.6 + */ +U_CAPI void U_EXPORT2 +utext_freeze(UText *ut); + + +/** + * UText provider properties (bit field indexes). + * + * @see UText + * @stable ICU 3.4 + */ +enum { + /** + * It is potentially time consuming for the provider to determine the length of the text. + * @stable ICU 3.4 + */ + UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE = 1, + /** + * Text chunks remain valid and usable until the text object is modified or + * deleted, not just until the next time the access() function is called + * (which is the default). + * @stable ICU 3.4 + */ + UTEXT_PROVIDER_STABLE_CHUNKS = 2, + /** + * The provider supports modifying the text via the replace() and copy() + * functions. + * @see Replaceable + * @stable ICU 3.4 + */ + UTEXT_PROVIDER_WRITABLE = 3, + /** + * There is meta data associated with the text. + * @see Replaceable::hasMetaData() + * @stable ICU 3.4 + */ + UTEXT_PROVIDER_HAS_META_DATA = 4, + /** + * Text provider owns the text storage. + * Generally occurs as the result of a deep clone of the UText. + * When closing the UText, the associated text must + * also be closed/deleted/freed/ whatever is appropriate. + * @stable ICU 3.6 + */ + UTEXT_PROVIDER_OWNS_TEXT = 5 +}; + +/** + * Function type declaration for UText.clone(). + * + * clone a UText. Much like opening a UText where the source text is itself + * another UText. + * + * A deep clone will copy both the UText data structures and the underlying text. + * The original and cloned UText will operate completely independently; modifications + * made to the text in one will not effect the other. Text providers are not + * required to support deep clones. The user of clone() must check the status return + * and be prepared to handle failures. + * + * A shallow clone replicates only the UText data structures; it does not make + * a copy of the underlying text. Shallow clones can be used as an efficient way to + * have multiple iterators active in a single text string that is not being + * modified. + * + * A shallow clone operation must not fail except for truly exceptional conditions such + * as memory allocation failures. + * + * A UText and its clone may be safely concurrently accessed by separate threads. + * This is true for both shallow and deep clones. + * It is the responsibility of the Text Provider to ensure that this thread safety + * constraint is met. + + * + * @param dest A UText struct to be filled in with the result of the clone operation, + * or NULL if the clone function should heap-allocate a new UText struct. + * @param src The UText to be cloned. + * @param deep true to request a deep clone, false for a shallow clone. + * @param status Errors are returned here. For deep clones, U_UNSUPPORTED_ERROR + * should be returned if the text provider is unable to clone the + * original text. + * @return The newly created clone, or NULL if the clone operation failed. + * + * @stable ICU 3.4 + */ +typedef UText * U_CALLCONV +UTextClone(UText *dest, const UText *src, UBool deep, UErrorCode *status); + + +/** + * Function type declaration for UText.nativeLength(). + * + * @param ut the UText to get the length of. + * @return the length, in the native units of the original text string. + * @see UText + * @stable ICU 3.4 + */ +typedef int64_t U_CALLCONV +UTextNativeLength(UText *ut); + +/** + * Function type declaration for UText.access(). Get the description of the text chunk + * containing the text at a requested native index. The UText's iteration + * position will be left at the requested index. If the index is out + * of bounds, the iteration position will be left at the start or end + * of the string, as appropriate. + * + * Chunks must begin and end on code point boundaries. A single code point + * comprised of multiple storage units must never span a chunk boundary. + * + * + * @param ut the UText being accessed. + * @param nativeIndex Requested index of the text to be accessed. + * @param forward If true, then the returned chunk must contain text + * starting from the index, so that start<=index + * The size (number of 16 bit UChars) in the data to be extracted is returned. The + * full amount is returned, even when the specified buffer size is smaller. + *

+ * The extracted string will (if you are a user) / must (if you are a text provider) + * be NUL-terminated if there is sufficient space in the destination buffer. + * + * @param ut the UText from which to extract data. + * @param nativeStart the native index of the first character to extract. + * @param nativeLimit the native string index of the position following the last + * character to extract. + * @param dest the UChar (UTF-16) buffer into which the extracted text is placed + * @param destCapacity The size, in UChars, of the destination buffer. May be zero + * for precomputing the required size. + * @param status receives any error status. + * If U_BUFFER_OVERFLOW_ERROR: Returns number of UChars for + * preflighting. + * @return Number of UChars in the data. Does not include a trailing NUL. + * + * @stable ICU 3.4 + */ +typedef int32_t U_CALLCONV +UTextExtract(UText *ut, + int64_t nativeStart, int64_t nativeLimit, + UChar *dest, int32_t destCapacity, + UErrorCode *status); + +/** + * Function type declaration for UText.replace(). + * + * Replace a range of the original text with a replacement text. + * + * Leaves the current iteration position at the position following the + * newly inserted replacement text. + * + * This function need only be implemented on UText types that support writing. + * + * When using this function, there should be only a single UText opened onto the + * underlying native text string. The function is responsible for updating the + * text chunk within the UText to reflect the updated iteration position, + * taking into account any changes to the underlying string's structure caused + * by the replace operation. + * + * @param ut the UText representing the text to be operated on. + * @param nativeStart the index of the start of the region to be replaced + * @param nativeLimit the index of the character following the region to be replaced. + * @param replacementText pointer to the replacement text + * @param replacmentLength length of the replacement text in UChars, or -1 if the text is NUL terminated. + * @param status receives any error status. Possible errors include + * U_NO_WRITE_PERMISSION + * + * @return The signed number of (native) storage units by which + * the length of the text expanded or contracted. + * + * @stable ICU 3.4 + */ +typedef int32_t U_CALLCONV +UTextReplace(UText *ut, + int64_t nativeStart, int64_t nativeLimit, + const UChar *replacementText, int32_t replacmentLength, + UErrorCode *status); + +/** + * Function type declaration for UText.copy(). + * + * Copy or move a substring from one position to another within the text, + * while retaining any metadata associated with the text. + * This function is used to duplicate or reorder substrings. + * The destination index must not overlap the source range. + * + * The text to be copied or moved is inserted at destIndex; + * it does not replace or overwrite any existing text. + * + * This function need only be implemented for UText types that support writing. + * + * When using this function, there should be only a single UText opened onto the + * underlying native text string. The function is responsible for updating the + * text chunk within the UText to reflect the updated iteration position, + * taking into account any changes to the underlying string's structure caused + * by the replace operation. + * + * @param ut The UText representing the text to be operated on. + * @param nativeStart The index of the start of the region to be copied or moved + * @param nativeLimit The index of the character following the region to be replaced. + * @param nativeDest The destination index to which the source substring is copied or moved. + * @param move If true, then the substring is moved, not copied/duplicated. + * @param status receives any error status. Possible errors include U_NO_WRITE_PERMISSION + * + * @stable ICU 3.4 + */ +typedef void U_CALLCONV +UTextCopy(UText *ut, + int64_t nativeStart, int64_t nativeLimit, + int64_t nativeDest, + UBool move, + UErrorCode *status); + +/** + * Function type declaration for UText.mapOffsetToNative(). + * Map from the current UChar offset within the current text chunk to + * the corresponding native index in the original source text. + * + * This is required only for text providers that do not use native UTF-16 indexes. + * + * @param ut the UText. + * @return Absolute (native) index corresponding to chunkOffset in the current chunk. + * The returned native index should always be to a code point boundary. + * + * @stable ICU 3.4 + */ +typedef int64_t U_CALLCONV +UTextMapOffsetToNative(const UText *ut); + +/** + * Function type declaration for UText.mapIndexToUTF16(). + * Map from a native index to a UChar offset within a text chunk. + * Behavior is undefined if the native index does not fall within the + * current chunk. + * + * This function is required only for text providers that do not use native UTF-16 indexes. + * + * @param ut The UText containing the text chunk. + * @param nativeIndex Absolute (native) text index, chunk->start<=index<=chunk->limit. + * @return Chunk-relative UTF-16 offset corresponding to the specified native + * index. + * + * @stable ICU 3.4 + */ +typedef int32_t U_CALLCONV +UTextMapNativeIndexToUTF16(const UText *ut, int64_t nativeIndex); + + +/** + * Function type declaration for UText.utextClose(). + * + * A Text Provider close function is only required for provider types that make + * allocations in their open function (or other functions) that must be + * cleaned when the UText is closed. + * + * The allocation of the UText struct itself and any "extra" storage + * associated with the UText is handled by the common UText implementation + * and does not require provider specific cleanup in a close function. + * + * Most UText provider implementations do not need to implement this function. + * + * @param ut A UText object to be closed. + * + * @stable ICU 3.4 + */ +typedef void U_CALLCONV +UTextClose(UText *ut); + + +/** + * (public) Function dispatch table for UText. + * Conceptually very much like a C++ Virtual Function Table. + * This struct defines the organization of the table. + * Each text provider implementation must provide an + * actual table that is initialized with the appropriate functions + * for the type of text being handled. + * @stable ICU 3.6 + */ +struct UTextFuncs { + /** + * (public) Function table size, sizeof(UTextFuncs) + * Intended for use should the table grow to accommodate added + * functions in the future, to allow tests for older format + * function tables that do not contain the extensions. + * + * Fields are placed for optimal alignment on + * 32/64/128-bit-pointer machines, by normally grouping together + * 4 32-bit fields, + * 4 pointers, + * 2 64-bit fields + * in sequence. + * @stable ICU 3.6 + */ + int32_t tableSize; + + /** + * (private) Alignment padding. + * Do not use, reserved for use by the UText framework only. + * @internal + */ + int32_t reserved1, /** @internal */ reserved2, /** @internal */ reserved3; + + + /** + * (public) Function pointer for UTextClone + * + * @see UTextClone + * @stable ICU 3.6 + */ + UTextClone *clone; + + /** + * (public) function pointer for UTextLength + * May be expensive to compute! + * + * @see UTextLength + * @stable ICU 3.6 + */ + UTextNativeLength *nativeLength; + + /** + * (public) Function pointer for UTextAccess. + * + * @see UTextAccess + * @stable ICU 3.6 + */ + UTextAccess *access; + + /** + * (public) Function pointer for UTextExtract. + * + * @see UTextExtract + * @stable ICU 3.6 + */ + UTextExtract *extract; + + /** + * (public) Function pointer for UTextReplace. + * + * @see UTextReplace + * @stable ICU 3.6 + */ + UTextReplace *replace; + + /** + * (public) Function pointer for UTextCopy. + * + * @see UTextCopy + * @stable ICU 3.6 + */ + UTextCopy *copy; + + /** + * (public) Function pointer for UTextMapOffsetToNative. + * + * @see UTextMapOffsetToNative + * @stable ICU 3.6 + */ + UTextMapOffsetToNative *mapOffsetToNative; + + /** + * (public) Function pointer for UTextMapNativeIndexToUTF16. + * + * @see UTextMapNativeIndexToUTF16 + * @stable ICU 3.6 + */ + UTextMapNativeIndexToUTF16 *mapNativeIndexToUTF16; + + /** + * (public) Function pointer for UTextClose. + * + * @see UTextClose + * @stable ICU 3.6 + */ + UTextClose *close; + + /** + * (private) Spare function pointer + * @internal + */ + UTextClose *spare1; + + /** + * (private) Spare function pointer + * @internal + */ + UTextClose *spare2; + + /** + * (private) Spare function pointer + * @internal + */ + UTextClose *spare3; + +}; +/** + * Function dispatch table for UText + * @see UTextFuncs + */ +typedef struct UTextFuncs UTextFuncs; + + /** + * UText struct. Provides the interface between the generic UText access code + * and the UText provider code that works on specific kinds of + * text (UTF-8, noncontiguous UTF-16, whatever.) + * + * Applications that are using predefined types of text providers + * to pass text data to ICU services will have no need to view the + * internals of the UText structs that they open. + * + * @stable ICU 3.6 + */ +struct UText { + /** + * (private) Magic. Used to help detect when UText functions are handed + * invalid or uninitialized UText structs. + * utext_openXYZ() functions take an initialized, + * but not necessarily open, UText struct as an + * optional fill-in parameter. This magic field + * is used to check for that initialization. + * Text provider close functions must NOT clear + * the magic field because that would prevent + * reuse of the UText struct. + * @internal + */ + uint32_t magic; + + + /** + * (private) Flags for managing the allocation and freeing of + * memory associated with this UText. + * @internal + */ + int32_t flags; + + + /** + * Text provider properties. This set of flags is maintained by the + * text provider implementation. + * @stable ICU 3.4 + */ + int32_t providerProperties; + + /** + * (public) sizeOfStruct=sizeof(UText) + * Allows possible backward compatible extension. + * + * @stable ICU 3.4 + */ + int32_t sizeOfStruct; + + /* ------ 16 byte alignment boundary ----------- */ + + + /** + * (protected) Native index of the first character position following + * the current chunk. + * @stable ICU 3.6 + */ + int64_t chunkNativeLimit; + + /** + * (protected) Size in bytes of the extra space (pExtra). + * @stable ICU 3.4 + */ + int32_t extraSize; + + /** + * (protected) The highest chunk offset where native indexing and + * chunk (UTF-16) indexing correspond. For UTF-16 sources, value + * will be equal to chunkLength. + * + * @stable ICU 3.6 + */ + int32_t nativeIndexingLimit; + + /* ---- 16 byte alignment boundary------ */ + + /** + * (protected) Native index of the first character in the text chunk. + * @stable ICU 3.6 + */ + int64_t chunkNativeStart; + + /** + * (protected) Current iteration position within the text chunk (UTF-16 buffer). + * This is the index to the character that will be returned by utext_next32(). + * @stable ICU 3.6 + */ + int32_t chunkOffset; + + /** + * (protected) Length the text chunk (UTF-16 buffer), in UChars. + * @stable ICU 3.6 + */ + int32_t chunkLength; + + /* ---- 16 byte alignment boundary-- */ + + + /** + * (protected) pointer to a chunk of text in UTF-16 format. + * May refer either to original storage of the source of the text, or + * if conversion was required, to a buffer owned by the UText. + * @stable ICU 3.6 + */ + const UChar *chunkContents; + + /** + * (public) Pointer to Dispatch table for accessing functions for this UText. + * @stable ICU 3.6 + */ + const UTextFuncs *pFuncs; + + /** + * (protected) Pointer to additional space requested by the + * text provider during the utext_open operation. + * @stable ICU 3.4 + */ + void *pExtra; + + /** + * (protected) Pointer to string or text-containing object or similar. + * This is the source of the text that this UText is wrapping, in a format + * that is known to the text provider functions. + * @stable ICU 3.4 + */ + const void *context; + + /* --- 16 byte alignment boundary--- */ + + /** + * (protected) Pointer fields available for use by the text provider. + * Not used by UText common code. + * @stable ICU 3.6 + */ + const void *p; + /** + * (protected) Pointer fields available for use by the text provider. + * Not used by UText common code. + * @stable ICU 3.6 + */ + const void *q; + /** + * (protected) Pointer fields available for use by the text provider. + * Not used by UText common code. + * @stable ICU 3.6 + */ + const void *r; + + /** + * Private field reserved for future use by the UText framework + * itself. This is not to be touched by the text providers. + * @internal ICU 3.4 + */ + void *privP; + + + /* --- 16 byte alignment boundary--- */ + + + /** + * (protected) Integer field reserved for use by the text provider. + * Not used by the UText framework, or by the client (user) of the UText. + * @stable ICU 3.4 + */ + int64_t a; + + /** + * (protected) Integer field reserved for use by the text provider. + * Not used by the UText framework, or by the client (user) of the UText. + * @stable ICU 3.4 + */ + int32_t b; + + /** + * (protected) Integer field reserved for use by the text provider. + * Not used by the UText framework, or by the client (user) of the UText. + * @stable ICU 3.4 + */ + int32_t c; + + /* ---- 16 byte alignment boundary---- */ + + + /** + * Private field reserved for future use by the UText framework + * itself. This is not to be touched by the text providers. + * @internal ICU 3.4 + */ + int64_t privA; + /** + * Private field reserved for future use by the UText framework + * itself. This is not to be touched by the text providers. + * @internal ICU 3.4 + */ + int32_t privB; + /** + * Private field reserved for future use by the UText framework + * itself. This is not to be touched by the text providers. + * @internal ICU 3.4 + */ + int32_t privC; +}; + + +/** + * Common function for use by Text Provider implementations to allocate and/or initialize + * a new UText struct. To be called in the implementation of utext_open() functions. + * If the supplied UText parameter is null, a new UText struct will be allocated on the heap. + * If the supplied UText is already open, the provider's close function will be called + * so that the struct can be reused by the open that is in progress. + * + * @param ut pointer to a UText struct to be re-used, or null if a new UText + * should be allocated. + * @param extraSpace The amount of additional space to be allocated as part + * of this UText, for use by types of providers that require + * additional storage. + * @param status Errors are returned here. + * @return pointer to the UText, allocated if necessary, with extra space set up if requested. + * @stable ICU 3.4 + */ +U_CAPI UText * U_EXPORT2 +utext_setup(UText *ut, int32_t extraSpace, UErrorCode *status); + +// do not use #ifndef U_HIDE_INTERNAL_API around the following! +/** + * @internal + * Value used to help identify correctly initialized UText structs. + * Note: must be publicly visible so that UTEXT_INITIALIZER can access it. + */ +enum { + UTEXT_MAGIC = 0x345ad82c +}; + +/** + * initializer to be used with local (stack) instances of a UText + * struct. UText structs must be initialized before passing + * them to one of the utext_open functions. + * + * @stable ICU 3.6 + */ +#define UTEXT_INITIALIZER { \ + UTEXT_MAGIC, /* magic */ \ + 0, /* flags */ \ + 0, /* providerProps */ \ + sizeof(UText), /* sizeOfStruct */ \ + 0, /* chunkNativeLimit */ \ + 0, /* extraSize */ \ + 0, /* nativeIndexingLimit */ \ + 0, /* chunkNativeStart */ \ + 0, /* chunkOffset */ \ + 0, /* chunkLength */ \ + NULL, /* chunkContents */ \ + NULL, /* pFuncs */ \ + NULL, /* pExtra */ \ + NULL, /* context */ \ + NULL, NULL, NULL, /* p, q, r */ \ + NULL, /* privP */ \ + 0, 0, 0, /* a, b, c */ \ + 0, 0, 0 /* privA,B,C, */ \ + } + + +U_CDECL_END + + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUTextPointer + * "Smart pointer" class, closes a UText via utext_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUTextPointer, UText, utext_close); + +U_NAMESPACE_END + +#endif + + +#endif diff --git a/deps/icu-small/source/common/unicode/utf.h b/deps/icu-small/source/common/unicode/utf.h index c9d5f5785c5a46..4c896d61bc7f20 100644 --- a/deps/icu-small/source/common/unicode/utf.h +++ b/deps/icu-small/source/common/unicode/utf.h @@ -1,225 +1,225 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1999-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: utf.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999sep09 -* created by: Markus W. Scherer -*/ - -/** - * \file - * \brief C API: Code point macros - * - * This file defines macros for checking whether a code point is - * a surrogate or a non-character etc. - * - * If U_NO_DEFAULT_INCLUDE_UTF_HEADERS is 0 then utf.h is included by utypes.h - * and itself includes utf8.h and utf16.h after some - * common definitions. - * If U_NO_DEFAULT_INCLUDE_UTF_HEADERS is 1 then each of these headers must be - * included explicitly if their definitions are used. - * - * utf8.h and utf16.h define macros for efficiently getting code points - * in and out of UTF-8/16 strings. - * utf16.h macros have "U16_" prefixes. - * utf8.h defines similar macros with "U8_" prefixes for UTF-8 string handling. - * - * ICU mostly processes 16-bit Unicode strings. - * Most of the time, such strings are well-formed UTF-16. - * Single, unpaired surrogates must be handled as well, and are treated in ICU - * like regular code points where possible. - * (Pairs of surrogate code points are indistinguishable from supplementary - * code points encoded as pairs of supplementary code units.) - * - * In fact, almost all Unicode code points in normal text (>99%) - * are on the BMP (<=U+ffff) and even <=U+d7ff. - * ICU functions handle supplementary code points (U+10000..U+10ffff) - * but are optimized for the much more frequently occurring BMP code points. - * - * umachine.h defines UChar to be an unsigned 16-bit integer. - * Since ICU 59, ICU uses char16_t in C++, UChar only in C, - * and defines UChar=char16_t by default. See the UChar API docs for details. - * - * UChar32 is defined to be a signed 32-bit integer (int32_t), large enough for a 21-bit - * Unicode code point (Unicode scalar value, 0..0x10ffff) and U_SENTINEL (-1). - * Before ICU 2.4, the definition of UChar32 was similarly platform-dependent as - * the definition of UChar. For details see the documentation for UChar32 itself. - * - * utf.h defines a small number of C macros for single Unicode code points. - * These are simple checks for surrogates and non-characters. - * For actual Unicode character properties see uchar.h. - * - * By default, string operations must be done with error checking in case - * a string is not well-formed UTF-16 or UTF-8. - * - * The U16_ macros detect if a surrogate code unit is unpaired - * (lead unit without trail unit or vice versa) and just return the unit itself - * as the code point. - * - * The U8_ macros detect illegal byte sequences and return a negative value. - * Starting with ICU 60, the observable length of a single illegal byte sequence - * skipped by one of these macros follows the Unicode 6+ recommendation - * which is consistent with the W3C Encoding Standard. - * - * There are ..._OR_FFFD versions of both U16_ and U8_ macros - * that return U+FFFD for illegal code unit sequences. - * - * The regular "safe" macros require that the initial, passed-in string index - * is within bounds. They only check the index when they read more than one - * code unit. This is usually done with code similar to the following loop: - *

while(i
- *
- * When it is safe to assume that text is well-formed UTF-16
- * (does not contain single, unpaired surrogates), then one can use
- * U16_..._UNSAFE macros.
- * These do not check for proper code unit sequences or truncated text and may
- * yield wrong results or even cause a crash if they are used with "malformed"
- * text.
- * In practice, U16_..._UNSAFE macros will produce slightly less code but
- * should not be faster because the processing is only different when a
- * surrogate code unit is detected, which will be rare.
- *
- * Similarly for UTF-8, there are "safe" macros without a suffix,
- * and U8_..._UNSAFE versions.
- * The performance differences are much larger here because UTF-8 provides so
- * many opportunities for malformed sequences.
- * The unsafe UTF-8 macros are entirely implemented inside the macro definitions
- * and are fast, while the safe UTF-8 macros call functions for some complicated cases.
- *
- * Unlike with UTF-16, malformed sequences cannot be expressed with distinct
- * code point values (0..U+10ffff). They are indicated with negative values instead.
- *
- * For more information see the ICU User Guide Strings chapter
- * (https://unicode-org.github.io/icu/userguide/strings).
- *
- * Usage:
- * ICU coding guidelines for if() statements should be followed when using these macros.
- * Compound statements (curly braces {}) must be used  for if-else-while... 
- * bodies and all macro statements should be terminated with semicolon.
- *
- * @stable ICU 2.4
- */
-
-#ifndef __UTF_H__
-#define __UTF_H__
-
-#include "unicode/umachine.h"
-/* include the utfXX.h after the following definitions */
-
-/* single-code point definitions -------------------------------------------- */
-
-/**
- * Is this code point a Unicode noncharacter?
- * @param c 32-bit code point
- * @return true or false
- * @stable ICU 2.4
- */
-#define U_IS_UNICODE_NONCHAR(c) \
-    ((c)>=0xfdd0 && \
-     ((c)<=0xfdef || ((c)&0xfffe)==0xfffe) && (c)<=0x10ffff)
-
-/**
- * Is c a Unicode code point value (0..U+10ffff)
- * that can be assigned a character?
- *
- * Code points that are not characters include:
- * - single surrogate code points (U+d800..U+dfff, 2048 code points)
- * - the last two code points on each plane (U+__fffe and U+__ffff, 34 code points)
- * - U+fdd0..U+fdef (new with Unicode 3.1, 32 code points)
- * - the highest Unicode code point value is U+10ffff
- *
- * This means that all code points below U+d800 are character code points,
- * and that boundary is tested first for performance.
- *
- * @param c 32-bit code point
- * @return true or false
- * @stable ICU 2.4
- */
-#define U_IS_UNICODE_CHAR(c) \
-    ((uint32_t)(c)<0xd800 || \
-        (0xdfff<(c) && (c)<=0x10ffff && !U_IS_UNICODE_NONCHAR(c)))
-
-/**
- * Is this code point a BMP code point (U+0000..U+ffff)?
- * @param c 32-bit code point
- * @return true or false
- * @stable ICU 2.8
- */
-#define U_IS_BMP(c) ((uint32_t)(c)<=0xffff)
-
-/**
- * Is this code point a supplementary code point (U+10000..U+10ffff)?
- * @param c 32-bit code point
- * @return true or false
- * @stable ICU 2.8
- */
-#define U_IS_SUPPLEMENTARY(c) ((uint32_t)((c)-0x10000)<=0xfffff)
- 
-/**
- * Is this code point a lead surrogate (U+d800..U+dbff)?
- * @param c 32-bit code point
- * @return true or false
- * @stable ICU 2.4
- */
-#define U_IS_LEAD(c) (((c)&0xfffffc00)==0xd800)
-
-/**
- * Is this code point a trail surrogate (U+dc00..U+dfff)?
- * @param c 32-bit code point
- * @return true or false
- * @stable ICU 2.4
- */
-#define U_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00)
-
-/**
- * Is this code point a surrogate (U+d800..U+dfff)?
- * @param c 32-bit code point
- * @return true or false
- * @stable ICU 2.4
- */
-#define U_IS_SURROGATE(c) (((c)&0xfffff800)==0xd800)
-
-/**
- * Assuming c is a surrogate code point (U_IS_SURROGATE(c)),
- * is it a lead surrogate?
- * @param c 32-bit code point
- * @return true or false
- * @stable ICU 2.4
- */
-#define U_IS_SURROGATE_LEAD(c) (((c)&0x400)==0)
-
-/**
- * Assuming c is a surrogate code point (U_IS_SURROGATE(c)),
- * is it a trail surrogate?
- * @param c 32-bit code point
- * @return true or false
- * @stable ICU 4.2
- */
-#define U_IS_SURROGATE_TRAIL(c) (((c)&0x400)!=0)
-
-/* include the utfXX.h ------------------------------------------------------ */
-
-#if !U_NO_DEFAULT_INCLUDE_UTF_HEADERS
-
-#include "unicode/utf8.h"
-#include "unicode/utf16.h"
-
-/* utf_old.h contains deprecated, pre-ICU 2.4 definitions */
-#include "unicode/utf_old.h"
-
-#endif  /* !U_NO_DEFAULT_INCLUDE_UTF_HEADERS */
-
-#endif  /* __UTF_H__ */
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/*
+*******************************************************************************
+*
+*   Copyright (C) 1999-2011, International Business Machines
+*   Corporation and others.  All Rights Reserved.
+*
+*******************************************************************************
+*   file name:  utf.h
+*   encoding:   UTF-8
+*   tab size:   8 (not used)
+*   indentation:4
+*
+*   created on: 1999sep09
+*   created by: Markus W. Scherer
+*/
+
+/**
+ * \file
+ * \brief C API: Code point macros
+ *
+ * This file defines macros for checking whether a code point is
+ * a surrogate or a non-character etc.
+ *
+ * If U_NO_DEFAULT_INCLUDE_UTF_HEADERS is 0 then utf.h is included by utypes.h
+ * and itself includes utf8.h and utf16.h after some
+ * common definitions.
+ * If U_NO_DEFAULT_INCLUDE_UTF_HEADERS is 1 then each of these headers must be
+ * included explicitly if their definitions are used.
+ *
+ * utf8.h and utf16.h define macros for efficiently getting code points
+ * in and out of UTF-8/16 strings.
+ * utf16.h macros have "U16_" prefixes.
+ * utf8.h defines similar macros with "U8_" prefixes for UTF-8 string handling.
+ *
+ * ICU mostly processes 16-bit Unicode strings.
+ * Most of the time, such strings are well-formed UTF-16.
+ * Single, unpaired surrogates must be handled as well, and are treated in ICU
+ * like regular code points where possible.
+ * (Pairs of surrogate code points are indistinguishable from supplementary
+ * code points encoded as pairs of supplementary code units.)
+ *
+ * In fact, almost all Unicode code points in normal text (>99%)
+ * are on the BMP (<=U+ffff) and even <=U+d7ff.
+ * ICU functions handle supplementary code points (U+10000..U+10ffff)
+ * but are optimized for the much more frequently occurring BMP code points.
+ *
+ * umachine.h defines UChar to be an unsigned 16-bit integer.
+ * Since ICU 59, ICU uses char16_t in C++, UChar only in C,
+ * and defines UChar=char16_t by default. See the UChar API docs for details.
+ *
+ * UChar32 is defined to be a signed 32-bit integer (int32_t), large enough for a 21-bit
+ * Unicode code point (Unicode scalar value, 0..0x10ffff) and U_SENTINEL (-1).
+ * Before ICU 2.4, the definition of UChar32 was similarly platform-dependent as
+ * the definition of UChar. For details see the documentation for UChar32 itself.
+ *
+ * utf.h defines a small number of C macros for single Unicode code points.
+ * These are simple checks for surrogates and non-characters.
+ * For actual Unicode character properties see uchar.h.
+ *
+ * By default, string operations must be done with error checking in case
+ * a string is not well-formed UTF-16 or UTF-8.
+ *
+ * The U16_ macros detect if a surrogate code unit is unpaired
+ * (lead unit without trail unit or vice versa) and just return the unit itself
+ * as the code point.
+ *
+ * The U8_ macros detect illegal byte sequences and return a negative value.
+ * Starting with ICU 60, the observable length of a single illegal byte sequence
+ * skipped by one of these macros follows the Unicode 6+ recommendation
+ * which is consistent with the W3C Encoding Standard.
+ *
+ * There are ..._OR_FFFD versions of both U16_ and U8_ macros
+ * that return U+FFFD for illegal code unit sequences.
+ *
+ * The regular "safe" macros require that the initial, passed-in string index
+ * is within bounds. They only check the index when they read more than one
+ * code unit. This is usually done with code similar to the following loop:
+ * 
while(i
+ *
+ * When it is safe to assume that text is well-formed UTF-16
+ * (does not contain single, unpaired surrogates), then one can use
+ * U16_..._UNSAFE macros.
+ * These do not check for proper code unit sequences or truncated text and may
+ * yield wrong results or even cause a crash if they are used with "malformed"
+ * text.
+ * In practice, U16_..._UNSAFE macros will produce slightly less code but
+ * should not be faster because the processing is only different when a
+ * surrogate code unit is detected, which will be rare.
+ *
+ * Similarly for UTF-8, there are "safe" macros without a suffix,
+ * and U8_..._UNSAFE versions.
+ * The performance differences are much larger here because UTF-8 provides so
+ * many opportunities for malformed sequences.
+ * The unsafe UTF-8 macros are entirely implemented inside the macro definitions
+ * and are fast, while the safe UTF-8 macros call functions for some complicated cases.
+ *
+ * Unlike with UTF-16, malformed sequences cannot be expressed with distinct
+ * code point values (0..U+10ffff). They are indicated with negative values instead.
+ *
+ * For more information see the ICU User Guide Strings chapter
+ * (https://unicode-org.github.io/icu/userguide/strings).
+ *
+ * Usage:
+ * ICU coding guidelines for if() statements should be followed when using these macros.
+ * Compound statements (curly braces {}) must be used  for if-else-while... 
+ * bodies and all macro statements should be terminated with semicolon.
+ *
+ * @stable ICU 2.4
+ */
+
+#ifndef __UTF_H__
+#define __UTF_H__
+
+#include "unicode/umachine.h"
+/* include the utfXX.h after the following definitions */
+
+/* single-code point definitions -------------------------------------------- */
+
+/**
+ * Is this code point a Unicode noncharacter?
+ * @param c 32-bit code point
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U_IS_UNICODE_NONCHAR(c) \
+    ((c)>=0xfdd0 && \
+     ((c)<=0xfdef || ((c)&0xfffe)==0xfffe) && (c)<=0x10ffff)
+
+/**
+ * Is c a Unicode code point value (0..U+10ffff)
+ * that can be assigned a character?
+ *
+ * Code points that are not characters include:
+ * - single surrogate code points (U+d800..U+dfff, 2048 code points)
+ * - the last two code points on each plane (U+__fffe and U+__ffff, 34 code points)
+ * - U+fdd0..U+fdef (new with Unicode 3.1, 32 code points)
+ * - the highest Unicode code point value is U+10ffff
+ *
+ * This means that all code points below U+d800 are character code points,
+ * and that boundary is tested first for performance.
+ *
+ * @param c 32-bit code point
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U_IS_UNICODE_CHAR(c) \
+    ((uint32_t)(c)<0xd800 || \
+        (0xdfff<(c) && (c)<=0x10ffff && !U_IS_UNICODE_NONCHAR(c)))
+
+/**
+ * Is this code point a BMP code point (U+0000..U+ffff)?
+ * @param c 32-bit code point
+ * @return true or false
+ * @stable ICU 2.8
+ */
+#define U_IS_BMP(c) ((uint32_t)(c)<=0xffff)
+
+/**
+ * Is this code point a supplementary code point (U+10000..U+10ffff)?
+ * @param c 32-bit code point
+ * @return true or false
+ * @stable ICU 2.8
+ */
+#define U_IS_SUPPLEMENTARY(c) ((uint32_t)((c)-0x10000)<=0xfffff)
+ 
+/**
+ * Is this code point a lead surrogate (U+d800..U+dbff)?
+ * @param c 32-bit code point
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U_IS_LEAD(c) (((c)&0xfffffc00)==0xd800)
+
+/**
+ * Is this code point a trail surrogate (U+dc00..U+dfff)?
+ * @param c 32-bit code point
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00)
+
+/**
+ * Is this code point a surrogate (U+d800..U+dfff)?
+ * @param c 32-bit code point
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U_IS_SURROGATE(c) (((c)&0xfffff800)==0xd800)
+
+/**
+ * Assuming c is a surrogate code point (U_IS_SURROGATE(c)),
+ * is it a lead surrogate?
+ * @param c 32-bit code point
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U_IS_SURROGATE_LEAD(c) (((c)&0x400)==0)
+
+/**
+ * Assuming c is a surrogate code point (U_IS_SURROGATE(c)),
+ * is it a trail surrogate?
+ * @param c 32-bit code point
+ * @return true or false
+ * @stable ICU 4.2
+ */
+#define U_IS_SURROGATE_TRAIL(c) (((c)&0x400)!=0)
+
+/* include the utfXX.h ------------------------------------------------------ */
+
+#if !U_NO_DEFAULT_INCLUDE_UTF_HEADERS
+
+#include "unicode/utf8.h"
+#include "unicode/utf16.h"
+
+/* utf_old.h contains deprecated, pre-ICU 2.4 definitions */
+#include "unicode/utf_old.h"
+
+#endif  /* !U_NO_DEFAULT_INCLUDE_UTF_HEADERS */
+
+#endif  /* __UTF_H__ */
diff --git a/deps/icu-small/source/common/unicode/utf16.h b/deps/icu-small/source/common/unicode/utf16.h
index 3902c60e95e70d..e5dbbee34fd3c7 100644
--- a/deps/icu-small/source/common/unicode/utf16.h
+++ b/deps/icu-small/source/common/unicode/utf16.h
@@ -1,734 +1,734 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-*
-*   Copyright (C) 1999-2012, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-*
-*******************************************************************************
-*   file name:  utf16.h
-*   encoding:   UTF-8
-*   tab size:   8 (not used)
-*   indentation:4
-*
-*   created on: 1999sep09
-*   created by: Markus W. Scherer
-*/
-
-/**
- * \file
- * \brief C API: 16-bit Unicode handling macros
- * 
- * This file defines macros to deal with 16-bit Unicode (UTF-16) code units and strings.
- *
- * For more information see utf.h and the ICU User Guide Strings chapter
- * (https://unicode-org.github.io/icu/userguide/strings).
- *
- * Usage:
- * ICU coding guidelines for if() statements should be followed when using these macros.
- * Compound statements (curly braces {}) must be used  for if-else-while... 
- * bodies and all macro statements should be terminated with semicolon.
- */
-
-#ifndef __UTF16_H__
-#define __UTF16_H__
-
-#include 
-#include "unicode/umachine.h"
-#ifndef __UTF_H__
-#   include "unicode/utf.h"
-#endif
-
-/* single-code point definitions -------------------------------------------- */
-
-/**
- * Does this code unit alone encode a code point (BMP, not a surrogate)?
- * @param c 16-bit code unit
- * @return true or false
- * @stable ICU 2.4
- */
-#define U16_IS_SINGLE(c) !U_IS_SURROGATE(c)
-
-/**
- * Is this code unit a lead surrogate (U+d800..U+dbff)?
- * @param c 16-bit code unit
- * @return true or false
- * @stable ICU 2.4
- */
-#define U16_IS_LEAD(c) (((c)&0xfffffc00)==0xd800)
-
-/**
- * Is this code unit a trail surrogate (U+dc00..U+dfff)?
- * @param c 16-bit code unit
- * @return true or false
- * @stable ICU 2.4
- */
-#define U16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00)
-
-/**
- * Is this code unit a surrogate (U+d800..U+dfff)?
- * @param c 16-bit code unit
- * @return true or false
- * @stable ICU 2.4
- */
-#define U16_IS_SURROGATE(c) U_IS_SURROGATE(c)
-
-/**
- * Assuming c is a surrogate code point (U16_IS_SURROGATE(c)),
- * is it a lead surrogate?
- * @param c 16-bit code unit
- * @return true or false
- * @stable ICU 2.4
- */
-#define U16_IS_SURROGATE_LEAD(c) (((c)&0x400)==0)
-
-/**
- * Assuming c is a surrogate code point (U16_IS_SURROGATE(c)),
- * is it a trail surrogate?
- * @param c 16-bit code unit
- * @return true or false
- * @stable ICU 4.2
- */
-#define U16_IS_SURROGATE_TRAIL(c) (((c)&0x400)!=0)
-
-/**
- * Helper constant for U16_GET_SUPPLEMENTARY.
- * @internal
- */
-#define U16_SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000)
-
-/**
- * Get a supplementary code point value (U+10000..U+10ffff)
- * from its lead and trail surrogates.
- * The result is undefined if the input values are not
- * lead and trail surrogates.
- *
- * @param lead lead surrogate (U+d800..U+dbff)
- * @param trail trail surrogate (U+dc00..U+dfff)
- * @return supplementary code point (U+10000..U+10ffff)
- * @stable ICU 2.4
- */
-#define U16_GET_SUPPLEMENTARY(lead, trail) \
-    (((UChar32)(lead)<<10UL)+(UChar32)(trail)-U16_SURROGATE_OFFSET)
-
-
-/**
- * Get the lead surrogate (0xd800..0xdbff) for a
- * supplementary code point (0x10000..0x10ffff).
- * @param supplementary 32-bit code point (U+10000..U+10ffff)
- * @return lead surrogate (U+d800..U+dbff) for supplementary
- * @stable ICU 2.4
- */
-#define U16_LEAD(supplementary) (UChar)(((supplementary)>>10)+0xd7c0)
-
-/**
- * Get the trail surrogate (0xdc00..0xdfff) for a
- * supplementary code point (0x10000..0x10ffff).
- * @param supplementary 32-bit code point (U+10000..U+10ffff)
- * @return trail surrogate (U+dc00..U+dfff) for supplementary
- * @stable ICU 2.4
- */
-#define U16_TRAIL(supplementary) (UChar)(((supplementary)&0x3ff)|0xdc00)
-
-/**
- * How many 16-bit code units are used to encode this Unicode code point? (1 or 2)
- * The result is not defined if c is not a Unicode code point (U+0000..U+10ffff).
- * @param c 32-bit code point
- * @return 1 or 2
- * @stable ICU 2.4
- */
-#define U16_LENGTH(c) ((uint32_t)(c)<=0xffff ? 1 : 2)
-
-/**
- * The maximum number of 16-bit code units per Unicode code point (U+0000..U+10ffff).
- * @return 2
- * @stable ICU 2.4
- */
-#define U16_MAX_LENGTH 2
-
-/**
- * Get a code point from a string at a random-access offset,
- * without changing the offset.
- * "Unsafe" macro, assumes well-formed UTF-16.
- *
- * The offset may point to either the lead or trail surrogate unit
- * for a supplementary code point, in which case the macro will read
- * the adjacent matching surrogate as well.
- * The result is undefined if the offset points to a single, unpaired surrogate.
- * Iteration through a string is more efficient with U16_NEXT_UNSAFE or U16_NEXT.
- *
- * @param s const UChar * string
- * @param i string offset
- * @param c output UChar32 variable
- * @see U16_GET
- * @stable ICU 2.4
- */
-#define U16_GET_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \
-    (c)=(s)[i]; \
-    if(U16_IS_SURROGATE(c)) { \
-        if(U16_IS_SURROGATE_LEAD(c)) { \
-            (c)=U16_GET_SUPPLEMENTARY((c), (s)[(i)+1]); \
-        } else { \
-            (c)=U16_GET_SUPPLEMENTARY((s)[(i)-1], (c)); \
-        } \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Get a code point from a string at a random-access offset,
- * without changing the offset.
- * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
- *
- * The offset may point to either the lead or trail surrogate unit
- * for a supplementary code point, in which case the macro will read
- * the adjacent matching surrogate as well.
- *
- * The length can be negative for a NUL-terminated string.
- *
- * If the offset points to a single, unpaired surrogate, then
- * c is set to that unpaired surrogate.
- * Iteration through a string is more efficient with U16_NEXT_UNSAFE or U16_NEXT.
- *
- * @param s const UChar * string
- * @param start starting string offset (usually 0)
- * @param i string offset, must be start<=i(start) && U16_IS_LEAD(__c2=(s)[(i)-1])) { \
-                (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \
-            } \
-        } \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Get a code point from a string at a random-access offset,
- * without changing the offset.
- * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
- *
- * The offset may point to either the lead or trail surrogate unit
- * for a supplementary code point, in which case the macro will read
- * the adjacent matching surrogate as well.
- *
- * The length can be negative for a NUL-terminated string.
- *
- * If the offset points to a single, unpaired surrogate, then
- * c is set to U+FFFD.
- * Iteration through a string is more efficient with U16_NEXT_UNSAFE or U16_NEXT_OR_FFFD.
- *
- * @param s const UChar * string
- * @param start starting string offset (usually 0)
- * @param i string offset, must be start<=i(start) && U16_IS_LEAD(__c2=(s)[(i)-1])) { \
-                (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \
-            } else { \
-                (c)=0xfffd; \
-            } \
-        } \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/* definitions with forward iteration --------------------------------------- */
-
-/**
- * Get a code point from a string at a code point boundary offset,
- * and advance the offset to the next code point boundary.
- * (Post-incrementing forward iteration.)
- * "Unsafe" macro, assumes well-formed UTF-16.
- *
- * The offset may point to the lead surrogate unit
- * for a supplementary code point, in which case the macro will read
- * the following trail surrogate as well.
- * If the offset points to a trail surrogate, then that itself
- * will be returned as the code point.
- * The result is undefined if the offset points to a single, unpaired lead surrogate.
- *
- * @param s const UChar * string
- * @param i string offset
- * @param c output UChar32 variable
- * @see U16_NEXT
- * @stable ICU 2.4
- */
-#define U16_NEXT_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \
-    (c)=(s)[(i)++]; \
-    if(U16_IS_LEAD(c)) { \
-        (c)=U16_GET_SUPPLEMENTARY((c), (s)[(i)++]); \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Get a code point from a string at a code point boundary offset,
- * and advance the offset to the next code point boundary.
- * (Post-incrementing forward iteration.)
- * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
- *
- * The length can be negative for a NUL-terminated string.
- *
- * The offset may point to the lead surrogate unit
- * for a supplementary code point, in which case the macro will read
- * the following trail surrogate as well.
- * If the offset points to a trail surrogate or
- * to a single, unpaired lead surrogate, then c is set to that unpaired surrogate.
- *
- * @param s const UChar * string
- * @param i string offset, must be i>10)+0xd7c0); \
-        (s)[(i)++]=(uint16_t)(((c)&0x3ff)|0xdc00); \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Append a code point to a string, overwriting 1 or 2 code units.
- * The offset points to the current end of the string contents
- * and is advanced (post-increment).
- * "Safe" macro, checks for a valid code point.
- * If a surrogate pair is written, checks for sufficient space in the string.
- * If the code point is not valid or a trail surrogate does not fit,
- * then isError is set to true.
- *
- * @param s const UChar * string buffer
- * @param i string offset, must be i>10)+0xd7c0); \
-        (s)[(i)++]=(uint16_t)(((c)&0x3ff)|0xdc00); \
-    } else /* c>0x10ffff or not enough space */ { \
-        (isError)=true; \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Advance the string offset from one code point boundary to the next.
- * (Post-incrementing iteration.)
- * "Unsafe" macro, assumes well-formed UTF-16.
- *
- * @param s const UChar * string
- * @param i string offset
- * @see U16_FWD_1
- * @stable ICU 2.4
- */
-#define U16_FWD_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
-    if(U16_IS_LEAD((s)[(i)++])) { \
-        ++(i); \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Advance the string offset from one code point boundary to the next.
- * (Post-incrementing iteration.)
- * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
- *
- * The length can be negative for a NUL-terminated string.
- *
- * @param s const UChar * string
- * @param i string offset, must be i0) { \
-        U16_FWD_1_UNSAFE(s, i); \
-        --__N; \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Advance the string offset from one code point boundary to the n-th next one,
- * i.e., move forward by n code points.
- * (Post-incrementing iteration.)
- * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
- *
- * The length can be negative for a NUL-terminated string.
- *
- * @param s const UChar * string
- * @param i int32_t string offset, must be i0 && ((i)<(length) || ((length)<0 && (s)[i]!=0))) { \
-        U16_FWD_1(s, i, length); \
-        --__N; \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Adjust a random-access offset to a code point boundary
- * at the start of a code point.
- * If the offset points to the trail surrogate of a surrogate pair,
- * then the offset is decremented.
- * Otherwise, it is not modified.
- * "Unsafe" macro, assumes well-formed UTF-16.
- *
- * @param s const UChar * string
- * @param i string offset
- * @see U16_SET_CP_START
- * @stable ICU 2.4
- */
-#define U16_SET_CP_START_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
-    if(U16_IS_TRAIL((s)[i])) { \
-        --(i); \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Adjust a random-access offset to a code point boundary
- * at the start of a code point.
- * If the offset points to the trail surrogate of a surrogate pair,
- * then the offset is decremented.
- * Otherwise, it is not modified.
- * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
- *
- * @param s const UChar * string
- * @param start starting string offset (usually 0)
- * @param i string offset, must be start<=i
- * @see U16_SET_CP_START_UNSAFE
- * @stable ICU 2.4
- */
-#define U16_SET_CP_START(s, start, i) UPRV_BLOCK_MACRO_BEGIN { \
-    if(U16_IS_TRAIL((s)[i]) && (i)>(start) && U16_IS_LEAD((s)[(i)-1])) { \
-        --(i); \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/* definitions with backward iteration -------------------------------------- */
-
-/**
- * Move the string offset from one code point boundary to the previous one
- * and get the code point between them.
- * (Pre-decrementing backward iteration.)
- * "Unsafe" macro, assumes well-formed UTF-16.
- *
- * The input offset may be the same as the string length.
- * If the offset is behind a trail surrogate unit
- * for a supplementary code point, then the macro will read
- * the preceding lead surrogate as well.
- * If the offset is behind a lead surrogate, then that itself
- * will be returned as the code point.
- * The result is undefined if the offset is behind a single, unpaired trail surrogate.
- *
- * @param s const UChar * string
- * @param i string offset
- * @param c output UChar32 variable
- * @see U16_PREV
- * @stable ICU 2.4
- */
-#define U16_PREV_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \
-    (c)=(s)[--(i)]; \
-    if(U16_IS_TRAIL(c)) { \
-        (c)=U16_GET_SUPPLEMENTARY((s)[--(i)], (c)); \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Move the string offset from one code point boundary to the previous one
- * and get the code point between them.
- * (Pre-decrementing backward iteration.)
- * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
- *
- * The input offset may be the same as the string length.
- * If the offset is behind a trail surrogate unit
- * for a supplementary code point, then the macro will read
- * the preceding lead surrogate as well.
- * If the offset is behind a lead surrogate or behind a single, unpaired
- * trail surrogate, then c is set to that unpaired surrogate.
- *
- * @param s const UChar * string
- * @param start starting string offset (usually 0)
- * @param i string offset, must be start(start) && U16_IS_LEAD(__c2=(s)[(i)-1])) { \
-            --(i); \
-            (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \
-        } \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Move the string offset from one code point boundary to the previous one
- * and get the code point between them.
- * (Pre-decrementing backward iteration.)
- * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
- *
- * The input offset may be the same as the string length.
- * If the offset is behind a trail surrogate unit
- * for a supplementary code point, then the macro will read
- * the preceding lead surrogate as well.
- * If the offset is behind a lead surrogate or behind a single, unpaired
- * trail surrogate, then c is set to U+FFFD.
- *
- * @param s const UChar * string
- * @param start starting string offset (usually 0)
- * @param i string offset, must be start(start) && U16_IS_LEAD(__c2=(s)[(i)-1])) { \
-            --(i); \
-            (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \
-        } else { \
-            (c)=0xfffd; \
-        } \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Move the string offset from one code point boundary to the previous one.
- * (Pre-decrementing backward iteration.)
- * The input offset may be the same as the string length.
- * "Unsafe" macro, assumes well-formed UTF-16.
- *
- * @param s const UChar * string
- * @param i string offset
- * @see U16_BACK_1
- * @stable ICU 2.4
- */
-#define U16_BACK_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
-    if(U16_IS_TRAIL((s)[--(i)])) { \
-        --(i); \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Move the string offset from one code point boundary to the previous one.
- * (Pre-decrementing backward iteration.)
- * The input offset may be the same as the string length.
- * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
- *
- * @param s const UChar * string
- * @param start starting string offset (usually 0)
- * @param i string offset, must be start(start) && U16_IS_LEAD((s)[(i)-1])) { \
-        --(i); \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Move the string offset from one code point boundary to the n-th one before it,
- * i.e., move backward by n code points.
- * (Pre-decrementing backward iteration.)
- * The input offset may be the same as the string length.
- * "Unsafe" macro, assumes well-formed UTF-16.
- *
- * @param s const UChar * string
- * @param i string offset
- * @param n number of code points to skip
- * @see U16_BACK_N
- * @stable ICU 2.4
- */
-#define U16_BACK_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \
-    int32_t __N=(n); \
-    while(__N>0) { \
-        U16_BACK_1_UNSAFE(s, i); \
-        --__N; \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Move the string offset from one code point boundary to the n-th one before it,
- * i.e., move backward by n code points.
- * (Pre-decrementing backward iteration.)
- * The input offset may be the same as the string length.
- * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
- *
- * @param s const UChar * string
- * @param start start of string
- * @param i string offset, must be start0 && (i)>(start)) { \
-        U16_BACK_1(s, start, i); \
-        --__N; \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Adjust a random-access offset to a code point boundary after a code point.
- * If the offset is behind the lead surrogate of a surrogate pair,
- * then the offset is incremented.
- * Otherwise, it is not modified.
- * The input offset may be the same as the string length.
- * "Unsafe" macro, assumes well-formed UTF-16.
- *
- * @param s const UChar * string
- * @param i string offset
- * @see U16_SET_CP_LIMIT
- * @stable ICU 2.4
- */
-#define U16_SET_CP_LIMIT_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
-    if(U16_IS_LEAD((s)[(i)-1])) { \
-        ++(i); \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Adjust a random-access offset to a code point boundary after a code point.
- * If the offset is behind the lead surrogate of a surrogate pair,
- * then the offset is incremented.
- * Otherwise, it is not modified.
- * The input offset may be the same as the string length.
- * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
- *
- * The length can be negative for a NUL-terminated string.
- *
- * @param s const UChar * string
- * @param start int32_t starting string offset (usually 0)
- * @param i int32_t string offset, start<=i<=length
- * @param length int32_t string length
- * @see U16_SET_CP_LIMIT_UNSAFE
- * @stable ICU 2.4
- */
-#define U16_SET_CP_LIMIT(s, start, i, length) UPRV_BLOCK_MACRO_BEGIN { \
-    if((start)<(i) && ((i)<(length) || (length)<0) && U16_IS_LEAD((s)[(i)-1]) && U16_IS_TRAIL((s)[i])) { \
-        ++(i); \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-#endif
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/*
+*******************************************************************************
+*
+*   Copyright (C) 1999-2012, International Business Machines
+*   Corporation and others.  All Rights Reserved.
+*
+*******************************************************************************
+*   file name:  utf16.h
+*   encoding:   UTF-8
+*   tab size:   8 (not used)
+*   indentation:4
+*
+*   created on: 1999sep09
+*   created by: Markus W. Scherer
+*/
+
+/**
+ * \file
+ * \brief C API: 16-bit Unicode handling macros
+ * 
+ * This file defines macros to deal with 16-bit Unicode (UTF-16) code units and strings.
+ *
+ * For more information see utf.h and the ICU User Guide Strings chapter
+ * (https://unicode-org.github.io/icu/userguide/strings).
+ *
+ * Usage:
+ * ICU coding guidelines for if() statements should be followed when using these macros.
+ * Compound statements (curly braces {}) must be used  for if-else-while... 
+ * bodies and all macro statements should be terminated with semicolon.
+ */
+
+#ifndef __UTF16_H__
+#define __UTF16_H__
+
+#include 
+#include "unicode/umachine.h"
+#ifndef __UTF_H__
+#   include "unicode/utf.h"
+#endif
+
+/* single-code point definitions -------------------------------------------- */
+
+/**
+ * Does this code unit alone encode a code point (BMP, not a surrogate)?
+ * @param c 16-bit code unit
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U16_IS_SINGLE(c) !U_IS_SURROGATE(c)
+
+/**
+ * Is this code unit a lead surrogate (U+d800..U+dbff)?
+ * @param c 16-bit code unit
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U16_IS_LEAD(c) (((c)&0xfffffc00)==0xd800)
+
+/**
+ * Is this code unit a trail surrogate (U+dc00..U+dfff)?
+ * @param c 16-bit code unit
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00)
+
+/**
+ * Is this code unit a surrogate (U+d800..U+dfff)?
+ * @param c 16-bit code unit
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U16_IS_SURROGATE(c) U_IS_SURROGATE(c)
+
+/**
+ * Assuming c is a surrogate code point (U16_IS_SURROGATE(c)),
+ * is it a lead surrogate?
+ * @param c 16-bit code unit
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U16_IS_SURROGATE_LEAD(c) (((c)&0x400)==0)
+
+/**
+ * Assuming c is a surrogate code point (U16_IS_SURROGATE(c)),
+ * is it a trail surrogate?
+ * @param c 16-bit code unit
+ * @return true or false
+ * @stable ICU 4.2
+ */
+#define U16_IS_SURROGATE_TRAIL(c) (((c)&0x400)!=0)
+
+/**
+ * Helper constant for U16_GET_SUPPLEMENTARY.
+ * @internal
+ */
+#define U16_SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000)
+
+/**
+ * Get a supplementary code point value (U+10000..U+10ffff)
+ * from its lead and trail surrogates.
+ * The result is undefined if the input values are not
+ * lead and trail surrogates.
+ *
+ * @param lead lead surrogate (U+d800..U+dbff)
+ * @param trail trail surrogate (U+dc00..U+dfff)
+ * @return supplementary code point (U+10000..U+10ffff)
+ * @stable ICU 2.4
+ */
+#define U16_GET_SUPPLEMENTARY(lead, trail) \
+    (((UChar32)(lead)<<10UL)+(UChar32)(trail)-U16_SURROGATE_OFFSET)
+
+
+/**
+ * Get the lead surrogate (0xd800..0xdbff) for a
+ * supplementary code point (0x10000..0x10ffff).
+ * @param supplementary 32-bit code point (U+10000..U+10ffff)
+ * @return lead surrogate (U+d800..U+dbff) for supplementary
+ * @stable ICU 2.4
+ */
+#define U16_LEAD(supplementary) (UChar)(((supplementary)>>10)+0xd7c0)
+
+/**
+ * Get the trail surrogate (0xdc00..0xdfff) for a
+ * supplementary code point (0x10000..0x10ffff).
+ * @param supplementary 32-bit code point (U+10000..U+10ffff)
+ * @return trail surrogate (U+dc00..U+dfff) for supplementary
+ * @stable ICU 2.4
+ */
+#define U16_TRAIL(supplementary) (UChar)(((supplementary)&0x3ff)|0xdc00)
+
+/**
+ * How many 16-bit code units are used to encode this Unicode code point? (1 or 2)
+ * The result is not defined if c is not a Unicode code point (U+0000..U+10ffff).
+ * @param c 32-bit code point
+ * @return 1 or 2
+ * @stable ICU 2.4
+ */
+#define U16_LENGTH(c) ((uint32_t)(c)<=0xffff ? 1 : 2)
+
+/**
+ * The maximum number of 16-bit code units per Unicode code point (U+0000..U+10ffff).
+ * @return 2
+ * @stable ICU 2.4
+ */
+#define U16_MAX_LENGTH 2
+
+/**
+ * Get a code point from a string at a random-access offset,
+ * without changing the offset.
+ * "Unsafe" macro, assumes well-formed UTF-16.
+ *
+ * The offset may point to either the lead or trail surrogate unit
+ * for a supplementary code point, in which case the macro will read
+ * the adjacent matching surrogate as well.
+ * The result is undefined if the offset points to a single, unpaired surrogate.
+ * Iteration through a string is more efficient with U16_NEXT_UNSAFE or U16_NEXT.
+ *
+ * @param s const UChar * string
+ * @param i string offset
+ * @param c output UChar32 variable
+ * @see U16_GET
+ * @stable ICU 2.4
+ */
+#define U16_GET_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \
+    (c)=(s)[i]; \
+    if(U16_IS_SURROGATE(c)) { \
+        if(U16_IS_SURROGATE_LEAD(c)) { \
+            (c)=U16_GET_SUPPLEMENTARY((c), (s)[(i)+1]); \
+        } else { \
+            (c)=U16_GET_SUPPLEMENTARY((s)[(i)-1], (c)); \
+        } \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Get a code point from a string at a random-access offset,
+ * without changing the offset.
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The offset may point to either the lead or trail surrogate unit
+ * for a supplementary code point, in which case the macro will read
+ * the adjacent matching surrogate as well.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * If the offset points to a single, unpaired surrogate, then
+ * c is set to that unpaired surrogate.
+ * Iteration through a string is more efficient with U16_NEXT_UNSAFE or U16_NEXT.
+ *
+ * @param s const UChar * string
+ * @param start starting string offset (usually 0)
+ * @param i string offset, must be start<=i(start) && U16_IS_LEAD(__c2=(s)[(i)-1])) { \
+                (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \
+            } \
+        } \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Get a code point from a string at a random-access offset,
+ * without changing the offset.
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The offset may point to either the lead or trail surrogate unit
+ * for a supplementary code point, in which case the macro will read
+ * the adjacent matching surrogate as well.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * If the offset points to a single, unpaired surrogate, then
+ * c is set to U+FFFD.
+ * Iteration through a string is more efficient with U16_NEXT_UNSAFE or U16_NEXT_OR_FFFD.
+ *
+ * @param s const UChar * string
+ * @param start starting string offset (usually 0)
+ * @param i string offset, must be start<=i(start) && U16_IS_LEAD(__c2=(s)[(i)-1])) { \
+                (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \
+            } else { \
+                (c)=0xfffd; \
+            } \
+        } \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/* definitions with forward iteration --------------------------------------- */
+
+/**
+ * Get a code point from a string at a code point boundary offset,
+ * and advance the offset to the next code point boundary.
+ * (Post-incrementing forward iteration.)
+ * "Unsafe" macro, assumes well-formed UTF-16.
+ *
+ * The offset may point to the lead surrogate unit
+ * for a supplementary code point, in which case the macro will read
+ * the following trail surrogate as well.
+ * If the offset points to a trail surrogate, then that itself
+ * will be returned as the code point.
+ * The result is undefined if the offset points to a single, unpaired lead surrogate.
+ *
+ * @param s const UChar * string
+ * @param i string offset
+ * @param c output UChar32 variable
+ * @see U16_NEXT
+ * @stable ICU 2.4
+ */
+#define U16_NEXT_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \
+    (c)=(s)[(i)++]; \
+    if(U16_IS_LEAD(c)) { \
+        (c)=U16_GET_SUPPLEMENTARY((c), (s)[(i)++]); \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Get a code point from a string at a code point boundary offset,
+ * and advance the offset to the next code point boundary.
+ * (Post-incrementing forward iteration.)
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * The offset may point to the lead surrogate unit
+ * for a supplementary code point, in which case the macro will read
+ * the following trail surrogate as well.
+ * If the offset points to a trail surrogate or
+ * to a single, unpaired lead surrogate, then c is set to that unpaired surrogate.
+ *
+ * @param s const UChar * string
+ * @param i string offset, must be i>10)+0xd7c0); \
+        (s)[(i)++]=(uint16_t)(((c)&0x3ff)|0xdc00); \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Append a code point to a string, overwriting 1 or 2 code units.
+ * The offset points to the current end of the string contents
+ * and is advanced (post-increment).
+ * "Safe" macro, checks for a valid code point.
+ * If a surrogate pair is written, checks for sufficient space in the string.
+ * If the code point is not valid or a trail surrogate does not fit,
+ * then isError is set to true.
+ *
+ * @param s const UChar * string buffer
+ * @param i string offset, must be i>10)+0xd7c0); \
+        (s)[(i)++]=(uint16_t)(((c)&0x3ff)|0xdc00); \
+    } else /* c>0x10ffff or not enough space */ { \
+        (isError)=true; \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Advance the string offset from one code point boundary to the next.
+ * (Post-incrementing iteration.)
+ * "Unsafe" macro, assumes well-formed UTF-16.
+ *
+ * @param s const UChar * string
+ * @param i string offset
+ * @see U16_FWD_1
+ * @stable ICU 2.4
+ */
+#define U16_FWD_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
+    if(U16_IS_LEAD((s)[(i)++])) { \
+        ++(i); \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Advance the string offset from one code point boundary to the next.
+ * (Post-incrementing iteration.)
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * @param s const UChar * string
+ * @param i string offset, must be i0) { \
+        U16_FWD_1_UNSAFE(s, i); \
+        --__N; \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Advance the string offset from one code point boundary to the n-th next one,
+ * i.e., move forward by n code points.
+ * (Post-incrementing iteration.)
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * @param s const UChar * string
+ * @param i int32_t string offset, must be i0 && ((i)<(length) || ((length)<0 && (s)[i]!=0))) { \
+        U16_FWD_1(s, i, length); \
+        --__N; \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Adjust a random-access offset to a code point boundary
+ * at the start of a code point.
+ * If the offset points to the trail surrogate of a surrogate pair,
+ * then the offset is decremented.
+ * Otherwise, it is not modified.
+ * "Unsafe" macro, assumes well-formed UTF-16.
+ *
+ * @param s const UChar * string
+ * @param i string offset
+ * @see U16_SET_CP_START
+ * @stable ICU 2.4
+ */
+#define U16_SET_CP_START_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
+    if(U16_IS_TRAIL((s)[i])) { \
+        --(i); \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Adjust a random-access offset to a code point boundary
+ * at the start of a code point.
+ * If the offset points to the trail surrogate of a surrogate pair,
+ * then the offset is decremented.
+ * Otherwise, it is not modified.
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * @param s const UChar * string
+ * @param start starting string offset (usually 0)
+ * @param i string offset, must be start<=i
+ * @see U16_SET_CP_START_UNSAFE
+ * @stable ICU 2.4
+ */
+#define U16_SET_CP_START(s, start, i) UPRV_BLOCK_MACRO_BEGIN { \
+    if(U16_IS_TRAIL((s)[i]) && (i)>(start) && U16_IS_LEAD((s)[(i)-1])) { \
+        --(i); \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/* definitions with backward iteration -------------------------------------- */
+
+/**
+ * Move the string offset from one code point boundary to the previous one
+ * and get the code point between them.
+ * (Pre-decrementing backward iteration.)
+ * "Unsafe" macro, assumes well-formed UTF-16.
+ *
+ * The input offset may be the same as the string length.
+ * If the offset is behind a trail surrogate unit
+ * for a supplementary code point, then the macro will read
+ * the preceding lead surrogate as well.
+ * If the offset is behind a lead surrogate, then that itself
+ * will be returned as the code point.
+ * The result is undefined if the offset is behind a single, unpaired trail surrogate.
+ *
+ * @param s const UChar * string
+ * @param i string offset
+ * @param c output UChar32 variable
+ * @see U16_PREV
+ * @stable ICU 2.4
+ */
+#define U16_PREV_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \
+    (c)=(s)[--(i)]; \
+    if(U16_IS_TRAIL(c)) { \
+        (c)=U16_GET_SUPPLEMENTARY((s)[--(i)], (c)); \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Move the string offset from one code point boundary to the previous one
+ * and get the code point between them.
+ * (Pre-decrementing backward iteration.)
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The input offset may be the same as the string length.
+ * If the offset is behind a trail surrogate unit
+ * for a supplementary code point, then the macro will read
+ * the preceding lead surrogate as well.
+ * If the offset is behind a lead surrogate or behind a single, unpaired
+ * trail surrogate, then c is set to that unpaired surrogate.
+ *
+ * @param s const UChar * string
+ * @param start starting string offset (usually 0)
+ * @param i string offset, must be start(start) && U16_IS_LEAD(__c2=(s)[(i)-1])) { \
+            --(i); \
+            (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \
+        } \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Move the string offset from one code point boundary to the previous one
+ * and get the code point between them.
+ * (Pre-decrementing backward iteration.)
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The input offset may be the same as the string length.
+ * If the offset is behind a trail surrogate unit
+ * for a supplementary code point, then the macro will read
+ * the preceding lead surrogate as well.
+ * If the offset is behind a lead surrogate or behind a single, unpaired
+ * trail surrogate, then c is set to U+FFFD.
+ *
+ * @param s const UChar * string
+ * @param start starting string offset (usually 0)
+ * @param i string offset, must be start(start) && U16_IS_LEAD(__c2=(s)[(i)-1])) { \
+            --(i); \
+            (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \
+        } else { \
+            (c)=0xfffd; \
+        } \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Move the string offset from one code point boundary to the previous one.
+ * (Pre-decrementing backward iteration.)
+ * The input offset may be the same as the string length.
+ * "Unsafe" macro, assumes well-formed UTF-16.
+ *
+ * @param s const UChar * string
+ * @param i string offset
+ * @see U16_BACK_1
+ * @stable ICU 2.4
+ */
+#define U16_BACK_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
+    if(U16_IS_TRAIL((s)[--(i)])) { \
+        --(i); \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Move the string offset from one code point boundary to the previous one.
+ * (Pre-decrementing backward iteration.)
+ * The input offset may be the same as the string length.
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * @param s const UChar * string
+ * @param start starting string offset (usually 0)
+ * @param i string offset, must be start(start) && U16_IS_LEAD((s)[(i)-1])) { \
+        --(i); \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Move the string offset from one code point boundary to the n-th one before it,
+ * i.e., move backward by n code points.
+ * (Pre-decrementing backward iteration.)
+ * The input offset may be the same as the string length.
+ * "Unsafe" macro, assumes well-formed UTF-16.
+ *
+ * @param s const UChar * string
+ * @param i string offset
+ * @param n number of code points to skip
+ * @see U16_BACK_N
+ * @stable ICU 2.4
+ */
+#define U16_BACK_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \
+    int32_t __N=(n); \
+    while(__N>0) { \
+        U16_BACK_1_UNSAFE(s, i); \
+        --__N; \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Move the string offset from one code point boundary to the n-th one before it,
+ * i.e., move backward by n code points.
+ * (Pre-decrementing backward iteration.)
+ * The input offset may be the same as the string length.
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * @param s const UChar * string
+ * @param start start of string
+ * @param i string offset, must be start0 && (i)>(start)) { \
+        U16_BACK_1(s, start, i); \
+        --__N; \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Adjust a random-access offset to a code point boundary after a code point.
+ * If the offset is behind the lead surrogate of a surrogate pair,
+ * then the offset is incremented.
+ * Otherwise, it is not modified.
+ * The input offset may be the same as the string length.
+ * "Unsafe" macro, assumes well-formed UTF-16.
+ *
+ * @param s const UChar * string
+ * @param i string offset
+ * @see U16_SET_CP_LIMIT
+ * @stable ICU 2.4
+ */
+#define U16_SET_CP_LIMIT_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
+    if(U16_IS_LEAD((s)[(i)-1])) { \
+        ++(i); \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Adjust a random-access offset to a code point boundary after a code point.
+ * If the offset is behind the lead surrogate of a surrogate pair,
+ * then the offset is incremented.
+ * Otherwise, it is not modified.
+ * The input offset may be the same as the string length.
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * @param s const UChar * string
+ * @param start int32_t starting string offset (usually 0)
+ * @param i int32_t string offset, start<=i<=length
+ * @param length int32_t string length
+ * @see U16_SET_CP_LIMIT_UNSAFE
+ * @stable ICU 2.4
+ */
+#define U16_SET_CP_LIMIT(s, start, i, length) UPRV_BLOCK_MACRO_BEGIN { \
+    if((start)<(i) && ((i)<(length) || (length)<0) && U16_IS_LEAD((s)[(i)-1]) && U16_IS_TRAIL((s)[i])) { \
+        ++(i); \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+#endif
diff --git a/deps/icu-small/source/common/unicode/utf32.h b/deps/icu-small/source/common/unicode/utf32.h
index 8822c4dd0962c8..ef25094d1ddfb5 100644
--- a/deps/icu-small/source/common/unicode/utf32.h
+++ b/deps/icu-small/source/common/unicode/utf32.h
@@ -1,25 +1,25 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-*
-*   Copyright (C) 1999-2001, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-*
-*******************************************************************************
-*   file name:  utf32.h
-*   encoding:   UTF-8
-*   tab size:   8 (not used)
-*   indentation:4
-*
-*   created on: 1999sep20
-*   created by: Markus W. Scherer
-*/
-/**
- * \file
- * \brief C API: UTF-32 macros
- *
- * This file is obsolete and its contents moved to utf_old.h.
- * See utf_old.h and Jitterbug 2150 and its discussion on the ICU mailing list
- * in September 2002.
- */
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/*
+*******************************************************************************
+*
+*   Copyright (C) 1999-2001, International Business Machines
+*   Corporation and others.  All Rights Reserved.
+*
+*******************************************************************************
+*   file name:  utf32.h
+*   encoding:   UTF-8
+*   tab size:   8 (not used)
+*   indentation:4
+*
+*   created on: 1999sep20
+*   created by: Markus W. Scherer
+*/
+/**
+ * \file
+ * \brief C API: UTF-32 macros
+ *
+ * This file is obsolete and its contents moved to utf_old.h.
+ * See utf_old.h and Jitterbug 2150 and its discussion on the ICU mailing list
+ * in September 2002.
+ */
diff --git a/deps/icu-small/source/common/unicode/utf8.h b/deps/icu-small/source/common/unicode/utf8.h
index 5a07435fcf6096..e89fc015d29f2a 100644
--- a/deps/icu-small/source/common/unicode/utf8.h
+++ b/deps/icu-small/source/common/unicode/utf8.h
@@ -1,882 +1,882 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-*
-*   Copyright (C) 1999-2015, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-*
-*******************************************************************************
-*   file name:  utf8.h
-*   encoding:   UTF-8
-*   tab size:   8 (not used)
-*   indentation:4
-*
-*   created on: 1999sep13
-*   created by: Markus W. Scherer
-*/
-
-/**
- * \file
- * \brief C API: 8-bit Unicode handling macros
- * 
- * This file defines macros to deal with 8-bit Unicode (UTF-8) code units (bytes) and strings.
- *
- * For more information see utf.h and the ICU User Guide Strings chapter
- * (https://unicode-org.github.io/icu/userguide/strings).
- *
- * Usage:
- * ICU coding guidelines for if() statements should be followed when using these macros.
- * Compound statements (curly braces {}) must be used  for if-else-while... 
- * bodies and all macro statements should be terminated with semicolon.
- */
-
-#ifndef __UTF8_H__
-#define __UTF8_H__
-
-#include 
-#include "unicode/umachine.h"
-#ifndef __UTF_H__
-#   include "unicode/utf.h"
-#endif
-
-/* internal definitions ----------------------------------------------------- */
-
-/**
- * Counts the trail bytes for a UTF-8 lead byte.
- * Returns 0 for 0..0xc1 as well as for 0xf5..0xff.
- * leadByte might be evaluated multiple times.
- *
- * This is internal since it is not meant to be called directly by external clients;
- * however it is called by public macros in this file and thus must remain stable.
- *
- * @param leadByte The first byte of a UTF-8 sequence. Must be 0..0xff.
- * @internal
- */
-#define U8_COUNT_TRAIL_BYTES(leadByte) \
-    (U8_IS_LEAD(leadByte) ? \
-        ((uint8_t)(leadByte)>=0xe0)+((uint8_t)(leadByte)>=0xf0)+1 : 0)
-
-/**
- * Counts the trail bytes for a UTF-8 lead byte of a valid UTF-8 sequence.
- * Returns 0 for 0..0xc1. Undefined for 0xf5..0xff.
- * leadByte might be evaluated multiple times.
- *
- * This is internal since it is not meant to be called directly by external clients;
- * however it is called by public macros in this file and thus must remain stable.
- *
- * @param leadByte The first byte of a UTF-8 sequence. Must be 0..0xff.
- * @internal
- */
-#define U8_COUNT_TRAIL_BYTES_UNSAFE(leadByte) \
-    (((uint8_t)(leadByte)>=0xc2)+((uint8_t)(leadByte)>=0xe0)+((uint8_t)(leadByte)>=0xf0))
-
-/**
- * Mask a UTF-8 lead byte, leave only the lower bits that form part of the code point value.
- *
- * This is internal since it is not meant to be called directly by external clients;
- * however it is called by public macros in this file and thus must remain stable.
- * @internal
- */
-#define U8_MASK_LEAD_BYTE(leadByte, countTrailBytes) ((leadByte)&=(1<<(6-(countTrailBytes)))-1)
-
-/**
- * Internal bit vector for 3-byte UTF-8 validity check, for use in U8_IS_VALID_LEAD3_AND_T1.
- * Each bit indicates whether one lead byte + first trail byte pair starts a valid sequence.
- * Lead byte E0..EF bits 3..0 are used as byte index,
- * first trail byte bits 7..5 are used as bit index into that byte.
- * @see U8_IS_VALID_LEAD3_AND_T1
- * @internal
- */
-#define U8_LEAD3_T1_BITS "\x20\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x10\x30\x30"
-
-/**
- * Internal 3-byte UTF-8 validity check.
- * Non-zero if lead byte E0..EF and first trail byte 00..FF start a valid sequence.
- * @internal
- */
-#define U8_IS_VALID_LEAD3_AND_T1(lead, t1) (U8_LEAD3_T1_BITS[(lead)&0xf]&(1<<((uint8_t)(t1)>>5)))
-
-/**
- * Internal bit vector for 4-byte UTF-8 validity check, for use in U8_IS_VALID_LEAD4_AND_T1.
- * Each bit indicates whether one lead byte + first trail byte pair starts a valid sequence.
- * First trail byte bits 7..4 are used as byte index,
- * lead byte F0..F4 bits 2..0 are used as bit index into that byte.
- * @see U8_IS_VALID_LEAD4_AND_T1
- * @internal
- */
-#define U8_LEAD4_T1_BITS "\x00\x00\x00\x00\x00\x00\x00\x00\x1E\x0F\x0F\x0F\x00\x00\x00\x00"
-
-/**
- * Internal 4-byte UTF-8 validity check.
- * Non-zero if lead byte F0..F4 and first trail byte 00..FF start a valid sequence.
- * @internal
- */
-#define U8_IS_VALID_LEAD4_AND_T1(lead, t1) (U8_LEAD4_T1_BITS[(uint8_t)(t1)>>4]&(1<<((lead)&7)))
-
-/**
- * Function for handling "next code point" with error-checking.
- *
- * This is internal since it is not meant to be called directly by external clients;
- * however it is called by public macros in this
- * file and thus must remain stable, and should not be hidden when other internal
- * functions are hidden (otherwise public macros would fail to compile).
- * @internal
- */
-U_CAPI UChar32 U_EXPORT2
-utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, UChar32 c, UBool strict);
-
-/**
- * Function for handling "append code point" with error-checking.
- *
- * This is internal since it is not meant to be called directly by external clients;
- * however it is called by public macros in this
- * file and thus must remain stable, and should not be hidden when other internal
- * functions are hidden (otherwise public macros would fail to compile).
- * @internal
- */
-U_CAPI int32_t U_EXPORT2
-utf8_appendCharSafeBody(uint8_t *s, int32_t i, int32_t length, UChar32 c, UBool *pIsError);
-
-/**
- * Function for handling "previous code point" with error-checking.
- *
- * This is internal since it is not meant to be called directly by external clients;
- * however it is called by public macros in this
- * file and thus must remain stable, and should not be hidden when other internal
- * functions are hidden (otherwise public macros would fail to compile).
- * @internal
- */
-U_CAPI UChar32 U_EXPORT2
-utf8_prevCharSafeBody(const uint8_t *s, int32_t start, int32_t *pi, UChar32 c, UBool strict);
-
-/**
- * Function for handling "skip backward one code point" with error-checking.
- *
- * This is internal since it is not meant to be called directly by external clients;
- * however it is called by public macros in this
- * file and thus must remain stable, and should not be hidden when other internal
- * functions are hidden (otherwise public macros would fail to compile).
- * @internal
- */
-U_CAPI int32_t U_EXPORT2
-utf8_back1SafeBody(const uint8_t *s, int32_t start, int32_t i);
-
-/* single-code point definitions -------------------------------------------- */
-
-/**
- * Does this code unit (byte) encode a code point by itself (US-ASCII 0..0x7f)?
- * @param c 8-bit code unit (byte)
- * @return true or false
- * @stable ICU 2.4
- */
-#define U8_IS_SINGLE(c) (((c)&0x80)==0)
-
-/**
- * Is this code unit (byte) a UTF-8 lead byte? (0xC2..0xF4)
- * @param c 8-bit code unit (byte)
- * @return true or false
- * @stable ICU 2.4
- */
-#define U8_IS_LEAD(c) ((uint8_t)((c)-0xc2)<=0x32)
-// 0x32=0xf4-0xc2
-
-/**
- * Is this code unit (byte) a UTF-8 trail byte? (0x80..0xBF)
- * @param c 8-bit code unit (byte)
- * @return true or false
- * @stable ICU 2.4
- */
-#define U8_IS_TRAIL(c) ((int8_t)(c)<-0x40)
-
-/**
- * How many code units (bytes) are used for the UTF-8 encoding
- * of this Unicode code point?
- * @param c 32-bit code point
- * @return 1..4, or 0 if c is a surrogate or not a Unicode code point
- * @stable ICU 2.4
- */
-#define U8_LENGTH(c) \
-    ((uint32_t)(c)<=0x7f ? 1 : \
-        ((uint32_t)(c)<=0x7ff ? 2 : \
-            ((uint32_t)(c)<=0xd7ff ? 3 : \
-                ((uint32_t)(c)<=0xdfff || (uint32_t)(c)>0x10ffff ? 0 : \
-                    ((uint32_t)(c)<=0xffff ? 3 : 4)\
-                ) \
-            ) \
-        ) \
-    )
-
-/**
- * The maximum number of UTF-8 code units (bytes) per Unicode code point (U+0000..U+10ffff).
- * @return 4
- * @stable ICU 2.4
- */
-#define U8_MAX_LENGTH 4
-
-/**
- * Get a code point from a string at a random-access offset,
- * without changing the offset.
- * The offset may point to either the lead byte or one of the trail bytes
- * for a code point, in which case the macro will read all of the bytes
- * for the code point.
- * The result is undefined if the offset points to an illegal UTF-8
- * byte sequence.
- * Iteration through a string is more efficient with U8_NEXT_UNSAFE or U8_NEXT.
- *
- * @param s const uint8_t * string
- * @param i string offset
- * @param c output UChar32 variable
- * @see U8_GET
- * @stable ICU 2.4
- */
-#define U8_GET_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \
-    int32_t _u8_get_unsafe_index=(int32_t)(i); \
-    U8_SET_CP_START_UNSAFE(s, _u8_get_unsafe_index); \
-    U8_NEXT_UNSAFE(s, _u8_get_unsafe_index, c); \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Get a code point from a string at a random-access offset,
- * without changing the offset.
- * The offset may point to either the lead byte or one of the trail bytes
- * for a code point, in which case the macro will read all of the bytes
- * for the code point.
- *
- * The length can be negative for a NUL-terminated string.
- *
- * If the offset points to an illegal UTF-8 byte sequence, then
- * c is set to a negative value.
- * Iteration through a string is more efficient with U8_NEXT_UNSAFE or U8_NEXT.
- *
- * @param s const uint8_t * string
- * @param start int32_t starting string offset
- * @param i int32_t string offset, must be start<=i=0xe0 ? \
-                ((c)<0xf0 ?  /* U+0800..U+FFFF except surrogates */ \
-                    U8_LEAD3_T1_BITS[(c)&=0xf]&(1<<((__t=(s)[i])>>5)) && \
-                    (__t&=0x3f, 1) \
-                :  /* U+10000..U+10FFFF */ \
-                    ((c)-=0xf0)<=4 && \
-                    U8_LEAD4_T1_BITS[(__t=(s)[i])>>4]&(1<<(c)) && \
-                    ((c)=((c)<<6)|(__t&0x3f), ++(i)!=(length)) && \
-                    (__t=(s)[i]-0x80)<=0x3f) && \
-                /* valid second-to-last trail byte */ \
-                ((c)=((c)<<6)|__t, ++(i)!=(length)) \
-            :  /* U+0080..U+07FF */ \
-                (c)>=0xc2 && ((c)&=0x1f, 1)) && \
-            /* last trail byte */ \
-            (__t=(s)[i]-0x80)<=0x3f && \
-            ((c)=((c)<<6)|__t, ++(i), 1)) { \
-        } else { \
-            (c)=(sub);  /* ill-formed*/ \
-        } \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Append a code point to a string, overwriting 1 to 4 bytes.
- * The offset points to the current end of the string contents
- * and is advanced (post-increment).
- * "Unsafe" macro, assumes a valid code point and sufficient space in the string.
- * Otherwise, the result is undefined.
- *
- * @param s const uint8_t * string buffer
- * @param i string offset
- * @param c code point to append
- * @see U8_APPEND
- * @stable ICU 2.4
- */
-#define U8_APPEND_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \
-    uint32_t __uc=(c); \
-    if(__uc<=0x7f) { \
-        (s)[(i)++]=(uint8_t)__uc; \
-    } else { \
-        if(__uc<=0x7ff) { \
-            (s)[(i)++]=(uint8_t)((__uc>>6)|0xc0); \
-        } else { \
-            if(__uc<=0xffff) { \
-                (s)[(i)++]=(uint8_t)((__uc>>12)|0xe0); \
-            } else { \
-                (s)[(i)++]=(uint8_t)((__uc>>18)|0xf0); \
-                (s)[(i)++]=(uint8_t)(((__uc>>12)&0x3f)|0x80); \
-            } \
-            (s)[(i)++]=(uint8_t)(((__uc>>6)&0x3f)|0x80); \
-        } \
-        (s)[(i)++]=(uint8_t)((__uc&0x3f)|0x80); \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Append a code point to a string, overwriting 1 to 4 bytes.
- * The offset points to the current end of the string contents
- * and is advanced (post-increment).
- * "Safe" macro, checks for a valid code point.
- * If a non-ASCII code point is written, checks for sufficient space in the string.
- * If the code point is not valid or trail bytes do not fit,
- * then isError is set to true.
- *
- * @param s const uint8_t * string buffer
- * @param i int32_t string offset, must be i>6)|0xc0); \
-        (s)[(i)++]=(uint8_t)((__uc&0x3f)|0x80); \
-    } else if((__uc<=0xd7ff || (0xe000<=__uc && __uc<=0xffff)) && (i)+2<(capacity)) { \
-        (s)[(i)++]=(uint8_t)((__uc>>12)|0xe0); \
-        (s)[(i)++]=(uint8_t)(((__uc>>6)&0x3f)|0x80); \
-        (s)[(i)++]=(uint8_t)((__uc&0x3f)|0x80); \
-    } else if(0xffff<__uc && __uc<=0x10ffff && (i)+3<(capacity)) { \
-        (s)[(i)++]=(uint8_t)((__uc>>18)|0xf0); \
-        (s)[(i)++]=(uint8_t)(((__uc>>12)&0x3f)|0x80); \
-        (s)[(i)++]=(uint8_t)(((__uc>>6)&0x3f)|0x80); \
-        (s)[(i)++]=(uint8_t)((__uc&0x3f)|0x80); \
-    } else { \
-        (isError)=true; \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Advance the string offset from one code point boundary to the next.
- * (Post-incrementing iteration.)
- * "Unsafe" macro, assumes well-formed UTF-8.
- *
- * @param s const uint8_t * string
- * @param i string offset
- * @see U8_FWD_1
- * @stable ICU 2.4
- */
-#define U8_FWD_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
-    (i)+=1+U8_COUNT_TRAIL_BYTES_UNSAFE((s)[i]); \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Advance the string offset from one code point boundary to the next.
- * (Post-incrementing iteration.)
- * "Safe" macro, checks for illegal sequences and for string boundaries.
- *
- * The length can be negative for a NUL-terminated string.
- *
- * @param s const uint8_t * string
- * @param i int32_t string offset, must be i=0xf0 */ { \
-            if(U8_IS_VALID_LEAD4_AND_T1(__b, __t1) && \
-                    ++(i)!=(length) && U8_IS_TRAIL((s)[i]) && \
-                    ++(i)!=(length) && U8_IS_TRAIL((s)[i])) { \
-                ++(i); \
-            } \
-        } \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Advance the string offset from one code point boundary to the n-th next one,
- * i.e., move forward by n code points.
- * (Post-incrementing iteration.)
- * "Unsafe" macro, assumes well-formed UTF-8.
- *
- * @param s const uint8_t * string
- * @param i string offset
- * @param n number of code points to skip
- * @see U8_FWD_N
- * @stable ICU 2.4
- */
-#define U8_FWD_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \
-    int32_t __N=(n); \
-    while(__N>0) { \
-        U8_FWD_1_UNSAFE(s, i); \
-        --__N; \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Advance the string offset from one code point boundary to the n-th next one,
- * i.e., move forward by n code points.
- * (Post-incrementing iteration.)
- * "Safe" macro, checks for illegal sequences and for string boundaries.
- *
- * The length can be negative for a NUL-terminated string.
- *
- * @param s const uint8_t * string
- * @param i int32_t string offset, must be i0 && ((i)<(length) || ((length)<0 && (s)[i]!=0))) { \
-        U8_FWD_1(s, i, length); \
-        --__N; \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Adjust a random-access offset to a code point boundary
- * at the start of a code point.
- * If the offset points to a UTF-8 trail byte,
- * then the offset is moved backward to the corresponding lead byte.
- * Otherwise, it is not modified.
- * "Unsafe" macro, assumes well-formed UTF-8.
- *
- * @param s const uint8_t * string
- * @param i string offset
- * @see U8_SET_CP_START
- * @stable ICU 2.4
- */
-#define U8_SET_CP_START_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
-    while(U8_IS_TRAIL((s)[i])) { --(i); } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Adjust a random-access offset to a code point boundary
- * at the start of a code point.
- * If the offset points to a UTF-8 trail byte,
- * then the offset is moved backward to the corresponding lead byte.
- * Otherwise, it is not modified.
- *
- * "Safe" macro, checks for illegal sequences and for string boundaries.
- * Unlike U8_TRUNCATE_IF_INCOMPLETE(), this macro always reads s[i].
- *
- * @param s const uint8_t * string
- * @param start int32_t starting string offset (usually 0)
- * @param i int32_t string offset, must be start<=i
- * @see U8_SET_CP_START_UNSAFE
- * @see U8_TRUNCATE_IF_INCOMPLETE
- * @stable ICU 2.4
- */
-#define U8_SET_CP_START(s, start, i) UPRV_BLOCK_MACRO_BEGIN { \
-    if(U8_IS_TRAIL((s)[(i)])) { \
-        (i)=utf8_back1SafeBody(s, start, (i)); \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * If the string ends with a UTF-8 byte sequence that is valid so far
- * but incomplete, then reduce the length of the string to end before
- * the lead byte of that incomplete sequence.
- * For example, if the string ends with E1 80, the length is reduced by 2.
- *
- * In all other cases (the string ends with a complete sequence, or it is not
- * possible for any further trail byte to extend the trailing sequence)
- * the length remains unchanged.
- *
- * Useful for processing text split across multiple buffers
- * (save the incomplete sequence for later)
- * and for optimizing iteration
- * (check for string length only once per character).
- *
- * "Safe" macro, checks for illegal sequences and for string boundaries.
- * Unlike U8_SET_CP_START(), this macro never reads s[length].
- *
- * (In UTF-16, simply check for U16_IS_LEAD(last code unit).)
- *
- * @param s const uint8_t * string
- * @param start int32_t starting string offset (usually 0)
- * @param length int32_t string length (usually start<=length)
- * @see U8_SET_CP_START
- * @stable ICU 61
- */
-#define U8_TRUNCATE_IF_INCOMPLETE(s, start, length) UPRV_BLOCK_MACRO_BEGIN { \
-    if((length)>(start)) { \
-        uint8_t __b1=s[(length)-1]; \
-        if(U8_IS_SINGLE(__b1)) { \
-            /* common ASCII character */ \
-        } else if(U8_IS_LEAD(__b1)) { \
-            --(length); \
-        } else if(U8_IS_TRAIL(__b1) && ((length)-2)>=(start)) { \
-            uint8_t __b2=s[(length)-2]; \
-            if(0xe0<=__b2 && __b2<=0xf4) { \
-                if(__b2<0xf0 ? U8_IS_VALID_LEAD3_AND_T1(__b2, __b1) : \
-                        U8_IS_VALID_LEAD4_AND_T1(__b2, __b1)) { \
-                    (length)-=2; \
-                } \
-            } else if(U8_IS_TRAIL(__b2) && ((length)-3)>=(start)) { \
-                uint8_t __b3=s[(length)-3]; \
-                if(0xf0<=__b3 && __b3<=0xf4 && U8_IS_VALID_LEAD4_AND_T1(__b3, __b2)) { \
-                    (length)-=3; \
-                } \
-            } \
-        } \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/* definitions with backward iteration -------------------------------------- */
-
-/**
- * Move the string offset from one code point boundary to the previous one
- * and get the code point between them.
- * (Pre-decrementing backward iteration.)
- * "Unsafe" macro, assumes well-formed UTF-8.
- *
- * The input offset may be the same as the string length.
- * If the offset is behind a multi-byte sequence, then the macro will read
- * the whole sequence.
- * If the offset is behind a lead byte, then that itself
- * will be returned as the code point.
- * The result is undefined if the offset is behind an illegal UTF-8 sequence.
- *
- * @param s const uint8_t * string
- * @param i string offset
- * @param c output UChar32 variable
- * @see U8_PREV
- * @stable ICU 2.4
- */
-#define U8_PREV_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \
-    (c)=(uint8_t)(s)[--(i)]; \
-    if(U8_IS_TRAIL(c)) { \
-        uint8_t __b, __count=1, __shift=6; \
-\
-        /* c is a trail byte */ \
-        (c)&=0x3f; \
-        for(;;) { \
-            __b=(s)[--(i)]; \
-            if(__b>=0xc0) { \
-                U8_MASK_LEAD_BYTE(__b, __count); \
-                (c)|=(UChar32)__b<<__shift; \
-                break; \
-            } else { \
-                (c)|=(UChar32)(__b&0x3f)<<__shift; \
-                ++__count; \
-                __shift+=6; \
-            } \
-        } \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Move the string offset from one code point boundary to the previous one
- * and get the code point between them.
- * (Pre-decrementing backward iteration.)
- * "Safe" macro, checks for illegal sequences and for string boundaries.
- *
- * The input offset may be the same as the string length.
- * If the offset is behind a multi-byte sequence, then the macro will read
- * the whole sequence.
- * If the offset is behind a lead byte, then that itself
- * will be returned as the code point.
- * If the offset is behind an illegal UTF-8 sequence, then c is set to a negative value.
- *
- * @param s const uint8_t * string
- * @param start int32_t starting string offset (usually 0)
- * @param i int32_t string offset, must be start0) { \
-        U8_BACK_1_UNSAFE(s, i); \
-        --__N; \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Move the string offset from one code point boundary to the n-th one before it,
- * i.e., move backward by n code points.
- * (Pre-decrementing backward iteration.)
- * The input offset may be the same as the string length.
- * "Safe" macro, checks for illegal sequences and for string boundaries.
- *
- * @param s const uint8_t * string
- * @param start int32_t index of the start of the string
- * @param i int32_t string offset, must be start0 && (i)>(start)) { \
-        U8_BACK_1(s, start, i); \
-        --__N; \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Adjust a random-access offset to a code point boundary after a code point.
- * If the offset is behind a partial multi-byte sequence,
- * then the offset is incremented to behind the whole sequence.
- * Otherwise, it is not modified.
- * The input offset may be the same as the string length.
- * "Unsafe" macro, assumes well-formed UTF-8.
- *
- * @param s const uint8_t * string
- * @param i string offset
- * @see U8_SET_CP_LIMIT
- * @stable ICU 2.4
- */
-#define U8_SET_CP_LIMIT_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
-    U8_BACK_1_UNSAFE(s, i); \
-    U8_FWD_1_UNSAFE(s, i); \
-} UPRV_BLOCK_MACRO_END
-
-/**
- * Adjust a random-access offset to a code point boundary after a code point.
- * If the offset is behind a partial multi-byte sequence,
- * then the offset is incremented to behind the whole sequence.
- * Otherwise, it is not modified.
- * The input offset may be the same as the string length.
- * "Safe" macro, checks for illegal sequences and for string boundaries.
- *
- * The length can be negative for a NUL-terminated string.
- *
- * @param s const uint8_t * string
- * @param start int32_t starting string offset (usually 0)
- * @param i int32_t string offset, must be start<=i<=length
- * @param length int32_t string length
- * @see U8_SET_CP_LIMIT_UNSAFE
- * @stable ICU 2.4
- */
-#define U8_SET_CP_LIMIT(s, start, i, length) UPRV_BLOCK_MACRO_BEGIN { \
-    if((start)<(i) && ((i)<(length) || (length)<0)) { \
-        U8_BACK_1(s, start, i); \
-        U8_FWD_1(s, i, length); \
-    } \
-} UPRV_BLOCK_MACRO_END
-
-#endif
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/*
+*******************************************************************************
+*
+*   Copyright (C) 1999-2015, International Business Machines
+*   Corporation and others.  All Rights Reserved.
+*
+*******************************************************************************
+*   file name:  utf8.h
+*   encoding:   UTF-8
+*   tab size:   8 (not used)
+*   indentation:4
+*
+*   created on: 1999sep13
+*   created by: Markus W. Scherer
+*/
+
+/**
+ * \file
+ * \brief C API: 8-bit Unicode handling macros
+ * 
+ * This file defines macros to deal with 8-bit Unicode (UTF-8) code units (bytes) and strings.
+ *
+ * For more information see utf.h and the ICU User Guide Strings chapter
+ * (https://unicode-org.github.io/icu/userguide/strings).
+ *
+ * Usage:
+ * ICU coding guidelines for if() statements should be followed when using these macros.
+ * Compound statements (curly braces {}) must be used  for if-else-while... 
+ * bodies and all macro statements should be terminated with semicolon.
+ */
+
+#ifndef __UTF8_H__
+#define __UTF8_H__
+
+#include 
+#include "unicode/umachine.h"
+#ifndef __UTF_H__
+#   include "unicode/utf.h"
+#endif
+
+/* internal definitions ----------------------------------------------------- */
+
+/**
+ * Counts the trail bytes for a UTF-8 lead byte.
+ * Returns 0 for 0..0xc1 as well as for 0xf5..0xff.
+ * leadByte might be evaluated multiple times.
+ *
+ * This is internal since it is not meant to be called directly by external clients;
+ * however it is called by public macros in this file and thus must remain stable.
+ *
+ * @param leadByte The first byte of a UTF-8 sequence. Must be 0..0xff.
+ * @internal
+ */
+#define U8_COUNT_TRAIL_BYTES(leadByte) \
+    (U8_IS_LEAD(leadByte) ? \
+        ((uint8_t)(leadByte)>=0xe0)+((uint8_t)(leadByte)>=0xf0)+1 : 0)
+
+/**
+ * Counts the trail bytes for a UTF-8 lead byte of a valid UTF-8 sequence.
+ * Returns 0 for 0..0xc1. Undefined for 0xf5..0xff.
+ * leadByte might be evaluated multiple times.
+ *
+ * This is internal since it is not meant to be called directly by external clients;
+ * however it is called by public macros in this file and thus must remain stable.
+ *
+ * @param leadByte The first byte of a UTF-8 sequence. Must be 0..0xff.
+ * @internal
+ */
+#define U8_COUNT_TRAIL_BYTES_UNSAFE(leadByte) \
+    (((uint8_t)(leadByte)>=0xc2)+((uint8_t)(leadByte)>=0xe0)+((uint8_t)(leadByte)>=0xf0))
+
+/**
+ * Mask a UTF-8 lead byte, leave only the lower bits that form part of the code point value.
+ *
+ * This is internal since it is not meant to be called directly by external clients;
+ * however it is called by public macros in this file and thus must remain stable.
+ * @internal
+ */
+#define U8_MASK_LEAD_BYTE(leadByte, countTrailBytes) ((leadByte)&=(1<<(6-(countTrailBytes)))-1)
+
+/**
+ * Internal bit vector for 3-byte UTF-8 validity check, for use in U8_IS_VALID_LEAD3_AND_T1.
+ * Each bit indicates whether one lead byte + first trail byte pair starts a valid sequence.
+ * Lead byte E0..EF bits 3..0 are used as byte index,
+ * first trail byte bits 7..5 are used as bit index into that byte.
+ * @see U8_IS_VALID_LEAD3_AND_T1
+ * @internal
+ */
+#define U8_LEAD3_T1_BITS "\x20\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x10\x30\x30"
+
+/**
+ * Internal 3-byte UTF-8 validity check.
+ * Non-zero if lead byte E0..EF and first trail byte 00..FF start a valid sequence.
+ * @internal
+ */
+#define U8_IS_VALID_LEAD3_AND_T1(lead, t1) (U8_LEAD3_T1_BITS[(lead)&0xf]&(1<<((uint8_t)(t1)>>5)))
+
+/**
+ * Internal bit vector for 4-byte UTF-8 validity check, for use in U8_IS_VALID_LEAD4_AND_T1.
+ * Each bit indicates whether one lead byte + first trail byte pair starts a valid sequence.
+ * First trail byte bits 7..4 are used as byte index,
+ * lead byte F0..F4 bits 2..0 are used as bit index into that byte.
+ * @see U8_IS_VALID_LEAD4_AND_T1
+ * @internal
+ */
+#define U8_LEAD4_T1_BITS "\x00\x00\x00\x00\x00\x00\x00\x00\x1E\x0F\x0F\x0F\x00\x00\x00\x00"
+
+/**
+ * Internal 4-byte UTF-8 validity check.
+ * Non-zero if lead byte F0..F4 and first trail byte 00..FF start a valid sequence.
+ * @internal
+ */
+#define U8_IS_VALID_LEAD4_AND_T1(lead, t1) (U8_LEAD4_T1_BITS[(uint8_t)(t1)>>4]&(1<<((lead)&7)))
+
+/**
+ * Function for handling "next code point" with error-checking.
+ *
+ * This is internal since it is not meant to be called directly by external clients;
+ * however it is called by public macros in this
+ * file and thus must remain stable, and should not be hidden when other internal
+ * functions are hidden (otherwise public macros would fail to compile).
+ * @internal
+ */
+U_CAPI UChar32 U_EXPORT2
+utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, UChar32 c, UBool strict);
+
+/**
+ * Function for handling "append code point" with error-checking.
+ *
+ * This is internal since it is not meant to be called directly by external clients;
+ * however it is called by public macros in this
+ * file and thus must remain stable, and should not be hidden when other internal
+ * functions are hidden (otherwise public macros would fail to compile).
+ * @internal
+ */
+U_CAPI int32_t U_EXPORT2
+utf8_appendCharSafeBody(uint8_t *s, int32_t i, int32_t length, UChar32 c, UBool *pIsError);
+
+/**
+ * Function for handling "previous code point" with error-checking.
+ *
+ * This is internal since it is not meant to be called directly by external clients;
+ * however it is called by public macros in this
+ * file and thus must remain stable, and should not be hidden when other internal
+ * functions are hidden (otherwise public macros would fail to compile).
+ * @internal
+ */
+U_CAPI UChar32 U_EXPORT2
+utf8_prevCharSafeBody(const uint8_t *s, int32_t start, int32_t *pi, UChar32 c, UBool strict);
+
+/**
+ * Function for handling "skip backward one code point" with error-checking.
+ *
+ * This is internal since it is not meant to be called directly by external clients;
+ * however it is called by public macros in this
+ * file and thus must remain stable, and should not be hidden when other internal
+ * functions are hidden (otherwise public macros would fail to compile).
+ * @internal
+ */
+U_CAPI int32_t U_EXPORT2
+utf8_back1SafeBody(const uint8_t *s, int32_t start, int32_t i);
+
+/* single-code point definitions -------------------------------------------- */
+
+/**
+ * Does this code unit (byte) encode a code point by itself (US-ASCII 0..0x7f)?
+ * @param c 8-bit code unit (byte)
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U8_IS_SINGLE(c) (((c)&0x80)==0)
+
+/**
+ * Is this code unit (byte) a UTF-8 lead byte? (0xC2..0xF4)
+ * @param c 8-bit code unit (byte)
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U8_IS_LEAD(c) ((uint8_t)((c)-0xc2)<=0x32)
+// 0x32=0xf4-0xc2
+
+/**
+ * Is this code unit (byte) a UTF-8 trail byte? (0x80..0xBF)
+ * @param c 8-bit code unit (byte)
+ * @return true or false
+ * @stable ICU 2.4
+ */
+#define U8_IS_TRAIL(c) ((int8_t)(c)<-0x40)
+
+/**
+ * How many code units (bytes) are used for the UTF-8 encoding
+ * of this Unicode code point?
+ * @param c 32-bit code point
+ * @return 1..4, or 0 if c is a surrogate or not a Unicode code point
+ * @stable ICU 2.4
+ */
+#define U8_LENGTH(c) \
+    ((uint32_t)(c)<=0x7f ? 1 : \
+        ((uint32_t)(c)<=0x7ff ? 2 : \
+            ((uint32_t)(c)<=0xd7ff ? 3 : \
+                ((uint32_t)(c)<=0xdfff || (uint32_t)(c)>0x10ffff ? 0 : \
+                    ((uint32_t)(c)<=0xffff ? 3 : 4)\
+                ) \
+            ) \
+        ) \
+    )
+
+/**
+ * The maximum number of UTF-8 code units (bytes) per Unicode code point (U+0000..U+10ffff).
+ * @return 4
+ * @stable ICU 2.4
+ */
+#define U8_MAX_LENGTH 4
+
+/**
+ * Get a code point from a string at a random-access offset,
+ * without changing the offset.
+ * The offset may point to either the lead byte or one of the trail bytes
+ * for a code point, in which case the macro will read all of the bytes
+ * for the code point.
+ * The result is undefined if the offset points to an illegal UTF-8
+ * byte sequence.
+ * Iteration through a string is more efficient with U8_NEXT_UNSAFE or U8_NEXT.
+ *
+ * @param s const uint8_t * string
+ * @param i string offset
+ * @param c output UChar32 variable
+ * @see U8_GET
+ * @stable ICU 2.4
+ */
+#define U8_GET_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \
+    int32_t _u8_get_unsafe_index=(int32_t)(i); \
+    U8_SET_CP_START_UNSAFE(s, _u8_get_unsafe_index); \
+    U8_NEXT_UNSAFE(s, _u8_get_unsafe_index, c); \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Get a code point from a string at a random-access offset,
+ * without changing the offset.
+ * The offset may point to either the lead byte or one of the trail bytes
+ * for a code point, in which case the macro will read all of the bytes
+ * for the code point.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * If the offset points to an illegal UTF-8 byte sequence, then
+ * c is set to a negative value.
+ * Iteration through a string is more efficient with U8_NEXT_UNSAFE or U8_NEXT.
+ *
+ * @param s const uint8_t * string
+ * @param start int32_t starting string offset
+ * @param i int32_t string offset, must be start<=i=0xe0 ? \
+                ((c)<0xf0 ?  /* U+0800..U+FFFF except surrogates */ \
+                    U8_LEAD3_T1_BITS[(c)&=0xf]&(1<<((__t=(s)[i])>>5)) && \
+                    (__t&=0x3f, 1) \
+                :  /* U+10000..U+10FFFF */ \
+                    ((c)-=0xf0)<=4 && \
+                    U8_LEAD4_T1_BITS[(__t=(s)[i])>>4]&(1<<(c)) && \
+                    ((c)=((c)<<6)|(__t&0x3f), ++(i)!=(length)) && \
+                    (__t=(s)[i]-0x80)<=0x3f) && \
+                /* valid second-to-last trail byte */ \
+                ((c)=((c)<<6)|__t, ++(i)!=(length)) \
+            :  /* U+0080..U+07FF */ \
+                (c)>=0xc2 && ((c)&=0x1f, 1)) && \
+            /* last trail byte */ \
+            (__t=(s)[i]-0x80)<=0x3f && \
+            ((c)=((c)<<6)|__t, ++(i), 1)) { \
+        } else { \
+            (c)=(sub);  /* ill-formed*/ \
+        } \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Append a code point to a string, overwriting 1 to 4 bytes.
+ * The offset points to the current end of the string contents
+ * and is advanced (post-increment).
+ * "Unsafe" macro, assumes a valid code point and sufficient space in the string.
+ * Otherwise, the result is undefined.
+ *
+ * @param s const uint8_t * string buffer
+ * @param i string offset
+ * @param c code point to append
+ * @see U8_APPEND
+ * @stable ICU 2.4
+ */
+#define U8_APPEND_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \
+    uint32_t __uc=(c); \
+    if(__uc<=0x7f) { \
+        (s)[(i)++]=(uint8_t)__uc; \
+    } else { \
+        if(__uc<=0x7ff) { \
+            (s)[(i)++]=(uint8_t)((__uc>>6)|0xc0); \
+        } else { \
+            if(__uc<=0xffff) { \
+                (s)[(i)++]=(uint8_t)((__uc>>12)|0xe0); \
+            } else { \
+                (s)[(i)++]=(uint8_t)((__uc>>18)|0xf0); \
+                (s)[(i)++]=(uint8_t)(((__uc>>12)&0x3f)|0x80); \
+            } \
+            (s)[(i)++]=(uint8_t)(((__uc>>6)&0x3f)|0x80); \
+        } \
+        (s)[(i)++]=(uint8_t)((__uc&0x3f)|0x80); \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Append a code point to a string, overwriting 1 to 4 bytes.
+ * The offset points to the current end of the string contents
+ * and is advanced (post-increment).
+ * "Safe" macro, checks for a valid code point.
+ * If a non-ASCII code point is written, checks for sufficient space in the string.
+ * If the code point is not valid or trail bytes do not fit,
+ * then isError is set to true.
+ *
+ * @param s const uint8_t * string buffer
+ * @param i int32_t string offset, must be i>6)|0xc0); \
+        (s)[(i)++]=(uint8_t)((__uc&0x3f)|0x80); \
+    } else if((__uc<=0xd7ff || (0xe000<=__uc && __uc<=0xffff)) && (i)+2<(capacity)) { \
+        (s)[(i)++]=(uint8_t)((__uc>>12)|0xe0); \
+        (s)[(i)++]=(uint8_t)(((__uc>>6)&0x3f)|0x80); \
+        (s)[(i)++]=(uint8_t)((__uc&0x3f)|0x80); \
+    } else if(0xffff<__uc && __uc<=0x10ffff && (i)+3<(capacity)) { \
+        (s)[(i)++]=(uint8_t)((__uc>>18)|0xf0); \
+        (s)[(i)++]=(uint8_t)(((__uc>>12)&0x3f)|0x80); \
+        (s)[(i)++]=(uint8_t)(((__uc>>6)&0x3f)|0x80); \
+        (s)[(i)++]=(uint8_t)((__uc&0x3f)|0x80); \
+    } else { \
+        (isError)=true; \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Advance the string offset from one code point boundary to the next.
+ * (Post-incrementing iteration.)
+ * "Unsafe" macro, assumes well-formed UTF-8.
+ *
+ * @param s const uint8_t * string
+ * @param i string offset
+ * @see U8_FWD_1
+ * @stable ICU 2.4
+ */
+#define U8_FWD_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
+    (i)+=1+U8_COUNT_TRAIL_BYTES_UNSAFE((s)[i]); \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Advance the string offset from one code point boundary to the next.
+ * (Post-incrementing iteration.)
+ * "Safe" macro, checks for illegal sequences and for string boundaries.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * @param s const uint8_t * string
+ * @param i int32_t string offset, must be i=0xf0 */ { \
+            if(U8_IS_VALID_LEAD4_AND_T1(__b, __t1) && \
+                    ++(i)!=(length) && U8_IS_TRAIL((s)[i]) && \
+                    ++(i)!=(length) && U8_IS_TRAIL((s)[i])) { \
+                ++(i); \
+            } \
+        } \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Advance the string offset from one code point boundary to the n-th next one,
+ * i.e., move forward by n code points.
+ * (Post-incrementing iteration.)
+ * "Unsafe" macro, assumes well-formed UTF-8.
+ *
+ * @param s const uint8_t * string
+ * @param i string offset
+ * @param n number of code points to skip
+ * @see U8_FWD_N
+ * @stable ICU 2.4
+ */
+#define U8_FWD_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \
+    int32_t __N=(n); \
+    while(__N>0) { \
+        U8_FWD_1_UNSAFE(s, i); \
+        --__N; \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Advance the string offset from one code point boundary to the n-th next one,
+ * i.e., move forward by n code points.
+ * (Post-incrementing iteration.)
+ * "Safe" macro, checks for illegal sequences and for string boundaries.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * @param s const uint8_t * string
+ * @param i int32_t string offset, must be i0 && ((i)<(length) || ((length)<0 && (s)[i]!=0))) { \
+        U8_FWD_1(s, i, length); \
+        --__N; \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Adjust a random-access offset to a code point boundary
+ * at the start of a code point.
+ * If the offset points to a UTF-8 trail byte,
+ * then the offset is moved backward to the corresponding lead byte.
+ * Otherwise, it is not modified.
+ * "Unsafe" macro, assumes well-formed UTF-8.
+ *
+ * @param s const uint8_t * string
+ * @param i string offset
+ * @see U8_SET_CP_START
+ * @stable ICU 2.4
+ */
+#define U8_SET_CP_START_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
+    while(U8_IS_TRAIL((s)[i])) { --(i); } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Adjust a random-access offset to a code point boundary
+ * at the start of a code point.
+ * If the offset points to a UTF-8 trail byte,
+ * then the offset is moved backward to the corresponding lead byte.
+ * Otherwise, it is not modified.
+ *
+ * "Safe" macro, checks for illegal sequences and for string boundaries.
+ * Unlike U8_TRUNCATE_IF_INCOMPLETE(), this macro always reads s[i].
+ *
+ * @param s const uint8_t * string
+ * @param start int32_t starting string offset (usually 0)
+ * @param i int32_t string offset, must be start<=i
+ * @see U8_SET_CP_START_UNSAFE
+ * @see U8_TRUNCATE_IF_INCOMPLETE
+ * @stable ICU 2.4
+ */
+#define U8_SET_CP_START(s, start, i) UPRV_BLOCK_MACRO_BEGIN { \
+    if(U8_IS_TRAIL((s)[(i)])) { \
+        (i)=utf8_back1SafeBody(s, start, (i)); \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * If the string ends with a UTF-8 byte sequence that is valid so far
+ * but incomplete, then reduce the length of the string to end before
+ * the lead byte of that incomplete sequence.
+ * For example, if the string ends with E1 80, the length is reduced by 2.
+ *
+ * In all other cases (the string ends with a complete sequence, or it is not
+ * possible for any further trail byte to extend the trailing sequence)
+ * the length remains unchanged.
+ *
+ * Useful for processing text split across multiple buffers
+ * (save the incomplete sequence for later)
+ * and for optimizing iteration
+ * (check for string length only once per character).
+ *
+ * "Safe" macro, checks for illegal sequences and for string boundaries.
+ * Unlike U8_SET_CP_START(), this macro never reads s[length].
+ *
+ * (In UTF-16, simply check for U16_IS_LEAD(last code unit).)
+ *
+ * @param s const uint8_t * string
+ * @param start int32_t starting string offset (usually 0)
+ * @param length int32_t string length (usually start<=length)
+ * @see U8_SET_CP_START
+ * @stable ICU 61
+ */
+#define U8_TRUNCATE_IF_INCOMPLETE(s, start, length) UPRV_BLOCK_MACRO_BEGIN { \
+    if((length)>(start)) { \
+        uint8_t __b1=s[(length)-1]; \
+        if(U8_IS_SINGLE(__b1)) { \
+            /* common ASCII character */ \
+        } else if(U8_IS_LEAD(__b1)) { \
+            --(length); \
+        } else if(U8_IS_TRAIL(__b1) && ((length)-2)>=(start)) { \
+            uint8_t __b2=s[(length)-2]; \
+            if(0xe0<=__b2 && __b2<=0xf4) { \
+                if(__b2<0xf0 ? U8_IS_VALID_LEAD3_AND_T1(__b2, __b1) : \
+                        U8_IS_VALID_LEAD4_AND_T1(__b2, __b1)) { \
+                    (length)-=2; \
+                } \
+            } else if(U8_IS_TRAIL(__b2) && ((length)-3)>=(start)) { \
+                uint8_t __b3=s[(length)-3]; \
+                if(0xf0<=__b3 && __b3<=0xf4 && U8_IS_VALID_LEAD4_AND_T1(__b3, __b2)) { \
+                    (length)-=3; \
+                } \
+            } \
+        } \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/* definitions with backward iteration -------------------------------------- */
+
+/**
+ * Move the string offset from one code point boundary to the previous one
+ * and get the code point between them.
+ * (Pre-decrementing backward iteration.)
+ * "Unsafe" macro, assumes well-formed UTF-8.
+ *
+ * The input offset may be the same as the string length.
+ * If the offset is behind a multi-byte sequence, then the macro will read
+ * the whole sequence.
+ * If the offset is behind a lead byte, then that itself
+ * will be returned as the code point.
+ * The result is undefined if the offset is behind an illegal UTF-8 sequence.
+ *
+ * @param s const uint8_t * string
+ * @param i string offset
+ * @param c output UChar32 variable
+ * @see U8_PREV
+ * @stable ICU 2.4
+ */
+#define U8_PREV_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \
+    (c)=(uint8_t)(s)[--(i)]; \
+    if(U8_IS_TRAIL(c)) { \
+        uint8_t __b, __count=1, __shift=6; \
+\
+        /* c is a trail byte */ \
+        (c)&=0x3f; \
+        for(;;) { \
+            __b=(s)[--(i)]; \
+            if(__b>=0xc0) { \
+                U8_MASK_LEAD_BYTE(__b, __count); \
+                (c)|=(UChar32)__b<<__shift; \
+                break; \
+            } else { \
+                (c)|=(UChar32)(__b&0x3f)<<__shift; \
+                ++__count; \
+                __shift+=6; \
+            } \
+        } \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Move the string offset from one code point boundary to the previous one
+ * and get the code point between them.
+ * (Pre-decrementing backward iteration.)
+ * "Safe" macro, checks for illegal sequences and for string boundaries.
+ *
+ * The input offset may be the same as the string length.
+ * If the offset is behind a multi-byte sequence, then the macro will read
+ * the whole sequence.
+ * If the offset is behind a lead byte, then that itself
+ * will be returned as the code point.
+ * If the offset is behind an illegal UTF-8 sequence, then c is set to a negative value.
+ *
+ * @param s const uint8_t * string
+ * @param start int32_t starting string offset (usually 0)
+ * @param i int32_t string offset, must be start0) { \
+        U8_BACK_1_UNSAFE(s, i); \
+        --__N; \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Move the string offset from one code point boundary to the n-th one before it,
+ * i.e., move backward by n code points.
+ * (Pre-decrementing backward iteration.)
+ * The input offset may be the same as the string length.
+ * "Safe" macro, checks for illegal sequences and for string boundaries.
+ *
+ * @param s const uint8_t * string
+ * @param start int32_t index of the start of the string
+ * @param i int32_t string offset, must be start0 && (i)>(start)) { \
+        U8_BACK_1(s, start, i); \
+        --__N; \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Adjust a random-access offset to a code point boundary after a code point.
+ * If the offset is behind a partial multi-byte sequence,
+ * then the offset is incremented to behind the whole sequence.
+ * Otherwise, it is not modified.
+ * The input offset may be the same as the string length.
+ * "Unsafe" macro, assumes well-formed UTF-8.
+ *
+ * @param s const uint8_t * string
+ * @param i string offset
+ * @see U8_SET_CP_LIMIT
+ * @stable ICU 2.4
+ */
+#define U8_SET_CP_LIMIT_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \
+    U8_BACK_1_UNSAFE(s, i); \
+    U8_FWD_1_UNSAFE(s, i); \
+} UPRV_BLOCK_MACRO_END
+
+/**
+ * Adjust a random-access offset to a code point boundary after a code point.
+ * If the offset is behind a partial multi-byte sequence,
+ * then the offset is incremented to behind the whole sequence.
+ * Otherwise, it is not modified.
+ * The input offset may be the same as the string length.
+ * "Safe" macro, checks for illegal sequences and for string boundaries.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * @param s const uint8_t * string
+ * @param start int32_t starting string offset (usually 0)
+ * @param i int32_t string offset, must be start<=i<=length
+ * @param length int32_t string length
+ * @see U8_SET_CP_LIMIT_UNSAFE
+ * @stable ICU 2.4
+ */
+#define U8_SET_CP_LIMIT(s, start, i, length) UPRV_BLOCK_MACRO_BEGIN { \
+    if((start)<(i) && ((i)<(length) || (length)<0)) { \
+        U8_BACK_1(s, start, i); \
+        U8_FWD_1(s, i, length); \
+    } \
+} UPRV_BLOCK_MACRO_END
+
+#endif
diff --git a/deps/icu-small/source/common/unicode/utf_old.h b/deps/icu-small/source/common/unicode/utf_old.h
index 6b868c72809b18..61b8566415406b 100644
--- a/deps/icu-small/source/common/unicode/utf_old.h
+++ b/deps/icu-small/source/common/unicode/utf_old.h
@@ -1,1201 +1,1201 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-*******************************************************************************
-*
-*   Copyright (C) 2002-2012, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-*
-*******************************************************************************
-*   file name:  utf_old.h
-*   encoding:   UTF-8
-*   tab size:   8 (not used)
-*   indentation:4
-*
-*   created on: 2002sep21
-*   created by: Markus W. Scherer
-*/
-
-/**
- * \file
- * \brief C API: Deprecated macros for Unicode string handling
- *
- * The macros in utf_old.h are all deprecated and their use discouraged.
- * Some of the design principles behind the set of UTF macros
- * have changed or proved impractical.
- * Almost all of the old "UTF macros" are at least renamed.
- * If you are looking for a new equivalent to an old macro, please see the
- * comment at the old one.
- *
- * Brief summary of reasons for deprecation:
- * - Switch on UTF_SIZE (selection of UTF-8/16/32 default string processing)
- *   was impractical.
- * - Switch on UTF_SAFE etc. (selection of unsafe/safe/strict default string processing)
- *   was of little use and impractical.
- * - Whole classes of macros became obsolete outside of the UTF_SIZE/UTF_SAFE
- *   selection framework: UTF32_ macros (all trivial)
- *   and UTF_ default and intermediate macros (all aliases).
- * - The selection framework also caused many macro aliases.
- * - Change in Unicode standard: "irregular" sequences (3.0) became illegal (3.2).
- * - Change of language in Unicode standard:
- *   Growing distinction between internal x-bit Unicode strings and external UTF-x
- *   forms, with the former more lenient.
- *   Suggests renaming of UTF16_ macros to U16_.
- * - The prefix "UTF_" without a width number confused some users.
- * - "Safe" append macros needed the addition of an error indicator output.
- * - "Safe" UTF-8 macros used legitimate (if rarely used) code point values
- *   to indicate error conditions.
- * - The use of the "_CHAR" infix for code point operations confused some users.
- *
- * More details:
- *
- * Until ICU 2.2, utf.h theoretically allowed to choose among UTF-8/16/32
- * for string processing, and among unsafe/safe/strict default macros for that.
- *
- * It proved nearly impossible to write non-trivial, high-performance code
- * that is UTF-generic.
- * Unsafe default macros would be dangerous for default string processing,
- * and the main reason for the "strict" versions disappeared:
- * Between Unicode 3.0 and 3.2 all "irregular" UTF-8 sequences became illegal.
- * The only other conditions that "strict" checked for were non-characters,
- * which are valid during processing. Only during text input/output should they
- * be checked, and at that time other well-formedness checks may be
- * necessary or useful as well.
- * This can still be done by using U16_NEXT and U_IS_UNICODE_NONCHAR
- * or U_IS_UNICODE_CHAR.
- *
- * The old UTF8_..._SAFE macros also used some normal Unicode code points
- * to indicate malformed sequences.
- * The new UTF8_ macros without suffix use negative values instead.
- *
- * The entire contents of utf32.h was moved here without replacement
- * because all those macros were trivial and
- * were meaningful only in the framework of choosing the UTF size.
- *
- * See Jitterbug 2150 and its discussion on the ICU mailing list
- * in September 2002.
- *
- * 
- * - * Obsolete part of pre-ICU 2.4 utf.h file documentation: - * - *

The original concept for these files was for ICU to allow - * in principle to set which UTF (UTF-8/16/32) is used internally - * by defining UTF_SIZE to either 8, 16, or 32. utf.h would then define the UChar type - * accordingly. UTF-16 was the default.

- * - *

This concept has been abandoned. - * A lot of the ICU source code assumes UChar strings are in UTF-16. - * This is especially true for low-level code like - * conversion, normalization, and collation. - * The utf.h header enforces the default of UTF-16. - * The UTF-8 and UTF-32 macros remain for now for completeness and backward compatibility.

- * - *

Accordingly, utf.h defines UChar to be an unsigned 16-bit integer. If this matches wchar_t, then - * UChar is defined to be exactly wchar_t, otherwise uint16_t.

- * - *

UChar32 is defined to be a signed 32-bit integer (int32_t), large enough for a 21-bit - * Unicode code point (Unicode scalar value, 0..0x10ffff). - * Before ICU 2.4, the definition of UChar32 was similarly platform-dependent as - * the definition of UChar. For details see the documentation for UChar32 itself.

- * - *

utf.h also defines a number of C macros for handling single Unicode code points and - * for using UTF Unicode strings. It includes utf8.h, utf16.h, and utf32.h for the actual - * implementations of those macros and then aliases one set of them (for UTF-16) for general use. - * The UTF-specific macros have the UTF size in the macro name prefixes (UTF16_...), while - * the general alias macros always begin with UTF_...

- * - *

Many string operations can be done with or without error checking. - * Where such a distinction is useful, there are two versions of the macros, "unsafe" and "safe" - * ones with ..._UNSAFE and ..._SAFE suffixes. The unsafe macros are fast but may cause - * program failures if the strings are not well-formed. The safe macros have an additional, boolean - * parameter "strict". If strict is false, then only illegal sequences are detected. - * Otherwise, irregular sequences and non-characters are detected as well (like single surrogates). - * Safe macros return special error code points for illegal/irregular sequences: - * Typically, U+ffff, or values that would result in a code unit sequence of the same length - * as the erroneous input sequence.
- * Note that _UNSAFE macros have fewer parameters: They do not have the strictness parameter, and - * they do not have start/length parameters for boundary checking.

- * - *

Here, the macros are aliased in two steps: - * In the first step, the UTF-specific macros with UTF16_ prefix and _UNSAFE and _SAFE suffixes are - * aliased according to the UTF_SIZE to macros with UTF_ prefix and the same suffixes and signatures. - * Then, in a second step, the default, general alias macros are set to use either the unsafe or - * the safe/not strict (default) or the safe/strict macro; - * these general macros do not have a strictness parameter.

- * - *

It is possible to change the default choice for the general alias macros to be unsafe, safe/not strict or safe/strict. - * The default is safe/not strict. It is not recommended to select the unsafe macros as the basis for - * Unicode string handling in ICU! To select this, define UTF_SAFE, UTF_STRICT, or UTF_UNSAFE.

- * - *

For general use, one should use the default, general macros with UTF_ prefix and no _SAFE/_UNSAFE suffix. - * Only in some cases it may be necessary to control the choice of macro directly and use a less generic alias. - * For example, if it can be assumed that a string is well-formed and the index will stay within the bounds, - * then the _UNSAFE version may be used. - * If a UTF-8 string is to be processed, then the macros with UTF8_ prefixes need to be used.

- * - *
- * - * Deprecated ICU 2.4. Use the macros in utf.h, utf16.h, utf8.h instead. - */ - -#ifndef __UTF_OLD_H__ -#define __UTF_OLD_H__ - -#include "unicode/utf.h" -#include "unicode/utf8.h" -#include "unicode/utf16.h" - -/** - * \def U_HIDE_OBSOLETE_UTF_OLD_H - * - * Hides the obsolete definitions in unicode/utf_old.h. - * Recommended to be set to 1 at compile time to make sure - * the long-deprecated macros are no longer used. - * - * For reasons for the deprecation see the utf_old.h file comments. - * - * @internal - */ -#ifndef U_HIDE_OBSOLETE_UTF_OLD_H -# define U_HIDE_OBSOLETE_UTF_OLD_H 0 -#endif - -#if !defined(U_HIDE_DEPRECATED_API) && !U_HIDE_OBSOLETE_UTF_OLD_H - -/* Formerly utf.h, part 1 --------------------------------------------------- */ - -#ifdef U_USE_UTF_DEPRECATES -/** - * Unicode string and array offset and index type. - * ICU always counts Unicode code units (UChars) for - * string offsets, indexes, and lengths, not Unicode code points. - * - * @obsolete ICU 2.6. Use int32_t directly instead since this API will be removed in that release. - */ -typedef int32_t UTextOffset; -#endif - -/** Number of bits in a Unicode string code unit - ICU uses 16-bit Unicode. @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF_SIZE 16 - -/** - * The default choice for general Unicode string macros is to use the ..._SAFE macro implementations - * with strict=false. - * - * @deprecated ICU 2.4. Obsolete, see utf_old.h. - */ -#define UTF_SAFE -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#undef UTF_UNSAFE -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#undef UTF_STRICT - -/** - * UTF8_ERROR_VALUE_1 and UTF8_ERROR_VALUE_2 are special error values for UTF-8, - * which need 1 or 2 bytes in UTF-8: - * \code - * U+0015 = NAK = Negative Acknowledge, C0 control character - * U+009f = highest C1 control character - * \endcode - * - * These are used by UTF8_..._SAFE macros so that they can return an error value - * that needs the same number of code units (bytes) as were seen by - * a macro. They should be tested with UTF_IS_ERROR() or UTF_IS_VALID(). - * - * @deprecated ICU 2.4. Obsolete, see utf_old.h. - */ -#define UTF8_ERROR_VALUE_1 0x15 - -/** - * See documentation on UTF8_ERROR_VALUE_1 for details. - * - * @deprecated ICU 2.4. Obsolete, see utf_old.h. - */ -#define UTF8_ERROR_VALUE_2 0x9f - -/** - * Error value for all UTFs. This code point value will be set by macros with error - * checking if an error is detected. - * - * @deprecated ICU 2.4. Obsolete, see utf_old.h. - */ -#define UTF_ERROR_VALUE 0xffff - -/** - * Is a given 32-bit code an error value - * as returned by one of the macros for any UTF? - * - * @deprecated ICU 2.4. Obsolete, see utf_old.h. - */ -#define UTF_IS_ERROR(c) \ - (((c)&0xfffe)==0xfffe || (c)==UTF8_ERROR_VALUE_1 || (c)==UTF8_ERROR_VALUE_2) - -/** - * This is a combined macro: Is c a valid Unicode value _and_ not an error code? - * - * @deprecated ICU 2.4. Obsolete, see utf_old.h. - */ -#define UTF_IS_VALID(c) \ - (UTF_IS_UNICODE_CHAR(c) && \ - (c)!=UTF8_ERROR_VALUE_1 && (c)!=UTF8_ERROR_VALUE_2) - -/** - * Is this code unit or code point a surrogate (U+d800..U+dfff)? - * @deprecated ICU 2.4. Renamed to U_IS_SURROGATE and U16_IS_SURROGATE, see utf_old.h. - */ -#define UTF_IS_SURROGATE(uchar) (((uchar)&0xfffff800)==0xd800) - -/** - * Is a given 32-bit code point a Unicode noncharacter? - * - * @deprecated ICU 2.4. Renamed to U_IS_UNICODE_NONCHAR, see utf_old.h. - */ -#define UTF_IS_UNICODE_NONCHAR(c) \ - ((c)>=0xfdd0 && \ - ((uint32_t)(c)<=0xfdef || ((c)&0xfffe)==0xfffe) && \ - (uint32_t)(c)<=0x10ffff) - -/** - * Is a given 32-bit value a Unicode code point value (0..U+10ffff) - * that can be assigned a character? - * - * Code points that are not characters include: - * - single surrogate code points (U+d800..U+dfff, 2048 code points) - * - the last two code points on each plane (U+__fffe and U+__ffff, 34 code points) - * - U+fdd0..U+fdef (new with Unicode 3.1, 32 code points) - * - the highest Unicode code point value is U+10ffff - * - * This means that all code points below U+d800 are character code points, - * and that boundary is tested first for performance. - * - * @deprecated ICU 2.4. Renamed to U_IS_UNICODE_CHAR, see utf_old.h. - */ -#define UTF_IS_UNICODE_CHAR(c) \ - ((uint32_t)(c)<0xd800 || \ - ((uint32_t)(c)>0xdfff && \ - (uint32_t)(c)<=0x10ffff && \ - !UTF_IS_UNICODE_NONCHAR(c))) - -/* Formerly utf8.h ---------------------------------------------------------- */ - -/** -* \var utf8_countTrailBytes -* Internal array with numbers of trail bytes for any given byte used in -* lead byte position. -* -* This is internal since it is not meant to be called directly by external clients; -* however it is called by public macros in this file and thus must remain stable, -* and should not be hidden when other internal functions are hidden (otherwise -* public macros would fail to compile). -* @internal -*/ -#ifdef U_UTF8_IMPL -// No forward declaration if compiling utf_impl.cpp, which defines utf8_countTrailBytes. -#elif defined(U_STATIC_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) -U_CAPI const uint8_t utf8_countTrailBytes[]; -#else -U_CFUNC U_IMPORT const uint8_t utf8_countTrailBytes[]; -#endif - -/** - * Count the trail bytes for a UTF-8 lead byte. - * @deprecated ICU 2.4. Renamed to U8_COUNT_TRAIL_BYTES, see utf_old.h. - */ -#define UTF8_COUNT_TRAIL_BYTES(leadByte) (utf8_countTrailBytes[(uint8_t)leadByte]) - -/** - * Mask a UTF-8 lead byte, leave only the lower bits that form part of the code point value. - * @deprecated ICU 2.4. Renamed to U8_MASK_LEAD_BYTE, see utf_old.h. - */ -#define UTF8_MASK_LEAD_BYTE(leadByte, countTrailBytes) ((leadByte)&=(1<<(6-(countTrailBytes)))-1) - -/** Is this this code point a single code unit (byte)? @deprecated ICU 2.4. Renamed to U8_IS_SINGLE, see utf_old.h. */ -#define UTF8_IS_SINGLE(uchar) (((uchar)&0x80)==0) -/** Is this this code unit the lead code unit (byte) of a code point? @deprecated ICU 2.4. Renamed to U8_IS_LEAD, see utf_old.h. */ -#define UTF8_IS_LEAD(uchar) ((uint8_t)((uchar)-0xc0)<0x3e) -/** Is this this code unit a trailing code unit (byte) of a code point? @deprecated ICU 2.4. Renamed to U8_IS_TRAIL, see utf_old.h. */ -#define UTF8_IS_TRAIL(uchar) (((uchar)&0xc0)==0x80) - -/** Does this scalar Unicode value need multiple code units for storage? @deprecated ICU 2.4. Use U8_LENGTH or test ((uint32_t)(c)>0x7f) instead, see utf_old.h. */ -#define UTF8_NEED_MULTIPLE_UCHAR(c) ((uint32_t)(c)>0x7f) - -/** - * Given the lead character, how many bytes are taken by this code point. - * ICU does not deal with code points >0x10ffff - * unless necessary for advancing in the byte stream. - * - * These length macros take into account that for values >0x10ffff - * the UTF8_APPEND_CHAR_SAFE macros would write the error code point 0xffff - * with 3 bytes. - * Code point comparisons need to be in uint32_t because UChar32 - * may be a signed type, and negative values must be recognized. - * - * @deprecated ICU 2.4. Use U8_LENGTH instead, see utf.h. - */ -#if 1 -# define UTF8_CHAR_LENGTH(c) \ - ((uint32_t)(c)<=0x7f ? 1 : \ - ((uint32_t)(c)<=0x7ff ? 2 : \ - ((uint32_t)((c)-0x10000)>0xfffff ? 3 : 4) \ - ) \ - ) -#else -# define UTF8_CHAR_LENGTH(c) \ - ((uint32_t)(c)<=0x7f ? 1 : \ - ((uint32_t)(c)<=0x7ff ? 2 : \ - ((uint32_t)(c)<=0xffff ? 3 : \ - ((uint32_t)(c)<=0x10ffff ? 4 : \ - ((uint32_t)(c)<=0x3ffffff ? 5 : \ - ((uint32_t)(c)<=0x7fffffff ? 6 : 3) \ - ) \ - ) \ - ) \ - ) \ - ) -#endif - -/** The maximum number of bytes per code point. @deprecated ICU 2.4. Renamed to U8_MAX_LENGTH, see utf_old.h. */ -#define UTF8_MAX_CHAR_LENGTH 4 - -/** Average number of code units compared to UTF-16. @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF8_ARRAY_SIZE(size) ((5*(size))/2) - -/** @deprecated ICU 2.4. Renamed to U8_GET_UNSAFE, see utf_old.h. */ -#define UTF8_GET_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ - int32_t _utf8_get_char_unsafe_index=(int32_t)(i); \ - UTF8_SET_CHAR_START_UNSAFE(s, _utf8_get_char_unsafe_index); \ - UTF8_NEXT_CHAR_UNSAFE(s, _utf8_get_char_unsafe_index, c); \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Use U8_GET instead, see utf_old.h. */ -#define UTF8_GET_CHAR_SAFE(s, start, i, length, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ - int32_t _utf8_get_char_safe_index=(int32_t)(i); \ - UTF8_SET_CHAR_START_SAFE(s, start, _utf8_get_char_safe_index); \ - UTF8_NEXT_CHAR_SAFE(s, _utf8_get_char_safe_index, length, c, strict); \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U8_NEXT_UNSAFE, see utf_old.h. */ -#define UTF8_NEXT_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[(i)++]; \ - if((uint8_t)((c)-0xc0)<0x35) { \ - uint8_t __count=UTF8_COUNT_TRAIL_BYTES(c); \ - UTF8_MASK_LEAD_BYTE(c, __count); \ - switch(__count) { \ - /* each following branch falls through to the next one */ \ - case 3: \ - (c)=((c)<<6)|((s)[(i)++]&0x3f); \ - case 2: \ - (c)=((c)<<6)|((s)[(i)++]&0x3f); \ - case 1: \ - (c)=((c)<<6)|((s)[(i)++]&0x3f); \ - /* no other branches to optimize switch() */ \ - break; \ - } \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U8_APPEND_UNSAFE, see utf_old.h. */ -#define UTF8_APPEND_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ - if((uint32_t)(c)<=0x7f) { \ - (s)[(i)++]=(uint8_t)(c); \ - } else { \ - if((uint32_t)(c)<=0x7ff) { \ - (s)[(i)++]=(uint8_t)(((c)>>6)|0xc0); \ - } else { \ - if((uint32_t)(c)<=0xffff) { \ - (s)[(i)++]=(uint8_t)(((c)>>12)|0xe0); \ - } else { \ - (s)[(i)++]=(uint8_t)(((c)>>18)|0xf0); \ - (s)[(i)++]=(uint8_t)((((c)>>12)&0x3f)|0x80); \ - } \ - (s)[(i)++]=(uint8_t)((((c)>>6)&0x3f)|0x80); \ - } \ - (s)[(i)++]=(uint8_t)(((c)&0x3f)|0x80); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U8_FWD_1_UNSAFE, see utf_old.h. */ -#define UTF8_FWD_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ - (i)+=1+UTF8_COUNT_TRAIL_BYTES((s)[i]); \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U8_FWD_N_UNSAFE, see utf_old.h. */ -#define UTF8_FWD_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \ - int32_t __N=(n); \ - while(__N>0) { \ - UTF8_FWD_1_UNSAFE(s, i); \ - --__N; \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U8_SET_CP_START_UNSAFE, see utf_old.h. */ -#define UTF8_SET_CHAR_START_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ - while(UTF8_IS_TRAIL((s)[i])) { --(i); } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Use U8_NEXT instead, see utf_old.h. */ -#define UTF8_NEXT_CHAR_SAFE(s, i, length, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[(i)++]; \ - if((c)>=0x80) { \ - if(UTF8_IS_LEAD(c)) { \ - (c)=utf8_nextCharSafeBody(s, &(i), (int32_t)(length), c, strict); \ - } else { \ - (c)=UTF8_ERROR_VALUE_1; \ - } \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Use U8_APPEND instead, see utf_old.h. */ -#define UTF8_APPEND_CHAR_SAFE(s, i, length, c) UPRV_BLOCK_MACRO_BEGIN { \ - if((uint32_t)(c)<=0x7f) { \ - (s)[(i)++]=(uint8_t)(c); \ - } else { \ - (i)=utf8_appendCharSafeBody(s, (int32_t)(i), (int32_t)(length), c, NULL); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U8_FWD_1, see utf_old.h. */ -#define UTF8_FWD_1_SAFE(s, i, length) U8_FWD_1(s, i, length) - -/** @deprecated ICU 2.4. Renamed to U8_FWD_N, see utf_old.h. */ -#define UTF8_FWD_N_SAFE(s, i, length, n) U8_FWD_N(s, i, length, n) - -/** @deprecated ICU 2.4. Renamed to U8_SET_CP_START, see utf_old.h. */ -#define UTF8_SET_CHAR_START_SAFE(s, start, i) U8_SET_CP_START(s, start, i) - -/** @deprecated ICU 2.4. Renamed to U8_PREV_UNSAFE, see utf_old.h. */ -#define UTF8_PREV_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[--(i)]; \ - if(UTF8_IS_TRAIL(c)) { \ - uint8_t __b, __count=1, __shift=6; \ -\ - /* c is a trail byte */ \ - (c)&=0x3f; \ - for(;;) { \ - __b=(s)[--(i)]; \ - if(__b>=0xc0) { \ - UTF8_MASK_LEAD_BYTE(__b, __count); \ - (c)|=(UChar32)__b<<__shift; \ - break; \ - } else { \ - (c)|=(UChar32)(__b&0x3f)<<__shift; \ - ++__count; \ - __shift+=6; \ - } \ - } \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U8_BACK_1_UNSAFE, see utf_old.h. */ -#define UTF8_BACK_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ - while(UTF8_IS_TRAIL((s)[--(i)])) {} \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U8_BACK_N_UNSAFE, see utf_old.h. */ -#define UTF8_BACK_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \ - int32_t __N=(n); \ - while(__N>0) { \ - UTF8_BACK_1_UNSAFE(s, i); \ - --__N; \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U8_SET_CP_LIMIT_UNSAFE, see utf_old.h. */ -#define UTF8_SET_CHAR_LIMIT_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ - UTF8_BACK_1_UNSAFE(s, i); \ - UTF8_FWD_1_UNSAFE(s, i); \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Use U8_PREV instead, see utf_old.h. */ -#define UTF8_PREV_CHAR_SAFE(s, start, i, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[--(i)]; \ - if((c)>=0x80) { \ - if((c)<=0xbf) { \ - (c)=utf8_prevCharSafeBody(s, start, &(i), c, strict); \ - } else { \ - (c)=UTF8_ERROR_VALUE_1; \ - } \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U8_BACK_1, see utf_old.h. */ -#define UTF8_BACK_1_SAFE(s, start, i) U8_BACK_1(s, start, i) - -/** @deprecated ICU 2.4. Renamed to U8_BACK_N, see utf_old.h. */ -#define UTF8_BACK_N_SAFE(s, start, i, n) U8_BACK_N(s, start, i, n) - -/** @deprecated ICU 2.4. Renamed to U8_SET_CP_LIMIT, see utf_old.h. */ -#define UTF8_SET_CHAR_LIMIT_SAFE(s, start, i, length) U8_SET_CP_LIMIT(s, start, i, length) - -/* Formerly utf16.h --------------------------------------------------------- */ - -/** Is uchar a first/lead surrogate? @deprecated ICU 2.4. Renamed to U_IS_LEAD and U16_IS_LEAD, see utf_old.h. */ -#define UTF_IS_FIRST_SURROGATE(uchar) (((uchar)&0xfffffc00)==0xd800) - -/** Is uchar a second/trail surrogate? @deprecated ICU 2.4. Renamed to U_IS_TRAIL and U16_IS_TRAIL, see utf_old.h. */ -#define UTF_IS_SECOND_SURROGATE(uchar) (((uchar)&0xfffffc00)==0xdc00) - -/** Assuming c is a surrogate, is it a first/lead surrogate? @deprecated ICU 2.4. Renamed to U_IS_SURROGATE_LEAD and U16_IS_SURROGATE_LEAD, see utf_old.h. */ -#define UTF_IS_SURROGATE_FIRST(c) (((c)&0x400)==0) - -/** Helper constant for UTF16_GET_PAIR_VALUE. @deprecated ICU 2.4. Renamed to U16_SURROGATE_OFFSET, see utf_old.h. */ -#define UTF_SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000) - -/** Get the UTF-32 value from the surrogate code units. @deprecated ICU 2.4. Renamed to U16_GET_SUPPLEMENTARY, see utf_old.h. */ -#define UTF16_GET_PAIR_VALUE(first, second) \ - (((first)<<10UL)+(second)-UTF_SURROGATE_OFFSET) - -/** @deprecated ICU 2.4. Renamed to U16_LEAD, see utf_old.h. */ -#define UTF_FIRST_SURROGATE(supplementary) (UChar)(((supplementary)>>10)+0xd7c0) - -/** @deprecated ICU 2.4. Renamed to U16_TRAIL, see utf_old.h. */ -#define UTF_SECOND_SURROGATE(supplementary) (UChar)(((supplementary)&0x3ff)|0xdc00) - -/** @deprecated ICU 2.4. Renamed to U16_LEAD, see utf_old.h. */ -#define UTF16_LEAD(supplementary) UTF_FIRST_SURROGATE(supplementary) - -/** @deprecated ICU 2.4. Renamed to U16_TRAIL, see utf_old.h. */ -#define UTF16_TRAIL(supplementary) UTF_SECOND_SURROGATE(supplementary) - -/** @deprecated ICU 2.4. Renamed to U16_IS_SINGLE, see utf_old.h. */ -#define UTF16_IS_SINGLE(uchar) !UTF_IS_SURROGATE(uchar) - -/** @deprecated ICU 2.4. Renamed to U16_IS_LEAD, see utf_old.h. */ -#define UTF16_IS_LEAD(uchar) UTF_IS_FIRST_SURROGATE(uchar) - -/** @deprecated ICU 2.4. Renamed to U16_IS_TRAIL, see utf_old.h. */ -#define UTF16_IS_TRAIL(uchar) UTF_IS_SECOND_SURROGATE(uchar) - -/** Does this scalar Unicode value need multiple code units for storage? @deprecated ICU 2.4. Use U16_LENGTH or test ((uint32_t)(c)>0xffff) instead, see utf_old.h. */ -#define UTF16_NEED_MULTIPLE_UCHAR(c) ((uint32_t)(c)>0xffff) - -/** @deprecated ICU 2.4. Renamed to U16_LENGTH, see utf_old.h. */ -#define UTF16_CHAR_LENGTH(c) ((uint32_t)(c)<=0xffff ? 1 : 2) - -/** @deprecated ICU 2.4. Renamed to U16_MAX_LENGTH, see utf_old.h. */ -#define UTF16_MAX_CHAR_LENGTH 2 - -/** Average number of code units compared to UTF-16. @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF16_ARRAY_SIZE(size) (size) - -/** - * Get a single code point from an offset that points to any - * of the code units that belong to that code point. - * Assume 0<=i=(start) && UTF_IS_FIRST_SURROGATE(__c2=(s)[(i)-1])) { \ - (c)=UTF16_GET_PAIR_VALUE(__c2, (c)); \ - /* strict: ((c)&0xfffe)==0xfffe is caught by UTF_IS_ERROR() and UTF_IS_UNICODE_CHAR() */ \ - } else if(strict) {\ - /* unmatched second surrogate */ \ - (c)=UTF_ERROR_VALUE; \ - } \ - } \ - } else if((strict) && !UTF_IS_UNICODE_CHAR(c)) { \ - (c)=UTF_ERROR_VALUE; \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U16_NEXT_UNSAFE, see utf_old.h. */ -#define UTF16_NEXT_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[(i)++]; \ - if(UTF_IS_FIRST_SURROGATE(c)) { \ - (c)=UTF16_GET_PAIR_VALUE((c), (s)[(i)++]); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U16_APPEND_UNSAFE, see utf_old.h. */ -#define UTF16_APPEND_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ - if((uint32_t)(c)<=0xffff) { \ - (s)[(i)++]=(uint16_t)(c); \ - } else { \ - (s)[(i)++]=(uint16_t)(((c)>>10)+0xd7c0); \ - (s)[(i)++]=(uint16_t)(((c)&0x3ff)|0xdc00); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U16_FWD_1_UNSAFE, see utf_old.h. */ -#define UTF16_FWD_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTF_IS_FIRST_SURROGATE((s)[(i)++])) { \ - ++(i); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U16_FWD_N_UNSAFE, see utf_old.h. */ -#define UTF16_FWD_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \ - int32_t __N=(n); \ - while(__N>0) { \ - UTF16_FWD_1_UNSAFE(s, i); \ - --__N; \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U16_SET_CP_START_UNSAFE, see utf_old.h. */ -#define UTF16_SET_CHAR_START_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTF_IS_SECOND_SURROGATE((s)[i])) { \ - --(i); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Use U16_NEXT instead, see utf_old.h. */ -#define UTF16_NEXT_CHAR_SAFE(s, i, length, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[(i)++]; \ - if(UTF_IS_FIRST_SURROGATE(c)) { \ - uint16_t __c2; \ - if((i)<(length) && UTF_IS_SECOND_SURROGATE(__c2=(s)[(i)])) { \ - ++(i); \ - (c)=UTF16_GET_PAIR_VALUE((c), __c2); \ - /* strict: ((c)&0xfffe)==0xfffe is caught by UTF_IS_ERROR() and UTF_IS_UNICODE_CHAR() */ \ - } else if(strict) {\ - /* unmatched first surrogate */ \ - (c)=UTF_ERROR_VALUE; \ - } \ - } else if((strict) && !UTF_IS_UNICODE_CHAR(c)) { \ - /* unmatched second surrogate or other non-character */ \ - (c)=UTF_ERROR_VALUE; \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Use U16_APPEND instead, see utf_old.h. */ -#define UTF16_APPEND_CHAR_SAFE(s, i, length, c) UPRV_BLOCK_MACRO_BEGIN { \ - if((uint32_t)(c)<=0xffff) { \ - (s)[(i)++]=(uint16_t)(c); \ - } else if((uint32_t)(c)<=0x10ffff) { \ - if((i)+1<(length)) { \ - (s)[(i)++]=(uint16_t)(((c)>>10)+0xd7c0); \ - (s)[(i)++]=(uint16_t)(((c)&0x3ff)|0xdc00); \ - } else /* not enough space */ { \ - (s)[(i)++]=UTF_ERROR_VALUE; \ - } \ - } else /* c>0x10ffff, write error value */ { \ - (s)[(i)++]=UTF_ERROR_VALUE; \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U16_FWD_1, see utf_old.h. */ -#define UTF16_FWD_1_SAFE(s, i, length) U16_FWD_1(s, i, length) - -/** @deprecated ICU 2.4. Renamed to U16_FWD_N, see utf_old.h. */ -#define UTF16_FWD_N_SAFE(s, i, length, n) U16_FWD_N(s, i, length, n) - -/** @deprecated ICU 2.4. Renamed to U16_SET_CP_START, see utf_old.h. */ -#define UTF16_SET_CHAR_START_SAFE(s, start, i) U16_SET_CP_START(s, start, i) - -/** @deprecated ICU 2.4. Renamed to U16_PREV_UNSAFE, see utf_old.h. */ -#define UTF16_PREV_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[--(i)]; \ - if(UTF_IS_SECOND_SURROGATE(c)) { \ - (c)=UTF16_GET_PAIR_VALUE((s)[--(i)], (c)); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U16_BACK_1_UNSAFE, see utf_old.h. */ -#define UTF16_BACK_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTF_IS_SECOND_SURROGATE((s)[--(i)])) { \ - --(i); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U16_BACK_N_UNSAFE, see utf_old.h. */ -#define UTF16_BACK_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \ - int32_t __N=(n); \ - while(__N>0) { \ - UTF16_BACK_1_UNSAFE(s, i); \ - --__N; \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U16_SET_CP_LIMIT_UNSAFE, see utf_old.h. */ -#define UTF16_SET_CHAR_LIMIT_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTF_IS_FIRST_SURROGATE((s)[(i)-1])) { \ - ++(i); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Use U16_PREV instead, see utf_old.h. */ -#define UTF16_PREV_CHAR_SAFE(s, start, i, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[--(i)]; \ - if(UTF_IS_SECOND_SURROGATE(c)) { \ - uint16_t __c2; \ - if((i)>(start) && UTF_IS_FIRST_SURROGATE(__c2=(s)[(i)-1])) { \ - --(i); \ - (c)=UTF16_GET_PAIR_VALUE(__c2, (c)); \ - /* strict: ((c)&0xfffe)==0xfffe is caught by UTF_IS_ERROR() and UTF_IS_UNICODE_CHAR() */ \ - } else if(strict) {\ - /* unmatched second surrogate */ \ - (c)=UTF_ERROR_VALUE; \ - } \ - } else if((strict) && !UTF_IS_UNICODE_CHAR(c)) { \ - /* unmatched first surrogate or other non-character */ \ - (c)=UTF_ERROR_VALUE; \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Renamed to U16_BACK_1, see utf_old.h. */ -#define UTF16_BACK_1_SAFE(s, start, i) U16_BACK_1(s, start, i) - -/** @deprecated ICU 2.4. Renamed to U16_BACK_N, see utf_old.h. */ -#define UTF16_BACK_N_SAFE(s, start, i, n) U16_BACK_N(s, start, i, n) - -/** @deprecated ICU 2.4. Renamed to U16_SET_CP_LIMIT, see utf_old.h. */ -#define UTF16_SET_CHAR_LIMIT_SAFE(s, start, i, length) U16_SET_CP_LIMIT(s, start, i, length) - -/* Formerly utf32.h --------------------------------------------------------- */ - -/* -* Old documentation: -* -* This file defines macros to deal with UTF-32 code units and code points. -* Signatures and semantics are the same as for the similarly named macros -* in utf16.h. -* utf32.h is included by utf.h after unicode/umachine.h

-* and some common definitions. -*

Usage: ICU coding guidelines for if() statements should be followed when using these macros. -* Compound statements (curly braces {}) must be used for if-else-while... -* bodies and all macro statements should be terminated with semicolon.

-*/ - -/* internal definitions ----------------------------------------------------- */ - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_IS_SAFE(c, strict) \ - (!(strict) ? \ - (uint32_t)(c)<=0x10ffff : \ - UTF_IS_UNICODE_CHAR(c)) - -/* - * For the semantics of all of these macros, see utf16.h. - * The UTF-32 versions are trivial because any code point is - * encoded using exactly one code unit. - */ - -/* single-code point definitions -------------------------------------------- */ - -/* classes of code unit values */ - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_IS_SINGLE(uchar) 1 -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_IS_LEAD(uchar) 0 -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_IS_TRAIL(uchar) 0 - -/* number of code units per code point */ - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_NEED_MULTIPLE_UCHAR(c) 0 -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_CHAR_LENGTH(c) 1 -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_MAX_CHAR_LENGTH 1 - -/* average number of code units compared to UTF-16 */ - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_ARRAY_SIZE(size) (size) - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_GET_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[i]; \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_GET_CHAR_SAFE(s, start, i, length, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[i]; \ - if(!UTF32_IS_SAFE(c, strict)) { \ - (c)=UTF_ERROR_VALUE; \ - } \ -} UPRV_BLOCK_MACRO_END - -/* definitions with forward iteration --------------------------------------- */ - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_NEXT_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[(i)++]; \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_APPEND_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ - (s)[(i)++]=(c); \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_FWD_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ - ++(i); \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_FWD_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \ - (i)+=(n); \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_SET_CHAR_START_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_NEXT_CHAR_SAFE(s, i, length, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[(i)++]; \ - if(!UTF32_IS_SAFE(c, strict)) { \ - (c)=UTF_ERROR_VALUE; \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_APPEND_CHAR_SAFE(s, i, length, c) UPRV_BLOCK_MACRO_BEGIN { \ - if((uint32_t)(c)<=0x10ffff) { \ - (s)[(i)++]=(c); \ - } else /* c>0x10ffff, write 0xfffd */ { \ - (s)[(i)++]=0xfffd; \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_FWD_1_SAFE(s, i, length) UPRV_BLOCK_MACRO_BEGIN { \ - ++(i); \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_FWD_N_SAFE(s, i, length, n) UPRV_BLOCK_MACRO_BEGIN { \ - if(((i)+=(n))>(length)) { \ - (i)=(length); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_SET_CHAR_START_SAFE(s, start, i) UPRV_BLOCK_MACRO_BEGIN { \ -} UPRV_BLOCK_MACRO_END - -/* definitions with backward iteration -------------------------------------- */ - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_PREV_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[--(i)]; \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_BACK_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ - --(i); \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_BACK_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \ - (i)-=(n); \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_SET_CHAR_LIMIT_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_PREV_CHAR_SAFE(s, start, i, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=(s)[--(i)]; \ - if(!UTF32_IS_SAFE(c, strict)) { \ - (c)=UTF_ERROR_VALUE; \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_BACK_1_SAFE(s, start, i) UPRV_BLOCK_MACRO_BEGIN { \ - --(i); \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_BACK_N_SAFE(s, start, i, n) UPRV_BLOCK_MACRO_BEGIN { \ - (i)-=(n); \ - if((i)<(start)) { \ - (i)=(start); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ -#define UTF32_SET_CHAR_LIMIT_SAFE(s, i, length) UPRV_BLOCK_MACRO_BEGIN { \ -} UPRV_BLOCK_MACRO_END - -/* Formerly utf.h, part 2 --------------------------------------------------- */ - -/** - * Estimate the number of code units for a string based on the number of UTF-16 code units. - * - * @deprecated ICU 2.4. Obsolete, see utf_old.h. - */ -#define UTF_ARRAY_SIZE(size) UTF16_ARRAY_SIZE(size) - -/** @deprecated ICU 2.4. Renamed to U16_GET_UNSAFE, see utf_old.h. */ -#define UTF_GET_CHAR_UNSAFE(s, i, c) UTF16_GET_CHAR_UNSAFE(s, i, c) - -/** @deprecated ICU 2.4. Use U16_GET instead, see utf_old.h. */ -#define UTF_GET_CHAR_SAFE(s, start, i, length, c, strict) UTF16_GET_CHAR_SAFE(s, start, i, length, c, strict) - - -/** @deprecated ICU 2.4. Renamed to U16_NEXT_UNSAFE, see utf_old.h. */ -#define UTF_NEXT_CHAR_UNSAFE(s, i, c) UTF16_NEXT_CHAR_UNSAFE(s, i, c) - -/** @deprecated ICU 2.4. Use U16_NEXT instead, see utf_old.h. */ -#define UTF_NEXT_CHAR_SAFE(s, i, length, c, strict) UTF16_NEXT_CHAR_SAFE(s, i, length, c, strict) - - -/** @deprecated ICU 2.4. Renamed to U16_APPEND_UNSAFE, see utf_old.h. */ -#define UTF_APPEND_CHAR_UNSAFE(s, i, c) UTF16_APPEND_CHAR_UNSAFE(s, i, c) - -/** @deprecated ICU 2.4. Use U16_APPEND instead, see utf_old.h. */ -#define UTF_APPEND_CHAR_SAFE(s, i, length, c) UTF16_APPEND_CHAR_SAFE(s, i, length, c) - - -/** @deprecated ICU 2.4. Renamed to U16_FWD_1_UNSAFE, see utf_old.h. */ -#define UTF_FWD_1_UNSAFE(s, i) UTF16_FWD_1_UNSAFE(s, i) - -/** @deprecated ICU 2.4. Renamed to U16_FWD_1, see utf_old.h. */ -#define UTF_FWD_1_SAFE(s, i, length) UTF16_FWD_1_SAFE(s, i, length) - - -/** @deprecated ICU 2.4. Renamed to U16_FWD_N_UNSAFE, see utf_old.h. */ -#define UTF_FWD_N_UNSAFE(s, i, n) UTF16_FWD_N_UNSAFE(s, i, n) - -/** @deprecated ICU 2.4. Renamed to U16_FWD_N, see utf_old.h. */ -#define UTF_FWD_N_SAFE(s, i, length, n) UTF16_FWD_N_SAFE(s, i, length, n) - - -/** @deprecated ICU 2.4. Renamed to U16_SET_CP_START_UNSAFE, see utf_old.h. */ -#define UTF_SET_CHAR_START_UNSAFE(s, i) UTF16_SET_CHAR_START_UNSAFE(s, i) - -/** @deprecated ICU 2.4. Renamed to U16_SET_CP_START, see utf_old.h. */ -#define UTF_SET_CHAR_START_SAFE(s, start, i) UTF16_SET_CHAR_START_SAFE(s, start, i) - - -/** @deprecated ICU 2.4. Renamed to U16_PREV_UNSAFE, see utf_old.h. */ -#define UTF_PREV_CHAR_UNSAFE(s, i, c) UTF16_PREV_CHAR_UNSAFE(s, i, c) - -/** @deprecated ICU 2.4. Use U16_PREV instead, see utf_old.h. */ -#define UTF_PREV_CHAR_SAFE(s, start, i, c, strict) UTF16_PREV_CHAR_SAFE(s, start, i, c, strict) - - -/** @deprecated ICU 2.4. Renamed to U16_BACK_1_UNSAFE, see utf_old.h. */ -#define UTF_BACK_1_UNSAFE(s, i) UTF16_BACK_1_UNSAFE(s, i) - -/** @deprecated ICU 2.4. Renamed to U16_BACK_1, see utf_old.h. */ -#define UTF_BACK_1_SAFE(s, start, i) UTF16_BACK_1_SAFE(s, start, i) - - -/** @deprecated ICU 2.4. Renamed to U16_BACK_N_UNSAFE, see utf_old.h. */ -#define UTF_BACK_N_UNSAFE(s, i, n) UTF16_BACK_N_UNSAFE(s, i, n) - -/** @deprecated ICU 2.4. Renamed to U16_BACK_N, see utf_old.h. */ -#define UTF_BACK_N_SAFE(s, start, i, n) UTF16_BACK_N_SAFE(s, start, i, n) - - -/** @deprecated ICU 2.4. Renamed to U16_SET_CP_LIMIT_UNSAFE, see utf_old.h. */ -#define UTF_SET_CHAR_LIMIT_UNSAFE(s, i) UTF16_SET_CHAR_LIMIT_UNSAFE(s, i) - -/** @deprecated ICU 2.4. Renamed to U16_SET_CP_LIMIT, see utf_old.h. */ -#define UTF_SET_CHAR_LIMIT_SAFE(s, start, i, length) UTF16_SET_CHAR_LIMIT_SAFE(s, start, i, length) - -/* Define default macros (UTF-16 "safe") ------------------------------------ */ - -/** - * Does this code unit alone encode a code point (BMP, not a surrogate)? - * Same as UTF16_IS_SINGLE. - * @deprecated ICU 2.4. Renamed to U_IS_SINGLE and U16_IS_SINGLE, see utf_old.h. - */ -#define UTF_IS_SINGLE(uchar) U16_IS_SINGLE(uchar) - -/** - * Is this code unit the first one of several (a lead surrogate)? - * Same as UTF16_IS_LEAD. - * @deprecated ICU 2.4. Renamed to U_IS_LEAD and U16_IS_LEAD, see utf_old.h. - */ -#define UTF_IS_LEAD(uchar) U16_IS_LEAD(uchar) - -/** - * Is this code unit one of several but not the first one (a trail surrogate)? - * Same as UTF16_IS_TRAIL. - * @deprecated ICU 2.4. Renamed to U_IS_TRAIL and U16_IS_TRAIL, see utf_old.h. - */ -#define UTF_IS_TRAIL(uchar) U16_IS_TRAIL(uchar) - -/** - * Does this code point require multiple code units (is it a supplementary code point)? - * Same as UTF16_NEED_MULTIPLE_UCHAR. - * @deprecated ICU 2.4. Use U16_LENGTH or test ((uint32_t)(c)>0xffff) instead. - */ -#define UTF_NEED_MULTIPLE_UCHAR(c) UTF16_NEED_MULTIPLE_UCHAR(c) - -/** - * How many code units are used to encode this code point (1 or 2)? - * Same as UTF16_CHAR_LENGTH. - * @deprecated ICU 2.4. Renamed to U16_LENGTH, see utf_old.h. - */ -#define UTF_CHAR_LENGTH(c) U16_LENGTH(c) - -/** - * How many code units are used at most for any Unicode code point (2)? - * Same as UTF16_MAX_CHAR_LENGTH. - * @deprecated ICU 2.4. Renamed to U16_MAX_LENGTH, see utf_old.h. - */ -#define UTF_MAX_CHAR_LENGTH U16_MAX_LENGTH - -/** - * Set c to the code point that contains the code unit i. - * i could point to the lead or the trail surrogate for the code point. - * i is not modified. - * Same as UTF16_GET_CHAR. - * \pre 0<=i + * + * Obsolete part of pre-ICU 2.4 utf.h file documentation: + * + *

The original concept for these files was for ICU to allow + * in principle to set which UTF (UTF-8/16/32) is used internally + * by defining UTF_SIZE to either 8, 16, or 32. utf.h would then define the UChar type + * accordingly. UTF-16 was the default.

+ * + *

This concept has been abandoned. + * A lot of the ICU source code assumes UChar strings are in UTF-16. + * This is especially true for low-level code like + * conversion, normalization, and collation. + * The utf.h header enforces the default of UTF-16. + * The UTF-8 and UTF-32 macros remain for now for completeness and backward compatibility.

+ * + *

Accordingly, utf.h defines UChar to be an unsigned 16-bit integer. If this matches wchar_t, then + * UChar is defined to be exactly wchar_t, otherwise uint16_t.

+ * + *

UChar32 is defined to be a signed 32-bit integer (int32_t), large enough for a 21-bit + * Unicode code point (Unicode scalar value, 0..0x10ffff). + * Before ICU 2.4, the definition of UChar32 was similarly platform-dependent as + * the definition of UChar. For details see the documentation for UChar32 itself.

+ * + *

utf.h also defines a number of C macros for handling single Unicode code points and + * for using UTF Unicode strings. It includes utf8.h, utf16.h, and utf32.h for the actual + * implementations of those macros and then aliases one set of them (for UTF-16) for general use. + * The UTF-specific macros have the UTF size in the macro name prefixes (UTF16_...), while + * the general alias macros always begin with UTF_...

+ * + *

Many string operations can be done with or without error checking. + * Where such a distinction is useful, there are two versions of the macros, "unsafe" and "safe" + * ones with ..._UNSAFE and ..._SAFE suffixes. The unsafe macros are fast but may cause + * program failures if the strings are not well-formed. The safe macros have an additional, boolean + * parameter "strict". If strict is false, then only illegal sequences are detected. + * Otherwise, irregular sequences and non-characters are detected as well (like single surrogates). + * Safe macros return special error code points for illegal/irregular sequences: + * Typically, U+ffff, or values that would result in a code unit sequence of the same length + * as the erroneous input sequence.
+ * Note that _UNSAFE macros have fewer parameters: They do not have the strictness parameter, and + * they do not have start/length parameters for boundary checking.

+ * + *

Here, the macros are aliased in two steps: + * In the first step, the UTF-specific macros with UTF16_ prefix and _UNSAFE and _SAFE suffixes are + * aliased according to the UTF_SIZE to macros with UTF_ prefix and the same suffixes and signatures. + * Then, in a second step, the default, general alias macros are set to use either the unsafe or + * the safe/not strict (default) or the safe/strict macro; + * these general macros do not have a strictness parameter.

+ * + *

It is possible to change the default choice for the general alias macros to be unsafe, safe/not strict or safe/strict. + * The default is safe/not strict. It is not recommended to select the unsafe macros as the basis for + * Unicode string handling in ICU! To select this, define UTF_SAFE, UTF_STRICT, or UTF_UNSAFE.

+ * + *

For general use, one should use the default, general macros with UTF_ prefix and no _SAFE/_UNSAFE suffix. + * Only in some cases it may be necessary to control the choice of macro directly and use a less generic alias. + * For example, if it can be assumed that a string is well-formed and the index will stay within the bounds, + * then the _UNSAFE version may be used. + * If a UTF-8 string is to be processed, then the macros with UTF8_ prefixes need to be used.

+ * + *
+ * + * Deprecated ICU 2.4. Use the macros in utf.h, utf16.h, utf8.h instead. + */ + +#ifndef __UTF_OLD_H__ +#define __UTF_OLD_H__ + +#include "unicode/utf.h" +#include "unicode/utf8.h" +#include "unicode/utf16.h" + +/** + * \def U_HIDE_OBSOLETE_UTF_OLD_H + * + * Hides the obsolete definitions in unicode/utf_old.h. + * Recommended to be set to 1 at compile time to make sure + * the long-deprecated macros are no longer used. + * + * For reasons for the deprecation see the utf_old.h file comments. + * + * @internal + */ +#ifndef U_HIDE_OBSOLETE_UTF_OLD_H +# define U_HIDE_OBSOLETE_UTF_OLD_H 0 +#endif + +#if !defined(U_HIDE_DEPRECATED_API) && !U_HIDE_OBSOLETE_UTF_OLD_H + +/* Formerly utf.h, part 1 --------------------------------------------------- */ + +#ifdef U_USE_UTF_DEPRECATES +/** + * Unicode string and array offset and index type. + * ICU always counts Unicode code units (UChars) for + * string offsets, indexes, and lengths, not Unicode code points. + * + * @obsolete ICU 2.6. Use int32_t directly instead since this API will be removed in that release. + */ +typedef int32_t UTextOffset; +#endif + +/** Number of bits in a Unicode string code unit - ICU uses 16-bit Unicode. @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF_SIZE 16 + +/** + * The default choice for general Unicode string macros is to use the ..._SAFE macro implementations + * with strict=false. + * + * @deprecated ICU 2.4. Obsolete, see utf_old.h. + */ +#define UTF_SAFE +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#undef UTF_UNSAFE +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#undef UTF_STRICT + +/** + * UTF8_ERROR_VALUE_1 and UTF8_ERROR_VALUE_2 are special error values for UTF-8, + * which need 1 or 2 bytes in UTF-8: + * \code + * U+0015 = NAK = Negative Acknowledge, C0 control character + * U+009f = highest C1 control character + * \endcode + * + * These are used by UTF8_..._SAFE macros so that they can return an error value + * that needs the same number of code units (bytes) as were seen by + * a macro. They should be tested with UTF_IS_ERROR() or UTF_IS_VALID(). + * + * @deprecated ICU 2.4. Obsolete, see utf_old.h. + */ +#define UTF8_ERROR_VALUE_1 0x15 + +/** + * See documentation on UTF8_ERROR_VALUE_1 for details. + * + * @deprecated ICU 2.4. Obsolete, see utf_old.h. + */ +#define UTF8_ERROR_VALUE_2 0x9f + +/** + * Error value for all UTFs. This code point value will be set by macros with error + * checking if an error is detected. + * + * @deprecated ICU 2.4. Obsolete, see utf_old.h. + */ +#define UTF_ERROR_VALUE 0xffff + +/** + * Is a given 32-bit code an error value + * as returned by one of the macros for any UTF? + * + * @deprecated ICU 2.4. Obsolete, see utf_old.h. + */ +#define UTF_IS_ERROR(c) \ + (((c)&0xfffe)==0xfffe || (c)==UTF8_ERROR_VALUE_1 || (c)==UTF8_ERROR_VALUE_2) + +/** + * This is a combined macro: Is c a valid Unicode value _and_ not an error code? + * + * @deprecated ICU 2.4. Obsolete, see utf_old.h. + */ +#define UTF_IS_VALID(c) \ + (UTF_IS_UNICODE_CHAR(c) && \ + (c)!=UTF8_ERROR_VALUE_1 && (c)!=UTF8_ERROR_VALUE_2) + +/** + * Is this code unit or code point a surrogate (U+d800..U+dfff)? + * @deprecated ICU 2.4. Renamed to U_IS_SURROGATE and U16_IS_SURROGATE, see utf_old.h. + */ +#define UTF_IS_SURROGATE(uchar) (((uchar)&0xfffff800)==0xd800) + +/** + * Is a given 32-bit code point a Unicode noncharacter? + * + * @deprecated ICU 2.4. Renamed to U_IS_UNICODE_NONCHAR, see utf_old.h. + */ +#define UTF_IS_UNICODE_NONCHAR(c) \ + ((c)>=0xfdd0 && \ + ((uint32_t)(c)<=0xfdef || ((c)&0xfffe)==0xfffe) && \ + (uint32_t)(c)<=0x10ffff) + +/** + * Is a given 32-bit value a Unicode code point value (0..U+10ffff) + * that can be assigned a character? + * + * Code points that are not characters include: + * - single surrogate code points (U+d800..U+dfff, 2048 code points) + * - the last two code points on each plane (U+__fffe and U+__ffff, 34 code points) + * - U+fdd0..U+fdef (new with Unicode 3.1, 32 code points) + * - the highest Unicode code point value is U+10ffff + * + * This means that all code points below U+d800 are character code points, + * and that boundary is tested first for performance. + * + * @deprecated ICU 2.4. Renamed to U_IS_UNICODE_CHAR, see utf_old.h. + */ +#define UTF_IS_UNICODE_CHAR(c) \ + ((uint32_t)(c)<0xd800 || \ + ((uint32_t)(c)>0xdfff && \ + (uint32_t)(c)<=0x10ffff && \ + !UTF_IS_UNICODE_NONCHAR(c))) + +/* Formerly utf8.h ---------------------------------------------------------- */ + +/** +* \var utf8_countTrailBytes +* Internal array with numbers of trail bytes for any given byte used in +* lead byte position. +* +* This is internal since it is not meant to be called directly by external clients; +* however it is called by public macros in this file and thus must remain stable, +* and should not be hidden when other internal functions are hidden (otherwise +* public macros would fail to compile). +* @internal +*/ +#ifdef U_UTF8_IMPL +// No forward declaration if compiling utf_impl.cpp, which defines utf8_countTrailBytes. +#elif defined(U_STATIC_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) +U_CAPI const uint8_t utf8_countTrailBytes[]; +#else +U_CFUNC U_IMPORT const uint8_t utf8_countTrailBytes[]; +#endif + +/** + * Count the trail bytes for a UTF-8 lead byte. + * @deprecated ICU 2.4. Renamed to U8_COUNT_TRAIL_BYTES, see utf_old.h. + */ +#define UTF8_COUNT_TRAIL_BYTES(leadByte) (utf8_countTrailBytes[(uint8_t)leadByte]) + +/** + * Mask a UTF-8 lead byte, leave only the lower bits that form part of the code point value. + * @deprecated ICU 2.4. Renamed to U8_MASK_LEAD_BYTE, see utf_old.h. + */ +#define UTF8_MASK_LEAD_BYTE(leadByte, countTrailBytes) ((leadByte)&=(1<<(6-(countTrailBytes)))-1) + +/** Is this this code point a single code unit (byte)? @deprecated ICU 2.4. Renamed to U8_IS_SINGLE, see utf_old.h. */ +#define UTF8_IS_SINGLE(uchar) (((uchar)&0x80)==0) +/** Is this this code unit the lead code unit (byte) of a code point? @deprecated ICU 2.4. Renamed to U8_IS_LEAD, see utf_old.h. */ +#define UTF8_IS_LEAD(uchar) ((uint8_t)((uchar)-0xc0)<0x3e) +/** Is this this code unit a trailing code unit (byte) of a code point? @deprecated ICU 2.4. Renamed to U8_IS_TRAIL, see utf_old.h. */ +#define UTF8_IS_TRAIL(uchar) (((uchar)&0xc0)==0x80) + +/** Does this scalar Unicode value need multiple code units for storage? @deprecated ICU 2.4. Use U8_LENGTH or test ((uint32_t)(c)>0x7f) instead, see utf_old.h. */ +#define UTF8_NEED_MULTIPLE_UCHAR(c) ((uint32_t)(c)>0x7f) + +/** + * Given the lead character, how many bytes are taken by this code point. + * ICU does not deal with code points >0x10ffff + * unless necessary for advancing in the byte stream. + * + * These length macros take into account that for values >0x10ffff + * the UTF8_APPEND_CHAR_SAFE macros would write the error code point 0xffff + * with 3 bytes. + * Code point comparisons need to be in uint32_t because UChar32 + * may be a signed type, and negative values must be recognized. + * + * @deprecated ICU 2.4. Use U8_LENGTH instead, see utf.h. + */ +#if 1 +# define UTF8_CHAR_LENGTH(c) \ + ((uint32_t)(c)<=0x7f ? 1 : \ + ((uint32_t)(c)<=0x7ff ? 2 : \ + ((uint32_t)((c)-0x10000)>0xfffff ? 3 : 4) \ + ) \ + ) +#else +# define UTF8_CHAR_LENGTH(c) \ + ((uint32_t)(c)<=0x7f ? 1 : \ + ((uint32_t)(c)<=0x7ff ? 2 : \ + ((uint32_t)(c)<=0xffff ? 3 : \ + ((uint32_t)(c)<=0x10ffff ? 4 : \ + ((uint32_t)(c)<=0x3ffffff ? 5 : \ + ((uint32_t)(c)<=0x7fffffff ? 6 : 3) \ + ) \ + ) \ + ) \ + ) \ + ) +#endif + +/** The maximum number of bytes per code point. @deprecated ICU 2.4. Renamed to U8_MAX_LENGTH, see utf_old.h. */ +#define UTF8_MAX_CHAR_LENGTH 4 + +/** Average number of code units compared to UTF-16. @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF8_ARRAY_SIZE(size) ((5*(size))/2) + +/** @deprecated ICU 2.4. Renamed to U8_GET_UNSAFE, see utf_old.h. */ +#define UTF8_GET_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ + int32_t _utf8_get_char_unsafe_index=(int32_t)(i); \ + UTF8_SET_CHAR_START_UNSAFE(s, _utf8_get_char_unsafe_index); \ + UTF8_NEXT_CHAR_UNSAFE(s, _utf8_get_char_unsafe_index, c); \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Use U8_GET instead, see utf_old.h. */ +#define UTF8_GET_CHAR_SAFE(s, start, i, length, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ + int32_t _utf8_get_char_safe_index=(int32_t)(i); \ + UTF8_SET_CHAR_START_SAFE(s, start, _utf8_get_char_safe_index); \ + UTF8_NEXT_CHAR_SAFE(s, _utf8_get_char_safe_index, length, c, strict); \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U8_NEXT_UNSAFE, see utf_old.h. */ +#define UTF8_NEXT_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[(i)++]; \ + if((uint8_t)((c)-0xc0)<0x35) { \ + uint8_t __count=UTF8_COUNT_TRAIL_BYTES(c); \ + UTF8_MASK_LEAD_BYTE(c, __count); \ + switch(__count) { \ + /* each following branch falls through to the next one */ \ + case 3: \ + (c)=((c)<<6)|((s)[(i)++]&0x3f); \ + case 2: \ + (c)=((c)<<6)|((s)[(i)++]&0x3f); \ + case 1: \ + (c)=((c)<<6)|((s)[(i)++]&0x3f); \ + /* no other branches to optimize switch() */ \ + break; \ + } \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U8_APPEND_UNSAFE, see utf_old.h. */ +#define UTF8_APPEND_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ + if((uint32_t)(c)<=0x7f) { \ + (s)[(i)++]=(uint8_t)(c); \ + } else { \ + if((uint32_t)(c)<=0x7ff) { \ + (s)[(i)++]=(uint8_t)(((c)>>6)|0xc0); \ + } else { \ + if((uint32_t)(c)<=0xffff) { \ + (s)[(i)++]=(uint8_t)(((c)>>12)|0xe0); \ + } else { \ + (s)[(i)++]=(uint8_t)(((c)>>18)|0xf0); \ + (s)[(i)++]=(uint8_t)((((c)>>12)&0x3f)|0x80); \ + } \ + (s)[(i)++]=(uint8_t)((((c)>>6)&0x3f)|0x80); \ + } \ + (s)[(i)++]=(uint8_t)(((c)&0x3f)|0x80); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U8_FWD_1_UNSAFE, see utf_old.h. */ +#define UTF8_FWD_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ + (i)+=1+UTF8_COUNT_TRAIL_BYTES((s)[i]); \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U8_FWD_N_UNSAFE, see utf_old.h. */ +#define UTF8_FWD_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \ + int32_t __N=(n); \ + while(__N>0) { \ + UTF8_FWD_1_UNSAFE(s, i); \ + --__N; \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U8_SET_CP_START_UNSAFE, see utf_old.h. */ +#define UTF8_SET_CHAR_START_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ + while(UTF8_IS_TRAIL((s)[i])) { --(i); } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Use U8_NEXT instead, see utf_old.h. */ +#define UTF8_NEXT_CHAR_SAFE(s, i, length, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[(i)++]; \ + if((c)>=0x80) { \ + if(UTF8_IS_LEAD(c)) { \ + (c)=utf8_nextCharSafeBody(s, &(i), (int32_t)(length), c, strict); \ + } else { \ + (c)=UTF8_ERROR_VALUE_1; \ + } \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Use U8_APPEND instead, see utf_old.h. */ +#define UTF8_APPEND_CHAR_SAFE(s, i, length, c) UPRV_BLOCK_MACRO_BEGIN { \ + if((uint32_t)(c)<=0x7f) { \ + (s)[(i)++]=(uint8_t)(c); \ + } else { \ + (i)=utf8_appendCharSafeBody(s, (int32_t)(i), (int32_t)(length), c, NULL); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U8_FWD_1, see utf_old.h. */ +#define UTF8_FWD_1_SAFE(s, i, length) U8_FWD_1(s, i, length) + +/** @deprecated ICU 2.4. Renamed to U8_FWD_N, see utf_old.h. */ +#define UTF8_FWD_N_SAFE(s, i, length, n) U8_FWD_N(s, i, length, n) + +/** @deprecated ICU 2.4. Renamed to U8_SET_CP_START, see utf_old.h. */ +#define UTF8_SET_CHAR_START_SAFE(s, start, i) U8_SET_CP_START(s, start, i) + +/** @deprecated ICU 2.4. Renamed to U8_PREV_UNSAFE, see utf_old.h. */ +#define UTF8_PREV_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[--(i)]; \ + if(UTF8_IS_TRAIL(c)) { \ + uint8_t __b, __count=1, __shift=6; \ +\ + /* c is a trail byte */ \ + (c)&=0x3f; \ + for(;;) { \ + __b=(s)[--(i)]; \ + if(__b>=0xc0) { \ + UTF8_MASK_LEAD_BYTE(__b, __count); \ + (c)|=(UChar32)__b<<__shift; \ + break; \ + } else { \ + (c)|=(UChar32)(__b&0x3f)<<__shift; \ + ++__count; \ + __shift+=6; \ + } \ + } \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U8_BACK_1_UNSAFE, see utf_old.h. */ +#define UTF8_BACK_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ + while(UTF8_IS_TRAIL((s)[--(i)])) {} \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U8_BACK_N_UNSAFE, see utf_old.h. */ +#define UTF8_BACK_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \ + int32_t __N=(n); \ + while(__N>0) { \ + UTF8_BACK_1_UNSAFE(s, i); \ + --__N; \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U8_SET_CP_LIMIT_UNSAFE, see utf_old.h. */ +#define UTF8_SET_CHAR_LIMIT_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ + UTF8_BACK_1_UNSAFE(s, i); \ + UTF8_FWD_1_UNSAFE(s, i); \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Use U8_PREV instead, see utf_old.h. */ +#define UTF8_PREV_CHAR_SAFE(s, start, i, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[--(i)]; \ + if((c)>=0x80) { \ + if((c)<=0xbf) { \ + (c)=utf8_prevCharSafeBody(s, start, &(i), c, strict); \ + } else { \ + (c)=UTF8_ERROR_VALUE_1; \ + } \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U8_BACK_1, see utf_old.h. */ +#define UTF8_BACK_1_SAFE(s, start, i) U8_BACK_1(s, start, i) + +/** @deprecated ICU 2.4. Renamed to U8_BACK_N, see utf_old.h. */ +#define UTF8_BACK_N_SAFE(s, start, i, n) U8_BACK_N(s, start, i, n) + +/** @deprecated ICU 2.4. Renamed to U8_SET_CP_LIMIT, see utf_old.h. */ +#define UTF8_SET_CHAR_LIMIT_SAFE(s, start, i, length) U8_SET_CP_LIMIT(s, start, i, length) + +/* Formerly utf16.h --------------------------------------------------------- */ + +/** Is uchar a first/lead surrogate? @deprecated ICU 2.4. Renamed to U_IS_LEAD and U16_IS_LEAD, see utf_old.h. */ +#define UTF_IS_FIRST_SURROGATE(uchar) (((uchar)&0xfffffc00)==0xd800) + +/** Is uchar a second/trail surrogate? @deprecated ICU 2.4. Renamed to U_IS_TRAIL and U16_IS_TRAIL, see utf_old.h. */ +#define UTF_IS_SECOND_SURROGATE(uchar) (((uchar)&0xfffffc00)==0xdc00) + +/** Assuming c is a surrogate, is it a first/lead surrogate? @deprecated ICU 2.4. Renamed to U_IS_SURROGATE_LEAD and U16_IS_SURROGATE_LEAD, see utf_old.h. */ +#define UTF_IS_SURROGATE_FIRST(c) (((c)&0x400)==0) + +/** Helper constant for UTF16_GET_PAIR_VALUE. @deprecated ICU 2.4. Renamed to U16_SURROGATE_OFFSET, see utf_old.h. */ +#define UTF_SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000) + +/** Get the UTF-32 value from the surrogate code units. @deprecated ICU 2.4. Renamed to U16_GET_SUPPLEMENTARY, see utf_old.h. */ +#define UTF16_GET_PAIR_VALUE(first, second) \ + (((first)<<10UL)+(second)-UTF_SURROGATE_OFFSET) + +/** @deprecated ICU 2.4. Renamed to U16_LEAD, see utf_old.h. */ +#define UTF_FIRST_SURROGATE(supplementary) (UChar)(((supplementary)>>10)+0xd7c0) + +/** @deprecated ICU 2.4. Renamed to U16_TRAIL, see utf_old.h. */ +#define UTF_SECOND_SURROGATE(supplementary) (UChar)(((supplementary)&0x3ff)|0xdc00) + +/** @deprecated ICU 2.4. Renamed to U16_LEAD, see utf_old.h. */ +#define UTF16_LEAD(supplementary) UTF_FIRST_SURROGATE(supplementary) + +/** @deprecated ICU 2.4. Renamed to U16_TRAIL, see utf_old.h. */ +#define UTF16_TRAIL(supplementary) UTF_SECOND_SURROGATE(supplementary) + +/** @deprecated ICU 2.4. Renamed to U16_IS_SINGLE, see utf_old.h. */ +#define UTF16_IS_SINGLE(uchar) !UTF_IS_SURROGATE(uchar) + +/** @deprecated ICU 2.4. Renamed to U16_IS_LEAD, see utf_old.h. */ +#define UTF16_IS_LEAD(uchar) UTF_IS_FIRST_SURROGATE(uchar) + +/** @deprecated ICU 2.4. Renamed to U16_IS_TRAIL, see utf_old.h. */ +#define UTF16_IS_TRAIL(uchar) UTF_IS_SECOND_SURROGATE(uchar) + +/** Does this scalar Unicode value need multiple code units for storage? @deprecated ICU 2.4. Use U16_LENGTH or test ((uint32_t)(c)>0xffff) instead, see utf_old.h. */ +#define UTF16_NEED_MULTIPLE_UCHAR(c) ((uint32_t)(c)>0xffff) + +/** @deprecated ICU 2.4. Renamed to U16_LENGTH, see utf_old.h. */ +#define UTF16_CHAR_LENGTH(c) ((uint32_t)(c)<=0xffff ? 1 : 2) + +/** @deprecated ICU 2.4. Renamed to U16_MAX_LENGTH, see utf_old.h. */ +#define UTF16_MAX_CHAR_LENGTH 2 + +/** Average number of code units compared to UTF-16. @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF16_ARRAY_SIZE(size) (size) + +/** + * Get a single code point from an offset that points to any + * of the code units that belong to that code point. + * Assume 0<=i=(start) && UTF_IS_FIRST_SURROGATE(__c2=(s)[(i)-1])) { \ + (c)=UTF16_GET_PAIR_VALUE(__c2, (c)); \ + /* strict: ((c)&0xfffe)==0xfffe is caught by UTF_IS_ERROR() and UTF_IS_UNICODE_CHAR() */ \ + } else if(strict) {\ + /* unmatched second surrogate */ \ + (c)=UTF_ERROR_VALUE; \ + } \ + } \ + } else if((strict) && !UTF_IS_UNICODE_CHAR(c)) { \ + (c)=UTF_ERROR_VALUE; \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U16_NEXT_UNSAFE, see utf_old.h. */ +#define UTF16_NEXT_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[(i)++]; \ + if(UTF_IS_FIRST_SURROGATE(c)) { \ + (c)=UTF16_GET_PAIR_VALUE((c), (s)[(i)++]); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U16_APPEND_UNSAFE, see utf_old.h. */ +#define UTF16_APPEND_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ + if((uint32_t)(c)<=0xffff) { \ + (s)[(i)++]=(uint16_t)(c); \ + } else { \ + (s)[(i)++]=(uint16_t)(((c)>>10)+0xd7c0); \ + (s)[(i)++]=(uint16_t)(((c)&0x3ff)|0xdc00); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U16_FWD_1_UNSAFE, see utf_old.h. */ +#define UTF16_FWD_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTF_IS_FIRST_SURROGATE((s)[(i)++])) { \ + ++(i); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U16_FWD_N_UNSAFE, see utf_old.h. */ +#define UTF16_FWD_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \ + int32_t __N=(n); \ + while(__N>0) { \ + UTF16_FWD_1_UNSAFE(s, i); \ + --__N; \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U16_SET_CP_START_UNSAFE, see utf_old.h. */ +#define UTF16_SET_CHAR_START_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTF_IS_SECOND_SURROGATE((s)[i])) { \ + --(i); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Use U16_NEXT instead, see utf_old.h. */ +#define UTF16_NEXT_CHAR_SAFE(s, i, length, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[(i)++]; \ + if(UTF_IS_FIRST_SURROGATE(c)) { \ + uint16_t __c2; \ + if((i)<(length) && UTF_IS_SECOND_SURROGATE(__c2=(s)[(i)])) { \ + ++(i); \ + (c)=UTF16_GET_PAIR_VALUE((c), __c2); \ + /* strict: ((c)&0xfffe)==0xfffe is caught by UTF_IS_ERROR() and UTF_IS_UNICODE_CHAR() */ \ + } else if(strict) {\ + /* unmatched first surrogate */ \ + (c)=UTF_ERROR_VALUE; \ + } \ + } else if((strict) && !UTF_IS_UNICODE_CHAR(c)) { \ + /* unmatched second surrogate or other non-character */ \ + (c)=UTF_ERROR_VALUE; \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Use U16_APPEND instead, see utf_old.h. */ +#define UTF16_APPEND_CHAR_SAFE(s, i, length, c) UPRV_BLOCK_MACRO_BEGIN { \ + if((uint32_t)(c)<=0xffff) { \ + (s)[(i)++]=(uint16_t)(c); \ + } else if((uint32_t)(c)<=0x10ffff) { \ + if((i)+1<(length)) { \ + (s)[(i)++]=(uint16_t)(((c)>>10)+0xd7c0); \ + (s)[(i)++]=(uint16_t)(((c)&0x3ff)|0xdc00); \ + } else /* not enough space */ { \ + (s)[(i)++]=UTF_ERROR_VALUE; \ + } \ + } else /* c>0x10ffff, write error value */ { \ + (s)[(i)++]=UTF_ERROR_VALUE; \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U16_FWD_1, see utf_old.h. */ +#define UTF16_FWD_1_SAFE(s, i, length) U16_FWD_1(s, i, length) + +/** @deprecated ICU 2.4. Renamed to U16_FWD_N, see utf_old.h. */ +#define UTF16_FWD_N_SAFE(s, i, length, n) U16_FWD_N(s, i, length, n) + +/** @deprecated ICU 2.4. Renamed to U16_SET_CP_START, see utf_old.h. */ +#define UTF16_SET_CHAR_START_SAFE(s, start, i) U16_SET_CP_START(s, start, i) + +/** @deprecated ICU 2.4. Renamed to U16_PREV_UNSAFE, see utf_old.h. */ +#define UTF16_PREV_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[--(i)]; \ + if(UTF_IS_SECOND_SURROGATE(c)) { \ + (c)=UTF16_GET_PAIR_VALUE((s)[--(i)], (c)); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U16_BACK_1_UNSAFE, see utf_old.h. */ +#define UTF16_BACK_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTF_IS_SECOND_SURROGATE((s)[--(i)])) { \ + --(i); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U16_BACK_N_UNSAFE, see utf_old.h. */ +#define UTF16_BACK_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \ + int32_t __N=(n); \ + while(__N>0) { \ + UTF16_BACK_1_UNSAFE(s, i); \ + --__N; \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U16_SET_CP_LIMIT_UNSAFE, see utf_old.h. */ +#define UTF16_SET_CHAR_LIMIT_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTF_IS_FIRST_SURROGATE((s)[(i)-1])) { \ + ++(i); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Use U16_PREV instead, see utf_old.h. */ +#define UTF16_PREV_CHAR_SAFE(s, start, i, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[--(i)]; \ + if(UTF_IS_SECOND_SURROGATE(c)) { \ + uint16_t __c2; \ + if((i)>(start) && UTF_IS_FIRST_SURROGATE(__c2=(s)[(i)-1])) { \ + --(i); \ + (c)=UTF16_GET_PAIR_VALUE(__c2, (c)); \ + /* strict: ((c)&0xfffe)==0xfffe is caught by UTF_IS_ERROR() and UTF_IS_UNICODE_CHAR() */ \ + } else if(strict) {\ + /* unmatched second surrogate */ \ + (c)=UTF_ERROR_VALUE; \ + } \ + } else if((strict) && !UTF_IS_UNICODE_CHAR(c)) { \ + /* unmatched first surrogate or other non-character */ \ + (c)=UTF_ERROR_VALUE; \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Renamed to U16_BACK_1, see utf_old.h. */ +#define UTF16_BACK_1_SAFE(s, start, i) U16_BACK_1(s, start, i) + +/** @deprecated ICU 2.4. Renamed to U16_BACK_N, see utf_old.h. */ +#define UTF16_BACK_N_SAFE(s, start, i, n) U16_BACK_N(s, start, i, n) + +/** @deprecated ICU 2.4. Renamed to U16_SET_CP_LIMIT, see utf_old.h. */ +#define UTF16_SET_CHAR_LIMIT_SAFE(s, start, i, length) U16_SET_CP_LIMIT(s, start, i, length) + +/* Formerly utf32.h --------------------------------------------------------- */ + +/* +* Old documentation: +* +* This file defines macros to deal with UTF-32 code units and code points. +* Signatures and semantics are the same as for the similarly named macros +* in utf16.h. +* utf32.h is included by utf.h after unicode/umachine.h

+* and some common definitions. +*

Usage: ICU coding guidelines for if() statements should be followed when using these macros. +* Compound statements (curly braces {}) must be used for if-else-while... +* bodies and all macro statements should be terminated with semicolon.

+*/ + +/* internal definitions ----------------------------------------------------- */ + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_IS_SAFE(c, strict) \ + (!(strict) ? \ + (uint32_t)(c)<=0x10ffff : \ + UTF_IS_UNICODE_CHAR(c)) + +/* + * For the semantics of all of these macros, see utf16.h. + * The UTF-32 versions are trivial because any code point is + * encoded using exactly one code unit. + */ + +/* single-code point definitions -------------------------------------------- */ + +/* classes of code unit values */ + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_IS_SINGLE(uchar) 1 +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_IS_LEAD(uchar) 0 +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_IS_TRAIL(uchar) 0 + +/* number of code units per code point */ + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_NEED_MULTIPLE_UCHAR(c) 0 +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_CHAR_LENGTH(c) 1 +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_MAX_CHAR_LENGTH 1 + +/* average number of code units compared to UTF-16 */ + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_ARRAY_SIZE(size) (size) + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_GET_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[i]; \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_GET_CHAR_SAFE(s, start, i, length, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[i]; \ + if(!UTF32_IS_SAFE(c, strict)) { \ + (c)=UTF_ERROR_VALUE; \ + } \ +} UPRV_BLOCK_MACRO_END + +/* definitions with forward iteration --------------------------------------- */ + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_NEXT_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[(i)++]; \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_APPEND_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ + (s)[(i)++]=(c); \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_FWD_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ + ++(i); \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_FWD_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \ + (i)+=(n); \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_SET_CHAR_START_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_NEXT_CHAR_SAFE(s, i, length, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[(i)++]; \ + if(!UTF32_IS_SAFE(c, strict)) { \ + (c)=UTF_ERROR_VALUE; \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_APPEND_CHAR_SAFE(s, i, length, c) UPRV_BLOCK_MACRO_BEGIN { \ + if((uint32_t)(c)<=0x10ffff) { \ + (s)[(i)++]=(c); \ + } else /* c>0x10ffff, write 0xfffd */ { \ + (s)[(i)++]=0xfffd; \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_FWD_1_SAFE(s, i, length) UPRV_BLOCK_MACRO_BEGIN { \ + ++(i); \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_FWD_N_SAFE(s, i, length, n) UPRV_BLOCK_MACRO_BEGIN { \ + if(((i)+=(n))>(length)) { \ + (i)=(length); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_SET_CHAR_START_SAFE(s, start, i) UPRV_BLOCK_MACRO_BEGIN { \ +} UPRV_BLOCK_MACRO_END + +/* definitions with backward iteration -------------------------------------- */ + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_PREV_CHAR_UNSAFE(s, i, c) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[--(i)]; \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_BACK_1_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ + --(i); \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_BACK_N_UNSAFE(s, i, n) UPRV_BLOCK_MACRO_BEGIN { \ + (i)-=(n); \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_SET_CHAR_LIMIT_UNSAFE(s, i) UPRV_BLOCK_MACRO_BEGIN { \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_PREV_CHAR_SAFE(s, start, i, c, strict) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=(s)[--(i)]; \ + if(!UTF32_IS_SAFE(c, strict)) { \ + (c)=UTF_ERROR_VALUE; \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_BACK_1_SAFE(s, start, i) UPRV_BLOCK_MACRO_BEGIN { \ + --(i); \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_BACK_N_SAFE(s, start, i, n) UPRV_BLOCK_MACRO_BEGIN { \ + (i)-=(n); \ + if((i)<(start)) { \ + (i)=(start); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** @deprecated ICU 2.4. Obsolete, see utf_old.h. */ +#define UTF32_SET_CHAR_LIMIT_SAFE(s, i, length) UPRV_BLOCK_MACRO_BEGIN { \ +} UPRV_BLOCK_MACRO_END + +/* Formerly utf.h, part 2 --------------------------------------------------- */ + +/** + * Estimate the number of code units for a string based on the number of UTF-16 code units. + * + * @deprecated ICU 2.4. Obsolete, see utf_old.h. + */ +#define UTF_ARRAY_SIZE(size) UTF16_ARRAY_SIZE(size) + +/** @deprecated ICU 2.4. Renamed to U16_GET_UNSAFE, see utf_old.h. */ +#define UTF_GET_CHAR_UNSAFE(s, i, c) UTF16_GET_CHAR_UNSAFE(s, i, c) + +/** @deprecated ICU 2.4. Use U16_GET instead, see utf_old.h. */ +#define UTF_GET_CHAR_SAFE(s, start, i, length, c, strict) UTF16_GET_CHAR_SAFE(s, start, i, length, c, strict) + + +/** @deprecated ICU 2.4. Renamed to U16_NEXT_UNSAFE, see utf_old.h. */ +#define UTF_NEXT_CHAR_UNSAFE(s, i, c) UTF16_NEXT_CHAR_UNSAFE(s, i, c) + +/** @deprecated ICU 2.4. Use U16_NEXT instead, see utf_old.h. */ +#define UTF_NEXT_CHAR_SAFE(s, i, length, c, strict) UTF16_NEXT_CHAR_SAFE(s, i, length, c, strict) + + +/** @deprecated ICU 2.4. Renamed to U16_APPEND_UNSAFE, see utf_old.h. */ +#define UTF_APPEND_CHAR_UNSAFE(s, i, c) UTF16_APPEND_CHAR_UNSAFE(s, i, c) + +/** @deprecated ICU 2.4. Use U16_APPEND instead, see utf_old.h. */ +#define UTF_APPEND_CHAR_SAFE(s, i, length, c) UTF16_APPEND_CHAR_SAFE(s, i, length, c) + + +/** @deprecated ICU 2.4. Renamed to U16_FWD_1_UNSAFE, see utf_old.h. */ +#define UTF_FWD_1_UNSAFE(s, i) UTF16_FWD_1_UNSAFE(s, i) + +/** @deprecated ICU 2.4. Renamed to U16_FWD_1, see utf_old.h. */ +#define UTF_FWD_1_SAFE(s, i, length) UTF16_FWD_1_SAFE(s, i, length) + + +/** @deprecated ICU 2.4. Renamed to U16_FWD_N_UNSAFE, see utf_old.h. */ +#define UTF_FWD_N_UNSAFE(s, i, n) UTF16_FWD_N_UNSAFE(s, i, n) + +/** @deprecated ICU 2.4. Renamed to U16_FWD_N, see utf_old.h. */ +#define UTF_FWD_N_SAFE(s, i, length, n) UTF16_FWD_N_SAFE(s, i, length, n) + + +/** @deprecated ICU 2.4. Renamed to U16_SET_CP_START_UNSAFE, see utf_old.h. */ +#define UTF_SET_CHAR_START_UNSAFE(s, i) UTF16_SET_CHAR_START_UNSAFE(s, i) + +/** @deprecated ICU 2.4. Renamed to U16_SET_CP_START, see utf_old.h. */ +#define UTF_SET_CHAR_START_SAFE(s, start, i) UTF16_SET_CHAR_START_SAFE(s, start, i) + + +/** @deprecated ICU 2.4. Renamed to U16_PREV_UNSAFE, see utf_old.h. */ +#define UTF_PREV_CHAR_UNSAFE(s, i, c) UTF16_PREV_CHAR_UNSAFE(s, i, c) + +/** @deprecated ICU 2.4. Use U16_PREV instead, see utf_old.h. */ +#define UTF_PREV_CHAR_SAFE(s, start, i, c, strict) UTF16_PREV_CHAR_SAFE(s, start, i, c, strict) + + +/** @deprecated ICU 2.4. Renamed to U16_BACK_1_UNSAFE, see utf_old.h. */ +#define UTF_BACK_1_UNSAFE(s, i) UTF16_BACK_1_UNSAFE(s, i) + +/** @deprecated ICU 2.4. Renamed to U16_BACK_1, see utf_old.h. */ +#define UTF_BACK_1_SAFE(s, start, i) UTF16_BACK_1_SAFE(s, start, i) + + +/** @deprecated ICU 2.4. Renamed to U16_BACK_N_UNSAFE, see utf_old.h. */ +#define UTF_BACK_N_UNSAFE(s, i, n) UTF16_BACK_N_UNSAFE(s, i, n) + +/** @deprecated ICU 2.4. Renamed to U16_BACK_N, see utf_old.h. */ +#define UTF_BACK_N_SAFE(s, start, i, n) UTF16_BACK_N_SAFE(s, start, i, n) + + +/** @deprecated ICU 2.4. Renamed to U16_SET_CP_LIMIT_UNSAFE, see utf_old.h. */ +#define UTF_SET_CHAR_LIMIT_UNSAFE(s, i) UTF16_SET_CHAR_LIMIT_UNSAFE(s, i) + +/** @deprecated ICU 2.4. Renamed to U16_SET_CP_LIMIT, see utf_old.h. */ +#define UTF_SET_CHAR_LIMIT_SAFE(s, start, i, length) UTF16_SET_CHAR_LIMIT_SAFE(s, start, i, length) + +/* Define default macros (UTF-16 "safe") ------------------------------------ */ + +/** + * Does this code unit alone encode a code point (BMP, not a surrogate)? + * Same as UTF16_IS_SINGLE. + * @deprecated ICU 2.4. Renamed to U_IS_SINGLE and U16_IS_SINGLE, see utf_old.h. + */ +#define UTF_IS_SINGLE(uchar) U16_IS_SINGLE(uchar) + +/** + * Is this code unit the first one of several (a lead surrogate)? + * Same as UTF16_IS_LEAD. + * @deprecated ICU 2.4. Renamed to U_IS_LEAD and U16_IS_LEAD, see utf_old.h. + */ +#define UTF_IS_LEAD(uchar) U16_IS_LEAD(uchar) + +/** + * Is this code unit one of several but not the first one (a trail surrogate)? + * Same as UTF16_IS_TRAIL. + * @deprecated ICU 2.4. Renamed to U_IS_TRAIL and U16_IS_TRAIL, see utf_old.h. + */ +#define UTF_IS_TRAIL(uchar) U16_IS_TRAIL(uchar) + +/** + * Does this code point require multiple code units (is it a supplementary code point)? + * Same as UTF16_NEED_MULTIPLE_UCHAR. + * @deprecated ICU 2.4. Use U16_LENGTH or test ((uint32_t)(c)>0xffff) instead. + */ +#define UTF_NEED_MULTIPLE_UCHAR(c) UTF16_NEED_MULTIPLE_UCHAR(c) + +/** + * How many code units are used to encode this code point (1 or 2)? + * Same as UTF16_CHAR_LENGTH. + * @deprecated ICU 2.4. Renamed to U16_LENGTH, see utf_old.h. + */ +#define UTF_CHAR_LENGTH(c) U16_LENGTH(c) + +/** + * How many code units are used at most for any Unicode code point (2)? + * Same as UTF16_MAX_CHAR_LENGTH. + * @deprecated ICU 2.4. Renamed to U16_MAX_LENGTH, see utf_old.h. + */ +#define UTF_MAX_CHAR_LENGTH U16_MAX_LENGTH + +/** + * Set c to the code point that contains the code unit i. + * i could point to the lead or the trail surrogate for the code point. + * i is not modified. + * Same as UTF16_GET_CHAR. + * \pre 0<=i -#include "unicode/utypes.h" - -/** - * \file - * \brief C API: Definitions for ICU tracing/logging. - * - * This provides API for debugging the internals of ICU without the use of - * a traditional debugger. - * - * By default, tracing is disabled in ICU. If you need to debug ICU with - * tracing, please compile ICU with the --enable-tracing configure option. - */ - -U_CDECL_BEGIN - -/** - * Trace severity levels. Higher levels increase the verbosity of the trace output. - * @see utrace_setLevel - * @stable ICU 2.8 - */ -typedef enum UTraceLevel { - /** Disable all tracing @stable ICU 2.8*/ - UTRACE_OFF=-1, - /** Trace error conditions only @stable ICU 2.8*/ - UTRACE_ERROR=0, - /** Trace errors and warnings @stable ICU 2.8*/ - UTRACE_WARNING=3, - /** Trace opens and closes of ICU services @stable ICU 2.8*/ - UTRACE_OPEN_CLOSE=5, - /** Trace an intermediate number of ICU operations @stable ICU 2.8*/ - UTRACE_INFO=7, - /** Trace the maximum number of ICU operations @stable ICU 2.8*/ - UTRACE_VERBOSE=9 -} UTraceLevel; - -/** - * These are the ICU functions that will be traced when tracing is enabled. - * @stable ICU 2.8 - */ -typedef enum UTraceFunctionNumber { - UTRACE_FUNCTION_START=0, - UTRACE_U_INIT=UTRACE_FUNCTION_START, - UTRACE_U_CLEANUP, - -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal collation trace location. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UTRACE_FUNCTION_LIMIT, -#endif // U_HIDE_DEPRECATED_API - - UTRACE_CONVERSION_START=0x1000, - UTRACE_UCNV_OPEN=UTRACE_CONVERSION_START, - UTRACE_UCNV_OPEN_PACKAGE, - UTRACE_UCNV_OPEN_ALGORITHMIC, - UTRACE_UCNV_CLONE, - UTRACE_UCNV_CLOSE, - UTRACE_UCNV_FLUSH_CACHE, - UTRACE_UCNV_LOAD, - UTRACE_UCNV_UNLOAD, - -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal collation trace location. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UTRACE_CONVERSION_LIMIT, -#endif // U_HIDE_DEPRECATED_API - - UTRACE_COLLATION_START=0x2000, - UTRACE_UCOL_OPEN=UTRACE_COLLATION_START, - UTRACE_UCOL_CLOSE, - UTRACE_UCOL_STRCOLL, - UTRACE_UCOL_GET_SORTKEY, - UTRACE_UCOL_GETLOCALE, - UTRACE_UCOL_NEXTSORTKEYPART, - UTRACE_UCOL_STRCOLLITER, - UTRACE_UCOL_OPEN_FROM_SHORT_STRING, - UTRACE_UCOL_STRCOLLUTF8, /**< @stable ICU 50 */ - -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal collation trace location. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - UTRACE_COLLATION_LIMIT, -#endif // U_HIDE_DEPRECATED_API - - /** - * The lowest resource/data location. - * @stable ICU 65 - */ - UTRACE_UDATA_START=0x3000, - - /** - * Indicates that a value was read from a resource bundle. Provides three - * C-style strings to UTraceData: type, file name, and resource path. The - * possible types are: - * - * - "string" (a string value was accessed) - * - "binary" (a binary value was accessed) - * - "intvector" (a integer vector value was accessed) - * - "int" (a signed integer value was accessed) - * - "uint" (a unsigned integer value was accessed) - * - "get" (a path was loaded, but the value was not accessed) - * - "getalias" (a path was loaded, and an alias was resolved) - * - * @stable ICU 65 - */ - UTRACE_UDATA_RESOURCE=UTRACE_UDATA_START, - - /** - * Indicates that a resource bundle was opened. - * - * Provides one C-style string to UTraceData: file name. - * @stable ICU 65 - */ - UTRACE_UDATA_BUNDLE, - - /** - * Indicates that a data file was opened, but not *.res files. - * - * Provides one C-style string to UTraceData: file name. - * - * @stable ICU 65 - */ - UTRACE_UDATA_DATA_FILE, - - /** - * Indicates that a *.res file was opened. - * - * This differs from UTRACE_UDATA_BUNDLE because a res file is typically - * opened only once per application runtime, but the bundle corresponding - * to that res file may be opened many times. - * - * Provides one C-style string to UTraceData: file name. - * - * @stable ICU 65 - */ - UTRACE_UDATA_RES_FILE, - -#ifndef U_HIDE_INTERNAL_API - /** - * One more than the highest normal resource/data trace location. - * @internal The numeric value may change over time, see ICU ticket #12420. - */ - UTRACE_RES_DATA_LIMIT, -#endif // U_HIDE_INTERNAL_API - - /** - * The lowest break iterator location. - * @stable ICU 67 - */ - UTRACE_UBRK_START=0x4000, - - /** - * Indicates that a character instance of break iterator was created. - * - * @stable ICU 67 - */ - UTRACE_UBRK_CREATE_CHARACTER = UTRACE_UBRK_START, - - /** - * Indicates that a word instance of break iterator was created. - * - * @stable ICU 67 - */ - UTRACE_UBRK_CREATE_WORD, - - /** - * Indicates that a line instance of break iterator was created. - * - * Provides one C-style string to UTraceData: the lb value ("", - * "loose", "strict", or "normal"). - * - * @stable ICU 67 - */ - UTRACE_UBRK_CREATE_LINE, - - /** - * Indicates that a sentence instance of break iterator was created. - * - * @stable ICU 67 - */ - UTRACE_UBRK_CREATE_SENTENCE, - - /** - * Indicates that a title instance of break iterator was created. - * - * @stable ICU 67 - */ - UTRACE_UBRK_CREATE_TITLE, - - /** - * Indicates that an internal dictionary break engine was created. - * - * Provides one C-style string to UTraceData: the script code of what - * the break engine cover ("Hani", "Khmr", "Laoo", "Mymr", or "Thai"). - * - * @stable ICU 67 - */ - UTRACE_UBRK_CREATE_BREAK_ENGINE, - -#ifndef U_HIDE_INTERNAL_API - /** - * One more than the highest normal break iterator trace location. - * @internal The numeric value may change over time, see ICU ticket #12420. - */ - UTRACE_UBRK_LIMIT, -#endif // U_HIDE_INTERNAL_API - -} UTraceFunctionNumber; - -/** - * Setter for the trace level. - * @param traceLevel A UTraceLevel value. - * @stable ICU 2.8 - */ -U_CAPI void U_EXPORT2 -utrace_setLevel(int32_t traceLevel); - -/** - * Getter for the trace level. - * @return The UTraceLevel value being used by ICU. - * @stable ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -utrace_getLevel(void); - -/* Trace function pointers types ----------------------------- */ - -/** - * Type signature for the trace function to be called when entering a function. - * @param context value supplied at the time the trace functions are set. - * @param fnNumber Enum value indicating the ICU function being entered. - * @stable ICU 2.8 - */ -typedef void U_CALLCONV -UTraceEntry(const void *context, int32_t fnNumber); - -/** - * Type signature for the trace function to be called when exiting from a function. - * @param context value supplied at the time the trace functions are set. - * @param fnNumber Enum value indicating the ICU function being exited. - * @param fmt A formatting string that describes the number and types - * of arguments included with the variable args. The fmt - * string has the same form as the utrace_vformat format - * string. - * @param args A variable arguments list. Contents are described by - * the fmt parameter. - * @see utrace_vformat - * @stable ICU 2.8 - */ -typedef void U_CALLCONV -UTraceExit(const void *context, int32_t fnNumber, - const char *fmt, va_list args); - -/** - * Type signature for the trace function to be called from within an ICU function - * to display data or messages. - * @param context value supplied at the time the trace functions are set. - * @param fnNumber Enum value indicating the ICU function being exited. - * @param level The current tracing level - * @param fmt A format string describing the tracing data that is supplied - * as variable args - * @param args The data being traced, passed as variable args. - * @stable ICU 2.8 - */ -typedef void U_CALLCONV -UTraceData(const void *context, int32_t fnNumber, int32_t level, - const char *fmt, va_list args); - -/** - * Set ICU Tracing functions. Installs application-provided tracing - * functions into ICU. After doing this, subsequent ICU operations - * will call back to the installed functions, providing a trace - * of the use of ICU. Passing a NULL pointer for a tracing function - * is allowed, and inhibits tracing action at points where that function - * would be called. - *

- * Tracing and Threads: Tracing functions are global to a process, and - * will be called in response to ICU operations performed by any - * thread. If tracing of an individual thread is desired, the - * tracing functions must themselves filter by checking that the - * current thread is the desired thread. - * - * @param context an uninterpreted pointer. Whatever is passed in - * here will in turn be passed to each of the tracing - * functions UTraceEntry, UTraceExit and UTraceData. - * ICU does not use or alter this pointer. - * @param e Callback function to be called on entry to a - * a traced ICU function. - * @param x Callback function to be called on exit from a - * traced ICU function. - * @param d Callback function to be called from within a - * traced ICU function, for the purpose of providing - * data to the trace. - * - * @stable ICU 2.8 - */ -U_CAPI void U_EXPORT2 -utrace_setFunctions(const void *context, - UTraceEntry *e, UTraceExit *x, UTraceData *d); - -/** - * Get the currently installed ICU tracing functions. Note that a null function - * pointer will be returned if no trace function has been set. - * - * @param context The currently installed tracing context. - * @param e The currently installed UTraceEntry function. - * @param x The currently installed UTraceExit function. - * @param d The currently installed UTraceData function. - * @stable ICU 2.8 - */ -U_CAPI void U_EXPORT2 -utrace_getFunctions(const void **context, - UTraceEntry **e, UTraceExit **x, UTraceData **d); - - - -/* - * - * ICU trace format string syntax - * - * Format Strings are passed to UTraceData functions, and define the - * number and types of the trace data being passed on each call. - * - * The UTraceData function, which is supplied by the application, - * not by ICU, can either forward the trace data (passed via - * varargs) and the format string back to ICU for formatting into - * a displayable string, or it can interpret the format itself, - * and do as it wishes with the trace data. - * - * - * Goals for the format string - * - basic data output - * - easy to use for trace programmer - * - sufficient provision for data types for trace output readability - * - well-defined types and binary portable APIs - * - * Non-goals - * - printf compatibility - * - fancy formatting - * - argument reordering and other internationalization features - * - * ICU trace format strings contain plain text with argument inserts, - * much like standard printf format strings. - * Each insert begins with a '%', then optionally contains a 'v', - * then exactly one type character. - * Two '%' in a row represent a '%' instead of an insert. - * The trace format strings need not have \n at the end. - * - * - * Types - * ----- - * - * Type characters: - * - c A char character in the default codepage. - * - s A NUL-terminated char * string in the default codepage. - * - S A UChar * string. Requires two params, (ptr, length). Length=-1 for nul term. - * - b A byte (8-bit integer). - * - h A 16-bit integer. Also a 16 bit Unicode code unit. - * - d A 32-bit integer. Also a 20 bit Unicode code point value. - * - l A 64-bit integer. - * - p A data pointer. - * - * Vectors - * ------- - * - * If the 'v' is not specified, then one item of the specified type - * is passed in. - * If the 'v' (for "vector") is specified, then a vector of items of the - * specified type is passed in, via a pointer to the first item - * and an int32_t value for the length of the vector. - * Length==-1 means zero or NUL termination. Works for vectors of all types. - * - * Note: %vS is a vector of (UChar *) strings. The strings must - * be nul terminated as there is no way to provide a - * separate length parameter for each string. The length - * parameter (required for all vectors) is the number of - * strings, not the length of the strings. - * - * Examples - * -------- - * - * These examples show the parameters that will be passed to an application's - * UTraceData() function for various formats. - * - * - the precise formatting is up to the application! - * - the examples use type casts for arguments only to _show_ the types of - * arguments without needing variable declarations in the examples; - * the type casts will not be necessary in actual code - * - * UTraceDataFunc(context, fnNumber, level, - * "There is a character %c in the string %s.", // Format String - * (char)c, (const char *)s); // varargs parameters - * -> There is a character 0x42 'B' in the string "Bravo". - * - * UTraceDataFunc(context, fnNumber, level, - * "Vector of bytes %vb vector of chars %vc", - * (const uint8_t *)bytes, (int32_t)bytesLength, - * (const char *)chars, (int32_t)charsLength); - * -> Vector of bytes - * 42 63 64 3f [4] - * vector of chars - * "Bcd?"[4] - * - * UTraceDataFunc(context, fnNumber, level, - * "An int32_t %d and a whole bunch of them %vd", - * (int32_t)-5, (const int32_t *)ints, (int32_t)intsLength); - * -> An int32_t 0xfffffffb and a whole bunch of them - * fffffffb 00000005 0000010a [3] - * - */ - - - -/** - * Trace output Formatter. An application's UTraceData tracing functions may call - * back to this function to format the trace output in a - * human readable form. Note that a UTraceData function may choose - * to not format the data; it could, for example, save it in - * in the raw form it was received (more compact), leaving - * formatting for a later trace analysis tool. - * @param outBuf pointer to a buffer to receive the formatted output. Output - * will be nul terminated if there is space in the buffer - - * if the length of the requested output < the output buffer size. - * @param capacity Length of the output buffer. - * @param indent Number of spaces to indent the output. Intended to allow - * data displayed from nested functions to be indented for readability. - * @param fmt Format specification for the data to output - * @param args Data to be formatted. - * @return Length of formatted output, including the terminating NUL. - * If buffer capacity is insufficient, the required capacity is returned. - * @stable ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -utrace_vformat(char *outBuf, int32_t capacity, - int32_t indent, const char *fmt, va_list args); - -/** - * Trace output Formatter. An application's UTraceData tracing functions may call - * this function to format any additional trace data, beyond that - * provided by default, in human readable form with the same - * formatting conventions used by utrace_vformat(). - * @param outBuf pointer to a buffer to receive the formatted output. Output - * will be nul terminated if there is space in the buffer - - * if the length of the requested output < the output buffer size. - * @param capacity Length of the output buffer. - * @param indent Number of spaces to indent the output. Intended to allow - * data displayed from nested functions to be indented for readability. - * @param fmt Format specification for the data to output - * @param ... Data to be formatted. - * @return Length of formatted output, including the terminating NUL. - * If buffer capacity is insufficient, the required capacity is returned. - * @stable ICU 2.8 - */ -U_CAPI int32_t U_EXPORT2 -utrace_format(char *outBuf, int32_t capacity, - int32_t indent, const char *fmt, ...); - - - -/* Trace function numbers --------------------------------------------------- */ - -/** - * Get the name of a function from its trace function number. - * - * @param fnNumber The trace number for an ICU function. - * @return The name string for the function. - * - * @see UTraceFunctionNumber - * @stable ICU 2.8 - */ -U_CAPI const char * U_EXPORT2 -utrace_functionName(int32_t fnNumber); - -U_CDECL_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2003-2013, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: utrace.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2003aug06 +* created by: Markus W. Scherer +* +* Definitions for ICU tracing/logging. +* +*/ + +#ifndef __UTRACE_H__ +#define __UTRACE_H__ + +#include +#include "unicode/utypes.h" + +/** + * \file + * \brief C API: Definitions for ICU tracing/logging. + * + * This provides API for debugging the internals of ICU without the use of + * a traditional debugger. + * + * By default, tracing is disabled in ICU. If you need to debug ICU with + * tracing, please compile ICU with the --enable-tracing configure option. + */ + +U_CDECL_BEGIN + +/** + * Trace severity levels. Higher levels increase the verbosity of the trace output. + * @see utrace_setLevel + * @stable ICU 2.8 + */ +typedef enum UTraceLevel { + /** Disable all tracing @stable ICU 2.8*/ + UTRACE_OFF=-1, + /** Trace error conditions only @stable ICU 2.8*/ + UTRACE_ERROR=0, + /** Trace errors and warnings @stable ICU 2.8*/ + UTRACE_WARNING=3, + /** Trace opens and closes of ICU services @stable ICU 2.8*/ + UTRACE_OPEN_CLOSE=5, + /** Trace an intermediate number of ICU operations @stable ICU 2.8*/ + UTRACE_INFO=7, + /** Trace the maximum number of ICU operations @stable ICU 2.8*/ + UTRACE_VERBOSE=9 +} UTraceLevel; + +/** + * These are the ICU functions that will be traced when tracing is enabled. + * @stable ICU 2.8 + */ +typedef enum UTraceFunctionNumber { + UTRACE_FUNCTION_START=0, + UTRACE_U_INIT=UTRACE_FUNCTION_START, + UTRACE_U_CLEANUP, + +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal collation trace location. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UTRACE_FUNCTION_LIMIT, +#endif // U_HIDE_DEPRECATED_API + + UTRACE_CONVERSION_START=0x1000, + UTRACE_UCNV_OPEN=UTRACE_CONVERSION_START, + UTRACE_UCNV_OPEN_PACKAGE, + UTRACE_UCNV_OPEN_ALGORITHMIC, + UTRACE_UCNV_CLONE, + UTRACE_UCNV_CLOSE, + UTRACE_UCNV_FLUSH_CACHE, + UTRACE_UCNV_LOAD, + UTRACE_UCNV_UNLOAD, + +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal collation trace location. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UTRACE_CONVERSION_LIMIT, +#endif // U_HIDE_DEPRECATED_API + + UTRACE_COLLATION_START=0x2000, + UTRACE_UCOL_OPEN=UTRACE_COLLATION_START, + UTRACE_UCOL_CLOSE, + UTRACE_UCOL_STRCOLL, + UTRACE_UCOL_GET_SORTKEY, + UTRACE_UCOL_GETLOCALE, + UTRACE_UCOL_NEXTSORTKEYPART, + UTRACE_UCOL_STRCOLLITER, + UTRACE_UCOL_OPEN_FROM_SHORT_STRING, + UTRACE_UCOL_STRCOLLUTF8, /**< @stable ICU 50 */ + +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal collation trace location. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + UTRACE_COLLATION_LIMIT, +#endif // U_HIDE_DEPRECATED_API + + /** + * The lowest resource/data location. + * @stable ICU 65 + */ + UTRACE_UDATA_START=0x3000, + + /** + * Indicates that a value was read from a resource bundle. Provides three + * C-style strings to UTraceData: type, file name, and resource path. The + * possible types are: + * + * - "string" (a string value was accessed) + * - "binary" (a binary value was accessed) + * - "intvector" (a integer vector value was accessed) + * - "int" (a signed integer value was accessed) + * - "uint" (a unsigned integer value was accessed) + * - "get" (a path was loaded, but the value was not accessed) + * - "getalias" (a path was loaded, and an alias was resolved) + * + * @stable ICU 65 + */ + UTRACE_UDATA_RESOURCE=UTRACE_UDATA_START, + + /** + * Indicates that a resource bundle was opened. + * + * Provides one C-style string to UTraceData: file name. + * @stable ICU 65 + */ + UTRACE_UDATA_BUNDLE, + + /** + * Indicates that a data file was opened, but not *.res files. + * + * Provides one C-style string to UTraceData: file name. + * + * @stable ICU 65 + */ + UTRACE_UDATA_DATA_FILE, + + /** + * Indicates that a *.res file was opened. + * + * This differs from UTRACE_UDATA_BUNDLE because a res file is typically + * opened only once per application runtime, but the bundle corresponding + * to that res file may be opened many times. + * + * Provides one C-style string to UTraceData: file name. + * + * @stable ICU 65 + */ + UTRACE_UDATA_RES_FILE, + +#ifndef U_HIDE_INTERNAL_API + /** + * One more than the highest normal resource/data trace location. + * @internal The numeric value may change over time, see ICU ticket #12420. + */ + UTRACE_RES_DATA_LIMIT, +#endif // U_HIDE_INTERNAL_API + + /** + * The lowest break iterator location. + * @stable ICU 67 + */ + UTRACE_UBRK_START=0x4000, + + /** + * Indicates that a character instance of break iterator was created. + * + * @stable ICU 67 + */ + UTRACE_UBRK_CREATE_CHARACTER = UTRACE_UBRK_START, + + /** + * Indicates that a word instance of break iterator was created. + * + * @stable ICU 67 + */ + UTRACE_UBRK_CREATE_WORD, + + /** + * Indicates that a line instance of break iterator was created. + * + * Provides one C-style string to UTraceData: the lb value ("", + * "loose", "strict", or "normal"). + * + * @stable ICU 67 + */ + UTRACE_UBRK_CREATE_LINE, + + /** + * Indicates that a sentence instance of break iterator was created. + * + * @stable ICU 67 + */ + UTRACE_UBRK_CREATE_SENTENCE, + + /** + * Indicates that a title instance of break iterator was created. + * + * @stable ICU 67 + */ + UTRACE_UBRK_CREATE_TITLE, + + /** + * Indicates that an internal dictionary break engine was created. + * + * Provides one C-style string to UTraceData: the script code of what + * the break engine cover ("Hani", "Khmr", "Laoo", "Mymr", or "Thai"). + * + * @stable ICU 67 + */ + UTRACE_UBRK_CREATE_BREAK_ENGINE, + +#ifndef U_HIDE_INTERNAL_API + /** + * One more than the highest normal break iterator trace location. + * @internal The numeric value may change over time, see ICU ticket #12420. + */ + UTRACE_UBRK_LIMIT, +#endif // U_HIDE_INTERNAL_API + +} UTraceFunctionNumber; + +/** + * Setter for the trace level. + * @param traceLevel A UTraceLevel value. + * @stable ICU 2.8 + */ +U_CAPI void U_EXPORT2 +utrace_setLevel(int32_t traceLevel); + +/** + * Getter for the trace level. + * @return The UTraceLevel value being used by ICU. + * @stable ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +utrace_getLevel(void); + +/* Trace function pointers types ----------------------------- */ + +/** + * Type signature for the trace function to be called when entering a function. + * @param context value supplied at the time the trace functions are set. + * @param fnNumber Enum value indicating the ICU function being entered. + * @stable ICU 2.8 + */ +typedef void U_CALLCONV +UTraceEntry(const void *context, int32_t fnNumber); + +/** + * Type signature for the trace function to be called when exiting from a function. + * @param context value supplied at the time the trace functions are set. + * @param fnNumber Enum value indicating the ICU function being exited. + * @param fmt A formatting string that describes the number and types + * of arguments included with the variable args. The fmt + * string has the same form as the utrace_vformat format + * string. + * @param args A variable arguments list. Contents are described by + * the fmt parameter. + * @see utrace_vformat + * @stable ICU 2.8 + */ +typedef void U_CALLCONV +UTraceExit(const void *context, int32_t fnNumber, + const char *fmt, va_list args); + +/** + * Type signature for the trace function to be called from within an ICU function + * to display data or messages. + * @param context value supplied at the time the trace functions are set. + * @param fnNumber Enum value indicating the ICU function being exited. + * @param level The current tracing level + * @param fmt A format string describing the tracing data that is supplied + * as variable args + * @param args The data being traced, passed as variable args. + * @stable ICU 2.8 + */ +typedef void U_CALLCONV +UTraceData(const void *context, int32_t fnNumber, int32_t level, + const char *fmt, va_list args); + +/** + * Set ICU Tracing functions. Installs application-provided tracing + * functions into ICU. After doing this, subsequent ICU operations + * will call back to the installed functions, providing a trace + * of the use of ICU. Passing a NULL pointer for a tracing function + * is allowed, and inhibits tracing action at points where that function + * would be called. + *

+ * Tracing and Threads: Tracing functions are global to a process, and + * will be called in response to ICU operations performed by any + * thread. If tracing of an individual thread is desired, the + * tracing functions must themselves filter by checking that the + * current thread is the desired thread. + * + * @param context an uninterpreted pointer. Whatever is passed in + * here will in turn be passed to each of the tracing + * functions UTraceEntry, UTraceExit and UTraceData. + * ICU does not use or alter this pointer. + * @param e Callback function to be called on entry to a + * a traced ICU function. + * @param x Callback function to be called on exit from a + * traced ICU function. + * @param d Callback function to be called from within a + * traced ICU function, for the purpose of providing + * data to the trace. + * + * @stable ICU 2.8 + */ +U_CAPI void U_EXPORT2 +utrace_setFunctions(const void *context, + UTraceEntry *e, UTraceExit *x, UTraceData *d); + +/** + * Get the currently installed ICU tracing functions. Note that a null function + * pointer will be returned if no trace function has been set. + * + * @param context The currently installed tracing context. + * @param e The currently installed UTraceEntry function. + * @param x The currently installed UTraceExit function. + * @param d The currently installed UTraceData function. + * @stable ICU 2.8 + */ +U_CAPI void U_EXPORT2 +utrace_getFunctions(const void **context, + UTraceEntry **e, UTraceExit **x, UTraceData **d); + + + +/* + * + * ICU trace format string syntax + * + * Format Strings are passed to UTraceData functions, and define the + * number and types of the trace data being passed on each call. + * + * The UTraceData function, which is supplied by the application, + * not by ICU, can either forward the trace data (passed via + * varargs) and the format string back to ICU for formatting into + * a displayable string, or it can interpret the format itself, + * and do as it wishes with the trace data. + * + * + * Goals for the format string + * - basic data output + * - easy to use for trace programmer + * - sufficient provision for data types for trace output readability + * - well-defined types and binary portable APIs + * + * Non-goals + * - printf compatibility + * - fancy formatting + * - argument reordering and other internationalization features + * + * ICU trace format strings contain plain text with argument inserts, + * much like standard printf format strings. + * Each insert begins with a '%', then optionally contains a 'v', + * then exactly one type character. + * Two '%' in a row represent a '%' instead of an insert. + * The trace format strings need not have \n at the end. + * + * + * Types + * ----- + * + * Type characters: + * - c A char character in the default codepage. + * - s A NUL-terminated char * string in the default codepage. + * - S A UChar * string. Requires two params, (ptr, length). Length=-1 for nul term. + * - b A byte (8-bit integer). + * - h A 16-bit integer. Also a 16 bit Unicode code unit. + * - d A 32-bit integer. Also a 20 bit Unicode code point value. + * - l A 64-bit integer. + * - p A data pointer. + * + * Vectors + * ------- + * + * If the 'v' is not specified, then one item of the specified type + * is passed in. + * If the 'v' (for "vector") is specified, then a vector of items of the + * specified type is passed in, via a pointer to the first item + * and an int32_t value for the length of the vector. + * Length==-1 means zero or NUL termination. Works for vectors of all types. + * + * Note: %vS is a vector of (UChar *) strings. The strings must + * be nul terminated as there is no way to provide a + * separate length parameter for each string. The length + * parameter (required for all vectors) is the number of + * strings, not the length of the strings. + * + * Examples + * -------- + * + * These examples show the parameters that will be passed to an application's + * UTraceData() function for various formats. + * + * - the precise formatting is up to the application! + * - the examples use type casts for arguments only to _show_ the types of + * arguments without needing variable declarations in the examples; + * the type casts will not be necessary in actual code + * + * UTraceDataFunc(context, fnNumber, level, + * "There is a character %c in the string %s.", // Format String + * (char)c, (const char *)s); // varargs parameters + * -> There is a character 0x42 'B' in the string "Bravo". + * + * UTraceDataFunc(context, fnNumber, level, + * "Vector of bytes %vb vector of chars %vc", + * (const uint8_t *)bytes, (int32_t)bytesLength, + * (const char *)chars, (int32_t)charsLength); + * -> Vector of bytes + * 42 63 64 3f [4] + * vector of chars + * "Bcd?"[4] + * + * UTraceDataFunc(context, fnNumber, level, + * "An int32_t %d and a whole bunch of them %vd", + * (int32_t)-5, (const int32_t *)ints, (int32_t)intsLength); + * -> An int32_t 0xfffffffb and a whole bunch of them + * fffffffb 00000005 0000010a [3] + * + */ + + + +/** + * Trace output Formatter. An application's UTraceData tracing functions may call + * back to this function to format the trace output in a + * human readable form. Note that a UTraceData function may choose + * to not format the data; it could, for example, save it in + * in the raw form it was received (more compact), leaving + * formatting for a later trace analysis tool. + * @param outBuf pointer to a buffer to receive the formatted output. Output + * will be nul terminated if there is space in the buffer - + * if the length of the requested output < the output buffer size. + * @param capacity Length of the output buffer. + * @param indent Number of spaces to indent the output. Intended to allow + * data displayed from nested functions to be indented for readability. + * @param fmt Format specification for the data to output + * @param args Data to be formatted. + * @return Length of formatted output, including the terminating NUL. + * If buffer capacity is insufficient, the required capacity is returned. + * @stable ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +utrace_vformat(char *outBuf, int32_t capacity, + int32_t indent, const char *fmt, va_list args); + +/** + * Trace output Formatter. An application's UTraceData tracing functions may call + * this function to format any additional trace data, beyond that + * provided by default, in human readable form with the same + * formatting conventions used by utrace_vformat(). + * @param outBuf pointer to a buffer to receive the formatted output. Output + * will be nul terminated if there is space in the buffer - + * if the length of the requested output < the output buffer size. + * @param capacity Length of the output buffer. + * @param indent Number of spaces to indent the output. Intended to allow + * data displayed from nested functions to be indented for readability. + * @param fmt Format specification for the data to output + * @param ... Data to be formatted. + * @return Length of formatted output, including the terminating NUL. + * If buffer capacity is insufficient, the required capacity is returned. + * @stable ICU 2.8 + */ +U_CAPI int32_t U_EXPORT2 +utrace_format(char *outBuf, int32_t capacity, + int32_t indent, const char *fmt, ...); + + + +/* Trace function numbers --------------------------------------------------- */ + +/** + * Get the name of a function from its trace function number. + * + * @param fnNumber The trace number for an ICU function. + * @return The name string for the function. + * + * @see UTraceFunctionNumber + * @stable ICU 2.8 + */ +U_CAPI const char * U_EXPORT2 +utrace_functionName(int32_t fnNumber); + +U_CDECL_END + +#endif diff --git a/deps/icu-small/source/common/unicode/utypes.h b/deps/icu-small/source/common/unicode/utypes.h index f890d5d1dbbebf..449611b6e06dda 100644 --- a/deps/icu-small/source/common/unicode/utypes.h +++ b/deps/icu-small/source/common/unicode/utypes.h @@ -1,730 +1,730 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1996-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* FILE NAME : UTYPES.H (formerly ptypes.h) -* -* Date Name Description -* 12/11/96 helena Creation. -* 02/27/97 aliu Added typedefs for UClassID, int8, int16, int32, -* uint8, uint16, and uint32. -* 04/01/97 aliu Added XP_CPLUSPLUS and modified to work under C as -* well as C++. -* Modified to use memcpy() for uprv_arrayCopy() fns. -* 04/14/97 aliu Added TPlatformUtilities. -* 05/07/97 aliu Added import/export specifiers (replacing the old -* broken EXT_CLASS). Added version number for our -* code. Cleaned up header. -* 6/20/97 helena Java class name change. -* 08/11/98 stephen UErrorCode changed from typedef to enum -* 08/12/98 erm Changed T_ANALYTIC_PACKAGE_VERSION to 3 -* 08/14/98 stephen Added uprv_arrayCopy() for int8_t, int16_t, int32_t -* 12/09/98 jfitz Added BUFFER_OVERFLOW_ERROR (bug 1100066) -* 04/20/99 stephen Cleaned up & reworked for autoconf. -* Renamed to utypes.h. -* 05/05/99 stephen Changed to use -* 12/07/99 helena Moved copyright notice string from ucnv_bld.h here. -******************************************************************************* -*/ - -#ifndef UTYPES_H -#define UTYPES_H - - -#include "unicode/umachine.h" -#include "unicode/uversion.h" -#include "unicode/uconfig.h" -#include - -#if !U_NO_DEFAULT_INCLUDE_UTF_HEADERS -# include "unicode/utf.h" -#endif - -/*! - * \file - * \brief Basic definitions for ICU, for both C and C++ APIs - * - * This file defines basic types, constants, and enumerations directly or - * indirectly by including other header files, especially utf.h for the - * basic character and string definitions and umachine.h for consistent - * integer and other types. - */ - - -/** - * \def U_SHOW_CPLUSPLUS_API - * @internal - */ -#ifdef __cplusplus -# ifndef U_SHOW_CPLUSPLUS_API -# define U_SHOW_CPLUSPLUS_API 1 -# endif -#else -# undef U_SHOW_CPLUSPLUS_API -# define U_SHOW_CPLUSPLUS_API 0 -#endif - -/** @{ API visibility control */ - -/** - * \def U_HIDE_DRAFT_API - * Define this to 1 to request that draft API be "hidden" - * @internal - */ -/** - * \def U_HIDE_INTERNAL_API - * Define this to 1 to request that internal API be "hidden" - * @internal - */ -#if !U_DEFAULT_SHOW_DRAFT && !defined(U_SHOW_DRAFT_API) -#define U_HIDE_DRAFT_API 1 -#endif -#if !U_DEFAULT_SHOW_DRAFT && !defined(U_SHOW_INTERNAL_API) -#define U_HIDE_INTERNAL_API 1 -#endif - -/** @} */ - -/*===========================================================================*/ -/* ICUDATA naming scheme */ -/*===========================================================================*/ - -/** - * \def U_ICUDATA_TYPE_LETTER - * - * This is a platform-dependent string containing one letter: - * - b for big-endian, ASCII-family platforms - * - l for little-endian, ASCII-family platforms - * - e for big-endian, EBCDIC-family platforms - * This letter is part of the common data file name. - * @stable ICU 2.0 - */ - -/** - * \def U_ICUDATA_TYPE_LITLETTER - * The non-string form of U_ICUDATA_TYPE_LETTER - * @stable ICU 2.0 - */ -#if U_CHARSET_FAMILY -# if U_IS_BIG_ENDIAN - /* EBCDIC - should always be BE */ -# define U_ICUDATA_TYPE_LETTER "e" -# define U_ICUDATA_TYPE_LITLETTER e -# else -# error "Don't know what to do with little endian EBCDIC!" -# define U_ICUDATA_TYPE_LETTER "x" -# define U_ICUDATA_TYPE_LITLETTER x -# endif -#else -# if U_IS_BIG_ENDIAN - /* Big-endian ASCII */ -# define U_ICUDATA_TYPE_LETTER "b" -# define U_ICUDATA_TYPE_LITLETTER b -# else - /* Little-endian ASCII */ -# define U_ICUDATA_TYPE_LETTER "l" -# define U_ICUDATA_TYPE_LITLETTER l -# endif -#endif - -/** - * A single string literal containing the icudata stub name. i.e. 'icudt18e' for - * ICU 1.8.x on EBCDIC, etc.. - * @stable ICU 2.0 - */ -#define U_ICUDATA_NAME "icudt" U_ICU_VERSION_SHORT U_ICUDATA_TYPE_LETTER -#ifndef U_HIDE_INTERNAL_API -#define U_USRDATA_NAME "usrdt" U_ICU_VERSION_SHORT U_ICUDATA_TYPE_LETTER /**< @internal */ -#define U_USE_USRDATA 0 /**< @internal */ -#endif /* U_HIDE_INTERNAL_API */ - -/** - * U_ICU_ENTRY_POINT is the name of the DLL entry point to the ICU data library. - * Defined as a literal, not a string. - * Tricky Preprocessor use - ## operator replaces macro parameters with the literal string - * from the corresponding macro invocation, _before_ other macro substitutions. - * Need a nested \#defines to get the actual version numbers rather than - * the literal text U_ICU_VERSION_MAJOR_NUM into the name. - * The net result will be something of the form - * \#define U_ICU_ENTRY_POINT icudt19_dat - * @stable ICU 2.4 - */ -#define U_ICUDATA_ENTRY_POINT U_DEF2_ICUDATA_ENTRY_POINT(U_ICU_VERSION_MAJOR_NUM,U_LIB_SUFFIX_C_NAME) - -#ifndef U_HIDE_INTERNAL_API -/** - * Do not use. Note that it's OK for the 2nd argument to be undefined (literal). - * @internal - */ -#define U_DEF2_ICUDATA_ENTRY_POINT(major,suff) U_DEF_ICUDATA_ENTRY_POINT(major,suff) - -/** - * Do not use. - * @internal - */ -#ifndef U_DEF_ICUDATA_ENTRY_POINT -/* affected by symbol renaming. See platform.h */ -#ifndef U_LIB_SUFFIX_C_NAME -#define U_DEF_ICUDATA_ENTRY_POINT(major, suff) icudt##major##_dat -#else -#define U_DEF_ICUDATA_ENTRY_POINT(major, suff) icudt##suff ## major##_dat -#endif -#endif -#endif /* U_HIDE_INTERNAL_API */ - -/** - * \def NULL - * Define NULL if necessary, to nullptr for C++ and to ((void *)0) for C. - * @stable ICU 2.0 - */ -#ifndef NULL -#ifdef __cplusplus -#define NULL nullptr -#else -#define NULL ((void *)0) -#endif -#endif - -/*===========================================================================*/ -/* Calendar/TimeZone data types */ -/*===========================================================================*/ - -/** - * Date and Time data type. - * This is a primitive data type that holds the date and time - * as the number of milliseconds since 1970-jan-01, 00:00 UTC. - * UTC leap seconds are ignored. - * @stable ICU 2.0 - */ -typedef double UDate; - -/** The number of milliseconds per second @stable ICU 2.0 */ -#define U_MILLIS_PER_SECOND (1000) -/** The number of milliseconds per minute @stable ICU 2.0 */ -#define U_MILLIS_PER_MINUTE (60000) -/** The number of milliseconds per hour @stable ICU 2.0 */ -#define U_MILLIS_PER_HOUR (3600000) -/** The number of milliseconds per day @stable ICU 2.0 */ -#define U_MILLIS_PER_DAY (86400000) - -/** - * Maximum UDate value - * @stable ICU 4.8 - */ -#define U_DATE_MAX DBL_MAX - -/** - * Minimum UDate value - * @stable ICU 4.8 - */ -#define U_DATE_MIN -U_DATE_MAX - -/*===========================================================================*/ -/* Shared library/DLL import-export API control */ -/*===========================================================================*/ - -/* - * Control of symbol import/export. - * ICU is separated into three libraries. - */ - -/** - * \def U_COMBINED_IMPLEMENTATION - * Set to export library symbols from inside the ICU library - * when all of ICU is in a single library. - * This can be set as a compiler option while building ICU, and it - * needs to be the first one tested to override U_COMMON_API, U_I18N_API, etc. - * @stable ICU 2.0 - */ - -/** - * \def U_DATA_API - * Set to export library symbols from inside the stubdata library, - * and to import them from outside. - * @stable ICU 3.0 - */ - -/** - * \def U_COMMON_API - * Set to export library symbols from inside the common library, - * and to import them from outside. - * @stable ICU 2.0 - */ - -/** - * \def U_I18N_API - * Set to export library symbols from inside the i18n library, - * and to import them from outside. - * @stable ICU 2.0 - */ - -/** - * \def U_LAYOUT_API - * Set to export library symbols from inside the layout engine library, - * and to import them from outside. - * @stable ICU 2.0 - */ - -/** - * \def U_LAYOUTEX_API - * Set to export library symbols from inside the layout extensions library, - * and to import them from outside. - * @stable ICU 2.6 - */ - -/** - * \def U_IO_API - * Set to export library symbols from inside the ustdio library, - * and to import them from outside. - * @stable ICU 2.0 - */ - -/** - * \def U_TOOLUTIL_API - * Set to export library symbols from inside the toolutil library, - * and to import them from outside. - * @stable ICU 3.4 - */ - -#ifdef U_IN_DOXYGEN -// This definition is required when generating the API docs. -#define U_COMBINED_IMPLEMENTATION 1 -#endif - -#if defined(U_COMBINED_IMPLEMENTATION) -#define U_DATA_API U_EXPORT -#define U_COMMON_API U_EXPORT -#define U_I18N_API U_EXPORT -#define U_LAYOUT_API U_EXPORT -#define U_LAYOUTEX_API U_EXPORT -#define U_IO_API U_EXPORT -#define U_TOOLUTIL_API U_EXPORT -#elif defined(U_STATIC_IMPLEMENTATION) -#define U_DATA_API -#define U_COMMON_API -#define U_I18N_API -#define U_LAYOUT_API -#define U_LAYOUTEX_API -#define U_IO_API -#define U_TOOLUTIL_API -#elif defined(U_COMMON_IMPLEMENTATION) -#define U_DATA_API U_IMPORT -#define U_COMMON_API U_EXPORT -#define U_I18N_API U_IMPORT -#define U_LAYOUT_API U_IMPORT -#define U_LAYOUTEX_API U_IMPORT -#define U_IO_API U_IMPORT -#define U_TOOLUTIL_API U_IMPORT -#elif defined(U_I18N_IMPLEMENTATION) -#define U_DATA_API U_IMPORT -#define U_COMMON_API U_IMPORT -#define U_I18N_API U_EXPORT -#define U_LAYOUT_API U_IMPORT -#define U_LAYOUTEX_API U_IMPORT -#define U_IO_API U_IMPORT -#define U_TOOLUTIL_API U_IMPORT -#elif defined(U_LAYOUT_IMPLEMENTATION) -#define U_DATA_API U_IMPORT -#define U_COMMON_API U_IMPORT -#define U_I18N_API U_IMPORT -#define U_LAYOUT_API U_EXPORT -#define U_LAYOUTEX_API U_IMPORT -#define U_IO_API U_IMPORT -#define U_TOOLUTIL_API U_IMPORT -#elif defined(U_LAYOUTEX_IMPLEMENTATION) -#define U_DATA_API U_IMPORT -#define U_COMMON_API U_IMPORT -#define U_I18N_API U_IMPORT -#define U_LAYOUT_API U_IMPORT -#define U_LAYOUTEX_API U_EXPORT -#define U_IO_API U_IMPORT -#define U_TOOLUTIL_API U_IMPORT -#elif defined(U_IO_IMPLEMENTATION) -#define U_DATA_API U_IMPORT -#define U_COMMON_API U_IMPORT -#define U_I18N_API U_IMPORT -#define U_LAYOUT_API U_IMPORT -#define U_LAYOUTEX_API U_IMPORT -#define U_IO_API U_EXPORT -#define U_TOOLUTIL_API U_IMPORT -#elif defined(U_TOOLUTIL_IMPLEMENTATION) -#define U_DATA_API U_IMPORT -#define U_COMMON_API U_IMPORT -#define U_I18N_API U_IMPORT -#define U_LAYOUT_API U_IMPORT -#define U_LAYOUTEX_API U_IMPORT -#define U_IO_API U_IMPORT -#define U_TOOLUTIL_API U_EXPORT -#else -#define U_DATA_API U_IMPORT -#define U_COMMON_API U_IMPORT -#define U_I18N_API U_IMPORT -#define U_LAYOUT_API U_IMPORT -#define U_LAYOUTEX_API U_IMPORT -#define U_IO_API U_IMPORT -#define U_TOOLUTIL_API U_IMPORT -#endif - -/** - * \def U_STANDARD_CPP_NAMESPACE - * Control of C++ Namespace - * @stable ICU 2.0 - */ -#ifdef __cplusplus -#define U_STANDARD_CPP_NAMESPACE :: -#else -#define U_STANDARD_CPP_NAMESPACE -#endif - -/*===========================================================================*/ -/* UErrorCode */ -/*===========================================================================*/ - -/** - * Standard ICU4C error code type, a substitute for exceptions. - * - * Initialize the UErrorCode with U_ZERO_ERROR, and check for success or - * failure using U_SUCCESS() or U_FAILURE(): - * - * UErrorCode errorCode = U_ZERO_ERROR; - * // call ICU API that needs an error code parameter. - * if (U_FAILURE(errorCode)) { - * // An error occurred. Handle it here. - * } - * - * C++ code should use icu::ErrorCode, available in unicode/errorcode.h, or a - * suitable subclass. - * - * For more information, see: - * https://unicode-org.github.io/icu/userguide/dev/codingguidelines#details-about-icu-error-codes - * - * Note: By convention, ICU functions that take a reference (C++) or a pointer - * (C) to a UErrorCode first test: - * - * if (U_FAILURE(errorCode)) { return immediately; } - * - * so that in a chain of such functions the first one that sets an error code - * causes the following ones to not perform any operations. - * - * @stable ICU 2.0 - */ -typedef enum UErrorCode { - /* The ordering of U_ERROR_INFO_START Vs U_USING_FALLBACK_WARNING looks weird - * and is that way because VC++ debugger displays first encountered constant, - * which is not the what the code is used for - */ - - U_USING_FALLBACK_WARNING = -128, /**< A resource bundle lookup returned a fallback result (not an error) */ - - U_ERROR_WARNING_START = -128, /**< Start of information results (semantically successful) */ - - U_USING_DEFAULT_WARNING = -127, /**< A resource bundle lookup returned a result from the root locale (not an error) */ - - U_SAFECLONE_ALLOCATED_WARNING = -126, /**< A SafeClone operation required allocating memory (informational only) */ - - U_STATE_OLD_WARNING = -125, /**< ICU has to use compatibility layer to construct the service. Expect performance/memory usage degradation. Consider upgrading */ - - U_STRING_NOT_TERMINATED_WARNING = -124,/**< An output string could not be NUL-terminated because output length==destCapacity. */ - - U_SORT_KEY_TOO_SHORT_WARNING = -123, /**< Number of levels requested in getBound is higher than the number of levels in the sort key */ - - U_AMBIGUOUS_ALIAS_WARNING = -122, /**< This converter alias can go to different converter implementations */ - - U_DIFFERENT_UCA_VERSION = -121, /**< ucol_open encountered a mismatch between UCA version and collator image version, so the collator was constructed from rules. No impact to further function */ - - U_PLUGIN_CHANGED_LEVEL_WARNING = -120, /**< A plugin caused a level change. May not be an error, but later plugins may not load. */ - -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal UErrorCode warning value. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_ERROR_WARNING_LIMIT, -#endif // U_HIDE_DEPRECATED_API - - U_ZERO_ERROR = 0, /**< No error, no warning. */ - - U_ILLEGAL_ARGUMENT_ERROR = 1, /**< Start of codes indicating failure */ - U_MISSING_RESOURCE_ERROR = 2, /**< The requested resource cannot be found */ - U_INVALID_FORMAT_ERROR = 3, /**< Data format is not what is expected */ - U_FILE_ACCESS_ERROR = 4, /**< The requested file cannot be found */ - U_INTERNAL_PROGRAM_ERROR = 5, /**< Indicates a bug in the library code */ - U_MESSAGE_PARSE_ERROR = 6, /**< Unable to parse a message (message format) */ - U_MEMORY_ALLOCATION_ERROR = 7, /**< Memory allocation error */ - U_INDEX_OUTOFBOUNDS_ERROR = 8, /**< Trying to access the index that is out of bounds */ - U_PARSE_ERROR = 9, /**< Equivalent to Java ParseException */ - U_INVALID_CHAR_FOUND = 10, /**< Character conversion: Unmappable input sequence. In other APIs: Invalid character. */ - U_TRUNCATED_CHAR_FOUND = 11, /**< Character conversion: Incomplete input sequence. */ - U_ILLEGAL_CHAR_FOUND = 12, /**< Character conversion: Illegal input sequence/combination of input units. */ - U_INVALID_TABLE_FORMAT = 13, /**< Conversion table file found, but corrupted */ - U_INVALID_TABLE_FILE = 14, /**< Conversion table file not found */ - U_BUFFER_OVERFLOW_ERROR = 15, /**< A result would not fit in the supplied buffer */ - U_UNSUPPORTED_ERROR = 16, /**< Requested operation not supported in current context */ - U_RESOURCE_TYPE_MISMATCH = 17, /**< an operation is requested over a resource that does not support it */ - U_ILLEGAL_ESCAPE_SEQUENCE = 18, /**< ISO-2022 illegal escape sequence */ - U_UNSUPPORTED_ESCAPE_SEQUENCE = 19, /**< ISO-2022 unsupported escape sequence */ - U_NO_SPACE_AVAILABLE = 20, /**< No space available for in-buffer expansion for Arabic shaping */ - U_CE_NOT_FOUND_ERROR = 21, /**< Currently used only while setting variable top, but can be used generally */ - U_PRIMARY_TOO_LONG_ERROR = 22, /**< User tried to set variable top to a primary that is longer than two bytes */ - U_STATE_TOO_OLD_ERROR = 23, /**< ICU cannot construct a service from this state, as it is no longer supported */ - U_TOO_MANY_ALIASES_ERROR = 24, /**< There are too many aliases in the path to the requested resource. - It is very possible that a circular alias definition has occurred */ - U_ENUM_OUT_OF_SYNC_ERROR = 25, /**< UEnumeration out of sync with underlying collection */ - U_INVARIANT_CONVERSION_ERROR = 26, /**< Unable to convert a UChar* string to char* with the invariant converter. */ - U_INVALID_STATE_ERROR = 27, /**< Requested operation can not be completed with ICU in its current state */ - U_COLLATOR_VERSION_MISMATCH = 28, /**< Collator version is not compatible with the base version */ - U_USELESS_COLLATOR_ERROR = 29, /**< Collator is options only and no base is specified */ - U_NO_WRITE_PERMISSION = 30, /**< Attempt to modify read-only or constant data. */ - /** - * The input is impractically long for an operation. - * It is rejected because it may lead to problems such as excessive - * processing time, stack depth, or heap memory requirements. - * - * @stable ICU 68 - */ - U_INPUT_TOO_LONG_ERROR = 31, - -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest standard error code. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_STANDARD_ERROR_LIMIT = 32, -#endif // U_HIDE_DEPRECATED_API - - /* - * Error codes in the range 0x10000 0x10100 are reserved for Transliterator. - */ - U_BAD_VARIABLE_DEFINITION=0x10000,/**< Missing '$' or duplicate variable name */ - U_PARSE_ERROR_START = 0x10000, /**< Start of Transliterator errors */ - U_MALFORMED_RULE, /**< Elements of a rule are misplaced */ - U_MALFORMED_SET, /**< A UnicodeSet pattern is invalid*/ - U_MALFORMED_SYMBOL_REFERENCE, /**< UNUSED as of ICU 2.4 */ - U_MALFORMED_UNICODE_ESCAPE, /**< A Unicode escape pattern is invalid*/ - U_MALFORMED_VARIABLE_DEFINITION, /**< A variable definition is invalid */ - U_MALFORMED_VARIABLE_REFERENCE, /**< A variable reference is invalid */ - U_MISMATCHED_SEGMENT_DELIMITERS, /**< UNUSED as of ICU 2.4 */ - U_MISPLACED_ANCHOR_START, /**< A start anchor appears at an illegal position */ - U_MISPLACED_CURSOR_OFFSET, /**< A cursor offset occurs at an illegal position */ - U_MISPLACED_QUANTIFIER, /**< A quantifier appears after a segment close delimiter */ - U_MISSING_OPERATOR, /**< A rule contains no operator */ - U_MISSING_SEGMENT_CLOSE, /**< UNUSED as of ICU 2.4 */ - U_MULTIPLE_ANTE_CONTEXTS, /**< More than one ante context */ - U_MULTIPLE_CURSORS, /**< More than one cursor */ - U_MULTIPLE_POST_CONTEXTS, /**< More than one post context */ - U_TRAILING_BACKSLASH, /**< A dangling backslash */ - U_UNDEFINED_SEGMENT_REFERENCE, /**< A segment reference does not correspond to a defined segment */ - U_UNDEFINED_VARIABLE, /**< A variable reference does not correspond to a defined variable */ - U_UNQUOTED_SPECIAL, /**< A special character was not quoted or escaped */ - U_UNTERMINATED_QUOTE, /**< A closing single quote is missing */ - U_RULE_MASK_ERROR, /**< A rule is hidden by an earlier more general rule */ - U_MISPLACED_COMPOUND_FILTER, /**< A compound filter is in an invalid location */ - U_MULTIPLE_COMPOUND_FILTERS, /**< More than one compound filter */ - U_INVALID_RBT_SYNTAX, /**< A "::id" rule was passed to the RuleBasedTransliterator parser */ - U_INVALID_PROPERTY_PATTERN, /**< UNUSED as of ICU 2.4 */ - U_MALFORMED_PRAGMA, /**< A 'use' pragma is invalid */ - U_UNCLOSED_SEGMENT, /**< A closing ')' is missing */ - U_ILLEGAL_CHAR_IN_SEGMENT, /**< UNUSED as of ICU 2.4 */ - U_VARIABLE_RANGE_EXHAUSTED, /**< Too many stand-ins generated for the given variable range */ - U_VARIABLE_RANGE_OVERLAP, /**< The variable range overlaps characters used in rules */ - U_ILLEGAL_CHARACTER, /**< A special character is outside its allowed context */ - U_INTERNAL_TRANSLITERATOR_ERROR, /**< Internal transliterator system error */ - U_INVALID_ID, /**< A "::id" rule specifies an unknown transliterator */ - U_INVALID_FUNCTION, /**< A "&fn()" rule specifies an unknown transliterator */ -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal Transliterator error code. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_PARSE_ERROR_LIMIT, -#endif // U_HIDE_DEPRECATED_API - - /* - * Error codes in the range 0x10100 0x10200 are reserved for the formatting API. - */ - U_UNEXPECTED_TOKEN=0x10100, /**< Syntax error in format pattern */ - U_FMT_PARSE_ERROR_START=0x10100, /**< Start of format library errors */ - U_MULTIPLE_DECIMAL_SEPARATORS, /**< More than one decimal separator in number pattern */ - U_MULTIPLE_DECIMAL_SEPERATORS = U_MULTIPLE_DECIMAL_SEPARATORS, /**< Typo: kept for backward compatibility. Use U_MULTIPLE_DECIMAL_SEPARATORS */ - U_MULTIPLE_EXPONENTIAL_SYMBOLS, /**< More than one exponent symbol in number pattern */ - U_MALFORMED_EXPONENTIAL_PATTERN, /**< Grouping symbol in exponent pattern */ - U_MULTIPLE_PERCENT_SYMBOLS, /**< More than one percent symbol in number pattern */ - U_MULTIPLE_PERMILL_SYMBOLS, /**< More than one permill symbol in number pattern */ - U_MULTIPLE_PAD_SPECIFIERS, /**< More than one pad symbol in number pattern */ - U_PATTERN_SYNTAX_ERROR, /**< Syntax error in format pattern */ - U_ILLEGAL_PAD_POSITION, /**< Pad symbol misplaced in number pattern */ - U_UNMATCHED_BRACES, /**< Braces do not match in message pattern */ - U_UNSUPPORTED_PROPERTY, /**< UNUSED as of ICU 2.4 */ - U_UNSUPPORTED_ATTRIBUTE, /**< UNUSED as of ICU 2.4 */ - U_ARGUMENT_TYPE_MISMATCH, /**< Argument name and argument index mismatch in MessageFormat functions */ - U_DUPLICATE_KEYWORD, /**< Duplicate keyword in PluralFormat */ - U_UNDEFINED_KEYWORD, /**< Undefined Plural keyword */ - U_DEFAULT_KEYWORD_MISSING, /**< Missing DEFAULT rule in plural rules */ - U_DECIMAL_NUMBER_SYNTAX_ERROR, /**< Decimal number syntax error */ - U_FORMAT_INEXACT_ERROR, /**< Cannot format a number exactly and rounding mode is ROUND_UNNECESSARY @stable ICU 4.8 */ - U_NUMBER_ARG_OUTOFBOUNDS_ERROR, /**< The argument to a NumberFormatter helper method was out of bounds; the bounds are usually 0 to 999. @stable ICU 61 */ - U_NUMBER_SKELETON_SYNTAX_ERROR, /**< The number skeleton passed to C++ NumberFormatter or C UNumberFormatter was invalid or contained a syntax error. @stable ICU 62 */ -#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 = 0x10114, -#endif // U_HIDE_DEPRECATED_API - - /* - * Error codes in the range 0x10200 0x102ff are reserved for BreakIterator. - */ - U_BRK_INTERNAL_ERROR=0x10200, /**< An internal error (bug) was detected. */ - U_BRK_ERROR_START=0x10200, /**< Start of codes indicating Break Iterator failures */ - U_BRK_HEX_DIGITS_EXPECTED, /**< Hex digits expected as part of a escaped char in a rule. */ - U_BRK_SEMICOLON_EXPECTED, /**< Missing ';' at the end of a RBBI rule. */ - U_BRK_RULE_SYNTAX, /**< Syntax error in RBBI rule. */ - U_BRK_UNCLOSED_SET, /**< UnicodeSet writing an RBBI rule missing a closing ']'. */ - U_BRK_ASSIGN_ERROR, /**< Syntax error in RBBI rule assignment statement. */ - U_BRK_VARIABLE_REDFINITION, /**< RBBI rule $Variable redefined. */ - U_BRK_MISMATCHED_PAREN, /**< Mis-matched parentheses in an RBBI rule. */ - U_BRK_NEW_LINE_IN_QUOTED_STRING, /**< Missing closing quote in an RBBI rule. */ - U_BRK_UNDEFINED_VARIABLE, /**< Use of an undefined $Variable in an RBBI rule. */ - U_BRK_INIT_ERROR, /**< Initialization failure. Probable missing ICU Data. */ - U_BRK_RULE_EMPTY_SET, /**< Rule contains an empty Unicode Set. */ - U_BRK_UNRECOGNIZED_OPTION, /**< !!option in RBBI rules not recognized. */ - U_BRK_MALFORMED_RULE_TAG, /**< The {nnn} tag on a rule is malformed */ -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal BreakIterator error code. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_BRK_ERROR_LIMIT, -#endif // U_HIDE_DEPRECATED_API - - /* - * Error codes in the range 0x10300-0x103ff are reserved for regular expression related errors. - */ - U_REGEX_INTERNAL_ERROR=0x10300, /**< An internal error (bug) was detected. */ - U_REGEX_ERROR_START=0x10300, /**< Start of codes indicating Regexp failures */ - U_REGEX_RULE_SYNTAX, /**< Syntax error in regexp pattern. */ - U_REGEX_INVALID_STATE, /**< RegexMatcher in invalid state for requested operation */ - U_REGEX_BAD_ESCAPE_SEQUENCE, /**< Unrecognized backslash escape sequence in pattern */ - U_REGEX_PROPERTY_SYNTAX, /**< Incorrect Unicode property */ - U_REGEX_UNIMPLEMENTED, /**< Use of regexp feature that is not yet implemented. */ - U_REGEX_MISMATCHED_PAREN, /**< Incorrectly nested parentheses in regexp pattern. */ - U_REGEX_NUMBER_TOO_BIG, /**< Decimal number is too large. */ - U_REGEX_BAD_INTERVAL, /**< Error in {min,max} interval */ - U_REGEX_MAX_LT_MIN, /**< In {min,max}, max is less than min. */ - U_REGEX_INVALID_BACK_REF, /**< Back-reference to a non-existent capture group. */ - U_REGEX_INVALID_FLAG, /**< Invalid value for match mode flags. */ - U_REGEX_LOOK_BEHIND_LIMIT, /**< Look-Behind pattern matches must have a bounded maximum length. */ - U_REGEX_SET_CONTAINS_STRING, /**< Regexps cannot have UnicodeSets containing strings.*/ -#ifndef U_HIDE_DEPRECATED_API - U_REGEX_OCTAL_TOO_BIG, /**< Octal character constants must be <= 0377. @deprecated ICU 54. This error cannot occur. */ -#endif /* U_HIDE_DEPRECATED_API */ - U_REGEX_MISSING_CLOSE_BRACKET=U_REGEX_SET_CONTAINS_STRING+2, /**< Missing closing bracket on a bracket expression. */ - U_REGEX_INVALID_RANGE, /**< In a character range [x-y], x is greater than y. */ - U_REGEX_STACK_OVERFLOW, /**< Regular expression backtrack stack overflow. */ - U_REGEX_TIME_OUT, /**< Maximum allowed match time exceeded */ - U_REGEX_STOPPED_BY_CALLER, /**< Matching operation aborted by user callback fn. */ - U_REGEX_PATTERN_TOO_BIG, /**< Pattern exceeds limits on size or complexity. @stable ICU 55 */ - U_REGEX_INVALID_CAPTURE_GROUP_NAME, /**< Invalid capture group name. @stable ICU 55 */ -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal regular expression error code. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_REGEX_ERROR_LIMIT=U_REGEX_STOPPED_BY_CALLER+3, -#endif // U_HIDE_DEPRECATED_API - - /* - * Error codes in the range 0x10400-0x104ff are reserved for IDNA related error codes. - */ - U_IDNA_PROHIBITED_ERROR=0x10400, - U_IDNA_ERROR_START=0x10400, - U_IDNA_UNASSIGNED_ERROR, - U_IDNA_CHECK_BIDI_ERROR, - U_IDNA_STD3_ASCII_RULES_ERROR, - U_IDNA_ACE_PREFIX_ERROR, - U_IDNA_VERIFICATION_ERROR, - U_IDNA_LABEL_TOO_LONG_ERROR, - U_IDNA_ZERO_LENGTH_LABEL_ERROR, - U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR, -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal IDNA error code. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_IDNA_ERROR_LIMIT, -#endif // U_HIDE_DEPRECATED_API - /* - * Aliases for StringPrep - */ - U_STRINGPREP_PROHIBITED_ERROR = U_IDNA_PROHIBITED_ERROR, - U_STRINGPREP_UNASSIGNED_ERROR = U_IDNA_UNASSIGNED_ERROR, - U_STRINGPREP_CHECK_BIDI_ERROR = U_IDNA_CHECK_BIDI_ERROR, - - /* - * Error codes in the range 0x10500-0x105ff are reserved for Plugin related error codes. - */ - U_PLUGIN_ERROR_START=0x10500, /**< Start of codes indicating plugin failures */ - U_PLUGIN_TOO_HIGH=0x10500, /**< The plugin's level is too high to be loaded right now. */ - U_PLUGIN_DIDNT_SET_LEVEL, /**< The plugin didn't call uplug_setPlugLevel in response to a QUERY */ -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal plug-in error code. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_PLUGIN_ERROR_LIMIT, -#endif // U_HIDE_DEPRECATED_API - -#ifndef U_HIDE_DEPRECATED_API - /** - * One more than the highest normal error code. - * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. - */ - U_ERROR_LIMIT=U_PLUGIN_ERROR_LIMIT -#endif // U_HIDE_DEPRECATED_API -} UErrorCode; - -/* Use the following to determine if an UErrorCode represents */ -/* operational success or failure. */ - -#ifdef __cplusplus - /** - * Does the error code indicate success? - * @stable ICU 2.0 - */ - static - inline UBool U_SUCCESS(UErrorCode code) { return (UBool)(code<=U_ZERO_ERROR); } - /** - * Does the error code indicate a failure? - * @stable ICU 2.0 - */ - static - inline UBool U_FAILURE(UErrorCode code) { return (UBool)(code>U_ZERO_ERROR); } -#else - /** - * Does the error code indicate success? - * @stable ICU 2.0 - */ -# define U_SUCCESS(x) ((x)<=U_ZERO_ERROR) - /** - * Does the error code indicate a failure? - * @stable ICU 2.0 - */ -# define U_FAILURE(x) ((x)>U_ZERO_ERROR) -#endif - -/** - * Return a string for a UErrorCode value. - * The string will be the same as the name of the error code constant - * in the UErrorCode enum above. - * @stable ICU 2.0 - */ -U_CAPI const char * U_EXPORT2 -u_errorName(UErrorCode code); - - -#endif /* _UTYPES */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1996-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* FILE NAME : UTYPES.H (formerly ptypes.h) +* +* Date Name Description +* 12/11/96 helena Creation. +* 02/27/97 aliu Added typedefs for UClassID, int8, int16, int32, +* uint8, uint16, and uint32. +* 04/01/97 aliu Added XP_CPLUSPLUS and modified to work under C as +* well as C++. +* Modified to use memcpy() for uprv_arrayCopy() fns. +* 04/14/97 aliu Added TPlatformUtilities. +* 05/07/97 aliu Added import/export specifiers (replacing the old +* broken EXT_CLASS). Added version number for our +* code. Cleaned up header. +* 6/20/97 helena Java class name change. +* 08/11/98 stephen UErrorCode changed from typedef to enum +* 08/12/98 erm Changed T_ANALYTIC_PACKAGE_VERSION to 3 +* 08/14/98 stephen Added uprv_arrayCopy() for int8_t, int16_t, int32_t +* 12/09/98 jfitz Added BUFFER_OVERFLOW_ERROR (bug 1100066) +* 04/20/99 stephen Cleaned up & reworked for autoconf. +* Renamed to utypes.h. +* 05/05/99 stephen Changed to use +* 12/07/99 helena Moved copyright notice string from ucnv_bld.h here. +******************************************************************************* +*/ + +#ifndef UTYPES_H +#define UTYPES_H + + +#include "unicode/umachine.h" +#include "unicode/uversion.h" +#include "unicode/uconfig.h" +#include + +#if !U_NO_DEFAULT_INCLUDE_UTF_HEADERS +# include "unicode/utf.h" +#endif + +/*! + * \file + * \brief Basic definitions for ICU, for both C and C++ APIs + * + * This file defines basic types, constants, and enumerations directly or + * indirectly by including other header files, especially utf.h for the + * basic character and string definitions and umachine.h for consistent + * integer and other types. + */ + + +/** + * \def U_SHOW_CPLUSPLUS_API + * @internal + */ +#ifdef __cplusplus +# ifndef U_SHOW_CPLUSPLUS_API +# define U_SHOW_CPLUSPLUS_API 1 +# endif +#else +# undef U_SHOW_CPLUSPLUS_API +# define U_SHOW_CPLUSPLUS_API 0 +#endif + +/** @{ API visibility control */ + +/** + * \def U_HIDE_DRAFT_API + * Define this to 1 to request that draft API be "hidden" + * @internal + */ +/** + * \def U_HIDE_INTERNAL_API + * Define this to 1 to request that internal API be "hidden" + * @internal + */ +#if !U_DEFAULT_SHOW_DRAFT && !defined(U_SHOW_DRAFT_API) +#define U_HIDE_DRAFT_API 1 +#endif +#if !U_DEFAULT_SHOW_DRAFT && !defined(U_SHOW_INTERNAL_API) +#define U_HIDE_INTERNAL_API 1 +#endif + +/** @} */ + +/*===========================================================================*/ +/* ICUDATA naming scheme */ +/*===========================================================================*/ + +/** + * \def U_ICUDATA_TYPE_LETTER + * + * This is a platform-dependent string containing one letter: + * - b for big-endian, ASCII-family platforms + * - l for little-endian, ASCII-family platforms + * - e for big-endian, EBCDIC-family platforms + * This letter is part of the common data file name. + * @stable ICU 2.0 + */ + +/** + * \def U_ICUDATA_TYPE_LITLETTER + * The non-string form of U_ICUDATA_TYPE_LETTER + * @stable ICU 2.0 + */ +#if U_CHARSET_FAMILY +# if U_IS_BIG_ENDIAN + /* EBCDIC - should always be BE */ +# define U_ICUDATA_TYPE_LETTER "e" +# define U_ICUDATA_TYPE_LITLETTER e +# else +# error "Don't know what to do with little endian EBCDIC!" +# define U_ICUDATA_TYPE_LETTER "x" +# define U_ICUDATA_TYPE_LITLETTER x +# endif +#else +# if U_IS_BIG_ENDIAN + /* Big-endian ASCII */ +# define U_ICUDATA_TYPE_LETTER "b" +# define U_ICUDATA_TYPE_LITLETTER b +# else + /* Little-endian ASCII */ +# define U_ICUDATA_TYPE_LETTER "l" +# define U_ICUDATA_TYPE_LITLETTER l +# endif +#endif + +/** + * A single string literal containing the icudata stub name. i.e. 'icudt18e' for + * ICU 1.8.x on EBCDIC, etc.. + * @stable ICU 2.0 + */ +#define U_ICUDATA_NAME "icudt" U_ICU_VERSION_SHORT U_ICUDATA_TYPE_LETTER +#ifndef U_HIDE_INTERNAL_API +#define U_USRDATA_NAME "usrdt" U_ICU_VERSION_SHORT U_ICUDATA_TYPE_LETTER /**< @internal */ +#define U_USE_USRDATA 0 /**< @internal */ +#endif /* U_HIDE_INTERNAL_API */ + +/** + * U_ICU_ENTRY_POINT is the name of the DLL entry point to the ICU data library. + * Defined as a literal, not a string. + * Tricky Preprocessor use - ## operator replaces macro parameters with the literal string + * from the corresponding macro invocation, _before_ other macro substitutions. + * Need a nested \#defines to get the actual version numbers rather than + * the literal text U_ICU_VERSION_MAJOR_NUM into the name. + * The net result will be something of the form + * \#define U_ICU_ENTRY_POINT icudt19_dat + * @stable ICU 2.4 + */ +#define U_ICUDATA_ENTRY_POINT U_DEF2_ICUDATA_ENTRY_POINT(U_ICU_VERSION_MAJOR_NUM,U_LIB_SUFFIX_C_NAME) + +#ifndef U_HIDE_INTERNAL_API +/** + * Do not use. Note that it's OK for the 2nd argument to be undefined (literal). + * @internal + */ +#define U_DEF2_ICUDATA_ENTRY_POINT(major,suff) U_DEF_ICUDATA_ENTRY_POINT(major,suff) + +/** + * Do not use. + * @internal + */ +#ifndef U_DEF_ICUDATA_ENTRY_POINT +/* affected by symbol renaming. See platform.h */ +#ifndef U_LIB_SUFFIX_C_NAME +#define U_DEF_ICUDATA_ENTRY_POINT(major, suff) icudt##major##_dat +#else +#define U_DEF_ICUDATA_ENTRY_POINT(major, suff) icudt##suff ## major##_dat +#endif +#endif +#endif /* U_HIDE_INTERNAL_API */ + +/** + * \def NULL + * Define NULL if necessary, to nullptr for C++ and to ((void *)0) for C. + * @stable ICU 2.0 + */ +#ifndef NULL +#ifdef __cplusplus +#define NULL nullptr +#else +#define NULL ((void *)0) +#endif +#endif + +/*===========================================================================*/ +/* Calendar/TimeZone data types */ +/*===========================================================================*/ + +/** + * Date and Time data type. + * This is a primitive data type that holds the date and time + * as the number of milliseconds since 1970-jan-01, 00:00 UTC. + * UTC leap seconds are ignored. + * @stable ICU 2.0 + */ +typedef double UDate; + +/** The number of milliseconds per second @stable ICU 2.0 */ +#define U_MILLIS_PER_SECOND (1000) +/** The number of milliseconds per minute @stable ICU 2.0 */ +#define U_MILLIS_PER_MINUTE (60000) +/** The number of milliseconds per hour @stable ICU 2.0 */ +#define U_MILLIS_PER_HOUR (3600000) +/** The number of milliseconds per day @stable ICU 2.0 */ +#define U_MILLIS_PER_DAY (86400000) + +/** + * Maximum UDate value + * @stable ICU 4.8 + */ +#define U_DATE_MAX DBL_MAX + +/** + * Minimum UDate value + * @stable ICU 4.8 + */ +#define U_DATE_MIN -U_DATE_MAX + +/*===========================================================================*/ +/* Shared library/DLL import-export API control */ +/*===========================================================================*/ + +/* + * Control of symbol import/export. + * ICU is separated into three libraries. + */ + +/** + * \def U_COMBINED_IMPLEMENTATION + * Set to export library symbols from inside the ICU library + * when all of ICU is in a single library. + * This can be set as a compiler option while building ICU, and it + * needs to be the first one tested to override U_COMMON_API, U_I18N_API, etc. + * @stable ICU 2.0 + */ + +/** + * \def U_DATA_API + * Set to export library symbols from inside the stubdata library, + * and to import them from outside. + * @stable ICU 3.0 + */ + +/** + * \def U_COMMON_API + * Set to export library symbols from inside the common library, + * and to import them from outside. + * @stable ICU 2.0 + */ + +/** + * \def U_I18N_API + * Set to export library symbols from inside the i18n library, + * and to import them from outside. + * @stable ICU 2.0 + */ + +/** + * \def U_LAYOUT_API + * Set to export library symbols from inside the layout engine library, + * and to import them from outside. + * @stable ICU 2.0 + */ + +/** + * \def U_LAYOUTEX_API + * Set to export library symbols from inside the layout extensions library, + * and to import them from outside. + * @stable ICU 2.6 + */ + +/** + * \def U_IO_API + * Set to export library symbols from inside the ustdio library, + * and to import them from outside. + * @stable ICU 2.0 + */ + +/** + * \def U_TOOLUTIL_API + * Set to export library symbols from inside the toolutil library, + * and to import them from outside. + * @stable ICU 3.4 + */ + +#ifdef U_IN_DOXYGEN +// This definition is required when generating the API docs. +#define U_COMBINED_IMPLEMENTATION 1 +#endif + +#if defined(U_COMBINED_IMPLEMENTATION) +#define U_DATA_API U_EXPORT +#define U_COMMON_API U_EXPORT +#define U_I18N_API U_EXPORT +#define U_LAYOUT_API U_EXPORT +#define U_LAYOUTEX_API U_EXPORT +#define U_IO_API U_EXPORT +#define U_TOOLUTIL_API U_EXPORT +#elif defined(U_STATIC_IMPLEMENTATION) +#define U_DATA_API +#define U_COMMON_API +#define U_I18N_API +#define U_LAYOUT_API +#define U_LAYOUTEX_API +#define U_IO_API +#define U_TOOLUTIL_API +#elif defined(U_COMMON_IMPLEMENTATION) +#define U_DATA_API U_IMPORT +#define U_COMMON_API U_EXPORT +#define U_I18N_API U_IMPORT +#define U_LAYOUT_API U_IMPORT +#define U_LAYOUTEX_API U_IMPORT +#define U_IO_API U_IMPORT +#define U_TOOLUTIL_API U_IMPORT +#elif defined(U_I18N_IMPLEMENTATION) +#define U_DATA_API U_IMPORT +#define U_COMMON_API U_IMPORT +#define U_I18N_API U_EXPORT +#define U_LAYOUT_API U_IMPORT +#define U_LAYOUTEX_API U_IMPORT +#define U_IO_API U_IMPORT +#define U_TOOLUTIL_API U_IMPORT +#elif defined(U_LAYOUT_IMPLEMENTATION) +#define U_DATA_API U_IMPORT +#define U_COMMON_API U_IMPORT +#define U_I18N_API U_IMPORT +#define U_LAYOUT_API U_EXPORT +#define U_LAYOUTEX_API U_IMPORT +#define U_IO_API U_IMPORT +#define U_TOOLUTIL_API U_IMPORT +#elif defined(U_LAYOUTEX_IMPLEMENTATION) +#define U_DATA_API U_IMPORT +#define U_COMMON_API U_IMPORT +#define U_I18N_API U_IMPORT +#define U_LAYOUT_API U_IMPORT +#define U_LAYOUTEX_API U_EXPORT +#define U_IO_API U_IMPORT +#define U_TOOLUTIL_API U_IMPORT +#elif defined(U_IO_IMPLEMENTATION) +#define U_DATA_API U_IMPORT +#define U_COMMON_API U_IMPORT +#define U_I18N_API U_IMPORT +#define U_LAYOUT_API U_IMPORT +#define U_LAYOUTEX_API U_IMPORT +#define U_IO_API U_EXPORT +#define U_TOOLUTIL_API U_IMPORT +#elif defined(U_TOOLUTIL_IMPLEMENTATION) +#define U_DATA_API U_IMPORT +#define U_COMMON_API U_IMPORT +#define U_I18N_API U_IMPORT +#define U_LAYOUT_API U_IMPORT +#define U_LAYOUTEX_API U_IMPORT +#define U_IO_API U_IMPORT +#define U_TOOLUTIL_API U_EXPORT +#else +#define U_DATA_API U_IMPORT +#define U_COMMON_API U_IMPORT +#define U_I18N_API U_IMPORT +#define U_LAYOUT_API U_IMPORT +#define U_LAYOUTEX_API U_IMPORT +#define U_IO_API U_IMPORT +#define U_TOOLUTIL_API U_IMPORT +#endif + +/** + * \def U_STANDARD_CPP_NAMESPACE + * Control of C++ Namespace + * @stable ICU 2.0 + */ +#ifdef __cplusplus +#define U_STANDARD_CPP_NAMESPACE :: +#else +#define U_STANDARD_CPP_NAMESPACE +#endif + +/*===========================================================================*/ +/* UErrorCode */ +/*===========================================================================*/ + +/** + * Standard ICU4C error code type, a substitute for exceptions. + * + * Initialize the UErrorCode with U_ZERO_ERROR, and check for success or + * failure using U_SUCCESS() or U_FAILURE(): + * + * UErrorCode errorCode = U_ZERO_ERROR; + * // call ICU API that needs an error code parameter. + * if (U_FAILURE(errorCode)) { + * // An error occurred. Handle it here. + * } + * + * C++ code should use icu::ErrorCode, available in unicode/errorcode.h, or a + * suitable subclass. + * + * For more information, see: + * https://unicode-org.github.io/icu/userguide/dev/codingguidelines#details-about-icu-error-codes + * + * Note: By convention, ICU functions that take a reference (C++) or a pointer + * (C) to a UErrorCode first test: + * + * if (U_FAILURE(errorCode)) { return immediately; } + * + * so that in a chain of such functions the first one that sets an error code + * causes the following ones to not perform any operations. + * + * @stable ICU 2.0 + */ +typedef enum UErrorCode { + /* The ordering of U_ERROR_INFO_START Vs U_USING_FALLBACK_WARNING looks weird + * and is that way because VC++ debugger displays first encountered constant, + * which is not the what the code is used for + */ + + U_USING_FALLBACK_WARNING = -128, /**< A resource bundle lookup returned a fallback result (not an error) */ + + U_ERROR_WARNING_START = -128, /**< Start of information results (semantically successful) */ + + U_USING_DEFAULT_WARNING = -127, /**< A resource bundle lookup returned a result from the root locale (not an error) */ + + U_SAFECLONE_ALLOCATED_WARNING = -126, /**< A SafeClone operation required allocating memory (informational only) */ + + U_STATE_OLD_WARNING = -125, /**< ICU has to use compatibility layer to construct the service. Expect performance/memory usage degradation. Consider upgrading */ + + U_STRING_NOT_TERMINATED_WARNING = -124,/**< An output string could not be NUL-terminated because output length==destCapacity. */ + + U_SORT_KEY_TOO_SHORT_WARNING = -123, /**< Number of levels requested in getBound is higher than the number of levels in the sort key */ + + U_AMBIGUOUS_ALIAS_WARNING = -122, /**< This converter alias can go to different converter implementations */ + + U_DIFFERENT_UCA_VERSION = -121, /**< ucol_open encountered a mismatch between UCA version and collator image version, so the collator was constructed from rules. No impact to further function */ + + U_PLUGIN_CHANGED_LEVEL_WARNING = -120, /**< A plugin caused a level change. May not be an error, but later plugins may not load. */ + +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal UErrorCode warning value. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_ERROR_WARNING_LIMIT, +#endif // U_HIDE_DEPRECATED_API + + U_ZERO_ERROR = 0, /**< No error, no warning. */ + + U_ILLEGAL_ARGUMENT_ERROR = 1, /**< Start of codes indicating failure */ + U_MISSING_RESOURCE_ERROR = 2, /**< The requested resource cannot be found */ + U_INVALID_FORMAT_ERROR = 3, /**< Data format is not what is expected */ + U_FILE_ACCESS_ERROR = 4, /**< The requested file cannot be found */ + U_INTERNAL_PROGRAM_ERROR = 5, /**< Indicates a bug in the library code */ + U_MESSAGE_PARSE_ERROR = 6, /**< Unable to parse a message (message format) */ + U_MEMORY_ALLOCATION_ERROR = 7, /**< Memory allocation error */ + U_INDEX_OUTOFBOUNDS_ERROR = 8, /**< Trying to access the index that is out of bounds */ + U_PARSE_ERROR = 9, /**< Equivalent to Java ParseException */ + U_INVALID_CHAR_FOUND = 10, /**< Character conversion: Unmappable input sequence. In other APIs: Invalid character. */ + U_TRUNCATED_CHAR_FOUND = 11, /**< Character conversion: Incomplete input sequence. */ + U_ILLEGAL_CHAR_FOUND = 12, /**< Character conversion: Illegal input sequence/combination of input units. */ + U_INVALID_TABLE_FORMAT = 13, /**< Conversion table file found, but corrupted */ + U_INVALID_TABLE_FILE = 14, /**< Conversion table file not found */ + U_BUFFER_OVERFLOW_ERROR = 15, /**< A result would not fit in the supplied buffer */ + U_UNSUPPORTED_ERROR = 16, /**< Requested operation not supported in current context */ + U_RESOURCE_TYPE_MISMATCH = 17, /**< an operation is requested over a resource that does not support it */ + U_ILLEGAL_ESCAPE_SEQUENCE = 18, /**< ISO-2022 illegal escape sequence */ + U_UNSUPPORTED_ESCAPE_SEQUENCE = 19, /**< ISO-2022 unsupported escape sequence */ + U_NO_SPACE_AVAILABLE = 20, /**< No space available for in-buffer expansion for Arabic shaping */ + U_CE_NOT_FOUND_ERROR = 21, /**< Currently used only while setting variable top, but can be used generally */ + U_PRIMARY_TOO_LONG_ERROR = 22, /**< User tried to set variable top to a primary that is longer than two bytes */ + U_STATE_TOO_OLD_ERROR = 23, /**< ICU cannot construct a service from this state, as it is no longer supported */ + U_TOO_MANY_ALIASES_ERROR = 24, /**< There are too many aliases in the path to the requested resource. + It is very possible that a circular alias definition has occurred */ + U_ENUM_OUT_OF_SYNC_ERROR = 25, /**< UEnumeration out of sync with underlying collection */ + U_INVARIANT_CONVERSION_ERROR = 26, /**< Unable to convert a UChar* string to char* with the invariant converter. */ + U_INVALID_STATE_ERROR = 27, /**< Requested operation can not be completed with ICU in its current state */ + U_COLLATOR_VERSION_MISMATCH = 28, /**< Collator version is not compatible with the base version */ + U_USELESS_COLLATOR_ERROR = 29, /**< Collator is options only and no base is specified */ + U_NO_WRITE_PERMISSION = 30, /**< Attempt to modify read-only or constant data. */ + /** + * The input is impractically long for an operation. + * It is rejected because it may lead to problems such as excessive + * processing time, stack depth, or heap memory requirements. + * + * @stable ICU 68 + */ + U_INPUT_TOO_LONG_ERROR = 31, + +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest standard error code. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_STANDARD_ERROR_LIMIT = 32, +#endif // U_HIDE_DEPRECATED_API + + /* + * Error codes in the range 0x10000 0x10100 are reserved for Transliterator. + */ + U_BAD_VARIABLE_DEFINITION=0x10000,/**< Missing '$' or duplicate variable name */ + U_PARSE_ERROR_START = 0x10000, /**< Start of Transliterator errors */ + U_MALFORMED_RULE, /**< Elements of a rule are misplaced */ + U_MALFORMED_SET, /**< A UnicodeSet pattern is invalid*/ + U_MALFORMED_SYMBOL_REFERENCE, /**< UNUSED as of ICU 2.4 */ + U_MALFORMED_UNICODE_ESCAPE, /**< A Unicode escape pattern is invalid*/ + U_MALFORMED_VARIABLE_DEFINITION, /**< A variable definition is invalid */ + U_MALFORMED_VARIABLE_REFERENCE, /**< A variable reference is invalid */ + U_MISMATCHED_SEGMENT_DELIMITERS, /**< UNUSED as of ICU 2.4 */ + U_MISPLACED_ANCHOR_START, /**< A start anchor appears at an illegal position */ + U_MISPLACED_CURSOR_OFFSET, /**< A cursor offset occurs at an illegal position */ + U_MISPLACED_QUANTIFIER, /**< A quantifier appears after a segment close delimiter */ + U_MISSING_OPERATOR, /**< A rule contains no operator */ + U_MISSING_SEGMENT_CLOSE, /**< UNUSED as of ICU 2.4 */ + U_MULTIPLE_ANTE_CONTEXTS, /**< More than one ante context */ + U_MULTIPLE_CURSORS, /**< More than one cursor */ + U_MULTIPLE_POST_CONTEXTS, /**< More than one post context */ + U_TRAILING_BACKSLASH, /**< A dangling backslash */ + U_UNDEFINED_SEGMENT_REFERENCE, /**< A segment reference does not correspond to a defined segment */ + U_UNDEFINED_VARIABLE, /**< A variable reference does not correspond to a defined variable */ + U_UNQUOTED_SPECIAL, /**< A special character was not quoted or escaped */ + U_UNTERMINATED_QUOTE, /**< A closing single quote is missing */ + U_RULE_MASK_ERROR, /**< A rule is hidden by an earlier more general rule */ + U_MISPLACED_COMPOUND_FILTER, /**< A compound filter is in an invalid location */ + U_MULTIPLE_COMPOUND_FILTERS, /**< More than one compound filter */ + U_INVALID_RBT_SYNTAX, /**< A "::id" rule was passed to the RuleBasedTransliterator parser */ + U_INVALID_PROPERTY_PATTERN, /**< UNUSED as of ICU 2.4 */ + U_MALFORMED_PRAGMA, /**< A 'use' pragma is invalid */ + U_UNCLOSED_SEGMENT, /**< A closing ')' is missing */ + U_ILLEGAL_CHAR_IN_SEGMENT, /**< UNUSED as of ICU 2.4 */ + U_VARIABLE_RANGE_EXHAUSTED, /**< Too many stand-ins generated for the given variable range */ + U_VARIABLE_RANGE_OVERLAP, /**< The variable range overlaps characters used in rules */ + U_ILLEGAL_CHARACTER, /**< A special character is outside its allowed context */ + U_INTERNAL_TRANSLITERATOR_ERROR, /**< Internal transliterator system error */ + U_INVALID_ID, /**< A "::id" rule specifies an unknown transliterator */ + U_INVALID_FUNCTION, /**< A "&fn()" rule specifies an unknown transliterator */ +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal Transliterator error code. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_PARSE_ERROR_LIMIT, +#endif // U_HIDE_DEPRECATED_API + + /* + * Error codes in the range 0x10100 0x10200 are reserved for the formatting API. + */ + U_UNEXPECTED_TOKEN=0x10100, /**< Syntax error in format pattern */ + U_FMT_PARSE_ERROR_START=0x10100, /**< Start of format library errors */ + U_MULTIPLE_DECIMAL_SEPARATORS, /**< More than one decimal separator in number pattern */ + U_MULTIPLE_DECIMAL_SEPERATORS = U_MULTIPLE_DECIMAL_SEPARATORS, /**< Typo: kept for backward compatibility. Use U_MULTIPLE_DECIMAL_SEPARATORS */ + U_MULTIPLE_EXPONENTIAL_SYMBOLS, /**< More than one exponent symbol in number pattern */ + U_MALFORMED_EXPONENTIAL_PATTERN, /**< Grouping symbol in exponent pattern */ + U_MULTIPLE_PERCENT_SYMBOLS, /**< More than one percent symbol in number pattern */ + U_MULTIPLE_PERMILL_SYMBOLS, /**< More than one permill symbol in number pattern */ + U_MULTIPLE_PAD_SPECIFIERS, /**< More than one pad symbol in number pattern */ + U_PATTERN_SYNTAX_ERROR, /**< Syntax error in format pattern */ + U_ILLEGAL_PAD_POSITION, /**< Pad symbol misplaced in number pattern */ + U_UNMATCHED_BRACES, /**< Braces do not match in message pattern */ + U_UNSUPPORTED_PROPERTY, /**< UNUSED as of ICU 2.4 */ + U_UNSUPPORTED_ATTRIBUTE, /**< UNUSED as of ICU 2.4 */ + U_ARGUMENT_TYPE_MISMATCH, /**< Argument name and argument index mismatch in MessageFormat functions */ + U_DUPLICATE_KEYWORD, /**< Duplicate keyword in PluralFormat */ + U_UNDEFINED_KEYWORD, /**< Undefined Plural keyword */ + U_DEFAULT_KEYWORD_MISSING, /**< Missing DEFAULT rule in plural rules */ + U_DECIMAL_NUMBER_SYNTAX_ERROR, /**< Decimal number syntax error */ + U_FORMAT_INEXACT_ERROR, /**< Cannot format a number exactly and rounding mode is ROUND_UNNECESSARY @stable ICU 4.8 */ + U_NUMBER_ARG_OUTOFBOUNDS_ERROR, /**< The argument to a NumberFormatter helper method was out of bounds; the bounds are usually 0 to 999. @stable ICU 61 */ + U_NUMBER_SKELETON_SYNTAX_ERROR, /**< The number skeleton passed to C++ NumberFormatter or C UNumberFormatter was invalid or contained a syntax error. @stable ICU 62 */ +#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 = 0x10114, +#endif // U_HIDE_DEPRECATED_API + + /* + * Error codes in the range 0x10200 0x102ff are reserved for BreakIterator. + */ + U_BRK_INTERNAL_ERROR=0x10200, /**< An internal error (bug) was detected. */ + U_BRK_ERROR_START=0x10200, /**< Start of codes indicating Break Iterator failures */ + U_BRK_HEX_DIGITS_EXPECTED, /**< Hex digits expected as part of a escaped char in a rule. */ + U_BRK_SEMICOLON_EXPECTED, /**< Missing ';' at the end of a RBBI rule. */ + U_BRK_RULE_SYNTAX, /**< Syntax error in RBBI rule. */ + U_BRK_UNCLOSED_SET, /**< UnicodeSet writing an RBBI rule missing a closing ']'. */ + U_BRK_ASSIGN_ERROR, /**< Syntax error in RBBI rule assignment statement. */ + U_BRK_VARIABLE_REDFINITION, /**< RBBI rule $Variable redefined. */ + U_BRK_MISMATCHED_PAREN, /**< Mis-matched parentheses in an RBBI rule. */ + U_BRK_NEW_LINE_IN_QUOTED_STRING, /**< Missing closing quote in an RBBI rule. */ + U_BRK_UNDEFINED_VARIABLE, /**< Use of an undefined $Variable in an RBBI rule. */ + U_BRK_INIT_ERROR, /**< Initialization failure. Probable missing ICU Data. */ + U_BRK_RULE_EMPTY_SET, /**< Rule contains an empty Unicode Set. */ + U_BRK_UNRECOGNIZED_OPTION, /**< !!option in RBBI rules not recognized. */ + U_BRK_MALFORMED_RULE_TAG, /**< The {nnn} tag on a rule is malformed */ +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal BreakIterator error code. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_BRK_ERROR_LIMIT, +#endif // U_HIDE_DEPRECATED_API + + /* + * Error codes in the range 0x10300-0x103ff are reserved for regular expression related errors. + */ + U_REGEX_INTERNAL_ERROR=0x10300, /**< An internal error (bug) was detected. */ + U_REGEX_ERROR_START=0x10300, /**< Start of codes indicating Regexp failures */ + U_REGEX_RULE_SYNTAX, /**< Syntax error in regexp pattern. */ + U_REGEX_INVALID_STATE, /**< RegexMatcher in invalid state for requested operation */ + U_REGEX_BAD_ESCAPE_SEQUENCE, /**< Unrecognized backslash escape sequence in pattern */ + U_REGEX_PROPERTY_SYNTAX, /**< Incorrect Unicode property */ + U_REGEX_UNIMPLEMENTED, /**< Use of regexp feature that is not yet implemented. */ + U_REGEX_MISMATCHED_PAREN, /**< Incorrectly nested parentheses in regexp pattern. */ + U_REGEX_NUMBER_TOO_BIG, /**< Decimal number is too large. */ + U_REGEX_BAD_INTERVAL, /**< Error in {min,max} interval */ + U_REGEX_MAX_LT_MIN, /**< In {min,max}, max is less than min. */ + U_REGEX_INVALID_BACK_REF, /**< Back-reference to a non-existent capture group. */ + U_REGEX_INVALID_FLAG, /**< Invalid value for match mode flags. */ + U_REGEX_LOOK_BEHIND_LIMIT, /**< Look-Behind pattern matches must have a bounded maximum length. */ + U_REGEX_SET_CONTAINS_STRING, /**< Regexps cannot have UnicodeSets containing strings.*/ +#ifndef U_HIDE_DEPRECATED_API + U_REGEX_OCTAL_TOO_BIG, /**< Octal character constants must be <= 0377. @deprecated ICU 54. This error cannot occur. */ +#endif /* U_HIDE_DEPRECATED_API */ + U_REGEX_MISSING_CLOSE_BRACKET=U_REGEX_SET_CONTAINS_STRING+2, /**< Missing closing bracket on a bracket expression. */ + U_REGEX_INVALID_RANGE, /**< In a character range [x-y], x is greater than y. */ + U_REGEX_STACK_OVERFLOW, /**< Regular expression backtrack stack overflow. */ + U_REGEX_TIME_OUT, /**< Maximum allowed match time exceeded */ + U_REGEX_STOPPED_BY_CALLER, /**< Matching operation aborted by user callback fn. */ + U_REGEX_PATTERN_TOO_BIG, /**< Pattern exceeds limits on size or complexity. @stable ICU 55 */ + U_REGEX_INVALID_CAPTURE_GROUP_NAME, /**< Invalid capture group name. @stable ICU 55 */ +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal regular expression error code. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_REGEX_ERROR_LIMIT=U_REGEX_STOPPED_BY_CALLER+3, +#endif // U_HIDE_DEPRECATED_API + + /* + * Error codes in the range 0x10400-0x104ff are reserved for IDNA related error codes. + */ + U_IDNA_PROHIBITED_ERROR=0x10400, + U_IDNA_ERROR_START=0x10400, + U_IDNA_UNASSIGNED_ERROR, + U_IDNA_CHECK_BIDI_ERROR, + U_IDNA_STD3_ASCII_RULES_ERROR, + U_IDNA_ACE_PREFIX_ERROR, + U_IDNA_VERIFICATION_ERROR, + U_IDNA_LABEL_TOO_LONG_ERROR, + U_IDNA_ZERO_LENGTH_LABEL_ERROR, + U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR, +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal IDNA error code. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_IDNA_ERROR_LIMIT, +#endif // U_HIDE_DEPRECATED_API + /* + * Aliases for StringPrep + */ + U_STRINGPREP_PROHIBITED_ERROR = U_IDNA_PROHIBITED_ERROR, + U_STRINGPREP_UNASSIGNED_ERROR = U_IDNA_UNASSIGNED_ERROR, + U_STRINGPREP_CHECK_BIDI_ERROR = U_IDNA_CHECK_BIDI_ERROR, + + /* + * Error codes in the range 0x10500-0x105ff are reserved for Plugin related error codes. + */ + U_PLUGIN_ERROR_START=0x10500, /**< Start of codes indicating plugin failures */ + U_PLUGIN_TOO_HIGH=0x10500, /**< The plugin's level is too high to be loaded right now. */ + U_PLUGIN_DIDNT_SET_LEVEL, /**< The plugin didn't call uplug_setPlugLevel in response to a QUERY */ +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal plug-in error code. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_PLUGIN_ERROR_LIMIT, +#endif // U_HIDE_DEPRECATED_API + +#ifndef U_HIDE_DEPRECATED_API + /** + * One more than the highest normal error code. + * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. + */ + U_ERROR_LIMIT=U_PLUGIN_ERROR_LIMIT +#endif // U_HIDE_DEPRECATED_API +} UErrorCode; + +/* Use the following to determine if an UErrorCode represents */ +/* operational success or failure. */ + +#ifdef __cplusplus + /** + * Does the error code indicate success? + * @stable ICU 2.0 + */ + static + inline UBool U_SUCCESS(UErrorCode code) { return (UBool)(code<=U_ZERO_ERROR); } + /** + * Does the error code indicate a failure? + * @stable ICU 2.0 + */ + static + inline UBool U_FAILURE(UErrorCode code) { return (UBool)(code>U_ZERO_ERROR); } +#else + /** + * Does the error code indicate success? + * @stable ICU 2.0 + */ +# define U_SUCCESS(x) ((x)<=U_ZERO_ERROR) + /** + * Does the error code indicate a failure? + * @stable ICU 2.0 + */ +# define U_FAILURE(x) ((x)>U_ZERO_ERROR) +#endif + +/** + * Return a string for a UErrorCode value. + * The string will be the same as the name of the error code constant + * in the UErrorCode enum above. + * @stable ICU 2.0 + */ +U_CAPI const char * U_EXPORT2 +u_errorName(UErrorCode code); + + +#endif /* _UTYPES */ diff --git a/deps/icu-small/source/common/unicode/uvernum.h b/deps/icu-small/source/common/unicode/uvernum.h index a93f3509ef3621..de49a7133acb68 100644 --- a/deps/icu-small/source/common/unicode/uvernum.h +++ b/deps/icu-small/source/common/unicode/uvernum.h @@ -1,191 +1,191 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2000-2016, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* -* file name: uvernum.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* Created by: Vladimir Weinstein -* Updated by: Steven R. Loomis -* -*/ - -/** - * \file - * \brief C API: definitions of ICU version numbers - * - * This file is included by uversion.h and other files. This file contains only - * macros and definitions. The actual version numbers are defined here. - */ - - /* - * IMPORTANT: When updating version, the following things need to be done: - * source/common/unicode/uvernum.h - this file: update major, minor, - * patchlevel, suffix, version, short version constants, namespace, - * renaming macro, and copyright - * - * The following files need to be updated as well, which can be done - * by running the UNIX makefile target 'update-windows-makefiles' in icu4c/source. - * - * source/allinone/Build.Windows.IcuVersion.props - Update the IcuMajorVersion - * source/data/makedata.mak - change U_ICUDATA_NAME so that it contains - * the new major/minor combination, and UNICODE_VERSION - * for the Unicode version. - */ - -#ifndef UVERNUM_H -#define UVERNUM_H - -/** The standard copyright notice that gets compiled into each library. - * This value will change in the subsequent releases of ICU - * @stable ICU 2.4 - */ -#define U_COPYRIGHT_STRING \ - " Copyright (C) 2016 and later: Unicode, Inc. and others. License & terms of use: http://www.unicode.org/copyright.html " - -/** The current ICU major version as an integer. - * This value will change in the subsequent releases of ICU - * @stable ICU 2.4 - */ -#define U_ICU_VERSION_MAJOR_NUM 72 - -/** The current ICU minor version as an integer. - * This value will change in the subsequent releases of ICU - * @stable ICU 2.6 - */ -#define U_ICU_VERSION_MINOR_NUM 1 - -/** The current ICU patchlevel version as an integer. - * This value will change in the subsequent releases of ICU - * @stable ICU 2.4 - */ -#define U_ICU_VERSION_PATCHLEVEL_NUM 0 - -/** The current ICU build level version as an integer. - * This value is for use by ICU clients. It defaults to 0. - * @stable ICU 4.0 - */ -#ifndef U_ICU_VERSION_BUILDLEVEL_NUM -#define U_ICU_VERSION_BUILDLEVEL_NUM 0 -#endif - -/** Glued version suffix for renamers - * This value will change in the subsequent releases of ICU - * @stable ICU 2.6 - */ -#define U_ICU_VERSION_SUFFIX _72 - -/** - * \def U_DEF2_ICU_ENTRY_POINT_RENAME - * @internal - */ -/** - * \def U_DEF_ICU_ENTRY_POINT_RENAME - * @internal - */ -/** Glued version suffix function for renamers - * This value will change in the subsequent releases of ICU. - * If a custom suffix (such as matching library suffixes) is desired, this can be modified. - * Note that if present, platform.h may contain an earlier definition of this macro. - * \def U_ICU_ENTRY_POINT_RENAME - * @stable ICU 4.2 - */ -/** - * Disable the version suffix. Use the custom suffix if exists. - * \def U_DISABLE_VERSION_SUFFIX - * @internal - */ -#ifndef U_DISABLE_VERSION_SUFFIX -#define U_DISABLE_VERSION_SUFFIX 0 -#endif - -#ifndef U_ICU_ENTRY_POINT_RENAME -#ifdef U_HAVE_LIB_SUFFIX -# if !U_DISABLE_VERSION_SUFFIX -# define U_DEF_ICU_ENTRY_POINT_RENAME(x,y,z) x ## y ## z -# define U_DEF2_ICU_ENTRY_POINT_RENAME(x,y,z) U_DEF_ICU_ENTRY_POINT_RENAME(x,y,z) -# define U_ICU_ENTRY_POINT_RENAME(x) U_DEF2_ICU_ENTRY_POINT_RENAME(x,U_ICU_VERSION_SUFFIX,U_LIB_SUFFIX_C_NAME) -# else -# define U_DEF_ICU_ENTRY_POINT_RENAME(x,y) x ## y -# define U_DEF2_ICU_ENTRY_POINT_RENAME(x,y) U_DEF_ICU_ENTRY_POINT_RENAME(x,y) -# define U_ICU_ENTRY_POINT_RENAME(x) U_DEF2_ICU_ENTRY_POINT_RENAME(x,U_LIB_SUFFIX_C_NAME) -# endif -#else -# if !U_DISABLE_VERSION_SUFFIX -# define U_DEF_ICU_ENTRY_POINT_RENAME(x,y) x ## y -# define U_DEF2_ICU_ENTRY_POINT_RENAME(x,y) U_DEF_ICU_ENTRY_POINT_RENAME(x,y) -# define U_ICU_ENTRY_POINT_RENAME(x) U_DEF2_ICU_ENTRY_POINT_RENAME(x,U_ICU_VERSION_SUFFIX) -# else -# define U_ICU_ENTRY_POINT_RENAME(x) x -# endif -#endif -#endif - -/** The current ICU library version as a dotted-decimal string. The patchlevel - * only appears in this string if it non-zero. - * This value will change in the subsequent releases of ICU - * @stable ICU 2.4 - */ -#define U_ICU_VERSION "72.1" - -/** - * The current ICU library major version number as a string, for library name suffixes. - * This value will change in subsequent releases of ICU. - * - * Until ICU 4.8, this was the combination of the single-digit major and minor ICU version numbers - * into one string without dots ("48"). - * Since ICU 49, it is the double-digit major ICU version number. - * See https://unicode-org.github.io/icu/userguide/design#version-numbers-in-icu - * - * @stable ICU 2.6 - */ -#define U_ICU_VERSION_SHORT "72" - -#ifndef U_HIDE_INTERNAL_API -/** Data version in ICU4C. - * @internal ICU 4.4 Internal Use Only - **/ -#define U_ICU_DATA_VERSION "72.1" -#endif /* U_HIDE_INTERNAL_API */ - -/*=========================================================================== - * ICU collation framework version information - * Version info that can be obtained from a collator is affected by these - * numbers in a secret and magic way. Please use collator version as whole - *=========================================================================== - */ - -/** - * Collation runtime version (sort key generator, strcoll). - * If the version is different, sort keys for the same string could be different. - * This value may change in subsequent releases of ICU. - * @stable ICU 2.4 - */ -#define UCOL_RUNTIME_VERSION 9 - -/** - * Collation builder code version. - * When this is different, the same tailoring might result - * in assigning different collation elements to code points. - * This value may change in subsequent releases of ICU. - * @stable ICU 2.4 - */ -#define UCOL_BUILDER_VERSION 9 - -#ifndef U_HIDE_DEPRECATED_API -/** - * Constant 1. - * This was intended to be the version of collation tailorings, - * but instead the tailoring data carries a version number. - * @deprecated ICU 54 - */ -#define UCOL_TAILORINGS_VERSION 1 -#endif /* U_HIDE_DEPRECATED_API */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2000-2016, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* +* file name: uvernum.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* Created by: Vladimir Weinstein +* Updated by: Steven R. Loomis +* +*/ + +/** + * \file + * \brief C API: definitions of ICU version numbers + * + * This file is included by uversion.h and other files. This file contains only + * macros and definitions. The actual version numbers are defined here. + */ + + /* + * IMPORTANT: When updating version, the following things need to be done: + * source/common/unicode/uvernum.h - this file: update major, minor, + * patchlevel, suffix, version, short version constants, namespace, + * renaming macro, and copyright + * + * The following files need to be updated as well, which can be done + * by running the UNIX makefile target 'update-windows-makefiles' in icu4c/source. + * + * source/allinone/Build.Windows.IcuVersion.props - Update the IcuMajorVersion + * source/data/makedata.mak - change U_ICUDATA_NAME so that it contains + * the new major/minor combination, and UNICODE_VERSION + * for the Unicode version. + */ + +#ifndef UVERNUM_H +#define UVERNUM_H + +/** The standard copyright notice that gets compiled into each library. + * This value will change in the subsequent releases of ICU + * @stable ICU 2.4 + */ +#define U_COPYRIGHT_STRING \ + " Copyright (C) 2016 and later: Unicode, Inc. and others. License & terms of use: http://www.unicode.org/copyright.html " + +/** The current ICU major version as an integer. + * This value will change in the subsequent releases of ICU + * @stable ICU 2.4 + */ +#define U_ICU_VERSION_MAJOR_NUM 73 + +/** The current ICU minor version as an integer. + * This value will change in the subsequent releases of ICU + * @stable ICU 2.6 + */ +#define U_ICU_VERSION_MINOR_NUM 1 + +/** The current ICU patchlevel version as an integer. + * This value will change in the subsequent releases of ICU + * @stable ICU 2.4 + */ +#define U_ICU_VERSION_PATCHLEVEL_NUM 0 + +/** The current ICU build level version as an integer. + * This value is for use by ICU clients. It defaults to 0. + * @stable ICU 4.0 + */ +#ifndef U_ICU_VERSION_BUILDLEVEL_NUM +#define U_ICU_VERSION_BUILDLEVEL_NUM 0 +#endif + +/** Glued version suffix for renamers + * This value will change in the subsequent releases of ICU + * @stable ICU 2.6 + */ +#define U_ICU_VERSION_SUFFIX _73 + +/** + * \def U_DEF2_ICU_ENTRY_POINT_RENAME + * @internal + */ +/** + * \def U_DEF_ICU_ENTRY_POINT_RENAME + * @internal + */ +/** Glued version suffix function for renamers + * This value will change in the subsequent releases of ICU. + * If a custom suffix (such as matching library suffixes) is desired, this can be modified. + * Note that if present, platform.h may contain an earlier definition of this macro. + * \def U_ICU_ENTRY_POINT_RENAME + * @stable ICU 4.2 + */ +/** + * Disable the version suffix. Use the custom suffix if exists. + * \def U_DISABLE_VERSION_SUFFIX + * @internal + */ +#ifndef U_DISABLE_VERSION_SUFFIX +#define U_DISABLE_VERSION_SUFFIX 0 +#endif + +#ifndef U_ICU_ENTRY_POINT_RENAME +#ifdef U_HAVE_LIB_SUFFIX +# if !U_DISABLE_VERSION_SUFFIX +# define U_DEF_ICU_ENTRY_POINT_RENAME(x,y,z) x ## y ## z +# define U_DEF2_ICU_ENTRY_POINT_RENAME(x,y,z) U_DEF_ICU_ENTRY_POINT_RENAME(x,y,z) +# define U_ICU_ENTRY_POINT_RENAME(x) U_DEF2_ICU_ENTRY_POINT_RENAME(x,U_ICU_VERSION_SUFFIX,U_LIB_SUFFIX_C_NAME) +# else +# define U_DEF_ICU_ENTRY_POINT_RENAME(x,y) x ## y +# define U_DEF2_ICU_ENTRY_POINT_RENAME(x,y) U_DEF_ICU_ENTRY_POINT_RENAME(x,y) +# define U_ICU_ENTRY_POINT_RENAME(x) U_DEF2_ICU_ENTRY_POINT_RENAME(x,U_LIB_SUFFIX_C_NAME) +# endif +#else +# if !U_DISABLE_VERSION_SUFFIX +# define U_DEF_ICU_ENTRY_POINT_RENAME(x,y) x ## y +# define U_DEF2_ICU_ENTRY_POINT_RENAME(x,y) U_DEF_ICU_ENTRY_POINT_RENAME(x,y) +# define U_ICU_ENTRY_POINT_RENAME(x) U_DEF2_ICU_ENTRY_POINT_RENAME(x,U_ICU_VERSION_SUFFIX) +# else +# define U_ICU_ENTRY_POINT_RENAME(x) x +# endif +#endif +#endif + +/** The current ICU library version as a dotted-decimal string. The patchlevel + * only appears in this string if it non-zero. + * This value will change in the subsequent releases of ICU + * @stable ICU 2.4 + */ +#define U_ICU_VERSION "73.1" + +/** + * The current ICU library major version number as a string, for library name suffixes. + * This value will change in subsequent releases of ICU. + * + * Until ICU 4.8, this was the combination of the single-digit major and minor ICU version numbers + * into one string without dots ("48"). + * Since ICU 49, it is the double-digit major ICU version number. + * See https://unicode-org.github.io/icu/userguide/design#version-numbers-in-icu + * + * @stable ICU 2.6 + */ +#define U_ICU_VERSION_SHORT "73" + +#ifndef U_HIDE_INTERNAL_API +/** Data version in ICU4C. + * @internal ICU 4.4 Internal Use Only + **/ +#define U_ICU_DATA_VERSION "73.1" +#endif /* U_HIDE_INTERNAL_API */ + +/*=========================================================================== + * ICU collation framework version information + * Version info that can be obtained from a collator is affected by these + * numbers in a secret and magic way. Please use collator version as whole + *=========================================================================== + */ + +/** + * Collation runtime version (sort key generator, strcoll). + * If the version is different, sort keys for the same string could be different. + * This value may change in subsequent releases of ICU. + * @stable ICU 2.4 + */ +#define UCOL_RUNTIME_VERSION 9 + +/** + * Collation builder code version. + * When this is different, the same tailoring might result + * in assigning different collation elements to code points. + * This value may change in subsequent releases of ICU. + * @stable ICU 2.4 + */ +#define UCOL_BUILDER_VERSION 9 + +#ifndef U_HIDE_DEPRECATED_API +/** + * Constant 1. + * This was intended to be the version of collation tailorings, + * but instead the tailoring data carries a version number. + * @deprecated ICU 54 + */ +#define UCOL_TAILORINGS_VERSION 1 +#endif /* U_HIDE_DEPRECATED_API */ + +#endif diff --git a/deps/icu-small/source/common/unicode/uversion.h b/deps/icu-small/source/common/unicode/uversion.h index 113568df8c127d..aab275553313bb 100644 --- a/deps/icu-small/source/common/unicode/uversion.h +++ b/deps/icu-small/source/common/unicode/uversion.h @@ -1,187 +1,187 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2000-2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* -* file name: uversion.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* Created by: Vladimir Weinstein -* -* Gets included by utypes.h and Windows .rc files -*/ - -/** - * \file - * \brief C API: API for accessing ICU version numbers. - */ -/*===========================================================================*/ -/* Main ICU version information */ -/*===========================================================================*/ - -#ifndef UVERSION_H -#define UVERSION_H - -#include "unicode/umachine.h" - -/* Actual version info lives in uvernum.h */ -#include "unicode/uvernum.h" - -/** Maximum length of the copyright string. - * @stable ICU 2.4 - */ -#define U_COPYRIGHT_STRING_LENGTH 128 - -/** An ICU version consists of up to 4 numbers from 0..255. - * @stable ICU 2.4 - */ -#define U_MAX_VERSION_LENGTH 4 - -/** In a string, ICU version fields are delimited by dots. - * @stable ICU 2.4 - */ -#define U_VERSION_DELIMITER '.' - -/** The maximum length of an ICU version string. - * @stable ICU 2.4 - */ -#define U_MAX_VERSION_STRING_LENGTH 20 - -/** The binary form of a version on ICU APIs is an array of 4 uint8_t. - * To compare two versions, use memcmp(v1,v2,sizeof(UVersionInfo)). - * @stable ICU 2.4 - */ -typedef uint8_t UVersionInfo[U_MAX_VERSION_LENGTH]; - -/*===========================================================================*/ -/* C++ namespace if supported. Versioned unless versioning is disabled. */ -/*===========================================================================*/ - -/* Define C++ namespace symbols. */ -#ifdef __cplusplus - -/** - * \def U_NAMESPACE_BEGIN - * This is used to begin a declaration of a public ICU C++ API within - * versioned-ICU-namespace block. - * - * @stable ICU 2.4 - */ - -/** - * \def U_NAMESPACE_END - * This is used to end a declaration of a public ICU C++ API. - * It ends the versioned-ICU-namespace block begun by U_NAMESPACE_BEGIN. - * - * @stable ICU 2.4 - */ - -/** - * \def U_NAMESPACE_USE - * This is used to specify that the rest of the code uses the - * public ICU C++ API namespace. - * @stable ICU 2.4 - */ - -/** - * \def U_NAMESPACE_QUALIFIER - * This is used to qualify that a function or class is part of - * the public ICU C++ API namespace. - * - * This macro is unnecessary since ICU 49 requires namespace support. - * You can just use "icu::" instead. - * @stable ICU 2.4 - */ - -# if U_DISABLE_RENAMING -# define U_ICU_NAMESPACE icu - namespace U_ICU_NAMESPACE { } -# else -# define U_ICU_NAMESPACE U_ICU_ENTRY_POINT_RENAME(icu) - namespace U_ICU_NAMESPACE { } - namespace icu = U_ICU_NAMESPACE; -# endif - -# define U_NAMESPACE_BEGIN namespace U_ICU_NAMESPACE { -# define U_NAMESPACE_END } -# define U_NAMESPACE_USE using namespace U_ICU_NAMESPACE; -# define U_NAMESPACE_QUALIFIER U_ICU_NAMESPACE:: - -# ifndef U_USING_ICU_NAMESPACE -# if defined(U_COMBINED_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) || \ - defined(U_I18N_IMPLEMENTATION) || defined(U_IO_IMPLEMENTATION) || \ - defined(U_LAYOUTEX_IMPLEMENTATION) || defined(U_TOOLUTIL_IMPLEMENTATION) -# define U_USING_ICU_NAMESPACE 0 -# else -# define U_USING_ICU_NAMESPACE 0 -# endif -# endif -# if U_USING_ICU_NAMESPACE - U_NAMESPACE_USE -# endif -#endif /* __cplusplus */ - -/*===========================================================================*/ -/* General version helper functions. Definitions in putil.c */ -/*===========================================================================*/ - -/** - * Parse a string with dotted-decimal version information and - * fill in a UVersionInfo structure with the result. - * Definition of this function lives in putil.c - * - * @param versionArray The destination structure for the version information. - * @param versionString A string with dotted-decimal version information, - * with up to four non-negative number fields with - * values of up to 255 each. - * @stable ICU 2.4 - */ -U_CAPI void U_EXPORT2 -u_versionFromString(UVersionInfo versionArray, const char *versionString); - -/** - * Parse a Unicode string with dotted-decimal version information and - * fill in a UVersionInfo structure with the result. - * Definition of this function lives in putil.c - * - * @param versionArray The destination structure for the version information. - * @param versionString A Unicode string with dotted-decimal version - * information, with up to four non-negative number - * fields with values of up to 255 each. - * @stable ICU 4.2 - */ -U_CAPI void U_EXPORT2 -u_versionFromUString(UVersionInfo versionArray, const UChar *versionString); - - -/** - * Write a string with dotted-decimal version information according - * to the input UVersionInfo. - * Definition of this function lives in putil.c - * - * @param versionArray The version information to be written as a string. - * @param versionString A string buffer that will be filled in with - * a string corresponding to the numeric version - * information in versionArray. - * The buffer size must be at least U_MAX_VERSION_STRING_LENGTH. - * @stable ICU 2.4 - */ -U_CAPI void U_EXPORT2 -u_versionToString(const UVersionInfo versionArray, char *versionString); - -/** - * Gets the ICU release version. The version array stores the version information - * for ICU. For example, release "1.3.31.2" is then represented as 0x01031F02. - * Definition of this function lives in putil.c - * - * @param versionArray the version # information, the result will be filled in - * @stable ICU 2.0 - */ -U_CAPI void U_EXPORT2 -u_getVersion(UVersionInfo versionArray); -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2000-2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* +* file name: uversion.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* Created by: Vladimir Weinstein +* +* Gets included by utypes.h and Windows .rc files +*/ + +/** + * \file + * \brief C API: API for accessing ICU version numbers. + */ +/*===========================================================================*/ +/* Main ICU version information */ +/*===========================================================================*/ + +#ifndef UVERSION_H +#define UVERSION_H + +#include "unicode/umachine.h" + +/* Actual version info lives in uvernum.h */ +#include "unicode/uvernum.h" + +/** Maximum length of the copyright string. + * @stable ICU 2.4 + */ +#define U_COPYRIGHT_STRING_LENGTH 128 + +/** An ICU version consists of up to 4 numbers from 0..255. + * @stable ICU 2.4 + */ +#define U_MAX_VERSION_LENGTH 4 + +/** In a string, ICU version fields are delimited by dots. + * @stable ICU 2.4 + */ +#define U_VERSION_DELIMITER '.' + +/** The maximum length of an ICU version string. + * @stable ICU 2.4 + */ +#define U_MAX_VERSION_STRING_LENGTH 20 + +/** The binary form of a version on ICU APIs is an array of 4 uint8_t. + * To compare two versions, use memcmp(v1,v2,sizeof(UVersionInfo)). + * @stable ICU 2.4 + */ +typedef uint8_t UVersionInfo[U_MAX_VERSION_LENGTH]; + +/*===========================================================================*/ +/* C++ namespace if supported. Versioned unless versioning is disabled. */ +/*===========================================================================*/ + +/* Define C++ namespace symbols. */ +#ifdef __cplusplus + +/** + * \def U_NAMESPACE_BEGIN + * This is used to begin a declaration of a public ICU C++ API within + * versioned-ICU-namespace block. + * + * @stable ICU 2.4 + */ + +/** + * \def U_NAMESPACE_END + * This is used to end a declaration of a public ICU C++ API. + * It ends the versioned-ICU-namespace block begun by U_NAMESPACE_BEGIN. + * + * @stable ICU 2.4 + */ + +/** + * \def U_NAMESPACE_USE + * This is used to specify that the rest of the code uses the + * public ICU C++ API namespace. + * @stable ICU 2.4 + */ + +/** + * \def U_NAMESPACE_QUALIFIER + * This is used to qualify that a function or class is part of + * the public ICU C++ API namespace. + * + * This macro is unnecessary since ICU 49 requires namespace support. + * You can just use "icu::" instead. + * @stable ICU 2.4 + */ + +# if U_DISABLE_RENAMING +# define U_ICU_NAMESPACE icu + namespace U_ICU_NAMESPACE { } +# else +# define U_ICU_NAMESPACE U_ICU_ENTRY_POINT_RENAME(icu) + namespace U_ICU_NAMESPACE { } + namespace icu = U_ICU_NAMESPACE; +# endif + +# define U_NAMESPACE_BEGIN namespace U_ICU_NAMESPACE { +# define U_NAMESPACE_END } +# define U_NAMESPACE_USE using namespace U_ICU_NAMESPACE; +# define U_NAMESPACE_QUALIFIER U_ICU_NAMESPACE:: + +# ifndef U_USING_ICU_NAMESPACE +# if defined(U_COMBINED_IMPLEMENTATION) || defined(U_COMMON_IMPLEMENTATION) || \ + defined(U_I18N_IMPLEMENTATION) || defined(U_IO_IMPLEMENTATION) || \ + defined(U_LAYOUTEX_IMPLEMENTATION) || defined(U_TOOLUTIL_IMPLEMENTATION) +# define U_USING_ICU_NAMESPACE 0 +# else +# define U_USING_ICU_NAMESPACE 0 +# endif +# endif +# if U_USING_ICU_NAMESPACE + U_NAMESPACE_USE +# endif +#endif /* __cplusplus */ + +/*===========================================================================*/ +/* General version helper functions. Definitions in putil.c */ +/*===========================================================================*/ + +/** + * Parse a string with dotted-decimal version information and + * fill in a UVersionInfo structure with the result. + * Definition of this function lives in putil.c + * + * @param versionArray The destination structure for the version information. + * @param versionString A string with dotted-decimal version information, + * with up to four non-negative number fields with + * values of up to 255 each. + * @stable ICU 2.4 + */ +U_CAPI void U_EXPORT2 +u_versionFromString(UVersionInfo versionArray, const char *versionString); + +/** + * Parse a Unicode string with dotted-decimal version information and + * fill in a UVersionInfo structure with the result. + * Definition of this function lives in putil.c + * + * @param versionArray The destination structure for the version information. + * @param versionString A Unicode string with dotted-decimal version + * information, with up to four non-negative number + * fields with values of up to 255 each. + * @stable ICU 4.2 + */ +U_CAPI void U_EXPORT2 +u_versionFromUString(UVersionInfo versionArray, const UChar *versionString); + + +/** + * Write a string with dotted-decimal version information according + * to the input UVersionInfo. + * Definition of this function lives in putil.c + * + * @param versionArray The version information to be written as a string. + * @param versionString A string buffer that will be filled in with + * a string corresponding to the numeric version + * information in versionArray. + * The buffer size must be at least U_MAX_VERSION_STRING_LENGTH. + * @stable ICU 2.4 + */ +U_CAPI void U_EXPORT2 +u_versionToString(const UVersionInfo versionArray, char *versionString); + +/** + * Gets the ICU release version. The version array stores the version information + * for ICU. For example, release "1.3.31.2" is then represented as 0x01031F02. + * Definition of this function lives in putil.c + * + * @param versionArray the version # information, the result will be filled in + * @stable ICU 2.0 + */ +U_CAPI void U_EXPORT2 +u_getVersion(UVersionInfo versionArray); +#endif diff --git a/deps/icu-small/source/common/unifiedcache.cpp b/deps/icu-small/source/common/unifiedcache.cpp index cfb000b2c8e683..5662cf051751c7 100644 --- a/deps/icu-small/source/common/unifiedcache.cpp +++ b/deps/icu-small/source/common/unifiedcache.cpp @@ -1,522 +1,522 @@ -// © 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 unifiedcache.cpp -****************************************************************************** -*/ - -#include "unifiedcache.h" - -#include // For std::max() -#include - -#include "uassert.h" -#include "uhash.h" -#include "ucln_cmn.h" - -static icu::UnifiedCache *gCache = NULL; -static std::mutex *gCacheMutex = nullptr; -static std::condition_variable *gInProgressValueAddedCond; -static icu::UInitOnce gCacheInitOnce {}; - -static const int32_t MAX_EVICT_ITERATIONS = 10; -static const int32_t DEFAULT_MAX_UNUSED = 1000; -static const int32_t DEFAULT_PERCENTAGE_OF_IN_USE = 100; - - -U_CDECL_BEGIN -static UBool U_CALLCONV unifiedcache_cleanup() { - gCacheInitOnce.reset(); - delete gCache; - gCache = nullptr; - gCacheMutex->~mutex(); - gCacheMutex = nullptr; - gInProgressValueAddedCond->~condition_variable(); - gInProgressValueAddedCond = nullptr; - return true; -} -U_CDECL_END - - -U_NAMESPACE_BEGIN - -int32_t U_EXPORT2 -ucache_hashKeys(const UHashTok key) { - const CacheKeyBase *ckey = (const CacheKeyBase *) key.pointer; - return ckey->hashCode(); -} - -UBool U_EXPORT2 -ucache_compareKeys(const UHashTok key1, const UHashTok key2) { - const CacheKeyBase *p1 = (const CacheKeyBase *) key1.pointer; - const CacheKeyBase *p2 = (const CacheKeyBase *) key2.pointer; - return *p1 == *p2; -} - -void U_EXPORT2 -ucache_deleteKey(void *obj) { - CacheKeyBase *p = (CacheKeyBase *) obj; - delete p; -} - -CacheKeyBase::~CacheKeyBase() { -} - -static void U_CALLCONV cacheInit(UErrorCode &status) { - U_ASSERT(gCache == NULL); - ucln_common_registerCleanup( - UCLN_COMMON_UNIFIED_CACHE, unifiedcache_cleanup); - - gCacheMutex = STATIC_NEW(std::mutex); - gInProgressValueAddedCond = STATIC_NEW(std::condition_variable); - gCache = new UnifiedCache(status); - if (gCache == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } - if (U_FAILURE(status)) { - delete gCache; - gCache = NULL; - return; - } -} - -UnifiedCache *UnifiedCache::getInstance(UErrorCode &status) { - umtx_initOnce(gCacheInitOnce, &cacheInit, status); - if (U_FAILURE(status)) { - return NULL; - } - U_ASSERT(gCache != NULL); - return gCache; -} - -UnifiedCache::UnifiedCache(UErrorCode &status) : - fHashtable(NULL), - fEvictPos(UHASH_FIRST), - fNumValuesTotal(0), - fNumValuesInUse(0), - fMaxUnused(DEFAULT_MAX_UNUSED), - fMaxPercentageOfInUse(DEFAULT_PERCENTAGE_OF_IN_USE), - fAutoEvictedCount(0), - fNoValue(nullptr) { - if (U_FAILURE(status)) { - return; - } - fNoValue = new SharedObject(); - if (fNoValue == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - fNoValue->softRefCount = 1; // Add fake references to prevent fNoValue from being deleted - fNoValue->hardRefCount = 1; // when other references to it are removed. - fNoValue->cachePtr = this; - - fHashtable = uhash_open( - &ucache_hashKeys, - &ucache_compareKeys, - NULL, - &status); - if (U_FAILURE(status)) { - return; - } - uhash_setKeyDeleter(fHashtable, &ucache_deleteKey); -} - -void UnifiedCache::setEvictionPolicy( - int32_t count, int32_t percentageOfInUseItems, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if (count < 0 || percentageOfInUseItems < 0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - std::lock_guard lock(*gCacheMutex); - fMaxUnused = count; - fMaxPercentageOfInUse = percentageOfInUseItems; -} - -int32_t UnifiedCache::unusedCount() const { - std::lock_guard lock(*gCacheMutex); - return uhash_count(fHashtable) - fNumValuesInUse; -} - -int64_t UnifiedCache::autoEvictedCount() const { - std::lock_guard lock(*gCacheMutex); - return fAutoEvictedCount; -} - -int32_t UnifiedCache::keyCount() const { - std::lock_guard lock(*gCacheMutex); - return uhash_count(fHashtable); -} - -void UnifiedCache::flush() const { - std::lock_guard lock(*gCacheMutex); - - // Use a loop in case cache items that are flushed held hard references to - // other cache items making those additional cache items eligible for - // flushing. - while (_flush(false)); -} - -void UnifiedCache::handleUnreferencedObject() const { - std::lock_guard lock(*gCacheMutex); - --fNumValuesInUse; - _runEvictionSlice(); -} - -#ifdef UNIFIED_CACHE_DEBUG -#include - -void UnifiedCache::dump() { - UErrorCode status = U_ZERO_ERROR; - const UnifiedCache *cache = getInstance(status); - if (U_FAILURE(status)) { - fprintf(stderr, "Unified Cache: Error fetching cache.\n"); - return; - } - cache->dumpContents(); -} - -void UnifiedCache::dumpContents() const { - std::lock_guard lock(*gCacheMutex); - _dumpContents(); -} - -// Dumps content of cache. -// On entry, gCacheMutex must be held. -// On exit, cache contents dumped to stderr. -void UnifiedCache::_dumpContents() const { - int32_t pos = UHASH_FIRST; - const UHashElement *element = uhash_nextElement(fHashtable, &pos); - char buffer[256]; - int32_t cnt = 0; - for (; element != NULL; element = uhash_nextElement(fHashtable, &pos)) { - const SharedObject *sharedObject = - (const SharedObject *) element->value.pointer; - const CacheKeyBase *key = - (const CacheKeyBase *) element->key.pointer; - if (sharedObject->hasHardReferences()) { - ++cnt; - fprintf( - stderr, - "Unified Cache: Key '%s', error %d, value %p, total refcount %d, soft refcount %d\n", - key->writeDescription(buffer, 256), - key->creationStatus, - sharedObject == fNoValue ? NULL :sharedObject, - sharedObject->getRefCount(), - sharedObject->getSoftRefCount()); - } - } - fprintf(stderr, "Unified Cache: %d out of a total of %d still have hard references\n", cnt, uhash_count(fHashtable)); -} -#endif - -UnifiedCache::~UnifiedCache() { - // Try our best to clean up first. - flush(); - { - // Now all that should be left in the cache are entries that refer to - // each other and entries with hard references from outside the cache. - // Nothing we can do about these so proceed to wipe out the cache. - std::lock_guard lock(*gCacheMutex); - _flush(true); - } - uhash_close(fHashtable); - fHashtable = nullptr; - delete fNoValue; - fNoValue = nullptr; -} - -const UHashElement * -UnifiedCache::_nextElement() const { - const UHashElement *element = uhash_nextElement(fHashtable, &fEvictPos); - if (element == NULL) { - fEvictPos = UHASH_FIRST; - return uhash_nextElement(fHashtable, &fEvictPos); - } - return element; -} - -UBool UnifiedCache::_flush(UBool all) const { - UBool result = false; - int32_t origSize = uhash_count(fHashtable); - for (int32_t i = 0; i < origSize; ++i) { - const UHashElement *element = _nextElement(); - if (element == nullptr) { - break; - } - if (all || _isEvictable(element)) { - const SharedObject *sharedObject = - (const SharedObject *) element->value.pointer; - U_ASSERT(sharedObject->cachePtr == this); - uhash_removeElement(fHashtable, element); - removeSoftRef(sharedObject); // Deletes the sharedObject when softRefCount goes to zero. - result = true; - } - } - return result; -} - -int32_t UnifiedCache::_computeCountOfItemsToEvict() const { - int32_t totalItems = uhash_count(fHashtable); - int32_t evictableItems = totalItems - fNumValuesInUse; - - int32_t unusedLimitByPercentage = fNumValuesInUse * fMaxPercentageOfInUse / 100; - int32_t unusedLimit = std::max(unusedLimitByPercentage, fMaxUnused); - int32_t countOfItemsToEvict = std::max(0, evictableItems - unusedLimit); - return countOfItemsToEvict; -} - -void UnifiedCache::_runEvictionSlice() const { - int32_t maxItemsToEvict = _computeCountOfItemsToEvict(); - if (maxItemsToEvict <= 0) { - return; - } - for (int32_t i = 0; i < MAX_EVICT_ITERATIONS; ++i) { - const UHashElement *element = _nextElement(); - if (element == nullptr) { - break; - } - if (_isEvictable(element)) { - const SharedObject *sharedObject = - (const SharedObject *) element->value.pointer; - uhash_removeElement(fHashtable, element); - removeSoftRef(sharedObject); // Deletes sharedObject when SoftRefCount goes to zero. - ++fAutoEvictedCount; - if (--maxItemsToEvict == 0) { - break; - } - } - } -} - -void UnifiedCache::_putNew( - const CacheKeyBase &key, - const SharedObject *value, - const UErrorCode creationStatus, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return; - } - CacheKeyBase *keyToAdopt = key.clone(); - if (keyToAdopt == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - keyToAdopt->fCreationStatus = creationStatus; - if (value->softRefCount == 0) { - _registerPrimary(keyToAdopt, value); - } - void *oldValue = uhash_put(fHashtable, keyToAdopt, (void *) value, &status); - U_ASSERT(oldValue == nullptr); - (void)oldValue; - if (U_SUCCESS(status)) { - value->softRefCount++; - } -} - -void UnifiedCache::_putIfAbsentAndGet( - const CacheKeyBase &key, - const SharedObject *&value, - UErrorCode &status) const { - std::lock_guard lock(*gCacheMutex); - const UHashElement *element = uhash_find(fHashtable, &key); - if (element != NULL && !_inProgress(element)) { - _fetch(element, value, status); - return; - } - if (element == NULL) { - UErrorCode putError = U_ZERO_ERROR; - // best-effort basis only. - _putNew(key, value, status, putError); - } else { - _put(element, value, status); - } - // Run an eviction slice. This will run even if we added a primary entry - // which doesn't increase the unused count, but that is still o.k - _runEvictionSlice(); -} - - -UBool UnifiedCache::_poll( - const CacheKeyBase &key, - const SharedObject *&value, - UErrorCode &status) const { - U_ASSERT(value == NULL); - U_ASSERT(status == U_ZERO_ERROR); - std::unique_lock lock(*gCacheMutex); - const UHashElement *element = uhash_find(fHashtable, &key); - - // If the hash table contains an inProgress placeholder entry for this key, - // this means that another thread is currently constructing the value object. - // Loop, waiting for that construction to complete. - while (element != NULL && _inProgress(element)) { - gInProgressValueAddedCond->wait(lock); - element = uhash_find(fHashtable, &key); - } - - // If the hash table contains an entry for the key, - // fetch out the contents and return them. - if (element != NULL) { - _fetch(element, value, status); - return true; - } - - // The hash table contained nothing for this key. - // Insert an inProgress place holder value. - // Our caller will create the final value and update the hash table. - _putNew(key, fNoValue, U_ZERO_ERROR, status); - return false; -} - -void UnifiedCache::_get( - const CacheKeyBase &key, - const SharedObject *&value, - const void *creationContext, - UErrorCode &status) const { - U_ASSERT(value == NULL); - U_ASSERT(status == U_ZERO_ERROR); - if (_poll(key, value, status)) { - if (value == fNoValue) { - SharedObject::clearPtr(value); - } - return; - } - if (U_FAILURE(status)) { - return; - } - value = key.createObject(creationContext, status); - U_ASSERT(value == NULL || value->hasHardReferences()); - U_ASSERT(value != NULL || status != U_ZERO_ERROR); - if (value == NULL) { - SharedObject::copyPtr(fNoValue, value); - } - _putIfAbsentAndGet(key, value, status); - if (value == fNoValue) { - SharedObject::clearPtr(value); - } -} - -void UnifiedCache::_registerPrimary( - const CacheKeyBase *theKey, const SharedObject *value) const { - theKey->fIsPrimary = true; - value->cachePtr = this; - ++fNumValuesTotal; - ++fNumValuesInUse; -} - -void UnifiedCache::_put( - const UHashElement *element, - const SharedObject *value, - const UErrorCode status) const { - U_ASSERT(_inProgress(element)); - const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; - const SharedObject *oldValue = (const SharedObject *) element->value.pointer; - theKey->fCreationStatus = status; - if (value->softRefCount == 0) { - _registerPrimary(theKey, value); - } - value->softRefCount++; - UHashElement *ptr = const_cast(element); - ptr->value.pointer = (void *) value; - U_ASSERT(oldValue == fNoValue); - removeSoftRef(oldValue); - - // Tell waiting threads that we replace in-progress status with - // an error. - gInProgressValueAddedCond->notify_all(); -} - -void UnifiedCache::_fetch( - const UHashElement *element, - const SharedObject *&value, - UErrorCode &status) const { - const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; - status = theKey->fCreationStatus; - - // Since we have the cache lock, calling regular SharedObject add/removeRef - // could cause us to deadlock on ourselves since they may need to lock - // the cache mutex. - removeHardRef(value); - value = static_cast(element->value.pointer); - addHardRef(value); -} - - -UBool UnifiedCache::_inProgress(const UHashElement* element) const { - UErrorCode status = U_ZERO_ERROR; - const SharedObject * value = NULL; - _fetch(element, value, status); - UBool result = _inProgress(value, status); - removeHardRef(value); - return result; -} - -UBool UnifiedCache::_inProgress( - const SharedObject* theValue, UErrorCode creationStatus) const { - return (theValue == fNoValue && creationStatus == U_ZERO_ERROR); -} - -UBool UnifiedCache::_isEvictable(const UHashElement *element) const -{ - const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; - const SharedObject *theValue = - (const SharedObject *) element->value.pointer; - - // Entries that are under construction are never evictable - if (_inProgress(theValue, theKey->fCreationStatus)) { - return false; - } - - // We can evict entries that are either not a primary or have just - // one reference (The one reference being from the cache itself). - return (!theKey->fIsPrimary || (theValue->softRefCount == 1 && theValue->noHardReferences())); -} - -void UnifiedCache::removeSoftRef(const SharedObject *value) const { - U_ASSERT(value->cachePtr == this); - U_ASSERT(value->softRefCount > 0); - if (--value->softRefCount == 0) { - --fNumValuesTotal; - if (value->noHardReferences()) { - delete value; - } else { - // This path only happens from flush(all). Which only happens from the - // UnifiedCache destructor. Nulling out value.cacheptr changes the behavior - // of value.removeRef(), causing the deletion to be done there. - value->cachePtr = nullptr; - } - } -} - -int32_t UnifiedCache::removeHardRef(const SharedObject *value) const { - int refCount = 0; - if (value) { - refCount = umtx_atomic_dec(&value->hardRefCount); - U_ASSERT(refCount >= 0); - if (refCount == 0) { - --fNumValuesInUse; - } - } - return refCount; -} - -int32_t UnifiedCache::addHardRef(const SharedObject *value) const { - int refCount = 0; - if (value) { - refCount = umtx_atomic_inc(&value->hardRefCount); - U_ASSERT(refCount >= 1); - if (refCount == 1) { - fNumValuesInUse++; - } - } - return refCount; -} - -U_NAMESPACE_END +// © 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 unifiedcache.cpp +****************************************************************************** +*/ + +#include "unifiedcache.h" + +#include // For std::max() +#include + +#include "uassert.h" +#include "uhash.h" +#include "ucln_cmn.h" + +static icu::UnifiedCache *gCache = nullptr; +static std::mutex *gCacheMutex = nullptr; +static std::condition_variable *gInProgressValueAddedCond; +static icu::UInitOnce gCacheInitOnce {}; + +static const int32_t MAX_EVICT_ITERATIONS = 10; +static const int32_t DEFAULT_MAX_UNUSED = 1000; +static const int32_t DEFAULT_PERCENTAGE_OF_IN_USE = 100; + + +U_CDECL_BEGIN +static UBool U_CALLCONV unifiedcache_cleanup() { + gCacheInitOnce.reset(); + delete gCache; + gCache = nullptr; + gCacheMutex->~mutex(); + gCacheMutex = nullptr; + gInProgressValueAddedCond->~condition_variable(); + gInProgressValueAddedCond = nullptr; + return true; +} +U_CDECL_END + + +U_NAMESPACE_BEGIN + +int32_t U_EXPORT2 +ucache_hashKeys(const UHashTok key) { + const CacheKeyBase *ckey = (const CacheKeyBase *) key.pointer; + return ckey->hashCode(); +} + +UBool U_EXPORT2 +ucache_compareKeys(const UHashTok key1, const UHashTok key2) { + const CacheKeyBase *p1 = (const CacheKeyBase *) key1.pointer; + const CacheKeyBase *p2 = (const CacheKeyBase *) key2.pointer; + return *p1 == *p2; +} + +void U_EXPORT2 +ucache_deleteKey(void *obj) { + CacheKeyBase *p = (CacheKeyBase *) obj; + delete p; +} + +CacheKeyBase::~CacheKeyBase() { +} + +static void U_CALLCONV cacheInit(UErrorCode &status) { + U_ASSERT(gCache == nullptr); + ucln_common_registerCleanup( + UCLN_COMMON_UNIFIED_CACHE, unifiedcache_cleanup); + + gCacheMutex = STATIC_NEW(std::mutex); + gInProgressValueAddedCond = STATIC_NEW(std::condition_variable); + gCache = new UnifiedCache(status); + if (gCache == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + if (U_FAILURE(status)) { + delete gCache; + gCache = nullptr; + return; + } +} + +UnifiedCache *UnifiedCache::getInstance(UErrorCode &status) { + umtx_initOnce(gCacheInitOnce, &cacheInit, status); + if (U_FAILURE(status)) { + return nullptr; + } + U_ASSERT(gCache != nullptr); + return gCache; +} + +UnifiedCache::UnifiedCache(UErrorCode &status) : + fHashtable(nullptr), + fEvictPos(UHASH_FIRST), + fNumValuesTotal(0), + fNumValuesInUse(0), + fMaxUnused(DEFAULT_MAX_UNUSED), + fMaxPercentageOfInUse(DEFAULT_PERCENTAGE_OF_IN_USE), + fAutoEvictedCount(0), + fNoValue(nullptr) { + if (U_FAILURE(status)) { + return; + } + fNoValue = new SharedObject(); + if (fNoValue == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + fNoValue->softRefCount = 1; // Add fake references to prevent fNoValue from being deleted + fNoValue->hardRefCount = 1; // when other references to it are removed. + fNoValue->cachePtr = this; + + fHashtable = uhash_open( + &ucache_hashKeys, + &ucache_compareKeys, + nullptr, + &status); + if (U_FAILURE(status)) { + return; + } + uhash_setKeyDeleter(fHashtable, &ucache_deleteKey); +} + +void UnifiedCache::setEvictionPolicy( + int32_t count, int32_t percentageOfInUseItems, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + if (count < 0 || percentageOfInUseItems < 0) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + std::lock_guard lock(*gCacheMutex); + fMaxUnused = count; + fMaxPercentageOfInUse = percentageOfInUseItems; +} + +int32_t UnifiedCache::unusedCount() const { + std::lock_guard lock(*gCacheMutex); + return uhash_count(fHashtable) - fNumValuesInUse; +} + +int64_t UnifiedCache::autoEvictedCount() const { + std::lock_guard lock(*gCacheMutex); + return fAutoEvictedCount; +} + +int32_t UnifiedCache::keyCount() const { + std::lock_guard lock(*gCacheMutex); + return uhash_count(fHashtable); +} + +void UnifiedCache::flush() const { + std::lock_guard lock(*gCacheMutex); + + // Use a loop in case cache items that are flushed held hard references to + // other cache items making those additional cache items eligible for + // flushing. + while (_flush(false)); +} + +void UnifiedCache::handleUnreferencedObject() const { + std::lock_guard lock(*gCacheMutex); + --fNumValuesInUse; + _runEvictionSlice(); +} + +#ifdef UNIFIED_CACHE_DEBUG +#include + +void UnifiedCache::dump() { + UErrorCode status = U_ZERO_ERROR; + const UnifiedCache *cache = getInstance(status); + if (U_FAILURE(status)) { + fprintf(stderr, "Unified Cache: Error fetching cache.\n"); + return; + } + cache->dumpContents(); +} + +void UnifiedCache::dumpContents() const { + std::lock_guard lock(*gCacheMutex); + _dumpContents(); +} + +// Dumps content of cache. +// On entry, gCacheMutex must be held. +// On exit, cache contents dumped to stderr. +void UnifiedCache::_dumpContents() const { + int32_t pos = UHASH_FIRST; + const UHashElement *element = uhash_nextElement(fHashtable, &pos); + char buffer[256]; + int32_t cnt = 0; + for (; element != nullptr; element = uhash_nextElement(fHashtable, &pos)) { + const SharedObject *sharedObject = + (const SharedObject *) element->value.pointer; + const CacheKeyBase *key = + (const CacheKeyBase *) element->key.pointer; + if (sharedObject->hasHardReferences()) { + ++cnt; + fprintf( + stderr, + "Unified Cache: Key '%s', error %d, value %p, total refcount %d, soft refcount %d\n", + key->writeDescription(buffer, 256), + key->creationStatus, + sharedObject == fNoValue ? nullptr :sharedObject, + sharedObject->getRefCount(), + sharedObject->getSoftRefCount()); + } + } + fprintf(stderr, "Unified Cache: %d out of a total of %d still have hard references\n", cnt, uhash_count(fHashtable)); +} +#endif + +UnifiedCache::~UnifiedCache() { + // Try our best to clean up first. + flush(); + { + // Now all that should be left in the cache are entries that refer to + // each other and entries with hard references from outside the cache. + // Nothing we can do about these so proceed to wipe out the cache. + std::lock_guard lock(*gCacheMutex); + _flush(true); + } + uhash_close(fHashtable); + fHashtable = nullptr; + delete fNoValue; + fNoValue = nullptr; +} + +const UHashElement * +UnifiedCache::_nextElement() const { + const UHashElement *element = uhash_nextElement(fHashtable, &fEvictPos); + if (element == nullptr) { + fEvictPos = UHASH_FIRST; + return uhash_nextElement(fHashtable, &fEvictPos); + } + return element; +} + +UBool UnifiedCache::_flush(UBool all) const { + UBool result = false; + int32_t origSize = uhash_count(fHashtable); + for (int32_t i = 0; i < origSize; ++i) { + const UHashElement *element = _nextElement(); + if (element == nullptr) { + break; + } + if (all || _isEvictable(element)) { + const SharedObject *sharedObject = + (const SharedObject *) element->value.pointer; + U_ASSERT(sharedObject->cachePtr == this); + uhash_removeElement(fHashtable, element); + removeSoftRef(sharedObject); // Deletes the sharedObject when softRefCount goes to zero. + result = true; + } + } + return result; +} + +int32_t UnifiedCache::_computeCountOfItemsToEvict() const { + int32_t totalItems = uhash_count(fHashtable); + int32_t evictableItems = totalItems - fNumValuesInUse; + + int32_t unusedLimitByPercentage = fNumValuesInUse * fMaxPercentageOfInUse / 100; + int32_t unusedLimit = std::max(unusedLimitByPercentage, fMaxUnused); + int32_t countOfItemsToEvict = std::max(0, evictableItems - unusedLimit); + return countOfItemsToEvict; +} + +void UnifiedCache::_runEvictionSlice() const { + int32_t maxItemsToEvict = _computeCountOfItemsToEvict(); + if (maxItemsToEvict <= 0) { + return; + } + for (int32_t i = 0; i < MAX_EVICT_ITERATIONS; ++i) { + const UHashElement *element = _nextElement(); + if (element == nullptr) { + break; + } + if (_isEvictable(element)) { + const SharedObject *sharedObject = + (const SharedObject *) element->value.pointer; + uhash_removeElement(fHashtable, element); + removeSoftRef(sharedObject); // Deletes sharedObject when SoftRefCount goes to zero. + ++fAutoEvictedCount; + if (--maxItemsToEvict == 0) { + break; + } + } + } +} + +void UnifiedCache::_putNew( + const CacheKeyBase &key, + const SharedObject *value, + const UErrorCode creationStatus, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return; + } + CacheKeyBase *keyToAdopt = key.clone(); + if (keyToAdopt == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + keyToAdopt->fCreationStatus = creationStatus; + if (value->softRefCount == 0) { + _registerPrimary(keyToAdopt, value); + } + void *oldValue = uhash_put(fHashtable, keyToAdopt, (void *) value, &status); + U_ASSERT(oldValue == nullptr); + (void)oldValue; + if (U_SUCCESS(status)) { + value->softRefCount++; + } +} + +void UnifiedCache::_putIfAbsentAndGet( + const CacheKeyBase &key, + const SharedObject *&value, + UErrorCode &status) const { + std::lock_guard lock(*gCacheMutex); + const UHashElement *element = uhash_find(fHashtable, &key); + if (element != nullptr && !_inProgress(element)) { + _fetch(element, value, status); + return; + } + if (element == nullptr) { + UErrorCode putError = U_ZERO_ERROR; + // best-effort basis only. + _putNew(key, value, status, putError); + } else { + _put(element, value, status); + } + // Run an eviction slice. This will run even if we added a primary entry + // which doesn't increase the unused count, but that is still o.k + _runEvictionSlice(); +} + + +UBool UnifiedCache::_poll( + const CacheKeyBase &key, + const SharedObject *&value, + UErrorCode &status) const { + U_ASSERT(value == nullptr); + U_ASSERT(status == U_ZERO_ERROR); + std::unique_lock lock(*gCacheMutex); + const UHashElement *element = uhash_find(fHashtable, &key); + + // If the hash table contains an inProgress placeholder entry for this key, + // this means that another thread is currently constructing the value object. + // Loop, waiting for that construction to complete. + while (element != nullptr && _inProgress(element)) { + gInProgressValueAddedCond->wait(lock); + element = uhash_find(fHashtable, &key); + } + + // If the hash table contains an entry for the key, + // fetch out the contents and return them. + if (element != nullptr) { + _fetch(element, value, status); + return true; + } + + // The hash table contained nothing for this key. + // Insert an inProgress place holder value. + // Our caller will create the final value and update the hash table. + _putNew(key, fNoValue, U_ZERO_ERROR, status); + return false; +} + +void UnifiedCache::_get( + const CacheKeyBase &key, + const SharedObject *&value, + const void *creationContext, + UErrorCode &status) const { + U_ASSERT(value == nullptr); + U_ASSERT(status == U_ZERO_ERROR); + if (_poll(key, value, status)) { + if (value == fNoValue) { + SharedObject::clearPtr(value); + } + return; + } + if (U_FAILURE(status)) { + return; + } + value = key.createObject(creationContext, status); + U_ASSERT(value == nullptr || value->hasHardReferences()); + U_ASSERT(value != nullptr || status != U_ZERO_ERROR); + if (value == nullptr) { + SharedObject::copyPtr(fNoValue, value); + } + _putIfAbsentAndGet(key, value, status); + if (value == fNoValue) { + SharedObject::clearPtr(value); + } +} + +void UnifiedCache::_registerPrimary( + const CacheKeyBase *theKey, const SharedObject *value) const { + theKey->fIsPrimary = true; + value->cachePtr = this; + ++fNumValuesTotal; + ++fNumValuesInUse; +} + +void UnifiedCache::_put( + const UHashElement *element, + const SharedObject *value, + const UErrorCode status) const { + U_ASSERT(_inProgress(element)); + const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; + const SharedObject *oldValue = (const SharedObject *) element->value.pointer; + theKey->fCreationStatus = status; + if (value->softRefCount == 0) { + _registerPrimary(theKey, value); + } + value->softRefCount++; + UHashElement *ptr = const_cast(element); + ptr->value.pointer = (void *) value; + U_ASSERT(oldValue == fNoValue); + removeSoftRef(oldValue); + + // Tell waiting threads that we replace in-progress status with + // an error. + gInProgressValueAddedCond->notify_all(); +} + +void UnifiedCache::_fetch( + const UHashElement *element, + const SharedObject *&value, + UErrorCode &status) const { + const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; + status = theKey->fCreationStatus; + + // Since we have the cache lock, calling regular SharedObject add/removeRef + // could cause us to deadlock on ourselves since they may need to lock + // the cache mutex. + removeHardRef(value); + value = static_cast(element->value.pointer); + addHardRef(value); +} + + +UBool UnifiedCache::_inProgress(const UHashElement* element) const { + UErrorCode status = U_ZERO_ERROR; + const SharedObject * value = nullptr; + _fetch(element, value, status); + UBool result = _inProgress(value, status); + removeHardRef(value); + return result; +} + +UBool UnifiedCache::_inProgress( + const SharedObject* theValue, UErrorCode creationStatus) const { + return (theValue == fNoValue && creationStatus == U_ZERO_ERROR); +} + +UBool UnifiedCache::_isEvictable(const UHashElement *element) const +{ + const CacheKeyBase *theKey = (const CacheKeyBase *) element->key.pointer; + const SharedObject *theValue = + (const SharedObject *) element->value.pointer; + + // Entries that are under construction are never evictable + if (_inProgress(theValue, theKey->fCreationStatus)) { + return false; + } + + // We can evict entries that are either not a primary or have just + // one reference (The one reference being from the cache itself). + return (!theKey->fIsPrimary || (theValue->softRefCount == 1 && theValue->noHardReferences())); +} + +void UnifiedCache::removeSoftRef(const SharedObject *value) const { + U_ASSERT(value->cachePtr == this); + U_ASSERT(value->softRefCount > 0); + if (--value->softRefCount == 0) { + --fNumValuesTotal; + if (value->noHardReferences()) { + delete value; + } else { + // This path only happens from flush(all). Which only happens from the + // UnifiedCache destructor. Nulling out value.cacheptr changes the behavior + // of value.removeRef(), causing the deletion to be done there. + value->cachePtr = nullptr; + } + } +} + +int32_t UnifiedCache::removeHardRef(const SharedObject *value) const { + int refCount = 0; + if (value) { + refCount = umtx_atomic_dec(&value->hardRefCount); + U_ASSERT(refCount >= 0); + if (refCount == 0) { + --fNumValuesInUse; + } + } + return refCount; +} + +int32_t UnifiedCache::addHardRef(const SharedObject *value) const { + int refCount = 0; + if (value) { + refCount = umtx_atomic_inc(&value->hardRefCount); + U_ASSERT(refCount >= 1); + if (refCount == 1) { + fNumValuesInUse++; + } + } + return refCount; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/unifiedcache.h b/deps/icu-small/source/common/unifiedcache.h index 4b9222124a2ff4..1797c9ecfaaa37 100644 --- a/deps/icu-small/source/common/unifiedcache.h +++ b/deps/icu-small/source/common/unifiedcache.h @@ -1,556 +1,556 @@ -// © 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 UNIFIEDCACHE.H - The ICU Unified cache. -****************************************************************************** -*/ - -#ifndef __UNIFIED_CACHE_H__ -#define __UNIFIED_CACHE_H__ - -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/uobject.h" -#include "unicode/locid.h" -#include "sharedobject.h" -#include "unicode/unistr.h" -#include "cstring.h" -#include "ustr_imp.h" - -struct UHashtable; -struct UHashElement; - -U_NAMESPACE_BEGIN - -class UnifiedCache; - -/** - * A base class for all cache keys. - */ -class U_COMMON_API CacheKeyBase : public UObject { - public: - CacheKeyBase() : fCreationStatus(U_ZERO_ERROR), fIsPrimary(false) {} - - /** - * Copy constructor. Needed to support cloning. - */ - CacheKeyBase(const CacheKeyBase &other) - : UObject(other), fCreationStatus(other.fCreationStatus), fIsPrimary(false) { } - virtual ~CacheKeyBase(); - - /** - * Returns the hash code for this object. - */ - virtual int32_t hashCode() const = 0; - - /** - * Clones this object polymorphically. Caller owns returned value. - */ - virtual CacheKeyBase *clone() const = 0; - - /** - * Create a new object for this key. Called by cache on cache miss. - * createObject must add a reference to the object it returns. Note - * that getting an object from the cache and returning it without calling - * removeRef on it satisfies this requirement. It can also return NULL - * and set status to an error. - * - * @param creationContext the context in which the object is being - * created. May be NULL. - * @param status Implementations can return a failure here. - * In addition, implementations may return a - * non NULL object and set a warning status. - */ - virtual const SharedObject *createObject( - const void *creationContext, UErrorCode &status) const = 0; - - /** - * Writes a description of this key to buffer and returns buffer. Written - * description is NULL terminated. - */ - virtual char *writeDescription(char *buffer, int32_t bufSize) const = 0; - - friend inline bool operator==(const CacheKeyBase& lhs, - const CacheKeyBase& rhs) { - return lhs.equals(rhs); - } - - friend inline bool operator!=(const CacheKeyBase& lhs, - const CacheKeyBase& rhs) { - return !lhs.equals(rhs); - } - - protected: - virtual bool equals(const CacheKeyBase& other) const = 0; - - private: - mutable UErrorCode fCreationStatus; - mutable UBool fIsPrimary; - friend class UnifiedCache; -}; - - - -/** - * Templated version of CacheKeyBase. - * A key of type LocaleCacheKey maps to a value of type T. - */ -template -class CacheKey : public CacheKeyBase { - public: - virtual ~CacheKey() { } - /** - * The template parameter, T, determines the hash code returned. - */ - virtual int32_t hashCode() const override { - const char *s = typeid(T).name(); - return ustr_hashCharsN(s, static_cast(uprv_strlen(s))); - } - - /** - * Use the value type, T, as the description. - */ - virtual char *writeDescription(char *buffer, int32_t bufLen) const override { - const char *s = typeid(T).name(); - uprv_strncpy(buffer, s, bufLen); - buffer[bufLen - 1] = 0; - return buffer; - } - - protected: - /** - * Two objects are equal if they are of the same type. - */ - virtual bool equals(const CacheKeyBase &other) const override { - return this == &other || typeid(*this) == typeid(other); - } -}; - -/** - * Cache key based on locale. - * A key of type LocaleCacheKey maps to a value of type T. - */ -template -class LocaleCacheKey : public CacheKey { - protected: - Locale fLoc; - virtual bool equals(const CacheKeyBase &other) const override { - if (!CacheKey::equals(other)) { - return false; - } - // We know this and other are of same class because equals() on - // CacheKey returned true. - return operator==(static_cast &>(other)); - } - public: - LocaleCacheKey(const Locale &loc) : fLoc(loc) {} - LocaleCacheKey(const LocaleCacheKey &other) - : CacheKey(other), fLoc(other.fLoc) { } - virtual ~LocaleCacheKey() { } - virtual int32_t hashCode() const override { - return (int32_t)(37u * (uint32_t)CacheKey::hashCode() + (uint32_t)fLoc.hashCode()); - } - inline bool operator == (const LocaleCacheKey &other) const { - return fLoc == other.fLoc; - } - virtual CacheKeyBase *clone() const override { - return new LocaleCacheKey(*this); - } - virtual const T *createObject( - const void *creationContext, UErrorCode &status) const override; - /** - * Use the locale id as the description. - */ - virtual char *writeDescription(char *buffer, int32_t bufLen) const override { - const char *s = fLoc.getName(); - uprv_strncpy(buffer, s, bufLen); - buffer[bufLen - 1] = 0; - return buffer; - } - -}; - -/** - * The unified cache. A singleton type. - * Design doc here: - * https://docs.google.com/document/d/1RwGQJs4N4tawNbf809iYDRCvXoMKqDJihxzYt1ysmd8/edit?usp=sharing - */ -class U_COMMON_API UnifiedCache : public UnifiedCacheBase { - public: - /** - * @internal - * Do not call directly. Instead use UnifiedCache::getInstance() as - * there should be only one UnifiedCache in an application. - */ - UnifiedCache(UErrorCode &status); - - /** - * Return a pointer to the global cache instance. - */ - static UnifiedCache *getInstance(UErrorCode &status); - - /** - * Fetches a value from the cache by key. Equivalent to - * get(key, NULL, ptr, status); - */ - template - void get( - const CacheKey& key, - const T *&ptr, - UErrorCode &status) const { - get(key, NULL, ptr, status); - } - - /** - * Fetches value from the cache by key. - * - * @param key the cache key. - * @param creationContext passed verbatim to createObject method of key - * @param ptr On entry, ptr must be NULL or be included if - * the reference count of the object it points - * to. On exit, ptr points to the fetched object - * from the cache or is left unchanged on - * failure. Caller must call removeRef on ptr - * if set to a non NULL value. - * @param status Any error returned here. May be set to a - * warning value even if ptr is set. - */ - template - void get( - const CacheKey& key, - const void *creationContext, - const T *&ptr, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return; - } - UErrorCode creationStatus = U_ZERO_ERROR; - const SharedObject *value = NULL; - _get(key, value, creationContext, creationStatus); - const T *tvalue = (const T *) value; - if (U_SUCCESS(creationStatus)) { - SharedObject::copyPtr(tvalue, ptr); - } - SharedObject::clearPtr(tvalue); - // Take care not to overwrite a warning status passed in with - // another warning or U_ZERO_ERROR. - if (status == U_ZERO_ERROR || U_FAILURE(creationStatus)) { - status = creationStatus; - } - } - -#ifdef UNIFIED_CACHE_DEBUG - /** - * Dumps the contents of this cache to standard error. Used for testing of - * cache only. - */ - void dumpContents() const; -#endif - - /** - * Convenience method to get a value of type T from cache for a - * particular locale with creationContext == NULL. - * @param loc the locale - * @param ptr On entry, must be NULL or included in the ref count - * of the object to which it points. - * On exit, fetched value stored here or is left - * unchanged on failure. Caller must call removeRef on - * ptr if set to a non NULL value. - * @param status Any error returned here. May be set to a - * warning value even if ptr is set. - */ - template - static void getByLocale( - const Locale &loc, const T *&ptr, UErrorCode &status) { - const UnifiedCache *cache = getInstance(status); - if (U_FAILURE(status)) { - return; - } - cache->get(LocaleCacheKey(loc), ptr, status); - } - -#ifdef UNIFIED_CACHE_DEBUG - /** - * Dumps the cache contents to stderr. For testing only. - */ - static void dump(); -#endif - - /** - * Returns the number of keys in this cache. For testing only. - */ - int32_t keyCount() const; - - /** - * Removes any values from cache that are not referenced outside - * the cache. - */ - void flush() const; - - /** - * Configures at what point eviction of unused entries will begin. - * Eviction is triggered whenever the number of evictable keys exceeds - * BOTH count AND (number of in-use items) * (percentageOfInUseItems / 100). - * Once the number of unused entries drops below one of these, - * eviction ceases. Because eviction happens incrementally, - * the actual unused entry count may exceed both these numbers - * from time to time. - * - * A cache entry is defined as unused if it is not essential to guarantee - * that for a given key X, the cache returns the same reference to the - * same value as long as the client already holds a reference to that - * value. - * - * If this method is never called, the default settings are 1000 and 100%. - * - * Although this method is thread-safe, it is designed to be called at - * application startup. If it is called in the middle of execution, it - * will have no immediate effect on the cache. However over time, the - * cache will perform eviction slices in an attempt to honor the new - * settings. - * - * If a client already holds references to many different unique values - * in the cache such that the number of those unique values far exceeds - * "count" then the cache may not be able to maintain this maximum. - * However, if this happens, the cache still guarantees that the number of - * unused entries will remain only a small percentage of the total cache - * size. - * - * If the parameters passed are negative, setEvctionPolicy sets status to - * U_ILLEGAL_ARGUMENT_ERROR. - */ - void setEvictionPolicy( - int32_t count, int32_t percentageOfInUseItems, UErrorCode &status); - - - /** - * Returns how many entries have been auto evicted during the lifetime - * of this cache. This only includes auto evicted entries, not - * entries evicted because of a call to flush(). - */ - int64_t autoEvictedCount() const; - - /** - * Returns the unused entry count in this cache. For testing only, - * Regular clients will not need this. - */ - int32_t unusedCount() const; - - virtual void handleUnreferencedObject() const override; - virtual ~UnifiedCache(); - - private: - UHashtable *fHashtable; - mutable int32_t fEvictPos; - mutable int32_t fNumValuesTotal; - mutable int32_t fNumValuesInUse; - int32_t fMaxUnused; - int32_t fMaxPercentageOfInUse; - mutable int64_t fAutoEvictedCount; - SharedObject *fNoValue; - - UnifiedCache(const UnifiedCache &other) = delete; - UnifiedCache &operator=(const UnifiedCache &other) = delete; - - /** - * Flushes the contents of the cache. If cache values hold references to other - * cache values then _flush should be called in a loop until it returns false. - * - * On entry, gCacheMutex must be held. - * On exit, those values with are evictable are flushed. - * - * @param all if false flush evictable items only, which are those with no external - * references, plus those that can be safely recreated.
- * if true, flush all elements. Any values (sharedObjects) with remaining - * hard (external) references are not deleted, but are detached from - * the cache, so that a subsequent removeRefs can delete them. - * _flush is not thread safe when all is true. - * @return true if any value in cache was flushed or false otherwise. - */ - UBool _flush(UBool all) const; - - /** - * Gets value out of cache. - * On entry. gCacheMutex must not be held. value must be NULL. status - * must be U_ZERO_ERROR. - * On exit. value and status set to what is in cache at key or on cache - * miss the key's createObject() is called and value and status are set to - * the result of that. In this latter case, best effort is made to add the - * value and status to the cache. If createObject() fails to create a value, - * fNoValue is stored in cache, and value is set to NULL. Caller must call - * removeRef on value if non NULL. - */ - void _get( - const CacheKeyBase &key, - const SharedObject *&value, - const void *creationContext, - UErrorCode &status) const; - - /** - * Attempts to fetch value and status for key from cache. - * On entry, gCacheMutex must not be held value must be NULL and status must - * be U_ZERO_ERROR. - * On exit, either returns false (In this - * case caller should try to create the object) or returns true with value - * pointing to the fetched value and status set to fetched status. When - * false is returned status may be set to failure if an in progress hash - * entry could not be made but value will remain unchanged. When true is - * returned, caller must call removeRef() on value. - */ - UBool _poll( - const CacheKeyBase &key, - const SharedObject *&value, - UErrorCode &status) const; - - /** - * Places a new value and creationStatus in the cache for the given key. - * On entry, gCacheMutex must be held. key must not exist in the cache. - * On exit, value and creation status placed under key. Soft reference added - * to value on successful add. On error sets status. - */ - void _putNew( - const CacheKeyBase &key, - const SharedObject *value, - const UErrorCode creationStatus, - UErrorCode &status) const; - - /** - * Places value and status at key if there is no value at key or if cache - * entry for key is in progress. Otherwise, it leaves the current value and - * status there. - * - * On entry. gCacheMutex must not be held. Value must be - * included in the reference count of the object to which it points. - * - * On exit, value and status are changed to what was already in the cache if - * something was there and not in progress. Otherwise, value and status are left - * unchanged in which case they are placed in the cache on a best-effort basis. - * Caller must call removeRef() on value. - */ - void _putIfAbsentAndGet( - const CacheKeyBase &key, - const SharedObject *&value, - UErrorCode &status) const; - - /** - * Returns the next element in the cache round robin style. - * Returns nullptr if the cache is empty. - * On entry, gCacheMutex must be held. - */ - const UHashElement *_nextElement() const; - - /** - * Return the number of cache items that would need to be evicted - * to bring usage into conformance with eviction policy. - * - * An item corresponds to an entry in the hash table, a hash table element. - * - * On entry, gCacheMutex must be held. - */ - int32_t _computeCountOfItemsToEvict() const; - - /** - * Run an eviction slice. - * On entry, gCacheMutex must be held. - * _runEvictionSlice runs a slice of the evict pipeline by examining the next - * 10 entries in the cache round robin style evicting them if they are eligible. - */ - void _runEvictionSlice() const; - - /** - * Register a primary cache entry. A primary key is the first key to create - * a given SharedObject value. Subsequent keys whose create function - * produce references to an already existing SharedObject are not primary - - * they can be evicted and subsequently recreated. - * - * On entry, gCacheMutex must be held. - * On exit, items in use count incremented, entry is marked as a primary - * entry, and value registered with cache so that subsequent calls to - * addRef() and removeRef() on it correctly interact with the cache. - */ - void _registerPrimary(const CacheKeyBase *theKey, const SharedObject *value) const; - - /** - * Store a value and creation error status in given hash entry. - * On entry, gCacheMutex must be held. Hash entry element must be in progress. - * value must be non NULL. - * On Exit, soft reference added to value. value and status stored in hash - * entry. Soft reference removed from previous stored value. Waiting - * threads notified. - */ - void _put( - const UHashElement *element, - const SharedObject *value, - const UErrorCode status) const; - /** - * Remove a soft reference, and delete the SharedObject if no references remain. - * To be used from within the UnifiedCache implementation only. - * gCacheMutex must be held by caller. - * @param value the SharedObject to be acted on. - */ - void removeSoftRef(const SharedObject *value) const; - - /** - * Increment the hard reference count of the given SharedObject. - * gCacheMutex must be held by the caller. - * Update numValuesEvictable on transitions between zero and one reference. - * - * @param value The SharedObject to be referenced. - * @return the hard reference count after the addition. - */ - int32_t addHardRef(const SharedObject *value) const; - - /** - * Decrement the hard reference count of the given SharedObject. - * gCacheMutex must be held by the caller. - * Update numValuesEvictable on transitions between one and zero reference. - * - * @param value The SharedObject to be referenced. - * @return the hard reference count after the removal. - */ - int32_t removeHardRef(const SharedObject *value) const; - - -#ifdef UNIFIED_CACHE_DEBUG - void _dumpContents() const; -#endif - - /** - * Fetch value and error code from a particular hash entry. - * On entry, gCacheMutex must be held. value must be either NULL or must be - * included in the ref count of the object to which it points. - * On exit, value and status set to what is in the hash entry. Caller must - * eventually call removeRef on value. - * If hash entry is in progress, value will be set to gNoValue and status will - * be set to U_ZERO_ERROR. - */ - void _fetch(const UHashElement *element, const SharedObject *&value, - UErrorCode &status) const; - - /** - * Determine if given hash entry is in progress. - * On entry, gCacheMutex must be held. - */ - UBool _inProgress(const UHashElement *element) const; - - /** - * Determine if given hash entry is in progress. - * On entry, gCacheMutex must be held. - */ - UBool _inProgress(const SharedObject *theValue, UErrorCode creationStatus) const; - - /** - * Determine if given hash entry is eligible for eviction. - * On entry, gCacheMutex must be held. - */ - UBool _isEvictable(const UHashElement *element) const; -}; - -U_NAMESPACE_END - -#endif +// © 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 UNIFIEDCACHE.H - The ICU Unified cache. +****************************************************************************** +*/ + +#ifndef __UNIFIED_CACHE_H__ +#define __UNIFIED_CACHE_H__ + +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/uobject.h" +#include "unicode/locid.h" +#include "sharedobject.h" +#include "unicode/unistr.h" +#include "cstring.h" +#include "ustr_imp.h" + +struct UHashtable; +struct UHashElement; + +U_NAMESPACE_BEGIN + +class UnifiedCache; + +/** + * A base class for all cache keys. + */ +class U_COMMON_API CacheKeyBase : public UObject { + public: + CacheKeyBase() : fCreationStatus(U_ZERO_ERROR), fIsPrimary(false) {} + + /** + * Copy constructor. Needed to support cloning. + */ + CacheKeyBase(const CacheKeyBase &other) + : UObject(other), fCreationStatus(other.fCreationStatus), fIsPrimary(false) { } + virtual ~CacheKeyBase(); + + /** + * Returns the hash code for this object. + */ + virtual int32_t hashCode() const = 0; + + /** + * Clones this object polymorphically. Caller owns returned value. + */ + virtual CacheKeyBase *clone() const = 0; + + /** + * Create a new object for this key. Called by cache on cache miss. + * createObject must add a reference to the object it returns. Note + * that getting an object from the cache and returning it without calling + * removeRef on it satisfies this requirement. It can also return nullptr + * and set status to an error. + * + * @param creationContext the context in which the object is being + * created. May be nullptr. + * @param status Implementations can return a failure here. + * In addition, implementations may return a + * non nullptr object and set a warning status. + */ + virtual const SharedObject *createObject( + const void *creationContext, UErrorCode &status) const = 0; + + /** + * Writes a description of this key to buffer and returns buffer. Written + * description is nullptr terminated. + */ + virtual char *writeDescription(char *buffer, int32_t bufSize) const = 0; + + friend inline bool operator==(const CacheKeyBase& lhs, + const CacheKeyBase& rhs) { + return lhs.equals(rhs); + } + + friend inline bool operator!=(const CacheKeyBase& lhs, + const CacheKeyBase& rhs) { + return !lhs.equals(rhs); + } + + protected: + virtual bool equals(const CacheKeyBase& other) const = 0; + + private: + mutable UErrorCode fCreationStatus; + mutable UBool fIsPrimary; + friend class UnifiedCache; +}; + + + +/** + * Templated version of CacheKeyBase. + * A key of type LocaleCacheKey maps to a value of type T. + */ +template +class CacheKey : public CacheKeyBase { + public: + virtual ~CacheKey() { } + /** + * The template parameter, T, determines the hash code returned. + */ + virtual int32_t hashCode() const override { + const char *s = typeid(T).name(); + return ustr_hashCharsN(s, static_cast(uprv_strlen(s))); + } + + /** + * Use the value type, T, as the description. + */ + virtual char *writeDescription(char *buffer, int32_t bufLen) const override { + const char *s = typeid(T).name(); + uprv_strncpy(buffer, s, bufLen); + buffer[bufLen - 1] = 0; + return buffer; + } + + protected: + /** + * Two objects are equal if they are of the same type. + */ + virtual bool equals(const CacheKeyBase &other) const override { + return this == &other || typeid(*this) == typeid(other); + } +}; + +/** + * Cache key based on locale. + * A key of type LocaleCacheKey maps to a value of type T. + */ +template +class LocaleCacheKey : public CacheKey { + protected: + Locale fLoc; + virtual bool equals(const CacheKeyBase &other) const override { + if (!CacheKey::equals(other)) { + return false; + } + // We know this and other are of same class because equals() on + // CacheKey returned true. + return operator==(static_cast &>(other)); + } + public: + LocaleCacheKey(const Locale &loc) : fLoc(loc) {} + LocaleCacheKey(const LocaleCacheKey &other) + : CacheKey(other), fLoc(other.fLoc) { } + virtual ~LocaleCacheKey() { } + virtual int32_t hashCode() const override { + return (int32_t)(37u * (uint32_t)CacheKey::hashCode() + (uint32_t)fLoc.hashCode()); + } + inline bool operator == (const LocaleCacheKey &other) const { + return fLoc == other.fLoc; + } + virtual CacheKeyBase *clone() const override { + return new LocaleCacheKey(*this); + } + virtual const T *createObject( + const void *creationContext, UErrorCode &status) const override; + /** + * Use the locale id as the description. + */ + virtual char *writeDescription(char *buffer, int32_t bufLen) const override { + const char *s = fLoc.getName(); + uprv_strncpy(buffer, s, bufLen); + buffer[bufLen - 1] = 0; + return buffer; + } + +}; + +/** + * The unified cache. A singleton type. + * Design doc here: + * https://docs.google.com/document/d/1RwGQJs4N4tawNbf809iYDRCvXoMKqDJihxzYt1ysmd8/edit?usp=sharing + */ +class U_COMMON_API UnifiedCache : public UnifiedCacheBase { + public: + /** + * @internal + * Do not call directly. Instead use UnifiedCache::getInstance() as + * there should be only one UnifiedCache in an application. + */ + UnifiedCache(UErrorCode &status); + + /** + * Return a pointer to the global cache instance. + */ + static UnifiedCache *getInstance(UErrorCode &status); + + /** + * Fetches a value from the cache by key. Equivalent to + * get(key, nullptr, ptr, status); + */ + template + void get( + const CacheKey& key, + const T *&ptr, + UErrorCode &status) const { + get(key, nullptr, ptr, status); + } + + /** + * Fetches value from the cache by key. + * + * @param key the cache key. + * @param creationContext passed verbatim to createObject method of key + * @param ptr On entry, ptr must be nullptr or be included if + * the reference count of the object it points + * to. On exit, ptr points to the fetched object + * from the cache or is left unchanged on + * failure. Caller must call removeRef on ptr + * if set to a non nullptr value. + * @param status Any error returned here. May be set to a + * warning value even if ptr is set. + */ + template + void get( + const CacheKey& key, + const void *creationContext, + const T *&ptr, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return; + } + UErrorCode creationStatus = U_ZERO_ERROR; + const SharedObject *value = nullptr; + _get(key, value, creationContext, creationStatus); + const T *tvalue = (const T *) value; + if (U_SUCCESS(creationStatus)) { + SharedObject::copyPtr(tvalue, ptr); + } + SharedObject::clearPtr(tvalue); + // Take care not to overwrite a warning status passed in with + // another warning or U_ZERO_ERROR. + if (status == U_ZERO_ERROR || U_FAILURE(creationStatus)) { + status = creationStatus; + } + } + +#ifdef UNIFIED_CACHE_DEBUG + /** + * Dumps the contents of this cache to standard error. Used for testing of + * cache only. + */ + void dumpContents() const; +#endif + + /** + * Convenience method to get a value of type T from cache for a + * particular locale with creationContext == nullptr. + * @param loc the locale + * @param ptr On entry, must be nullptr or included in the ref count + * of the object to which it points. + * On exit, fetched value stored here or is left + * unchanged on failure. Caller must call removeRef on + * ptr if set to a non nullptr value. + * @param status Any error returned here. May be set to a + * warning value even if ptr is set. + */ + template + static void getByLocale( + const Locale &loc, const T *&ptr, UErrorCode &status) { + const UnifiedCache *cache = getInstance(status); + if (U_FAILURE(status)) { + return; + } + cache->get(LocaleCacheKey(loc), ptr, status); + } + +#ifdef UNIFIED_CACHE_DEBUG + /** + * Dumps the cache contents to stderr. For testing only. + */ + static void dump(); +#endif + + /** + * Returns the number of keys in this cache. For testing only. + */ + int32_t keyCount() const; + + /** + * Removes any values from cache that are not referenced outside + * the cache. + */ + void flush() const; + + /** + * Configures at what point eviction of unused entries will begin. + * Eviction is triggered whenever the number of evictable keys exceeds + * BOTH count AND (number of in-use items) * (percentageOfInUseItems / 100). + * Once the number of unused entries drops below one of these, + * eviction ceases. Because eviction happens incrementally, + * the actual unused entry count may exceed both these numbers + * from time to time. + * + * A cache entry is defined as unused if it is not essential to guarantee + * that for a given key X, the cache returns the same reference to the + * same value as long as the client already holds a reference to that + * value. + * + * If this method is never called, the default settings are 1000 and 100%. + * + * Although this method is thread-safe, it is designed to be called at + * application startup. If it is called in the middle of execution, it + * will have no immediate effect on the cache. However over time, the + * cache will perform eviction slices in an attempt to honor the new + * settings. + * + * If a client already holds references to many different unique values + * in the cache such that the number of those unique values far exceeds + * "count" then the cache may not be able to maintain this maximum. + * However, if this happens, the cache still guarantees that the number of + * unused entries will remain only a small percentage of the total cache + * size. + * + * If the parameters passed are negative, setEvctionPolicy sets status to + * U_ILLEGAL_ARGUMENT_ERROR. + */ + void setEvictionPolicy( + int32_t count, int32_t percentageOfInUseItems, UErrorCode &status); + + + /** + * Returns how many entries have been auto evicted during the lifetime + * of this cache. This only includes auto evicted entries, not + * entries evicted because of a call to flush(). + */ + int64_t autoEvictedCount() const; + + /** + * Returns the unused entry count in this cache. For testing only, + * Regular clients will not need this. + */ + int32_t unusedCount() const; + + virtual void handleUnreferencedObject() const override; + virtual ~UnifiedCache(); + + private: + UHashtable *fHashtable; + mutable int32_t fEvictPos; + mutable int32_t fNumValuesTotal; + mutable int32_t fNumValuesInUse; + int32_t fMaxUnused; + int32_t fMaxPercentageOfInUse; + mutable int64_t fAutoEvictedCount; + SharedObject *fNoValue; + + UnifiedCache(const UnifiedCache &other) = delete; + UnifiedCache &operator=(const UnifiedCache &other) = delete; + + /** + * Flushes the contents of the cache. If cache values hold references to other + * cache values then _flush should be called in a loop until it returns false. + * + * On entry, gCacheMutex must be held. + * On exit, those values with are evictable are flushed. + * + * @param all if false flush evictable items only, which are those with no external + * references, plus those that can be safely recreated.
+ * if true, flush all elements. Any values (sharedObjects) with remaining + * hard (external) references are not deleted, but are detached from + * the cache, so that a subsequent removeRefs can delete them. + * _flush is not thread safe when all is true. + * @return true if any value in cache was flushed or false otherwise. + */ + UBool _flush(UBool all) const; + + /** + * Gets value out of cache. + * On entry. gCacheMutex must not be held. value must be nullptr. status + * must be U_ZERO_ERROR. + * On exit. value and status set to what is in cache at key or on cache + * miss the key's createObject() is called and value and status are set to + * the result of that. In this latter case, best effort is made to add the + * value and status to the cache. If createObject() fails to create a value, + * fNoValue is stored in cache, and value is set to nullptr. Caller must call + * removeRef on value if non nullptr. + */ + void _get( + const CacheKeyBase &key, + const SharedObject *&value, + const void *creationContext, + UErrorCode &status) const; + + /** + * Attempts to fetch value and status for key from cache. + * On entry, gCacheMutex must not be held value must be nullptr and status must + * be U_ZERO_ERROR. + * On exit, either returns false (In this + * case caller should try to create the object) or returns true with value + * pointing to the fetched value and status set to fetched status. When + * false is returned status may be set to failure if an in progress hash + * entry could not be made but value will remain unchanged. When true is + * returned, caller must call removeRef() on value. + */ + UBool _poll( + const CacheKeyBase &key, + const SharedObject *&value, + UErrorCode &status) const; + + /** + * Places a new value and creationStatus in the cache for the given key. + * On entry, gCacheMutex must be held. key must not exist in the cache. + * On exit, value and creation status placed under key. Soft reference added + * to value on successful add. On error sets status. + */ + void _putNew( + const CacheKeyBase &key, + const SharedObject *value, + const UErrorCode creationStatus, + UErrorCode &status) const; + + /** + * Places value and status at key if there is no value at key or if cache + * entry for key is in progress. Otherwise, it leaves the current value and + * status there. + * + * On entry. gCacheMutex must not be held. Value must be + * included in the reference count of the object to which it points. + * + * On exit, value and status are changed to what was already in the cache if + * something was there and not in progress. Otherwise, value and status are left + * unchanged in which case they are placed in the cache on a best-effort basis. + * Caller must call removeRef() on value. + */ + void _putIfAbsentAndGet( + const CacheKeyBase &key, + const SharedObject *&value, + UErrorCode &status) const; + + /** + * Returns the next element in the cache round robin style. + * Returns nullptr if the cache is empty. + * On entry, gCacheMutex must be held. + */ + const UHashElement *_nextElement() const; + + /** + * Return the number of cache items that would need to be evicted + * to bring usage into conformance with eviction policy. + * + * An item corresponds to an entry in the hash table, a hash table element. + * + * On entry, gCacheMutex must be held. + */ + int32_t _computeCountOfItemsToEvict() const; + + /** + * Run an eviction slice. + * On entry, gCacheMutex must be held. + * _runEvictionSlice runs a slice of the evict pipeline by examining the next + * 10 entries in the cache round robin style evicting them if they are eligible. + */ + void _runEvictionSlice() const; + + /** + * Register a primary cache entry. A primary key is the first key to create + * a given SharedObject value. Subsequent keys whose create function + * produce references to an already existing SharedObject are not primary - + * they can be evicted and subsequently recreated. + * + * On entry, gCacheMutex must be held. + * On exit, items in use count incremented, entry is marked as a primary + * entry, and value registered with cache so that subsequent calls to + * addRef() and removeRef() on it correctly interact with the cache. + */ + void _registerPrimary(const CacheKeyBase *theKey, const SharedObject *value) const; + + /** + * Store a value and creation error status in given hash entry. + * On entry, gCacheMutex must be held. Hash entry element must be in progress. + * value must be non nullptr. + * On Exit, soft reference added to value. value and status stored in hash + * entry. Soft reference removed from previous stored value. Waiting + * threads notified. + */ + void _put( + const UHashElement *element, + const SharedObject *value, + const UErrorCode status) const; + /** + * Remove a soft reference, and delete the SharedObject if no references remain. + * To be used from within the UnifiedCache implementation only. + * gCacheMutex must be held by caller. + * @param value the SharedObject to be acted on. + */ + void removeSoftRef(const SharedObject *value) const; + + /** + * Increment the hard reference count of the given SharedObject. + * gCacheMutex must be held by the caller. + * Update numValuesEvictable on transitions between zero and one reference. + * + * @param value The SharedObject to be referenced. + * @return the hard reference count after the addition. + */ + int32_t addHardRef(const SharedObject *value) const; + + /** + * Decrement the hard reference count of the given SharedObject. + * gCacheMutex must be held by the caller. + * Update numValuesEvictable on transitions between one and zero reference. + * + * @param value The SharedObject to be referenced. + * @return the hard reference count after the removal. + */ + int32_t removeHardRef(const SharedObject *value) const; + + +#ifdef UNIFIED_CACHE_DEBUG + void _dumpContents() const; +#endif + + /** + * Fetch value and error code from a particular hash entry. + * On entry, gCacheMutex must be held. value must be either nullptr or must be + * included in the ref count of the object to which it points. + * On exit, value and status set to what is in the hash entry. Caller must + * eventually call removeRef on value. + * If hash entry is in progress, value will be set to gNoValue and status will + * be set to U_ZERO_ERROR. + */ + void _fetch(const UHashElement *element, const SharedObject *&value, + UErrorCode &status) const; + + /** + * Determine if given hash entry is in progress. + * On entry, gCacheMutex must be held. + */ + UBool _inProgress(const UHashElement *element) const; + + /** + * Determine if given hash entry is in progress. + * On entry, gCacheMutex must be held. + */ + UBool _inProgress(const SharedObject *theValue, UErrorCode creationStatus) const; + + /** + * Determine if given hash entry is eligible for eviction. + * On entry, gCacheMutex must be held. + */ + UBool _isEvictable(const UHashElement *element) const; +}; + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/common/unifilt.cpp b/deps/icu-small/source/common/unifilt.cpp index 4ab0d9b5f934ba..16a821a203f227 100644 --- a/deps/icu-small/source/common/unifilt.cpp +++ b/deps/icu-small/source/common/unifilt.cpp @@ -1,71 +1,71 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2001-2012, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 07/18/01 aliu Creation. -********************************************************************** -*/ - -#include "unicode/unifilt.h" -#include "unicode/rep.h" -#include "unicode/utf16.h" - -U_NAMESPACE_BEGIN -UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(UnicodeFilter) - - -/* Define this here due to the lack of another file. - It can't be defined in the header */ -UnicodeMatcher::~UnicodeMatcher() {} - -UnicodeFilter::~UnicodeFilter() {} - -/** - * UnicodeFunctor API. - * Note that UnicodeMatcher is a base class of UnicodeFilter. - */ -UnicodeMatcher* UnicodeFilter::toMatcher() const { - return const_cast(this); -} - -void UnicodeFilter::setData(const TransliterationRuleData*) {} - -/** - * Default implementation of UnicodeMatcher::matches() for Unicode - * filters. Matches a single code point at offset (either one or - * two 16-bit code units). - */ -UMatchDegree UnicodeFilter::matches(const Replaceable& text, - int32_t& offset, - int32_t limit, - UBool incremental) { - UChar32 c; - if (offset < limit && - contains(c = text.char32At(offset))) { - offset += U16_LENGTH(c); - return U_MATCH; - } - if (offset > limit && - contains(c = text.char32At(offset))) { - // Backup offset by 1, unless the preceding character is a - // surrogate pair -- then backup by 2 (keep offset pointing at - // the lead surrogate). - --offset; - if (offset >= 0) { - offset -= U16_LENGTH(text.char32At(offset)) - 1; - } - return U_MATCH; - } - if (incremental && offset == limit) { - return U_PARTIAL_MATCH; - } - return U_MISMATCH; -} - -U_NAMESPACE_END - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2001-2012, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 07/18/01 aliu Creation. +********************************************************************** +*/ + +#include "unicode/unifilt.h" +#include "unicode/rep.h" +#include "unicode/utf16.h" + +U_NAMESPACE_BEGIN +UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(UnicodeFilter) + + +/* Define this here due to the lack of another file. + It can't be defined in the header */ +UnicodeMatcher::~UnicodeMatcher() {} + +UnicodeFilter::~UnicodeFilter() {} + +/** + * UnicodeFunctor API. + * Note that UnicodeMatcher is a base class of UnicodeFilter. + */ +UnicodeMatcher* UnicodeFilter::toMatcher() const { + return const_cast(this); +} + +void UnicodeFilter::setData(const TransliterationRuleData*) {} + +/** + * Default implementation of UnicodeMatcher::matches() for Unicode + * filters. Matches a single code point at offset (either one or + * two 16-bit code units). + */ +UMatchDegree UnicodeFilter::matches(const Replaceable& text, + int32_t& offset, + int32_t limit, + UBool incremental) { + UChar32 c; + if (offset < limit && + contains(c = text.char32At(offset))) { + offset += U16_LENGTH(c); + return U_MATCH; + } + if (offset > limit && + contains(c = text.char32At(offset))) { + // Backup offset by 1, unless the preceding character is a + // surrogate pair -- then backup by 2 (keep offset pointing at + // the lead surrogate). + --offset; + if (offset >= 0) { + offset -= U16_LENGTH(text.char32At(offset)) - 1; + } + return U_MATCH; + } + if (incremental && offset == limit) { + return U_PARTIAL_MATCH; + } + return U_MISMATCH; +} + +U_NAMESPACE_END + +//eof diff --git a/deps/icu-small/source/common/unifunct.cpp b/deps/icu-small/source/common/unifunct.cpp index f3995b298d2c46..434af18cdff229 100644 --- a/deps/icu-small/source/common/unifunct.cpp +++ b/deps/icu-small/source/common/unifunct.cpp @@ -1,28 +1,28 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2004, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#include "unicode/unifunct.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(UnicodeFunctor) - -UnicodeFunctor::~UnicodeFunctor() {} - -UnicodeMatcher* UnicodeFunctor::toMatcher() const { - return 0; -} - -UnicodeReplacer* UnicodeFunctor::toReplacer() const { - return 0; -} - -U_NAMESPACE_END - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2004, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#include "unicode/unifunct.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(UnicodeFunctor) + +UnicodeFunctor::~UnicodeFunctor() {} + +UnicodeMatcher* UnicodeFunctor::toMatcher() const { + return 0; +} + +UnicodeReplacer* UnicodeFunctor::toReplacer() const { + return 0; +} + +U_NAMESPACE_END + +//eof diff --git a/deps/icu-small/source/common/uniquecharstr.h b/deps/icu-small/source/common/uniquecharstr.h index 10cc924f7f9eb2..9e09d460b770de 100644 --- a/deps/icu-small/source/common/uniquecharstr.h +++ b/deps/icu-small/source/common/uniquecharstr.h @@ -1,98 +1,98 @@ -// © 2020 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// uniquecharstr.h -// created: 2020sep01 Frank Yung-Fong Tang - -#ifndef __UNIQUECHARSTR_H__ -#define __UNIQUECHARSTR_H__ - -#include "charstr.h" -#include "uassert.h" -#include "uhash.h" - -U_NAMESPACE_BEGIN - -/** - * Stores NUL-terminated strings with duplicate elimination. - * Checks for unique UTF-16 string pointers and converts to invariant characters. - * - * Intended to be stack-allocated. Add strings, get a unique number for each, - * freeze the object, get a char * pointer for each string, - * call orphanCharStrings() to capture the string storage, and let this object go out of scope. - */ -class UniqueCharStrings { -public: - UniqueCharStrings(UErrorCode &errorCode) : strings(nullptr) { - // Note: We hash on string contents but store stable char16_t * pointers. - // If the strings are stored in resource bundles which should be built with - // duplicate elimination, then we should be able to hash on just the pointer values. - uhash_init(&map, uhash_hashUChars, uhash_compareUChars, uhash_compareLong, &errorCode); - if (U_FAILURE(errorCode)) { return; } - strings = new CharString(); - if (strings == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - } - } - ~UniqueCharStrings() { - uhash_close(&map); - delete strings; - } - - /** Returns/orphans the CharString that contains all strings. */ - CharString *orphanCharStrings() { - CharString *result = strings; - strings = nullptr; - return result; - } - - /** - * Adds a string and returns a unique number for it. - * The string's buffer contents must not change, nor move around in memory, - * while this UniqueCharStrings is in use. - * The string contents must be NUL-terminated exactly at s.length(). - * - * Best used with read-only-alias UnicodeString objects that point to - * stable storage, such as strings returned by resource bundle functions. - */ - int32_t add(const UnicodeString &s, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return 0; } - if (isFrozen) { - errorCode = U_NO_WRITE_PERMISSION; - return 0; - } - // The string points into the resource bundle. - const char16_t *p = s.getBuffer(); - int32_t oldIndex = uhash_geti(&map, p); - if (oldIndex != 0) { // found duplicate - return oldIndex; - } - // Explicit NUL terminator for the previous string. - // The strings object is also terminated with one implicit NUL. - strings->append(0, errorCode); - int32_t newIndex = strings->length(); - strings->appendInvariantChars(s, errorCode); - uhash_puti(&map, const_cast(p), newIndex, &errorCode); - return newIndex; - } - - void freeze() { isFrozen = true; } - - /** - * Returns a string pointer for its unique number, if this object is frozen. - * Otherwise nullptr. - */ - const char *get(int32_t i) const { - U_ASSERT(isFrozen); - return isFrozen && i > 0 ? strings->data() + i : nullptr; - } - -private: - UHashtable map; - CharString *strings; - bool isFrozen = false; -}; - -U_NAMESPACE_END - -#endif // __UNIQUECHARSTR_H__ +// © 2020 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// uniquecharstr.h +// created: 2020sep01 Frank Yung-Fong Tang + +#ifndef __UNIQUECHARSTR_H__ +#define __UNIQUECHARSTR_H__ + +#include "charstr.h" +#include "uassert.h" +#include "uhash.h" + +U_NAMESPACE_BEGIN + +/** + * Stores NUL-terminated strings with duplicate elimination. + * Checks for unique UTF-16 string pointers and converts to invariant characters. + * + * Intended to be stack-allocated. Add strings, get a unique number for each, + * freeze the object, get a char * pointer for each string, + * call orphanCharStrings() to capture the string storage, and let this object go out of scope. + */ +class UniqueCharStrings { +public: + UniqueCharStrings(UErrorCode &errorCode) : strings(nullptr) { + // Note: We hash on string contents but store stable char16_t * pointers. + // If the strings are stored in resource bundles which should be built with + // duplicate elimination, then we should be able to hash on just the pointer values. + uhash_init(&map, uhash_hashUChars, uhash_compareUChars, uhash_compareLong, &errorCode); + if (U_FAILURE(errorCode)) { return; } + strings = new CharString(); + if (strings == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + } + } + ~UniqueCharStrings() { + uhash_close(&map); + delete strings; + } + + /** Returns/orphans the CharString that contains all strings. */ + CharString *orphanCharStrings() { + CharString *result = strings; + strings = nullptr; + return result; + } + + /** + * Adds a string and returns a unique number for it. + * The string's buffer contents must not change, nor move around in memory, + * while this UniqueCharStrings is in use. + * The string contents must be NUL-terminated exactly at s.length(). + * + * Best used with read-only-alias UnicodeString objects that point to + * stable storage, such as strings returned by resource bundle functions. + */ + int32_t add(const UnicodeString &s, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return 0; } + if (isFrozen) { + errorCode = U_NO_WRITE_PERMISSION; + return 0; + } + // The string points into the resource bundle. + const char16_t *p = s.getBuffer(); + int32_t oldIndex = uhash_geti(&map, p); + if (oldIndex != 0) { // found duplicate + return oldIndex; + } + // Explicit NUL terminator for the previous string. + // The strings object is also terminated with one implicit NUL. + strings->append(0, errorCode); + int32_t newIndex = strings->length(); + strings->appendInvariantChars(s, errorCode); + uhash_puti(&map, const_cast(p), newIndex, &errorCode); + return newIndex; + } + + void freeze() { isFrozen = true; } + + /** + * Returns a string pointer for its unique number, if this object is frozen. + * Otherwise nullptr. + */ + const char *get(int32_t i) const { + U_ASSERT(isFrozen); + return isFrozen && i > 0 ? strings->data() + i : nullptr; + } + +private: + UHashtable map; + CharString *strings; + bool isFrozen = false; +}; + +U_NAMESPACE_END + +#endif // __UNIQUECHARSTR_H__ diff --git a/deps/icu-small/source/common/uniset.cpp b/deps/icu-small/source/common/uniset.cpp index 4faace525c595b..93f6ff382f8ed0 100644 --- a/deps/icu-small/source/common/uniset.cpp +++ b/deps/icu-small/source/common/uniset.cpp @@ -1,2355 +1,2355 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2015, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 10/20/99 alan Creation. -********************************************************************** -*/ - -#include "unicode/utypes.h" -#include "unicode/parsepos.h" -#include "unicode/symtable.h" -#include "unicode/uniset.h" -#include "unicode/ustring.h" -#include "unicode/utf8.h" -#include "unicode/utf16.h" -#include "ruleiter.h" -#include "cmemory.h" -#include "cstring.h" -#include "patternprops.h" -#include "uelement.h" -#include "util.h" -#include "uvector.h" -#include "charstr.h" -#include "ustrfmt.h" -#include "uassert.h" -#include "bmpset.h" -#include "unisetspan.h" - -// HIGH_VALUE > all valid values. 110000 for codepoints -#define UNICODESET_HIGH 0x0110000 - -// LOW <= all valid values. ZERO for codepoints -#define UNICODESET_LOW 0x000000 - -/** Max list [0, 1, 2, ..., max code point, HIGH] */ -constexpr int32_t MAX_LENGTH = UNICODESET_HIGH + 1; - -U_NAMESPACE_BEGIN - -SymbolTable::~SymbolTable() {} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UnicodeSet) - -/** - * Modify the given UChar32 variable so that it is in range, by - * pinning values < UNICODESET_LOW to UNICODESET_LOW, and - * pinning values > UNICODESET_HIGH-1 to UNICODESET_HIGH-1. - * It modifies its argument in-place and also returns it. - */ -static inline UChar32 pinCodePoint(UChar32& c) { - if (c < UNICODESET_LOW) { - c = UNICODESET_LOW; - } else if (c > (UNICODESET_HIGH-1)) { - c = (UNICODESET_HIGH-1); - } - return c; -} - -//---------------------------------------------------------------- -// Debugging -//---------------------------------------------------------------- - -// DO NOT DELETE THIS CODE. This code is used to debug memory leaks. -// To enable the debugging, define the symbol DEBUG_MEM in the line -// below. This will result in text being sent to stdout that looks -// like this: -// DEBUG UnicodeSet: ct 0x00A39B20; 397 [\u0A81-\u0A83\u0A85- -// DEBUG UnicodeSet: dt 0x00A39B20; 396 [\u0A81-\u0A83\u0A85- -// Each line lists a construction (ct) or destruction (dt) event, the -// object address, the number of outstanding objects after the event, -// and the pattern of the object in question. - -// #define DEBUG_MEM - -#ifdef DEBUG_MEM -#include -static int32_t _dbgCount = 0; - -static inline void _dbgct(UnicodeSet* set) { - UnicodeString str; - set->toPattern(str, true); - char buf[40]; - str.extract(0, 39, buf, ""); - printf("DEBUG UnicodeSet: ct 0x%08X; %d %s\n", set, ++_dbgCount, buf); -} - -static inline void _dbgdt(UnicodeSet* set) { - UnicodeString str; - set->toPattern(str, true); - char buf[40]; - str.extract(0, 39, buf, ""); - printf("DEBUG UnicodeSet: dt 0x%08X; %d %s\n", set, --_dbgCount, buf); -} - -#else - -#define _dbgct(set) -#define _dbgdt(set) - -#endif - -//---------------------------------------------------------------- -// UnicodeString in UVector support -//---------------------------------------------------------------- - -static void U_CALLCONV cloneUnicodeString(UElement *dst, UElement *src) { - dst->pointer = new UnicodeString(*(UnicodeString*)src->pointer); -} - -static int32_t U_CALLCONV compareUnicodeString(UElement t1, UElement t2) { - const UnicodeString &a = *(const UnicodeString*)t1.pointer; - const UnicodeString &b = *(const UnicodeString*)t2.pointer; - return a.compare(b); -} - -UBool UnicodeSet::hasStrings() const { - return strings != nullptr && !strings->isEmpty(); -} - -int32_t UnicodeSet::stringsSize() const { - return strings == nullptr ? 0 : strings->size(); -} - -UBool UnicodeSet::stringsContains(const UnicodeString &s) const { - return strings != nullptr && strings->contains((void*) &s); -} - -//---------------------------------------------------------------- -// Constructors &c -//---------------------------------------------------------------- - -/** - * Constructs an empty set. - */ -UnicodeSet::UnicodeSet() { - list[0] = UNICODESET_HIGH; - _dbgct(this); -} - -/** - * Constructs a set containing the given range. If end > - * start then an empty set is created. - * - * @param start first character, inclusive, of range - * @param end last character, inclusive, of range - */ -UnicodeSet::UnicodeSet(UChar32 start, UChar32 end) { - list[0] = UNICODESET_HIGH; - add(start, end); - _dbgct(this); -} - -/** - * Constructs a set that is identical to the given UnicodeSet. - */ -UnicodeSet::UnicodeSet(const UnicodeSet& o) : UnicodeFilter(o) { - *this = o; - _dbgct(this); -} - -// Copy-construct as thawed. -UnicodeSet::UnicodeSet(const UnicodeSet& o, UBool /* asThawed */) : UnicodeFilter(o) { - if (ensureCapacity(o.len)) { - // *this = o except for bmpSet and stringSpan - len = o.len; - uprv_memcpy(list, o.list, (size_t)len*sizeof(UChar32)); - if (o.hasStrings()) { - UErrorCode status = U_ZERO_ERROR; - if (!allocateStrings(status) || - (strings->assign(*o.strings, cloneUnicodeString, status), U_FAILURE(status))) { - setToBogus(); - return; - } - } - if (o.pat) { - setPattern(o.pat, o.patLen); - } - _dbgct(this); - } -} - -/** - * Destructs the set. - */ -UnicodeSet::~UnicodeSet() { - _dbgdt(this); // first! - if (list != stackList) { - uprv_free(list); - } - delete bmpSet; - if (buffer != stackList) { - uprv_free(buffer); - } - delete strings; - delete stringSpan; - releasePattern(); -} - -/** - * Assigns this object to be a copy of another. - */ -UnicodeSet& UnicodeSet::operator=(const UnicodeSet& o) { - return copyFrom(o, false); -} - -UnicodeSet& UnicodeSet::copyFrom(const UnicodeSet& o, UBool asThawed) { - if (this == &o) { - return *this; - } - if (isFrozen()) { - return *this; - } - if (o.isBogus()) { - setToBogus(); - return *this; - } - if (!ensureCapacity(o.len)) { - // ensureCapacity will mark the UnicodeSet as Bogus if OOM failure happens. - return *this; - } - len = o.len; - uprv_memcpy(list, o.list, (size_t)len*sizeof(UChar32)); - if (o.bmpSet != nullptr && !asThawed) { - bmpSet = new BMPSet(*o.bmpSet, list, len); - if (bmpSet == NULL) { // Check for memory allocation error. - setToBogus(); - return *this; - } - } - if (o.hasStrings()) { - UErrorCode status = U_ZERO_ERROR; - if ((strings == nullptr && !allocateStrings(status)) || - (strings->assign(*o.strings, cloneUnicodeString, status), U_FAILURE(status))) { - setToBogus(); - return *this; - } - } else if (hasStrings()) { - strings->removeAllElements(); - } - if (o.stringSpan != nullptr && !asThawed) { - stringSpan = new UnicodeSetStringSpan(*o.stringSpan, *strings); - if (stringSpan == NULL) { // Check for memory allocation error. - setToBogus(); - return *this; - } - } - releasePattern(); - if (o.pat) { - setPattern(o.pat, o.patLen); - } - return *this; -} - -/** - * Returns a copy of this object. All UnicodeMatcher objects have - * to support cloning in order to allow classes using - * UnicodeMatchers, such as Transliterator, to implement cloning. - */ -UnicodeSet* UnicodeSet::clone() const { - return new UnicodeSet(*this); -} - -UnicodeSet *UnicodeSet::cloneAsThawed() const { - return new UnicodeSet(*this, true); -} - -/** - * Compares the specified object with this set for equality. Returns - * true if the two sets - * have the same size, and every member of the specified set is - * contained in this set (or equivalently, every member of this set is - * contained in the specified set). - * - * @param o set to be compared for equality with this set. - * @return true if the specified set is equal to this set. - */ -bool UnicodeSet::operator==(const UnicodeSet& o) const { - if (len != o.len) return false; - for (int32_t i = 0; i < len; ++i) { - if (list[i] != o.list[i]) return false; - } - if (hasStrings() != o.hasStrings()) { return false; } - if (hasStrings() && *strings != *o.strings) return false; - return true; -} - -/** - * Returns the hash code value for this set. - * - * @return the hash code value for this set. - * @see Object#hashCode() - */ -int32_t UnicodeSet::hashCode(void) const { - uint32_t result = static_cast(len); - for (int32_t i = 0; i < len; ++i) { - result *= 1000003u; - result += list[i]; - } - return static_cast(result); -} - -//---------------------------------------------------------------- -// Public API -//---------------------------------------------------------------- - -/** - * Returns the number of elements in this set (its cardinality), - * Note than the elements of a set may include both individual - * codepoints and strings. - * - * @return the number of elements in this set (its cardinality). - */ -int32_t UnicodeSet::size(void) const { - int32_t n = 0; - int32_t count = getRangeCount(); - for (int32_t i = 0; i < count; ++i) { - n += getRangeEnd(i) - getRangeStart(i) + 1; - } - return n + stringsSize(); -} - -/** - * Returns true if this set contains no elements. - * - * @return true if this set contains no elements. - */ -UBool UnicodeSet::isEmpty(void) const { - return len == 1 && !hasStrings(); -} - -/** - * Returns true if this set contains the given character. - * @param c character to be checked for containment - * @return true if the test condition is met - */ -UBool UnicodeSet::contains(UChar32 c) const { - // Set i to the index of the start item greater than ch - // We know we will terminate without length test! - // LATER: for large sets, add binary search - //int32_t i = -1; - //for (;;) { - // if (c < list[++i]) break; - //} - if (bmpSet != NULL) { - return bmpSet->contains(c); - } - if (stringSpan != NULL) { - return stringSpan->contains(c); - } - if (c >= UNICODESET_HIGH) { // Don't need to check LOW bound - return false; - } - int32_t i = findCodePoint(c); - return (UBool)(i & 1); // return true if odd -} - -/** - * Returns the smallest value i such that c < list[i]. Caller - * must ensure that c is a legal value or this method will enter - * an infinite loop. This method performs a binary search. - * @param c a character in the range MIN_VALUE..MAX_VALUE - * inclusive - * @return the smallest integer i in the range 0..len-1, - * inclusive, such that c < list[i] - */ -int32_t UnicodeSet::findCodePoint(UChar32 c) const { - /* Examples: - findCodePoint(c) - set list[] c=0 1 3 4 7 8 - === ============== =========== - [] [110000] 0 0 0 0 0 0 - [\u0000-\u0003] [0, 4, 110000] 1 1 1 2 2 2 - [\u0004-\u0007] [4, 8, 110000] 0 0 0 1 1 2 - [:Any:] [0, 110000] 1 1 1 1 1 1 - */ - - // Return the smallest i such that c < list[i]. Assume - // list[len - 1] == HIGH and that c is legal (0..HIGH-1). - if (c < list[0]) - return 0; - // High runner test. c is often after the last range, so an - // initial check for this condition pays off. - int32_t lo = 0; - int32_t hi = len - 1; - if (lo >= hi || c >= list[hi-1]) - return hi; - // invariant: c >= list[lo] - // invariant: c < list[hi] - for (;;) { - int32_t i = (lo + hi) >> 1; - if (i == lo) { - break; // Found! - } else if (c < list[i]) { - hi = i; - } else { - lo = i; - } - } - return hi; -} - -/** - * Returns true if this set contains every character - * of the given range. - * @param start first character, inclusive, of the range - * @param end last character, inclusive, of the range - * @return true if the test condition is met - */ -UBool UnicodeSet::contains(UChar32 start, UChar32 end) const { - //int32_t i = -1; - //for (;;) { - // if (start < list[++i]) break; - //} - int32_t i = findCodePoint(start); - return ((i & 1) != 0 && end < list[i]); -} - -/** - * Returns true if this set contains the given - * multicharacter string. - * @param s string to be checked for containment - * @return true if this set contains the specified string - */ -UBool UnicodeSet::contains(const UnicodeString& s) const { - int32_t cp = getSingleCP(s); - if (cp < 0) { - return stringsContains(s); - } else { - return contains((UChar32) cp); - } -} - -/** - * Returns true if this set contains all the characters and strings - * of the given set. - * @param c set to be checked for containment - * @return true if the test condition is met - */ -UBool UnicodeSet::containsAll(const UnicodeSet& c) const { - // The specified set is a subset if all of its pairs are contained in - // this set. It's possible to code this more efficiently in terms of - // direct manipulation of the inversion lists if the need arises. - int32_t n = c.getRangeCount(); - for (int i=0; icontainsAll(*c.strings)); -} - -/** - * Returns true if this set contains all the characters - * of the given string. - * @param s string containing characters to be checked for containment - * @return true if the test condition is met - */ -UBool UnicodeSet::containsAll(const UnicodeString& s) const { - return (UBool)(span(s.getBuffer(), s.length(), USET_SPAN_CONTAINED) == - s.length()); -} - -/** - * Returns true if this set contains none of the characters - * of the given range. - * @param start first character, inclusive, of the range - * @param end last character, inclusive, of the range - * @return true if the test condition is met - */ -UBool UnicodeSet::containsNone(UChar32 start, UChar32 end) const { - //int32_t i = -1; - //for (;;) { - // if (start < list[++i]) break; - //} - int32_t i = findCodePoint(start); - return ((i & 1) == 0 && end < list[i]); -} - -/** - * Returns true if this set contains none of the characters and strings - * of the given set. - * @param c set to be checked for containment - * @return true if the test condition is met - */ -UBool UnicodeSet::containsNone(const UnicodeSet& c) const { - // The specified set is a subset if all of its pairs are contained in - // this set. It's possible to code this more efficiently in terms of - // direct manipulation of the inversion lists if the need arises. - int32_t n = c.getRangeCount(); - for (int32_t i=0; icontainsNone(*c.strings); -} - -/** - * Returns true if this set contains none of the characters - * of the given string. - * @param s string containing characters to be checked for containment - * @return true if the test condition is met - */ -UBool UnicodeSet::containsNone(const UnicodeString& s) const { - return (UBool)(span(s.getBuffer(), s.length(), USET_SPAN_NOT_CONTAINED) == - s.length()); -} - -/** - * Returns true if this set contains any character whose low byte - * is the given value. This is used by RuleBasedTransliterator for - * indexing. - */ -UBool UnicodeSet::matchesIndexValue(uint8_t v) const { - /* The index value v, in the range [0,255], is contained in this set if - * it is contained in any pair of this set. Pairs either have the high - * bytes equal, or unequal. If the high bytes are equal, then we have - * aaxx..aayy, where aa is the high byte. Then v is contained if xx <= - * v <= yy. If the high bytes are unequal we have aaxx..bbyy, bb>aa. - * Then v is contained if xx <= v || v <= yy. (This is identical to the - * time zone month containment logic.) - */ - int32_t i; - int32_t rangeCount=getRangeCount(); - for (i=0; isize(); ++i) { - const UnicodeString& s = *(const UnicodeString*)strings->elementAt(i); - if (s.isEmpty()) { - continue; // skip the empty string - } - UChar32 c = s.char32At(0); - if ((c & 0xFF) == v) { - return true; - } - } - } - return false; -} - -/** - * Implementation of UnicodeMatcher::matches(). Always matches the - * longest possible multichar string. - */ -UMatchDegree UnicodeSet::matches(const Replaceable& text, - int32_t& offset, - int32_t limit, - UBool incremental) { - if (offset == limit) { - if (contains(U_ETHER)) { - return incremental ? U_PARTIAL_MATCH : U_MATCH; - } else { - return U_MISMATCH; - } - } else { - if (hasStrings()) { // try strings first - - // might separate forward and backward loops later - // for now they are combined - - // TODO Improve efficiency of this, at least in the forward - // direction, if not in both. In the forward direction we - // can assume the strings are sorted. - - int32_t i; - UBool forward = offset < limit; - - // firstChar is the leftmost char to match in the - // forward direction or the rightmost char to match in - // the reverse direction. - UChar firstChar = text.charAt(offset); - - // If there are multiple strings that can match we - // return the longest match. - int32_t highWaterLength = 0; - - for (i=0; isize(); ++i) { - const UnicodeString& trial = *(const UnicodeString*)strings->elementAt(i); - if (trial.isEmpty()) { - continue; // skip the empty string - } - - UChar c = trial.charAt(forward ? 0 : trial.length() - 1); - - // Strings are sorted, so we can optimize in the - // forward direction. - if (forward && c > firstChar) break; - if (c != firstChar) continue; - - int32_t matchLen = matchRest(text, offset, limit, trial); - - if (incremental) { - int32_t maxLen = forward ? limit-offset : offset-limit; - if (matchLen == maxLen) { - // We have successfully matched but only up to limit. - return U_PARTIAL_MATCH; - } - } - - if (matchLen == trial.length()) { - // We have successfully matched the whole string. - if (matchLen > highWaterLength) { - highWaterLength = matchLen; - } - // In the forward direction we know strings - // are sorted so we can bail early. - if (forward && matchLen < highWaterLength) { - break; - } - continue; - } - } - - // We've checked all strings without a partial match. - // If we have full matches, return the longest one. - if (highWaterLength != 0) { - offset += forward ? highWaterLength : -highWaterLength; - return U_MATCH; - } - } - return UnicodeFilter::matches(text, offset, limit, incremental); - } -} - -/** - * Returns the longest match for s in text at the given position. - * If limit > start then match forward from start+1 to limit - * matching all characters except s.charAt(0). If limit < start, - * go backward starting from start-1 matching all characters - * except s.charAt(s.length()-1). This method assumes that the - * first character, text.charAt(start), matches s, so it does not - * check it. - * @param text the text to match - * @param start the first character to match. In the forward - * direction, text.charAt(start) is matched against s.charAt(0). - * In the reverse direction, it is matched against - * s.charAt(s.length()-1). - * @param limit the limit offset for matching, either last+1 in - * the forward direction, or last-1 in the reverse direction, - * where last is the index of the last character to match. - * @return If part of s matches up to the limit, return |limit - - * start|. If all of s matches before reaching the limit, return - * s.length(). If there is a mismatch between s and text, return - * 0 - */ -int32_t UnicodeSet::matchRest(const Replaceable& text, - int32_t start, int32_t limit, - const UnicodeString& s) { - int32_t i; - int32_t maxLen; - int32_t slen = s.length(); - if (start < limit) { - maxLen = limit - start; - if (maxLen > slen) maxLen = slen; - for (i = 1; i < maxLen; ++i) { - if (text.charAt(start + i) != s.charAt(i)) return 0; - } - } else { - maxLen = start - limit; - if (maxLen > slen) maxLen = slen; - --slen; // <=> slen = s.length() - 1; - for (i = 1; i < maxLen; ++i) { - if (text.charAt(start - i) != s.charAt(slen - i)) return 0; - } - } - return maxLen; -} - -/** - * Implement of UnicodeMatcher - */ -void UnicodeSet::addMatchSetTo(UnicodeSet& toUnionTo) const { - toUnionTo.addAll(*this); -} - -/** - * Returns the index of the given character within this set, where - * the set is ordered by ascending code point. If the character - * is not in this set, return -1. The inverse of this method is - * charAt(). - * @return an index from 0..size()-1, or -1 - */ -int32_t UnicodeSet::indexOf(UChar32 c) const { - if (c < MIN_VALUE || c > MAX_VALUE) { - return -1; - } - int32_t i = 0; - int32_t n = 0; - for (;;) { - UChar32 start = list[i++]; - if (c < start) { - return -1; - } - UChar32 limit = list[i++]; - if (c < limit) { - return n + c - start; - } - n += limit - start; - } -} - -/** - * Returns the character at the given index within this set, where - * the set is ordered by ascending code point. If the index is - * out of range, return (UChar32)-1. The inverse of this method is - * indexOf(). - * @param index an index from 0..size()-1 - * @return the character at the given index, or (UChar32)-1. - */ -UChar32 UnicodeSet::charAt(int32_t index) const { - if (index >= 0) { - // len2 is the largest even integer <= len, that is, it is len - // for even values and len-1 for odd values. With odd values - // the last entry is UNICODESET_HIGH. - int32_t len2 = len & ~1; - for (int32_t i=0; i < len2;) { - UChar32 start = list[i++]; - int32_t count = list[i++] - start; - if (index < count) { - return (UChar32)(start + index); - } - index -= count; - } - } - return (UChar32)-1; -} - -/** - * Make this object represent the range start - end. - * If end > start then this object is set to an - * an empty range. - * - * @param start first character in the set, inclusive - * @rparam end last character in the set, inclusive - */ -UnicodeSet& UnicodeSet::set(UChar32 start, UChar32 end) { - clear(); - complement(start, end); - return *this; -} - -/** - * Adds the specified range to this set if it is not already - * present. If this set already contains the specified range, - * the call leaves this set unchanged. If end > start - * then an empty range is added, leaving the set unchanged. - * - * @param start first character, inclusive, of range to be added - * to this set. - * @param end last character, inclusive, of range to be added - * to this set. - */ -UnicodeSet& UnicodeSet::add(UChar32 start, UChar32 end) { - if (pinCodePoint(start) < pinCodePoint(end)) { - UChar32 limit = end + 1; - // Fast path for adding a new range after the last one. - // Odd list length: [..., lastStart, lastLimit, HIGH] - if ((len & 1) != 0) { - // If the list is empty, set lastLimit low enough to not be adjacent to 0. - UChar32 lastLimit = len == 1 ? -2 : list[len - 2]; - if (lastLimit <= start && !isFrozen() && !isBogus()) { - if (lastLimit == start) { - // Extend the last range. - list[len - 2] = limit; - if (limit == UNICODESET_HIGH) { - --len; - } - } else { - list[len - 1] = start; - if (limit < UNICODESET_HIGH) { - if (ensureCapacity(len + 2)) { - list[len++] = limit; - list[len++] = UNICODESET_HIGH; - } - } else { // limit == UNICODESET_HIGH - if (ensureCapacity(len + 1)) { - list[len++] = UNICODESET_HIGH; - } - } - } - releasePattern(); - return *this; - } - } - // This is slow. Could be much faster using findCodePoint(start) - // and modifying the list, dealing with adjacent & overlapping ranges. - UChar32 range[3] = { start, limit, UNICODESET_HIGH }; - add(range, 2, 0); - } else if (start == end) { - add(start); - } - return *this; -} - -// #define DEBUG_US_ADD - -#ifdef DEBUG_US_ADD -#include -void dump(UChar32 c) { - if (c <= 0xFF) { - printf("%c", (char)c); - } else { - printf("U+%04X", c); - } -} -void dump(const UChar32* list, int32_t len) { - printf("["); - for (int32_t i=0; i "); -#endif - - if (c == list[i]-1) { - // c is before start of next range - list[i] = c; - // if we touched the HIGH mark, then add a new one - if (c == (UNICODESET_HIGH - 1)) { - if (!ensureCapacity(len+1)) { - // ensureCapacity will mark the object as Bogus if OOM failure happens. - return *this; - } - list[len++] = UNICODESET_HIGH; - } - if (i > 0 && c == list[i-1]) { - // collapse adjacent ranges - - // [..., start_k-1, c, c, limit_k, ..., HIGH] - // ^ - // list[i] - - //for (int32_t k=i-1; k 0 && c == list[i-1]) { - // c is after end of prior range - list[i-1]++; - // no need to check for collapse here - } - - else { - // At this point we know the new char is not adjacent to - // any existing ranges, and it is not 10FFFF. - - - // [..., start_k-1, limit_k-1, start_k, limit_k, ..., HIGH] - // ^ - // list[i] - - // [..., start_k-1, limit_k-1, c, c+1, start_k, limit_k, ..., HIGH] - // ^ - // list[i] - - if (!ensureCapacity(len+2)) { - // ensureCapacity will mark the object as Bogus if OOM failure happens. - return *this; - } - - UChar32 *p = list + i; - uprv_memmove(p + 2, p, (len - i) * sizeof(*p)); - list[i] = c; - list[i+1] = c+1; - len += 2; - } - -#ifdef DEBUG_US_ADD - dump(list, len); - printf("\n"); - - for (i=1; i {"ch"} - * - * @param s the source string - * @return the modified set, for chaining - */ -UnicodeSet& UnicodeSet::add(const UnicodeString& s) { - if (isFrozen() || isBogus()) return *this; - int32_t cp = getSingleCP(s); - if (cp < 0) { - if (!stringsContains(s)) { - _add(s); - releasePattern(); - } - } else { - add((UChar32)cp); - } - return *this; -} - -/** - * Adds the given string, in order, to 'strings'. The given string - * must have been checked by the caller to not already be in 'strings'. - */ -void UnicodeSet::_add(const UnicodeString& s) { - if (isFrozen() || isBogus()) { - return; - } - UErrorCode ec = U_ZERO_ERROR; - if (strings == nullptr && !allocateStrings(ec)) { - setToBogus(); - return; - } - UnicodeString* t = new UnicodeString(s); - if (t == NULL) { // Check for memory allocation error. - setToBogus(); - return; - } - strings->sortedInsert(t, compareUnicodeString, ec); - if (U_FAILURE(ec)) { - setToBogus(); - } -} - -/** - * @return a code point IF the string consists of a single one. - * otherwise returns -1. - * @param string to test - */ -int32_t UnicodeSet::getSingleCP(const UnicodeString& s) { - int32_t sLength = s.length(); - if (sLength == 1) return s.charAt(0); - if (sLength == 2) { - UChar32 cp = s.char32At(0); - if (cp > 0xFFFF) { // is surrogate pair - return cp; - } - } - return -1; -} - -/** - * Adds each of the characters in this string to the set. Thus "ch" => {"c", "h"} - * If this set already any particular character, it has no effect on that character. - * @param the source string - * @return the modified set, for chaining - */ -UnicodeSet& UnicodeSet::addAll(const UnicodeString& s) { - UChar32 cp; - for (int32_t i = 0; i < s.length(); i += U16_LENGTH(cp)) { - cp = s.char32At(i); - add(cp); - } - return *this; -} - -/** - * Retains EACH of the characters in this string. Note: "ch" == {"c", "h"} - * If this set already any particular character, it has no effect on that character. - * @param the source string - * @return the modified set, for chaining - */ -UnicodeSet& UnicodeSet::retainAll(const UnicodeString& s) { - UnicodeSet set; - set.addAll(s); - retainAll(set); - return *this; -} - -/** - * Complement EACH of the characters in this string. Note: "ch" == {"c", "h"} - * If this set already any particular character, it has no effect on that character. - * @param the source string - * @return the modified set, for chaining - */ -UnicodeSet& UnicodeSet::complementAll(const UnicodeString& s) { - UnicodeSet set; - set.addAll(s); - complementAll(set); - return *this; -} - -/** - * Remove EACH of the characters in this string. Note: "ch" == {"c", "h"} - * If this set already any particular character, it has no effect on that character. - * @param the source string - * @return the modified set, for chaining - */ -UnicodeSet& UnicodeSet::removeAll(const UnicodeString& s) { - UnicodeSet set; - set.addAll(s); - removeAll(set); - return *this; -} - -UnicodeSet& UnicodeSet::removeAllStrings() { - if (!isFrozen() && hasStrings()) { - strings->removeAllElements(); - releasePattern(); - } - return *this; -} - - -/** - * Makes a set from a multicharacter string. Thus "ch" => {"ch"} - *
Warning: you cannot add an empty string ("") to a UnicodeSet. - * @param the source string - * @return a newly created set containing the given string - */ -UnicodeSet* U_EXPORT2 UnicodeSet::createFrom(const UnicodeString& s) { - UnicodeSet *set = new UnicodeSet(); - if (set != NULL) { // Check for memory allocation error. - set->add(s); - } - return set; -} - - -/** - * Makes a set from each of the characters in the string. Thus "ch" => {"c", "h"} - * @param the source string - * @return a newly created set containing the given characters - */ -UnicodeSet* U_EXPORT2 UnicodeSet::createFromAll(const UnicodeString& s) { - UnicodeSet *set = new UnicodeSet(); - if (set != NULL) { // Check for memory allocation error. - set->addAll(s); - } - return set; -} - -/** - * Retain only the elements in this set that are contained in the - * specified range. If end > start then an empty range is - * retained, leaving the set empty. - * - * @param start first character, inclusive, of range to be retained - * to this set. - * @param end last character, inclusive, of range to be retained - * to this set. - */ -UnicodeSet& UnicodeSet::retain(UChar32 start, UChar32 end) { - if (pinCodePoint(start) <= pinCodePoint(end)) { - UChar32 range[3] = { start, end+1, UNICODESET_HIGH }; - retain(range, 2, 0); - } else { - clear(); - } - return *this; -} - -UnicodeSet& UnicodeSet::retain(UChar32 c) { - return retain(c, c); -} - -UnicodeSet& UnicodeSet::retain(const UnicodeString &s) { - if (isFrozen() || isBogus()) { return *this; } - UChar32 cp = getSingleCP(s); - if (cp < 0) { - bool isIn = stringsContains(s); - // Check for getRangeCount() first to avoid somewhat-expensive size() - // when there are single code points. - if (isIn && getRangeCount() == 0 && size() == 1) { - return *this; - } - clear(); - if (isIn) { - _add(s); - } - } else { - retain(cp, cp); - } - return *this; -} - -/** - * Removes the specified range from this set if it is present. - * The set will not contain the specified range once the call - * returns. If end > start then an empty range is - * removed, leaving the set unchanged. - * - * @param start first character, inclusive, of range to be removed - * from this set. - * @param end last character, inclusive, of range to be removed - * from this set. - */ -UnicodeSet& UnicodeSet::remove(UChar32 start, UChar32 end) { - if (pinCodePoint(start) <= pinCodePoint(end)) { - UChar32 range[3] = { start, end+1, UNICODESET_HIGH }; - retain(range, 2, 2); - } - return *this; -} - -/** - * Removes the specified character from this set if it is present. - * The set will not contain the specified range once the call - * returns. - */ -UnicodeSet& UnicodeSet::remove(UChar32 c) { - return remove(c, c); -} - -/** - * Removes the specified string from this set if it is present. - * The set will not contain the specified character once the call - * returns. - * @param the source string - * @return the modified set, for chaining - */ -UnicodeSet& UnicodeSet::remove(const UnicodeString& s) { - if (isFrozen() || isBogus()) return *this; - int32_t cp = getSingleCP(s); - if (cp < 0) { - if (strings != nullptr && strings->removeElement((void*) &s)) { - releasePattern(); - } - } else { - remove((UChar32)cp, (UChar32)cp); - } - return *this; -} - -/** - * Complements the specified range in this set. Any character in - * the range will be removed if it is in this set, or will be - * added if it is not in this set. If end > start - * then an empty range is xor'ed, leaving the set unchanged. - * - * @param start first character, inclusive, of range to be removed - * from this set. - * @param end last character, inclusive, of range to be removed - * from this set. - */ -UnicodeSet& UnicodeSet::complement(UChar32 start, UChar32 end) { - if (isFrozen() || isBogus()) { - return *this; - } - if (pinCodePoint(start) <= pinCodePoint(end)) { - UChar32 range[3] = { start, end+1, UNICODESET_HIGH }; - exclusiveOr(range, 2, 0); - } - releasePattern(); - return *this; -} - -UnicodeSet& UnicodeSet::complement(UChar32 c) { - return complement(c, c); -} - -/** - * This is equivalent to - * complement(MIN_VALUE, MAX_VALUE). - */ -UnicodeSet& UnicodeSet::complement(void) { - if (isFrozen() || isBogus()) { - return *this; - } - if (list[0] == UNICODESET_LOW) { - uprv_memmove(list, list + 1, (size_t)(len-1)*sizeof(UChar32)); - --len; - } else { - if (!ensureCapacity(len+1)) { - return *this; - } - uprv_memmove(list + 1, list, (size_t)len*sizeof(UChar32)); - list[0] = UNICODESET_LOW; - ++len; - } - releasePattern(); - return *this; -} - -/** - * Complement the specified string in this set. - * The set will not contain the specified string once the call - * returns. - * - * @param s the string to complement - * @return this object, for chaining - */ -UnicodeSet& UnicodeSet::complement(const UnicodeString& s) { - if (isFrozen() || isBogus()) return *this; - int32_t cp = getSingleCP(s); - if (cp < 0) { - if (stringsContains(s)) { - strings->removeElement((void*) &s); - } else { - _add(s); - } - releasePattern(); - } else { - complement((UChar32)cp, (UChar32)cp); - } - return *this; -} - -/** - * Adds all of the elements in the specified set to this set if - * they're not already present. This operation effectively - * modifies this set so that its value is the union of the two - * sets. The behavior of this operation is unspecified if the specified - * collection is modified while the operation is in progress. - * - * @param c set whose elements are to be added to this set. - * @see #add(char, char) - */ -UnicodeSet& UnicodeSet::addAll(const UnicodeSet& c) { - if ( c.len>0 && c.list!=NULL ) { - add(c.list, c.len, 0); - } - - // Add strings in order - if ( c.strings!=NULL ) { - for (int32_t i=0; isize(); ++i) { - const UnicodeString* s = (const UnicodeString*)c.strings->elementAt(i); - if (!stringsContains(*s)) { - _add(*s); - } - } - } - return *this; -} - -/** - * Retains only the elements in this set that are contained in the - * specified set. In other words, removes from this set all of - * its elements that are not contained in the specified set. This - * operation effectively modifies this set so that its value is - * the intersection of the two sets. - * - * @param c set that defines which elements this set will retain. - */ -UnicodeSet& UnicodeSet::retainAll(const UnicodeSet& c) { - if (isFrozen() || isBogus()) { - return *this; - } - retain(c.list, c.len, 0); - if (hasStrings()) { - if (!c.hasStrings()) { - strings->removeAllElements(); - } else { - strings->retainAll(*c.strings); - } - } - return *this; -} - -/** - * Removes from this set all of its elements that are contained in the - * specified set. This operation effectively modifies this - * set so that its value is the asymmetric set difference of - * the two sets. - * - * @param c set that defines which elements will be removed from - * this set. - */ -UnicodeSet& UnicodeSet::removeAll(const UnicodeSet& c) { - if (isFrozen() || isBogus()) { - return *this; - } - retain(c.list, c.len, 2); - if (hasStrings() && c.hasStrings()) { - strings->removeAll(*c.strings); - } - return *this; -} - -/** - * Complements in this set all elements contained in the specified - * set. Any character in the other set will be removed if it is - * in this set, or will be added if it is not in this set. - * - * @param c set that defines which elements will be xor'ed from - * this set. - */ -UnicodeSet& UnicodeSet::complementAll(const UnicodeSet& c) { - if (isFrozen() || isBogus()) { - return *this; - } - exclusiveOr(c.list, c.len, 0); - - if (c.strings != nullptr) { - for (int32_t i=0; isize(); ++i) { - void* e = c.strings->elementAt(i); - if (strings == nullptr || !strings->removeElement(e)) { - _add(*(const UnicodeString*)e); - } - } - } - return *this; -} - -/** - * Removes all of the elements from this set. This set will be - * empty after this call returns. - */ -UnicodeSet& UnicodeSet::clear(void) { - if (isFrozen()) { - return *this; - } - list[0] = UNICODESET_HIGH; - len = 1; - releasePattern(); - if (strings != NULL) { - strings->removeAllElements(); - } - // Remove bogus - fFlags = 0; - return *this; -} - -/** - * Iteration method that returns the number of ranges contained in - * this set. - * @see #getRangeStart - * @see #getRangeEnd - */ -int32_t UnicodeSet::getRangeCount() const { - return len/2; -} - -/** - * Iteration method that returns the first character in the - * specified range of this set. - * @see #getRangeCount - * @see #getRangeEnd - */ -UChar32 UnicodeSet::getRangeStart(int32_t index) const { - return list[index*2]; -} - -/** - * Iteration method that returns the last character in the - * specified range of this set. - * @see #getRangeStart - * @see #getRangeEnd - */ -UChar32 UnicodeSet::getRangeEnd(int32_t index) const { - return list[index*2 + 1] - 1; -} - -const UnicodeString* UnicodeSet::getString(int32_t index) const { - return (const UnicodeString*) strings->elementAt(index); -} - -/** - * Reallocate this objects internal structures to take up the least - * possible space, without changing this object's value. - */ -UnicodeSet& UnicodeSet::compact() { - if (isFrozen() || isBogus()) { - return *this; - } - // Delete buffer first to defragment memory less. - if (buffer != stackList) { - uprv_free(buffer); - buffer = NULL; - bufferCapacity = 0; - } - if (list == stackList) { - // pass - } else if (len <= INITIAL_CAPACITY) { - uprv_memcpy(stackList, list, len * sizeof(UChar32)); - uprv_free(list); - list = stackList; - capacity = INITIAL_CAPACITY; - } else if ((len + 7) < capacity) { - // If we have more than a little unused capacity, shrink it to len. - UChar32* temp = (UChar32*) uprv_realloc(list, sizeof(UChar32) * len); - if (temp) { - list = temp; - capacity = len; - } - // else what the heck happened?! We allocated less memory! - // Oh well. We'll keep our original array. - } - if (strings != nullptr && strings->isEmpty()) { - delete strings; - strings = nullptr; - } - return *this; -} - -#ifdef DEBUG_SERIALIZE -#include -#endif - -/** - * Deserialize constructor. - */ -UnicodeSet::UnicodeSet(const uint16_t data[], int32_t dataLen, ESerialization serialization, - UErrorCode &ec) { - - if(U_FAILURE(ec)) { - setToBogus(); - return; - } - - if( (serialization != kSerialized) - || (data==NULL) - || (dataLen < 1)) { - ec = U_ILLEGAL_ARGUMENT_ERROR; - setToBogus(); - return; - } - - // bmp? - int32_t headerSize = ((data[0]&0x8000)) ?2:1; - int32_t bmpLength = (headerSize==1)?data[0]:data[1]; - - int32_t newLength = (((data[0]&0x7FFF)-bmpLength)/2)+bmpLength; -#ifdef DEBUG_SERIALIZE - printf("dataLen %d headerSize %d bmpLen %d len %d. data[0]=%X/%X/%X/%X\n", dataLen,headerSize,bmpLength,newLength, data[0],data[1],data[2],data[3]); -#endif - if(!ensureCapacity(newLength + 1)) { // +1 for HIGH - return; - } - // copy bmp - int32_t i; - for(i = 0; i< bmpLength;i++) { - list[i] = data[i+headerSize]; -#ifdef DEBUG_SERIALIZE - printf("<<16@%d[%d] %X\n", i+headerSize, i, list[i]); -#endif - } - // copy smp - for(i=bmpLength;i0 && dest==NULL)) { - ec=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* count necessary 16-bit units */ - length=this->len-1; // Subtract 1 to ignore final UNICODESET_HIGH - // assert(length>=0); - if (length==0) { - /* empty set */ - if (destCapacity>0) { - *dest=0; - } else { - ec=U_BUFFER_OVERFLOW_ERROR; - } - return 1; - } - /* now length>0 */ - - if (this->list[length-1]<=0xffff) { - /* all BMP */ - bmpLength=length; - } else if (this->list[0]>=0x10000) { - /* all supplementary */ - bmpLength=0; - length*=2; - } else { - /* some BMP, some supplementary */ - for (bmpLength=0; bmpLengthlist[bmpLength]<=0xffff; ++bmpLength) {} - length=bmpLength+2*(length-bmpLength); - } -#ifdef DEBUG_SERIALIZE - printf(">> bmpLength%d length%d len%d\n", bmpLength, length, len); -#endif - /* length: number of 16-bit array units */ - if (length>0x7fff) { - /* there are only 15 bits for the length in the first serialized word */ - ec=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - /* - * total serialized length: - * number of 16-bit array units (length) + - * 1 length unit (always) + - * 1 bmpLength unit (if there are supplementary values) - */ - destLength=length+((length>bmpLength)?2:1); - if (destLength<=destCapacity) { - const UChar32 *p; - int32_t i; - -#ifdef DEBUG_SERIALIZE - printf("writeHdr\n"); -#endif - *dest=(uint16_t)length; - if (length>bmpLength) { - *dest|=0x8000; - *++dest=(uint16_t)bmpLength; - } - ++dest; - - /* write the BMP part of the array */ - p=this->list; - for (i=0; i>16); - *dest++=(uint16_t)*p++; - } - } else { - ec=U_BUFFER_OVERFLOW_ERROR; - } - return destLength; -} - -//---------------------------------------------------------------- -// Implementation: Utility methods -//---------------------------------------------------------------- - -/** - * Allocate our strings vector and return true if successful. - */ -UBool UnicodeSet::allocateStrings(UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - strings = new UVector(uprv_deleteUObject, - uhash_compareUnicodeString, 1, status); - if (strings == NULL) { // Check for memory allocation error. - status = U_MEMORY_ALLOCATION_ERROR; - return false; - } - if (U_FAILURE(status)) { - delete strings; - strings = NULL; - return false; - } - return true; -} - -int32_t UnicodeSet::nextCapacity(int32_t minCapacity) { - // Grow exponentially to reduce the frequency of allocations. - if (minCapacity < INITIAL_CAPACITY) { - return minCapacity + INITIAL_CAPACITY; - } else if (minCapacity <= 2500) { - return 5 * minCapacity; - } else { - int32_t newCapacity = 2 * minCapacity; - if (newCapacity > MAX_LENGTH) { - newCapacity = MAX_LENGTH; - } - return newCapacity; - } -} - -bool UnicodeSet::ensureCapacity(int32_t newLen) { - if (newLen > MAX_LENGTH) { - newLen = MAX_LENGTH; - } - if (newLen <= capacity) { - return true; - } - int32_t newCapacity = nextCapacity(newLen); - UChar32* temp = (UChar32*) uprv_malloc(newCapacity * sizeof(UChar32)); - if (temp == NULL) { - setToBogus(); // set the object to bogus state if an OOM failure occurred. - return false; - } - // Copy only the actual contents. - uprv_memcpy(temp, list, len * sizeof(UChar32)); - if (list != stackList) { - uprv_free(list); - } - list = temp; - capacity = newCapacity; - return true; -} - -bool UnicodeSet::ensureBufferCapacity(int32_t newLen) { - if (newLen > MAX_LENGTH) { - newLen = MAX_LENGTH; - } - if (newLen <= bufferCapacity) { - return true; - } - int32_t newCapacity = nextCapacity(newLen); - UChar32* temp = (UChar32*) uprv_malloc(newCapacity * sizeof(UChar32)); - if (temp == NULL) { - setToBogus(); - return false; - } - // The buffer has no contents to be copied. - // It is always filled from scratch after this call. - if (buffer != stackList) { - uprv_free(buffer); - } - buffer = temp; - bufferCapacity = newCapacity; - return true; -} - -/** - * Swap list and buffer. - */ -void UnicodeSet::swapBuffers(void) { - // swap list and buffer - UChar32* temp = list; - list = buffer; - buffer = temp; - - int32_t c = capacity; - capacity = bufferCapacity; - bufferCapacity = c; -} - -void UnicodeSet::setToBogus() { - clear(); // Remove everything in the set. - fFlags = kIsBogus; -} - -//---------------------------------------------------------------- -// Implementation: Fundamental operators -//---------------------------------------------------------------- - -static inline UChar32 max(UChar32 a, UChar32 b) { - return (a > b) ? a : b; -} - -// polarity = 0, 3 is normal: x xor y -// polarity = 1, 2: x xor ~y == x === y - -void UnicodeSet::exclusiveOr(const UChar32* other, int32_t otherLen, int8_t polarity) { - if (isFrozen() || isBogus()) { - return; - } - if (!ensureBufferCapacity(len + otherLen)) { - return; - } - - int32_t i = 0, j = 0, k = 0; - UChar32 a = list[i++]; - UChar32 b; - if (polarity == 1 || polarity == 2) { - b = UNICODESET_LOW; - if (other[j] == UNICODESET_LOW) { // skip base if already LOW - ++j; - b = other[j]; - } - } else { - b = other[j++]; - } - // simplest of all the routines - // sort the values, discarding identicals! - for (;;) { - if (a < b) { - buffer[k++] = a; - a = list[i++]; - } else if (b < a) { - buffer[k++] = b; - b = other[j++]; - } else if (a != UNICODESET_HIGH) { // at this point, a == b - // discard both values! - a = list[i++]; - b = other[j++]; - } else { // DONE! - buffer[k++] = UNICODESET_HIGH; - len = k; - break; - } - } - swapBuffers(); - releasePattern(); -} - -// polarity = 0 is normal: x union y -// polarity = 2: x union ~y -// polarity = 1: ~x union y -// polarity = 3: ~x union ~y - -void UnicodeSet::add(const UChar32* other, int32_t otherLen, int8_t polarity) { - if (isFrozen() || isBogus() || other==NULL) { - return; - } - if (!ensureBufferCapacity(len + otherLen)) { - return; - } - - int32_t i = 0, j = 0, k = 0; - UChar32 a = list[i++]; - UChar32 b = other[j++]; - // change from xor is that we have to check overlapping pairs - // polarity bit 1 means a is second, bit 2 means b is. - for (;;) { - switch (polarity) { - case 0: // both first; take lower if unequal - if (a < b) { // take a - // Back up over overlapping ranges in buffer[] - if (k > 0 && a <= buffer[k-1]) { - // Pick latter end value in buffer[] vs. list[] - a = max(list[i], buffer[--k]); - } else { - // No overlap - buffer[k++] = a; - a = list[i]; - } - i++; // Common if/else code factored out - polarity ^= 1; - } else if (b < a) { // take b - if (k > 0 && b <= buffer[k-1]) { - b = max(other[j], buffer[--k]); - } else { - buffer[k++] = b; - b = other[j]; - } - j++; - polarity ^= 2; - } else { // a == b, take a, drop b - if (a == UNICODESET_HIGH) goto loop_end; - // This is symmetrical; it doesn't matter if - // we backtrack with a or b. - liu - if (k > 0 && a <= buffer[k-1]) { - a = max(list[i], buffer[--k]); - } else { - // No overlap - buffer[k++] = a; - a = list[i]; - } - i++; - polarity ^= 1; - b = other[j++]; - polarity ^= 2; - } - break; - case 3: // both second; take higher if unequal, and drop other - if (b <= a) { // take a - if (a == UNICODESET_HIGH) goto loop_end; - buffer[k++] = a; - } else { // take b - if (b == UNICODESET_HIGH) goto loop_end; - buffer[k++] = b; - } - a = list[i++]; - polarity ^= 1; // factored common code - b = other[j++]; - polarity ^= 2; - break; - case 1: // a second, b first; if b < a, overlap - if (a < b) { // no overlap, take a - buffer[k++] = a; a = list[i++]; polarity ^= 1; - } else if (b < a) { // OVERLAP, drop b - b = other[j++]; - polarity ^= 2; - } else { // a == b, drop both! - if (a == UNICODESET_HIGH) goto loop_end; - a = list[i++]; - polarity ^= 1; - b = other[j++]; - polarity ^= 2; - } - break; - case 2: // a first, b second; if a < b, overlap - if (b < a) { // no overlap, take b - buffer[k++] = b; - b = other[j++]; - polarity ^= 2; - } else if (a < b) { // OVERLAP, drop a - a = list[i++]; - polarity ^= 1; - } else { // a == b, drop both! - if (a == UNICODESET_HIGH) goto loop_end; - a = list[i++]; - polarity ^= 1; - b = other[j++]; - polarity ^= 2; - } - break; - } - } - loop_end: - buffer[k++] = UNICODESET_HIGH; // terminate - len = k; - swapBuffers(); - releasePattern(); -} - -// polarity = 0 is normal: x intersect y -// polarity = 2: x intersect ~y == set-minus -// polarity = 1: ~x intersect y -// polarity = 3: ~x intersect ~y - -void UnicodeSet::retain(const UChar32* other, int32_t otherLen, int8_t polarity) { - if (isFrozen() || isBogus()) { - return; - } - if (!ensureBufferCapacity(len + otherLen)) { - return; - } - - int32_t i = 0, j = 0, k = 0; - UChar32 a = list[i++]; - UChar32 b = other[j++]; - // change from xor is that we have to check overlapping pairs - // polarity bit 1 means a is second, bit 2 means b is. - for (;;) { - switch (polarity) { - case 0: // both first; drop the smaller - if (a < b) { // drop a - a = list[i++]; - polarity ^= 1; - } else if (b < a) { // drop b - b = other[j++]; - polarity ^= 2; - } else { // a == b, take one, drop other - if (a == UNICODESET_HIGH) goto loop_end; - buffer[k++] = a; - a = list[i++]; - polarity ^= 1; - b = other[j++]; - polarity ^= 2; - } - break; - case 3: // both second; take lower if unequal - if (a < b) { // take a - buffer[k++] = a; - a = list[i++]; - polarity ^= 1; - } else if (b < a) { // take b - buffer[k++] = b; - b = other[j++]; - polarity ^= 2; - } else { // a == b, take one, drop other - if (a == UNICODESET_HIGH) goto loop_end; - buffer[k++] = a; - a = list[i++]; - polarity ^= 1; - b = other[j++]; - polarity ^= 2; - } - break; - case 1: // a second, b first; - if (a < b) { // NO OVERLAP, drop a - a = list[i++]; - polarity ^= 1; - } else if (b < a) { // OVERLAP, take b - buffer[k++] = b; - b = other[j++]; - polarity ^= 2; - } else { // a == b, drop both! - if (a == UNICODESET_HIGH) goto loop_end; - a = list[i++]; - polarity ^= 1; - b = other[j++]; - polarity ^= 2; - } - break; - case 2: // a first, b second; if a < b, overlap - if (b < a) { // no overlap, drop b - b = other[j++]; - polarity ^= 2; - } else if (a < b) { // OVERLAP, take a - buffer[k++] = a; - a = list[i++]; - polarity ^= 1; - } else { // a == b, drop both! - if (a == UNICODESET_HIGH) goto loop_end; - a = list[i++]; - polarity ^= 1; - b = other[j++]; - polarity ^= 2; - } - break; - } - } - loop_end: - buffer[k++] = UNICODESET_HIGH; // terminate - len = k; - swapBuffers(); - releasePattern(); -} - -/** - * Append the toPattern() representation of a - * string to the given StringBuffer. - */ -void UnicodeSet::_appendToPat(UnicodeString& buf, const UnicodeString& s, UBool escapeUnprintable) { - UChar32 cp; - for (int32_t i = 0; i < s.length(); i += U16_LENGTH(cp)) { - _appendToPat(buf, cp = s.char32At(i), escapeUnprintable); - } -} - -/** - * Append the toPattern() representation of a - * character to the given StringBuffer. - */ -void UnicodeSet::_appendToPat(UnicodeString& buf, UChar32 c, UBool escapeUnprintable) { - if (escapeUnprintable ? ICU_Utility::isUnprintable(c) : ICU_Utility::shouldAlwaysBeEscaped(c)) { - // Use hex escape notation (\uxxxx or \Uxxxxxxxx) for anything - // unprintable - ICU_Utility::escape(buf, c); - return; - } - // Okay to let ':' pass through - switch (c) { - case u'[': - case u']': - case u'-': - case u'^': - case u'&': - case u'\\': - case u'{': - case u'}': - case u':': - case SymbolTable::SYMBOL_REF: - buf.append(u'\\'); - break; - default: - // Escape whitespace - if (PatternProps::isWhiteSpace(c)) { - buf.append(u'\\'); - } - break; - } - buf.append(c); -} - -void UnicodeSet::_appendToPat(UnicodeString &result, UChar32 start, UChar32 end, - UBool escapeUnprintable) { - _appendToPat(result, start, escapeUnprintable); - if (start != end) { - if ((start+1) != end || - // Avoid writing what looks like a lead+trail surrogate pair. - start == 0xdbff) { - result.append(u'-'); - } - _appendToPat(result, end, escapeUnprintable); - } -} - -/** - * Append a string representation of this set to result. This will be - * a cleaned version of the string passed to applyPattern(), if there - * is one. Otherwise it will be generated. - */ -UnicodeString& UnicodeSet::_toPattern(UnicodeString& result, - UBool escapeUnprintable) const -{ - if (pat != NULL) { - int32_t i; - int32_t backslashCount = 0; - for (i=0; i= 2 && - // getRangeStart(0) == MIN_VALUE && - // getRangeEnd(last) == MAX_VALUE) - // Invariant: list[len-1] == HIGH == MAX_VALUE + 1 - // If limit == len then len is even and the last range ends with MAX_VALUE. - // - // *But* do not write the inverse (complement) if there are strings. - // Since ICU 70, the '^' performs a code point complement which removes all strings. - if (len >= 4 && list[0] == 0 && limit == len && !hasStrings()) { - // Emit the inverse - result.append(u'^'); - // Offsetting the inversion list index by one lets us - // iterate over the ranges of the set complement. - i = 1; - --limit; - } - - // Emit the ranges as pairs. - while (i < limit) { - UChar32 start = list[i]; // getRangeStart() - UChar32 end = list[i + 1] - 1; // getRangeEnd() = range limit minus one - if (!(0xd800 <= end && end <= 0xdbff)) { - _appendToPat(result, start, end, escapeUnprintable); - i += 2; - } else { - // The range ends with a lead surrogate. - // Avoid writing what looks like a lead+trail surrogate pair. - // 1. Postpone ranges that start with a lead surrogate code point. - int32_t firstLead = i; - while ((i += 2) < limit && list[i] <= 0xdbff) {} - int32_t firstAfterLead = i; - // 2. Write following ranges that start with a trail surrogate code point. - while (i < limit && (start = list[i]) <= 0xdfff) { - _appendToPat(result, start, list[i + 1] - 1, escapeUnprintable); - i += 2; - } - // 3. Now write the postponed ranges. - for (int j = firstLead; j < firstAfterLead; j += 2) { - _appendToPat(result, list[j], list[j + 1] - 1, escapeUnprintable); - } - } - } - - if (strings != nullptr) { - for (int32_t i = 0; isize(); ++i) { - result.append(u'{'); - _appendToPat(result, - *(const UnicodeString*) strings->elementAt(i), - escapeUnprintable); - result.append(u'}'); - } - } - return result.append(u']'); -} - -/** -* Release existing cached pattern -*/ -void UnicodeSet::releasePattern() { - if (pat) { - uprv_free(pat); - pat = NULL; - patLen = 0; - } -} - -/** -* Set the new pattern to cache. -*/ -void UnicodeSet::setPattern(const char16_t *newPat, int32_t newPatLen) { - releasePattern(); - pat = (UChar *)uprv_malloc((newPatLen + 1) * sizeof(UChar)); - if (pat) { - patLen = newPatLen; - u_memcpy(pat, newPat, patLen); - pat[patLen] = 0; - } - // else we don't care if malloc failed. This was just a nice cache. - // We can regenerate an equivalent pattern later when requested. -} - -UnicodeSet *UnicodeSet::freeze() { - if(!isFrozen() && !isBogus()) { - compact(); - - // Optimize contains() and span() and similar functions. - if (hasStrings()) { - stringSpan = new UnicodeSetStringSpan(*this, *strings, UnicodeSetStringSpan::ALL); - if (stringSpan == nullptr) { - setToBogus(); - return this; - } else if (!stringSpan->needsStringSpanUTF16()) { - // All strings are irrelevant for span() etc. because - // all of each string's code points are contained in this set. - // Do not check needsStringSpanUTF8() because UTF-8 has at most as - // many relevant strings as UTF-16. - // (Thus needsStringSpanUTF8() implies needsStringSpanUTF16().) - delete stringSpan; - stringSpan = NULL; - } - } - if (stringSpan == NULL) { - // No span-relevant strings: Optimize for code point spans. - bmpSet=new BMPSet(list, len); - if (bmpSet == NULL) { // Check for memory allocation error. - setToBogus(); - } - } - } - return this; -} - -int32_t UnicodeSet::span(const UChar *s, int32_t length, USetSpanCondition spanCondition) const { - if(length>0 && bmpSet!=NULL) { - return (int32_t)(bmpSet->span(s, s+length, spanCondition)-s); - } - if(length<0) { - length=u_strlen(s); - } - if(length==0) { - return 0; - } - if(stringSpan!=NULL) { - return stringSpan->span(s, length, spanCondition); - } else if(hasStrings()) { - uint32_t which= spanCondition==USET_SPAN_NOT_CONTAINED ? - UnicodeSetStringSpan::FWD_UTF16_NOT_CONTAINED : - UnicodeSetStringSpan::FWD_UTF16_CONTAINED; - UnicodeSetStringSpan strSpan(*this, *strings, which); - if(strSpan.needsStringSpanUTF16()) { - return strSpan.span(s, length, spanCondition); - } - } - - if(spanCondition!=USET_SPAN_NOT_CONTAINED) { - spanCondition=USET_SPAN_CONTAINED; // Pin to 0/1 values. - } - - UChar32 c; - int32_t start=0, prev=0; - do { - U16_NEXT(s, start, length, c); - if(spanCondition!=contains(c)) { - break; - } - } while((prev=start)0 && bmpSet!=NULL) { - return (int32_t)(bmpSet->spanBack(s, s+length, spanCondition)-s); - } - if(length<0) { - length=u_strlen(s); - } - if(length==0) { - return 0; - } - if(stringSpan!=NULL) { - return stringSpan->spanBack(s, length, spanCondition); - } else if(hasStrings()) { - uint32_t which= spanCondition==USET_SPAN_NOT_CONTAINED ? - UnicodeSetStringSpan::BACK_UTF16_NOT_CONTAINED : - UnicodeSetStringSpan::BACK_UTF16_CONTAINED; - UnicodeSetStringSpan strSpan(*this, *strings, which); - if(strSpan.needsStringSpanUTF16()) { - return strSpan.spanBack(s, length, spanCondition); - } - } - - if(spanCondition!=USET_SPAN_NOT_CONTAINED) { - spanCondition=USET_SPAN_CONTAINED; // Pin to 0/1 values. - } - - UChar32 c; - int32_t prev=length; - do { - U16_PREV(s, 0, length, c); - if(spanCondition!=contains(c)) { - break; - } - } while((prev=length)>0); - return prev; -} - -int32_t UnicodeSet::spanUTF8(const char *s, int32_t length, USetSpanCondition spanCondition) const { - if(length>0 && bmpSet!=NULL) { - const uint8_t *s0=(const uint8_t *)s; - return (int32_t)(bmpSet->spanUTF8(s0, length, spanCondition)-s0); - } - if(length<0) { - length=(int32_t)uprv_strlen(s); - } - if(length==0) { - return 0; - } - if(stringSpan!=NULL) { - return stringSpan->spanUTF8((const uint8_t *)s, length, spanCondition); - } else if(hasStrings()) { - uint32_t which= spanCondition==USET_SPAN_NOT_CONTAINED ? - UnicodeSetStringSpan::FWD_UTF8_NOT_CONTAINED : - UnicodeSetStringSpan::FWD_UTF8_CONTAINED; - UnicodeSetStringSpan strSpan(*this, *strings, which); - if(strSpan.needsStringSpanUTF8()) { - return strSpan.spanUTF8((const uint8_t *)s, length, spanCondition); - } - } - - if(spanCondition!=USET_SPAN_NOT_CONTAINED) { - spanCondition=USET_SPAN_CONTAINED; // Pin to 0/1 values. - } - - UChar32 c; - int32_t start=0, prev=0; - do { - U8_NEXT_OR_FFFD(s, start, length, c); - if(spanCondition!=contains(c)) { - break; - } - } while((prev=start)0 && bmpSet!=NULL) { - const uint8_t *s0=(const uint8_t *)s; - return bmpSet->spanBackUTF8(s0, length, spanCondition); - } - if(length<0) { - length=(int32_t)uprv_strlen(s); - } - if(length==0) { - return 0; - } - if(stringSpan!=NULL) { - return stringSpan->spanBackUTF8((const uint8_t *)s, length, spanCondition); - } else if(hasStrings()) { - uint32_t which= spanCondition==USET_SPAN_NOT_CONTAINED ? - UnicodeSetStringSpan::BACK_UTF8_NOT_CONTAINED : - UnicodeSetStringSpan::BACK_UTF8_CONTAINED; - UnicodeSetStringSpan strSpan(*this, *strings, which); - if(strSpan.needsStringSpanUTF8()) { - return strSpan.spanBackUTF8((const uint8_t *)s, length, spanCondition); - } - } - - if(spanCondition!=USET_SPAN_NOT_CONTAINED) { - spanCondition=USET_SPAN_CONTAINED; // Pin to 0/1 values. - } - - UChar32 c; - int32_t prev=length; - do { - U8_PREV_OR_FFFD(s, 0, length, c); - if(spanCondition!=contains(c)) { - break; - } - } while((prev=length)>0); - return prev; -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2015, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 10/20/99 alan Creation. +********************************************************************** +*/ + +#include "unicode/utypes.h" +#include "unicode/parsepos.h" +#include "unicode/symtable.h" +#include "unicode/uniset.h" +#include "unicode/ustring.h" +#include "unicode/utf8.h" +#include "unicode/utf16.h" +#include "ruleiter.h" +#include "cmemory.h" +#include "cstring.h" +#include "patternprops.h" +#include "uelement.h" +#include "util.h" +#include "uvector.h" +#include "charstr.h" +#include "ustrfmt.h" +#include "uassert.h" +#include "bmpset.h" +#include "unisetspan.h" + +// HIGH_VALUE > all valid values. 110000 for codepoints +#define UNICODESET_HIGH 0x0110000 + +// LOW <= all valid values. ZERO for codepoints +#define UNICODESET_LOW 0x000000 + +/** Max list [0, 1, 2, ..., max code point, HIGH] */ +constexpr int32_t MAX_LENGTH = UNICODESET_HIGH + 1; + +U_NAMESPACE_BEGIN + +SymbolTable::~SymbolTable() {} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UnicodeSet) + +/** + * Modify the given UChar32 variable so that it is in range, by + * pinning values < UNICODESET_LOW to UNICODESET_LOW, and + * pinning values > UNICODESET_HIGH-1 to UNICODESET_HIGH-1. + * It modifies its argument in-place and also returns it. + */ +static inline UChar32 pinCodePoint(UChar32& c) { + if (c < UNICODESET_LOW) { + c = UNICODESET_LOW; + } else if (c > (UNICODESET_HIGH-1)) { + c = (UNICODESET_HIGH-1); + } + return c; +} + +//---------------------------------------------------------------- +// Debugging +//---------------------------------------------------------------- + +// DO NOT DELETE THIS CODE. This code is used to debug memory leaks. +// To enable the debugging, define the symbol DEBUG_MEM in the line +// below. This will result in text being sent to stdout that looks +// like this: +// DEBUG UnicodeSet: ct 0x00A39B20; 397 [\u0A81-\u0A83\u0A85- +// DEBUG UnicodeSet: dt 0x00A39B20; 396 [\u0A81-\u0A83\u0A85- +// Each line lists a construction (ct) or destruction (dt) event, the +// object address, the number of outstanding objects after the event, +// and the pattern of the object in question. + +// #define DEBUG_MEM + +#ifdef DEBUG_MEM +#include +static int32_t _dbgCount = 0; + +static inline void _dbgct(UnicodeSet* set) { + UnicodeString str; + set->toPattern(str, true); + char buf[40]; + str.extract(0, 39, buf, ""); + printf("DEBUG UnicodeSet: ct 0x%08X; %d %s\n", set, ++_dbgCount, buf); +} + +static inline void _dbgdt(UnicodeSet* set) { + UnicodeString str; + set->toPattern(str, true); + char buf[40]; + str.extract(0, 39, buf, ""); + printf("DEBUG UnicodeSet: dt 0x%08X; %d %s\n", set, --_dbgCount, buf); +} + +#else + +#define _dbgct(set) +#define _dbgdt(set) + +#endif + +//---------------------------------------------------------------- +// UnicodeString in UVector support +//---------------------------------------------------------------- + +static void U_CALLCONV cloneUnicodeString(UElement *dst, UElement *src) { + dst->pointer = new UnicodeString(*(UnicodeString*)src->pointer); +} + +static int32_t U_CALLCONV compareUnicodeString(UElement t1, UElement t2) { + const UnicodeString &a = *(const UnicodeString*)t1.pointer; + const UnicodeString &b = *(const UnicodeString*)t2.pointer; + return a.compare(b); +} + +UBool UnicodeSet::hasStrings() const { + return strings != nullptr && !strings->isEmpty(); +} + +int32_t UnicodeSet::stringsSize() const { + return strings == nullptr ? 0 : strings->size(); +} + +UBool UnicodeSet::stringsContains(const UnicodeString &s) const { + return strings != nullptr && strings->contains((void*) &s); +} + +//---------------------------------------------------------------- +// Constructors &c +//---------------------------------------------------------------- + +/** + * Constructs an empty set. + */ +UnicodeSet::UnicodeSet() { + list[0] = UNICODESET_HIGH; + _dbgct(this); +} + +/** + * Constructs a set containing the given range. If end > + * start then an empty set is created. + * + * @param start first character, inclusive, of range + * @param end last character, inclusive, of range + */ +UnicodeSet::UnicodeSet(UChar32 start, UChar32 end) { + list[0] = UNICODESET_HIGH; + add(start, end); + _dbgct(this); +} + +/** + * Constructs a set that is identical to the given UnicodeSet. + */ +UnicodeSet::UnicodeSet(const UnicodeSet& o) : UnicodeFilter(o) { + *this = o; + _dbgct(this); +} + +// Copy-construct as thawed. +UnicodeSet::UnicodeSet(const UnicodeSet& o, UBool /* asThawed */) : UnicodeFilter(o) { + if (ensureCapacity(o.len)) { + // *this = o except for bmpSet and stringSpan + len = o.len; + uprv_memcpy(list, o.list, (size_t)len*sizeof(UChar32)); + if (o.hasStrings()) { + UErrorCode status = U_ZERO_ERROR; + if (!allocateStrings(status) || + (strings->assign(*o.strings, cloneUnicodeString, status), U_FAILURE(status))) { + setToBogus(); + return; + } + } + if (o.pat) { + setPattern(o.pat, o.patLen); + } + _dbgct(this); + } +} + +/** + * Destructs the set. + */ +UnicodeSet::~UnicodeSet() { + _dbgdt(this); // first! + if (list != stackList) { + uprv_free(list); + } + delete bmpSet; + if (buffer != stackList) { + uprv_free(buffer); + } + delete strings; + delete stringSpan; + releasePattern(); +} + +/** + * Assigns this object to be a copy of another. + */ +UnicodeSet& UnicodeSet::operator=(const UnicodeSet& o) { + return copyFrom(o, false); +} + +UnicodeSet& UnicodeSet::copyFrom(const UnicodeSet& o, UBool asThawed) { + if (this == &o) { + return *this; + } + if (isFrozen()) { + return *this; + } + if (o.isBogus()) { + setToBogus(); + return *this; + } + if (!ensureCapacity(o.len)) { + // ensureCapacity will mark the UnicodeSet as Bogus if OOM failure happens. + return *this; + } + len = o.len; + uprv_memcpy(list, o.list, (size_t)len*sizeof(UChar32)); + if (o.bmpSet != nullptr && !asThawed) { + bmpSet = new BMPSet(*o.bmpSet, list, len); + if (bmpSet == nullptr) { // Check for memory allocation error. + setToBogus(); + return *this; + } + } + if (o.hasStrings()) { + UErrorCode status = U_ZERO_ERROR; + if ((strings == nullptr && !allocateStrings(status)) || + (strings->assign(*o.strings, cloneUnicodeString, status), U_FAILURE(status))) { + setToBogus(); + return *this; + } + } else if (hasStrings()) { + strings->removeAllElements(); + } + if (o.stringSpan != nullptr && !asThawed) { + stringSpan = new UnicodeSetStringSpan(*o.stringSpan, *strings); + if (stringSpan == nullptr) { // Check for memory allocation error. + setToBogus(); + return *this; + } + } + releasePattern(); + if (o.pat) { + setPattern(o.pat, o.patLen); + } + return *this; +} + +/** + * Returns a copy of this object. All UnicodeMatcher objects have + * to support cloning in order to allow classes using + * UnicodeMatchers, such as Transliterator, to implement cloning. + */ +UnicodeSet* UnicodeSet::clone() const { + return new UnicodeSet(*this); +} + +UnicodeSet *UnicodeSet::cloneAsThawed() const { + return new UnicodeSet(*this, true); +} + +/** + * Compares the specified object with this set for equality. Returns + * true if the two sets + * have the same size, and every member of the specified set is + * contained in this set (or equivalently, every member of this set is + * contained in the specified set). + * + * @param o set to be compared for equality with this set. + * @return true if the specified set is equal to this set. + */ +bool UnicodeSet::operator==(const UnicodeSet& o) const { + if (len != o.len) return false; + for (int32_t i = 0; i < len; ++i) { + if (list[i] != o.list[i]) return false; + } + if (hasStrings() != o.hasStrings()) { return false; } + if (hasStrings() && *strings != *o.strings) return false; + return true; +} + +/** + * Returns the hash code value for this set. + * + * @return the hash code value for this set. + * @see Object#hashCode() + */ +int32_t UnicodeSet::hashCode() const { + uint32_t result = static_cast(len); + for (int32_t i = 0; i < len; ++i) { + result *= 1000003u; + result += list[i]; + } + return static_cast(result); +} + +//---------------------------------------------------------------- +// Public API +//---------------------------------------------------------------- + +/** + * Returns the number of elements in this set (its cardinality), + * Note than the elements of a set may include both individual + * codepoints and strings. + * + * @return the number of elements in this set (its cardinality). + */ +int32_t UnicodeSet::size() const { + int32_t n = 0; + int32_t count = getRangeCount(); + for (int32_t i = 0; i < count; ++i) { + n += getRangeEnd(i) - getRangeStart(i) + 1; + } + return n + stringsSize(); +} + +/** + * Returns true if this set contains no elements. + * + * @return true if this set contains no elements. + */ +UBool UnicodeSet::isEmpty() const { + return len == 1 && !hasStrings(); +} + +/** + * Returns true if this set contains the given character. + * @param c character to be checked for containment + * @return true if the test condition is met + */ +UBool UnicodeSet::contains(UChar32 c) const { + // Set i to the index of the start item greater than ch + // We know we will terminate without length test! + // LATER: for large sets, add binary search + //int32_t i = -1; + //for (;;) { + // if (c < list[++i]) break; + //} + if (bmpSet != nullptr) { + return bmpSet->contains(c); + } + if (stringSpan != nullptr) { + return stringSpan->contains(c); + } + if (c >= UNICODESET_HIGH) { // Don't need to check LOW bound + return false; + } + int32_t i = findCodePoint(c); + return (UBool)(i & 1); // return true if odd +} + +/** + * Returns the smallest value i such that c < list[i]. Caller + * must ensure that c is a legal value or this method will enter + * an infinite loop. This method performs a binary search. + * @param c a character in the range MIN_VALUE..MAX_VALUE + * inclusive + * @return the smallest integer i in the range 0..len-1, + * inclusive, such that c < list[i] + */ +int32_t UnicodeSet::findCodePoint(UChar32 c) const { + /* Examples: + findCodePoint(c) + set list[] c=0 1 3 4 7 8 + === ============== =========== + [] [110000] 0 0 0 0 0 0 + [\u0000-\u0003] [0, 4, 110000] 1 1 1 2 2 2 + [\u0004-\u0007] [4, 8, 110000] 0 0 0 1 1 2 + [:Any:] [0, 110000] 1 1 1 1 1 1 + */ + + // Return the smallest i such that c < list[i]. Assume + // list[len - 1] == HIGH and that c is legal (0..HIGH-1). + if (c < list[0]) + return 0; + // High runner test. c is often after the last range, so an + // initial check for this condition pays off. + int32_t lo = 0; + int32_t hi = len - 1; + if (lo >= hi || c >= list[hi-1]) + return hi; + // invariant: c >= list[lo] + // invariant: c < list[hi] + for (;;) { + int32_t i = (lo + hi) >> 1; + if (i == lo) { + break; // Found! + } else if (c < list[i]) { + hi = i; + } else { + lo = i; + } + } + return hi; +} + +/** + * Returns true if this set contains every character + * of the given range. + * @param start first character, inclusive, of the range + * @param end last character, inclusive, of the range + * @return true if the test condition is met + */ +UBool UnicodeSet::contains(UChar32 start, UChar32 end) const { + //int32_t i = -1; + //for (;;) { + // if (start < list[++i]) break; + //} + int32_t i = findCodePoint(start); + return ((i & 1) != 0 && end < list[i]); +} + +/** + * Returns true if this set contains the given + * multicharacter string. + * @param s string to be checked for containment + * @return true if this set contains the specified string + */ +UBool UnicodeSet::contains(const UnicodeString& s) const { + int32_t cp = getSingleCP(s); + if (cp < 0) { + return stringsContains(s); + } else { + return contains((UChar32) cp); + } +} + +/** + * Returns true if this set contains all the characters and strings + * of the given set. + * @param c set to be checked for containment + * @return true if the test condition is met + */ +UBool UnicodeSet::containsAll(const UnicodeSet& c) const { + // The specified set is a subset if all of its pairs are contained in + // this set. It's possible to code this more efficiently in terms of + // direct manipulation of the inversion lists if the need arises. + int32_t n = c.getRangeCount(); + for (int i=0; icontainsAll(*c.strings)); +} + +/** + * Returns true if this set contains all the characters + * of the given string. + * @param s string containing characters to be checked for containment + * @return true if the test condition is met + */ +UBool UnicodeSet::containsAll(const UnicodeString& s) const { + return (UBool)(span(s.getBuffer(), s.length(), USET_SPAN_CONTAINED) == + s.length()); +} + +/** + * Returns true if this set contains none of the characters + * of the given range. + * @param start first character, inclusive, of the range + * @param end last character, inclusive, of the range + * @return true if the test condition is met + */ +UBool UnicodeSet::containsNone(UChar32 start, UChar32 end) const { + //int32_t i = -1; + //for (;;) { + // if (start < list[++i]) break; + //} + int32_t i = findCodePoint(start); + return ((i & 1) == 0 && end < list[i]); +} + +/** + * Returns true if this set contains none of the characters and strings + * of the given set. + * @param c set to be checked for containment + * @return true if the test condition is met + */ +UBool UnicodeSet::containsNone(const UnicodeSet& c) const { + // The specified set is a subset if all of its pairs are contained in + // this set. It's possible to code this more efficiently in terms of + // direct manipulation of the inversion lists if the need arises. + int32_t n = c.getRangeCount(); + for (int32_t i=0; icontainsNone(*c.strings); +} + +/** + * Returns true if this set contains none of the characters + * of the given string. + * @param s string containing characters to be checked for containment + * @return true if the test condition is met + */ +UBool UnicodeSet::containsNone(const UnicodeString& s) const { + return (UBool)(span(s.getBuffer(), s.length(), USET_SPAN_NOT_CONTAINED) == + s.length()); +} + +/** + * Returns true if this set contains any character whose low byte + * is the given value. This is used by RuleBasedTransliterator for + * indexing. + */ +UBool UnicodeSet::matchesIndexValue(uint8_t v) const { + /* The index value v, in the range [0,255], is contained in this set if + * it is contained in any pair of this set. Pairs either have the high + * bytes equal, or unequal. If the high bytes are equal, then we have + * aaxx..aayy, where aa is the high byte. Then v is contained if xx <= + * v <= yy. If the high bytes are unequal we have aaxx..bbyy, bb>aa. + * Then v is contained if xx <= v || v <= yy. (This is identical to the + * time zone month containment logic.) + */ + int32_t i; + int32_t rangeCount=getRangeCount(); + for (i=0; isize(); ++i) { + const UnicodeString& s = *(const UnicodeString*)strings->elementAt(i); + if (s.isEmpty()) { + continue; // skip the empty string + } + UChar32 c = s.char32At(0); + if ((c & 0xFF) == v) { + return true; + } + } + } + return false; +} + +/** + * Implementation of UnicodeMatcher::matches(). Always matches the + * longest possible multichar string. + */ +UMatchDegree UnicodeSet::matches(const Replaceable& text, + int32_t& offset, + int32_t limit, + UBool incremental) { + if (offset == limit) { + if (contains(U_ETHER)) { + return incremental ? U_PARTIAL_MATCH : U_MATCH; + } else { + return U_MISMATCH; + } + } else { + if (hasStrings()) { // try strings first + + // might separate forward and backward loops later + // for now they are combined + + // TODO Improve efficiency of this, at least in the forward + // direction, if not in both. In the forward direction we + // can assume the strings are sorted. + + int32_t i; + UBool forward = offset < limit; + + // firstChar is the leftmost char to match in the + // forward direction or the rightmost char to match in + // the reverse direction. + char16_t firstChar = text.charAt(offset); + + // If there are multiple strings that can match we + // return the longest match. + int32_t highWaterLength = 0; + + for (i=0; isize(); ++i) { + const UnicodeString& trial = *(const UnicodeString*)strings->elementAt(i); + if (trial.isEmpty()) { + continue; // skip the empty string + } + + char16_t c = trial.charAt(forward ? 0 : trial.length() - 1); + + // Strings are sorted, so we can optimize in the + // forward direction. + if (forward && c > firstChar) break; + if (c != firstChar) continue; + + int32_t matchLen = matchRest(text, offset, limit, trial); + + if (incremental) { + int32_t maxLen = forward ? limit-offset : offset-limit; + if (matchLen == maxLen) { + // We have successfully matched but only up to limit. + return U_PARTIAL_MATCH; + } + } + + if (matchLen == trial.length()) { + // We have successfully matched the whole string. + if (matchLen > highWaterLength) { + highWaterLength = matchLen; + } + // In the forward direction we know strings + // are sorted so we can bail early. + if (forward && matchLen < highWaterLength) { + break; + } + continue; + } + } + + // We've checked all strings without a partial match. + // If we have full matches, return the longest one. + if (highWaterLength != 0) { + offset += forward ? highWaterLength : -highWaterLength; + return U_MATCH; + } + } + return UnicodeFilter::matches(text, offset, limit, incremental); + } +} + +/** + * Returns the longest match for s in text at the given position. + * If limit > start then match forward from start+1 to limit + * matching all characters except s.charAt(0). If limit < start, + * go backward starting from start-1 matching all characters + * except s.charAt(s.length()-1). This method assumes that the + * first character, text.charAt(start), matches s, so it does not + * check it. + * @param text the text to match + * @param start the first character to match. In the forward + * direction, text.charAt(start) is matched against s.charAt(0). + * In the reverse direction, it is matched against + * s.charAt(s.length()-1). + * @param limit the limit offset for matching, either last+1 in + * the forward direction, or last-1 in the reverse direction, + * where last is the index of the last character to match. + * @return If part of s matches up to the limit, return |limit - + * start|. If all of s matches before reaching the limit, return + * s.length(). If there is a mismatch between s and text, return + * 0 + */ +int32_t UnicodeSet::matchRest(const Replaceable& text, + int32_t start, int32_t limit, + const UnicodeString& s) { + int32_t i; + int32_t maxLen; + int32_t slen = s.length(); + if (start < limit) { + maxLen = limit - start; + if (maxLen > slen) maxLen = slen; + for (i = 1; i < maxLen; ++i) { + if (text.charAt(start + i) != s.charAt(i)) return 0; + } + } else { + maxLen = start - limit; + if (maxLen > slen) maxLen = slen; + --slen; // <=> slen = s.length() - 1; + for (i = 1; i < maxLen; ++i) { + if (text.charAt(start - i) != s.charAt(slen - i)) return 0; + } + } + return maxLen; +} + +/** + * Implement of UnicodeMatcher + */ +void UnicodeSet::addMatchSetTo(UnicodeSet& toUnionTo) const { + toUnionTo.addAll(*this); +} + +/** + * Returns the index of the given character within this set, where + * the set is ordered by ascending code point. If the character + * is not in this set, return -1. The inverse of this method is + * charAt(). + * @return an index from 0..size()-1, or -1 + */ +int32_t UnicodeSet::indexOf(UChar32 c) const { + if (c < MIN_VALUE || c > MAX_VALUE) { + return -1; + } + int32_t i = 0; + int32_t n = 0; + for (;;) { + UChar32 start = list[i++]; + if (c < start) { + return -1; + } + UChar32 limit = list[i++]; + if (c < limit) { + return n + c - start; + } + n += limit - start; + } +} + +/** + * Returns the character at the given index within this set, where + * the set is ordered by ascending code point. If the index is + * out of range, return (UChar32)-1. The inverse of this method is + * indexOf(). + * @param index an index from 0..size()-1 + * @return the character at the given index, or (UChar32)-1. + */ +UChar32 UnicodeSet::charAt(int32_t index) const { + if (index >= 0) { + // len2 is the largest even integer <= len, that is, it is len + // for even values and len-1 for odd values. With odd values + // the last entry is UNICODESET_HIGH. + int32_t len2 = len & ~1; + for (int32_t i=0; i < len2;) { + UChar32 start = list[i++]; + int32_t count = list[i++] - start; + if (index < count) { + return (UChar32)(start + index); + } + index -= count; + } + } + return (UChar32)-1; +} + +/** + * Make this object represent the range start - end. + * If end > start then this object is set to an + * an empty range. + * + * @param start first character in the set, inclusive + * @rparam end last character in the set, inclusive + */ +UnicodeSet& UnicodeSet::set(UChar32 start, UChar32 end) { + clear(); + complement(start, end); + return *this; +} + +/** + * Adds the specified range to this set if it is not already + * present. If this set already contains the specified range, + * the call leaves this set unchanged. If end > start + * then an empty range is added, leaving the set unchanged. + * + * @param start first character, inclusive, of range to be added + * to this set. + * @param end last character, inclusive, of range to be added + * to this set. + */ +UnicodeSet& UnicodeSet::add(UChar32 start, UChar32 end) { + if (pinCodePoint(start) < pinCodePoint(end)) { + UChar32 limit = end + 1; + // Fast path for adding a new range after the last one. + // Odd list length: [..., lastStart, lastLimit, HIGH] + if ((len & 1) != 0) { + // If the list is empty, set lastLimit low enough to not be adjacent to 0. + UChar32 lastLimit = len == 1 ? -2 : list[len - 2]; + if (lastLimit <= start && !isFrozen() && !isBogus()) { + if (lastLimit == start) { + // Extend the last range. + list[len - 2] = limit; + if (limit == UNICODESET_HIGH) { + --len; + } + } else { + list[len - 1] = start; + if (limit < UNICODESET_HIGH) { + if (ensureCapacity(len + 2)) { + list[len++] = limit; + list[len++] = UNICODESET_HIGH; + } + } else { // limit == UNICODESET_HIGH + if (ensureCapacity(len + 1)) { + list[len++] = UNICODESET_HIGH; + } + } + } + releasePattern(); + return *this; + } + } + // This is slow. Could be much faster using findCodePoint(start) + // and modifying the list, dealing with adjacent & overlapping ranges. + UChar32 range[3] = { start, limit, UNICODESET_HIGH }; + add(range, 2, 0); + } else if (start == end) { + add(start); + } + return *this; +} + +// #define DEBUG_US_ADD + +#ifdef DEBUG_US_ADD +#include +void dump(UChar32 c) { + if (c <= 0xFF) { + printf("%c", (char)c); + } else { + printf("U+%04X", c); + } +} +void dump(const UChar32* list, int32_t len) { + printf("["); + for (int32_t i=0; i "); +#endif + + if (c == list[i]-1) { + // c is before start of next range + list[i] = c; + // if we touched the HIGH mark, then add a new one + if (c == (UNICODESET_HIGH - 1)) { + if (!ensureCapacity(len+1)) { + // ensureCapacity will mark the object as Bogus if OOM failure happens. + return *this; + } + list[len++] = UNICODESET_HIGH; + } + if (i > 0 && c == list[i-1]) { + // collapse adjacent ranges + + // [..., start_k-1, c, c, limit_k, ..., HIGH] + // ^ + // list[i] + + //for (int32_t k=i-1; k 0 && c == list[i-1]) { + // c is after end of prior range + list[i-1]++; + // no need to check for collapse here + } + + else { + // At this point we know the new char is not adjacent to + // any existing ranges, and it is not 10FFFF. + + + // [..., start_k-1, limit_k-1, start_k, limit_k, ..., HIGH] + // ^ + // list[i] + + // [..., start_k-1, limit_k-1, c, c+1, start_k, limit_k, ..., HIGH] + // ^ + // list[i] + + if (!ensureCapacity(len+2)) { + // ensureCapacity will mark the object as Bogus if OOM failure happens. + return *this; + } + + UChar32 *p = list + i; + uprv_memmove(p + 2, p, (len - i) * sizeof(*p)); + list[i] = c; + list[i+1] = c+1; + len += 2; + } + +#ifdef DEBUG_US_ADD + dump(list, len); + printf("\n"); + + for (i=1; i {"ch"} + * + * @param s the source string + * @return the modified set, for chaining + */ +UnicodeSet& UnicodeSet::add(const UnicodeString& s) { + if (isFrozen() || isBogus()) return *this; + int32_t cp = getSingleCP(s); + if (cp < 0) { + if (!stringsContains(s)) { + _add(s); + releasePattern(); + } + } else { + add((UChar32)cp); + } + return *this; +} + +/** + * Adds the given string, in order, to 'strings'. The given string + * must have been checked by the caller to not already be in 'strings'. + */ +void UnicodeSet::_add(const UnicodeString& s) { + if (isFrozen() || isBogus()) { + return; + } + UErrorCode ec = U_ZERO_ERROR; + if (strings == nullptr && !allocateStrings(ec)) { + setToBogus(); + return; + } + UnicodeString* t = new UnicodeString(s); + if (t == nullptr) { // Check for memory allocation error. + setToBogus(); + return; + } + strings->sortedInsert(t, compareUnicodeString, ec); + if (U_FAILURE(ec)) { + setToBogus(); + } +} + +/** + * @return a code point IF the string consists of a single one. + * otherwise returns -1. + * @param string to test + */ +int32_t UnicodeSet::getSingleCP(const UnicodeString& s) { + int32_t sLength = s.length(); + if (sLength == 1) return s.charAt(0); + if (sLength == 2) { + UChar32 cp = s.char32At(0); + if (cp > 0xFFFF) { // is surrogate pair + return cp; + } + } + return -1; +} + +/** + * Adds each of the characters in this string to the set. Thus "ch" => {"c", "h"} + * If this set already any particular character, it has no effect on that character. + * @param the source string + * @return the modified set, for chaining + */ +UnicodeSet& UnicodeSet::addAll(const UnicodeString& s) { + UChar32 cp; + for (int32_t i = 0; i < s.length(); i += U16_LENGTH(cp)) { + cp = s.char32At(i); + add(cp); + } + return *this; +} + +/** + * Retains EACH of the characters in this string. Note: "ch" == {"c", "h"} + * If this set already any particular character, it has no effect on that character. + * @param the source string + * @return the modified set, for chaining + */ +UnicodeSet& UnicodeSet::retainAll(const UnicodeString& s) { + UnicodeSet set; + set.addAll(s); + retainAll(set); + return *this; +} + +/** + * Complement EACH of the characters in this string. Note: "ch" == {"c", "h"} + * If this set already any particular character, it has no effect on that character. + * @param the source string + * @return the modified set, for chaining + */ +UnicodeSet& UnicodeSet::complementAll(const UnicodeString& s) { + UnicodeSet set; + set.addAll(s); + complementAll(set); + return *this; +} + +/** + * Remove EACH of the characters in this string. Note: "ch" == {"c", "h"} + * If this set already any particular character, it has no effect on that character. + * @param the source string + * @return the modified set, for chaining + */ +UnicodeSet& UnicodeSet::removeAll(const UnicodeString& s) { + UnicodeSet set; + set.addAll(s); + removeAll(set); + return *this; +} + +UnicodeSet& UnicodeSet::removeAllStrings() { + if (!isFrozen() && hasStrings()) { + strings->removeAllElements(); + releasePattern(); + } + return *this; +} + + +/** + * Makes a set from a multicharacter string. Thus "ch" => {"ch"} + *
Warning: you cannot add an empty string ("") to a UnicodeSet. + * @param the source string + * @return a newly created set containing the given string + */ +UnicodeSet* U_EXPORT2 UnicodeSet::createFrom(const UnicodeString& s) { + UnicodeSet *set = new UnicodeSet(); + if (set != nullptr) { // Check for memory allocation error. + set->add(s); + } + return set; +} + + +/** + * Makes a set from each of the characters in the string. Thus "ch" => {"c", "h"} + * @param the source string + * @return a newly created set containing the given characters + */ +UnicodeSet* U_EXPORT2 UnicodeSet::createFromAll(const UnicodeString& s) { + UnicodeSet *set = new UnicodeSet(); + if (set != nullptr) { // Check for memory allocation error. + set->addAll(s); + } + return set; +} + +/** + * Retain only the elements in this set that are contained in the + * specified range. If end > start then an empty range is + * retained, leaving the set empty. + * + * @param start first character, inclusive, of range to be retained + * to this set. + * @param end last character, inclusive, of range to be retained + * to this set. + */ +UnicodeSet& UnicodeSet::retain(UChar32 start, UChar32 end) { + if (pinCodePoint(start) <= pinCodePoint(end)) { + UChar32 range[3] = { start, end+1, UNICODESET_HIGH }; + retain(range, 2, 0); + } else { + clear(); + } + return *this; +} + +UnicodeSet& UnicodeSet::retain(UChar32 c) { + return retain(c, c); +} + +UnicodeSet& UnicodeSet::retain(const UnicodeString &s) { + if (isFrozen() || isBogus()) { return *this; } + UChar32 cp = getSingleCP(s); + if (cp < 0) { + bool isIn = stringsContains(s); + // Check for getRangeCount() first to avoid somewhat-expensive size() + // when there are single code points. + if (isIn && getRangeCount() == 0 && size() == 1) { + return *this; + } + clear(); + if (isIn) { + _add(s); + } + } else { + retain(cp, cp); + } + return *this; +} + +/** + * Removes the specified range from this set if it is present. + * The set will not contain the specified range once the call + * returns. If end > start then an empty range is + * removed, leaving the set unchanged. + * + * @param start first character, inclusive, of range to be removed + * from this set. + * @param end last character, inclusive, of range to be removed + * from this set. + */ +UnicodeSet& UnicodeSet::remove(UChar32 start, UChar32 end) { + if (pinCodePoint(start) <= pinCodePoint(end)) { + UChar32 range[3] = { start, end+1, UNICODESET_HIGH }; + retain(range, 2, 2); + } + return *this; +} + +/** + * Removes the specified character from this set if it is present. + * The set will not contain the specified range once the call + * returns. + */ +UnicodeSet& UnicodeSet::remove(UChar32 c) { + return remove(c, c); +} + +/** + * Removes the specified string from this set if it is present. + * The set will not contain the specified character once the call + * returns. + * @param the source string + * @return the modified set, for chaining + */ +UnicodeSet& UnicodeSet::remove(const UnicodeString& s) { + if (isFrozen() || isBogus()) return *this; + int32_t cp = getSingleCP(s); + if (cp < 0) { + if (strings != nullptr && strings->removeElement((void*) &s)) { + releasePattern(); + } + } else { + remove((UChar32)cp, (UChar32)cp); + } + return *this; +} + +/** + * Complements the specified range in this set. Any character in + * the range will be removed if it is in this set, or will be + * added if it is not in this set. If end > start + * then an empty range is xor'ed, leaving the set unchanged. + * + * @param start first character, inclusive, of range to be removed + * from this set. + * @param end last character, inclusive, of range to be removed + * from this set. + */ +UnicodeSet& UnicodeSet::complement(UChar32 start, UChar32 end) { + if (isFrozen() || isBogus()) { + return *this; + } + if (pinCodePoint(start) <= pinCodePoint(end)) { + UChar32 range[3] = { start, end+1, UNICODESET_HIGH }; + exclusiveOr(range, 2, 0); + } + releasePattern(); + return *this; +} + +UnicodeSet& UnicodeSet::complement(UChar32 c) { + return complement(c, c); +} + +/** + * This is equivalent to + * complement(MIN_VALUE, MAX_VALUE). + */ +UnicodeSet& UnicodeSet::complement() { + if (isFrozen() || isBogus()) { + return *this; + } + if (list[0] == UNICODESET_LOW) { + uprv_memmove(list, list + 1, (size_t)(len-1)*sizeof(UChar32)); + --len; + } else { + if (!ensureCapacity(len+1)) { + return *this; + } + uprv_memmove(list + 1, list, (size_t)len*sizeof(UChar32)); + list[0] = UNICODESET_LOW; + ++len; + } + releasePattern(); + return *this; +} + +/** + * Complement the specified string in this set. + * The set will not contain the specified string once the call + * returns. + * + * @param s the string to complement + * @return this object, for chaining + */ +UnicodeSet& UnicodeSet::complement(const UnicodeString& s) { + if (isFrozen() || isBogus()) return *this; + int32_t cp = getSingleCP(s); + if (cp < 0) { + if (stringsContains(s)) { + strings->removeElement((void*) &s); + } else { + _add(s); + } + releasePattern(); + } else { + complement((UChar32)cp, (UChar32)cp); + } + return *this; +} + +/** + * Adds all of the elements in the specified set to this set if + * they're not already present. This operation effectively + * modifies this set so that its value is the union of the two + * sets. The behavior of this operation is unspecified if the specified + * collection is modified while the operation is in progress. + * + * @param c set whose elements are to be added to this set. + * @see #add(char, char) + */ +UnicodeSet& UnicodeSet::addAll(const UnicodeSet& c) { + if ( c.len>0 && c.list!=nullptr ) { + add(c.list, c.len, 0); + } + + // Add strings in order + if ( c.strings!=nullptr ) { + for (int32_t i=0; isize(); ++i) { + const UnicodeString* s = (const UnicodeString*)c.strings->elementAt(i); + if (!stringsContains(*s)) { + _add(*s); + } + } + } + return *this; +} + +/** + * Retains only the elements in this set that are contained in the + * specified set. In other words, removes from this set all of + * its elements that are not contained in the specified set. This + * operation effectively modifies this set so that its value is + * the intersection of the two sets. + * + * @param c set that defines which elements this set will retain. + */ +UnicodeSet& UnicodeSet::retainAll(const UnicodeSet& c) { + if (isFrozen() || isBogus()) { + return *this; + } + retain(c.list, c.len, 0); + if (hasStrings()) { + if (!c.hasStrings()) { + strings->removeAllElements(); + } else { + strings->retainAll(*c.strings); + } + } + return *this; +} + +/** + * Removes from this set all of its elements that are contained in the + * specified set. This operation effectively modifies this + * set so that its value is the asymmetric set difference of + * the two sets. + * + * @param c set that defines which elements will be removed from + * this set. + */ +UnicodeSet& UnicodeSet::removeAll(const UnicodeSet& c) { + if (isFrozen() || isBogus()) { + return *this; + } + retain(c.list, c.len, 2); + if (hasStrings() && c.hasStrings()) { + strings->removeAll(*c.strings); + } + return *this; +} + +/** + * Complements in this set all elements contained in the specified + * set. Any character in the other set will be removed if it is + * in this set, or will be added if it is not in this set. + * + * @param c set that defines which elements will be xor'ed from + * this set. + */ +UnicodeSet& UnicodeSet::complementAll(const UnicodeSet& c) { + if (isFrozen() || isBogus()) { + return *this; + } + exclusiveOr(c.list, c.len, 0); + + if (c.strings != nullptr) { + for (int32_t i=0; isize(); ++i) { + void* e = c.strings->elementAt(i); + if (strings == nullptr || !strings->removeElement(e)) { + _add(*(const UnicodeString*)e); + } + } + } + return *this; +} + +/** + * Removes all of the elements from this set. This set will be + * empty after this call returns. + */ +UnicodeSet& UnicodeSet::clear() { + if (isFrozen()) { + return *this; + } + list[0] = UNICODESET_HIGH; + len = 1; + releasePattern(); + if (strings != nullptr) { + strings->removeAllElements(); + } + // Remove bogus + fFlags = 0; + return *this; +} + +/** + * Iteration method that returns the number of ranges contained in + * this set. + * @see #getRangeStart + * @see #getRangeEnd + */ +int32_t UnicodeSet::getRangeCount() const { + return len/2; +} + +/** + * Iteration method that returns the first character in the + * specified range of this set. + * @see #getRangeCount + * @see #getRangeEnd + */ +UChar32 UnicodeSet::getRangeStart(int32_t index) const { + return list[index*2]; +} + +/** + * Iteration method that returns the last character in the + * specified range of this set. + * @see #getRangeStart + * @see #getRangeEnd + */ +UChar32 UnicodeSet::getRangeEnd(int32_t index) const { + return list[index*2 + 1] - 1; +} + +const UnicodeString* UnicodeSet::getString(int32_t index) const { + return (const UnicodeString*) strings->elementAt(index); +} + +/** + * Reallocate this objects internal structures to take up the least + * possible space, without changing this object's value. + */ +UnicodeSet& UnicodeSet::compact() { + if (isFrozen() || isBogus()) { + return *this; + } + // Delete buffer first to defragment memory less. + if (buffer != stackList) { + uprv_free(buffer); + buffer = nullptr; + bufferCapacity = 0; + } + if (list == stackList) { + // pass + } else if (len <= INITIAL_CAPACITY) { + uprv_memcpy(stackList, list, len * sizeof(UChar32)); + uprv_free(list); + list = stackList; + capacity = INITIAL_CAPACITY; + } else if ((len + 7) < capacity) { + // If we have more than a little unused capacity, shrink it to len. + UChar32* temp = (UChar32*) uprv_realloc(list, sizeof(UChar32) * len); + if (temp) { + list = temp; + capacity = len; + } + // else what the heck happened?! We allocated less memory! + // Oh well. We'll keep our original array. + } + if (strings != nullptr && strings->isEmpty()) { + delete strings; + strings = nullptr; + } + return *this; +} + +#ifdef DEBUG_SERIALIZE +#include +#endif + +/** + * Deserialize constructor. + */ +UnicodeSet::UnicodeSet(const uint16_t data[], int32_t dataLen, ESerialization serialization, + UErrorCode &ec) { + + if(U_FAILURE(ec)) { + setToBogus(); + return; + } + + if( (serialization != kSerialized) + || (data==nullptr) + || (dataLen < 1)) { + ec = U_ILLEGAL_ARGUMENT_ERROR; + setToBogus(); + return; + } + + // bmp? + int32_t headerSize = ((data[0]&0x8000)) ?2:1; + int32_t bmpLength = (headerSize==1)?data[0]:data[1]; + + int32_t newLength = (((data[0]&0x7FFF)-bmpLength)/2)+bmpLength; +#ifdef DEBUG_SERIALIZE + printf("dataLen %d headerSize %d bmpLen %d len %d. data[0]=%X/%X/%X/%X\n", dataLen,headerSize,bmpLength,newLength, data[0],data[1],data[2],data[3]); +#endif + if(!ensureCapacity(newLength + 1)) { // +1 for HIGH + return; + } + // copy bmp + int32_t i; + for(i = 0; i< bmpLength;i++) { + list[i] = data[i+headerSize]; +#ifdef DEBUG_SERIALIZE + printf("<<16@%d[%d] %X\n", i+headerSize, i, list[i]); +#endif + } + // copy smp + for(i=bmpLength;i0 && dest==nullptr)) { + ec=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* count necessary 16-bit units */ + length=this->len-1; // Subtract 1 to ignore final UNICODESET_HIGH + // assert(length>=0); + if (length==0) { + /* empty set */ + if (destCapacity>0) { + *dest=0; + } else { + ec=U_BUFFER_OVERFLOW_ERROR; + } + return 1; + } + /* now length>0 */ + + if (this->list[length-1]<=0xffff) { + /* all BMP */ + bmpLength=length; + } else if (this->list[0]>=0x10000) { + /* all supplementary */ + bmpLength=0; + length*=2; + } else { + /* some BMP, some supplementary */ + for (bmpLength=0; bmpLengthlist[bmpLength]<=0xffff; ++bmpLength) {} + length=bmpLength+2*(length-bmpLength); + } +#ifdef DEBUG_SERIALIZE + printf(">> bmpLength%d length%d len%d\n", bmpLength, length, len); +#endif + /* length: number of 16-bit array units */ + if (length>0x7fff) { + /* there are only 15 bits for the length in the first serialized word */ + ec=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + /* + * total serialized length: + * number of 16-bit array units (length) + + * 1 length unit (always) + + * 1 bmpLength unit (if there are supplementary values) + */ + destLength=length+((length>bmpLength)?2:1); + if (destLength<=destCapacity) { + const UChar32 *p; + int32_t i; + +#ifdef DEBUG_SERIALIZE + printf("writeHdr\n"); +#endif + *dest=(uint16_t)length; + if (length>bmpLength) { + *dest|=0x8000; + *++dest=(uint16_t)bmpLength; + } + ++dest; + + /* write the BMP part of the array */ + p=this->list; + for (i=0; i>16); + *dest++=(uint16_t)*p++; + } + } else { + ec=U_BUFFER_OVERFLOW_ERROR; + } + return destLength; +} + +//---------------------------------------------------------------- +// Implementation: Utility methods +//---------------------------------------------------------------- + +/** + * Allocate our strings vector and return true if successful. + */ +UBool UnicodeSet::allocateStrings(UErrorCode &status) { + if (U_FAILURE(status)) { + return false; + } + strings = new UVector(uprv_deleteUObject, + uhash_compareUnicodeString, 1, status); + if (strings == nullptr) { // Check for memory allocation error. + status = U_MEMORY_ALLOCATION_ERROR; + return false; + } + if (U_FAILURE(status)) { + delete strings; + strings = nullptr; + return false; + } + return true; +} + +int32_t UnicodeSet::nextCapacity(int32_t minCapacity) { + // Grow exponentially to reduce the frequency of allocations. + if (minCapacity < INITIAL_CAPACITY) { + return minCapacity + INITIAL_CAPACITY; + } else if (minCapacity <= 2500) { + return 5 * minCapacity; + } else { + int32_t newCapacity = 2 * minCapacity; + if (newCapacity > MAX_LENGTH) { + newCapacity = MAX_LENGTH; + } + return newCapacity; + } +} + +bool UnicodeSet::ensureCapacity(int32_t newLen) { + if (newLen > MAX_LENGTH) { + newLen = MAX_LENGTH; + } + if (newLen <= capacity) { + return true; + } + int32_t newCapacity = nextCapacity(newLen); + UChar32* temp = (UChar32*) uprv_malloc(newCapacity * sizeof(UChar32)); + if (temp == nullptr) { + setToBogus(); // set the object to bogus state if an OOM failure occurred. + return false; + } + // Copy only the actual contents. + uprv_memcpy(temp, list, len * sizeof(UChar32)); + if (list != stackList) { + uprv_free(list); + } + list = temp; + capacity = newCapacity; + return true; +} + +bool UnicodeSet::ensureBufferCapacity(int32_t newLen) { + if (newLen > MAX_LENGTH) { + newLen = MAX_LENGTH; + } + if (newLen <= bufferCapacity) { + return true; + } + int32_t newCapacity = nextCapacity(newLen); + UChar32* temp = (UChar32*) uprv_malloc(newCapacity * sizeof(UChar32)); + if (temp == nullptr) { + setToBogus(); + return false; + } + // The buffer has no contents to be copied. + // It is always filled from scratch after this call. + if (buffer != stackList) { + uprv_free(buffer); + } + buffer = temp; + bufferCapacity = newCapacity; + return true; +} + +/** + * Swap list and buffer. + */ +void UnicodeSet::swapBuffers() { + // swap list and buffer + UChar32* temp = list; + list = buffer; + buffer = temp; + + int32_t c = capacity; + capacity = bufferCapacity; + bufferCapacity = c; +} + +void UnicodeSet::setToBogus() { + clear(); // Remove everything in the set. + fFlags = kIsBogus; +} + +//---------------------------------------------------------------- +// Implementation: Fundamental operators +//---------------------------------------------------------------- + +static inline UChar32 max(UChar32 a, UChar32 b) { + return (a > b) ? a : b; +} + +// polarity = 0, 3 is normal: x xor y +// polarity = 1, 2: x xor ~y == x === y + +void UnicodeSet::exclusiveOr(const UChar32* other, int32_t otherLen, int8_t polarity) { + if (isFrozen() || isBogus()) { + return; + } + if (!ensureBufferCapacity(len + otherLen)) { + return; + } + + int32_t i = 0, j = 0, k = 0; + UChar32 a = list[i++]; + UChar32 b; + if (polarity == 1 || polarity == 2) { + b = UNICODESET_LOW; + if (other[j] == UNICODESET_LOW) { // skip base if already LOW + ++j; + b = other[j]; + } + } else { + b = other[j++]; + } + // simplest of all the routines + // sort the values, discarding identicals! + for (;;) { + if (a < b) { + buffer[k++] = a; + a = list[i++]; + } else if (b < a) { + buffer[k++] = b; + b = other[j++]; + } else if (a != UNICODESET_HIGH) { // at this point, a == b + // discard both values! + a = list[i++]; + b = other[j++]; + } else { // DONE! + buffer[k++] = UNICODESET_HIGH; + len = k; + break; + } + } + swapBuffers(); + releasePattern(); +} + +// polarity = 0 is normal: x union y +// polarity = 2: x union ~y +// polarity = 1: ~x union y +// polarity = 3: ~x union ~y + +void UnicodeSet::add(const UChar32* other, int32_t otherLen, int8_t polarity) { + if (isFrozen() || isBogus() || other==nullptr) { + return; + } + if (!ensureBufferCapacity(len + otherLen)) { + return; + } + + int32_t i = 0, j = 0, k = 0; + UChar32 a = list[i++]; + UChar32 b = other[j++]; + // change from xor is that we have to check overlapping pairs + // polarity bit 1 means a is second, bit 2 means b is. + for (;;) { + switch (polarity) { + case 0: // both first; take lower if unequal + if (a < b) { // take a + // Back up over overlapping ranges in buffer[] + if (k > 0 && a <= buffer[k-1]) { + // Pick latter end value in buffer[] vs. list[] + a = max(list[i], buffer[--k]); + } else { + // No overlap + buffer[k++] = a; + a = list[i]; + } + i++; // Common if/else code factored out + polarity ^= 1; + } else if (b < a) { // take b + if (k > 0 && b <= buffer[k-1]) { + b = max(other[j], buffer[--k]); + } else { + buffer[k++] = b; + b = other[j]; + } + j++; + polarity ^= 2; + } else { // a == b, take a, drop b + if (a == UNICODESET_HIGH) goto loop_end; + // This is symmetrical; it doesn't matter if + // we backtrack with a or b. - liu + if (k > 0 && a <= buffer[k-1]) { + a = max(list[i], buffer[--k]); + } else { + // No overlap + buffer[k++] = a; + a = list[i]; + } + i++; + polarity ^= 1; + b = other[j++]; + polarity ^= 2; + } + break; + case 3: // both second; take higher if unequal, and drop other + if (b <= a) { // take a + if (a == UNICODESET_HIGH) goto loop_end; + buffer[k++] = a; + } else { // take b + if (b == UNICODESET_HIGH) goto loop_end; + buffer[k++] = b; + } + a = list[i++]; + polarity ^= 1; // factored common code + b = other[j++]; + polarity ^= 2; + break; + case 1: // a second, b first; if b < a, overlap + if (a < b) { // no overlap, take a + buffer[k++] = a; a = list[i++]; polarity ^= 1; + } else if (b < a) { // OVERLAP, drop b + b = other[j++]; + polarity ^= 2; + } else { // a == b, drop both! + if (a == UNICODESET_HIGH) goto loop_end; + a = list[i++]; + polarity ^= 1; + b = other[j++]; + polarity ^= 2; + } + break; + case 2: // a first, b second; if a < b, overlap + if (b < a) { // no overlap, take b + buffer[k++] = b; + b = other[j++]; + polarity ^= 2; + } else if (a < b) { // OVERLAP, drop a + a = list[i++]; + polarity ^= 1; + } else { // a == b, drop both! + if (a == UNICODESET_HIGH) goto loop_end; + a = list[i++]; + polarity ^= 1; + b = other[j++]; + polarity ^= 2; + } + break; + } + } + loop_end: + buffer[k++] = UNICODESET_HIGH; // terminate + len = k; + swapBuffers(); + releasePattern(); +} + +// polarity = 0 is normal: x intersect y +// polarity = 2: x intersect ~y == set-minus +// polarity = 1: ~x intersect y +// polarity = 3: ~x intersect ~y + +void UnicodeSet::retain(const UChar32* other, int32_t otherLen, int8_t polarity) { + if (isFrozen() || isBogus()) { + return; + } + if (!ensureBufferCapacity(len + otherLen)) { + return; + } + + int32_t i = 0, j = 0, k = 0; + UChar32 a = list[i++]; + UChar32 b = other[j++]; + // change from xor is that we have to check overlapping pairs + // polarity bit 1 means a is second, bit 2 means b is. + for (;;) { + switch (polarity) { + case 0: // both first; drop the smaller + if (a < b) { // drop a + a = list[i++]; + polarity ^= 1; + } else if (b < a) { // drop b + b = other[j++]; + polarity ^= 2; + } else { // a == b, take one, drop other + if (a == UNICODESET_HIGH) goto loop_end; + buffer[k++] = a; + a = list[i++]; + polarity ^= 1; + b = other[j++]; + polarity ^= 2; + } + break; + case 3: // both second; take lower if unequal + if (a < b) { // take a + buffer[k++] = a; + a = list[i++]; + polarity ^= 1; + } else if (b < a) { // take b + buffer[k++] = b; + b = other[j++]; + polarity ^= 2; + } else { // a == b, take one, drop other + if (a == UNICODESET_HIGH) goto loop_end; + buffer[k++] = a; + a = list[i++]; + polarity ^= 1; + b = other[j++]; + polarity ^= 2; + } + break; + case 1: // a second, b first; + if (a < b) { // NO OVERLAP, drop a + a = list[i++]; + polarity ^= 1; + } else if (b < a) { // OVERLAP, take b + buffer[k++] = b; + b = other[j++]; + polarity ^= 2; + } else { // a == b, drop both! + if (a == UNICODESET_HIGH) goto loop_end; + a = list[i++]; + polarity ^= 1; + b = other[j++]; + polarity ^= 2; + } + break; + case 2: // a first, b second; if a < b, overlap + if (b < a) { // no overlap, drop b + b = other[j++]; + polarity ^= 2; + } else if (a < b) { // OVERLAP, take a + buffer[k++] = a; + a = list[i++]; + polarity ^= 1; + } else { // a == b, drop both! + if (a == UNICODESET_HIGH) goto loop_end; + a = list[i++]; + polarity ^= 1; + b = other[j++]; + polarity ^= 2; + } + break; + } + } + loop_end: + buffer[k++] = UNICODESET_HIGH; // terminate + len = k; + swapBuffers(); + releasePattern(); +} + +/** + * Append the toPattern() representation of a + * string to the given StringBuffer. + */ +void UnicodeSet::_appendToPat(UnicodeString& buf, const UnicodeString& s, UBool escapeUnprintable) { + UChar32 cp; + for (int32_t i = 0; i < s.length(); i += U16_LENGTH(cp)) { + _appendToPat(buf, cp = s.char32At(i), escapeUnprintable); + } +} + +/** + * Append the toPattern() representation of a + * character to the given StringBuffer. + */ +void UnicodeSet::_appendToPat(UnicodeString& buf, UChar32 c, UBool escapeUnprintable) { + if (escapeUnprintable ? ICU_Utility::isUnprintable(c) : ICU_Utility::shouldAlwaysBeEscaped(c)) { + // Use hex escape notation (\uxxxx or \Uxxxxxxxx) for anything + // unprintable + ICU_Utility::escape(buf, c); + return; + } + // Okay to let ':' pass through + switch (c) { + case u'[': + case u']': + case u'-': + case u'^': + case u'&': + case u'\\': + case u'{': + case u'}': + case u':': + case SymbolTable::SYMBOL_REF: + buf.append(u'\\'); + break; + default: + // Escape whitespace + if (PatternProps::isWhiteSpace(c)) { + buf.append(u'\\'); + } + break; + } + buf.append(c); +} + +void UnicodeSet::_appendToPat(UnicodeString &result, UChar32 start, UChar32 end, + UBool escapeUnprintable) { + _appendToPat(result, start, escapeUnprintable); + if (start != end) { + if ((start+1) != end || + // Avoid writing what looks like a lead+trail surrogate pair. + start == 0xdbff) { + result.append(u'-'); + } + _appendToPat(result, end, escapeUnprintable); + } +} + +/** + * Append a string representation of this set to result. This will be + * a cleaned version of the string passed to applyPattern(), if there + * is one. Otherwise it will be generated. + */ +UnicodeString& UnicodeSet::_toPattern(UnicodeString& result, + UBool escapeUnprintable) const +{ + if (pat != nullptr) { + int32_t i; + int32_t backslashCount = 0; + for (i=0; i= 2 && + // getRangeStart(0) == MIN_VALUE && + // getRangeEnd(last) == MAX_VALUE) + // Invariant: list[len-1] == HIGH == MAX_VALUE + 1 + // If limit == len then len is even and the last range ends with MAX_VALUE. + // + // *But* do not write the inverse (complement) if there are strings. + // Since ICU 70, the '^' performs a code point complement which removes all strings. + if (len >= 4 && list[0] == 0 && limit == len && !hasStrings()) { + // Emit the inverse + result.append(u'^'); + // Offsetting the inversion list index by one lets us + // iterate over the ranges of the set complement. + i = 1; + --limit; + } + + // Emit the ranges as pairs. + while (i < limit) { + UChar32 start = list[i]; // getRangeStart() + UChar32 end = list[i + 1] - 1; // getRangeEnd() = range limit minus one + if (!(0xd800 <= end && end <= 0xdbff)) { + _appendToPat(result, start, end, escapeUnprintable); + i += 2; + } else { + // The range ends with a lead surrogate. + // Avoid writing what looks like a lead+trail surrogate pair. + // 1. Postpone ranges that start with a lead surrogate code point. + int32_t firstLead = i; + while ((i += 2) < limit && list[i] <= 0xdbff) {} + int32_t firstAfterLead = i; + // 2. Write following ranges that start with a trail surrogate code point. + while (i < limit && (start = list[i]) <= 0xdfff) { + _appendToPat(result, start, list[i + 1] - 1, escapeUnprintable); + i += 2; + } + // 3. Now write the postponed ranges. + for (int j = firstLead; j < firstAfterLead; j += 2) { + _appendToPat(result, list[j], list[j + 1] - 1, escapeUnprintable); + } + } + } + + if (strings != nullptr) { + for (int32_t i = 0; isize(); ++i) { + result.append(u'{'); + _appendToPat(result, + *(const UnicodeString*) strings->elementAt(i), + escapeUnprintable); + result.append(u'}'); + } + } + return result.append(u']'); +} + +/** +* Release existing cached pattern +*/ +void UnicodeSet::releasePattern() { + if (pat) { + uprv_free(pat); + pat = nullptr; + patLen = 0; + } +} + +/** +* Set the new pattern to cache. +*/ +void UnicodeSet::setPattern(const char16_t *newPat, int32_t newPatLen) { + releasePattern(); + pat = (char16_t *)uprv_malloc((newPatLen + 1) * sizeof(char16_t)); + if (pat) { + patLen = newPatLen; + u_memcpy(pat, newPat, patLen); + pat[patLen] = 0; + } + // else we don't care if malloc failed. This was just a nice cache. + // We can regenerate an equivalent pattern later when requested. +} + +UnicodeSet *UnicodeSet::freeze() { + if(!isFrozen() && !isBogus()) { + compact(); + + // Optimize contains() and span() and similar functions. + if (hasStrings()) { + stringSpan = new UnicodeSetStringSpan(*this, *strings, UnicodeSetStringSpan::ALL); + if (stringSpan == nullptr) { + setToBogus(); + return this; + } else if (!stringSpan->needsStringSpanUTF16()) { + // All strings are irrelevant for span() etc. because + // all of each string's code points are contained in this set. + // Do not check needsStringSpanUTF8() because UTF-8 has at most as + // many relevant strings as UTF-16. + // (Thus needsStringSpanUTF8() implies needsStringSpanUTF16().) + delete stringSpan; + stringSpan = nullptr; + } + } + if (stringSpan == nullptr) { + // No span-relevant strings: Optimize for code point spans. + bmpSet=new BMPSet(list, len); + if (bmpSet == nullptr) { // Check for memory allocation error. + setToBogus(); + } + } + } + return this; +} + +int32_t UnicodeSet::span(const char16_t *s, int32_t length, USetSpanCondition spanCondition) const { + if(length>0 && bmpSet!=nullptr) { + return (int32_t)(bmpSet->span(s, s+length, spanCondition)-s); + } + if(length<0) { + length=u_strlen(s); + } + if(length==0) { + return 0; + } + if(stringSpan!=nullptr) { + return stringSpan->span(s, length, spanCondition); + } else if(hasStrings()) { + uint32_t which= spanCondition==USET_SPAN_NOT_CONTAINED ? + UnicodeSetStringSpan::FWD_UTF16_NOT_CONTAINED : + UnicodeSetStringSpan::FWD_UTF16_CONTAINED; + UnicodeSetStringSpan strSpan(*this, *strings, which); + if(strSpan.needsStringSpanUTF16()) { + return strSpan.span(s, length, spanCondition); + } + } + + if(spanCondition!=USET_SPAN_NOT_CONTAINED) { + spanCondition=USET_SPAN_CONTAINED; // Pin to 0/1 values. + } + + UChar32 c; + int32_t start=0, prev=0; + do { + U16_NEXT(s, start, length, c); + if(spanCondition!=contains(c)) { + break; + } + } while((prev=start)0 && bmpSet!=nullptr) { + return (int32_t)(bmpSet->spanBack(s, s+length, spanCondition)-s); + } + if(length<0) { + length=u_strlen(s); + } + if(length==0) { + return 0; + } + if(stringSpan!=nullptr) { + return stringSpan->spanBack(s, length, spanCondition); + } else if(hasStrings()) { + uint32_t which= spanCondition==USET_SPAN_NOT_CONTAINED ? + UnicodeSetStringSpan::BACK_UTF16_NOT_CONTAINED : + UnicodeSetStringSpan::BACK_UTF16_CONTAINED; + UnicodeSetStringSpan strSpan(*this, *strings, which); + if(strSpan.needsStringSpanUTF16()) { + return strSpan.spanBack(s, length, spanCondition); + } + } + + if(spanCondition!=USET_SPAN_NOT_CONTAINED) { + spanCondition=USET_SPAN_CONTAINED; // Pin to 0/1 values. + } + + UChar32 c; + int32_t prev=length; + do { + U16_PREV(s, 0, length, c); + if(spanCondition!=contains(c)) { + break; + } + } while((prev=length)>0); + return prev; +} + +int32_t UnicodeSet::spanUTF8(const char *s, int32_t length, USetSpanCondition spanCondition) const { + if(length>0 && bmpSet!=nullptr) { + const uint8_t *s0=(const uint8_t *)s; + return (int32_t)(bmpSet->spanUTF8(s0, length, spanCondition)-s0); + } + if(length<0) { + length=(int32_t)uprv_strlen(s); + } + if(length==0) { + return 0; + } + if(stringSpan!=nullptr) { + return stringSpan->spanUTF8((const uint8_t *)s, length, spanCondition); + } else if(hasStrings()) { + uint32_t which= spanCondition==USET_SPAN_NOT_CONTAINED ? + UnicodeSetStringSpan::FWD_UTF8_NOT_CONTAINED : + UnicodeSetStringSpan::FWD_UTF8_CONTAINED; + UnicodeSetStringSpan strSpan(*this, *strings, which); + if(strSpan.needsStringSpanUTF8()) { + return strSpan.spanUTF8((const uint8_t *)s, length, spanCondition); + } + } + + if(spanCondition!=USET_SPAN_NOT_CONTAINED) { + spanCondition=USET_SPAN_CONTAINED; // Pin to 0/1 values. + } + + UChar32 c; + int32_t start=0, prev=0; + do { + U8_NEXT_OR_FFFD(s, start, length, c); + if(spanCondition!=contains(c)) { + break; + } + } while((prev=start)0 && bmpSet!=nullptr) { + const uint8_t *s0=(const uint8_t *)s; + return bmpSet->spanBackUTF8(s0, length, spanCondition); + } + if(length<0) { + length=(int32_t)uprv_strlen(s); + } + if(length==0) { + return 0; + } + if(stringSpan!=nullptr) { + return stringSpan->spanBackUTF8((const uint8_t *)s, length, spanCondition); + } else if(hasStrings()) { + uint32_t which= spanCondition==USET_SPAN_NOT_CONTAINED ? + UnicodeSetStringSpan::BACK_UTF8_NOT_CONTAINED : + UnicodeSetStringSpan::BACK_UTF8_CONTAINED; + UnicodeSetStringSpan strSpan(*this, *strings, which); + if(strSpan.needsStringSpanUTF8()) { + return strSpan.spanBackUTF8((const uint8_t *)s, length, spanCondition); + } + } + + if(spanCondition!=USET_SPAN_NOT_CONTAINED) { + spanCondition=USET_SPAN_CONTAINED; // Pin to 0/1 values. + } + + UChar32 c; + int32_t prev=length; + do { + U8_PREV_OR_FFFD(s, 0, length, c); + if(spanCondition!=contains(c)) { + break; + } + } while((prev=length)>0); + return prev; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/uniset_closure.cpp b/deps/icu-small/source/common/uniset_closure.cpp index d7dab2a17b7e59..8b7fdfc24e6cab 100644 --- a/deps/icu-small/source/common/uniset_closure.cpp +++ b/deps/icu-small/source/common/uniset_closure.cpp @@ -1,250 +1,358 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uniset_closure.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2011may30 -* created by: Markus W. Scherer -* -* UnicodeSet::closeOver() and related methods moved here from uniset_props.cpp -* to simplify dependencies. -* In particular, this depends on the BreakIterator, but the BreakIterator -* code also builds UnicodeSets from patterns and needs uniset_props. -*/ - -#include "unicode/brkiter.h" -#include "unicode/locid.h" -#include "unicode/parsepos.h" -#include "unicode/uniset.h" -#include "cmemory.h" -#include "ruleiter.h" -#include "ucase.h" -#include "util.h" -#include "uvector.h" - -U_NAMESPACE_BEGIN - -// TODO memory debugging provided inside uniset.cpp -// could be made available here but probably obsolete with use of modern -// memory leak checker tools -#define _dbgct(me) - -//---------------------------------------------------------------- -// Constructors &c -//---------------------------------------------------------------- - -UnicodeSet::UnicodeSet(const UnicodeString& pattern, - uint32_t options, - const SymbolTable* symbols, - UErrorCode& status) { - applyPattern(pattern, options, symbols, status); - _dbgct(this); -} - -UnicodeSet::UnicodeSet(const UnicodeString& pattern, ParsePosition& pos, - uint32_t options, - const SymbolTable* symbols, - UErrorCode& status) { - applyPattern(pattern, pos, options, symbols, status); - _dbgct(this); -} - -//---------------------------------------------------------------- -// Public API -//---------------------------------------------------------------- - -UnicodeSet& UnicodeSet::applyPattern(const UnicodeString& pattern, - uint32_t options, - const SymbolTable* symbols, - UErrorCode& status) { - ParsePosition pos(0); - applyPattern(pattern, pos, options, symbols, status); - if (U_FAILURE(status)) return *this; - - int32_t i = pos.getIndex(); - - if (options & USET_IGNORE_SPACE) { - // Skip over trailing whitespace - ICU_Utility::skipWhitespace(pattern, i, true); - } - - if (i != pattern.length()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } - return *this; -} - -UnicodeSet& UnicodeSet::applyPattern(const UnicodeString& pattern, - ParsePosition& pos, - uint32_t options, - const SymbolTable* symbols, - UErrorCode& status) { - if (U_FAILURE(status)) { - return *this; - } - if (isFrozen()) { - status = U_NO_WRITE_PERMISSION; - return *this; - } - // Need to build the pattern in a temporary string because - // _applyPattern calls add() etc., which set pat to empty. - UnicodeString rebuiltPat; - RuleCharacterIterator chars(pattern, symbols, pos); - applyPattern(chars, symbols, rebuiltPat, options, &UnicodeSet::closeOver, 0, status); - if (U_FAILURE(status)) return *this; - if (chars.inVariable()) { - // syntaxError(chars, "Extra chars in variable value"); - status = U_MALFORMED_SET; - return *this; - } - setPattern(rebuiltPat); - return *this; -} - -// USetAdder implementation -// Does not use uset.h to reduce code dependencies -static void U_CALLCONV -_set_add(USet *set, UChar32 c) { - ((UnicodeSet *)set)->add(c); -} - -static void U_CALLCONV -_set_addRange(USet *set, UChar32 start, UChar32 end) { - ((UnicodeSet *)set)->add(start, end); -} - -static void U_CALLCONV -_set_addString(USet *set, const UChar *str, int32_t length) { - ((UnicodeSet *)set)->add(UnicodeString((UBool)(length<0), str, length)); -} - -//---------------------------------------------------------------- -// Case folding API -//---------------------------------------------------------------- - -// add the result of a full case mapping to the set -// use str as a temporary string to avoid constructing one -static inline void -addCaseMapping(UnicodeSet &set, int32_t result, const UChar *full, UnicodeString &str) { - if(result >= 0) { - if(result > UCASE_MAX_STRING_LENGTH) { - // add a single-code point case mapping - set.add(result); - } else { - // add a string case mapping from full with length result - str.setTo((UBool)false, full, result); - set.add(str); - } - } - // result < 0: the code point mapped to itself, no need to add it - // see ucase.h -} - -UnicodeSet& UnicodeSet::closeOver(int32_t attribute) { - if (isFrozen() || isBogus()) { - return *this; - } - if (attribute & (USET_CASE_INSENSITIVE | USET_ADD_CASE_MAPPINGS)) { - { - UnicodeSet foldSet(*this); - UnicodeString str; - USetAdder sa = { - foldSet.toUSet(), - _set_add, - _set_addRange, - _set_addString, - NULL, // don't need remove() - NULL // don't need removeRange() - }; - - // start with input set to guarantee inclusion - // USET_CASE: remove strings because the strings will actually be reduced (folded); - // therefore, start with no strings and add only those needed - if ((attribute & USET_CASE_INSENSITIVE) && foldSet.hasStrings()) { - foldSet.strings->removeAllElements(); - } - - int32_t n = getRangeCount(); - UChar32 result; - const UChar *full; - - for (int32_t i=0; isize(); ++j) { - str = *(const UnicodeString *) strings->elementAt(j); - str.foldCase(); - if(!ucase_addStringCaseClosure(str.getBuffer(), str.length(), &sa)) { - foldSet.add(str); // does not map to code points: add the folded string itself - } - } - } else { - Locale root(""); -#if !UCONFIG_NO_BREAK_ITERATION - UErrorCode status = U_ZERO_ERROR; - BreakIterator *bi = BreakIterator::createWordInstance(root, status); - if (U_SUCCESS(status)) { -#endif - const UnicodeString *pStr; - - for (int32_t j=0; jsize(); ++j) { - pStr = (const UnicodeString *) strings->elementAt(j); - (str = *pStr).toLower(root); - foldSet.add(str); -#if !UCONFIG_NO_BREAK_ITERATION - (str = *pStr).toTitle(bi, root); - foldSet.add(str); -#endif - (str = *pStr).toUpper(root); - foldSet.add(str); - (str = *pStr).foldCase(); - foldSet.add(str); - } -#if !UCONFIG_NO_BREAK_ITERATION - } - delete bi; -#endif - } - } - *this = foldSet; - } - } - return *this; -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uniset_closure.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2011may30 +* created by: Markus W. Scherer +* +* UnicodeSet::closeOver() and related methods moved here from uniset_props.cpp +* to simplify dependencies. +* In particular, this depends on the BreakIterator, but the BreakIterator +* code also builds UnicodeSets from patterns and needs uniset_props. +*/ + +#include "unicode/brkiter.h" +#include "unicode/locid.h" +#include "unicode/parsepos.h" +#include "unicode/uniset.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "ruleiter.h" +#include "ucase.h" +#include "uprops.h" +#include "util.h" +#include "uvector.h" + +U_NAMESPACE_BEGIN + +// TODO memory debugging provided inside uniset.cpp +// could be made available here but probably obsolete with use of modern +// memory leak checker tools +#define _dbgct(me) + +//---------------------------------------------------------------- +// Constructors &c +//---------------------------------------------------------------- + +UnicodeSet::UnicodeSet(const UnicodeString& pattern, + uint32_t options, + const SymbolTable* symbols, + UErrorCode& status) { + applyPattern(pattern, options, symbols, status); + _dbgct(this); +} + +UnicodeSet::UnicodeSet(const UnicodeString& pattern, ParsePosition& pos, + uint32_t options, + const SymbolTable* symbols, + UErrorCode& status) { + applyPattern(pattern, pos, options, symbols, status); + _dbgct(this); +} + +//---------------------------------------------------------------- +// Public API +//---------------------------------------------------------------- + +UnicodeSet& UnicodeSet::applyPattern(const UnicodeString& pattern, + uint32_t options, + const SymbolTable* symbols, + UErrorCode& status) { + ParsePosition pos(0); + applyPattern(pattern, pos, options, symbols, status); + if (U_FAILURE(status)) return *this; + + int32_t i = pos.getIndex(); + + if (options & USET_IGNORE_SPACE) { + // Skip over trailing whitespace + ICU_Utility::skipWhitespace(pattern, i, true); + } + + if (i != pattern.length()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } + return *this; +} + +UnicodeSet& UnicodeSet::applyPattern(const UnicodeString& pattern, + ParsePosition& pos, + uint32_t options, + const SymbolTable* symbols, + UErrorCode& status) { + if (U_FAILURE(status)) { + return *this; + } + if (isFrozen()) { + status = U_NO_WRITE_PERMISSION; + return *this; + } + // Need to build the pattern in a temporary string because + // _applyPattern calls add() etc., which set pat to empty. + UnicodeString rebuiltPat; + RuleCharacterIterator chars(pattern, symbols, pos); + applyPattern(chars, symbols, rebuiltPat, options, &UnicodeSet::closeOver, 0, status); + if (U_FAILURE(status)) return *this; + if (chars.inVariable()) { + // syntaxError(chars, "Extra chars in variable value"); + status = U_MALFORMED_SET; + return *this; + } + setPattern(rebuiltPat); + return *this; +} + +// USetAdder implementation +// Does not use uset.h to reduce code dependencies +static void U_CALLCONV +_set_add(USet *set, UChar32 c) { + ((UnicodeSet *)set)->add(c); +} + +static void U_CALLCONV +_set_addRange(USet *set, UChar32 start, UChar32 end) { + ((UnicodeSet *)set)->add(start, end); +} + +static void U_CALLCONV +_set_addString(USet *set, const char16_t *str, int32_t length) { + ((UnicodeSet *)set)->add(UnicodeString((UBool)(length<0), str, length)); +} + +//---------------------------------------------------------------- +// Case folding API +//---------------------------------------------------------------- + +// add the result of a full case mapping to the set +// use str as a temporary string to avoid constructing one +static inline void +addCaseMapping(UnicodeSet &set, int32_t result, const char16_t *full, UnicodeString &str) { + if(result >= 0) { + if(result > UCASE_MAX_STRING_LENGTH) { + // add a single-code point case mapping + set.add(result); + } else { + // add a string case mapping from full with length result + str.setTo((UBool)false, full, result); + set.add(str); + } + } + // result < 0: the code point mapped to itself, no need to add it + // see ucase.h +} + +namespace { + +/** For case closure on a large set, look only at code points with relevant properties. */ +const UnicodeSet &maybeOnlyCaseSensitive(const UnicodeSet &src, UnicodeSet &subset) { + // The subset must have been constructed with all code points, + // so that the retainAll() intersection effectively copies all single code points from src. + U_ASSERT(subset.contains(0, 0x10ffff)); + if (src.size() < 30) { + return src; + } + // Return the intersection of the src code points with Case_Sensitive ones. + UErrorCode errorCode = U_ZERO_ERROR; + const UnicodeSet *sensitive = + CharacterProperties::getBinaryPropertySet(UCHAR_CASE_SENSITIVE, errorCode); + if (U_FAILURE(errorCode)) { + return src; + } + // Start by copying the "smaller" set. + // (We "copy" by intersecting all Unicode *code points* with the first set, + // which omits any strings.) + if (src.getRangeCount() > sensitive->getRangeCount()) { + subset.retainAll(*sensitive); + subset.retainAll(src); + } else { + subset.retainAll(src); + subset.retainAll(*sensitive); + } + return subset; +} + +// Per-character scf = Simple_Case_Folding of a string. +// (Normally when we case-fold a string we use full case foldings.) +bool scfString(const UnicodeString &s, UnicodeString &scf) { + // Iterate over the raw buffer for best performance. + const char16_t *p = s.getBuffer(); + int32_t length = s.length(); + // Loop while not needing modification. + for (int32_t i = 0; i < length;) { + UChar32 c; + U16_NEXT(p, i, length, c); // post-increments i + UChar32 scfChar = u_foldCase(c, U_FOLD_CASE_DEFAULT); + if (scfChar != c) { + // Copy the characters before c. + scf.setTo(p, i - U16_LENGTH(c)); + // Loop over the rest of the string and keep case-folding. + for (;;) { + scf.append(scfChar); + if (i == length) { + return true; + } + U16_NEXT(p, i, length, c); // post-increments i + scfChar = u_foldCase(c, U_FOLD_CASE_DEFAULT); + } + } + } + return false; +} + +} // namespace + +UnicodeSet& UnicodeSet::closeOver(int32_t attribute) { + if (isFrozen() || isBogus()) { + return *this; + } + switch (attribute & USET_CASE_MASK) { + case 0: + break; + case USET_CASE_INSENSITIVE: + closeOverCaseInsensitive(/* simple= */ false); + break; + case USET_ADD_CASE_MAPPINGS: + closeOverAddCaseMappings(); + break; + case USET_SIMPLE_CASE_INSENSITIVE: + closeOverCaseInsensitive(/* simple= */ true); + break; + default: + // bad option (unreachable) + break; + } + return *this; +} + +void UnicodeSet::closeOverCaseInsensitive(bool simple) { + // Start with input set to guarantee inclusion. + UnicodeSet foldSet(*this); + // Full case mappings closure: + // Remove strings because the strings will actually be reduced (folded); + // therefore, start with no strings and add only those needed. + // Do this before processing code points, because they may add strings. + if (!simple && foldSet.hasStrings()) { + foldSet.strings->removeAllElements(); + } + + USetAdder sa = { + foldSet.toUSet(), + _set_add, + _set_addRange, + _set_addString, + nullptr, // don't need remove() + nullptr // don't need removeRange() + }; + + UnicodeSet subset(0, 0x10ffff); + const UnicodeSet &codePoints = maybeOnlyCaseSensitive(*this, subset); + + // Iterate over the ranges of single code points. Nested loop for each code point. + int32_t n = codePoints.getRangeCount(); + + for (int32_t i=0; isize(); ++j) { + const UnicodeString *pStr = (const UnicodeString *) strings->elementAt(j); + if (simple) { + if (scfString(*pStr, str)) { + foldSet.remove(*pStr).add(str); + } + } else { + str = *pStr; + str.foldCase(); + if(!ucase_addStringCaseClosure(str.getBuffer(), str.length(), &sa)) { + foldSet.add(str); // does not map to code points: add the folded string itself + } + } + } + } + *this = foldSet; +} + +void UnicodeSet::closeOverAddCaseMappings() { + // Start with input set to guarantee inclusion. + UnicodeSet foldSet(*this); + + UnicodeSet subset(0, 0x10ffff); + const UnicodeSet &codePoints = maybeOnlyCaseSensitive(*this, subset); + + // Iterate over the ranges of single code points. Nested loop for each code point. + int32_t n = codePoints.getRangeCount(); + UChar32 result; + const char16_t *full; + UnicodeString str; + + for (int32_t i=0; isize(); ++j) { + const UnicodeString *pStr = (const UnicodeString *) strings->elementAt(j); + (str = *pStr).toLower(root); + foldSet.add(str); +#if !UCONFIG_NO_BREAK_ITERATION + (str = *pStr).toTitle(bi, root); + foldSet.add(str); +#endif + (str = *pStr).toUpper(root); + foldSet.add(str); + (str = *pStr).foldCase(); + foldSet.add(str); + } +#if !UCONFIG_NO_BREAK_ITERATION + } + delete bi; +#endif + } + *this = foldSet; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/uniset_props.cpp b/deps/icu-small/source/common/uniset_props.cpp index 48c0a26a710304..9d35523e6e5d4c 100644 --- a/deps/icu-small/source/common/uniset_props.cpp +++ b/deps/icu-small/source/common/uniset_props.cpp @@ -1,1142 +1,1139 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1999-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uniset_props.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004aug25 -* created by: Markus W. Scherer -* -* Character property dependent functions moved here from uniset.cpp -*/ - -#include "unicode/utypes.h" -#include "unicode/uniset.h" -#include "unicode/parsepos.h" -#include "unicode/uchar.h" -#include "unicode/uscript.h" -#include "unicode/symtable.h" -#include "unicode/uset.h" -#include "unicode/locid.h" -#include "unicode/brkiter.h" -#include "uset_imp.h" -#include "ruleiter.h" -#include "cmemory.h" -#include "ucln_cmn.h" -#include "util.h" -#include "uvector.h" -#include "uprops.h" -#include "propname.h" -#include "normalizer2impl.h" -#include "uinvchar.h" -#include "uprops.h" -#include "charstr.h" -#include "cstring.h" -#include "mutex.h" -#include "umutex.h" -#include "uassert.h" -#include "hash.h" - -U_NAMESPACE_USE - -// Special property set IDs -static const char ANY[] = "ANY"; // [\u0000-\U0010FFFF] -static const char ASCII[] = "ASCII"; // [\u0000-\u007F] -static const char ASSIGNED[] = "Assigned"; // [:^Cn:] - -// Unicode name property alias -#define NAME_PROP "na" -#define NAME_PROP_LENGTH 2 - -// Cached sets ------------------------------------------------------------- *** - -U_CDECL_BEGIN -static UBool U_CALLCONV uset_cleanup(); - -static UnicodeSet *uni32Singleton; -static icu::UInitOnce uni32InitOnce {}; - -/** - * Cleanup function for UnicodeSet - */ -static UBool U_CALLCONV uset_cleanup(void) { - delete uni32Singleton; - uni32Singleton = NULL; - uni32InitOnce.reset(); - return true; -} - -U_CDECL_END - -U_NAMESPACE_BEGIN - -namespace { - -// Cache some sets for other services -------------------------------------- *** -void U_CALLCONV createUni32Set(UErrorCode &errorCode) { - U_ASSERT(uni32Singleton == NULL); - uni32Singleton = new UnicodeSet(UNICODE_STRING_SIMPLE("[:age=3.2:]"), errorCode); - if(uni32Singleton==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } else { - uni32Singleton->freeze(); - } - ucln_common_registerCleanup(UCLN_COMMON_USET, uset_cleanup); -} - - -U_CFUNC UnicodeSet * -uniset_getUnicode32Instance(UErrorCode &errorCode) { - umtx_initOnce(uni32InitOnce, &createUni32Set, errorCode); - return uni32Singleton; -} - -// helper functions for matching of pattern syntax pieces ------------------ *** -// these functions are parallel to the PERL_OPEN etc. strings above - -// using these functions is not only faster than UnicodeString::compare() and -// caseCompare(), but they also make UnicodeSet work for simple patterns when -// no Unicode properties data is available - when caseCompare() fails - -static inline UBool -isPerlOpen(const UnicodeString &pattern, int32_t pos) { - UChar c; - return pattern.charAt(pos)==u'\\' && ((c=pattern.charAt(pos+1))==u'p' || c==u'P'); -} - -/*static inline UBool -isPerlClose(const UnicodeString &pattern, int32_t pos) { - return pattern.charAt(pos)==u'}'; -}*/ - -static inline UBool -isNameOpen(const UnicodeString &pattern, int32_t pos) { - return pattern.charAt(pos)==u'\\' && pattern.charAt(pos+1)==u'N'; -} - -static inline UBool -isPOSIXOpen(const UnicodeString &pattern, int32_t pos) { - return pattern.charAt(pos)==u'[' && pattern.charAt(pos+1)==u':'; -} - -/*static inline UBool -isPOSIXClose(const UnicodeString &pattern, int32_t pos) { - return pattern.charAt(pos)==u':' && pattern.charAt(pos+1)==u']'; -}*/ - -// TODO memory debugging provided inside uniset.cpp -// could be made available here but probably obsolete with use of modern -// memory leak checker tools -#define _dbgct(me) - -} // namespace - -//---------------------------------------------------------------- -// Constructors &c -//---------------------------------------------------------------- - -/** - * Constructs a set from the given pattern, optionally ignoring - * white space. See the class description for the syntax of the - * pattern language. - * @param pattern a string specifying what characters are in the set - */ -UnicodeSet::UnicodeSet(const UnicodeString& pattern, - UErrorCode& status) { - applyPattern(pattern, status); - _dbgct(this); -} - -//---------------------------------------------------------------- -// Public API -//---------------------------------------------------------------- - -UnicodeSet& UnicodeSet::applyPattern(const UnicodeString& pattern, - UErrorCode& status) { - // Equivalent to - // return applyPattern(pattern, USET_IGNORE_SPACE, NULL, status); - // but without dependency on closeOver(). - ParsePosition pos(0); - applyPatternIgnoreSpace(pattern, pos, NULL, status); - if (U_FAILURE(status)) return *this; - - int32_t i = pos.getIndex(); - // Skip over trailing whitespace - ICU_Utility::skipWhitespace(pattern, i, true); - if (i != pattern.length()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } - return *this; -} - -void -UnicodeSet::applyPatternIgnoreSpace(const UnicodeString& pattern, - ParsePosition& pos, - const SymbolTable* symbols, - UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - if (isFrozen()) { - status = U_NO_WRITE_PERMISSION; - return; - } - // Need to build the pattern in a temporary string because - // _applyPattern calls add() etc., which set pat to empty. - UnicodeString rebuiltPat; - RuleCharacterIterator chars(pattern, symbols, pos); - applyPattern(chars, symbols, rebuiltPat, USET_IGNORE_SPACE, NULL, 0, status); - if (U_FAILURE(status)) return; - if (chars.inVariable()) { - // syntaxError(chars, "Extra chars in variable value"); - status = U_MALFORMED_SET; - return; - } - setPattern(rebuiltPat); -} - -/** - * Return true if the given position, in the given pattern, appears - * to be the start of a UnicodeSet pattern. - */ -UBool UnicodeSet::resemblesPattern(const UnicodeString& pattern, int32_t pos) { - return ((pos+1) < pattern.length() && - pattern.charAt(pos) == (UChar)91/*[*/) || - resemblesPropertyPattern(pattern, pos); -} - -//---------------------------------------------------------------- -// Implementation: Pattern parsing -//---------------------------------------------------------------- - -namespace { - -/** - * A small all-inline class to manage a UnicodeSet pointer. Add - * operator->() etc. as needed. - */ -class UnicodeSetPointer { - UnicodeSet* p; -public: - inline UnicodeSetPointer() : p(0) {} - inline ~UnicodeSetPointer() { delete p; } - inline UnicodeSet* pointer() { return p; } - inline UBool allocate() { - if (p == 0) { - p = new UnicodeSet(); - } - return p != 0; - } -}; - -constexpr int32_t MAX_DEPTH = 100; - -} // namespace - -/** - * Parse the pattern from the given RuleCharacterIterator. The - * iterator is advanced over the parsed pattern. - * @param chars iterator over the pattern characters. Upon return - * it will be advanced to the first character after the parsed - * pattern, or the end of the iteration if all characters are - * parsed. - * @param symbols symbol table to use to parse and dereference - * variables, or null if none. - * @param rebuiltPat the pattern that was parsed, rebuilt or - * copied from the input pattern, as appropriate. - * @param options a bit mask of zero or more of the following: - * IGNORE_SPACE, CASE. - */ -void UnicodeSet::applyPattern(RuleCharacterIterator& chars, - const SymbolTable* symbols, - UnicodeString& rebuiltPat, - uint32_t options, - UnicodeSet& (UnicodeSet::*caseClosure)(int32_t attribute), - int32_t depth, - UErrorCode& ec) { - if (U_FAILURE(ec)) return; - if (depth > MAX_DEPTH) { - ec = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - // Syntax characters: [ ] ^ - & { } - - // Recognized special forms for chars, sets: c-c s-s s&s - - int32_t opts = RuleCharacterIterator::PARSE_VARIABLES | - RuleCharacterIterator::PARSE_ESCAPES; - if ((options & USET_IGNORE_SPACE) != 0) { - opts |= RuleCharacterIterator::SKIP_WHITESPACE; - } - - UnicodeString patLocal, buf; - UBool usePat = false; - UnicodeSetPointer scratch; - RuleCharacterIterator::Pos backup; - - // mode: 0=before [, 1=between [...], 2=after ] - // lastItem: 0=none, 1=char, 2=set - int8_t lastItem = 0, mode = 0; - UChar32 lastChar = 0; - UChar op = 0; - - UBool invert = false; - - clear(); - - while (mode != 2 && !chars.atEnd()) { - U_ASSERT((lastItem == 0 && op == 0) || - (lastItem == 1 && (op == 0 || op == u'-')) || - (lastItem == 2 && (op == 0 || op == u'-' || op == u'&'))); - - UChar32 c = 0; - UBool literal = false; - UnicodeSet* nested = 0; // alias - do not delete - - // -------- Check for property pattern - - // setMode: 0=none, 1=unicodeset, 2=propertypat, 3=preparsed - int8_t setMode = 0; - if (resemblesPropertyPattern(chars, opts)) { - setMode = 2; - } - - // -------- Parse '[' of opening delimiter OR nested set. - // If there is a nested set, use `setMode' to define how - // the set should be parsed. If the '[' is part of the - // opening delimiter for this pattern, parse special - // strings "[", "[^", "[-", and "[^-". Check for stand-in - // characters representing a nested set in the symbol - // table. - - else { - // Prepare to backup if necessary - chars.getPos(backup); - c = chars.next(opts, literal, ec); - if (U_FAILURE(ec)) return; - - if (c == u'[' && !literal) { - if (mode == 1) { - chars.setPos(backup); // backup - setMode = 1; - } else { - // Handle opening '[' delimiter - mode = 1; - patLocal.append(u'['); - chars.getPos(backup); // prepare to backup - c = chars.next(opts, literal, ec); - if (U_FAILURE(ec)) return; - if (c == u'^' && !literal) { - invert = true; - patLocal.append(u'^'); - chars.getPos(backup); // prepare to backup - c = chars.next(opts, literal, ec); - if (U_FAILURE(ec)) return; - } - // Fall through to handle special leading '-'; - // otherwise restart loop for nested [], \p{}, etc. - if (c == u'-') { - literal = true; - // Fall through to handle literal '-' below - } else { - chars.setPos(backup); // backup - continue; - } - } - } else if (symbols != 0) { - const UnicodeFunctor *m = symbols->lookupMatcher(c); - if (m != 0) { - const UnicodeSet *ms = dynamic_cast(m); - if (ms == NULL) { - ec = U_MALFORMED_SET; - return; - } - // casting away const, but `nested' won't be modified - // (important not to modify stored set) - nested = const_cast(ms); - setMode = 3; - } - } - } - - // -------- Handle a nested set. This either is inline in - // the pattern or represented by a stand-in that has - // previously been parsed and was looked up in the symbol - // table. - - if (setMode != 0) { - if (lastItem == 1) { - if (op != 0) { - // syntaxError(chars, "Char expected after operator"); - ec = U_MALFORMED_SET; - return; - } - add(lastChar, lastChar); - _appendToPat(patLocal, lastChar, false); - lastItem = 0; - op = 0; - } - - if (op == u'-' || op == u'&') { - patLocal.append(op); - } - - if (nested == 0) { - // lazy allocation - if (!scratch.allocate()) { - ec = U_MEMORY_ALLOCATION_ERROR; - return; - } - nested = scratch.pointer(); - } - switch (setMode) { - case 1: - nested->applyPattern(chars, symbols, patLocal, options, caseClosure, depth + 1, ec); - break; - case 2: - chars.skipIgnored(opts); - nested->applyPropertyPattern(chars, patLocal, ec); - if (U_FAILURE(ec)) return; - break; - case 3: // `nested' already parsed - nested->_toPattern(patLocal, false); - break; - } - - usePat = true; - - if (mode == 0) { - // Entire pattern is a category; leave parse loop - *this = *nested; - mode = 2; - break; - } - - switch (op) { - case u'-': - removeAll(*nested); - break; - case u'&': - retainAll(*nested); - break; - case 0: - addAll(*nested); - break; - } - - op = 0; - lastItem = 2; - - continue; - } - - if (mode == 0) { - // syntaxError(chars, "Missing '['"); - ec = U_MALFORMED_SET; - return; - } - - // -------- Parse special (syntax) characters. If the - // current character is not special, or if it is escaped, - // then fall through and handle it below. - - if (!literal) { - switch (c) { - case u']': - if (lastItem == 1) { - add(lastChar, lastChar); - _appendToPat(patLocal, lastChar, false); - } - // Treat final trailing '-' as a literal - if (op == u'-') { - add(op, op); - patLocal.append(op); - } else if (op == u'&') { - // syntaxError(chars, "Trailing '&'"); - ec = U_MALFORMED_SET; - return; - } - patLocal.append(u']'); - mode = 2; - continue; - case u'-': - if (op == 0) { - if (lastItem != 0) { - op = (UChar) c; - continue; - } else { - // Treat final trailing '-' as a literal - add(c, c); - c = chars.next(opts, literal, ec); - if (U_FAILURE(ec)) return; - if (c == u']' && !literal) { - patLocal.append(u"-]", 2); - mode = 2; - continue; - } - } - } - // syntaxError(chars, "'-' not after char or set"); - ec = U_MALFORMED_SET; - return; - case u'&': - if (lastItem == 2 && op == 0) { - op = (UChar) c; - continue; - } - // syntaxError(chars, "'&' not after set"); - ec = U_MALFORMED_SET; - return; - case u'^': - // syntaxError(chars, "'^' not after '['"); - ec = U_MALFORMED_SET; - return; - case u'{': - if (op != 0) { - // syntaxError(chars, "Missing operand after operator"); - ec = U_MALFORMED_SET; - return; - } - if (lastItem == 1) { - add(lastChar, lastChar); - _appendToPat(patLocal, lastChar, false); - } - lastItem = 0; - buf.truncate(0); - { - UBool ok = false; - while (!chars.atEnd()) { - c = chars.next(opts, literal, ec); - if (U_FAILURE(ec)) return; - if (c == u'}' && !literal) { - ok = true; - break; - } - buf.append(c); - } - if (!ok) { - // syntaxError(chars, "Invalid multicharacter string"); - ec = U_MALFORMED_SET; - return; - } - } - // We have new string. Add it to set and continue; - // we don't need to drop through to the further - // processing - add(buf); - patLocal.append(u'{'); - _appendToPat(patLocal, buf, false); - patLocal.append(u'}'); - continue; - case SymbolTable::SYMBOL_REF: - // symbols nosymbols - // [a-$] error error (ambiguous) - // [a$] anchor anchor - // [a-$x] var "x"* literal '$' - // [a-$.] error literal '$' - // *We won't get here in the case of var "x" - { - chars.getPos(backup); - c = chars.next(opts, literal, ec); - if (U_FAILURE(ec)) return; - UBool anchor = (c == u']' && !literal); - if (symbols == 0 && !anchor) { - c = SymbolTable::SYMBOL_REF; - chars.setPos(backup); - break; // literal '$' - } - if (anchor && op == 0) { - if (lastItem == 1) { - add(lastChar, lastChar); - _appendToPat(patLocal, lastChar, false); - } - add(U_ETHER); - usePat = true; - patLocal.append((UChar) SymbolTable::SYMBOL_REF); - patLocal.append(u']'); - mode = 2; - continue; - } - // syntaxError(chars, "Unquoted '$'"); - ec = U_MALFORMED_SET; - return; - } - default: - break; - } - } - - // -------- Parse literal characters. This includes both - // escaped chars ("\u4E01") and non-syntax characters - // ("a"). - - switch (lastItem) { - case 0: - lastItem = 1; - lastChar = c; - break; - case 1: - if (op == u'-') { - if (lastChar >= c) { - // Don't allow redundant (a-a) or empty (b-a) ranges; - // these are most likely typos. - // syntaxError(chars, "Invalid range"); - ec = U_MALFORMED_SET; - return; - } - add(lastChar, c); - _appendToPat(patLocal, lastChar, false); - patLocal.append(op); - _appendToPat(patLocal, c, false); - lastItem = 0; - op = 0; - } else { - add(lastChar, lastChar); - _appendToPat(patLocal, lastChar, false); - lastChar = c; - } - break; - case 2: - if (op != 0) { - // syntaxError(chars, "Set expected after operator"); - ec = U_MALFORMED_SET; - return; - } - lastChar = c; - lastItem = 1; - break; - } - } - - if (mode != 2) { - // syntaxError(chars, "Missing ']'"); - ec = U_MALFORMED_SET; - return; - } - - chars.skipIgnored(opts); - - /** - * Handle global flags (invert, case insensitivity). If this - * pattern should be compiled case-insensitive, then we need - * to close over case BEFORE COMPLEMENTING. This makes - * patterns like /[^abc]/i work. - */ - if ((options & USET_CASE_INSENSITIVE) != 0) { - (this->*caseClosure)(USET_CASE_INSENSITIVE); - } - else if ((options & USET_ADD_CASE_MAPPINGS) != 0) { - (this->*caseClosure)(USET_ADD_CASE_MAPPINGS); - } - if (invert) { - complement().removeAllStrings(); // code point complement - } - - // Use the rebuilt pattern (patLocal) only if necessary. Prefer the - // generated pattern. - if (usePat) { - rebuiltPat.append(patLocal); - } else { - _generatePattern(rebuiltPat, false); - } - if (isBogus() && U_SUCCESS(ec)) { - // We likely ran out of memory. AHHH! - ec = U_MEMORY_ALLOCATION_ERROR; - } -} - -//---------------------------------------------------------------- -// Property set implementation -//---------------------------------------------------------------- - -namespace { - -static UBool numericValueFilter(UChar32 ch, void* context) { - return u_getNumericValue(ch) == *(double*)context; -} - -static UBool generalCategoryMaskFilter(UChar32 ch, void* context) { - int32_t value = *(int32_t*)context; - return (U_GET_GC_MASK((UChar32) ch) & value) != 0; -} - -static UBool versionFilter(UChar32 ch, void* context) { - static const UVersionInfo none = { 0, 0, 0, 0 }; - UVersionInfo v; - u_charAge(ch, v); - UVersionInfo* version = (UVersionInfo*)context; - return uprv_memcmp(&v, &none, sizeof(v)) > 0 && uprv_memcmp(&v, version, sizeof(v)) <= 0; -} - -typedef struct { - UProperty prop; - int32_t value; -} IntPropertyContext; - -static UBool intPropertyFilter(UChar32 ch, void* context) { - IntPropertyContext* c = (IntPropertyContext*)context; - return u_getIntPropertyValue((UChar32) ch, c->prop) == c->value; -} - -static UBool scriptExtensionsFilter(UChar32 ch, void* context) { - return uscript_hasScript(ch, *(UScriptCode*)context); -} - -} // namespace - -/** - * Generic filter-based scanning code for UCD property UnicodeSets. - */ -void UnicodeSet::applyFilter(UnicodeSet::Filter filter, - void* context, - const UnicodeSet* inclusions, - UErrorCode &status) { - if (U_FAILURE(status)) return; - - // Logically, walk through all Unicode characters, noting the start - // and end of each range for which filter.contain(c) is - // true. Add each range to a set. - // - // To improve performance, use an inclusions set which - // encodes information about character ranges that are known - // to have identical properties. - // inclusions contains the first characters of - // same-value ranges for the given property. - - clear(); - - UChar32 startHasProperty = -1; - int32_t limitRange = inclusions->getRangeCount(); - - for (int j=0; jgetRangeStart(j); - UChar32 end = inclusions->getRangeEnd(j); - - // for all the code points in the range, process - for (UChar32 ch = start; ch <= end; ++ch) { - // only add to this UnicodeSet on inflection points -- - // where the hasProperty value changes to false - if ((*filter)(ch, context)) { - if (startHasProperty < 0) { - startHasProperty = ch; - } - } else if (startHasProperty >= 0) { - add(startHasProperty, ch-1); - startHasProperty = -1; - } - } - } - if (startHasProperty >= 0) { - add((UChar32)startHasProperty, (UChar32)0x10FFFF); - } - if (isBogus() && U_SUCCESS(status)) { - // We likely ran out of memory. AHHH! - status = U_MEMORY_ALLOCATION_ERROR; - } -} - -namespace { - -static UBool mungeCharName(char* dst, const char* src, int32_t dstCapacity) { - /* Note: we use ' ' in compiler code page */ - int32_t j = 0; - char ch; - --dstCapacity; /* make room for term. zero */ - while ((ch = *src++) != 0) { - if (ch == ' ' && (j==0 || (j>0 && dst[j-1]==' '))) { - continue; - } - if (j >= dstCapacity) return false; - dst[j++] = ch; - } - if (j > 0 && dst[j-1] == ' ') --j; - dst[j] = 0; - return true; -} - -} // namespace - -//---------------------------------------------------------------- -// Property set API -//---------------------------------------------------------------- - -#define FAIL(ec) UPRV_BLOCK_MACRO_BEGIN { \ - ec=U_ILLEGAL_ARGUMENT_ERROR; \ - return *this; \ -} UPRV_BLOCK_MACRO_END - -UnicodeSet& -UnicodeSet::applyIntPropertyValue(UProperty prop, int32_t value, UErrorCode& ec) { - if (U_FAILURE(ec) || isFrozen()) { return *this; } - if (prop == UCHAR_GENERAL_CATEGORY_MASK) { - const UnicodeSet* inclusions = CharacterProperties::getInclusionsForProperty(prop, ec); - applyFilter(generalCategoryMaskFilter, &value, inclusions, ec); - } else if (prop == UCHAR_SCRIPT_EXTENSIONS) { - const UnicodeSet* inclusions = CharacterProperties::getInclusionsForProperty(prop, ec); - UScriptCode script = (UScriptCode)value; - applyFilter(scriptExtensionsFilter, &script, inclusions, ec); - } else if (0 <= prop && prop < UCHAR_BINARY_LIMIT) { - if (value == 0 || value == 1) { - const USet *set = u_getBinaryPropertySet(prop, &ec); - if (U_FAILURE(ec)) { return *this; } - copyFrom(*UnicodeSet::fromUSet(set), true); - if (value == 0) { - complement().removeAllStrings(); // code point complement - } - } else { - clear(); - } - } else if (UCHAR_INT_START <= prop && prop < UCHAR_INT_LIMIT) { - const UnicodeSet* inclusions = CharacterProperties::getInclusionsForProperty(prop, ec); - IntPropertyContext c = {prop, value}; - applyFilter(intPropertyFilter, &c, inclusions, ec); - } else { - ec = U_ILLEGAL_ARGUMENT_ERROR; - } - return *this; -} - -UnicodeSet& -UnicodeSet::applyPropertyAlias(const UnicodeString& prop, - const UnicodeString& value, - UErrorCode& ec) { - if (U_FAILURE(ec) || isFrozen()) return *this; - - // prop and value used to be converted to char * using the default - // converter instead of the invariant conversion. - // This should not be necessary because all Unicode property and value - // names use only invariant characters. - // If there are any variant characters, then we won't find them anyway. - // Checking first avoids assertion failures in the conversion. - if( !uprv_isInvariantUString(prop.getBuffer(), prop.length()) || - !uprv_isInvariantUString(value.getBuffer(), value.length()) - ) { - FAIL(ec); - } - CharString pname, vname; - pname.appendInvariantChars(prop, ec); - vname.appendInvariantChars(value, ec); - if (U_FAILURE(ec)) return *this; - - UProperty p; - int32_t v; - UBool invert = false; - - if (value.length() > 0) { - p = u_getPropertyEnum(pname.data()); - if (p == UCHAR_INVALID_CODE) FAIL(ec); - - // Treat gc as gcm - if (p == UCHAR_GENERAL_CATEGORY) { - p = UCHAR_GENERAL_CATEGORY_MASK; - } - - if ((p >= UCHAR_BINARY_START && p < UCHAR_BINARY_LIMIT) || - (p >= UCHAR_INT_START && p < UCHAR_INT_LIMIT) || - (p >= UCHAR_MASK_START && p < UCHAR_MASK_LIMIT)) { - v = u_getPropertyValueEnum(p, vname.data()); - if (v == UCHAR_INVALID_CODE) { - // Handle numeric CCC - if (p == UCHAR_CANONICAL_COMBINING_CLASS || - p == UCHAR_TRAIL_CANONICAL_COMBINING_CLASS || - p == UCHAR_LEAD_CANONICAL_COMBINING_CLASS) { - char* end; - double val = uprv_strtod(vname.data(), &end); - // Anything between 0 and 255 is valid even if unused. - // Cast double->int only after range check. - // We catch NaN here because comparing it with both 0 and 255 will be false - // (as are all comparisons with NaN). - if (*end != 0 || !(0 <= val && val <= 255) || - (v = (int32_t)val) != val) { - // non-integral value or outside 0..255, or trailing junk - FAIL(ec); - } - } else { - FAIL(ec); - } - } - } - - else { - - switch (p) { - case UCHAR_NUMERIC_VALUE: - { - char* end; - double val = uprv_strtod(vname.data(), &end); - if (*end != 0) { - FAIL(ec); - } - applyFilter(numericValueFilter, &val, - CharacterProperties::getInclusionsForProperty(p, ec), ec); - return *this; - } - case UCHAR_NAME: - { - // Must munge name, since u_charFromName() does not do - // 'loose' matching. - char buf[128]; // it suffices that this be > uprv_getMaxCharNameLength - if (!mungeCharName(buf, vname.data(), sizeof(buf))) FAIL(ec); - UChar32 ch = u_charFromName(U_EXTENDED_CHAR_NAME, buf, &ec); - if (U_SUCCESS(ec)) { - clear(); - add(ch); - return *this; - } else { - FAIL(ec); - } - } - case UCHAR_UNICODE_1_NAME: - // ICU 49 deprecates the Unicode_1_Name property APIs. - FAIL(ec); - case UCHAR_AGE: - { - // Must munge name, since u_versionFromString() does not do - // 'loose' matching. - char buf[128]; - if (!mungeCharName(buf, vname.data(), sizeof(buf))) FAIL(ec); - UVersionInfo version; - u_versionFromString(version, buf); - applyFilter(versionFilter, &version, - CharacterProperties::getInclusionsForProperty(p, ec), ec); - return *this; - } - case UCHAR_SCRIPT_EXTENSIONS: - v = u_getPropertyValueEnum(UCHAR_SCRIPT, vname.data()); - if (v == UCHAR_INVALID_CODE) { - FAIL(ec); - } - // fall through to calling applyIntPropertyValue() - break; - default: - // p is a non-binary, non-enumerated property that we - // don't support (yet). - FAIL(ec); - } - } - } - - else { - // value is empty. Interpret as General Category, Script, or - // Binary property. - p = UCHAR_GENERAL_CATEGORY_MASK; - v = u_getPropertyValueEnum(p, pname.data()); - if (v == UCHAR_INVALID_CODE) { - p = UCHAR_SCRIPT; - v = u_getPropertyValueEnum(p, pname.data()); - if (v == UCHAR_INVALID_CODE) { - p = u_getPropertyEnum(pname.data()); - if (p >= UCHAR_BINARY_START && p < UCHAR_BINARY_LIMIT) { - v = 1; - } else if (0 == uprv_comparePropertyNames(ANY, pname.data())) { - set(MIN_VALUE, MAX_VALUE); - return *this; - } else if (0 == uprv_comparePropertyNames(ASCII, pname.data())) { - set(0, 0x7F); - return *this; - } else if (0 == uprv_comparePropertyNames(ASSIGNED, pname.data())) { - // [:Assigned:]=[:^Cn:] - p = UCHAR_GENERAL_CATEGORY_MASK; - v = U_GC_CN_MASK; - invert = true; - } else { - FAIL(ec); - } - } - } - } - - applyIntPropertyValue(p, v, ec); - if(invert) { - complement().removeAllStrings(); // code point complement - } - - if (isBogus() && U_SUCCESS(ec)) { - // We likely ran out of memory. AHHH! - ec = U_MEMORY_ALLOCATION_ERROR; - } - return *this; -} - -//---------------------------------------------------------------- -// Property set patterns -//---------------------------------------------------------------- - -/** - * Return true if the given position, in the given pattern, appears - * to be the start of a property set pattern. - */ -UBool UnicodeSet::resemblesPropertyPattern(const UnicodeString& pattern, - int32_t pos) { - // Patterns are at least 5 characters long - if ((pos+5) > pattern.length()) { - return false; - } - - // Look for an opening [:, [:^, \p, or \P - return isPOSIXOpen(pattern, pos) || isPerlOpen(pattern, pos) || isNameOpen(pattern, pos); -} - -/** - * Return true if the given iterator appears to point at a - * property pattern. Regardless of the result, return with the - * iterator unchanged. - * @param chars iterator over the pattern characters. Upon return - * it will be unchanged. - * @param iterOpts RuleCharacterIterator options - */ -UBool UnicodeSet::resemblesPropertyPattern(RuleCharacterIterator& chars, - int32_t iterOpts) { - // NOTE: literal will always be false, because we don't parse escapes. - UBool result = false, literal; - UErrorCode ec = U_ZERO_ERROR; - iterOpts &= ~RuleCharacterIterator::PARSE_ESCAPES; - RuleCharacterIterator::Pos pos; - chars.getPos(pos); - UChar32 c = chars.next(iterOpts, literal, ec); - if (c == u'[' || c == u'\\') { - UChar32 d = chars.next(iterOpts & ~RuleCharacterIterator::SKIP_WHITESPACE, - literal, ec); - result = (c == u'[') ? (d == u':') : - (d == u'N' || d == u'p' || d == u'P'); - } - chars.setPos(pos); - return result && U_SUCCESS(ec); -} - -/** - * Parse the given property pattern at the given parse position. - */ -UnicodeSet& UnicodeSet::applyPropertyPattern(const UnicodeString& pattern, - ParsePosition& ppos, - UErrorCode &ec) { - int32_t pos = ppos.getIndex(); - - UBool posix = false; // true for [:pat:], false for \p{pat} \P{pat} \N{pat} - UBool isName = false; // true for \N{pat}, o/w false - UBool invert = false; - - if (U_FAILURE(ec)) return *this; - - // Minimum length is 5 characters, e.g. \p{L} - if ((pos+5) > pattern.length()) { - FAIL(ec); - } - - // On entry, ppos should point to one of the following locations: - // Look for an opening [:, [:^, \p, or \P - if (isPOSIXOpen(pattern, pos)) { - posix = true; - pos += 2; - pos = ICU_Utility::skipWhitespace(pattern, pos); - if (pos < pattern.length() && pattern.charAt(pos) == u'^') { - ++pos; - invert = true; - } - } else if (isPerlOpen(pattern, pos) || isNameOpen(pattern, pos)) { - UChar c = pattern.charAt(pos+1); - invert = (c == u'P'); - isName = (c == u'N'); - pos += 2; - pos = ICU_Utility::skipWhitespace(pattern, pos); - if (pos == pattern.length() || pattern.charAt(pos++) != u'{') { - // Syntax error; "\p" or "\P" not followed by "{" - FAIL(ec); - } - } else { - // Open delimiter not seen - FAIL(ec); - } - - // Look for the matching close delimiter, either :] or } - int32_t close; - if (posix) { - close = pattern.indexOf(u":]", 2, pos); - } else { - close = pattern.indexOf(u'}', pos); - } - if (close < 0) { - // Syntax error; close delimiter missing - FAIL(ec); - } - - // Look for an '=' sign. If this is present, we will parse a - // medium \p{gc=Cf} or long \p{GeneralCategory=Format} - // pattern. - int32_t equals = pattern.indexOf(u'=', pos); - UnicodeString propName, valueName; - if (equals >= 0 && equals < close && !isName) { - // Equals seen; parse medium/long pattern - pattern.extractBetween(pos, equals, propName); - pattern.extractBetween(equals+1, close, valueName); - } - - else { - // Handle case where no '=' is seen, and \N{} - pattern.extractBetween(pos, close, propName); - - // Handle \N{name} - if (isName) { - // This is a little inefficient since it means we have to - // parse NAME_PROP back to UCHAR_NAME even though we already - // know it's UCHAR_NAME. If we refactor the API to - // support args of (UProperty, char*) then we can remove - // NAME_PROP and make this a little more efficient. - valueName = propName; - propName = UnicodeString(NAME_PROP, NAME_PROP_LENGTH, US_INV); - } - } - - applyPropertyAlias(propName, valueName, ec); - - if (U_SUCCESS(ec)) { - if (invert) { - complement().removeAllStrings(); // code point complement - } - - // Move to the limit position after the close delimiter if the - // parse succeeded. - ppos.setIndex(close + (posix ? 2 : 1)); - } - - return *this; -} - -/** - * Parse a property pattern. - * @param chars iterator over the pattern characters. Upon return - * it will be advanced to the first character after the parsed - * pattern, or the end of the iteration if all characters are - * parsed. - * @param rebuiltPat the pattern that was parsed, rebuilt or - * copied from the input pattern, as appropriate. - */ -void UnicodeSet::applyPropertyPattern(RuleCharacterIterator& chars, - UnicodeString& rebuiltPat, - UErrorCode& ec) { - if (U_FAILURE(ec)) return; - UnicodeString pattern; - chars.lookahead(pattern); - ParsePosition pos(0); - applyPropertyPattern(pattern, pos, ec); - if (U_FAILURE(ec)) return; - if (pos.getIndex() == 0) { - // syntaxError(chars, "Invalid property pattern"); - ec = U_MALFORMED_SET; - return; - } - chars.jumpahead(pos.getIndex()); - rebuiltPat.append(pattern, 0, pos.getIndex()); -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1999-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uniset_props.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004aug25 +* created by: Markus W. Scherer +* +* Character property dependent functions moved here from uniset.cpp +*/ + +#include "unicode/utypes.h" +#include "unicode/uniset.h" +#include "unicode/parsepos.h" +#include "unicode/uchar.h" +#include "unicode/uscript.h" +#include "unicode/symtable.h" +#include "unicode/uset.h" +#include "unicode/locid.h" +#include "unicode/brkiter.h" +#include "uset_imp.h" +#include "ruleiter.h" +#include "cmemory.h" +#include "ucln_cmn.h" +#include "util.h" +#include "uvector.h" +#include "uprops.h" +#include "propname.h" +#include "normalizer2impl.h" +#include "uinvchar.h" +#include "uprops.h" +#include "charstr.h" +#include "cstring.h" +#include "mutex.h" +#include "umutex.h" +#include "uassert.h" +#include "hash.h" + +U_NAMESPACE_USE + +// Special property set IDs +static const char ANY[] = "ANY"; // [\u0000-\U0010FFFF] +static const char ASCII[] = "ASCII"; // [\u0000-\u007F] +static const char ASSIGNED[] = "Assigned"; // [:^Cn:] + +// Unicode name property alias +#define NAME_PROP "na" +#define NAME_PROP_LENGTH 2 + +// Cached sets ------------------------------------------------------------- *** + +U_CDECL_BEGIN +static UBool U_CALLCONV uset_cleanup(); + +static UnicodeSet *uni32Singleton; +static icu::UInitOnce uni32InitOnce {}; + +/** + * Cleanup function for UnicodeSet + */ +static UBool U_CALLCONV uset_cleanup() { + delete uni32Singleton; + uni32Singleton = nullptr; + uni32InitOnce.reset(); + return true; +} + +U_CDECL_END + +U_NAMESPACE_BEGIN + +namespace { + +// Cache some sets for other services -------------------------------------- *** +void U_CALLCONV createUni32Set(UErrorCode &errorCode) { + U_ASSERT(uni32Singleton == nullptr); + uni32Singleton = new UnicodeSet(UNICODE_STRING_SIMPLE("[:age=3.2:]"), errorCode); + if(uni32Singleton==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } else { + uni32Singleton->freeze(); + } + ucln_common_registerCleanup(UCLN_COMMON_USET, uset_cleanup); +} + + +U_CFUNC UnicodeSet * +uniset_getUnicode32Instance(UErrorCode &errorCode) { + umtx_initOnce(uni32InitOnce, &createUni32Set, errorCode); + return uni32Singleton; +} + +// helper functions for matching of pattern syntax pieces ------------------ *** +// these functions are parallel to the PERL_OPEN etc. strings above + +// using these functions is not only faster than UnicodeString::compare() and +// caseCompare(), but they also make UnicodeSet work for simple patterns when +// no Unicode properties data is available - when caseCompare() fails + +static inline UBool +isPerlOpen(const UnicodeString &pattern, int32_t pos) { + char16_t c; + return pattern.charAt(pos)==u'\\' && ((c=pattern.charAt(pos+1))==u'p' || c==u'P'); +} + +/*static inline UBool +isPerlClose(const UnicodeString &pattern, int32_t pos) { + return pattern.charAt(pos)==u'}'; +}*/ + +static inline UBool +isNameOpen(const UnicodeString &pattern, int32_t pos) { + return pattern.charAt(pos)==u'\\' && pattern.charAt(pos+1)==u'N'; +} + +static inline UBool +isPOSIXOpen(const UnicodeString &pattern, int32_t pos) { + return pattern.charAt(pos)==u'[' && pattern.charAt(pos+1)==u':'; +} + +/*static inline UBool +isPOSIXClose(const UnicodeString &pattern, int32_t pos) { + return pattern.charAt(pos)==u':' && pattern.charAt(pos+1)==u']'; +}*/ + +// TODO memory debugging provided inside uniset.cpp +// could be made available here but probably obsolete with use of modern +// memory leak checker tools +#define _dbgct(me) + +} // namespace + +//---------------------------------------------------------------- +// Constructors &c +//---------------------------------------------------------------- + +/** + * Constructs a set from the given pattern, optionally ignoring + * white space. See the class description for the syntax of the + * pattern language. + * @param pattern a string specifying what characters are in the set + */ +UnicodeSet::UnicodeSet(const UnicodeString& pattern, + UErrorCode& status) { + applyPattern(pattern, status); + _dbgct(this); +} + +//---------------------------------------------------------------- +// Public API +//---------------------------------------------------------------- + +UnicodeSet& UnicodeSet::applyPattern(const UnicodeString& pattern, + UErrorCode& status) { + // Equivalent to + // return applyPattern(pattern, USET_IGNORE_SPACE, nullptr, status); + // but without dependency on closeOver(). + ParsePosition pos(0); + applyPatternIgnoreSpace(pattern, pos, nullptr, status); + if (U_FAILURE(status)) return *this; + + int32_t i = pos.getIndex(); + // Skip over trailing whitespace + ICU_Utility::skipWhitespace(pattern, i, true); + if (i != pattern.length()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } + return *this; +} + +void +UnicodeSet::applyPatternIgnoreSpace(const UnicodeString& pattern, + ParsePosition& pos, + const SymbolTable* symbols, + UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (isFrozen()) { + status = U_NO_WRITE_PERMISSION; + return; + } + // Need to build the pattern in a temporary string because + // _applyPattern calls add() etc., which set pat to empty. + UnicodeString rebuiltPat; + RuleCharacterIterator chars(pattern, symbols, pos); + applyPattern(chars, symbols, rebuiltPat, USET_IGNORE_SPACE, nullptr, 0, status); + if (U_FAILURE(status)) return; + if (chars.inVariable()) { + // syntaxError(chars, "Extra chars in variable value"); + status = U_MALFORMED_SET; + return; + } + setPattern(rebuiltPat); +} + +/** + * Return true if the given position, in the given pattern, appears + * to be the start of a UnicodeSet pattern. + */ +UBool UnicodeSet::resemblesPattern(const UnicodeString& pattern, int32_t pos) { + return ((pos+1) < pattern.length() && + pattern.charAt(pos) == (char16_t)91/*[*/) || + resemblesPropertyPattern(pattern, pos); +} + +//---------------------------------------------------------------- +// Implementation: Pattern parsing +//---------------------------------------------------------------- + +namespace { + +/** + * A small all-inline class to manage a UnicodeSet pointer. Add + * operator->() etc. as needed. + */ +class UnicodeSetPointer { + UnicodeSet* p; +public: + inline UnicodeSetPointer() : p(0) {} + inline ~UnicodeSetPointer() { delete p; } + inline UnicodeSet* pointer() { return p; } + inline UBool allocate() { + if (p == 0) { + p = new UnicodeSet(); + } + return p != 0; + } +}; + +constexpr int32_t MAX_DEPTH = 100; + +} // namespace + +/** + * Parse the pattern from the given RuleCharacterIterator. The + * iterator is advanced over the parsed pattern. + * @param chars iterator over the pattern characters. Upon return + * it will be advanced to the first character after the parsed + * pattern, or the end of the iteration if all characters are + * parsed. + * @param symbols symbol table to use to parse and dereference + * variables, or null if none. + * @param rebuiltPat the pattern that was parsed, rebuilt or + * copied from the input pattern, as appropriate. + * @param options a bit mask of zero or more of the following: + * IGNORE_SPACE, CASE. + */ +void UnicodeSet::applyPattern(RuleCharacterIterator& chars, + const SymbolTable* symbols, + UnicodeString& rebuiltPat, + uint32_t options, + UnicodeSet& (UnicodeSet::*caseClosure)(int32_t attribute), + int32_t depth, + UErrorCode& ec) { + if (U_FAILURE(ec)) return; + if (depth > MAX_DEPTH) { + ec = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + // Syntax characters: [ ] ^ - & { } + + // Recognized special forms for chars, sets: c-c s-s s&s + + int32_t opts = RuleCharacterIterator::PARSE_VARIABLES | + RuleCharacterIterator::PARSE_ESCAPES; + if ((options & USET_IGNORE_SPACE) != 0) { + opts |= RuleCharacterIterator::SKIP_WHITESPACE; + } + + UnicodeString patLocal, buf; + UBool usePat = false; + UnicodeSetPointer scratch; + RuleCharacterIterator::Pos backup; + + // mode: 0=before [, 1=between [...], 2=after ] + // lastItem: 0=none, 1=char, 2=set + int8_t lastItem = 0, mode = 0; + UChar32 lastChar = 0; + char16_t op = 0; + + UBool invert = false; + + clear(); + + while (mode != 2 && !chars.atEnd()) { + U_ASSERT((lastItem == 0 && op == 0) || + (lastItem == 1 && (op == 0 || op == u'-')) || + (lastItem == 2 && (op == 0 || op == u'-' || op == u'&'))); + + UChar32 c = 0; + UBool literal = false; + UnicodeSet* nested = 0; // alias - do not delete + + // -------- Check for property pattern + + // setMode: 0=none, 1=unicodeset, 2=propertypat, 3=preparsed + int8_t setMode = 0; + if (resemblesPropertyPattern(chars, opts)) { + setMode = 2; + } + + // -------- Parse '[' of opening delimiter OR nested set. + // If there is a nested set, use `setMode' to define how + // the set should be parsed. If the '[' is part of the + // opening delimiter for this pattern, parse special + // strings "[", "[^", "[-", and "[^-". Check for stand-in + // characters representing a nested set in the symbol + // table. + + else { + // Prepare to backup if necessary + chars.getPos(backup); + c = chars.next(opts, literal, ec); + if (U_FAILURE(ec)) return; + + if (c == u'[' && !literal) { + if (mode == 1) { + chars.setPos(backup); // backup + setMode = 1; + } else { + // Handle opening '[' delimiter + mode = 1; + patLocal.append(u'['); + chars.getPos(backup); // prepare to backup + c = chars.next(opts, literal, ec); + if (U_FAILURE(ec)) return; + if (c == u'^' && !literal) { + invert = true; + patLocal.append(u'^'); + chars.getPos(backup); // prepare to backup + c = chars.next(opts, literal, ec); + if (U_FAILURE(ec)) return; + } + // Fall through to handle special leading '-'; + // otherwise restart loop for nested [], \p{}, etc. + if (c == u'-') { + literal = true; + // Fall through to handle literal '-' below + } else { + chars.setPos(backup); // backup + continue; + } + } + } else if (symbols != 0) { + const UnicodeFunctor *m = symbols->lookupMatcher(c); + if (m != 0) { + const UnicodeSet *ms = dynamic_cast(m); + if (ms == nullptr) { + ec = U_MALFORMED_SET; + return; + } + // casting away const, but `nested' won't be modified + // (important not to modify stored set) + nested = const_cast(ms); + setMode = 3; + } + } + } + + // -------- Handle a nested set. This either is inline in + // the pattern or represented by a stand-in that has + // previously been parsed and was looked up in the symbol + // table. + + if (setMode != 0) { + if (lastItem == 1) { + if (op != 0) { + // syntaxError(chars, "Char expected after operator"); + ec = U_MALFORMED_SET; + return; + } + add(lastChar, lastChar); + _appendToPat(patLocal, lastChar, false); + lastItem = 0; + op = 0; + } + + if (op == u'-' || op == u'&') { + patLocal.append(op); + } + + if (nested == 0) { + // lazy allocation + if (!scratch.allocate()) { + ec = U_MEMORY_ALLOCATION_ERROR; + return; + } + nested = scratch.pointer(); + } + switch (setMode) { + case 1: + nested->applyPattern(chars, symbols, patLocal, options, caseClosure, depth + 1, ec); + break; + case 2: + chars.skipIgnored(opts); + nested->applyPropertyPattern(chars, patLocal, ec); + if (U_FAILURE(ec)) return; + break; + case 3: // `nested' already parsed + nested->_toPattern(patLocal, false); + break; + } + + usePat = true; + + if (mode == 0) { + // Entire pattern is a category; leave parse loop + *this = *nested; + mode = 2; + break; + } + + switch (op) { + case u'-': + removeAll(*nested); + break; + case u'&': + retainAll(*nested); + break; + case 0: + addAll(*nested); + break; + } + + op = 0; + lastItem = 2; + + continue; + } + + if (mode == 0) { + // syntaxError(chars, "Missing '['"); + ec = U_MALFORMED_SET; + return; + } + + // -------- Parse special (syntax) characters. If the + // current character is not special, or if it is escaped, + // then fall through and handle it below. + + if (!literal) { + switch (c) { + case u']': + if (lastItem == 1) { + add(lastChar, lastChar); + _appendToPat(patLocal, lastChar, false); + } + // Treat final trailing '-' as a literal + if (op == u'-') { + add(op, op); + patLocal.append(op); + } else if (op == u'&') { + // syntaxError(chars, "Trailing '&'"); + ec = U_MALFORMED_SET; + return; + } + patLocal.append(u']'); + mode = 2; + continue; + case u'-': + if (op == 0) { + if (lastItem != 0) { + op = (char16_t) c; + continue; + } else { + // Treat final trailing '-' as a literal + add(c, c); + c = chars.next(opts, literal, ec); + if (U_FAILURE(ec)) return; + if (c == u']' && !literal) { + patLocal.append(u"-]", 2); + mode = 2; + continue; + } + } + } + // syntaxError(chars, "'-' not after char or set"); + ec = U_MALFORMED_SET; + return; + case u'&': + if (lastItem == 2 && op == 0) { + op = (char16_t) c; + continue; + } + // syntaxError(chars, "'&' not after set"); + ec = U_MALFORMED_SET; + return; + case u'^': + // syntaxError(chars, "'^' not after '['"); + ec = U_MALFORMED_SET; + return; + case u'{': + if (op != 0) { + // syntaxError(chars, "Missing operand after operator"); + ec = U_MALFORMED_SET; + return; + } + if (lastItem == 1) { + add(lastChar, lastChar); + _appendToPat(patLocal, lastChar, false); + } + lastItem = 0; + buf.truncate(0); + { + UBool ok = false; + while (!chars.atEnd()) { + c = chars.next(opts, literal, ec); + if (U_FAILURE(ec)) return; + if (c == u'}' && !literal) { + ok = true; + break; + } + buf.append(c); + } + if (!ok) { + // syntaxError(chars, "Invalid multicharacter string"); + ec = U_MALFORMED_SET; + return; + } + } + // We have new string. Add it to set and continue; + // we don't need to drop through to the further + // processing + add(buf); + patLocal.append(u'{'); + _appendToPat(patLocal, buf, false); + patLocal.append(u'}'); + continue; + case SymbolTable::SYMBOL_REF: + // symbols nosymbols + // [a-$] error error (ambiguous) + // [a$] anchor anchor + // [a-$x] var "x"* literal '$' + // [a-$.] error literal '$' + // *We won't get here in the case of var "x" + { + chars.getPos(backup); + c = chars.next(opts, literal, ec); + if (U_FAILURE(ec)) return; + UBool anchor = (c == u']' && !literal); + if (symbols == 0 && !anchor) { + c = SymbolTable::SYMBOL_REF; + chars.setPos(backup); + break; // literal '$' + } + if (anchor && op == 0) { + if (lastItem == 1) { + add(lastChar, lastChar); + _appendToPat(patLocal, lastChar, false); + } + add(U_ETHER); + usePat = true; + patLocal.append((char16_t) SymbolTable::SYMBOL_REF); + patLocal.append(u']'); + mode = 2; + continue; + } + // syntaxError(chars, "Unquoted '$'"); + ec = U_MALFORMED_SET; + return; + } + default: + break; + } + } + + // -------- Parse literal characters. This includes both + // escaped chars ("\u4E01") and non-syntax characters + // ("a"). + + switch (lastItem) { + case 0: + lastItem = 1; + lastChar = c; + break; + case 1: + if (op == u'-') { + if (lastChar >= c) { + // Don't allow redundant (a-a) or empty (b-a) ranges; + // these are most likely typos. + // syntaxError(chars, "Invalid range"); + ec = U_MALFORMED_SET; + return; + } + add(lastChar, c); + _appendToPat(patLocal, lastChar, false); + patLocal.append(op); + _appendToPat(patLocal, c, false); + lastItem = 0; + op = 0; + } else { + add(lastChar, lastChar); + _appendToPat(patLocal, lastChar, false); + lastChar = c; + } + break; + case 2: + if (op != 0) { + // syntaxError(chars, "Set expected after operator"); + ec = U_MALFORMED_SET; + return; + } + lastChar = c; + lastItem = 1; + break; + } + } + + if (mode != 2) { + // syntaxError(chars, "Missing ']'"); + ec = U_MALFORMED_SET; + return; + } + + chars.skipIgnored(opts); + + /** + * Handle global flags (invert, case insensitivity). If this + * pattern should be compiled case-insensitive, then we need + * to close over case BEFORE COMPLEMENTING. This makes + * patterns like /[^abc]/i work. + */ + if ((options & USET_CASE_MASK) != 0) { + (this->*caseClosure)(options); + } + if (invert) { + complement().removeAllStrings(); // code point complement + } + + // Use the rebuilt pattern (patLocal) only if necessary. Prefer the + // generated pattern. + if (usePat) { + rebuiltPat.append(patLocal); + } else { + _generatePattern(rebuiltPat, false); + } + if (isBogus() && U_SUCCESS(ec)) { + // We likely ran out of memory. AHHH! + ec = U_MEMORY_ALLOCATION_ERROR; + } +} + +//---------------------------------------------------------------- +// Property set implementation +//---------------------------------------------------------------- + +namespace { + +static UBool numericValueFilter(UChar32 ch, void* context) { + return u_getNumericValue(ch) == *(double*)context; +} + +static UBool generalCategoryMaskFilter(UChar32 ch, void* context) { + int32_t value = *(int32_t*)context; + return (U_GET_GC_MASK((UChar32) ch) & value) != 0; +} + +static UBool versionFilter(UChar32 ch, void* context) { + static const UVersionInfo none = { 0, 0, 0, 0 }; + UVersionInfo v; + u_charAge(ch, v); + UVersionInfo* version = (UVersionInfo*)context; + return uprv_memcmp(&v, &none, sizeof(v)) > 0 && uprv_memcmp(&v, version, sizeof(v)) <= 0; +} + +typedef struct { + UProperty prop; + int32_t value; +} IntPropertyContext; + +static UBool intPropertyFilter(UChar32 ch, void* context) { + IntPropertyContext* c = (IntPropertyContext*)context; + return u_getIntPropertyValue((UChar32) ch, c->prop) == c->value; +} + +static UBool scriptExtensionsFilter(UChar32 ch, void* context) { + return uscript_hasScript(ch, *(UScriptCode*)context); +} + +} // namespace + +/** + * Generic filter-based scanning code for UCD property UnicodeSets. + */ +void UnicodeSet::applyFilter(UnicodeSet::Filter filter, + void* context, + const UnicodeSet* inclusions, + UErrorCode &status) { + if (U_FAILURE(status)) return; + + // Logically, walk through all Unicode characters, noting the start + // and end of each range for which filter.contain(c) is + // true. Add each range to a set. + // + // To improve performance, use an inclusions set which + // encodes information about character ranges that are known + // to have identical properties. + // inclusions contains the first characters of + // same-value ranges for the given property. + + clear(); + + UChar32 startHasProperty = -1; + int32_t limitRange = inclusions->getRangeCount(); + + for (int j=0; jgetRangeStart(j); + UChar32 end = inclusions->getRangeEnd(j); + + // for all the code points in the range, process + for (UChar32 ch = start; ch <= end; ++ch) { + // only add to this UnicodeSet on inflection points -- + // where the hasProperty value changes to false + if ((*filter)(ch, context)) { + if (startHasProperty < 0) { + startHasProperty = ch; + } + } else if (startHasProperty >= 0) { + add(startHasProperty, ch-1); + startHasProperty = -1; + } + } + } + if (startHasProperty >= 0) { + add((UChar32)startHasProperty, (UChar32)0x10FFFF); + } + if (isBogus() && U_SUCCESS(status)) { + // We likely ran out of memory. AHHH! + status = U_MEMORY_ALLOCATION_ERROR; + } +} + +namespace { + +static UBool mungeCharName(char* dst, const char* src, int32_t dstCapacity) { + /* Note: we use ' ' in compiler code page */ + int32_t j = 0; + char ch; + --dstCapacity; /* make room for term. zero */ + while ((ch = *src++) != 0) { + if (ch == ' ' && (j==0 || (j>0 && dst[j-1]==' '))) { + continue; + } + if (j >= dstCapacity) return false; + dst[j++] = ch; + } + if (j > 0 && dst[j-1] == ' ') --j; + dst[j] = 0; + return true; +} + +} // namespace + +//---------------------------------------------------------------- +// Property set API +//---------------------------------------------------------------- + +#define FAIL(ec) UPRV_BLOCK_MACRO_BEGIN { \ + ec=U_ILLEGAL_ARGUMENT_ERROR; \ + return *this; \ +} UPRV_BLOCK_MACRO_END + +UnicodeSet& +UnicodeSet::applyIntPropertyValue(UProperty prop, int32_t value, UErrorCode& ec) { + if (U_FAILURE(ec) || isFrozen()) { return *this; } + if (prop == UCHAR_GENERAL_CATEGORY_MASK) { + const UnicodeSet* inclusions = CharacterProperties::getInclusionsForProperty(prop, ec); + applyFilter(generalCategoryMaskFilter, &value, inclusions, ec); + } else if (prop == UCHAR_SCRIPT_EXTENSIONS) { + const UnicodeSet* inclusions = CharacterProperties::getInclusionsForProperty(prop, ec); + UScriptCode script = (UScriptCode)value; + applyFilter(scriptExtensionsFilter, &script, inclusions, ec); + } else if (0 <= prop && prop < UCHAR_BINARY_LIMIT) { + if (value == 0 || value == 1) { + const USet *set = u_getBinaryPropertySet(prop, &ec); + if (U_FAILURE(ec)) { return *this; } + copyFrom(*UnicodeSet::fromUSet(set), true); + if (value == 0) { + complement().removeAllStrings(); // code point complement + } + } else { + clear(); + } + } else if (UCHAR_INT_START <= prop && prop < UCHAR_INT_LIMIT) { + const UnicodeSet* inclusions = CharacterProperties::getInclusionsForProperty(prop, ec); + IntPropertyContext c = {prop, value}; + applyFilter(intPropertyFilter, &c, inclusions, ec); + } else { + ec = U_ILLEGAL_ARGUMENT_ERROR; + } + return *this; +} + +UnicodeSet& +UnicodeSet::applyPropertyAlias(const UnicodeString& prop, + const UnicodeString& value, + UErrorCode& ec) { + if (U_FAILURE(ec) || isFrozen()) return *this; + + // prop and value used to be converted to char * using the default + // converter instead of the invariant conversion. + // This should not be necessary because all Unicode property and value + // names use only invariant characters. + // If there are any variant characters, then we won't find them anyway. + // Checking first avoids assertion failures in the conversion. + if( !uprv_isInvariantUString(prop.getBuffer(), prop.length()) || + !uprv_isInvariantUString(value.getBuffer(), value.length()) + ) { + FAIL(ec); + } + CharString pname, vname; + pname.appendInvariantChars(prop, ec); + vname.appendInvariantChars(value, ec); + if (U_FAILURE(ec)) return *this; + + UProperty p; + int32_t v; + UBool invert = false; + + if (value.length() > 0) { + p = u_getPropertyEnum(pname.data()); + if (p == UCHAR_INVALID_CODE) FAIL(ec); + + // Treat gc as gcm + if (p == UCHAR_GENERAL_CATEGORY) { + p = UCHAR_GENERAL_CATEGORY_MASK; + } + + if ((p >= UCHAR_BINARY_START && p < UCHAR_BINARY_LIMIT) || + (p >= UCHAR_INT_START && p < UCHAR_INT_LIMIT) || + (p >= UCHAR_MASK_START && p < UCHAR_MASK_LIMIT)) { + v = u_getPropertyValueEnum(p, vname.data()); + if (v == UCHAR_INVALID_CODE) { + // Handle numeric CCC + if (p == UCHAR_CANONICAL_COMBINING_CLASS || + p == UCHAR_TRAIL_CANONICAL_COMBINING_CLASS || + p == UCHAR_LEAD_CANONICAL_COMBINING_CLASS) { + char* end; + double val = uprv_strtod(vname.data(), &end); + // Anything between 0 and 255 is valid even if unused. + // Cast double->int only after range check. + // We catch NaN here because comparing it with both 0 and 255 will be false + // (as are all comparisons with NaN). + if (*end != 0 || !(0 <= val && val <= 255) || + (v = (int32_t)val) != val) { + // non-integral value or outside 0..255, or trailing junk + FAIL(ec); + } + } else { + FAIL(ec); + } + } + } + + else { + + switch (p) { + case UCHAR_NUMERIC_VALUE: + { + char* end; + double val = uprv_strtod(vname.data(), &end); + if (*end != 0) { + FAIL(ec); + } + applyFilter(numericValueFilter, &val, + CharacterProperties::getInclusionsForProperty(p, ec), ec); + return *this; + } + case UCHAR_NAME: + { + // Must munge name, since u_charFromName() does not do + // 'loose' matching. + char buf[128]; // it suffices that this be > uprv_getMaxCharNameLength + if (!mungeCharName(buf, vname.data(), sizeof(buf))) FAIL(ec); + UChar32 ch = u_charFromName(U_EXTENDED_CHAR_NAME, buf, &ec); + if (U_SUCCESS(ec)) { + clear(); + add(ch); + return *this; + } else { + FAIL(ec); + } + } + case UCHAR_UNICODE_1_NAME: + // ICU 49 deprecates the Unicode_1_Name property APIs. + FAIL(ec); + case UCHAR_AGE: + { + // Must munge name, since u_versionFromString() does not do + // 'loose' matching. + char buf[128]; + if (!mungeCharName(buf, vname.data(), sizeof(buf))) FAIL(ec); + UVersionInfo version; + u_versionFromString(version, buf); + applyFilter(versionFilter, &version, + CharacterProperties::getInclusionsForProperty(p, ec), ec); + return *this; + } + case UCHAR_SCRIPT_EXTENSIONS: + v = u_getPropertyValueEnum(UCHAR_SCRIPT, vname.data()); + if (v == UCHAR_INVALID_CODE) { + FAIL(ec); + } + // fall through to calling applyIntPropertyValue() + break; + default: + // p is a non-binary, non-enumerated property that we + // don't support (yet). + FAIL(ec); + } + } + } + + else { + // value is empty. Interpret as General Category, Script, or + // Binary property. + p = UCHAR_GENERAL_CATEGORY_MASK; + v = u_getPropertyValueEnum(p, pname.data()); + if (v == UCHAR_INVALID_CODE) { + p = UCHAR_SCRIPT; + v = u_getPropertyValueEnum(p, pname.data()); + if (v == UCHAR_INVALID_CODE) { + p = u_getPropertyEnum(pname.data()); + if (p >= UCHAR_BINARY_START && p < UCHAR_BINARY_LIMIT) { + v = 1; + } else if (0 == uprv_comparePropertyNames(ANY, pname.data())) { + set(MIN_VALUE, MAX_VALUE); + return *this; + } else if (0 == uprv_comparePropertyNames(ASCII, pname.data())) { + set(0, 0x7F); + return *this; + } else if (0 == uprv_comparePropertyNames(ASSIGNED, pname.data())) { + // [:Assigned:]=[:^Cn:] + p = UCHAR_GENERAL_CATEGORY_MASK; + v = U_GC_CN_MASK; + invert = true; + } else { + FAIL(ec); + } + } + } + } + + applyIntPropertyValue(p, v, ec); + if(invert) { + complement().removeAllStrings(); // code point complement + } + + if (isBogus() && U_SUCCESS(ec)) { + // We likely ran out of memory. AHHH! + ec = U_MEMORY_ALLOCATION_ERROR; + } + return *this; +} + +//---------------------------------------------------------------- +// Property set patterns +//---------------------------------------------------------------- + +/** + * Return true if the given position, in the given pattern, appears + * to be the start of a property set pattern. + */ +UBool UnicodeSet::resemblesPropertyPattern(const UnicodeString& pattern, + int32_t pos) { + // Patterns are at least 5 characters long + if ((pos+5) > pattern.length()) { + return false; + } + + // Look for an opening [:, [:^, \p, or \P + return isPOSIXOpen(pattern, pos) || isPerlOpen(pattern, pos) || isNameOpen(pattern, pos); +} + +/** + * Return true if the given iterator appears to point at a + * property pattern. Regardless of the result, return with the + * iterator unchanged. + * @param chars iterator over the pattern characters. Upon return + * it will be unchanged. + * @param iterOpts RuleCharacterIterator options + */ +UBool UnicodeSet::resemblesPropertyPattern(RuleCharacterIterator& chars, + int32_t iterOpts) { + // NOTE: literal will always be false, because we don't parse escapes. + UBool result = false, literal; + UErrorCode ec = U_ZERO_ERROR; + iterOpts &= ~RuleCharacterIterator::PARSE_ESCAPES; + RuleCharacterIterator::Pos pos; + chars.getPos(pos); + UChar32 c = chars.next(iterOpts, literal, ec); + if (c == u'[' || c == u'\\') { + UChar32 d = chars.next(iterOpts & ~RuleCharacterIterator::SKIP_WHITESPACE, + literal, ec); + result = (c == u'[') ? (d == u':') : + (d == u'N' || d == u'p' || d == u'P'); + } + chars.setPos(pos); + return result && U_SUCCESS(ec); +} + +/** + * Parse the given property pattern at the given parse position. + */ +UnicodeSet& UnicodeSet::applyPropertyPattern(const UnicodeString& pattern, + ParsePosition& ppos, + UErrorCode &ec) { + int32_t pos = ppos.getIndex(); + + UBool posix = false; // true for [:pat:], false for \p{pat} \P{pat} \N{pat} + UBool isName = false; // true for \N{pat}, o/w false + UBool invert = false; + + if (U_FAILURE(ec)) return *this; + + // Minimum length is 5 characters, e.g. \p{L} + if ((pos+5) > pattern.length()) { + FAIL(ec); + } + + // On entry, ppos should point to one of the following locations: + // Look for an opening [:, [:^, \p, or \P + if (isPOSIXOpen(pattern, pos)) { + posix = true; + pos += 2; + pos = ICU_Utility::skipWhitespace(pattern, pos); + if (pos < pattern.length() && pattern.charAt(pos) == u'^') { + ++pos; + invert = true; + } + } else if (isPerlOpen(pattern, pos) || isNameOpen(pattern, pos)) { + char16_t c = pattern.charAt(pos+1); + invert = (c == u'P'); + isName = (c == u'N'); + pos += 2; + pos = ICU_Utility::skipWhitespace(pattern, pos); + if (pos == pattern.length() || pattern.charAt(pos++) != u'{') { + // Syntax error; "\p" or "\P" not followed by "{" + FAIL(ec); + } + } else { + // Open delimiter not seen + FAIL(ec); + } + + // Look for the matching close delimiter, either :] or } + int32_t close; + if (posix) { + close = pattern.indexOf(u":]", 2, pos); + } else { + close = pattern.indexOf(u'}', pos); + } + if (close < 0) { + // Syntax error; close delimiter missing + FAIL(ec); + } + + // Look for an '=' sign. If this is present, we will parse a + // medium \p{gc=Cf} or long \p{GeneralCategory=Format} + // pattern. + int32_t equals = pattern.indexOf(u'=', pos); + UnicodeString propName, valueName; + if (equals >= 0 && equals < close && !isName) { + // Equals seen; parse medium/long pattern + pattern.extractBetween(pos, equals, propName); + pattern.extractBetween(equals+1, close, valueName); + } + + else { + // Handle case where no '=' is seen, and \N{} + pattern.extractBetween(pos, close, propName); + + // Handle \N{name} + if (isName) { + // This is a little inefficient since it means we have to + // parse NAME_PROP back to UCHAR_NAME even though we already + // know it's UCHAR_NAME. If we refactor the API to + // support args of (UProperty, char*) then we can remove + // NAME_PROP and make this a little more efficient. + valueName = propName; + propName = UnicodeString(NAME_PROP, NAME_PROP_LENGTH, US_INV); + } + } + + applyPropertyAlias(propName, valueName, ec); + + if (U_SUCCESS(ec)) { + if (invert) { + complement().removeAllStrings(); // code point complement + } + + // Move to the limit position after the close delimiter if the + // parse succeeded. + ppos.setIndex(close + (posix ? 2 : 1)); + } + + return *this; +} + +/** + * Parse a property pattern. + * @param chars iterator over the pattern characters. Upon return + * it will be advanced to the first character after the parsed + * pattern, or the end of the iteration if all characters are + * parsed. + * @param rebuiltPat the pattern that was parsed, rebuilt or + * copied from the input pattern, as appropriate. + */ +void UnicodeSet::applyPropertyPattern(RuleCharacterIterator& chars, + UnicodeString& rebuiltPat, + UErrorCode& ec) { + if (U_FAILURE(ec)) return; + UnicodeString pattern; + chars.lookahead(pattern); + ParsePosition pos(0); + applyPropertyPattern(pattern, pos, ec); + if (U_FAILURE(ec)) return; + if (pos.getIndex() == 0) { + // syntaxError(chars, "Invalid property pattern"); + ec = U_MALFORMED_SET; + return; + } + chars.jumpahead(pos.getIndex()); + rebuiltPat.append(pattern, 0, pos.getIndex()); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/unisetspan.cpp b/deps/icu-small/source/common/unisetspan.cpp index e4277c5be60697..dd72c5098f62ad 100644 --- a/deps/icu-small/source/common/unisetspan.cpp +++ b/deps/icu-small/source/common/unisetspan.cpp @@ -1,1522 +1,1522 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2007-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: unisetspan.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2007mar01 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" -#include "unicode/uniset.h" -#include "unicode/ustring.h" -#include "unicode/utf8.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "uvector.h" -#include "unisetspan.h" - -U_NAMESPACE_BEGIN - -/* - * List of offsets from the current position from where to try matching - * a code point or a string. - * Store offsets rather than indexes to simplify the code and use the same list - * for both increments (in span()) and decrements (in spanBack()). - * - * Assumption: The maximum offset is limited, and the offsets that are stored - * at any one time are relatively dense, that is, there are normally no gaps of - * hundreds or thousands of offset values. - * - * The implementation uses a circular buffer of byte flags, - * each indicating whether the corresponding offset is in the list. - * This avoids inserting into a sorted list of offsets (or absolute indexes) and - * physically moving part of the list. - * - * Note: In principle, the caller should setMaxLength() to the maximum of the - * max string length and U16_LENGTH/U8_LENGTH to account for - * "long" single code points. - * However, this implementation uses at least a staticList with more than - * U8_LENGTH entries anyway. - * - * Note: If maxLength were guaranteed to be no more than 32 or 64, - * the list could be stored as bit flags in a single integer. - * Rather than handling a circular buffer with a start list index, - * the integer would simply be shifted when lower offsets are removed. - * UnicodeSet does not have a limit on the lengths of strings. - */ -class OffsetList { // Only ever stack-allocated, does not need to inherit UMemory. -public: - OffsetList() : list(staticList), capacity(0), length(0), start(0) {} - - ~OffsetList() { - if(list!=staticList) { - uprv_free(list); - } - } - - // Call exactly once if the list is to be used. - void setMaxLength(int32_t maxLength) { - if(maxLength<=(int32_t)sizeof(staticList)) { - capacity=(int32_t)sizeof(staticList); - } else { - UBool *l=(UBool *)uprv_malloc(maxLength); - if(l!=NULL) { - list=l; - capacity=maxLength; - } - } - uprv_memset(list, 0, capacity); - } - - void clear() { - uprv_memset(list, 0, capacity); - start=length=0; - } - - UBool isEmpty() const { - return (UBool)(length==0); - } - - // Reduce all stored offsets by delta, used when the current position - // moves by delta. - // There must not be any offsets lower than delta. - // If there is an offset equal to delta, it is removed. - // delta=[1..maxLength] - void shift(int32_t delta) { - int32_t i=start+delta; - if(i>=capacity) { - i-=capacity; - } - if(list[i]) { - list[i]=false; - --length; - } - start=i; - } - - // Add an offset. The list must not contain it yet. - // offset=[1..maxLength] - void addOffset(int32_t offset) { - int32_t i=start+offset; - if(i>=capacity) { - i-=capacity; - } - list[i]=true; - ++length; - } - - // offset=[1..maxLength] - UBool containsOffset(int32_t offset) const { - int32_t i=start+offset; - if(i>=capacity) { - i-=capacity; - } - return list[i]; - } - - // Find the lowest stored offset from a non-empty list, remove it, - // and reduce all other offsets by this minimum. - // Returns [1..maxLength]. - int32_t popMinimum() { - // Look for the next offset in list[start+1..capacity-1]. - int32_t i=start, result; - while(++imaxLength16) { - maxLength16=length16; - } - if((which&UTF8) && (thisRelevant || (which&CONTAINED))) { - int32_t length8=getUTF8Length(s16, length16); - utf8Length+=length8; - if(length8>maxLength8) { - maxLength8=length8; - } - } - } - if(!someRelevant) { - maxLength16=maxLength8=0; - return; - } - - // Freeze after checking for the need to use strings at all because freezing - // a set takes some time and memory which are wasted if there are no relevant strings. - if(all) { - spanSet.freeze(); - } - - uint8_t *spanBackLengths; - uint8_t *spanUTF8Lengths; - uint8_t *spanBackUTF8Lengths; - - // Allocate a block of meta data. - int32_t allocSize; - if(all) { - // UTF-8 lengths, 4 sets of span lengths, UTF-8 strings. - allocSize=stringsLength*(4+1+1+1+1)+utf8Length; - } else { - allocSize=stringsLength; // One set of span lengths. - if(which&UTF8) { - // UTF-8 lengths and UTF-8 strings. - allocSize+=stringsLength*4+utf8Length; - } - } - if(allocSize<=(int32_t)sizeof(staticLengths)) { - utf8Lengths=staticLengths; - } else { - utf8Lengths=(int32_t *)uprv_malloc(allocSize); - if(utf8Lengths==NULL) { - maxLength16=maxLength8=0; // Prevent usage by making needsStringSpanUTF16/8() return false. - return; // Out of memory. - } - } - - if(all) { - // Store span lengths for all span() variants. - spanLengths=(uint8_t *)(utf8Lengths+stringsLength); - spanBackLengths=spanLengths+stringsLength; - spanUTF8Lengths=spanBackLengths+stringsLength; - spanBackUTF8Lengths=spanUTF8Lengths+stringsLength; - utf8=spanBackUTF8Lengths+stringsLength; - } else { - // Store span lengths for only one span() variant. - if(which&UTF8) { - spanLengths=(uint8_t *)(utf8Lengths+stringsLength); - utf8=spanLengths+stringsLength; - } else { - spanLengths=(uint8_t *)utf8Lengths; - } - spanBackLengths=spanUTF8Lengths=spanBackUTF8Lengths=spanLengths; - } - - // Set the meta data and pSpanNotSet and write the UTF-8 strings. - int32_t utf8Count=0; // Count UTF-8 bytes written so far. - - for(i=0; i0) { // Relevant string. - if(which&UTF16) { - if(which&CONTAINED) { - if(which&FWD) { - spanLengths[i]=makeSpanLengthByte(spanLength); - } - if(which&BACK) { - spanLength=length16-spanSet.spanBack(s16, length16, USET_SPAN_CONTAINED); - spanBackLengths[i]=makeSpanLengthByte(spanLength); - } - } else /* not CONTAINED, not all, but NOT_CONTAINED */ { - spanLengths[i]=spanBackLengths[i]=0; // Only store a relevant/irrelevant flag. - } - } - if(which&UTF8) { - uint8_t *s8=utf8+utf8Count; - int32_t length8=appendUTF8(s16, length16, s8, utf8Length-utf8Count); - utf8Count+=utf8Lengths[i]=length8; - if(length8==0) { // Irrelevant for UTF-8 because not representable in UTF-8. - spanUTF8Lengths[i]=spanBackUTF8Lengths[i]=(uint8_t)ALL_CP_CONTAINED; - } else { // Relevant for UTF-8. - if(which&CONTAINED) { - if(which&FWD) { - spanLength=spanSet.spanUTF8((const char *)s8, length8, USET_SPAN_CONTAINED); - spanUTF8Lengths[i]=makeSpanLengthByte(spanLength); - } - if(which&BACK) { - spanLength=length8-spanSet.spanBackUTF8((const char *)s8, length8, USET_SPAN_CONTAINED); - spanBackUTF8Lengths[i]=makeSpanLengthByte(spanLength); - } - } else /* not CONTAINED, not all, but NOT_CONTAINED */ { - spanUTF8Lengths[i]=spanBackUTF8Lengths[i]=0; // Only store a relevant/irrelevant flag. - } - } - } - if(which&NOT_CONTAINED) { - // Add string start and end code points to the spanNotSet so that - // a span(while not contained) stops before any string. - UChar32 c; - if(which&FWD) { - int32_t len=0; - U16_NEXT(s16, len, length16, c); - addToSpanNotSet(c); - } - if(which&BACK) { - int32_t len=length16; - U16_PREV(s16, 0, len, c); - addToSpanNotSet(c); - } - } - } else { // Irrelevant string. (Also the empty string.) - if(which&UTF8) { - if(which&CONTAINED) { // Only necessary for LONGEST_MATCH. - uint8_t *s8=utf8+utf8Count; - int32_t length8=appendUTF8(s16, length16, s8, utf8Length-utf8Count); - utf8Count+=utf8Lengths[i]=length8; - } else { - utf8Lengths[i]=0; - } - } - if(all) { - spanLengths[i]=spanBackLengths[i]= - spanUTF8Lengths[i]=spanBackUTF8Lengths[i]= - (uint8_t)ALL_CP_CONTAINED; - } else { - // All spanXYZLengths pointers contain the same address. - spanLengths[i]=(uint8_t)ALL_CP_CONTAINED; - } - } - } - - // Finish. - if(all) { - pSpanNotSet->freeze(); - } -} - -// Copy constructor. Assumes which==ALL for a frozen set. -UnicodeSetStringSpan::UnicodeSetStringSpan(const UnicodeSetStringSpan &otherStringSpan, - const UVector &newParentSetStrings) - : spanSet(otherStringSpan.spanSet), pSpanNotSet(NULL), strings(newParentSetStrings), - utf8Lengths(NULL), spanLengths(NULL), utf8(NULL), - utf8Length(otherStringSpan.utf8Length), - maxLength16(otherStringSpan.maxLength16), maxLength8(otherStringSpan.maxLength8), - all(true) { - if(otherStringSpan.pSpanNotSet==&otherStringSpan.spanSet) { - pSpanNotSet=&spanSet; - } else { - pSpanNotSet=otherStringSpan.pSpanNotSet->clone(); - } - - // Allocate a block of meta data. - // UTF-8 lengths, 4 sets of span lengths, UTF-8 strings. - int32_t stringsLength=strings.size(); - int32_t allocSize=stringsLength*(4+1+1+1+1)+utf8Length; - if(allocSize<=(int32_t)sizeof(staticLengths)) { - utf8Lengths=staticLengths; - } else { - utf8Lengths=(int32_t *)uprv_malloc(allocSize); - if(utf8Lengths==NULL) { - maxLength16=maxLength8=0; // Prevent usage by making needsStringSpanUTF16/8() return false. - return; // Out of memory. - } - } - - spanLengths=(uint8_t *)(utf8Lengths+stringsLength); - utf8=spanLengths+stringsLength*4; - uprv_memcpy(utf8Lengths, otherStringSpan.utf8Lengths, allocSize); -} - -UnicodeSetStringSpan::~UnicodeSetStringSpan() { - if(pSpanNotSet!=NULL && pSpanNotSet!=&spanSet) { - delete pSpanNotSet; - } - if(utf8Lengths!=NULL && utf8Lengths!=staticLengths) { - uprv_free(utf8Lengths); - } -} - -void UnicodeSetStringSpan::addToSpanNotSet(UChar32 c) { - if(pSpanNotSet==NULL || pSpanNotSet==&spanSet) { - if(spanSet.contains(c)) { - return; // Nothing to do. - } - UnicodeSet *newSet=spanSet.cloneAsThawed(); - if(newSet==NULL) { - return; // Out of memory. - } else { - pSpanNotSet=newSet; - } - } - pSpanNotSet->add(c); -} - -// Compare strings without any argument checks. Requires length>0. -static inline UBool -matches16(const UChar *s, const UChar *t, int32_t length) { - do { - if(*s++!=*t++) { - return false; - } - } while(--length>0); - return true; -} - -static inline UBool -matches8(const uint8_t *s, const uint8_t *t, int32_t length) { - do { - if(*s++!=*t++) { - return false; - } - } while(--length>0); - return true; -} - -// Compare 16-bit Unicode strings (which may be malformed UTF-16) -// at code point boundaries. -// That is, each edge of a match must not be in the middle of a surrogate pair. -static inline UBool -matches16CPB(const UChar *s, int32_t start, int32_t limit, const UChar *t, int32_t length) { - s+=start; - limit-=start; - return matches16(s, t, length) && - !(0=0xd800 && c<=0xdbff && length>=2 && U16_IS_TRAIL(c2=s[1])) { - return set.contains(U16_GET_SUPPLEMENTARY(c, c2)) ? 2 : -2; - } - return set.contains(c) ? 1 : -1; -} - -static inline int32_t -spanOneBack(const UnicodeSet &set, const UChar *s, int32_t length) { - UChar c=s[length-1], c2; - if(c>=0xdc00 && c<=0xdfff && length>=2 && U16_IS_LEAD(c2=s[length-2])) { - return set.contains(U16_GET_SUPPLEMENTARY(c2, c)) ? 2 : -2; - } - return set.contains(c) ? 1 : -1; -} - -static inline int32_t -spanOneUTF8(const UnicodeSet &set, const uint8_t *s, int32_t length) { - UChar32 c=*s; - if(U8_IS_SINGLE(c)) { - return set.contains(c) ? 1 : -1; - } - // Take advantage of non-ASCII fastpaths in U8_NEXT_OR_FFFD(). - int32_t i=0; - U8_NEXT_OR_FFFD(s, i, length, c); - return set.contains(c) ? i : -i; -} - -static inline int32_t -spanOneBackUTF8(const UnicodeSet &set, const uint8_t *s, int32_t length) { - UChar32 c=s[length-1]; - if(U8_IS_SINGLE(c)) { - return set.contains(c) ? 1 : -1; - } - int32_t i=length-1; - c=utf8_prevCharSafeBody(s, 0, &i, c, -3); - length-=i; - return set.contains(c) ? length : -length; -} - -/* - * Note: In span() when spanLength==0 (after a string match, or at the beginning - * after an empty code point span) and in spanNot() and spanNotUTF8(), - * string matching could use a binary search - * because all string matches are done from the same start index. - * - * For UTF-8, this would require a comparison function that returns UTF-16 order. - * - * This optimization should not be necessary for normal UnicodeSets because - * most sets have no strings, and most sets with strings have - * very few very short strings. - * For cases with many strings, it might be better to use a different API - * and implementation with a DFA (state machine). - */ - -/* - * Algorithm for span(USET_SPAN_CONTAINED) - * - * Theoretical algorithm: - * - Iterate through the string, and at each code point boundary: - * + If the code point there is in the set, then remember to continue after it. - * + If a set string matches at the current position, then remember to continue after it. - * + Either recursively span for each code point or string match, - * or recursively span for all but the shortest one and - * iteratively continue the span with the shortest local match. - * + Remember the longest recursive span (the farthest end point). - * + If there is no match at the current position, neither for the code point there - * nor for any set string, then stop and return the longest recursive span length. - * - * Optimized implementation: - * - * (We assume that most sets will have very few very short strings. - * A span using a string-less set is extremely fast.) - * - * Create and cache a spanSet which contains all of the single code points - * of the original set but none of its strings. - * - * - Start with spanLength=spanSet.span(USET_SPAN_CONTAINED). - * - Loop: - * + Try to match each set string at the end of the spanLength. - * ~ Set strings that start with set-contained code points must be matched - * with a partial overlap because the recursive algorithm would have tried - * to match them at every position. - * ~ Set strings that entirely consist of set-contained code points - * are irrelevant for span(USET_SPAN_CONTAINED) because the - * recursive algorithm would continue after them anyway - * and find the longest recursive match from their end. - * ~ Rather than recursing, note each end point of a set string match. - * + If no set string matched after spanSet.span(), then return - * with where the spanSet.span() ended. - * + If at least one set string matched after spanSet.span(), then - * pop the shortest string match end point and continue - * the loop, trying to match all set strings from there. - * + If at least one more set string matched after a previous string match, - * then test if the code point after the previous string match is also - * contained in the set. - * Continue the loop with the shortest end point of either this code point - * or a matching set string. - * + If no more set string matched after a previous string match, - * then try another spanLength=spanSet.span(USET_SPAN_CONTAINED). - * Stop if spanLength==0, otherwise continue the loop. - * - * By noting each end point of a set string match, - * the function visits each string position at most once and finishes - * in linear time. - * - * The recursive algorithm may visit the same string position many times - * if multiple paths lead to it and finishes in exponential time. - */ - -/* - * Algorithm for span(USET_SPAN_SIMPLE) - * - * Theoretical algorithm: - * - Iterate through the string, and at each code point boundary: - * + If the code point there is in the set, then remember to continue after it. - * + If a set string matches at the current position, then remember to continue after it. - * + Continue from the farthest match position and ignore all others. - * + If there is no match at the current position, - * then stop and return the current position. - * - * Optimized implementation: - * - * (Same assumption and spanSet as above.) - * - * - Start with spanLength=spanSet.span(USET_SPAN_CONTAINED). - * - Loop: - * + Try to match each set string at the end of the spanLength. - * ~ Set strings that start with set-contained code points must be matched - * with a partial overlap because the standard algorithm would have tried - * to match them earlier. - * ~ Set strings that entirely consist of set-contained code points - * must be matched with a full overlap because the longest-match algorithm - * would hide set string matches that end earlier. - * Such set strings need not be matched earlier inside the code point span - * because the standard algorithm would then have continued after - * the set string match anyway. - * ~ Remember the longest set string match (farthest end point) from the earliest - * starting point. - * + If no set string matched after spanSet.span(), then return - * with where the spanSet.span() ended. - * + If at least one set string matched, then continue the loop after the - * longest match from the earliest position. - * + If no more set string matched after a previous string match, - * then try another spanLength=spanSet.span(USET_SPAN_CONTAINED). - * Stop if spanLength==0, otherwise continue the loop. - */ - -int32_t UnicodeSetStringSpan::span(const UChar *s, int32_t length, USetSpanCondition spanCondition) const { - if(spanCondition==USET_SPAN_NOT_CONTAINED) { - return spanNot(s, length); - } - int32_t spanLength=spanSet.span(s, length, USET_SPAN_CONTAINED); - if(spanLength==length) { - return length; - } - - // Consider strings; they may overlap with the span. - OffsetList offsets; - if(spanCondition==USET_SPAN_CONTAINED) { - // Use offset list to try all possibilities. - offsets.setMaxLength(maxLength16); - } - int32_t pos=spanLength, rest=length-pos; - int32_t i, stringsLength=strings.size(); - for(;;) { - if(spanCondition==USET_SPAN_CONTAINED) { - for(i=0; i0); - - // Try to match this string at pos-overlap..pos. - if(overlap>=LONG_SPAN) { - overlap=length16; - // While contained: No point matching fully inside the code point span. - U16_BACK_1(s16, 0, overlap); // Length of the string minus the last code point. - } - if(overlap>spanLength) { - overlap=spanLength; - } - int32_t inc=length16-overlap; // Keep overlap+inc==length16. - for(;;) { - if(inc>rest) { - break; - } - // Try to match if the increment is not listed already. - if(!offsets.containsOffset(inc) && matches16CPB(s, pos-overlap, length, s16, length16)) { - if(inc==rest) { - return length; // Reached the end of the string. - } - offsets.addOffset(inc); - } - if(overlap==0) { - break; - } - --overlap; - ++inc; - } - } - } else /* USET_SPAN_SIMPLE */ { - int32_t maxInc=0, maxOverlap=0; - for(i=0; i=LONG_SPAN) { - overlap=length16; - // Longest match: Need to match fully inside the code point span - // to find the match from the earliest start. - } - if(overlap>spanLength) { - overlap=spanLength; - } - int32_t inc=length16-overlap; // Keep overlap+inc==length16. - for(;;) { - if(inc>rest || overlapmaxOverlap || /* redundant overlap==maxOverlap && */ inc>maxInc) && - matches16CPB(s, pos-overlap, length, s16, length16) - ) { - maxInc=inc; // Longest match from earliest start. - maxOverlap=overlap; - break; - } - --overlap; - ++inc; - } - } - - if(maxInc!=0 || maxOverlap!=0) { - // Longest-match algorithm, and there was a string match. - // Simply continue after it. - pos+=maxInc; - rest-=maxInc; - if(rest==0) { - return length; // Reached the end of the string. - } - spanLength=0; // Match strings from after a string match. - continue; - } - } - // Finished trying to match all strings at pos. - - if(spanLength!=0 || pos==0) { - // The position is after an unlimited code point span (spanLength!=0), - // not after a string match. - // The only position where spanLength==0 after a span is pos==0. - // Otherwise, an unlimited code point span is only tried again when no - // strings match, and if such a non-initial span fails we stop. - if(offsets.isEmpty()) { - return pos; // No strings matched after a span. - } - // Match strings from after the next string match. - } else { - // The position is after a string match (or a single code point). - if(offsets.isEmpty()) { - // No more strings matched after a previous string match. - // Try another code point span from after the last string match. - spanLength=spanSet.span(s+pos, rest, USET_SPAN_CONTAINED); - if( spanLength==rest || // Reached the end of the string, or - spanLength==0 // neither strings nor span progressed. - ) { - return pos+spanLength; - } - pos+=spanLength; - rest-=spanLength; - continue; // spanLength>0: Match strings from after a span. - } else { - // Try to match only one code point from after a string match if some - // string matched beyond it, so that we try all possible positions - // and don't overshoot. - spanLength=spanOne(spanSet, s+pos, rest); - if(spanLength>0) { - if(spanLength==rest) { - return length; // Reached the end of the string. - } - // Match strings after this code point. - // There cannot be any increments below it because UnicodeSet strings - // contain multiple code points. - pos+=spanLength; - rest-=spanLength; - offsets.shift(spanLength); - spanLength=0; - continue; // Match strings from after a single code point. - } - // Match strings from after the next string match. - } - } - int32_t minOffset=offsets.popMinimum(); - pos+=minOffset; - rest-=minOffset; - spanLength=0; // Match strings from after a string match. - } -} - -int32_t UnicodeSetStringSpan::spanBack(const UChar *s, int32_t length, USetSpanCondition spanCondition) const { - if(spanCondition==USET_SPAN_NOT_CONTAINED) { - return spanNotBack(s, length); - } - int32_t pos=spanSet.spanBack(s, length, USET_SPAN_CONTAINED); - if(pos==0) { - return 0; - } - int32_t spanLength=length-pos; - - // Consider strings; they may overlap with the span. - OffsetList offsets; - if(spanCondition==USET_SPAN_CONTAINED) { - // Use offset list to try all possibilities. - offsets.setMaxLength(maxLength16); - } - int32_t i, stringsLength=strings.size(); - uint8_t *spanBackLengths=spanLengths; - if(all) { - spanBackLengths+=stringsLength; - } - for(;;) { - if(spanCondition==USET_SPAN_CONTAINED) { - for(i=0; i0); - - // Try to match this string at pos-(length16-overlap)..pos-length16. - if(overlap>=LONG_SPAN) { - overlap=length16; - // While contained: No point matching fully inside the code point span. - int32_t len1=0; - U16_FWD_1(s16, len1, overlap); - overlap-=len1; // Length of the string minus the first code point. - } - if(overlap>spanLength) { - overlap=spanLength; - } - int32_t dec=length16-overlap; // Keep dec+overlap==length16. - for(;;) { - if(dec>pos) { - break; - } - // Try to match if the decrement is not listed already. - if(!offsets.containsOffset(dec) && matches16CPB(s, pos-dec, length, s16, length16)) { - if(dec==pos) { - return 0; // Reached the start of the string. - } - offsets.addOffset(dec); - } - if(overlap==0) { - break; - } - --overlap; - ++dec; - } - } - } else /* USET_SPAN_SIMPLE */ { - int32_t maxDec=0, maxOverlap=0; - for(i=0; i=LONG_SPAN) { - overlap=length16; - // Longest match: Need to match fully inside the code point span - // to find the match from the latest end. - } - if(overlap>spanLength) { - overlap=spanLength; - } - int32_t dec=length16-overlap; // Keep dec+overlap==length16. - for(;;) { - if(dec>pos || overlapmaxOverlap || /* redundant overlap==maxOverlap && */ dec>maxDec) && - matches16CPB(s, pos-dec, length, s16, length16) - ) { - maxDec=dec; // Longest match from latest end. - maxOverlap=overlap; - break; - } - --overlap; - ++dec; - } - } - - if(maxDec!=0 || maxOverlap!=0) { - // Longest-match algorithm, and there was a string match. - // Simply continue before it. - pos-=maxDec; - if(pos==0) { - return 0; // Reached the start of the string. - } - spanLength=0; // Match strings from before a string match. - continue; - } - } - // Finished trying to match all strings at pos. - - if(spanLength!=0 || pos==length) { - // The position is before an unlimited code point span (spanLength!=0), - // not before a string match. - // The only position where spanLength==0 before a span is pos==length. - // Otherwise, an unlimited code point span is only tried again when no - // strings match, and if such a non-initial span fails we stop. - if(offsets.isEmpty()) { - return pos; // No strings matched before a span. - } - // Match strings from before the next string match. - } else { - // The position is before a string match (or a single code point). - if(offsets.isEmpty()) { - // No more strings matched before a previous string match. - // Try another code point span from before the last string match. - int32_t oldPos=pos; - pos=spanSet.spanBack(s, oldPos, USET_SPAN_CONTAINED); - spanLength=oldPos-pos; - if( pos==0 || // Reached the start of the string, or - spanLength==0 // neither strings nor span progressed. - ) { - return pos; - } - continue; // spanLength>0: Match strings from before a span. - } else { - // Try to match only one code point from before a string match if some - // string matched beyond it, so that we try all possible positions - // and don't overshoot. - spanLength=spanOneBack(spanSet, s, pos); - if(spanLength>0) { - if(spanLength==pos) { - return 0; // Reached the start of the string. - } - // Match strings before this code point. - // There cannot be any decrements below it because UnicodeSet strings - // contain multiple code points. - pos-=spanLength; - offsets.shift(spanLength); - spanLength=0; - continue; // Match strings from before a single code point. - } - // Match strings from before the next string match. - } - } - pos-=offsets.popMinimum(); - spanLength=0; // Match strings from before a string match. - } -} - -int32_t UnicodeSetStringSpan::spanUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const { - if(spanCondition==USET_SPAN_NOT_CONTAINED) { - return spanNotUTF8(s, length); - } - int32_t spanLength=spanSet.spanUTF8((const char *)s, length, USET_SPAN_CONTAINED); - if(spanLength==length) { - return length; - } - - // Consider strings; they may overlap with the span. - OffsetList offsets; - if(spanCondition==USET_SPAN_CONTAINED) { - // Use offset list to try all possibilities. - offsets.setMaxLength(maxLength8); - } - int32_t pos=spanLength, rest=length-pos; - int32_t i, stringsLength=strings.size(); - uint8_t *spanUTF8Lengths=spanLengths; - if(all) { - spanUTF8Lengths+=2*stringsLength; - } - for(;;) { - const uint8_t *s8=utf8; - int32_t length8; - if(spanCondition==USET_SPAN_CONTAINED) { - for(i=0; i=LONG_SPAN) { - overlap=length8; - // While contained: No point matching fully inside the code point span. - U8_BACK_1(s8, 0, overlap); // Length of the string minus the last code point. - } - if(overlap>spanLength) { - overlap=spanLength; - } - int32_t inc=length8-overlap; // Keep overlap+inc==length8. - for(;;) { - if(inc>rest) { - break; - } - // Try to match if the increment is not listed already. - // Match at code point boundaries. (The UTF-8 strings were converted - // from UTF-16 and are guaranteed to be well-formed.) - if(!U8_IS_TRAIL(s[pos-overlap]) && - !offsets.containsOffset(inc) && - matches8(s+pos-overlap, s8, length8)) { - if(inc==rest) { - return length; // Reached the end of the string. - } - offsets.addOffset(inc); - } - if(overlap==0) { - break; - } - --overlap; - ++inc; - } - s8+=length8; - } - } else /* USET_SPAN_SIMPLE */ { - int32_t maxInc=0, maxOverlap=0; - for(i=0; i=LONG_SPAN) { - overlap=length8; - // Longest match: Need to match fully inside the code point span - // to find the match from the earliest start. - } - if(overlap>spanLength) { - overlap=spanLength; - } - int32_t inc=length8-overlap; // Keep overlap+inc==length8. - for(;;) { - if(inc>rest || overlapmaxOverlap || - /* redundant overlap==maxOverlap && */ inc>maxInc) && - matches8(s+pos-overlap, s8, length8)) { - maxInc=inc; // Longest match from earliest start. - maxOverlap=overlap; - break; - } - --overlap; - ++inc; - } - s8+=length8; - } - - if(maxInc!=0 || maxOverlap!=0) { - // Longest-match algorithm, and there was a string match. - // Simply continue after it. - pos+=maxInc; - rest-=maxInc; - if(rest==0) { - return length; // Reached the end of the string. - } - spanLength=0; // Match strings from after a string match. - continue; - } - } - // Finished trying to match all strings at pos. - - if(spanLength!=0 || pos==0) { - // The position is after an unlimited code point span (spanLength!=0), - // not after a string match. - // The only position where spanLength==0 after a span is pos==0. - // Otherwise, an unlimited code point span is only tried again when no - // strings match, and if such a non-initial span fails we stop. - if(offsets.isEmpty()) { - return pos; // No strings matched after a span. - } - // Match strings from after the next string match. - } else { - // The position is after a string match (or a single code point). - if(offsets.isEmpty()) { - // No more strings matched after a previous string match. - // Try another code point span from after the last string match. - spanLength=spanSet.spanUTF8((const char *)s+pos, rest, USET_SPAN_CONTAINED); - if( spanLength==rest || // Reached the end of the string, or - spanLength==0 // neither strings nor span progressed. - ) { - return pos+spanLength; - } - pos+=spanLength; - rest-=spanLength; - continue; // spanLength>0: Match strings from after a span. - } else { - // Try to match only one code point from after a string match if some - // string matched beyond it, so that we try all possible positions - // and don't overshoot. - spanLength=spanOneUTF8(spanSet, s+pos, rest); - if(spanLength>0) { - if(spanLength==rest) { - return length; // Reached the end of the string. - } - // Match strings after this code point. - // There cannot be any increments below it because UnicodeSet strings - // contain multiple code points. - pos+=spanLength; - rest-=spanLength; - offsets.shift(spanLength); - spanLength=0; - continue; // Match strings from after a single code point. - } - // Match strings from after the next string match. - } - } - int32_t minOffset=offsets.popMinimum(); - pos+=minOffset; - rest-=minOffset; - spanLength=0; // Match strings from after a string match. - } -} - -int32_t UnicodeSetStringSpan::spanBackUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const { - if(spanCondition==USET_SPAN_NOT_CONTAINED) { - return spanNotBackUTF8(s, length); - } - int32_t pos=spanSet.spanBackUTF8((const char *)s, length, USET_SPAN_CONTAINED); - if(pos==0) { - return 0; - } - int32_t spanLength=length-pos; - - // Consider strings; they may overlap with the span. - OffsetList offsets; - if(spanCondition==USET_SPAN_CONTAINED) { - // Use offset list to try all possibilities. - offsets.setMaxLength(maxLength8); - } - int32_t i, stringsLength=strings.size(); - uint8_t *spanBackUTF8Lengths=spanLengths; - if(all) { - spanBackUTF8Lengths+=3*stringsLength; - } - for(;;) { - const uint8_t *s8=utf8; - int32_t length8; - if(spanCondition==USET_SPAN_CONTAINED) { - for(i=0; i=LONG_SPAN) { - overlap=length8; - // While contained: No point matching fully inside the code point span. - int32_t len1=0; - U8_FWD_1(s8, len1, overlap); - overlap-=len1; // Length of the string minus the first code point. - } - if(overlap>spanLength) { - overlap=spanLength; - } - int32_t dec=length8-overlap; // Keep dec+overlap==length8. - for(;;) { - if(dec>pos) { - break; - } - // Try to match if the decrement is not listed already. - // Match at code point boundaries. (The UTF-8 strings were converted - // from UTF-16 and are guaranteed to be well-formed.) - if( !U8_IS_TRAIL(s[pos-dec]) && - !offsets.containsOffset(dec) && - matches8(s+pos-dec, s8, length8) - ) { - if(dec==pos) { - return 0; // Reached the start of the string. - } - offsets.addOffset(dec); - } - if(overlap==0) { - break; - } - --overlap; - ++dec; - } - s8+=length8; - } - } else /* USET_SPAN_SIMPLE */ { - int32_t maxDec=0, maxOverlap=0; - for(i=0; i=LONG_SPAN) { - overlap=length8; - // Longest match: Need to match fully inside the code point span - // to find the match from the latest end. - } - if(overlap>spanLength) { - overlap=spanLength; - } - int32_t dec=length8-overlap; // Keep dec+overlap==length8. - for(;;) { - if(dec>pos || overlapmaxOverlap || /* redundant overlap==maxOverlap && */ dec>maxDec) && - matches8(s+pos-dec, s8, length8) - ) { - maxDec=dec; // Longest match from latest end. - maxOverlap=overlap; - break; - } - --overlap; - ++dec; - } - s8+=length8; - } - - if(maxDec!=0 || maxOverlap!=0) { - // Longest-match algorithm, and there was a string match. - // Simply continue before it. - pos-=maxDec; - if(pos==0) { - return 0; // Reached the start of the string. - } - spanLength=0; // Match strings from before a string match. - continue; - } - } - // Finished trying to match all strings at pos. - - if(spanLength!=0 || pos==length) { - // The position is before an unlimited code point span (spanLength!=0), - // not before a string match. - // The only position where spanLength==0 before a span is pos==length. - // Otherwise, an unlimited code point span is only tried again when no - // strings match, and if such a non-initial span fails we stop. - if(offsets.isEmpty()) { - return pos; // No strings matched before a span. - } - // Match strings from before the next string match. - } else { - // The position is before a string match (or a single code point). - if(offsets.isEmpty()) { - // No more strings matched before a previous string match. - // Try another code point span from before the last string match. - int32_t oldPos=pos; - pos=spanSet.spanBackUTF8((const char *)s, oldPos, USET_SPAN_CONTAINED); - spanLength=oldPos-pos; - if( pos==0 || // Reached the start of the string, or - spanLength==0 // neither strings nor span progressed. - ) { - return pos; - } - continue; // spanLength>0: Match strings from before a span. - } else { - // Try to match only one code point from before a string match if some - // string matched beyond it, so that we try all possible positions - // and don't overshoot. - spanLength=spanOneBackUTF8(spanSet, s, pos); - if(spanLength>0) { - if(spanLength==pos) { - return 0; // Reached the start of the string. - } - // Match strings before this code point. - // There cannot be any decrements below it because UnicodeSet strings - // contain multiple code points. - pos-=spanLength; - offsets.shift(spanLength); - spanLength=0; - continue; // Match strings from before a single code point. - } - // Match strings from before the next string match. - } - } - pos-=offsets.popMinimum(); - spanLength=0; // Match strings from before a string match. - } -} - -/* - * Algorithm for spanNot()==span(USET_SPAN_NOT_CONTAINED) - * - * Theoretical algorithm: - * - Iterate through the string, and at each code point boundary: - * + If the code point there is in the set, then return with the current position. - * + If a set string matches at the current position, then return with the current position. - * - * Optimized implementation: - * - * (Same assumption as for span() above.) - * - * Create and cache a spanNotSet which contains all of the single code points - * of the original set but none of its strings. - * For each set string add its initial code point to the spanNotSet. - * (Also add its final code point for spanNotBack().) - * - * - Loop: - * + Do spanLength=spanNotSet.span(USET_SPAN_NOT_CONTAINED). - * + If the current code point is in the original set, then - * return the current position. - * + If any set string matches at the current position, then - * return the current position. - * + If there is no match at the current position, neither for the code point there - * nor for any set string, then skip this code point and continue the loop. - * This happens for set-string-initial code points that were added to spanNotSet - * when there is not actually a match for such a set string. - */ - -int32_t UnicodeSetStringSpan::spanNot(const UChar *s, int32_t length) const { - int32_t pos=0, rest=length; - int32_t i, stringsLength=strings.size(); - do { - // Span until we find a code point from the set, - // or a code point that starts or ends some string. - i=pSpanNotSet->span(s+pos, rest, USET_SPAN_NOT_CONTAINED); - if(i==rest) { - return length; // Reached the end of the string. - } - pos+=i; - rest-=i; - - // Check whether the current code point is in the original set, - // without the string starts and ends. - int32_t cpLength=spanOne(spanSet, s+pos, rest); - if(cpLength>0) { - return pos; // There is a set element at pos. - } - - // Try to match the strings at pos. - for(i=0; i0); - if(length16<=rest && matches16CPB(s, pos, length, s16, length16)) { - return pos; // There is a set element at pos. - } - } - - // The span(while not contained) ended on a string start/end which is - // not in the original set. Skip this code point and continue. - // cpLength<0 - pos-=cpLength; - rest+=cpLength; - } while(rest!=0); - return length; // Reached the end of the string. -} - -int32_t UnicodeSetStringSpan::spanNotBack(const UChar *s, int32_t length) const { - int32_t pos=length; - int32_t i, stringsLength=strings.size(); - do { - // Span until we find a code point from the set, - // or a code point that starts or ends some string. - pos=pSpanNotSet->spanBack(s, pos, USET_SPAN_NOT_CONTAINED); - if(pos==0) { - return 0; // Reached the start of the string. - } - - // Check whether the current code point is in the original set, - // without the string starts and ends. - int32_t cpLength=spanOneBack(spanSet, s, pos); - if(cpLength>0) { - return pos; // There is a set element at pos. - } - - // Try to match the strings at pos. - for(i=0; i0); - if(length16<=pos && matches16CPB(s, pos-length16, length, s16, length16)) { - return pos; // There is a set element at pos. - } - } - - // The span(while not contained) ended on a string start/end which is - // not in the original set. Skip this code point and continue. - // cpLength<0 - pos+=cpLength; - } while(pos!=0); - return 0; // Reached the start of the string. -} - -int32_t UnicodeSetStringSpan::spanNotUTF8(const uint8_t *s, int32_t length) const { - int32_t pos=0, rest=length; - int32_t i, stringsLength=strings.size(); - uint8_t *spanUTF8Lengths=spanLengths; - if(all) { - spanUTF8Lengths+=2*stringsLength; - } - do { - // Span until we find a code point from the set, - // or a code point that starts or ends some string. - i=pSpanNotSet->spanUTF8((const char *)s+pos, rest, USET_SPAN_NOT_CONTAINED); - if(i==rest) { - return length; // Reached the end of the string. - } - pos+=i; - rest-=i; - - // Check whether the current code point is in the original set, - // without the string starts and ends. - int32_t cpLength=spanOneUTF8(spanSet, s+pos, rest); - if(cpLength>0) { - return pos; // There is a set element at pos. - } - - // Try to match the strings at pos. - const uint8_t *s8=utf8; - int32_t length8; - for(i=0; ispanBackUTF8((const char *)s, pos, USET_SPAN_NOT_CONTAINED); - if(pos==0) { - return 0; // Reached the start of the string. - } - - // Check whether the current code point is in the original set, - // without the string starts and ends. - int32_t cpLength=spanOneBackUTF8(spanSet, s, pos); - if(cpLength>0) { - return pos; // There is a set element at pos. - } - - // Try to match the strings at pos. - const uint8_t *s8=utf8; - int32_t length8; - for(i=0; i=capacity) { + i-=capacity; + } + if(list[i]) { + list[i]=false; + --length; + } + start=i; + } + + // Add an offset. The list must not contain it yet. + // offset=[1..maxLength] + void addOffset(int32_t offset) { + int32_t i=start+offset; + if(i>=capacity) { + i-=capacity; + } + list[i]=true; + ++length; + } + + // offset=[1..maxLength] + UBool containsOffset(int32_t offset) const { + int32_t i=start+offset; + if(i>=capacity) { + i-=capacity; + } + return list[i]; + } + + // Find the lowest stored offset from a non-empty list, remove it, + // and reduce all other offsets by this minimum. + // Returns [1..maxLength]. + int32_t popMinimum() { + // Look for the next offset in list[start+1..capacity-1]. + int32_t i=start, result; + while(++imaxLength16) { + maxLength16=length16; + } + if((which&UTF8) && (thisRelevant || (which&CONTAINED))) { + int32_t length8=getUTF8Length(s16, length16); + utf8Length+=length8; + if(length8>maxLength8) { + maxLength8=length8; + } + } + } + if(!someRelevant) { + maxLength16=maxLength8=0; + return; + } + + // Freeze after checking for the need to use strings at all because freezing + // a set takes some time and memory which are wasted if there are no relevant strings. + if(all) { + spanSet.freeze(); + } + + uint8_t *spanBackLengths; + uint8_t *spanUTF8Lengths; + uint8_t *spanBackUTF8Lengths; + + // Allocate a block of meta data. + int32_t allocSize; + if(all) { + // UTF-8 lengths, 4 sets of span lengths, UTF-8 strings. + allocSize=stringsLength*(4+1+1+1+1)+utf8Length; + } else { + allocSize=stringsLength; // One set of span lengths. + if(which&UTF8) { + // UTF-8 lengths and UTF-8 strings. + allocSize+=stringsLength*4+utf8Length; + } + } + if(allocSize<=(int32_t)sizeof(staticLengths)) { + utf8Lengths=staticLengths; + } else { + utf8Lengths=(int32_t *)uprv_malloc(allocSize); + if(utf8Lengths==nullptr) { + maxLength16=maxLength8=0; // Prevent usage by making needsStringSpanUTF16/8() return false. + return; // Out of memory. + } + } + + if(all) { + // Store span lengths for all span() variants. + spanLengths=(uint8_t *)(utf8Lengths+stringsLength); + spanBackLengths=spanLengths+stringsLength; + spanUTF8Lengths=spanBackLengths+stringsLength; + spanBackUTF8Lengths=spanUTF8Lengths+stringsLength; + utf8=spanBackUTF8Lengths+stringsLength; + } else { + // Store span lengths for only one span() variant. + if(which&UTF8) { + spanLengths=(uint8_t *)(utf8Lengths+stringsLength); + utf8=spanLengths+stringsLength; + } else { + spanLengths=(uint8_t *)utf8Lengths; + } + spanBackLengths=spanUTF8Lengths=spanBackUTF8Lengths=spanLengths; + } + + // Set the meta data and pSpanNotSet and write the UTF-8 strings. + int32_t utf8Count=0; // Count UTF-8 bytes written so far. + + for(i=0; i0) { // Relevant string. + if(which&UTF16) { + if(which&CONTAINED) { + if(which&FWD) { + spanLengths[i]=makeSpanLengthByte(spanLength); + } + if(which&BACK) { + spanLength=length16-spanSet.spanBack(s16, length16, USET_SPAN_CONTAINED); + spanBackLengths[i]=makeSpanLengthByte(spanLength); + } + } else /* not CONTAINED, not all, but NOT_CONTAINED */ { + spanLengths[i]=spanBackLengths[i]=0; // Only store a relevant/irrelevant flag. + } + } + if(which&UTF8) { + uint8_t *s8=utf8+utf8Count; + int32_t length8=appendUTF8(s16, length16, s8, utf8Length-utf8Count); + utf8Count+=utf8Lengths[i]=length8; + if(length8==0) { // Irrelevant for UTF-8 because not representable in UTF-8. + spanUTF8Lengths[i]=spanBackUTF8Lengths[i]=(uint8_t)ALL_CP_CONTAINED; + } else { // Relevant for UTF-8. + if(which&CONTAINED) { + if(which&FWD) { + spanLength=spanSet.spanUTF8((const char *)s8, length8, USET_SPAN_CONTAINED); + spanUTF8Lengths[i]=makeSpanLengthByte(spanLength); + } + if(which&BACK) { + spanLength=length8-spanSet.spanBackUTF8((const char *)s8, length8, USET_SPAN_CONTAINED); + spanBackUTF8Lengths[i]=makeSpanLengthByte(spanLength); + } + } else /* not CONTAINED, not all, but NOT_CONTAINED */ { + spanUTF8Lengths[i]=spanBackUTF8Lengths[i]=0; // Only store a relevant/irrelevant flag. + } + } + } + if(which&NOT_CONTAINED) { + // Add string start and end code points to the spanNotSet so that + // a span(while not contained) stops before any string. + UChar32 c; + if(which&FWD) { + int32_t len=0; + U16_NEXT(s16, len, length16, c); + addToSpanNotSet(c); + } + if(which&BACK) { + int32_t len=length16; + U16_PREV(s16, 0, len, c); + addToSpanNotSet(c); + } + } + } else { // Irrelevant string. (Also the empty string.) + if(which&UTF8) { + if(which&CONTAINED) { // Only necessary for LONGEST_MATCH. + uint8_t *s8=utf8+utf8Count; + int32_t length8=appendUTF8(s16, length16, s8, utf8Length-utf8Count); + utf8Count+=utf8Lengths[i]=length8; + } else { + utf8Lengths[i]=0; + } + } + if(all) { + spanLengths[i]=spanBackLengths[i]= + spanUTF8Lengths[i]=spanBackUTF8Lengths[i]= + (uint8_t)ALL_CP_CONTAINED; + } else { + // All spanXYZLengths pointers contain the same address. + spanLengths[i]=(uint8_t)ALL_CP_CONTAINED; + } + } + } + + // Finish. + if(all) { + pSpanNotSet->freeze(); + } +} + +// Copy constructor. Assumes which==ALL for a frozen set. +UnicodeSetStringSpan::UnicodeSetStringSpan(const UnicodeSetStringSpan &otherStringSpan, + const UVector &newParentSetStrings) + : spanSet(otherStringSpan.spanSet), pSpanNotSet(nullptr), strings(newParentSetStrings), + utf8Lengths(nullptr), spanLengths(nullptr), utf8(nullptr), + utf8Length(otherStringSpan.utf8Length), + maxLength16(otherStringSpan.maxLength16), maxLength8(otherStringSpan.maxLength8), + all(true) { + if(otherStringSpan.pSpanNotSet==&otherStringSpan.spanSet) { + pSpanNotSet=&spanSet; + } else { + pSpanNotSet=otherStringSpan.pSpanNotSet->clone(); + } + + // Allocate a block of meta data. + // UTF-8 lengths, 4 sets of span lengths, UTF-8 strings. + int32_t stringsLength=strings.size(); + int32_t allocSize=stringsLength*(4+1+1+1+1)+utf8Length; + if(allocSize<=(int32_t)sizeof(staticLengths)) { + utf8Lengths=staticLengths; + } else { + utf8Lengths=(int32_t *)uprv_malloc(allocSize); + if(utf8Lengths==nullptr) { + maxLength16=maxLength8=0; // Prevent usage by making needsStringSpanUTF16/8() return false. + return; // Out of memory. + } + } + + spanLengths=(uint8_t *)(utf8Lengths+stringsLength); + utf8=spanLengths+stringsLength*4; + uprv_memcpy(utf8Lengths, otherStringSpan.utf8Lengths, allocSize); +} + +UnicodeSetStringSpan::~UnicodeSetStringSpan() { + if(pSpanNotSet!=nullptr && pSpanNotSet!=&spanSet) { + delete pSpanNotSet; + } + if(utf8Lengths!=nullptr && utf8Lengths!=staticLengths) { + uprv_free(utf8Lengths); + } +} + +void UnicodeSetStringSpan::addToSpanNotSet(UChar32 c) { + if(pSpanNotSet==nullptr || pSpanNotSet==&spanSet) { + if(spanSet.contains(c)) { + return; // Nothing to do. + } + UnicodeSet *newSet=spanSet.cloneAsThawed(); + if(newSet==nullptr) { + return; // Out of memory. + } else { + pSpanNotSet=newSet; + } + } + pSpanNotSet->add(c); +} + +// Compare strings without any argument checks. Requires length>0. +static inline UBool +matches16(const char16_t *s, const char16_t *t, int32_t length) { + do { + if(*s++!=*t++) { + return false; + } + } while(--length>0); + return true; +} + +static inline UBool +matches8(const uint8_t *s, const uint8_t *t, int32_t length) { + do { + if(*s++!=*t++) { + return false; + } + } while(--length>0); + return true; +} + +// Compare 16-bit Unicode strings (which may be malformed UTF-16) +// at code point boundaries. +// That is, each edge of a match must not be in the middle of a surrogate pair. +static inline UBool +matches16CPB(const char16_t *s, int32_t start, int32_t limit, const char16_t *t, int32_t length) { + s+=start; + limit-=start; + return matches16(s, t, length) && + !(0=0xd800 && c<=0xdbff && length>=2 && U16_IS_TRAIL(c2=s[1])) { + return set.contains(U16_GET_SUPPLEMENTARY(c, c2)) ? 2 : -2; + } + return set.contains(c) ? 1 : -1; +} + +static inline int32_t +spanOneBack(const UnicodeSet &set, const char16_t *s, int32_t length) { + char16_t c=s[length-1], c2; + if(c>=0xdc00 && c<=0xdfff && length>=2 && U16_IS_LEAD(c2=s[length-2])) { + return set.contains(U16_GET_SUPPLEMENTARY(c2, c)) ? 2 : -2; + } + return set.contains(c) ? 1 : -1; +} + +static inline int32_t +spanOneUTF8(const UnicodeSet &set, const uint8_t *s, int32_t length) { + UChar32 c=*s; + if(U8_IS_SINGLE(c)) { + return set.contains(c) ? 1 : -1; + } + // Take advantage of non-ASCII fastpaths in U8_NEXT_OR_FFFD(). + int32_t i=0; + U8_NEXT_OR_FFFD(s, i, length, c); + return set.contains(c) ? i : -i; +} + +static inline int32_t +spanOneBackUTF8(const UnicodeSet &set, const uint8_t *s, int32_t length) { + UChar32 c=s[length-1]; + if(U8_IS_SINGLE(c)) { + return set.contains(c) ? 1 : -1; + } + int32_t i=length-1; + c=utf8_prevCharSafeBody(s, 0, &i, c, -3); + length-=i; + return set.contains(c) ? length : -length; +} + +/* + * Note: In span() when spanLength==0 (after a string match, or at the beginning + * after an empty code point span) and in spanNot() and spanNotUTF8(), + * string matching could use a binary search + * because all string matches are done from the same start index. + * + * For UTF-8, this would require a comparison function that returns UTF-16 order. + * + * This optimization should not be necessary for normal UnicodeSets because + * most sets have no strings, and most sets with strings have + * very few very short strings. + * For cases with many strings, it might be better to use a different API + * and implementation with a DFA (state machine). + */ + +/* + * Algorithm for span(USET_SPAN_CONTAINED) + * + * Theoretical algorithm: + * - Iterate through the string, and at each code point boundary: + * + If the code point there is in the set, then remember to continue after it. + * + If a set string matches at the current position, then remember to continue after it. + * + Either recursively span for each code point or string match, + * or recursively span for all but the shortest one and + * iteratively continue the span with the shortest local match. + * + Remember the longest recursive span (the farthest end point). + * + If there is no match at the current position, neither for the code point there + * nor for any set string, then stop and return the longest recursive span length. + * + * Optimized implementation: + * + * (We assume that most sets will have very few very short strings. + * A span using a string-less set is extremely fast.) + * + * Create and cache a spanSet which contains all of the single code points + * of the original set but none of its strings. + * + * - Start with spanLength=spanSet.span(USET_SPAN_CONTAINED). + * - Loop: + * + Try to match each set string at the end of the spanLength. + * ~ Set strings that start with set-contained code points must be matched + * with a partial overlap because the recursive algorithm would have tried + * to match them at every position. + * ~ Set strings that entirely consist of set-contained code points + * are irrelevant for span(USET_SPAN_CONTAINED) because the + * recursive algorithm would continue after them anyway + * and find the longest recursive match from their end. + * ~ Rather than recursing, note each end point of a set string match. + * + If no set string matched after spanSet.span(), then return + * with where the spanSet.span() ended. + * + If at least one set string matched after spanSet.span(), then + * pop the shortest string match end point and continue + * the loop, trying to match all set strings from there. + * + If at least one more set string matched after a previous string match, + * then test if the code point after the previous string match is also + * contained in the set. + * Continue the loop with the shortest end point of either this code point + * or a matching set string. + * + If no more set string matched after a previous string match, + * then try another spanLength=spanSet.span(USET_SPAN_CONTAINED). + * Stop if spanLength==0, otherwise continue the loop. + * + * By noting each end point of a set string match, + * the function visits each string position at most once and finishes + * in linear time. + * + * The recursive algorithm may visit the same string position many times + * if multiple paths lead to it and finishes in exponential time. + */ + +/* + * Algorithm for span(USET_SPAN_SIMPLE) + * + * Theoretical algorithm: + * - Iterate through the string, and at each code point boundary: + * + If the code point there is in the set, then remember to continue after it. + * + If a set string matches at the current position, then remember to continue after it. + * + Continue from the farthest match position and ignore all others. + * + If there is no match at the current position, + * then stop and return the current position. + * + * Optimized implementation: + * + * (Same assumption and spanSet as above.) + * + * - Start with spanLength=spanSet.span(USET_SPAN_CONTAINED). + * - Loop: + * + Try to match each set string at the end of the spanLength. + * ~ Set strings that start with set-contained code points must be matched + * with a partial overlap because the standard algorithm would have tried + * to match them earlier. + * ~ Set strings that entirely consist of set-contained code points + * must be matched with a full overlap because the longest-match algorithm + * would hide set string matches that end earlier. + * Such set strings need not be matched earlier inside the code point span + * because the standard algorithm would then have continued after + * the set string match anyway. + * ~ Remember the longest set string match (farthest end point) from the earliest + * starting point. + * + If no set string matched after spanSet.span(), then return + * with where the spanSet.span() ended. + * + If at least one set string matched, then continue the loop after the + * longest match from the earliest position. + * + If no more set string matched after a previous string match, + * then try another spanLength=spanSet.span(USET_SPAN_CONTAINED). + * Stop if spanLength==0, otherwise continue the loop. + */ + +int32_t UnicodeSetStringSpan::span(const char16_t *s, int32_t length, USetSpanCondition spanCondition) const { + if(spanCondition==USET_SPAN_NOT_CONTAINED) { + return spanNot(s, length); + } + int32_t spanLength=spanSet.span(s, length, USET_SPAN_CONTAINED); + if(spanLength==length) { + return length; + } + + // Consider strings; they may overlap with the span. + OffsetList offsets; + if(spanCondition==USET_SPAN_CONTAINED) { + // Use offset list to try all possibilities. + offsets.setMaxLength(maxLength16); + } + int32_t pos=spanLength, rest=length-pos; + int32_t i, stringsLength=strings.size(); + for(;;) { + if(spanCondition==USET_SPAN_CONTAINED) { + for(i=0; i0); + + // Try to match this string at pos-overlap..pos. + if(overlap>=LONG_SPAN) { + overlap=length16; + // While contained: No point matching fully inside the code point span. + U16_BACK_1(s16, 0, overlap); // Length of the string minus the last code point. + } + if(overlap>spanLength) { + overlap=spanLength; + } + int32_t inc=length16-overlap; // Keep overlap+inc==length16. + for(;;) { + if(inc>rest) { + break; + } + // Try to match if the increment is not listed already. + if(!offsets.containsOffset(inc) && matches16CPB(s, pos-overlap, length, s16, length16)) { + if(inc==rest) { + return length; // Reached the end of the string. + } + offsets.addOffset(inc); + } + if(overlap==0) { + break; + } + --overlap; + ++inc; + } + } + } else /* USET_SPAN_SIMPLE */ { + int32_t maxInc=0, maxOverlap=0; + for(i=0; i=LONG_SPAN) { + overlap=length16; + // Longest match: Need to match fully inside the code point span + // to find the match from the earliest start. + } + if(overlap>spanLength) { + overlap=spanLength; + } + int32_t inc=length16-overlap; // Keep overlap+inc==length16. + for(;;) { + if(inc>rest || overlapmaxOverlap || /* redundant overlap==maxOverlap && */ inc>maxInc) && + matches16CPB(s, pos-overlap, length, s16, length16) + ) { + maxInc=inc; // Longest match from earliest start. + maxOverlap=overlap; + break; + } + --overlap; + ++inc; + } + } + + if(maxInc!=0 || maxOverlap!=0) { + // Longest-match algorithm, and there was a string match. + // Simply continue after it. + pos+=maxInc; + rest-=maxInc; + if(rest==0) { + return length; // Reached the end of the string. + } + spanLength=0; // Match strings from after a string match. + continue; + } + } + // Finished trying to match all strings at pos. + + if(spanLength!=0 || pos==0) { + // The position is after an unlimited code point span (spanLength!=0), + // not after a string match. + // The only position where spanLength==0 after a span is pos==0. + // Otherwise, an unlimited code point span is only tried again when no + // strings match, and if such a non-initial span fails we stop. + if(offsets.isEmpty()) { + return pos; // No strings matched after a span. + } + // Match strings from after the next string match. + } else { + // The position is after a string match (or a single code point). + if(offsets.isEmpty()) { + // No more strings matched after a previous string match. + // Try another code point span from after the last string match. + spanLength=spanSet.span(s+pos, rest, USET_SPAN_CONTAINED); + if( spanLength==rest || // Reached the end of the string, or + spanLength==0 // neither strings nor span progressed. + ) { + return pos+spanLength; + } + pos+=spanLength; + rest-=spanLength; + continue; // spanLength>0: Match strings from after a span. + } else { + // Try to match only one code point from after a string match if some + // string matched beyond it, so that we try all possible positions + // and don't overshoot. + spanLength=spanOne(spanSet, s+pos, rest); + if(spanLength>0) { + if(spanLength==rest) { + return length; // Reached the end of the string. + } + // Match strings after this code point. + // There cannot be any increments below it because UnicodeSet strings + // contain multiple code points. + pos+=spanLength; + rest-=spanLength; + offsets.shift(spanLength); + spanLength=0; + continue; // Match strings from after a single code point. + } + // Match strings from after the next string match. + } + } + int32_t minOffset=offsets.popMinimum(); + pos+=minOffset; + rest-=minOffset; + spanLength=0; // Match strings from after a string match. + } +} + +int32_t UnicodeSetStringSpan::spanBack(const char16_t *s, int32_t length, USetSpanCondition spanCondition) const { + if(spanCondition==USET_SPAN_NOT_CONTAINED) { + return spanNotBack(s, length); + } + int32_t pos=spanSet.spanBack(s, length, USET_SPAN_CONTAINED); + if(pos==0) { + return 0; + } + int32_t spanLength=length-pos; + + // Consider strings; they may overlap with the span. + OffsetList offsets; + if(spanCondition==USET_SPAN_CONTAINED) { + // Use offset list to try all possibilities. + offsets.setMaxLength(maxLength16); + } + int32_t i, stringsLength=strings.size(); + uint8_t *spanBackLengths=spanLengths; + if(all) { + spanBackLengths+=stringsLength; + } + for(;;) { + if(spanCondition==USET_SPAN_CONTAINED) { + for(i=0; i0); + + // Try to match this string at pos-(length16-overlap)..pos-length16. + if(overlap>=LONG_SPAN) { + overlap=length16; + // While contained: No point matching fully inside the code point span. + int32_t len1=0; + U16_FWD_1(s16, len1, overlap); + overlap-=len1; // Length of the string minus the first code point. + } + if(overlap>spanLength) { + overlap=spanLength; + } + int32_t dec=length16-overlap; // Keep dec+overlap==length16. + for(;;) { + if(dec>pos) { + break; + } + // Try to match if the decrement is not listed already. + if(!offsets.containsOffset(dec) && matches16CPB(s, pos-dec, length, s16, length16)) { + if(dec==pos) { + return 0; // Reached the start of the string. + } + offsets.addOffset(dec); + } + if(overlap==0) { + break; + } + --overlap; + ++dec; + } + } + } else /* USET_SPAN_SIMPLE */ { + int32_t maxDec=0, maxOverlap=0; + for(i=0; i=LONG_SPAN) { + overlap=length16; + // Longest match: Need to match fully inside the code point span + // to find the match from the latest end. + } + if(overlap>spanLength) { + overlap=spanLength; + } + int32_t dec=length16-overlap; // Keep dec+overlap==length16. + for(;;) { + if(dec>pos || overlapmaxOverlap || /* redundant overlap==maxOverlap && */ dec>maxDec) && + matches16CPB(s, pos-dec, length, s16, length16) + ) { + maxDec=dec; // Longest match from latest end. + maxOverlap=overlap; + break; + } + --overlap; + ++dec; + } + } + + if(maxDec!=0 || maxOverlap!=0) { + // Longest-match algorithm, and there was a string match. + // Simply continue before it. + pos-=maxDec; + if(pos==0) { + return 0; // Reached the start of the string. + } + spanLength=0; // Match strings from before a string match. + continue; + } + } + // Finished trying to match all strings at pos. + + if(spanLength!=0 || pos==length) { + // The position is before an unlimited code point span (spanLength!=0), + // not before a string match. + // The only position where spanLength==0 before a span is pos==length. + // Otherwise, an unlimited code point span is only tried again when no + // strings match, and if such a non-initial span fails we stop. + if(offsets.isEmpty()) { + return pos; // No strings matched before a span. + } + // Match strings from before the next string match. + } else { + // The position is before a string match (or a single code point). + if(offsets.isEmpty()) { + // No more strings matched before a previous string match. + // Try another code point span from before the last string match. + int32_t oldPos=pos; + pos=spanSet.spanBack(s, oldPos, USET_SPAN_CONTAINED); + spanLength=oldPos-pos; + if( pos==0 || // Reached the start of the string, or + spanLength==0 // neither strings nor span progressed. + ) { + return pos; + } + continue; // spanLength>0: Match strings from before a span. + } else { + // Try to match only one code point from before a string match if some + // string matched beyond it, so that we try all possible positions + // and don't overshoot. + spanLength=spanOneBack(spanSet, s, pos); + if(spanLength>0) { + if(spanLength==pos) { + return 0; // Reached the start of the string. + } + // Match strings before this code point. + // There cannot be any decrements below it because UnicodeSet strings + // contain multiple code points. + pos-=spanLength; + offsets.shift(spanLength); + spanLength=0; + continue; // Match strings from before a single code point. + } + // Match strings from before the next string match. + } + } + pos-=offsets.popMinimum(); + spanLength=0; // Match strings from before a string match. + } +} + +int32_t UnicodeSetStringSpan::spanUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const { + if(spanCondition==USET_SPAN_NOT_CONTAINED) { + return spanNotUTF8(s, length); + } + int32_t spanLength=spanSet.spanUTF8((const char *)s, length, USET_SPAN_CONTAINED); + if(spanLength==length) { + return length; + } + + // Consider strings; they may overlap with the span. + OffsetList offsets; + if(spanCondition==USET_SPAN_CONTAINED) { + // Use offset list to try all possibilities. + offsets.setMaxLength(maxLength8); + } + int32_t pos=spanLength, rest=length-pos; + int32_t i, stringsLength=strings.size(); + uint8_t *spanUTF8Lengths=spanLengths; + if(all) { + spanUTF8Lengths+=2*stringsLength; + } + for(;;) { + const uint8_t *s8=utf8; + int32_t length8; + if(spanCondition==USET_SPAN_CONTAINED) { + for(i=0; i=LONG_SPAN) { + overlap=length8; + // While contained: No point matching fully inside the code point span. + U8_BACK_1(s8, 0, overlap); // Length of the string minus the last code point. + } + if(overlap>spanLength) { + overlap=spanLength; + } + int32_t inc=length8-overlap; // Keep overlap+inc==length8. + for(;;) { + if(inc>rest) { + break; + } + // Try to match if the increment is not listed already. + // Match at code point boundaries. (The UTF-8 strings were converted + // from UTF-16 and are guaranteed to be well-formed.) + if(!U8_IS_TRAIL(s[pos-overlap]) && + !offsets.containsOffset(inc) && + matches8(s+pos-overlap, s8, length8)) { + if(inc==rest) { + return length; // Reached the end of the string. + } + offsets.addOffset(inc); + } + if(overlap==0) { + break; + } + --overlap; + ++inc; + } + s8+=length8; + } + } else /* USET_SPAN_SIMPLE */ { + int32_t maxInc=0, maxOverlap=0; + for(i=0; i=LONG_SPAN) { + overlap=length8; + // Longest match: Need to match fully inside the code point span + // to find the match from the earliest start. + } + if(overlap>spanLength) { + overlap=spanLength; + } + int32_t inc=length8-overlap; // Keep overlap+inc==length8. + for(;;) { + if(inc>rest || overlapmaxOverlap || + /* redundant overlap==maxOverlap && */ inc>maxInc) && + matches8(s+pos-overlap, s8, length8)) { + maxInc=inc; // Longest match from earliest start. + maxOverlap=overlap; + break; + } + --overlap; + ++inc; + } + s8+=length8; + } + + if(maxInc!=0 || maxOverlap!=0) { + // Longest-match algorithm, and there was a string match. + // Simply continue after it. + pos+=maxInc; + rest-=maxInc; + if(rest==0) { + return length; // Reached the end of the string. + } + spanLength=0; // Match strings from after a string match. + continue; + } + } + // Finished trying to match all strings at pos. + + if(spanLength!=0 || pos==0) { + // The position is after an unlimited code point span (spanLength!=0), + // not after a string match. + // The only position where spanLength==0 after a span is pos==0. + // Otherwise, an unlimited code point span is only tried again when no + // strings match, and if such a non-initial span fails we stop. + if(offsets.isEmpty()) { + return pos; // No strings matched after a span. + } + // Match strings from after the next string match. + } else { + // The position is after a string match (or a single code point). + if(offsets.isEmpty()) { + // No more strings matched after a previous string match. + // Try another code point span from after the last string match. + spanLength=spanSet.spanUTF8((const char *)s+pos, rest, USET_SPAN_CONTAINED); + if( spanLength==rest || // Reached the end of the string, or + spanLength==0 // neither strings nor span progressed. + ) { + return pos+spanLength; + } + pos+=spanLength; + rest-=spanLength; + continue; // spanLength>0: Match strings from after a span. + } else { + // Try to match only one code point from after a string match if some + // string matched beyond it, so that we try all possible positions + // and don't overshoot. + spanLength=spanOneUTF8(spanSet, s+pos, rest); + if(spanLength>0) { + if(spanLength==rest) { + return length; // Reached the end of the string. + } + // Match strings after this code point. + // There cannot be any increments below it because UnicodeSet strings + // contain multiple code points. + pos+=spanLength; + rest-=spanLength; + offsets.shift(spanLength); + spanLength=0; + continue; // Match strings from after a single code point. + } + // Match strings from after the next string match. + } + } + int32_t minOffset=offsets.popMinimum(); + pos+=minOffset; + rest-=minOffset; + spanLength=0; // Match strings from after a string match. + } +} + +int32_t UnicodeSetStringSpan::spanBackUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const { + if(spanCondition==USET_SPAN_NOT_CONTAINED) { + return spanNotBackUTF8(s, length); + } + int32_t pos=spanSet.spanBackUTF8((const char *)s, length, USET_SPAN_CONTAINED); + if(pos==0) { + return 0; + } + int32_t spanLength=length-pos; + + // Consider strings; they may overlap with the span. + OffsetList offsets; + if(spanCondition==USET_SPAN_CONTAINED) { + // Use offset list to try all possibilities. + offsets.setMaxLength(maxLength8); + } + int32_t i, stringsLength=strings.size(); + uint8_t *spanBackUTF8Lengths=spanLengths; + if(all) { + spanBackUTF8Lengths+=3*stringsLength; + } + for(;;) { + const uint8_t *s8=utf8; + int32_t length8; + if(spanCondition==USET_SPAN_CONTAINED) { + for(i=0; i=LONG_SPAN) { + overlap=length8; + // While contained: No point matching fully inside the code point span. + int32_t len1=0; + U8_FWD_1(s8, len1, overlap); + overlap-=len1; // Length of the string minus the first code point. + } + if(overlap>spanLength) { + overlap=spanLength; + } + int32_t dec=length8-overlap; // Keep dec+overlap==length8. + for(;;) { + if(dec>pos) { + break; + } + // Try to match if the decrement is not listed already. + // Match at code point boundaries. (The UTF-8 strings were converted + // from UTF-16 and are guaranteed to be well-formed.) + if( !U8_IS_TRAIL(s[pos-dec]) && + !offsets.containsOffset(dec) && + matches8(s+pos-dec, s8, length8) + ) { + if(dec==pos) { + return 0; // Reached the start of the string. + } + offsets.addOffset(dec); + } + if(overlap==0) { + break; + } + --overlap; + ++dec; + } + s8+=length8; + } + } else /* USET_SPAN_SIMPLE */ { + int32_t maxDec=0, maxOverlap=0; + for(i=0; i=LONG_SPAN) { + overlap=length8; + // Longest match: Need to match fully inside the code point span + // to find the match from the latest end. + } + if(overlap>spanLength) { + overlap=spanLength; + } + int32_t dec=length8-overlap; // Keep dec+overlap==length8. + for(;;) { + if(dec>pos || overlapmaxOverlap || /* redundant overlap==maxOverlap && */ dec>maxDec) && + matches8(s+pos-dec, s8, length8) + ) { + maxDec=dec; // Longest match from latest end. + maxOverlap=overlap; + break; + } + --overlap; + ++dec; + } + s8+=length8; + } + + if(maxDec!=0 || maxOverlap!=0) { + // Longest-match algorithm, and there was a string match. + // Simply continue before it. + pos-=maxDec; + if(pos==0) { + return 0; // Reached the start of the string. + } + spanLength=0; // Match strings from before a string match. + continue; + } + } + // Finished trying to match all strings at pos. + + if(spanLength!=0 || pos==length) { + // The position is before an unlimited code point span (spanLength!=0), + // not before a string match. + // The only position where spanLength==0 before a span is pos==length. + // Otherwise, an unlimited code point span is only tried again when no + // strings match, and if such a non-initial span fails we stop. + if(offsets.isEmpty()) { + return pos; // No strings matched before a span. + } + // Match strings from before the next string match. + } else { + // The position is before a string match (or a single code point). + if(offsets.isEmpty()) { + // No more strings matched before a previous string match. + // Try another code point span from before the last string match. + int32_t oldPos=pos; + pos=spanSet.spanBackUTF8((const char *)s, oldPos, USET_SPAN_CONTAINED); + spanLength=oldPos-pos; + if( pos==0 || // Reached the start of the string, or + spanLength==0 // neither strings nor span progressed. + ) { + return pos; + } + continue; // spanLength>0: Match strings from before a span. + } else { + // Try to match only one code point from before a string match if some + // string matched beyond it, so that we try all possible positions + // and don't overshoot. + spanLength=spanOneBackUTF8(spanSet, s, pos); + if(spanLength>0) { + if(spanLength==pos) { + return 0; // Reached the start of the string. + } + // Match strings before this code point. + // There cannot be any decrements below it because UnicodeSet strings + // contain multiple code points. + pos-=spanLength; + offsets.shift(spanLength); + spanLength=0; + continue; // Match strings from before a single code point. + } + // Match strings from before the next string match. + } + } + pos-=offsets.popMinimum(); + spanLength=0; // Match strings from before a string match. + } +} + +/* + * Algorithm for spanNot()==span(USET_SPAN_NOT_CONTAINED) + * + * Theoretical algorithm: + * - Iterate through the string, and at each code point boundary: + * + If the code point there is in the set, then return with the current position. + * + If a set string matches at the current position, then return with the current position. + * + * Optimized implementation: + * + * (Same assumption as for span() above.) + * + * Create and cache a spanNotSet which contains all of the single code points + * of the original set but none of its strings. + * For each set string add its initial code point to the spanNotSet. + * (Also add its final code point for spanNotBack().) + * + * - Loop: + * + Do spanLength=spanNotSet.span(USET_SPAN_NOT_CONTAINED). + * + If the current code point is in the original set, then + * return the current position. + * + If any set string matches at the current position, then + * return the current position. + * + If there is no match at the current position, neither for the code point there + * nor for any set string, then skip this code point and continue the loop. + * This happens for set-string-initial code points that were added to spanNotSet + * when there is not actually a match for such a set string. + */ + +int32_t UnicodeSetStringSpan::spanNot(const char16_t *s, int32_t length) const { + int32_t pos=0, rest=length; + int32_t i, stringsLength=strings.size(); + do { + // Span until we find a code point from the set, + // or a code point that starts or ends some string. + i=pSpanNotSet->span(s+pos, rest, USET_SPAN_NOT_CONTAINED); + if(i==rest) { + return length; // Reached the end of the string. + } + pos+=i; + rest-=i; + + // Check whether the current code point is in the original set, + // without the string starts and ends. + int32_t cpLength=spanOne(spanSet, s+pos, rest); + if(cpLength>0) { + return pos; // There is a set element at pos. + } + + // Try to match the strings at pos. + for(i=0; i0); + if(length16<=rest && matches16CPB(s, pos, length, s16, length16)) { + return pos; // There is a set element at pos. + } + } + + // The span(while not contained) ended on a string start/end which is + // not in the original set. Skip this code point and continue. + // cpLength<0 + pos-=cpLength; + rest+=cpLength; + } while(rest!=0); + return length; // Reached the end of the string. +} + +int32_t UnicodeSetStringSpan::spanNotBack(const char16_t *s, int32_t length) const { + int32_t pos=length; + int32_t i, stringsLength=strings.size(); + do { + // Span until we find a code point from the set, + // or a code point that starts or ends some string. + pos=pSpanNotSet->spanBack(s, pos, USET_SPAN_NOT_CONTAINED); + if(pos==0) { + return 0; // Reached the start of the string. + } + + // Check whether the current code point is in the original set, + // without the string starts and ends. + int32_t cpLength=spanOneBack(spanSet, s, pos); + if(cpLength>0) { + return pos; // There is a set element at pos. + } + + // Try to match the strings at pos. + for(i=0; i0); + if(length16<=pos && matches16CPB(s, pos-length16, length, s16, length16)) { + return pos; // There is a set element at pos. + } + } + + // The span(while not contained) ended on a string start/end which is + // not in the original set. Skip this code point and continue. + // cpLength<0 + pos+=cpLength; + } while(pos!=0); + return 0; // Reached the start of the string. +} + +int32_t UnicodeSetStringSpan::spanNotUTF8(const uint8_t *s, int32_t length) const { + int32_t pos=0, rest=length; + int32_t i, stringsLength=strings.size(); + uint8_t *spanUTF8Lengths=spanLengths; + if(all) { + spanUTF8Lengths+=2*stringsLength; + } + do { + // Span until we find a code point from the set, + // or a code point that starts or ends some string. + i=pSpanNotSet->spanUTF8((const char *)s+pos, rest, USET_SPAN_NOT_CONTAINED); + if(i==rest) { + return length; // Reached the end of the string. + } + pos+=i; + rest-=i; + + // Check whether the current code point is in the original set, + // without the string starts and ends. + int32_t cpLength=spanOneUTF8(spanSet, s+pos, rest); + if(cpLength>0) { + return pos; // There is a set element at pos. + } + + // Try to match the strings at pos. + const uint8_t *s8=utf8; + int32_t length8; + for(i=0; ispanBackUTF8((const char *)s, pos, USET_SPAN_NOT_CONTAINED); + if(pos==0) { + return 0; // Reached the start of the string. + } + + // Check whether the current code point is in the original set, + // without the string starts and ends. + int32_t cpLength=spanOneBackUTF8(spanSet, s, pos); + if(cpLength>0) { + return pos; // There is a set element at pos. + } + + // Try to match the strings at pos. + const uint8_t *s8=utf8; + int32_t length8; + for(i=0; i=0xfe. - LONG_SPAN=0xfe, - // All code points in the string are contained in the parent set. - ALL_CP_CONTAINED=0xff - }; - - // Add a starting or ending string character to the spanNotSet - // so that a character span ends before any string. - void addToSpanNotSet(UChar32 c); - - int32_t spanNot(const UChar *s, int32_t length) const; - int32_t spanNotBack(const UChar *s, int32_t length) const; - int32_t spanNotUTF8(const uint8_t *s, int32_t length) const; - int32_t spanNotBackUTF8(const uint8_t *s, int32_t length) const; - - // Set for span(). Same as parent but without strings. - UnicodeSet spanSet; - - // Set for span(not contained). - // Same as spanSet, plus characters that start or end strings. - UnicodeSet *pSpanNotSet; - - // The strings of the parent set. - const UVector &strings; - - // Pointer to the UTF-8 string lengths. - // Also pointer to further allocated storage for meta data and - // UTF-8 string contents as necessary. - int32_t *utf8Lengths; - - // Pointer to the part of the (utf8Lengths) memory block that stores - // the lengths of span(), spanBack() etc. for each string. - uint8_t *spanLengths; - - // Pointer to the part of the (utf8Lengths) memory block that stores - // the UTF-8 versions of the parent set's strings. - uint8_t *utf8; - - // Number of bytes for all UTF-8 versions of strings together. - int32_t utf8Length; - - // Maximum lengths of relevant strings. - int32_t maxLength16; - int32_t maxLength8; - - // Set up for all variants of span()? - UBool all; - - // Memory for small numbers and lengths of strings. - // For example, for 8 strings: - // 8 UTF-8 lengths, 8*4 bytes span lengths, 8*2 3-byte UTF-8 characters - // = 112 bytes = int32_t[28]. - int32_t staticLengths[32]; -}; - -UBool UnicodeSetStringSpan::needsStringSpanUTF16() { - return (UBool)(maxLength16!=0); -} - -UBool UnicodeSetStringSpan::needsStringSpanUTF8() { - return (UBool)(maxLength8!=0); -} - -UBool UnicodeSetStringSpan::contains(UChar32 c) const { - return spanSet.contains(c); -} - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2007, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: unisetspan.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2007mar01 +* created by: Markus W. Scherer +*/ + +#ifndef __UNISETSPAN_H__ +#define __UNISETSPAN_H__ + +#include "unicode/utypes.h" +#include "unicode/uniset.h" + +U_NAMESPACE_BEGIN + +/* + * Implement span() etc. for a set with strings. + * Avoid recursion because of its exponential complexity. + * Instead, try multiple paths at once and track them with an IndexList. + */ +class UnicodeSetStringSpan : public UMemory { +public: + /* + * Which span() variant will be used? + * The object is either built for one variant and used once, + * or built for all and may be used many times. + */ + enum { + FWD = 0x20, + BACK = 0x10, + UTF16 = 8, + UTF8 = 4, + CONTAINED = 2, + NOT_CONTAINED = 1, + + ALL = 0x3f, + + FWD_UTF16_CONTAINED = FWD | UTF16 | CONTAINED, + FWD_UTF16_NOT_CONTAINED = FWD | UTF16 | NOT_CONTAINED, + FWD_UTF8_CONTAINED = FWD | UTF8 | CONTAINED, + FWD_UTF8_NOT_CONTAINED = FWD | UTF8 | NOT_CONTAINED, + BACK_UTF16_CONTAINED = BACK | UTF16 | CONTAINED, + BACK_UTF16_NOT_CONTAINED= BACK | UTF16 | NOT_CONTAINED, + BACK_UTF8_CONTAINED = BACK | UTF8 | CONTAINED, + BACK_UTF8_NOT_CONTAINED = BACK | UTF8 | NOT_CONTAINED + }; + + UnicodeSetStringSpan(const UnicodeSet &set, const UVector &setStrings, uint32_t which); + + // Copy constructor. Assumes which==ALL for a frozen set. + UnicodeSetStringSpan(const UnicodeSetStringSpan &otherStringSpan, const UVector &newParentSetStrings); + + ~UnicodeSetStringSpan(); + + /* + * Do the strings need to be checked in span() etc.? + * @return true if strings need to be checked (call span() here), + * false if not (use a BMPSet for best performance). + */ + inline UBool needsStringSpanUTF16(); + inline UBool needsStringSpanUTF8(); + + // For fast UnicodeSet::contains(c). + inline UBool contains(UChar32 c) const; + + int32_t span(const char16_t *s, int32_t length, USetSpanCondition spanCondition) const; + + int32_t spanBack(const char16_t *s, int32_t length, USetSpanCondition spanCondition) const; + + int32_t spanUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const; + + int32_t spanBackUTF8(const uint8_t *s, int32_t length, USetSpanCondition spanCondition) const; + +private: + // Special spanLength byte values. + enum { + // The spanLength is >=0xfe. + LONG_SPAN=0xfe, + // All code points in the string are contained in the parent set. + ALL_CP_CONTAINED=0xff + }; + + // Add a starting or ending string character to the spanNotSet + // so that a character span ends before any string. + void addToSpanNotSet(UChar32 c); + + int32_t spanNot(const char16_t *s, int32_t length) const; + int32_t spanNotBack(const char16_t *s, int32_t length) const; + int32_t spanNotUTF8(const uint8_t *s, int32_t length) const; + int32_t spanNotBackUTF8(const uint8_t *s, int32_t length) const; + + // Set for span(). Same as parent but without strings. + UnicodeSet spanSet; + + // Set for span(not contained). + // Same as spanSet, plus characters that start or end strings. + UnicodeSet *pSpanNotSet; + + // The strings of the parent set. + const UVector &strings; + + // Pointer to the UTF-8 string lengths. + // Also pointer to further allocated storage for meta data and + // UTF-8 string contents as necessary. + int32_t *utf8Lengths; + + // Pointer to the part of the (utf8Lengths) memory block that stores + // the lengths of span(), spanBack() etc. for each string. + uint8_t *spanLengths; + + // Pointer to the part of the (utf8Lengths) memory block that stores + // the UTF-8 versions of the parent set's strings. + uint8_t *utf8; + + // Number of bytes for all UTF-8 versions of strings together. + int32_t utf8Length; + + // Maximum lengths of relevant strings. + int32_t maxLength16; + int32_t maxLength8; + + // Set up for all variants of span()? + UBool all; + + // Memory for small numbers and lengths of strings. + // For example, for 8 strings: + // 8 UTF-8 lengths, 8*4 bytes span lengths, 8*2 3-byte UTF-8 characters + // = 112 bytes = int32_t[28]. + int32_t staticLengths[32]; +}; + +UBool UnicodeSetStringSpan::needsStringSpanUTF16() { + return (UBool)(maxLength16!=0); +} + +UBool UnicodeSetStringSpan::needsStringSpanUTF8() { + return (UBool)(maxLength8!=0); +} + +UBool UnicodeSetStringSpan::contains(UChar32 c) const { + return spanSet.contains(c); +} + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/common/unistr.cpp b/deps/icu-small/source/common/unistr.cpp index 4125d194724d4d..d462dcf5b7fb61 100644 --- a/deps/icu-small/source/common/unistr.cpp +++ b/deps/icu-small/source/common/unistr.cpp @@ -1,1988 +1,2030 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 1999-2016, International Business Machines Corporation and -* others. All Rights Reserved. -****************************************************************************** -* -* File unistr.cpp -* -* Modification History: -* -* Date Name Description -* 09/25/98 stephen Creation. -* 04/20/99 stephen Overhauled per 4/16 code review. -* 07/09/99 stephen Renamed {hi,lo},{byte,word} to icu_X for HP/UX -* 11/18/99 aliu Added handleReplaceBetween() to make inherit from -* Replaceable. -* 06/25/01 grhoten Removed the dependency on iostream -****************************************************************************** -*/ - -#include "unicode/utypes.h" -#include "unicode/appendable.h" -#include "unicode/putil.h" -#include "cstring.h" -#include "cmemory.h" -#include "unicode/ustring.h" -#include "unicode/unistr.h" -#include "unicode/utf.h" -#include "unicode/utf16.h" -#include "uelement.h" -#include "ustr_imp.h" -#include "umutex.h" -#include "uassert.h" - -#if 0 - -#include -using namespace std; - -//DEBUGGING -void -print(const UnicodeString& s, - const char *name) -{ - UChar c; - cout << name << ":|"; - for(int i = 0; i < s.length(); ++i) { - c = s[i]; - if(c>= 0x007E || c < 0x0020) - cout << "[0x" << hex << s[i] << "]"; - else - cout << (char) s[i]; - } - cout << '|' << endl; -} - -void -print(const UChar *s, - int32_t len, - const char *name) -{ - UChar c; - cout << name << ":|"; - for(int i = 0; i < len; ++i) { - c = s[i]; - if(c>= 0x007E || c < 0x0020) - cout << "[0x" << hex << s[i] << "]"; - else - cout << (char) s[i]; - } - cout << '|' << endl; -} -// END DEBUGGING -#endif - -// Local function definitions for now - -// need to copy areas that may overlap -static -inline void -us_arrayCopy(const UChar *src, int32_t srcStart, - UChar *dst, int32_t dstStart, int32_t count) -{ - if(count>0) { - uprv_memmove(dst+dstStart, src+srcStart, (size_t)count*sizeof(*src)); - } -} - -// u_unescapeAt() callback to get a UChar from a UnicodeString -U_CDECL_BEGIN -static UChar U_CALLCONV -UnicodeString_charAt(int32_t offset, void *context) { - return ((icu::UnicodeString*) context)->charAt(offset); -} -U_CDECL_END - -U_NAMESPACE_BEGIN - -/* The Replaceable virtual destructor can't be defined in the header - due to how AIX works with multiple definitions of virtual functions. -*/ -Replaceable::~Replaceable() {} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UnicodeString) - -UnicodeString U_EXPORT2 -operator+ (const UnicodeString &s1, const UnicodeString &s2) { - return - UnicodeString(s1.length()+s2.length()+1, (UChar32)0, 0). - append(s1). - append(s2); -} - -//======================================== -// Reference Counting functions, put at top of file so that optimizing compilers -// have a chance to automatically inline. -//======================================== - -void -UnicodeString::addRef() { - umtx_atomic_inc((u_atomic_int32_t *)fUnion.fFields.fArray - 1); -} - -int32_t -UnicodeString::removeRef() { - return umtx_atomic_dec((u_atomic_int32_t *)fUnion.fFields.fArray - 1); -} - -int32_t -UnicodeString::refCount() const { - return umtx_loadAcquire(*((u_atomic_int32_t *)fUnion.fFields.fArray - 1)); -} - -void -UnicodeString::releaseArray() { - if((fUnion.fFields.fLengthAndFlags & kRefCounted) && removeRef() == 0) { - uprv_free((int32_t *)fUnion.fFields.fArray - 1); - } -} - - - -//======================================== -// Constructors -//======================================== - -// The default constructor is inline in unistr.h. - -UnicodeString::UnicodeString(int32_t capacity, UChar32 c, int32_t count) { - fUnion.fFields.fLengthAndFlags = 0; - if(count <= 0 || (uint32_t)c > 0x10ffff) { - // just allocate and do not do anything else - allocate(capacity); - } else if(c <= 0xffff) { - int32_t length = count; - if(capacity < length) { - capacity = length; - } - if(allocate(capacity)) { - UChar *array = getArrayStart(); - UChar unit = (UChar)c; - for(int32_t i = 0; i < length; ++i) { - array[i] = unit; - } - setLength(length); - } - } else { // supplementary code point, write surrogate pairs - if(count > (INT32_MAX / 2)) { - // We would get more than 2G UChars. - allocate(capacity); - return; - } - int32_t length = count * 2; - if(capacity < length) { - capacity = length; - } - if(allocate(capacity)) { - UChar *array = getArrayStart(); - UChar lead = U16_LEAD(c); - UChar trail = U16_TRAIL(c); - for(int32_t i = 0; i < length; i += 2) { - array[i] = lead; - array[i + 1] = trail; - } - setLength(length); - } - } -} - -UnicodeString::UnicodeString(UChar ch) { - fUnion.fFields.fLengthAndFlags = kLength1 | kShortString; - fUnion.fStackFields.fBuffer[0] = ch; -} - -UnicodeString::UnicodeString(UChar32 ch) { - fUnion.fFields.fLengthAndFlags = kShortString; - int32_t i = 0; - UBool isError = false; - U16_APPEND(fUnion.fStackFields.fBuffer, i, US_STACKBUF_SIZE, ch, isError); - // We test isError so that the compiler does not complain that we don't. - // If isError then i==0 which is what we want anyway. - if(!isError) { - setShortLength(i); - } -} - -UnicodeString::UnicodeString(const UChar *text) { - fUnion.fFields.fLengthAndFlags = kShortString; - doAppend(text, 0, -1); -} - -UnicodeString::UnicodeString(const UChar *text, - int32_t textLength) { - fUnion.fFields.fLengthAndFlags = kShortString; - doAppend(text, 0, textLength); -} - -UnicodeString::UnicodeString(UBool isTerminated, - ConstChar16Ptr textPtr, - int32_t textLength) { - fUnion.fFields.fLengthAndFlags = kReadonlyAlias; - const UChar *text = textPtr; - if(text == NULL) { - // treat as an empty string, do not alias - setToEmpty(); - } else if(textLength < -1 || - (textLength == -1 && !isTerminated) || - (textLength >= 0 && isTerminated && text[textLength] != 0) - ) { - setToBogus(); - } else { - if(textLength == -1) { - // text is terminated, or else it would have failed the above test - textLength = u_strlen(text); - } - setArray(const_cast(text), textLength, - isTerminated ? textLength + 1 : textLength); - } -} - -UnicodeString::UnicodeString(UChar *buff, - int32_t buffLength, - int32_t buffCapacity) { - fUnion.fFields.fLengthAndFlags = kWritableAlias; - if(buff == NULL) { - // treat as an empty string, do not alias - setToEmpty(); - } else if(buffLength < -1 || buffCapacity < 0 || buffLength > buffCapacity) { - setToBogus(); - } else { - if(buffLength == -1) { - // fLength = u_strlen(buff); but do not look beyond buffCapacity - const UChar *p = buff, *limit = buff + buffCapacity; - while(p != limit && *p != 0) { - ++p; - } - buffLength = (int32_t)(p - buff); - } - setArray(buff, buffLength, buffCapacity); - } -} - -UnicodeString::UnicodeString(const char *src, int32_t length, EInvariant) { - fUnion.fFields.fLengthAndFlags = kShortString; - if(src==NULL) { - // treat as an empty string - } else { - if(length<0) { - length=(int32_t)uprv_strlen(src); - } - if(cloneArrayIfNeeded(length, length, false)) { - u_charsToUChars(src, getArrayStart(), length); - setLength(length); - } else { - setToBogus(); - } - } -} - -#if U_CHARSET_IS_UTF8 - -UnicodeString::UnicodeString(const char *codepageData) { - fUnion.fFields.fLengthAndFlags = kShortString; - if(codepageData != 0) { - setToUTF8(codepageData); - } -} - -UnicodeString::UnicodeString(const char *codepageData, int32_t dataLength) { - fUnion.fFields.fLengthAndFlags = kShortString; - // if there's nothing to convert, do nothing - if(codepageData == 0 || dataLength == 0 || dataLength < -1) { - return; - } - if(dataLength == -1) { - dataLength = (int32_t)uprv_strlen(codepageData); - } - setToUTF8(StringPiece(codepageData, dataLength)); -} - -// else see unistr_cnv.cpp -#endif - -UnicodeString::UnicodeString(const UnicodeString& that) { - fUnion.fFields.fLengthAndFlags = kShortString; - copyFrom(that); -} - -UnicodeString::UnicodeString(UnicodeString &&src) U_NOEXCEPT { - copyFieldsFrom(src, true); -} - -UnicodeString::UnicodeString(const UnicodeString& that, - int32_t srcStart) { - fUnion.fFields.fLengthAndFlags = kShortString; - setTo(that, srcStart); -} - -UnicodeString::UnicodeString(const UnicodeString& that, - int32_t srcStart, - int32_t srcLength) { - fUnion.fFields.fLengthAndFlags = kShortString; - setTo(that, srcStart, srcLength); -} - -// Replaceable base class clone() default implementation, does not clone -Replaceable * -Replaceable::clone() const { - return NULL; -} - -// UnicodeString overrides clone() with a real implementation -UnicodeString * -UnicodeString::clone() const { - LocalPointer clonedString(new UnicodeString(*this)); - return clonedString.isValid() && !clonedString->isBogus() ? clonedString.orphan() : nullptr; -} - -//======================================== -// array allocation -//======================================== - -namespace { - -const int32_t kGrowSize = 128; - -// The number of bytes for one int32_t reference counter and capacity UChars -// must fit into a 32-bit size_t (at least when on a 32-bit platform). -// We also add one for the NUL terminator, to avoid reallocation in getTerminatedBuffer(), -// and round up to a multiple of 16 bytes. -// This means that capacity must be at most (0xfffffff0 - 4) / 2 - 1 = 0x7ffffff5. -// (With more complicated checks we could go up to 0x7ffffffd without rounding up, -// but that does not seem worth it.) -const int32_t kMaxCapacity = 0x7ffffff5; - -int32_t getGrowCapacity(int32_t newLength) { - int32_t growSize = (newLength >> 2) + kGrowSize; - if(growSize <= (kMaxCapacity - newLength)) { - return newLength + growSize; - } else { - return kMaxCapacity; - } -} - -} // namespace - -UBool -UnicodeString::allocate(int32_t capacity) { - if(capacity <= US_STACKBUF_SIZE) { - fUnion.fFields.fLengthAndFlags = kShortString; - return true; - } - if(capacity <= kMaxCapacity) { - ++capacity; // for the NUL - // Switch to size_t which is unsigned so that we can allocate up to 4GB. - // Reference counter + UChars. - size_t numBytes = sizeof(int32_t) + (size_t)capacity * U_SIZEOF_UCHAR; - // Round up to a multiple of 16. - numBytes = (numBytes + 15) & ~15; - int32_t *array = (int32_t *) uprv_malloc(numBytes); - if(array != NULL) { - // set initial refCount and point behind the refCount - *array++ = 1; - numBytes -= sizeof(int32_t); - - // have fArray point to the first UChar - fUnion.fFields.fArray = (UChar *)array; - fUnion.fFields.fCapacity = (int32_t)(numBytes / U_SIZEOF_UCHAR); - fUnion.fFields.fLengthAndFlags = kLongString; - return true; - } - } - fUnion.fFields.fLengthAndFlags = kIsBogus; - fUnion.fFields.fArray = 0; - fUnion.fFields.fCapacity = 0; - return false; -} - -//======================================== -// Destructor -//======================================== - -#ifdef UNISTR_COUNT_FINAL_STRING_LENGTHS -static u_atomic_int32_t finalLengthCounts[0x400]; // UnicodeString::kMaxShortLength+1 -static u_atomic_int32_t beyondCount(0); - -U_CAPI void unistr_printLengths() { - int32_t i; - for(i = 0; i <= 59; ++i) { - printf("%2d, %9d\n", i, (int32_t)finalLengthCounts[i]); - } - int32_t beyond = beyondCount; - for(; i < UPRV_LENGTHOF(finalLengthCounts); ++i) { - beyond += finalLengthCounts[i]; - } - printf(">59, %9d\n", beyond); -} -#endif - -UnicodeString::~UnicodeString() -{ -#ifdef UNISTR_COUNT_FINAL_STRING_LENGTHS - // Count lengths of strings at the end of their lifetime. - // Useful for discussion of a desirable stack buffer size. - // Count the contents length, not the optional NUL terminator nor further capacity. - // Ignore open-buffer strings and strings which alias external storage. - if((fUnion.fFields.fLengthAndFlags&(kOpenGetBuffer|kReadonlyAlias|kWritableAlias)) == 0) { - if(hasShortLength()) { - umtx_atomic_inc(finalLengthCounts + getShortLength()); - } else { - umtx_atomic_inc(&beyondCount); - } - } -#endif - - releaseArray(); -} - -//======================================== -// Factory methods -//======================================== - -UnicodeString UnicodeString::fromUTF8(StringPiece utf8) { - UnicodeString result; - result.setToUTF8(utf8); - return result; -} - -UnicodeString UnicodeString::fromUTF32(const UChar32 *utf32, int32_t length) { - UnicodeString result; - int32_t capacity; - // Most UTF-32 strings will be BMP-only and result in a same-length - // UTF-16 string. We overestimate the capacity just slightly, - // just in case there are a few supplementary characters. - if(length <= US_STACKBUF_SIZE) { - capacity = US_STACKBUF_SIZE; - } else { - capacity = length + (length >> 4) + 4; - } - do { - UChar *utf16 = result.getBuffer(capacity); - int32_t length16; - UErrorCode errorCode = U_ZERO_ERROR; - u_strFromUTF32WithSub(utf16, result.getCapacity(), &length16, - utf32, length, - 0xfffd, // Substitution character. - NULL, // Don't care about number of substitutions. - &errorCode); - result.releaseBuffer(length16); - if(errorCode == U_BUFFER_OVERFLOW_ERROR) { - capacity = length16 + 1; // +1 for the terminating NUL. - continue; - } else if(U_FAILURE(errorCode)) { - result.setToBogus(); - } - break; - } while(true); - return result; -} - -//======================================== -// Assignment -//======================================== - -UnicodeString & -UnicodeString::operator=(const UnicodeString &src) { - return copyFrom(src); -} - -UnicodeString & -UnicodeString::fastCopyFrom(const UnicodeString &src) { - return copyFrom(src, true); -} - -UnicodeString & -UnicodeString::copyFrom(const UnicodeString &src, UBool fastCopy) { - // if assigning to ourselves, do nothing - if(this == &src) { - return *this; - } - - // is the right side bogus? - if(src.isBogus()) { - setToBogus(); - return *this; - } - - // delete the current contents - releaseArray(); - - if(src.isEmpty()) { - // empty string - use the stack buffer - setToEmpty(); - return *this; - } - - // fLength>0 and not an "open" src.getBuffer(minCapacity) - fUnion.fFields.fLengthAndFlags = src.fUnion.fFields.fLengthAndFlags; - switch(src.fUnion.fFields.fLengthAndFlags & kAllStorageFlags) { - case kShortString: - // short string using the stack buffer, do the same - uprv_memcpy(fUnion.fStackFields.fBuffer, src.fUnion.fStackFields.fBuffer, - getShortLength() * U_SIZEOF_UCHAR); - break; - case kLongString: - // src uses a refCounted string buffer, use that buffer with refCount - // src is const, use a cast - we don't actually change it - ((UnicodeString &)src).addRef(); - // copy all fields, share the reference-counted buffer - fUnion.fFields.fArray = src.fUnion.fFields.fArray; - fUnion.fFields.fCapacity = src.fUnion.fFields.fCapacity; - if(!hasShortLength()) { - fUnion.fFields.fLength = src.fUnion.fFields.fLength; - } - break; - case kReadonlyAlias: - if(fastCopy) { - // src is a readonly alias, do the same - // -> maintain the readonly alias as such - fUnion.fFields.fArray = src.fUnion.fFields.fArray; - fUnion.fFields.fCapacity = src.fUnion.fFields.fCapacity; - if(!hasShortLength()) { - fUnion.fFields.fLength = src.fUnion.fFields.fLength; - } - break; - } - // else if(!fastCopy) fall through to case kWritableAlias - // -> allocate a new buffer and copy the contents - U_FALLTHROUGH; - case kWritableAlias: { - // src is a writable alias; we make a copy of that instead - int32_t srcLength = src.length(); - if(allocate(srcLength)) { - u_memcpy(getArrayStart(), src.getArrayStart(), srcLength); - setLength(srcLength); - break; - } - // if there is not enough memory, then fall through to setting to bogus - U_FALLTHROUGH; - } - default: - // if src is bogus, set ourselves to bogus - // do not call setToBogus() here because fArray and flags are not consistent here - fUnion.fFields.fLengthAndFlags = kIsBogus; - fUnion.fFields.fArray = 0; - fUnion.fFields.fCapacity = 0; - break; - } - - return *this; -} - -UnicodeString &UnicodeString::operator=(UnicodeString &&src) U_NOEXCEPT { - // No explicit check for self move assignment, consistent with standard library. - // Self move assignment causes no crash nor leak but might make the object bogus. - releaseArray(); - copyFieldsFrom(src, true); - return *this; -} - -// Same as move assignment except without memory management. -void UnicodeString::copyFieldsFrom(UnicodeString &src, UBool setSrcToBogus) U_NOEXCEPT { - int16_t lengthAndFlags = fUnion.fFields.fLengthAndFlags = src.fUnion.fFields.fLengthAndFlags; - if(lengthAndFlags & kUsingStackBuffer) { - // Short string using the stack buffer, copy the contents. - // Check for self assignment to prevent "overlap in memcpy" warnings, - // although it should be harmless to copy a buffer to itself exactly. - if(this != &src) { - uprv_memcpy(fUnion.fStackFields.fBuffer, src.fUnion.fStackFields.fBuffer, - getShortLength() * U_SIZEOF_UCHAR); - } - } else { - // In all other cases, copy all fields. - fUnion.fFields.fArray = src.fUnion.fFields.fArray; - fUnion.fFields.fCapacity = src.fUnion.fFields.fCapacity; - if(!hasShortLength()) { - fUnion.fFields.fLength = src.fUnion.fFields.fLength; - } - if(setSrcToBogus) { - // Set src to bogus without releasing any memory. - src.fUnion.fFields.fLengthAndFlags = kIsBogus; - src.fUnion.fFields.fArray = NULL; - src.fUnion.fFields.fCapacity = 0; - } - } -} - -void UnicodeString::swap(UnicodeString &other) U_NOEXCEPT { - UnicodeString temp; // Empty short string: Known not to need releaseArray(). - // Copy fields without resetting source values in between. - temp.copyFieldsFrom(*this, false); - this->copyFieldsFrom(other, false); - other.copyFieldsFrom(temp, false); - // Set temp to an empty string so that other's memory is not released twice. - temp.fUnion.fFields.fLengthAndFlags = kShortString; -} - -//======================================== -// Miscellaneous operations -//======================================== - -UnicodeString UnicodeString::unescape() const { - UnicodeString result(length(), (UChar32)0, (int32_t)0); // construct with capacity - if (result.isBogus()) { - return result; - } - const UChar *array = getBuffer(); - int32_t len = length(); - int32_t prev = 0; - for (int32_t i=0;;) { - if (i == len) { - result.append(array, prev, len - prev); - break; - } - if (array[i++] == 0x5C /*'\\'*/) { - result.append(array, prev, (i - 1) - prev); - UChar32 c = unescapeAt(i); // advances i - if (c < 0) { - result.remove(); // return empty string - break; // invalid escape sequence - } - result.append(c); - prev = i; - } - } - return result; -} - -UChar32 UnicodeString::unescapeAt(int32_t &offset) const { - return u_unescapeAt(UnicodeString_charAt, &offset, length(), (void*)this); -} - -//======================================== -// Read-only implementation -//======================================== -UBool -UnicodeString::doEquals(const UnicodeString &text, int32_t len) const { - // Requires: this & text not bogus and have same lengths. - // Byte-wise comparison works for equality regardless of endianness. - return uprv_memcmp(getArrayStart(), text.getArrayStart(), len * U_SIZEOF_UCHAR) == 0; -} - -int8_t -UnicodeString::doCompare( int32_t start, - int32_t length, - const UChar *srcChars, - int32_t srcStart, - int32_t srcLength) const -{ - // compare illegal string values - if(isBogus()) { - return -1; - } - - // pin indices to legal values - pinIndices(start, length); - - if(srcChars == NULL) { - // treat const UChar *srcChars==NULL as an empty string - return length == 0 ? 0 : 1; - } - - // get the correct pointer - const UChar *chars = getArrayStart(); - - chars += start; - srcChars += srcStart; - - int32_t minLength; - int8_t lengthResult; - - // get the srcLength if necessary - if(srcLength < 0) { - srcLength = u_strlen(srcChars + srcStart); - } - - // are we comparing different lengths? - if(length != srcLength) { - if(length < srcLength) { - minLength = length; - lengthResult = -1; - } else { - minLength = srcLength; - lengthResult = 1; - } - } else { - minLength = length; - lengthResult = 0; - } - - /* - * note that uprv_memcmp() returns an int but we return an int8_t; - * we need to take care not to truncate the result - - * one way to do this is to right-shift the value to - * move the sign bit into the lower 8 bits and making sure that this - * does not become 0 itself - */ - - if(minLength > 0 && chars != srcChars) { - int32_t result; - -# if U_IS_BIG_ENDIAN - // big-endian: byte comparison works - result = uprv_memcmp(chars, srcChars, minLength * sizeof(UChar)); - if(result != 0) { - return (int8_t)(result >> 15 | 1); - } -# else - // little-endian: compare UChar units - do { - result = ((int32_t)*(chars++) - (int32_t)*(srcChars++)); - if(result != 0) { - return (int8_t)(result >> 15 | 1); - } - } while(--minLength > 0); -# endif - } - return lengthResult; -} - -/* String compare in code point order - doCompare() compares in code unit order. */ -int8_t -UnicodeString::doCompareCodePointOrder(int32_t start, - int32_t length, - const UChar *srcChars, - int32_t srcStart, - int32_t srcLength) const -{ - // compare illegal string values - // treat const UChar *srcChars==NULL as an empty string - if(isBogus()) { - return -1; - } - - // pin indices to legal values - pinIndices(start, length); - - if(srcChars == NULL) { - srcStart = srcLength = 0; - } - - int32_t diff = uprv_strCompare(getArrayStart() + start, length, (srcChars!=NULL)?(srcChars + srcStart):NULL, srcLength, false, true); - /* translate the 32-bit result into an 8-bit one */ - if(diff!=0) { - return (int8_t)(diff >> 15 | 1); - } else { - return 0; - } -} - -int32_t -UnicodeString::getLength() const { - return length(); -} - -UChar -UnicodeString::getCharAt(int32_t offset) const { - return charAt(offset); -} - -UChar32 -UnicodeString::getChar32At(int32_t offset) const { - return char32At(offset); -} - -UChar32 -UnicodeString::char32At(int32_t offset) const -{ - int32_t len = length(); - if((uint32_t)offset < (uint32_t)len) { - const UChar *array = getArrayStart(); - UChar32 c; - U16_GET(array, 0, offset, len, c); - return c; - } else { - return kInvalidUChar; - } -} - -int32_t -UnicodeString::getChar32Start(int32_t offset) const { - if((uint32_t)offset < (uint32_t)length()) { - const UChar *array = getArrayStart(); - U16_SET_CP_START(array, 0, offset); - return offset; - } else { - return 0; - } -} - -int32_t -UnicodeString::getChar32Limit(int32_t offset) const { - int32_t len = length(); - if((uint32_t)offset < (uint32_t)len) { - const UChar *array = getArrayStart(); - U16_SET_CP_LIMIT(array, 0, offset, len); - return offset; - } else { - return len; - } -} - -int32_t -UnicodeString::countChar32(int32_t start, int32_t length) const { - pinIndices(start, length); - // if(isBogus()) then fArray==0 and start==0 - u_countChar32() checks for NULL - return u_countChar32(getArrayStart()+start, length); -} - -UBool -UnicodeString::hasMoreChar32Than(int32_t start, int32_t length, int32_t number) const { - pinIndices(start, length); - // if(isBogus()) then fArray==0 and start==0 - u_strHasMoreChar32Than() checks for NULL - return u_strHasMoreChar32Than(getArrayStart()+start, length, number); -} - -int32_t -UnicodeString::moveIndex32(int32_t index, int32_t delta) const { - // pin index - int32_t len = length(); - if(index<0) { - index=0; - } else if(index>len) { - index=len; - } - - const UChar *array = getArrayStart(); - if(delta>0) { - U16_FWD_N(array, index, len, delta); - } else { - U16_BACK_N(array, 0, index, -delta); - } - - return index; -} - -void -UnicodeString::doExtract(int32_t start, - int32_t length, - UChar *dst, - int32_t dstStart) const -{ - // pin indices to legal values - pinIndices(start, length); - - // do not copy anything if we alias dst itself - const UChar *array = getArrayStart(); - if(array + start != dst + dstStart) { - us_arrayCopy(array, start, dst, dstStart, length); - } -} - -int32_t -UnicodeString::extract(Char16Ptr dest, int32_t destCapacity, - UErrorCode &errorCode) const { - int32_t len = length(); - if(U_SUCCESS(errorCode)) { - if(isBogus() || destCapacity<0 || (destCapacity>0 && dest==0)) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - } else { - const UChar *array = getArrayStart(); - if(len>0 && len<=destCapacity && array!=dest) { - u_memcpy(dest, array, len); - } - return u_terminateUChars(dest, destCapacity, len, &errorCode); - } - } - - return len; -} - -int32_t -UnicodeString::extract(int32_t start, - int32_t length, - char *target, - int32_t targetCapacity, - enum EInvariant) const -{ - // if the arguments are illegal, then do nothing - if(targetCapacity < 0 || (targetCapacity > 0 && target == NULL)) { - return 0; - } - - // pin the indices to legal values - pinIndices(start, length); - - if(length <= targetCapacity) { - u_UCharsToChars(getArrayStart() + start, target, length); - } - UErrorCode status = U_ZERO_ERROR; - return u_terminateChars(target, targetCapacity, length, &status); -} - -UnicodeString -UnicodeString::tempSubString(int32_t start, int32_t len) const { - pinIndices(start, len); - const UChar *array = getBuffer(); // not getArrayStart() to check kIsBogus & kOpenGetBuffer - if(array==NULL) { - array=fUnion.fStackFields.fBuffer; // anything not NULL because that would make an empty string - len=-2; // bogus result string - } - return UnicodeString(false, array + start, len); -} - -int32_t -UnicodeString::toUTF8(int32_t start, int32_t len, - char *target, int32_t capacity) const { - pinIndices(start, len); - int32_t length8; - UErrorCode errorCode = U_ZERO_ERROR; - u_strToUTF8WithSub(target, capacity, &length8, - getBuffer() + start, len, - 0xFFFD, // Standard substitution character. - NULL, // Don't care about number of substitutions. - &errorCode); - return length8; -} - -#if U_CHARSET_IS_UTF8 - -int32_t -UnicodeString::extract(int32_t start, int32_t len, - char *target, uint32_t dstSize) const { - // if the arguments are illegal, then do nothing - if(/*dstSize < 0 || */(dstSize > 0 && target == 0)) { - return 0; - } - return toUTF8(start, len, target, dstSize <= 0x7fffffff ? (int32_t)dstSize : 0x7fffffff); -} - -// else see unistr_cnv.cpp -#endif - -void -UnicodeString::extractBetween(int32_t start, - int32_t limit, - UnicodeString& target) const { - pinIndex(start); - pinIndex(limit); - doExtract(start, limit - start, target); -} - -// When converting from UTF-16 to UTF-8, the result will have at most 3 times -// as many bytes as the source has UChars. -// The "worst cases" are writing systems like Indic, Thai and CJK with -// 3:1 bytes:UChars. -void -UnicodeString::toUTF8(ByteSink &sink) const { - int32_t length16 = length(); - if(length16 != 0) { - char stackBuffer[1024]; - int32_t capacity = (int32_t)sizeof(stackBuffer); - UBool utf8IsOwned = false; - char *utf8 = sink.GetAppendBuffer(length16 < capacity ? length16 : capacity, - 3*length16, - stackBuffer, capacity, - &capacity); - int32_t length8 = 0; - UErrorCode errorCode = U_ZERO_ERROR; - u_strToUTF8WithSub(utf8, capacity, &length8, - getBuffer(), length16, - 0xFFFD, // Standard substitution character. - NULL, // Don't care about number of substitutions. - &errorCode); - if(errorCode == U_BUFFER_OVERFLOW_ERROR) { - utf8 = (char *)uprv_malloc(length8); - if(utf8 != NULL) { - utf8IsOwned = true; - errorCode = U_ZERO_ERROR; - u_strToUTF8WithSub(utf8, length8, &length8, - getBuffer(), length16, - 0xFFFD, // Standard substitution character. - NULL, // Don't care about number of substitutions. - &errorCode); - } else { - errorCode = U_MEMORY_ALLOCATION_ERROR; - } - } - if(U_SUCCESS(errorCode)) { - sink.Append(utf8, length8); - sink.Flush(); - } - if(utf8IsOwned) { - uprv_free(utf8); - } - } -} - -int32_t -UnicodeString::toUTF32(UChar32 *utf32, int32_t capacity, UErrorCode &errorCode) const { - int32_t length32=0; - if(U_SUCCESS(errorCode)) { - // getBuffer() and u_strToUTF32WithSub() check for illegal arguments. - u_strToUTF32WithSub(utf32, capacity, &length32, - getBuffer(), length(), - 0xfffd, // Substitution character. - NULL, // Don't care about number of substitutions. - &errorCode); - } - return length32; -} - -int32_t -UnicodeString::indexOf(const UChar *srcChars, - int32_t srcStart, - int32_t srcLength, - int32_t start, - int32_t length) const -{ - if(isBogus() || srcChars == 0 || srcStart < 0 || srcLength == 0) { - return -1; - } - - // UnicodeString does not find empty substrings - if(srcLength < 0 && srcChars[srcStart] == 0) { - return -1; - } - - // get the indices within bounds - pinIndices(start, length); - - // find the first occurrence of the substring - const UChar *array = getArrayStart(); - const UChar *match = u_strFindFirst(array + start, length, srcChars + srcStart, srcLength); - if(match == NULL) { - return -1; - } else { - return (int32_t)(match - array); - } -} - -int32_t -UnicodeString::doIndexOf(UChar c, - int32_t start, - int32_t length) const -{ - // pin indices - pinIndices(start, length); - - // find the first occurrence of c - const UChar *array = getArrayStart(); - const UChar *match = u_memchr(array + start, c, length); - if(match == NULL) { - return -1; - } else { - return (int32_t)(match - array); - } -} - -int32_t -UnicodeString::doIndexOf(UChar32 c, - int32_t start, - int32_t length) const { - // pin indices - pinIndices(start, length); - - // find the first occurrence of c - const UChar *array = getArrayStart(); - const UChar *match = u_memchr32(array + start, c, length); - if(match == NULL) { - return -1; - } else { - return (int32_t)(match - array); - } -} - -int32_t -UnicodeString::lastIndexOf(const UChar *srcChars, - int32_t srcStart, - int32_t srcLength, - int32_t start, - int32_t length) const -{ - if(isBogus() || srcChars == 0 || srcStart < 0 || srcLength == 0) { - return -1; - } - - // UnicodeString does not find empty substrings - if(srcLength < 0 && srcChars[srcStart] == 0) { - return -1; - } - - // get the indices within bounds - pinIndices(start, length); - - // find the last occurrence of the substring - const UChar *array = getArrayStart(); - const UChar *match = u_strFindLast(array + start, length, srcChars + srcStart, srcLength); - if(match == NULL) { - return -1; - } else { - return (int32_t)(match - array); - } -} - -int32_t -UnicodeString::doLastIndexOf(UChar c, - int32_t start, - int32_t length) const -{ - if(isBogus()) { - return -1; - } - - // pin indices - pinIndices(start, length); - - // find the last occurrence of c - const UChar *array = getArrayStart(); - const UChar *match = u_memrchr(array + start, c, length); - if(match == NULL) { - return -1; - } else { - return (int32_t)(match - array); - } -} - -int32_t -UnicodeString::doLastIndexOf(UChar32 c, - int32_t start, - int32_t length) const { - // pin indices - pinIndices(start, length); - - // find the last occurrence of c - const UChar *array = getArrayStart(); - const UChar *match = u_memrchr32(array + start, c, length); - if(match == NULL) { - return -1; - } else { - return (int32_t)(match - array); - } -} - -//======================================== -// Write implementation -//======================================== - -UnicodeString& -UnicodeString::findAndReplace(int32_t start, - int32_t length, - const UnicodeString& oldText, - int32_t oldStart, - int32_t oldLength, - const UnicodeString& newText, - int32_t newStart, - int32_t newLength) -{ - if(isBogus() || oldText.isBogus() || newText.isBogus()) { - return *this; - } - - pinIndices(start, length); - oldText.pinIndices(oldStart, oldLength); - newText.pinIndices(newStart, newLength); - - if(oldLength == 0) { - return *this; - } - - while(length > 0 && length >= oldLength) { - int32_t pos = indexOf(oldText, oldStart, oldLength, start, length); - if(pos < 0) { - // no more oldText's here: done - break; - } else { - // we found oldText, replace it by newText and go beyond it - replace(pos, oldLength, newText, newStart, newLength); - length -= pos + oldLength - start; - start = pos + newLength; - } - } - - return *this; -} - - -void -UnicodeString::setToBogus() -{ - releaseArray(); - - fUnion.fFields.fLengthAndFlags = kIsBogus; - fUnion.fFields.fArray = 0; - fUnion.fFields.fCapacity = 0; -} - -// turn a bogus string into an empty one -void -UnicodeString::unBogus() { - if(fUnion.fFields.fLengthAndFlags & kIsBogus) { - setToEmpty(); - } -} - -const char16_t * -UnicodeString::getTerminatedBuffer() { - if(!isWritable()) { - return nullptr; - } - UChar *array = getArrayStart(); - int32_t len = length(); - if(len < getCapacity()) { - if(fUnion.fFields.fLengthAndFlags & kBufferIsReadonly) { - // If len= 0 && isTerminated && text[textLength] != 0) - ) { - setToBogus(); - return *this; - } - - releaseArray(); - - if(textLength == -1) { - // text is terminated, or else it would have failed the above test - textLength = u_strlen(text); - } - fUnion.fFields.fLengthAndFlags = kReadonlyAlias; - setArray((UChar *)text, textLength, isTerminated ? textLength + 1 : textLength); - return *this; -} - -// setTo() analogous to the writable-aliasing constructor with the same signature -UnicodeString & -UnicodeString::setTo(UChar *buffer, - int32_t buffLength, - int32_t buffCapacity) { - if(fUnion.fFields.fLengthAndFlags & kOpenGetBuffer) { - // do not modify a string that has an "open" getBuffer(minCapacity) - return *this; - } - - if(buffer == NULL) { - // treat as an empty string, do not alias - releaseArray(); - setToEmpty(); - return *this; - } - - if(buffLength < -1 || buffCapacity < 0 || buffLength > buffCapacity) { - setToBogus(); - return *this; - } else if(buffLength == -1) { - // buffLength = u_strlen(buff); but do not look beyond buffCapacity - const UChar *p = buffer, *limit = buffer + buffCapacity; - while(p != limit && *p != 0) { - ++p; - } - buffLength = (int32_t)(p - buffer); - } - - releaseArray(); - - fUnion.fFields.fLengthAndFlags = kWritableAlias; - setArray(buffer, buffLength, buffCapacity); - return *this; -} - -UnicodeString &UnicodeString::setToUTF8(StringPiece utf8) { - unBogus(); - int32_t length = utf8.length(); - int32_t capacity; - // The UTF-16 string will be at most as long as the UTF-8 string. - if(length <= US_STACKBUF_SIZE) { - capacity = US_STACKBUF_SIZE; - } else { - capacity = length + 1; // +1 for the terminating NUL. - } - UChar *utf16 = getBuffer(capacity); - int32_t length16; - UErrorCode errorCode = U_ZERO_ERROR; - u_strFromUTF8WithSub(utf16, getCapacity(), &length16, - utf8.data(), length, - 0xfffd, // Substitution character. - NULL, // Don't care about number of substitutions. - &errorCode); - releaseBuffer(length16); - if(U_FAILURE(errorCode)) { - setToBogus(); - } - return *this; -} - -UnicodeString& -UnicodeString::setCharAt(int32_t offset, - UChar c) -{ - int32_t len = length(); - if(cloneArrayIfNeeded() && len > 0) { - if(offset < 0) { - offset = 0; - } else if(offset >= len) { - offset = len - 1; - } - - getArrayStart()[offset] = c; - } - return *this; -} - -UnicodeString& -UnicodeString::replace(int32_t start, - int32_t _length, - UChar32 srcChar) { - UChar buffer[U16_MAX_LENGTH]; - int32_t count = 0; - UBool isError = false; - U16_APPEND(buffer, count, U16_MAX_LENGTH, srcChar, isError); - // We test isError so that the compiler does not complain that we don't. - // If isError (srcChar is not a valid code point) then count==0 which means - // we remove the source segment rather than replacing it with srcChar. - return doReplace(start, _length, buffer, 0, isError ? 0 : count); -} - -UnicodeString& -UnicodeString::append(UChar32 srcChar) { - UChar buffer[U16_MAX_LENGTH]; - int32_t _length = 0; - UBool isError = false; - U16_APPEND(buffer, _length, U16_MAX_LENGTH, srcChar, isError); - // We test isError so that the compiler does not complain that we don't. - // If isError then _length==0 which turns the doAppend() into a no-op anyway. - return isError ? *this : doAppend(buffer, 0, _length); -} - -UnicodeString& -UnicodeString::doReplace( int32_t start, - int32_t length, - const UnicodeString& src, - int32_t srcStart, - int32_t srcLength) -{ - // pin the indices to legal values - src.pinIndices(srcStart, srcLength); - - // get the characters from src - // and replace the range in ourselves with them - return doReplace(start, length, src.getArrayStart(), srcStart, srcLength); -} - -UnicodeString& -UnicodeString::doReplace(int32_t start, - int32_t length, - const UChar *srcChars, - int32_t srcStart, - int32_t srcLength) -{ - if(!isWritable()) { - return *this; - } - - int32_t oldLength = this->length(); - - // optimize (read-only alias).remove(0, start) and .remove(start, end) - if((fUnion.fFields.fLengthAndFlags&kBufferIsReadonly) && srcLength == 0) { - if(start == 0) { - // remove prefix by adjusting the array pointer - pinIndex(length); - fUnion.fFields.fArray += length; - fUnion.fFields.fCapacity -= length; - setLength(oldLength - length); - return *this; - } else { - pinIndex(start); - if(length >= (oldLength - start)) { - // remove suffix by reducing the length (like truncate()) - setLength(start); - fUnion.fFields.fCapacity = start; // not NUL-terminated any more - return *this; - } - } - } - - if(start == oldLength) { - return doAppend(srcChars, srcStart, srcLength); - } - - if(srcChars == 0) { - srcLength = 0; - } else { - // Perform all remaining operations relative to srcChars + srcStart. - // From this point forward, do not use srcStart. - srcChars += srcStart; - if (srcLength < 0) { - // get the srcLength if necessary - srcLength = u_strlen(srcChars); - } - } - - // pin the indices to legal values - pinIndices(start, length); - - // Calculate the size of the string after the replace. - // Avoid int32_t overflow. - int32_t newLength = oldLength - length; - if(srcLength > (INT32_MAX - newLength)) { - setToBogus(); - return *this; - } - newLength += srcLength; - - // Check for insertion into ourself - const UChar *oldArray = getArrayStart(); - if (isBufferWritable() && - oldArray < srcChars + srcLength && - srcChars < oldArray + oldLength) { - // Copy into a new UnicodeString and start over - UnicodeString copy(srcChars, srcLength); - if (copy.isBogus()) { - setToBogus(); - return *this; - } - return doReplace(start, length, copy.getArrayStart(), 0, srcLength); - } - - // cloneArrayIfNeeded(doCopyArray=false) may change fArray but will not copy the current contents; - // therefore we need to keep the current fArray - UChar oldStackBuffer[US_STACKBUF_SIZE]; - if((fUnion.fFields.fLengthAndFlags&kUsingStackBuffer) && (newLength > US_STACKBUF_SIZE)) { - // copy the stack buffer contents because it will be overwritten with - // fUnion.fFields values - u_memcpy(oldStackBuffer, oldArray, oldLength); - oldArray = oldStackBuffer; - } - - // clone our array and allocate a bigger array if needed - int32_t *bufferToDelete = 0; - if(!cloneArrayIfNeeded(newLength, getGrowCapacity(newLength), - false, &bufferToDelete) - ) { - return *this; - } - - // now do the replace - - UChar *newArray = getArrayStart(); - if(newArray != oldArray) { - // if fArray changed, then we need to copy everything except what will change - us_arrayCopy(oldArray, 0, newArray, 0, start); - us_arrayCopy(oldArray, start + length, - newArray, start + srcLength, - oldLength - (start + length)); - } else if(length != srcLength) { - // fArray did not change; copy only the portion that isn't changing, leaving a hole - us_arrayCopy(oldArray, start + length, - newArray, start + srcLength, - oldLength - (start + length)); - } - - // now fill in the hole with the new string - us_arrayCopy(srcChars, 0, newArray, start, srcLength); - - setLength(newLength); - - // delayed delete in case srcChars == fArray when we started, and - // to keep oldArray alive for the above operations - if (bufferToDelete) { - uprv_free(bufferToDelete); - } - - return *this; -} - -// Versions of doReplace() only for append() variants. -// doReplace() and doAppend() optimize for different cases. - -UnicodeString& -UnicodeString::doAppend(const UnicodeString& src, int32_t srcStart, int32_t srcLength) { - if(srcLength == 0) { - return *this; - } - - // pin the indices to legal values - src.pinIndices(srcStart, srcLength); - return doAppend(src.getArrayStart(), srcStart, srcLength); -} - -UnicodeString& -UnicodeString::doAppend(const UChar *srcChars, int32_t srcStart, int32_t srcLength) { - if(!isWritable() || srcLength == 0 || srcChars == NULL) { - return *this; - } - - // Perform all remaining operations relative to srcChars + srcStart. - // From this point forward, do not use srcStart. - srcChars += srcStart; - - if(srcLength < 0) { - // get the srcLength if necessary - if((srcLength = u_strlen(srcChars)) == 0) { - return *this; - } - } - - int32_t oldLength = length(); - int32_t newLength; - if (uprv_add32_overflow(oldLength, srcLength, &newLength)) { - setToBogus(); - return *this; - } - - // Check for append onto ourself - const UChar* oldArray = getArrayStart(); - if (isBufferWritable() && - oldArray < srcChars + srcLength && - srcChars < oldArray + oldLength) { - // Copy into a new UnicodeString and start over - UnicodeString copy(srcChars, srcLength); - if (copy.isBogus()) { - setToBogus(); - return *this; - } - return doAppend(copy.getArrayStart(), 0, srcLength); - } - - // optimize append() onto a large-enough, owned string - if((newLength <= getCapacity() && isBufferWritable()) || - cloneArrayIfNeeded(newLength, getGrowCapacity(newLength))) { - UChar *newArray = getArrayStart(); - // Do not copy characters when - // UChar *buffer=str.getAppendBuffer(...); - // is followed by - // str.append(buffer, length); - // or - // str.appendString(buffer, length) - // or similar. - if(srcChars != newArray + oldLength) { - us_arrayCopy(srcChars, 0, newArray, oldLength, srcLength); - } - setLength(newLength); - } - return *this; -} - -/** - * Replaceable API - */ -void -UnicodeString::handleReplaceBetween(int32_t start, - int32_t limit, - const UnicodeString& text) { - replaceBetween(start, limit, text); -} - -/** - * Replaceable API - */ -void -UnicodeString::copy(int32_t start, int32_t limit, int32_t dest) { - if (limit <= start) { - return; // Nothing to do; avoid bogus malloc call - } - UChar* text = (UChar*) uprv_malloc( sizeof(UChar) * (limit - start) ); - // Check to make sure text is not null. - if (text != NULL) { - extractBetween(start, limit, text, 0); - insert(dest, text, 0, limit - start); - uprv_free(text); - } -} - -/** - * Replaceable API - * - * NOTE: This is for the Replaceable class. There is no rep.cpp, - * so we implement this function here. - */ -UBool Replaceable::hasMetaData() const { - return true; -} - -/** - * Replaceable API - */ -UBool UnicodeString::hasMetaData() const { - return false; -} - -UnicodeString& -UnicodeString::doReverse(int32_t start, int32_t length) { - if(length <= 1 || !cloneArrayIfNeeded()) { - return *this; - } - - // pin the indices to legal values - pinIndices(start, length); - if(length <= 1) { // pinIndices() might have shrunk the length - return *this; - } - - UChar *left = getArrayStart() + start; - UChar *right = left + length - 1; // -1 for inclusive boundary (length>=2) - UChar swap; - UBool hasSupplementary = false; - - // Before the loop we know left=2. - do { - hasSupplementary |= (UBool)U16_IS_LEAD(swap = *left); - hasSupplementary |= (UBool)U16_IS_LEAD(*left++ = *right); - *right-- = swap; - } while(left < right); - // Make sure to test the middle code unit of an odd-length string. - // Redundant if the length is even. - hasSupplementary |= (UBool)U16_IS_LEAD(*left); - - /* if there are supplementary code points in the reversed range, then re-swap their surrogates */ - if(hasSupplementary) { - UChar swap2; - - left = getArrayStart() + start; - right = left + length - 1; // -1 so that we can look at *(left+1) if left= targetLength || !cloneArrayIfNeeded(targetLength)) { - return false; - } else { - // move contents up by padding width - UChar *array = getArrayStart(); - int32_t start = targetLength - oldLength; - us_arrayCopy(array, 0, array, start, oldLength); - - // fill in padding character - while(--start >= 0) { - array[start] = padChar; - } - setLength(targetLength); - return true; - } -} - -UBool -UnicodeString::padTrailing(int32_t targetLength, - UChar padChar) -{ - int32_t oldLength = length(); - if(oldLength >= targetLength || !cloneArrayIfNeeded(targetLength)) { - return false; - } else { - // fill in padding character - UChar *array = getArrayStart(); - int32_t length = targetLength; - while(--length >= oldLength) { - array[length] = padChar; - } - setLength(targetLength); - return true; - } -} - -//======================================== -// Hashing -//======================================== -int32_t -UnicodeString::doHashCode() const -{ - /* Delegate hash computation to uhash. This makes UnicodeString - * hashing consistent with UChar* hashing. */ - int32_t hashCode = ustr_hashUCharsN(getArrayStart(), length()); - if (hashCode == kInvalidHashCode) { - hashCode = kEmptyHashCode; - } - return hashCode; -} - -//======================================== -// External Buffer -//======================================== - -char16_t * -UnicodeString::getBuffer(int32_t minCapacity) { - if(minCapacity>=-1 && cloneArrayIfNeeded(minCapacity)) { - fUnion.fFields.fLengthAndFlags|=kOpenGetBuffer; - setZeroLength(); - return getArrayStart(); - } else { - return nullptr; - } -} - -void -UnicodeString::releaseBuffer(int32_t newLength) { - if(fUnion.fFields.fLengthAndFlags&kOpenGetBuffer && newLength>=-1) { - // set the new fLength - int32_t capacity=getCapacity(); - if(newLength==-1) { - // the new length is the string length, capped by fCapacity - const UChar *array=getArrayStart(), *p=array, *limit=array+capacity; - while(pcapacity) { - newLength=capacity; - } - setLength(newLength); - fUnion.fFields.fLengthAndFlags&=~kOpenGetBuffer; - } -} - -//======================================== -// Miscellaneous -//======================================== -UBool -UnicodeString::cloneArrayIfNeeded(int32_t newCapacity, - int32_t growCapacity, - UBool doCopyArray, - int32_t **pBufferToDelete, - UBool forceClone) { - // default parameters need to be static, therefore - // the defaults are -1 to have convenience defaults - if(newCapacity == -1) { - newCapacity = getCapacity(); - } - - // while a getBuffer(minCapacity) is "open", - // prevent any modifications of the string by returning false here - // if the string is bogus, then only an assignment or similar can revive it - if(!isWritable()) { - return false; - } - - /* - * We need to make a copy of the array if - * the buffer is read-only, or - * the buffer is refCounted (shared), and refCount>1, or - * the buffer is too small. - * Return false if memory could not be allocated. - */ - if(forceClone || - fUnion.fFields.fLengthAndFlags & kBufferIsReadonly || - (fUnion.fFields.fLengthAndFlags & kRefCounted && refCount() > 1) || - newCapacity > getCapacity() - ) { - // check growCapacity for default value and use of the stack buffer - if(growCapacity < 0) { - growCapacity = newCapacity; - } else if(newCapacity <= US_STACKBUF_SIZE && growCapacity > US_STACKBUF_SIZE) { - growCapacity = US_STACKBUF_SIZE; - } - - // save old values - UChar oldStackBuffer[US_STACKBUF_SIZE]; - UChar *oldArray; - int32_t oldLength = length(); - int16_t flags = fUnion.fFields.fLengthAndFlags; - - if(flags&kUsingStackBuffer) { - U_ASSERT(!(flags&kRefCounted)); /* kRefCounted and kUsingStackBuffer are mutally exclusive */ - if(doCopyArray && growCapacity > US_STACKBUF_SIZE) { - // copy the stack buffer contents because it will be overwritten with - // fUnion.fFields values - us_arrayCopy(fUnion.fStackFields.fBuffer, 0, oldStackBuffer, 0, oldLength); - oldArray = oldStackBuffer; - } else { - oldArray = NULL; // no need to copy from the stack buffer to itself - } - } else { - oldArray = fUnion.fFields.fArray; - U_ASSERT(oldArray!=NULL); /* when stack buffer is not used, oldArray must have a non-NULL reference */ - } - - // allocate a new array - if(allocate(growCapacity) || - (newCapacity < growCapacity && allocate(newCapacity)) - ) { - if(doCopyArray) { - // copy the contents - // do not copy more than what fits - it may be smaller than before - int32_t minLength = oldLength; - newCapacity = getCapacity(); - if(newCapacity < minLength) { - minLength = newCapacity; - } - if(oldArray != NULL) { - us_arrayCopy(oldArray, 0, getArrayStart(), 0, minLength); - } - setLength(minLength); - } else { - setZeroLength(); - } - - // release the old array - if(flags & kRefCounted) { - // the array is refCounted; decrement and release if 0 - u_atomic_int32_t *pRefCount = ((u_atomic_int32_t *)oldArray - 1); - if(umtx_atomic_dec(pRefCount) == 0) { - if(pBufferToDelete == 0) { - // Note: cast to (void *) is needed with MSVC, where u_atomic_int32_t - // is defined as volatile. (Volatile has useful non-standard behavior - // with this compiler.) - uprv_free((void *)pRefCount); - } else { - // the caller requested to delete it himself - *pBufferToDelete = (int32_t *)pRefCount; - } - } - } - } else { - // not enough memory for growCapacity and not even for the smaller newCapacity - // reset the old values for setToBogus() to release the array - if(!(flags&kUsingStackBuffer)) { - fUnion.fFields.fArray = oldArray; - } - fUnion.fFields.fLengthAndFlags = flags; - setToBogus(); - return false; - } - } - return true; -} - -// UnicodeStringAppendable ------------------------------------------------- *** - -UnicodeStringAppendable::~UnicodeStringAppendable() {} - -UBool -UnicodeStringAppendable::appendCodeUnit(UChar c) { - return str.doAppend(&c, 0, 1).isWritable(); -} - -UBool -UnicodeStringAppendable::appendCodePoint(UChar32 c) { - UChar buffer[U16_MAX_LENGTH]; - int32_t cLength = 0; - UBool isError = false; - U16_APPEND(buffer, cLength, U16_MAX_LENGTH, c, isError); - return !isError && str.doAppend(buffer, 0, cLength).isWritable(); -} - -UBool -UnicodeStringAppendable::appendString(const UChar *s, int32_t length) { - return str.doAppend(s, 0, length).isWritable(); -} - -UBool -UnicodeStringAppendable::reserveAppendCapacity(int32_t appendCapacity) { - return str.cloneArrayIfNeeded(str.length() + appendCapacity); -} - -UChar * -UnicodeStringAppendable::getAppendBuffer(int32_t minCapacity, - int32_t desiredCapacityHint, - UChar *scratch, int32_t scratchCapacity, - int32_t *resultCapacity) { - if(minCapacity < 1 || scratchCapacity < minCapacity) { - *resultCapacity = 0; - return NULL; - } - int32_t oldLength = str.length(); - if(minCapacity <= (kMaxCapacity - oldLength) && - desiredCapacityHint <= (kMaxCapacity - oldLength) && - str.cloneArrayIfNeeded(oldLength + minCapacity, oldLength + desiredCapacityHint)) { - *resultCapacity = str.getCapacity() - oldLength; - return str.getArrayStart() + oldLength; - } - *resultCapacity = scratchCapacity; - return scratch; -} - -U_NAMESPACE_END - -U_NAMESPACE_USE - -U_CAPI int32_t U_EXPORT2 -uhash_hashUnicodeString(const UElement key) { - const UnicodeString *str = (const UnicodeString*) key.pointer; - return (str == NULL) ? 0 : str->hashCode(); -} - -// Moved here from uhash_us.cpp so that using a UVector of UnicodeString* -// does not depend on hashtable code. -U_CAPI UBool U_EXPORT2 -uhash_compareUnicodeString(const UElement key1, const UElement key2) { - const UnicodeString *str1 = (const UnicodeString*) key1.pointer; - const UnicodeString *str2 = (const UnicodeString*) key2.pointer; - if (str1 == str2) { - return true; - } - if (str1 == NULL || str2 == NULL) { - return false; - } - return *str1 == *str2; -} - -#ifdef U_STATIC_IMPLEMENTATION -/* -This should never be called. It is defined here to make sure that the -virtual vector deleting destructor is defined within unistr.cpp. -The vector deleting destructor is already a part of UObject, -but defining it here makes sure that it is included with this object file. -This makes sure that static library dependencies are kept to a minimum. -*/ -#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 1100 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -static void uprv_UnicodeStringDummy(void) { - delete [] (new UnicodeString[2]); -} -#pragma GCC diagnostic pop -#endif -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 1999-2016, International Business Machines Corporation and +* others. All Rights Reserved. +****************************************************************************** +* +* File unistr.cpp +* +* Modification History: +* +* Date Name Description +* 09/25/98 stephen Creation. +* 04/20/99 stephen Overhauled per 4/16 code review. +* 07/09/99 stephen Renamed {hi,lo},{byte,word} to icu_X for HP/UX +* 11/18/99 aliu Added handleReplaceBetween() to make inherit from +* Replaceable. +* 06/25/01 grhoten Removed the dependency on iostream +****************************************************************************** +*/ + +#include "unicode/utypes.h" +#include "unicode/appendable.h" +#include "unicode/putil.h" +#include "cstring.h" +#include "cmemory.h" +#include "unicode/ustring.h" +#include "unicode/unistr.h" +#include "unicode/utf.h" +#include "unicode/utf16.h" +#include "uelement.h" +#include "ustr_imp.h" +#include "umutex.h" +#include "uassert.h" + +#if 0 + +#include +using namespace std; + +//DEBUGGING +void +print(const UnicodeString& s, + const char *name) +{ + char16_t c; + cout << name << ":|"; + for(int i = 0; i < s.length(); ++i) { + c = s[i]; + if(c>= 0x007E || c < 0x0020) + cout << "[0x" << hex << s[i] << "]"; + else + cout << (char) s[i]; + } + cout << '|' << endl; +} + +void +print(const char16_t *s, + int32_t len, + const char *name) +{ + char16_t c; + cout << name << ":|"; + for(int i = 0; i < len; ++i) { + c = s[i]; + if(c>= 0x007E || c < 0x0020) + cout << "[0x" << hex << s[i] << "]"; + else + cout << (char) s[i]; + } + cout << '|' << endl; +} +// END DEBUGGING +#endif + +// Local function definitions for now + +// need to copy areas that may overlap +static +inline void +us_arrayCopy(const char16_t *src, int32_t srcStart, + char16_t *dst, int32_t dstStart, int32_t count) +{ + if(count>0) { + uprv_memmove(dst+dstStart, src+srcStart, (size_t)count*sizeof(*src)); + } +} + +// u_unescapeAt() callback to get a char16_t from a UnicodeString +U_CDECL_BEGIN +static char16_t U_CALLCONV +UnicodeString_charAt(int32_t offset, void *context) { + return ((icu::UnicodeString*) context)->charAt(offset); +} +U_CDECL_END + +U_NAMESPACE_BEGIN + +/* The Replaceable virtual destructor can't be defined in the header + due to how AIX works with multiple definitions of virtual functions. +*/ +Replaceable::~Replaceable() {} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UnicodeString) + +UnicodeString U_EXPORT2 +operator+ (const UnicodeString &s1, const UnicodeString &s2) { + return + UnicodeString(s1.length()+s2.length()+1, (UChar32)0, 0). + append(s1). + append(s2); +} + +//======================================== +// Reference Counting functions, put at top of file so that optimizing compilers +// have a chance to automatically inline. +//======================================== + +void +UnicodeString::addRef() { + umtx_atomic_inc((u_atomic_int32_t *)fUnion.fFields.fArray - 1); +} + +int32_t +UnicodeString::removeRef() { + return umtx_atomic_dec((u_atomic_int32_t *)fUnion.fFields.fArray - 1); +} + +int32_t +UnicodeString::refCount() const { + return umtx_loadAcquire(*((u_atomic_int32_t *)fUnion.fFields.fArray - 1)); +} + +void +UnicodeString::releaseArray() { + if((fUnion.fFields.fLengthAndFlags & kRefCounted) && removeRef() == 0) { + uprv_free((int32_t *)fUnion.fFields.fArray - 1); + } +} + + + +//======================================== +// Constructors +//======================================== + +// The default constructor is inline in unistr.h. + +UnicodeString::UnicodeString(int32_t capacity, UChar32 c, int32_t count) { + fUnion.fFields.fLengthAndFlags = 0; + if(count <= 0 || (uint32_t)c > 0x10ffff) { + // just allocate and do not do anything else + allocate(capacity); + } else if(c <= 0xffff) { + int32_t length = count; + if(capacity < length) { + capacity = length; + } + if(allocate(capacity)) { + char16_t *array = getArrayStart(); + char16_t unit = (char16_t)c; + for(int32_t i = 0; i < length; ++i) { + array[i] = unit; + } + setLength(length); + } + } else { // supplementary code point, write surrogate pairs + if(count > (INT32_MAX / 2)) { + // We would get more than 2G UChars. + allocate(capacity); + return; + } + int32_t length = count * 2; + if(capacity < length) { + capacity = length; + } + if(allocate(capacity)) { + char16_t *array = getArrayStart(); + char16_t lead = U16_LEAD(c); + char16_t trail = U16_TRAIL(c); + for(int32_t i = 0; i < length; i += 2) { + array[i] = lead; + array[i + 1] = trail; + } + setLength(length); + } + } +} + +UnicodeString::UnicodeString(char16_t ch) { + fUnion.fFields.fLengthAndFlags = kLength1 | kShortString; + fUnion.fStackFields.fBuffer[0] = ch; +} + +UnicodeString::UnicodeString(UChar32 ch) { + fUnion.fFields.fLengthAndFlags = kShortString; + int32_t i = 0; + UBool isError = false; + U16_APPEND(fUnion.fStackFields.fBuffer, i, US_STACKBUF_SIZE, ch, isError); + // We test isError so that the compiler does not complain that we don't. + // If isError then i==0 which is what we want anyway. + if(!isError) { + setShortLength(i); + } +} + +UnicodeString::UnicodeString(const char16_t *text) { + fUnion.fFields.fLengthAndFlags = kShortString; + doAppend(text, 0, -1); +} + +UnicodeString::UnicodeString(const char16_t *text, + int32_t textLength) { + fUnion.fFields.fLengthAndFlags = kShortString; + doAppend(text, 0, textLength); +} + +UnicodeString::UnicodeString(UBool isTerminated, + ConstChar16Ptr textPtr, + int32_t textLength) { + fUnion.fFields.fLengthAndFlags = kReadonlyAlias; + const char16_t *text = textPtr; + if(text == nullptr) { + // treat as an empty string, do not alias + setToEmpty(); + } else if(textLength < -1 || + (textLength == -1 && !isTerminated) || + (textLength >= 0 && isTerminated && text[textLength] != 0) + ) { + setToBogus(); + } else { + if(textLength == -1) { + // text is terminated, or else it would have failed the above test + textLength = u_strlen(text); + } + setArray(const_cast(text), textLength, + isTerminated ? textLength + 1 : textLength); + } +} + +UnicodeString::UnicodeString(char16_t *buff, + int32_t buffLength, + int32_t buffCapacity) { + fUnion.fFields.fLengthAndFlags = kWritableAlias; + if(buff == nullptr) { + // treat as an empty string, do not alias + setToEmpty(); + } else if(buffLength < -1 || buffCapacity < 0 || buffLength > buffCapacity) { + setToBogus(); + } else { + if(buffLength == -1) { + // fLength = u_strlen(buff); but do not look beyond buffCapacity + const char16_t *p = buff, *limit = buff + buffCapacity; + while(p != limit && *p != 0) { + ++p; + } + buffLength = (int32_t)(p - buff); + } + setArray(buff, buffLength, buffCapacity); + } +} + +UnicodeString::UnicodeString(const char *src, int32_t length, EInvariant) { + fUnion.fFields.fLengthAndFlags = kShortString; + if(src==nullptr) { + // treat as an empty string + } else { + if(length<0) { + length=(int32_t)uprv_strlen(src); + } + if(cloneArrayIfNeeded(length, length, false)) { + u_charsToUChars(src, getArrayStart(), length); + setLength(length); + } else { + setToBogus(); + } + } +} + +#if U_CHARSET_IS_UTF8 + +UnicodeString::UnicodeString(const char *codepageData) { + fUnion.fFields.fLengthAndFlags = kShortString; + if(codepageData != 0) { + setToUTF8(codepageData); + } +} + +UnicodeString::UnicodeString(const char *codepageData, int32_t dataLength) { + fUnion.fFields.fLengthAndFlags = kShortString; + // if there's nothing to convert, do nothing + if(codepageData == 0 || dataLength == 0 || dataLength < -1) { + return; + } + if(dataLength == -1) { + dataLength = (int32_t)uprv_strlen(codepageData); + } + setToUTF8(StringPiece(codepageData, dataLength)); +} + +// else see unistr_cnv.cpp +#endif + +UnicodeString::UnicodeString(const UnicodeString& that) { + fUnion.fFields.fLengthAndFlags = kShortString; + copyFrom(that); +} + +UnicodeString::UnicodeString(UnicodeString &&src) noexcept { + copyFieldsFrom(src, true); +} + +UnicodeString::UnicodeString(const UnicodeString& that, + int32_t srcStart) { + fUnion.fFields.fLengthAndFlags = kShortString; + setTo(that, srcStart); +} + +UnicodeString::UnicodeString(const UnicodeString& that, + int32_t srcStart, + int32_t srcLength) { + fUnion.fFields.fLengthAndFlags = kShortString; + setTo(that, srcStart, srcLength); +} + +// Replaceable base class clone() default implementation, does not clone +Replaceable * +Replaceable::clone() const { + return nullptr; +} + +// UnicodeString overrides clone() with a real implementation +UnicodeString * +UnicodeString::clone() const { + LocalPointer clonedString(new UnicodeString(*this)); + return clonedString.isValid() && !clonedString->isBogus() ? clonedString.orphan() : nullptr; +} + +//======================================== +// array allocation +//======================================== + +namespace { + +const int32_t kGrowSize = 128; + +// The number of bytes for one int32_t reference counter and capacity UChars +// must fit into a 32-bit size_t (at least when on a 32-bit platform). +// We also add one for the NUL terminator, to avoid reallocation in getTerminatedBuffer(), +// and round up to a multiple of 16 bytes. +// This means that capacity must be at most (0xfffffff0 - 4) / 2 - 1 = 0x7ffffff5. +// (With more complicated checks we could go up to 0x7ffffffd without rounding up, +// but that does not seem worth it.) +const int32_t kMaxCapacity = 0x7ffffff5; + +int32_t getGrowCapacity(int32_t newLength) { + int32_t growSize = (newLength >> 2) + kGrowSize; + if(growSize <= (kMaxCapacity - newLength)) { + return newLength + growSize; + } else { + return kMaxCapacity; + } +} + +} // namespace + +UBool +UnicodeString::allocate(int32_t capacity) { + if(capacity <= US_STACKBUF_SIZE) { + fUnion.fFields.fLengthAndFlags = kShortString; + return true; + } + if(capacity <= kMaxCapacity) { + ++capacity; // for the NUL + // Switch to size_t which is unsigned so that we can allocate up to 4GB. + // Reference counter + UChars. + size_t numBytes = sizeof(int32_t) + (size_t)capacity * U_SIZEOF_UCHAR; + // Round up to a multiple of 16. + numBytes = (numBytes + 15) & ~15; + int32_t *array = (int32_t *) uprv_malloc(numBytes); + if(array != nullptr) { + // set initial refCount and point behind the refCount + *array++ = 1; + numBytes -= sizeof(int32_t); + + // have fArray point to the first char16_t + fUnion.fFields.fArray = (char16_t *)array; + fUnion.fFields.fCapacity = (int32_t)(numBytes / U_SIZEOF_UCHAR); + fUnion.fFields.fLengthAndFlags = kLongString; + return true; + } + } + fUnion.fFields.fLengthAndFlags = kIsBogus; + fUnion.fFields.fArray = 0; + fUnion.fFields.fCapacity = 0; + return false; +} + +//======================================== +// Destructor +//======================================== + +#ifdef UNISTR_COUNT_FINAL_STRING_LENGTHS +static u_atomic_int32_t finalLengthCounts[0x400]; // UnicodeString::kMaxShortLength+1 +static u_atomic_int32_t beyondCount(0); + +U_CAPI void unistr_printLengths() { + int32_t i; + for(i = 0; i <= 59; ++i) { + printf("%2d, %9d\n", i, (int32_t)finalLengthCounts[i]); + } + int32_t beyond = beyondCount; + for(; i < UPRV_LENGTHOF(finalLengthCounts); ++i) { + beyond += finalLengthCounts[i]; + } + printf(">59, %9d\n", beyond); +} +#endif + +UnicodeString::~UnicodeString() +{ +#ifdef UNISTR_COUNT_FINAL_STRING_LENGTHS + // Count lengths of strings at the end of their lifetime. + // Useful for discussion of a desirable stack buffer size. + // Count the contents length, not the optional NUL terminator nor further capacity. + // Ignore open-buffer strings and strings which alias external storage. + if((fUnion.fFields.fLengthAndFlags&(kOpenGetBuffer|kReadonlyAlias|kWritableAlias)) == 0) { + if(hasShortLength()) { + umtx_atomic_inc(finalLengthCounts + getShortLength()); + } else { + umtx_atomic_inc(&beyondCount); + } + } +#endif + + releaseArray(); +} + +//======================================== +// Factory methods +//======================================== + +UnicodeString UnicodeString::fromUTF8(StringPiece utf8) { + UnicodeString result; + result.setToUTF8(utf8); + return result; +} + +UnicodeString UnicodeString::fromUTF32(const UChar32 *utf32, int32_t length) { + UnicodeString result; + int32_t capacity; + // Most UTF-32 strings will be BMP-only and result in a same-length + // UTF-16 string. We overestimate the capacity just slightly, + // just in case there are a few supplementary characters. + if(length <= US_STACKBUF_SIZE) { + capacity = US_STACKBUF_SIZE; + } else { + capacity = length + (length >> 4) + 4; + } + do { + char16_t *utf16 = result.getBuffer(capacity); + int32_t length16; + UErrorCode errorCode = U_ZERO_ERROR; + u_strFromUTF32WithSub(utf16, result.getCapacity(), &length16, + utf32, length, + 0xfffd, // Substitution character. + nullptr, // Don't care about number of substitutions. + &errorCode); + result.releaseBuffer(length16); + if(errorCode == U_BUFFER_OVERFLOW_ERROR) { + capacity = length16 + 1; // +1 for the terminating NUL. + continue; + } else if(U_FAILURE(errorCode)) { + result.setToBogus(); + } + break; + } while(true); + return result; +} + +//======================================== +// Assignment +//======================================== + +UnicodeString & +UnicodeString::operator=(const UnicodeString &src) { + return copyFrom(src); +} + +UnicodeString & +UnicodeString::fastCopyFrom(const UnicodeString &src) { + return copyFrom(src, true); +} + +UnicodeString & +UnicodeString::copyFrom(const UnicodeString &src, UBool fastCopy) { + // if assigning to ourselves, do nothing + if(this == &src) { + return *this; + } + + // is the right side bogus? + if(src.isBogus()) { + setToBogus(); + return *this; + } + + // delete the current contents + releaseArray(); + + if(src.isEmpty()) { + // empty string - use the stack buffer + setToEmpty(); + return *this; + } + + // fLength>0 and not an "open" src.getBuffer(minCapacity) + fUnion.fFields.fLengthAndFlags = src.fUnion.fFields.fLengthAndFlags; + switch(src.fUnion.fFields.fLengthAndFlags & kAllStorageFlags) { + case kShortString: + // short string using the stack buffer, do the same + uprv_memcpy(fUnion.fStackFields.fBuffer, src.fUnion.fStackFields.fBuffer, + getShortLength() * U_SIZEOF_UCHAR); + break; + case kLongString: + // src uses a refCounted string buffer, use that buffer with refCount + // src is const, use a cast - we don't actually change it + const_cast(src).addRef(); + // copy all fields, share the reference-counted buffer + fUnion.fFields.fArray = src.fUnion.fFields.fArray; + fUnion.fFields.fCapacity = src.fUnion.fFields.fCapacity; + if(!hasShortLength()) { + fUnion.fFields.fLength = src.fUnion.fFields.fLength; + } + break; + case kReadonlyAlias: + if(fastCopy) { + // src is a readonly alias, do the same + // -> maintain the readonly alias as such + fUnion.fFields.fArray = src.fUnion.fFields.fArray; + fUnion.fFields.fCapacity = src.fUnion.fFields.fCapacity; + if(!hasShortLength()) { + fUnion.fFields.fLength = src.fUnion.fFields.fLength; + } + break; + } + // else if(!fastCopy) fall through to case kWritableAlias + // -> allocate a new buffer and copy the contents + U_FALLTHROUGH; + case kWritableAlias: { + // src is a writable alias; we make a copy of that instead + int32_t srcLength = src.length(); + if(allocate(srcLength)) { + u_memcpy(getArrayStart(), src.getArrayStart(), srcLength); + setLength(srcLength); + break; + } + // if there is not enough memory, then fall through to setting to bogus + U_FALLTHROUGH; + } + default: + // if src is bogus, set ourselves to bogus + // do not call setToBogus() here because fArray and flags are not consistent here + fUnion.fFields.fLengthAndFlags = kIsBogus; + fUnion.fFields.fArray = 0; + fUnion.fFields.fCapacity = 0; + break; + } + + return *this; +} + +UnicodeString &UnicodeString::operator=(UnicodeString &&src) noexcept { + // No explicit check for self move assignment, consistent with standard library. + // Self move assignment causes no crash nor leak but might make the object bogus. + releaseArray(); + copyFieldsFrom(src, true); + return *this; +} + +// Same as move assignment except without memory management. +void UnicodeString::copyFieldsFrom(UnicodeString &src, UBool setSrcToBogus) noexcept { + int16_t lengthAndFlags = fUnion.fFields.fLengthAndFlags = src.fUnion.fFields.fLengthAndFlags; + if(lengthAndFlags & kUsingStackBuffer) { + // Short string using the stack buffer, copy the contents. + // Check for self assignment to prevent "overlap in memcpy" warnings, + // although it should be harmless to copy a buffer to itself exactly. + if(this != &src) { + uprv_memcpy(fUnion.fStackFields.fBuffer, src.fUnion.fStackFields.fBuffer, + getShortLength() * U_SIZEOF_UCHAR); + } + } else { + // In all other cases, copy all fields. + fUnion.fFields.fArray = src.fUnion.fFields.fArray; + fUnion.fFields.fCapacity = src.fUnion.fFields.fCapacity; + if(!hasShortLength()) { + fUnion.fFields.fLength = src.fUnion.fFields.fLength; + } + if(setSrcToBogus) { + // Set src to bogus without releasing any memory. + src.fUnion.fFields.fLengthAndFlags = kIsBogus; + src.fUnion.fFields.fArray = nullptr; + src.fUnion.fFields.fCapacity = 0; + } + } +} + +void UnicodeString::swap(UnicodeString &other) noexcept { + UnicodeString temp; // Empty short string: Known not to need releaseArray(). + // Copy fields without resetting source values in between. + temp.copyFieldsFrom(*this, false); + this->copyFieldsFrom(other, false); + other.copyFieldsFrom(temp, false); + // Set temp to an empty string so that other's memory is not released twice. + temp.fUnion.fFields.fLengthAndFlags = kShortString; +} + +//======================================== +// Miscellaneous operations +//======================================== + +UnicodeString UnicodeString::unescape() const { + UnicodeString result(length(), (UChar32)0, (int32_t)0); // construct with capacity + if (result.isBogus()) { + return result; + } + const char16_t *array = getBuffer(); + int32_t len = length(); + int32_t prev = 0; + for (int32_t i=0;;) { + if (i == len) { + result.append(array, prev, len - prev); + break; + } + if (array[i++] == 0x5C /*'\\'*/) { + result.append(array, prev, (i - 1) - prev); + UChar32 c = unescapeAt(i); // advances i + if (c < 0) { + result.remove(); // return empty string + break; // invalid escape sequence + } + result.append(c); + prev = i; + } + } + return result; +} + +UChar32 UnicodeString::unescapeAt(int32_t &offset) const { + return u_unescapeAt(UnicodeString_charAt, &offset, length(), (void*)this); +} + +//======================================== +// Read-only implementation +//======================================== +UBool +UnicodeString::doEquals(const UnicodeString &text, int32_t len) const { + // Requires: this & text not bogus and have same lengths. + // Byte-wise comparison works for equality regardless of endianness. + return uprv_memcmp(getArrayStart(), text.getArrayStart(), len * U_SIZEOF_UCHAR) == 0; +} + +UBool +UnicodeString::doEqualsSubstring( int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const +{ + // compare illegal string values + if(isBogus()) { + return false; + } + + // pin indices to legal values + pinIndices(start, length); + + if(srcChars == nullptr) { + // treat const char16_t *srcChars==nullptr as an empty string + return length == 0 ? true : false; + } + + // get the correct pointer + const char16_t *chars = getArrayStart(); + + chars += start; + srcChars += srcStart; + + // get the srcLength if necessary + if(srcLength < 0) { + srcLength = u_strlen(srcChars + srcStart); + } + + if (length != srcLength) { + return false; + } + + if(length == 0 || chars == srcChars) { + return true; + } + + return u_memcmp(chars, srcChars, srcLength) == 0; +} + +int8_t +UnicodeString::doCompare( int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const +{ + // compare illegal string values + if(isBogus()) { + return -1; + } + + // pin indices to legal values + pinIndices(start, length); + + if(srcChars == nullptr) { + // treat const char16_t *srcChars==nullptr as an empty string + return length == 0 ? 0 : 1; + } + + // get the correct pointer + const char16_t *chars = getArrayStart(); + + chars += start; + srcChars += srcStart; + + int32_t minLength; + int8_t lengthResult; + + // get the srcLength if necessary + if(srcLength < 0) { + srcLength = u_strlen(srcChars + srcStart); + } + + // are we comparing different lengths? + if(length != srcLength) { + if(length < srcLength) { + minLength = length; + lengthResult = -1; + } else { + minLength = srcLength; + lengthResult = 1; + } + } else { + minLength = length; + lengthResult = 0; + } + + /* + * note that uprv_memcmp() returns an int but we return an int8_t; + * we need to take care not to truncate the result - + * one way to do this is to right-shift the value to + * move the sign bit into the lower 8 bits and making sure that this + * does not become 0 itself + */ + + if(minLength > 0 && chars != srcChars) { + int32_t result; + +# if U_IS_BIG_ENDIAN + // big-endian: byte comparison works + result = uprv_memcmp(chars, srcChars, minLength * sizeof(char16_t)); + if(result != 0) { + return (int8_t)(result >> 15 | 1); + } +# else + // little-endian: compare char16_t units + do { + result = ((int32_t)*(chars++) - (int32_t)*(srcChars++)); + if(result != 0) { + return (int8_t)(result >> 15 | 1); + } + } while(--minLength > 0); +# endif + } + return lengthResult; +} + +/* String compare in code point order - doCompare() compares in code unit order. */ +int8_t +UnicodeString::doCompareCodePointOrder(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) const +{ + // compare illegal string values + // treat const char16_t *srcChars==nullptr as an empty string + if(isBogus()) { + return -1; + } + + // pin indices to legal values + pinIndices(start, length); + + if(srcChars == nullptr) { + srcStart = srcLength = 0; + } + + int32_t diff = uprv_strCompare(getArrayStart() + start, length, (srcChars!=nullptr)?(srcChars + srcStart):nullptr, srcLength, false, true); + /* translate the 32-bit result into an 8-bit one */ + if(diff!=0) { + return (int8_t)(diff >> 15 | 1); + } else { + return 0; + } +} + +int32_t +UnicodeString::getLength() const { + return length(); +} + +char16_t +UnicodeString::getCharAt(int32_t offset) const { + return charAt(offset); +} + +UChar32 +UnicodeString::getChar32At(int32_t offset) const { + return char32At(offset); +} + +UChar32 +UnicodeString::char32At(int32_t offset) const +{ + int32_t len = length(); + if((uint32_t)offset < (uint32_t)len) { + const char16_t *array = getArrayStart(); + UChar32 c; + U16_GET(array, 0, offset, len, c); + return c; + } else { + return kInvalidUChar; + } +} + +int32_t +UnicodeString::getChar32Start(int32_t offset) const { + if((uint32_t)offset < (uint32_t)length()) { + const char16_t *array = getArrayStart(); + U16_SET_CP_START(array, 0, offset); + return offset; + } else { + return 0; + } +} + +int32_t +UnicodeString::getChar32Limit(int32_t offset) const { + int32_t len = length(); + if((uint32_t)offset < (uint32_t)len) { + const char16_t *array = getArrayStart(); + U16_SET_CP_LIMIT(array, 0, offset, len); + return offset; + } else { + return len; + } +} + +int32_t +UnicodeString::countChar32(int32_t start, int32_t length) const { + pinIndices(start, length); + // if(isBogus()) then fArray==0 and start==0 - u_countChar32() checks for nullptr + return u_countChar32(getArrayStart()+start, length); +} + +UBool +UnicodeString::hasMoreChar32Than(int32_t start, int32_t length, int32_t number) const { + pinIndices(start, length); + // if(isBogus()) then fArray==0 and start==0 - u_strHasMoreChar32Than() checks for nullptr + return u_strHasMoreChar32Than(getArrayStart()+start, length, number); +} + +int32_t +UnicodeString::moveIndex32(int32_t index, int32_t delta) const { + // pin index + int32_t len = length(); + if(index<0) { + index=0; + } else if(index>len) { + index=len; + } + + const char16_t *array = getArrayStart(); + if(delta>0) { + U16_FWD_N(array, index, len, delta); + } else { + U16_BACK_N(array, 0, index, -delta); + } + + return index; +} + +void +UnicodeString::doExtract(int32_t start, + int32_t length, + char16_t *dst, + int32_t dstStart) const +{ + // pin indices to legal values + pinIndices(start, length); + + // do not copy anything if we alias dst itself + const char16_t *array = getArrayStart(); + if(array + start != dst + dstStart) { + us_arrayCopy(array, start, dst, dstStart, length); + } +} + +int32_t +UnicodeString::extract(Char16Ptr dest, int32_t destCapacity, + UErrorCode &errorCode) const { + int32_t len = length(); + if(U_SUCCESS(errorCode)) { + if(isBogus() || destCapacity<0 || (destCapacity>0 && dest==0)) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + } else { + const char16_t *array = getArrayStart(); + if(len>0 && len<=destCapacity && array!=dest) { + u_memcpy(dest, array, len); + } + return u_terminateUChars(dest, destCapacity, len, &errorCode); + } + } + + return len; +} + +int32_t +UnicodeString::extract(int32_t start, + int32_t length, + char *target, + int32_t targetCapacity, + enum EInvariant) const +{ + // if the arguments are illegal, then do nothing + if(targetCapacity < 0 || (targetCapacity > 0 && target == nullptr)) { + return 0; + } + + // pin the indices to legal values + pinIndices(start, length); + + if(length <= targetCapacity) { + u_UCharsToChars(getArrayStart() + start, target, length); + } + UErrorCode status = U_ZERO_ERROR; + return u_terminateChars(target, targetCapacity, length, &status); +} + +UnicodeString +UnicodeString::tempSubString(int32_t start, int32_t len) const { + pinIndices(start, len); + const char16_t *array = getBuffer(); // not getArrayStart() to check kIsBogus & kOpenGetBuffer + if(array==nullptr) { + array=fUnion.fStackFields.fBuffer; // anything not nullptr because that would make an empty string + len=-2; // bogus result string + } + return UnicodeString(false, array + start, len); +} + +int32_t +UnicodeString::toUTF8(int32_t start, int32_t len, + char *target, int32_t capacity) const { + pinIndices(start, len); + int32_t length8; + UErrorCode errorCode = U_ZERO_ERROR; + u_strToUTF8WithSub(target, capacity, &length8, + getBuffer() + start, len, + 0xFFFD, // Standard substitution character. + nullptr, // Don't care about number of substitutions. + &errorCode); + return length8; +} + +#if U_CHARSET_IS_UTF8 + +int32_t +UnicodeString::extract(int32_t start, int32_t len, + char *target, uint32_t dstSize) const { + // if the arguments are illegal, then do nothing + if(/*dstSize < 0 || */(dstSize > 0 && target == 0)) { + return 0; + } + return toUTF8(start, len, target, dstSize <= 0x7fffffff ? (int32_t)dstSize : 0x7fffffff); +} + +// else see unistr_cnv.cpp +#endif + +void +UnicodeString::extractBetween(int32_t start, + int32_t limit, + UnicodeString& target) const { + pinIndex(start); + pinIndex(limit); + doExtract(start, limit - start, target); +} + +// When converting from UTF-16 to UTF-8, the result will have at most 3 times +// as many bytes as the source has UChars. +// The "worst cases" are writing systems like Indic, Thai and CJK with +// 3:1 bytes:UChars. +void +UnicodeString::toUTF8(ByteSink &sink) const { + int32_t length16 = length(); + if(length16 != 0) { + char stackBuffer[1024]; + int32_t capacity = (int32_t)sizeof(stackBuffer); + UBool utf8IsOwned = false; + char *utf8 = sink.GetAppendBuffer(length16 < capacity ? length16 : capacity, + 3*length16, + stackBuffer, capacity, + &capacity); + int32_t length8 = 0; + UErrorCode errorCode = U_ZERO_ERROR; + u_strToUTF8WithSub(utf8, capacity, &length8, + getBuffer(), length16, + 0xFFFD, // Standard substitution character. + nullptr, // Don't care about number of substitutions. + &errorCode); + if(errorCode == U_BUFFER_OVERFLOW_ERROR) { + utf8 = (char *)uprv_malloc(length8); + if(utf8 != nullptr) { + utf8IsOwned = true; + errorCode = U_ZERO_ERROR; + u_strToUTF8WithSub(utf8, length8, &length8, + getBuffer(), length16, + 0xFFFD, // Standard substitution character. + nullptr, // Don't care about number of substitutions. + &errorCode); + } else { + errorCode = U_MEMORY_ALLOCATION_ERROR; + } + } + if(U_SUCCESS(errorCode)) { + sink.Append(utf8, length8); + sink.Flush(); + } + if(utf8IsOwned) { + uprv_free(utf8); + } + } +} + +int32_t +UnicodeString::toUTF32(UChar32 *utf32, int32_t capacity, UErrorCode &errorCode) const { + int32_t length32=0; + if(U_SUCCESS(errorCode)) { + // getBuffer() and u_strToUTF32WithSub() check for illegal arguments. + u_strToUTF32WithSub(utf32, capacity, &length32, + getBuffer(), length(), + 0xfffd, // Substitution character. + nullptr, // Don't care about number of substitutions. + &errorCode); + } + return length32; +} + +int32_t +UnicodeString::indexOf(const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength, + int32_t start, + int32_t length) const +{ + if(isBogus() || srcChars == 0 || srcStart < 0 || srcLength == 0) { + return -1; + } + + // UnicodeString does not find empty substrings + if(srcLength < 0 && srcChars[srcStart] == 0) { + return -1; + } + + // get the indices within bounds + pinIndices(start, length); + + // find the first occurrence of the substring + const char16_t *array = getArrayStart(); + const char16_t *match = u_strFindFirst(array + start, length, srcChars + srcStart, srcLength); + if(match == nullptr) { + return -1; + } else { + return (int32_t)(match - array); + } +} + +int32_t +UnicodeString::doIndexOf(char16_t c, + int32_t start, + int32_t length) const +{ + // pin indices + pinIndices(start, length); + + // find the first occurrence of c + const char16_t *array = getArrayStart(); + const char16_t *match = u_memchr(array + start, c, length); + if(match == nullptr) { + return -1; + } else { + return (int32_t)(match - array); + } +} + +int32_t +UnicodeString::doIndexOf(UChar32 c, + int32_t start, + int32_t length) const { + // pin indices + pinIndices(start, length); + + // find the first occurrence of c + const char16_t *array = getArrayStart(); + const char16_t *match = u_memchr32(array + start, c, length); + if(match == nullptr) { + return -1; + } else { + return (int32_t)(match - array); + } +} + +int32_t +UnicodeString::lastIndexOf(const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength, + int32_t start, + int32_t length) const +{ + if(isBogus() || srcChars == 0 || srcStart < 0 || srcLength == 0) { + return -1; + } + + // UnicodeString does not find empty substrings + if(srcLength < 0 && srcChars[srcStart] == 0) { + return -1; + } + + // get the indices within bounds + pinIndices(start, length); + + // find the last occurrence of the substring + const char16_t *array = getArrayStart(); + const char16_t *match = u_strFindLast(array + start, length, srcChars + srcStart, srcLength); + if(match == nullptr) { + return -1; + } else { + return (int32_t)(match - array); + } +} + +int32_t +UnicodeString::doLastIndexOf(char16_t c, + int32_t start, + int32_t length) const +{ + if(isBogus()) { + return -1; + } + + // pin indices + pinIndices(start, length); + + // find the last occurrence of c + const char16_t *array = getArrayStart(); + const char16_t *match = u_memrchr(array + start, c, length); + if(match == nullptr) { + return -1; + } else { + return (int32_t)(match - array); + } +} + +int32_t +UnicodeString::doLastIndexOf(UChar32 c, + int32_t start, + int32_t length) const { + // pin indices + pinIndices(start, length); + + // find the last occurrence of c + const char16_t *array = getArrayStart(); + const char16_t *match = u_memrchr32(array + start, c, length); + if(match == nullptr) { + return -1; + } else { + return (int32_t)(match - array); + } +} + +//======================================== +// Write implementation +//======================================== + +UnicodeString& +UnicodeString::findAndReplace(int32_t start, + int32_t length, + const UnicodeString& oldText, + int32_t oldStart, + int32_t oldLength, + const UnicodeString& newText, + int32_t newStart, + int32_t newLength) +{ + if(isBogus() || oldText.isBogus() || newText.isBogus()) { + return *this; + } + + pinIndices(start, length); + oldText.pinIndices(oldStart, oldLength); + newText.pinIndices(newStart, newLength); + + if(oldLength == 0) { + return *this; + } + + while(length > 0 && length >= oldLength) { + int32_t pos = indexOf(oldText, oldStart, oldLength, start, length); + if(pos < 0) { + // no more oldText's here: done + break; + } else { + // we found oldText, replace it by newText and go beyond it + replace(pos, oldLength, newText, newStart, newLength); + length -= pos + oldLength - start; + start = pos + newLength; + } + } + + return *this; +} + + +void +UnicodeString::setToBogus() +{ + releaseArray(); + + fUnion.fFields.fLengthAndFlags = kIsBogus; + fUnion.fFields.fArray = 0; + fUnion.fFields.fCapacity = 0; +} + +// turn a bogus string into an empty one +void +UnicodeString::unBogus() { + if(fUnion.fFields.fLengthAndFlags & kIsBogus) { + setToEmpty(); + } +} + +const char16_t * +UnicodeString::getTerminatedBuffer() { + if(!isWritable()) { + return nullptr; + } + char16_t *array = getArrayStart(); + int32_t len = length(); + if(len < getCapacity()) { + if(fUnion.fFields.fLengthAndFlags & kBufferIsReadonly) { + // If len= 0 && isTerminated && text[textLength] != 0) + ) { + setToBogus(); + return *this; + } + + releaseArray(); + + if(textLength == -1) { + // text is terminated, or else it would have failed the above test + textLength = u_strlen(text); + } + fUnion.fFields.fLengthAndFlags = kReadonlyAlias; + setArray((char16_t *)text, textLength, isTerminated ? textLength + 1 : textLength); + return *this; +} + +// setTo() analogous to the writable-aliasing constructor with the same signature +UnicodeString & +UnicodeString::setTo(char16_t *buffer, + int32_t buffLength, + int32_t buffCapacity) { + if(fUnion.fFields.fLengthAndFlags & kOpenGetBuffer) { + // do not modify a string that has an "open" getBuffer(minCapacity) + return *this; + } + + if(buffer == nullptr) { + // treat as an empty string, do not alias + releaseArray(); + setToEmpty(); + return *this; + } + + if(buffLength < -1 || buffCapacity < 0 || buffLength > buffCapacity) { + setToBogus(); + return *this; + } else if(buffLength == -1) { + // buffLength = u_strlen(buff); but do not look beyond buffCapacity + const char16_t *p = buffer, *limit = buffer + buffCapacity; + while(p != limit && *p != 0) { + ++p; + } + buffLength = (int32_t)(p - buffer); + } + + releaseArray(); + + fUnion.fFields.fLengthAndFlags = kWritableAlias; + setArray(buffer, buffLength, buffCapacity); + return *this; +} + +UnicodeString &UnicodeString::setToUTF8(StringPiece utf8) { + unBogus(); + int32_t length = utf8.length(); + int32_t capacity; + // The UTF-16 string will be at most as long as the UTF-8 string. + if(length <= US_STACKBUF_SIZE) { + capacity = US_STACKBUF_SIZE; + } else { + capacity = length + 1; // +1 for the terminating NUL. + } + char16_t *utf16 = getBuffer(capacity); + int32_t length16; + UErrorCode errorCode = U_ZERO_ERROR; + u_strFromUTF8WithSub(utf16, getCapacity(), &length16, + utf8.data(), length, + 0xfffd, // Substitution character. + nullptr, // Don't care about number of substitutions. + &errorCode); + releaseBuffer(length16); + if(U_FAILURE(errorCode)) { + setToBogus(); + } + return *this; +} + +UnicodeString& +UnicodeString::setCharAt(int32_t offset, + char16_t c) +{ + int32_t len = length(); + if(cloneArrayIfNeeded() && len > 0) { + if(offset < 0) { + offset = 0; + } else if(offset >= len) { + offset = len - 1; + } + + getArrayStart()[offset] = c; + } + return *this; +} + +UnicodeString& +UnicodeString::replace(int32_t start, + int32_t _length, + UChar32 srcChar) { + char16_t buffer[U16_MAX_LENGTH]; + int32_t count = 0; + UBool isError = false; + U16_APPEND(buffer, count, U16_MAX_LENGTH, srcChar, isError); + // We test isError so that the compiler does not complain that we don't. + // If isError (srcChar is not a valid code point) then count==0 which means + // we remove the source segment rather than replacing it with srcChar. + return doReplace(start, _length, buffer, 0, isError ? 0 : count); +} + +UnicodeString& +UnicodeString::append(UChar32 srcChar) { + char16_t buffer[U16_MAX_LENGTH]; + int32_t _length = 0; + UBool isError = false; + U16_APPEND(buffer, _length, U16_MAX_LENGTH, srcChar, isError); + // We test isError so that the compiler does not complain that we don't. + // If isError then _length==0 which turns the doAppend() into a no-op anyway. + return isError ? *this : doAppend(buffer, 0, _length); +} + +UnicodeString& +UnicodeString::doReplace( int32_t start, + int32_t length, + const UnicodeString& src, + int32_t srcStart, + int32_t srcLength) +{ + // pin the indices to legal values + src.pinIndices(srcStart, srcLength); + + // get the characters from src + // and replace the range in ourselves with them + return doReplace(start, length, src.getArrayStart(), srcStart, srcLength); +} + +UnicodeString& +UnicodeString::doReplace(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength) +{ + if(!isWritable()) { + return *this; + } + + int32_t oldLength = this->length(); + + // optimize (read-only alias).remove(0, start) and .remove(start, end) + if((fUnion.fFields.fLengthAndFlags&kBufferIsReadonly) && srcLength == 0) { + if(start == 0) { + // remove prefix by adjusting the array pointer + pinIndex(length); + fUnion.fFields.fArray += length; + fUnion.fFields.fCapacity -= length; + setLength(oldLength - length); + return *this; + } else { + pinIndex(start); + if(length >= (oldLength - start)) { + // remove suffix by reducing the length (like truncate()) + setLength(start); + fUnion.fFields.fCapacity = start; // not NUL-terminated any more + return *this; + } + } + } + + if(start == oldLength) { + return doAppend(srcChars, srcStart, srcLength); + } + + if(srcChars == 0) { + srcLength = 0; + } else { + // Perform all remaining operations relative to srcChars + srcStart. + // From this point forward, do not use srcStart. + srcChars += srcStart; + if (srcLength < 0) { + // get the srcLength if necessary + srcLength = u_strlen(srcChars); + } + } + + // pin the indices to legal values + pinIndices(start, length); + + // Calculate the size of the string after the replace. + // Avoid int32_t overflow. + int32_t newLength = oldLength - length; + if(srcLength > (INT32_MAX - newLength)) { + setToBogus(); + return *this; + } + newLength += srcLength; + + // Check for insertion into ourself + const char16_t *oldArray = getArrayStart(); + if (isBufferWritable() && + oldArray < srcChars + srcLength && + srcChars < oldArray + oldLength) { + // Copy into a new UnicodeString and start over + UnicodeString copy(srcChars, srcLength); + if (copy.isBogus()) { + setToBogus(); + return *this; + } + return doReplace(start, length, copy.getArrayStart(), 0, srcLength); + } + + // cloneArrayIfNeeded(doCopyArray=false) may change fArray but will not copy the current contents; + // therefore we need to keep the current fArray + char16_t oldStackBuffer[US_STACKBUF_SIZE]; + if((fUnion.fFields.fLengthAndFlags&kUsingStackBuffer) && (newLength > US_STACKBUF_SIZE)) { + // copy the stack buffer contents because it will be overwritten with + // fUnion.fFields values + u_memcpy(oldStackBuffer, oldArray, oldLength); + oldArray = oldStackBuffer; + } + + // clone our array and allocate a bigger array if needed + int32_t *bufferToDelete = 0; + if(!cloneArrayIfNeeded(newLength, getGrowCapacity(newLength), + false, &bufferToDelete) + ) { + return *this; + } + + // now do the replace + + char16_t *newArray = getArrayStart(); + if(newArray != oldArray) { + // if fArray changed, then we need to copy everything except what will change + us_arrayCopy(oldArray, 0, newArray, 0, start); + us_arrayCopy(oldArray, start + length, + newArray, start + srcLength, + oldLength - (start + length)); + } else if(length != srcLength) { + // fArray did not change; copy only the portion that isn't changing, leaving a hole + us_arrayCopy(oldArray, start + length, + newArray, start + srcLength, + oldLength - (start + length)); + } + + // now fill in the hole with the new string + us_arrayCopy(srcChars, 0, newArray, start, srcLength); + + setLength(newLength); + + // delayed delete in case srcChars == fArray when we started, and + // to keep oldArray alive for the above operations + if (bufferToDelete) { + uprv_free(bufferToDelete); + } + + return *this; +} + +// Versions of doReplace() only for append() variants. +// doReplace() and doAppend() optimize for different cases. + +UnicodeString& +UnicodeString::doAppend(const UnicodeString& src, int32_t srcStart, int32_t srcLength) { + if(srcLength == 0) { + return *this; + } + + // pin the indices to legal values + src.pinIndices(srcStart, srcLength); + return doAppend(src.getArrayStart(), srcStart, srcLength); +} + +UnicodeString& +UnicodeString::doAppend(const char16_t *srcChars, int32_t srcStart, int32_t srcLength) { + if(!isWritable() || srcLength == 0 || srcChars == nullptr) { + return *this; + } + + // Perform all remaining operations relative to srcChars + srcStart. + // From this point forward, do not use srcStart. + srcChars += srcStart; + + if(srcLength < 0) { + // get the srcLength if necessary + if((srcLength = u_strlen(srcChars)) == 0) { + return *this; + } + } + + int32_t oldLength = length(); + int32_t newLength; + if (uprv_add32_overflow(oldLength, srcLength, &newLength)) { + setToBogus(); + return *this; + } + + // Check for append onto ourself + const char16_t* oldArray = getArrayStart(); + if (isBufferWritable() && + oldArray < srcChars + srcLength && + srcChars < oldArray + oldLength) { + // Copy into a new UnicodeString and start over + UnicodeString copy(srcChars, srcLength); + if (copy.isBogus()) { + setToBogus(); + return *this; + } + return doAppend(copy.getArrayStart(), 0, srcLength); + } + + // optimize append() onto a large-enough, owned string + if((newLength <= getCapacity() && isBufferWritable()) || + cloneArrayIfNeeded(newLength, getGrowCapacity(newLength))) { + char16_t *newArray = getArrayStart(); + // Do not copy characters when + // char16_t *buffer=str.getAppendBuffer(...); + // is followed by + // str.append(buffer, length); + // or + // str.appendString(buffer, length) + // or similar. + if(srcChars != newArray + oldLength) { + us_arrayCopy(srcChars, 0, newArray, oldLength, srcLength); + } + setLength(newLength); + } + return *this; +} + +/** + * Replaceable API + */ +void +UnicodeString::handleReplaceBetween(int32_t start, + int32_t limit, + const UnicodeString& text) { + replaceBetween(start, limit, text); +} + +/** + * Replaceable API + */ +void +UnicodeString::copy(int32_t start, int32_t limit, int32_t dest) { + if (limit <= start) { + return; // Nothing to do; avoid bogus malloc call + } + char16_t* text = (char16_t*) uprv_malloc( sizeof(char16_t) * (limit - start) ); + // Check to make sure text is not null. + if (text != nullptr) { + extractBetween(start, limit, text, 0); + insert(dest, text, 0, limit - start); + uprv_free(text); + } +} + +/** + * Replaceable API + * + * NOTE: This is for the Replaceable class. There is no rep.cpp, + * so we implement this function here. + */ +UBool Replaceable::hasMetaData() const { + return true; +} + +/** + * Replaceable API + */ +UBool UnicodeString::hasMetaData() const { + return false; +} + +UnicodeString& +UnicodeString::doReverse(int32_t start, int32_t length) { + if(length <= 1 || !cloneArrayIfNeeded()) { + return *this; + } + + // pin the indices to legal values + pinIndices(start, length); + if(length <= 1) { // pinIndices() might have shrunk the length + return *this; + } + + char16_t *left = getArrayStart() + start; + char16_t *right = left + length - 1; // -1 for inclusive boundary (length>=2) + char16_t swap; + UBool hasSupplementary = false; + + // Before the loop we know left=2. + do { + hasSupplementary |= (UBool)U16_IS_LEAD(swap = *left); + hasSupplementary |= (UBool)U16_IS_LEAD(*left++ = *right); + *right-- = swap; + } while(left < right); + // Make sure to test the middle code unit of an odd-length string. + // Redundant if the length is even. + hasSupplementary |= (UBool)U16_IS_LEAD(*left); + + /* if there are supplementary code points in the reversed range, then re-swap their surrogates */ + if(hasSupplementary) { + char16_t swap2; + + left = getArrayStart() + start; + right = left + length - 1; // -1 so that we can look at *(left+1) if left= targetLength || !cloneArrayIfNeeded(targetLength)) { + return false; + } else { + // move contents up by padding width + char16_t *array = getArrayStart(); + int32_t start = targetLength - oldLength; + us_arrayCopy(array, 0, array, start, oldLength); + + // fill in padding character + while(--start >= 0) { + array[start] = padChar; + } + setLength(targetLength); + return true; + } +} + +UBool +UnicodeString::padTrailing(int32_t targetLength, + char16_t padChar) +{ + int32_t oldLength = length(); + if(oldLength >= targetLength || !cloneArrayIfNeeded(targetLength)) { + return false; + } else { + // fill in padding character + char16_t *array = getArrayStart(); + int32_t length = targetLength; + while(--length >= oldLength) { + array[length] = padChar; + } + setLength(targetLength); + return true; + } +} + +//======================================== +// Hashing +//======================================== +int32_t +UnicodeString::doHashCode() const +{ + /* Delegate hash computation to uhash. This makes UnicodeString + * hashing consistent with char16_t* hashing. */ + int32_t hashCode = ustr_hashUCharsN(getArrayStart(), length()); + if (hashCode == kInvalidHashCode) { + hashCode = kEmptyHashCode; + } + return hashCode; +} + +//======================================== +// External Buffer +//======================================== + +char16_t * +UnicodeString::getBuffer(int32_t minCapacity) { + if(minCapacity>=-1 && cloneArrayIfNeeded(minCapacity)) { + fUnion.fFields.fLengthAndFlags|=kOpenGetBuffer; + setZeroLength(); + return getArrayStart(); + } else { + return nullptr; + } +} + +void +UnicodeString::releaseBuffer(int32_t newLength) { + if(fUnion.fFields.fLengthAndFlags&kOpenGetBuffer && newLength>=-1) { + // set the new fLength + int32_t capacity=getCapacity(); + if(newLength==-1) { + // the new length is the string length, capped by fCapacity + const char16_t *array=getArrayStart(), *p=array, *limit=array+capacity; + while(pcapacity) { + newLength=capacity; + } + setLength(newLength); + fUnion.fFields.fLengthAndFlags&=~kOpenGetBuffer; + } +} + +//======================================== +// Miscellaneous +//======================================== +UBool +UnicodeString::cloneArrayIfNeeded(int32_t newCapacity, + int32_t growCapacity, + UBool doCopyArray, + int32_t **pBufferToDelete, + UBool forceClone) { + // default parameters need to be static, therefore + // the defaults are -1 to have convenience defaults + if(newCapacity == -1) { + newCapacity = getCapacity(); + } + + // while a getBuffer(minCapacity) is "open", + // prevent any modifications of the string by returning false here + // if the string is bogus, then only an assignment or similar can revive it + if(!isWritable()) { + return false; + } + + /* + * We need to make a copy of the array if + * the buffer is read-only, or + * the buffer is refCounted (shared), and refCount>1, or + * the buffer is too small. + * Return false if memory could not be allocated. + */ + if(forceClone || + fUnion.fFields.fLengthAndFlags & kBufferIsReadonly || + (fUnion.fFields.fLengthAndFlags & kRefCounted && refCount() > 1) || + newCapacity > getCapacity() + ) { + // check growCapacity for default value and use of the stack buffer + if(growCapacity < 0) { + growCapacity = newCapacity; + } else if(newCapacity <= US_STACKBUF_SIZE && growCapacity > US_STACKBUF_SIZE) { + growCapacity = US_STACKBUF_SIZE; + } + + // save old values + char16_t oldStackBuffer[US_STACKBUF_SIZE]; + char16_t *oldArray; + int32_t oldLength = length(); + int16_t flags = fUnion.fFields.fLengthAndFlags; + + if(flags&kUsingStackBuffer) { + U_ASSERT(!(flags&kRefCounted)); /* kRefCounted and kUsingStackBuffer are mutally exclusive */ + if(doCopyArray && growCapacity > US_STACKBUF_SIZE) { + // copy the stack buffer contents because it will be overwritten with + // fUnion.fFields values + us_arrayCopy(fUnion.fStackFields.fBuffer, 0, oldStackBuffer, 0, oldLength); + oldArray = oldStackBuffer; + } else { + oldArray = nullptr; // no need to copy from the stack buffer to itself + } + } else { + oldArray = fUnion.fFields.fArray; + U_ASSERT(oldArray!=nullptr); /* when stack buffer is not used, oldArray must have a non-nullptr reference */ + } + + // allocate a new array + if(allocate(growCapacity) || + (newCapacity < growCapacity && allocate(newCapacity)) + ) { + if(doCopyArray) { + // copy the contents + // do not copy more than what fits - it may be smaller than before + int32_t minLength = oldLength; + newCapacity = getCapacity(); + if(newCapacity < minLength) { + minLength = newCapacity; + } + if(oldArray != nullptr) { + us_arrayCopy(oldArray, 0, getArrayStart(), 0, minLength); + } + setLength(minLength); + } else { + setZeroLength(); + } + + // release the old array + if(flags & kRefCounted) { + // the array is refCounted; decrement and release if 0 + u_atomic_int32_t *pRefCount = ((u_atomic_int32_t *)oldArray - 1); + if(umtx_atomic_dec(pRefCount) == 0) { + if(pBufferToDelete == 0) { + // Note: cast to (void *) is needed with MSVC, where u_atomic_int32_t + // is defined as volatile. (Volatile has useful non-standard behavior + // with this compiler.) + uprv_free((void *)pRefCount); + } else { + // the caller requested to delete it himself + *pBufferToDelete = (int32_t *)pRefCount; + } + } + } + } else { + // not enough memory for growCapacity and not even for the smaller newCapacity + // reset the old values for setToBogus() to release the array + if(!(flags&kUsingStackBuffer)) { + fUnion.fFields.fArray = oldArray; + } + fUnion.fFields.fLengthAndFlags = flags; + setToBogus(); + return false; + } + } + return true; +} + +// UnicodeStringAppendable ------------------------------------------------- *** + +UnicodeStringAppendable::~UnicodeStringAppendable() {} + +UBool +UnicodeStringAppendable::appendCodeUnit(char16_t c) { + return str.doAppend(&c, 0, 1).isWritable(); +} + +UBool +UnicodeStringAppendable::appendCodePoint(UChar32 c) { + char16_t buffer[U16_MAX_LENGTH]; + int32_t cLength = 0; + UBool isError = false; + U16_APPEND(buffer, cLength, U16_MAX_LENGTH, c, isError); + return !isError && str.doAppend(buffer, 0, cLength).isWritable(); +} + +UBool +UnicodeStringAppendable::appendString(const char16_t *s, int32_t length) { + return str.doAppend(s, 0, length).isWritable(); +} + +UBool +UnicodeStringAppendable::reserveAppendCapacity(int32_t appendCapacity) { + return str.cloneArrayIfNeeded(str.length() + appendCapacity); +} + +char16_t * +UnicodeStringAppendable::getAppendBuffer(int32_t minCapacity, + int32_t desiredCapacityHint, + char16_t *scratch, int32_t scratchCapacity, + int32_t *resultCapacity) { + if(minCapacity < 1 || scratchCapacity < minCapacity) { + *resultCapacity = 0; + return nullptr; + } + int32_t oldLength = str.length(); + if(minCapacity <= (kMaxCapacity - oldLength) && + desiredCapacityHint <= (kMaxCapacity - oldLength) && + str.cloneArrayIfNeeded(oldLength + minCapacity, oldLength + desiredCapacityHint)) { + *resultCapacity = str.getCapacity() - oldLength; + return str.getArrayStart() + oldLength; + } + *resultCapacity = scratchCapacity; + return scratch; +} + +U_NAMESPACE_END + +U_NAMESPACE_USE + +U_CAPI int32_t U_EXPORT2 +uhash_hashUnicodeString(const UElement key) { + const UnicodeString *str = (const UnicodeString*) key.pointer; + return (str == nullptr) ? 0 : str->hashCode(); +} + +// Moved here from uhash_us.cpp so that using a UVector of UnicodeString* +// does not depend on hashtable code. +U_CAPI UBool U_EXPORT2 +uhash_compareUnicodeString(const UElement key1, const UElement key2) { + const UnicodeString *str1 = (const UnicodeString*) key1.pointer; + const UnicodeString *str2 = (const UnicodeString*) key2.pointer; + if (str1 == str2) { + return true; + } + if (str1 == nullptr || str2 == nullptr) { + return false; + } + return *str1 == *str2; +} + +#ifdef U_STATIC_IMPLEMENTATION +/* +This should never be called. It is defined here to make sure that the +virtual vector deleting destructor is defined within unistr.cpp. +The vector deleting destructor is already a part of UObject, +but defining it here makes sure that it is included with this object file. +This makes sure that static library dependencies are kept to a minimum. +*/ +#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 1100 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +static void uprv_UnicodeStringDummy() { + delete [] (new UnicodeString[2]); +} +#pragma GCC diagnostic pop +#endif +#endif diff --git a/deps/icu-small/source/common/unistr_case.cpp b/deps/icu-small/source/common/unistr_case.cpp index f4c43b4889f0b1..af505427426126 100644 --- a/deps/icu-small/source/common/unistr_case.cpp +++ b/deps/icu-small/source/common/unistr_case.cpp @@ -1,250 +1,250 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1999-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: unistr_case.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:2 -* -* created on: 2004aug19 -* created by: Markus W. Scherer -* -* Case-mapping functions moved here from unistr.cpp -*/ - -#include "unicode/utypes.h" -#include "unicode/brkiter.h" -#include "unicode/casemap.h" -#include "unicode/edits.h" -#include "unicode/putil.h" -#include "cstring.h" -#include "cmemory.h" -#include "unicode/ustring.h" -#include "unicode/unistr.h" -#include "unicode/uchar.h" -#include "uassert.h" -#include "ucasemap_imp.h" -#include "uelement.h" - -U_NAMESPACE_BEGIN - -//======================================== -// Read-only implementation -//======================================== - -int8_t -UnicodeString::doCaseCompare(int32_t start, - int32_t length, - const UChar *srcChars, - int32_t srcStart, - int32_t srcLength, - uint32_t options) const -{ - // compare illegal string values - // treat const UChar *srcChars==NULL as an empty string - if(isBogus()) { - return -1; - } - - // pin indices to legal values - pinIndices(start, length); - - if(srcChars == NULL) { - srcStart = srcLength = 0; - } - - // get the correct pointer - const UChar *chars = getArrayStart(); - - chars += start; - if(srcStart!=0) { - srcChars += srcStart; - } - - if(chars != srcChars) { - UErrorCode errorCode=U_ZERO_ERROR; - int32_t result=u_strcmpFold(chars, length, srcChars, srcLength, - options|U_COMPARE_IGNORE_CASE, &errorCode); - if(result!=0) { - return (int8_t)(result >> 24 | 1); - } - } else { - // get the srcLength if necessary - if(srcLength < 0) { - srcLength = u_strlen(srcChars + srcStart); - } - if(length != srcLength) { - return (int8_t)((length - srcLength) >> 24 | 1); - } - } - return 0; -} - -//======================================== -// Write implementation -//======================================== - -UnicodeString & -UnicodeString::caseMap(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM - UStringCaseMapper *stringCaseMapper) { - if(isEmpty() || !isWritable()) { - // nothing to do - return *this; - } - - UChar oldBuffer[2 * US_STACKBUF_SIZE]; - UChar *oldArray; - int32_t oldLength = length(); - int32_t newLength; - UBool writable = isBufferWritable(); - UErrorCode errorCode = U_ZERO_ERROR; - -#if !UCONFIG_NO_BREAK_ITERATION - // Read-only alias to the original string contents for the titlecasing BreakIterator. - // We cannot set the iterator simply to *this because *this is being modified. - UnicodeString oldString; -#endif - - // Try to avoid heap-allocating a new character array for this string. - if (writable ? oldLength <= UPRV_LENGTHOF(oldBuffer) : oldLength < US_STACKBUF_SIZE) { - // Short string: Copy the contents into a temporary buffer and - // case-map back into the current array, or into the stack buffer. - UChar *buffer = getArrayStart(); - int32_t capacity; - oldArray = oldBuffer; - u_memcpy(oldBuffer, buffer, oldLength); - if (writable) { - capacity = getCapacity(); - } else { - // Switch from the read-only alias or shared heap buffer to the stack buffer. - if (!cloneArrayIfNeeded(US_STACKBUF_SIZE, US_STACKBUF_SIZE, /* doCopyArray= */ false)) { - return *this; - } - U_ASSERT(fUnion.fFields.fLengthAndFlags & kUsingStackBuffer); - buffer = fUnion.fStackFields.fBuffer; - capacity = US_STACKBUF_SIZE; - } -#if !UCONFIG_NO_BREAK_ITERATION - if (iter != nullptr) { - oldString.setTo(false, oldArray, oldLength); - iter->setText(oldString); - } -#endif - newLength = stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR - buffer, capacity, - oldArray, oldLength, NULL, errorCode); - if (U_SUCCESS(errorCode)) { - setLength(newLength); - return *this; - } else if (errorCode == U_BUFFER_OVERFLOW_ERROR) { - // common overflow handling below - } else { - setToBogus(); - return *this; - } - } else { - // Longer string or read-only buffer: - // Collect only changes and then apply them to this string. - // Case mapping often changes only small parts of a string, - // and often does not change its length. - oldArray = getArrayStart(); - Edits edits; - UChar replacementChars[200]; -#if !UCONFIG_NO_BREAK_ITERATION - if (iter != nullptr) { - oldString.setTo(false, oldArray, oldLength); - iter->setText(oldString); - } -#endif - stringCaseMapper(caseLocale, options | U_OMIT_UNCHANGED_TEXT, UCASEMAP_BREAK_ITERATOR - replacementChars, UPRV_LENGTHOF(replacementChars), - oldArray, oldLength, &edits, errorCode); - if (U_SUCCESS(errorCode)) { - // Grow the buffer at most once, not for multiple doReplace() calls. - newLength = oldLength + edits.lengthDelta(); - if (newLength > oldLength && !cloneArrayIfNeeded(newLength, newLength)) { - return *this; - } - for (Edits::Iterator ei = edits.getCoarseChangesIterator(); ei.next(errorCode);) { - doReplace(ei.destinationIndex(), ei.oldLength(), - replacementChars, ei.replacementIndex(), ei.newLength()); - } - if (U_FAILURE(errorCode)) { - setToBogus(); - } - return *this; - } else if (errorCode == U_BUFFER_OVERFLOW_ERROR) { - // common overflow handling below - newLength = oldLength + edits.lengthDelta(); - } else { - setToBogus(); - return *this; - } - } - - // Handle buffer overflow, newLength is known. - // We need to allocate a new buffer for the internal string case mapping function. - // This is very similar to how doReplace() keeps the old array pointer - // and deletes the old array itself after it is done. - // In addition, we are forcing cloneArrayIfNeeded() to always allocate a new array. - int32_t *bufferToDelete = 0; - if (!cloneArrayIfNeeded(newLength, newLength, false, &bufferToDelete, true)) { - return *this; - } - errorCode = U_ZERO_ERROR; - // No need to iter->setText() again: The case mapper restarts via iter->first(). - newLength = stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR - getArrayStart(), getCapacity(), - oldArray, oldLength, NULL, errorCode); - if (bufferToDelete) { - uprv_free(bufferToDelete); - } - if (U_SUCCESS(errorCode)) { - setLength(newLength); - } else { - setToBogus(); - } - return *this; -} - -UnicodeString & -UnicodeString::foldCase(uint32_t options) { - return caseMap(UCASE_LOC_ROOT, options, UCASEMAP_BREAK_ITERATOR_NULL ustrcase_internalFold); -} - -U_NAMESPACE_END - -// Defined here to reduce dependencies on break iterator -U_CAPI int32_t U_EXPORT2 -uhash_hashCaselessUnicodeString(const UElement key) { - U_NAMESPACE_USE - const UnicodeString *str = (const UnicodeString*) key.pointer; - if (str == NULL) { - return 0; - } - // Inefficient; a better way would be to have a hash function in - // UnicodeString that does case folding on the fly. - UnicodeString copy(*str); - return copy.foldCase().hashCode(); -} - -// Defined here to reduce dependencies on break iterator -U_CAPI UBool U_EXPORT2 -uhash_compareCaselessUnicodeString(const UElement key1, const UElement key2) { - U_NAMESPACE_USE - const UnicodeString *str1 = (const UnicodeString*) key1.pointer; - const UnicodeString *str2 = (const UnicodeString*) key2.pointer; - if (str1 == str2) { - return true; - } - if (str1 == NULL || str2 == NULL) { - return false; - } - return str1->caseCompare(*str2, U_FOLD_CASE_DEFAULT) == 0; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1999-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: unistr_case.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:2 +* +* created on: 2004aug19 +* created by: Markus W. Scherer +* +* Case-mapping functions moved here from unistr.cpp +*/ + +#include "unicode/utypes.h" +#include "unicode/brkiter.h" +#include "unicode/casemap.h" +#include "unicode/edits.h" +#include "unicode/putil.h" +#include "cstring.h" +#include "cmemory.h" +#include "unicode/ustring.h" +#include "unicode/unistr.h" +#include "unicode/uchar.h" +#include "uassert.h" +#include "ucasemap_imp.h" +#include "uelement.h" + +U_NAMESPACE_BEGIN + +//======================================== +// Read-only implementation +//======================================== + +int8_t +UnicodeString::doCaseCompare(int32_t start, + int32_t length, + const char16_t *srcChars, + int32_t srcStart, + int32_t srcLength, + uint32_t options) const +{ + // compare illegal string values + // treat const char16_t *srcChars==nullptr as an empty string + if(isBogus()) { + return -1; + } + + // pin indices to legal values + pinIndices(start, length); + + if(srcChars == nullptr) { + srcStart = srcLength = 0; + } + + // get the correct pointer + const char16_t *chars = getArrayStart(); + + chars += start; + if(srcStart!=0) { + srcChars += srcStart; + } + + if(chars != srcChars) { + UErrorCode errorCode=U_ZERO_ERROR; + int32_t result=u_strcmpFold(chars, length, srcChars, srcLength, + options|U_COMPARE_IGNORE_CASE, &errorCode); + if(result!=0) { + return (int8_t)(result >> 24 | 1); + } + } else { + // get the srcLength if necessary + if(srcLength < 0) { + srcLength = u_strlen(srcChars + srcStart); + } + if(length != srcLength) { + return (int8_t)((length - srcLength) >> 24 | 1); + } + } + return 0; +} + +//======================================== +// Write implementation +//======================================== + +UnicodeString & +UnicodeString::caseMap(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM + UStringCaseMapper *stringCaseMapper) { + if(isEmpty() || !isWritable()) { + // nothing to do + return *this; + } + + char16_t oldBuffer[2 * US_STACKBUF_SIZE]; + char16_t *oldArray; + int32_t oldLength = length(); + int32_t newLength; + UBool writable = isBufferWritable(); + UErrorCode errorCode = U_ZERO_ERROR; + +#if !UCONFIG_NO_BREAK_ITERATION + // Read-only alias to the original string contents for the titlecasing BreakIterator. + // We cannot set the iterator simply to *this because *this is being modified. + UnicodeString oldString; +#endif + + // Try to avoid heap-allocating a new character array for this string. + if (writable ? oldLength <= UPRV_LENGTHOF(oldBuffer) : oldLength < US_STACKBUF_SIZE) { + // Short string: Copy the contents into a temporary buffer and + // case-map back into the current array, or into the stack buffer. + char16_t *buffer = getArrayStart(); + int32_t capacity; + oldArray = oldBuffer; + u_memcpy(oldBuffer, buffer, oldLength); + if (writable) { + capacity = getCapacity(); + } else { + // Switch from the read-only alias or shared heap buffer to the stack buffer. + if (!cloneArrayIfNeeded(US_STACKBUF_SIZE, US_STACKBUF_SIZE, /* doCopyArray= */ false)) { + return *this; + } + U_ASSERT(fUnion.fFields.fLengthAndFlags & kUsingStackBuffer); + buffer = fUnion.fStackFields.fBuffer; + capacity = US_STACKBUF_SIZE; + } +#if !UCONFIG_NO_BREAK_ITERATION + if (iter != nullptr) { + oldString.setTo(false, oldArray, oldLength); + iter->setText(oldString); + } +#endif + newLength = stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR + buffer, capacity, + oldArray, oldLength, nullptr, errorCode); + if (U_SUCCESS(errorCode)) { + setLength(newLength); + return *this; + } else if (errorCode == U_BUFFER_OVERFLOW_ERROR) { + // common overflow handling below + } else { + setToBogus(); + return *this; + } + } else { + // Longer string or read-only buffer: + // Collect only changes and then apply them to this string. + // Case mapping often changes only small parts of a string, + // and often does not change its length. + oldArray = getArrayStart(); + Edits edits; + char16_t replacementChars[200]; +#if !UCONFIG_NO_BREAK_ITERATION + if (iter != nullptr) { + oldString.setTo(false, oldArray, oldLength); + iter->setText(oldString); + } +#endif + stringCaseMapper(caseLocale, options | U_OMIT_UNCHANGED_TEXT, UCASEMAP_BREAK_ITERATOR + replacementChars, UPRV_LENGTHOF(replacementChars), + oldArray, oldLength, &edits, errorCode); + if (U_SUCCESS(errorCode)) { + // Grow the buffer at most once, not for multiple doReplace() calls. + newLength = oldLength + edits.lengthDelta(); + if (newLength > oldLength && !cloneArrayIfNeeded(newLength, newLength)) { + return *this; + } + for (Edits::Iterator ei = edits.getCoarseChangesIterator(); ei.next(errorCode);) { + doReplace(ei.destinationIndex(), ei.oldLength(), + replacementChars, ei.replacementIndex(), ei.newLength()); + } + if (U_FAILURE(errorCode)) { + setToBogus(); + } + return *this; + } else if (errorCode == U_BUFFER_OVERFLOW_ERROR) { + // common overflow handling below + newLength = oldLength + edits.lengthDelta(); + } else { + setToBogus(); + return *this; + } + } + + // Handle buffer overflow, newLength is known. + // We need to allocate a new buffer for the internal string case mapping function. + // This is very similar to how doReplace() keeps the old array pointer + // and deletes the old array itself after it is done. + // In addition, we are forcing cloneArrayIfNeeded() to always allocate a new array. + int32_t *bufferToDelete = 0; + if (!cloneArrayIfNeeded(newLength, newLength, false, &bufferToDelete, true)) { + return *this; + } + errorCode = U_ZERO_ERROR; + // No need to iter->setText() again: The case mapper restarts via iter->first(). + newLength = stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR + getArrayStart(), getCapacity(), + oldArray, oldLength, nullptr, errorCode); + if (bufferToDelete) { + uprv_free(bufferToDelete); + } + if (U_SUCCESS(errorCode)) { + setLength(newLength); + } else { + setToBogus(); + } + return *this; +} + +UnicodeString & +UnicodeString::foldCase(uint32_t options) { + return caseMap(UCASE_LOC_ROOT, options, UCASEMAP_BREAK_ITERATOR_NULL ustrcase_internalFold); +} + +U_NAMESPACE_END + +// Defined here to reduce dependencies on break iterator +U_CAPI int32_t U_EXPORT2 +uhash_hashCaselessUnicodeString(const UElement key) { + U_NAMESPACE_USE + const UnicodeString *str = (const UnicodeString*) key.pointer; + if (str == nullptr) { + return 0; + } + // Inefficient; a better way would be to have a hash function in + // UnicodeString that does case folding on the fly. + UnicodeString copy(*str); + return copy.foldCase().hashCode(); +} + +// Defined here to reduce dependencies on break iterator +U_CAPI UBool U_EXPORT2 +uhash_compareCaselessUnicodeString(const UElement key1, const UElement key2) { + U_NAMESPACE_USE + const UnicodeString *str1 = (const UnicodeString*) key1.pointer; + const UnicodeString *str2 = (const UnicodeString*) key2.pointer; + if (str1 == str2) { + return true; + } + if (str1 == nullptr || str2 == nullptr) { + return false; + } + return str1->caseCompare(*str2, U_FOLD_CASE_DEFAULT) == 0; +} diff --git a/deps/icu-small/source/common/unistr_case_locale.cpp b/deps/icu-small/source/common/unistr_case_locale.cpp index f0f3048d06f517..bd1be852976203 100644 --- a/deps/icu-small/source/common/unistr_case_locale.cpp +++ b/deps/icu-small/source/common/unistr_case_locale.cpp @@ -1,56 +1,56 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: unistr_case_locale.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2011may31 -* created by: Markus W. Scherer -* -* Locale-sensitive case mapping functions (ones that call uloc_getDefault()) -* were moved here to break dependency cycles among parts of the common library. -*/ - -#include "unicode/utypes.h" -#include "unicode/locid.h" -#include "unicode/ucasemap.h" -#include "unicode/unistr.h" -#include "ucasemap_imp.h" - -U_NAMESPACE_BEGIN - -//======================================== -// Write implementation -//======================================== - -UnicodeString & -UnicodeString::toLower() { - return caseMap(ustrcase_getCaseLocale(NULL), 0, - UCASEMAP_BREAK_ITERATOR_NULL ustrcase_internalToLower); -} - -UnicodeString & -UnicodeString::toLower(const Locale &locale) { - return caseMap(ustrcase_getCaseLocale(locale.getBaseName()), 0, - UCASEMAP_BREAK_ITERATOR_NULL ustrcase_internalToLower); -} - -UnicodeString & -UnicodeString::toUpper() { - return caseMap(ustrcase_getCaseLocale(NULL), 0, - UCASEMAP_BREAK_ITERATOR_NULL ustrcase_internalToUpper); -} - -UnicodeString & -UnicodeString::toUpper(const Locale &locale) { - return caseMap(ustrcase_getCaseLocale(locale.getBaseName()), 0, - UCASEMAP_BREAK_ITERATOR_NULL ustrcase_internalToUpper); -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: unistr_case_locale.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2011may31 +* created by: Markus W. Scherer +* +* Locale-sensitive case mapping functions (ones that call uloc_getDefault()) +* were moved here to break dependency cycles among parts of the common library. +*/ + +#include "unicode/utypes.h" +#include "unicode/locid.h" +#include "unicode/ucasemap.h" +#include "unicode/unistr.h" +#include "ucasemap_imp.h" + +U_NAMESPACE_BEGIN + +//======================================== +// Write implementation +//======================================== + +UnicodeString & +UnicodeString::toLower() { + return caseMap(ustrcase_getCaseLocale(nullptr), 0, + UCASEMAP_BREAK_ITERATOR_NULL ustrcase_internalToLower); +} + +UnicodeString & +UnicodeString::toLower(const Locale &locale) { + return caseMap(ustrcase_getCaseLocale(locale.getBaseName()), 0, + UCASEMAP_BREAK_ITERATOR_NULL ustrcase_internalToLower); +} + +UnicodeString & +UnicodeString::toUpper() { + return caseMap(ustrcase_getCaseLocale(nullptr), 0, + UCASEMAP_BREAK_ITERATOR_NULL ustrcase_internalToUpper); +} + +UnicodeString & +UnicodeString::toUpper(const Locale &locale) { + return caseMap(ustrcase_getCaseLocale(locale.getBaseName()), 0, + UCASEMAP_BREAK_ITERATOR_NULL ustrcase_internalToUpper); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/unistr_cnv.cpp b/deps/icu-small/source/common/unistr_cnv.cpp index e1f60d4487acb6..47e2f5b23b7dd2 100644 --- a/deps/icu-small/source/common/unistr_cnv.cpp +++ b/deps/icu-small/source/common/unistr_cnv.cpp @@ -1,417 +1,417 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1999-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: unistr_cnv.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:2 -* -* created on: 2004aug19 -* created by: Markus W. Scherer -* -* Character conversion functions moved here from unistr.cpp -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/putil.h" -#include "cstring.h" -#include "cmemory.h" -#include "unicode/ustring.h" -#include "unicode/unistr.h" -#include "unicode/ucnv.h" -#include "ucnv_imp.h" -#include "putilimp.h" -#include "ustr_cnv.h" -#include "ustr_imp.h" - -U_NAMESPACE_BEGIN - -//======================================== -// Constructors -//======================================== - -#if !U_CHARSET_IS_UTF8 - -UnicodeString::UnicodeString(const char *codepageData) { - fUnion.fFields.fLengthAndFlags = kShortString; - if(codepageData != 0) { - doCodepageCreate(codepageData, (int32_t)uprv_strlen(codepageData), 0); - } -} - -UnicodeString::UnicodeString(const char *codepageData, - int32_t dataLength) { - fUnion.fFields.fLengthAndFlags = kShortString; - if(codepageData != 0) { - doCodepageCreate(codepageData, dataLength, 0); - } -} - -// else see unistr.cpp -#endif - -UnicodeString::UnicodeString(const char *codepageData, - const char *codepage) { - fUnion.fFields.fLengthAndFlags = kShortString; - if(codepageData != 0) { - doCodepageCreate(codepageData, (int32_t)uprv_strlen(codepageData), codepage); - } -} - -UnicodeString::UnicodeString(const char *codepageData, - int32_t dataLength, - const char *codepage) { - fUnion.fFields.fLengthAndFlags = kShortString; - if(codepageData != 0) { - doCodepageCreate(codepageData, dataLength, codepage); - } -} - -UnicodeString::UnicodeString(const char *src, int32_t srcLength, - UConverter *cnv, - UErrorCode &errorCode) { - fUnion.fFields.fLengthAndFlags = kShortString; - if(U_SUCCESS(errorCode)) { - // check arguments - if(src==NULL) { - // treat as an empty string, do nothing more - } else if(srcLength<-1) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - } else { - // get input length - if(srcLength==-1) { - srcLength=(int32_t)uprv_strlen(src); - } - if(srcLength>0) { - if(cnv!=0) { - // use the provided converter - ucnv_resetToUnicode(cnv); - doCodepageCreate(src, srcLength, cnv, errorCode); - } else { - // use the default converter - cnv=u_getDefaultConverter(&errorCode); - doCodepageCreate(src, srcLength, cnv, errorCode); - u_releaseDefaultConverter(cnv); - } - } - } - - if(U_FAILURE(errorCode)) { - setToBogus(); - } - } -} - -//======================================== -// Codeset conversion -//======================================== - -#if !U_CHARSET_IS_UTF8 - -int32_t -UnicodeString::extract(int32_t start, - int32_t length, - char *target, - uint32_t dstSize) const { - return extract(start, length, target, dstSize, 0); -} - -// else see unistr.cpp -#endif - -int32_t -UnicodeString::extract(int32_t start, - int32_t length, - char *target, - uint32_t dstSize, - const char *codepage) const -{ - // if the arguments are illegal, then do nothing - if(/*dstSize < 0 || */(dstSize > 0 && target == 0)) { - return 0; - } - - // pin the indices to legal values - pinIndices(start, length); - - // We need to cast dstSize to int32_t for all subsequent code. - // I don't know why the API was defined with uint32_t but we are stuck with it. - // Also, dstSize==0xffffffff means "unlimited" but if we use target+dstSize - // as a limit in some functions, it may wrap around and yield a pointer - // that compares less-than target. - int32_t capacity; - if(dstSize < 0x7fffffff) { - // Assume that the capacity is real and a limit pointer won't wrap around. - capacity = (int32_t)dstSize; - } else { - // Pin the capacity so that a limit pointer does not wrap around. - char *targetLimit = (char *)U_MAX_PTR(target); - // U_MAX_PTR(target) returns a targetLimit that is at most 0x7fffffff - // greater than target and does not wrap around the top of the address space. - capacity = (int32_t)(targetLimit - target); - } - - // create the converter - UConverter *converter; - UErrorCode status = U_ZERO_ERROR; - - // just write the NUL if the string length is 0 - if(length == 0) { - return u_terminateChars(target, capacity, 0, &status); - } - - // if the codepage is the default, use our cache - // if it is an empty string, then use the "invariant character" conversion - if (codepage == 0) { - const char *defaultName = ucnv_getDefaultName(); - if(UCNV_FAST_IS_UTF8(defaultName)) { - return toUTF8(start, length, target, capacity); - } - converter = u_getDefaultConverter(&status); - } else if (*codepage == 0) { - // use the "invariant characters" conversion - int32_t destLength; - if(length <= capacity) { - destLength = length; - } else { - destLength = capacity; - } - u_UCharsToChars(getArrayStart() + start, target, destLength); - return u_terminateChars(target, capacity, length, &status); - } else { - converter = ucnv_open(codepage, &status); - } - - length = doExtract(start, length, target, capacity, converter, status); - - // close the converter - if (codepage == 0) { - u_releaseDefaultConverter(converter); - } else { - ucnv_close(converter); - } - - return length; -} - -int32_t -UnicodeString::extract(char *dest, int32_t destCapacity, - UConverter *cnv, - UErrorCode &errorCode) const -{ - if(U_FAILURE(errorCode)) { - return 0; - } - - if(isBogus() || destCapacity<0 || (destCapacity>0 && dest==0)) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - // nothing to do? - if(isEmpty()) { - return u_terminateChars(dest, destCapacity, 0, &errorCode); - } - - // get the converter - UBool isDefaultConverter; - if(cnv==0) { - isDefaultConverter=true; - cnv=u_getDefaultConverter(&errorCode); - if(U_FAILURE(errorCode)) { - return 0; - } - } else { - isDefaultConverter=false; - ucnv_resetFromUnicode(cnv); - } - - // convert - int32_t len=doExtract(0, length(), dest, destCapacity, cnv, errorCode); - - // release the converter - if(isDefaultConverter) { - u_releaseDefaultConverter(cnv); - } - - return len; -} - -int32_t -UnicodeString::doExtract(int32_t start, int32_t length, - char *dest, int32_t destCapacity, - UConverter *cnv, - UErrorCode &errorCode) const -{ - if(U_FAILURE(errorCode)) { - if(destCapacity!=0) { - *dest=0; - } - return 0; - } - - const UChar *src=getArrayStart()+start, *srcLimit=src+length; - char *originalDest=dest; - const char *destLimit; - - if(destCapacity==0) { - destLimit=dest=0; - } else if(destCapacity==-1) { - // Pin the limit to U_MAX_PTR if the "magic" destCapacity is used. - destLimit=(char*)U_MAX_PTR(dest); - // for NUL-termination, translate into highest int32_t - destCapacity=0x7fffffff; - } else { - destLimit=dest+destCapacity; - } - - // perform the conversion - ucnv_fromUnicode(cnv, &dest, destLimit, &src, srcLimit, 0, true, &errorCode); - length=(int32_t)(dest-originalDest); - - // if an overflow occurs, then get the preflighting length - if(errorCode==U_BUFFER_OVERFLOW_ERROR) { - char buffer[1024]; - - destLimit=buffer+sizeof(buffer); - do { - dest=buffer; - errorCode=U_ZERO_ERROR; - ucnv_fromUnicode(cnv, &dest, destLimit, &src, srcLimit, 0, true, &errorCode); - length+=(int32_t)(dest-buffer); - } while(errorCode==U_BUFFER_OVERFLOW_ERROR); - } - - return u_terminateChars(originalDest, destCapacity, length, &errorCode); -} - -void -UnicodeString::doCodepageCreate(const char *codepageData, - int32_t dataLength, - const char *codepage) -{ - // if there's nothing to convert, do nothing - if(codepageData == 0 || dataLength == 0 || dataLength < -1) { - return; - } - if(dataLength == -1) { - dataLength = (int32_t)uprv_strlen(codepageData); - } - - UErrorCode status = U_ZERO_ERROR; - - // create the converter - // if the codepage is the default, use our cache - // if it is an empty string, then use the "invariant character" conversion - UConverter *converter; - if (codepage == 0) { - const char *defaultName = ucnv_getDefaultName(); - if(UCNV_FAST_IS_UTF8(defaultName)) { - setToUTF8(StringPiece(codepageData, dataLength)); - return; - } - converter = u_getDefaultConverter(&status); - } else if(*codepage == 0) { - // use the "invariant characters" conversion - if(cloneArrayIfNeeded(dataLength, dataLength, false)) { - u_charsToUChars(codepageData, getArrayStart(), dataLength); - setLength(dataLength); - } else { - setToBogus(); - } - return; - } else { - converter = ucnv_open(codepage, &status); - } - - // if we failed, set the appropriate flags and return - if(U_FAILURE(status)) { - setToBogus(); - return; - } - - // perform the conversion - doCodepageCreate(codepageData, dataLength, converter, status); - if(U_FAILURE(status)) { - setToBogus(); - } - - // close the converter - if(codepage == 0) { - u_releaseDefaultConverter(converter); - } else { - ucnv_close(converter); - } -} - -void -UnicodeString::doCodepageCreate(const char *codepageData, - int32_t dataLength, - UConverter *converter, - UErrorCode &status) -{ - if(U_FAILURE(status)) { - return; - } - - // set up the conversion parameters - const char *mySource = codepageData; - const char *mySourceEnd = mySource + dataLength; - UChar *array, *myTarget; - - // estimate the size needed: - int32_t arraySize; - if(dataLength <= US_STACKBUF_SIZE) { - // try to use the stack buffer - arraySize = US_STACKBUF_SIZE; - } else { - // 1.25 UChar's per source byte should cover most cases - arraySize = dataLength + (dataLength >> 2); - } - - // we do not care about the current contents - UBool doCopyArray = false; - for(;;) { - if(!cloneArrayIfNeeded(arraySize, arraySize, doCopyArray)) { - setToBogus(); - break; - } - - // perform the conversion - array = getArrayStart(); - myTarget = array + length(); - ucnv_toUnicode(converter, &myTarget, array + getCapacity(), - &mySource, mySourceEnd, 0, true, &status); - - // update the conversion parameters - setLength((int32_t)(myTarget - array)); - - // allocate more space and copy data, if needed - if(status == U_BUFFER_OVERFLOW_ERROR) { - // reset the error code - status = U_ZERO_ERROR; - - // keep the previous conversion results - doCopyArray = true; - - // estimate the new size needed, larger than before - // try 2 UChar's per remaining source byte - arraySize = (int32_t)(length() + 2 * (mySourceEnd - mySource)); - } else { - break; - } - } -} - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1999-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: unistr_cnv.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:2 +* +* created on: 2004aug19 +* created by: Markus W. Scherer +* +* Character conversion functions moved here from unistr.cpp +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/putil.h" +#include "cstring.h" +#include "cmemory.h" +#include "unicode/ustring.h" +#include "unicode/unistr.h" +#include "unicode/ucnv.h" +#include "ucnv_imp.h" +#include "putilimp.h" +#include "ustr_cnv.h" +#include "ustr_imp.h" + +U_NAMESPACE_BEGIN + +//======================================== +// Constructors +//======================================== + +#if !U_CHARSET_IS_UTF8 + +UnicodeString::UnicodeString(const char *codepageData) { + fUnion.fFields.fLengthAndFlags = kShortString; + if(codepageData != 0) { + doCodepageCreate(codepageData, (int32_t)uprv_strlen(codepageData), 0); + } +} + +UnicodeString::UnicodeString(const char *codepageData, + int32_t dataLength) { + fUnion.fFields.fLengthAndFlags = kShortString; + if(codepageData != 0) { + doCodepageCreate(codepageData, dataLength, 0); + } +} + +// else see unistr.cpp +#endif + +UnicodeString::UnicodeString(const char *codepageData, + const char *codepage) { + fUnion.fFields.fLengthAndFlags = kShortString; + if(codepageData != 0) { + doCodepageCreate(codepageData, (int32_t)uprv_strlen(codepageData), codepage); + } +} + +UnicodeString::UnicodeString(const char *codepageData, + int32_t dataLength, + const char *codepage) { + fUnion.fFields.fLengthAndFlags = kShortString; + if(codepageData != 0) { + doCodepageCreate(codepageData, dataLength, codepage); + } +} + +UnicodeString::UnicodeString(const char *src, int32_t srcLength, + UConverter *cnv, + UErrorCode &errorCode) { + fUnion.fFields.fLengthAndFlags = kShortString; + if(U_SUCCESS(errorCode)) { + // check arguments + if(src==nullptr) { + // treat as an empty string, do nothing more + } else if(srcLength<-1) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + } else { + // get input length + if(srcLength==-1) { + srcLength=(int32_t)uprv_strlen(src); + } + if(srcLength>0) { + if(cnv!=0) { + // use the provided converter + ucnv_resetToUnicode(cnv); + doCodepageCreate(src, srcLength, cnv, errorCode); + } else { + // use the default converter + cnv=u_getDefaultConverter(&errorCode); + doCodepageCreate(src, srcLength, cnv, errorCode); + u_releaseDefaultConverter(cnv); + } + } + } + + if(U_FAILURE(errorCode)) { + setToBogus(); + } + } +} + +//======================================== +// Codeset conversion +//======================================== + +#if !U_CHARSET_IS_UTF8 + +int32_t +UnicodeString::extract(int32_t start, + int32_t length, + char *target, + uint32_t dstSize) const { + return extract(start, length, target, dstSize, 0); +} + +// else see unistr.cpp +#endif + +int32_t +UnicodeString::extract(int32_t start, + int32_t length, + char *target, + uint32_t dstSize, + const char *codepage) const +{ + // if the arguments are illegal, then do nothing + if(/*dstSize < 0 || */(dstSize > 0 && target == 0)) { + return 0; + } + + // pin the indices to legal values + pinIndices(start, length); + + // We need to cast dstSize to int32_t for all subsequent code. + // I don't know why the API was defined with uint32_t but we are stuck with it. + // Also, dstSize==0xffffffff means "unlimited" but if we use target+dstSize + // as a limit in some functions, it may wrap around and yield a pointer + // that compares less-than target. + int32_t capacity; + if(dstSize < 0x7fffffff) { + // Assume that the capacity is real and a limit pointer won't wrap around. + capacity = (int32_t)dstSize; + } else { + // Pin the capacity so that a limit pointer does not wrap around. + char *targetLimit = (char *)U_MAX_PTR(target); + // U_MAX_PTR(target) returns a targetLimit that is at most 0x7fffffff + // greater than target and does not wrap around the top of the address space. + capacity = (int32_t)(targetLimit - target); + } + + // create the converter + UConverter *converter; + UErrorCode status = U_ZERO_ERROR; + + // just write the NUL if the string length is 0 + if(length == 0) { + return u_terminateChars(target, capacity, 0, &status); + } + + // if the codepage is the default, use our cache + // if it is an empty string, then use the "invariant character" conversion + if (codepage == 0) { + const char *defaultName = ucnv_getDefaultName(); + if(UCNV_FAST_IS_UTF8(defaultName)) { + return toUTF8(start, length, target, capacity); + } + converter = u_getDefaultConverter(&status); + } else if (*codepage == 0) { + // use the "invariant characters" conversion + int32_t destLength; + if(length <= capacity) { + destLength = length; + } else { + destLength = capacity; + } + u_UCharsToChars(getArrayStart() + start, target, destLength); + return u_terminateChars(target, capacity, length, &status); + } else { + converter = ucnv_open(codepage, &status); + } + + length = doExtract(start, length, target, capacity, converter, status); + + // close the converter + if (codepage == 0) { + u_releaseDefaultConverter(converter); + } else { + ucnv_close(converter); + } + + return length; +} + +int32_t +UnicodeString::extract(char *dest, int32_t destCapacity, + UConverter *cnv, + UErrorCode &errorCode) const +{ + if(U_FAILURE(errorCode)) { + return 0; + } + + if(isBogus() || destCapacity<0 || (destCapacity>0 && dest==0)) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + // nothing to do? + if(isEmpty()) { + return u_terminateChars(dest, destCapacity, 0, &errorCode); + } + + // get the converter + UBool isDefaultConverter; + if(cnv==0) { + isDefaultConverter=true; + cnv=u_getDefaultConverter(&errorCode); + if(U_FAILURE(errorCode)) { + return 0; + } + } else { + isDefaultConverter=false; + ucnv_resetFromUnicode(cnv); + } + + // convert + int32_t len=doExtract(0, length(), dest, destCapacity, cnv, errorCode); + + // release the converter + if(isDefaultConverter) { + u_releaseDefaultConverter(cnv); + } + + return len; +} + +int32_t +UnicodeString::doExtract(int32_t start, int32_t length, + char *dest, int32_t destCapacity, + UConverter *cnv, + UErrorCode &errorCode) const +{ + if(U_FAILURE(errorCode)) { + if(destCapacity!=0) { + *dest=0; + } + return 0; + } + + const char16_t *src=getArrayStart()+start, *srcLimit=src+length; + char *originalDest=dest; + const char *destLimit; + + if(destCapacity==0) { + destLimit=dest=0; + } else if(destCapacity==-1) { + // Pin the limit to U_MAX_PTR if the "magic" destCapacity is used. + destLimit=(char*)U_MAX_PTR(dest); + // for NUL-termination, translate into highest int32_t + destCapacity=0x7fffffff; + } else { + destLimit=dest+destCapacity; + } + + // perform the conversion + ucnv_fromUnicode(cnv, &dest, destLimit, &src, srcLimit, 0, true, &errorCode); + length=(int32_t)(dest-originalDest); + + // if an overflow occurs, then get the preflighting length + if(errorCode==U_BUFFER_OVERFLOW_ERROR) { + char buffer[1024]; + + destLimit=buffer+sizeof(buffer); + do { + dest=buffer; + errorCode=U_ZERO_ERROR; + ucnv_fromUnicode(cnv, &dest, destLimit, &src, srcLimit, 0, true, &errorCode); + length+=(int32_t)(dest-buffer); + } while(errorCode==U_BUFFER_OVERFLOW_ERROR); + } + + return u_terminateChars(originalDest, destCapacity, length, &errorCode); +} + +void +UnicodeString::doCodepageCreate(const char *codepageData, + int32_t dataLength, + const char *codepage) +{ + // if there's nothing to convert, do nothing + if(codepageData == 0 || dataLength == 0 || dataLength < -1) { + return; + } + if(dataLength == -1) { + dataLength = (int32_t)uprv_strlen(codepageData); + } + + UErrorCode status = U_ZERO_ERROR; + + // create the converter + // if the codepage is the default, use our cache + // if it is an empty string, then use the "invariant character" conversion + UConverter *converter; + if (codepage == 0) { + const char *defaultName = ucnv_getDefaultName(); + if(UCNV_FAST_IS_UTF8(defaultName)) { + setToUTF8(StringPiece(codepageData, dataLength)); + return; + } + converter = u_getDefaultConverter(&status); + } else if(*codepage == 0) { + // use the "invariant characters" conversion + if(cloneArrayIfNeeded(dataLength, dataLength, false)) { + u_charsToUChars(codepageData, getArrayStart(), dataLength); + setLength(dataLength); + } else { + setToBogus(); + } + return; + } else { + converter = ucnv_open(codepage, &status); + } + + // if we failed, set the appropriate flags and return + if(U_FAILURE(status)) { + setToBogus(); + return; + } + + // perform the conversion + doCodepageCreate(codepageData, dataLength, converter, status); + if(U_FAILURE(status)) { + setToBogus(); + } + + // close the converter + if(codepage == 0) { + u_releaseDefaultConverter(converter); + } else { + ucnv_close(converter); + } +} + +void +UnicodeString::doCodepageCreate(const char *codepageData, + int32_t dataLength, + UConverter *converter, + UErrorCode &status) +{ + if(U_FAILURE(status)) { + return; + } + + // set up the conversion parameters + const char *mySource = codepageData; + const char *mySourceEnd = mySource + dataLength; + char16_t *array, *myTarget; + + // estimate the size needed: + int32_t arraySize; + if(dataLength <= US_STACKBUF_SIZE) { + // try to use the stack buffer + arraySize = US_STACKBUF_SIZE; + } else { + // 1.25 char16_t's per source byte should cover most cases + arraySize = dataLength + (dataLength >> 2); + } + + // we do not care about the current contents + UBool doCopyArray = false; + for(;;) { + if(!cloneArrayIfNeeded(arraySize, arraySize, doCopyArray)) { + setToBogus(); + break; + } + + // perform the conversion + array = getArrayStart(); + myTarget = array + length(); + ucnv_toUnicode(converter, &myTarget, array + getCapacity(), + &mySource, mySourceEnd, 0, true, &status); + + // update the conversion parameters + setLength((int32_t)(myTarget - array)); + + // allocate more space and copy data, if needed + if(status == U_BUFFER_OVERFLOW_ERROR) { + // reset the error code + status = U_ZERO_ERROR; + + // keep the previous conversion results + doCopyArray = true; + + // estimate the new size needed, larger than before + // try 2 char16_t's per remaining source byte + arraySize = (int32_t)(length() + 2 * (mySourceEnd - mySource)); + } else { + break; + } + } +} + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/common/unistr_props.cpp b/deps/icu-small/source/common/unistr_props.cpp index 40064757902ca8..cb8e4eba0c4759 100644 --- a/deps/icu-small/source/common/unistr_props.cpp +++ b/deps/icu-small/source/common/unistr_props.cpp @@ -1,77 +1,77 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1999-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: unistr_props.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:2 -* -* created on: 2004aug25 -* created by: Markus W. Scherer -* -* Character property dependent functions moved here from unistr.cpp -*/ - -#include "unicode/utypes.h" -#include "unicode/uchar.h" -#include "unicode/unistr.h" -#include "unicode/utf16.h" - -U_NAMESPACE_BEGIN - -UnicodeString& -UnicodeString::trim() -{ - if(isBogus()) { - return *this; - } - - UChar *array = getArrayStart(); - UChar32 c; - int32_t oldLength = this->length(); - int32_t i = oldLength, length; - - // first cut off trailing white space - for(;;) { - length = i; - if(i <= 0) { - break; - } - U16_PREV(array, 0, i, c); - if(!(c == 0x20 || u_isWhitespace(c))) { - break; - } - } - if(length < oldLength) { - setLength(length); - } - - // find leading white space - int32_t start; - i = 0; - for(;;) { - start = i; - if(i >= length) { - break; - } - U16_NEXT(array, i, length, c); - if(!(c == 0x20 || u_isWhitespace(c))) { - break; - } - } - - // move string forward over leading white space - if(start > 0) { - doReplace(0, start, 0, 0, 0); - } - - return *this; -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1999-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: unistr_props.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:2 +* +* created on: 2004aug25 +* created by: Markus W. Scherer +* +* Character property dependent functions moved here from unistr.cpp +*/ + +#include "unicode/utypes.h" +#include "unicode/uchar.h" +#include "unicode/unistr.h" +#include "unicode/utf16.h" + +U_NAMESPACE_BEGIN + +UnicodeString& +UnicodeString::trim() +{ + if(isBogus()) { + return *this; + } + + char16_t *array = getArrayStart(); + UChar32 c; + int32_t oldLength = this->length(); + int32_t i = oldLength, length; + + // first cut off trailing white space + for(;;) { + length = i; + if(i <= 0) { + break; + } + U16_PREV(array, 0, i, c); + if(!(c == 0x20 || u_isWhitespace(c))) { + break; + } + } + if(length < oldLength) { + setLength(length); + } + + // find leading white space + int32_t start; + i = 0; + for(;;) { + start = i; + if(i >= length) { + break; + } + U16_NEXT(array, i, length, c); + if(!(c == 0x20 || u_isWhitespace(c))) { + break; + } + } + + // move string forward over leading white space + if(start > 0) { + doReplace(0, start, 0, 0, 0); + } + + return *this; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/unistr_titlecase_brkiter.cpp b/deps/icu-small/source/common/unistr_titlecase_brkiter.cpp index 4969884b0dc9b9..6b574deab692ec 100644 --- a/deps/icu-small/source/common/unistr_titlecase_brkiter.cpp +++ b/deps/icu-small/source/common/unistr_titlecase_brkiter.cpp @@ -1,57 +1,57 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: unistr_titlecase_brkiter.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:2 -* -* created on: 2011may30 -* created by: Markus W. Scherer -* -* Titlecasing functions that are based on BreakIterator -* were moved here to break dependency cycles among parts of the common library. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/brkiter.h" -#include "unicode/locid.h" -#include "unicode/ucasemap.h" -#include "unicode/unistr.h" -#include "ucasemap_imp.h" - -U_NAMESPACE_BEGIN - -UnicodeString & -UnicodeString::toTitle(BreakIterator *iter) { - return toTitle(iter, Locale::getDefault(), 0); -} - -UnicodeString & -UnicodeString::toTitle(BreakIterator *iter, const Locale &locale) { - return toTitle(iter, locale, 0); -} - -UnicodeString & -UnicodeString::toTitle(BreakIterator *iter, const Locale &locale, uint32_t options) { - LocalPointer ownedIter; - UErrorCode errorCode = U_ZERO_ERROR; - iter = ustrcase_getTitleBreakIterator(&locale, "", options, iter, ownedIter, errorCode); - if (iter == nullptr) { - setToBogus(); - return *this; - } - caseMap(ustrcase_getCaseLocale(locale.getBaseName()), options, iter, ustrcase_internalToTitle); - return *this; -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_BREAK_ITERATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: unistr_titlecase_brkiter.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:2 +* +* created on: 2011may30 +* created by: Markus W. Scherer +* +* Titlecasing functions that are based on BreakIterator +* were moved here to break dependency cycles among parts of the common library. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/brkiter.h" +#include "unicode/locid.h" +#include "unicode/ucasemap.h" +#include "unicode/unistr.h" +#include "ucasemap_imp.h" + +U_NAMESPACE_BEGIN + +UnicodeString & +UnicodeString::toTitle(BreakIterator *iter) { + return toTitle(iter, Locale::getDefault(), 0); +} + +UnicodeString & +UnicodeString::toTitle(BreakIterator *iter, const Locale &locale) { + return toTitle(iter, locale, 0); +} + +UnicodeString & +UnicodeString::toTitle(BreakIterator *iter, const Locale &locale, uint32_t options) { + LocalPointer ownedIter; + UErrorCode errorCode = U_ZERO_ERROR; + iter = ustrcase_getTitleBreakIterator(&locale, "", options, iter, ownedIter, errorCode); + if (iter == nullptr) { + setToBogus(); + return *this; + } + caseMap(ustrcase_getCaseLocale(locale.getBaseName()), options, iter, ustrcase_internalToTitle); + return *this; +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_BREAK_ITERATION diff --git a/deps/icu-small/source/common/unistrappender.h b/deps/icu-small/source/common/unistrappender.h index 75fcb9e775f1eb..23094774ad168a 100644 --- a/deps/icu-small/source/common/unistrappender.h +++ b/deps/icu-small/source/common/unistrappender.h @@ -1,90 +1,90 @@ -// © 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 unistrappender.h -****************************************************************************** -*/ - -#ifndef __UNISTRAPPENDER_H__ -#define __UNISTRAPPENDER_H__ - -#include "unicode/unistr.h" -#include "unicode/uobject.h" -#include "unicode/utf16.h" -#include "unicode/utypes.h" -#include "cmemory.h" - -U_NAMESPACE_BEGIN - -/** - * An optimization for the slowness of calling UnicodeString::append() - * one character at a time in a loop. It stores appends in a buffer while - * never actually calling append on the unicode string unless the buffer - * fills up or is flushed. - * - * proper usage: - * { - * UnicodeStringAppender appender(astring); - * for (int32_t i = 0; i < 100; ++i) { - * appender.append((UChar) i); - * } - * // appender flushed automatically when it goes out of scope. - * } - */ -class UnicodeStringAppender : public UMemory { -public: - - /** - * dest is the UnicodeString being appended to. It must always - * exist while this instance exists. - */ - UnicodeStringAppender(UnicodeString &dest) : fDest(&dest), fIdx(0) { } - - inline void append(UChar x) { - if (fIdx == UPRV_LENGTHOF(fBuffer)) { - fDest->append(fBuffer, 0, fIdx); - fIdx = 0; - } - fBuffer[fIdx++] = x; - } - - inline void append(UChar32 x) { - if (fIdx >= UPRV_LENGTHOF(fBuffer) - 1) { - fDest->append(fBuffer, 0, fIdx); - fIdx = 0; - } - U16_APPEND_UNSAFE(fBuffer, fIdx, x); - } - - /** - * Ensures that all appended characters have been written out to dest. - */ - inline void flush() { - if (fIdx) { - fDest->append(fBuffer, 0, fIdx); - } - fIdx = 0; - } - - /** - * flush the buffer when we go out of scope. - */ - ~UnicodeStringAppender() { - flush(); - } -private: - UnicodeString *fDest; - int32_t fIdx; - UChar fBuffer[32]; - UnicodeStringAppender(const UnicodeStringAppender &other); - UnicodeStringAppender &operator=(const UnicodeStringAppender &other); -}; - -U_NAMESPACE_END - -#endif +// © 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 unistrappender.h +****************************************************************************** +*/ + +#ifndef __UNISTRAPPENDER_H__ +#define __UNISTRAPPENDER_H__ + +#include "unicode/unistr.h" +#include "unicode/uobject.h" +#include "unicode/utf16.h" +#include "unicode/utypes.h" +#include "cmemory.h" + +U_NAMESPACE_BEGIN + +/** + * An optimization for the slowness of calling UnicodeString::append() + * one character at a time in a loop. It stores appends in a buffer while + * never actually calling append on the unicode string unless the buffer + * fills up or is flushed. + * + * proper usage: + * { + * UnicodeStringAppender appender(astring); + * for (int32_t i = 0; i < 100; ++i) { + * appender.append((char16_t) i); + * } + * // appender flushed automatically when it goes out of scope. + * } + */ +class UnicodeStringAppender : public UMemory { +public: + + /** + * dest is the UnicodeString being appended to. It must always + * exist while this instance exists. + */ + UnicodeStringAppender(UnicodeString &dest) : fDest(&dest), fIdx(0) { } + + inline void append(char16_t x) { + if (fIdx == UPRV_LENGTHOF(fBuffer)) { + fDest->append(fBuffer, 0, fIdx); + fIdx = 0; + } + fBuffer[fIdx++] = x; + } + + inline void append(UChar32 x) { + if (fIdx >= UPRV_LENGTHOF(fBuffer) - 1) { + fDest->append(fBuffer, 0, fIdx); + fIdx = 0; + } + U16_APPEND_UNSAFE(fBuffer, fIdx, x); + } + + /** + * Ensures that all appended characters have been written out to dest. + */ + inline void flush() { + if (fIdx) { + fDest->append(fBuffer, 0, fIdx); + } + fIdx = 0; + } + + /** + * flush the buffer when we go out of scope. + */ + ~UnicodeStringAppender() { + flush(); + } +private: + UnicodeString *fDest; + int32_t fIdx; + char16_t fBuffer[32]; + UnicodeStringAppender(const UnicodeStringAppender &other); + UnicodeStringAppender &operator=(const UnicodeStringAppender &other); +}; + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/common/unorm.cpp b/deps/icu-small/source/common/unorm.cpp index cf3915c27f3c9b..1234e18f6b77b3 100644 --- a/deps/icu-small/source/common/unorm.cpp +++ b/deps/icu-small/source/common/unorm.cpp @@ -1,280 +1,280 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (c) 1996-2014, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* File unorm.cpp -* -* Created by: Vladimir Weinstein 12052000 -* -* Modification history : -* -* Date Name Description -* 02/01/01 synwee Added normalization quickcheck enum and method. -* 02/12/01 synwee Commented out quickcheck util api has been approved -* Added private method for doing FCD checks -* 02/23/01 synwee Modified quickcheck and checkFCE to run through -* string for codepoints < 0x300 for the normalization -* mode NFC. -* 05/25/01+ Markus Scherer total rewrite, implement all normalization here -* instead of just wrappers around normlzr.cpp, -* load unorm.dat, support Unicode 3.1 with -* supplementary code points, etc. -* 2009-nov..2010-jan Markus Scherer total rewrite, new Normalizer2 API & code -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_NORMALIZATION - -#include "unicode/udata.h" -#include "unicode/ustring.h" -#include "unicode/uiter.h" -#include "unicode/unorm.h" -#include "unicode/unorm2.h" -#include "normalizer2impl.h" -#include "unormimp.h" -#include "uprops.h" -#include "ustr_imp.h" - -U_NAMESPACE_USE - -/* quick check functions ---------------------------------------------------- */ - -U_CAPI UNormalizationCheckResult U_EXPORT2 -unorm_quickCheck(const UChar *src, - int32_t srcLength, - UNormalizationMode mode, - UErrorCode *pErrorCode) { - const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); - return unorm2_quickCheck((const UNormalizer2 *)n2, src, srcLength, pErrorCode); -} - -U_CAPI UNormalizationCheckResult U_EXPORT2 -unorm_quickCheckWithOptions(const UChar *src, int32_t srcLength, - UNormalizationMode mode, int32_t options, - UErrorCode *pErrorCode) { - const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); - if(options&UNORM_UNICODE_3_2) { - FilteredNormalizer2 fn2(*n2, *uniset_getUnicode32Instance(*pErrorCode)); - return unorm2_quickCheck( - reinterpret_cast(static_cast(&fn2)), - src, srcLength, pErrorCode); - } else { - return unorm2_quickCheck((const UNormalizer2 *)n2, src, srcLength, pErrorCode); - } -} - -U_CAPI UBool U_EXPORT2 -unorm_isNormalized(const UChar *src, int32_t srcLength, - UNormalizationMode mode, - UErrorCode *pErrorCode) { - const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); - return unorm2_isNormalized((const UNormalizer2 *)n2, src, srcLength, pErrorCode); -} - -U_CAPI UBool U_EXPORT2 -unorm_isNormalizedWithOptions(const UChar *src, int32_t srcLength, - UNormalizationMode mode, int32_t options, - UErrorCode *pErrorCode) { - const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); - if(options&UNORM_UNICODE_3_2) { - FilteredNormalizer2 fn2(*n2, *uniset_getUnicode32Instance(*pErrorCode)); - return unorm2_isNormalized( - reinterpret_cast(static_cast(&fn2)), - src, srcLength, pErrorCode); - } else { - return unorm2_isNormalized((const UNormalizer2 *)n2, src, srcLength, pErrorCode); - } -} - -/* normalize() API ---------------------------------------------------------- */ - -/** Public API for normalizing. */ -U_CAPI int32_t U_EXPORT2 -unorm_normalize(const UChar *src, int32_t srcLength, - UNormalizationMode mode, int32_t options, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) { - const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); - if(options&UNORM_UNICODE_3_2) { - FilteredNormalizer2 fn2(*n2, *uniset_getUnicode32Instance(*pErrorCode)); - return unorm2_normalize( - reinterpret_cast(static_cast(&fn2)), - src, srcLength, dest, destCapacity, pErrorCode); - } else { - return unorm2_normalize((const UNormalizer2 *)n2, - src, srcLength, dest, destCapacity, pErrorCode); - } -} - - -/* iteration functions ------------------------------------------------------ */ - -static int32_t -_iterate(UCharIterator *src, UBool forward, - UChar *dest, int32_t destCapacity, - const Normalizer2 *n2, - UBool doNormalize, UBool *pNeededToNormalize, - UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if(destCapacity<0 || (dest==NULL && destCapacity>0) || src==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - if(pNeededToNormalize!=NULL) { - *pNeededToNormalize=false; - } - if(!(forward ? src->hasNext(src) : src->hasPrevious(src))) { - return u_terminateUChars(dest, destCapacity, 0, pErrorCode); - } - - UnicodeString buffer; - UChar32 c; - if(forward) { - /* get one character and ignore its properties */ - buffer.append(uiter_next32(src)); - /* get all following characters until we see a boundary */ - while((c=uiter_next32(src))>=0) { - if(n2->hasBoundaryBefore(c)) { - /* back out the latest movement to stop at the boundary */ - src->move(src, -U16_LENGTH(c), UITER_CURRENT); - break; - } else { - buffer.append(c); - } - } - } else { - while((c=uiter_previous32(src))>=0) { - /* always write this character to the front of the buffer */ - buffer.insert(0, c); - /* stop if this just-copied character is a boundary */ - if(n2->hasBoundaryBefore(c)) { - break; - } - } - } - - UnicodeString destString(dest, 0, destCapacity); - if(buffer.length()>0 && doNormalize) { - n2->normalize(buffer, destString, *pErrorCode).extract(dest, destCapacity, *pErrorCode); - if(pNeededToNormalize!=NULL && U_SUCCESS(*pErrorCode)) { - *pNeededToNormalize= destString!=buffer; - } - return destString.length(); - } else { - /* just copy the source characters */ - return buffer.extract(dest, destCapacity, *pErrorCode); - } -} - -static int32_t -unorm_iterate(UCharIterator *src, UBool forward, - UChar *dest, int32_t destCapacity, - UNormalizationMode mode, int32_t options, - UBool doNormalize, UBool *pNeededToNormalize, - UErrorCode *pErrorCode) { - const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); - if(options&UNORM_UNICODE_3_2) { - const UnicodeSet *uni32 = uniset_getUnicode32Instance(*pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return 0; - } - FilteredNormalizer2 fn2(*n2, *uni32); - return _iterate(src, forward, dest, destCapacity, - &fn2, doNormalize, pNeededToNormalize, pErrorCode); - } - return _iterate(src, forward, dest, destCapacity, - n2, doNormalize, pNeededToNormalize, pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -unorm_previous(UCharIterator *src, - UChar *dest, int32_t destCapacity, - UNormalizationMode mode, int32_t options, - UBool doNormalize, UBool *pNeededToNormalize, - UErrorCode *pErrorCode) { - return unorm_iterate(src, false, - dest, destCapacity, - mode, options, - doNormalize, pNeededToNormalize, - pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -unorm_next(UCharIterator *src, - UChar *dest, int32_t destCapacity, - UNormalizationMode mode, int32_t options, - UBool doNormalize, UBool *pNeededToNormalize, - UErrorCode *pErrorCode) { - return unorm_iterate(src, true, - dest, destCapacity, - mode, options, - doNormalize, pNeededToNormalize, - pErrorCode); -} - -/* Concatenation of normalized strings -------------------------------------- */ - -static int32_t -_concatenate(const UChar *left, int32_t leftLength, - const UChar *right, int32_t rightLength, - UChar *dest, int32_t destCapacity, - const Normalizer2 *n2, - UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if(destCapacity<0 || (dest==NULL && destCapacity>0) || - left==NULL || leftLength<-1 || right==NULL || rightLength<-1) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* check for overlapping right and destination */ - if( dest!=NULL && - ((right>=dest && right<(dest+destCapacity)) || - (rightLength>0 && dest>=right && dest<(right+rightLength))) - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* allow left==dest */ - UnicodeString destString; - if(left==dest) { - destString.setTo(dest, leftLength, destCapacity); - } else { - destString.setTo(dest, 0, destCapacity); - destString.append(left, leftLength); - } - return n2->append(destString, UnicodeString(rightLength<0, right, rightLength), *pErrorCode). - extract(dest, destCapacity, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -unorm_concatenate(const UChar *left, int32_t leftLength, - const UChar *right, int32_t rightLength, - UChar *dest, int32_t destCapacity, - UNormalizationMode mode, int32_t options, - UErrorCode *pErrorCode) { - const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); - if(options&UNORM_UNICODE_3_2) { - const UnicodeSet *uni32 = uniset_getUnicode32Instance(*pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return 0; - } - FilteredNormalizer2 fn2(*n2, *uni32); - return _concatenate(left, leftLength, right, rightLength, - dest, destCapacity, &fn2, pErrorCode); - } - return _concatenate(left, leftLength, right, rightLength, - dest, destCapacity, n2, pErrorCode); -} - -#endif /* #if !UCONFIG_NO_NORMALIZATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (c) 1996-2014, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* File unorm.cpp +* +* Created by: Vladimir Weinstein 12052000 +* +* Modification history : +* +* Date Name Description +* 02/01/01 synwee Added normalization quickcheck enum and method. +* 02/12/01 synwee Commented out quickcheck util api has been approved +* Added private method for doing FCD checks +* 02/23/01 synwee Modified quickcheck and checkFCE to run through +* string for codepoints < 0x300 for the normalization +* mode NFC. +* 05/25/01+ Markus Scherer total rewrite, implement all normalization here +* instead of just wrappers around normlzr.cpp, +* load unorm.dat, support Unicode 3.1 with +* supplementary code points, etc. +* 2009-nov..2010-jan Markus Scherer total rewrite, new Normalizer2 API & code +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_NORMALIZATION + +#include "unicode/udata.h" +#include "unicode/ustring.h" +#include "unicode/uiter.h" +#include "unicode/unorm.h" +#include "unicode/unorm2.h" +#include "normalizer2impl.h" +#include "unormimp.h" +#include "uprops.h" +#include "ustr_imp.h" + +U_NAMESPACE_USE + +/* quick check functions ---------------------------------------------------- */ + +U_CAPI UNormalizationCheckResult U_EXPORT2 +unorm_quickCheck(const char16_t *src, + int32_t srcLength, + UNormalizationMode mode, + UErrorCode *pErrorCode) { + const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); + return unorm2_quickCheck((const UNormalizer2 *)n2, src, srcLength, pErrorCode); +} + +U_CAPI UNormalizationCheckResult U_EXPORT2 +unorm_quickCheckWithOptions(const char16_t *src, int32_t srcLength, + UNormalizationMode mode, int32_t options, + UErrorCode *pErrorCode) { + const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); + if(options&UNORM_UNICODE_3_2) { + FilteredNormalizer2 fn2(*n2, *uniset_getUnicode32Instance(*pErrorCode)); + return unorm2_quickCheck( + reinterpret_cast(static_cast(&fn2)), + src, srcLength, pErrorCode); + } else { + return unorm2_quickCheck((const UNormalizer2 *)n2, src, srcLength, pErrorCode); + } +} + +U_CAPI UBool U_EXPORT2 +unorm_isNormalized(const char16_t *src, int32_t srcLength, + UNormalizationMode mode, + UErrorCode *pErrorCode) { + const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); + return unorm2_isNormalized((const UNormalizer2 *)n2, src, srcLength, pErrorCode); +} + +U_CAPI UBool U_EXPORT2 +unorm_isNormalizedWithOptions(const char16_t *src, int32_t srcLength, + UNormalizationMode mode, int32_t options, + UErrorCode *pErrorCode) { + const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); + if(options&UNORM_UNICODE_3_2) { + FilteredNormalizer2 fn2(*n2, *uniset_getUnicode32Instance(*pErrorCode)); + return unorm2_isNormalized( + reinterpret_cast(static_cast(&fn2)), + src, srcLength, pErrorCode); + } else { + return unorm2_isNormalized((const UNormalizer2 *)n2, src, srcLength, pErrorCode); + } +} + +/* normalize() API ---------------------------------------------------------- */ + +/** Public API for normalizing. */ +U_CAPI int32_t U_EXPORT2 +unorm_normalize(const char16_t *src, int32_t srcLength, + UNormalizationMode mode, int32_t options, + char16_t *dest, int32_t destCapacity, + UErrorCode *pErrorCode) { + const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); + if(options&UNORM_UNICODE_3_2) { + FilteredNormalizer2 fn2(*n2, *uniset_getUnicode32Instance(*pErrorCode)); + return unorm2_normalize( + reinterpret_cast(static_cast(&fn2)), + src, srcLength, dest, destCapacity, pErrorCode); + } else { + return unorm2_normalize((const UNormalizer2 *)n2, + src, srcLength, dest, destCapacity, pErrorCode); + } +} + + +/* iteration functions ------------------------------------------------------ */ + +static int32_t +_iterate(UCharIterator *src, UBool forward, + char16_t *dest, int32_t destCapacity, + const Normalizer2 *n2, + UBool doNormalize, UBool *pNeededToNormalize, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(destCapacity<0 || (dest==nullptr && destCapacity>0) || src==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + if(pNeededToNormalize!=nullptr) { + *pNeededToNormalize=false; + } + if(!(forward ? src->hasNext(src) : src->hasPrevious(src))) { + return u_terminateUChars(dest, destCapacity, 0, pErrorCode); + } + + UnicodeString buffer; + UChar32 c; + if(forward) { + /* get one character and ignore its properties */ + buffer.append(uiter_next32(src)); + /* get all following characters until we see a boundary */ + while((c=uiter_next32(src))>=0) { + if(n2->hasBoundaryBefore(c)) { + /* back out the latest movement to stop at the boundary */ + src->move(src, -U16_LENGTH(c), UITER_CURRENT); + break; + } else { + buffer.append(c); + } + } + } else { + while((c=uiter_previous32(src))>=0) { + /* always write this character to the front of the buffer */ + buffer.insert(0, c); + /* stop if this just-copied character is a boundary */ + if(n2->hasBoundaryBefore(c)) { + break; + } + } + } + + UnicodeString destString(dest, 0, destCapacity); + if(buffer.length()>0 && doNormalize) { + n2->normalize(buffer, destString, *pErrorCode).extract(dest, destCapacity, *pErrorCode); + if(pNeededToNormalize!=nullptr && U_SUCCESS(*pErrorCode)) { + *pNeededToNormalize= destString!=buffer; + } + return destString.length(); + } else { + /* just copy the source characters */ + return buffer.extract(dest, destCapacity, *pErrorCode); + } +} + +static int32_t +unorm_iterate(UCharIterator *src, UBool forward, + char16_t *dest, int32_t destCapacity, + UNormalizationMode mode, int32_t options, + UBool doNormalize, UBool *pNeededToNormalize, + UErrorCode *pErrorCode) { + const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); + if(options&UNORM_UNICODE_3_2) { + const UnicodeSet *uni32 = uniset_getUnicode32Instance(*pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return 0; + } + FilteredNormalizer2 fn2(*n2, *uni32); + return _iterate(src, forward, dest, destCapacity, + &fn2, doNormalize, pNeededToNormalize, pErrorCode); + } + return _iterate(src, forward, dest, destCapacity, + n2, doNormalize, pNeededToNormalize, pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +unorm_previous(UCharIterator *src, + char16_t *dest, int32_t destCapacity, + UNormalizationMode mode, int32_t options, + UBool doNormalize, UBool *pNeededToNormalize, + UErrorCode *pErrorCode) { + return unorm_iterate(src, false, + dest, destCapacity, + mode, options, + doNormalize, pNeededToNormalize, + pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +unorm_next(UCharIterator *src, + char16_t *dest, int32_t destCapacity, + UNormalizationMode mode, int32_t options, + UBool doNormalize, UBool *pNeededToNormalize, + UErrorCode *pErrorCode) { + return unorm_iterate(src, true, + dest, destCapacity, + mode, options, + doNormalize, pNeededToNormalize, + pErrorCode); +} + +/* Concatenation of normalized strings -------------------------------------- */ + +static int32_t +_concatenate(const char16_t *left, int32_t leftLength, + const char16_t *right, int32_t rightLength, + char16_t *dest, int32_t destCapacity, + const Normalizer2 *n2, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(destCapacity<0 || (dest==nullptr && destCapacity>0) || + left==nullptr || leftLength<-1 || right==nullptr || rightLength<-1) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* check for overlapping right and destination */ + if( dest!=nullptr && + ((right>=dest && right<(dest+destCapacity)) || + (rightLength>0 && dest>=right && dest<(right+rightLength))) + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* allow left==dest */ + UnicodeString destString; + if(left==dest) { + destString.setTo(dest, leftLength, destCapacity); + } else { + destString.setTo(dest, 0, destCapacity); + destString.append(left, leftLength); + } + return n2->append(destString, UnicodeString(rightLength<0, right, rightLength), *pErrorCode). + extract(dest, destCapacity, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +unorm_concatenate(const char16_t *left, int32_t leftLength, + const char16_t *right, int32_t rightLength, + char16_t *dest, int32_t destCapacity, + UNormalizationMode mode, int32_t options, + UErrorCode *pErrorCode) { + const Normalizer2 *n2=Normalizer2Factory::getInstance(mode, *pErrorCode); + if(options&UNORM_UNICODE_3_2) { + const UnicodeSet *uni32 = uniset_getUnicode32Instance(*pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return 0; + } + FilteredNormalizer2 fn2(*n2, *uni32); + return _concatenate(left, leftLength, right, rightLength, + dest, destCapacity, &fn2, pErrorCode); + } + return _concatenate(left, leftLength, right, rightLength, + dest, destCapacity, n2, pErrorCode); +} + +#endif /* #if !UCONFIG_NO_NORMALIZATION */ diff --git a/deps/icu-small/source/common/unormcmp.cpp b/deps/icu-small/source/common/unormcmp.cpp index e22419097255c5..7679e866d9bf8c 100644 --- a/deps/icu-small/source/common/unormcmp.cpp +++ b/deps/icu-small/source/common/unormcmp.cpp @@ -1,640 +1,640 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2001-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: unormcmp.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004sep13 -* created by: Markus W. Scherer -* -* unorm_compare() function moved here from unorm.cpp for better modularization. -* Depends on both normalization and case folding. -* Allows unorm.cpp to not depend on any character properties code. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_NORMALIZATION - -#include "unicode/unorm.h" -#include "unicode/ustring.h" -#include "cmemory.h" -#include "normalizer2impl.h" -#include "ucase.h" -#include "uprops.h" -#include "ustr_imp.h" - -U_NAMESPACE_USE - -/* compare canonically equivalent ------------------------------------------- */ - -/* - * Compare two strings for canonical equivalence. - * Further options include case-insensitive comparison and - * code point order (as opposed to code unit order). - * - * In this function, canonical equivalence is optional as well. - * If canonical equivalence is tested, then both strings must fulfill - * the FCD check. - * - * Semantically, this is equivalent to - * strcmp[CodePointOrder](NFD(foldCase(s1)), NFD(foldCase(s2))) - * where code point order, NFD and foldCase are all optional. - * - * String comparisons almost always yield results before processing both strings - * completely. - * They are generally more efficient working incrementally instead of - * performing the sub-processing (strlen, normalization, case-folding) - * on the entire strings first. - * - * It is also unnecessary to not normalize identical characters. - * - * This function works in principle as follows: - * - * loop { - * get one code unit c1 from s1 (-1 if end of source) - * get one code unit c2 from s2 (-1 if end of source) - * - * if(either string finished) { - * return result; - * } - * if(c1==c2) { - * continue; - * } - * - * // c1!=c2 - * try to decompose/case-fold c1/c2, and continue if one does; - * - * // still c1!=c2 and neither decomposes/case-folds, return result - * return c1-c2; - * } - * - * When a character decomposes, then the pointer for that source changes to - * the decomposition, pushing the previous pointer onto a stack. - * When the end of the decomposition is reached, then the code unit reader - * pops the previous source from the stack. - * (Same for case-folding.) - * - * This is complicated further by operating on variable-width UTF-16. - * The top part of the loop works on code units, while lookups for decomposition - * and case-folding need code points. - * Code points are assembled after the equality/end-of-source part. - * The source pointer is only advanced beyond all code units when the code point - * actually decomposes/case-folds. - * - * If we were on a trail surrogate unit when assembling a code point, - * and the code point decomposes/case-folds, then the decomposition/folding - * result must be compared with the part of the other string that corresponds to - * this string's lead surrogate. - * Since we only assemble a code point when hitting a trail unit when the - * preceding lead units were identical, we back up the other string by one unit - * in such a case. - * - * The optional code point order comparison at the end works with - * the same fix-up as the other code point order comparison functions. - * See ustring.c and the comment near the end of this function. - * - * Assumption: A decomposition or case-folding result string never contains - * a single surrogate. This is a safe assumption in the Unicode Standard. - * Therefore, we do not need to check for surrogate pairs across - * decomposition/case-folding boundaries. - * - * Further assumptions (see verifications tstnorm.cpp): - * The API function checks for FCD first, while the core function - * first case-folds and then decomposes. This requires that case-folding does not - * un-FCD any strings. - * - * The API function may also NFD the input and turn off decomposition. - * This requires that case-folding does not un-NFD strings either. - * - * TODO If any of the above two assumptions is violated, - * then this entire code must be re-thought. - * If this happens, then a simple solution is to case-fold both strings up front - * and to turn off UNORM_INPUT_IS_FCD. - * We already do this when not both strings are in FCD because makeFCD - * would be a partial NFD before the case folding, which does not work. - * Note that all of this is only a problem when case-folding _and_ - * canonical equivalence come together. - * (Comments in unorm_compare() are more up to date than this TODO.) - */ - -/* stack element for previous-level source/decomposition pointers */ -struct CmpEquivLevel { - const UChar *start, *s, *limit; -}; -typedef struct CmpEquivLevel CmpEquivLevel; - -/** - * Internal option for unorm_cmpEquivFold() for decomposing. - * If not set, just do strcasecmp(). - */ -#define _COMPARE_EQUIV 0x80000 - -/* internal function */ -static int32_t -unorm_cmpEquivFold(const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - uint32_t options, - UErrorCode *pErrorCode) { - const Normalizer2Impl *nfcImpl; - - /* current-level start/limit - s1/s2 as current */ - const UChar *start1, *start2, *limit1, *limit2; - - /* decomposition and case folding variables */ - const UChar *p; - int32_t length; - - /* stacks of previous-level start/current/limit */ - CmpEquivLevel stack1[2], stack2[2]; - - /* buffers for algorithmic decompositions */ - UChar decomp1[4], decomp2[4]; - - /* case folding buffers, only use current-level start/limit */ - UChar fold1[UCASE_MAX_STRING_LENGTH+1], fold2[UCASE_MAX_STRING_LENGTH+1]; - - /* track which is the current level per string */ - int32_t level1, level2; - - /* current code units, and code points for lookups */ - UChar32 c1, c2, cp1, cp2; - - /* no argument error checking because this itself is not an API */ - - /* - * assume that at least one of the options _COMPARE_EQUIV and U_COMPARE_IGNORE_CASE is set - * otherwise this function must behave exactly as uprv_strCompare() - * not checking for that here makes testing this function easier - */ - - /* normalization/properties data loaded? */ - if((options&_COMPARE_EQUIV)!=0) { - nfcImpl=Normalizer2Factory::getNFCImpl(*pErrorCode); - } else { - nfcImpl=NULL; - } - if(U_FAILURE(*pErrorCode)) { - return 0; - } - - /* initialize */ - start1=s1; - if(length1==-1) { - limit1=NULL; - } else { - limit1=s1+length1; - } - - start2=s2; - if(length2==-1) { - limit2=NULL; - } else { - limit2=s2+length2; - } - - level1=level2=0; - c1=c2=-1; - - /* comparison loop */ - for(;;) { - /* - * here a code unit value of -1 means "get another code unit" - * below it will mean "this source is finished" - */ - - if(c1<0) { - /* get next code unit from string 1, post-increment */ - for(;;) { - if(s1==limit1 || ((c1=*s1)==0 && (limit1==NULL || (options&_STRNCMP_STYLE)))) { - if(level1==0) { - c1=-1; - break; - } - } else { - ++s1; - break; - } - - /* reached end of level buffer, pop one level */ - do { - --level1; - start1=stack1[level1].start; /*Not uninitialized*/ - } while(start1==NULL); - s1=stack1[level1].s; /*Not uninitialized*/ - limit1=stack1[level1].limit; /*Not uninitialized*/ - } - } - - if(c2<0) { - /* get next code unit from string 2, post-increment */ - for(;;) { - if(s2==limit2 || ((c2=*s2)==0 && (limit2==NULL || (options&_STRNCMP_STYLE)))) { - if(level2==0) { - c2=-1; - break; - } - } else { - ++s2; - break; - } - - /* reached end of level buffer, pop one level */ - do { - --level2; - start2=stack2[level2].start; /*Not uninitialized*/ - } while(start2==NULL); - s2=stack2[level2].s; /*Not uninitialized*/ - limit2=stack2[level2].limit; /*Not uninitialized*/ - } - } - - /* - * compare c1 and c2 - * either variable c1, c2 is -1 only if the corresponding string is finished - */ - if(c1==c2) { - if(c1<0) { - return 0; /* c1==c2==-1 indicating end of strings */ - } - c1=c2=-1; /* make us fetch new code units */ - continue; - } else if(c1<0) { - return -1; /* string 1 ends before string 2 */ - } else if(c2<0) { - return 1; /* string 2 ends before string 1 */ - } - /* c1!=c2 && c1>=0 && c2>=0 */ - - /* get complete code points for c1, c2 for lookups if either is a surrogate */ - cp1=c1; - if(U_IS_SURROGATE(c1)) { - UChar c; - - if(U_IS_SURROGATE_LEAD(c1)) { - if(s1!=limit1 && U16_IS_TRAIL(c=*s1)) { - /* advance ++s1; only below if cp1 decomposes/case-folds */ - cp1=U16_GET_SUPPLEMENTARY(c1, c); - } - } else /* isTrail(c1) */ { - if(start1<=(s1-2) && U16_IS_LEAD(c=*(s1-2))) { - cp1=U16_GET_SUPPLEMENTARY(c, c1); - } - } - } - - cp2=c2; - if(U_IS_SURROGATE(c2)) { - UChar c; - - if(U_IS_SURROGATE_LEAD(c2)) { - if(s2!=limit2 && U16_IS_TRAIL(c=*s2)) { - /* advance ++s2; only below if cp2 decomposes/case-folds */ - cp2=U16_GET_SUPPLEMENTARY(c2, c); - } - } else /* isTrail(c2) */ { - if(start2<=(s2-2) && U16_IS_LEAD(c=*(s2-2))) { - cp2=U16_GET_SUPPLEMENTARY(c, c2); - } - } - } - - /* - * go down one level for each string - * continue with the main loop as soon as there is a real change - */ - - if( level1==0 && (options&U_COMPARE_IGNORE_CASE) && - (length=ucase_toFullFolding((UChar32)cp1, &p, options))>=0 - ) { - /* cp1 case-folds to the code point "length" or to p[length] */ - if(U_IS_SURROGATE(c1)) { - if(U_IS_SURROGATE_LEAD(c1)) { - /* advance beyond source surrogate pair if it case-folds */ - ++s1; - } else /* isTrail(c1) */ { - /* - * we got a supplementary code point when hitting its trail surrogate, - * therefore the lead surrogate must have been the same as in the other string; - * compare this decomposition with the lead surrogate in the other string - * remember that this simulates bulk text replacement: - * the decomposition would replace the entire code point - */ - --s2; - c2=*(s2-1); - } - } - - /* push current level pointers */ - stack1[0].start=start1; - stack1[0].s=s1; - stack1[0].limit=limit1; - ++level1; - - /* copy the folding result to fold1[] */ - if(length<=UCASE_MAX_STRING_LENGTH) { - u_memcpy(fold1, p, length); - } else { - int32_t i=0; - U16_APPEND_UNSAFE(fold1, i, length); - length=i; - } - - /* set next level pointers to case folding */ - start1=s1=fold1; - limit1=fold1+length; - - /* get ready to read from decomposition, continue with loop */ - c1=-1; - continue; - } - - if( level2==0 && (options&U_COMPARE_IGNORE_CASE) && - (length=ucase_toFullFolding((UChar32)cp2, &p, options))>=0 - ) { - /* cp2 case-folds to the code point "length" or to p[length] */ - if(U_IS_SURROGATE(c2)) { - if(U_IS_SURROGATE_LEAD(c2)) { - /* advance beyond source surrogate pair if it case-folds */ - ++s2; - } else /* isTrail(c2) */ { - /* - * we got a supplementary code point when hitting its trail surrogate, - * therefore the lead surrogate must have been the same as in the other string; - * compare this decomposition with the lead surrogate in the other string - * remember that this simulates bulk text replacement: - * the decomposition would replace the entire code point - */ - --s1; - c1=*(s1-1); - } - } - - /* push current level pointers */ - stack2[0].start=start2; - stack2[0].s=s2; - stack2[0].limit=limit2; - ++level2; - - /* copy the folding result to fold2[] */ - if(length<=UCASE_MAX_STRING_LENGTH) { - u_memcpy(fold2, p, length); - } else { - int32_t i=0; - U16_APPEND_UNSAFE(fold2, i, length); - length=i; - } - - /* set next level pointers to case folding */ - start2=s2=fold2; - limit2=fold2+length; - - /* get ready to read from decomposition, continue with loop */ - c2=-1; - continue; - } - - if( level1<2 && (options&_COMPARE_EQUIV) && - 0!=(p=nfcImpl->getDecomposition((UChar32)cp1, decomp1, length)) - ) { - /* cp1 decomposes into p[length] */ - if(U_IS_SURROGATE(c1)) { - if(U_IS_SURROGATE_LEAD(c1)) { - /* advance beyond source surrogate pair if it decomposes */ - ++s1; - } else /* isTrail(c1) */ { - /* - * we got a supplementary code point when hitting its trail surrogate, - * therefore the lead surrogate must have been the same as in the other string; - * compare this decomposition with the lead surrogate in the other string - * remember that this simulates bulk text replacement: - * the decomposition would replace the entire code point - */ - --s2; - c2=*(s2-1); - } - } - - /* push current level pointers */ - stack1[level1].start=start1; - stack1[level1].s=s1; - stack1[level1].limit=limit1; - ++level1; - - /* set empty intermediate level if skipped */ - if(level1<2) { - stack1[level1++].start=NULL; - } - - /* set next level pointers to decomposition */ - start1=s1=p; - limit1=p+length; - - /* get ready to read from decomposition, continue with loop */ - c1=-1; - continue; - } - - if( level2<2 && (options&_COMPARE_EQUIV) && - 0!=(p=nfcImpl->getDecomposition((UChar32)cp2, decomp2, length)) - ) { - /* cp2 decomposes into p[length] */ - if(U_IS_SURROGATE(c2)) { - if(U_IS_SURROGATE_LEAD(c2)) { - /* advance beyond source surrogate pair if it decomposes */ - ++s2; - } else /* isTrail(c2) */ { - /* - * we got a supplementary code point when hitting its trail surrogate, - * therefore the lead surrogate must have been the same as in the other string; - * compare this decomposition with the lead surrogate in the other string - * remember that this simulates bulk text replacement: - * the decomposition would replace the entire code point - */ - --s1; - c1=*(s1-1); - } - } - - /* push current level pointers */ - stack2[level2].start=start2; - stack2[level2].s=s2; - stack2[level2].limit=limit2; - ++level2; - - /* set empty intermediate level if skipped */ - if(level2<2) { - stack2[level2++].start=NULL; - } - - /* set next level pointers to decomposition */ - start2=s2=p; - limit2=p+length; - - /* get ready to read from decomposition, continue with loop */ - c2=-1; - continue; - } - - /* - * no decomposition/case folding, max level for both sides: - * return difference result - * - * code point order comparison must not just return cp1-cp2 - * because when single surrogates are present then the surrogate pairs - * that formed cp1 and cp2 may be from different string indexes - * - * example: { d800 d800 dc01 } vs. { d800 dc00 }, compare at second code units - * c1=d800 cp1=10001 c2=dc00 cp2=10000 - * cp1-cp2>0 but c1-c2<0 and in fact in UTF-32 it is { d800 10001 } < { 10000 } - * - * therefore, use same fix-up as in ustring.c/uprv_strCompare() - * except: uprv_strCompare() fetches c=*s while this functions fetches c=*s++ - * so we have slightly different pointer/start/limit comparisons here - */ - - if(c1>=0xd800 && c2>=0xd800 && (options&U_COMPARE_CODE_POINT_ORDER)) { - /* subtract 0x2800 from BMP code points to make them smaller than supplementary ones */ - if( - (c1<=0xdbff && s1!=limit1 && U16_IS_TRAIL(*s1)) || - (U16_IS_TRAIL(c1) && start1!=(s1-1) && U16_IS_LEAD(*(s1-2))) - ) { - /* part of a surrogate pair, leave >=d800 */ - } else { - /* BMP code point - may be surrogate code point - make =d800 */ - } else { - /* BMP code point - may be surrogate code point - make spanQuickCheckYes(str, *pErrorCode); - if (U_FAILURE(*pErrorCode)) { - return false; - } - /* - * ICU 2.4 had a further optimization: - * If both strings were not in FCD, then they were both NFD'ed, - * and the _COMPARE_EQUIV option was turned off. - * It is not entirely clear that this is valid with the current - * definition of the canonical caseless match. - * Therefore, ICU 2.6 removes that optimization. - */ - if(spanQCYesnormalizeSecondAndAppend(normalized, unnormalized, *pErrorCode); - if (U_SUCCESS(*pErrorCode)) { - return true; - } - } - return false; -} - -U_CAPI int32_t U_EXPORT2 -unorm_compare(const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - uint32_t options, - UErrorCode *pErrorCode) { - /* argument checking */ - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if(s1==0 || length1<-1 || s2==0 || length2<-1) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - UnicodeString fcd1, fcd2; - int32_t normOptions=(int32_t)(options>>UNORM_COMPARE_NORM_OPTIONS_SHIFT); - options|=_COMPARE_EQUIV; - - /* - * UAX #21 Case Mappings, as fixed for Unicode version 4 - * (see Jitterbug 2021), defines a canonical caseless match as - * - * A string X is a canonical caseless match - * for a string Y if and only if - * NFD(toCasefold(NFD(X))) = NFD(toCasefold(NFD(Y))) - * - * For better performance, we check for FCD (or let the caller tell us that - * both strings are in FCD) for the inner normalization. - * BasicNormalizerTest::FindFoldFCDExceptions() makes sure that - * case-folding preserves the FCD-ness of a string. - * The outer normalization is then only performed by unorm_cmpEquivFold() - * when there is a difference. - * - * Exception: When using the Turkic case-folding option, we do perform - * full NFD first. This is because in the Turkic case precomposed characters - * with 0049 capital I or 0069 small i fold differently whether they - * are first decomposed or not, so an FCD check - a check only for - * canonical order - is not sufficient. - */ - if(!(options&UNORM_INPUT_IS_FCD) || (options&U_FOLD_CASE_EXCLUDE_SPECIAL_I)) { - const Normalizer2 *n2; - if(options&U_FOLD_CASE_EXCLUDE_SPECIAL_I) { - n2=Normalizer2::getNFDInstance(*pErrorCode); - } else { - n2=Normalizer2Factory::getFCDInstance(*pErrorCode); - } - if (U_FAILURE(*pErrorCode)) { - return 0; - } - - if(normOptions&UNORM_UNICODE_3_2) { - const UnicodeSet *uni32=uniset_getUnicode32Instance(*pErrorCode); - FilteredNormalizer2 fn2(*n2, *uni32); - if(_normalize(&fn2, s1, length1, fcd1, pErrorCode)) { - s1=fcd1.getBuffer(); - length1=fcd1.length(); - } - if(_normalize(&fn2, s2, length2, fcd2, pErrorCode)) { - s2=fcd2.getBuffer(); - length2=fcd2.length(); - } - } else { - if(_normalize(n2, s1, length1, fcd1, pErrorCode)) { - s1=fcd1.getBuffer(); - length1=fcd1.length(); - } - if(_normalize(n2, s2, length2, fcd2, pErrorCode)) { - s2=fcd2.getBuffer(); - length2=fcd2.length(); - } - } - } - - if(U_SUCCESS(*pErrorCode)) { - return unorm_cmpEquivFold(s1, length1, s2, length2, options, pErrorCode); - } else { - return 0; - } -} - -#endif /* #if !UCONFIG_NO_NORMALIZATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2001-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: unormcmp.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004sep13 +* created by: Markus W. Scherer +* +* unorm_compare() function moved here from unorm.cpp for better modularization. +* Depends on both normalization and case folding. +* Allows unorm.cpp to not depend on any character properties code. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_NORMALIZATION + +#include "unicode/unorm.h" +#include "unicode/ustring.h" +#include "cmemory.h" +#include "normalizer2impl.h" +#include "ucase.h" +#include "uprops.h" +#include "ustr_imp.h" + +U_NAMESPACE_USE + +/* compare canonically equivalent ------------------------------------------- */ + +/* + * Compare two strings for canonical equivalence. + * Further options include case-insensitive comparison and + * code point order (as opposed to code unit order). + * + * In this function, canonical equivalence is optional as well. + * If canonical equivalence is tested, then both strings must fulfill + * the FCD check. + * + * Semantically, this is equivalent to + * strcmp[CodePointOrder](NFD(foldCase(s1)), NFD(foldCase(s2))) + * where code point order, NFD and foldCase are all optional. + * + * String comparisons almost always yield results before processing both strings + * completely. + * They are generally more efficient working incrementally instead of + * performing the sub-processing (strlen, normalization, case-folding) + * on the entire strings first. + * + * It is also unnecessary to not normalize identical characters. + * + * This function works in principle as follows: + * + * loop { + * get one code unit c1 from s1 (-1 if end of source) + * get one code unit c2 from s2 (-1 if end of source) + * + * if(either string finished) { + * return result; + * } + * if(c1==c2) { + * continue; + * } + * + * // c1!=c2 + * try to decompose/case-fold c1/c2, and continue if one does; + * + * // still c1!=c2 and neither decomposes/case-folds, return result + * return c1-c2; + * } + * + * When a character decomposes, then the pointer for that source changes to + * the decomposition, pushing the previous pointer onto a stack. + * When the end of the decomposition is reached, then the code unit reader + * pops the previous source from the stack. + * (Same for case-folding.) + * + * This is complicated further by operating on variable-width UTF-16. + * The top part of the loop works on code units, while lookups for decomposition + * and case-folding need code points. + * Code points are assembled after the equality/end-of-source part. + * The source pointer is only advanced beyond all code units when the code point + * actually decomposes/case-folds. + * + * If we were on a trail surrogate unit when assembling a code point, + * and the code point decomposes/case-folds, then the decomposition/folding + * result must be compared with the part of the other string that corresponds to + * this string's lead surrogate. + * Since we only assemble a code point when hitting a trail unit when the + * preceding lead units were identical, we back up the other string by one unit + * in such a case. + * + * The optional code point order comparison at the end works with + * the same fix-up as the other code point order comparison functions. + * See ustring.c and the comment near the end of this function. + * + * Assumption: A decomposition or case-folding result string never contains + * a single surrogate. This is a safe assumption in the Unicode Standard. + * Therefore, we do not need to check for surrogate pairs across + * decomposition/case-folding boundaries. + * + * Further assumptions (see verifications tstnorm.cpp): + * The API function checks for FCD first, while the core function + * first case-folds and then decomposes. This requires that case-folding does not + * un-FCD any strings. + * + * The API function may also NFD the input and turn off decomposition. + * This requires that case-folding does not un-NFD strings either. + * + * TODO If any of the above two assumptions is violated, + * then this entire code must be re-thought. + * If this happens, then a simple solution is to case-fold both strings up front + * and to turn off UNORM_INPUT_IS_FCD. + * We already do this when not both strings are in FCD because makeFCD + * would be a partial NFD before the case folding, which does not work. + * Note that all of this is only a problem when case-folding _and_ + * canonical equivalence come together. + * (Comments in unorm_compare() are more up to date than this TODO.) + */ + +/* stack element for previous-level source/decomposition pointers */ +struct CmpEquivLevel { + const char16_t *start, *s, *limit; +}; +typedef struct CmpEquivLevel CmpEquivLevel; + +/** + * Internal option for unorm_cmpEquivFold() for decomposing. + * If not set, just do strcasecmp(). + */ +#define _COMPARE_EQUIV 0x80000 + +/* internal function */ +static int32_t +unorm_cmpEquivFold(const char16_t *s1, int32_t length1, + const char16_t *s2, int32_t length2, + uint32_t options, + UErrorCode *pErrorCode) { + const Normalizer2Impl *nfcImpl; + + /* current-level start/limit - s1/s2 as current */ + const char16_t *start1, *start2, *limit1, *limit2; + + /* decomposition and case folding variables */ + const char16_t *p; + int32_t length; + + /* stacks of previous-level start/current/limit */ + CmpEquivLevel stack1[2], stack2[2]; + + /* buffers for algorithmic decompositions */ + char16_t decomp1[4], decomp2[4]; + + /* case folding buffers, only use current-level start/limit */ + char16_t fold1[UCASE_MAX_STRING_LENGTH+1], fold2[UCASE_MAX_STRING_LENGTH+1]; + + /* track which is the current level per string */ + int32_t level1, level2; + + /* current code units, and code points for lookups */ + UChar32 c1, c2, cp1, cp2; + + /* no argument error checking because this itself is not an API */ + + /* + * assume that at least one of the options _COMPARE_EQUIV and U_COMPARE_IGNORE_CASE is set + * otherwise this function must behave exactly as uprv_strCompare() + * not checking for that here makes testing this function easier + */ + + /* normalization/properties data loaded? */ + if((options&_COMPARE_EQUIV)!=0) { + nfcImpl=Normalizer2Factory::getNFCImpl(*pErrorCode); + } else { + nfcImpl=nullptr; + } + if(U_FAILURE(*pErrorCode)) { + return 0; + } + + /* initialize */ + start1=s1; + if(length1==-1) { + limit1=nullptr; + } else { + limit1=s1+length1; + } + + start2=s2; + if(length2==-1) { + limit2=nullptr; + } else { + limit2=s2+length2; + } + + level1=level2=0; + c1=c2=-1; + + /* comparison loop */ + for(;;) { + /* + * here a code unit value of -1 means "get another code unit" + * below it will mean "this source is finished" + */ + + if(c1<0) { + /* get next code unit from string 1, post-increment */ + for(;;) { + if(s1==limit1 || ((c1=*s1)==0 && (limit1==nullptr || (options&_STRNCMP_STYLE)))) { + if(level1==0) { + c1=-1; + break; + } + } else { + ++s1; + break; + } + + /* reached end of level buffer, pop one level */ + do { + --level1; + start1=stack1[level1].start; /*Not uninitialized*/ + } while(start1==nullptr); + s1=stack1[level1].s; /*Not uninitialized*/ + limit1=stack1[level1].limit; /*Not uninitialized*/ + } + } + + if(c2<0) { + /* get next code unit from string 2, post-increment */ + for(;;) { + if(s2==limit2 || ((c2=*s2)==0 && (limit2==nullptr || (options&_STRNCMP_STYLE)))) { + if(level2==0) { + c2=-1; + break; + } + } else { + ++s2; + break; + } + + /* reached end of level buffer, pop one level */ + do { + --level2; + start2=stack2[level2].start; /*Not uninitialized*/ + } while(start2==nullptr); + s2=stack2[level2].s; /*Not uninitialized*/ + limit2=stack2[level2].limit; /*Not uninitialized*/ + } + } + + /* + * compare c1 and c2 + * either variable c1, c2 is -1 only if the corresponding string is finished + */ + if(c1==c2) { + if(c1<0) { + return 0; /* c1==c2==-1 indicating end of strings */ + } + c1=c2=-1; /* make us fetch new code units */ + continue; + } else if(c1<0) { + return -1; /* string 1 ends before string 2 */ + } else if(c2<0) { + return 1; /* string 2 ends before string 1 */ + } + /* c1!=c2 && c1>=0 && c2>=0 */ + + /* get complete code points for c1, c2 for lookups if either is a surrogate */ + cp1=c1; + if(U_IS_SURROGATE(c1)) { + char16_t c; + + if(U_IS_SURROGATE_LEAD(c1)) { + if(s1!=limit1 && U16_IS_TRAIL(c=*s1)) { + /* advance ++s1; only below if cp1 decomposes/case-folds */ + cp1=U16_GET_SUPPLEMENTARY(c1, c); + } + } else /* isTrail(c1) */ { + if(start1<=(s1-2) && U16_IS_LEAD(c=*(s1-2))) { + cp1=U16_GET_SUPPLEMENTARY(c, c1); + } + } + } + + cp2=c2; + if(U_IS_SURROGATE(c2)) { + char16_t c; + + if(U_IS_SURROGATE_LEAD(c2)) { + if(s2!=limit2 && U16_IS_TRAIL(c=*s2)) { + /* advance ++s2; only below if cp2 decomposes/case-folds */ + cp2=U16_GET_SUPPLEMENTARY(c2, c); + } + } else /* isTrail(c2) */ { + if(start2<=(s2-2) && U16_IS_LEAD(c=*(s2-2))) { + cp2=U16_GET_SUPPLEMENTARY(c, c2); + } + } + } + + /* + * go down one level for each string + * continue with the main loop as soon as there is a real change + */ + + if( level1==0 && (options&U_COMPARE_IGNORE_CASE) && + (length=ucase_toFullFolding((UChar32)cp1, &p, options))>=0 + ) { + /* cp1 case-folds to the code point "length" or to p[length] */ + if(U_IS_SURROGATE(c1)) { + if(U_IS_SURROGATE_LEAD(c1)) { + /* advance beyond source surrogate pair if it case-folds */ + ++s1; + } else /* isTrail(c1) */ { + /* + * we got a supplementary code point when hitting its trail surrogate, + * therefore the lead surrogate must have been the same as in the other string; + * compare this decomposition with the lead surrogate in the other string + * remember that this simulates bulk text replacement: + * the decomposition would replace the entire code point + */ + --s2; + c2=*(s2-1); + } + } + + /* push current level pointers */ + stack1[0].start=start1; + stack1[0].s=s1; + stack1[0].limit=limit1; + ++level1; + + /* copy the folding result to fold1[] */ + if(length<=UCASE_MAX_STRING_LENGTH) { + u_memcpy(fold1, p, length); + } else { + int32_t i=0; + U16_APPEND_UNSAFE(fold1, i, length); + length=i; + } + + /* set next level pointers to case folding */ + start1=s1=fold1; + limit1=fold1+length; + + /* get ready to read from decomposition, continue with loop */ + c1=-1; + continue; + } + + if( level2==0 && (options&U_COMPARE_IGNORE_CASE) && + (length=ucase_toFullFolding((UChar32)cp2, &p, options))>=0 + ) { + /* cp2 case-folds to the code point "length" or to p[length] */ + if(U_IS_SURROGATE(c2)) { + if(U_IS_SURROGATE_LEAD(c2)) { + /* advance beyond source surrogate pair if it case-folds */ + ++s2; + } else /* isTrail(c2) */ { + /* + * we got a supplementary code point when hitting its trail surrogate, + * therefore the lead surrogate must have been the same as in the other string; + * compare this decomposition with the lead surrogate in the other string + * remember that this simulates bulk text replacement: + * the decomposition would replace the entire code point + */ + --s1; + c1=*(s1-1); + } + } + + /* push current level pointers */ + stack2[0].start=start2; + stack2[0].s=s2; + stack2[0].limit=limit2; + ++level2; + + /* copy the folding result to fold2[] */ + if(length<=UCASE_MAX_STRING_LENGTH) { + u_memcpy(fold2, p, length); + } else { + int32_t i=0; + U16_APPEND_UNSAFE(fold2, i, length); + length=i; + } + + /* set next level pointers to case folding */ + start2=s2=fold2; + limit2=fold2+length; + + /* get ready to read from decomposition, continue with loop */ + c2=-1; + continue; + } + + if( level1<2 && (options&_COMPARE_EQUIV) && + 0!=(p=nfcImpl->getDecomposition((UChar32)cp1, decomp1, length)) + ) { + /* cp1 decomposes into p[length] */ + if(U_IS_SURROGATE(c1)) { + if(U_IS_SURROGATE_LEAD(c1)) { + /* advance beyond source surrogate pair if it decomposes */ + ++s1; + } else /* isTrail(c1) */ { + /* + * we got a supplementary code point when hitting its trail surrogate, + * therefore the lead surrogate must have been the same as in the other string; + * compare this decomposition with the lead surrogate in the other string + * remember that this simulates bulk text replacement: + * the decomposition would replace the entire code point + */ + --s2; + c2=*(s2-1); + } + } + + /* push current level pointers */ + stack1[level1].start=start1; + stack1[level1].s=s1; + stack1[level1].limit=limit1; + ++level1; + + /* set empty intermediate level if skipped */ + if(level1<2) { + stack1[level1++].start=nullptr; + } + + /* set next level pointers to decomposition */ + start1=s1=p; + limit1=p+length; + + /* get ready to read from decomposition, continue with loop */ + c1=-1; + continue; + } + + if( level2<2 && (options&_COMPARE_EQUIV) && + 0!=(p=nfcImpl->getDecomposition((UChar32)cp2, decomp2, length)) + ) { + /* cp2 decomposes into p[length] */ + if(U_IS_SURROGATE(c2)) { + if(U_IS_SURROGATE_LEAD(c2)) { + /* advance beyond source surrogate pair if it decomposes */ + ++s2; + } else /* isTrail(c2) */ { + /* + * we got a supplementary code point when hitting its trail surrogate, + * therefore the lead surrogate must have been the same as in the other string; + * compare this decomposition with the lead surrogate in the other string + * remember that this simulates bulk text replacement: + * the decomposition would replace the entire code point + */ + --s1; + c1=*(s1-1); + } + } + + /* push current level pointers */ + stack2[level2].start=start2; + stack2[level2].s=s2; + stack2[level2].limit=limit2; + ++level2; + + /* set empty intermediate level if skipped */ + if(level2<2) { + stack2[level2++].start=nullptr; + } + + /* set next level pointers to decomposition */ + start2=s2=p; + limit2=p+length; + + /* get ready to read from decomposition, continue with loop */ + c2=-1; + continue; + } + + /* + * no decomposition/case folding, max level for both sides: + * return difference result + * + * code point order comparison must not just return cp1-cp2 + * because when single surrogates are present then the surrogate pairs + * that formed cp1 and cp2 may be from different string indexes + * + * example: { d800 d800 dc01 } vs. { d800 dc00 }, compare at second code units + * c1=d800 cp1=10001 c2=dc00 cp2=10000 + * cp1-cp2>0 but c1-c2<0 and in fact in UTF-32 it is { d800 10001 } < { 10000 } + * + * therefore, use same fix-up as in ustring.c/uprv_strCompare() + * except: uprv_strCompare() fetches c=*s while this functions fetches c=*s++ + * so we have slightly different pointer/start/limit comparisons here + */ + + if(c1>=0xd800 && c2>=0xd800 && (options&U_COMPARE_CODE_POINT_ORDER)) { + /* subtract 0x2800 from BMP code points to make them smaller than supplementary ones */ + if( + (c1<=0xdbff && s1!=limit1 && U16_IS_TRAIL(*s1)) || + (U16_IS_TRAIL(c1) && start1!=(s1-1) && U16_IS_LEAD(*(s1-2))) + ) { + /* part of a surrogate pair, leave >=d800 */ + } else { + /* BMP code point - may be surrogate code point - make =d800 */ + } else { + /* BMP code point - may be surrogate code point - make spanQuickCheckYes(str, *pErrorCode); + if (U_FAILURE(*pErrorCode)) { + return false; + } + /* + * ICU 2.4 had a further optimization: + * If both strings were not in FCD, then they were both NFD'ed, + * and the _COMPARE_EQUIV option was turned off. + * It is not entirely clear that this is valid with the current + * definition of the canonical caseless match. + * Therefore, ICU 2.6 removes that optimization. + */ + if(spanQCYesnormalizeSecondAndAppend(normalized, unnormalized, *pErrorCode); + if (U_SUCCESS(*pErrorCode)) { + return true; + } + } + return false; +} + +U_CAPI int32_t U_EXPORT2 +unorm_compare(const char16_t *s1, int32_t length1, + const char16_t *s2, int32_t length2, + uint32_t options, + UErrorCode *pErrorCode) { + /* argument checking */ + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(s1==0 || length1<-1 || s2==0 || length2<-1) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + UnicodeString fcd1, fcd2; + int32_t normOptions=(int32_t)(options>>UNORM_COMPARE_NORM_OPTIONS_SHIFT); + options|=_COMPARE_EQUIV; + + /* + * UAX #21 Case Mappings, as fixed for Unicode version 4 + * (see Jitterbug 2021), defines a canonical caseless match as + * + * A string X is a canonical caseless match + * for a string Y if and only if + * NFD(toCasefold(NFD(X))) = NFD(toCasefold(NFD(Y))) + * + * For better performance, we check for FCD (or let the caller tell us that + * both strings are in FCD) for the inner normalization. + * BasicNormalizerTest::FindFoldFCDExceptions() makes sure that + * case-folding preserves the FCD-ness of a string. + * The outer normalization is then only performed by unorm_cmpEquivFold() + * when there is a difference. + * + * Exception: When using the Turkic case-folding option, we do perform + * full NFD first. This is because in the Turkic case precomposed characters + * with 0049 capital I or 0069 small i fold differently whether they + * are first decomposed or not, so an FCD check - a check only for + * canonical order - is not sufficient. + */ + if(!(options&UNORM_INPUT_IS_FCD) || (options&U_FOLD_CASE_EXCLUDE_SPECIAL_I)) { + const Normalizer2 *n2; + if(options&U_FOLD_CASE_EXCLUDE_SPECIAL_I) { + n2=Normalizer2::getNFDInstance(*pErrorCode); + } else { + n2=Normalizer2Factory::getFCDInstance(*pErrorCode); + } + if (U_FAILURE(*pErrorCode)) { + return 0; + } + + if(normOptions&UNORM_UNICODE_3_2) { + const UnicodeSet *uni32=uniset_getUnicode32Instance(*pErrorCode); + FilteredNormalizer2 fn2(*n2, *uni32); + if(_normalize(&fn2, s1, length1, fcd1, pErrorCode)) { + s1=fcd1.getBuffer(); + length1=fcd1.length(); + } + if(_normalize(&fn2, s2, length2, fcd2, pErrorCode)) { + s2=fcd2.getBuffer(); + length2=fcd2.length(); + } + } else { + if(_normalize(n2, s1, length1, fcd1, pErrorCode)) { + s1=fcd1.getBuffer(); + length1=fcd1.length(); + } + if(_normalize(n2, s2, length2, fcd2, pErrorCode)) { + s2=fcd2.getBuffer(); + length2=fcd2.length(); + } + } + } + + if(U_SUCCESS(*pErrorCode)) { + return unorm_cmpEquivFold(s1, length1, s2, length2, options, pErrorCode); + } else { + return 0; + } +} + +#endif /* #if !UCONFIG_NO_NORMALIZATION */ diff --git a/deps/icu-small/source/common/unormimp.h b/deps/icu-small/source/common/unormimp.h index d2604adb4a91fa..38cee5d1c54595 100644 --- a/deps/icu-small/source/common/unormimp.h +++ b/deps/icu-small/source/common/unormimp.h @@ -1,488 +1,488 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2001-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: unormimp.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2001may25 -* created by: Markus W. Scherer -*/ - -#ifndef __UNORMIMP_H__ -#define __UNORMIMP_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_NORMALIZATION - -#include "udataswp.h" - -/* - * The 2001-2010 implementation of the normalization code loads its data from - * unorm.icu, which is generated with the gennorm tool. - * The format of that file is described at the end of this file. - */ - -/* norm32 value constants */ -enum { - /* quick check flags 0..3 set mean "no" for their forms */ - _NORM_QC_NFC=0x11, /* no|maybe */ - _NORM_QC_NFKC=0x22, /* no|maybe */ - _NORM_QC_NFD=4, /* no */ - _NORM_QC_NFKD=8, /* no */ - - _NORM_QC_ANY_NO=0xf, - - /* quick check flags 4..5 mean "maybe" for their forms; test flags>=_NORM_QC_MAYBE */ - _NORM_QC_MAYBE=0x10, - _NORM_QC_ANY_MAYBE=0x30, - - _NORM_QC_MASK=0x3f, - - _NORM_COMBINES_FWD=0x40, - _NORM_COMBINES_BACK=0x80, - _NORM_COMBINES_ANY=0xc0, - - _NORM_CC_SHIFT=8, /* UnicodeData.txt combining class in bits 15..8 */ - _NORM_CC_MASK=0xff00, - - _NORM_EXTRA_SHIFT=16, /* 16 bits for the index to UChars and other extra data */ - _NORM_EXTRA_INDEX_TOP=0xfc00, /* start of surrogate specials after shift */ - - _NORM_EXTRA_SURROGATE_MASK=0x3ff, - _NORM_EXTRA_SURROGATE_TOP=0x3f0, /* hangul etc. */ - - _NORM_EXTRA_HANGUL=_NORM_EXTRA_SURROGATE_TOP, - _NORM_EXTRA_JAMO_L, - _NORM_EXTRA_JAMO_V, - _NORM_EXTRA_JAMO_T -}; - -/* norm32 value constants using >16 bits */ -#define _NORM_MIN_SPECIAL 0xfc000000 -#define _NORM_SURROGATES_TOP 0xfff00000 -#define _NORM_MIN_HANGUL 0xfff00000 -#define _NORM_MIN_JAMO_V 0xfff20000 -#define _NORM_JAMO_V_TOP 0xfff30000 - -/* value constants for auxTrie */ -enum { - _NORM_AUX_COMP_EX_SHIFT=10, - _NORM_AUX_UNSAFE_SHIFT=11, - _NORM_AUX_NFC_SKIPPABLE_F_SHIFT=12 -}; - -#define _NORM_AUX_MAX_FNC ((int32_t)1<<_NORM_AUX_COMP_EX_SHIFT) - -#define _NORM_AUX_FNC_MASK (uint32_t)(_NORM_AUX_MAX_FNC-1) -#define _NORM_AUX_COMP_EX_MASK ((uint32_t)1<<_NORM_AUX_COMP_EX_SHIFT) -#define _NORM_AUX_UNSAFE_MASK ((uint32_t)1<<_NORM_AUX_UNSAFE_SHIFT) -#define _NORM_AUX_NFC_SKIP_F_MASK ((uint32_t)1<<_NORM_AUX_NFC_SKIPPABLE_F_SHIFT) - -/* canonStartSets[0..31] contains indexes for what is in the array */ -enum { - _NORM_SET_INDEX_CANON_SETS_LENGTH, /* number of uint16_t in canonical starter sets */ - _NORM_SET_INDEX_CANON_BMP_TABLE_LENGTH, /* number of uint16_t in the BMP search table (contains pairs) */ - _NORM_SET_INDEX_CANON_SUPP_TABLE_LENGTH,/* number of uint16_t in the supplementary search table (contains triplets) */ - - /* from formatVersion 2.3: */ - _NORM_SET_INDEX_NX_CJK_COMPAT_OFFSET, /* uint16_t offset from canonStartSets[0] to the - exclusion set for CJK compatibility characters */ - _NORM_SET_INDEX_NX_UNICODE32_OFFSET, /* uint16_t offset from canonStartSets[0] to the - exclusion set for Unicode 3.2 characters */ - _NORM_SET_INDEX_NX_RESERVED_OFFSET, /* uint16_t offset from canonStartSets[0] to the - end of the previous exclusion set */ - - _NORM_SET_INDEX_TOP=32 /* changing this requires a new formatVersion */ -}; - -/* more constants for canonical starter sets */ - -/* 14 bit indexes to canonical USerializedSets */ -#define _NORM_MAX_CANON_SETS 0x4000 - -/* single-code point BMP sets are encoded directly in the search table except if result=0x4000..0x7fff */ -#define _NORM_CANON_SET_BMP_MASK 0xc000 -#define _NORM_CANON_SET_BMP_IS_INDEX 0x4000 - -/* indexes[] value names */ -enum { - _NORM_INDEX_TRIE_SIZE, /* number of bytes in normalization trie */ - _NORM_INDEX_UCHAR_COUNT, /* number of UChars in extra data */ - - _NORM_INDEX_COMBINE_DATA_COUNT, /* number of uint16_t words for combining data */ - _NORM_INDEX_COMBINE_FWD_COUNT, /* number of code points that combine forward */ - _NORM_INDEX_COMBINE_BOTH_COUNT, /* number of code points that combine forward and backward */ - _NORM_INDEX_COMBINE_BACK_COUNT, /* number of code points that combine backward */ - - _NORM_INDEX_MIN_NFC_NO_MAYBE, /* first code point with quick check NFC NO/MAYBE */ - _NORM_INDEX_MIN_NFKC_NO_MAYBE, /* first code point with quick check NFKC NO/MAYBE */ - _NORM_INDEX_MIN_NFD_NO_MAYBE, /* first code point with quick check NFD NO/MAYBE */ - _NORM_INDEX_MIN_NFKD_NO_MAYBE, /* first code point with quick check NFKD NO/MAYBE */ - - _NORM_INDEX_FCD_TRIE_SIZE, /* number of bytes in FCD trie */ - - _NORM_INDEX_AUX_TRIE_SIZE, /* number of bytes in the auxiliary trie */ - _NORM_INDEX_CANON_SET_COUNT, /* number of uint16_t in the array of serialized USet */ - - _NORM_INDEX_TOP=32 /* changing this requires a new formatVersion */ -}; - -enum { - /* FCD check: everything below this code point is known to have a 0 lead combining class */ - _NORM_MIN_WITH_LEAD_CC=0x300 -}; - -enum { - /** - * Bit 7 of the length byte for a decomposition string in extra data is - * a flag indicating whether the decomposition string is - * preceded by a 16-bit word with the leading and trailing cc - * of the decomposition (like for A-umlaut); - * if not, then both cc's are zero (like for compatibility ideographs). - */ - _NORM_DECOMP_FLAG_LENGTH_HAS_CC=0x80, - /** - * Bits 6..0 of the length byte contain the actual length. - */ - _NORM_DECOMP_LENGTH_MASK=0x7f -}; - -/** Constants for options flags for normalization. */ -enum { - /** Options bit 0, do not decompose Hangul syllables. */ - UNORM_NX_HANGUL=1, - /** Options bit 1, do not decompose CJK compatibility characters. */ - UNORM_NX_CJK_COMPAT=2 -}; - -/** - * Description of the format of unorm.icu version 2.3. - * - * Main change from version 1 to version 2: - * Use of new, common UTrie instead of normalization-specific tries. - * Change to version 2.1: add third/auxiliary trie with associated data. - * Change to version 2.2: add skippable (f) flag data (_NORM_AUX_NFC_SKIP_F_MASK). - * Change to version 2.3: add serialized sets for normalization exclusions - * stored inside canonStartSets[] - * - * For more details of how to use the data structures see the code - * in unorm.cpp (runtime normalization code) and - * in gennorm.c and gennorm/store.c (build-time data generation). - * - * For the serialized format of UTrie see utrie.c/UTrieHeader. - * - * - Overall partition - * - * unorm.dat customarily begins with a UDataInfo structure, see udata.h and .c. - * After that there are the following structures: - * - * int32_t indexes[_NORM_INDEX_TOP]; -- _NORM_INDEX_TOP=32, see enum in this file - * - * UTrie normTrie; -- size in bytes=indexes[_NORM_INDEX_TRIE_SIZE] - * - * uint16_t extraData[extraDataTop]; -- extraDataTop=indexes[_NORM_INDEX_UCHAR_COUNT] - * extraData[0] contains the number of units for - * FC_NFKC_Closure (formatVersion>=2.1) - * - * uint16_t combiningTable[combiningTableTop]; -- combiningTableTop=indexes[_NORM_INDEX_COMBINE_DATA_COUNT] - * combiningTableTop may include one 16-bit padding unit - * to make sure that fcdTrie is 32-bit-aligned - * - * UTrie fcdTrie; -- size in bytes=indexes[_NORM_INDEX_FCD_TRIE_SIZE] - * - * UTrie auxTrie; -- size in bytes=indexes[_NORM_INDEX_AUX_TRIE_SIZE] - * - * uint16_t canonStartSets[canonStartSetsTop] -- canonStartSetsTop=indexes[_NORM_INDEX_CANON_SET_COUNT] - * serialized USets and binary search tables, see below - * - * - * The indexes array contains lengths and sizes of the following arrays and structures - * as well as the following values: - * indexes[_NORM_INDEX_COMBINE_FWD_COUNT]=combineFwdTop - * -- one more than the highest combining index computed for forward-only-combining characters - * indexes[_NORM_INDEX_COMBINE_BOTH_COUNT]=combineBothTop-combineFwdTop - * -- number of combining indexes computed for both-ways-combining characters - * indexes[_NORM_INDEX_COMBINE_BACK_COUNT]=combineBackTop-combineBothTop - * -- number of combining indexes computed for backward-only-combining characters - * - * indexes[_NORM_INDEX_MIN_NF*_NO_MAYBE] (where *={ C, D, KC, KD }) - * -- first code point with a quick check NF* value of NO/MAYBE - * - * - * - Tries - * - * The main structures are two UTrie tables ("compact arrays"), - * each with one index array and one data array. - * See utrie.h and utrie.c. - * - * - * - Tries in unorm.dat - * - * The first trie (normTrie above) - * provides data for the NF* quick checks and normalization. - * The second trie (fcdTrie above) provides data just for FCD checks. - * - * - * - norm32 data words from the first trie - * - * The norm32Table contains one 32-bit word "norm32" per code point. - * It contains the following bit fields: - * 31..16 extra data index, _NORM_EXTRA_SHIFT is used to shift this field down - * if this index is <_NORM_EXTRA_INDEX_TOP then it is an index into - * extraData[] where variable-length normalization data for this - * code point is found - * if this index is <_NORM_EXTRA_INDEX_TOP+_NORM_EXTRA_SURROGATE_TOP - * then this is a norm32 for a leading surrogate, and the index - * value is used together with the following trailing surrogate - * code unit in the second trie access - * if this index is >=_NORM_EXTRA_INDEX_TOP+_NORM_EXTRA_SURROGATE_TOP - * then this is a norm32 for a "special" character, - * i.e., the character is a Hangul syllable or a Jamo - * see _NORM_EXTRA_HANGUL etc. - * generally, instead of extracting this index from the norm32 and - * comparing it with the above constants, - * the normalization code compares the entire norm32 value - * with _NORM_MIN_SPECIAL, _NORM_SURROGATES_TOP, _NORM_MIN_HANGUL etc. - * - * 15..8 combining class (cc) according to UnicodeData.txt - * - * 7..6 _NORM_COMBINES_ANY flags, used in composition to see if a character - * combines with any following or preceding character(s) - * at all - * 7 _NORM_COMBINES_BACK - * 6 _NORM_COMBINES_FWD - * - * 5..0 quick check flags, set for "no" or "maybe", with separate flags for - * each normalization form - * the higher bits are "maybe" flags; for NF*D there are no such flags - * the lower bits are "no" flags for all forms, in the same order - * as the "maybe" flags, - * which is (MSB to LSB): NFKD NFD NFKC NFC - * 5..4 _NORM_QC_ANY_MAYBE - * 3..0 _NORM_QC_ANY_NO - * see further related constants - * - * - * - Extra data per code point - * - * "Extra data" is referenced by the index in norm32. - * It is variable-length data. It is only present, and only those parts - * of it are, as needed for a given character. - * The norm32 extra data index is added to the beginning of extraData[] - * to get to a vector of 16-bit words with data at the following offsets: - * - * [-1] Combining index for composition. - * Stored only if norm32&_NORM_COMBINES_ANY . - * [0] Lengths of the canonical and compatibility decomposition strings. - * Stored only if there are decompositions, i.e., - * if norm32&(_NORM_QC_NFD|_NORM_QC_NFKD) - * High byte: length of NFKD, or 0 if none - * Low byte: length of NFD, or 0 if none - * Each length byte also has another flag: - * Bit 7 of a length byte is set if there are non-zero - * combining classes (cc's) associated with the respective - * decomposition. If this flag is set, then the decomposition - * is preceded by a 16-bit word that contains the - * leading and trailing cc's. - * Bits 6..0 of a length byte are the length of the - * decomposition string, not counting the cc word. - * [1..n] NFD - * [n+1..] NFKD - * - * Each of the two decompositions consists of up to two parts: - * - The 16-bit words with the leading and trailing cc's. - * This is only stored if bit 7 of the corresponding length byte - * is set. In this case, at least one of the cc's is not zero. - * High byte: leading cc==cc of the first code point in the decomposition string - * Low byte: trailing cc==cc of the last code point in the decomposition string - * - The decomposition string in UTF-16, with length code units. - * - * - * - Combining indexes and combiningTable[] - * - * Combining indexes are stored at the [-1] offset of the extra data - * if the character combines forward or backward with any other characters. - * They are used for (re)composition in NF*C. - * Values of combining indexes are arranged according to whether a character - * combines forward, backward, or both ways: - * forward-only < both ways < backward-only - * - * The index values for forward-only and both-ways combining characters - * are indexes into the combiningTable[]. - * The index values for backward-only combining characters are simply - * incremented from the preceding index values to be unique. - * - * In the combiningTable[], a variable-length list - * of variable-length (back-index, code point) pair entries is stored - * for each forward-combining character. - * - * These back-indexes are the combining indexes of both-ways or backward-only - * combining characters that the forward-combining character combines with. - * - * Each list is sorted in ascending order of back-indexes. - * Each list is terminated with the last back-index having bit 15 set. - * - * Each pair (back-index, code point) takes up either 2 or 3 - * 16-bit words. - * The first word of a list entry is the back-index, with its bit 15 set if - * this is the last pair in the list. - * - * The second word contains flags in bits 15..13 that determine - * if there is a third word and how the combined character is encoded: - * 15 set if there is a third word in this list entry - * 14 set if the result is a supplementary character - * 13 set if the result itself combines forward - * - * According to these bits 15..14 of the second word, - * the result character is encoded as follows: - * 00 or 01 The result is <=0x1fff and stored in bits 12..0 of - * the second word. - * 10 The result is 0x2000..0xffff and stored in the third word. - * Bits 12..0 of the second word are not used. - * 11 The result is a supplementary character. - * Bits 9..0 of the leading surrogate are in bits 9..0 of - * the second word. - * Add 0xd800 to these bits to get the complete surrogate. - * Bits 12..10 of the second word are not used. - * The trailing surrogate is stored in the third word. - * - * - * - FCD trie - * - * The FCD trie is very simple. - * It is a folded trie with 16-bit data words. - * In each word, the high byte contains the leading cc of the character, - * and the low byte contains the trailing cc of the character. - * These cc's are the cc's of the first and last code points in the - * canonical decomposition of the character. - * - * Since all 16 bits are used for cc's, lead surrogates must be tested - * by checking the code unit instead of the trie data. - * This is done only if the 16-bit data word is not zero. - * If the code unit is a leading surrogate and the data word is not zero, - * then instead of cc's it contains the offset for the second trie lookup. - * - * - * - Auxiliary trie and data - * - * The auxiliary 16-bit trie contains data for additional properties. - * Bits - * 15..13 reserved - * 12 not NFC_Skippable (f) (formatVersion>=2.2) - * 11 flag: not a safe starter for canonical closure - * 10 composition exclusion - * 9.. 0 index into extraData[] to FC_NFKC_Closure string - * (not for lead surrogate), - * or lead surrogate offset (for lead surrogate, if 9..0 not zero) - * - * - FC_NFKC_Closure strings in extraData[] - * - * Strings are either stored as a single code unit or as the length - * followed by that many units. - * const UChar *s=extraData+(index from auxTrie data bits 9..0); - * int32_t length; - * if(*s<0xff00) { - * // s points to the single-unit string - * length=1; - * } else { - * length=*s&0xff; - * ++s; - * } - * - * Conditions for "NF* Skippable" from Mark Davis' com.ibm.text.UCD.NFSkippable: - * (used in NormalizerTransliterator) - * - * A skippable character is - * a) unassigned, or ALL of the following: - * b) of combining class 0. - * c) not decomposed by this normalization form. - * AND if NFC or NFKC, - * d) can never compose with a previous character. - * e) can never compose with a following character. - * f) can never change if another character is added. - * Example: a-breve might satisfy all but f, but if you - * add an ogonek it changes to a-ogonek + breve - * - * a)..e) must be tested from norm32. - * Since f) is more complicated, the (not-)NFC_Skippable flag (f) is built - * into the auxiliary trie. - * The same bit is used for NFC and NFKC; (c) differs for them. - * As usual, we build the "not skippable" flags so that unassigned - * code points get a 0 bit. - * This bit is only valid after (a)..(e) test false; test NFD_NO before (f) as well. - * Test Hangul LV syllables entirely in code. - * - * - * - structure inside canonStartSets[] - * - * This array maps from code points c to sets of code points (USerializedSet). - * The result sets are the code points whose canonical decompositions start - * with c. - * - * canonStartSets[] contains the following sub-arrays: - * - * indexes[_NORM_SET_INDEX_TOP] - * - contains lengths of sub-arrays etc. - * - * startSets[indexes[_NORM_SET_INDEX_CANON_SETS_LENGTH]-_NORM_SET_INDEX_TOP] - * - contains serialized sets (USerializedSet) of canonical starters for - * enumerating canonically equivalent strings - * indexes[_NORM_SET_INDEX_CANON_SETS_LENGTH] includes _NORM_SET_INDEX_TOP - * for details about the structure see uset.c - * - * bmpTable[indexes[_NORM_SET_INDEX_CANON_BMP_TABLE_LENGTH]] - * - a sorted search table for BMP code points whose results are - * either indexes to USerializedSets or single code points for - * single-code point sets; - * each entry is a pair of { code point, result } with result=(binary) yy xxxxxx xxxxxxxx - * if yy==01 then there is a USerializedSet at canonStartSets+x - * else build a USerializedSet with result as the single code point - * - * suppTable[indexes[_NORM_SET_INDEX_CANON_SUPP_TABLE_LENGTH]] - * - a sorted search table for supplementary code points whose results are - * either indexes to USerializedSets or single code points for - * single-code point sets; - * each entry is a triplet of { high16(cp), low16(cp), result } - * each code point's high-word may contain extra data in bits 15..5: - * if the high word has bit 15 set, then build a set with a single code point - * which is (((high16(cp)&0x1f00)<<8)|result; - * else there is a USerializedSet at canonStartSets+result - * - * FormatVersion 2.3 adds 2 serialized sets for normalization exclusions. - * They are stored in the data file so that the runtime normalization code need - * not depend on other properties and their data and implementation files. - * The _NORM_SET_INDEX_NX_..._OFFSET offsets in the canonStartSets index table - * give the location for each set. - * There is no set stored for UNORM_NX_HANGUL because it's trivial to create - * without using properties. - * - * Set contents: - * - * _NORM_SET_INDEX_NX_CJK_COMPAT_OFFSET (for UNORM_NX_CJK_COMPAT) - * [[:Ideographic:]&[:NFD_QC=No:]] - * =[CJK Ideographs]&[has canonical decomposition] - * - * _NORM_SET_INDEX_NX_UNICODE32_OFFSET (for UNORM_UNICODE_3_2) - * [:^Age=3.2:] - * =set with all code points that were not designated by the specified Unicode version - * - * _NORM_SET_INDEX_NX_RESERVED_OFFSET - * This is an offset that points to where the next, future set would start. - * Currently it indicates where the previous set ends, and thus its length. - * The name for this enum constant may in the future be applied to different - * index slots. In order to get the limit of a set, use its index slot and - * the immediately following one regardless of that one's enum name. - */ - -#endif /* #if !UCONFIG_NO_NORMALIZATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2001-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: unormimp.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2001may25 +* created by: Markus W. Scherer +*/ + +#ifndef __UNORMIMP_H__ +#define __UNORMIMP_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_NORMALIZATION + +#include "udataswp.h" + +/* + * The 2001-2010 implementation of the normalization code loads its data from + * unorm.icu, which is generated with the gennorm tool. + * The format of that file is described at the end of this file. + */ + +/* norm32 value constants */ +enum { + /* quick check flags 0..3 set mean "no" for their forms */ + _NORM_QC_NFC=0x11, /* no|maybe */ + _NORM_QC_NFKC=0x22, /* no|maybe */ + _NORM_QC_NFD=4, /* no */ + _NORM_QC_NFKD=8, /* no */ + + _NORM_QC_ANY_NO=0xf, + + /* quick check flags 4..5 mean "maybe" for their forms; test flags>=_NORM_QC_MAYBE */ + _NORM_QC_MAYBE=0x10, + _NORM_QC_ANY_MAYBE=0x30, + + _NORM_QC_MASK=0x3f, + + _NORM_COMBINES_FWD=0x40, + _NORM_COMBINES_BACK=0x80, + _NORM_COMBINES_ANY=0xc0, + + _NORM_CC_SHIFT=8, /* UnicodeData.txt combining class in bits 15..8 */ + _NORM_CC_MASK=0xff00, + + _NORM_EXTRA_SHIFT=16, /* 16 bits for the index to UChars and other extra data */ + _NORM_EXTRA_INDEX_TOP=0xfc00, /* start of surrogate specials after shift */ + + _NORM_EXTRA_SURROGATE_MASK=0x3ff, + _NORM_EXTRA_SURROGATE_TOP=0x3f0, /* hangul etc. */ + + _NORM_EXTRA_HANGUL=_NORM_EXTRA_SURROGATE_TOP, + _NORM_EXTRA_JAMO_L, + _NORM_EXTRA_JAMO_V, + _NORM_EXTRA_JAMO_T +}; + +/* norm32 value constants using >16 bits */ +#define _NORM_MIN_SPECIAL 0xfc000000 +#define _NORM_SURROGATES_TOP 0xfff00000 +#define _NORM_MIN_HANGUL 0xfff00000 +#define _NORM_MIN_JAMO_V 0xfff20000 +#define _NORM_JAMO_V_TOP 0xfff30000 + +/* value constants for auxTrie */ +enum { + _NORM_AUX_COMP_EX_SHIFT=10, + _NORM_AUX_UNSAFE_SHIFT=11, + _NORM_AUX_NFC_SKIPPABLE_F_SHIFT=12 +}; + +#define _NORM_AUX_MAX_FNC ((int32_t)1<<_NORM_AUX_COMP_EX_SHIFT) + +#define _NORM_AUX_FNC_MASK (uint32_t)(_NORM_AUX_MAX_FNC-1) +#define _NORM_AUX_COMP_EX_MASK ((uint32_t)1<<_NORM_AUX_COMP_EX_SHIFT) +#define _NORM_AUX_UNSAFE_MASK ((uint32_t)1<<_NORM_AUX_UNSAFE_SHIFT) +#define _NORM_AUX_NFC_SKIP_F_MASK ((uint32_t)1<<_NORM_AUX_NFC_SKIPPABLE_F_SHIFT) + +/* canonStartSets[0..31] contains indexes for what is in the array */ +enum { + _NORM_SET_INDEX_CANON_SETS_LENGTH, /* number of uint16_t in canonical starter sets */ + _NORM_SET_INDEX_CANON_BMP_TABLE_LENGTH, /* number of uint16_t in the BMP search table (contains pairs) */ + _NORM_SET_INDEX_CANON_SUPP_TABLE_LENGTH,/* number of uint16_t in the supplementary search table (contains triplets) */ + + /* from formatVersion 2.3: */ + _NORM_SET_INDEX_NX_CJK_COMPAT_OFFSET, /* uint16_t offset from canonStartSets[0] to the + exclusion set for CJK compatibility characters */ + _NORM_SET_INDEX_NX_UNICODE32_OFFSET, /* uint16_t offset from canonStartSets[0] to the + exclusion set for Unicode 3.2 characters */ + _NORM_SET_INDEX_NX_RESERVED_OFFSET, /* uint16_t offset from canonStartSets[0] to the + end of the previous exclusion set */ + + _NORM_SET_INDEX_TOP=32 /* changing this requires a new formatVersion */ +}; + +/* more constants for canonical starter sets */ + +/* 14 bit indexes to canonical USerializedSets */ +#define _NORM_MAX_CANON_SETS 0x4000 + +/* single-code point BMP sets are encoded directly in the search table except if result=0x4000..0x7fff */ +#define _NORM_CANON_SET_BMP_MASK 0xc000 +#define _NORM_CANON_SET_BMP_IS_INDEX 0x4000 + +/* indexes[] value names */ +enum { + _NORM_INDEX_TRIE_SIZE, /* number of bytes in normalization trie */ + _NORM_INDEX_UCHAR_COUNT, /* number of UChars in extra data */ + + _NORM_INDEX_COMBINE_DATA_COUNT, /* number of uint16_t words for combining data */ + _NORM_INDEX_COMBINE_FWD_COUNT, /* number of code points that combine forward */ + _NORM_INDEX_COMBINE_BOTH_COUNT, /* number of code points that combine forward and backward */ + _NORM_INDEX_COMBINE_BACK_COUNT, /* number of code points that combine backward */ + + _NORM_INDEX_MIN_NFC_NO_MAYBE, /* first code point with quick check NFC NO/MAYBE */ + _NORM_INDEX_MIN_NFKC_NO_MAYBE, /* first code point with quick check NFKC NO/MAYBE */ + _NORM_INDEX_MIN_NFD_NO_MAYBE, /* first code point with quick check NFD NO/MAYBE */ + _NORM_INDEX_MIN_NFKD_NO_MAYBE, /* first code point with quick check NFKD NO/MAYBE */ + + _NORM_INDEX_FCD_TRIE_SIZE, /* number of bytes in FCD trie */ + + _NORM_INDEX_AUX_TRIE_SIZE, /* number of bytes in the auxiliary trie */ + _NORM_INDEX_CANON_SET_COUNT, /* number of uint16_t in the array of serialized USet */ + + _NORM_INDEX_TOP=32 /* changing this requires a new formatVersion */ +}; + +enum { + /* FCD check: everything below this code point is known to have a 0 lead combining class */ + _NORM_MIN_WITH_LEAD_CC=0x300 +}; + +enum { + /** + * Bit 7 of the length byte for a decomposition string in extra data is + * a flag indicating whether the decomposition string is + * preceded by a 16-bit word with the leading and trailing cc + * of the decomposition (like for A-umlaut); + * if not, then both cc's are zero (like for compatibility ideographs). + */ + _NORM_DECOMP_FLAG_LENGTH_HAS_CC=0x80, + /** + * Bits 6..0 of the length byte contain the actual length. + */ + _NORM_DECOMP_LENGTH_MASK=0x7f +}; + +/** Constants for options flags for normalization. */ +enum { + /** Options bit 0, do not decompose Hangul syllables. */ + UNORM_NX_HANGUL=1, + /** Options bit 1, do not decompose CJK compatibility characters. */ + UNORM_NX_CJK_COMPAT=2 +}; + +/** + * Description of the format of unorm.icu version 2.3. + * + * Main change from version 1 to version 2: + * Use of new, common UTrie instead of normalization-specific tries. + * Change to version 2.1: add third/auxiliary trie with associated data. + * Change to version 2.2: add skippable (f) flag data (_NORM_AUX_NFC_SKIP_F_MASK). + * Change to version 2.3: add serialized sets for normalization exclusions + * stored inside canonStartSets[] + * + * For more details of how to use the data structures see the code + * in unorm.cpp (runtime normalization code) and + * in gennorm.c and gennorm/store.c (build-time data generation). + * + * For the serialized format of UTrie see utrie.c/UTrieHeader. + * + * - Overall partition + * + * unorm.dat customarily begins with a UDataInfo structure, see udata.h and .c. + * After that there are the following structures: + * + * int32_t indexes[_NORM_INDEX_TOP]; -- _NORM_INDEX_TOP=32, see enum in this file + * + * UTrie normTrie; -- size in bytes=indexes[_NORM_INDEX_TRIE_SIZE] + * + * uint16_t extraData[extraDataTop]; -- extraDataTop=indexes[_NORM_INDEX_UCHAR_COUNT] + * extraData[0] contains the number of units for + * FC_NFKC_Closure (formatVersion>=2.1) + * + * uint16_t combiningTable[combiningTableTop]; -- combiningTableTop=indexes[_NORM_INDEX_COMBINE_DATA_COUNT] + * combiningTableTop may include one 16-bit padding unit + * to make sure that fcdTrie is 32-bit-aligned + * + * UTrie fcdTrie; -- size in bytes=indexes[_NORM_INDEX_FCD_TRIE_SIZE] + * + * UTrie auxTrie; -- size in bytes=indexes[_NORM_INDEX_AUX_TRIE_SIZE] + * + * uint16_t canonStartSets[canonStartSetsTop] -- canonStartSetsTop=indexes[_NORM_INDEX_CANON_SET_COUNT] + * serialized USets and binary search tables, see below + * + * + * The indexes array contains lengths and sizes of the following arrays and structures + * as well as the following values: + * indexes[_NORM_INDEX_COMBINE_FWD_COUNT]=combineFwdTop + * -- one more than the highest combining index computed for forward-only-combining characters + * indexes[_NORM_INDEX_COMBINE_BOTH_COUNT]=combineBothTop-combineFwdTop + * -- number of combining indexes computed for both-ways-combining characters + * indexes[_NORM_INDEX_COMBINE_BACK_COUNT]=combineBackTop-combineBothTop + * -- number of combining indexes computed for backward-only-combining characters + * + * indexes[_NORM_INDEX_MIN_NF*_NO_MAYBE] (where *={ C, D, KC, KD }) + * -- first code point with a quick check NF* value of NO/MAYBE + * + * + * - Tries + * + * The main structures are two UTrie tables ("compact arrays"), + * each with one index array and one data array. + * See utrie.h and utrie.c. + * + * + * - Tries in unorm.dat + * + * The first trie (normTrie above) + * provides data for the NF* quick checks and normalization. + * The second trie (fcdTrie above) provides data just for FCD checks. + * + * + * - norm32 data words from the first trie + * + * The norm32Table contains one 32-bit word "norm32" per code point. + * It contains the following bit fields: + * 31..16 extra data index, _NORM_EXTRA_SHIFT is used to shift this field down + * if this index is <_NORM_EXTRA_INDEX_TOP then it is an index into + * extraData[] where variable-length normalization data for this + * code point is found + * if this index is <_NORM_EXTRA_INDEX_TOP+_NORM_EXTRA_SURROGATE_TOP + * then this is a norm32 for a leading surrogate, and the index + * value is used together with the following trailing surrogate + * code unit in the second trie access + * if this index is >=_NORM_EXTRA_INDEX_TOP+_NORM_EXTRA_SURROGATE_TOP + * then this is a norm32 for a "special" character, + * i.e., the character is a Hangul syllable or a Jamo + * see _NORM_EXTRA_HANGUL etc. + * generally, instead of extracting this index from the norm32 and + * comparing it with the above constants, + * the normalization code compares the entire norm32 value + * with _NORM_MIN_SPECIAL, _NORM_SURROGATES_TOP, _NORM_MIN_HANGUL etc. + * + * 15..8 combining class (cc) according to UnicodeData.txt + * + * 7..6 _NORM_COMBINES_ANY flags, used in composition to see if a character + * combines with any following or preceding character(s) + * at all + * 7 _NORM_COMBINES_BACK + * 6 _NORM_COMBINES_FWD + * + * 5..0 quick check flags, set for "no" or "maybe", with separate flags for + * each normalization form + * the higher bits are "maybe" flags; for NF*D there are no such flags + * the lower bits are "no" flags for all forms, in the same order + * as the "maybe" flags, + * which is (MSB to LSB): NFKD NFD NFKC NFC + * 5..4 _NORM_QC_ANY_MAYBE + * 3..0 _NORM_QC_ANY_NO + * see further related constants + * + * + * - Extra data per code point + * + * "Extra data" is referenced by the index in norm32. + * It is variable-length data. It is only present, and only those parts + * of it are, as needed for a given character. + * The norm32 extra data index is added to the beginning of extraData[] + * to get to a vector of 16-bit words with data at the following offsets: + * + * [-1] Combining index for composition. + * Stored only if norm32&_NORM_COMBINES_ANY . + * [0] Lengths of the canonical and compatibility decomposition strings. + * Stored only if there are decompositions, i.e., + * if norm32&(_NORM_QC_NFD|_NORM_QC_NFKD) + * High byte: length of NFKD, or 0 if none + * Low byte: length of NFD, or 0 if none + * Each length byte also has another flag: + * Bit 7 of a length byte is set if there are non-zero + * combining classes (cc's) associated with the respective + * decomposition. If this flag is set, then the decomposition + * is preceded by a 16-bit word that contains the + * leading and trailing cc's. + * Bits 6..0 of a length byte are the length of the + * decomposition string, not counting the cc word. + * [1..n] NFD + * [n+1..] NFKD + * + * Each of the two decompositions consists of up to two parts: + * - The 16-bit words with the leading and trailing cc's. + * This is only stored if bit 7 of the corresponding length byte + * is set. In this case, at least one of the cc's is not zero. + * High byte: leading cc==cc of the first code point in the decomposition string + * Low byte: trailing cc==cc of the last code point in the decomposition string + * - The decomposition string in UTF-16, with length code units. + * + * + * - Combining indexes and combiningTable[] + * + * Combining indexes are stored at the [-1] offset of the extra data + * if the character combines forward or backward with any other characters. + * They are used for (re)composition in NF*C. + * Values of combining indexes are arranged according to whether a character + * combines forward, backward, or both ways: + * forward-only < both ways < backward-only + * + * The index values for forward-only and both-ways combining characters + * are indexes into the combiningTable[]. + * The index values for backward-only combining characters are simply + * incremented from the preceding index values to be unique. + * + * In the combiningTable[], a variable-length list + * of variable-length (back-index, code point) pair entries is stored + * for each forward-combining character. + * + * These back-indexes are the combining indexes of both-ways or backward-only + * combining characters that the forward-combining character combines with. + * + * Each list is sorted in ascending order of back-indexes. + * Each list is terminated with the last back-index having bit 15 set. + * + * Each pair (back-index, code point) takes up either 2 or 3 + * 16-bit words. + * The first word of a list entry is the back-index, with its bit 15 set if + * this is the last pair in the list. + * + * The second word contains flags in bits 15..13 that determine + * if there is a third word and how the combined character is encoded: + * 15 set if there is a third word in this list entry + * 14 set if the result is a supplementary character + * 13 set if the result itself combines forward + * + * According to these bits 15..14 of the second word, + * the result character is encoded as follows: + * 00 or 01 The result is <=0x1fff and stored in bits 12..0 of + * the second word. + * 10 The result is 0x2000..0xffff and stored in the third word. + * Bits 12..0 of the second word are not used. + * 11 The result is a supplementary character. + * Bits 9..0 of the leading surrogate are in bits 9..0 of + * the second word. + * Add 0xd800 to these bits to get the complete surrogate. + * Bits 12..10 of the second word are not used. + * The trailing surrogate is stored in the third word. + * + * + * - FCD trie + * + * The FCD trie is very simple. + * It is a folded trie with 16-bit data words. + * In each word, the high byte contains the leading cc of the character, + * and the low byte contains the trailing cc of the character. + * These cc's are the cc's of the first and last code points in the + * canonical decomposition of the character. + * + * Since all 16 bits are used for cc's, lead surrogates must be tested + * by checking the code unit instead of the trie data. + * This is done only if the 16-bit data word is not zero. + * If the code unit is a leading surrogate and the data word is not zero, + * then instead of cc's it contains the offset for the second trie lookup. + * + * + * - Auxiliary trie and data + * + * The auxiliary 16-bit trie contains data for additional properties. + * Bits + * 15..13 reserved + * 12 not NFC_Skippable (f) (formatVersion>=2.2) + * 11 flag: not a safe starter for canonical closure + * 10 composition exclusion + * 9.. 0 index into extraData[] to FC_NFKC_Closure string + * (not for lead surrogate), + * or lead surrogate offset (for lead surrogate, if 9..0 not zero) + * + * - FC_NFKC_Closure strings in extraData[] + * + * Strings are either stored as a single code unit or as the length + * followed by that many units. + * const char16_t *s=extraData+(index from auxTrie data bits 9..0); + * int32_t length; + * if(*s<0xff00) { + * // s points to the single-unit string + * length=1; + * } else { + * length=*s&0xff; + * ++s; + * } + * + * Conditions for "NF* Skippable" from Mark Davis' com.ibm.text.UCD.NFSkippable: + * (used in NormalizerTransliterator) + * + * A skippable character is + * a) unassigned, or ALL of the following: + * b) of combining class 0. + * c) not decomposed by this normalization form. + * AND if NFC or NFKC, + * d) can never compose with a previous character. + * e) can never compose with a following character. + * f) can never change if another character is added. + * Example: a-breve might satisfy all but f, but if you + * add an ogonek it changes to a-ogonek + breve + * + * a)..e) must be tested from norm32. + * Since f) is more complicated, the (not-)NFC_Skippable flag (f) is built + * into the auxiliary trie. + * The same bit is used for NFC and NFKC; (c) differs for them. + * As usual, we build the "not skippable" flags so that unassigned + * code points get a 0 bit. + * This bit is only valid after (a)..(e) test false; test NFD_NO before (f) as well. + * Test Hangul LV syllables entirely in code. + * + * + * - structure inside canonStartSets[] + * + * This array maps from code points c to sets of code points (USerializedSet). + * The result sets are the code points whose canonical decompositions start + * with c. + * + * canonStartSets[] contains the following sub-arrays: + * + * indexes[_NORM_SET_INDEX_TOP] + * - contains lengths of sub-arrays etc. + * + * startSets[indexes[_NORM_SET_INDEX_CANON_SETS_LENGTH]-_NORM_SET_INDEX_TOP] + * - contains serialized sets (USerializedSet) of canonical starters for + * enumerating canonically equivalent strings + * indexes[_NORM_SET_INDEX_CANON_SETS_LENGTH] includes _NORM_SET_INDEX_TOP + * for details about the structure see uset.c + * + * bmpTable[indexes[_NORM_SET_INDEX_CANON_BMP_TABLE_LENGTH]] + * - a sorted search table for BMP code points whose results are + * either indexes to USerializedSets or single code points for + * single-code point sets; + * each entry is a pair of { code point, result } with result=(binary) yy xxxxxx xxxxxxxx + * if yy==01 then there is a USerializedSet at canonStartSets+x + * else build a USerializedSet with result as the single code point + * + * suppTable[indexes[_NORM_SET_INDEX_CANON_SUPP_TABLE_LENGTH]] + * - a sorted search table for supplementary code points whose results are + * either indexes to USerializedSets or single code points for + * single-code point sets; + * each entry is a triplet of { high16(cp), low16(cp), result } + * each code point's high-word may contain extra data in bits 15..5: + * if the high word has bit 15 set, then build a set with a single code point + * which is (((high16(cp)&0x1f00)<<8)|result; + * else there is a USerializedSet at canonStartSets+result + * + * FormatVersion 2.3 adds 2 serialized sets for normalization exclusions. + * They are stored in the data file so that the runtime normalization code need + * not depend on other properties and their data and implementation files. + * The _NORM_SET_INDEX_NX_..._OFFSET offsets in the canonStartSets index table + * give the location for each set. + * There is no set stored for UNORM_NX_HANGUL because it's trivial to create + * without using properties. + * + * Set contents: + * + * _NORM_SET_INDEX_NX_CJK_COMPAT_OFFSET (for UNORM_NX_CJK_COMPAT) + * [[:Ideographic:]&[:NFD_QC=No:]] + * =[CJK Ideographs]&[has canonical decomposition] + * + * _NORM_SET_INDEX_NX_UNICODE32_OFFSET (for UNORM_UNICODE_3_2) + * [:^Age=3.2:] + * =set with all code points that were not designated by the specified Unicode version + * + * _NORM_SET_INDEX_NX_RESERVED_OFFSET + * This is an offset that points to where the next, future set would start. + * Currently it indicates where the previous set ends, and thus its length. + * The name for this enum constant may in the future be applied to different + * index slots. In order to get the limit of a set, use its index slot and + * the immediately following one regardless of that one's enum name. + */ + +#endif /* #if !UCONFIG_NO_NORMALIZATION */ + +#endif diff --git a/deps/icu-small/source/common/uobject.cpp b/deps/icu-small/source/common/uobject.cpp index e222b2ce9b9096..f48a2c1b6bc1d2 100644 --- a/deps/icu-small/source/common/uobject.cpp +++ b/deps/icu-small/source/common/uobject.cpp @@ -1,105 +1,105 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2002-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: uobject.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002jun26 -* created by: Markus W. Scherer -*/ - -#include "unicode/uobject.h" -#include "cmemory.h" - -U_NAMESPACE_BEGIN - -#if U_OVERRIDE_CXX_ALLOCATION - -/* - * Default implementation of UMemory::new/delete - * using uprv_malloc() and uprv_free(). - * - * For testing, this is used together with a list of imported symbols to verify - * that ICU is not using the global ::new and ::delete operators. - * - * These operators can be implemented like this or any other appropriate way - * when customizing ICU for certain environments. - * Whenever ICU is customized in binary incompatible ways please be sure - * to use library name suffixes to distinguish such libraries from - * the standard build. - * - * Instead of just modifying these C++ new/delete operators, it is usually best - * to modify the uprv_malloc()/uprv_free()/uprv_realloc() functions in cmemory.c. - * - * Memory test on Windows/MSVC 6: - * The global operators new and delete look as follows: - * 04F 00000000 UNDEF notype () External | ??2@YAPAXI@Z (void * __cdecl operator new(unsigned int)) - * 03F 00000000 UNDEF notype () External | ??3@YAXPAX@Z (void __cdecl operator delete(void *)) - * - * These lines are from output generated by the MSVC 6 tool dumpbin with - * dumpbin /symbols *.obj - * - * ??2@YAPAXI@Z and ??3@YAXPAX@Z are the linker symbols in the .obj - * files and are imported from msvcrtd.dll (in a debug build). - * - * Make sure that with the UMemory operators new and delete defined these two symbols - * do not appear in the dumpbin /symbols output for the ICU libraries! - * - * If such a symbol appears in the output then look in the preceding lines in the output - * for which file and function calls the global new or delete operator, - * and replace with uprv_malloc/uprv_free. - */ - -void * U_EXPORT2 UMemory::operator new(size_t size) U_NOEXCEPT { - return uprv_malloc(size); -} - -void U_EXPORT2 UMemory::operator delete(void *p) U_NOEXCEPT { - if(p!=NULL) { - uprv_free(p); - } -} - -void * U_EXPORT2 UMemory::operator new[](size_t size) U_NOEXCEPT { - return uprv_malloc(size); -} - -void U_EXPORT2 UMemory::operator delete[](void *p) U_NOEXCEPT { - if(p!=NULL) { - uprv_free(p); - } -} - -#if U_HAVE_DEBUG_LOCATION_NEW -void * U_EXPORT2 UMemory::operator new(size_t size, const char* /*file*/, int /*line*/) U_NOEXCEPT { - return UMemory::operator new(size); -} - -void U_EXPORT2 UMemory::operator delete(void* p, const char* /*file*/, int /*line*/) U_NOEXCEPT { - UMemory::operator delete(p); -} -#endif /* U_HAVE_DEBUG_LOCATION_NEW */ - - -#endif - -UObject::~UObject() {} - -UClassID UObject::getDynamicClassID() const { return NULL; } - -U_NAMESPACE_END - -U_NAMESPACE_USE - -U_CAPI void U_EXPORT2 -uprv_deleteUObject(void *obj) { - delete static_cast(obj); -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2002-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: uobject.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002jun26 +* created by: Markus W. Scherer +*/ + +#include "unicode/uobject.h" +#include "cmemory.h" + +U_NAMESPACE_BEGIN + +#if U_OVERRIDE_CXX_ALLOCATION + +/* + * Default implementation of UMemory::new/delete + * using uprv_malloc() and uprv_free(). + * + * For testing, this is used together with a list of imported symbols to verify + * that ICU is not using the global ::new and ::delete operators. + * + * These operators can be implemented like this or any other appropriate way + * when customizing ICU for certain environments. + * Whenever ICU is customized in binary incompatible ways please be sure + * to use library name suffixes to distinguish such libraries from + * the standard build. + * + * Instead of just modifying these C++ new/delete operators, it is usually best + * to modify the uprv_malloc()/uprv_free()/uprv_realloc() functions in cmemory.c. + * + * Memory test on Windows/MSVC 6: + * The global operators new and delete look as follows: + * 04F 00000000 UNDEF notype () External | ??2@YAPAXI@Z (void * __cdecl operator new(unsigned int)) + * 03F 00000000 UNDEF notype () External | ??3@YAXPAX@Z (void __cdecl operator delete(void *)) + * + * These lines are from output generated by the MSVC 6 tool dumpbin with + * dumpbin /symbols *.obj + * + * ??2@YAPAXI@Z and ??3@YAXPAX@Z are the linker symbols in the .obj + * files and are imported from msvcrtd.dll (in a debug build). + * + * Make sure that with the UMemory operators new and delete defined these two symbols + * do not appear in the dumpbin /symbols output for the ICU libraries! + * + * If such a symbol appears in the output then look in the preceding lines in the output + * for which file and function calls the global new or delete operator, + * and replace with uprv_malloc/uprv_free. + */ + +void * U_EXPORT2 UMemory::operator new(size_t size) noexcept { + return uprv_malloc(size); +} + +void U_EXPORT2 UMemory::operator delete(void *p) noexcept { + if(p!=nullptr) { + uprv_free(p); + } +} + +void * U_EXPORT2 UMemory::operator new[](size_t size) noexcept { + return uprv_malloc(size); +} + +void U_EXPORT2 UMemory::operator delete[](void *p) noexcept { + if(p!=nullptr) { + uprv_free(p); + } +} + +#if U_HAVE_DEBUG_LOCATION_NEW +void * U_EXPORT2 UMemory::operator new(size_t size, const char* /*file*/, int /*line*/) noexcept { + return UMemory::operator new(size); +} + +void U_EXPORT2 UMemory::operator delete(void* p, const char* /*file*/, int /*line*/) noexcept { + UMemory::operator delete(p); +} +#endif /* U_HAVE_DEBUG_LOCATION_NEW */ + + +#endif + +UObject::~UObject() {} + +UClassID UObject::getDynamicClassID() const { return nullptr; } + +U_NAMESPACE_END + +U_NAMESPACE_USE + +U_CAPI void U_EXPORT2 +uprv_deleteUObject(void *obj) { + delete static_cast(obj); +} diff --git a/deps/icu-small/source/common/uposixdefs.h b/deps/icu-small/source/common/uposixdefs.h index 23c3f6d466700c..6eb268bbc507f5 100644 --- a/deps/icu-small/source/common/uposixdefs.h +++ b/deps/icu-small/source/common/uposixdefs.h @@ -1,77 +1,77 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: uposixdefs.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2011jul25 -* created by: Markus W. Scherer -* -* Common definitions for implementation files working with POSIX functions. -* *Important*: #include this file before any other header files! -*/ - -#ifndef __UPOSIXDEFS_H__ -#define __UPOSIXDEFS_H__ - -/* - * Define _XOPEN_SOURCE for access to POSIX functions. - * - * We cannot use U_PLATFORM from platform.h/utypes.h because - * "The Open Group Base Specifications" - * chapter "2.2 The Compilation Environment" says: - * "In the compilation of an application that #defines a feature test macro - * specified by IEEE Std 1003.1-2001, - * no header defined by IEEE Std 1003.1-2001 shall be included prior to - * the definition of the feature test macro." - */ -#ifdef _XOPEN_SOURCE - /* Use the predefined value. */ -#else - /* - * Version 6.0: - * The Open Group Base Specifications Issue 6 (IEEE Std 1003.1, 2004 Edition) - * also known as - * SUSv3 = Open Group Single UNIX Specification, Version 3 (UNIX03) - * - * Note: This definition used to be in C source code (e.g., putil.c) - * and define _XOPEN_SOURCE to different values depending on __STDC_VERSION__. - * In C++ source code (e.g., putil.cpp), __STDC_VERSION__ is not defined at all. - */ -# define _XOPEN_SOURCE 600 -#endif - -/* - * Make sure things like readlink and such functions work. - * Poorly upgraded Solaris machines can't have this defined. - * Cleanly installed Solaris can use this #define. - * - * z/OS needs this definition for timeval and to get usleep. - */ -#if !defined(_XOPEN_SOURCE_EXTENDED) && defined(__TOS_MVS__) -# define _XOPEN_SOURCE_EXTENDED 1 -#endif - -/** - * Solaris says: - * "...it is invalid to compile an XPG6 or a POSIX.1-2001 application with anything other - * than a c99 or later compiler." - * Apparently C++11 is not "or later". Work around this. - */ -#if defined(__cplusplus) && (defined(sun) || defined(__sun)) && !defined (_STDC_C99) -# define _STDC_C99 -#endif - -#if !defined _POSIX_C_SOURCE && \ - defined(__APPLE__) && defined(__MACH__) && !defined(__clang__) -// Needed to prevent EOWNERDEAD issues with GCC on Mac -#define _POSIX_C_SOURCE 200809L -#endif - -#endif /* __UPOSIXDEFS_H__ */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2011-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: uposixdefs.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2011jul25 +* created by: Markus W. Scherer +* +* Common definitions for implementation files working with POSIX functions. +* *Important*: #include this file before any other header files! +*/ + +#ifndef __UPOSIXDEFS_H__ +#define __UPOSIXDEFS_H__ + +/* + * Define _XOPEN_SOURCE for access to POSIX functions. + * + * We cannot use U_PLATFORM from platform.h/utypes.h because + * "The Open Group Base Specifications" + * chapter "2.2 The Compilation Environment" says: + * "In the compilation of an application that #defines a feature test macro + * specified by IEEE Std 1003.1-2001, + * no header defined by IEEE Std 1003.1-2001 shall be included prior to + * the definition of the feature test macro." + */ +#ifdef _XOPEN_SOURCE + /* Use the predefined value. */ +#else + /* + * Version 6.0: + * The Open Group Base Specifications Issue 6 (IEEE Std 1003.1, 2004 Edition) + * also known as + * SUSv3 = Open Group Single UNIX Specification, Version 3 (UNIX03) + * + * Note: This definition used to be in C source code (e.g., putil.c) + * and define _XOPEN_SOURCE to different values depending on __STDC_VERSION__. + * In C++ source code (e.g., putil.cpp), __STDC_VERSION__ is not defined at all. + */ +# define _XOPEN_SOURCE 600 +#endif + +/* + * Make sure things like realpath and such functions work. + * Poorly upgraded Solaris machines can't have this defined. + * Cleanly installed Solaris can use this #define. + * + * z/OS needs this definition for timeval and to get usleep. + */ +#if !defined(_XOPEN_SOURCE_EXTENDED) && defined(__TOS_MVS__) +# define _XOPEN_SOURCE_EXTENDED 1 +#endif + +/** + * Solaris says: + * "...it is invalid to compile an XPG6 or a POSIX.1-2001 application with anything other + * than a c99 or later compiler." + * Apparently C++11 is not "or later". Work around this. + */ +#if defined(__cplusplus) && (defined(sun) || defined(__sun)) && !defined (_STDC_C99) +# define _STDC_C99 +#endif + +#if !defined _POSIX_C_SOURCE && \ + defined(__APPLE__) && defined(__MACH__) && !defined(__clang__) +// Needed to prevent EOWNERDEAD issues with GCC on Mac +#define _POSIX_C_SOURCE 200809L +#endif + +#endif /* __UPOSIXDEFS_H__ */ diff --git a/deps/icu-small/source/common/uprops.cpp b/deps/icu-small/source/common/uprops.cpp index 26e950b876b103..67d31a8baa7187 100644 --- a/deps/icu-small/source/common/uprops.cpp +++ b/deps/icu-small/source/common/uprops.cpp @@ -1,830 +1,843 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uprops.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002feb24 -* created by: Markus W. Scherer -* -* Implementations for mostly non-core Unicode character properties -* stored in uprops.icu. -* -* With the APIs implemented here, almost all properties files and -* their associated implementation files are used from this file, -* including those for normalization and case mappings. -*/ - -#include "unicode/utypes.h" -#include "unicode/uchar.h" -#include "unicode/ucptrie.h" -#include "unicode/udata.h" -#include "unicode/unorm2.h" -#include "unicode/uscript.h" -#include "unicode/ustring.h" -#include "unicode/utf16.h" -#include "cstring.h" -#include "emojiprops.h" -#include "mutex.h" -#include "normalizer2impl.h" -#include "umutex.h" -#include "ubidi_props.h" -#include "uprops.h" -#include "ucase.h" -#include "ucln_cmn.h" -#include "ulayout_props.h" -#include "ustr_imp.h" - -U_NAMESPACE_USE - -// Unicode text layout properties data ----------------------------------------- - -namespace { - -icu::UInitOnce gLayoutInitOnce {}; -UDataMemory *gLayoutMemory = nullptr; - -UCPTrie *gInpcTrie = nullptr; // Indic_Positional_Category -UCPTrie *gInscTrie = nullptr; // Indic_Syllabic_Category -UCPTrie *gVoTrie = nullptr; // Vertical_Orientation - -int32_t gMaxInpcValue = 0; -int32_t gMaxInscValue = 0; -int32_t gMaxVoValue = 0; - -UBool U_CALLCONV uprops_cleanup() { - udata_close(gLayoutMemory); - gLayoutMemory = nullptr; - - ucptrie_close(gInpcTrie); - gInpcTrie = nullptr; - ucptrie_close(gInscTrie); - gInscTrie = nullptr; - ucptrie_close(gVoTrie); - gVoTrie = nullptr; - - gMaxInpcValue = 0; - gMaxInscValue = 0; - gMaxVoValue = 0; - - gLayoutInitOnce.reset(); - return true; -} - -UBool U_CALLCONV -ulayout_isAcceptable(void * /*context*/, - const char * /* type */, const char * /*name*/, - const UDataInfo *pInfo) { - return pInfo->size >= 20 && - pInfo->isBigEndian == U_IS_BIG_ENDIAN && - pInfo->charsetFamily == U_CHARSET_FAMILY && - pInfo->dataFormat[0] == ULAYOUT_FMT_0 && - pInfo->dataFormat[1] == ULAYOUT_FMT_1 && - pInfo->dataFormat[2] == ULAYOUT_FMT_2 && - pInfo->dataFormat[3] == ULAYOUT_FMT_3 && - pInfo->formatVersion[0] == 1; -} - -// UInitOnce singleton initialization function -void U_CALLCONV ulayout_load(UErrorCode &errorCode) { - gLayoutMemory = udata_openChoice( - nullptr, ULAYOUT_DATA_TYPE, ULAYOUT_DATA_NAME, - ulayout_isAcceptable, nullptr, &errorCode); - if (U_FAILURE(errorCode)) { return; } - - const uint8_t *inBytes = (const uint8_t *)udata_getMemory(gLayoutMemory); - const int32_t *inIndexes = (const int32_t *)inBytes; - int32_t indexesLength = inIndexes[ULAYOUT_IX_INDEXES_LENGTH]; - if (indexesLength < 12) { - errorCode = U_INVALID_FORMAT_ERROR; // Not enough indexes. - return; - } - int32_t offset = indexesLength * 4; - int32_t top = inIndexes[ULAYOUT_IX_INPC_TRIE_TOP]; - int32_t trieSize = top - offset; - if (trieSize >= 16) { - gInpcTrie = ucptrie_openFromBinary( - UCPTRIE_TYPE_ANY, UCPTRIE_VALUE_BITS_ANY, - inBytes + offset, trieSize, nullptr, &errorCode); - } - offset = top; - top = inIndexes[ULAYOUT_IX_INSC_TRIE_TOP]; - trieSize = top - offset; - if (trieSize >= 16) { - gInscTrie = ucptrie_openFromBinary( - UCPTRIE_TYPE_ANY, UCPTRIE_VALUE_BITS_ANY, - inBytes + offset, trieSize, nullptr, &errorCode); - } - offset = top; - top = inIndexes[ULAYOUT_IX_VO_TRIE_TOP]; - trieSize = top - offset; - if (trieSize >= 16) { - gVoTrie = ucptrie_openFromBinary( - UCPTRIE_TYPE_ANY, UCPTRIE_VALUE_BITS_ANY, - inBytes + offset, trieSize, nullptr, &errorCode); - } - - uint32_t maxValues = inIndexes[ULAYOUT_IX_MAX_VALUES]; - gMaxInpcValue = maxValues >> ULAYOUT_MAX_INPC_SHIFT; - gMaxInscValue = (maxValues >> ULAYOUT_MAX_INSC_SHIFT) & 0xff; - gMaxVoValue = (maxValues >> ULAYOUT_MAX_VO_SHIFT) & 0xff; - - ucln_common_registerCleanup(UCLN_COMMON_UPROPS, uprops_cleanup); -} - -UBool ulayout_ensureData(UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return false; } - umtx_initOnce(gLayoutInitOnce, &ulayout_load, errorCode); - return U_SUCCESS(errorCode); -} - -UBool ulayout_ensureData() { - UErrorCode errorCode = U_ZERO_ERROR; - return ulayout_ensureData(errorCode); -} - -} // namespace - -/* general properties API functions ----------------------------------------- */ - -struct BinaryProperty; - -typedef UBool BinaryPropertyContains(const BinaryProperty &prop, UChar32 c, UProperty which); - -struct BinaryProperty { - int32_t column; // SRC_PROPSVEC column, or "source" if mask==0 - uint32_t mask; - BinaryPropertyContains *contains; -}; - -static UBool defaultContains(const BinaryProperty &prop, UChar32 c, UProperty /*which*/) { - /* systematic, directly stored properties */ - return (u_getUnicodeProperties(c, prop.column)&prop.mask)!=0; -} - -static UBool caseBinaryPropertyContains(const BinaryProperty &/*prop*/, UChar32 c, UProperty which) { - return static_cast(ucase_hasBinaryProperty(c, which)); -} - -static UBool isBidiControl(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return ubidi_isBidiControl(c); -} - -static UBool isMirrored(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return ubidi_isMirrored(c); -} - -static UBool isJoinControl(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return ubidi_isJoinControl(c); -} - -#if UCONFIG_NO_NORMALIZATION -static UBool hasFullCompositionExclusion(const BinaryProperty &, UChar32, UProperty) { - return false; -} -#else -static UBool hasFullCompositionExclusion(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - // By definition, Full_Composition_Exclusion is the same as NFC_QC=No. - UErrorCode errorCode=U_ZERO_ERROR; - const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(errorCode); - return U_SUCCESS(errorCode) && impl->isCompNo(impl->getNorm16(c)); -} -#endif - -// UCHAR_NF*_INERT properties -#if UCONFIG_NO_NORMALIZATION -static UBool isNormInert(const BinaryProperty &, UChar32, UProperty) { - return false; -} -#else -static UBool isNormInert(const BinaryProperty &/*prop*/, UChar32 c, UProperty which) { - UErrorCode errorCode=U_ZERO_ERROR; - const Normalizer2 *norm2=Normalizer2Factory::getInstance( - (UNormalizationMode)(which-UCHAR_NFD_INERT+UNORM_NFD), errorCode); - return U_SUCCESS(errorCode) && norm2->isInert(c); -} -#endif - -#if UCONFIG_NO_NORMALIZATION -static UBool changesWhenCasefolded(const BinaryProperty &, UChar32, UProperty) { - return false; -} -#else -static UBool changesWhenCasefolded(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - UnicodeString nfd; - UErrorCode errorCode=U_ZERO_ERROR; - const Normalizer2 *nfcNorm2=Normalizer2::getNFCInstance(errorCode); - if(U_FAILURE(errorCode)) { - return false; - } - if(nfcNorm2->getDecomposition(c, nfd)) { - /* c has a decomposition */ - if(nfd.length()==1) { - c=nfd[0]; /* single BMP code point */ - } else if(nfd.length()<=U16_MAX_LENGTH && - nfd.length()==U16_LENGTH(c=nfd.char32At(0)) - ) { - /* single supplementary code point */ - } else { - c=U_SENTINEL; - } - } else if(c<0) { - return false; /* protect against bad input */ - } - if(c>=0) { - /* single code point */ - const UChar *resultString; - return (UBool)(ucase_toFullFolding(c, &resultString, U_FOLD_CASE_DEFAULT)>=0); - } else { - /* guess some large but stack-friendly capacity */ - UChar dest[2*UCASE_MAX_STRING_LENGTH]; - int32_t destLength; - destLength=u_strFoldCase(dest, UPRV_LENGTHOF(dest), - nfd.getBuffer(), nfd.length(), - U_FOLD_CASE_DEFAULT, &errorCode); - return (UBool)(U_SUCCESS(errorCode) && - 0!=u_strCompare(nfd.getBuffer(), nfd.length(), - dest, destLength, false)); - } -} -#endif - -#if UCONFIG_NO_NORMALIZATION -static UBool changesWhenNFKC_Casefolded(const BinaryProperty &, UChar32, UProperty) { - return false; -} -#else -static UBool changesWhenNFKC_Casefolded(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - UErrorCode errorCode=U_ZERO_ERROR; - const Normalizer2Impl *kcf=Normalizer2Factory::getNFKC_CFImpl(errorCode); - if(U_FAILURE(errorCode)) { - return false; - } - UnicodeString src(c); - UnicodeString dest; - { - // The ReorderingBuffer must be in a block because its destructor - // needs to release dest's buffer before we look at its contents. - ReorderingBuffer buffer(*kcf, dest); - // Small destCapacity for NFKC_CF(c). - if(buffer.init(5, errorCode)) { - const UChar *srcArray=src.getBuffer(); - kcf->compose(srcArray, srcArray+src.length(), false, - true, buffer, errorCode); - } - } - return U_SUCCESS(errorCode) && dest!=src; -} -#endif - -#if UCONFIG_NO_NORMALIZATION -static UBool isCanonSegmentStarter(const BinaryProperty &, UChar32, UProperty) { - return false; -} -#else -static UBool isCanonSegmentStarter(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - UErrorCode errorCode=U_ZERO_ERROR; - const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(errorCode); - return - U_SUCCESS(errorCode) && impl->ensureCanonIterData(errorCode) && - impl->isCanonSegmentStarter(c); -} -#endif - -static UBool isPOSIX_alnum(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return u_isalnumPOSIX(c); -} - -static UBool isPOSIX_blank(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return u_isblank(c); -} - -static UBool isPOSIX_graph(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return u_isgraphPOSIX(c); -} - -static UBool isPOSIX_print(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return u_isprintPOSIX(c); -} - -static UBool isPOSIX_xdigit(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return u_isxdigit(c); -} - -static UBool isRegionalIndicator(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - // Property starts are a subset of lb=RI etc. - return 0x1F1E6<=c && c<=0x1F1FF; -} - -static UBool hasEmojiProperty(const BinaryProperty &/*prop*/, UChar32 c, UProperty which) { - return EmojiProps::hasBinaryProperty(c, which); -} - -static const BinaryProperty binProps[UCHAR_BINARY_LIMIT]={ - /* - * column and mask values for binary properties from u_getUnicodeProperties(). - * Must be in order of corresponding UProperty, - * and there must be exactly one entry per binary UProperty. - * - * Properties with mask==0 are handled in code. - * For them, column is the UPropertySource value. - */ - { 1, U_MASK(UPROPS_ALPHABETIC), defaultContains }, - { 1, U_MASK(UPROPS_ASCII_HEX_DIGIT), defaultContains }, - { UPROPS_SRC_BIDI, 0, isBidiControl }, - { UPROPS_SRC_BIDI, 0, isMirrored }, - { 1, U_MASK(UPROPS_DASH), defaultContains }, - { 1, U_MASK(UPROPS_DEFAULT_IGNORABLE_CODE_POINT), defaultContains }, - { 1, U_MASK(UPROPS_DEPRECATED), defaultContains }, - { 1, U_MASK(UPROPS_DIACRITIC), defaultContains }, - { 1, U_MASK(UPROPS_EXTENDER), defaultContains }, - { UPROPS_SRC_NFC, 0, hasFullCompositionExclusion }, - { 1, U_MASK(UPROPS_GRAPHEME_BASE), defaultContains }, - { 1, U_MASK(UPROPS_GRAPHEME_EXTEND), defaultContains }, - { 1, U_MASK(UPROPS_GRAPHEME_LINK), defaultContains }, - { 1, U_MASK(UPROPS_HEX_DIGIT), defaultContains }, - { 1, U_MASK(UPROPS_HYPHEN), defaultContains }, - { 1, U_MASK(UPROPS_ID_CONTINUE), defaultContains }, - { 1, U_MASK(UPROPS_ID_START), defaultContains }, - { 1, U_MASK(UPROPS_IDEOGRAPHIC), defaultContains }, - { 1, U_MASK(UPROPS_IDS_BINARY_OPERATOR), defaultContains }, - { 1, U_MASK(UPROPS_IDS_TRINARY_OPERATOR), defaultContains }, - { UPROPS_SRC_BIDI, 0, isJoinControl }, - { 1, U_MASK(UPROPS_LOGICAL_ORDER_EXCEPTION), defaultContains }, - { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_LOWERCASE - { 1, U_MASK(UPROPS_MATH), defaultContains }, - { 1, U_MASK(UPROPS_NONCHARACTER_CODE_POINT), defaultContains }, - { 1, U_MASK(UPROPS_QUOTATION_MARK), defaultContains }, - { 1, U_MASK(UPROPS_RADICAL), defaultContains }, - { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_SOFT_DOTTED - { 1, U_MASK(UPROPS_TERMINAL_PUNCTUATION), defaultContains }, - { 1, U_MASK(UPROPS_UNIFIED_IDEOGRAPH), defaultContains }, - { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_UPPERCASE - { 1, U_MASK(UPROPS_WHITE_SPACE), defaultContains }, - { 1, U_MASK(UPROPS_XID_CONTINUE), defaultContains }, - { 1, U_MASK(UPROPS_XID_START), defaultContains }, - { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CASE_SENSITIVE - { 1, U_MASK(UPROPS_S_TERM), defaultContains }, - { 1, U_MASK(UPROPS_VARIATION_SELECTOR), defaultContains }, - { UPROPS_SRC_NFC, 0, isNormInert }, // UCHAR_NFD_INERT - { UPROPS_SRC_NFKC, 0, isNormInert }, // UCHAR_NFKD_INERT - { UPROPS_SRC_NFC, 0, isNormInert }, // UCHAR_NFC_INERT - { UPROPS_SRC_NFKC, 0, isNormInert }, // UCHAR_NFKC_INERT - { UPROPS_SRC_NFC_CANON_ITER, 0, isCanonSegmentStarter }, - { 1, U_MASK(UPROPS_PATTERN_SYNTAX), defaultContains }, - { 1, U_MASK(UPROPS_PATTERN_WHITE_SPACE), defaultContains }, - { UPROPS_SRC_CHAR_AND_PROPSVEC, 0, isPOSIX_alnum }, - { UPROPS_SRC_CHAR, 0, isPOSIX_blank }, - { UPROPS_SRC_CHAR, 0, isPOSIX_graph }, - { UPROPS_SRC_CHAR, 0, isPOSIX_print }, - { UPROPS_SRC_CHAR, 0, isPOSIX_xdigit }, - { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CASED - { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CASE_IGNORABLE - { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CHANGES_WHEN_LOWERCASED - { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CHANGES_WHEN_UPPERCASED - { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CHANGES_WHEN_TITLECASED - { UPROPS_SRC_CASE_AND_NORM, 0, changesWhenCasefolded }, - { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CHANGES_WHEN_CASEMAPPED - { UPROPS_SRC_NFKC_CF, 0, changesWhenNFKC_Casefolded }, - { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EMOJI - { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EMOJI_PRESENTATION - { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EMOJI_MODIFIER - { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EMOJI_MODIFIER_BASE - { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EMOJI_COMPONENT - { 2, 0, isRegionalIndicator }, - { 1, U_MASK(UPROPS_PREPENDED_CONCATENATION_MARK), defaultContains }, - { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EXTENDED_PICTOGRAPHIC - { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_BASIC_EMOJI - { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EMOJI_KEYCAP_SEQUENCE - { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_RGI_EMOJI_MODIFIER_SEQUENCE - { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_RGI_EMOJI_FLAG_SEQUENCE - { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_RGI_EMOJI_TAG_SEQUENCE - { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_RGI_EMOJI_ZWJ_SEQUENCE - { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_RGI_EMOJI -}; - -U_CAPI UBool U_EXPORT2 -u_hasBinaryProperty(UChar32 c, UProperty which) { - /* c is range-checked in the functions that are called from here */ - if(which 0 ? i == length : s[i] == 0) { - return u_hasBinaryProperty(c, which); // single code point - } - } - // Only call into EmojiProps for a relevant property, - // so that we not unnecessarily try to load its data file. - return UCHAR_BASIC_EMOJI <= which && which <= UCHAR_RGI_EMOJI && - EmojiProps::hasBinaryProperty(s, length, which); -} - -struct IntProperty; - -typedef int32_t IntPropertyGetValue(const IntProperty &prop, UChar32 c, UProperty which); -typedef int32_t IntPropertyGetMaxValue(const IntProperty &prop, UProperty which); - -struct IntProperty { - int32_t column; // SRC_PROPSVEC column, or "source" if mask==0 - uint32_t mask; - int32_t shift; // =maxValue if getMaxValueFromShift() is used - IntPropertyGetValue *getValue; - IntPropertyGetMaxValue *getMaxValue; -}; - -static int32_t defaultGetValue(const IntProperty &prop, UChar32 c, UProperty /*which*/) { - /* systematic, directly stored properties */ - return (int32_t)(u_getUnicodeProperties(c, prop.column)&prop.mask)>>prop.shift; -} - -static int32_t defaultGetMaxValue(const IntProperty &prop, UProperty /*which*/) { - return (uprv_getMaxValues(prop.column)&prop.mask)>>prop.shift; -} - -static int32_t getMaxValueFromShift(const IntProperty &prop, UProperty /*which*/) { - return prop.shift; -} - -static int32_t getBiDiClass(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return (int32_t)u_charDirection(c); -} - -static int32_t getBiDiPairedBracketType(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return (int32_t)ubidi_getPairedBracketType(c); -} - -static int32_t biDiGetMaxValue(const IntProperty &/*prop*/, UProperty which) { - return ubidi_getMaxValue(which); -} - -#if UCONFIG_NO_NORMALIZATION -static int32_t getCombiningClass(const IntProperty &, UChar32, UProperty) { - return 0; -} -#else -static int32_t getCombiningClass(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return u_getCombiningClass(c); -} -#endif - -static int32_t getGeneralCategory(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return (int32_t)u_charType(c); -} - -static int32_t getJoiningGroup(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return ubidi_getJoiningGroup(c); -} - -static int32_t getJoiningType(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return ubidi_getJoiningType(c); -} - -static int32_t getNumericType(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - int32_t ntv=(int32_t)GET_NUMERIC_TYPE_VALUE(u_getMainProperties(c)); - return UPROPS_NTV_GET_TYPE(ntv); -} - -static int32_t getScript(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - UErrorCode errorCode=U_ZERO_ERROR; - return (int32_t)uscript_getScript(c, &errorCode); -} - -static int32_t scriptGetMaxValue(const IntProperty &/*prop*/, UProperty /*which*/) { - uint32_t scriptX=uprv_getMaxValues(0)&UPROPS_SCRIPT_X_MASK; - return uprops_mergeScriptCodeOrIndex(scriptX); -} - -/* - * Map some of the Grapheme Cluster Break values to Hangul Syllable Types. - * Hangul_Syllable_Type is fully redundant with a subset of Grapheme_Cluster_Break. - */ -static const UHangulSyllableType gcbToHst[]={ - U_HST_NOT_APPLICABLE, /* U_GCB_OTHER */ - U_HST_NOT_APPLICABLE, /* U_GCB_CONTROL */ - U_HST_NOT_APPLICABLE, /* U_GCB_CR */ - U_HST_NOT_APPLICABLE, /* U_GCB_EXTEND */ - U_HST_LEADING_JAMO, /* U_GCB_L */ - U_HST_NOT_APPLICABLE, /* U_GCB_LF */ - U_HST_LV_SYLLABLE, /* U_GCB_LV */ - U_HST_LVT_SYLLABLE, /* U_GCB_LVT */ - U_HST_TRAILING_JAMO, /* U_GCB_T */ - U_HST_VOWEL_JAMO /* U_GCB_V */ - /* - * Omit GCB values beyond what we need for hst. - * The code below checks for the array length. - */ -}; - -static int32_t getHangulSyllableType(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - /* see comments on gcbToHst[] above */ - int32_t gcb=(int32_t)(u_getUnicodeProperties(c, 2)&UPROPS_GCB_MASK)>>UPROPS_GCB_SHIFT; - if(gcb>8; -} -#endif - -#if UCONFIG_NO_NORMALIZATION -static int32_t getTrailCombiningClass(const IntProperty &, UChar32, UProperty) { - return 0; -} -#else -static int32_t getTrailCombiningClass(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { - return unorm_getFCD16(c)&0xff; -} -#endif - -static int32_t getInPC(const IntProperty &, UChar32 c, UProperty) { - return ulayout_ensureData() && gInpcTrie != nullptr ? ucptrie_get(gInpcTrie, c) : 0; -} - -static int32_t getInSC(const IntProperty &, UChar32 c, UProperty) { - return ulayout_ensureData() && gInscTrie != nullptr ? ucptrie_get(gInscTrie, c) : 0; -} - -static int32_t getVo(const IntProperty &, UChar32 c, UProperty) { - return ulayout_ensureData() && gVoTrie != nullptr ? ucptrie_get(gVoTrie, c) : 0; -} - -static int32_t layoutGetMaxValue(const IntProperty &/*prop*/, UProperty which) { - if (!ulayout_ensureData()) { return 0; } - switch (which) { - case UCHAR_INDIC_POSITIONAL_CATEGORY: - return gMaxInpcValue; - case UCHAR_INDIC_SYLLABIC_CATEGORY: - return gMaxInscValue; - case UCHAR_VERTICAL_ORIENTATION: - return gMaxVoValue; - default: - return 0; - } -} - -static const IntProperty intProps[UCHAR_INT_LIMIT-UCHAR_INT_START]={ - /* - * column, mask and shift values for int-value properties from u_getUnicodeProperties(). - * Must be in order of corresponding UProperty, - * and there must be exactly one entry per int UProperty. - * - * Properties with mask==0 are handled in code. - * For them, column is the UPropertySource value. - */ - { UPROPS_SRC_BIDI, 0, 0, getBiDiClass, biDiGetMaxValue }, - { 0, UPROPS_BLOCK_MASK, UPROPS_BLOCK_SHIFT, defaultGetValue, defaultGetMaxValue }, - { UPROPS_SRC_NFC, 0, 0xff, getCombiningClass, getMaxValueFromShift }, - { 2, UPROPS_DT_MASK, 0, defaultGetValue, defaultGetMaxValue }, - { 0, UPROPS_EA_MASK, UPROPS_EA_SHIFT, defaultGetValue, defaultGetMaxValue }, - { UPROPS_SRC_CHAR, 0, (int32_t)U_CHAR_CATEGORY_COUNT-1,getGeneralCategory, getMaxValueFromShift }, - { UPROPS_SRC_BIDI, 0, 0, getJoiningGroup, biDiGetMaxValue }, - { UPROPS_SRC_BIDI, 0, 0, getJoiningType, biDiGetMaxValue }, - { 2, UPROPS_LB_MASK, UPROPS_LB_SHIFT, defaultGetValue, defaultGetMaxValue }, - { UPROPS_SRC_CHAR, 0, (int32_t)U_NT_COUNT-1, getNumericType, getMaxValueFromShift }, - { UPROPS_SRC_PROPSVEC, 0, 0, getScript, scriptGetMaxValue }, - { UPROPS_SRC_PROPSVEC, 0, (int32_t)U_HST_COUNT-1, getHangulSyllableType, getMaxValueFromShift }, - // UCHAR_NFD_QUICK_CHECK: max=1=YES -- never "maybe", only "no" or "yes" - { UPROPS_SRC_NFC, 0, (int32_t)UNORM_YES, getNormQuickCheck, getMaxValueFromShift }, - // UCHAR_NFKD_QUICK_CHECK: max=1=YES -- never "maybe", only "no" or "yes" - { UPROPS_SRC_NFKC, 0, (int32_t)UNORM_YES, getNormQuickCheck, getMaxValueFromShift }, - // UCHAR_NFC_QUICK_CHECK: max=2=MAYBE - { UPROPS_SRC_NFC, 0, (int32_t)UNORM_MAYBE, getNormQuickCheck, getMaxValueFromShift }, - // UCHAR_NFKC_QUICK_CHECK: max=2=MAYBE - { UPROPS_SRC_NFKC, 0, (int32_t)UNORM_MAYBE, getNormQuickCheck, getMaxValueFromShift }, - { UPROPS_SRC_NFC, 0, 0xff, getLeadCombiningClass, getMaxValueFromShift }, - { UPROPS_SRC_NFC, 0, 0xff, getTrailCombiningClass, getMaxValueFromShift }, - { 2, UPROPS_GCB_MASK, UPROPS_GCB_SHIFT, defaultGetValue, defaultGetMaxValue }, - { 2, UPROPS_SB_MASK, UPROPS_SB_SHIFT, defaultGetValue, defaultGetMaxValue }, - { 2, UPROPS_WB_MASK, UPROPS_WB_SHIFT, defaultGetValue, defaultGetMaxValue }, - { UPROPS_SRC_BIDI, 0, 0, getBiDiPairedBracketType, biDiGetMaxValue }, - { UPROPS_SRC_INPC, 0, 0, getInPC, layoutGetMaxValue }, - { UPROPS_SRC_INSC, 0, 0, getInSC, layoutGetMaxValue }, - { UPROPS_SRC_VO, 0, 0, getVo, layoutGetMaxValue }, -}; - -U_CAPI int32_t U_EXPORT2 -u_getIntPropertyValue(UChar32 c, UProperty which) { - if(which= 0) { - sa->add(sa->set, start); - start = end + 1; - } -} - -#if !UCONFIG_NO_NORMALIZATION - -U_CAPI int32_t U_EXPORT2 -u_getFC_NFKC_Closure(UChar32 c, UChar *dest, int32_t destCapacity, UErrorCode *pErrorCode) { - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(destCapacity<0 || (dest==NULL && destCapacity>0)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - // Compute the FC_NFKC_Closure on the fly: - // We have the API for complete coverage of Unicode properties, although - // this value by itself is not useful via API. - // (What could be useful is a custom normalization table that combines - // case folding and NFKC.) - // For the derivation, see Unicode's DerivedNormalizationProps.txt. - const Normalizer2 *nfkc=Normalizer2::getNFKCInstance(*pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return 0; - } - // first: b = NFKC(Fold(a)) - UnicodeString folded1String; - const UChar *folded1; - int32_t folded1Length=ucase_toFullFolding(c, &folded1, U_FOLD_CASE_DEFAULT); - if(folded1Length<0) { - const Normalizer2Impl *nfkcImpl=Normalizer2Factory::getImpl(nfkc); - if(nfkcImpl->getCompQuickCheck(nfkcImpl->getNorm16(c))!=UNORM_NO) { - return u_terminateUChars(dest, destCapacity, 0, pErrorCode); // c does not change at all under CaseFolding+NFKC - } - folded1String.setTo(c); - } else { - if(folded1Length>UCASE_MAX_STRING_LENGTH) { - folded1String.setTo(folded1Length); - } else { - folded1String.setTo(false, folded1, folded1Length); - } - } - UnicodeString kc1=nfkc->normalize(folded1String, *pErrorCode); - // second: c = NFKC(Fold(b)) - UnicodeString folded2String(kc1); - UnicodeString kc2=nfkc->normalize(folded2String.foldCase(), *pErrorCode); - // if (c != b) add the mapping from a to c - if(U_FAILURE(*pErrorCode) || kc1==kc2) { - return u_terminateUChars(dest, destCapacity, 0, pErrorCode); - } else { - return kc2.extract(dest, destCapacity, *pErrorCode); - } -} - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uprops.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002feb24 +* created by: Markus W. Scherer +* +* Implementations for mostly non-core Unicode character properties +* stored in uprops.icu. +* +* With the APIs implemented here, almost all properties files and +* their associated implementation files are used from this file, +* including those for normalization and case mappings. +*/ + +#include "unicode/utypes.h" +#include "unicode/uchar.h" +#include "unicode/ucptrie.h" +#include "unicode/udata.h" +#include "unicode/unorm2.h" +#include "unicode/uscript.h" +#include "unicode/ustring.h" +#include "unicode/utf16.h" +#include "cstring.h" +#include "emojiprops.h" +#include "mutex.h" +#include "normalizer2impl.h" +#include "umutex.h" +#include "ubidi_props.h" +#include "uprops.h" +#include "ucase.h" +#include "ucln_cmn.h" +#include "ulayout_props.h" +#include "ustr_imp.h" + +U_NAMESPACE_USE + +// Unicode text layout properties data ----------------------------------------- + +namespace { + +icu::UInitOnce gLayoutInitOnce {}; +UDataMemory *gLayoutMemory = nullptr; + +UCPTrie *gInpcTrie = nullptr; // Indic_Positional_Category +UCPTrie *gInscTrie = nullptr; // Indic_Syllabic_Category +UCPTrie *gVoTrie = nullptr; // Vertical_Orientation + +int32_t gMaxInpcValue = 0; +int32_t gMaxInscValue = 0; +int32_t gMaxVoValue = 0; + +UBool U_CALLCONV uprops_cleanup() { + udata_close(gLayoutMemory); + gLayoutMemory = nullptr; + + ucptrie_close(gInpcTrie); + gInpcTrie = nullptr; + ucptrie_close(gInscTrie); + gInscTrie = nullptr; + ucptrie_close(gVoTrie); + gVoTrie = nullptr; + + gMaxInpcValue = 0; + gMaxInscValue = 0; + gMaxVoValue = 0; + + gLayoutInitOnce.reset(); + return true; +} + +UBool U_CALLCONV +ulayout_isAcceptable(void * /*context*/, + const char * /* type */, const char * /*name*/, + const UDataInfo *pInfo) { + return pInfo->size >= 20 && + pInfo->isBigEndian == U_IS_BIG_ENDIAN && + pInfo->charsetFamily == U_CHARSET_FAMILY && + pInfo->dataFormat[0] == ULAYOUT_FMT_0 && + pInfo->dataFormat[1] == ULAYOUT_FMT_1 && + pInfo->dataFormat[2] == ULAYOUT_FMT_2 && + pInfo->dataFormat[3] == ULAYOUT_FMT_3 && + pInfo->formatVersion[0] == 1; +} + +// UInitOnce singleton initialization function +void U_CALLCONV ulayout_load(UErrorCode &errorCode) { + gLayoutMemory = udata_openChoice( + nullptr, ULAYOUT_DATA_TYPE, ULAYOUT_DATA_NAME, + ulayout_isAcceptable, nullptr, &errorCode); + if (U_FAILURE(errorCode)) { return; } + + const uint8_t *inBytes = (const uint8_t *)udata_getMemory(gLayoutMemory); + const int32_t *inIndexes = (const int32_t *)inBytes; + int32_t indexesLength = inIndexes[ULAYOUT_IX_INDEXES_LENGTH]; + if (indexesLength < 12) { + errorCode = U_INVALID_FORMAT_ERROR; // Not enough indexes. + return; + } + int32_t offset = indexesLength * 4; + int32_t top = inIndexes[ULAYOUT_IX_INPC_TRIE_TOP]; + int32_t trieSize = top - offset; + if (trieSize >= 16) { + gInpcTrie = ucptrie_openFromBinary( + UCPTRIE_TYPE_ANY, UCPTRIE_VALUE_BITS_ANY, + inBytes + offset, trieSize, nullptr, &errorCode); + } + offset = top; + top = inIndexes[ULAYOUT_IX_INSC_TRIE_TOP]; + trieSize = top - offset; + if (trieSize >= 16) { + gInscTrie = ucptrie_openFromBinary( + UCPTRIE_TYPE_ANY, UCPTRIE_VALUE_BITS_ANY, + inBytes + offset, trieSize, nullptr, &errorCode); + } + offset = top; + top = inIndexes[ULAYOUT_IX_VO_TRIE_TOP]; + trieSize = top - offset; + if (trieSize >= 16) { + gVoTrie = ucptrie_openFromBinary( + UCPTRIE_TYPE_ANY, UCPTRIE_VALUE_BITS_ANY, + inBytes + offset, trieSize, nullptr, &errorCode); + } + + uint32_t maxValues = inIndexes[ULAYOUT_IX_MAX_VALUES]; + gMaxInpcValue = maxValues >> ULAYOUT_MAX_INPC_SHIFT; + gMaxInscValue = (maxValues >> ULAYOUT_MAX_INSC_SHIFT) & 0xff; + gMaxVoValue = (maxValues >> ULAYOUT_MAX_VO_SHIFT) & 0xff; + + ucln_common_registerCleanup(UCLN_COMMON_UPROPS, uprops_cleanup); +} + +UBool ulayout_ensureData(UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return false; } + umtx_initOnce(gLayoutInitOnce, &ulayout_load, errorCode); + return U_SUCCESS(errorCode); +} + +UBool ulayout_ensureData() { + UErrorCode errorCode = U_ZERO_ERROR; + return ulayout_ensureData(errorCode); +} + +} // namespace + +/* general properties API functions ----------------------------------------- */ + +struct BinaryProperty; + +typedef UBool BinaryPropertyContains(const BinaryProperty &prop, UChar32 c, UProperty which); + +struct BinaryProperty { + int32_t column; // SRC_PROPSVEC column, or "source" if mask==0 + uint32_t mask; + BinaryPropertyContains *contains; +}; + +static UBool defaultContains(const BinaryProperty &prop, UChar32 c, UProperty /*which*/) { + /* systematic, directly stored properties */ + return (u_getUnicodeProperties(c, prop.column)&prop.mask)!=0; +} + +static UBool caseBinaryPropertyContains(const BinaryProperty &/*prop*/, UChar32 c, UProperty which) { + return static_cast(ucase_hasBinaryProperty(c, which)); +} + +static UBool isBidiControl(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return ubidi_isBidiControl(c); +} + +static UBool isMirrored(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return ubidi_isMirrored(c); +} + +static UBool isJoinControl(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return ubidi_isJoinControl(c); +} + +#if UCONFIG_NO_NORMALIZATION +static UBool hasFullCompositionExclusion(const BinaryProperty &, UChar32, UProperty) { + return false; +} +#else +static UBool hasFullCompositionExclusion(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + // By definition, Full_Composition_Exclusion is the same as NFC_QC=No. + UErrorCode errorCode=U_ZERO_ERROR; + const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(errorCode); + return U_SUCCESS(errorCode) && impl->isCompNo(impl->getNorm16(c)); +} +#endif + +// UCHAR_NF*_INERT properties +#if UCONFIG_NO_NORMALIZATION +static UBool isNormInert(const BinaryProperty &, UChar32, UProperty) { + return false; +} +#else +static UBool isNormInert(const BinaryProperty &/*prop*/, UChar32 c, UProperty which) { + UErrorCode errorCode=U_ZERO_ERROR; + const Normalizer2 *norm2=Normalizer2Factory::getInstance( + (UNormalizationMode)(which-UCHAR_NFD_INERT+UNORM_NFD), errorCode); + return U_SUCCESS(errorCode) && norm2->isInert(c); +} +#endif + +#if UCONFIG_NO_NORMALIZATION +static UBool changesWhenCasefolded(const BinaryProperty &, UChar32, UProperty) { + return false; +} +#else +static UBool changesWhenCasefolded(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + UnicodeString nfd; + UErrorCode errorCode=U_ZERO_ERROR; + const Normalizer2 *nfcNorm2=Normalizer2::getNFCInstance(errorCode); + if(U_FAILURE(errorCode)) { + return false; + } + if(nfcNorm2->getDecomposition(c, nfd)) { + /* c has a decomposition */ + if(nfd.length()==1) { + c=nfd[0]; /* single BMP code point */ + } else if(nfd.length()<=U16_MAX_LENGTH && + nfd.length()==U16_LENGTH(c=nfd.char32At(0)) + ) { + /* single supplementary code point */ + } else { + c=U_SENTINEL; + } + } else if(c<0) { + return false; /* protect against bad input */ + } + if(c>=0) { + /* single code point */ + const char16_t *resultString; + return (UBool)(ucase_toFullFolding(c, &resultString, U_FOLD_CASE_DEFAULT)>=0); + } else { + /* guess some large but stack-friendly capacity */ + char16_t dest[2*UCASE_MAX_STRING_LENGTH]; + int32_t destLength; + destLength=u_strFoldCase(dest, UPRV_LENGTHOF(dest), + nfd.getBuffer(), nfd.length(), + U_FOLD_CASE_DEFAULT, &errorCode); + return (UBool)(U_SUCCESS(errorCode) && + 0!=u_strCompare(nfd.getBuffer(), nfd.length(), + dest, destLength, false)); + } +} +#endif + +#if UCONFIG_NO_NORMALIZATION +static UBool changesWhenNFKC_Casefolded(const BinaryProperty &, UChar32, UProperty) { + return false; +} +#else +static UBool changesWhenNFKC_Casefolded(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + UErrorCode errorCode=U_ZERO_ERROR; + const Normalizer2Impl *kcf=Normalizer2Factory::getNFKC_CFImpl(errorCode); + if(U_FAILURE(errorCode)) { + return false; + } + UnicodeString src(c); + UnicodeString dest; + { + // The ReorderingBuffer must be in a block because its destructor + // needs to release dest's buffer before we look at its contents. + ReorderingBuffer buffer(*kcf, dest); + // Small destCapacity for NFKC_CF(c). + if(buffer.init(5, errorCode)) { + const char16_t *srcArray=src.getBuffer(); + kcf->compose(srcArray, srcArray+src.length(), false, + true, buffer, errorCode); + } + } + return U_SUCCESS(errorCode) && dest!=src; +} +#endif + +#if UCONFIG_NO_NORMALIZATION +static UBool isCanonSegmentStarter(const BinaryProperty &, UChar32, UProperty) { + return false; +} +#else +static UBool isCanonSegmentStarter(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + UErrorCode errorCode=U_ZERO_ERROR; + const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(errorCode); + return + U_SUCCESS(errorCode) && impl->ensureCanonIterData(errorCode) && + impl->isCanonSegmentStarter(c); +} +#endif + +static UBool isPOSIX_alnum(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return u_isalnumPOSIX(c); +} + +static UBool isPOSIX_blank(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return u_isblank(c); +} + +static UBool isPOSIX_graph(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return u_isgraphPOSIX(c); +} + +static UBool isPOSIX_print(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return u_isprintPOSIX(c); +} + +static UBool isPOSIX_xdigit(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return u_isxdigit(c); +} + +static UBool isRegionalIndicator(const BinaryProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + // Property starts are a subset of lb=RI etc. + return 0x1F1E6<=c && c<=0x1F1FF; +} + +static UBool hasEmojiProperty(const BinaryProperty &/*prop*/, UChar32 c, UProperty which) { + return EmojiProps::hasBinaryProperty(c, which); +} + +static const BinaryProperty binProps[UCHAR_BINARY_LIMIT]={ + /* + * column and mask values for binary properties from u_getUnicodeProperties(). + * Must be in order of corresponding UProperty, + * and there must be exactly one entry per binary UProperty. + * + * Properties with mask==0 are handled in code. + * For them, column is the UPropertySource value. + */ + { 1, U_MASK(UPROPS_ALPHABETIC), defaultContains }, + { 1, U_MASK(UPROPS_ASCII_HEX_DIGIT), defaultContains }, + { UPROPS_SRC_BIDI, 0, isBidiControl }, + { UPROPS_SRC_BIDI, 0, isMirrored }, + { 1, U_MASK(UPROPS_DASH), defaultContains }, + { 1, U_MASK(UPROPS_DEFAULT_IGNORABLE_CODE_POINT), defaultContains }, + { 1, U_MASK(UPROPS_DEPRECATED), defaultContains }, + { 1, U_MASK(UPROPS_DIACRITIC), defaultContains }, + { 1, U_MASK(UPROPS_EXTENDER), defaultContains }, + { UPROPS_SRC_NFC, 0, hasFullCompositionExclusion }, + { 1, U_MASK(UPROPS_GRAPHEME_BASE), defaultContains }, + { 1, U_MASK(UPROPS_GRAPHEME_EXTEND), defaultContains }, + { 1, U_MASK(UPROPS_GRAPHEME_LINK), defaultContains }, + { 1, U_MASK(UPROPS_HEX_DIGIT), defaultContains }, + { 1, U_MASK(UPROPS_HYPHEN), defaultContains }, + { 1, U_MASK(UPROPS_ID_CONTINUE), defaultContains }, + { 1, U_MASK(UPROPS_ID_START), defaultContains }, + { 1, U_MASK(UPROPS_IDEOGRAPHIC), defaultContains }, + { 1, U_MASK(UPROPS_IDS_BINARY_OPERATOR), defaultContains }, + { 1, U_MASK(UPROPS_IDS_TRINARY_OPERATOR), defaultContains }, + { UPROPS_SRC_BIDI, 0, isJoinControl }, + { 1, U_MASK(UPROPS_LOGICAL_ORDER_EXCEPTION), defaultContains }, + { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_LOWERCASE + { 1, U_MASK(UPROPS_MATH), defaultContains }, + { 1, U_MASK(UPROPS_NONCHARACTER_CODE_POINT), defaultContains }, + { 1, U_MASK(UPROPS_QUOTATION_MARK), defaultContains }, + { 1, U_MASK(UPROPS_RADICAL), defaultContains }, + { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_SOFT_DOTTED + { 1, U_MASK(UPROPS_TERMINAL_PUNCTUATION), defaultContains }, + { 1, U_MASK(UPROPS_UNIFIED_IDEOGRAPH), defaultContains }, + { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_UPPERCASE + { 1, U_MASK(UPROPS_WHITE_SPACE), defaultContains }, + { 1, U_MASK(UPROPS_XID_CONTINUE), defaultContains }, + { 1, U_MASK(UPROPS_XID_START), defaultContains }, + { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CASE_SENSITIVE + { 1, U_MASK(UPROPS_S_TERM), defaultContains }, + { 1, U_MASK(UPROPS_VARIATION_SELECTOR), defaultContains }, + { UPROPS_SRC_NFC, 0, isNormInert }, // UCHAR_NFD_INERT + { UPROPS_SRC_NFKC, 0, isNormInert }, // UCHAR_NFKD_INERT + { UPROPS_SRC_NFC, 0, isNormInert }, // UCHAR_NFC_INERT + { UPROPS_SRC_NFKC, 0, isNormInert }, // UCHAR_NFKC_INERT + { UPROPS_SRC_NFC_CANON_ITER, 0, isCanonSegmentStarter }, + { 1, U_MASK(UPROPS_PATTERN_SYNTAX), defaultContains }, + { 1, U_MASK(UPROPS_PATTERN_WHITE_SPACE), defaultContains }, + { UPROPS_SRC_CHAR_AND_PROPSVEC, 0, isPOSIX_alnum }, + { UPROPS_SRC_CHAR, 0, isPOSIX_blank }, + { UPROPS_SRC_CHAR, 0, isPOSIX_graph }, + { UPROPS_SRC_CHAR, 0, isPOSIX_print }, + { UPROPS_SRC_CHAR, 0, isPOSIX_xdigit }, + { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CASED + { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CASE_IGNORABLE + { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CHANGES_WHEN_LOWERCASED + { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CHANGES_WHEN_UPPERCASED + { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CHANGES_WHEN_TITLECASED + { UPROPS_SRC_CASE_AND_NORM, 0, changesWhenCasefolded }, + { UPROPS_SRC_CASE, 0, caseBinaryPropertyContains }, // UCHAR_CHANGES_WHEN_CASEMAPPED + { UPROPS_SRC_NFKC_CF, 0, changesWhenNFKC_Casefolded }, + { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EMOJI + { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EMOJI_PRESENTATION + { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EMOJI_MODIFIER + { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EMOJI_MODIFIER_BASE + { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EMOJI_COMPONENT + { 2, 0, isRegionalIndicator }, + { 1, U_MASK(UPROPS_PREPENDED_CONCATENATION_MARK), defaultContains }, + { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EXTENDED_PICTOGRAPHIC + { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_BASIC_EMOJI + { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_EMOJI_KEYCAP_SEQUENCE + { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_RGI_EMOJI_MODIFIER_SEQUENCE + { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_RGI_EMOJI_FLAG_SEQUENCE + { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_RGI_EMOJI_TAG_SEQUENCE + { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_RGI_EMOJI_ZWJ_SEQUENCE + { UPROPS_SRC_EMOJI, 0, hasEmojiProperty }, // UCHAR_RGI_EMOJI +}; + +U_CAPI UBool U_EXPORT2 +u_hasBinaryProperty(UChar32 c, UProperty which) { + /* c is range-checked in the functions that are called from here */ + if(which 0 ? i == length : s[i] == 0) { + return u_hasBinaryProperty(c, which); // single code point + } + } + // Only call into EmojiProps for a relevant property, + // so that we not unnecessarily try to load its data file. + return UCHAR_BASIC_EMOJI <= which && which <= UCHAR_RGI_EMOJI && + EmojiProps::hasBinaryProperty(s, length, which); +} + +struct IntProperty; + +typedef int32_t IntPropertyGetValue(const IntProperty &prop, UChar32 c, UProperty which); +typedef int32_t IntPropertyGetMaxValue(const IntProperty &prop, UProperty which); + +struct IntProperty { + int32_t column; // SRC_PROPSVEC column, or "source" if mask==0 + uint32_t mask; + int32_t shift; // =maxValue if getMaxValueFromShift() is used + IntPropertyGetValue *getValue; + IntPropertyGetMaxValue *getMaxValue; +}; + +static int32_t defaultGetValue(const IntProperty &prop, UChar32 c, UProperty /*which*/) { + /* systematic, directly stored properties */ + return (int32_t)(u_getUnicodeProperties(c, prop.column)&prop.mask)>>prop.shift; +} + +static int32_t defaultGetMaxValue(const IntProperty &prop, UProperty /*which*/) { + return (uprv_getMaxValues(prop.column)&prop.mask)>>prop.shift; +} + +static int32_t getMaxValueFromShift(const IntProperty &prop, UProperty /*which*/) { + return prop.shift; +} + +static int32_t getBiDiClass(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return (int32_t)u_charDirection(c); +} + +static int32_t getBiDiPairedBracketType(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return (int32_t)ubidi_getPairedBracketType(c); +} + +static int32_t biDiGetMaxValue(const IntProperty &/*prop*/, UProperty which) { + return ubidi_getMaxValue(which); +} + +#if UCONFIG_NO_NORMALIZATION +static int32_t getCombiningClass(const IntProperty &, UChar32, UProperty) { + return 0; +} +#else +static int32_t getCombiningClass(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return u_getCombiningClass(c); +} +#endif + +static int32_t getGeneralCategory(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return (int32_t)u_charType(c); +} + +static int32_t getJoiningGroup(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return ubidi_getJoiningGroup(c); +} + +static int32_t getJoiningType(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return ubidi_getJoiningType(c); +} + +static int32_t getNumericType(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + int32_t ntv=(int32_t)GET_NUMERIC_TYPE_VALUE(u_getMainProperties(c)); + return UPROPS_NTV_GET_TYPE(ntv); +} + +static int32_t getScript(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + UErrorCode errorCode=U_ZERO_ERROR; + return (int32_t)uscript_getScript(c, &errorCode); +} + +static int32_t scriptGetMaxValue(const IntProperty &/*prop*/, UProperty /*which*/) { + uint32_t scriptX=uprv_getMaxValues(0)&UPROPS_SCRIPT_X_MASK; + return uprops_mergeScriptCodeOrIndex(scriptX); +} + +/* + * Map some of the Grapheme Cluster Break values to Hangul Syllable Types. + * Hangul_Syllable_Type is fully redundant with a subset of Grapheme_Cluster_Break. + */ +static const UHangulSyllableType gcbToHst[]={ + U_HST_NOT_APPLICABLE, /* U_GCB_OTHER */ + U_HST_NOT_APPLICABLE, /* U_GCB_CONTROL */ + U_HST_NOT_APPLICABLE, /* U_GCB_CR */ + U_HST_NOT_APPLICABLE, /* U_GCB_EXTEND */ + U_HST_LEADING_JAMO, /* U_GCB_L */ + U_HST_NOT_APPLICABLE, /* U_GCB_LF */ + U_HST_LV_SYLLABLE, /* U_GCB_LV */ + U_HST_LVT_SYLLABLE, /* U_GCB_LVT */ + U_HST_TRAILING_JAMO, /* U_GCB_T */ + U_HST_VOWEL_JAMO /* U_GCB_V */ + /* + * Omit GCB values beyond what we need for hst. + * The code below checks for the array length. + */ +}; + +static int32_t getHangulSyllableType(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + /* see comments on gcbToHst[] above */ + int32_t gcb=(int32_t)(u_getUnicodeProperties(c, 2)&UPROPS_GCB_MASK)>>UPROPS_GCB_SHIFT; + if(gcb>8; +} +#endif + +#if UCONFIG_NO_NORMALIZATION +static int32_t getTrailCombiningClass(const IntProperty &, UChar32, UProperty) { + return 0; +} +#else +static int32_t getTrailCombiningClass(const IntProperty &/*prop*/, UChar32 c, UProperty /*which*/) { + return unorm_getFCD16(c)&0xff; +} +#endif + +static int32_t getInPC(const IntProperty &, UChar32 c, UProperty) { + return ulayout_ensureData() && gInpcTrie != nullptr ? ucptrie_get(gInpcTrie, c) : 0; +} + +static int32_t getInSC(const IntProperty &, UChar32 c, UProperty) { + return ulayout_ensureData() && gInscTrie != nullptr ? ucptrie_get(gInscTrie, c) : 0; +} + +static int32_t getVo(const IntProperty &, UChar32 c, UProperty) { + return ulayout_ensureData() && gVoTrie != nullptr ? ucptrie_get(gVoTrie, c) : 0; +} + +static int32_t layoutGetMaxValue(const IntProperty &/*prop*/, UProperty which) { + if (!ulayout_ensureData()) { return 0; } + switch (which) { + case UCHAR_INDIC_POSITIONAL_CATEGORY: + return gMaxInpcValue; + case UCHAR_INDIC_SYLLABIC_CATEGORY: + return gMaxInscValue; + case UCHAR_VERTICAL_ORIENTATION: + return gMaxVoValue; + default: + return 0; + } +} + +static const IntProperty intProps[UCHAR_INT_LIMIT-UCHAR_INT_START]={ + /* + * column, mask and shift values for int-value properties from u_getUnicodeProperties(). + * Must be in order of corresponding UProperty, + * and there must be exactly one entry per int UProperty. + * + * Properties with mask==0 are handled in code. + * For them, column is the UPropertySource value. + */ + { UPROPS_SRC_BIDI, 0, 0, getBiDiClass, biDiGetMaxValue }, + { 0, UPROPS_BLOCK_MASK, UPROPS_BLOCK_SHIFT, defaultGetValue, defaultGetMaxValue }, + { UPROPS_SRC_NFC, 0, 0xff, getCombiningClass, getMaxValueFromShift }, + { 2, UPROPS_DT_MASK, 0, defaultGetValue, defaultGetMaxValue }, + { 0, UPROPS_EA_MASK, UPROPS_EA_SHIFT, defaultGetValue, defaultGetMaxValue }, + { UPROPS_SRC_CHAR, 0, (int32_t)U_CHAR_CATEGORY_COUNT-1,getGeneralCategory, getMaxValueFromShift }, + { UPROPS_SRC_BIDI, 0, 0, getJoiningGroup, biDiGetMaxValue }, + { UPROPS_SRC_BIDI, 0, 0, getJoiningType, biDiGetMaxValue }, + { 2, UPROPS_LB_MASK, UPROPS_LB_SHIFT, defaultGetValue, defaultGetMaxValue }, + { UPROPS_SRC_CHAR, 0, (int32_t)U_NT_COUNT-1, getNumericType, getMaxValueFromShift }, + { UPROPS_SRC_PROPSVEC, 0, 0, getScript, scriptGetMaxValue }, + { UPROPS_SRC_PROPSVEC, 0, (int32_t)U_HST_COUNT-1, getHangulSyllableType, getMaxValueFromShift }, + // UCHAR_NFD_QUICK_CHECK: max=1=YES -- never "maybe", only "no" or "yes" + { UPROPS_SRC_NFC, 0, (int32_t)UNORM_YES, getNormQuickCheck, getMaxValueFromShift }, + // UCHAR_NFKD_QUICK_CHECK: max=1=YES -- never "maybe", only "no" or "yes" + { UPROPS_SRC_NFKC, 0, (int32_t)UNORM_YES, getNormQuickCheck, getMaxValueFromShift }, + // UCHAR_NFC_QUICK_CHECK: max=2=MAYBE + { UPROPS_SRC_NFC, 0, (int32_t)UNORM_MAYBE, getNormQuickCheck, getMaxValueFromShift }, + // UCHAR_NFKC_QUICK_CHECK: max=2=MAYBE + { UPROPS_SRC_NFKC, 0, (int32_t)UNORM_MAYBE, getNormQuickCheck, getMaxValueFromShift }, + { UPROPS_SRC_NFC, 0, 0xff, getLeadCombiningClass, getMaxValueFromShift }, + { UPROPS_SRC_NFC, 0, 0xff, getTrailCombiningClass, getMaxValueFromShift }, + { 2, UPROPS_GCB_MASK, UPROPS_GCB_SHIFT, defaultGetValue, defaultGetMaxValue }, + { 2, UPROPS_SB_MASK, UPROPS_SB_SHIFT, defaultGetValue, defaultGetMaxValue }, + { 2, UPROPS_WB_MASK, UPROPS_WB_SHIFT, defaultGetValue, defaultGetMaxValue }, + { UPROPS_SRC_BIDI, 0, 0, getBiDiPairedBracketType, biDiGetMaxValue }, + { UPROPS_SRC_INPC, 0, 0, getInPC, layoutGetMaxValue }, + { UPROPS_SRC_INSC, 0, 0, getInSC, layoutGetMaxValue }, + { UPROPS_SRC_VO, 0, 0, getVo, layoutGetMaxValue }, +}; + +U_CAPI int32_t U_EXPORT2 +u_getIntPropertyValue(UChar32 c, UProperty which) { + if(which= 0) { + sa->add(sa->set, start); + start = end + 1; + } +} + +#if !UCONFIG_NO_NORMALIZATION + +U_CAPI int32_t U_EXPORT2 +u_getFC_NFKC_Closure(UChar32 c, char16_t *dest, int32_t destCapacity, UErrorCode *pErrorCode) { + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(destCapacity<0 || (dest==nullptr && destCapacity>0)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + // Compute the FC_NFKC_Closure on the fly: + // We have the API for complete coverage of Unicode properties, although + // this value by itself is not useful via API. + // (What could be useful is a custom normalization table that combines + // case folding and NFKC.) + // For the derivation, see Unicode's DerivedNormalizationProps.txt. + const Normalizer2 *nfkc=Normalizer2::getNFKCInstance(*pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return 0; + } + // first: b = NFKC(Fold(a)) + UnicodeString folded1String; + const char16_t *folded1; + int32_t folded1Length=ucase_toFullFolding(c, &folded1, U_FOLD_CASE_DEFAULT); + if(folded1Length<0) { + const Normalizer2Impl *nfkcImpl=Normalizer2Factory::getImpl(nfkc); + if(nfkcImpl->getCompQuickCheck(nfkcImpl->getNorm16(c))!=UNORM_NO) { + return u_terminateUChars(dest, destCapacity, 0, pErrorCode); // c does not change at all under CaseFolding+NFKC + } + folded1String.setTo(c); + } else { + if(folded1Length>UCASE_MAX_STRING_LENGTH) { + folded1String.setTo(folded1Length); + } else { + folded1String.setTo(false, folded1, folded1Length); + } + } + UnicodeString kc1=nfkc->normalize(folded1String, *pErrorCode); + // second: c = NFKC(Fold(b)) + UnicodeString folded2String(kc1); + UnicodeString kc2=nfkc->normalize(folded2String.foldCase(), *pErrorCode); + // if (c != b) add the mapping from a to c + if(U_FAILURE(*pErrorCode) || kc1==kc2) { + return u_terminateUChars(dest, destCapacity, 0, pErrorCode); + } else { + return kc2.extract(dest, destCapacity, *pErrorCode); + } +} + +#endif diff --git a/deps/icu-small/source/common/uprops.h b/deps/icu-small/source/common/uprops.h index 2004394db64e1b..e2762c945e1879 100644 --- a/deps/icu-small/source/common/uprops.h +++ b/deps/icu-small/source/common/uprops.h @@ -1,454 +1,455 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uprops.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002feb24 -* created by: Markus W. Scherer -* -* Constants for mostly non-core Unicode character properties -* stored in uprops.icu. -*/ - -#ifndef __UPROPS_H__ -#define __UPROPS_H__ - -#include "unicode/utypes.h" -#include "unicode/uset.h" -#include "uset_imp.h" -#include "udataswp.h" - -/* indexes[] entries */ -enum { - UPROPS_PROPS32_INDEX, - UPROPS_EXCEPTIONS_INDEX, - UPROPS_EXCEPTIONS_TOP_INDEX, - - UPROPS_ADDITIONAL_TRIE_INDEX, - UPROPS_ADDITIONAL_VECTORS_INDEX, - UPROPS_ADDITIONAL_VECTORS_COLUMNS_INDEX, - - UPROPS_SCRIPT_EXTENSIONS_INDEX, - - UPROPS_RESERVED_INDEX_7, - UPROPS_RESERVED_INDEX_8, - - /* size of the data file (number of 32-bit units after the header) */ - UPROPS_DATA_TOP_INDEX, - - /* maximum values for code values in vector word 0 */ - UPROPS_MAX_VALUES_INDEX=10, - /* maximum values for code values in vector word 2 */ - UPROPS_MAX_VALUES_2_INDEX, - - UPROPS_INDEX_COUNT=16 -}; - -/* definitions for the main properties words */ -enum { - /* general category shift==0 0 (5 bits) */ - /* reserved 5 (1 bit) */ - UPROPS_NUMERIC_TYPE_VALUE_SHIFT=6 /* 6 (10 bits) */ -}; - -#define GET_CATEGORY(props) ((props)&0x1f) -#define CAT_MASK(props) U_MASK(GET_CATEGORY(props)) - -#define GET_NUMERIC_TYPE_VALUE(props) ((props)>>UPROPS_NUMERIC_TYPE_VALUE_SHIFT) - -/* constants for the storage form of numeric types and values */ -enum { - /** No numeric value. */ - UPROPS_NTV_NONE=0, - /** Decimal digits: nv=0..9 */ - UPROPS_NTV_DECIMAL_START=1, - /** Other digits: nv=0..9 */ - UPROPS_NTV_DIGIT_START=11, - /** Small integers: nv=0..154 */ - UPROPS_NTV_NUMERIC_START=21, - /** Fractions: ((ntv>>4)-12) / ((ntv&0xf)+1) = -1..17 / 1..16 */ - UPROPS_NTV_FRACTION_START=0xb0, - /** - * Large integers: - * ((ntv>>5)-14) * 10^((ntv&0x1f)+2) = (1..9)*(10^2..10^33) - * (only one significant decimal digit) - */ - UPROPS_NTV_LARGE_START=0x1e0, - /** - * Sexagesimal numbers: - * ((ntv>>2)-0xbf) * 60^((ntv&3)+1) = (1..9)*(60^1..60^4) - */ - UPROPS_NTV_BASE60_START=0x300, - /** - * Fraction-20 values: - * frac20 = ntv-0x324 = 0..0x17 -> 1|3|5|7 / 20|40|80|160|320|640 - * numerator: num = 2*(frac20&3)+1 - * denominator: den = 20<<(frac20>>2) - */ - UPROPS_NTV_FRACTION20_START=UPROPS_NTV_BASE60_START+36, // 0x300+9*4=0x324 - /** - * Fraction-32 values: - * frac32 = ntv-0x34c = 0..15 -> 1|3|5|7 / 32|64|128|256 - * numerator: num = 2*(frac32&3)+1 - * denominator: den = 32<<(frac32>>2) - */ - UPROPS_NTV_FRACTION32_START=UPROPS_NTV_FRACTION20_START+24, // 0x324+6*4=0x34c - /** No numeric value (yet). */ - UPROPS_NTV_RESERVED_START=UPROPS_NTV_FRACTION32_START+16, // 0x34c+4*4=0x35c - - UPROPS_NTV_MAX_SMALL_INT=UPROPS_NTV_FRACTION_START-UPROPS_NTV_NUMERIC_START-1 -}; - -#define UPROPS_NTV_GET_TYPE(ntv) \ - ((ntv==UPROPS_NTV_NONE) ? U_NT_NONE : \ - (ntv> UPROPS_SCRIPT_HIGH_SHIFT) | - (scriptX & UPROPS_SCRIPT_LOW_MASK); -} - -} // namespace - -#endif // __cplusplus - -/* - * Properties in vector word 1 - * Each bit encodes one binary property. - * The following constants represent the bit number, use 1<>UPROPS_NUMERIC_TYPE_VALUE_SHIFT) + +/* constants for the storage form of numeric types and values */ +enum { + /** No numeric value. */ + UPROPS_NTV_NONE=0, + /** Decimal digits: nv=0..9 */ + UPROPS_NTV_DECIMAL_START=1, + /** Other digits: nv=0..9 */ + UPROPS_NTV_DIGIT_START=11, + /** Small integers: nv=0..154 */ + UPROPS_NTV_NUMERIC_START=21, + /** Fractions: ((ntv>>4)-12) / ((ntv&0xf)+1) = -1..17 / 1..16 */ + UPROPS_NTV_FRACTION_START=0xb0, + /** + * Large integers: + * ((ntv>>5)-14) * 10^((ntv&0x1f)+2) = (1..9)*(10^2..10^33) + * (only one significant decimal digit) + */ + UPROPS_NTV_LARGE_START=0x1e0, + /** + * Sexagesimal numbers: + * ((ntv>>2)-0xbf) * 60^((ntv&3)+1) = (1..9)*(60^1..60^4) + */ + UPROPS_NTV_BASE60_START=0x300, + /** + * Fraction-20 values: + * frac20 = ntv-0x324 = 0..0x17 -> 1|3|5|7 / 20|40|80|160|320|640 + * numerator: num = 2*(frac20&3)+1 + * denominator: den = 20<<(frac20>>2) + */ + UPROPS_NTV_FRACTION20_START=UPROPS_NTV_BASE60_START+36, // 0x300+9*4=0x324 + /** + * Fraction-32 values: + * frac32 = ntv-0x34c = 0..15 -> 1|3|5|7 / 32|64|128|256 + * numerator: num = 2*(frac32&3)+1 + * denominator: den = 32<<(frac32>>2) + */ + UPROPS_NTV_FRACTION32_START=UPROPS_NTV_FRACTION20_START+24, // 0x324+6*4=0x34c + /** No numeric value (yet). */ + UPROPS_NTV_RESERVED_START=UPROPS_NTV_FRACTION32_START+16, // 0x34c+4*4=0x35c + + UPROPS_NTV_MAX_SMALL_INT=UPROPS_NTV_FRACTION_START-UPROPS_NTV_NUMERIC_START-1 +}; + +#define UPROPS_NTV_GET_TYPE(ntv) \ + ((ntv==UPROPS_NTV_NONE) ? U_NT_NONE : \ + (ntv> UPROPS_SCRIPT_HIGH_SHIFT) | + (scriptX & UPROPS_SCRIPT_LOW_MASK); +} + +} // namespace + +#endif // __cplusplus + +/* + * Properties in vector word 1 + * Each bit encodes one binary property. + * The following constants represent the bit number, use 1<=(int32_t)sizeof(pathBuffer)) { - *status=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } else if(uprv_isInvariantUString(myPath, length)) { - /* - * the invariant converter is sufficient for package and tree names - * and is more efficient - */ - u_UCharsToChars(myPath, path, length+1); /* length+1 to include the NUL */ - } else { -#if !UCONFIG_NO_CONVERSION - /* use the default converter to support variant-character paths */ - UConverter *cnv=u_getDefaultConverter(status); - length=ucnv_fromUChars(cnv, path, (int32_t)sizeof(pathBuffer), myPath, length, status); - u_releaseDefaultConverter(cnv); - if(U_FAILURE(*status)) { - return NULL; - } - if(length>=(int32_t)sizeof(pathBuffer)) { - /* not NUL-terminated - path too long */ - *status=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } -#else - /* the default converter is not available */ - *status=U_UNSUPPORTED_ERROR; - return NULL; -#endif - } - } - - return ures_open(path, localeID, status); -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1997-2006, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: ures_cnv.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004aug25 +* created by: Markus W. Scherer +* +* Character conversion functions moved here from uresbund.c +*/ + +#include "unicode/utypes.h" +#include "unicode/putil.h" +#include "unicode/ustring.h" +#include "unicode/ucnv.h" +#include "unicode/ures.h" +#include "uinvchar.h" +#include "ustr_cnv.h" + +U_CAPI UResourceBundle * U_EXPORT2 +ures_openU(const char16_t *myPath, + const char *localeID, + UErrorCode *status) +{ + char pathBuffer[1024]; + int32_t length; + char *path = pathBuffer; + + if(status==nullptr || U_FAILURE(*status)) { + return nullptr; + } + if(myPath==nullptr) { + path = nullptr; + } + else { + length=u_strlen(myPath); + if(length>=(int32_t)sizeof(pathBuffer)) { + *status=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } else if(uprv_isInvariantUString(myPath, length)) { + /* + * the invariant converter is sufficient for package and tree names + * and is more efficient + */ + u_UCharsToChars(myPath, path, length+1); /* length+1 to include the NUL */ + } else { +#if !UCONFIG_NO_CONVERSION + /* use the default converter to support variant-character paths */ + UConverter *cnv=u_getDefaultConverter(status); + length=ucnv_fromUChars(cnv, path, (int32_t)sizeof(pathBuffer), myPath, length, status); + u_releaseDefaultConverter(cnv); + if(U_FAILURE(*status)) { + return nullptr; + } + if(length>=(int32_t)sizeof(pathBuffer)) { + /* not NUL-terminated - path too long */ + *status=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } +#else + /* the default converter is not available */ + *status=U_UNSUPPORTED_ERROR; + return nullptr; +#endif + } + } + + return ures_open(path, localeID, status); +} diff --git a/deps/icu-small/source/common/uresbund.cpp b/deps/icu-small/source/common/uresbund.cpp index 17c0177a05cb5e..8a4574005d68bf 100644 --- a/deps/icu-small/source/common/uresbund.cpp +++ b/deps/icu-small/source/common/uresbund.cpp @@ -1,3461 +1,3462 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 1997-2016, International Business Machines Corporation and -* others. All Rights Reserved. -****************************************************************************** -* -* File uresbund.cpp -* -* Modification History: -* -* Date Name Description -* 04/01/97 aliu Creation. -* 06/14/99 stephen Removed functions taking a filename suffix. -* 07/20/99 stephen Changed for UResourceBundle typedef'd to void* -* 11/09/99 weiv Added ures_getLocale() -* March 2000 weiv Total overhaul - using data in DLLs -* 06/20/2000 helena OS/400 port changes; mostly typecast. -* 06/24/02 weiv Added support for resource sharing -****************************************************************************** -*/ - -#include "unicode/ures.h" -#include "unicode/ustring.h" -#include "unicode/ucnv.h" -#include "charstr.h" -#include "uresimp.h" -#include "ustr_imp.h" -#include "cwchar.h" -#include "ucln_cmn.h" -#include "cmemory.h" -#include "cstring.h" -#include "mutex.h" -#include "uhash.h" -#include "unicode/uenum.h" -#include "uenumimp.h" -#include "ulocimp.h" -#include "umutex.h" -#include "putilimp.h" -#include "uassert.h" -#include "uresdata.h" - -using namespace icu; - -/* -Static cache for already opened resource bundles - mostly for keeping fallback info -TODO: This cache should probably be removed when the deprecated code is - completely removed. -*/ -static UHashtable *cache = NULL; -static icu::UInitOnce gCacheInitOnce {}; - -static UMutex resbMutex; - -/* INTERNAL: hashes an entry */ -static int32_t U_CALLCONV hashEntry(const UHashTok parm) { - UResourceDataEntry *b = (UResourceDataEntry *)parm.pointer; - UHashTok namekey, pathkey; - namekey.pointer = b->fName; - pathkey.pointer = b->fPath; - return uhash_hashChars(namekey)+37u*uhash_hashChars(pathkey); -} - -/* INTERNAL: compares two entries */ -static UBool U_CALLCONV compareEntries(const UHashTok p1, const UHashTok p2) { - UResourceDataEntry *b1 = (UResourceDataEntry *)p1.pointer; - UResourceDataEntry *b2 = (UResourceDataEntry *)p2.pointer; - UHashTok name1, name2, path1, path2; - name1.pointer = b1->fName; - name2.pointer = b2->fName; - path1.pointer = b1->fPath; - path2.pointer = b2->fPath; - return (UBool)(uhash_compareChars(name1, name2) && - uhash_compareChars(path1, path2)); -} - - -/** - * Internal function, gets parts of locale name according - * to the position of '_' character - */ -static UBool chopLocale(char *name) { - char *i = uprv_strrchr(name, '_'); - - if(i != NULL) { - *i = '\0'; - return true; - } - - return false; -} - -static UBool hasVariant(const char* localeID) { - UErrorCode err = U_ZERO_ERROR; - int32_t variantLength = uloc_getVariant(localeID, NULL, 0, &err); - return variantLength != 0; -} - -// This file contains the tables for doing locale fallback, which are generated -// by the CLDR-to-ICU process directly from the CLDR data. This file should only -// ever be included from here. -#define INCLUDED_FROM_URESBUND_CPP -#include "localefallback_data.h" - -static const char* performFallbackLookup(const char* key, - const char* keyStrs, - const char* valueStrs, - const int32_t* lookupTable, - int32_t lookupTableLength) { - const int32_t* bottom = lookupTable; - const int32_t* top = lookupTable + lookupTableLength; - - while (bottom < top) { - // Effectively, divide by 2 and round down to an even index - const int32_t* middle = bottom + (((top - bottom) / 4) * 2); - const char* entryKey = &(keyStrs[*middle]); - int32_t strcmpResult = uprv_strcmp(key, entryKey); - if (strcmpResult == 0) { - return &(valueStrs[middle[1]]); - } else if (strcmpResult < 0) { - top = middle; - } else { - bottom = middle + 2; - } - } - return nullptr; -} - -static CharString getDefaultScript(const CharString& language, const CharString& region) { - const char* defaultScript = nullptr; - UErrorCode err = U_ZERO_ERROR; - - // the default script will be "Latn" if we don't find the locale ID in the tables - CharString result("Latn", err); - - // if we were passed both language and region, make them into a locale ID and look that up in the default - // script table - if (!region.isEmpty()) { - CharString localeID; - localeID.append(language, err).append("_", err).append(region, err); - if (U_FAILURE(err)) { - return result; - } - defaultScript = performFallbackLookup(localeID.data(), dsLocaleIDChars, scriptCodeChars, defaultScriptTable, UPRV_LENGTHOF(defaultScriptTable)); - } - - // if we didn't find anything, look up just the language in the default script table - if (defaultScript == nullptr) { - defaultScript = performFallbackLookup(language.data(), dsLocaleIDChars, scriptCodeChars, defaultScriptTable, UPRV_LENGTHOF(defaultScriptTable)); - } - - // if either lookup above succeeded, copy the result from "defaultScript" into "result"; otherwise, return "Latn" - if (defaultScript != nullptr) { - result.clear(); - result.append(defaultScript, err); - } - return result; -} - -enum UResOpenType { - /** - * Open a resource bundle for the locale; - * if there is not even a base language bundle, then fall back to the default locale; - * if there is no bundle for that either, then load the root bundle. - * - * This is the default bundle loading behavior. - */ - URES_OPEN_LOCALE_DEFAULT_ROOT, - // TODO: ICU ticket #11271 "consistent default locale across locale trees" - // Add an option to look at the main locale tree for whether to - // fall back to root directly (if the locale has main data) or - // fall back to the default locale first (if the locale does not even have main data). - /** - * Open a resource bundle for the locale; - * if there is not even a base language bundle, then load the root bundle; - * never fall back to the default locale. - * - * This is used for algorithms that have good pan-Unicode default behavior, - * such as case mappings, collation, and segmentation (BreakIterator). - */ - URES_OPEN_LOCALE_ROOT, - /** - * Open a resource bundle for the exact bundle name as requested; - * no fallbacks, do not load parent bundles. - * - * This is used for supplemental (non-locale) data. - */ - URES_OPEN_DIRECT -}; -typedef enum UResOpenType UResOpenType; - -/** - * Internal function, determines the search path for resource bundle files. - * Currently, this function is used only by findFirstExisting() to help search for resource bundle files when a bundle for the specified - * locale doesn't exist. The code that supports inheritance of resources between existing resource bundle files continues to - * use chopLocale() below. - * @param name In-out parameter: On input, the locale ID to get a parent locale ID for (this is a locale's base name, without keywords); on output, the - * requested parent locale ID. - * @param origName The original locale ID the caller of findFirstExisting() requested. This is the same as `name` on the first call to this function, - * but as findFirstExisting() ascends the resource bundle's parent tree, this parameter will continue to be the original locale ID requested. - */ -static bool getParentLocaleID(char *name, const char *origName, UResOpenType openType) { - // early out if the locale ID has a variant code or ends with _ - if (name[uprv_strlen(name) - 1] == '_' || hasVariant(name)) { - return chopLocale(name); - } - - UErrorCode err = U_ZERO_ERROR; - const char* tempNamePtr = name; - CharString language = ulocimp_getLanguage(tempNamePtr, &tempNamePtr, err); - if (*tempNamePtr == '_') { - ++tempNamePtr; - } - CharString script = ulocimp_getScript(tempNamePtr, &tempNamePtr, err); - if (*tempNamePtr == '_') { - ++tempNamePtr; - } - CharString region = ulocimp_getCountry(tempNamePtr, &tempNamePtr, err); - CharString workingLocale; - if (U_FAILURE(err)) { - // hopefully this never happens... - return chopLocale(name); - } - - // if the open type is URES_OPEN_LOCALE_DEFAULT_ROOT, first look the locale ID up in the parent locale table; - // if that table specifies a parent for it, return that (we don't do this for the other open types-- if we're not - // falling back through the system default locale, we also want to do straight truncation fallback instead - // of looking things up in the parent locale table-- see https://www.unicode.org/reports/tr35/tr35.html#Parent_Locales: - // "Collation data, however, is an exception...") - if (openType == URES_OPEN_LOCALE_DEFAULT_ROOT) { - const char* parentID = performFallbackLookup(name, parentLocaleChars, parentLocaleChars, parentLocaleTable, UPRV_LENGTHOF(parentLocaleTable)); - if (parentID != NULL) { - uprv_strcpy(name, parentID); - return true; - } - } - - // if it's not in the parent locale table, figure out the fallback script algorithmically - // (see CLDR-15265 for an explanation of the algorithm) - if (!script.isEmpty() && !region.isEmpty()) { - // if "name" has both script and region, is the script the default script? - // - if so, remove it and keep the region - // - if not, remove the region and keep the script - if (getDefaultScript(language, region) == script.toStringPiece()) { - workingLocale.append(language, err).append("_", err).append(region, err); - } else { - workingLocale.append(language, err).append("_", err).append(script, err); - } - } else if (!region.isEmpty()) { - // if "name" has region but not script, did the original locale ID specify a script? - // - if yes, replace the region with the script from the original locale ID - // - if no, replace the region with the default script for that language and region - UErrorCode err = U_ZERO_ERROR; - tempNamePtr = origName; - CharString origNameLanguage = ulocimp_getLanguage(tempNamePtr, &tempNamePtr, err); - if (*tempNamePtr == '_') { - ++tempNamePtr; - } - CharString origNameScript = ulocimp_getScript(origName, nullptr, err); - if (!origNameScript.isEmpty()) { - workingLocale.append(language, err).append("_", err).append(origNameScript, err); - } else { - workingLocale.append(language, err).append("_", err).append(getDefaultScript(language, region), err); - } - } else if (!script.isEmpty()) { - // if "name" has script but not region (and our open type if URES_OPEN_LOCALE_DEFAULT_ROOT), is the script - // the default script for the language? - // - if so, remove it from the locale ID - // - if not, return false to continue up the chain - // (we don't do this for other open types for the same reason we don't look things up in the parent - // locale table for other open types-- see the reference to UTS #35 above) - if (openType != URES_OPEN_LOCALE_DEFAULT_ROOT || getDefaultScript(language, CharString()) == script.toStringPiece()) { - workingLocale.append(language, err); - } else { - return false; - } - } else { - // if "name" just contains a language code, return false so the calling code falls back to "root" - return false; - } - if (U_SUCCESS(err) && !workingLocale.isEmpty()) { - uprv_strcpy(name, workingLocale.data()); - return true; - } else { - return false; - } -} - -/** - * Called to check whether a name without '_' needs to be checked for a parent. - * Some code had assumed that locale IDs with '_' could not have a non-root parent. - * We may want a better way of doing this. - */ -static UBool mayHaveParent(char *name) { - return (name[0] != 0 && uprv_strstr("nb nn",name) != nullptr); -} - -/** - * Internal function - */ -static void entryIncrease(UResourceDataEntry *entry) { - Mutex lock(&resbMutex); - entry->fCountExisting++; - while(entry->fParent != NULL) { - entry = entry->fParent; - entry->fCountExisting++; - } -} - -/** - * Internal function. Tries to find a resource in given Resource - * Bundle, as well as in its parents - */ -static UResourceDataEntry *getFallbackData( - const UResourceBundle *resBundle, - const char **resTag, Resource *res, UErrorCode *status) { - UResourceDataEntry *dataEntry = resBundle->fData; - int32_t indexR = -1; - int32_t i = 0; - *res = RES_BOGUS; - if(dataEntry == nullptr) { - *status = U_MISSING_RESOURCE_ERROR; - return nullptr; - } - if(dataEntry->fBogus == U_ZERO_ERROR) { /* if this resource is real, */ - *res = res_getTableItemByKey(&(dataEntry->fData), dataEntry->fData.rootRes, &indexR, resTag); /* try to get data from there */ - i++; - } - if(resBundle->fHasFallback) { - // Otherwise, we'll look in parents. - while(*res == RES_BOGUS && dataEntry->fParent != nullptr) { - dataEntry = dataEntry->fParent; - if(dataEntry->fBogus == U_ZERO_ERROR) { - i++; - *res = res_getTableItemByKey(&(dataEntry->fData), dataEntry->fData.rootRes, &indexR, resTag); - } - } - } - - if(*res == RES_BOGUS) { - // If the resource is not found, we need to give an error. - *status = U_MISSING_RESOURCE_ERROR; - return nullptr; - } - // If the resource is found in parents, we need to adjust the error. - if(i>1) { - if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) { - *status = U_USING_DEFAULT_WARNING; - } else { - *status = U_USING_FALLBACK_WARNING; - } - } - return dataEntry; -} - -static void -free_entry(UResourceDataEntry *entry) { - UResourceDataEntry *alias; - res_unload(&(entry->fData)); - if(entry->fName != NULL && entry->fName != entry->fNameBuffer) { - uprv_free(entry->fName); - } - if(entry->fPath != NULL) { - uprv_free(entry->fPath); - } - if(entry->fPool != NULL) { - --entry->fPool->fCountExisting; - } - alias = entry->fAlias; - if(alias != NULL) { - while(alias->fAlias != NULL) { - alias = alias->fAlias; - } - --alias->fCountExisting; - } - uprv_free(entry); -} - -/* Works just like ucnv_flushCache() */ -static int32_t ures_flushCache() -{ - UResourceDataEntry *resB; - int32_t pos; - int32_t rbDeletedNum = 0; - const UHashElement *e; - UBool deletedMore; - - /*if shared data hasn't even been lazy evaluated yet - * return 0 - */ - Mutex lock(&resbMutex); - if (cache == NULL) { - return 0; - } - - do { - deletedMore = false; - /*creates an enumeration to iterate through every element in the table */ - pos = UHASH_FIRST; - while ((e = uhash_nextElement(cache, &pos)) != NULL) - { - resB = (UResourceDataEntry *) e->value.pointer; - /* Deletes only if reference counter == 0 - * Don't worry about the children of this node. - * Those will eventually get deleted too, if not already. - * Don't worry about the parents of this node. - * Those will eventually get deleted too, if not already. - */ - /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */ - /* some resource bundles are still open somewhere. */ - - if (resB->fCountExisting == 0) { - rbDeletedNum++; - deletedMore = true; - uhash_removeElement(cache, e); - free_entry(resB); - } - } - /* - * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting - * got decremented by free_entry(). - */ - } while(deletedMore); - - return rbDeletedNum; -} - -#ifdef URES_DEBUG -#include - -U_CAPI UBool U_EXPORT2 ures_dumpCacheContents(void) { - UBool cacheNotEmpty = false; - int32_t pos = UHASH_FIRST; - const UHashElement *e; - UResourceDataEntry *resB; - - Mutex lock(&resbMutex); - if (cache == NULL) { - fprintf(stderr,"%s:%d: RB Cache is NULL.\n", __FILE__, __LINE__); - return false; - } - - while ((e = uhash_nextElement(cache, &pos)) != NULL) { - cacheNotEmpty=true; - resB = (UResourceDataEntry *) e->value.pointer; - fprintf(stderr,"%s:%d: RB Cache: Entry @0x%p, refcount %d, name %s:%s. Pool 0x%p, alias 0x%p, parent 0x%p\n", - __FILE__, __LINE__, - (void*)resB, resB->fCountExisting, - resB->fName?resB->fName:"NULL", - resB->fPath?resB->fPath:"NULL", - (void*)resB->fPool, - (void*)resB->fAlias, - (void*)resB->fParent); - } - - fprintf(stderr,"%s:%d: RB Cache still contains %d items.\n", __FILE__, __LINE__, uhash_count(cache)); - return cacheNotEmpty; -} - -#endif - -static UBool U_CALLCONV ures_cleanup(void) -{ - if (cache != NULL) { - ures_flushCache(); - uhash_close(cache); - cache = NULL; - } - gCacheInitOnce.reset(); - return true; -} - -/** INTERNAL: Initializes the cache for resources */ -static void U_CALLCONV createCache(UErrorCode &status) { - U_ASSERT(cache == NULL); - cache = uhash_open(hashEntry, compareEntries, NULL, &status); - ucln_common_registerCleanup(UCLN_COMMON_URES, ures_cleanup); -} - -static void initCache(UErrorCode *status) { - umtx_initOnce(gCacheInitOnce, &createCache, *status); -} - -/** INTERNAL: sets the name (locale) of the resource bundle to given name */ - -static void setEntryName(UResourceDataEntry *res, const char *name, UErrorCode *status) { - int32_t len = (int32_t)uprv_strlen(name); - if(res->fName != NULL && res->fName != res->fNameBuffer) { - uprv_free(res->fName); - } - if (len < (int32_t)sizeof(res->fNameBuffer)) { - res->fName = res->fNameBuffer; - } - else { - res->fName = (char *)uprv_malloc(len+1); - } - if(res->fName == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - } else { - uprv_strcpy(res->fName, name); - } -} - -static UResourceDataEntry * -getPoolEntry(const char *path, UErrorCode *status); - -/** - * INTERNAL: Inits and opens an entry from a data DLL. - * CAUTION: resbMutex must be locked when calling this function. - */ -static UResourceDataEntry *init_entry(const char *localeID, const char *path, UErrorCode *status) { - UResourceDataEntry *r = NULL; - UResourceDataEntry find; - /*int32_t hashValue;*/ - const char *name; - char aliasName[100] = { 0 }; - int32_t aliasLen = 0; - /*UBool isAlias = false;*/ - /*UHashTok hashkey; */ - - if(U_FAILURE(*status)) { - return NULL; - } - - /* here we try to deduce the right locale name */ - if(localeID == NULL) { /* if localeID is NULL, we're trying to open default locale */ - name = uloc_getDefault(); - } else if(*localeID == 0) { /* if localeID is "" then we try to open root locale */ - name = kRootLocaleName; - } else { /* otherwise, we'll open what we're given */ - name = localeID; - } - - find.fName = (char *)name; - find.fPath = (char *)path; - - /* calculate the hash value of the entry */ - /*hashkey.pointer = (void *)&find;*/ - /*hashValue = hashEntry(hashkey);*/ - - /* check to see if we already have this entry */ - r = (UResourceDataEntry *)uhash_get(cache, &find); - if(r == NULL) { - /* if the entry is not yet in the hash table, we'll try to construct a new one */ - r = (UResourceDataEntry *) uprv_malloc(sizeof(UResourceDataEntry)); - if(r == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - uprv_memset(r, 0, sizeof(UResourceDataEntry)); - /*r->fHashKey = hashValue;*/ - - setEntryName(r, name, status); - if (U_FAILURE(*status)) { - uprv_free(r); - return NULL; - } - - if(path != NULL) { - r->fPath = (char *)uprv_strdup(path); - if(r->fPath == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - uprv_free(r); - return NULL; - } - } - - /* this is the actual loading */ - res_load(&(r->fData), r->fPath, r->fName, status); - - if (U_FAILURE(*status)) { - /* if we failed to load due to an out-of-memory error, exit early. */ - if (*status == U_MEMORY_ALLOCATION_ERROR) { - uprv_free(r); - return NULL; - } - /* we have no such entry in dll, so it will always use fallback */ - *status = U_USING_FALLBACK_WARNING; - r->fBogus = U_USING_FALLBACK_WARNING; - } else { /* if we have a regular entry */ - Resource aliasres; - if (r->fData.usesPoolBundle) { - r->fPool = getPoolEntry(r->fPath, status); - if (U_SUCCESS(*status)) { - const int32_t *poolIndexes = r->fPool->fData.pRoot + 1; - if(r->fData.pRoot[1 + URES_INDEX_POOL_CHECKSUM] == poolIndexes[URES_INDEX_POOL_CHECKSUM]) { - r->fData.poolBundleKeys = (const char *)(poolIndexes + (poolIndexes[URES_INDEX_LENGTH] & 0xff)); - r->fData.poolBundleStrings = r->fPool->fData.p16BitUnits; - } else { - r->fBogus = *status = U_INVALID_FORMAT_ERROR; - } - } else { - r->fBogus = *status; - } - } - if (U_SUCCESS(*status)) { - /* handle the alias by trying to get out the %%Alias tag.*/ - /* We'll try to get alias string from the bundle */ - aliasres = res_getResource(&(r->fData), "%%ALIAS"); - if (aliasres != RES_BOGUS) { - // No tracing: called during initial data loading - const UChar *alias = res_getStringNoTrace(&(r->fData), aliasres, &aliasLen); - if(alias != NULL && aliasLen > 0) { /* if there is actual alias - unload and load new data */ - u_UCharsToChars(alias, aliasName, aliasLen+1); - r->fAlias = init_entry(aliasName, path, status); - } - } - } - } - - { - UResourceDataEntry *oldR = NULL; - if((oldR = (UResourceDataEntry *)uhash_get(cache, r)) == NULL) { /* if the data is not cached */ - /* just insert it in the cache */ - UErrorCode cacheStatus = U_ZERO_ERROR; - uhash_put(cache, (void *)r, r, &cacheStatus); - if (U_FAILURE(cacheStatus)) { - *status = cacheStatus; - free_entry(r); - r = NULL; - } - } else { - /* somebody have already inserted it while we were working, discard newly opened data */ - /* Also, we could get here IF we opened an alias */ - free_entry(r); - r = oldR; - } - } - - } - if(r != NULL) { - /* return the real bundle */ - while(r->fAlias != NULL) { - r = r->fAlias; - } - r->fCountExisting++; /* we increase its reference count */ - /* if the resource has a warning */ - /* we don't want to overwrite a status with no error */ - if(r->fBogus != U_ZERO_ERROR && U_SUCCESS(*status)) { - *status = r->fBogus; /* set the returning status */ - } - } - return r; -} - -static UResourceDataEntry * -getPoolEntry(const char *path, UErrorCode *status) { - UResourceDataEntry *poolBundle = init_entry(kPoolBundleName, path, status); - if( U_SUCCESS(*status) && - (poolBundle == NULL || poolBundle->fBogus != U_ZERO_ERROR || !poolBundle->fData.isPoolBundle) - ) { - *status = U_INVALID_FORMAT_ERROR; - } - return poolBundle; -} - -/* INTERNAL: */ -/* CAUTION: resbMutex must be locked when calling this function! */ -static UResourceDataEntry * -findFirstExisting(const char* path, char* name, const char* defaultLocale, UResOpenType openType, - UBool *isRoot, UBool *foundParent, UBool *isDefault, UErrorCode* status) { - UResourceDataEntry *r = NULL; - UBool hasRealData = false; - *foundParent = true; /* we're starting with a fresh name */ - char origName[ULOC_FULLNAME_CAPACITY]; - - uprv_strcpy(origName, name); - while(*foundParent && !hasRealData) { - r = init_entry(name, path, status); - /* Null pointer test */ - if (U_FAILURE(*status)) { - return NULL; - } - *isDefault = (UBool)(uprv_strncmp(name, defaultLocale, uprv_strlen(name)) == 0); - hasRealData = (UBool)(r->fBogus == U_ZERO_ERROR); - if(!hasRealData) { - /* this entry is not real. We will discard it. */ - /* However, the parent line for this entry is */ - /* not to be used - as there might be parent */ - /* lines in cache from previous openings that */ - /* are not updated yet. */ - r->fCountExisting--; - /*entryCloseInt(r);*/ - r = NULL; - *status = U_USING_FALLBACK_WARNING; - } else { - uprv_strcpy(name, r->fName); /* this is needed for supporting aliases */ - } - - *isRoot = (UBool)(uprv_strcmp(name, kRootLocaleName) == 0); - - /*Fallback data stuff*/ - if (!hasRealData) { - *foundParent = getParentLocaleID(name, origName, openType); - } else { - // we've already found a real resource file; what we return to the caller is the parent - // locale ID for inheritance, which should come from chopLocale(), not getParentLocaleID() - *foundParent = chopLocale(name); - } - if (*foundParent && *name == '\0') { - uprv_strcpy(name, "und"); - } - } - return r; -} - -static void ures_setIsStackObject( UResourceBundle* resB, UBool state) { - if(state) { - resB->fMagic1 = 0; - resB->fMagic2 = 0; - } else { - resB->fMagic1 = MAGIC1; - resB->fMagic2 = MAGIC2; - } -} - -static UBool ures_isStackObject(const UResourceBundle* resB) { - return((resB->fMagic1 == MAGIC1 && resB->fMagic2 == MAGIC2)?false:true); -} - - -U_CFUNC void ures_initStackObject(UResourceBundle* resB) { - uprv_memset(resB, 0, sizeof(UResourceBundle)); - ures_setIsStackObject(resB, true); -} - -U_NAMESPACE_BEGIN - -StackUResourceBundle::StackUResourceBundle() { - ures_initStackObject(&bundle); -} - -StackUResourceBundle::~StackUResourceBundle() { - ures_close(&bundle); -} - -U_NAMESPACE_END - -static UBool // returns U_SUCCESS(*status) -loadParentsExceptRoot(UResourceDataEntry *&t1, - char name[], int32_t nameCapacity, - UBool usingUSRData, char usrDataPath[], UErrorCode *status) { - if (U_FAILURE(*status)) { return false; } - UBool checkParent = true; - while (checkParent && t1->fParent == NULL && !t1->fData.noFallback && - res_getResource(&t1->fData,"%%ParentIsRoot") == RES_BOGUS) { - Resource parentRes = res_getResource(&t1->fData, "%%Parent"); - if (parentRes != RES_BOGUS) { // An explicit parent was found. - int32_t parentLocaleLen = 0; - // No tracing: called during initial data loading - const UChar *parentLocaleName = res_getStringNoTrace(&(t1->fData), parentRes, &parentLocaleLen); - if(parentLocaleName != NULL && 0 < parentLocaleLen && parentLocaleLen < nameCapacity) { - u_UCharsToChars(parentLocaleName, name, parentLocaleLen + 1); - if (uprv_strcmp(name, kRootLocaleName) == 0) { - return true; - } - } - } - // Insert regular parents. - UErrorCode parentStatus = U_ZERO_ERROR; - UResourceDataEntry *t2 = init_entry(name, t1->fPath, &parentStatus); - if (U_FAILURE(parentStatus)) { - *status = parentStatus; - return false; - } - UResourceDataEntry *u2 = NULL; - UErrorCode usrStatus = U_ZERO_ERROR; - if (usingUSRData) { // This code inserts user override data into the inheritance chain. - u2 = init_entry(name, usrDataPath, &usrStatus); - // If we failed due to out-of-memory, report that to the caller and exit early. - if (usrStatus == U_MEMORY_ALLOCATION_ERROR) { - *status = usrStatus; - return false; - } - } - - if (usingUSRData && U_SUCCESS(usrStatus) && u2->fBogus == U_ZERO_ERROR) { - t1->fParent = u2; - u2->fParent = t2; - } else { - t1->fParent = t2; - if (usingUSRData) { - // The USR override data wasn't found, set it to be deleted. - u2->fCountExisting = 0; - } - } - t1 = t2; - checkParent = chopLocale(name) || mayHaveParent(name); - } - return true; -} - -static UBool // returns U_SUCCESS(*status) -insertRootBundle(UResourceDataEntry *&t1, UErrorCode *status) { - if (U_FAILURE(*status)) { return false; } - UErrorCode parentStatus = U_ZERO_ERROR; - UResourceDataEntry *t2 = init_entry(kRootLocaleName, t1->fPath, &parentStatus); - if (U_FAILURE(parentStatus)) { - *status = parentStatus; - return false; - } - t1->fParent = t2; - t1 = t2; - return true; -} - -static UResourceDataEntry *entryOpen(const char* path, const char* localeID, - UResOpenType openType, UErrorCode* status) { - U_ASSERT(openType != URES_OPEN_DIRECT); - UErrorCode intStatus = U_ZERO_ERROR; - UResourceDataEntry *r = NULL; - UResourceDataEntry *t1 = NULL; - UBool isDefault = false; - UBool isRoot = false; - UBool hasRealData = false; - UBool hasChopped = true; - UBool usingUSRData = U_USE_USRDATA && ( path == NULL || uprv_strncmp(path,U_ICUDATA_NAME,8) == 0); - - char name[ULOC_FULLNAME_CAPACITY]; - char usrDataPath[96]; - - initCache(status); - - if(U_FAILURE(*status)) { - return NULL; - } - - uprv_strncpy(name, localeID, sizeof(name) - 1); - name[sizeof(name) - 1] = 0; - - if ( usingUSRData ) { - if ( path == NULL ) { - uprv_strcpy(usrDataPath, U_USRDATA_NAME); - } else { - uprv_strncpy(usrDataPath, path, sizeof(usrDataPath) - 1); - usrDataPath[0] = 'u'; - usrDataPath[1] = 's'; - usrDataPath[2] = 'r'; - usrDataPath[sizeof(usrDataPath) - 1] = 0; - } - } - - // Note: We need to query the default locale *before* locking resbMutex. - const char *defaultLocale = uloc_getDefault(); - - Mutex lock(&resbMutex); // Lock resbMutex until the end of this function. - - /* We're going to skip all the locales that do not have any data */ - r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus); - - // If we failed due to out-of-memory, report the failure and exit early. - if (intStatus == U_MEMORY_ALLOCATION_ERROR) { - *status = intStatus; - goto finish; - } - - if(r != NULL) { /* if there is one real locale, we can look for parents. */ - t1 = r; - hasRealData = true; - if ( usingUSRData ) { /* This code inserts user override data into the inheritance chain */ - UErrorCode usrStatus = U_ZERO_ERROR; - UResourceDataEntry *u1 = init_entry(t1->fName, usrDataPath, &usrStatus); - // If we failed due to out-of-memory, report the failure and exit early. - if (intStatus == U_MEMORY_ALLOCATION_ERROR) { - *status = intStatus; - goto finish; - } - if ( u1 != NULL ) { - if(u1->fBogus == U_ZERO_ERROR) { - u1->fParent = t1; - r = u1; - } else { - /* the USR override data wasn't found, set it to be deleted */ - u1->fCountExisting = 0; - } - } - } - if ((hasChopped || mayHaveParent(name)) && !isRoot) { - if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) { - goto finish; - } - } - } - - /* we could have reached this point without having any real data */ - /* if that is the case, we need to chain in the default locale */ - if(r==NULL && openType == URES_OPEN_LOCALE_DEFAULT_ROOT && !isDefault && !isRoot) { - /* insert default locale */ - uprv_strcpy(name, defaultLocale); - r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus); - // If we failed due to out-of-memory, report the failure and exit early. - if (intStatus == U_MEMORY_ALLOCATION_ERROR) { - *status = intStatus; - goto finish; - } - intStatus = U_USING_DEFAULT_WARNING; - if(r != NULL) { /* the default locale exists */ - t1 = r; - hasRealData = true; - isDefault = true; - // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path? - if ((hasChopped || mayHaveParent(name)) && !isRoot) { - if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) { - goto finish; - } - } - } - } - - /* we could still have r == NULL at this point - maybe even default locale is not */ - /* present */ - if(r == NULL) { - uprv_strcpy(name, kRootLocaleName); - r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus); - // If we failed due to out-of-memory, report the failure and exit early. - if (intStatus == U_MEMORY_ALLOCATION_ERROR) { - *status = intStatus; - goto finish; - } - if(r != NULL) { - t1 = r; - intStatus = U_USING_DEFAULT_WARNING; - hasRealData = true; - } else { /* we don't even have the root locale */ - *status = U_MISSING_RESOURCE_ERROR; - goto finish; - } - } else if(!isRoot && uprv_strcmp(t1->fName, kRootLocaleName) != 0 && - t1->fParent == NULL && !r->fData.noFallback) { - if (!insertRootBundle(t1, status)) { - goto finish; - } - if(!hasRealData) { - r->fBogus = U_USING_DEFAULT_WARNING; - } - } - - // TODO: Does this ever loop? - while(r != NULL && !isRoot && t1->fParent != NULL) { - t1->fParent->fCountExisting++; - t1 = t1->fParent; - } - -finish: - if(U_SUCCESS(*status)) { - if(intStatus != U_ZERO_ERROR) { - *status = intStatus; - } - return r; - } else { - return NULL; - } -} - -/** - * Version of entryOpen() and findFirstExisting() for ures_openDirect(), - * with no fallbacks. - * Parent and root locale bundles are loaded if - * the requested bundle does not have the "nofallback" flag. - */ -static UResourceDataEntry * -entryOpenDirect(const char* path, const char* localeID, UErrorCode* status) { - initCache(status); - if(U_FAILURE(*status)) { - return NULL; - } - - // Note: We need to query the default locale *before* locking resbMutex. - // If the localeID is NULL, then we want to use the default locale. - if (localeID == NULL) { - localeID = uloc_getDefault(); - } else if (*localeID == 0) { - // If the localeID is "", then we want to use the root locale. - localeID = kRootLocaleName; - } - - Mutex lock(&resbMutex); - - // findFirstExisting() without fallbacks. - UResourceDataEntry *r = init_entry(localeID, path, status); - if(U_SUCCESS(*status)) { - if(r->fBogus != U_ZERO_ERROR) { - r->fCountExisting--; - r = NULL; - } - } else { - r = NULL; - } - - // Some code depends on the ures_openDirect() bundle to have a parent bundle chain, - // unless it is marked with "nofallback". - UResourceDataEntry *t1 = r; - if(r != NULL && uprv_strcmp(localeID, kRootLocaleName) != 0 && // not root - r->fParent == NULL && !r->fData.noFallback && - uprv_strlen(localeID) < ULOC_FULLNAME_CAPACITY) { - char name[ULOC_FULLNAME_CAPACITY]; - uprv_strcpy(name, localeID); - if(!chopLocale(name) || uprv_strcmp(name, kRootLocaleName) == 0 || - loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), false, NULL, status)) { - if(uprv_strcmp(t1->fName, kRootLocaleName) != 0 && t1->fParent == NULL) { - insertRootBundle(t1, status); - } - } - if(U_FAILURE(*status)) { - r = NULL; - } - } - - if(r != NULL) { - // TODO: Does this ever loop? - while(t1->fParent != NULL) { - t1->fParent->fCountExisting++; - t1 = t1->fParent; - } - } - return r; -} - -/** - * Functions to create and destroy resource bundles. - * CAUTION: resbMutex must be locked when calling this function. - */ -/* INTERNAL: */ -static void entryCloseInt(UResourceDataEntry *resB) { - UResourceDataEntry *p = resB; - - while(resB != NULL) { - p = resB->fParent; - resB->fCountExisting--; - - /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush - of the cache. */ -/* - if(resB->fCountExisting <= 0) { - uhash_remove(cache, resB); - if(resB->fBogus == U_ZERO_ERROR) { - res_unload(&(resB->fData)); - } - if(resB->fName != NULL) { - uprv_free(resB->fName); - } - if(resB->fPath != NULL) { - uprv_free(resB->fPath); - } - uprv_free(resB); - } -*/ - - resB = p; - } -} - -/** - * API: closes a resource bundle and cleans up. - */ - -static void entryClose(UResourceDataEntry *resB) { - Mutex lock(&resbMutex); - entryCloseInt(resB); -} - -/* -U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) { - if(resB->fResPath == NULL) { - resB->fResPath = resB->fResBuf; - *(resB->fResPath) = 0; - } - resB->fResPathLen = uprv_strlen(toAdd); - if(RES_BUFSIZE <= resB->fResPathLen+1) { - if(resB->fResPath == resB->fResBuf) { - resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char)); - } else { - resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char)); - } - } - uprv_strcpy(resB->fResPath, toAdd); -} -*/ -static void ures_appendResPath(UResourceBundle *resB, const char* toAdd, int32_t lenToAdd, UErrorCode *status) { - int32_t resPathLenOrig = resB->fResPathLen; - if(resB->fResPath == NULL) { - resB->fResPath = resB->fResBuf; - *(resB->fResPath) = 0; - resB->fResPathLen = 0; - } - resB->fResPathLen += lenToAdd; - if(RES_BUFSIZE <= resB->fResPathLen+1) { - if(resB->fResPath == resB->fResBuf) { - resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char)); - /* Check that memory was allocated correctly. */ - if (resB->fResPath == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_strcpy(resB->fResPath, resB->fResBuf); - } else { - char *temp = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char)); - /* Check that memory was reallocated correctly. */ - if (temp == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return; - } - resB->fResPath = temp; - } - } - uprv_strcpy(resB->fResPath + resPathLenOrig, toAdd); -} - -static void ures_freeResPath(UResourceBundle *resB) { - if (resB->fResPath && resB->fResPath != resB->fResBuf) { - uprv_free(resB->fResPath); - } - resB->fResPath = NULL; - resB->fResPathLen = 0; -} - -static void -ures_closeBundle(UResourceBundle* resB, UBool freeBundleObj) -{ - if(resB != NULL) { - if(resB->fData != NULL) { - entryClose(resB->fData); - } - if(resB->fVersion != NULL) { - uprv_free(resB->fVersion); - } - ures_freeResPath(resB); - - if(ures_isStackObject(resB) == false && freeBundleObj) { - uprv_free(resB); - } -#if 0 /*U_DEBUG*/ - else { - /* poison the data */ - uprv_memset(resB, -1, sizeof(UResourceBundle)); - } -#endif - } -} - -U_CAPI void U_EXPORT2 -ures_close(UResourceBundle* resB) -{ - ures_closeBundle(resB, true); -} - -namespace { - -UResourceBundle *init_resb_result( - UResourceDataEntry *dataEntry, Resource r, const char *key, int32_t idx, - UResourceDataEntry *validLocaleDataEntry, const char *containerResPath, - int32_t recursionDepth, - UResourceBundle *resB, UErrorCode *status); - -// TODO: Try to refactor further, so that we output a dataEntry + Resource + (optionally) resPath, -// rather than a UResourceBundle. -// May need to entryIncrease() the resulting dataEntry. -UResourceBundle *getAliasTargetAsResourceBundle( - const ResourceData &resData, Resource r, const char *key, int32_t idx, - UResourceDataEntry *validLocaleDataEntry, const char *containerResPath, - int32_t recursionDepth, - UResourceBundle *resB, UErrorCode *status) { - // TODO: When an error occurs: Should we return nullptr vs. resB? - if (U_FAILURE(*status)) { return resB; } - U_ASSERT(RES_GET_TYPE(r) == URES_ALIAS); - int32_t len = 0; - const UChar *alias = res_getAlias(&resData, r, &len); - if(len <= 0) { - // bad alias - *status = U_ILLEGAL_ARGUMENT_ERROR; - return resB; - } - - // Copy the UTF-16 alias string into an invariant-character string. - // - // We do this so that res_findResource() can modify the path, - // which allows us to remove redundant _res_findResource() variants - // in uresdata.c. - // res_findResource() now NUL-terminates each segment so that table keys - // can always be compared with strcmp() instead of strncmp(). - // Saves code there and simplifies testing and code coverage. - // - // markus 2003oct17 - CharString chAlias; - chAlias.appendInvariantChars(alias, len, *status); - if (U_FAILURE(*status)) { - return nullptr; - } - - // We have an alias, now let's cut it up. - const char *path = nullptr, *locale = nullptr, *keyPath = nullptr; - if(chAlias[0] == RES_PATH_SEPARATOR) { - // There is a path included. - char *chAliasData = chAlias.data(); - char *sep = chAliasData + 1; - path = sep; - sep = uprv_strchr(sep, RES_PATH_SEPARATOR); - if(sep != nullptr) { - *sep++ = 0; - } - if(uprv_strcmp(path, "LOCALE") == 0) { - // This is an XPath alias, starting with "/LOCALE/". - // It contains the path to a resource which should be looked up - // starting in the valid locale. - // TODO: Can/should we forbid a /LOCALE alias without key path? - // It seems weird to alias to the same path, just starting from the valid locale. - // That will often yield an infinite loop. - keyPath = sep; - // Read from the valid locale which we already have. - path = locale = nullptr; - } else { - if(uprv_strcmp(path, "ICUDATA") == 0) { /* want ICU data */ - path = nullptr; - } - if (sep == nullptr) { - // TODO: This ends up using the root bundle. Can/should we forbid this? - locale = ""; - } else { - locale = sep; - sep = uprv_strchr(sep, RES_PATH_SEPARATOR); - if(sep != nullptr) { - *sep++ = 0; - } - keyPath = sep; - } - } - } else { - // No path, start with a locale. - char *sep = chAlias.data(); - locale = sep; - sep = uprv_strchr(sep, RES_PATH_SEPARATOR); - if(sep != nullptr) { - *sep++ = 0; - } - keyPath = sep; - path = validLocaleDataEntry->fPath; - } - - // Got almost everything, let's try to open. - // First, open the bundle with real data. - LocalUResourceBundlePointer mainRes; - UResourceDataEntry *dataEntry; - if (locale == nullptr) { - // alias = /LOCALE/keyPath - // Read from the valid locale which we already have. - dataEntry = validLocaleDataEntry; - } else { - UErrorCode intStatus = U_ZERO_ERROR; - // TODO: Shouldn't we use ures_open() for locale data bundles (!noFallback)? - mainRes.adoptInstead(ures_openDirect(path, locale, &intStatus)); - if(U_FAILURE(intStatus)) { - // We failed to open the resource bundle we're aliasing to. - *status = intStatus; - return resB; - } - dataEntry = mainRes->fData; - } - - const char* temp = nullptr; - if(keyPath == nullptr) { - // No key path. This means that we are going to to use the corresponding resource from - // another bundle. - // TODO: Why the special code path? - // Why not put together a key path from containerResPath + key or idx, - // as a comment below suggests, and go into the regular code branch? - // First, we are going to get a corresponding container - // resource to the one we are searching. - r = dataEntry->fData.rootRes; - if(containerResPath) { - chAlias.clear().append(containerResPath, *status); - if (U_FAILURE(*status)) { - return nullptr; - } - char *aKey = chAlias.data(); - // TODO: should res_findResource() return a new dataEntry, too? - r = res_findResource(&dataEntry->fData, r, &aKey, &temp); - } - if(key) { - // We need to make keyPath from the containerResPath and - // current key, if there is a key associated. - chAlias.clear().append(key, *status); - if (U_FAILURE(*status)) { - return nullptr; - } - char *aKey = chAlias.data(); - r = res_findResource(&dataEntry->fData, r, &aKey, &temp); - } else if(idx != -1) { - // If there is no key, but there is an index, try to get by the index. - // Here we have either a table or an array, so get the element. - int32_t type = RES_GET_TYPE(r); - if(URES_IS_TABLE(type)) { - const char *aKey; - r = res_getTableItemByIndex(&dataEntry->fData, r, idx, &aKey); - } else { /* array */ - r = res_getArrayItem(&dataEntry->fData, r, idx); - } - } - if(r != RES_BOGUS) { - resB = init_resb_result( - dataEntry, r, temp, -1, validLocaleDataEntry, nullptr, recursionDepth+1, - resB, status); - } else { - *status = U_MISSING_RESOURCE_ERROR; - } - } else { - // This one is a bit trickier. - // We start finding keys, but after we resolve one alias, the path might continue. - // Consider: - // aliastest:alias { "testtypes/anotheralias/Sequence" } - // anotheralias:alias { "/ICUDATA/sh/CollationElements" } - // aliastest resource should finally have the sequence, not collation elements. - CharString pathBuf(keyPath, *status); - if (U_FAILURE(*status)) { - return nullptr; - } - char *myPath = pathBuf.data(); - containerResPath = nullptr; - // Now we have fallback following here. - for(;;) { - r = dataEntry->fData.rootRes; - // TODO: Move containerResPath = nullptr to here, - // consistent with restarting from the rootRes of another bundle?! - - // This loop handles 'found' resources over several levels. - while(*myPath && U_SUCCESS(*status)) { - r = res_findResource(&(dataEntry->fData), r, &myPath, &temp); - if(r == RES_BOGUS) { - // No resource found, we don't really want to look anymore on this level. - break; - } - // Found a resource, but it might be an indirection. - resB = init_resb_result( - dataEntry, r, temp, -1, - validLocaleDataEntry, containerResPath, recursionDepth+1, - resB, status); - if (U_FAILURE(*status)) { - break; - } - if (temp == nullptr || uprv_strcmp(keyPath, temp) != 0) { - // The call to init_resb_result() above will set resB->fKeyPath to be - // the same as resB->fKey, - // throwing away any additional path elements if we had them -- - // if the key path wasn't just a single resource ID, clear out - // the bundle's key path and re-set it to be equal to keyPath. - ures_freeResPath(resB); - ures_appendResPath(resB, keyPath, (int32_t)uprv_strlen(keyPath), status); - if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) { - ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status); - } - if (U_FAILURE(*status)) { - break; - } - } - r = resB->fRes; /* switch to a new resource, possibly a new tree */ - dataEntry = resB->fData; - containerResPath = resB->fResPath; - } - if (U_FAILURE(*status) || r != RES_BOGUS) { - break; - } - // Fall back to the parent bundle, if there is one. - dataEntry = dataEntry->fParent; - if (dataEntry == nullptr) { - *status = U_MISSING_RESOURCE_ERROR; - break; - } - // Copy the same keyPath again. - myPath = pathBuf.data(); - uprv_strcpy(myPath, keyPath); - } - } - if(mainRes.getAlias() == resB) { - mainRes.orphan(); - } - ResourceTracer(resB).maybeTrace("getalias"); - return resB; -} - -// Recursive function, should be called only by itself, by its simpler wrapper, -// or by getAliasTargetAsResourceBundle(). -UResourceBundle *init_resb_result( - UResourceDataEntry *dataEntry, Resource r, const char *key, int32_t idx, - UResourceDataEntry *validLocaleDataEntry, const char *containerResPath, - int32_t recursionDepth, - UResourceBundle *resB, UErrorCode *status) { - // TODO: When an error occurs: Should we return nullptr vs. resB? - if(status == NULL || U_FAILURE(*status)) { - return resB; - } - if (validLocaleDataEntry == nullptr) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - if(RES_GET_TYPE(r) == URES_ALIAS) { - // This is an alias, need to exchange with real data. - if(recursionDepth >= URES_MAX_ALIAS_LEVEL) { - *status = U_TOO_MANY_ALIASES_ERROR; - return resB; - } - return getAliasTargetAsResourceBundle( - dataEntry->fData, r, key, idx, - validLocaleDataEntry, containerResPath, recursionDepth, resB, status); - } - if(resB == NULL) { - resB = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle)); - if (resB == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - ures_setIsStackObject(resB, false); - resB->fResPath = NULL; - resB->fResPathLen = 0; - } else { - if(resB->fData != NULL) { - entryClose(resB->fData); - } - if(resB->fVersion != NULL) { - uprv_free(resB->fVersion); - } - /* - weiv: if stack object was passed in, it doesn't really need to be reinited, - since the purpose of initing is to remove stack junk. However, at this point - we would not do anything to an allocated object, so stack object should be - treated the same - */ - /* - if(ures_isStackObject(resB) != false) { - ures_initStackObject(resB); - } - */ - if(containerResPath != resB->fResPath) { - ures_freeResPath(resB); - } - } - resB->fData = dataEntry; - entryIncrease(resB->fData); - resB->fHasFallback = false; - resB->fIsTopLevel = false; - resB->fIndex = -1; - resB->fKey = key; - resB->fValidLocaleDataEntry = validLocaleDataEntry; - if(containerResPath != resB->fResPath) { - ures_appendResPath( - resB, containerResPath, static_cast(uprv_strlen(containerResPath)), status); - } - if(key != NULL) { - ures_appendResPath(resB, key, (int32_t)uprv_strlen(key), status); - if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) { - ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status); - } - } else if(idx >= 0) { - char buf[256]; - int32_t len = T_CString_integerToString(buf, idx, 10); - ures_appendResPath(resB, buf, len, status); - if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) { - ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status); - } - } - /* Make sure that Purify doesn't complain about uninitialized memory copies. */ - { - int32_t usedLen = ((resB->fResBuf == resB->fResPath) ? resB->fResPathLen : 0); - uprv_memset(resB->fResBuf + usedLen, 0, sizeof(resB->fResBuf) - usedLen); - } - - resB->fVersion = NULL; - resB->fRes = r; - resB->fSize = res_countArrayItems(&resB->getResData(), resB->fRes); - ResourceTracer(resB).trace("get"); - return resB; -} - -UResourceBundle *init_resb_result( - UResourceDataEntry *dataEntry, Resource r, const char *key, int32_t idx, - // validLocaleDataEntry + containerResPath - const UResourceBundle *container, - UResourceBundle *resB, UErrorCode *status) { - return init_resb_result( - dataEntry, r, key, idx, - container->fValidLocaleDataEntry, container->fResPath, 0, resB, status); -} - -} // namespace - -UResourceBundle *ures_copyResb(UResourceBundle *r, const UResourceBundle *original, UErrorCode *status) { - UBool isStackObject; - if(U_FAILURE(*status) || r == original) { - return r; - } - if(original != NULL) { - if(r == NULL) { - isStackObject = false; - r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle)); - /* test for NULL */ - if (r == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - } else { - isStackObject = ures_isStackObject(r); - ures_closeBundle(r, false); - } - uprv_memcpy(r, original, sizeof(UResourceBundle)); - r->fResPath = NULL; - r->fResPathLen = 0; - if(original->fResPath) { - ures_appendResPath(r, original->fResPath, original->fResPathLen, status); - } - ures_setIsStackObject(r, isStackObject); - if(r->fData != NULL) { - entryIncrease(r->fData); - } - } - return r; -} - -/** - * Functions to retrieve data from resource bundles. - */ - -U_CAPI const UChar* U_EXPORT2 ures_getString(const UResourceBundle* resB, int32_t* len, UErrorCode* status) { - const UChar *s; - if (status==NULL || U_FAILURE(*status)) { - return NULL; - } - if(resB == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - s = res_getString({resB}, &resB->getResData(), resB->fRes, len); - if (s == NULL) { - *status = U_RESOURCE_TYPE_MISMATCH; - } - return s; -} - -static const char * -ures_toUTF8String(const UChar *s16, int32_t length16, - char *dest, int32_t *pLength, - UBool forceCopy, - UErrorCode *status) { - int32_t capacity; - - if (U_FAILURE(*status)) { - return NULL; - } - if (pLength != NULL) { - capacity = *pLength; - } else { - capacity = 0; - } - if (capacity < 0 || (capacity > 0 && dest == NULL)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - if (length16 == 0) { - /* empty string, return as read-only pointer */ - if (pLength != NULL) { - *pLength = 0; - } - if (forceCopy) { - u_terminateChars(dest, capacity, 0, status); - return dest; - } else { - return ""; - } - } else { - /* We need to transform the string to the destination buffer. */ - if (capacity < length16) { - /* No chance for the string to fit. Pure preflighting. */ - return u_strToUTF8(NULL, 0, pLength, s16, length16, status); - } - if (!forceCopy && (length16 <= 0x2aaaaaaa)) { - /* - * We know the string will fit into dest because each UChar turns - * into at most three UTF-8 bytes. Fill the latter part of dest - * so that callers do not expect to use dest as a string pointer, - * hopefully leading to more robust code for when resource bundles - * may store UTF-8 natively. - * (In which case dest would not be used at all.) - * - * We do not do this if forceCopy=true because then the caller - * expects the string to start exactly at dest. - * - * The test above for <= 0x2aaaaaaa prevents overflows. - * The +1 is for the NUL terminator. - */ - int32_t maxLength = 3 * length16 + 1; - if (capacity > maxLength) { - dest += capacity - maxLength; - capacity = maxLength; - } - } - return u_strToUTF8(dest, capacity, pLength, s16, length16, status); - } -} - -U_CAPI const char * U_EXPORT2 -ures_getUTF8String(const UResourceBundle *resB, - char *dest, int32_t *pLength, - UBool forceCopy, - UErrorCode *status) { - int32_t length16; - const UChar *s16 = ures_getString(resB, &length16, status); - return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status); -} - -U_CAPI const uint8_t* U_EXPORT2 ures_getBinary(const UResourceBundle* resB, int32_t* len, - UErrorCode* status) { - const uint8_t *p; - if (status==NULL || U_FAILURE(*status)) { - return NULL; - } - if(resB == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - p = res_getBinary({resB}, &resB->getResData(), resB->fRes, len); - if (p == NULL) { - *status = U_RESOURCE_TYPE_MISMATCH; - } - return p; -} - -U_CAPI const int32_t* U_EXPORT2 ures_getIntVector(const UResourceBundle* resB, int32_t* len, - UErrorCode* status) { - const int32_t *p; - if (status==NULL || U_FAILURE(*status)) { - return NULL; - } - if(resB == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - p = res_getIntVector({resB}, &resB->getResData(), resB->fRes, len); - if (p == NULL) { - *status = U_RESOURCE_TYPE_MISMATCH; - } - return p; -} - -/* this function returns a signed integer */ -/* it performs sign extension */ -U_CAPI int32_t U_EXPORT2 ures_getInt(const UResourceBundle* resB, UErrorCode *status) { - if (status==NULL || U_FAILURE(*status)) { - return 0xffffffff; - } - if(resB == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0xffffffff; - } - if(RES_GET_TYPE(resB->fRes) != URES_INT) { - *status = U_RESOURCE_TYPE_MISMATCH; - return 0xffffffff; - } - return res_getInt({resB}, resB->fRes); -} - -U_CAPI uint32_t U_EXPORT2 ures_getUInt(const UResourceBundle* resB, UErrorCode *status) { - if (status==NULL || U_FAILURE(*status)) { - return 0xffffffff; - } - if(resB == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0xffffffff; - } - if(RES_GET_TYPE(resB->fRes) != URES_INT) { - *status = U_RESOURCE_TYPE_MISMATCH; - return 0xffffffff; - } - return res_getUInt({resB}, resB->fRes); -} - -U_CAPI UResType U_EXPORT2 ures_getType(const UResourceBundle *resB) { - if(resB == NULL) { - return URES_NONE; - } - return res_getPublicType(resB->fRes); -} - -U_CAPI const char * U_EXPORT2 ures_getKey(const UResourceBundle *resB) { - // - // TODO: Trace ures_getKey? I guess not usually. - // - // We usually get the key string to decide whether we want the value, or to - // make a key-value pair. Tracing the value should suffice. - // - // However, I believe we have some data (e.g., in res_index) where the key - // strings are the data. Tracing the enclosing table should suffice. - // - if(resB == NULL) { - return NULL; - } - return(resB->fKey); -} - -U_CAPI int32_t U_EXPORT2 ures_getSize(const UResourceBundle *resB) { - if(resB == NULL) { - return 0; - } - - return resB->fSize; -} - -static const UChar* ures_getStringWithAlias(const UResourceBundle *resB, Resource r, int32_t sIndex, int32_t *len, UErrorCode *status) { - if(RES_GET_TYPE(r) == URES_ALIAS) { - const UChar* result = 0; - UResourceBundle *tempRes = ures_getByIndex(resB, sIndex, NULL, status); - result = ures_getString(tempRes, len, status); - ures_close(tempRes); - return result; - } else { - return res_getString({resB, sIndex}, &resB->getResData(), r, len); - } -} - -U_CAPI void U_EXPORT2 ures_resetIterator(UResourceBundle *resB){ - if(resB == NULL) { - return; - } - resB->fIndex = -1; -} - -U_CAPI UBool U_EXPORT2 ures_hasNext(const UResourceBundle *resB) { - if(resB == NULL) { - return false; - } - return (UBool)(resB->fIndex < resB->fSize-1); -} - -U_CAPI const UChar* U_EXPORT2 ures_getNextString(UResourceBundle *resB, int32_t* len, const char ** key, UErrorCode *status) { - Resource r = RES_BOGUS; - - if (status==NULL || U_FAILURE(*status)) { - return NULL; - } - if(resB == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - if(resB->fIndex == resB->fSize-1) { - *status = U_INDEX_OUTOFBOUNDS_ERROR; - } else { - resB->fIndex++; - switch(RES_GET_TYPE(resB->fRes)) { - case URES_STRING: - case URES_STRING_V2: - return res_getString({resB}, &resB->getResData(), resB->fRes, len); - case URES_TABLE: - case URES_TABLE16: - case URES_TABLE32: - r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, resB->fIndex, key); - if(r == RES_BOGUS && resB->fHasFallback) { - /* TODO: do the fallback */ - } - return ures_getStringWithAlias(resB, r, resB->fIndex, len, status); - case URES_ARRAY: - case URES_ARRAY16: - r = res_getArrayItem(&resB->getResData(), resB->fRes, resB->fIndex); - if(r == RES_BOGUS && resB->fHasFallback) { - /* TODO: do the fallback */ - } - return ures_getStringWithAlias(resB, r, resB->fIndex, len, status); - case URES_ALIAS: - return ures_getStringWithAlias(resB, resB->fRes, resB->fIndex, len, status); - case URES_INT: - case URES_BINARY: - case URES_INT_VECTOR: - *status = U_RESOURCE_TYPE_MISMATCH; - U_FALLTHROUGH; - default: - return NULL; - } - } - - return NULL; -} - -U_CAPI UResourceBundle* U_EXPORT2 ures_getNextResource(UResourceBundle *resB, UResourceBundle *fillIn, UErrorCode *status) { - const char *key = NULL; - Resource r = RES_BOGUS; - - if (status==NULL || U_FAILURE(*status)) { - /*return NULL;*/ - return fillIn; - } - if(resB == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - /*return NULL;*/ - return fillIn; - } - - if(resB->fIndex == resB->fSize-1) { - *status = U_INDEX_OUTOFBOUNDS_ERROR; - /*return NULL;*/ - } else { - resB->fIndex++; - switch(RES_GET_TYPE(resB->fRes)) { - case URES_INT: - case URES_BINARY: - case URES_STRING: - case URES_STRING_V2: - case URES_INT_VECTOR: - return ures_copyResb(fillIn, resB, status); - case URES_TABLE: - case URES_TABLE16: - case URES_TABLE32: - r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, resB->fIndex, &key); - if(r == RES_BOGUS && resB->fHasFallback) { - /* TODO: do the fallback */ - } - return init_resb_result(resB->fData, r, key, resB->fIndex, resB, fillIn, status); - case URES_ARRAY: - case URES_ARRAY16: - r = res_getArrayItem(&resB->getResData(), resB->fRes, resB->fIndex); - if(r == RES_BOGUS && resB->fHasFallback) { - /* TODO: do the fallback */ - } - return init_resb_result(resB->fData, r, key, resB->fIndex, resB, fillIn, status); - default: - /*return NULL;*/ - return fillIn; - } - } - /*return NULL;*/ - return fillIn; -} - -U_CAPI UResourceBundle* U_EXPORT2 ures_getByIndex(const UResourceBundle *resB, int32_t indexR, UResourceBundle *fillIn, UErrorCode *status) { - const char* key = NULL; - Resource r = RES_BOGUS; - - if (status==NULL || U_FAILURE(*status)) { - /*return NULL;*/ - return fillIn; - } - if(resB == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - /*return NULL;*/ - return fillIn; - } - - if(indexR >= 0 && resB->fSize > indexR) { - switch(RES_GET_TYPE(resB->fRes)) { - case URES_INT: - case URES_BINARY: - case URES_STRING: - case URES_STRING_V2: - case URES_INT_VECTOR: - return ures_copyResb(fillIn, resB, status); - case URES_TABLE: - case URES_TABLE16: - case URES_TABLE32: - r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, indexR, &key); - if(r == RES_BOGUS && resB->fHasFallback) { - /* TODO: do the fallback */ - } - return init_resb_result(resB->fData, r, key, indexR, resB, fillIn, status); - case URES_ARRAY: - case URES_ARRAY16: - r = res_getArrayItem(&resB->getResData(), resB->fRes, indexR); - if(r == RES_BOGUS && resB->fHasFallback) { - /* TODO: do the fallback */ - } - return init_resb_result(resB->fData, r, key, indexR, resB, fillIn, status); - default: - /*return NULL;*/ - return fillIn; - } - } else { - *status = U_MISSING_RESOURCE_ERROR; - } - /*return NULL;*/ - return fillIn; -} - -U_CAPI const UChar* U_EXPORT2 ures_getStringByIndex(const UResourceBundle *resB, int32_t indexS, int32_t* len, UErrorCode *status) { - const char* key = NULL; - Resource r = RES_BOGUS; - - if (status==NULL || U_FAILURE(*status)) { - return NULL; - } - if(resB == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - if(indexS >= 0 && resB->fSize > indexS) { - switch(RES_GET_TYPE(resB->fRes)) { - case URES_STRING: - case URES_STRING_V2: - return res_getString({resB}, &resB->getResData(), resB->fRes, len); - case URES_TABLE: - case URES_TABLE16: - case URES_TABLE32: - r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, indexS, &key); - if(r == RES_BOGUS && resB->fHasFallback) { - /* TODO: do the fallback */ - } - return ures_getStringWithAlias(resB, r, indexS, len, status); - case URES_ARRAY: - case URES_ARRAY16: - r = res_getArrayItem(&resB->getResData(), resB->fRes, indexS); - if(r == RES_BOGUS && resB->fHasFallback) { - /* TODO: do the fallback */ - } - return ures_getStringWithAlias(resB, r, indexS, len, status); - case URES_ALIAS: - return ures_getStringWithAlias(resB, resB->fRes, indexS, len, status); - case URES_INT: - case URES_BINARY: - case URES_INT_VECTOR: - *status = U_RESOURCE_TYPE_MISMATCH; - break; - default: - /* must not occur */ - *status = U_INTERNAL_PROGRAM_ERROR; - break; - } - } else { - *status = U_MISSING_RESOURCE_ERROR; - } - return NULL; -} - -U_CAPI const char * U_EXPORT2 -ures_getUTF8StringByIndex(const UResourceBundle *resB, - int32_t idx, - char *dest, int32_t *pLength, - UBool forceCopy, - UErrorCode *status) { - int32_t length16; - const UChar *s16 = ures_getStringByIndex(resB, idx, &length16, status); - return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status); -} - -/*U_CAPI const char *ures_getResPath(UResourceBundle *resB) { - return resB->fResPath; -}*/ - -U_CAPI UResourceBundle* U_EXPORT2 -ures_findResource(const char* path, UResourceBundle *fillIn, UErrorCode *status) -{ - UResourceBundle *first = NULL; - UResourceBundle *result = fillIn; - char *packageName = NULL; - char *pathToResource = NULL, *save = NULL; - char *locale = NULL, *localeEnd = NULL; - int32_t length; - - if(status == NULL || U_FAILURE(*status)) { - return result; - } - - length = (int32_t)(uprv_strlen(path)+1); - save = pathToResource = (char *)uprv_malloc(length*sizeof(char)); - /* test for NULL */ - if(pathToResource == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return result; - } - uprv_memcpy(pathToResource, path, length); - - locale = pathToResource; - if(*pathToResource == RES_PATH_SEPARATOR) { /* there is a path specification */ - pathToResource++; - packageName = pathToResource; - pathToResource = uprv_strchr(pathToResource, RES_PATH_SEPARATOR); - if(pathToResource == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - } else { - *pathToResource = 0; - locale = pathToResource+1; - } - } - - localeEnd = uprv_strchr(locale, RES_PATH_SEPARATOR); - if(localeEnd != NULL) { - *localeEnd = 0; - } - - first = ures_open(packageName, locale, status); - - if(U_SUCCESS(*status)) { - if(localeEnd) { - result = ures_findSubResource(first, localeEnd+1, fillIn, status); - } else { - result = ures_copyResb(fillIn, first, status); - } - ures_close(first); - } - uprv_free(save); - return result; -} - -U_CAPI UResourceBundle* U_EXPORT2 -ures_findSubResource(const UResourceBundle *resB, char* path, UResourceBundle *fillIn, UErrorCode *status) -{ - Resource res = RES_BOGUS; - UResourceBundle *result = fillIn; - const char *key; - - if(status == NULL || U_FAILURE(*status)) { - return result; - } - - /* here we do looping and circular alias checking */ - /* this loop is here because aliasing is resolved on this level, not on res level */ - /* so, when we encounter an alias, it is not an aggregate resource, so we return */ - do { - res = res_findResource(&resB->getResData(), resB->fRes, &path, &key); - if(res != RES_BOGUS) { - result = init_resb_result(resB->fData, res, key, -1, resB, fillIn, status); - resB = result; - } else { - *status = U_MISSING_RESOURCE_ERROR; - break; - } - } while(*path); /* there is more stuff in the path */ - - return result; -} -U_CAPI const UChar* U_EXPORT2 -ures_getStringByKeyWithFallback(const UResourceBundle *resB, - const char* inKey, - int32_t* len, - UErrorCode *status) { - - UResourceBundle stack; - const UChar* retVal = NULL; - ures_initStackObject(&stack); - ures_getByKeyWithFallback(resB, inKey, &stack, status); - int32_t length; - retVal = ures_getString(&stack, &length, status); - ures_close(&stack); - if (U_FAILURE(*status)) { - return NULL; - } - if (length == 3 && retVal[0] == EMPTY_SET && retVal[1] == EMPTY_SET && retVal[2] == EMPTY_SET ) { - retVal = NULL; - length = 0; - *status = U_MISSING_RESOURCE_ERROR; - } - if (len != NULL) { - *len = length; - } - return retVal; -} - -/* - Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort". -*/ -static Resource getTableItemByKeyPath(const ResourceData *pResData, Resource table, const char *key) { - Resource resource = table; /* The current resource */ - icu::CharString path; - UErrorCode errorCode = U_ZERO_ERROR; - path.append(key, errorCode); - if (U_FAILURE(errorCode)) { return RES_BOGUS; } - char *pathPart = path.data(); /* Path from current resource to desired resource */ - UResType type = (UResType)RES_GET_TYPE(resource); /* the current resource type */ - while (*pathPart && resource != RES_BOGUS && URES_IS_CONTAINER(type)) { - char *nextPathPart = uprv_strchr(pathPart, RES_PATH_SEPARATOR); - if (nextPathPart != NULL) { - *nextPathPart = 0; /* Terminating null for this part of path. */ - nextPathPart++; - } else { - nextPathPart = uprv_strchr(pathPart, 0); - } - int32_t t; - const char *pathP = pathPart; - resource = res_getTableItemByKey(pResData, resource, &t, &pathP); - type = (UResType)RES_GET_TYPE(resource); - pathPart = nextPathPart; - } - if (*pathPart) { - return RES_BOGUS; - } - return resource; -} - -static void createPath(const char* origResPath, - int32_t origResPathLen, - const char* resPath, - int32_t resPathLen, - const char* inKey, - CharString& path, - UErrorCode* status) { - // This is a utility function used by ures_getByKeyWithFallback() below. This function builds a path from - // resPath and inKey, returning the result in `path`. Originally, this function just cleared `path` and - // appended resPath and inKey to it, but that caused problems for horizontal inheritance. - // - // In normal cases, resPath is the same as origResPath, but if ures_getByKeyWithFallback() has followed an - // alias, resPath may be different from origResPath. Not only may the existing path elements be different, - // but resPath may also have MORE path elements than origResPath did. If it does, those additional path - // elements SUPERSEDE the corresponding elements of inKey. So this code counts the number of elements in - // resPath and origResPath and, for each path element in resPath that doesn't have a counterpart in origResPath, - // deletes a path element from the beginning of inKey. The remainder of inKey is then appended to - // resPath to form the result. (We're not using uprv_strchr() here because resPath and origResPath may - // not be zero-terminated.) - path.clear(); - const char* key = inKey; - if (resPathLen > 0) { - path.append(resPath, resPathLen, *status); - if (U_SUCCESS(*status)) { - const char* resPathLimit = resPath + resPathLen; - const char* origResPathLimit = origResPath + origResPathLen; - const char* resPathPtr = resPath; - const char* origResPathPtr = origResPath; - - // Remove from the beginning of resPath the number of segments that are contained in origResPath. - // If origResPath has MORE segments than resPath, this will leave resPath as the empty string. - while (origResPathPtr < origResPathLimit && resPathPtr < resPathLimit) { - while (origResPathPtr < origResPathLimit && *origResPathPtr != RES_PATH_SEPARATOR) { - ++origResPathPtr; - } - if (origResPathPtr < origResPathLimit && *origResPathPtr == RES_PATH_SEPARATOR) { - ++origResPathPtr; - } - while (resPathPtr < resPathLimit && *resPathPtr != RES_PATH_SEPARATOR) { - ++resPathPtr; - } - if (resPathPtr < resPathLimit && *resPathPtr == RES_PATH_SEPARATOR) { - ++resPathPtr; - } - } - - // New remove from the beginning of `key` the number of segments remaining in resPath. - // If resPath has more segments than `key` does, `key` will end up empty. - while (resPathPtr < resPathLimit && *key != '\0') { - while (resPathPtr < resPathLimit && *resPathPtr != RES_PATH_SEPARATOR) { - ++resPathPtr; - } - if (resPathPtr < resPathLimit && *resPathPtr == RES_PATH_SEPARATOR) { - ++resPathPtr; - } - while (*key != '\0' && *key != RES_PATH_SEPARATOR) { - ++key; - } - if (*key == RES_PATH_SEPARATOR) { - ++key; - } - } - } - // Finally, append what's left of `key` to `path`. What you end up with here is `resPath`, plus - // any pieces of `key` that aren't superseded by `resPath`. - // Or, to put it another way, calculate <#-segments-in-key> - (<#-segments-in-resPath> - <#-segments-in-origResPath>), - // and append that many segments from the end of `key` to `resPath` to produce the result. - path.append(key, *status); - } else { - path.append(inKey, *status); - } -} - -U_CAPI UResourceBundle* U_EXPORT2 -ures_getByKeyWithFallback(const UResourceBundle *resB, - const char* inKey, - UResourceBundle *fillIn, - UErrorCode *status) { - Resource res = RES_BOGUS, rootRes = RES_BOGUS; - UResourceBundle *helper = NULL; - - if (status==NULL || U_FAILURE(*status)) { - return fillIn; - } - if(resB == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return fillIn; - } - - int32_t type = RES_GET_TYPE(resB->fRes); - if(URES_IS_TABLE(type)) { - const char* origResPath = resB->fResPath; - int32_t origResPathLen = resB->fResPathLen; - res = getTableItemByKeyPath(&resB->getResData(), resB->fRes, inKey); - const char* key = inKey; - bool didRootOnce = false; - if(res == RES_BOGUS) { - UResourceDataEntry *dataEntry = resB->fData; - CharString path; - char *myPath = NULL; - const char* resPath = resB->fResPath; - int32_t len = resB->fResPathLen; - while(res == RES_BOGUS && (dataEntry->fParent != NULL || !didRootOnce)) { /* Otherwise, we'll look in parents */ - if (dataEntry->fParent != NULL) { - dataEntry = dataEntry->fParent; - } else { - // We can't just stop when we get to a bundle whose fParent is NULL. That'll work most of the time, - // but if the bundle that the caller passed to us was "root" (which happens in getAllItemsWithFallback(), - // this function will drop right out without doing anything if "root" doesn't contain the exact key path - // specified. In that case, we need one extra time through this loop to make sure we follow any - // applicable aliases at the root level. - didRootOnce = true; - } - rootRes = dataEntry->fData.rootRes; - - if(dataEntry->fBogus == U_ZERO_ERROR) { - createPath(origResPath, origResPathLen, resPath, len, inKey, path, status); - if (U_FAILURE(*status)) { - ures_close(helper); - return fillIn; - } - myPath = path.data(); - key = inKey; - do { - res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key); - if (RES_GET_TYPE(res) == URES_ALIAS && *myPath) { - /* We hit an alias, but we didn't finish following the path. */ - helper = init_resb_result(dataEntry, res, NULL, -1, resB, helper, status); - /*helper = init_resb_result(dataEntry, res, inKey, -1, resB, helper, status);*/ - if(helper) { - dataEntry = helper->fData; - rootRes = helper->fRes; - resPath = helper->fResPath; - len = helper->fResPathLen; - - } else { - break; - } - } else if (res == RES_BOGUS) { - break; - } - } while(*myPath); /* Continue until the whole path is consumed */ - } - } - /*dataEntry = getFallbackData(resB, &key, &res, status);*/ - if(res != RES_BOGUS) { - /* check if resB->fResPath gives the right name here */ - if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) { - *status = U_USING_DEFAULT_WARNING; - } else { - *status = U_USING_FALLBACK_WARNING; - } - - fillIn = init_resb_result(dataEntry, res, key, -1, resB, fillIn, status); - if (resPath != nullptr) { - createPath(origResPath, origResPathLen, resPath, len, inKey, path, status); - } else { - const char* separator = nullptr; - if (fillIn->fResPath != nullptr) { - separator = uprv_strchr(fillIn->fResPath, RES_PATH_SEPARATOR); - } - if (separator != nullptr && separator[1] != '\0') { - createPath(origResPath, origResPathLen, fillIn->fResPath, - static_cast(uprv_strlen(fillIn->fResPath)), inKey, path, status); - } else { - createPath(origResPath, origResPathLen, "", 0, inKey, path, status); - } - } - ures_freeResPath(fillIn); - ures_appendResPath(fillIn, path.data(), path.length(), status); - if(fillIn->fResPath[fillIn->fResPathLen-1] != RES_PATH_SEPARATOR) { - ures_appendResPath(fillIn, RES_PATH_SEPARATOR_S, 1, status); - } - } else { - *status = U_MISSING_RESOURCE_ERROR; - } - } else { - fillIn = init_resb_result(resB->fData, res, key, -1, resB, fillIn, status); - } - } - else { - *status = U_RESOURCE_TYPE_MISMATCH; - } - ures_close(helper); - return fillIn; -} - -namespace { - -void getAllItemsWithFallback( - const UResourceBundle *bundle, ResourceDataValue &value, - ResourceSink &sink, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return; } - // We recursively enumerate child-first, - // only storing parent items in the absence of child items. - // The sink needs to store a placeholder value for the no-fallback/no-inheritance marker - // to prevent a parent item from being stored. - // - // It would be possible to recursively enumerate parent-first, - // overriding parent items with child items. - // When the sink sees the no-fallback/no-inheritance marker, - // then it would remove the parent's item. - // We would deserialize parent values even though they are overridden in a child bundle. - value.setData(bundle->getResData()); - value.setValidLocaleDataEntry(bundle->fValidLocaleDataEntry); - UResourceDataEntry *parentEntry = bundle->fData->fParent; - UBool hasParent = parentEntry != NULL && U_SUCCESS(parentEntry->fBogus); - value.setResource(bundle->fRes, ResourceTracer(bundle)); - sink.put(bundle->fKey, value, !hasParent, errorCode); - if (hasParent) { - // We might try to query the sink whether - // any fallback from the parent bundle is still possible. - - // Turn the parent UResourceDataEntry into a UResourceBundle, - // much like in ures_openWithType(). - // TODO: See if we can refactor ures_getByKeyWithFallback() - // and pull out an inner function that takes and returns a UResourceDataEntry - // so that we need not create UResourceBundle objects. - StackUResourceBundle parentBundle; - UResourceBundle &parentRef = parentBundle.ref(); - parentRef.fData = parentEntry; - parentRef.fValidLocaleDataEntry = bundle->fValidLocaleDataEntry; - parentRef.fHasFallback = !parentRef.getResData().noFallback; - parentRef.fIsTopLevel = true; - parentRef.fRes = parentRef.getResData().rootRes; - parentRef.fSize = res_countArrayItems(&parentRef.getResData(), parentRef.fRes); - parentRef.fIndex = -1; - entryIncrease(parentEntry); - - // Look up the container item in the parent bundle. - StackUResourceBundle containerBundle; - const UResourceBundle *rb; - UErrorCode pathErrorCode = U_ZERO_ERROR; // Ignore if parents up to root do not have this path. - if (bundle->fResPath == NULL || *bundle->fResPath == 0) { - rb = parentBundle.getAlias(); - } else { - rb = ures_getByKeyWithFallback(parentBundle.getAlias(), bundle->fResPath, - containerBundle.getAlias(), &pathErrorCode); - } - if (U_SUCCESS(pathErrorCode)) { - getAllItemsWithFallback(rb, value, sink, errorCode); - } - } -} - -struct GetAllChildrenSink : public ResourceSink { - // Destination sink - ResourceSink& dest; - - GetAllChildrenSink(ResourceSink& dest) - : dest(dest) {} - virtual ~GetAllChildrenSink() override; - virtual void put(const char *key, ResourceValue &value, UBool isRoot, - UErrorCode &errorCode) override { - ResourceTable itemsTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) { - if (value.getType() == URES_ALIAS) { - ResourceDataValue& rdv = static_cast(value); - StackUResourceBundle stackTempBundle; - UResourceBundle* aliasRB = getAliasTargetAsResourceBundle(rdv.getData(), rdv.getResource(), nullptr, -1, - rdv.getValidLocaleDataEntry(), nullptr, 0, - stackTempBundle.getAlias(), &errorCode); - if (U_SUCCESS(errorCode)) { - ResourceDataValue aliasedValue; - aliasedValue.setData(aliasRB->getResData()); - aliasedValue.setValidLocaleDataEntry(aliasRB->fValidLocaleDataEntry); - aliasedValue.setResource(aliasRB->fRes, ResourceTracer(aliasRB)); - dest.put(key, aliasedValue, isRoot, errorCode); - } - } else { - dest.put(key, value, isRoot, errorCode); - } - if (U_FAILURE(errorCode)) { return; } - } - } -}; - -// Virtual destructors must be defined out of line. -GetAllChildrenSink::~GetAllChildrenSink() {} - -U_CAPI void U_EXPORT2 -ures_getAllChildrenWithFallback(const UResourceBundle *bundle, const char *path, - icu::ResourceSink &sink, UErrorCode &errorCode) { - GetAllChildrenSink allChildrenSink(sink); - ures_getAllItemsWithFallback(bundle, path, allChildrenSink, errorCode); -} - -} // namespace - -// Requires a ResourceDataValue fill-in, so that we need not cast from a ResourceValue. -// Unfortunately, the caller must know which subclass to make and pass in. -// Alternatively, we could make it as polymorphic as in Java by -// returning a ResourceValue pointer (possibly wrapped into a LocalPointer) -// that the caller then owns. -// -// Also requires a UResourceBundle fill-in, so that the value's ResourceTracer -// can point to a non-local bundle. -// Without tracing, the child bundle could be a function-local object. -U_CAPI void U_EXPORT2 -ures_getValueWithFallback(const UResourceBundle *bundle, const char *path, - UResourceBundle *tempFillIn, - ResourceDataValue &value, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return; } - if (path == nullptr) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - const UResourceBundle *rb; - if (*path == 0) { - // empty path - rb = bundle; - } else { - rb = ures_getByKeyWithFallback(bundle, path, tempFillIn, &errorCode); - if (U_FAILURE(errorCode)) { - return; - } - } - value.setData(rb->getResData()); - value.setValidLocaleDataEntry(rb->fValidLocaleDataEntry); - value.setResource(rb->fRes, ResourceTracer(rb)); -} - -U_CAPI void U_EXPORT2 -ures_getAllItemsWithFallback(const UResourceBundle *bundle, const char *path, - icu::ResourceSink &sink, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return; } - if (path == nullptr) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - StackUResourceBundle stackBundle; - const UResourceBundle *rb; - if (*path == 0) { - // empty path - rb = bundle; - } else { - rb = ures_getByKeyWithFallback(bundle, path, stackBundle.getAlias(), &errorCode); - if (U_FAILURE(errorCode)) { - return; - } - } - // Get all table items with fallback. - ResourceDataValue value; - getAllItemsWithFallback(rb, value, sink, errorCode); -} - -U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, const char* inKey, UResourceBundle *fillIn, UErrorCode *status) { - Resource res = RES_BOGUS; - UResourceDataEntry *dataEntry = NULL; - const char *key = inKey; - - if (status==NULL || U_FAILURE(*status)) { - return fillIn; - } - if(resB == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return fillIn; - } - - int32_t type = RES_GET_TYPE(resB->fRes); - if(URES_IS_TABLE(type)) { - int32_t t; - res = res_getTableItemByKey(&resB->getResData(), resB->fRes, &t, &key); - if(res == RES_BOGUS) { - key = inKey; - if(resB->fHasFallback == true) { - dataEntry = getFallbackData(resB, &key, &res, status); - if(U_SUCCESS(*status)) { - /* check if resB->fResPath gives the right name here */ - return init_resb_result(dataEntry, res, key, -1, resB, fillIn, status); - } else { - *status = U_MISSING_RESOURCE_ERROR; - } - } else { - *status = U_MISSING_RESOURCE_ERROR; - } - } else { - return init_resb_result(resB->fData, res, key, -1, resB, fillIn, status); - } - } -#if 0 - /* this is a kind of TODO item. If we have an array with an index table, we could do this. */ - /* not currently */ - else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == true) { - /* here should go a first attempt to locate the key using index table */ - dataEntry = getFallbackData(resB, &key, &res, status); - if(U_SUCCESS(*status)) { - return init_resb_result(dataEntry, res, key, resB, fillIn, status); - } else { - *status = U_MISSING_RESOURCE_ERROR; - } - } -#endif - else { - *status = U_RESOURCE_TYPE_MISMATCH; - } - return fillIn; -} - -U_CAPI const UChar* U_EXPORT2 ures_getStringByKey(const UResourceBundle *resB, const char* inKey, int32_t* len, UErrorCode *status) { - Resource res = RES_BOGUS; - UResourceDataEntry *dataEntry = NULL; - const char* key = inKey; - - if (status==NULL || U_FAILURE(*status)) { - return NULL; - } - if(resB == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - int32_t type = RES_GET_TYPE(resB->fRes); - if(URES_IS_TABLE(type)) { - int32_t t=0; - - res = res_getTableItemByKey(&resB->getResData(), resB->fRes, &t, &key); - - if(res == RES_BOGUS) { - key = inKey; - if(resB->fHasFallback == true) { - dataEntry = getFallbackData(resB, &key, &res, status); - if(U_SUCCESS(*status)) { - switch (RES_GET_TYPE(res)) { - case URES_STRING: - case URES_STRING_V2: - return res_getString({resB, key}, &dataEntry->fData, res, len); - case URES_ALIAS: - { - const UChar* result = 0; - UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status); - result = ures_getString(tempRes, len, status); - ures_close(tempRes); - return result; - } - default: - *status = U_RESOURCE_TYPE_MISMATCH; - } - } else { - *status = U_MISSING_RESOURCE_ERROR; - } - } else { - *status = U_MISSING_RESOURCE_ERROR; - } - } else { - switch (RES_GET_TYPE(res)) { - case URES_STRING: - case URES_STRING_V2: - return res_getString({resB, key}, &resB->getResData(), res, len); - case URES_ALIAS: - { - const UChar* result = 0; - UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status); - result = ures_getString(tempRes, len, status); - ures_close(tempRes); - return result; - } - default: - *status = U_RESOURCE_TYPE_MISMATCH; - } - } - } -#if 0 - /* this is a kind of TODO item. If we have an array with an index table, we could do this. */ - /* not currently */ - else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == true) { - /* here should go a first attempt to locate the key using index table */ - dataEntry = getFallbackData(resB, &key, &res, status); - if(U_SUCCESS(*status)) { - // TODO: Tracing - return res_getString(rd, res, len); - } else { - *status = U_MISSING_RESOURCE_ERROR; - } - } -#endif - else { - *status = U_RESOURCE_TYPE_MISMATCH; - } - return NULL; -} - -U_CAPI const char * U_EXPORT2 -ures_getUTF8StringByKey(const UResourceBundle *resB, - const char *key, - char *dest, int32_t *pLength, - UBool forceCopy, - UErrorCode *status) { - int32_t length16; - const UChar *s16 = ures_getStringByKey(resB, key, &length16, status); - return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status); -} - -/* TODO: clean from here down */ - -/** - * INTERNAL: Get the name of the first real locale (not placeholder) - * that has resource bundle data. - */ -U_CAPI const char* U_EXPORT2 -ures_getLocaleInternal(const UResourceBundle* resourceBundle, UErrorCode* status) -{ - if (status==NULL || U_FAILURE(*status)) { - return NULL; - } - if (!resourceBundle) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } else { - return resourceBundle->fData->fName; - } -} - -U_CAPI const char* U_EXPORT2 -ures_getLocale(const UResourceBundle* resourceBundle, - UErrorCode* status) -{ - return ures_getLocaleInternal(resourceBundle, status); -} - - -U_CAPI const char* U_EXPORT2 -ures_getLocaleByType(const UResourceBundle* resourceBundle, - ULocDataLocaleType type, - UErrorCode* status) { - if (status==NULL || U_FAILURE(*status)) { - return NULL; - } - if (!resourceBundle) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } else { - switch(type) { - case ULOC_ACTUAL_LOCALE: - return resourceBundle->fData->fName; - case ULOC_VALID_LOCALE: - return resourceBundle->fValidLocaleDataEntry->fName; - case ULOC_REQUESTED_LOCALE: - default: - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - } -} - -U_CFUNC const char* ures_getName(const UResourceBundle* resB) { - if(resB == NULL) { - return NULL; - } - - return resB->fData->fName; -} - -#ifdef URES_DEBUG -U_CFUNC const char* ures_getPath(const UResourceBundle* resB) { - if(resB == NULL) { - return NULL; - } - - return resB->fData->fPath; -} -#endif - -static UResourceBundle* -ures_openWithType(UResourceBundle *r, const char* path, const char* localeID, - UResOpenType openType, UErrorCode* status) { - if(U_FAILURE(*status)) { - return NULL; - } - - UResourceDataEntry *entry; - if(openType != URES_OPEN_DIRECT) { - /* first "canonicalize" the locale ID */ - char canonLocaleID[ULOC_FULLNAME_CAPACITY]; - uloc_getBaseName(localeID, canonLocaleID, UPRV_LENGTHOF(canonLocaleID), status); - if(U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - entry = entryOpen(path, canonLocaleID, openType, status); - } else { - entry = entryOpenDirect(path, localeID, status); - } - if(U_FAILURE(*status)) { - return NULL; - } - if(entry == NULL) { - *status = U_MISSING_RESOURCE_ERROR; - return NULL; - } - - UBool isStackObject; - if(r == NULL) { - r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle)); - if(r == NULL) { - entryClose(entry); - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - isStackObject = false; - } else { // fill-in - isStackObject = ures_isStackObject(r); - ures_closeBundle(r, false); - } - uprv_memset(r, 0, sizeof(UResourceBundle)); - ures_setIsStackObject(r, isStackObject); - - r->fValidLocaleDataEntry = r->fData = entry; - r->fHasFallback = openType != URES_OPEN_DIRECT && !r->getResData().noFallback; - r->fIsTopLevel = true; - r->fRes = r->getResData().rootRes; - r->fSize = res_countArrayItems(&r->getResData(), r->fRes); - r->fIndex = -1; - - ResourceTracer(r).traceOpen(); - - return r; -} - -U_CAPI UResourceBundle* U_EXPORT2 -ures_open(const char* path, const char* localeID, UErrorCode* status) { - return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status); -} - -U_CAPI UResourceBundle* U_EXPORT2 -ures_openNoDefault(const char* path, const char* localeID, UErrorCode* status) { - return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_ROOT, status); -} - -/** - * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed - * or sought. However, alias substitution will happen! - */ -U_CAPI UResourceBundle* U_EXPORT2 -ures_openDirect(const char* path, const char* localeID, UErrorCode* status) { - return ures_openWithType(NULL, path, localeID, URES_OPEN_DIRECT, status); -} - -/** - * Internal API: This function is used to open a resource bundle - * proper fallback chaining is executed while initialization. - * The result is stored in cache for later fallback search. - * - * Same as ures_open(), but uses the fill-in parameter and does not allocate a new bundle. - */ -U_CAPI void U_EXPORT2 -ures_openFillIn(UResourceBundle *r, const char* path, - const char* localeID, UErrorCode* status) { - if(U_SUCCESS(*status) && r == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - ures_openWithType(r, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status); -} - -/** - * Same as ures_openDirect(), but uses the fill-in parameter and does not allocate a new bundle. - */ -U_CAPI void U_EXPORT2 -ures_openDirectFillIn(UResourceBundle *r, const char* path, const char* localeID, UErrorCode* status) { - if(U_SUCCESS(*status) && r == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - ures_openWithType(r, path, localeID, URES_OPEN_DIRECT, status); -} - -/** - * API: Counts members. For arrays and tables, returns number of resources. - * For strings, returns 1. - */ -U_CAPI int32_t U_EXPORT2 -ures_countArrayItems(const UResourceBundle* resourceBundle, - const char* resourceKey, - UErrorCode* status) -{ - UResourceBundle resData; - ures_initStackObject(&resData); - if (status==NULL || U_FAILURE(*status)) { - return 0; - } - if(resourceBundle == NULL) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - ures_getByKey(resourceBundle, resourceKey, &resData, status); - - if(resData.getResData().data != NULL) { - int32_t result = res_countArrayItems(&resData.getResData(), resData.fRes); - ures_close(&resData); - return result; - } else { - *status = U_MISSING_RESOURCE_ERROR; - ures_close(&resData); - return 0; - } -} - -/** - * Internal function. - * Return the version number associated with this ResourceBundle as a string. - * - * @param resourceBundle The resource bundle for which the version is checked. - * @return A version number string as specified in the resource bundle or its parent. - * The caller does not own this string. - * @see ures_getVersion - * @internal - */ -U_CAPI const char* U_EXPORT2 -ures_getVersionNumberInternal(const UResourceBundle *resourceBundle) -{ - if (!resourceBundle) return NULL; - - if(resourceBundle->fVersion == NULL) { - - /* If the version ID has not been built yet, then do so. Retrieve */ - /* the minor version from the file. */ - UErrorCode status = U_ZERO_ERROR; - int32_t minor_len = 0; - int32_t len; - - const UChar* minor_version = ures_getStringByKey(resourceBundle, kVersionTag, &minor_len, &status); - - /* Determine the length of of the final version string. This is */ - /* the length of the major part + the length of the separator */ - /* (==1) + the length of the minor part (+ 1 for the zero byte at */ - /* the end). */ - - len = (minor_len > 0) ? minor_len : 1; - - /* Allocate the string, and build it up. */ - /* + 1 for zero byte */ - - - ((UResourceBundle *)resourceBundle)->fVersion = (char *)uprv_malloc(1 + len); - /* Check for null pointer. */ - if (((UResourceBundle *)resourceBundle)->fVersion == NULL) { - return NULL; - } - - if(minor_len > 0) { - u_UCharsToChars(minor_version, resourceBundle->fVersion , minor_len); - resourceBundle->fVersion[len] = '\0'; - } - else { - uprv_strcpy(resourceBundle->fVersion, kDefaultMinorVersion); - } - } - - return resourceBundle->fVersion; -} - -U_CAPI const char* U_EXPORT2 -ures_getVersionNumber(const UResourceBundle* resourceBundle) -{ - return ures_getVersionNumberInternal(resourceBundle); -} - -U_CAPI void U_EXPORT2 ures_getVersion(const UResourceBundle* resB, UVersionInfo versionInfo) { - if (!resB) return; - - u_versionFromString(versionInfo, ures_getVersionNumberInternal(resB)); -} - -/** Tree support functions *******************************/ -#define INDEX_LOCALE_NAME "res_index" -#define INDEX_TAG "InstalledLocales" -#define DEFAULT_TAG "default" - -#if defined(URES_TREE_DEBUG) -#include -#endif - -typedef struct ULocalesContext { - UResourceBundle installed; - UResourceBundle curr; -} ULocalesContext; - -static void U_CALLCONV -ures_loc_closeLocales(UEnumeration *enumerator) { - ULocalesContext *ctx = (ULocalesContext *)enumerator->context; - ures_close(&ctx->curr); - ures_close(&ctx->installed); - uprv_free(ctx); - uprv_free(enumerator); -} - -static int32_t U_CALLCONV -ures_loc_countLocales(UEnumeration *en, UErrorCode * /*status*/) { - ULocalesContext *ctx = (ULocalesContext *)en->context; - return ures_getSize(&ctx->installed); -} - -U_CDECL_BEGIN - - -static const char * U_CALLCONV -ures_loc_nextLocale(UEnumeration* en, - int32_t* resultLength, - UErrorCode* status) { - ULocalesContext *ctx = (ULocalesContext *)en->context; - UResourceBundle *res = &(ctx->installed); - UResourceBundle *k = NULL; - const char *result = NULL; - int32_t len = 0; - if(ures_hasNext(res) && (k = ures_getNextResource(res, &ctx->curr, status)) != 0) { - result = ures_getKey(k); - len = (int32_t)uprv_strlen(result); - } - if (resultLength) { - *resultLength = len; - } - return result; -} - -static void U_CALLCONV -ures_loc_resetLocales(UEnumeration* en, - UErrorCode* /*status*/) { - UResourceBundle *res = &((ULocalesContext *)en->context)->installed; - ures_resetIterator(res); -} - -U_CDECL_END - -static const UEnumeration gLocalesEnum = { - NULL, - NULL, - ures_loc_closeLocales, - ures_loc_countLocales, - uenum_unextDefault, - ures_loc_nextLocale, - ures_loc_resetLocales -}; - - -U_CAPI UEnumeration* U_EXPORT2 -ures_openAvailableLocales(const char *path, UErrorCode *status) -{ - UResourceBundle *idx = NULL; - UEnumeration *en = NULL; - ULocalesContext *myContext = NULL; - - if(U_FAILURE(*status)) { - return NULL; - } - myContext = static_cast(uprv_malloc(sizeof(ULocalesContext))); - en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration)); - if(!en || !myContext) { - *status = U_MEMORY_ALLOCATION_ERROR; - uprv_free(en); - uprv_free(myContext); - return NULL; - } - uprv_memcpy(en, &gLocalesEnum, sizeof(UEnumeration)); - - ures_initStackObject(&myContext->installed); - ures_initStackObject(&myContext->curr); - idx = ures_openDirect(path, INDEX_LOCALE_NAME, status); - ures_getByKey(idx, INDEX_TAG, &myContext->installed, status); - if(U_SUCCESS(*status)) { -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "Got %s::%s::[%s] : %s\n", - path, INDEX_LOCALE_NAME, INDEX_TAG, ures_getKey(&myContext->installed)); -#endif - en->context = myContext; - } else { -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s open failed - %s\n", path, u_errorName(*status)); -#endif - ures_close(&myContext->installed); - uprv_free(myContext); - uprv_free(en); - en = NULL; - } - - ures_close(idx); - - return en; -} - -static UBool isLocaleInList(UEnumeration *locEnum, const char *locToSearch, UErrorCode *status) { - const char *loc; - while ((loc = uenum_next(locEnum, NULL, status)) != NULL) { - if (uprv_strcmp(loc, locToSearch) == 0) { - return true; - } - } - return false; -} - -U_CAPI int32_t U_EXPORT2 -ures_getFunctionalEquivalent(char *result, int32_t resultCapacity, - const char *path, const char *resName, const char *keyword, const char *locid, - UBool *isAvailable, UBool omitDefault, UErrorCode *status) -{ - char kwVal[1024] = ""; /* value of keyword 'keyword' */ - char defVal[1024] = ""; /* default value for given locale */ - char defLoc[1024] = ""; /* default value for given locale */ - char base[1024] = ""; /* base locale */ - char found[1024] = ""; - char parent[1024] = ""; - char full[1024] = ""; - UResourceBundle bund1, bund2; - UResourceBundle *res = NULL; - UErrorCode subStatus = U_ZERO_ERROR; - int32_t length = 0; - if(U_FAILURE(*status)) return 0; - uloc_getKeywordValue(locid, keyword, kwVal, 1024-1,&subStatus); - if(!uprv_strcmp(kwVal, DEFAULT_TAG)) { - kwVal[0]=0; - } - uloc_getBaseName(locid, base, 1024-1,&subStatus); -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n", - locid, keyword, kwVal, base, u_errorName(subStatus)); -#endif - ures_initStackObject(&bund1); - ures_initStackObject(&bund2); - - - uprv_strcpy(parent, base); - uprv_strcpy(found, base); - - if(isAvailable) { - UEnumeration *locEnum = ures_openAvailableLocales(path, &subStatus); - *isAvailable = true; - if (U_SUCCESS(subStatus)) { - *isAvailable = isLocaleInList(locEnum, parent, &subStatus); - } - uenum_close(locEnum); - } - - if(U_FAILURE(subStatus)) { - *status = subStatus; - return 0; - } - - do { - subStatus = U_ZERO_ERROR; - res = ures_open(path, parent, &subStatus); - if(((subStatus == U_USING_FALLBACK_WARNING) || - (subStatus == U_USING_DEFAULT_WARNING)) && isAvailable) - { - *isAvailable = false; - } - isAvailable = NULL; /* only want to set this the first time around */ - -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s;%s -> %s [%s]\n", path?path:"ICUDATA", parent, u_errorName(subStatus), ures_getLocale(res, &subStatus)); -#endif - if(U_FAILURE(subStatus)) { - *status = subStatus; - } else if(subStatus == U_ZERO_ERROR) { - ures_getByKey(res,resName,&bund1, &subStatus); - if(subStatus == U_ZERO_ERROR) { - const UChar *defUstr; - int32_t defLen; - /* look for default item */ -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s;%s : loaded default -> %s\n", - path?path:"ICUDATA", parent, u_errorName(subStatus)); -#endif - defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus); - if(U_SUCCESS(subStatus) && defLen) { - u_UCharsToChars(defUstr, defVal, u_strlen(defUstr)); -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s;%s -> default %s=%s, %s\n", - path?path:"ICUDATA", parent, keyword, defVal, u_errorName(subStatus)); -#endif - uprv_strcpy(defLoc, parent); - if(kwVal[0]==0) { - uprv_strcpy(kwVal, defVal); -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s;%s -> kwVal = %s\n", - path?path:"ICUDATA", parent, keyword, kwVal); -#endif - } - } - } - } - - subStatus = U_ZERO_ERROR; - - if (res != NULL) { - uprv_strcpy(found, ures_getLocaleByType(res, ULOC_VALID_LOCALE, &subStatus)); - } - - uloc_getParent(found,parent,sizeof(parent),&subStatus); - ures_close(res); - } while(!defVal[0] && *found && uprv_strcmp(found, "root") != 0 && U_SUCCESS(*status)); - - /* Now, see if we can find the kwVal collator.. start the search over.. */ - uprv_strcpy(parent, base); - uprv_strcpy(found, base); - - do { - subStatus = U_ZERO_ERROR; - res = ures_open(path, parent, &subStatus); - if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) { - *isAvailable = false; - } - isAvailable = NULL; /* only want to set this the first time around */ - -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s;%s -> %s (looking for %s)\n", - path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal); -#endif - if(U_FAILURE(subStatus)) { - *status = subStatus; - } else if(subStatus == U_ZERO_ERROR) { - ures_getByKey(res,resName,&bund1, &subStatus); -#if defined(URES_TREE_DEBUG) -/**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, resName, u_errorName(subStatus)); -#endif - if(subStatus == U_ZERO_ERROR) { - ures_getByKey(&bund1, kwVal, &bund2, &subStatus); -#if defined(URES_TREE_DEBUG) -/**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, kwVal, u_errorName(subStatus)); -#endif - if(subStatus == U_ZERO_ERROR) { -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s;%s -> full0 %s=%s, %s\n", - path?path:"ICUDATA", parent, keyword, kwVal, u_errorName(subStatus)); -#endif - uprv_strcpy(full, parent); - if(*full == 0) { - uprv_strcpy(full, "root"); - } - /* now, recalculate default kw if need be */ - if(uprv_strlen(defLoc) > uprv_strlen(full)) { - const UChar *defUstr; - int32_t defLen; - /* look for default item */ -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s;%s -> recalculating Default0\n", - path?path:"ICUDATA", full); -#endif - defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus); - if(U_SUCCESS(subStatus) && defLen) { - u_UCharsToChars(defUstr, defVal, u_strlen(defUstr)); -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s;%s -> default0 %s=%s, %s\n", - path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus)); -#endif - uprv_strcpy(defLoc, full); - } - } /* end of recalculate default KW */ -#if defined(URES_TREE_DEBUG) - else { - fprintf(stderr, "No trim0, %s <= %s\n", defLoc, full); - } -#endif - } else { -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "err=%s in %s looking for %s\n", - u_errorName(subStatus), parent, kwVal); -#endif - } - } - } - - subStatus = U_ZERO_ERROR; - - uprv_strcpy(found, parent); - uloc_getParent(found,parent,1023,&subStatus); - ures_close(res); - } while(!full[0] && *found && U_SUCCESS(*status)); - - if((full[0]==0) && uprv_strcmp(kwVal, defVal)) { -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "Failed to locate kw %s - try default %s\n", kwVal, defVal); -#endif - uprv_strcpy(kwVal, defVal); - uprv_strcpy(parent, base); - uprv_strcpy(found, base); - - do { /* search for 'default' named item */ - subStatus = U_ZERO_ERROR; - res = ures_open(path, parent, &subStatus); - if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) { - *isAvailable = false; - } - isAvailable = NULL; /* only want to set this the first time around */ - -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s;%s -> %s (looking for default %s)\n", - path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal); -#endif - if(U_FAILURE(subStatus)) { - *status = subStatus; - } else if(subStatus == U_ZERO_ERROR) { - ures_getByKey(res,resName,&bund1, &subStatus); - if(subStatus == U_ZERO_ERROR) { - ures_getByKey(&bund1, kwVal, &bund2, &subStatus); - if(subStatus == U_ZERO_ERROR) { -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s;%s -> full1 %s=%s, %s\n", path?path:"ICUDATA", - parent, keyword, kwVal, u_errorName(subStatus)); -#endif - uprv_strcpy(full, parent); - if(*full == 0) { - uprv_strcpy(full, "root"); - } - - /* now, recalculate default kw if need be */ - if(uprv_strlen(defLoc) > uprv_strlen(full)) { - const UChar *defUstr; - int32_t defLen; - /* look for default item */ -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s;%s -> recalculating Default1\n", - path?path:"ICUDATA", full); -#endif - defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus); - if(U_SUCCESS(subStatus) && defLen) { - u_UCharsToChars(defUstr, defVal, u_strlen(defUstr)); -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s;%s -> default %s=%s, %s\n", - path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus)); -#endif - uprv_strcpy(defLoc, full); - } - } /* end of recalculate default KW */ -#if defined(URES_TREE_DEBUG) - else { - fprintf(stderr, "No trim1, %s <= %s\n", defLoc, full); - } -#endif - } - } - } - subStatus = U_ZERO_ERROR; - - uprv_strcpy(found, parent); - uloc_getParent(found,parent,1023,&subStatus); - ures_close(res); - } while(!full[0] && *found && U_SUCCESS(*status)); - } - - if(U_SUCCESS(*status)) { - if(!full[0]) { -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "Still could not load keyword %s=%s\n", keyword, kwVal); -#endif - *status = U_MISSING_RESOURCE_ERROR; - } else if(omitDefault) { -#if defined(URES_TREE_DEBUG) - fprintf(stderr,"Trim? full=%s, defLoc=%s, found=%s\n", full, defLoc, found); -#endif - if(uprv_strlen(defLoc) <= uprv_strlen(full)) { - /* found the keyword in a *child* of where the default tag was present. */ - if(!uprv_strcmp(kwVal, defVal)) { /* if the requested kw is default, */ - /* and the default is in or in an ancestor of the current locale */ -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "Removing unneeded var %s=%s\n", keyword, kwVal); -#endif - kwVal[0]=0; - } - } - } - uprv_strcpy(found, full); - if(kwVal[0]) { - uprv_strcat(found, "@"); - uprv_strcat(found, keyword); - uprv_strcat(found, "="); - uprv_strcat(found, kwVal); - } else if(!omitDefault) { - uprv_strcat(found, "@"); - uprv_strcat(found, keyword); - uprv_strcat(found, "="); - uprv_strcat(found, defVal); - } - } - /* we found the default locale - no need to repeat it.*/ - - ures_close(&bund1); - ures_close(&bund2); - - length = (int32_t)uprv_strlen(found); - - if(U_SUCCESS(*status)) { - int32_t copyLength = uprv_min(length, resultCapacity); - if(copyLength>0) { - uprv_strncpy(result, found, copyLength); - } - if(length == 0) { - *status = U_MISSING_RESOURCE_ERROR; - } - } else { - length = 0; - result[0]=0; - } - return u_terminateChars(result, resultCapacity, length, status); -} - -U_CAPI UEnumeration* U_EXPORT2 -ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status) -{ -#define VALUES_BUF_SIZE 2048 -#define VALUES_LIST_SIZE 512 - - char valuesBuf[VALUES_BUF_SIZE]; - int32_t valuesIndex = 0; - const char *valuesList[VALUES_LIST_SIZE]; - int32_t valuesCount = 0; - - const char *locale; - int32_t locLen; - - UEnumeration *locs = NULL; - - UResourceBundle item; - UResourceBundle subItem; - - ures_initStackObject(&item); - ures_initStackObject(&subItem); - locs = ures_openAvailableLocales(path, status); - - if(U_FAILURE(*status)) { - ures_close(&item); - ures_close(&subItem); - return NULL; - } - - valuesBuf[0]=0; - valuesBuf[1]=0; - - while((locale = uenum_next(locs, &locLen, status)) != 0) { - UResourceBundle *bund = NULL; - UResourceBundle *subPtr = NULL; - UErrorCode subStatus = U_ZERO_ERROR; /* don't fail if a bundle is unopenable */ - bund = ures_open(path, locale, &subStatus); - -#if defined(URES_TREE_DEBUG) - if(!bund || U_FAILURE(subStatus)) { - fprintf(stderr, "%s-%s values: Can't open %s locale - skipping. (%s)\n", - path?path:"", keyword, locale, u_errorName(subStatus)); - } -#endif - - ures_getByKey(bund, keyword, &item, &subStatus); - - if(!bund || U_FAILURE(subStatus)) { -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s-%s values: Can't find in %s - skipping. (%s)\n", - path?path:"", keyword, locale, u_errorName(subStatus)); -#endif - ures_close(bund); - bund = NULL; - continue; - } - - while((subPtr = ures_getNextResource(&item,&subItem,&subStatus)) != 0 - && U_SUCCESS(subStatus)) { - const char *k; - int32_t i; - k = ures_getKey(subPtr); - -#if defined(URES_TREE_DEBUG) - /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"", keyword, locale, k); */ -#endif - if(k == NULL || *k == 0 || - uprv_strcmp(k, DEFAULT_TAG) == 0 || uprv_strncmp(k, "private-", 8) == 0) { - // empty or "default" or unlisted type - continue; - } - for(i=0; i= (VALUES_LIST_SIZE-1)) || /* no more space in list .. */ - ((valuesIndex+kLen+1+1) >= VALUES_BUF_SIZE)) { /* no more space in buffer (string + 2 nulls) */ - *status = U_ILLEGAL_ARGUMENT_ERROR; /* out of space.. */ - } else { - uprv_strcpy(valuesBuf+valuesIndex, k); - valuesList[valuesCount++] = valuesBuf+valuesIndex; - valuesIndex += kLen; -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s | %s | %s | [%s] (UNIQUE)\n", - path?path:"", keyword, locale, k); -#endif - valuesBuf[valuesIndex++] = 0; /* terminate */ - } - } - } - ures_close(bund); - } - valuesBuf[valuesIndex++] = 0; /* terminate */ - - ures_close(&item); - ures_close(&subItem); - uenum_close(locs); -#if defined(URES_TREE_DEBUG) - fprintf(stderr, "%s: size %d, #%d\n", u_errorName(*status), - valuesIndex, valuesCount); -#endif - return uloc_openKeywordList(valuesBuf, valuesIndex, status); -} -#if 0 -/* This code isn't needed, and given the documentation warnings the implementation is suspect */ -U_CAPI UBool U_EXPORT2 -ures_equal(const UResourceBundle* res1, const UResourceBundle* res2){ - if(res1==NULL || res2==NULL){ - return res1==res2; /* pointer comparison */ - } - if(res1->fKey==NULL|| res2->fKey==NULL){ - return (res1->fKey==res2->fKey); - }else{ - if(uprv_strcmp(res1->fKey, res2->fKey)!=0){ - return false; - } - } - if(uprv_strcmp(res1->fData->fName, res2->fData->fName)!=0){ - return false; - } - if(res1->fData->fPath == NULL|| res2->fData->fPath==NULL){ - return (res1->fData->fPath == res2->fData->fPath); - }else{ - if(uprv_strcmp(res1->fData->fPath, res2->fData->fPath)!=0){ - return false; - } - } - if(uprv_strcmp(res1->fData->fParent->fName, res2->fData->fParent->fName)!=0){ - return false; - } - if(uprv_strcmp(res1->fData->fParent->fPath, res2->fData->fParent->fPath)!=0){ - return false; - } - if(uprv_strncmp(res1->fResPath, res2->fResPath, res1->fResPathLen)!=0){ - return false; - } - if(res1->fRes != res2->fRes){ - return false; - } - return true; -} -U_CAPI UResourceBundle* U_EXPORT2 -ures_clone(const UResourceBundle* res, UErrorCode* status){ - UResourceBundle* bundle = NULL; - UResourceBundle* ret = NULL; - if(U_FAILURE(*status) || res == NULL){ - return NULL; - } - bundle = ures_open(res->fData->fPath, res->fData->fName, status); - if(res->fResPath!=NULL){ - ret = ures_findSubResource(bundle, res->fResPath, NULL, status); - ures_close(bundle); - }else{ - ret = bundle; - } - return ret; -} -U_CAPI const UResourceBundle* U_EXPORT2 -ures_getParentBundle(const UResourceBundle* res){ - if(res==NULL){ - return NULL; - } - return res->fParentRes; -} -#endif - -U_CAPI void U_EXPORT2 -ures_getVersionByKey(const UResourceBundle* res, const char *key, UVersionInfo ver, UErrorCode *status) { - const UChar *str; - int32_t len; - str = ures_getStringByKey(res, key, &len, status); - if(U_SUCCESS(*status)) { - u_versionFromUString(ver, str); - } -} - -/* eof */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 1997-2016, International Business Machines Corporation and +* others. All Rights Reserved. +****************************************************************************** +* +* File uresbund.cpp +* +* Modification History: +* +* Date Name Description +* 04/01/97 aliu Creation. +* 06/14/99 stephen Removed functions taking a filename suffix. +* 07/20/99 stephen Changed for UResourceBundle typedef'd to void* +* 11/09/99 weiv Added ures_getLocale() +* March 2000 weiv Total overhaul - using data in DLLs +* 06/20/2000 helena OS/400 port changes; mostly typecast. +* 06/24/02 weiv Added support for resource sharing +****************************************************************************** +*/ + +#include "unicode/ures.h" +#include "unicode/ustring.h" +#include "unicode/ucnv.h" +#include "charstr.h" +#include "uresimp.h" +#include "ustr_imp.h" +#include "cwchar.h" +#include "ucln_cmn.h" +#include "cmemory.h" +#include "cstring.h" +#include "mutex.h" +#include "uhash.h" +#include "unicode/uenum.h" +#include "uenumimp.h" +#include "ulocimp.h" +#include "umutex.h" +#include "putilimp.h" +#include "uassert.h" +#include "uresdata.h" + +using namespace icu; + +/* +Static cache for already opened resource bundles - mostly for keeping fallback info +TODO: This cache should probably be removed when the deprecated code is + completely removed. +*/ +static UHashtable *cache = nullptr; +static icu::UInitOnce gCacheInitOnce {}; + +static UMutex resbMutex; + +/* INTERNAL: hashes an entry */ +static int32_t U_CALLCONV hashEntry(const UHashTok parm) { + UResourceDataEntry *b = (UResourceDataEntry *)parm.pointer; + UHashTok namekey, pathkey; + namekey.pointer = b->fName; + pathkey.pointer = b->fPath; + return uhash_hashChars(namekey)+37u*uhash_hashChars(pathkey); +} + +/* INTERNAL: compares two entries */ +static UBool U_CALLCONV compareEntries(const UHashTok p1, const UHashTok p2) { + UResourceDataEntry *b1 = (UResourceDataEntry *)p1.pointer; + UResourceDataEntry *b2 = (UResourceDataEntry *)p2.pointer; + UHashTok name1, name2, path1, path2; + name1.pointer = b1->fName; + name2.pointer = b2->fName; + path1.pointer = b1->fPath; + path2.pointer = b2->fPath; + return (UBool)(uhash_compareChars(name1, name2) && + uhash_compareChars(path1, path2)); +} + + +/** + * Internal function, gets parts of locale name according + * to the position of '_' character + */ +static UBool chopLocale(char *name) { + char *i = uprv_strrchr(name, '_'); + + if(i != nullptr) { + *i = '\0'; + return true; + } + + return false; +} + +static UBool hasVariant(const char* localeID) { + UErrorCode err = U_ZERO_ERROR; + int32_t variantLength = uloc_getVariant(localeID, nullptr, 0, &err); + return variantLength != 0; +} + +// This file contains the tables for doing locale fallback, which are generated +// by the CLDR-to-ICU process directly from the CLDR data. This file should only +// ever be included from here. +#define INCLUDED_FROM_URESBUND_CPP +#include "localefallback_data.h" + +static const char* performFallbackLookup(const char* key, + const char* keyStrs, + const char* valueStrs, + const int32_t* lookupTable, + int32_t lookupTableLength) { + const int32_t* bottom = lookupTable; + const int32_t* top = lookupTable + lookupTableLength; + + while (bottom < top) { + // Effectively, divide by 2 and round down to an even index + const int32_t* middle = bottom + (((top - bottom) / 4) * 2); + const char* entryKey = &(keyStrs[*middle]); + int32_t strcmpResult = uprv_strcmp(key, entryKey); + if (strcmpResult == 0) { + return &(valueStrs[middle[1]]); + } else if (strcmpResult < 0) { + top = middle; + } else { + bottom = middle + 2; + } + } + return nullptr; +} + +static CharString getDefaultScript(const CharString& language, const CharString& region) { + const char* defaultScript = nullptr; + UErrorCode err = U_ZERO_ERROR; + + // the default script will be "Latn" if we don't find the locale ID in the tables + CharString result("Latn", err); + + // if we were passed both language and region, make them into a locale ID and look that up in the default + // script table + if (!region.isEmpty()) { + CharString localeID; + localeID.append(language, err).append("_", err).append(region, err); + if (U_FAILURE(err)) { + return result; + } + defaultScript = performFallbackLookup(localeID.data(), dsLocaleIDChars, scriptCodeChars, defaultScriptTable, UPRV_LENGTHOF(defaultScriptTable)); + } + + // if we didn't find anything, look up just the language in the default script table + if (defaultScript == nullptr) { + defaultScript = performFallbackLookup(language.data(), dsLocaleIDChars, scriptCodeChars, defaultScriptTable, UPRV_LENGTHOF(defaultScriptTable)); + } + + // if either lookup above succeeded, copy the result from "defaultScript" into "result"; otherwise, return "Latn" + if (defaultScript != nullptr) { + result.clear(); + result.append(defaultScript, err); + } + return result; +} + +enum UResOpenType { + /** + * Open a resource bundle for the locale; + * if there is not even a base language bundle, then fall back to the default locale; + * if there is no bundle for that either, then load the root bundle. + * + * This is the default bundle loading behavior. + */ + URES_OPEN_LOCALE_DEFAULT_ROOT, + // TODO: ICU ticket #11271 "consistent default locale across locale trees" + // Add an option to look at the main locale tree for whether to + // fall back to root directly (if the locale has main data) or + // fall back to the default locale first (if the locale does not even have main data). + /** + * Open a resource bundle for the locale; + * if there is not even a base language bundle, then load the root bundle; + * never fall back to the default locale. + * + * This is used for algorithms that have good pan-Unicode default behavior, + * such as case mappings, collation, and segmentation (BreakIterator). + */ + URES_OPEN_LOCALE_ROOT, + /** + * Open a resource bundle for the exact bundle name as requested; + * no fallbacks, do not load parent bundles. + * + * This is used for supplemental (non-locale) data. + */ + URES_OPEN_DIRECT +}; +typedef enum UResOpenType UResOpenType; + +/** + * Internal function, determines the search path for resource bundle files. + * Currently, this function is used only by findFirstExisting() to help search for resource bundle files when a bundle for the specified + * locale doesn't exist. The code that supports inheritance of resources between existing resource bundle files continues to + * use chopLocale() below. + * @param name In-out parameter: On input, the locale ID to get a parent locale ID for (this is a locale's base name, without keywords); on output, the + * requested parent locale ID. + * @param origName The original locale ID the caller of findFirstExisting() requested. This is the same as `name` on the first call to this function, + * but as findFirstExisting() ascends the resource bundle's parent tree, this parameter will continue to be the original locale ID requested. + */ +static bool getParentLocaleID(char *name, const char *origName, UResOpenType openType) { + // early out if the locale ID has a variant code or ends with _ + size_t nameLen = uprv_strlen(name); + if (!nameLen || name[nameLen - 1] == '_' || hasVariant(name)) { + return chopLocale(name); + } + + UErrorCode err = U_ZERO_ERROR; + const char* tempNamePtr = name; + CharString language = ulocimp_getLanguage(tempNamePtr, &tempNamePtr, err); + if (*tempNamePtr == '_') { + ++tempNamePtr; + } + CharString script = ulocimp_getScript(tempNamePtr, &tempNamePtr, err); + if (*tempNamePtr == '_') { + ++tempNamePtr; + } + CharString region = ulocimp_getCountry(tempNamePtr, &tempNamePtr, err); + CharString workingLocale; + if (U_FAILURE(err)) { + // hopefully this never happens... + return chopLocale(name); + } + + // if the open type is URES_OPEN_LOCALE_DEFAULT_ROOT, first look the locale ID up in the parent locale table; + // if that table specifies a parent for it, return that (we don't do this for the other open types-- if we're not + // falling back through the system default locale, we also want to do straight truncation fallback instead + // of looking things up in the parent locale table-- see https://www.unicode.org/reports/tr35/tr35.html#Parent_Locales: + // "Collation data, however, is an exception...") + if (openType == URES_OPEN_LOCALE_DEFAULT_ROOT) { + const char* parentID = performFallbackLookup(name, parentLocaleChars, parentLocaleChars, parentLocaleTable, UPRV_LENGTHOF(parentLocaleTable)); + if (parentID != nullptr) { + uprv_strcpy(name, parentID); + return true; + } + } + + // if it's not in the parent locale table, figure out the fallback script algorithmically + // (see CLDR-15265 for an explanation of the algorithm) + if (!script.isEmpty() && !region.isEmpty()) { + // if "name" has both script and region, is the script the default script? + // - if so, remove it and keep the region + // - if not, remove the region and keep the script + if (getDefaultScript(language, region) == script.toStringPiece()) { + workingLocale.append(language, err).append("_", err).append(region, err); + } else { + workingLocale.append(language, err).append("_", err).append(script, err); + } + } else if (!region.isEmpty()) { + // if "name" has region but not script, did the original locale ID specify a script? + // - if yes, replace the region with the script from the original locale ID + // - if no, replace the region with the default script for that language and region + UErrorCode err = U_ZERO_ERROR; + tempNamePtr = origName; + CharString origNameLanguage = ulocimp_getLanguage(tempNamePtr, &tempNamePtr, err); + if (*tempNamePtr == '_') { + ++tempNamePtr; + } + CharString origNameScript = ulocimp_getScript(origName, nullptr, err); + if (!origNameScript.isEmpty()) { + workingLocale.append(language, err).append("_", err).append(origNameScript, err); + } else { + workingLocale.append(language, err).append("_", err).append(getDefaultScript(language, region), err); + } + } else if (!script.isEmpty()) { + // if "name" has script but not region (and our open type if URES_OPEN_LOCALE_DEFAULT_ROOT), is the script + // the default script for the language? + // - if so, remove it from the locale ID + // - if not, return false to continue up the chain + // (we don't do this for other open types for the same reason we don't look things up in the parent + // locale table for other open types-- see the reference to UTS #35 above) + if (openType != URES_OPEN_LOCALE_DEFAULT_ROOT || getDefaultScript(language, CharString()) == script.toStringPiece()) { + workingLocale.append(language, err); + } else { + return false; + } + } else { + // if "name" just contains a language code, return false so the calling code falls back to "root" + return false; + } + if (U_SUCCESS(err) && !workingLocale.isEmpty()) { + uprv_strcpy(name, workingLocale.data()); + return true; + } else { + return false; + } +} + +/** + * Called to check whether a name without '_' needs to be checked for a parent. + * Some code had assumed that locale IDs with '_' could not have a non-root parent. + * We may want a better way of doing this. + */ +static UBool mayHaveParent(char *name) { + return (name[0] != 0 && uprv_strstr("nb nn",name) != nullptr); +} + +/** + * Internal function + */ +static void entryIncrease(UResourceDataEntry *entry) { + Mutex lock(&resbMutex); + entry->fCountExisting++; + while(entry->fParent != nullptr) { + entry = entry->fParent; + entry->fCountExisting++; + } +} + +/** + * Internal function. Tries to find a resource in given Resource + * Bundle, as well as in its parents + */ +static UResourceDataEntry *getFallbackData( + const UResourceBundle *resBundle, + const char **resTag, Resource *res, UErrorCode *status) { + UResourceDataEntry *dataEntry = resBundle->fData; + int32_t indexR = -1; + int32_t i = 0; + *res = RES_BOGUS; + if(dataEntry == nullptr) { + *status = U_MISSING_RESOURCE_ERROR; + return nullptr; + } + if(dataEntry->fBogus == U_ZERO_ERROR) { /* if this resource is real, */ + *res = res_getTableItemByKey(&(dataEntry->fData), dataEntry->fData.rootRes, &indexR, resTag); /* try to get data from there */ + i++; + } + if(resBundle->fHasFallback) { + // Otherwise, we'll look in parents. + while(*res == RES_BOGUS && dataEntry->fParent != nullptr) { + dataEntry = dataEntry->fParent; + if(dataEntry->fBogus == U_ZERO_ERROR) { + i++; + *res = res_getTableItemByKey(&(dataEntry->fData), dataEntry->fData.rootRes, &indexR, resTag); + } + } + } + + if(*res == RES_BOGUS) { + // If the resource is not found, we need to give an error. + *status = U_MISSING_RESOURCE_ERROR; + return nullptr; + } + // If the resource is found in parents, we need to adjust the error. + if(i>1) { + if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) { + *status = U_USING_DEFAULT_WARNING; + } else { + *status = U_USING_FALLBACK_WARNING; + } + } + return dataEntry; +} + +static void +free_entry(UResourceDataEntry *entry) { + UResourceDataEntry *alias; + res_unload(&(entry->fData)); + if(entry->fName != nullptr && entry->fName != entry->fNameBuffer) { + uprv_free(entry->fName); + } + if(entry->fPath != nullptr) { + uprv_free(entry->fPath); + } + if(entry->fPool != nullptr) { + --entry->fPool->fCountExisting; + } + alias = entry->fAlias; + if(alias != nullptr) { + while(alias->fAlias != nullptr) { + alias = alias->fAlias; + } + --alias->fCountExisting; + } + uprv_free(entry); +} + +/* Works just like ucnv_flushCache() */ +static int32_t ures_flushCache() +{ + UResourceDataEntry *resB; + int32_t pos; + int32_t rbDeletedNum = 0; + const UHashElement *e; + UBool deletedMore; + + /*if shared data hasn't even been lazy evaluated yet + * return 0 + */ + Mutex lock(&resbMutex); + if (cache == nullptr) { + return 0; + } + + do { + deletedMore = false; + /*creates an enumeration to iterate through every element in the table */ + pos = UHASH_FIRST; + while ((e = uhash_nextElement(cache, &pos)) != nullptr) + { + resB = (UResourceDataEntry *) e->value.pointer; + /* Deletes only if reference counter == 0 + * Don't worry about the children of this node. + * Those will eventually get deleted too, if not already. + * Don't worry about the parents of this node. + * Those will eventually get deleted too, if not already. + */ + /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */ + /* some resource bundles are still open somewhere. */ + + if (resB->fCountExisting == 0) { + rbDeletedNum++; + deletedMore = true; + uhash_removeElement(cache, e); + free_entry(resB); + } + } + /* + * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting + * got decremented by free_entry(). + */ + } while(deletedMore); + + return rbDeletedNum; +} + +#ifdef URES_DEBUG +#include + +U_CAPI UBool U_EXPORT2 ures_dumpCacheContents() { + UBool cacheNotEmpty = false; + int32_t pos = UHASH_FIRST; + const UHashElement *e; + UResourceDataEntry *resB; + + Mutex lock(&resbMutex); + if (cache == nullptr) { + fprintf(stderr,"%s:%d: RB Cache is nullptr.\n", __FILE__, __LINE__); + return false; + } + + while ((e = uhash_nextElement(cache, &pos)) != nullptr) { + cacheNotEmpty=true; + resB = (UResourceDataEntry *) e->value.pointer; + fprintf(stderr,"%s:%d: RB Cache: Entry @0x%p, refcount %d, name %s:%s. Pool 0x%p, alias 0x%p, parent 0x%p\n", + __FILE__, __LINE__, + (void*)resB, resB->fCountExisting, + resB->fName?resB->fName:"nullptr", + resB->fPath?resB->fPath:"nullptr", + (void*)resB->fPool, + (void*)resB->fAlias, + (void*)resB->fParent); + } + + fprintf(stderr,"%s:%d: RB Cache still contains %d items.\n", __FILE__, __LINE__, uhash_count(cache)); + return cacheNotEmpty; +} + +#endif + +static UBool U_CALLCONV ures_cleanup() +{ + if (cache != nullptr) { + ures_flushCache(); + uhash_close(cache); + cache = nullptr; + } + gCacheInitOnce.reset(); + return true; +} + +/** INTERNAL: Initializes the cache for resources */ +static void U_CALLCONV createCache(UErrorCode &status) { + U_ASSERT(cache == nullptr); + cache = uhash_open(hashEntry, compareEntries, nullptr, &status); + ucln_common_registerCleanup(UCLN_COMMON_URES, ures_cleanup); +} + +static void initCache(UErrorCode *status) { + umtx_initOnce(gCacheInitOnce, &createCache, *status); +} + +/** INTERNAL: sets the name (locale) of the resource bundle to given name */ + +static void setEntryName(UResourceDataEntry *res, const char *name, UErrorCode *status) { + int32_t len = (int32_t)uprv_strlen(name); + if(res->fName != nullptr && res->fName != res->fNameBuffer) { + uprv_free(res->fName); + } + if (len < (int32_t)sizeof(res->fNameBuffer)) { + res->fName = res->fNameBuffer; + } + else { + res->fName = (char *)uprv_malloc(len+1); + } + if(res->fName == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + } else { + uprv_strcpy(res->fName, name); + } +} + +static UResourceDataEntry * +getPoolEntry(const char *path, UErrorCode *status); + +/** + * INTERNAL: Inits and opens an entry from a data DLL. + * CAUTION: resbMutex must be locked when calling this function. + */ +static UResourceDataEntry *init_entry(const char *localeID, const char *path, UErrorCode *status) { + UResourceDataEntry *r = nullptr; + UResourceDataEntry find; + /*int32_t hashValue;*/ + const char *name; + char aliasName[100] = { 0 }; + int32_t aliasLen = 0; + /*UBool isAlias = false;*/ + /*UHashTok hashkey; */ + + if(U_FAILURE(*status)) { + return nullptr; + } + + /* here we try to deduce the right locale name */ + if(localeID == nullptr) { /* if localeID is nullptr, we're trying to open default locale */ + name = uloc_getDefault(); + } else if(*localeID == 0) { /* if localeID is "" then we try to open root locale */ + name = kRootLocaleName; + } else { /* otherwise, we'll open what we're given */ + name = localeID; + } + + find.fName = (char *)name; + find.fPath = (char *)path; + + /* calculate the hash value of the entry */ + /*hashkey.pointer = (void *)&find;*/ + /*hashValue = hashEntry(hashkey);*/ + + /* check to see if we already have this entry */ + r = (UResourceDataEntry *)uhash_get(cache, &find); + if(r == nullptr) { + /* if the entry is not yet in the hash table, we'll try to construct a new one */ + r = (UResourceDataEntry *) uprv_malloc(sizeof(UResourceDataEntry)); + if(r == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + uprv_memset(r, 0, sizeof(UResourceDataEntry)); + /*r->fHashKey = hashValue;*/ + + setEntryName(r, name, status); + if (U_FAILURE(*status)) { + uprv_free(r); + return nullptr; + } + + if(path != nullptr) { + r->fPath = (char *)uprv_strdup(path); + if(r->fPath == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + uprv_free(r); + return nullptr; + } + } + + /* this is the actual loading */ + res_load(&(r->fData), r->fPath, r->fName, status); + + if (U_FAILURE(*status)) { + /* if we failed to load due to an out-of-memory error, exit early. */ + if (*status == U_MEMORY_ALLOCATION_ERROR) { + uprv_free(r); + return nullptr; + } + /* we have no such entry in dll, so it will always use fallback */ + *status = U_USING_FALLBACK_WARNING; + r->fBogus = U_USING_FALLBACK_WARNING; + } else { /* if we have a regular entry */ + Resource aliasres; + if (r->fData.usesPoolBundle) { + r->fPool = getPoolEntry(r->fPath, status); + if (U_SUCCESS(*status)) { + const int32_t *poolIndexes = r->fPool->fData.pRoot + 1; + if(r->fData.pRoot[1 + URES_INDEX_POOL_CHECKSUM] == poolIndexes[URES_INDEX_POOL_CHECKSUM]) { + r->fData.poolBundleKeys = (const char *)(poolIndexes + (poolIndexes[URES_INDEX_LENGTH] & 0xff)); + r->fData.poolBundleStrings = r->fPool->fData.p16BitUnits; + } else { + r->fBogus = *status = U_INVALID_FORMAT_ERROR; + } + } else { + r->fBogus = *status; + } + } + if (U_SUCCESS(*status)) { + /* handle the alias by trying to get out the %%Alias tag.*/ + /* We'll try to get alias string from the bundle */ + aliasres = res_getResource(&(r->fData), "%%ALIAS"); + if (aliasres != RES_BOGUS) { + // No tracing: called during initial data loading + const char16_t *alias = res_getStringNoTrace(&(r->fData), aliasres, &aliasLen); + if(alias != nullptr && aliasLen > 0) { /* if there is actual alias - unload and load new data */ + u_UCharsToChars(alias, aliasName, aliasLen+1); + r->fAlias = init_entry(aliasName, path, status); + } + } + } + } + + { + UResourceDataEntry *oldR = nullptr; + if((oldR = (UResourceDataEntry *)uhash_get(cache, r)) == nullptr) { /* if the data is not cached */ + /* just insert it in the cache */ + UErrorCode cacheStatus = U_ZERO_ERROR; + uhash_put(cache, (void *)r, r, &cacheStatus); + if (U_FAILURE(cacheStatus)) { + *status = cacheStatus; + free_entry(r); + r = nullptr; + } + } else { + /* somebody have already inserted it while we were working, discard newly opened data */ + /* Also, we could get here IF we opened an alias */ + free_entry(r); + r = oldR; + } + } + + } + if(r != nullptr) { + /* return the real bundle */ + while(r->fAlias != nullptr) { + r = r->fAlias; + } + r->fCountExisting++; /* we increase its reference count */ + /* if the resource has a warning */ + /* we don't want to overwrite a status with no error */ + if(r->fBogus != U_ZERO_ERROR && U_SUCCESS(*status)) { + *status = r->fBogus; /* set the returning status */ + } + } + return r; +} + +static UResourceDataEntry * +getPoolEntry(const char *path, UErrorCode *status) { + UResourceDataEntry *poolBundle = init_entry(kPoolBundleName, path, status); + if( U_SUCCESS(*status) && + (poolBundle == nullptr || poolBundle->fBogus != U_ZERO_ERROR || !poolBundle->fData.isPoolBundle) + ) { + *status = U_INVALID_FORMAT_ERROR; + } + return poolBundle; +} + +/* INTERNAL: */ +/* CAUTION: resbMutex must be locked when calling this function! */ +static UResourceDataEntry * +findFirstExisting(const char* path, char* name, const char* defaultLocale, UResOpenType openType, + UBool *isRoot, UBool *foundParent, UBool *isDefault, UErrorCode* status) { + UResourceDataEntry *r = nullptr; + UBool hasRealData = false; + *foundParent = true; /* we're starting with a fresh name */ + char origName[ULOC_FULLNAME_CAPACITY]; + + uprv_strcpy(origName, name); + while(*foundParent && !hasRealData) { + r = init_entry(name, path, status); + /* Null pointer test */ + if (U_FAILURE(*status)) { + return nullptr; + } + *isDefault = (UBool)(uprv_strncmp(name, defaultLocale, uprv_strlen(name)) == 0); + hasRealData = (UBool)(r->fBogus == U_ZERO_ERROR); + if(!hasRealData) { + /* this entry is not real. We will discard it. */ + /* However, the parent line for this entry is */ + /* not to be used - as there might be parent */ + /* lines in cache from previous openings that */ + /* are not updated yet. */ + r->fCountExisting--; + /*entryCloseInt(r);*/ + r = nullptr; + *status = U_USING_FALLBACK_WARNING; + } else { + uprv_strcpy(name, r->fName); /* this is needed for supporting aliases */ + } + + *isRoot = (UBool)(uprv_strcmp(name, kRootLocaleName) == 0); + + /*Fallback data stuff*/ + if (!hasRealData) { + *foundParent = getParentLocaleID(name, origName, openType); + } else { + // we've already found a real resource file; what we return to the caller is the parent + // locale ID for inheritance, which should come from chopLocale(), not getParentLocaleID() + *foundParent = chopLocale(name); + } + if (*foundParent && *name == '\0') { + uprv_strcpy(name, "und"); + } + } + return r; +} + +static void ures_setIsStackObject( UResourceBundle* resB, UBool state) { + if(state) { + resB->fMagic1 = 0; + resB->fMagic2 = 0; + } else { + resB->fMagic1 = MAGIC1; + resB->fMagic2 = MAGIC2; + } +} + +static UBool ures_isStackObject(const UResourceBundle* resB) { + return((resB->fMagic1 == MAGIC1 && resB->fMagic2 == MAGIC2)?false:true); +} + + +U_CFUNC void ures_initStackObject(UResourceBundle* resB) { + uprv_memset(resB, 0, sizeof(UResourceBundle)); + ures_setIsStackObject(resB, true); +} + +U_NAMESPACE_BEGIN + +StackUResourceBundle::StackUResourceBundle() { + ures_initStackObject(&bundle); +} + +StackUResourceBundle::~StackUResourceBundle() { + ures_close(&bundle); +} + +U_NAMESPACE_END + +static UBool // returns U_SUCCESS(*status) +loadParentsExceptRoot(UResourceDataEntry *&t1, + char name[], int32_t nameCapacity, + UBool usingUSRData, char usrDataPath[], UErrorCode *status) { + if (U_FAILURE(*status)) { return false; } + UBool checkParent = true; + while (checkParent && t1->fParent == nullptr && !t1->fData.noFallback && + res_getResource(&t1->fData,"%%ParentIsRoot") == RES_BOGUS) { + Resource parentRes = res_getResource(&t1->fData, "%%Parent"); + if (parentRes != RES_BOGUS) { // An explicit parent was found. + int32_t parentLocaleLen = 0; + // No tracing: called during initial data loading + const char16_t *parentLocaleName = res_getStringNoTrace(&(t1->fData), parentRes, &parentLocaleLen); + if(parentLocaleName != nullptr && 0 < parentLocaleLen && parentLocaleLen < nameCapacity) { + u_UCharsToChars(parentLocaleName, name, parentLocaleLen + 1); + if (uprv_strcmp(name, kRootLocaleName) == 0) { + return true; + } + } + } + // Insert regular parents. + UErrorCode parentStatus = U_ZERO_ERROR; + UResourceDataEntry *t2 = init_entry(name, t1->fPath, &parentStatus); + if (U_FAILURE(parentStatus)) { + *status = parentStatus; + return false; + } + UResourceDataEntry *u2 = nullptr; + UErrorCode usrStatus = U_ZERO_ERROR; + if (usingUSRData) { // This code inserts user override data into the inheritance chain. + u2 = init_entry(name, usrDataPath, &usrStatus); + // If we failed due to out-of-memory, report that to the caller and exit early. + if (usrStatus == U_MEMORY_ALLOCATION_ERROR) { + *status = usrStatus; + return false; + } + } + + if (usingUSRData && U_SUCCESS(usrStatus) && u2->fBogus == U_ZERO_ERROR) { + t1->fParent = u2; + u2->fParent = t2; + } else { + t1->fParent = t2; + if (usingUSRData) { + // The USR override data wasn't found, set it to be deleted. + u2->fCountExisting = 0; + } + } + t1 = t2; + checkParent = chopLocale(name) || mayHaveParent(name); + } + return true; +} + +static UBool // returns U_SUCCESS(*status) +insertRootBundle(UResourceDataEntry *&t1, UErrorCode *status) { + if (U_FAILURE(*status)) { return false; } + UErrorCode parentStatus = U_ZERO_ERROR; + UResourceDataEntry *t2 = init_entry(kRootLocaleName, t1->fPath, &parentStatus); + if (U_FAILURE(parentStatus)) { + *status = parentStatus; + return false; + } + t1->fParent = t2; + t1 = t2; + return true; +} + +static UResourceDataEntry *entryOpen(const char* path, const char* localeID, + UResOpenType openType, UErrorCode* status) { + U_ASSERT(openType != URES_OPEN_DIRECT); + UErrorCode intStatus = U_ZERO_ERROR; + UResourceDataEntry *r = nullptr; + UResourceDataEntry *t1 = nullptr; + UBool isDefault = false; + UBool isRoot = false; + UBool hasRealData = false; + UBool hasChopped = true; + UBool usingUSRData = U_USE_USRDATA && ( path == nullptr || uprv_strncmp(path,U_ICUDATA_NAME,8) == 0); + + char name[ULOC_FULLNAME_CAPACITY]; + char usrDataPath[96]; + + initCache(status); + + if(U_FAILURE(*status)) { + return nullptr; + } + + uprv_strncpy(name, localeID, sizeof(name) - 1); + name[sizeof(name) - 1] = 0; + + if ( usingUSRData ) { + if ( path == nullptr ) { + uprv_strcpy(usrDataPath, U_USRDATA_NAME); + } else { + uprv_strncpy(usrDataPath, path, sizeof(usrDataPath) - 1); + usrDataPath[0] = 'u'; + usrDataPath[1] = 's'; + usrDataPath[2] = 'r'; + usrDataPath[sizeof(usrDataPath) - 1] = 0; + } + } + + // Note: We need to query the default locale *before* locking resbMutex. + const char *defaultLocale = uloc_getDefault(); + + Mutex lock(&resbMutex); // Lock resbMutex until the end of this function. + + /* We're going to skip all the locales that do not have any data */ + r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus); + + // If we failed due to out-of-memory, report the failure and exit early. + if (intStatus == U_MEMORY_ALLOCATION_ERROR) { + *status = intStatus; + goto finish; + } + + if(r != nullptr) { /* if there is one real locale, we can look for parents. */ + t1 = r; + hasRealData = true; + if ( usingUSRData ) { /* This code inserts user override data into the inheritance chain */ + UErrorCode usrStatus = U_ZERO_ERROR; + UResourceDataEntry *u1 = init_entry(t1->fName, usrDataPath, &usrStatus); + // If we failed due to out-of-memory, report the failure and exit early. + if (intStatus == U_MEMORY_ALLOCATION_ERROR) { + *status = intStatus; + goto finish; + } + if ( u1 != nullptr ) { + if(u1->fBogus == U_ZERO_ERROR) { + u1->fParent = t1; + r = u1; + } else { + /* the USR override data wasn't found, set it to be deleted */ + u1->fCountExisting = 0; + } + } + } + if ((hasChopped || mayHaveParent(name)) && !isRoot) { + if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) { + goto finish; + } + } + } + + /* we could have reached this point without having any real data */ + /* if that is the case, we need to chain in the default locale */ + if(r==nullptr && openType == URES_OPEN_LOCALE_DEFAULT_ROOT && !isDefault && !isRoot) { + /* insert default locale */ + uprv_strcpy(name, defaultLocale); + r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus); + // If we failed due to out-of-memory, report the failure and exit early. + if (intStatus == U_MEMORY_ALLOCATION_ERROR) { + *status = intStatus; + goto finish; + } + intStatus = U_USING_DEFAULT_WARNING; + if(r != nullptr) { /* the default locale exists */ + t1 = r; + hasRealData = true; + isDefault = true; + // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path? + if ((hasChopped || mayHaveParent(name)) && !isRoot) { + if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) { + goto finish; + } + } + } + } + + /* we could still have r == nullptr at this point - maybe even default locale is not */ + /* present */ + if(r == nullptr) { + uprv_strcpy(name, kRootLocaleName); + r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus); + // If we failed due to out-of-memory, report the failure and exit early. + if (intStatus == U_MEMORY_ALLOCATION_ERROR) { + *status = intStatus; + goto finish; + } + if(r != nullptr) { + t1 = r; + intStatus = U_USING_DEFAULT_WARNING; + hasRealData = true; + } else { /* we don't even have the root locale */ + *status = U_MISSING_RESOURCE_ERROR; + goto finish; + } + } else if(!isRoot && uprv_strcmp(t1->fName, kRootLocaleName) != 0 && + t1->fParent == nullptr && !r->fData.noFallback) { + if (!insertRootBundle(t1, status)) { + goto finish; + } + if(!hasRealData) { + r->fBogus = U_USING_DEFAULT_WARNING; + } + } + + // TODO: Does this ever loop? + while(r != nullptr && !isRoot && t1->fParent != nullptr) { + t1->fParent->fCountExisting++; + t1 = t1->fParent; + } + +finish: + if(U_SUCCESS(*status)) { + if(intStatus != U_ZERO_ERROR) { + *status = intStatus; + } + return r; + } else { + return nullptr; + } +} + +/** + * Version of entryOpen() and findFirstExisting() for ures_openDirect(), + * with no fallbacks. + * Parent and root locale bundles are loaded if + * the requested bundle does not have the "nofallback" flag. + */ +static UResourceDataEntry * +entryOpenDirect(const char* path, const char* localeID, UErrorCode* status) { + initCache(status); + if(U_FAILURE(*status)) { + return nullptr; + } + + // Note: We need to query the default locale *before* locking resbMutex. + // If the localeID is nullptr, then we want to use the default locale. + if (localeID == nullptr) { + localeID = uloc_getDefault(); + } else if (*localeID == 0) { + // If the localeID is "", then we want to use the root locale. + localeID = kRootLocaleName; + } + + Mutex lock(&resbMutex); + + // findFirstExisting() without fallbacks. + UResourceDataEntry *r = init_entry(localeID, path, status); + if(U_SUCCESS(*status)) { + if(r->fBogus != U_ZERO_ERROR) { + r->fCountExisting--; + r = nullptr; + } + } else { + r = nullptr; + } + + // Some code depends on the ures_openDirect() bundle to have a parent bundle chain, + // unless it is marked with "nofallback". + UResourceDataEntry *t1 = r; + if(r != nullptr && uprv_strcmp(localeID, kRootLocaleName) != 0 && // not root + r->fParent == nullptr && !r->fData.noFallback && + uprv_strlen(localeID) < ULOC_FULLNAME_CAPACITY) { + char name[ULOC_FULLNAME_CAPACITY]; + uprv_strcpy(name, localeID); + if(!chopLocale(name) || uprv_strcmp(name, kRootLocaleName) == 0 || + loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), false, nullptr, status)) { + if(uprv_strcmp(t1->fName, kRootLocaleName) != 0 && t1->fParent == nullptr) { + insertRootBundle(t1, status); + } + } + if(U_FAILURE(*status)) { + r = nullptr; + } + } + + if(r != nullptr) { + // TODO: Does this ever loop? + while(t1->fParent != nullptr) { + t1->fParent->fCountExisting++; + t1 = t1->fParent; + } + } + return r; +} + +/** + * Functions to create and destroy resource bundles. + * CAUTION: resbMutex must be locked when calling this function. + */ +/* INTERNAL: */ +static void entryCloseInt(UResourceDataEntry *resB) { + UResourceDataEntry *p = resB; + + while(resB != nullptr) { + p = resB->fParent; + resB->fCountExisting--; + + /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush + of the cache. */ +/* + if(resB->fCountExisting <= 0) { + uhash_remove(cache, resB); + if(resB->fBogus == U_ZERO_ERROR) { + res_unload(&(resB->fData)); + } + if(resB->fName != nullptr) { + uprv_free(resB->fName); + } + if(resB->fPath != nullptr) { + uprv_free(resB->fPath); + } + uprv_free(resB); + } +*/ + + resB = p; + } +} + +/** + * API: closes a resource bundle and cleans up. + */ + +static void entryClose(UResourceDataEntry *resB) { + Mutex lock(&resbMutex); + entryCloseInt(resB); +} + +/* +U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) { + if(resB->fResPath == nullptr) { + resB->fResPath = resB->fResBuf; + *(resB->fResPath) = 0; + } + resB->fResPathLen = uprv_strlen(toAdd); + if(RES_BUFSIZE <= resB->fResPathLen+1) { + if(resB->fResPath == resB->fResBuf) { + resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char)); + } else { + resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char)); + } + } + uprv_strcpy(resB->fResPath, toAdd); +} +*/ +static void ures_appendResPath(UResourceBundle *resB, const char* toAdd, int32_t lenToAdd, UErrorCode *status) { + int32_t resPathLenOrig = resB->fResPathLen; + if(resB->fResPath == nullptr) { + resB->fResPath = resB->fResBuf; + *(resB->fResPath) = 0; + resB->fResPathLen = 0; + } + resB->fResPathLen += lenToAdd; + if(RES_BUFSIZE <= resB->fResPathLen+1) { + if(resB->fResPath == resB->fResBuf) { + resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char)); + /* Check that memory was allocated correctly. */ + if (resB->fResPath == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_strcpy(resB->fResPath, resB->fResBuf); + } else { + char *temp = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char)); + /* Check that memory was reallocated correctly. */ + if (temp == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return; + } + resB->fResPath = temp; + } + } + uprv_strcpy(resB->fResPath + resPathLenOrig, toAdd); +} + +static void ures_freeResPath(UResourceBundle *resB) { + if (resB->fResPath && resB->fResPath != resB->fResBuf) { + uprv_free(resB->fResPath); + } + resB->fResPath = nullptr; + resB->fResPathLen = 0; +} + +static void +ures_closeBundle(UResourceBundle* resB, UBool freeBundleObj) +{ + if(resB != nullptr) { + if(resB->fData != nullptr) { + entryClose(resB->fData); + } + if(resB->fVersion != nullptr) { + uprv_free(resB->fVersion); + } + ures_freeResPath(resB); + + if(ures_isStackObject(resB) == false && freeBundleObj) { + uprv_free(resB); + } +#if 0 /*U_DEBUG*/ + else { + /* poison the data */ + uprv_memset(resB, -1, sizeof(UResourceBundle)); + } +#endif + } +} + +U_CAPI void U_EXPORT2 +ures_close(UResourceBundle* resB) +{ + ures_closeBundle(resB, true); +} + +namespace { + +UResourceBundle *init_resb_result( + UResourceDataEntry *dataEntry, Resource r, const char *key, int32_t idx, + UResourceDataEntry *validLocaleDataEntry, const char *containerResPath, + int32_t recursionDepth, + UResourceBundle *resB, UErrorCode *status); + +// TODO: Try to refactor further, so that we output a dataEntry + Resource + (optionally) resPath, +// rather than a UResourceBundle. +// May need to entryIncrease() the resulting dataEntry. +UResourceBundle *getAliasTargetAsResourceBundle( + const ResourceData &resData, Resource r, const char *key, int32_t idx, + UResourceDataEntry *validLocaleDataEntry, const char *containerResPath, + int32_t recursionDepth, + UResourceBundle *resB, UErrorCode *status) { + // TODO: When an error occurs: Should we return nullptr vs. resB? + if (U_FAILURE(*status)) { return resB; } + U_ASSERT(RES_GET_TYPE(r) == URES_ALIAS); + int32_t len = 0; + const char16_t *alias = res_getAlias(&resData, r, &len); + if(len <= 0) { + // bad alias + *status = U_ILLEGAL_ARGUMENT_ERROR; + return resB; + } + + // Copy the UTF-16 alias string into an invariant-character string. + // + // We do this so that res_findResource() can modify the path, + // which allows us to remove redundant _res_findResource() variants + // in uresdata.c. + // res_findResource() now NUL-terminates each segment so that table keys + // can always be compared with strcmp() instead of strncmp(). + // Saves code there and simplifies testing and code coverage. + // + // markus 2003oct17 + CharString chAlias; + chAlias.appendInvariantChars(alias, len, *status); + if (U_FAILURE(*status)) { + return nullptr; + } + + // We have an alias, now let's cut it up. + const char *path = nullptr, *locale = nullptr, *keyPath = nullptr; + if(chAlias[0] == RES_PATH_SEPARATOR) { + // There is a path included. + char *chAliasData = chAlias.data(); + char *sep = chAliasData + 1; + path = sep; + sep = uprv_strchr(sep, RES_PATH_SEPARATOR); + if(sep != nullptr) { + *sep++ = 0; + } + if(uprv_strcmp(path, "LOCALE") == 0) { + // This is an XPath alias, starting with "/LOCALE/". + // It contains the path to a resource which should be looked up + // starting in the valid locale. + // TODO: Can/should we forbid a /LOCALE alias without key path? + // It seems weird to alias to the same path, just starting from the valid locale. + // That will often yield an infinite loop. + keyPath = sep; + // Read from the valid locale which we already have. + path = locale = nullptr; + } else { + if(uprv_strcmp(path, "ICUDATA") == 0) { /* want ICU data */ + path = nullptr; + } + if (sep == nullptr) { + // TODO: This ends up using the root bundle. Can/should we forbid this? + locale = ""; + } else { + locale = sep; + sep = uprv_strchr(sep, RES_PATH_SEPARATOR); + if(sep != nullptr) { + *sep++ = 0; + } + keyPath = sep; + } + } + } else { + // No path, start with a locale. + char *sep = chAlias.data(); + locale = sep; + sep = uprv_strchr(sep, RES_PATH_SEPARATOR); + if(sep != nullptr) { + *sep++ = 0; + } + keyPath = sep; + path = validLocaleDataEntry->fPath; + } + + // Got almost everything, let's try to open. + // First, open the bundle with real data. + LocalUResourceBundlePointer mainRes; + UResourceDataEntry *dataEntry; + if (locale == nullptr) { + // alias = /LOCALE/keyPath + // Read from the valid locale which we already have. + dataEntry = validLocaleDataEntry; + } else { + UErrorCode intStatus = U_ZERO_ERROR; + // TODO: Shouldn't we use ures_open() for locale data bundles (!noFallback)? + mainRes.adoptInstead(ures_openDirect(path, locale, &intStatus)); + if(U_FAILURE(intStatus)) { + // We failed to open the resource bundle we're aliasing to. + *status = intStatus; + return resB; + } + dataEntry = mainRes->fData; + } + + const char* temp = nullptr; + if(keyPath == nullptr) { + // No key path. This means that we are going to to use the corresponding resource from + // another bundle. + // TODO: Why the special code path? + // Why not put together a key path from containerResPath + key or idx, + // as a comment below suggests, and go into the regular code branch? + // First, we are going to get a corresponding container + // resource to the one we are searching. + r = dataEntry->fData.rootRes; + if(containerResPath) { + chAlias.clear().append(containerResPath, *status); + if (U_FAILURE(*status)) { + return nullptr; + } + char *aKey = chAlias.data(); + // TODO: should res_findResource() return a new dataEntry, too? + r = res_findResource(&dataEntry->fData, r, &aKey, &temp); + } + if(key) { + // We need to make keyPath from the containerResPath and + // current key, if there is a key associated. + chAlias.clear().append(key, *status); + if (U_FAILURE(*status)) { + return nullptr; + } + char *aKey = chAlias.data(); + r = res_findResource(&dataEntry->fData, r, &aKey, &temp); + } else if(idx != -1) { + // If there is no key, but there is an index, try to get by the index. + // Here we have either a table or an array, so get the element. + int32_t type = RES_GET_TYPE(r); + if(URES_IS_TABLE(type)) { + const char *aKey; + r = res_getTableItemByIndex(&dataEntry->fData, r, idx, &aKey); + } else { /* array */ + r = res_getArrayItem(&dataEntry->fData, r, idx); + } + } + if(r != RES_BOGUS) { + resB = init_resb_result( + dataEntry, r, temp, -1, validLocaleDataEntry, nullptr, recursionDepth+1, + resB, status); + } else { + *status = U_MISSING_RESOURCE_ERROR; + } + } else { + // This one is a bit trickier. + // We start finding keys, but after we resolve one alias, the path might continue. + // Consider: + // aliastest:alias { "testtypes/anotheralias/Sequence" } + // anotheralias:alias { "/ICUDATA/sh/CollationElements" } + // aliastest resource should finally have the sequence, not collation elements. + CharString pathBuf(keyPath, *status); + if (U_FAILURE(*status)) { + return nullptr; + } + char *myPath = pathBuf.data(); + containerResPath = nullptr; + // Now we have fallback following here. + for(;;) { + r = dataEntry->fData.rootRes; + // TODO: Move containerResPath = nullptr to here, + // consistent with restarting from the rootRes of another bundle?! + + // This loop handles 'found' resources over several levels. + while(*myPath && U_SUCCESS(*status)) { + r = res_findResource(&(dataEntry->fData), r, &myPath, &temp); + if(r == RES_BOGUS) { + // No resource found, we don't really want to look anymore on this level. + break; + } + // Found a resource, but it might be an indirection. + resB = init_resb_result( + dataEntry, r, temp, -1, + validLocaleDataEntry, containerResPath, recursionDepth+1, + resB, status); + if (U_FAILURE(*status)) { + break; + } + if (temp == nullptr || uprv_strcmp(keyPath, temp) != 0) { + // The call to init_resb_result() above will set resB->fKeyPath to be + // the same as resB->fKey, + // throwing away any additional path elements if we had them -- + // if the key path wasn't just a single resource ID, clear out + // the bundle's key path and re-set it to be equal to keyPath. + ures_freeResPath(resB); + ures_appendResPath(resB, keyPath, (int32_t)uprv_strlen(keyPath), status); + if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) { + ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status); + } + if (U_FAILURE(*status)) { + break; + } + } + r = resB->fRes; /* switch to a new resource, possibly a new tree */ + dataEntry = resB->fData; + containerResPath = resB->fResPath; + } + if (U_FAILURE(*status) || r != RES_BOGUS) { + break; + } + // Fall back to the parent bundle, if there is one. + dataEntry = dataEntry->fParent; + if (dataEntry == nullptr) { + *status = U_MISSING_RESOURCE_ERROR; + break; + } + // Copy the same keyPath again. + myPath = pathBuf.data(); + uprv_strcpy(myPath, keyPath); + } + } + if(mainRes.getAlias() == resB) { + mainRes.orphan(); + } + ResourceTracer(resB).maybeTrace("getalias"); + return resB; +} + +// Recursive function, should be called only by itself, by its simpler wrapper, +// or by getAliasTargetAsResourceBundle(). +UResourceBundle *init_resb_result( + UResourceDataEntry *dataEntry, Resource r, const char *key, int32_t idx, + UResourceDataEntry *validLocaleDataEntry, const char *containerResPath, + int32_t recursionDepth, + UResourceBundle *resB, UErrorCode *status) { + // TODO: When an error occurs: Should we return nullptr vs. resB? + if(status == nullptr || U_FAILURE(*status)) { + return resB; + } + if (validLocaleDataEntry == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + if(RES_GET_TYPE(r) == URES_ALIAS) { + // This is an alias, need to exchange with real data. + if(recursionDepth >= URES_MAX_ALIAS_LEVEL) { + *status = U_TOO_MANY_ALIASES_ERROR; + return resB; + } + return getAliasTargetAsResourceBundle( + dataEntry->fData, r, key, idx, + validLocaleDataEntry, containerResPath, recursionDepth, resB, status); + } + if(resB == nullptr) { + resB = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle)); + if (resB == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + ures_setIsStackObject(resB, false); + resB->fResPath = nullptr; + resB->fResPathLen = 0; + } else { + if(resB->fData != nullptr) { + entryClose(resB->fData); + } + if(resB->fVersion != nullptr) { + uprv_free(resB->fVersion); + } + /* + weiv: if stack object was passed in, it doesn't really need to be reinited, + since the purpose of initing is to remove stack junk. However, at this point + we would not do anything to an allocated object, so stack object should be + treated the same + */ + /* + if(ures_isStackObject(resB) != false) { + ures_initStackObject(resB); + } + */ + if(containerResPath != resB->fResPath) { + ures_freeResPath(resB); + } + } + resB->fData = dataEntry; + entryIncrease(resB->fData); + resB->fHasFallback = false; + resB->fIsTopLevel = false; + resB->fIndex = -1; + resB->fKey = key; + resB->fValidLocaleDataEntry = validLocaleDataEntry; + if(containerResPath != resB->fResPath) { + ures_appendResPath( + resB, containerResPath, static_cast(uprv_strlen(containerResPath)), status); + } + if(key != nullptr) { + ures_appendResPath(resB, key, (int32_t)uprv_strlen(key), status); + if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) { + ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status); + } + } else if(idx >= 0) { + char buf[256]; + int32_t len = T_CString_integerToString(buf, idx, 10); + ures_appendResPath(resB, buf, len, status); + if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) { + ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status); + } + } + /* Make sure that Purify doesn't complain about uninitialized memory copies. */ + { + int32_t usedLen = ((resB->fResBuf == resB->fResPath) ? resB->fResPathLen : 0); + uprv_memset(resB->fResBuf + usedLen, 0, sizeof(resB->fResBuf) - usedLen); + } + + resB->fVersion = nullptr; + resB->fRes = r; + resB->fSize = res_countArrayItems(&resB->getResData(), resB->fRes); + ResourceTracer(resB).trace("get"); + return resB; +} + +UResourceBundle *init_resb_result( + UResourceDataEntry *dataEntry, Resource r, const char *key, int32_t idx, + // validLocaleDataEntry + containerResPath + const UResourceBundle *container, + UResourceBundle *resB, UErrorCode *status) { + return init_resb_result( + dataEntry, r, key, idx, + container->fValidLocaleDataEntry, container->fResPath, 0, resB, status); +} + +} // namespace + +UResourceBundle *ures_copyResb(UResourceBundle *r, const UResourceBundle *original, UErrorCode *status) { + UBool isStackObject; + if(U_FAILURE(*status) || r == original) { + return r; + } + if(original != nullptr) { + if(r == nullptr) { + isStackObject = false; + r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle)); + /* test for nullptr */ + if (r == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + } else { + isStackObject = ures_isStackObject(r); + ures_closeBundle(r, false); + } + uprv_memcpy(r, original, sizeof(UResourceBundle)); + r->fResPath = nullptr; + r->fResPathLen = 0; + if(original->fResPath) { + ures_appendResPath(r, original->fResPath, original->fResPathLen, status); + } + ures_setIsStackObject(r, isStackObject); + if(r->fData != nullptr) { + entryIncrease(r->fData); + } + } + return r; +} + +/** + * Functions to retrieve data from resource bundles. + */ + +U_CAPI const char16_t* U_EXPORT2 ures_getString(const UResourceBundle* resB, int32_t* len, UErrorCode* status) { + const char16_t *s; + if (status==nullptr || U_FAILURE(*status)) { + return nullptr; + } + if(resB == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + s = res_getString({resB}, &resB->getResData(), resB->fRes, len); + if (s == nullptr) { + *status = U_RESOURCE_TYPE_MISMATCH; + } + return s; +} + +static const char * +ures_toUTF8String(const char16_t *s16, int32_t length16, + char *dest, int32_t *pLength, + UBool forceCopy, + UErrorCode *status) { + int32_t capacity; + + if (U_FAILURE(*status)) { + return nullptr; + } + if (pLength != nullptr) { + capacity = *pLength; + } else { + capacity = 0; + } + if (capacity < 0 || (capacity > 0 && dest == nullptr)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + if (length16 == 0) { + /* empty string, return as read-only pointer */ + if (pLength != nullptr) { + *pLength = 0; + } + if (forceCopy) { + u_terminateChars(dest, capacity, 0, status); + return dest; + } else { + return ""; + } + } else { + /* We need to transform the string to the destination buffer. */ + if (capacity < length16) { + /* No chance for the string to fit. Pure preflighting. */ + return u_strToUTF8(nullptr, 0, pLength, s16, length16, status); + } + if (!forceCopy && (length16 <= 0x2aaaaaaa)) { + /* + * We know the string will fit into dest because each char16_t turns + * into at most three UTF-8 bytes. Fill the latter part of dest + * so that callers do not expect to use dest as a string pointer, + * hopefully leading to more robust code for when resource bundles + * may store UTF-8 natively. + * (In which case dest would not be used at all.) + * + * We do not do this if forceCopy=true because then the caller + * expects the string to start exactly at dest. + * + * The test above for <= 0x2aaaaaaa prevents overflows. + * The +1 is for the NUL terminator. + */ + int32_t maxLength = 3 * length16 + 1; + if (capacity > maxLength) { + dest += capacity - maxLength; + capacity = maxLength; + } + } + return u_strToUTF8(dest, capacity, pLength, s16, length16, status); + } +} + +U_CAPI const char * U_EXPORT2 +ures_getUTF8String(const UResourceBundle *resB, + char *dest, int32_t *pLength, + UBool forceCopy, + UErrorCode *status) { + int32_t length16; + const char16_t *s16 = ures_getString(resB, &length16, status); + return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status); +} + +U_CAPI const uint8_t* U_EXPORT2 ures_getBinary(const UResourceBundle* resB, int32_t* len, + UErrorCode* status) { + const uint8_t *p; + if (status==nullptr || U_FAILURE(*status)) { + return nullptr; + } + if(resB == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + p = res_getBinary({resB}, &resB->getResData(), resB->fRes, len); + if (p == nullptr) { + *status = U_RESOURCE_TYPE_MISMATCH; + } + return p; +} + +U_CAPI const int32_t* U_EXPORT2 ures_getIntVector(const UResourceBundle* resB, int32_t* len, + UErrorCode* status) { + const int32_t *p; + if (status==nullptr || U_FAILURE(*status)) { + return nullptr; + } + if(resB == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + p = res_getIntVector({resB}, &resB->getResData(), resB->fRes, len); + if (p == nullptr) { + *status = U_RESOURCE_TYPE_MISMATCH; + } + return p; +} + +/* this function returns a signed integer */ +/* it performs sign extension */ +U_CAPI int32_t U_EXPORT2 ures_getInt(const UResourceBundle* resB, UErrorCode *status) { + if (status==nullptr || U_FAILURE(*status)) { + return 0xffffffff; + } + if(resB == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0xffffffff; + } + if(RES_GET_TYPE(resB->fRes) != URES_INT) { + *status = U_RESOURCE_TYPE_MISMATCH; + return 0xffffffff; + } + return res_getInt({resB}, resB->fRes); +} + +U_CAPI uint32_t U_EXPORT2 ures_getUInt(const UResourceBundle* resB, UErrorCode *status) { + if (status==nullptr || U_FAILURE(*status)) { + return 0xffffffff; + } + if(resB == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0xffffffff; + } + if(RES_GET_TYPE(resB->fRes) != URES_INT) { + *status = U_RESOURCE_TYPE_MISMATCH; + return 0xffffffff; + } + return res_getUInt({resB}, resB->fRes); +} + +U_CAPI UResType U_EXPORT2 ures_getType(const UResourceBundle *resB) { + if(resB == nullptr) { + return URES_NONE; + } + return res_getPublicType(resB->fRes); +} + +U_CAPI const char * U_EXPORT2 ures_getKey(const UResourceBundle *resB) { + // + // TODO: Trace ures_getKey? I guess not usually. + // + // We usually get the key string to decide whether we want the value, or to + // make a key-value pair. Tracing the value should suffice. + // + // However, I believe we have some data (e.g., in res_index) where the key + // strings are the data. Tracing the enclosing table should suffice. + // + if(resB == nullptr) { + return nullptr; + } + return(resB->fKey); +} + +U_CAPI int32_t U_EXPORT2 ures_getSize(const UResourceBundle *resB) { + if(resB == nullptr) { + return 0; + } + + return resB->fSize; +} + +static const char16_t* ures_getStringWithAlias(const UResourceBundle *resB, Resource r, int32_t sIndex, int32_t *len, UErrorCode *status) { + if(RES_GET_TYPE(r) == URES_ALIAS) { + const char16_t* result = 0; + UResourceBundle *tempRes = ures_getByIndex(resB, sIndex, nullptr, status); + result = ures_getString(tempRes, len, status); + ures_close(tempRes); + return result; + } else { + return res_getString({resB, sIndex}, &resB->getResData(), r, len); + } +} + +U_CAPI void U_EXPORT2 ures_resetIterator(UResourceBundle *resB){ + if(resB == nullptr) { + return; + } + resB->fIndex = -1; +} + +U_CAPI UBool U_EXPORT2 ures_hasNext(const UResourceBundle *resB) { + if(resB == nullptr) { + return false; + } + return (UBool)(resB->fIndex < resB->fSize-1); +} + +U_CAPI const char16_t* U_EXPORT2 ures_getNextString(UResourceBundle *resB, int32_t* len, const char ** key, UErrorCode *status) { + Resource r = RES_BOGUS; + + if (status==nullptr || U_FAILURE(*status)) { + return nullptr; + } + if(resB == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + if(resB->fIndex == resB->fSize-1) { + *status = U_INDEX_OUTOFBOUNDS_ERROR; + } else { + resB->fIndex++; + switch(RES_GET_TYPE(resB->fRes)) { + case URES_STRING: + case URES_STRING_V2: + return res_getString({resB}, &resB->getResData(), resB->fRes, len); + case URES_TABLE: + case URES_TABLE16: + case URES_TABLE32: + r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, resB->fIndex, key); + if(r == RES_BOGUS && resB->fHasFallback) { + /* TODO: do the fallback */ + } + return ures_getStringWithAlias(resB, r, resB->fIndex, len, status); + case URES_ARRAY: + case URES_ARRAY16: + r = res_getArrayItem(&resB->getResData(), resB->fRes, resB->fIndex); + if(r == RES_BOGUS && resB->fHasFallback) { + /* TODO: do the fallback */ + } + return ures_getStringWithAlias(resB, r, resB->fIndex, len, status); + case URES_ALIAS: + return ures_getStringWithAlias(resB, resB->fRes, resB->fIndex, len, status); + case URES_INT: + case URES_BINARY: + case URES_INT_VECTOR: + *status = U_RESOURCE_TYPE_MISMATCH; + U_FALLTHROUGH; + default: + return nullptr; + } + } + + return nullptr; +} + +U_CAPI UResourceBundle* U_EXPORT2 ures_getNextResource(UResourceBundle *resB, UResourceBundle *fillIn, UErrorCode *status) { + const char *key = nullptr; + Resource r = RES_BOGUS; + + if (status==nullptr || U_FAILURE(*status)) { + /*return nullptr;*/ + return fillIn; + } + if(resB == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + /*return nullptr;*/ + return fillIn; + } + + if(resB->fIndex == resB->fSize-1) { + *status = U_INDEX_OUTOFBOUNDS_ERROR; + /*return nullptr;*/ + } else { + resB->fIndex++; + switch(RES_GET_TYPE(resB->fRes)) { + case URES_INT: + case URES_BINARY: + case URES_STRING: + case URES_STRING_V2: + case URES_INT_VECTOR: + return ures_copyResb(fillIn, resB, status); + case URES_TABLE: + case URES_TABLE16: + case URES_TABLE32: + r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, resB->fIndex, &key); + if(r == RES_BOGUS && resB->fHasFallback) { + /* TODO: do the fallback */ + } + return init_resb_result(resB->fData, r, key, resB->fIndex, resB, fillIn, status); + case URES_ARRAY: + case URES_ARRAY16: + r = res_getArrayItem(&resB->getResData(), resB->fRes, resB->fIndex); + if(r == RES_BOGUS && resB->fHasFallback) { + /* TODO: do the fallback */ + } + return init_resb_result(resB->fData, r, key, resB->fIndex, resB, fillIn, status); + default: + /*return nullptr;*/ + return fillIn; + } + } + /*return nullptr;*/ + return fillIn; +} + +U_CAPI UResourceBundle* U_EXPORT2 ures_getByIndex(const UResourceBundle *resB, int32_t indexR, UResourceBundle *fillIn, UErrorCode *status) { + const char* key = nullptr; + Resource r = RES_BOGUS; + + if (status==nullptr || U_FAILURE(*status)) { + /*return nullptr;*/ + return fillIn; + } + if(resB == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + /*return nullptr;*/ + return fillIn; + } + + if(indexR >= 0 && resB->fSize > indexR) { + switch(RES_GET_TYPE(resB->fRes)) { + case URES_INT: + case URES_BINARY: + case URES_STRING: + case URES_STRING_V2: + case URES_INT_VECTOR: + return ures_copyResb(fillIn, resB, status); + case URES_TABLE: + case URES_TABLE16: + case URES_TABLE32: + r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, indexR, &key); + if(r == RES_BOGUS && resB->fHasFallback) { + /* TODO: do the fallback */ + } + return init_resb_result(resB->fData, r, key, indexR, resB, fillIn, status); + case URES_ARRAY: + case URES_ARRAY16: + r = res_getArrayItem(&resB->getResData(), resB->fRes, indexR); + if(r == RES_BOGUS && resB->fHasFallback) { + /* TODO: do the fallback */ + } + return init_resb_result(resB->fData, r, key, indexR, resB, fillIn, status); + default: + /*return nullptr;*/ + return fillIn; + } + } else { + *status = U_MISSING_RESOURCE_ERROR; + } + /*return nullptr;*/ + return fillIn; +} + +U_CAPI const char16_t* U_EXPORT2 ures_getStringByIndex(const UResourceBundle *resB, int32_t indexS, int32_t* len, UErrorCode *status) { + const char* key = nullptr; + Resource r = RES_BOGUS; + + if (status==nullptr || U_FAILURE(*status)) { + return nullptr; + } + if(resB == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + if(indexS >= 0 && resB->fSize > indexS) { + switch(RES_GET_TYPE(resB->fRes)) { + case URES_STRING: + case URES_STRING_V2: + return res_getString({resB}, &resB->getResData(), resB->fRes, len); + case URES_TABLE: + case URES_TABLE16: + case URES_TABLE32: + r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, indexS, &key); + if(r == RES_BOGUS && resB->fHasFallback) { + /* TODO: do the fallback */ + } + return ures_getStringWithAlias(resB, r, indexS, len, status); + case URES_ARRAY: + case URES_ARRAY16: + r = res_getArrayItem(&resB->getResData(), resB->fRes, indexS); + if(r == RES_BOGUS && resB->fHasFallback) { + /* TODO: do the fallback */ + } + return ures_getStringWithAlias(resB, r, indexS, len, status); + case URES_ALIAS: + return ures_getStringWithAlias(resB, resB->fRes, indexS, len, status); + case URES_INT: + case URES_BINARY: + case URES_INT_VECTOR: + *status = U_RESOURCE_TYPE_MISMATCH; + break; + default: + /* must not occur */ + *status = U_INTERNAL_PROGRAM_ERROR; + break; + } + } else { + *status = U_MISSING_RESOURCE_ERROR; + } + return nullptr; +} + +U_CAPI const char * U_EXPORT2 +ures_getUTF8StringByIndex(const UResourceBundle *resB, + int32_t idx, + char *dest, int32_t *pLength, + UBool forceCopy, + UErrorCode *status) { + int32_t length16; + const char16_t *s16 = ures_getStringByIndex(resB, idx, &length16, status); + return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status); +} + +/*U_CAPI const char *ures_getResPath(UResourceBundle *resB) { + return resB->fResPath; +}*/ + +U_CAPI UResourceBundle* U_EXPORT2 +ures_findResource(const char* path, UResourceBundle *fillIn, UErrorCode *status) +{ + UResourceBundle *first = nullptr; + UResourceBundle *result = fillIn; + char *packageName = nullptr; + char *pathToResource = nullptr, *save = nullptr; + char *locale = nullptr, *localeEnd = nullptr; + int32_t length; + + if(status == nullptr || U_FAILURE(*status)) { + return result; + } + + length = (int32_t)(uprv_strlen(path)+1); + save = pathToResource = (char *)uprv_malloc(length*sizeof(char)); + /* test for nullptr */ + if(pathToResource == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return result; + } + uprv_memcpy(pathToResource, path, length); + + locale = pathToResource; + if(*pathToResource == RES_PATH_SEPARATOR) { /* there is a path specification */ + pathToResource++; + packageName = pathToResource; + pathToResource = uprv_strchr(pathToResource, RES_PATH_SEPARATOR); + if(pathToResource == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + } else { + *pathToResource = 0; + locale = pathToResource+1; + } + } + + localeEnd = uprv_strchr(locale, RES_PATH_SEPARATOR); + if(localeEnd != nullptr) { + *localeEnd = 0; + } + + first = ures_open(packageName, locale, status); + + if(U_SUCCESS(*status)) { + if(localeEnd) { + result = ures_findSubResource(first, localeEnd+1, fillIn, status); + } else { + result = ures_copyResb(fillIn, first, status); + } + ures_close(first); + } + uprv_free(save); + return result; +} + +U_CAPI UResourceBundle* U_EXPORT2 +ures_findSubResource(const UResourceBundle *resB, char* path, UResourceBundle *fillIn, UErrorCode *status) +{ + Resource res = RES_BOGUS; + UResourceBundle *result = fillIn; + const char *key; + + if(status == nullptr || U_FAILURE(*status)) { + return result; + } + + /* here we do looping and circular alias checking */ + /* this loop is here because aliasing is resolved on this level, not on res level */ + /* so, when we encounter an alias, it is not an aggregate resource, so we return */ + do { + res = res_findResource(&resB->getResData(), resB->fRes, &path, &key); + if(res != RES_BOGUS) { + result = init_resb_result(resB->fData, res, key, -1, resB, fillIn, status); + resB = result; + } else { + *status = U_MISSING_RESOURCE_ERROR; + break; + } + } while(*path); /* there is more stuff in the path */ + + return result; +} +U_CAPI const char16_t* U_EXPORT2 +ures_getStringByKeyWithFallback(const UResourceBundle *resB, + const char* inKey, + int32_t* len, + UErrorCode *status) { + + UResourceBundle stack; + const char16_t* retVal = nullptr; + ures_initStackObject(&stack); + ures_getByKeyWithFallback(resB, inKey, &stack, status); + int32_t length; + retVal = ures_getString(&stack, &length, status); + ures_close(&stack); + if (U_FAILURE(*status)) { + return nullptr; + } + if (length == 3 && retVal[0] == EMPTY_SET && retVal[1] == EMPTY_SET && retVal[2] == EMPTY_SET ) { + retVal = nullptr; + length = 0; + *status = U_MISSING_RESOURCE_ERROR; + } + if (len != nullptr) { + *len = length; + } + return retVal; +} + +/* + Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort". +*/ +static Resource getTableItemByKeyPath(const ResourceData *pResData, Resource table, const char *key) { + Resource resource = table; /* The current resource */ + icu::CharString path; + UErrorCode errorCode = U_ZERO_ERROR; + path.append(key, errorCode); + if (U_FAILURE(errorCode)) { return RES_BOGUS; } + char *pathPart = path.data(); /* Path from current resource to desired resource */ + UResType type = (UResType)RES_GET_TYPE(resource); /* the current resource type */ + while (*pathPart && resource != RES_BOGUS && URES_IS_CONTAINER(type)) { + char *nextPathPart = uprv_strchr(pathPart, RES_PATH_SEPARATOR); + if (nextPathPart != nullptr) { + *nextPathPart = 0; /* Terminating null for this part of path. */ + nextPathPart++; + } else { + nextPathPart = uprv_strchr(pathPart, 0); + } + int32_t t; + const char *pathP = pathPart; + resource = res_getTableItemByKey(pResData, resource, &t, &pathP); + type = (UResType)RES_GET_TYPE(resource); + pathPart = nextPathPart; + } + if (*pathPart) { + return RES_BOGUS; + } + return resource; +} + +static void createPath(const char* origResPath, + int32_t origResPathLen, + const char* resPath, + int32_t resPathLen, + const char* inKey, + CharString& path, + UErrorCode* status) { + // This is a utility function used by ures_getByKeyWithFallback() below. This function builds a path from + // resPath and inKey, returning the result in `path`. Originally, this function just cleared `path` and + // appended resPath and inKey to it, but that caused problems for horizontal inheritance. + // + // In normal cases, resPath is the same as origResPath, but if ures_getByKeyWithFallback() has followed an + // alias, resPath may be different from origResPath. Not only may the existing path elements be different, + // but resPath may also have MORE path elements than origResPath did. If it does, those additional path + // elements SUPERSEDE the corresponding elements of inKey. So this code counts the number of elements in + // resPath and origResPath and, for each path element in resPath that doesn't have a counterpart in origResPath, + // deletes a path element from the beginning of inKey. The remainder of inKey is then appended to + // resPath to form the result. (We're not using uprv_strchr() here because resPath and origResPath may + // not be zero-terminated.) + path.clear(); + const char* key = inKey; + if (resPathLen > 0) { + path.append(resPath, resPathLen, *status); + if (U_SUCCESS(*status)) { + const char* resPathLimit = resPath + resPathLen; + const char* origResPathLimit = origResPath + origResPathLen; + const char* resPathPtr = resPath; + const char* origResPathPtr = origResPath; + + // Remove from the beginning of resPath the number of segments that are contained in origResPath. + // If origResPath has MORE segments than resPath, this will leave resPath as the empty string. + while (origResPathPtr < origResPathLimit && resPathPtr < resPathLimit) { + while (origResPathPtr < origResPathLimit && *origResPathPtr != RES_PATH_SEPARATOR) { + ++origResPathPtr; + } + if (origResPathPtr < origResPathLimit && *origResPathPtr == RES_PATH_SEPARATOR) { + ++origResPathPtr; + } + while (resPathPtr < resPathLimit && *resPathPtr != RES_PATH_SEPARATOR) { + ++resPathPtr; + } + if (resPathPtr < resPathLimit && *resPathPtr == RES_PATH_SEPARATOR) { + ++resPathPtr; + } + } + + // New remove from the beginning of `key` the number of segments remaining in resPath. + // If resPath has more segments than `key` does, `key` will end up empty. + while (resPathPtr < resPathLimit && *key != '\0') { + while (resPathPtr < resPathLimit && *resPathPtr != RES_PATH_SEPARATOR) { + ++resPathPtr; + } + if (resPathPtr < resPathLimit && *resPathPtr == RES_PATH_SEPARATOR) { + ++resPathPtr; + } + while (*key != '\0' && *key != RES_PATH_SEPARATOR) { + ++key; + } + if (*key == RES_PATH_SEPARATOR) { + ++key; + } + } + } + // Finally, append what's left of `key` to `path`. What you end up with here is `resPath`, plus + // any pieces of `key` that aren't superseded by `resPath`. + // Or, to put it another way, calculate <#-segments-in-key> - (<#-segments-in-resPath> - <#-segments-in-origResPath>), + // and append that many segments from the end of `key` to `resPath` to produce the result. + path.append(key, *status); + } else { + path.append(inKey, *status); + } +} + +U_CAPI UResourceBundle* U_EXPORT2 +ures_getByKeyWithFallback(const UResourceBundle *resB, + const char* inKey, + UResourceBundle *fillIn, + UErrorCode *status) { + Resource res = RES_BOGUS, rootRes = RES_BOGUS; + UResourceBundle *helper = nullptr; + + if (status==nullptr || U_FAILURE(*status)) { + return fillIn; + } + if(resB == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return fillIn; + } + + int32_t type = RES_GET_TYPE(resB->fRes); + if(URES_IS_TABLE(type)) { + const char* origResPath = resB->fResPath; + int32_t origResPathLen = resB->fResPathLen; + res = getTableItemByKeyPath(&resB->getResData(), resB->fRes, inKey); + const char* key = inKey; + bool didRootOnce = false; + if(res == RES_BOGUS) { + UResourceDataEntry *dataEntry = resB->fData; + CharString path; + char *myPath = nullptr; + const char* resPath = resB->fResPath; + int32_t len = resB->fResPathLen; + while(res == RES_BOGUS && (dataEntry->fParent != nullptr || !didRootOnce)) { /* Otherwise, we'll look in parents */ + if (dataEntry->fParent != nullptr) { + dataEntry = dataEntry->fParent; + } else { + // We can't just stop when we get to a bundle whose fParent is nullptr. That'll work most of the time, + // but if the bundle that the caller passed to us was "root" (which happens in getAllItemsWithFallback(), + // this function will drop right out without doing anything if "root" doesn't contain the exact key path + // specified. In that case, we need one extra time through this loop to make sure we follow any + // applicable aliases at the root level. + didRootOnce = true; + } + rootRes = dataEntry->fData.rootRes; + + if(dataEntry->fBogus == U_ZERO_ERROR) { + createPath(origResPath, origResPathLen, resPath, len, inKey, path, status); + if (U_FAILURE(*status)) { + ures_close(helper); + return fillIn; + } + myPath = path.data(); + key = inKey; + do { + res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key); + if (RES_GET_TYPE(res) == URES_ALIAS && *myPath) { + /* We hit an alias, but we didn't finish following the path. */ + helper = init_resb_result(dataEntry, res, nullptr, -1, resB, helper, status); + /*helper = init_resb_result(dataEntry, res, inKey, -1, resB, helper, status);*/ + if(helper) { + dataEntry = helper->fData; + rootRes = helper->fRes; + resPath = helper->fResPath; + len = helper->fResPathLen; + + } else { + break; + } + } else if (res == RES_BOGUS) { + break; + } + } while(*myPath); /* Continue until the whole path is consumed */ + } + } + /*dataEntry = getFallbackData(resB, &key, &res, status);*/ + if(res != RES_BOGUS) { + /* check if resB->fResPath gives the right name here */ + if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) { + *status = U_USING_DEFAULT_WARNING; + } else { + *status = U_USING_FALLBACK_WARNING; + } + + fillIn = init_resb_result(dataEntry, res, key, -1, resB, fillIn, status); + if (resPath != nullptr) { + createPath(origResPath, origResPathLen, resPath, len, inKey, path, status); + } else { + const char* separator = nullptr; + if (fillIn->fResPath != nullptr) { + separator = uprv_strchr(fillIn->fResPath, RES_PATH_SEPARATOR); + } + if (separator != nullptr && separator[1] != '\0') { + createPath(origResPath, origResPathLen, fillIn->fResPath, + static_cast(uprv_strlen(fillIn->fResPath)), inKey, path, status); + } else { + createPath(origResPath, origResPathLen, "", 0, inKey, path, status); + } + } + ures_freeResPath(fillIn); + ures_appendResPath(fillIn, path.data(), path.length(), status); + if(fillIn->fResPath[fillIn->fResPathLen-1] != RES_PATH_SEPARATOR) { + ures_appendResPath(fillIn, RES_PATH_SEPARATOR_S, 1, status); + } + } else { + *status = U_MISSING_RESOURCE_ERROR; + } + } else { + fillIn = init_resb_result(resB->fData, res, key, -1, resB, fillIn, status); + } + } + else { + *status = U_RESOURCE_TYPE_MISMATCH; + } + ures_close(helper); + return fillIn; +} + +namespace { + +void getAllItemsWithFallback( + const UResourceBundle *bundle, ResourceDataValue &value, + ResourceSink &sink, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return; } + // We recursively enumerate child-first, + // only storing parent items in the absence of child items. + // The sink needs to store a placeholder value for the no-fallback/no-inheritance marker + // to prevent a parent item from being stored. + // + // It would be possible to recursively enumerate parent-first, + // overriding parent items with child items. + // When the sink sees the no-fallback/no-inheritance marker, + // then it would remove the parent's item. + // We would deserialize parent values even though they are overridden in a child bundle. + value.setData(bundle->getResData()); + value.setValidLocaleDataEntry(bundle->fValidLocaleDataEntry); + UResourceDataEntry *parentEntry = bundle->fData->fParent; + UBool hasParent = parentEntry != nullptr && U_SUCCESS(parentEntry->fBogus); + value.setResource(bundle->fRes, ResourceTracer(bundle)); + sink.put(bundle->fKey, value, !hasParent, errorCode); + if (hasParent) { + // We might try to query the sink whether + // any fallback from the parent bundle is still possible. + + // Turn the parent UResourceDataEntry into a UResourceBundle, + // much like in ures_openWithType(). + // TODO: See if we can refactor ures_getByKeyWithFallback() + // and pull out an inner function that takes and returns a UResourceDataEntry + // so that we need not create UResourceBundle objects. + StackUResourceBundle parentBundle; + UResourceBundle &parentRef = parentBundle.ref(); + parentRef.fData = parentEntry; + parentRef.fValidLocaleDataEntry = bundle->fValidLocaleDataEntry; + parentRef.fHasFallback = !parentRef.getResData().noFallback; + parentRef.fIsTopLevel = true; + parentRef.fRes = parentRef.getResData().rootRes; + parentRef.fSize = res_countArrayItems(&parentRef.getResData(), parentRef.fRes); + parentRef.fIndex = -1; + entryIncrease(parentEntry); + + // Look up the container item in the parent bundle. + StackUResourceBundle containerBundle; + const UResourceBundle *rb; + UErrorCode pathErrorCode = U_ZERO_ERROR; // Ignore if parents up to root do not have this path. + if (bundle->fResPath == nullptr || *bundle->fResPath == 0) { + rb = parentBundle.getAlias(); + } else { + rb = ures_getByKeyWithFallback(parentBundle.getAlias(), bundle->fResPath, + containerBundle.getAlias(), &pathErrorCode); + } + if (U_SUCCESS(pathErrorCode)) { + getAllItemsWithFallback(rb, value, sink, errorCode); + } + } +} + +struct GetAllChildrenSink : public ResourceSink { + // Destination sink + ResourceSink& dest; + + GetAllChildrenSink(ResourceSink& dest) + : dest(dest) {} + virtual ~GetAllChildrenSink() override; + virtual void put(const char *key, ResourceValue &value, UBool isRoot, + UErrorCode &errorCode) override { + ResourceTable itemsTable = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) { + if (value.getType() == URES_ALIAS) { + ResourceDataValue& rdv = static_cast(value); + StackUResourceBundle stackTempBundle; + UResourceBundle* aliasRB = getAliasTargetAsResourceBundle(rdv.getData(), rdv.getResource(), nullptr, -1, + rdv.getValidLocaleDataEntry(), nullptr, 0, + stackTempBundle.getAlias(), &errorCode); + if (U_SUCCESS(errorCode)) { + ResourceDataValue aliasedValue; + aliasedValue.setData(aliasRB->getResData()); + aliasedValue.setValidLocaleDataEntry(aliasRB->fValidLocaleDataEntry); + aliasedValue.setResource(aliasRB->fRes, ResourceTracer(aliasRB)); + dest.put(key, aliasedValue, isRoot, errorCode); + } + } else { + dest.put(key, value, isRoot, errorCode); + } + if (U_FAILURE(errorCode)) { return; } + } + } +}; + +// Virtual destructors must be defined out of line. +GetAllChildrenSink::~GetAllChildrenSink() {} + +U_CAPI void U_EXPORT2 +ures_getAllChildrenWithFallback(const UResourceBundle *bundle, const char *path, + icu::ResourceSink &sink, UErrorCode &errorCode) { + GetAllChildrenSink allChildrenSink(sink); + ures_getAllItemsWithFallback(bundle, path, allChildrenSink, errorCode); +} + +} // namespace + +// Requires a ResourceDataValue fill-in, so that we need not cast from a ResourceValue. +// Unfortunately, the caller must know which subclass to make and pass in. +// Alternatively, we could make it as polymorphic as in Java by +// returning a ResourceValue pointer (possibly wrapped into a LocalPointer) +// that the caller then owns. +// +// Also requires a UResourceBundle fill-in, so that the value's ResourceTracer +// can point to a non-local bundle. +// Without tracing, the child bundle could be a function-local object. +U_CAPI void U_EXPORT2 +ures_getValueWithFallback(const UResourceBundle *bundle, const char *path, + UResourceBundle *tempFillIn, + ResourceDataValue &value, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return; } + if (path == nullptr) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + const UResourceBundle *rb; + if (*path == 0) { + // empty path + rb = bundle; + } else { + rb = ures_getByKeyWithFallback(bundle, path, tempFillIn, &errorCode); + if (U_FAILURE(errorCode)) { + return; + } + } + value.setData(rb->getResData()); + value.setValidLocaleDataEntry(rb->fValidLocaleDataEntry); + value.setResource(rb->fRes, ResourceTracer(rb)); +} + +U_CAPI void U_EXPORT2 +ures_getAllItemsWithFallback(const UResourceBundle *bundle, const char *path, + icu::ResourceSink &sink, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return; } + if (path == nullptr) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + StackUResourceBundle stackBundle; + const UResourceBundle *rb; + if (*path == 0) { + // empty path + rb = bundle; + } else { + rb = ures_getByKeyWithFallback(bundle, path, stackBundle.getAlias(), &errorCode); + if (U_FAILURE(errorCode)) { + return; + } + } + // Get all table items with fallback. + ResourceDataValue value; + getAllItemsWithFallback(rb, value, sink, errorCode); +} + +U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, const char* inKey, UResourceBundle *fillIn, UErrorCode *status) { + Resource res = RES_BOGUS; + UResourceDataEntry *dataEntry = nullptr; + const char *key = inKey; + + if (status==nullptr || U_FAILURE(*status)) { + return fillIn; + } + if(resB == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return fillIn; + } + + int32_t type = RES_GET_TYPE(resB->fRes); + if(URES_IS_TABLE(type)) { + int32_t t; + res = res_getTableItemByKey(&resB->getResData(), resB->fRes, &t, &key); + if(res == RES_BOGUS) { + key = inKey; + if(resB->fHasFallback) { + dataEntry = getFallbackData(resB, &key, &res, status); + if(U_SUCCESS(*status)) { + /* check if resB->fResPath gives the right name here */ + return init_resb_result(dataEntry, res, key, -1, resB, fillIn, status); + } else { + *status = U_MISSING_RESOURCE_ERROR; + } + } else { + *status = U_MISSING_RESOURCE_ERROR; + } + } else { + return init_resb_result(resB->fData, res, key, -1, resB, fillIn, status); + } + } +#if 0 + /* this is a kind of TODO item. If we have an array with an index table, we could do this. */ + /* not currently */ + else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == true) { + /* here should go a first attempt to locate the key using index table */ + dataEntry = getFallbackData(resB, &key, &res, status); + if(U_SUCCESS(*status)) { + return init_resb_result(dataEntry, res, key, resB, fillIn, status); + } else { + *status = U_MISSING_RESOURCE_ERROR; + } + } +#endif + else { + *status = U_RESOURCE_TYPE_MISMATCH; + } + return fillIn; +} + +U_CAPI const char16_t* U_EXPORT2 ures_getStringByKey(const UResourceBundle *resB, const char* inKey, int32_t* len, UErrorCode *status) { + Resource res = RES_BOGUS; + UResourceDataEntry *dataEntry = nullptr; + const char* key = inKey; + + if (status==nullptr || U_FAILURE(*status)) { + return nullptr; + } + if(resB == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + int32_t type = RES_GET_TYPE(resB->fRes); + if(URES_IS_TABLE(type)) { + int32_t t=0; + + res = res_getTableItemByKey(&resB->getResData(), resB->fRes, &t, &key); + + if(res == RES_BOGUS) { + key = inKey; + if(resB->fHasFallback) { + dataEntry = getFallbackData(resB, &key, &res, status); + if(U_SUCCESS(*status)) { + switch (RES_GET_TYPE(res)) { + case URES_STRING: + case URES_STRING_V2: + return res_getString({resB, key}, &dataEntry->fData, res, len); + case URES_ALIAS: + { + const char16_t* result = 0; + UResourceBundle *tempRes = ures_getByKey(resB, inKey, nullptr, status); + result = ures_getString(tempRes, len, status); + ures_close(tempRes); + return result; + } + default: + *status = U_RESOURCE_TYPE_MISMATCH; + } + } else { + *status = U_MISSING_RESOURCE_ERROR; + } + } else { + *status = U_MISSING_RESOURCE_ERROR; + } + } else { + switch (RES_GET_TYPE(res)) { + case URES_STRING: + case URES_STRING_V2: + return res_getString({resB, key}, &resB->getResData(), res, len); + case URES_ALIAS: + { + const char16_t* result = 0; + UResourceBundle *tempRes = ures_getByKey(resB, inKey, nullptr, status); + result = ures_getString(tempRes, len, status); + ures_close(tempRes); + return result; + } + default: + *status = U_RESOURCE_TYPE_MISMATCH; + } + } + } +#if 0 + /* this is a kind of TODO item. If we have an array with an index table, we could do this. */ + /* not currently */ + else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == true) { + /* here should go a first attempt to locate the key using index table */ + dataEntry = getFallbackData(resB, &key, &res, status); + if(U_SUCCESS(*status)) { + // TODO: Tracing + return res_getString(rd, res, len); + } else { + *status = U_MISSING_RESOURCE_ERROR; + } + } +#endif + else { + *status = U_RESOURCE_TYPE_MISMATCH; + } + return nullptr; +} + +U_CAPI const char * U_EXPORT2 +ures_getUTF8StringByKey(const UResourceBundle *resB, + const char *key, + char *dest, int32_t *pLength, + UBool forceCopy, + UErrorCode *status) { + int32_t length16; + const char16_t *s16 = ures_getStringByKey(resB, key, &length16, status); + return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status); +} + +/* TODO: clean from here down */ + +/** + * INTERNAL: Get the name of the first real locale (not placeholder) + * that has resource bundle data. + */ +U_CAPI const char* U_EXPORT2 +ures_getLocaleInternal(const UResourceBundle* resourceBundle, UErrorCode* status) +{ + if (status==nullptr || U_FAILURE(*status)) { + return nullptr; + } + if (!resourceBundle) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } else { + return resourceBundle->fData->fName; + } +} + +U_CAPI const char* U_EXPORT2 +ures_getLocale(const UResourceBundle* resourceBundle, + UErrorCode* status) +{ + return ures_getLocaleInternal(resourceBundle, status); +} + + +U_CAPI const char* U_EXPORT2 +ures_getLocaleByType(const UResourceBundle* resourceBundle, + ULocDataLocaleType type, + UErrorCode* status) { + if (status==nullptr || U_FAILURE(*status)) { + return nullptr; + } + if (!resourceBundle) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } else { + switch(type) { + case ULOC_ACTUAL_LOCALE: + return resourceBundle->fData->fName; + case ULOC_VALID_LOCALE: + return resourceBundle->fValidLocaleDataEntry->fName; + case ULOC_REQUESTED_LOCALE: + default: + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + } +} + +U_CFUNC const char* ures_getName(const UResourceBundle* resB) { + if(resB == nullptr) { + return nullptr; + } + + return resB->fData->fName; +} + +#ifdef URES_DEBUG +U_CFUNC const char* ures_getPath(const UResourceBundle* resB) { + if(resB == nullptr) { + return nullptr; + } + + return resB->fData->fPath; +} +#endif + +static UResourceBundle* +ures_openWithType(UResourceBundle *r, const char* path, const char* localeID, + UResOpenType openType, UErrorCode* status) { + if(U_FAILURE(*status)) { + return nullptr; + } + + UResourceDataEntry *entry; + if(openType != URES_OPEN_DIRECT) { + /* first "canonicalize" the locale ID */ + char canonLocaleID[ULOC_FULLNAME_CAPACITY]; + uloc_getBaseName(localeID, canonLocaleID, UPRV_LENGTHOF(canonLocaleID), status); + if(U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + entry = entryOpen(path, canonLocaleID, openType, status); + } else { + entry = entryOpenDirect(path, localeID, status); + } + if(U_FAILURE(*status)) { + return nullptr; + } + if(entry == nullptr) { + *status = U_MISSING_RESOURCE_ERROR; + return nullptr; + } + + UBool isStackObject; + if(r == nullptr) { + r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle)); + if(r == nullptr) { + entryClose(entry); + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + isStackObject = false; + } else { // fill-in + isStackObject = ures_isStackObject(r); + ures_closeBundle(r, false); + } + uprv_memset(r, 0, sizeof(UResourceBundle)); + ures_setIsStackObject(r, isStackObject); + + r->fValidLocaleDataEntry = r->fData = entry; + r->fHasFallback = openType != URES_OPEN_DIRECT && !r->getResData().noFallback; + r->fIsTopLevel = true; + r->fRes = r->getResData().rootRes; + r->fSize = res_countArrayItems(&r->getResData(), r->fRes); + r->fIndex = -1; + + ResourceTracer(r).traceOpen(); + + return r; +} + +U_CAPI UResourceBundle* U_EXPORT2 +ures_open(const char* path, const char* localeID, UErrorCode* status) { + return ures_openWithType(nullptr, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status); +} + +U_CAPI UResourceBundle* U_EXPORT2 +ures_openNoDefault(const char* path, const char* localeID, UErrorCode* status) { + return ures_openWithType(nullptr, path, localeID, URES_OPEN_LOCALE_ROOT, status); +} + +/** + * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed + * or sought. However, alias substitution will happen! + */ +U_CAPI UResourceBundle* U_EXPORT2 +ures_openDirect(const char* path, const char* localeID, UErrorCode* status) { + return ures_openWithType(nullptr, path, localeID, URES_OPEN_DIRECT, status); +} + +/** + * Internal API: This function is used to open a resource bundle + * proper fallback chaining is executed while initialization. + * The result is stored in cache for later fallback search. + * + * Same as ures_open(), but uses the fill-in parameter and does not allocate a new bundle. + */ +U_CAPI void U_EXPORT2 +ures_openFillIn(UResourceBundle *r, const char* path, + const char* localeID, UErrorCode* status) { + if(U_SUCCESS(*status) && r == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + ures_openWithType(r, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status); +} + +/** + * Same as ures_openDirect(), but uses the fill-in parameter and does not allocate a new bundle. + */ +U_CAPI void U_EXPORT2 +ures_openDirectFillIn(UResourceBundle *r, const char* path, const char* localeID, UErrorCode* status) { + if(U_SUCCESS(*status) && r == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + ures_openWithType(r, path, localeID, URES_OPEN_DIRECT, status); +} + +/** + * API: Counts members. For arrays and tables, returns number of resources. + * For strings, returns 1. + */ +U_CAPI int32_t U_EXPORT2 +ures_countArrayItems(const UResourceBundle* resourceBundle, + const char* resourceKey, + UErrorCode* status) +{ + UResourceBundle resData; + ures_initStackObject(&resData); + if (status==nullptr || U_FAILURE(*status)) { + return 0; + } + if(resourceBundle == nullptr) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + ures_getByKey(resourceBundle, resourceKey, &resData, status); + + if(resData.getResData().data != nullptr) { + int32_t result = res_countArrayItems(&resData.getResData(), resData.fRes); + ures_close(&resData); + return result; + } else { + *status = U_MISSING_RESOURCE_ERROR; + ures_close(&resData); + return 0; + } +} + +/** + * Internal function. + * Return the version number associated with this ResourceBundle as a string. + * + * @param resourceBundle The resource bundle for which the version is checked. + * @return A version number string as specified in the resource bundle or its parent. + * The caller does not own this string. + * @see ures_getVersion + * @internal + */ +U_CAPI const char* U_EXPORT2 +ures_getVersionNumberInternal(const UResourceBundle *resourceBundle) +{ + if (!resourceBundle) return nullptr; + + if(resourceBundle->fVersion == nullptr) { + + /* If the version ID has not been built yet, then do so. Retrieve */ + /* the minor version from the file. */ + UErrorCode status = U_ZERO_ERROR; + int32_t minor_len = 0; + int32_t len; + + const char16_t* minor_version = ures_getStringByKey(resourceBundle, kVersionTag, &minor_len, &status); + + /* Determine the length of of the final version string. This is */ + /* the length of the major part + the length of the separator */ + /* (==1) + the length of the minor part (+ 1 for the zero byte at */ + /* the end). */ + + len = (minor_len > 0) ? minor_len : 1; + + /* Allocate the string, and build it up. */ + /* + 1 for zero byte */ + + + ((UResourceBundle *)resourceBundle)->fVersion = (char *)uprv_malloc(1 + len); + /* Check for null pointer. */ + if (((UResourceBundle *)resourceBundle)->fVersion == nullptr) { + return nullptr; + } + + if(minor_len > 0) { + u_UCharsToChars(minor_version, resourceBundle->fVersion , minor_len); + resourceBundle->fVersion[len] = '\0'; + } + else { + uprv_strcpy(resourceBundle->fVersion, kDefaultMinorVersion); + } + } + + return resourceBundle->fVersion; +} + +U_CAPI const char* U_EXPORT2 +ures_getVersionNumber(const UResourceBundle* resourceBundle) +{ + return ures_getVersionNumberInternal(resourceBundle); +} + +U_CAPI void U_EXPORT2 ures_getVersion(const UResourceBundle* resB, UVersionInfo versionInfo) { + if (!resB) return; + + u_versionFromString(versionInfo, ures_getVersionNumberInternal(resB)); +} + +/** Tree support functions *******************************/ +#define INDEX_LOCALE_NAME "res_index" +#define INDEX_TAG "InstalledLocales" +#define DEFAULT_TAG "default" + +#if defined(URES_TREE_DEBUG) +#include +#endif + +typedef struct ULocalesContext { + UResourceBundle installed; + UResourceBundle curr; +} ULocalesContext; + +static void U_CALLCONV +ures_loc_closeLocales(UEnumeration *enumerator) { + ULocalesContext *ctx = (ULocalesContext *)enumerator->context; + ures_close(&ctx->curr); + ures_close(&ctx->installed); + uprv_free(ctx); + uprv_free(enumerator); +} + +static int32_t U_CALLCONV +ures_loc_countLocales(UEnumeration *en, UErrorCode * /*status*/) { + ULocalesContext *ctx = (ULocalesContext *)en->context; + return ures_getSize(&ctx->installed); +} + +U_CDECL_BEGIN + + +static const char * U_CALLCONV +ures_loc_nextLocale(UEnumeration* en, + int32_t* resultLength, + UErrorCode* status) { + ULocalesContext *ctx = (ULocalesContext *)en->context; + UResourceBundle *res = &(ctx->installed); + UResourceBundle *k = nullptr; + const char *result = nullptr; + int32_t len = 0; + if(ures_hasNext(res) && (k = ures_getNextResource(res, &ctx->curr, status)) != 0) { + result = ures_getKey(k); + len = (int32_t)uprv_strlen(result); + } + if (resultLength) { + *resultLength = len; + } + return result; +} + +static void U_CALLCONV +ures_loc_resetLocales(UEnumeration* en, + UErrorCode* /*status*/) { + UResourceBundle *res = &((ULocalesContext *)en->context)->installed; + ures_resetIterator(res); +} + +U_CDECL_END + +static const UEnumeration gLocalesEnum = { + nullptr, + nullptr, + ures_loc_closeLocales, + ures_loc_countLocales, + uenum_unextDefault, + ures_loc_nextLocale, + ures_loc_resetLocales +}; + + +U_CAPI UEnumeration* U_EXPORT2 +ures_openAvailableLocales(const char *path, UErrorCode *status) +{ + UResourceBundle *idx = nullptr; + UEnumeration *en = nullptr; + ULocalesContext *myContext = nullptr; + + if(U_FAILURE(*status)) { + return nullptr; + } + myContext = static_cast(uprv_malloc(sizeof(ULocalesContext))); + en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration)); + if(!en || !myContext) { + *status = U_MEMORY_ALLOCATION_ERROR; + uprv_free(en); + uprv_free(myContext); + return nullptr; + } + uprv_memcpy(en, &gLocalesEnum, sizeof(UEnumeration)); + + ures_initStackObject(&myContext->installed); + ures_initStackObject(&myContext->curr); + idx = ures_openDirect(path, INDEX_LOCALE_NAME, status); + ures_getByKey(idx, INDEX_TAG, &myContext->installed, status); + if(U_SUCCESS(*status)) { +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "Got %s::%s::[%s] : %s\n", + path, INDEX_LOCALE_NAME, INDEX_TAG, ures_getKey(&myContext->installed)); +#endif + en->context = myContext; + } else { +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s open failed - %s\n", path, u_errorName(*status)); +#endif + ures_close(&myContext->installed); + uprv_free(myContext); + uprv_free(en); + en = nullptr; + } + + ures_close(idx); + + return en; +} + +static UBool isLocaleInList(UEnumeration *locEnum, const char *locToSearch, UErrorCode *status) { + const char *loc; + while ((loc = uenum_next(locEnum, nullptr, status)) != nullptr) { + if (uprv_strcmp(loc, locToSearch) == 0) { + return true; + } + } + return false; +} + +U_CAPI int32_t U_EXPORT2 +ures_getFunctionalEquivalent(char *result, int32_t resultCapacity, + const char *path, const char *resName, const char *keyword, const char *locid, + UBool *isAvailable, UBool omitDefault, UErrorCode *status) +{ + char kwVal[1024] = ""; /* value of keyword 'keyword' */ + char defVal[1024] = ""; /* default value for given locale */ + char defLoc[1024] = ""; /* default value for given locale */ + char base[1024] = ""; /* base locale */ + char found[1024] = ""; + char parent[1024] = ""; + char full[1024] = ""; + UResourceBundle bund1, bund2; + UResourceBundle *res = nullptr; + UErrorCode subStatus = U_ZERO_ERROR; + int32_t length = 0; + if(U_FAILURE(*status)) return 0; + uloc_getKeywordValue(locid, keyword, kwVal, 1024-1,&subStatus); + if(!uprv_strcmp(kwVal, DEFAULT_TAG)) { + kwVal[0]=0; + } + uloc_getBaseName(locid, base, 1024-1,&subStatus); +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n", + locid, keyword, kwVal, base, u_errorName(subStatus)); +#endif + ures_initStackObject(&bund1); + ures_initStackObject(&bund2); + + + uprv_strcpy(parent, base); + uprv_strcpy(found, base); + + if(isAvailable) { + UEnumeration *locEnum = ures_openAvailableLocales(path, &subStatus); + *isAvailable = true; + if (U_SUCCESS(subStatus)) { + *isAvailable = isLocaleInList(locEnum, parent, &subStatus); + } + uenum_close(locEnum); + } + + if(U_FAILURE(subStatus)) { + *status = subStatus; + return 0; + } + + do { + subStatus = U_ZERO_ERROR; + res = ures_open(path, parent, &subStatus); + if(((subStatus == U_USING_FALLBACK_WARNING) || + (subStatus == U_USING_DEFAULT_WARNING)) && isAvailable) + { + *isAvailable = false; + } + isAvailable = nullptr; /* only want to set this the first time around */ + +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s;%s -> %s [%s]\n", path?path:"ICUDATA", parent, u_errorName(subStatus), ures_getLocale(res, &subStatus)); +#endif + if(U_FAILURE(subStatus)) { + *status = subStatus; + } else if(subStatus == U_ZERO_ERROR) { + ures_getByKey(res,resName,&bund1, &subStatus); + if(subStatus == U_ZERO_ERROR) { + const char16_t *defUstr; + int32_t defLen; + /* look for default item */ +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s;%s : loaded default -> %s\n", + path?path:"ICUDATA", parent, u_errorName(subStatus)); +#endif + defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus); + if(U_SUCCESS(subStatus) && defLen) { + u_UCharsToChars(defUstr, defVal, u_strlen(defUstr)); +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s;%s -> default %s=%s, %s\n", + path?path:"ICUDATA", parent, keyword, defVal, u_errorName(subStatus)); +#endif + uprv_strcpy(defLoc, parent); + if(kwVal[0]==0) { + uprv_strcpy(kwVal, defVal); +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s;%s -> kwVal = %s\n", + path?path:"ICUDATA", parent, keyword, kwVal); +#endif + } + } + } + } + + subStatus = U_ZERO_ERROR; + + if (res != nullptr) { + uprv_strcpy(found, ures_getLocaleByType(res, ULOC_VALID_LOCALE, &subStatus)); + } + + uloc_getParent(found,parent,sizeof(parent),&subStatus); + ures_close(res); + } while(!defVal[0] && *found && uprv_strcmp(found, "root") != 0 && U_SUCCESS(*status)); + + /* Now, see if we can find the kwVal collator.. start the search over.. */ + uprv_strcpy(parent, base); + uprv_strcpy(found, base); + + do { + subStatus = U_ZERO_ERROR; + res = ures_open(path, parent, &subStatus); + if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) { + *isAvailable = false; + } + isAvailable = nullptr; /* only want to set this the first time around */ + +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s;%s -> %s (looking for %s)\n", + path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal); +#endif + if(U_FAILURE(subStatus)) { + *status = subStatus; + } else if(subStatus == U_ZERO_ERROR) { + ures_getByKey(res,resName,&bund1, &subStatus); +#if defined(URES_TREE_DEBUG) +/**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, resName, u_errorName(subStatus)); +#endif + if(subStatus == U_ZERO_ERROR) { + ures_getByKey(&bund1, kwVal, &bund2, &subStatus); +#if defined(URES_TREE_DEBUG) +/**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, kwVal, u_errorName(subStatus)); +#endif + if(subStatus == U_ZERO_ERROR) { +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s;%s -> full0 %s=%s, %s\n", + path?path:"ICUDATA", parent, keyword, kwVal, u_errorName(subStatus)); +#endif + uprv_strcpy(full, parent); + if(*full == 0) { + uprv_strcpy(full, "root"); + } + /* now, recalculate default kw if need be */ + if(uprv_strlen(defLoc) > uprv_strlen(full)) { + const char16_t *defUstr; + int32_t defLen; + /* look for default item */ +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s;%s -> recalculating Default0\n", + path?path:"ICUDATA", full); +#endif + defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus); + if(U_SUCCESS(subStatus) && defLen) { + u_UCharsToChars(defUstr, defVal, u_strlen(defUstr)); +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s;%s -> default0 %s=%s, %s\n", + path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus)); +#endif + uprv_strcpy(defLoc, full); + } + } /* end of recalculate default KW */ +#if defined(URES_TREE_DEBUG) + else { + fprintf(stderr, "No trim0, %s <= %s\n", defLoc, full); + } +#endif + } else { +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "err=%s in %s looking for %s\n", + u_errorName(subStatus), parent, kwVal); +#endif + } + } + } + + subStatus = U_ZERO_ERROR; + + uprv_strcpy(found, parent); + uloc_getParent(found,parent,1023,&subStatus); + ures_close(res); + } while(!full[0] && *found && U_SUCCESS(*status)); + + if((full[0]==0) && uprv_strcmp(kwVal, defVal)) { +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "Failed to locate kw %s - try default %s\n", kwVal, defVal); +#endif + uprv_strcpy(kwVal, defVal); + uprv_strcpy(parent, base); + uprv_strcpy(found, base); + + do { /* search for 'default' named item */ + subStatus = U_ZERO_ERROR; + res = ures_open(path, parent, &subStatus); + if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) { + *isAvailable = false; + } + isAvailable = nullptr; /* only want to set this the first time around */ + +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s;%s -> %s (looking for default %s)\n", + path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal); +#endif + if(U_FAILURE(subStatus)) { + *status = subStatus; + } else if(subStatus == U_ZERO_ERROR) { + ures_getByKey(res,resName,&bund1, &subStatus); + if(subStatus == U_ZERO_ERROR) { + ures_getByKey(&bund1, kwVal, &bund2, &subStatus); + if(subStatus == U_ZERO_ERROR) { +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s;%s -> full1 %s=%s, %s\n", path?path:"ICUDATA", + parent, keyword, kwVal, u_errorName(subStatus)); +#endif + uprv_strcpy(full, parent); + if(*full == 0) { + uprv_strcpy(full, "root"); + } + + /* now, recalculate default kw if need be */ + if(uprv_strlen(defLoc) > uprv_strlen(full)) { + const char16_t *defUstr; + int32_t defLen; + /* look for default item */ +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s;%s -> recalculating Default1\n", + path?path:"ICUDATA", full); +#endif + defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus); + if(U_SUCCESS(subStatus) && defLen) { + u_UCharsToChars(defUstr, defVal, u_strlen(defUstr)); +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s;%s -> default %s=%s, %s\n", + path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus)); +#endif + uprv_strcpy(defLoc, full); + } + } /* end of recalculate default KW */ +#if defined(URES_TREE_DEBUG) + else { + fprintf(stderr, "No trim1, %s <= %s\n", defLoc, full); + } +#endif + } + } + } + subStatus = U_ZERO_ERROR; + + uprv_strcpy(found, parent); + uloc_getParent(found,parent,1023,&subStatus); + ures_close(res); + } while(!full[0] && *found && U_SUCCESS(*status)); + } + + if(U_SUCCESS(*status)) { + if(!full[0]) { +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "Still could not load keyword %s=%s\n", keyword, kwVal); +#endif + *status = U_MISSING_RESOURCE_ERROR; + } else if(omitDefault) { +#if defined(URES_TREE_DEBUG) + fprintf(stderr,"Trim? full=%s, defLoc=%s, found=%s\n", full, defLoc, found); +#endif + if(uprv_strlen(defLoc) <= uprv_strlen(full)) { + /* found the keyword in a *child* of where the default tag was present. */ + if(!uprv_strcmp(kwVal, defVal)) { /* if the requested kw is default, */ + /* and the default is in or in an ancestor of the current locale */ +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "Removing unneeded var %s=%s\n", keyword, kwVal); +#endif + kwVal[0]=0; + } + } + } + uprv_strcpy(found, full); + if(kwVal[0]) { + uprv_strcat(found, "@"); + uprv_strcat(found, keyword); + uprv_strcat(found, "="); + uprv_strcat(found, kwVal); + } else if(!omitDefault) { + uprv_strcat(found, "@"); + uprv_strcat(found, keyword); + uprv_strcat(found, "="); + uprv_strcat(found, defVal); + } + } + /* we found the default locale - no need to repeat it.*/ + + ures_close(&bund1); + ures_close(&bund2); + + length = (int32_t)uprv_strlen(found); + + if(U_SUCCESS(*status)) { + int32_t copyLength = uprv_min(length, resultCapacity); + if(copyLength>0) { + uprv_strncpy(result, found, copyLength); + } + if(length == 0) { + *status = U_MISSING_RESOURCE_ERROR; + } + } else { + length = 0; + result[0]=0; + } + return u_terminateChars(result, resultCapacity, length, status); +} + +U_CAPI UEnumeration* U_EXPORT2 +ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status) +{ +#define VALUES_BUF_SIZE 2048 +#define VALUES_LIST_SIZE 512 + + char valuesBuf[VALUES_BUF_SIZE]; + int32_t valuesIndex = 0; + const char *valuesList[VALUES_LIST_SIZE]; + int32_t valuesCount = 0; + + const char *locale; + int32_t locLen; + + UEnumeration *locs = nullptr; + + UResourceBundle item; + UResourceBundle subItem; + + ures_initStackObject(&item); + ures_initStackObject(&subItem); + locs = ures_openAvailableLocales(path, status); + + if(U_FAILURE(*status)) { + ures_close(&item); + ures_close(&subItem); + return nullptr; + } + + valuesBuf[0]=0; + valuesBuf[1]=0; + + while((locale = uenum_next(locs, &locLen, status)) != 0) { + UResourceBundle *bund = nullptr; + UResourceBundle *subPtr = nullptr; + UErrorCode subStatus = U_ZERO_ERROR; /* don't fail if a bundle is unopenable */ + bund = ures_open(path, locale, &subStatus); + +#if defined(URES_TREE_DEBUG) + if(!bund || U_FAILURE(subStatus)) { + fprintf(stderr, "%s-%s values: Can't open %s locale - skipping. (%s)\n", + path?path:"", keyword, locale, u_errorName(subStatus)); + } +#endif + + ures_getByKey(bund, keyword, &item, &subStatus); + + if(!bund || U_FAILURE(subStatus)) { +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s-%s values: Can't find in %s - skipping. (%s)\n", + path?path:"", keyword, locale, u_errorName(subStatus)); +#endif + ures_close(bund); + bund = nullptr; + continue; + } + + while((subPtr = ures_getNextResource(&item,&subItem,&subStatus)) != 0 + && U_SUCCESS(subStatus)) { + const char *k; + int32_t i; + k = ures_getKey(subPtr); + +#if defined(URES_TREE_DEBUG) + /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"", keyword, locale, k); */ +#endif + if(k == nullptr || *k == 0 || + uprv_strcmp(k, DEFAULT_TAG) == 0 || uprv_strncmp(k, "private-", 8) == 0) { + // empty or "default" or unlisted type + continue; + } + for(i=0; i= (VALUES_LIST_SIZE-1)) || /* no more space in list .. */ + ((valuesIndex+kLen+1+1) >= VALUES_BUF_SIZE)) { /* no more space in buffer (string + 2 nulls) */ + *status = U_ILLEGAL_ARGUMENT_ERROR; /* out of space.. */ + } else { + uprv_strcpy(valuesBuf+valuesIndex, k); + valuesList[valuesCount++] = valuesBuf+valuesIndex; + valuesIndex += kLen; +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s | %s | %s | [%s] (UNIQUE)\n", + path?path:"", keyword, locale, k); +#endif + valuesBuf[valuesIndex++] = 0; /* terminate */ + } + } + } + ures_close(bund); + } + valuesBuf[valuesIndex++] = 0; /* terminate */ + + ures_close(&item); + ures_close(&subItem); + uenum_close(locs); +#if defined(URES_TREE_DEBUG) + fprintf(stderr, "%s: size %d, #%d\n", u_errorName(*status), + valuesIndex, valuesCount); +#endif + return uloc_openKeywordList(valuesBuf, valuesIndex, status); +} +#if 0 +/* This code isn't needed, and given the documentation warnings the implementation is suspect */ +U_CAPI UBool U_EXPORT2 +ures_equal(const UResourceBundle* res1, const UResourceBundle* res2){ + if(res1==nullptr || res2==nullptr){ + return res1==res2; /* pointer comparison */ + } + if(res1->fKey==nullptr|| res2->fKey==nullptr){ + return (res1->fKey==res2->fKey); + }else{ + if(uprv_strcmp(res1->fKey, res2->fKey)!=0){ + return false; + } + } + if(uprv_strcmp(res1->fData->fName, res2->fData->fName)!=0){ + return false; + } + if(res1->fData->fPath == nullptr|| res2->fData->fPath==nullptr){ + return (res1->fData->fPath == res2->fData->fPath); + }else{ + if(uprv_strcmp(res1->fData->fPath, res2->fData->fPath)!=0){ + return false; + } + } + if(uprv_strcmp(res1->fData->fParent->fName, res2->fData->fParent->fName)!=0){ + return false; + } + if(uprv_strcmp(res1->fData->fParent->fPath, res2->fData->fParent->fPath)!=0){ + return false; + } + if(uprv_strncmp(res1->fResPath, res2->fResPath, res1->fResPathLen)!=0){ + return false; + } + if(res1->fRes != res2->fRes){ + return false; + } + return true; +} +U_CAPI UResourceBundle* U_EXPORT2 +ures_clone(const UResourceBundle* res, UErrorCode* status){ + UResourceBundle* bundle = nullptr; + UResourceBundle* ret = nullptr; + if(U_FAILURE(*status) || res == nullptr){ + return nullptr; + } + bundle = ures_open(res->fData->fPath, res->fData->fName, status); + if(res->fResPath!=nullptr){ + ret = ures_findSubResource(bundle, res->fResPath, nullptr, status); + ures_close(bundle); + }else{ + ret = bundle; + } + return ret; +} +U_CAPI const UResourceBundle* U_EXPORT2 +ures_getParentBundle(const UResourceBundle* res){ + if(res==nullptr){ + return nullptr; + } + return res->fParentRes; +} +#endif + +U_CAPI void U_EXPORT2 +ures_getVersionByKey(const UResourceBundle* res, const char *key, UVersionInfo ver, UErrorCode *status) { + const char16_t *str; + int32_t len; + str = ures_getStringByKey(res, key, &len, status); + if(U_SUCCESS(*status)) { + u_versionFromUString(ver, str); + } +} + +/* eof */ diff --git a/deps/icu-small/source/common/uresdata.cpp b/deps/icu-small/source/common/uresdata.cpp index a1222d415ce6d1..e73c9c25dcd32d 100644 --- a/deps/icu-small/source/common/uresdata.cpp +++ b/deps/icu-small/source/common/uresdata.cpp @@ -1,1510 +1,1510 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1999-2016, International Business Machines Corporation -* and others. All Rights Reserved. -******************************************************************************* -* file name: uresdata.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999dec08 -* created by: Markus W. Scherer -* Modification History: -* -* Date Name Description -* 06/20/2000 helena OS/400 port changes; mostly typecast. -* 06/24/02 weiv Added support for resource sharing -*/ - -#include "unicode/utypes.h" -#include "unicode/udata.h" -#include "unicode/ustring.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "cstring.h" -#include "resource.h" -#include "uarrsort.h" -#include "uassert.h" -#include "ucol_swp.h" -#include "udataswp.h" -#include "uinvchar.h" -#include "uresdata.h" -#include "uresimp.h" -#include "utracimp.h" - -/* - * Resource access helpers - */ - -/* get a const char* pointer to the key with the keyOffset byte offset from pRoot */ -#define RES_GET_KEY16(pResData, keyOffset) \ - ((keyOffset)<(pResData)->localKeyLimit ? \ - (const char *)(pResData)->pRoot+(keyOffset) : \ - (pResData)->poolBundleKeys+(keyOffset)-(pResData)->localKeyLimit) - -#define RES_GET_KEY32(pResData, keyOffset) \ - ((keyOffset)>=0 ? \ - (const char *)(pResData)->pRoot+(keyOffset) : \ - (pResData)->poolBundleKeys+((keyOffset)&0x7fffffff)) - -#define URESDATA_ITEM_NOT_FOUND -1 - -/* empty resources, returned when the resource offset is 0 */ -static const uint16_t gEmpty16=0; - -static const struct { - int32_t length; - int32_t res; -} gEmpty32={ 0, 0 }; - -static const struct { - int32_t length; - UChar nul; - UChar pad; -} gEmptyString={ 0, 0, 0 }; - -/* - * All the type-access functions assume that - * the resource is of the expected type. - */ - -static int32_t -_res_findTableItem(const ResourceData *pResData, const uint16_t *keyOffsets, int32_t length, - const char *key, const char **realKey) { - const char *tableKey; - int32_t mid, start, limit; - int result; - - /* do a binary search for the key */ - start=0; - limit=length; - while(startuseNativeStrcmp) { - result = uprv_strcmp(key, tableKey); - } else { - result = uprv_compareInvCharsAsAscii(key, tableKey); - } - if (result < 0) { - limit = mid; - } else if (result > 0) { - start = mid + 1; - } else { - /* We found it! */ - *realKey=tableKey; - return mid; - } - } - return URESDATA_ITEM_NOT_FOUND; /* not found or table is empty. */ -} - -static int32_t -_res_findTable32Item(const ResourceData *pResData, const int32_t *keyOffsets, int32_t length, - const char *key, const char **realKey) { - const char *tableKey; - int32_t mid, start, limit; - int result; - - /* do a binary search for the key */ - start=0; - limit=length; - while(startuseNativeStrcmp) { - result = uprv_strcmp(key, tableKey); - } else { - result = uprv_compareInvCharsAsAscii(key, tableKey); - } - if (result < 0) { - limit = mid; - } else if (result > 0) { - start = mid + 1; - } else { - /* We found it! */ - *realKey=tableKey; - return mid; - } - } - return URESDATA_ITEM_NOT_FOUND; /* not found or table is empty. */ -} - -/* helper for res_load() ---------------------------------------------------- */ - -static UBool U_CALLCONV -isAcceptable(void *context, - const char * /*type*/, const char * /*name*/, - const UDataInfo *pInfo) { - uprv_memcpy(context, pInfo->formatVersion, 4); - return (UBool)( - pInfo->size>=20 && - pInfo->isBigEndian==U_IS_BIG_ENDIAN && - pInfo->charsetFamily==U_CHARSET_FAMILY && - pInfo->sizeofUChar==U_SIZEOF_UCHAR && - pInfo->dataFormat[0]==0x52 && /* dataFormat="ResB" */ - pInfo->dataFormat[1]==0x65 && - pInfo->dataFormat[2]==0x73 && - pInfo->dataFormat[3]==0x42 && - (1<=pInfo->formatVersion[0] && pInfo->formatVersion[0]<=3)); -} - -/* semi-public functions ---------------------------------------------------- */ - -static void -res_init(ResourceData *pResData, - UVersionInfo formatVersion, const void *inBytes, int32_t length, - UErrorCode *errorCode) { - UResType rootType; - - /* get the root resource */ - pResData->pRoot=(const int32_t *)inBytes; - pResData->rootRes=(Resource)*pResData->pRoot; - pResData->p16BitUnits=&gEmpty16; - - /* formatVersion 1.1 must have a root item and at least 5 indexes */ - if(length>=0 && (length/4)<((formatVersion[0]==1 && formatVersion[1]==0) ? 1 : 1+5)) { - *errorCode=U_INVALID_FORMAT_ERROR; - res_unload(pResData); - return; - } - - /* currently, we accept only resources that have a Table as their roots */ - rootType=(UResType)RES_GET_TYPE(pResData->rootRes); - if(!URES_IS_TABLE(rootType)) { - *errorCode=U_INVALID_FORMAT_ERROR; - res_unload(pResData); - return; - } - - if(formatVersion[0]==1 && formatVersion[1]==0) { - pResData->localKeyLimit=0x10000; /* greater than any 16-bit key string offset */ - } else { - /* bundles with formatVersion 1.1 and later contain an indexes[] array */ - const int32_t *indexes=pResData->pRoot+1; - int32_t indexLength=indexes[URES_INDEX_LENGTH]&0xff; - if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) { - *errorCode=U_INVALID_FORMAT_ERROR; - res_unload(pResData); - return; - } - if( length>=0 && - (length<((1+indexLength)<<2) || - length<(indexes[URES_INDEX_BUNDLE_TOP]<<2)) - ) { - *errorCode=U_INVALID_FORMAT_ERROR; - res_unload(pResData); - return; - } - if(indexes[URES_INDEX_KEYS_TOP]>(1+indexLength)) { - pResData->localKeyLimit=indexes[URES_INDEX_KEYS_TOP]<<2; - } - if(formatVersion[0]>=3) { - // In formatVersion 1, the indexLength took up this whole int. - // In version 2, bits 31..8 were reserved and always 0. - // In version 3, they contain bits 23..0 of the poolStringIndexLimit. - // Bits 27..24 are in indexes[URES_INDEX_ATTRIBUTES] bits 15..12. - pResData->poolStringIndexLimit=(int32_t)((uint32_t)indexes[URES_INDEX_LENGTH]>>8); - } - if(indexLength>URES_INDEX_ATTRIBUTES) { - int32_t att=indexes[URES_INDEX_ATTRIBUTES]; - pResData->noFallback=(UBool)(att&URES_ATT_NO_FALLBACK); - pResData->isPoolBundle=(UBool)((att&URES_ATT_IS_POOL_BUNDLE)!=0); - pResData->usesPoolBundle=(UBool)((att&URES_ATT_USES_POOL_BUNDLE)!=0); - pResData->poolStringIndexLimit|=(att&0xf000)<<12; // bits 15..12 -> 27..24 - pResData->poolStringIndex16Limit=(int32_t)((uint32_t)att>>16); - } - if((pResData->isPoolBundle || pResData->usesPoolBundle) && indexLength<=URES_INDEX_POOL_CHECKSUM) { - *errorCode=U_INVALID_FORMAT_ERROR; - res_unload(pResData); - return; - } - if( indexLength>URES_INDEX_16BIT_TOP && - indexes[URES_INDEX_16BIT_TOP]>indexes[URES_INDEX_KEYS_TOP] - ) { - pResData->p16BitUnits=(const uint16_t *)(pResData->pRoot+indexes[URES_INDEX_KEYS_TOP]); - } - } - - if(formatVersion[0]==1 || U_CHARSET_FAMILY==U_ASCII_FAMILY) { - /* - * formatVersion 1: compare key strings in native-charset order - * formatVersion 2 and up: compare key strings in ASCII order - */ - pResData->useNativeStrcmp=true; - } -} - -U_CAPI void U_EXPORT2 -res_read(ResourceData *pResData, - const UDataInfo *pInfo, const void *inBytes, int32_t length, - UErrorCode *errorCode) { - UVersionInfo formatVersion; - - uprv_memset(pResData, 0, sizeof(ResourceData)); - if(U_FAILURE(*errorCode)) { - return; - } - if(!isAcceptable(formatVersion, NULL, NULL, pInfo)) { - *errorCode=U_INVALID_FORMAT_ERROR; - return; - } - res_init(pResData, formatVersion, inBytes, length, errorCode); -} - -U_CFUNC void -res_load(ResourceData *pResData, - const char *path, const char *name, UErrorCode *errorCode) { - UVersionInfo formatVersion; - - uprv_memset(pResData, 0, sizeof(ResourceData)); - - /* load the ResourceBundle file */ - pResData->data=udata_openChoice(path, "res", name, isAcceptable, formatVersion, errorCode); - if(U_FAILURE(*errorCode)) { - return; - } - - /* get its memory and initialize *pResData */ - res_init(pResData, formatVersion, udata_getMemory(pResData->data), -1, errorCode); -} - -U_CFUNC void -res_unload(ResourceData *pResData) { - if(pResData->data!=NULL) { - udata_close(pResData->data); - pResData->data=NULL; - } -} - -static const int8_t gPublicTypes[URES_LIMIT] = { - URES_STRING, - URES_BINARY, - URES_TABLE, - URES_ALIAS, - - URES_TABLE, /* URES_TABLE32 */ - URES_TABLE, /* URES_TABLE16 */ - URES_STRING, /* URES_STRING_V2 */ - URES_INT, - - URES_ARRAY, - URES_ARRAY, /* URES_ARRAY16 */ - URES_NONE, - URES_NONE, - - URES_NONE, - URES_NONE, - URES_INT_VECTOR, - URES_NONE -}; - -U_CAPI UResType U_EXPORT2 -res_getPublicType(Resource res) { - return (UResType)gPublicTypes[RES_GET_TYPE(res)]; -} - -U_CAPI const UChar * U_EXPORT2 -res_getStringNoTrace(const ResourceData *pResData, Resource res, int32_t *pLength) { - const UChar *p; - uint32_t offset=RES_GET_OFFSET(res); - int32_t length; - if(RES_GET_TYPE(res)==URES_STRING_V2) { - int32_t first; - if((int32_t)offsetpoolStringIndexLimit) { - p=(const UChar *)pResData->poolBundleStrings+offset; - } else { - p=(const UChar *)pResData->p16BitUnits+(offset-pResData->poolStringIndexLimit); - } - first=*p; - if(!U16_IS_TRAIL(first)) { - length=u_strlen(p); - } else if(first<0xdfef) { - length=first&0x3ff; - ++p; - } else if(first<0xdfff) { - length=((first-0xdfef)<<16)|p[1]; - p+=2; - } else { - length=((int32_t)p[1]<<16)|p[2]; - p+=3; - } - } else if(res==offset) /* RES_GET_TYPE(res)==URES_STRING */ { - const int32_t *p32= res==0 ? &gEmptyString.length : pResData->pRoot+res; - length=*p32++; - p=(const UChar *)p32; - } else { - p=NULL; - length=0; - } - if(pLength) { - *pLength=length; - } - return p; -} - -namespace { - -/** - * CLDR string value (three empty-set symbols)=={2205, 2205, 2205} - * prevents fallback to the parent bundle. - * TODO: combine with other code that handles this marker, use EMPTY_SET constant. - * TODO: maybe move to uresbund.cpp? - */ -UBool isNoInheritanceMarker(const ResourceData *pResData, Resource res) { - uint32_t offset=RES_GET_OFFSET(res); - if (offset == 0) { - // empty string - } else if (res == offset) { - const int32_t *p32=pResData->pRoot+res; - int32_t length=*p32; - const UChar *p=(const UChar *)p32; - return length == 3 && p[2] == 0x2205 && p[3] == 0x2205 && p[4] == 0x2205; - } else if (RES_GET_TYPE(res) == URES_STRING_V2) { - const UChar *p; - if((int32_t)offsetpoolStringIndexLimit) { - p=(const UChar *)pResData->poolBundleStrings+offset; - } else { - p=(const UChar *)pResData->p16BitUnits+(offset-pResData->poolStringIndexLimit); - } - int32_t first=*p; - if (first == 0x2205) { // implicit length - return p[1] == 0x2205 && p[2] == 0x2205 && p[3] == 0; - } else if (first == 0xdc03) { // explicit length 3 (should not occur) - return p[1] == 0x2205 && p[2] == 0x2205 && p[3] == 0x2205; - } else { - // Assume that the string has not been stored with more length units than necessary. - return false; - } - } - return false; -} - -int32_t getStringArray(const ResourceData *pResData, const icu::ResourceArray &array, - icu::UnicodeString *dest, int32_t capacity, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - return 0; - } - if(dest == NULL ? capacity != 0 : capacity < 0) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - int32_t length = array.getSize(); - if(length == 0) { - return 0; - } - if(length > capacity) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return length; - } - for(int32_t i = 0; i < length; ++i) { - int32_t sLength; - // No tracing: handled by the caller - const UChar *s = res_getStringNoTrace(pResData, array.internalGetResource(pResData, i), &sLength); - if(s == NULL) { - errorCode = U_RESOURCE_TYPE_MISMATCH; - return 0; - } - dest[i].setTo(true, s, sLength); - } - return length; -} - -} // namespace - -U_CAPI const UChar * U_EXPORT2 -res_getAlias(const ResourceData *pResData, Resource res, int32_t *pLength) { - const UChar *p; - uint32_t offset=RES_GET_OFFSET(res); - int32_t length; - if(RES_GET_TYPE(res)==URES_ALIAS) { - const int32_t *p32= offset==0 ? &gEmptyString.length : pResData->pRoot+offset; - length=*p32++; - p=(const UChar *)p32; - } else { - p=NULL; - length=0; - } - if(pLength) { - *pLength=length; - } - return p; -} - -U_CAPI const uint8_t * U_EXPORT2 -res_getBinaryNoTrace(const ResourceData *pResData, Resource res, int32_t *pLength) { - const uint8_t *p; - uint32_t offset=RES_GET_OFFSET(res); - int32_t length; - if(RES_GET_TYPE(res)==URES_BINARY) { - const int32_t *p32= offset==0 ? (const int32_t*)&gEmpty32 : pResData->pRoot+offset; - length=*p32++; - p=(const uint8_t *)p32; - } else { - p=NULL; - length=0; - } - if(pLength) { - *pLength=length; - } - return p; -} - - -U_CAPI const int32_t * U_EXPORT2 -res_getIntVectorNoTrace(const ResourceData *pResData, Resource res, int32_t *pLength) { - const int32_t *p; - uint32_t offset=RES_GET_OFFSET(res); - int32_t length; - if(RES_GET_TYPE(res)==URES_INT_VECTOR) { - p= offset==0 ? (const int32_t *)&gEmpty32 : pResData->pRoot+offset; - length=*p++; - } else { - p=NULL; - length=0; - } - if(pLength) { - *pLength=length; - } - return p; -} - -U_CAPI int32_t U_EXPORT2 -res_countArrayItems(const ResourceData *pResData, Resource res) { - uint32_t offset=RES_GET_OFFSET(res); - switch(RES_GET_TYPE(res)) { - case URES_STRING: - case URES_STRING_V2: - case URES_BINARY: - case URES_ALIAS: - case URES_INT: - case URES_INT_VECTOR: - return 1; - case URES_ARRAY: - case URES_TABLE32: - return offset==0 ? 0 : *(pResData->pRoot+offset); - case URES_TABLE: - return offset==0 ? 0 : *((const uint16_t *)(pResData->pRoot+offset)); - case URES_ARRAY16: - case URES_TABLE16: - return pResData->p16BitUnits[offset]; - default: - return 0; - } -} - -U_NAMESPACE_BEGIN - -ResourceDataValue::~ResourceDataValue() {} - -UResType ResourceDataValue::getType() const { - return res_getPublicType(res); -} - -const UChar *ResourceDataValue::getString(int32_t &length, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { - return NULL; - } - const UChar *s = res_getString(fTraceInfo, &getData(), res, &length); - if(s == NULL) { - errorCode = U_RESOURCE_TYPE_MISMATCH; - } - return s; -} - -const UChar *ResourceDataValue::getAliasString(int32_t &length, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { - return NULL; - } - const UChar *s = res_getAlias(&getData(), res, &length); - if(s == NULL) { - errorCode = U_RESOURCE_TYPE_MISMATCH; - } - return s; -} - -int32_t ResourceDataValue::getInt(UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { - return 0; - } - if(RES_GET_TYPE(res) != URES_INT) { - errorCode = U_RESOURCE_TYPE_MISMATCH; - } - return res_getInt(fTraceInfo, res); -} - -uint32_t ResourceDataValue::getUInt(UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { - return 0; - } - if(RES_GET_TYPE(res) != URES_INT) { - errorCode = U_RESOURCE_TYPE_MISMATCH; - } - return res_getUInt(fTraceInfo, res); -} - -const int32_t *ResourceDataValue::getIntVector(int32_t &length, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { - return NULL; - } - const int32_t *iv = res_getIntVector(fTraceInfo, &getData(), res, &length); - if(iv == NULL) { - errorCode = U_RESOURCE_TYPE_MISMATCH; - } - return iv; -} - -const uint8_t *ResourceDataValue::getBinary(int32_t &length, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { - return NULL; - } - const uint8_t *b = res_getBinary(fTraceInfo, &getData(), res, &length); - if(b == NULL) { - errorCode = U_RESOURCE_TYPE_MISMATCH; - } - return b; -} - -ResourceArray ResourceDataValue::getArray(UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { - return ResourceArray(); - } - const uint16_t *items16 = NULL; - const Resource *items32 = NULL; - uint32_t offset=RES_GET_OFFSET(res); - int32_t length = 0; - switch(RES_GET_TYPE(res)) { - case URES_ARRAY: - if (offset!=0) { // empty if offset==0 - items32 = (const Resource *)getData().pRoot+offset; - length = *items32++; - } - break; - case URES_ARRAY16: - items16 = getData().p16BitUnits+offset; - length = *items16++; - break; - default: - errorCode = U_RESOURCE_TYPE_MISMATCH; - return ResourceArray(); - } - return ResourceArray(items16, items32, length, fTraceInfo); -} - -ResourceTable ResourceDataValue::getTable(UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { - return ResourceTable(); - } - const uint16_t *keys16 = NULL; - const int32_t *keys32 = NULL; - const uint16_t *items16 = NULL; - const Resource *items32 = NULL; - uint32_t offset = RES_GET_OFFSET(res); - int32_t length = 0; - switch(RES_GET_TYPE(res)) { - case URES_TABLE: - if (offset != 0) { // empty if offset==0 - keys16 = (const uint16_t *)(getData().pRoot+offset); - length = *keys16++; - items32 = (const Resource *)(keys16+length+(~length&1)); - } - break; - case URES_TABLE16: - keys16 = getData().p16BitUnits+offset; - length = *keys16++; - items16 = keys16 + length; - break; - case URES_TABLE32: - if (offset != 0) { // empty if offset==0 - keys32 = getData().pRoot+offset; - length = *keys32++; - items32 = (const Resource *)keys32 + length; - } - break; - default: - errorCode = U_RESOURCE_TYPE_MISMATCH; - return ResourceTable(); - } - return ResourceTable(keys16, keys32, items16, items32, length, fTraceInfo); -} - -UBool ResourceDataValue::isNoInheritanceMarker() const { - return ::isNoInheritanceMarker(&getData(), res); -} - -int32_t ResourceDataValue::getStringArray(UnicodeString *dest, int32_t capacity, - UErrorCode &errorCode) const { - return ::getStringArray(&getData(), getArray(errorCode), dest, capacity, errorCode); -} - -int32_t ResourceDataValue::getStringArrayOrStringAsArray(UnicodeString *dest, int32_t capacity, - UErrorCode &errorCode) const { - if(URES_IS_ARRAY(res)) { - return ::getStringArray(&getData(), getArray(errorCode), dest, capacity, errorCode); - } - if(U_FAILURE(errorCode)) { - return 0; - } - if(dest == NULL ? capacity != 0 : capacity < 0) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - if(capacity < 1) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return 1; - } - int32_t sLength; - const UChar *s = res_getString(fTraceInfo, &getData(), res, &sLength); - if(s != NULL) { - dest[0].setTo(true, s, sLength); - return 1; - } - errorCode = U_RESOURCE_TYPE_MISMATCH; - return 0; -} - -UnicodeString ResourceDataValue::getStringOrFirstOfArray(UErrorCode &errorCode) const { - UnicodeString us; - if(U_FAILURE(errorCode)) { - return us; - } - int32_t sLength; - const UChar *s = res_getString(fTraceInfo, &getData(), res, &sLength); - if(s != NULL) { - us.setTo(true, s, sLength); - return us; - } - ResourceArray array = getArray(errorCode); - if(U_FAILURE(errorCode)) { - return us; - } - if(array.getSize() > 0) { - // Tracing is already performed above (unimportant for trace that this is an array) - s = res_getStringNoTrace(&getData(), array.internalGetResource(&getData(), 0), &sLength); - if(s != NULL) { - us.setTo(true, s, sLength); - return us; - } - } - errorCode = U_RESOURCE_TYPE_MISMATCH; - return us; -} - -U_NAMESPACE_END - -static Resource -makeResourceFrom16(const ResourceData *pResData, int32_t res16) { - if(res16poolStringIndex16Limit) { - // Pool string, nothing to do. - } else { - // Local string, adjust the 16-bit offset to a regular one, - // with a larger pool string index limit. - res16=res16-pResData->poolStringIndex16Limit+pResData->poolStringIndexLimit; - } - return URES_MAKE_RESOURCE(URES_STRING_V2, res16); -} - -U_CAPI Resource U_EXPORT2 -res_getTableItemByKey(const ResourceData *pResData, Resource table, - int32_t *indexR, const char **key) { - uint32_t offset=RES_GET_OFFSET(table); - int32_t length; - int32_t idx; - if(key == NULL || *key == NULL) { - return RES_BOGUS; - } - switch(RES_GET_TYPE(table)) { - case URES_TABLE: { - if (offset!=0) { /* empty if offset==0 */ - const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset); - length=*p++; - *indexR=idx=_res_findTableItem(pResData, p, length, *key, key); - if(idx>=0) { - const Resource *p32=(const Resource *)(p+length+(~length&1)); - return p32[idx]; - } - } - break; - } - case URES_TABLE16: { - const uint16_t *p=pResData->p16BitUnits+offset; - length=*p++; - *indexR=idx=_res_findTableItem(pResData, p, length, *key, key); - if(idx>=0) { - return makeResourceFrom16(pResData, p[length+idx]); - } - break; - } - case URES_TABLE32: { - if (offset!=0) { /* empty if offset==0 */ - const int32_t *p= pResData->pRoot+offset; - length=*p++; - *indexR=idx=_res_findTable32Item(pResData, p, length, *key, key); - if(idx>=0) { - return (Resource)p[length+idx]; - } - } - break; - } - default: - break; - } - return RES_BOGUS; -} - -U_CAPI Resource U_EXPORT2 -res_getTableItemByIndex(const ResourceData *pResData, Resource table, - int32_t indexR, const char **key) { - uint32_t offset=RES_GET_OFFSET(table); - int32_t length; - if (indexR < 0) { - return RES_BOGUS; - } - switch(RES_GET_TYPE(table)) { - case URES_TABLE: { - if (offset != 0) { /* empty if offset==0 */ - const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset); - length=*p++; - if(indexRp16BitUnits+offset; - length=*p++; - if(indexRpRoot+offset; - length=*p++; - if(indexRrootRes, &idx, &realKey); -} - - -UBool icu::ResourceTable::getKeyAndValue(int32_t i, - const char *&key, icu::ResourceValue &value) const { - if(0 <= i && i < length) { - icu::ResourceDataValue &rdValue = static_cast(value); - if (keys16 != nullptr) { - key = RES_GET_KEY16(&rdValue.getData(), keys16[i]); - } else { - key = RES_GET_KEY32(&rdValue.getData(), keys32[i]); - } - Resource res; - if (items16 != nullptr) { - res = makeResourceFrom16(&rdValue.getData(), items16[i]); - } else { - res = items32[i]; - } - // Note: the ResourceTracer keeps a reference to the field of this - // ResourceTable. This is OK because the ResourceTable should remain - // alive for the duration that fields are being read from it - // (including nested fields). - rdValue.setResource(res, ResourceTracer(fTraceInfo, key)); - return true; - } - return false; -} - -UBool icu::ResourceTable::findValue(const char *key, ResourceValue &value) const { - icu::ResourceDataValue &rdValue = static_cast(value); - const char *realKey = nullptr; - int32_t i; - if (keys16 != nullptr) { - i = _res_findTableItem(&rdValue.getData(), keys16, length, key, &realKey); - } else { - i = _res_findTable32Item(&rdValue.getData(), keys32, length, key, &realKey); - } - if (i >= 0) { - Resource res; - if (items16 != nullptr) { - res = makeResourceFrom16(&rdValue.getData(), items16[i]); - } else { - res = items32[i]; - } - // Same note about lifetime as in getKeyAndValue(). - rdValue.setResource(res, ResourceTracer(fTraceInfo, key)); - return true; - } - return false; -} - -U_CAPI Resource U_EXPORT2 -res_getArrayItem(const ResourceData *pResData, Resource array, int32_t indexR) { - uint32_t offset=RES_GET_OFFSET(array); - if (indexR < 0) { - return RES_BOGUS; - } - switch(RES_GET_TYPE(array)) { - case URES_ARRAY: { - if (offset!=0) { /* empty if offset==0 */ - const int32_t *p= pResData->pRoot+offset; - if(indexR<*p) { - return (Resource)p[1+indexR]; - } - } - break; - } - case URES_ARRAY16: { - const uint16_t *p=pResData->p16BitUnits+offset; - if(indexR<*p) { - return makeResourceFrom16(pResData, p[1+indexR]); - } - break; - } - default: - break; - } - return RES_BOGUS; -} - -uint32_t icu::ResourceArray::internalGetResource(const ResourceData *pResData, int32_t i) const { - if (items16 != NULL) { - return makeResourceFrom16(pResData, items16[i]); - } else { - return items32[i]; - } -} - -UBool icu::ResourceArray::getValue(int32_t i, icu::ResourceValue &value) const { - if(0 <= i && i < length) { - icu::ResourceDataValue &rdValue = static_cast(value); - // Note: the ResourceTracer keeps a reference to the field of this - // ResourceArray. This is OK because the ResourceArray should remain - // alive for the duration that fields are being read from it - // (including nested fields). - rdValue.setResource( - internalGetResource(&rdValue.getData(), i), - ResourceTracer(fTraceInfo, i)); - return true; - } - return false; -} - -U_CFUNC Resource -res_findResource(const ResourceData *pResData, Resource r, char** path, const char** key) { - char *pathP = *path, *nextSepP = *path; - char *closeIndex = NULL; - Resource t1 = r; - Resource t2; - int32_t indexR = 0; - UResType type = (UResType)RES_GET_TYPE(t1); - - /* if you come in with an empty path, you'll be getting back the same resource */ - if(!uprv_strlen(pathP)) { - return r; - } - - /* one needs to have an aggregate resource in order to search in it */ - if(!URES_IS_CONTAINER(type)) { - return RES_BOGUS; - } - - while(nextSepP && *pathP && t1 != RES_BOGUS && URES_IS_CONTAINER(type)) { - /* Iteration stops if: the path has been consumed, we found a non-existing - * resource (t1 == RES_BOGUS) or we found a scalar resource (including alias) - */ - nextSepP = uprv_strchr(pathP, RES_PATH_SEPARATOR); - /* if there are more separators, terminate string - * and set path to the remaining part of the string - */ - if(nextSepP != NULL) { - if(nextSepP == pathP) { - // Empty key string. - return RES_BOGUS; - } - *nextSepP = 0; /* overwrite the separator with a NUL to terminate the key */ - *path = nextSepP+1; - } else { - *path = uprv_strchr(pathP, 0); - } - - /* if the resource is a table */ - /* try the key based access */ - if(URES_IS_TABLE(type)) { - *key = pathP; - t2 = res_getTableItemByKey(pResData, t1, &indexR, key); - } else if(URES_IS_ARRAY(type)) { - indexR = uprv_strtol(pathP, &closeIndex, 10); - if(indexR >= 0 && *closeIndex == 0) { - t2 = res_getArrayItem(pResData, t1, indexR); - } else { - t2 = RES_BOGUS; /* have an array, but don't have a valid index */ - } - *key = NULL; - } else { /* can't do much here, except setting t2 to bogus */ - t2 = RES_BOGUS; - } - t1 = t2; - type = (UResType)RES_GET_TYPE(t1); - /* position pathP to next resource key/index */ - pathP = *path; - } - - return t1; -} - -/* resource bundle swapping ------------------------------------------------- */ - -/* - * Need to always enumerate the entire item tree, - * track the lowest address of any item to use as the limit for char keys[], - * track the highest address of any item to return the size of the data. - * - * We should have thought of storing those in the data... - * It is possible to extend the data structure by putting additional values - * in places that are inaccessible by ordinary enumeration of the item tree. - * For example, additional integers could be stored at the beginning or - * end of the key strings; this could be indicated by a minor version number, - * and the data swapping would have to know about these values. - * - * The data structure does not forbid keys to be shared, so we must swap - * all keys once instead of each key when it is referenced. - * - * These swapping functions assume that a resource bundle always has a length - * that is a multiple of 4 bytes. - * Currently, this is trivially true because genrb writes bundle tree leaves - * physically first, before their branches, so that the root table with its - * array of resource items (uint32_t values) is always last. - */ - -/* definitions for table sorting ------------------------ */ - -/* - * row of a temporary array - * - * gets platform-endian key string indexes and sorting indexes; - * after sorting this array by keys, the actual key/value arrays are permutated - * according to the sorting indexes - */ -typedef struct Row { - int32_t keyIndex, sortIndex; -} Row; - -static int32_t U_CALLCONV -ures_compareRows(const void *context, const void *left, const void *right) { - const char *keyChars=(const char *)context; - return (int32_t)uprv_strcmp(keyChars+((const Row *)left)->keyIndex, - keyChars+((const Row *)right)->keyIndex); -} - -typedef struct TempTable { - const char *keyChars; - Row *rows; - int32_t *resort; - uint32_t *resFlags; - int32_t localKeyLimit; - uint8_t majorFormatVersion; -} TempTable; - -enum { - STACK_ROW_CAPACITY=200 -}; - -/* The table item key string is not locally available. */ -static const char *const gUnknownKey=""; - -/* resource table key for collation binaries: "%%CollationBin" */ -static const UChar gCollationBinKey[]={ - 0x25, 0x25, - 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x42, 0x69, 0x6e, - 0 -}; - -/* - * swap one resource item - */ -static void -ures_swapResource(const UDataSwapper *ds, - const Resource *inBundle, Resource *outBundle, - Resource res, /* caller swaps res itself */ - const char *key, - TempTable *pTempTable, - UErrorCode *pErrorCode) { - const Resource *p; - Resource *q; - int32_t offset, count; - - switch(RES_GET_TYPE(res)) { - case URES_TABLE16: - case URES_STRING_V2: - case URES_INT: - case URES_ARRAY16: - /* integer, or points to 16-bit units, nothing to do here */ - return; - default: - break; - } - - /* all other types use an offset to point to their data */ - offset=(int32_t)RES_GET_OFFSET(res); - if(offset==0) { - /* special offset indicating an empty item */ - return; - } - if(pTempTable->resFlags[offset>>5]&((uint32_t)1<<(offset&0x1f))) { - /* we already swapped this resource item */ - return; - } else { - /* mark it as swapped now */ - pTempTable->resFlags[offset>>5]|=((uint32_t)1<<(offset&0x1f)); - } - - p=inBundle+offset; - q=outBundle+offset; - - switch(RES_GET_TYPE(res)) { - case URES_ALIAS: - /* physically same value layout as string, fall through */ - U_FALLTHROUGH; - case URES_STRING: - count=udata_readInt32(ds, (int32_t)*p); - /* swap length */ - ds->swapArray32(ds, p, 4, q, pErrorCode); - /* swap each UChar (the terminating NUL would not change) */ - ds->swapArray16(ds, p+1, 2*count, q+1, pErrorCode); - break; - case URES_BINARY: - count=udata_readInt32(ds, (int32_t)*p); - /* swap length */ - ds->swapArray32(ds, p, 4, q, pErrorCode); - /* no need to swap or copy bytes - ures_swap() copied them all */ - - /* swap known formats */ -#if !UCONFIG_NO_COLLATION - if( key!=NULL && /* the binary is in a table */ - (key!=gUnknownKey ? - /* its table key string is "%%CollationBin" */ - 0==ds->compareInvChars(ds, key, -1, - gCollationBinKey, UPRV_LENGTHOF(gCollationBinKey)-1) : - /* its table key string is unknown but it looks like a collation binary */ - ucol_looksLikeCollationBinary(ds, p+1, count)) - ) { - ucol_swap(ds, p+1, count, q+1, pErrorCode); - } -#endif - break; - case URES_TABLE: - case URES_TABLE32: - { - const uint16_t *pKey16; - uint16_t *qKey16; - - const int32_t *pKey32; - int32_t *qKey32; - - Resource item; - int32_t i, oldIndex; - - if(RES_GET_TYPE(res)==URES_TABLE) { - /* get table item count */ - pKey16=(const uint16_t *)p; - qKey16=(uint16_t *)q; - count=ds->readUInt16(*pKey16); - - pKey32=qKey32=NULL; - - /* swap count */ - ds->swapArray16(ds, pKey16++, 2, qKey16++, pErrorCode); - - offset+=((1+count)+1)/2; - } else { - /* get table item count */ - pKey32=(const int32_t *)p; - qKey32=(int32_t *)q; - count=udata_readInt32(ds, *pKey32); - - pKey16=qKey16=NULL; - - /* swap count */ - ds->swapArray32(ds, pKey32++, 4, qKey32++, pErrorCode); - - offset+=1+count; - } - - if(count==0) { - break; - } - - p=inBundle+offset; /* pointer to table resources */ - q=outBundle+offset; - - /* recurse */ - for(i=0; ireadUInt16(pKey16[i]); - if(keyOffsetlocalKeyLimit) { - itemKey=(const char *)outBundle+keyOffset; - } - } else { - int32_t keyOffset=udata_readInt32(ds, pKey32[i]); - if(keyOffset>=0) { - itemKey=(const char *)outBundle+keyOffset; - } - } - item=ds->readUInt32(p[i]); - ures_swapResource(ds, inBundle, outBundle, item, itemKey, pTempTable, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - udata_printError(ds, "ures_swapResource(table res=%08x)[%d].recurse(%08x) failed\n", - res, i, item); - return; - } - } - - if(pTempTable->majorFormatVersion>1 || ds->inCharset==ds->outCharset) { - /* no need to sort, just swap the offset/value arrays */ - if(pKey16!=NULL) { - ds->swapArray16(ds, pKey16, count*2, qKey16, pErrorCode); - ds->swapArray32(ds, p, count*4, q, pErrorCode); - } else { - /* swap key offsets and items as one array */ - ds->swapArray32(ds, pKey32, count*2*4, qKey32, pErrorCode); - } - break; - } - - /* - * We need to sort tables by outCharset key strings because they - * sort differently for different charset families. - * ures_swap() already set pTempTable->keyChars appropriately. - * First we set up a temporary table with the key indexes and - * sorting indexes and sort that. - * Then we permutate and copy/swap the actual values. - */ - if(pKey16!=NULL) { - for(i=0; irows[i].keyIndex=ds->readUInt16(pKey16[i]); - pTempTable->rows[i].sortIndex=i; - } - } else { - for(i=0; irows[i].keyIndex=udata_readInt32(ds, pKey32[i]); - pTempTable->rows[i].sortIndex=i; - } - } - uprv_sortArray(pTempTable->rows, count, sizeof(Row), - ures_compareRows, pTempTable->keyChars, - false, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - udata_printError(ds, "ures_swapResource(table res=%08x).uprv_sortArray(%d items) failed\n", - res, count); - return; - } - - /* - * copy/swap/permutate items - * - * If we swap in-place, then the permutation must use another - * temporary array (pTempTable->resort) - * before the results are copied to the outBundle. - */ - /* keys */ - if(pKey16!=NULL) { - uint16_t *rKey16; - - if(pKey16!=qKey16) { - rKey16=qKey16; - } else { - rKey16=(uint16_t *)pTempTable->resort; - } - for(i=0; irows[i].sortIndex; - ds->swapArray16(ds, pKey16+oldIndex, 2, rKey16+i, pErrorCode); - } - if(qKey16!=rKey16) { - uprv_memcpy(qKey16, rKey16, 2*count); - } - } else { - int32_t *rKey32; - - if(pKey32!=qKey32) { - rKey32=qKey32; - } else { - rKey32=pTempTable->resort; - } - for(i=0; irows[i].sortIndex; - ds->swapArray32(ds, pKey32+oldIndex, 4, rKey32+i, pErrorCode); - } - if(qKey32!=rKey32) { - uprv_memcpy(qKey32, rKey32, 4*count); - } - } - - /* resources */ - { - Resource *r; - - - if(p!=q) { - r=q; - } else { - r=(Resource *)pTempTable->resort; - } - for(i=0; irows[i].sortIndex; - ds->swapArray32(ds, p+oldIndex, 4, r+i, pErrorCode); - } - if(q!=r) { - uprv_memcpy(q, r, 4*count); - } - } - } - break; - case URES_ARRAY: - { - Resource item; - int32_t i; - - count=udata_readInt32(ds, (int32_t)*p); - /* swap length */ - ds->swapArray32(ds, p++, 4, q++, pErrorCode); - - /* recurse */ - for(i=0; ireadUInt32(p[i]); - ures_swapResource(ds, inBundle, outBundle, item, NULL, pTempTable, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - udata_printError(ds, "ures_swapResource(array res=%08x)[%d].recurse(%08x) failed\n", - res, i, item); - return; - } - } - - /* swap items */ - ds->swapArray32(ds, p, 4*count, q, pErrorCode); - } - break; - case URES_INT_VECTOR: - count=udata_readInt32(ds, (int32_t)*p); - /* swap length and each integer */ - ds->swapArray32(ds, p, 4*(1+count), q, pErrorCode); - break; - default: - /* also catches RES_BOGUS */ - *pErrorCode=U_UNSUPPORTED_ERROR; - break; - } -} - -U_CAPI int32_t U_EXPORT2 -ures_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const UDataInfo *pInfo; - const Resource *inBundle; - Resource rootRes; - int32_t headerSize, maxTableLength; - - Row rows[STACK_ROW_CAPACITY]; - int32_t resort[STACK_ROW_CAPACITY]; - TempTable tempTable; - - const int32_t *inIndexes; - - /* the following integers count Resource item offsets (4 bytes each), not bytes */ - int32_t bundleLength, indexLength, keysBottom, keysTop, resBottom, top; - - /* udata_swapDataHeader checks the arguments */ - headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - /* check data format and format version */ - pInfo=(const UDataInfo *)((const char *)inData+4); - if(!( - pInfo->dataFormat[0]==0x52 && /* dataFormat="ResB" */ - pInfo->dataFormat[1]==0x65 && - pInfo->dataFormat[2]==0x73 && - pInfo->dataFormat[3]==0x42 && - /* formatVersion 1.1+ or 2.x or 3.x */ - ((pInfo->formatVersion[0]==1 && pInfo->formatVersion[1]>=1) || - pInfo->formatVersion[0]==2 || pInfo->formatVersion[0]==3) - )) { - udata_printError(ds, "ures_swap(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not a resource bundle\n", - pInfo->dataFormat[0], pInfo->dataFormat[1], - pInfo->dataFormat[2], pInfo->dataFormat[3], - pInfo->formatVersion[0], pInfo->formatVersion[1]); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - tempTable.majorFormatVersion=pInfo->formatVersion[0]; - - /* a resource bundle must contain at least one resource item */ - if(length<0) { - bundleLength=-1; - } else { - bundleLength=(length-headerSize)/4; - - /* formatVersion 1.1 must have a root item and at least 5 indexes */ - if(bundleLength<(1+5)) { - udata_printError(ds, "ures_swap(): too few bytes (%d after header) for a resource bundle\n", - length-headerSize); - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - } - - inBundle=(const Resource *)((const char *)inData+headerSize); - rootRes=ds->readUInt32(*inBundle); - - /* formatVersion 1.1 adds the indexes[] array */ - inIndexes=(const int32_t *)(inBundle+1); - - indexLength=udata_readInt32(ds, inIndexes[URES_INDEX_LENGTH])&0xff; - if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) { - udata_printError(ds, "ures_swap(): too few indexes for a 1.1+ resource bundle\n"); - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - keysBottom=1+indexLength; - keysTop=udata_readInt32(ds, inIndexes[URES_INDEX_KEYS_TOP]); - if(indexLength>URES_INDEX_16BIT_TOP) { - resBottom=udata_readInt32(ds, inIndexes[URES_INDEX_16BIT_TOP]); - } else { - resBottom=keysTop; - } - top=udata_readInt32(ds, inIndexes[URES_INDEX_BUNDLE_TOP]); - maxTableLength=udata_readInt32(ds, inIndexes[URES_INDEX_MAX_TABLE_LENGTH]); - - if(0<=bundleLength && bundleLength(1+indexLength)) { - tempTable.localKeyLimit=keysTop<<2; - } else { - tempTable.localKeyLimit=0; - } - - if(length>=0) { - Resource *outBundle=(Resource *)((char *)outData+headerSize); - - /* track which resources we have already swapped */ - uint32_t stackResFlags[STACK_ROW_CAPACITY]; - int32_t resFlagsLength; - - /* - * We need one bit per 4 resource bundle bytes so that we can track - * every possible Resource for whether we have swapped it already. - * Multiple Resource words can refer to the same bundle offsets - * for sharing identical values. - * We could optimize this by allocating only for locations above - * where Resource values are stored (above keys & strings). - */ - resFlagsLength=(length+31)>>5; /* number of bytes needed */ - resFlagsLength=(resFlagsLength+3)&~3; /* multiple of 4 bytes for uint32_t */ - if(resFlagsLength<=(int32_t)sizeof(stackResFlags)) { - tempTable.resFlags=stackResFlags; - } else { - tempTable.resFlags=(uint32_t *)uprv_malloc(resFlagsLength); - if(tempTable.resFlags==NULL) { - udata_printError(ds, "ures_swap(): unable to allocate memory for tracking resources\n"); - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return 0; - } - } - uprv_memset(tempTable.resFlags, 0, resFlagsLength); - - /* copy the bundle for binary and inaccessible data */ - if(inData!=outData) { - uprv_memcpy(outBundle, inBundle, 4*top); - } - - /* swap the key strings, but not the padding bytes (0xaa) after the last string and its NUL */ - udata_swapInvStringBlock(ds, inBundle+keysBottom, 4*(keysTop-keysBottom), - outBundle+keysBottom, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - udata_printError(ds, "ures_swap().udata_swapInvStringBlock(keys[%d]) failed\n", 4*(keysTop-keysBottom)); - return 0; - } - - /* swap the 16-bit units (strings, table16, array16) */ - if(keysTopswapArray16(ds, inBundle+keysTop, (resBottom-keysTop)*4, outBundle+keysTop, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - udata_printError(ds, "ures_swap().swapArray16(16-bit units[%d]) failed\n", 2*(resBottom-keysTop)); - return 0; - } - } - - /* allocate the temporary table for sorting resource tables */ - tempTable.keyChars=(const char *)outBundle; /* sort by outCharset */ - if(tempTable.majorFormatVersion>1 || maxTableLength<=STACK_ROW_CAPACITY) { - tempTable.rows=rows; - tempTable.resort=resort; - } else { - tempTable.rows=(Row *)uprv_malloc(maxTableLength*sizeof(Row)+maxTableLength*4); - if(tempTable.rows==NULL) { - udata_printError(ds, "ures_swap(): unable to allocate memory for sorting tables (max length: %d)\n", - maxTableLength); - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - if(tempTable.resFlags!=stackResFlags) { - uprv_free(tempTable.resFlags); - } - return 0; - } - tempTable.resort=(int32_t *)(tempTable.rows+maxTableLength); - } - - /* swap the resources */ - ures_swapResource(ds, inBundle, outBundle, rootRes, NULL, &tempTable, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - udata_printError(ds, "ures_swapResource(root res=%08x) failed\n", - rootRes); - } - - if(tempTable.rows!=rows) { - uprv_free(tempTable.rows); - } - if(tempTable.resFlags!=stackResFlags) { - uprv_free(tempTable.resFlags); - } - - /* swap the root resource and indexes */ - ds->swapArray32(ds, inBundle, keysBottom*4, outBundle, pErrorCode); - } - - return headerSize+4*top; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1999-2016, International Business Machines Corporation +* and others. All Rights Reserved. +******************************************************************************* +* file name: uresdata.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999dec08 +* created by: Markus W. Scherer +* Modification History: +* +* Date Name Description +* 06/20/2000 helena OS/400 port changes; mostly typecast. +* 06/24/02 weiv Added support for resource sharing +*/ + +#include "unicode/utypes.h" +#include "unicode/udata.h" +#include "unicode/ustring.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "cstring.h" +#include "resource.h" +#include "uarrsort.h" +#include "uassert.h" +#include "ucol_swp.h" +#include "udataswp.h" +#include "uinvchar.h" +#include "uresdata.h" +#include "uresimp.h" +#include "utracimp.h" + +/* + * Resource access helpers + */ + +/* get a const char* pointer to the key with the keyOffset byte offset from pRoot */ +#define RES_GET_KEY16(pResData, keyOffset) \ + ((keyOffset)<(pResData)->localKeyLimit ? \ + (const char *)(pResData)->pRoot+(keyOffset) : \ + (pResData)->poolBundleKeys+(keyOffset)-(pResData)->localKeyLimit) + +#define RES_GET_KEY32(pResData, keyOffset) \ + ((keyOffset)>=0 ? \ + (const char *)(pResData)->pRoot+(keyOffset) : \ + (pResData)->poolBundleKeys+((keyOffset)&0x7fffffff)) + +#define URESDATA_ITEM_NOT_FOUND -1 + +/* empty resources, returned when the resource offset is 0 */ +static const uint16_t gEmpty16=0; + +static const struct { + int32_t length; + int32_t res; +} gEmpty32={ 0, 0 }; + +static const struct { + int32_t length; + char16_t nul; + char16_t pad; +} gEmptyString={ 0, 0, 0 }; + +/* + * All the type-access functions assume that + * the resource is of the expected type. + */ + +static int32_t +_res_findTableItem(const ResourceData *pResData, const uint16_t *keyOffsets, int32_t length, + const char *key, const char **realKey) { + const char *tableKey; + int32_t mid, start, limit; + int result; + + /* do a binary search for the key */ + start=0; + limit=length; + while(startuseNativeStrcmp) { + result = uprv_strcmp(key, tableKey); + } else { + result = uprv_compareInvCharsAsAscii(key, tableKey); + } + if (result < 0) { + limit = mid; + } else if (result > 0) { + start = mid + 1; + } else { + /* We found it! */ + *realKey=tableKey; + return mid; + } + } + return URESDATA_ITEM_NOT_FOUND; /* not found or table is empty. */ +} + +static int32_t +_res_findTable32Item(const ResourceData *pResData, const int32_t *keyOffsets, int32_t length, + const char *key, const char **realKey) { + const char *tableKey; + int32_t mid, start, limit; + int result; + + /* do a binary search for the key */ + start=0; + limit=length; + while(startuseNativeStrcmp) { + result = uprv_strcmp(key, tableKey); + } else { + result = uprv_compareInvCharsAsAscii(key, tableKey); + } + if (result < 0) { + limit = mid; + } else if (result > 0) { + start = mid + 1; + } else { + /* We found it! */ + *realKey=tableKey; + return mid; + } + } + return URESDATA_ITEM_NOT_FOUND; /* not found or table is empty. */ +} + +/* helper for res_load() ---------------------------------------------------- */ + +static UBool U_CALLCONV +isAcceptable(void *context, + const char * /*type*/, const char * /*name*/, + const UDataInfo *pInfo) { + uprv_memcpy(context, pInfo->formatVersion, 4); + return (UBool)( + pInfo->size>=20 && + pInfo->isBigEndian==U_IS_BIG_ENDIAN && + pInfo->charsetFamily==U_CHARSET_FAMILY && + pInfo->sizeofUChar==U_SIZEOF_UCHAR && + pInfo->dataFormat[0]==0x52 && /* dataFormat="ResB" */ + pInfo->dataFormat[1]==0x65 && + pInfo->dataFormat[2]==0x73 && + pInfo->dataFormat[3]==0x42 && + (1<=pInfo->formatVersion[0] && pInfo->formatVersion[0]<=3)); +} + +/* semi-public functions ---------------------------------------------------- */ + +static void +res_init(ResourceData *pResData, + UVersionInfo formatVersion, const void *inBytes, int32_t length, + UErrorCode *errorCode) { + UResType rootType; + + /* get the root resource */ + pResData->pRoot=(const int32_t *)inBytes; + pResData->rootRes=(Resource)*pResData->pRoot; + pResData->p16BitUnits=&gEmpty16; + + /* formatVersion 1.1 must have a root item and at least 5 indexes */ + if(length>=0 && (length/4)<((formatVersion[0]==1 && formatVersion[1]==0) ? 1 : 1+5)) { + *errorCode=U_INVALID_FORMAT_ERROR; + res_unload(pResData); + return; + } + + /* currently, we accept only resources that have a Table as their roots */ + rootType=(UResType)RES_GET_TYPE(pResData->rootRes); + if(!URES_IS_TABLE(rootType)) { + *errorCode=U_INVALID_FORMAT_ERROR; + res_unload(pResData); + return; + } + + if(formatVersion[0]==1 && formatVersion[1]==0) { + pResData->localKeyLimit=0x10000; /* greater than any 16-bit key string offset */ + } else { + /* bundles with formatVersion 1.1 and later contain an indexes[] array */ + const int32_t *indexes=pResData->pRoot+1; + int32_t indexLength=indexes[URES_INDEX_LENGTH]&0xff; + if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) { + *errorCode=U_INVALID_FORMAT_ERROR; + res_unload(pResData); + return; + } + if( length>=0 && + (length<((1+indexLength)<<2) || + length<(indexes[URES_INDEX_BUNDLE_TOP]<<2)) + ) { + *errorCode=U_INVALID_FORMAT_ERROR; + res_unload(pResData); + return; + } + if(indexes[URES_INDEX_KEYS_TOP]>(1+indexLength)) { + pResData->localKeyLimit=indexes[URES_INDEX_KEYS_TOP]<<2; + } + if(formatVersion[0]>=3) { + // In formatVersion 1, the indexLength took up this whole int. + // In version 2, bits 31..8 were reserved and always 0. + // In version 3, they contain bits 23..0 of the poolStringIndexLimit. + // Bits 27..24 are in indexes[URES_INDEX_ATTRIBUTES] bits 15..12. + pResData->poolStringIndexLimit=(int32_t)((uint32_t)indexes[URES_INDEX_LENGTH]>>8); + } + if(indexLength>URES_INDEX_ATTRIBUTES) { + int32_t att=indexes[URES_INDEX_ATTRIBUTES]; + pResData->noFallback=(UBool)(att&URES_ATT_NO_FALLBACK); + pResData->isPoolBundle=(UBool)((att&URES_ATT_IS_POOL_BUNDLE)!=0); + pResData->usesPoolBundle=(UBool)((att&URES_ATT_USES_POOL_BUNDLE)!=0); + pResData->poolStringIndexLimit|=(att&0xf000)<<12; // bits 15..12 -> 27..24 + pResData->poolStringIndex16Limit=(int32_t)((uint32_t)att>>16); + } + if((pResData->isPoolBundle || pResData->usesPoolBundle) && indexLength<=URES_INDEX_POOL_CHECKSUM) { + *errorCode=U_INVALID_FORMAT_ERROR; + res_unload(pResData); + return; + } + if( indexLength>URES_INDEX_16BIT_TOP && + indexes[URES_INDEX_16BIT_TOP]>indexes[URES_INDEX_KEYS_TOP] + ) { + pResData->p16BitUnits=(const uint16_t *)(pResData->pRoot+indexes[URES_INDEX_KEYS_TOP]); + } + } + + if(formatVersion[0]==1 || U_CHARSET_FAMILY==U_ASCII_FAMILY) { + /* + * formatVersion 1: compare key strings in native-charset order + * formatVersion 2 and up: compare key strings in ASCII order + */ + pResData->useNativeStrcmp=true; + } +} + +U_CAPI void U_EXPORT2 +res_read(ResourceData *pResData, + const UDataInfo *pInfo, const void *inBytes, int32_t length, + UErrorCode *errorCode) { + UVersionInfo formatVersion; + + uprv_memset(pResData, 0, sizeof(ResourceData)); + if(U_FAILURE(*errorCode)) { + return; + } + if(!isAcceptable(formatVersion, nullptr, nullptr, pInfo)) { + *errorCode=U_INVALID_FORMAT_ERROR; + return; + } + res_init(pResData, formatVersion, inBytes, length, errorCode); +} + +U_CFUNC void +res_load(ResourceData *pResData, + const char *path, const char *name, UErrorCode *errorCode) { + UVersionInfo formatVersion; + + uprv_memset(pResData, 0, sizeof(ResourceData)); + + /* load the ResourceBundle file */ + pResData->data=udata_openChoice(path, "res", name, isAcceptable, formatVersion, errorCode); + if(U_FAILURE(*errorCode)) { + return; + } + + /* get its memory and initialize *pResData */ + res_init(pResData, formatVersion, udata_getMemory(pResData->data), -1, errorCode); +} + +U_CFUNC void +res_unload(ResourceData *pResData) { + if(pResData->data!=nullptr) { + udata_close(pResData->data); + pResData->data=nullptr; + } +} + +static const int8_t gPublicTypes[URES_LIMIT] = { + URES_STRING, + URES_BINARY, + URES_TABLE, + URES_ALIAS, + + URES_TABLE, /* URES_TABLE32 */ + URES_TABLE, /* URES_TABLE16 */ + URES_STRING, /* URES_STRING_V2 */ + URES_INT, + + URES_ARRAY, + URES_ARRAY, /* URES_ARRAY16 */ + URES_NONE, + URES_NONE, + + URES_NONE, + URES_NONE, + URES_INT_VECTOR, + URES_NONE +}; + +U_CAPI UResType U_EXPORT2 +res_getPublicType(Resource res) { + return (UResType)gPublicTypes[RES_GET_TYPE(res)]; +} + +U_CAPI const char16_t * U_EXPORT2 +res_getStringNoTrace(const ResourceData *pResData, Resource res, int32_t *pLength) { + const char16_t *p; + uint32_t offset=RES_GET_OFFSET(res); + int32_t length; + if(RES_GET_TYPE(res)==URES_STRING_V2) { + int32_t first; + if((int32_t)offsetpoolStringIndexLimit) { + p=(const char16_t *)pResData->poolBundleStrings+offset; + } else { + p=(const char16_t *)pResData->p16BitUnits+(offset-pResData->poolStringIndexLimit); + } + first=*p; + if(!U16_IS_TRAIL(first)) { + length=u_strlen(p); + } else if(first<0xdfef) { + length=first&0x3ff; + ++p; + } else if(first<0xdfff) { + length=((first-0xdfef)<<16)|p[1]; + p+=2; + } else { + length=((int32_t)p[1]<<16)|p[2]; + p+=3; + } + } else if(res==offset) /* RES_GET_TYPE(res)==URES_STRING */ { + const int32_t *p32= res==0 ? &gEmptyString.length : pResData->pRoot+res; + length=*p32++; + p=(const char16_t *)p32; + } else { + p=nullptr; + length=0; + } + if(pLength) { + *pLength=length; + } + return p; +} + +namespace { + +/** + * CLDR string value (three empty-set symbols)=={2205, 2205, 2205} + * prevents fallback to the parent bundle. + * TODO: combine with other code that handles this marker, use EMPTY_SET constant. + * TODO: maybe move to uresbund.cpp? + */ +UBool isNoInheritanceMarker(const ResourceData *pResData, Resource res) { + uint32_t offset=RES_GET_OFFSET(res); + if (offset == 0) { + // empty string + } else if (res == offset) { + const int32_t *p32=pResData->pRoot+res; + int32_t length=*p32; + const char16_t *p=(const char16_t *)p32; + return length == 3 && p[2] == 0x2205 && p[3] == 0x2205 && p[4] == 0x2205; + } else if (RES_GET_TYPE(res) == URES_STRING_V2) { + const char16_t *p; + if((int32_t)offsetpoolStringIndexLimit) { + p=(const char16_t *)pResData->poolBundleStrings+offset; + } else { + p=(const char16_t *)pResData->p16BitUnits+(offset-pResData->poolStringIndexLimit); + } + int32_t first=*p; + if (first == 0x2205) { // implicit length + return p[1] == 0x2205 && p[2] == 0x2205 && p[3] == 0; + } else if (first == 0xdc03) { // explicit length 3 (should not occur) + return p[1] == 0x2205 && p[2] == 0x2205 && p[3] == 0x2205; + } else { + // Assume that the string has not been stored with more length units than necessary. + return false; + } + } + return false; +} + +int32_t getStringArray(const ResourceData *pResData, const icu::ResourceArray &array, + icu::UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + return 0; + } + if(dest == nullptr ? capacity != 0 : capacity < 0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + int32_t length = array.getSize(); + if(length == 0) { + return 0; + } + if(length > capacity) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return length; + } + for(int32_t i = 0; i < length; ++i) { + int32_t sLength; + // No tracing: handled by the caller + const char16_t *s = res_getStringNoTrace(pResData, array.internalGetResource(pResData, i), &sLength); + if(s == nullptr) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + return 0; + } + dest[i].setTo(true, s, sLength); + } + return length; +} + +} // namespace + +U_CAPI const char16_t * U_EXPORT2 +res_getAlias(const ResourceData *pResData, Resource res, int32_t *pLength) { + const char16_t *p; + uint32_t offset=RES_GET_OFFSET(res); + int32_t length; + if(RES_GET_TYPE(res)==URES_ALIAS) { + const int32_t *p32= offset==0 ? &gEmptyString.length : pResData->pRoot+offset; + length=*p32++; + p=(const char16_t *)p32; + } else { + p=nullptr; + length=0; + } + if(pLength) { + *pLength=length; + } + return p; +} + +U_CAPI const uint8_t * U_EXPORT2 +res_getBinaryNoTrace(const ResourceData *pResData, Resource res, int32_t *pLength) { + const uint8_t *p; + uint32_t offset=RES_GET_OFFSET(res); + int32_t length; + if(RES_GET_TYPE(res)==URES_BINARY) { + const int32_t *p32= offset==0 ? (const int32_t*)&gEmpty32 : pResData->pRoot+offset; + length=*p32++; + p=(const uint8_t *)p32; + } else { + p=nullptr; + length=0; + } + if(pLength) { + *pLength=length; + } + return p; +} + + +U_CAPI const int32_t * U_EXPORT2 +res_getIntVectorNoTrace(const ResourceData *pResData, Resource res, int32_t *pLength) { + const int32_t *p; + uint32_t offset=RES_GET_OFFSET(res); + int32_t length; + if(RES_GET_TYPE(res)==URES_INT_VECTOR) { + p= offset==0 ? (const int32_t *)&gEmpty32 : pResData->pRoot+offset; + length=*p++; + } else { + p=nullptr; + length=0; + } + if(pLength) { + *pLength=length; + } + return p; +} + +U_CAPI int32_t U_EXPORT2 +res_countArrayItems(const ResourceData *pResData, Resource res) { + uint32_t offset=RES_GET_OFFSET(res); + switch(RES_GET_TYPE(res)) { + case URES_STRING: + case URES_STRING_V2: + case URES_BINARY: + case URES_ALIAS: + case URES_INT: + case URES_INT_VECTOR: + return 1; + case URES_ARRAY: + case URES_TABLE32: + return offset==0 ? 0 : *(pResData->pRoot+offset); + case URES_TABLE: + return offset==0 ? 0 : *((const uint16_t *)(pResData->pRoot+offset)); + case URES_ARRAY16: + case URES_TABLE16: + return pResData->p16BitUnits[offset]; + default: + return 0; + } +} + +U_NAMESPACE_BEGIN + +ResourceDataValue::~ResourceDataValue() {} + +UResType ResourceDataValue::getType() const { + return res_getPublicType(res); +} + +const char16_t *ResourceDataValue::getString(int32_t &length, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return nullptr; + } + const char16_t *s = res_getString(fTraceInfo, &getData(), res, &length); + if(s == nullptr) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + } + return s; +} + +const char16_t *ResourceDataValue::getAliasString(int32_t &length, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return nullptr; + } + const char16_t *s = res_getAlias(&getData(), res, &length); + if(s == nullptr) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + } + return s; +} + +int32_t ResourceDataValue::getInt(UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return 0; + } + if(RES_GET_TYPE(res) != URES_INT) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + } + return res_getInt(fTraceInfo, res); +} + +uint32_t ResourceDataValue::getUInt(UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return 0; + } + if(RES_GET_TYPE(res) != URES_INT) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + } + return res_getUInt(fTraceInfo, res); +} + +const int32_t *ResourceDataValue::getIntVector(int32_t &length, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return nullptr; + } + const int32_t *iv = res_getIntVector(fTraceInfo, &getData(), res, &length); + if(iv == nullptr) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + } + return iv; +} + +const uint8_t *ResourceDataValue::getBinary(int32_t &length, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return nullptr; + } + const uint8_t *b = res_getBinary(fTraceInfo, &getData(), res, &length); + if(b == nullptr) { + errorCode = U_RESOURCE_TYPE_MISMATCH; + } + return b; +} + +ResourceArray ResourceDataValue::getArray(UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return ResourceArray(); + } + const uint16_t *items16 = nullptr; + const Resource *items32 = nullptr; + uint32_t offset=RES_GET_OFFSET(res); + int32_t length = 0; + switch(RES_GET_TYPE(res)) { + case URES_ARRAY: + if (offset!=0) { // empty if offset==0 + items32 = (const Resource *)getData().pRoot+offset; + length = *items32++; + } + break; + case URES_ARRAY16: + items16 = getData().p16BitUnits+offset; + length = *items16++; + break; + default: + errorCode = U_RESOURCE_TYPE_MISMATCH; + return ResourceArray(); + } + return ResourceArray(items16, items32, length, fTraceInfo); +} + +ResourceTable ResourceDataValue::getTable(UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return ResourceTable(); + } + const uint16_t *keys16 = nullptr; + const int32_t *keys32 = nullptr; + const uint16_t *items16 = nullptr; + const Resource *items32 = nullptr; + uint32_t offset = RES_GET_OFFSET(res); + int32_t length = 0; + switch(RES_GET_TYPE(res)) { + case URES_TABLE: + if (offset != 0) { // empty if offset==0 + keys16 = (const uint16_t *)(getData().pRoot+offset); + length = *keys16++; + items32 = (const Resource *)(keys16+length+(~length&1)); + } + break; + case URES_TABLE16: + keys16 = getData().p16BitUnits+offset; + length = *keys16++; + items16 = keys16 + length; + break; + case URES_TABLE32: + if (offset != 0) { // empty if offset==0 + keys32 = getData().pRoot+offset; + length = *keys32++; + items32 = (const Resource *)keys32 + length; + } + break; + default: + errorCode = U_RESOURCE_TYPE_MISMATCH; + return ResourceTable(); + } + return ResourceTable(keys16, keys32, items16, items32, length, fTraceInfo); +} + +UBool ResourceDataValue::isNoInheritanceMarker() const { + return ::isNoInheritanceMarker(&getData(), res); +} + +int32_t ResourceDataValue::getStringArray(UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) const { + return ::getStringArray(&getData(), getArray(errorCode), dest, capacity, errorCode); +} + +int32_t ResourceDataValue::getStringArrayOrStringAsArray(UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) const { + if(URES_IS_ARRAY(res)) { + return ::getStringArray(&getData(), getArray(errorCode), dest, capacity, errorCode); + } + if(U_FAILURE(errorCode)) { + return 0; + } + if(dest == nullptr ? capacity != 0 : capacity < 0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + if(capacity < 1) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return 1; + } + int32_t sLength; + const char16_t *s = res_getString(fTraceInfo, &getData(), res, &sLength); + if(s != nullptr) { + dest[0].setTo(true, s, sLength); + return 1; + } + errorCode = U_RESOURCE_TYPE_MISMATCH; + return 0; +} + +UnicodeString ResourceDataValue::getStringOrFirstOfArray(UErrorCode &errorCode) const { + UnicodeString us; + if(U_FAILURE(errorCode)) { + return us; + } + int32_t sLength; + const char16_t *s = res_getString(fTraceInfo, &getData(), res, &sLength); + if(s != nullptr) { + us.setTo(true, s, sLength); + return us; + } + ResourceArray array = getArray(errorCode); + if(U_FAILURE(errorCode)) { + return us; + } + if(array.getSize() > 0) { + // Tracing is already performed above (unimportant for trace that this is an array) + s = res_getStringNoTrace(&getData(), array.internalGetResource(&getData(), 0), &sLength); + if(s != nullptr) { + us.setTo(true, s, sLength); + return us; + } + } + errorCode = U_RESOURCE_TYPE_MISMATCH; + return us; +} + +U_NAMESPACE_END + +static Resource +makeResourceFrom16(const ResourceData *pResData, int32_t res16) { + if(res16poolStringIndex16Limit) { + // Pool string, nothing to do. + } else { + // Local string, adjust the 16-bit offset to a regular one, + // with a larger pool string index limit. + res16=res16-pResData->poolStringIndex16Limit+pResData->poolStringIndexLimit; + } + return URES_MAKE_RESOURCE(URES_STRING_V2, res16); +} + +U_CAPI Resource U_EXPORT2 +res_getTableItemByKey(const ResourceData *pResData, Resource table, + int32_t *indexR, const char **key) { + uint32_t offset=RES_GET_OFFSET(table); + int32_t length; + int32_t idx; + if(key == nullptr || *key == nullptr) { + return RES_BOGUS; + } + switch(RES_GET_TYPE(table)) { + case URES_TABLE: { + if (offset!=0) { /* empty if offset==0 */ + const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset); + length=*p++; + *indexR=idx=_res_findTableItem(pResData, p, length, *key, key); + if(idx>=0) { + const Resource *p32=(const Resource *)(p+length+(~length&1)); + return p32[idx]; + } + } + break; + } + case URES_TABLE16: { + const uint16_t *p=pResData->p16BitUnits+offset; + length=*p++; + *indexR=idx=_res_findTableItem(pResData, p, length, *key, key); + if(idx>=0) { + return makeResourceFrom16(pResData, p[length+idx]); + } + break; + } + case URES_TABLE32: { + if (offset!=0) { /* empty if offset==0 */ + const int32_t *p= pResData->pRoot+offset; + length=*p++; + *indexR=idx=_res_findTable32Item(pResData, p, length, *key, key); + if(idx>=0) { + return (Resource)p[length+idx]; + } + } + break; + } + default: + break; + } + return RES_BOGUS; +} + +U_CAPI Resource U_EXPORT2 +res_getTableItemByIndex(const ResourceData *pResData, Resource table, + int32_t indexR, const char **key) { + uint32_t offset=RES_GET_OFFSET(table); + int32_t length; + if (indexR < 0) { + return RES_BOGUS; + } + switch(RES_GET_TYPE(table)) { + case URES_TABLE: { + if (offset != 0) { /* empty if offset==0 */ + const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset); + length=*p++; + if(indexRp16BitUnits+offset; + length=*p++; + if(indexRpRoot+offset; + length=*p++; + if(indexRrootRes, &idx, &realKey); +} + + +UBool icu::ResourceTable::getKeyAndValue(int32_t i, + const char *&key, icu::ResourceValue &value) const { + if(0 <= i && i < length) { + icu::ResourceDataValue &rdValue = static_cast(value); + if (keys16 != nullptr) { + key = RES_GET_KEY16(&rdValue.getData(), keys16[i]); + } else { + key = RES_GET_KEY32(&rdValue.getData(), keys32[i]); + } + Resource res; + if (items16 != nullptr) { + res = makeResourceFrom16(&rdValue.getData(), items16[i]); + } else { + res = items32[i]; + } + // Note: the ResourceTracer keeps a reference to the field of this + // ResourceTable. This is OK because the ResourceTable should remain + // alive for the duration that fields are being read from it + // (including nested fields). + rdValue.setResource(res, ResourceTracer(fTraceInfo, key)); + return true; + } + return false; +} + +UBool icu::ResourceTable::findValue(const char *key, ResourceValue &value) const { + icu::ResourceDataValue &rdValue = static_cast(value); + const char *realKey = nullptr; + int32_t i; + if (keys16 != nullptr) { + i = _res_findTableItem(&rdValue.getData(), keys16, length, key, &realKey); + } else { + i = _res_findTable32Item(&rdValue.getData(), keys32, length, key, &realKey); + } + if (i >= 0) { + Resource res; + if (items16 != nullptr) { + res = makeResourceFrom16(&rdValue.getData(), items16[i]); + } else { + res = items32[i]; + } + // Same note about lifetime as in getKeyAndValue(). + rdValue.setResource(res, ResourceTracer(fTraceInfo, key)); + return true; + } + return false; +} + +U_CAPI Resource U_EXPORT2 +res_getArrayItem(const ResourceData *pResData, Resource array, int32_t indexR) { + uint32_t offset=RES_GET_OFFSET(array); + if (indexR < 0) { + return RES_BOGUS; + } + switch(RES_GET_TYPE(array)) { + case URES_ARRAY: { + if (offset!=0) { /* empty if offset==0 */ + const int32_t *p= pResData->pRoot+offset; + if(indexR<*p) { + return (Resource)p[1+indexR]; + } + } + break; + } + case URES_ARRAY16: { + const uint16_t *p=pResData->p16BitUnits+offset; + if(indexR<*p) { + return makeResourceFrom16(pResData, p[1+indexR]); + } + break; + } + default: + break; + } + return RES_BOGUS; +} + +uint32_t icu::ResourceArray::internalGetResource(const ResourceData *pResData, int32_t i) const { + if (items16 != nullptr) { + return makeResourceFrom16(pResData, items16[i]); + } else { + return items32[i]; + } +} + +UBool icu::ResourceArray::getValue(int32_t i, icu::ResourceValue &value) const { + if(0 <= i && i < length) { + icu::ResourceDataValue &rdValue = static_cast(value); + // Note: the ResourceTracer keeps a reference to the field of this + // ResourceArray. This is OK because the ResourceArray should remain + // alive for the duration that fields are being read from it + // (including nested fields). + rdValue.setResource( + internalGetResource(&rdValue.getData(), i), + ResourceTracer(fTraceInfo, i)); + return true; + } + return false; +} + +U_CFUNC Resource +res_findResource(const ResourceData *pResData, Resource r, char** path, const char** key) { + char *pathP = *path, *nextSepP = *path; + char *closeIndex = nullptr; + Resource t1 = r; + Resource t2; + int32_t indexR = 0; + UResType type = (UResType)RES_GET_TYPE(t1); + + /* if you come in with an empty path, you'll be getting back the same resource */ + if(!uprv_strlen(pathP)) { + return r; + } + + /* one needs to have an aggregate resource in order to search in it */ + if(!URES_IS_CONTAINER(type)) { + return RES_BOGUS; + } + + while(nextSepP && *pathP && t1 != RES_BOGUS && URES_IS_CONTAINER(type)) { + /* Iteration stops if: the path has been consumed, we found a non-existing + * resource (t1 == RES_BOGUS) or we found a scalar resource (including alias) + */ + nextSepP = uprv_strchr(pathP, RES_PATH_SEPARATOR); + /* if there are more separators, terminate string + * and set path to the remaining part of the string + */ + if(nextSepP != nullptr) { + if(nextSepP == pathP) { + // Empty key string. + return RES_BOGUS; + } + *nextSepP = 0; /* overwrite the separator with a NUL to terminate the key */ + *path = nextSepP+1; + } else { + *path = uprv_strchr(pathP, 0); + } + + /* if the resource is a table */ + /* try the key based access */ + if(URES_IS_TABLE(type)) { + *key = pathP; + t2 = res_getTableItemByKey(pResData, t1, &indexR, key); + } else if(URES_IS_ARRAY(type)) { + indexR = uprv_strtol(pathP, &closeIndex, 10); + if(indexR >= 0 && *closeIndex == 0) { + t2 = res_getArrayItem(pResData, t1, indexR); + } else { + t2 = RES_BOGUS; /* have an array, but don't have a valid index */ + } + *key = nullptr; + } else { /* can't do much here, except setting t2 to bogus */ + t2 = RES_BOGUS; + } + t1 = t2; + type = (UResType)RES_GET_TYPE(t1); + /* position pathP to next resource key/index */ + pathP = *path; + } + + return t1; +} + +/* resource bundle swapping ------------------------------------------------- */ + +/* + * Need to always enumerate the entire item tree, + * track the lowest address of any item to use as the limit for char keys[], + * track the highest address of any item to return the size of the data. + * + * We should have thought of storing those in the data... + * It is possible to extend the data structure by putting additional values + * in places that are inaccessible by ordinary enumeration of the item tree. + * For example, additional integers could be stored at the beginning or + * end of the key strings; this could be indicated by a minor version number, + * and the data swapping would have to know about these values. + * + * The data structure does not forbid keys to be shared, so we must swap + * all keys once instead of each key when it is referenced. + * + * These swapping functions assume that a resource bundle always has a length + * that is a multiple of 4 bytes. + * Currently, this is trivially true because genrb writes bundle tree leaves + * physically first, before their branches, so that the root table with its + * array of resource items (uint32_t values) is always last. + */ + +/* definitions for table sorting ------------------------ */ + +/* + * row of a temporary array + * + * gets platform-endian key string indexes and sorting indexes; + * after sorting this array by keys, the actual key/value arrays are permutated + * according to the sorting indexes + */ +typedef struct Row { + int32_t keyIndex, sortIndex; +} Row; + +static int32_t U_CALLCONV +ures_compareRows(const void *context, const void *left, const void *right) { + const char *keyChars=(const char *)context; + return (int32_t)uprv_strcmp(keyChars+((const Row *)left)->keyIndex, + keyChars+((const Row *)right)->keyIndex); +} + +typedef struct TempTable { + const char *keyChars; + Row *rows; + int32_t *resort; + uint32_t *resFlags; + int32_t localKeyLimit; + uint8_t majorFormatVersion; +} TempTable; + +enum { + STACK_ROW_CAPACITY=200 +}; + +/* The table item key string is not locally available. */ +static const char *const gUnknownKey=""; + +/* resource table key for collation binaries: "%%CollationBin" */ +static const char16_t gCollationBinKey[]={ + 0x25, 0x25, + 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x69, 0x6e, + 0 +}; + +/* + * swap one resource item + */ +static void +ures_swapResource(const UDataSwapper *ds, + const Resource *inBundle, Resource *outBundle, + Resource res, /* caller swaps res itself */ + const char *key, + TempTable *pTempTable, + UErrorCode *pErrorCode) { + const Resource *p; + Resource *q; + int32_t offset, count; + + switch(RES_GET_TYPE(res)) { + case URES_TABLE16: + case URES_STRING_V2: + case URES_INT: + case URES_ARRAY16: + /* integer, or points to 16-bit units, nothing to do here */ + return; + default: + break; + } + + /* all other types use an offset to point to their data */ + offset=(int32_t)RES_GET_OFFSET(res); + if(offset==0) { + /* special offset indicating an empty item */ + return; + } + if(pTempTable->resFlags[offset>>5]&((uint32_t)1<<(offset&0x1f))) { + /* we already swapped this resource item */ + return; + } else { + /* mark it as swapped now */ + pTempTable->resFlags[offset>>5]|=((uint32_t)1<<(offset&0x1f)); + } + + p=inBundle+offset; + q=outBundle+offset; + + switch(RES_GET_TYPE(res)) { + case URES_ALIAS: + /* physically same value layout as string, fall through */ + U_FALLTHROUGH; + case URES_STRING: + count=udata_readInt32(ds, (int32_t)*p); + /* swap length */ + ds->swapArray32(ds, p, 4, q, pErrorCode); + /* swap each char16_t (the terminating NUL would not change) */ + ds->swapArray16(ds, p+1, 2*count, q+1, pErrorCode); + break; + case URES_BINARY: + count=udata_readInt32(ds, (int32_t)*p); + /* swap length */ + ds->swapArray32(ds, p, 4, q, pErrorCode); + /* no need to swap or copy bytes - ures_swap() copied them all */ + + /* swap known formats */ +#if !UCONFIG_NO_COLLATION + if( key!=nullptr && /* the binary is in a table */ + (key!=gUnknownKey ? + /* its table key string is "%%CollationBin" */ + 0==ds->compareInvChars(ds, key, -1, + gCollationBinKey, UPRV_LENGTHOF(gCollationBinKey)-1) : + /* its table key string is unknown but it looks like a collation binary */ + ucol_looksLikeCollationBinary(ds, p+1, count)) + ) { + ucol_swap(ds, p+1, count, q+1, pErrorCode); + } +#endif + break; + case URES_TABLE: + case URES_TABLE32: + { + const uint16_t *pKey16; + uint16_t *qKey16; + + const int32_t *pKey32; + int32_t *qKey32; + + Resource item; + int32_t i, oldIndex; + + if(RES_GET_TYPE(res)==URES_TABLE) { + /* get table item count */ + pKey16=(const uint16_t *)p; + qKey16=(uint16_t *)q; + count=ds->readUInt16(*pKey16); + + pKey32=qKey32=nullptr; + + /* swap count */ + ds->swapArray16(ds, pKey16++, 2, qKey16++, pErrorCode); + + offset+=((1+count)+1)/2; + } else { + /* get table item count */ + pKey32=(const int32_t *)p; + qKey32=(int32_t *)q; + count=udata_readInt32(ds, *pKey32); + + pKey16=qKey16=nullptr; + + /* swap count */ + ds->swapArray32(ds, pKey32++, 4, qKey32++, pErrorCode); + + offset+=1+count; + } + + if(count==0) { + break; + } + + p=inBundle+offset; /* pointer to table resources */ + q=outBundle+offset; + + /* recurse */ + for(i=0; ireadUInt16(pKey16[i]); + if(keyOffsetlocalKeyLimit) { + itemKey=(const char *)outBundle+keyOffset; + } + } else { + int32_t keyOffset=udata_readInt32(ds, pKey32[i]); + if(keyOffset>=0) { + itemKey=(const char *)outBundle+keyOffset; + } + } + item=ds->readUInt32(p[i]); + ures_swapResource(ds, inBundle, outBundle, item, itemKey, pTempTable, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ures_swapResource(table res=%08x)[%d].recurse(%08x) failed\n", + res, i, item); + return; + } + } + + if(pTempTable->majorFormatVersion>1 || ds->inCharset==ds->outCharset) { + /* no need to sort, just swap the offset/value arrays */ + if(pKey16!=nullptr) { + ds->swapArray16(ds, pKey16, count*2, qKey16, pErrorCode); + ds->swapArray32(ds, p, count*4, q, pErrorCode); + } else { + /* swap key offsets and items as one array */ + ds->swapArray32(ds, pKey32, count*2*4, qKey32, pErrorCode); + } + break; + } + + /* + * We need to sort tables by outCharset key strings because they + * sort differently for different charset families. + * ures_swap() already set pTempTable->keyChars appropriately. + * First we set up a temporary table with the key indexes and + * sorting indexes and sort that. + * Then we permutate and copy/swap the actual values. + */ + if(pKey16!=nullptr) { + for(i=0; irows[i].keyIndex=ds->readUInt16(pKey16[i]); + pTempTable->rows[i].sortIndex=i; + } + } else { + for(i=0; irows[i].keyIndex=udata_readInt32(ds, pKey32[i]); + pTempTable->rows[i].sortIndex=i; + } + } + uprv_sortArray(pTempTable->rows, count, sizeof(Row), + ures_compareRows, pTempTable->keyChars, + false, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ures_swapResource(table res=%08x).uprv_sortArray(%d items) failed\n", + res, count); + return; + } + + /* + * copy/swap/permutate items + * + * If we swap in-place, then the permutation must use another + * temporary array (pTempTable->resort) + * before the results are copied to the outBundle. + */ + /* keys */ + if(pKey16!=nullptr) { + uint16_t *rKey16; + + if(pKey16!=qKey16) { + rKey16=qKey16; + } else { + rKey16=(uint16_t *)pTempTable->resort; + } + for(i=0; irows[i].sortIndex; + ds->swapArray16(ds, pKey16+oldIndex, 2, rKey16+i, pErrorCode); + } + if(qKey16!=rKey16) { + uprv_memcpy(qKey16, rKey16, 2*count); + } + } else { + int32_t *rKey32; + + if(pKey32!=qKey32) { + rKey32=qKey32; + } else { + rKey32=pTempTable->resort; + } + for(i=0; irows[i].sortIndex; + ds->swapArray32(ds, pKey32+oldIndex, 4, rKey32+i, pErrorCode); + } + if(qKey32!=rKey32) { + uprv_memcpy(qKey32, rKey32, 4*count); + } + } + + /* resources */ + { + Resource *r; + + + if(p!=q) { + r=q; + } else { + r=(Resource *)pTempTable->resort; + } + for(i=0; irows[i].sortIndex; + ds->swapArray32(ds, p+oldIndex, 4, r+i, pErrorCode); + } + if(q!=r) { + uprv_memcpy(q, r, 4*count); + } + } + } + break; + case URES_ARRAY: + { + Resource item; + int32_t i; + + count=udata_readInt32(ds, (int32_t)*p); + /* swap length */ + ds->swapArray32(ds, p++, 4, q++, pErrorCode); + + /* recurse */ + for(i=0; ireadUInt32(p[i]); + ures_swapResource(ds, inBundle, outBundle, item, nullptr, pTempTable, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ures_swapResource(array res=%08x)[%d].recurse(%08x) failed\n", + res, i, item); + return; + } + } + + /* swap items */ + ds->swapArray32(ds, p, 4*count, q, pErrorCode); + } + break; + case URES_INT_VECTOR: + count=udata_readInt32(ds, (int32_t)*p); + /* swap length and each integer */ + ds->swapArray32(ds, p, 4*(1+count), q, pErrorCode); + break; + default: + /* also catches RES_BOGUS */ + *pErrorCode=U_UNSUPPORTED_ERROR; + break; + } +} + +U_CAPI int32_t U_EXPORT2 +ures_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const UDataInfo *pInfo; + const Resource *inBundle; + Resource rootRes; + int32_t headerSize, maxTableLength; + + Row rows[STACK_ROW_CAPACITY]; + int32_t resort[STACK_ROW_CAPACITY]; + TempTable tempTable; + + const int32_t *inIndexes; + + /* the following integers count Resource item offsets (4 bytes each), not bytes */ + int32_t bundleLength, indexLength, keysBottom, keysTop, resBottom, top; + + /* udata_swapDataHeader checks the arguments */ + headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + /* check data format and format version */ + pInfo=(const UDataInfo *)((const char *)inData+4); + if(!( + pInfo->dataFormat[0]==0x52 && /* dataFormat="ResB" */ + pInfo->dataFormat[1]==0x65 && + pInfo->dataFormat[2]==0x73 && + pInfo->dataFormat[3]==0x42 && + /* formatVersion 1.1+ or 2.x or 3.x */ + ((pInfo->formatVersion[0]==1 && pInfo->formatVersion[1]>=1) || + pInfo->formatVersion[0]==2 || pInfo->formatVersion[0]==3) + )) { + udata_printError(ds, "ures_swap(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not a resource bundle\n", + pInfo->dataFormat[0], pInfo->dataFormat[1], + pInfo->dataFormat[2], pInfo->dataFormat[3], + pInfo->formatVersion[0], pInfo->formatVersion[1]); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + tempTable.majorFormatVersion=pInfo->formatVersion[0]; + + /* a resource bundle must contain at least one resource item */ + if(length<0) { + bundleLength=-1; + } else { + bundleLength=(length-headerSize)/4; + + /* formatVersion 1.1 must have a root item and at least 5 indexes */ + if(bundleLength<(1+5)) { + udata_printError(ds, "ures_swap(): too few bytes (%d after header) for a resource bundle\n", + length-headerSize); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + } + + inBundle=(const Resource *)((const char *)inData+headerSize); + rootRes=ds->readUInt32(*inBundle); + + /* formatVersion 1.1 adds the indexes[] array */ + inIndexes=(const int32_t *)(inBundle+1); + + indexLength=udata_readInt32(ds, inIndexes[URES_INDEX_LENGTH])&0xff; + if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) { + udata_printError(ds, "ures_swap(): too few indexes for a 1.1+ resource bundle\n"); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + keysBottom=1+indexLength; + keysTop=udata_readInt32(ds, inIndexes[URES_INDEX_KEYS_TOP]); + if(indexLength>URES_INDEX_16BIT_TOP) { + resBottom=udata_readInt32(ds, inIndexes[URES_INDEX_16BIT_TOP]); + } else { + resBottom=keysTop; + } + top=udata_readInt32(ds, inIndexes[URES_INDEX_BUNDLE_TOP]); + maxTableLength=udata_readInt32(ds, inIndexes[URES_INDEX_MAX_TABLE_LENGTH]); + + if(0<=bundleLength && bundleLength(1+indexLength)) { + tempTable.localKeyLimit=keysTop<<2; + } else { + tempTable.localKeyLimit=0; + } + + if(length>=0) { + Resource *outBundle=(Resource *)((char *)outData+headerSize); + + /* track which resources we have already swapped */ + uint32_t stackResFlags[STACK_ROW_CAPACITY]; + int32_t resFlagsLength; + + /* + * We need one bit per 4 resource bundle bytes so that we can track + * every possible Resource for whether we have swapped it already. + * Multiple Resource words can refer to the same bundle offsets + * for sharing identical values. + * We could optimize this by allocating only for locations above + * where Resource values are stored (above keys & strings). + */ + resFlagsLength=(length+31)>>5; /* number of bytes needed */ + resFlagsLength=(resFlagsLength+3)&~3; /* multiple of 4 bytes for uint32_t */ + if(resFlagsLength<=(int32_t)sizeof(stackResFlags)) { + tempTable.resFlags=stackResFlags; + } else { + tempTable.resFlags=(uint32_t *)uprv_malloc(resFlagsLength); + if(tempTable.resFlags==nullptr) { + udata_printError(ds, "ures_swap(): unable to allocate memory for tracking resources\n"); + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return 0; + } + } + uprv_memset(tempTable.resFlags, 0, resFlagsLength); + + /* copy the bundle for binary and inaccessible data */ + if(inData!=outData) { + uprv_memcpy(outBundle, inBundle, 4*top); + } + + /* swap the key strings, but not the padding bytes (0xaa) after the last string and its NUL */ + udata_swapInvStringBlock(ds, inBundle+keysBottom, 4*(keysTop-keysBottom), + outBundle+keysBottom, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ures_swap().udata_swapInvStringBlock(keys[%d]) failed\n", 4*(keysTop-keysBottom)); + return 0; + } + + /* swap the 16-bit units (strings, table16, array16) */ + if(keysTopswapArray16(ds, inBundle+keysTop, (resBottom-keysTop)*4, outBundle+keysTop, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ures_swap().swapArray16(16-bit units[%d]) failed\n", 2*(resBottom-keysTop)); + return 0; + } + } + + /* allocate the temporary table for sorting resource tables */ + tempTable.keyChars=(const char *)outBundle; /* sort by outCharset */ + if(tempTable.majorFormatVersion>1 || maxTableLength<=STACK_ROW_CAPACITY) { + tempTable.rows=rows; + tempTable.resort=resort; + } else { + tempTable.rows=(Row *)uprv_malloc(maxTableLength*sizeof(Row)+maxTableLength*4); + if(tempTable.rows==nullptr) { + udata_printError(ds, "ures_swap(): unable to allocate memory for sorting tables (max length: %d)\n", + maxTableLength); + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + if(tempTable.resFlags!=stackResFlags) { + uprv_free(tempTable.resFlags); + } + return 0; + } + tempTable.resort=(int32_t *)(tempTable.rows+maxTableLength); + } + + /* swap the resources */ + ures_swapResource(ds, inBundle, outBundle, rootRes, nullptr, &tempTable, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + udata_printError(ds, "ures_swapResource(root res=%08x) failed\n", + rootRes); + } + + if(tempTable.rows!=rows) { + uprv_free(tempTable.rows); + } + if(tempTable.resFlags!=stackResFlags) { + uprv_free(tempTable.resFlags); + } + + /* swap the root resource and indexes */ + ds->swapArray32(ds, inBundle, keysBottom*4, outBundle, pErrorCode); + } + + return headerSize+4*top; +} diff --git a/deps/icu-small/source/common/uresdata.h b/deps/icu-small/source/common/uresdata.h index b8a3adba63489d..44566e59c47492 100644 --- a/deps/icu-small/source/common/uresdata.h +++ b/deps/icu-small/source/common/uresdata.h @@ -1,574 +1,574 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 1999-2016, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* file name: uresdata.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999dec08 -* created by: Markus W. Scherer -* 06/24/02 weiv Added support for resource sharing -*/ - -#ifndef __RESDATA_H__ -#define __RESDATA_H__ - -#include "unicode/utypes.h" -#include "unicode/udata.h" -#include "unicode/ures.h" -#include "putilimp.h" -#include "udataswp.h" - -/** - * Numeric constants for internal-only types of resource items. - * These must use different numeric values than UResType constants - * because they are used together. - * Internal types are never returned by ures_getType(). - */ -typedef enum { - /** Include a negative value so that the compiler uses the same int type as for UResType. */ - URES_INTERNAL_NONE=-1, - - /** Resource type constant for tables with 32-bit count, key offsets and values. */ - URES_TABLE32=4, - - /** - * Resource type constant for tables with 16-bit count, key offsets and values. - * All values are URES_STRING_V2 strings. - */ - URES_TABLE16=5, - - /** Resource type constant for 16-bit Unicode strings in formatVersion 2. */ - URES_STRING_V2=6, - - /** - * Resource type constant for arrays with 16-bit count and values. - * All values are URES_STRING_V2 strings. - */ - URES_ARRAY16=9 - - /* Resource type 15 is not defined but effectively used by RES_BOGUS=0xffffffff. */ -} UResInternalType; - -/* - * A Resource is a 32-bit value that has 2 bit fields: - * 31..28 4-bit type, see enum below - * 27..0 28-bit four-byte-offset or value according to the type - */ -typedef uint32_t Resource; - -#define RES_BOGUS 0xffffffff -#define RES_MAX_OFFSET 0x0fffffff - -#define RES_GET_TYPE(res) ((int32_t)((res)>>28UL)) -#define RES_GET_OFFSET(res) ((res)&0x0fffffff) -#define RES_GET_POINTER(pRoot, res) ((pRoot)+RES_GET_OFFSET(res)) - -/* get signed and unsigned integer values directly from the Resource handle - * NOTE: For proper logging, please use the res_getInt() constexpr - */ -#if U_SIGNED_RIGHT_SHIFT_IS_ARITHMETIC -# define RES_GET_INT_NO_TRACE(res) (((int32_t)((res)<<4L))>>4L) -#else -# define RES_GET_INT_NO_TRACE(res) (int32_t)(((res)&0x08000000) ? (res)|0xf0000000 : (res)&0x07ffffff) -#endif - -#define RES_GET_UINT_NO_TRACE(res) ((res)&0x0fffffff) - -#define URES_IS_ARRAY(type) ((int32_t)(type)==URES_ARRAY || (int32_t)(type)==URES_ARRAY16) -#define URES_IS_TABLE(type) ((int32_t)(type)==URES_TABLE || (int32_t)(type)==URES_TABLE16 || (int32_t)(type)==URES_TABLE32) -#define URES_IS_CONTAINER(type) (URES_IS_TABLE(type) || URES_IS_ARRAY(type)) - -#define URES_MAKE_RESOURCE(type, offset) (((Resource)(type)<<28)|(Resource)(offset)) -#define URES_MAKE_EMPTY_RESOURCE(type) ((Resource)(type)<<28) - -/* indexes[] value names; indexes are generally 32-bit (Resource) indexes */ -enum { - /** - * [0] contains the length of indexes[] - * which is at most URES_INDEX_TOP of the latest format version - * - * formatVersion==1: all bits contain the length of indexes[] - * but the length is much less than 0xff; - * formatVersion>1: - * only bits 7..0 contain the length of indexes[], - * bits 31..8 are reserved and set to 0 - * formatVersion>=3: - * bits 31..8 poolStringIndexLimit bits 23..0 - */ - URES_INDEX_LENGTH, - /** - * [1] contains the top of the key strings, - * same as the bottom of resources or UTF-16 strings, rounded up - */ - URES_INDEX_KEYS_TOP, - /** [2] contains the top of all resources */ - URES_INDEX_RESOURCES_TOP, - /** - * [3] contains the top of the bundle, - * in case it were ever different from [2] - */ - URES_INDEX_BUNDLE_TOP, - /** [4] max. length of any table */ - URES_INDEX_MAX_TABLE_LENGTH, - /** - * [5] attributes bit set, see URES_ATT_* (new in formatVersion 1.2) - * - * formatVersion>=3: - * bits 31..16 poolStringIndex16Limit - * bits 15..12 poolStringIndexLimit bits 27..24 - */ - URES_INDEX_ATTRIBUTES, - /** - * [6] top of the 16-bit units (UTF-16 string v2 UChars, URES_TABLE16, URES_ARRAY16), - * rounded up (new in formatVersion 2.0, ICU 4.4) - */ - URES_INDEX_16BIT_TOP, - /** [7] checksum of the pool bundle (new in formatVersion 2.0, ICU 4.4) */ - URES_INDEX_POOL_CHECKSUM, - URES_INDEX_TOP -}; - -/* - * Nofallback attribute, attribute bit 0 in indexes[URES_INDEX_ATTRIBUTES]. - * New in formatVersion 1.2 (ICU 3.6). - * - * If set, then this resource bundle is a standalone bundle. - * If not set, then the bundle participates in locale fallback, eventually - * all the way to the root bundle. - * If indexes[] is missing or too short, then the attribute cannot be determined - * reliably. Dependency checking should ignore such bundles, and loading should - * use fallbacks. - */ -#define URES_ATT_NO_FALLBACK 1 - -/* - * Attributes for bundles that are, or use, a pool bundle. - * A pool bundle provides key strings that are shared among several other bundles - * to reduce their total size. - * New in formatVersion 2 (ICU 4.4). - */ -#define URES_ATT_IS_POOL_BUNDLE 2 -#define URES_ATT_USES_POOL_BUNDLE 4 - -/* - * File format for .res resource bundle files - * - * ICU 56: New in formatVersion 3 compared with 2: ------------- - * - * Resource bundles can optionally use shared string-v2 values - * stored in the pool bundle. - * If so, then the indexes[] contain two new values - * in previously-unused bits of existing indexes[] slots: - * - poolStringIndexLimit: - * String-v2 offsets (in 32-bit Resource words) below this limit - * point to pool bundle string-v2 values. - * - poolStringIndex16Limit: - * Resource16 string-v2 offsets below this limit - * point to pool bundle string-v2 values. - * Guarantee: poolStringIndex16Limit <= poolStringIndexLimit - * - * The local bundle's poolStringIndexLimit is greater than - * any pool bundle string index used in the local bundle. - * The poolStringIndexLimit should not be greater than - * the maximum possible pool bundle string index. - * - * The maximum possible pool bundle string index is the index to the last non-NUL - * pool string character, due to suffix sharing. - * - * In the pool bundle, there is no structure that lists the strings. - * (The root resource is an empty Table.) - * If the strings need to be enumerated (as genrb --usePoolBundle does), - * then iterate through the pool bundle's 16-bit-units array from the beginning. - * Stop at the end of the array, or when an explicit or implicit string length - * would lead beyond the end of the array, - * or when an apparent string is not NUL-terminated. - * (Future genrb version might terminate the strings with - * what looks like a large explicit string length.) - * - * ICU 4.4: New in formatVersion 2 compared with 1.3: ------------- - * - * Three new resource types -- String-v2, Table16 and Array16 -- have their - * values stored in a new array of 16-bit units between the table key strings - * and the start of the other resources. - * - * genrb eliminates duplicates among Unicode string-v2 values. - * Multiple Unicode strings may use the same offset and string data, - * or a short string may point to the suffix of a longer string. ("Suffix sharing") - * For example, one string "abc" may be reused for another string "bc" by pointing - * to the second character. (Short strings-v2 are NUL-terminated - * and not preceded by an explicit length value.) - * - * It is allowed for all resource types to share values. - * The swapper code (ures_swap()) has been modified so that it swaps each item - * exactly once. - * - * A resource bundle may use a special pool bundle. Some or all of the table key strings - * of the using-bundle are omitted, and the key string offsets for such key strings refer - * to offsets in the pool bundle. - * The using-bundle's and the pool-bundle's indexes[URES_INDEX_POOL_CHECKSUM] values - * must match. - * Two bits in indexes[URES_INDEX_ATTRIBUTES] indicate whether a resource bundle - * is or uses a pool bundle. - * - * Table key strings must be compared in ASCII order, even if they are not - * stored in ASCII. - * - * New in formatVersion 1.3 compared with 1.2: ------------- - * - * genrb eliminates duplicates among key strings. - * Multiple table items may share one key string, or one item may point - * to the suffix of another's key string. ("Suffix sharing") - * For example, one key "abc" may be reused for another key "bc" by pointing - * to the second character. (Key strings are NUL-terminated.) - * - * ------------- - * - * An ICU4C resource bundle file (.res) is a binary, memory-mappable file - * with nested, hierarchical data structures. - * It physically contains the following: - * - * Resource root; -- 32-bit Resource item, root item for this bundle's tree; - * currently, the root item must be a table or table32 resource item - * int32_t indexes[indexes[0]]; -- array of indexes for friendly - * reading and swapping; see URES_INDEX_* above - * new in formatVersion 1.1 (ICU 2.8) - * char keys[]; -- characters for key strings - * (formatVersion 1.0: up to 65k of characters; 1.1: <2G) - * (minus the space for root and indexes[]), - * which consist of invariant characters (ASCII/EBCDIC) and are NUL-terminated; - * padded to multiple of 4 bytes for 4-alignment of the following data - * uint16_t 16BitUnits[]; -- resources that are stored entirely as sequences of 16-bit units - * (new in formatVersion 2/ICU 4.4) - * data is indexed by the offset values in 16-bit resource types, - * with offset 0 pointing to the beginning of this array; - * there is a 0 at offset 0, for empty resources; - * padded to multiple of 4 bytes for 4-alignment of the following data - * data; -- data directly and indirectly indexed by the root item; - * the structure is determined by walking the tree - * - * Each resource bundle item has a 32-bit Resource handle (see typedef above) - * which contains the item type number in its upper 4 bits (31..28) and either - * an offset or a direct value in its lower 28 bits (27..0). - * The order of items is undefined and only determined by walking the tree. - * Leaves of the tree may be stored first or last or anywhere in between, - * and it is in theory possible to have unreferenced holes in the file. - * - * 16-bit-unit values: - * Starting with formatVersion 2/ICU 4.4, some resources are stored in a special - * array of 16-bit units. Each resource value is a sequence of 16-bit units, - * with no per-resource padding to a 4-byte boundary. - * 16-bit container types (Table16 and Array16) contain Resource16 values - * which are offsets to String-v2 resources in the same 16-bit-units array. - * - * Direct values: - * - Empty Unicode strings have an offset value of 0 in the Resource handle itself. - * - Starting with formatVersion 2/ICU 4.4, an offset value of 0 for - * _any_ resource type indicates an empty value. - * - Integer values are 28-bit values stored in the Resource handle itself; - * the interpretation of unsigned vs. signed integers is up to the application. - * - * All other types and values use 28-bit offsets to point to the item's data. - * The offset is an index to the first 32-bit word of the value, relative to the - * start of the resource data (i.e., the root item handle is at offset 0). - * To get byte offsets, the offset is multiplied by 4 (or shifted left by 2 bits). - * All resource item values are 4-aligned. - * - * New in formatVersion 2/ICU 4.4: Some types use offsets into the 16-bit-units array, - * indexing 16-bit units in that array. - * - * The structures (memory layouts) for the values for each item type are listed - * in the table below. - * - * Nested, hierarchical structures: ------------- - * - * Table items contain key-value pairs where the keys are offsets to char * key strings. - * The values of these pairs are either Resource handles or - * offsets into the 16-bit-units array, depending on the table type. - * - * Array items are simple vectors of Resource handles, - * or of offsets into the 16-bit-units array, depending on the array type. - * - * Table key string offsets: ------- - * - * Key string offsets are relative to the start of the resource data (of the root handle), - * i.e., the first string has an offset of 4+sizeof(indexes). - * (After the 4-byte root handle and after the indexes array.) - * - * If the resource bundle uses a pool bundle, then some key strings are stored - * in the pool bundle rather than in the local bundle itself. - * - In a Table or Table16, the 16-bit key string offset is local if it is - * less than indexes[URES_INDEX_KEYS_TOP]<<2. - * Otherwise, subtract indexes[URES_INDEX_KEYS_TOP]<<2 to get the offset into - * the pool bundle key strings. - * - In a Table32, the 32-bit key string offset is local if it is non-negative. - * Otherwise, reset bit 31 to get the pool key string offset. - * - * Unlike the local offset, the pool key offset is relative to - * the start of the key strings, not to the start of the bundle. - * - * An alias item is special (and new in ICU 2.4): -------------- - * - * Its memory layout is just like for a UnicodeString, but at runtime it resolves to - * another resource bundle's item according to the path in the string. - * This is used to share items across bundles that are in different lookup/fallback - * chains (e.g., large collation data among zh_TW and zh_HK). - * This saves space (for large items) and maintenance effort (less duplication of data). - * - * -------------------------------------------------------------------------- - * - * Resource types: - * - * Most resources have their values stored at four-byte offsets from the start - * of the resource data. These values are at least 4-aligned. - * Some resource values are stored directly in the offset field of the Resource itself. - * See UResType in unicode/ures.h for enumeration constants for Resource types. - * - * Some resources have their values stored as sequences of 16-bit units, - * at 2-byte offsets from the start of a contiguous 16-bit-unit array between - * the table key strings and the other resources. (new in formatVersion 2/ICU 4.4) - * At offset 0 of that array is a 16-bit zero value for empty 16-bit resources. - * - * Resource16 values in Table16 and Array16 are 16-bit offsets to String-v2 - * resources, with the offsets relative to the start of the 16-bit-units array. - * Starting with formatVersion 3/ICU 56, if offset(URES_NONE)), - fTraceInfo() {} - virtual ~ResourceDataValue(); - - void setData(const ResourceData &data) { - pResData = &data; - } - - void setValidLocaleDataEntry(UResourceDataEntry *entry) { - validLocaleDataEntry = entry; - } - - void setResource(Resource r, ResourceTracer&& traceInfo) { - res = r; - fTraceInfo = traceInfo; - } - - const ResourceData &getData() const { return *pResData; } - UResourceDataEntry *getValidLocaleDataEntry() const { return validLocaleDataEntry; } - Resource getResource() const { return res; } - virtual UResType getType() const override; - virtual const UChar *getString(int32_t &length, UErrorCode &errorCode) const override; - virtual const UChar *getAliasString(int32_t &length, UErrorCode &errorCode) const override; - virtual int32_t getInt(UErrorCode &errorCode) const override; - virtual uint32_t getUInt(UErrorCode &errorCode) const override; - virtual const int32_t *getIntVector(int32_t &length, UErrorCode &errorCode) const override; - virtual const uint8_t *getBinary(int32_t &length, UErrorCode &errorCode) const override; - virtual ResourceArray getArray(UErrorCode &errorCode) const override; - virtual ResourceTable getTable(UErrorCode &errorCode) const override; - virtual UBool isNoInheritanceMarker() const override; - virtual int32_t getStringArray(UnicodeString *dest, int32_t capacity, - UErrorCode &errorCode) const override; - virtual int32_t getStringArrayOrStringAsArray(UnicodeString *dest, int32_t capacity, - UErrorCode &errorCode) const override; - virtual UnicodeString getStringOrFirstOfArray(UErrorCode &errorCode) const override; - -private: - const ResourceData *pResData; - UResourceDataEntry *validLocaleDataEntry; - Resource res; - ResourceTracer fTraceInfo; -}; - -U_NAMESPACE_END - -#endif /* __cplusplus */ - -/** - * Swap an ICU resource bundle. See udataswp.h. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -ures_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 1999-2016, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* file name: uresdata.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999dec08 +* created by: Markus W. Scherer +* 06/24/02 weiv Added support for resource sharing +*/ + +#ifndef __RESDATA_H__ +#define __RESDATA_H__ + +#include "unicode/utypes.h" +#include "unicode/udata.h" +#include "unicode/ures.h" +#include "putilimp.h" +#include "udataswp.h" + +/** + * Numeric constants for internal-only types of resource items. + * These must use different numeric values than UResType constants + * because they are used together. + * Internal types are never returned by ures_getType(). + */ +typedef enum { + /** Include a negative value so that the compiler uses the same int type as for UResType. */ + URES_INTERNAL_NONE=-1, + + /** Resource type constant for tables with 32-bit count, key offsets and values. */ + URES_TABLE32=4, + + /** + * Resource type constant for tables with 16-bit count, key offsets and values. + * All values are URES_STRING_V2 strings. + */ + URES_TABLE16=5, + + /** Resource type constant for 16-bit Unicode strings in formatVersion 2. */ + URES_STRING_V2=6, + + /** + * Resource type constant for arrays with 16-bit count and values. + * All values are URES_STRING_V2 strings. + */ + URES_ARRAY16=9 + + /* Resource type 15 is not defined but effectively used by RES_BOGUS=0xffffffff. */ +} UResInternalType; + +/* + * A Resource is a 32-bit value that has 2 bit fields: + * 31..28 4-bit type, see enum below + * 27..0 28-bit four-byte-offset or value according to the type + */ +typedef uint32_t Resource; + +#define RES_BOGUS 0xffffffff +#define RES_MAX_OFFSET 0x0fffffff + +#define RES_GET_TYPE(res) ((int32_t)((res)>>28UL)) +#define RES_GET_OFFSET(res) ((res)&0x0fffffff) +#define RES_GET_POINTER(pRoot, res) ((pRoot)+RES_GET_OFFSET(res)) + +/* get signed and unsigned integer values directly from the Resource handle + * NOTE: For proper logging, please use the res_getInt() constexpr + */ +#if U_SIGNED_RIGHT_SHIFT_IS_ARITHMETIC +# define RES_GET_INT_NO_TRACE(res) (((int32_t)((res)<<4L))>>4L) +#else +# define RES_GET_INT_NO_TRACE(res) (int32_t)(((res)&0x08000000) ? (res)|0xf0000000 : (res)&0x07ffffff) +#endif + +#define RES_GET_UINT_NO_TRACE(res) ((res)&0x0fffffff) + +#define URES_IS_ARRAY(type) ((int32_t)(type)==URES_ARRAY || (int32_t)(type)==URES_ARRAY16) +#define URES_IS_TABLE(type) ((int32_t)(type)==URES_TABLE || (int32_t)(type)==URES_TABLE16 || (int32_t)(type)==URES_TABLE32) +#define URES_IS_CONTAINER(type) (URES_IS_TABLE(type) || URES_IS_ARRAY(type)) + +#define URES_MAKE_RESOURCE(type, offset) (((Resource)(type)<<28)|(Resource)(offset)) +#define URES_MAKE_EMPTY_RESOURCE(type) ((Resource)(type)<<28) + +/* indexes[] value names; indexes are generally 32-bit (Resource) indexes */ +enum { + /** + * [0] contains the length of indexes[] + * which is at most URES_INDEX_TOP of the latest format version + * + * formatVersion==1: all bits contain the length of indexes[] + * but the length is much less than 0xff; + * formatVersion>1: + * only bits 7..0 contain the length of indexes[], + * bits 31..8 are reserved and set to 0 + * formatVersion>=3: + * bits 31..8 poolStringIndexLimit bits 23..0 + */ + URES_INDEX_LENGTH, + /** + * [1] contains the top of the key strings, + * same as the bottom of resources or UTF-16 strings, rounded up + */ + URES_INDEX_KEYS_TOP, + /** [2] contains the top of all resources */ + URES_INDEX_RESOURCES_TOP, + /** + * [3] contains the top of the bundle, + * in case it were ever different from [2] + */ + URES_INDEX_BUNDLE_TOP, + /** [4] max. length of any table */ + URES_INDEX_MAX_TABLE_LENGTH, + /** + * [5] attributes bit set, see URES_ATT_* (new in formatVersion 1.2) + * + * formatVersion>=3: + * bits 31..16 poolStringIndex16Limit + * bits 15..12 poolStringIndexLimit bits 27..24 + */ + URES_INDEX_ATTRIBUTES, + /** + * [6] top of the 16-bit units (UTF-16 string v2 UChars, URES_TABLE16, URES_ARRAY16), + * rounded up (new in formatVersion 2.0, ICU 4.4) + */ + URES_INDEX_16BIT_TOP, + /** [7] checksum of the pool bundle (new in formatVersion 2.0, ICU 4.4) */ + URES_INDEX_POOL_CHECKSUM, + URES_INDEX_TOP +}; + +/* + * Nofallback attribute, attribute bit 0 in indexes[URES_INDEX_ATTRIBUTES]. + * New in formatVersion 1.2 (ICU 3.6). + * + * If set, then this resource bundle is a standalone bundle. + * If not set, then the bundle participates in locale fallback, eventually + * all the way to the root bundle. + * If indexes[] is missing or too short, then the attribute cannot be determined + * reliably. Dependency checking should ignore such bundles, and loading should + * use fallbacks. + */ +#define URES_ATT_NO_FALLBACK 1 + +/* + * Attributes for bundles that are, or use, a pool bundle. + * A pool bundle provides key strings that are shared among several other bundles + * to reduce their total size. + * New in formatVersion 2 (ICU 4.4). + */ +#define URES_ATT_IS_POOL_BUNDLE 2 +#define URES_ATT_USES_POOL_BUNDLE 4 + +/* + * File format for .res resource bundle files + * + * ICU 56: New in formatVersion 3 compared with 2: ------------- + * + * Resource bundles can optionally use shared string-v2 values + * stored in the pool bundle. + * If so, then the indexes[] contain two new values + * in previously-unused bits of existing indexes[] slots: + * - poolStringIndexLimit: + * String-v2 offsets (in 32-bit Resource words) below this limit + * point to pool bundle string-v2 values. + * - poolStringIndex16Limit: + * Resource16 string-v2 offsets below this limit + * point to pool bundle string-v2 values. + * Guarantee: poolStringIndex16Limit <= poolStringIndexLimit + * + * The local bundle's poolStringIndexLimit is greater than + * any pool bundle string index used in the local bundle. + * The poolStringIndexLimit should not be greater than + * the maximum possible pool bundle string index. + * + * The maximum possible pool bundle string index is the index to the last non-NUL + * pool string character, due to suffix sharing. + * + * In the pool bundle, there is no structure that lists the strings. + * (The root resource is an empty Table.) + * If the strings need to be enumerated (as genrb --usePoolBundle does), + * then iterate through the pool bundle's 16-bit-units array from the beginning. + * Stop at the end of the array, or when an explicit or implicit string length + * would lead beyond the end of the array, + * or when an apparent string is not NUL-terminated. + * (Future genrb version might terminate the strings with + * what looks like a large explicit string length.) + * + * ICU 4.4: New in formatVersion 2 compared with 1.3: ------------- + * + * Three new resource types -- String-v2, Table16 and Array16 -- have their + * values stored in a new array of 16-bit units between the table key strings + * and the start of the other resources. + * + * genrb eliminates duplicates among Unicode string-v2 values. + * Multiple Unicode strings may use the same offset and string data, + * or a short string may point to the suffix of a longer string. ("Suffix sharing") + * For example, one string "abc" may be reused for another string "bc" by pointing + * to the second character. (Short strings-v2 are NUL-terminated + * and not preceded by an explicit length value.) + * + * It is allowed for all resource types to share values. + * The swapper code (ures_swap()) has been modified so that it swaps each item + * exactly once. + * + * A resource bundle may use a special pool bundle. Some or all of the table key strings + * of the using-bundle are omitted, and the key string offsets for such key strings refer + * to offsets in the pool bundle. + * The using-bundle's and the pool-bundle's indexes[URES_INDEX_POOL_CHECKSUM] values + * must match. + * Two bits in indexes[URES_INDEX_ATTRIBUTES] indicate whether a resource bundle + * is or uses a pool bundle. + * + * Table key strings must be compared in ASCII order, even if they are not + * stored in ASCII. + * + * New in formatVersion 1.3 compared with 1.2: ------------- + * + * genrb eliminates duplicates among key strings. + * Multiple table items may share one key string, or one item may point + * to the suffix of another's key string. ("Suffix sharing") + * For example, one key "abc" may be reused for another key "bc" by pointing + * to the second character. (Key strings are NUL-terminated.) + * + * ------------- + * + * An ICU4C resource bundle file (.res) is a binary, memory-mappable file + * with nested, hierarchical data structures. + * It physically contains the following: + * + * Resource root; -- 32-bit Resource item, root item for this bundle's tree; + * currently, the root item must be a table or table32 resource item + * int32_t indexes[indexes[0]]; -- array of indexes for friendly + * reading and swapping; see URES_INDEX_* above + * new in formatVersion 1.1 (ICU 2.8) + * char keys[]; -- characters for key strings + * (formatVersion 1.0: up to 65k of characters; 1.1: <2G) + * (minus the space for root and indexes[]), + * which consist of invariant characters (ASCII/EBCDIC) and are NUL-terminated; + * padded to multiple of 4 bytes for 4-alignment of the following data + * uint16_t 16BitUnits[]; -- resources that are stored entirely as sequences of 16-bit units + * (new in formatVersion 2/ICU 4.4) + * data is indexed by the offset values in 16-bit resource types, + * with offset 0 pointing to the beginning of this array; + * there is a 0 at offset 0, for empty resources; + * padded to multiple of 4 bytes for 4-alignment of the following data + * data; -- data directly and indirectly indexed by the root item; + * the structure is determined by walking the tree + * + * Each resource bundle item has a 32-bit Resource handle (see typedef above) + * which contains the item type number in its upper 4 bits (31..28) and either + * an offset or a direct value in its lower 28 bits (27..0). + * The order of items is undefined and only determined by walking the tree. + * Leaves of the tree may be stored first or last or anywhere in between, + * and it is in theory possible to have unreferenced holes in the file. + * + * 16-bit-unit values: + * Starting with formatVersion 2/ICU 4.4, some resources are stored in a special + * array of 16-bit units. Each resource value is a sequence of 16-bit units, + * with no per-resource padding to a 4-byte boundary. + * 16-bit container types (Table16 and Array16) contain Resource16 values + * which are offsets to String-v2 resources in the same 16-bit-units array. + * + * Direct values: + * - Empty Unicode strings have an offset value of 0 in the Resource handle itself. + * - Starting with formatVersion 2/ICU 4.4, an offset value of 0 for + * _any_ resource type indicates an empty value. + * - Integer values are 28-bit values stored in the Resource handle itself; + * the interpretation of unsigned vs. signed integers is up to the application. + * + * All other types and values use 28-bit offsets to point to the item's data. + * The offset is an index to the first 32-bit word of the value, relative to the + * start of the resource data (i.e., the root item handle is at offset 0). + * To get byte offsets, the offset is multiplied by 4 (or shifted left by 2 bits). + * All resource item values are 4-aligned. + * + * New in formatVersion 2/ICU 4.4: Some types use offsets into the 16-bit-units array, + * indexing 16-bit units in that array. + * + * The structures (memory layouts) for the values for each item type are listed + * in the table below. + * + * Nested, hierarchical structures: ------------- + * + * Table items contain key-value pairs where the keys are offsets to char * key strings. + * The values of these pairs are either Resource handles or + * offsets into the 16-bit-units array, depending on the table type. + * + * Array items are simple vectors of Resource handles, + * or of offsets into the 16-bit-units array, depending on the array type. + * + * Table key string offsets: ------- + * + * Key string offsets are relative to the start of the resource data (of the root handle), + * i.e., the first string has an offset of 4+sizeof(indexes). + * (After the 4-byte root handle and after the indexes array.) + * + * If the resource bundle uses a pool bundle, then some key strings are stored + * in the pool bundle rather than in the local bundle itself. + * - In a Table or Table16, the 16-bit key string offset is local if it is + * less than indexes[URES_INDEX_KEYS_TOP]<<2. + * Otherwise, subtract indexes[URES_INDEX_KEYS_TOP]<<2 to get the offset into + * the pool bundle key strings. + * - In a Table32, the 32-bit key string offset is local if it is non-negative. + * Otherwise, reset bit 31 to get the pool key string offset. + * + * Unlike the local offset, the pool key offset is relative to + * the start of the key strings, not to the start of the bundle. + * + * An alias item is special (and new in ICU 2.4): -------------- + * + * Its memory layout is just like for a UnicodeString, but at runtime it resolves to + * another resource bundle's item according to the path in the string. + * This is used to share items across bundles that are in different lookup/fallback + * chains (e.g., large collation data among zh_TW and zh_HK). + * This saves space (for large items) and maintenance effort (less duplication of data). + * + * -------------------------------------------------------------------------- + * + * Resource types: + * + * Most resources have their values stored at four-byte offsets from the start + * of the resource data. These values are at least 4-aligned. + * Some resource values are stored directly in the offset field of the Resource itself. + * See UResType in unicode/ures.h for enumeration constants for Resource types. + * + * Some resources have their values stored as sequences of 16-bit units, + * at 2-byte offsets from the start of a contiguous 16-bit-unit array between + * the table key strings and the other resources. (new in formatVersion 2/ICU 4.4) + * At offset 0 of that array is a 16-bit zero value for empty 16-bit resources. + * + * Resource16 values in Table16 and Array16 are 16-bit offsets to String-v2 + * resources, with the offsets relative to the start of the 16-bit-units array. + * Starting with formatVersion 3/ICU 56, if offset(URES_NONE)), + fTraceInfo() {} + virtual ~ResourceDataValue(); + + void setData(const ResourceData &data) { + pResData = &data; + } + + void setValidLocaleDataEntry(UResourceDataEntry *entry) { + validLocaleDataEntry = entry; + } + + void setResource(Resource r, ResourceTracer&& traceInfo) { + res = r; + fTraceInfo = traceInfo; + } + + const ResourceData &getData() const { return *pResData; } + UResourceDataEntry *getValidLocaleDataEntry() const { return validLocaleDataEntry; } + Resource getResource() const { return res; } + virtual UResType getType() const override; + virtual const char16_t *getString(int32_t &length, UErrorCode &errorCode) const override; + virtual const char16_t *getAliasString(int32_t &length, UErrorCode &errorCode) const override; + virtual int32_t getInt(UErrorCode &errorCode) const override; + virtual uint32_t getUInt(UErrorCode &errorCode) const override; + virtual const int32_t *getIntVector(int32_t &length, UErrorCode &errorCode) const override; + virtual const uint8_t *getBinary(int32_t &length, UErrorCode &errorCode) const override; + virtual ResourceArray getArray(UErrorCode &errorCode) const override; + virtual ResourceTable getTable(UErrorCode &errorCode) const override; + virtual UBool isNoInheritanceMarker() const override; + virtual int32_t getStringArray(UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) const override; + virtual int32_t getStringArrayOrStringAsArray(UnicodeString *dest, int32_t capacity, + UErrorCode &errorCode) const override; + virtual UnicodeString getStringOrFirstOfArray(UErrorCode &errorCode) const override; + +private: + const ResourceData *pResData; + UResourceDataEntry *validLocaleDataEntry; + Resource res; + ResourceTracer fTraceInfo; +}; + +U_NAMESPACE_END + +#endif /* __cplusplus */ + +/** + * Swap an ICU resource bundle. See udataswp.h. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +ures_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode); + +#endif diff --git a/deps/icu-small/source/common/uresimp.h b/deps/icu-small/source/common/uresimp.h index 4ac09bd8c4d998..09bdedfcdd26ea 100644 --- a/deps/icu-small/source/common/uresimp.h +++ b/deps/icu-small/source/common/uresimp.h @@ -1,397 +1,397 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2000-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#ifndef URESIMP_H -#define URESIMP_H - -#include "unicode/ures.h" -#include "unicode/utypes.h" - -#include "uresdata.h" - -#define kRootLocaleName "root" -#define kPoolBundleName "pool" - -/* - The default minor version and the version separator must be exactly one - character long. -*/ - -#define kDefaultMinorVersion "0" -#define kVersionSeparator "." -#define kVersionTag "Version" - -#define MAGIC1 19700503 -#define MAGIC2 19641227 - -#define URES_MAX_ALIAS_LEVEL 256 - -#define EMPTY_SET 0x2205 - -struct UResourceDataEntry; -typedef struct UResourceDataEntry UResourceDataEntry; - -/* - * Note: If we wanted to make this structure smaller, then we could try - * to use one UResourceDataEntry pointer for fAlias and fPool, with a separate - * flag to distinguish whether this struct is for a real bundle with a pool, - * or for an alias entry for which we won't use the pool after loading. - */ -struct UResourceDataEntry { - char *fName; /* name of the locale for bundle - still to decide whether it is original or fallback */ - char *fPath; /* path to bundle - used for distinguishing between resources with the same name */ - UResourceDataEntry *fParent; /*next resource in fallback chain*/ - UResourceDataEntry *fAlias; - UResourceDataEntry *fPool; - ResourceData fData; /* data for low level access */ - char fNameBuffer[3]; /* A small buffer of free space for fName. The free space is due to struct padding. */ - uint32_t fCountExisting; /* how much is this resource used */ - UErrorCode fBogus; - /* int32_t fHashKey;*/ /* for faster access in the hashtable */ -}; - -#define RES_BUFSIZE 64 -#define RES_PATH_SEPARATOR '/' -#define RES_PATH_SEPARATOR_S "/" - -U_CAPI void U_EXPORT2 ures_initStackObject(UResourceBundle* resB); - -#ifdef __cplusplus - -struct UResourceBundle { - const char *fKey; /*tag*/ - /** - * The dataEntry for the actual locale in which this item lives. - * Used for accessing the item's data. - * Non-const pointer for reference counting via entryIncrease(). - */ - UResourceDataEntry *fData; /*for low-level access*/ - char *fVersion; - /** - * The dataEntry for the valid locale. - * Used for /LOCALE/path alias resolution that starts back from the valid locale, - * rather than from the actual locale of this item which might live in - * an ancestor bundle. - */ - UResourceDataEntry *fValidLocaleDataEntry; - char *fResPath; /* full path to the resource: "zh_TW/CollationElements/Sequence" */ - char fResBuf[RES_BUFSIZE]; - int32_t fResPathLen; - Resource fRes; - UBool fHasFallback; - UBool fIsTopLevel; - uint32_t fMagic1; /* For determining if it's a stack object */ - uint32_t fMagic2; /* For determining if it's a stack object */ - int32_t fIndex; - int32_t fSize; - - inline const ResourceData &getResData() const { return fData->fData; } -}; - -U_NAMESPACE_BEGIN - -/** - * \class StackUResourceBundle - * "Smart pointer" like class, closes a UResourceBundle via ures_close(). - * - * This code: - * - * StackUResourceBundle bundle; - * foo(bundle.getAlias()); - * - * Is equivalent to this code: - * - * UResourceBundle bundle; - * ures_initStackObject(&bundle); - * foo(&bundle); - * ures_close(&bundle); - * - * @see LocalUResourceBundlePointer - * @internal - */ -class U_COMMON_API StackUResourceBundle { -public: - // No heap allocation. Use only on the stack. - static void* U_EXPORT2 operator new(size_t) U_NOEXCEPT = delete; - static void* U_EXPORT2 operator new[](size_t) U_NOEXCEPT = delete; -#if U_HAVE_PLACEMENT_NEW - static void* U_EXPORT2 operator new(size_t, void*) U_NOEXCEPT = delete; -#endif - - StackUResourceBundle(); - ~StackUResourceBundle(); - - UResourceBundle* getAlias() { return &bundle; } - - UResourceBundle& ref() { return bundle; } - const UResourceBundle& ref() const { return bundle; } - - StackUResourceBundle(const StackUResourceBundle&) = delete; - StackUResourceBundle& operator=(const StackUResourceBundle&) = delete; - - StackUResourceBundle(StackUResourceBundle&&) = delete; - StackUResourceBundle& operator=(StackUResourceBundle&&) = delete; - -private: - UResourceBundle bundle; -}; - -U_NAMESPACE_END - -#endif /* __cplusplus */ - -/** - * Opens a resource bundle for the locale; - * if there is not even a base language bundle, then loads the root bundle; - * never falls back to the default locale. - * - * This is used for algorithms that have good pan-Unicode default behavior, - * such as case mappings, collation, and segmentation (BreakIterator). - */ -U_CAPI UResourceBundle* U_EXPORT2 -ures_openNoDefault(const char* path, const char* localeID, UErrorCode* status); - -/* Some getters used by the copy constructor */ -U_CFUNC const char* ures_getName(const UResourceBundle* resB); -#ifdef URES_DEBUG -U_CFUNC const char* ures_getPath(const UResourceBundle* resB); -/** - * If anything was in the RB cache, dump it to the screen. - * @return true if there was anything into the cache - */ -U_CAPI UBool U_EXPORT2 ures_dumpCacheContents(void); -#endif - -/* Candidates for export */ -U_CFUNC UResourceBundle *ures_copyResb(UResourceBundle *r, const UResourceBundle *original, UErrorCode *status); - -/** - * Returns a resource that can be located using the pathToResource argument. One needs optional package, locale - * and path inside the locale, for example: "/myData/en/zoneStrings/3". Keys and indexes are supported. Keys - * need to reference data in named structures, while indexes can reference both named and anonymous resources. - * Features a fill-in parameter. - * - * Note, this function does NOT have a syntax for specifying items within a tree. May want to consider a - * syntax that delineates between package/tree and resource. - * - * @param pathToResource a path that will lead to the requested resource - * @param fillIn if NULL a new UResourceBundle struct is allocated and must be deleted by the caller. - * Alternatively, you can supply a struct to be filled by this function. - * @param status fills in the outgoing error code. - * @return a pointer to a UResourceBundle struct. If fill in param was NULL, caller must delete it - */ -U_CAPI UResourceBundle* U_EXPORT2 -ures_findResource(const char* pathToResource, - UResourceBundle *fillIn, UErrorCode *status); - -/** - * Returns a sub resource that can be located using the pathToResource argument. One needs a path inside - * the supplied resource, for example, if you have "en_US" resource bundle opened, you might ask for - * "zoneStrings/3". Keys and indexes are supported. Keys - * need to reference data in named structures, while indexes can reference both - * named and anonymous resources. - * Features a fill-in parameter. - * - * @param resourceBundle a resource - * @param pathToResource a path that will lead to the requested resource - * @param fillIn if NULL a new UResourceBundle struct is allocated and must be deleted by the caller. - * Alternatively, you can supply a struct to be filled by this function. - * @param status fills in the outgoing error code. - * @return a pointer to a UResourceBundle struct. If fill in param was NULL, caller must delete it - */ -U_CAPI UResourceBundle* U_EXPORT2 -ures_findSubResource(const UResourceBundle *resB, - char* pathToResource, - UResourceBundle *fillIn, UErrorCode *status); - -/** - * Returns a functionally equivalent locale (considering keywords) for the specified keyword. - * @param result fillin for the equivalent locale - * @param resultCapacity capacity of the fillin buffer - * @param path path to the tree, or NULL for ICU data - * @param resName top level resource. Example: "collations" - * @param keyword locale keyword. Example: "collation" - * @param locid The requested locale - * @param isAvailable If non-null, pointer to fillin parameter that indicates whether the - * requested locale was available. The locale is defined as 'available' if it physically - * exists within the specified tree. - * @param omitDefault if true, omit keyword and value if default. 'de_DE\@collation=standard' -> 'de_DE' - * @param status error code - * @return the actual buffer size needed for the full locale. If it's greater - * than resultCapacity, the returned full name will be truncated and an error code will be returned. - */ -U_CAPI int32_t U_EXPORT2 -ures_getFunctionalEquivalent(char *result, int32_t resultCapacity, - const char *path, const char *resName, const char *keyword, const char *locid, - UBool *isAvailable, UBool omitDefault, UErrorCode *status); - -/** - * Given a tree path and keyword, return a string enumeration of all possible values for that keyword. - * @param path path to the tree, or NULL for ICU data - * @param keyword a particular keyword to consider, must match a top level resource name - * within the tree. - * @param status error code - */ -U_CAPI UEnumeration* U_EXPORT2 -ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status); - - -/** - * Get a resource with multi-level fallback. Normally only the top level resources will - * fallback to its parent. This performs fallback on subresources. For example, when a table - * is defined in a resource bundle and a parent resource bundle, normally no fallback occurs - * on the sub-resources because the table is defined in the current resource bundle, but this - * function can perform fallback on the sub-resources of the table. - * @param resB a resource - * @param inKey a key associated with the requested resource - * @param fillIn if NULL a new UResourceBundle struct is allocated and must be deleted by the caller. - * Alternatively, you can supply a struct to be filled by this function. - * @param status: fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * could be a non-failing error - * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING - * @return a pointer to a UResourceBundle struct. If fill in param was NULL, caller must delete it - */ -U_CAPI UResourceBundle* U_EXPORT2 -ures_getByKeyWithFallback(const UResourceBundle *resB, - const char* inKey, - UResourceBundle *fillIn, - UErrorCode *status); - -/** - * Get a String with multi-level fallback. Normally only the top level resources will - * fallback to its parent. This performs fallback on subresources. For example, when a table - * is defined in a resource bundle and a parent resource bundle, normally no fallback occurs - * on the sub-resources because the table is defined in the current resource bundle, but this - * function can perform fallback on the sub-resources of the table. - * @param resB a resource - * @param inKey a key associated with the requested resource - * @param len if not NULL, used to return the length of the string - * @param status: fills in the outgoing error code - * could be U_MISSING_RESOURCE_ERROR if the key is not found - * could be a non-failing error - * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING - * @return returns a pointer to a zero-terminated UChar array which lives in a - * memory mapped/DLL file. - */ -U_CAPI const UChar* U_EXPORT2 -ures_getStringByKeyWithFallback(const UResourceBundle *resB, - const char* inKey, - int32_t* len, - UErrorCode *status); - -#ifdef __cplusplus - -U_CAPI void U_EXPORT2 -ures_getValueWithFallback(const UResourceBundle *bundle, const char *path, - UResourceBundle *tempFillIn, - icu::ResourceDataValue &value, UErrorCode &errorCode); - -/** - * Locates the resource specified by `path` in the resource bundle specified by `bundle` (performing any - * necessary fallback and following any aliases) and calls the specified `sink`'s `put()` method with that - * resource. Then walks the bundle's parent chain, calling `put()` on the sink for each item in the - * parent chain. - * @param bundle The bundle to search - * @param path The path of the desired resource - * @param sink A `ResourceSink` that gets called for each resource in the parent chain - * @param errorCode The error code - */ -U_CAPI void U_EXPORT2 -ures_getAllItemsWithFallback(const UResourceBundle *bundle, const char *path, - icu::ResourceSink &sink, UErrorCode &errorCode); - -/** - * Locates the resource specified by `path` in the resource bundle specified by `bundle` (performing any - * necessary fallback and following any aliases) and, if the resource is a table resource, iterates over its - * immediate child resources (again, following any aliases to get the individual resource values), and calls the specified - * `sink`'s `put()` method for each child resource (passing it that resource's key and either its actual value or, - * if that value is an alias, the value you get by following the alias). Then walks back over the bundle's - * parent chain, similarly iterating over each parent table resource's child resources. - * Does not descend beyond one level of table children. - * @param bundle The bundle to search - * @param path The path of the desired resource - * @param sink A `ResourceSink` that gets called for each child resource of the specified resource (and each child - * of the resources in its parent chain). - * @param errorCode The error code. This will be U_RESOURCE_TYPE_MISMATCH if the resource the caller - * is asking for isn't a table resource. - */ -U_CAPI void U_EXPORT2 -ures_getAllChildrenWithFallback(const UResourceBundle *bundle, const char *path, - icu::ResourceSink &sink, UErrorCode &errorCode); - -#endif /* __cplusplus */ - -/** - * Get a version number by key - * @param resB bundle containing version number - * @param key the key for the version number - * @param ver fillin for the version number - * @param status error code - */ -U_CAPI void U_EXPORT2 -ures_getVersionByKey(const UResourceBundle *resB, - const char *key, - UVersionInfo ver, - UErrorCode *status); - - -/** - * Internal function. - * Return the version number associated with this ResourceBundle as a string. - * - * @param resourceBundle The resource bundle for which the version is checked. - * @return A version number string as specified in the resource bundle or its parent. - * The caller does not own this string. - * @see ures_getVersion - */ -U_CAPI const char* U_EXPORT2 -ures_getVersionNumberInternal(const UResourceBundle *resourceBundle); - -/** - * Return the name of the Locale associated with this ResourceBundle. This API allows - * you to query for the real locale of the resource. For example, if you requested - * "en_US_CALIFORNIA" and only "en_US" bundle exists, "en_US" will be returned. - * For subresources, the locale where this resource comes from will be returned. - * If fallback has occurred, getLocale will reflect this. - * - * This internal version avoids deprecated-warnings in ICU code. - * - * @param resourceBundle resource bundle in question - * @param status just for catching illegal arguments - * @return A Locale name - */ -U_CAPI const char* U_EXPORT2 -ures_getLocaleInternal(const UResourceBundle* resourceBundle, - UErrorCode* status); - -/** - * Same as ures_openDirect() but uses the fill-in parameter instead of allocating a new bundle. - * - * @param r The existing UResourceBundle to fill in. If NULL then status will be - * set to U_ILLEGAL_ARGUMENT_ERROR. - * @param packageName The packageName and locale together point to an ICU udata object, - * as defined by udata_open( packageName, "res", locale, err) - * or equivalent. Typically, packageName will refer to a (.dat) file, or to - * a package registered with udata_setAppData(). Using a full file or directory - * pathname for packageName is deprecated. If NULL, ICU data will be used. - * @param locale specifies the locale for which we want to open the resource - * if NULL, the default locale will be used. If strlen(locale) == 0 - * root locale will be used. - * @param status The error code. - * @see ures_openDirect - * @internal - */ -U_CAPI void U_EXPORT2 -ures_openDirectFillIn(UResourceBundle *r, - const char *packageName, - const char *locale, - UErrorCode *status); - -#endif /*URESIMP_H*/ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2000-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#ifndef URESIMP_H +#define URESIMP_H + +#include "unicode/ures.h" +#include "unicode/utypes.h" + +#include "uresdata.h" + +#define kRootLocaleName "root" +#define kPoolBundleName "pool" + +/* + The default minor version and the version separator must be exactly one + character long. +*/ + +#define kDefaultMinorVersion "0" +#define kVersionSeparator "." +#define kVersionTag "Version" + +#define MAGIC1 19700503 +#define MAGIC2 19641227 + +#define URES_MAX_ALIAS_LEVEL 256 + +#define EMPTY_SET 0x2205 + +struct UResourceDataEntry; +typedef struct UResourceDataEntry UResourceDataEntry; + +/* + * Note: If we wanted to make this structure smaller, then we could try + * to use one UResourceDataEntry pointer for fAlias and fPool, with a separate + * flag to distinguish whether this struct is for a real bundle with a pool, + * or for an alias entry for which we won't use the pool after loading. + */ +struct UResourceDataEntry { + char *fName; /* name of the locale for bundle - still to decide whether it is original or fallback */ + char *fPath; /* path to bundle - used for distinguishing between resources with the same name */ + UResourceDataEntry *fParent; /*next resource in fallback chain*/ + UResourceDataEntry *fAlias; + UResourceDataEntry *fPool; + ResourceData fData; /* data for low level access */ + char fNameBuffer[3]; /* A small buffer of free space for fName. The free space is due to struct padding. */ + uint32_t fCountExisting; /* how much is this resource used */ + UErrorCode fBogus; + /* int32_t fHashKey;*/ /* for faster access in the hashtable */ +}; + +#define RES_BUFSIZE 64 +#define RES_PATH_SEPARATOR '/' +#define RES_PATH_SEPARATOR_S "/" + +U_CAPI void U_EXPORT2 ures_initStackObject(UResourceBundle* resB); + +#ifdef __cplusplus + +struct UResourceBundle { + const char *fKey; /*tag*/ + /** + * The dataEntry for the actual locale in which this item lives. + * Used for accessing the item's data. + * Non-const pointer for reference counting via entryIncrease(). + */ + UResourceDataEntry *fData; /*for low-level access*/ + char *fVersion; + /** + * The dataEntry for the valid locale. + * Used for /LOCALE/path alias resolution that starts back from the valid locale, + * rather than from the actual locale of this item which might live in + * an ancestor bundle. + */ + UResourceDataEntry *fValidLocaleDataEntry; + char *fResPath; /* full path to the resource: "zh_TW/CollationElements/Sequence" */ + char fResBuf[RES_BUFSIZE]; + int32_t fResPathLen; + Resource fRes; + UBool fHasFallback; + UBool fIsTopLevel; + uint32_t fMagic1; /* For determining if it's a stack object */ + uint32_t fMagic2; /* For determining if it's a stack object */ + int32_t fIndex; + int32_t fSize; + + inline const ResourceData &getResData() const { return fData->fData; } +}; + +U_NAMESPACE_BEGIN + +/** + * \class StackUResourceBundle + * "Smart pointer" like class, closes a UResourceBundle via ures_close(). + * + * This code: + * + * StackUResourceBundle bundle; + * foo(bundle.getAlias()); + * + * Is equivalent to this code: + * + * UResourceBundle bundle; + * ures_initStackObject(&bundle); + * foo(&bundle); + * ures_close(&bundle); + * + * @see LocalUResourceBundlePointer + * @internal + */ +class U_COMMON_API StackUResourceBundle { +public: + // No heap allocation. Use only on the stack. + static void* U_EXPORT2 operator new(size_t) noexcept = delete; + static void* U_EXPORT2 operator new[](size_t) noexcept = delete; +#if U_HAVE_PLACEMENT_NEW + static void* U_EXPORT2 operator new(size_t, void*) noexcept = delete; +#endif + + StackUResourceBundle(); + ~StackUResourceBundle(); + + UResourceBundle* getAlias() { return &bundle; } + + UResourceBundle& ref() { return bundle; } + const UResourceBundle& ref() const { return bundle; } + + StackUResourceBundle(const StackUResourceBundle&) = delete; + StackUResourceBundle& operator=(const StackUResourceBundle&) = delete; + + StackUResourceBundle(StackUResourceBundle&&) = delete; + StackUResourceBundle& operator=(StackUResourceBundle&&) = delete; + +private: + UResourceBundle bundle; +}; + +U_NAMESPACE_END + +#endif /* __cplusplus */ + +/** + * Opens a resource bundle for the locale; + * if there is not even a base language bundle, then loads the root bundle; + * never falls back to the default locale. + * + * This is used for algorithms that have good pan-Unicode default behavior, + * such as case mappings, collation, and segmentation (BreakIterator). + */ +U_CAPI UResourceBundle* U_EXPORT2 +ures_openNoDefault(const char* path, const char* localeID, UErrorCode* status); + +/* Some getters used by the copy constructor */ +U_CFUNC const char* ures_getName(const UResourceBundle* resB); +#ifdef URES_DEBUG +U_CFUNC const char* ures_getPath(const UResourceBundle* resB); +/** + * If anything was in the RB cache, dump it to the screen. + * @return true if there was anything into the cache + */ +U_CAPI UBool U_EXPORT2 ures_dumpCacheContents(void); +#endif + +/* Candidates for export */ +U_CFUNC UResourceBundle *ures_copyResb(UResourceBundle *r, const UResourceBundle *original, UErrorCode *status); + +/** + * Returns a resource that can be located using the pathToResource argument. One needs optional package, locale + * and path inside the locale, for example: "/myData/en/zoneStrings/3". Keys and indexes are supported. Keys + * need to reference data in named structures, while indexes can reference both named and anonymous resources. + * Features a fill-in parameter. + * + * Note, this function does NOT have a syntax for specifying items within a tree. May want to consider a + * syntax that delineates between package/tree and resource. + * + * @param pathToResource a path that will lead to the requested resource + * @param fillIn if NULL a new UResourceBundle struct is allocated and must be deleted by the caller. + * Alternatively, you can supply a struct to be filled by this function. + * @param status fills in the outgoing error code. + * @return a pointer to a UResourceBundle struct. If fill in param was NULL, caller must delete it + */ +U_CAPI UResourceBundle* U_EXPORT2 +ures_findResource(const char* pathToResource, + UResourceBundle *fillIn, UErrorCode *status); + +/** + * Returns a sub resource that can be located using the pathToResource argument. One needs a path inside + * the supplied resource, for example, if you have "en_US" resource bundle opened, you might ask for + * "zoneStrings/3". Keys and indexes are supported. Keys + * need to reference data in named structures, while indexes can reference both + * named and anonymous resources. + * Features a fill-in parameter. + * + * @param resourceBundle a resource + * @param pathToResource a path that will lead to the requested resource + * @param fillIn if NULL a new UResourceBundle struct is allocated and must be deleted by the caller. + * Alternatively, you can supply a struct to be filled by this function. + * @param status fills in the outgoing error code. + * @return a pointer to a UResourceBundle struct. If fill in param was NULL, caller must delete it + */ +U_CAPI UResourceBundle* U_EXPORT2 +ures_findSubResource(const UResourceBundle *resB, + char* pathToResource, + UResourceBundle *fillIn, UErrorCode *status); + +/** + * Returns a functionally equivalent locale (considering keywords) for the specified keyword. + * @param result fillin for the equivalent locale + * @param resultCapacity capacity of the fillin buffer + * @param path path to the tree, or NULL for ICU data + * @param resName top level resource. Example: "collations" + * @param keyword locale keyword. Example: "collation" + * @param locid The requested locale + * @param isAvailable If non-null, pointer to fillin parameter that indicates whether the + * requested locale was available. The locale is defined as 'available' if it physically + * exists within the specified tree. + * @param omitDefault if true, omit keyword and value if default. 'de_DE\@collation=standard' -> 'de_DE' + * @param status error code + * @return the actual buffer size needed for the full locale. If it's greater + * than resultCapacity, the returned full name will be truncated and an error code will be returned. + */ +U_CAPI int32_t U_EXPORT2 +ures_getFunctionalEquivalent(char *result, int32_t resultCapacity, + const char *path, const char *resName, const char *keyword, const char *locid, + UBool *isAvailable, UBool omitDefault, UErrorCode *status); + +/** + * Given a tree path and keyword, return a string enumeration of all possible values for that keyword. + * @param path path to the tree, or NULL for ICU data + * @param keyword a particular keyword to consider, must match a top level resource name + * within the tree. + * @param status error code + */ +U_CAPI UEnumeration* U_EXPORT2 +ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status); + + +/** + * Get a resource with multi-level fallback. Normally only the top level resources will + * fallback to its parent. This performs fallback on subresources. For example, when a table + * is defined in a resource bundle and a parent resource bundle, normally no fallback occurs + * on the sub-resources because the table is defined in the current resource bundle, but this + * function can perform fallback on the sub-resources of the table. + * @param resB a resource + * @param inKey a key associated with the requested resource + * @param fillIn if NULL a new UResourceBundle struct is allocated and must be deleted by the caller. + * Alternatively, you can supply a struct to be filled by this function. + * @param status: fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * could be a non-failing error + * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING + * @return a pointer to a UResourceBundle struct. If fill in param was NULL, caller must delete it + */ +U_CAPI UResourceBundle* U_EXPORT2 +ures_getByKeyWithFallback(const UResourceBundle *resB, + const char* inKey, + UResourceBundle *fillIn, + UErrorCode *status); + +/** + * Get a String with multi-level fallback. Normally only the top level resources will + * fallback to its parent. This performs fallback on subresources. For example, when a table + * is defined in a resource bundle and a parent resource bundle, normally no fallback occurs + * on the sub-resources because the table is defined in the current resource bundle, but this + * function can perform fallback on the sub-resources of the table. + * @param resB a resource + * @param inKey a key associated with the requested resource + * @param len if not NULL, used to return the length of the string + * @param status: fills in the outgoing error code + * could be U_MISSING_RESOURCE_ERROR if the key is not found + * could be a non-failing error + * e.g.: U_USING_FALLBACK_WARNING,U_USING_DEFAULT_WARNING + * @return returns a pointer to a zero-terminated UChar array which lives in a + * memory mapped/DLL file. + */ +U_CAPI const UChar* U_EXPORT2 +ures_getStringByKeyWithFallback(const UResourceBundle *resB, + const char* inKey, + int32_t* len, + UErrorCode *status); + +#ifdef __cplusplus + +U_CAPI void U_EXPORT2 +ures_getValueWithFallback(const UResourceBundle *bundle, const char *path, + UResourceBundle *tempFillIn, + icu::ResourceDataValue &value, UErrorCode &errorCode); + +/** + * Locates the resource specified by `path` in the resource bundle specified by `bundle` (performing any + * necessary fallback and following any aliases) and calls the specified `sink`'s `put()` method with that + * resource. Then walks the bundle's parent chain, calling `put()` on the sink for each item in the + * parent chain. + * @param bundle The bundle to search + * @param path The path of the desired resource + * @param sink A `ResourceSink` that gets called for each resource in the parent chain + * @param errorCode The error code + */ +U_CAPI void U_EXPORT2 +ures_getAllItemsWithFallback(const UResourceBundle *bundle, const char *path, + icu::ResourceSink &sink, UErrorCode &errorCode); + +/** + * Locates the resource specified by `path` in the resource bundle specified by `bundle` (performing any + * necessary fallback and following any aliases) and, if the resource is a table resource, iterates over its + * immediate child resources (again, following any aliases to get the individual resource values), and calls the specified + * `sink`'s `put()` method for each child resource (passing it that resource's key and either its actual value or, + * if that value is an alias, the value you get by following the alias). Then walks back over the bundle's + * parent chain, similarly iterating over each parent table resource's child resources. + * Does not descend beyond one level of table children. + * @param bundle The bundle to search + * @param path The path of the desired resource + * @param sink A `ResourceSink` that gets called for each child resource of the specified resource (and each child + * of the resources in its parent chain). + * @param errorCode The error code. This will be U_RESOURCE_TYPE_MISMATCH if the resource the caller + * is asking for isn't a table resource. + */ +U_CAPI void U_EXPORT2 +ures_getAllChildrenWithFallback(const UResourceBundle *bundle, const char *path, + icu::ResourceSink &sink, UErrorCode &errorCode); + +#endif /* __cplusplus */ + +/** + * Get a version number by key + * @param resB bundle containing version number + * @param key the key for the version number + * @param ver fillin for the version number + * @param status error code + */ +U_CAPI void U_EXPORT2 +ures_getVersionByKey(const UResourceBundle *resB, + const char *key, + UVersionInfo ver, + UErrorCode *status); + + +/** + * Internal function. + * Return the version number associated with this ResourceBundle as a string. + * + * @param resourceBundle The resource bundle for which the version is checked. + * @return A version number string as specified in the resource bundle or its parent. + * The caller does not own this string. + * @see ures_getVersion + */ +U_CAPI const char* U_EXPORT2 +ures_getVersionNumberInternal(const UResourceBundle *resourceBundle); + +/** + * Return the name of the Locale associated with this ResourceBundle. This API allows + * you to query for the real locale of the resource. For example, if you requested + * "en_US_CALIFORNIA" and only "en_US" bundle exists, "en_US" will be returned. + * For subresources, the locale where this resource comes from will be returned. + * If fallback has occurred, getLocale will reflect this. + * + * This internal version avoids deprecated-warnings in ICU code. + * + * @param resourceBundle resource bundle in question + * @param status just for catching illegal arguments + * @return A Locale name + */ +U_CAPI const char* U_EXPORT2 +ures_getLocaleInternal(const UResourceBundle* resourceBundle, + UErrorCode* status); + +/** + * Same as ures_openDirect() but uses the fill-in parameter instead of allocating a new bundle. + * + * @param r The existing UResourceBundle to fill in. If NULL then status will be + * set to U_ILLEGAL_ARGUMENT_ERROR. + * @param packageName The packageName and locale together point to an ICU udata object, + * as defined by udata_open( packageName, "res", locale, err) + * or equivalent. Typically, packageName will refer to a (.dat) file, or to + * a package registered with udata_setAppData(). Using a full file or directory + * pathname for packageName is deprecated. If NULL, ICU data will be used. + * @param locale specifies the locale for which we want to open the resource + * if NULL, the default locale will be used. If strlen(locale) == 0 + * root locale will be used. + * @param status The error code. + * @see ures_openDirect + * @internal + */ +U_CAPI void U_EXPORT2 +ures_openDirectFillIn(UResourceBundle *r, + const char *packageName, + const char *locale, + UErrorCode *status); + +#endif /*URESIMP_H*/ diff --git a/deps/icu-small/source/common/ureslocs.h b/deps/icu-small/source/common/ureslocs.h index f7c3344ef201a8..0643c0f382e726 100644 --- a/deps/icu-small/source/common/ureslocs.h +++ b/deps/icu-small/source/common/ureslocs.h @@ -1,27 +1,27 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2009-2014 International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#ifndef __URESLOCS_H__ -#define __URESLOCS_H__ - -#include "unicode/utypes.h" -#include "unicode/udata.h" - -U_CDECL_BEGIN - - -#define U_ICUDATA_LANG U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "lang" -#define U_ICUDATA_REGION U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "region" -#define U_ICUDATA_CURR U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "curr" -#define U_ICUDATA_ZONE U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "zone" -#define U_ICUDATA_UNIT U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "unit" - -U_CDECL_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2009-2014 International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#ifndef __URESLOCS_H__ +#define __URESLOCS_H__ + +#include "unicode/utypes.h" +#include "unicode/udata.h" + +U_CDECL_BEGIN + + +#define U_ICUDATA_LANG U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "lang" +#define U_ICUDATA_REGION U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "region" +#define U_ICUDATA_CURR U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "curr" +#define U_ICUDATA_ZONE U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "zone" +#define U_ICUDATA_UNIT U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "unit" + +U_CDECL_END + +#endif diff --git a/deps/icu-small/source/common/usc_impl.cpp b/deps/icu-small/source/common/usc_impl.cpp index a4e2fc6069a0b2..cbe126f51029db 100644 --- a/deps/icu-small/source/common/usc_impl.cpp +++ b/deps/icu-small/source/common/usc_impl.cpp @@ -1,361 +1,361 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* File USC_IMPL.C -* -* Modification History: -* -* Date Name Description -* 07/08/2002 Eric Mader Creation. -****************************************************************************** -*/ - -#include "unicode/uscript.h" -#include "usc_impl.h" -#include "cmemory.h" - -#define PAREN_STACK_DEPTH 32 - -#define MOD(sp) ((sp) % PAREN_STACK_DEPTH) -#define LIMIT_INC(sp) (((sp) < PAREN_STACK_DEPTH)? (sp) + 1 : PAREN_STACK_DEPTH) -#define INC(sp,count) (MOD((sp) + (count))) -#define INC1(sp) (INC(sp, 1)) -#define DEC(sp,count) (MOD((sp) + PAREN_STACK_DEPTH - (count))) -#define DEC1(sp) (DEC(sp, 1)) -#define STACK_IS_EMPTY(scriptRun) ((scriptRun)->pushCount <= 0) -#define STACK_IS_NOT_EMPTY(scriptRun) (! STACK_IS_EMPTY(scriptRun)) -#define TOP(scriptRun) ((scriptRun)->parenStack[(scriptRun)->parenSP]) -#define SYNC_FIXUP(scriptRun) ((scriptRun)->fixupCount = 0) - -struct ParenStackEntry -{ - int32_t pairIndex; - UScriptCode scriptCode; -}; - -struct UScriptRun -{ - int32_t textLength; - const UChar *textArray; - - int32_t scriptStart; - int32_t scriptLimit; - UScriptCode scriptCode; - - struct ParenStackEntry parenStack[PAREN_STACK_DEPTH]; - int32_t parenSP; - int32_t pushCount; - int32_t fixupCount; -}; - -static int8_t highBit(int32_t value); - -static const UChar32 pairedChars[] = { - 0x0028, 0x0029, /* ascii paired punctuation */ - 0x003c, 0x003e, - 0x005b, 0x005d, - 0x007b, 0x007d, - 0x00ab, 0x00bb, /* guillemets */ - 0x2018, 0x2019, /* general punctuation */ - 0x201c, 0x201d, - 0x2039, 0x203a, - 0x3008, 0x3009, /* chinese paired punctuation */ - 0x300a, 0x300b, - 0x300c, 0x300d, - 0x300e, 0x300f, - 0x3010, 0x3011, - 0x3014, 0x3015, - 0x3016, 0x3017, - 0x3018, 0x3019, - 0x301a, 0x301b -}; - -static void push(UScriptRun *scriptRun, int32_t pairIndex, UScriptCode scriptCode) -{ - scriptRun->pushCount = LIMIT_INC(scriptRun->pushCount); - scriptRun->fixupCount = LIMIT_INC(scriptRun->fixupCount); - - scriptRun->parenSP = INC1(scriptRun->parenSP); - scriptRun->parenStack[scriptRun->parenSP].pairIndex = pairIndex; - scriptRun->parenStack[scriptRun->parenSP].scriptCode = scriptCode; -} - -static void pop(UScriptRun *scriptRun) -{ - if (STACK_IS_EMPTY(scriptRun)) { - return; - } - - if (scriptRun->fixupCount > 0) { - scriptRun->fixupCount -= 1; - } - - scriptRun->pushCount -= 1; - scriptRun->parenSP = DEC1(scriptRun->parenSP); - - /* If the stack is now empty, reset the stack - pointers to their initial values. - */ - if (STACK_IS_EMPTY(scriptRun)) { - scriptRun->parenSP = -1; - } -} - -static void fixup(UScriptRun *scriptRun, UScriptCode scriptCode) -{ - int32_t fixupSP = DEC(scriptRun->parenSP, scriptRun->fixupCount); - - while (scriptRun->fixupCount-- > 0) { - fixupSP = INC1(fixupSP); - scriptRun->parenStack[fixupSP].scriptCode = scriptCode; - } -} - -static int8_t -highBit(int32_t value) -{ - int8_t bit = 0; - - if (value <= 0) { - return -32; - } - - if (value >= 1 << 16) { - value >>= 16; - bit += 16; - } - - if (value >= 1 << 8) { - value >>= 8; - bit += 8; - } - - if (value >= 1 << 4) { - value >>= 4; - bit += 4; - } - - if (value >= 1 << 2) { - value >>= 2; - bit += 2; - } - - if (value >= 1 << 1) { - //value >>= 1; - bit += 1; - } - - return bit; -} - -static int32_t -getPairIndex(UChar32 ch) -{ - int32_t pairedCharCount = UPRV_LENGTHOF(pairedChars); - int32_t pairedCharPower = 1 << highBit(pairedCharCount); - int32_t pairedCharExtra = pairedCharCount - pairedCharPower; - - int32_t probe = pairedCharPower; - int32_t pairIndex = 0; - - if (ch >= pairedChars[pairedCharExtra]) { - pairIndex = pairedCharExtra; - } - - while (probe > (1 << 0)) { - probe >>= 1; - - if (ch >= pairedChars[pairIndex + probe]) { - pairIndex += probe; - } - } - - if (pairedChars[pairIndex] != ch) { - pairIndex = -1; - } - - return pairIndex; -} - -static UBool -sameScript(UScriptCode scriptOne, UScriptCode scriptTwo) -{ - return scriptOne <= USCRIPT_INHERITED || scriptTwo <= USCRIPT_INHERITED || scriptOne == scriptTwo; -} - -U_CAPI UScriptRun * U_EXPORT2 -uscript_openRun(const UChar *src, int32_t length, UErrorCode *pErrorCode) -{ - UScriptRun *result = NULL; - - if (pErrorCode == NULL || U_FAILURE(*pErrorCode)) { - return NULL; - } - - result = (UScriptRun *)uprv_malloc(sizeof (UScriptRun)); - - if (result == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - uscript_setRunText(result, src, length, pErrorCode); - - /* Release the UScriptRun if uscript_setRunText() returns an error */ - if (U_FAILURE(*pErrorCode)) { - uprv_free(result); - result = NULL; - } - - return result; -} - -U_CAPI void U_EXPORT2 -uscript_closeRun(UScriptRun *scriptRun) -{ - if (scriptRun != NULL) { - uprv_free(scriptRun); - } -} - -U_CAPI void U_EXPORT2 -uscript_resetRun(UScriptRun *scriptRun) -{ - if (scriptRun != NULL) { - scriptRun->scriptStart = 0; - scriptRun->scriptLimit = 0; - scriptRun->scriptCode = USCRIPT_INVALID_CODE; - scriptRun->parenSP = -1; - scriptRun->pushCount = 0; - scriptRun->fixupCount = 0; - } -} - -U_CAPI void U_EXPORT2 -uscript_setRunText(UScriptRun *scriptRun, const UChar *src, int32_t length, UErrorCode *pErrorCode) -{ - if (pErrorCode == NULL || U_FAILURE(*pErrorCode)) { - return; - } - - if (scriptRun == NULL || length < 0 || ((src == NULL) != (length == 0))) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - scriptRun->textArray = src; - scriptRun->textLength = length; - - uscript_resetRun(scriptRun); -} - -U_CAPI UBool U_EXPORT2 -uscript_nextRun(UScriptRun *scriptRun, int32_t *pRunStart, int32_t *pRunLimit, UScriptCode *pRunScript) -{ - UErrorCode error = U_ZERO_ERROR; - - /* if we've fallen off the end of the text, we're done */ - if (scriptRun == NULL || scriptRun->scriptLimit >= scriptRun->textLength) { - return false; - } - - SYNC_FIXUP(scriptRun); - scriptRun->scriptCode = USCRIPT_COMMON; - - for (scriptRun->scriptStart = scriptRun->scriptLimit; scriptRun->scriptLimit < scriptRun->textLength; scriptRun->scriptLimit += 1) { - UChar high = scriptRun->textArray[scriptRun->scriptLimit]; - UChar32 ch = high; - UScriptCode sc; - int32_t pairIndex; - - /* - * if the character is a high surrogate and it's not the last one - * in the text, see if it's followed by a low surrogate - */ - if (high >= 0xD800 && high <= 0xDBFF && scriptRun->scriptLimit < scriptRun->textLength - 1) { - UChar low = scriptRun->textArray[scriptRun->scriptLimit + 1]; - - /* - * if it is followed by a low surrogate, - * consume it and form the full character - */ - if (low >= 0xDC00 && low <= 0xDFFF) { - ch = (high - 0xD800) * 0x0400 + low - 0xDC00 + 0x10000; - scriptRun->scriptLimit += 1; - } - } - - sc = uscript_getScript(ch, &error); - pairIndex = getPairIndex(ch); - - /* - * Paired character handling: - * - * if it's an open character, push it onto the stack. - * if it's a close character, find the matching open on the - * stack, and use that script code. Any non-matching open - * characters above it on the stack will be poped. - */ - if (pairIndex >= 0) { - if ((pairIndex & 1) == 0) { - push(scriptRun, pairIndex, scriptRun->scriptCode); - } else { - int32_t pi = pairIndex & ~1; - - while (STACK_IS_NOT_EMPTY(scriptRun) && TOP(scriptRun).pairIndex != pi) { - pop(scriptRun); - } - - if (STACK_IS_NOT_EMPTY(scriptRun)) { - sc = TOP(scriptRun).scriptCode; - } - } - } - - if (sameScript(scriptRun->scriptCode, sc)) { - if (scriptRun->scriptCode <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) { - scriptRun->scriptCode = sc; - - fixup(scriptRun, scriptRun->scriptCode); - } - - /* - * if this character is a close paired character, - * pop the matching open character from the stack - */ - if (pairIndex >= 0 && (pairIndex & 1) != 0) { - pop(scriptRun); - } - } else { - /* - * if the run broke on a surrogate pair, - * end it before the high surrogate - */ - if (ch >= 0x10000) { - scriptRun->scriptLimit -= 1; - } - - break; - } - } - - - if (pRunStart != NULL) { - *pRunStart = scriptRun->scriptStart; - } - - if (pRunLimit != NULL) { - *pRunLimit = scriptRun->scriptLimit; - } - - if (pRunScript != NULL) { - *pRunScript = scriptRun->scriptCode; - } - - return true; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* File USC_IMPL.C +* +* Modification History: +* +* Date Name Description +* 07/08/2002 Eric Mader Creation. +****************************************************************************** +*/ + +#include "unicode/uscript.h" +#include "usc_impl.h" +#include "cmemory.h" + +#define PAREN_STACK_DEPTH 32 + +#define MOD(sp) ((sp) % PAREN_STACK_DEPTH) +#define LIMIT_INC(sp) (((sp) < PAREN_STACK_DEPTH)? (sp) + 1 : PAREN_STACK_DEPTH) +#define INC(sp,count) (MOD((sp) + (count))) +#define INC1(sp) (INC(sp, 1)) +#define DEC(sp,count) (MOD((sp) + PAREN_STACK_DEPTH - (count))) +#define DEC1(sp) (DEC(sp, 1)) +#define STACK_IS_EMPTY(scriptRun) ((scriptRun)->pushCount <= 0) +#define STACK_IS_NOT_EMPTY(scriptRun) (! STACK_IS_EMPTY(scriptRun)) +#define TOP(scriptRun) ((scriptRun)->parenStack[(scriptRun)->parenSP]) +#define SYNC_FIXUP(scriptRun) ((scriptRun)->fixupCount = 0) + +struct ParenStackEntry +{ + int32_t pairIndex; + UScriptCode scriptCode; +}; + +struct UScriptRun +{ + int32_t textLength; + const char16_t *textArray; + + int32_t scriptStart; + int32_t scriptLimit; + UScriptCode scriptCode; + + struct ParenStackEntry parenStack[PAREN_STACK_DEPTH]; + int32_t parenSP; + int32_t pushCount; + int32_t fixupCount; +}; + +static int8_t highBit(int32_t value); + +static const UChar32 pairedChars[] = { + 0x0028, 0x0029, /* ascii paired punctuation */ + 0x003c, 0x003e, + 0x005b, 0x005d, + 0x007b, 0x007d, + 0x00ab, 0x00bb, /* guillemets */ + 0x2018, 0x2019, /* general punctuation */ + 0x201c, 0x201d, + 0x2039, 0x203a, + 0x3008, 0x3009, /* chinese paired punctuation */ + 0x300a, 0x300b, + 0x300c, 0x300d, + 0x300e, 0x300f, + 0x3010, 0x3011, + 0x3014, 0x3015, + 0x3016, 0x3017, + 0x3018, 0x3019, + 0x301a, 0x301b +}; + +static void push(UScriptRun *scriptRun, int32_t pairIndex, UScriptCode scriptCode) +{ + scriptRun->pushCount = LIMIT_INC(scriptRun->pushCount); + scriptRun->fixupCount = LIMIT_INC(scriptRun->fixupCount); + + scriptRun->parenSP = INC1(scriptRun->parenSP); + scriptRun->parenStack[scriptRun->parenSP].pairIndex = pairIndex; + scriptRun->parenStack[scriptRun->parenSP].scriptCode = scriptCode; +} + +static void pop(UScriptRun *scriptRun) +{ + if (STACK_IS_EMPTY(scriptRun)) { + return; + } + + if (scriptRun->fixupCount > 0) { + scriptRun->fixupCount -= 1; + } + + scriptRun->pushCount -= 1; + scriptRun->parenSP = DEC1(scriptRun->parenSP); + + /* If the stack is now empty, reset the stack + pointers to their initial values. + */ + if (STACK_IS_EMPTY(scriptRun)) { + scriptRun->parenSP = -1; + } +} + +static void fixup(UScriptRun *scriptRun, UScriptCode scriptCode) +{ + int32_t fixupSP = DEC(scriptRun->parenSP, scriptRun->fixupCount); + + while (scriptRun->fixupCount-- > 0) { + fixupSP = INC1(fixupSP); + scriptRun->parenStack[fixupSP].scriptCode = scriptCode; + } +} + +static int8_t +highBit(int32_t value) +{ + int8_t bit = 0; + + if (value <= 0) { + return -32; + } + + if (value >= 1 << 16) { + value >>= 16; + bit += 16; + } + + if (value >= 1 << 8) { + value >>= 8; + bit += 8; + } + + if (value >= 1 << 4) { + value >>= 4; + bit += 4; + } + + if (value >= 1 << 2) { + value >>= 2; + bit += 2; + } + + if (value >= 1 << 1) { + //value >>= 1; + bit += 1; + } + + return bit; +} + +static int32_t +getPairIndex(UChar32 ch) +{ + int32_t pairedCharCount = UPRV_LENGTHOF(pairedChars); + int32_t pairedCharPower = 1 << highBit(pairedCharCount); + int32_t pairedCharExtra = pairedCharCount - pairedCharPower; + + int32_t probe = pairedCharPower; + int32_t pairIndex = 0; + + if (ch >= pairedChars[pairedCharExtra]) { + pairIndex = pairedCharExtra; + } + + while (probe > (1 << 0)) { + probe >>= 1; + + if (ch >= pairedChars[pairIndex + probe]) { + pairIndex += probe; + } + } + + if (pairedChars[pairIndex] != ch) { + pairIndex = -1; + } + + return pairIndex; +} + +static UBool +sameScript(UScriptCode scriptOne, UScriptCode scriptTwo) +{ + return scriptOne <= USCRIPT_INHERITED || scriptTwo <= USCRIPT_INHERITED || scriptOne == scriptTwo; +} + +U_CAPI UScriptRun * U_EXPORT2 +uscript_openRun(const char16_t *src, int32_t length, UErrorCode *pErrorCode) +{ + UScriptRun *result = nullptr; + + if (pErrorCode == nullptr || U_FAILURE(*pErrorCode)) { + return nullptr; + } + + result = (UScriptRun *)uprv_malloc(sizeof (UScriptRun)); + + if (result == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + uscript_setRunText(result, src, length, pErrorCode); + + /* Release the UScriptRun if uscript_setRunText() returns an error */ + if (U_FAILURE(*pErrorCode)) { + uprv_free(result); + result = nullptr; + } + + return result; +} + +U_CAPI void U_EXPORT2 +uscript_closeRun(UScriptRun *scriptRun) +{ + if (scriptRun != nullptr) { + uprv_free(scriptRun); + } +} + +U_CAPI void U_EXPORT2 +uscript_resetRun(UScriptRun *scriptRun) +{ + if (scriptRun != nullptr) { + scriptRun->scriptStart = 0; + scriptRun->scriptLimit = 0; + scriptRun->scriptCode = USCRIPT_INVALID_CODE; + scriptRun->parenSP = -1; + scriptRun->pushCount = 0; + scriptRun->fixupCount = 0; + } +} + +U_CAPI void U_EXPORT2 +uscript_setRunText(UScriptRun *scriptRun, const char16_t *src, int32_t length, UErrorCode *pErrorCode) +{ + if (pErrorCode == nullptr || U_FAILURE(*pErrorCode)) { + return; + } + + if (scriptRun == nullptr || length < 0 || ((src == nullptr) != (length == 0))) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + scriptRun->textArray = src; + scriptRun->textLength = length; + + uscript_resetRun(scriptRun); +} + +U_CAPI UBool U_EXPORT2 +uscript_nextRun(UScriptRun *scriptRun, int32_t *pRunStart, int32_t *pRunLimit, UScriptCode *pRunScript) +{ + UErrorCode error = U_ZERO_ERROR; + + /* if we've fallen off the end of the text, we're done */ + if (scriptRun == nullptr || scriptRun->scriptLimit >= scriptRun->textLength) { + return false; + } + + SYNC_FIXUP(scriptRun); + scriptRun->scriptCode = USCRIPT_COMMON; + + for (scriptRun->scriptStart = scriptRun->scriptLimit; scriptRun->scriptLimit < scriptRun->textLength; scriptRun->scriptLimit += 1) { + char16_t high = scriptRun->textArray[scriptRun->scriptLimit]; + UChar32 ch = high; + UScriptCode sc; + int32_t pairIndex; + + /* + * if the character is a high surrogate and it's not the last one + * in the text, see if it's followed by a low surrogate + */ + if (high >= 0xD800 && high <= 0xDBFF && scriptRun->scriptLimit < scriptRun->textLength - 1) { + char16_t low = scriptRun->textArray[scriptRun->scriptLimit + 1]; + + /* + * if it is followed by a low surrogate, + * consume it and form the full character + */ + if (low >= 0xDC00 && low <= 0xDFFF) { + ch = (high - 0xD800) * 0x0400 + low - 0xDC00 + 0x10000; + scriptRun->scriptLimit += 1; + } + } + + sc = uscript_getScript(ch, &error); + pairIndex = getPairIndex(ch); + + /* + * Paired character handling: + * + * if it's an open character, push it onto the stack. + * if it's a close character, find the matching open on the + * stack, and use that script code. Any non-matching open + * characters above it on the stack will be poped. + */ + if (pairIndex >= 0) { + if ((pairIndex & 1) == 0) { + push(scriptRun, pairIndex, scriptRun->scriptCode); + } else { + int32_t pi = pairIndex & ~1; + + while (STACK_IS_NOT_EMPTY(scriptRun) && TOP(scriptRun).pairIndex != pi) { + pop(scriptRun); + } + + if (STACK_IS_NOT_EMPTY(scriptRun)) { + sc = TOP(scriptRun).scriptCode; + } + } + } + + if (sameScript(scriptRun->scriptCode, sc)) { + if (scriptRun->scriptCode <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) { + scriptRun->scriptCode = sc; + + fixup(scriptRun, scriptRun->scriptCode); + } + + /* + * if this character is a close paired character, + * pop the matching open character from the stack + */ + if (pairIndex >= 0 && (pairIndex & 1) != 0) { + pop(scriptRun); + } + } else { + /* + * if the run broke on a surrogate pair, + * end it before the high surrogate + */ + if (ch >= 0x10000) { + scriptRun->scriptLimit -= 1; + } + + break; + } + } + + + if (pRunStart != nullptr) { + *pRunStart = scriptRun->scriptStart; + } + + if (pRunLimit != nullptr) { + *pRunLimit = scriptRun->scriptLimit; + } + + if (pRunScript != nullptr) { + *pRunScript = scriptRun->scriptCode; + } + + return true; +} diff --git a/deps/icu-small/source/common/usc_impl.h b/deps/icu-small/source/common/usc_impl.h index cd76990501ac72..0469e7b7dad9ee 100644 --- a/deps/icu-small/source/common/usc_impl.h +++ b/deps/icu-small/source/common/usc_impl.h @@ -1,139 +1,139 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* File USC_IMPL.H -* -* Modification History: -* -* Date Name Description -* 07/08/2002 Eric Mader Creation. -****************************************************************************** -*/ - -#ifndef USC_IMPL_H -#define USC_IMPL_H -#include "unicode/utypes.h" -#include "unicode/uscript.h" - -/** - * UScriptRun is used to find runs of characters in - * the same script. It implements a simple iterator over an array - * of characters. The iterator will resolve script-neutral characters - * like punctuation into the script of the surrounding characters. - * - * The iterator will try to match paired punctuation. If it sees an - * opening punctuation character, it will remember the script that - * was assigned to that character, and assign the same script to the - * matching closing punctuation. - * - * Scripts are chosen based on the UScriptCode enumeration. - * No attempt is made to combine related scripts into a single run. In - * particular, Hiragana, Katakana, and Han characters will appear in separate - * runs. - - * Here is an example of how to iterate over script runs: - *

- * \code
- * void printScriptRuns(const UChar *text, int32_t length)
- * {
- *     UErrorCode error = U_ZERO_ERROR;
- *     UScriptRun *scriptRun = uscript_openRun(text, testLength, &error);
- *     int32_t start = 0, limit = 0;
- *     UScriptCode code = USCRIPT_INVALID_CODE;
- *
- *     while (uscript_nextRun(&start, &limit, &code)) {
- *         printf("Script '%s' from %d to %d.\n", uscript_getName(code), start, limit);
- *     }
- *
- *     uscript_closeRun(scriptRun);
- *  }
- * 
- */ -struct UScriptRun; - -typedef struct UScriptRun UScriptRun; - -/** - * Create a UScriptRun object for iterating over the given text. This object must - * be freed using uscript_closeRun(). Note that this object does not copy the source text, - * only the pointer to it. You must make sure that the pointer remains valid until you call - * uscript_closeRun() or uscript_setRunText(). - * - * @param src is the address of the array of characters over which to iterate. - * if src == NULL and length == 0, - * an empty UScriptRun object will be returned. - * - * @param length is the number of characters over which to iterate. - * - * @param pErrorCode is a pointer to a valid UErrorCode value. If this value - * indicates a failure on entry, the function will immediately return. - * On exit the value will indicate the success of the operation. - * - * @return the address of UScriptRun object which will iterate over the text, - * or NULL if the operation failed. - */ -U_CAPI UScriptRun * U_EXPORT2 -uscript_openRun(const UChar *src, int32_t length, UErrorCode *pErrorCode); - -/** - * Frees the given UScriptRun object and any storage associated with it. - * On return, scriptRun no longer points to a valid UScriptRun object. - * - * @param scriptRun is the UScriptRun object which will be freed. - */ -U_CAPI void U_EXPORT2 -uscript_closeRun(UScriptRun *scriptRun); - -/** - * Reset the UScriptRun object so that it will start iterating from - * the beginning. - * - * @param scriptRun is the address of the UScriptRun object to be reset. - */ -U_CAPI void U_EXPORT2 -uscript_resetRun(UScriptRun *scriptRun); - -/** - * Change the text over which the given UScriptRun object iterates. - * - * @param scriptRun is the UScriptRun object which will be changed. - * - * @param src is the address of the new array of characters over which to iterate. - * If src == NULL and length == 0, - * the UScriptRun object will become empty. - * - * @param length is the new number of characters over which to iterate - * - * @param pErrorCode is a pointer to a valid UErrorCode value. If this value - * indicates a failure on entry, the function will immediately return. - * On exit the value will indicate the success of the operation. - */ -U_CAPI void U_EXPORT2 -uscript_setRunText(UScriptRun *scriptRun, const UChar *src, int32_t length, UErrorCode *pErrorCode); - -/** - * Advance the UScriptRun object to the next script run, return the start and limit - * offsets, and the script of the run. - * - * @param scriptRun is the address of the UScriptRun object. - * - * @param pRunStart is a pointer to the variable to receive the starting offset of the next run. - * This pointer can be NULL if the value is not needed. - * - * @param pRunLimit is a pointer to the variable to receive the limit offset of the next run. - * This pointer can be NULL if the value is not needed. - * - * @param pRunScript is a pointer to the variable to receive the UScriptCode for the - * script of the current run. This pointer can be NULL if the value is not needed. - * - * @return true if there was another script run. - */ -U_CAPI UBool U_EXPORT2 -uscript_nextRun(UScriptRun *scriptRun, int32_t *pRunStart, int32_t *pRunLimit, UScriptCode *pRunScript); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* File USC_IMPL.H +* +* Modification History: +* +* Date Name Description +* 07/08/2002 Eric Mader Creation. +****************************************************************************** +*/ + +#ifndef USC_IMPL_H +#define USC_IMPL_H +#include "unicode/utypes.h" +#include "unicode/uscript.h" + +/** + * UScriptRun is used to find runs of characters in + * the same script. It implements a simple iterator over an array + * of characters. The iterator will resolve script-neutral characters + * like punctuation into the script of the surrounding characters. + * + * The iterator will try to match paired punctuation. If it sees an + * opening punctuation character, it will remember the script that + * was assigned to that character, and assign the same script to the + * matching closing punctuation. + * + * Scripts are chosen based on the UScriptCode enumeration. + * No attempt is made to combine related scripts into a single run. In + * particular, Hiragana, Katakana, and Han characters will appear in separate + * runs. + + * Here is an example of how to iterate over script runs: + *
+ * \code
+ * void printScriptRuns(const UChar *text, int32_t length)
+ * {
+ *     UErrorCode error = U_ZERO_ERROR;
+ *     UScriptRun *scriptRun = uscript_openRun(text, testLength, &error);
+ *     int32_t start = 0, limit = 0;
+ *     UScriptCode code = USCRIPT_INVALID_CODE;
+ *
+ *     while (uscript_nextRun(&start, &limit, &code)) {
+ *         printf("Script '%s' from %d to %d.\n", uscript_getName(code), start, limit);
+ *     }
+ *
+ *     uscript_closeRun(scriptRun);
+ *  }
+ * 
+ */ +struct UScriptRun; + +typedef struct UScriptRun UScriptRun; + +/** + * Create a UScriptRun object for iterating over the given text. This object must + * be freed using uscript_closeRun(). Note that this object does not copy the source text, + * only the pointer to it. You must make sure that the pointer remains valid until you call + * uscript_closeRun() or uscript_setRunText(). + * + * @param src is the address of the array of characters over which to iterate. + * if src == NULL and length == 0, + * an empty UScriptRun object will be returned. + * + * @param length is the number of characters over which to iterate. + * + * @param pErrorCode is a pointer to a valid UErrorCode value. If this value + * indicates a failure on entry, the function will immediately return. + * On exit the value will indicate the success of the operation. + * + * @return the address of UScriptRun object which will iterate over the text, + * or NULL if the operation failed. + */ +U_CAPI UScriptRun * U_EXPORT2 +uscript_openRun(const UChar *src, int32_t length, UErrorCode *pErrorCode); + +/** + * Frees the given UScriptRun object and any storage associated with it. + * On return, scriptRun no longer points to a valid UScriptRun object. + * + * @param scriptRun is the UScriptRun object which will be freed. + */ +U_CAPI void U_EXPORT2 +uscript_closeRun(UScriptRun *scriptRun); + +/** + * Reset the UScriptRun object so that it will start iterating from + * the beginning. + * + * @param scriptRun is the address of the UScriptRun object to be reset. + */ +U_CAPI void U_EXPORT2 +uscript_resetRun(UScriptRun *scriptRun); + +/** + * Change the text over which the given UScriptRun object iterates. + * + * @param scriptRun is the UScriptRun object which will be changed. + * + * @param src is the address of the new array of characters over which to iterate. + * If src == NULL and length == 0, + * the UScriptRun object will become empty. + * + * @param length is the new number of characters over which to iterate + * + * @param pErrorCode is a pointer to a valid UErrorCode value. If this value + * indicates a failure on entry, the function will immediately return. + * On exit the value will indicate the success of the operation. + */ +U_CAPI void U_EXPORT2 +uscript_setRunText(UScriptRun *scriptRun, const UChar *src, int32_t length, UErrorCode *pErrorCode); + +/** + * Advance the UScriptRun object to the next script run, return the start and limit + * offsets, and the script of the run. + * + * @param scriptRun is the address of the UScriptRun object. + * + * @param pRunStart is a pointer to the variable to receive the starting offset of the next run. + * This pointer can be NULL if the value is not needed. + * + * @param pRunLimit is a pointer to the variable to receive the limit offset of the next run. + * This pointer can be NULL if the value is not needed. + * + * @param pRunScript is a pointer to the variable to receive the UScriptCode for the + * script of the current run. This pointer can be NULL if the value is not needed. + * + * @return true if there was another script run. + */ +U_CAPI UBool U_EXPORT2 +uscript_nextRun(UScriptRun *scriptRun, int32_t *pRunStart, int32_t *pRunLimit, UScriptCode *pRunScript); + +#endif diff --git a/deps/icu-small/source/common/uscript.cpp b/deps/icu-small/source/common/uscript.cpp index 1ededbb268ae23..4c9b4a65fcc03e 100644 --- a/deps/icu-small/source/common/uscript.cpp +++ b/deps/icu-small/source/common/uscript.cpp @@ -1,149 +1,162 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1997-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* File USCRIPT.C -* -* Modification History: -* -* Date Name Description -* 07/06/2001 Ram Creation. -****************************************************************************** -*/ - -#include "unicode/uchar.h" -#include "unicode/uscript.h" -#include "unicode/uloc.h" -#include "bytesinkutil.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "ulocimp.h" - -static const UScriptCode JAPANESE[3] = { USCRIPT_KATAKANA, USCRIPT_HIRAGANA, USCRIPT_HAN }; -static const UScriptCode KOREAN[2] = { USCRIPT_HANGUL, USCRIPT_HAN }; -static const UScriptCode HAN_BOPO[2] = { USCRIPT_HAN, USCRIPT_BOPOMOFO }; - -static int32_t -setCodes(const UScriptCode *src, int32_t length, - UScriptCode *dest, int32_t capacity, UErrorCode *err) { - int32_t i; - if(U_FAILURE(*err)) { return 0; } - if(length > capacity) { - *err = U_BUFFER_OVERFLOW_ERROR; - return length; - } - for(i = 0; i < length; ++i) { - dest[i] = src[i]; - } - return length; -} - -static int32_t -setOneCode(UScriptCode script, UScriptCode *scripts, int32_t capacity, UErrorCode *err) { - if(U_FAILURE(*err)) { return 0; } - if(1 > capacity) { - *err = U_BUFFER_OVERFLOW_ERROR; - return 1; - } - scripts[0] = script; - return 1; -} - -static int32_t -getCodesFromLocale(const char *locale, - UScriptCode *scripts, int32_t capacity, UErrorCode *err) { - UErrorCode internalErrorCode = U_ZERO_ERROR; - char lang[8] = {0}; - char script[8] = {0}; - int32_t scriptLength; - if(U_FAILURE(*err)) { return 0; } - // Multi-script languages, equivalent to the LocaleScript data - // that we used to load from locale resource bundles. - /*length = */ uloc_getLanguage(locale, lang, UPRV_LENGTHOF(lang), &internalErrorCode); - if(U_FAILURE(internalErrorCode) || internalErrorCode == U_STRING_NOT_TERMINATED_WARNING) { - return 0; - } - if(0 == uprv_strcmp(lang, "ja")) { - return setCodes(JAPANESE, UPRV_LENGTHOF(JAPANESE), scripts, capacity, err); - } - if(0 == uprv_strcmp(lang, "ko")) { - return setCodes(KOREAN, UPRV_LENGTHOF(KOREAN), scripts, capacity, err); - } - scriptLength = uloc_getScript(locale, script, UPRV_LENGTHOF(script), &internalErrorCode); - if(U_FAILURE(internalErrorCode) || internalErrorCode == U_STRING_NOT_TERMINATED_WARNING) { - return 0; - } - if(0 == uprv_strcmp(lang, "zh") && 0 == uprv_strcmp(script, "Hant")) { - return setCodes(HAN_BOPO, UPRV_LENGTHOF(HAN_BOPO), scripts, capacity, err); - } - // Explicit script code. - if(scriptLength != 0) { - UScriptCode scriptCode = (UScriptCode)u_getPropertyValueEnum(UCHAR_SCRIPT, script); - if(scriptCode != USCRIPT_INVALID_CODE) { - if(scriptCode == USCRIPT_SIMPLIFIED_HAN || scriptCode == USCRIPT_TRADITIONAL_HAN) { - scriptCode = USCRIPT_HAN; - } - return setOneCode(scriptCode, scripts, capacity, err); - } - } - return 0; -} - -/* TODO: this is a bad API and should be deprecated, ticket #11141 */ -U_CAPI int32_t U_EXPORT2 -uscript_getCode(const char* nameOrAbbrOrLocale, - UScriptCode* fillIn, - int32_t capacity, - UErrorCode* err){ - UBool triedCode; - UErrorCode internalErrorCode; - int32_t length; - - if(U_FAILURE(*err)) { - return 0; - } - if(nameOrAbbrOrLocale==NULL || - (fillIn == NULL ? capacity != 0 : capacity < 0)) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - triedCode = false; - if(uprv_strchr(nameOrAbbrOrLocale, '-')==NULL && uprv_strchr(nameOrAbbrOrLocale, '_')==NULL ){ - /* try long and abbreviated script names first */ - UScriptCode code = (UScriptCode) u_getPropertyValueEnum(UCHAR_SCRIPT, nameOrAbbrOrLocale); - if(code!=USCRIPT_INVALID_CODE) { - return setOneCode(code, fillIn, capacity, err); - } - triedCode = true; - } - internalErrorCode = U_ZERO_ERROR; - length = getCodesFromLocale(nameOrAbbrOrLocale, fillIn, capacity, err); - if(U_FAILURE(*err) || length != 0) { - return length; - } - icu::CharString likely; - { - icu::CharStringByteSink sink(&likely); - ulocimp_addLikelySubtags(nameOrAbbrOrLocale, sink, &internalErrorCode); - } - if(U_SUCCESS(internalErrorCode) && internalErrorCode != U_STRING_NOT_TERMINATED_WARNING) { - length = getCodesFromLocale(likely.data(), fillIn, capacity, err); - if(U_FAILURE(*err) || length != 0) { - return length; - } - } - if(!triedCode) { - /* still not found .. try long and abbreviated script names again */ - UScriptCode code = (UScriptCode) u_getPropertyValueEnum(UCHAR_SCRIPT, nameOrAbbrOrLocale); - if(code!=USCRIPT_INVALID_CODE) { - return setOneCode(code, fillIn, capacity, err); - } - } - return 0; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1997-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* File USCRIPT.C +* +* Modification History: +* +* Date Name Description +* 07/06/2001 Ram Creation. +****************************************************************************** +*/ + +#include "unicode/uchar.h" +#include "unicode/uscript.h" +#include "unicode/uloc.h" +#include "bytesinkutil.h" +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "ulocimp.h" + +static const UScriptCode JAPANESE[3] = { USCRIPT_KATAKANA, USCRIPT_HIRAGANA, USCRIPT_HAN }; +static const UScriptCode KOREAN[2] = { USCRIPT_HANGUL, USCRIPT_HAN }; +static const UScriptCode HAN_BOPO[2] = { USCRIPT_HAN, USCRIPT_BOPOMOFO }; + +static int32_t +setCodes(const UScriptCode *src, int32_t length, + UScriptCode *dest, int32_t capacity, UErrorCode *err) { + int32_t i; + if(U_FAILURE(*err)) { return 0; } + if(length > capacity) { + *err = U_BUFFER_OVERFLOW_ERROR; + return length; + } + for(i = 0; i < length; ++i) { + dest[i] = src[i]; + } + return length; +} + +static int32_t +setOneCode(UScriptCode script, UScriptCode *scripts, int32_t capacity, UErrorCode *err) { + if(U_FAILURE(*err)) { return 0; } + if(1 > capacity) { + *err = U_BUFFER_OVERFLOW_ERROR; + return 1; + } + scripts[0] = script; + return 1; +} + +static int32_t +getCodesFromLocale(const char *locale, + UScriptCode *scripts, int32_t capacity, UErrorCode *err) { + UErrorCode internalErrorCode = U_ZERO_ERROR; + char lang[8] = {0}; + char script[8] = {0}; + int32_t scriptLength; + if(U_FAILURE(*err)) { return 0; } + // Multi-script languages, equivalent to the LocaleScript data + // that we used to load from locale resource bundles. + /*length = */ uloc_getLanguage(locale, lang, UPRV_LENGTHOF(lang), &internalErrorCode); + if(U_FAILURE(internalErrorCode) || internalErrorCode == U_STRING_NOT_TERMINATED_WARNING) { + return 0; + } + if(0 == uprv_strcmp(lang, "ja")) { + return setCodes(JAPANESE, UPRV_LENGTHOF(JAPANESE), scripts, capacity, err); + } + if(0 == uprv_strcmp(lang, "ko")) { + return setCodes(KOREAN, UPRV_LENGTHOF(KOREAN), scripts, capacity, err); + } + scriptLength = uloc_getScript(locale, script, UPRV_LENGTHOF(script), &internalErrorCode); + if(U_FAILURE(internalErrorCode) || internalErrorCode == U_STRING_NOT_TERMINATED_WARNING) { + return 0; + } + if(0 == uprv_strcmp(lang, "zh") && 0 == uprv_strcmp(script, "Hant")) { + return setCodes(HAN_BOPO, UPRV_LENGTHOF(HAN_BOPO), scripts, capacity, err); + } + // Explicit script code. + if(scriptLength != 0) { + UScriptCode scriptCode = (UScriptCode)u_getPropertyValueEnum(UCHAR_SCRIPT, script); + if(scriptCode != USCRIPT_INVALID_CODE) { + if(scriptCode == USCRIPT_SIMPLIFIED_HAN || scriptCode == USCRIPT_TRADITIONAL_HAN) { + scriptCode = USCRIPT_HAN; + } + return setOneCode(scriptCode, scripts, capacity, err); + } + } + return 0; +} + +/* TODO: this is a bad API and should be deprecated, ticket #11141 */ +U_CAPI int32_t U_EXPORT2 +uscript_getCode(const char* nameOrAbbrOrLocale, + UScriptCode* fillIn, + int32_t capacity, + UErrorCode* err){ + UBool triedCode; + UErrorCode internalErrorCode; + int32_t length; + + if(U_FAILURE(*err)) { + return 0; + } + if(nameOrAbbrOrLocale==nullptr || + (fillIn == nullptr ? capacity != 0 : capacity < 0)) { + *err = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + triedCode = false; + const char* lastSepPtr = uprv_strrchr(nameOrAbbrOrLocale, '-'); + if (lastSepPtr==nullptr) { + lastSepPtr = uprv_strrchr(nameOrAbbrOrLocale, '_'); + } + // Favor interpretation of nameOrAbbrOrLocale as a script alias if either + // 1. nameOrAbbrOrLocale does not contain -/_. Handles Han, Mro, Nko, etc. + // 2. The last instance of -/_ is at offset 3, and the portion after that is + // longer than 4 characters (i.e. not a script or region code). This handles + // Old_Hungarian, Old_Italic, etc. ("old" is a valid language code) + // 3. The last instance of -/_ is at offset 7, and the portion after that is + // 3 characters. This handles New_Tai_Lue ("new" is a valid language code). + if (lastSepPtr==nullptr + || (lastSepPtr-nameOrAbbrOrLocale == 3 && uprv_strlen(nameOrAbbrOrLocale) > 8) + || (lastSepPtr-nameOrAbbrOrLocale == 7 && uprv_strlen(nameOrAbbrOrLocale) == 11) ) { + /* try long and abbreviated script names first */ + UScriptCode code = (UScriptCode) u_getPropertyValueEnum(UCHAR_SCRIPT, nameOrAbbrOrLocale); + if(code!=USCRIPT_INVALID_CODE) { + return setOneCode(code, fillIn, capacity, err); + } + triedCode = true; + } + internalErrorCode = U_ZERO_ERROR; + length = getCodesFromLocale(nameOrAbbrOrLocale, fillIn, capacity, err); + if(U_FAILURE(*err) || length != 0) { + return length; + } + icu::CharString likely; + { + icu::CharStringByteSink sink(&likely); + ulocimp_addLikelySubtags(nameOrAbbrOrLocale, sink, &internalErrorCode); + } + if(U_SUCCESS(internalErrorCode) && internalErrorCode != U_STRING_NOT_TERMINATED_WARNING) { + length = getCodesFromLocale(likely.data(), fillIn, capacity, err); + if(U_FAILURE(*err) || length != 0) { + return length; + } + } + if(!triedCode) { + /* still not found .. try long and abbreviated script names again */ + UScriptCode code = (UScriptCode) u_getPropertyValueEnum(UCHAR_SCRIPT, nameOrAbbrOrLocale); + if(code!=USCRIPT_INVALID_CODE) { + return setOneCode(code, fillIn, capacity, err); + } + } + return 0; +} diff --git a/deps/icu-small/source/common/uscript_props.cpp b/deps/icu-small/source/common/uscript_props.cpp index 886acfafa88366..c2515c73bbf50d 100644 --- a/deps/icu-small/source/common/uscript_props.cpp +++ b/deps/icu-small/source/common/uscript_props.cpp @@ -1,309 +1,309 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2016, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: uscript_props.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2013feb16 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" -#include "unicode/unistr.h" -#include "unicode/uscript.h" -#include "unicode/utf16.h" -#include "ustr_imp.h" -#include "cmemory.h" - -namespace { - -// Script metadata (script properties). -// See http://unicode.org/cldr/trac/browser/trunk/common/properties/scriptMetadata.txt - -// 0 = NOT_ENCODED, no sample character, default false script properties. -// Bits 20.. 0: sample character - -// Bits 23..21: usage -const int32_t UNKNOWN = 1 << 21; -const int32_t EXCLUSION = 2 << 21; -const int32_t LIMITED_USE = 3 << 21; -// st int32_t ASPIRATIONAL = 4 << 21; -- not used any more since Unicode 10 -const int32_t RECOMMENDED = 5 << 21; - -// Bits 31..24: Single-bit flags -const int32_t RTL = 1 << 24; -const int32_t LB_LETTERS = 1 << 25; -const int32_t CASED = 1 << 26; - -const int32_t SCRIPT_PROPS[] = { - // Begin copy-paste output from - // tools/trunk/unicode/py/parsescriptmetadata.py - 0x0040 | RECOMMENDED, // Zyyy - 0x0308 | RECOMMENDED, // Zinh - 0x0628 | RECOMMENDED | RTL, // Arab - 0x0531 | RECOMMENDED | CASED, // Armn - 0x0995 | RECOMMENDED, // Beng - 0x3105 | RECOMMENDED | LB_LETTERS, // Bopo - 0x13C4 | LIMITED_USE | CASED, // Cher - 0x03E2 | EXCLUSION | CASED, // Copt - 0x042F | RECOMMENDED | CASED, // Cyrl - 0x10414 | EXCLUSION | CASED, // Dsrt - 0x0905 | RECOMMENDED, // Deva - 0x12A0 | RECOMMENDED, // Ethi - 0x10D3 | RECOMMENDED, // Geor - 0x10330 | EXCLUSION, // Goth - 0x03A9 | RECOMMENDED | CASED, // Grek - 0x0A95 | RECOMMENDED, // Gujr - 0x0A15 | RECOMMENDED, // Guru - 0x5B57 | RECOMMENDED | LB_LETTERS, // Hani - 0xAC00 | RECOMMENDED, // Hang - 0x05D0 | RECOMMENDED | RTL, // Hebr - 0x304B | RECOMMENDED | LB_LETTERS, // Hira - 0x0C95 | RECOMMENDED, // Knda - 0x30AB | RECOMMENDED | LB_LETTERS, // Kana - 0x1780 | RECOMMENDED | LB_LETTERS, // Khmr - 0x0EA5 | RECOMMENDED | LB_LETTERS, // Laoo - 0x004C | RECOMMENDED | CASED, // Latn - 0x0D15 | RECOMMENDED, // Mlym - 0x1826 | EXCLUSION, // Mong - 0x1000 | RECOMMENDED | LB_LETTERS, // Mymr - 0x168F | EXCLUSION, // Ogam - 0x10300 | EXCLUSION, // Ital - 0x0B15 | RECOMMENDED, // Orya - 0x16A0 | EXCLUSION, // Runr - 0x0D85 | RECOMMENDED, // Sinh - 0x0710 | LIMITED_USE | RTL, // Syrc - 0x0B95 | RECOMMENDED, // Taml - 0x0C15 | RECOMMENDED, // Telu - 0x078C | RECOMMENDED | RTL, // Thaa - 0x0E17 | RECOMMENDED | LB_LETTERS, // Thai - 0x0F40 | RECOMMENDED, // Tibt - 0x14C0 | LIMITED_USE, // Cans - 0xA288 | LIMITED_USE | LB_LETTERS, // Yiii - 0x1703 | EXCLUSION, // Tglg - 0x1723 | EXCLUSION, // Hano - 0x1743 | EXCLUSION, // Buhd - 0x1763 | EXCLUSION, // Tagb - 0x280E | UNKNOWN, // Brai - 0x10800 | EXCLUSION | RTL, // Cprt - 0x1900 | LIMITED_USE, // Limb - 0x10000 | EXCLUSION, // Linb - 0x10480 | EXCLUSION, // Osma - 0x10450 | EXCLUSION, // Shaw - 0x1950 | LIMITED_USE | LB_LETTERS, // Tale - 0x10380 | EXCLUSION, // Ugar - 0, - 0x1A00 | EXCLUSION, // Bugi - 0x2C00 | EXCLUSION | CASED, // Glag - 0x10A00 | EXCLUSION | RTL, // Khar - 0xA800 | LIMITED_USE, // Sylo - 0x1980 | LIMITED_USE | LB_LETTERS, // Talu - 0x2D30 | LIMITED_USE, // Tfng - 0x103A0 | EXCLUSION, // Xpeo - 0x1B05 | LIMITED_USE, // Bali - 0x1BC0 | LIMITED_USE, // Batk - 0, - 0x11005 | EXCLUSION, // Brah - 0xAA00 | LIMITED_USE, // Cham - 0, - 0, - 0, - 0, - 0x13153 | EXCLUSION, // Egyp - 0, - 0x5B57 | RECOMMENDED | LB_LETTERS, // Hans - 0x5B57 | RECOMMENDED | LB_LETTERS, // Hant - 0x16B1C | EXCLUSION, // Hmng - 0x10CA1 | EXCLUSION | RTL | CASED, // Hung - 0, - 0xA984 | LIMITED_USE, // Java - 0xA90A | LIMITED_USE, // Kali - 0, - 0, - 0x1C00 | LIMITED_USE, // Lepc - 0x10647 | EXCLUSION, // Lina - 0x0840 | LIMITED_USE | RTL, // Mand - 0, - 0x10980 | EXCLUSION | RTL, // Mero - 0x07CA | LIMITED_USE | RTL, // Nkoo - 0x10C00 | EXCLUSION | RTL, // Orkh - 0x1036B | EXCLUSION, // Perm - 0xA840 | EXCLUSION, // Phag - 0x10900 | EXCLUSION | RTL, // Phnx - 0x16F00 | LIMITED_USE, // Plrd - 0, - 0, - 0, - 0, - 0, - 0, - 0xA549 | LIMITED_USE, // Vaii - 0, - 0x12000 | EXCLUSION, // Xsux - 0, - 0xFDD0 | UNKNOWN, // Zzzz - 0x102A0 | EXCLUSION, // Cari - 0x304B | RECOMMENDED | LB_LETTERS, // Jpan - 0x1A20 | LIMITED_USE | LB_LETTERS, // Lana - 0x10280 | EXCLUSION, // Lyci - 0x10920 | EXCLUSION | RTL, // Lydi - 0x1C5A | LIMITED_USE, // Olck - 0xA930 | EXCLUSION, // Rjng - 0xA882 | LIMITED_USE, // Saur - 0x1D850 | EXCLUSION, // Sgnw - 0x1B83 | LIMITED_USE, // Sund - 0, - 0xABC0 | LIMITED_USE, // Mtei - 0x10840 | EXCLUSION | RTL, // Armi - 0x10B00 | EXCLUSION | RTL, // Avst - 0x11103 | LIMITED_USE, // Cakm - 0xAC00 | RECOMMENDED, // Kore - 0x11083 | EXCLUSION, // Kthi - 0x10AD8 | EXCLUSION | RTL, // Mani - 0x10B60 | EXCLUSION | RTL, // Phli - 0x10B8F | EXCLUSION | RTL, // Phlp - 0, - 0x10B40 | EXCLUSION | RTL, // Prti - 0x0800 | EXCLUSION | RTL, // Samr - 0xAA80 | LIMITED_USE | LB_LETTERS, // Tavt - 0, - 0, - 0xA6A0 | LIMITED_USE, // Bamu - 0xA4D0 | LIMITED_USE, // Lisu - 0, - 0x10A60 | EXCLUSION | RTL, // Sarb - 0x16AE6 | EXCLUSION, // Bass - 0x1BC20 | EXCLUSION, // Dupl - 0x10500 | EXCLUSION, // Elba - 0x11315 | EXCLUSION, // Gran - 0, - 0, - 0x1E802 | EXCLUSION | RTL, // Mend - 0x109A0 | EXCLUSION | RTL, // Merc - 0x10A95 | EXCLUSION | RTL, // Narb - 0x10896 | EXCLUSION | RTL, // Nbat - 0x10873 | EXCLUSION | RTL, // Palm - 0x112BE | EXCLUSION, // Sind - 0x118B4 | EXCLUSION | CASED, // Wara - 0, - 0, - 0x16A4F | EXCLUSION, // Mroo - 0x1B1C4 | EXCLUSION | LB_LETTERS, // Nshu - 0x11183 | EXCLUSION, // Shrd - 0x110D0 | EXCLUSION, // Sora - 0x11680 | EXCLUSION, // Takr - 0x18229 | EXCLUSION | LB_LETTERS, // Tang - 0, - 0x14400 | EXCLUSION, // Hluw - 0x11208 | EXCLUSION, // Khoj - 0x11484 | EXCLUSION, // Tirh - 0x10537 | EXCLUSION, // Aghb - 0x11152 | EXCLUSION, // Mahj - 0x11717 | EXCLUSION | LB_LETTERS, // Ahom - 0x108F4 | EXCLUSION | RTL, // Hatr - 0x1160E | EXCLUSION, // Modi - 0x1128F | EXCLUSION, // Mult - 0x11AC0 | EXCLUSION, // Pauc - 0x1158E | EXCLUSION, // Sidd - 0x1E909 | LIMITED_USE | RTL | CASED, // Adlm - 0x11C0E | EXCLUSION, // Bhks - 0x11C72 | EXCLUSION, // Marc - 0x11412 | LIMITED_USE, // Newa - 0x104B5 | LIMITED_USE | CASED, // Osge - 0x5B57 | RECOMMENDED | LB_LETTERS, // Hanb - 0x1112 | RECOMMENDED, // Jamo - 0, - 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 - 0x10FF1 | EXCLUSION | RTL, // Elym - 0x1E108 | LIMITED_USE, // Hmnp - 0x119CE | EXCLUSION, // Nand - 0x1E2E1 | LIMITED_USE, // Wcho - 0x10FBF | EXCLUSION | RTL, // Chrs - 0x1190C | EXCLUSION, // Diak - 0x18C65 | EXCLUSION | LB_LETTERS, // Kits - 0x10E88 | EXCLUSION | RTL, // Yezi - 0x12FE5 | EXCLUSION, // Cpmn - 0x10F7C | EXCLUSION | RTL, // Ougr - 0x16ABC | EXCLUSION, // Tnsa - 0x1E290 | EXCLUSION, // Toto - 0x10582 | EXCLUSION | CASED, // Vith - 0x11F1B | EXCLUSION | LB_LETTERS, // Kawi - 0x1E4E6 | EXCLUSION, // Nagm - // End copy-paste from parsescriptmetadata.py -}; - -int32_t getScriptProps(UScriptCode script) { - if (0 <= script && script < UPRV_LENGTHOF(SCRIPT_PROPS)) { - return SCRIPT_PROPS[script]; - } else { - return 0; - } -} - -} // namespace - -U_CAPI int32_t U_EXPORT2 -uscript_getSampleString(UScriptCode script, UChar *dest, int32_t capacity, UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { return 0; } - if(capacity < 0 || (capacity > 0 && dest == NULL)) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - int32_t sampleChar = getScriptProps(script) & 0x1fffff; - int32_t length; - if(sampleChar == 0) { - length = 0; - } else { - length = U16_LENGTH(sampleChar); - if(length <= capacity) { - int32_t i = 0; - U16_APPEND_UNSAFE(dest, i, sampleChar); - } - } - return u_terminateUChars(dest, capacity, length, pErrorCode); -} - -U_COMMON_API icu::UnicodeString U_EXPORT2 -uscript_getSampleUnicodeString(UScriptCode script) { - icu::UnicodeString sample; - int32_t sampleChar = getScriptProps(script) & 0x1fffff; - if(sampleChar != 0) { - sample.append(sampleChar); - } - return sample; -} - -U_CAPI UScriptUsage U_EXPORT2 -uscript_getUsage(UScriptCode script) { - return (UScriptUsage)((getScriptProps(script) >> 21) & 7); -} - -U_CAPI UBool U_EXPORT2 -uscript_isRightToLeft(UScriptCode script) { - return (getScriptProps(script) & RTL) != 0; -} - -U_CAPI UBool U_EXPORT2 -uscript_breaksBetweenLetters(UScriptCode script) { - return (getScriptProps(script) & LB_LETTERS) != 0; -} - -U_CAPI UBool U_EXPORT2 -uscript_isCased(UScriptCode script) { - return (getScriptProps(script) & CASED) != 0; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2016, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: uscript_props.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2013feb16 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" +#include "unicode/unistr.h" +#include "unicode/uscript.h" +#include "unicode/utf16.h" +#include "ustr_imp.h" +#include "cmemory.h" + +namespace { + +// Script metadata (script properties). +// See http://unicode.org/cldr/trac/browser/trunk/common/properties/scriptMetadata.txt + +// 0 = NOT_ENCODED, no sample character, default false script properties. +// Bits 20.. 0: sample character + +// Bits 23..21: usage +const int32_t UNKNOWN = 1 << 21; +const int32_t EXCLUSION = 2 << 21; +const int32_t LIMITED_USE = 3 << 21; +// st int32_t ASPIRATIONAL = 4 << 21; -- not used any more since Unicode 10 +const int32_t RECOMMENDED = 5 << 21; + +// Bits 31..24: Single-bit flags +const int32_t RTL = 1 << 24; +const int32_t LB_LETTERS = 1 << 25; +const int32_t CASED = 1 << 26; + +const int32_t SCRIPT_PROPS[] = { + // Begin copy-paste output from + // tools/trunk/unicode/py/parsescriptmetadata.py + 0x0040 | RECOMMENDED, // Zyyy + 0x0308 | RECOMMENDED, // Zinh + 0x0628 | RECOMMENDED | RTL, // Arab + 0x0531 | RECOMMENDED | CASED, // Armn + 0x0995 | RECOMMENDED, // Beng + 0x3105 | RECOMMENDED | LB_LETTERS, // Bopo + 0x13C4 | LIMITED_USE | CASED, // Cher + 0x03E2 | EXCLUSION | CASED, // Copt + 0x042F | RECOMMENDED | CASED, // Cyrl + 0x10414 | EXCLUSION | CASED, // Dsrt + 0x0905 | RECOMMENDED, // Deva + 0x12A0 | RECOMMENDED, // Ethi + 0x10D3 | RECOMMENDED, // Geor + 0x10330 | EXCLUSION, // Goth + 0x03A9 | RECOMMENDED | CASED, // Grek + 0x0A95 | RECOMMENDED, // Gujr + 0x0A15 | RECOMMENDED, // Guru + 0x5B57 | RECOMMENDED | LB_LETTERS, // Hani + 0xAC00 | RECOMMENDED, // Hang + 0x05D0 | RECOMMENDED | RTL, // Hebr + 0x304B | RECOMMENDED | LB_LETTERS, // Hira + 0x0C95 | RECOMMENDED, // Knda + 0x30AB | RECOMMENDED | LB_LETTERS, // Kana + 0x1780 | RECOMMENDED | LB_LETTERS, // Khmr + 0x0EA5 | RECOMMENDED | LB_LETTERS, // Laoo + 0x004C | RECOMMENDED | CASED, // Latn + 0x0D15 | RECOMMENDED, // Mlym + 0x1826 | EXCLUSION, // Mong + 0x1000 | RECOMMENDED | LB_LETTERS, // Mymr + 0x168F | EXCLUSION, // Ogam + 0x10300 | EXCLUSION, // Ital + 0x0B15 | RECOMMENDED, // Orya + 0x16A0 | EXCLUSION, // Runr + 0x0D85 | RECOMMENDED, // Sinh + 0x0710 | LIMITED_USE | RTL, // Syrc + 0x0B95 | RECOMMENDED, // Taml + 0x0C15 | RECOMMENDED, // Telu + 0x078C | RECOMMENDED | RTL, // Thaa + 0x0E17 | RECOMMENDED | LB_LETTERS, // Thai + 0x0F40 | RECOMMENDED, // Tibt + 0x14C0 | LIMITED_USE, // Cans + 0xA288 | LIMITED_USE | LB_LETTERS, // Yiii + 0x1703 | EXCLUSION, // Tglg + 0x1723 | EXCLUSION, // Hano + 0x1743 | EXCLUSION, // Buhd + 0x1763 | EXCLUSION, // Tagb + 0x280E | UNKNOWN, // Brai + 0x10800 | EXCLUSION | RTL, // Cprt + 0x1900 | LIMITED_USE, // Limb + 0x10000 | EXCLUSION, // Linb + 0x10480 | EXCLUSION, // Osma + 0x10450 | EXCLUSION, // Shaw + 0x1950 | LIMITED_USE | LB_LETTERS, // Tale + 0x10380 | EXCLUSION, // Ugar + 0, + 0x1A00 | EXCLUSION, // Bugi + 0x2C00 | EXCLUSION | CASED, // Glag + 0x10A00 | EXCLUSION | RTL, // Khar + 0xA800 | LIMITED_USE, // Sylo + 0x1980 | LIMITED_USE | LB_LETTERS, // Talu + 0x2D30 | LIMITED_USE, // Tfng + 0x103A0 | EXCLUSION, // Xpeo + 0x1B05 | LIMITED_USE, // Bali + 0x1BC0 | LIMITED_USE, // Batk + 0, + 0x11005 | EXCLUSION, // Brah + 0xAA00 | LIMITED_USE, // Cham + 0, + 0, + 0, + 0, + 0x13153 | EXCLUSION, // Egyp + 0, + 0x5B57 | RECOMMENDED | LB_LETTERS, // Hans + 0x5B57 | RECOMMENDED | LB_LETTERS, // Hant + 0x16B1C | EXCLUSION, // Hmng + 0x10CA1 | EXCLUSION | RTL | CASED, // Hung + 0, + 0xA984 | LIMITED_USE, // Java + 0xA90A | LIMITED_USE, // Kali + 0, + 0, + 0x1C00 | LIMITED_USE, // Lepc + 0x10647 | EXCLUSION, // Lina + 0x0840 | LIMITED_USE | RTL, // Mand + 0, + 0x10980 | EXCLUSION | RTL, // Mero + 0x07CA | LIMITED_USE | RTL, // Nkoo + 0x10C00 | EXCLUSION | RTL, // Orkh + 0x1036B | EXCLUSION, // Perm + 0xA840 | EXCLUSION, // Phag + 0x10900 | EXCLUSION | RTL, // Phnx + 0x16F00 | LIMITED_USE, // Plrd + 0, + 0, + 0, + 0, + 0, + 0, + 0xA549 | LIMITED_USE, // Vaii + 0, + 0x12000 | EXCLUSION, // Xsux + 0, + 0xFDD0 | UNKNOWN, // Zzzz + 0x102A0 | EXCLUSION, // Cari + 0x304B | RECOMMENDED | LB_LETTERS, // Jpan + 0x1A20 | LIMITED_USE | LB_LETTERS, // Lana + 0x10280 | EXCLUSION, // Lyci + 0x10920 | EXCLUSION | RTL, // Lydi + 0x1C5A | LIMITED_USE, // Olck + 0xA930 | EXCLUSION, // Rjng + 0xA882 | LIMITED_USE, // Saur + 0x1D850 | EXCLUSION, // Sgnw + 0x1B83 | LIMITED_USE, // Sund + 0, + 0xABC0 | LIMITED_USE, // Mtei + 0x10840 | EXCLUSION | RTL, // Armi + 0x10B00 | EXCLUSION | RTL, // Avst + 0x11103 | LIMITED_USE, // Cakm + 0xAC00 | RECOMMENDED, // Kore + 0x11083 | EXCLUSION, // Kthi + 0x10AD8 | EXCLUSION | RTL, // Mani + 0x10B60 | EXCLUSION | RTL, // Phli + 0x10B8F | EXCLUSION | RTL, // Phlp + 0, + 0x10B40 | EXCLUSION | RTL, // Prti + 0x0800 | EXCLUSION | RTL, // Samr + 0xAA80 | LIMITED_USE | LB_LETTERS, // Tavt + 0, + 0, + 0xA6A0 | LIMITED_USE, // Bamu + 0xA4D0 | LIMITED_USE, // Lisu + 0, + 0x10A60 | EXCLUSION | RTL, // Sarb + 0x16AE6 | EXCLUSION, // Bass + 0x1BC20 | EXCLUSION, // Dupl + 0x10500 | EXCLUSION, // Elba + 0x11315 | EXCLUSION, // Gran + 0, + 0, + 0x1E802 | EXCLUSION | RTL, // Mend + 0x109A0 | EXCLUSION | RTL, // Merc + 0x10A95 | EXCLUSION | RTL, // Narb + 0x10896 | EXCLUSION | RTL, // Nbat + 0x10873 | EXCLUSION | RTL, // Palm + 0x112BE | EXCLUSION, // Sind + 0x118B4 | EXCLUSION | CASED, // Wara + 0, + 0, + 0x16A4F | EXCLUSION, // Mroo + 0x1B1C4 | EXCLUSION | LB_LETTERS, // Nshu + 0x11183 | EXCLUSION, // Shrd + 0x110D0 | EXCLUSION, // Sora + 0x11680 | EXCLUSION, // Takr + 0x18229 | EXCLUSION | LB_LETTERS, // Tang + 0, + 0x14400 | EXCLUSION, // Hluw + 0x11208 | EXCLUSION, // Khoj + 0x11484 | EXCLUSION, // Tirh + 0x10537 | EXCLUSION, // Aghb + 0x11152 | EXCLUSION, // Mahj + 0x11717 | EXCLUSION | LB_LETTERS, // Ahom + 0x108F4 | EXCLUSION | RTL, // Hatr + 0x1160E | EXCLUSION, // Modi + 0x1128F | EXCLUSION, // Mult + 0x11AC0 | EXCLUSION, // Pauc + 0x1158E | EXCLUSION, // Sidd + 0x1E909 | LIMITED_USE | RTL | CASED, // Adlm + 0x11C0E | EXCLUSION, // Bhks + 0x11C72 | EXCLUSION, // Marc + 0x11412 | LIMITED_USE, // Newa + 0x104B5 | LIMITED_USE | CASED, // Osge + 0x5B57 | RECOMMENDED | LB_LETTERS, // Hanb + 0x1112 | RECOMMENDED, // Jamo + 0, + 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 + 0x10FF1 | EXCLUSION | RTL, // Elym + 0x1E108 | LIMITED_USE, // Hmnp + 0x119CE | EXCLUSION, // Nand + 0x1E2E1 | LIMITED_USE, // Wcho + 0x10FBF | EXCLUSION | RTL, // Chrs + 0x1190C | EXCLUSION, // Diak + 0x18C65 | EXCLUSION | LB_LETTERS, // Kits + 0x10E88 | EXCLUSION | RTL, // Yezi + 0x12FE5 | EXCLUSION, // Cpmn + 0x10F7C | EXCLUSION | RTL, // Ougr + 0x16ABC | EXCLUSION, // Tnsa + 0x1E290 | EXCLUSION, // Toto + 0x10582 | EXCLUSION | CASED, // Vith + 0x11F1B | EXCLUSION | LB_LETTERS, // Kawi + 0x1E4E6 | EXCLUSION, // Nagm + // End copy-paste from parsescriptmetadata.py +}; + +int32_t getScriptProps(UScriptCode script) { + if (0 <= script && script < UPRV_LENGTHOF(SCRIPT_PROPS)) { + return SCRIPT_PROPS[script]; + } else { + return 0; + } +} + +} // namespace + +U_CAPI int32_t U_EXPORT2 +uscript_getSampleString(UScriptCode script, char16_t *dest, int32_t capacity, UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { return 0; } + if(capacity < 0 || (capacity > 0 && dest == nullptr)) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + int32_t sampleChar = getScriptProps(script) & 0x1fffff; + int32_t length; + if(sampleChar == 0) { + length = 0; + } else { + length = U16_LENGTH(sampleChar); + if(length <= capacity) { + int32_t i = 0; + U16_APPEND_UNSAFE(dest, i, sampleChar); + } + } + return u_terminateUChars(dest, capacity, length, pErrorCode); +} + +U_COMMON_API icu::UnicodeString U_EXPORT2 +uscript_getSampleUnicodeString(UScriptCode script) { + icu::UnicodeString sample; + int32_t sampleChar = getScriptProps(script) & 0x1fffff; + if(sampleChar != 0) { + sample.append(sampleChar); + } + return sample; +} + +U_CAPI UScriptUsage U_EXPORT2 +uscript_getUsage(UScriptCode script) { + return (UScriptUsage)((getScriptProps(script) >> 21) & 7); +} + +U_CAPI UBool U_EXPORT2 +uscript_isRightToLeft(UScriptCode script) { + return (getScriptProps(script) & RTL) != 0; +} + +U_CAPI UBool U_EXPORT2 +uscript_breaksBetweenLetters(UScriptCode script) { + return (getScriptProps(script) & LB_LETTERS) != 0; +} + +U_CAPI UBool U_EXPORT2 +uscript_isCased(UScriptCode script) { + return (getScriptProps(script) & CASED) != 0; +} diff --git a/deps/icu-small/source/common/uset.cpp b/deps/icu-small/source/common/uset.cpp index 2152693560b324..edff2b822cb5bd 100644 --- a/deps/icu-small/source/common/uset.cpp +++ b/deps/icu-small/source/common/uset.cpp @@ -1,681 +1,681 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uset.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002mar07 -* created by: Markus W. Scherer -* -* There are functions to efficiently serialize a USet into an array of uint16_t -* and functions to use such a serialized form efficiently without -* instantiating a new USet. -*/ - -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "unicode/uset.h" -#include "unicode/uniset.h" -#include "cmemory.h" -#include "unicode/ustring.h" -#include "unicode/parsepos.h" - -U_NAMESPACE_USE - -U_CAPI USet* U_EXPORT2 -uset_openEmpty() { - return (USet*) new UnicodeSet(); -} - -U_CAPI USet* U_EXPORT2 -uset_open(UChar32 start, UChar32 end) { - return (USet*) new UnicodeSet(start, end); -} - -U_CAPI void U_EXPORT2 -uset_close(USet* set) { - delete (UnicodeSet*) set; -} - -U_CAPI USet * U_EXPORT2 -uset_clone(const USet *set) { - return (USet*) (((UnicodeSet*) set)->UnicodeSet::clone()); -} - -U_CAPI UBool U_EXPORT2 -uset_isFrozen(const USet *set) { - return ((UnicodeSet*) set)->UnicodeSet::isFrozen(); -} - -U_CAPI void U_EXPORT2 -uset_freeze(USet *set) { - ((UnicodeSet*) set)->UnicodeSet::freeze(); -} - -U_CAPI USet * U_EXPORT2 -uset_cloneAsThawed(const USet *set) { - return (USet*) (((UnicodeSet*) set)->UnicodeSet::cloneAsThawed()); -} - -U_CAPI void U_EXPORT2 -uset_set(USet* set, - UChar32 start, UChar32 end) { - ((UnicodeSet*) set)->UnicodeSet::set(start, end); -} - -U_CAPI void U_EXPORT2 -uset_addAll(USet* set, const USet *additionalSet) { - ((UnicodeSet*) set)->UnicodeSet::addAll(*((const UnicodeSet*)additionalSet)); -} - -U_CAPI void U_EXPORT2 -uset_add(USet* set, UChar32 c) { - ((UnicodeSet*) set)->UnicodeSet::add(c); -} - -U_CAPI void U_EXPORT2 -uset_addRange(USet* set, UChar32 start, UChar32 end) { - ((UnicodeSet*) set)->UnicodeSet::add(start, end); -} - -U_CAPI void U_EXPORT2 -uset_addString(USet* set, const UChar* str, int32_t strLen) { - // UnicodeString handles -1 for strLen - UnicodeString s(strLen<0, str, strLen); - ((UnicodeSet*) set)->UnicodeSet::add(s); -} - -U_CAPI void U_EXPORT2 -uset_addAllCodePoints(USet* set, const UChar *str, int32_t strLen) { - // UnicodeString handles -1 for strLen - UnicodeString s(str, strLen); - ((UnicodeSet*) set)->UnicodeSet::addAll(s); -} - -U_CAPI void U_EXPORT2 -uset_remove(USet* set, UChar32 c) { - ((UnicodeSet*) set)->UnicodeSet::remove(c); -} - -U_CAPI void U_EXPORT2 -uset_removeRange(USet* set, UChar32 start, UChar32 end) { - ((UnicodeSet*) set)->UnicodeSet::remove(start, end); -} - -U_CAPI void U_EXPORT2 -uset_removeString(USet* set, const UChar* str, int32_t strLen) { - UnicodeString s(strLen==-1, str, strLen); - ((UnicodeSet*) set)->UnicodeSet::remove(s); -} - -U_CAPI void U_EXPORT2 -uset_removeAllCodePoints(USet *set, const UChar *str, int32_t length) { - UnicodeString s(length==-1, str, length); - ((UnicodeSet*) set)->UnicodeSet::removeAll(s); -} - -U_CAPI void U_EXPORT2 -uset_removeAll(USet* set, const USet* remove) { - ((UnicodeSet*) set)->UnicodeSet::removeAll(*(const UnicodeSet*)remove); -} - -U_CAPI void U_EXPORT2 -uset_retain(USet* set, UChar32 start, UChar32 end) { - ((UnicodeSet*) set)->UnicodeSet::retain(start, end); -} - -U_CAPI void U_EXPORT2 -uset_retainString(USet *set, const UChar *str, int32_t length) { - UnicodeString s(length==-1, str, length); - ((UnicodeSet*) set)->UnicodeSet::retain(s); -} - -U_CAPI void U_EXPORT2 -uset_retainAllCodePoints(USet *set, const UChar *str, int32_t length) { - UnicodeString s(length==-1, str, length); - ((UnicodeSet*) set)->UnicodeSet::retainAll(s); -} - -U_CAPI void U_EXPORT2 -uset_retainAll(USet* set, const USet* retain) { - ((UnicodeSet*) set)->UnicodeSet::retainAll(*(const UnicodeSet*)retain); -} - -U_CAPI void U_EXPORT2 -uset_compact(USet* set) { - ((UnicodeSet*) set)->UnicodeSet::compact(); -} - -U_CAPI void U_EXPORT2 -uset_complement(USet* set) { - ((UnicodeSet*) set)->UnicodeSet::complement(); -} - -U_CAPI void U_EXPORT2 -uset_complementRange(USet *set, UChar32 start, UChar32 end) { - ((UnicodeSet*) set)->UnicodeSet::complement(start, end); -} - -U_CAPI void U_EXPORT2 -uset_complementString(USet *set, const UChar *str, int32_t length) { - UnicodeString s(length==-1, str, length); - ((UnicodeSet*) set)->UnicodeSet::complement(s); -} - -U_CAPI void U_EXPORT2 -uset_complementAllCodePoints(USet *set, const UChar *str, int32_t length) { - UnicodeString s(length==-1, str, length); - ((UnicodeSet*) set)->UnicodeSet::complementAll(s); -} - -U_CAPI void U_EXPORT2 -uset_complementAll(USet* set, const USet* complement) { - ((UnicodeSet*) set)->UnicodeSet::complementAll(*(const UnicodeSet*)complement); -} - -U_CAPI void U_EXPORT2 -uset_clear(USet* set) { - ((UnicodeSet*) set)->UnicodeSet::clear(); -} - -U_CAPI void U_EXPORT2 -uset_removeAllStrings(USet* set) { - ((UnicodeSet*) set)->UnicodeSet::removeAllStrings(); -} - -U_CAPI UBool U_EXPORT2 -uset_isEmpty(const USet* set) { - return ((const UnicodeSet*) set)->UnicodeSet::isEmpty(); -} - -U_CAPI UBool U_EXPORT2 -uset_hasStrings(const USet* set) { - return ((const UnicodeSet*) set)->UnicodeSet::hasStrings(); -} - -U_CAPI UBool U_EXPORT2 -uset_contains(const USet* set, UChar32 c) { - return ((const UnicodeSet*) set)->UnicodeSet::contains(c); -} - -U_CAPI UBool U_EXPORT2 -uset_containsRange(const USet* set, UChar32 start, UChar32 end) { - return ((const UnicodeSet*) set)->UnicodeSet::contains(start, end); -} - -U_CAPI UBool U_EXPORT2 -uset_containsString(const USet* set, const UChar* str, int32_t strLen) { - UnicodeString s(strLen==-1, str, strLen); - return ((const UnicodeSet*) set)->UnicodeSet::contains(s); -} - -U_CAPI UBool U_EXPORT2 -uset_containsAll(const USet* set1, const USet* set2) { - return ((const UnicodeSet*) set1)->UnicodeSet::containsAll(* (const UnicodeSet*) set2); -} - -U_CAPI UBool U_EXPORT2 -uset_containsAllCodePoints(const USet* set, const UChar *str, int32_t strLen) { - // Create a string alias, since nothing is being added to the set. - UnicodeString s(strLen==-1, str, strLen); - return ((const UnicodeSet*) set)->UnicodeSet::containsAll(s); -} - -U_CAPI UBool U_EXPORT2 -uset_containsNone(const USet* set1, const USet* set2) { - return ((const UnicodeSet*) set1)->UnicodeSet::containsNone(* (const UnicodeSet*) set2); -} - -U_CAPI UBool U_EXPORT2 -uset_containsSome(const USet* set1, const USet* set2) { - return ((const UnicodeSet*) set1)->UnicodeSet::containsSome(* (const UnicodeSet*) set2); -} - -U_CAPI int32_t U_EXPORT2 -uset_span(const USet *set, const UChar *s, int32_t length, USetSpanCondition spanCondition) { - return ((UnicodeSet*) set)->UnicodeSet::span(s, length, spanCondition); -} - -U_CAPI int32_t U_EXPORT2 -uset_spanBack(const USet *set, const UChar *s, int32_t length, USetSpanCondition spanCondition) { - return ((UnicodeSet*) set)->UnicodeSet::spanBack(s, length, spanCondition); -} - -U_CAPI int32_t U_EXPORT2 -uset_spanUTF8(const USet *set, const char *s, int32_t length, USetSpanCondition spanCondition) { - return ((UnicodeSet*) set)->UnicodeSet::spanUTF8(s, length, spanCondition); -} - -U_CAPI int32_t U_EXPORT2 -uset_spanBackUTF8(const USet *set, const char *s, int32_t length, USetSpanCondition spanCondition) { - return ((UnicodeSet*) set)->UnicodeSet::spanBackUTF8(s, length, spanCondition); -} - -U_CAPI UBool U_EXPORT2 -uset_equals(const USet* set1, const USet* set2) { - return *(const UnicodeSet*)set1 == *(const UnicodeSet*)set2; -} - -U_CAPI int32_t U_EXPORT2 -uset_indexOf(const USet* set, UChar32 c) { - return ((UnicodeSet*) set)->UnicodeSet::indexOf(c); -} - -U_CAPI UChar32 U_EXPORT2 -uset_charAt(const USet* set, int32_t index) { - return ((UnicodeSet*) set)->UnicodeSet::charAt(index); -} - -U_CAPI int32_t U_EXPORT2 -uset_size(const USet* set) { - return ((const UnicodeSet*) set)->UnicodeSet::size(); -} - -U_NAMESPACE_BEGIN -/** - * This class only exists to provide access to the UnicodeSet private - * USet support API. Declaring a class a friend is more portable than - * trying to declare extern "C" functions as friends. - */ -class USetAccess /* not : public UObject because all methods are static */ { -public: - /* Try to have the compiler inline these*/ - inline static int32_t getStringCount(const UnicodeSet& set) { - return set.stringsSize(); - } - inline static const UnicodeString* getString(const UnicodeSet& set, - int32_t i) { - return set.getString(i); - } -private: - /* do not instantiate*/ - USetAccess(); -}; -U_NAMESPACE_END - -U_CAPI int32_t U_EXPORT2 -uset_getRangeCount(const USet *set) { - return ((const UnicodeSet *)set)->UnicodeSet::getRangeCount(); -} - -U_CAPI int32_t U_EXPORT2 -uset_getItemCount(const USet* uset) { - const UnicodeSet& set = *(const UnicodeSet*)uset; - return set.getRangeCount() + USetAccess::getStringCount(set); -} - -U_CAPI int32_t U_EXPORT2 -uset_getItem(const USet* uset, int32_t itemIndex, - UChar32* start, UChar32* end, - UChar* str, int32_t strCapacity, - UErrorCode* ec) { - if (U_FAILURE(*ec)) return 0; - const UnicodeSet& set = *(const UnicodeSet*)uset; - int32_t rangeCount; - - if (itemIndex < 0) { - *ec = U_ILLEGAL_ARGUMENT_ERROR; - return -1; - } else if (itemIndex < (rangeCount = set.getRangeCount())) { - *start = set.getRangeStart(itemIndex); - *end = set.getRangeEnd(itemIndex); - return 0; - } else { - itemIndex -= rangeCount; - if (itemIndex < USetAccess::getStringCount(set)) { - const UnicodeString* s = USetAccess::getString(set, itemIndex); - return s->extract(str, strCapacity, *ec); - } else { - *ec = U_INDEX_OUTOFBOUNDS_ERROR; - return -1; - } - } -} - -//U_CAPI UBool U_EXPORT2 -//uset_getRange(const USet* set, int32_t rangeIndex, -// UChar32* pStart, UChar32* pEnd) { -// if ((uint32_t) rangeIndex >= (uint32_t) uset_getRangeCount(set)) { -// return false; -// } -// const UnicodeSet* us = (const UnicodeSet*) set; -// *pStart = us->getRangeStart(rangeIndex); -// *pEnd = us->getRangeEnd(rangeIndex); -// return true; -//} - -/* - * Serialize a USet into 16-bit units. - * Store BMP code points as themselves with one 16-bit unit each. - * - * Important: the code points in the array are in ascending order, - * therefore all BMP code points precede all supplementary code points. - * - * Store each supplementary code point in 2 16-bit units, - * simply with higher-then-lower 16-bit halves. - * - * Precede the entire list with the length. - * If there are supplementary code points, then set bit 15 in the length - * and add the bmpLength between it and the array. - * - * In other words: - * - all BMP: (length=bmpLength) BMP, .., BMP - * - some supplementary: (length|0x8000) (bmpLengthUnicodeSet::serialize(dest, destCapacity,* ec); -} - -U_CAPI UBool U_EXPORT2 -uset_getSerializedSet(USerializedSet* fillSet, const uint16_t* src, int32_t srcLength) { - int32_t length; - - if(fillSet==NULL) { - return false; - } - if(src==NULL || srcLength<=0) { - fillSet->length=fillSet->bmpLength=0; - return false; - } - - length=*src++; - if(length&0x8000) { - /* there are supplementary values */ - length&=0x7fff; - if(srcLength<(2+length)) { - fillSet->length=fillSet->bmpLength=0; - return false; - } - fillSet->bmpLength=*src++; - } else { - /* only BMP values */ - if(srcLength<(1+length)) { - fillSet->length=fillSet->bmpLength=0; - return false; - } - fillSet->bmpLength=length; - } - fillSet->array=src; - fillSet->length=length; - return true; -} - -U_CAPI void U_EXPORT2 -uset_setSerializedToOne(USerializedSet* fillSet, UChar32 c) { - if(fillSet==NULL || (uint32_t)c>0x10ffff) { - return; - } - - fillSet->array=fillSet->staticArray; - if(c<0xffff) { - fillSet->bmpLength=fillSet->length=2; - fillSet->staticArray[0]=(uint16_t)c; - fillSet->staticArray[1]=(uint16_t)c+1; - } else if(c==0xffff) { - fillSet->bmpLength=1; - fillSet->length=3; - fillSet->staticArray[0]=0xffff; - fillSet->staticArray[1]=1; - fillSet->staticArray[2]=0; - } else if(c<0x10ffff) { - fillSet->bmpLength=0; - fillSet->length=4; - fillSet->staticArray[0]=(uint16_t)(c>>16); - fillSet->staticArray[1]=(uint16_t)c; - ++c; - fillSet->staticArray[2]=(uint16_t)(c>>16); - fillSet->staticArray[3]=(uint16_t)c; - } else /* c==0x10ffff */ { - fillSet->bmpLength=0; - fillSet->length=2; - fillSet->staticArray[0]=0x10; - fillSet->staticArray[1]=0xffff; - } -} - -U_CAPI UBool U_EXPORT2 -uset_serializedContains(const USerializedSet* set, UChar32 c) { - const uint16_t* array; - - if(set==NULL || (uint32_t)c>0x10ffff) { - return false; - } - - array=set->array; - if(c<=0xffff) { - /* find c in the BMP part */ - int32_t lo = 0; - int32_t hi = set->bmpLength-1; - if (c < array[0]) { - hi = 0; - } else if (c < array[hi]) { - for(;;) { - int32_t i = (lo + hi) >> 1; - if (i == lo) { - break; // Done! - } else if (c < array[i]) { - hi = i; - } else { - lo = i; - } - } - } else { - hi += 1; - } - return (UBool)(hi&1); - } else { - /* find c in the supplementary part */ - uint16_t high=(uint16_t)(c>>16), low=(uint16_t)c; - int32_t base = set->bmpLength; - int32_t lo = 0; - int32_t hi = set->length - 2 - base; - if (high < array[base] || (high==array[base] && low> 1) & ~1; // Guarantee even result - int32_t iabs = i + base; - if (i == lo) { - break; // Done! - } else if (high < array[iabs] || (high==array[iabs] && lowbmpLength+(set->length-set->bmpLength)/2+1)/2; -} - -U_CAPI UBool U_EXPORT2 -uset_getSerializedRange(const USerializedSet* set, int32_t rangeIndex, - UChar32* pStart, UChar32* pEnd) { - const uint16_t* array; - int32_t bmpLength, length; - - if(set==NULL || rangeIndex<0 || pStart==NULL || pEnd==NULL) { - return false; - } - - array=set->array; - length=set->length; - bmpLength=set->bmpLength; - - rangeIndex*=2; /* address start/limit pairs */ - if(rangeIndex0) { -// if(c>=array[length-1]) { -// return length; -// } -// -// /* do not check the last range limit again in the loop below */ -// --length; -// } -// -// for(i=0; i=array[i]; ++i) {} -// return i; -// } -// -// static UBool -// addRemove(USet* set, UChar32 c, int32_t doRemove) { -// int32_t i, length, more; -// -// if(set==NULL || (uint32_t)c>0x10ffff) { -// return false; -// } -// -// length=set->length; -// i=findChar(set->array, length, c); -// if((i&1)^doRemove) { -// /* c is already in the set */ -// return true; -// } -// -// /* how many more array items do we need? */ -// if(iarray[i]) { -// /* c is just before the following range, extend that in-place by one */ -// set->array[i]=c; -// if(i>0) { -// --i; -// if(c==set->array[i]) { -// /* the previous range collapsed, remove it */ -// set->length=length-=2; -// if(iarray+i, set->array+i+2, (length-i)*4); -// } -// } -// } -// return true; -// } else if(i>0 && c==set->array[i-1]) { -// /* c is just after the previous range, extend that in-place by one */ -// if(++c<=0x10ffff) { -// set->array[i-1]=c; -// if(iarray[i]) { -// /* the following range collapsed, remove it */ -// --i; -// set->length=length-=2; -// if(iarray+i, set->array+i+2, (length-i)*4); -// } -// } -// } else { -// /* extend the previous range (had limit 0x10ffff) to the end of Unicode */ -// set->length=i-1; -// } -// return true; -// } else if(i==length && c==0x10ffff) { -// /* insert one range limit c */ -// more=1; -// } else { -// /* insert two range limits c, c+1 */ -// more=2; -// } -// -// /* insert range limits */ -// if(length+more>set->capacity) { -// /* reallocate */ -// int32_t newCapacity=set->capacity+set->capacity/2+USET_GROW_DELTA; -// UChar32* newArray=(UChar32* )uprv_malloc(newCapacity*4); -// if(newArray==NULL) { -// return false; -// } -// set->capacity=newCapacity; -// uprv_memcpy(newArray, set->array, length*4); -// -// if(set->array!=set->staticBuffer) { -// uprv_free(set->array); -// } -// set->array=newArray; -// } -// -// if(iarray+i+more, set->array+i, (length-i)*4); -// } -// set->array[i]=c; -// if(more==2) { -// set->array[i+1]=c+1; -// } -// set->length+=more; -// -// return true; -// } -// -// U_CAPI UBool U_EXPORT2 -// uset_add(USet* set, UChar32 c) { -// return addRemove(set, c, 0); -// } -// -// U_CAPI void U_EXPORT2 -// uset_remove(USet* set, UChar32 c) { -// addRemove(set, c, 1); -// } +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uset.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002mar07 +* created by: Markus W. Scherer +* +* There are functions to efficiently serialize a USet into an array of uint16_t +* and functions to use such a serialized form efficiently without +* instantiating a new USet. +*/ + +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "unicode/uset.h" +#include "unicode/uniset.h" +#include "cmemory.h" +#include "unicode/ustring.h" +#include "unicode/parsepos.h" + +U_NAMESPACE_USE + +U_CAPI USet* U_EXPORT2 +uset_openEmpty() { + return (USet*) new UnicodeSet(); +} + +U_CAPI USet* U_EXPORT2 +uset_open(UChar32 start, UChar32 end) { + return (USet*) new UnicodeSet(start, end); +} + +U_CAPI void U_EXPORT2 +uset_close(USet* set) { + delete (UnicodeSet*) set; +} + +U_CAPI USet * U_EXPORT2 +uset_clone(const USet *set) { + return (USet*) (((UnicodeSet*) set)->UnicodeSet::clone()); +} + +U_CAPI UBool U_EXPORT2 +uset_isFrozen(const USet *set) { + return ((UnicodeSet*) set)->UnicodeSet::isFrozen(); +} + +U_CAPI void U_EXPORT2 +uset_freeze(USet *set) { + ((UnicodeSet*) set)->UnicodeSet::freeze(); +} + +U_CAPI USet * U_EXPORT2 +uset_cloneAsThawed(const USet *set) { + return (USet*) (((UnicodeSet*) set)->UnicodeSet::cloneAsThawed()); +} + +U_CAPI void U_EXPORT2 +uset_set(USet* set, + UChar32 start, UChar32 end) { + ((UnicodeSet*) set)->UnicodeSet::set(start, end); +} + +U_CAPI void U_EXPORT2 +uset_addAll(USet* set, const USet *additionalSet) { + ((UnicodeSet*) set)->UnicodeSet::addAll(*((const UnicodeSet*)additionalSet)); +} + +U_CAPI void U_EXPORT2 +uset_add(USet* set, UChar32 c) { + ((UnicodeSet*) set)->UnicodeSet::add(c); +} + +U_CAPI void U_EXPORT2 +uset_addRange(USet* set, UChar32 start, UChar32 end) { + ((UnicodeSet*) set)->UnicodeSet::add(start, end); +} + +U_CAPI void U_EXPORT2 +uset_addString(USet* set, const char16_t* str, int32_t strLen) { + // UnicodeString handles -1 for strLen + UnicodeString s(strLen<0, str, strLen); + ((UnicodeSet*) set)->UnicodeSet::add(s); +} + +U_CAPI void U_EXPORT2 +uset_addAllCodePoints(USet* set, const char16_t *str, int32_t strLen) { + // UnicodeString handles -1 for strLen + UnicodeString s(str, strLen); + ((UnicodeSet*) set)->UnicodeSet::addAll(s); +} + +U_CAPI void U_EXPORT2 +uset_remove(USet* set, UChar32 c) { + ((UnicodeSet*) set)->UnicodeSet::remove(c); +} + +U_CAPI void U_EXPORT2 +uset_removeRange(USet* set, UChar32 start, UChar32 end) { + ((UnicodeSet*) set)->UnicodeSet::remove(start, end); +} + +U_CAPI void U_EXPORT2 +uset_removeString(USet* set, const char16_t* str, int32_t strLen) { + UnicodeString s(strLen==-1, str, strLen); + ((UnicodeSet*) set)->UnicodeSet::remove(s); +} + +U_CAPI void U_EXPORT2 +uset_removeAllCodePoints(USet *set, const char16_t *str, int32_t length) { + UnicodeString s(length==-1, str, length); + ((UnicodeSet*) set)->UnicodeSet::removeAll(s); +} + +U_CAPI void U_EXPORT2 +uset_removeAll(USet* set, const USet* remove) { + ((UnicodeSet*) set)->UnicodeSet::removeAll(*(const UnicodeSet*)remove); +} + +U_CAPI void U_EXPORT2 +uset_retain(USet* set, UChar32 start, UChar32 end) { + ((UnicodeSet*) set)->UnicodeSet::retain(start, end); +} + +U_CAPI void U_EXPORT2 +uset_retainString(USet *set, const char16_t *str, int32_t length) { + UnicodeString s(length==-1, str, length); + ((UnicodeSet*) set)->UnicodeSet::retain(s); +} + +U_CAPI void U_EXPORT2 +uset_retainAllCodePoints(USet *set, const char16_t *str, int32_t length) { + UnicodeString s(length==-1, str, length); + ((UnicodeSet*) set)->UnicodeSet::retainAll(s); +} + +U_CAPI void U_EXPORT2 +uset_retainAll(USet* set, const USet* retain) { + ((UnicodeSet*) set)->UnicodeSet::retainAll(*(const UnicodeSet*)retain); +} + +U_CAPI void U_EXPORT2 +uset_compact(USet* set) { + ((UnicodeSet*) set)->UnicodeSet::compact(); +} + +U_CAPI void U_EXPORT2 +uset_complement(USet* set) { + ((UnicodeSet*) set)->UnicodeSet::complement(); +} + +U_CAPI void U_EXPORT2 +uset_complementRange(USet *set, UChar32 start, UChar32 end) { + ((UnicodeSet*) set)->UnicodeSet::complement(start, end); +} + +U_CAPI void U_EXPORT2 +uset_complementString(USet *set, const char16_t *str, int32_t length) { + UnicodeString s(length==-1, str, length); + ((UnicodeSet*) set)->UnicodeSet::complement(s); +} + +U_CAPI void U_EXPORT2 +uset_complementAllCodePoints(USet *set, const char16_t *str, int32_t length) { + UnicodeString s(length==-1, str, length); + ((UnicodeSet*) set)->UnicodeSet::complementAll(s); +} + +U_CAPI void U_EXPORT2 +uset_complementAll(USet* set, const USet* complement) { + ((UnicodeSet*) set)->UnicodeSet::complementAll(*(const UnicodeSet*)complement); +} + +U_CAPI void U_EXPORT2 +uset_clear(USet* set) { + ((UnicodeSet*) set)->UnicodeSet::clear(); +} + +U_CAPI void U_EXPORT2 +uset_removeAllStrings(USet* set) { + ((UnicodeSet*) set)->UnicodeSet::removeAllStrings(); +} + +U_CAPI UBool U_EXPORT2 +uset_isEmpty(const USet* set) { + return ((const UnicodeSet*) set)->UnicodeSet::isEmpty(); +} + +U_CAPI UBool U_EXPORT2 +uset_hasStrings(const USet* set) { + return ((const UnicodeSet*) set)->UnicodeSet::hasStrings(); +} + +U_CAPI UBool U_EXPORT2 +uset_contains(const USet* set, UChar32 c) { + return ((const UnicodeSet*) set)->UnicodeSet::contains(c); +} + +U_CAPI UBool U_EXPORT2 +uset_containsRange(const USet* set, UChar32 start, UChar32 end) { + return ((const UnicodeSet*) set)->UnicodeSet::contains(start, end); +} + +U_CAPI UBool U_EXPORT2 +uset_containsString(const USet* set, const char16_t* str, int32_t strLen) { + UnicodeString s(strLen==-1, str, strLen); + return ((const UnicodeSet*) set)->UnicodeSet::contains(s); +} + +U_CAPI UBool U_EXPORT2 +uset_containsAll(const USet* set1, const USet* set2) { + return ((const UnicodeSet*) set1)->UnicodeSet::containsAll(* (const UnicodeSet*) set2); +} + +U_CAPI UBool U_EXPORT2 +uset_containsAllCodePoints(const USet* set, const char16_t *str, int32_t strLen) { + // Create a string alias, since nothing is being added to the set. + UnicodeString s(strLen==-1, str, strLen); + return ((const UnicodeSet*) set)->UnicodeSet::containsAll(s); +} + +U_CAPI UBool U_EXPORT2 +uset_containsNone(const USet* set1, const USet* set2) { + return ((const UnicodeSet*) set1)->UnicodeSet::containsNone(* (const UnicodeSet*) set2); +} + +U_CAPI UBool U_EXPORT2 +uset_containsSome(const USet* set1, const USet* set2) { + return ((const UnicodeSet*) set1)->UnicodeSet::containsSome(* (const UnicodeSet*) set2); +} + +U_CAPI int32_t U_EXPORT2 +uset_span(const USet *set, const char16_t *s, int32_t length, USetSpanCondition spanCondition) { + return ((UnicodeSet*) set)->UnicodeSet::span(s, length, spanCondition); +} + +U_CAPI int32_t U_EXPORT2 +uset_spanBack(const USet *set, const char16_t *s, int32_t length, USetSpanCondition spanCondition) { + return ((UnicodeSet*) set)->UnicodeSet::spanBack(s, length, spanCondition); +} + +U_CAPI int32_t U_EXPORT2 +uset_spanUTF8(const USet *set, const char *s, int32_t length, USetSpanCondition spanCondition) { + return ((UnicodeSet*) set)->UnicodeSet::spanUTF8(s, length, spanCondition); +} + +U_CAPI int32_t U_EXPORT2 +uset_spanBackUTF8(const USet *set, const char *s, int32_t length, USetSpanCondition spanCondition) { + return ((UnicodeSet*) set)->UnicodeSet::spanBackUTF8(s, length, spanCondition); +} + +U_CAPI UBool U_EXPORT2 +uset_equals(const USet* set1, const USet* set2) { + return *(const UnicodeSet*)set1 == *(const UnicodeSet*)set2; +} + +U_CAPI int32_t U_EXPORT2 +uset_indexOf(const USet* set, UChar32 c) { + return ((UnicodeSet*) set)->UnicodeSet::indexOf(c); +} + +U_CAPI UChar32 U_EXPORT2 +uset_charAt(const USet* set, int32_t index) { + return ((UnicodeSet*) set)->UnicodeSet::charAt(index); +} + +U_CAPI int32_t U_EXPORT2 +uset_size(const USet* set) { + return ((const UnicodeSet*) set)->UnicodeSet::size(); +} + +U_NAMESPACE_BEGIN +/** + * This class only exists to provide access to the UnicodeSet private + * USet support API. Declaring a class a friend is more portable than + * trying to declare extern "C" functions as friends. + */ +class USetAccess /* not : public UObject because all methods are static */ { +public: + /* Try to have the compiler inline these*/ + inline static int32_t getStringCount(const UnicodeSet& set) { + return set.stringsSize(); + } + inline static const UnicodeString* getString(const UnicodeSet& set, + int32_t i) { + return set.getString(i); + } +private: + /* do not instantiate*/ + USetAccess(); +}; +U_NAMESPACE_END + +U_CAPI int32_t U_EXPORT2 +uset_getRangeCount(const USet *set) { + return ((const UnicodeSet *)set)->UnicodeSet::getRangeCount(); +} + +U_CAPI int32_t U_EXPORT2 +uset_getItemCount(const USet* uset) { + const UnicodeSet& set = *(const UnicodeSet*)uset; + return set.getRangeCount() + USetAccess::getStringCount(set); +} + +U_CAPI int32_t U_EXPORT2 +uset_getItem(const USet* uset, int32_t itemIndex, + UChar32* start, UChar32* end, + char16_t* str, int32_t strCapacity, + UErrorCode* ec) { + if (U_FAILURE(*ec)) return 0; + const UnicodeSet& set = *(const UnicodeSet*)uset; + int32_t rangeCount; + + if (itemIndex < 0) { + *ec = U_ILLEGAL_ARGUMENT_ERROR; + return -1; + } else if (itemIndex < (rangeCount = set.getRangeCount())) { + *start = set.getRangeStart(itemIndex); + *end = set.getRangeEnd(itemIndex); + return 0; + } else { + itemIndex -= rangeCount; + if (itemIndex < USetAccess::getStringCount(set)) { + const UnicodeString* s = USetAccess::getString(set, itemIndex); + return s->extract(str, strCapacity, *ec); + } else { + *ec = U_INDEX_OUTOFBOUNDS_ERROR; + return -1; + } + } +} + +//U_CAPI UBool U_EXPORT2 +//uset_getRange(const USet* set, int32_t rangeIndex, +// UChar32* pStart, UChar32* pEnd) { +// if ((uint32_t) rangeIndex >= (uint32_t) uset_getRangeCount(set)) { +// return false; +// } +// const UnicodeSet* us = (const UnicodeSet*) set; +// *pStart = us->getRangeStart(rangeIndex); +// *pEnd = us->getRangeEnd(rangeIndex); +// return true; +//} + +/* + * Serialize a USet into 16-bit units. + * Store BMP code points as themselves with one 16-bit unit each. + * + * Important: the code points in the array are in ascending order, + * therefore all BMP code points precede all supplementary code points. + * + * Store each supplementary code point in 2 16-bit units, + * simply with higher-then-lower 16-bit halves. + * + * Precede the entire list with the length. + * If there are supplementary code points, then set bit 15 in the length + * and add the bmpLength between it and the array. + * + * In other words: + * - all BMP: (length=bmpLength) BMP, .., BMP + * - some supplementary: (length|0x8000) (bmpLengthUnicodeSet::serialize(dest, destCapacity,* ec); +} + +U_CAPI UBool U_EXPORT2 +uset_getSerializedSet(USerializedSet* fillSet, const uint16_t* src, int32_t srcLength) { + int32_t length; + + if(fillSet==nullptr) { + return false; + } + if(src==nullptr || srcLength<=0) { + fillSet->length=fillSet->bmpLength=0; + return false; + } + + length=*src++; + if(length&0x8000) { + /* there are supplementary values */ + length&=0x7fff; + if(srcLength<(2+length)) { + fillSet->length=fillSet->bmpLength=0; + return false; + } + fillSet->bmpLength=*src++; + } else { + /* only BMP values */ + if(srcLength<(1+length)) { + fillSet->length=fillSet->bmpLength=0; + return false; + } + fillSet->bmpLength=length; + } + fillSet->array=src; + fillSet->length=length; + return true; +} + +U_CAPI void U_EXPORT2 +uset_setSerializedToOne(USerializedSet* fillSet, UChar32 c) { + if(fillSet==nullptr || (uint32_t)c>0x10ffff) { + return; + } + + fillSet->array=fillSet->staticArray; + if(c<0xffff) { + fillSet->bmpLength=fillSet->length=2; + fillSet->staticArray[0]=(uint16_t)c; + fillSet->staticArray[1]=(uint16_t)c+1; + } else if(c==0xffff) { + fillSet->bmpLength=1; + fillSet->length=3; + fillSet->staticArray[0]=0xffff; + fillSet->staticArray[1]=1; + fillSet->staticArray[2]=0; + } else if(c<0x10ffff) { + fillSet->bmpLength=0; + fillSet->length=4; + fillSet->staticArray[0]=(uint16_t)(c>>16); + fillSet->staticArray[1]=(uint16_t)c; + ++c; + fillSet->staticArray[2]=(uint16_t)(c>>16); + fillSet->staticArray[3]=(uint16_t)c; + } else /* c==0x10ffff */ { + fillSet->bmpLength=0; + fillSet->length=2; + fillSet->staticArray[0]=0x10; + fillSet->staticArray[1]=0xffff; + } +} + +U_CAPI UBool U_EXPORT2 +uset_serializedContains(const USerializedSet* set, UChar32 c) { + const uint16_t* array; + + if(set==nullptr || (uint32_t)c>0x10ffff) { + return false; + } + + array=set->array; + if(c<=0xffff) { + /* find c in the BMP part */ + int32_t lo = 0; + int32_t hi = set->bmpLength-1; + if (c < array[0]) { + hi = 0; + } else if (c < array[hi]) { + for(;;) { + int32_t i = (lo + hi) >> 1; + if (i == lo) { + break; // Done! + } else if (c < array[i]) { + hi = i; + } else { + lo = i; + } + } + } else { + hi += 1; + } + return (UBool)(hi&1); + } else { + /* find c in the supplementary part */ + uint16_t high=(uint16_t)(c>>16), low=(uint16_t)c; + int32_t base = set->bmpLength; + int32_t lo = 0; + int32_t hi = set->length - 2 - base; + if (high < array[base] || (high==array[base] && low> 1) & ~1; // Guarantee even result + int32_t iabs = i + base; + if (i == lo) { + break; // Done! + } else if (high < array[iabs] || (high==array[iabs] && lowbmpLength+(set->length-set->bmpLength)/2+1)/2; +} + +U_CAPI UBool U_EXPORT2 +uset_getSerializedRange(const USerializedSet* set, int32_t rangeIndex, + UChar32* pStart, UChar32* pEnd) { + const uint16_t* array; + int32_t bmpLength, length; + + if(set==nullptr || rangeIndex<0 || pStart==nullptr || pEnd==nullptr) { + return false; + } + + array=set->array; + length=set->length; + bmpLength=set->bmpLength; + + rangeIndex*=2; /* address start/limit pairs */ + if(rangeIndex0) { +// if(c>=array[length-1]) { +// return length; +// } +// +// /* do not check the last range limit again in the loop below */ +// --length; +// } +// +// for(i=0; i=array[i]; ++i) {} +// return i; +// } +// +// static UBool +// addRemove(USet* set, UChar32 c, int32_t doRemove) { +// int32_t i, length, more; +// +// if(set==nullptr || (uint32_t)c>0x10ffff) { +// return false; +// } +// +// length=set->length; +// i=findChar(set->array, length, c); +// if((i&1)^doRemove) { +// /* c is already in the set */ +// return true; +// } +// +// /* how many more array items do we need? */ +// if(iarray[i]) { +// /* c is just before the following range, extend that in-place by one */ +// set->array[i]=c; +// if(i>0) { +// --i; +// if(c==set->array[i]) { +// /* the previous range collapsed, remove it */ +// set->length=length-=2; +// if(iarray+i, set->array+i+2, (length-i)*4); +// } +// } +// } +// return true; +// } else if(i>0 && c==set->array[i-1]) { +// /* c is just after the previous range, extend that in-place by one */ +// if(++c<=0x10ffff) { +// set->array[i-1]=c; +// if(iarray[i]) { +// /* the following range collapsed, remove it */ +// --i; +// set->length=length-=2; +// if(iarray+i, set->array+i+2, (length-i)*4); +// } +// } +// } else { +// /* extend the previous range (had limit 0x10ffff) to the end of Unicode */ +// set->length=i-1; +// } +// return true; +// } else if(i==length && c==0x10ffff) { +// /* insert one range limit c */ +// more=1; +// } else { +// /* insert two range limits c, c+1 */ +// more=2; +// } +// +// /* insert range limits */ +// if(length+more>set->capacity) { +// /* reallocate */ +// int32_t newCapacity=set->capacity+set->capacity/2+USET_GROW_DELTA; +// UChar32* newArray=(UChar32* )uprv_malloc(newCapacity*4); +// if(newArray==nullptr) { +// return false; +// } +// set->capacity=newCapacity; +// uprv_memcpy(newArray, set->array, length*4); +// +// if(set->array!=set->staticBuffer) { +// uprv_free(set->array); +// } +// set->array=newArray; +// } +// +// if(iarray+i+more, set->array+i, (length-i)*4); +// } +// set->array[i]=c; +// if(more==2) { +// set->array[i+1]=c+1; +// } +// set->length+=more; +// +// return true; +// } +// +// U_CAPI UBool U_EXPORT2 +// uset_add(USet* set, UChar32 c) { +// return addRemove(set, c, 0); +// } +// +// U_CAPI void U_EXPORT2 +// uset_remove(USet* set, UChar32 c) { +// addRemove(set, c, 1); +// } diff --git a/deps/icu-small/source/common/uset_imp.h b/deps/icu-small/source/common/uset_imp.h index 7233b9303c3a17..7b764e0b4afe2e 100644 --- a/deps/icu-small/source/common/uset_imp.h +++ b/deps/icu-small/source/common/uset_imp.h @@ -1,62 +1,71 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2004-2007, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uset_imp.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004sep07 -* created by: Markus W. Scherer -* -* Internal USet definitions. -*/ - -#ifndef __USET_IMP_H__ -#define __USET_IMP_H__ - -#include "unicode/utypes.h" -#include "unicode/uset.h" - -U_CDECL_BEGIN - -typedef void U_CALLCONV -USetAdd(USet *set, UChar32 c); - -typedef void U_CALLCONV -USetAddRange(USet *set, UChar32 start, UChar32 end); - -typedef void U_CALLCONV -USetAddString(USet *set, const UChar *str, int32_t length); - -typedef void U_CALLCONV -USetRemove(USet *set, UChar32 c); - -typedef void U_CALLCONV -USetRemoveRange(USet *set, UChar32 start, UChar32 end); - -/** - * Interface for adding items to a USet, to keep low-level code from - * statically depending on the USet implementation. - * Calls will look like sa->add(sa->set, c); - */ -struct USetAdder { - USet *set; - USetAdd *add; - USetAddRange *addRange; - USetAddString *addString; - USetRemove *remove; - USetRemoveRange *removeRange; -}; -typedef struct USetAdder USetAdder; - -U_CDECL_END - -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2004-2007, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uset_imp.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004sep07 +* created by: Markus W. Scherer +* +* Internal USet definitions. +*/ + +#ifndef __USET_IMP_H__ +#define __USET_IMP_H__ + +#include "unicode/utypes.h" +#include "unicode/uset.h" + +U_CDECL_BEGIN + +typedef void U_CALLCONV +USetAdd(USet *set, UChar32 c); + +typedef void U_CALLCONV +USetAddRange(USet *set, UChar32 start, UChar32 end); + +typedef void U_CALLCONV +USetAddString(USet *set, const UChar *str, int32_t length); + +typedef void U_CALLCONV +USetRemove(USet *set, UChar32 c); + +typedef void U_CALLCONV +USetRemoveRange(USet *set, UChar32 start, UChar32 end); + +/** + * Interface for adding items to a USet, to keep low-level code from + * statically depending on the USet implementation. + * Calls will look like sa->add(sa->set, c); + */ +struct USetAdder { + USet *set; + USetAdd *add; + USetAddRange *addRange; + USetAddString *addString; + USetRemove *remove; + USetRemoveRange *removeRange; +}; +typedef struct USetAdder USetAdder; + +U_CDECL_END + +#ifdef __cplusplus + +namespace { + +constexpr int32_t USET_CASE_MASK = USET_CASE_INSENSITIVE | USET_ADD_CASE_MAPPINGS; + +} // namespace + +#endif // __cplusplus + +#endif diff --git a/deps/icu-small/source/common/uset_props.cpp b/deps/icu-small/source/common/uset_props.cpp index f08e760b10d4e1..2288baed97b730 100644 --- a/deps/icu-small/source/common/uset_props.cpp +++ b/deps/icu-small/source/common/uset_props.cpp @@ -1,143 +1,143 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2002-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: uset_props.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004aug30 -* created by: Markus W. Scherer -* -* C wrappers around UnicodeSet functions that are implemented in -* uniset_props.cpp, split off for modularization. -*/ - -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "unicode/uset.h" -#include "unicode/uniset.h" -#include "cmemory.h" -#include "unicode/ustring.h" -#include "unicode/parsepos.h" - -U_NAMESPACE_USE - -U_CAPI USet* U_EXPORT2 -uset_openPattern(const UChar* pattern, int32_t patternLength, - UErrorCode* ec) -{ - UnicodeString pat(patternLength==-1, pattern, patternLength); - UnicodeSet* set = new UnicodeSet(pat, *ec); - /* test for NULL */ - if(set == 0) { - *ec = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - - if (U_FAILURE(*ec)) { - delete set; - set = NULL; - } - return (USet*) set; -} - -U_CAPI USet* U_EXPORT2 -uset_openPatternOptions(const UChar* pattern, int32_t patternLength, - uint32_t options, - UErrorCode* ec) -{ - UnicodeString pat(patternLength==-1, pattern, patternLength); - UnicodeSet* set = new UnicodeSet(pat, options, NULL, *ec); - /* test for NULL */ - if(set == 0) { - *ec = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - - if (U_FAILURE(*ec)) { - delete set; - set = NULL; - } - return (USet*) set; -} - - -U_CAPI int32_t U_EXPORT2 -uset_applyPattern(USet *set, - const UChar *pattern, int32_t patternLength, - uint32_t options, - UErrorCode *status){ - - // status code needs to be checked since we - // dereference it - if(status == NULL || U_FAILURE(*status)){ - return 0; - } - - // check only the set paramenter - // if pattern is NULL or null terminate - // UnicodeString constructor takes care of it - if(set == NULL){ - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - UnicodeString pat(pattern, patternLength); - - ParsePosition pos; - - ((UnicodeSet*) set)->applyPattern(pat, pos, options, NULL, *status); - - return pos.getIndex(); -} - -U_CAPI void U_EXPORT2 -uset_applyIntPropertyValue(USet* set, - UProperty prop, int32_t value, UErrorCode* ec) { - ((UnicodeSet*) set)->applyIntPropertyValue(prop, value, *ec); -} - -U_CAPI void U_EXPORT2 -uset_applyPropertyAlias(USet* set, - const UChar *prop, int32_t propLength, - const UChar *value, int32_t valueLength, - UErrorCode* ec) { - - UnicodeString p(prop, propLength); - UnicodeString v(value, valueLength); - - ((UnicodeSet*) set)->applyPropertyAlias(p, v, *ec); -} - -U_CAPI UBool U_EXPORT2 -uset_resemblesPattern(const UChar *pattern, int32_t patternLength, - int32_t pos) { - - UnicodeString pat(pattern, patternLength); - - return ((pos+1) < pat.length() && - pat.charAt(pos) == (UChar)91/*[*/) || - UnicodeSet::resemblesPattern(pat, pos); -} - -U_CAPI int32_t U_EXPORT2 -uset_toPattern(const USet* set, - UChar* result, int32_t resultCapacity, - UBool escapeUnprintable, - UErrorCode* ec) { - UnicodeString pat; - ((const UnicodeSet*) set)->toPattern(pat, escapeUnprintable); - return pat.extract(result, resultCapacity, *ec); -} - -U_CAPI void U_EXPORT2 -uset_closeOver(USet* set, int32_t attributes) { - ((UnicodeSet*) set)->UnicodeSet::closeOver(attributes); -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2002-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: uset_props.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004aug30 +* created by: Markus W. Scherer +* +* C wrappers around UnicodeSet functions that are implemented in +* uniset_props.cpp, split off for modularization. +*/ + +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "unicode/uset.h" +#include "unicode/uniset.h" +#include "cmemory.h" +#include "unicode/ustring.h" +#include "unicode/parsepos.h" + +U_NAMESPACE_USE + +U_CAPI USet* U_EXPORT2 +uset_openPattern(const char16_t* pattern, int32_t patternLength, + UErrorCode* ec) +{ + UnicodeString pat(patternLength==-1, pattern, patternLength); + UnicodeSet* set = new UnicodeSet(pat, *ec); + /* test for nullptr */ + if(set == 0) { + *ec = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + + if (U_FAILURE(*ec)) { + delete set; + set = nullptr; + } + return (USet*) set; +} + +U_CAPI USet* U_EXPORT2 +uset_openPatternOptions(const char16_t* pattern, int32_t patternLength, + uint32_t options, + UErrorCode* ec) +{ + UnicodeString pat(patternLength==-1, pattern, patternLength); + UnicodeSet* set = new UnicodeSet(pat, options, nullptr, *ec); + /* test for nullptr */ + if(set == 0) { + *ec = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + + if (U_FAILURE(*ec)) { + delete set; + set = nullptr; + } + return (USet*) set; +} + + +U_CAPI int32_t U_EXPORT2 +uset_applyPattern(USet *set, + const char16_t *pattern, int32_t patternLength, + uint32_t options, + UErrorCode *status){ + + // status code needs to be checked since we + // dereference it + if(status == nullptr || U_FAILURE(*status)){ + return 0; + } + + // check only the set paramenter + // if pattern is nullptr or NUL terminated + // UnicodeString constructor takes care of it + if(set == nullptr){ + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + UnicodeString pat(pattern, patternLength); + + ParsePosition pos; + + ((UnicodeSet*) set)->applyPattern(pat, pos, options, nullptr, *status); + + return pos.getIndex(); +} + +U_CAPI void U_EXPORT2 +uset_applyIntPropertyValue(USet* set, + UProperty prop, int32_t value, UErrorCode* ec) { + ((UnicodeSet*) set)->applyIntPropertyValue(prop, value, *ec); +} + +U_CAPI void U_EXPORT2 +uset_applyPropertyAlias(USet* set, + const char16_t *prop, int32_t propLength, + const char16_t *value, int32_t valueLength, + UErrorCode* ec) { + + UnicodeString p(prop, propLength); + UnicodeString v(value, valueLength); + + ((UnicodeSet*) set)->applyPropertyAlias(p, v, *ec); +} + +U_CAPI UBool U_EXPORT2 +uset_resemblesPattern(const char16_t *pattern, int32_t patternLength, + int32_t pos) { + + UnicodeString pat(pattern, patternLength); + + return ((pos+1) < pat.length() && + pat.charAt(pos) == (char16_t)91/*[*/) || + UnicodeSet::resemblesPattern(pat, pos); +} + +U_CAPI int32_t U_EXPORT2 +uset_toPattern(const USet* set, + char16_t* result, int32_t resultCapacity, + UBool escapeUnprintable, + UErrorCode* ec) { + UnicodeString pat; + ((const UnicodeSet*) set)->toPattern(pat, escapeUnprintable); + return pat.extract(result, resultCapacity, *ec); +} + +U_CAPI void U_EXPORT2 +uset_closeOver(USet* set, int32_t attributes) { + ((UnicodeSet*) set)->UnicodeSet::closeOver(attributes); +} diff --git a/deps/icu-small/source/common/usetiter.cpp b/deps/icu-small/source/common/usetiter.cpp index 3cdece5500b497..263e7a42a7b80c 100644 --- a/deps/icu-small/source/common/usetiter.cpp +++ b/deps/icu-small/source/common/usetiter.cpp @@ -1,152 +1,152 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2006, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ -#include "unicode/usetiter.h" -#include "unicode/uniset.h" -#include "unicode/unistr.h" -#include "uvector.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UnicodeSetIterator) - -/** - * Create an iterator - * @param set set to iterate over - */ -UnicodeSetIterator::UnicodeSetIterator(const UnicodeSet& uSet) { - cpString = NULL; - reset(uSet); -} - -/** - * Create an iterator. Convenience for when the contents are to be set later. - */ -UnicodeSetIterator::UnicodeSetIterator() { - this->set = NULL; - cpString = NULL; - reset(); -} - -UnicodeSetIterator::~UnicodeSetIterator() { - delete cpString; -} - -/** - * Returns the next element in the set. - * @return true if there was another element in the set. - * if so, if codepoint == IS_STRING, the value is a string in the string field - * else the value is a single code point in the codepoint field. - *
You are guaranteed that the codepoints are in sorted order, and the strings are in sorted order, - * and that all code points are returned before any strings are returned. - *
Note also that the codepointEnd is undefined after calling this method. - */ -UBool UnicodeSetIterator::next() { - if (nextElement <= endElement) { - codepoint = codepointEnd = nextElement++; - string = NULL; - return true; - } - if (range < endRange) { - loadRange(++range); - codepoint = codepointEnd = nextElement++; - string = NULL; - return true; - } - - if (nextString >= stringCount) return false; - codepoint = (UChar32)IS_STRING; // signal that value is actually a string - string = (const UnicodeString*) set->strings->elementAt(nextString++); - return true; -} - -/** - * @return true if there was another element in the set. - * if so, if codepoint == IS_STRING, the value is a string in the string field - * else the value is a range of codepoints in the fields. - *
Note that the codepoints are in sorted order, and the strings are in sorted order, - * and that all code points are returned before any strings are returned. - *
You are guaranteed that the ranges are in sorted order, and the strings are in sorted order, - * and that all ranges are returned before any strings are returned. - *
You are also guaranteed that ranges are disjoint and non-contiguous. - *
Note also that the codepointEnd is undefined after calling this method. - */ -UBool UnicodeSetIterator::nextRange() { - string = NULL; - if (nextElement <= endElement) { - codepointEnd = endElement; - codepoint = nextElement; - nextElement = endElement+1; - return true; - } - if (range < endRange) { - loadRange(++range); - codepointEnd = endElement; - codepoint = nextElement; - nextElement = endElement+1; - return true; - } - - if (nextString >= stringCount) return false; - codepoint = (UChar32)IS_STRING; // signal that value is actually a string - string = (const UnicodeString*) set->strings->elementAt(nextString++); - return true; -} - -/** - *@param set the set to iterate over. This allows reuse of the iterator. - */ -void UnicodeSetIterator::reset(const UnicodeSet& uSet) { - this->set = &uSet; - reset(); -} - -/** - * Resets to the start, to allow the iteration to start over again. - */ -void UnicodeSetIterator::reset() { - if (set == NULL) { - // Set up indices to empty iteration - endRange = -1; - stringCount = 0; - } else { - endRange = set->getRangeCount() - 1; - stringCount = set->stringsSize(); - } - range = 0; - endElement = -1; - nextElement = 0; - if (endRange >= 0) { - loadRange(range); - } - nextString = 0; - string = NULL; -} - -void UnicodeSetIterator::loadRange(int32_t iRange) { - nextElement = set->getRangeStart(iRange); - endElement = set->getRangeEnd(iRange); -} - - -const UnicodeString& UnicodeSetIterator::getString() { - if (string==NULL && codepoint!=(UChar32)IS_STRING) { - if (cpString == NULL) { - cpString = new UnicodeString(); - } - if (cpString != NULL) { - cpString->setTo((UChar32)codepoint); - } - string = cpString; - } - return *string; -} - -U_NAMESPACE_END - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2006, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ +#include "unicode/usetiter.h" +#include "unicode/uniset.h" +#include "unicode/unistr.h" +#include "uvector.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UnicodeSetIterator) + +/** + * Create an iterator + * @param set set to iterate over + */ +UnicodeSetIterator::UnicodeSetIterator(const UnicodeSet& uSet) { + cpString = nullptr; + reset(uSet); +} + +/** + * Create an iterator. Convenience for when the contents are to be set later. + */ +UnicodeSetIterator::UnicodeSetIterator() { + this->set = nullptr; + cpString = nullptr; + reset(); +} + +UnicodeSetIterator::~UnicodeSetIterator() { + delete cpString; +} + +/** + * Returns the next element in the set. + * @return true if there was another element in the set. + * if so, if codepoint == IS_STRING, the value is a string in the string field + * else the value is a single code point in the codepoint field. + *
You are guaranteed that the codepoints are in sorted order, and the strings are in sorted order, + * and that all code points are returned before any strings are returned. + *
Note also that the codepointEnd is undefined after calling this method. + */ +UBool UnicodeSetIterator::next() { + if (nextElement <= endElement) { + codepoint = codepointEnd = nextElement++; + string = nullptr; + return true; + } + if (range < endRange) { + loadRange(++range); + codepoint = codepointEnd = nextElement++; + string = nullptr; + return true; + } + + if (nextString >= stringCount) return false; + codepoint = (UChar32)IS_STRING; // signal that value is actually a string + string = (const UnicodeString*) set->strings->elementAt(nextString++); + return true; +} + +/** + * @return true if there was another element in the set. + * if so, if codepoint == IS_STRING, the value is a string in the string field + * else the value is a range of codepoints in the fields. + *
Note that the codepoints are in sorted order, and the strings are in sorted order, + * and that all code points are returned before any strings are returned. + *
You are guaranteed that the ranges are in sorted order, and the strings are in sorted order, + * and that all ranges are returned before any strings are returned. + *
You are also guaranteed that ranges are disjoint and non-contiguous. + *
Note also that the codepointEnd is undefined after calling this method. + */ +UBool UnicodeSetIterator::nextRange() { + string = nullptr; + if (nextElement <= endElement) { + codepointEnd = endElement; + codepoint = nextElement; + nextElement = endElement+1; + return true; + } + if (range < endRange) { + loadRange(++range); + codepointEnd = endElement; + codepoint = nextElement; + nextElement = endElement+1; + return true; + } + + if (nextString >= stringCount) return false; + codepoint = (UChar32)IS_STRING; // signal that value is actually a string + string = (const UnicodeString*) set->strings->elementAt(nextString++); + return true; +} + +/** + *@param set the set to iterate over. This allows reuse of the iterator. + */ +void UnicodeSetIterator::reset(const UnicodeSet& uSet) { + this->set = &uSet; + reset(); +} + +/** + * Resets to the start, to allow the iteration to start over again. + */ +void UnicodeSetIterator::reset() { + if (set == nullptr) { + // Set up indices to empty iteration + endRange = -1; + stringCount = 0; + } else { + endRange = set->getRangeCount() - 1; + stringCount = set->stringsSize(); + } + range = 0; + endElement = -1; + nextElement = 0; + if (endRange >= 0) { + loadRange(range); + } + nextString = 0; + string = nullptr; +} + +void UnicodeSetIterator::loadRange(int32_t iRange) { + nextElement = set->getRangeStart(iRange); + endElement = set->getRangeEnd(iRange); +} + + +const UnicodeString& UnicodeSetIterator::getString() { + if (string==nullptr && codepoint!=(UChar32)IS_STRING) { + if (cpString == nullptr) { + cpString = new UnicodeString(); + } + if (cpString != nullptr) { + cpString->setTo((UChar32)codepoint); + } + string = cpString; + } + return *string; +} + +U_NAMESPACE_END + +//eof diff --git a/deps/icu-small/source/common/ushape.cpp b/deps/icu-small/source/common/ushape.cpp index babbbe52a831e8..575cf691d77d3c 100644 --- a/deps/icu-small/source/common/ushape.cpp +++ b/deps/icu-small/source/common/ushape.cpp @@ -1,1728 +1,1728 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ****************************************************************************** - * - * Copyright (C) 2000-2016, International Business Machines - * Corporation and others. All Rights Reserved. - * - ****************************************************************************** - * file name: ushape.cpp - * encoding: UTF-8 - * tab size: 8 (not used) - * indentation:4 - * - * created on: 2000jun29 - * created by: Markus W. Scherer - * - * Arabic letter shaping implemented by Ayman Roshdy - */ - -#include "unicode/utypes.h" -#include "unicode/uchar.h" -#include "unicode/ustring.h" -#include "unicode/ushape.h" -#include "cmemory.h" -#include "putilimp.h" -#include "ustr_imp.h" -#include "ubidi_props.h" -#include "uassert.h" - -/* - * This implementation is designed for 16-bit Unicode strings. - * The main assumption is that the Arabic characters and their - * presentation forms each fit into a single UChar. - * With UTF-8, they occupy 2 or 3 bytes, and more than the ASCII - * characters. - */ - -/* - * ### TODO in general for letter shaping: - * - the letter shaping code is UTF-16-unaware; needs update - * + especially invertBuffer()?! - * - needs to handle the "Arabic Tail" that is used in some legacy codepages - * as a glyph fragment of wide-glyph letters - * + IBM Unicode conversion tables map it to U+200B (ZWSP) - * + IBM Egypt has proposed to encode the tail in Unicode among Arabic Presentation Forms - * + Unicode 3.2 added U+FE73 ARABIC TAIL FRAGMENT - */ - -/* definitions for Arabic letter shaping ------------------------------------ */ - -#define IRRELEVANT 4 -#define LAMTYPE 16 -#define ALEFTYPE 32 -#define LINKR 1 -#define LINKL 2 -#define APRESENT 8 -#define SHADDA 64 -#define CSHADDA 128 -#define COMBINE (SHADDA+CSHADDA) - -#define HAMZAFE_CHAR 0xfe80 -#define HAMZA06_CHAR 0x0621 -#define YEH_HAMZA_CHAR 0x0626 -#define YEH_HAMZAFE_CHAR 0xFE89 -#define LAMALEF_SPACE_SUB 0xFFFF -#define TASHKEEL_SPACE_SUB 0xFFFE -#define NEW_TAIL_CHAR 0xFE73 -#define OLD_TAIL_CHAR 0x200B -#define LAM_CHAR 0x0644 -#define SPACE_CHAR 0x0020 -#define SHADDA_CHAR 0xFE7C -#define TATWEEL_CHAR 0x0640 -#define SHADDA_TATWEEL_CHAR 0xFE7D -#define SHADDA06_CHAR 0x0651 - -#define SHAPE_MODE 0 -#define DESHAPE_MODE 1 - -struct uShapeVariables { - UChar tailChar; - uint32_t uShapeLamalefBegin; - uint32_t uShapeLamalefEnd; - uint32_t uShapeTashkeelBegin; - uint32_t uShapeTashkeelEnd; - int spacesRelativeToTextBeginEnd; -}; - -static const uint8_t tailFamilyIsolatedFinal[] = { - /* FEB1 */ 1, - /* FEB2 */ 1, - /* FEB3 */ 0, - /* FEB4 */ 0, - /* FEB5 */ 1, - /* FEB6 */ 1, - /* FEB7 */ 0, - /* FEB8 */ 0, - /* FEB9 */ 1, - /* FEBA */ 1, - /* FEBB */ 0, - /* FEBC */ 0, - /* FEBD */ 1, - /* FEBE */ 1 -}; - -static const uint8_t tashkeelMedial[] = { - /* FE70 */ 0, - /* FE71 */ 1, - /* FE72 */ 0, - /* FE73 */ 0, - /* FE74 */ 0, - /* FE75 */ 0, - /* FE76 */ 0, - /* FE77 */ 1, - /* FE78 */ 0, - /* FE79 */ 1, - /* FE7A */ 0, - /* FE7B */ 1, - /* FE7C */ 0, - /* FE7D */ 1, - /* FE7E */ 0, - /* FE7F */ 1 -}; - -static const UChar yehHamzaToYeh[] = -{ -/* isolated*/ 0xFEEF, -/* final */ 0xFEF0 -}; - -static const uint8_t IrrelevantPos[] = { - 0x0, 0x2, 0x4, 0x6, - 0x8, 0xA, 0xC, 0xE -}; - - -static const UChar convertLamAlef[] = -{ -/*FEF5*/ 0x0622, -/*FEF6*/ 0x0622, -/*FEF7*/ 0x0623, -/*FEF8*/ 0x0623, -/*FEF9*/ 0x0625, -/*FEFA*/ 0x0625, -/*FEFB*/ 0x0627, -/*FEFC*/ 0x0627 -}; - -static const UChar araLink[178]= -{ - 1 + 32 + 256 * 0x11,/*0x0622*/ - 1 + 32 + 256 * 0x13,/*0x0623*/ - 1 + 256 * 0x15,/*0x0624*/ - 1 + 32 + 256 * 0x17,/*0x0625*/ - 1 + 2 + 256 * 0x19,/*0x0626*/ - 1 + 32 + 256 * 0x1D,/*0x0627*/ - 1 + 2 + 256 * 0x1F,/*0x0628*/ - 1 + 256 * 0x23,/*0x0629*/ - 1 + 2 + 256 * 0x25,/*0x062A*/ - 1 + 2 + 256 * 0x29,/*0x062B*/ - 1 + 2 + 256 * 0x2D,/*0x062C*/ - 1 + 2 + 256 * 0x31,/*0x062D*/ - 1 + 2 + 256 * 0x35,/*0x062E*/ - 1 + 256 * 0x39,/*0x062F*/ - 1 + 256 * 0x3B,/*0x0630*/ - 1 + 256 * 0x3D,/*0x0631*/ - 1 + 256 * 0x3F,/*0x0632*/ - 1 + 2 + 256 * 0x41,/*0x0633*/ - 1 + 2 + 256 * 0x45,/*0x0634*/ - 1 + 2 + 256 * 0x49,/*0x0635*/ - 1 + 2 + 256 * 0x4D,/*0x0636*/ - 1 + 2 + 256 * 0x51,/*0x0637*/ - 1 + 2 + 256 * 0x55,/*0x0638*/ - 1 + 2 + 256 * 0x59,/*0x0639*/ - 1 + 2 + 256 * 0x5D,/*0x063A*/ - 0, 0, 0, 0, 0, /*0x063B-0x063F*/ - 1 + 2, /*0x0640*/ - 1 + 2 + 256 * 0x61,/*0x0641*/ - 1 + 2 + 256 * 0x65,/*0x0642*/ - 1 + 2 + 256 * 0x69,/*0x0643*/ - 1 + 2 + 16 + 256 * 0x6D,/*0x0644*/ - 1 + 2 + 256 * 0x71,/*0x0645*/ - 1 + 2 + 256 * 0x75,/*0x0646*/ - 1 + 2 + 256 * 0x79,/*0x0647*/ - 1 + 256 * 0x7D,/*0x0648*/ - 1 + 256 * 0x7F,/*0x0649*/ - 1 + 2 + 256 * 0x81,/*0x064A*/ - 4 + 256 * 1, /*0x064B*/ - 4 + 128 + 256 * 1, /*0x064C*/ - 4 + 128 + 256 * 1, /*0x064D*/ - 4 + 128 + 256 * 1, /*0x064E*/ - 4 + 128 + 256 * 1, /*0x064F*/ - 4 + 128 + 256 * 1, /*0x0650*/ - 4 + 64 + 256 * 3, /*0x0651*/ - 4 + 256 * 1, /*0x0652*/ - 4 + 256 * 7, /*0x0653*/ - 4 + 256 * 8, /*0x0654*/ - 4 + 256 * 8, /*0x0655*/ - 4 + 256 * 1, /*0x0656*/ - 0, 0, 0, 0, 0, /*0x0657-0x065B*/ - 1 + 256 * 0x85,/*0x065C*/ - 1 + 256 * 0x87,/*0x065D*/ - 1 + 256 * 0x89,/*0x065E*/ - 1 + 256 * 0x8B,/*0x065F*/ - 0, 0, 0, 0, 0, /*0x0660-0x0664*/ - 0, 0, 0, 0, 0, /*0x0665-0x0669*/ - 0, 0, 0, 0, 0, 0, /*0x066A-0x066F*/ - 4 + 256 * 6, /*0x0670*/ - 1 + 8 + 256 * 0x00,/*0x0671*/ - 1 + 32, /*0x0672*/ - 1 + 32, /*0x0673*/ - 0, /*0x0674*/ - 1 + 32, /*0x0675*/ - 1, 1, /*0x0676-0x0677*/ - 1 + 2, /*0x0678*/ - 1 + 2 + 8 + 256 * 0x16,/*0x0679*/ - 1 + 2 + 8 + 256 * 0x0E,/*0x067A*/ - 1 + 2 + 8 + 256 * 0x02,/*0x067B*/ - 1+2, 1+2, /*0x67C-0x067D*/ - 1+2+8+256 * 0x06, 1+2, 1+2, 1+2, 1+2, 1+2, /*0x067E-0x0683*/ - 1+2, 1+2, 1+2+8+256 * 0x2A, 1+2, /*0x0684-0x0687*/ - 1 + 8 + 256 * 0x38,/*0x0688*/ - 1, 1, 1, /*0x0689-0x068B*/ - 1 + 8 + 256 * 0x34,/*0x068C*/ - 1 + 8 + 256 * 0x32,/*0x068D*/ - 1 + 8 + 256 * 0x36,/*0x068E*/ - 1, 1, /*0x068F-0x0690*/ - 1 + 8 + 256 * 0x3C,/*0x0691*/ - 1, 1, 1, 1, 1, 1, 1+8+256 * 0x3A, 1, /*0x0692-0x0699*/ - 1+2, 1+2, 1+2, 1+2, 1+2, 1+2, /*0x069A-0x06A3*/ - 1+2, 1+2, 1+2, 1+2, /*0x069A-0x06A3*/ - 1+2, 1+2, 1+2, 1+2, 1+2, 1+2+8+256 * 0x3E, /*0x06A4-0x06AD*/ - 1+2, 1+2, 1+2, 1+2, /*0x06A4-0x06AD*/ - 1+2, 1+2+8+256 * 0x42, 1+2, 1+2, 1+2, 1+2, /*0x06AE-0x06B7*/ - 1+2, 1+2, 1+2, 1+2, /*0x06AE-0x06B7*/ - 1+2, 1+2, /*0x06B8-0x06B9*/ - 1 + 8 + 256 * 0x4E,/*0x06BA*/ - 1 + 2 + 8 + 256 * 0x50,/*0x06BB*/ - 1+2, 1+2, /*0x06BC-0x06BD*/ - 1 + 2 + 8 + 256 * 0x5A,/*0x06BE*/ - 1+2, /*0x06BF*/ - 1 + 8 + 256 * 0x54,/*0x06C0*/ - 1 + 2 + 8 + 256 * 0x56,/*0x06C1*/ - 1, 1, 1, /*0x06C2-0x06C4*/ - 1 + 8 + 256 * 0x90,/*0x06C5*/ - 1 + 8 + 256 * 0x89,/*0x06C6*/ - 1 + 8 + 256 * 0x87,/*0x06C7*/ - 1 + 8 + 256 * 0x8B,/*0x06C8*/ - 1 + 8 + 256 * 0x92,/*0x06C9*/ - 1, /*0x06CA*/ - 1 + 8 + 256 * 0x8E,/*0x06CB*/ - 1 + 2 + 8 + 256 * 0xAC,/*0x06CC*/ - 1, /*0x06CD*/ - 1+2, 1+2, /*0x06CE-0x06CF*/ - 1 + 2 + 8 + 256 * 0x94,/*0x06D0*/ - 1+2, /*0x06D1*/ - 1 + 8 + 256 * 0x5E,/*0x06D2*/ - 1 + 8 + 256 * 0x60 /*0x06D3*/ -}; - -static const uint8_t presALink[] = { -/***********0*****1*****2*****3*****4*****5*****6*****7*****8*****9*****A*****B*****C*****D*****E*****F*/ -/*FB5*/ 0, 1, 0, 0, 0, 0, 0, 1, 2,1 + 2, 0, 0, 0, 0, 0, 0, -/*FB6*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FB7*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2,1 + 2, 0, 0, -/*FB8*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, -/*FB9*/ 2,1 + 2, 0, 1, 2,1 + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FBA*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FBB*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FBC*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FBD*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FBE*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FBF*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2,1 + 2, -/*FC0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FC1*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FC2*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FC3*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FC4*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FC5*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, -/*FC6*/ 4, 4, 4 -}; - -static const uint8_t presBLink[]= -{ -/***********0*****1*****2*****3*****4*****5*****6*****7*****8*****9*****A*****B*****C*****D*****E*****F*/ -/*FE7*/1 + 2,1 + 2,1 + 2, 0,1 + 2, 0,1 + 2,1 + 2,1 + 2,1 + 2,1 + 2,1 + 2,1 + 2,1 + 2,1 + 2,1 + 2, -/*FE8*/ 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2,1 + 2, 0, 1, 0, -/*FE9*/ 1, 2,1 + 2, 0, 1, 0, 1, 2,1 + 2, 0, 1, 2,1 + 2, 0, 1, 2, -/*FEA*/1 + 2, 0, 1, 2,1 + 2, 0, 1, 2,1 + 2, 0, 1, 0, 1, 0, 1, 0, -/*FEB*/ 1, 0, 1, 2,1 + 2, 0, 1, 2,1 + 2, 0, 1, 2,1 + 2, 0, 1, 2, -/*FEC*/1 + 2, 0, 1, 2,1 + 2, 0, 1, 2,1 + 2, 0, 1, 2,1 + 2, 0, 1, 2, -/*FED*/1 + 2, 0, 1, 2,1 + 2, 0, 1, 2,1 + 2, 0, 1, 2,1 + 2, 0, 1, 2, -/*FEE*/1 + 2, 0, 1, 2,1 + 2, 0, 1, 2,1 + 2, 0, 1, 2,1 + 2, 0, 1, 0, -/*FEF*/ 1, 0, 1, 2,1 + 2, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0 -}; - -static const UChar convertFBto06[] = -{ -/***********0******1******2******3******4******5******6******7******8******9******A******B******C******D******E******F***/ -/*FB5*/ 0x671, 0x671, 0x67B, 0x67B, 0x67B, 0x67B, 0x67E, 0x67E, 0x67E, 0x67E, 0, 0, 0, 0, 0x67A, 0x67A, -/*FB6*/ 0x67A, 0x67A, 0, 0, 0, 0, 0x679, 0x679, 0x679, 0x679, 0, 0, 0, 0, 0, 0, -/*FB7*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x686, 0x686, 0x686, 0x686, 0, 0, -/*FB8*/ 0, 0, 0x68D, 0x68D, 0x68C, 0x68C, 0x68E, 0x68E, 0x688, 0x688, 0x698, 0x698, 0x691, 0x691, 0x6A9, 0x6A9, -/*FB9*/ 0x6A9, 0x6A9, 0x6AF, 0x6AF, 0x6AF, 0x6AF, 0, 0, 0, 0, 0, 0, 0, 0, 0x6BA, 0x6BA, -/*FBA*/ 0x6BB, 0x6BB, 0x6BB, 0x6BB, 0x6C0, 0x6C0, 0x6C1, 0x6C1, 0x6C1, 0x6C1, 0x6BE, 0x6BE, 0x6BE, 0x6BE, 0x6d2, 0x6D2, -/*FBB*/ 0x6D3, 0x6D3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FBC*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FBD*/ 0, 0, 0, 0, 0, 0, 0, 0x6C7, 0x6C7, 0x6C6, 0x6C6, 0x6C8, 0x6C8, 0, 0x6CB, 0x6CB, -/*FBE*/ 0x6C5, 0x6C5, 0x6C9, 0x6C9, 0x6D0, 0x6D0, 0x6D0, 0x6D0, 0, 0, 0, 0, 0, 0, 0, 0, -/*FBF*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x6CC, 0x6CC, 0x6CC, 0x6CC -}; - -static const UChar convertFEto06[] = -{ -/***********0******1******2******3******4******5******6******7******8******9******A******B******C******D******E******F***/ -/*FE7*/ 0x64B, 0x64B, 0x64C, 0x64C, 0x64D, 0x64D, 0x64E, 0x64E, 0x64F, 0x64F, 0x650, 0x650, 0x651, 0x651, 0x652, 0x652, -/*FE8*/ 0x621, 0x622, 0x622, 0x623, 0x623, 0x624, 0x624, 0x625, 0x625, 0x626, 0x626, 0x626, 0x626, 0x627, 0x627, 0x628, -/*FE9*/ 0x628, 0x628, 0x628, 0x629, 0x629, 0x62A, 0x62A, 0x62A, 0x62A, 0x62B, 0x62B, 0x62B, 0x62B, 0x62C, 0x62C, 0x62C, -/*FEA*/ 0x62C, 0x62D, 0x62D, 0x62D, 0x62D, 0x62E, 0x62E, 0x62E, 0x62E, 0x62F, 0x62F, 0x630, 0x630, 0x631, 0x631, 0x632, -/*FEB*/ 0x632, 0x633, 0x633, 0x633, 0x633, 0x634, 0x634, 0x634, 0x634, 0x635, 0x635, 0x635, 0x635, 0x636, 0x636, 0x636, -/*FEC*/ 0x636, 0x637, 0x637, 0x637, 0x637, 0x638, 0x638, 0x638, 0x638, 0x639, 0x639, 0x639, 0x639, 0x63A, 0x63A, 0x63A, -/*FED*/ 0x63A, 0x641, 0x641, 0x641, 0x641, 0x642, 0x642, 0x642, 0x642, 0x643, 0x643, 0x643, 0x643, 0x644, 0x644, 0x644, -/*FEE*/ 0x644, 0x645, 0x645, 0x645, 0x645, 0x646, 0x646, 0x646, 0x646, 0x647, 0x647, 0x647, 0x647, 0x648, 0x648, 0x649, -/*FEF*/ 0x649, 0x64A, 0x64A, 0x64A, 0x64A, 0x65C, 0x65C, 0x65D, 0x65D, 0x65E, 0x65E, 0x65F, 0x65F -}; - -static const uint8_t shapeTable[4][4][4]= -{ - { {0,0,0,0}, {0,0,0,0}, {0,1,0,3}, {0,1,0,1} }, - { {0,0,2,2}, {0,0,1,2}, {0,1,1,2}, {0,1,1,3} }, - { {0,0,0,0}, {0,0,0,0}, {0,1,0,3}, {0,1,0,3} }, - { {0,0,1,2}, {0,0,1,2}, {0,1,1,2}, {0,1,1,3} } -}; - -/* - * This function shapes European digits to Arabic-Indic digits - * in-place, writing over the input characters. - * Since we know that we are only looking for BMP code points, - * we can safely just work with code units (again, at least UTF-16). - */ -static void -_shapeToArabicDigitsWithContext(UChar *s, int32_t length, - UChar digitBase, - UBool isLogical, UBool lastStrongWasAL) { - int32_t i; - UChar c; - - digitBase-=0x30; - - /* the iteration direction depends on the type of input */ - if(isLogical) { - for(i=0; i0; /* pre-decrement in the body */) { - c=s[--i]; - switch(ubidi_getClass(c)) { - case U_LEFT_TO_RIGHT: /* L */ - case U_RIGHT_TO_LEFT: /* R */ - lastStrongWasAL=false; - break; - case U_RIGHT_TO_LEFT_ARABIC: /* AL */ - lastStrongWasAL=true; - break; - case U_EUROPEAN_NUMBER: /* EN */ - if(lastStrongWasAL && (uint32_t)(c-0x30)<10) { - s[i]=(UChar)(digitBase+c); /* digitBase+(c-0x30) - digitBase was modified above */ - } - break; - default : - break; - } - } - } -} - -/* - *Name : invertBuffer - *Function : This function inverts the buffer, it's used - * in case the user specifies the buffer to be - * U_SHAPE_TEXT_DIRECTION_LOGICAL - */ -static void -invertBuffer(UChar *buffer, int32_t size, uint32_t /*options*/, int32_t lowlimit, int32_t highlimit) { - UChar temp; - int32_t i=0,j=0; - for(i=lowlimit,j=size-highlimit-1;i= 0x0622 && ch <= 0x06D3) { - return(araLink[ch-0x0622]); - } else if(ch == 0x200D) { - return(3); - } else if(ch >= 0x206D && ch <= 0x206F) { - return(4); - }else if(ch >= 0xFB50 && ch <= 0xFC62) { - return(presALink[ch-0xFB50]); - } else if(ch >= 0xFE70 && ch <= 0xFEFC) { - return(presBLink[ch-0xFE70]); - }else { - return(0); - } -} - -/* - *Name : countSpaces - *Function : Counts the number of spaces - * at each end of the logical buffer - */ -static void -countSpaces(UChar *dest, int32_t size, uint32_t /*options*/, int32_t *spacesCountl, int32_t *spacesCountr) { - int32_t i = 0; - int32_t countl = 0,countr = 0; - while((dest[i] == SPACE_CHAR) && (countl < size)) { - countl++; - i++; - } - if (countl < size) { /* the entire buffer is not all space */ - while(dest[size-1] == SPACE_CHAR) { - countr++; - size--; - } - } - *spacesCountl = countl; - *spacesCountr = countr; -} - -/* - *Name : isTashkeelChar - *Function : Returns 1 for Tashkeel characters in 06 range else return 0 - */ -static inline int32_t -isTashkeelChar(UChar ch) { - return (int32_t)( ch>=0x064B && ch<= 0x0652 ); -} - -/* - *Name : isTashkeelCharFE - *Function : Returns 1 for Tashkeel characters in FE range else return 0 - */ -static inline int32_t -isTashkeelCharFE(UChar ch) { - return (int32_t)( ch>=0xFE70 && ch<= 0xFE7F ); -} - -/* - *Name : isAlefChar - *Function : Returns 1 for Alef characters else return 0 - */ -static inline int32_t -isAlefChar(UChar ch) { - return (int32_t)( (ch==0x0622)||(ch==0x0623)||(ch==0x0625)||(ch==0x0627) ); -} - -/* - *Name : isLamAlefChar - *Function : Returns 1 for LamAlef characters else return 0 - */ -static inline int32_t -isLamAlefChar(UChar ch) { - return (int32_t)((ch>=0xFEF5)&&(ch<=0xFEFC) ); -} - -/*BIDI - *Name : isTailChar - *Function : returns 1 if the character matches one of the tail characters (0xfe73 or 0x200b) otherwise returns 0 - */ - -static inline int32_t -isTailChar(UChar ch) { - if(ch == OLD_TAIL_CHAR || ch == NEW_TAIL_CHAR){ - return 1; - }else{ - return 0; - } -} - -/*BIDI - *Name : isSeenTailFamilyChar - *Function : returns 1 if the character is a seen family isolated character - * in the FE range otherwise returns 0 - */ - -static inline int32_t -isSeenTailFamilyChar(UChar ch) { - if(ch >= 0xfeb1 && ch < 0xfebf){ - return tailFamilyIsolatedFinal [ch - 0xFEB1]; - }else{ - return 0; - } -} - - /* Name : isSeenFamilyChar - * Function : returns 1 if the character is a seen family character in the Unicode - * 06 range otherwise returns 0 - */ - -static inline int32_t -isSeenFamilyChar(UChar ch){ - if(ch >= 0x633 && ch <= 0x636){ - return 1; - }else { - return 0; - } -} - -/*Start of BIDI*/ -/* - *Name : isAlefMaksouraChar - *Function : returns 1 if the character is a Alef Maksoura Final or isolated - * otherwise returns 0 - */ -static inline int32_t -isAlefMaksouraChar(UChar ch) { - return (int32_t)( (ch == 0xFEEF) || ( ch == 0xFEF0) || (ch == 0x0649)); -} - -/* - * Name : isYehHamzaChar - * Function : returns 1 if the character is a yehHamza isolated or yehhamza - * final is found otherwise returns 0 - */ -static inline int32_t -isYehHamzaChar(UChar ch) { - if((ch==0xFE89)||(ch==0xFE8A)){ - return 1; - }else{ - return 0; - } -} - - /* - * Name: isTashkeelOnTatweelChar - * Function: Checks if the Tashkeel Character is on Tatweel or not,if the - * Tashkeel on tatweel (FE range), it returns 1 else if the - * Tashkeel with shadda on tatweel (FC range)return 2 otherwise - * returns 0 - */ -static inline int32_t -isTashkeelOnTatweelChar(UChar ch){ - if(ch >= 0xfe70 && ch <= 0xfe7f && ch != NEW_TAIL_CHAR && ch != 0xFE75 && ch != SHADDA_TATWEEL_CHAR) - { - return tashkeelMedial [ch - 0xFE70]; - }else if( (ch >= 0xfcf2 && ch <= 0xfcf4) || (ch == SHADDA_TATWEEL_CHAR)) { - return 2; - }else{ - return 0; - } -} - -/* - * Name: isIsolatedTashkeelChar - * Function: Checks if the Tashkeel Character is in the isolated form - * (i.e. Unicode FE range) returns 1 else if the Tashkeel - * with shadda is in the isolated form (i.e. Unicode FC range) - * returns 2 otherwise returns 0 - */ -static inline int32_t -isIsolatedTashkeelChar(UChar ch){ - if(ch >= 0xfe70 && ch <= 0xfe7f && ch != NEW_TAIL_CHAR && ch != 0xFE75){ - return (1 - tashkeelMedial [ch - 0xFE70]); - }else if(ch >= 0xfc5e && ch <= 0xfc63){ - return 1; - }else{ - return 0; - } -} - - - - -/* - *Name : calculateSize - *Function : This function calculates the destSize to be used in preflighting - * when the destSize is equal to 0 - * It is used also to calculate the new destsize in case the - * destination buffer will be resized. - */ - -static int32_t -calculateSize(const UChar *source, int32_t sourceLength, -int32_t destSize,uint32_t options) { - int32_t i = 0; - - int lamAlefOption = 0; - int tashkeelOption = 0; - - destSize = sourceLength; - - if (((options&U_SHAPE_LETTERS_MASK) == U_SHAPE_LETTERS_SHAPE || - ((options&U_SHAPE_LETTERS_MASK) == U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED )) && - ((options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_RESIZE )){ - lamAlefOption = 1; - } - if((options&U_SHAPE_LETTERS_MASK) == U_SHAPE_LETTERS_SHAPE && - ((options&U_SHAPE_TASHKEEL_MASK) == U_SHAPE_TASHKEEL_RESIZE ) ){ - tashkeelOption = 1; - } - - if(lamAlefOption || tashkeelOption){ - if((options&U_SHAPE_TEXT_DIRECTION_MASK)==U_SHAPE_TEXT_DIRECTION_VISUAL_LTR) { - for(i=0;i= 0) { - tempbuffer[i] = 0x0000; - i--; - count--; - } - - u_memcpy(dest, tempbuffer, sourceLength); - destSize = u_strlen(dest); - } - - lamAlefOption = 0; - - if (shapingMode == 0){ - if ( (options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_NEAR ){ - lamAlefOption = 1; - } - } - - if (lamAlefOption){ - /* Lam+Alef is already shaped into LamAlef + FFFF */ - i = 0; - while(i < sourceLength) { - if(lamAlefOption&&dest[i] == LAMALEF_SPACE_SUB){ - dest[i] = SPACE_CHAR; - } - i++; - } - destSize = sourceLength; - } - lamAlefOption = 0; - tashkeelOption = 0; - - if (shapingMode == 0) { - if ( ((options&U_SHAPE_LAMALEF_MASK) == shapeVars.uShapeLamalefBegin) || - (((options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_AUTO ) - && (shapeVars.spacesRelativeToTextBeginEnd==1)) ) { - lamAlefOption = 1; - } - if ( (options&U_SHAPE_TASHKEEL_MASK) == shapeVars.uShapeTashkeelBegin ) { - tashkeelOption = 1; - } - } - - if(lamAlefOption || tashkeelOption){ - uprv_memset(tempbuffer, 0, (sourceLength+1)*U_SIZEOF_UCHAR); - - i = j = sourceLength; count = 0; - - while(i >= 0) { - if ( (lamAlefOption && dest[i] == LAMALEF_SPACE_SUB) || - (tashkeelOption && dest[i] == TASHKEEL_SPACE_SUB) ){ - j++; - count++; - }else { - tempbuffer[j] = dest[i]; - } - i--; - j--; - } - - for(i=0 ;i < count; i++){ - tempbuffer[i] = SPACE_CHAR; - } - - u_memcpy(dest, tempbuffer, sourceLength); - destSize = sourceLength; - } - - - - lamAlefOption = 0; - tashkeelOption = 0; - - if (shapingMode == 0) { - if ( ((options&U_SHAPE_LAMALEF_MASK) == shapeVars.uShapeLamalefEnd) || - (((options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_AUTO ) - && (shapeVars.spacesRelativeToTextBeginEnd==0)) ) { - lamAlefOption = 1; - } - if ( (options&U_SHAPE_TASHKEEL_MASK) == shapeVars.uShapeTashkeelEnd ){ - tashkeelOption = 1; - } - } - - if(lamAlefOption || tashkeelOption){ - uprv_memset(tempbuffer, 0, (sourceLength+1)*U_SIZEOF_UCHAR); - - i = j = 0; count = 0; - while(i < sourceLength) { - if ( (lamAlefOption && dest[i] == LAMALEF_SPACE_SUB) || - (tashkeelOption && dest[i] == TASHKEEL_SPACE_SUB) ){ - j--; - count++; - }else { - tempbuffer[j] = dest[i]; - } - i++; - j++; - } - - while(count >= 0) { - tempbuffer[i] = SPACE_CHAR; - i--; - count--; - } - - u_memcpy(dest, tempbuffer, sourceLength); - destSize = sourceLength; - } - - - if(tempbuffer){ - uprv_free(tempbuffer); - } - - return destSize; -} - -/* - *Name :expandCompositCharAtBegin - *Function :Expands the LamAlef character to Lam and Alef consuming the required - * space from beginning of the buffer. If the text type was visual_LTR - * and the option U_SHAPE_SPACES_RELATIVE_TO_TEXT_BEGIN_END was selected - * the spaces will be located at end of buffer. - * If there are no spaces to expand the LamAlef, an error - * will be set to U_NO_SPACE_AVAILABLE as defined in utypes.h - */ - -static int32_t -expandCompositCharAtBegin(UChar *dest, int32_t sourceLength, int32_t destSize,UErrorCode *pErrorCode) { - int32_t i = 0,j = 0; - int32_t countl = 0; - UChar *tempbuffer=NULL; - - tempbuffer = (UChar *)uprv_malloc((sourceLength+1)*U_SIZEOF_UCHAR); - - /* Test for NULL */ - if(tempbuffer == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - - uprv_memset(tempbuffer, 0, (sourceLength+1)*U_SIZEOF_UCHAR); - - i = 0; - while(dest[i] == SPACE_CHAR) { - countl++; - i++; - } - - i = j = sourceLength-1; - - while(i >= 0 && j >= 0) { - if( countl>0 && isLamAlefChar(dest[i])) { - tempbuffer[j] = LAM_CHAR; - /* to ensure the array index is within the range */ - U_ASSERT(dest[i] >= 0xFEF5u - && dest[i]-0xFEF5u < UPRV_LENGTHOF(convertLamAlef)); - tempbuffer[j-1] = convertLamAlef[ dest[i] - 0xFEF5 ]; - j--; - countl--; - }else { - if( countl == 0 && isLamAlefChar(dest[i]) ) { - *pErrorCode=U_NO_SPACE_AVAILABLE; - } - tempbuffer[j] = dest[i]; - } - i--; - j--; - } - u_memcpy(dest, tempbuffer, sourceLength); - - uprv_free(tempbuffer); - - destSize = sourceLength; - return destSize; -} - -/* - *Name : expandCompositCharAtEnd - *Function : Expands the LamAlef character to Lam and Alef consuming the - * required space from end of the buffer. If the text type was - * Visual LTR and the option U_SHAPE_SPACES_RELATIVE_TO_TEXT_BEGIN_END - * was used, the spaces will be consumed from begin of buffer. If - * there are no spaces to expand the LamAlef, an error - * will be set to U_NO_SPACE_AVAILABLE as defined in utypes.h - */ - -static int32_t -expandCompositCharAtEnd(UChar *dest, int32_t sourceLength, int32_t destSize,UErrorCode *pErrorCode) { - int32_t i = 0,j = 0; - - int32_t countr = 0; - int32_t inpsize = sourceLength; - - UChar *tempbuffer=NULL; - tempbuffer = (UChar *)uprv_malloc((sourceLength+1)*U_SIZEOF_UCHAR); - - /* Test for NULL */ - if(tempbuffer == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - - uprv_memset(tempbuffer, 0, (sourceLength+1)*U_SIZEOF_UCHAR); - - while(dest[inpsize-1] == SPACE_CHAR) { - countr++; - inpsize--; - } - - i = sourceLength - countr - 1; - j = sourceLength - 1; - - while(i >= 0 && j >= 0) { - if( countr>0 && isLamAlefChar(dest[i]) ) { - tempbuffer[j] = LAM_CHAR; - tempbuffer[j-1] = convertLamAlef[ dest[i] - 0xFEF5 ]; - j--; - countr--; - }else { - if ((countr == 0) && isLamAlefChar(dest[i]) ) { - *pErrorCode=U_NO_SPACE_AVAILABLE; - } - tempbuffer[j] = dest[i]; - } - i--; - j--; - } - - if(countr > 0) { - u_memmove(tempbuffer, tempbuffer+countr, sourceLength); - if(u_strlen(tempbuffer) < sourceLength) { - for(i=sourceLength-1;i>=sourceLength-countr;i--) { - tempbuffer[i] = SPACE_CHAR; - } - } - } - u_memcpy(dest, tempbuffer, sourceLength); - - uprv_free(tempbuffer); - - destSize = sourceLength; - return destSize; -} - -/* - *Name : expandCompositCharAtNear - *Function : Expands the LamAlef character into Lam + Alef, YehHamza character - * into Yeh + Hamza, SeenFamily character into SeenFamily character - * + Tail, while consuming the space next to the character. - * If there are no spaces next to the character, an error - * will be set to U_NO_SPACE_AVAILABLE as defined in utypes.h - */ - -static int32_t -expandCompositCharAtNear(UChar *dest, int32_t sourceLength, int32_t destSize,UErrorCode *pErrorCode, - int yehHamzaOption, int seenTailOption, int lamAlefOption, struct uShapeVariables shapeVars) { - int32_t i = 0; - - - UChar lamalefChar, yehhamzaChar; - - for(i = 0 ;i<=sourceLength-1;i++) { - if (seenTailOption && isSeenTailFamilyChar(dest[i])) { - if ((i>0) && (dest[i-1] == SPACE_CHAR) ) { - dest[i-1] = shapeVars.tailChar; - }else { - *pErrorCode=U_NO_SPACE_AVAILABLE; - } - }else if(yehHamzaOption && (isYehHamzaChar(dest[i])) ) { - if ((i>0) && (dest[i-1] == SPACE_CHAR) ) { - yehhamzaChar = dest[i]; - dest[i] = yehHamzaToYeh[yehhamzaChar - YEH_HAMZAFE_CHAR]; - dest[i-1] = HAMZAFE_CHAR; - }else { - - *pErrorCode=U_NO_SPACE_AVAILABLE; - } - }else if(lamAlefOption && isLamAlefChar(dest[i+1])) { - if(dest[i] == SPACE_CHAR){ - lamalefChar = dest[i+1]; - dest[i+1] = LAM_CHAR; - dest[i] = convertLamAlef[ lamalefChar - 0xFEF5 ]; - }else { - *pErrorCode=U_NO_SPACE_AVAILABLE; - } - } - } - destSize = sourceLength; - return destSize; -} - /* - * Name : expandCompositChar - * Function : LamAlef, need special handling, since it expands from one - * character into two characters while shaping or deshaping. - * In order to expand it, near or far spaces according to the - * options user specifies. Also buffer size can be increased. - * - * For SeenFamily characters and YehHamza only the near option is - * supported, while for LamAlef we can take spaces from begin, end, - * near or even increase the buffer size. - * There is also the Auto option for LamAlef only, which will first - * search for a space at end, begin then near, respectively. - * If there are no spaces to expand these characters, an error will be set to - * U_NO_SPACE_AVAILABLE as defined in utypes.h - */ - -static int32_t -expandCompositChar(UChar *dest, int32_t sourceLength, - int32_t destSize,uint32_t options, - UErrorCode *pErrorCode, int shapingMode,struct uShapeVariables shapeVars) { - - int32_t i = 0,j = 0; - - UChar *tempbuffer=NULL; - int yehHamzaOption = 0; - int seenTailOption = 0; - int lamAlefOption = 0; - - if (shapingMode == 1){ - if ( (options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_AUTO){ - - if(shapeVars.spacesRelativeToTextBeginEnd == 0) { - destSize = expandCompositCharAtEnd(dest, sourceLength, destSize, pErrorCode); - - if(*pErrorCode == U_NO_SPACE_AVAILABLE) { - *pErrorCode = U_ZERO_ERROR; - destSize = expandCompositCharAtBegin(dest, sourceLength, destSize, pErrorCode); - } - }else { - destSize = expandCompositCharAtBegin(dest, sourceLength, destSize, pErrorCode); - - if(*pErrorCode == U_NO_SPACE_AVAILABLE) { - *pErrorCode = U_ZERO_ERROR; - destSize = expandCompositCharAtEnd(dest, sourceLength, destSize, pErrorCode); - } - } - - if(*pErrorCode == U_NO_SPACE_AVAILABLE) { - *pErrorCode = U_ZERO_ERROR; - destSize = expandCompositCharAtNear(dest, sourceLength, destSize, pErrorCode, yehHamzaOption, - seenTailOption, 1,shapeVars); - } - } - } - - if (shapingMode == 1){ - if ( (options&U_SHAPE_LAMALEF_MASK) == shapeVars.uShapeLamalefEnd){ - destSize = expandCompositCharAtEnd(dest, sourceLength, destSize, pErrorCode); - } - } - - if (shapingMode == 1){ - if ( (options&U_SHAPE_LAMALEF_MASK) == shapeVars.uShapeLamalefBegin){ - destSize = expandCompositCharAtBegin(dest, sourceLength, destSize, pErrorCode); - } - } - - if (shapingMode == 0){ - if ((options&U_SHAPE_YEHHAMZA_MASK) == U_SHAPE_YEHHAMZA_TWOCELL_NEAR){ - yehHamzaOption = 1; - } - if ((options&U_SHAPE_SEEN_MASK) == U_SHAPE_SEEN_TWOCELL_NEAR){ - seenTailOption = 1; - } - } - if (shapingMode == 1) { - if ( (options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_NEAR) { - lamAlefOption = 1; - } - } - - - if (yehHamzaOption || seenTailOption || lamAlefOption){ - destSize = expandCompositCharAtNear(dest, sourceLength, destSize, pErrorCode, yehHamzaOption, - seenTailOption,lamAlefOption,shapeVars); - } - - - if (shapingMode == 1){ - if ( (options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_RESIZE){ - destSize = calculateSize(dest,sourceLength,destSize,options); - tempbuffer = (UChar *)uprv_malloc((destSize+1)*U_SIZEOF_UCHAR); - - /* Test for NULL */ - if(tempbuffer == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - - uprv_memset(tempbuffer, 0, (destSize+1)*U_SIZEOF_UCHAR); - - i = j = 0; - while(i < destSize && j < destSize) { - if(isLamAlefChar(dest[i]) ) { - tempbuffer[j] = convertLamAlef[ dest[i] - 0xFEF5 ]; - tempbuffer[j+1] = LAM_CHAR; - j++; - }else { - tempbuffer[j] = dest[i]; - } - i++; - j++; - } - - u_memcpy(dest, tempbuffer, destSize); - } - } - - if(tempbuffer) { - uprv_free(tempbuffer); - } - return destSize; -} - -/* - *Name : shapeUnicode - *Function : Converts an Arabic Unicode buffer in 06xx Range into a shaped - * arabic Unicode buffer in FExx Range - */ -static int32_t -shapeUnicode(UChar *dest, int32_t sourceLength, - int32_t destSize,uint32_t options, - UErrorCode *pErrorCode, - int tashkeelFlag, struct uShapeVariables shapeVars) { - - int32_t i, iend; - int32_t step; - int32_t lastPos,Nx, Nw; - unsigned int Shape; - int32_t lamalef_found = 0; - int32_t seenfamFound = 0, yehhamzaFound =0, tashkeelFound = 0; - UChar prevLink = 0, lastLink = 0, currLink, nextLink = 0; - UChar wLamalef; - - /* - * Converts the input buffer from FExx Range into 06xx Range - * to make sure that all characters are in the 06xx range - * even the lamalef is converted to the special region in - * the 06xx range - */ - if ((options & U_SHAPE_PRESERVE_PRESENTATION_MASK) == U_SHAPE_PRESERVE_PRESENTATION_NOOP) { - for (i = 0; i < sourceLength; i++) { - UChar inputChar = dest[i]; - if ( (inputChar >= 0xFB50) && (inputChar <= 0xFBFF)) { - UChar c = convertFBto06 [ (inputChar - 0xFB50) ]; - if (c != 0) - dest[i] = c; - } else if ( (inputChar >= 0xFE70) && (inputChar <= 0xFEFC)) { - dest[i] = convertFEto06 [ (inputChar - 0xFE70) ] ; - } else { - dest[i] = inputChar ; - } - } - } - - - /* sets the index to the end of the buffer, together with the step point to -1 */ - i = sourceLength - 1; - iend = -1; - step = -1; - - /* - * This function resolves the link between the characters . - * Arabic characters have four forms : - * Isolated Form, Initial Form, Middle Form and Final Form - */ - currLink = getLink(dest[i]); - - lastPos = i; - Nx = -2, Nw = 0; - - while (i != iend) { - /* If high byte of currLink > 0 then more than one shape */ - if ((currLink & 0xFF00) > 0 || (getLink(dest[i]) & IRRELEVANT) != 0) { - Nw = i + step; - while (Nx < 0) { /* we need to know about next char */ - if(Nw == iend) { - nextLink = 0; - Nx = 3000; - } else { - nextLink = getLink(dest[Nw]); - if((nextLink & IRRELEVANT) == 0) { - Nx = Nw; - } else { - Nw = Nw + step; - } - } - } - - if ( ((currLink & ALEFTYPE) > 0) && ((lastLink & LAMTYPE) > 0) ) { - lamalef_found = 1; - wLamalef = changeLamAlef(dest[i]); /*get from 0x065C-0x065f */ - if ( wLamalef != 0) { - dest[i] = LAMALEF_SPACE_SUB; /* The default case is to drop the Alef and replace */ - dest[lastPos] =wLamalef; /* it by LAMALEF_SPACE_SUB which is the last character in the */ - i=lastPos; /* unicode private use area, this is done to make */ - } /* sure that removeLamAlefSpaces() handles only the */ - lastLink = prevLink; /* spaces generated during lamalef generation. */ - currLink = getLink(wLamalef); /* LAMALEF_SPACE_SUB is added here and is replaced by spaces */ - } /* in removeLamAlefSpaces() */ - - if ((i > 0) && (dest[i-1] == SPACE_CHAR)){ - if ( isSeenFamilyChar(dest[i])) { - seenfamFound = 1; - } else if (dest[i] == YEH_HAMZA_CHAR) { - yehhamzaFound = 1; - } - } - else if(i==0){ - if ( isSeenFamilyChar(dest[i])){ - seenfamFound = 1; - } else if (dest[i] == YEH_HAMZA_CHAR) { - yehhamzaFound = 1; - } - } - - /* - * get the proper shape according to link ability of neighbors - * and of character; depends on the order of the shapes - * (isolated, initial, middle, final) in the compatibility area - */ - Shape = shapeTable[nextLink & (LINKR + LINKL)] - [lastLink & (LINKR + LINKL)] - [currLink & (LINKR + LINKL)]; - - if ((currLink & (LINKR+LINKL)) == 1) { - Shape &= 1; - } else if(isTashkeelChar(dest[i])) { - if( (lastLink & LINKL) && (nextLink & LINKR) && (tashkeelFlag == 1) && - dest[i] != 0x064C && dest[i] != 0x064D ) - { - Shape = 1; - if( (nextLink&ALEFTYPE) == ALEFTYPE && (lastLink&LAMTYPE) == LAMTYPE ) { - Shape = 0; - } - } else if(tashkeelFlag == 2 && dest[i] == SHADDA06_CHAR){ - Shape = 1; - } else { - Shape = 0; - } - } - if ((dest[i] ^ 0x0600) < 0x100) { - if ( isTashkeelChar(dest[i]) ){ - if (tashkeelFlag == 2 && dest[i] != SHADDA06_CHAR){ - dest[i] = TASHKEEL_SPACE_SUB; - tashkeelFound = 1; - } else { - /* to ensure the array index is within the range */ - U_ASSERT(dest[i] >= 0x064Bu - && dest[i]-0x064Bu < UPRV_LENGTHOF(IrrelevantPos)); - dest[i] = 0xFE70 + IrrelevantPos[(dest[i] - 0x064B)] + static_cast(Shape); - } - }else if ((currLink & APRESENT) > 0) { - dest[i] = (UChar)(0xFB50 + (currLink >> 8) + Shape); - }else if ((currLink >> 8) > 0 && (currLink & IRRELEVANT) == 0) { - dest[i] = (UChar)(0xFE70 + (currLink >> 8) + Shape); - } - } - } - - /* move one notch forward */ - if ((currLink & IRRELEVANT) == 0) { - prevLink = lastLink; - lastLink = currLink; - lastPos = i; - } - - i = i + step; - if (i == Nx) { - currLink = nextLink; - Nx = -2; - } else if(i != iend) { - currLink = getLink(dest[i]); - } - } - destSize = sourceLength; - if ( (lamalef_found != 0 ) || (tashkeelFound != 0) ){ - destSize = handleGeneratedSpaces(dest,sourceLength,destSize,options,pErrorCode, shapeVars); - } - - if ( (seenfamFound != 0) || (yehhamzaFound != 0) ) { - destSize = expandCompositChar(dest, sourceLength,destSize,options,pErrorCode, SHAPE_MODE,shapeVars); - } - return destSize; -} - -/* - *Name : deShapeUnicode - *Function : Converts an Arabic Unicode buffer in FExx Range into unshaped - * arabic Unicode buffer in 06xx Range - */ -static int32_t -deShapeUnicode(UChar *dest, int32_t sourceLength, - int32_t destSize,uint32_t options, - UErrorCode *pErrorCode, struct uShapeVariables shapeVars) { - int32_t i = 0; - int32_t lamalef_found = 0; - int32_t yehHamzaComposeEnabled = 0; - int32_t seenComposeEnabled = 0; - - yehHamzaComposeEnabled = ((options&U_SHAPE_YEHHAMZA_MASK) == U_SHAPE_YEHHAMZA_TWOCELL_NEAR) ? 1 : 0; - seenComposeEnabled = ((options&U_SHAPE_SEEN_MASK) == U_SHAPE_SEEN_TWOCELL_NEAR)? 1 : 0; - - /* - *This for loop changes the buffer from the Unicode FE range to - *the Unicode 06 range - */ - - for(i = 0; i < sourceLength; i++) { - UChar inputChar = dest[i]; - if ( (inputChar >= 0xFB50) && (inputChar <= 0xFBFF)) { /* FBxx Arabic range */ - UChar c = convertFBto06 [ (inputChar - 0xFB50) ]; - if (c != 0) - dest[i] = c; - } else if( (yehHamzaComposeEnabled == 1) && ((inputChar == HAMZA06_CHAR) || (inputChar == HAMZAFE_CHAR)) - && (i < (sourceLength - 1)) && isAlefMaksouraChar(dest[i+1] )) { - dest[i] = SPACE_CHAR; - dest[i+1] = YEH_HAMZA_CHAR; - } else if ( (seenComposeEnabled == 1) && (isTailChar(inputChar)) && (i< (sourceLength - 1)) - && (isSeenTailFamilyChar(dest[i+1])) ) { - dest[i] = SPACE_CHAR; - } else if (( inputChar >= 0xFE70) && (inputChar <= 0xFEF4 )) { /* FExx Arabic range */ - dest[i] = convertFEto06 [ (inputChar - 0xFE70) ]; - } else { - dest[i] = inputChar ; - } - - if( isLamAlefChar(dest[i]) ) - lamalef_found = 1; - } - - destSize = sourceLength; - if (lamalef_found != 0){ - destSize = expandCompositChar(dest,sourceLength,destSize,options,pErrorCode,DESHAPE_MODE, shapeVars); - } - return destSize; -} - -/* - **************************************** - * u_shapeArabic - **************************************** - */ - -U_CAPI int32_t U_EXPORT2 -u_shapeArabic(const UChar *source, int32_t sourceLength, - UChar *dest, int32_t destCapacity, - uint32_t options, - UErrorCode *pErrorCode) { - - int32_t destLength; - struct uShapeVariables shapeVars = { OLD_TAIL_CHAR,U_SHAPE_LAMALEF_BEGIN,U_SHAPE_LAMALEF_END,U_SHAPE_TASHKEEL_BEGIN,U_SHAPE_TASHKEEL_END,0}; - - /* usual error checking */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - /* make sure that no reserved options values are used; allow dest==NULL only for preflighting */ - if( source==NULL || sourceLength<-1 || (dest==NULL && destCapacity!=0) || destCapacity<0 || - (((options&U_SHAPE_TASHKEEL_MASK) > 0) && - ((options&U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED) == U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED) ) || - (((options&U_SHAPE_TASHKEEL_MASK) > 0) && - ((options&U_SHAPE_LETTERS_MASK) == U_SHAPE_LETTERS_UNSHAPE)) || - (options&U_SHAPE_DIGIT_TYPE_RESERVED)==U_SHAPE_DIGIT_TYPE_RESERVED || - (options&U_SHAPE_DIGITS_MASK)==U_SHAPE_DIGITS_RESERVED || - ((options&U_SHAPE_LAMALEF_MASK) != U_SHAPE_LAMALEF_RESIZE && - (options&U_SHAPE_AGGREGATE_TASHKEEL_MASK) != 0) || - ((options&U_SHAPE_AGGREGATE_TASHKEEL_MASK) == U_SHAPE_AGGREGATE_TASHKEEL && - (options&U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED) != U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED) - ) - { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - /* Validate lamalef options */ - if(((options&U_SHAPE_LAMALEF_MASK) > 0)&& - !(((options & U_SHAPE_LAMALEF_MASK)==U_SHAPE_LAMALEF_BEGIN) || - ((options & U_SHAPE_LAMALEF_MASK)==U_SHAPE_LAMALEF_END ) || - ((options & U_SHAPE_LAMALEF_MASK)==U_SHAPE_LAMALEF_RESIZE )|| - ((options & U_SHAPE_LAMALEF_MASK)==U_SHAPE_LAMALEF_AUTO) || - ((options & U_SHAPE_LAMALEF_MASK)==U_SHAPE_LAMALEF_NEAR))) - { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - /* Validate Tashkeel options */ - if(((options&U_SHAPE_TASHKEEL_MASK) > 0)&& - !(((options & U_SHAPE_TASHKEEL_MASK)==U_SHAPE_TASHKEEL_BEGIN) || - ((options & U_SHAPE_TASHKEEL_MASK)==U_SHAPE_TASHKEEL_END ) - ||((options & U_SHAPE_TASHKEEL_MASK)==U_SHAPE_TASHKEEL_RESIZE )|| - ((options & U_SHAPE_TASHKEEL_MASK)==U_SHAPE_TASHKEEL_REPLACE_BY_TATWEEL))) - { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - /* determine the source length */ - if(sourceLength==-1) { - sourceLength=u_strlen(source); - } - if(sourceLength<=0) { - return u_terminateUChars(dest, destCapacity, 0, pErrorCode); - } - - /* check that source and destination do not overlap */ - if( dest!=NULL && - ((source<=dest && dest0) { - int32_t logical_order = (options&U_SHAPE_TEXT_DIRECTION_MASK) == U_SHAPE_TEXT_DIRECTION_LOGICAL; - int32_t aggregate_tashkeel = - (options&(U_SHAPE_AGGREGATE_TASHKEEL_MASK+U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED)) == - (U_SHAPE_AGGREGATE_TASHKEEL+U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED); - int step=logical_order?1:-1; - int j=logical_order?-1:2*sourceLength; - int i=logical_order?-1:sourceLength; - int end=logical_order?sourceLength:-1; - int aggregation_possible = 1; - UChar prev = 0; - UChar prevLink, currLink = 0; - int newSourceLength = 0; - tempsource = (UChar *)uprv_malloc(2*sourceLength*U_SIZEOF_UCHAR); - if(tempsource == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - - while ((i+=step) != end) { - prevLink = currLink; - currLink = getLink(source[i]); - if (aggregate_tashkeel && ((prevLink|currLink)&COMBINE) == COMBINE && aggregation_possible) { - aggregation_possible = 0; - tempsource[j] = (prevdestCapacity) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - if (tempsource != NULL) uprv_free(tempsource); - return outputSize; - } - - /* - * need a temporary buffer of size max(outputSize, sourceLength) - * because at first we copy source->temp - */ - if(sourceLength>outputSize) { - outputSize=sourceLength; - } - - /* Start of Arabic letter shaping part */ - if(outputSize<=UPRV_LENGTHOF(buffer)) { - outputSize=UPRV_LENGTHOF(buffer); - tempbuffer=buffer; - } else { - tempbuffer = (UChar *)uprv_malloc(outputSize*U_SIZEOF_UCHAR); - - /*Test for NULL*/ - if(tempbuffer == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - if (tempsource != NULL) uprv_free(tempsource); - return 0; - } - } - u_memcpy(tempbuffer, source, sourceLength); - if (tempsource != NULL){ - uprv_free(tempsource); - } - - if(sourceLength 0 - && ((options&U_SHAPE_TASHKEEL_MASK) !=U_SHAPE_TASHKEEL_REPLACE_BY_TATWEEL)) { - /* Call the shaping function with tashkeel flag == 2 for removal of tashkeel */ - destLength = shapeUnicode(tempbuffer,sourceLength,destCapacity,options,pErrorCode,2,shapeVars); - }else { - /* default Call the shaping function with tashkeel flag == 1 */ - destLength = shapeUnicode(tempbuffer,sourceLength,destCapacity,options,pErrorCode,1,shapeVars); - - /*After shaping text check if user wants to remove tashkeel and replace it with tatweel*/ - if( (options&U_SHAPE_TASHKEEL_MASK) == U_SHAPE_TASHKEEL_REPLACE_BY_TATWEEL){ - destLength = handleTashkeelWithTatweel(tempbuffer,destLength,destCapacity,options,pErrorCode); - } - } - break; - case U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED : - /* Call the shaping function with tashkeel flag == 0 */ - destLength = shapeUnicode(tempbuffer,sourceLength,destCapacity,options,pErrorCode,0,shapeVars); - break; - - case U_SHAPE_LETTERS_UNSHAPE : - /* Call the deshaping function */ - destLength = deShapeUnicode(tempbuffer,sourceLength,destCapacity,options,pErrorCode,shapeVars); - break; - default : - /* will never occur because of validity checks above */ - destLength = 0; - break; - } - - /* - * TODO: (markus 2002aug01) - * For as long as we always preflight the outputSize above - * we should U_ASSERT(outputSize==destLength) - * except for the adjustment above before the tempbuffer allocation - */ - - if((options&U_SHAPE_TEXT_DIRECTION_MASK) == U_SHAPE_TEXT_DIRECTION_LOGICAL) { - countSpaces(tempbuffer,destLength,options,&spacesCountl,&spacesCountr); - invertBuffer(tempbuffer,destLength,options,spacesCountl,spacesCountr); - } - u_memcpy(dest, tempbuffer, uprv_min(destLength, destCapacity)); - - if(tempbuffer!=buffer) { - uprv_free(tempbuffer); - } - - if(destLength>destCapacity) { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - return destLength; - } - - /* End of Arabic letter shaping part */ - } else { - /* - * No letter shaping: - * just make sure the destination is large enough and copy the string. - */ - if(destCapacity0; /* pre-decrement in the body */) { + c=s[--i]; + switch(ubidi_getClass(c)) { + case U_LEFT_TO_RIGHT: /* L */ + case U_RIGHT_TO_LEFT: /* R */ + lastStrongWasAL=false; + break; + case U_RIGHT_TO_LEFT_ARABIC: /* AL */ + lastStrongWasAL=true; + break; + case U_EUROPEAN_NUMBER: /* EN */ + if(lastStrongWasAL && (uint32_t)(c-0x30)<10) { + s[i]=(char16_t)(digitBase+c); /* digitBase+(c-0x30) - digitBase was modified above */ + } + break; + default : + break; + } + } + } +} + +/* + *Name : invertBuffer + *Function : This function inverts the buffer, it's used + * in case the user specifies the buffer to be + * U_SHAPE_TEXT_DIRECTION_LOGICAL + */ +static void +invertBuffer(char16_t *buffer, int32_t size, uint32_t /*options*/, int32_t lowlimit, int32_t highlimit) { + char16_t temp; + int32_t i=0,j=0; + for(i=lowlimit,j=size-highlimit-1;i= 0x0622 && ch <= 0x06D3) { + return(araLink[ch-0x0622]); + } else if(ch == 0x200D) { + return(3); + } else if(ch >= 0x206D && ch <= 0x206F) { + return(4); + }else if(ch >= 0xFB50 && ch <= 0xFC62) { + return(presALink[ch-0xFB50]); + } else if(ch >= 0xFE70 && ch <= 0xFEFC) { + return(presBLink[ch-0xFE70]); + }else { + return(0); + } +} + +/* + *Name : countSpaces + *Function : Counts the number of spaces + * at each end of the logical buffer + */ +static void +countSpaces(char16_t *dest, int32_t size, uint32_t /*options*/, int32_t *spacesCountl, int32_t *spacesCountr) { + int32_t i = 0; + int32_t countl = 0,countr = 0; + while((dest[i] == SPACE_CHAR) && (countl < size)) { + countl++; + i++; + } + if (countl < size) { /* the entire buffer is not all space */ + while(dest[size-1] == SPACE_CHAR) { + countr++; + size--; + } + } + *spacesCountl = countl; + *spacesCountr = countr; +} + +/* + *Name : isTashkeelChar + *Function : Returns 1 for Tashkeel characters in 06 range else return 0 + */ +static inline int32_t +isTashkeelChar(char16_t ch) { + return (int32_t)( ch>=0x064B && ch<= 0x0652 ); +} + +/* + *Name : isTashkeelCharFE + *Function : Returns 1 for Tashkeel characters in FE range else return 0 + */ +static inline int32_t +isTashkeelCharFE(char16_t ch) { + return (int32_t)( ch>=0xFE70 && ch<= 0xFE7F ); +} + +/* + *Name : isAlefChar + *Function : Returns 1 for Alef characters else return 0 + */ +static inline int32_t +isAlefChar(char16_t ch) { + return (int32_t)( (ch==0x0622)||(ch==0x0623)||(ch==0x0625)||(ch==0x0627) ); +} + +/* + *Name : isLamAlefChar + *Function : Returns 1 for LamAlef characters else return 0 + */ +static inline int32_t +isLamAlefChar(char16_t ch) { + return (int32_t)((ch>=0xFEF5)&&(ch<=0xFEFC) ); +} + +/*BIDI + *Name : isTailChar + *Function : returns 1 if the character matches one of the tail characters (0xfe73 or 0x200b) otherwise returns 0 + */ + +static inline int32_t +isTailChar(char16_t ch) { + if(ch == OLD_TAIL_CHAR || ch == NEW_TAIL_CHAR){ + return 1; + }else{ + return 0; + } +} + +/*BIDI + *Name : isSeenTailFamilyChar + *Function : returns 1 if the character is a seen family isolated character + * in the FE range otherwise returns 0 + */ + +static inline int32_t +isSeenTailFamilyChar(char16_t ch) { + if(ch >= 0xfeb1 && ch < 0xfebf){ + return tailFamilyIsolatedFinal [ch - 0xFEB1]; + }else{ + return 0; + } +} + + /* Name : isSeenFamilyChar + * Function : returns 1 if the character is a seen family character in the Unicode + * 06 range otherwise returns 0 + */ + +static inline int32_t +isSeenFamilyChar(char16_t ch){ + if(ch >= 0x633 && ch <= 0x636){ + return 1; + }else { + return 0; + } +} + +/*Start of BIDI*/ +/* + *Name : isAlefMaksouraChar + *Function : returns 1 if the character is a Alef Maksoura Final or isolated + * otherwise returns 0 + */ +static inline int32_t +isAlefMaksouraChar(char16_t ch) { + return (int32_t)( (ch == 0xFEEF) || ( ch == 0xFEF0) || (ch == 0x0649)); +} + +/* + * Name : isYehHamzaChar + * Function : returns 1 if the character is a yehHamza isolated or yehhamza + * final is found otherwise returns 0 + */ +static inline int32_t +isYehHamzaChar(char16_t ch) { + if((ch==0xFE89)||(ch==0xFE8A)){ + return 1; + }else{ + return 0; + } +} + + /* + * Name: isTashkeelOnTatweelChar + * Function: Checks if the Tashkeel Character is on Tatweel or not,if the + * Tashkeel on tatweel (FE range), it returns 1 else if the + * Tashkeel with shadda on tatweel (FC range)return 2 otherwise + * returns 0 + */ +static inline int32_t +isTashkeelOnTatweelChar(char16_t ch){ + if(ch >= 0xfe70 && ch <= 0xfe7f && ch != NEW_TAIL_CHAR && ch != 0xFE75 && ch != SHADDA_TATWEEL_CHAR) + { + return tashkeelMedial [ch - 0xFE70]; + }else if( (ch >= 0xfcf2 && ch <= 0xfcf4) || (ch == SHADDA_TATWEEL_CHAR)) { + return 2; + }else{ + return 0; + } +} + +/* + * Name: isIsolatedTashkeelChar + * Function: Checks if the Tashkeel Character is in the isolated form + * (i.e. Unicode FE range) returns 1 else if the Tashkeel + * with shadda is in the isolated form (i.e. Unicode FC range) + * returns 2 otherwise returns 0 + */ +static inline int32_t +isIsolatedTashkeelChar(char16_t ch){ + if(ch >= 0xfe70 && ch <= 0xfe7f && ch != NEW_TAIL_CHAR && ch != 0xFE75){ + return (1 - tashkeelMedial [ch - 0xFE70]); + }else if(ch >= 0xfc5e && ch <= 0xfc63){ + return 1; + }else{ + return 0; + } +} + + + + +/* + *Name : calculateSize + *Function : This function calculates the destSize to be used in preflighting + * when the destSize is equal to 0 + * It is used also to calculate the new destsize in case the + * destination buffer will be resized. + */ + +static int32_t +calculateSize(const char16_t *source, int32_t sourceLength, +int32_t destSize,uint32_t options) { + int32_t i = 0; + + int lamAlefOption = 0; + int tashkeelOption = 0; + + destSize = sourceLength; + + if (((options&U_SHAPE_LETTERS_MASK) == U_SHAPE_LETTERS_SHAPE || + ((options&U_SHAPE_LETTERS_MASK) == U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED )) && + ((options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_RESIZE )){ + lamAlefOption = 1; + } + if((options&U_SHAPE_LETTERS_MASK) == U_SHAPE_LETTERS_SHAPE && + ((options&U_SHAPE_TASHKEEL_MASK) == U_SHAPE_TASHKEEL_RESIZE ) ){ + tashkeelOption = 1; + } + + if(lamAlefOption || tashkeelOption){ + if((options&U_SHAPE_TEXT_DIRECTION_MASK)==U_SHAPE_TEXT_DIRECTION_VISUAL_LTR) { + for(i=0;i= 0) { + tempbuffer[i] = 0x0000; + i--; + count--; + } + + u_memcpy(dest, tempbuffer, sourceLength); + destSize = u_strlen(dest); + } + + lamAlefOption = 0; + + if (shapingMode == 0){ + if ( (options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_NEAR ){ + lamAlefOption = 1; + } + } + + if (lamAlefOption){ + /* Lam+Alef is already shaped into LamAlef + FFFF */ + i = 0; + while(i < sourceLength) { + if(lamAlefOption&&dest[i] == LAMALEF_SPACE_SUB){ + dest[i] = SPACE_CHAR; + } + i++; + } + destSize = sourceLength; + } + lamAlefOption = 0; + tashkeelOption = 0; + + if (shapingMode == 0) { + if ( ((options&U_SHAPE_LAMALEF_MASK) == shapeVars.uShapeLamalefBegin) || + (((options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_AUTO ) + && (shapeVars.spacesRelativeToTextBeginEnd==1)) ) { + lamAlefOption = 1; + } + if ( (options&U_SHAPE_TASHKEEL_MASK) == shapeVars.uShapeTashkeelBegin ) { + tashkeelOption = 1; + } + } + + if(lamAlefOption || tashkeelOption){ + uprv_memset(tempbuffer, 0, (sourceLength+1)*U_SIZEOF_UCHAR); + + i = j = sourceLength; count = 0; + + while(i >= 0) { + if ( (lamAlefOption && dest[i] == LAMALEF_SPACE_SUB) || + (tashkeelOption && dest[i] == TASHKEEL_SPACE_SUB) ){ + j++; + count++; + }else { + tempbuffer[j] = dest[i]; + } + i--; + j--; + } + + for(i=0 ;i < count; i++){ + tempbuffer[i] = SPACE_CHAR; + } + + u_memcpy(dest, tempbuffer, sourceLength); + destSize = sourceLength; + } + + + + lamAlefOption = 0; + tashkeelOption = 0; + + if (shapingMode == 0) { + if ( ((options&U_SHAPE_LAMALEF_MASK) == shapeVars.uShapeLamalefEnd) || + (((options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_AUTO ) + && (shapeVars.spacesRelativeToTextBeginEnd==0)) ) { + lamAlefOption = 1; + } + if ( (options&U_SHAPE_TASHKEEL_MASK) == shapeVars.uShapeTashkeelEnd ){ + tashkeelOption = 1; + } + } + + if(lamAlefOption || tashkeelOption){ + uprv_memset(tempbuffer, 0, (sourceLength+1)*U_SIZEOF_UCHAR); + + i = j = 0; count = 0; + while(i < sourceLength) { + if ( (lamAlefOption && dest[i] == LAMALEF_SPACE_SUB) || + (tashkeelOption && dest[i] == TASHKEEL_SPACE_SUB) ){ + j--; + count++; + }else { + tempbuffer[j] = dest[i]; + } + i++; + j++; + } + + while(count >= 0) { + tempbuffer[i] = SPACE_CHAR; + i--; + count--; + } + + u_memcpy(dest, tempbuffer, sourceLength); + destSize = sourceLength; + } + + + if(tempbuffer){ + uprv_free(tempbuffer); + } + + return destSize; +} + +/* + *Name :expandCompositCharAtBegin + *Function :Expands the LamAlef character to Lam and Alef consuming the required + * space from beginning of the buffer. If the text type was visual_LTR + * and the option U_SHAPE_SPACES_RELATIVE_TO_TEXT_BEGIN_END was selected + * the spaces will be located at end of buffer. + * If there are no spaces to expand the LamAlef, an error + * will be set to U_NO_SPACE_AVAILABLE as defined in utypes.h + */ + +static int32_t +expandCompositCharAtBegin(char16_t *dest, int32_t sourceLength, int32_t destSize,UErrorCode *pErrorCode) { + int32_t i = 0,j = 0; + int32_t countl = 0; + char16_t *tempbuffer=nullptr; + + tempbuffer = (char16_t *)uprv_malloc((sourceLength+1)*U_SIZEOF_UCHAR); + + /* Test for nullptr */ + if(tempbuffer == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + + uprv_memset(tempbuffer, 0, (sourceLength+1)*U_SIZEOF_UCHAR); + + i = 0; + while(dest[i] == SPACE_CHAR) { + countl++; + i++; + } + + i = j = sourceLength-1; + + while(i >= 0 && j >= 0) { + if( countl>0 && isLamAlefChar(dest[i])) { + tempbuffer[j] = LAM_CHAR; + /* to ensure the array index is within the range */ + U_ASSERT(dest[i] >= 0xFEF5u + && dest[i]-0xFEF5u < UPRV_LENGTHOF(convertLamAlef)); + tempbuffer[j-1] = convertLamAlef[ dest[i] - 0xFEF5 ]; + j--; + countl--; + }else { + if( countl == 0 && isLamAlefChar(dest[i]) ) { + *pErrorCode=U_NO_SPACE_AVAILABLE; + } + tempbuffer[j] = dest[i]; + } + i--; + j--; + } + u_memcpy(dest, tempbuffer, sourceLength); + + uprv_free(tempbuffer); + + destSize = sourceLength; + return destSize; +} + +/* + *Name : expandCompositCharAtEnd + *Function : Expands the LamAlef character to Lam and Alef consuming the + * required space from end of the buffer. If the text type was + * Visual LTR and the option U_SHAPE_SPACES_RELATIVE_TO_TEXT_BEGIN_END + * was used, the spaces will be consumed from begin of buffer. If + * there are no spaces to expand the LamAlef, an error + * will be set to U_NO_SPACE_AVAILABLE as defined in utypes.h + */ + +static int32_t +expandCompositCharAtEnd(char16_t *dest, int32_t sourceLength, int32_t destSize,UErrorCode *pErrorCode) { + int32_t i = 0,j = 0; + + int32_t countr = 0; + int32_t inpsize = sourceLength; + + char16_t *tempbuffer=nullptr; + tempbuffer = (char16_t *)uprv_malloc((sourceLength+1)*U_SIZEOF_UCHAR); + + /* Test for nullptr */ + if(tempbuffer == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + + uprv_memset(tempbuffer, 0, (sourceLength+1)*U_SIZEOF_UCHAR); + + while(dest[inpsize-1] == SPACE_CHAR) { + countr++; + inpsize--; + } + + i = sourceLength - countr - 1; + j = sourceLength - 1; + + while(i >= 0 && j >= 0) { + if( countr>0 && isLamAlefChar(dest[i]) ) { + tempbuffer[j] = LAM_CHAR; + tempbuffer[j-1] = convertLamAlef[ dest[i] - 0xFEF5 ]; + j--; + countr--; + }else { + if ((countr == 0) && isLamAlefChar(dest[i]) ) { + *pErrorCode=U_NO_SPACE_AVAILABLE; + } + tempbuffer[j] = dest[i]; + } + i--; + j--; + } + + if(countr > 0) { + u_memmove(tempbuffer, tempbuffer+countr, sourceLength); + if(u_strlen(tempbuffer) < sourceLength) { + for(i=sourceLength-1;i>=sourceLength-countr;i--) { + tempbuffer[i] = SPACE_CHAR; + } + } + } + u_memcpy(dest, tempbuffer, sourceLength); + + uprv_free(tempbuffer); + + destSize = sourceLength; + return destSize; +} + +/* + *Name : expandCompositCharAtNear + *Function : Expands the LamAlef character into Lam + Alef, YehHamza character + * into Yeh + Hamza, SeenFamily character into SeenFamily character + * + Tail, while consuming the space next to the character. + * If there are no spaces next to the character, an error + * will be set to U_NO_SPACE_AVAILABLE as defined in utypes.h + */ + +static int32_t +expandCompositCharAtNear(char16_t *dest, int32_t sourceLength, int32_t destSize,UErrorCode *pErrorCode, + int yehHamzaOption, int seenTailOption, int lamAlefOption, struct uShapeVariables shapeVars) { + int32_t i = 0; + + + char16_t lamalefChar, yehhamzaChar; + + for(i = 0 ;i<=sourceLength-1;i++) { + if (seenTailOption && isSeenTailFamilyChar(dest[i])) { + if ((i>0) && (dest[i-1] == SPACE_CHAR) ) { + dest[i-1] = shapeVars.tailChar; + }else { + *pErrorCode=U_NO_SPACE_AVAILABLE; + } + }else if(yehHamzaOption && (isYehHamzaChar(dest[i])) ) { + if ((i>0) && (dest[i-1] == SPACE_CHAR) ) { + yehhamzaChar = dest[i]; + dest[i] = yehHamzaToYeh[yehhamzaChar - YEH_HAMZAFE_CHAR]; + dest[i-1] = HAMZAFE_CHAR; + }else { + + *pErrorCode=U_NO_SPACE_AVAILABLE; + } + }else if(lamAlefOption && isLamAlefChar(dest[i+1])) { + if(dest[i] == SPACE_CHAR){ + lamalefChar = dest[i+1]; + dest[i+1] = LAM_CHAR; + dest[i] = convertLamAlef[ lamalefChar - 0xFEF5 ]; + }else { + *pErrorCode=U_NO_SPACE_AVAILABLE; + } + } + } + destSize = sourceLength; + return destSize; +} + /* + * Name : expandCompositChar + * Function : LamAlef, need special handling, since it expands from one + * character into two characters while shaping or deshaping. + * In order to expand it, near or far spaces according to the + * options user specifies. Also buffer size can be increased. + * + * For SeenFamily characters and YehHamza only the near option is + * supported, while for LamAlef we can take spaces from begin, end, + * near or even increase the buffer size. + * There is also the Auto option for LamAlef only, which will first + * search for a space at end, begin then near, respectively. + * If there are no spaces to expand these characters, an error will be set to + * U_NO_SPACE_AVAILABLE as defined in utypes.h + */ + +static int32_t +expandCompositChar(char16_t *dest, int32_t sourceLength, + int32_t destSize,uint32_t options, + UErrorCode *pErrorCode, int shapingMode,struct uShapeVariables shapeVars) { + + int32_t i = 0,j = 0; + + char16_t *tempbuffer=nullptr; + int yehHamzaOption = 0; + int seenTailOption = 0; + int lamAlefOption = 0; + + if (shapingMode == 1){ + if ( (options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_AUTO){ + + if(shapeVars.spacesRelativeToTextBeginEnd == 0) { + destSize = expandCompositCharAtEnd(dest, sourceLength, destSize, pErrorCode); + + if(*pErrorCode == U_NO_SPACE_AVAILABLE) { + *pErrorCode = U_ZERO_ERROR; + destSize = expandCompositCharAtBegin(dest, sourceLength, destSize, pErrorCode); + } + }else { + destSize = expandCompositCharAtBegin(dest, sourceLength, destSize, pErrorCode); + + if(*pErrorCode == U_NO_SPACE_AVAILABLE) { + *pErrorCode = U_ZERO_ERROR; + destSize = expandCompositCharAtEnd(dest, sourceLength, destSize, pErrorCode); + } + } + + if(*pErrorCode == U_NO_SPACE_AVAILABLE) { + *pErrorCode = U_ZERO_ERROR; + destSize = expandCompositCharAtNear(dest, sourceLength, destSize, pErrorCode, yehHamzaOption, + seenTailOption, 1,shapeVars); + } + } + } + + if (shapingMode == 1){ + if ( (options&U_SHAPE_LAMALEF_MASK) == shapeVars.uShapeLamalefEnd){ + destSize = expandCompositCharAtEnd(dest, sourceLength, destSize, pErrorCode); + } + } + + if (shapingMode == 1){ + if ( (options&U_SHAPE_LAMALEF_MASK) == shapeVars.uShapeLamalefBegin){ + destSize = expandCompositCharAtBegin(dest, sourceLength, destSize, pErrorCode); + } + } + + if (shapingMode == 0){ + if ((options&U_SHAPE_YEHHAMZA_MASK) == U_SHAPE_YEHHAMZA_TWOCELL_NEAR){ + yehHamzaOption = 1; + } + if ((options&U_SHAPE_SEEN_MASK) == U_SHAPE_SEEN_TWOCELL_NEAR){ + seenTailOption = 1; + } + } + if (shapingMode == 1) { + if ( (options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_NEAR) { + lamAlefOption = 1; + } + } + + + if (yehHamzaOption || seenTailOption || lamAlefOption){ + destSize = expandCompositCharAtNear(dest, sourceLength, destSize, pErrorCode, yehHamzaOption, + seenTailOption,lamAlefOption,shapeVars); + } + + + if (shapingMode == 1){ + if ( (options&U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_RESIZE){ + destSize = calculateSize(dest,sourceLength,destSize,options); + tempbuffer = (char16_t *)uprv_malloc((destSize+1)*U_SIZEOF_UCHAR); + + /* Test for nullptr */ + if(tempbuffer == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + + uprv_memset(tempbuffer, 0, (destSize+1)*U_SIZEOF_UCHAR); + + i = j = 0; + while(i < destSize && j < destSize) { + if(isLamAlefChar(dest[i]) ) { + tempbuffer[j] = convertLamAlef[ dest[i] - 0xFEF5 ]; + tempbuffer[j+1] = LAM_CHAR; + j++; + }else { + tempbuffer[j] = dest[i]; + } + i++; + j++; + } + + u_memcpy(dest, tempbuffer, destSize); + } + } + + if(tempbuffer) { + uprv_free(tempbuffer); + } + return destSize; +} + +/* + *Name : shapeUnicode + *Function : Converts an Arabic Unicode buffer in 06xx Range into a shaped + * arabic Unicode buffer in FExx Range + */ +static int32_t +shapeUnicode(char16_t *dest, int32_t sourceLength, + int32_t destSize,uint32_t options, + UErrorCode *pErrorCode, + int tashkeelFlag, struct uShapeVariables shapeVars) { + + int32_t i, iend; + int32_t step; + int32_t lastPos,Nx, Nw; + unsigned int Shape; + int32_t lamalef_found = 0; + int32_t seenfamFound = 0, yehhamzaFound =0, tashkeelFound = 0; + char16_t prevLink = 0, lastLink = 0, currLink, nextLink = 0; + char16_t wLamalef; + + /* + * Converts the input buffer from FExx Range into 06xx Range + * to make sure that all characters are in the 06xx range + * even the lamalef is converted to the special region in + * the 06xx range + */ + if ((options & U_SHAPE_PRESERVE_PRESENTATION_MASK) == U_SHAPE_PRESERVE_PRESENTATION_NOOP) { + for (i = 0; i < sourceLength; i++) { + char16_t inputChar = dest[i]; + if ( (inputChar >= 0xFB50) && (inputChar <= 0xFBFF)) { + char16_t c = convertFBto06 [ (inputChar - 0xFB50) ]; + if (c != 0) + dest[i] = c; + } else if ( (inputChar >= 0xFE70) && (inputChar <= 0xFEFC)) { + dest[i] = convertFEto06 [ (inputChar - 0xFE70) ] ; + } else { + dest[i] = inputChar ; + } + } + } + + + /* sets the index to the end of the buffer, together with the step point to -1 */ + i = sourceLength - 1; + iend = -1; + step = -1; + + /* + * This function resolves the link between the characters . + * Arabic characters have four forms : + * Isolated Form, Initial Form, Middle Form and Final Form + */ + currLink = getLink(dest[i]); + + lastPos = i; + Nx = -2, Nw = 0; + + while (i != iend) { + /* If high byte of currLink > 0 then more than one shape */ + if ((currLink & 0xFF00) > 0 || (getLink(dest[i]) & IRRELEVANT) != 0) { + Nw = i + step; + while (Nx < 0) { /* we need to know about next char */ + if(Nw == iend) { + nextLink = 0; + Nx = 3000; + } else { + nextLink = getLink(dest[Nw]); + if((nextLink & IRRELEVANT) == 0) { + Nx = Nw; + } else { + Nw = Nw + step; + } + } + } + + if ( ((currLink & ALEFTYPE) > 0) && ((lastLink & LAMTYPE) > 0) ) { + lamalef_found = 1; + wLamalef = changeLamAlef(dest[i]); /*get from 0x065C-0x065f */ + if ( wLamalef != 0) { + dest[i] = LAMALEF_SPACE_SUB; /* The default case is to drop the Alef and replace */ + dest[lastPos] =wLamalef; /* it by LAMALEF_SPACE_SUB which is the last character in the */ + i=lastPos; /* unicode private use area, this is done to make */ + } /* sure that removeLamAlefSpaces() handles only the */ + lastLink = prevLink; /* spaces generated during lamalef generation. */ + currLink = getLink(wLamalef); /* LAMALEF_SPACE_SUB is added here and is replaced by spaces */ + } /* in removeLamAlefSpaces() */ + + if ((i > 0) && (dest[i-1] == SPACE_CHAR)){ + if ( isSeenFamilyChar(dest[i])) { + seenfamFound = 1; + } else if (dest[i] == YEH_HAMZA_CHAR) { + yehhamzaFound = 1; + } + } + else if(i==0){ + if ( isSeenFamilyChar(dest[i])){ + seenfamFound = 1; + } else if (dest[i] == YEH_HAMZA_CHAR) { + yehhamzaFound = 1; + } + } + + /* + * get the proper shape according to link ability of neighbors + * and of character; depends on the order of the shapes + * (isolated, initial, middle, final) in the compatibility area + */ + Shape = shapeTable[nextLink & (LINKR + LINKL)] + [lastLink & (LINKR + LINKL)] + [currLink & (LINKR + LINKL)]; + + if ((currLink & (LINKR+LINKL)) == 1) { + Shape &= 1; + } else if(isTashkeelChar(dest[i])) { + if( (lastLink & LINKL) && (nextLink & LINKR) && (tashkeelFlag == 1) && + dest[i] != 0x064C && dest[i] != 0x064D ) + { + Shape = 1; + if( (nextLink&ALEFTYPE) == ALEFTYPE && (lastLink&LAMTYPE) == LAMTYPE ) { + Shape = 0; + } + } else if(tashkeelFlag == 2 && dest[i] == SHADDA06_CHAR){ + Shape = 1; + } else { + Shape = 0; + } + } + if ((dest[i] ^ 0x0600) < 0x100) { + if ( isTashkeelChar(dest[i]) ){ + if (tashkeelFlag == 2 && dest[i] != SHADDA06_CHAR){ + dest[i] = TASHKEEL_SPACE_SUB; + tashkeelFound = 1; + } else { + /* to ensure the array index is within the range */ + U_ASSERT(dest[i] >= 0x064Bu + && dest[i]-0x064Bu < UPRV_LENGTHOF(IrrelevantPos)); + dest[i] = 0xFE70 + IrrelevantPos[(dest[i] - 0x064B)] + static_cast(Shape); + } + }else if ((currLink & APRESENT) > 0) { + dest[i] = (char16_t)(0xFB50 + (currLink >> 8) + Shape); + }else if ((currLink >> 8) > 0 && (currLink & IRRELEVANT) == 0) { + dest[i] = (char16_t)(0xFE70 + (currLink >> 8) + Shape); + } + } + } + + /* move one notch forward */ + if ((currLink & IRRELEVANT) == 0) { + prevLink = lastLink; + lastLink = currLink; + lastPos = i; + } + + i = i + step; + if (i == Nx) { + currLink = nextLink; + Nx = -2; + } else if(i != iend) { + currLink = getLink(dest[i]); + } + } + destSize = sourceLength; + if ( (lamalef_found != 0 ) || (tashkeelFound != 0) ){ + destSize = handleGeneratedSpaces(dest,sourceLength,destSize,options,pErrorCode, shapeVars); + } + + if ( (seenfamFound != 0) || (yehhamzaFound != 0) ) { + destSize = expandCompositChar(dest, sourceLength,destSize,options,pErrorCode, SHAPE_MODE,shapeVars); + } + return destSize; +} + +/* + *Name : deShapeUnicode + *Function : Converts an Arabic Unicode buffer in FExx Range into unshaped + * arabic Unicode buffer in 06xx Range + */ +static int32_t +deShapeUnicode(char16_t *dest, int32_t sourceLength, + int32_t destSize,uint32_t options, + UErrorCode *pErrorCode, struct uShapeVariables shapeVars) { + int32_t i = 0; + int32_t lamalef_found = 0; + int32_t yehHamzaComposeEnabled = 0; + int32_t seenComposeEnabled = 0; + + yehHamzaComposeEnabled = ((options&U_SHAPE_YEHHAMZA_MASK) == U_SHAPE_YEHHAMZA_TWOCELL_NEAR) ? 1 : 0; + seenComposeEnabled = ((options&U_SHAPE_SEEN_MASK) == U_SHAPE_SEEN_TWOCELL_NEAR)? 1 : 0; + + /* + *This for loop changes the buffer from the Unicode FE range to + *the Unicode 06 range + */ + + for(i = 0; i < sourceLength; i++) { + char16_t inputChar = dest[i]; + if ( (inputChar >= 0xFB50) && (inputChar <= 0xFBFF)) { /* FBxx Arabic range */ + char16_t c = convertFBto06 [ (inputChar - 0xFB50) ]; + if (c != 0) + dest[i] = c; + } else if( (yehHamzaComposeEnabled == 1) && ((inputChar == HAMZA06_CHAR) || (inputChar == HAMZAFE_CHAR)) + && (i < (sourceLength - 1)) && isAlefMaksouraChar(dest[i+1] )) { + dest[i] = SPACE_CHAR; + dest[i+1] = YEH_HAMZA_CHAR; + } else if ( (seenComposeEnabled == 1) && (isTailChar(inputChar)) && (i< (sourceLength - 1)) + && (isSeenTailFamilyChar(dest[i+1])) ) { + dest[i] = SPACE_CHAR; + } else if (( inputChar >= 0xFE70) && (inputChar <= 0xFEF4 )) { /* FExx Arabic range */ + dest[i] = convertFEto06 [ (inputChar - 0xFE70) ]; + } else { + dest[i] = inputChar ; + } + + if( isLamAlefChar(dest[i]) ) + lamalef_found = 1; + } + + destSize = sourceLength; + if (lamalef_found != 0){ + destSize = expandCompositChar(dest,sourceLength,destSize,options,pErrorCode,DESHAPE_MODE, shapeVars); + } + return destSize; +} + +/* + **************************************** + * u_shapeArabic + **************************************** + */ + +U_CAPI int32_t U_EXPORT2 +u_shapeArabic(const char16_t *source, int32_t sourceLength, + char16_t *dest, int32_t destCapacity, + uint32_t options, + UErrorCode *pErrorCode) { + + int32_t destLength; + struct uShapeVariables shapeVars = { OLD_TAIL_CHAR,U_SHAPE_LAMALEF_BEGIN,U_SHAPE_LAMALEF_END,U_SHAPE_TASHKEEL_BEGIN,U_SHAPE_TASHKEEL_END,0}; + + /* usual error checking */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + /* make sure that no reserved options values are used; allow dest==nullptr only for preflighting */ + if( source==nullptr || sourceLength<-1 || (dest==nullptr && destCapacity!=0) || destCapacity<0 || + (((options&U_SHAPE_TASHKEEL_MASK) > 0) && + ((options&U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED) == U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED) ) || + (((options&U_SHAPE_TASHKEEL_MASK) > 0) && + ((options&U_SHAPE_LETTERS_MASK) == U_SHAPE_LETTERS_UNSHAPE)) || + (options&U_SHAPE_DIGIT_TYPE_RESERVED)==U_SHAPE_DIGIT_TYPE_RESERVED || + (options&U_SHAPE_DIGITS_MASK)==U_SHAPE_DIGITS_RESERVED || + ((options&U_SHAPE_LAMALEF_MASK) != U_SHAPE_LAMALEF_RESIZE && + (options&U_SHAPE_AGGREGATE_TASHKEEL_MASK) != 0) || + ((options&U_SHAPE_AGGREGATE_TASHKEEL_MASK) == U_SHAPE_AGGREGATE_TASHKEEL && + (options&U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED) != U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED) + ) + { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + /* Validate lamalef options */ + if(((options&U_SHAPE_LAMALEF_MASK) > 0)&& + !(((options & U_SHAPE_LAMALEF_MASK)==U_SHAPE_LAMALEF_BEGIN) || + ((options & U_SHAPE_LAMALEF_MASK)==U_SHAPE_LAMALEF_END ) || + ((options & U_SHAPE_LAMALEF_MASK)==U_SHAPE_LAMALEF_RESIZE )|| + ((options & U_SHAPE_LAMALEF_MASK)==U_SHAPE_LAMALEF_AUTO) || + ((options & U_SHAPE_LAMALEF_MASK)==U_SHAPE_LAMALEF_NEAR))) + { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + /* Validate Tashkeel options */ + if(((options&U_SHAPE_TASHKEEL_MASK) > 0)&& + !(((options & U_SHAPE_TASHKEEL_MASK)==U_SHAPE_TASHKEEL_BEGIN) || + ((options & U_SHAPE_TASHKEEL_MASK)==U_SHAPE_TASHKEEL_END ) + ||((options & U_SHAPE_TASHKEEL_MASK)==U_SHAPE_TASHKEEL_RESIZE )|| + ((options & U_SHAPE_TASHKEEL_MASK)==U_SHAPE_TASHKEEL_REPLACE_BY_TATWEEL))) + { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + /* determine the source length */ + if(sourceLength==-1) { + sourceLength=u_strlen(source); + } + if(sourceLength<=0) { + return u_terminateUChars(dest, destCapacity, 0, pErrorCode); + } + + /* check that source and destination do not overlap */ + if( dest!=nullptr && + ((source<=dest && dest0) { + int32_t logical_order = (options&U_SHAPE_TEXT_DIRECTION_MASK) == U_SHAPE_TEXT_DIRECTION_LOGICAL; + int32_t aggregate_tashkeel = + (options&(U_SHAPE_AGGREGATE_TASHKEEL_MASK+U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED)) == + (U_SHAPE_AGGREGATE_TASHKEEL+U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED); + int step=logical_order?1:-1; + int j=logical_order?-1:2*sourceLength; + int i=logical_order?-1:sourceLength; + int end=logical_order?sourceLength:-1; + int aggregation_possible = 1; + char16_t prev = 0; + char16_t prevLink, currLink = 0; + int newSourceLength = 0; + tempsource = (char16_t *)uprv_malloc(2*sourceLength*U_SIZEOF_UCHAR); + if(tempsource == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + + while ((i+=step) != end) { + prevLink = currLink; + currLink = getLink(source[i]); + if (aggregate_tashkeel && ((prevLink|currLink)&COMBINE) == COMBINE && aggregation_possible) { + aggregation_possible = 0; + tempsource[j] = (prevdestCapacity) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + if (tempsource != nullptr) uprv_free(tempsource); + return outputSize; + } + + /* + * need a temporary buffer of size max(outputSize, sourceLength) + * because at first we copy source->temp + */ + if(sourceLength>outputSize) { + outputSize=sourceLength; + } + + /* Start of Arabic letter shaping part */ + if(outputSize<=UPRV_LENGTHOF(buffer)) { + outputSize=UPRV_LENGTHOF(buffer); + tempbuffer=buffer; + } else { + tempbuffer = (char16_t *)uprv_malloc(outputSize*U_SIZEOF_UCHAR); + + /*Test for nullptr*/ + if(tempbuffer == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + if (tempsource != nullptr) uprv_free(tempsource); + return 0; + } + } + u_memcpy(tempbuffer, source, sourceLength); + if (tempsource != nullptr){ + uprv_free(tempsource); + } + + if(sourceLength 0 + && ((options&U_SHAPE_TASHKEEL_MASK) !=U_SHAPE_TASHKEEL_REPLACE_BY_TATWEEL)) { + /* Call the shaping function with tashkeel flag == 2 for removal of tashkeel */ + destLength = shapeUnicode(tempbuffer,sourceLength,destCapacity,options,pErrorCode,2,shapeVars); + }else { + /* default Call the shaping function with tashkeel flag == 1 */ + destLength = shapeUnicode(tempbuffer,sourceLength,destCapacity,options,pErrorCode,1,shapeVars); + + /*After shaping text check if user wants to remove tashkeel and replace it with tatweel*/ + if( (options&U_SHAPE_TASHKEEL_MASK) == U_SHAPE_TASHKEEL_REPLACE_BY_TATWEEL){ + destLength = handleTashkeelWithTatweel(tempbuffer,destLength,destCapacity,options,pErrorCode); + } + } + break; + case U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED : + /* Call the shaping function with tashkeel flag == 0 */ + destLength = shapeUnicode(tempbuffer,sourceLength,destCapacity,options,pErrorCode,0,shapeVars); + break; + + case U_SHAPE_LETTERS_UNSHAPE : + /* Call the deshaping function */ + destLength = deShapeUnicode(tempbuffer,sourceLength,destCapacity,options,pErrorCode,shapeVars); + break; + default : + /* will never occur because of validity checks above */ + destLength = 0; + break; + } + + /* + * TODO: (markus 2002aug01) + * For as long as we always preflight the outputSize above + * we should U_ASSERT(outputSize==destLength) + * except for the adjustment above before the tempbuffer allocation + */ + + if((options&U_SHAPE_TEXT_DIRECTION_MASK) == U_SHAPE_TEXT_DIRECTION_LOGICAL) { + countSpaces(tempbuffer,destLength,options,&spacesCountl,&spacesCountr); + invertBuffer(tempbuffer,destLength,options,spacesCountl,spacesCountr); + } + u_memcpy(dest, tempbuffer, uprv_min(destLength, destCapacity)); + + if(tempbuffer!=buffer) { + uprv_free(tempbuffer); + } + + if(destLength>destCapacity) { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + return destLength; + } + + /* End of Arabic letter shaping part */ + } else { + /* + * No letter shaping: + * just make sure the destination is large enough and copy the string. + */ + if(destCapacitysize>=20 && - pInfo->isBigEndian==U_IS_BIG_ENDIAN && - pInfo->charsetFamily==U_CHARSET_FAMILY && - pInfo->dataFormat[0]==0x53 && /* dataFormat="SPRP" */ - pInfo->dataFormat[1]==0x50 && - pInfo->dataFormat[2]==0x52 && - pInfo->dataFormat[3]==0x50 && - pInfo->formatVersion[0]==3 && - pInfo->formatVersion[2]==UTRIE_SHIFT && - pInfo->formatVersion[3]==UTRIE_INDEX_SHIFT - ) { - //uprv_memcpy(formatVersion, pInfo->formatVersion, 4); - uprv_memcpy(dataVersion, pInfo->dataVersion, 4); - return true; - } else { - return false; - } -} - -static int32_t U_CALLCONV -getSPrepFoldingOffset(uint32_t data) { - - return (int32_t)data; - -} - -/* hashes an entry */ -static int32_t U_CALLCONV -hashEntry(const UHashTok parm) { - UStringPrepKey *b = (UStringPrepKey *)parm.pointer; - UHashTok namekey, pathkey; - namekey.pointer = b->name; - pathkey.pointer = b->path; - uint32_t unsignedHash = static_cast(uhash_hashChars(namekey)) + - 37u * static_cast(uhash_hashChars(pathkey)); - return static_cast(unsignedHash); -} - -/* compares two entries */ -static UBool U_CALLCONV -compareEntries(const UHashTok p1, const UHashTok p2) { - UStringPrepKey *b1 = (UStringPrepKey *)p1.pointer; - UStringPrepKey *b2 = (UStringPrepKey *)p2.pointer; - UHashTok name1, name2, path1, path2; - name1.pointer = b1->name; - name2.pointer = b2->name; - path1.pointer = b1->path; - path2.pointer = b2->path; - return ((UBool)(uhash_compareChars(name1, name2) & - uhash_compareChars(path1, path2))); -} - -static void -usprep_unload(UStringPrepProfile* data){ - udata_close(data->sprepData); -} - -static int32_t -usprep_internal_flushCache(UBool noRefCount){ - UStringPrepProfile *profile = NULL; - UStringPrepKey *key = NULL; - int32_t pos = UHASH_FIRST; - int32_t deletedNum = 0; - const UHashElement *e; - - /* - * if shared data hasn't even been lazy evaluated yet - * return 0 - */ - umtx_lock(&usprepMutex); - if (SHARED_DATA_HASHTABLE == NULL) { - umtx_unlock(&usprepMutex); - return 0; - } - - /*creates an enumeration to iterate through every element in the table */ - while ((e = uhash_nextElement(SHARED_DATA_HASHTABLE, &pos)) != NULL) - { - profile = (UStringPrepProfile *) e->value.pointer; - key = (UStringPrepKey *) e->key.pointer; - - if ((noRefCount== false && profile->refCount == 0) || - noRefCount== true) { - deletedNum++; - uhash_removeElement(SHARED_DATA_HASHTABLE, e); - - /* unload the data */ - usprep_unload(profile); - - if(key->name != NULL) { - uprv_free(key->name); - key->name=NULL; - } - if(key->path != NULL) { - uprv_free(key->path); - key->path=NULL; - } - uprv_free(profile); - uprv_free(key); - } - - } - umtx_unlock(&usprepMutex); - - return deletedNum; -} - -/* Works just like ucnv_flushCache() -static int32_t -usprep_flushCache(){ - return usprep_internal_flushCache(false); -} -*/ - -static UBool U_CALLCONV usprep_cleanup(void){ - if (SHARED_DATA_HASHTABLE != NULL) { - usprep_internal_flushCache(true); - if (SHARED_DATA_HASHTABLE != NULL && uhash_count(SHARED_DATA_HASHTABLE) == 0) { - uhash_close(SHARED_DATA_HASHTABLE); - SHARED_DATA_HASHTABLE = NULL; - } - } - gSharedDataInitOnce.reset(); - return (SHARED_DATA_HASHTABLE == NULL); -} -U_CDECL_END - - -/** Initializes the cache for resources */ -static void U_CALLCONV -createCache(UErrorCode &status) { - SHARED_DATA_HASHTABLE = uhash_open(hashEntry, compareEntries, NULL, &status); - if (U_FAILURE(status)) { - SHARED_DATA_HASHTABLE = NULL; - } - ucln_common_registerCleanup(UCLN_COMMON_USPREP, usprep_cleanup); -} - -static void -initCache(UErrorCode *status) { - umtx_initOnce(gSharedDataInitOnce, &createCache, *status); -} - -static UBool U_CALLCONV -loadData(UStringPrepProfile* profile, - const char* path, - const char* name, - const char* type, - UErrorCode* errorCode) { - /* load Unicode SPREP data from file */ - UTrie _sprepTrie={ 0,0,0,0,0,0,0 }; - UDataMemory *dataMemory; - const int32_t *p=NULL; - const uint8_t *pb; - UVersionInfo normUnicodeVersion; - int32_t normUniVer, sprepUniVer, normCorrVer; - - if(errorCode==NULL || U_FAILURE(*errorCode)) { - return 0; - } - - /* open the data outside the mutex block */ - //TODO: change the path - dataMemory=udata_openChoice(path, type, name, isSPrepAcceptable, NULL, errorCode); - if(U_FAILURE(*errorCode)) { - return false; - } - - p=(const int32_t *)udata_getMemory(dataMemory); - pb=(const uint8_t *)(p+_SPREP_INDEX_TOP); - utrie_unserialize(&_sprepTrie, pb, p[_SPREP_INDEX_TRIE_SIZE], errorCode); - _sprepTrie.getFoldingOffset=getSPrepFoldingOffset; - - - if(U_FAILURE(*errorCode)) { - udata_close(dataMemory); - return false; - } - - /* in the mutex block, set the data for this process */ - umtx_lock(&usprepMutex); - if(profile->sprepData==NULL) { - profile->sprepData=dataMemory; - dataMemory=NULL; - uprv_memcpy(&profile->indexes, p, sizeof(profile->indexes)); - uprv_memcpy(&profile->sprepTrie, &_sprepTrie, sizeof(UTrie)); - } else { - p=(const int32_t *)udata_getMemory(profile->sprepData); - } - umtx_unlock(&usprepMutex); - /* initialize some variables */ - profile->mappingData=(uint16_t *)((uint8_t *)(p+_SPREP_INDEX_TOP)+profile->indexes[_SPREP_INDEX_TRIE_SIZE]); - - u_getUnicodeVersion(normUnicodeVersion); - normUniVer = (normUnicodeVersion[0] << 24) + (normUnicodeVersion[1] << 16) + - (normUnicodeVersion[2] << 8 ) + (normUnicodeVersion[3]); - sprepUniVer = (dataVersion[0] << 24) + (dataVersion[1] << 16) + - (dataVersion[2] << 8 ) + (dataVersion[3]); - normCorrVer = profile->indexes[_SPREP_NORM_CORRECTNS_LAST_UNI_VERSION]; - - if(U_FAILURE(*errorCode)){ - udata_close(dataMemory); - return false; - } - if( normUniVer < sprepUniVer && /* the Unicode version of SPREP file must be less than the Unicode Version of the normalization data */ - normUniVer < normCorrVer && /* the Unicode version of the NormalizationCorrections.txt file should be less than the Unicode Version of the normalization data */ - ((profile->indexes[_SPREP_OPTIONS] & _SPREP_NORMALIZATION_ON) > 0) /* normalization turned on*/ - ){ - *errorCode = U_INVALID_FORMAT_ERROR; - udata_close(dataMemory); - return false; - } - profile->isDataLoaded = true; - - /* if a different thread set it first, then close the extra data */ - if(dataMemory!=NULL) { - udata_close(dataMemory); /* NULL if it was set correctly */ - } - - - return profile->isDataLoaded; -} - -static UStringPrepProfile* -usprep_getProfile(const char* path, - const char* name, - UErrorCode *status){ - - UStringPrepProfile* profile = NULL; - - initCache(status); - - if(U_FAILURE(*status)){ - return NULL; - } - - UStringPrepKey stackKey; - /* - * const is cast way to save malloc, strcpy and free calls - * we use the passed in pointers for fetching the data from the - * hash table which is safe - */ - stackKey.name = (char*) name; - stackKey.path = (char*) path; - - /* fetch the data from the cache */ - umtx_lock(&usprepMutex); - profile = (UStringPrepProfile*) (uhash_get(SHARED_DATA_HASHTABLE,&stackKey)); - if(profile != NULL) { - profile->refCount++; - } - umtx_unlock(&usprepMutex); - - if(profile == NULL) { - /* else load the data and put the data in the cache */ - LocalMemory newProfile; - if(newProfile.allocateInsteadAndReset() == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - /* load the data */ - if(!loadData(newProfile.getAlias(), path, name, _SPREP_DATA_TYPE, status) || U_FAILURE(*status) ){ - return NULL; - } - - /* get the options */ - newProfile->doNFKC = (UBool)((newProfile->indexes[_SPREP_OPTIONS] & _SPREP_NORMALIZATION_ON) > 0); - newProfile->checkBiDi = (UBool)((newProfile->indexes[_SPREP_OPTIONS] & _SPREP_CHECK_BIDI_ON) > 0); - - LocalMemory key; - LocalMemory keyName; - LocalMemory keyPath; - if( key.allocateInsteadAndReset() == NULL || - keyName.allocateInsteadAndCopy(static_cast(uprv_strlen(name)+1)) == NULL || - (path != NULL && - keyPath.allocateInsteadAndCopy(static_cast(uprv_strlen(path)+1)) == NULL) - ) { - *status = U_MEMORY_ALLOCATION_ERROR; - usprep_unload(newProfile.getAlias()); - return NULL; - } - - umtx_lock(&usprepMutex); - // If another thread already inserted the same key/value, refcount and cleanup our thread data - profile = (UStringPrepProfile*) (uhash_get(SHARED_DATA_HASHTABLE,&stackKey)); - if(profile != NULL) { - profile->refCount++; - usprep_unload(newProfile.getAlias()); - } - else { - /* initialize the key members */ - key->name = keyName.orphan(); - uprv_strcpy(key->name, name); - if(path != NULL){ - key->path = keyPath.orphan(); - uprv_strcpy(key->path, path); - } - profile = newProfile.orphan(); - - /* add the data object to the cache */ - profile->refCount = 1; - uhash_put(SHARED_DATA_HASHTABLE, key.orphan(), profile, status); - } - umtx_unlock(&usprepMutex); - } - - return profile; -} - -U_CAPI UStringPrepProfile* U_EXPORT2 -usprep_open(const char* path, - const char* name, - UErrorCode* status){ - - if(status == NULL || U_FAILURE(*status)){ - return NULL; - } - - /* initialize the profile struct members */ - return usprep_getProfile(path,name,status); -} - -U_CAPI UStringPrepProfile* U_EXPORT2 -usprep_openByType(UStringPrepProfileType type, - UErrorCode* status) { - if(status == NULL || U_FAILURE(*status)){ - return NULL; - } - int32_t index = (int32_t)type; - if (index < 0 || index >= UPRV_LENGTHOF(PROFILE_NAMES)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - return usprep_open(NULL, PROFILE_NAMES[index], status); -} - -U_CAPI void U_EXPORT2 -usprep_close(UStringPrepProfile* profile){ - if(profile==NULL){ - return; - } - - umtx_lock(&usprepMutex); - /* decrement the ref count*/ - if(profile->refCount > 0){ - profile->refCount--; - } - umtx_unlock(&usprepMutex); - -} - -U_CFUNC void -uprv_syntaxError(const UChar* rules, - int32_t pos, - int32_t rulesLen, - UParseError* parseError){ - if(parseError == NULL){ - return; - } - parseError->offset = pos; - parseError->line = 0 ; // we are not using line numbers - - // for pre-context - int32_t start = (pos < U_PARSE_CONTEXT_LEN)? 0 : (pos - (U_PARSE_CONTEXT_LEN-1)); - int32_t limit = pos; - - u_memcpy(parseError->preContext,rules+start,limit-start); - //null terminate the buffer - parseError->preContext[limit-start] = 0; - - // for post-context; include error rules[pos] - start = pos; - limit = start + (U_PARSE_CONTEXT_LEN-1); - if (limit > rulesLen) { - limit = rulesLen; - } - if (start < rulesLen) { - u_memcpy(parseError->postContext,rules+start,limit-start); - } - //null terminate the buffer - parseError->postContext[limit-start]= 0; -} - - -static inline UStringPrepType -getValues(uint16_t trieWord, int16_t& value, UBool& isIndex){ - - UStringPrepType type; - if(trieWord == 0){ - /* - * Initial value stored in the mapping table - * just return USPREP_TYPE_LIMIT .. so that - * the source codepoint is copied to the destination - */ - type = USPREP_TYPE_LIMIT; - isIndex =false; - value = 0; - }else if(trieWord >= _SPREP_TYPE_THRESHOLD){ - type = (UStringPrepType) (trieWord - _SPREP_TYPE_THRESHOLD); - isIndex =false; - value = 0; - }else{ - /* get the type */ - type = USPREP_MAP; - /* ascertain if the value is index or delta */ - if(trieWord & 0x02){ - isIndex = true; - value = trieWord >> 2; //mask off the lower 2 bits and shift - }else{ - isIndex = false; - value = (int16_t)trieWord; - value = (value >> 2); - } - - if((trieWord>>2) == _SPREP_MAX_INDEX_VALUE){ - type = USPREP_DELETE; - isIndex =false; - value = 0; - } - } - return type; -} - -// TODO: change to writing to UnicodeString not UChar * -static int32_t -usprep_map( const UStringPrepProfile* profile, - const UChar* src, int32_t srcLength, - UChar* dest, int32_t destCapacity, - int32_t options, - UParseError* parseError, - UErrorCode* status ){ - - uint16_t result; - int32_t destIndex=0; - int32_t srcIndex; - UBool allowUnassigned = (UBool) ((options & USPREP_ALLOW_UNASSIGNED)>0); - UStringPrepType type; - int16_t value; - UBool isIndex; - const int32_t* indexes = profile->indexes; - - // no error checking the caller check for error and arguments - // no string length check the caller finds out the string length - - for(srcIndex=0;srcIndexsprepTrie,ch,result); - - type = getValues(result, value, isIndex); - - // check if the source codepoint is unassigned - if(type == USPREP_UNASSIGNED && allowUnassigned == false){ - - uprv_syntaxError(src,srcIndex-U16_LENGTH(ch), srcLength,parseError); - *status = U_STRINGPREP_UNASSIGNED_ERROR; - return 0; - - }else if(type == USPREP_MAP){ - - int32_t index, length; - - if(isIndex){ - index = value; - if(index >= indexes[_SPREP_ONE_UCHAR_MAPPING_INDEX_START] && - index < indexes[_SPREP_TWO_UCHARS_MAPPING_INDEX_START]){ - length = 1; - }else if(index >= indexes[_SPREP_TWO_UCHARS_MAPPING_INDEX_START] && - index < indexes[_SPREP_THREE_UCHARS_MAPPING_INDEX_START]){ - length = 2; - }else if(index >= indexes[_SPREP_THREE_UCHARS_MAPPING_INDEX_START] && - index < indexes[_SPREP_FOUR_UCHARS_MAPPING_INDEX_START]){ - length = 3; - }else{ - length = profile->mappingData[index++]; - - } - - /* copy mapping to destination */ - for(int32_t i=0; i< length; i++){ - if(destIndex < destCapacity ){ - dest[destIndex] = profile->mappingData[index+i]; - } - destIndex++; /* for pre-flighting */ - } - continue; - }else{ - // subtract the delta to arrive at the code point - ch -= value; - } - - }else if(type==USPREP_DELETE){ - // just consume the codepoint and continue - continue; - } - //copy the code point into destination - if(ch <= 0xFFFF){ - if(destIndex < destCapacity ){ - dest[destIndex] = (UChar)ch; - } - destIndex++; - }else{ - if(destIndex+1 < destCapacity ){ - dest[destIndex] = U16_LEAD(ch); - dest[destIndex+1] = U16_TRAIL(ch); - } - destIndex +=2; - } - - } - - return u_terminateUChars(dest, destCapacity, destIndex, status); -} - -/* - 1) Map -- For each character in the input, check if it has a mapping - and, if so, replace it with its mapping. - - 2) Normalize -- Possibly normalize the result of step 1 using Unicode - normalization. - - 3) Prohibit -- Check for any characters that are not allowed in the - output. If any are found, return an error. - - 4) Check bidi -- Possibly check for right-to-left characters, and if - any are found, make sure that the whole string satisfies the - requirements for bidirectional strings. If the string does not - satisfy the requirements for bidirectional strings, return an - error. - [Unicode3.2] defines several bidirectional categories; each character - has one bidirectional category assigned to it. For the purposes of - the requirements below, an "RandALCat character" is a character that - has Unicode bidirectional categories "R" or "AL"; an "LCat character" - is a character that has Unicode bidirectional category "L". Note - - - that there are many characters which fall in neither of the above - definitions; Latin digits ( through ) are examples of - this because they have bidirectional category "EN". - - In any profile that specifies bidirectional character handling, all - three of the following requirements MUST be met: - - 1) The characters in section 5.8 MUST be prohibited. - - 2) If a string contains any RandALCat character, the string MUST NOT - contain any LCat character. - - 3) If a string contains any RandALCat character, a RandALCat - character MUST be the first character of the string, and a - RandALCat character MUST be the last character of the string. -*/ -U_CAPI int32_t U_EXPORT2 -usprep_prepare( const UStringPrepProfile* profile, - const UChar* src, int32_t srcLength, - UChar* dest, int32_t destCapacity, - int32_t options, - UParseError* parseError, - UErrorCode* status ){ - - // check error status - if(U_FAILURE(*status)){ - return 0; - } - - //check arguments - if(profile==NULL || - (src==NULL ? srcLength!=0 : srcLength<-1) || - (dest==NULL ? destCapacity!=0 : destCapacity<0)) { - *status=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - //get the string length - if(srcLength < 0){ - srcLength = u_strlen(src); - } - // map - UnicodeString s1; - UChar *b1 = s1.getBuffer(srcLength); - if(b1==NULL){ - *status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - int32_t b1Len = usprep_map(profile, src, srcLength, - b1, s1.getCapacity(), options, parseError, status); - s1.releaseBuffer(U_SUCCESS(*status) ? b1Len : 0); - - if(*status == U_BUFFER_OVERFLOW_ERROR){ - // redo processing of string - /* we do not have enough room so grow the buffer*/ - b1 = s1.getBuffer(b1Len); - if(b1==NULL){ - *status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - - *status = U_ZERO_ERROR; // reset error - b1Len = usprep_map(profile, src, srcLength, - b1, s1.getCapacity(), options, parseError, status); - s1.releaseBuffer(U_SUCCESS(*status) ? b1Len : 0); - } - if(U_FAILURE(*status)){ - return 0; - } - - // normalize - UnicodeString s2; - if(profile->doNFKC){ - const Normalizer2 *n2 = Normalizer2::getNFKCInstance(*status); - FilteredNormalizer2 fn2(*n2, *uniset_getUnicode32Instance(*status)); - if(U_FAILURE(*status)){ - return 0; - } - fn2.normalize(s1, s2, *status); - }else{ - s2.fastCopyFrom(s1); - } - if(U_FAILURE(*status)){ - return 0; - } - - // Prohibit and checkBiDi in one pass - const UChar *b2 = s2.getBuffer(); - int32_t b2Len = s2.length(); - UCharDirection direction=U_CHAR_DIRECTION_COUNT, firstCharDir=U_CHAR_DIRECTION_COUNT; - UBool leftToRight=false, rightToLeft=false; - int32_t rtlPos =-1, ltrPos =-1; - - for(int32_t b2Index=0; b2IndexsprepTrie,ch,result); - - int16_t value; - UBool isIndex; - UStringPrepType type = getValues(result, value, isIndex); - - if( type == USPREP_PROHIBITED || - ((result < _SPREP_TYPE_THRESHOLD) && (result & 0x01) /* first bit says it the code point is prohibited*/) - ){ - *status = U_STRINGPREP_PROHIBITED_ERROR; - uprv_syntaxError(b2, b2Index-U16_LENGTH(ch), b2Len, parseError); - return 0; - } - - if(profile->checkBiDi) { - direction = ubidi_getClass(ch); - if(firstCharDir == U_CHAR_DIRECTION_COUNT){ - firstCharDir = direction; - } - if(direction == U_LEFT_TO_RIGHT){ - leftToRight = true; - ltrPos = b2Index-1; - } - if(direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC){ - rightToLeft = true; - rtlPos = b2Index-1; - } - } - } - if(profile->checkBiDi == true){ - // satisfy 2 - if( leftToRight == true && rightToLeft == true){ - *status = U_STRINGPREP_CHECK_BIDI_ERROR; - uprv_syntaxError(b2,(rtlPos>ltrPos) ? rtlPos : ltrPos, b2Len, parseError); - return 0; - } - - //satisfy 3 - if( rightToLeft == true && - !((firstCharDir == U_RIGHT_TO_LEFT || firstCharDir == U_RIGHT_TO_LEFT_ARABIC) && - (direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC)) - ){ - *status = U_STRINGPREP_CHECK_BIDI_ERROR; - uprv_syntaxError(b2, rtlPos, b2Len, parseError); - return false; - } - } - return s2.extract(dest, destCapacity, *status); -} - - -/* data swapping ------------------------------------------------------------ */ - -U_CAPI int32_t U_EXPORT2 -usprep_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const UDataInfo *pInfo; - int32_t headerSize; - - const uint8_t *inBytes; - uint8_t *outBytes; - - const int32_t *inIndexes; - int32_t indexes[16]; - - int32_t i, offset, count, size; - - /* udata_swapDataHeader checks the arguments */ - headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - - /* check data format and format version */ - pInfo=(const UDataInfo *)((const char *)inData+4); - if(!( - pInfo->dataFormat[0]==0x53 && /* dataFormat="SPRP" */ - pInfo->dataFormat[1]==0x50 && - pInfo->dataFormat[2]==0x52 && - pInfo->dataFormat[3]==0x50 && - pInfo->formatVersion[0]==3 - )) { - udata_printError(ds, "usprep_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as StringPrep .spp data\n", - pInfo->dataFormat[0], pInfo->dataFormat[1], - pInfo->dataFormat[2], pInfo->dataFormat[3], - pInfo->formatVersion[0]); - *pErrorCode=U_UNSUPPORTED_ERROR; - return 0; - } - - inBytes=(const uint8_t *)inData+headerSize; - outBytes=(uint8_t *)outData+headerSize; - - inIndexes=(const int32_t *)inBytes; - - if(length>=0) { - length-=headerSize; - if(length<16*4) { - udata_printError(ds, "usprep_swap(): too few bytes (%d after header) for StringPrep .spp data\n", - length); - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - } - - /* read the first 16 indexes (ICU 2.8/format version 3: _SPREP_INDEX_TOP==16, might grow) */ - for(i=0; i<16; ++i) { - indexes[i]=udata_readInt32(ds, inIndexes[i]); - } - - /* calculate the total length of the data */ - size= - 16*4+ /* size of indexes[] */ - indexes[_SPREP_INDEX_TRIE_SIZE]+ - indexes[_SPREP_INDEX_MAPPING_DATA_SIZE]; - - if(length>=0) { - if(lengthswapArray32(ds, inBytes, count, outBytes, pErrorCode); - offset+=count; - - /* swap the UTrie */ - count=indexes[_SPREP_INDEX_TRIE_SIZE]; - utrie_swap(ds, inBytes+offset, count, outBytes+offset, pErrorCode); - offset+=count; - - /* swap the uint16_t mappingTable[] */ - count=indexes[_SPREP_INDEX_MAPPING_DATA_SIZE]; - ds->swapArray16(ds, inBytes+offset, count, outBytes+offset, pErrorCode); - //offset+=count; - } - - return headerSize+size; -} - -#endif /* #if !UCONFIG_NO_IDNA */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************************* + * + * Copyright (C) 2003-2016, International Business Machines + * Corporation and others. All Rights Reserved. + * + ******************************************************************************* + * file name: usprep.cpp + * encoding: UTF-8 + * tab size: 8 (not used) + * indentation:4 + * + * created on: 2003jul2 + * created by: Ram Viswanadha + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_IDNA + +#include "unicode/usprep.h" + +#include "unicode/normalizer2.h" +#include "unicode/ustring.h" +#include "unicode/uchar.h" +#include "unicode/uversion.h" +#include "umutex.h" +#include "cmemory.h" +#include "sprpimpl.h" +#include "ustr_imp.h" +#include "uhash.h" +#include "cstring.h" +#include "udataswp.h" +#include "ucln_cmn.h" +#include "ubidi_props.h" +#include "uprops.h" + +U_NAMESPACE_USE + +U_CDECL_BEGIN + +/* +Static cache for already opened StringPrep profiles +*/ +static UHashtable *SHARED_DATA_HASHTABLE = nullptr; +static icu::UInitOnce gSharedDataInitOnce {}; + +static UMutex usprepMutex; +/* format version of spp file */ +//static uint8_t formatVersion[4]={ 0, 0, 0, 0 }; + +/* the Unicode version of the sprep data */ +static UVersionInfo dataVersion={ 0, 0, 0, 0 }; + +/* Profile names must be aligned to UStringPrepProfileType */ +static const char * const PROFILE_NAMES[] = { + "rfc3491", /* USPREP_RFC3491_NAMEPREP */ + "rfc3530cs", /* USPREP_RFC3530_NFS4_CS_PREP */ + "rfc3530csci", /* USPREP_RFC3530_NFS4_CS_PREP_CI */ + "rfc3491", /* USPREP_RFC3530_NSF4_CIS_PREP */ + "rfc3530mixp", /* USPREP_RFC3530_NSF4_MIXED_PREP_PREFIX */ + "rfc3491", /* USPREP_RFC3530_NSF4_MIXED_PREP_SUFFIX */ + "rfc3722", /* USPREP_RFC3722_ISCSI */ + "rfc3920node", /* USPREP_RFC3920_NODEPREP */ + "rfc3920res", /* USPREP_RFC3920_RESOURCEPREP */ + "rfc4011", /* USPREP_RFC4011_MIB */ + "rfc4013", /* USPREP_RFC4013_SASLPREP */ + "rfc4505", /* USPREP_RFC4505_TRACE */ + "rfc4518", /* USPREP_RFC4518_LDAP */ + "rfc4518ci", /* USPREP_RFC4518_LDAP_CI */ +}; + +static UBool U_CALLCONV +isSPrepAcceptable(void * /* context */, + const char * /* type */, + const char * /* name */, + const UDataInfo *pInfo) { + if( + pInfo->size>=20 && + pInfo->isBigEndian==U_IS_BIG_ENDIAN && + pInfo->charsetFamily==U_CHARSET_FAMILY && + pInfo->dataFormat[0]==0x53 && /* dataFormat="SPRP" */ + pInfo->dataFormat[1]==0x50 && + pInfo->dataFormat[2]==0x52 && + pInfo->dataFormat[3]==0x50 && + pInfo->formatVersion[0]==3 && + pInfo->formatVersion[2]==UTRIE_SHIFT && + pInfo->formatVersion[3]==UTRIE_INDEX_SHIFT + ) { + //uprv_memcpy(formatVersion, pInfo->formatVersion, 4); + uprv_memcpy(dataVersion, pInfo->dataVersion, 4); + return true; + } else { + return false; + } +} + +static int32_t U_CALLCONV +getSPrepFoldingOffset(uint32_t data) { + + return (int32_t)data; + +} + +/* hashes an entry */ +static int32_t U_CALLCONV +hashEntry(const UHashTok parm) { + UStringPrepKey *b = (UStringPrepKey *)parm.pointer; + UHashTok namekey, pathkey; + namekey.pointer = b->name; + pathkey.pointer = b->path; + uint32_t unsignedHash = static_cast(uhash_hashChars(namekey)) + + 37u * static_cast(uhash_hashChars(pathkey)); + return static_cast(unsignedHash); +} + +/* compares two entries */ +static UBool U_CALLCONV +compareEntries(const UHashTok p1, const UHashTok p2) { + UStringPrepKey *b1 = (UStringPrepKey *)p1.pointer; + UStringPrepKey *b2 = (UStringPrepKey *)p2.pointer; + UHashTok name1, name2, path1, path2; + name1.pointer = b1->name; + name2.pointer = b2->name; + path1.pointer = b1->path; + path2.pointer = b2->path; + return ((UBool)(uhash_compareChars(name1, name2) & + uhash_compareChars(path1, path2))); +} + +static void +usprep_unload(UStringPrepProfile* data){ + udata_close(data->sprepData); +} + +static int32_t +usprep_internal_flushCache(UBool noRefCount){ + UStringPrepProfile *profile = nullptr; + UStringPrepKey *key = nullptr; + int32_t pos = UHASH_FIRST; + int32_t deletedNum = 0; + const UHashElement *e; + + /* + * if shared data hasn't even been lazy evaluated yet + * return 0 + */ + umtx_lock(&usprepMutex); + if (SHARED_DATA_HASHTABLE == nullptr) { + umtx_unlock(&usprepMutex); + return 0; + } + + /*creates an enumeration to iterate through every element in the table */ + while ((e = uhash_nextElement(SHARED_DATA_HASHTABLE, &pos)) != nullptr) + { + profile = (UStringPrepProfile *) e->value.pointer; + key = (UStringPrepKey *) e->key.pointer; + + if ((noRefCount== false && profile->refCount == 0) || + noRefCount) { + deletedNum++; + uhash_removeElement(SHARED_DATA_HASHTABLE, e); + + /* unload the data */ + usprep_unload(profile); + + if(key->name != nullptr) { + uprv_free(key->name); + key->name=nullptr; + } + if(key->path != nullptr) { + uprv_free(key->path); + key->path=nullptr; + } + uprv_free(profile); + uprv_free(key); + } + + } + umtx_unlock(&usprepMutex); + + return deletedNum; +} + +/* Works just like ucnv_flushCache() +static int32_t +usprep_flushCache(){ + return usprep_internal_flushCache(false); +} +*/ + +static UBool U_CALLCONV usprep_cleanup(){ + if (SHARED_DATA_HASHTABLE != nullptr) { + usprep_internal_flushCache(true); + if (SHARED_DATA_HASHTABLE != nullptr && uhash_count(SHARED_DATA_HASHTABLE) == 0) { + uhash_close(SHARED_DATA_HASHTABLE); + SHARED_DATA_HASHTABLE = nullptr; + } + } + gSharedDataInitOnce.reset(); + return (SHARED_DATA_HASHTABLE == nullptr); +} +U_CDECL_END + + +/** Initializes the cache for resources */ +static void U_CALLCONV +createCache(UErrorCode &status) { + SHARED_DATA_HASHTABLE = uhash_open(hashEntry, compareEntries, nullptr, &status); + if (U_FAILURE(status)) { + SHARED_DATA_HASHTABLE = nullptr; + } + ucln_common_registerCleanup(UCLN_COMMON_USPREP, usprep_cleanup); +} + +static void +initCache(UErrorCode *status) { + umtx_initOnce(gSharedDataInitOnce, &createCache, *status); +} + +static UBool U_CALLCONV +loadData(UStringPrepProfile* profile, + const char* path, + const char* name, + const char* type, + UErrorCode* errorCode) { + /* load Unicode SPREP data from file */ + UTrie _sprepTrie={ 0,0,0,0,0,0,0 }; + UDataMemory *dataMemory; + const int32_t *p=nullptr; + const uint8_t *pb; + UVersionInfo normUnicodeVersion; + int32_t normUniVer, sprepUniVer, normCorrVer; + + if(errorCode==nullptr || U_FAILURE(*errorCode)) { + return 0; + } + + /* open the data outside the mutex block */ + //TODO: change the path + dataMemory=udata_openChoice(path, type, name, isSPrepAcceptable, nullptr, errorCode); + if(U_FAILURE(*errorCode)) { + return false; + } + + p=(const int32_t *)udata_getMemory(dataMemory); + pb=(const uint8_t *)(p+_SPREP_INDEX_TOP); + utrie_unserialize(&_sprepTrie, pb, p[_SPREP_INDEX_TRIE_SIZE], errorCode); + _sprepTrie.getFoldingOffset=getSPrepFoldingOffset; + + + if(U_FAILURE(*errorCode)) { + udata_close(dataMemory); + return false; + } + + /* in the mutex block, set the data for this process */ + umtx_lock(&usprepMutex); + if(profile->sprepData==nullptr) { + profile->sprepData=dataMemory; + dataMemory=nullptr; + uprv_memcpy(&profile->indexes, p, sizeof(profile->indexes)); + uprv_memcpy(&profile->sprepTrie, &_sprepTrie, sizeof(UTrie)); + } else { + p=(const int32_t *)udata_getMemory(profile->sprepData); + } + umtx_unlock(&usprepMutex); + /* initialize some variables */ + profile->mappingData=(uint16_t *)((uint8_t *)(p+_SPREP_INDEX_TOP)+profile->indexes[_SPREP_INDEX_TRIE_SIZE]); + + u_getUnicodeVersion(normUnicodeVersion); + normUniVer = (normUnicodeVersion[0] << 24) + (normUnicodeVersion[1] << 16) + + (normUnicodeVersion[2] << 8 ) + (normUnicodeVersion[3]); + sprepUniVer = (dataVersion[0] << 24) + (dataVersion[1] << 16) + + (dataVersion[2] << 8 ) + (dataVersion[3]); + normCorrVer = profile->indexes[_SPREP_NORM_CORRECTNS_LAST_UNI_VERSION]; + + if(U_FAILURE(*errorCode)){ + udata_close(dataMemory); + return false; + } + if( normUniVer < sprepUniVer && /* the Unicode version of SPREP file must be less than the Unicode Version of the normalization data */ + normUniVer < normCorrVer && /* the Unicode version of the NormalizationCorrections.txt file should be less than the Unicode Version of the normalization data */ + ((profile->indexes[_SPREP_OPTIONS] & _SPREP_NORMALIZATION_ON) > 0) /* normalization turned on*/ + ){ + *errorCode = U_INVALID_FORMAT_ERROR; + udata_close(dataMemory); + return false; + } + profile->isDataLoaded = true; + + /* if a different thread set it first, then close the extra data */ + if(dataMemory!=nullptr) { + udata_close(dataMemory); /* nullptr if it was set correctly */ + } + + + return profile->isDataLoaded; +} + +static UStringPrepProfile* +usprep_getProfile(const char* path, + const char* name, + UErrorCode *status){ + + UStringPrepProfile* profile = nullptr; + + initCache(status); + + if(U_FAILURE(*status)){ + return nullptr; + } + + UStringPrepKey stackKey; + /* + * const is cast way to save malloc, strcpy and free calls + * we use the passed in pointers for fetching the data from the + * hash table which is safe + */ + stackKey.name = (char*) name; + stackKey.path = (char*) path; + + /* fetch the data from the cache */ + umtx_lock(&usprepMutex); + profile = (UStringPrepProfile*) (uhash_get(SHARED_DATA_HASHTABLE,&stackKey)); + if(profile != nullptr) { + profile->refCount++; + } + umtx_unlock(&usprepMutex); + + if(profile == nullptr) { + /* else load the data and put the data in the cache */ + LocalMemory newProfile; + if(newProfile.allocateInsteadAndReset() == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + /* load the data */ + if(!loadData(newProfile.getAlias(), path, name, _SPREP_DATA_TYPE, status) || U_FAILURE(*status) ){ + return nullptr; + } + + /* get the options */ + newProfile->doNFKC = (UBool)((newProfile->indexes[_SPREP_OPTIONS] & _SPREP_NORMALIZATION_ON) > 0); + newProfile->checkBiDi = (UBool)((newProfile->indexes[_SPREP_OPTIONS] & _SPREP_CHECK_BIDI_ON) > 0); + + LocalMemory key; + LocalMemory keyName; + LocalMemory keyPath; + if( key.allocateInsteadAndReset() == nullptr || + keyName.allocateInsteadAndCopy(static_cast(uprv_strlen(name)+1)) == nullptr || + (path != nullptr && + keyPath.allocateInsteadAndCopy(static_cast(uprv_strlen(path)+1)) == nullptr) + ) { + *status = U_MEMORY_ALLOCATION_ERROR; + usprep_unload(newProfile.getAlias()); + return nullptr; + } + + umtx_lock(&usprepMutex); + // If another thread already inserted the same key/value, refcount and cleanup our thread data + profile = (UStringPrepProfile*) (uhash_get(SHARED_DATA_HASHTABLE,&stackKey)); + if(profile != nullptr) { + profile->refCount++; + usprep_unload(newProfile.getAlias()); + } + else { + /* initialize the key members */ + key->name = keyName.orphan(); + uprv_strcpy(key->name, name); + if(path != nullptr){ + key->path = keyPath.orphan(); + uprv_strcpy(key->path, path); + } + profile = newProfile.orphan(); + + /* add the data object to the cache */ + profile->refCount = 1; + uhash_put(SHARED_DATA_HASHTABLE, key.orphan(), profile, status); + } + umtx_unlock(&usprepMutex); + } + + return profile; +} + +U_CAPI UStringPrepProfile* U_EXPORT2 +usprep_open(const char* path, + const char* name, + UErrorCode* status){ + + if(status == nullptr || U_FAILURE(*status)){ + return nullptr; + } + + /* initialize the profile struct members */ + return usprep_getProfile(path,name,status); +} + +U_CAPI UStringPrepProfile* U_EXPORT2 +usprep_openByType(UStringPrepProfileType type, + UErrorCode* status) { + if(status == nullptr || U_FAILURE(*status)){ + return nullptr; + } + int32_t index = (int32_t)type; + if (index < 0 || index >= UPRV_LENGTHOF(PROFILE_NAMES)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + return usprep_open(nullptr, PROFILE_NAMES[index], status); +} + +U_CAPI void U_EXPORT2 +usprep_close(UStringPrepProfile* profile){ + if(profile==nullptr){ + return; + } + + umtx_lock(&usprepMutex); + /* decrement the ref count*/ + if(profile->refCount > 0){ + profile->refCount--; + } + umtx_unlock(&usprepMutex); + +} + +U_CFUNC void +uprv_syntaxError(const char16_t* rules, + int32_t pos, + int32_t rulesLen, + UParseError* parseError){ + if(parseError == nullptr){ + return; + } + parseError->offset = pos; + parseError->line = 0 ; // we are not using line numbers + + // for pre-context + int32_t start = (pos < U_PARSE_CONTEXT_LEN)? 0 : (pos - (U_PARSE_CONTEXT_LEN-1)); + int32_t limit = pos; + + u_memcpy(parseError->preContext,rules+start,limit-start); + //null terminate the buffer + parseError->preContext[limit-start] = 0; + + // for post-context; include error rules[pos] + start = pos; + limit = start + (U_PARSE_CONTEXT_LEN-1); + if (limit > rulesLen) { + limit = rulesLen; + } + if (start < rulesLen) { + u_memcpy(parseError->postContext,rules+start,limit-start); + } + //null terminate the buffer + parseError->postContext[limit-start]= 0; +} + + +static inline UStringPrepType +getValues(uint16_t trieWord, int16_t& value, UBool& isIndex){ + + UStringPrepType type; + if(trieWord == 0){ + /* + * Initial value stored in the mapping table + * just return USPREP_TYPE_LIMIT .. so that + * the source codepoint is copied to the destination + */ + type = USPREP_TYPE_LIMIT; + isIndex =false; + value = 0; + }else if(trieWord >= _SPREP_TYPE_THRESHOLD){ + type = (UStringPrepType) (trieWord - _SPREP_TYPE_THRESHOLD); + isIndex =false; + value = 0; + }else{ + /* get the type */ + type = USPREP_MAP; + /* ascertain if the value is index or delta */ + if(trieWord & 0x02){ + isIndex = true; + value = trieWord >> 2; //mask off the lower 2 bits and shift + }else{ + isIndex = false; + value = (int16_t)trieWord; + value = (value >> 2); + } + + if((trieWord>>2) == _SPREP_MAX_INDEX_VALUE){ + type = USPREP_DELETE; + isIndex =false; + value = 0; + } + } + return type; +} + +// TODO: change to writing to UnicodeString not char16_t * +static int32_t +usprep_map( const UStringPrepProfile* profile, + const char16_t* src, int32_t srcLength, + char16_t* dest, int32_t destCapacity, + int32_t options, + UParseError* parseError, + UErrorCode* status ){ + + uint16_t result; + int32_t destIndex=0; + int32_t srcIndex; + UBool allowUnassigned = (UBool) ((options & USPREP_ALLOW_UNASSIGNED)>0); + UStringPrepType type; + int16_t value; + UBool isIndex; + const int32_t* indexes = profile->indexes; + + // no error checking the caller check for error and arguments + // no string length check the caller finds out the string length + + for(srcIndex=0;srcIndexsprepTrie,ch,result); + + type = getValues(result, value, isIndex); + + // check if the source codepoint is unassigned + if(type == USPREP_UNASSIGNED && allowUnassigned == false){ + + uprv_syntaxError(src,srcIndex-U16_LENGTH(ch), srcLength,parseError); + *status = U_STRINGPREP_UNASSIGNED_ERROR; + return 0; + + }else if(type == USPREP_MAP){ + + int32_t index, length; + + if(isIndex){ + index = value; + if(index >= indexes[_SPREP_ONE_UCHAR_MAPPING_INDEX_START] && + index < indexes[_SPREP_TWO_UCHARS_MAPPING_INDEX_START]){ + length = 1; + }else if(index >= indexes[_SPREP_TWO_UCHARS_MAPPING_INDEX_START] && + index < indexes[_SPREP_THREE_UCHARS_MAPPING_INDEX_START]){ + length = 2; + }else if(index >= indexes[_SPREP_THREE_UCHARS_MAPPING_INDEX_START] && + index < indexes[_SPREP_FOUR_UCHARS_MAPPING_INDEX_START]){ + length = 3; + }else{ + length = profile->mappingData[index++]; + + } + + /* copy mapping to destination */ + for(int32_t i=0; i< length; i++){ + if(destIndex < destCapacity ){ + dest[destIndex] = profile->mappingData[index+i]; + } + destIndex++; /* for pre-flighting */ + } + continue; + }else{ + // subtract the delta to arrive at the code point + ch -= value; + } + + }else if(type==USPREP_DELETE){ + // just consume the codepoint and continue + continue; + } + //copy the code point into destination + if(ch <= 0xFFFF){ + if(destIndex < destCapacity ){ + dest[destIndex] = (char16_t)ch; + } + destIndex++; + }else{ + if(destIndex+1 < destCapacity ){ + dest[destIndex] = U16_LEAD(ch); + dest[destIndex+1] = U16_TRAIL(ch); + } + destIndex +=2; + } + + } + + return u_terminateUChars(dest, destCapacity, destIndex, status); +} + +/* + 1) Map -- For each character in the input, check if it has a mapping + and, if so, replace it with its mapping. + + 2) Normalize -- Possibly normalize the result of step 1 using Unicode + normalization. + + 3) Prohibit -- Check for any characters that are not allowed in the + output. If any are found, return an error. + + 4) Check bidi -- Possibly check for right-to-left characters, and if + any are found, make sure that the whole string satisfies the + requirements for bidirectional strings. If the string does not + satisfy the requirements for bidirectional strings, return an + error. + [Unicode3.2] defines several bidirectional categories; each character + has one bidirectional category assigned to it. For the purposes of + the requirements below, an "RandALCat character" is a character that + has Unicode bidirectional categories "R" or "AL"; an "LCat character" + is a character that has Unicode bidirectional category "L". Note + + + that there are many characters which fall in neither of the above + definitions; Latin digits ( through ) are examples of + this because they have bidirectional category "EN". + + In any profile that specifies bidirectional character handling, all + three of the following requirements MUST be met: + + 1) The characters in section 5.8 MUST be prohibited. + + 2) If a string contains any RandALCat character, the string MUST NOT + contain any LCat character. + + 3) If a string contains any RandALCat character, a RandALCat + character MUST be the first character of the string, and a + RandALCat character MUST be the last character of the string. +*/ +U_CAPI int32_t U_EXPORT2 +usprep_prepare( const UStringPrepProfile* profile, + const char16_t* src, int32_t srcLength, + char16_t* dest, int32_t destCapacity, + int32_t options, + UParseError* parseError, + UErrorCode* status ){ + + // check error status + if(U_FAILURE(*status)){ + return 0; + } + + //check arguments + if(profile==nullptr || + (src==nullptr ? srcLength!=0 : srcLength<-1) || + (dest==nullptr ? destCapacity!=0 : destCapacity<0)) { + *status=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + //get the string length + if(srcLength < 0){ + srcLength = u_strlen(src); + } + // map + UnicodeString s1; + char16_t *b1 = s1.getBuffer(srcLength); + if(b1==nullptr){ + *status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + int32_t b1Len = usprep_map(profile, src, srcLength, + b1, s1.getCapacity(), options, parseError, status); + s1.releaseBuffer(U_SUCCESS(*status) ? b1Len : 0); + + if(*status == U_BUFFER_OVERFLOW_ERROR){ + // redo processing of string + /* we do not have enough room so grow the buffer*/ + b1 = s1.getBuffer(b1Len); + if(b1==nullptr){ + *status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + + *status = U_ZERO_ERROR; // reset error + b1Len = usprep_map(profile, src, srcLength, + b1, s1.getCapacity(), options, parseError, status); + s1.releaseBuffer(U_SUCCESS(*status) ? b1Len : 0); + } + if(U_FAILURE(*status)){ + return 0; + } + + // normalize + UnicodeString s2; + if(profile->doNFKC){ + const Normalizer2 *n2 = Normalizer2::getNFKCInstance(*status); + FilteredNormalizer2 fn2(*n2, *uniset_getUnicode32Instance(*status)); + if(U_FAILURE(*status)){ + return 0; + } + fn2.normalize(s1, s2, *status); + }else{ + s2.fastCopyFrom(s1); + } + if(U_FAILURE(*status)){ + return 0; + } + + // Prohibit and checkBiDi in one pass + const char16_t *b2 = s2.getBuffer(); + int32_t b2Len = s2.length(); + UCharDirection direction=U_CHAR_DIRECTION_COUNT, firstCharDir=U_CHAR_DIRECTION_COUNT; + UBool leftToRight=false, rightToLeft=false; + int32_t rtlPos =-1, ltrPos =-1; + + for(int32_t b2Index=0; b2IndexsprepTrie,ch,result); + + int16_t value; + UBool isIndex; + UStringPrepType type = getValues(result, value, isIndex); + + if( type == USPREP_PROHIBITED || + ((result < _SPREP_TYPE_THRESHOLD) && (result & 0x01) /* first bit says it the code point is prohibited*/) + ){ + *status = U_STRINGPREP_PROHIBITED_ERROR; + uprv_syntaxError(b2, b2Index-U16_LENGTH(ch), b2Len, parseError); + return 0; + } + + if(profile->checkBiDi) { + direction = ubidi_getClass(ch); + if(firstCharDir == U_CHAR_DIRECTION_COUNT){ + firstCharDir = direction; + } + if(direction == U_LEFT_TO_RIGHT){ + leftToRight = true; + ltrPos = b2Index-1; + } + if(direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC){ + rightToLeft = true; + rtlPos = b2Index-1; + } + } + } + if(profile->checkBiDi){ + // satisfy 2 + if( leftToRight && rightToLeft){ + *status = U_STRINGPREP_CHECK_BIDI_ERROR; + uprv_syntaxError(b2,(rtlPos>ltrPos) ? rtlPos : ltrPos, b2Len, parseError); + return 0; + } + + //satisfy 3 + if( rightToLeft && + !((firstCharDir == U_RIGHT_TO_LEFT || firstCharDir == U_RIGHT_TO_LEFT_ARABIC) && + (direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC)) + ){ + *status = U_STRINGPREP_CHECK_BIDI_ERROR; + uprv_syntaxError(b2, rtlPos, b2Len, parseError); + return false; + } + } + return s2.extract(dest, destCapacity, *status); +} + + +/* data swapping ------------------------------------------------------------ */ + +U_CAPI int32_t U_EXPORT2 +usprep_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const UDataInfo *pInfo; + int32_t headerSize; + + const uint8_t *inBytes; + uint8_t *outBytes; + + const int32_t *inIndexes; + int32_t indexes[16]; + + int32_t i, offset, count, size; + + /* udata_swapDataHeader checks the arguments */ + headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + + /* check data format and format version */ + pInfo=(const UDataInfo *)((const char *)inData+4); + if(!( + pInfo->dataFormat[0]==0x53 && /* dataFormat="SPRP" */ + pInfo->dataFormat[1]==0x50 && + pInfo->dataFormat[2]==0x52 && + pInfo->dataFormat[3]==0x50 && + pInfo->formatVersion[0]==3 + )) { + udata_printError(ds, "usprep_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as StringPrep .spp data\n", + pInfo->dataFormat[0], pInfo->dataFormat[1], + pInfo->dataFormat[2], pInfo->dataFormat[3], + pInfo->formatVersion[0]); + *pErrorCode=U_UNSUPPORTED_ERROR; + return 0; + } + + inBytes=(const uint8_t *)inData+headerSize; + outBytes= (outData == nullptr ) ? nullptr : (uint8_t *)outData+headerSize; + + inIndexes=(const int32_t *)inBytes; + + if(length>=0) { + length-=headerSize; + if(length<16*4) { + udata_printError(ds, "usprep_swap(): too few bytes (%d after header) for StringPrep .spp data\n", + length); + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + } + + /* read the first 16 indexes (ICU 2.8/format version 3: _SPREP_INDEX_TOP==16, might grow) */ + for(i=0; i<16; ++i) { + indexes[i]=udata_readInt32(ds, inIndexes[i]); + } + + /* calculate the total length of the data */ + size= + 16*4+ /* size of indexes[] */ + indexes[_SPREP_INDEX_TRIE_SIZE]+ + indexes[_SPREP_INDEX_MAPPING_DATA_SIZE]; + + if(length>=0) { + if(lengthswapArray32(ds, inBytes, count, outBytes, pErrorCode); + offset+=count; + + /* swap the UTrie */ + count=indexes[_SPREP_INDEX_TRIE_SIZE]; + utrie_swap(ds, inBytes+offset, count, outBytes+offset, pErrorCode); + offset+=count; + + /* swap the uint16_t mappingTable[] */ + count=indexes[_SPREP_INDEX_MAPPING_DATA_SIZE]; + ds->swapArray16(ds, inBytes+offset, count, outBytes+offset, pErrorCode); + //offset+=count; + } + + return headerSize+size; +} + +#endif /* #if !UCONFIG_NO_IDNA */ diff --git a/deps/icu-small/source/common/ustack.cpp b/deps/icu-small/source/common/ustack.cpp index 8d9e475374d959..76a3eb0eb050a3 100644 --- a/deps/icu-small/source/common/ustack.cpp +++ b/deps/icu-small/source/common/ustack.cpp @@ -1,62 +1,62 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2003-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#include "uvector.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UStack) - -UStack::UStack(UErrorCode &status) : - UVector(status) -{ -} - -UStack::UStack(int32_t initialCapacity, UErrorCode &status) : - UVector(initialCapacity, status) -{ -} - -UStack::UStack(UObjectDeleter *d, UElementsAreEqual *c, UErrorCode &status) : - UVector(d, c, status) -{ -} - -UStack::UStack(UObjectDeleter *d, UElementsAreEqual *c, int32_t initialCapacity, UErrorCode &status) : - UVector(d, c, initialCapacity, status) -{ -} - -UStack::~UStack() {} - -void* UStack::pop(void) { - int32_t n = size() - 1; - void* result = nullptr; - if (n >= 0) { - result = orphanElementAt(n); - } - return result; -} - -int32_t UStack::popi(void) { - int32_t n = size() - 1; - int32_t result = 0; - if (n >= 0) { - result = elementAti(n); - removeElementAt(n); - } - return result; -} - -int32_t UStack::search(void* obj) const { - int32_t i = indexOf(obj); - return (i >= 0) ? size() - i : i; -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2003-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#include "uvector.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UStack) + +UStack::UStack(UErrorCode &status) : + UVector(status) +{ +} + +UStack::UStack(int32_t initialCapacity, UErrorCode &status) : + UVector(initialCapacity, status) +{ +} + +UStack::UStack(UObjectDeleter *d, UElementsAreEqual *c, UErrorCode &status) : + UVector(d, c, status) +{ +} + +UStack::UStack(UObjectDeleter *d, UElementsAreEqual *c, int32_t initialCapacity, UErrorCode &status) : + UVector(d, c, initialCapacity, status) +{ +} + +UStack::~UStack() {} + +void* UStack::pop() { + int32_t n = size() - 1; + void* result = nullptr; + if (n >= 0) { + result = orphanElementAt(n); + } + return result; +} + +int32_t UStack::popi() { + int32_t n = size() - 1; + int32_t result = 0; + if (n >= 0) { + result = elementAti(n); + removeElementAt(n); + } + return result; +} + +int32_t UStack::search(void* obj) const { + int32_t i = indexOf(obj); + return (i >= 0) ? size() - i : i; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/ustr_cnv.cpp b/deps/icu-small/source/common/ustr_cnv.cpp index 97fbc527a3749c..f55e8ab9796aba 100644 --- a/deps/icu-small/source/common/ustr_cnv.cpp +++ b/deps/icu-small/source/common/ustr_cnv.cpp @@ -1,256 +1,256 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1998-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: ustr_cnv.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004aug24 -* created by: Markus W. Scherer -* -* Character conversion functions moved here from ustring.c -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ustring.h" -#include "unicode/ucnv.h" -#include "cstring.h" -#include "cmemory.h" -#include "umutex.h" -#include "ustr_cnv.h" -#include "ucnv_bld.h" - -/* mutexed access to a shared default converter ----------------------------- */ - -static UConverter *gDefaultConverter = NULL; - -U_CAPI UConverter* U_EXPORT2 -u_getDefaultConverter(UErrorCode *status) -{ - UConverter *converter = NULL; - - if (gDefaultConverter != NULL) { - icu::umtx_lock(NULL); - - /* need to check to make sure it wasn't taken out from under us */ - if (gDefaultConverter != NULL) { - converter = gDefaultConverter; - gDefaultConverter = NULL; - } - icu::umtx_unlock(NULL); - } - - /* if the cache was empty, create a converter */ - if(converter == NULL) { - converter = ucnv_open(NULL, status); - if(U_FAILURE(*status)) { - ucnv_close(converter); - converter = NULL; - } - } - - return converter; -} - -U_CAPI void U_EXPORT2 -u_releaseDefaultConverter(UConverter *converter) -{ - if(gDefaultConverter == NULL) { - if (converter != NULL) { - ucnv_reset(converter); - } - ucnv_enableCleanup(); - icu::umtx_lock(NULL); - if(gDefaultConverter == NULL) { - gDefaultConverter = converter; - converter = NULL; - } - icu::umtx_unlock(NULL); - } - - if(converter != NULL) { - ucnv_close(converter); - } -} - -U_CAPI void U_EXPORT2 -u_flushDefaultConverter() -{ - UConverter *converter = NULL; - - if (gDefaultConverter != NULL) { - icu::umtx_lock(NULL); - - /* need to check to make sure it wasn't taken out from under us */ - if (gDefaultConverter != NULL) { - converter = gDefaultConverter; - gDefaultConverter = NULL; - } - icu::umtx_unlock(NULL); - } - - /* if the cache was populated, flush it */ - if(converter != NULL) { - ucnv_close(converter); - } -} - - -/* conversions between char* and UChar* ------------------------------------- */ - -/* maximum string length for u_uastrcpy() and u_austrcpy() implementations */ -#define MAX_STRLEN 0x0FFFFFFF - -/* - returns the minimum of (the length of the null-terminated string) and n. -*/ -static int32_t u_astrnlen(const char *s1, int32_t n) -{ - int32_t len = 0; - - if (s1) - { - while (n-- && *(s1++)) - { - len++; - } - } - return len; -} - -U_CAPI UChar* U_EXPORT2 -u_uastrncpy(UChar *ucs1, - const char *s2, - int32_t n) -{ - UChar *target = ucs1; - UErrorCode err = U_ZERO_ERROR; - UConverter *cnv = u_getDefaultConverter(&err); - if(U_SUCCESS(err) && cnv != NULL) { - ucnv_reset(cnv); - ucnv_toUnicode(cnv, - &target, - ucs1+n, - &s2, - s2+u_astrnlen(s2, n), - NULL, - true, - &err); - ucnv_reset(cnv); /* be good citizens */ - u_releaseDefaultConverter(cnv); - if(U_FAILURE(err) && (err != U_BUFFER_OVERFLOW_ERROR) ) { - *ucs1 = 0; /* failure */ - } - if(target < (ucs1+n)) { /* U_BUFFER_OVERFLOW_ERROR isn't an err, just means no termination will happen. */ - *target = 0; /* terminate */ - } - } else { - *ucs1 = 0; - } - return ucs1; -} - -U_CAPI UChar* U_EXPORT2 -u_uastrcpy(UChar *ucs1, - const char *s2 ) -{ - UErrorCode err = U_ZERO_ERROR; - UConverter *cnv = u_getDefaultConverter(&err); - if(U_SUCCESS(err) && cnv != NULL) { - ucnv_toUChars(cnv, - ucs1, - MAX_STRLEN, - s2, - (int32_t)uprv_strlen(s2), - &err); - u_releaseDefaultConverter(cnv); - if(U_FAILURE(err)) { - *ucs1 = 0; - } - } else { - *ucs1 = 0; - } - return ucs1; -} - -/* - returns the minimum of (the length of the null-terminated string) and n. -*/ -static int32_t u_ustrnlen(const UChar *ucs1, int32_t n) -{ - int32_t len = 0; - - if (ucs1) - { - while (n-- && *(ucs1++)) - { - len++; - } - } - return len; -} - -U_CAPI char* U_EXPORT2 -u_austrncpy(char *s1, - const UChar *ucs2, - int32_t n) -{ - char *target = s1; - UErrorCode err = U_ZERO_ERROR; - UConverter *cnv = u_getDefaultConverter(&err); - if(U_SUCCESS(err) && cnv != NULL) { - ucnv_reset(cnv); - ucnv_fromUnicode(cnv, - &target, - s1+n, - &ucs2, - ucs2+u_ustrnlen(ucs2, n), - NULL, - true, - &err); - ucnv_reset(cnv); /* be good citizens */ - u_releaseDefaultConverter(cnv); - if(U_FAILURE(err) && (err != U_BUFFER_OVERFLOW_ERROR) ) { - *s1 = 0; /* failure */ - } - if(target < (s1+n)) { /* U_BUFFER_OVERFLOW_ERROR isn't an err, just means no termination will happen. */ - *target = 0; /* terminate */ - } - } else { - *s1 = 0; - } - return s1; -} - -U_CAPI char* U_EXPORT2 -u_austrcpy(char *s1, - const UChar *ucs2 ) -{ - UErrorCode err = U_ZERO_ERROR; - UConverter *cnv = u_getDefaultConverter(&err); - if(U_SUCCESS(err) && cnv != NULL) { - int32_t len = ucnv_fromUChars(cnv, - s1, - MAX_STRLEN, - ucs2, - -1, - &err); - u_releaseDefaultConverter(cnv); - s1[len] = 0; - } else { - *s1 = 0; - } - return s1; -} - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1998-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: ustr_cnv.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004aug24 +* created by: Markus W. Scherer +* +* Character conversion functions moved here from ustring.c +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ustring.h" +#include "unicode/ucnv.h" +#include "cstring.h" +#include "cmemory.h" +#include "umutex.h" +#include "ustr_cnv.h" +#include "ucnv_bld.h" + +/* mutexed access to a shared default converter ----------------------------- */ + +static UConverter *gDefaultConverter = nullptr; + +U_CAPI UConverter* U_EXPORT2 +u_getDefaultConverter(UErrorCode *status) +{ + UConverter *converter = nullptr; + + if (gDefaultConverter != nullptr) { + icu::umtx_lock(nullptr); + + /* need to check to make sure it wasn't taken out from under us */ + if (gDefaultConverter != nullptr) { + converter = gDefaultConverter; + gDefaultConverter = nullptr; + } + icu::umtx_unlock(nullptr); + } + + /* if the cache was empty, create a converter */ + if(converter == nullptr) { + converter = ucnv_open(nullptr, status); + if(U_FAILURE(*status)) { + ucnv_close(converter); + converter = nullptr; + } + } + + return converter; +} + +U_CAPI void U_EXPORT2 +u_releaseDefaultConverter(UConverter *converter) +{ + if(gDefaultConverter == nullptr) { + if (converter != nullptr) { + ucnv_reset(converter); + } + ucnv_enableCleanup(); + icu::umtx_lock(nullptr); + if(gDefaultConverter == nullptr) { + gDefaultConverter = converter; + converter = nullptr; + } + icu::umtx_unlock(nullptr); + } + + if(converter != nullptr) { + ucnv_close(converter); + } +} + +U_CAPI void U_EXPORT2 +u_flushDefaultConverter() +{ + UConverter *converter = nullptr; + + if (gDefaultConverter != nullptr) { + icu::umtx_lock(nullptr); + + /* need to check to make sure it wasn't taken out from under us */ + if (gDefaultConverter != nullptr) { + converter = gDefaultConverter; + gDefaultConverter = nullptr; + } + icu::umtx_unlock(nullptr); + } + + /* if the cache was populated, flush it */ + if(converter != nullptr) { + ucnv_close(converter); + } +} + + +/* conversions between char* and char16_t* ------------------------------------- */ + +/* maximum string length for u_uastrcpy() and u_austrcpy() implementations */ +#define MAX_STRLEN 0x0FFFFFFF + +/* + returns the minimum of (the length of the null-terminated string) and n. +*/ +static int32_t u_astrnlen(const char *s1, int32_t n) +{ + int32_t len = 0; + + if (s1) + { + while (n-- && *(s1++)) + { + len++; + } + } + return len; +} + +U_CAPI char16_t* U_EXPORT2 +u_uastrncpy(char16_t *ucs1, + const char *s2, + int32_t n) +{ + char16_t *target = ucs1; + UErrorCode err = U_ZERO_ERROR; + UConverter *cnv = u_getDefaultConverter(&err); + if(U_SUCCESS(err) && cnv != nullptr) { + ucnv_reset(cnv); + ucnv_toUnicode(cnv, + &target, + ucs1+n, + &s2, + s2+u_astrnlen(s2, n), + nullptr, + true, + &err); + ucnv_reset(cnv); /* be good citizens */ + u_releaseDefaultConverter(cnv); + if(U_FAILURE(err) && (err != U_BUFFER_OVERFLOW_ERROR) ) { + *ucs1 = 0; /* failure */ + } + if(target < (ucs1+n)) { /* U_BUFFER_OVERFLOW_ERROR isn't an err, just means no termination will happen. */ + *target = 0; /* terminate */ + } + } else { + *ucs1 = 0; + } + return ucs1; +} + +U_CAPI char16_t* U_EXPORT2 +u_uastrcpy(char16_t *ucs1, + const char *s2 ) +{ + UErrorCode err = U_ZERO_ERROR; + UConverter *cnv = u_getDefaultConverter(&err); + if(U_SUCCESS(err) && cnv != nullptr) { + ucnv_toUChars(cnv, + ucs1, + MAX_STRLEN, + s2, + (int32_t)uprv_strlen(s2), + &err); + u_releaseDefaultConverter(cnv); + if(U_FAILURE(err)) { + *ucs1 = 0; + } + } else { + *ucs1 = 0; + } + return ucs1; +} + +/* + returns the minimum of (the length of the null-terminated string) and n. +*/ +static int32_t u_ustrnlen(const char16_t *ucs1, int32_t n) +{ + int32_t len = 0; + + if (ucs1) + { + while (n-- && *(ucs1++)) + { + len++; + } + } + return len; +} + +U_CAPI char* U_EXPORT2 +u_austrncpy(char *s1, + const char16_t *ucs2, + int32_t n) +{ + char *target = s1; + UErrorCode err = U_ZERO_ERROR; + UConverter *cnv = u_getDefaultConverter(&err); + if(U_SUCCESS(err) && cnv != nullptr) { + ucnv_reset(cnv); + ucnv_fromUnicode(cnv, + &target, + s1+n, + &ucs2, + ucs2+u_ustrnlen(ucs2, n), + nullptr, + true, + &err); + ucnv_reset(cnv); /* be good citizens */ + u_releaseDefaultConverter(cnv); + if(U_FAILURE(err) && (err != U_BUFFER_OVERFLOW_ERROR) ) { + *s1 = 0; /* failure */ + } + if(target < (s1+n)) { /* U_BUFFER_OVERFLOW_ERROR isn't an err, just means no termination will happen. */ + *target = 0; /* terminate */ + } + } else { + *s1 = 0; + } + return s1; +} + +U_CAPI char* U_EXPORT2 +u_austrcpy(char *s1, + const char16_t *ucs2 ) +{ + UErrorCode err = U_ZERO_ERROR; + UConverter *cnv = u_getDefaultConverter(&err); + if(U_SUCCESS(err) && cnv != nullptr) { + int32_t len = ucnv_fromUChars(cnv, + s1, + MAX_STRLEN, + ucs2, + -1, + &err); + u_releaseDefaultConverter(cnv); + s1[len] = 0; + } else { + *s1 = 0; + } + return s1; +} + +#endif diff --git a/deps/icu-small/source/common/ustr_cnv.h b/deps/icu-small/source/common/ustr_cnv.h index 861e3ebff0613b..70975259ba9290 100644 --- a/deps/icu-small/source/common/ustr_cnv.h +++ b/deps/icu-small/source/common/ustr_cnv.h @@ -1,51 +1,51 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2010, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: ustr_cnv.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004Aug27 -* created by: George Rhoten -*/ - -#ifndef USTR_CNV_IMP_H -#define USTR_CNV_IMP_H - -#include "unicode/utypes.h" -#include "unicode/ucnv.h" - -#if !UCONFIG_NO_CONVERSION - -/** - * Get the default converter. This is a commonly used converter - * that is used for the ustring and UnicodeString API. - * Remember to use the u_releaseDefaultConverter when you are done. - * @internal - */ -U_CAPI UConverter* U_EXPORT2 -u_getDefaultConverter(UErrorCode *status); - - -/** - * Release the default converter to the converter cache. - * @internal - */ -U_CAPI void U_EXPORT2 -u_releaseDefaultConverter(UConverter *converter); - -/** - * Flush the default converter, if cached. - * @internal - */ -U_CAPI void U_EXPORT2 -u_flushDefaultConverter(void); - -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2010, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: ustr_cnv.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004Aug27 +* created by: George Rhoten +*/ + +#ifndef USTR_CNV_IMP_H +#define USTR_CNV_IMP_H + +#include "unicode/utypes.h" +#include "unicode/ucnv.h" + +#if !UCONFIG_NO_CONVERSION + +/** + * Get the default converter. This is a commonly used converter + * that is used for the ustring and UnicodeString API. + * Remember to use the u_releaseDefaultConverter when you are done. + * @internal + */ +U_CAPI UConverter* U_EXPORT2 +u_getDefaultConverter(UErrorCode *status); + + +/** + * Release the default converter to the converter cache. + * @internal + */ +U_CAPI void U_EXPORT2 +u_releaseDefaultConverter(UConverter *converter); + +/** + * Flush the default converter, if cached. + * @internal + */ +U_CAPI void U_EXPORT2 +u_flushDefaultConverter(void); + +#endif + +#endif diff --git a/deps/icu-small/source/common/ustr_imp.h b/deps/icu-small/source/common/ustr_imp.h index 3c4b9cc2a5397a..c041fdb1e34cb1 100644 --- a/deps/icu-small/source/common/ustr_imp.h +++ b/deps/icu-small/source/common/ustr_imp.h @@ -1,155 +1,155 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2015, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* file name: ustr_imp.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2001jan30 -* created by: Markus W. Scherer -*/ - -#ifndef __USTR_IMP_H__ -#define __USTR_IMP_H__ - -#include "unicode/utypes.h" -#include "unicode/utf8.h" - -/** - * Internal option for unorm_cmpEquivFold() for strncmp style. - * If set, checks for both string length and terminating NUL. - */ -#define _STRNCMP_STYLE 0x1000 - -/** - * Compare two strings in code point order or code unit order. - * Works in strcmp style (both lengths -1), - * strncmp style (lengths equal and >=0, flag true), - * and memcmp/UnicodeString style (at least one length >=0). - */ -U_CFUNC int32_t U_EXPORT2 -uprv_strCompare(const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - UBool strncmpStyle, UBool codePointOrder); - -U_CAPI int32_t U_EXPORT2 -ustr_hashUCharsN(const UChar *str, int32_t length); - -U_CAPI int32_t U_EXPORT2 -ustr_hashCharsN(const char *str, int32_t length); - -U_CAPI int32_t U_EXPORT2 -ustr_hashICharsN(const char *str, int32_t length); - -/** - * Convert an ASCII-range lowercase character to uppercase. - * - * @param c A UChar. - * @return If UChar is a lowercase ASCII character, returns the uppercase version. - * Otherwise, returns the input character. - */ -U_CAPI UChar U_EXPORT2 -u_asciiToUpper(UChar c); - -// TODO: Add u_asciiToLower if/when there is a need for it. - -/** - * NUL-terminate a UChar * string if possible. - * If length < destCapacity then NUL-terminate. - * If length == destCapacity then do not terminate but set U_STRING_NOT_TERMINATED_WARNING. - * If length > destCapacity then do not terminate but set U_BUFFER_OVERFLOW_ERROR. - * - * @param dest Destination buffer, can be NULL if destCapacity==0. - * @param destCapacity Number of UChars available at dest. - * @param length Number of UChars that were (to be) written to dest. - * @param pErrorCode ICU error code. - * @return length - */ -U_CAPI int32_t U_EXPORT2 -u_terminateUChars(UChar *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode); - -/** - * NUL-terminate a char * string if possible. - * Same as u_terminateUChars() but for a different string type. - */ -U_CAPI int32_t U_EXPORT2 -u_terminateChars(char *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode); - -/** - * NUL-terminate a UChar32 * string if possible. - * Same as u_terminateUChars() but for a different string type. - */ -U_CAPI int32_t U_EXPORT2 -u_terminateUChar32s(UChar32 *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode); - -/** - * NUL-terminate a wchar_t * string if possible. - * Same as u_terminateUChars() but for a different string type. - */ -U_CAPI int32_t U_EXPORT2 -u_terminateWChars(wchar_t *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode); - -/** - * Counts the bytes of any whole valid sequence for a UTF-8 lead byte. - * Returns 1 for ASCII 0..0x7f. - * Returns 0 for 0x80..0xc1 as well as for 0xf5..0xff. - * leadByte might be evaluated multiple times. - * - * @param leadByte The first byte of a UTF-8 sequence. Must be 0..0xff. - * @return 0..4 - */ -#define U8_COUNT_BYTES(leadByte) \ - (U8_IS_SINGLE(leadByte) ? 1 : U8_COUNT_BYTES_NON_ASCII(leadByte)) - -/** - * Counts the bytes of any whole valid sequence for a UTF-8 lead byte. - * Returns 0 for 0x00..0xc1 as well as for 0xf5..0xff. - * leadByte might be evaluated multiple times. - * - * @param leadByte The first byte of a UTF-8 sequence. Must be 0..0xff. - * @return 0 or 2..4 - */ -#define U8_COUNT_BYTES_NON_ASCII(leadByte) \ - (U8_IS_LEAD(leadByte) ? ((uint8_t)(leadByte)>=0xe0)+((uint8_t)(leadByte)>=0xf0)+2 : 0) - -#ifdef __cplusplus - -U_NAMESPACE_BEGIN - -class UTF8 { -public: - UTF8() = delete; // all static - - /** - * Is t a valid UTF-8 trail byte? - * - * @param prev Must be the preceding lead byte if i==1 and length>=3; - * otherwise ignored. - * @param t The i-th byte following the lead byte. - * @param i The index (1..3) of byte t in the byte sequence. 0 1) { - return U8_IS_TRAIL(t); - } else if (length == 3) { - return U8_IS_VALID_LEAD3_AND_T1(prev, t); - } else { // length == 4 - return U8_IS_VALID_LEAD4_AND_T1(prev, t); - } - } -}; - -U_NAMESPACE_END - -#endif // __cplusplus - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2015, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* file name: ustr_imp.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2001jan30 +* created by: Markus W. Scherer +*/ + +#ifndef __USTR_IMP_H__ +#define __USTR_IMP_H__ + +#include "unicode/utypes.h" +#include "unicode/utf8.h" + +/** + * Internal option for unorm_cmpEquivFold() for strncmp style. + * If set, checks for both string length and terminating NUL. + */ +#define _STRNCMP_STYLE 0x1000 + +/** + * Compare two strings in code point order or code unit order. + * Works in strcmp style (both lengths -1), + * strncmp style (lengths equal and >=0, flag true), + * and memcmp/UnicodeString style (at least one length >=0). + */ +U_CFUNC int32_t U_EXPORT2 +uprv_strCompare(const UChar *s1, int32_t length1, + const UChar *s2, int32_t length2, + UBool strncmpStyle, UBool codePointOrder); + +U_CAPI int32_t U_EXPORT2 +ustr_hashUCharsN(const UChar *str, int32_t length); + +U_CAPI int32_t U_EXPORT2 +ustr_hashCharsN(const char *str, int32_t length); + +U_CAPI int32_t U_EXPORT2 +ustr_hashICharsN(const char *str, int32_t length); + +/** + * Convert an ASCII-range lowercase character to uppercase. + * + * @param c A UChar. + * @return If UChar is a lowercase ASCII character, returns the uppercase version. + * Otherwise, returns the input character. + */ +U_CAPI UChar U_EXPORT2 +u_asciiToUpper(UChar c); + +// TODO: Add u_asciiToLower if/when there is a need for it. + +/** + * NUL-terminate a UChar * string if possible. + * If length < destCapacity then NUL-terminate. + * If length == destCapacity then do not terminate but set U_STRING_NOT_TERMINATED_WARNING. + * If length > destCapacity then do not terminate but set U_BUFFER_OVERFLOW_ERROR. + * + * @param dest Destination buffer, can be NULL if destCapacity==0. + * @param destCapacity Number of UChars available at dest. + * @param length Number of UChars that were (to be) written to dest. + * @param pErrorCode ICU error code. + * @return length + */ +U_CAPI int32_t U_EXPORT2 +u_terminateUChars(UChar *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode); + +/** + * NUL-terminate a char * string if possible. + * Same as u_terminateUChars() but for a different string type. + */ +U_CAPI int32_t U_EXPORT2 +u_terminateChars(char *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode); + +/** + * NUL-terminate a UChar32 * string if possible. + * Same as u_terminateUChars() but for a different string type. + */ +U_CAPI int32_t U_EXPORT2 +u_terminateUChar32s(UChar32 *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode); + +/** + * NUL-terminate a wchar_t * string if possible. + * Same as u_terminateUChars() but for a different string type. + */ +U_CAPI int32_t U_EXPORT2 +u_terminateWChars(wchar_t *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode); + +/** + * Counts the bytes of any whole valid sequence for a UTF-8 lead byte. + * Returns 1 for ASCII 0..0x7f. + * Returns 0 for 0x80..0xc1 as well as for 0xf5..0xff. + * leadByte might be evaluated multiple times. + * + * @param leadByte The first byte of a UTF-8 sequence. Must be 0..0xff. + * @return 0..4 + */ +#define U8_COUNT_BYTES(leadByte) \ + (U8_IS_SINGLE(leadByte) ? 1 : U8_COUNT_BYTES_NON_ASCII(leadByte)) + +/** + * Counts the bytes of any whole valid sequence for a UTF-8 lead byte. + * Returns 0 for 0x00..0xc1 as well as for 0xf5..0xff. + * leadByte might be evaluated multiple times. + * + * @param leadByte The first byte of a UTF-8 sequence. Must be 0..0xff. + * @return 0 or 2..4 + */ +#define U8_COUNT_BYTES_NON_ASCII(leadByte) \ + (U8_IS_LEAD(leadByte) ? ((uint8_t)(leadByte)>=0xe0)+((uint8_t)(leadByte)>=0xf0)+2 : 0) + +#ifdef __cplusplus + +U_NAMESPACE_BEGIN + +class UTF8 { +public: + UTF8() = delete; // all static + + /** + * Is t a valid UTF-8 trail byte? + * + * @param prev Must be the preceding lead byte if i==1 and length>=3; + * otherwise ignored. + * @param t The i-th byte following the lead byte. + * @param i The index (1..3) of byte t in the byte sequence. 0 1) { + return U8_IS_TRAIL(t); + } else if (length == 3) { + return U8_IS_VALID_LEAD3_AND_T1(prev, t); + } else { // length == 4 + return U8_IS_VALID_LEAD4_AND_T1(prev, t); + } + } +}; + +U_NAMESPACE_END + +#endif // __cplusplus + +#endif diff --git a/deps/icu-small/source/common/ustr_titlecase_brkiter.cpp b/deps/icu-small/source/common/ustr_titlecase_brkiter.cpp index 85dfa0decb427a..3ebd4ed0c60c71 100644 --- a/deps/icu-small/source/common/ustr_titlecase_brkiter.cpp +++ b/deps/icu-small/source/common/ustr_titlecase_brkiter.cpp @@ -1,237 +1,237 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: ustr_titlecase_brkiter.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2011may30 -* created by: Markus W. Scherer -* -* Titlecasing functions that are based on BreakIterator -* were moved here to break dependency cycles among parts of the common library. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/brkiter.h" -#include "unicode/casemap.h" -#include "unicode/chariter.h" -#include "unicode/localpointer.h" -#include "unicode/ubrk.h" -#include "unicode/ucasemap.h" -#include "unicode/utext.h" -#include "cmemory.h" -#include "uassert.h" -#include "ucase.h" -#include "ucasemap_imp.h" - -U_NAMESPACE_BEGIN - -/** - * Whole-string BreakIterator. - * Titlecasing only calls setText(), first(), and next(). - * We implement the rest only to satisfy the abstract interface. - */ -class WholeStringBreakIterator : public BreakIterator { -public: - WholeStringBreakIterator() : BreakIterator(), length(0) {} - ~WholeStringBreakIterator() U_OVERRIDE; - bool operator==(const BreakIterator&) const U_OVERRIDE; - WholeStringBreakIterator *clone() const U_OVERRIDE; - static UClassID U_EXPORT2 getStaticClassID(); - UClassID getDynamicClassID() const U_OVERRIDE; - CharacterIterator &getText() const U_OVERRIDE; - UText *getUText(UText *fillIn, UErrorCode &errorCode) const U_OVERRIDE; - void setText(const UnicodeString &text) U_OVERRIDE; - void setText(UText *text, UErrorCode &errorCode) U_OVERRIDE; - void adoptText(CharacterIterator* it) U_OVERRIDE; - int32_t first() U_OVERRIDE; - int32_t last() U_OVERRIDE; - int32_t previous() U_OVERRIDE; - int32_t next() U_OVERRIDE; - int32_t current() const U_OVERRIDE; - int32_t following(int32_t offset) U_OVERRIDE; - int32_t preceding(int32_t offset) U_OVERRIDE; - UBool isBoundary(int32_t offset) U_OVERRIDE; - int32_t next(int32_t n) U_OVERRIDE; - WholeStringBreakIterator *createBufferClone(void *stackBuffer, int32_t &BufferSize, - UErrorCode &errorCode) U_OVERRIDE; - WholeStringBreakIterator &refreshInputText(UText *input, UErrorCode &errorCode) U_OVERRIDE; - -private: - int32_t length; -}; - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(WholeStringBreakIterator) - -WholeStringBreakIterator::~WholeStringBreakIterator() {} -bool WholeStringBreakIterator::operator==(const BreakIterator&) const { return false; } -WholeStringBreakIterator *WholeStringBreakIterator::clone() const { return nullptr; } - -CharacterIterator &WholeStringBreakIterator::getText() const { - UPRV_UNREACHABLE_EXIT; // really should not be called -} -UText *WholeStringBreakIterator::getUText(UText * /*fillIn*/, UErrorCode &errorCode) const { - if (U_SUCCESS(errorCode)) { - errorCode = U_UNSUPPORTED_ERROR; - } - return nullptr; -} - -void WholeStringBreakIterator::setText(const UnicodeString &text) { - length = text.length(); -} -void WholeStringBreakIterator::setText(UText *text, UErrorCode &errorCode) { - if (U_SUCCESS(errorCode)) { - int64_t length64 = utext_nativeLength(text); - if (length64 <= INT32_MAX) { - length = (int32_t)length64; - } else { - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - } - } -} -void WholeStringBreakIterator::adoptText(CharacterIterator*) { - UPRV_UNREACHABLE_EXIT; // should not be called -} - -int32_t WholeStringBreakIterator::first() { return 0; } -int32_t WholeStringBreakIterator::last() { return length; } -int32_t WholeStringBreakIterator::previous() { return 0; } -int32_t WholeStringBreakIterator::next() { return length; } -int32_t WholeStringBreakIterator::current() const { return 0; } -int32_t WholeStringBreakIterator::following(int32_t /*offset*/) { return length; } -int32_t WholeStringBreakIterator::preceding(int32_t /*offset*/) { return 0; } -UBool WholeStringBreakIterator::isBoundary(int32_t /*offset*/) { return false; } -int32_t WholeStringBreakIterator::next(int32_t /*n*/) { return length; } - -WholeStringBreakIterator *WholeStringBreakIterator::createBufferClone( - void * /*stackBuffer*/, int32_t & /*BufferSize*/, UErrorCode &errorCode) { - if (U_SUCCESS(errorCode)) { - errorCode = U_UNSUPPORTED_ERROR; - } - return nullptr; -} -WholeStringBreakIterator &WholeStringBreakIterator::refreshInputText( - UText * /*input*/, UErrorCode &errorCode) { - if (U_SUCCESS(errorCode)) { - errorCode = U_UNSUPPORTED_ERROR; - } - return *this; -} - -U_CFUNC -BreakIterator *ustrcase_getTitleBreakIterator( - const Locale *locale, const char *locID, uint32_t options, BreakIterator *iter, - LocalPointer &ownedIter, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return nullptr; } - options &= U_TITLECASE_ITERATOR_MASK; - if (options != 0 && iter != nullptr) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - if (iter == nullptr) { - switch (options) { - case 0: - iter = BreakIterator::createWordInstance( - locale != nullptr ? *locale : Locale(locID), errorCode); - break; - case U_TITLECASE_WHOLE_STRING: - iter = new WholeStringBreakIterator(); - if (iter == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - } - break; - case U_TITLECASE_SENTENCES: - iter = BreakIterator::createSentenceInstance( - locale != nullptr ? *locale : Locale(locID), errorCode); - break; - default: - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - ownedIter.adoptInstead(iter); - } - return iter; -} - -int32_t CaseMap::toTitle( - const char *locale, uint32_t options, BreakIterator *iter, - const UChar *src, int32_t srcLength, - UChar *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode) { - LocalPointer ownedIter; - iter = ustrcase_getTitleBreakIterator(nullptr, locale, options, iter, ownedIter, errorCode); - if(iter==NULL) { - return 0; - } - UnicodeString s(srcLength<0, src, srcLength); - iter->setText(s); - return ustrcase_map( - ustrcase_getCaseLocale(locale), options, iter, - dest, destCapacity, - src, srcLength, - ustrcase_internalToTitle, edits, errorCode); -} - -U_NAMESPACE_END - -U_NAMESPACE_USE - -U_CAPI int32_t U_EXPORT2 -u_strToTitle(UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - UBreakIterator *titleIter, - const char *locale, - UErrorCode *pErrorCode) { - LocalPointer ownedIter; - BreakIterator *iter = ustrcase_getTitleBreakIterator( - nullptr, locale, 0, reinterpret_cast(titleIter), - ownedIter, *pErrorCode); - if (iter == nullptr) { - return 0; - } - UnicodeString s(srcLength<0, src, srcLength); - iter->setText(s); - return ustrcase_mapWithOverlap( - ustrcase_getCaseLocale(locale), 0, iter, - dest, destCapacity, - src, srcLength, - ustrcase_internalToTitle, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -ucasemap_toTitle(UCaseMap *csm, - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - UErrorCode *pErrorCode) { - if (U_FAILURE(*pErrorCode)) { - return 0; - } - if (csm->iter == NULL) { - LocalPointer ownedIter; - BreakIterator *iter = ustrcase_getTitleBreakIterator( - nullptr, csm->locale, csm->options, nullptr, ownedIter, *pErrorCode); - if (iter == nullptr) { - return 0; - } - csm->iter = ownedIter.orphan(); - } - UnicodeString s(srcLength<0, src, srcLength); - csm->iter->setText(s); - return ustrcase_map( - csm->caseLocale, csm->options, csm->iter, - dest, destCapacity, - src, srcLength, - ustrcase_internalToTitle, NULL, *pErrorCode); -} - -#endif // !UCONFIG_NO_BREAK_ITERATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: ustr_titlecase_brkiter.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2011may30 +* created by: Markus W. Scherer +* +* Titlecasing functions that are based on BreakIterator +* were moved here to break dependency cycles among parts of the common library. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/brkiter.h" +#include "unicode/casemap.h" +#include "unicode/chariter.h" +#include "unicode/localpointer.h" +#include "unicode/ubrk.h" +#include "unicode/ucasemap.h" +#include "unicode/utext.h" +#include "cmemory.h" +#include "uassert.h" +#include "ucase.h" +#include "ucasemap_imp.h" + +U_NAMESPACE_BEGIN + +/** + * Whole-string BreakIterator. + * Titlecasing only calls setText(), first(), and next(). + * We implement the rest only to satisfy the abstract interface. + */ +class WholeStringBreakIterator : public BreakIterator { +public: + WholeStringBreakIterator() : BreakIterator(), length(0) {} + ~WholeStringBreakIterator() override; + bool operator==(const BreakIterator&) const override; + WholeStringBreakIterator *clone() const override; + static UClassID U_EXPORT2 getStaticClassID(); + UClassID getDynamicClassID() const override; + CharacterIterator &getText() const override; + UText *getUText(UText *fillIn, UErrorCode &errorCode) const override; + void setText(const UnicodeString &text) override; + void setText(UText *text, UErrorCode &errorCode) override; + void adoptText(CharacterIterator* it) override; + int32_t first() override; + int32_t last() override; + int32_t previous() override; + int32_t next() override; + int32_t current() const override; + int32_t following(int32_t offset) override; + int32_t preceding(int32_t offset) override; + UBool isBoundary(int32_t offset) override; + int32_t next(int32_t n) override; + WholeStringBreakIterator *createBufferClone(void *stackBuffer, int32_t &BufferSize, + UErrorCode &errorCode) override; + WholeStringBreakIterator &refreshInputText(UText *input, UErrorCode &errorCode) override; + +private: + int32_t length; +}; + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(WholeStringBreakIterator) + +WholeStringBreakIterator::~WholeStringBreakIterator() {} +bool WholeStringBreakIterator::operator==(const BreakIterator&) const { return false; } +WholeStringBreakIterator *WholeStringBreakIterator::clone() const { return nullptr; } + +CharacterIterator &WholeStringBreakIterator::getText() const { + UPRV_UNREACHABLE_EXIT; // really should not be called +} +UText *WholeStringBreakIterator::getUText(UText * /*fillIn*/, UErrorCode &errorCode) const { + if (U_SUCCESS(errorCode)) { + errorCode = U_UNSUPPORTED_ERROR; + } + return nullptr; +} + +void WholeStringBreakIterator::setText(const UnicodeString &text) { + length = text.length(); +} +void WholeStringBreakIterator::setText(UText *text, UErrorCode &errorCode) { + if (U_SUCCESS(errorCode)) { + int64_t length64 = utext_nativeLength(text); + if (length64 <= INT32_MAX) { + length = (int32_t)length64; + } else { + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + } + } +} +void WholeStringBreakIterator::adoptText(CharacterIterator*) { + UPRV_UNREACHABLE_EXIT; // should not be called +} + +int32_t WholeStringBreakIterator::first() { return 0; } +int32_t WholeStringBreakIterator::last() { return length; } +int32_t WholeStringBreakIterator::previous() { return 0; } +int32_t WholeStringBreakIterator::next() { return length; } +int32_t WholeStringBreakIterator::current() const { return 0; } +int32_t WholeStringBreakIterator::following(int32_t /*offset*/) { return length; } +int32_t WholeStringBreakIterator::preceding(int32_t /*offset*/) { return 0; } +UBool WholeStringBreakIterator::isBoundary(int32_t /*offset*/) { return false; } +int32_t WholeStringBreakIterator::next(int32_t /*n*/) { return length; } + +WholeStringBreakIterator *WholeStringBreakIterator::createBufferClone( + void * /*stackBuffer*/, int32_t & /*BufferSize*/, UErrorCode &errorCode) { + if (U_SUCCESS(errorCode)) { + errorCode = U_UNSUPPORTED_ERROR; + } + return nullptr; +} +WholeStringBreakIterator &WholeStringBreakIterator::refreshInputText( + UText * /*input*/, UErrorCode &errorCode) { + if (U_SUCCESS(errorCode)) { + errorCode = U_UNSUPPORTED_ERROR; + } + return *this; +} + +U_CFUNC +BreakIterator *ustrcase_getTitleBreakIterator( + const Locale *locale, const char *locID, uint32_t options, BreakIterator *iter, + LocalPointer &ownedIter, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return nullptr; } + options &= U_TITLECASE_ITERATOR_MASK; + if (options != 0 && iter != nullptr) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + if (iter == nullptr) { + switch (options) { + case 0: + iter = BreakIterator::createWordInstance( + locale != nullptr ? *locale : Locale(locID), errorCode); + break; + case U_TITLECASE_WHOLE_STRING: + iter = new WholeStringBreakIterator(); + if (iter == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + } + break; + case U_TITLECASE_SENTENCES: + iter = BreakIterator::createSentenceInstance( + locale != nullptr ? *locale : Locale(locID), errorCode); + break; + default: + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + ownedIter.adoptInstead(iter); + } + return iter; +} + +int32_t CaseMap::toTitle( + const char *locale, uint32_t options, BreakIterator *iter, + const char16_t *src, int32_t srcLength, + char16_t *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode) { + LocalPointer ownedIter; + iter = ustrcase_getTitleBreakIterator(nullptr, locale, options, iter, ownedIter, errorCode); + if(iter==nullptr) { + return 0; + } + UnicodeString s(srcLength<0, src, srcLength); + iter->setText(s); + return ustrcase_map( + ustrcase_getCaseLocale(locale), options, iter, + dest, destCapacity, + src, srcLength, + ustrcase_internalToTitle, edits, errorCode); +} + +U_NAMESPACE_END + +U_NAMESPACE_USE + +U_CAPI int32_t U_EXPORT2 +u_strToTitle(char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + UBreakIterator *titleIter, + const char *locale, + UErrorCode *pErrorCode) { + LocalPointer ownedIter; + BreakIterator *iter = ustrcase_getTitleBreakIterator( + nullptr, locale, 0, reinterpret_cast(titleIter), + ownedIter, *pErrorCode); + if (iter == nullptr) { + return 0; + } + UnicodeString s(srcLength<0, src, srcLength); + iter->setText(s); + return ustrcase_mapWithOverlap( + ustrcase_getCaseLocale(locale), 0, iter, + dest, destCapacity, + src, srcLength, + ustrcase_internalToTitle, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +ucasemap_toTitle(UCaseMap *csm, + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + UErrorCode *pErrorCode) { + if (U_FAILURE(*pErrorCode)) { + return 0; + } + if (csm->iter == nullptr) { + LocalPointer ownedIter; + BreakIterator *iter = ustrcase_getTitleBreakIterator( + nullptr, csm->locale, csm->options, nullptr, ownedIter, *pErrorCode); + if (iter == nullptr) { + return 0; + } + csm->iter = ownedIter.orphan(); + } + UnicodeString s(srcLength<0, src, srcLength); + csm->iter->setText(s); + return ustrcase_map( + csm->caseLocale, csm->options, csm->iter, + dest, destCapacity, + src, srcLength, + ustrcase_internalToTitle, nullptr, *pErrorCode); +} + +#endif // !UCONFIG_NO_BREAK_ITERATION diff --git a/deps/icu-small/source/common/ustr_wcs.cpp b/deps/icu-small/source/common/ustr_wcs.cpp index 1a6ea2375d0bb6..3e349bf0fb8536 100644 --- a/deps/icu-small/source/common/ustr_wcs.cpp +++ b/deps/icu-small/source/common/ustr_wcs.cpp @@ -1,535 +1,535 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2001-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: ustr_wcs.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004sep07 -* created by: Markus W. Scherer -* -* u_strToWCS() and u_strFromWCS() functions -* moved here from ustrtrns.c for better modularization. -*/ - -#include "unicode/utypes.h" -#include "unicode/ustring.h" -#include "cstring.h" -#include "cwchar.h" -#include "cmemory.h" -#include "ustr_imp.h" -#include "ustr_cnv.h" - -#if defined(U_WCHAR_IS_UTF16) || defined(U_WCHAR_IS_UTF32) || !UCONFIG_NO_CONVERSION - -#define _STACK_BUFFER_CAPACITY 1000 -#define _BUFFER_CAPACITY_MULTIPLIER 2 - -#if !defined(U_WCHAR_IS_UTF16) && !defined(U_WCHAR_IS_UTF32) -// TODO: We should use CharString for char buffers and UnicodeString for UChar buffers. -// Then we could change this to work only with wchar_t buffers. -static inline UBool -u_growAnyBufferFromStatic(void *context, - void **pBuffer, int32_t *pCapacity, int32_t reqCapacity, - int32_t length, int32_t size) { - // Use char* not void* to avoid the compiler's strict-aliasing assumptions - // and related warnings. - char *newBuffer=(char *)uprv_malloc(reqCapacity*size); - if(newBuffer!=NULL) { - if(length>0) { - uprv_memcpy(newBuffer, *pBuffer, (size_t)length*size); - } - *pCapacity=reqCapacity; - } else { - *pCapacity=0; - } - - /* release the old pBuffer if it was not statically allocated */ - if(*pBuffer!=(char *)context) { - uprv_free(*pBuffer); - } - - *pBuffer=newBuffer; - return (UBool)(newBuffer!=NULL); -} - -/* helper function */ -static wchar_t* -_strToWCS(wchar_t *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar *src, - int32_t srcLength, - UErrorCode *pErrorCode){ - - char stackBuffer [_STACK_BUFFER_CAPACITY]; - char* tempBuf = stackBuffer; - int32_t tempBufCapacity = _STACK_BUFFER_CAPACITY; - char* tempBufLimit = stackBuffer + tempBufCapacity; - UConverter* conv = NULL; - char* saveBuf = tempBuf; - wchar_t* intTarget=NULL; - int32_t intTargetCapacity=0; - int count=0,retVal=0; - - const UChar *pSrcLimit =NULL; - const UChar *pSrc = src; - - conv = u_getDefaultConverter(pErrorCode); - - if(U_FAILURE(*pErrorCode)){ - return NULL; - } - - if(srcLength == -1){ - srcLength = u_strlen(pSrc); - } - - pSrcLimit = pSrc + srcLength; - - for(;;) { - /* reset the error state */ - *pErrorCode = U_ZERO_ERROR; - - /* convert to chars using default converter */ - ucnv_fromUnicode(conv,&tempBuf,tempBufLimit,&pSrc,pSrcLimit,NULL,(UBool)(pSrc==pSrcLimit),pErrorCode); - count =(tempBuf - saveBuf); - - /* This should rarely occur */ - if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR){ - tempBuf = saveBuf; - - /* we don't have enough room on the stack grow the buffer */ - int32_t newCapacity = 2 * srcLength; - if(newCapacity <= tempBufCapacity) { - newCapacity = _BUFFER_CAPACITY_MULTIPLIER * tempBufCapacity; - } - if(!u_growAnyBufferFromStatic(stackBuffer,(void**) &tempBuf, &tempBufCapacity, - newCapacity, count, 1)) { - goto cleanup; - } - - saveBuf = tempBuf; - tempBufLimit = tempBuf + tempBufCapacity; - tempBuf = tempBuf + count; - - } else { - break; - } - } - - if(U_FAILURE(*pErrorCode)){ - goto cleanup; - } - - /* done with conversion null terminate the char buffer */ - if(count>=tempBufCapacity){ - tempBuf = saveBuf; - /* we don't have enough room on the stack grow the buffer */ - if(!u_growAnyBufferFromStatic(stackBuffer,(void**) &tempBuf, &tempBufCapacity, - count+1, count, 1)) { - goto cleanup; - } - saveBuf = tempBuf; - } - - saveBuf[count]=0; - - - /* allocate more space than required - * here we assume that every char requires - * no more than 2 wchar_ts - */ - intTargetCapacity = (count * _BUFFER_CAPACITY_MULTIPLIER + 1) /*for null termination */; - intTarget = (wchar_t*)uprv_malloc( intTargetCapacity * sizeof(wchar_t) ); - - if(intTarget){ - - int32_t nulLen = 0; - int32_t remaining = intTargetCapacity; - wchar_t* pIntTarget=intTarget; - tempBuf = saveBuf; - - /* now convert the mbs to wcs */ - for(;;){ - - /* we can call the system API since we are sure that - * there is atleast 1 null in the input - */ - retVal = uprv_mbstowcs(pIntTarget,(tempBuf+nulLen),remaining); - - if(retVal==-1){ - *pErrorCode = U_INVALID_CHAR_FOUND; - break; - }else if(retVal== remaining){/* should never occur */ - int numWritten = (pIntTarget-intTarget); - u_growAnyBufferFromStatic(NULL,(void**) &intTarget, - &intTargetCapacity, - intTargetCapacity * _BUFFER_CAPACITY_MULTIPLIER, - numWritten, - sizeof(wchar_t)); - pIntTarget = intTarget; - remaining=intTargetCapacity; - - if(nulLen!=count){ /*there are embedded nulls*/ - pIntTarget+=numWritten; - remaining-=numWritten; - } - - }else{ - int32_t nulVal; - /*scan for nulls */ - /* we donot check for limit since tempBuf is null terminated */ - while(tempBuf[nulLen++] != 0){ - } - nulVal = (nulLen < srcLength) ? 1 : 0; - pIntTarget = pIntTarget + retVal+nulVal; - remaining -=(retVal+nulVal); - - /* check if we have reached the source limit*/ - if(nulLen>=(count)){ - break; - } - } - } - count = (int32_t)(pIntTarget-intTarget); - - if(0 < count && count <= destCapacity){ - uprv_memcpy(dest, intTarget, (size_t)count*sizeof(wchar_t)); - } - - if(pDestLength){ - *pDestLength = count; - } - - /* free the allocated memory */ - uprv_free(intTarget); - - }else{ - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - } -cleanup: - /* are we still using stack buffer */ - if(stackBuffer != saveBuf){ - uprv_free(saveBuf); - } - u_terminateWChars(dest,destCapacity,count,pErrorCode); - - u_releaseDefaultConverter(conv); - - return dest; -} -#endif - -U_CAPI wchar_t* U_EXPORT2 -u_strToWCS(wchar_t *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar *src, - int32_t srcLength, - UErrorCode *pErrorCode){ - - /* args check */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)){ - return NULL; - } - - if( (src==NULL && srcLength!=0) || srcLength < -1 || - (destCapacity<0) || (dest == NULL && destCapacity > 0) - ) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - -#ifdef U_WCHAR_IS_UTF16 - /* wchar_t is UTF-16 just do a memcpy */ - if(srcLength == -1){ - srcLength = u_strlen(src); - } - if(0 < srcLength && srcLength <= destCapacity){ - u_memcpy((UChar *)dest, src, srcLength); - } - if(pDestLength){ - *pDestLength = srcLength; - } - - u_terminateUChars((UChar *)dest,destCapacity,srcLength,pErrorCode); - - return dest; - -#elif defined U_WCHAR_IS_UTF32 - - return (wchar_t*)u_strToUTF32((UChar32*)dest, destCapacity, pDestLength, - src, srcLength, pErrorCode); - -#else - - return _strToWCS(dest,destCapacity,pDestLength,src,srcLength, pErrorCode); - -#endif - -} - -#if !defined(U_WCHAR_IS_UTF16) && !defined(U_WCHAR_IS_UTF32) -/* helper function */ -static UChar* -_strFromWCS( UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const wchar_t *src, - int32_t srcLength, - UErrorCode *pErrorCode) -{ - int32_t retVal =0, count =0 ; - UConverter* conv = NULL; - UChar* pTarget = NULL; - UChar* pTargetLimit = NULL; - UChar* target = NULL; - - UChar uStack [_STACK_BUFFER_CAPACITY]; - - wchar_t wStack[_STACK_BUFFER_CAPACITY]; - wchar_t* pWStack = wStack; - - - char cStack[_STACK_BUFFER_CAPACITY]; - int32_t cStackCap = _STACK_BUFFER_CAPACITY; - char* pCSrc=cStack; - char* pCSave=pCSrc; - char* pCSrcLimit=NULL; - - const wchar_t* pSrc = src; - const wchar_t* pSrcLimit = NULL; - - if(srcLength ==-1){ - /* if the wchar_t source is null terminated we can safely - * assume that there are no embedded nulls, this is a fast - * path for null terminated strings. - */ - for(;;){ - /* convert wchars to chars */ - retVal = uprv_wcstombs(pCSrc,src, cStackCap); - - if(retVal == -1){ - *pErrorCode = U_ILLEGAL_CHAR_FOUND; - goto cleanup; - }else if(retVal >= (cStackCap-1)){ - /* Should rarely occur */ - u_growAnyBufferFromStatic(cStack,(void**)&pCSrc,&cStackCap, - cStackCap * _BUFFER_CAPACITY_MULTIPLIER, 0, sizeof(char)); - pCSave = pCSrc; - }else{ - /* converted every thing */ - pCSrc = pCSrc+retVal; - break; - } - } - - }else{ - /* here the source is not null terminated - * so it may have nulls embedded and we need to - * do some extra processing - */ - int32_t remaining =cStackCap; - - pSrcLimit = src + srcLength; - - for(;;){ - int32_t nulLen = 0; - - /* find nulls in the string */ - while(nulLen= _STACK_BUFFER_CAPACITY){ - /* Should rarely occur */ - /* allocate new buffer buffer */ - pWStack =(wchar_t*) uprv_malloc(sizeof(wchar_t) * (nulLen + 1)); - if(pWStack==NULL){ - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - goto cleanup; - } - } - if(nulLen>0){ - /* copy the contents to tempStack */ - uprv_memcpy(pWStack, pSrc, (size_t)nulLen*sizeof(wchar_t)); - } - - /* null terminate the tempBuffer */ - pWStack[nulLen] =0 ; - - if(remaining < (nulLen * MB_CUR_MAX)){ - /* Should rarely occur */ - int32_t len = (pCSrc-pCSave); - pCSrc = pCSave; - /* we do not have enough room so grow the buffer*/ - u_growAnyBufferFromStatic(cStack,(void**)&pCSrc,&cStackCap, - cStackCap+(nulLen*MB_CUR_MAX),len,sizeof(char)); - - pCSave = pCSrc; - pCSrc = pCSave+len; - remaining = cStackCap-(pCSrc - pCSave); - } - /* convert to chars */ - retVal = uprv_wcstombs(pCSrc,pWStack,remaining); - - pCSrc += retVal; - pSrc += nulLen; - srcLength-=nulLen; /* decrement the srcLength */ - break; - } - } - } - - /* OK..now we have converted from wchar_ts to chars now - * convert chars to UChars - */ - pCSrcLimit = pCSrc; - pCSrc = pCSave; - pTarget = target= dest; - pTargetLimit = dest + destCapacity; - - conv= u_getDefaultConverter(pErrorCode); - - if(U_FAILURE(*pErrorCode)|| conv==NULL){ - goto cleanup; - } - - for(;;) { - - *pErrorCode = U_ZERO_ERROR; - - /* convert to stack buffer*/ - ucnv_toUnicode(conv,&pTarget,pTargetLimit,(const char**)&pCSrc,pCSrcLimit,NULL,(UBool)(pCSrc==pCSrcLimit),pErrorCode); - - /* increment count to number written to stack */ - count+= pTarget - target; - - if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR){ - target = uStack; - pTarget = uStack; - pTargetLimit = uStack + _STACK_BUFFER_CAPACITY; - } else { - break; - } - - } - - if(pDestLength){ - *pDestLength =count; - } - - u_terminateUChars(dest,destCapacity,count,pErrorCode); - -cleanup: - - if(cStack != pCSave){ - uprv_free(pCSave); - } - - if(wStack != pWStack){ - uprv_free(pWStack); - } - - u_releaseDefaultConverter(conv); - - return dest; -} -#endif - -U_CAPI UChar* U_EXPORT2 -u_strFromWCS(UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const wchar_t *src, - int32_t srcLength, - UErrorCode *pErrorCode) -{ - - /* args check */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)){ - return NULL; - } - - if( (src==NULL && srcLength!=0) || srcLength < -1 || - (destCapacity<0) || (dest == NULL && destCapacity > 0) - ) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - -#ifdef U_WCHAR_IS_UTF16 - /* wchar_t is UTF-16 just do a memcpy */ - if(srcLength == -1){ - srcLength = u_strlen((const UChar *)src); - } - if(0 < srcLength && srcLength <= destCapacity){ - u_memcpy(dest, (const UChar *)src, srcLength); - } - if(pDestLength){ - *pDestLength = srcLength; - } - - u_terminateUChars(dest,destCapacity,srcLength,pErrorCode); - - return dest; - -#elif defined U_WCHAR_IS_UTF32 - - return u_strFromUTF32(dest, destCapacity, pDestLength, - (UChar32*)src, srcLength, pErrorCode); - -#else - - return _strFromWCS(dest,destCapacity,pDestLength,src,srcLength,pErrorCode); - -#endif - -} - -#endif /* #if !defined(U_WCHAR_IS_UTF16) && !defined(U_WCHAR_IS_UTF32) && !UCONFIG_NO_CONVERSION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2001-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: ustr_wcs.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004sep07 +* created by: Markus W. Scherer +* +* u_strToWCS() and u_strFromWCS() functions +* moved here from ustrtrns.c for better modularization. +*/ + +#include "unicode/utypes.h" +#include "unicode/ustring.h" +#include "cstring.h" +#include "cwchar.h" +#include "cmemory.h" +#include "ustr_imp.h" +#include "ustr_cnv.h" + +#if defined(U_WCHAR_IS_UTF16) || defined(U_WCHAR_IS_UTF32) || !UCONFIG_NO_CONVERSION + +#define _STACK_BUFFER_CAPACITY 1000 +#define _BUFFER_CAPACITY_MULTIPLIER 2 + +#if !defined(U_WCHAR_IS_UTF16) && !defined(U_WCHAR_IS_UTF32) +// TODO: We should use CharString for char buffers and UnicodeString for char16_t buffers. +// Then we could change this to work only with wchar_t buffers. +static inline UBool +u_growAnyBufferFromStatic(void *context, + void **pBuffer, int32_t *pCapacity, int32_t reqCapacity, + int32_t length, int32_t size) { + // Use char* not void* to avoid the compiler's strict-aliasing assumptions + // and related warnings. + char *newBuffer=(char *)uprv_malloc(reqCapacity*size); + if(newBuffer!=nullptr) { + if(length>0) { + uprv_memcpy(newBuffer, *pBuffer, (size_t)length*size); + } + *pCapacity=reqCapacity; + } else { + *pCapacity=0; + } + + /* release the old pBuffer if it was not statically allocated */ + if(*pBuffer!=(char *)context) { + uprv_free(*pBuffer); + } + + *pBuffer=newBuffer; + return (UBool)(newBuffer!=nullptr); +} + +/* helper function */ +static wchar_t* +_strToWCS(wchar_t *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char16_t *src, + int32_t srcLength, + UErrorCode *pErrorCode){ + + char stackBuffer [_STACK_BUFFER_CAPACITY]; + char* tempBuf = stackBuffer; + int32_t tempBufCapacity = _STACK_BUFFER_CAPACITY; + char* tempBufLimit = stackBuffer + tempBufCapacity; + UConverter* conv = nullptr; + char* saveBuf = tempBuf; + wchar_t* intTarget=nullptr; + int32_t intTargetCapacity=0; + int count=0,retVal=0; + + const char16_t *pSrcLimit =nullptr; + const char16_t *pSrc = src; + + conv = u_getDefaultConverter(pErrorCode); + + if(U_FAILURE(*pErrorCode)){ + return nullptr; + } + + if(srcLength == -1){ + srcLength = u_strlen(pSrc); + } + + pSrcLimit = pSrc + srcLength; + + for(;;) { + /* reset the error state */ + *pErrorCode = U_ZERO_ERROR; + + /* convert to chars using default converter */ + ucnv_fromUnicode(conv,&tempBuf,tempBufLimit,&pSrc,pSrcLimit,nullptr,(UBool)(pSrc==pSrcLimit),pErrorCode); + count =(tempBuf - saveBuf); + + /* This should rarely occur */ + if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR){ + tempBuf = saveBuf; + + /* we don't have enough room on the stack grow the buffer */ + int32_t newCapacity = 2 * srcLength; + if(newCapacity <= tempBufCapacity) { + newCapacity = _BUFFER_CAPACITY_MULTIPLIER * tempBufCapacity; + } + if(!u_growAnyBufferFromStatic(stackBuffer,(void**) &tempBuf, &tempBufCapacity, + newCapacity, count, 1)) { + goto cleanup; + } + + saveBuf = tempBuf; + tempBufLimit = tempBuf + tempBufCapacity; + tempBuf = tempBuf + count; + + } else { + break; + } + } + + if(U_FAILURE(*pErrorCode)){ + goto cleanup; + } + + /* done with conversion null terminate the char buffer */ + if(count>=tempBufCapacity){ + tempBuf = saveBuf; + /* we don't have enough room on the stack grow the buffer */ + if(!u_growAnyBufferFromStatic(stackBuffer,(void**) &tempBuf, &tempBufCapacity, + count+1, count, 1)) { + goto cleanup; + } + saveBuf = tempBuf; + } + + saveBuf[count]=0; + + + /* allocate more space than required + * here we assume that every char requires + * no more than 2 wchar_ts + */ + intTargetCapacity = (count * _BUFFER_CAPACITY_MULTIPLIER + 1) /*for null termination */; + intTarget = (wchar_t*)uprv_malloc( intTargetCapacity * sizeof(wchar_t) ); + + if(intTarget){ + + int32_t nulLen = 0; + int32_t remaining = intTargetCapacity; + wchar_t* pIntTarget=intTarget; + tempBuf = saveBuf; + + /* now convert the mbs to wcs */ + for(;;){ + + /* we can call the system API since we are sure that + * there is atleast 1 null in the input + */ + retVal = uprv_mbstowcs(pIntTarget,(tempBuf+nulLen),remaining); + + if(retVal==-1){ + *pErrorCode = U_INVALID_CHAR_FOUND; + break; + }else if(retVal== remaining){/* should never occur */ + int numWritten = (pIntTarget-intTarget); + u_growAnyBufferFromStatic(nullptr,(void**) &intTarget, + &intTargetCapacity, + intTargetCapacity * _BUFFER_CAPACITY_MULTIPLIER, + numWritten, + sizeof(wchar_t)); + pIntTarget = intTarget; + remaining=intTargetCapacity; + + if(nulLen!=count){ /*there are embedded nulls*/ + pIntTarget+=numWritten; + remaining-=numWritten; + } + + }else{ + int32_t nulVal; + /*scan for nulls */ + /* we donot check for limit since tempBuf is null terminated */ + while(tempBuf[nulLen++] != 0){ + } + nulVal = (nulLen < srcLength) ? 1 : 0; + pIntTarget = pIntTarget + retVal+nulVal; + remaining -=(retVal+nulVal); + + /* check if we have reached the source limit*/ + if(nulLen>=(count)){ + break; + } + } + } + count = (int32_t)(pIntTarget-intTarget); + + if(0 < count && count <= destCapacity){ + uprv_memcpy(dest, intTarget, (size_t)count*sizeof(wchar_t)); + } + + if(pDestLength){ + *pDestLength = count; + } + + /* free the allocated memory */ + uprv_free(intTarget); + + }else{ + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + } +cleanup: + /* are we still using stack buffer */ + if(stackBuffer != saveBuf){ + uprv_free(saveBuf); + } + u_terminateWChars(dest,destCapacity,count,pErrorCode); + + u_releaseDefaultConverter(conv); + + return dest; +} +#endif + +U_CAPI wchar_t* U_EXPORT2 +u_strToWCS(wchar_t *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char16_t *src, + int32_t srcLength, + UErrorCode *pErrorCode){ + + /* args check */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)){ + return nullptr; + } + + if( (src==nullptr && srcLength!=0) || srcLength < -1 || + (destCapacity<0) || (dest == nullptr && destCapacity > 0) + ) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + +#ifdef U_WCHAR_IS_UTF16 + /* wchar_t is UTF-16 just do a memcpy */ + if(srcLength == -1){ + srcLength = u_strlen(src); + } + if(0 < srcLength && srcLength <= destCapacity){ + u_memcpy((char16_t *)dest, src, srcLength); + } + if(pDestLength){ + *pDestLength = srcLength; + } + + u_terminateUChars((char16_t *)dest,destCapacity,srcLength,pErrorCode); + + return dest; + +#elif defined U_WCHAR_IS_UTF32 + + return (wchar_t*)u_strToUTF32((UChar32*)dest, destCapacity, pDestLength, + src, srcLength, pErrorCode); + +#else + + return _strToWCS(dest,destCapacity,pDestLength,src,srcLength, pErrorCode); + +#endif + +} + +#if !defined(U_WCHAR_IS_UTF16) && !defined(U_WCHAR_IS_UTF32) +/* helper function */ +static char16_t* +_strFromWCS( char16_t *dest, + int32_t destCapacity, + int32_t *pDestLength, + const wchar_t *src, + int32_t srcLength, + UErrorCode *pErrorCode) +{ + int32_t retVal =0, count =0 ; + UConverter* conv = nullptr; + char16_t* pTarget = nullptr; + char16_t* pTargetLimit = nullptr; + char16_t* target = nullptr; + + char16_t uStack [_STACK_BUFFER_CAPACITY]; + + wchar_t wStack[_STACK_BUFFER_CAPACITY]; + wchar_t* pWStack = wStack; + + + char cStack[_STACK_BUFFER_CAPACITY]; + int32_t cStackCap = _STACK_BUFFER_CAPACITY; + char* pCSrc=cStack; + char* pCSave=pCSrc; + char* pCSrcLimit=nullptr; + + const wchar_t* pSrc = src; + const wchar_t* pSrcLimit = nullptr; + + if(srcLength ==-1){ + /* if the wchar_t source is null terminated we can safely + * assume that there are no embedded nulls, this is a fast + * path for null terminated strings. + */ + for(;;){ + /* convert wchars to chars */ + retVal = uprv_wcstombs(pCSrc,src, cStackCap); + + if(retVal == -1){ + *pErrorCode = U_ILLEGAL_CHAR_FOUND; + goto cleanup; + }else if(retVal >= (cStackCap-1)){ + /* Should rarely occur */ + u_growAnyBufferFromStatic(cStack,(void**)&pCSrc,&cStackCap, + cStackCap * _BUFFER_CAPACITY_MULTIPLIER, 0, sizeof(char)); + pCSave = pCSrc; + }else{ + /* converted every thing */ + pCSrc = pCSrc+retVal; + break; + } + } + + }else{ + /* here the source is not null terminated + * so it may have nulls embedded and we need to + * do some extra processing + */ + int32_t remaining =cStackCap; + + pSrcLimit = src + srcLength; + + for(;;){ + int32_t nulLen = 0; + + /* find nulls in the string */ + while(nulLen= _STACK_BUFFER_CAPACITY){ + /* Should rarely occur */ + /* allocate new buffer buffer */ + pWStack =(wchar_t*) uprv_malloc(sizeof(wchar_t) * (nulLen + 1)); + if(pWStack==nullptr){ + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + goto cleanup; + } + } + if(nulLen>0){ + /* copy the contents to tempStack */ + uprv_memcpy(pWStack, pSrc, (size_t)nulLen*sizeof(wchar_t)); + } + + /* null terminate the tempBuffer */ + pWStack[nulLen] =0 ; + + if(remaining < (nulLen * MB_CUR_MAX)){ + /* Should rarely occur */ + int32_t len = (pCSrc-pCSave); + pCSrc = pCSave; + /* we do not have enough room so grow the buffer*/ + u_growAnyBufferFromStatic(cStack,(void**)&pCSrc,&cStackCap, + cStackCap+(nulLen*MB_CUR_MAX),len,sizeof(char)); + + pCSave = pCSrc; + pCSrc = pCSave+len; + remaining = cStackCap-(pCSrc - pCSave); + } + /* convert to chars */ + retVal = uprv_wcstombs(pCSrc,pWStack,remaining); + + pCSrc += retVal; + pSrc += nulLen; + srcLength-=nulLen; /* decrement the srcLength */ + break; + } + } + } + + /* OK..now we have converted from wchar_ts to chars now + * convert chars to UChars + */ + pCSrcLimit = pCSrc; + pCSrc = pCSave; + pTarget = target= dest; + pTargetLimit = dest + destCapacity; + + conv= u_getDefaultConverter(pErrorCode); + + if(U_FAILURE(*pErrorCode)|| conv==nullptr){ + goto cleanup; + } + + for(;;) { + + *pErrorCode = U_ZERO_ERROR; + + /* convert to stack buffer*/ + ucnv_toUnicode(conv,&pTarget,pTargetLimit,(const char**)&pCSrc,pCSrcLimit,nullptr,(UBool)(pCSrc==pCSrcLimit),pErrorCode); + + /* increment count to number written to stack */ + count+= pTarget - target; + + if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR){ + target = uStack; + pTarget = uStack; + pTargetLimit = uStack + _STACK_BUFFER_CAPACITY; + } else { + break; + } + + } + + if(pDestLength){ + *pDestLength =count; + } + + u_terminateUChars(dest,destCapacity,count,pErrorCode); + +cleanup: + + if(cStack != pCSave){ + uprv_free(pCSave); + } + + if(wStack != pWStack){ + uprv_free(pWStack); + } + + u_releaseDefaultConverter(conv); + + return dest; +} +#endif + +U_CAPI char16_t* U_EXPORT2 +u_strFromWCS(char16_t *dest, + int32_t destCapacity, + int32_t *pDestLength, + const wchar_t *src, + int32_t srcLength, + UErrorCode *pErrorCode) +{ + + /* args check */ + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)){ + return nullptr; + } + + if( (src==nullptr && srcLength!=0) || srcLength < -1 || + (destCapacity<0) || (dest == nullptr && destCapacity > 0) + ) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + +#ifdef U_WCHAR_IS_UTF16 + /* wchar_t is UTF-16 just do a memcpy */ + if(srcLength == -1){ + srcLength = u_strlen((const char16_t *)src); + } + if(0 < srcLength && srcLength <= destCapacity){ + u_memcpy(dest, (const char16_t *)src, srcLength); + } + if(pDestLength){ + *pDestLength = srcLength; + } + + u_terminateUChars(dest,destCapacity,srcLength,pErrorCode); + + return dest; + +#elif defined U_WCHAR_IS_UTF32 + + return u_strFromUTF32(dest, destCapacity, pDestLength, + (UChar32*)src, srcLength, pErrorCode); + +#else + + return _strFromWCS(dest,destCapacity,pDestLength,src,srcLength,pErrorCode); + +#endif + +} + +#endif /* #if !defined(U_WCHAR_IS_UTF16) && !defined(U_WCHAR_IS_UTF32) && !UCONFIG_NO_CONVERSION */ diff --git a/deps/icu-small/source/common/ustrcase.cpp b/deps/icu-small/source/common/ustrcase.cpp index 8037c09b4f0a97..9c8ea2de09c460 100644 --- a/deps/icu-small/source/common/ustrcase.cpp +++ b/deps/icu-small/source/common/ustrcase.cpp @@ -1,1900 +1,1900 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2001-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: ustrcase.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2002feb20 -* created by: Markus W. Scherer -* -* Implementation file for string casing C API functions. -* Uses functions from uchar.c for basic functionality that requires access -* to the Unicode Character Database (uprops.dat). -*/ - -#include "unicode/utypes.h" -#include "unicode/brkiter.h" -#include "unicode/casemap.h" -#include "unicode/edits.h" -#include "unicode/stringoptions.h" -#include "unicode/ustring.h" -#include "unicode/ucasemap.h" -#include "unicode/ubrk.h" -#include "unicode/utf.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "ucase.h" -#include "ucasemap_imp.h" -#include "ustr_imp.h" -#include "uassert.h" - -/** - * Code point for COMBINING ACUTE ACCENT - * @internal - */ -#define ACUTE u'\u0301' - -U_NAMESPACE_BEGIN - -namespace { - -int32_t checkOverflowAndEditsError(int32_t destIndex, int32_t destCapacity, - Edits *edits, UErrorCode &errorCode) { - if (U_SUCCESS(errorCode)) { - if (destIndex > destCapacity) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - } else if (edits != NULL) { - edits->copyErrorTo(errorCode); - } - } - return destIndex; -} - -/* Appends a full case mapping result, see UCASE_MAX_STRING_LENGTH. */ -inline int32_t -appendResult(UChar *dest, int32_t destIndex, int32_t destCapacity, - int32_t result, const UChar *s, - int32_t cpLength, uint32_t options, icu::Edits *edits) { - UChar32 c; - int32_t length; - - /* decode the result */ - if(result<0) { - /* (not) original code point */ - if(edits!=NULL) { - edits->addUnchanged(cpLength); - } - if(options & U_OMIT_UNCHANGED_TEXT) { - return destIndex; - } - c=~result; - if(destIndexaddReplace(cpLength, 1); - } - return destIndex; - } else { - c=result; - length=U16_LENGTH(c); - } - if(edits!=NULL) { - edits->addReplace(cpLength, length); - } - } - if(length>(INT32_MAX-destIndex)) { - return -1; // integer overflow - } - - if(destIndex=0) { - /* code point */ - UBool isError=false; - U16_APPEND(dest, destIndex, destCapacity, c, isError); - if(isError) { - /* overflow, nothing written */ - destIndex+=length; - } - } else { - /* string */ - if((destIndex+length)<=destCapacity) { - while(length>0) { - dest[destIndex++]=*s++; - --length; - } - } else { - /* overflow */ - destIndex+=length; - } - } - } else { - /* preflight */ - destIndex+=length; - } - return destIndex; -} - -inline int32_t -appendUChar(UChar *dest, int32_t destIndex, int32_t destCapacity, UChar c) { - if(destIndexaddUnchanged(length); - } - if(options & U_OMIT_UNCHANGED_TEXT) { - return destIndex; - } - if(length>(INT32_MAX-destIndex)) { - return -1; // integer overflow - } - if((destIndex+length)<=destCapacity) { - u_memcpy(dest+destIndex, s, length); - } - return destIndex + length; -} - -inline int32_t -appendUnchanged(UChar *dest, int32_t destIndex, int32_t destCapacity, - const UChar *s, int32_t length, uint32_t options, icu::Edits *edits) { - if (length <= 0) { - return destIndex; - } - return appendNonEmptyUnchanged(dest, destIndex, destCapacity, s, length, options, edits); -} - -UChar32 U_CALLCONV -utf16_caseContextIterator(void *context, int8_t dir) { - UCaseContext *csc=(UCaseContext *)context; - UChar32 c; - - if(dir<0) { - /* reset for backward iteration */ - csc->index=csc->cpStart; - csc->dir=dir; - } else if(dir>0) { - /* reset for forward iteration */ - csc->index=csc->cpLimit; - csc->dir=dir; - } else { - /* continue current iteration direction */ - dir=csc->dir; - } - - if(dir<0) { - if(csc->startindex) { - U16_PREV((const UChar *)csc->p, csc->start, csc->index, c); - return c; - } - } else { - if(csc->indexlimit) { - U16_NEXT((const UChar *)csc->p, csc->index, csc->limit, c); - return c; - } - } - return U_SENTINEL; -} - -/** - * caseLocale >= 0: Lowercases [srcStart..srcLimit[ but takes context [0..srcLength[ into account. - * caseLocale < 0: Case-folds [srcStart..srcLimit[. - */ -int32_t toLower(int32_t caseLocale, uint32_t options, - UChar *dest, int32_t destCapacity, - const UChar *src, UCaseContext *csc, int32_t srcStart, int32_t srcLimit, - icu::Edits *edits, UErrorCode &errorCode) { - const int8_t *latinToLower; - if (caseLocale == UCASE_LOC_ROOT || - (caseLocale >= 0 ? - !(caseLocale == UCASE_LOC_TURKISH || caseLocale == UCASE_LOC_LITHUANIAN) : - (options & _FOLD_CASE_OPTIONS_MASK) == U_FOLD_CASE_DEFAULT)) { - latinToLower = LatinCase::TO_LOWER_NORMAL; - } else { - latinToLower = LatinCase::TO_LOWER_TR_LT; - } - const UTrie2 *trie = ucase_getTrie(); - int32_t destIndex = 0; - int32_t prev = srcStart; - int32_t srcIndex = srcStart; - for (;;) { - // fast path for simple cases - UChar lead = 0; - while (srcIndex < srcLimit) { - lead = src[srcIndex]; - int32_t delta; - if (lead < LatinCase::LONG_S) { - int8_t d = latinToLower[lead]; - if (d == LatinCase::EXC) { break; } - ++srcIndex; - if (d == 0) { continue; } - delta = d; - } else if (lead >= 0xd800) { - break; // surrogate or higher - } else { - uint16_t props = UTRIE2_GET16_FROM_U16_SINGLE_LEAD(trie, lead); - if (UCASE_HAS_EXCEPTION(props)) { break; } - ++srcIndex; - if (!UCASE_IS_UPPER_OR_TITLE(props) || (delta = UCASE_GET_DELTA(props)) == 0) { - continue; - } - } - lead += static_cast(delta); - destIndex = appendUnchanged(dest, destIndex, destCapacity, - src + prev, srcIndex - 1 - prev, options, edits); - if (destIndex >= 0) { - destIndex = appendUChar(dest, destIndex, destCapacity, lead); - if (edits != nullptr) { - edits->addReplace(1, 1); - } - } - if (destIndex < 0) { - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - prev = srcIndex; - } - if (srcIndex >= srcLimit) { - break; - } - // slow path - int32_t cpStart = srcIndex++; - UChar trail; - UChar32 c; - if (U16_IS_LEAD(lead) && srcIndex < srcLimit && U16_IS_TRAIL(trail = src[srcIndex])) { - c = U16_GET_SUPPLEMENTARY(lead, trail); - ++srcIndex; - } else { - c = lead; - } - const UChar *s; - if (caseLocale >= 0) { - csc->cpStart = cpStart; - csc->cpLimit = srcIndex; - c = ucase_toFullLower(c, utf16_caseContextIterator, csc, &s, caseLocale); - } else { - c = ucase_toFullFolding(c, &s, options); - } - if (c >= 0) { - destIndex = appendUnchanged(dest, destIndex, destCapacity, - src + prev, cpStart - prev, options, edits); - if (destIndex >= 0) { - destIndex = appendResult(dest, destIndex, destCapacity, c, s, - srcIndex - cpStart, options, edits); - } - if (destIndex < 0) { - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - prev = srcIndex; - } - } - destIndex = appendUnchanged(dest, destIndex, destCapacity, - src + prev, srcIndex - prev, options, edits); - if (destIndex < 0) { - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - return destIndex; -} - -int32_t toUpper(int32_t caseLocale, uint32_t options, - UChar *dest, int32_t destCapacity, - const UChar *src, UCaseContext *csc, int32_t srcLength, - icu::Edits *edits, UErrorCode &errorCode) { - const int8_t *latinToUpper; - if (caseLocale == UCASE_LOC_TURKISH) { - latinToUpper = LatinCase::TO_UPPER_TR; - } else { - latinToUpper = LatinCase::TO_UPPER_NORMAL; - } - const UTrie2 *trie = ucase_getTrie(); - int32_t destIndex = 0; - int32_t prev = 0; - int32_t srcIndex = 0; - for (;;) { - // fast path for simple cases - UChar lead = 0; - while (srcIndex < srcLength) { - lead = src[srcIndex]; - int32_t delta; - if (lead < LatinCase::LONG_S) { - int8_t d = latinToUpper[lead]; - if (d == LatinCase::EXC) { break; } - ++srcIndex; - if (d == 0) { continue; } - delta = d; - } else if (lead >= 0xd800) { - break; // surrogate or higher - } else { - uint16_t props = UTRIE2_GET16_FROM_U16_SINGLE_LEAD(trie, lead); - if (UCASE_HAS_EXCEPTION(props)) { break; } - ++srcIndex; - if (UCASE_GET_TYPE(props) != UCASE_LOWER || (delta = UCASE_GET_DELTA(props)) == 0) { - continue; - } - } - lead += static_cast(delta); - destIndex = appendUnchanged(dest, destIndex, destCapacity, - src + prev, srcIndex - 1 - prev, options, edits); - if (destIndex >= 0) { - destIndex = appendUChar(dest, destIndex, destCapacity, lead); - if (edits != nullptr) { - edits->addReplace(1, 1); - } - } - if (destIndex < 0) { - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - prev = srcIndex; - } - if (srcIndex >= srcLength) { - break; - } - // slow path - int32_t cpStart; - csc->cpStart = cpStart = srcIndex++; - UChar trail; - UChar32 c; - if (U16_IS_LEAD(lead) && srcIndex < srcLength && U16_IS_TRAIL(trail = src[srcIndex])) { - c = U16_GET_SUPPLEMENTARY(lead, trail); - ++srcIndex; - } else { - c = lead; - } - csc->cpLimit = srcIndex; - const UChar *s; - c = ucase_toFullUpper(c, utf16_caseContextIterator, csc, &s, caseLocale); - if (c >= 0) { - destIndex = appendUnchanged(dest, destIndex, destCapacity, - src + prev, cpStart - prev, options, edits); - if (destIndex >= 0) { - destIndex = appendResult(dest, destIndex, destCapacity, c, s, - srcIndex - cpStart, options, edits); - } - if (destIndex < 0) { - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - prev = srcIndex; - } - } - destIndex = appendUnchanged(dest, destIndex, destCapacity, - src + prev, srcIndex - prev, options, edits); - if (destIndex < 0) { - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - return destIndex; -} - -} // namespace - -U_NAMESPACE_END - -U_NAMESPACE_USE - -#if !UCONFIG_NO_BREAK_ITERATION - -namespace { - -/** - * Input: c is a letter I with or without acute accent. - * start is the index in src after c, and is less than segmentLimit. - * If a plain i/I is followed by a plain j/J, - * or an i/I with acute (precomposed or decomposed) is followed by a j/J with acute, - * then we output accordingly. - * - * @return the src index after the titlecased sequence, or the start index if no Dutch IJ - */ -int32_t maybeTitleDutchIJ(const UChar *src, UChar32 c, int32_t start, int32_t segmentLimit, - UChar *dest, int32_t &destIndex, int32_t destCapacity, uint32_t options, - icu::Edits *edits) { - U_ASSERT(start < segmentLimit); - - int32_t index = start; - bool withAcute = false; - - // If the conditions are met, then the following variables tell us what to output. - int32_t unchanged1 = 0; // code units before the j, or the whole sequence (0..3) - bool doTitleJ = false; // true if the j needs to be titlecased - int32_t unchanged2 = 0; // after the j (0 or 1) - - // next character after the first letter - UChar c2 = src[index++]; - - // Is the first letter an i/I with accent? - if (c == u'I') { - if (c2 == ACUTE) { - withAcute = true; - unchanged1 = 1; - if (index == segmentLimit) { return start; } - c2 = src[index++]; - } - } else { // Í - withAcute = true; - } - - // Is the next character a j/J? - if (c2 == u'j') { - doTitleJ = true; - } else if (c2 == u'J') { - ++unchanged1; - } else { - return start; - } - - // A plain i/I must be followed by a plain j/J. - // An i/I with acute must be followed by a j/J with acute. - if (withAcute) { - if (index == segmentLimit || src[index++] != ACUTE) { return start; } - if (doTitleJ) { - unchanged2 = 1; - } else { - ++unchanged1; - } - } - - // There must not be another combining mark. - if (index < segmentLimit) { - int32_t cp; - int32_t i = index; - U16_NEXT(src, i, segmentLimit, cp); - uint32_t typeMask = U_GET_GC_MASK(cp); - if ((typeMask & U_GC_M_MASK) != 0) { - return start; - } - } - - // Output the rest of the Dutch IJ. - destIndex = appendUnchanged(dest, destIndex, destCapacity, src + start, unchanged1, options, edits); - start += unchanged1; - if (doTitleJ) { - destIndex = appendUChar(dest, destIndex, destCapacity, u'J'); - if (edits != nullptr) { - edits->addReplace(1, 1); - } - ++start; - } - destIndex = appendUnchanged(dest, destIndex, destCapacity, src + start, unchanged2, options, edits); - - U_ASSERT(start + unchanged2 == index); - return index; -} - -} // namespace - -U_CFUNC int32_t U_CALLCONV -ustrcase_internalToTitle(int32_t caseLocale, uint32_t options, BreakIterator *iter, - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - icu::Edits *edits, - UErrorCode &errorCode) { - if (!ustrcase_checkTitleAdjustmentOptions(options, errorCode)) { - return 0; - } - - /* set up local variables */ - UCaseContext csc=UCASECONTEXT_INITIALIZER; - csc.p=(void *)src; - csc.limit=srcLength; - int32_t destIndex=0; - int32_t prev=0; - bool isFirstIndex=true; - - /* titlecasing loop */ - while(prevfirst(); - } else { - index=iter->next(); - } - if(index==UBRK_DONE || index>srcLength) { - index=srcLength; - } - - /* - * Segment [prev..index[ into 3 parts: - * a) skipped characters (copy as-is) [prev..titleStart[ - * b) first letter (titlecase) [titleStart..titleLimit[ - * c) subsequent characters (lowercase) [titleLimit..index[ - */ - if(prev 0) { - uint32_t upper = data & UPPER_MASK; - // Add a dialytika to this iota or ypsilon vowel - // if we removed a tonos from the previous vowel, - // and that previous vowel did not also have (or gain) a dialytika. - // Adding one only to the final vowel in a longer sequence - // (which does not occur in normal writing) would require lookahead. - // Set the same flag as for preserving an existing dialytika. - if ((data & HAS_VOWEL) != 0 && (state & AFTER_VOWEL_WITH_ACCENT) != 0 && - (upper == 0x399 || upper == 0x3A5)) { - data |= HAS_DIALYTIKA; - } - int32_t numYpogegrammeni = 0; // Map each one to a trailing, spacing, capital iota. - if ((data & HAS_YPOGEGRAMMENI) != 0) { - numYpogegrammeni = 1; - } - // Skip combining diacritics after this Greek letter. - while (nextIndex < srcLength) { - uint32_t diacriticData = getDiacriticData(src[nextIndex]); - if (diacriticData != 0) { - data |= diacriticData; - if ((diacriticData & HAS_YPOGEGRAMMENI) != 0) { - ++numYpogegrammeni; - } - ++nextIndex; - } else { - break; // not a Greek diacritic - } - } - if ((data & HAS_VOWEL_AND_ACCENT_AND_DIALYTIKA) == HAS_VOWEL_AND_ACCENT) { - nextState |= AFTER_VOWEL_WITH_ACCENT; - } - // Map according to Greek rules. - UBool addTonos = false; - if (upper == 0x397 && - (data & HAS_ACCENT) != 0 && - numYpogegrammeni == 0 && - (state & AFTER_CASED) == 0 && - !isFollowedByCasedLetter(src, nextIndex, srcLength)) { - // Keep disjunctive "or" with (only) a tonos. - // We use the same "word boundary" conditions as for the Final_Sigma test. - if (i == nextIndex) { - upper = 0x389; // Preserve the precomposed form. - } else { - addTonos = true; - } - } else if ((data & HAS_DIALYTIKA) != 0) { - // Preserve a vowel with dialytika in precomposed form if it exists. - if (upper == 0x399) { - upper = 0x3AA; - data &= ~HAS_EITHER_DIALYTIKA; - } else if (upper == 0x3A5) { - upper = 0x3AB; - data &= ~HAS_EITHER_DIALYTIKA; - } - } - - UBool change; - if (edits == nullptr && (options & U_OMIT_UNCHANGED_TEXT) == 0) { - change = true; // common, simple usage - } else { - // Find out first whether we are changing the text. - change = src[i] != upper || numYpogegrammeni > 0; - int32_t i2 = i + 1; - if ((data & HAS_EITHER_DIALYTIKA) != 0) { - change |= i2 >= nextIndex || src[i2] != 0x308; - ++i2; - } - if (addTonos) { - change |= i2 >= nextIndex || src[i2] != 0x301; - ++i2; - } - int32_t oldLength = nextIndex - i; - int32_t newLength = (i2 - i) + numYpogegrammeni; - change |= oldLength != newLength; - if (change) { - if (edits != NULL) { - edits->addReplace(oldLength, newLength); - } - } else { - if (edits != NULL) { - edits->addUnchanged(oldLength); - } - // Write unchanged text? - change = (options & U_OMIT_UNCHANGED_TEXT) == 0; - } - } - - if (change) { - destIndex=appendUChar(dest, destIndex, destCapacity, (UChar)upper); - if (destIndex >= 0 && (data & HAS_EITHER_DIALYTIKA) != 0) { - destIndex=appendUChar(dest, destIndex, destCapacity, 0x308); // restore or add a dialytika - } - if (destIndex >= 0 && addTonos) { - destIndex=appendUChar(dest, destIndex, destCapacity, 0x301); - } - while (destIndex >= 0 && numYpogegrammeni > 0) { - destIndex=appendUChar(dest, destIndex, destCapacity, 0x399); - --numYpogegrammeni; - } - if(destIndex<0) { - errorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - } - } else { - const UChar *s; - c=ucase_toFullUpper(c, NULL, NULL, &s, UCASE_LOC_GREEK); - destIndex = appendResult(dest, destIndex, destCapacity, c, s, - nextIndex - i, options, edits); - if (destIndex < 0) { - errorCode = U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - } - i = nextIndex; - state = nextState; - } - - return destIndex; -} - -} // namespace GreekUpper -U_NAMESPACE_END - -/* functions available in the common library (for unistr_case.cpp) */ - -U_CFUNC int32_t U_CALLCONV -ustrcase_internalToLower(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - icu::Edits *edits, - UErrorCode &errorCode) { - UCaseContext csc=UCASECONTEXT_INITIALIZER; - csc.p=(void *)src; - csc.limit=srcLength; - int32_t destIndex = toLower( - caseLocale, options, - dest, destCapacity, - src, &csc, 0, srcLength, - edits, errorCode); - return checkOverflowAndEditsError(destIndex, destCapacity, edits, errorCode); -} - -U_CFUNC int32_t U_CALLCONV -ustrcase_internalToUpper(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - icu::Edits *edits, - UErrorCode &errorCode) { - int32_t destIndex; - if (caseLocale == UCASE_LOC_GREEK) { - destIndex = GreekUpper::toUpper(options, dest, destCapacity, - src, srcLength, edits, errorCode); - } else { - UCaseContext csc=UCASECONTEXT_INITIALIZER; - csc.p=(void *)src; - csc.limit=srcLength; - destIndex = toUpper( - caseLocale, options, - dest, destCapacity, - src, &csc, srcLength, - edits, errorCode); - } - return checkOverflowAndEditsError(destIndex, destCapacity, edits, errorCode); -} - -U_CFUNC int32_t U_CALLCONV -ustrcase_internalFold(int32_t /* caseLocale */, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - icu::Edits *edits, - UErrorCode &errorCode) { - int32_t destIndex = toLower( - -1, options, - dest, destCapacity, - src, nullptr, 0, srcLength, - edits, errorCode); - return checkOverflowAndEditsError(destIndex, destCapacity, edits, errorCode); -} - -U_CFUNC int32_t -ustrcase_map(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - UStringCaseMapper *stringCaseMapper, - icu::Edits *edits, - UErrorCode &errorCode) { - int32_t destLength; - - /* check argument values */ - if(U_FAILURE(errorCode)) { - return 0; - } - if( destCapacity<0 || - (dest==NULL && destCapacity>0) || - src==NULL || - srcLength<-1 - ) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* get the string length */ - if(srcLength==-1) { - srcLength=u_strlen(src); - } - - /* check for overlapping source and destination */ - if( dest!=NULL && - ((src>=dest && src<(dest+destCapacity)) || - (dest>=src && dest<(src+srcLength))) - ) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - if (edits != nullptr && (options & U_EDITS_NO_RESET) == 0) { - edits->reset(); - } - destLength=stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR - dest, destCapacity, src, srcLength, edits, errorCode); - return u_terminateUChars(dest, destCapacity, destLength, &errorCode); -} - -U_CFUNC int32_t -ustrcase_mapWithOverlap(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM - UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - UStringCaseMapper *stringCaseMapper, - UErrorCode &errorCode) { - UChar buffer[300]; - UChar *temp; - - int32_t destLength; - - /* check argument values */ - if(U_FAILURE(errorCode)) { - return 0; - } - if( destCapacity<0 || - (dest==NULL && destCapacity>0) || - src==NULL || - srcLength<-1 - ) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* get the string length */ - if(srcLength==-1) { - srcLength=u_strlen(src); - } - - /* check for overlapping source and destination */ - if( dest!=NULL && - ((src>=dest && src<(dest+destCapacity)) || - (dest>=src && dest<(src+srcLength))) - ) { - /* overlap: provide a temporary destination buffer and later copy the result */ - if(destCapacity<=UPRV_LENGTHOF(buffer)) { - /* the stack buffer is large enough */ - temp=buffer; - } else { - /* allocate a buffer */ - temp=(UChar *)uprv_malloc(destCapacity*U_SIZEOF_UCHAR); - if(temp==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return 0; - } - } - } else { - temp=dest; - } - - destLength=stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR - temp, destCapacity, src, srcLength, NULL, errorCode); - if(temp!=dest) { - /* copy the result string to the destination buffer */ - if (U_SUCCESS(errorCode) && 0 < destLength && destLength <= destCapacity) { - u_memmove(dest, temp, destLength); - } - if(temp!=buffer) { - uprv_free(temp); - } - } - - return u_terminateUChars(dest, destCapacity, destLength, &errorCode); -} - -/* public API functions */ - -U_CAPI int32_t U_EXPORT2 -u_strFoldCase(UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - uint32_t options, - UErrorCode *pErrorCode) { - return ustrcase_mapWithOverlap( - UCASE_LOC_ROOT, options, UCASEMAP_BREAK_ITERATOR_NULL - dest, destCapacity, - src, srcLength, - ustrcase_internalFold, *pErrorCode); -} - -U_NAMESPACE_BEGIN - -int32_t CaseMap::fold( - uint32_t options, - const UChar *src, int32_t srcLength, - UChar *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode) { - return ustrcase_map( - UCASE_LOC_ROOT, options, UCASEMAP_BREAK_ITERATOR_NULL - dest, destCapacity, - src, srcLength, - ustrcase_internalFold, edits, errorCode); -} - -U_NAMESPACE_END - -/* case-insensitive string comparisons -------------------------------------- */ - -/* - * This function is a copy of unorm_cmpEquivFold() minus the parts for - * canonical equivalence. - * Keep the functions in sync, and see there for how this works. - * The duplication is for modularization: - * It makes caseless (but not canonical caseless) matches independent of - * the normalization code. - */ - -/* stack element for previous-level source/decomposition pointers */ -struct CmpEquivLevel { - const UChar *start, *s, *limit; -}; -typedef struct CmpEquivLevel CmpEquivLevel; - -/** - * Internal implementation code comparing string with case fold. - * This function is called from u_strcmpFold() and u_caseInsensitivePrefixMatch(). - * - * @param s1 input string 1 - * @param length1 length of string 1, or -1 (NULL terminated) - * @param s2 input string 2 - * @param length2 length of string 2, or -1 (NULL terminated) - * @param options compare options - * @param matchLen1 (output) length of partial prefix match in s1 - * @param matchLen2 (output) length of partial prefix match in s2 - * @param pErrorCode receives error status - * @return The result of comparison - */ -static int32_t _cmpFold( - const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - uint32_t options, - int32_t *matchLen1, int32_t *matchLen2, - UErrorCode *pErrorCode) { - int32_t cmpRes = 0; - - /* current-level start/limit - s1/s2 as current */ - const UChar *start1, *start2, *limit1, *limit2; - - /* points to the original start address */ - const UChar *org1, *org2; - - /* points to the end of match + 1 */ - const UChar *m1, *m2; - - /* case folding variables */ - const UChar *p; - int32_t length; - - /* stacks of previous-level start/current/limit */ - CmpEquivLevel stack1[2], stack2[2]; - - /* case folding buffers, only use current-level start/limit */ - UChar fold1[UCASE_MAX_STRING_LENGTH+1], fold2[UCASE_MAX_STRING_LENGTH+1]; - - /* track which is the current level per string */ - int32_t level1, level2; - - /* current code units, and code points for lookups */ - UChar32 c1, c2, cp1, cp2; - - /* no argument error checking because this itself is not an API */ - - /* - * assume that at least the option U_COMPARE_IGNORE_CASE is set - * otherwise this function would have to behave exactly as uprv_strCompare() - */ - if(U_FAILURE(*pErrorCode)) { - return 0; - } - - /* initialize */ - if(matchLen1) { - U_ASSERT(matchLen2 !=NULL); - *matchLen1=0; - *matchLen2=0; - } - - start1=m1=org1=s1; - if(length1==-1) { - limit1=NULL; - } else { - limit1=s1+length1; - } - - start2=m2=org2=s2; - if(length2==-1) { - limit2=NULL; - } else { - limit2=s2+length2; - } - - level1=level2=0; - c1=c2=-1; - - /* comparison loop */ - for(;;) { - /* - * here a code unit value of -1 means "get another code unit" - * below it will mean "this source is finished" - */ - - if(c1<0) { - /* get next code unit from string 1, post-increment */ - for(;;) { - if(s1==limit1 || ((c1=*s1)==0 && (limit1==NULL || (options&_STRNCMP_STYLE)))) { - if(level1==0) { - c1=-1; - break; - } - } else { - ++s1; - break; - } - - /* reached end of level buffer, pop one level */ - do { - --level1; - start1=stack1[level1].start; /*Not uninitialized*/ - } while(start1==NULL); - s1=stack1[level1].s; /*Not uninitialized*/ - limit1=stack1[level1].limit; /*Not uninitialized*/ - } - } - - if(c2<0) { - /* get next code unit from string 2, post-increment */ - for(;;) { - if(s2==limit2 || ((c2=*s2)==0 && (limit2==NULL || (options&_STRNCMP_STYLE)))) { - if(level2==0) { - c2=-1; - break; - } - } else { - ++s2; - break; - } - - /* reached end of level buffer, pop one level */ - do { - --level2; - start2=stack2[level2].start; /*Not uninitialized*/ - } while(start2==NULL); - s2=stack2[level2].s; /*Not uninitialized*/ - limit2=stack2[level2].limit; /*Not uninitialized*/ - } - } - - /* - * compare c1 and c2 - * either variable c1, c2 is -1 only if the corresponding string is finished - */ - if(c1==c2) { - const UChar *next1, *next2; - - if(c1<0) { - cmpRes=0; /* c1==c2==-1 indicating end of strings */ - break; - } - - /* - * Note: Move the match positions in both strings at the same time - * only when corresponding code point(s) in the original strings - * are fully consumed. For example, when comparing s1="Fust" and - * s2="Fu\u00dfball", s2[2] is folded into "ss", and s1[2] matches - * the first code point in the case-folded data. But the second "s" - * has no matching code point in s1, so this implementation returns - * 2 as the prefix match length ("Fu"). - */ - next1=next2=NULL; - if(level1==0) { - next1=s1; - } else if(s1==limit1) { - /* Note: This implementation only use a single level of stack. - * If this code needs to be changed to use multiple levels - * of stacks, the code above should check if the current - * code is at the end of all stacks. - */ - U_ASSERT(level1==1); - - /* is s1 at the end of the current stack? */ - next1=stack1[0].s; - } - - if (next1!=NULL) { - if(level2==0) { - next2=s2; - } else if(s2==limit2) { - U_ASSERT(level2==1); - - /* is s2 at the end of the current stack? */ - next2=stack2[0].s; - } - if(next2!=NULL) { - m1=next1; - m2=next2; - } - } - c1=c2=-1; /* make us fetch new code units */ - continue; - } else if(c1<0) { - cmpRes=-1; /* string 1 ends before string 2 */ - break; - } else if(c2<0) { - cmpRes=1; /* string 2 ends before string 1 */ - break; - } - /* c1!=c2 && c1>=0 && c2>=0 */ - - /* get complete code points for c1, c2 for lookups if either is a surrogate */ - cp1=c1; - if(U_IS_SURROGATE(c1)) { - UChar c; - - if(U_IS_SURROGATE_LEAD(c1)) { - if(s1!=limit1 && U16_IS_TRAIL(c=*s1)) { - /* advance ++s1; only below if cp1 decomposes/case-folds */ - cp1=U16_GET_SUPPLEMENTARY(c1, c); - } - } else /* isTrail(c1) */ { - if(start1<=(s1-2) && U16_IS_LEAD(c=*(s1-2))) { - cp1=U16_GET_SUPPLEMENTARY(c, c1); - } - } - } - - cp2=c2; - if(U_IS_SURROGATE(c2)) { - UChar c; - - if(U_IS_SURROGATE_LEAD(c2)) { - if(s2!=limit2 && U16_IS_TRAIL(c=*s2)) { - /* advance ++s2; only below if cp2 decomposes/case-folds */ - cp2=U16_GET_SUPPLEMENTARY(c2, c); - } - } else /* isTrail(c2) */ { - if(start2<=(s2-2) && U16_IS_LEAD(c=*(s2-2))) { - cp2=U16_GET_SUPPLEMENTARY(c, c2); - } - } - } - - /* - * go down one level for each string - * continue with the main loop as soon as there is a real change - */ - - if( level1==0 && - (length=ucase_toFullFolding((UChar32)cp1, &p, options))>=0 - ) { - /* cp1 case-folds to the code point "length" or to p[length] */ - if(U_IS_SURROGATE(c1)) { - if(U_IS_SURROGATE_LEAD(c1)) { - /* advance beyond source surrogate pair if it case-folds */ - ++s1; - } else /* isTrail(c1) */ { - /* - * we got a supplementary code point when hitting its trail surrogate, - * therefore the lead surrogate must have been the same as in the other string; - * compare this decomposition with the lead surrogate in the other string - * remember that this simulates bulk text replacement: - * the decomposition would replace the entire code point - */ - --s2; - --m2; - c2=*(s2-1); - } - } - - /* push current level pointers */ - stack1[0].start=start1; - stack1[0].s=s1; - stack1[0].limit=limit1; - ++level1; - - /* copy the folding result to fold1[] */ - if(length<=UCASE_MAX_STRING_LENGTH) { - u_memcpy(fold1, p, length); - } else { - int32_t i=0; - U16_APPEND_UNSAFE(fold1, i, length); - length=i; - } - - /* set next level pointers to case folding */ - start1=s1=fold1; - limit1=fold1+length; - - /* get ready to read from decomposition, continue with loop */ - c1=-1; - continue; - } - - if( level2==0 && - (length=ucase_toFullFolding((UChar32)cp2, &p, options))>=0 - ) { - /* cp2 case-folds to the code point "length" or to p[length] */ - if(U_IS_SURROGATE(c2)) { - if(U_IS_SURROGATE_LEAD(c2)) { - /* advance beyond source surrogate pair if it case-folds */ - ++s2; - } else /* isTrail(c2) */ { - /* - * we got a supplementary code point when hitting its trail surrogate, - * therefore the lead surrogate must have been the same as in the other string; - * compare this decomposition with the lead surrogate in the other string - * remember that this simulates bulk text replacement: - * the decomposition would replace the entire code point - */ - --s1; - --m2; - c1=*(s1-1); - } - } - - /* push current level pointers */ - stack2[0].start=start2; - stack2[0].s=s2; - stack2[0].limit=limit2; - ++level2; - - /* copy the folding result to fold2[] */ - if(length<=UCASE_MAX_STRING_LENGTH) { - u_memcpy(fold2, p, length); - } else { - int32_t i=0; - U16_APPEND_UNSAFE(fold2, i, length); - length=i; - } - - /* set next level pointers to case folding */ - start2=s2=fold2; - limit2=fold2+length; - - /* get ready to read from decomposition, continue with loop */ - c2=-1; - continue; - } - - /* - * no decomposition/case folding, max level for both sides: - * return difference result - * - * code point order comparison must not just return cp1-cp2 - * because when single surrogates are present then the surrogate pairs - * that formed cp1 and cp2 may be from different string indexes - * - * example: { d800 d800 dc01 } vs. { d800 dc00 }, compare at second code units - * c1=d800 cp1=10001 c2=dc00 cp2=10000 - * cp1-cp2>0 but c1-c2<0 and in fact in UTF-32 it is { d800 10001 } < { 10000 } - * - * therefore, use same fix-up as in ustring.c/uprv_strCompare() - * except: uprv_strCompare() fetches c=*s while this functions fetches c=*s++ - * so we have slightly different pointer/start/limit comparisons here - */ - - if(c1>=0xd800 && c2>=0xd800 && (options&U_COMPARE_CODE_POINT_ORDER)) { - /* subtract 0x2800 from BMP code points to make them smaller than supplementary ones */ - if( - (c1<=0xdbff && s1!=limit1 && U16_IS_TRAIL(*s1)) || - (U16_IS_TRAIL(c1) && start1!=(s1-1) && U16_IS_LEAD(*(s1-2))) - ) { - /* part of a surrogate pair, leave >=d800 */ - } else { - /* BMP code point - may be surrogate code point - make =d800 */ - } else { - /* BMP code point - may be surrogate code point - make (m1-org1); - *matchLen2=static_cast(m2-org2); - } - return cmpRes; -} - -/* internal function */ -U_CFUNC int32_t -u_strcmpFold(const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - uint32_t options, - UErrorCode *pErrorCode) { - return _cmpFold(s1, length1, s2, length2, options, NULL, NULL, pErrorCode); -} - -/* public API functions */ - -U_CAPI int32_t U_EXPORT2 -u_strCaseCompare(const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - uint32_t options, - UErrorCode *pErrorCode) { - /* argument checking */ - if(pErrorCode==0 || U_FAILURE(*pErrorCode)) { - return 0; - } - if(s1==NULL || length1<-1 || s2==NULL || length2<-1) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - return u_strcmpFold(s1, length1, s2, length2, - options|U_COMPARE_IGNORE_CASE, - pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -u_strcasecmp(const UChar *s1, const UChar *s2, uint32_t options) { - UErrorCode errorCode=U_ZERO_ERROR; - return u_strcmpFold(s1, -1, s2, -1, - options|U_COMPARE_IGNORE_CASE, - &errorCode); -} - -U_CAPI int32_t U_EXPORT2 -u_memcasecmp(const UChar *s1, const UChar *s2, int32_t length, uint32_t options) { - UErrorCode errorCode=U_ZERO_ERROR; - return u_strcmpFold(s1, length, s2, length, - options|U_COMPARE_IGNORE_CASE, - &errorCode); -} - -U_CAPI int32_t U_EXPORT2 -u_strncasecmp(const UChar *s1, const UChar *s2, int32_t n, uint32_t options) { - UErrorCode errorCode=U_ZERO_ERROR; - return u_strcmpFold(s1, n, s2, n, - options|(U_COMPARE_IGNORE_CASE|_STRNCMP_STYLE), - &errorCode); -} - -/* internal API - detect length of shared prefix */ -U_CAPI void -u_caseInsensitivePrefixMatch(const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - uint32_t options, - int32_t *matchLen1, int32_t *matchLen2, - UErrorCode *pErrorCode) { - _cmpFold(s1, length1, s2, length2, options, - matchLen1, matchLen2, pErrorCode); -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2001-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: ustrcase.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2002feb20 +* created by: Markus W. Scherer +* +* Implementation file for string casing C API functions. +* Uses functions from uchar.c for basic functionality that requires access +* to the Unicode Character Database (uprops.dat). +*/ + +#include "unicode/utypes.h" +#include "unicode/brkiter.h" +#include "unicode/casemap.h" +#include "unicode/edits.h" +#include "unicode/stringoptions.h" +#include "unicode/ustring.h" +#include "unicode/ucasemap.h" +#include "unicode/ubrk.h" +#include "unicode/utf.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "ucase.h" +#include "ucasemap_imp.h" +#include "ustr_imp.h" +#include "uassert.h" + +/** + * Code point for COMBINING ACUTE ACCENT + * @internal + */ +#define ACUTE u'\u0301' + +U_NAMESPACE_BEGIN + +namespace { + +int32_t checkOverflowAndEditsError(int32_t destIndex, int32_t destCapacity, + Edits *edits, UErrorCode &errorCode) { + if (U_SUCCESS(errorCode)) { + if (destIndex > destCapacity) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + } else if (edits != nullptr) { + edits->copyErrorTo(errorCode); + } + } + return destIndex; +} + +/* Appends a full case mapping result, see UCASE_MAX_STRING_LENGTH. */ +inline int32_t +appendResult(char16_t *dest, int32_t destIndex, int32_t destCapacity, + int32_t result, const char16_t *s, + int32_t cpLength, uint32_t options, icu::Edits *edits) { + UChar32 c; + int32_t length; + + /* decode the result */ + if(result<0) { + /* (not) original code point */ + if(edits!=nullptr) { + edits->addUnchanged(cpLength); + } + if(options & U_OMIT_UNCHANGED_TEXT) { + return destIndex; + } + c=~result; + if(destIndexaddReplace(cpLength, 1); + } + return destIndex; + } else { + c=result; + length=U16_LENGTH(c); + } + if(edits!=nullptr) { + edits->addReplace(cpLength, length); + } + } + if(length>(INT32_MAX-destIndex)) { + return -1; // integer overflow + } + + if(destIndex=0) { + /* code point */ + UBool isError=false; + U16_APPEND(dest, destIndex, destCapacity, c, isError); + if(isError) { + /* overflow, nothing written */ + destIndex+=length; + } + } else { + /* string */ + if((destIndex+length)<=destCapacity) { + while(length>0) { + dest[destIndex++]=*s++; + --length; + } + } else { + /* overflow */ + destIndex+=length; + } + } + } else { + /* preflight */ + destIndex+=length; + } + return destIndex; +} + +inline int32_t +appendUChar(char16_t *dest, int32_t destIndex, int32_t destCapacity, char16_t c) { + if(destIndexaddUnchanged(length); + } + if(options & U_OMIT_UNCHANGED_TEXT) { + return destIndex; + } + if(length>(INT32_MAX-destIndex)) { + return -1; // integer overflow + } + if((destIndex+length)<=destCapacity) { + u_memcpy(dest+destIndex, s, length); + } + return destIndex + length; +} + +inline int32_t +appendUnchanged(char16_t *dest, int32_t destIndex, int32_t destCapacity, + const char16_t *s, int32_t length, uint32_t options, icu::Edits *edits) { + if (length <= 0) { + return destIndex; + } + return appendNonEmptyUnchanged(dest, destIndex, destCapacity, s, length, options, edits); +} + +UChar32 U_CALLCONV +utf16_caseContextIterator(void *context, int8_t dir) { + UCaseContext *csc=(UCaseContext *)context; + UChar32 c; + + if(dir<0) { + /* reset for backward iteration */ + csc->index=csc->cpStart; + csc->dir=dir; + } else if(dir>0) { + /* reset for forward iteration */ + csc->index=csc->cpLimit; + csc->dir=dir; + } else { + /* continue current iteration direction */ + dir=csc->dir; + } + + if(dir<0) { + if(csc->startindex) { + U16_PREV((const char16_t *)csc->p, csc->start, csc->index, c); + return c; + } + } else { + if(csc->indexlimit) { + U16_NEXT((const char16_t *)csc->p, csc->index, csc->limit, c); + return c; + } + } + return U_SENTINEL; +} + +/** + * caseLocale >= 0: Lowercases [srcStart..srcLimit[ but takes context [0..srcLength[ into account. + * caseLocale < 0: Case-folds [srcStart..srcLimit[. + */ +int32_t toLower(int32_t caseLocale, uint32_t options, + char16_t *dest, int32_t destCapacity, + const char16_t *src, UCaseContext *csc, int32_t srcStart, int32_t srcLimit, + icu::Edits *edits, UErrorCode &errorCode) { + const int8_t *latinToLower; + if (caseLocale == UCASE_LOC_ROOT || + (caseLocale >= 0 ? + !(caseLocale == UCASE_LOC_TURKISH || caseLocale == UCASE_LOC_LITHUANIAN) : + (options & _FOLD_CASE_OPTIONS_MASK) == U_FOLD_CASE_DEFAULT)) { + latinToLower = LatinCase::TO_LOWER_NORMAL; + } else { + latinToLower = LatinCase::TO_LOWER_TR_LT; + } + const UTrie2 *trie = ucase_getTrie(); + int32_t destIndex = 0; + int32_t prev = srcStart; + int32_t srcIndex = srcStart; + for (;;) { + // fast path for simple cases + char16_t lead = 0; + while (srcIndex < srcLimit) { + lead = src[srcIndex]; + int32_t delta; + if (lead < LatinCase::LONG_S) { + int8_t d = latinToLower[lead]; + if (d == LatinCase::EXC) { break; } + ++srcIndex; + if (d == 0) { continue; } + delta = d; + } else if (lead >= 0xd800) { + break; // surrogate or higher + } else { + uint16_t props = UTRIE2_GET16_FROM_U16_SINGLE_LEAD(trie, lead); + if (UCASE_HAS_EXCEPTION(props)) { break; } + ++srcIndex; + if (!UCASE_IS_UPPER_OR_TITLE(props) || (delta = UCASE_GET_DELTA(props)) == 0) { + continue; + } + } + lead += static_cast(delta); + destIndex = appendUnchanged(dest, destIndex, destCapacity, + src + prev, srcIndex - 1 - prev, options, edits); + if (destIndex >= 0) { + destIndex = appendUChar(dest, destIndex, destCapacity, lead); + if (edits != nullptr) { + edits->addReplace(1, 1); + } + } + if (destIndex < 0) { + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + prev = srcIndex; + } + if (srcIndex >= srcLimit) { + break; + } + // slow path + int32_t cpStart = srcIndex++; + char16_t trail; + UChar32 c; + if (U16_IS_LEAD(lead) && srcIndex < srcLimit && U16_IS_TRAIL(trail = src[srcIndex])) { + c = U16_GET_SUPPLEMENTARY(lead, trail); + ++srcIndex; + } else { + c = lead; + } + const char16_t *s; + if (caseLocale >= 0) { + csc->cpStart = cpStart; + csc->cpLimit = srcIndex; + c = ucase_toFullLower(c, utf16_caseContextIterator, csc, &s, caseLocale); + } else { + c = ucase_toFullFolding(c, &s, options); + } + if (c >= 0) { + destIndex = appendUnchanged(dest, destIndex, destCapacity, + src + prev, cpStart - prev, options, edits); + if (destIndex >= 0) { + destIndex = appendResult(dest, destIndex, destCapacity, c, s, + srcIndex - cpStart, options, edits); + } + if (destIndex < 0) { + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + prev = srcIndex; + } + } + destIndex = appendUnchanged(dest, destIndex, destCapacity, + src + prev, srcIndex - prev, options, edits); + if (destIndex < 0) { + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + return destIndex; +} + +int32_t toUpper(int32_t caseLocale, uint32_t options, + char16_t *dest, int32_t destCapacity, + const char16_t *src, UCaseContext *csc, int32_t srcLength, + icu::Edits *edits, UErrorCode &errorCode) { + const int8_t *latinToUpper; + if (caseLocale == UCASE_LOC_TURKISH) { + latinToUpper = LatinCase::TO_UPPER_TR; + } else { + latinToUpper = LatinCase::TO_UPPER_NORMAL; + } + const UTrie2 *trie = ucase_getTrie(); + int32_t destIndex = 0; + int32_t prev = 0; + int32_t srcIndex = 0; + for (;;) { + // fast path for simple cases + char16_t lead = 0; + while (srcIndex < srcLength) { + lead = src[srcIndex]; + int32_t delta; + if (lead < LatinCase::LONG_S) { + int8_t d = latinToUpper[lead]; + if (d == LatinCase::EXC) { break; } + ++srcIndex; + if (d == 0) { continue; } + delta = d; + } else if (lead >= 0xd800) { + break; // surrogate or higher + } else { + uint16_t props = UTRIE2_GET16_FROM_U16_SINGLE_LEAD(trie, lead); + if (UCASE_HAS_EXCEPTION(props)) { break; } + ++srcIndex; + if (UCASE_GET_TYPE(props) != UCASE_LOWER || (delta = UCASE_GET_DELTA(props)) == 0) { + continue; + } + } + lead += static_cast(delta); + destIndex = appendUnchanged(dest, destIndex, destCapacity, + src + prev, srcIndex - 1 - prev, options, edits); + if (destIndex >= 0) { + destIndex = appendUChar(dest, destIndex, destCapacity, lead); + if (edits != nullptr) { + edits->addReplace(1, 1); + } + } + if (destIndex < 0) { + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + prev = srcIndex; + } + if (srcIndex >= srcLength) { + break; + } + // slow path + int32_t cpStart; + csc->cpStart = cpStart = srcIndex++; + char16_t trail; + UChar32 c; + if (U16_IS_LEAD(lead) && srcIndex < srcLength && U16_IS_TRAIL(trail = src[srcIndex])) { + c = U16_GET_SUPPLEMENTARY(lead, trail); + ++srcIndex; + } else { + c = lead; + } + csc->cpLimit = srcIndex; + const char16_t *s; + c = ucase_toFullUpper(c, utf16_caseContextIterator, csc, &s, caseLocale); + if (c >= 0) { + destIndex = appendUnchanged(dest, destIndex, destCapacity, + src + prev, cpStart - prev, options, edits); + if (destIndex >= 0) { + destIndex = appendResult(dest, destIndex, destCapacity, c, s, + srcIndex - cpStart, options, edits); + } + if (destIndex < 0) { + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + prev = srcIndex; + } + } + destIndex = appendUnchanged(dest, destIndex, destCapacity, + src + prev, srcIndex - prev, options, edits); + if (destIndex < 0) { + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + return destIndex; +} + +} // namespace + +U_NAMESPACE_END + +U_NAMESPACE_USE + +#if !UCONFIG_NO_BREAK_ITERATION + +namespace { + +/** + * Input: c is a letter I with or without acute accent. + * start is the index in src after c, and is less than segmentLimit. + * If a plain i/I is followed by a plain j/J, + * or an i/I with acute (precomposed or decomposed) is followed by a j/J with acute, + * then we output accordingly. + * + * @return the src index after the titlecased sequence, or the start index if no Dutch IJ + */ +int32_t maybeTitleDutchIJ(const char16_t *src, UChar32 c, int32_t start, int32_t segmentLimit, + char16_t *dest, int32_t &destIndex, int32_t destCapacity, uint32_t options, + icu::Edits *edits) { + U_ASSERT(start < segmentLimit); + + int32_t index = start; + bool withAcute = false; + + // If the conditions are met, then the following variables tell us what to output. + int32_t unchanged1 = 0; // code units before the j, or the whole sequence (0..3) + bool doTitleJ = false; // true if the j needs to be titlecased + int32_t unchanged2 = 0; // after the j (0 or 1) + + // next character after the first letter + char16_t c2 = src[index++]; + + // Is the first letter an i/I with accent? + if (c == u'I') { + if (c2 == ACUTE) { + withAcute = true; + unchanged1 = 1; + if (index == segmentLimit) { return start; } + c2 = src[index++]; + } + } else { // Í + withAcute = true; + } + + // Is the next character a j/J? + if (c2 == u'j') { + doTitleJ = true; + } else if (c2 == u'J') { + ++unchanged1; + } else { + return start; + } + + // A plain i/I must be followed by a plain j/J. + // An i/I with acute must be followed by a j/J with acute. + if (withAcute) { + if (index == segmentLimit || src[index++] != ACUTE) { return start; } + if (doTitleJ) { + unchanged2 = 1; + } else { + ++unchanged1; + } + } + + // There must not be another combining mark. + if (index < segmentLimit) { + int32_t cp; + int32_t i = index; + U16_NEXT(src, i, segmentLimit, cp); + uint32_t typeMask = U_GET_GC_MASK(cp); + if ((typeMask & U_GC_M_MASK) != 0) { + return start; + } + } + + // Output the rest of the Dutch IJ. + destIndex = appendUnchanged(dest, destIndex, destCapacity, src + start, unchanged1, options, edits); + start += unchanged1; + if (doTitleJ) { + destIndex = appendUChar(dest, destIndex, destCapacity, u'J'); + if (edits != nullptr) { + edits->addReplace(1, 1); + } + ++start; + } + destIndex = appendUnchanged(dest, destIndex, destCapacity, src + start, unchanged2, options, edits); + + U_ASSERT(start + unchanged2 == index); + return index; +} + +} // namespace + +U_CFUNC int32_t U_CALLCONV +ustrcase_internalToTitle(int32_t caseLocale, uint32_t options, BreakIterator *iter, + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + icu::Edits *edits, + UErrorCode &errorCode) { + if (!ustrcase_checkTitleAdjustmentOptions(options, errorCode)) { + return 0; + } + + /* set up local variables */ + UCaseContext csc=UCASECONTEXT_INITIALIZER; + csc.p=(void *)src; + csc.limit=srcLength; + int32_t destIndex=0; + int32_t prev=0; + bool isFirstIndex=true; + + /* titlecasing loop */ + while(prevfirst(); + } else { + index=iter->next(); + } + if(index==UBRK_DONE || index>srcLength) { + index=srcLength; + } + + /* + * Segment [prev..index[ into 3 parts: + * a) skipped characters (copy as-is) [prev..titleStart[ + * b) first letter (titlecase) [titleStart..titleLimit[ + * c) subsequent characters (lowercase) [titleLimit..index[ + */ + if(prev 0) { + uint32_t upper = data & UPPER_MASK; + // Add a dialytika to this iota or ypsilon vowel + // if we removed a tonos from the previous vowel, + // and that previous vowel did not also have (or gain) a dialytika. + // Adding one only to the final vowel in a longer sequence + // (which does not occur in normal writing) would require lookahead. + // Set the same flag as for preserving an existing dialytika. + if ((data & HAS_VOWEL) != 0 && (state & AFTER_VOWEL_WITH_ACCENT) != 0 && + (upper == 0x399 || upper == 0x3A5)) { + data |= HAS_DIALYTIKA; + } + int32_t numYpogegrammeni = 0; // Map each one to a trailing, spacing, capital iota. + if ((data & HAS_YPOGEGRAMMENI) != 0) { + numYpogegrammeni = 1; + } + // Skip combining diacritics after this Greek letter. + while (nextIndex < srcLength) { + uint32_t diacriticData = getDiacriticData(src[nextIndex]); + if (diacriticData != 0) { + data |= diacriticData; + if ((diacriticData & HAS_YPOGEGRAMMENI) != 0) { + ++numYpogegrammeni; + } + ++nextIndex; + } else { + break; // not a Greek diacritic + } + } + if ((data & HAS_VOWEL_AND_ACCENT_AND_DIALYTIKA) == HAS_VOWEL_AND_ACCENT) { + nextState |= AFTER_VOWEL_WITH_ACCENT; + } + // Map according to Greek rules. + UBool addTonos = false; + if (upper == 0x397 && + (data & HAS_ACCENT) != 0 && + numYpogegrammeni == 0 && + (state & AFTER_CASED) == 0 && + !isFollowedByCasedLetter(src, nextIndex, srcLength)) { + // Keep disjunctive "or" with (only) a tonos. + // We use the same "word boundary" conditions as for the Final_Sigma test. + if (i == nextIndex) { + upper = 0x389; // Preserve the precomposed form. + } else { + addTonos = true; + } + } else if ((data & HAS_DIALYTIKA) != 0) { + // Preserve a vowel with dialytika in precomposed form if it exists. + if (upper == 0x399) { + upper = 0x3AA; + data &= ~HAS_EITHER_DIALYTIKA; + } else if (upper == 0x3A5) { + upper = 0x3AB; + data &= ~HAS_EITHER_DIALYTIKA; + } + } + + UBool change; + if (edits == nullptr && (options & U_OMIT_UNCHANGED_TEXT) == 0) { + change = true; // common, simple usage + } else { + // Find out first whether we are changing the text. + change = src[i] != upper || numYpogegrammeni > 0; + int32_t i2 = i + 1; + if ((data & HAS_EITHER_DIALYTIKA) != 0) { + change |= i2 >= nextIndex || src[i2] != 0x308; + ++i2; + } + if (addTonos) { + change |= i2 >= nextIndex || src[i2] != 0x301; + ++i2; + } + int32_t oldLength = nextIndex - i; + int32_t newLength = (i2 - i) + numYpogegrammeni; + change |= oldLength != newLength; + if (change) { + if (edits != nullptr) { + edits->addReplace(oldLength, newLength); + } + } else { + if (edits != nullptr) { + edits->addUnchanged(oldLength); + } + // Write unchanged text? + change = (options & U_OMIT_UNCHANGED_TEXT) == 0; + } + } + + if (change) { + destIndex=appendUChar(dest, destIndex, destCapacity, (char16_t)upper); + if (destIndex >= 0 && (data & HAS_EITHER_DIALYTIKA) != 0) { + destIndex=appendUChar(dest, destIndex, destCapacity, 0x308); // restore or add a dialytika + } + if (destIndex >= 0 && addTonos) { + destIndex=appendUChar(dest, destIndex, destCapacity, 0x301); + } + while (destIndex >= 0 && numYpogegrammeni > 0) { + destIndex=appendUChar(dest, destIndex, destCapacity, 0x399); + --numYpogegrammeni; + } + if(destIndex<0) { + errorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + } + } else { + const char16_t *s; + c=ucase_toFullUpper(c, nullptr, nullptr, &s, UCASE_LOC_GREEK); + destIndex = appendResult(dest, destIndex, destCapacity, c, s, + nextIndex - i, options, edits); + if (destIndex < 0) { + errorCode = U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + } + i = nextIndex; + state = nextState; + } + + return destIndex; +} + +} // namespace GreekUpper +U_NAMESPACE_END + +/* functions available in the common library (for unistr_case.cpp) */ + +U_CFUNC int32_t U_CALLCONV +ustrcase_internalToLower(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + icu::Edits *edits, + UErrorCode &errorCode) { + UCaseContext csc=UCASECONTEXT_INITIALIZER; + csc.p=(void *)src; + csc.limit=srcLength; + int32_t destIndex = toLower( + caseLocale, options, + dest, destCapacity, + src, &csc, 0, srcLength, + edits, errorCode); + return checkOverflowAndEditsError(destIndex, destCapacity, edits, errorCode); +} + +U_CFUNC int32_t U_CALLCONV +ustrcase_internalToUpper(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + icu::Edits *edits, + UErrorCode &errorCode) { + int32_t destIndex; + if (caseLocale == UCASE_LOC_GREEK) { + destIndex = GreekUpper::toUpper(options, dest, destCapacity, + src, srcLength, edits, errorCode); + } else { + UCaseContext csc=UCASECONTEXT_INITIALIZER; + csc.p=(void *)src; + csc.limit=srcLength; + destIndex = toUpper( + caseLocale, options, + dest, destCapacity, + src, &csc, srcLength, + edits, errorCode); + } + return checkOverflowAndEditsError(destIndex, destCapacity, edits, errorCode); +} + +U_CFUNC int32_t U_CALLCONV +ustrcase_internalFold(int32_t /* caseLocale */, uint32_t options, UCASEMAP_BREAK_ITERATOR_UNUSED + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + icu::Edits *edits, + UErrorCode &errorCode) { + int32_t destIndex = toLower( + -1, options, + dest, destCapacity, + src, nullptr, 0, srcLength, + edits, errorCode); + return checkOverflowAndEditsError(destIndex, destCapacity, edits, errorCode); +} + +U_CFUNC int32_t +ustrcase_map(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + UStringCaseMapper *stringCaseMapper, + icu::Edits *edits, + UErrorCode &errorCode) { + int32_t destLength; + + /* check argument values */ + if(U_FAILURE(errorCode)) { + return 0; + } + if( destCapacity<0 || + (dest==nullptr && destCapacity>0) || + src==nullptr || + srcLength<-1 + ) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* get the string length */ + if(srcLength==-1) { + srcLength=u_strlen(src); + } + + /* check for overlapping source and destination */ + if( dest!=nullptr && + ((src>=dest && src<(dest+destCapacity)) || + (dest>=src && dest<(src+srcLength))) + ) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + if (edits != nullptr && (options & U_EDITS_NO_RESET) == 0) { + edits->reset(); + } + destLength=stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR + dest, destCapacity, src, srcLength, edits, errorCode); + return u_terminateUChars(dest, destCapacity, destLength, &errorCode); +} + +U_CFUNC int32_t +ustrcase_mapWithOverlap(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM + char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + UStringCaseMapper *stringCaseMapper, + UErrorCode &errorCode) { + char16_t buffer[300]; + char16_t *temp; + + int32_t destLength; + + /* check argument values */ + if(U_FAILURE(errorCode)) { + return 0; + } + if( destCapacity<0 || + (dest==nullptr && destCapacity>0) || + src==nullptr || + srcLength<-1 + ) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* get the string length */ + if(srcLength==-1) { + srcLength=u_strlen(src); + } + + /* check for overlapping source and destination */ + if( dest!=nullptr && + ((src>=dest && src<(dest+destCapacity)) || + (dest>=src && dest<(src+srcLength))) + ) { + /* overlap: provide a temporary destination buffer and later copy the result */ + if(destCapacity<=UPRV_LENGTHOF(buffer)) { + /* the stack buffer is large enough */ + temp=buffer; + } else { + /* allocate a buffer */ + temp=(char16_t *)uprv_malloc(destCapacity*U_SIZEOF_UCHAR); + if(temp==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return 0; + } + } + } else { + temp=dest; + } + + destLength=stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR + temp, destCapacity, src, srcLength, nullptr, errorCode); + if(temp!=dest) { + /* copy the result string to the destination buffer */ + if (U_SUCCESS(errorCode) && 0 < destLength && destLength <= destCapacity) { + u_memmove(dest, temp, destLength); + } + if(temp!=buffer) { + uprv_free(temp); + } + } + + return u_terminateUChars(dest, destCapacity, destLength, &errorCode); +} + +/* public API functions */ + +U_CAPI int32_t U_EXPORT2 +u_strFoldCase(char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + uint32_t options, + UErrorCode *pErrorCode) { + return ustrcase_mapWithOverlap( + UCASE_LOC_ROOT, options, UCASEMAP_BREAK_ITERATOR_NULL + dest, destCapacity, + src, srcLength, + ustrcase_internalFold, *pErrorCode); +} + +U_NAMESPACE_BEGIN + +int32_t CaseMap::fold( + uint32_t options, + const char16_t *src, int32_t srcLength, + char16_t *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode) { + return ustrcase_map( + UCASE_LOC_ROOT, options, UCASEMAP_BREAK_ITERATOR_NULL + dest, destCapacity, + src, srcLength, + ustrcase_internalFold, edits, errorCode); +} + +U_NAMESPACE_END + +/* case-insensitive string comparisons -------------------------------------- */ + +/* + * This function is a copy of unorm_cmpEquivFold() minus the parts for + * canonical equivalence. + * Keep the functions in sync, and see there for how this works. + * The duplication is for modularization: + * It makes caseless (but not canonical caseless) matches independent of + * the normalization code. + */ + +/* stack element for previous-level source/decomposition pointers */ +struct CmpEquivLevel { + const char16_t *start, *s, *limit; +}; +typedef struct CmpEquivLevel CmpEquivLevel; + +/** + * Internal implementation code comparing string with case fold. + * This function is called from u_strcmpFold() and u_caseInsensitivePrefixMatch(). + * + * @param s1 input string 1 + * @param length1 length of string 1, or -1 (NUL terminated) + * @param s2 input string 2 + * @param length2 length of string 2, or -1 (NUL terminated) + * @param options compare options + * @param matchLen1 (output) length of partial prefix match in s1 + * @param matchLen2 (output) length of partial prefix match in s2 + * @param pErrorCode receives error status + * @return The result of comparison + */ +static int32_t _cmpFold( + const char16_t *s1, int32_t length1, + const char16_t *s2, int32_t length2, + uint32_t options, + int32_t *matchLen1, int32_t *matchLen2, + UErrorCode *pErrorCode) { + int32_t cmpRes = 0; + + /* current-level start/limit - s1/s2 as current */ + const char16_t *start1, *start2, *limit1, *limit2; + + /* points to the original start address */ + const char16_t *org1, *org2; + + /* points to the end of match + 1 */ + const char16_t *m1, *m2; + + /* case folding variables */ + const char16_t *p; + int32_t length; + + /* stacks of previous-level start/current/limit */ + CmpEquivLevel stack1[2], stack2[2]; + + /* case folding buffers, only use current-level start/limit */ + char16_t fold1[UCASE_MAX_STRING_LENGTH+1], fold2[UCASE_MAX_STRING_LENGTH+1]; + + /* track which is the current level per string */ + int32_t level1, level2; + + /* current code units, and code points for lookups */ + UChar32 c1, c2, cp1, cp2; + + /* no argument error checking because this itself is not an API */ + + /* + * assume that at least the option U_COMPARE_IGNORE_CASE is set + * otherwise this function would have to behave exactly as uprv_strCompare() + */ + if(U_FAILURE(*pErrorCode)) { + return 0; + } + + /* initialize */ + if(matchLen1) { + U_ASSERT(matchLen2 !=nullptr); + *matchLen1=0; + *matchLen2=0; + } + + start1=m1=org1=s1; + if(length1==-1) { + limit1=nullptr; + } else { + limit1=s1+length1; + } + + start2=m2=org2=s2; + if(length2==-1) { + limit2=nullptr; + } else { + limit2=s2+length2; + } + + level1=level2=0; + c1=c2=-1; + + /* comparison loop */ + for(;;) { + /* + * here a code unit value of -1 means "get another code unit" + * below it will mean "this source is finished" + */ + + if(c1<0) { + /* get next code unit from string 1, post-increment */ + for(;;) { + if(s1==limit1 || ((c1=*s1)==0 && (limit1==nullptr || (options&_STRNCMP_STYLE)))) { + if(level1==0) { + c1=-1; + break; + } + } else { + ++s1; + break; + } + + /* reached end of level buffer, pop one level */ + do { + --level1; + start1=stack1[level1].start; /*Not uninitialized*/ + } while(start1==nullptr); + s1=stack1[level1].s; /*Not uninitialized*/ + limit1=stack1[level1].limit; /*Not uninitialized*/ + } + } + + if(c2<0) { + /* get next code unit from string 2, post-increment */ + for(;;) { + if(s2==limit2 || ((c2=*s2)==0 && (limit2==nullptr || (options&_STRNCMP_STYLE)))) { + if(level2==0) { + c2=-1; + break; + } + } else { + ++s2; + break; + } + + /* reached end of level buffer, pop one level */ + do { + --level2; + start2=stack2[level2].start; /*Not uninitialized*/ + } while(start2==nullptr); + s2=stack2[level2].s; /*Not uninitialized*/ + limit2=stack2[level2].limit; /*Not uninitialized*/ + } + } + + /* + * compare c1 and c2 + * either variable c1, c2 is -1 only if the corresponding string is finished + */ + if(c1==c2) { + const char16_t *next1, *next2; + + if(c1<0) { + cmpRes=0; /* c1==c2==-1 indicating end of strings */ + break; + } + + /* + * Note: Move the match positions in both strings at the same time + * only when corresponding code point(s) in the original strings + * are fully consumed. For example, when comparing s1="Fust" and + * s2="Fu\u00dfball", s2[2] is folded into "ss", and s1[2] matches + * the first code point in the case-folded data. But the second "s" + * has no matching code point in s1, so this implementation returns + * 2 as the prefix match length ("Fu"). + */ + next1=next2=nullptr; + if(level1==0) { + next1=s1; + } else if(s1==limit1) { + /* Note: This implementation only use a single level of stack. + * If this code needs to be changed to use multiple levels + * of stacks, the code above should check if the current + * code is at the end of all stacks. + */ + U_ASSERT(level1==1); + + /* is s1 at the end of the current stack? */ + next1=stack1[0].s; + } + + if (next1!=nullptr) { + if(level2==0) { + next2=s2; + } else if(s2==limit2) { + U_ASSERT(level2==1); + + /* is s2 at the end of the current stack? */ + next2=stack2[0].s; + } + if(next2!=nullptr) { + m1=next1; + m2=next2; + } + } + c1=c2=-1; /* make us fetch new code units */ + continue; + } else if(c1<0) { + cmpRes=-1; /* string 1 ends before string 2 */ + break; + } else if(c2<0) { + cmpRes=1; /* string 2 ends before string 1 */ + break; + } + /* c1!=c2 && c1>=0 && c2>=0 */ + + /* get complete code points for c1, c2 for lookups if either is a surrogate */ + cp1=c1; + if(U_IS_SURROGATE(c1)) { + char16_t c; + + if(U_IS_SURROGATE_LEAD(c1)) { + if(s1!=limit1 && U16_IS_TRAIL(c=*s1)) { + /* advance ++s1; only below if cp1 decomposes/case-folds */ + cp1=U16_GET_SUPPLEMENTARY(c1, c); + } + } else /* isTrail(c1) */ { + if(start1<=(s1-2) && U16_IS_LEAD(c=*(s1-2))) { + cp1=U16_GET_SUPPLEMENTARY(c, c1); + } + } + } + + cp2=c2; + if(U_IS_SURROGATE(c2)) { + char16_t c; + + if(U_IS_SURROGATE_LEAD(c2)) { + if(s2!=limit2 && U16_IS_TRAIL(c=*s2)) { + /* advance ++s2; only below if cp2 decomposes/case-folds */ + cp2=U16_GET_SUPPLEMENTARY(c2, c); + } + } else /* isTrail(c2) */ { + if(start2<=(s2-2) && U16_IS_LEAD(c=*(s2-2))) { + cp2=U16_GET_SUPPLEMENTARY(c, c2); + } + } + } + + /* + * go down one level for each string + * continue with the main loop as soon as there is a real change + */ + + if( level1==0 && + (length=ucase_toFullFolding((UChar32)cp1, &p, options))>=0 + ) { + /* cp1 case-folds to the code point "length" or to p[length] */ + if(U_IS_SURROGATE(c1)) { + if(U_IS_SURROGATE_LEAD(c1)) { + /* advance beyond source surrogate pair if it case-folds */ + ++s1; + } else /* isTrail(c1) */ { + /* + * we got a supplementary code point when hitting its trail surrogate, + * therefore the lead surrogate must have been the same as in the other string; + * compare this decomposition with the lead surrogate in the other string + * remember that this simulates bulk text replacement: + * the decomposition would replace the entire code point + */ + --s2; + --m2; + c2=*(s2-1); + } + } + + /* push current level pointers */ + stack1[0].start=start1; + stack1[0].s=s1; + stack1[0].limit=limit1; + ++level1; + + /* copy the folding result to fold1[] */ + if(length<=UCASE_MAX_STRING_LENGTH) { + u_memcpy(fold1, p, length); + } else { + int32_t i=0; + U16_APPEND_UNSAFE(fold1, i, length); + length=i; + } + + /* set next level pointers to case folding */ + start1=s1=fold1; + limit1=fold1+length; + + /* get ready to read from decomposition, continue with loop */ + c1=-1; + continue; + } + + if( level2==0 && + (length=ucase_toFullFolding((UChar32)cp2, &p, options))>=0 + ) { + /* cp2 case-folds to the code point "length" or to p[length] */ + if(U_IS_SURROGATE(c2)) { + if(U_IS_SURROGATE_LEAD(c2)) { + /* advance beyond source surrogate pair if it case-folds */ + ++s2; + } else /* isTrail(c2) */ { + /* + * we got a supplementary code point when hitting its trail surrogate, + * therefore the lead surrogate must have been the same as in the other string; + * compare this decomposition with the lead surrogate in the other string + * remember that this simulates bulk text replacement: + * the decomposition would replace the entire code point + */ + --s1; + --m2; + c1=*(s1-1); + } + } + + /* push current level pointers */ + stack2[0].start=start2; + stack2[0].s=s2; + stack2[0].limit=limit2; + ++level2; + + /* copy the folding result to fold2[] */ + if(length<=UCASE_MAX_STRING_LENGTH) { + u_memcpy(fold2, p, length); + } else { + int32_t i=0; + U16_APPEND_UNSAFE(fold2, i, length); + length=i; + } + + /* set next level pointers to case folding */ + start2=s2=fold2; + limit2=fold2+length; + + /* get ready to read from decomposition, continue with loop */ + c2=-1; + continue; + } + + /* + * no decomposition/case folding, max level for both sides: + * return difference result + * + * code point order comparison must not just return cp1-cp2 + * because when single surrogates are present then the surrogate pairs + * that formed cp1 and cp2 may be from different string indexes + * + * example: { d800 d800 dc01 } vs. { d800 dc00 }, compare at second code units + * c1=d800 cp1=10001 c2=dc00 cp2=10000 + * cp1-cp2>0 but c1-c2<0 and in fact in UTF-32 it is { d800 10001 } < { 10000 } + * + * therefore, use same fix-up as in ustring.c/uprv_strCompare() + * except: uprv_strCompare() fetches c=*s while this functions fetches c=*s++ + * so we have slightly different pointer/start/limit comparisons here + */ + + if(c1>=0xd800 && c2>=0xd800 && (options&U_COMPARE_CODE_POINT_ORDER)) { + /* subtract 0x2800 from BMP code points to make them smaller than supplementary ones */ + if( + (c1<=0xdbff && s1!=limit1 && U16_IS_TRAIL(*s1)) || + (U16_IS_TRAIL(c1) && start1!=(s1-1) && U16_IS_LEAD(*(s1-2))) + ) { + /* part of a surrogate pair, leave >=d800 */ + } else { + /* BMP code point - may be surrogate code point - make =d800 */ + } else { + /* BMP code point - may be surrogate code point - make (m1-org1); + *matchLen2=static_cast(m2-org2); + } + return cmpRes; +} + +/* internal function */ +U_CFUNC int32_t +u_strcmpFold(const char16_t *s1, int32_t length1, + const char16_t *s2, int32_t length2, + uint32_t options, + UErrorCode *pErrorCode) { + return _cmpFold(s1, length1, s2, length2, options, nullptr, nullptr, pErrorCode); +} + +/* public API functions */ + +U_CAPI int32_t U_EXPORT2 +u_strCaseCompare(const char16_t *s1, int32_t length1, + const char16_t *s2, int32_t length2, + uint32_t options, + UErrorCode *pErrorCode) { + /* argument checking */ + if(pErrorCode==0 || U_FAILURE(*pErrorCode)) { + return 0; + } + if(s1==nullptr || length1<-1 || s2==nullptr || length2<-1) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + return u_strcmpFold(s1, length1, s2, length2, + options|U_COMPARE_IGNORE_CASE, + pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +u_strcasecmp(const char16_t *s1, const char16_t *s2, uint32_t options) { + UErrorCode errorCode=U_ZERO_ERROR; + return u_strcmpFold(s1, -1, s2, -1, + options|U_COMPARE_IGNORE_CASE, + &errorCode); +} + +U_CAPI int32_t U_EXPORT2 +u_memcasecmp(const char16_t *s1, const char16_t *s2, int32_t length, uint32_t options) { + UErrorCode errorCode=U_ZERO_ERROR; + return u_strcmpFold(s1, length, s2, length, + options|U_COMPARE_IGNORE_CASE, + &errorCode); +} + +U_CAPI int32_t U_EXPORT2 +u_strncasecmp(const char16_t *s1, const char16_t *s2, int32_t n, uint32_t options) { + UErrorCode errorCode=U_ZERO_ERROR; + return u_strcmpFold(s1, n, s2, n, + options|(U_COMPARE_IGNORE_CASE|_STRNCMP_STYLE), + &errorCode); +} + +/* internal API - detect length of shared prefix */ +U_CAPI void +u_caseInsensitivePrefixMatch(const char16_t *s1, int32_t length1, + const char16_t *s2, int32_t length2, + uint32_t options, + int32_t *matchLen1, int32_t *matchLen2, + UErrorCode *pErrorCode) { + _cmpFold(s1, length1, s2, length2, options, + matchLen1, matchLen2, pErrorCode); +} diff --git a/deps/icu-small/source/common/ustrcase_locale.cpp b/deps/icu-small/source/common/ustrcase_locale.cpp index 2ecd24f03ec02e..735be3c0c6cccb 100644 --- a/deps/icu-small/source/common/ustrcase_locale.cpp +++ b/deps/icu-small/source/common/ustrcase_locale.cpp @@ -1,94 +1,94 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: ustrcase_locale.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2011may31 -* created by: Markus W. Scherer -* -* Locale-sensitive case mapping functions (ones that call uloc_getDefault()) -* were moved here to break dependency cycles among parts of the common library. -*/ - -#include "unicode/utypes.h" -#include "uassert.h" -#include "unicode/brkiter.h" -#include "unicode/casemap.h" -#include "unicode/ucasemap.h" -#include "unicode/uloc.h" -#include "unicode/ustring.h" -#include "ucase.h" -#include "ucasemap_imp.h" - -U_CFUNC int32_t -ustrcase_getCaseLocale(const char *locale) { - if (locale == NULL) { - locale = uloc_getDefault(); - } - if (*locale == 0) { - return UCASE_LOC_ROOT; - } else { - return ucase_getCaseLocale(locale); - } -} - -/* public API functions */ - -U_CAPI int32_t U_EXPORT2 -u_strToLower(UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - const char *locale, - UErrorCode *pErrorCode) { - return ustrcase_mapWithOverlap( - ustrcase_getCaseLocale(locale), 0, UCASEMAP_BREAK_ITERATOR_NULL - dest, destCapacity, - src, srcLength, - ustrcase_internalToLower, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -u_strToUpper(UChar *dest, int32_t destCapacity, - const UChar *src, int32_t srcLength, - const char *locale, - UErrorCode *pErrorCode) { - return ustrcase_mapWithOverlap( - ustrcase_getCaseLocale(locale), 0, UCASEMAP_BREAK_ITERATOR_NULL - dest, destCapacity, - src, srcLength, - ustrcase_internalToUpper, *pErrorCode); -} - -U_NAMESPACE_BEGIN - -int32_t CaseMap::toLower( - const char *locale, uint32_t options, - const UChar *src, int32_t srcLength, - UChar *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode) { - return ustrcase_map( - ustrcase_getCaseLocale(locale), options, UCASEMAP_BREAK_ITERATOR_NULL - dest, destCapacity, - src, srcLength, - ustrcase_internalToLower, edits, errorCode); -} - -int32_t CaseMap::toUpper( - const char *locale, uint32_t options, - const UChar *src, int32_t srcLength, - UChar *dest, int32_t destCapacity, Edits *edits, - UErrorCode &errorCode) { - return ustrcase_map( - ustrcase_getCaseLocale(locale), options, UCASEMAP_BREAK_ITERATOR_NULL - dest, destCapacity, - src, srcLength, - ustrcase_internalToUpper, edits, errorCode); -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: ustrcase_locale.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2011may31 +* created by: Markus W. Scherer +* +* Locale-sensitive case mapping functions (ones that call uloc_getDefault()) +* were moved here to break dependency cycles among parts of the common library. +*/ + +#include "unicode/utypes.h" +#include "uassert.h" +#include "unicode/brkiter.h" +#include "unicode/casemap.h" +#include "unicode/ucasemap.h" +#include "unicode/uloc.h" +#include "unicode/ustring.h" +#include "ucase.h" +#include "ucasemap_imp.h" + +U_CFUNC int32_t +ustrcase_getCaseLocale(const char *locale) { + if (locale == nullptr) { + locale = uloc_getDefault(); + } + if (*locale == 0) { + return UCASE_LOC_ROOT; + } else { + return ucase_getCaseLocale(locale); + } +} + +/* public API functions */ + +U_CAPI int32_t U_EXPORT2 +u_strToLower(char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + const char *locale, + UErrorCode *pErrorCode) { + return ustrcase_mapWithOverlap( + ustrcase_getCaseLocale(locale), 0, UCASEMAP_BREAK_ITERATOR_NULL + dest, destCapacity, + src, srcLength, + ustrcase_internalToLower, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +u_strToUpper(char16_t *dest, int32_t destCapacity, + const char16_t *src, int32_t srcLength, + const char *locale, + UErrorCode *pErrorCode) { + return ustrcase_mapWithOverlap( + ustrcase_getCaseLocale(locale), 0, UCASEMAP_BREAK_ITERATOR_NULL + dest, destCapacity, + src, srcLength, + ustrcase_internalToUpper, *pErrorCode); +} + +U_NAMESPACE_BEGIN + +int32_t CaseMap::toLower( + const char *locale, uint32_t options, + const char16_t *src, int32_t srcLength, + char16_t *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode) { + return ustrcase_map( + ustrcase_getCaseLocale(locale), options, UCASEMAP_BREAK_ITERATOR_NULL + dest, destCapacity, + src, srcLength, + ustrcase_internalToLower, edits, errorCode); +} + +int32_t CaseMap::toUpper( + const char *locale, uint32_t options, + const char16_t *src, int32_t srcLength, + char16_t *dest, int32_t destCapacity, Edits *edits, + UErrorCode &errorCode) { + return ustrcase_map( + ustrcase_getCaseLocale(locale), options, UCASEMAP_BREAK_ITERATOR_NULL + dest, destCapacity, + src, srcLength, + ustrcase_internalToUpper, edits, errorCode); +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/ustrenum.cpp b/deps/icu-small/source/common/ustrenum.cpp index 08a1bf29c3a975..bd83161cf9d258 100644 --- a/deps/icu-small/source/common/ustrenum.cpp +++ b/deps/icu-small/source/common/ustrenum.cpp @@ -1,398 +1,398 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: November 11 2002 -* Since: ICU 2.4 -********************************************************************** -*/ -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/ustring.h" -#include "unicode/strenum.h" -#include "unicode/putil.h" -#include "uenumimp.h" -#include "ustrenum.h" -#include "cstring.h" -#include "cmemory.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN -// StringEnumeration implementation ---------------------------------------- *** - -StringEnumeration::StringEnumeration() - : chars(charsBuffer), charsCapacity(sizeof(charsBuffer)) { -} - -StringEnumeration::~StringEnumeration() { - if (chars != NULL && chars != charsBuffer) { - uprv_free(chars); - } -} - -// StringEnumeration base class clone() default implementation, does not clone -StringEnumeration * -StringEnumeration::clone() const { - return NULL; -} - -const char * -StringEnumeration::next(int32_t *resultLength, UErrorCode &status) { - const UnicodeString *s=snext(status); - if(U_SUCCESS(status) && s!=NULL) { - unistr=*s; - ensureCharsCapacity(unistr.length()+1, status); - if(U_SUCCESS(status)) { - if(resultLength!=NULL) { - *resultLength=unistr.length(); - } - unistr.extract(0, INT32_MAX, chars, charsCapacity, US_INV); - return chars; - } - } - - return NULL; -} - -const UChar * -StringEnumeration::unext(int32_t *resultLength, UErrorCode &status) { - const UnicodeString *s=snext(status); - if(U_SUCCESS(status) && s!=NULL) { - unistr=*s; - if(resultLength!=NULL) { - *resultLength=unistr.length(); - } - return unistr.getTerminatedBuffer(); - } - - return NULL; -} - -const UnicodeString * -StringEnumeration::snext(UErrorCode &status) { - int32_t length; - const char *s=next(&length, status); - return setChars(s, length, status); -} - -void -StringEnumeration::ensureCharsCapacity(int32_t capacity, UErrorCode &status) { - if(U_SUCCESS(status) && capacity>charsCapacity) { - if(capacity<(charsCapacity+charsCapacity/2)) { - // avoid allocation thrashing - capacity=charsCapacity+charsCapacity/2; - } - if(chars!=charsBuffer) { - uprv_free(chars); - } - chars=(char *)uprv_malloc(capacity); - if(chars==NULL) { - chars=charsBuffer; - charsCapacity=sizeof(charsBuffer); - status=U_MEMORY_ALLOCATION_ERROR; - } else { - charsCapacity=capacity; - } - } -} - -UnicodeString * -StringEnumeration::setChars(const char *s, int32_t length, UErrorCode &status) { - if(U_SUCCESS(status) && s!=NULL) { - if(length<0) { - length=(int32_t)uprv_strlen(s); - } - - UChar *buffer=unistr.getBuffer(length+1); - if(buffer!=NULL) { - u_charsToUChars(s, buffer, length); - buffer[length]=0; - unistr.releaseBuffer(length); - return &unistr; - } else { - status=U_MEMORY_ALLOCATION_ERROR; - } - } - - return NULL; -} -bool -StringEnumeration::operator==(const StringEnumeration& that)const { - return typeid(*this) == typeid(that); -} - -bool -StringEnumeration::operator!=(const StringEnumeration& that)const { - return !operator==(that); -} - -// UStringEnumeration implementation --------------------------------------- *** - -UStringEnumeration * U_EXPORT2 -UStringEnumeration::fromUEnumeration( - UEnumeration *uenumToAdopt, UErrorCode &status) { - if (U_FAILURE(status)) { - uenum_close(uenumToAdopt); - return NULL; - } - UStringEnumeration *result = new UStringEnumeration(uenumToAdopt); - if (result == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - uenum_close(uenumToAdopt); - return NULL; - } - return result; -} - -UStringEnumeration::UStringEnumeration(UEnumeration* _uenum) : - uenum(_uenum) { - U_ASSERT(_uenum != 0); -} - -UStringEnumeration::~UStringEnumeration() { - uenum_close(uenum); -} - -int32_t UStringEnumeration::count(UErrorCode& status) const { - return uenum_count(uenum, &status); -} - -const char *UStringEnumeration::next(int32_t *resultLength, UErrorCode &status) { - return uenum_next(uenum, resultLength, &status); -} - -const UnicodeString* UStringEnumeration::snext(UErrorCode& status) { - int32_t length; - const UChar* str = uenum_unext(uenum, &length, &status); - if (str == 0 || U_FAILURE(status)) { - return 0; - } - return &unistr.setTo(str, length); -} - -void UStringEnumeration::reset(UErrorCode& status) { - uenum_reset(uenum, &status); -} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UStringEnumeration) -U_NAMESPACE_END - -// C wrapper --------------------------------------------------------------- *** - -#define THIS(en) ((icu::StringEnumeration*)(en->context)) - -U_CDECL_BEGIN - -/** - * Wrapper API to make StringEnumeration look like UEnumeration. - */ -static void U_CALLCONV -ustrenum_close(UEnumeration* en) { - delete THIS(en); - uprv_free(en); -} - -/** - * Wrapper API to make StringEnumeration look like UEnumeration. - */ -static int32_t U_CALLCONV -ustrenum_count(UEnumeration* en, - UErrorCode* ec) -{ - return THIS(en)->count(*ec); -} - -/** - * Wrapper API to make StringEnumeration look like UEnumeration. - */ -static const UChar* U_CALLCONV -ustrenum_unext(UEnumeration* en, - int32_t* resultLength, - UErrorCode* ec) -{ - return THIS(en)->unext(resultLength, *ec); -} - -/** - * Wrapper API to make StringEnumeration look like UEnumeration. - */ -static const char* U_CALLCONV -ustrenum_next(UEnumeration* en, - int32_t* resultLength, - UErrorCode* ec) -{ - return THIS(en)->next(resultLength, *ec); -} - -/** - * Wrapper API to make StringEnumeration look like UEnumeration. - */ -static void U_CALLCONV -ustrenum_reset(UEnumeration* en, - UErrorCode* ec) -{ - THIS(en)->reset(*ec); -} - -/** - * Pseudo-vtable for UEnumeration wrapper around StringEnumeration. - * The StringEnumeration pointer will be stored in 'context'. - */ -static const UEnumeration USTRENUM_VT = { - NULL, - NULL, // store StringEnumeration pointer here - ustrenum_close, - ustrenum_count, - ustrenum_unext, - ustrenum_next, - ustrenum_reset -}; - -U_CDECL_END - -/** - * Given a StringEnumeration, wrap it in a UEnumeration. The - * StringEnumeration is adopted; after this call, the caller must not - * delete it (regardless of error status). - */ -U_CAPI UEnumeration* U_EXPORT2 -uenum_openFromStringEnumeration(icu::StringEnumeration* adopted, UErrorCode* ec) { - UEnumeration* result = NULL; - if (U_SUCCESS(*ec) && adopted != NULL) { - result = (UEnumeration*) uprv_malloc(sizeof(UEnumeration)); - if (result == NULL) { - *ec = U_MEMORY_ALLOCATION_ERROR; - } else { - uprv_memcpy(result, &USTRENUM_VT, sizeof(USTRENUM_VT)); - result->context = adopted; - } - } - if (result == NULL) { - delete adopted; - } - return result; -} - -// C wrapper --------------------------------------------------------------- *** - -U_CDECL_BEGIN - -typedef struct UCharStringEnumeration { - UEnumeration uenum; - int32_t index, count; -} UCharStringEnumeration; - -static void U_CALLCONV -ucharstrenum_close(UEnumeration* en) { - uprv_free(en); -} - -static int32_t U_CALLCONV -ucharstrenum_count(UEnumeration* en, - UErrorCode* /*ec*/) { - return ((UCharStringEnumeration*)en)->count; -} - -static const UChar* U_CALLCONV -ucharstrenum_unext(UEnumeration* en, - int32_t* resultLength, - UErrorCode* /*ec*/) { - UCharStringEnumeration *e = (UCharStringEnumeration*) en; - if (e->index >= e->count) { - return NULL; - } - const UChar* result = ((const UChar**)e->uenum.context)[e->index++]; - if (resultLength) { - *resultLength = (int32_t)u_strlen(result); - } - return result; -} - - -static const char* U_CALLCONV -ucharstrenum_next(UEnumeration* en, - int32_t* resultLength, - UErrorCode* /*ec*/) { - UCharStringEnumeration *e = (UCharStringEnumeration*) en; - if (e->index >= e->count) { - return NULL; - } - const char* result = ((const char**)e->uenum.context)[e->index++]; - if (resultLength) { - *resultLength = (int32_t)uprv_strlen(result); - } - return result; -} - -static void U_CALLCONV -ucharstrenum_reset(UEnumeration* en, - UErrorCode* /*ec*/) { - ((UCharStringEnumeration*)en)->index = 0; -} - -static const UEnumeration UCHARSTRENUM_VT = { - NULL, - NULL, // store StringEnumeration pointer here - ucharstrenum_close, - ucharstrenum_count, - uenum_unextDefault, - ucharstrenum_next, - ucharstrenum_reset -}; - -static const UEnumeration UCHARSTRENUM_U_VT = { - NULL, - NULL, // store StringEnumeration pointer here - ucharstrenum_close, - ucharstrenum_count, - ucharstrenum_unext, - uenum_nextDefault, - ucharstrenum_reset -}; - -U_CDECL_END - -U_CAPI UEnumeration* U_EXPORT2 -uenum_openCharStringsEnumeration(const char* const strings[], int32_t count, - UErrorCode* ec) { - UCharStringEnumeration* result = NULL; - if (U_SUCCESS(*ec) && count >= 0 && (count == 0 || strings != 0)) { - result = (UCharStringEnumeration*) uprv_malloc(sizeof(UCharStringEnumeration)); - if (result == NULL) { - *ec = U_MEMORY_ALLOCATION_ERROR; - } else { - U_ASSERT((char*)result==(char*)(&result->uenum)); - uprv_memcpy(result, &UCHARSTRENUM_VT, sizeof(UCHARSTRENUM_VT)); - result->uenum.context = (void*)strings; - result->index = 0; - result->count = count; - } - } - return (UEnumeration*) result; -} - -U_CAPI UEnumeration* U_EXPORT2 -uenum_openUCharStringsEnumeration(const UChar* const strings[], int32_t count, - UErrorCode* ec) { - UCharStringEnumeration* result = NULL; - if (U_SUCCESS(*ec) && count >= 0 && (count == 0 || strings != 0)) { - result = (UCharStringEnumeration*) uprv_malloc(sizeof(UCharStringEnumeration)); - if (result == NULL) { - *ec = U_MEMORY_ALLOCATION_ERROR; - } else { - U_ASSERT((char*)result==(char*)(&result->uenum)); - uprv_memcpy(result, &UCHARSTRENUM_U_VT, sizeof(UCHARSTRENUM_U_VT)); - result->uenum.context = (void*)strings; - result->index = 0; - result->count = count; - } - } - return (UEnumeration*) result; -} - - -// end C Wrapper +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: November 11 2002 +* Since: ICU 2.4 +********************************************************************** +*/ +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/ustring.h" +#include "unicode/strenum.h" +#include "unicode/putil.h" +#include "uenumimp.h" +#include "ustrenum.h" +#include "cstring.h" +#include "cmemory.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN +// StringEnumeration implementation ---------------------------------------- *** + +StringEnumeration::StringEnumeration() + : chars(charsBuffer), charsCapacity(sizeof(charsBuffer)) { +} + +StringEnumeration::~StringEnumeration() { + if (chars != nullptr && chars != charsBuffer) { + uprv_free(chars); + } +} + +// StringEnumeration base class clone() default implementation, does not clone +StringEnumeration * +StringEnumeration::clone() const { + return nullptr; +} + +const char * +StringEnumeration::next(int32_t *resultLength, UErrorCode &status) { + const UnicodeString *s=snext(status); + if(U_SUCCESS(status) && s!=nullptr) { + unistr=*s; + ensureCharsCapacity(unistr.length()+1, status); + if(U_SUCCESS(status)) { + if(resultLength!=nullptr) { + *resultLength=unistr.length(); + } + unistr.extract(0, INT32_MAX, chars, charsCapacity, US_INV); + return chars; + } + } + + return nullptr; +} + +const char16_t * +StringEnumeration::unext(int32_t *resultLength, UErrorCode &status) { + const UnicodeString *s=snext(status); + if(U_SUCCESS(status) && s!=nullptr) { + unistr=*s; + if(resultLength!=nullptr) { + *resultLength=unistr.length(); + } + return unistr.getTerminatedBuffer(); + } + + return nullptr; +} + +const UnicodeString * +StringEnumeration::snext(UErrorCode &status) { + int32_t length; + const char *s=next(&length, status); + return setChars(s, length, status); +} + +void +StringEnumeration::ensureCharsCapacity(int32_t capacity, UErrorCode &status) { + if(U_SUCCESS(status) && capacity>charsCapacity) { + if(capacity<(charsCapacity+charsCapacity/2)) { + // avoid allocation thrashing + capacity=charsCapacity+charsCapacity/2; + } + if(chars!=charsBuffer) { + uprv_free(chars); + } + chars=(char *)uprv_malloc(capacity); + if(chars==nullptr) { + chars=charsBuffer; + charsCapacity=sizeof(charsBuffer); + status=U_MEMORY_ALLOCATION_ERROR; + } else { + charsCapacity=capacity; + } + } +} + +UnicodeString * +StringEnumeration::setChars(const char *s, int32_t length, UErrorCode &status) { + if(U_SUCCESS(status) && s!=nullptr) { + if(length<0) { + length=(int32_t)uprv_strlen(s); + } + + char16_t *buffer=unistr.getBuffer(length+1); + if(buffer!=nullptr) { + u_charsToUChars(s, buffer, length); + buffer[length]=0; + unistr.releaseBuffer(length); + return &unistr; + } else { + status=U_MEMORY_ALLOCATION_ERROR; + } + } + + return nullptr; +} +bool +StringEnumeration::operator==(const StringEnumeration& that)const { + return typeid(*this) == typeid(that); +} + +bool +StringEnumeration::operator!=(const StringEnumeration& that)const { + return !operator==(that); +} + +// UStringEnumeration implementation --------------------------------------- *** + +UStringEnumeration * U_EXPORT2 +UStringEnumeration::fromUEnumeration( + UEnumeration *uenumToAdopt, UErrorCode &status) { + if (U_FAILURE(status)) { + uenum_close(uenumToAdopt); + return nullptr; + } + UStringEnumeration *result = new UStringEnumeration(uenumToAdopt); + if (result == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + uenum_close(uenumToAdopt); + return nullptr; + } + return result; +} + +UStringEnumeration::UStringEnumeration(UEnumeration* _uenum) : + uenum(_uenum) { + U_ASSERT(_uenum != 0); +} + +UStringEnumeration::~UStringEnumeration() { + uenum_close(uenum); +} + +int32_t UStringEnumeration::count(UErrorCode& status) const { + return uenum_count(uenum, &status); +} + +const char *UStringEnumeration::next(int32_t *resultLength, UErrorCode &status) { + return uenum_next(uenum, resultLength, &status); +} + +const UnicodeString* UStringEnumeration::snext(UErrorCode& status) { + int32_t length; + const char16_t* str = uenum_unext(uenum, &length, &status); + if (str == 0 || U_FAILURE(status)) { + return 0; + } + return &unistr.setTo(str, length); +} + +void UStringEnumeration::reset(UErrorCode& status) { + uenum_reset(uenum, &status); +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UStringEnumeration) +U_NAMESPACE_END + +// C wrapper --------------------------------------------------------------- *** + +#define THIS(en) ((icu::StringEnumeration*)(en->context)) + +U_CDECL_BEGIN + +/** + * Wrapper API to make StringEnumeration look like UEnumeration. + */ +static void U_CALLCONV +ustrenum_close(UEnumeration* en) { + delete THIS(en); + uprv_free(en); +} + +/** + * Wrapper API to make StringEnumeration look like UEnumeration. + */ +static int32_t U_CALLCONV +ustrenum_count(UEnumeration* en, + UErrorCode* ec) +{ + return THIS(en)->count(*ec); +} + +/** + * Wrapper API to make StringEnumeration look like UEnumeration. + */ +static const char16_t* U_CALLCONV +ustrenum_unext(UEnumeration* en, + int32_t* resultLength, + UErrorCode* ec) +{ + return THIS(en)->unext(resultLength, *ec); +} + +/** + * Wrapper API to make StringEnumeration look like UEnumeration. + */ +static const char* U_CALLCONV +ustrenum_next(UEnumeration* en, + int32_t* resultLength, + UErrorCode* ec) +{ + return THIS(en)->next(resultLength, *ec); +} + +/** + * Wrapper API to make StringEnumeration look like UEnumeration. + */ +static void U_CALLCONV +ustrenum_reset(UEnumeration* en, + UErrorCode* ec) +{ + THIS(en)->reset(*ec); +} + +/** + * Pseudo-vtable for UEnumeration wrapper around StringEnumeration. + * The StringEnumeration pointer will be stored in 'context'. + */ +static const UEnumeration USTRENUM_VT = { + nullptr, + nullptr, // store StringEnumeration pointer here + ustrenum_close, + ustrenum_count, + ustrenum_unext, + ustrenum_next, + ustrenum_reset +}; + +U_CDECL_END + +/** + * Given a StringEnumeration, wrap it in a UEnumeration. The + * StringEnumeration is adopted; after this call, the caller must not + * delete it (regardless of error status). + */ +U_CAPI UEnumeration* U_EXPORT2 +uenum_openFromStringEnumeration(icu::StringEnumeration* adopted, UErrorCode* ec) { + UEnumeration* result = nullptr; + if (U_SUCCESS(*ec) && adopted != nullptr) { + result = (UEnumeration*) uprv_malloc(sizeof(UEnumeration)); + if (result == nullptr) { + *ec = U_MEMORY_ALLOCATION_ERROR; + } else { + uprv_memcpy(result, &USTRENUM_VT, sizeof(USTRENUM_VT)); + result->context = adopted; + } + } + if (result == nullptr) { + delete adopted; + } + return result; +} + +// C wrapper --------------------------------------------------------------- *** + +U_CDECL_BEGIN + +typedef struct UCharStringEnumeration { + UEnumeration uenum; + int32_t index, count; +} UCharStringEnumeration; + +static void U_CALLCONV +ucharstrenum_close(UEnumeration* en) { + uprv_free(en); +} + +static int32_t U_CALLCONV +ucharstrenum_count(UEnumeration* en, + UErrorCode* /*ec*/) { + return ((UCharStringEnumeration*)en)->count; +} + +static const char16_t* U_CALLCONV +ucharstrenum_unext(UEnumeration* en, + int32_t* resultLength, + UErrorCode* /*ec*/) { + UCharStringEnumeration *e = (UCharStringEnumeration*) en; + if (e->index >= e->count) { + return nullptr; + } + const char16_t* result = ((const char16_t**)e->uenum.context)[e->index++]; + if (resultLength) { + *resultLength = (int32_t)u_strlen(result); + } + return result; +} + + +static const char* U_CALLCONV +ucharstrenum_next(UEnumeration* en, + int32_t* resultLength, + UErrorCode* /*ec*/) { + UCharStringEnumeration *e = (UCharStringEnumeration*) en; + if (e->index >= e->count) { + return nullptr; + } + const char* result = ((const char**)e->uenum.context)[e->index++]; + if (resultLength) { + *resultLength = (int32_t)uprv_strlen(result); + } + return result; +} + +static void U_CALLCONV +ucharstrenum_reset(UEnumeration* en, + UErrorCode* /*ec*/) { + ((UCharStringEnumeration*)en)->index = 0; +} + +static const UEnumeration UCHARSTRENUM_VT = { + nullptr, + nullptr, // store StringEnumeration pointer here + ucharstrenum_close, + ucharstrenum_count, + uenum_unextDefault, + ucharstrenum_next, + ucharstrenum_reset +}; + +static const UEnumeration UCHARSTRENUM_U_VT = { + nullptr, + nullptr, // store StringEnumeration pointer here + ucharstrenum_close, + ucharstrenum_count, + ucharstrenum_unext, + uenum_nextDefault, + ucharstrenum_reset +}; + +U_CDECL_END + +U_CAPI UEnumeration* U_EXPORT2 +uenum_openCharStringsEnumeration(const char* const strings[], int32_t count, + UErrorCode* ec) { + UCharStringEnumeration* result = nullptr; + if (U_SUCCESS(*ec) && count >= 0 && (count == 0 || strings != 0)) { + result = (UCharStringEnumeration*) uprv_malloc(sizeof(UCharStringEnumeration)); + if (result == nullptr) { + *ec = U_MEMORY_ALLOCATION_ERROR; + } else { + U_ASSERT((char*)result==(char*)(&result->uenum)); + uprv_memcpy(result, &UCHARSTRENUM_VT, sizeof(UCHARSTRENUM_VT)); + result->uenum.context = (void*)strings; + result->index = 0; + result->count = count; + } + } + return (UEnumeration*) result; +} + +U_CAPI UEnumeration* U_EXPORT2 +uenum_openUCharStringsEnumeration(const char16_t* const strings[], int32_t count, + UErrorCode* ec) { + UCharStringEnumeration* result = nullptr; + if (U_SUCCESS(*ec) && count >= 0 && (count == 0 || strings != 0)) { + result = (UCharStringEnumeration*) uprv_malloc(sizeof(UCharStringEnumeration)); + if (result == nullptr) { + *ec = U_MEMORY_ALLOCATION_ERROR; + } else { + U_ASSERT((char*)result==(char*)(&result->uenum)); + uprv_memcpy(result, &UCHARSTRENUM_U_VT, sizeof(UCHARSTRENUM_U_VT)); + result->uenum.context = (void*)strings; + result->index = 0; + result->count = count; + } + } + return (UEnumeration*) result; +} + + +// end C Wrapper diff --git a/deps/icu-small/source/common/ustrenum.h b/deps/icu-small/source/common/ustrenum.h index 3703dedb97db12..5aa9b32f1e1b70 100644 --- a/deps/icu-small/source/common/ustrenum.h +++ b/deps/icu-small/source/common/ustrenum.h @@ -1,87 +1,87 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: November 11 2002 -* Since: ICU 2.4 -********************************************************************** -*/ -#ifndef _USTRENUM_H_ -#define _USTRENUM_H_ - -#include "unicode/uenum.h" -#include "unicode/strenum.h" - -//---------------------------------------------------------------------- -U_NAMESPACE_BEGIN - -/** - * A wrapper to make a UEnumeration into a StringEnumeration. The - * wrapper adopts the UEnumeration is wraps. - */ -class U_COMMON_API UStringEnumeration : public StringEnumeration { - -public: - /** - * Constructor. This constructor adopts its UEnumeration - * argument. - * @param uenum a UEnumeration object. This object takes - * ownership of 'uenum' and will close it in its destructor. The - * caller must not call uenum_close on 'uenum' after calling this - * constructor. - */ - UStringEnumeration(UEnumeration* uenum); - - /** - * Destructor. This closes the UEnumeration passed in to the - * constructor. - */ - virtual ~UStringEnumeration(); - - /** - * Return the number of elements that the iterator traverses. - * @param status the error code. - * @return number of elements in the iterator. - */ - virtual int32_t count(UErrorCode& status) const override; - - virtual const char* next(int32_t *resultLength, UErrorCode& status) override; - - /** - * Returns the next element a UnicodeString*. If there are no - * more elements, returns NULL. - * @param status the error code. - * @return a pointer to the string, or NULL. - */ - virtual const UnicodeString* snext(UErrorCode& status) override; - - /** - * Resets the iterator. - * @param status the error code. - */ - virtual void reset(UErrorCode& status) override; - - /** - * ICU4C "poor man's RTTI", returns a UClassID for the actual ICU class. - */ - virtual UClassID getDynamicClassID() const override; - - /** - * ICU4C "poor man's RTTI", returns a UClassID for this ICU class. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - static UStringEnumeration * U_EXPORT2 fromUEnumeration( - UEnumeration *enumToAdopt, UErrorCode &status); -private: - UEnumeration *uenum; // owned -}; - -U_NAMESPACE_END - -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: November 11 2002 +* Since: ICU 2.4 +********************************************************************** +*/ +#ifndef _USTRENUM_H_ +#define _USTRENUM_H_ + +#include "unicode/uenum.h" +#include "unicode/strenum.h" + +//---------------------------------------------------------------------- +U_NAMESPACE_BEGIN + +/** + * A wrapper to make a UEnumeration into a StringEnumeration. The + * wrapper adopts the UEnumeration is wraps. + */ +class U_COMMON_API UStringEnumeration : public StringEnumeration { + +public: + /** + * Constructor. This constructor adopts its UEnumeration + * argument. + * @param uenum a UEnumeration object. This object takes + * ownership of 'uenum' and will close it in its destructor. The + * caller must not call uenum_close on 'uenum' after calling this + * constructor. + */ + UStringEnumeration(UEnumeration* uenum); + + /** + * Destructor. This closes the UEnumeration passed in to the + * constructor. + */ + virtual ~UStringEnumeration(); + + /** + * Return the number of elements that the iterator traverses. + * @param status the error code. + * @return number of elements in the iterator. + */ + virtual int32_t count(UErrorCode& status) const override; + + virtual const char* next(int32_t *resultLength, UErrorCode& status) override; + + /** + * Returns the next element a UnicodeString*. If there are no + * more elements, returns nullptr. + * @param status the error code. + * @return a pointer to the string, or nullptr. + */ + virtual const UnicodeString* snext(UErrorCode& status) override; + + /** + * Resets the iterator. + * @param status the error code. + */ + virtual void reset(UErrorCode& status) override; + + /** + * ICU4C "poor man's RTTI", returns a UClassID for the actual ICU class. + */ + virtual UClassID getDynamicClassID() const override; + + /** + * ICU4C "poor man's RTTI", returns a UClassID for this ICU class. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + static UStringEnumeration * U_EXPORT2 fromUEnumeration( + UEnumeration *enumToAdopt, UErrorCode &status); +private: + UEnumeration *uenum; // owned +}; + +U_NAMESPACE_END + +#endif + diff --git a/deps/icu-small/source/common/ustrfmt.cpp b/deps/icu-small/source/common/ustrfmt.cpp index 1a9b15a59febce..9d14945cbd34c5 100644 --- a/deps/icu-small/source/common/ustrfmt.cpp +++ b/deps/icu-small/source/common/ustrfmt.cpp @@ -1,59 +1,59 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2001-2006, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -#include "cstring.h" -#include "ustrfmt.h" - - -/*** - * Fills in a UChar* string with the radix-based representation of a - * uint32_t number padded with zeroes to minwidth. The result - * will be null terminated if there is room. - * - * @param buffer UChar buffer to receive result - * @param capacity capacity of buffer - * @param i the unsigned number to be formatted - * @param radix the radix from 2..36 - * @param minwidth the minimum width. If the result is narrower than - * this, '0's will be added on the left. Must be <= - * capacity. - * @return the length of the result, not including any terminating - * null - */ -U_CAPI int32_t U_EXPORT2 -uprv_itou (UChar * buffer, int32_t capacity, - uint32_t i, uint32_t radix, int32_t minwidth) -{ - int32_t length = 0; - int digit; - int32_t j; - UChar temp; - - do{ - digit = (int)(i % radix); - buffer[length++]=(UChar)(digit<=9?(0x0030+digit):(0x0030+digit+7)); - i=i/radix; - } while(i && length= 0) { - return (UChar *)string + idx; - } else { - return NULL; - } -} - -/* Search for a codepoint in a string that matches one of the matchSet codepoints. */ -U_CAPI int32_t U_EXPORT2 -u_strcspn(const UChar *string, const UChar *matchSet) -{ - int32_t idx = _matchFromSet(string, matchSet, true); - if(idx >= 0) { - return idx; - } else { - return -idx - 1; /* == u_strlen(string) */ - } -} - -/* Search for a codepoint in a string that does not match one of the matchSet codepoints. */ -U_CAPI int32_t U_EXPORT2 -u_strspn(const UChar *string, const UChar *matchSet) -{ - int32_t idx = _matchFromSet(string, matchSet, false); - if(idx >= 0) { - return idx; - } else { - return -idx - 1; /* == u_strlen(string) */ - } -} - -/* ----- Text manipulation functions --- */ - -U_CAPI UChar* U_EXPORT2 -u_strtok_r(UChar *src, - const UChar *delim, - UChar **saveState) -{ - UChar *tokSource; - UChar *nextToken; - uint32_t nonDelimIdx; - - /* If saveState is NULL, the user messed up. */ - if (src != NULL) { - tokSource = src; - *saveState = src; /* Set to "src" in case there are no delimiters */ - } - else if (*saveState) { - tokSource = *saveState; - } - else { - /* src == NULL && *saveState == NULL */ - /* This shouldn't happen. We already finished tokenizing. */ - return NULL; - } - - /* Skip initial delimiters */ - nonDelimIdx = u_strspn(tokSource, delim); - tokSource = &tokSource[nonDelimIdx]; - - if (*tokSource) { - nextToken = u_strpbrk(tokSource, delim); - if (nextToken != NULL) { - /* Create a token */ - *(nextToken++) = 0; - *saveState = nextToken; - return tokSource; - } - else if (*saveState) { - /* Return the last token */ - *saveState = NULL; - return tokSource; - } - } - else { - /* No tokens were found. Only delimiters were left. */ - *saveState = NULL; - } - return NULL; -} - -/* Miscellaneous functions -------------------------------------------------- */ - -U_CAPI UChar* U_EXPORT2 -u_strcat(UChar *dst, - const UChar *src) -{ - UChar *anchor = dst; /* save a pointer to start of dst */ - - while(*dst != 0) { /* To end of first string */ - ++dst; - } - while((*(dst++) = *(src++)) != 0) { /* copy string 2 over */ - } - - return anchor; -} - -U_CAPI UChar* U_EXPORT2 -u_strncat(UChar *dst, - const UChar *src, - int32_t n ) -{ - if(n > 0) { - UChar *anchor = dst; /* save a pointer to start of dst */ - - while(*dst != 0) { /* To end of first string */ - ++dst; - } - while((*dst = *src) != 0) { /* copy string 2 over */ - ++dst; - if(--n == 0) { - *dst = 0; - break; - } - ++src; - } - - return anchor; - } else { - return dst; - } -} - -/* ----- Text property functions --- */ - -U_CAPI int32_t U_EXPORT2 -u_strcmp(const UChar *s1, - const UChar *s2) -{ - UChar c1, c2; - - for(;;) { - c1=*s1++; - c2=*s2++; - if (c1 != c2 || c1 == 0) { - break; - } - } - return (int32_t)c1 - (int32_t)c2; -} - -U_CFUNC int32_t U_EXPORT2 -uprv_strCompare(const UChar *s1, int32_t length1, - const UChar *s2, int32_t length2, - UBool strncmpStyle, UBool codePointOrder) { - const UChar *start1, *start2, *limit1, *limit2; - UChar c1, c2; - - /* setup for fix-up */ - start1=s1; - start2=s2; - - /* compare identical prefixes - they do not need to be fixed up */ - if(length1<0 && length2<0) { - /* strcmp style, both NUL-terminated */ - if(s1==s2) { - return 0; - } - - for(;;) { - c1=*s1; - c2=*s2; - if(c1!=c2) { - break; - } - if(c1==0) { - return 0; - } - ++s1; - ++s2; - } - - /* setup for fix-up */ - limit1=limit2=NULL; - } else if(strncmpStyle) { - /* special handling for strncmp, assume length1==length2>=0 but also check for NUL */ - if(s1==s2) { - return 0; - } - - limit1=start1+length1; - - for(;;) { - /* both lengths are same, check only one limit */ - if(s1==limit1) { - return 0; - } - - c1=*s1; - c2=*s2; - if(c1!=c2) { - break; - } - if(c1==0) { - return 0; - } - ++s1; - ++s2; - } - - /* setup for fix-up */ - limit2=start2+length1; /* use length1 here, too, to enforce assumption */ - } else { - /* memcmp/UnicodeString style, both length-specified */ - int32_t lengthResult; - - if(length1<0) { - length1=u_strlen(s1); - } - if(length2<0) { - length2=u_strlen(s2); - } - - /* limit1=start1+min(length1, length2) */ - if(length1length2 */ { - lengthResult=1; - limit1=start1+length2; - } - - if(s1==s2) { - return lengthResult; - } - - for(;;) { - /* check pseudo-limit */ - if(s1==limit1) { - return lengthResult; - } - - c1=*s1; - c2=*s2; - if(c1!=c2) { - break; - } - ++s1; - ++s2; - } - - /* setup for fix-up */ - limit1=start1+length1; - limit2=start2+length2; - } - - /* if both values are in or above the surrogate range, fix them up */ - if(c1>=0xd800 && c2>=0xd800 && codePointOrder) { - /* subtract 0x2800 from BMP code points to make them smaller than supplementary ones */ - if( - (c1<=0xdbff && (s1+1)!=limit1 && U16_IS_TRAIL(*(s1+1))) || - (U16_IS_TRAIL(c1) && start1!=s1 && U16_IS_LEAD(*(s1-1))) - ) { - /* part of a surrogate pair, leave >=d800 */ - } else { - /* BMP code point - may be surrogate code point - make =d800 */ - } else { - /* BMP code point - may be surrogate code point - make move(iter1, 0, UITER_START); - iter2->move(iter2, 0, UITER_START); - - /* compare identical prefixes - they do not need to be fixed up */ - for(;;) { - c1=iter1->next(iter1); - c2=iter2->next(iter2); - if(c1!=c2) { - break; - } - if(c1==-1) { - return 0; - } - } - - /* if both values are in or above the surrogate range, fix them up */ - if(c1>=0xd800 && c2>=0xd800 && codePointOrder) { - /* subtract 0x2800 from BMP code points to make them smaller than supplementary ones */ - if( - (c1<=0xdbff && U16_IS_TRAIL(iter1->current(iter1))) || - (U16_IS_TRAIL(c1) && (iter1->previous(iter1), U16_IS_LEAD(iter1->previous(iter1)))) - ) { - /* part of a surrogate pair, leave >=d800 */ - } else { - /* BMP code point - may be surrogate code point - make current(iter2))) || - (U16_IS_TRAIL(c2) && (iter2->previous(iter2), U16_IS_LEAD(iter2->previous(iter2)))) - ) { - /* part of a surrogate pair, leave >=d800 */ - } else { - /* BMP code point - may be surrogate code point - make =0. - * - * Consistently leaving them _behind_ the different units is not an option - * because the current "unit" is the end of the string if that is reached, - * and in such a case the iterator does not move. - * For example, when comparing "ab" with "abc", both iterators rest _on_ the end - * of their strings. Calling previous() on each does not move them to where - * the comparison fails. - * - * So the simplest semantics is to not define where the iterators end up. - * - * The following fragment is part of what would need to be done for backing up. - */ -void fragment { - /* iff a surrogate is part of a surrogate pair, leave >=d800 */ - if(c1<=0xdbff) { - if(!U16_IS_TRAIL(iter1->current(iter1))) { - /* lead surrogate code point - make getIndex(iter1, UITER_CURRENT); - iter1->previous(iter1); /* ==c1 */ - if(!U16_IS_LEAD(iter1->previous(iter1))) { - /* trail surrogate code point - make move(iter1, idx, UITER_ZERO); - } else /* 0xe000<=c1<=0xffff */ { - /* BMP code point - make 0) { - int32_t rc; - for(;;) { - rc = (int32_t)*s1 - (int32_t)*s2; - if(rc != 0 || *s1 == 0 || --n == 0) { - return rc; - } - ++s1; - ++s2; - } - } else { - return 0; - } -} - -U_CAPI int32_t U_EXPORT2 -u_strncmpCodePointOrder(const UChar *s1, const UChar *s2, int32_t n) { - return uprv_strCompare(s1, n, s2, n, true, true); -} - -U_CAPI UChar* U_EXPORT2 -u_strcpy(UChar *dst, - const UChar *src) -{ - UChar *anchor = dst; /* save a pointer to start of dst */ - - while((*(dst++) = *(src++)) != 0) { /* copy string 2 over */ - } - - return anchor; -} - -U_CAPI UChar* U_EXPORT2 -u_strncpy(UChar *dst, - const UChar *src, - int32_t n) -{ - UChar *anchor = dst; /* save a pointer to start of dst */ - - /* copy string 2 over */ - while(n > 0 && (*(dst++) = *(src++)) != 0) { - --n; - } - - return anchor; -} - -U_CAPI int32_t U_EXPORT2 -u_strlen(const UChar *s) -{ -#if U_SIZEOF_WCHAR_T == U_SIZEOF_UCHAR - return (int32_t)uprv_wcslen((const wchar_t *)s); -#else - const UChar *t = s; - while(*t != 0) { - ++t; - } - return t - s; -#endif -} - -U_CAPI int32_t U_EXPORT2 -u_countChar32(const UChar *s, int32_t length) { - int32_t count; - - if(s==NULL || length<-1) { - return 0; - } - - count=0; - if(length>=0) { - while(length>0) { - ++count; - if(U16_IS_LEAD(*s) && length>=2 && U16_IS_TRAIL(*(s+1))) { - s+=2; - length-=2; - } else { - ++s; - --length; - } - } - } else /* length==-1 */ { - UChar c; - - for(;;) { - if((c=*s++)==0) { - break; - } - ++count; - - /* - * sufficient to look ahead one because of UTF-16; - * safe to look ahead one because at worst that would be the terminating NUL - */ - if(U16_IS_LEAD(c) && U16_IS_TRAIL(*s)) { - ++s; - } - } - } - return count; -} - -U_CAPI UBool U_EXPORT2 -u_strHasMoreChar32Than(const UChar *s, int32_t length, int32_t number) { - - if(number<0) { - return true; - } - if(s==NULL || length<-1) { - return false; - } - - if(length==-1) { - /* s is NUL-terminated */ - UChar c; - - /* count code points until they exceed */ - for(;;) { - if((c=*s++)==0) { - return false; - } - if(number==0) { - return true; - } - if(U16_IS_LEAD(c) && U16_IS_TRAIL(*s)) { - ++s; - } - --number; - } - } else { - /* length>=0 known */ - const UChar *limit; - int32_t maxSupplementary; - - /* s contains at least (length+1)/2 code points: <=2 UChars per cp */ - if(((length+1)/2)>number) { - return true; - } - - /* check if s does not even contain enough UChars */ - maxSupplementary=length-number; - if(maxSupplementary<=0) { - return false; - } - /* there are maxSupplementary=length-number more UChars than asked-for code points */ - - /* - * count code points until they exceed and also check that there are - * no more than maxSupplementary supplementary code points (UChar pairs) - */ - limit=s+length; - for(;;) { - if(s==limit) { - return false; - } - if(number==0) { - return true; - } - if(U16_IS_LEAD(*s++) && s!=limit && U16_IS_TRAIL(*s)) { - ++s; - if(--maxSupplementary<=0) { - /* too many pairs - too few code points */ - return false; - } - } - --number; - } - } -} - -U_CAPI UChar * U_EXPORT2 -u_memcpy(UChar *dest, const UChar *src, int32_t count) { - if(count > 0) { - uprv_memcpy(dest, src, (size_t)count*U_SIZEOF_UCHAR); - } - return dest; -} - -U_CAPI UChar * U_EXPORT2 -u_memmove(UChar *dest, const UChar *src, int32_t count) { - if(count > 0) { - uprv_memmove(dest, src, (size_t)count*U_SIZEOF_UCHAR); - } - return dest; -} - -U_CAPI UChar * U_EXPORT2 -u_memset(UChar *dest, UChar c, int32_t count) { - if(count > 0) { - UChar *ptr = dest; - UChar *limit = dest + count; - - while (ptr < limit) { - *(ptr++) = c; - } - } - return dest; -} - -U_CAPI int32_t U_EXPORT2 -u_memcmp(const UChar *buf1, const UChar *buf2, int32_t count) { - if(count > 0) { - const UChar *limit = buf1 + count; - int32_t result; - - while (buf1 < limit) { - result = (int32_t)(uint16_t)*buf1 - (int32_t)(uint16_t)*buf2; - if (result != 0) { - return result; - } - buf1++; - buf2++; - } - } - return 0; -} - -U_CAPI int32_t U_EXPORT2 -u_memcmpCodePointOrder(const UChar *s1, const UChar *s2, int32_t count) { - return uprv_strCompare(s1, count, s2, count, false, true); -} - -/* u_unescape & support fns ------------------------------------------------- */ - -/* This map must be in ASCENDING ORDER OF THE ESCAPE CODE */ -static const UChar UNESCAPE_MAP[] = { - /*" 0x22, 0x22 */ - /*' 0x27, 0x27 */ - /*? 0x3F, 0x3F */ - /*\ 0x5C, 0x5C */ - /*a*/ 0x61, 0x07, - /*b*/ 0x62, 0x08, - /*e*/ 0x65, 0x1b, - /*f*/ 0x66, 0x0c, - /*n*/ 0x6E, 0x0a, - /*r*/ 0x72, 0x0d, - /*t*/ 0x74, 0x09, - /*v*/ 0x76, 0x0b -}; -enum { UNESCAPE_MAP_LENGTH = UPRV_LENGTHOF(UNESCAPE_MAP) }; - -/* Convert one octal digit to a numeric value 0..7, or -1 on failure */ -static int32_t _digit8(UChar c) { - if (c >= u'0' && c <= u'7') { - return c - u'0'; - } - return -1; -} - -/* Convert one hex digit to a numeric value 0..F, or -1 on failure */ -static int32_t _digit16(UChar c) { - if (c >= u'0' && c <= u'9') { - return c - u'0'; - } - if (c >= u'A' && c <= u'F') { - return c - (u'A' - 10); - } - if (c >= u'a' && c <= u'f') { - return c - (u'a' - 10); - } - return -1; -} - -/* Parse a single escape sequence. Although this method deals in - * UChars, it does not use C++ or UnicodeString. This allows it to - * be used from C contexts. */ -U_CAPI UChar32 U_EXPORT2 -u_unescapeAt(UNESCAPE_CHAR_AT charAt, - int32_t *offset, - int32_t length, - void *context) { - - int32_t start = *offset; - UChar32 c; - UChar32 result = 0; - int8_t n = 0; - int8_t minDig = 0; - int8_t maxDig = 0; - int8_t bitsPerDigit = 4; - int32_t dig; - UBool braces = false; - - /* Check that offset is in range */ - if (*offset < 0 || *offset >= length) { - goto err; - } - - /* Fetch first UChar after '\\' */ - c = charAt((*offset)++, context); - - /* Convert hexadecimal and octal escapes */ - switch (c) { - case u'u': - minDig = maxDig = 4; - break; - case u'U': - minDig = maxDig = 8; - break; - case u'x': - minDig = 1; - if (*offset < length && charAt(*offset, context) == u'{') { - ++(*offset); - braces = true; - maxDig = 8; - } else { - maxDig = 2; - } - break; - default: - dig = _digit8(c); - if (dig >= 0) { - minDig = 1; - maxDig = 3; - n = 1; /* Already have first octal digit */ - bitsPerDigit = 3; - result = dig; - } - break; - } - if (minDig != 0) { - while (*offset < length && n < maxDig) { - c = charAt(*offset, context); - dig = (bitsPerDigit == 3) ? _digit8(c) : _digit16(c); - if (dig < 0) { - break; - } - result = (result << bitsPerDigit) | dig; - ++(*offset); - ++n; - } - if (n < minDig) { - goto err; - } - if (braces) { - if (c != u'}') { - goto err; - } - ++(*offset); - } - if (result < 0 || result >= 0x110000) { - goto err; - } - /* If an escape sequence specifies a lead surrogate, see if - * there is a trail surrogate after it, either as an escape or - * as a literal. If so, join them up into a supplementary. - */ - if (*offset < length && U16_IS_LEAD(result)) { - int32_t ahead = *offset + 1; - c = charAt(*offset, context); - if (c == u'\\' && ahead < length) { - // Calling ourselves recursively may cause a stack overflow if - // we have repeated escaped lead surrogates. - // Limit the length to 11 ("x{0000DFFF}") after ahead. - int32_t tailLimit = ahead + 11; - if (tailLimit > length) { - tailLimit = length; - } - c = u_unescapeAt(charAt, &ahead, tailLimit, context); - } - if (U16_IS_TRAIL(c)) { - *offset = ahead; - result = U16_GET_SUPPLEMENTARY(result, c); - } - } - return result; - } - - /* Convert C-style escapes in table */ - for (int32_t i=0; i destCapacity) { - srcLen = destCapacity; - } - u_charsToUChars(src, dest, srcLen); -} - -/* Do an invariant conversion of char* -> UChar*, with escape parsing */ -U_CAPI int32_t U_EXPORT2 -u_unescape(const char *src, UChar *dest, int32_t destCapacity) { - const char *segment = src; - int32_t i = 0; - char c; - - while ((c=*src) != 0) { - /* '\\' intentionally written as compiler-specific - * character constant to correspond to compiler-specific - * char* constants. */ - if (c == '\\') { - int32_t lenParsed = 0; - UChar32 c32; - if (src != segment) { - if (dest != NULL) { - _appendUChars(dest + i, destCapacity - i, - segment, (int32_t)(src - segment)); - } - i += (int32_t)(src - segment); - } - ++src; /* advance past '\\' */ - c32 = (UChar32)u_unescapeAt(_charPtr_charAt, &lenParsed, (int32_t)uprv_strlen(src), (void*)src); - if (lenParsed == 0) { - goto err; - } - src += lenParsed; /* advance past escape seq. */ - if (dest != NULL && U16_LENGTH(c32) <= (destCapacity - i)) { - U16_APPEND_UNSAFE(dest, i, c32); - } else { - i += U16_LENGTH(c32); - } - segment = src; - } else { - ++src; - } - } - if (src != segment) { - if (dest != NULL) { - _appendUChars(dest + i, destCapacity - i, - segment, (int32_t)(src - segment)); - } - i += (int32_t)(src - segment); - } - if (dest != NULL && i < destCapacity) { - dest[i] = 0; - } - return i; - - err: - if (dest != NULL && destCapacity > 0) { - *dest = 0; - } - return 0; -} - -/* NUL-termination of strings ----------------------------------------------- */ - -/** - * NUL-terminate a string no matter what its type. - * Set warning and error codes accordingly. - */ -#define __TERMINATE_STRING(dest, destCapacity, length, pErrorCode) UPRV_BLOCK_MACRO_BEGIN { \ - if(pErrorCode!=NULL && U_SUCCESS(*pErrorCode)) { \ - /* not a public function, so no complete argument checking */ \ - \ - if(length<0) { \ - /* assume that the caller handles this */ \ - } else if(lengthdestCapacity */ { \ - /* even the string itself did not fit - set an error code */ \ - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; \ - } \ - } \ -} UPRV_BLOCK_MACRO_END - -U_CAPI UChar U_EXPORT2 -u_asciiToUpper(UChar c) { - if (u'a' <= c && c <= u'z') { - c = c + u'A' - u'a'; - } - return c; -} - -U_CAPI int32_t U_EXPORT2 -u_terminateUChars(UChar *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode) { - __TERMINATE_STRING(dest, destCapacity, length, pErrorCode); - return length; -} - -U_CAPI int32_t U_EXPORT2 -u_terminateChars(char *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode) { - __TERMINATE_STRING(dest, destCapacity, length, pErrorCode); - return length; -} - -U_CAPI int32_t U_EXPORT2 -u_terminateUChar32s(UChar32 *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode) { - __TERMINATE_STRING(dest, destCapacity, length, pErrorCode); - return length; -} - -U_CAPI int32_t U_EXPORT2 -u_terminateWChars(wchar_t *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode) { - __TERMINATE_STRING(dest, destCapacity, length, pErrorCode); - return length; -} - -// Compute the hash code for a string -------------------------------------- *** - -// Moved here from uhash.c so that UnicodeString::hashCode() does not depend -// on UHashtable code. - -/* - Compute the hash by iterating sparsely over about 32 (up to 63) - characters spaced evenly through the string. For each character, - multiply the previous hash value by a prime number and add the new - character in, like a linear congruential random number generator, - producing a pseudorandom deterministic value well distributed over - the output range. [LIU] -*/ - -#define STRING_HASH(TYPE, STR, STRLEN, DEREF) UPRV_BLOCK_MACRO_BEGIN { \ - uint32_t hash = 0; \ - const TYPE *p = (const TYPE*) STR; \ - if (p != NULL) { \ - int32_t len = (int32_t)(STRLEN); \ - int32_t inc = ((len - 32) / 32) + 1; \ - const TYPE *limit = p + len; \ - while (p(hash); \ -} UPRV_BLOCK_MACRO_END - -/* Used by UnicodeString to compute its hashcode - Not public API. */ -U_CAPI int32_t U_EXPORT2 -ustr_hashUCharsN(const UChar *str, int32_t length) { - STRING_HASH(UChar, str, length, *p); -} - -U_CAPI int32_t U_EXPORT2 -ustr_hashCharsN(const char *str, int32_t length) { - STRING_HASH(uint8_t, str, length, *p); -} - -U_CAPI int32_t U_EXPORT2 -ustr_hashICharsN(const char *str, int32_t length) { - STRING_HASH(char, str, length, (uint8_t)uprv_tolower(*p)); -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1998-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* +* File ustring.cpp +* +* Modification History: +* +* Date Name Description +* 12/07/98 bertrand Creation. +****************************************************************************** +*/ + +#include "unicode/utypes.h" +#include "unicode/putil.h" +#include "unicode/uchar.h" +#include "unicode/ustring.h" +#include "unicode/utf16.h" +#include "cstring.h" +#include "cwchar.h" +#include "cmemory.h" +#include "ustr_imp.h" + +/* ANSI string.h - style functions ------------------------------------------ */ + +/* U+ffff is the highest BMP code point, the highest one that fits into a 16-bit char16_t */ +#define U_BMP_MAX 0xffff + +/* Forward binary string search functions ----------------------------------- */ + +/* + * Test if a substring match inside a string is at code point boundaries. + * All pointers refer to the same buffer. + * The limit pointer may be nullptr, all others must be real pointers. + */ +static inline UBool +isMatchAtCPBoundary(const char16_t *start, const char16_t *match, const char16_t *matchLimit, const char16_t *limit) { + if(U16_IS_TRAIL(*match) && start!=match && U16_IS_LEAD(*(match-1))) { + /* the leading edge of the match is in the middle of a surrogate pair */ + return false; + } + if(U16_IS_LEAD(*(matchLimit-1)) && matchLimit!=limit && U16_IS_TRAIL(*matchLimit)) { + /* the trailing edge of the match is in the middle of a surrogate pair */ + return false; + } + return true; +} + +U_CAPI char16_t * U_EXPORT2 +u_strFindFirst(const char16_t *s, int32_t length, + const char16_t *sub, int32_t subLength) { + const char16_t *start, *p, *q, *subLimit; + char16_t c, cs, cq; + + if(sub==nullptr || subLength<-1) { + return (char16_t *)s; + } + if(s==nullptr || length<-1) { + return nullptr; + } + + start=s; + + if(length<0 && subLength<0) { + /* both strings are NUL-terminated */ + if((cs=*sub++)==0) { + return (char16_t *)s; + } + if(*sub==0 && !U16_IS_SURROGATE(cs)) { + /* the substring consists of a single, non-surrogate BMP code point */ + return u_strchr(s, cs); + } + + while((c=*s++)!=0) { + if(c==cs) { + /* found first substring char16_t, compare rest */ + p=s; + q=sub; + for(;;) { + if((cq=*q)==0) { + if(isMatchAtCPBoundary(start, s-1, p, nullptr)) { + return (char16_t *)(s-1); /* well-formed match */ + } else { + break; /* no match because surrogate pair is split */ + } + } + if((c=*p)==0) { + return nullptr; /* no match, and none possible after s */ + } + if(c!=cq) { + break; /* no match */ + } + ++p; + ++q; + } + } + } + + /* not found */ + return nullptr; + } + + if(subLength<0) { + subLength=u_strlen(sub); + } + if(subLength==0) { + return (char16_t *)s; + } + + /* get sub[0] to search for it fast */ + cs=*sub++; + --subLength; + subLimit=sub+subLength; + + if(subLength==0 && !U16_IS_SURROGATE(cs)) { + /* the substring consists of a single, non-surrogate BMP code point */ + return length<0 ? u_strchr(s, cs) : u_memchr(s, cs, length); + } + + if(length<0) { + /* s is NUL-terminated */ + while((c=*s++)!=0) { + if(c==cs) { + /* found first substring char16_t, compare rest */ + p=s; + q=sub; + for(;;) { + if(q==subLimit) { + if(isMatchAtCPBoundary(start, s-1, p, nullptr)) { + return (char16_t *)(s-1); /* well-formed match */ + } else { + break; /* no match because surrogate pair is split */ + } + } + if((c=*p)==0) { + return nullptr; /* no match, and none possible after s */ + } + if(c!=*q) { + break; /* no match */ + } + ++p; + ++q; + } + } + } + } else { + const char16_t *limit, *preLimit; + + /* subLength was decremented above */ + if(length<=subLength) { + return nullptr; /* s is shorter than sub */ + } + + limit=s+length; + + /* the substring must start before preLimit */ + preLimit=limit-subLength; + + while(s!=preLimit) { + c=*s++; + if(c==cs) { + /* found first substring char16_t, compare rest */ + p=s; + q=sub; + for(;;) { + if(q==subLimit) { + if(isMatchAtCPBoundary(start, s-1, p, limit)) { + return (char16_t *)(s-1); /* well-formed match */ + } else { + break; /* no match because surrogate pair is split */ + } + } + if(*p!=*q) { + break; /* no match */ + } + ++p; + ++q; + } + } + } + } + + /* not found */ + return nullptr; +} + +U_CAPI char16_t * U_EXPORT2 +u_strstr(const char16_t *s, const char16_t *substring) { + return u_strFindFirst(s, -1, substring, -1); +} + +U_CAPI char16_t * U_EXPORT2 +u_strchr(const char16_t *s, char16_t c) { + if(U16_IS_SURROGATE(c)) { + /* make sure to not find half of a surrogate pair */ + return u_strFindFirst(s, -1, &c, 1); + } else { + char16_t cs; + + /* trivial search for a BMP code point */ + for(;;) { + if((cs=*s)==c) { + return (char16_t *)s; + } + if(cs==0) { + return nullptr; + } + ++s; + } + } +} + +U_CAPI char16_t * U_EXPORT2 +u_strchr32(const char16_t *s, UChar32 c) { + if((uint32_t)c<=U_BMP_MAX) { + /* find BMP code point */ + return u_strchr(s, (char16_t)c); + } else if((uint32_t)c<=UCHAR_MAX_VALUE) { + /* find supplementary code point as surrogate pair */ + char16_t cs, lead=U16_LEAD(c), trail=U16_TRAIL(c); + + while((cs=*s++)!=0) { + if(cs==lead && *s==trail) { + return (char16_t *)(s-1); + } + } + return nullptr; + } else { + /* not a Unicode code point, not findable */ + return nullptr; + } +} + +U_CAPI char16_t * U_EXPORT2 +u_memchr(const char16_t *s, char16_t c, int32_t count) { + if(count<=0) { + return nullptr; /* no string */ + } else if(U16_IS_SURROGATE(c)) { + /* make sure to not find half of a surrogate pair */ + return u_strFindFirst(s, count, &c, 1); + } else { + /* trivial search for a BMP code point */ + const char16_t *limit=s+count; + do { + if(*s==c) { + return (char16_t *)s; + } + } while(++s!=limit); + return nullptr; + } +} + +U_CAPI char16_t * U_EXPORT2 +u_memchr32(const char16_t *s, UChar32 c, int32_t count) { + if((uint32_t)c<=U_BMP_MAX) { + /* find BMP code point */ + return u_memchr(s, (char16_t)c, count); + } else if(count<2) { + /* too short for a surrogate pair */ + return nullptr; + } else if((uint32_t)c<=UCHAR_MAX_VALUE) { + /* find supplementary code point as surrogate pair */ + const char16_t *limit=s+count-1; /* -1 so that we do not need a separate check for the trail unit */ + char16_t lead=U16_LEAD(c), trail=U16_TRAIL(c); + + do { + if(*s==lead && *(s+1)==trail) { + return (char16_t *)s; + } + } while(++s!=limit); + return nullptr; + } else { + /* not a Unicode code point, not findable */ + return nullptr; + } +} + +/* Backward binary string search functions ---------------------------------- */ + +U_CAPI char16_t * U_EXPORT2 +u_strFindLast(const char16_t *s, int32_t length, + const char16_t *sub, int32_t subLength) { + const char16_t *start, *limit, *p, *q, *subLimit; + char16_t c, cs; + + if(sub==nullptr || subLength<-1) { + return (char16_t *)s; + } + if(s==nullptr || length<-1) { + return nullptr; + } + + /* + * This implementation is more lazy than the one for u_strFindFirst(): + * There is no special search code for NUL-terminated strings. + * It does not seem to be worth it for searching substrings to + * search forward and find all matches like in u_strrchr() and similar. + * Therefore, we simply get both string lengths and search backward. + * + * markus 2002oct23 + */ + + if(subLength<0) { + subLength=u_strlen(sub); + } + if(subLength==0) { + return (char16_t *)s; + } + + /* get sub[subLength-1] to search for it fast */ + subLimit=sub+subLength; + cs=*(--subLimit); + --subLength; + + if(subLength==0 && !U16_IS_SURROGATE(cs)) { + /* the substring consists of a single, non-surrogate BMP code point */ + return length<0 ? u_strrchr(s, cs) : u_memrchr(s, cs, length); + } + + if(length<0) { + length=u_strlen(s); + } + + /* subLength was decremented above */ + if(length<=subLength) { + return nullptr; /* s is shorter than sub */ + } + + start=s; + limit=s+length; + + /* the substring must start no later than s+subLength */ + s+=subLength; + + while(s!=limit) { + c=*(--limit); + if(c==cs) { + /* found last substring char16_t, compare rest */ + p=limit; + q=subLimit; + for(;;) { + if(q==sub) { + if(isMatchAtCPBoundary(start, p, limit+1, start+length)) { + return (char16_t *)p; /* well-formed match */ + } else { + break; /* no match because surrogate pair is split */ + } + } + if(*(--p)!=*(--q)) { + break; /* no match */ + } + } + } + } + + /* not found */ + return nullptr; +} + +U_CAPI char16_t * U_EXPORT2 +u_strrstr(const char16_t *s, const char16_t *substring) { + return u_strFindLast(s, -1, substring, -1); +} + +U_CAPI char16_t * U_EXPORT2 +u_strrchr(const char16_t *s, char16_t c) { + if(U16_IS_SURROGATE(c)) { + /* make sure to not find half of a surrogate pair */ + return u_strFindLast(s, -1, &c, 1); + } else { + const char16_t *result=nullptr; + char16_t cs; + + /* trivial search for a BMP code point */ + for(;;) { + if((cs=*s)==c) { + result=s; + } + if(cs==0) { + return (char16_t *)result; + } + ++s; + } + } +} + +U_CAPI char16_t * U_EXPORT2 +u_strrchr32(const char16_t *s, UChar32 c) { + if((uint32_t)c<=U_BMP_MAX) { + /* find BMP code point */ + return u_strrchr(s, (char16_t)c); + } else if((uint32_t)c<=UCHAR_MAX_VALUE) { + /* find supplementary code point as surrogate pair */ + const char16_t *result=nullptr; + char16_t cs, lead=U16_LEAD(c), trail=U16_TRAIL(c); + + while((cs=*s++)!=0) { + if(cs==lead && *s==trail) { + result=s-1; + } + } + return (char16_t *)result; + } else { + /* not a Unicode code point, not findable */ + return nullptr; + } +} + +U_CAPI char16_t * U_EXPORT2 +u_memrchr(const char16_t *s, char16_t c, int32_t count) { + if(count<=0) { + return nullptr; /* no string */ + } else if(U16_IS_SURROGATE(c)) { + /* make sure to not find half of a surrogate pair */ + return u_strFindLast(s, count, &c, 1); + } else { + /* trivial search for a BMP code point */ + const char16_t *limit=s+count; + do { + if(*(--limit)==c) { + return (char16_t *)limit; + } + } while(s!=limit); + return nullptr; + } +} + +U_CAPI char16_t * U_EXPORT2 +u_memrchr32(const char16_t *s, UChar32 c, int32_t count) { + if((uint32_t)c<=U_BMP_MAX) { + /* find BMP code point */ + return u_memrchr(s, (char16_t)c, count); + } else if(count<2) { + /* too short for a surrogate pair */ + return nullptr; + } else if((uint32_t)c<=UCHAR_MAX_VALUE) { + /* find supplementary code point as surrogate pair */ + const char16_t *limit=s+count-1; + char16_t lead=U16_LEAD(c), trail=U16_TRAIL(c); + + do { + if(*limit==trail && *(limit-1)==lead) { + return (char16_t *)(limit-1); + } + } while(s!=--limit); + return nullptr; + } else { + /* not a Unicode code point, not findable */ + return nullptr; + } +} + +/* Tokenization functions --------------------------------------------------- */ + +/* + * Match each code point in a string against each code point in the matchSet. + * Return the index of the first string code point that + * is (polarity==true) or is not (false) contained in the matchSet. + * Return -(string length)-1 if there is no such code point. + */ +static int32_t +_matchFromSet(const char16_t *string, const char16_t *matchSet, UBool polarity) { + int32_t matchLen, matchBMPLen, strItr, matchItr; + UChar32 stringCh, matchCh; + char16_t c, c2; + + /* first part of matchSet contains only BMP code points */ + matchBMPLen = 0; + while((c = matchSet[matchBMPLen]) != 0 && U16_IS_SINGLE(c)) { + ++matchBMPLen; + } + + /* second part of matchSet contains BMP and supplementary code points */ + matchLen = matchBMPLen; + while(matchSet[matchLen] != 0) { + ++matchLen; + } + + for(strItr = 0; (c = string[strItr]) != 0;) { + ++strItr; + if(U16_IS_SINGLE(c)) { + if(polarity) { + for(matchItr = 0; matchItr < matchLen; ++matchItr) { + if(c == matchSet[matchItr]) { + return strItr - 1; /* one matches */ + } + } + } else { + for(matchItr = 0; matchItr < matchLen; ++matchItr) { + if(c == matchSet[matchItr]) { + goto endloop; + } + } + return strItr - 1; /* none matches */ + } + } else { + /* + * No need to check for string length before U16_IS_TRAIL + * because c2 could at worst be the terminating NUL. + */ + if(U16_IS_SURROGATE_LEAD(c) && U16_IS_TRAIL(c2 = string[strItr])) { + ++strItr; + stringCh = U16_GET_SUPPLEMENTARY(c, c2); + } else { + stringCh = c; /* unpaired trail surrogate */ + } + + if(polarity) { + for(matchItr = matchBMPLen; matchItr < matchLen;) { + U16_NEXT(matchSet, matchItr, matchLen, matchCh); + if(stringCh == matchCh) { + return strItr - U16_LENGTH(stringCh); /* one matches */ + } + } + } else { + for(matchItr = matchBMPLen; matchItr < matchLen;) { + U16_NEXT(matchSet, matchItr, matchLen, matchCh); + if(stringCh == matchCh) { + goto endloop; + } + } + return strItr - U16_LENGTH(stringCh); /* none matches */ + } + } +endloop: + /* wish C had continue with labels like Java... */; + } + + /* Didn't find it. */ + return -strItr-1; +} + +/* Search for a codepoint in a string that matches one of the matchSet codepoints. */ +U_CAPI char16_t * U_EXPORT2 +u_strpbrk(const char16_t *string, const char16_t *matchSet) +{ + int32_t idx = _matchFromSet(string, matchSet, true); + if(idx >= 0) { + return (char16_t *)string + idx; + } else { + return nullptr; + } +} + +/* Search for a codepoint in a string that matches one of the matchSet codepoints. */ +U_CAPI int32_t U_EXPORT2 +u_strcspn(const char16_t *string, const char16_t *matchSet) +{ + int32_t idx = _matchFromSet(string, matchSet, true); + if(idx >= 0) { + return idx; + } else { + return -idx - 1; /* == u_strlen(string) */ + } +} + +/* Search for a codepoint in a string that does not match one of the matchSet codepoints. */ +U_CAPI int32_t U_EXPORT2 +u_strspn(const char16_t *string, const char16_t *matchSet) +{ + int32_t idx = _matchFromSet(string, matchSet, false); + if(idx >= 0) { + return idx; + } else { + return -idx - 1; /* == u_strlen(string) */ + } +} + +/* ----- Text manipulation functions --- */ + +U_CAPI char16_t* U_EXPORT2 +u_strtok_r(char16_t *src, + const char16_t *delim, + char16_t **saveState) +{ + char16_t *tokSource; + char16_t *nextToken; + uint32_t nonDelimIdx; + + /* If saveState is nullptr, the user messed up. */ + if (src != nullptr) { + tokSource = src; + *saveState = src; /* Set to "src" in case there are no delimiters */ + } + else if (*saveState) { + tokSource = *saveState; + } + else { + /* src == nullptr && *saveState == nullptr */ + /* This shouldn't happen. We already finished tokenizing. */ + return nullptr; + } + + /* Skip initial delimiters */ + nonDelimIdx = u_strspn(tokSource, delim); + tokSource = &tokSource[nonDelimIdx]; + + if (*tokSource) { + nextToken = u_strpbrk(tokSource, delim); + if (nextToken != nullptr) { + /* Create a token */ + *(nextToken++) = 0; + *saveState = nextToken; + return tokSource; + } + else if (*saveState) { + /* Return the last token */ + *saveState = nullptr; + return tokSource; + } + } + else { + /* No tokens were found. Only delimiters were left. */ + *saveState = nullptr; + } + return nullptr; +} + +/* Miscellaneous functions -------------------------------------------------- */ + +U_CAPI char16_t* U_EXPORT2 +u_strcat(char16_t *dst, + const char16_t *src) +{ + char16_t *anchor = dst; /* save a pointer to start of dst */ + + while(*dst != 0) { /* To end of first string */ + ++dst; + } + while((*(dst++) = *(src++)) != 0) { /* copy string 2 over */ + } + + return anchor; +} + +U_CAPI char16_t* U_EXPORT2 +u_strncat(char16_t *dst, + const char16_t *src, + int32_t n ) +{ + if(n > 0) { + char16_t *anchor = dst; /* save a pointer to start of dst */ + + while(*dst != 0) { /* To end of first string */ + ++dst; + } + while((*dst = *src) != 0) { /* copy string 2 over */ + ++dst; + if(--n == 0) { + *dst = 0; + break; + } + ++src; + } + + return anchor; + } else { + return dst; + } +} + +/* ----- Text property functions --- */ + +U_CAPI int32_t U_EXPORT2 +u_strcmp(const char16_t *s1, + const char16_t *s2) +{ + char16_t c1, c2; + + for(;;) { + c1=*s1++; + c2=*s2++; + if (c1 != c2 || c1 == 0) { + break; + } + } + return (int32_t)c1 - (int32_t)c2; +} + +U_CFUNC int32_t U_EXPORT2 +uprv_strCompare(const char16_t *s1, int32_t length1, + const char16_t *s2, int32_t length2, + UBool strncmpStyle, UBool codePointOrder) { + const char16_t *start1, *start2, *limit1, *limit2; + char16_t c1, c2; + + /* setup for fix-up */ + start1=s1; + start2=s2; + + /* compare identical prefixes - they do not need to be fixed up */ + if(length1<0 && length2<0) { + /* strcmp style, both NUL-terminated */ + if(s1==s2) { + return 0; + } + + for(;;) { + c1=*s1; + c2=*s2; + if(c1!=c2) { + break; + } + if(c1==0) { + return 0; + } + ++s1; + ++s2; + } + + /* setup for fix-up */ + limit1=limit2=nullptr; + } else if(strncmpStyle) { + /* special handling for strncmp, assume length1==length2>=0 but also check for NUL */ + if(s1==s2) { + return 0; + } + + limit1=start1+length1; + + for(;;) { + /* both lengths are same, check only one limit */ + if(s1==limit1) { + return 0; + } + + c1=*s1; + c2=*s2; + if(c1!=c2) { + break; + } + if(c1==0) { + return 0; + } + ++s1; + ++s2; + } + + /* setup for fix-up */ + limit2=start2+length1; /* use length1 here, too, to enforce assumption */ + } else { + /* memcmp/UnicodeString style, both length-specified */ + int32_t lengthResult; + + if(length1<0) { + length1=u_strlen(s1); + } + if(length2<0) { + length2=u_strlen(s2); + } + + /* limit1=start1+min(length1, length2) */ + if(length1length2 */ { + lengthResult=1; + limit1=start1+length2; + } + + if(s1==s2) { + return lengthResult; + } + + for(;;) { + /* check pseudo-limit */ + if(s1==limit1) { + return lengthResult; + } + + c1=*s1; + c2=*s2; + if(c1!=c2) { + break; + } + ++s1; + ++s2; + } + + /* setup for fix-up */ + limit1=start1+length1; + limit2=start2+length2; + } + + /* if both values are in or above the surrogate range, fix them up */ + if(c1>=0xd800 && c2>=0xd800 && codePointOrder) { + /* subtract 0x2800 from BMP code points to make them smaller than supplementary ones */ + if( + (c1<=0xdbff && (s1+1)!=limit1 && U16_IS_TRAIL(*(s1+1))) || + (U16_IS_TRAIL(c1) && start1!=s1 && U16_IS_LEAD(*(s1-1))) + ) { + /* part of a surrogate pair, leave >=d800 */ + } else { + /* BMP code point - may be surrogate code point - make =d800 */ + } else { + /* BMP code point - may be surrogate code point - make move(iter1, 0, UITER_START); + iter2->move(iter2, 0, UITER_START); + + /* compare identical prefixes - they do not need to be fixed up */ + for(;;) { + c1=iter1->next(iter1); + c2=iter2->next(iter2); + if(c1!=c2) { + break; + } + if(c1==-1) { + return 0; + } + } + + /* if both values are in or above the surrogate range, fix them up */ + if(c1>=0xd800 && c2>=0xd800 && codePointOrder) { + /* subtract 0x2800 from BMP code points to make them smaller than supplementary ones */ + if( + (c1<=0xdbff && U16_IS_TRAIL(iter1->current(iter1))) || + (U16_IS_TRAIL(c1) && (iter1->previous(iter1), U16_IS_LEAD(iter1->previous(iter1)))) + ) { + /* part of a surrogate pair, leave >=d800 */ + } else { + /* BMP code point - may be surrogate code point - make current(iter2))) || + (U16_IS_TRAIL(c2) && (iter2->previous(iter2), U16_IS_LEAD(iter2->previous(iter2)))) + ) { + /* part of a surrogate pair, leave >=d800 */ + } else { + /* BMP code point - may be surrogate code point - make =0. + * + * Consistently leaving them _behind_ the different units is not an option + * because the current "unit" is the end of the string if that is reached, + * and in such a case the iterator does not move. + * For example, when comparing "ab" with "abc", both iterators rest _on_ the end + * of their strings. Calling previous() on each does not move them to where + * the comparison fails. + * + * So the simplest semantics is to not define where the iterators end up. + * + * The following fragment is part of what would need to be done for backing up. + */ +void fragment { + /* iff a surrogate is part of a surrogate pair, leave >=d800 */ + if(c1<=0xdbff) { + if(!U16_IS_TRAIL(iter1->current(iter1))) { + /* lead surrogate code point - make getIndex(iter1, UITER_CURRENT); + iter1->previous(iter1); /* ==c1 */ + if(!U16_IS_LEAD(iter1->previous(iter1))) { + /* trail surrogate code point - make move(iter1, idx, UITER_ZERO); + } else /* 0xe000<=c1<=0xffff */ { + /* BMP code point - make 0) { + int32_t rc; + for(;;) { + rc = (int32_t)*s1 - (int32_t)*s2; + if(rc != 0 || *s1 == 0 || --n == 0) { + return rc; + } + ++s1; + ++s2; + } + } else { + return 0; + } +} + +U_CAPI int32_t U_EXPORT2 +u_strncmpCodePointOrder(const char16_t *s1, const char16_t *s2, int32_t n) { + return uprv_strCompare(s1, n, s2, n, true, true); +} + +U_CAPI char16_t* U_EXPORT2 +u_strcpy(char16_t *dst, + const char16_t *src) +{ + char16_t *anchor = dst; /* save a pointer to start of dst */ + + while((*(dst++) = *(src++)) != 0) { /* copy string 2 over */ + } + + return anchor; +} + +U_CAPI char16_t* U_EXPORT2 +u_strncpy(char16_t *dst, + const char16_t *src, + int32_t n) +{ + char16_t *anchor = dst; /* save a pointer to start of dst */ + + /* copy string 2 over */ + while(n > 0 && (*(dst++) = *(src++)) != 0) { + --n; + } + + return anchor; +} + +U_CAPI int32_t U_EXPORT2 +u_strlen(const char16_t *s) +{ +#if U_SIZEOF_WCHAR_T == U_SIZEOF_UCHAR + return (int32_t)uprv_wcslen((const wchar_t *)s); +#else + const char16_t *t = s; + while(*t != 0) { + ++t; + } + return t - s; +#endif +} + +U_CAPI int32_t U_EXPORT2 +u_countChar32(const char16_t *s, int32_t length) { + int32_t count; + + if(s==nullptr || length<-1) { + return 0; + } + + count=0; + if(length>=0) { + while(length>0) { + ++count; + if(U16_IS_LEAD(*s) && length>=2 && U16_IS_TRAIL(*(s+1))) { + s+=2; + length-=2; + } else { + ++s; + --length; + } + } + } else /* length==-1 */ { + char16_t c; + + for(;;) { + if((c=*s++)==0) { + break; + } + ++count; + + /* + * sufficient to look ahead one because of UTF-16; + * safe to look ahead one because at worst that would be the terminating NUL + */ + if(U16_IS_LEAD(c) && U16_IS_TRAIL(*s)) { + ++s; + } + } + } + return count; +} + +U_CAPI UBool U_EXPORT2 +u_strHasMoreChar32Than(const char16_t *s, int32_t length, int32_t number) { + + if(number<0) { + return true; + } + if(s==nullptr || length<-1) { + return false; + } + + if(length==-1) { + /* s is NUL-terminated */ + char16_t c; + + /* count code points until they exceed */ + for(;;) { + if((c=*s++)==0) { + return false; + } + if(number==0) { + return true; + } + if(U16_IS_LEAD(c) && U16_IS_TRAIL(*s)) { + ++s; + } + --number; + } + } else { + /* length>=0 known */ + const char16_t *limit; + int32_t maxSupplementary; + + /* s contains at least (length+1)/2 code points: <=2 UChars per cp */ + if(((length+1)/2)>number) { + return true; + } + + /* check if s does not even contain enough UChars */ + maxSupplementary=length-number; + if(maxSupplementary<=0) { + return false; + } + /* there are maxSupplementary=length-number more UChars than asked-for code points */ + + /* + * count code points until they exceed and also check that there are + * no more than maxSupplementary supplementary code points (char16_t pairs) + */ + limit=s+length; + for(;;) { + if(s==limit) { + return false; + } + if(number==0) { + return true; + } + if(U16_IS_LEAD(*s++) && s!=limit && U16_IS_TRAIL(*s)) { + ++s; + if(--maxSupplementary<=0) { + /* too many pairs - too few code points */ + return false; + } + } + --number; + } + } +} + +U_CAPI char16_t * U_EXPORT2 +u_memcpy(char16_t *dest, const char16_t *src, int32_t count) { + if(count > 0) { + uprv_memcpy(dest, src, (size_t)count*U_SIZEOF_UCHAR); + } + return dest; +} + +U_CAPI char16_t * U_EXPORT2 +u_memmove(char16_t *dest, const char16_t *src, int32_t count) { + if(count > 0) { + uprv_memmove(dest, src, (size_t)count*U_SIZEOF_UCHAR); + } + return dest; +} + +U_CAPI char16_t * U_EXPORT2 +u_memset(char16_t *dest, char16_t c, int32_t count) { + if(count > 0) { + char16_t *ptr = dest; + char16_t *limit = dest + count; + + while (ptr < limit) { + *(ptr++) = c; + } + } + return dest; +} + +U_CAPI int32_t U_EXPORT2 +u_memcmp(const char16_t *buf1, const char16_t *buf2, int32_t count) { + if(count > 0) { + const char16_t *limit = buf1 + count; + int32_t result; + + while (buf1 < limit) { + result = (int32_t)(uint16_t)*buf1 - (int32_t)(uint16_t)*buf2; + if (result != 0) { + return result; + } + buf1++; + buf2++; + } + } + return 0; +} + +U_CAPI int32_t U_EXPORT2 +u_memcmpCodePointOrder(const char16_t *s1, const char16_t *s2, int32_t count) { + return uprv_strCompare(s1, count, s2, count, false, true); +} + +/* u_unescape & support fns ------------------------------------------------- */ + +/* This map must be in ASCENDING ORDER OF THE ESCAPE CODE */ +static const char16_t UNESCAPE_MAP[] = { + /*" 0x22, 0x22 */ + /*' 0x27, 0x27 */ + /*? 0x3F, 0x3F */ + /*\ 0x5C, 0x5C */ + /*a*/ 0x61, 0x07, + /*b*/ 0x62, 0x08, + /*e*/ 0x65, 0x1b, + /*f*/ 0x66, 0x0c, + /*n*/ 0x6E, 0x0a, + /*r*/ 0x72, 0x0d, + /*t*/ 0x74, 0x09, + /*v*/ 0x76, 0x0b +}; +enum { UNESCAPE_MAP_LENGTH = UPRV_LENGTHOF(UNESCAPE_MAP) }; + +/* Convert one octal digit to a numeric value 0..7, or -1 on failure */ +static int32_t _digit8(char16_t c) { + if (c >= u'0' && c <= u'7') { + return c - u'0'; + } + return -1; +} + +/* Convert one hex digit to a numeric value 0..F, or -1 on failure */ +static int32_t _digit16(char16_t c) { + if (c >= u'0' && c <= u'9') { + return c - u'0'; + } + if (c >= u'A' && c <= u'F') { + return c - (u'A' - 10); + } + if (c >= u'a' && c <= u'f') { + return c - (u'a' - 10); + } + return -1; +} + +/* Parse a single escape sequence. Although this method deals in + * UChars, it does not use C++ or UnicodeString. This allows it to + * be used from C contexts. */ +U_CAPI UChar32 U_EXPORT2 +u_unescapeAt(UNESCAPE_CHAR_AT charAt, + int32_t *offset, + int32_t length, + void *context) { + + int32_t start = *offset; + UChar32 c; + UChar32 result = 0; + int8_t n = 0; + int8_t minDig = 0; + int8_t maxDig = 0; + int8_t bitsPerDigit = 4; + int32_t dig; + UBool braces = false; + + /* Check that offset is in range */ + if (*offset < 0 || *offset >= length) { + goto err; + } + + /* Fetch first char16_t after '\\' */ + c = charAt((*offset)++, context); + + /* Convert hexadecimal and octal escapes */ + switch (c) { + case u'u': + minDig = maxDig = 4; + break; + case u'U': + minDig = maxDig = 8; + break; + case u'x': + minDig = 1; + if (*offset < length && charAt(*offset, context) == u'{') { + ++(*offset); + braces = true; + maxDig = 8; + } else { + maxDig = 2; + } + break; + default: + dig = _digit8(c); + if (dig >= 0) { + minDig = 1; + maxDig = 3; + n = 1; /* Already have first octal digit */ + bitsPerDigit = 3; + result = dig; + } + break; + } + if (minDig != 0) { + while (*offset < length && n < maxDig) { + c = charAt(*offset, context); + dig = (bitsPerDigit == 3) ? _digit8(c) : _digit16(c); + if (dig < 0) { + break; + } + result = (result << bitsPerDigit) | dig; + ++(*offset); + ++n; + } + if (n < minDig) { + goto err; + } + if (braces) { + if (c != u'}') { + goto err; + } + ++(*offset); + } + if (result < 0 || result >= 0x110000) { + goto err; + } + /* If an escape sequence specifies a lead surrogate, see if + * there is a trail surrogate after it, either as an escape or + * as a literal. If so, join them up into a supplementary. + */ + if (*offset < length && U16_IS_LEAD(result)) { + int32_t ahead = *offset + 1; + c = charAt(*offset, context); + if (c == u'\\' && ahead < length) { + // Calling ourselves recursively may cause a stack overflow if + // we have repeated escaped lead surrogates. + // Limit the length to 11 ("x{0000DFFF}") after ahead. + int32_t tailLimit = ahead + 11; + if (tailLimit > length) { + tailLimit = length; + } + c = u_unescapeAt(charAt, &ahead, tailLimit, context); + } + if (U16_IS_TRAIL(c)) { + *offset = ahead; + result = U16_GET_SUPPLEMENTARY(result, c); + } + } + return result; + } + + /* Convert C-style escapes in table */ + for (int32_t i=0; i destCapacity) { + srcLen = destCapacity; + } + u_charsToUChars(src, dest, srcLen); +} + +/* Do an invariant conversion of char* -> char16_t*, with escape parsing */ +U_CAPI int32_t U_EXPORT2 +u_unescape(const char *src, char16_t *dest, int32_t destCapacity) { + const char *segment = src; + int32_t i = 0; + char c; + + while ((c=*src) != 0) { + /* '\\' intentionally written as compiler-specific + * character constant to correspond to compiler-specific + * char* constants. */ + if (c == '\\') { + int32_t lenParsed = 0; + UChar32 c32; + if (src != segment) { + if (dest != nullptr) { + _appendUChars(dest + i, destCapacity - i, + segment, (int32_t)(src - segment)); + } + i += (int32_t)(src - segment); + } + ++src; /* advance past '\\' */ + c32 = (UChar32)u_unescapeAt(_charPtr_charAt, &lenParsed, (int32_t)uprv_strlen(src), (void*)src); + if (lenParsed == 0) { + goto err; + } + src += lenParsed; /* advance past escape seq. */ + if (dest != nullptr && U16_LENGTH(c32) <= (destCapacity - i)) { + U16_APPEND_UNSAFE(dest, i, c32); + } else { + i += U16_LENGTH(c32); + } + segment = src; + } else { + ++src; + } + } + if (src != segment) { + if (dest != nullptr) { + _appendUChars(dest + i, destCapacity - i, + segment, (int32_t)(src - segment)); + } + i += (int32_t)(src - segment); + } + if (dest != nullptr && i < destCapacity) { + dest[i] = 0; + } + return i; + + err: + if (dest != nullptr && destCapacity > 0) { + *dest = 0; + } + return 0; +} + +/* NUL-termination of strings ----------------------------------------------- */ + +/** + * NUL-terminate a string no matter what its type. + * Set warning and error codes accordingly. + */ +#define __TERMINATE_STRING(dest, destCapacity, length, pErrorCode) UPRV_BLOCK_MACRO_BEGIN { \ + if(pErrorCode!=nullptr && U_SUCCESS(*pErrorCode)) { \ + /* not a public function, so no complete argument checking */ \ + \ + if(length<0) { \ + /* assume that the caller handles this */ \ + } else if(lengthdestCapacity */ { \ + /* even the string itself did not fit - set an error code */ \ + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; \ + } \ + } \ +} UPRV_BLOCK_MACRO_END + +U_CAPI char16_t U_EXPORT2 +u_asciiToUpper(char16_t c) { + if (u'a' <= c && c <= u'z') { + c = c + u'A' - u'a'; + } + return c; +} + +U_CAPI int32_t U_EXPORT2 +u_terminateUChars(char16_t *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode) { + __TERMINATE_STRING(dest, destCapacity, length, pErrorCode); + return length; +} + +U_CAPI int32_t U_EXPORT2 +u_terminateChars(char *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode) { + __TERMINATE_STRING(dest, destCapacity, length, pErrorCode); + return length; +} + +U_CAPI int32_t U_EXPORT2 +u_terminateUChar32s(UChar32 *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode) { + __TERMINATE_STRING(dest, destCapacity, length, pErrorCode); + return length; +} + +U_CAPI int32_t U_EXPORT2 +u_terminateWChars(wchar_t *dest, int32_t destCapacity, int32_t length, UErrorCode *pErrorCode) { + __TERMINATE_STRING(dest, destCapacity, length, pErrorCode); + return length; +} + +// Compute the hash code for a string -------------------------------------- *** + +// Moved here from uhash.c so that UnicodeString::hashCode() does not depend +// on UHashtable code. + +/* + Compute the hash by iterating sparsely over about 32 (up to 63) + characters spaced evenly through the string. For each character, + multiply the previous hash value by a prime number and add the new + character in, like a linear congruential random number generator, + producing a pseudorandom deterministic value well distributed over + the output range. [LIU] +*/ + +#define STRING_HASH(TYPE, STR, STRLEN, DEREF) UPRV_BLOCK_MACRO_BEGIN { \ + uint32_t hash = 0; \ + const TYPE *p = (const TYPE*) STR; \ + if (p != nullptr) { \ + int32_t len = (int32_t)(STRLEN); \ + int32_t inc = ((len - 32) / 32) + 1; \ + const TYPE *limit = p + len; \ + while (p(hash); \ +} UPRV_BLOCK_MACRO_END + +/* Used by UnicodeString to compute its hashcode - Not public API. */ +U_CAPI int32_t U_EXPORT2 +ustr_hashUCharsN(const char16_t *str, int32_t length) { + STRING_HASH(char16_t, str, length, *p); +} + +U_CAPI int32_t U_EXPORT2 +ustr_hashCharsN(const char *str, int32_t length) { + STRING_HASH(uint8_t, str, length, *p); +} + +U_CAPI int32_t U_EXPORT2 +ustr_hashICharsN(const char *str, int32_t length) { + STRING_HASH(char, str, length, (uint8_t)uprv_tolower(*p)); +} diff --git a/deps/icu-small/source/common/ustrtrns.cpp b/deps/icu-small/source/common/ustrtrns.cpp index dcb9dc5878323b..658f54810c6a80 100644 --- a/deps/icu-small/source/common/ustrtrns.cpp +++ b/deps/icu-small/source/common/ustrtrns.cpp @@ -1,1451 +1,1451 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2001-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* File ustrtrns.cpp -* -* Modification History: -* -* Date Name Description -* 9/10/2001 Ram Creation. -****************************************************************************** -*/ - -/******************************************************************************* - * - * u_strTo* and u_strFrom* APIs - * WCS functions moved to ustr_wcs.c for better modularization - * - ******************************************************************************* - */ - - -#include "unicode/putil.h" -#include "unicode/ustring.h" -#include "unicode/utf.h" -#include "unicode/utf8.h" -#include "unicode/utf16.h" -#include "cstring.h" -#include "cmemory.h" -#include "ustr_imp.h" -#include "uassert.h" - -U_CAPI UChar* U_EXPORT2 -u_strFromUTF32WithSub(UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar32 *src, - int32_t srcLength, - UChar32 subchar, int32_t *pNumSubstitutions, - UErrorCode *pErrorCode) { - const UChar32 *srcLimit; - UChar32 ch; - UChar *destLimit; - UChar *pDest; - int32_t reqLength; - int32_t numSubstitutions; - - /* args check */ - if(U_FAILURE(*pErrorCode)){ - return NULL; - } - if( (src==NULL && srcLength!=0) || srcLength < -1 || - (destCapacity<0) || (dest == NULL && destCapacity > 0) || - subchar > 0x10ffff || U_IS_SURROGATE(subchar) - ) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - if(pNumSubstitutions != NULL) { - *pNumSubstitutions = 0; - } - - pDest = dest; - destLimit = (dest!=NULL)?(dest + destCapacity):NULL; - reqLength = 0; - numSubstitutions = 0; - - if(srcLength < 0) { - /* simple loop for conversion of a NUL-terminated BMP string */ - while((ch=*src) != 0 && - ((uint32_t)ch < 0xd800 || (0xe000 <= ch && ch <= 0xffff))) { - ++src; - if(pDest < destLimit) { - *pDest++ = (UChar)ch; - } else { - ++reqLength; - } - } - srcLimit = src; - if(ch != 0) { - /* "complicated" case, find the end of the remaining string */ - while(*++srcLimit != 0) {} - } - } else { - srcLimit = (src!=NULL)?(src + srcLength):NULL; - } - - /* convert with length */ - while(src < srcLimit) { - ch = *src++; - do { - /* usually "loops" once; twice only for writing subchar */ - if((uint32_t)ch < 0xd800 || (0xe000 <= ch && ch <= 0xffff)) { - if(pDest < destLimit) { - *pDest++ = (UChar)ch; - } else { - ++reqLength; - } - break; - } else if(0x10000 <= ch && ch <= 0x10ffff) { - if(pDest!=NULL && ((pDest + 2) <= destLimit)) { - *pDest++ = U16_LEAD(ch); - *pDest++ = U16_TRAIL(ch); - } else { - reqLength += 2; - } - break; - } else if((ch = subchar) < 0) { - /* surrogate code point, or not a Unicode code point at all */ - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } else { - ++numSubstitutions; - } - } while(true); - } - - reqLength += (int32_t)(pDest - dest); - if(pDestLength) { - *pDestLength = reqLength; - } - if(pNumSubstitutions != NULL) { - *pNumSubstitutions = numSubstitutions; - } - - /* Terminate the buffer */ - u_terminateUChars(dest, destCapacity, reqLength, pErrorCode); - - return dest; -} - -U_CAPI UChar* U_EXPORT2 -u_strFromUTF32(UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar32 *src, - int32_t srcLength, - UErrorCode *pErrorCode) { - return u_strFromUTF32WithSub( - dest, destCapacity, pDestLength, - src, srcLength, - U_SENTINEL, NULL, - pErrorCode); -} - -U_CAPI UChar32* U_EXPORT2 -u_strToUTF32WithSub(UChar32 *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar *src, - int32_t srcLength, - UChar32 subchar, int32_t *pNumSubstitutions, - UErrorCode *pErrorCode) { - const UChar *srcLimit; - UChar32 ch; - UChar ch2; - UChar32 *destLimit; - UChar32 *pDest; - int32_t reqLength; - int32_t numSubstitutions; - - /* args check */ - if(U_FAILURE(*pErrorCode)){ - return NULL; - } - if( (src==NULL && srcLength!=0) || srcLength < -1 || - (destCapacity<0) || (dest == NULL && destCapacity > 0) || - subchar > 0x10ffff || U_IS_SURROGATE(subchar) - ) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - if(pNumSubstitutions != NULL) { - *pNumSubstitutions = 0; - } - - pDest = dest; - destLimit = (dest!=NULL)?(dest + destCapacity):NULL; - reqLength = 0; - numSubstitutions = 0; - - if(srcLength < 0) { - /* simple loop for conversion of a NUL-terminated BMP string */ - while((ch=*src) != 0 && !U16_IS_SURROGATE(ch)) { - ++src; - if(pDest < destLimit) { - *pDest++ = ch; - } else { - ++reqLength; - } - } - srcLimit = src; - if(ch != 0) { - /* "complicated" case, find the end of the remaining string */ - while(*++srcLimit != 0) {} - } - } else { - srcLimit = (src!=NULL)?(src + srcLength):NULL; - } - - /* convert with length */ - while(src < srcLimit) { - ch = *src++; - if(!U16_IS_SURROGATE(ch)) { - /* write or count ch below */ - } else if(U16_IS_SURROGATE_LEAD(ch) && src < srcLimit && U16_IS_TRAIL(ch2 = *src)) { - ++src; - ch = U16_GET_SUPPLEMENTARY(ch, ch2); - } else if((ch = subchar) < 0) { - /* unpaired surrogate */ - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } else { - ++numSubstitutions; - } - if(pDest < destLimit) { - *pDest++ = ch; - } else { - ++reqLength; - } - } - - reqLength += (int32_t)(pDest - dest); - if(pDestLength) { - *pDestLength = reqLength; - } - if(pNumSubstitutions != NULL) { - *pNumSubstitutions = numSubstitutions; - } - - /* Terminate the buffer */ - u_terminateUChar32s(dest, destCapacity, reqLength, pErrorCode); - - return dest; -} - -U_CAPI UChar32* U_EXPORT2 -u_strToUTF32(UChar32 *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar *src, - int32_t srcLength, - UErrorCode *pErrorCode) { - return u_strToUTF32WithSub( - dest, destCapacity, pDestLength, - src, srcLength, - U_SENTINEL, NULL, - pErrorCode); -} - -U_CAPI UChar* U_EXPORT2 -u_strFromUTF8WithSub(UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const char* src, - int32_t srcLength, - UChar32 subchar, int32_t *pNumSubstitutions, - UErrorCode *pErrorCode){ - /* args check */ - if(U_FAILURE(*pErrorCode)) { - return NULL; - } - if( (src==NULL && srcLength!=0) || srcLength < -1 || - (destCapacity<0) || (dest == NULL && destCapacity > 0) || - subchar > 0x10ffff || U_IS_SURROGATE(subchar) - ) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - if(pNumSubstitutions!=NULL) { - *pNumSubstitutions=0; - } - UChar *pDest = dest; - UChar *pDestLimit = dest+destCapacity; - int32_t reqLength = 0; - int32_t numSubstitutions=0; - - /* - * Inline processing of UTF-8 byte sequences: - * - * Byte sequences for the most common characters are handled inline in - * the conversion loops. In order to reduce the path lengths for those - * characters, the tests are arranged in a kind of binary search. - * ASCII (<=0x7f) is checked first, followed by the dividing point - * between 2- and 3-byte sequences (0xe0). - * The 3-byte branch is tested first to speed up CJK text. - * The compiler should combine the subtractions for the two tests for 0xe0. - * Each branch then tests for the other end of its range. - */ - - if(srcLength < 0){ - /* - * Transform a NUL-terminated string. - * The code explicitly checks for NULs only in the lead byte position. - * A NUL byte in the trail byte position fails the trail byte range check anyway. - */ - int32_t i; - UChar32 c; - for(i = 0; (c = (uint8_t)src[i]) != 0 && (pDest < pDestLimit);) { - // modified copy of U8_NEXT() - ++i; - if(U8_IS_SINGLE(c)) { - *pDest++=(UChar)c; - } else { - uint8_t __t1, __t2; - if( /* handle U+0800..U+FFFF inline */ - (0xe0<=(c) && (c)<0xf0) && - U8_IS_VALID_LEAD3_AND_T1((c), src[i]) && - (__t2=src[(i)+1]-0x80)<=0x3f) { - *pDest++ = (((c)&0xf)<<12)|((src[i]&0x3f)<<6)|__t2; - i+=2; - } else if( /* handle U+0080..U+07FF inline */ - ((c)<0xe0 && (c)>=0xc2) && - (__t1=src[i]-0x80)<=0x3f) { - *pDest++ = (((c)&0x1f)<<6)|__t1; - ++(i); - } else { - /* function call for "complicated" and error cases */ - (c)=utf8_nextCharSafeBody((const uint8_t *)src, &(i), -1, c, -1); - if(c<0 && (++numSubstitutions, c = subchar) < 0) { - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } else if(c<=0xFFFF) { - *(pDest++)=(UChar)c; - } else { - *(pDest++)=U16_LEAD(c); - if(pDest=0xc2) && - (__t1=src[i]-0x80)<=0x3f) { - ++reqLength; - ++(i); - } else { - /* function call for "complicated" and error cases */ - (c)=utf8_nextCharSafeBody((const uint8_t *)src, &(i), -1, c, -1); - if(c<0 && (++numSubstitutions, c = subchar) < 0) { - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } - reqLength += U16_LENGTH(c); - } - } - } - } else /* srcLength >= 0 */ { - /* Faster loop without ongoing checking for srcLength and pDestLimit. */ - int32_t i = 0; - UChar32 c; - for(;;) { - /* - * Each iteration of the inner loop progresses by at most 3 UTF-8 - * bytes and one UChar, for most characters. - * For supplementary code points (4 & 2), which are rare, - * there is an additional adjustment. - */ - int32_t count = (int32_t)(pDestLimit - pDest); - int32_t count2 = (srcLength - i) / 3; - if(count > count2) { - count = count2; /* min(remaining dest, remaining src/3) */ - } - if(count < 3) { - /* - * Too much overhead if we get near the end of the string, - * continue with the next loop. - */ - break; - } - - do { - // modified copy of U8_NEXT() - c = (uint8_t)src[i++]; - if(U8_IS_SINGLE(c)) { - *pDest++=(UChar)c; - } else { - uint8_t __t1, __t2; - if( /* handle U+0800..U+FFFF inline */ - (0xe0<=(c) && (c)<0xf0) && - ((i)+1)=0xc2) && - ((i)!=srcLength) && - (__t1=src[i]-0x80)<=0x3f) { - *pDest++ = (((c)&0x1f)<<6)|__t1; - ++(i); - } else { - if(c >= 0xf0 || subchar > 0xffff) { - // We may read up to four bytes and write up to two UChars, - // which we didn't account for with computing count, - // so we adjust it here. - if(--count == 0) { - --i; // back out byte c - break; - } - } - - /* function call for "complicated" and error cases */ - (c)=utf8_nextCharSafeBody((const uint8_t *)src, &(i), srcLength, c, -1); - if(c<0 && (++numSubstitutions, c = subchar) < 0) { - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } else if(c<=0xFFFF) { - *(pDest++)=(UChar)c; - } else { - *(pDest++)=U16_LEAD(c); - *(pDest++)=U16_TRAIL(c); - } - } - } - } while(--count > 0); - } - - while(i < srcLength && (pDest < pDestLimit)) { - // modified copy of U8_NEXT() - c = (uint8_t)src[i++]; - if(U8_IS_SINGLE(c)) { - *pDest++=(UChar)c; - } else { - uint8_t __t1, __t2; - if( /* handle U+0800..U+FFFF inline */ - (0xe0<=(c) && (c)<0xf0) && - ((i)+1)=0xc2) && - ((i)!=srcLength) && - (__t1=src[i]-0x80)<=0x3f) { - *pDest++ = (((c)&0x1f)<<6)|__t1; - ++(i); - } else { - /* function call for "complicated" and error cases */ - (c)=utf8_nextCharSafeBody((const uint8_t *)src, &(i), srcLength, c, -1); - if(c<0 && (++numSubstitutions, c = subchar) < 0) { - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } else if(c<=0xFFFF) { - *(pDest++)=(UChar)c; - } else { - *(pDest++)=U16_LEAD(c); - if(pDest=0xc2) && - ((i)!=srcLength) && - (__t1=src[i]-0x80)<=0x3f) { - ++reqLength; - ++(i); - } else { - /* function call for "complicated" and error cases */ - (c)=utf8_nextCharSafeBody((const uint8_t *)src, &(i), srcLength, c, -1); - if(c<0 && (++numSubstitutions, c = subchar) < 0) { - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } - reqLength += U16_LENGTH(c); - } - } - } - } - - reqLength+=(int32_t)(pDest - dest); - - if(pNumSubstitutions!=NULL) { - *pNumSubstitutions=numSubstitutions; - } - - if(pDestLength){ - *pDestLength = reqLength; - } - - /* Terminate the buffer */ - u_terminateUChars(dest,destCapacity,reqLength,pErrorCode); - - return dest; -} - -U_CAPI UChar* U_EXPORT2 -u_strFromUTF8(UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const char* src, - int32_t srcLength, - UErrorCode *pErrorCode){ - return u_strFromUTF8WithSub( - dest, destCapacity, pDestLength, - src, srcLength, - U_SENTINEL, NULL, - pErrorCode); -} - -U_CAPI UChar * U_EXPORT2 -u_strFromUTF8Lenient(UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const char *src, - int32_t srcLength, - UErrorCode *pErrorCode) { - UChar *pDest = dest; - UChar32 ch; - int32_t reqLength = 0; - uint8_t* pSrc = (uint8_t*) src; - - /* args check */ - if(U_FAILURE(*pErrorCode)){ - return NULL; - } - - if( (src==NULL && srcLength!=0) || srcLength < -1 || - (destCapacity<0) || (dest == NULL && destCapacity > 0) - ) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - if(srcLength < 0) { - /* Transform a NUL-terminated string. */ - UChar *pDestLimit = (dest!=NULL)?(dest+destCapacity):NULL; - uint8_t t1, t2, t3; /* trail bytes */ - - while(((ch = *pSrc) != 0) && (pDest < pDestLimit)) { - if(ch < 0xc0) { - /* - * ASCII, or a trail byte in lead position which is treated like - * a single-byte sequence for better character boundary - * resynchronization after illegal sequences. - */ - *pDest++=(UChar)ch; - ++pSrc; - continue; - } else if(ch < 0xe0) { /* U+0080..U+07FF */ - if((t1 = pSrc[1]) != 0) { - /* 0x3080 = (0xc0 << 6) + 0x80 */ - *pDest++ = (UChar)((ch << 6) + t1 - 0x3080); - pSrc += 2; - continue; - } - } else if(ch < 0xf0) { /* U+0800..U+FFFF */ - if((t1 = pSrc[1]) != 0 && (t2 = pSrc[2]) != 0) { - /* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (UChar) */ - /* 0x2080 = (0x80 << 6) + 0x80 */ - *pDest++ = (UChar)((ch << 12) + (t1 << 6) + t2 - 0x2080); - pSrc += 3; - continue; - } - } else /* f0..f4 */ { /* U+10000..U+10FFFF */ - if((t1 = pSrc[1]) != 0 && (t2 = pSrc[2]) != 0 && (t3 = pSrc[3]) != 0) { - pSrc += 4; - /* 0x3c82080 = (0xf0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */ - ch = (ch << 18) + (t1 << 12) + (t2 << 6) + t3 - 0x3c82080; - *(pDest++) = U16_LEAD(ch); - if(pDest < pDestLimit) { - *(pDest++) = U16_TRAIL(ch); - } else { - reqLength = 1; - break; - } - continue; - } - } - - /* truncated character at the end */ - *pDest++ = 0xfffd; - while(*++pSrc != 0) {} - break; - } - - /* Pre-flight the rest of the string. */ - while((ch = *pSrc) != 0) { - if(ch < 0xc0) { - /* - * ASCII, or a trail byte in lead position which is treated like - * a single-byte sequence for better character boundary - * resynchronization after illegal sequences. - */ - ++reqLength; - ++pSrc; - continue; - } else if(ch < 0xe0) { /* U+0080..U+07FF */ - if(pSrc[1] != 0) { - ++reqLength; - pSrc += 2; - continue; - } - } else if(ch < 0xf0) { /* U+0800..U+FFFF */ - if(pSrc[1] != 0 && pSrc[2] != 0) { - ++reqLength; - pSrc += 3; - continue; - } - } else /* f0..f4 */ { /* U+10000..U+10FFFF */ - if(pSrc[1] != 0 && pSrc[2] != 0 && pSrc[3] != 0) { - reqLength += 2; - pSrc += 4; - continue; - } - } - - /* truncated character at the end */ - ++reqLength; - break; - } - } else /* srcLength >= 0 */ { - const uint8_t *pSrcLimit = (pSrc!=NULL)?(pSrc + srcLength):NULL; - - /* - * This function requires that if srcLength is given, then it must be - * destCapatity >= srcLength so that we need not check for - * destination buffer overflow in the loop. - */ - if(destCapacity < srcLength) { - if(pDestLength != NULL) { - *pDestLength = srcLength; /* this likely overestimates the true destLength! */ - } - *pErrorCode = U_BUFFER_OVERFLOW_ERROR; - return NULL; - } - - if((pSrcLimit - pSrc) >= 4) { - pSrcLimit -= 3; /* temporarily reduce pSrcLimit */ - - /* in this loop, we can always access at least 4 bytes, up to pSrc+3 */ - do { - ch = *pSrc++; - if(ch < 0xc0) { - /* - * ASCII, or a trail byte in lead position which is treated like - * a single-byte sequence for better character boundary - * resynchronization after illegal sequences. - */ - *pDest++=(UChar)ch; - } else if(ch < 0xe0) { /* U+0080..U+07FF */ - /* 0x3080 = (0xc0 << 6) + 0x80 */ - *pDest++ = (UChar)((ch << 6) + *pSrc++ - 0x3080); - } else if(ch < 0xf0) { /* U+0800..U+FFFF */ - /* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (UChar) */ - /* 0x2080 = (0x80 << 6) + 0x80 */ - ch = (ch << 12) + (*pSrc++ << 6); - *pDest++ = (UChar)(ch + *pSrc++ - 0x2080); - } else /* f0..f4 */ { /* U+10000..U+10FFFF */ - /* 0x3c82080 = (0xf0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */ - ch = (ch << 18) + (*pSrc++ << 12); - ch += *pSrc++ << 6; - ch += *pSrc++ - 0x3c82080; - *(pDest++) = U16_LEAD(ch); - *(pDest++) = U16_TRAIL(ch); - } - } while(pSrc < pSrcLimit); - - pSrcLimit += 3; /* restore original pSrcLimit */ - } - - while(pSrc < pSrcLimit) { - ch = *pSrc++; - if(ch < 0xc0) { - /* - * ASCII, or a trail byte in lead position which is treated like - * a single-byte sequence for better character boundary - * resynchronization after illegal sequences. - */ - *pDest++=(UChar)ch; - continue; - } else if(ch < 0xe0) { /* U+0080..U+07FF */ - if(pSrc < pSrcLimit) { - /* 0x3080 = (0xc0 << 6) + 0x80 */ - *pDest++ = (UChar)((ch << 6) + *pSrc++ - 0x3080); - continue; - } - } else if(ch < 0xf0) { /* U+0800..U+FFFF */ - if((pSrcLimit - pSrc) >= 2) { - /* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (UChar) */ - /* 0x2080 = (0x80 << 6) + 0x80 */ - ch = (ch << 12) + (*pSrc++ << 6); - *pDest++ = (UChar)(ch + *pSrc++ - 0x2080); - pSrc += 3; - continue; - } - } else /* f0..f4 */ { /* U+10000..U+10FFFF */ - if((pSrcLimit - pSrc) >= 3) { - /* 0x3c82080 = (0xf0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */ - ch = (ch << 18) + (*pSrc++ << 12); - ch += *pSrc++ << 6; - ch += *pSrc++ - 0x3c82080; - *(pDest++) = U16_LEAD(ch); - *(pDest++) = U16_TRAIL(ch); - pSrc += 4; - continue; - } - } - - /* truncated character at the end */ - *pDest++ = 0xfffd; - break; - } - } - - reqLength+=(int32_t)(pDest - dest); - - if(pDestLength){ - *pDestLength = reqLength; - } - - /* Terminate the buffer */ - u_terminateUChars(dest,destCapacity,reqLength,pErrorCode); - - return dest; -} - -static inline uint8_t * -_appendUTF8(uint8_t *pDest, UChar32 c) { - /* it is 0<=c<=0x10ffff and not a surrogate if called by a validating function */ - if((c)<=0x7f) { - *pDest++=(uint8_t)c; - } else if(c<=0x7ff) { - *pDest++=(uint8_t)((c>>6)|0xc0); - *pDest++=(uint8_t)((c&0x3f)|0x80); - } else if(c<=0xffff) { - *pDest++=(uint8_t)((c>>12)|0xe0); - *pDest++=(uint8_t)(((c>>6)&0x3f)|0x80); - *pDest++=(uint8_t)(((c)&0x3f)|0x80); - } else /* if((uint32_t)(c)<=0x10ffff) */ { - *pDest++=(uint8_t)(((c)>>18)|0xf0); - *pDest++=(uint8_t)((((c)>>12)&0x3f)|0x80); - *pDest++=(uint8_t)((((c)>>6)&0x3f)|0x80); - *pDest++=(uint8_t)(((c)&0x3f)|0x80); - } - return pDest; -} - - -U_CAPI char* U_EXPORT2 -u_strToUTF8WithSub(char *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar *pSrc, - int32_t srcLength, - UChar32 subchar, int32_t *pNumSubstitutions, - UErrorCode *pErrorCode){ - int32_t reqLength=0; - uint32_t ch=0,ch2=0; - uint8_t *pDest = (uint8_t *)dest; - uint8_t *pDestLimit = (pDest!=NULL)?(pDest + destCapacity):NULL; - int32_t numSubstitutions; - - /* args check */ - if(U_FAILURE(*pErrorCode)){ - return NULL; - } - - if( (pSrc==NULL && srcLength!=0) || srcLength < -1 || - (destCapacity<0) || (dest == NULL && destCapacity > 0) || - subchar > 0x10ffff || U_IS_SURROGATE(subchar) - ) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - if(pNumSubstitutions!=NULL) { - *pNumSubstitutions=0; - } - numSubstitutions=0; - - if(srcLength==-1) { - while((ch=*pSrc)!=0) { - ++pSrc; - if(ch <= 0x7f) { - if(pDest= 2) { - *pDest++=(uint8_t)((ch>>6)|0xc0); - *pDest++=(uint8_t)((ch&0x3f)|0x80); - } else { - reqLength = 2; - break; - } - } else if(ch <= 0xd7ff || ch >= 0xe000) { - if((pDestLimit - pDest) >= 3) { - *pDest++=(uint8_t)((ch>>12)|0xe0); - *pDest++=(uint8_t)(((ch>>6)&0x3f)|0x80); - *pDest++=(uint8_t)((ch&0x3f)|0x80); - } else { - reqLength = 3; - break; - } - } else /* ch is a surrogate */ { - int32_t length; - - /*need not check for NUL because NUL fails U16_IS_TRAIL() anyway*/ - if(U16_IS_SURROGATE_LEAD(ch) && U16_IS_TRAIL(ch2=*pSrc)) { - ++pSrc; - ch=U16_GET_SUPPLEMENTARY(ch, ch2); - } else if(subchar>=0) { - ch=subchar; - ++numSubstitutions; - } else { - /* Unicode 3.2 forbids surrogate code points in UTF-8 */ - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } - - length = U8_LENGTH(ch); - if((pDestLimit - pDest) >= length) { - /* convert and append*/ - pDest=_appendUTF8(pDest, ch); - } else { - reqLength = length; - break; - } - } - } - while((ch=*pSrc++)!=0) { - if(ch<=0x7f) { - ++reqLength; - } else if(ch<=0x7ff) { - reqLength+=2; - } else if(!U16_IS_SURROGATE(ch)) { - reqLength+=3; - } else if(U16_IS_SURROGATE_LEAD(ch) && U16_IS_TRAIL(ch2=*pSrc)) { - ++pSrc; - reqLength+=4; - } else if(subchar>=0) { - reqLength+=U8_LENGTH(subchar); - ++numSubstitutions; - } else { - /* Unicode 3.2 forbids surrogate code points in UTF-8 */ - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } - } - } else { - const UChar *pSrcLimit = (pSrc!=NULL)?(pSrc+srcLength):NULL; - int32_t count; - - /* Faster loop without ongoing checking for pSrcLimit and pDestLimit. */ - for(;;) { - /* - * Each iteration of the inner loop progresses by at most 3 UTF-8 - * bytes and one UChar, for most characters. - * For supplementary code points (4 & 2), which are rare, - * there is an additional adjustment. - */ - count = (int32_t)((pDestLimit - pDest) / 3); - srcLength = (int32_t)(pSrcLimit - pSrc); - if(count > srcLength) { - count = srcLength; /* min(remaining dest/3, remaining src) */ - } - if(count < 3) { - /* - * Too much overhead if we get near the end of the string, - * continue with the next loop. - */ - break; - } - do { - ch=*pSrc++; - if(ch <= 0x7f) { - *pDest++ = (uint8_t)ch; - } else if(ch <= 0x7ff) { - *pDest++=(uint8_t)((ch>>6)|0xc0); - *pDest++=(uint8_t)((ch&0x3f)|0x80); - } else if(ch <= 0xd7ff || ch >= 0xe000) { - *pDest++=(uint8_t)((ch>>12)|0xe0); - *pDest++=(uint8_t)(((ch>>6)&0x3f)|0x80); - *pDest++=(uint8_t)((ch&0x3f)|0x80); - } else /* ch is a surrogate */ { - /* - * We will read two UChars and probably output four bytes, - * which we didn't account for with computing count, - * so we adjust it here. - */ - if(--count == 0) { - --pSrc; /* undo ch=*pSrc++ for the lead surrogate */ - break; /* recompute count */ - } - - if(U16_IS_SURROGATE_LEAD(ch) && U16_IS_TRAIL(ch2=*pSrc)) { - ++pSrc; - ch=U16_GET_SUPPLEMENTARY(ch, ch2); - - /* writing 4 bytes per 2 UChars is ok */ - *pDest++=(uint8_t)((ch>>18)|0xf0); - *pDest++=(uint8_t)(((ch>>12)&0x3f)|0x80); - *pDest++=(uint8_t)(((ch>>6)&0x3f)|0x80); - *pDest++=(uint8_t)((ch&0x3f)|0x80); - } else { - /* Unicode 3.2 forbids surrogate code points in UTF-8 */ - if(subchar>=0) { - ch=subchar; - ++numSubstitutions; - } else { - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } - - /* convert and append*/ - pDest=_appendUTF8(pDest, ch); - } - } - } while(--count > 0); - } - - while(pSrc= 2) { - *pDest++=(uint8_t)((ch>>6)|0xc0); - *pDest++=(uint8_t)((ch&0x3f)|0x80); - } else { - reqLength = 2; - break; - } - } else if(ch <= 0xd7ff || ch >= 0xe000) { - if((pDestLimit - pDest) >= 3) { - *pDest++=(uint8_t)((ch>>12)|0xe0); - *pDest++=(uint8_t)(((ch>>6)&0x3f)|0x80); - *pDest++=(uint8_t)((ch&0x3f)|0x80); - } else { - reqLength = 3; - break; - } - } else /* ch is a surrogate */ { - int32_t length; - - if(U16_IS_SURROGATE_LEAD(ch) && pSrc=0) { - ch=subchar; - ++numSubstitutions; - } else { - /* Unicode 3.2 forbids surrogate code points in UTF-8 */ - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } - - length = U8_LENGTH(ch); - if((pDestLimit - pDest) >= length) { - /* convert and append*/ - pDest=_appendUTF8(pDest, ch); - } else { - reqLength = length; - break; - } - } - } - while(pSrc=0) { - reqLength+=U8_LENGTH(subchar); - ++numSubstitutions; - } else { - /* Unicode 3.2 forbids surrogate code points in UTF-8 */ - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } - } - } - - reqLength+=(int32_t)(pDest - (uint8_t *)dest); - - if(pNumSubstitutions!=NULL) { - *pNumSubstitutions=numSubstitutions; - } - - if(pDestLength){ - *pDestLength = reqLength; - } - - /* Terminate the buffer */ - u_terminateChars(dest, destCapacity, reqLength, pErrorCode); - return dest; -} - -U_CAPI char* U_EXPORT2 -u_strToUTF8(char *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar *pSrc, - int32_t srcLength, - UErrorCode *pErrorCode){ - return u_strToUTF8WithSub( - dest, destCapacity, pDestLength, - pSrc, srcLength, - U_SENTINEL, NULL, - pErrorCode); -} - -U_CAPI UChar* U_EXPORT2 -u_strFromJavaModifiedUTF8WithSub( - UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const char *src, - int32_t srcLength, - UChar32 subchar, int32_t *pNumSubstitutions, - UErrorCode *pErrorCode) { - /* args check */ - if(U_FAILURE(*pErrorCode)) { - return NULL; - } - if( (src==NULL && srcLength!=0) || srcLength < -1 || - (dest==NULL && destCapacity!=0) || destCapacity<0 || - subchar > 0x10ffff || U_IS_SURROGATE(subchar) - ) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - if(pNumSubstitutions!=NULL) { - *pNumSubstitutions=0; - } - UChar *pDest = dest; - UChar *pDestLimit = dest+destCapacity; - int32_t reqLength = 0; - int32_t numSubstitutions=0; - - if(srcLength < 0) { - /* - * Transform a NUL-terminated ASCII string. - * Handle non-ASCII strings with slower code. - */ - UChar32 c; - while(((c = (uint8_t)*src) != 0) && c <= 0x7f && (pDest < pDestLimit)) { - *pDest++=(UChar)c; - ++src; - } - if(c == 0) { - reqLength=(int32_t)(pDest - dest); - if(pDestLength) { - *pDestLength = reqLength; - } - - /* Terminate the buffer */ - u_terminateUChars(dest, destCapacity, reqLength, pErrorCode); - return dest; - } - srcLength = static_cast(uprv_strlen(src)); - } - - /* Faster loop without ongoing checking for srcLength and pDestLimit. */ - UChar32 ch; - uint8_t t1, t2; - int32_t i = 0; - for(;;) { - int32_t count = (int32_t)(pDestLimit - pDest); - int32_t count2 = srcLength - i; - if(count >= count2 && srcLength > 0 && U8_IS_SINGLE(*src)) { - /* fast ASCII loop */ - int32_t start = i; - uint8_t b; - while(i < srcLength && U8_IS_SINGLE(b = src[i])) { - *pDest++=b; - ++i; - } - int32_t delta = i - start; - count -= delta; - count2 -= delta; - } - /* - * Each iteration of the inner loop progresses by at most 3 UTF-8 - * bytes and one UChar. - */ - if(subchar > 0xFFFF) { - break; - } - count2 /= 3; - if(count > count2) { - count = count2; /* min(remaining dest, remaining src/3) */ - } - if(count < 3) { - /* - * Too much overhead if we get near the end of the string, - * continue with the next loop. - */ - break; - } - do { - ch = (uint8_t)src[i++]; - if(U8_IS_SINGLE(ch)) { - *pDest++=(UChar)ch; - } else { - if(ch >= 0xe0) { - if( /* handle U+0000..U+FFFF inline */ - ch <= 0xef && - (t1 = (uint8_t)(src[i] - 0x80)) <= 0x3f && - (t2 = (uint8_t)(src[i+1] - 0x80)) <= 0x3f - ) { - /* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (UChar) */ - *pDest++ = (UChar)((ch << 12) | (t1 << 6) | t2); - i += 2; - continue; - } - } else { - if( /* handle U+0000..U+07FF inline */ - ch >= 0xc0 && - (t1 = (uint8_t)(src[i] - 0x80)) <= 0x3f - ) { - *pDest++ = (UChar)(((ch & 0x1f) << 6) | t1); - ++i; - continue; - } - } - - if(subchar < 0) { - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } else if(subchar > 0xffff && --count == 0) { - /* - * We need to write two UChars, adjusted count for that, - * and ran out of space. - */ - --i; // back out byte ch - break; - } else { - /* function call for error cases */ - utf8_nextCharSafeBody((const uint8_t *)src, &(i), srcLength, ch, -1); - ++numSubstitutions; - *(pDest++)=(UChar)subchar; - } - } - } while(--count > 0); - } - - while(i < srcLength && (pDest < pDestLimit)) { - ch = (uint8_t)src[i++]; - if(U8_IS_SINGLE(ch)){ - *pDest++=(UChar)ch; - } else { - if(ch >= 0xe0) { - if( /* handle U+0000..U+FFFF inline */ - ch <= 0xef && - (i+1) < srcLength && - (t1 = (uint8_t)(src[i] - 0x80)) <= 0x3f && - (t2 = (uint8_t)(src[i+1] - 0x80)) <= 0x3f - ) { - /* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (UChar) */ - *pDest++ = (UChar)((ch << 12) | (t1 << 6) | t2); - i += 2; - continue; - } - } else { - if( /* handle U+0000..U+07FF inline */ - ch >= 0xc0 && - i < srcLength && - (t1 = (uint8_t)(src[i] - 0x80)) <= 0x3f - ) { - *pDest++ = (UChar)(((ch & 0x1f) << 6) | t1); - ++i; - continue; - } - } - - if(subchar < 0) { - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } else { - /* function call for error cases */ - utf8_nextCharSafeBody((const uint8_t *)src, &(i), srcLength, ch, -1); - ++numSubstitutions; - if(subchar<=0xFFFF) { - *(pDest++)=(UChar)subchar; - } else { - *(pDest++)=U16_LEAD(subchar); - if(pDest= 0xe0) { - if( /* handle U+0000..U+FFFF inline */ - ch <= 0xef && - (i+1) < srcLength && - (uint8_t)(src[i] - 0x80) <= 0x3f && - (uint8_t)(src[i+1] - 0x80) <= 0x3f - ) { - reqLength++; - i += 2; - continue; - } - } else { - if( /* handle U+0000..U+07FF inline */ - ch >= 0xc0 && - i < srcLength && - (uint8_t)(src[i] - 0x80) <= 0x3f - ) { - reqLength++; - ++i; - continue; - } - } - - if(subchar < 0) { - *pErrorCode = U_INVALID_CHAR_FOUND; - return NULL; - } else { - /* function call for error cases */ - utf8_nextCharSafeBody((const uint8_t *)src, &(i), srcLength, ch, -1); - ++numSubstitutions; - reqLength+=U16_LENGTH(ch); - } - } - } - - if(pNumSubstitutions!=NULL) { - *pNumSubstitutions=numSubstitutions; - } - - reqLength+=(int32_t)(pDest - dest); - if(pDestLength) { - *pDestLength = reqLength; - } - - /* Terminate the buffer */ - u_terminateUChars(dest, destCapacity, reqLength, pErrorCode); - return dest; -} - -U_CAPI char* U_EXPORT2 -u_strToJavaModifiedUTF8( - char *dest, - int32_t destCapacity, - int32_t *pDestLength, - const UChar *src, - int32_t srcLength, - UErrorCode *pErrorCode) { - int32_t reqLength=0; - uint32_t ch=0; - uint8_t *pDest = (uint8_t *)dest; - uint8_t *pDestLimit = pDest + destCapacity; - const UChar *pSrcLimit; - int32_t count; - - /* args check */ - if(U_FAILURE(*pErrorCode)){ - return NULL; - } - if( (src==NULL && srcLength!=0) || srcLength < -1 || - (dest==NULL && destCapacity!=0) || destCapacity<0 - ) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - if(srcLength==-1) { - /* Convert NUL-terminated ASCII, then find the string length. */ - while((ch=*src)<=0x7f && ch != 0 && pDest= srcLength && srcLength > 0 && *src <= 0x7f) { - /* fast ASCII loop */ - const UChar *prevSrc = src; - int32_t delta; - while(src < pSrcLimit && (ch = *src) <= 0x7f && ch != 0) { - *pDest++=(uint8_t)ch; - ++src; - } - delta = (int32_t)(src - prevSrc); - count -= delta; - srcLength -= delta; - } - /* - * Each iteration of the inner loop progresses by at most 3 UTF-8 - * bytes and one UChar. - */ - count /= 3; - if(count > srcLength) { - count = srcLength; /* min(remaining dest/3, remaining src) */ - } - if(count < 3) { - /* - * Too much overhead if we get near the end of the string, - * continue with the next loop. - */ - break; - } - do { - ch=*src++; - if(ch <= 0x7f && ch != 0) { - *pDest++ = (uint8_t)ch; - } else if(ch <= 0x7ff) { - *pDest++=(uint8_t)((ch>>6)|0xc0); - *pDest++=(uint8_t)((ch&0x3f)|0x80); - } else { - *pDest++=(uint8_t)((ch>>12)|0xe0); - *pDest++=(uint8_t)(((ch>>6)&0x3f)|0x80); - *pDest++=(uint8_t)((ch&0x3f)|0x80); - } - } while(--count > 0); - } - - while(src= 2) { - *pDest++=(uint8_t)((ch>>6)|0xc0); - *pDest++=(uint8_t)((ch&0x3f)|0x80); - } else { - reqLength = 2; - break; - } - } else { - if((pDestLimit - pDest) >= 3) { - *pDest++=(uint8_t)((ch>>12)|0xe0); - *pDest++=(uint8_t)(((ch>>6)&0x3f)|0x80); - *pDest++=(uint8_t)((ch&0x3f)|0x80); - } else { - reqLength = 3; - break; - } - } - } - while(src 0) || + subchar > 0x10ffff || U_IS_SURROGATE(subchar) + ) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + if(pNumSubstitutions != nullptr) { + *pNumSubstitutions = 0; + } + + pDest = dest; + destLimit = (dest!=nullptr)?(dest + destCapacity):nullptr; + reqLength = 0; + numSubstitutions = 0; + + if(srcLength < 0) { + /* simple loop for conversion of a NUL-terminated BMP string */ + while((ch=*src) != 0 && + ((uint32_t)ch < 0xd800 || (0xe000 <= ch && ch <= 0xffff))) { + ++src; + if(pDest < destLimit) { + *pDest++ = (char16_t)ch; + } else { + ++reqLength; + } + } + srcLimit = src; + if(ch != 0) { + /* "complicated" case, find the end of the remaining string */ + while(*++srcLimit != 0) {} + } + } else { + srcLimit = (src!=nullptr)?(src + srcLength):nullptr; + } + + /* convert with length */ + while(src < srcLimit) { + ch = *src++; + do { + /* usually "loops" once; twice only for writing subchar */ + if((uint32_t)ch < 0xd800 || (0xe000 <= ch && ch <= 0xffff)) { + if(pDest < destLimit) { + *pDest++ = (char16_t)ch; + } else { + ++reqLength; + } + break; + } else if(0x10000 <= ch && ch <= 0x10ffff) { + if(pDest!=nullptr && ((pDest + 2) <= destLimit)) { + *pDest++ = U16_LEAD(ch); + *pDest++ = U16_TRAIL(ch); + } else { + reqLength += 2; + } + break; + } else if((ch = subchar) < 0) { + /* surrogate code point, or not a Unicode code point at all */ + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } else { + ++numSubstitutions; + } + } while(true); + } + + reqLength += (int32_t)(pDest - dest); + if(pDestLength) { + *pDestLength = reqLength; + } + if(pNumSubstitutions != nullptr) { + *pNumSubstitutions = numSubstitutions; + } + + /* Terminate the buffer */ + u_terminateUChars(dest, destCapacity, reqLength, pErrorCode); + + return dest; +} + +U_CAPI char16_t* U_EXPORT2 +u_strFromUTF32(char16_t *dest, + int32_t destCapacity, + int32_t *pDestLength, + const UChar32 *src, + int32_t srcLength, + UErrorCode *pErrorCode) { + return u_strFromUTF32WithSub( + dest, destCapacity, pDestLength, + src, srcLength, + U_SENTINEL, nullptr, + pErrorCode); +} + +U_CAPI UChar32* U_EXPORT2 +u_strToUTF32WithSub(UChar32 *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char16_t *src, + int32_t srcLength, + UChar32 subchar, int32_t *pNumSubstitutions, + UErrorCode *pErrorCode) { + const char16_t *srcLimit; + UChar32 ch; + char16_t ch2; + UChar32 *destLimit; + UChar32 *pDest; + int32_t reqLength; + int32_t numSubstitutions; + + /* args check */ + if(U_FAILURE(*pErrorCode)){ + return nullptr; + } + if( (src==nullptr && srcLength!=0) || srcLength < -1 || + (destCapacity<0) || (dest == nullptr && destCapacity > 0) || + subchar > 0x10ffff || U_IS_SURROGATE(subchar) + ) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + if(pNumSubstitutions != nullptr) { + *pNumSubstitutions = 0; + } + + pDest = dest; + destLimit = (dest!=nullptr)?(dest + destCapacity):nullptr; + reqLength = 0; + numSubstitutions = 0; + + if(srcLength < 0) { + /* simple loop for conversion of a NUL-terminated BMP string */ + while((ch=*src) != 0 && !U16_IS_SURROGATE(ch)) { + ++src; + if(pDest < destLimit) { + *pDest++ = ch; + } else { + ++reqLength; + } + } + srcLimit = src; + if(ch != 0) { + /* "complicated" case, find the end of the remaining string */ + while(*++srcLimit != 0) {} + } + } else { + srcLimit = (src!=nullptr)?(src + srcLength):nullptr; + } + + /* convert with length */ + while(src < srcLimit) { + ch = *src++; + if(!U16_IS_SURROGATE(ch)) { + /* write or count ch below */ + } else if(U16_IS_SURROGATE_LEAD(ch) && src < srcLimit && U16_IS_TRAIL(ch2 = *src)) { + ++src; + ch = U16_GET_SUPPLEMENTARY(ch, ch2); + } else if((ch = subchar) < 0) { + /* unpaired surrogate */ + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } else { + ++numSubstitutions; + } + if(pDest < destLimit) { + *pDest++ = ch; + } else { + ++reqLength; + } + } + + reqLength += (int32_t)(pDest - dest); + if(pDestLength) { + *pDestLength = reqLength; + } + if(pNumSubstitutions != nullptr) { + *pNumSubstitutions = numSubstitutions; + } + + /* Terminate the buffer */ + u_terminateUChar32s(dest, destCapacity, reqLength, pErrorCode); + + return dest; +} + +U_CAPI UChar32* U_EXPORT2 +u_strToUTF32(UChar32 *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char16_t *src, + int32_t srcLength, + UErrorCode *pErrorCode) { + return u_strToUTF32WithSub( + dest, destCapacity, pDestLength, + src, srcLength, + U_SENTINEL, nullptr, + pErrorCode); +} + +U_CAPI char16_t* U_EXPORT2 +u_strFromUTF8WithSub(char16_t *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char* src, + int32_t srcLength, + UChar32 subchar, int32_t *pNumSubstitutions, + UErrorCode *pErrorCode){ + /* args check */ + if(U_FAILURE(*pErrorCode)) { + return nullptr; + } + if( (src==nullptr && srcLength!=0) || srcLength < -1 || + (destCapacity<0) || (dest == nullptr && destCapacity > 0) || + subchar > 0x10ffff || U_IS_SURROGATE(subchar) + ) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + if(pNumSubstitutions!=nullptr) { + *pNumSubstitutions=0; + } + char16_t *pDest = dest; + char16_t *pDestLimit = dest+destCapacity; + int32_t reqLength = 0; + int32_t numSubstitutions=0; + + /* + * Inline processing of UTF-8 byte sequences: + * + * Byte sequences for the most common characters are handled inline in + * the conversion loops. In order to reduce the path lengths for those + * characters, the tests are arranged in a kind of binary search. + * ASCII (<=0x7f) is checked first, followed by the dividing point + * between 2- and 3-byte sequences (0xe0). + * The 3-byte branch is tested first to speed up CJK text. + * The compiler should combine the subtractions for the two tests for 0xe0. + * Each branch then tests for the other end of its range. + */ + + if(srcLength < 0){ + /* + * Transform a NUL-terminated string. + * The code explicitly checks for NULs only in the lead byte position. + * A NUL byte in the trail byte position fails the trail byte range check anyway. + */ + int32_t i; + UChar32 c; + for(i = 0; (c = (uint8_t)src[i]) != 0 && (pDest < pDestLimit);) { + // modified copy of U8_NEXT() + ++i; + if(U8_IS_SINGLE(c)) { + *pDest++=(char16_t)c; + } else { + uint8_t __t1, __t2; + if( /* handle U+0800..U+FFFF inline */ + (0xe0<=(c) && (c)<0xf0) && + U8_IS_VALID_LEAD3_AND_T1((c), src[i]) && + (__t2=src[(i)+1]-0x80)<=0x3f) { + *pDest++ = (((c)&0xf)<<12)|((src[i]&0x3f)<<6)|__t2; + i+=2; + } else if( /* handle U+0080..U+07FF inline */ + ((c)<0xe0 && (c)>=0xc2) && + (__t1=src[i]-0x80)<=0x3f) { + *pDest++ = (((c)&0x1f)<<6)|__t1; + ++(i); + } else { + /* function call for "complicated" and error cases */ + (c)=utf8_nextCharSafeBody((const uint8_t *)src, &(i), -1, c, -1); + if(c<0 && (++numSubstitutions, c = subchar) < 0) { + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } else if(c<=0xFFFF) { + *(pDest++)=(char16_t)c; + } else { + *(pDest++)=U16_LEAD(c); + if(pDest=0xc2) && + (__t1=src[i]-0x80)<=0x3f) { + ++reqLength; + ++(i); + } else { + /* function call for "complicated" and error cases */ + (c)=utf8_nextCharSafeBody((const uint8_t *)src, &(i), -1, c, -1); + if(c<0 && (++numSubstitutions, c = subchar) < 0) { + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } + reqLength += U16_LENGTH(c); + } + } + } + } else /* srcLength >= 0 */ { + /* Faster loop without ongoing checking for srcLength and pDestLimit. */ + int32_t i = 0; + UChar32 c; + for(;;) { + /* + * Each iteration of the inner loop progresses by at most 3 UTF-8 + * bytes and one char16_t, for most characters. + * For supplementary code points (4 & 2), which are rare, + * there is an additional adjustment. + */ + int32_t count = (int32_t)(pDestLimit - pDest); + int32_t count2 = (srcLength - i) / 3; + if(count > count2) { + count = count2; /* min(remaining dest, remaining src/3) */ + } + if(count < 3) { + /* + * Too much overhead if we get near the end of the string, + * continue with the next loop. + */ + break; + } + + do { + // modified copy of U8_NEXT() + c = (uint8_t)src[i++]; + if(U8_IS_SINGLE(c)) { + *pDest++=(char16_t)c; + } else { + uint8_t __t1, __t2; + if( /* handle U+0800..U+FFFF inline */ + (0xe0<=(c) && (c)<0xf0) && + ((i)+1)=0xc2) && + ((i)!=srcLength) && + (__t1=src[i]-0x80)<=0x3f) { + *pDest++ = (((c)&0x1f)<<6)|__t1; + ++(i); + } else { + if(c >= 0xf0 || subchar > 0xffff) { + // We may read up to four bytes and write up to two UChars, + // which we didn't account for with computing count, + // so we adjust it here. + if(--count == 0) { + --i; // back out byte c + break; + } + } + + /* function call for "complicated" and error cases */ + (c)=utf8_nextCharSafeBody((const uint8_t *)src, &(i), srcLength, c, -1); + if(c<0 && (++numSubstitutions, c = subchar) < 0) { + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } else if(c<=0xFFFF) { + *(pDest++)=(char16_t)c; + } else { + *(pDest++)=U16_LEAD(c); + *(pDest++)=U16_TRAIL(c); + } + } + } + } while(--count > 0); + } + + while(i < srcLength && (pDest < pDestLimit)) { + // modified copy of U8_NEXT() + c = (uint8_t)src[i++]; + if(U8_IS_SINGLE(c)) { + *pDest++=(char16_t)c; + } else { + uint8_t __t1, __t2; + if( /* handle U+0800..U+FFFF inline */ + (0xe0<=(c) && (c)<0xf0) && + ((i)+1)=0xc2) && + ((i)!=srcLength) && + (__t1=src[i]-0x80)<=0x3f) { + *pDest++ = (((c)&0x1f)<<6)|__t1; + ++(i); + } else { + /* function call for "complicated" and error cases */ + (c)=utf8_nextCharSafeBody((const uint8_t *)src, &(i), srcLength, c, -1); + if(c<0 && (++numSubstitutions, c = subchar) < 0) { + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } else if(c<=0xFFFF) { + *(pDest++)=(char16_t)c; + } else { + *(pDest++)=U16_LEAD(c); + if(pDest=0xc2) && + ((i)!=srcLength) && + (__t1=src[i]-0x80)<=0x3f) { + ++reqLength; + ++(i); + } else { + /* function call for "complicated" and error cases */ + (c)=utf8_nextCharSafeBody((const uint8_t *)src, &(i), srcLength, c, -1); + if(c<0 && (++numSubstitutions, c = subchar) < 0) { + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } + reqLength += U16_LENGTH(c); + } + } + } + } + + reqLength+=(int32_t)(pDest - dest); + + if(pNumSubstitutions!=nullptr) { + *pNumSubstitutions=numSubstitutions; + } + + if(pDestLength){ + *pDestLength = reqLength; + } + + /* Terminate the buffer */ + u_terminateUChars(dest,destCapacity,reqLength,pErrorCode); + + return dest; +} + +U_CAPI char16_t* U_EXPORT2 +u_strFromUTF8(char16_t *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char* src, + int32_t srcLength, + UErrorCode *pErrorCode){ + return u_strFromUTF8WithSub( + dest, destCapacity, pDestLength, + src, srcLength, + U_SENTINEL, nullptr, + pErrorCode); +} + +U_CAPI char16_t * U_EXPORT2 +u_strFromUTF8Lenient(char16_t *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char *src, + int32_t srcLength, + UErrorCode *pErrorCode) { + char16_t *pDest = dest; + UChar32 ch; + int32_t reqLength = 0; + uint8_t* pSrc = (uint8_t*) src; + + /* args check */ + if(U_FAILURE(*pErrorCode)){ + return nullptr; + } + + if( (src==nullptr && srcLength!=0) || srcLength < -1 || + (destCapacity<0) || (dest == nullptr && destCapacity > 0) + ) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + if(srcLength < 0) { + /* Transform a NUL-terminated string. */ + char16_t *pDestLimit = (dest!=nullptr)?(dest+destCapacity):nullptr; + uint8_t t1, t2, t3; /* trail bytes */ + + while(((ch = *pSrc) != 0) && (pDest < pDestLimit)) { + if(ch < 0xc0) { + /* + * ASCII, or a trail byte in lead position which is treated like + * a single-byte sequence for better character boundary + * resynchronization after illegal sequences. + */ + *pDest++=(char16_t)ch; + ++pSrc; + continue; + } else if(ch < 0xe0) { /* U+0080..U+07FF */ + if((t1 = pSrc[1]) != 0) { + /* 0x3080 = (0xc0 << 6) + 0x80 */ + *pDest++ = (char16_t)((ch << 6) + t1 - 0x3080); + pSrc += 2; + continue; + } + } else if(ch < 0xf0) { /* U+0800..U+FFFF */ + if((t1 = pSrc[1]) != 0 && (t2 = pSrc[2]) != 0) { + /* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (char16_t) */ + /* 0x2080 = (0x80 << 6) + 0x80 */ + *pDest++ = (char16_t)((ch << 12) + (t1 << 6) + t2 - 0x2080); + pSrc += 3; + continue; + } + } else /* f0..f4 */ { /* U+10000..U+10FFFF */ + if((t1 = pSrc[1]) != 0 && (t2 = pSrc[2]) != 0 && (t3 = pSrc[3]) != 0) { + pSrc += 4; + /* 0x3c82080 = (0xf0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */ + ch = (ch << 18) + (t1 << 12) + (t2 << 6) + t3 - 0x3c82080; + *(pDest++) = U16_LEAD(ch); + if(pDest < pDestLimit) { + *(pDest++) = U16_TRAIL(ch); + } else { + reqLength = 1; + break; + } + continue; + } + } + + /* truncated character at the end */ + *pDest++ = 0xfffd; + while(*++pSrc != 0) {} + break; + } + + /* Pre-flight the rest of the string. */ + while((ch = *pSrc) != 0) { + if(ch < 0xc0) { + /* + * ASCII, or a trail byte in lead position which is treated like + * a single-byte sequence for better character boundary + * resynchronization after illegal sequences. + */ + ++reqLength; + ++pSrc; + continue; + } else if(ch < 0xe0) { /* U+0080..U+07FF */ + if(pSrc[1] != 0) { + ++reqLength; + pSrc += 2; + continue; + } + } else if(ch < 0xf0) { /* U+0800..U+FFFF */ + if(pSrc[1] != 0 && pSrc[2] != 0) { + ++reqLength; + pSrc += 3; + continue; + } + } else /* f0..f4 */ { /* U+10000..U+10FFFF */ + if(pSrc[1] != 0 && pSrc[2] != 0 && pSrc[3] != 0) { + reqLength += 2; + pSrc += 4; + continue; + } + } + + /* truncated character at the end */ + ++reqLength; + break; + } + } else /* srcLength >= 0 */ { + const uint8_t *pSrcLimit = (pSrc!=nullptr)?(pSrc + srcLength):nullptr; + + /* + * This function requires that if srcLength is given, then it must be + * destCapatity >= srcLength so that we need not check for + * destination buffer overflow in the loop. + */ + if(destCapacity < srcLength) { + if(pDestLength != nullptr) { + *pDestLength = srcLength; /* this likely overestimates the true destLength! */ + } + *pErrorCode = U_BUFFER_OVERFLOW_ERROR; + return nullptr; + } + + if((pSrcLimit - pSrc) >= 4) { + pSrcLimit -= 3; /* temporarily reduce pSrcLimit */ + + /* in this loop, we can always access at least 4 bytes, up to pSrc+3 */ + do { + ch = *pSrc++; + if(ch < 0xc0) { + /* + * ASCII, or a trail byte in lead position which is treated like + * a single-byte sequence for better character boundary + * resynchronization after illegal sequences. + */ + *pDest++=(char16_t)ch; + } else if(ch < 0xe0) { /* U+0080..U+07FF */ + /* 0x3080 = (0xc0 << 6) + 0x80 */ + *pDest++ = (char16_t)((ch << 6) + *pSrc++ - 0x3080); + } else if(ch < 0xf0) { /* U+0800..U+FFFF */ + /* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (char16_t) */ + /* 0x2080 = (0x80 << 6) + 0x80 */ + ch = (ch << 12) + (*pSrc++ << 6); + *pDest++ = (char16_t)(ch + *pSrc++ - 0x2080); + } else /* f0..f4 */ { /* U+10000..U+10FFFF */ + /* 0x3c82080 = (0xf0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */ + ch = (ch << 18) + (*pSrc++ << 12); + ch += *pSrc++ << 6; + ch += *pSrc++ - 0x3c82080; + *(pDest++) = U16_LEAD(ch); + *(pDest++) = U16_TRAIL(ch); + } + } while(pSrc < pSrcLimit); + + pSrcLimit += 3; /* restore original pSrcLimit */ + } + + while(pSrc < pSrcLimit) { + ch = *pSrc++; + if(ch < 0xc0) { + /* + * ASCII, or a trail byte in lead position which is treated like + * a single-byte sequence for better character boundary + * resynchronization after illegal sequences. + */ + *pDest++=(char16_t)ch; + continue; + } else if(ch < 0xe0) { /* U+0080..U+07FF */ + if(pSrc < pSrcLimit) { + /* 0x3080 = (0xc0 << 6) + 0x80 */ + *pDest++ = (char16_t)((ch << 6) + *pSrc++ - 0x3080); + continue; + } + } else if(ch < 0xf0) { /* U+0800..U+FFFF */ + if((pSrcLimit - pSrc) >= 2) { + /* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (char16_t) */ + /* 0x2080 = (0x80 << 6) + 0x80 */ + ch = (ch << 12) + (*pSrc++ << 6); + *pDest++ = (char16_t)(ch + *pSrc++ - 0x2080); + pSrc += 3; + continue; + } + } else /* f0..f4 */ { /* U+10000..U+10FFFF */ + if((pSrcLimit - pSrc) >= 3) { + /* 0x3c82080 = (0xf0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */ + ch = (ch << 18) + (*pSrc++ << 12); + ch += *pSrc++ << 6; + ch += *pSrc++ - 0x3c82080; + *(pDest++) = U16_LEAD(ch); + *(pDest++) = U16_TRAIL(ch); + pSrc += 4; + continue; + } + } + + /* truncated character at the end */ + *pDest++ = 0xfffd; + break; + } + } + + reqLength+=(int32_t)(pDest - dest); + + if(pDestLength){ + *pDestLength = reqLength; + } + + /* Terminate the buffer */ + u_terminateUChars(dest,destCapacity,reqLength,pErrorCode); + + return dest; +} + +static inline uint8_t * +_appendUTF8(uint8_t *pDest, UChar32 c) { + /* it is 0<=c<=0x10ffff and not a surrogate if called by a validating function */ + if((c)<=0x7f) { + *pDest++=(uint8_t)c; + } else if(c<=0x7ff) { + *pDest++=(uint8_t)((c>>6)|0xc0); + *pDest++=(uint8_t)((c&0x3f)|0x80); + } else if(c<=0xffff) { + *pDest++=(uint8_t)((c>>12)|0xe0); + *pDest++=(uint8_t)(((c>>6)&0x3f)|0x80); + *pDest++=(uint8_t)(((c)&0x3f)|0x80); + } else /* if((uint32_t)(c)<=0x10ffff) */ { + *pDest++=(uint8_t)(((c)>>18)|0xf0); + *pDest++=(uint8_t)((((c)>>12)&0x3f)|0x80); + *pDest++=(uint8_t)((((c)>>6)&0x3f)|0x80); + *pDest++=(uint8_t)(((c)&0x3f)|0x80); + } + return pDest; +} + + +U_CAPI char* U_EXPORT2 +u_strToUTF8WithSub(char *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char16_t *pSrc, + int32_t srcLength, + UChar32 subchar, int32_t *pNumSubstitutions, + UErrorCode *pErrorCode){ + int32_t reqLength=0; + uint32_t ch=0,ch2=0; + uint8_t *pDest = (uint8_t *)dest; + uint8_t *pDestLimit = (pDest!=nullptr)?(pDest + destCapacity):nullptr; + int32_t numSubstitutions; + + /* args check */ + if(U_FAILURE(*pErrorCode)){ + return nullptr; + } + + if( (pSrc==nullptr && srcLength!=0) || srcLength < -1 || + (destCapacity<0) || (dest == nullptr && destCapacity > 0) || + subchar > 0x10ffff || U_IS_SURROGATE(subchar) + ) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + if(pNumSubstitutions!=nullptr) { + *pNumSubstitutions=0; + } + numSubstitutions=0; + + if(srcLength==-1) { + while((ch=*pSrc)!=0) { + ++pSrc; + if(ch <= 0x7f) { + if(pDest= 2) { + *pDest++=(uint8_t)((ch>>6)|0xc0); + *pDest++=(uint8_t)((ch&0x3f)|0x80); + } else { + reqLength = 2; + break; + } + } else if(ch <= 0xd7ff || ch >= 0xe000) { + if((pDestLimit - pDest) >= 3) { + *pDest++=(uint8_t)((ch>>12)|0xe0); + *pDest++=(uint8_t)(((ch>>6)&0x3f)|0x80); + *pDest++=(uint8_t)((ch&0x3f)|0x80); + } else { + reqLength = 3; + break; + } + } else /* ch is a surrogate */ { + int32_t length; + + /*need not check for NUL because NUL fails U16_IS_TRAIL() anyway*/ + if(U16_IS_SURROGATE_LEAD(ch) && U16_IS_TRAIL(ch2=*pSrc)) { + ++pSrc; + ch=U16_GET_SUPPLEMENTARY(ch, ch2); + } else if(subchar>=0) { + ch=subchar; + ++numSubstitutions; + } else { + /* Unicode 3.2 forbids surrogate code points in UTF-8 */ + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } + + length = U8_LENGTH(ch); + if((pDestLimit - pDest) >= length) { + /* convert and append*/ + pDest=_appendUTF8(pDest, ch); + } else { + reqLength = length; + break; + } + } + } + while((ch=*pSrc++)!=0) { + if(ch<=0x7f) { + ++reqLength; + } else if(ch<=0x7ff) { + reqLength+=2; + } else if(!U16_IS_SURROGATE(ch)) { + reqLength+=3; + } else if(U16_IS_SURROGATE_LEAD(ch) && U16_IS_TRAIL(ch2=*pSrc)) { + ++pSrc; + reqLength+=4; + } else if(subchar>=0) { + reqLength+=U8_LENGTH(subchar); + ++numSubstitutions; + } else { + /* Unicode 3.2 forbids surrogate code points in UTF-8 */ + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } + } + } else { + const char16_t *pSrcLimit = (pSrc!=nullptr)?(pSrc+srcLength):nullptr; + int32_t count; + + /* Faster loop without ongoing checking for pSrcLimit and pDestLimit. */ + for(;;) { + /* + * Each iteration of the inner loop progresses by at most 3 UTF-8 + * bytes and one char16_t, for most characters. + * For supplementary code points (4 & 2), which are rare, + * there is an additional adjustment. + */ + count = (int32_t)((pDestLimit - pDest) / 3); + srcLength = (int32_t)(pSrcLimit - pSrc); + if(count > srcLength) { + count = srcLength; /* min(remaining dest/3, remaining src) */ + } + if(count < 3) { + /* + * Too much overhead if we get near the end of the string, + * continue with the next loop. + */ + break; + } + do { + ch=*pSrc++; + if(ch <= 0x7f) { + *pDest++ = (uint8_t)ch; + } else if(ch <= 0x7ff) { + *pDest++=(uint8_t)((ch>>6)|0xc0); + *pDest++=(uint8_t)((ch&0x3f)|0x80); + } else if(ch <= 0xd7ff || ch >= 0xe000) { + *pDest++=(uint8_t)((ch>>12)|0xe0); + *pDest++=(uint8_t)(((ch>>6)&0x3f)|0x80); + *pDest++=(uint8_t)((ch&0x3f)|0x80); + } else /* ch is a surrogate */ { + /* + * We will read two UChars and probably output four bytes, + * which we didn't account for with computing count, + * so we adjust it here. + */ + if(--count == 0) { + --pSrc; /* undo ch=*pSrc++ for the lead surrogate */ + break; /* recompute count */ + } + + if(U16_IS_SURROGATE_LEAD(ch) && U16_IS_TRAIL(ch2=*pSrc)) { + ++pSrc; + ch=U16_GET_SUPPLEMENTARY(ch, ch2); + + /* writing 4 bytes per 2 UChars is ok */ + *pDest++=(uint8_t)((ch>>18)|0xf0); + *pDest++=(uint8_t)(((ch>>12)&0x3f)|0x80); + *pDest++=(uint8_t)(((ch>>6)&0x3f)|0x80); + *pDest++=(uint8_t)((ch&0x3f)|0x80); + } else { + /* Unicode 3.2 forbids surrogate code points in UTF-8 */ + if(subchar>=0) { + ch=subchar; + ++numSubstitutions; + } else { + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } + + /* convert and append*/ + pDest=_appendUTF8(pDest, ch); + } + } + } while(--count > 0); + } + + while(pSrc= 2) { + *pDest++=(uint8_t)((ch>>6)|0xc0); + *pDest++=(uint8_t)((ch&0x3f)|0x80); + } else { + reqLength = 2; + break; + } + } else if(ch <= 0xd7ff || ch >= 0xe000) { + if((pDestLimit - pDest) >= 3) { + *pDest++=(uint8_t)((ch>>12)|0xe0); + *pDest++=(uint8_t)(((ch>>6)&0x3f)|0x80); + *pDest++=(uint8_t)((ch&0x3f)|0x80); + } else { + reqLength = 3; + break; + } + } else /* ch is a surrogate */ { + int32_t length; + + if(U16_IS_SURROGATE_LEAD(ch) && pSrc=0) { + ch=subchar; + ++numSubstitutions; + } else { + /* Unicode 3.2 forbids surrogate code points in UTF-8 */ + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } + + length = U8_LENGTH(ch); + if((pDestLimit - pDest) >= length) { + /* convert and append*/ + pDest=_appendUTF8(pDest, ch); + } else { + reqLength = length; + break; + } + } + } + while(pSrc=0) { + reqLength+=U8_LENGTH(subchar); + ++numSubstitutions; + } else { + /* Unicode 3.2 forbids surrogate code points in UTF-8 */ + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } + } + } + + reqLength+=(int32_t)(pDest - (uint8_t *)dest); + + if(pNumSubstitutions!=nullptr) { + *pNumSubstitutions=numSubstitutions; + } + + if(pDestLength){ + *pDestLength = reqLength; + } + + /* Terminate the buffer */ + u_terminateChars(dest, destCapacity, reqLength, pErrorCode); + return dest; +} + +U_CAPI char* U_EXPORT2 +u_strToUTF8(char *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char16_t *pSrc, + int32_t srcLength, + UErrorCode *pErrorCode){ + return u_strToUTF8WithSub( + dest, destCapacity, pDestLength, + pSrc, srcLength, + U_SENTINEL, nullptr, + pErrorCode); +} + +U_CAPI char16_t* U_EXPORT2 +u_strFromJavaModifiedUTF8WithSub( + char16_t *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char *src, + int32_t srcLength, + UChar32 subchar, int32_t *pNumSubstitutions, + UErrorCode *pErrorCode) { + /* args check */ + if(U_FAILURE(*pErrorCode)) { + return nullptr; + } + if( (src==nullptr && srcLength!=0) || srcLength < -1 || + (dest==nullptr && destCapacity!=0) || destCapacity<0 || + subchar > 0x10ffff || U_IS_SURROGATE(subchar) + ) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + if(pNumSubstitutions!=nullptr) { + *pNumSubstitutions=0; + } + char16_t *pDest = dest; + char16_t *pDestLimit = dest+destCapacity; + int32_t reqLength = 0; + int32_t numSubstitutions=0; + + if(srcLength < 0) { + /* + * Transform a NUL-terminated ASCII string. + * Handle non-ASCII strings with slower code. + */ + UChar32 c; + while(((c = (uint8_t)*src) != 0) && c <= 0x7f && (pDest < pDestLimit)) { + *pDest++=(char16_t)c; + ++src; + } + if(c == 0) { + reqLength=(int32_t)(pDest - dest); + if(pDestLength) { + *pDestLength = reqLength; + } + + /* Terminate the buffer */ + u_terminateUChars(dest, destCapacity, reqLength, pErrorCode); + return dest; + } + srcLength = static_cast(uprv_strlen(src)); + } + + /* Faster loop without ongoing checking for srcLength and pDestLimit. */ + UChar32 ch; + uint8_t t1, t2; + int32_t i = 0; + for(;;) { + int32_t count = (int32_t)(pDestLimit - pDest); + int32_t count2 = srcLength - i; + if(count >= count2 && srcLength > 0 && U8_IS_SINGLE(*src)) { + /* fast ASCII loop */ + int32_t start = i; + uint8_t b; + while(i < srcLength && U8_IS_SINGLE(b = src[i])) { + *pDest++=b; + ++i; + } + int32_t delta = i - start; + count -= delta; + count2 -= delta; + } + /* + * Each iteration of the inner loop progresses by at most 3 UTF-8 + * bytes and one char16_t. + */ + if(subchar > 0xFFFF) { + break; + } + count2 /= 3; + if(count > count2) { + count = count2; /* min(remaining dest, remaining src/3) */ + } + if(count < 3) { + /* + * Too much overhead if we get near the end of the string, + * continue with the next loop. + */ + break; + } + do { + ch = (uint8_t)src[i++]; + if(U8_IS_SINGLE(ch)) { + *pDest++=(char16_t)ch; + } else { + if(ch >= 0xe0) { + if( /* handle U+0000..U+FFFF inline */ + ch <= 0xef && + (t1 = (uint8_t)(src[i] - 0x80)) <= 0x3f && + (t2 = (uint8_t)(src[i+1] - 0x80)) <= 0x3f + ) { + /* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (char16_t) */ + *pDest++ = (char16_t)((ch << 12) | (t1 << 6) | t2); + i += 2; + continue; + } + } else { + if( /* handle U+0000..U+07FF inline */ + ch >= 0xc0 && + (t1 = (uint8_t)(src[i] - 0x80)) <= 0x3f + ) { + *pDest++ = (char16_t)(((ch & 0x1f) << 6) | t1); + ++i; + continue; + } + } + + if(subchar < 0) { + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } else if(subchar > 0xffff && --count == 0) { + /* + * We need to write two UChars, adjusted count for that, + * and ran out of space. + */ + --i; // back out byte ch + break; + } else { + /* function call for error cases */ + utf8_nextCharSafeBody((const uint8_t *)src, &(i), srcLength, ch, -1); + ++numSubstitutions; + *(pDest++)=(char16_t)subchar; + } + } + } while(--count > 0); + } + + while(i < srcLength && (pDest < pDestLimit)) { + ch = (uint8_t)src[i++]; + if(U8_IS_SINGLE(ch)){ + *pDest++=(char16_t)ch; + } else { + if(ch >= 0xe0) { + if( /* handle U+0000..U+FFFF inline */ + ch <= 0xef && + (i+1) < srcLength && + (t1 = (uint8_t)(src[i] - 0x80)) <= 0x3f && + (t2 = (uint8_t)(src[i+1] - 0x80)) <= 0x3f + ) { + /* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (char16_t) */ + *pDest++ = (char16_t)((ch << 12) | (t1 << 6) | t2); + i += 2; + continue; + } + } else { + if( /* handle U+0000..U+07FF inline */ + ch >= 0xc0 && + i < srcLength && + (t1 = (uint8_t)(src[i] - 0x80)) <= 0x3f + ) { + *pDest++ = (char16_t)(((ch & 0x1f) << 6) | t1); + ++i; + continue; + } + } + + if(subchar < 0) { + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } else { + /* function call for error cases */ + utf8_nextCharSafeBody((const uint8_t *)src, &(i), srcLength, ch, -1); + ++numSubstitutions; + if(subchar<=0xFFFF) { + *(pDest++)=(char16_t)subchar; + } else { + *(pDest++)=U16_LEAD(subchar); + if(pDest= 0xe0) { + if( /* handle U+0000..U+FFFF inline */ + ch <= 0xef && + (i+1) < srcLength && + (uint8_t)(src[i] - 0x80) <= 0x3f && + (uint8_t)(src[i+1] - 0x80) <= 0x3f + ) { + reqLength++; + i += 2; + continue; + } + } else { + if( /* handle U+0000..U+07FF inline */ + ch >= 0xc0 && + i < srcLength && + (uint8_t)(src[i] - 0x80) <= 0x3f + ) { + reqLength++; + ++i; + continue; + } + } + + if(subchar < 0) { + *pErrorCode = U_INVALID_CHAR_FOUND; + return nullptr; + } else { + /* function call for error cases */ + utf8_nextCharSafeBody((const uint8_t *)src, &(i), srcLength, ch, -1); + ++numSubstitutions; + reqLength+=U16_LENGTH(ch); + } + } + } + + if(pNumSubstitutions!=nullptr) { + *pNumSubstitutions=numSubstitutions; + } + + reqLength+=(int32_t)(pDest - dest); + if(pDestLength) { + *pDestLength = reqLength; + } + + /* Terminate the buffer */ + u_terminateUChars(dest, destCapacity, reqLength, pErrorCode); + return dest; +} + +U_CAPI char* U_EXPORT2 +u_strToJavaModifiedUTF8( + char *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char16_t *src, + int32_t srcLength, + UErrorCode *pErrorCode) { + int32_t reqLength=0; + uint32_t ch=0; + const char16_t *pSrcLimit; + int32_t count; + + /* args check */ + if(U_FAILURE(*pErrorCode)){ + return nullptr; + } + if( (src==nullptr && srcLength!=0) || srcLength < -1 || + (dest==nullptr && destCapacity!=0) || destCapacity<0 + ) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + uint8_t *pDest = (uint8_t *)dest; + uint8_t *pDestLimit = pDest + destCapacity; + + if(srcLength==-1) { + /* Convert NUL-terminated ASCII, then find the string length. */ + while((ch=*src)<=0x7f && ch != 0 && pDest= srcLength && srcLength > 0 && *src <= 0x7f) { + /* fast ASCII loop */ + const char16_t *prevSrc = src; + int32_t delta; + while(src < pSrcLimit && (ch = *src) <= 0x7f && ch != 0) { + *pDest++=(uint8_t)ch; + ++src; + } + delta = (int32_t)(src - prevSrc); + count -= delta; + srcLength -= delta; + } + /* + * Each iteration of the inner loop progresses by at most 3 UTF-8 + * bytes and one char16_t. + */ + count /= 3; + if(count > srcLength) { + count = srcLength; /* min(remaining dest/3, remaining src) */ + } + if(count < 3) { + /* + * Too much overhead if we get near the end of the string, + * continue with the next loop. + */ + break; + } + do { + ch=*src++; + if(ch <= 0x7f && ch != 0) { + *pDest++ = (uint8_t)ch; + } else if(ch <= 0x7ff) { + *pDest++=(uint8_t)((ch>>6)|0xc0); + *pDest++=(uint8_t)((ch&0x3f)|0x80); + } else { + *pDest++=(uint8_t)((ch>>12)|0xe0); + *pDest++=(uint8_t)(((ch>>6)&0x3f)|0x80); + *pDest++=(uint8_t)((ch&0x3f)|0x80); + } + } while(--count > 0); + } + + while(src= 2) { + *pDest++=(uint8_t)((ch>>6)|0xc0); + *pDest++=(uint8_t)((ch&0x3f)|0x80); + } else { + reqLength = 2; + break; + } + } else { + if((pDestLimit - pDest) >= 3) { + *pDest++=(uint8_t)((ch>>12)|0xe0); + *pDest++=(uint8_t)(((ch>>6)&0x3f)|0x80); + *pDest++=(uint8_t)((ch&0x3f)|0x80); + } else { + reqLength = 3; + break; + } + } + } + while(src - -#include "unicode/utypes.h" -#include "unicode/ustring.h" -#include "unicode/unistr.h" -#include "unicode/chariter.h" -#include "unicode/utext.h" -#include "unicode/utf.h" -#include "unicode/utf8.h" -#include "unicode/utf16.h" -#include "ustr_imp.h" -#include "cmemory.h" -#include "cstring.h" -#include "uassert.h" -#include "putilimp.h" - -U_NAMESPACE_USE - -#define I32_FLAG(bitIndex) ((int32_t)1<<(bitIndex)) - - -static UBool -utext_access(UText *ut, int64_t index, UBool forward) { - return ut->pFuncs->access(ut, index, forward); -} - - - -U_CAPI UBool U_EXPORT2 -utext_moveIndex32(UText *ut, int32_t delta) { - UChar32 c; - if (delta > 0) { - do { - if(ut->chunkOffset>=ut->chunkLength && !utext_access(ut, ut->chunkNativeLimit, true)) { - return false; - } - c = ut->chunkContents[ut->chunkOffset]; - if (U16_IS_SURROGATE(c)) { - c = utext_next32(ut); - if (c == U_SENTINEL) { - return false; - } - } else { - ut->chunkOffset++; - } - } while(--delta>0); - - } else if (delta<0) { - do { - if(ut->chunkOffset<=0 && !utext_access(ut, ut->chunkNativeStart, false)) { - return false; - } - c = ut->chunkContents[ut->chunkOffset-1]; - if (U16_IS_SURROGATE(c)) { - c = utext_previous32(ut); - if (c == U_SENTINEL) { - return false; - } - } else { - ut->chunkOffset--; - } - } while(++delta<0); - } - - return true; -} - - -U_CAPI int64_t U_EXPORT2 -utext_nativeLength(UText *ut) { - return ut->pFuncs->nativeLength(ut); -} - - -U_CAPI UBool U_EXPORT2 -utext_isLengthExpensive(const UText *ut) { - UBool r = (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE)) != 0; - return r; -} - - -U_CAPI int64_t U_EXPORT2 -utext_getNativeIndex(const UText *ut) { - if(ut->chunkOffset <= ut->nativeIndexingLimit) { - return ut->chunkNativeStart+ut->chunkOffset; - } else { - return ut->pFuncs->mapOffsetToNative(ut); - } -} - - -U_CAPI void U_EXPORT2 -utext_setNativeIndex(UText *ut, int64_t index) { - if(indexchunkNativeStart || index>=ut->chunkNativeLimit) { - // The desired position is outside of the current chunk. - // Access the new position. Assume a forward iteration from here, - // which will also be optimimum for a single random access. - // Reverse iterations may suffer slightly. - ut->pFuncs->access(ut, index, true); - } else if((int32_t)(index - ut->chunkNativeStart) <= ut->nativeIndexingLimit) { - // utf-16 indexing. - ut->chunkOffset=(int32_t)(index-ut->chunkNativeStart); - } else { - ut->chunkOffset=ut->pFuncs->mapNativeIndexToUTF16(ut, index); - } - // The convention is that the index must always be on a code point boundary. - // Adjust the index position if it is in the middle of a surrogate pair. - if (ut->chunkOffsetchunkLength) { - UChar c= ut->chunkContents[ut->chunkOffset]; - if (U16_IS_TRAIL(c)) { - if (ut->chunkOffset==0) { - ut->pFuncs->access(ut, ut->chunkNativeStart, false); - } - if (ut->chunkOffset>0) { - UChar lead = ut->chunkContents[ut->chunkOffset-1]; - if (U16_IS_LEAD(lead)) { - ut->chunkOffset--; - } - } - } - } -} - - - -U_CAPI int64_t U_EXPORT2 -utext_getPreviousNativeIndex(UText *ut) { - // - // Fast-path the common case. - // Common means current position is not at the beginning of a chunk - // and the preceding character is not supplementary. - // - int32_t i = ut->chunkOffset - 1; - int64_t result; - if (i >= 0) { - UChar c = ut->chunkContents[i]; - if (U16_IS_TRAIL(c) == false) { - if (i <= ut->nativeIndexingLimit) { - result = ut->chunkNativeStart + i; - } else { - ut->chunkOffset = i; - result = ut->pFuncs->mapOffsetToNative(ut); - ut->chunkOffset++; - } - return result; - } - } - - // If at the start of text, simply return 0. - if (ut->chunkOffset==0 && ut->chunkNativeStart==0) { - return 0; - } - - // Harder, less common cases. We are at a chunk boundary, or on a surrogate. - // Keep it simple, use other functions to handle the edges. - // - utext_previous32(ut); - result = UTEXT_GETNATIVEINDEX(ut); - utext_next32(ut); - return result; -} - - -// -// utext_current32. Get the UChar32 at the current position. -// UText iteration position is always on a code point boundary, -// never on the trail half of a surrogate pair. -// -U_CAPI UChar32 U_EXPORT2 -utext_current32(UText *ut) { - UChar32 c; - if (ut->chunkOffset==ut->chunkLength) { - // Current position is just off the end of the chunk. - if (ut->pFuncs->access(ut, ut->chunkNativeLimit, true) == false) { - // Off the end of the text. - return U_SENTINEL; - } - } - - c = ut->chunkContents[ut->chunkOffset]; - if (U16_IS_LEAD(c) == false) { - // Normal, non-supplementary case. - return c; - } - - // - // Possible supplementary char. - // - UChar32 trail = 0; - UChar32 supplementaryC = c; - if ((ut->chunkOffset+1) < ut->chunkLength) { - // The trail surrogate is in the same chunk. - trail = ut->chunkContents[ut->chunkOffset+1]; - } else { - // The trail surrogate is in a different chunk. - // Because we must maintain the iteration position, we need to switch forward - // into the new chunk, get the trail surrogate, then revert the chunk back to the - // original one. - // An edge case to be careful of: the entire text may end with an unpaired - // leading surrogate. The attempt to access the trail will fail, but - // the original position before the unpaired lead still needs to be restored. - int64_t nativePosition = ut->chunkNativeLimit; - int32_t originalOffset = ut->chunkOffset; - if (ut->pFuncs->access(ut, nativePosition, true)) { - trail = ut->chunkContents[ut->chunkOffset]; - } - UBool r = ut->pFuncs->access(ut, nativePosition, false); // reverse iteration flag loads preceding chunk - U_ASSERT(r==true); - ut->chunkOffset = originalOffset; - if(!r) { - return U_SENTINEL; - } - } - - if (U16_IS_TRAIL(trail)) { - supplementaryC = U16_GET_SUPPLEMENTARY(c, trail); - } - return supplementaryC; - -} - - -U_CAPI UChar32 U_EXPORT2 -utext_char32At(UText *ut, int64_t nativeIndex) { - UChar32 c = U_SENTINEL; - - // Fast path the common case. - if (nativeIndex>=ut->chunkNativeStart && nativeIndex < ut->chunkNativeStart + ut->nativeIndexingLimit) { - ut->chunkOffset = (int32_t)(nativeIndex - ut->chunkNativeStart); - c = ut->chunkContents[ut->chunkOffset]; - if (U16_IS_SURROGATE(c) == false) { - return c; - } - } - - - utext_setNativeIndex(ut, nativeIndex); - if (nativeIndex>=ut->chunkNativeStart && ut->chunkOffsetchunkLength) { - c = ut->chunkContents[ut->chunkOffset]; - if (U16_IS_SURROGATE(c)) { - // For surrogates, let current32() deal with the complications - // of supplementaries that may span chunk boundaries. - c = utext_current32(ut); - } - } - return c; -} - - -U_CAPI UChar32 U_EXPORT2 -utext_next32(UText *ut) { - UChar32 c; - - if (ut->chunkOffset >= ut->chunkLength) { - if (ut->pFuncs->access(ut, ut->chunkNativeLimit, true) == false) { - return U_SENTINEL; - } - } - - c = ut->chunkContents[ut->chunkOffset++]; - if (U16_IS_LEAD(c) == false) { - // Normal case, not supplementary. - // (A trail surrogate seen here is just returned as is, as a surrogate value. - // It cannot be part of a pair.) - return c; - } - - if (ut->chunkOffset >= ut->chunkLength) { - if (ut->pFuncs->access(ut, ut->chunkNativeLimit, true) == false) { - // c is an unpaired lead surrogate at the end of the text. - // return it as it is. - return c; - } - } - UChar32 trail = ut->chunkContents[ut->chunkOffset]; - if (U16_IS_TRAIL(trail) == false) { - // c was an unpaired lead surrogate, not at the end of the text. - // return it as it is (unpaired). Iteration position is on the - // following character, possibly in the next chunk, where the - // trail surrogate would have been if it had existed. - return c; - } - - UChar32 supplementary = U16_GET_SUPPLEMENTARY(c, trail); - ut->chunkOffset++; // move iteration position over the trail surrogate. - return supplementary; - } - - -U_CAPI UChar32 U_EXPORT2 -utext_previous32(UText *ut) { - UChar32 c; - - if (ut->chunkOffset <= 0) { - if (ut->pFuncs->access(ut, ut->chunkNativeStart, false) == false) { - return U_SENTINEL; - } - } - ut->chunkOffset--; - c = ut->chunkContents[ut->chunkOffset]; - if (U16_IS_TRAIL(c) == false) { - // Normal case, not supplementary. - // (A lead surrogate seen here is just returned as is, as a surrogate value. - // It cannot be part of a pair.) - return c; - } - - if (ut->chunkOffset <= 0) { - if (ut->pFuncs->access(ut, ut->chunkNativeStart, false) == false) { - // c is an unpaired trail surrogate at the start of the text. - // return it as it is. - return c; - } - } - - UChar32 lead = ut->chunkContents[ut->chunkOffset-1]; - if (U16_IS_LEAD(lead) == false) { - // c was an unpaired trail surrogate, not at the end of the text. - // return it as it is (unpaired). Iteration position is at c - return c; - } - - UChar32 supplementary = U16_GET_SUPPLEMENTARY(lead, c); - ut->chunkOffset--; // move iteration position over the lead surrogate. - return supplementary; -} - - - -U_CAPI UChar32 U_EXPORT2 -utext_next32From(UText *ut, int64_t index) { - UChar32 c = U_SENTINEL; - - if(indexchunkNativeStart || index>=ut->chunkNativeLimit) { - // Desired position is outside of the current chunk. - if(!ut->pFuncs->access(ut, index, true)) { - // no chunk available here - return U_SENTINEL; - } - } else if (index - ut->chunkNativeStart <= (int64_t)ut->nativeIndexingLimit) { - // Desired position is in chunk, with direct 1:1 native to UTF16 indexing - ut->chunkOffset = (int32_t)(index - ut->chunkNativeStart); - } else { - // Desired position is in chunk, with non-UTF16 indexing. - ut->chunkOffset = ut->pFuncs->mapNativeIndexToUTF16(ut, index); - } - - c = ut->chunkContents[ut->chunkOffset++]; - if (U16_IS_SURROGATE(c)) { - // Surrogates. Many edge cases. Use other functions that already - // deal with the problems. - utext_setNativeIndex(ut, index); - c = utext_next32(ut); - } - return c; -} - - -U_CAPI UChar32 U_EXPORT2 -utext_previous32From(UText *ut, int64_t index) { - // - // Return the character preceding the specified index. - // Leave the iteration position at the start of the character that was returned. - // - UChar32 cPrev; // The character preceding cCurr, which is what we will return. - - // Address the chunk containing the position preceding the incoming index - // A tricky edge case: - // We try to test the requested native index against the chunkNativeStart to determine - // whether the character preceding the one at the index is in the current chunk. - // BUT, this test can fail with UTF-8 (or any other multibyte encoding), when the - // requested index is on something other than the first position of the first char. - // - if(index<=ut->chunkNativeStart || index>ut->chunkNativeLimit) { - // Requested native index is outside of the current chunk. - if(!ut->pFuncs->access(ut, index, false)) { - // no chunk available here - return U_SENTINEL; - } - } else if(index - ut->chunkNativeStart <= (int64_t)ut->nativeIndexingLimit) { - // Direct UTF-16 indexing. - ut->chunkOffset = (int32_t)(index - ut->chunkNativeStart); - } else { - ut->chunkOffset=ut->pFuncs->mapNativeIndexToUTF16(ut, index); - if (ut->chunkOffset==0 && !ut->pFuncs->access(ut, index, false)) { - // no chunk available here - return U_SENTINEL; - } - } - - // - // Simple case with no surrogates. - // - ut->chunkOffset--; - cPrev = ut->chunkContents[ut->chunkOffset]; - - if (U16_IS_SURROGATE(cPrev)) { - // Possible supplementary. Many edge cases. - // Let other functions do the heavy lifting. - utext_setNativeIndex(ut, index); - cPrev = utext_previous32(ut); - } - return cPrev; -} - - -U_CAPI int32_t U_EXPORT2 -utext_extract(UText *ut, - int64_t start, int64_t limit, - UChar *dest, int32_t destCapacity, - UErrorCode *status) { - return ut->pFuncs->extract(ut, start, limit, dest, destCapacity, status); - } - - - -U_CAPI UBool U_EXPORT2 -utext_equals(const UText *a, const UText *b) { - if (a==NULL || b==NULL || - a->magic != UTEXT_MAGIC || - b->magic != UTEXT_MAGIC) { - // Null or invalid arguments don't compare equal to anything. - return false; - } - - if (a->pFuncs != b->pFuncs) { - // Different types of text providers. - return false; - } - - if (a->context != b->context) { - // Different sources (different strings) - return false; - } - if (utext_getNativeIndex(a) != utext_getNativeIndex(b)) { - // Different current position in the string. - return false; - } - - return true; -} - -U_CAPI UBool U_EXPORT2 -utext_isWritable(const UText *ut) -{ - UBool b = (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_WRITABLE)) != 0; - return b; -} - - -U_CAPI void U_EXPORT2 -utext_freeze(UText *ut) { - // Zero out the WRITABLE flag. - ut->providerProperties &= ~(I32_FLAG(UTEXT_PROVIDER_WRITABLE)); -} - - -U_CAPI UBool U_EXPORT2 -utext_hasMetaData(const UText *ut) -{ - UBool b = (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_HAS_META_DATA)) != 0; - return b; -} - - - -U_CAPI int32_t U_EXPORT2 -utext_replace(UText *ut, - int64_t nativeStart, int64_t nativeLimit, - const UChar *replacementText, int32_t replacementLength, - UErrorCode *status) -{ - if (U_FAILURE(*status)) { - return 0; - } - if ((ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_WRITABLE)) == 0) { - *status = U_NO_WRITE_PERMISSION; - return 0; - } - int32_t i = ut->pFuncs->replace(ut, nativeStart, nativeLimit, replacementText, replacementLength, status); - return i; -} - -U_CAPI void U_EXPORT2 -utext_copy(UText *ut, - int64_t nativeStart, int64_t nativeLimit, - int64_t destIndex, - UBool move, - UErrorCode *status) -{ - if (U_FAILURE(*status)) { - return; - } - if ((ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_WRITABLE)) == 0) { - *status = U_NO_WRITE_PERMISSION; - return; - } - ut->pFuncs->copy(ut, nativeStart, nativeLimit, destIndex, move, status); -} - - - -U_CAPI UText * U_EXPORT2 -utext_clone(UText *dest, const UText *src, UBool deep, UBool readOnly, UErrorCode *status) { - if (U_FAILURE(*status)) { - return dest; - } - UText *result = src->pFuncs->clone(dest, src, deep, status); - if (U_FAILURE(*status)) { - return result; - } - if (result == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return result; - } - if (readOnly) { - utext_freeze(result); - } - return result; -} - - - -//------------------------------------------------------------------------------ -// -// UText common functions implementation -// -//------------------------------------------------------------------------------ - -// -// UText.flags bit definitions -// -enum { - UTEXT_HEAP_ALLOCATED = 1, // 1 if ICU has allocated this UText struct on the heap. - // 0 if caller provided storage for the UText. - - UTEXT_EXTRA_HEAP_ALLOCATED = 2, // 1 if ICU has allocated extra storage as a separate - // heap block. - // 0 if there is no separate allocation. Either no extra - // storage was requested, or it is appended to the end - // of the main UText storage. - - UTEXT_OPEN = 4 // 1 if this UText is currently open - // 0 if this UText is not open. -}; - - -// -// Extended form of a UText. The purpose is to aid in computing the total size required -// when a provider asks for a UText to be allocated with extra storage. - -struct ExtendedUText { - UText ut; - std::max_align_t extension; -}; - -static const UText emptyText = UTEXT_INITIALIZER; - -U_CAPI UText * U_EXPORT2 -utext_setup(UText *ut, int32_t extraSpace, UErrorCode *status) { - if (U_FAILURE(*status)) { - return ut; - } - - if (ut == NULL) { - // We need to heap-allocate storage for the new UText - int32_t spaceRequired = sizeof(UText); - if (extraSpace > 0) { - spaceRequired = sizeof(ExtendedUText) + extraSpace - sizeof(std::max_align_t); - } - ut = (UText *)uprv_malloc(spaceRequired); - if (ut == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } else { - *ut = emptyText; - ut->flags |= UTEXT_HEAP_ALLOCATED; - if (spaceRequired>0) { - ut->extraSize = extraSpace; - ut->pExtra = &((ExtendedUText *)ut)->extension; - } - } - } else { - // We have been supplied with an already existing UText. - // Verify that it really appears to be a UText. - if (ut->magic != UTEXT_MAGIC) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return ut; - } - // If the ut is already open and there's a provider supplied close - // function, call it. - if ((ut->flags & UTEXT_OPEN) && ut->pFuncs->close != NULL) { - ut->pFuncs->close(ut); - } - ut->flags &= ~UTEXT_OPEN; - - // If extra space was requested by our caller, check whether - // sufficient already exists, and allocate new if needed. - if (extraSpace > ut->extraSize) { - // Need more space. If there is existing separately allocated space, - // delete it first, then allocate new space. - if (ut->flags & UTEXT_EXTRA_HEAP_ALLOCATED) { - uprv_free(ut->pExtra); - ut->extraSize = 0; - } - ut->pExtra = uprv_malloc(extraSpace); - if (ut->pExtra == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - } else { - ut->extraSize = extraSpace; - ut->flags |= UTEXT_EXTRA_HEAP_ALLOCATED; - } - } - } - if (U_SUCCESS(*status)) { - ut->flags |= UTEXT_OPEN; - - // Initialize all remaining fields of the UText. - // - ut->context = NULL; - ut->chunkContents = NULL; - ut->p = NULL; - ut->q = NULL; - ut->r = NULL; - ut->a = 0; - ut->b = 0; - ut->c = 0; - ut->chunkOffset = 0; - ut->chunkLength = 0; - ut->chunkNativeStart = 0; - ut->chunkNativeLimit = 0; - ut->nativeIndexingLimit = 0; - ut->providerProperties = 0; - ut->privA = 0; - ut->privB = 0; - ut->privC = 0; - ut->privP = NULL; - if (ut->pExtra!=NULL && ut->extraSize>0) - uprv_memset(ut->pExtra, 0, ut->extraSize); - - } - return ut; -} - - -U_CAPI UText * U_EXPORT2 -utext_close(UText *ut) { - if (ut==NULL || - ut->magic != UTEXT_MAGIC || - (ut->flags & UTEXT_OPEN) == 0) - { - // The supplied ut is not an open UText. - // Do nothing. - return ut; - } - - // If the provider gave us a close function, call it now. - // This will clean up anything allocated specifically by the provider. - if (ut->pFuncs->close != NULL) { - ut->pFuncs->close(ut); - } - ut->flags &= ~UTEXT_OPEN; - - // If we (the framework) allocated the UText or subsidiary storage, - // delete it. - if (ut->flags & UTEXT_EXTRA_HEAP_ALLOCATED) { - uprv_free(ut->pExtra); - ut->pExtra = NULL; - ut->flags &= ~UTEXT_EXTRA_HEAP_ALLOCATED; - ut->extraSize = 0; - } - - // Zero out function table of the closed UText. This is a defensive move, - // intended to cause applications that inadvertently use a closed - // utext to crash with null pointer errors. - ut->pFuncs = NULL; - - if (ut->flags & UTEXT_HEAP_ALLOCATED) { - // This UText was allocated by UText setup. We need to free it. - // Clear magic, so we can detect if the user messes up and immediately - // tries to reopen another UText using the deleted storage. - ut->magic = 0; - uprv_free(ut); - ut = NULL; - } - return ut; -} - - - - -// -// invalidateChunk Reset a chunk to have no contents, so that the next call -// to access will cause new data to load. -// This is needed when copy/move/replace operate directly on the -// backing text, potentially putting it out of sync with the -// contents in the chunk. -// -static void -invalidateChunk(UText *ut) { - ut->chunkLength = 0; - ut->chunkNativeLimit = 0; - ut->chunkNativeStart = 0; - ut->chunkOffset = 0; - ut->nativeIndexingLimit = 0; -} - -// -// pinIndex Do range pinning on a native index parameter. -// 64 bit pinning is done in place. -// 32 bit truncated result is returned as a convenience for -// use in providers that don't need 64 bits. -static int32_t -pinIndex(int64_t &index, int64_t limit) { - if (index<0) { - index = 0; - } else if (index > limit) { - index = limit; - } - return (int32_t)index; -} - - -U_CDECL_BEGIN - -// -// Pointer relocation function, -// a utility used by shallow clone. -// Adjust a pointer that refers to something within one UText (the source) -// to refer to the same relative offset within a another UText (the target) -// -static void adjustPointer(UText *dest, const void **destPtr, const UText *src) { - // convert all pointers to (char *) so that byte address arithmetic will work. - char *dptr = (char *)*destPtr; - char *dUText = (char *)dest; - char *sUText = (char *)src; - - if (dptr >= (char *)src->pExtra && dptr < ((char*)src->pExtra)+src->extraSize) { - // target ptr was to something within the src UText's pExtra storage. - // relocate it into the target UText's pExtra region. - *destPtr = ((char *)dest->pExtra) + (dptr - (char *)src->pExtra); - } else if (dptr>=sUText && dptr < sUText+src->sizeOfStruct) { - // target ptr was pointing to somewhere within the source UText itself. - // Move it to the same offset within the target UText. - *destPtr = dUText + (dptr-sUText); - } -} - - -// -// Clone. This is a generic copy-the-utext-by-value clone function that can be -// used as-is with some utext types, and as a helper by other clones. -// -static UText * U_CALLCONV -shallowTextClone(UText * dest, const UText * src, UErrorCode * status) { - if (U_FAILURE(*status)) { - return NULL; - } - int32_t srcExtraSize = src->extraSize; - - // - // Use the generic text_setup to allocate storage if required. - // - dest = utext_setup(dest, srcExtraSize, status); - if (U_FAILURE(*status)) { - return dest; - } - - // - // flags (how the UText was allocated) and the pointer to the - // extra storage must retain the values in the cloned utext that - // were set up by utext_setup. Save them separately before - // copying the whole struct. - // - void *destExtra = dest->pExtra; - int32_t flags = dest->flags; - - - // - // Copy the whole UText struct by value. - // Any "Extra" storage is copied also. - // - int sizeToCopy = src->sizeOfStruct; - if (sizeToCopy > dest->sizeOfStruct) { - sizeToCopy = dest->sizeOfStruct; - } - uprv_memcpy(dest, src, sizeToCopy); - dest->pExtra = destExtra; - dest->flags = flags; - if (srcExtraSize > 0) { - uprv_memcpy(dest->pExtra, src->pExtra, srcExtraSize); - } - - // - // Relocate any pointers in the target that refer to the UText itself - // to point to the cloned copy rather than the original source. - // - adjustPointer(dest, &dest->context, src); - adjustPointer(dest, &dest->p, src); - adjustPointer(dest, &dest->q, src); - adjustPointer(dest, &dest->r, src); - adjustPointer(dest, (const void **)&dest->chunkContents, src); - - // The newly shallow-cloned UText does _not_ own the underlying storage for the text. - // (The source for the clone may or may not have owned the text.) - - dest->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT); - - return dest; -} - - -U_CDECL_END - - - -//------------------------------------------------------------------------------ -// -// UText implementation for UTF-8 char * strings (read-only) -// Limitation: string length must be <= 0x7fffffff in length. -// (length must for in an int32_t variable) -// -// Use of UText data members: -// context pointer to UTF-8 string -// utext.b is the input string length (bytes). -// utext.c Length scanned so far in string -// (for optimizing finding length of zero terminated strings.) -// utext.p pointer to the current buffer -// utext.q pointer to the other buffer. -// -//------------------------------------------------------------------------------ - -// Chunk size. -// Must be less than 85 (256/3), because of byte mapping from UChar indexes to native indexes. -// Worst case is three native bytes to one UChar. (Supplemenaries are 4 native bytes -// to two UChars.) -// The longest illegal byte sequence treated as a single error (and converted to U+FFFD) -// is a three-byte sequence (truncated four-byte sequence). -// -enum { UTF8_TEXT_CHUNK_SIZE=32 }; - -// -// UTF8Buf Two of these structs will be set up in the UText's extra allocated space. -// Each contains the UChar chunk buffer, the to and from native maps, and -// header info. -// -// because backwards iteration fills the buffers starting at the end and -// working towards the front, the filled part of the buffers may not begin -// at the start of the available storage for the buffers. -// -// Buffer size is one bigger than the specified UTF8_TEXT_CHUNK_SIZE to allow for -// the last character added being a supplementary, and thus requiring a surrogate -// pair. Doing this is simpler than checking for the edge case. -// - -struct UTF8Buf { - int32_t bufNativeStart; // Native index of first char in UChar buf - int32_t bufNativeLimit; // Native index following last char in buf. - int32_t bufStartIdx; // First filled position in buf. - int32_t bufLimitIdx; // Limit of filled range in buf. - int32_t bufNILimit; // Limit of native indexing part of buf - int32_t toUCharsMapStart; // Native index corresponding to - // mapToUChars[0]. - // Set to bufNativeStart when filling forwards. - // Set to computed value when filling backwards. - - UChar buf[UTF8_TEXT_CHUNK_SIZE+4]; // The UChar buffer. Requires one extra position beyond the - // the chunk size, to allow for surrogate at the end. - // Length must be identical to mapToNative array, below, - // because of the way indexing works when the array is - // filled backwards during a reverse iteration. Thus, - // the additional extra size. - uint8_t mapToNative[UTF8_TEXT_CHUNK_SIZE+4]; // map UChar index in buf to - // native offset from bufNativeStart. - // Requires two extra slots, - // one for a supplementary starting in the last normal position, - // and one for an entry for the buffer limit position. - uint8_t mapToUChars[UTF8_TEXT_CHUNK_SIZE*3+6]; // Map native offset from bufNativeStart to - // corresponding offset in filled part of buf. - int32_t align; -}; - -U_CDECL_BEGIN - -// -// utf8TextLength -// -// Get the length of the string. If we don't already know it, -// we'll need to scan for the trailing nul. -// -static int64_t U_CALLCONV -utf8TextLength(UText *ut) { - if (ut->b < 0) { - // Zero terminated string, and we haven't scanned to the end yet. - // Scan it now. - const char *r = (const char *)ut->context + ut->c; - while (*r != 0) { - r++; - } - if ((r - (const char *)ut->context) < 0x7fffffff) { - ut->b = (int32_t)(r - (const char *)ut->context); - } else { - // Actual string was bigger (more than 2 gig) than we - // can handle. Clip it to 2 GB. - ut->b = 0x7fffffff; - } - ut->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); - } - return ut->b; -} - - - - - - -static UBool U_CALLCONV -utf8TextAccess(UText *ut, int64_t index, UBool forward) { - // - // Apologies to those who are allergic to goto statements. - // Consider each goto to a labelled block to be the equivalent of - // call the named block as if it were a function(); - // return; - // - const uint8_t *s8=(const uint8_t *)ut->context; - UTF8Buf *u8b = NULL; - int32_t length = ut->b; // Length of original utf-8 - int32_t ix= (int32_t)index; // Requested index, trimmed to 32 bits. - int32_t mapIndex = 0; - if (index<0) { - ix=0; - } else if (index > 0x7fffffff) { - // Strings with 64 bit lengths not supported by this UTF-8 provider. - ix = 0x7fffffff; - } - - // Pin requested index to the string length. - if (ix>length) { - if (length>=0) { - ix=length; - } else if (ix>=ut->c) { - // Zero terminated string, and requested index is beyond - // the region that has already been scanned. - // Scan up to either the end of the string or to the - // requested position, whichever comes first. - while (ut->cc]!=0) { - ut->c++; - } - // TODO: support for null terminated string length > 32 bits. - if (s8[ut->c] == 0) { - // We just found the actual length of the string. - // Trim the requested index back to that. - ix = ut->c; - ut->b = ut->c; - length = ut->c; - ut->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); - } - } - } - - // - // Dispatch to the appropriate action for a forward iteration request. - // - if (forward) { - if (ix==ut->chunkNativeLimit) { - // Check for normal sequential iteration cases first. - if (ix==length) { - // Just reached end of string - // Don't swap buffers, but do set the - // current buffer position. - ut->chunkOffset = ut->chunkLength; - return false; - } else { - // End of current buffer. - // check whether other buffer already has what we need. - UTF8Buf *altB = (UTF8Buf *)ut->q; - if (ix>=altB->bufNativeStart && ixbufNativeLimit) { - goto swapBuffers; - } - } - } - - // A random access. Desired index could be in either or niether buf. - // For optimizing the order of testing, first check for the index - // being in the other buffer. This will be the case for uses that - // move back and forth over a fairly limited range - { - u8b = (UTF8Buf *)ut->q; // the alternate buffer - if (ix>=u8b->bufNativeStart && ixbufNativeLimit) { - // Requested index is in the other buffer. - goto swapBuffers; - } - if (ix == length) { - // Requested index is end-of-string. - // (this is the case of randomly seeking to the end. - // The case of iterating off the end is handled earlier.) - if (ix == ut->chunkNativeLimit) { - // Current buffer extends up to the end of the string. - // Leave it as the current buffer. - ut->chunkOffset = ut->chunkLength; - return false; - } - if (ix == u8b->bufNativeLimit) { - // Alternate buffer extends to the end of string. - // Swap it in as the current buffer. - goto swapBuffersAndFail; - } - - // Neither existing buffer extends to the end of the string. - goto makeStubBuffer; - } - - if (ixchunkNativeStart || ix>=ut->chunkNativeLimit) { - // Requested index is in neither buffer. - goto fillForward; - } - - // Requested index is in this buffer. - u8b = (UTF8Buf *)ut->p; // the current buffer - mapIndex = ix - u8b->toUCharsMapStart; - U_ASSERT(mapIndex < (int32_t)sizeof(UTF8Buf::mapToUChars)); - ut->chunkOffset = u8b->mapToUChars[mapIndex] - u8b->bufStartIdx; - return true; - - } - } - - - // - // Dispatch to the appropriate action for a - // Backwards Direction iteration request. - // - if (ix==ut->chunkNativeStart) { - // Check for normal sequential iteration cases first. - if (ix==0) { - // Just reached the start of string - // Don't swap buffers, but do set the - // current buffer position. - ut->chunkOffset = 0; - return false; - } else { - // Start of current buffer. - // check whether other buffer already has what we need. - UTF8Buf *altB = (UTF8Buf *)ut->q; - if (ix>altB->bufNativeStart && ix<=altB->bufNativeLimit) { - goto swapBuffers; - } - } - } - - // A random access. Desired index could be in either or niether buf. - // For optimizing the order of testing, - // Most likely case: in the other buffer. - // Second most likely: in neither buffer. - // Unlikely, but must work: in the current buffer. - u8b = (UTF8Buf *)ut->q; // the alternate buffer - if (ix>u8b->bufNativeStart && ix<=u8b->bufNativeLimit) { - // Requested index is in the other buffer. - goto swapBuffers; - } - // Requested index is start-of-string. - // (this is the case of randomly seeking to the start. - // The case of iterating off the start is handled earlier.) - if (ix==0) { - if (u8b->bufNativeStart==0) { - // Alternate buffer contains the data for the start string. - // Make it be the current buffer. - goto swapBuffersAndFail; - } else { - // Request for data before the start of string, - // neither buffer is usable. - // set up a zero-length buffer. - goto makeStubBuffer; - } - } - - if (ix<=ut->chunkNativeStart || ix>ut->chunkNativeLimit) { - // Requested index is in neither buffer. - goto fillReverse; - } - - // Requested index is in this buffer. - // Set the utf16 buffer index. - u8b = (UTF8Buf *)ut->p; - mapIndex = ix - u8b->toUCharsMapStart; - ut->chunkOffset = u8b->mapToUChars[mapIndex] - u8b->bufStartIdx; - if (ut->chunkOffset==0) { - // This occurs when the first character in the text is - // a multi-byte UTF-8 char, and the requested index is to - // one of the trailing bytes. Because there is no preceding , - // character, this access fails. We can't pick up on the - // situation sooner because the requested index is not zero. - return false; - } else { - return true; - } - - - -swapBuffers: - // The alternate buffer (ut->q) has the string data that was requested. - // Swap the primary and alternate buffers, and set the - // chunk index into the new primary buffer. - { - u8b = (UTF8Buf *)ut->q; - ut->q = ut->p; - ut->p = u8b; - ut->chunkContents = &u8b->buf[u8b->bufStartIdx]; - ut->chunkLength = u8b->bufLimitIdx - u8b->bufStartIdx; - ut->chunkNativeStart = u8b->bufNativeStart; - ut->chunkNativeLimit = u8b->bufNativeLimit; - ut->nativeIndexingLimit = u8b->bufNILimit; - - // Index into the (now current) chunk - // Use the map to set the chunk index. It's more trouble than it's worth - // to check whether native indexing can be used. - U_ASSERT(ix>=u8b->bufNativeStart); - U_ASSERT(ix<=u8b->bufNativeLimit); - mapIndex = ix - u8b->toUCharsMapStart; - U_ASSERT(mapIndex>=0); - U_ASSERT(mapIndex<(int32_t)sizeof(u8b->mapToUChars)); - ut->chunkOffset = u8b->mapToUChars[mapIndex] - u8b->bufStartIdx; - - return true; - } - - - swapBuffersAndFail: - // We got a request for either the start or end of the string, - // with iteration continuing in the out-of-bounds direction. - // The alternate buffer already contains the data up to the - // start/end. - // Swap the buffers, then return failure, indicating that we couldn't - // make things correct for continuing the iteration in the requested - // direction. The position & buffer are correct should the - // user decide to iterate in the opposite direction. - u8b = (UTF8Buf *)ut->q; - ut->q = ut->p; - ut->p = u8b; - ut->chunkContents = &u8b->buf[u8b->bufStartIdx]; - ut->chunkLength = u8b->bufLimitIdx - u8b->bufStartIdx; - ut->chunkNativeStart = u8b->bufNativeStart; - ut->chunkNativeLimit = u8b->bufNativeLimit; - ut->nativeIndexingLimit = u8b->bufNILimit; - - // Index into the (now current) chunk - // For this function (swapBuffersAndFail), the requested index - // will always be at either the start or end of the chunk. - if (ix==u8b->bufNativeLimit) { - ut->chunkOffset = ut->chunkLength; - } else { - ut->chunkOffset = 0; - U_ASSERT(ix == u8b->bufNativeStart); - } - return false; - -makeStubBuffer: - // The user has done a seek/access past the start or end - // of the string. Rather than loading data that is likely - // to never be used, just set up a zero-length buffer at - // the position. - u8b = (UTF8Buf *)ut->q; - u8b->bufNativeStart = ix; - u8b->bufNativeLimit = ix; - u8b->bufStartIdx = 0; - u8b->bufLimitIdx = 0; - u8b->bufNILimit = 0; - u8b->toUCharsMapStart = ix; - u8b->mapToNative[0] = 0; - u8b->mapToUChars[0] = 0; - goto swapBuffersAndFail; - - - -fillForward: - { - // Move the incoming index to a code point boundary. - U8_SET_CP_START(s8, 0, ix); - - // Swap the UText buffers. - // We want to fill what was previously the alternate buffer, - // and make what was the current buffer be the new alternate. - UTF8Buf *u8b_swap = (UTF8Buf *)ut->q; - ut->q = ut->p; - ut->p = u8b_swap; - - int32_t strLen = ut->b; - UBool nulTerminated = false; - if (strLen < 0) { - strLen = 0x7fffffff; - nulTerminated = true; - } - - UChar *buf = u8b_swap->buf; - uint8_t *mapToNative = u8b_swap->mapToNative; - uint8_t *mapToUChars = u8b_swap->mapToUChars; - int32_t destIx = 0; - int32_t srcIx = ix; - UBool seenNonAscii = false; - UChar32 c = 0; - - // Fill the chunk buffer and mapping arrays. - while (destIx0 && c<0x80) { - // Special case ASCII range for speed. - // zero is excluded to simplify bounds checking. - buf[destIx] = (UChar)c; - mapToNative[destIx] = (uint8_t)(srcIx - ix); - mapToUChars[srcIx-ix] = (uint8_t)destIx; - srcIx++; - destIx++; - } else { - // General case, handle everything. - if (seenNonAscii == false) { - seenNonAscii = true; - u8b_swap->bufNILimit = destIx; - } - - int32_t cIx = srcIx; - int32_t dIx = destIx; - int32_t dIxSaved = destIx; - U8_NEXT_OR_FFFD(s8, srcIx, strLen, c); - if (c==0 && nulTerminated) { - srcIx--; - break; - } - - U16_APPEND_UNSAFE(buf, destIx, c); - do { - mapToNative[dIx++] = (uint8_t)(cIx - ix); - } while (dIx < destIx); - - do { - mapToUChars[cIx++ - ix] = (uint8_t)dIxSaved; - } while (cIx < srcIx); - } - if (srcIx>=strLen) { - break; - } - - } - - // store Native <--> Chunk Map entries for the end of the buffer. - // There is no actual character here, but the index position is valid. - mapToNative[destIx] = (uint8_t)(srcIx - ix); - mapToUChars[srcIx - ix] = (uint8_t)destIx; - - // fill in Buffer descriptor - u8b_swap->bufNativeStart = ix; - u8b_swap->bufNativeLimit = srcIx; - u8b_swap->bufStartIdx = 0; - u8b_swap->bufLimitIdx = destIx; - if (seenNonAscii == false) { - u8b_swap->bufNILimit = destIx; - } - u8b_swap->toUCharsMapStart = u8b_swap->bufNativeStart; - - // Set UText chunk to refer to this buffer. - ut->chunkContents = buf; - ut->chunkOffset = 0; - ut->chunkLength = u8b_swap->bufLimitIdx; - ut->chunkNativeStart = u8b_swap->bufNativeStart; - ut->chunkNativeLimit = u8b_swap->bufNativeLimit; - ut->nativeIndexingLimit = u8b_swap->bufNILimit; - - // For zero terminated strings, keep track of the maximum point - // scanned so far. - if (nulTerminated && srcIx>ut->c) { - ut->c = srcIx; - if (c==0) { - // We scanned to the end. - // Remember the actual length. - ut->b = srcIx; - ut->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); - } - } - return true; - } - - -fillReverse: - { - // Move the incoming index to a code point boundary. - // Can only do this if the incoming index is somewhere in the interior of the string. - // If index is at the end, there is no character there to look at. - if (ix != ut->b) { - // Note: this function will only move the index back if it is on a trail byte - // and there is a preceding lead byte and the sequence from the lead - // through this trail could be part of a valid UTF-8 sequence - // Otherwise the index remains unchanged. - U8_SET_CP_START(s8, 0, ix); - } - - // Swap the UText buffers. - // We want to fill what was previously the alternate buffer, - // and make what was the current buffer be the new alternate. - UTF8Buf *u8b_swap = (UTF8Buf *)ut->q; - ut->q = ut->p; - ut->p = u8b_swap; - - UChar *buf = u8b_swap->buf; - uint8_t *mapToNative = u8b_swap->mapToNative; - uint8_t *mapToUChars = u8b_swap->mapToUChars; - int32_t toUCharsMapStart = ix - sizeof(UTF8Buf::mapToUChars) + 1; - // Note that toUCharsMapStart can be negative. Happens when the remaining - // text from current position to the beginning is less than the buffer size. - // + 1 because mapToUChars must have a slot at the end for the bufNativeLimit entry. - int32_t destIx = UTF8_TEXT_CHUNK_SIZE+2; // Start in the overflow region - // at end of buffer to leave room - // for a surrogate pair at the - // buffer start. - int32_t srcIx = ix; - int32_t bufNILimit = destIx; - UChar32 c; - - // Map to/from Native Indexes, fill in for the position at the end of - // the buffer. - // - mapToNative[destIx] = (uint8_t)(srcIx - toUCharsMapStart); - mapToUChars[srcIx - toUCharsMapStart] = (uint8_t)destIx; - - // Fill the chunk buffer - // Work backwards, filling from the end of the buffer towards the front. - // - while (destIx>2 && (srcIx - toUCharsMapStart > 5) && (srcIx > 0)) { - srcIx--; - destIx--; - - // Get last byte of the UTF-8 character - c = s8[srcIx]; - if (c<0x80) { - // Special case ASCII range for speed. - buf[destIx] = (UChar)c; - U_ASSERT(toUCharsMapStart <= srcIx); - mapToUChars[srcIx - toUCharsMapStart] = (uint8_t)destIx; - mapToNative[destIx] = (uint8_t)(srcIx - toUCharsMapStart); - } else { - // General case, handle everything non-ASCII. - - int32_t sIx = srcIx; // ix of last byte of multi-byte u8 char - - // Get the full character from the UTF8 string. - // use code derived from the macros in utf8.h - // Leaves srcIx pointing at the first byte of the UTF-8 char. - // - c=utf8_prevCharSafeBody(s8, 0, &srcIx, c, -3); - // leaves srcIx at first byte of the multi-byte char. - - // Store the character in UTF-16 buffer. - if (c<0x10000) { - buf[destIx] = (UChar)c; - mapToNative[destIx] = (uint8_t)(srcIx - toUCharsMapStart); - } else { - buf[destIx] = U16_TRAIL(c); - mapToNative[destIx] = (uint8_t)(srcIx - toUCharsMapStart); - buf[--destIx] = U16_LEAD(c); - mapToNative[destIx] = (uint8_t)(srcIx - toUCharsMapStart); - } - - // Fill in the map from native indexes to UChars buf index. - do { - mapToUChars[sIx-- - toUCharsMapStart] = (uint8_t)destIx; - } while (sIx >= srcIx); - U_ASSERT(toUCharsMapStart <= (srcIx+1)); - - // Set native indexing limit to be the current position. - // We are processing a non-ascii, non-native-indexing char now; - // the limit will be here if the rest of the chars to be - // added to this buffer are ascii. - bufNILimit = destIx; - } - } - u8b_swap->bufNativeStart = srcIx; - u8b_swap->bufNativeLimit = ix; - u8b_swap->bufStartIdx = destIx; - u8b_swap->bufLimitIdx = UTF8_TEXT_CHUNK_SIZE+2; - u8b_swap->bufNILimit = bufNILimit - u8b_swap->bufStartIdx; - u8b_swap->toUCharsMapStart = toUCharsMapStart; - - ut->chunkContents = &buf[u8b_swap->bufStartIdx]; - ut->chunkLength = u8b_swap->bufLimitIdx - u8b_swap->bufStartIdx; - ut->chunkOffset = ut->chunkLength; - ut->chunkNativeStart = u8b_swap->bufNativeStart; - ut->chunkNativeLimit = u8b_swap->bufNativeLimit; - ut->nativeIndexingLimit = u8b_swap->bufNILimit; - return true; - } - -} - - - -// -// This is a slightly modified copy of u_strFromUTF8, -// Inserts a Replacement Char rather than failing on invalid UTF-8 -// Removes unnecessary features. -// -static UChar* -utext_strFromUTF8(UChar *dest, - int32_t destCapacity, - int32_t *pDestLength, - const char* src, - int32_t srcLength, // required. NUL terminated not supported. - UErrorCode *pErrorCode - ) -{ - - UChar *pDest = dest; - UChar *pDestLimit = (dest!=NULL)?(dest+destCapacity):NULL; - UChar32 ch=0; - int32_t index = 0; - int32_t reqLength = 0; - uint8_t* pSrc = (uint8_t*) src; - - - while((index < srcLength)&&(pDest0)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - int32_t length = ut->b; - int32_t start32 = pinIndex(start, length); - int32_t limit32 = pinIndex(limit, length); - - if(start32>limit32) { - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - - // adjust the incoming indexes to land on code point boundaries if needed. - // adjust by no more than three, because that is the largest number of trail bytes - // in a well formed UTF8 character. - const uint8_t *buf = (const uint8_t *)ut->context; - int i; - if (start32 < ut->chunkNativeLimit) { - for (i=0; i<3; i++) { - if (U8_IS_SINGLE(buf[start32]) || U8_IS_LEAD(buf[start32]) || start32==0) { - break; - } - start32--; - } - } - - if (limit32 < ut->chunkNativeLimit) { - for (i=0; i<3; i++) { - if (U8_IS_SINGLE(buf[limit32]) || U8_IS_LEAD(buf[limit32]) || limit32==0) { - break; - } - limit32--; - } - } - - // Do the actual extract. - int32_t destLength=0; - utext_strFromUTF8(dest, destCapacity, &destLength, - (const char *)ut->context+start32, limit32-start32, - pErrorCode); - utf8TextAccess(ut, limit32, true); - return destLength; -} - -// -// utf8TextMapOffsetToNative -// -// Map a chunk (UTF-16) offset to a native index. -static int64_t U_CALLCONV -utf8TextMapOffsetToNative(const UText *ut) { - // - UTF8Buf *u8b = (UTF8Buf *)ut->p; - U_ASSERT(ut->chunkOffset>ut->nativeIndexingLimit && ut->chunkOffset<=ut->chunkLength); - int32_t nativeOffset = u8b->mapToNative[ut->chunkOffset + u8b->bufStartIdx] + u8b->toUCharsMapStart; - U_ASSERT(nativeOffset >= ut->chunkNativeStart && nativeOffset <= ut->chunkNativeLimit); - return nativeOffset; -} - -// -// Map a native index to the corresponding chunk offset -// -static int32_t U_CALLCONV -utf8TextMapIndexToUTF16(const UText *ut, int64_t index64) { - U_ASSERT(index64 <= 0x7fffffff); - int32_t index = (int32_t)index64; - UTF8Buf *u8b = (UTF8Buf *)ut->p; - U_ASSERT(index>=ut->chunkNativeStart+ut->nativeIndexingLimit); - U_ASSERT(index<=ut->chunkNativeLimit); - int32_t mapIndex = index - u8b->toUCharsMapStart; - U_ASSERT(mapIndex < (int32_t)sizeof(UTF8Buf::mapToUChars)); - int32_t offset = u8b->mapToUChars[mapIndex] - u8b->bufStartIdx; - U_ASSERT(offset>=0 && offset<=ut->chunkLength); - return offset; -} - -static UText * U_CALLCONV -utf8TextClone(UText *dest, const UText *src, UBool deep, UErrorCode *status) -{ - // First do a generic shallow clone. Does everything needed for the UText struct itself. - dest = shallowTextClone(dest, src, status); - - // For deep clones, make a copy of the string. - // The copied storage is owned by the newly created clone. - // - // TODO: There is an issue with using utext_nativeLength(). - // That function is non-const in cases where the input was NUL terminated - // and the length has not yet been determined. - // This function (clone()) is const. - // There potentially a thread safety issue lurking here. - // - if (deep && U_SUCCESS(*status)) { - int32_t len = (int32_t)utext_nativeLength((UText *)src); - char *copyStr = (char *)uprv_malloc(len+1); - if (copyStr == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - } else { - uprv_memcpy(copyStr, src->context, len+1); - dest->context = copyStr; - dest->providerProperties |= I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT); - } - } - return dest; -} - - -static void U_CALLCONV -utf8TextClose(UText *ut) { - // Most of the work of close is done by the generic UText framework close. - // All that needs to be done here is to delete the UTF8 string if the UText - // owns it. This occurs if the UText was created by cloning. - if (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT)) { - char *s = (char *)ut->context; - uprv_free(s); - ut->context = NULL; - } -} - -U_CDECL_END - - -static const struct UTextFuncs utf8Funcs = -{ - sizeof(UTextFuncs), - 0, 0, 0, // Reserved alignment padding - utf8TextClone, - utf8TextLength, - utf8TextAccess, - utf8TextExtract, - NULL, /* replace*/ - NULL, /* copy */ - utf8TextMapOffsetToNative, - utf8TextMapIndexToUTF16, - utf8TextClose, - NULL, // spare 1 - NULL, // spare 2 - NULL // spare 3 -}; - - -static const char gEmptyString[] = {0}; - -U_CAPI UText * U_EXPORT2 -utext_openUTF8(UText *ut, const char *s, int64_t length, UErrorCode *status) { - if(U_FAILURE(*status)) { - return NULL; - } - if(s==NULL && length==0) { - s = gEmptyString; - } - - if(s==NULL || length<-1 || length>INT32_MAX) { - *status=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - ut = utext_setup(ut, sizeof(UTF8Buf) * 2, status); - if (U_FAILURE(*status)) { - return ut; - } - - ut->pFuncs = &utf8Funcs; - ut->context = s; - ut->b = (int32_t)length; - ut->c = (int32_t)length; - if (ut->c < 0) { - ut->c = 0; - ut->providerProperties |= I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); - } - ut->p = ut->pExtra; - ut->q = (char *)ut->pExtra + sizeof(UTF8Buf); - return ut; - -} - - - - - - - - -//------------------------------------------------------------------------------ -// -// UText implementation wrapper for Replaceable (read/write) -// -// Use of UText data members: -// context pointer to Replaceable. -// p pointer to Replaceable if it is owned by the UText. -// -//------------------------------------------------------------------------------ - - - -// minimum chunk size for this implementation: 3 -// to allow for possible trimming for code point boundaries -enum { REP_TEXT_CHUNK_SIZE=10 }; - -struct ReplExtra { - /* - * Chunk UChars. - * +1 to simplify filling with surrogate pair at the end. - */ - UChar s[REP_TEXT_CHUNK_SIZE+1]; -}; - - -U_CDECL_BEGIN - -static UText * U_CALLCONV -repTextClone(UText *dest, const UText *src, UBool deep, UErrorCode *status) { - // First do a generic shallow clone. Does everything needed for the UText struct itself. - dest = shallowTextClone(dest, src, status); - - // For deep clones, make a copy of the Replaceable. - // The copied Replaceable storage is owned by the newly created UText clone. - // A non-NULL pointer in UText.p is the signal to the close() function to delete - // it. - // - if (deep && U_SUCCESS(*status)) { - const Replaceable *replSrc = (const Replaceable *)src->context; - dest->context = replSrc->clone(); - dest->providerProperties |= I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT); - - // with deep clone, the copy is writable, even when the source is not. - dest->providerProperties |= I32_FLAG(UTEXT_PROVIDER_WRITABLE); - } - return dest; -} - - -static void U_CALLCONV -repTextClose(UText *ut) { - // Most of the work of close is done by the generic UText framework close. - // All that needs to be done here is delete the Replaceable if the UText - // owns it. This occurs if the UText was created by cloning. - if (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT)) { - Replaceable *rep = (Replaceable *)ut->context; - delete rep; - ut->context = NULL; - } -} - - -static int64_t U_CALLCONV -repTextLength(UText *ut) { - const Replaceable *replSrc = (const Replaceable *)ut->context; - int32_t len = replSrc->length(); - return len; -} - - -static UBool U_CALLCONV -repTextAccess(UText *ut, int64_t index, UBool forward) { - const Replaceable *rep=(const Replaceable *)ut->context; - int32_t length=rep->length(); // Full length of the input text (bigger than a chunk) - - // clip the requested index to the limits of the text. - int32_t index32 = pinIndex(index, length); - U_ASSERT(index<=INT32_MAX); - - - /* - * Compute start/limit boundaries around index, for a segment of text - * to be extracted. - * To allow for the possibility that our user gave an index to the trailing - * half of a surrogate pair, we must request one extra preceding UChar when - * going in the forward direction. This will ensure that the buffer has the - * entire code point at the specified index. - */ - if(forward) { - - if (index32>=ut->chunkNativeStart && index32chunkNativeLimit) { - // Buffer already contains the requested position. - ut->chunkOffset = (int32_t)(index - ut->chunkNativeStart); - return true; - } - if (index32>=length && ut->chunkNativeLimit==length) { - // Request for end of string, and buffer already extends up to it. - // Can't get the data, but don't change the buffer. - ut->chunkOffset = length - (int32_t)ut->chunkNativeStart; - return false; - } - - ut->chunkNativeLimit = index + REP_TEXT_CHUNK_SIZE - 1; - // Going forward, so we want to have the buffer with stuff at and beyond - // the requested index. The -1 gets us one code point before the - // requested index also, to handle the case of the index being on - // a trail surrogate of a surrogate pair. - if(ut->chunkNativeLimit > length) { - ut->chunkNativeLimit = length; - } - // unless buffer ran off end, start is index-1. - ut->chunkNativeStart = ut->chunkNativeLimit - REP_TEXT_CHUNK_SIZE; - if(ut->chunkNativeStart < 0) { - ut->chunkNativeStart = 0; - } - } else { - // Reverse iteration. Fill buffer with data preceding the requested index. - if (index32>ut->chunkNativeStart && index32<=ut->chunkNativeLimit) { - // Requested position already in buffer. - ut->chunkOffset = index32 - (int32_t)ut->chunkNativeStart; - return true; - } - if (index32==0 && ut->chunkNativeStart==0) { - // Request for start, buffer already begins at start. - // No data, but keep the buffer as is. - ut->chunkOffset = 0; - return false; - } - - // Figure out the bounds of the chunk to extract for reverse iteration. - // Need to worry about chunk not splitting surrogate pairs, and while still - // containing the data we need. - // Fix by requesting a chunk that includes an extra UChar at the end. - // If this turns out to be a lead surrogate, we can lop it off and still have - // the data we wanted. - ut->chunkNativeStart = index32 + 1 - REP_TEXT_CHUNK_SIZE; - if (ut->chunkNativeStart < 0) { - ut->chunkNativeStart = 0; - } - - ut->chunkNativeLimit = index32 + 1; - if (ut->chunkNativeLimit > length) { - ut->chunkNativeLimit = length; - } - } - - // Extract the new chunk of text from the Replaceable source. - ReplExtra *ex = (ReplExtra *)ut->pExtra; - // UnicodeString with its buffer a writable alias to the chunk buffer - UnicodeString buffer(ex->s, 0 /*buffer length*/, REP_TEXT_CHUNK_SIZE /*buffer capacity*/); - rep->extractBetween((int32_t)ut->chunkNativeStart, (int32_t)ut->chunkNativeLimit, buffer); - - ut->chunkContents = ex->s; - ut->chunkLength = (int32_t)(ut->chunkNativeLimit - ut->chunkNativeStart); - ut->chunkOffset = (int32_t)(index32 - ut->chunkNativeStart); - - // Surrogate pairs from the input text must not span chunk boundaries. - // If end of chunk could be the start of a surrogate, trim it off. - if (ut->chunkNativeLimit < length && - U16_IS_LEAD(ex->s[ut->chunkLength-1])) { - ut->chunkLength--; - ut->chunkNativeLimit--; - if (ut->chunkOffset > ut->chunkLength) { - ut->chunkOffset = ut->chunkLength; - } - } - - // if the first UChar in the chunk could be the trailing half of a surrogate pair, - // trim it off. - if(ut->chunkNativeStart>0 && U16_IS_TRAIL(ex->s[0])) { - ++(ut->chunkContents); - ++(ut->chunkNativeStart); - --(ut->chunkLength); - --(ut->chunkOffset); - } - - // adjust the index/chunkOffset to a code point boundary - U16_SET_CP_START(ut->chunkContents, 0, ut->chunkOffset); - - // Use fast indexing for get/setNativeIndex() - ut->nativeIndexingLimit = ut->chunkLength; - - return true; -} - - - -static int32_t U_CALLCONV -repTextExtract(UText *ut, - int64_t start, int64_t limit, - UChar *dest, int32_t destCapacity, - UErrorCode *status) { - const Replaceable *rep=(const Replaceable *)ut->context; - int32_t length=rep->length(); - - if(U_FAILURE(*status)) { - return 0; - } - if(destCapacity<0 || (dest==NULL && destCapacity>0)) { - *status=U_ILLEGAL_ARGUMENT_ERROR; - } - if(start>limit) { - *status=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - int32_t start32 = pinIndex(start, length); - int32_t limit32 = pinIndex(limit, length); - - // adjust start, limit if they point to trail half of surrogates - if (start32charAt(start32)) && - U_IS_SUPPLEMENTARY(rep->char32At(start32))){ - start32--; - } - if (limit32charAt(limit32)) && - U_IS_SUPPLEMENTARY(rep->char32At(limit32))){ - limit32--; - } - - length=limit32-start32; - if(length>destCapacity) { - limit32 = start32 + destCapacity; - } - UnicodeString buffer(dest, 0, destCapacity); // writable alias - rep->extractBetween(start32, limit32, buffer); - repTextAccess(ut, limit32, true); - - return u_terminateUChars(dest, destCapacity, length, status); -} - -static int32_t U_CALLCONV -repTextReplace(UText *ut, - int64_t start, int64_t limit, - const UChar *src, int32_t length, - UErrorCode *status) { - Replaceable *rep=(Replaceable *)ut->context; - int32_t oldLength; - - if(U_FAILURE(*status)) { - return 0; - } - if(src==NULL && length!=0) { - *status=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - oldLength=rep->length(); // will subtract from new length - if(start>limit ) { - *status=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - int32_t start32 = pinIndex(start, oldLength); - int32_t limit32 = pinIndex(limit, oldLength); - - // Snap start & limit to code point boundaries. - if (start32charAt(start32)) && - start32>0 && U16_IS_LEAD(rep->charAt(start32-1))) - { - start32--; - } - if (limit32charAt(limit32-1)) && - U16_IS_TRAIL(rep->charAt(limit32))) - { - limit32++; - } - - // Do the actual replace operation using methods of the Replaceable class - UnicodeString replStr((UBool)(length<0), src, length); // read-only alias - rep->handleReplaceBetween(start32, limit32, replStr); - int32_t newLength = rep->length(); - int32_t lengthDelta = newLength - oldLength; - - // Is the UText chunk buffer OK? - if (ut->chunkNativeLimit > start32) { - // this replace operation may have impacted the current chunk. - // invalidate it, which will force a reload on the next access. - invalidateChunk(ut); - } - - // set the iteration position to the end of the newly inserted replacement text. - int32_t newIndexPos = limit32 + lengthDelta; - repTextAccess(ut, newIndexPos, true); - - return lengthDelta; -} - - -static void U_CALLCONV -repTextCopy(UText *ut, - int64_t start, int64_t limit, - int64_t destIndex, - UBool move, - UErrorCode *status) -{ - Replaceable *rep=(Replaceable *)ut->context; - int32_t length=rep->length(); - - if(U_FAILURE(*status)) { - return; - } - if (start>limit || (startcopy(start32, limit32, destIndex32); - if(destIndex32handleReplaceBetween(start32, limit32, UnicodeString()); - } else { - // copy - rep->copy(start32, limit32, destIndex32); - } - - // If the change to the text touched the region in the chunk buffer, - // invalidate the buffer. - int32_t firstAffectedIndex = destIndex32; - if (move && start32chunkNativeLimit) { - // changes may have affected range covered by the chunk - invalidateChunk(ut); - } - - // Put iteration position at the newly inserted (moved) block, - int32_t nativeIterIndex = destIndex32 + limit32 - start32; - if (move && destIndex32>start32) { - // moved a block of text towards the end of the string. - nativeIterIndex = destIndex32; - } - - // Set position, reload chunk if needed. - repTextAccess(ut, nativeIterIndex, true); -} - -static const struct UTextFuncs repFuncs = -{ - sizeof(UTextFuncs), - 0, 0, 0, // Reserved alignment padding - repTextClone, - repTextLength, - repTextAccess, - repTextExtract, - repTextReplace, - repTextCopy, - NULL, // MapOffsetToNative, - NULL, // MapIndexToUTF16, - repTextClose, - NULL, // spare 1 - NULL, // spare 2 - NULL // spare 3 -}; - - -U_CAPI UText * U_EXPORT2 -utext_openReplaceable(UText *ut, Replaceable *rep, UErrorCode *status) -{ - if(U_FAILURE(*status)) { - return NULL; - } - if(rep==NULL) { - *status=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - ut = utext_setup(ut, sizeof(ReplExtra), status); - if(U_FAILURE(*status)) { - return ut; - } - - ut->providerProperties = I32_FLAG(UTEXT_PROVIDER_WRITABLE); - if(rep->hasMetaData()) { - ut->providerProperties |=I32_FLAG(UTEXT_PROVIDER_HAS_META_DATA); - } - - ut->pFuncs = &repFuncs; - ut->context = rep; - return ut; -} - -U_CDECL_END - - - - - - - - -//------------------------------------------------------------------------------ -// -// UText implementation for UnicodeString (read/write) and -// for const UnicodeString (read only) -// (same implementation, only the flags are different) -// -// Use of UText data members: -// context pointer to UnicodeString -// p pointer to UnicodeString IF this UText owns the string -// and it must be deleted on close(). NULL otherwise. -// -//------------------------------------------------------------------------------ - -U_CDECL_BEGIN - - -static UText * U_CALLCONV -unistrTextClone(UText *dest, const UText *src, UBool deep, UErrorCode *status) { - // First do a generic shallow clone. Does everything needed for the UText struct itself. - dest = shallowTextClone(dest, src, status); - - // For deep clones, make a copy of the UnicodeSring. - // The copied UnicodeString storage is owned by the newly created UText clone. - // A non-NULL pointer in UText.p is the signal to the close() function to delete - // the UText. - // - if (deep && U_SUCCESS(*status)) { - const UnicodeString *srcString = (const UnicodeString *)src->context; - dest->context = new UnicodeString(*srcString); - dest->providerProperties |= I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT); - - // with deep clone, the copy is writable, even when the source is not. - dest->providerProperties |= I32_FLAG(UTEXT_PROVIDER_WRITABLE); - } - return dest; -} - -static void U_CALLCONV -unistrTextClose(UText *ut) { - // Most of the work of close is done by the generic UText framework close. - // All that needs to be done here is delete the UnicodeString if the UText - // owns it. This occurs if the UText was created by cloning. - if (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT)) { - UnicodeString *str = (UnicodeString *)ut->context; - delete str; - ut->context = NULL; - } -} - - -static int64_t U_CALLCONV -unistrTextLength(UText *t) { - return ((const UnicodeString *)t->context)->length(); -} - - -static UBool U_CALLCONV -unistrTextAccess(UText *ut, int64_t index, UBool forward) { - int32_t length = ut->chunkLength; - ut->chunkOffset = pinIndex(index, length); - - // Check whether request is at the start or end - UBool retVal = (forward && index0); - return retVal; -} - - - -static int32_t U_CALLCONV -unistrTextExtract(UText *t, - int64_t start, int64_t limit, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) { - const UnicodeString *us=(const UnicodeString *)t->context; - int32_t length=us->length(); - - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if(destCapacity<0 || (dest==NULL && destCapacity>0)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - } - if(start<0 || start>limit) { - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - int32_t start32 = startgetChar32Start((int32_t)start) : length; - int32_t limit32 = limitgetChar32Start((int32_t)limit) : length; - - length=limit32-start32; - if (destCapacity>0 && dest!=NULL) { - int32_t trimmedLength = length; - if(trimmedLength>destCapacity) { - trimmedLength=destCapacity; - } - us->extract(start32, trimmedLength, dest); - t->chunkOffset = start32+trimmedLength; - } else { - t->chunkOffset = start32; - } - u_terminateUChars(dest, destCapacity, length, pErrorCode); - return length; -} - -static int32_t U_CALLCONV -unistrTextReplace(UText *ut, - int64_t start, int64_t limit, - const UChar *src, int32_t length, - UErrorCode *pErrorCode) { - UnicodeString *us=(UnicodeString *)ut->context; - int32_t oldLength; - - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if(src==NULL && length!=0) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - } - if(start>limit) { - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - oldLength=us->length(); - int32_t start32 = pinIndex(start, oldLength); - int32_t limit32 = pinIndex(limit, oldLength); - if (start32 < oldLength) { - start32 = us->getChar32Start(start32); - } - if (limit32 < oldLength) { - limit32 = us->getChar32Start(limit32); - } - - // replace - us->replace(start32, limit32-start32, src, length); - int32_t newLength = us->length(); - - // Update the chunk description. - ut->chunkContents = us->getBuffer(); - ut->chunkLength = newLength; - ut->chunkNativeLimit = newLength; - ut->nativeIndexingLimit = newLength; - - // Set iteration position to the point just following the newly inserted text. - int32_t lengthDelta = newLength - oldLength; - ut->chunkOffset = limit32 + lengthDelta; - - return lengthDelta; -} - -static void U_CALLCONV -unistrTextCopy(UText *ut, - int64_t start, int64_t limit, - int64_t destIndex, - UBool move, - UErrorCode *pErrorCode) { - UnicodeString *us=(UnicodeString *)ut->context; - int32_t length=us->length(); - - if(U_FAILURE(*pErrorCode)) { - return; - } - int32_t start32 = pinIndex(start, length); - int32_t limit32 = pinIndex(limit, length); - int32_t destIndex32 = pinIndex(destIndex, length); - - if( start32>limit32 || (start32copy(start32, limit32, destIndex32); - if(destIndex32remove(start32, segLength); - } else { - // copy - us->copy(start32, limit32, destIndex32); - } - - // update chunk description, set iteration position. - ut->chunkContents = us->getBuffer(); - if (move==false) { - // copy operation, string length grows - ut->chunkLength += limit32-start32; - ut->chunkNativeLimit = ut->chunkLength; - ut->nativeIndexingLimit = ut->chunkLength; - } - - // Iteration position to end of the newly inserted text. - ut->chunkOffset = destIndex32+limit32-start32; - if (move && destIndex32>start32) { - ut->chunkOffset = destIndex32; - } - -} - -static const struct UTextFuncs unistrFuncs = -{ - sizeof(UTextFuncs), - 0, 0, 0, // Reserved alignment padding - unistrTextClone, - unistrTextLength, - unistrTextAccess, - unistrTextExtract, - unistrTextReplace, - unistrTextCopy, - NULL, // MapOffsetToNative, - NULL, // MapIndexToUTF16, - unistrTextClose, - NULL, // spare 1 - NULL, // spare 2 - NULL // spare 3 -}; - - - -U_CDECL_END - - -U_CAPI UText * U_EXPORT2 -utext_openUnicodeString(UText *ut, UnicodeString *s, UErrorCode *status) { - ut = utext_openConstUnicodeString(ut, s, status); - if (U_SUCCESS(*status)) { - ut->providerProperties |= I32_FLAG(UTEXT_PROVIDER_WRITABLE); - } - return ut; -} - - - -U_CAPI UText * U_EXPORT2 -utext_openConstUnicodeString(UText *ut, const UnicodeString *s, UErrorCode *status) { - if (U_SUCCESS(*status) && s->isBogus()) { - // The UnicodeString is bogus, but we still need to detach the UText - // from whatever it was hooked to before, if anything. - utext_openUChars(ut, NULL, 0, status); - *status = U_ILLEGAL_ARGUMENT_ERROR; - return ut; - } - ut = utext_setup(ut, 0, status); - // note: use the standard (writable) function table for UnicodeString. - // The flag settings disable writing, so having the functions in - // the table is harmless. - if (U_SUCCESS(*status)) { - ut->pFuncs = &unistrFuncs; - ut->context = s; - ut->providerProperties = I32_FLAG(UTEXT_PROVIDER_STABLE_CHUNKS); - ut->chunkContents = s->getBuffer(); - ut->chunkLength = s->length(); - ut->chunkNativeStart = 0; - ut->chunkNativeLimit = ut->chunkLength; - ut->nativeIndexingLimit = ut->chunkLength; - } - return ut; -} - -//------------------------------------------------------------------------------ -// -// UText implementation for const UChar * strings -// -// Use of UText data members: -// context pointer to UnicodeString -// a length. -1 if not yet known. -// -// TODO: support 64 bit lengths. -// -//------------------------------------------------------------------------------ - -U_CDECL_BEGIN - - -static UText * U_CALLCONV -ucstrTextClone(UText *dest, const UText * src, UBool deep, UErrorCode * status) { - // First do a generic shallow clone. - dest = shallowTextClone(dest, src, status); - - // For deep clones, make a copy of the string. - // The copied storage is owned by the newly created clone. - // A non-NULL pointer in UText.p is the signal to the close() function to delete - // it. - // - if (deep && U_SUCCESS(*status)) { - U_ASSERT(utext_nativeLength(dest) < INT32_MAX); - int32_t len = (int32_t)utext_nativeLength(dest); - - // The cloned string IS going to be NUL terminated, whether or not the original was. - const UChar *srcStr = (const UChar *)src->context; - UChar *copyStr = (UChar *)uprv_malloc((len+1) * sizeof(UChar)); - if (copyStr == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - } else { - int64_t i; - for (i=0; icontext = copyStr; - dest->providerProperties |= I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT); - } - } - return dest; -} - - -static void U_CALLCONV -ucstrTextClose(UText *ut) { - // Most of the work of close is done by the generic UText framework close. - // All that needs to be done here is delete the string if the UText - // owns it. This occurs if the UText was created by cloning. - if (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT)) { - UChar *s = (UChar *)ut->context; - uprv_free(s); - ut->context = NULL; - } -} - - - -static int64_t U_CALLCONV -ucstrTextLength(UText *ut) { - if (ut->a < 0) { - // null terminated, we don't yet know the length. Scan for it. - // Access is not convenient for doing this - // because the current iteration position can't be changed. - const UChar *str = (const UChar *)ut->context; - for (;;) { - if (str[ut->chunkNativeLimit] == 0) { - break; - } - ut->chunkNativeLimit++; - } - ut->a = ut->chunkNativeLimit; - ut->chunkLength = (int32_t)ut->chunkNativeLimit; - ut->nativeIndexingLimit = ut->chunkLength; - ut->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); - } - return ut->a; -} - - -static UBool U_CALLCONV -ucstrTextAccess(UText *ut, int64_t index, UBool forward) { - const UChar *str = (const UChar *)ut->context; - - // pin the requested index to the bounds of the string, - // and set current iteration position. - if (index<0) { - index = 0; - } else if (index < ut->chunkNativeLimit) { - // The request data is within the chunk as it is known so far. - // Put index on a code point boundary. - U16_SET_CP_START(str, 0, index); - } else if (ut->a >= 0) { - // We know the length of this string, and the user is requesting something - // at or beyond the length. Pin the requested index to the length. - index = ut->a; - } else { - // Null terminated string, length not yet known, and the requested index - // is beyond where we have scanned so far. - // Scan to 32 UChars beyond the requested index. The strategy here is - // to avoid fully scanning a long string when the caller only wants to - // see a few characters at its beginning. - int32_t scanLimit = (int32_t)index + 32; - if ((index + 32)>INT32_MAX || (index + 32)<0 ) { // note: int64 expression - scanLimit = INT32_MAX; - } - - int32_t chunkLimit = (int32_t)ut->chunkNativeLimit; - for (; chunkLimita = chunkLimit; - ut->chunkLength = chunkLimit; - ut->nativeIndexingLimit = chunkLimit; - if (index >= chunkLimit) { - index = chunkLimit; - } else { - U16_SET_CP_START(str, 0, index); - } - - ut->chunkNativeLimit = chunkLimit; - ut->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); - goto breakout; - } - } - // We scanned through the next batch of UChars without finding the end. - U16_SET_CP_START(str, 0, index); - if (chunkLimit == INT32_MAX) { - // Scanned to the limit of a 32 bit length. - // Forceably trim the overlength string back so length fits in int32 - // TODO: add support for 64 bit strings. - ut->a = chunkLimit; - ut->chunkLength = chunkLimit; - ut->nativeIndexingLimit = chunkLimit; - if (index > chunkLimit) { - index = chunkLimit; - } - ut->chunkNativeLimit = chunkLimit; - ut->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); - } else { - // The endpoint of a chunk must not be left in the middle of a surrogate pair. - // If the current end is on a lead surrogate, back the end up by one. - // It doesn't matter if the end char happens to be an unpaired surrogate, - // and it's simpler not to worry about it. - if (U16_IS_LEAD(str[chunkLimit-1])) { - --chunkLimit; - } - // Null-terminated chunk with end still unknown. - // Update the chunk length to reflect what has been scanned thus far. - // That the full length is still unknown is (still) flagged by - // ut->a being < 0. - ut->chunkNativeLimit = chunkLimit; - ut->nativeIndexingLimit = chunkLimit; - ut->chunkLength = chunkLimit; - } - - } -breakout: - U_ASSERT(index<=INT32_MAX); - ut->chunkOffset = (int32_t)index; - - // Check whether request is at the start or end - UBool retVal = (forward && indexchunkNativeLimit) || (!forward && index>0); - return retVal; -} - - - -static int32_t U_CALLCONV -ucstrTextExtract(UText *ut, - int64_t start, int64_t limit, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) -{ - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if(destCapacity<0 || (dest==NULL && destCapacity>0) || start>limit) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - //const UChar *s=(const UChar *)ut->context; - int32_t si, di; - - int32_t start32; - int32_t limit32; - - // Access the start. Does two things we need: - // Pins 'start' to the length of the string, if it came in out-of-bounds. - // Snaps 'start' to the beginning of a code point. - ucstrTextAccess(ut, start, true); - const UChar *s=ut->chunkContents; - start32 = ut->chunkOffset; - - int32_t strLength=(int32_t)ut->a; - if (strLength >= 0) { - limit32 = pinIndex(limit, strLength); - } else { - limit32 = pinIndex(limit, INT32_MAX); - } - di = 0; - for (si=start32; sia = si; // set string length for this UText - ut->chunkNativeLimit = si; - ut->chunkLength = si; - ut->nativeIndexingLimit = si; - strLength = si; - limit32 = si; - break; - } - U_ASSERT(di>=0); /* to ensure di never exceeds INT32_MAX, which must not happen logically */ - if (di=0) { - // We have filled the destination buffer, and the string length is known. - // Cut the loop short. There is no need to scan string termination. - di = limit32 - start32; - si = limit32; - break; - } - } - di++; - } - - // If the limit index points to a lead surrogate of a pair, - // add the corresponding trail surrogate to the destination. - if (si>0 && U16_IS_LEAD(s[si-1]) && - ((sichunkNativeLimit) { - ut->chunkOffset = si; - } else { - ucstrTextAccess(ut, si, true); - } - - // Add a terminating NUL if space in the buffer permits, - // and set the error status as required. - u_terminateUChars(dest, destCapacity, di, pErrorCode); - return di; -} - -static const struct UTextFuncs ucstrFuncs = -{ - sizeof(UTextFuncs), - 0, 0, 0, // Reserved alignment padding - ucstrTextClone, - ucstrTextLength, - ucstrTextAccess, - ucstrTextExtract, - NULL, // Replace - NULL, // Copy - NULL, // MapOffsetToNative, - NULL, // MapIndexToUTF16, - ucstrTextClose, - NULL, // spare 1 - NULL, // spare 2 - NULL, // spare 3 -}; - -U_CDECL_END - -static const UChar gEmptyUString[] = {0}; - -U_CAPI UText * U_EXPORT2 -utext_openUChars(UText *ut, const UChar *s, int64_t length, UErrorCode *status) { - if (U_FAILURE(*status)) { - return NULL; - } - if(s==NULL && length==0) { - s = gEmptyUString; - } - if (s==NULL || length < -1 || length>INT32_MAX) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - ut = utext_setup(ut, 0, status); - if (U_SUCCESS(*status)) { - ut->pFuncs = &ucstrFuncs; - ut->context = s; - ut->providerProperties = I32_FLAG(UTEXT_PROVIDER_STABLE_CHUNKS); - if (length==-1) { - ut->providerProperties |= I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); - } - ut->a = length; - ut->chunkContents = s; - ut->chunkNativeStart = 0; - ut->chunkNativeLimit = length>=0? length : 0; - ut->chunkLength = (int32_t)ut->chunkNativeLimit; - ut->chunkOffset = 0; - ut->nativeIndexingLimit = ut->chunkLength; - } - return ut; -} - - -//------------------------------------------------------------------------------ -// -// UText implementation for text from ICU CharacterIterators -// -// Use of UText data members: -// context pointer to the CharacterIterator -// a length of the full text. -// p pointer to buffer 1 -// b start index of local buffer 1 contents -// q pointer to buffer 2 -// c start index of local buffer 2 contents -// r pointer to the character iterator if the UText owns it. -// Null otherwise. -// -//------------------------------------------------------------------------------ -#define CIBufSize 16 - -U_CDECL_BEGIN -static void U_CALLCONV -charIterTextClose(UText *ut) { - // Most of the work of close is done by the generic UText framework close. - // All that needs to be done here is delete the CharacterIterator if the UText - // owns it. This occurs if the UText was created by cloning. - CharacterIterator *ci = (CharacterIterator *)ut->r; - delete ci; - ut->r = NULL; -} - -static int64_t U_CALLCONV -charIterTextLength(UText *ut) { - return (int32_t)ut->a; -} - -static UBool U_CALLCONV -charIterTextAccess(UText *ut, int64_t index, UBool forward) { - CharacterIterator *ci = (CharacterIterator *)ut->context; - - int32_t clippedIndex = (int32_t)index; - if (clippedIndex<0) { - clippedIndex=0; - } else if (clippedIndex>=ut->a) { - clippedIndex=(int32_t)ut->a; - } - int32_t neededIndex = clippedIndex; - if (!forward && neededIndex>0) { - // reverse iteration, want the position just before what was asked for. - neededIndex--; - } else if (forward && neededIndex==ut->a && neededIndex>0) { - // Forward iteration, don't ask for something past the end of the text. - neededIndex--; - } - - // Find the native index of the start of the buffer containing what we want. - neededIndex -= neededIndex % CIBufSize; - - UChar *buf = NULL; - UBool needChunkSetup = true; - int i; - if (ut->chunkNativeStart == neededIndex) { - // The buffer we want is already the current chunk. - needChunkSetup = false; - } else if (ut->b == neededIndex) { - // The first buffer (buffer p) has what we need. - buf = (UChar *)ut->p; - } else if (ut->c == neededIndex) { - // The second buffer (buffer q) has what we need. - buf = (UChar *)ut->q; - } else { - // Neither buffer already has what we need. - // Load new data from the character iterator. - // Use the buf that is not the current buffer. - buf = (UChar *)ut->p; - if (ut->p == ut->chunkContents) { - buf = (UChar *)ut->q; - } - ci->setIndex(neededIndex); - for (i=0; inextPostInc(); - if (i+neededIndex > ut->a) { - break; - } - } - } - - // We have a buffer with the data we need. - // Set it up as the current chunk, if it wasn't already. - if (needChunkSetup) { - ut->chunkContents = buf; - ut->chunkLength = CIBufSize; - ut->chunkNativeStart = neededIndex; - ut->chunkNativeLimit = neededIndex + CIBufSize; - if (ut->chunkNativeLimit > ut->a) { - ut->chunkNativeLimit = ut->a; - ut->chunkLength = (int32_t)(ut->chunkNativeLimit)-(int32_t)(ut->chunkNativeStart); - } - ut->nativeIndexingLimit = ut->chunkLength; - U_ASSERT(ut->chunkOffset>=0 && ut->chunkOffset<=CIBufSize); - } - ut->chunkOffset = clippedIndex - (int32_t)ut->chunkNativeStart; - UBool success = (forward? ut->chunkOffsetchunkLength : ut->chunkOffset>0); - return success; -} - -static UText * U_CALLCONV -charIterTextClone(UText *dest, const UText *src, UBool deep, UErrorCode * status) { - if (U_FAILURE(*status)) { - return NULL; - } - - if (deep) { - // There is no CharacterIterator API for cloning the underlying text storage. - *status = U_UNSUPPORTED_ERROR; - return NULL; - } else { - CharacterIterator *srcCI =(CharacterIterator *)src->context; - srcCI = srcCI->clone(); - dest = utext_openCharacterIterator(dest, srcCI, status); - if (U_FAILURE(*status)) { - return dest; - } - // cast off const on getNativeIndex. - // For CharacterIterator based UTexts, this is safe, the operation is const. - int64_t ix = utext_getNativeIndex((UText *)src); - utext_setNativeIndex(dest, ix); - dest->r = srcCI; // flags that this UText owns the CharacterIterator - } - return dest; -} - -static int32_t U_CALLCONV -charIterTextExtract(UText *ut, - int64_t start, int64_t limit, - UChar *dest, int32_t destCapacity, - UErrorCode *status) -{ - if(U_FAILURE(*status)) { - return 0; - } - if(destCapacity<0 || (dest==NULL && destCapacity>0) || start>limit) { - *status=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - int32_t length = (int32_t)ut->a; - int32_t start32 = pinIndex(start, length); - int32_t limit32 = pinIndex(limit, length); - int32_t desti = 0; - int32_t srci; - int32_t copyLimit; - - CharacterIterator *ci = (CharacterIterator *)ut->context; - ci->setIndex32(start32); // Moves ix to lead of surrogate pair, if needed. - srci = ci->getIndex(); - copyLimit = srci; - while (srcinext32PostInc(); - int32_t len = U16_LENGTH(c); - U_ASSERT(desti+len>0); /* to ensure desti+len never exceeds MAX_INT32, which must not happen logically */ - if (desti+len <= destCapacity) { - U16_APPEND_UNSAFE(dest, desti, c); - copyLimit = srci+len; - } else { - desti += len; - *status = U_BUFFER_OVERFLOW_ERROR; - } - srci += len; - } - - charIterTextAccess(ut, copyLimit, true); - - u_terminateUChars(dest, destCapacity, desti, status); - return desti; -} - -static const struct UTextFuncs charIterFuncs = -{ - sizeof(UTextFuncs), - 0, 0, 0, // Reserved alignment padding - charIterTextClone, - charIterTextLength, - charIterTextAccess, - charIterTextExtract, - NULL, // Replace - NULL, // Copy - NULL, // MapOffsetToNative, - NULL, // MapIndexToUTF16, - charIterTextClose, - NULL, // spare 1 - NULL, // spare 2 - NULL // spare 3 -}; -U_CDECL_END - - -U_CAPI UText * U_EXPORT2 -utext_openCharacterIterator(UText *ut, CharacterIterator *ci, UErrorCode *status) { - if (U_FAILURE(*status)) { - return NULL; - } - - if (ci->startIndex() > 0) { - // No support for CharacterIterators that do not start indexing from zero. - *status = U_UNSUPPORTED_ERROR; - return NULL; - } - - // Extra space in UText for 2 buffers of CIBufSize UChars each. - int32_t extraSpace = 2 * CIBufSize * sizeof(UChar); - ut = utext_setup(ut, extraSpace, status); - if (U_SUCCESS(*status)) { - ut->pFuncs = &charIterFuncs; - ut->context = ci; - ut->providerProperties = 0; - ut->a = ci->endIndex(); // Length of text - ut->p = ut->pExtra; // First buffer - ut->b = -1; // Native index of first buffer contents - ut->q = (UChar*)ut->pExtra+CIBufSize; // Second buffer - ut->c = -1; // Native index of second buffer contents - - // Initialize current chunk contents to be empty. - // First access will fault something in. - // Note: The initial nativeStart and chunkOffset must sum to zero - // so that getNativeIndex() will correctly compute to zero - // if no call to Access() has ever been made. They can't be both - // zero without Access() thinking that the chunk is valid. - ut->chunkContents = (UChar *)ut->p; - ut->chunkNativeStart = -1; - ut->chunkOffset = 1; - ut->chunkNativeLimit = 0; - ut->chunkLength = 0; - ut->nativeIndexingLimit = ut->chunkOffset; // enables native indexing - } - return ut; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2005-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: utext.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2005apr12 +* created by: Markus W. Scherer +*/ + +#include + +#include "unicode/utypes.h" +#include "unicode/ustring.h" +#include "unicode/unistr.h" +#include "unicode/chariter.h" +#include "unicode/utext.h" +#include "unicode/utf.h" +#include "unicode/utf8.h" +#include "unicode/utf16.h" +#include "ustr_imp.h" +#include "cmemory.h" +#include "cstring.h" +#include "uassert.h" +#include "putilimp.h" + +U_NAMESPACE_USE + +#define I32_FLAG(bitIndex) ((int32_t)1<<(bitIndex)) + + +static UBool +utext_access(UText *ut, int64_t index, UBool forward) { + return ut->pFuncs->access(ut, index, forward); +} + + + +U_CAPI UBool U_EXPORT2 +utext_moveIndex32(UText *ut, int32_t delta) { + UChar32 c; + if (delta > 0) { + do { + if(ut->chunkOffset>=ut->chunkLength && !utext_access(ut, ut->chunkNativeLimit, true)) { + return false; + } + c = ut->chunkContents[ut->chunkOffset]; + if (U16_IS_SURROGATE(c)) { + c = utext_next32(ut); + if (c == U_SENTINEL) { + return false; + } + } else { + ut->chunkOffset++; + } + } while(--delta>0); + + } else if (delta<0) { + do { + if(ut->chunkOffset<=0 && !utext_access(ut, ut->chunkNativeStart, false)) { + return false; + } + c = ut->chunkContents[ut->chunkOffset-1]; + if (U16_IS_SURROGATE(c)) { + c = utext_previous32(ut); + if (c == U_SENTINEL) { + return false; + } + } else { + ut->chunkOffset--; + } + } while(++delta<0); + } + + return true; +} + + +U_CAPI int64_t U_EXPORT2 +utext_nativeLength(UText *ut) { + return ut->pFuncs->nativeLength(ut); +} + + +U_CAPI UBool U_EXPORT2 +utext_isLengthExpensive(const UText *ut) { + UBool r = (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE)) != 0; + return r; +} + + +U_CAPI int64_t U_EXPORT2 +utext_getNativeIndex(const UText *ut) { + if(ut->chunkOffset <= ut->nativeIndexingLimit) { + return ut->chunkNativeStart+ut->chunkOffset; + } else { + return ut->pFuncs->mapOffsetToNative(ut); + } +} + + +U_CAPI void U_EXPORT2 +utext_setNativeIndex(UText *ut, int64_t index) { + if(indexchunkNativeStart || index>=ut->chunkNativeLimit) { + // The desired position is outside of the current chunk. + // Access the new position. Assume a forward iteration from here, + // which will also be optimimum for a single random access. + // Reverse iterations may suffer slightly. + ut->pFuncs->access(ut, index, true); + } else if((int32_t)(index - ut->chunkNativeStart) <= ut->nativeIndexingLimit) { + // utf-16 indexing. + ut->chunkOffset=(int32_t)(index-ut->chunkNativeStart); + } else { + ut->chunkOffset=ut->pFuncs->mapNativeIndexToUTF16(ut, index); + } + // The convention is that the index must always be on a code point boundary. + // Adjust the index position if it is in the middle of a surrogate pair. + if (ut->chunkOffsetchunkLength) { + char16_t c= ut->chunkContents[ut->chunkOffset]; + if (U16_IS_TRAIL(c)) { + if (ut->chunkOffset==0) { + ut->pFuncs->access(ut, ut->chunkNativeStart, false); + } + if (ut->chunkOffset>0) { + char16_t lead = ut->chunkContents[ut->chunkOffset-1]; + if (U16_IS_LEAD(lead)) { + ut->chunkOffset--; + } + } + } + } +} + + + +U_CAPI int64_t U_EXPORT2 +utext_getPreviousNativeIndex(UText *ut) { + // + // Fast-path the common case. + // Common means current position is not at the beginning of a chunk + // and the preceding character is not supplementary. + // + int32_t i = ut->chunkOffset - 1; + int64_t result; + if (i >= 0) { + char16_t c = ut->chunkContents[i]; + if (U16_IS_TRAIL(c) == false) { + if (i <= ut->nativeIndexingLimit) { + result = ut->chunkNativeStart + i; + } else { + ut->chunkOffset = i; + result = ut->pFuncs->mapOffsetToNative(ut); + ut->chunkOffset++; + } + return result; + } + } + + // If at the start of text, simply return 0. + if (ut->chunkOffset==0 && ut->chunkNativeStart==0) { + return 0; + } + + // Harder, less common cases. We are at a chunk boundary, or on a surrogate. + // Keep it simple, use other functions to handle the edges. + // + utext_previous32(ut); + result = UTEXT_GETNATIVEINDEX(ut); + utext_next32(ut); + return result; +} + + +// +// utext_current32. Get the UChar32 at the current position. +// UText iteration position is always on a code point boundary, +// never on the trail half of a surrogate pair. +// +U_CAPI UChar32 U_EXPORT2 +utext_current32(UText *ut) { + UChar32 c; + if (ut->chunkOffset==ut->chunkLength) { + // Current position is just off the end of the chunk. + if (ut->pFuncs->access(ut, ut->chunkNativeLimit, true) == false) { + // Off the end of the text. + return U_SENTINEL; + } + } + + c = ut->chunkContents[ut->chunkOffset]; + if (U16_IS_LEAD(c) == false) { + // Normal, non-supplementary case. + return c; + } + + // + // Possible supplementary char. + // + UChar32 trail = 0; + UChar32 supplementaryC = c; + if ((ut->chunkOffset+1) < ut->chunkLength) { + // The trail surrogate is in the same chunk. + trail = ut->chunkContents[ut->chunkOffset+1]; + } else { + // The trail surrogate is in a different chunk. + // Because we must maintain the iteration position, we need to switch forward + // into the new chunk, get the trail surrogate, then revert the chunk back to the + // original one. + // An edge case to be careful of: the entire text may end with an unpaired + // leading surrogate. The attempt to access the trail will fail, but + // the original position before the unpaired lead still needs to be restored. + int64_t nativePosition = ut->chunkNativeLimit; + if (ut->pFuncs->access(ut, nativePosition, true)) { + trail = ut->chunkContents[ut->chunkOffset]; + } + UBool r = ut->pFuncs->access(ut, nativePosition, false); // reverse iteration flag loads preceding chunk + U_ASSERT(r); + // Here we need to restore chunkOffset since the access functions were called with + // chunkNativeLimit but that is not where we were (we were 1 code unit before the + // limit). Restoring was originally added in ICU-4669 but did not support access + // functions that changed the chunk size, the following does. + ut->chunkOffset = ut->chunkLength - 1; + if(!r) { + return U_SENTINEL; + } + } + + if (U16_IS_TRAIL(trail)) { + supplementaryC = U16_GET_SUPPLEMENTARY(c, trail); + } + return supplementaryC; + +} + + +U_CAPI UChar32 U_EXPORT2 +utext_char32At(UText *ut, int64_t nativeIndex) { + UChar32 c = U_SENTINEL; + + // Fast path the common case. + if (nativeIndex>=ut->chunkNativeStart && nativeIndex < ut->chunkNativeStart + ut->nativeIndexingLimit) { + ut->chunkOffset = (int32_t)(nativeIndex - ut->chunkNativeStart); + c = ut->chunkContents[ut->chunkOffset]; + if (U16_IS_SURROGATE(c) == false) { + return c; + } + } + + + utext_setNativeIndex(ut, nativeIndex); + if (nativeIndex>=ut->chunkNativeStart && ut->chunkOffsetchunkLength) { + c = ut->chunkContents[ut->chunkOffset]; + if (U16_IS_SURROGATE(c)) { + // For surrogates, let current32() deal with the complications + // of supplementaries that may span chunk boundaries. + c = utext_current32(ut); + } + } + return c; +} + + +U_CAPI UChar32 U_EXPORT2 +utext_next32(UText *ut) { + UChar32 c; + + if (ut->chunkOffset >= ut->chunkLength) { + if (ut->pFuncs->access(ut, ut->chunkNativeLimit, true) == false) { + return U_SENTINEL; + } + } + + c = ut->chunkContents[ut->chunkOffset++]; + if (U16_IS_LEAD(c) == false) { + // Normal case, not supplementary. + // (A trail surrogate seen here is just returned as is, as a surrogate value. + // It cannot be part of a pair.) + return c; + } + + if (ut->chunkOffset >= ut->chunkLength) { + if (ut->pFuncs->access(ut, ut->chunkNativeLimit, true) == false) { + // c is an unpaired lead surrogate at the end of the text. + // return it as it is. + return c; + } + } + UChar32 trail = ut->chunkContents[ut->chunkOffset]; + if (U16_IS_TRAIL(trail) == false) { + // c was an unpaired lead surrogate, not at the end of the text. + // return it as it is (unpaired). Iteration position is on the + // following character, possibly in the next chunk, where the + // trail surrogate would have been if it had existed. + return c; + } + + UChar32 supplementary = U16_GET_SUPPLEMENTARY(c, trail); + ut->chunkOffset++; // move iteration position over the trail surrogate. + return supplementary; + } + + +U_CAPI UChar32 U_EXPORT2 +utext_previous32(UText *ut) { + UChar32 c; + + if (ut->chunkOffset <= 0) { + if (ut->pFuncs->access(ut, ut->chunkNativeStart, false) == false) { + return U_SENTINEL; + } + } + ut->chunkOffset--; + c = ut->chunkContents[ut->chunkOffset]; + if (U16_IS_TRAIL(c) == false) { + // Normal case, not supplementary. + // (A lead surrogate seen here is just returned as is, as a surrogate value. + // It cannot be part of a pair.) + return c; + } + + if (ut->chunkOffset <= 0) { + if (ut->pFuncs->access(ut, ut->chunkNativeStart, false) == false) { + // c is an unpaired trail surrogate at the start of the text. + // return it as it is. + return c; + } + } + + UChar32 lead = ut->chunkContents[ut->chunkOffset-1]; + if (U16_IS_LEAD(lead) == false) { + // c was an unpaired trail surrogate, not at the end of the text. + // return it as it is (unpaired). Iteration position is at c + return c; + } + + UChar32 supplementary = U16_GET_SUPPLEMENTARY(lead, c); + ut->chunkOffset--; // move iteration position over the lead surrogate. + return supplementary; +} + + + +U_CAPI UChar32 U_EXPORT2 +utext_next32From(UText *ut, int64_t index) { + UChar32 c = U_SENTINEL; + + if(indexchunkNativeStart || index>=ut->chunkNativeLimit) { + // Desired position is outside of the current chunk. + if(!ut->pFuncs->access(ut, index, true)) { + // no chunk available here + return U_SENTINEL; + } + } else if (index - ut->chunkNativeStart <= (int64_t)ut->nativeIndexingLimit) { + // Desired position is in chunk, with direct 1:1 native to UTF16 indexing + ut->chunkOffset = (int32_t)(index - ut->chunkNativeStart); + } else { + // Desired position is in chunk, with non-UTF16 indexing. + ut->chunkOffset = ut->pFuncs->mapNativeIndexToUTF16(ut, index); + } + + c = ut->chunkContents[ut->chunkOffset++]; + if (U16_IS_SURROGATE(c)) { + // Surrogates. Many edge cases. Use other functions that already + // deal with the problems. + utext_setNativeIndex(ut, index); + c = utext_next32(ut); + } + return c; +} + + +U_CAPI UChar32 U_EXPORT2 +utext_previous32From(UText *ut, int64_t index) { + // + // Return the character preceding the specified index. + // Leave the iteration position at the start of the character that was returned. + // + UChar32 cPrev; // The character preceding cCurr, which is what we will return. + + // Address the chunk containing the position preceding the incoming index + // A tricky edge case: + // We try to test the requested native index against the chunkNativeStart to determine + // whether the character preceding the one at the index is in the current chunk. + // BUT, this test can fail with UTF-8 (or any other multibyte encoding), when the + // requested index is on something other than the first position of the first char. + // + if(index<=ut->chunkNativeStart || index>ut->chunkNativeLimit) { + // Requested native index is outside of the current chunk. + if(!ut->pFuncs->access(ut, index, false)) { + // no chunk available here + return U_SENTINEL; + } + } else if(index - ut->chunkNativeStart <= (int64_t)ut->nativeIndexingLimit) { + // Direct UTF-16 indexing. + ut->chunkOffset = (int32_t)(index - ut->chunkNativeStart); + } else { + ut->chunkOffset=ut->pFuncs->mapNativeIndexToUTF16(ut, index); + if (ut->chunkOffset==0 && !ut->pFuncs->access(ut, index, false)) { + // no chunk available here + return U_SENTINEL; + } + } + + // + // Simple case with no surrogates. + // + ut->chunkOffset--; + cPrev = ut->chunkContents[ut->chunkOffset]; + + if (U16_IS_SURROGATE(cPrev)) { + // Possible supplementary. Many edge cases. + // Let other functions do the heavy lifting. + utext_setNativeIndex(ut, index); + cPrev = utext_previous32(ut); + } + return cPrev; +} + + +U_CAPI int32_t U_EXPORT2 +utext_extract(UText *ut, + int64_t start, int64_t limit, + char16_t *dest, int32_t destCapacity, + UErrorCode *status) { + return ut->pFuncs->extract(ut, start, limit, dest, destCapacity, status); + } + + + +U_CAPI UBool U_EXPORT2 +utext_equals(const UText *a, const UText *b) { + if (a==nullptr || b==nullptr || + a->magic != UTEXT_MAGIC || + b->magic != UTEXT_MAGIC) { + // Null or invalid arguments don't compare equal to anything. + return false; + } + + if (a->pFuncs != b->pFuncs) { + // Different types of text providers. + return false; + } + + if (a->context != b->context) { + // Different sources (different strings) + return false; + } + if (utext_getNativeIndex(a) != utext_getNativeIndex(b)) { + // Different current position in the string. + return false; + } + + return true; +} + +U_CAPI UBool U_EXPORT2 +utext_isWritable(const UText *ut) +{ + UBool b = (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_WRITABLE)) != 0; + return b; +} + + +U_CAPI void U_EXPORT2 +utext_freeze(UText *ut) { + // Zero out the WRITABLE flag. + ut->providerProperties &= ~(I32_FLAG(UTEXT_PROVIDER_WRITABLE)); +} + + +U_CAPI UBool U_EXPORT2 +utext_hasMetaData(const UText *ut) +{ + UBool b = (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_HAS_META_DATA)) != 0; + return b; +} + + + +U_CAPI int32_t U_EXPORT2 +utext_replace(UText *ut, + int64_t nativeStart, int64_t nativeLimit, + const char16_t *replacementText, int32_t replacementLength, + UErrorCode *status) +{ + if (U_FAILURE(*status)) { + return 0; + } + if ((ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_WRITABLE)) == 0) { + *status = U_NO_WRITE_PERMISSION; + return 0; + } + int32_t i = ut->pFuncs->replace(ut, nativeStart, nativeLimit, replacementText, replacementLength, status); + return i; +} + +U_CAPI void U_EXPORT2 +utext_copy(UText *ut, + int64_t nativeStart, int64_t nativeLimit, + int64_t destIndex, + UBool move, + UErrorCode *status) +{ + if (U_FAILURE(*status)) { + return; + } + if ((ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_WRITABLE)) == 0) { + *status = U_NO_WRITE_PERMISSION; + return; + } + ut->pFuncs->copy(ut, nativeStart, nativeLimit, destIndex, move, status); +} + + + +U_CAPI UText * U_EXPORT2 +utext_clone(UText *dest, const UText *src, UBool deep, UBool readOnly, UErrorCode *status) { + if (U_FAILURE(*status)) { + return dest; + } + UText *result = src->pFuncs->clone(dest, src, deep, status); + if (U_FAILURE(*status)) { + return result; + } + if (result == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return result; + } + if (readOnly) { + utext_freeze(result); + } + return result; +} + + + +//------------------------------------------------------------------------------ +// +// UText common functions implementation +// +//------------------------------------------------------------------------------ + +// +// UText.flags bit definitions +// +enum { + UTEXT_HEAP_ALLOCATED = 1, // 1 if ICU has allocated this UText struct on the heap. + // 0 if caller provided storage for the UText. + + UTEXT_EXTRA_HEAP_ALLOCATED = 2, // 1 if ICU has allocated extra storage as a separate + // heap block. + // 0 if there is no separate allocation. Either no extra + // storage was requested, or it is appended to the end + // of the main UText storage. + + UTEXT_OPEN = 4 // 1 if this UText is currently open + // 0 if this UText is not open. +}; + + +// +// Extended form of a UText. The purpose is to aid in computing the total size required +// when a provider asks for a UText to be allocated with extra storage. + +struct ExtendedUText { + UText ut; + std::max_align_t extension; +}; + +static const UText emptyText = UTEXT_INITIALIZER; + +U_CAPI UText * U_EXPORT2 +utext_setup(UText *ut, int32_t extraSpace, UErrorCode *status) { + if (U_FAILURE(*status)) { + return ut; + } + + if (ut == nullptr) { + // We need to heap-allocate storage for the new UText + int32_t spaceRequired = sizeof(UText); + if (extraSpace > 0) { + spaceRequired = sizeof(ExtendedUText) + extraSpace - sizeof(std::max_align_t); + } + ut = (UText *)uprv_malloc(spaceRequired); + if (ut == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } else { + *ut = emptyText; + ut->flags |= UTEXT_HEAP_ALLOCATED; + if (spaceRequired>0) { + ut->extraSize = extraSpace; + ut->pExtra = &((ExtendedUText *)ut)->extension; + } + } + } else { + // We have been supplied with an already existing UText. + // Verify that it really appears to be a UText. + if (ut->magic != UTEXT_MAGIC) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return ut; + } + // If the ut is already open and there's a provider supplied close + // function, call it. + if ((ut->flags & UTEXT_OPEN) && ut->pFuncs->close != nullptr) { + ut->pFuncs->close(ut); + } + ut->flags &= ~UTEXT_OPEN; + + // If extra space was requested by our caller, check whether + // sufficient already exists, and allocate new if needed. + if (extraSpace > ut->extraSize) { + // Need more space. If there is existing separately allocated space, + // delete it first, then allocate new space. + if (ut->flags & UTEXT_EXTRA_HEAP_ALLOCATED) { + uprv_free(ut->pExtra); + ut->extraSize = 0; + } + ut->pExtra = uprv_malloc(extraSpace); + if (ut->pExtra == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + } else { + ut->extraSize = extraSpace; + ut->flags |= UTEXT_EXTRA_HEAP_ALLOCATED; + } + } + } + if (U_SUCCESS(*status)) { + ut->flags |= UTEXT_OPEN; + + // Initialize all remaining fields of the UText. + // + ut->context = nullptr; + ut->chunkContents = nullptr; + ut->p = nullptr; + ut->q = nullptr; + ut->r = nullptr; + ut->a = 0; + ut->b = 0; + ut->c = 0; + ut->chunkOffset = 0; + ut->chunkLength = 0; + ut->chunkNativeStart = 0; + ut->chunkNativeLimit = 0; + ut->nativeIndexingLimit = 0; + ut->providerProperties = 0; + ut->privA = 0; + ut->privB = 0; + ut->privC = 0; + ut->privP = nullptr; + if (ut->pExtra!=nullptr && ut->extraSize>0) + uprv_memset(ut->pExtra, 0, ut->extraSize); + + } + return ut; +} + + +U_CAPI UText * U_EXPORT2 +utext_close(UText *ut) { + if (ut==nullptr || + ut->magic != UTEXT_MAGIC || + (ut->flags & UTEXT_OPEN) == 0) + { + // The supplied ut is not an open UText. + // Do nothing. + return ut; + } + + // If the provider gave us a close function, call it now. + // This will clean up anything allocated specifically by the provider. + if (ut->pFuncs->close != nullptr) { + ut->pFuncs->close(ut); + } + ut->flags &= ~UTEXT_OPEN; + + // If we (the framework) allocated the UText or subsidiary storage, + // delete it. + if (ut->flags & UTEXT_EXTRA_HEAP_ALLOCATED) { + uprv_free(ut->pExtra); + ut->pExtra = nullptr; + ut->flags &= ~UTEXT_EXTRA_HEAP_ALLOCATED; + ut->extraSize = 0; + } + + // Zero out function table of the closed UText. This is a defensive move, + // intended to cause applications that inadvertently use a closed + // utext to crash with null pointer errors. + ut->pFuncs = nullptr; + + if (ut->flags & UTEXT_HEAP_ALLOCATED) { + // This UText was allocated by UText setup. We need to free it. + // Clear magic, so we can detect if the user messes up and immediately + // tries to reopen another UText using the deleted storage. + ut->magic = 0; + uprv_free(ut); + ut = nullptr; + } + return ut; +} + + + + +// +// invalidateChunk Reset a chunk to have no contents, so that the next call +// to access will cause new data to load. +// This is needed when copy/move/replace operate directly on the +// backing text, potentially putting it out of sync with the +// contents in the chunk. +// +static void +invalidateChunk(UText *ut) { + ut->chunkLength = 0; + ut->chunkNativeLimit = 0; + ut->chunkNativeStart = 0; + ut->chunkOffset = 0; + ut->nativeIndexingLimit = 0; +} + +// +// pinIndex Do range pinning on a native index parameter. +// 64 bit pinning is done in place. +// 32 bit truncated result is returned as a convenience for +// use in providers that don't need 64 bits. +static int32_t +pinIndex(int64_t &index, int64_t limit) { + if (index<0) { + index = 0; + } else if (index > limit) { + index = limit; + } + return (int32_t)index; +} + + +U_CDECL_BEGIN + +// +// Pointer relocation function, +// a utility used by shallow clone. +// Adjust a pointer that refers to something within one UText (the source) +// to refer to the same relative offset within a another UText (the target) +// +static void adjustPointer(UText *dest, const void **destPtr, const UText *src) { + // convert all pointers to (char *) so that byte address arithmetic will work. + char *dptr = (char *)*destPtr; + char *dUText = (char *)dest; + char *sUText = (char *)src; + + if (dptr >= (char *)src->pExtra && dptr < ((char*)src->pExtra)+src->extraSize) { + // target ptr was to something within the src UText's pExtra storage. + // relocate it into the target UText's pExtra region. + *destPtr = ((char *)dest->pExtra) + (dptr - (char *)src->pExtra); + } else if (dptr>=sUText && dptr < sUText+src->sizeOfStruct) { + // target ptr was pointing to somewhere within the source UText itself. + // Move it to the same offset within the target UText. + *destPtr = dUText + (dptr-sUText); + } +} + + +// +// Clone. This is a generic copy-the-utext-by-value clone function that can be +// used as-is with some utext types, and as a helper by other clones. +// +static UText * U_CALLCONV +shallowTextClone(UText * dest, const UText * src, UErrorCode * status) { + if (U_FAILURE(*status)) { + return nullptr; + } + int32_t srcExtraSize = src->extraSize; + + // + // Use the generic text_setup to allocate storage if required. + // + dest = utext_setup(dest, srcExtraSize, status); + if (U_FAILURE(*status)) { + return dest; + } + + // + // flags (how the UText was allocated) and the pointer to the + // extra storage must retain the values in the cloned utext that + // were set up by utext_setup. Save them separately before + // copying the whole struct. + // + void *destExtra = dest->pExtra; + int32_t flags = dest->flags; + + + // + // Copy the whole UText struct by value. + // Any "Extra" storage is copied also. + // + int sizeToCopy = src->sizeOfStruct; + if (sizeToCopy > dest->sizeOfStruct) { + sizeToCopy = dest->sizeOfStruct; + } + uprv_memcpy(dest, src, sizeToCopy); + dest->pExtra = destExtra; + dest->flags = flags; + if (srcExtraSize > 0) { + uprv_memcpy(dest->pExtra, src->pExtra, srcExtraSize); + } + + // + // Relocate any pointers in the target that refer to the UText itself + // to point to the cloned copy rather than the original source. + // + adjustPointer(dest, &dest->context, src); + adjustPointer(dest, &dest->p, src); + adjustPointer(dest, &dest->q, src); + adjustPointer(dest, &dest->r, src); + adjustPointer(dest, (const void **)&dest->chunkContents, src); + + // The newly shallow-cloned UText does _not_ own the underlying storage for the text. + // (The source for the clone may or may not have owned the text.) + + dest->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT); + + return dest; +} + + +U_CDECL_END + + + +//------------------------------------------------------------------------------ +// +// UText implementation for UTF-8 char * strings (read-only) +// Limitation: string length must be <= 0x7fffffff in length. +// (length must for in an int32_t variable) +// +// Use of UText data members: +// context pointer to UTF-8 string +// utext.b is the input string length (bytes). +// utext.c Length scanned so far in string +// (for optimizing finding length of zero terminated strings.) +// utext.p pointer to the current buffer +// utext.q pointer to the other buffer. +// +//------------------------------------------------------------------------------ + +// Chunk size. +// Must be less than 85 (256/3), because of byte mapping from char16_t indexes to native indexes. +// Worst case is three native bytes to one char16_t. (Supplemenaries are 4 native bytes +// to two UChars.) +// The longest illegal byte sequence treated as a single error (and converted to U+FFFD) +// is a three-byte sequence (truncated four-byte sequence). +// +enum { UTF8_TEXT_CHUNK_SIZE=32 }; + +// +// UTF8Buf Two of these structs will be set up in the UText's extra allocated space. +// Each contains the char16_t chunk buffer, the to and from native maps, and +// header info. +// +// because backwards iteration fills the buffers starting at the end and +// working towards the front, the filled part of the buffers may not begin +// at the start of the available storage for the buffers. +// +// Buffer size is one bigger than the specified UTF8_TEXT_CHUNK_SIZE to allow for +// the last character added being a supplementary, and thus requiring a surrogate +// pair. Doing this is simpler than checking for the edge case. +// + +struct UTF8Buf { + int32_t bufNativeStart; // Native index of first char in char16_t buf + int32_t bufNativeLimit; // Native index following last char in buf. + int32_t bufStartIdx; // First filled position in buf. + int32_t bufLimitIdx; // Limit of filled range in buf. + int32_t bufNILimit; // Limit of native indexing part of buf + int32_t toUCharsMapStart; // Native index corresponding to + // mapToUChars[0]. + // Set to bufNativeStart when filling forwards. + // Set to computed value when filling backwards. + + char16_t buf[UTF8_TEXT_CHUNK_SIZE+4]; // The char16_t buffer. Requires one extra position beyond the + // the chunk size, to allow for surrogate at the end. + // Length must be identical to mapToNative array, below, + // because of the way indexing works when the array is + // filled backwards during a reverse iteration. Thus, + // the additional extra size. + uint8_t mapToNative[UTF8_TEXT_CHUNK_SIZE+4]; // map char16_t index in buf to + // native offset from bufNativeStart. + // Requires two extra slots, + // one for a supplementary starting in the last normal position, + // and one for an entry for the buffer limit position. + uint8_t mapToUChars[UTF8_TEXT_CHUNK_SIZE*3+6]; // Map native offset from bufNativeStart to + // corresponding offset in filled part of buf. + int32_t align; +}; + +U_CDECL_BEGIN + +// +// utf8TextLength +// +// Get the length of the string. If we don't already know it, +// we'll need to scan for the trailing nul. +// +static int64_t U_CALLCONV +utf8TextLength(UText *ut) { + if (ut->b < 0) { + // Zero terminated string, and we haven't scanned to the end yet. + // Scan it now. + const char *r = (const char *)ut->context + ut->c; + while (*r != 0) { + r++; + } + if ((r - (const char *)ut->context) < 0x7fffffff) { + ut->b = (int32_t)(r - (const char *)ut->context); + } else { + // Actual string was bigger (more than 2 gig) than we + // can handle. Clip it to 2 GB. + ut->b = 0x7fffffff; + } + ut->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); + } + return ut->b; +} + + + + + + +static UBool U_CALLCONV +utf8TextAccess(UText *ut, int64_t index, UBool forward) { + // + // Apologies to those who are allergic to goto statements. + // Consider each goto to a labelled block to be the equivalent of + // call the named block as if it were a function(); + // return; + // + const uint8_t *s8=(const uint8_t *)ut->context; + UTF8Buf *u8b = nullptr; + int32_t length = ut->b; // Length of original utf-8 + int32_t ix= (int32_t)index; // Requested index, trimmed to 32 bits. + int32_t mapIndex = 0; + if (index<0) { + ix=0; + } else if (index > 0x7fffffff) { + // Strings with 64 bit lengths not supported by this UTF-8 provider. + ix = 0x7fffffff; + } + + // Pin requested index to the string length. + if (ix>length) { + if (length>=0) { + ix=length; + } else if (ix>=ut->c) { + // Zero terminated string, and requested index is beyond + // the region that has already been scanned. + // Scan up to either the end of the string or to the + // requested position, whichever comes first. + while (ut->cc]!=0) { + ut->c++; + } + // TODO: support for null terminated string length > 32 bits. + if (s8[ut->c] == 0) { + // We just found the actual length of the string. + // Trim the requested index back to that. + ix = ut->c; + ut->b = ut->c; + length = ut->c; + ut->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); + } + } + } + + // + // Dispatch to the appropriate action for a forward iteration request. + // + if (forward) { + if (ix==ut->chunkNativeLimit) { + // Check for normal sequential iteration cases first. + if (ix==length) { + // Just reached end of string + // Don't swap buffers, but do set the + // current buffer position. + ut->chunkOffset = ut->chunkLength; + return false; + } else { + // End of current buffer. + // check whether other buffer already has what we need. + UTF8Buf *altB = (UTF8Buf *)ut->q; + if (ix>=altB->bufNativeStart && ixbufNativeLimit) { + goto swapBuffers; + } + } + } + + // A random access. Desired index could be in either or niether buf. + // For optimizing the order of testing, first check for the index + // being in the other buffer. This will be the case for uses that + // move back and forth over a fairly limited range + { + u8b = (UTF8Buf *)ut->q; // the alternate buffer + if (ix>=u8b->bufNativeStart && ixbufNativeLimit) { + // Requested index is in the other buffer. + goto swapBuffers; + } + if (ix == length) { + // Requested index is end-of-string. + // (this is the case of randomly seeking to the end. + // The case of iterating off the end is handled earlier.) + if (ix == ut->chunkNativeLimit) { + // Current buffer extends up to the end of the string. + // Leave it as the current buffer. + ut->chunkOffset = ut->chunkLength; + return false; + } + if (ix == u8b->bufNativeLimit) { + // Alternate buffer extends to the end of string. + // Swap it in as the current buffer. + goto swapBuffersAndFail; + } + + // Neither existing buffer extends to the end of the string. + goto makeStubBuffer; + } + + if (ixchunkNativeStart || ix>=ut->chunkNativeLimit) { + // Requested index is in neither buffer. + goto fillForward; + } + + // Requested index is in this buffer. + u8b = (UTF8Buf *)ut->p; // the current buffer + mapIndex = ix - u8b->toUCharsMapStart; + U_ASSERT(mapIndex < (int32_t)sizeof(UTF8Buf::mapToUChars)); + ut->chunkOffset = u8b->mapToUChars[mapIndex] - u8b->bufStartIdx; + return true; + + } + } + + + // + // Dispatch to the appropriate action for a + // Backwards Direction iteration request. + // + if (ix==ut->chunkNativeStart) { + // Check for normal sequential iteration cases first. + if (ix==0) { + // Just reached the start of string + // Don't swap buffers, but do set the + // current buffer position. + ut->chunkOffset = 0; + return false; + } else { + // Start of current buffer. + // check whether other buffer already has what we need. + UTF8Buf *altB = (UTF8Buf *)ut->q; + if (ix>altB->bufNativeStart && ix<=altB->bufNativeLimit) { + goto swapBuffers; + } + } + } + + // A random access. Desired index could be in either or niether buf. + // For optimizing the order of testing, + // Most likely case: in the other buffer. + // Second most likely: in neither buffer. + // Unlikely, but must work: in the current buffer. + u8b = (UTF8Buf *)ut->q; // the alternate buffer + if (ix>u8b->bufNativeStart && ix<=u8b->bufNativeLimit) { + // Requested index is in the other buffer. + goto swapBuffers; + } + // Requested index is start-of-string. + // (this is the case of randomly seeking to the start. + // The case of iterating off the start is handled earlier.) + if (ix==0) { + if (u8b->bufNativeStart==0) { + // Alternate buffer contains the data for the start string. + // Make it be the current buffer. + goto swapBuffersAndFail; + } else { + // Request for data before the start of string, + // neither buffer is usable. + // set up a zero-length buffer. + goto makeStubBuffer; + } + } + + if (ix<=ut->chunkNativeStart || ix>ut->chunkNativeLimit) { + // Requested index is in neither buffer. + goto fillReverse; + } + + // Requested index is in this buffer. + // Set the utf16 buffer index. + u8b = (UTF8Buf *)ut->p; + mapIndex = ix - u8b->toUCharsMapStart; + ut->chunkOffset = u8b->mapToUChars[mapIndex] - u8b->bufStartIdx; + if (ut->chunkOffset==0) { + // This occurs when the first character in the text is + // a multi-byte UTF-8 char, and the requested index is to + // one of the trailing bytes. Because there is no preceding , + // character, this access fails. We can't pick up on the + // situation sooner because the requested index is not zero. + return false; + } else { + return true; + } + + + +swapBuffers: + // The alternate buffer (ut->q) has the string data that was requested. + // Swap the primary and alternate buffers, and set the + // chunk index into the new primary buffer. + { + u8b = (UTF8Buf *)ut->q; + ut->q = ut->p; + ut->p = u8b; + ut->chunkContents = &u8b->buf[u8b->bufStartIdx]; + ut->chunkLength = u8b->bufLimitIdx - u8b->bufStartIdx; + ut->chunkNativeStart = u8b->bufNativeStart; + ut->chunkNativeLimit = u8b->bufNativeLimit; + ut->nativeIndexingLimit = u8b->bufNILimit; + + // Index into the (now current) chunk + // Use the map to set the chunk index. It's more trouble than it's worth + // to check whether native indexing can be used. + U_ASSERT(ix>=u8b->bufNativeStart); + U_ASSERT(ix<=u8b->bufNativeLimit); + mapIndex = ix - u8b->toUCharsMapStart; + U_ASSERT(mapIndex>=0); + U_ASSERT(mapIndex<(int32_t)sizeof(u8b->mapToUChars)); + ut->chunkOffset = u8b->mapToUChars[mapIndex] - u8b->bufStartIdx; + + return true; + } + + + swapBuffersAndFail: + // We got a request for either the start or end of the string, + // with iteration continuing in the out-of-bounds direction. + // The alternate buffer already contains the data up to the + // start/end. + // Swap the buffers, then return failure, indicating that we couldn't + // make things correct for continuing the iteration in the requested + // direction. The position & buffer are correct should the + // user decide to iterate in the opposite direction. + u8b = (UTF8Buf *)ut->q; + ut->q = ut->p; + ut->p = u8b; + ut->chunkContents = &u8b->buf[u8b->bufStartIdx]; + ut->chunkLength = u8b->bufLimitIdx - u8b->bufStartIdx; + ut->chunkNativeStart = u8b->bufNativeStart; + ut->chunkNativeLimit = u8b->bufNativeLimit; + ut->nativeIndexingLimit = u8b->bufNILimit; + + // Index into the (now current) chunk + // For this function (swapBuffersAndFail), the requested index + // will always be at either the start or end of the chunk. + if (ix==u8b->bufNativeLimit) { + ut->chunkOffset = ut->chunkLength; + } else { + ut->chunkOffset = 0; + U_ASSERT(ix == u8b->bufNativeStart); + } + return false; + +makeStubBuffer: + // The user has done a seek/access past the start or end + // of the string. Rather than loading data that is likely + // to never be used, just set up a zero-length buffer at + // the position. + u8b = (UTF8Buf *)ut->q; + u8b->bufNativeStart = ix; + u8b->bufNativeLimit = ix; + u8b->bufStartIdx = 0; + u8b->bufLimitIdx = 0; + u8b->bufNILimit = 0; + u8b->toUCharsMapStart = ix; + u8b->mapToNative[0] = 0; + u8b->mapToUChars[0] = 0; + goto swapBuffersAndFail; + + + +fillForward: + { + // Move the incoming index to a code point boundary. + U8_SET_CP_START(s8, 0, ix); + + // Swap the UText buffers. + // We want to fill what was previously the alternate buffer, + // and make what was the current buffer be the new alternate. + UTF8Buf *u8b_swap = (UTF8Buf *)ut->q; + ut->q = ut->p; + ut->p = u8b_swap; + + int32_t strLen = ut->b; + UBool nulTerminated = false; + if (strLen < 0) { + strLen = 0x7fffffff; + nulTerminated = true; + } + + char16_t *buf = u8b_swap->buf; + uint8_t *mapToNative = u8b_swap->mapToNative; + uint8_t *mapToUChars = u8b_swap->mapToUChars; + int32_t destIx = 0; + int32_t srcIx = ix; + UBool seenNonAscii = false; + UChar32 c = 0; + + // Fill the chunk buffer and mapping arrays. + while (destIx0 && c<0x80) { + // Special case ASCII range for speed. + // zero is excluded to simplify bounds checking. + buf[destIx] = (char16_t)c; + mapToNative[destIx] = (uint8_t)(srcIx - ix); + mapToUChars[srcIx-ix] = (uint8_t)destIx; + srcIx++; + destIx++; + } else { + // General case, handle everything. + if (seenNonAscii == false) { + seenNonAscii = true; + u8b_swap->bufNILimit = destIx; + } + + int32_t cIx = srcIx; + int32_t dIx = destIx; + int32_t dIxSaved = destIx; + U8_NEXT_OR_FFFD(s8, srcIx, strLen, c); + if (c==0 && nulTerminated) { + srcIx--; + break; + } + + U16_APPEND_UNSAFE(buf, destIx, c); + do { + mapToNative[dIx++] = (uint8_t)(cIx - ix); + } while (dIx < destIx); + + do { + mapToUChars[cIx++ - ix] = (uint8_t)dIxSaved; + } while (cIx < srcIx); + } + if (srcIx>=strLen) { + break; + } + + } + + // store Native <--> Chunk Map entries for the end of the buffer. + // There is no actual character here, but the index position is valid. + mapToNative[destIx] = (uint8_t)(srcIx - ix); + mapToUChars[srcIx - ix] = (uint8_t)destIx; + + // fill in Buffer descriptor + u8b_swap->bufNativeStart = ix; + u8b_swap->bufNativeLimit = srcIx; + u8b_swap->bufStartIdx = 0; + u8b_swap->bufLimitIdx = destIx; + if (seenNonAscii == false) { + u8b_swap->bufNILimit = destIx; + } + u8b_swap->toUCharsMapStart = u8b_swap->bufNativeStart; + + // Set UText chunk to refer to this buffer. + ut->chunkContents = buf; + ut->chunkOffset = 0; + ut->chunkLength = u8b_swap->bufLimitIdx; + ut->chunkNativeStart = u8b_swap->bufNativeStart; + ut->chunkNativeLimit = u8b_swap->bufNativeLimit; + ut->nativeIndexingLimit = u8b_swap->bufNILimit; + + // For zero terminated strings, keep track of the maximum point + // scanned so far. + if (nulTerminated && srcIx>ut->c) { + ut->c = srcIx; + if (c==0) { + // We scanned to the end. + // Remember the actual length. + ut->b = srcIx; + ut->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); + } + } + return true; + } + + +fillReverse: + { + // Move the incoming index to a code point boundary. + // Can only do this if the incoming index is somewhere in the interior of the string. + // If index is at the end, there is no character there to look at. + if (ix != ut->b) { + // Note: this function will only move the index back if it is on a trail byte + // and there is a preceding lead byte and the sequence from the lead + // through this trail could be part of a valid UTF-8 sequence + // Otherwise the index remains unchanged. + U8_SET_CP_START(s8, 0, ix); + } + + // Swap the UText buffers. + // We want to fill what was previously the alternate buffer, + // and make what was the current buffer be the new alternate. + UTF8Buf *u8b_swap = (UTF8Buf *)ut->q; + ut->q = ut->p; + ut->p = u8b_swap; + + char16_t *buf = u8b_swap->buf; + uint8_t *mapToNative = u8b_swap->mapToNative; + uint8_t *mapToUChars = u8b_swap->mapToUChars; + int32_t toUCharsMapStart = ix - sizeof(UTF8Buf::mapToUChars) + 1; + // Note that toUCharsMapStart can be negative. Happens when the remaining + // text from current position to the beginning is less than the buffer size. + // + 1 because mapToUChars must have a slot at the end for the bufNativeLimit entry. + int32_t destIx = UTF8_TEXT_CHUNK_SIZE+2; // Start in the overflow region + // at end of buffer to leave room + // for a surrogate pair at the + // buffer start. + int32_t srcIx = ix; + int32_t bufNILimit = destIx; + UChar32 c; + + // Map to/from Native Indexes, fill in for the position at the end of + // the buffer. + // + mapToNative[destIx] = (uint8_t)(srcIx - toUCharsMapStart); + mapToUChars[srcIx - toUCharsMapStart] = (uint8_t)destIx; + + // Fill the chunk buffer + // Work backwards, filling from the end of the buffer towards the front. + // + while (destIx>2 && (srcIx - toUCharsMapStart > 5) && (srcIx > 0)) { + srcIx--; + destIx--; + + // Get last byte of the UTF-8 character + c = s8[srcIx]; + if (c<0x80) { + // Special case ASCII range for speed. + buf[destIx] = (char16_t)c; + U_ASSERT(toUCharsMapStart <= srcIx); + mapToUChars[srcIx - toUCharsMapStart] = (uint8_t)destIx; + mapToNative[destIx] = (uint8_t)(srcIx - toUCharsMapStart); + } else { + // General case, handle everything non-ASCII. + + int32_t sIx = srcIx; // ix of last byte of multi-byte u8 char + + // Get the full character from the UTF8 string. + // use code derived from the macros in utf8.h + // Leaves srcIx pointing at the first byte of the UTF-8 char. + // + c=utf8_prevCharSafeBody(s8, 0, &srcIx, c, -3); + // leaves srcIx at first byte of the multi-byte char. + + // Store the character in UTF-16 buffer. + if (c<0x10000) { + buf[destIx] = (char16_t)c; + mapToNative[destIx] = (uint8_t)(srcIx - toUCharsMapStart); + } else { + buf[destIx] = U16_TRAIL(c); + mapToNative[destIx] = (uint8_t)(srcIx - toUCharsMapStart); + buf[--destIx] = U16_LEAD(c); + mapToNative[destIx] = (uint8_t)(srcIx - toUCharsMapStart); + } + + // Fill in the map from native indexes to UChars buf index. + do { + mapToUChars[sIx-- - toUCharsMapStart] = (uint8_t)destIx; + } while (sIx >= srcIx); + U_ASSERT(toUCharsMapStart <= (srcIx+1)); + + // Set native indexing limit to be the current position. + // We are processing a non-ascii, non-native-indexing char now; + // the limit will be here if the rest of the chars to be + // added to this buffer are ascii. + bufNILimit = destIx; + } + } + u8b_swap->bufNativeStart = srcIx; + u8b_swap->bufNativeLimit = ix; + u8b_swap->bufStartIdx = destIx; + u8b_swap->bufLimitIdx = UTF8_TEXT_CHUNK_SIZE+2; + u8b_swap->bufNILimit = bufNILimit - u8b_swap->bufStartIdx; + u8b_swap->toUCharsMapStart = toUCharsMapStart; + + ut->chunkContents = &buf[u8b_swap->bufStartIdx]; + ut->chunkLength = u8b_swap->bufLimitIdx - u8b_swap->bufStartIdx; + ut->chunkOffset = ut->chunkLength; + ut->chunkNativeStart = u8b_swap->bufNativeStart; + ut->chunkNativeLimit = u8b_swap->bufNativeLimit; + ut->nativeIndexingLimit = u8b_swap->bufNILimit; + return true; + } + +} + + + +// +// This is a slightly modified copy of u_strFromUTF8, +// Inserts a Replacement Char rather than failing on invalid UTF-8 +// Removes unnecessary features. +// +static char16_t* +utext_strFromUTF8(char16_t *dest, + int32_t destCapacity, + int32_t *pDestLength, + const char* src, + int32_t srcLength, // required. NUL terminated not supported. + UErrorCode *pErrorCode + ) +{ + + char16_t *pDest = dest; + char16_t *pDestLimit = (dest!=nullptr)?(dest+destCapacity):nullptr; + UChar32 ch=0; + int32_t index = 0; + int32_t reqLength = 0; + uint8_t* pSrc = (uint8_t*) src; + + + while((index < srcLength)&&(pDest0)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + int32_t length = ut->b; + int32_t start32 = pinIndex(start, length); + int32_t limit32 = pinIndex(limit, length); + + if(start32>limit32) { + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + + // adjust the incoming indexes to land on code point boundaries if needed. + // adjust by no more than three, because that is the largest number of trail bytes + // in a well formed UTF8 character. + const uint8_t *buf = (const uint8_t *)ut->context; + int i; + if (start32 < ut->chunkNativeLimit) { + for (i=0; i<3; i++) { + if (U8_IS_SINGLE(buf[start32]) || U8_IS_LEAD(buf[start32]) || start32==0) { + break; + } + start32--; + } + } + + if (limit32 < ut->chunkNativeLimit) { + for (i=0; i<3; i++) { + if (U8_IS_SINGLE(buf[limit32]) || U8_IS_LEAD(buf[limit32]) || limit32==0) { + break; + } + limit32--; + } + } + + // Do the actual extract. + int32_t destLength=0; + utext_strFromUTF8(dest, destCapacity, &destLength, + (const char *)ut->context+start32, limit32-start32, + pErrorCode); + utf8TextAccess(ut, limit32, true); + return destLength; +} + +// +// utf8TextMapOffsetToNative +// +// Map a chunk (UTF-16) offset to a native index. +static int64_t U_CALLCONV +utf8TextMapOffsetToNative(const UText *ut) { + // + UTF8Buf *u8b = (UTF8Buf *)ut->p; + U_ASSERT(ut->chunkOffset>ut->nativeIndexingLimit && ut->chunkOffset<=ut->chunkLength); + int32_t nativeOffset = u8b->mapToNative[ut->chunkOffset + u8b->bufStartIdx] + u8b->toUCharsMapStart; + U_ASSERT(nativeOffset >= ut->chunkNativeStart && nativeOffset <= ut->chunkNativeLimit); + return nativeOffset; +} + +// +// Map a native index to the corresponding chunk offset +// +static int32_t U_CALLCONV +utf8TextMapIndexToUTF16(const UText *ut, int64_t index64) { + U_ASSERT(index64 <= 0x7fffffff); + int32_t index = (int32_t)index64; + UTF8Buf *u8b = (UTF8Buf *)ut->p; + U_ASSERT(index>=ut->chunkNativeStart+ut->nativeIndexingLimit); + U_ASSERT(index<=ut->chunkNativeLimit); + int32_t mapIndex = index - u8b->toUCharsMapStart; + U_ASSERT(mapIndex < (int32_t)sizeof(UTF8Buf::mapToUChars)); + int32_t offset = u8b->mapToUChars[mapIndex] - u8b->bufStartIdx; + U_ASSERT(offset>=0 && offset<=ut->chunkLength); + return offset; +} + +static UText * U_CALLCONV +utf8TextClone(UText *dest, const UText *src, UBool deep, UErrorCode *status) +{ + // First do a generic shallow clone. Does everything needed for the UText struct itself. + dest = shallowTextClone(dest, src, status); + + // For deep clones, make a copy of the string. + // The copied storage is owned by the newly created clone. + // + // TODO: There is an issue with using utext_nativeLength(). + // That function is non-const in cases where the input was NUL terminated + // and the length has not yet been determined. + // This function (clone()) is const. + // There potentially a thread safety issue lurking here. + // + if (deep && U_SUCCESS(*status)) { + int32_t len = (int32_t)utext_nativeLength((UText *)src); + char *copyStr = (char *)uprv_malloc(len+1); + if (copyStr == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + } else { + uprv_memcpy(copyStr, src->context, len+1); + dest->context = copyStr; + dest->providerProperties |= I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT); + } + } + return dest; +} + + +static void U_CALLCONV +utf8TextClose(UText *ut) { + // Most of the work of close is done by the generic UText framework close. + // All that needs to be done here is to delete the UTF8 string if the UText + // owns it. This occurs if the UText was created by cloning. + if (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT)) { + char *s = (char *)ut->context; + uprv_free(s); + ut->context = nullptr; + } +} + +U_CDECL_END + + +static const struct UTextFuncs utf8Funcs = +{ + sizeof(UTextFuncs), + 0, 0, 0, // Reserved alignment padding + utf8TextClone, + utf8TextLength, + utf8TextAccess, + utf8TextExtract, + nullptr, /* replace*/ + nullptr, /* copy */ + utf8TextMapOffsetToNative, + utf8TextMapIndexToUTF16, + utf8TextClose, + nullptr, // spare 1 + nullptr, // spare 2 + nullptr // spare 3 +}; + + +static const char gEmptyString[] = {0}; + +U_CAPI UText * U_EXPORT2 +utext_openUTF8(UText *ut, const char *s, int64_t length, UErrorCode *status) { + if(U_FAILURE(*status)) { + return nullptr; + } + if(s==nullptr && length==0) { + s = gEmptyString; + } + + if(s==nullptr || length<-1 || length>INT32_MAX) { + *status=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + ut = utext_setup(ut, sizeof(UTF8Buf) * 2, status); + if (U_FAILURE(*status)) { + return ut; + } + + ut->pFuncs = &utf8Funcs; + ut->context = s; + ut->b = (int32_t)length; + ut->c = (int32_t)length; + if (ut->c < 0) { + ut->c = 0; + ut->providerProperties |= I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); + } + ut->p = ut->pExtra; + ut->q = (char *)ut->pExtra + sizeof(UTF8Buf); + return ut; + +} + + + + + + + + +//------------------------------------------------------------------------------ +// +// UText implementation wrapper for Replaceable (read/write) +// +// Use of UText data members: +// context pointer to Replaceable. +// p pointer to Replaceable if it is owned by the UText. +// +//------------------------------------------------------------------------------ + + + +// minimum chunk size for this implementation: 3 +// to allow for possible trimming for code point boundaries +enum { REP_TEXT_CHUNK_SIZE=10 }; + +struct ReplExtra { + /* + * Chunk UChars. + * +1 to simplify filling with surrogate pair at the end. + */ + char16_t s[REP_TEXT_CHUNK_SIZE+1]; +}; + + +U_CDECL_BEGIN + +static UText * U_CALLCONV +repTextClone(UText *dest, const UText *src, UBool deep, UErrorCode *status) { + // First do a generic shallow clone. Does everything needed for the UText struct itself. + dest = shallowTextClone(dest, src, status); + + // For deep clones, make a copy of the Replaceable. + // The copied Replaceable storage is owned by the newly created UText clone. + // A non-nullptr pointer in UText.p is the signal to the close() function to delete + // it. + // + if (deep && U_SUCCESS(*status)) { + const Replaceable *replSrc = (const Replaceable *)src->context; + dest->context = replSrc->clone(); + dest->providerProperties |= I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT); + + // with deep clone, the copy is writable, even when the source is not. + dest->providerProperties |= I32_FLAG(UTEXT_PROVIDER_WRITABLE); + } + return dest; +} + + +static void U_CALLCONV +repTextClose(UText *ut) { + // Most of the work of close is done by the generic UText framework close. + // All that needs to be done here is delete the Replaceable if the UText + // owns it. This occurs if the UText was created by cloning. + if (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT)) { + Replaceable *rep = (Replaceable *)ut->context; + delete rep; + ut->context = nullptr; + } +} + + +static int64_t U_CALLCONV +repTextLength(UText *ut) { + const Replaceable *replSrc = (const Replaceable *)ut->context; + int32_t len = replSrc->length(); + return len; +} + + +static UBool U_CALLCONV +repTextAccess(UText *ut, int64_t index, UBool forward) { + const Replaceable *rep=(const Replaceable *)ut->context; + int32_t length=rep->length(); // Full length of the input text (bigger than a chunk) + + // clip the requested index to the limits of the text. + int32_t index32 = pinIndex(index, length); + U_ASSERT(index<=INT32_MAX); + + + /* + * Compute start/limit boundaries around index, for a segment of text + * to be extracted. + * To allow for the possibility that our user gave an index to the trailing + * half of a surrogate pair, we must request one extra preceding char16_t when + * going in the forward direction. This will ensure that the buffer has the + * entire code point at the specified index. + */ + if(forward) { + + if (index32>=ut->chunkNativeStart && index32chunkNativeLimit) { + // Buffer already contains the requested position. + ut->chunkOffset = (int32_t)(index - ut->chunkNativeStart); + return true; + } + if (index32>=length && ut->chunkNativeLimit==length) { + // Request for end of string, and buffer already extends up to it. + // Can't get the data, but don't change the buffer. + ut->chunkOffset = length - (int32_t)ut->chunkNativeStart; + return false; + } + + ut->chunkNativeLimit = index + REP_TEXT_CHUNK_SIZE - 1; + // Going forward, so we want to have the buffer with stuff at and beyond + // the requested index. The -1 gets us one code point before the + // requested index also, to handle the case of the index being on + // a trail surrogate of a surrogate pair. + if(ut->chunkNativeLimit > length) { + ut->chunkNativeLimit = length; + } + // unless buffer ran off end, start is index-1. + ut->chunkNativeStart = ut->chunkNativeLimit - REP_TEXT_CHUNK_SIZE; + if(ut->chunkNativeStart < 0) { + ut->chunkNativeStart = 0; + } + } else { + // Reverse iteration. Fill buffer with data preceding the requested index. + if (index32>ut->chunkNativeStart && index32<=ut->chunkNativeLimit) { + // Requested position already in buffer. + ut->chunkOffset = index32 - (int32_t)ut->chunkNativeStart; + return true; + } + if (index32==0 && ut->chunkNativeStart==0) { + // Request for start, buffer already begins at start. + // No data, but keep the buffer as is. + ut->chunkOffset = 0; + return false; + } + + // Figure out the bounds of the chunk to extract for reverse iteration. + // Need to worry about chunk not splitting surrogate pairs, and while still + // containing the data we need. + // Fix by requesting a chunk that includes an extra char16_t at the end. + // If this turns out to be a lead surrogate, we can lop it off and still have + // the data we wanted. + ut->chunkNativeStart = index32 + 1 - REP_TEXT_CHUNK_SIZE; + if (ut->chunkNativeStart < 0) { + ut->chunkNativeStart = 0; + } + + ut->chunkNativeLimit = index32 + 1; + if (ut->chunkNativeLimit > length) { + ut->chunkNativeLimit = length; + } + } + + // Extract the new chunk of text from the Replaceable source. + ReplExtra *ex = (ReplExtra *)ut->pExtra; + // UnicodeString with its buffer a writable alias to the chunk buffer + UnicodeString buffer(ex->s, 0 /*buffer length*/, REP_TEXT_CHUNK_SIZE /*buffer capacity*/); + rep->extractBetween((int32_t)ut->chunkNativeStart, (int32_t)ut->chunkNativeLimit, buffer); + + ut->chunkContents = ex->s; + ut->chunkLength = (int32_t)(ut->chunkNativeLimit - ut->chunkNativeStart); + ut->chunkOffset = (int32_t)(index32 - ut->chunkNativeStart); + + // Surrogate pairs from the input text must not span chunk boundaries. + // If end of chunk could be the start of a surrogate, trim it off. + if (ut->chunkNativeLimit < length && + U16_IS_LEAD(ex->s[ut->chunkLength-1])) { + ut->chunkLength--; + ut->chunkNativeLimit--; + if (ut->chunkOffset > ut->chunkLength) { + ut->chunkOffset = ut->chunkLength; + } + } + + // if the first char16_t in the chunk could be the trailing half of a surrogate pair, + // trim it off. + if(ut->chunkNativeStart>0 && U16_IS_TRAIL(ex->s[0])) { + ++(ut->chunkContents); + ++(ut->chunkNativeStart); + --(ut->chunkLength); + --(ut->chunkOffset); + } + + // adjust the index/chunkOffset to a code point boundary + U16_SET_CP_START(ut->chunkContents, 0, ut->chunkOffset); + + // Use fast indexing for get/setNativeIndex() + ut->nativeIndexingLimit = ut->chunkLength; + + return true; +} + + + +static int32_t U_CALLCONV +repTextExtract(UText *ut, + int64_t start, int64_t limit, + char16_t *dest, int32_t destCapacity, + UErrorCode *status) { + const Replaceable *rep=(const Replaceable *)ut->context; + int32_t length=rep->length(); + + if(U_FAILURE(*status)) { + return 0; + } + if(destCapacity<0 || (dest==nullptr && destCapacity>0)) { + *status=U_ILLEGAL_ARGUMENT_ERROR; + } + if(start>limit) { + *status=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + int32_t start32 = pinIndex(start, length); + int32_t limit32 = pinIndex(limit, length); + + // adjust start, limit if they point to trail half of surrogates + if (start32charAt(start32)) && + U_IS_SUPPLEMENTARY(rep->char32At(start32))){ + start32--; + } + if (limit32charAt(limit32)) && + U_IS_SUPPLEMENTARY(rep->char32At(limit32))){ + limit32--; + } + + length=limit32-start32; + if(length>destCapacity) { + limit32 = start32 + destCapacity; + } + UnicodeString buffer(dest, 0, destCapacity); // writable alias + rep->extractBetween(start32, limit32, buffer); + repTextAccess(ut, limit32, true); + + return u_terminateUChars(dest, destCapacity, length, status); +} + +static int32_t U_CALLCONV +repTextReplace(UText *ut, + int64_t start, int64_t limit, + const char16_t *src, int32_t length, + UErrorCode *status) { + Replaceable *rep=(Replaceable *)ut->context; + int32_t oldLength; + + if(U_FAILURE(*status)) { + return 0; + } + if(src==nullptr && length!=0) { + *status=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + oldLength=rep->length(); // will subtract from new length + if(start>limit ) { + *status=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + int32_t start32 = pinIndex(start, oldLength); + int32_t limit32 = pinIndex(limit, oldLength); + + // Snap start & limit to code point boundaries. + if (start32charAt(start32)) && + start32>0 && U16_IS_LEAD(rep->charAt(start32-1))) + { + start32--; + } + if (limit32charAt(limit32-1)) && + U16_IS_TRAIL(rep->charAt(limit32))) + { + limit32++; + } + + // Do the actual replace operation using methods of the Replaceable class + UnicodeString replStr((UBool)(length<0), src, length); // read-only alias + rep->handleReplaceBetween(start32, limit32, replStr); + int32_t newLength = rep->length(); + int32_t lengthDelta = newLength - oldLength; + + // Is the UText chunk buffer OK? + if (ut->chunkNativeLimit > start32) { + // this replace operation may have impacted the current chunk. + // invalidate it, which will force a reload on the next access. + invalidateChunk(ut); + } + + // set the iteration position to the end of the newly inserted replacement text. + int32_t newIndexPos = limit32 + lengthDelta; + repTextAccess(ut, newIndexPos, true); + + return lengthDelta; +} + + +static void U_CALLCONV +repTextCopy(UText *ut, + int64_t start, int64_t limit, + int64_t destIndex, + UBool move, + UErrorCode *status) +{ + Replaceable *rep=(Replaceable *)ut->context; + int32_t length=rep->length(); + + if(U_FAILURE(*status)) { + return; + } + if (start>limit || (startcopy(start32, limit32, destIndex32); + if(destIndex32handleReplaceBetween(start32, limit32, UnicodeString()); + } else { + // copy + rep->copy(start32, limit32, destIndex32); + } + + // If the change to the text touched the region in the chunk buffer, + // invalidate the buffer. + int32_t firstAffectedIndex = destIndex32; + if (move && start32chunkNativeLimit) { + // changes may have affected range covered by the chunk + invalidateChunk(ut); + } + + // Put iteration position at the newly inserted (moved) block, + int32_t nativeIterIndex = destIndex32 + limit32 - start32; + if (move && destIndex32>start32) { + // moved a block of text towards the end of the string. + nativeIterIndex = destIndex32; + } + + // Set position, reload chunk if needed. + repTextAccess(ut, nativeIterIndex, true); +} + +static const struct UTextFuncs repFuncs = +{ + sizeof(UTextFuncs), + 0, 0, 0, // Reserved alignment padding + repTextClone, + repTextLength, + repTextAccess, + repTextExtract, + repTextReplace, + repTextCopy, + nullptr, // MapOffsetToNative, + nullptr, // MapIndexToUTF16, + repTextClose, + nullptr, // spare 1 + nullptr, // spare 2 + nullptr // spare 3 +}; + + +U_CAPI UText * U_EXPORT2 +utext_openReplaceable(UText *ut, Replaceable *rep, UErrorCode *status) +{ + if(U_FAILURE(*status)) { + return nullptr; + } + if(rep==nullptr) { + *status=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + ut = utext_setup(ut, sizeof(ReplExtra), status); + if(U_FAILURE(*status)) { + return ut; + } + + ut->providerProperties = I32_FLAG(UTEXT_PROVIDER_WRITABLE); + if(rep->hasMetaData()) { + ut->providerProperties |=I32_FLAG(UTEXT_PROVIDER_HAS_META_DATA); + } + + ut->pFuncs = &repFuncs; + ut->context = rep; + return ut; +} + +U_CDECL_END + + + + + + + + +//------------------------------------------------------------------------------ +// +// UText implementation for UnicodeString (read/write) and +// for const UnicodeString (read only) +// (same implementation, only the flags are different) +// +// Use of UText data members: +// context pointer to UnicodeString +// p pointer to UnicodeString IF this UText owns the string +// and it must be deleted on close(). nullptr otherwise. +// +//------------------------------------------------------------------------------ + +U_CDECL_BEGIN + + +static UText * U_CALLCONV +unistrTextClone(UText *dest, const UText *src, UBool deep, UErrorCode *status) { + // First do a generic shallow clone. Does everything needed for the UText struct itself. + dest = shallowTextClone(dest, src, status); + + // For deep clones, make a copy of the UnicodeSring. + // The copied UnicodeString storage is owned by the newly created UText clone. + // A non-nullptr pointer in UText.p is the signal to the close() function to delete + // the UText. + // + if (deep && U_SUCCESS(*status)) { + const UnicodeString *srcString = (const UnicodeString *)src->context; + dest->context = new UnicodeString(*srcString); + dest->providerProperties |= I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT); + + // with deep clone, the copy is writable, even when the source is not. + dest->providerProperties |= I32_FLAG(UTEXT_PROVIDER_WRITABLE); + } + return dest; +} + +static void U_CALLCONV +unistrTextClose(UText *ut) { + // Most of the work of close is done by the generic UText framework close. + // All that needs to be done here is delete the UnicodeString if the UText + // owns it. This occurs if the UText was created by cloning. + if (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT)) { + UnicodeString *str = (UnicodeString *)ut->context; + delete str; + ut->context = nullptr; + } +} + + +static int64_t U_CALLCONV +unistrTextLength(UText *t) { + return ((const UnicodeString *)t->context)->length(); +} + + +static UBool U_CALLCONV +unistrTextAccess(UText *ut, int64_t index, UBool forward) { + int32_t length = ut->chunkLength; + ut->chunkOffset = pinIndex(index, length); + + // Check whether request is at the start or end + UBool retVal = (forward && index0); + return retVal; +} + + + +static int32_t U_CALLCONV +unistrTextExtract(UText *t, + int64_t start, int64_t limit, + char16_t *dest, int32_t destCapacity, + UErrorCode *pErrorCode) { + const UnicodeString *us=(const UnicodeString *)t->context; + int32_t length=us->length(); + + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(destCapacity<0 || (dest==nullptr && destCapacity>0)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + } + if(start<0 || start>limit) { + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + int32_t start32 = startgetChar32Start((int32_t)start) : length; + int32_t limit32 = limitgetChar32Start((int32_t)limit) : length; + + length=limit32-start32; + if (destCapacity>0 && dest!=nullptr) { + int32_t trimmedLength = length; + if(trimmedLength>destCapacity) { + trimmedLength=destCapacity; + } + us->extract(start32, trimmedLength, dest); + t->chunkOffset = start32+trimmedLength; + } else { + t->chunkOffset = start32; + } + u_terminateUChars(dest, destCapacity, length, pErrorCode); + return length; +} + +static int32_t U_CALLCONV +unistrTextReplace(UText *ut, + int64_t start, int64_t limit, + const char16_t *src, int32_t length, + UErrorCode *pErrorCode) { + UnicodeString *us=(UnicodeString *)ut->context; + int32_t oldLength; + + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(src==nullptr && length!=0) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + } + if(start>limit) { + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + oldLength=us->length(); + int32_t start32 = pinIndex(start, oldLength); + int32_t limit32 = pinIndex(limit, oldLength); + if (start32 < oldLength) { + start32 = us->getChar32Start(start32); + } + if (limit32 < oldLength) { + limit32 = us->getChar32Start(limit32); + } + + // replace + us->replace(start32, limit32-start32, src, length); + int32_t newLength = us->length(); + + // Update the chunk description. + ut->chunkContents = us->getBuffer(); + ut->chunkLength = newLength; + ut->chunkNativeLimit = newLength; + ut->nativeIndexingLimit = newLength; + + // Set iteration position to the point just following the newly inserted text. + int32_t lengthDelta = newLength - oldLength; + ut->chunkOffset = limit32 + lengthDelta; + + return lengthDelta; +} + +static void U_CALLCONV +unistrTextCopy(UText *ut, + int64_t start, int64_t limit, + int64_t destIndex, + UBool move, + UErrorCode *pErrorCode) { + UnicodeString *us=(UnicodeString *)ut->context; + int32_t length=us->length(); + + if(U_FAILURE(*pErrorCode)) { + return; + } + int32_t start32 = pinIndex(start, length); + int32_t limit32 = pinIndex(limit, length); + int32_t destIndex32 = pinIndex(destIndex, length); + + if( start32>limit32 || (start32copy(start32, limit32, destIndex32); + if(destIndex32remove(start32, segLength); + } else { + // copy + us->copy(start32, limit32, destIndex32); + } + + // update chunk description, set iteration position. + ut->chunkContents = us->getBuffer(); + if (move==false) { + // copy operation, string length grows + ut->chunkLength += limit32-start32; + ut->chunkNativeLimit = ut->chunkLength; + ut->nativeIndexingLimit = ut->chunkLength; + } + + // Iteration position to end of the newly inserted text. + ut->chunkOffset = destIndex32+limit32-start32; + if (move && destIndex32>start32) { + ut->chunkOffset = destIndex32; + } + +} + +static const struct UTextFuncs unistrFuncs = +{ + sizeof(UTextFuncs), + 0, 0, 0, // Reserved alignment padding + unistrTextClone, + unistrTextLength, + unistrTextAccess, + unistrTextExtract, + unistrTextReplace, + unistrTextCopy, + nullptr, // MapOffsetToNative, + nullptr, // MapIndexToUTF16, + unistrTextClose, + nullptr, // spare 1 + nullptr, // spare 2 + nullptr // spare 3 +}; + + + +U_CDECL_END + + +U_CAPI UText * U_EXPORT2 +utext_openUnicodeString(UText *ut, UnicodeString *s, UErrorCode *status) { + ut = utext_openConstUnicodeString(ut, s, status); + if (U_SUCCESS(*status)) { + ut->providerProperties |= I32_FLAG(UTEXT_PROVIDER_WRITABLE); + } + return ut; +} + + + +U_CAPI UText * U_EXPORT2 +utext_openConstUnicodeString(UText *ut, const UnicodeString *s, UErrorCode *status) { + if (U_SUCCESS(*status) && s->isBogus()) { + // The UnicodeString is bogus, but we still need to detach the UText + // from whatever it was hooked to before, if anything. + utext_openUChars(ut, nullptr, 0, status); + *status = U_ILLEGAL_ARGUMENT_ERROR; + return ut; + } + ut = utext_setup(ut, 0, status); + // note: use the standard (writable) function table for UnicodeString. + // The flag settings disable writing, so having the functions in + // the table is harmless. + if (U_SUCCESS(*status)) { + ut->pFuncs = &unistrFuncs; + ut->context = s; + ut->providerProperties = I32_FLAG(UTEXT_PROVIDER_STABLE_CHUNKS); + ut->chunkContents = s->getBuffer(); + ut->chunkLength = s->length(); + ut->chunkNativeStart = 0; + ut->chunkNativeLimit = ut->chunkLength; + ut->nativeIndexingLimit = ut->chunkLength; + } + return ut; +} + +//------------------------------------------------------------------------------ +// +// UText implementation for const char16_t * strings +// +// Use of UText data members: +// context pointer to UnicodeString +// a length. -1 if not yet known. +// +// TODO: support 64 bit lengths. +// +//------------------------------------------------------------------------------ + +U_CDECL_BEGIN + + +static UText * U_CALLCONV +ucstrTextClone(UText *dest, const UText * src, UBool deep, UErrorCode * status) { + // First do a generic shallow clone. + dest = shallowTextClone(dest, src, status); + + // For deep clones, make a copy of the string. + // The copied storage is owned by the newly created clone. + // A non-nullptr pointer in UText.p is the signal to the close() function to delete + // it. + // + if (deep && U_SUCCESS(*status)) { + U_ASSERT(utext_nativeLength(dest) < INT32_MAX); + int32_t len = (int32_t)utext_nativeLength(dest); + + // The cloned string IS going to be NUL terminated, whether or not the original was. + const char16_t *srcStr = (const char16_t *)src->context; + char16_t *copyStr = (char16_t *)uprv_malloc((len+1) * sizeof(char16_t)); + if (copyStr == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + } else { + int64_t i; + for (i=0; icontext = copyStr; + dest->providerProperties |= I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT); + } + } + return dest; +} + + +static void U_CALLCONV +ucstrTextClose(UText *ut) { + // Most of the work of close is done by the generic UText framework close. + // All that needs to be done here is delete the string if the UText + // owns it. This occurs if the UText was created by cloning. + if (ut->providerProperties & I32_FLAG(UTEXT_PROVIDER_OWNS_TEXT)) { + char16_t *s = (char16_t *)ut->context; + uprv_free(s); + ut->context = nullptr; + } +} + + + +static int64_t U_CALLCONV +ucstrTextLength(UText *ut) { + if (ut->a < 0) { + // null terminated, we don't yet know the length. Scan for it. + // Access is not convenient for doing this + // because the current iteration position can't be changed. + const char16_t *str = (const char16_t *)ut->context; + for (;;) { + if (str[ut->chunkNativeLimit] == 0) { + break; + } + ut->chunkNativeLimit++; + } + ut->a = ut->chunkNativeLimit; + ut->chunkLength = (int32_t)ut->chunkNativeLimit; + ut->nativeIndexingLimit = ut->chunkLength; + ut->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); + } + return ut->a; +} + + +static UBool U_CALLCONV +ucstrTextAccess(UText *ut, int64_t index, UBool forward) { + const char16_t *str = (const char16_t *)ut->context; + + // pin the requested index to the bounds of the string, + // and set current iteration position. + if (index<0) { + index = 0; + } else if (index < ut->chunkNativeLimit) { + // The request data is within the chunk as it is known so far. + // Put index on a code point boundary. + U16_SET_CP_START(str, 0, index); + } else if (ut->a >= 0) { + // We know the length of this string, and the user is requesting something + // at or beyond the length. Pin the requested index to the length. + index = ut->a; + } else { + // Null terminated string, length not yet known, and the requested index + // is beyond where we have scanned so far. + // Scan to 32 UChars beyond the requested index. The strategy here is + // to avoid fully scanning a long string when the caller only wants to + // see a few characters at its beginning. + int32_t scanLimit = (int32_t)index + 32; + if ((index + 32)>INT32_MAX || (index + 32)<0 ) { // note: int64 expression + scanLimit = INT32_MAX; + } + + int32_t chunkLimit = (int32_t)ut->chunkNativeLimit; + for (; chunkLimita = chunkLimit; + ut->chunkLength = chunkLimit; + ut->nativeIndexingLimit = chunkLimit; + if (index >= chunkLimit) { + index = chunkLimit; + } else { + U16_SET_CP_START(str, 0, index); + } + + ut->chunkNativeLimit = chunkLimit; + ut->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); + goto breakout; + } + } + // We scanned through the next batch of UChars without finding the end. + U16_SET_CP_START(str, 0, index); + if (chunkLimit == INT32_MAX) { + // Scanned to the limit of a 32 bit length. + // Forceably trim the overlength string back so length fits in int32 + // TODO: add support for 64 bit strings. + ut->a = chunkLimit; + ut->chunkLength = chunkLimit; + ut->nativeIndexingLimit = chunkLimit; + if (index > chunkLimit) { + index = chunkLimit; + } + ut->chunkNativeLimit = chunkLimit; + ut->providerProperties &= ~I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); + } else { + // The endpoint of a chunk must not be left in the middle of a surrogate pair. + // If the current end is on a lead surrogate, back the end up by one. + // It doesn't matter if the end char happens to be an unpaired surrogate, + // and it's simpler not to worry about it. + if (U16_IS_LEAD(str[chunkLimit-1])) { + --chunkLimit; + } + // Null-terminated chunk with end still unknown. + // Update the chunk length to reflect what has been scanned thus far. + // That the full length is still unknown is (still) flagged by + // ut->a being < 0. + ut->chunkNativeLimit = chunkLimit; + ut->nativeIndexingLimit = chunkLimit; + ut->chunkLength = chunkLimit; + } + + } +breakout: + U_ASSERT(index<=INT32_MAX); + ut->chunkOffset = (int32_t)index; + + // Check whether request is at the start or end + UBool retVal = (forward && indexchunkNativeLimit) || (!forward && index>0); + return retVal; +} + + + +static int32_t U_CALLCONV +ucstrTextExtract(UText *ut, + int64_t start, int64_t limit, + char16_t *dest, int32_t destCapacity, + UErrorCode *pErrorCode) +{ + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(destCapacity<0 || (dest==nullptr && destCapacity>0) || start>limit) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + //const char16_t *s=(const char16_t *)ut->context; + int32_t si, di; + + int32_t start32; + int32_t limit32; + + // Access the start. Does two things we need: + // Pins 'start' to the length of the string, if it came in out-of-bounds. + // Snaps 'start' to the beginning of a code point. + ucstrTextAccess(ut, start, true); + const char16_t *s=ut->chunkContents; + start32 = ut->chunkOffset; + + int32_t strLength=(int32_t)ut->a; + if (strLength >= 0) { + limit32 = pinIndex(limit, strLength); + } else { + limit32 = pinIndex(limit, INT32_MAX); + } + di = 0; + for (si=start32; sia = si; // set string length for this UText + ut->chunkNativeLimit = si; + ut->chunkLength = si; + ut->nativeIndexingLimit = si; + strLength = si; + limit32 = si; + break; + } + U_ASSERT(di>=0); /* to ensure di never exceeds INT32_MAX, which must not happen logically */ + if (di=0) { + // We have filled the destination buffer, and the string length is known. + // Cut the loop short. There is no need to scan string termination. + di = limit32 - start32; + si = limit32; + break; + } + } + di++; + } + + // If the limit index points to a lead surrogate of a pair, + // add the corresponding trail surrogate to the destination. + if (si>0 && U16_IS_LEAD(s[si-1]) && + ((sichunkNativeLimit) { + ut->chunkOffset = si; + } else { + ucstrTextAccess(ut, si, true); + } + + // Add a terminating NUL if space in the buffer permits, + // and set the error status as required. + u_terminateUChars(dest, destCapacity, di, pErrorCode); + return di; +} + +static const struct UTextFuncs ucstrFuncs = +{ + sizeof(UTextFuncs), + 0, 0, 0, // Reserved alignment padding + ucstrTextClone, + ucstrTextLength, + ucstrTextAccess, + ucstrTextExtract, + nullptr, // Replace + nullptr, // Copy + nullptr, // MapOffsetToNative, + nullptr, // MapIndexToUTF16, + ucstrTextClose, + nullptr, // spare 1 + nullptr, // spare 2 + nullptr, // spare 3 +}; + +U_CDECL_END + +static const char16_t gEmptyUString[] = {0}; + +U_CAPI UText * U_EXPORT2 +utext_openUChars(UText *ut, const char16_t *s, int64_t length, UErrorCode *status) { + if (U_FAILURE(*status)) { + return nullptr; + } + if(s==nullptr && length==0) { + s = gEmptyUString; + } + if (s==nullptr || length < -1 || length>INT32_MAX) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + ut = utext_setup(ut, 0, status); + if (U_SUCCESS(*status)) { + ut->pFuncs = &ucstrFuncs; + ut->context = s; + ut->providerProperties = I32_FLAG(UTEXT_PROVIDER_STABLE_CHUNKS); + if (length==-1) { + ut->providerProperties |= I32_FLAG(UTEXT_PROVIDER_LENGTH_IS_EXPENSIVE); + } + ut->a = length; + ut->chunkContents = s; + ut->chunkNativeStart = 0; + ut->chunkNativeLimit = length>=0? length : 0; + ut->chunkLength = (int32_t)ut->chunkNativeLimit; + ut->chunkOffset = 0; + ut->nativeIndexingLimit = ut->chunkLength; + } + return ut; +} + + +//------------------------------------------------------------------------------ +// +// UText implementation for text from ICU CharacterIterators +// +// Use of UText data members: +// context pointer to the CharacterIterator +// a length of the full text. +// p pointer to buffer 1 +// b start index of local buffer 1 contents +// q pointer to buffer 2 +// c start index of local buffer 2 contents +// r pointer to the character iterator if the UText owns it. +// Null otherwise. +// +//------------------------------------------------------------------------------ +#define CIBufSize 16 + +U_CDECL_BEGIN +static void U_CALLCONV +charIterTextClose(UText *ut) { + // Most of the work of close is done by the generic UText framework close. + // All that needs to be done here is delete the CharacterIterator if the UText + // owns it. This occurs if the UText was created by cloning. + CharacterIterator *ci = (CharacterIterator *)ut->r; + delete ci; + ut->r = nullptr; +} + +static int64_t U_CALLCONV +charIterTextLength(UText *ut) { + return (int32_t)ut->a; +} + +static UBool U_CALLCONV +charIterTextAccess(UText *ut, int64_t index, UBool forward) { + CharacterIterator *ci = (CharacterIterator *)ut->context; + + int32_t clippedIndex = (int32_t)index; + if (clippedIndex<0) { + clippedIndex=0; + } else if (clippedIndex>=ut->a) { + clippedIndex=(int32_t)ut->a; + } + int32_t neededIndex = clippedIndex; + if (!forward && neededIndex>0) { + // reverse iteration, want the position just before what was asked for. + neededIndex--; + } else if (forward && neededIndex==ut->a && neededIndex>0) { + // Forward iteration, don't ask for something past the end of the text. + neededIndex--; + } + + // Find the native index of the start of the buffer containing what we want. + neededIndex -= neededIndex % CIBufSize; + + char16_t *buf = nullptr; + UBool needChunkSetup = true; + int i; + if (ut->chunkNativeStart == neededIndex) { + // The buffer we want is already the current chunk. + needChunkSetup = false; + } else if (ut->b == neededIndex) { + // The first buffer (buffer p) has what we need. + buf = (char16_t *)ut->p; + } else if (ut->c == neededIndex) { + // The second buffer (buffer q) has what we need. + buf = (char16_t *)ut->q; + } else { + // Neither buffer already has what we need. + // Load new data from the character iterator. + // Use the buf that is not the current buffer. + buf = (char16_t *)ut->p; + if (ut->p == ut->chunkContents) { + buf = (char16_t *)ut->q; + } + ci->setIndex(neededIndex); + for (i=0; inextPostInc(); + if (i+neededIndex > ut->a) { + break; + } + } + } + + // We have a buffer with the data we need. + // Set it up as the current chunk, if it wasn't already. + if (needChunkSetup) { + ut->chunkContents = buf; + ut->chunkLength = CIBufSize; + ut->chunkNativeStart = neededIndex; + ut->chunkNativeLimit = neededIndex + CIBufSize; + if (ut->chunkNativeLimit > ut->a) { + ut->chunkNativeLimit = ut->a; + ut->chunkLength = (int32_t)(ut->chunkNativeLimit)-(int32_t)(ut->chunkNativeStart); + } + ut->nativeIndexingLimit = ut->chunkLength; + U_ASSERT(ut->chunkOffset>=0 && ut->chunkOffset<=CIBufSize); + } + ut->chunkOffset = clippedIndex - (int32_t)ut->chunkNativeStart; + UBool success = (forward? ut->chunkOffsetchunkLength : ut->chunkOffset>0); + return success; +} + +static UText * U_CALLCONV +charIterTextClone(UText *dest, const UText *src, UBool deep, UErrorCode * status) { + if (U_FAILURE(*status)) { + return nullptr; + } + + if (deep) { + // There is no CharacterIterator API for cloning the underlying text storage. + *status = U_UNSUPPORTED_ERROR; + return nullptr; + } else { + CharacterIterator *srcCI =(CharacterIterator *)src->context; + srcCI = srcCI->clone(); + dest = utext_openCharacterIterator(dest, srcCI, status); + if (U_FAILURE(*status)) { + return dest; + } + // cast off const on getNativeIndex. + // For CharacterIterator based UTexts, this is safe, the operation is const. + int64_t ix = utext_getNativeIndex((UText *)src); + utext_setNativeIndex(dest, ix); + dest->r = srcCI; // flags that this UText owns the CharacterIterator + } + return dest; +} + +static int32_t U_CALLCONV +charIterTextExtract(UText *ut, + int64_t start, int64_t limit, + char16_t *dest, int32_t destCapacity, + UErrorCode *status) +{ + if(U_FAILURE(*status)) { + return 0; + } + if(destCapacity<0 || (dest==nullptr && destCapacity>0) || start>limit) { + *status=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + int32_t length = (int32_t)ut->a; + int32_t start32 = pinIndex(start, length); + int32_t limit32 = pinIndex(limit, length); + int32_t desti = 0; + int32_t srci; + int32_t copyLimit; + + CharacterIterator *ci = (CharacterIterator *)ut->context; + ci->setIndex32(start32); // Moves ix to lead of surrogate pair, if needed. + srci = ci->getIndex(); + copyLimit = srci; + while (srcinext32PostInc(); + int32_t len = U16_LENGTH(c); + U_ASSERT(desti+len>0); /* to ensure desti+len never exceeds MAX_INT32, which must not happen logically */ + if (desti+len <= destCapacity) { + U16_APPEND_UNSAFE(dest, desti, c); + copyLimit = srci+len; + } else { + desti += len; + *status = U_BUFFER_OVERFLOW_ERROR; + } + srci += len; + } + + charIterTextAccess(ut, copyLimit, true); + + u_terminateUChars(dest, destCapacity, desti, status); + return desti; +} + +static const struct UTextFuncs charIterFuncs = +{ + sizeof(UTextFuncs), + 0, 0, 0, // Reserved alignment padding + charIterTextClone, + charIterTextLength, + charIterTextAccess, + charIterTextExtract, + nullptr, // Replace + nullptr, // Copy + nullptr, // MapOffsetToNative, + nullptr, // MapIndexToUTF16, + charIterTextClose, + nullptr, // spare 1 + nullptr, // spare 2 + nullptr // spare 3 +}; +U_CDECL_END + + +U_CAPI UText * U_EXPORT2 +utext_openCharacterIterator(UText *ut, CharacterIterator *ci, UErrorCode *status) { + if (U_FAILURE(*status)) { + return nullptr; + } + + if (ci->startIndex() > 0) { + // No support for CharacterIterators that do not start indexing from zero. + *status = U_UNSUPPORTED_ERROR; + return nullptr; + } + + // Extra space in UText for 2 buffers of CIBufSize UChars each. + int32_t extraSpace = 2 * CIBufSize * sizeof(char16_t); + ut = utext_setup(ut, extraSpace, status); + if (U_SUCCESS(*status)) { + ut->pFuncs = &charIterFuncs; + ut->context = ci; + ut->providerProperties = 0; + ut->a = ci->endIndex(); // Length of text + ut->p = ut->pExtra; // First buffer + ut->b = -1; // Native index of first buffer contents + ut->q = (char16_t*)ut->pExtra+CIBufSize; // Second buffer + ut->c = -1; // Native index of second buffer contents + + // Initialize current chunk contents to be empty. + // First access will fault something in. + // Note: The initial nativeStart and chunkOffset must sum to zero + // so that getNativeIndex() will correctly compute to zero + // if no call to Access() has ever been made. They can't be both + // zero without Access() thinking that the chunk is valid. + ut->chunkContents = (char16_t *)ut->p; + ut->chunkNativeStart = -1; + ut->chunkOffset = 1; + ut->chunkNativeLimit = 0; + ut->chunkLength = 0; + ut->nativeIndexingLimit = ut->chunkOffset; // enables native indexing + } + return ut; +} diff --git a/deps/icu-small/source/common/utf_impl.cpp b/deps/icu-small/source/common/utf_impl.cpp index a1f9c6529a7a62..b1db51797ef617 100644 --- a/deps/icu-small/source/common/utf_impl.cpp +++ b/deps/icu-small/source/common/utf_impl.cpp @@ -1,329 +1,329 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1999-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: utf_impl.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 1999sep13 -* created by: Markus W. Scherer -* -* This file provides implementation functions for macros in the utfXX.h -* that would otherwise be too long as macros. -*/ - -/* set import/export definitions */ -#ifndef U_UTF8_IMPL -# define U_UTF8_IMPL -#endif - -#include "unicode/utypes.h" -#include "unicode/utf.h" -#include "unicode/utf8.h" -#include "uassert.h" - -/* - * Table of the number of utf8 trail bytes, indexed by the lead byte. - * Used by the deprecated macro UTF8_COUNT_TRAIL_BYTES, defined in utf_old.h - * - * The current macro, U8_COUNT_TRAIL_BYTES, does _not_ use this table. - * - * Note that this table cannot be removed, even if UTF8_COUNT_TRAIL_BYTES were - * changed to no longer use it. References to the table from expansions of UTF8_COUNT_TRAIL_BYTES - * may exist in old client code that must continue to run with newer icu library versions. - * - * This table could be replaced on many machines by - * a few lines of assembler code using an - * "index of first 0-bit from msb" instruction and - * one or two more integer instructions. - * - * For example, on an i386, do something like - * - MOV AL, leadByte - * - NOT AL (8-bit, leave b15..b8==0..0, reverse only b7..b0) - * - MOV AH, 0 - * - BSR BX, AX (16-bit) - * - MOV AX, 6 (result) - * - JZ finish (ZF==1 if leadByte==0xff) - * - SUB AX, BX (result) - * -finish: - * (BSR: Bit Scan Reverse, scans for a 1-bit, starting from the MSB) - */ -U_CAPI const uint8_t -utf8_countTrailBytes[256]={ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - // illegal C0 & C1 - // 2-byte lead bytes C2..DF - 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, - - // 3-byte lead bytes E0..EF - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - // 4-byte lead bytes F0..F4 - // illegal F5..FF - 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static const UChar32 -utf8_errorValue[6]={ - // Same values as UTF8_ERROR_VALUE_1, UTF8_ERROR_VALUE_2, UTF_ERROR_VALUE, - // but without relying on the obsolete unicode/utf_old.h. - 0x15, 0x9f, 0xffff, - 0x10ffff -}; - -static UChar32 -errorValue(int32_t count, int8_t strict) { - if(strict>=0) { - return utf8_errorValue[count]; - } else if(strict==-3) { - return 0xfffd; - } else { - return U_SENTINEL; - } -} - -/* - * Handle the non-inline part of the U8_NEXT() and U8_NEXT_FFFD() macros - * and their obsolete sibling UTF8_NEXT_CHAR_SAFE(). - * - * U8_NEXT() supports NUL-terminated strings indicated via length<0. - * - * The "strict" parameter controls the error behavior: - * <0 "Safe" behavior of U8_NEXT(): - * -1: All illegal byte sequences yield U_SENTINEL=-1. - * -2: Same as -1, except for lenient treatment of surrogate code points as legal. - * Some implementations use this for roundtripping of - * Unicode 16-bit strings that are not well-formed UTF-16, that is, they - * contain unpaired surrogates. - * -3: All illegal byte sequences yield U+FFFD. - * 0 Obsolete "safe" behavior of UTF8_NEXT_CHAR_SAFE(..., false): - * All illegal byte sequences yield a positive code point such that this - * result code point would be encoded with the same number of bytes as - * the illegal sequence. - * >0 Obsolete "strict" behavior of UTF8_NEXT_CHAR_SAFE(..., true): - * Same as the obsolete "safe" behavior, but non-characters are also treated - * like illegal sequences. - * - * Note that a UBool is the same as an int8_t. - */ -U_CAPI UChar32 U_EXPORT2 -utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, UChar32 c, UBool strict) { - // *pi is one after byte c. - int32_t i=*pi; - // length can be negative for NUL-terminated strings: Read and validate one byte at a time. - if(i==length || c>0xf4) { - // end of string, or not a lead byte - } else if(c>=0xf0) { - // Test for 4-byte sequences first because - // U8_NEXT() handles shorter valid sequences inline. - uint8_t t1=s[i], t2, t3; - c&=7; - if(U8_IS_VALID_LEAD4_AND_T1(c, t1) && - ++i!=length && (t2=s[i]-0x80)<=0x3f && - ++i!=length && (t3=s[i]-0x80)<=0x3f) { - ++i; - c=(c<<18)|((t1&0x3f)<<12)|(t2<<6)|t3; - // strict: forbid non-characters like U+fffe - if(strict<=0 || !U_IS_UNICODE_NONCHAR(c)) { - *pi=i; - return c; - } - } - } else if(c>=0xe0) { - c&=0xf; - if(strict!=-2) { - uint8_t t1=s[i], t2; - if(U8_IS_VALID_LEAD3_AND_T1(c, t1) && - ++i!=length && (t2=s[i]-0x80)<=0x3f) { - ++i; - c=(c<<12)|((t1&0x3f)<<6)|t2; - // strict: forbid non-characters like U+fffe - if(strict<=0 || !U_IS_UNICODE_NONCHAR(c)) { - *pi=i; - return c; - } - } - } else { - // strict=-2 -> lenient: allow surrogates - uint8_t t1=s[i]-0x80, t2; - if(t1<=0x3f && (c>0 || t1>=0x20) && - ++i!=length && (t2=s[i]-0x80)<=0x3f) { - *pi=i+1; - return (c<<12)|(t1<<6)|t2; - } - } - } else if(c>=0xc2) { - uint8_t t1=s[i]-0x80; - if(t1<=0x3f) { - *pi=i+1; - return ((c-0xc0)<<6)|t1; - } - } // else 0x80<=c<0xc2 is not a lead byte - - /* error handling */ - c=errorValue(i-*pi, strict); - *pi=i; - return c; -} - -U_CAPI int32_t U_EXPORT2 -utf8_appendCharSafeBody(uint8_t *s, int32_t i, int32_t length, UChar32 c, UBool *pIsError) { - if((uint32_t)(c)<=0x7ff) { - if((i)+1<(length)) { - (s)[(i)++]=(uint8_t)(((c)>>6)|0xc0); - (s)[(i)++]=(uint8_t)(((c)&0x3f)|0x80); - return i; - } - } else if((uint32_t)(c)<=0xffff) { - /* Starting with Unicode 3.2, surrogate code points must not be encoded in UTF-8. */ - if((i)+2<(length) && !U_IS_SURROGATE(c)) { - (s)[(i)++]=(uint8_t)(((c)>>12)|0xe0); - (s)[(i)++]=(uint8_t)((((c)>>6)&0x3f)|0x80); - (s)[(i)++]=(uint8_t)(((c)&0x3f)|0x80); - return i; - } - } else if((uint32_t)(c)<=0x10ffff) { - if((i)+3<(length)) { - (s)[(i)++]=(uint8_t)(((c)>>18)|0xf0); - (s)[(i)++]=(uint8_t)((((c)>>12)&0x3f)|0x80); - (s)[(i)++]=(uint8_t)((((c)>>6)&0x3f)|0x80); - (s)[(i)++]=(uint8_t)(((c)&0x3f)|0x80); - return i; - } - } - /* c>0x10ffff or not enough space, write an error value */ - if(pIsError!=NULL) { - *pIsError=true; - } else { - length-=i; - if(length>0) { - int32_t offset; - if(length>3) { - length=3; - } - s+=i; - offset=0; - c=utf8_errorValue[length-1]; - U8_APPEND_UNSAFE(s, offset, c); - i=i+offset; - } - } - return i; -} - -U_CAPI UChar32 U_EXPORT2 -utf8_prevCharSafeBody(const uint8_t *s, int32_t start, int32_t *pi, UChar32 c, UBool strict) { - // *pi is the index of byte c. - int32_t i=*pi; - if(U8_IS_TRAIL(c) && i>start) { - uint8_t b1=s[--i]; - if(U8_IS_LEAD(b1)) { - if(b1<0xe0) { - *pi=i; - return ((b1-0xc0)<<6)|(c&0x3f); - } else if(b1<0xf0 ? U8_IS_VALID_LEAD3_AND_T1(b1, c) : U8_IS_VALID_LEAD4_AND_T1(b1, c)) { - // Truncated 3- or 4-byte sequence. - *pi=i; - return errorValue(1, strict); - } - } else if(U8_IS_TRAIL(b1) && i>start) { - // Extract the value bits from the last trail byte. - c&=0x3f; - uint8_t b2=s[--i]; - if(0xe0<=b2 && b2<=0xf4) { - if(b2<0xf0) { - b2&=0xf; - if(strict!=-2) { - if(U8_IS_VALID_LEAD3_AND_T1(b2, b1)) { - *pi=i; - c=(b2<<12)|((b1&0x3f)<<6)|c; - if(strict<=0 || !U_IS_UNICODE_NONCHAR(c)) { - return c; - } else { - // strict: forbid non-characters like U+fffe - return errorValue(2, strict); - } - } - } else { - // strict=-2 -> lenient: allow surrogates - b1-=0x80; - if((b2>0 || b1>=0x20)) { - *pi=i; - return (b2<<12)|(b1<<6)|c; - } - } - } else if(U8_IS_VALID_LEAD4_AND_T1(b2, b1)) { - // Truncated 4-byte sequence. - *pi=i; - return errorValue(2, strict); - } - } else if(U8_IS_TRAIL(b2) && i>start) { - uint8_t b3=s[--i]; - if(0xf0<=b3 && b3<=0xf4) { - b3&=7; - if(U8_IS_VALID_LEAD4_AND_T1(b3, b2)) { - *pi=i; - c=(b3<<18)|((b2&0x3f)<<12)|((b1&0x3f)<<6)|c; - if(strict<=0 || !U_IS_UNICODE_NONCHAR(c)) { - return c; - } else { - // strict: forbid non-characters like U+fffe - return errorValue(3, strict); - } - } - } - } - } - } - return errorValue(0, strict); -} - -U_CAPI int32_t U_EXPORT2 -utf8_back1SafeBody(const uint8_t *s, int32_t start, int32_t i) { - // Same as utf8_prevCharSafeBody(..., strict=-1) minus assembling code points. - int32_t orig_i=i; - uint8_t c=s[i]; - if(U8_IS_TRAIL(c) && i>start) { - uint8_t b1=s[--i]; - if(U8_IS_LEAD(b1)) { - if(b1<0xe0 || - (b1<0xf0 ? U8_IS_VALID_LEAD3_AND_T1(b1, c) : U8_IS_VALID_LEAD4_AND_T1(b1, c))) { - return i; - } - } else if(U8_IS_TRAIL(b1) && i>start) { - uint8_t b2=s[--i]; - if(0xe0<=b2 && b2<=0xf4) { - if(b2<0xf0 ? U8_IS_VALID_LEAD3_AND_T1(b2, b1) : U8_IS_VALID_LEAD4_AND_T1(b2, b1)) { - return i; - } - } else if(U8_IS_TRAIL(b2) && i>start) { - uint8_t b3=s[--i]; - if(0xf0<=b3 && b3<=0xf4 && U8_IS_VALID_LEAD4_AND_T1(b3, b2)) { - return i; - } - } - } - } - return orig_i; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 1999-2012, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: utf_impl.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999sep13 +* created by: Markus W. Scherer +* +* This file provides implementation functions for macros in the utfXX.h +* that would otherwise be too long as macros. +*/ + +/* set import/export definitions */ +#ifndef U_UTF8_IMPL +# define U_UTF8_IMPL +#endif + +#include "unicode/utypes.h" +#include "unicode/utf.h" +#include "unicode/utf8.h" +#include "uassert.h" + +/* + * Table of the number of utf8 trail bytes, indexed by the lead byte. + * Used by the deprecated macro UTF8_COUNT_TRAIL_BYTES, defined in utf_old.h + * + * The current macro, U8_COUNT_TRAIL_BYTES, does _not_ use this table. + * + * Note that this table cannot be removed, even if UTF8_COUNT_TRAIL_BYTES were + * changed to no longer use it. References to the table from expansions of UTF8_COUNT_TRAIL_BYTES + * may exist in old client code that must continue to run with newer icu library versions. + * + * This table could be replaced on many machines by + * a few lines of assembler code using an + * "index of first 0-bit from msb" instruction and + * one or two more integer instructions. + * + * For example, on an i386, do something like + * - MOV AL, leadByte + * - NOT AL (8-bit, leave b15..b8==0..0, reverse only b7..b0) + * - MOV AH, 0 + * - BSR BX, AX (16-bit) + * - MOV AX, 6 (result) + * - JZ finish (ZF==1 if leadByte==0xff) + * - SUB AX, BX (result) + * -finish: + * (BSR: Bit Scan Reverse, scans for a 1-bit, starting from the MSB) + */ +U_CAPI const uint8_t +utf8_countTrailBytes[256]={ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + // illegal C0 & C1 + // 2-byte lead bytes C2..DF + 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, + + // 3-byte lead bytes E0..EF + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + // 4-byte lead bytes F0..F4 + // illegal F5..FF + 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const UChar32 +utf8_errorValue[6]={ + // Same values as UTF8_ERROR_VALUE_1, UTF8_ERROR_VALUE_2, UTF_ERROR_VALUE, + // but without relying on the obsolete unicode/utf_old.h. + 0x15, 0x9f, 0xffff, + 0x10ffff +}; + +static UChar32 +errorValue(int32_t count, int8_t strict) { + if(strict>=0) { + return utf8_errorValue[count]; + } else if(strict==-3) { + return 0xfffd; + } else { + return U_SENTINEL; + } +} + +/* + * Handle the non-inline part of the U8_NEXT() and U8_NEXT_FFFD() macros + * and their obsolete sibling UTF8_NEXT_CHAR_SAFE(). + * + * U8_NEXT() supports NUL-terminated strings indicated via length<0. + * + * The "strict" parameter controls the error behavior: + * <0 "Safe" behavior of U8_NEXT(): + * -1: All illegal byte sequences yield U_SENTINEL=-1. + * -2: Same as -1, except for lenient treatment of surrogate code points as legal. + * Some implementations use this for roundtripping of + * Unicode 16-bit strings that are not well-formed UTF-16, that is, they + * contain unpaired surrogates. + * -3: All illegal byte sequences yield U+FFFD. + * 0 Obsolete "safe" behavior of UTF8_NEXT_CHAR_SAFE(..., false): + * All illegal byte sequences yield a positive code point such that this + * result code point would be encoded with the same number of bytes as + * the illegal sequence. + * >0 Obsolete "strict" behavior of UTF8_NEXT_CHAR_SAFE(..., true): + * Same as the obsolete "safe" behavior, but non-characters are also treated + * like illegal sequences. + * + * Note that a UBool is the same as an int8_t. + */ +U_CAPI UChar32 U_EXPORT2 +utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, UChar32 c, UBool strict) { + // *pi is one after byte c. + int32_t i=*pi; + // length can be negative for NUL-terminated strings: Read and validate one byte at a time. + if(i==length || c>0xf4) { + // end of string, or not a lead byte + } else if(c>=0xf0) { + // Test for 4-byte sequences first because + // U8_NEXT() handles shorter valid sequences inline. + uint8_t t1=s[i], t2, t3; + c&=7; + if(U8_IS_VALID_LEAD4_AND_T1(c, t1) && + ++i!=length && (t2=s[i]-0x80)<=0x3f && + ++i!=length && (t3=s[i]-0x80)<=0x3f) { + ++i; + c=(c<<18)|((t1&0x3f)<<12)|(t2<<6)|t3; + // strict: forbid non-characters like U+fffe + if(strict<=0 || !U_IS_UNICODE_NONCHAR(c)) { + *pi=i; + return c; + } + } + } else if(c>=0xe0) { + c&=0xf; + if(strict!=-2) { + uint8_t t1=s[i], t2; + if(U8_IS_VALID_LEAD3_AND_T1(c, t1) && + ++i!=length && (t2=s[i]-0x80)<=0x3f) { + ++i; + c=(c<<12)|((t1&0x3f)<<6)|t2; + // strict: forbid non-characters like U+fffe + if(strict<=0 || !U_IS_UNICODE_NONCHAR(c)) { + *pi=i; + return c; + } + } + } else { + // strict=-2 -> lenient: allow surrogates + uint8_t t1=s[i]-0x80, t2; + if(t1<=0x3f && (c>0 || t1>=0x20) && + ++i!=length && (t2=s[i]-0x80)<=0x3f) { + *pi=i+1; + return (c<<12)|(t1<<6)|t2; + } + } + } else if(c>=0xc2) { + uint8_t t1=s[i]-0x80; + if(t1<=0x3f) { + *pi=i+1; + return ((c-0xc0)<<6)|t1; + } + } // else 0x80<=c<0xc2 is not a lead byte + + /* error handling */ + c=errorValue(i-*pi, strict); + *pi=i; + return c; +} + +U_CAPI int32_t U_EXPORT2 +utf8_appendCharSafeBody(uint8_t *s, int32_t i, int32_t length, UChar32 c, UBool *pIsError) { + if((uint32_t)(c)<=0x7ff) { + if((i)+1<(length)) { + (s)[(i)++]=(uint8_t)(((c)>>6)|0xc0); + (s)[(i)++]=(uint8_t)(((c)&0x3f)|0x80); + return i; + } + } else if((uint32_t)(c)<=0xffff) { + /* Starting with Unicode 3.2, surrogate code points must not be encoded in UTF-8. */ + if((i)+2<(length) && !U_IS_SURROGATE(c)) { + (s)[(i)++]=(uint8_t)(((c)>>12)|0xe0); + (s)[(i)++]=(uint8_t)((((c)>>6)&0x3f)|0x80); + (s)[(i)++]=(uint8_t)(((c)&0x3f)|0x80); + return i; + } + } else if((uint32_t)(c)<=0x10ffff) { + if((i)+3<(length)) { + (s)[(i)++]=(uint8_t)(((c)>>18)|0xf0); + (s)[(i)++]=(uint8_t)((((c)>>12)&0x3f)|0x80); + (s)[(i)++]=(uint8_t)((((c)>>6)&0x3f)|0x80); + (s)[(i)++]=(uint8_t)(((c)&0x3f)|0x80); + return i; + } + } + /* c>0x10ffff or not enough space, write an error value */ + if(pIsError!=nullptr) { + *pIsError=true; + } else { + length-=i; + if(length>0) { + int32_t offset; + if(length>3) { + length=3; + } + s+=i; + offset=0; + c=utf8_errorValue[length-1]; + U8_APPEND_UNSAFE(s, offset, c); + i=i+offset; + } + } + return i; +} + +U_CAPI UChar32 U_EXPORT2 +utf8_prevCharSafeBody(const uint8_t *s, int32_t start, int32_t *pi, UChar32 c, UBool strict) { + // *pi is the index of byte c. + int32_t i=*pi; + if(U8_IS_TRAIL(c) && i>start) { + uint8_t b1=s[--i]; + if(U8_IS_LEAD(b1)) { + if(b1<0xe0) { + *pi=i; + return ((b1-0xc0)<<6)|(c&0x3f); + } else if(b1<0xf0 ? U8_IS_VALID_LEAD3_AND_T1(b1, c) : U8_IS_VALID_LEAD4_AND_T1(b1, c)) { + // Truncated 3- or 4-byte sequence. + *pi=i; + return errorValue(1, strict); + } + } else if(U8_IS_TRAIL(b1) && i>start) { + // Extract the value bits from the last trail byte. + c&=0x3f; + uint8_t b2=s[--i]; + if(0xe0<=b2 && b2<=0xf4) { + if(b2<0xf0) { + b2&=0xf; + if(strict!=-2) { + if(U8_IS_VALID_LEAD3_AND_T1(b2, b1)) { + *pi=i; + c=(b2<<12)|((b1&0x3f)<<6)|c; + if(strict<=0 || !U_IS_UNICODE_NONCHAR(c)) { + return c; + } else { + // strict: forbid non-characters like U+fffe + return errorValue(2, strict); + } + } + } else { + // strict=-2 -> lenient: allow surrogates + b1-=0x80; + if((b2>0 || b1>=0x20)) { + *pi=i; + return (b2<<12)|(b1<<6)|c; + } + } + } else if(U8_IS_VALID_LEAD4_AND_T1(b2, b1)) { + // Truncated 4-byte sequence. + *pi=i; + return errorValue(2, strict); + } + } else if(U8_IS_TRAIL(b2) && i>start) { + uint8_t b3=s[--i]; + if(0xf0<=b3 && b3<=0xf4) { + b3&=7; + if(U8_IS_VALID_LEAD4_AND_T1(b3, b2)) { + *pi=i; + c=(b3<<18)|((b2&0x3f)<<12)|((b1&0x3f)<<6)|c; + if(strict<=0 || !U_IS_UNICODE_NONCHAR(c)) { + return c; + } else { + // strict: forbid non-characters like U+fffe + return errorValue(3, strict); + } + } + } + } + } + } + return errorValue(0, strict); +} + +U_CAPI int32_t U_EXPORT2 +utf8_back1SafeBody(const uint8_t *s, int32_t start, int32_t i) { + // Same as utf8_prevCharSafeBody(..., strict=-1) minus assembling code points. + int32_t orig_i=i; + uint8_t c=s[i]; + if(U8_IS_TRAIL(c) && i>start) { + uint8_t b1=s[--i]; + if(U8_IS_LEAD(b1)) { + if(b1<0xe0 || + (b1<0xf0 ? U8_IS_VALID_LEAD3_AND_T1(b1, c) : U8_IS_VALID_LEAD4_AND_T1(b1, c))) { + return i; + } + } else if(U8_IS_TRAIL(b1) && i>start) { + uint8_t b2=s[--i]; + if(0xe0<=b2 && b2<=0xf4) { + if(b2<0xf0 ? U8_IS_VALID_LEAD3_AND_T1(b2, b1) : U8_IS_VALID_LEAD4_AND_T1(b2, b1)) { + return i; + } + } else if(U8_IS_TRAIL(b2) && i>start) { + uint8_t b3=s[--i]; + if(0xf0<=b3 && b3<=0xf4 && U8_IS_VALID_LEAD4_AND_T1(b3, b2)) { + return i; + } + } + } + } + return orig_i; +} diff --git a/deps/icu-small/source/common/util.cpp b/deps/icu-small/source/common/util.cpp index 3dcc05578b795f..7422465085e022 100644 --- a/deps/icu-small/source/common/util.cpp +++ b/deps/icu-small/source/common/util.cpp @@ -1,435 +1,435 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2001-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/19/2001 aliu Creation. -********************************************************************** -*/ - -#include "unicode/unimatch.h" -#include "unicode/utf16.h" -#include "patternprops.h" -#include "util.h" - -// Define UChar constants using hex for EBCDIC compatibility - -static const UChar BACKSLASH = 0x005C; /*\*/ -static const UChar UPPER_U = 0x0055; /*U*/ -static const UChar LOWER_U = 0x0075; /*u*/ -static const UChar APOSTROPHE = 0x0027; // '\'' -static const UChar SPACE = 0x0020; // ' ' - -// "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" -static const UChar DIGITS[] = { - 48,49,50,51,52,53,54,55,56,57, - 65,66,67,68,69,70,71,72,73,74, - 75,76,77,78,79,80,81,82,83,84, - 85,86,87,88,89,90 -}; - -U_NAMESPACE_BEGIN - -UnicodeString& ICU_Utility::appendNumber(UnicodeString& result, int32_t n, - int32_t radix, int32_t minDigits) { - if (radix < 2 || radix > 36) { - // Bogus radix - return result.append((UChar)63/*?*/); - } - // Handle negatives - if (n < 0) { - n = -n; - result.append((UChar)45/*-*/); - } - // First determine the number of digits - int32_t nn = n; - int32_t r = 1; - while (nn >= radix) { - nn /= radix; - r *= radix; - --minDigits; - } - // Now generate the digits - while (--minDigits > 0) { - result.append(DIGITS[0]); - } - while (r > 0) { - int32_t digit = n / r; - result.append(DIGITS[digit]); - n -= digit * r; - r /= radix; - } - return result; -} - -UBool ICU_Utility::isUnprintable(UChar32 c) { - return !(c >= 0x20 && c <= 0x7E); -} - -UBool ICU_Utility::shouldAlwaysBeEscaped(UChar32 c) { - if (c < 0x20) { - return true; // C0 control codes - } else if (c <= 0x7e) { - return false; // printable ASCII - } else if (c <= 0x9f) { - return true; // C1 control codes - } else if (c < 0xd800) { - return false; // most of the BMP - } else if (c <= 0xdfff || (0xfdd0 <= c && c <= 0xfdef) || (c & 0xfffe) == 0xfffe) { - return true; // surrogate or noncharacter code points - } else if (c <= 0x10ffff) { - return false; // all else - } else { - return true; // not a code point - } -} - -UBool ICU_Utility::escapeUnprintable(UnicodeString& result, UChar32 c) { - if (isUnprintable(c)) { - escape(result, c); - return true; - } - return false; -} - -UnicodeString &ICU_Utility::escape(UnicodeString& result, UChar32 c) { - result.append(BACKSLASH); - if (c & ~0xFFFF) { - result.append(UPPER_U); - result.append(DIGITS[0xF&(c>>28)]); - result.append(DIGITS[0xF&(c>>24)]); - result.append(DIGITS[0xF&(c>>20)]); - result.append(DIGITS[0xF&(c>>16)]); - } else { - result.append(LOWER_U); - } - result.append(DIGITS[0xF&(c>>12)]); - result.append(DIGITS[0xF&(c>>8)]); - result.append(DIGITS[0xF&(c>>4)]); - result.append(DIGITS[0xF&c]); - return result; -} - -/** - * Returns the index of a character, ignoring quoted text. - * For example, in the string "abc'hide'h", the 'h' in "hide" will not be - * found by a search for 'h'. - */ -// FOR FUTURE USE. DISABLE FOR NOW for coverage reasons. -/* -int32_t ICU_Utility::quotedIndexOf(const UnicodeString& text, - int32_t start, int32_t limit, - UChar charToFind) { - for (int32_t i=start; i= pos); -//? -//? if (!isForward) { -//? --pos; // pos is a limit, so back up by one -//? } -//? -//? while (pos != stop && -//? PatternProps::isWhiteSpace(c = text.char32At(pos))) { -//? if (isForward) { -//? pos += U16_LENGTH(c); -//? } else { -//? pos -= U16_LENGTH(c); -//? } -//? } -//? -//? if (!isForward) { -//? ++pos; // make pos back into a limit -//? } -//? -//? return pos; -//?} - -/** - * Parse a single non-whitespace character 'ch', optionally - * preceded by whitespace. - * @param id the string to be parsed - * @param pos INPUT-OUTPUT parameter. On input, pos[0] is the - * offset of the first character to be parsed. On output, pos[0] - * is the index after the last parsed character. If the parse - * fails, pos[0] will be unchanged. - * @param ch the non-whitespace character to be parsed. - * @return true if 'ch' is seen preceded by zero or more - * whitespace characters. - */ -UBool ICU_Utility::parseChar(const UnicodeString& id, int32_t& pos, UChar ch) { - int32_t start = pos; - skipWhitespace(id, pos, true); - if (pos == id.length() || - id.charAt(pos) != ch) { - pos = start; - return false; - } - ++pos; - return true; -} - -/** - * Parse a pattern string within the given Replaceable and a parsing - * pattern. Characters are matched literally and case-sensitively - * except for the following special characters: - * - * ~ zero or more Pattern_White_Space chars - * - * If end of pattern is reached with all matches along the way, - * pos is advanced to the first unparsed index and returned. - * Otherwise -1 is returned. - * @param pat pattern that controls parsing - * @param text text to be parsed, starting at index - * @param index offset to first character to parse - * @param limit offset after last character to parse - * @return index after last parsed character, or -1 on parse failure. - */ -int32_t ICU_Utility::parsePattern(const UnicodeString& pat, - const Replaceable& text, - int32_t index, - int32_t limit) { - int32_t ipat = 0; - - // empty pattern matches immediately - if (ipat == pat.length()) { - return index; - } - - UChar32 cpat = pat.char32At(ipat); - - while (index < limit) { - UChar32 c = text.char32At(index); - - // parse \s* - if (cpat == 126 /*~*/) { - if (PatternProps::isWhiteSpace(c)) { - index += U16_LENGTH(c); - continue; - } else { - if (++ipat == pat.length()) { - return index; // success; c unparsed - } - // fall thru; process c again with next cpat - } - } - - // parse literal - else if (c == cpat) { - index += U16_LENGTH(c); - ipat += U16_LENGTH(cpat); - if (ipat == pat.length()) { - return index; // success; c parsed - } - // fall thru; get next cpat - } - - // match failure of literal - else { - return -1; - } - - cpat = pat.char32At(ipat); - } - - return -1; // text ended before end of pat -} - -int32_t ICU_Utility::parseAsciiInteger(const UnicodeString& str, int32_t& pos) { - int32_t result = 0; - UChar c; - while (pos < str.length() && (c = str.charAt(pos)) >= u'0' && c <= u'9') { - result = result * 10 + (c - u'0'); - pos++; - } - return result; -} - -/** - * Append a character to a rule that is being built up. To flush - * the quoteBuf to rule, make one final call with isLiteral == true. - * If there is no final character, pass in (UChar32)-1 as c. - * @param rule the string to append the character to - * @param c the character to append, or (UChar32)-1 if none. - * @param isLiteral if true, then the given character should not be - * quoted or escaped. Usually this means it is a syntactic element - * such as > or $ - * @param escapeUnprintable if true, then unprintable characters - * should be escaped using \uxxxx or \Uxxxxxxxx. These escapes will - * appear outside of quotes. - * @param quoteBuf a buffer which is used to build up quoted - * substrings. The caller should initially supply an empty buffer, - * and thereafter should not modify the buffer. The buffer should be - * cleared out by, at the end, calling this method with a literal - * character. - */ -void ICU_Utility::appendToRule(UnicodeString& rule, - UChar32 c, - UBool isLiteral, - UBool escapeUnprintable, - UnicodeString& quoteBuf) { - // If we are escaping unprintables, then escape them outside - // quotes. \u and \U are not recognized within quotes. The same - // logic applies to literals, but literals are never escaped. - if (isLiteral || - (escapeUnprintable && ICU_Utility::isUnprintable(c))) { - if (quoteBuf.length() > 0) { - // We prefer backslash APOSTROPHE to double APOSTROPHE - // (more readable, less similar to ") so if there are - // double APOSTROPHEs at the ends, we pull them outside - // of the quote. - - // If the first thing in the quoteBuf is APOSTROPHE - // (doubled) then pull it out. - while (quoteBuf.length() >= 2 && - quoteBuf.charAt(0) == APOSTROPHE && - quoteBuf.charAt(1) == APOSTROPHE) { - rule.append(BACKSLASH).append(APOSTROPHE); - quoteBuf.remove(0, 2); - } - // If the last thing in the quoteBuf is APOSTROPHE - // (doubled) then remove and count it and add it after. - int32_t trailingCount = 0; - while (quoteBuf.length() >= 2 && - quoteBuf.charAt(quoteBuf.length()-2) == APOSTROPHE && - quoteBuf.charAt(quoteBuf.length()-1) == APOSTROPHE) { - quoteBuf.truncate(quoteBuf.length()-2); - ++trailingCount; - } - if (quoteBuf.length() > 0) { - rule.append(APOSTROPHE); - rule.append(quoteBuf); - rule.append(APOSTROPHE); - quoteBuf.truncate(0); - } - while (trailingCount-- > 0) { - rule.append(BACKSLASH).append(APOSTROPHE); - } - } - if (c != (UChar32)-1) { - /* Since spaces are ignored during parsing, they are - * emitted only for readability. We emit one here - * only if there isn't already one at the end of the - * rule. - */ - if (c == SPACE) { - int32_t len = rule.length(); - if (len > 0 && rule.charAt(len-1) != c) { - rule.append(c); - } - } else if (!escapeUnprintable || !ICU_Utility::escapeUnprintable(rule, c)) { - rule.append(c); - } - } - } - - // Escape ' and '\' and don't begin a quote just for them - else if (quoteBuf.length() == 0 && - (c == APOSTROPHE || c == BACKSLASH)) { - rule.append(BACKSLASH); - rule.append(c); - } - - // Specials (printable ascii that isn't [0-9a-zA-Z]) and - // whitespace need quoting. Also append stuff to quotes if we are - // building up a quoted substring already. - else if (quoteBuf.length() > 0 || - (c >= 0x0021 && c <= 0x007E && - !((c >= 0x0030/*'0'*/ && c <= 0x0039/*'9'*/) || - (c >= 0x0041/*'A'*/ && c <= 0x005A/*'Z'*/) || - (c >= 0x0061/*'a'*/ && c <= 0x007A/*'z'*/))) || - PatternProps::isWhiteSpace(c)) { - quoteBuf.append(c); - // Double ' within a quote - if (c == APOSTROPHE) { - quoteBuf.append(c); - } - } - - // Otherwise just append - else { - rule.append(c); - } -} - -void ICU_Utility::appendToRule(UnicodeString& rule, - const UnicodeString& text, - UBool isLiteral, - UBool escapeUnprintable, - UnicodeString& quoteBuf) { - for (int32_t i=0; itoPattern(pat, escapeUnprintable), - true, escapeUnprintable, quoteBuf); - } -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2001-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/19/2001 aliu Creation. +********************************************************************** +*/ + +#include "unicode/unimatch.h" +#include "unicode/utf16.h" +#include "patternprops.h" +#include "util.h" + +// Define char16_t constants using hex for EBCDIC compatibility + +static const char16_t BACKSLASH = 0x005C; /*\*/ +static const char16_t UPPER_U = 0x0055; /*U*/ +static const char16_t LOWER_U = 0x0075; /*u*/ +static const char16_t APOSTROPHE = 0x0027; // '\'' +static const char16_t SPACE = 0x0020; // ' ' + +// "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" +static const char16_t DIGITS[] = { + 48,49,50,51,52,53,54,55,56,57, + 65,66,67,68,69,70,71,72,73,74, + 75,76,77,78,79,80,81,82,83,84, + 85,86,87,88,89,90 +}; + +U_NAMESPACE_BEGIN + +UnicodeString& ICU_Utility::appendNumber(UnicodeString& result, int32_t n, + int32_t radix, int32_t minDigits) { + if (radix < 2 || radix > 36) { + // Bogus radix + return result.append((char16_t)63/*?*/); + } + // Handle negatives + if (n < 0) { + n = -n; + result.append((char16_t)45/*-*/); + } + // First determine the number of digits + int32_t nn = n; + int32_t r = 1; + while (nn >= radix) { + nn /= radix; + r *= radix; + --minDigits; + } + // Now generate the digits + while (--minDigits > 0) { + result.append(DIGITS[0]); + } + while (r > 0) { + int32_t digit = n / r; + result.append(DIGITS[digit]); + n -= digit * r; + r /= radix; + } + return result; +} + +UBool ICU_Utility::isUnprintable(UChar32 c) { + return !(c >= 0x20 && c <= 0x7E); +} + +UBool ICU_Utility::shouldAlwaysBeEscaped(UChar32 c) { + if (c < 0x20) { + return true; // C0 control codes + } else if (c <= 0x7e) { + return false; // printable ASCII + } else if (c <= 0x9f) { + return true; // C1 control codes + } else if (c < 0xd800) { + return false; // most of the BMP + } else if (c <= 0xdfff || (0xfdd0 <= c && c <= 0xfdef) || (c & 0xfffe) == 0xfffe) { + return true; // surrogate or noncharacter code points + } else if (c <= 0x10ffff) { + return false; // all else + } else { + return true; // not a code point + } +} + +UBool ICU_Utility::escapeUnprintable(UnicodeString& result, UChar32 c) { + if (isUnprintable(c)) { + escape(result, c); + return true; + } + return false; +} + +UnicodeString &ICU_Utility::escape(UnicodeString& result, UChar32 c) { + result.append(BACKSLASH); + if (c & ~0xFFFF) { + result.append(UPPER_U); + result.append(DIGITS[0xF&(c>>28)]); + result.append(DIGITS[0xF&(c>>24)]); + result.append(DIGITS[0xF&(c>>20)]); + result.append(DIGITS[0xF&(c>>16)]); + } else { + result.append(LOWER_U); + } + result.append(DIGITS[0xF&(c>>12)]); + result.append(DIGITS[0xF&(c>>8)]); + result.append(DIGITS[0xF&(c>>4)]); + result.append(DIGITS[0xF&c]); + return result; +} + +/** + * Returns the index of a character, ignoring quoted text. + * For example, in the string "abc'hide'h", the 'h' in "hide" will not be + * found by a search for 'h'. + */ +// FOR FUTURE USE. DISABLE FOR NOW for coverage reasons. +/* +int32_t ICU_Utility::quotedIndexOf(const UnicodeString& text, + int32_t start, int32_t limit, + char16_t charToFind) { + for (int32_t i=start; i= pos); +//? +//? if (!isForward) { +//? --pos; // pos is a limit, so back up by one +//? } +//? +//? while (pos != stop && +//? PatternProps::isWhiteSpace(c = text.char32At(pos))) { +//? if (isForward) { +//? pos += U16_LENGTH(c); +//? } else { +//? pos -= U16_LENGTH(c); +//? } +//? } +//? +//? if (!isForward) { +//? ++pos; // make pos back into a limit +//? } +//? +//? return pos; +//?} + +/** + * Parse a single non-whitespace character 'ch', optionally + * preceded by whitespace. + * @param id the string to be parsed + * @param pos INPUT-OUTPUT parameter. On input, pos[0] is the + * offset of the first character to be parsed. On output, pos[0] + * is the index after the last parsed character. If the parse + * fails, pos[0] will be unchanged. + * @param ch the non-whitespace character to be parsed. + * @return true if 'ch' is seen preceded by zero or more + * whitespace characters. + */ +UBool ICU_Utility::parseChar(const UnicodeString& id, int32_t& pos, char16_t ch) { + int32_t start = pos; + skipWhitespace(id, pos, true); + if (pos == id.length() || + id.charAt(pos) != ch) { + pos = start; + return false; + } + ++pos; + return true; +} + +/** + * Parse a pattern string within the given Replaceable and a parsing + * pattern. Characters are matched literally and case-sensitively + * except for the following special characters: + * + * ~ zero or more Pattern_White_Space chars + * + * If end of pattern is reached with all matches along the way, + * pos is advanced to the first unparsed index and returned. + * Otherwise -1 is returned. + * @param pat pattern that controls parsing + * @param text text to be parsed, starting at index + * @param index offset to first character to parse + * @param limit offset after last character to parse + * @return index after last parsed character, or -1 on parse failure. + */ +int32_t ICU_Utility::parsePattern(const UnicodeString& pat, + const Replaceable& text, + int32_t index, + int32_t limit) { + int32_t ipat = 0; + + // empty pattern matches immediately + if (ipat == pat.length()) { + return index; + } + + UChar32 cpat = pat.char32At(ipat); + + while (index < limit) { + UChar32 c = text.char32At(index); + + // parse \s* + if (cpat == 126 /*~*/) { + if (PatternProps::isWhiteSpace(c)) { + index += U16_LENGTH(c); + continue; + } else { + if (++ipat == pat.length()) { + return index; // success; c unparsed + } + // fall thru; process c again with next cpat + } + } + + // parse literal + else if (c == cpat) { + index += U16_LENGTH(c); + ipat += U16_LENGTH(cpat); + if (ipat == pat.length()) { + return index; // success; c parsed + } + // fall thru; get next cpat + } + + // match failure of literal + else { + return -1; + } + + cpat = pat.char32At(ipat); + } + + return -1; // text ended before end of pat +} + +int32_t ICU_Utility::parseAsciiInteger(const UnicodeString& str, int32_t& pos) { + int32_t result = 0; + char16_t c; + while (pos < str.length() && (c = str.charAt(pos)) >= u'0' && c <= u'9') { + result = result * 10 + (c - u'0'); + pos++; + } + return result; +} + +/** + * Append a character to a rule that is being built up. To flush + * the quoteBuf to rule, make one final call with isLiteral == true. + * If there is no final character, pass in (UChar32)-1 as c. + * @param rule the string to append the character to + * @param c the character to append, or (UChar32)-1 if none. + * @param isLiteral if true, then the given character should not be + * quoted or escaped. Usually this means it is a syntactic element + * such as > or $ + * @param escapeUnprintable if true, then unprintable characters + * should be escaped using \uxxxx or \Uxxxxxxxx. These escapes will + * appear outside of quotes. + * @param quoteBuf a buffer which is used to build up quoted + * substrings. The caller should initially supply an empty buffer, + * and thereafter should not modify the buffer. The buffer should be + * cleared out by, at the end, calling this method with a literal + * character. + */ +void ICU_Utility::appendToRule(UnicodeString& rule, + UChar32 c, + UBool isLiteral, + UBool escapeUnprintable, + UnicodeString& quoteBuf) { + // If we are escaping unprintables, then escape them outside + // quotes. \u and \U are not recognized within quotes. The same + // logic applies to literals, but literals are never escaped. + if (isLiteral || + (escapeUnprintable && ICU_Utility::isUnprintable(c))) { + if (quoteBuf.length() > 0) { + // We prefer backslash APOSTROPHE to double APOSTROPHE + // (more readable, less similar to ") so if there are + // double APOSTROPHEs at the ends, we pull them outside + // of the quote. + + // If the first thing in the quoteBuf is APOSTROPHE + // (doubled) then pull it out. + while (quoteBuf.length() >= 2 && + quoteBuf.charAt(0) == APOSTROPHE && + quoteBuf.charAt(1) == APOSTROPHE) { + rule.append(BACKSLASH).append(APOSTROPHE); + quoteBuf.remove(0, 2); + } + // If the last thing in the quoteBuf is APOSTROPHE + // (doubled) then remove and count it and add it after. + int32_t trailingCount = 0; + while (quoteBuf.length() >= 2 && + quoteBuf.charAt(quoteBuf.length()-2) == APOSTROPHE && + quoteBuf.charAt(quoteBuf.length()-1) == APOSTROPHE) { + quoteBuf.truncate(quoteBuf.length()-2); + ++trailingCount; + } + if (quoteBuf.length() > 0) { + rule.append(APOSTROPHE); + rule.append(quoteBuf); + rule.append(APOSTROPHE); + quoteBuf.truncate(0); + } + while (trailingCount-- > 0) { + rule.append(BACKSLASH).append(APOSTROPHE); + } + } + if (c != (UChar32)-1) { + /* Since spaces are ignored during parsing, they are + * emitted only for readability. We emit one here + * only if there isn't already one at the end of the + * rule. + */ + if (c == SPACE) { + int32_t len = rule.length(); + if (len > 0 && rule.charAt(len-1) != c) { + rule.append(c); + } + } else if (!escapeUnprintable || !ICU_Utility::escapeUnprintable(rule, c)) { + rule.append(c); + } + } + } + + // Escape ' and '\' and don't begin a quote just for them + else if (quoteBuf.length() == 0 && + (c == APOSTROPHE || c == BACKSLASH)) { + rule.append(BACKSLASH); + rule.append(c); + } + + // Specials (printable ascii that isn't [0-9a-zA-Z]) and + // whitespace need quoting. Also append stuff to quotes if we are + // building up a quoted substring already. + else if (quoteBuf.length() > 0 || + (c >= 0x0021 && c <= 0x007E && + !((c >= 0x0030/*'0'*/ && c <= 0x0039/*'9'*/) || + (c >= 0x0041/*'A'*/ && c <= 0x005A/*'Z'*/) || + (c >= 0x0061/*'a'*/ && c <= 0x007A/*'z'*/))) || + PatternProps::isWhiteSpace(c)) { + quoteBuf.append(c); + // Double ' within a quote + if (c == APOSTROPHE) { + quoteBuf.append(c); + } + } + + // Otherwise just append + else { + rule.append(c); + } +} + +void ICU_Utility::appendToRule(UnicodeString& rule, + const UnicodeString& text, + UBool isLiteral, + UBool escapeUnprintable, + UnicodeString& quoteBuf) { + for (int32_t i=0; itoPattern(pat, escapeUnprintable), + true, escapeUnprintable, quoteBuf); + } +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/util.h b/deps/icu-small/source/common/util.h index 4a9ae831de36c2..e500bf4a2f2c57 100644 --- a/deps/icu-small/source/common/util.h +++ b/deps/icu-small/source/common/util.h @@ -1,267 +1,267 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (c) 2001-2011, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - * Date Name Description - * 11/19/2001 aliu Creation. - ********************************************************************** - */ - -#ifndef ICU_UTIL_H -#define ICU_UTIL_H - -#include "charstr.h" -#include "unicode/unistr.h" -#include "unicode/uobject.h" -#include "unicode/utypes.h" -//-------------------------------------------------------------------- -// class ICU_Utility -// i18n utility functions, scoped into the class ICU_Utility. -//-------------------------------------------------------------------- - -U_NAMESPACE_BEGIN - -class UnicodeMatcher; - -class U_COMMON_API ICU_Utility /* not : public UObject because all methods are static */ { - public: - - /** - * Append a number to the given UnicodeString in the given radix. - * Standard digits '0'-'9' are used and letters 'A'-'Z' for - * radices 11 through 36. - * @param result the digits of the number are appended here - * @param n the number to be converted to digits; may be negative. - * If negative, a '-' is prepended to the digits. - * @param radix a radix from 2 to 36 inclusive. - * @param minDigits the minimum number of digits, not including - * any '-', to produce. Values less than 2 have no effect. One - * digit is always emitted regardless of this parameter. - * @return a reference to result - */ - static UnicodeString& appendNumber(UnicodeString& result, int32_t n, - 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. - * The tab, newline and linefeed characters are considered unprintable. - */ - static UBool isUnprintable(UChar32 c); - - /** - * @return true for control codes and for surrogate and noncharacter code points - */ - static UBool shouldAlwaysBeEscaped(UChar32 c); - - /** - * Escapes one unprintable code point using \uxxxx notation for U+0000 to - * U+FFFF and \Uxxxxxxxx for U+10000 and above. If the character is - * printable ASCII, then do nothing and return false. Otherwise, - * append the escaped notation and return true. - */ - static UBool escapeUnprintable(UnicodeString& result, UChar32 c); - - /** - * Escapes one code point using \uxxxx notation - * for U+0000 to U+FFFF and \Uxxxxxxxx for U+10000 and above. - * @return result - */ - static UnicodeString &escape(UnicodeString& result, UChar32 c); - - /** - * Returns the index of a character, ignoring quoted text. - * For example, in the string "abc'hide'h", the 'h' in "hide" will not be - * found by a search for 'h'. - * @param text text to be searched - * @param start the beginning index, inclusive; 0 <= start - * <= limit. - * @param limit the ending index, exclusive; start <= limit - * <= text.length(). - * @param c character to search for - * @return Offset of the first instance of c, or -1 if not found. - */ -//?FOR FUTURE USE. DISABLE FOR NOW for coverage reasons. -// static int32_t quotedIndexOf(const UnicodeString& text, -// int32_t start, int32_t limit, -// UChar c); - - /** - * Skip over a sequence of zero or more white space characters at pos. - * @param advance if true, advance pos to the first non-white-space - * character at or after pos, or str.length(), if there is none. - * Otherwise leave pos unchanged. - * @return the index of the first non-white-space character at or - * after pos, or str.length(), if there is none. - */ - static int32_t skipWhitespace(const UnicodeString& str, int32_t& pos, - UBool advance = false); - - /** - * Skip over Pattern_White_Space in a Replaceable. - * Skipping may be done in the forward or - * reverse direction. In either case, the leftmost index will be - * inclusive, and the rightmost index will be exclusive. That is, - * given a range defined as [start, limit), the call - * skipWhitespace(text, start, limit) will advance start past leading - * whitespace, whereas the call skipWhitespace(text, limit, start), - * will back up limit past trailing whitespace. - * @param text the text to be analyzed - * @param pos either the start or limit of a range of 'text', to skip - * leading or trailing whitespace, respectively - * @param stop either the limit or start of a range of 'text', to skip - * leading or trailing whitespace, respectively - * @return the new start or limit, depending on what was passed in to - * 'pos' - */ -//?FOR FUTURE USE. DISABLE FOR NOW for coverage reasons. -//? static int32_t skipWhitespace(const Replaceable& text, -//? int32_t pos, int32_t stop); - - /** - * Parse a single non-whitespace character 'ch', optionally - * preceded by whitespace. - * @param id the string to be parsed - * @param pos INPUT-OUTPUT parameter. On input, pos[0] is the - * offset of the first character to be parsed. On output, pos[0] - * is the index after the last parsed character. If the parse - * fails, pos[0] will be unchanged. - * @param ch the non-whitespace character to be parsed. - * @return true if 'ch' is seen preceded by zero or more - * whitespace characters. - */ - static UBool parseChar(const UnicodeString& id, int32_t& pos, UChar ch); - - /** - * Parse a pattern string starting at offset pos. Keywords are - * matched case-insensitively. Spaces may be skipped and may be - * optional or required. Integer values may be parsed, and if - * they are, they will be returned in the given array. If - * successful, the offset of the next non-space character is - * returned. On failure, -1 is returned. - * @param pattern must only contain lowercase characters, which - * will match their uppercase equivalents as well. A space - * character matches one or more required spaces. A '~' character - * matches zero or more optional spaces. A '#' character matches - * an integer and stores it in parsedInts, which the caller must - * ensure has enough capacity. - * @param parsedInts array to receive parsed integers. Caller - * must ensure that parsedInts.length is >= the number of '#' - * signs in 'pattern'. - * @return the position after the last character parsed, or -1 if - * the parse failed - */ - static int32_t parsePattern(const UnicodeString& rule, int32_t pos, int32_t limit, - const UnicodeString& pattern, int32_t* parsedInts); - - /** - * Parse a pattern string within the given Replaceable and a parsing - * pattern. Characters are matched literally and case-sensitively - * except for the following special characters: - * - * ~ zero or more Pattern_White_Space chars - * - * If end of pattern is reached with all matches along the way, - * pos is advanced to the first unparsed index and returned. - * Otherwise -1 is returned. - * @param pat pattern that controls parsing - * @param text text to be parsed, starting at index - * @param index offset to first character to parse - * @param limit offset after last character to parse - * @return index after last parsed character, or -1 on parse failure. - */ - static int32_t parsePattern(const UnicodeString& pat, - const Replaceable& text, - int32_t index, - int32_t limit); - - /** - * Parse an integer at pos, either of the form \d+ or of the form - * 0x[0-9A-Fa-f]+ or 0[0-7]+, that is, in standard decimal, hex, - * or octal format. - * @param pos INPUT-OUTPUT parameter. On input, the index of the first - * character to parse. On output, the index of the character after the - * last parsed character. - */ - static int32_t parseInteger(const UnicodeString& rule, int32_t& pos, int32_t limit); - - /** - * Parse an integer at pos using only ASCII digits. - * Base 10 only. - * @param pos INPUT-OUTPUT parameter. On input, the index of the first - * character to parse. On output, the index of the character after the - * last parsed character. - */ - static int32_t parseAsciiInteger(const UnicodeString& str, int32_t& pos); - - /** - * Parse a Unicode identifier from the given string at the given - * position. Return the identifier, or an empty string if there - * is no identifier. - * @param str the string to parse - * @param pos INPUT-OUTPUT parameter. On INPUT, pos is the - * first character to examine. It must be less than str.length(), - * and it must not point to a whitespace character. That is, must - * have pos < str.length() and - * !UCharacter::isWhitespace(str.char32At(pos)). On - * OUTPUT, the position after the last parsed character. - * @return the Unicode identifier, or an empty string if there is - * no valid identifier at pos. - */ - static UnicodeString parseUnicodeIdentifier(const UnicodeString& str, int32_t& pos); - - /** - * Parse an unsigned 31-bit integer at the given offset. Use - * UCharacter.digit() to parse individual characters into digits. - * @param text the text to be parsed - * @param pos INPUT-OUTPUT parameter. On entry, pos is the - * offset within text at which to start parsing; it should point - * to a valid digit. On exit, pos is the offset after the last - * parsed character. If the parse failed, it will be unchanged on - * exit. Must be >= 0 on entry. - * @param radix the radix in which to parse; must be >= 2 and <= - * 36. - * @return a non-negative parsed number, or -1 upon parse failure. - * Parse fails if there are no digits, that is, if pos does not - * point to a valid digit on entry, or if the number to be parsed - * does not fit into a 31-bit unsigned integer. - */ - static int32_t parseNumber(const UnicodeString& text, - int32_t& pos, int8_t radix); - - static void appendToRule(UnicodeString& rule, - UChar32 c, - UBool isLiteral, - UBool escapeUnprintable, - UnicodeString& quoteBuf); - - static void appendToRule(UnicodeString& rule, - const UnicodeString& text, - UBool isLiteral, - UBool escapeUnprintable, - UnicodeString& quoteBuf); - - static void appendToRule(UnicodeString& rule, - const UnicodeMatcher* matcher, - UBool escapeUnprintable, - UnicodeString& quoteBuf); - -private: - // do not instantiate - ICU_Utility() = delete; -}; - -U_NAMESPACE_END - -#endif -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (c) 2001-2011, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + * Date Name Description + * 11/19/2001 aliu Creation. + ********************************************************************** + */ + +#ifndef ICU_UTIL_H +#define ICU_UTIL_H + +#include "charstr.h" +#include "unicode/unistr.h" +#include "unicode/uobject.h" +#include "unicode/utypes.h" +//-------------------------------------------------------------------- +// class ICU_Utility +// i18n utility functions, scoped into the class ICU_Utility. +//-------------------------------------------------------------------- + +U_NAMESPACE_BEGIN + +class UnicodeMatcher; + +class U_COMMON_API ICU_Utility /* not : public UObject because all methods are static */ { + public: + + /** + * Append a number to the given UnicodeString in the given radix. + * Standard digits '0'-'9' are used and letters 'A'-'Z' for + * radices 11 through 36. + * @param result the digits of the number are appended here + * @param n the number to be converted to digits; may be negative. + * If negative, a '-' is prepended to the digits. + * @param radix a radix from 2 to 36 inclusive. + * @param minDigits the minimum number of digits, not including + * any '-', to produce. Values less than 2 have no effect. One + * digit is always emitted regardless of this parameter. + * @return a reference to result + */ + static UnicodeString& appendNumber(UnicodeString& result, int32_t n, + 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. + * The tab, newline and linefeed characters are considered unprintable. + */ + static UBool isUnprintable(UChar32 c); + + /** + * @return true for control codes and for surrogate and noncharacter code points + */ + static UBool shouldAlwaysBeEscaped(UChar32 c); + + /** + * Escapes one unprintable code point using \uxxxx notation for U+0000 to + * U+FFFF and \Uxxxxxxxx for U+10000 and above. If the character is + * printable ASCII, then do nothing and return false. Otherwise, + * append the escaped notation and return true. + */ + static UBool escapeUnprintable(UnicodeString& result, UChar32 c); + + /** + * Escapes one code point using \uxxxx notation + * for U+0000 to U+FFFF and \Uxxxxxxxx for U+10000 and above. + * @return result + */ + static UnicodeString &escape(UnicodeString& result, UChar32 c); + + /** + * Returns the index of a character, ignoring quoted text. + * For example, in the string "abc'hide'h", the 'h' in "hide" will not be + * found by a search for 'h'. + * @param text text to be searched + * @param start the beginning index, inclusive; 0 <= start + * <= limit. + * @param limit the ending index, exclusive; start <= limit + * <= text.length(). + * @param c character to search for + * @return Offset of the first instance of c, or -1 if not found. + */ +//?FOR FUTURE USE. DISABLE FOR NOW for coverage reasons. +// static int32_t quotedIndexOf(const UnicodeString& text, +// int32_t start, int32_t limit, +// char16_t c); + + /** + * Skip over a sequence of zero or more white space characters at pos. + * @param advance if true, advance pos to the first non-white-space + * character at or after pos, or str.length(), if there is none. + * Otherwise leave pos unchanged. + * @return the index of the first non-white-space character at or + * after pos, or str.length(), if there is none. + */ + static int32_t skipWhitespace(const UnicodeString& str, int32_t& pos, + UBool advance = false); + + /** + * Skip over Pattern_White_Space in a Replaceable. + * Skipping may be done in the forward or + * reverse direction. In either case, the leftmost index will be + * inclusive, and the rightmost index will be exclusive. That is, + * given a range defined as [start, limit), the call + * skipWhitespace(text, start, limit) will advance start past leading + * whitespace, whereas the call skipWhitespace(text, limit, start), + * will back up limit past trailing whitespace. + * @param text the text to be analyzed + * @param pos either the start or limit of a range of 'text', to skip + * leading or trailing whitespace, respectively + * @param stop either the limit or start of a range of 'text', to skip + * leading or trailing whitespace, respectively + * @return the new start or limit, depending on what was passed in to + * 'pos' + */ +//?FOR FUTURE USE. DISABLE FOR NOW for coverage reasons. +//? static int32_t skipWhitespace(const Replaceable& text, +//? int32_t pos, int32_t stop); + + /** + * Parse a single non-whitespace character 'ch', optionally + * preceded by whitespace. + * @param id the string to be parsed + * @param pos INPUT-OUTPUT parameter. On input, pos[0] is the + * offset of the first character to be parsed. On output, pos[0] + * is the index after the last parsed character. If the parse + * fails, pos[0] will be unchanged. + * @param ch the non-whitespace character to be parsed. + * @return true if 'ch' is seen preceded by zero or more + * whitespace characters. + */ + static UBool parseChar(const UnicodeString& id, int32_t& pos, char16_t ch); + + /** + * Parse a pattern string starting at offset pos. Keywords are + * matched case-insensitively. Spaces may be skipped and may be + * optional or required. Integer values may be parsed, and if + * they are, they will be returned in the given array. If + * successful, the offset of the next non-space character is + * returned. On failure, -1 is returned. + * @param pattern must only contain lowercase characters, which + * will match their uppercase equivalents as well. A space + * character matches one or more required spaces. A '~' character + * matches zero or more optional spaces. A '#' character matches + * an integer and stores it in parsedInts, which the caller must + * ensure has enough capacity. + * @param parsedInts array to receive parsed integers. Caller + * must ensure that parsedInts.length is >= the number of '#' + * signs in 'pattern'. + * @return the position after the last character parsed, or -1 if + * the parse failed + */ + static int32_t parsePattern(const UnicodeString& rule, int32_t pos, int32_t limit, + const UnicodeString& pattern, int32_t* parsedInts); + + /** + * Parse a pattern string within the given Replaceable and a parsing + * pattern. Characters are matched literally and case-sensitively + * except for the following special characters: + * + * ~ zero or more Pattern_White_Space chars + * + * If end of pattern is reached with all matches along the way, + * pos is advanced to the first unparsed index and returned. + * Otherwise -1 is returned. + * @param pat pattern that controls parsing + * @param text text to be parsed, starting at index + * @param index offset to first character to parse + * @param limit offset after last character to parse + * @return index after last parsed character, or -1 on parse failure. + */ + static int32_t parsePattern(const UnicodeString& pat, + const Replaceable& text, + int32_t index, + int32_t limit); + + /** + * Parse an integer at pos, either of the form \d+ or of the form + * 0x[0-9A-Fa-f]+ or 0[0-7]+, that is, in standard decimal, hex, + * or octal format. + * @param pos INPUT-OUTPUT parameter. On input, the index of the first + * character to parse. On output, the index of the character after the + * last parsed character. + */ + static int32_t parseInteger(const UnicodeString& rule, int32_t& pos, int32_t limit); + + /** + * Parse an integer at pos using only ASCII digits. + * Base 10 only. + * @param pos INPUT-OUTPUT parameter. On input, the index of the first + * character to parse. On output, the index of the character after the + * last parsed character. + */ + static int32_t parseAsciiInteger(const UnicodeString& str, int32_t& pos); + + /** + * Parse a Unicode identifier from the given string at the given + * position. Return the identifier, or an empty string if there + * is no identifier. + * @param str the string to parse + * @param pos INPUT-OUTPUT parameter. On INPUT, pos is the + * first character to examine. It must be less than str.length(), + * and it must not point to a whitespace character. That is, must + * have pos < str.length() and + * !UCharacter::isWhitespace(str.char32At(pos)). On + * OUTPUT, the position after the last parsed character. + * @return the Unicode identifier, or an empty string if there is + * no valid identifier at pos. + */ + static UnicodeString parseUnicodeIdentifier(const UnicodeString& str, int32_t& pos); + + /** + * Parse an unsigned 31-bit integer at the given offset. Use + * UCharacter.digit() to parse individual characters into digits. + * @param text the text to be parsed + * @param pos INPUT-OUTPUT parameter. On entry, pos is the + * offset within text at which to start parsing; it should point + * to a valid digit. On exit, pos is the offset after the last + * parsed character. If the parse failed, it will be unchanged on + * exit. Must be >= 0 on entry. + * @param radix the radix in which to parse; must be >= 2 and <= + * 36. + * @return a non-negative parsed number, or -1 upon parse failure. + * Parse fails if there are no digits, that is, if pos does not + * point to a valid digit on entry, or if the number to be parsed + * does not fit into a 31-bit unsigned integer. + */ + static int32_t parseNumber(const UnicodeString& text, + int32_t& pos, int8_t radix); + + static void appendToRule(UnicodeString& rule, + UChar32 c, + UBool isLiteral, + UBool escapeUnprintable, + UnicodeString& quoteBuf); + + static void appendToRule(UnicodeString& rule, + const UnicodeString& text, + UBool isLiteral, + UBool escapeUnprintable, + UnicodeString& quoteBuf); + + static void appendToRule(UnicodeString& rule, + const UnicodeMatcher* matcher, + UBool escapeUnprintable, + UnicodeString& quoteBuf); + +private: + // do not instantiate + ICU_Utility() = delete; +}; + +U_NAMESPACE_END + +#endif +//eof diff --git a/deps/icu-small/source/common/util_props.cpp b/deps/icu-small/source/common/util_props.cpp index d6ae052214cc13..7106bc2e687e5b 100644 --- a/deps/icu-small/source/common/util_props.cpp +++ b/deps/icu-small/source/common/util_props.cpp @@ -1,217 +1,217 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2001-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/19/2001 aliu Creation. -********************************************************************** -*/ - -#include "unicode/uchar.h" -#include "unicode/utf16.h" -#include "patternprops.h" -#include "util.h" - -U_NAMESPACE_BEGIN - -/** - * Parse an integer at pos, either of the form \d+ or of the form - * 0x[0-9A-Fa-f]+ or 0[0-7]+, that is, in standard decimal, hex, - * or octal format. - * @param pos INPUT-OUTPUT parameter. On input, the first - * character to parse. On output, the character after the last - * parsed character. - */ -int32_t ICU_Utility::parseInteger(const UnicodeString& rule, int32_t& pos, int32_t limit) { - int32_t count = 0; - int32_t value = 0; - int32_t p = pos; - int8_t radix = 10; - - if (p < limit && rule.charAt(p) == 48 /*0*/) { - if (p+1 < limit && (rule.charAt(p+1) == 0x78 /*x*/ || rule.charAt(p+1) == 0x58 /*X*/)) { - p += 2; - radix = 16; - } - else { - p++; - count = 1; - radix = 8; - } - } - - while (p < limit) { - int32_t d = u_digit(rule.charAt(p++), radix); - if (d < 0) { - --p; - break; - } - ++count; - int32_t v = (value * radix) + d; - if (v <= value) { - // If there are too many input digits, at some point - // the value will go negative, e.g., if we have seen - // "0x8000000" already and there is another '0', when - // we parse the next 0 the value will go negative. - return 0; - } - value = v; - } - if (count > 0) { - pos = p; - } - return value; -} - -/** - * Parse a pattern string starting at offset pos. Keywords are - * matched case-insensitively. Spaces may be skipped and may be - * optional or required. Integer values may be parsed, and if - * they are, they will be returned in the given array. If - * successful, the offset of the next non-space character is - * returned. On failure, -1 is returned. - * @param pattern must only contain lowercase characters, which - * will match their uppercase equivalents as well. A space - * character matches one or more required spaces. A '~' character - * matches zero or more optional spaces. A '#' character matches - * an integer and stores it in parsedInts, which the caller must - * ensure has enough capacity. - * @param parsedInts array to receive parsed integers. Caller - * must ensure that parsedInts.length is >= the number of '#' - * signs in 'pattern'. - * @return the position after the last character parsed, or -1 if - * the parse failed - */ -int32_t ICU_Utility::parsePattern(const UnicodeString& rule, int32_t pos, int32_t limit, - const UnicodeString& pattern, int32_t* parsedInts) { - // TODO Update this to handle surrogates - int32_t p; - int32_t intCount = 0; // number of integers parsed - for (int32_t i=0; i= limit) { - return -1; - } - c = rule.charAt(pos++); - if (!PatternProps::isWhiteSpace(c)) { - return -1; - } - // FALL THROUGH to skipWhitespace - U_FALLTHROUGH; - case 126 /*'~'*/: - pos = skipWhitespace(rule, pos); - break; - case 35 /*'#'*/: - p = pos; - parsedInts[intCount++] = parseInteger(rule, p, limit); - if (p == pos) { - // Syntax error; failed to parse integer - return -1; - } - pos = p; - break; - default: - if (pos >= limit) { - return -1; - } - c = (UChar) u_tolower(rule.charAt(pos++)); - if (c != cpat) { - return -1; - } - break; - } - } - return pos; -} - -/** - * Parse a Unicode identifier from the given string at the given - * position. Return the identifier, or an empty string if there - * is no identifier. - * @param str the string to parse - * @param pos INPUT-OUTPUT parameter. On INPUT, pos is the - * first character to examine. It must be less than str.length(), - * and it must not point to a whitespace character. That is, must - * have pos < str.length(). On - * OUTPUT, the position after the last parsed character. - * @return the Unicode identifier, or an empty string if there is - * no valid identifier at pos. - */ -UnicodeString ICU_Utility::parseUnicodeIdentifier(const UnicodeString& str, int32_t& pos) { - // assert(pos < str.length()); - UnicodeString buf; - int p = pos; - while (p < str.length()) { - UChar32 ch = str.char32At(p); - if (buf.length() == 0) { - if (u_isIDStart(ch)) { - buf.append(ch); - } else { - buf.truncate(0); - return buf; - } - } else { - if (u_isIDPart(ch)) { - buf.append(ch); - } else { - break; - } - } - p += U16_LENGTH(ch); - } - pos = p; - return buf; -} - -/** - * Parse an unsigned 31-bit integer at the given offset. Use - * UCharacter.digit() to parse individual characters into digits. - * @param text the text to be parsed - * @param pos INPUT-OUTPUT parameter. On entry, pos[0] is the - * offset within text at which to start parsing; it should point - * to a valid digit. On exit, pos[0] is the offset after the last - * parsed character. If the parse failed, it will be unchanged on - * exit. Must be >= 0 on entry. - * @param radix the radix in which to parse; must be >= 2 and <= - * 36. - * @return a non-negative parsed number, or -1 upon parse failure. - * Parse fails if there are no digits, that is, if pos[0] does not - * point to a valid digit on entry, or if the number to be parsed - * does not fit into a 31-bit unsigned integer. - */ -int32_t ICU_Utility::parseNumber(const UnicodeString& text, - int32_t& pos, int8_t radix) { - // assert(pos[0] >= 0); - // assert(radix >= 2); - // assert(radix <= 36); - int32_t n = 0; - int32_t p = pos; - while (p < text.length()) { - UChar32 ch = text.char32At(p); - int32_t d = u_digit(ch, radix); - if (d < 0) { - break; - } - n = radix*n + d; - // ASSUME that when a 32-bit integer overflows it becomes - // negative. E.g., 214748364 * 10 + 8 => negative value. - if (n < 0) { - return -1; - } - ++p; - } - if (p == pos) { - return -1; - } - pos = p; - return n; -} - -U_NAMESPACE_END - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2001-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/19/2001 aliu Creation. +********************************************************************** +*/ + +#include "unicode/uchar.h" +#include "unicode/utf16.h" +#include "patternprops.h" +#include "util.h" + +U_NAMESPACE_BEGIN + +/** + * Parse an integer at pos, either of the form \d+ or of the form + * 0x[0-9A-Fa-f]+ or 0[0-7]+, that is, in standard decimal, hex, + * or octal format. + * @param pos INPUT-OUTPUT parameter. On input, the first + * character to parse. On output, the character after the last + * parsed character. + */ +int32_t ICU_Utility::parseInteger(const UnicodeString& rule, int32_t& pos, int32_t limit) { + int32_t count = 0; + int32_t value = 0; + int32_t p = pos; + int8_t radix = 10; + + if (p < limit && rule.charAt(p) == 48 /*0*/) { + if (p+1 < limit && (rule.charAt(p+1) == 0x78 /*x*/ || rule.charAt(p+1) == 0x58 /*X*/)) { + p += 2; + radix = 16; + } + else { + p++; + count = 1; + radix = 8; + } + } + + while (p < limit) { + int32_t d = u_digit(rule.charAt(p++), radix); + if (d < 0) { + --p; + break; + } + ++count; + int32_t v = (value * radix) + d; + if (v <= value) { + // If there are too many input digits, at some point + // the value will go negative, e.g., if we have seen + // "0x8000000" already and there is another '0', when + // we parse the next 0 the value will go negative. + return 0; + } + value = v; + } + if (count > 0) { + pos = p; + } + return value; +} + +/** + * Parse a pattern string starting at offset pos. Keywords are + * matched case-insensitively. Spaces may be skipped and may be + * optional or required. Integer values may be parsed, and if + * they are, they will be returned in the given array. If + * successful, the offset of the next non-space character is + * returned. On failure, -1 is returned. + * @param pattern must only contain lowercase characters, which + * will match their uppercase equivalents as well. A space + * character matches one or more required spaces. A '~' character + * matches zero or more optional spaces. A '#' character matches + * an integer and stores it in parsedInts, which the caller must + * ensure has enough capacity. + * @param parsedInts array to receive parsed integers. Caller + * must ensure that parsedInts.length is >= the number of '#' + * signs in 'pattern'. + * @return the position after the last character parsed, or -1 if + * the parse failed + */ +int32_t ICU_Utility::parsePattern(const UnicodeString& rule, int32_t pos, int32_t limit, + const UnicodeString& pattern, int32_t* parsedInts) { + // TODO Update this to handle surrogates + int32_t p; + int32_t intCount = 0; // number of integers parsed + for (int32_t i=0; i= limit) { + return -1; + } + c = rule.charAt(pos++); + if (!PatternProps::isWhiteSpace(c)) { + return -1; + } + // FALL THROUGH to skipWhitespace + U_FALLTHROUGH; + case 126 /*'~'*/: + pos = skipWhitespace(rule, pos); + break; + case 35 /*'#'*/: + p = pos; + parsedInts[intCount++] = parseInteger(rule, p, limit); + if (p == pos) { + // Syntax error; failed to parse integer + return -1; + } + pos = p; + break; + default: + if (pos >= limit) { + return -1; + } + c = (char16_t) u_tolower(rule.charAt(pos++)); + if (c != cpat) { + return -1; + } + break; + } + } + return pos; +} + +/** + * Parse a Unicode identifier from the given string at the given + * position. Return the identifier, or an empty string if there + * is no identifier. + * @param str the string to parse + * @param pos INPUT-OUTPUT parameter. On INPUT, pos is the + * first character to examine. It must be less than str.length(), + * and it must not point to a whitespace character. That is, must + * have pos < str.length(). On + * OUTPUT, the position after the last parsed character. + * @return the Unicode identifier, or an empty string if there is + * no valid identifier at pos. + */ +UnicodeString ICU_Utility::parseUnicodeIdentifier(const UnicodeString& str, int32_t& pos) { + // assert(pos < str.length()); + UnicodeString buf; + int p = pos; + while (p < str.length()) { + UChar32 ch = str.char32At(p); + if (buf.length() == 0) { + if (u_isIDStart(ch)) { + buf.append(ch); + } else { + buf.truncate(0); + return buf; + } + } else { + if (u_isIDPart(ch)) { + buf.append(ch); + } else { + break; + } + } + p += U16_LENGTH(ch); + } + pos = p; + return buf; +} + +/** + * Parse an unsigned 31-bit integer at the given offset. Use + * UCharacter.digit() to parse individual characters into digits. + * @param text the text to be parsed + * @param pos INPUT-OUTPUT parameter. On entry, pos[0] is the + * offset within text at which to start parsing; it should point + * to a valid digit. On exit, pos[0] is the offset after the last + * parsed character. If the parse failed, it will be unchanged on + * exit. Must be >= 0 on entry. + * @param radix the radix in which to parse; must be >= 2 and <= + * 36. + * @return a non-negative parsed number, or -1 upon parse failure. + * Parse fails if there are no digits, that is, if pos[0] does not + * point to a valid digit on entry, or if the number to be parsed + * does not fit into a 31-bit unsigned integer. + */ +int32_t ICU_Utility::parseNumber(const UnicodeString& text, + int32_t& pos, int8_t radix) { + // assert(pos[0] >= 0); + // assert(radix >= 2); + // assert(radix <= 36); + int32_t n = 0; + int32_t p = pos; + while (p < text.length()) { + UChar32 ch = text.char32At(p); + int32_t d = u_digit(ch, radix); + if (d < 0) { + break; + } + n = radix*n + d; + // ASSUME that when a 32-bit integer overflows it becomes + // negative. E.g., 214748364 * 10 + 8 => negative value. + if (n < 0) { + return -1; + } + ++p; + } + if (p == pos) { + return -1; + } + pos = p; + return n; +} + +U_NAMESPACE_END + diff --git a/deps/icu-small/source/common/utrace.cpp b/deps/icu-small/source/common/utrace.cpp index f7b8ade67431a6..20d093b5c10973 100644 --- a/deps/icu-small/source/common/utrace.cpp +++ b/deps/icu-small/source/common/utrace.cpp @@ -1,504 +1,504 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2003-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: utrace.c -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -*/ - -#include "unicode/utrace.h" -#include "utracimp.h" -#include "cstring.h" -#include "uassert.h" -#include "ucln_cmn.h" - - -static UTraceEntry *pTraceEntryFunc = NULL; -static UTraceExit *pTraceExitFunc = NULL; -static UTraceData *pTraceDataFunc = NULL; -static const void *gTraceContext = NULL; - -/** - * \var utrace_level - * Trace level variable. Negative for "off". - */ -static int32_t -utrace_level = UTRACE_ERROR; - -U_CAPI void U_EXPORT2 -utrace_entry(int32_t fnNumber) { - if (pTraceEntryFunc != NULL) { - (*pTraceEntryFunc)(gTraceContext, fnNumber); - } -} - - -static const char gExitFmt[] = "Returns."; -static const char gExitFmtValue[] = "Returns %d."; -static const char gExitFmtStatus[] = "Returns. Status = %d."; -static const char gExitFmtValueStatus[] = "Returns %d. Status = %d."; -static const char gExitFmtPtrStatus[] = "Returns %d. Status = %p."; - -U_CAPI void U_EXPORT2 -utrace_exit(int32_t fnNumber, int32_t returnType, ...) { - if (pTraceExitFunc != NULL) { - va_list args; - const char *fmt; - - switch (returnType) { - case 0: - fmt = gExitFmt; - break; - case UTRACE_EXITV_I32: - fmt = gExitFmtValue; - break; - case UTRACE_EXITV_STATUS: - fmt = gExitFmtStatus; - break; - case UTRACE_EXITV_I32 | UTRACE_EXITV_STATUS: - fmt = gExitFmtValueStatus; - break; - case UTRACE_EXITV_PTR | UTRACE_EXITV_STATUS: - fmt = gExitFmtPtrStatus; - break; - default: - UPRV_UNREACHABLE_EXIT; - } - - va_start(args, returnType); - (*pTraceExitFunc)(gTraceContext, fnNumber, fmt, args); - va_end(args); - } -} - - - -U_CAPI void U_EXPORT2 -utrace_data(int32_t fnNumber, int32_t level, const char *fmt, ...) { - if (pTraceDataFunc != NULL) { - va_list args; - va_start(args, fmt ); - (*pTraceDataFunc)(gTraceContext, fnNumber, level, fmt, args); - va_end(args); - } -} - - -static void outputChar(char c, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) { - int32_t i; - /* Check whether a start of line indenting is needed. Three cases: - * 1. At the start of the first line (output index == 0). - * 2. At the start of subsequent lines (preceding char in buffer == '\n') - * 3. When preflighting buffer len (buffer capacity is exceeded), when - * a \n is output. Ideally we wouldn't do the indent until the following char - * is received, but that won't work because there's no place to remember that - * the preceding char was \n. Meaning that we may overstimate the - * buffer size needed. No harm done. - */ - if (*outIx==0 || /* case 1. */ - (c!='\n' && c!=0 && *outIx < capacity && outBuf[(*outIx)-1]=='\n') || /* case 2. */ - (c=='\n' && *outIx>=capacity)) /* case 3 */ - { - /* At the start of a line. Indent. */ - for(i=0; i= 0; shiftCount-=4) { - char c = gHexChars[(val >> shiftCount) & 0xf]; - outputChar(c, outBuf, outIx, capacity, 0); - } -} - -/* Output a pointer value in hex. Work with any size of pointer */ -static void outputPtrBytes(void *val, char *outBuf, int32_t *outIx, int32_t capacity) { - uint32_t i; - int32_t incVal = 1; /* +1 for big endian, -1 for little endian */ - char *p = (char *)&val; /* point to current byte to output in the ptr val */ - -#if !U_IS_BIG_ENDIAN - /* Little Endian. Move p to most significant end of the value */ - incVal = -1; - p += sizeof(void *) - 1; -#endif - - /* Loop through the bytes of the ptr as it sits in memory, from - * most significant to least significant end */ - for (i=0; i 0) { - outputHexBytes(longArg, charsToOutput, outBuf, &outIx, capacity); - outputChar(' ', outBuf, &outIx, capacity, indent); - } - if (vectorLen == -1 && longArg == 0) { - break; - } - } - } - outputChar('[', outBuf, &outIx, capacity, indent); - outputHexBytes(vectorLen, 8, outBuf, &outIx, capacity); - outputChar(']', outBuf, &outIx, capacity, indent); - } - break; - - - default: - /* %. in format string, where . is some character not in the set - * of recognized format chars. Just output it as if % wasn't there. - * (Covers "%%" outputting a single '%') - */ - outputChar(fmtC, outBuf, &outIx, capacity, indent); - } - } - outputChar(0, outBuf, &outIx, capacity, indent); /* Make sure that output is null terminated */ - return outIx + 1; /* outIx + 1 because outIx does not increment when outputting final null. */ -} - - - - -U_CAPI int32_t U_EXPORT2 -utrace_format(char *outBuf, int32_t capacity, - int32_t indent, const char *fmt, ...) { - int32_t retVal; - va_list args; - va_start(args, fmt ); - retVal = utrace_vformat(outBuf, capacity, indent, fmt, args); - va_end(args); - return retVal; -} - - -U_CAPI void U_EXPORT2 -utrace_setFunctions(const void *context, - UTraceEntry *e, UTraceExit *x, UTraceData *d) { - pTraceEntryFunc = e; - pTraceExitFunc = x; - pTraceDataFunc = d; - gTraceContext = context; -} - - -U_CAPI void U_EXPORT2 -utrace_getFunctions(const void **context, - UTraceEntry **e, UTraceExit **x, UTraceData **d) { - *e = pTraceEntryFunc; - *x = pTraceExitFunc; - *d = pTraceDataFunc; - *context = gTraceContext; -} - -U_CAPI void U_EXPORT2 -utrace_setLevel(int32_t level) { - if (level < UTRACE_OFF) { - level = UTRACE_OFF; - } - if (level > UTRACE_VERBOSE) { - level = UTRACE_VERBOSE; - } - utrace_level = level; -} - -U_CAPI int32_t U_EXPORT2 -utrace_getLevel() { - return utrace_level; -} - - -U_CFUNC UBool -utrace_cleanup() { - pTraceEntryFunc = NULL; - pTraceExitFunc = NULL; - pTraceDataFunc = NULL; - utrace_level = UTRACE_OFF; - gTraceContext = NULL; - return true; -} - - -static const char * const -trFnName[] = { - "u_init", - "u_cleanup", - NULL -}; - - -static const char * const -trConvNames[] = { - "ucnv_open", - "ucnv_openPackage", - "ucnv_openAlgorithmic", - "ucnv_clone", - "ucnv_close", - "ucnv_flushCache", - "ucnv_load", - "ucnv_unload", - NULL -}; - - -static const char * const -trCollNames[] = { - "ucol_open", - "ucol_close", - "ucol_strcoll", - "ucol_getSortKey", - "ucol_getLocale", - "ucol_nextSortKeyPart", - "ucol_strcollIter", - "ucol_openFromShortString", - "ucol_strcollUTF8", - NULL -}; - - -static const char* const -trResDataNames[] = { - "resc", - "bundle-open", - "file-open", - "res-open", - NULL -}; - - -U_CAPI const char * U_EXPORT2 -utrace_functionName(int32_t fnNumber) { - if(UTRACE_FUNCTION_START <= fnNumber && fnNumber < UTRACE_FUNCTION_LIMIT) { - return trFnName[fnNumber]; - } else if(UTRACE_CONVERSION_START <= fnNumber && fnNumber < UTRACE_CONVERSION_LIMIT) { - return trConvNames[fnNumber - UTRACE_CONVERSION_START]; - } else if(UTRACE_COLLATION_START <= fnNumber && fnNumber < UTRACE_COLLATION_LIMIT){ - return trCollNames[fnNumber - UTRACE_COLLATION_START]; - } else if(UTRACE_UDATA_START <= fnNumber && fnNumber < UTRACE_RES_DATA_LIMIT){ - return trResDataNames[fnNumber - UTRACE_UDATA_START]; - } else { - return "[BOGUS Trace Function Number]"; - } -} - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2003-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: utrace.c +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +*/ + +#include "unicode/utrace.h" +#include "utracimp.h" +#include "cstring.h" +#include "uassert.h" +#include "ucln_cmn.h" + + +static UTraceEntry *pTraceEntryFunc = nullptr; +static UTraceExit *pTraceExitFunc = nullptr; +static UTraceData *pTraceDataFunc = nullptr; +static const void *gTraceContext = nullptr; + +/** + * \var utrace_level + * Trace level variable. Negative for "off". + */ +static int32_t +utrace_level = UTRACE_ERROR; + +U_CAPI void U_EXPORT2 +utrace_entry(int32_t fnNumber) { + if (pTraceEntryFunc != nullptr) { + (*pTraceEntryFunc)(gTraceContext, fnNumber); + } +} + + +static const char gExitFmt[] = "Returns."; +static const char gExitFmtValue[] = "Returns %d."; +static const char gExitFmtStatus[] = "Returns. Status = %d."; +static const char gExitFmtValueStatus[] = "Returns %d. Status = %d."; +static const char gExitFmtPtrStatus[] = "Returns %d. Status = %p."; + +U_CAPI void U_EXPORT2 +utrace_exit(int32_t fnNumber, int32_t returnType, ...) { + if (pTraceExitFunc != nullptr) { + va_list args; + const char *fmt; + + switch (returnType) { + case 0: + fmt = gExitFmt; + break; + case UTRACE_EXITV_I32: + fmt = gExitFmtValue; + break; + case UTRACE_EXITV_STATUS: + fmt = gExitFmtStatus; + break; + case UTRACE_EXITV_I32 | UTRACE_EXITV_STATUS: + fmt = gExitFmtValueStatus; + break; + case UTRACE_EXITV_PTR | UTRACE_EXITV_STATUS: + fmt = gExitFmtPtrStatus; + break; + default: + UPRV_UNREACHABLE_EXIT; + } + + va_start(args, returnType); + (*pTraceExitFunc)(gTraceContext, fnNumber, fmt, args); + va_end(args); + } +} + + + +U_CAPI void U_EXPORT2 +utrace_data(int32_t fnNumber, int32_t level, const char *fmt, ...) { + if (pTraceDataFunc != nullptr) { + va_list args; + va_start(args, fmt ); + (*pTraceDataFunc)(gTraceContext, fnNumber, level, fmt, args); + va_end(args); + } +} + + +static void outputChar(char c, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) { + int32_t i; + /* Check whether a start of line indenting is needed. Three cases: + * 1. At the start of the first line (output index == 0). + * 2. At the start of subsequent lines (preceding char in buffer == '\n') + * 3. When preflighting buffer len (buffer capacity is exceeded), when + * a \n is output. Ideally we wouldn't do the indent until the following char + * is received, but that won't work because there's no place to remember that + * the preceding char was \n. Meaning that we may overstimate the + * buffer size needed. No harm done. + */ + if (*outIx==0 || /* case 1. */ + (c!='\n' && c!=0 && *outIx < capacity && outBuf[(*outIx)-1]=='\n') || /* case 2. */ + (c=='\n' && *outIx>=capacity)) /* case 3 */ + { + /* At the start of a line. Indent. */ + for(i=0; i= 0; shiftCount-=4) { + char c = gHexChars[(val >> shiftCount) & 0xf]; + outputChar(c, outBuf, outIx, capacity, 0); + } +} + +/* Output a pointer value in hex. Work with any size of pointer */ +static void outputPtrBytes(void *val, char *outBuf, int32_t *outIx, int32_t capacity) { + uint32_t i; + int32_t incVal = 1; /* +1 for big endian, -1 for little endian */ + char *p = (char *)&val; /* point to current byte to output in the ptr val */ + +#if !U_IS_BIG_ENDIAN + /* Little Endian. Move p to most significant end of the value */ + incVal = -1; + p += sizeof(void *) - 1; +#endif + + /* Loop through the bytes of the ptr as it sits in memory, from + * most significant to least significant end */ + for (i=0; i 0) { + outputHexBytes(longArg, charsToOutput, outBuf, &outIx, capacity); + outputChar(' ', outBuf, &outIx, capacity, indent); + } + if (vectorLen == -1 && longArg == 0) { + break; + } + } + } + outputChar('[', outBuf, &outIx, capacity, indent); + outputHexBytes(vectorLen, 8, outBuf, &outIx, capacity); + outputChar(']', outBuf, &outIx, capacity, indent); + } + break; + + + default: + /* %. in format string, where . is some character not in the set + * of recognized format chars. Just output it as if % wasn't there. + * (Covers "%%" outputting a single '%') + */ + outputChar(fmtC, outBuf, &outIx, capacity, indent); + } + } + outputChar(0, outBuf, &outIx, capacity, indent); /* Make sure that output is NUL terminated */ + return outIx + 1; /* outIx + 1 because outIx does not increment when outputting final NUL. */ +} + + + + +U_CAPI int32_t U_EXPORT2 +utrace_format(char *outBuf, int32_t capacity, + int32_t indent, const char *fmt, ...) { + int32_t retVal; + va_list args; + va_start(args, fmt ); + retVal = utrace_vformat(outBuf, capacity, indent, fmt, args); + va_end(args); + return retVal; +} + + +U_CAPI void U_EXPORT2 +utrace_setFunctions(const void *context, + UTraceEntry *e, UTraceExit *x, UTraceData *d) { + pTraceEntryFunc = e; + pTraceExitFunc = x; + pTraceDataFunc = d; + gTraceContext = context; +} + + +U_CAPI void U_EXPORT2 +utrace_getFunctions(const void **context, + UTraceEntry **e, UTraceExit **x, UTraceData **d) { + *e = pTraceEntryFunc; + *x = pTraceExitFunc; + *d = pTraceDataFunc; + *context = gTraceContext; +} + +U_CAPI void U_EXPORT2 +utrace_setLevel(int32_t level) { + if (level < UTRACE_OFF) { + level = UTRACE_OFF; + } + if (level > UTRACE_VERBOSE) { + level = UTRACE_VERBOSE; + } + utrace_level = level; +} + +U_CAPI int32_t U_EXPORT2 +utrace_getLevel() { + return utrace_level; +} + + +U_CFUNC UBool +utrace_cleanup() { + pTraceEntryFunc = nullptr; + pTraceExitFunc = nullptr; + pTraceDataFunc = nullptr; + utrace_level = UTRACE_OFF; + gTraceContext = nullptr; + return true; +} + + +static const char * const +trFnName[] = { + "u_init", + "u_cleanup", + nullptr +}; + + +static const char * const +trConvNames[] = { + "ucnv_open", + "ucnv_openPackage", + "ucnv_openAlgorithmic", + "ucnv_clone", + "ucnv_close", + "ucnv_flushCache", + "ucnv_load", + "ucnv_unload", + nullptr +}; + + +static const char * const +trCollNames[] = { + "ucol_open", + "ucol_close", + "ucol_strcoll", + "ucol_getSortKey", + "ucol_getLocale", + "ucol_nextSortKeyPart", + "ucol_strcollIter", + "ucol_openFromShortString", + "ucol_strcollUTF8", + nullptr +}; + + +static const char* const +trResDataNames[] = { + "resc", + "bundle-open", + "file-open", + "res-open", + nullptr +}; + + +U_CAPI const char * U_EXPORT2 +utrace_functionName(int32_t fnNumber) { + if(UTRACE_FUNCTION_START <= fnNumber && fnNumber < UTRACE_FUNCTION_LIMIT) { + return trFnName[fnNumber]; + } else if(UTRACE_CONVERSION_START <= fnNumber && fnNumber < UTRACE_CONVERSION_LIMIT) { + return trConvNames[fnNumber - UTRACE_CONVERSION_START]; + } else if(UTRACE_COLLATION_START <= fnNumber && fnNumber < UTRACE_COLLATION_LIMIT){ + return trCollNames[fnNumber - UTRACE_COLLATION_START]; + } else if(UTRACE_UDATA_START <= fnNumber && fnNumber < UTRACE_RES_DATA_LIMIT){ + return trResDataNames[fnNumber - UTRACE_UDATA_START]; + } else { + return "[BOGUS Trace Function Number]"; + } +} + diff --git a/deps/icu-small/source/common/utracimp.h b/deps/icu-small/source/common/utracimp.h index 945540d25af992..698022449f1aaa 100644 --- a/deps/icu-small/source/common/utracimp.h +++ b/deps/icu-small/source/common/utracimp.h @@ -1,391 +1,391 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2003-2009, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: utracimp.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2003aug06 -* created by: Markus W. Scherer -* -* Internal header for ICU tracing/logging. -* -* -* Various notes: -* - using a trace level variable to only call trace functions -* when the level is sufficient -* - using the same variable for tracing on/off to never make a function -* call when off -* - the function number is put into a local variable by the entry macro -* and used implicitly to avoid copy&paste/typing mistakes by the developer -* - the application must call utrace_setFunctions() and pass in -* implementations for the trace functions -* - ICU trace macros call ICU functions that route through the function -* pointers if they have been set; -* this avoids an indirection at the call site -* (which would cost more code for another check and for the indirection) -* -* ### TODO Issues: -* - Verify that va_list is portable among compilers for the same platform. -* va_list should be portable because printf() would fail otherwise! -* - Should enum values like UTraceLevel be passed into int32_t-type arguments, -* or should enum types be used? -*/ - -#ifndef __UTRACIMP_H__ -#define __UTRACIMP_H__ - -#include "unicode/utrace.h" -#include - -U_CDECL_BEGIN - -/** - * Traced Function Exit return types. - * Flags indicating the number and types of varargs included in a call - * to a UTraceExit function. - * Bits 0-3: The function return type. First variable param. - * Bit 4: Flag for presence of U_ErrorCode status param. - * @internal - */ -typedef enum UTraceExitVal { - /** The traced function returns no value @internal */ - UTRACE_EXITV_NONE = 0, - /** The traced function returns an int32_t, or compatible, type. @internal */ - UTRACE_EXITV_I32 = 1, - /** The traced function returns a pointer @internal */ - UTRACE_EXITV_PTR = 2, - /** The traced function returns a UBool @internal */ - UTRACE_EXITV_BOOL = 3, - /** Mask to extract the return type values from a UTraceExitVal @internal */ - UTRACE_EXITV_MASK = 0xf, - /** Bit indicating that the traced function includes a UErrorCode parameter @internal */ - UTRACE_EXITV_STATUS = 0x10 -} UTraceExitVal; - -/** - * Trace function for the entry point of a function. - * Do not use directly, use UTRACE_ENTRY instead. - * @param fnNumber The UTraceFunctionNumber for the current function. - * @internal - */ -U_CAPI void U_EXPORT2 -utrace_entry(int32_t fnNumber); - -/** - * Trace function for each exit point of a function. - * Do not use directly, use UTRACE_EXIT* instead. - * @param fnNumber The UTraceFunctionNumber for the current function. - * @param returnType The type of the value returned by the function. - * @param errorCode The UErrorCode value at function exit. See UTRACE_EXIT. - * @internal - */ -U_CAPI void U_EXPORT2 -utrace_exit(int32_t fnNumber, int32_t returnType, ...); - - -/** - * Trace function used inside functions that have a UTRACE_ENTRY() statement. - * Do not use directly, use UTRACE_DATAX() macros instead. - * - * @param utraceFnNumber The number of the current function, from the local - * variable of the same name. - * @param level The trace level for this message. - * @param fmt The trace format string. - * - * @internal - */ -U_CAPI void U_EXPORT2 -utrace_data(int32_t utraceFnNumber, int32_t level, const char *fmt, ...); - -U_CDECL_END - -#if U_ENABLE_TRACING - -/** - * Boolean expression to see if ICU tracing is turned on - * to at least the specified level. - * @internal - */ -#define UTRACE_LEVEL(level) (utrace_getLevel()>=(level)) - -/** - * Flag bit in utraceFnNumber, the local variable added to each function - * with tracing code to contains the function number. - * - * Set the flag if the function's entry is traced, which will cause the - * function's exit to also be traced. utraceFnNumber is uncoditionally - * set at entry, whether or not the entry is traced, so that it will - * always be available for error trace output. - * @internal - */ -#define UTRACE_TRACED_ENTRY 0x80000000 - -/** - * Trace statement for the entry point of a function. - * Stores the function number in a local variable. - * In C code, must be placed immediately after the last variable declaration. - * Must be matched with UTRACE_EXIT() at all function exit points. - * - * Tracing should start with UTRACE_ENTRY after checking for - * U_FAILURE at function entry, so that if a function returns immediately - * because of a pre-existing error condition, it does not show up in the trace, - * consistent with ICU's error handling model. - * - * @param fnNumber The UTraceFunctionNumber for the current function. - * @internal - */ -#define UTRACE_ENTRY(fnNumber) \ - int32_t utraceFnNumber=(fnNumber); \ -UPRV_BLOCK_MACRO_BEGIN { \ - if(utrace_getLevel()>=UTRACE_INFO) { \ - utrace_entry(fnNumber); \ - utraceFnNumber |= UTRACE_TRACED_ENTRY; \ - } \ -} UPRV_BLOCK_MACRO_END - - -/** - * Trace statement for the entry point of open and close functions. - * Produces trace output at a less verbose setting than plain UTRACE_ENTRY - * Stores the function number in a local variable. - * In C code, must be placed immediately after the last variable declaration. - * Must be matched with UTRACE_EXIT() at all function exit points. - * - * @param fnNumber The UTraceFunctionNumber for the current function. - * @internal - */ -#define UTRACE_ENTRY_OC(fnNumber) \ - int32_t utraceFnNumber=(fnNumber); \ -UPRV_BLOCK_MACRO_BEGIN { \ - if(utrace_getLevel()>=UTRACE_OPEN_CLOSE) { \ - utrace_entry(fnNumber); \ - utraceFnNumber |= UTRACE_TRACED_ENTRY; \ - } \ -} UPRV_BLOCK_MACRO_END - -/** - * Trace statement for each exit point of a function that has a UTRACE_ENTRY() - * statement. - * - * @param errorCode The function's ICU UErrorCode value at function exit, - * or U_ZERO_ERROR if the function does not use a UErrorCode. - * 0==U_ZERO_ERROR indicates success, - * positive values an error (see u_errorName()), - * negative values an informational status. - * - * @internal - */ -#define UTRACE_EXIT() UPRV_BLOCK_MACRO_BEGIN { \ - if(utraceFnNumber & UTRACE_TRACED_ENTRY) { \ - utrace_exit(utraceFnNumber & ~UTRACE_TRACED_ENTRY, UTRACE_EXITV_NONE); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** - * Trace statement for each exit point of a function that has a UTRACE_ENTRY() - * statement, and that returns a value. - * - * @param val The function's return value, int32_t or compatible type. - * - * @internal - */ -#define UTRACE_EXIT_VALUE(val) UPRV_BLOCK_MACRO_BEGIN { \ - if(utraceFnNumber & UTRACE_TRACED_ENTRY) { \ - utrace_exit(utraceFnNumber & ~UTRACE_TRACED_ENTRY, UTRACE_EXITV_I32, val); \ - } \ -} UPRV_BLOCK_MACRO_END - -#define UTRACE_EXIT_STATUS(status) UPRV_BLOCK_MACRO_BEGIN { \ - if(utraceFnNumber & UTRACE_TRACED_ENTRY) { \ - utrace_exit(utraceFnNumber & ~UTRACE_TRACED_ENTRY, UTRACE_EXITV_STATUS, status); \ - } \ -} UPRV_BLOCK_MACRO_END - -#define UTRACE_EXIT_VALUE_STATUS(val, status) UPRV_BLOCK_MACRO_BEGIN { \ - if(utraceFnNumber & UTRACE_TRACED_ENTRY) { \ - utrace_exit(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (UTRACE_EXITV_I32 | UTRACE_EXITV_STATUS), val, status); \ - } \ -} UPRV_BLOCK_MACRO_END - -#define UTRACE_EXIT_PTR_STATUS(ptr, status) UPRV_BLOCK_MACRO_BEGIN { \ - if(utraceFnNumber & UTRACE_TRACED_ENTRY) { \ - utrace_exit(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (UTRACE_EXITV_PTR | UTRACE_EXITV_STATUS), ptr, status); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** - * Trace statement used inside functions that have a UTRACE_ENTRY() statement. - * Takes no data arguments. - * The number of arguments for this macro must match the number of inserts - * in the format string. Vector inserts count as two arguments. - * Calls utrace_data() if the level is high enough. - * @internal - */ -#define UTRACE_DATA0(level, fmt) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTRACE_LEVEL(level)) { \ - utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt)); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** - * Trace statement used inside functions that have a UTRACE_ENTRY() statement. - * Takes one data argument. - * The number of arguments for this macro must match the number of inserts - * in the format string. Vector inserts count as two arguments. - * Calls utrace_data() if the level is high enough. - * @internal - */ -#define UTRACE_DATA1(level, fmt, a) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTRACE_LEVEL(level)) { \ - utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY , (level), (fmt), (a)); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** - * Trace statement used inside functions that have a UTRACE_ENTRY() statement. - * Takes two data arguments. - * The number of arguments for this macro must match the number of inserts - * in the format string. Vector inserts count as two arguments. - * Calls utrace_data() if the level is high enough. - * @internal - */ -#define UTRACE_DATA2(level, fmt, a, b) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTRACE_LEVEL(level)) { \ - utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY , (level), (fmt), (a), (b)); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** - * Trace statement used inside functions that have a UTRACE_ENTRY() statement. - * Takes three data arguments. - * The number of arguments for this macro must match the number of inserts - * in the format string. Vector inserts count as two arguments. - * Calls utrace_data() if the level is high enough. - * @internal - */ -#define UTRACE_DATA3(level, fmt, a, b, c) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTRACE_LEVEL(level)) { \ - utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c)); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** - * Trace statement used inside functions that have a UTRACE_ENTRY() statement. - * Takes four data arguments. - * The number of arguments for this macro must match the number of inserts - * in the format string. Vector inserts count as two arguments. - * Calls utrace_data() if the level is high enough. - * @internal - */ -#define UTRACE_DATA4(level, fmt, a, b, c, d) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTRACE_LEVEL(level)) { \ - utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c), (d)); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** - * Trace statement used inside functions that have a UTRACE_ENTRY() statement. - * Takes five data arguments. - * The number of arguments for this macro must match the number of inserts - * in the format string. Vector inserts count as two arguments. - * Calls utrace_data() if the level is high enough. - * @internal - */ -#define UTRACE_DATA5(level, fmt, a, b, c, d, e) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTRACE_LEVEL(level)) { \ - utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c), (d), (e)); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** - * Trace statement used inside functions that have a UTRACE_ENTRY() statement. - * Takes six data arguments. - * The number of arguments for this macro must match the number of inserts - * in the format string. Vector inserts count as two arguments. - * Calls utrace_data() if the level is high enough. - * @internal - */ -#define UTRACE_DATA6(level, fmt, a, b, c, d, e, f) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTRACE_LEVEL(level)) { \ - utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c), (d), (e), (f)); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** - * Trace statement used inside functions that have a UTRACE_ENTRY() statement. - * Takes seven data arguments. - * The number of arguments for this macro must match the number of inserts - * in the format string. Vector inserts count as two arguments. - * Calls utrace_data() if the level is high enough. - * @internal - */ -#define UTRACE_DATA7(level, fmt, a, b, c, d, e, f, g) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTRACE_LEVEL(level)) { \ - utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c), (d), (e), (f), (g)); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** - * Trace statement used inside functions that have a UTRACE_ENTRY() statement. - * Takes eight data arguments. - * The number of arguments for this macro must match the number of inserts - * in the format string. Vector inserts count as two arguments. - * Calls utrace_data() if the level is high enough. - * @internal - */ -#define UTRACE_DATA8(level, fmt, a, b, c, d, e, f, g, h) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTRACE_LEVEL(level)) { \ - utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c), (d), (e), (f), (g), (h)); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** - * Trace statement used inside functions that have a UTRACE_ENTRY() statement. - * Takes nine data arguments. - * The number of arguments for this macro must match the number of inserts - * in the format string. Vector inserts count as two arguments. - * Calls utrace_data() if the level is high enough. - * @internal - */ -#define UTRACE_DATA9(level, fmt, a, b, c, d, e, f, g, h, i) UPRV_BLOCK_MACRO_BEGIN { \ - if(UTRACE_LEVEL(level)) { \ - utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c), (d), (e), (f), (g), (h), (i)); \ - } \ -} UPRV_BLOCK_MACRO_END - -#else - -/* - * When tracing is disabled, the following macros become empty - */ - -#define UTRACE_LEVEL(level) 0 -#define UTRACE_ENTRY(fnNumber) -#define UTRACE_ENTRY_OC(fnNumber) -#define UTRACE_EXIT() -#define UTRACE_EXIT_VALUE(val) -#define UTRACE_EXIT_STATUS(status) -#define UTRACE_EXIT_VALUE_STATUS(val, status) -#define UTRACE_EXIT_PTR_STATUS(ptr, status) -#define UTRACE_DATA0(level, fmt) -#define UTRACE_DATA1(level, fmt, a) -#define UTRACE_DATA2(level, fmt, a, b) -#define UTRACE_DATA3(level, fmt, a, b, c) -#define UTRACE_DATA4(level, fmt, a, b, c, d) -#define UTRACE_DATA5(level, fmt, a, b, c, d, e) -#define UTRACE_DATA6(level, fmt, a, b, c, d, e, f) -#define UTRACE_DATA7(level, fmt, a, b, c, d, e, f, g) -#define UTRACE_DATA8(level, fmt, a, b, c, d, e, f, g, h) -#define UTRACE_DATA9(level, fmt, a, b, c, d, e, f, g, h, i) - -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2003-2009, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: utracimp.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2003aug06 +* created by: Markus W. Scherer +* +* Internal header for ICU tracing/logging. +* +* +* Various notes: +* - using a trace level variable to only call trace functions +* when the level is sufficient +* - using the same variable for tracing on/off to never make a function +* call when off +* - the function number is put into a local variable by the entry macro +* and used implicitly to avoid copy&paste/typing mistakes by the developer +* - the application must call utrace_setFunctions() and pass in +* implementations for the trace functions +* - ICU trace macros call ICU functions that route through the function +* pointers if they have been set; +* this avoids an indirection at the call site +* (which would cost more code for another check and for the indirection) +* +* ### TODO Issues: +* - Verify that va_list is portable among compilers for the same platform. +* va_list should be portable because printf() would fail otherwise! +* - Should enum values like UTraceLevel be passed into int32_t-type arguments, +* or should enum types be used? +*/ + +#ifndef __UTRACIMP_H__ +#define __UTRACIMP_H__ + +#include "unicode/utrace.h" +#include + +U_CDECL_BEGIN + +/** + * Traced Function Exit return types. + * Flags indicating the number and types of varargs included in a call + * to a UTraceExit function. + * Bits 0-3: The function return type. First variable param. + * Bit 4: Flag for presence of U_ErrorCode status param. + * @internal + */ +typedef enum UTraceExitVal { + /** The traced function returns no value @internal */ + UTRACE_EXITV_NONE = 0, + /** The traced function returns an int32_t, or compatible, type. @internal */ + UTRACE_EXITV_I32 = 1, + /** The traced function returns a pointer @internal */ + UTRACE_EXITV_PTR = 2, + /** The traced function returns a UBool @internal */ + UTRACE_EXITV_BOOL = 3, + /** Mask to extract the return type values from a UTraceExitVal @internal */ + UTRACE_EXITV_MASK = 0xf, + /** Bit indicating that the traced function includes a UErrorCode parameter @internal */ + UTRACE_EXITV_STATUS = 0x10 +} UTraceExitVal; + +/** + * Trace function for the entry point of a function. + * Do not use directly, use UTRACE_ENTRY instead. + * @param fnNumber The UTraceFunctionNumber for the current function. + * @internal + */ +U_CAPI void U_EXPORT2 +utrace_entry(int32_t fnNumber); + +/** + * Trace function for each exit point of a function. + * Do not use directly, use UTRACE_EXIT* instead. + * @param fnNumber The UTraceFunctionNumber for the current function. + * @param returnType The type of the value returned by the function. + * @param errorCode The UErrorCode value at function exit. See UTRACE_EXIT. + * @internal + */ +U_CAPI void U_EXPORT2 +utrace_exit(int32_t fnNumber, int32_t returnType, ...); + + +/** + * Trace function used inside functions that have a UTRACE_ENTRY() statement. + * Do not use directly, use UTRACE_DATAX() macros instead. + * + * @param utraceFnNumber The number of the current function, from the local + * variable of the same name. + * @param level The trace level for this message. + * @param fmt The trace format string. + * + * @internal + */ +U_CAPI void U_EXPORT2 +utrace_data(int32_t utraceFnNumber, int32_t level, const char *fmt, ...); + +U_CDECL_END + +#if U_ENABLE_TRACING + +/** + * Boolean expression to see if ICU tracing is turned on + * to at least the specified level. + * @internal + */ +#define UTRACE_LEVEL(level) (utrace_getLevel()>=(level)) + +/** + * Flag bit in utraceFnNumber, the local variable added to each function + * with tracing code to contains the function number. + * + * Set the flag if the function's entry is traced, which will cause the + * function's exit to also be traced. utraceFnNumber is uncoditionally + * set at entry, whether or not the entry is traced, so that it will + * always be available for error trace output. + * @internal + */ +#define UTRACE_TRACED_ENTRY 0x80000000 + +/** + * Trace statement for the entry point of a function. + * Stores the function number in a local variable. + * In C code, must be placed immediately after the last variable declaration. + * Must be matched with UTRACE_EXIT() at all function exit points. + * + * Tracing should start with UTRACE_ENTRY after checking for + * U_FAILURE at function entry, so that if a function returns immediately + * because of a pre-existing error condition, it does not show up in the trace, + * consistent with ICU's error handling model. + * + * @param fnNumber The UTraceFunctionNumber for the current function. + * @internal + */ +#define UTRACE_ENTRY(fnNumber) \ + int32_t utraceFnNumber=(fnNumber); \ +UPRV_BLOCK_MACRO_BEGIN { \ + if(utrace_getLevel()>=UTRACE_INFO) { \ + utrace_entry(fnNumber); \ + utraceFnNumber |= UTRACE_TRACED_ENTRY; \ + } \ +} UPRV_BLOCK_MACRO_END + + +/** + * Trace statement for the entry point of open and close functions. + * Produces trace output at a less verbose setting than plain UTRACE_ENTRY + * Stores the function number in a local variable. + * In C code, must be placed immediately after the last variable declaration. + * Must be matched with UTRACE_EXIT() at all function exit points. + * + * @param fnNumber The UTraceFunctionNumber for the current function. + * @internal + */ +#define UTRACE_ENTRY_OC(fnNumber) \ + int32_t utraceFnNumber=(fnNumber); \ +UPRV_BLOCK_MACRO_BEGIN { \ + if(utrace_getLevel()>=UTRACE_OPEN_CLOSE) { \ + utrace_entry(fnNumber); \ + utraceFnNumber |= UTRACE_TRACED_ENTRY; \ + } \ +} UPRV_BLOCK_MACRO_END + +/** + * Trace statement for each exit point of a function that has a UTRACE_ENTRY() + * statement. + * + * @param errorCode The function's ICU UErrorCode value at function exit, + * or U_ZERO_ERROR if the function does not use a UErrorCode. + * 0==U_ZERO_ERROR indicates success, + * positive values an error (see u_errorName()), + * negative values an informational status. + * + * @internal + */ +#define UTRACE_EXIT() UPRV_BLOCK_MACRO_BEGIN { \ + if(utraceFnNumber & UTRACE_TRACED_ENTRY) { \ + utrace_exit(utraceFnNumber & ~UTRACE_TRACED_ENTRY, UTRACE_EXITV_NONE); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** + * Trace statement for each exit point of a function that has a UTRACE_ENTRY() + * statement, and that returns a value. + * + * @param val The function's return value, int32_t or compatible type. + * + * @internal + */ +#define UTRACE_EXIT_VALUE(val) UPRV_BLOCK_MACRO_BEGIN { \ + if(utraceFnNumber & UTRACE_TRACED_ENTRY) { \ + utrace_exit(utraceFnNumber & ~UTRACE_TRACED_ENTRY, UTRACE_EXITV_I32, val); \ + } \ +} UPRV_BLOCK_MACRO_END + +#define UTRACE_EXIT_STATUS(status) UPRV_BLOCK_MACRO_BEGIN { \ + if(utraceFnNumber & UTRACE_TRACED_ENTRY) { \ + utrace_exit(utraceFnNumber & ~UTRACE_TRACED_ENTRY, UTRACE_EXITV_STATUS, status); \ + } \ +} UPRV_BLOCK_MACRO_END + +#define UTRACE_EXIT_VALUE_STATUS(val, status) UPRV_BLOCK_MACRO_BEGIN { \ + if(utraceFnNumber & UTRACE_TRACED_ENTRY) { \ + utrace_exit(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (UTRACE_EXITV_I32 | UTRACE_EXITV_STATUS), val, status); \ + } \ +} UPRV_BLOCK_MACRO_END + +#define UTRACE_EXIT_PTR_STATUS(ptr, status) UPRV_BLOCK_MACRO_BEGIN { \ + if(utraceFnNumber & UTRACE_TRACED_ENTRY) { \ + utrace_exit(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (UTRACE_EXITV_PTR | UTRACE_EXITV_STATUS), ptr, status); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** + * Trace statement used inside functions that have a UTRACE_ENTRY() statement. + * Takes no data arguments. + * The number of arguments for this macro must match the number of inserts + * in the format string. Vector inserts count as two arguments. + * Calls utrace_data() if the level is high enough. + * @internal + */ +#define UTRACE_DATA0(level, fmt) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTRACE_LEVEL(level)) { \ + utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt)); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** + * Trace statement used inside functions that have a UTRACE_ENTRY() statement. + * Takes one data argument. + * The number of arguments for this macro must match the number of inserts + * in the format string. Vector inserts count as two arguments. + * Calls utrace_data() if the level is high enough. + * @internal + */ +#define UTRACE_DATA1(level, fmt, a) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTRACE_LEVEL(level)) { \ + utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY , (level), (fmt), (a)); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** + * Trace statement used inside functions that have a UTRACE_ENTRY() statement. + * Takes two data arguments. + * The number of arguments for this macro must match the number of inserts + * in the format string. Vector inserts count as two arguments. + * Calls utrace_data() if the level is high enough. + * @internal + */ +#define UTRACE_DATA2(level, fmt, a, b) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTRACE_LEVEL(level)) { \ + utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY , (level), (fmt), (a), (b)); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** + * Trace statement used inside functions that have a UTRACE_ENTRY() statement. + * Takes three data arguments. + * The number of arguments for this macro must match the number of inserts + * in the format string. Vector inserts count as two arguments. + * Calls utrace_data() if the level is high enough. + * @internal + */ +#define UTRACE_DATA3(level, fmt, a, b, c) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTRACE_LEVEL(level)) { \ + utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c)); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** + * Trace statement used inside functions that have a UTRACE_ENTRY() statement. + * Takes four data arguments. + * The number of arguments for this macro must match the number of inserts + * in the format string. Vector inserts count as two arguments. + * Calls utrace_data() if the level is high enough. + * @internal + */ +#define UTRACE_DATA4(level, fmt, a, b, c, d) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTRACE_LEVEL(level)) { \ + utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c), (d)); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** + * Trace statement used inside functions that have a UTRACE_ENTRY() statement. + * Takes five data arguments. + * The number of arguments for this macro must match the number of inserts + * in the format string. Vector inserts count as two arguments. + * Calls utrace_data() if the level is high enough. + * @internal + */ +#define UTRACE_DATA5(level, fmt, a, b, c, d, e) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTRACE_LEVEL(level)) { \ + utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c), (d), (e)); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** + * Trace statement used inside functions that have a UTRACE_ENTRY() statement. + * Takes six data arguments. + * The number of arguments for this macro must match the number of inserts + * in the format string. Vector inserts count as two arguments. + * Calls utrace_data() if the level is high enough. + * @internal + */ +#define UTRACE_DATA6(level, fmt, a, b, c, d, e, f) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTRACE_LEVEL(level)) { \ + utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c), (d), (e), (f)); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** + * Trace statement used inside functions that have a UTRACE_ENTRY() statement. + * Takes seven data arguments. + * The number of arguments for this macro must match the number of inserts + * in the format string. Vector inserts count as two arguments. + * Calls utrace_data() if the level is high enough. + * @internal + */ +#define UTRACE_DATA7(level, fmt, a, b, c, d, e, f, g) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTRACE_LEVEL(level)) { \ + utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c), (d), (e), (f), (g)); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** + * Trace statement used inside functions that have a UTRACE_ENTRY() statement. + * Takes eight data arguments. + * The number of arguments for this macro must match the number of inserts + * in the format string. Vector inserts count as two arguments. + * Calls utrace_data() if the level is high enough. + * @internal + */ +#define UTRACE_DATA8(level, fmt, a, b, c, d, e, f, g, h) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTRACE_LEVEL(level)) { \ + utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c), (d), (e), (f), (g), (h)); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** + * Trace statement used inside functions that have a UTRACE_ENTRY() statement. + * Takes nine data arguments. + * The number of arguments for this macro must match the number of inserts + * in the format string. Vector inserts count as two arguments. + * Calls utrace_data() if the level is high enough. + * @internal + */ +#define UTRACE_DATA9(level, fmt, a, b, c, d, e, f, g, h, i) UPRV_BLOCK_MACRO_BEGIN { \ + if(UTRACE_LEVEL(level)) { \ + utrace_data(utraceFnNumber & ~UTRACE_TRACED_ENTRY, (level), (fmt), (a), (b), (c), (d), (e), (f), (g), (h), (i)); \ + } \ +} UPRV_BLOCK_MACRO_END + +#else + +/* + * When tracing is disabled, the following macros become empty + */ + +#define UTRACE_LEVEL(level) 0 +#define UTRACE_ENTRY(fnNumber) +#define UTRACE_ENTRY_OC(fnNumber) +#define UTRACE_EXIT() +#define UTRACE_EXIT_VALUE(val) +#define UTRACE_EXIT_STATUS(status) +#define UTRACE_EXIT_VALUE_STATUS(val, status) +#define UTRACE_EXIT_PTR_STATUS(ptr, status) +#define UTRACE_DATA0(level, fmt) +#define UTRACE_DATA1(level, fmt, a) +#define UTRACE_DATA2(level, fmt, a, b) +#define UTRACE_DATA3(level, fmt, a, b, c) +#define UTRACE_DATA4(level, fmt, a, b, c, d) +#define UTRACE_DATA5(level, fmt, a, b, c, d, e) +#define UTRACE_DATA6(level, fmt, a, b, c, d, e, f) +#define UTRACE_DATA7(level, fmt, a, b, c, d, e, f, g) +#define UTRACE_DATA8(level, fmt, a, b, c, d, e, f, g, h) +#define UTRACE_DATA9(level, fmt, a, b, c, d, e, f, g, h, i) + +#endif + +#endif diff --git a/deps/icu-small/source/common/utrie.cpp b/deps/icu-small/source/common/utrie.cpp index 96f2397ca13a66..3cc78ca8ade185 100644 --- a/deps/icu-small/source/common/utrie.cpp +++ b/deps/icu-small/source/common/utrie.cpp @@ -1,1234 +1,1234 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2001-2012, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: utrie.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2001oct20 -* created by: Markus W. Scherer -* -* This is a common implementation of a "folded" trie. -* It is a kind of compressed, serializable table of 16- or 32-bit values associated with -* Unicode code points (0..0x10ffff). -*/ - -#ifdef UTRIE_DEBUG -# include -#endif - -#include "unicode/utypes.h" -#include "cmemory.h" -#include "utrie.h" - -/* miscellaneous ------------------------------------------------------------ */ - -#undef ABS -#define ABS(x) ((x)>=0 ? (x) : -(x)) - -static inline UBool -equal_uint32(const uint32_t *s, const uint32_t *t, int32_t length) { - while(length>0 && *s==*t) { - ++s; - ++t; - --length; - } - return (UBool)(length==0); -} - -/* Building a trie ----------------------------------------------------------*/ - -U_CAPI UNewTrie * U_EXPORT2 -utrie_open(UNewTrie *fillIn, - uint32_t *aliasData, int32_t maxDataLength, - uint32_t initialValue, uint32_t leadUnitValue, - UBool latin1Linear) { - UNewTrie *trie; - int32_t i, j; - - if( maxDataLengthisAllocated= (UBool)(fillIn==NULL); - - if(aliasData!=NULL) { - trie->data=aliasData; - trie->isDataAllocated=false; - } else { - trie->data=(uint32_t *)uprv_malloc(maxDataLength*4); - if(trie->data==NULL) { - uprv_free(trie); - return NULL; - } - trie->isDataAllocated=true; - } - - /* preallocate and reset the first data block (block index 0) */ - j=UTRIE_DATA_BLOCK_LENGTH; - - if(latin1Linear) { - /* preallocate and reset the first block (number 0) and Latin-1 (U+0000..U+00ff) after that */ - /* made sure above that maxDataLength>=1024 */ - - /* set indexes to point to consecutive data blocks */ - i=0; - do { - /* do this at least for trie->index[0] even if that block is only partly used for Latin-1 */ - trie->index[i++]=j; - j+=UTRIE_DATA_BLOCK_LENGTH; - } while(i<(256>>UTRIE_SHIFT)); - } - - /* reset the initially allocated blocks to the initial value */ - trie->dataLength=j; - while(j>0) { - trie->data[--j]=initialValue; - } - - trie->leadUnitValue=leadUnitValue; - trie->indexLength=UTRIE_MAX_INDEX_LENGTH; - trie->dataCapacity=maxDataLength; - trie->isLatin1Linear=latin1Linear; - trie->isCompacted=false; - return trie; -} - -U_CAPI UNewTrie * U_EXPORT2 -utrie_clone(UNewTrie *fillIn, const UNewTrie *other, uint32_t *aliasData, int32_t aliasDataCapacity) { - UNewTrie *trie; - UBool isDataAllocated; - - /* do not clone if other is not valid or already compacted */ - if(other==NULL || other->data==NULL || other->isCompacted) { - return NULL; - } - - /* clone data */ - if(aliasData!=NULL && aliasDataCapacity>=other->dataCapacity) { - isDataAllocated=false; - } else { - aliasDataCapacity=other->dataCapacity; - aliasData=(uint32_t *)uprv_malloc(other->dataCapacity*4); - if(aliasData==NULL) { - return NULL; - } - isDataAllocated=true; - } - - trie=utrie_open(fillIn, aliasData, aliasDataCapacity, - other->data[0], other->leadUnitValue, - other->isLatin1Linear); - if(trie==NULL) { - uprv_free(aliasData); - } else { - uprv_memcpy(trie->index, other->index, sizeof(trie->index)); - uprv_memcpy(trie->data, other->data, (size_t)other->dataLength*4); - trie->dataLength=other->dataLength; - trie->isDataAllocated=isDataAllocated; - } - - return trie; -} - -U_CAPI void U_EXPORT2 -utrie_close(UNewTrie *trie) { - if(trie!=NULL) { - if(trie->isDataAllocated) { - uprv_free(trie->data); - trie->data=NULL; - } - if(trie->isAllocated) { - uprv_free(trie); - } - } -} - -U_CAPI uint32_t * U_EXPORT2 -utrie_getData(UNewTrie *trie, int32_t *pLength) { - if(trie==NULL || pLength==NULL) { - return NULL; - } - - *pLength=trie->dataLength; - return trie->data; -} - -static int32_t -utrie_allocDataBlock(UNewTrie *trie) { - int32_t newBlock, newTop; - - newBlock=trie->dataLength; - newTop=newBlock+UTRIE_DATA_BLOCK_LENGTH; - if(newTop>trie->dataCapacity) { - /* out of memory in the data array */ - return -1; - } - trie->dataLength=newTop; - return newBlock; -} - -/** - * No error checking for illegal arguments. - * - * @return -1 if no new data block available (out of memory in data array) - * @internal - */ -static int32_t -utrie_getDataBlock(UNewTrie *trie, UChar32 c) { - int32_t indexValue, newBlock; - - c>>=UTRIE_SHIFT; - indexValue=trie->index[c]; - if(indexValue>0) { - return indexValue; - } - - /* allocate a new data block */ - newBlock=utrie_allocDataBlock(trie); - if(newBlock<0) { - /* out of memory in the data array */ - return -1; - } - trie->index[c]=newBlock; - - /* copy-on-write for a block from a setRange() */ - uprv_memcpy(trie->data+newBlock, trie->data-indexValue, 4*UTRIE_DATA_BLOCK_LENGTH); - return newBlock; -} - -/** - * @return true if the value was successfully set - */ -U_CAPI UBool U_EXPORT2 -utrie_set32(UNewTrie *trie, UChar32 c, uint32_t value) { - int32_t block; - - /* valid, uncompacted trie and valid c? */ - if(trie==NULL || trie->isCompacted || (uint32_t)c>0x10ffff) { - return false; - } - - block=utrie_getDataBlock(trie, c); - if(block<0) { - return false; - } - - trie->data[block+(c&UTRIE_MASK)]=value; - return true; -} - -U_CAPI uint32_t U_EXPORT2 -utrie_get32(UNewTrie *trie, UChar32 c, UBool *pInBlockZero) { - int32_t block; - - /* valid, uncompacted trie and valid c? */ - if(trie==NULL || trie->isCompacted || (uint32_t)c>0x10ffff) { - if(pInBlockZero!=NULL) { - *pInBlockZero=true; - } - return 0; - } - - block=trie->index[c>>UTRIE_SHIFT]; - if(pInBlockZero!=NULL) { - *pInBlockZero= (UBool)(block==0); - } - - return trie->data[ABS(block)+(c&UTRIE_MASK)]; -} - -/** - * @internal - */ -static void -utrie_fillBlock(uint32_t *block, UChar32 start, UChar32 limit, - uint32_t value, uint32_t initialValue, UBool overwrite) { - uint32_t *pLimit; - - pLimit=block+limit; - block+=start; - if(overwrite) { - while(blockisCompacted || - (uint32_t)start>0x10ffff || (uint32_t)limit>0x110000 || start>limit - ) { - return false; - } - if(start==limit) { - return true; /* nothing to do */ - } - - initialValue=trie->data[0]; - if(start&UTRIE_MASK) { - UChar32 nextStart; - - /* set partial block at [start..following block boundary[ */ - block=utrie_getDataBlock(trie, start); - if(block<0) { - return false; - } - - nextStart=(start+UTRIE_DATA_BLOCK_LENGTH)&~UTRIE_MASK; - if(nextStart<=limit) { - utrie_fillBlock(trie->data+block, start&UTRIE_MASK, UTRIE_DATA_BLOCK_LENGTH, - value, initialValue, overwrite); - start=nextStart; - } else { - utrie_fillBlock(trie->data+block, start&UTRIE_MASK, limit&UTRIE_MASK, - value, initialValue, overwrite); - return true; - } - } - - /* number of positions in the last, partial block */ - rest=limit&UTRIE_MASK; - - /* round down limit to a block boundary */ - limit&=~UTRIE_MASK; - - /* iterate over all-value blocks */ - if(value==initialValue) { - repeatBlock=0; - } else { - repeatBlock=-1; - } - while(startindex[start>>UTRIE_SHIFT]; - if(block>0) { - /* already allocated, fill in value */ - utrie_fillBlock(trie->data+block, 0, UTRIE_DATA_BLOCK_LENGTH, value, initialValue, overwrite); - } else if(trie->data[-block]!=value && (block==0 || overwrite)) { - /* set the repeatBlock instead of the current block 0 or range block */ - if(repeatBlock>=0) { - trie->index[start>>UTRIE_SHIFT]=-repeatBlock; - } else { - /* create and set and fill the repeatBlock */ - repeatBlock=utrie_getDataBlock(trie, start); - if(repeatBlock<0) { - return false; - } - - /* set the negative block number to indicate that it is a repeat block */ - trie->index[start>>UTRIE_SHIFT]=-repeatBlock; - utrie_fillBlock(trie->data+repeatBlock, 0, UTRIE_DATA_BLOCK_LENGTH, value, initialValue, true); - } - } - - start+=UTRIE_DATA_BLOCK_LENGTH; - } - - if(rest>0) { - /* set partial block at [last block boundary..limit[ */ - block=utrie_getDataBlock(trie, start); - if(block<0) { - return false; - } - - utrie_fillBlock(trie->data+block, 0, rest, value, initialValue, overwrite); - } - - return true; -} - -static int32_t -_findSameIndexBlock(const int32_t *idx, int32_t indexLength, - int32_t otherBlock) { - int32_t block, i; - - for(block=UTRIE_BMP_INDEX_LENGTH; blockindex; - - /* copy the lead surrogate indexes into a temporary array */ - uprv_memcpy(leadIndexes, idx+(0xd800>>UTRIE_SHIFT), 4*UTRIE_SURROGATE_BLOCK_COUNT); - - /* - * set all values for lead surrogate code *units* to leadUnitValue - * so that, by default, runtime lookups will find no data for associated - * supplementary code points, unless there is data for such code points - * which will result in a non-zero folding value below that is set for - * the respective lead units - * - * the above saved the indexes for surrogate code *points* - * fill the indexes with simplified code from utrie_setRange32() - */ - if(trie->leadUnitValue==trie->data[0]) { - block=0; /* leadUnitValue==initialValue, use all-initial-value block */ - } else { - /* create and fill the repeatBlock */ - block=utrie_allocDataBlock(trie); - if(block<0) { - /* data table overflow */ - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - utrie_fillBlock(trie->data+block, 0, UTRIE_DATA_BLOCK_LENGTH, trie->leadUnitValue, trie->data[0], true); - block=-block; /* negative block number to indicate that it is a repeat block */ - } - for(c=(0xd800>>UTRIE_SHIFT); c<(0xdc00>>UTRIE_SHIFT); ++c) { - trie->index[c]=block; - } - - /* - * Fold significant index values into the area just after the BMP indexes. - * In case the first lead surrogate has significant data, - * its index block must be used first (in which case the folding is a no-op). - * Later all folded index blocks are moved up one to insert the copied - * lead surrogate indexes. - */ - indexLength=UTRIE_BMP_INDEX_LENGTH; - - /* search for any index (stage 1) entries for supplementary code points */ - for(c=0x10000; c<0x110000;) { - if(idx[c>>UTRIE_SHIFT]!=0) { - /* there is data, treat the full block for a lead surrogate */ - c&=~0x3ff; - -#ifdef UTRIE_DEBUG - ++countLeadCUWithData; - /* printf("supplementary data for lead surrogate U+%04lx\n", (long)(0xd7c0+(c>>10))); */ -#endif - - /* is there an identical index block? */ - block=_findSameIndexBlock(idx, indexLength, c>>UTRIE_SHIFT); - - /* - * get a folded value for [c..c+0x400[ and, - * if different from the value for the lead surrogate code point, - * set it for the lead surrogate code unit - */ - value=getFoldedValue(trie, c, block+UTRIE_SURROGATE_BLOCK_COUNT); - if(value!=utrie_get32(trie, U16_LEAD(c), NULL)) { - if(!utrie_set32(trie, U16_LEAD(c), value)) { - /* data table overflow */ - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - - /* if we did not find an identical index block... */ - if(block==indexLength) { - /* move the actual index (stage 1) entries from the supplementary position to the new one */ - uprv_memmove(idx+indexLength, - idx+(c>>UTRIE_SHIFT), - 4*UTRIE_SURROGATE_BLOCK_COUNT); - indexLength+=UTRIE_SURROGATE_BLOCK_COUNT; - } - } - c+=0x400; - } else { - c+=UTRIE_DATA_BLOCK_LENGTH; - } - } -#ifdef UTRIE_DEBUG - if(countLeadCUWithData>0) { - printf("supplementary data for %d lead surrogates\n", countLeadCUWithData); - } -#endif - - /* - * index array overflow? - * This is to guarantee that a folding offset is of the form - * UTRIE_BMP_INDEX_LENGTH+n*UTRIE_SURROGATE_BLOCK_COUNT with n=0..1023. - * If the index is too large, then n>=1024 and more than 10 bits are necessary. - * - * In fact, it can only ever become n==1024 with completely unfoldable data and - * the additional block of duplicated values for lead surrogates. - */ - if(indexLength>=UTRIE_MAX_INDEX_LENGTH) { - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return; - } - - /* - * make space for the lead surrogate index block and - * insert it between the BMP indexes and the folded ones - */ - uprv_memmove(idx+UTRIE_BMP_INDEX_LENGTH+UTRIE_SURROGATE_BLOCK_COUNT, - idx+UTRIE_BMP_INDEX_LENGTH, - 4*(indexLength-UTRIE_BMP_INDEX_LENGTH)); - uprv_memcpy(idx+UTRIE_BMP_INDEX_LENGTH, - leadIndexes, - 4*UTRIE_SURROGATE_BLOCK_COUNT); - indexLength+=UTRIE_SURROGATE_BLOCK_COUNT; - -#ifdef UTRIE_DEBUG - printf("trie index count: BMP %ld all Unicode %ld folded %ld\n", - UTRIE_BMP_INDEX_LENGTH, (long)UTRIE_MAX_INDEX_LENGTH, indexLength); -#endif - - trie->indexLength=indexLength; -} - -/* - * Set a value in the trie index map to indicate which data block - * is referenced and which one is not. - * utrie_compact() will remove data blocks that are not used at all. - * Set - * - 0 if it is used - * - -1 if it is not used - */ -static void -_findUnusedBlocks(UNewTrie *trie) { - int32_t i; - - /* fill the entire map with "not used" */ - uprv_memset(trie->map, 0xff, (UTRIE_MAX_BUILD_TIME_DATA_LENGTH>>UTRIE_SHIFT)*4); - - /* mark each block that _is_ used with 0 */ - for(i=0; iindexLength; ++i) { - trie->map[ABS(trie->index[i])>>UTRIE_SHIFT]=0; - } - - /* never move the all-initial-value block 0 */ - trie->map[0]=0; -} - -static int32_t -_findSameDataBlock(const uint32_t *data, int32_t dataLength, - int32_t otherBlock, int32_t step) { - int32_t block; - - /* ensure that we do not even partially get past dataLength */ - dataLength-=UTRIE_DATA_BLOCK_LENGTH; - - for(block=0; block<=dataLength; block+=step) { - if(equal_uint32(data+block, data+otherBlock, UTRIE_DATA_BLOCK_LENGTH)) { - return block; - } - } - return -1; -} - -/* - * Compact a folded build-time trie. - * - * The compaction - * - removes blocks that are identical with earlier ones - * - overlaps adjacent blocks as much as possible (if overlap==true) - * - moves blocks in steps of the data granularity - * - moves and overlaps blocks that overlap with multiple values in the overlap region - * - * It does not - * - try to move and overlap blocks that are not already adjacent - */ -static void -utrie_compact(UNewTrie *trie, UBool overlap, UErrorCode *pErrorCode) { - int32_t i, start, newStart, overlapStart; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return; - } - - /* valid, uncompacted trie? */ - if(trie==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if(trie->isCompacted) { - return; /* nothing left to do */ - } - - /* compaction */ - - /* initialize the index map with "block is used/unused" flags */ - _findUnusedBlocks(trie); - - /* if Latin-1 is preallocated and linear, then do not compact Latin-1 data */ - if(trie->isLatin1Linear && UTRIE_SHIFT<=8) { - overlapStart=UTRIE_DATA_BLOCK_LENGTH+256; - } else { - overlapStart=UTRIE_DATA_BLOCK_LENGTH; - } - - newStart=UTRIE_DATA_BLOCK_LENGTH; - for(start=newStart; startdataLength;) { - /* - * start: index of first entry of current block - * newStart: index where the current block is to be moved - * (right after current end of already-compacted data) - */ - - /* skip blocks that are not used */ - if(trie->map[start>>UTRIE_SHIFT]<0) { - /* advance start to the next block */ - start+=UTRIE_DATA_BLOCK_LENGTH; - - /* leave newStart with the previous block! */ - continue; - } - - /* search for an identical block */ - if( start>=overlapStart && - (i=_findSameDataBlock(trie->data, newStart, start, - overlap ? UTRIE_DATA_GRANULARITY : UTRIE_DATA_BLOCK_LENGTH)) - >=0 - ) { - /* found an identical block, set the other block's index value for the current block */ - trie->map[start>>UTRIE_SHIFT]=i; - - /* advance start to the next block */ - start+=UTRIE_DATA_BLOCK_LENGTH; - - /* leave newStart with the previous block! */ - continue; - } - - /* see if the beginning of this block can be overlapped with the end of the previous block */ - if(overlap && start>=overlapStart) { - /* look for maximum overlap (modulo granularity) with the previous, adjacent block */ - for(i=UTRIE_DATA_BLOCK_LENGTH-UTRIE_DATA_GRANULARITY; - i>0 && !equal_uint32(trie->data+(newStart-i), trie->data+start, i); - i-=UTRIE_DATA_GRANULARITY) {} - } else { - i=0; - } - - if(i>0) { - /* some overlap */ - trie->map[start>>UTRIE_SHIFT]=newStart-i; - - /* move the non-overlapping indexes to their new positions */ - start+=i; - for(i=UTRIE_DATA_BLOCK_LENGTH-i; i>0; --i) { - trie->data[newStart++]=trie->data[start++]; - } - } else if(newStartmap[start>>UTRIE_SHIFT]=newStart; - for(i=UTRIE_DATA_BLOCK_LENGTH; i>0; --i) { - trie->data[newStart++]=trie->data[start++]; - } - } else /* no overlap && newStart==start */ { - trie->map[start>>UTRIE_SHIFT]=start; - newStart+=UTRIE_DATA_BLOCK_LENGTH; - start=newStart; - } - } - - /* now adjust the index (stage 1) table */ - for(i=0; iindexLength; ++i) { - trie->index[i]=trie->map[ABS(trie->index[i])>>UTRIE_SHIFT]; - } - -#ifdef UTRIE_DEBUG - /* we saved some space */ - printf("compacting trie: count of 32-bit words %lu->%lu\n", - (long)trie->dataLength, (long)newStart); -#endif - - trie->dataLength=newStart; -} - -/* serialization ------------------------------------------------------------ */ - -/* - * Default function for the folding value: - * Just store the offset (16 bits) if there is any non-initial-value entry. - * - * The offset parameter is never 0. - * Returning the offset itself is safe for UTRIE_SHIFT>=5 because - * for UTRIE_SHIFT==5 the maximum index length is UTRIE_MAX_INDEX_LENGTH==0x8800 - * which fits into 16-bit trie values; - * for higher UTRIE_SHIFT, UTRIE_MAX_INDEX_LENGTH decreases. - * - * Theoretically, it would be safer for all possible UTRIE_SHIFT including - * those of 4 and lower to return offset>>UTRIE_SURROGATE_BLOCK_BITS - * which would always result in a value of 0x40..0x43f - * (start/end 1k blocks of supplementary Unicode code points). - * However, this would be uglier, and would not work for some existing - * binary data file formats. - * - * Also, we do not plan to change UTRIE_SHIFT because it would change binary - * data file formats, and we would probably not make it smaller because of - * the then even larger BMP index length even for empty tries. - */ -static uint32_t U_CALLCONV -defaultGetFoldedValue(UNewTrie *trie, UChar32 start, int32_t offset) { - uint32_t value, initialValue; - UChar32 limit; - UBool inBlockZero; - - initialValue=trie->data[0]; - limit=start+0x400; - while(start0 && dt==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - if(getFoldedValue==NULL) { - getFoldedValue=defaultGetFoldedValue; - } - - data = (uint8_t*)dt; - /* fold and compact if necessary, also checks that indexLength is within limits */ - if(!trie->isCompacted) { - /* compact once without overlap to improve folding */ - utrie_compact(trie, false, pErrorCode); - - /* fold the supplementary part of the index array */ - utrie_fold(trie, getFoldedValue, pErrorCode); - - /* compact again with overlap for minimum data array length */ - utrie_compact(trie, true, pErrorCode); - - trie->isCompacted=true; - if(U_FAILURE(*pErrorCode)) { - return 0; - } - } - - /* is dataLength within limits? */ - if( (reduceTo16Bits ? (trie->dataLength+trie->indexLength) : trie->dataLength) >= UTRIE_MAX_DATA_LENGTH) { - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - } - - length=sizeof(UTrieHeader)+2*trie->indexLength; - if(reduceTo16Bits) { - length+=2*trie->dataLength; - } else { - length+=4*trie->dataLength; - } - - if(length>capacity) { - return length; /* preflighting */ - } - -#ifdef UTRIE_DEBUG - printf("**UTrieLengths(serialize)** index:%6ld data:%6ld serialized:%6ld\n", - (long)trie->indexLength, (long)trie->dataLength, (long)length); -#endif - - /* set the header fields */ - header=(UTrieHeader *)data; - data+=sizeof(UTrieHeader); - - header->signature=0x54726965; /* "Trie" */ - header->options=UTRIE_SHIFT | (UTRIE_INDEX_SHIFT<options|=UTRIE_OPTIONS_DATA_IS_32_BIT; - } - if(trie->isLatin1Linear) { - header->options|=UTRIE_OPTIONS_LATIN1_IS_LINEAR; - } - - header->indexLength=trie->indexLength; - header->dataLength=trie->dataLength; - - /* write the index (stage 1) array and the 16/32-bit data (stage 2) array */ - if(reduceTo16Bits) { - /* write 16-bit index values shifted right by UTRIE_INDEX_SHIFT, after adding indexLength */ - p=(uint32_t *)trie->index; - dest16=(uint16_t *)data; - for(i=trie->indexLength; i>0; --i) { - *dest16++=(uint16_t)((*p++ + trie->indexLength)>>UTRIE_INDEX_SHIFT); - } - - /* write 16-bit data values */ - p=trie->data; - for(i=trie->dataLength; i>0; --i) { - *dest16++=(uint16_t)*p++; - } - } else { - /* write 16-bit index values shifted right by UTRIE_INDEX_SHIFT */ - p=(uint32_t *)trie->index; - dest16=(uint16_t *)data; - for(i=trie->indexLength; i>0; --i) { - *dest16++=(uint16_t)(*p++ >> UTRIE_INDEX_SHIFT); - } - - /* write 32-bit data values */ - uprv_memcpy(dest16, trie->data, 4*(size_t)trie->dataLength); - } - - return length; -} - -/* inverse to defaultGetFoldedValue() */ -U_CAPI int32_t U_EXPORT2 -utrie_defaultGetFoldingOffset(uint32_t data) { - return (int32_t)data; -} - -U_CAPI int32_t U_EXPORT2 -utrie_unserialize(UTrie *trie, const void *data, int32_t length, UErrorCode *pErrorCode) { - const UTrieHeader *header; - const uint16_t *p16; - uint32_t options; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return -1; - } - - /* enough data for a trie header? */ - if(length<(int32_t)sizeof(UTrieHeader)) { - *pErrorCode=U_INVALID_FORMAT_ERROR; - return -1; - } - - /* check the signature */ - header=(const UTrieHeader *)data; - if(header->signature!=0x54726965) { - *pErrorCode=U_INVALID_FORMAT_ERROR; - return -1; - } - - /* get the options and check the shift values */ - options=header->options; - if( (options&UTRIE_OPTIONS_SHIFT_MASK)!=UTRIE_SHIFT || - ((options>>UTRIE_OPTIONS_INDEX_SHIFT)&UTRIE_OPTIONS_SHIFT_MASK)!=UTRIE_INDEX_SHIFT - ) { - *pErrorCode=U_INVALID_FORMAT_ERROR; - return -1; - } - trie->isLatin1Linear= (UBool)((options&UTRIE_OPTIONS_LATIN1_IS_LINEAR)!=0); - - /* get the length values */ - trie->indexLength=header->indexLength; - trie->dataLength=header->dataLength; - - length-=(int32_t)sizeof(UTrieHeader); - - /* enough data for the index? */ - if(length<2*trie->indexLength) { - *pErrorCode=U_INVALID_FORMAT_ERROR; - return -1; - } - p16=(const uint16_t *)(header+1); - trie->index=p16; - p16+=trie->indexLength; - length-=2*trie->indexLength; - - /* get the data */ - if(options&UTRIE_OPTIONS_DATA_IS_32_BIT) { - if(length<4*trie->dataLength) { - *pErrorCode=U_INVALID_FORMAT_ERROR; - return -1; - } - trie->data32=(const uint32_t *)p16; - trie->initialValue=trie->data32[0]; - length=(int32_t)sizeof(UTrieHeader)+2*trie->indexLength+4*trie->dataLength; - } else { - if(length<2*trie->dataLength) { - *pErrorCode=U_INVALID_FORMAT_ERROR; - return -1; - } - - /* the "data16" data is used via the index pointer */ - trie->data32=NULL; - trie->initialValue=trie->index[trie->indexLength]; - length=(int32_t)sizeof(UTrieHeader)+2*trie->indexLength+2*trie->dataLength; - } - - trie->getFoldingOffset=utrie_defaultGetFoldingOffset; - - return length; -} - -U_CAPI int32_t U_EXPORT2 -utrie_unserializeDummy(UTrie *trie, - void *data, int32_t length, - uint32_t initialValue, uint32_t leadUnitValue, - UBool make16BitTrie, - UErrorCode *pErrorCode) { - uint16_t *p16; - int32_t actualLength, latin1Length, i, limit; - uint16_t block; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return -1; - } - - /* calculate the actual size of the dummy trie data */ - - /* max(Latin-1, block 0) */ - latin1Length= 256; /*UTRIE_SHIFT<=8 ? 256 : UTRIE_DATA_BLOCK_LENGTH;*/ - - trie->indexLength=UTRIE_BMP_INDEX_LENGTH+UTRIE_SURROGATE_BLOCK_COUNT; - trie->dataLength=latin1Length; - if(leadUnitValue!=initialValue) { - trie->dataLength+=UTRIE_DATA_BLOCK_LENGTH; - } - - actualLength=trie->indexLength*2; - if(make16BitTrie) { - actualLength+=trie->dataLength*2; - } else { - actualLength+=trie->dataLength*4; - } - - /* enough space for the dummy trie? */ - if(lengthisLatin1Linear=true; - trie->initialValue=initialValue; - - /* fill the index and data arrays */ - p16=(uint16_t *)data; - trie->index=p16; - - if(make16BitTrie) { - /* indexes to block 0 */ - block=(uint16_t)(trie->indexLength>>UTRIE_INDEX_SHIFT); - limit=trie->indexLength; - for(i=0; i>UTRIE_INDEX_SHIFT); - i=0xd800>>UTRIE_SHIFT; - limit=0xdc00>>UTRIE_SHIFT; - for(; idata32=NULL; - - /* Latin-1 data */ - p16+=trie->indexLength; - for(i=0; iindexLength*2); - - if(leadUnitValue!=initialValue) { - /* indexes for lead surrogate code units to the block after Latin-1 */ - block=(uint16_t)(latin1Length>>UTRIE_INDEX_SHIFT); - i=0xd800>>UTRIE_SHIFT; - limit=0xdc00>>UTRIE_SHIFT; - for(; idata32=p32=(uint32_t *)(p16+trie->indexLength); - - /* Latin-1 data */ - for(i=0; igetFoldingOffset=utrie_defaultGetFoldingOffset; - - return actualLength; -} - -/* enumeration -------------------------------------------------------------- */ - -/* default UTrieEnumValue() returns the input value itself */ -static uint32_t U_CALLCONV -enumSameValue(const void * /*context*/, uint32_t value) { - return value; -} - -/** - * Enumerate all ranges of code points with the same relevant values. - * The values are transformed from the raw trie entries by the enumValue function. - */ -U_CAPI void U_EXPORT2 -utrie_enum(const UTrie *trie, - UTrieEnumValue *enumValue, UTrieEnumRange *enumRange, const void *context) { - const uint32_t *data32; - const uint16_t *idx; - - uint32_t value, prevValue, initialValue; - UChar32 c, prev; - int32_t l, i, j, block, prevBlock, nullBlock, offset; - - /* check arguments */ - if(trie==NULL || trie->index==NULL || enumRange==NULL) { - return; - } - if(enumValue==NULL) { - enumValue=enumSameValue; - } - - idx=trie->index; - data32=trie->data32; - - /* get the enumeration value that corresponds to an initial-value trie data entry */ - initialValue=enumValue(context, trie->initialValue); - - if(data32==NULL) { - nullBlock=trie->indexLength; - } else { - nullBlock=0; - } - - /* set variables for previous range */ - prevBlock=nullBlock; - prev=0; - prevValue=initialValue; - - /* enumerate BMP - the main loop enumerates data blocks */ - for(i=0, c=0; c<=0xffff; ++i) { - if(c==0xd800) { - /* skip lead surrogate code _units_, go to lead surr. code _points_ */ - i=UTRIE_BMP_INDEX_LENGTH; - } else if(c==0xdc00) { - /* go back to regular BMP code points */ - i=c>>UTRIE_SHIFT; - } - - block=idx[i]<0) { - /* the block is not filled with all the same value */ - prevBlock=-1; - } - prev=c; - prevValue=value; - } - ++c; - } - } - } - - /* enumerate supplementary code points */ - for(l=0xd800; l<0xdc00;) { - /* lead surrogate access */ - offset=idx[l>>UTRIE_SHIFT]<getFoldingOffset(value); - if(offset<=0) { - /* no data for this lead surrogate */ - if(prevValue!=initialValue) { - if(prev0) { - /* the block is not filled with all the same value */ - prevBlock=-1; - } - prev=c; - prevValue=value; - } - ++c; - } - } - } while(++i +#endif + +#include "unicode/utypes.h" +#include "cmemory.h" +#include "utrie.h" + +/* miscellaneous ------------------------------------------------------------ */ + +#undef ABS +#define ABS(x) ((x)>=0 ? (x) : -(x)) + +static inline UBool +equal_uint32(const uint32_t *s, const uint32_t *t, int32_t length) { + while(length>0 && *s==*t) { + ++s; + ++t; + --length; + } + return (UBool)(length==0); +} + +/* Building a trie ----------------------------------------------------------*/ + +U_CAPI UNewTrie * U_EXPORT2 +utrie_open(UNewTrie *fillIn, + uint32_t *aliasData, int32_t maxDataLength, + uint32_t initialValue, uint32_t leadUnitValue, + UBool latin1Linear) { + UNewTrie *trie; + int32_t i, j; + + if( maxDataLengthisAllocated= (UBool)(fillIn==nullptr); + + if(aliasData!=nullptr) { + trie->data=aliasData; + trie->isDataAllocated=false; + } else { + trie->data=(uint32_t *)uprv_malloc(maxDataLength*4); + if(trie->data==nullptr) { + uprv_free(trie); + return nullptr; + } + trie->isDataAllocated=true; + } + + /* preallocate and reset the first data block (block index 0) */ + j=UTRIE_DATA_BLOCK_LENGTH; + + if(latin1Linear) { + /* preallocate and reset the first block (number 0) and Latin-1 (U+0000..U+00ff) after that */ + /* made sure above that maxDataLength>=1024 */ + + /* set indexes to point to consecutive data blocks */ + i=0; + do { + /* do this at least for trie->index[0] even if that block is only partly used for Latin-1 */ + trie->index[i++]=j; + j+=UTRIE_DATA_BLOCK_LENGTH; + } while(i<(256>>UTRIE_SHIFT)); + } + + /* reset the initially allocated blocks to the initial value */ + trie->dataLength=j; + while(j>0) { + trie->data[--j]=initialValue; + } + + trie->leadUnitValue=leadUnitValue; + trie->indexLength=UTRIE_MAX_INDEX_LENGTH; + trie->dataCapacity=maxDataLength; + trie->isLatin1Linear=latin1Linear; + trie->isCompacted=false; + return trie; +} + +U_CAPI UNewTrie * U_EXPORT2 +utrie_clone(UNewTrie *fillIn, const UNewTrie *other, uint32_t *aliasData, int32_t aliasDataCapacity) { + UNewTrie *trie; + UBool isDataAllocated; + + /* do not clone if other is not valid or already compacted */ + if(other==nullptr || other->data==nullptr || other->isCompacted) { + return nullptr; + } + + /* clone data */ + if(aliasData!=nullptr && aliasDataCapacity>=other->dataCapacity) { + isDataAllocated=false; + } else { + aliasDataCapacity=other->dataCapacity; + aliasData=(uint32_t *)uprv_malloc(other->dataCapacity*4); + if(aliasData==nullptr) { + return nullptr; + } + isDataAllocated=true; + } + + trie=utrie_open(fillIn, aliasData, aliasDataCapacity, + other->data[0], other->leadUnitValue, + other->isLatin1Linear); + if(trie==nullptr) { + uprv_free(aliasData); + } else { + uprv_memcpy(trie->index, other->index, sizeof(trie->index)); + uprv_memcpy(trie->data, other->data, (size_t)other->dataLength*4); + trie->dataLength=other->dataLength; + trie->isDataAllocated=isDataAllocated; + } + + return trie; +} + +U_CAPI void U_EXPORT2 +utrie_close(UNewTrie *trie) { + if(trie!=nullptr) { + if(trie->isDataAllocated) { + uprv_free(trie->data); + trie->data=nullptr; + } + if(trie->isAllocated) { + uprv_free(trie); + } + } +} + +U_CAPI uint32_t * U_EXPORT2 +utrie_getData(UNewTrie *trie, int32_t *pLength) { + if(trie==nullptr || pLength==nullptr) { + return nullptr; + } + + *pLength=trie->dataLength; + return trie->data; +} + +static int32_t +utrie_allocDataBlock(UNewTrie *trie) { + int32_t newBlock, newTop; + + newBlock=trie->dataLength; + newTop=newBlock+UTRIE_DATA_BLOCK_LENGTH; + if(newTop>trie->dataCapacity) { + /* out of memory in the data array */ + return -1; + } + trie->dataLength=newTop; + return newBlock; +} + +/** + * No error checking for illegal arguments. + * + * @return -1 if no new data block available (out of memory in data array) + * @internal + */ +static int32_t +utrie_getDataBlock(UNewTrie *trie, UChar32 c) { + int32_t indexValue, newBlock; + + c>>=UTRIE_SHIFT; + indexValue=trie->index[c]; + if(indexValue>0) { + return indexValue; + } + + /* allocate a new data block */ + newBlock=utrie_allocDataBlock(trie); + if(newBlock<0) { + /* out of memory in the data array */ + return -1; + } + trie->index[c]=newBlock; + + /* copy-on-write for a block from a setRange() */ + uprv_memcpy(trie->data+newBlock, trie->data-indexValue, 4*UTRIE_DATA_BLOCK_LENGTH); + return newBlock; +} + +/** + * @return true if the value was successfully set + */ +U_CAPI UBool U_EXPORT2 +utrie_set32(UNewTrie *trie, UChar32 c, uint32_t value) { + int32_t block; + + /* valid, uncompacted trie and valid c? */ + if(trie==nullptr || trie->isCompacted || (uint32_t)c>0x10ffff) { + return false; + } + + block=utrie_getDataBlock(trie, c); + if(block<0) { + return false; + } + + trie->data[block+(c&UTRIE_MASK)]=value; + return true; +} + +U_CAPI uint32_t U_EXPORT2 +utrie_get32(UNewTrie *trie, UChar32 c, UBool *pInBlockZero) { + int32_t block; + + /* valid, uncompacted trie and valid c? */ + if(trie==nullptr || trie->isCompacted || (uint32_t)c>0x10ffff) { + if(pInBlockZero!=nullptr) { + *pInBlockZero=true; + } + return 0; + } + + block=trie->index[c>>UTRIE_SHIFT]; + if(pInBlockZero!=nullptr) { + *pInBlockZero= (UBool)(block==0); + } + + return trie->data[ABS(block)+(c&UTRIE_MASK)]; +} + +/** + * @internal + */ +static void +utrie_fillBlock(uint32_t *block, UChar32 start, UChar32 limit, + uint32_t value, uint32_t initialValue, UBool overwrite) { + uint32_t *pLimit; + + pLimit=block+limit; + block+=start; + if(overwrite) { + while(blockisCompacted || + (uint32_t)start>0x10ffff || (uint32_t)limit>0x110000 || start>limit + ) { + return false; + } + if(start==limit) { + return true; /* nothing to do */ + } + + initialValue=trie->data[0]; + if(start&UTRIE_MASK) { + UChar32 nextStart; + + /* set partial block at [start..following block boundary[ */ + block=utrie_getDataBlock(trie, start); + if(block<0) { + return false; + } + + nextStart=(start+UTRIE_DATA_BLOCK_LENGTH)&~UTRIE_MASK; + if(nextStart<=limit) { + utrie_fillBlock(trie->data+block, start&UTRIE_MASK, UTRIE_DATA_BLOCK_LENGTH, + value, initialValue, overwrite); + start=nextStart; + } else { + utrie_fillBlock(trie->data+block, start&UTRIE_MASK, limit&UTRIE_MASK, + value, initialValue, overwrite); + return true; + } + } + + /* number of positions in the last, partial block */ + rest=limit&UTRIE_MASK; + + /* round down limit to a block boundary */ + limit&=~UTRIE_MASK; + + /* iterate over all-value blocks */ + if(value==initialValue) { + repeatBlock=0; + } else { + repeatBlock=-1; + } + while(startindex[start>>UTRIE_SHIFT]; + if(block>0) { + /* already allocated, fill in value */ + utrie_fillBlock(trie->data+block, 0, UTRIE_DATA_BLOCK_LENGTH, value, initialValue, overwrite); + } else if(trie->data[-block]!=value && (block==0 || overwrite)) { + /* set the repeatBlock instead of the current block 0 or range block */ + if(repeatBlock>=0) { + trie->index[start>>UTRIE_SHIFT]=-repeatBlock; + } else { + /* create and set and fill the repeatBlock */ + repeatBlock=utrie_getDataBlock(trie, start); + if(repeatBlock<0) { + return false; + } + + /* set the negative block number to indicate that it is a repeat block */ + trie->index[start>>UTRIE_SHIFT]=-repeatBlock; + utrie_fillBlock(trie->data+repeatBlock, 0, UTRIE_DATA_BLOCK_LENGTH, value, initialValue, true); + } + } + + start+=UTRIE_DATA_BLOCK_LENGTH; + } + + if(rest>0) { + /* set partial block at [last block boundary..limit[ */ + block=utrie_getDataBlock(trie, start); + if(block<0) { + return false; + } + + utrie_fillBlock(trie->data+block, 0, rest, value, initialValue, overwrite); + } + + return true; +} + +static int32_t +_findSameIndexBlock(const int32_t *idx, int32_t indexLength, + int32_t otherBlock) { + int32_t block, i; + + for(block=UTRIE_BMP_INDEX_LENGTH; blockindex; + + /* copy the lead surrogate indexes into a temporary array */ + uprv_memcpy(leadIndexes, idx+(0xd800>>UTRIE_SHIFT), 4*UTRIE_SURROGATE_BLOCK_COUNT); + + /* + * set all values for lead surrogate code *units* to leadUnitValue + * so that, by default, runtime lookups will find no data for associated + * supplementary code points, unless there is data for such code points + * which will result in a non-zero folding value below that is set for + * the respective lead units + * + * the above saved the indexes for surrogate code *points* + * fill the indexes with simplified code from utrie_setRange32() + */ + if(trie->leadUnitValue==trie->data[0]) { + block=0; /* leadUnitValue==initialValue, use all-initial-value block */ + } else { + /* create and fill the repeatBlock */ + block=utrie_allocDataBlock(trie); + if(block<0) { + /* data table overflow */ + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + utrie_fillBlock(trie->data+block, 0, UTRIE_DATA_BLOCK_LENGTH, trie->leadUnitValue, trie->data[0], true); + block=-block; /* negative block number to indicate that it is a repeat block */ + } + for(c=(0xd800>>UTRIE_SHIFT); c<(0xdc00>>UTRIE_SHIFT); ++c) { + trie->index[c]=block; + } + + /* + * Fold significant index values into the area just after the BMP indexes. + * In case the first lead surrogate has significant data, + * its index block must be used first (in which case the folding is a no-op). + * Later all folded index blocks are moved up one to insert the copied + * lead surrogate indexes. + */ + indexLength=UTRIE_BMP_INDEX_LENGTH; + + /* search for any index (stage 1) entries for supplementary code points */ + for(c=0x10000; c<0x110000;) { + if(idx[c>>UTRIE_SHIFT]!=0) { + /* there is data, treat the full block for a lead surrogate */ + c&=~0x3ff; + +#ifdef UTRIE_DEBUG + ++countLeadCUWithData; + /* printf("supplementary data for lead surrogate U+%04lx\n", (long)(0xd7c0+(c>>10))); */ +#endif + + /* is there an identical index block? */ + block=_findSameIndexBlock(idx, indexLength, c>>UTRIE_SHIFT); + + /* + * get a folded value for [c..c+0x400[ and, + * if different from the value for the lead surrogate code point, + * set it for the lead surrogate code unit + */ + value=getFoldedValue(trie, c, block+UTRIE_SURROGATE_BLOCK_COUNT); + if(value!=utrie_get32(trie, U16_LEAD(c), nullptr)) { + if(!utrie_set32(trie, U16_LEAD(c), value)) { + /* data table overflow */ + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + + /* if we did not find an identical index block... */ + if(block==indexLength) { + /* move the actual index (stage 1) entries from the supplementary position to the new one */ + uprv_memmove(idx+indexLength, + idx+(c>>UTRIE_SHIFT), + 4*UTRIE_SURROGATE_BLOCK_COUNT); + indexLength+=UTRIE_SURROGATE_BLOCK_COUNT; + } + } + c+=0x400; + } else { + c+=UTRIE_DATA_BLOCK_LENGTH; + } + } +#ifdef UTRIE_DEBUG + if(countLeadCUWithData>0) { + printf("supplementary data for %d lead surrogates\n", countLeadCUWithData); + } +#endif + + /* + * index array overflow? + * This is to guarantee that a folding offset is of the form + * UTRIE_BMP_INDEX_LENGTH+n*UTRIE_SURROGATE_BLOCK_COUNT with n=0..1023. + * If the index is too large, then n>=1024 and more than 10 bits are necessary. + * + * In fact, it can only ever become n==1024 with completely unfoldable data and + * the additional block of duplicated values for lead surrogates. + */ + if(indexLength>=UTRIE_MAX_INDEX_LENGTH) { + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return; + } + + /* + * make space for the lead surrogate index block and + * insert it between the BMP indexes and the folded ones + */ + uprv_memmove(idx+UTRIE_BMP_INDEX_LENGTH+UTRIE_SURROGATE_BLOCK_COUNT, + idx+UTRIE_BMP_INDEX_LENGTH, + 4*(indexLength-UTRIE_BMP_INDEX_LENGTH)); + uprv_memcpy(idx+UTRIE_BMP_INDEX_LENGTH, + leadIndexes, + 4*UTRIE_SURROGATE_BLOCK_COUNT); + indexLength+=UTRIE_SURROGATE_BLOCK_COUNT; + +#ifdef UTRIE_DEBUG + printf("trie index count: BMP %ld all Unicode %ld folded %ld\n", + UTRIE_BMP_INDEX_LENGTH, (long)UTRIE_MAX_INDEX_LENGTH, indexLength); +#endif + + trie->indexLength=indexLength; +} + +/* + * Set a value in the trie index map to indicate which data block + * is referenced and which one is not. + * utrie_compact() will remove data blocks that are not used at all. + * Set + * - 0 if it is used + * - -1 if it is not used + */ +static void +_findUnusedBlocks(UNewTrie *trie) { + int32_t i; + + /* fill the entire map with "not used" */ + uprv_memset(trie->map, 0xff, (UTRIE_MAX_BUILD_TIME_DATA_LENGTH>>UTRIE_SHIFT)*4); + + /* mark each block that _is_ used with 0 */ + for(i=0; iindexLength; ++i) { + trie->map[ABS(trie->index[i])>>UTRIE_SHIFT]=0; + } + + /* never move the all-initial-value block 0 */ + trie->map[0]=0; +} + +static int32_t +_findSameDataBlock(const uint32_t *data, int32_t dataLength, + int32_t otherBlock, int32_t step) { + int32_t block; + + /* ensure that we do not even partially get past dataLength */ + dataLength-=UTRIE_DATA_BLOCK_LENGTH; + + for(block=0; block<=dataLength; block+=step) { + if(equal_uint32(data+block, data+otherBlock, UTRIE_DATA_BLOCK_LENGTH)) { + return block; + } + } + return -1; +} + +/* + * Compact a folded build-time trie. + * + * The compaction + * - removes blocks that are identical with earlier ones + * - overlaps adjacent blocks as much as possible (if overlap==true) + * - moves blocks in steps of the data granularity + * - moves and overlaps blocks that overlap with multiple values in the overlap region + * + * It does not + * - try to move and overlap blocks that are not already adjacent + */ +static void +utrie_compact(UNewTrie *trie, UBool overlap, UErrorCode *pErrorCode) { + int32_t i, start, newStart, overlapStart; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return; + } + + /* valid, uncompacted trie? */ + if(trie==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if(trie->isCompacted) { + return; /* nothing left to do */ + } + + /* compaction */ + + /* initialize the index map with "block is used/unused" flags */ + _findUnusedBlocks(trie); + + /* if Latin-1 is preallocated and linear, then do not compact Latin-1 data */ + if(trie->isLatin1Linear && UTRIE_SHIFT<=8) { + overlapStart=UTRIE_DATA_BLOCK_LENGTH+256; + } else { + overlapStart=UTRIE_DATA_BLOCK_LENGTH; + } + + newStart=UTRIE_DATA_BLOCK_LENGTH; + for(start=newStart; startdataLength;) { + /* + * start: index of first entry of current block + * newStart: index where the current block is to be moved + * (right after current end of already-compacted data) + */ + + /* skip blocks that are not used */ + if(trie->map[start>>UTRIE_SHIFT]<0) { + /* advance start to the next block */ + start+=UTRIE_DATA_BLOCK_LENGTH; + + /* leave newStart with the previous block! */ + continue; + } + + /* search for an identical block */ + if( start>=overlapStart && + (i=_findSameDataBlock(trie->data, newStart, start, + overlap ? UTRIE_DATA_GRANULARITY : UTRIE_DATA_BLOCK_LENGTH)) + >=0 + ) { + /* found an identical block, set the other block's index value for the current block */ + trie->map[start>>UTRIE_SHIFT]=i; + + /* advance start to the next block */ + start+=UTRIE_DATA_BLOCK_LENGTH; + + /* leave newStart with the previous block! */ + continue; + } + + /* see if the beginning of this block can be overlapped with the end of the previous block */ + if(overlap && start>=overlapStart) { + /* look for maximum overlap (modulo granularity) with the previous, adjacent block */ + for(i=UTRIE_DATA_BLOCK_LENGTH-UTRIE_DATA_GRANULARITY; + i>0 && !equal_uint32(trie->data+(newStart-i), trie->data+start, i); + i-=UTRIE_DATA_GRANULARITY) {} + } else { + i=0; + } + + if(i>0) { + /* some overlap */ + trie->map[start>>UTRIE_SHIFT]=newStart-i; + + /* move the non-overlapping indexes to their new positions */ + start+=i; + for(i=UTRIE_DATA_BLOCK_LENGTH-i; i>0; --i) { + trie->data[newStart++]=trie->data[start++]; + } + } else if(newStartmap[start>>UTRIE_SHIFT]=newStart; + for(i=UTRIE_DATA_BLOCK_LENGTH; i>0; --i) { + trie->data[newStart++]=trie->data[start++]; + } + } else /* no overlap && newStart==start */ { + trie->map[start>>UTRIE_SHIFT]=start; + newStart+=UTRIE_DATA_BLOCK_LENGTH; + start=newStart; + } + } + + /* now adjust the index (stage 1) table */ + for(i=0; iindexLength; ++i) { + trie->index[i]=trie->map[ABS(trie->index[i])>>UTRIE_SHIFT]; + } + +#ifdef UTRIE_DEBUG + /* we saved some space */ + printf("compacting trie: count of 32-bit words %lu->%lu\n", + (long)trie->dataLength, (long)newStart); +#endif + + trie->dataLength=newStart; +} + +/* serialization ------------------------------------------------------------ */ + +/* + * Default function for the folding value: + * Just store the offset (16 bits) if there is any non-initial-value entry. + * + * The offset parameter is never 0. + * Returning the offset itself is safe for UTRIE_SHIFT>=5 because + * for UTRIE_SHIFT==5 the maximum index length is UTRIE_MAX_INDEX_LENGTH==0x8800 + * which fits into 16-bit trie values; + * for higher UTRIE_SHIFT, UTRIE_MAX_INDEX_LENGTH decreases. + * + * Theoretically, it would be safer for all possible UTRIE_SHIFT including + * those of 4 and lower to return offset>>UTRIE_SURROGATE_BLOCK_BITS + * which would always result in a value of 0x40..0x43f + * (start/end 1k blocks of supplementary Unicode code points). + * However, this would be uglier, and would not work for some existing + * binary data file formats. + * + * Also, we do not plan to change UTRIE_SHIFT because it would change binary + * data file formats, and we would probably not make it smaller because of + * the then even larger BMP index length even for empty tries. + */ +static uint32_t U_CALLCONV +defaultGetFoldedValue(UNewTrie *trie, UChar32 start, int32_t offset) { + uint32_t value, initialValue; + UChar32 limit; + UBool inBlockZero; + + initialValue=trie->data[0]; + limit=start+0x400; + while(start0 && dt==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + if(getFoldedValue==nullptr) { + getFoldedValue=defaultGetFoldedValue; + } + + data = (uint8_t*)dt; + /* fold and compact if necessary, also checks that indexLength is within limits */ + if(!trie->isCompacted) { + /* compact once without overlap to improve folding */ + utrie_compact(trie, false, pErrorCode); + + /* fold the supplementary part of the index array */ + utrie_fold(trie, getFoldedValue, pErrorCode); + + /* compact again with overlap for minimum data array length */ + utrie_compact(trie, true, pErrorCode); + + trie->isCompacted=true; + if(U_FAILURE(*pErrorCode)) { + return 0; + } + } + + /* is dataLength within limits? */ + if( (reduceTo16Bits ? (trie->dataLength+trie->indexLength) : trie->dataLength) >= UTRIE_MAX_DATA_LENGTH) { + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + } + + length=sizeof(UTrieHeader)+2*trie->indexLength; + if(reduceTo16Bits) { + length+=2*trie->dataLength; + } else { + length+=4*trie->dataLength; + } + + if(length>capacity) { + return length; /* preflighting */ + } + +#ifdef UTRIE_DEBUG + printf("**UTrieLengths(serialize)** index:%6ld data:%6ld serialized:%6ld\n", + (long)trie->indexLength, (long)trie->dataLength, (long)length); +#endif + + /* set the header fields */ + header=(UTrieHeader *)data; + data+=sizeof(UTrieHeader); + + header->signature=0x54726965; /* "Trie" */ + header->options=UTRIE_SHIFT | (UTRIE_INDEX_SHIFT<options|=UTRIE_OPTIONS_DATA_IS_32_BIT; + } + if(trie->isLatin1Linear) { + header->options|=UTRIE_OPTIONS_LATIN1_IS_LINEAR; + } + + header->indexLength=trie->indexLength; + header->dataLength=trie->dataLength; + + /* write the index (stage 1) array and the 16/32-bit data (stage 2) array */ + if(reduceTo16Bits) { + /* write 16-bit index values shifted right by UTRIE_INDEX_SHIFT, after adding indexLength */ + p=(uint32_t *)trie->index; + dest16=(uint16_t *)data; + for(i=trie->indexLength; i>0; --i) { + *dest16++=(uint16_t)((*p++ + trie->indexLength)>>UTRIE_INDEX_SHIFT); + } + + /* write 16-bit data values */ + p=trie->data; + for(i=trie->dataLength; i>0; --i) { + *dest16++=(uint16_t)*p++; + } + } else { + /* write 16-bit index values shifted right by UTRIE_INDEX_SHIFT */ + p=(uint32_t *)trie->index; + dest16=(uint16_t *)data; + for(i=trie->indexLength; i>0; --i) { + *dest16++=(uint16_t)(*p++ >> UTRIE_INDEX_SHIFT); + } + + /* write 32-bit data values */ + uprv_memcpy(dest16, trie->data, 4*(size_t)trie->dataLength); + } + + return length; +} + +/* inverse to defaultGetFoldedValue() */ +U_CAPI int32_t U_EXPORT2 +utrie_defaultGetFoldingOffset(uint32_t data) { + return (int32_t)data; +} + +U_CAPI int32_t U_EXPORT2 +utrie_unserialize(UTrie *trie, const void *data, int32_t length, UErrorCode *pErrorCode) { + const UTrieHeader *header; + const uint16_t *p16; + uint32_t options; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return -1; + } + + /* enough data for a trie header? */ + if(length<(int32_t)sizeof(UTrieHeader)) { + *pErrorCode=U_INVALID_FORMAT_ERROR; + return -1; + } + + /* check the signature */ + header=(const UTrieHeader *)data; + if(header->signature!=0x54726965) { + *pErrorCode=U_INVALID_FORMAT_ERROR; + return -1; + } + + /* get the options and check the shift values */ + options=header->options; + if( (options&UTRIE_OPTIONS_SHIFT_MASK)!=UTRIE_SHIFT || + ((options>>UTRIE_OPTIONS_INDEX_SHIFT)&UTRIE_OPTIONS_SHIFT_MASK)!=UTRIE_INDEX_SHIFT + ) { + *pErrorCode=U_INVALID_FORMAT_ERROR; + return -1; + } + trie->isLatin1Linear= (UBool)((options&UTRIE_OPTIONS_LATIN1_IS_LINEAR)!=0); + + /* get the length values */ + trie->indexLength=header->indexLength; + trie->dataLength=header->dataLength; + + length-=(int32_t)sizeof(UTrieHeader); + + /* enough data for the index? */ + if(length<2*trie->indexLength) { + *pErrorCode=U_INVALID_FORMAT_ERROR; + return -1; + } + p16=(const uint16_t *)(header+1); + trie->index=p16; + p16+=trie->indexLength; + length-=2*trie->indexLength; + + /* get the data */ + if(options&UTRIE_OPTIONS_DATA_IS_32_BIT) { + if(length<4*trie->dataLength) { + *pErrorCode=U_INVALID_FORMAT_ERROR; + return -1; + } + trie->data32=(const uint32_t *)p16; + trie->initialValue=trie->data32[0]; + length=(int32_t)sizeof(UTrieHeader)+2*trie->indexLength+4*trie->dataLength; + } else { + if(length<2*trie->dataLength) { + *pErrorCode=U_INVALID_FORMAT_ERROR; + return -1; + } + + /* the "data16" data is used via the index pointer */ + trie->data32=nullptr; + trie->initialValue=trie->index[trie->indexLength]; + length=(int32_t)sizeof(UTrieHeader)+2*trie->indexLength+2*trie->dataLength; + } + + trie->getFoldingOffset=utrie_defaultGetFoldingOffset; + + return length; +} + +U_CAPI int32_t U_EXPORT2 +utrie_unserializeDummy(UTrie *trie, + void *data, int32_t length, + uint32_t initialValue, uint32_t leadUnitValue, + UBool make16BitTrie, + UErrorCode *pErrorCode) { + uint16_t *p16; + int32_t actualLength, latin1Length, i, limit; + uint16_t block; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return -1; + } + + /* calculate the actual size of the dummy trie data */ + + /* max(Latin-1, block 0) */ + latin1Length= 256; /*UTRIE_SHIFT<=8 ? 256 : UTRIE_DATA_BLOCK_LENGTH;*/ + + trie->indexLength=UTRIE_BMP_INDEX_LENGTH+UTRIE_SURROGATE_BLOCK_COUNT; + trie->dataLength=latin1Length; + if(leadUnitValue!=initialValue) { + trie->dataLength+=UTRIE_DATA_BLOCK_LENGTH; + } + + actualLength=trie->indexLength*2; + if(make16BitTrie) { + actualLength+=trie->dataLength*2; + } else { + actualLength+=trie->dataLength*4; + } + + /* enough space for the dummy trie? */ + if(lengthisLatin1Linear=true; + trie->initialValue=initialValue; + + /* fill the index and data arrays */ + p16=(uint16_t *)data; + trie->index=p16; + + if(make16BitTrie) { + /* indexes to block 0 */ + block=(uint16_t)(trie->indexLength>>UTRIE_INDEX_SHIFT); + limit=trie->indexLength; + for(i=0; i>UTRIE_INDEX_SHIFT); + i=0xd800>>UTRIE_SHIFT; + limit=0xdc00>>UTRIE_SHIFT; + for(; idata32=nullptr; + + /* Latin-1 data */ + p16+=trie->indexLength; + for(i=0; iindexLength*2); + + if(leadUnitValue!=initialValue) { + /* indexes for lead surrogate code units to the block after Latin-1 */ + block=(uint16_t)(latin1Length>>UTRIE_INDEX_SHIFT); + i=0xd800>>UTRIE_SHIFT; + limit=0xdc00>>UTRIE_SHIFT; + for(; idata32=p32=(uint32_t *)(p16+trie->indexLength); + + /* Latin-1 data */ + for(i=0; igetFoldingOffset=utrie_defaultGetFoldingOffset; + + return actualLength; +} + +/* enumeration -------------------------------------------------------------- */ + +/* default UTrieEnumValue() returns the input value itself */ +static uint32_t U_CALLCONV +enumSameValue(const void * /*context*/, uint32_t value) { + return value; +} + +/** + * Enumerate all ranges of code points with the same relevant values. + * The values are transformed from the raw trie entries by the enumValue function. + */ +U_CAPI void U_EXPORT2 +utrie_enum(const UTrie *trie, + UTrieEnumValue *enumValue, UTrieEnumRange *enumRange, const void *context) { + const uint32_t *data32; + const uint16_t *idx; + + uint32_t value, prevValue, initialValue; + UChar32 c, prev; + int32_t l, i, j, block, prevBlock, nullBlock, offset; + + /* check arguments */ + if(trie==nullptr || trie->index==nullptr || enumRange==nullptr) { + return; + } + if(enumValue==nullptr) { + enumValue=enumSameValue; + } + + idx=trie->index; + data32=trie->data32; + + /* get the enumeration value that corresponds to an initial-value trie data entry */ + initialValue=enumValue(context, trie->initialValue); + + if(data32==nullptr) { + nullBlock=trie->indexLength; + } else { + nullBlock=0; + } + + /* set variables for previous range */ + prevBlock=nullBlock; + prev=0; + prevValue=initialValue; + + /* enumerate BMP - the main loop enumerates data blocks */ + for(i=0, c=0; c<=0xffff; ++i) { + if(c==0xd800) { + /* skip lead surrogate code _units_, go to lead surr. code _points_ */ + i=UTRIE_BMP_INDEX_LENGTH; + } else if(c==0xdc00) { + /* go back to regular BMP code points */ + i=c>>UTRIE_SHIFT; + } + + block=idx[i]<0) { + /* the block is not filled with all the same value */ + prevBlock=-1; + } + prev=c; + prevValue=value; + } + ++c; + } + } + } + + /* enumerate supplementary code points */ + for(l=0xd800; l<0xdc00;) { + /* lead surrogate access */ + offset=idx[l>>UTRIE_SHIFT]<getFoldingOffset(value); + if(offset<=0) { + /* no data for this lead surrogate */ + if(prevValue!=initialValue) { + if(prev0) { + /* the block is not filled with all the same value */ + prevBlock=-1; + } + prev=c; + prevValue=value; + } + ++c; + } + } + } while(++i>UTRIE_SHIFT, - - /** - * Shift size for shifting left the index array values. - * Increases possible data size with 16-bit index values at the cost - * of compactability. - * This requires blocks of stage 2 data to be aligned by UTRIE_DATA_GRANULARITY. - * 0..UTRIE_SHIFT - */ - UTRIE_INDEX_SHIFT=2, - - /** The alignment size of a stage 2 data block. Also the granularity for compaction. */ - UTRIE_DATA_GRANULARITY=1<>UTRIE_SHIFT - */ - UTRIE_SURROGATE_BLOCK_COUNT=(1<>UTRIE_SHIFT -}; - -/** - * Length of the index (stage 1) array before folding. - * Maximum number of Unicode code points (0x110000) shifted right by UTRIE_SHIFT. - */ -#define UTRIE_MAX_INDEX_LENGTH (0x110000>>UTRIE_SHIFT) - -/** - * Maximum length of the runtime data (stage 2) array. - * Limited by 16-bit index values that are left-shifted by UTRIE_INDEX_SHIFT. - */ -#define UTRIE_MAX_DATA_LENGTH (0x10000<=UTRIE_BMP_INDEX_LENGTH, or 0 if there is no data for the lead surrogate - */ -typedef int32_t U_CALLCONV -UTrieGetFoldingOffset(uint32_t data); - -/** - * Run-time Trie structure. - * - * Either the data table is 16 bits wide and accessed via the index - * pointer, with each index item increased by indexLength; - * in this case, data32==NULL. - * - * Or the data table is 32 bits wide and accessed via the data32 pointer. - */ -struct UTrie { - const uint16_t *index; - const uint32_t *data32; /* NULL if 16b data is used via index */ - - /** - * This function is not used in _FROM_LEAD, _FROM_BMP, and _FROM_OFFSET_TRAIL macros. - * If convenience macros like _GET16 or _NEXT32 are used, this function must be set. - * - * utrie_unserialize() sets a default function which simply returns - * the lead surrogate's value itself - which is the inverse of the default - * folding function used by utrie_serialize(). - * - * @see UTrieGetFoldingOffset - */ - UTrieGetFoldingOffset *getFoldingOffset; - - int32_t indexLength, dataLength; - uint32_t initialValue; - UBool isLatin1Linear; -}; - -#ifndef __UTRIE2_H__ -typedef struct UTrie UTrie; -#endif - -/** Internal trie getter from an offset (0 if c16 is a BMP/lead units) and a 16-bit unit */ -#define _UTRIE_GET_RAW(trie, data, offset, c16) \ - (trie)->data[ \ - ((int32_t)((trie)->index[(offset)+((c16)>>UTRIE_SHIFT)])<getFoldingOffset(result); \ -\ - /* get the real data from the folded lead/trail units */ \ - if(__offset>0) { \ - (result)=_UTRIE_GET_RAW((trie), data, __offset, (c2)&0x3ff); \ - } else { \ - (result)=(resultType)((trie)->initialValue); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** Internal trie getter from a BMP code point, treating a lead surrogate as a normal code point */ -#define _UTRIE_GET_FROM_BMP(trie, data, c16) \ - _UTRIE_GET_RAW(trie, data, 0xd800<=(c16) && (c16)<=0xdbff ? UTRIE_LEAD_INDEX_DISP : 0, c16) - -/** - * Internal trie getter from a code point. - * Could be faster(?) but longer with - * if((c32)<=0xd7ff) { (result)=_UTRIE_GET_RAW(trie, data, 0, c32); } - */ -#define _UTRIE_GET(trie, data, c32, result, resultType) UPRV_BLOCK_MACRO_BEGIN { \ - if((uint32_t)(c32)<=0xffff) { \ - /* BMP code points */ \ - (result)=_UTRIE_GET_FROM_BMP(trie, data, c32); \ - } else if((uint32_t)(c32)<=0x10ffff) { \ - /* supplementary code point */ \ - UChar __lead16=U16_LEAD(c32); \ - _UTRIE_GET_FROM_PAIR(trie, data, __lead16, c32, result, resultType); \ - } else { \ - /* out of range */ \ - (result)=(resultType)((trie)->initialValue); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** Internal next-post-increment: get the next code point (c, c2) and its data */ -#define _UTRIE_NEXT(trie, data, src, limit, c, c2, result, resultType) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=*(src)++; \ - if(!U16_IS_LEAD(c)) { \ - (c2)=0; \ - (result)=_UTRIE_GET_RAW((trie), data, 0, (c)); \ - } else if((src)!=(limit) && U16_IS_TRAIL((c2)=*(src))) { \ - ++(src); \ - _UTRIE_GET_FROM_PAIR((trie), data, (c), (c2), (result), resultType); \ - } else { \ - /* unpaired lead surrogate code point */ \ - (c2)=0; \ - (result)=_UTRIE_GET_RAW((trie), data, UTRIE_LEAD_INDEX_DISP, (c)); \ - } \ -} UPRV_BLOCK_MACRO_END - -/** Internal previous: get the previous code point (c, c2) and its data */ -#define _UTRIE_PREVIOUS(trie, data, start, src, c, c2, result, resultType) UPRV_BLOCK_MACRO_BEGIN { \ - (c)=*--(src); \ - if(!U16_IS_SURROGATE(c)) { \ - (c2)=0; \ - (result)=_UTRIE_GET_RAW((trie), data, 0, (c)); \ - } else if(!U16_IS_SURROGATE_LEAD(c)) { \ - /* trail surrogate */ \ - if((start)!=(src) && U16_IS_LEAD((c2)=*((src)-1))) { \ - --(src); \ - (result)=(c); (c)=(c2); (c2)=(UChar)(result); /* swap c, c2 */ \ - _UTRIE_GET_FROM_PAIR((trie), data, (c), (c2), (result), resultType); \ - } else { \ - /* unpaired trail surrogate code point */ \ - (c2)=0; \ - (result)=_UTRIE_GET_RAW((trie), data, 0, (c)); \ - } \ - } else { \ - /* unpaired lead surrogate code point */ \ - (c2)=0; \ - (result)=_UTRIE_GET_RAW((trie), data, UTRIE_LEAD_INDEX_DISP, (c)); \ - } \ -} UPRV_BLOCK_MACRO_END - -/* Public UTrie API ---------------------------------------------------------*/ - -/** - * Get a pointer to the contiguous part of the data array - * for the Latin-1 range (U+0000..U+00ff). - * Must be used only if the Latin-1 range is in fact linear - * (trie->isLatin1Linear). - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @return (const uint16_t *) pointer to values for Latin-1 code points - */ -#define UTRIE_GET16_LATIN1(trie) ((trie)->index+(trie)->indexLength+UTRIE_DATA_BLOCK_LENGTH) - -/** - * Get a pointer to the contiguous part of the data array - * for the Latin-1 range (U+0000..U+00ff). - * Must be used only if the Latin-1 range is in fact linear - * (trie->isLatin1Linear). - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @return (const uint32_t *) pointer to values for Latin-1 code points - */ -#define UTRIE_GET32_LATIN1(trie) ((trie)->data32+UTRIE_DATA_BLOCK_LENGTH) - -/** - * Get a 16-bit trie value from a BMP code point (UChar, <=U+ffff). - * c16 may be a lead surrogate, which may have a value including a folding offset. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param c16 (UChar, in) the input BMP code point - * @return (uint16_t) trie lookup result - */ -#define UTRIE_GET16_FROM_LEAD(trie, c16) _UTRIE_GET_RAW(trie, index, 0, c16) - -/** - * Get a 32-bit trie value from a BMP code point (UChar, <=U+ffff). - * c16 may be a lead surrogate, which may have a value including a folding offset. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param c16 (UChar, in) the input BMP code point - * @return (uint32_t) trie lookup result - */ -#define UTRIE_GET32_FROM_LEAD(trie, c16) _UTRIE_GET_RAW(trie, data32, 0, c16) - -/** - * Get a 16-bit trie value from a BMP code point (UChar, <=U+ffff). - * Even lead surrogate code points are treated as normal code points, - * with unfolded values that may differ from _FROM_LEAD() macro results for them. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param c16 (UChar, in) the input BMP code point - * @return (uint16_t) trie lookup result - */ -#define UTRIE_GET16_FROM_BMP(trie, c16) _UTRIE_GET_FROM_BMP(trie, index, c16) - -/** - * Get a 32-bit trie value from a BMP code point (UChar, <=U+ffff). - * Even lead surrogate code points are treated as normal code points, - * with unfolded values that may differ from _FROM_LEAD() macro results for them. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param c16 (UChar, in) the input BMP code point - * @return (uint32_t) trie lookup result - */ -#define UTRIE_GET32_FROM_BMP(trie, c16) _UTRIE_GET_FROM_BMP(trie, data32, c16) - -/** - * Get a 16-bit trie value from a code point. - * Even lead surrogate code points are treated as normal code points, - * with unfolded values that may differ from _FROM_LEAD() macro results for them. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param c32 (UChar32, in) the input code point - * @param result (uint16_t, out) uint16_t variable for the trie lookup result - */ -#define UTRIE_GET16(trie, c32, result) _UTRIE_GET(trie, index, c32, result, uint16_t) - -/** - * Get a 32-bit trie value from a code point. - * Even lead surrogate code points are treated as normal code points, - * with unfolded values that may differ from _FROM_LEAD() macro results for them. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param c32 (UChar32, in) the input code point - * @param result (uint32_t, out) uint32_t variable for the trie lookup result - */ -#define UTRIE_GET32(trie, c32, result) _UTRIE_GET(trie, data32, c32, result, uint32_t) - -/** - * Get the next code point (c, c2), post-increment src, - * and get a 16-bit value from the trie. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param src (const UChar *, in/out) the source text pointer - * @param limit (const UChar *, in) the limit pointer for the text, or NULL - * @param c (UChar, out) variable for the BMP or lead code unit - * @param c2 (UChar, out) variable for 0 or the trail code unit - * @param result (uint16_t, out) uint16_t variable for the trie lookup result - */ -#define UTRIE_NEXT16(trie, src, limit, c, c2, result) _UTRIE_NEXT(trie, index, src, limit, c, c2, result, uint16_t) - -/** - * Get the next code point (c, c2), post-increment src, - * and get a 32-bit value from the trie. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param src (const UChar *, in/out) the source text pointer - * @param limit (const UChar *, in) the limit pointer for the text, or NULL - * @param c (UChar, out) variable for the BMP or lead code unit - * @param c2 (UChar, out) variable for 0 or the trail code unit - * @param result (uint32_t, out) uint32_t variable for the trie lookup result - */ -#define UTRIE_NEXT32(trie, src, limit, c, c2, result) _UTRIE_NEXT(trie, data32, src, limit, c, c2, result, uint32_t) - -/** - * Get the previous code point (c, c2), pre-decrement src, - * and get a 16-bit value from the trie. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param start (const UChar *, in) the start pointer for the text, or NULL - * @param src (const UChar *, in/out) the source text pointer - * @param c (UChar, out) variable for the BMP or lead code unit - * @param c2 (UChar, out) variable for 0 or the trail code unit - * @param result (uint16_t, out) uint16_t variable for the trie lookup result - */ -#define UTRIE_PREVIOUS16(trie, start, src, c, c2, result) _UTRIE_PREVIOUS(trie, index, start, src, c, c2, result, uint16_t) - -/** - * Get the previous code point (c, c2), pre-decrement src, - * and get a 32-bit value from the trie. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param start (const UChar *, in) the start pointer for the text, or NULL - * @param src (const UChar *, in/out) the source text pointer - * @param c (UChar, out) variable for the BMP or lead code unit - * @param c2 (UChar, out) variable for 0 or the trail code unit - * @param result (uint32_t, out) uint32_t variable for the trie lookup result - */ -#define UTRIE_PREVIOUS32(trie, start, src, c, c2, result) _UTRIE_PREVIOUS(trie, data32, start, src, c, c2, result, uint32_t) - -/** - * Get a 16-bit trie value from a pair of surrogates. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param c (UChar, in) a lead surrogate - * @param c2 (UChar, in) a trail surrogate - * @param result (uint16_t, out) uint16_t variable for the trie lookup result - */ -#define UTRIE_GET16_FROM_PAIR(trie, c, c2, result) _UTRIE_GET_FROM_PAIR(trie, index, c, c2, result, uint16_t) - -/** - * Get a 32-bit trie value from a pair of surrogates. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param c (UChar, in) a lead surrogate - * @param c2 (UChar, in) a trail surrogate - * @param result (uint32_t, out) uint32_t variable for the trie lookup result - */ -#define UTRIE_GET32_FROM_PAIR(trie, c, c2, result) _UTRIE_GET_FROM_PAIR(trie, data32, c, c2, result, uint32_t) - -/** - * Get a 16-bit trie value from a folding offset (from the value of a lead surrogate) - * and a trail surrogate. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param offset (int32_t, in) the folding offset from the value of a lead surrogate - * @param c2 (UChar, in) a trail surrogate (only the 10 low bits are significant) - * @return (uint16_t) trie lookup result - */ -#define UTRIE_GET16_FROM_OFFSET_TRAIL(trie, offset, c2) _UTRIE_GET_RAW(trie, index, offset, (c2)&0x3ff) - -/** - * Get a 32-bit trie value from a folding offset (from the value of a lead surrogate) - * and a trail surrogate. - * - * @param trie (const UTrie *, in) a pointer to the runtime trie structure - * @param offset (int32_t, in) the folding offset from the value of a lead surrogate - * @param c2 (UChar, in) a trail surrogate (only the 10 low bits are significant) - * @return (uint32_t) trie lookup result - */ -#define UTRIE_GET32_FROM_OFFSET_TRAIL(trie, offset, c2) _UTRIE_GET_RAW(trie, data32, offset, (c2)&0x3ff) - -/* enumeration callback types */ - -/** - * Callback from utrie_enum(), extracts a uint32_t value from a - * trie value. This value will be passed on to the UTrieEnumRange function. - * - * @param context an opaque pointer, as passed into utrie_enum() - * @param value a value from the trie - * @return the value that is to be passed on to the UTrieEnumRange function - */ -typedef uint32_t U_CALLCONV -UTrieEnumValue(const void *context, uint32_t value); - -/** - * Callback from utrie_enum(), is called for each contiguous range - * of code points with the same value as retrieved from the trie and - * transformed by the UTrieEnumValue function. - * - * The callback function can stop the enumeration by returning false. - * - * @param context an opaque pointer, as passed into utrie_enum() - * @param start the first code point in a contiguous range with value - * @param limit one past the last code point in a contiguous range with value - * @param value the value that is set for all code points in [start..limit[ - * @return false to stop the enumeration - */ -typedef UBool U_CALLCONV -UTrieEnumRange(const void *context, UChar32 start, UChar32 limit, uint32_t value); - -/** - * Enumerate efficiently all values in a trie. - * For each entry in the trie, the value to be delivered is passed through - * the UTrieEnumValue function. - * The value is unchanged if that function pointer is NULL. - * - * For each contiguous range of code points with a given value, - * the UTrieEnumRange function is called. - * - * @param trie a pointer to the runtime trie structure - * @param enumValue a pointer to a function that may transform the trie entry value, - * or NULL if the values from the trie are to be used directly - * @param enumRange a pointer to a function that is called for each contiguous range - * of code points with the same value - * @param context an opaque pointer that is passed on to the callback functions - */ -U_CAPI void U_EXPORT2 -utrie_enum(const UTrie *trie, - UTrieEnumValue *enumValue, UTrieEnumRange *enumRange, const void *context); - -/** - * Unserialize a trie from 32-bit-aligned memory. - * Inverse of utrie_serialize(). - * Fills the UTrie runtime trie structure with the settings for the trie data. - * - * @param trie a pointer to the runtime trie structure - * @param data a pointer to 32-bit-aligned memory containing trie data - * @param length the number of bytes available at data - * @param pErrorCode an in/out ICU UErrorCode - * @return the number of bytes at data taken up by the trie data - */ -U_CAPI int32_t U_EXPORT2 -utrie_unserialize(UTrie *trie, const void *data, int32_t length, UErrorCode *pErrorCode); - -/** - * "Unserialize" a dummy trie. - * A dummy trie is an empty runtime trie, used when a real data trie cannot - * be loaded. - * - * The input memory is filled so that the trie always returns the initialValue, - * or the leadUnitValue for lead surrogate code points. - * The Latin-1 part is always set up to be linear. - * - * @param trie a pointer to the runtime trie structure - * @param data a pointer to 32-bit-aligned memory to be filled with the dummy trie data - * @param length the number of bytes available at data (recommended to use UTRIE_DUMMY_SIZE) - * @param initialValue the initial value that is set for all code points - * @param leadUnitValue the value for lead surrogate code _units_ that do not - * have associated supplementary data - * @param pErrorCode an in/out ICU UErrorCode - * - * @see UTRIE_DUMMY_SIZE - * @see utrie_open - */ -U_CAPI int32_t U_EXPORT2 -utrie_unserializeDummy(UTrie *trie, - void *data, int32_t length, - uint32_t initialValue, uint32_t leadUnitValue, - UBool make16BitTrie, - UErrorCode *pErrorCode); - -/** - * Default implementation for UTrie.getFoldingOffset, set automatically by - * utrie_unserialize(). - * Simply returns the lead surrogate's value itself - which is the inverse - * of the default folding function used by utrie_serialize(). - * Exported for static const UTrie structures. - * - * @see UTrieGetFoldingOffset - */ -U_CAPI int32_t U_EXPORT2 -utrie_defaultGetFoldingOffset(uint32_t data); - -/* Building a trie ----------------------------------------------------------*/ - -/** - * Build-time trie structure. - * Opaque definition, here only to make fillIn parameters possible - * for utrie_open() and utrie_clone(). - */ -struct UNewTrie { - /** - * Index values at build-time are 32 bits wide for easier processing. - * Bit 31 is set if the data block is used by multiple index values (from utrie_setRange()). - */ - int32_t index[UTRIE_MAX_INDEX_LENGTH+UTRIE_SURROGATE_BLOCK_COUNT]; - uint32_t *data; - - uint32_t leadUnitValue; - int32_t indexLength, dataCapacity, dataLength; - UBool isAllocated, isDataAllocated; - UBool isLatin1Linear, isCompacted; - - /** - * Map of adjusted indexes, used in utrie_compact(). - * Maps from original indexes to new ones. - */ - int32_t map[UTRIE_MAX_BUILD_TIME_DATA_LENGTH>>UTRIE_SHIFT]; -}; - -typedef struct UNewTrie UNewTrie; - -/** - * Build-time trie callback function, used with utrie_serialize(). - * This function calculates a lead surrogate's value including a folding offset - * from the 1024 supplementary code points [start..start+1024[ . - * It is U+10000 <= start <= U+10fc00 and (start&0x3ff)==0. - * - * The folding offset is provided by the caller. - * It is offset=UTRIE_BMP_INDEX_LENGTH+n*UTRIE_SURROGATE_BLOCK_COUNT with n=0..1023. - * Instead of the offset itself, n can be stored in 10 bits - - * or fewer if it can be assumed that few lead surrogates have associated data. - * - * The returned value must be - * - not zero if and only if there is relevant data - * for the corresponding 1024 supplementary code points - * - such that UTrie.getFoldingOffset(UNewTrieGetFoldedValue(..., offset))==offset - * - * @return a folded value, or 0 if there is no relevant data for the lead surrogate. - */ -typedef uint32_t U_CALLCONV -UNewTrieGetFoldedValue(UNewTrie *trie, UChar32 start, int32_t offset); - -/** - * Open a build-time trie structure. - * The size of the build-time data array is specified to avoid allocating a large - * array in all cases. The array itself can also be passed in. - * - * Although the trie is never fully expanded to a linear array, especially when - * utrie_setRange32() is used, the data array could be large during build time. - * The maximum length is - * UTRIE_MAX_BUILD_TIME_DATA_LENGTH=0x110000+UTRIE_DATA_BLOCK_LENGTH+0x400. - * (Number of Unicode code points + one all-initial-value block + - * possible duplicate entries for 1024 lead surrogates.) - * (UTRIE_DATA_BLOCK_LENGTH<=0x200 in all cases.) - * - * @param fillIn a pointer to a UNewTrie structure to be initialized (will not be released), or - * NULL if one is to be allocated - * @param aliasData a pointer to a data array to be used (will not be released), or - * NULL if one is to be allocated - * @param maxDataLength the capacity of aliasData (if not NULL) or - * the length of the data array to be allocated - * @param initialValue the initial value that is set for all code points - * @param leadUnitValue the value for lead surrogate code _units_ that do not - * have associated supplementary data - * @param latin1Linear a flag indicating whether the Latin-1 range is to be allocated and - * kept in a linear, contiguous part of the data array - * @return a pointer to the initialized fillIn or the allocated and initialized new UNewTrie - */ -U_CAPI UNewTrie * U_EXPORT2 -utrie_open(UNewTrie *fillIn, - uint32_t *aliasData, int32_t maxDataLength, - uint32_t initialValue, uint32_t leadUnitValue, - UBool latin1Linear); - -/** - * Clone a build-time trie structure with all entries. - * - * @param fillIn like in utrie_open() - * @param other the build-time trie structure to clone - * @param aliasData like in utrie_open(), - * used if aliasDataLength>=(capacity of other's data array) - * @param aliasDataLength the length of aliasData - * @return a pointer to the initialized fillIn or the allocated and initialized new UNewTrie - */ -U_CAPI UNewTrie * U_EXPORT2 -utrie_clone(UNewTrie *fillIn, const UNewTrie *other, uint32_t *aliasData, int32_t aliasDataLength); - -/** - * Close a build-time trie structure, and release memory - * that was allocated by utrie_open() or utrie_clone(). - * - * @param trie the build-time trie - */ -U_CAPI void U_EXPORT2 -utrie_close(UNewTrie *trie); - -/** - * Get the data array of a build-time trie. - * The data may be modified, but entries that are equal before - * must still be equal after modification. - * - * @param trie the build-time trie - * @param pLength (out) a pointer to a variable that receives the number - * of entries in the data array - * @return the data array - */ -U_CAPI uint32_t * U_EXPORT2 -utrie_getData(UNewTrie *trie, int32_t *pLength); - -/** - * Set a value for a code point. - * - * @param trie the build-time trie - * @param c the code point - * @param value the value - * @return false if a failure occurred (illegal argument or data array overrun) - */ -U_CAPI UBool U_EXPORT2 -utrie_set32(UNewTrie *trie, UChar32 c, uint32_t value); - -/** - * Get a value from a code point as stored in the build-time trie. - * - * @param trie the build-time trie - * @param c the code point - * @param pInBlockZero if not NULL, then *pInBlockZero is set to true - * iff the value is retrieved from block 0; - * block 0 is the all-initial-value initial block - * @return the value - */ -U_CAPI uint32_t U_EXPORT2 -utrie_get32(UNewTrie *trie, UChar32 c, UBool *pInBlockZero); - -/** - * Set a value in a range of code points [start..limit[. - * All code points c with start<=c=UTRIE_DATA_BLOCK_LENGTH */ - int32_t dataLength; -} UTrieHeader; - -/** - * Constants for use with UTrieHeader.options. - * @internal - */ -enum { - /** Mask to get the UTRIE_SHIFT value from options. */ - UTRIE_OPTIONS_SHIFT_MASK=0xf, - - /** Shift options right this much to get the UTRIE_INDEX_SHIFT value. */ - UTRIE_OPTIONS_INDEX_SHIFT=4, - - /** If set, then the data (stage 2) array is 32 bits wide. */ - UTRIE_OPTIONS_DATA_IS_32_BIT=0x100, - - /** - * If set, then Latin-1 data (for U+0000..U+00ff) is stored in the data (stage 2) array - * as a simple, linear array at data+UTRIE_DATA_BLOCK_LENGTH. - */ - UTRIE_OPTIONS_LATIN1_IS_LINEAR=0x200 -}; - -U_CDECL_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2001-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: utrie.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2001nov08 +* created by: Markus W. Scherer +*/ + +#ifndef __UTRIE_H__ +#define __UTRIE_H__ + +#include "unicode/utypes.h" +#include "unicode/utf16.h" + +U_CDECL_BEGIN + +/** + * \file + * + * This is a common implementation of a "folded" trie. + * It is a kind of compressed, serializable table of 16- or 32-bit values associated with + * Unicode code points (0..0x10ffff). + * + * This implementation is optimized for getting values while walking forward + * through a UTF-16 string. + * Therefore, the simplest and fastest access macros are the + * _FROM_LEAD() and _FROM_OFFSET_TRAIL() macros. + * + * The _FROM_BMP() macros are a little more complicated; they get values + * even for lead surrogate code _points_, while the _FROM_LEAD() macros + * get special "folded" values for lead surrogate code _units_ if + * there is relevant data associated with them. + * From such a folded value, an offset needs to be extracted to supply + * to the _FROM_OFFSET_TRAIL() macros. + * + * Most of the more complex (and more convenient) functions/macros call a callback function + * to get that offset from the folded value for a lead surrogate unit. + */ + +/** + * Trie constants, defining shift widths, index array lengths, etc. + */ +enum { + /** Shift size for shifting right the input index. 1..9 */ + UTRIE_SHIFT=5, + + /** Number of data values in a stage 2 (data array) block. 2, 4, 8, .., 0x200 */ + UTRIE_DATA_BLOCK_LENGTH=1<>UTRIE_SHIFT, + + /** + * Shift size for shifting left the index array values. + * Increases possible data size with 16-bit index values at the cost + * of compactability. + * This requires blocks of stage 2 data to be aligned by UTRIE_DATA_GRANULARITY. + * 0..UTRIE_SHIFT + */ + UTRIE_INDEX_SHIFT=2, + + /** The alignment size of a stage 2 data block. Also the granularity for compaction. */ + UTRIE_DATA_GRANULARITY=1<>UTRIE_SHIFT + */ + UTRIE_SURROGATE_BLOCK_COUNT=(1<>UTRIE_SHIFT +}; + +/** + * Length of the index (stage 1) array before folding. + * Maximum number of Unicode code points (0x110000) shifted right by UTRIE_SHIFT. + */ +#define UTRIE_MAX_INDEX_LENGTH (0x110000>>UTRIE_SHIFT) + +/** + * Maximum length of the runtime data (stage 2) array. + * Limited by 16-bit index values that are left-shifted by UTRIE_INDEX_SHIFT. + */ +#define UTRIE_MAX_DATA_LENGTH (0x10000<=UTRIE_BMP_INDEX_LENGTH, or 0 if there is no data for the lead surrogate + */ +typedef int32_t U_CALLCONV +UTrieGetFoldingOffset(uint32_t data); + +/** + * Run-time Trie structure. + * + * Either the data table is 16 bits wide and accessed via the index + * pointer, with each index item increased by indexLength; + * in this case, data32==NULL. + * + * Or the data table is 32 bits wide and accessed via the data32 pointer. + */ +struct UTrie { + const uint16_t *index; + const uint32_t *data32; /* NULL if 16b data is used via index */ + + /** + * This function is not used in _FROM_LEAD, _FROM_BMP, and _FROM_OFFSET_TRAIL macros. + * If convenience macros like _GET16 or _NEXT32 are used, this function must be set. + * + * utrie_unserialize() sets a default function which simply returns + * the lead surrogate's value itself - which is the inverse of the default + * folding function used by utrie_serialize(). + * + * @see UTrieGetFoldingOffset + */ + UTrieGetFoldingOffset *getFoldingOffset; + + int32_t indexLength, dataLength; + uint32_t initialValue; + UBool isLatin1Linear; +}; + +#ifndef __UTRIE2_H__ +typedef struct UTrie UTrie; +#endif + +/** Internal trie getter from an offset (0 if c16 is a BMP/lead units) and a 16-bit unit */ +#define _UTRIE_GET_RAW(trie, data, offset, c16) \ + (trie)->data[ \ + ((int32_t)((trie)->index[(offset)+((c16)>>UTRIE_SHIFT)])<getFoldingOffset(result); \ +\ + /* get the real data from the folded lead/trail units */ \ + if(__offset>0) { \ + (result)=_UTRIE_GET_RAW((trie), data, __offset, (c2)&0x3ff); \ + } else { \ + (result)=(resultType)((trie)->initialValue); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** Internal trie getter from a BMP code point, treating a lead surrogate as a normal code point */ +#define _UTRIE_GET_FROM_BMP(trie, data, c16) \ + _UTRIE_GET_RAW(trie, data, 0xd800<=(c16) && (c16)<=0xdbff ? UTRIE_LEAD_INDEX_DISP : 0, c16) + +/** + * Internal trie getter from a code point. + * Could be faster(?) but longer with + * if((c32)<=0xd7ff) { (result)=_UTRIE_GET_RAW(trie, data, 0, c32); } + */ +#define _UTRIE_GET(trie, data, c32, result, resultType) UPRV_BLOCK_MACRO_BEGIN { \ + if((uint32_t)(c32)<=0xffff) { \ + /* BMP code points */ \ + (result)=_UTRIE_GET_FROM_BMP(trie, data, c32); \ + } else if((uint32_t)(c32)<=0x10ffff) { \ + /* supplementary code point */ \ + UChar __lead16=U16_LEAD(c32); \ + _UTRIE_GET_FROM_PAIR(trie, data, __lead16, c32, result, resultType); \ + } else { \ + /* out of range */ \ + (result)=(resultType)((trie)->initialValue); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** Internal next-post-increment: get the next code point (c, c2) and its data */ +#define _UTRIE_NEXT(trie, data, src, limit, c, c2, result, resultType) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=*(src)++; \ + if(!U16_IS_LEAD(c)) { \ + (c2)=0; \ + (result)=_UTRIE_GET_RAW((trie), data, 0, (c)); \ + } else if((src)!=(limit) && U16_IS_TRAIL((c2)=*(src))) { \ + ++(src); \ + _UTRIE_GET_FROM_PAIR((trie), data, (c), (c2), (result), resultType); \ + } else { \ + /* unpaired lead surrogate code point */ \ + (c2)=0; \ + (result)=_UTRIE_GET_RAW((trie), data, UTRIE_LEAD_INDEX_DISP, (c)); \ + } \ +} UPRV_BLOCK_MACRO_END + +/** Internal previous: get the previous code point (c, c2) and its data */ +#define _UTRIE_PREVIOUS(trie, data, start, src, c, c2, result, resultType) UPRV_BLOCK_MACRO_BEGIN { \ + (c)=*--(src); \ + if(!U16_IS_SURROGATE(c)) { \ + (c2)=0; \ + (result)=_UTRIE_GET_RAW((trie), data, 0, (c)); \ + } else if(!U16_IS_SURROGATE_LEAD(c)) { \ + /* trail surrogate */ \ + if((start)!=(src) && U16_IS_LEAD((c2)=*((src)-1))) { \ + --(src); \ + (result)=(c); (c)=(c2); (c2)=(UChar)(result); /* swap c, c2 */ \ + _UTRIE_GET_FROM_PAIR((trie), data, (c), (c2), (result), resultType); \ + } else { \ + /* unpaired trail surrogate code point */ \ + (c2)=0; \ + (result)=_UTRIE_GET_RAW((trie), data, 0, (c)); \ + } \ + } else { \ + /* unpaired lead surrogate code point */ \ + (c2)=0; \ + (result)=_UTRIE_GET_RAW((trie), data, UTRIE_LEAD_INDEX_DISP, (c)); \ + } \ +} UPRV_BLOCK_MACRO_END + +/* Public UTrie API ---------------------------------------------------------*/ + +/** + * Get a pointer to the contiguous part of the data array + * for the Latin-1 range (U+0000..U+00ff). + * Must be used only if the Latin-1 range is in fact linear + * (trie->isLatin1Linear). + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @return (const uint16_t *) pointer to values for Latin-1 code points + */ +#define UTRIE_GET16_LATIN1(trie) ((trie)->index+(trie)->indexLength+UTRIE_DATA_BLOCK_LENGTH) + +/** + * Get a pointer to the contiguous part of the data array + * for the Latin-1 range (U+0000..U+00ff). + * Must be used only if the Latin-1 range is in fact linear + * (trie->isLatin1Linear). + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @return (const uint32_t *) pointer to values for Latin-1 code points + */ +#define UTRIE_GET32_LATIN1(trie) ((trie)->data32+UTRIE_DATA_BLOCK_LENGTH) + +/** + * Get a 16-bit trie value from a BMP code point (UChar, <=U+ffff). + * c16 may be a lead surrogate, which may have a value including a folding offset. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param c16 (UChar, in) the input BMP code point + * @return (uint16_t) trie lookup result + */ +#define UTRIE_GET16_FROM_LEAD(trie, c16) _UTRIE_GET_RAW(trie, index, 0, c16) + +/** + * Get a 32-bit trie value from a BMP code point (UChar, <=U+ffff). + * c16 may be a lead surrogate, which may have a value including a folding offset. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param c16 (UChar, in) the input BMP code point + * @return (uint32_t) trie lookup result + */ +#define UTRIE_GET32_FROM_LEAD(trie, c16) _UTRIE_GET_RAW(trie, data32, 0, c16) + +/** + * Get a 16-bit trie value from a BMP code point (UChar, <=U+ffff). + * Even lead surrogate code points are treated as normal code points, + * with unfolded values that may differ from _FROM_LEAD() macro results for them. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param c16 (UChar, in) the input BMP code point + * @return (uint16_t) trie lookup result + */ +#define UTRIE_GET16_FROM_BMP(trie, c16) _UTRIE_GET_FROM_BMP(trie, index, c16) + +/** + * Get a 32-bit trie value from a BMP code point (UChar, <=U+ffff). + * Even lead surrogate code points are treated as normal code points, + * with unfolded values that may differ from _FROM_LEAD() macro results for them. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param c16 (UChar, in) the input BMP code point + * @return (uint32_t) trie lookup result + */ +#define UTRIE_GET32_FROM_BMP(trie, c16) _UTRIE_GET_FROM_BMP(trie, data32, c16) + +/** + * Get a 16-bit trie value from a code point. + * Even lead surrogate code points are treated as normal code points, + * with unfolded values that may differ from _FROM_LEAD() macro results for them. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param c32 (UChar32, in) the input code point + * @param result (uint16_t, out) uint16_t variable for the trie lookup result + */ +#define UTRIE_GET16(trie, c32, result) _UTRIE_GET(trie, index, c32, result, uint16_t) + +/** + * Get a 32-bit trie value from a code point. + * Even lead surrogate code points are treated as normal code points, + * with unfolded values that may differ from _FROM_LEAD() macro results for them. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param c32 (UChar32, in) the input code point + * @param result (uint32_t, out) uint32_t variable for the trie lookup result + */ +#define UTRIE_GET32(trie, c32, result) _UTRIE_GET(trie, data32, c32, result, uint32_t) + +/** + * Get the next code point (c, c2), post-increment src, + * and get a 16-bit value from the trie. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param src (const UChar *, in/out) the source text pointer + * @param limit (const UChar *, in) the limit pointer for the text, or NULL + * @param c (UChar, out) variable for the BMP or lead code unit + * @param c2 (UChar, out) variable for 0 or the trail code unit + * @param result (uint16_t, out) uint16_t variable for the trie lookup result + */ +#define UTRIE_NEXT16(trie, src, limit, c, c2, result) _UTRIE_NEXT(trie, index, src, limit, c, c2, result, uint16_t) + +/** + * Get the next code point (c, c2), post-increment src, + * and get a 32-bit value from the trie. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param src (const UChar *, in/out) the source text pointer + * @param limit (const UChar *, in) the limit pointer for the text, or NULL + * @param c (UChar, out) variable for the BMP or lead code unit + * @param c2 (UChar, out) variable for 0 or the trail code unit + * @param result (uint32_t, out) uint32_t variable for the trie lookup result + */ +#define UTRIE_NEXT32(trie, src, limit, c, c2, result) _UTRIE_NEXT(trie, data32, src, limit, c, c2, result, uint32_t) + +/** + * Get the previous code point (c, c2), pre-decrement src, + * and get a 16-bit value from the trie. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param start (const UChar *, in) the start pointer for the text, or NULL + * @param src (const UChar *, in/out) the source text pointer + * @param c (UChar, out) variable for the BMP or lead code unit + * @param c2 (UChar, out) variable for 0 or the trail code unit + * @param result (uint16_t, out) uint16_t variable for the trie lookup result + */ +#define UTRIE_PREVIOUS16(trie, start, src, c, c2, result) _UTRIE_PREVIOUS(trie, index, start, src, c, c2, result, uint16_t) + +/** + * Get the previous code point (c, c2), pre-decrement src, + * and get a 32-bit value from the trie. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param start (const UChar *, in) the start pointer for the text, or NULL + * @param src (const UChar *, in/out) the source text pointer + * @param c (UChar, out) variable for the BMP or lead code unit + * @param c2 (UChar, out) variable for 0 or the trail code unit + * @param result (uint32_t, out) uint32_t variable for the trie lookup result + */ +#define UTRIE_PREVIOUS32(trie, start, src, c, c2, result) _UTRIE_PREVIOUS(trie, data32, start, src, c, c2, result, uint32_t) + +/** + * Get a 16-bit trie value from a pair of surrogates. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param c (UChar, in) a lead surrogate + * @param c2 (UChar, in) a trail surrogate + * @param result (uint16_t, out) uint16_t variable for the trie lookup result + */ +#define UTRIE_GET16_FROM_PAIR(trie, c, c2, result) _UTRIE_GET_FROM_PAIR(trie, index, c, c2, result, uint16_t) + +/** + * Get a 32-bit trie value from a pair of surrogates. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param c (UChar, in) a lead surrogate + * @param c2 (UChar, in) a trail surrogate + * @param result (uint32_t, out) uint32_t variable for the trie lookup result + */ +#define UTRIE_GET32_FROM_PAIR(trie, c, c2, result) _UTRIE_GET_FROM_PAIR(trie, data32, c, c2, result, uint32_t) + +/** + * Get a 16-bit trie value from a folding offset (from the value of a lead surrogate) + * and a trail surrogate. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param offset (int32_t, in) the folding offset from the value of a lead surrogate + * @param c2 (UChar, in) a trail surrogate (only the 10 low bits are significant) + * @return (uint16_t) trie lookup result + */ +#define UTRIE_GET16_FROM_OFFSET_TRAIL(trie, offset, c2) _UTRIE_GET_RAW(trie, index, offset, (c2)&0x3ff) + +/** + * Get a 32-bit trie value from a folding offset (from the value of a lead surrogate) + * and a trail surrogate. + * + * @param trie (const UTrie *, in) a pointer to the runtime trie structure + * @param offset (int32_t, in) the folding offset from the value of a lead surrogate + * @param c2 (UChar, in) a trail surrogate (only the 10 low bits are significant) + * @return (uint32_t) trie lookup result + */ +#define UTRIE_GET32_FROM_OFFSET_TRAIL(trie, offset, c2) _UTRIE_GET_RAW(trie, data32, offset, (c2)&0x3ff) + +/* enumeration callback types */ + +/** + * Callback from utrie_enum(), extracts a uint32_t value from a + * trie value. This value will be passed on to the UTrieEnumRange function. + * + * @param context an opaque pointer, as passed into utrie_enum() + * @param value a value from the trie + * @return the value that is to be passed on to the UTrieEnumRange function + */ +typedef uint32_t U_CALLCONV +UTrieEnumValue(const void *context, uint32_t value); + +/** + * Callback from utrie_enum(), is called for each contiguous range + * of code points with the same value as retrieved from the trie and + * transformed by the UTrieEnumValue function. + * + * The callback function can stop the enumeration by returning false. + * + * @param context an opaque pointer, as passed into utrie_enum() + * @param start the first code point in a contiguous range with value + * @param limit one past the last code point in a contiguous range with value + * @param value the value that is set for all code points in [start..limit[ + * @return false to stop the enumeration + */ +typedef UBool U_CALLCONV +UTrieEnumRange(const void *context, UChar32 start, UChar32 limit, uint32_t value); + +/** + * Enumerate efficiently all values in a trie. + * For each entry in the trie, the value to be delivered is passed through + * the UTrieEnumValue function. + * The value is unchanged if that function pointer is NULL. + * + * For each contiguous range of code points with a given value, + * the UTrieEnumRange function is called. + * + * @param trie a pointer to the runtime trie structure + * @param enumValue a pointer to a function that may transform the trie entry value, + * or NULL if the values from the trie are to be used directly + * @param enumRange a pointer to a function that is called for each contiguous range + * of code points with the same value + * @param context an opaque pointer that is passed on to the callback functions + */ +U_CAPI void U_EXPORT2 +utrie_enum(const UTrie *trie, + UTrieEnumValue *enumValue, UTrieEnumRange *enumRange, const void *context); + +/** + * Unserialize a trie from 32-bit-aligned memory. + * Inverse of utrie_serialize(). + * Fills the UTrie runtime trie structure with the settings for the trie data. + * + * @param trie a pointer to the runtime trie structure + * @param data a pointer to 32-bit-aligned memory containing trie data + * @param length the number of bytes available at data + * @param pErrorCode an in/out ICU UErrorCode + * @return the number of bytes at data taken up by the trie data + */ +U_CAPI int32_t U_EXPORT2 +utrie_unserialize(UTrie *trie, const void *data, int32_t length, UErrorCode *pErrorCode); + +/** + * "Unserialize" a dummy trie. + * A dummy trie is an empty runtime trie, used when a real data trie cannot + * be loaded. + * + * The input memory is filled so that the trie always returns the initialValue, + * or the leadUnitValue for lead surrogate code points. + * The Latin-1 part is always set up to be linear. + * + * @param trie a pointer to the runtime trie structure + * @param data a pointer to 32-bit-aligned memory to be filled with the dummy trie data + * @param length the number of bytes available at data (recommended to use UTRIE_DUMMY_SIZE) + * @param initialValue the initial value that is set for all code points + * @param leadUnitValue the value for lead surrogate code _units_ that do not + * have associated supplementary data + * @param pErrorCode an in/out ICU UErrorCode + * + * @see UTRIE_DUMMY_SIZE + * @see utrie_open + */ +U_CAPI int32_t U_EXPORT2 +utrie_unserializeDummy(UTrie *trie, + void *data, int32_t length, + uint32_t initialValue, uint32_t leadUnitValue, + UBool make16BitTrie, + UErrorCode *pErrorCode); + +/** + * Default implementation for UTrie.getFoldingOffset, set automatically by + * utrie_unserialize(). + * Simply returns the lead surrogate's value itself - which is the inverse + * of the default folding function used by utrie_serialize(). + * Exported for static const UTrie structures. + * + * @see UTrieGetFoldingOffset + */ +U_CAPI int32_t U_EXPORT2 +utrie_defaultGetFoldingOffset(uint32_t data); + +/* Building a trie ----------------------------------------------------------*/ + +/** + * Build-time trie structure. + * Opaque definition, here only to make fillIn parameters possible + * for utrie_open() and utrie_clone(). + */ +struct UNewTrie { + /** + * Index values at build-time are 32 bits wide for easier processing. + * Bit 31 is set if the data block is used by multiple index values (from utrie_setRange()). + */ + int32_t index[UTRIE_MAX_INDEX_LENGTH+UTRIE_SURROGATE_BLOCK_COUNT]; + uint32_t *data; + + uint32_t leadUnitValue; + int32_t indexLength, dataCapacity, dataLength; + UBool isAllocated, isDataAllocated; + UBool isLatin1Linear, isCompacted; + + /** + * Map of adjusted indexes, used in utrie_compact(). + * Maps from original indexes to new ones. + */ + int32_t map[UTRIE_MAX_BUILD_TIME_DATA_LENGTH>>UTRIE_SHIFT]; +}; + +typedef struct UNewTrie UNewTrie; + +/** + * Build-time trie callback function, used with utrie_serialize(). + * This function calculates a lead surrogate's value including a folding offset + * from the 1024 supplementary code points [start..start+1024[ . + * It is U+10000 <= start <= U+10fc00 and (start&0x3ff)==0. + * + * The folding offset is provided by the caller. + * It is offset=UTRIE_BMP_INDEX_LENGTH+n*UTRIE_SURROGATE_BLOCK_COUNT with n=0..1023. + * Instead of the offset itself, n can be stored in 10 bits - + * or fewer if it can be assumed that few lead surrogates have associated data. + * + * The returned value must be + * - not zero if and only if there is relevant data + * for the corresponding 1024 supplementary code points + * - such that UTrie.getFoldingOffset(UNewTrieGetFoldedValue(..., offset))==offset + * + * @return a folded value, or 0 if there is no relevant data for the lead surrogate. + */ +typedef uint32_t U_CALLCONV +UNewTrieGetFoldedValue(UNewTrie *trie, UChar32 start, int32_t offset); + +/** + * Open a build-time trie structure. + * The size of the build-time data array is specified to avoid allocating a large + * array in all cases. The array itself can also be passed in. + * + * Although the trie is never fully expanded to a linear array, especially when + * utrie_setRange32() is used, the data array could be large during build time. + * The maximum length is + * UTRIE_MAX_BUILD_TIME_DATA_LENGTH=0x110000+UTRIE_DATA_BLOCK_LENGTH+0x400. + * (Number of Unicode code points + one all-initial-value block + + * possible duplicate entries for 1024 lead surrogates.) + * (UTRIE_DATA_BLOCK_LENGTH<=0x200 in all cases.) + * + * @param fillIn a pointer to a UNewTrie structure to be initialized (will not be released), or + * NULL if one is to be allocated + * @param aliasData a pointer to a data array to be used (will not be released), or + * NULL if one is to be allocated + * @param maxDataLength the capacity of aliasData (if not NULL) or + * the length of the data array to be allocated + * @param initialValue the initial value that is set for all code points + * @param leadUnitValue the value for lead surrogate code _units_ that do not + * have associated supplementary data + * @param latin1Linear a flag indicating whether the Latin-1 range is to be allocated and + * kept in a linear, contiguous part of the data array + * @return a pointer to the initialized fillIn or the allocated and initialized new UNewTrie + */ +U_CAPI UNewTrie * U_EXPORT2 +utrie_open(UNewTrie *fillIn, + uint32_t *aliasData, int32_t maxDataLength, + uint32_t initialValue, uint32_t leadUnitValue, + UBool latin1Linear); + +/** + * Clone a build-time trie structure with all entries. + * + * @param fillIn like in utrie_open() + * @param other the build-time trie structure to clone + * @param aliasData like in utrie_open(), + * used if aliasDataLength>=(capacity of other's data array) + * @param aliasDataLength the length of aliasData + * @return a pointer to the initialized fillIn or the allocated and initialized new UNewTrie + */ +U_CAPI UNewTrie * U_EXPORT2 +utrie_clone(UNewTrie *fillIn, const UNewTrie *other, uint32_t *aliasData, int32_t aliasDataLength); + +/** + * Close a build-time trie structure, and release memory + * that was allocated by utrie_open() or utrie_clone(). + * + * @param trie the build-time trie + */ +U_CAPI void U_EXPORT2 +utrie_close(UNewTrie *trie); + +/** + * Get the data array of a build-time trie. + * The data may be modified, but entries that are equal before + * must still be equal after modification. + * + * @param trie the build-time trie + * @param pLength (out) a pointer to a variable that receives the number + * of entries in the data array + * @return the data array + */ +U_CAPI uint32_t * U_EXPORT2 +utrie_getData(UNewTrie *trie, int32_t *pLength); + +/** + * Set a value for a code point. + * + * @param trie the build-time trie + * @param c the code point + * @param value the value + * @return false if a failure occurred (illegal argument or data array overrun) + */ +U_CAPI UBool U_EXPORT2 +utrie_set32(UNewTrie *trie, UChar32 c, uint32_t value); + +/** + * Get a value from a code point as stored in the build-time trie. + * + * @param trie the build-time trie + * @param c the code point + * @param pInBlockZero if not NULL, then *pInBlockZero is set to true + * iff the value is retrieved from block 0; + * block 0 is the all-initial-value initial block + * @return the value + */ +U_CAPI uint32_t U_EXPORT2 +utrie_get32(UNewTrie *trie, UChar32 c, UBool *pInBlockZero); + +/** + * Set a value in a range of code points [start..limit[. + * All code points c with start<=c=UTRIE_DATA_BLOCK_LENGTH */ + int32_t dataLength; +} UTrieHeader; + +/** + * Constants for use with UTrieHeader.options. + * @internal + */ +enum { + /** Mask to get the UTRIE_SHIFT value from options. */ + UTRIE_OPTIONS_SHIFT_MASK=0xf, + + /** Shift options right this much to get the UTRIE_INDEX_SHIFT value. */ + UTRIE_OPTIONS_INDEX_SHIFT=4, + + /** If set, then the data (stage 2) array is 32 bits wide. */ + UTRIE_OPTIONS_DATA_IS_32_BIT=0x100, + + /** + * If set, then Latin-1 data (for U+0000..U+00ff) is stored in the data (stage 2) array + * as a simple, linear array at data+UTRIE_DATA_BLOCK_LENGTH. + */ + UTRIE_OPTIONS_LATIN1_IS_LINEAR=0x200 +}; + +U_CDECL_END + +#endif diff --git a/deps/icu-small/source/common/utrie2.cpp b/deps/icu-small/source/common/utrie2.cpp index 0fb74ba1c3688c..a78f4fcdc0ae59 100644 --- a/deps/icu-small/source/common/utrie2.cpp +++ b/deps/icu-small/source/common/utrie2.cpp @@ -1,663 +1,663 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2001-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: utrie2.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2008aug16 (starting from a copy of utrie.c) -* created by: Markus W. Scherer -* -* This is a common implementation of a Unicode trie. -* It is a kind of compressed, serializable table of 16- or 32-bit values associated with -* Unicode code points (0..0x10ffff). -* This is the second common version of a Unicode trie (hence the name UTrie2). -* See utrie2.h for a comparison. -* -* This file contains only the runtime and enumeration code, for read-only access. -* See utrie2_builder.c for the builder code. -*/ -#include "unicode/utypes.h" -#ifdef UCPTRIE_DEBUG -#include "unicode/umutablecptrie.h" -#endif -#include "unicode/utf.h" -#include "unicode/utf8.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "utrie2.h" -#include "utrie2_impl.h" -#include "uassert.h" - -/* Public UTrie2 API implementation ----------------------------------------- */ - -static uint32_t -get32(const UNewTrie2 *trie, UChar32 c, UBool fromLSCP) { - int32_t i2, block; - - if(c>=trie->highStart && (!U_IS_LEAD(c) || fromLSCP)) { - return trie->data[trie->dataLength-UTRIE2_DATA_GRANULARITY]; - } - - if(U_IS_LEAD(c) && fromLSCP) { - i2=(UTRIE2_LSCP_INDEX_2_OFFSET-(0xd800>>UTRIE2_SHIFT_2))+ - (c>>UTRIE2_SHIFT_2); - } else { - i2=trie->index1[c>>UTRIE2_SHIFT_1]+ - ((c>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK); - } - block=trie->index2[i2]; - return trie->data[block+(c&UTRIE2_DATA_MASK)]; -} - -U_CAPI uint32_t U_EXPORT2 -utrie2_get32(const UTrie2 *trie, UChar32 c) { - if(trie->data16!=NULL) { - return UTRIE2_GET16(trie, c); - } else if(trie->data32!=NULL) { - return UTRIE2_GET32(trie, c); - } else if((uint32_t)c>0x10ffff) { - return trie->errorValue; - } else { - return get32(trie->newTrie, c, true); - } -} - -U_CAPI uint32_t U_EXPORT2 -utrie2_get32FromLeadSurrogateCodeUnit(const UTrie2 *trie, UChar32 c) { - if(!U_IS_LEAD(c)) { - return trie->errorValue; - } - if(trie->data16!=NULL) { - return UTRIE2_GET16_FROM_U16_SINGLE_LEAD(trie, c); - } else if(trie->data32!=NULL) { - return UTRIE2_GET32_FROM_U16_SINGLE_LEAD(trie, c); - } else { - return get32(trie->newTrie, c, false); - } -} - -static inline int32_t -u8Index(const UTrie2 *trie, UChar32 c, int32_t i) { - int32_t idx= - _UTRIE2_INDEX_FROM_CP( - trie, - trie->data32==NULL ? trie->indexLength : 0, - c); - return (idx<<3)|i; -} - -U_CAPI int32_t U_EXPORT2 -utrie2_internalU8NextIndex(const UTrie2 *trie, UChar32 c, - const uint8_t *src, const uint8_t *limit) { - int32_t i, length; - i=0; - /* support 64-bit pointers by avoiding cast of arbitrary difference */ - if((limit-src)<=7) { - length=(int32_t)(limit-src); - } else { - length=7; - } - c=utf8_nextCharSafeBody(src, &i, length, c, -1); - return u8Index(trie, c, i); -} - -U_CAPI int32_t U_EXPORT2 -utrie2_internalU8PrevIndex(const UTrie2 *trie, UChar32 c, - const uint8_t *start, const uint8_t *src) { - int32_t i, length; - /* support 64-bit pointers by avoiding cast of arbitrary difference */ - if((src-start)<=7) { - i=length=(int32_t)(src-start); - } else { - i=length=7; - start=src-7; - } - c=utf8_prevCharSafeBody(start, 0, &i, c, -1); - i=length-i; /* number of bytes read backward from src */ - return u8Index(trie, c, i); -} - -U_CAPI UTrie2 * U_EXPORT2 -utrie2_openFromSerialized(UTrie2ValueBits valueBits, - const void *data, int32_t length, int32_t *pActualLength, - UErrorCode *pErrorCode) { - const UTrie2Header *header; - const uint16_t *p16; - int32_t actualLength; - - UTrie2 tempTrie; - UTrie2 *trie; - - if(U_FAILURE(*pErrorCode)) { - return 0; - } - - if( length<=0 || (U_POINTER_MASK_LSB(data, 3)!=0) || - valueBits<0 || UTRIE2_COUNT_VALUE_BITS<=valueBits - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* enough data for a trie header? */ - if(length<(int32_t)sizeof(UTrie2Header)) { - *pErrorCode=U_INVALID_FORMAT_ERROR; - return 0; - } - - /* check the signature */ - header=(const UTrie2Header *)data; - if(header->signature!=UTRIE2_SIG) { - *pErrorCode=U_INVALID_FORMAT_ERROR; - return 0; - } - - /* get the options */ - if(valueBits!=(UTrie2ValueBits)(header->options&UTRIE2_OPTIONS_VALUE_BITS_MASK)) { - *pErrorCode=U_INVALID_FORMAT_ERROR; - return 0; - } - - /* get the length values and offsets */ - uprv_memset(&tempTrie, 0, sizeof(tempTrie)); - tempTrie.indexLength=header->indexLength; - tempTrie.dataLength=header->shiftedDataLength<index2NullOffset; - tempTrie.dataNullOffset=header->dataNullOffset; - - tempTrie.highStart=header->shiftedHighStart<memory=(uint32_t *)data; - trie->length=actualLength; - trie->isMemoryOwned=false; -#ifdef UTRIE2_DEBUG - trie->name="fromSerialized"; -#endif - - /* set the pointers to its index and data arrays */ - p16=(const uint16_t *)(header+1); - trie->index=p16; - p16+=trie->indexLength; - - /* get the data */ - switch(valueBits) { - case UTRIE2_16_VALUE_BITS: - trie->data16=p16; - trie->data32=NULL; - trie->initialValue=trie->index[trie->dataNullOffset]; - trie->errorValue=trie->data16[UTRIE2_BAD_UTF8_DATA_OFFSET]; - break; - case UTRIE2_32_VALUE_BITS: - trie->data16=NULL; - trie->data32=(const uint32_t *)p16; - trie->initialValue=trie->data32[trie->dataNullOffset]; - trie->errorValue=trie->data32[UTRIE2_BAD_UTF8_DATA_OFFSET]; - break; - default: - *pErrorCode=U_INVALID_FORMAT_ERROR; - return 0; - } - - if(pActualLength!=NULL) { - *pActualLength=actualLength; - } - return trie; -} - -U_CAPI UTrie2 * U_EXPORT2 -utrie2_openDummy(UTrie2ValueBits valueBits, - uint32_t initialValue, uint32_t errorValue, - UErrorCode *pErrorCode) { - UTrie2 *trie; - UTrie2Header *header; - uint32_t *p; - uint16_t *dest16; - int32_t indexLength, dataLength, length, i; - int32_t dataMove; /* >0 if the data is moved to the end of the index array */ - - if(U_FAILURE(*pErrorCode)) { - return 0; - } - - if(valueBits<0 || UTRIE2_COUNT_VALUE_BITS<=valueBits) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* calculate the total length of the dummy trie data */ - indexLength=UTRIE2_INDEX_1_OFFSET; - dataLength=UTRIE2_DATA_START_OFFSET+UTRIE2_DATA_GRANULARITY; - length=(int32_t)sizeof(UTrie2Header)+indexLength*2; - if(valueBits==UTRIE2_16_VALUE_BITS) { - length+=dataLength*2; - } else { - length+=dataLength*4; - } - - /* allocate the trie */ - trie=(UTrie2 *)uprv_malloc(sizeof(UTrie2)); - if(trie==NULL) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return 0; - } - uprv_memset(trie, 0, sizeof(UTrie2)); - trie->memory=uprv_malloc(length); - if(trie->memory==NULL) { - uprv_free(trie); - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return 0; - } - trie->length=length; - trie->isMemoryOwned=true; - - /* set the UTrie2 fields */ - if(valueBits==UTRIE2_16_VALUE_BITS) { - dataMove=indexLength; - } else { - dataMove=0; - } - - trie->indexLength=indexLength; - trie->dataLength=dataLength; - trie->index2NullOffset=UTRIE2_INDEX_2_OFFSET; - trie->dataNullOffset=(uint16_t)dataMove; - trie->initialValue=initialValue; - trie->errorValue=errorValue; - trie->highStart=0; - trie->highValueIndex=dataMove+UTRIE2_DATA_START_OFFSET; -#ifdef UTRIE2_DEBUG - trie->name="dummy"; -#endif - - /* set the header fields */ - header=(UTrie2Header *)trie->memory; - - header->signature=UTRIE2_SIG; /* "Tri2" */ - header->options=(uint16_t)valueBits; - - header->indexLength=(uint16_t)indexLength; - header->shiftedDataLength=(uint16_t)(dataLength>>UTRIE2_INDEX_SHIFT); - header->index2NullOffset=(uint16_t)UTRIE2_INDEX_2_OFFSET; - header->dataNullOffset=(uint16_t)dataMove; - header->shiftedHighStart=0; - - /* fill the index and data arrays */ - dest16=(uint16_t *)(header+1); - trie->index=dest16; - - /* write the index-2 array values shifted right by UTRIE2_INDEX_SHIFT */ - for(i=0; i>UTRIE2_INDEX_SHIFT); /* null data block */ - } - - /* write UTF-8 2-byte index-2 values, not right-shifted */ - for(i=0; i<(0xc2-0xc0); ++i) { /* C0..C1 */ - *dest16++=(uint16_t)(dataMove+UTRIE2_BAD_UTF8_DATA_OFFSET); - } - for(; i<(0xe0-0xc0); ++i) { /* C2..DF */ - *dest16++=(uint16_t)dataMove; - } - - /* write the 16/32-bit data array */ - switch(valueBits) { - case UTRIE2_16_VALUE_BITS: - /* write 16-bit data values */ - trie->data16=dest16; - trie->data32=NULL; - for(i=0; i<0x80; ++i) { - *dest16++=(uint16_t)initialValue; - } - for(; i<0xc0; ++i) { - *dest16++=(uint16_t)errorValue; - } - /* highValue and reserved values */ - for(i=0; idata16=NULL; - trie->data32=p; - for(i=0; i<0x80; ++i) { - *p++=initialValue; - } - for(; i<0xc0; ++i) { - *p++=errorValue; - } - /* highValue and reserved values */ - for(i=0; iisMemoryOwned) { - uprv_free(trie->memory); - } - if(trie->newTrie!=NULL) { - uprv_free(trie->newTrie->data); -#ifdef UCPTRIE_DEBUG - umutablecptrie_close(trie->newTrie->t3); -#endif - uprv_free(trie->newTrie); - } - uprv_free(trie); - } -} - -U_CAPI UBool U_EXPORT2 -utrie2_isFrozen(const UTrie2 *trie) { - return (UBool)(trie->newTrie==NULL); -} - -U_CAPI int32_t U_EXPORT2 -utrie2_serialize(const UTrie2 *trie, - void *data, int32_t capacity, - UErrorCode *pErrorCode) { - /* argument check */ - if(U_FAILURE(*pErrorCode)) { - return 0; - } - - if( trie==NULL || trie->memory==NULL || trie->newTrie!=NULL || - capacity<0 || (capacity>0 && (data==NULL || (U_POINTER_MASK_LSB(data, 3)!=0))) - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - if(capacity>=trie->length) { - uprv_memcpy(data, trie->memory, trie->length); - } else { - *pErrorCode=U_BUFFER_OVERFLOW_ERROR; - } - return trie->length; -} - -/* enumeration -------------------------------------------------------------- */ - -#define MIN_VALUE(a, b) ((a)<(b) ? (a) : (b)) - -/* default UTrie2EnumValue() returns the input value itself */ -static uint32_t U_CALLCONV -enumSameValue(const void * /*context*/, uint32_t value) { - return value; -} - -/** - * Enumerate all ranges of code points with the same relevant values. - * The values are transformed from the raw trie entries by the enumValue function. - * - * Currently requires startnewTrie==NULL) { - /* frozen trie */ - idx=trie->index; - U_ASSERT(idx!=NULL); /* the following code assumes trie->newTrie is not NULL when idx is NULL */ - data32=trie->data32; - - index2NullOffset=trie->index2NullOffset; - nullBlock=trie->dataNullOffset; - } else { - /* unfrozen, mutable trie */ - idx=NULL; - data32=trie->newTrie->data; - U_ASSERT(data32!=NULL); /* the following code assumes idx is not NULL when data32 is NULL */ - - index2NullOffset=trie->newTrie->index2NullOffset; - nullBlock=trie->newTrie->dataNullOffset; - } - - highStart=trie->highStart; - - /* get the enumeration value that corresponds to an initial-value trie data entry */ - initialValue=enumValue(context, trie->initialValue); - - /* set variables for previous range */ - prevI2Block=-1; - prevBlock=-1; - prev=start; - prevValue=0; - - /* enumerate index-2 blocks */ - for(c=start; c>UTRIE2_SHIFT_2; - } else if(U_IS_SURROGATE_LEAD(c)) { - /* - * Enumerate values for lead surrogate code points, not code units: - * This special block has half the normal length. - */ - i2Block=UTRIE2_LSCP_INDEX_2_OFFSET; - tempLimit=MIN_VALUE(0xdc00, limit); - } else { - /* - * Switch back to the normal part of the index-2 table. - * Enumerate the second half of the surrogates block. - */ - i2Block=0xd800>>UTRIE2_SHIFT_2; - tempLimit=MIN_VALUE(0xe000, limit); - } - } else { - /* supplementary code points */ - if(idx!=NULL) { - i2Block=idx[(UTRIE2_INDEX_1_OFFSET-UTRIE2_OMITTED_BMP_INDEX_1_LENGTH)+ - (c>>UTRIE2_SHIFT_1)]; - } else { - i2Block=trie->newTrie->index1[c>>UTRIE2_SHIFT_1]; - } - if(i2Block==prevI2Block && (c-prev)>=UTRIE2_CP_PER_INDEX_1_ENTRY) { - /* - * The index-2 block is the same as the previous one, and filled with prevValue. - * Only possible for supplementary code points because the linear-BMP index-2 - * table creates unique i2Block values. - */ - c+=UTRIE2_CP_PER_INDEX_1_ENTRY; - continue; - } - } - prevI2Block=i2Block; - if(i2Block==index2NullOffset) { - /* this is the null index-2 block */ - if(prevValue!=initialValue) { - if(prev>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK; - if((c>>UTRIE2_SHIFT_1)==(tempLimit>>UTRIE2_SHIFT_1)) { - i2Limit=(tempLimit>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK; - } else { - i2Limit=UTRIE2_INDEX_2_BLOCK_LENGTH; - } - for(; i2newTrie->index2[i2Block+i2]; - } - if(block==prevBlock && (c-prev)>=UTRIE2_DATA_BLOCK_LENGTH) { - /* the block is the same as the previous one, and filled with prevValue */ - c+=UTRIE2_DATA_BLOCK_LENGTH; - continue; - } - prevBlock=block; - if(block==nullBlock) { - /* this is the null data block */ - if(prevValue!=initialValue) { - if(prevlimit) { - c=limit; /* could be higher if in the index2NullOffset */ - } else if(chighValueIndex] : - idx[trie->highValueIndex]; - } else { - highValue=trie->newTrie->data[trie->newTrie->dataLength-UTRIE2_DATA_GRANULARITY]; - } - value=enumValue(context, highValue); - if(value!=prevValue) { - if(prev=codePointStart) { - codePoint=U_SENTINEL; - return static_cast(trie->errorValue); - } - uint16_t result; - UTRIE2_U16_PREV16(trie, start, codePointStart, codePoint, result); - return result; -} - -uint16_t ForwardUTrie2StringIterator::next16() { - codePointStart=codePointLimit; - if(codePointLimit==limit) { - codePoint=U_SENTINEL; - return static_cast(trie->errorValue); - } - uint16_t result; - UTRIE2_U16_NEXT16(trie, codePointLimit, limit, codePoint, result); - return result; -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2001-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: utrie2.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2008aug16 (starting from a copy of utrie.c) +* created by: Markus W. Scherer +* +* This is a common implementation of a Unicode trie. +* It is a kind of compressed, serializable table of 16- or 32-bit values associated with +* Unicode code points (0..0x10ffff). +* This is the second common version of a Unicode trie (hence the name UTrie2). +* See utrie2.h for a comparison. +* +* This file contains only the runtime and enumeration code, for read-only access. +* See utrie2_builder.c for the builder code. +*/ +#include "unicode/utypes.h" +#ifdef UCPTRIE_DEBUG +#include "unicode/umutablecptrie.h" +#endif +#include "unicode/utf.h" +#include "unicode/utf8.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "utrie2.h" +#include "utrie2_impl.h" +#include "uassert.h" + +/* Public UTrie2 API implementation ----------------------------------------- */ + +static uint32_t +get32(const UNewTrie2 *trie, UChar32 c, UBool fromLSCP) { + int32_t i2, block; + + if(c>=trie->highStart && (!U_IS_LEAD(c) || fromLSCP)) { + return trie->data[trie->dataLength-UTRIE2_DATA_GRANULARITY]; + } + + if(U_IS_LEAD(c) && fromLSCP) { + i2=(UTRIE2_LSCP_INDEX_2_OFFSET-(0xd800>>UTRIE2_SHIFT_2))+ + (c>>UTRIE2_SHIFT_2); + } else { + i2=trie->index1[c>>UTRIE2_SHIFT_1]+ + ((c>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK); + } + block=trie->index2[i2]; + return trie->data[block+(c&UTRIE2_DATA_MASK)]; +} + +U_CAPI uint32_t U_EXPORT2 +utrie2_get32(const UTrie2 *trie, UChar32 c) { + if(trie->data16!=nullptr) { + return UTRIE2_GET16(trie, c); + } else if(trie->data32!=nullptr) { + return UTRIE2_GET32(trie, c); + } else if((uint32_t)c>0x10ffff) { + return trie->errorValue; + } else { + return get32(trie->newTrie, c, true); + } +} + +U_CAPI uint32_t U_EXPORT2 +utrie2_get32FromLeadSurrogateCodeUnit(const UTrie2 *trie, UChar32 c) { + if(!U_IS_LEAD(c)) { + return trie->errorValue; + } + if(trie->data16!=nullptr) { + return UTRIE2_GET16_FROM_U16_SINGLE_LEAD(trie, c); + } else if(trie->data32!=nullptr) { + return UTRIE2_GET32_FROM_U16_SINGLE_LEAD(trie, c); + } else { + return get32(trie->newTrie, c, false); + } +} + +static inline int32_t +u8Index(const UTrie2 *trie, UChar32 c, int32_t i) { + int32_t idx= + _UTRIE2_INDEX_FROM_CP( + trie, + trie->data32==nullptr ? trie->indexLength : 0, + c); + return (idx<<3)|i; +} + +U_CAPI int32_t U_EXPORT2 +utrie2_internalU8NextIndex(const UTrie2 *trie, UChar32 c, + const uint8_t *src, const uint8_t *limit) { + int32_t i, length; + i=0; + /* support 64-bit pointers by avoiding cast of arbitrary difference */ + if((limit-src)<=7) { + length=(int32_t)(limit-src); + } else { + length=7; + } + c=utf8_nextCharSafeBody(src, &i, length, c, -1); + return u8Index(trie, c, i); +} + +U_CAPI int32_t U_EXPORT2 +utrie2_internalU8PrevIndex(const UTrie2 *trie, UChar32 c, + const uint8_t *start, const uint8_t *src) { + int32_t i, length; + /* support 64-bit pointers by avoiding cast of arbitrary difference */ + if((src-start)<=7) { + i=length=(int32_t)(src-start); + } else { + i=length=7; + start=src-7; + } + c=utf8_prevCharSafeBody(start, 0, &i, c, -1); + i=length-i; /* number of bytes read backward from src */ + return u8Index(trie, c, i); +} + +U_CAPI UTrie2 * U_EXPORT2 +utrie2_openFromSerialized(UTrie2ValueBits valueBits, + const void *data, int32_t length, int32_t *pActualLength, + UErrorCode *pErrorCode) { + const UTrie2Header *header; + const uint16_t *p16; + int32_t actualLength; + + UTrie2 tempTrie; + UTrie2 *trie; + + if(U_FAILURE(*pErrorCode)) { + return 0; + } + + if( length<=0 || (U_POINTER_MASK_LSB(data, 3)!=0) || + valueBits<0 || UTRIE2_COUNT_VALUE_BITS<=valueBits + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* enough data for a trie header? */ + if(length<(int32_t)sizeof(UTrie2Header)) { + *pErrorCode=U_INVALID_FORMAT_ERROR; + return 0; + } + + /* check the signature */ + header=(const UTrie2Header *)data; + if(header->signature!=UTRIE2_SIG) { + *pErrorCode=U_INVALID_FORMAT_ERROR; + return 0; + } + + /* get the options */ + if(valueBits!=(UTrie2ValueBits)(header->options&UTRIE2_OPTIONS_VALUE_BITS_MASK)) { + *pErrorCode=U_INVALID_FORMAT_ERROR; + return 0; + } + + /* get the length values and offsets */ + uprv_memset(&tempTrie, 0, sizeof(tempTrie)); + tempTrie.indexLength=header->indexLength; + tempTrie.dataLength=header->shiftedDataLength<index2NullOffset; + tempTrie.dataNullOffset=header->dataNullOffset; + + tempTrie.highStart=header->shiftedHighStart<memory=(uint32_t *)data; + trie->length=actualLength; + trie->isMemoryOwned=false; +#ifdef UTRIE2_DEBUG + trie->name="fromSerialized"; +#endif + + /* set the pointers to its index and data arrays */ + p16=(const uint16_t *)(header+1); + trie->index=p16; + p16+=trie->indexLength; + + /* get the data */ + switch(valueBits) { + case UTRIE2_16_VALUE_BITS: + trie->data16=p16; + trie->data32=nullptr; + trie->initialValue=trie->index[trie->dataNullOffset]; + trie->errorValue=trie->data16[UTRIE2_BAD_UTF8_DATA_OFFSET]; + break; + case UTRIE2_32_VALUE_BITS: + trie->data16=nullptr; + trie->data32=(const uint32_t *)p16; + trie->initialValue=trie->data32[trie->dataNullOffset]; + trie->errorValue=trie->data32[UTRIE2_BAD_UTF8_DATA_OFFSET]; + break; + default: + *pErrorCode=U_INVALID_FORMAT_ERROR; + return 0; + } + + if(pActualLength!=nullptr) { + *pActualLength=actualLength; + } + return trie; +} + +U_CAPI UTrie2 * U_EXPORT2 +utrie2_openDummy(UTrie2ValueBits valueBits, + uint32_t initialValue, uint32_t errorValue, + UErrorCode *pErrorCode) { + UTrie2 *trie; + UTrie2Header *header; + uint32_t *p; + uint16_t *dest16; + int32_t indexLength, dataLength, length, i; + int32_t dataMove; /* >0 if the data is moved to the end of the index array */ + + if(U_FAILURE(*pErrorCode)) { + return 0; + } + + if(valueBits<0 || UTRIE2_COUNT_VALUE_BITS<=valueBits) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* calculate the total length of the dummy trie data */ + indexLength=UTRIE2_INDEX_1_OFFSET; + dataLength=UTRIE2_DATA_START_OFFSET+UTRIE2_DATA_GRANULARITY; + length=(int32_t)sizeof(UTrie2Header)+indexLength*2; + if(valueBits==UTRIE2_16_VALUE_BITS) { + length+=dataLength*2; + } else { + length+=dataLength*4; + } + + /* allocate the trie */ + trie=(UTrie2 *)uprv_malloc(sizeof(UTrie2)); + if(trie==nullptr) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return 0; + } + uprv_memset(trie, 0, sizeof(UTrie2)); + trie->memory=uprv_malloc(length); + if(trie->memory==nullptr) { + uprv_free(trie); + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return 0; + } + trie->length=length; + trie->isMemoryOwned=true; + + /* set the UTrie2 fields */ + if(valueBits==UTRIE2_16_VALUE_BITS) { + dataMove=indexLength; + } else { + dataMove=0; + } + + trie->indexLength=indexLength; + trie->dataLength=dataLength; + trie->index2NullOffset=UTRIE2_INDEX_2_OFFSET; + trie->dataNullOffset=(uint16_t)dataMove; + trie->initialValue=initialValue; + trie->errorValue=errorValue; + trie->highStart=0; + trie->highValueIndex=dataMove+UTRIE2_DATA_START_OFFSET; +#ifdef UTRIE2_DEBUG + trie->name="dummy"; +#endif + + /* set the header fields */ + header=(UTrie2Header *)trie->memory; + + header->signature=UTRIE2_SIG; /* "Tri2" */ + header->options=(uint16_t)valueBits; + + header->indexLength=(uint16_t)indexLength; + header->shiftedDataLength=(uint16_t)(dataLength>>UTRIE2_INDEX_SHIFT); + header->index2NullOffset=(uint16_t)UTRIE2_INDEX_2_OFFSET; + header->dataNullOffset=(uint16_t)dataMove; + header->shiftedHighStart=0; + + /* fill the index and data arrays */ + dest16=(uint16_t *)(header+1); + trie->index=dest16; + + /* write the index-2 array values shifted right by UTRIE2_INDEX_SHIFT */ + for(i=0; i>UTRIE2_INDEX_SHIFT); /* null data block */ + } + + /* write UTF-8 2-byte index-2 values, not right-shifted */ + for(i=0; i<(0xc2-0xc0); ++i) { /* C0..C1 */ + *dest16++=(uint16_t)(dataMove+UTRIE2_BAD_UTF8_DATA_OFFSET); + } + for(; i<(0xe0-0xc0); ++i) { /* C2..DF */ + *dest16++=(uint16_t)dataMove; + } + + /* write the 16/32-bit data array */ + switch(valueBits) { + case UTRIE2_16_VALUE_BITS: + /* write 16-bit data values */ + trie->data16=dest16; + trie->data32=nullptr; + for(i=0; i<0x80; ++i) { + *dest16++=(uint16_t)initialValue; + } + for(; i<0xc0; ++i) { + *dest16++=(uint16_t)errorValue; + } + /* highValue and reserved values */ + for(i=0; idata16=nullptr; + trie->data32=p; + for(i=0; i<0x80; ++i) { + *p++=initialValue; + } + for(; i<0xc0; ++i) { + *p++=errorValue; + } + /* highValue and reserved values */ + for(i=0; iisMemoryOwned) { + uprv_free(trie->memory); + } + if(trie->newTrie!=nullptr) { + uprv_free(trie->newTrie->data); +#ifdef UCPTRIE_DEBUG + umutablecptrie_close(trie->newTrie->t3); +#endif + uprv_free(trie->newTrie); + } + uprv_free(trie); + } +} + +U_CAPI UBool U_EXPORT2 +utrie2_isFrozen(const UTrie2 *trie) { + return (UBool)(trie->newTrie==nullptr); +} + +U_CAPI int32_t U_EXPORT2 +utrie2_serialize(const UTrie2 *trie, + void *data, int32_t capacity, + UErrorCode *pErrorCode) { + /* argument check */ + if(U_FAILURE(*pErrorCode)) { + return 0; + } + + if( trie==nullptr || trie->memory==nullptr || trie->newTrie!=nullptr || + capacity<0 || (capacity>0 && (data==nullptr || (U_POINTER_MASK_LSB(data, 3)!=0))) + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + if(capacity>=trie->length) { + uprv_memcpy(data, trie->memory, trie->length); + } else { + *pErrorCode=U_BUFFER_OVERFLOW_ERROR; + } + return trie->length; +} + +/* enumeration -------------------------------------------------------------- */ + +#define MIN_VALUE(a, b) ((a)<(b) ? (a) : (b)) + +/* default UTrie2EnumValue() returns the input value itself */ +static uint32_t U_CALLCONV +enumSameValue(const void * /*context*/, uint32_t value) { + return value; +} + +/** + * Enumerate all ranges of code points with the same relevant values. + * The values are transformed from the raw trie entries by the enumValue function. + * + * Currently requires startnewTrie==nullptr) { + /* frozen trie */ + idx=trie->index; + U_ASSERT(idx!=nullptr); /* the following code assumes trie->newTrie is not nullptr when idx is nullptr */ + data32=trie->data32; + + index2NullOffset=trie->index2NullOffset; + nullBlock=trie->dataNullOffset; + } else { + /* unfrozen, mutable trie */ + idx=nullptr; + data32=trie->newTrie->data; + U_ASSERT(data32!=nullptr); /* the following code assumes idx is not nullptr when data32 is nullptr */ + + index2NullOffset=trie->newTrie->index2NullOffset; + nullBlock=trie->newTrie->dataNullOffset; + } + + highStart=trie->highStart; + + /* get the enumeration value that corresponds to an initial-value trie data entry */ + initialValue=enumValue(context, trie->initialValue); + + /* set variables for previous range */ + prevI2Block=-1; + prevBlock=-1; + prev=start; + prevValue=0; + + /* enumerate index-2 blocks */ + for(c=start; c>UTRIE2_SHIFT_2; + } else if(U_IS_SURROGATE_LEAD(c)) { + /* + * Enumerate values for lead surrogate code points, not code units: + * This special block has half the normal length. + */ + i2Block=UTRIE2_LSCP_INDEX_2_OFFSET; + tempLimit=MIN_VALUE(0xdc00, limit); + } else { + /* + * Switch back to the normal part of the index-2 table. + * Enumerate the second half of the surrogates block. + */ + i2Block=0xd800>>UTRIE2_SHIFT_2; + tempLimit=MIN_VALUE(0xe000, limit); + } + } else { + /* supplementary code points */ + if(idx!=nullptr) { + i2Block=idx[(UTRIE2_INDEX_1_OFFSET-UTRIE2_OMITTED_BMP_INDEX_1_LENGTH)+ + (c>>UTRIE2_SHIFT_1)]; + } else { + i2Block=trie->newTrie->index1[c>>UTRIE2_SHIFT_1]; + } + if(i2Block==prevI2Block && (c-prev)>=UTRIE2_CP_PER_INDEX_1_ENTRY) { + /* + * The index-2 block is the same as the previous one, and filled with prevValue. + * Only possible for supplementary code points because the linear-BMP index-2 + * table creates unique i2Block values. + */ + c+=UTRIE2_CP_PER_INDEX_1_ENTRY; + continue; + } + } + prevI2Block=i2Block; + if(i2Block==index2NullOffset) { + /* this is the null index-2 block */ + if(prevValue!=initialValue) { + if(prev>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK; + if((c>>UTRIE2_SHIFT_1)==(tempLimit>>UTRIE2_SHIFT_1)) { + i2Limit=(tempLimit>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK; + } else { + i2Limit=UTRIE2_INDEX_2_BLOCK_LENGTH; + } + for(; i2newTrie->index2[i2Block+i2]; + } + if(block==prevBlock && (c-prev)>=UTRIE2_DATA_BLOCK_LENGTH) { + /* the block is the same as the previous one, and filled with prevValue */ + c+=UTRIE2_DATA_BLOCK_LENGTH; + continue; + } + prevBlock=block; + if(block==nullBlock) { + /* this is the null data block */ + if(prevValue!=initialValue) { + if(prevlimit) { + c=limit; /* could be higher if in the index2NullOffset */ + } else if(chighValueIndex] : + idx[trie->highValueIndex]; + } else { + highValue=trie->newTrie->data[trie->newTrie->dataLength-UTRIE2_DATA_GRANULARITY]; + } + value=enumValue(context, highValue); + if(value!=prevValue) { + if(prev=codePointStart) { + codePoint=U_SENTINEL; + return static_cast(trie->errorValue); + } + uint16_t result; + UTRIE2_U16_PREV16(trie, start, codePointStart, codePoint, result); + return result; +} + +uint16_t ForwardUTrie2StringIterator::next16() { + codePointStart=codePointLimit; + if(codePointLimit==limit) { + codePoint=U_SENTINEL; + return static_cast(trie->errorValue); + } + uint16_t result; + UTRIE2_U16_NEXT16(trie, codePointLimit, limit, codePoint, result); + return result; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/common/utrie2.h b/deps/icu-small/source/common/utrie2.h index ace52cce37bf63..f7e7c47053450a 100644 --- a/deps/icu-small/source/common/utrie2.h +++ b/deps/icu-small/source/common/utrie2.h @@ -1,955 +1,955 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2001-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: utrie2.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2008aug16 (starting from a copy of utrie.h) -* created by: Markus W. Scherer -*/ - -#ifndef __UTRIE2_H__ -#define __UTRIE2_H__ - -#include "unicode/utypes.h" -#include "unicode/utf8.h" -#include "putilimp.h" - -U_CDECL_BEGIN - -struct UTrie; /* forward declaration */ -#ifndef __UTRIE_H__ -typedef struct UTrie UTrie; -#endif - -/** - * \file - * - * This is a common implementation of a Unicode trie. - * It is a kind of compressed, serializable table of 16- or 32-bit values associated with - * Unicode code points (0..0x10ffff). (A map from code points to integers.) - * - * This is the second common version of a Unicode trie (hence the name UTrie2). - * Compared with UTrie version 1: - * - Still splitting BMP code points 11:5 bits for index and data table lookups. - * - Still separate data for lead surrogate code _units_ vs. code _points_, - * but the lead surrogate code unit values are not required any more - * for data lookup for supplementary code points. - * - The "folding" mechanism is removed. In UTrie version 1, this somewhat - * hard-to-explain mechanism was meant to be used for optimized UTF-16 - * processing, with application-specific encoding of indexing bits - * in the lead surrogate data for the associated supplementary code points. - * - For the last single-value code point range (ending with U+10ffff), - * the starting code point ("highStart") and the value are stored. - * - For supplementary code points U+10000..highStart-1 a three-table lookup - * (two index tables and one data table) is used. The first index - * is truncated, omitting both the BMP portion and the high range. - * - There is a special small index for 2-byte UTF-8, and the initial data - * entries are designed for fast 1/2-byte UTF-8 lookup. - * Starting with ICU 60, C0 and C1 are not recognized as UTF-8 lead bytes any more at all, - * and the associated 2-byte indexes are unused. - */ - -/** - * Trie structure. - * Use only with public API macros and functions. - */ -struct UTrie2; -typedef struct UTrie2 UTrie2; - -/* Public UTrie2 API functions: read-only access ---------------------------- */ - -/** - * Selectors for the width of a UTrie2 data value. - */ -enum UTrie2ValueBits { - /** 16 bits per UTrie2 data value. */ - UTRIE2_16_VALUE_BITS, - /** 32 bits per UTrie2 data value. */ - UTRIE2_32_VALUE_BITS, - /** Number of selectors for the width of UTrie2 data values. */ - UTRIE2_COUNT_VALUE_BITS -}; -typedef enum UTrie2ValueBits UTrie2ValueBits; - -/** - * Open a frozen trie from its serialized from, stored in 32-bit-aligned memory. - * Inverse of utrie2_serialize(). - * The memory must remain valid and unchanged as long as the trie is used. - * You must utrie2_close() the trie once you are done using it. - * - * @param valueBits selects the data entry size; results in an - * U_INVALID_FORMAT_ERROR if it does not match the serialized form - * @param data a pointer to 32-bit-aligned memory containing the serialized form of a UTrie2 - * @param length the number of bytes available at data; - * can be more than necessary - * @param pActualLength receives the actual number of bytes at data taken up by the trie data; - * can be NULL - * @param pErrorCode an in/out ICU UErrorCode - * @return the unserialized trie - * - * @see utrie2_open - * @see utrie2_serialize - */ -U_CAPI UTrie2 * U_EXPORT2 -utrie2_openFromSerialized(UTrie2ValueBits valueBits, - const void *data, int32_t length, int32_t *pActualLength, - UErrorCode *pErrorCode); - -/** - * Open a frozen, empty "dummy" trie. - * A dummy trie is an empty trie, used when a real data trie cannot - * be loaded. Equivalent to calling utrie2_open() and utrie2_freeze(), - * but without internally creating and compacting/serializing the - * builder data structure. - * - * The trie always returns the initialValue, - * or the errorValue for out-of-range code points and illegal UTF-8. - * - * You must utrie2_close() the trie once you are done using it. - * - * @param valueBits selects the data entry size - * @param initialValue the initial value that is set for all code points - * @param errorValue the value for out-of-range code points and illegal UTF-8 - * @param pErrorCode an in/out ICU UErrorCode - * @return the dummy trie - * - * @see utrie2_openFromSerialized - * @see utrie2_open - */ -U_CAPI UTrie2 * U_EXPORT2 -utrie2_openDummy(UTrie2ValueBits valueBits, - uint32_t initialValue, uint32_t errorValue, - UErrorCode *pErrorCode); - -/** - * Get a value from a code point as stored in the trie. - * Easier to use than UTRIE2_GET16() and UTRIE2_GET32() but slower. - * Easier to use because, unlike the macros, this function works on all UTrie2 - * objects, frozen or not, holding 16-bit or 32-bit data values. - * - * @param trie the trie - * @param c the code point - * @return the value - */ -U_CAPI uint32_t U_EXPORT2 -utrie2_get32(const UTrie2 *trie, UChar32 c); - -/* enumeration callback types */ - -/** - * Callback from utrie2_enum(), extracts a uint32_t value from a - * trie value. This value will be passed on to the UTrie2EnumRange function. - * - * @param context an opaque pointer, as passed into utrie2_enum() - * @param value a value from the trie - * @return the value that is to be passed on to the UTrie2EnumRange function - */ -typedef uint32_t U_CALLCONV -UTrie2EnumValue(const void *context, uint32_t value); - -/** - * Callback from utrie2_enum(), is called for each contiguous range - * of code points with the same value as retrieved from the trie and - * transformed by the UTrie2EnumValue function. - * - * The callback function can stop the enumeration by returning false. - * - * @param context an opaque pointer, as passed into utrie2_enum() - * @param start the first code point in a contiguous range with value - * @param end the last code point in a contiguous range with value (inclusive) - * @param value the value that is set for all code points in [start..end] - * @return false to stop the enumeration - */ -typedef UBool U_CALLCONV -UTrie2EnumRange(const void *context, UChar32 start, UChar32 end, uint32_t value); - -/** - * Enumerate efficiently all values in a trie. - * Do not modify the trie during the enumeration. - * - * For each entry in the trie, the value to be delivered is passed through - * the UTrie2EnumValue function. - * The value is unchanged if that function pointer is NULL. - * - * For each contiguous range of code points with a given (transformed) value, - * the UTrie2EnumRange function is called. - * - * @param trie a pointer to the trie - * @param enumValue a pointer to a function that may transform the trie entry value, - * or NULL if the values from the trie are to be used directly - * @param enumRange a pointer to a function that is called for each contiguous range - * of code points with the same (transformed) value - * @param context an opaque pointer that is passed on to the callback functions - */ -U_CAPI void U_EXPORT2 -utrie2_enum(const UTrie2 *trie, - UTrie2EnumValue *enumValue, UTrie2EnumRange *enumRange, const void *context); - -/* Building a trie ---------------------------------------------------------- */ - -/** - * Open an empty, writable trie. At build time, 32-bit data values are used. - * utrie2_freeze() takes a valueBits parameter - * which determines the data value width in the serialized and frozen forms. - * You must utrie2_close() the trie once you are done using it. - * - * @param initialValue the initial value that is set for all code points - * @param errorValue the value for out-of-range code points and illegal UTF-8 - * @param pErrorCode an in/out ICU UErrorCode - * @return a pointer to the allocated and initialized new trie - */ -U_CAPI UTrie2 * U_EXPORT2 -utrie2_open(uint32_t initialValue, uint32_t errorValue, UErrorCode *pErrorCode); - -/** - * Clone a trie. - * You must utrie2_close() the clone once you are done using it. - * - * @param other the trie to clone - * @param pErrorCode an in/out ICU UErrorCode - * @return a pointer to the new trie clone - */ -U_CAPI UTrie2 * U_EXPORT2 -utrie2_clone(const UTrie2 *other, UErrorCode *pErrorCode); - -/** - * Clone a trie. The clone will be mutable/writable even if the other trie - * is frozen. (See utrie2_freeze().) - * You must utrie2_close() the clone once you are done using it. - * - * @param other the trie to clone - * @param pErrorCode an in/out ICU UErrorCode - * @return a pointer to the new trie clone - */ -U_CAPI UTrie2 * U_EXPORT2 -utrie2_cloneAsThawed(const UTrie2 *other, UErrorCode *pErrorCode); - -/** - * Close a trie and release associated memory. - * - * @param trie the trie - */ -U_CAPI void U_EXPORT2 -utrie2_close(UTrie2 *trie); - -/** - * Set a value for a code point. - * - * @param trie the unfrozen trie - * @param c the code point - * @param value the value - * @param pErrorCode an in/out ICU UErrorCode; among other possible error codes: - * - U_NO_WRITE_PERMISSION if the trie is frozen - */ -U_CAPI void U_EXPORT2 -utrie2_set32(UTrie2 *trie, UChar32 c, uint32_t value, UErrorCode *pErrorCode); - -/** - * Set a value in a range of code points [start..end]. - * All code points c with start<=c<=end will get the value if - * overwrite is true or if the old value is the initial value. - * - * @param trie the unfrozen trie - * @param start the first code point to get the value - * @param end the last code point to get the value (inclusive) - * @param value the value - * @param overwrite flag for whether old non-initial values are to be overwritten - * @param pErrorCode an in/out ICU UErrorCode; among other possible error codes: - * - U_NO_WRITE_PERMISSION if the trie is frozen - */ -U_CAPI void U_EXPORT2 -utrie2_setRange32(UTrie2 *trie, - UChar32 start, UChar32 end, - uint32_t value, UBool overwrite, - UErrorCode *pErrorCode); - -/** - * Freeze a trie. Make it immutable (read-only) and compact it, - * ready for serialization and for use with fast macros. - * Functions to set values will fail after serializing. - * - * A trie can be frozen only once. If this function is called again with different - * valueBits then it will set a U_ILLEGAL_ARGUMENT_ERROR. - * - * @param trie the trie - * @param valueBits selects the data entry size; if smaller than 32 bits, then - * the values stored in the trie will be truncated - * @param pErrorCode an in/out ICU UErrorCode; among other possible error codes: - * - U_INDEX_OUTOFBOUNDS_ERROR if the compacted index or data arrays are too long - * for serialization - * (the trie will be immutable and usable, - * but not frozen and not usable with the fast macros) - * - * @see utrie2_cloneAsThawed - */ -U_CAPI void U_EXPORT2 -utrie2_freeze(UTrie2 *trie, UTrie2ValueBits valueBits, UErrorCode *pErrorCode); - -/** - * Test if the trie is frozen. (See utrie2_freeze().) - * - * @param trie the trie - * @return true if the trie is frozen, that is, immutable, ready for serialization - * and for use with fast macros - */ -U_CAPI UBool U_EXPORT2 -utrie2_isFrozen(const UTrie2 *trie); - -/** - * Serialize a frozen trie into 32-bit aligned memory. - * If the trie is not frozen, then the function returns with a U_ILLEGAL_ARGUMENT_ERROR. - * A trie can be serialized multiple times. - * - * @param trie the frozen trie - * @param data a pointer to 32-bit-aligned memory to be filled with the trie data, - * can be NULL if capacity==0 - * @param capacity the number of bytes available at data, - * or 0 for preflighting - * @param pErrorCode an in/out ICU UErrorCode; among other possible error codes: - * - U_BUFFER_OVERFLOW_ERROR if the data storage block is too small for serialization - * - U_ILLEGAL_ARGUMENT_ERROR if the trie is not frozen or the data and capacity - * parameters are bad - * @return the number of bytes written or needed for the trie - * - * @see utrie2_openFromSerialized() - */ -U_CAPI int32_t U_EXPORT2 -utrie2_serialize(const UTrie2 *trie, - void *data, int32_t capacity, - UErrorCode *pErrorCode); - -/* Public UTrie2 API: miscellaneous functions ------------------------------- */ - -/** - * Build a UTrie2 (version 2) from a UTrie (version 1). - * Enumerates all values in the UTrie and builds a UTrie2 with the same values. - * The resulting UTrie2 will be frozen. - * - * @param trie1 the runtime UTrie structure to be enumerated - * @param errorValue the value for out-of-range code points and illegal UTF-8 - * @param pErrorCode an in/out ICU UErrorCode - * @return The frozen UTrie2 with the same values as the UTrie. - */ -U_CAPI UTrie2 * U_EXPORT2 -utrie2_fromUTrie(const UTrie *trie1, uint32_t errorValue, UErrorCode *pErrorCode); - -/* Public UTrie2 API macros ------------------------------------------------- */ - -/* - * These macros provide fast data lookup from a frozen trie. - * They will crash when used on an unfrozen trie. - */ - -/** - * Return a 16-bit trie value from a code point, with range checking. - * Returns trie->errorValue if c is not in the range 0..U+10ffff. - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param c (UChar32, in) the input code point - * @return (uint16_t) The code point's trie value. - */ -#define UTRIE2_GET16(trie, c) _UTRIE2_GET((trie), index, (trie)->indexLength, (c)) - -/** - * Return a 32-bit trie value from a code point, with range checking. - * Returns trie->errorValue if c is not in the range 0..U+10ffff. - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param c (UChar32, in) the input code point - * @return (uint32_t) The code point's trie value. - */ -#define UTRIE2_GET32(trie, c) _UTRIE2_GET((trie), data32, 0, (c)) - -/** - * UTF-16: Get the next code point (UChar32 c, out), post-increment src, - * and get a 16-bit value from the trie. - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param src (const UChar *, in/out) the source text pointer - * @param limit (const UChar *, in) the limit pointer for the text, or NULL if NUL-terminated - * @param c (UChar32, out) variable for the code point - * @param result (uint16_t, out) uint16_t variable for the trie lookup result - */ -#define UTRIE2_U16_NEXT16(trie, src, limit, c, result) _UTRIE2_U16_NEXT(trie, index, src, limit, c, result) - -/** - * UTF-16: Get the next code point (UChar32 c, out), post-increment src, - * and get a 32-bit value from the trie. - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param src (const UChar *, in/out) the source text pointer - * @param limit (const UChar *, in) the limit pointer for the text, or NULL if NUL-terminated - * @param c (UChar32, out) variable for the code point - * @param result (uint32_t, out) uint32_t variable for the trie lookup result - */ -#define UTRIE2_U16_NEXT32(trie, src, limit, c, result) _UTRIE2_U16_NEXT(trie, data32, src, limit, c, result) - -/** - * UTF-16: Get the previous code point (UChar32 c, out), pre-decrement src, - * and get a 16-bit value from the trie. - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param start (const UChar *, in) the start pointer for the text - * @param src (const UChar *, in/out) the source text pointer - * @param c (UChar32, out) variable for the code point - * @param result (uint16_t, out) uint16_t variable for the trie lookup result - */ -#define UTRIE2_U16_PREV16(trie, start, src, c, result) _UTRIE2_U16_PREV(trie, index, start, src, c, result) - -/** - * UTF-16: Get the previous code point (UChar32 c, out), pre-decrement src, - * and get a 32-bit value from the trie. - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param start (const UChar *, in) the start pointer for the text - * @param src (const UChar *, in/out) the source text pointer - * @param c (UChar32, out) variable for the code point - * @param result (uint32_t, out) uint32_t variable for the trie lookup result - */ -#define UTRIE2_U16_PREV32(trie, start, src, c, result) _UTRIE2_U16_PREV(trie, data32, start, src, c, result) - -/** - * UTF-8: Post-increment src and get a 16-bit value from the trie. - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param src (const char *, in/out) the source text pointer - * @param limit (const char *, in) the limit pointer for the text (must not be NULL) - * @param result (uint16_t, out) uint16_t variable for the trie lookup result - */ -#define UTRIE2_U8_NEXT16(trie, src, limit, result)\ - _UTRIE2_U8_NEXT(trie, data16, index, src, limit, result) - -/** - * UTF-8: Post-increment src and get a 32-bit value from the trie. - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param src (const char *, in/out) the source text pointer - * @param limit (const char *, in) the limit pointer for the text (must not be NULL) - * @param result (uint16_t, out) uint32_t variable for the trie lookup result - */ -#define UTRIE2_U8_NEXT32(trie, src, limit, result) \ - _UTRIE2_U8_NEXT(trie, data32, data32, src, limit, result) - -/** - * UTF-8: Pre-decrement src and get a 16-bit value from the trie. - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param start (const char *, in) the start pointer for the text - * @param src (const char *, in/out) the source text pointer - * @param result (uint16_t, out) uint16_t variable for the trie lookup result - */ -#define UTRIE2_U8_PREV16(trie, start, src, result) \ - _UTRIE2_U8_PREV(trie, data16, index, start, src, result) - -/** - * UTF-8: Pre-decrement src and get a 32-bit value from the trie. - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param start (const char *, in) the start pointer for the text - * @param src (const char *, in/out) the source text pointer - * @param result (uint16_t, out) uint32_t variable for the trie lookup result - */ -#define UTRIE2_U8_PREV32(trie, start, src, result) \ - _UTRIE2_U8_PREV(trie, data32, data32, start, src, result) - -/* Public UTrie2 API: optimized UTF-16 access ------------------------------- */ - -/* - * The following functions and macros are used for highly optimized UTF-16 - * text processing. The UTRIE2_U16_NEXTxy() macros do not depend on these. - * - * A UTrie2 stores separate values for lead surrogate code _units_ vs. code _points_. - * UTF-16 text processing can be optimized by detecting surrogate pairs and - * assembling supplementary code points only when there is non-trivial data - * available. - * - * At build-time, use utrie2_enumForLeadSurrogate() to see if there - * is non-trivial (non-initialValue) data for any of the supplementary - * code points associated with a lead surrogate. - * If so, then set a special (application-specific) value for the - * lead surrogate code _unit_, with utrie2_set32ForLeadSurrogateCodeUnit(). - * - * At runtime, use UTRIE2_GET16_FROM_U16_SINGLE_LEAD() or - * UTRIE2_GET32_FROM_U16_SINGLE_LEAD() per code unit. If there is non-trivial - * data and the code unit is a lead surrogate, then check if a trail surrogate - * follows. If so, assemble the supplementary code point with - * U16_GET_SUPPLEMENTARY() and look up its value with UTRIE2_GET16_FROM_SUPP() - * or UTRIE2_GET32_FROM_SUPP(); otherwise reset the lead - * surrogate's value or do a code point lookup for it. - * - * If there is only trivial data for lead and trail surrogates, then processing - * can often skip them. For example, in normalization or case mapping - * all characters that do not have any mappings are simply copied as is. - */ - -/** - * Get a value from a lead surrogate code unit as stored in the trie. - * - * @param trie the trie - * @param c the code unit (U+D800..U+DBFF) - * @return the value - */ -U_CAPI uint32_t U_EXPORT2 -utrie2_get32FromLeadSurrogateCodeUnit(const UTrie2 *trie, UChar32 c); - -/** - * Enumerate the trie values for the 1024=0x400 code points - * corresponding to a given lead surrogate. - * For example, for the lead surrogate U+D87E it will enumerate the values - * for [U+2F800..U+2FC00[. - * Used by data builder code that sets special lead surrogate code unit values - * for optimized UTF-16 string processing. - * - * Do not modify the trie during the enumeration. - * - * Except for the limited code point range, this functions just like utrie2_enum(): - * For each entry in the trie, the value to be delivered is passed through - * the UTrie2EnumValue function. - * The value is unchanged if that function pointer is NULL. - * - * For each contiguous range of code points with a given (transformed) value, - * the UTrie2EnumRange function is called. - * - * @param trie a pointer to the trie - * @param enumValue a pointer to a function that may transform the trie entry value, - * or NULL if the values from the trie are to be used directly - * @param enumRange a pointer to a function that is called for each contiguous range - * of code points with the same (transformed) value - * @param context an opaque pointer that is passed on to the callback functions - */ -U_CAPI void U_EXPORT2 -utrie2_enumForLeadSurrogate(const UTrie2 *trie, UChar32 lead, - UTrie2EnumValue *enumValue, UTrie2EnumRange *enumRange, - const void *context); - -/** - * Set a value for a lead surrogate code unit. - * - * @param trie the unfrozen trie - * @param lead the lead surrogate code unit (U+D800..U+DBFF) - * @param value the value - * @param pErrorCode an in/out ICU UErrorCode; among other possible error codes: - * - U_NO_WRITE_PERMISSION if the trie is frozen - */ -U_CAPI void U_EXPORT2 -utrie2_set32ForLeadSurrogateCodeUnit(UTrie2 *trie, - UChar32 lead, uint32_t value, - UErrorCode *pErrorCode); - -/** - * Return a 16-bit trie value from a UTF-16 single/lead code unit (<=U+ffff). - * Same as UTRIE2_GET16() if c is a BMP code point except for lead surrogates, - * but smaller and faster. - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param c (UChar32, in) the input code unit, must be 0<=c<=U+ffff - * @return (uint16_t) The code unit's trie value. - */ -#define UTRIE2_GET16_FROM_U16_SINGLE_LEAD(trie, c) _UTRIE2_GET_FROM_U16_SINGLE_LEAD((trie), index, c) - -/** - * Return a 32-bit trie value from a UTF-16 single/lead code unit (<=U+ffff). - * Same as UTRIE2_GET32() if c is a BMP code point except for lead surrogates, - * but smaller and faster. - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param c (UChar32, in) the input code unit, must be 0<=c<=U+ffff - * @return (uint32_t) The code unit's trie value. - */ -#define UTRIE2_GET32_FROM_U16_SINGLE_LEAD(trie, c) _UTRIE2_GET_FROM_U16_SINGLE_LEAD((trie), data32, c) - -/** - * Return a 16-bit trie value from a supplementary code point (U+10000..U+10ffff). - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param c (UChar32, in) the input code point, must be U+10000<=c<=U+10ffff - * @return (uint16_t) The code point's trie value. - */ -#define UTRIE2_GET16_FROM_SUPP(trie, c) _UTRIE2_GET_FROM_SUPP((trie), index, c) - -/** - * Return a 32-bit trie value from a supplementary code point (U+10000..U+10ffff). - * - * @param trie (const UTrie2 *, in) a frozen trie - * @param c (UChar32, in) the input code point, must be U+10000<=c<=U+10ffff - * @return (uint32_t) The code point's trie value. - */ -#define UTRIE2_GET32_FROM_SUPP(trie, c) _UTRIE2_GET_FROM_SUPP((trie), data32, c) - -U_CDECL_END - -/* C++ convenience wrappers ------------------------------------------------- */ - -#ifdef __cplusplus - -#include "unicode/uobject.h" -#include "unicode/utf.h" - -U_NAMESPACE_BEGIN - -// Use the Forward/Backward subclasses below. -class UTrie2StringIterator : public UMemory { -public: - UTrie2StringIterator(const UTrie2 *t, const UChar *p) : - trie(t), codePointStart(p), codePointLimit(p), codePoint(U_SENTINEL) {} - - const UTrie2 *trie; - const UChar *codePointStart, *codePointLimit; - UChar32 codePoint; -}; - -class BackwardUTrie2StringIterator : public UTrie2StringIterator { -public: - BackwardUTrie2StringIterator(const UTrie2 *t, const UChar *s, const UChar *p) : - UTrie2StringIterator(t, p), start(s) {} - - uint16_t previous16(); - - const UChar *start; -}; - -class ForwardUTrie2StringIterator : public UTrie2StringIterator { -public: - // Iteration limit l can be NULL. - // In that case, the caller must detect c==0 and stop. - ForwardUTrie2StringIterator(const UTrie2 *t, const UChar *p, const UChar *l) : - UTrie2StringIterator(t, p), limit(l) {} - - uint16_t next16(); - - const UChar *limit; -}; - -U_NAMESPACE_END - -#endif - -/* Internal definitions ----------------------------------------------------- */ - -U_CDECL_BEGIN - -/** Build-time trie structure. */ -struct UNewTrie2; -typedef struct UNewTrie2 UNewTrie2; - -/* - * Trie structure definition. - * - * Either the data table is 16 bits wide and accessed via the index - * pointer, with each index item increased by indexLength; - * in this case, data32==NULL, and data16 is used for direct ASCII access. - * - * Or the data table is 32 bits wide and accessed via the data32 pointer. - */ -struct UTrie2 { - /* protected: used by macros and functions for reading values */ - const uint16_t *index; - const uint16_t *data16; /* for fast UTF-8 ASCII access, if 16b data */ - const uint32_t *data32; /* NULL if 16b data is used via index */ - - int32_t indexLength, dataLength; - uint16_t index2NullOffset; /* 0xffff if there is no dedicated index-2 null block */ - uint16_t dataNullOffset; - uint32_t initialValue; - /** Value returned for out-of-range code points and illegal UTF-8. */ - uint32_t errorValue; - - /* Start of the last range which ends at U+10ffff, and its value. */ - UChar32 highStart; - int32_t highValueIndex; - - /* private: used by builder and unserialization functions */ - void *memory; /* serialized bytes; NULL if not frozen yet */ - int32_t length; /* number of serialized bytes at memory; 0 if not frozen yet */ - UBool isMemoryOwned; /* true if the trie owns the memory */ - UBool padding1; - int16_t padding2; - UNewTrie2 *newTrie; /* builder object; NULL when frozen */ - -#ifdef UTRIE2_DEBUG - const char *name; -#endif -}; - -/** - * Trie constants, defining shift widths, index array lengths, etc. - * - * These are needed for the runtime macros but users can treat these as - * implementation details and skip to the actual public API further below. - */ -enum { - /** Shift size for getting the index-1 table offset. */ - UTRIE2_SHIFT_1=6+5, - - /** Shift size for getting the index-2 table offset. */ - UTRIE2_SHIFT_2=5, - - /** - * Difference between the two shift sizes, - * for getting an index-1 offset from an index-2 offset. 6=11-5 - */ - UTRIE2_SHIFT_1_2=UTRIE2_SHIFT_1-UTRIE2_SHIFT_2, - - /** - * Number of index-1 entries for the BMP. 32=0x20 - * This part of the index-1 table is omitted from the serialized form. - */ - UTRIE2_OMITTED_BMP_INDEX_1_LENGTH=0x10000>>UTRIE2_SHIFT_1, - - /** Number of code points per index-1 table entry. 2048=0x800 */ - UTRIE2_CP_PER_INDEX_1_ENTRY=1<>UTRIE2_SHIFT_2. - */ - UTRIE2_INDEX_2_OFFSET=0, - - /** - * The part of the index-2 table for U+D800..U+DBFF stores values for - * lead surrogate code _units_ not code _points_. - * Values for lead surrogate code _points_ are indexed with this portion of the table. - * Length=32=0x20=0x400>>UTRIE2_SHIFT_2. (There are 1024=0x400 lead surrogates.) - */ - UTRIE2_LSCP_INDEX_2_OFFSET=0x10000>>UTRIE2_SHIFT_2, - UTRIE2_LSCP_INDEX_2_LENGTH=0x400>>UTRIE2_SHIFT_2, - - /** Count the lengths of both BMP pieces. 2080=0x820 */ - UTRIE2_INDEX_2_BMP_LENGTH=UTRIE2_LSCP_INDEX_2_OFFSET+UTRIE2_LSCP_INDEX_2_LENGTH, - - /** - * The 2-byte UTF-8 version of the index-2 table follows at offset 2080=0x820. - * Length 32=0x20 for lead bytes C0..DF, regardless of UTRIE2_SHIFT_2. - */ - UTRIE2_UTF8_2B_INDEX_2_OFFSET=UTRIE2_INDEX_2_BMP_LENGTH, - UTRIE2_UTF8_2B_INDEX_2_LENGTH=0x800>>6, /* U+0800 is the first code point after 2-byte UTF-8 */ - - /** - * The index-1 table, only used for supplementary code points, at offset 2112=0x840. - * Variable length, for code points up to highStart, where the last single-value range starts. - * Maximum length 512=0x200=0x100000>>UTRIE2_SHIFT_1. - * (For 0x100000 supplementary code points U+10000..U+10ffff.) - * - * The part of the index-2 table for supplementary code points starts - * after this index-1 table. - * - * Both the index-1 table and the following part of the index-2 table - * are omitted completely if there is only BMP data. - */ - UTRIE2_INDEX_1_OFFSET=UTRIE2_UTF8_2B_INDEX_2_OFFSET+UTRIE2_UTF8_2B_INDEX_2_LENGTH, - UTRIE2_MAX_INDEX_1_LENGTH=0x100000>>UTRIE2_SHIFT_1, - - /* - * Fixed layout of the first part of the data array. ----------------------- - * Starts with 4 blocks (128=0x80 entries) for ASCII. - */ - - /** - * The illegal-UTF-8 data block follows the ASCII block, at offset 128=0x80. - * Used with linear access for single bytes 0..0xbf for simple error handling. - * Length 64=0x40, not UTRIE2_DATA_BLOCK_LENGTH. - */ - UTRIE2_BAD_UTF8_DATA_OFFSET=0x80, - - /** The start of non-linear-ASCII data blocks, at offset 192=0xc0. */ - UTRIE2_DATA_START_OFFSET=0xc0 -}; - -/* Internal functions and macros -------------------------------------------- */ - -/** - * Internal function for part of the UTRIE2_U8_NEXTxx() macro implementations. - * Do not call directly. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -utrie2_internalU8NextIndex(const UTrie2 *trie, UChar32 c, - const uint8_t *src, const uint8_t *limit); - -/** - * Internal function for part of the UTRIE2_U8_PREVxx() macro implementations. - * Do not call directly. - * @internal - */ -U_CAPI int32_t U_EXPORT2 -utrie2_internalU8PrevIndex(const UTrie2 *trie, UChar32 c, - const uint8_t *start, const uint8_t *src); - - -/** Internal low-level trie getter. Returns a data index. */ -#define _UTRIE2_INDEX_RAW(offset, trieIndex, c) \ - (((int32_t)((trieIndex)[(offset)+((c)>>UTRIE2_SHIFT_2)]) \ - <>UTRIE2_SHIFT_2), trieIndex, c) - -/** Internal trie getter from a BMP code point. Returns the data index. */ -#define _UTRIE2_INDEX_FROM_BMP(trieIndex, c) \ - _UTRIE2_INDEX_RAW(U_IS_LEAD(c) ? UTRIE2_LSCP_INDEX_2_OFFSET-(0xd800>>UTRIE2_SHIFT_2) : 0, \ - trieIndex, c) - -/** Internal trie getter from a supplementary code point below highStart. Returns the data index. */ -#define _UTRIE2_INDEX_FROM_SUPP(trieIndex, c) \ - (((int32_t)((trieIndex)[ \ - (trieIndex)[(UTRIE2_INDEX_1_OFFSET-UTRIE2_OMITTED_BMP_INDEX_1_LENGTH)+ \ - ((c)>>UTRIE2_SHIFT_1)]+ \ - (((c)>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK)]) \ - <index, c) : \ - (uint32_t)(c)<=0xffff ? \ - _UTRIE2_INDEX_RAW( \ - (c)<=0xdbff ? UTRIE2_LSCP_INDEX_2_OFFSET-(0xd800>>UTRIE2_SHIFT_2) : 0, \ - (trie)->index, c) : \ - (uint32_t)(c)>0x10ffff ? \ - (asciiOffset)+UTRIE2_BAD_UTF8_DATA_OFFSET : \ - (c)>=(trie)->highStart ? \ - (trie)->highValueIndex : \ - _UTRIE2_INDEX_FROM_SUPP((trie)->index, c)) - -/** Internal trie getter from a UTF-16 single/lead code unit. Returns the data. */ -#define _UTRIE2_GET_FROM_U16_SINGLE_LEAD(trie, data, c) \ - (trie)->data[_UTRIE2_INDEX_FROM_U16_SINGLE_LEAD((trie)->index, c)] - -/** Internal trie getter from a supplementary code point. Returns the data. */ -#define _UTRIE2_GET_FROM_SUPP(trie, data, c) \ - (trie)->data[(c)>=(trie)->highStart ? (trie)->highValueIndex : \ - _UTRIE2_INDEX_FROM_SUPP((trie)->index, c)] - -/** - * Internal trie getter from a code point, with checking that c is in 0..10FFFF. - * Returns the data. - */ -#define _UTRIE2_GET(trie, data, asciiOffset, c) \ - (trie)->data[_UTRIE2_INDEX_FROM_CP(trie, asciiOffset, c)] - -/** Internal next-post-increment: get the next code point (c) and its data. */ -#define _UTRIE2_U16_NEXT(trie, data, src, limit, c, result) UPRV_BLOCK_MACRO_BEGIN { \ - { \ - uint16_t __c2; \ - (c)=*(src)++; \ - if(!U16_IS_LEAD(c)) { \ - (result)=_UTRIE2_GET_FROM_U16_SINGLE_LEAD(trie, data, c); \ - } else if((src)==(limit) || !U16_IS_TRAIL(__c2=*(src))) { \ - (result)=(trie)->data[_UTRIE2_INDEX_FROM_LSCP((trie)->index, c)]; \ - } else { \ - ++(src); \ - (c)=U16_GET_SUPPLEMENTARY((c), __c2); \ - (result)=_UTRIE2_GET_FROM_SUPP((trie), data, (c)); \ - } \ - } \ -} UPRV_BLOCK_MACRO_END - -/** Internal pre-decrement-previous: get the previous code point (c) and its data */ -#define _UTRIE2_U16_PREV(trie, data, start, src, c, result) UPRV_BLOCK_MACRO_BEGIN { \ - { \ - uint16_t __c2; \ - (c)=*--(src); \ - if(!U16_IS_TRAIL(c) || (src)==(start) || !U16_IS_LEAD(__c2=*((src)-1))) { \ - (result)=(trie)->data[_UTRIE2_INDEX_FROM_BMP((trie)->index, c)]; \ - } else { \ - --(src); \ - (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \ - (result)=_UTRIE2_GET_FROM_SUPP((trie), data, (c)); \ - } \ - } \ -} UPRV_BLOCK_MACRO_END - -/** Internal UTF-8 next-post-increment: get the next code point's data. */ -#define _UTRIE2_U8_NEXT(trie, ascii, data, src, limit, result) UPRV_BLOCK_MACRO_BEGIN { \ - uint8_t __lead=(uint8_t)*(src)++; \ - if(U8_IS_SINGLE(__lead)) { \ - (result)=(trie)->ascii[__lead]; \ - } else { \ - uint8_t __t1, __t2; \ - if( /* handle U+0800..U+FFFF inline */ \ - 0xe0<=__lead && __lead<0xf0 && ((src)+1)<(limit) && \ - U8_IS_VALID_LEAD3_AND_T1(__lead, __t1=(uint8_t)*(src)) && \ - (__t2=(uint8_t)(*((src)+1)-0x80))<= 0x3f \ - ) { \ - (src)+=2; \ - (result)=(trie)->data[ \ - ((int32_t)((trie)->index[((__lead-0xe0)<<(12-UTRIE2_SHIFT_2))+ \ - ((__t1&0x3f)<<(6-UTRIE2_SHIFT_2))+(__t2>>UTRIE2_SHIFT_2)]) \ - <=0xc2 && (src)<(limit) && \ - (__t1=(uint8_t)(*(src)-0x80))<=0x3f \ - ) { \ - ++(src); \ - (result)=(trie)->data[ \ - (trie)->index[(UTRIE2_UTF8_2B_INDEX_2_OFFSET-0xc0)+__lead]+ \ - __t1]; \ - } else { \ - int32_t __index=utrie2_internalU8NextIndex((trie), __lead, (const uint8_t *)(src), \ - (const uint8_t *)(limit)); \ - (src)+=__index&7; \ - (result)=(trie)->data[__index>>3]; \ - } \ - } \ -} UPRV_BLOCK_MACRO_END - -/** Internal UTF-8 pre-decrement-previous: get the previous code point's data. */ -#define _UTRIE2_U8_PREV(trie, ascii, data, start, src, result) UPRV_BLOCK_MACRO_BEGIN { \ - uint8_t __b=(uint8_t)*--(src); \ - if(U8_IS_SINGLE(__b)) { \ - (result)=(trie)->ascii[__b]; \ - } else { \ - int32_t __index=utrie2_internalU8PrevIndex((trie), __b, (const uint8_t *)(start), \ - (const uint8_t *)(src)); \ - (src)-=__index&7; \ - (result)=(trie)->data[__index>>3]; \ - } \ -} UPRV_BLOCK_MACRO_END - -U_CDECL_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2001-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: utrie2.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2008aug16 (starting from a copy of utrie.h) +* created by: Markus W. Scherer +*/ + +#ifndef __UTRIE2_H__ +#define __UTRIE2_H__ + +#include "unicode/utypes.h" +#include "unicode/utf8.h" +#include "putilimp.h" + +U_CDECL_BEGIN + +struct UTrie; /* forward declaration */ +#ifndef __UTRIE_H__ +typedef struct UTrie UTrie; +#endif + +/** + * \file + * + * This is a common implementation of a Unicode trie. + * It is a kind of compressed, serializable table of 16- or 32-bit values associated with + * Unicode code points (0..0x10ffff). (A map from code points to integers.) + * + * This is the second common version of a Unicode trie (hence the name UTrie2). + * Compared with UTrie version 1: + * - Still splitting BMP code points 11:5 bits for index and data table lookups. + * - Still separate data for lead surrogate code _units_ vs. code _points_, + * but the lead surrogate code unit values are not required any more + * for data lookup for supplementary code points. + * - The "folding" mechanism is removed. In UTrie version 1, this somewhat + * hard-to-explain mechanism was meant to be used for optimized UTF-16 + * processing, with application-specific encoding of indexing bits + * in the lead surrogate data for the associated supplementary code points. + * - For the last single-value code point range (ending with U+10ffff), + * the starting code point ("highStart") and the value are stored. + * - For supplementary code points U+10000..highStart-1 a three-table lookup + * (two index tables and one data table) is used. The first index + * is truncated, omitting both the BMP portion and the high range. + * - There is a special small index for 2-byte UTF-8, and the initial data + * entries are designed for fast 1/2-byte UTF-8 lookup. + * Starting with ICU 60, C0 and C1 are not recognized as UTF-8 lead bytes any more at all, + * and the associated 2-byte indexes are unused. + */ + +/** + * Trie structure. + * Use only with public API macros and functions. + */ +struct UTrie2; +typedef struct UTrie2 UTrie2; + +/* Public UTrie2 API functions: read-only access ---------------------------- */ + +/** + * Selectors for the width of a UTrie2 data value. + */ +enum UTrie2ValueBits { + /** 16 bits per UTrie2 data value. */ + UTRIE2_16_VALUE_BITS, + /** 32 bits per UTrie2 data value. */ + UTRIE2_32_VALUE_BITS, + /** Number of selectors for the width of UTrie2 data values. */ + UTRIE2_COUNT_VALUE_BITS +}; +typedef enum UTrie2ValueBits UTrie2ValueBits; + +/** + * Open a frozen trie from its serialized from, stored in 32-bit-aligned memory. + * Inverse of utrie2_serialize(). + * The memory must remain valid and unchanged as long as the trie is used. + * You must utrie2_close() the trie once you are done using it. + * + * @param valueBits selects the data entry size; results in an + * U_INVALID_FORMAT_ERROR if it does not match the serialized form + * @param data a pointer to 32-bit-aligned memory containing the serialized form of a UTrie2 + * @param length the number of bytes available at data; + * can be more than necessary + * @param pActualLength receives the actual number of bytes at data taken up by the trie data; + * can be NULL + * @param pErrorCode an in/out ICU UErrorCode + * @return the unserialized trie + * + * @see utrie2_open + * @see utrie2_serialize + */ +U_CAPI UTrie2 * U_EXPORT2 +utrie2_openFromSerialized(UTrie2ValueBits valueBits, + const void *data, int32_t length, int32_t *pActualLength, + UErrorCode *pErrorCode); + +/** + * Open a frozen, empty "dummy" trie. + * A dummy trie is an empty trie, used when a real data trie cannot + * be loaded. Equivalent to calling utrie2_open() and utrie2_freeze(), + * but without internally creating and compacting/serializing the + * builder data structure. + * + * The trie always returns the initialValue, + * or the errorValue for out-of-range code points and illegal UTF-8. + * + * You must utrie2_close() the trie once you are done using it. + * + * @param valueBits selects the data entry size + * @param initialValue the initial value that is set for all code points + * @param errorValue the value for out-of-range code points and illegal UTF-8 + * @param pErrorCode an in/out ICU UErrorCode + * @return the dummy trie + * + * @see utrie2_openFromSerialized + * @see utrie2_open + */ +U_CAPI UTrie2 * U_EXPORT2 +utrie2_openDummy(UTrie2ValueBits valueBits, + uint32_t initialValue, uint32_t errorValue, + UErrorCode *pErrorCode); + +/** + * Get a value from a code point as stored in the trie. + * Easier to use than UTRIE2_GET16() and UTRIE2_GET32() but slower. + * Easier to use because, unlike the macros, this function works on all UTrie2 + * objects, frozen or not, holding 16-bit or 32-bit data values. + * + * @param trie the trie + * @param c the code point + * @return the value + */ +U_CAPI uint32_t U_EXPORT2 +utrie2_get32(const UTrie2 *trie, UChar32 c); + +/* enumeration callback types */ + +/** + * Callback from utrie2_enum(), extracts a uint32_t value from a + * trie value. This value will be passed on to the UTrie2EnumRange function. + * + * @param context an opaque pointer, as passed into utrie2_enum() + * @param value a value from the trie + * @return the value that is to be passed on to the UTrie2EnumRange function + */ +typedef uint32_t U_CALLCONV +UTrie2EnumValue(const void *context, uint32_t value); + +/** + * Callback from utrie2_enum(), is called for each contiguous range + * of code points with the same value as retrieved from the trie and + * transformed by the UTrie2EnumValue function. + * + * The callback function can stop the enumeration by returning false. + * + * @param context an opaque pointer, as passed into utrie2_enum() + * @param start the first code point in a contiguous range with value + * @param end the last code point in a contiguous range with value (inclusive) + * @param value the value that is set for all code points in [start..end] + * @return false to stop the enumeration + */ +typedef UBool U_CALLCONV +UTrie2EnumRange(const void *context, UChar32 start, UChar32 end, uint32_t value); + +/** + * Enumerate efficiently all values in a trie. + * Do not modify the trie during the enumeration. + * + * For each entry in the trie, the value to be delivered is passed through + * the UTrie2EnumValue function. + * The value is unchanged if that function pointer is NULL. + * + * For each contiguous range of code points with a given (transformed) value, + * the UTrie2EnumRange function is called. + * + * @param trie a pointer to the trie + * @param enumValue a pointer to a function that may transform the trie entry value, + * or NULL if the values from the trie are to be used directly + * @param enumRange a pointer to a function that is called for each contiguous range + * of code points with the same (transformed) value + * @param context an opaque pointer that is passed on to the callback functions + */ +U_CAPI void U_EXPORT2 +utrie2_enum(const UTrie2 *trie, + UTrie2EnumValue *enumValue, UTrie2EnumRange *enumRange, const void *context); + +/* Building a trie ---------------------------------------------------------- */ + +/** + * Open an empty, writable trie. At build time, 32-bit data values are used. + * utrie2_freeze() takes a valueBits parameter + * which determines the data value width in the serialized and frozen forms. + * You must utrie2_close() the trie once you are done using it. + * + * @param initialValue the initial value that is set for all code points + * @param errorValue the value for out-of-range code points and illegal UTF-8 + * @param pErrorCode an in/out ICU UErrorCode + * @return a pointer to the allocated and initialized new trie + */ +U_CAPI UTrie2 * U_EXPORT2 +utrie2_open(uint32_t initialValue, uint32_t errorValue, UErrorCode *pErrorCode); + +/** + * Clone a trie. + * You must utrie2_close() the clone once you are done using it. + * + * @param other the trie to clone + * @param pErrorCode an in/out ICU UErrorCode + * @return a pointer to the new trie clone + */ +U_CAPI UTrie2 * U_EXPORT2 +utrie2_clone(const UTrie2 *other, UErrorCode *pErrorCode); + +/** + * Clone a trie. The clone will be mutable/writable even if the other trie + * is frozen. (See utrie2_freeze().) + * You must utrie2_close() the clone once you are done using it. + * + * @param other the trie to clone + * @param pErrorCode an in/out ICU UErrorCode + * @return a pointer to the new trie clone + */ +U_CAPI UTrie2 * U_EXPORT2 +utrie2_cloneAsThawed(const UTrie2 *other, UErrorCode *pErrorCode); + +/** + * Close a trie and release associated memory. + * + * @param trie the trie + */ +U_CAPI void U_EXPORT2 +utrie2_close(UTrie2 *trie); + +/** + * Set a value for a code point. + * + * @param trie the unfrozen trie + * @param c the code point + * @param value the value + * @param pErrorCode an in/out ICU UErrorCode; among other possible error codes: + * - U_NO_WRITE_PERMISSION if the trie is frozen + */ +U_CAPI void U_EXPORT2 +utrie2_set32(UTrie2 *trie, UChar32 c, uint32_t value, UErrorCode *pErrorCode); + +/** + * Set a value in a range of code points [start..end]. + * All code points c with start<=c<=end will get the value if + * overwrite is true or if the old value is the initial value. + * + * @param trie the unfrozen trie + * @param start the first code point to get the value + * @param end the last code point to get the value (inclusive) + * @param value the value + * @param overwrite flag for whether old non-initial values are to be overwritten + * @param pErrorCode an in/out ICU UErrorCode; among other possible error codes: + * - U_NO_WRITE_PERMISSION if the trie is frozen + */ +U_CAPI void U_EXPORT2 +utrie2_setRange32(UTrie2 *trie, + UChar32 start, UChar32 end, + uint32_t value, UBool overwrite, + UErrorCode *pErrorCode); + +/** + * Freeze a trie. Make it immutable (read-only) and compact it, + * ready for serialization and for use with fast macros. + * Functions to set values will fail after serializing. + * + * A trie can be frozen only once. If this function is called again with different + * valueBits then it will set a U_ILLEGAL_ARGUMENT_ERROR. + * + * @param trie the trie + * @param valueBits selects the data entry size; if smaller than 32 bits, then + * the values stored in the trie will be truncated + * @param pErrorCode an in/out ICU UErrorCode; among other possible error codes: + * - U_INDEX_OUTOFBOUNDS_ERROR if the compacted index or data arrays are too long + * for serialization + * (the trie will be immutable and usable, + * but not frozen and not usable with the fast macros) + * + * @see utrie2_cloneAsThawed + */ +U_CAPI void U_EXPORT2 +utrie2_freeze(UTrie2 *trie, UTrie2ValueBits valueBits, UErrorCode *pErrorCode); + +/** + * Test if the trie is frozen. (See utrie2_freeze().) + * + * @param trie the trie + * @return true if the trie is frozen, that is, immutable, ready for serialization + * and for use with fast macros + */ +U_CAPI UBool U_EXPORT2 +utrie2_isFrozen(const UTrie2 *trie); + +/** + * Serialize a frozen trie into 32-bit aligned memory. + * If the trie is not frozen, then the function returns with a U_ILLEGAL_ARGUMENT_ERROR. + * A trie can be serialized multiple times. + * + * @param trie the frozen trie + * @param data a pointer to 32-bit-aligned memory to be filled with the trie data, + * can be NULL if capacity==0 + * @param capacity the number of bytes available at data, + * or 0 for preflighting + * @param pErrorCode an in/out ICU UErrorCode; among other possible error codes: + * - U_BUFFER_OVERFLOW_ERROR if the data storage block is too small for serialization + * - U_ILLEGAL_ARGUMENT_ERROR if the trie is not frozen or the data and capacity + * parameters are bad + * @return the number of bytes written or needed for the trie + * + * @see utrie2_openFromSerialized() + */ +U_CAPI int32_t U_EXPORT2 +utrie2_serialize(const UTrie2 *trie, + void *data, int32_t capacity, + UErrorCode *pErrorCode); + +/* Public UTrie2 API: miscellaneous functions ------------------------------- */ + +/** + * Build a UTrie2 (version 2) from a UTrie (version 1). + * Enumerates all values in the UTrie and builds a UTrie2 with the same values. + * The resulting UTrie2 will be frozen. + * + * @param trie1 the runtime UTrie structure to be enumerated + * @param errorValue the value for out-of-range code points and illegal UTF-8 + * @param pErrorCode an in/out ICU UErrorCode + * @return The frozen UTrie2 with the same values as the UTrie. + */ +U_CAPI UTrie2 * U_EXPORT2 +utrie2_fromUTrie(const UTrie *trie1, uint32_t errorValue, UErrorCode *pErrorCode); + +/* Public UTrie2 API macros ------------------------------------------------- */ + +/* + * These macros provide fast data lookup from a frozen trie. + * They will crash when used on an unfrozen trie. + */ + +/** + * Return a 16-bit trie value from a code point, with range checking. + * Returns trie->errorValue if c is not in the range 0..U+10ffff. + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param c (UChar32, in) the input code point + * @return (uint16_t) The code point's trie value. + */ +#define UTRIE2_GET16(trie, c) _UTRIE2_GET((trie), index, (trie)->indexLength, (c)) + +/** + * Return a 32-bit trie value from a code point, with range checking. + * Returns trie->errorValue if c is not in the range 0..U+10ffff. + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param c (UChar32, in) the input code point + * @return (uint32_t) The code point's trie value. + */ +#define UTRIE2_GET32(trie, c) _UTRIE2_GET((trie), data32, 0, (c)) + +/** + * UTF-16: Get the next code point (UChar32 c, out), post-increment src, + * and get a 16-bit value from the trie. + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param src (const UChar *, in/out) the source text pointer + * @param limit (const UChar *, in) the limit pointer for the text, or NULL if NUL-terminated + * @param c (UChar32, out) variable for the code point + * @param result (uint16_t, out) uint16_t variable for the trie lookup result + */ +#define UTRIE2_U16_NEXT16(trie, src, limit, c, result) _UTRIE2_U16_NEXT(trie, index, src, limit, c, result) + +/** + * UTF-16: Get the next code point (UChar32 c, out), post-increment src, + * and get a 32-bit value from the trie. + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param src (const UChar *, in/out) the source text pointer + * @param limit (const UChar *, in) the limit pointer for the text, or NULL if NUL-terminated + * @param c (UChar32, out) variable for the code point + * @param result (uint32_t, out) uint32_t variable for the trie lookup result + */ +#define UTRIE2_U16_NEXT32(trie, src, limit, c, result) _UTRIE2_U16_NEXT(trie, data32, src, limit, c, result) + +/** + * UTF-16: Get the previous code point (UChar32 c, out), pre-decrement src, + * and get a 16-bit value from the trie. + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param start (const UChar *, in) the start pointer for the text + * @param src (const UChar *, in/out) the source text pointer + * @param c (UChar32, out) variable for the code point + * @param result (uint16_t, out) uint16_t variable for the trie lookup result + */ +#define UTRIE2_U16_PREV16(trie, start, src, c, result) _UTRIE2_U16_PREV(trie, index, start, src, c, result) + +/** + * UTF-16: Get the previous code point (UChar32 c, out), pre-decrement src, + * and get a 32-bit value from the trie. + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param start (const UChar *, in) the start pointer for the text + * @param src (const UChar *, in/out) the source text pointer + * @param c (UChar32, out) variable for the code point + * @param result (uint32_t, out) uint32_t variable for the trie lookup result + */ +#define UTRIE2_U16_PREV32(trie, start, src, c, result) _UTRIE2_U16_PREV(trie, data32, start, src, c, result) + +/** + * UTF-8: Post-increment src and get a 16-bit value from the trie. + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param src (const char *, in/out) the source text pointer + * @param limit (const char *, in) the limit pointer for the text (must not be NULL) + * @param result (uint16_t, out) uint16_t variable for the trie lookup result + */ +#define UTRIE2_U8_NEXT16(trie, src, limit, result)\ + _UTRIE2_U8_NEXT(trie, data16, index, src, limit, result) + +/** + * UTF-8: Post-increment src and get a 32-bit value from the trie. + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param src (const char *, in/out) the source text pointer + * @param limit (const char *, in) the limit pointer for the text (must not be NULL) + * @param result (uint16_t, out) uint32_t variable for the trie lookup result + */ +#define UTRIE2_U8_NEXT32(trie, src, limit, result) \ + _UTRIE2_U8_NEXT(trie, data32, data32, src, limit, result) + +/** + * UTF-8: Pre-decrement src and get a 16-bit value from the trie. + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param start (const char *, in) the start pointer for the text + * @param src (const char *, in/out) the source text pointer + * @param result (uint16_t, out) uint16_t variable for the trie lookup result + */ +#define UTRIE2_U8_PREV16(trie, start, src, result) \ + _UTRIE2_U8_PREV(trie, data16, index, start, src, result) + +/** + * UTF-8: Pre-decrement src and get a 32-bit value from the trie. + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param start (const char *, in) the start pointer for the text + * @param src (const char *, in/out) the source text pointer + * @param result (uint16_t, out) uint32_t variable for the trie lookup result + */ +#define UTRIE2_U8_PREV32(trie, start, src, result) \ + _UTRIE2_U8_PREV(trie, data32, data32, start, src, result) + +/* Public UTrie2 API: optimized UTF-16 access ------------------------------- */ + +/* + * The following functions and macros are used for highly optimized UTF-16 + * text processing. The UTRIE2_U16_NEXTxy() macros do not depend on these. + * + * A UTrie2 stores separate values for lead surrogate code _units_ vs. code _points_. + * UTF-16 text processing can be optimized by detecting surrogate pairs and + * assembling supplementary code points only when there is non-trivial data + * available. + * + * At build-time, use utrie2_enumForLeadSurrogate() to see if there + * is non-trivial (non-initialValue) data for any of the supplementary + * code points associated with a lead surrogate. + * If so, then set a special (application-specific) value for the + * lead surrogate code _unit_, with utrie2_set32ForLeadSurrogateCodeUnit(). + * + * At runtime, use UTRIE2_GET16_FROM_U16_SINGLE_LEAD() or + * UTRIE2_GET32_FROM_U16_SINGLE_LEAD() per code unit. If there is non-trivial + * data and the code unit is a lead surrogate, then check if a trail surrogate + * follows. If so, assemble the supplementary code point with + * U16_GET_SUPPLEMENTARY() and look up its value with UTRIE2_GET16_FROM_SUPP() + * or UTRIE2_GET32_FROM_SUPP(); otherwise reset the lead + * surrogate's value or do a code point lookup for it. + * + * If there is only trivial data for lead and trail surrogates, then processing + * can often skip them. For example, in normalization or case mapping + * all characters that do not have any mappings are simply copied as is. + */ + +/** + * Get a value from a lead surrogate code unit as stored in the trie. + * + * @param trie the trie + * @param c the code unit (U+D800..U+DBFF) + * @return the value + */ +U_CAPI uint32_t U_EXPORT2 +utrie2_get32FromLeadSurrogateCodeUnit(const UTrie2 *trie, UChar32 c); + +/** + * Enumerate the trie values for the 1024=0x400 code points + * corresponding to a given lead surrogate. + * For example, for the lead surrogate U+D87E it will enumerate the values + * for [U+2F800..U+2FC00[. + * Used by data builder code that sets special lead surrogate code unit values + * for optimized UTF-16 string processing. + * + * Do not modify the trie during the enumeration. + * + * Except for the limited code point range, this functions just like utrie2_enum(): + * For each entry in the trie, the value to be delivered is passed through + * the UTrie2EnumValue function. + * The value is unchanged if that function pointer is NULL. + * + * For each contiguous range of code points with a given (transformed) value, + * the UTrie2EnumRange function is called. + * + * @param trie a pointer to the trie + * @param enumValue a pointer to a function that may transform the trie entry value, + * or NULL if the values from the trie are to be used directly + * @param enumRange a pointer to a function that is called for each contiguous range + * of code points with the same (transformed) value + * @param context an opaque pointer that is passed on to the callback functions + */ +U_CAPI void U_EXPORT2 +utrie2_enumForLeadSurrogate(const UTrie2 *trie, UChar32 lead, + UTrie2EnumValue *enumValue, UTrie2EnumRange *enumRange, + const void *context); + +/** + * Set a value for a lead surrogate code unit. + * + * @param trie the unfrozen trie + * @param lead the lead surrogate code unit (U+D800..U+DBFF) + * @param value the value + * @param pErrorCode an in/out ICU UErrorCode; among other possible error codes: + * - U_NO_WRITE_PERMISSION if the trie is frozen + */ +U_CAPI void U_EXPORT2 +utrie2_set32ForLeadSurrogateCodeUnit(UTrie2 *trie, + UChar32 lead, uint32_t value, + UErrorCode *pErrorCode); + +/** + * Return a 16-bit trie value from a UTF-16 single/lead code unit (<=U+ffff). + * Same as UTRIE2_GET16() if c is a BMP code point except for lead surrogates, + * but smaller and faster. + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param c (UChar32, in) the input code unit, must be 0<=c<=U+ffff + * @return (uint16_t) The code unit's trie value. + */ +#define UTRIE2_GET16_FROM_U16_SINGLE_LEAD(trie, c) _UTRIE2_GET_FROM_U16_SINGLE_LEAD((trie), index, c) + +/** + * Return a 32-bit trie value from a UTF-16 single/lead code unit (<=U+ffff). + * Same as UTRIE2_GET32() if c is a BMP code point except for lead surrogates, + * but smaller and faster. + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param c (UChar32, in) the input code unit, must be 0<=c<=U+ffff + * @return (uint32_t) The code unit's trie value. + */ +#define UTRIE2_GET32_FROM_U16_SINGLE_LEAD(trie, c) _UTRIE2_GET_FROM_U16_SINGLE_LEAD((trie), data32, c) + +/** + * Return a 16-bit trie value from a supplementary code point (U+10000..U+10ffff). + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param c (UChar32, in) the input code point, must be U+10000<=c<=U+10ffff + * @return (uint16_t) The code point's trie value. + */ +#define UTRIE2_GET16_FROM_SUPP(trie, c) _UTRIE2_GET_FROM_SUPP((trie), index, c) + +/** + * Return a 32-bit trie value from a supplementary code point (U+10000..U+10ffff). + * + * @param trie (const UTrie2 *, in) a frozen trie + * @param c (UChar32, in) the input code point, must be U+10000<=c<=U+10ffff + * @return (uint32_t) The code point's trie value. + */ +#define UTRIE2_GET32_FROM_SUPP(trie, c) _UTRIE2_GET_FROM_SUPP((trie), data32, c) + +U_CDECL_END + +/* C++ convenience wrappers ------------------------------------------------- */ + +#ifdef __cplusplus + +#include "unicode/uobject.h" +#include "unicode/utf.h" + +U_NAMESPACE_BEGIN + +// Use the Forward/Backward subclasses below. +class UTrie2StringIterator : public UMemory { +public: + UTrie2StringIterator(const UTrie2 *t, const char16_t *p) : + trie(t), codePointStart(p), codePointLimit(p), codePoint(U_SENTINEL) {} + + const UTrie2 *trie; + const char16_t *codePointStart, *codePointLimit; + UChar32 codePoint; +}; + +class BackwardUTrie2StringIterator : public UTrie2StringIterator { +public: + BackwardUTrie2StringIterator(const UTrie2 *t, const char16_t *s, const char16_t *p) : + UTrie2StringIterator(t, p), start(s) {} + + uint16_t previous16(); + + const char16_t *start; +}; + +class ForwardUTrie2StringIterator : public UTrie2StringIterator { +public: + // Iteration limit l can be nullptr. + // In that case, the caller must detect c==0 and stop. + ForwardUTrie2StringIterator(const UTrie2 *t, const char16_t *p, const char16_t *l) : + UTrie2StringIterator(t, p), limit(l) {} + + uint16_t next16(); + + const char16_t *limit; +}; + +U_NAMESPACE_END + +#endif + +/* Internal definitions ----------------------------------------------------- */ + +U_CDECL_BEGIN + +/** Build-time trie structure. */ +struct UNewTrie2; +typedef struct UNewTrie2 UNewTrie2; + +/* + * Trie structure definition. + * + * Either the data table is 16 bits wide and accessed via the index + * pointer, with each index item increased by indexLength; + * in this case, data32==NULL, and data16 is used for direct ASCII access. + * + * Or the data table is 32 bits wide and accessed via the data32 pointer. + */ +struct UTrie2 { + /* protected: used by macros and functions for reading values */ + const uint16_t *index; + const uint16_t *data16; /* for fast UTF-8 ASCII access, if 16b data */ + const uint32_t *data32; /* NULL if 16b data is used via index */ + + int32_t indexLength, dataLength; + uint16_t index2NullOffset; /* 0xffff if there is no dedicated index-2 null block */ + uint16_t dataNullOffset; + uint32_t initialValue; + /** Value returned for out-of-range code points and illegal UTF-8. */ + uint32_t errorValue; + + /* Start of the last range which ends at U+10ffff, and its value. */ + UChar32 highStart; + int32_t highValueIndex; + + /* private: used by builder and unserialization functions */ + void *memory; /* serialized bytes; NULL if not frozen yet */ + int32_t length; /* number of serialized bytes at memory; 0 if not frozen yet */ + UBool isMemoryOwned; /* true if the trie owns the memory */ + UBool padding1; + int16_t padding2; + UNewTrie2 *newTrie; /* builder object; NULL when frozen */ + +#ifdef UTRIE2_DEBUG + const char *name; +#endif +}; + +/** + * Trie constants, defining shift widths, index array lengths, etc. + * + * These are needed for the runtime macros but users can treat these as + * implementation details and skip to the actual public API further below. + */ +enum { + /** Shift size for getting the index-1 table offset. */ + UTRIE2_SHIFT_1=6+5, + + /** Shift size for getting the index-2 table offset. */ + UTRIE2_SHIFT_2=5, + + /** + * Difference between the two shift sizes, + * for getting an index-1 offset from an index-2 offset. 6=11-5 + */ + UTRIE2_SHIFT_1_2=UTRIE2_SHIFT_1-UTRIE2_SHIFT_2, + + /** + * Number of index-1 entries for the BMP. 32=0x20 + * This part of the index-1 table is omitted from the serialized form. + */ + UTRIE2_OMITTED_BMP_INDEX_1_LENGTH=0x10000>>UTRIE2_SHIFT_1, + + /** Number of code points per index-1 table entry. 2048=0x800 */ + UTRIE2_CP_PER_INDEX_1_ENTRY=1<>UTRIE2_SHIFT_2. + */ + UTRIE2_INDEX_2_OFFSET=0, + + /** + * The part of the index-2 table for U+D800..U+DBFF stores values for + * lead surrogate code _units_ not code _points_. + * Values for lead surrogate code _points_ are indexed with this portion of the table. + * Length=32=0x20=0x400>>UTRIE2_SHIFT_2. (There are 1024=0x400 lead surrogates.) + */ + UTRIE2_LSCP_INDEX_2_OFFSET=0x10000>>UTRIE2_SHIFT_2, + UTRIE2_LSCP_INDEX_2_LENGTH=0x400>>UTRIE2_SHIFT_2, + + /** Count the lengths of both BMP pieces. 2080=0x820 */ + UTRIE2_INDEX_2_BMP_LENGTH=UTRIE2_LSCP_INDEX_2_OFFSET+UTRIE2_LSCP_INDEX_2_LENGTH, + + /** + * The 2-byte UTF-8 version of the index-2 table follows at offset 2080=0x820. + * Length 32=0x20 for lead bytes C0..DF, regardless of UTRIE2_SHIFT_2. + */ + UTRIE2_UTF8_2B_INDEX_2_OFFSET=UTRIE2_INDEX_2_BMP_LENGTH, + UTRIE2_UTF8_2B_INDEX_2_LENGTH=0x800>>6, /* U+0800 is the first code point after 2-byte UTF-8 */ + + /** + * The index-1 table, only used for supplementary code points, at offset 2112=0x840. + * Variable length, for code points up to highStart, where the last single-value range starts. + * Maximum length 512=0x200=0x100000>>UTRIE2_SHIFT_1. + * (For 0x100000 supplementary code points U+10000..U+10ffff.) + * + * The part of the index-2 table for supplementary code points starts + * after this index-1 table. + * + * Both the index-1 table and the following part of the index-2 table + * are omitted completely if there is only BMP data. + */ + UTRIE2_INDEX_1_OFFSET=UTRIE2_UTF8_2B_INDEX_2_OFFSET+UTRIE2_UTF8_2B_INDEX_2_LENGTH, + UTRIE2_MAX_INDEX_1_LENGTH=0x100000>>UTRIE2_SHIFT_1, + + /* + * Fixed layout of the first part of the data array. ----------------------- + * Starts with 4 blocks (128=0x80 entries) for ASCII. + */ + + /** + * The illegal-UTF-8 data block follows the ASCII block, at offset 128=0x80. + * Used with linear access for single bytes 0..0xbf for simple error handling. + * Length 64=0x40, not UTRIE2_DATA_BLOCK_LENGTH. + */ + UTRIE2_BAD_UTF8_DATA_OFFSET=0x80, + + /** The start of non-linear-ASCII data blocks, at offset 192=0xc0. */ + UTRIE2_DATA_START_OFFSET=0xc0 +}; + +/* Internal functions and macros -------------------------------------------- */ + +/** + * Internal function for part of the UTRIE2_U8_NEXTxx() macro implementations. + * Do not call directly. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +utrie2_internalU8NextIndex(const UTrie2 *trie, UChar32 c, + const uint8_t *src, const uint8_t *limit); + +/** + * Internal function for part of the UTRIE2_U8_PREVxx() macro implementations. + * Do not call directly. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +utrie2_internalU8PrevIndex(const UTrie2 *trie, UChar32 c, + const uint8_t *start, const uint8_t *src); + + +/** Internal low-level trie getter. Returns a data index. */ +#define _UTRIE2_INDEX_RAW(offset, trieIndex, c) \ + (((int32_t)((trieIndex)[(offset)+((c)>>UTRIE2_SHIFT_2)]) \ + <>UTRIE2_SHIFT_2), trieIndex, c) + +/** Internal trie getter from a BMP code point. Returns the data index. */ +#define _UTRIE2_INDEX_FROM_BMP(trieIndex, c) \ + _UTRIE2_INDEX_RAW(U_IS_LEAD(c) ? UTRIE2_LSCP_INDEX_2_OFFSET-(0xd800>>UTRIE2_SHIFT_2) : 0, \ + trieIndex, c) + +/** Internal trie getter from a supplementary code point below highStart. Returns the data index. */ +#define _UTRIE2_INDEX_FROM_SUPP(trieIndex, c) \ + (((int32_t)((trieIndex)[ \ + (trieIndex)[(UTRIE2_INDEX_1_OFFSET-UTRIE2_OMITTED_BMP_INDEX_1_LENGTH)+ \ + ((c)>>UTRIE2_SHIFT_1)]+ \ + (((c)>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK)]) \ + <index, c) : \ + (uint32_t)(c)<=0xffff ? \ + _UTRIE2_INDEX_RAW( \ + (c)<=0xdbff ? UTRIE2_LSCP_INDEX_2_OFFSET-(0xd800>>UTRIE2_SHIFT_2) : 0, \ + (trie)->index, c) : \ + (uint32_t)(c)>0x10ffff ? \ + (asciiOffset)+UTRIE2_BAD_UTF8_DATA_OFFSET : \ + (c)>=(trie)->highStart ? \ + (trie)->highValueIndex : \ + _UTRIE2_INDEX_FROM_SUPP((trie)->index, c)) + +/** Internal trie getter from a UTF-16 single/lead code unit. Returns the data. */ +#define _UTRIE2_GET_FROM_U16_SINGLE_LEAD(trie, data, c) \ + (trie)->data[_UTRIE2_INDEX_FROM_U16_SINGLE_LEAD((trie)->index, c)] + +/** Internal trie getter from a supplementary code point. Returns the data. */ +#define _UTRIE2_GET_FROM_SUPP(trie, data, c) \ + (trie)->data[(c)>=(trie)->highStart ? (trie)->highValueIndex : \ + _UTRIE2_INDEX_FROM_SUPP((trie)->index, c)] + +/** + * Internal trie getter from a code point, with checking that c is in 0..10FFFF. + * Returns the data. + */ +#define _UTRIE2_GET(trie, data, asciiOffset, c) \ + (trie)->data[_UTRIE2_INDEX_FROM_CP(trie, asciiOffset, c)] + +/** Internal next-post-increment: get the next code point (c) and its data. */ +#define _UTRIE2_U16_NEXT(trie, data, src, limit, c, result) UPRV_BLOCK_MACRO_BEGIN { \ + { \ + uint16_t __c2; \ + (c)=*(src)++; \ + if(!U16_IS_LEAD(c)) { \ + (result)=_UTRIE2_GET_FROM_U16_SINGLE_LEAD(trie, data, c); \ + } else if((src)==(limit) || !U16_IS_TRAIL(__c2=*(src))) { \ + (result)=(trie)->data[_UTRIE2_INDEX_FROM_LSCP((trie)->index, c)]; \ + } else { \ + ++(src); \ + (c)=U16_GET_SUPPLEMENTARY((c), __c2); \ + (result)=_UTRIE2_GET_FROM_SUPP((trie), data, (c)); \ + } \ + } \ +} UPRV_BLOCK_MACRO_END + +/** Internal pre-decrement-previous: get the previous code point (c) and its data */ +#define _UTRIE2_U16_PREV(trie, data, start, src, c, result) UPRV_BLOCK_MACRO_BEGIN { \ + { \ + uint16_t __c2; \ + (c)=*--(src); \ + if(!U16_IS_TRAIL(c) || (src)==(start) || !U16_IS_LEAD(__c2=*((src)-1))) { \ + (result)=(trie)->data[_UTRIE2_INDEX_FROM_BMP((trie)->index, c)]; \ + } else { \ + --(src); \ + (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \ + (result)=_UTRIE2_GET_FROM_SUPP((trie), data, (c)); \ + } \ + } \ +} UPRV_BLOCK_MACRO_END + +/** Internal UTF-8 next-post-increment: get the next code point's data. */ +#define _UTRIE2_U8_NEXT(trie, ascii, data, src, limit, result) UPRV_BLOCK_MACRO_BEGIN { \ + uint8_t __lead=(uint8_t)*(src)++; \ + if(U8_IS_SINGLE(__lead)) { \ + (result)=(trie)->ascii[__lead]; \ + } else { \ + uint8_t __t1, __t2; \ + if( /* handle U+0800..U+FFFF inline */ \ + 0xe0<=__lead && __lead<0xf0 && ((src)+1)<(limit) && \ + U8_IS_VALID_LEAD3_AND_T1(__lead, __t1=(uint8_t)*(src)) && \ + (__t2=(uint8_t)(*((src)+1)-0x80))<= 0x3f \ + ) { \ + (src)+=2; \ + (result)=(trie)->data[ \ + ((int32_t)((trie)->index[((__lead-0xe0)<<(12-UTRIE2_SHIFT_2))+ \ + ((__t1&0x3f)<<(6-UTRIE2_SHIFT_2))+(__t2>>UTRIE2_SHIFT_2)]) \ + <=0xc2 && (src)<(limit) && \ + (__t1=(uint8_t)(*(src)-0x80))<=0x3f \ + ) { \ + ++(src); \ + (result)=(trie)->data[ \ + (trie)->index[(UTRIE2_UTF8_2B_INDEX_2_OFFSET-0xc0)+__lead]+ \ + __t1]; \ + } else { \ + int32_t __index=utrie2_internalU8NextIndex((trie), __lead, (const uint8_t *)(src), \ + (const uint8_t *)(limit)); \ + (src)+=__index&7; \ + (result)=(trie)->data[__index>>3]; \ + } \ + } \ +} UPRV_BLOCK_MACRO_END + +/** Internal UTF-8 pre-decrement-previous: get the previous code point's data. */ +#define _UTRIE2_U8_PREV(trie, ascii, data, start, src, result) UPRV_BLOCK_MACRO_BEGIN { \ + uint8_t __b=(uint8_t)*--(src); \ + if(U8_IS_SINGLE(__b)) { \ + (result)=(trie)->ascii[__b]; \ + } else { \ + int32_t __index=utrie2_internalU8PrevIndex((trie), __b, (const uint8_t *)(start), \ + (const uint8_t *)(src)); \ + (src)-=__index&7; \ + (result)=(trie)->data[__index>>3]; \ + } \ +} UPRV_BLOCK_MACRO_END + +U_CDECL_END + +#endif diff --git a/deps/icu-small/source/common/utrie2_builder.cpp b/deps/icu-small/source/common/utrie2_builder.cpp index 2513332b80a7c4..e2a71dc66bbbbd 100644 --- a/deps/icu-small/source/common/utrie2_builder.cpp +++ b/deps/icu-small/source/common/utrie2_builder.cpp @@ -1,1483 +1,1483 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2001-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: utrie2_builder.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2008sep26 (split off from utrie2.c) -* created by: Markus W. Scherer -* -* This is a common implementation of a Unicode trie. -* It is a kind of compressed, serializable table of 16- or 32-bit values associated with -* Unicode code points (0..0x10ffff). -* This is the second common version of a Unicode trie (hence the name UTrie2). -* See utrie2.h for a comparison. -* -* This file contains only the builder code. -* See utrie2.c for the runtime and enumeration code. -*/ -// #define UTRIE2_DEBUG -#ifdef UTRIE2_DEBUG -# include -#endif -// #define UCPTRIE_DEBUG - -#include "unicode/utypes.h" -#ifdef UCPTRIE_DEBUG -#include "unicode/ucptrie.h" -#include "unicode/umutablecptrie.h" -#include "ucptrie_impl.h" -#endif -#include "cmemory.h" -#include "utrie2.h" -#include "utrie2_impl.h" - -#include "utrie.h" // for utrie2_fromUTrie() - -/* Implementation notes ----------------------------------------------------- */ - -/* - * The UTRIE2_SHIFT_1, UTRIE2_SHIFT_2, UTRIE2_INDEX_SHIFT and other values - * have been chosen to minimize trie sizes overall. - * Most of the code is flexible enough to work with a range of values, - * within certain limits. - * - * Exception: Support for separate values for lead surrogate code _units_ - * vs. code _points_ was added after the constants were fixed, - * and has not been tested nor particularly designed for different constant values. - * (Especially the utrie2_enum() code that jumps to the special LSCP index-2 - * part and back.) - * - * Requires UTRIE2_SHIFT_2<=6. Otherwise 0xc0 which is the top of the ASCII-linear data - * including the bad-UTF-8-data block is not a multiple of UTRIE2_DATA_BLOCK_LENGTH - * and map[block>>UTRIE2_SHIFT_2] (used in reference counting and compaction - * remapping) stops working. - * - * Requires UTRIE2_SHIFT_1>=10 because utrie2_enumForLeadSurrogate() - * assumes that a single index-2 block is used for 0x400 code points - * corresponding to one lead surrogate. - * - * Requires UTRIE2_SHIFT_1<=16. Otherwise one single index-2 block contains - * more than one Unicode plane, and the split of the index-2 table into a BMP - * part and a supplementary part, with a gap in between, would not work. - * - * Requires UTRIE2_INDEX_SHIFT>=1 not because of the code but because - * there is data with more than 64k distinct values, - * for example for Unihan collation with a separate collation weight per - * Han character. - */ - -/* Building a trie ----------------------------------------------------------*/ - -enum { - /** The null index-2 block, following the gap in the index-2 table. */ - UNEWTRIE2_INDEX_2_NULL_OFFSET=UNEWTRIE2_INDEX_GAP_OFFSET+UNEWTRIE2_INDEX_GAP_LENGTH, - - /** The start of allocated index-2 blocks. */ - UNEWTRIE2_INDEX_2_START_OFFSET=UNEWTRIE2_INDEX_2_NULL_OFFSET+UTRIE2_INDEX_2_BLOCK_LENGTH, - - /** - * The null data block. - * Length 64=0x40 even if UTRIE2_DATA_BLOCK_LENGTH is smaller, - * to work with 6-bit trail bytes from 2-byte UTF-8. - */ - UNEWTRIE2_DATA_NULL_OFFSET=UTRIE2_DATA_START_OFFSET, - - /** The start of allocated data blocks. */ - UNEWTRIE2_DATA_START_OFFSET=UNEWTRIE2_DATA_NULL_OFFSET+0x40, - - /** - * The start of data blocks for U+0800 and above. - * Below, compaction uses a block length of 64 for 2-byte UTF-8. - * From here on, compaction uses UTRIE2_DATA_BLOCK_LENGTH. - * Data values for 0x780 code points beyond ASCII. - */ - UNEWTRIE2_DATA_0800_OFFSET=UNEWTRIE2_DATA_START_OFFSET+0x780 -}; - -/* Start with allocation of 16k data entries. */ -#define UNEWTRIE2_INITIAL_DATA_LENGTH ((int32_t)1<<14) - -/* Grow about 8x each time. */ -#define UNEWTRIE2_MEDIUM_DATA_LENGTH ((int32_t)1<<17) - -static int32_t -allocIndex2Block(UNewTrie2 *trie); - -U_CAPI UTrie2 * U_EXPORT2 -utrie2_open(uint32_t initialValue, uint32_t errorValue, UErrorCode *pErrorCode) { - UTrie2 *trie; - UNewTrie2 *newTrie; - uint32_t *data; - int32_t i, j; - - if(U_FAILURE(*pErrorCode)) { - return NULL; - } - - trie=(UTrie2 *)uprv_malloc(sizeof(UTrie2)); - newTrie=(UNewTrie2 *)uprv_malloc(sizeof(UNewTrie2)); - data=(uint32_t *)uprv_malloc(UNEWTRIE2_INITIAL_DATA_LENGTH*4); - if(trie==NULL || newTrie==NULL || data==NULL) { - uprv_free(trie); - uprv_free(newTrie); - uprv_free(data); - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return 0; - } - - uprv_memset(trie, 0, sizeof(UTrie2)); - trie->initialValue=initialValue; - trie->errorValue=errorValue; - trie->highStart=0x110000; - trie->newTrie=newTrie; -#ifdef UTRIE2_DEBUG - trie->name="open"; -#endif - - newTrie->data=data; -#ifdef UCPTRIE_DEBUG - newTrie->t3=umutablecptrie_open(initialValue, errorValue, pErrorCode); -#endif - newTrie->dataCapacity=UNEWTRIE2_INITIAL_DATA_LENGTH; - newTrie->initialValue=initialValue; - newTrie->errorValue=errorValue; - newTrie->highStart=0x110000; - newTrie->firstFreeBlock=0; /* no free block in the list */ - newTrie->isCompacted=false; - - /* - * preallocate and reset - * - ASCII - * - the bad-UTF-8-data block - * - the null data block - */ - for(i=0; i<0x80; ++i) { - newTrie->data[i]=initialValue; - } - for(; i<0xc0; ++i) { - newTrie->data[i]=errorValue; - } - for(i=UNEWTRIE2_DATA_NULL_OFFSET; idata[i]=initialValue; - } - newTrie->dataNullOffset=UNEWTRIE2_DATA_NULL_OFFSET; - newTrie->dataLength=UNEWTRIE2_DATA_START_OFFSET; - - /* set the index-2 indexes for the 2=0x80>>UTRIE2_SHIFT_2 ASCII data blocks */ - for(i=0, j=0; j<0x80; ++i, j+=UTRIE2_DATA_BLOCK_LENGTH) { - newTrie->index2[i]=j; - newTrie->map[i]=1; - } - /* reference counts for the bad-UTF-8-data block */ - for(; j<0xc0; ++i, j+=UTRIE2_DATA_BLOCK_LENGTH) { - newTrie->map[i]=0; - } - /* - * Reference counts for the null data block: all blocks except for the ASCII blocks. - * Plus 1 so that we don't drop this block during compaction. - * Plus as many as needed for lead surrogate code points. - */ - /* i==newTrie->dataNullOffset */ - newTrie->map[i++]= - (0x110000>>UTRIE2_SHIFT_2)- - (0x80>>UTRIE2_SHIFT_2)+ - 1+ - UTRIE2_LSCP_INDEX_2_LENGTH; - j+=UTRIE2_DATA_BLOCK_LENGTH; - for(; jmap[i]=0; - } - - /* - * set the remaining indexes in the BMP index-2 block - * to the null data block - */ - for(i=0x80>>UTRIE2_SHIFT_2; iindex2[i]=UNEWTRIE2_DATA_NULL_OFFSET; - } - - /* - * Fill the index gap with impossible values so that compaction - * does not overlap other index-2 blocks with the gap. - */ - for(i=0; iindex2[UNEWTRIE2_INDEX_GAP_OFFSET+i]=-1; - } - - /* set the indexes in the null index-2 block */ - for(i=0; iindex2[UNEWTRIE2_INDEX_2_NULL_OFFSET+i]=UNEWTRIE2_DATA_NULL_OFFSET; - } - newTrie->index2NullOffset=UNEWTRIE2_INDEX_2_NULL_OFFSET; - newTrie->index2Length=UNEWTRIE2_INDEX_2_START_OFFSET; - - /* set the index-1 indexes for the linear index-2 block */ - for(i=0, j=0; - iindex1[i]=j; - } - - /* set the remaining index-1 indexes to the null index-2 block */ - for(; iindex1[i]=UNEWTRIE2_INDEX_2_NULL_OFFSET; - } - - /* - * Preallocate and reset data for U+0080..U+07ff, - * for 2-byte UTF-8 which will be compacted in 64-blocks - * even if UTRIE2_DATA_BLOCK_LENGTH is smaller. - */ - for(i=0x80; i<0x800; i+=UTRIE2_DATA_BLOCK_LENGTH) { - utrie2_set32(trie, i, initialValue, pErrorCode); - } - - return trie; -} - -static UNewTrie2 * -cloneBuilder(const UNewTrie2 *other) { - UNewTrie2 *trie; - - trie=(UNewTrie2 *)uprv_malloc(sizeof(UNewTrie2)); - if(trie==NULL) { - return NULL; - } - - trie->data=(uint32_t *)uprv_malloc(other->dataCapacity*4); - if(trie->data==NULL) { - uprv_free(trie); - return NULL; - } -#ifdef UCPTRIE_DEBUG - if(other->t3==nullptr) { - trie->t3=nullptr; - } else { - UErrorCode errorCode=U_ZERO_ERROR; - trie->t3=umutablecptrie_clone(other->t3, &errorCode); - } -#endif - trie->dataCapacity=other->dataCapacity; - - /* clone data */ - uprv_memcpy(trie->index1, other->index1, sizeof(trie->index1)); - uprv_memcpy(trie->index2, other->index2, (size_t)other->index2Length*4); - trie->index2NullOffset=other->index2NullOffset; - trie->index2Length=other->index2Length; - - uprv_memcpy(trie->data, other->data, (size_t)other->dataLength*4); - trie->dataNullOffset=other->dataNullOffset; - trie->dataLength=other->dataLength; - - /* reference counters */ - if(other->isCompacted) { - trie->firstFreeBlock=0; - } else { - uprv_memcpy(trie->map, other->map, ((size_t)other->dataLength>>UTRIE2_SHIFT_2)*4); - trie->firstFreeBlock=other->firstFreeBlock; - } - - trie->initialValue=other->initialValue; - trie->errorValue=other->errorValue; - trie->highStart=other->highStart; - trie->isCompacted=other->isCompacted; - - return trie; -} - -U_CAPI UTrie2 * U_EXPORT2 -utrie2_clone(const UTrie2 *other, UErrorCode *pErrorCode) { - UTrie2 *trie; - - if(U_FAILURE(*pErrorCode)) { - return NULL; - } - if(other==NULL || (other->memory==NULL && other->newTrie==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - trie=(UTrie2 *)uprv_malloc(sizeof(UTrie2)); - if(trie==NULL) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uprv_memcpy(trie, other, sizeof(UTrie2)); - - if(other->memory!=NULL) { - trie->memory=uprv_malloc(other->length); - if(trie->memory!=NULL) { - trie->isMemoryOwned=true; - uprv_memcpy(trie->memory, other->memory, other->length); - - /* make the clone's pointers point to its own memory */ - trie->index=(uint16_t *)trie->memory+(other->index-(uint16_t *)other->memory); - if(other->data16!=NULL) { - trie->data16=(uint16_t *)trie->memory+(other->data16-(uint16_t *)other->memory); - } - if(other->data32!=NULL) { - trie->data32=(uint32_t *)trie->memory+(other->data32-(uint32_t *)other->memory); - } - } - } else /* other->newTrie!=NULL */ { - trie->newTrie=cloneBuilder(other->newTrie); - } - - if(trie->memory==NULL && trie->newTrie==NULL) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - uprv_free(trie); - trie=NULL; - } - return trie; -} - -typedef struct NewTrieAndStatus { - UTrie2 *trie; - UErrorCode errorCode; - UBool exclusiveLimit; /* rather than inclusive range end */ -} NewTrieAndStatus; - -static UBool U_CALLCONV -copyEnumRange(const void *context, UChar32 start, UChar32 end, uint32_t value) { - NewTrieAndStatus *nt=(NewTrieAndStatus *)context; - if(value!=nt->trie->initialValue) { - if(nt->exclusiveLimit) { - --end; - } - if(start==end) { - utrie2_set32(nt->trie, start, value, &nt->errorCode); - } else { - utrie2_setRange32(nt->trie, start, end, value, true, &nt->errorCode); - } - return U_SUCCESS(nt->errorCode); - } else { - return true; - } -} - -#ifdef UTRIE2_DEBUG -static long countInitial(const UTrie2 *trie) { - uint32_t initialValue=trie->initialValue; - int32_t length=trie->dataLength; - long count=0; - if(trie->data16!=nullptr) { - for(int32_t i=0; idata16[i]==initialValue) { ++count; } - } - } else { - for(int32_t i=0; idata32[i]==initialValue) { ++count; } - } - } - return count; -} - -static void -utrie_printLengths(const UTrie *trie) { - long indexLength=trie->indexLength; - long dataLength=(long)trie->dataLength; - long totalLength=(long)sizeof(UTrieHeader)+indexLength*2+dataLength*(trie->data32!=NULL ? 4 : 2); - printf("**UTrieLengths** index:%6ld data:%6ld serialized:%6ld\n", - indexLength, dataLength, totalLength); -} - -static void -utrie2_printLengths(const UTrie2 *trie, const char *which) { - long indexLength=trie->indexLength; - long dataLength=(long)trie->dataLength; - long totalLength=(long)sizeof(UTrie2Header)+indexLength*2+dataLength*(trie->data32!=NULL ? 4 : 2); - printf("**UTrie2Lengths(%s %s)** index:%6ld data:%6ld countInitial:%6ld serialized:%6ld\n", - which, trie->name, indexLength, dataLength, countInitial(trie), totalLength); -} -#endif - -U_CAPI UTrie2 * U_EXPORT2 -utrie2_cloneAsThawed(const UTrie2 *other, UErrorCode *pErrorCode) { - NewTrieAndStatus context; - UChar lead; - - if(U_FAILURE(*pErrorCode)) { - return NULL; - } - if(other==NULL || (other->memory==NULL && other->newTrie==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - if(other->newTrie!=NULL && !other->newTrie->isCompacted) { - return utrie2_clone(other, pErrorCode); /* clone an unfrozen trie */ - } - - /* Clone the frozen trie by enumerating it and building a new one. */ - context.trie=utrie2_open(other->initialValue, other->errorValue, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return NULL; - } - context.exclusiveLimit=false; - context.errorCode=*pErrorCode; - utrie2_enum(other, NULL, copyEnumRange, &context); - *pErrorCode=context.errorCode; - for(lead=0xd800; lead<0xdc00; ++lead) { - uint32_t value; - if(other->data32==NULL) { - value=UTRIE2_GET16_FROM_U16_SINGLE_LEAD(other, lead); - } else { - value=UTRIE2_GET32_FROM_U16_SINGLE_LEAD(other, lead); - } - if(value!=other->initialValue) { - utrie2_set32ForLeadSurrogateCodeUnit(context.trie, lead, value, pErrorCode); - } - } - if(U_FAILURE(*pErrorCode)) { - utrie2_close(context.trie); - context.trie=NULL; - } - return context.trie; -} - -/* Almost the same as utrie2_cloneAsThawed() but copies a UTrie and freezes the clone. */ -U_CAPI UTrie2 * U_EXPORT2 -utrie2_fromUTrie(const UTrie *trie1, uint32_t errorValue, UErrorCode *pErrorCode) { - NewTrieAndStatus context; - UChar lead; - - if(U_FAILURE(*pErrorCode)) { - return NULL; - } - if(trie1==NULL) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - context.trie=utrie2_open(trie1->initialValue, errorValue, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return NULL; - } - context.exclusiveLimit=true; - context.errorCode=*pErrorCode; - utrie_enum(trie1, NULL, copyEnumRange, &context); - *pErrorCode=context.errorCode; - for(lead=0xd800; lead<0xdc00; ++lead) { - uint32_t value; - if(trie1->data32==NULL) { - value=UTRIE_GET16_FROM_LEAD(trie1, lead); - } else { - value=UTRIE_GET32_FROM_LEAD(trie1, lead); - } - if(value!=trie1->initialValue) { - utrie2_set32ForLeadSurrogateCodeUnit(context.trie, lead, value, pErrorCode); - } - } - if(U_SUCCESS(*pErrorCode)) { - utrie2_freeze(context.trie, - trie1->data32!=NULL ? UTRIE2_32_VALUE_BITS : UTRIE2_16_VALUE_BITS, - pErrorCode); - } -#ifdef UTRIE2_DEBUG - if(U_SUCCESS(*pErrorCode)) { - utrie_printLengths(trie1); - utrie2_printLengths(context.trie, "fromUTrie"); - } -#endif - if(U_FAILURE(*pErrorCode)) { - utrie2_close(context.trie); - context.trie=NULL; - } - return context.trie; -} - -static inline UBool -isInNullBlock(UNewTrie2 *trie, UChar32 c, UBool forLSCP) { - int32_t i2, block; - - if(U_IS_LEAD(c) && forLSCP) { - i2=(UTRIE2_LSCP_INDEX_2_OFFSET-(0xd800>>UTRIE2_SHIFT_2))+ - (c>>UTRIE2_SHIFT_2); - } else { - i2=trie->index1[c>>UTRIE2_SHIFT_1]+ - ((c>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK); - } - block=trie->index2[i2]; - return (UBool)(block==trie->dataNullOffset); -} - -static int32_t -allocIndex2Block(UNewTrie2 *trie) { - int32_t newBlock, newTop; - - newBlock=trie->index2Length; - newTop=newBlock+UTRIE2_INDEX_2_BLOCK_LENGTH; - if(newTop>UPRV_LENGTHOF(trie->index2)) { - /* - * Should never occur. - * Either UTRIE2_MAX_BUILD_TIME_INDEX_LENGTH is incorrect, - * or the code writes more values than should be possible. - */ - return -1; - } - trie->index2Length=newTop; - uprv_memcpy(trie->index2+newBlock, trie->index2+trie->index2NullOffset, UTRIE2_INDEX_2_BLOCK_LENGTH*4); - return newBlock; -} - -static int32_t -getIndex2Block(UNewTrie2 *trie, UChar32 c, UBool forLSCP) { - int32_t i1, i2; - - if(U_IS_LEAD(c) && forLSCP) { - return UTRIE2_LSCP_INDEX_2_OFFSET; - } - - i1=c>>UTRIE2_SHIFT_1; - i2=trie->index1[i1]; - if(i2==trie->index2NullOffset) { - i2=allocIndex2Block(trie); - if(i2<0) { - return -1; /* program error */ - } - trie->index1[i1]=i2; - } - return i2; -} - -static int32_t -allocDataBlock(UNewTrie2 *trie, int32_t copyBlock) { - int32_t newBlock, newTop; - - if(trie->firstFreeBlock!=0) { - /* get the first free block */ - newBlock=trie->firstFreeBlock; - trie->firstFreeBlock=-trie->map[newBlock>>UTRIE2_SHIFT_2]; - } else { - /* get a new block from the high end */ - newBlock=trie->dataLength; - newTop=newBlock+UTRIE2_DATA_BLOCK_LENGTH; - if(newTop>trie->dataCapacity) { - /* out of memory in the data array */ - int32_t capacity; - uint32_t *data; - - if(trie->dataCapacitydataCapacitydata, (size_t)trie->dataLength*4); - uprv_free(trie->data); - trie->data=data; - trie->dataCapacity=capacity; - } - trie->dataLength=newTop; - } - uprv_memcpy(trie->data+newBlock, trie->data+copyBlock, UTRIE2_DATA_BLOCK_LENGTH*4); - trie->map[newBlock>>UTRIE2_SHIFT_2]=0; - return newBlock; -} - -/* call when the block's reference counter reaches 0 */ -static void -releaseDataBlock(UNewTrie2 *trie, int32_t block) { - /* put this block at the front of the free-block chain */ - trie->map[block>>UTRIE2_SHIFT_2]=-trie->firstFreeBlock; - trie->firstFreeBlock=block; -} - -static inline UBool -isWritableBlock(UNewTrie2 *trie, int32_t block) { - return (UBool)(block!=trie->dataNullOffset && 1==trie->map[block>>UTRIE2_SHIFT_2]); -} - -static inline void -setIndex2Entry(UNewTrie2 *trie, int32_t i2, int32_t block) { - int32_t oldBlock; - ++trie->map[block>>UTRIE2_SHIFT_2]; /* increment first, in case block==oldBlock! */ - oldBlock=trie->index2[i2]; - if(0 == --trie->map[oldBlock>>UTRIE2_SHIFT_2]) { - releaseDataBlock(trie, oldBlock); - } - trie->index2[i2]=block; -} - -/** - * No error checking for illegal arguments. - * - * @return -1 if no new data block available (out of memory in data array) - * @internal - */ -static int32_t -getDataBlock(UNewTrie2 *trie, UChar32 c, UBool forLSCP) { - int32_t i2, oldBlock, newBlock; - - i2=getIndex2Block(trie, c, forLSCP); - if(i2<0) { - return -1; /* program error */ - } - - i2+=(c>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK; - oldBlock=trie->index2[i2]; - if(isWritableBlock(trie, oldBlock)) { - return oldBlock; - } - - /* allocate a new data block */ - newBlock=allocDataBlock(trie, oldBlock); - if(newBlock<0) { - /* out of memory in the data array */ - return -1; - } - setIndex2Entry(trie, i2, newBlock); - return newBlock; -} - -/** - * @return true if the value was successfully set - */ -static void -set32(UNewTrie2 *trie, - UChar32 c, UBool forLSCP, uint32_t value, - UErrorCode *pErrorCode) { - int32_t block; - - if(trie==NULL || trie->isCompacted) { - *pErrorCode=U_NO_WRITE_PERMISSION; - return; - } -#ifdef UCPTRIE_DEBUG - umutablecptrie_set(trie->t3, c, value, pErrorCode); -#endif - - block=getDataBlock(trie, c, forLSCP); - if(block<0) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - - trie->data[block+(c&UTRIE2_DATA_MASK)]=value; -} - -U_CAPI void U_EXPORT2 -utrie2_set32(UTrie2 *trie, UChar32 c, uint32_t value, UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return; - } - if((uint32_t)c>0x10ffff) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - set32(trie->newTrie, c, true, value, pErrorCode); -} - -U_CAPI void U_EXPORT2 -utrie2_set32ForLeadSurrogateCodeUnit(UTrie2 *trie, - UChar32 c, uint32_t value, - UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return; - } - if(!U_IS_LEAD(c)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - set32(trie->newTrie, c, false, value, pErrorCode); -} - -static void -writeBlock(uint32_t *block, uint32_t value) { - uint32_t *limit=block+UTRIE2_DATA_BLOCK_LENGTH; - while(block0x10ffff || (uint32_t)end>0x10ffff || start>end) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - newTrie=trie->newTrie; - if(newTrie==NULL || newTrie->isCompacted) { - *pErrorCode=U_NO_WRITE_PERMISSION; - return; - } -#ifdef UCPTRIE_DEBUG - umutablecptrie_setRange(newTrie->t3, start, end, value, pErrorCode); -#endif - if(!overwrite && value==newTrie->initialValue) { - return; /* nothing to do */ - } - - limit=end+1; - if(start&UTRIE2_DATA_MASK) { - UChar32 nextStart; - - /* set partial block at [start..following block boundary[ */ - block=getDataBlock(newTrie, start, true); - if(block<0) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - - nextStart=(start+UTRIE2_DATA_MASK)&~UTRIE2_DATA_MASK; - if(nextStart<=limit) { - fillBlock(newTrie->data+block, start&UTRIE2_DATA_MASK, UTRIE2_DATA_BLOCK_LENGTH, - value, newTrie->initialValue, overwrite); - start=nextStart; - } else { - fillBlock(newTrie->data+block, start&UTRIE2_DATA_MASK, limit&UTRIE2_DATA_MASK, - value, newTrie->initialValue, overwrite); - return; - } - } - - /* number of positions in the last, partial block */ - rest=limit&UTRIE2_DATA_MASK; - - /* round down limit to a block boundary */ - limit&=~UTRIE2_DATA_MASK; - - /* iterate over all-value blocks */ - if(value==newTrie->initialValue) { - repeatBlock=newTrie->dataNullOffset; - } else { - repeatBlock=-1; - } - - while(startinitialValue && isInNullBlock(newTrie, start, true)) { - start+=UTRIE2_DATA_BLOCK_LENGTH; /* nothing to do */ - continue; - } - - /* get index value */ - i2=getIndex2Block(newTrie, start, true); - if(i2<0) { - *pErrorCode=U_INTERNAL_PROGRAM_ERROR; - return; - } - i2+=(start>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK; - block=newTrie->index2[i2]; - if(isWritableBlock(newTrie, block)) { - /* already allocated */ - if(overwrite && block>=UNEWTRIE2_DATA_0800_OFFSET) { - /* - * We overwrite all values, and it's not a - * protected (ASCII-linear or 2-byte UTF-8) block: - * replace with the repeatBlock. - */ - setRepeatBlock=true; - } else { - /* !overwrite, or protected block: just write the values into this block */ - fillBlock(newTrie->data+block, - 0, UTRIE2_DATA_BLOCK_LENGTH, - value, newTrie->initialValue, overwrite); - } - } else if(newTrie->data[block]!=value && (overwrite || block==newTrie->dataNullOffset)) { - /* - * Set the repeatBlock instead of the null block or previous repeat block: - * - * If !isWritableBlock() then all entries in the block have the same value - * because it's the null block or a range block (the repeatBlock from a previous - * call to utrie2_setRange32()). - * No other blocks are used multiple times before compacting. - * - * The null block is the only non-writable block with the initialValue because - * of the repeatBlock initialization above. (If value==initialValue, then - * the repeatBlock will be the null data block.) - * - * We set our repeatBlock if the desired value differs from the block's value, - * and if we overwrite any data or if the data is all initial values - * (which is the same as the block being the null block, see above). - */ - setRepeatBlock=true; - } - if(setRepeatBlock) { - if(repeatBlock>=0) { - setIndex2Entry(newTrie, i2, repeatBlock); - } else { - /* create and set and fill the repeatBlock */ - repeatBlock=getDataBlock(newTrie, start, true); - if(repeatBlock<0) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - writeBlock(newTrie->data+repeatBlock, value); - } - } - - start+=UTRIE2_DATA_BLOCK_LENGTH; - } - - if(rest>0) { - /* set partial block at [last block boundary..limit[ */ - block=getDataBlock(newTrie, start, true); - if(block<0) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - - fillBlock(newTrie->data+block, 0, rest, value, newTrie->initialValue, overwrite); - } - - return; -} - -/* compaction --------------------------------------------------------------- */ - -static inline UBool -equal_int32(const int32_t *s, const int32_t *t, int32_t length) { - while(length>0 && *s==*t) { - ++s; - ++t; - --length; - } - return (UBool)(length==0); -} - -static inline UBool -equal_uint32(const uint32_t *s, const uint32_t *t, int32_t length) { - while(length>0 && *s==*t) { - ++s; - ++t; - --length; - } - return (UBool)(length==0); -} - -static int32_t -findSameIndex2Block(const int32_t *idx, int32_t index2Length, int32_t otherBlock) { - int32_t block; - - /* ensure that we do not even partially get past index2Length */ - index2Length-=UTRIE2_INDEX_2_BLOCK_LENGTH; - - for(block=0; block<=index2Length; ++block) { - if(equal_int32(idx+block, idx+otherBlock, UTRIE2_INDEX_2_BLOCK_LENGTH)) { - return block; - } - } - return -1; -} - -static int32_t -findSameDataBlock(const uint32_t *data, int32_t dataLength, int32_t otherBlock, int32_t blockLength) { - int32_t block; - - /* ensure that we do not even partially get past dataLength */ - dataLength-=blockLength; - - for(block=0; block<=dataLength; block+=UTRIE2_DATA_GRANULARITY) { - if(equal_uint32(data+block, data+otherBlock, blockLength)) { - return block; - } - } - return -1; -} - -/* - * Find the start of the last range in the trie by enumerating backward. - * Indexes for supplementary code points higher than this will be omitted. - */ -static UChar32 -findHighStart(UNewTrie2 *trie, uint32_t highValue) { - const uint32_t *data32; - - uint32_t value, initialValue; - UChar32 c, prev; - int32_t i1, i2, j, i2Block, prevI2Block, index2NullOffset, block, prevBlock, nullBlock; - - data32=trie->data; - initialValue=trie->initialValue; - - index2NullOffset=trie->index2NullOffset; - nullBlock=trie->dataNullOffset; - - /* set variables for previous range */ - if(highValue==initialValue) { - prevI2Block=index2NullOffset; - prevBlock=nullBlock; - } else { - prevI2Block=-1; - prevBlock=-1; - } - prev=0x110000; - - /* enumerate index-2 blocks */ - i1=UNEWTRIE2_INDEX_1_LENGTH; - c=prev; - while(c>0) { - i2Block=trie->index1[--i1]; - if(i2Block==prevI2Block) { - /* the index-2 block is the same as the previous one, and filled with highValue */ - c-=UTRIE2_CP_PER_INDEX_1_ENTRY; - continue; - } - prevI2Block=i2Block; - if(i2Block==index2NullOffset) { - /* this is the null index-2 block */ - if(highValue!=initialValue) { - return c; - } - c-=UTRIE2_CP_PER_INDEX_1_ENTRY; - } else { - /* enumerate data blocks for one index-2 block */ - for(i2=UTRIE2_INDEX_2_BLOCK_LENGTH; i2>0;) { - block=trie->index2[i2Block+ --i2]; - if(block==prevBlock) { - /* the block is the same as the previous one, and filled with highValue */ - c-=UTRIE2_DATA_BLOCK_LENGTH; - continue; - } - prevBlock=block; - if(block==nullBlock) { - /* this is the null data block */ - if(highValue!=initialValue) { - return c; - } - c-=UTRIE2_DATA_BLOCK_LENGTH; - } else { - for(j=UTRIE2_DATA_BLOCK_LENGTH; j>0;) { - value=data32[block+ --j]; - if(value!=highValue) { - return c; - } - --c; - } - } - } - } - } - - /* deliver last range */ - return 0; -} - -/* - * Compact a build-time trie. - * - * The compaction - * - removes blocks that are identical with earlier ones - * - overlaps adjacent blocks as much as possible (if overlap==true) - * - moves blocks in steps of the data granularity - * - moves and overlaps blocks that overlap with multiple values in the overlap region - * - * It does not - * - try to move and overlap blocks that are not already adjacent - */ -static void -compactData(UNewTrie2 *trie) { -#ifdef UTRIE2_DEBUG - int32_t countSame=0, sumOverlaps=0; -#endif - - int32_t start, newStart, movedStart; - int32_t blockLength, overlap; - int32_t i, mapIndex, blockCount; - - /* do not compact linear-ASCII data */ - newStart=UTRIE2_DATA_START_OFFSET; - for(start=0, i=0; startmap[i]=start; - } - - /* - * Start with a block length of 64 for 2-byte UTF-8, - * then switch to UTRIE2_DATA_BLOCK_LENGTH. - */ - blockLength=64; - blockCount=blockLength>>UTRIE2_SHIFT_2; - for(start=newStart; startdataLength;) { - /* - * start: index of first entry of current block - * newStart: index where the current block is to be moved - * (right after current end of already-compacted data) - */ - if(start==UNEWTRIE2_DATA_0800_OFFSET) { - blockLength=UTRIE2_DATA_BLOCK_LENGTH; - blockCount=1; - } - - /* skip blocks that are not used */ - if(trie->map[start>>UTRIE2_SHIFT_2]<=0) { - /* advance start to the next block */ - start+=blockLength; - - /* leave newStart with the previous block! */ - continue; - } - - /* search for an identical block */ - if( (movedStart=findSameDataBlock(trie->data, newStart, start, blockLength)) - >=0 - ) { -#ifdef UTRIE2_DEBUG - ++countSame; -#endif - /* found an identical block, set the other block's index value for the current block */ - for(i=blockCount, mapIndex=start>>UTRIE2_SHIFT_2; i>0; --i) { - trie->map[mapIndex++]=movedStart; - movedStart+=UTRIE2_DATA_BLOCK_LENGTH; - } - - /* advance start to the next block */ - start+=blockLength; - - /* leave newStart with the previous block! */ - continue; - } - - /* see if the beginning of this block can be overlapped with the end of the previous block */ - /* look for maximum overlap (modulo granularity) with the previous, adjacent block */ - for(overlap=blockLength-UTRIE2_DATA_GRANULARITY; - overlap>0 && !equal_uint32(trie->data+(newStart-overlap), trie->data+start, overlap); - overlap-=UTRIE2_DATA_GRANULARITY) {} - -#ifdef UTRIE2_DEBUG - sumOverlaps+=overlap; -#endif - if(overlap>0 || newStart>UTRIE2_SHIFT_2; i>0; --i) { - trie->map[mapIndex++]=movedStart; - movedStart+=UTRIE2_DATA_BLOCK_LENGTH; - } - - /* move the non-overlapping indexes to their new positions */ - start+=overlap; - for(i=blockLength-overlap; i>0; --i) { - trie->data[newStart++]=trie->data[start++]; - } - } else /* no overlap && newStart==start */ { - for(i=blockCount, mapIndex=start>>UTRIE2_SHIFT_2; i>0; --i) { - trie->map[mapIndex++]=start; - start+=UTRIE2_DATA_BLOCK_LENGTH; - } - newStart=start; - } - } - - /* now adjust the index-2 table */ - for(i=0; iindex2Length; ++i) { - if(i==UNEWTRIE2_INDEX_GAP_OFFSET) { - /* Gap indexes are invalid (-1). Skip over the gap. */ - i+=UNEWTRIE2_INDEX_GAP_LENGTH; - } - trie->index2[i]=trie->map[trie->index2[i]>>UTRIE2_SHIFT_2]; - } - trie->dataNullOffset=trie->map[trie->dataNullOffset>>UTRIE2_SHIFT_2]; - - /* ensure dataLength alignment */ - while((newStart&(UTRIE2_DATA_GRANULARITY-1))!=0) { - trie->data[newStart++]=trie->initialValue; - } - -#ifdef UTRIE2_DEBUG - /* we saved some space */ - printf("compacting UTrie2: count of 32-bit data words %lu->%lu countSame=%ld sumOverlaps=%ld\n", - (long)trie->dataLength, (long)newStart, (long)countSame, (long)sumOverlaps); -#endif - - trie->dataLength=newStart; -} - -static void -compactIndex2(UNewTrie2 *trie) { - int32_t i, start, newStart, movedStart, overlap; - - /* do not compact linear-BMP index-2 blocks */ - newStart=UTRIE2_INDEX_2_BMP_LENGTH; - for(start=0, i=0; startmap[i]=start; - } - - /* Reduce the index table gap to what will be needed at runtime. */ - newStart+=UTRIE2_UTF8_2B_INDEX_2_LENGTH+((trie->highStart-0x10000)>>UTRIE2_SHIFT_1); - - for(start=UNEWTRIE2_INDEX_2_NULL_OFFSET; startindex2Length;) { - /* - * start: index of first entry of current block - * newStart: index where the current block is to be moved - * (right after current end of already-compacted data) - */ - - /* search for an identical block */ - if( (movedStart=findSameIndex2Block(trie->index2, newStart, start)) - >=0 - ) { - /* found an identical block, set the other block's index value for the current block */ - trie->map[start>>UTRIE2_SHIFT_1_2]=movedStart; - - /* advance start to the next block */ - start+=UTRIE2_INDEX_2_BLOCK_LENGTH; - - /* leave newStart with the previous block! */ - continue; - } - - /* see if the beginning of this block can be overlapped with the end of the previous block */ - /* look for maximum overlap with the previous, adjacent block */ - for(overlap=UTRIE2_INDEX_2_BLOCK_LENGTH-1; - overlap>0 && !equal_int32(trie->index2+(newStart-overlap), trie->index2+start, overlap); - --overlap) {} - - if(overlap>0 || newStartmap[start>>UTRIE2_SHIFT_1_2]=newStart-overlap; - - /* move the non-overlapping indexes to their new positions */ - start+=overlap; - for(i=UTRIE2_INDEX_2_BLOCK_LENGTH-overlap; i>0; --i) { - trie->index2[newStart++]=trie->index2[start++]; - } - } else /* no overlap && newStart==start */ { - trie->map[start>>UTRIE2_SHIFT_1_2]=start; - start+=UTRIE2_INDEX_2_BLOCK_LENGTH; - newStart=start; - } - } - - /* now adjust the index-1 table */ - for(i=0; iindex1[i]=trie->map[trie->index1[i]>>UTRIE2_SHIFT_1_2]; - } - trie->index2NullOffset=trie->map[trie->index2NullOffset>>UTRIE2_SHIFT_1_2]; - - /* - * Ensure data table alignment: - * Needs to be granularity-aligned for 16-bit trie - * (so that dataMove will be down-shiftable), - * and 2-aligned for uint32_t data. - */ - while((newStart&((UTRIE2_DATA_GRANULARITY-1)|1))!=0) { - /* Arbitrary value: 0x3fffc not possible for real data. */ - trie->index2[newStart++]=(int32_t)0xffff<%lu\n", - (long)trie->index2Length, (long)newStart); -#endif - - trie->index2Length=newStart; -} - -static void -compactTrie(UTrie2 *trie, UErrorCode *pErrorCode) { - UNewTrie2 *newTrie; - UChar32 highStart, suppHighStart; - uint32_t highValue; - - newTrie=trie->newTrie; - - /* find highStart and round it up */ - highValue=utrie2_get32(trie, 0x10ffff); - highStart=findHighStart(newTrie, highValue); - highStart=(highStart+(UTRIE2_CP_PER_INDEX_1_ENTRY-1))&~(UTRIE2_CP_PER_INDEX_1_ENTRY-1); - if(highStart==0x110000) { - highValue=trie->errorValue; - } - - /* - * Set trie->highStart only after utrie2_get32(trie, highStart). - * Otherwise utrie2_get32(trie, highStart) would try to read the highValue. - */ - trie->highStart=newTrie->highStart=highStart; - -#ifdef UTRIE2_DEBUG - printf("UTrie2: highStart U+%06lx highValue 0x%lx initialValue 0x%lx\n", - (long)highStart, (long)highValue, (long)trie->initialValue); -#endif - - if(highStart<0x110000) { - /* Blank out [highStart..10ffff] to release associated data blocks. */ - suppHighStart= highStart<=0x10000 ? 0x10000 : highStart; - utrie2_setRange32(trie, suppHighStart, 0x10ffff, trie->initialValue, true, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return; - } - } - - compactData(newTrie); - if(highStart>0x10000) { - compactIndex2(newTrie); -#ifdef UTRIE2_DEBUG - } else { - printf("UTrie2: highStart U+%04lx count of 16-bit index words %lu->%lu\n", - (long)highStart, (long)trie->newTrie->index2Length, (long)UTRIE2_INDEX_1_OFFSET); -#endif - } - - /* - * Store the highValue in the data array and round up the dataLength. - * Must be done after compactData() because that assumes that dataLength - * is a multiple of UTRIE2_DATA_BLOCK_LENGTH. - */ - newTrie->data[newTrie->dataLength++]=highValue; - while((newTrie->dataLength&(UTRIE2_DATA_GRANULARITY-1))!=0) { - newTrie->data[newTrie->dataLength++]=trie->initialValue; - } - - newTrie->isCompacted=true; -} - -/* serialization ------------------------------------------------------------ */ - -/** - * Maximum length of the runtime index array. - * Limited by its own 16-bit index values, and by uint16_t UTrie2Header.indexLength. - * (The actual maximum length is lower, - * (0x110000>>UTRIE2_SHIFT_2)+UTRIE2_UTF8_2B_INDEX_2_LENGTH+UTRIE2_MAX_INDEX_1_LENGTH.) - */ -#define UTRIE2_MAX_INDEX_LENGTH 0xffff - -/** - * Maximum length of the runtime data array. - * Limited by 16-bit index values that are left-shifted by UTRIE2_INDEX_SHIFT, - * and by uint16_t UTrie2Header.shiftedDataLength. - */ -#define UTRIE2_MAX_DATA_LENGTH (0xffff<0 if the data is moved to the end of the index array */ - UChar32 highStart; - - /* argument check */ - if(U_FAILURE(*pErrorCode)) { - return; - } - if( trie==NULL || - valueBits<0 || UTRIE2_COUNT_VALUE_BITS<=valueBits - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - newTrie=trie->newTrie; - if(newTrie==NULL) { - /* already frozen */ - UTrie2ValueBits frozenValueBits= - trie->data16!=NULL ? UTRIE2_16_VALUE_BITS : UTRIE2_32_VALUE_BITS; - if(valueBits!=frozenValueBits) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - } - return; - } - - /* compact if necessary */ - if(!newTrie->isCompacted) { - compactTrie(trie, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return; - } - } - highStart=trie->highStart; - - if(highStart<=0x10000) { - allIndexesLength=UTRIE2_INDEX_1_OFFSET; - } else { - allIndexesLength=newTrie->index2Length; - } - if(valueBits==UTRIE2_16_VALUE_BITS) { - dataMove=allIndexesLength; - } else { - dataMove=0; - } - - /* are indexLength and dataLength within limits? */ - if( /* for unshifted indexLength */ - allIndexesLength>UTRIE2_MAX_INDEX_LENGTH || - /* for unshifted dataNullOffset */ - (dataMove+newTrie->dataNullOffset)>0xffff || - /* for unshifted 2-byte UTF-8 index-2 values */ - (dataMove+UNEWTRIE2_DATA_0800_OFFSET)>0xffff || - /* for shiftedDataLength */ - (dataMove+newTrie->dataLength)>UTRIE2_MAX_DATA_LENGTH - ) { - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return; - } - - /* calculate the total serialized length */ - length=sizeof(UTrie2Header)+allIndexesLength*2; - if(valueBits==UTRIE2_16_VALUE_BITS) { - length+=newTrie->dataLength*2; - } else { - length+=newTrie->dataLength*4; - } - - trie->memory=uprv_malloc(length); - if(trie->memory==NULL) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; - } - trie->length=length; - trie->isMemoryOwned=true; - - trie->indexLength=allIndexesLength; - trie->dataLength=newTrie->dataLength; - if(highStart<=0x10000) { - trie->index2NullOffset=0xffff; - } else { - trie->index2NullOffset=static_cast(UTRIE2_INDEX_2_OFFSET+newTrie->index2NullOffset); - } - trie->dataNullOffset=(uint16_t)(dataMove+newTrie->dataNullOffset); - trie->highValueIndex=dataMove+trie->dataLength-UTRIE2_DATA_GRANULARITY; - - /* set the header fields */ - header=(UTrie2Header *)trie->memory; - - header->signature=UTRIE2_SIG; /* "Tri2" */ - header->options=(uint16_t)valueBits; - - header->indexLength=(uint16_t)trie->indexLength; - header->shiftedDataLength=(uint16_t)(trie->dataLength>>UTRIE2_INDEX_SHIFT); - header->index2NullOffset=trie->index2NullOffset; - header->dataNullOffset=trie->dataNullOffset; - header->shiftedHighStart=(uint16_t)(highStart>>UTRIE2_SHIFT_1); - - /* fill the index and data arrays */ - dest16=(uint16_t *)(header+1); - trie->index=dest16; - - /* write the index-2 array values shifted right by UTRIE2_INDEX_SHIFT, after adding dataMove */ - p=(uint32_t *)newTrie->index2; - for(i=UTRIE2_INDEX_2_BMP_LENGTH; i>0; --i) { - *dest16++=(uint16_t)((dataMove + *p++)>>UTRIE2_INDEX_SHIFT); - } - - /* write UTF-8 2-byte index-2 values, not right-shifted */ - for(i=0; i<(0xc2-0xc0); ++i) { /* C0..C1 */ - *dest16++=(uint16_t)(dataMove+UTRIE2_BAD_UTF8_DATA_OFFSET); - } - for(; i<(0xe0-0xc0); ++i) { /* C2..DF */ - *dest16++=(uint16_t)(dataMove+newTrie->index2[i<<(6-UTRIE2_SHIFT_2)]); - } - - if(highStart>0x10000) { - int32_t index1Length=(highStart-0x10000)>>UTRIE2_SHIFT_1; - int32_t index2Offset=UTRIE2_INDEX_2_BMP_LENGTH+UTRIE2_UTF8_2B_INDEX_2_LENGTH+index1Length; - - /* write 16-bit index-1 values for supplementary code points */ - p=(uint32_t *)newTrie->index1+UTRIE2_OMITTED_BMP_INDEX_1_LENGTH; - for(i=index1Length; i>0; --i) { - *dest16++=(uint16_t)(UTRIE2_INDEX_2_OFFSET + *p++); - } - - /* - * write the index-2 array values for supplementary code points, - * shifted right by UTRIE2_INDEX_SHIFT, after adding dataMove - */ - p=(uint32_t *)newTrie->index2+index2Offset; - for(i=newTrie->index2Length-index2Offset; i>0; --i) { - *dest16++=(uint16_t)((dataMove + *p++)>>UTRIE2_INDEX_SHIFT); - } - } - - /* write the 16/32-bit data array */ - switch(valueBits) { - case UTRIE2_16_VALUE_BITS: - /* write 16-bit data values */ - trie->data16=dest16; - trie->data32=NULL; - p=newTrie->data; - for(i=newTrie->dataLength; i>0; --i) { - *dest16++=(uint16_t)*p++; - } - break; - case UTRIE2_32_VALUE_BITS: - /* write 32-bit data values */ - trie->data16=NULL; - trie->data32=(uint32_t *)dest16; - uprv_memcpy(dest16, newTrie->data, (size_t)newTrie->dataLength*4); - break; - default: - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - -#ifdef UTRIE2_DEBUG - utrie2_printLengths(trie, ""); -#endif - -#ifdef UCPTRIE_DEBUG - umutablecptrie_setName(newTrie->t3, trie->name); - ucptrie_close( - umutablecptrie_buildImmutable( - newTrie->t3, UCPTRIE_TYPE_FAST, (UCPTrieValueWidth)valueBits, pErrorCode)); -#endif - /* Delete the UNewTrie2. */ - uprv_free(newTrie->data); - uprv_free(newTrie); - trie->newTrie=NULL; -} +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2001-2014, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: utrie2_builder.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2008sep26 (split off from utrie2.c) +* created by: Markus W. Scherer +* +* This is a common implementation of a Unicode trie. +* It is a kind of compressed, serializable table of 16- or 32-bit values associated with +* Unicode code points (0..0x10ffff). +* This is the second common version of a Unicode trie (hence the name UTrie2). +* See utrie2.h for a comparison. +* +* This file contains only the builder code. +* See utrie2.c for the runtime and enumeration code. +*/ +// #define UTRIE2_DEBUG +#ifdef UTRIE2_DEBUG +# include +#endif +// #define UCPTRIE_DEBUG + +#include "unicode/utypes.h" +#ifdef UCPTRIE_DEBUG +#include "unicode/ucptrie.h" +#include "unicode/umutablecptrie.h" +#include "ucptrie_impl.h" +#endif +#include "cmemory.h" +#include "utrie2.h" +#include "utrie2_impl.h" + +#include "utrie.h" // for utrie2_fromUTrie() + +/* Implementation notes ----------------------------------------------------- */ + +/* + * The UTRIE2_SHIFT_1, UTRIE2_SHIFT_2, UTRIE2_INDEX_SHIFT and other values + * have been chosen to minimize trie sizes overall. + * Most of the code is flexible enough to work with a range of values, + * within certain limits. + * + * Exception: Support for separate values for lead surrogate code _units_ + * vs. code _points_ was added after the constants were fixed, + * and has not been tested nor particularly designed for different constant values. + * (Especially the utrie2_enum() code that jumps to the special LSCP index-2 + * part and back.) + * + * Requires UTRIE2_SHIFT_2<=6. Otherwise 0xc0 which is the top of the ASCII-linear data + * including the bad-UTF-8-data block is not a multiple of UTRIE2_DATA_BLOCK_LENGTH + * and map[block>>UTRIE2_SHIFT_2] (used in reference counting and compaction + * remapping) stops working. + * + * Requires UTRIE2_SHIFT_1>=10 because utrie2_enumForLeadSurrogate() + * assumes that a single index-2 block is used for 0x400 code points + * corresponding to one lead surrogate. + * + * Requires UTRIE2_SHIFT_1<=16. Otherwise one single index-2 block contains + * more than one Unicode plane, and the split of the index-2 table into a BMP + * part and a supplementary part, with a gap in between, would not work. + * + * Requires UTRIE2_INDEX_SHIFT>=1 not because of the code but because + * there is data with more than 64k distinct values, + * for example for Unihan collation with a separate collation weight per + * Han character. + */ + +/* Building a trie ----------------------------------------------------------*/ + +enum { + /** The null index-2 block, following the gap in the index-2 table. */ + UNEWTRIE2_INDEX_2_NULL_OFFSET=UNEWTRIE2_INDEX_GAP_OFFSET+UNEWTRIE2_INDEX_GAP_LENGTH, + + /** The start of allocated index-2 blocks. */ + UNEWTRIE2_INDEX_2_START_OFFSET=UNEWTRIE2_INDEX_2_NULL_OFFSET+UTRIE2_INDEX_2_BLOCK_LENGTH, + + /** + * The null data block. + * Length 64=0x40 even if UTRIE2_DATA_BLOCK_LENGTH is smaller, + * to work with 6-bit trail bytes from 2-byte UTF-8. + */ + UNEWTRIE2_DATA_NULL_OFFSET=UTRIE2_DATA_START_OFFSET, + + /** The start of allocated data blocks. */ + UNEWTRIE2_DATA_START_OFFSET=UNEWTRIE2_DATA_NULL_OFFSET+0x40, + + /** + * The start of data blocks for U+0800 and above. + * Below, compaction uses a block length of 64 for 2-byte UTF-8. + * From here on, compaction uses UTRIE2_DATA_BLOCK_LENGTH. + * Data values for 0x780 code points beyond ASCII. + */ + UNEWTRIE2_DATA_0800_OFFSET=UNEWTRIE2_DATA_START_OFFSET+0x780 +}; + +/* Start with allocation of 16k data entries. */ +#define UNEWTRIE2_INITIAL_DATA_LENGTH ((int32_t)1<<14) + +/* Grow about 8x each time. */ +#define UNEWTRIE2_MEDIUM_DATA_LENGTH ((int32_t)1<<17) + +static int32_t +allocIndex2Block(UNewTrie2 *trie); + +U_CAPI UTrie2 * U_EXPORT2 +utrie2_open(uint32_t initialValue, uint32_t errorValue, UErrorCode *pErrorCode) { + UTrie2 *trie; + UNewTrie2 *newTrie; + uint32_t *data; + int32_t i, j; + + if(U_FAILURE(*pErrorCode)) { + return nullptr; + } + + trie=(UTrie2 *)uprv_malloc(sizeof(UTrie2)); + newTrie=(UNewTrie2 *)uprv_malloc(sizeof(UNewTrie2)); + data=(uint32_t *)uprv_malloc(UNEWTRIE2_INITIAL_DATA_LENGTH*4); + if(trie==nullptr || newTrie==nullptr || data==nullptr) { + uprv_free(trie); + uprv_free(newTrie); + uprv_free(data); + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return 0; + } + + uprv_memset(trie, 0, sizeof(UTrie2)); + trie->initialValue=initialValue; + trie->errorValue=errorValue; + trie->highStart=0x110000; + trie->newTrie=newTrie; +#ifdef UTRIE2_DEBUG + trie->name="open"; +#endif + + newTrie->data=data; +#ifdef UCPTRIE_DEBUG + newTrie->t3=umutablecptrie_open(initialValue, errorValue, pErrorCode); +#endif + newTrie->dataCapacity=UNEWTRIE2_INITIAL_DATA_LENGTH; + newTrie->initialValue=initialValue; + newTrie->errorValue=errorValue; + newTrie->highStart=0x110000; + newTrie->firstFreeBlock=0; /* no free block in the list */ + newTrie->isCompacted=false; + + /* + * preallocate and reset + * - ASCII + * - the bad-UTF-8-data block + * - the null data block + */ + for(i=0; i<0x80; ++i) { + newTrie->data[i]=initialValue; + } + for(; i<0xc0; ++i) { + newTrie->data[i]=errorValue; + } + for(i=UNEWTRIE2_DATA_NULL_OFFSET; idata[i]=initialValue; + } + newTrie->dataNullOffset=UNEWTRIE2_DATA_NULL_OFFSET; + newTrie->dataLength=UNEWTRIE2_DATA_START_OFFSET; + + /* set the index-2 indexes for the 2=0x80>>UTRIE2_SHIFT_2 ASCII data blocks */ + for(i=0, j=0; j<0x80; ++i, j+=UTRIE2_DATA_BLOCK_LENGTH) { + newTrie->index2[i]=j; + newTrie->map[i]=1; + } + /* reference counts for the bad-UTF-8-data block */ + for(; j<0xc0; ++i, j+=UTRIE2_DATA_BLOCK_LENGTH) { + newTrie->map[i]=0; + } + /* + * Reference counts for the null data block: all blocks except for the ASCII blocks. + * Plus 1 so that we don't drop this block during compaction. + * Plus as many as needed for lead surrogate code points. + */ + /* i==newTrie->dataNullOffset */ + newTrie->map[i++]= + (0x110000>>UTRIE2_SHIFT_2)- + (0x80>>UTRIE2_SHIFT_2)+ + 1+ + UTRIE2_LSCP_INDEX_2_LENGTH; + j+=UTRIE2_DATA_BLOCK_LENGTH; + for(; jmap[i]=0; + } + + /* + * set the remaining indexes in the BMP index-2 block + * to the null data block + */ + for(i=0x80>>UTRIE2_SHIFT_2; iindex2[i]=UNEWTRIE2_DATA_NULL_OFFSET; + } + + /* + * Fill the index gap with impossible values so that compaction + * does not overlap other index-2 blocks with the gap. + */ + for(i=0; iindex2[UNEWTRIE2_INDEX_GAP_OFFSET+i]=-1; + } + + /* set the indexes in the null index-2 block */ + for(i=0; iindex2[UNEWTRIE2_INDEX_2_NULL_OFFSET+i]=UNEWTRIE2_DATA_NULL_OFFSET; + } + newTrie->index2NullOffset=UNEWTRIE2_INDEX_2_NULL_OFFSET; + newTrie->index2Length=UNEWTRIE2_INDEX_2_START_OFFSET; + + /* set the index-1 indexes for the linear index-2 block */ + for(i=0, j=0; + iindex1[i]=j; + } + + /* set the remaining index-1 indexes to the null index-2 block */ + for(; iindex1[i]=UNEWTRIE2_INDEX_2_NULL_OFFSET; + } + + /* + * Preallocate and reset data for U+0080..U+07ff, + * for 2-byte UTF-8 which will be compacted in 64-blocks + * even if UTRIE2_DATA_BLOCK_LENGTH is smaller. + */ + for(i=0x80; i<0x800; i+=UTRIE2_DATA_BLOCK_LENGTH) { + utrie2_set32(trie, i, initialValue, pErrorCode); + } + + return trie; +} + +static UNewTrie2 * +cloneBuilder(const UNewTrie2 *other) { + UNewTrie2 *trie; + + trie=(UNewTrie2 *)uprv_malloc(sizeof(UNewTrie2)); + if(trie==nullptr) { + return nullptr; + } + + trie->data=(uint32_t *)uprv_malloc(other->dataCapacity*4); + if(trie->data==nullptr) { + uprv_free(trie); + return nullptr; + } +#ifdef UCPTRIE_DEBUG + if(other->t3==nullptr) { + trie->t3=nullptr; + } else { + UErrorCode errorCode=U_ZERO_ERROR; + trie->t3=umutablecptrie_clone(other->t3, &errorCode); + } +#endif + trie->dataCapacity=other->dataCapacity; + + /* clone data */ + uprv_memcpy(trie->index1, other->index1, sizeof(trie->index1)); + uprv_memcpy(trie->index2, other->index2, (size_t)other->index2Length*4); + trie->index2NullOffset=other->index2NullOffset; + trie->index2Length=other->index2Length; + + uprv_memcpy(trie->data, other->data, (size_t)other->dataLength*4); + trie->dataNullOffset=other->dataNullOffset; + trie->dataLength=other->dataLength; + + /* reference counters */ + if(other->isCompacted) { + trie->firstFreeBlock=0; + } else { + uprv_memcpy(trie->map, other->map, ((size_t)other->dataLength>>UTRIE2_SHIFT_2)*4); + trie->firstFreeBlock=other->firstFreeBlock; + } + + trie->initialValue=other->initialValue; + trie->errorValue=other->errorValue; + trie->highStart=other->highStart; + trie->isCompacted=other->isCompacted; + + return trie; +} + +U_CAPI UTrie2 * U_EXPORT2 +utrie2_clone(const UTrie2 *other, UErrorCode *pErrorCode) { + UTrie2 *trie; + + if(U_FAILURE(*pErrorCode)) { + return nullptr; + } + if(other==nullptr || (other->memory==nullptr && other->newTrie==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + trie=(UTrie2 *)uprv_malloc(sizeof(UTrie2)); + if(trie==nullptr) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memcpy(trie, other, sizeof(UTrie2)); + + if(other->memory!=nullptr) { + trie->memory=uprv_malloc(other->length); + if(trie->memory!=nullptr) { + trie->isMemoryOwned=true; + uprv_memcpy(trie->memory, other->memory, other->length); + + /* make the clone's pointers point to its own memory */ + trie->index=(uint16_t *)trie->memory+(other->index-(uint16_t *)other->memory); + if(other->data16!=nullptr) { + trie->data16=(uint16_t *)trie->memory+(other->data16-(uint16_t *)other->memory); + } + if(other->data32!=nullptr) { + trie->data32=(uint32_t *)trie->memory+(other->data32-(uint32_t *)other->memory); + } + } + } else /* other->newTrie!=nullptr */ { + trie->newTrie=cloneBuilder(other->newTrie); + } + + if(trie->memory==nullptr && trie->newTrie==nullptr) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + uprv_free(trie); + trie=nullptr; + } + return trie; +} + +typedef struct NewTrieAndStatus { + UTrie2 *trie; + UErrorCode errorCode; + UBool exclusiveLimit; /* rather than inclusive range end */ +} NewTrieAndStatus; + +static UBool U_CALLCONV +copyEnumRange(const void *context, UChar32 start, UChar32 end, uint32_t value) { + NewTrieAndStatus *nt=(NewTrieAndStatus *)context; + if(value!=nt->trie->initialValue) { + if(nt->exclusiveLimit) { + --end; + } + if(start==end) { + utrie2_set32(nt->trie, start, value, &nt->errorCode); + } else { + utrie2_setRange32(nt->trie, start, end, value, true, &nt->errorCode); + } + return U_SUCCESS(nt->errorCode); + } else { + return true; + } +} + +#ifdef UTRIE2_DEBUG +static long countInitial(const UTrie2 *trie) { + uint32_t initialValue=trie->initialValue; + int32_t length=trie->dataLength; + long count=0; + if(trie->data16!=nullptr) { + for(int32_t i=0; idata16[i]==initialValue) { ++count; } + } + } else { + for(int32_t i=0; idata32[i]==initialValue) { ++count; } + } + } + return count; +} + +static void +utrie_printLengths(const UTrie *trie) { + long indexLength=trie->indexLength; + long dataLength=(long)trie->dataLength; + long totalLength=(long)sizeof(UTrieHeader)+indexLength*2+dataLength*(trie->data32!=nullptr ? 4 : 2); + printf("**UTrieLengths** index:%6ld data:%6ld serialized:%6ld\n", + indexLength, dataLength, totalLength); +} + +static void +utrie2_printLengths(const UTrie2 *trie, const char *which) { + long indexLength=trie->indexLength; + long dataLength=(long)trie->dataLength; + long totalLength=(long)sizeof(UTrie2Header)+indexLength*2+dataLength*(trie->data32!=nullptr ? 4 : 2); + printf("**UTrie2Lengths(%s %s)** index:%6ld data:%6ld countInitial:%6ld serialized:%6ld\n", + which, trie->name, indexLength, dataLength, countInitial(trie), totalLength); +} +#endif + +U_CAPI UTrie2 * U_EXPORT2 +utrie2_cloneAsThawed(const UTrie2 *other, UErrorCode *pErrorCode) { + NewTrieAndStatus context; + char16_t lead; + + if(U_FAILURE(*pErrorCode)) { + return nullptr; + } + if(other==nullptr || (other->memory==nullptr && other->newTrie==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + if(other->newTrie!=nullptr && !other->newTrie->isCompacted) { + return utrie2_clone(other, pErrorCode); /* clone an unfrozen trie */ + } + + /* Clone the frozen trie by enumerating it and building a new one. */ + context.trie=utrie2_open(other->initialValue, other->errorValue, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return nullptr; + } + context.exclusiveLimit=false; + context.errorCode=*pErrorCode; + utrie2_enum(other, nullptr, copyEnumRange, &context); + *pErrorCode=context.errorCode; + for(lead=0xd800; lead<0xdc00; ++lead) { + uint32_t value; + if(other->data32==nullptr) { + value=UTRIE2_GET16_FROM_U16_SINGLE_LEAD(other, lead); + } else { + value=UTRIE2_GET32_FROM_U16_SINGLE_LEAD(other, lead); + } + if(value!=other->initialValue) { + utrie2_set32ForLeadSurrogateCodeUnit(context.trie, lead, value, pErrorCode); + } + } + if(U_FAILURE(*pErrorCode)) { + utrie2_close(context.trie); + context.trie=nullptr; + } + return context.trie; +} + +/* Almost the same as utrie2_cloneAsThawed() but copies a UTrie and freezes the clone. */ +U_CAPI UTrie2 * U_EXPORT2 +utrie2_fromUTrie(const UTrie *trie1, uint32_t errorValue, UErrorCode *pErrorCode) { + NewTrieAndStatus context; + char16_t lead; + + if(U_FAILURE(*pErrorCode)) { + return nullptr; + } + if(trie1==nullptr) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + context.trie=utrie2_open(trie1->initialValue, errorValue, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return nullptr; + } + context.exclusiveLimit=true; + context.errorCode=*pErrorCode; + utrie_enum(trie1, nullptr, copyEnumRange, &context); + *pErrorCode=context.errorCode; + for(lead=0xd800; lead<0xdc00; ++lead) { + uint32_t value; + if(trie1->data32==nullptr) { + value=UTRIE_GET16_FROM_LEAD(trie1, lead); + } else { + value=UTRIE_GET32_FROM_LEAD(trie1, lead); + } + if(value!=trie1->initialValue) { + utrie2_set32ForLeadSurrogateCodeUnit(context.trie, lead, value, pErrorCode); + } + } + if(U_SUCCESS(*pErrorCode)) { + utrie2_freeze(context.trie, + trie1->data32!=nullptr ? UTRIE2_32_VALUE_BITS : UTRIE2_16_VALUE_BITS, + pErrorCode); + } +#ifdef UTRIE2_DEBUG + if(U_SUCCESS(*pErrorCode)) { + utrie_printLengths(trie1); + utrie2_printLengths(context.trie, "fromUTrie"); + } +#endif + if(U_FAILURE(*pErrorCode)) { + utrie2_close(context.trie); + context.trie=nullptr; + } + return context.trie; +} + +static inline UBool +isInNullBlock(UNewTrie2 *trie, UChar32 c, UBool forLSCP) { + int32_t i2, block; + + if(U_IS_LEAD(c) && forLSCP) { + i2=(UTRIE2_LSCP_INDEX_2_OFFSET-(0xd800>>UTRIE2_SHIFT_2))+ + (c>>UTRIE2_SHIFT_2); + } else { + i2=trie->index1[c>>UTRIE2_SHIFT_1]+ + ((c>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK); + } + block=trie->index2[i2]; + return (UBool)(block==trie->dataNullOffset); +} + +static int32_t +allocIndex2Block(UNewTrie2 *trie) { + int32_t newBlock, newTop; + + newBlock=trie->index2Length; + newTop=newBlock+UTRIE2_INDEX_2_BLOCK_LENGTH; + if(newTop>UPRV_LENGTHOF(trie->index2)) { + /* + * Should never occur. + * Either UTRIE2_MAX_BUILD_TIME_INDEX_LENGTH is incorrect, + * or the code writes more values than should be possible. + */ + return -1; + } + trie->index2Length=newTop; + uprv_memcpy(trie->index2+newBlock, trie->index2+trie->index2NullOffset, UTRIE2_INDEX_2_BLOCK_LENGTH*4); + return newBlock; +} + +static int32_t +getIndex2Block(UNewTrie2 *trie, UChar32 c, UBool forLSCP) { + int32_t i1, i2; + + if(U_IS_LEAD(c) && forLSCP) { + return UTRIE2_LSCP_INDEX_2_OFFSET; + } + + i1=c>>UTRIE2_SHIFT_1; + i2=trie->index1[i1]; + if(i2==trie->index2NullOffset) { + i2=allocIndex2Block(trie); + if(i2<0) { + return -1; /* program error */ + } + trie->index1[i1]=i2; + } + return i2; +} + +static int32_t +allocDataBlock(UNewTrie2 *trie, int32_t copyBlock) { + int32_t newBlock, newTop; + + if(trie->firstFreeBlock!=0) { + /* get the first free block */ + newBlock=trie->firstFreeBlock; + trie->firstFreeBlock=-trie->map[newBlock>>UTRIE2_SHIFT_2]; + } else { + /* get a new block from the high end */ + newBlock=trie->dataLength; + newTop=newBlock+UTRIE2_DATA_BLOCK_LENGTH; + if(newTop>trie->dataCapacity) { + /* out of memory in the data array */ + int32_t capacity; + uint32_t *data; + + if(trie->dataCapacitydataCapacitydata, (size_t)trie->dataLength*4); + uprv_free(trie->data); + trie->data=data; + trie->dataCapacity=capacity; + } + trie->dataLength=newTop; + } + uprv_memcpy(trie->data+newBlock, trie->data+copyBlock, UTRIE2_DATA_BLOCK_LENGTH*4); + trie->map[newBlock>>UTRIE2_SHIFT_2]=0; + return newBlock; +} + +/* call when the block's reference counter reaches 0 */ +static void +releaseDataBlock(UNewTrie2 *trie, int32_t block) { + /* put this block at the front of the free-block chain */ + trie->map[block>>UTRIE2_SHIFT_2]=-trie->firstFreeBlock; + trie->firstFreeBlock=block; +} + +static inline UBool +isWritableBlock(UNewTrie2 *trie, int32_t block) { + return (UBool)(block!=trie->dataNullOffset && 1==trie->map[block>>UTRIE2_SHIFT_2]); +} + +static inline void +setIndex2Entry(UNewTrie2 *trie, int32_t i2, int32_t block) { + int32_t oldBlock; + ++trie->map[block>>UTRIE2_SHIFT_2]; /* increment first, in case block==oldBlock! */ + oldBlock=trie->index2[i2]; + if(0 == --trie->map[oldBlock>>UTRIE2_SHIFT_2]) { + releaseDataBlock(trie, oldBlock); + } + trie->index2[i2]=block; +} + +/** + * No error checking for illegal arguments. + * + * @return -1 if no new data block available (out of memory in data array) + * @internal + */ +static int32_t +getDataBlock(UNewTrie2 *trie, UChar32 c, UBool forLSCP) { + int32_t i2, oldBlock, newBlock; + + i2=getIndex2Block(trie, c, forLSCP); + if(i2<0) { + return -1; /* program error */ + } + + i2+=(c>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK; + oldBlock=trie->index2[i2]; + if(isWritableBlock(trie, oldBlock)) { + return oldBlock; + } + + /* allocate a new data block */ + newBlock=allocDataBlock(trie, oldBlock); + if(newBlock<0) { + /* out of memory in the data array */ + return -1; + } + setIndex2Entry(trie, i2, newBlock); + return newBlock; +} + +/** + * @return true if the value was successfully set + */ +static void +set32(UNewTrie2 *trie, + UChar32 c, UBool forLSCP, uint32_t value, + UErrorCode *pErrorCode) { + int32_t block; + + if(trie==nullptr || trie->isCompacted) { + *pErrorCode=U_NO_WRITE_PERMISSION; + return; + } +#ifdef UCPTRIE_DEBUG + umutablecptrie_set(trie->t3, c, value, pErrorCode); +#endif + + block=getDataBlock(trie, c, forLSCP); + if(block<0) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + + trie->data[block+(c&UTRIE2_DATA_MASK)]=value; +} + +U_CAPI void U_EXPORT2 +utrie2_set32(UTrie2 *trie, UChar32 c, uint32_t value, UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return; + } + if((uint32_t)c>0x10ffff) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + set32(trie->newTrie, c, true, value, pErrorCode); +} + +U_CAPI void U_EXPORT2 +utrie2_set32ForLeadSurrogateCodeUnit(UTrie2 *trie, + UChar32 c, uint32_t value, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return; + } + if(!U_IS_LEAD(c)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + set32(trie->newTrie, c, false, value, pErrorCode); +} + +static void +writeBlock(uint32_t *block, uint32_t value) { + uint32_t *limit=block+UTRIE2_DATA_BLOCK_LENGTH; + while(block0x10ffff || (uint32_t)end>0x10ffff || start>end) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + newTrie=trie->newTrie; + if(newTrie==nullptr || newTrie->isCompacted) { + *pErrorCode=U_NO_WRITE_PERMISSION; + return; + } +#ifdef UCPTRIE_DEBUG + umutablecptrie_setRange(newTrie->t3, start, end, value, pErrorCode); +#endif + if(!overwrite && value==newTrie->initialValue) { + return; /* nothing to do */ + } + + limit=end+1; + if(start&UTRIE2_DATA_MASK) { + UChar32 nextStart; + + /* set partial block at [start..following block boundary[ */ + block=getDataBlock(newTrie, start, true); + if(block<0) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + + nextStart=(start+UTRIE2_DATA_MASK)&~UTRIE2_DATA_MASK; + if(nextStart<=limit) { + fillBlock(newTrie->data+block, start&UTRIE2_DATA_MASK, UTRIE2_DATA_BLOCK_LENGTH, + value, newTrie->initialValue, overwrite); + start=nextStart; + } else { + fillBlock(newTrie->data+block, start&UTRIE2_DATA_MASK, limit&UTRIE2_DATA_MASK, + value, newTrie->initialValue, overwrite); + return; + } + } + + /* number of positions in the last, partial block */ + rest=limit&UTRIE2_DATA_MASK; + + /* round down limit to a block boundary */ + limit&=~UTRIE2_DATA_MASK; + + /* iterate over all-value blocks */ + if(value==newTrie->initialValue) { + repeatBlock=newTrie->dataNullOffset; + } else { + repeatBlock=-1; + } + + while(startinitialValue && isInNullBlock(newTrie, start, true)) { + start+=UTRIE2_DATA_BLOCK_LENGTH; /* nothing to do */ + continue; + } + + /* get index value */ + i2=getIndex2Block(newTrie, start, true); + if(i2<0) { + *pErrorCode=U_INTERNAL_PROGRAM_ERROR; + return; + } + i2+=(start>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK; + block=newTrie->index2[i2]; + if(isWritableBlock(newTrie, block)) { + /* already allocated */ + if(overwrite && block>=UNEWTRIE2_DATA_0800_OFFSET) { + /* + * We overwrite all values, and it's not a + * protected (ASCII-linear or 2-byte UTF-8) block: + * replace with the repeatBlock. + */ + setRepeatBlock=true; + } else { + /* !overwrite, or protected block: just write the values into this block */ + fillBlock(newTrie->data+block, + 0, UTRIE2_DATA_BLOCK_LENGTH, + value, newTrie->initialValue, overwrite); + } + } else if(newTrie->data[block]!=value && (overwrite || block==newTrie->dataNullOffset)) { + /* + * Set the repeatBlock instead of the null block or previous repeat block: + * + * If !isWritableBlock() then all entries in the block have the same value + * because it's the null block or a range block (the repeatBlock from a previous + * call to utrie2_setRange32()). + * No other blocks are used multiple times before compacting. + * + * The null block is the only non-writable block with the initialValue because + * of the repeatBlock initialization above. (If value==initialValue, then + * the repeatBlock will be the null data block.) + * + * We set our repeatBlock if the desired value differs from the block's value, + * and if we overwrite any data or if the data is all initial values + * (which is the same as the block being the null block, see above). + */ + setRepeatBlock=true; + } + if(setRepeatBlock) { + if(repeatBlock>=0) { + setIndex2Entry(newTrie, i2, repeatBlock); + } else { + /* create and set and fill the repeatBlock */ + repeatBlock=getDataBlock(newTrie, start, true); + if(repeatBlock<0) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + writeBlock(newTrie->data+repeatBlock, value); + } + } + + start+=UTRIE2_DATA_BLOCK_LENGTH; + } + + if(rest>0) { + /* set partial block at [last block boundary..limit[ */ + block=getDataBlock(newTrie, start, true); + if(block<0) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + + fillBlock(newTrie->data+block, 0, rest, value, newTrie->initialValue, overwrite); + } + + return; +} + +/* compaction --------------------------------------------------------------- */ + +static inline UBool +equal_int32(const int32_t *s, const int32_t *t, int32_t length) { + while(length>0 && *s==*t) { + ++s; + ++t; + --length; + } + return (UBool)(length==0); +} + +static inline UBool +equal_uint32(const uint32_t *s, const uint32_t *t, int32_t length) { + while(length>0 && *s==*t) { + ++s; + ++t; + --length; + } + return (UBool)(length==0); +} + +static int32_t +findSameIndex2Block(const int32_t *idx, int32_t index2Length, int32_t otherBlock) { + int32_t block; + + /* ensure that we do not even partially get past index2Length */ + index2Length-=UTRIE2_INDEX_2_BLOCK_LENGTH; + + for(block=0; block<=index2Length; ++block) { + if(equal_int32(idx+block, idx+otherBlock, UTRIE2_INDEX_2_BLOCK_LENGTH)) { + return block; + } + } + return -1; +} + +static int32_t +findSameDataBlock(const uint32_t *data, int32_t dataLength, int32_t otherBlock, int32_t blockLength) { + int32_t block; + + /* ensure that we do not even partially get past dataLength */ + dataLength-=blockLength; + + for(block=0; block<=dataLength; block+=UTRIE2_DATA_GRANULARITY) { + if(equal_uint32(data+block, data+otherBlock, blockLength)) { + return block; + } + } + return -1; +} + +/* + * Find the start of the last range in the trie by enumerating backward. + * Indexes for supplementary code points higher than this will be omitted. + */ +static UChar32 +findHighStart(UNewTrie2 *trie, uint32_t highValue) { + const uint32_t *data32; + + uint32_t value, initialValue; + UChar32 c, prev; + int32_t i1, i2, j, i2Block, prevI2Block, index2NullOffset, block, prevBlock, nullBlock; + + data32=trie->data; + initialValue=trie->initialValue; + + index2NullOffset=trie->index2NullOffset; + nullBlock=trie->dataNullOffset; + + /* set variables for previous range */ + if(highValue==initialValue) { + prevI2Block=index2NullOffset; + prevBlock=nullBlock; + } else { + prevI2Block=-1; + prevBlock=-1; + } + prev=0x110000; + + /* enumerate index-2 blocks */ + i1=UNEWTRIE2_INDEX_1_LENGTH; + c=prev; + while(c>0) { + i2Block=trie->index1[--i1]; + if(i2Block==prevI2Block) { + /* the index-2 block is the same as the previous one, and filled with highValue */ + c-=UTRIE2_CP_PER_INDEX_1_ENTRY; + continue; + } + prevI2Block=i2Block; + if(i2Block==index2NullOffset) { + /* this is the null index-2 block */ + if(highValue!=initialValue) { + return c; + } + c-=UTRIE2_CP_PER_INDEX_1_ENTRY; + } else { + /* enumerate data blocks for one index-2 block */ + for(i2=UTRIE2_INDEX_2_BLOCK_LENGTH; i2>0;) { + block=trie->index2[i2Block+ --i2]; + if(block==prevBlock) { + /* the block is the same as the previous one, and filled with highValue */ + c-=UTRIE2_DATA_BLOCK_LENGTH; + continue; + } + prevBlock=block; + if(block==nullBlock) { + /* this is the null data block */ + if(highValue!=initialValue) { + return c; + } + c-=UTRIE2_DATA_BLOCK_LENGTH; + } else { + for(j=UTRIE2_DATA_BLOCK_LENGTH; j>0;) { + value=data32[block+ --j]; + if(value!=highValue) { + return c; + } + --c; + } + } + } + } + } + + /* deliver last range */ + return 0; +} + +/* + * Compact a build-time trie. + * + * The compaction + * - removes blocks that are identical with earlier ones + * - overlaps adjacent blocks as much as possible (if overlap==true) + * - moves blocks in steps of the data granularity + * - moves and overlaps blocks that overlap with multiple values in the overlap region + * + * It does not + * - try to move and overlap blocks that are not already adjacent + */ +static void +compactData(UNewTrie2 *trie) { +#ifdef UTRIE2_DEBUG + int32_t countSame=0, sumOverlaps=0; +#endif + + int32_t start, newStart, movedStart; + int32_t blockLength, overlap; + int32_t i, mapIndex, blockCount; + + /* do not compact linear-ASCII data */ + newStart=UTRIE2_DATA_START_OFFSET; + for(start=0, i=0; startmap[i]=start; + } + + /* + * Start with a block length of 64 for 2-byte UTF-8, + * then switch to UTRIE2_DATA_BLOCK_LENGTH. + */ + blockLength=64; + blockCount=blockLength>>UTRIE2_SHIFT_2; + for(start=newStart; startdataLength;) { + /* + * start: index of first entry of current block + * newStart: index where the current block is to be moved + * (right after current end of already-compacted data) + */ + if(start==UNEWTRIE2_DATA_0800_OFFSET) { + blockLength=UTRIE2_DATA_BLOCK_LENGTH; + blockCount=1; + } + + /* skip blocks that are not used */ + if(trie->map[start>>UTRIE2_SHIFT_2]<=0) { + /* advance start to the next block */ + start+=blockLength; + + /* leave newStart with the previous block! */ + continue; + } + + /* search for an identical block */ + if( (movedStart=findSameDataBlock(trie->data, newStart, start, blockLength)) + >=0 + ) { +#ifdef UTRIE2_DEBUG + ++countSame; +#endif + /* found an identical block, set the other block's index value for the current block */ + for(i=blockCount, mapIndex=start>>UTRIE2_SHIFT_2; i>0; --i) { + trie->map[mapIndex++]=movedStart; + movedStart+=UTRIE2_DATA_BLOCK_LENGTH; + } + + /* advance start to the next block */ + start+=blockLength; + + /* leave newStart with the previous block! */ + continue; + } + + /* see if the beginning of this block can be overlapped with the end of the previous block */ + /* look for maximum overlap (modulo granularity) with the previous, adjacent block */ + for(overlap=blockLength-UTRIE2_DATA_GRANULARITY; + overlap>0 && !equal_uint32(trie->data+(newStart-overlap), trie->data+start, overlap); + overlap-=UTRIE2_DATA_GRANULARITY) {} + +#ifdef UTRIE2_DEBUG + sumOverlaps+=overlap; +#endif + if(overlap>0 || newStart>UTRIE2_SHIFT_2; i>0; --i) { + trie->map[mapIndex++]=movedStart; + movedStart+=UTRIE2_DATA_BLOCK_LENGTH; + } + + /* move the non-overlapping indexes to their new positions */ + start+=overlap; + for(i=blockLength-overlap; i>0; --i) { + trie->data[newStart++]=trie->data[start++]; + } + } else /* no overlap && newStart==start */ { + for(i=blockCount, mapIndex=start>>UTRIE2_SHIFT_2; i>0; --i) { + trie->map[mapIndex++]=start; + start+=UTRIE2_DATA_BLOCK_LENGTH; + } + newStart=start; + } + } + + /* now adjust the index-2 table */ + for(i=0; iindex2Length; ++i) { + if(i==UNEWTRIE2_INDEX_GAP_OFFSET) { + /* Gap indexes are invalid (-1). Skip over the gap. */ + i+=UNEWTRIE2_INDEX_GAP_LENGTH; + } + trie->index2[i]=trie->map[trie->index2[i]>>UTRIE2_SHIFT_2]; + } + trie->dataNullOffset=trie->map[trie->dataNullOffset>>UTRIE2_SHIFT_2]; + + /* ensure dataLength alignment */ + while((newStart&(UTRIE2_DATA_GRANULARITY-1))!=0) { + trie->data[newStart++]=trie->initialValue; + } + +#ifdef UTRIE2_DEBUG + /* we saved some space */ + printf("compacting UTrie2: count of 32-bit data words %lu->%lu countSame=%ld sumOverlaps=%ld\n", + (long)trie->dataLength, (long)newStart, (long)countSame, (long)sumOverlaps); +#endif + + trie->dataLength=newStart; +} + +static void +compactIndex2(UNewTrie2 *trie) { + int32_t i, start, newStart, movedStart, overlap; + + /* do not compact linear-BMP index-2 blocks */ + newStart=UTRIE2_INDEX_2_BMP_LENGTH; + for(start=0, i=0; startmap[i]=start; + } + + /* Reduce the index table gap to what will be needed at runtime. */ + newStart+=UTRIE2_UTF8_2B_INDEX_2_LENGTH+((trie->highStart-0x10000)>>UTRIE2_SHIFT_1); + + for(start=UNEWTRIE2_INDEX_2_NULL_OFFSET; startindex2Length;) { + /* + * start: index of first entry of current block + * newStart: index where the current block is to be moved + * (right after current end of already-compacted data) + */ + + /* search for an identical block */ + if( (movedStart=findSameIndex2Block(trie->index2, newStart, start)) + >=0 + ) { + /* found an identical block, set the other block's index value for the current block */ + trie->map[start>>UTRIE2_SHIFT_1_2]=movedStart; + + /* advance start to the next block */ + start+=UTRIE2_INDEX_2_BLOCK_LENGTH; + + /* leave newStart with the previous block! */ + continue; + } + + /* see if the beginning of this block can be overlapped with the end of the previous block */ + /* look for maximum overlap with the previous, adjacent block */ + for(overlap=UTRIE2_INDEX_2_BLOCK_LENGTH-1; + overlap>0 && !equal_int32(trie->index2+(newStart-overlap), trie->index2+start, overlap); + --overlap) {} + + if(overlap>0 || newStartmap[start>>UTRIE2_SHIFT_1_2]=newStart-overlap; + + /* move the non-overlapping indexes to their new positions */ + start+=overlap; + for(i=UTRIE2_INDEX_2_BLOCK_LENGTH-overlap; i>0; --i) { + trie->index2[newStart++]=trie->index2[start++]; + } + } else /* no overlap && newStart==start */ { + trie->map[start>>UTRIE2_SHIFT_1_2]=start; + start+=UTRIE2_INDEX_2_BLOCK_LENGTH; + newStart=start; + } + } + + /* now adjust the index-1 table */ + for(i=0; iindex1[i]=trie->map[trie->index1[i]>>UTRIE2_SHIFT_1_2]; + } + trie->index2NullOffset=trie->map[trie->index2NullOffset>>UTRIE2_SHIFT_1_2]; + + /* + * Ensure data table alignment: + * Needs to be granularity-aligned for 16-bit trie + * (so that dataMove will be down-shiftable), + * and 2-aligned for uint32_t data. + */ + while((newStart&((UTRIE2_DATA_GRANULARITY-1)|1))!=0) { + /* Arbitrary value: 0x3fffc not possible for real data. */ + trie->index2[newStart++]=(int32_t)0xffff<%lu\n", + (long)trie->index2Length, (long)newStart); +#endif + + trie->index2Length=newStart; +} + +static void +compactTrie(UTrie2 *trie, UErrorCode *pErrorCode) { + UNewTrie2 *newTrie; + UChar32 highStart, suppHighStart; + uint32_t highValue; + + newTrie=trie->newTrie; + + /* find highStart and round it up */ + highValue=utrie2_get32(trie, 0x10ffff); + highStart=findHighStart(newTrie, highValue); + highStart=(highStart+(UTRIE2_CP_PER_INDEX_1_ENTRY-1))&~(UTRIE2_CP_PER_INDEX_1_ENTRY-1); + if(highStart==0x110000) { + highValue=trie->errorValue; + } + + /* + * Set trie->highStart only after utrie2_get32(trie, highStart). + * Otherwise utrie2_get32(trie, highStart) would try to read the highValue. + */ + trie->highStart=newTrie->highStart=highStart; + +#ifdef UTRIE2_DEBUG + printf("UTrie2: highStart U+%06lx highValue 0x%lx initialValue 0x%lx\n", + (long)highStart, (long)highValue, (long)trie->initialValue); +#endif + + if(highStart<0x110000) { + /* Blank out [highStart..10ffff] to release associated data blocks. */ + suppHighStart= highStart<=0x10000 ? 0x10000 : highStart; + utrie2_setRange32(trie, suppHighStart, 0x10ffff, trie->initialValue, true, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return; + } + } + + compactData(newTrie); + if(highStart>0x10000) { + compactIndex2(newTrie); +#ifdef UTRIE2_DEBUG + } else { + printf("UTrie2: highStart U+%04lx count of 16-bit index words %lu->%lu\n", + (long)highStart, (long)trie->newTrie->index2Length, (long)UTRIE2_INDEX_1_OFFSET); +#endif + } + + /* + * Store the highValue in the data array and round up the dataLength. + * Must be done after compactData() because that assumes that dataLength + * is a multiple of UTRIE2_DATA_BLOCK_LENGTH. + */ + newTrie->data[newTrie->dataLength++]=highValue; + while((newTrie->dataLength&(UTRIE2_DATA_GRANULARITY-1))!=0) { + newTrie->data[newTrie->dataLength++]=trie->initialValue; + } + + newTrie->isCompacted=true; +} + +/* serialization ------------------------------------------------------------ */ + +/** + * Maximum length of the runtime index array. + * Limited by its own 16-bit index values, and by uint16_t UTrie2Header.indexLength. + * (The actual maximum length is lower, + * (0x110000>>UTRIE2_SHIFT_2)+UTRIE2_UTF8_2B_INDEX_2_LENGTH+UTRIE2_MAX_INDEX_1_LENGTH.) + */ +#define UTRIE2_MAX_INDEX_LENGTH 0xffff + +/** + * Maximum length of the runtime data array. + * Limited by 16-bit index values that are left-shifted by UTRIE2_INDEX_SHIFT, + * and by uint16_t UTrie2Header.shiftedDataLength. + */ +#define UTRIE2_MAX_DATA_LENGTH (0xffff<0 if the data is moved to the end of the index array */ + UChar32 highStart; + + /* argument check */ + if(U_FAILURE(*pErrorCode)) { + return; + } + if( trie==nullptr || + valueBits<0 || UTRIE2_COUNT_VALUE_BITS<=valueBits + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + newTrie=trie->newTrie; + if(newTrie==nullptr) { + /* already frozen */ + UTrie2ValueBits frozenValueBits= + trie->data16!=nullptr ? UTRIE2_16_VALUE_BITS : UTRIE2_32_VALUE_BITS; + if(valueBits!=frozenValueBits) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + } + return; + } + + /* compact if necessary */ + if(!newTrie->isCompacted) { + compactTrie(trie, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return; + } + } + highStart=trie->highStart; + + if(highStart<=0x10000) { + allIndexesLength=UTRIE2_INDEX_1_OFFSET; + } else { + allIndexesLength=newTrie->index2Length; + } + if(valueBits==UTRIE2_16_VALUE_BITS) { + dataMove=allIndexesLength; + } else { + dataMove=0; + } + + /* are indexLength and dataLength within limits? */ + if( /* for unshifted indexLength */ + allIndexesLength>UTRIE2_MAX_INDEX_LENGTH || + /* for unshifted dataNullOffset */ + (dataMove+newTrie->dataNullOffset)>0xffff || + /* for unshifted 2-byte UTF-8 index-2 values */ + (dataMove+UNEWTRIE2_DATA_0800_OFFSET)>0xffff || + /* for shiftedDataLength */ + (dataMove+newTrie->dataLength)>UTRIE2_MAX_DATA_LENGTH + ) { + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return; + } + + /* calculate the total serialized length */ + length=sizeof(UTrie2Header)+allIndexesLength*2; + if(valueBits==UTRIE2_16_VALUE_BITS) { + length+=newTrie->dataLength*2; + } else { + length+=newTrie->dataLength*4; + } + + trie->memory=uprv_malloc(length); + if(trie->memory==nullptr) { + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; + } + trie->length=length; + trie->isMemoryOwned=true; + + trie->indexLength=allIndexesLength; + trie->dataLength=newTrie->dataLength; + if(highStart<=0x10000) { + trie->index2NullOffset=0xffff; + } else { + trie->index2NullOffset=static_cast(UTRIE2_INDEX_2_OFFSET+newTrie->index2NullOffset); + } + trie->dataNullOffset=(uint16_t)(dataMove+newTrie->dataNullOffset); + trie->highValueIndex=dataMove+trie->dataLength-UTRIE2_DATA_GRANULARITY; + + /* set the header fields */ + header=(UTrie2Header *)trie->memory; + + header->signature=UTRIE2_SIG; /* "Tri2" */ + header->options=(uint16_t)valueBits; + + header->indexLength=(uint16_t)trie->indexLength; + header->shiftedDataLength=(uint16_t)(trie->dataLength>>UTRIE2_INDEX_SHIFT); + header->index2NullOffset=trie->index2NullOffset; + header->dataNullOffset=trie->dataNullOffset; + header->shiftedHighStart=(uint16_t)(highStart>>UTRIE2_SHIFT_1); + + /* fill the index and data arrays */ + dest16=(uint16_t *)(header+1); + trie->index=dest16; + + /* write the index-2 array values shifted right by UTRIE2_INDEX_SHIFT, after adding dataMove */ + p=(uint32_t *)newTrie->index2; + for(i=UTRIE2_INDEX_2_BMP_LENGTH; i>0; --i) { + *dest16++=(uint16_t)((dataMove + *p++)>>UTRIE2_INDEX_SHIFT); + } + + /* write UTF-8 2-byte index-2 values, not right-shifted */ + for(i=0; i<(0xc2-0xc0); ++i) { /* C0..C1 */ + *dest16++=(uint16_t)(dataMove+UTRIE2_BAD_UTF8_DATA_OFFSET); + } + for(; i<(0xe0-0xc0); ++i) { /* C2..DF */ + *dest16++=(uint16_t)(dataMove+newTrie->index2[i<<(6-UTRIE2_SHIFT_2)]); + } + + if(highStart>0x10000) { + int32_t index1Length=(highStart-0x10000)>>UTRIE2_SHIFT_1; + int32_t index2Offset=UTRIE2_INDEX_2_BMP_LENGTH+UTRIE2_UTF8_2B_INDEX_2_LENGTH+index1Length; + + /* write 16-bit index-1 values for supplementary code points */ + p=(uint32_t *)newTrie->index1+UTRIE2_OMITTED_BMP_INDEX_1_LENGTH; + for(i=index1Length; i>0; --i) { + *dest16++=(uint16_t)(UTRIE2_INDEX_2_OFFSET + *p++); + } + + /* + * write the index-2 array values for supplementary code points, + * shifted right by UTRIE2_INDEX_SHIFT, after adding dataMove + */ + p=(uint32_t *)newTrie->index2+index2Offset; + for(i=newTrie->index2Length-index2Offset; i>0; --i) { + *dest16++=(uint16_t)((dataMove + *p++)>>UTRIE2_INDEX_SHIFT); + } + } + + /* write the 16/32-bit data array */ + switch(valueBits) { + case UTRIE2_16_VALUE_BITS: + /* write 16-bit data values */ + trie->data16=dest16; + trie->data32=nullptr; + p=newTrie->data; + for(i=newTrie->dataLength; i>0; --i) { + *dest16++=(uint16_t)*p++; + } + break; + case UTRIE2_32_VALUE_BITS: + /* write 32-bit data values */ + trie->data16=nullptr; + trie->data32=(uint32_t *)dest16; + uprv_memcpy(dest16, newTrie->data, (size_t)newTrie->dataLength*4); + break; + default: + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + +#ifdef UTRIE2_DEBUG + utrie2_printLengths(trie, ""); +#endif + +#ifdef UCPTRIE_DEBUG + umutablecptrie_setName(newTrie->t3, trie->name); + ucptrie_close( + umutablecptrie_buildImmutable( + newTrie->t3, UCPTRIE_TYPE_FAST, (UCPTrieValueWidth)valueBits, pErrorCode)); +#endif + /* Delete the UNewTrie2. */ + uprv_free(newTrie->data); + uprv_free(newTrie); + trie->newTrie=nullptr; +} diff --git a/deps/icu-small/source/common/utrie2_impl.h b/deps/icu-small/source/common/utrie2_impl.h index 2a14db3a6bdd1a..f36b381d08b786 100644 --- a/deps/icu-small/source/common/utrie2_impl.h +++ b/deps/icu-small/source/common/utrie2_impl.h @@ -1,175 +1,175 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2001-2008, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* file name: utrie2_impl.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2008sep26 (split off from utrie2.c) -* created by: Markus W. Scherer -* -* Definitions needed for both runtime and builder code for UTrie2, -* used by utrie2.c and utrie2_builder.c. -*/ - -#ifndef __UTRIE2_IMPL_H__ -#define __UTRIE2_IMPL_H__ - -#ifdef UCPTRIE_DEBUG -#include "unicode/umutablecptrie.h" -#endif -#include "utrie2.h" - -/* Public UTrie2 API implementation ----------------------------------------- */ - -/* - * These definitions are mostly needed by utrie2.cpp, - * but also by utrie2_serialize() and utrie2_swap(). - */ - -// UTrie2 signature values, in platform endianness and opposite endianness. -// The UTrie2 signature ASCII byte values spell "Tri2". -#define UTRIE2_SIG 0x54726932 -#define UTRIE2_OE_SIG 0x32697254 - -/** - * Trie data structure in serialized form: - * - * UTrie2Header header; - * uint16_t index[header.index2Length]; - * uint16_t data[header.shiftedDataLength<<2]; -- or uint32_t data[...] - * @internal - */ -typedef struct UTrie2Header { - /** "Tri2" in big-endian US-ASCII (0x54726932) */ - uint32_t signature; - - /** - * options bit field: - * 15.. 4 reserved (0) - * 3.. 0 UTrie2ValueBits valueBits - */ - uint16_t options; - - /** UTRIE2_INDEX_1_OFFSET..UTRIE2_MAX_INDEX_LENGTH */ - uint16_t indexLength; - - /** (UTRIE2_DATA_START_OFFSET..UTRIE2_MAX_DATA_LENGTH)>>UTRIE2_INDEX_SHIFT */ - uint16_t shiftedDataLength; - - /** Null index and data blocks, not shifted. */ - uint16_t index2NullOffset, dataNullOffset; - - /** - * First code point of the single-value range ending with U+10ffff, - * rounded up and then shifted right by UTRIE2_SHIFT_1. - */ - uint16_t shiftedHighStart; -} UTrie2Header; - -/** - * Constants for use with UTrie2Header.options. - * @internal - */ -enum { - /** Mask to get the UTrie2ValueBits valueBits from options. */ - UTRIE2_OPTIONS_VALUE_BITS_MASK=0xf -}; - -/* Building a trie ---------------------------------------------------------- */ - -/* - * These definitions are mostly needed by utrie2_builder.c, but also by - * utrie2_get32() and utrie2_enum(). - */ - -enum { - /** - * At build time, leave a gap in the index-2 table, - * at least as long as the maximum lengths of the 2-byte UTF-8 index-2 table - * and the supplementary index-1 table. - * Round up to UTRIE2_INDEX_2_BLOCK_LENGTH for proper compacting. - */ - UNEWTRIE2_INDEX_GAP_OFFSET=UTRIE2_INDEX_2_BMP_LENGTH, - UNEWTRIE2_INDEX_GAP_LENGTH= - ((UTRIE2_UTF8_2B_INDEX_2_LENGTH+UTRIE2_MAX_INDEX_1_LENGTH)+UTRIE2_INDEX_2_MASK)& - ~UTRIE2_INDEX_2_MASK, - - /** - * Maximum length of the build-time index-2 array. - * Maximum number of Unicode code points (0x110000) shifted right by UTRIE2_SHIFT_2, - * plus the part of the index-2 table for lead surrogate code points, - * plus the build-time index gap, - * plus the null index-2 block. - */ - UNEWTRIE2_MAX_INDEX_2_LENGTH= - (0x110000>>UTRIE2_SHIFT_2)+ - UTRIE2_LSCP_INDEX_2_LENGTH+ - UNEWTRIE2_INDEX_GAP_LENGTH+ - UTRIE2_INDEX_2_BLOCK_LENGTH, - - UNEWTRIE2_INDEX_1_LENGTH=0x110000>>UTRIE2_SHIFT_1 -}; - -/** - * Maximum length of the build-time data array. - * One entry per 0x110000 code points, plus the illegal-UTF-8 block and the null block, - * plus values for the 0x400 surrogate code units. - */ -#define UNEWTRIE2_MAX_DATA_LENGTH (0x110000+0x40+0x40+0x400) - -/* - * Build-time trie structure. - * - * Just using a boolean flag for "repeat use" could lead to data array overflow - * because we would not be able to detect when a data block becomes unused. - * It also leads to orphan data blocks that are kept through serialization. - * - * Need to use reference counting for data blocks, - * and allocDataBlock() needs to look for a free block before increasing dataLength. - * - * This scheme seems like overkill for index-2 blocks since the whole index array is - * preallocated anyway (unlike the growable data array). - * Just allocating multiple index-2 blocks as needed. - */ -struct UNewTrie2 { - int32_t index1[UNEWTRIE2_INDEX_1_LENGTH]; - int32_t index2[UNEWTRIE2_MAX_INDEX_2_LENGTH]; - uint32_t *data; -#ifdef UCPTRIE_DEBUG - UMutableCPTrie *t3; -#endif - - uint32_t initialValue, errorValue; - int32_t index2Length, dataCapacity, dataLength; - int32_t firstFreeBlock; - int32_t index2NullOffset, dataNullOffset; - UChar32 highStart; - UBool isCompacted; - - /** - * Multi-purpose per-data-block table. - * - * Before compacting: - * - * Per-data-block reference counters/free-block list. - * 0: unused - * >0: reference counter (number of index-2 entries pointing here) - * <0: next free data block in free-block list - * - * While compacting: - * - * Map of adjusted indexes, used in compactData() and compactIndex2(). - * Maps from original indexes to new ones. - */ - int32_t map[UNEWTRIE2_MAX_DATA_LENGTH>>UTRIE2_SHIFT_2]; -}; - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2001-2008, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: utrie2_impl.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2008sep26 (split off from utrie2.c) +* created by: Markus W. Scherer +* +* Definitions needed for both runtime and builder code for UTrie2, +* used by utrie2.c and utrie2_builder.c. +*/ + +#ifndef __UTRIE2_IMPL_H__ +#define __UTRIE2_IMPL_H__ + +#ifdef UCPTRIE_DEBUG +#include "unicode/umutablecptrie.h" +#endif +#include "utrie2.h" + +/* Public UTrie2 API implementation ----------------------------------------- */ + +/* + * These definitions are mostly needed by utrie2.cpp, + * but also by utrie2_serialize() and utrie2_swap(). + */ + +// UTrie2 signature values, in platform endianness and opposite endianness. +// The UTrie2 signature ASCII byte values spell "Tri2". +#define UTRIE2_SIG 0x54726932 +#define UTRIE2_OE_SIG 0x32697254 + +/** + * Trie data structure in serialized form: + * + * UTrie2Header header; + * uint16_t index[header.index2Length]; + * uint16_t data[header.shiftedDataLength<<2]; -- or uint32_t data[...] + * @internal + */ +typedef struct UTrie2Header { + /** "Tri2" in big-endian US-ASCII (0x54726932) */ + uint32_t signature; + + /** + * options bit field: + * 15.. 4 reserved (0) + * 3.. 0 UTrie2ValueBits valueBits + */ + uint16_t options; + + /** UTRIE2_INDEX_1_OFFSET..UTRIE2_MAX_INDEX_LENGTH */ + uint16_t indexLength; + + /** (UTRIE2_DATA_START_OFFSET..UTRIE2_MAX_DATA_LENGTH)>>UTRIE2_INDEX_SHIFT */ + uint16_t shiftedDataLength; + + /** Null index and data blocks, not shifted. */ + uint16_t index2NullOffset, dataNullOffset; + + /** + * First code point of the single-value range ending with U+10ffff, + * rounded up and then shifted right by UTRIE2_SHIFT_1. + */ + uint16_t shiftedHighStart; +} UTrie2Header; + +/** + * Constants for use with UTrie2Header.options. + * @internal + */ +enum { + /** Mask to get the UTrie2ValueBits valueBits from options. */ + UTRIE2_OPTIONS_VALUE_BITS_MASK=0xf +}; + +/* Building a trie ---------------------------------------------------------- */ + +/* + * These definitions are mostly needed by utrie2_builder.c, but also by + * utrie2_get32() and utrie2_enum(). + */ + +enum { + /** + * At build time, leave a gap in the index-2 table, + * at least as long as the maximum lengths of the 2-byte UTF-8 index-2 table + * and the supplementary index-1 table. + * Round up to UTRIE2_INDEX_2_BLOCK_LENGTH for proper compacting. + */ + UNEWTRIE2_INDEX_GAP_OFFSET=UTRIE2_INDEX_2_BMP_LENGTH, + UNEWTRIE2_INDEX_GAP_LENGTH= + ((UTRIE2_UTF8_2B_INDEX_2_LENGTH+UTRIE2_MAX_INDEX_1_LENGTH)+UTRIE2_INDEX_2_MASK)& + ~UTRIE2_INDEX_2_MASK, + + /** + * Maximum length of the build-time index-2 array. + * Maximum number of Unicode code points (0x110000) shifted right by UTRIE2_SHIFT_2, + * plus the part of the index-2 table for lead surrogate code points, + * plus the build-time index gap, + * plus the null index-2 block. + */ + UNEWTRIE2_MAX_INDEX_2_LENGTH= + (0x110000>>UTRIE2_SHIFT_2)+ + UTRIE2_LSCP_INDEX_2_LENGTH+ + UNEWTRIE2_INDEX_GAP_LENGTH+ + UTRIE2_INDEX_2_BLOCK_LENGTH, + + UNEWTRIE2_INDEX_1_LENGTH=0x110000>>UTRIE2_SHIFT_1 +}; + +/** + * Maximum length of the build-time data array. + * One entry per 0x110000 code points, plus the illegal-UTF-8 block and the null block, + * plus values for the 0x400 surrogate code units. + */ +#define UNEWTRIE2_MAX_DATA_LENGTH (0x110000+0x40+0x40+0x400) + +/* + * Build-time trie structure. + * + * Just using a boolean flag for "repeat use" could lead to data array overflow + * because we would not be able to detect when a data block becomes unused. + * It also leads to orphan data blocks that are kept through serialization. + * + * Need to use reference counting for data blocks, + * and allocDataBlock() needs to look for a free block before increasing dataLength. + * + * This scheme seems like overkill for index-2 blocks since the whole index array is + * preallocated anyway (unlike the growable data array). + * Just allocating multiple index-2 blocks as needed. + */ +struct UNewTrie2 { + int32_t index1[UNEWTRIE2_INDEX_1_LENGTH]; + int32_t index2[UNEWTRIE2_MAX_INDEX_2_LENGTH]; + uint32_t *data; +#ifdef UCPTRIE_DEBUG + UMutableCPTrie *t3; +#endif + + uint32_t initialValue, errorValue; + int32_t index2Length, dataCapacity, dataLength; + int32_t firstFreeBlock; + int32_t index2NullOffset, dataNullOffset; + UChar32 highStart; + UBool isCompacted; + + /** + * Multi-purpose per-data-block table. + * + * Before compacting: + * + * Per-data-block reference counters/free-block list. + * 0: unused + * >0: reference counter (number of index-2 entries pointing here) + * <0: next free data block in free-block list + * + * While compacting: + * + * Map of adjusted indexes, used in compactData() and compactIndex2(). + * Maps from original indexes to new ones. + */ + int32_t map[UNEWTRIE2_MAX_DATA_LENGTH>>UTRIE2_SHIFT_2]; +}; + +#endif diff --git a/deps/icu-small/source/common/utrie_swap.cpp b/deps/icu-small/source/common/utrie_swap.cpp index b01b94601e4d6f..ee8b3b87223117 100644 --- a/deps/icu-small/source/common/utrie_swap.cpp +++ b/deps/icu-small/source/common/utrie_swap.cpp @@ -1,348 +1,348 @@ -// © 2018 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// utrie_swap.cpp -// created: 2018aug08 Markus W. Scherer - -#include "unicode/utypes.h" -#include "cmemory.h" -#include "ucptrie_impl.h" -#include "udataswp.h" -#include "utrie.h" -#include "utrie2_impl.h" - -// These functions for swapping different generations of ICU code point tries are here -// so that their implementation files need not depend on swapper code, -// need not depend on each other, and so that other swapper code -// need not depend on other trie code. - -namespace { - -constexpr int32_t ASCII_LIMIT = 0x80; - -} // namespace - -U_CAPI int32_t U_EXPORT2 -utrie_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const UTrieHeader *inTrie; - UTrieHeader trie; - int32_t size; - UBool dataIs32; - - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || (length>=0 && outData==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* setup and swapping */ - if(length>=0 && (uint32_t)lengthreadUInt32(inTrie->signature); - trie.options=ds->readUInt32(inTrie->options); - trie.indexLength=udata_readInt32(ds, inTrie->indexLength); - trie.dataLength=udata_readInt32(ds, inTrie->dataLength); - - if( trie.signature!=0x54726965 || - (trie.options&UTRIE_OPTIONS_SHIFT_MASK)!=UTRIE_SHIFT || - ((trie.options>>UTRIE_OPTIONS_INDEX_SHIFT)&UTRIE_OPTIONS_SHIFT_MASK)!=UTRIE_INDEX_SHIFT || - trie.indexLength=0) { - UTrieHeader *outTrie; - - if(lengthswapArray32(ds, inTrie, sizeof(UTrieHeader), outTrie, pErrorCode); - - /* swap the index and the data */ - if(dataIs32) { - ds->swapArray16(ds, inTrie+1, trie.indexLength*2, outTrie+1, pErrorCode); - ds->swapArray32(ds, (const uint16_t *)(inTrie+1)+trie.indexLength, trie.dataLength*4, - (uint16_t *)(outTrie+1)+trie.indexLength, pErrorCode); - } else { - ds->swapArray16(ds, inTrie+1, (trie.indexLength+trie.dataLength)*2, outTrie+1, pErrorCode); - } - } - - return size; -} - -U_CAPI int32_t U_EXPORT2 -utrie2_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const UTrie2Header *inTrie; - UTrie2Header trie; - int32_t dataLength, size; - UTrie2ValueBits valueBits; - - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==NULL || inData==NULL || (length>=0 && outData==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* setup and swapping */ - if(length>=0 && length<(int32_t)sizeof(UTrie2Header)) { - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - inTrie=(const UTrie2Header *)inData; - trie.signature=ds->readUInt32(inTrie->signature); - trie.options=ds->readUInt16(inTrie->options); - trie.indexLength=ds->readUInt16(inTrie->indexLength); - trie.shiftedDataLength=ds->readUInt16(inTrie->shiftedDataLength); - - valueBits=(UTrie2ValueBits)(trie.options&UTRIE2_OPTIONS_VALUE_BITS_MASK); - dataLength=(int32_t)trie.shiftedDataLength<=0) { - UTrie2Header *outTrie; - - if(lengthswapArray32(ds, &inTrie->signature, 4, &outTrie->signature, pErrorCode); - ds->swapArray16(ds, &inTrie->options, 12, &outTrie->options, pErrorCode); - - /* swap the index and the data */ - switch(valueBits) { - case UTRIE2_16_VALUE_BITS: - ds->swapArray16(ds, inTrie+1, (trie.indexLength+dataLength)*2, outTrie+1, pErrorCode); - break; - case UTRIE2_32_VALUE_BITS: - ds->swapArray16(ds, inTrie+1, trie.indexLength*2, outTrie+1, pErrorCode); - ds->swapArray32(ds, (const uint16_t *)(inTrie+1)+trie.indexLength, dataLength*4, - (uint16_t *)(outTrie+1)+trie.indexLength, pErrorCode); - break; - default: - *pErrorCode=U_INVALID_FORMAT_ERROR; - return 0; - } - } - - return size; -} - -U_CAPI int32_t U_EXPORT2 -ucptrie_swap(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - const UCPTrieHeader *inTrie; - UCPTrieHeader trie; - int32_t dataLength, size; - UCPTrieValueWidth valueWidth; - - if(U_FAILURE(*pErrorCode)) { - return 0; - } - if(ds==nullptr || inData==nullptr || (length>=0 && outData==nullptr)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* setup and swapping */ - if(length>=0 && length<(int32_t)sizeof(UCPTrieHeader)) { - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; - return 0; - } - - inTrie=(const UCPTrieHeader *)inData; - trie.signature=ds->readUInt32(inTrie->signature); - trie.options=ds->readUInt16(inTrie->options); - trie.indexLength=ds->readUInt16(inTrie->indexLength); - trie.dataLength = ds->readUInt16(inTrie->dataLength); - - UCPTrieType type = (UCPTrieType)((trie.options >> 6) & 3); - valueWidth = (UCPTrieValueWidth)(trie.options & UCPTRIE_OPTIONS_VALUE_BITS_MASK); - dataLength = ((int32_t)(trie.options & UCPTRIE_OPTIONS_DATA_LENGTH_MASK) << 4) | trie.dataLength; - - int32_t minIndexLength = type == UCPTRIE_TYPE_FAST ? - UCPTRIE_BMP_INDEX_LENGTH : UCPTRIE_SMALL_INDEX_LENGTH; - if( trie.signature!=UCPTRIE_SIG || - type > UCPTRIE_TYPE_SMALL || - (trie.options & UCPTRIE_OPTIONS_RESERVED_MASK) != 0 || - valueWidth > UCPTRIE_VALUE_BITS_8 || - trie.indexLength < minIndexLength || - dataLength < ASCII_LIMIT - ) { - *pErrorCode=U_INVALID_FORMAT_ERROR; /* not a UCPTrie */ - return 0; - } - - size=sizeof(UCPTrieHeader)+trie.indexLength*2; - switch(valueWidth) { - case UCPTRIE_VALUE_BITS_16: - size+=dataLength*2; - break; - case UCPTRIE_VALUE_BITS_32: - size+=dataLength*4; - break; - case UCPTRIE_VALUE_BITS_8: - size+=dataLength; - break; - default: - *pErrorCode=U_INVALID_FORMAT_ERROR; - return 0; - } - - if(length>=0) { - UCPTrieHeader *outTrie; - - if(lengthswapArray32(ds, &inTrie->signature, 4, &outTrie->signature, pErrorCode); - ds->swapArray16(ds, &inTrie->options, 12, &outTrie->options, pErrorCode); - - /* swap the index */ - const uint16_t *inIndex=reinterpret_cast(inTrie+1); - uint16_t *outIndex=reinterpret_cast(outTrie+1); - ds->swapArray16(ds, inIndex, trie.indexLength*2, outIndex, pErrorCode); - - /* swap the data */ - const uint16_t *inData=inIndex+trie.indexLength; - uint16_t *outData=outIndex+trie.indexLength; - switch(valueWidth) { - case UCPTRIE_VALUE_BITS_16: - ds->swapArray16(ds, inData, dataLength*2, outData, pErrorCode); - break; - case UCPTRIE_VALUE_BITS_32: - ds->swapArray32(ds, inData, dataLength*4, outData, pErrorCode); - break; - case UCPTRIE_VALUE_BITS_8: - if(inTrie!=outTrie) { - uprv_memmove(outData, inData, dataLength); - } - break; - default: - *pErrorCode=U_INVALID_FORMAT_ERROR; - return 0; - } - } - - return size; -} - -namespace { - -/** - * Gets the trie version from 32-bit-aligned memory containing the serialized form - * of a UTrie (version 1), a UTrie2 (version 2), or a UCPTrie (version 3). - * - * @param data a pointer to 32-bit-aligned memory containing the serialized form of a trie - * @param length the number of bytes available at data; - * can be more than necessary (see return value) - * @param anyEndianOk If false, only platform-endian serialized forms are recognized. - * If true, opposite-endian serialized forms are recognized as well. - * @return the trie version of the serialized form, or 0 if it is not - * recognized as a serialized trie - */ -int32_t -getVersion(const void *data, int32_t length, UBool anyEndianOk) { - uint32_t signature; - if(length<16 || data==nullptr || (U_POINTER_MASK_LSB(data, 3)!=0)) { - return 0; - } - signature=*(const uint32_t *)data; - if(signature==UCPTRIE_SIG) { - return 3; - } - if(anyEndianOk && signature==UCPTRIE_OE_SIG) { - return 3; - } - if(signature==UTRIE2_SIG) { - return 2; - } - if(anyEndianOk && signature==UTRIE2_OE_SIG) { - return 2; - } - if(signature==UTRIE_SIG) { - return 1; - } - if(anyEndianOk && signature==UTRIE_OE_SIG) { - return 1; - } - return 0; -} - -} // namespace - -U_CAPI int32_t U_EXPORT2 -utrie_swapAnyVersion(const UDataSwapper *ds, - const void *inData, int32_t length, void *outData, - UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { return 0; } - switch(getVersion(inData, length, true)) { - case 1: - return utrie_swap(ds, inData, length, outData, pErrorCode); - case 2: - return utrie2_swap(ds, inData, length, outData, pErrorCode); - case 3: - return ucptrie_swap(ds, inData, length, outData, pErrorCode); - default: - *pErrorCode=U_INVALID_FORMAT_ERROR; - return 0; - } -} +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// utrie_swap.cpp +// created: 2018aug08 Markus W. Scherer + +#include "unicode/utypes.h" +#include "cmemory.h" +#include "ucptrie_impl.h" +#include "udataswp.h" +#include "utrie.h" +#include "utrie2_impl.h" + +// These functions for swapping different generations of ICU code point tries are here +// so that their implementation files need not depend on swapper code, +// need not depend on each other, and so that other swapper code +// need not depend on other trie code. + +namespace { + +constexpr int32_t ASCII_LIMIT = 0x80; + +} // namespace + +U_CAPI int32_t U_EXPORT2 +utrie_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const UTrieHeader *inTrie; + UTrieHeader trie; + int32_t size; + UBool dataIs32; + + if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || (length>=0 && outData==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* setup and swapping */ + if(length>=0 && (uint32_t)lengthreadUInt32(inTrie->signature); + trie.options=ds->readUInt32(inTrie->options); + trie.indexLength=udata_readInt32(ds, inTrie->indexLength); + trie.dataLength=udata_readInt32(ds, inTrie->dataLength); + + if( trie.signature!=0x54726965 || + (trie.options&UTRIE_OPTIONS_SHIFT_MASK)!=UTRIE_SHIFT || + ((trie.options>>UTRIE_OPTIONS_INDEX_SHIFT)&UTRIE_OPTIONS_SHIFT_MASK)!=UTRIE_INDEX_SHIFT || + trie.indexLength=0) { + UTrieHeader *outTrie; + + if(lengthswapArray32(ds, inTrie, sizeof(UTrieHeader), outTrie, pErrorCode); + + /* swap the index and the data */ + if(dataIs32) { + ds->swapArray16(ds, inTrie+1, trie.indexLength*2, outTrie+1, pErrorCode); + ds->swapArray32(ds, (const uint16_t *)(inTrie+1)+trie.indexLength, trie.dataLength*4, + (uint16_t *)(outTrie+1)+trie.indexLength, pErrorCode); + } else { + ds->swapArray16(ds, inTrie+1, (trie.indexLength+trie.dataLength)*2, outTrie+1, pErrorCode); + } + } + + return size; +} + +U_CAPI int32_t U_EXPORT2 +utrie2_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const UTrie2Header *inTrie; + UTrie2Header trie; + int32_t dataLength, size; + UTrie2ValueBits valueBits; + + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || (length>=0 && outData==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* setup and swapping */ + if(length>=0 && length<(int32_t)sizeof(UTrie2Header)) { + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + inTrie=(const UTrie2Header *)inData; + trie.signature=ds->readUInt32(inTrie->signature); + trie.options=ds->readUInt16(inTrie->options); + trie.indexLength=ds->readUInt16(inTrie->indexLength); + trie.shiftedDataLength=ds->readUInt16(inTrie->shiftedDataLength); + + valueBits=(UTrie2ValueBits)(trie.options&UTRIE2_OPTIONS_VALUE_BITS_MASK); + dataLength=(int32_t)trie.shiftedDataLength<=0) { + UTrie2Header *outTrie; + + if(lengthswapArray32(ds, &inTrie->signature, 4, &outTrie->signature, pErrorCode); + ds->swapArray16(ds, &inTrie->options, 12, &outTrie->options, pErrorCode); + + /* swap the index and the data */ + switch(valueBits) { + case UTRIE2_16_VALUE_BITS: + ds->swapArray16(ds, inTrie+1, (trie.indexLength+dataLength)*2, outTrie+1, pErrorCode); + break; + case UTRIE2_32_VALUE_BITS: + ds->swapArray16(ds, inTrie+1, trie.indexLength*2, outTrie+1, pErrorCode); + ds->swapArray32(ds, (const uint16_t *)(inTrie+1)+trie.indexLength, dataLength*4, + (uint16_t *)(outTrie+1)+trie.indexLength, pErrorCode); + break; + default: + *pErrorCode=U_INVALID_FORMAT_ERROR; + return 0; + } + } + + return size; +} + +U_CAPI int32_t U_EXPORT2 +ucptrie_swap(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + const UCPTrieHeader *inTrie; + UCPTrieHeader trie; + int32_t dataLength, size; + UCPTrieValueWidth valueWidth; + + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(ds==nullptr || inData==nullptr || (length>=0 && outData==nullptr)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + /* setup and swapping */ + if(length>=0 && length<(int32_t)sizeof(UCPTrieHeader)) { + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + inTrie=(const UCPTrieHeader *)inData; + trie.signature=ds->readUInt32(inTrie->signature); + trie.options=ds->readUInt16(inTrie->options); + trie.indexLength=ds->readUInt16(inTrie->indexLength); + trie.dataLength = ds->readUInt16(inTrie->dataLength); + + UCPTrieType type = (UCPTrieType)((trie.options >> 6) & 3); + valueWidth = (UCPTrieValueWidth)(trie.options & UCPTRIE_OPTIONS_VALUE_BITS_MASK); + dataLength = ((int32_t)(trie.options & UCPTRIE_OPTIONS_DATA_LENGTH_MASK) << 4) | trie.dataLength; + + int32_t minIndexLength = type == UCPTRIE_TYPE_FAST ? + UCPTRIE_BMP_INDEX_LENGTH : UCPTRIE_SMALL_INDEX_LENGTH; + if( trie.signature!=UCPTRIE_SIG || + type > UCPTRIE_TYPE_SMALL || + (trie.options & UCPTRIE_OPTIONS_RESERVED_MASK) != 0 || + valueWidth > UCPTRIE_VALUE_BITS_8 || + trie.indexLength < minIndexLength || + dataLength < ASCII_LIMIT + ) { + *pErrorCode=U_INVALID_FORMAT_ERROR; /* not a UCPTrie */ + return 0; + } + + size=sizeof(UCPTrieHeader)+trie.indexLength*2; + switch(valueWidth) { + case UCPTRIE_VALUE_BITS_16: + size+=dataLength*2; + break; + case UCPTRIE_VALUE_BITS_32: + size+=dataLength*4; + break; + case UCPTRIE_VALUE_BITS_8: + size+=dataLength; + break; + default: + *pErrorCode=U_INVALID_FORMAT_ERROR; + return 0; + } + + if(length>=0) { + UCPTrieHeader *outTrie; + + if(lengthswapArray32(ds, &inTrie->signature, 4, &outTrie->signature, pErrorCode); + ds->swapArray16(ds, &inTrie->options, 12, &outTrie->options, pErrorCode); + + /* swap the index */ + const uint16_t *inIndex=reinterpret_cast(inTrie+1); + uint16_t *outIndex=reinterpret_cast(outTrie+1); + ds->swapArray16(ds, inIndex, trie.indexLength*2, outIndex, pErrorCode); + + /* swap the data */ + const uint16_t *inData=inIndex+trie.indexLength; + uint16_t *outData=outIndex+trie.indexLength; + switch(valueWidth) { + case UCPTRIE_VALUE_BITS_16: + ds->swapArray16(ds, inData, dataLength*2, outData, pErrorCode); + break; + case UCPTRIE_VALUE_BITS_32: + ds->swapArray32(ds, inData, dataLength*4, outData, pErrorCode); + break; + case UCPTRIE_VALUE_BITS_8: + if(inTrie!=outTrie) { + uprv_memmove(outData, inData, dataLength); + } + break; + default: + *pErrorCode=U_INVALID_FORMAT_ERROR; + return 0; + } + } + + return size; +} + +namespace { + +/** + * Gets the trie version from 32-bit-aligned memory containing the serialized form + * of a UTrie (version 1), a UTrie2 (version 2), or a UCPTrie (version 3). + * + * @param data a pointer to 32-bit-aligned memory containing the serialized form of a trie + * @param length the number of bytes available at data; + * can be more than necessary (see return value) + * @param anyEndianOk If false, only platform-endian serialized forms are recognized. + * If true, opposite-endian serialized forms are recognized as well. + * @return the trie version of the serialized form, or 0 if it is not + * recognized as a serialized trie + */ +int32_t +getVersion(const void *data, int32_t length, UBool anyEndianOk) { + uint32_t signature; + if(length<16 || data==nullptr || (U_POINTER_MASK_LSB(data, 3)!=0)) { + return 0; + } + signature=*(const uint32_t *)data; + if(signature==UCPTRIE_SIG) { + return 3; + } + if(anyEndianOk && signature==UCPTRIE_OE_SIG) { + return 3; + } + if(signature==UTRIE2_SIG) { + return 2; + } + if(anyEndianOk && signature==UTRIE2_OE_SIG) { + return 2; + } + if(signature==UTRIE_SIG) { + return 1; + } + if(anyEndianOk && signature==UTRIE_OE_SIG) { + return 1; + } + return 0; +} + +} // namespace + +U_CAPI int32_t U_EXPORT2 +utrie_swapAnyVersion(const UDataSwapper *ds, + const void *inData, int32_t length, void *outData, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { return 0; } + switch(getVersion(inData, length, true)) { + case 1: + return utrie_swap(ds, inData, length, outData, pErrorCode); + case 2: + return utrie2_swap(ds, inData, length, outData, pErrorCode); + case 3: + return ucptrie_swap(ds, inData, length, outData, pErrorCode); + default: + *pErrorCode=U_INVALID_FORMAT_ERROR; + return 0; + } +} diff --git a/deps/icu-small/source/common/uts46.cpp b/deps/icu-small/source/common/uts46.cpp index 10a4f565973d73..1f44d506a2bad3 100644 --- a/deps/icu-small/source/common/uts46.cpp +++ b/deps/icu-small/source/common/uts46.cpp @@ -1,1494 +1,1494 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: uts46.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2010mar09 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_IDNA - -#include "unicode/idna.h" -#include "unicode/normalizer2.h" -#include "unicode/uscript.h" -#include "unicode/ustring.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "cstring.h" -#include "punycode.h" -#include "ubidi_props.h" -#include "ustr_imp.h" - -// Note about tests for UIDNA_ERROR_DOMAIN_NAME_TOO_LONG: -// -// The domain name length limit is 255 octets in an internal DNS representation -// where the last ("root") label is the empty label -// represented by length byte 0 alone. -// In a conventional string, this translates to 253 characters, or 254 -// if there is a trailing dot for the root label. - -U_NAMESPACE_BEGIN - -// Severe errors which usually result in a U+FFFD replacement character in the result string. -const uint32_t severeErrors= - UIDNA_ERROR_LEADING_COMBINING_MARK| - UIDNA_ERROR_DISALLOWED| - UIDNA_ERROR_PUNYCODE| - UIDNA_ERROR_LABEL_HAS_DOT| - UIDNA_ERROR_INVALID_ACE_LABEL; - -static inline UBool -isASCIIString(const UnicodeString &dest) { - const UChar *s=dest.getBuffer(); - const UChar *limit=s+dest.length(); - while(s0x7f) { - return false; - } - } - return true; -} - -static UBool -isASCIIOkBiDi(const UChar *s, int32_t length); - -static UBool -isASCIIOkBiDi(const char *s, int32_t length); - -// IDNA class default implementations -------------------------------------- *** - -IDNA::~IDNA() {} - -void -IDNA::labelToASCII_UTF8(StringPiece label, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - if(U_SUCCESS(errorCode)) { - UnicodeString destString; - labelToASCII(UnicodeString::fromUTF8(label), destString, - info, errorCode).toUTF8(dest); - } -} - -void -IDNA::labelToUnicodeUTF8(StringPiece label, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - if(U_SUCCESS(errorCode)) { - UnicodeString destString; - labelToUnicode(UnicodeString::fromUTF8(label), destString, - info, errorCode).toUTF8(dest); - } -} - -void -IDNA::nameToASCII_UTF8(StringPiece name, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - if(U_SUCCESS(errorCode)) { - UnicodeString destString; - nameToASCII(UnicodeString::fromUTF8(name), destString, - info, errorCode).toUTF8(dest); - } -} - -void -IDNA::nameToUnicodeUTF8(StringPiece name, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - if(U_SUCCESS(errorCode)) { - UnicodeString destString; - nameToUnicode(UnicodeString::fromUTF8(name), destString, - info, errorCode).toUTF8(dest); - } -} - -// UTS46 class declaration ------------------------------------------------- *** - -class UTS46 : public IDNA { -public: - UTS46(uint32_t options, UErrorCode &errorCode); - virtual ~UTS46(); - - virtual UnicodeString & - labelToASCII(const UnicodeString &label, UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const override; - - virtual UnicodeString & - labelToUnicode(const UnicodeString &label, UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const override; - - virtual UnicodeString & - nameToASCII(const UnicodeString &name, UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const override; - - virtual UnicodeString & - nameToUnicode(const UnicodeString &name, UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const override; - - virtual void - labelToASCII_UTF8(StringPiece label, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const override; - - virtual void - labelToUnicodeUTF8(StringPiece label, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const override; - - virtual void - nameToASCII_UTF8(StringPiece name, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const override; - - virtual void - nameToUnicodeUTF8(StringPiece name, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const override; - -private: - UnicodeString & - process(const UnicodeString &src, - UBool isLabel, UBool toASCII, - UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const; - - void - processUTF8(StringPiece src, - UBool isLabel, UBool toASCII, - ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const; - - UnicodeString & - processUnicode(const UnicodeString &src, - int32_t labelStart, int32_t mappingStart, - UBool isLabel, UBool toASCII, - UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const; - - // returns the new dest.length() - int32_t - mapDevChars(UnicodeString &dest, int32_t labelStart, int32_t mappingStart, - UErrorCode &errorCode) const; - - // returns the new label length - int32_t - processLabel(UnicodeString &dest, - int32_t labelStart, int32_t labelLength, - UBool toASCII, - IDNAInfo &info, UErrorCode &errorCode) const; - int32_t - markBadACELabel(UnicodeString &dest, - int32_t labelStart, int32_t labelLength, - UBool toASCII, IDNAInfo &info, UErrorCode &errorCode) const; - - void - checkLabelBiDi(const UChar *label, int32_t labelLength, IDNAInfo &info) const; - - UBool - isLabelOkContextJ(const UChar *label, int32_t labelLength) const; - - void - checkLabelContextO(const UChar *label, int32_t labelLength, IDNAInfo &info) const; - - const Normalizer2 &uts46Norm2; // uts46.nrm - uint32_t options; -}; - -IDNA * -IDNA::createUTS46Instance(uint32_t options, UErrorCode &errorCode) { - if(U_SUCCESS(errorCode)) { - IDNA *idna=new UTS46(options, errorCode); - if(idna==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - } else if(U_FAILURE(errorCode)) { - delete idna; - idna=NULL; - } - return idna; - } else { - return NULL; - } -} - -// UTS46 implementation ---------------------------------------------------- *** - -UTS46::UTS46(uint32_t opt, UErrorCode &errorCode) - : uts46Norm2(*Normalizer2::getInstance(NULL, "uts46", UNORM2_COMPOSE, errorCode)), - options(opt) {} - -UTS46::~UTS46() {} - -UnicodeString & -UTS46::labelToASCII(const UnicodeString &label, UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - return process(label, true, true, dest, info, errorCode); -} - -UnicodeString & -UTS46::labelToUnicode(const UnicodeString &label, UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - return process(label, true, false, dest, info, errorCode); -} - -UnicodeString & -UTS46::nameToASCII(const UnicodeString &name, UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - process(name, false, true, dest, info, errorCode); - if( dest.length()>=254 && (info.errors&UIDNA_ERROR_DOMAIN_NAME_TOO_LONG)==0 && - isASCIIString(dest) && - (dest.length()>254 || dest[253]!=0x2e) - ) { - info.errors|=UIDNA_ERROR_DOMAIN_NAME_TOO_LONG; - } - return dest; -} - -UnicodeString & -UTS46::nameToUnicode(const UnicodeString &name, UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - return process(name, false, false, dest, info, errorCode); -} - -void -UTS46::labelToASCII_UTF8(StringPiece label, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - processUTF8(label, true, true, dest, info, errorCode); -} - -void -UTS46::labelToUnicodeUTF8(StringPiece label, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - processUTF8(label, true, false, dest, info, errorCode); -} - -void -UTS46::nameToASCII_UTF8(StringPiece name, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - processUTF8(name, false, true, dest, info, errorCode); -} - -void -UTS46::nameToUnicodeUTF8(StringPiece name, ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - processUTF8(name, false, false, dest, info, errorCode); -} - -// UTS #46 data for ASCII characters. -// The normalizer (using uts46.nrm) maps uppercase ASCII letters to lowercase -// and passes through all other ASCII characters. -// If UIDNA_USE_STD3_RULES is set, then non-LDH characters are disallowed -// using this data. -// The ASCII fastpath also uses this data. -// Values: -1=disallowed 0==valid 1==mapped (lowercase) -static const int8_t asciiData[128]={ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - // 002D..002E; valid # HYPHEN-MINUS..FULL STOP - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, - // 0030..0039; valid # DIGIT ZERO..DIGIT NINE - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, - // 0041..005A; mapped # LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z - -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, - // 0061..007A; valid # LATIN SMALL LETTER A..LATIN SMALL LETTER Z - -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, -1, -1, -1, -1, -1 -}; - -UnicodeString & -UTS46::process(const UnicodeString &src, - UBool isLabel, UBool toASCII, - UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - // uts46Norm2.normalize() would do all of this error checking and setup, - // but with the ASCII fastpath we do not always call it, and do not - // call it first. - if(U_FAILURE(errorCode)) { - dest.setToBogus(); - return dest; - } - const UChar *srcArray=src.getBuffer(); - if(&dest==&src || srcArray==NULL) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - dest.setToBogus(); - return dest; - } - // Arguments are fine, reset output values. - dest.remove(); - info.reset(); - int32_t srcLength=src.length(); - if(srcLength==0) { - info.errors|=UIDNA_ERROR_EMPTY_LABEL; - return dest; - } - UChar *destArray=dest.getBuffer(srcLength); - if(destArray==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return dest; - } - // ASCII fastpath - UBool disallowNonLDHDot=(options&UIDNA_USE_STD3_RULES)!=0; - int32_t labelStart=0; - int32_t i; - for(i=0;; ++i) { - if(i==srcLength) { - if(toASCII) { - if((i-labelStart)>63) { - info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; - } - // There is a trailing dot if labelStart==i. - if(!isLabel && i>=254 && (i>254 || labelStart0x7f) { - break; - } - int cData=asciiData[c]; - if(cData>0) { - destArray[i]=c+0x20; // Lowercase an uppercase ASCII letter. - } else if(cData<0 && disallowNonLDHDot) { - break; // Replacing with U+FFFD can be complicated for toASCII. - } else { - destArray[i]=c; - if(c==0x2d) { // hyphen - if(i==(labelStart+3) && srcArray[i-1]==0x2d) { - // "??--..." is Punycode or forbidden. - ++i; // '-' was copied to dest already - break; - } - if(i==labelStart) { - // label starts with "-" - info.labelErrors|=UIDNA_ERROR_LEADING_HYPHEN; - } - if((i+1)==srcLength || srcArray[i+1]==0x2e) { - // label ends with "-" - info.labelErrors|=UIDNA_ERROR_TRAILING_HYPHEN; - } - } else if(c==0x2e) { // dot - if(isLabel) { - // Replacing with U+FFFD can be complicated for toASCII. - ++i; // '.' was copied to dest already - break; - } - if(i==labelStart) { - info.labelErrors|=UIDNA_ERROR_EMPTY_LABEL; - } - if(toASCII && (i-labelStart)>63) { - info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; - } - info.errors|=info.labelErrors; - info.labelErrors=0; - labelStart=i+1; - } - } - } - info.errors|=info.labelErrors; - dest.releaseBuffer(i); - processUnicode(src, labelStart, i, isLabel, toASCII, dest, info, errorCode); - if( info.isBiDi && U_SUCCESS(errorCode) && (info.errors&severeErrors)==0 && - (!info.isOkBiDi || (labelStart>0 && !isASCIIOkBiDi(dest.getBuffer(), labelStart))) - ) { - info.errors|=UIDNA_ERROR_BIDI; - } - return dest; -} - -void -UTS46::processUTF8(StringPiece src, - UBool isLabel, UBool toASCII, - ByteSink &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { - return; - } - const char *srcArray=src.data(); - int32_t srcLength=src.length(); - if(srcArray==NULL && srcLength!=0) { - errorCode=U_ILLEGAL_ARGUMENT_ERROR; - return; - } - // Arguments are fine, reset output values. - info.reset(); - if(srcLength==0) { - info.errors|=UIDNA_ERROR_EMPTY_LABEL; - dest.Flush(); - return; - } - UnicodeString destString; - int32_t labelStart=0; - if(srcLength<=256) { // length of stackArray[] - // ASCII fastpath - char stackArray[256]; - int32_t destCapacity; - char *destArray=dest.GetAppendBuffer(srcLength, srcLength+20, - stackArray, UPRV_LENGTHOF(stackArray), &destCapacity); - UBool disallowNonLDHDot=(options&UIDNA_USE_STD3_RULES)!=0; - int32_t i; - for(i=0;; ++i) { - if(i==srcLength) { - if(toASCII) { - if((i-labelStart)>63) { - info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; - } - // There is a trailing dot if labelStart==i. - if(!isLabel && i>=254 && (i>254 || labelStart0x7f - break; - } - int cData=asciiData[(int)c]; // Cast: gcc warns about indexing with a char. - if(cData>0) { - destArray[i]=c+0x20; // Lowercase an uppercase ASCII letter. - } else if(cData<0 && disallowNonLDHDot) { - break; // Replacing with U+FFFD can be complicated for toASCII. - } else { - destArray[i]=c; - if(c==0x2d) { // hyphen - if(i==(labelStart+3) && srcArray[i-1]==0x2d) { - // "??--..." is Punycode or forbidden. - break; - } - if(i==labelStart) { - // label starts with "-" - info.labelErrors|=UIDNA_ERROR_LEADING_HYPHEN; - } - if((i+1)==srcLength || srcArray[i+1]==0x2e) { - // label ends with "-" - info.labelErrors|=UIDNA_ERROR_TRAILING_HYPHEN; - } - } else if(c==0x2e) { // dot - if(isLabel) { - break; // Replacing with U+FFFD can be complicated for toASCII. - } - if(i==labelStart) { - info.labelErrors|=UIDNA_ERROR_EMPTY_LABEL; - } - if(toASCII && (i-labelStart)>63) { - info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; - } - info.errors|=info.labelErrors; - info.labelErrors=0; - labelStart=i+1; - } - } - } - info.errors|=info.labelErrors; - // Convert the processed ASCII prefix of the current label to UTF-16. - int32_t mappingStart=i-labelStart; - destString=UnicodeString::fromUTF8(StringPiece(destArray+labelStart, mappingStart)); - // Output the previous ASCII labels and process the rest of src in UTF-16. - dest.Append(destArray, labelStart); - processUnicode(UnicodeString::fromUTF8(StringPiece(src, labelStart)), 0, mappingStart, - isLabel, toASCII, - destString, info, errorCode); - } else { - // src is too long for the ASCII fastpath implementation. - processUnicode(UnicodeString::fromUTF8(src), 0, 0, - isLabel, toASCII, - destString, info, errorCode); - } - destString.toUTF8(dest); // calls dest.Flush() - if(toASCII && !isLabel) { - // length==labelStart==254 means that there is a trailing dot (ok) and - // destString is empty (do not index at 253-labelStart). - int32_t length=labelStart+destString.length(); - if( length>=254 && isASCIIString(destString) && - (length>254 || - (labelStart<254 && destString[253-labelStart]!=0x2e)) - ) { - info.errors|=UIDNA_ERROR_DOMAIN_NAME_TOO_LONG; - } - } - if( info.isBiDi && U_SUCCESS(errorCode) && (info.errors&severeErrors)==0 && - (!info.isOkBiDi || (labelStart>0 && !isASCIIOkBiDi(srcArray, labelStart))) - ) { - info.errors|=UIDNA_ERROR_BIDI; - } -} - -UnicodeString & -UTS46::processUnicode(const UnicodeString &src, - int32_t labelStart, int32_t mappingStart, - UBool isLabel, UBool toASCII, - UnicodeString &dest, - IDNAInfo &info, UErrorCode &errorCode) const { - if(mappingStart==0) { - uts46Norm2.normalize(src, dest, errorCode); - } else { - uts46Norm2.normalizeSecondAndAppend(dest, src.tempSubString(mappingStart), errorCode); - } - if(U_FAILURE(errorCode)) { - return dest; - } - UBool doMapDevChars= - toASCII ? (options&UIDNA_NONTRANSITIONAL_TO_ASCII)==0 : - (options&UIDNA_NONTRANSITIONAL_TO_UNICODE)==0; - const UChar *destArray=dest.getBuffer(); - int32_t destLength=dest.length(); - int32_t labelLimit=labelStart; - while(labelLimit=0x200c)) { - info.isTransDiff=true; - if(doMapDevChars) { - destLength=mapDevChars(dest, labelStart, labelLimit, errorCode); - if(U_FAILURE(errorCode)) { - return dest; - } - destArray=dest.getBuffer(); - // All deviation characters have been mapped, no need to check for them again. - doMapDevChars=false; - // Do not increment labelLimit in case c was removed. - continue; - } - } else if(U16_IS_SURROGATE(c)) { - if(U16_IS_SURROGATE_LEAD(c) ? - (labelLimit+1)==destLength || !U16_IS_TRAIL(destArray[labelLimit+1]) : - labelLimit==labelStart || !U16_IS_LEAD(destArray[labelLimit-1])) { - // Map an unpaired surrogate to U+FFFD before normalization so that when - // that removes characters we do not turn two unpaired ones into a pair. - info.labelErrors|=UIDNA_ERROR_DISALLOWED; - dest.setCharAt(labelLimit, 0xfffd); - destArray=dest.getBuffer(); - } - } - ++labelLimit; - } - // Permit an empty label at the end (0=4 && label[0]==0x78 && label[1]==0x6e && label[2]==0x2d && label[3]==0x2d) { - // Label starts with "xn--", try to un-Punycode it. - // In IDNA2008, labels like "xn--" (decodes to an empty string) and - // "xn--ASCII-" (decodes to just "ASCII") fail the round-trip validation from - // comparing the ToUnicode input with the back-to-ToASCII output. - // They are alternate encodings of the respective ASCII labels. - // Ignore "xn---" here: It will fail Punycode.decode() which logically comes before - // the round-trip verification. - if(labelLength==4 || (labelLength>5 && label[labelLength-1]==u'-')) { - info.labelErrors|=UIDNA_ERROR_INVALID_ACE_LABEL; - return markBadACELabel(dest, labelStart, labelLength, toASCII, info, errorCode); - } - wasPunycode=true; - UChar *unicodeBuffer=fromPunycode.getBuffer(-1); // capacity==-1: most labels should fit - if(unicodeBuffer==NULL) { - // Should never occur if we used capacity==-1 which uses the internal buffer. - errorCode=U_MEMORY_ALLOCATION_ERROR; - return labelLength; - } - UErrorCode punycodeErrorCode=U_ZERO_ERROR; - int32_t unicodeLength=u_strFromPunycode(label+4, labelLength-4, - unicodeBuffer, fromPunycode.getCapacity(), - NULL, &punycodeErrorCode); - if(punycodeErrorCode==U_BUFFER_OVERFLOW_ERROR) { - fromPunycode.releaseBuffer(0); - unicodeBuffer=fromPunycode.getBuffer(unicodeLength); - if(unicodeBuffer==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return labelLength; - } - punycodeErrorCode=U_ZERO_ERROR; - unicodeLength=u_strFromPunycode(label+4, labelLength-4, - unicodeBuffer, fromPunycode.getCapacity(), - NULL, &punycodeErrorCode); - } - fromPunycode.releaseBuffer(unicodeLength); - if(U_FAILURE(punycodeErrorCode)) { - info.labelErrors|=UIDNA_ERROR_PUNYCODE; - return markBadACELabel(dest, labelStart, labelLength, toASCII, info, errorCode); - } - // Check for NFC, and for characters that are not - // valid or deviation characters according to the normalizer. - // If there is something wrong, then the string will change. - // Note that the normalizer passes through non-LDH ASCII and deviation characters. - // Deviation characters are ok in Punycode even in transitional processing. - // In the code further below, if we find non-LDH ASCII and we have UIDNA_USE_STD3_RULES - // then we will set UIDNA_ERROR_INVALID_ACE_LABEL there too. - UBool isValid=uts46Norm2.isNormalized(fromPunycode, errorCode); - if(U_FAILURE(errorCode)) { - return labelLength; - } - if(!isValid) { - info.labelErrors|=UIDNA_ERROR_INVALID_ACE_LABEL; - return markBadACELabel(dest, labelStart, labelLength, toASCII, info, errorCode); - } - labelString=&fromPunycode; - label=fromPunycode.getBuffer(); - labelStart=0; - labelLength=fromPunycode.length(); - } else { - wasPunycode=false; - labelString=&dest; - } - // Validity check - if(labelLength==0) { - info.labelErrors|=UIDNA_ERROR_EMPTY_LABEL; - return replaceLabel(dest, destLabelStart, destLabelLength, - *labelString, labelLength, errorCode); - } - // labelLength>0 - if(labelLength>=4 && label[2]==0x2d && label[3]==0x2d) { - // label starts with "??--" - info.labelErrors|=UIDNA_ERROR_HYPHEN_3_4; - } - if(label[0]==0x2d) { - // label starts with "-" - info.labelErrors|=UIDNA_ERROR_LEADING_HYPHEN; - } - if(label[labelLength-1]==0x2d) { - // label ends with "-" - info.labelErrors|=UIDNA_ERROR_TRAILING_HYPHEN; - } - // If the label was not a Punycode label, then it was the result of - // mapping, normalization and label segmentation. - // If the label was in Punycode, then we mapped it again above - // and checked its validity. - // Now we handle the STD3 restriction to LDH characters (if set) - // and we look for U+FFFD which indicates disallowed characters - // in a non-Punycode label or U+FFFD itself in a Punycode label. - // We also check for dots which can come from the input to a single-label function. - // Ok to cast away const because we own the UnicodeString. - UChar *s=(UChar *)label; - const UChar *limit=label+labelLength; - UChar oredChars=0; - // If we enforce STD3 rules, then ASCII characters other than LDH and dot are disallowed. - UBool disallowNonLDHDot=(options&UIDNA_USE_STD3_RULES)!=0; - do { - UChar c=*s; - if(c<=0x7f) { - if(c==0x2e) { - info.labelErrors|=UIDNA_ERROR_LABEL_HAS_DOT; - *s=0xfffd; - } else if(disallowNonLDHDot && asciiData[c]<0) { - info.labelErrors|=UIDNA_ERROR_DISALLOWED; - *s=0xfffd; - } - } else { - oredChars|=c; - if(disallowNonLDHDot && isNonASCIIDisallowedSTD3Valid(c)) { - info.labelErrors|=UIDNA_ERROR_DISALLOWED; - *s=0xfffd; - } else if(c==0xfffd) { - info.labelErrors|=UIDNA_ERROR_DISALLOWED; - } - } - ++s; - } while(sreplace(labelStart, cpLength, (UChar)0xfffd); - label=labelString->getBuffer()+labelStart; - labelLength+=1-cpLength; - if(labelString==&dest) { - destLabelLength=labelLength; - } - } - if((info.labelErrors&severeErrors)==0) { - // Do contextual checks only if we do not have U+FFFD from a severe error - // because U+FFFD can make these checks fail. - if((options&UIDNA_CHECK_BIDI)!=0 && (!info.isBiDi || info.isOkBiDi)) { - checkLabelBiDi(label, labelLength, info); - } - if( (options&UIDNA_CHECK_CONTEXTJ)!=0 && (oredChars&0x200c)==0x200c && - !isLabelOkContextJ(label, labelLength) - ) { - info.labelErrors|=UIDNA_ERROR_CONTEXTJ; - } - if((options&UIDNA_CHECK_CONTEXTO)!=0 && oredChars>=0xb7) { - checkLabelContextO(label, labelLength, info); - } - if(toASCII) { - if(wasPunycode) { - // Leave a Punycode label unchanged if it has no severe errors. - if(destLabelLength>63) { - info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; - } - return destLabelLength; - } else if(oredChars>=0x80) { - // Contains non-ASCII characters. - UnicodeString punycode; - UChar *buffer=punycode.getBuffer(63); // 63==maximum DNS label length - if(buffer==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return destLabelLength; - } - buffer[0]=0x78; // Write "xn--". - buffer[1]=0x6e; - buffer[2]=0x2d; - buffer[3]=0x2d; - int32_t punycodeLength=u_strToPunycode(label, labelLength, - buffer+4, punycode.getCapacity()-4, - NULL, &errorCode); - if(errorCode==U_BUFFER_OVERFLOW_ERROR) { - errorCode=U_ZERO_ERROR; - punycode.releaseBuffer(4); - buffer=punycode.getBuffer(4+punycodeLength); - if(buffer==NULL) { - errorCode=U_MEMORY_ALLOCATION_ERROR; - return destLabelLength; - } - punycodeLength=u_strToPunycode(label, labelLength, - buffer+4, punycode.getCapacity()-4, - NULL, &errorCode); - } - punycodeLength+=4; - punycode.releaseBuffer(punycodeLength); - if(U_FAILURE(errorCode)) { - return destLabelLength; - } - if(punycodeLength>63) { - info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; - } - return replaceLabel(dest, destLabelStart, destLabelLength, - punycode, punycodeLength, errorCode); - } else { - // all-ASCII label - if(labelLength>63) { - info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; - } - } - } - } else { - // If a Punycode label has severe errors, - // then leave it but make sure it does not look valid. - if(wasPunycode) { - info.labelErrors|=UIDNA_ERROR_INVALID_ACE_LABEL; - return markBadACELabel(dest, destLabelStart, destLabelLength, toASCII, info, errorCode); - } - } - return replaceLabel(dest, destLabelStart, destLabelLength, - *labelString, labelLength, errorCode); -} - -// Make sure an ACE label does not look valid. -// Append U+FFFD if the label has only LDH characters. -// If UIDNA_USE_STD3_RULES, also replace disallowed ASCII characters with U+FFFD. -int32_t -UTS46::markBadACELabel(UnicodeString &dest, - int32_t labelStart, int32_t labelLength, - UBool toASCII, IDNAInfo &info, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { - return 0; - } - UBool disallowNonLDHDot=(options&UIDNA_USE_STD3_RULES)!=0; - UBool isASCII=true; - UBool onlyLDH=true; - const UChar *label=dest.getBuffer()+labelStart; - const UChar *limit=label+labelLength; - // Start after the initial "xn--". - // Ok to cast away const because we own the UnicodeString. - for(UChar *s=const_cast(label+4); s63) { - info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; - } - } - return labelLength; -} - -const uint32_t L_MASK=U_MASK(U_LEFT_TO_RIGHT); -const uint32_t R_AL_MASK=U_MASK(U_RIGHT_TO_LEFT)|U_MASK(U_RIGHT_TO_LEFT_ARABIC); -const uint32_t L_R_AL_MASK=L_MASK|R_AL_MASK; - -const uint32_t R_AL_AN_MASK=R_AL_MASK|U_MASK(U_ARABIC_NUMBER); - -const uint32_t EN_AN_MASK=U_MASK(U_EUROPEAN_NUMBER)|U_MASK(U_ARABIC_NUMBER); -const uint32_t R_AL_EN_AN_MASK=R_AL_MASK|EN_AN_MASK; -const uint32_t L_EN_MASK=L_MASK|U_MASK(U_EUROPEAN_NUMBER); - -const uint32_t ES_CS_ET_ON_BN_NSM_MASK= - U_MASK(U_EUROPEAN_NUMBER_SEPARATOR)| - U_MASK(U_COMMON_NUMBER_SEPARATOR)| - U_MASK(U_EUROPEAN_NUMBER_TERMINATOR)| - U_MASK(U_OTHER_NEUTRAL)| - U_MASK(U_BOUNDARY_NEUTRAL)| - U_MASK(U_DIR_NON_SPACING_MARK); -const uint32_t L_EN_ES_CS_ET_ON_BN_NSM_MASK=L_EN_MASK|ES_CS_ET_ON_BN_NSM_MASK; -const uint32_t R_AL_AN_EN_ES_CS_ET_ON_BN_NSM_MASK=R_AL_MASK|EN_AN_MASK|ES_CS_ET_ON_BN_NSM_MASK; - -// We scan the whole label and check both for whether it contains RTL characters -// and whether it passes the BiDi Rule. -// In a BiDi domain name, all labels must pass the BiDi Rule, but we might find -// that a domain name is a BiDi domain name (has an RTL label) only after -// processing several earlier labels. -void -UTS46::checkLabelBiDi(const UChar *label, int32_t labelLength, IDNAInfo &info) const { - // IDNA2008 BiDi rule - // Get the directionality of the first character. - UChar32 c; - int32_t i=0; - U16_NEXT_UNSAFE(label, i, c); - uint32_t firstMask=U_MASK(u_charDirection(c)); - // 1. The first character must be a character with BIDI property L, R - // or AL. If it has the R or AL property, it is an RTL label; if it - // has the L property, it is an LTR label. - if((firstMask&~L_R_AL_MASK)!=0) { - info.isOkBiDi=false; - } - // Get the directionality of the last non-NSM character. - uint32_t lastMask; - for(;;) { - if(i>=labelLength) { - lastMask=firstMask; - break; - } - U16_PREV_UNSAFE(label, labelLength, c); - UCharDirection dir=u_charDirection(c); - if(dir!=U_DIR_NON_SPACING_MARK) { - lastMask=U_MASK(dir); - break; - } - } - // 3. In an RTL label, the end of the label must be a character with - // BIDI property R, AL, EN or AN, followed by zero or more - // characters with BIDI property NSM. - // 6. In an LTR label, the end of the label must be a character with - // BIDI property L or EN, followed by zero or more characters with - // BIDI property NSM. - if( (firstMask&L_MASK)!=0 ? - (lastMask&~L_EN_MASK)!=0 : - (lastMask&~R_AL_EN_AN_MASK)!=0 - ) { - info.isOkBiDi=false; - } - // Add the directionalities of the intervening characters. - uint32_t mask=firstMask|lastMask; - while(ilabelStart) { - c=s[i-1]; - if(!(0x61<=c && c<=0x7a) && !(0x30<=c && c<=0x39)) { - // Last character in the label is not an L or EN. - return false; - } - } - labelStart=i+1; - } else if(i==labelStart) { - if(!(0x61<=c && c<=0x7a)) { - // First character in the label is not an L. - return false; - } - } else { - if(c<=0x20 && (c>=0x1c || (9<=c && c<=0xd))) { - // Intermediate character in the label is a B, S or WS. - return false; - } - } - } - return true; -} - -// UTF-8 version, called for source ASCII prefix. -// Can contain uppercase A-Z. -// s[length-1] must be the trailing dot. -static UBool -isASCIIOkBiDi(const char *s, int32_t length) { - int32_t labelStart=0; - for(int32_t i=0; ilabelStart) { - c=s[i-1]; - if(!(0x61<=c && c<=0x7a) && !(0x41<=c && c<=0x5a) && !(0x30<=c && c<=0x39)) { - // Last character in the label is not an L or EN. - return false; - } - } - labelStart=i+1; - } else if(i==labelStart) { - if(!(0x61<=c && c<=0x7a) && !(0x41<=c && c<=0x5a)) { - // First character in the label is not an L. - return false; - } - } else { - if(c<=0x20 && (c>=0x1c || (9<=c && c<=0xd))) { - // Intermediate character in the label is a B, S or WS. - return false; - } - } - } - return true; -} - -UBool -UTS46::isLabelOkContextJ(const UChar *label, int32_t labelLength) const { - // [IDNA2008-Tables] - // 200C..200D ; CONTEXTJ # ZERO WIDTH NON-JOINER..ZERO WIDTH JOINER - for(int32_t i=0; i0) { - info.labelErrors|=UIDNA_ERROR_CONTEXTO_DIGITS; - } - arabicDigits=-1; - } else if(0x6f0<=c) { - if(arabicDigits<0) { - info.labelErrors|=UIDNA_ERROR_CONTEXTO_DIGITS; - } - arabicDigits=1; - } - } - } else if(c==0x30fb) { - // Appendix A.7. KATAKANA MIDDLE DOT (U+30FB) - // Rule Set: - // False; - // For All Characters: - // If Script(cp) .in. {Hiragana, Katakana, Han} Then True; - // End For; - UErrorCode errorCode=U_ZERO_ERROR; - for(int j=0;;) { - if(j>labelEnd) { - info.labelErrors|=UIDNA_ERROR_CONTEXTO_PUNCTUATION; - break; - } - U16_NEXT(label, j, labelLength, c); - UScriptCode script=uscript_getScript(c, &errorCode); - if(script==USCRIPT_HIRAGANA || script==USCRIPT_KATAKANA || script==USCRIPT_HAN) { - break; - } - } - } - } -} - -U_NAMESPACE_END - -// C API ------------------------------------------------------------------- *** - -U_NAMESPACE_USE - -U_CAPI UIDNA * U_EXPORT2 -uidna_openUTS46(uint32_t options, UErrorCode *pErrorCode) { - return reinterpret_cast(IDNA::createUTS46Instance(options, *pErrorCode)); -} - -U_CAPI void U_EXPORT2 -uidna_close(UIDNA *idna) { - delete reinterpret_cast(idna); -} - -static UBool -checkArgs(const void *label, int32_t length, - void *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { - return false; - } - // sizeof(UIDNAInfo)=16 in the first API version. - if(pInfo==NULL || pInfo->size<16) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - if( (label==NULL ? length!=0 : length<-1) || - (dest==NULL ? capacity!=0 : capacity<0) || - (dest==label && label!=NULL) - ) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - // Set all *pInfo bytes to 0 except for the size field itself. - uprv_memset(&pInfo->size+1, 0, pInfo->size-sizeof(pInfo->size)); - return true; -} - -static void -idnaInfoToStruct(IDNAInfo &info, UIDNAInfo *pInfo) { - pInfo->isTransitionalDifferent=info.isTransitionalDifferent(); - pInfo->errors=info.getErrors(); -} - -U_CAPI int32_t U_EXPORT2 -uidna_labelToASCII(const UIDNA *idna, - const UChar *label, int32_t length, - UChar *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode) { - if(!checkArgs(label, length, dest, capacity, pInfo, pErrorCode)) { - return 0; - } - UnicodeString src((UBool)(length<0), label, length); - UnicodeString destString(dest, 0, capacity); - IDNAInfo info; - reinterpret_cast(idna)->labelToASCII(src, destString, info, *pErrorCode); - idnaInfoToStruct(info, pInfo); - return destString.extract(dest, capacity, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uidna_labelToUnicode(const UIDNA *idna, - const UChar *label, int32_t length, - UChar *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode) { - if(!checkArgs(label, length, dest, capacity, pInfo, pErrorCode)) { - return 0; - } - UnicodeString src((UBool)(length<0), label, length); - UnicodeString destString(dest, 0, capacity); - IDNAInfo info; - reinterpret_cast(idna)->labelToUnicode(src, destString, info, *pErrorCode); - idnaInfoToStruct(info, pInfo); - return destString.extract(dest, capacity, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uidna_nameToASCII(const UIDNA *idna, - const UChar *name, int32_t length, - UChar *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode) { - if(!checkArgs(name, length, dest, capacity, pInfo, pErrorCode)) { - return 0; - } - UnicodeString src((UBool)(length<0), name, length); - UnicodeString destString(dest, 0, capacity); - IDNAInfo info; - reinterpret_cast(idna)->nameToASCII(src, destString, info, *pErrorCode); - idnaInfoToStruct(info, pInfo); - return destString.extract(dest, capacity, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uidna_nameToUnicode(const UIDNA *idna, - const UChar *name, int32_t length, - UChar *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode) { - if(!checkArgs(name, length, dest, capacity, pInfo, pErrorCode)) { - return 0; - } - UnicodeString src((UBool)(length<0), name, length); - UnicodeString destString(dest, 0, capacity); - IDNAInfo info; - reinterpret_cast(idna)->nameToUnicode(src, destString, info, *pErrorCode); - idnaInfoToStruct(info, pInfo); - return destString.extract(dest, capacity, *pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uidna_labelToASCII_UTF8(const UIDNA *idna, - const char *label, int32_t length, - char *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode) { - if(!checkArgs(label, length, dest, capacity, pInfo, pErrorCode)) { - return 0; - } - StringPiece src(label, length<0 ? static_cast(uprv_strlen(label)) : length); - CheckedArrayByteSink sink(dest, capacity); - IDNAInfo info; - reinterpret_cast(idna)->labelToASCII_UTF8(src, sink, info, *pErrorCode); - idnaInfoToStruct(info, pInfo); - return u_terminateChars(dest, capacity, sink.NumberOfBytesAppended(), pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uidna_labelToUnicodeUTF8(const UIDNA *idna, - const char *label, int32_t length, - char *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode) { - if(!checkArgs(label, length, dest, capacity, pInfo, pErrorCode)) { - return 0; - } - StringPiece src(label, length<0 ? static_cast(uprv_strlen(label)) : length); - CheckedArrayByteSink sink(dest, capacity); - IDNAInfo info; - reinterpret_cast(idna)->labelToUnicodeUTF8(src, sink, info, *pErrorCode); - idnaInfoToStruct(info, pInfo); - return u_terminateChars(dest, capacity, sink.NumberOfBytesAppended(), pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uidna_nameToASCII_UTF8(const UIDNA *idna, - const char *name, int32_t length, - char *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode) { - if(!checkArgs(name, length, dest, capacity, pInfo, pErrorCode)) { - return 0; - } - StringPiece src(name, length<0 ? static_cast(uprv_strlen(name)) : length); - CheckedArrayByteSink sink(dest, capacity); - IDNAInfo info; - reinterpret_cast(idna)->nameToASCII_UTF8(src, sink, info, *pErrorCode); - idnaInfoToStruct(info, pInfo); - return u_terminateChars(dest, capacity, sink.NumberOfBytesAppended(), pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uidna_nameToUnicodeUTF8(const UIDNA *idna, - const char *name, int32_t length, - char *dest, int32_t capacity, - UIDNAInfo *pInfo, UErrorCode *pErrorCode) { - if(!checkArgs(name, length, dest, capacity, pInfo, pErrorCode)) { - return 0; - } - StringPiece src(name, length<0 ? static_cast(uprv_strlen(name)) : length); - CheckedArrayByteSink sink(dest, capacity); - IDNAInfo info; - reinterpret_cast(idna)->nameToUnicodeUTF8(src, sink, info, *pErrorCode); - idnaInfoToStruct(info, pInfo); - return u_terminateChars(dest, capacity, sink.NumberOfBytesAppended(), pErrorCode); -} - -#endif // UCONFIG_NO_IDNA +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: uts46.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2010mar09 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_IDNA + +#include "unicode/idna.h" +#include "unicode/normalizer2.h" +#include "unicode/uscript.h" +#include "unicode/ustring.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "cstring.h" +#include "punycode.h" +#include "ubidi_props.h" +#include "ustr_imp.h" + +// Note about tests for UIDNA_ERROR_DOMAIN_NAME_TOO_LONG: +// +// The domain name length limit is 255 octets in an internal DNS representation +// where the last ("root") label is the empty label +// represented by length byte 0 alone. +// In a conventional string, this translates to 253 characters, or 254 +// if there is a trailing dot for the root label. + +U_NAMESPACE_BEGIN + +// Severe errors which usually result in a U+FFFD replacement character in the result string. +const uint32_t severeErrors= + UIDNA_ERROR_LEADING_COMBINING_MARK| + UIDNA_ERROR_DISALLOWED| + UIDNA_ERROR_PUNYCODE| + UIDNA_ERROR_LABEL_HAS_DOT| + UIDNA_ERROR_INVALID_ACE_LABEL; + +static inline UBool +isASCIIString(const UnicodeString &dest) { + const char16_t *s=dest.getBuffer(); + const char16_t *limit=s+dest.length(); + while(s0x7f) { + return false; + } + } + return true; +} + +static UBool +isASCIIOkBiDi(const char16_t *s, int32_t length); + +static UBool +isASCIIOkBiDi(const char *s, int32_t length); + +// IDNA class default implementations -------------------------------------- *** + +IDNA::~IDNA() {} + +void +IDNA::labelToASCII_UTF8(StringPiece label, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + if(U_SUCCESS(errorCode)) { + UnicodeString destString; + labelToASCII(UnicodeString::fromUTF8(label), destString, + info, errorCode).toUTF8(dest); + } +} + +void +IDNA::labelToUnicodeUTF8(StringPiece label, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + if(U_SUCCESS(errorCode)) { + UnicodeString destString; + labelToUnicode(UnicodeString::fromUTF8(label), destString, + info, errorCode).toUTF8(dest); + } +} + +void +IDNA::nameToASCII_UTF8(StringPiece name, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + if(U_SUCCESS(errorCode)) { + UnicodeString destString; + nameToASCII(UnicodeString::fromUTF8(name), destString, + info, errorCode).toUTF8(dest); + } +} + +void +IDNA::nameToUnicodeUTF8(StringPiece name, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + if(U_SUCCESS(errorCode)) { + UnicodeString destString; + nameToUnicode(UnicodeString::fromUTF8(name), destString, + info, errorCode).toUTF8(dest); + } +} + +// UTS46 class declaration ------------------------------------------------- *** + +class UTS46 : public IDNA { +public: + UTS46(uint32_t options, UErrorCode &errorCode); + virtual ~UTS46(); + + virtual UnicodeString & + labelToASCII(const UnicodeString &label, UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const override; + + virtual UnicodeString & + labelToUnicode(const UnicodeString &label, UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const override; + + virtual UnicodeString & + nameToASCII(const UnicodeString &name, UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const override; + + virtual UnicodeString & + nameToUnicode(const UnicodeString &name, UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const override; + + virtual void + labelToASCII_UTF8(StringPiece label, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const override; + + virtual void + labelToUnicodeUTF8(StringPiece label, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const override; + + virtual void + nameToASCII_UTF8(StringPiece name, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const override; + + virtual void + nameToUnicodeUTF8(StringPiece name, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const override; + +private: + UnicodeString & + process(const UnicodeString &src, + UBool isLabel, UBool toASCII, + UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const; + + void + processUTF8(StringPiece src, + UBool isLabel, UBool toASCII, + ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const; + + UnicodeString & + processUnicode(const UnicodeString &src, + int32_t labelStart, int32_t mappingStart, + UBool isLabel, UBool toASCII, + UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const; + + // returns the new dest.length() + int32_t + mapDevChars(UnicodeString &dest, int32_t labelStart, int32_t mappingStart, + UErrorCode &errorCode) const; + + // returns the new label length + int32_t + processLabel(UnicodeString &dest, + int32_t labelStart, int32_t labelLength, + UBool toASCII, + IDNAInfo &info, UErrorCode &errorCode) const; + int32_t + markBadACELabel(UnicodeString &dest, + int32_t labelStart, int32_t labelLength, + UBool toASCII, IDNAInfo &info, UErrorCode &errorCode) const; + + void + checkLabelBiDi(const char16_t *label, int32_t labelLength, IDNAInfo &info) const; + + UBool + isLabelOkContextJ(const char16_t *label, int32_t labelLength) const; + + void + checkLabelContextO(const char16_t *label, int32_t labelLength, IDNAInfo &info) const; + + const Normalizer2 &uts46Norm2; // uts46.nrm + uint32_t options; +}; + +IDNA * +IDNA::createUTS46Instance(uint32_t options, UErrorCode &errorCode) { + if(U_SUCCESS(errorCode)) { + IDNA *idna=new UTS46(options, errorCode); + if(idna==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } else if(U_FAILURE(errorCode)) { + delete idna; + idna=nullptr; + } + return idna; + } else { + return nullptr; + } +} + +// UTS46 implementation ---------------------------------------------------- *** + +UTS46::UTS46(uint32_t opt, UErrorCode &errorCode) + : uts46Norm2(*Normalizer2::getInstance(nullptr, "uts46", UNORM2_COMPOSE, errorCode)), + options(opt) {} + +UTS46::~UTS46() {} + +UnicodeString & +UTS46::labelToASCII(const UnicodeString &label, UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + return process(label, true, true, dest, info, errorCode); +} + +UnicodeString & +UTS46::labelToUnicode(const UnicodeString &label, UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + return process(label, true, false, dest, info, errorCode); +} + +UnicodeString & +UTS46::nameToASCII(const UnicodeString &name, UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + process(name, false, true, dest, info, errorCode); + if( dest.length()>=254 && (info.errors&UIDNA_ERROR_DOMAIN_NAME_TOO_LONG)==0 && + isASCIIString(dest) && + (dest.length()>254 || dest[253]!=0x2e) + ) { + info.errors|=UIDNA_ERROR_DOMAIN_NAME_TOO_LONG; + } + return dest; +} + +UnicodeString & +UTS46::nameToUnicode(const UnicodeString &name, UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + return process(name, false, false, dest, info, errorCode); +} + +void +UTS46::labelToASCII_UTF8(StringPiece label, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + processUTF8(label, true, true, dest, info, errorCode); +} + +void +UTS46::labelToUnicodeUTF8(StringPiece label, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + processUTF8(label, true, false, dest, info, errorCode); +} + +void +UTS46::nameToASCII_UTF8(StringPiece name, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + processUTF8(name, false, true, dest, info, errorCode); +} + +void +UTS46::nameToUnicodeUTF8(StringPiece name, ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + processUTF8(name, false, false, dest, info, errorCode); +} + +// UTS #46 data for ASCII characters. +// The normalizer (using uts46.nrm) maps uppercase ASCII letters to lowercase +// and passes through all other ASCII characters. +// If UIDNA_USE_STD3_RULES is set, then non-LDH characters are disallowed +// using this data. +// The ASCII fastpath also uses this data. +// Values: -1=disallowed 0==valid 1==mapped (lowercase) +static const int8_t asciiData[128]={ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + // 002D..002E; valid # HYPHEN-MINUS..FULL STOP + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, + // 0030..0039; valid # DIGIT ZERO..DIGIT NINE + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, + // 0041..005A; mapped # LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z + -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, + // 0061..007A; valid # LATIN SMALL LETTER A..LATIN SMALL LETTER Z + -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, -1, -1, -1, -1, -1 +}; + +UnicodeString & +UTS46::process(const UnicodeString &src, + UBool isLabel, UBool toASCII, + UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + // uts46Norm2.normalize() would do all of this error checking and setup, + // but with the ASCII fastpath we do not always call it, and do not + // call it first. + if(U_FAILURE(errorCode)) { + dest.setToBogus(); + return dest; + } + const char16_t *srcArray=src.getBuffer(); + if(&dest==&src || srcArray==nullptr) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + dest.setToBogus(); + return dest; + } + // Arguments are fine, reset output values. + dest.remove(); + info.reset(); + int32_t srcLength=src.length(); + if(srcLength==0) { + info.errors|=UIDNA_ERROR_EMPTY_LABEL; + return dest; + } + char16_t *destArray=dest.getBuffer(srcLength); + if(destArray==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return dest; + } + // ASCII fastpath + UBool disallowNonLDHDot=(options&UIDNA_USE_STD3_RULES)!=0; + int32_t labelStart=0; + int32_t i; + for(i=0;; ++i) { + if(i==srcLength) { + if(toASCII) { + if((i-labelStart)>63) { + info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; + } + // There is a trailing dot if labelStart==i. + if(!isLabel && i>=254 && (i>254 || labelStart0x7f) { + break; + } + int cData=asciiData[c]; + if(cData>0) { + destArray[i]=c+0x20; // Lowercase an uppercase ASCII letter. + } else if(cData<0 && disallowNonLDHDot) { + break; // Replacing with U+FFFD can be complicated for toASCII. + } else { + destArray[i]=c; + if(c==0x2d) { // hyphen + if(i==(labelStart+3) && srcArray[i-1]==0x2d) { + // "??--..." is Punycode or forbidden. + ++i; // '-' was copied to dest already + break; + } + if(i==labelStart) { + // label starts with "-" + info.labelErrors|=UIDNA_ERROR_LEADING_HYPHEN; + } + if((i+1)==srcLength || srcArray[i+1]==0x2e) { + // label ends with "-" + info.labelErrors|=UIDNA_ERROR_TRAILING_HYPHEN; + } + } else if(c==0x2e) { // dot + if(isLabel) { + // Replacing with U+FFFD can be complicated for toASCII. + ++i; // '.' was copied to dest already + break; + } + if(i==labelStart) { + info.labelErrors|=UIDNA_ERROR_EMPTY_LABEL; + } + if(toASCII && (i-labelStart)>63) { + info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; + } + info.errors|=info.labelErrors; + info.labelErrors=0; + labelStart=i+1; + } + } + } + info.errors|=info.labelErrors; + dest.releaseBuffer(i); + processUnicode(src, labelStart, i, isLabel, toASCII, dest, info, errorCode); + if( info.isBiDi && U_SUCCESS(errorCode) && (info.errors&severeErrors)==0 && + (!info.isOkBiDi || (labelStart>0 && !isASCIIOkBiDi(dest.getBuffer(), labelStart))) + ) { + info.errors|=UIDNA_ERROR_BIDI; + } + return dest; +} + +void +UTS46::processUTF8(StringPiece src, + UBool isLabel, UBool toASCII, + ByteSink &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return; + } + const char *srcArray=src.data(); + int32_t srcLength=src.length(); + if(srcArray==nullptr && srcLength!=0) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + // Arguments are fine, reset output values. + info.reset(); + if(srcLength==0) { + info.errors|=UIDNA_ERROR_EMPTY_LABEL; + dest.Flush(); + return; + } + UnicodeString destString; + int32_t labelStart=0; + if(srcLength<=256) { // length of stackArray[] + // ASCII fastpath + char stackArray[256]; + int32_t destCapacity; + char *destArray=dest.GetAppendBuffer(srcLength, srcLength+20, + stackArray, UPRV_LENGTHOF(stackArray), &destCapacity); + UBool disallowNonLDHDot=(options&UIDNA_USE_STD3_RULES)!=0; + int32_t i; + for(i=0;; ++i) { + if(i==srcLength) { + if(toASCII) { + if((i-labelStart)>63) { + info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; + } + // There is a trailing dot if labelStart==i. + if(!isLabel && i>=254 && (i>254 || labelStart0x7f + break; + } + int cData=asciiData[(int)c]; // Cast: gcc warns about indexing with a char. + if(cData>0) { + destArray[i]=c+0x20; // Lowercase an uppercase ASCII letter. + } else if(cData<0 && disallowNonLDHDot) { + break; // Replacing with U+FFFD can be complicated for toASCII. + } else { + destArray[i]=c; + if(c==0x2d) { // hyphen + if(i==(labelStart+3) && srcArray[i-1]==0x2d) { + // "??--..." is Punycode or forbidden. + break; + } + if(i==labelStart) { + // label starts with "-" + info.labelErrors|=UIDNA_ERROR_LEADING_HYPHEN; + } + if((i+1)==srcLength || srcArray[i+1]==0x2e) { + // label ends with "-" + info.labelErrors|=UIDNA_ERROR_TRAILING_HYPHEN; + } + } else if(c==0x2e) { // dot + if(isLabel) { + break; // Replacing with U+FFFD can be complicated for toASCII. + } + if(i==labelStart) { + info.labelErrors|=UIDNA_ERROR_EMPTY_LABEL; + } + if(toASCII && (i-labelStart)>63) { + info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; + } + info.errors|=info.labelErrors; + info.labelErrors=0; + labelStart=i+1; + } + } + } + info.errors|=info.labelErrors; + // Convert the processed ASCII prefix of the current label to UTF-16. + int32_t mappingStart=i-labelStart; + destString=UnicodeString::fromUTF8(StringPiece(destArray+labelStart, mappingStart)); + // Output the previous ASCII labels and process the rest of src in UTF-16. + dest.Append(destArray, labelStart); + processUnicode(UnicodeString::fromUTF8(StringPiece(src, labelStart)), 0, mappingStart, + isLabel, toASCII, + destString, info, errorCode); + } else { + // src is too long for the ASCII fastpath implementation. + processUnicode(UnicodeString::fromUTF8(src), 0, 0, + isLabel, toASCII, + destString, info, errorCode); + } + destString.toUTF8(dest); // calls dest.Flush() + if(toASCII && !isLabel) { + // length==labelStart==254 means that there is a trailing dot (ok) and + // destString is empty (do not index at 253-labelStart). + int32_t length=labelStart+destString.length(); + if( length>=254 && isASCIIString(destString) && + (length>254 || + (labelStart<254 && destString[253-labelStart]!=0x2e)) + ) { + info.errors|=UIDNA_ERROR_DOMAIN_NAME_TOO_LONG; + } + } + if( info.isBiDi && U_SUCCESS(errorCode) && (info.errors&severeErrors)==0 && + (!info.isOkBiDi || (labelStart>0 && !isASCIIOkBiDi(srcArray, labelStart))) + ) { + info.errors|=UIDNA_ERROR_BIDI; + } +} + +UnicodeString & +UTS46::processUnicode(const UnicodeString &src, + int32_t labelStart, int32_t mappingStart, + UBool isLabel, UBool toASCII, + UnicodeString &dest, + IDNAInfo &info, UErrorCode &errorCode) const { + if(mappingStart==0) { + uts46Norm2.normalize(src, dest, errorCode); + } else { + uts46Norm2.normalizeSecondAndAppend(dest, src.tempSubString(mappingStart), errorCode); + } + if(U_FAILURE(errorCode)) { + return dest; + } + UBool doMapDevChars= + toASCII ? (options&UIDNA_NONTRANSITIONAL_TO_ASCII)==0 : + (options&UIDNA_NONTRANSITIONAL_TO_UNICODE)==0; + const char16_t *destArray=dest.getBuffer(); + int32_t destLength=dest.length(); + int32_t labelLimit=labelStart; + while(labelLimit=0x200c)) { + info.isTransDiff=true; + if(doMapDevChars) { + destLength=mapDevChars(dest, labelStart, labelLimit, errorCode); + if(U_FAILURE(errorCode)) { + return dest; + } + destArray=dest.getBuffer(); + // All deviation characters have been mapped, no need to check for them again. + doMapDevChars=false; + // Do not increment labelLimit in case c was removed. + continue; + } + } else if(U16_IS_SURROGATE(c)) { + if(U16_IS_SURROGATE_LEAD(c) ? + (labelLimit+1)==destLength || !U16_IS_TRAIL(destArray[labelLimit+1]) : + labelLimit==labelStart || !U16_IS_LEAD(destArray[labelLimit-1])) { + // Map an unpaired surrogate to U+FFFD before normalization so that when + // that removes characters we do not turn two unpaired ones into a pair. + info.labelErrors|=UIDNA_ERROR_DISALLOWED; + dest.setCharAt(labelLimit, 0xfffd); + destArray=dest.getBuffer(); + } + } + ++labelLimit; + } + // Permit an empty label at the end (0=4 && label[0]==0x78 && label[1]==0x6e && label[2]==0x2d && label[3]==0x2d) { + // Label starts with "xn--", try to un-Punycode it. + // In IDNA2008, labels like "xn--" (decodes to an empty string) and + // "xn--ASCII-" (decodes to just "ASCII") fail the round-trip validation from + // comparing the ToUnicode input with the back-to-ToASCII output. + // They are alternate encodings of the respective ASCII labels. + // Ignore "xn---" here: It will fail Punycode.decode() which logically comes before + // the round-trip verification. + if(labelLength==4 || (labelLength>5 && label[labelLength-1]==u'-')) { + info.labelErrors|=UIDNA_ERROR_INVALID_ACE_LABEL; + return markBadACELabel(dest, labelStart, labelLength, toASCII, info, errorCode); + } + wasPunycode=true; + char16_t *unicodeBuffer=fromPunycode.getBuffer(-1); // capacity==-1: most labels should fit + if(unicodeBuffer==nullptr) { + // Should never occur if we used capacity==-1 which uses the internal buffer. + errorCode=U_MEMORY_ALLOCATION_ERROR; + return labelLength; + } + UErrorCode punycodeErrorCode=U_ZERO_ERROR; + int32_t unicodeLength=u_strFromPunycode(label+4, labelLength-4, + unicodeBuffer, fromPunycode.getCapacity(), + nullptr, &punycodeErrorCode); + if(punycodeErrorCode==U_BUFFER_OVERFLOW_ERROR) { + fromPunycode.releaseBuffer(0); + unicodeBuffer=fromPunycode.getBuffer(unicodeLength); + if(unicodeBuffer==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return labelLength; + } + punycodeErrorCode=U_ZERO_ERROR; + unicodeLength=u_strFromPunycode(label+4, labelLength-4, + unicodeBuffer, fromPunycode.getCapacity(), + nullptr, &punycodeErrorCode); + } + fromPunycode.releaseBuffer(unicodeLength); + if(U_FAILURE(punycodeErrorCode)) { + info.labelErrors|=UIDNA_ERROR_PUNYCODE; + return markBadACELabel(dest, labelStart, labelLength, toASCII, info, errorCode); + } + // Check for NFC, and for characters that are not + // valid or deviation characters according to the normalizer. + // If there is something wrong, then the string will change. + // Note that the normalizer passes through non-LDH ASCII and deviation characters. + // Deviation characters are ok in Punycode even in transitional processing. + // In the code further below, if we find non-LDH ASCII and we have UIDNA_USE_STD3_RULES + // then we will set UIDNA_ERROR_INVALID_ACE_LABEL there too. + UBool isValid=uts46Norm2.isNormalized(fromPunycode, errorCode); + if(U_FAILURE(errorCode)) { + return labelLength; + } + if(!isValid) { + info.labelErrors|=UIDNA_ERROR_INVALID_ACE_LABEL; + return markBadACELabel(dest, labelStart, labelLength, toASCII, info, errorCode); + } + labelString=&fromPunycode; + label=fromPunycode.getBuffer(); + labelStart=0; + labelLength=fromPunycode.length(); + } else { + wasPunycode=false; + labelString=&dest; + } + // Validity check + if(labelLength==0) { + info.labelErrors|=UIDNA_ERROR_EMPTY_LABEL; + return replaceLabel(dest, destLabelStart, destLabelLength, + *labelString, labelLength, errorCode); + } + // labelLength>0 + if(labelLength>=4 && label[2]==0x2d && label[3]==0x2d) { + // label starts with "??--" + info.labelErrors|=UIDNA_ERROR_HYPHEN_3_4; + } + if(label[0]==0x2d) { + // label starts with "-" + info.labelErrors|=UIDNA_ERROR_LEADING_HYPHEN; + } + if(label[labelLength-1]==0x2d) { + // label ends with "-" + info.labelErrors|=UIDNA_ERROR_TRAILING_HYPHEN; + } + // If the label was not a Punycode label, then it was the result of + // mapping, normalization and label segmentation. + // If the label was in Punycode, then we mapped it again above + // and checked its validity. + // Now we handle the STD3 restriction to LDH characters (if set) + // and we look for U+FFFD which indicates disallowed characters + // in a non-Punycode label or U+FFFD itself in a Punycode label. + // We also check for dots which can come from the input to a single-label function. + // Ok to cast away const because we own the UnicodeString. + char16_t *s=(char16_t *)label; + const char16_t *limit=label+labelLength; + char16_t oredChars=0; + // If we enforce STD3 rules, then ASCII characters other than LDH and dot are disallowed. + UBool disallowNonLDHDot=(options&UIDNA_USE_STD3_RULES)!=0; + do { + char16_t c=*s; + if(c<=0x7f) { + if(c==0x2e) { + info.labelErrors|=UIDNA_ERROR_LABEL_HAS_DOT; + *s=0xfffd; + } else if(disallowNonLDHDot && asciiData[c]<0) { + info.labelErrors|=UIDNA_ERROR_DISALLOWED; + *s=0xfffd; + } + } else { + oredChars|=c; + if(disallowNonLDHDot && isNonASCIIDisallowedSTD3Valid(c)) { + info.labelErrors|=UIDNA_ERROR_DISALLOWED; + *s=0xfffd; + } else if(c==0xfffd) { + info.labelErrors|=UIDNA_ERROR_DISALLOWED; + } + } + ++s; + } while(sreplace(labelStart, cpLength, (char16_t)0xfffd); + label=labelString->getBuffer()+labelStart; + labelLength+=1-cpLength; + if(labelString==&dest) { + destLabelLength=labelLength; + } + } + if((info.labelErrors&severeErrors)==0) { + // Do contextual checks only if we do not have U+FFFD from a severe error + // because U+FFFD can make these checks fail. + if((options&UIDNA_CHECK_BIDI)!=0 && (!info.isBiDi || info.isOkBiDi)) { + checkLabelBiDi(label, labelLength, info); + } + if( (options&UIDNA_CHECK_CONTEXTJ)!=0 && (oredChars&0x200c)==0x200c && + !isLabelOkContextJ(label, labelLength) + ) { + info.labelErrors|=UIDNA_ERROR_CONTEXTJ; + } + if((options&UIDNA_CHECK_CONTEXTO)!=0 && oredChars>=0xb7) { + checkLabelContextO(label, labelLength, info); + } + if(toASCII) { + if(wasPunycode) { + // Leave a Punycode label unchanged if it has no severe errors. + if(destLabelLength>63) { + info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; + } + return destLabelLength; + } else if(oredChars>=0x80) { + // Contains non-ASCII characters. + UnicodeString punycode; + char16_t *buffer=punycode.getBuffer(63); // 63==maximum DNS label length + if(buffer==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return destLabelLength; + } + buffer[0]=0x78; // Write "xn--". + buffer[1]=0x6e; + buffer[2]=0x2d; + buffer[3]=0x2d; + int32_t punycodeLength=u_strToPunycode(label, labelLength, + buffer+4, punycode.getCapacity()-4, + nullptr, &errorCode); + if(errorCode==U_BUFFER_OVERFLOW_ERROR) { + errorCode=U_ZERO_ERROR; + punycode.releaseBuffer(4); + buffer=punycode.getBuffer(4+punycodeLength); + if(buffer==nullptr) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return destLabelLength; + } + punycodeLength=u_strToPunycode(label, labelLength, + buffer+4, punycode.getCapacity()-4, + nullptr, &errorCode); + } + punycodeLength+=4; + punycode.releaseBuffer(punycodeLength); + if(U_FAILURE(errorCode)) { + return destLabelLength; + } + if(punycodeLength>63) { + info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; + } + return replaceLabel(dest, destLabelStart, destLabelLength, + punycode, punycodeLength, errorCode); + } else { + // all-ASCII label + if(labelLength>63) { + info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; + } + } + } + } else { + // If a Punycode label has severe errors, + // then leave it but make sure it does not look valid. + if(wasPunycode) { + info.labelErrors|=UIDNA_ERROR_INVALID_ACE_LABEL; + return markBadACELabel(dest, destLabelStart, destLabelLength, toASCII, info, errorCode); + } + } + return replaceLabel(dest, destLabelStart, destLabelLength, + *labelString, labelLength, errorCode); +} + +// Make sure an ACE label does not look valid. +// Append U+FFFD if the label has only LDH characters. +// If UIDNA_USE_STD3_RULES, also replace disallowed ASCII characters with U+FFFD. +int32_t +UTS46::markBadACELabel(UnicodeString &dest, + int32_t labelStart, int32_t labelLength, + UBool toASCII, IDNAInfo &info, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return 0; + } + UBool disallowNonLDHDot=(options&UIDNA_USE_STD3_RULES)!=0; + UBool isASCII=true; + UBool onlyLDH=true; + const char16_t *label=dest.getBuffer()+labelStart; + const char16_t *limit=label+labelLength; + // Start after the initial "xn--". + // Ok to cast away const because we own the UnicodeString. + for(char16_t *s=const_cast(label+4); s63) { + info.labelErrors|=UIDNA_ERROR_LABEL_TOO_LONG; + } + } + return labelLength; +} + +const uint32_t L_MASK=U_MASK(U_LEFT_TO_RIGHT); +const uint32_t R_AL_MASK=U_MASK(U_RIGHT_TO_LEFT)|U_MASK(U_RIGHT_TO_LEFT_ARABIC); +const uint32_t L_R_AL_MASK=L_MASK|R_AL_MASK; + +const uint32_t R_AL_AN_MASK=R_AL_MASK|U_MASK(U_ARABIC_NUMBER); + +const uint32_t EN_AN_MASK=U_MASK(U_EUROPEAN_NUMBER)|U_MASK(U_ARABIC_NUMBER); +const uint32_t R_AL_EN_AN_MASK=R_AL_MASK|EN_AN_MASK; +const uint32_t L_EN_MASK=L_MASK|U_MASK(U_EUROPEAN_NUMBER); + +const uint32_t ES_CS_ET_ON_BN_NSM_MASK= + U_MASK(U_EUROPEAN_NUMBER_SEPARATOR)| + U_MASK(U_COMMON_NUMBER_SEPARATOR)| + U_MASK(U_EUROPEAN_NUMBER_TERMINATOR)| + U_MASK(U_OTHER_NEUTRAL)| + U_MASK(U_BOUNDARY_NEUTRAL)| + U_MASK(U_DIR_NON_SPACING_MARK); +const uint32_t L_EN_ES_CS_ET_ON_BN_NSM_MASK=L_EN_MASK|ES_CS_ET_ON_BN_NSM_MASK; +const uint32_t R_AL_AN_EN_ES_CS_ET_ON_BN_NSM_MASK=R_AL_MASK|EN_AN_MASK|ES_CS_ET_ON_BN_NSM_MASK; + +// We scan the whole label and check both for whether it contains RTL characters +// and whether it passes the BiDi Rule. +// In a BiDi domain name, all labels must pass the BiDi Rule, but we might find +// that a domain name is a BiDi domain name (has an RTL label) only after +// processing several earlier labels. +void +UTS46::checkLabelBiDi(const char16_t *label, int32_t labelLength, IDNAInfo &info) const { + // IDNA2008 BiDi rule + // Get the directionality of the first character. + UChar32 c; + int32_t i=0; + U16_NEXT_UNSAFE(label, i, c); + uint32_t firstMask=U_MASK(u_charDirection(c)); + // 1. The first character must be a character with BIDI property L, R + // or AL. If it has the R or AL property, it is an RTL label; if it + // has the L property, it is an LTR label. + if((firstMask&~L_R_AL_MASK)!=0) { + info.isOkBiDi=false; + } + // Get the directionality of the last non-NSM character. + uint32_t lastMask; + for(;;) { + if(i>=labelLength) { + lastMask=firstMask; + break; + } + U16_PREV_UNSAFE(label, labelLength, c); + UCharDirection dir=u_charDirection(c); + if(dir!=U_DIR_NON_SPACING_MARK) { + lastMask=U_MASK(dir); + break; + } + } + // 3. In an RTL label, the end of the label must be a character with + // BIDI property R, AL, EN or AN, followed by zero or more + // characters with BIDI property NSM. + // 6. In an LTR label, the end of the label must be a character with + // BIDI property L or EN, followed by zero or more characters with + // BIDI property NSM. + if( (firstMask&L_MASK)!=0 ? + (lastMask&~L_EN_MASK)!=0 : + (lastMask&~R_AL_EN_AN_MASK)!=0 + ) { + info.isOkBiDi=false; + } + // Add the directionalities of the intervening characters. + uint32_t mask=firstMask|lastMask; + while(ilabelStart) { + c=s[i-1]; + if(!(0x61<=c && c<=0x7a) && !(0x30<=c && c<=0x39)) { + // Last character in the label is not an L or EN. + return false; + } + } + labelStart=i+1; + } else if(i==labelStart) { + if(!(0x61<=c && c<=0x7a)) { + // First character in the label is not an L. + return false; + } + } else { + if(c<=0x20 && (c>=0x1c || (9<=c && c<=0xd))) { + // Intermediate character in the label is a B, S or WS. + return false; + } + } + } + return true; +} + +// UTF-8 version, called for source ASCII prefix. +// Can contain uppercase A-Z. +// s[length-1] must be the trailing dot. +static UBool +isASCIIOkBiDi(const char *s, int32_t length) { + int32_t labelStart=0; + for(int32_t i=0; ilabelStart) { + c=s[i-1]; + if(!(0x61<=c && c<=0x7a) && !(0x41<=c && c<=0x5a) && !(0x30<=c && c<=0x39)) { + // Last character in the label is not an L or EN. + return false; + } + } + labelStart=i+1; + } else if(i==labelStart) { + if(!(0x61<=c && c<=0x7a) && !(0x41<=c && c<=0x5a)) { + // First character in the label is not an L. + return false; + } + } else { + if(c<=0x20 && (c>=0x1c || (9<=c && c<=0xd))) { + // Intermediate character in the label is a B, S or WS. + return false; + } + } + } + return true; +} + +UBool +UTS46::isLabelOkContextJ(const char16_t *label, int32_t labelLength) const { + // [IDNA2008-Tables] + // 200C..200D ; CONTEXTJ # ZERO WIDTH NON-JOINER..ZERO WIDTH JOINER + for(int32_t i=0; i0) { + info.labelErrors|=UIDNA_ERROR_CONTEXTO_DIGITS; + } + arabicDigits=-1; + } else if(0x6f0<=c) { + if(arabicDigits<0) { + info.labelErrors|=UIDNA_ERROR_CONTEXTO_DIGITS; + } + arabicDigits=1; + } + } + } else if(c==0x30fb) { + // Appendix A.7. KATAKANA MIDDLE DOT (U+30FB) + // Rule Set: + // False; + // For All Characters: + // If Script(cp) .in. {Hiragana, Katakana, Han} Then True; + // End For; + UErrorCode errorCode=U_ZERO_ERROR; + for(int j=0;;) { + if(j>labelEnd) { + info.labelErrors|=UIDNA_ERROR_CONTEXTO_PUNCTUATION; + break; + } + U16_NEXT(label, j, labelLength, c); + UScriptCode script=uscript_getScript(c, &errorCode); + if(script==USCRIPT_HIRAGANA || script==USCRIPT_KATAKANA || script==USCRIPT_HAN) { + break; + } + } + } + } +} + +U_NAMESPACE_END + +// C API ------------------------------------------------------------------- *** + +U_NAMESPACE_USE + +U_CAPI UIDNA * U_EXPORT2 +uidna_openUTS46(uint32_t options, UErrorCode *pErrorCode) { + return reinterpret_cast(IDNA::createUTS46Instance(options, *pErrorCode)); +} + +U_CAPI void U_EXPORT2 +uidna_close(UIDNA *idna) { + delete reinterpret_cast(idna); +} + +static UBool +checkArgs(const void *label, int32_t length, + void *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return false; + } + // sizeof(UIDNAInfo)=16 in the first API version. + if(pInfo==nullptr || pInfo->size<16) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + if( (label==nullptr ? length!=0 : length<-1) || + (dest==nullptr ? capacity!=0 : capacity<0) || + (dest==label && label!=nullptr) + ) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + // Set all *pInfo bytes to 0 except for the size field itself. + uprv_memset(&pInfo->size+1, 0, pInfo->size-sizeof(pInfo->size)); + return true; +} + +static void +idnaInfoToStruct(IDNAInfo &info, UIDNAInfo *pInfo) { + pInfo->isTransitionalDifferent=info.isTransitionalDifferent(); + pInfo->errors=info.getErrors(); +} + +U_CAPI int32_t U_EXPORT2 +uidna_labelToASCII(const UIDNA *idna, + const char16_t *label, int32_t length, + char16_t *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode) { + if(!checkArgs(label, length, dest, capacity, pInfo, pErrorCode)) { + return 0; + } + UnicodeString src((UBool)(length<0), label, length); + UnicodeString destString(dest, 0, capacity); + IDNAInfo info; + reinterpret_cast(idna)->labelToASCII(src, destString, info, *pErrorCode); + idnaInfoToStruct(info, pInfo); + return destString.extract(dest, capacity, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uidna_labelToUnicode(const UIDNA *idna, + const char16_t *label, int32_t length, + char16_t *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode) { + if(!checkArgs(label, length, dest, capacity, pInfo, pErrorCode)) { + return 0; + } + UnicodeString src((UBool)(length<0), label, length); + UnicodeString destString(dest, 0, capacity); + IDNAInfo info; + reinterpret_cast(idna)->labelToUnicode(src, destString, info, *pErrorCode); + idnaInfoToStruct(info, pInfo); + return destString.extract(dest, capacity, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uidna_nameToASCII(const UIDNA *idna, + const char16_t *name, int32_t length, + char16_t *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode) { + if(!checkArgs(name, length, dest, capacity, pInfo, pErrorCode)) { + return 0; + } + UnicodeString src((UBool)(length<0), name, length); + UnicodeString destString(dest, 0, capacity); + IDNAInfo info; + reinterpret_cast(idna)->nameToASCII(src, destString, info, *pErrorCode); + idnaInfoToStruct(info, pInfo); + return destString.extract(dest, capacity, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uidna_nameToUnicode(const UIDNA *idna, + const char16_t *name, int32_t length, + char16_t *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode) { + if(!checkArgs(name, length, dest, capacity, pInfo, pErrorCode)) { + return 0; + } + UnicodeString src((UBool)(length<0), name, length); + UnicodeString destString(dest, 0, capacity); + IDNAInfo info; + reinterpret_cast(idna)->nameToUnicode(src, destString, info, *pErrorCode); + idnaInfoToStruct(info, pInfo); + return destString.extract(dest, capacity, *pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uidna_labelToASCII_UTF8(const UIDNA *idna, + const char *label, int32_t length, + char *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode) { + if(!checkArgs(label, length, dest, capacity, pInfo, pErrorCode)) { + return 0; + } + StringPiece src(label, length<0 ? static_cast(uprv_strlen(label)) : length); + CheckedArrayByteSink sink(dest, capacity); + IDNAInfo info; + reinterpret_cast(idna)->labelToASCII_UTF8(src, sink, info, *pErrorCode); + idnaInfoToStruct(info, pInfo); + return u_terminateChars(dest, capacity, sink.NumberOfBytesAppended(), pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uidna_labelToUnicodeUTF8(const UIDNA *idna, + const char *label, int32_t length, + char *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode) { + if(!checkArgs(label, length, dest, capacity, pInfo, pErrorCode)) { + return 0; + } + StringPiece src(label, length<0 ? static_cast(uprv_strlen(label)) : length); + CheckedArrayByteSink sink(dest, capacity); + IDNAInfo info; + reinterpret_cast(idna)->labelToUnicodeUTF8(src, sink, info, *pErrorCode); + idnaInfoToStruct(info, pInfo); + return u_terminateChars(dest, capacity, sink.NumberOfBytesAppended(), pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uidna_nameToASCII_UTF8(const UIDNA *idna, + const char *name, int32_t length, + char *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode) { + if(!checkArgs(name, length, dest, capacity, pInfo, pErrorCode)) { + return 0; + } + StringPiece src(name, length<0 ? static_cast(uprv_strlen(name)) : length); + CheckedArrayByteSink sink(dest, capacity); + IDNAInfo info; + reinterpret_cast(idna)->nameToASCII_UTF8(src, sink, info, *pErrorCode); + idnaInfoToStruct(info, pInfo); + return u_terminateChars(dest, capacity, sink.NumberOfBytesAppended(), pErrorCode); +} + +U_CAPI int32_t U_EXPORT2 +uidna_nameToUnicodeUTF8(const UIDNA *idna, + const char *name, int32_t length, + char *dest, int32_t capacity, + UIDNAInfo *pInfo, UErrorCode *pErrorCode) { + if(!checkArgs(name, length, dest, capacity, pInfo, pErrorCode)) { + return 0; + } + StringPiece src(name, length<0 ? static_cast(uprv_strlen(name)) : length); + CheckedArrayByteSink sink(dest, capacity); + IDNAInfo info; + reinterpret_cast(idna)->nameToUnicodeUTF8(src, sink, info, *pErrorCode); + idnaInfoToStruct(info, pInfo); + return u_terminateChars(dest, capacity, sink.NumberOfBytesAppended(), pErrorCode); +} + +#endif // UCONFIG_NO_IDNA diff --git a/deps/icu-small/source/common/utypeinfo.h b/deps/icu-small/source/common/utypeinfo.h index c6663734fc3dc4..648f6cc4889c89 100644 --- a/deps/icu-small/source/common/utypeinfo.h +++ b/deps/icu-small/source/common/utypeinfo.h @@ -1,32 +1,32 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 2012-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -*/ - -#ifndef __UTYPEINFO_H__ -#define __UTYPEINFO_H__ - -// Windows header does not define 'exception' in 'std' namespace. -// Therefore, a project using ICU cannot be compiled with _HAS_EXCEPTIONS -// set to 0 on Windows with Visual Studio. To work around that, we have to -// include explicitly and add using statement below. -// Whenever 'typeid' is used, this header has to be included -// instead of . -// Visual Studio 10 emits warning 4275 with this change. If you compile -// with exception disabled, you have to suppress warning 4275. -#if defined(_MSC_VER) && _HAS_EXCEPTIONS == 0 -#include -using std::exception; -#endif -#if defined(__GLIBCXX__) -namespace std { class type_info; } // WORKAROUND: http://llvm.org/bugs/show_bug.cgi?id=13364 -#endif -#include // for 'typeid' to work - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* +* Copyright (C) 2012-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +*/ + +#ifndef __UTYPEINFO_H__ +#define __UTYPEINFO_H__ + +// Windows header does not define 'exception' in 'std' namespace. +// Therefore, a project using ICU cannot be compiled with _HAS_EXCEPTIONS +// set to 0 on Windows with Visual Studio. To work around that, we have to +// include explicitly and add using statement below. +// Whenever 'typeid' is used, this header has to be included +// instead of . +// Visual Studio 10 emits warning 4275 with this change. If you compile +// with exception disabled, you have to suppress warning 4275. +#if defined(_MSC_VER) && _HAS_EXCEPTIONS == 0 +#include +using std::exception; +#endif +#if defined(__GLIBCXX__) +namespace std { class type_info; } // WORKAROUND: http://llvm.org/bugs/show_bug.cgi?id=13364 +#endif +#include // for 'typeid' to work + +#endif diff --git a/deps/icu-small/source/common/utypes.cpp b/deps/icu-small/source/common/utypes.cpp index 63e05b1249b6ff..e515d71d5ef95d 100644 --- a/deps/icu-small/source/common/utypes.cpp +++ b/deps/icu-small/source/common/utypes.cpp @@ -1,227 +1,227 @@ -// © 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 NAME : utypes.c (previously putil.c) -* -* Date Name Description -* 10/07/2004 grhoten split from putil.c -****************************************************************************** -*/ - -#include "unicode/utypes.h" - -/* u_errorName() ------------------------------------------------------------ */ - -static const char * const -_uErrorInfoName[U_ERROR_WARNING_LIMIT-U_ERROR_WARNING_START]={ - "U_USING_FALLBACK_WARNING", - "U_USING_DEFAULT_WARNING", - "U_SAFECLONE_ALLOCATED_WARNING", - "U_STATE_OLD_WARNING", - "U_STRING_NOT_TERMINATED_WARNING", - "U_SORT_KEY_TOO_SHORT_WARNING", - "U_AMBIGUOUS_ALIAS_WARNING", - "U_DIFFERENT_UCA_VERSION", - "U_PLUGIN_CHANGED_LEVEL_WARNING", -}; - -static const char * const -_uTransErrorName[U_PARSE_ERROR_LIMIT - U_PARSE_ERROR_START]={ - "U_BAD_VARIABLE_DEFINITION", - "U_MALFORMED_RULE", - "U_MALFORMED_SET", - "U_MALFORMED_SYMBOL_REFERENCE", - "U_MALFORMED_UNICODE_ESCAPE", - "U_MALFORMED_VARIABLE_DEFINITION", - "U_MALFORMED_VARIABLE_REFERENCE", - "U_MISMATCHED_SEGMENT_DELIMITERS", - "U_MISPLACED_ANCHOR_START", - "U_MISPLACED_CURSOR_OFFSET", - "U_MISPLACED_QUANTIFIER", - "U_MISSING_OPERATOR", - "U_MISSING_SEGMENT_CLOSE", - "U_MULTIPLE_ANTE_CONTEXTS", - "U_MULTIPLE_CURSORS", - "U_MULTIPLE_POST_CONTEXTS", - "U_TRAILING_BACKSLASH", - "U_UNDEFINED_SEGMENT_REFERENCE", - "U_UNDEFINED_VARIABLE", - "U_UNQUOTED_SPECIAL", - "U_UNTERMINATED_QUOTE", - "U_RULE_MASK_ERROR", - "U_MISPLACED_COMPOUND_FILTER", - "U_MULTIPLE_COMPOUND_FILTERS", - "U_INVALID_RBT_SYNTAX", - "U_INVALID_PROPERTY_PATTERN", - "U_MALFORMED_PRAGMA", - "U_UNCLOSED_SEGMENT", - "U_ILLEGAL_CHAR_IN_SEGMENT", - "U_VARIABLE_RANGE_EXHAUSTED", - "U_VARIABLE_RANGE_OVERLAP", - "U_ILLEGAL_CHARACTER", - "U_INTERNAL_TRANSLITERATOR_ERROR", - "U_INVALID_ID", - "U_INVALID_FUNCTION" -}; - -static const char * const -_uErrorName[U_STANDARD_ERROR_LIMIT]={ - "U_ZERO_ERROR", - - "U_ILLEGAL_ARGUMENT_ERROR", - "U_MISSING_RESOURCE_ERROR", - "U_INVALID_FORMAT_ERROR", - "U_FILE_ACCESS_ERROR", - "U_INTERNAL_PROGRAM_ERROR", - "U_MESSAGE_PARSE_ERROR", - "U_MEMORY_ALLOCATION_ERROR", - "U_INDEX_OUTOFBOUNDS_ERROR", - "U_PARSE_ERROR", - "U_INVALID_CHAR_FOUND", - "U_TRUNCATED_CHAR_FOUND", - "U_ILLEGAL_CHAR_FOUND", - "U_INVALID_TABLE_FORMAT", - "U_INVALID_TABLE_FILE", - "U_BUFFER_OVERFLOW_ERROR", - "U_UNSUPPORTED_ERROR", - "U_RESOURCE_TYPE_MISMATCH", - "U_ILLEGAL_ESCAPE_SEQUENCE", - "U_UNSUPPORTED_ESCAPE_SEQUENCE", - "U_NO_SPACE_AVAILABLE", - "U_CE_NOT_FOUND_ERROR", - "U_PRIMARY_TOO_LONG_ERROR", - "U_STATE_TOO_OLD_ERROR", - "U_TOO_MANY_ALIASES_ERROR", - "U_ENUM_OUT_OF_SYNC_ERROR", - "U_INVARIANT_CONVERSION_ERROR", - "U_INVALID_STATE_ERROR", - "U_COLLATOR_VERSION_MISMATCH", - "U_USELESS_COLLATOR_ERROR", - "U_NO_WRITE_PERMISSION", - "U_INPUT_TOO_LONG_ERROR" -}; -static const char * const -_uFmtErrorName[U_FMT_PARSE_ERROR_LIMIT - U_FMT_PARSE_ERROR_START] = { - "U_UNEXPECTED_TOKEN", - "U_MULTIPLE_DECIMAL_SEPARATORS", - "U_MULTIPLE_EXPONENTIAL_SYMBOLS", - "U_MALFORMED_EXPONENTIAL_PATTERN", - "U_MULTIPLE_PERCENT_SYMBOLS", - "U_MULTIPLE_PERMILL_SYMBOLS", - "U_MULTIPLE_PAD_SPECIFIERS", - "U_PATTERN_SYNTAX_ERROR", - "U_ILLEGAL_PAD_POSITION", - "U_UNMATCHED_BRACES", - "U_UNSUPPORTED_PROPERTY", - "U_UNSUPPORTED_ATTRIBUTE", - "U_ARGUMENT_TYPE_MISMATCH", - "U_DUPLICATE_KEYWORD", - "U_UNDEFINED_KEYWORD", - "U_DEFAULT_KEYWORD_MISSING", - "U_DECIMAL_NUMBER_SYNTAX_ERROR", - "U_FORMAT_INEXACT_ERROR", - "U_NUMBER_ARG_OUTOFBOUNDS_ERROR", - "U_NUMBER_SKELETON_SYNTAX_ERROR", -}; - -static const char * const -_uBrkErrorName[U_BRK_ERROR_LIMIT - U_BRK_ERROR_START] = { - "U_BRK_INTERNAL_ERROR", - "U_BRK_HEX_DIGITS_EXPECTED", - "U_BRK_SEMICOLON_EXPECTED", - "U_BRK_RULE_SYNTAX", - "U_BRK_UNCLOSED_SET", - "U_BRK_ASSIGN_ERROR", - "U_BRK_VARIABLE_REDFINITION", - "U_BRK_MISMATCHED_PAREN", - "U_BRK_NEW_LINE_IN_QUOTED_STRING", - "U_BRK_UNDEFINED_VARIABLE", - "U_BRK_INIT_ERROR", - "U_BRK_RULE_EMPTY_SET", - "U_BRK_UNRECOGNIZED_OPTION", - "U_BRK_MALFORMED_RULE_TAG" -}; - -static const char * const -_uRegexErrorName[U_REGEX_ERROR_LIMIT - U_REGEX_ERROR_START] = { - "U_REGEX_INTERNAL_ERROR", - "U_REGEX_RULE_SYNTAX", - "U_REGEX_INVALID_STATE", - "U_REGEX_BAD_ESCAPE_SEQUENCE", - "U_REGEX_PROPERTY_SYNTAX", - "U_REGEX_UNIMPLEMENTED", - "U_REGEX_MISMATCHED_PAREN", - "U_REGEX_NUMBER_TOO_BIG", - "U_REGEX_BAD_INTERVAL", - "U_REGEX_MAX_LT_MIN", - "U_REGEX_INVALID_BACK_REF", - "U_REGEX_INVALID_FLAG", - "U_REGEX_LOOK_BEHIND_LIMIT", - "U_REGEX_SET_CONTAINS_STRING", - "U_REGEX_OCTAL_TOO_BIG", - "U_REGEX_MISSING_CLOSE_BRACKET", - "U_REGEX_INVALID_RANGE", - "U_REGEX_STACK_OVERFLOW", - "U_REGEX_TIME_OUT", - "U_REGEX_STOPPED_BY_CALLER", - "U_REGEX_PATTERN_TOO_BIG", - "U_REGEX_INVALID_CAPTURE_GROUP_NAME" -}; - -static const char * const -_uIDNAErrorName[U_IDNA_ERROR_LIMIT - U_IDNA_ERROR_START] = { - "U_STRINGPREP_PROHIBITED_ERROR", - "U_STRINGPREP_UNASSIGNED_ERROR", - "U_STRINGPREP_CHECK_BIDI_ERROR", - "U_IDNA_STD3_ASCII_RULES_ERROR", - "U_IDNA_ACE_PREFIX_ERROR", - "U_IDNA_VERIFICATION_ERROR", - "U_IDNA_LABEL_TOO_LONG_ERROR", - "U_IDNA_ZERO_LENGTH_LABEL_ERROR", - "U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR" -}; - -static const char * const -_uPluginErrorName[U_PLUGIN_ERROR_LIMIT - U_PLUGIN_ERROR_START] = { - "U_PLUGIN_TOO_HIGH", - "U_PLUGIN_DIDNT_SET_LEVEL", -}; - -U_CAPI const char * U_EXPORT2 -u_errorName(UErrorCode code) { - if(U_ZERO_ERROR <= code && code < U_STANDARD_ERROR_LIMIT) { - return _uErrorName[code]; - } else if(U_ERROR_WARNING_START <= code && code < U_ERROR_WARNING_LIMIT) { - return _uErrorInfoName[code - U_ERROR_WARNING_START]; - } else if(U_PARSE_ERROR_START <= code && code < U_PARSE_ERROR_LIMIT){ - return _uTransErrorName[code - U_PARSE_ERROR_START]; - } else if(U_FMT_PARSE_ERROR_START <= code && code < U_FMT_PARSE_ERROR_LIMIT){ - return _uFmtErrorName[code - U_FMT_PARSE_ERROR_START]; - } else if (U_BRK_ERROR_START <= code && code < U_BRK_ERROR_LIMIT){ - return _uBrkErrorName[code - U_BRK_ERROR_START]; - } else if (U_REGEX_ERROR_START <= code && code < U_REGEX_ERROR_LIMIT) { - return _uRegexErrorName[code - U_REGEX_ERROR_START]; - } else if(U_IDNA_ERROR_START <= code && code < U_IDNA_ERROR_LIMIT) { - return _uIDNAErrorName[code - U_IDNA_ERROR_START]; - } else if(U_PLUGIN_ERROR_START <= code && code < U_PLUGIN_ERROR_LIMIT) { - return _uPluginErrorName[code - U_PLUGIN_ERROR_START]; - } else { - return "[BOGUS UErrorCode]"; - } -} - -/* - * Hey, Emacs, please set the following: - * - * Local Variables: - * indent-tabs-mode: nil - * End: - * - */ +// © 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 NAME : utypes.c (previously putil.c) +* +* Date Name Description +* 10/07/2004 grhoten split from putil.c +****************************************************************************** +*/ + +#include "unicode/utypes.h" + +/* u_errorName() ------------------------------------------------------------ */ + +static const char * const +_uErrorInfoName[U_ERROR_WARNING_LIMIT-U_ERROR_WARNING_START]={ + "U_USING_FALLBACK_WARNING", + "U_USING_DEFAULT_WARNING", + "U_SAFECLONE_ALLOCATED_WARNING", + "U_STATE_OLD_WARNING", + "U_STRING_NOT_TERMINATED_WARNING", + "U_SORT_KEY_TOO_SHORT_WARNING", + "U_AMBIGUOUS_ALIAS_WARNING", + "U_DIFFERENT_UCA_VERSION", + "U_PLUGIN_CHANGED_LEVEL_WARNING", +}; + +static const char * const +_uTransErrorName[U_PARSE_ERROR_LIMIT - U_PARSE_ERROR_START]={ + "U_BAD_VARIABLE_DEFINITION", + "U_MALFORMED_RULE", + "U_MALFORMED_SET", + "U_MALFORMED_SYMBOL_REFERENCE", + "U_MALFORMED_UNICODE_ESCAPE", + "U_MALFORMED_VARIABLE_DEFINITION", + "U_MALFORMED_VARIABLE_REFERENCE", + "U_MISMATCHED_SEGMENT_DELIMITERS", + "U_MISPLACED_ANCHOR_START", + "U_MISPLACED_CURSOR_OFFSET", + "U_MISPLACED_QUANTIFIER", + "U_MISSING_OPERATOR", + "U_MISSING_SEGMENT_CLOSE", + "U_MULTIPLE_ANTE_CONTEXTS", + "U_MULTIPLE_CURSORS", + "U_MULTIPLE_POST_CONTEXTS", + "U_TRAILING_BACKSLASH", + "U_UNDEFINED_SEGMENT_REFERENCE", + "U_UNDEFINED_VARIABLE", + "U_UNQUOTED_SPECIAL", + "U_UNTERMINATED_QUOTE", + "U_RULE_MASK_ERROR", + "U_MISPLACED_COMPOUND_FILTER", + "U_MULTIPLE_COMPOUND_FILTERS", + "U_INVALID_RBT_SYNTAX", + "U_INVALID_PROPERTY_PATTERN", + "U_MALFORMED_PRAGMA", + "U_UNCLOSED_SEGMENT", + "U_ILLEGAL_CHAR_IN_SEGMENT", + "U_VARIABLE_RANGE_EXHAUSTED", + "U_VARIABLE_RANGE_OVERLAP", + "U_ILLEGAL_CHARACTER", + "U_INTERNAL_TRANSLITERATOR_ERROR", + "U_INVALID_ID", + "U_INVALID_FUNCTION" +}; + +static const char * const +_uErrorName[U_STANDARD_ERROR_LIMIT]={ + "U_ZERO_ERROR", + + "U_ILLEGAL_ARGUMENT_ERROR", + "U_MISSING_RESOURCE_ERROR", + "U_INVALID_FORMAT_ERROR", + "U_FILE_ACCESS_ERROR", + "U_INTERNAL_PROGRAM_ERROR", + "U_MESSAGE_PARSE_ERROR", + "U_MEMORY_ALLOCATION_ERROR", + "U_INDEX_OUTOFBOUNDS_ERROR", + "U_PARSE_ERROR", + "U_INVALID_CHAR_FOUND", + "U_TRUNCATED_CHAR_FOUND", + "U_ILLEGAL_CHAR_FOUND", + "U_INVALID_TABLE_FORMAT", + "U_INVALID_TABLE_FILE", + "U_BUFFER_OVERFLOW_ERROR", + "U_UNSUPPORTED_ERROR", + "U_RESOURCE_TYPE_MISMATCH", + "U_ILLEGAL_ESCAPE_SEQUENCE", + "U_UNSUPPORTED_ESCAPE_SEQUENCE", + "U_NO_SPACE_AVAILABLE", + "U_CE_NOT_FOUND_ERROR", + "U_PRIMARY_TOO_LONG_ERROR", + "U_STATE_TOO_OLD_ERROR", + "U_TOO_MANY_ALIASES_ERROR", + "U_ENUM_OUT_OF_SYNC_ERROR", + "U_INVARIANT_CONVERSION_ERROR", + "U_INVALID_STATE_ERROR", + "U_COLLATOR_VERSION_MISMATCH", + "U_USELESS_COLLATOR_ERROR", + "U_NO_WRITE_PERMISSION", + "U_INPUT_TOO_LONG_ERROR" +}; +static const char * const +_uFmtErrorName[U_FMT_PARSE_ERROR_LIMIT - U_FMT_PARSE_ERROR_START] = { + "U_UNEXPECTED_TOKEN", + "U_MULTIPLE_DECIMAL_SEPARATORS", + "U_MULTIPLE_EXPONENTIAL_SYMBOLS", + "U_MALFORMED_EXPONENTIAL_PATTERN", + "U_MULTIPLE_PERCENT_SYMBOLS", + "U_MULTIPLE_PERMILL_SYMBOLS", + "U_MULTIPLE_PAD_SPECIFIERS", + "U_PATTERN_SYNTAX_ERROR", + "U_ILLEGAL_PAD_POSITION", + "U_UNMATCHED_BRACES", + "U_UNSUPPORTED_PROPERTY", + "U_UNSUPPORTED_ATTRIBUTE", + "U_ARGUMENT_TYPE_MISMATCH", + "U_DUPLICATE_KEYWORD", + "U_UNDEFINED_KEYWORD", + "U_DEFAULT_KEYWORD_MISSING", + "U_DECIMAL_NUMBER_SYNTAX_ERROR", + "U_FORMAT_INEXACT_ERROR", + "U_NUMBER_ARG_OUTOFBOUNDS_ERROR", + "U_NUMBER_SKELETON_SYNTAX_ERROR", +}; + +static const char * const +_uBrkErrorName[U_BRK_ERROR_LIMIT - U_BRK_ERROR_START] = { + "U_BRK_INTERNAL_ERROR", + "U_BRK_HEX_DIGITS_EXPECTED", + "U_BRK_SEMICOLON_EXPECTED", + "U_BRK_RULE_SYNTAX", + "U_BRK_UNCLOSED_SET", + "U_BRK_ASSIGN_ERROR", + "U_BRK_VARIABLE_REDFINITION", + "U_BRK_MISMATCHED_PAREN", + "U_BRK_NEW_LINE_IN_QUOTED_STRING", + "U_BRK_UNDEFINED_VARIABLE", + "U_BRK_INIT_ERROR", + "U_BRK_RULE_EMPTY_SET", + "U_BRK_UNRECOGNIZED_OPTION", + "U_BRK_MALFORMED_RULE_TAG" +}; + +static const char * const +_uRegexErrorName[U_REGEX_ERROR_LIMIT - U_REGEX_ERROR_START] = { + "U_REGEX_INTERNAL_ERROR", + "U_REGEX_RULE_SYNTAX", + "U_REGEX_INVALID_STATE", + "U_REGEX_BAD_ESCAPE_SEQUENCE", + "U_REGEX_PROPERTY_SYNTAX", + "U_REGEX_UNIMPLEMENTED", + "U_REGEX_MISMATCHED_PAREN", + "U_REGEX_NUMBER_TOO_BIG", + "U_REGEX_BAD_INTERVAL", + "U_REGEX_MAX_LT_MIN", + "U_REGEX_INVALID_BACK_REF", + "U_REGEX_INVALID_FLAG", + "U_REGEX_LOOK_BEHIND_LIMIT", + "U_REGEX_SET_CONTAINS_STRING", + "U_REGEX_OCTAL_TOO_BIG", + "U_REGEX_MISSING_CLOSE_BRACKET", + "U_REGEX_INVALID_RANGE", + "U_REGEX_STACK_OVERFLOW", + "U_REGEX_TIME_OUT", + "U_REGEX_STOPPED_BY_CALLER", + "U_REGEX_PATTERN_TOO_BIG", + "U_REGEX_INVALID_CAPTURE_GROUP_NAME" +}; + +static const char * const +_uIDNAErrorName[U_IDNA_ERROR_LIMIT - U_IDNA_ERROR_START] = { + "U_STRINGPREP_PROHIBITED_ERROR", + "U_STRINGPREP_UNASSIGNED_ERROR", + "U_STRINGPREP_CHECK_BIDI_ERROR", + "U_IDNA_STD3_ASCII_RULES_ERROR", + "U_IDNA_ACE_PREFIX_ERROR", + "U_IDNA_VERIFICATION_ERROR", + "U_IDNA_LABEL_TOO_LONG_ERROR", + "U_IDNA_ZERO_LENGTH_LABEL_ERROR", + "U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR" +}; + +static const char * const +_uPluginErrorName[U_PLUGIN_ERROR_LIMIT - U_PLUGIN_ERROR_START] = { + "U_PLUGIN_TOO_HIGH", + "U_PLUGIN_DIDNT_SET_LEVEL", +}; + +U_CAPI const char * U_EXPORT2 +u_errorName(UErrorCode code) { + if(U_ZERO_ERROR <= code && code < U_STANDARD_ERROR_LIMIT) { + return _uErrorName[code]; + } else if(U_ERROR_WARNING_START <= code && code < U_ERROR_WARNING_LIMIT) { + return _uErrorInfoName[code - U_ERROR_WARNING_START]; + } else if(U_PARSE_ERROR_START <= code && code < U_PARSE_ERROR_LIMIT){ + return _uTransErrorName[code - U_PARSE_ERROR_START]; + } else if(U_FMT_PARSE_ERROR_START <= code && code < U_FMT_PARSE_ERROR_LIMIT){ + return _uFmtErrorName[code - U_FMT_PARSE_ERROR_START]; + } else if (U_BRK_ERROR_START <= code && code < U_BRK_ERROR_LIMIT){ + return _uBrkErrorName[code - U_BRK_ERROR_START]; + } else if (U_REGEX_ERROR_START <= code && code < U_REGEX_ERROR_LIMIT) { + return _uRegexErrorName[code - U_REGEX_ERROR_START]; + } else if(U_IDNA_ERROR_START <= code && code < U_IDNA_ERROR_LIMIT) { + return _uIDNAErrorName[code - U_IDNA_ERROR_START]; + } else if(U_PLUGIN_ERROR_START <= code && code < U_PLUGIN_ERROR_LIMIT) { + return _uPluginErrorName[code - U_PLUGIN_ERROR_START]; + } else { + return "[BOGUS UErrorCode]"; + } +} + +/* + * Hey, Emacs, please set the following: + * + * Local Variables: + * indent-tabs-mode: nil + * End: + * + */ diff --git a/deps/icu-small/source/common/uvector.cpp b/deps/icu-small/source/common/uvector.cpp index 729314ae95dcff..aa134e5ae3c74b 100644 --- a/deps/icu-small/source/common/uvector.cpp +++ b/deps/icu-small/source/common/uvector.cpp @@ -1,566 +1,566 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 1999-2013, International Business Machines Corporation and -* others. All Rights Reserved. -****************************************************************************** -* Date Name Description -* 10/22/99 alan Creation. -********************************************************************** -*/ - -#include "uvector.h" -#include "cmemory.h" -#include "uarrsort.h" -#include "uelement.h" - -U_NAMESPACE_BEGIN - -constexpr int32_t DEFAULT_CAPACITY = 8; - -/* - * Constants for hinting whether a key is an integer - * or a pointer. If a hint bit is zero, then the associated - * token is assumed to be an integer. This is needed for iSeries - */ -constexpr int8_t HINT_KEY_POINTER = 1; -constexpr int8_t HINT_KEY_INTEGER = 0; - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UVector) - -UVector::UVector(UErrorCode &status) : - UVector(nullptr, nullptr, DEFAULT_CAPACITY, status) { -} - -UVector::UVector(int32_t initialCapacity, UErrorCode &status) : - UVector(nullptr, nullptr, initialCapacity, status) { -} - -UVector::UVector(UObjectDeleter *d, UElementsAreEqual *c, UErrorCode &status) : - UVector(d, c, DEFAULT_CAPACITY, status) { -} - -UVector::UVector(UObjectDeleter *d, UElementsAreEqual *c, int32_t initialCapacity, UErrorCode &status) : - deleter(d), - comparer(c) -{ - if (U_FAILURE(status)) { - return; - } - // Fix bogus initialCapacity values; avoid malloc(0) and integer overflow - if ((initialCapacity < 1) || (initialCapacity > (int32_t)(INT32_MAX / sizeof(UElement)))) { - initialCapacity = DEFAULT_CAPACITY; - } - elements = (UElement *)uprv_malloc(sizeof(UElement)*initialCapacity); - if (elements == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - capacity = initialCapacity; - } -} - -UVector::~UVector() { - removeAllElements(); - uprv_free(elements); - elements = nullptr; -} - -/** - * Assign this object to another (make this a copy of 'other'). - * Use the 'assign' function to assign each element. - */ -void UVector::assign(const UVector& other, UElementAssigner *assign, UErrorCode &ec) { - if (ensureCapacity(other.count, ec)) { - setSize(other.count, ec); - if (U_SUCCESS(ec)) { - for (int32_t i=0; iindex; --i) { - elements[i] = elements[i-1]; - } - elements[index].pointer = obj; - ++count; - } else { - /* index out of range */ - status = U_ILLEGAL_ARGUMENT_ERROR; - } - } - if (U_FAILURE(status) && deleter != nullptr) { - (*deleter)(obj); - } -} - -void UVector::insertElementAt(int32_t elem, int32_t index, UErrorCode &status) { - U_ASSERT(deleter == nullptr); // Usage error. Mixing up ints and pointers. - // must have 0 <= index <= count - if (ensureCapacity(count + 1, status)) { - if (0 <= index && index <= count) { - for (int32_t i=count; i>index; --i) { - elements[i] = elements[i-1]; - } - elements[index].pointer = nullptr; - elements[index].integer = elem; - ++count; - } else { - /* index out of range */ - status = U_ILLEGAL_ARGUMENT_ERROR; - } - } -} - -void* UVector::elementAt(int32_t index) const { - return (0 <= index && index < count) ? elements[index].pointer : 0; -} - -int32_t UVector::elementAti(int32_t index) const { - return (0 <= index && index < count) ? elements[index].integer : 0; -} - -UBool UVector::containsAll(const UVector& other) const { - for (int32_t i=0; i= 0) { - return false; - } - } - return true; -} - -UBool UVector::removeAll(const UVector& other) { - UBool changed = false; - for (int32_t i=0; i= 0) { - removeElementAt(j); - changed = true; - } - } - return changed; -} - -UBool UVector::retainAll(const UVector& other) { - UBool changed = false; - for (int32_t j=size()-1; j>=0; --j) { - int32_t i = other.indexOf(elements[j]); - if (i < 0) { - removeElementAt(j); - changed = true; - } - } - return changed; -} - -void UVector::removeElementAt(int32_t index) { - void* e = orphanElementAt(index); - if (e != nullptr && deleter != nullptr) { - (*deleter)(e); - } -} - -UBool UVector::removeElement(void* obj) { - int32_t i = indexOf(obj); - if (i >= 0) { - removeElementAt(i); - return true; - } - return false; -} - -void UVector::removeAllElements(void) { - if (deleter != nullptr) { - for (int32_t i=0; icount != other.count) { - return false; - } - if (comparer == nullptr) { - for (i=0; i (INT32_MAX - 1) / 2) { // integer overflow check - status = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - int32_t newCap = capacity * 2; - if (newCap < minimumCapacity) { - newCap = minimumCapacity; - } - if (newCap > (int32_t)(INT32_MAX / sizeof(UElement))) { // integer overflow check - // We keep the original memory contents on bad minimumCapacity. - status = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - UElement* newElems = (UElement *)uprv_realloc(elements, sizeof(UElement)*newCap); - if (newElems == nullptr) { - // We keep the original contents on the memory failure on realloc or bad minimumCapacity. - status = U_MEMORY_ALLOCATION_ERROR; - return false; - } - elements = newElems; - capacity = newCap; - } - return true; -} - -/** - * Change the size of this vector as follows: If newSize is smaller, - * then truncate the array, possibly deleting held elements for i >= - * newSize. If newSize is larger, grow the array, filling in new - * slots with nullptr. - */ -void UVector::setSize(int32_t newSize, UErrorCode &status) { - if (!ensureCapacity(newSize, status)) { - return; - } - if (newSize > count) { - UElement empty; - empty.pointer = nullptr; - empty.integer = 0; - for (int32_t i=count; i=newSize; --i) { - removeElementAt(i); - } - } - count = newSize; -} - -/** - * Fill in the given array with all elements of this vector. - */ -void** UVector::toArray(void** result) const { - void** a = result; - for (int i=0; i 0) { - max = probe; - } else { - // assert(c <= 0); - min = probe + 1; - } - } - for (int32_t i=count; i>min; --i) { - elements[i] = elements[i-1]; - } - elements[min] = e; - ++count; -} - -/** - * Array sort comparator function. - * Used from UVector::sort() - * Conforms to function signature required for uprv_sortArray(). - * This function is essentially just a wrapper, to make a - * UVector style comparator function usable with uprv_sortArray(). - * - * The context pointer to this function is a pointer back - * (with some extra indirection) to the user supplied comparator. - * - */ -static int32_t U_CALLCONV -sortComparator(const void *context, const void *left, const void *right) { - UElementComparator *compare = *static_cast(context); - UElement e1 = *static_cast(left); - UElement e2 = *static_cast(right); - int32_t result = (*compare)(e1, e2); - return result; -} - - -/** - * Array sort comparison function for use from UVector::sorti() - * Compares int32_t vector elements. - */ -static int32_t U_CALLCONV -sortiComparator(const void * /*context */, const void *left, const void *right) { - const UElement *e1 = static_cast(left); - const UElement *e2 = static_cast(right); - int32_t result = e1->integer < e2->integer? -1 : - e1->integer == e2->integer? 0 : 1; - return result; -} - -/** - * Sort the vector, assuming it contains ints. - * (A more general sort would take a comparison function, but it's - * not clear whether UVector's UElementComparator or - * UComparator from uprv_sortAray would be more appropriate.) - */ -void UVector::sorti(UErrorCode &ec) { - if (U_SUCCESS(ec)) { - uprv_sortArray(elements, count, sizeof(UElement), - sortiComparator, nullptr, false, &ec); - } -} - - -/** - * Sort with a user supplied comparator. - * - * The comparator function handling is confusing because the function type - * for UVector (as defined for sortedInsert()) is different from the signature - * required by uprv_sortArray(). This is handled by passing the - * the UVector sort function pointer via the context pointer to a - * sortArray() comparator function, which can then call back to - * the original user function. - * - * An additional twist is that it's not safe to pass a pointer-to-function - * as a (void *) data pointer, so instead we pass a (data) pointer to a - * pointer-to-function variable. - */ -void UVector::sort(UElementComparator *compare, UErrorCode &ec) { - if (U_SUCCESS(ec)) { - uprv_sortArray(elements, count, sizeof(UElement), - sortComparator, &compare, false, &ec); - } -} - - -/** - * Stable sort with a user supplied comparator of type UComparator. - */ -void UVector::sortWithUComparator(UComparator *compare, const void *context, UErrorCode &ec) { - if (U_SUCCESS(ec)) { - uprv_sortArray(elements, count, sizeof(UElement), - compare, context, true, &ec); - } -} - -U_NAMESPACE_END - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 1999-2013, International Business Machines Corporation and +* others. All Rights Reserved. +****************************************************************************** +* Date Name Description +* 10/22/99 alan Creation. +********************************************************************** +*/ + +#include "uvector.h" +#include "cmemory.h" +#include "uarrsort.h" +#include "uelement.h" + +U_NAMESPACE_BEGIN + +constexpr int32_t DEFAULT_CAPACITY = 8; + +/* + * Constants for hinting whether a key is an integer + * or a pointer. If a hint bit is zero, then the associated + * token is assumed to be an integer. This is needed for iSeries + */ +constexpr int8_t HINT_KEY_POINTER = 1; +constexpr int8_t HINT_KEY_INTEGER = 0; + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UVector) + +UVector::UVector(UErrorCode &status) : + UVector(nullptr, nullptr, DEFAULT_CAPACITY, status) { +} + +UVector::UVector(int32_t initialCapacity, UErrorCode &status) : + UVector(nullptr, nullptr, initialCapacity, status) { +} + +UVector::UVector(UObjectDeleter *d, UElementsAreEqual *c, UErrorCode &status) : + UVector(d, c, DEFAULT_CAPACITY, status) { +} + +UVector::UVector(UObjectDeleter *d, UElementsAreEqual *c, int32_t initialCapacity, UErrorCode &status) : + deleter(d), + comparer(c) +{ + if (U_FAILURE(status)) { + return; + } + // Fix bogus initialCapacity values; avoid malloc(0) and integer overflow + if ((initialCapacity < 1) || (initialCapacity > (int32_t)(INT32_MAX / sizeof(UElement)))) { + initialCapacity = DEFAULT_CAPACITY; + } + elements = (UElement *)uprv_malloc(sizeof(UElement)*initialCapacity); + if (elements == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + capacity = initialCapacity; + } +} + +UVector::~UVector() { + removeAllElements(); + uprv_free(elements); + elements = nullptr; +} + +/** + * Assign this object to another (make this a copy of 'other'). + * Use the 'assign' function to assign each element. + */ +void UVector::assign(const UVector& other, UElementAssigner *assign, UErrorCode &ec) { + if (ensureCapacity(other.count, ec)) { + setSize(other.count, ec); + if (U_SUCCESS(ec)) { + for (int32_t i=0; iindex; --i) { + elements[i] = elements[i-1]; + } + elements[index].pointer = obj; + ++count; + } else { + /* index out of range */ + status = U_ILLEGAL_ARGUMENT_ERROR; + } + } + if (U_FAILURE(status) && deleter != nullptr) { + (*deleter)(obj); + } +} + +void UVector::insertElementAt(int32_t elem, int32_t index, UErrorCode &status) { + U_ASSERT(deleter == nullptr); // Usage error. Mixing up ints and pointers. + // must have 0 <= index <= count + if (ensureCapacity(count + 1, status)) { + if (0 <= index && index <= count) { + for (int32_t i=count; i>index; --i) { + elements[i] = elements[i-1]; + } + elements[index].pointer = nullptr; + elements[index].integer = elem; + ++count; + } else { + /* index out of range */ + status = U_ILLEGAL_ARGUMENT_ERROR; + } + } +} + +void* UVector::elementAt(int32_t index) const { + return (0 <= index && index < count) ? elements[index].pointer : 0; +} + +int32_t UVector::elementAti(int32_t index) const { + return (0 <= index && index < count) ? elements[index].integer : 0; +} + +UBool UVector::containsAll(const UVector& other) const { + for (int32_t i=0; i= 0) { + return false; + } + } + return true; +} + +UBool UVector::removeAll(const UVector& other) { + UBool changed = false; + for (int32_t i=0; i= 0) { + removeElementAt(j); + changed = true; + } + } + return changed; +} + +UBool UVector::retainAll(const UVector& other) { + UBool changed = false; + for (int32_t j=size()-1; j>=0; --j) { + int32_t i = other.indexOf(elements[j]); + if (i < 0) { + removeElementAt(j); + changed = true; + } + } + return changed; +} + +void UVector::removeElementAt(int32_t index) { + void* e = orphanElementAt(index); + if (e != nullptr && deleter != nullptr) { + (*deleter)(e); + } +} + +UBool UVector::removeElement(void* obj) { + int32_t i = indexOf(obj); + if (i >= 0) { + removeElementAt(i); + return true; + } + return false; +} + +void UVector::removeAllElements() { + if (deleter != nullptr) { + for (int32_t i=0; icount != other.count) { + return false; + } + if (comparer == nullptr) { + for (i=0; i (INT32_MAX - 1) / 2) { // integer overflow check + status = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + int32_t newCap = capacity * 2; + if (newCap < minimumCapacity) { + newCap = minimumCapacity; + } + if (newCap > (int32_t)(INT32_MAX / sizeof(UElement))) { // integer overflow check + // We keep the original memory contents on bad minimumCapacity. + status = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + UElement* newElems = (UElement *)uprv_realloc(elements, sizeof(UElement)*newCap); + if (newElems == nullptr) { + // We keep the original contents on the memory failure on realloc or bad minimumCapacity. + status = U_MEMORY_ALLOCATION_ERROR; + return false; + } + elements = newElems; + capacity = newCap; + } + return true; +} + +/** + * Change the size of this vector as follows: If newSize is smaller, + * then truncate the array, possibly deleting held elements for i >= + * newSize. If newSize is larger, grow the array, filling in new + * slots with nullptr. + */ +void UVector::setSize(int32_t newSize, UErrorCode &status) { + if (!ensureCapacity(newSize, status)) { + return; + } + if (newSize > count) { + UElement empty; + empty.pointer = nullptr; + empty.integer = 0; + for (int32_t i=count; i=newSize; --i) { + removeElementAt(i); + } + } + count = newSize; +} + +/** + * Fill in the given array with all elements of this vector. + */ +void** UVector::toArray(void** result) const { + void** a = result; + for (int i=0; i 0) { + max = probe; + } else { + // assert(c <= 0); + min = probe + 1; + } + } + for (int32_t i=count; i>min; --i) { + elements[i] = elements[i-1]; + } + elements[min] = e; + ++count; +} + +/** + * Array sort comparator function. + * Used from UVector::sort() + * Conforms to function signature required for uprv_sortArray(). + * This function is essentially just a wrapper, to make a + * UVector style comparator function usable with uprv_sortArray(). + * + * The context pointer to this function is a pointer back + * (with some extra indirection) to the user supplied comparator. + * + */ +static int32_t U_CALLCONV +sortComparator(const void *context, const void *left, const void *right) { + UElementComparator *compare = *static_cast(context); + UElement e1 = *static_cast(left); + UElement e2 = *static_cast(right); + int32_t result = (*compare)(e1, e2); + return result; +} + + +/** + * Array sort comparison function for use from UVector::sorti() + * Compares int32_t vector elements. + */ +static int32_t U_CALLCONV +sortiComparator(const void * /*context */, const void *left, const void *right) { + const UElement *e1 = static_cast(left); + const UElement *e2 = static_cast(right); + int32_t result = e1->integer < e2->integer? -1 : + e1->integer == e2->integer? 0 : 1; + return result; +} + +/** + * Sort the vector, assuming it contains ints. + * (A more general sort would take a comparison function, but it's + * not clear whether UVector's UElementComparator or + * UComparator from uprv_sortAray would be more appropriate.) + */ +void UVector::sorti(UErrorCode &ec) { + if (U_SUCCESS(ec)) { + uprv_sortArray(elements, count, sizeof(UElement), + sortiComparator, nullptr, false, &ec); + } +} + + +/** + * Sort with a user supplied comparator. + * + * The comparator function handling is confusing because the function type + * for UVector (as defined for sortedInsert()) is different from the signature + * required by uprv_sortArray(). This is handled by passing the + * the UVector sort function pointer via the context pointer to a + * sortArray() comparator function, which can then call back to + * the original user function. + * + * An additional twist is that it's not safe to pass a pointer-to-function + * as a (void *) data pointer, so instead we pass a (data) pointer to a + * pointer-to-function variable. + */ +void UVector::sort(UElementComparator *compare, UErrorCode &ec) { + if (U_SUCCESS(ec)) { + uprv_sortArray(elements, count, sizeof(UElement), + sortComparator, &compare, false, &ec); + } +} + + +/** + * Stable sort with a user supplied comparator of type UComparator. + */ +void UVector::sortWithUComparator(UComparator *compare, const void *context, UErrorCode &ec) { + if (U_SUCCESS(ec)) { + uprv_sortArray(elements, count, sizeof(UElement), + compare, context, true, &ec); + } +} + +U_NAMESPACE_END + diff --git a/deps/icu-small/source/common/uvector.h b/deps/icu-small/source/common/uvector.h index 1eb7d136e7ab82..40d8f8cf687537 100644 --- a/deps/icu-small/source/common/uvector.h +++ b/deps/icu-small/source/common/uvector.h @@ -1,386 +1,386 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 10/22/99 alan Creation. This is an internal header. -* It should not be exported. -********************************************************************** -*/ - -#ifndef UVECTOR_H -#define UVECTOR_H - -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "cmemory.h" -#include "uarrsort.h" -#include "uelement.h" - -U_NAMESPACE_BEGIN - -/** - * Ultralightweight C++ implementation of a `void*` vector - * that is (mostly) compatible with java.util.Vector. - * - * This is a very simple implementation, written to satisfy an - * immediate porting need. As such, it is not completely fleshed out, - * and it aims for simplicity and conformity. Nonetheless, it serves - * its purpose (porting code from java that uses java.util.Vector) - * well, and it could be easily made into a more robust vector class. - * - * *Design notes* - * - * There is index bounds checking, but little is done about it. If - * indices are out of bounds, either nothing happens, or zero is - * returned. We *do* avoid indexing off into the weeds. - * - * Since we don't have garbage collection, UVector was given the - * option to *own* its contents. To employ this, set a deleter - * function. The deleter is called on a `void *` pointer when that - * pointer is released by the vector, either when the vector itself is - * destructed, or when a call to `setElementAt()` overwrites an element, - * or when a call to remove()` or one of its variants explicitly - * removes an element. If no deleter is set, or the deleter is set to - * zero, then it is assumed that the caller will delete elements as - * needed. - * - * *Error Handling* Functions that can fail, from out of memory conditions - * for example, include a UErrorCode parameter. Any function called - * with an error code already indicating a failure will not modify the - * vector in any way. - * - * For vectors that have a deleter function, any failure in inserting - * an element into the vector will instead delete the element that - * could not be adopted. This simplifies object ownership - * management around calls to `addElement()` and `insertElementAt()`; - * error or no, the function always takes ownership of an incoming object - * from the caller. - * - * In order to implement methods such as `contains()` and `indexOf()`, - * UVector needs a way to compare objects for equality. To do so, it - * uses a comparison function, or "comparer." If the comparer is not - * set, or is set to zero, then all such methods will act as if the - * vector contains no element. That is, indexOf() will always return - * -1, contains() will always return false, etc. - * - *

To do - * - *

Improve the handling of index out of bounds errors. - * - * @author Alan Liu - */ -class U_COMMON_API UVector : public UObject { - // NOTE: UVector uses the UElement (union of void* and int32_t) as - // its basic storage type. It uses UElementsAreEqual as its - // comparison function. It uses UObjectDeleter as its deleter - // function. This allows sharing of support functions with UHashtable. - -private: - int32_t count = 0; - - int32_t capacity = 0; - - UElement* elements = nullptr; - - UObjectDeleter *deleter = nullptr; - - UElementsAreEqual *comparer = nullptr; - -public: - UVector(UErrorCode &status); - - UVector(int32_t initialCapacity, UErrorCode &status); - - UVector(UObjectDeleter *d, UElementsAreEqual *c, UErrorCode &status); - - UVector(UObjectDeleter *d, UElementsAreEqual *c, int32_t initialCapacity, UErrorCode &status); - - virtual ~UVector(); - - /** - * Assign this object to another (make this a copy of 'other'). - * Use the 'assign' function to assign each element. - */ - void assign(const UVector& other, UElementAssigner *assign, UErrorCode &ec); - - /** - * Compare this vector with another. They will be considered - * equal if they are of the same size and all elements are equal, - * as compared using this object's comparer. - */ - bool operator==(const UVector& other) const; - - /** - * Equivalent to !operator==() - */ - inline bool operator!=(const UVector& other) const {return !operator==(other);} - - //------------------------------------------------------------ - // java.util.Vector API - //------------------------------------------------------------ - - /** - * Add an element at the end of the vector. - * For use only with vectors that do not adopt their elements, which is to say, - * have not set an element deleter function. See `adoptElement()`. - */ - void addElement(void *obj, UErrorCode &status); - - /** - * Add an element at the end of the vector. - * For use only with vectors that adopt their elements, which is to say, - * have set an element deleter function. See `addElement()`. - * - * If the element cannot be successfully added, it will be deleted. This is - * normal ICU _adopt_ behavior - one way or another ownership of the incoming - * object is transferred from the caller. - * - * `addElement()` and `adoptElement()` are separate functions to make it easier - * to see what the function is doing at call sites. Having a single combined function, - * as in earlier versions of UVector, had proved to be error-prone. - */ - void adoptElement(void *obj, UErrorCode &status); - - void addElement(int32_t elem, UErrorCode &status); - - void setElementAt(void* obj, int32_t index); - - void setElementAt(int32_t elem, int32_t index); - - void insertElementAt(void* obj, int32_t index, UErrorCode &status); - - void insertElementAt(int32_t elem, int32_t index, UErrorCode &status); - - void* elementAt(int32_t index) const; - - int32_t elementAti(int32_t index) const; - - UBool equals(const UVector &other) const; - - inline void* firstElement(void) const {return elementAt(0);} - - inline void* lastElement(void) const {return elementAt(count-1);} - - inline int32_t lastElementi(void) const {return elementAti(count-1);} - - int32_t indexOf(void* obj, int32_t startIndex = 0) const; - - int32_t indexOf(int32_t obj, int32_t startIndex = 0) const; - - inline UBool contains(void* obj) const {return indexOf(obj) >= 0;} - - inline UBool contains(int32_t obj) const {return indexOf(obj) >= 0;} - - UBool containsAll(const UVector& other) const; - - UBool removeAll(const UVector& other); - - UBool retainAll(const UVector& other); - - void removeElementAt(int32_t index); - - UBool removeElement(void* obj); - - void removeAllElements(); - - inline int32_t size(void) const {return count;} - - inline UBool isEmpty(void) const {return count == 0;} - - UBool ensureCapacity(int32_t minimumCapacity, UErrorCode &status); - - /** - * Change the size of this vector as follows: If newSize is - * smaller, then truncate the array, possibly deleting held - * elements for i >= newSize. If newSize is larger, grow the - * array, filling in new slots with NULL. - */ - void setSize(int32_t newSize, UErrorCode &status); - - /** - * Fill in the given array with all elements of this vector. - */ - void** toArray(void** result) const; - - //------------------------------------------------------------ - // New API - //------------------------------------------------------------ - - UObjectDeleter *setDeleter(UObjectDeleter *d); - bool hasDeleter() {return deleter != nullptr;} - - UElementsAreEqual *setComparer(UElementsAreEqual *c); - - inline void* operator[](int32_t index) const {return elementAt(index);} - - /** - * Removes the element at the given index from this vector and - * transfer ownership of it to the caller. After this call, the - * caller owns the result and must delete it and the vector entry - * at 'index' is removed, shifting all subsequent entries back by - * one index and shortening the size of the vector by one. If the - * index is out of range or if there is no item at the given index - * then 0 is returned and the vector is unchanged. - */ - void* orphanElementAt(int32_t index); - - /** - * Returns true if this vector contains none of the elements - * of the given vector. - * @param other vector to be checked for containment - * @return true if the test condition is met - */ - UBool containsNone(const UVector& other) const; - - /** - * Insert the given object into this vector at its sorted position - * as defined by 'compare'. The current elements are assumed to - * be sorted already. - */ - void sortedInsert(void* obj, UElementComparator *compare, UErrorCode& ec); - - /** - * Insert the given integer into this vector at its sorted position - * as defined by 'compare'. The current elements are assumed to - * be sorted already. - */ - void sortedInsert(int32_t obj, UElementComparator *compare, UErrorCode& ec); - - /** - * Sort the contents of the vector, assuming that the contents of the - * vector are of type int32_t. - */ - void sorti(UErrorCode &ec); - - /** - * Sort the contents of this vector, using a caller-supplied function - * to do the comparisons. (It's confusing that - * UVector's UElementComparator function is different from the - * UComparator function type defined in uarrsort.h) - */ - void sort(UElementComparator *compare, UErrorCode &ec); - - /** - * Stable sort the contents of this vector using a caller-supplied function - * of type UComparator to do the comparison. Provides more flexibility - * than UVector::sort() because an additional user parameter can be passed to - * the comparison function. - */ - void sortWithUComparator(UComparator *compare, const void *context, UErrorCode &ec); - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - virtual UClassID getDynamicClassID() const override; - -private: - int32_t indexOf(UElement key, int32_t startIndex = 0, int8_t hint = 0) const; - - void sortedInsert(UElement e, UElementComparator *compare, UErrorCode& ec); - -public: - // Disallow - UVector(const UVector&) = delete; - - // Disallow - UVector& operator=(const UVector&) = delete; - -}; - - -/** - * Ultralightweight C++ implementation of a `void*` stack - * that is (mostly) compatible with java.util.Stack. As in java, this - * is merely a paper thin layer around UVector. See the UVector - * documentation for further information. - * - * *Design notes* - * - * The element at index `n-1` is (of course) the top of the - * stack. - * - * The poorly named `empty()` method doesn't empty the - * stack; it determines if the stack is empty. - * - * @author Alan Liu - */ -class U_COMMON_API UStack : public UVector { -public: - UStack(UErrorCode &status); - - UStack(int32_t initialCapacity, UErrorCode &status); - - UStack(UObjectDeleter *d, UElementsAreEqual *c, UErrorCode &status); - - UStack(UObjectDeleter *d, UElementsAreEqual *c, int32_t initialCapacity, UErrorCode &status); - - virtual ~UStack(); - - // It's okay not to have a virtual destructor (in UVector) - // because UStack has no special cleanup to do. - - inline UBool empty(void) const {return isEmpty();} - - inline void* peek(void) const {return lastElement();} - - inline int32_t peeki(void) const {return lastElementi();} - - /** - * Pop and return an element from the stack. - * For stacks with a deleter function, the caller takes ownership - * of the popped element. - */ - void* pop(void); - - int32_t popi(void); - - inline void* push(void* obj, UErrorCode &status) { - if (hasDeleter()) { - adoptElement(obj, status); - return (U_SUCCESS(status)) ? obj : nullptr; - } else { - addElement(obj, status); - return obj; - } - } - - inline int32_t push(int32_t i, UErrorCode &status) { - addElement(i, status); - return i; - } - - /* - If the object o occurs as an item in this stack, - this method returns the 1-based distance from the top of the stack. - */ - int32_t search(void* obj) const; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - virtual UClassID getDynamicClassID() const override; - - // Disallow - UStack(const UStack&) = delete; - - // Disallow - UStack& operator=(const UStack&) = delete; -}; - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 10/22/99 alan Creation. This is an internal header. +* It should not be exported. +********************************************************************** +*/ + +#ifndef UVECTOR_H +#define UVECTOR_H + +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "cmemory.h" +#include "uarrsort.h" +#include "uelement.h" + +U_NAMESPACE_BEGIN + +/** + * Ultralightweight C++ implementation of a `void*` vector + * that is (mostly) compatible with java.util.Vector. + * + * This is a very simple implementation, written to satisfy an + * immediate porting need. As such, it is not completely fleshed out, + * and it aims for simplicity and conformity. Nonetheless, it serves + * its purpose (porting code from java that uses java.util.Vector) + * well, and it could be easily made into a more robust vector class. + * + * *Design notes* + * + * There is index bounds checking, but little is done about it. If + * indices are out of bounds, either nothing happens, or zero is + * returned. We *do* avoid indexing off into the weeds. + * + * Since we don't have garbage collection, UVector was given the + * option to *own* its contents. To employ this, set a deleter + * function. The deleter is called on a `void *` pointer when that + * pointer is released by the vector, either when the vector itself is + * destructed, or when a call to `setElementAt()` overwrites an element, + * or when a call to remove()` or one of its variants explicitly + * removes an element. If no deleter is set, or the deleter is set to + * zero, then it is assumed that the caller will delete elements as + * needed. + * + * *Error Handling* Functions that can fail, from out of memory conditions + * for example, include a UErrorCode parameter. Any function called + * with an error code already indicating a failure will not modify the + * vector in any way. + * + * For vectors that have a deleter function, any failure in inserting + * an element into the vector will instead delete the element that + * could not be adopted. This simplifies object ownership + * management around calls to `addElement()` and `insertElementAt()`; + * error or no, the function always takes ownership of an incoming object + * from the caller. + * + * In order to implement methods such as `contains()` and `indexOf()`, + * UVector needs a way to compare objects for equality. To do so, it + * uses a comparison function, or "comparer." If the comparer is not + * set, or is set to zero, then all such methods will act as if the + * vector contains no element. That is, indexOf() will always return + * -1, contains() will always return false, etc. + * + *

To do + * + *

Improve the handling of index out of bounds errors. + * + * @author Alan Liu + */ +class U_COMMON_API UVector : public UObject { + // NOTE: UVector uses the UElement (union of void* and int32_t) as + // its basic storage type. It uses UElementsAreEqual as its + // comparison function. It uses UObjectDeleter as its deleter + // function. This allows sharing of support functions with UHashtable. + +private: + int32_t count = 0; + + int32_t capacity = 0; + + UElement* elements = nullptr; + + UObjectDeleter *deleter = nullptr; + + UElementsAreEqual *comparer = nullptr; + +public: + UVector(UErrorCode &status); + + UVector(int32_t initialCapacity, UErrorCode &status); + + UVector(UObjectDeleter *d, UElementsAreEqual *c, UErrorCode &status); + + UVector(UObjectDeleter *d, UElementsAreEqual *c, int32_t initialCapacity, UErrorCode &status); + + virtual ~UVector(); + + /** + * Assign this object to another (make this a copy of 'other'). + * Use the 'assign' function to assign each element. + */ + void assign(const UVector& other, UElementAssigner *assign, UErrorCode &ec); + + /** + * Compare this vector with another. They will be considered + * equal if they are of the same size and all elements are equal, + * as compared using this object's comparer. + */ + bool operator==(const UVector& other) const; + + /** + * Equivalent to !operator==() + */ + inline bool operator!=(const UVector& other) const {return !operator==(other);} + + //------------------------------------------------------------ + // java.util.Vector API + //------------------------------------------------------------ + + /** + * Add an element at the end of the vector. + * For use only with vectors that do not adopt their elements, which is to say, + * have not set an element deleter function. See `adoptElement()`. + */ + void addElement(void *obj, UErrorCode &status); + + /** + * Add an element at the end of the vector. + * For use only with vectors that adopt their elements, which is to say, + * have set an element deleter function. See `addElement()`. + * + * If the element cannot be successfully added, it will be deleted. This is + * normal ICU _adopt_ behavior - one way or another ownership of the incoming + * object is transferred from the caller. + * + * `addElement()` and `adoptElement()` are separate functions to make it easier + * to see what the function is doing at call sites. Having a single combined function, + * as in earlier versions of UVector, had proved to be error-prone. + */ + void adoptElement(void *obj, UErrorCode &status); + + void addElement(int32_t elem, UErrorCode &status); + + void setElementAt(void* obj, int32_t index); + + void setElementAt(int32_t elem, int32_t index); + + void insertElementAt(void* obj, int32_t index, UErrorCode &status); + + void insertElementAt(int32_t elem, int32_t index, UErrorCode &status); + + void* elementAt(int32_t index) const; + + int32_t elementAti(int32_t index) const; + + UBool equals(const UVector &other) const; + + inline void* firstElement() const {return elementAt(0);} + + inline void* lastElement() const {return elementAt(count-1);} + + inline int32_t lastElementi() const {return elementAti(count-1);} + + int32_t indexOf(void* obj, int32_t startIndex = 0) const; + + int32_t indexOf(int32_t obj, int32_t startIndex = 0) const; + + inline UBool contains(void* obj) const {return indexOf(obj) >= 0;} + + inline UBool contains(int32_t obj) const {return indexOf(obj) >= 0;} + + UBool containsAll(const UVector& other) const; + + UBool removeAll(const UVector& other); + + UBool retainAll(const UVector& other); + + void removeElementAt(int32_t index); + + UBool removeElement(void* obj); + + void removeAllElements(); + + inline int32_t size() const {return count;} + + inline UBool isEmpty() const {return count == 0;} + + UBool ensureCapacity(int32_t minimumCapacity, UErrorCode &status); + + /** + * Change the size of this vector as follows: If newSize is + * smaller, then truncate the array, possibly deleting held + * elements for i >= newSize. If newSize is larger, grow the + * array, filling in new slots with nullptr. + */ + void setSize(int32_t newSize, UErrorCode &status); + + /** + * Fill in the given array with all elements of this vector. + */ + void** toArray(void** result) const; + + //------------------------------------------------------------ + // New API + //------------------------------------------------------------ + + UObjectDeleter *setDeleter(UObjectDeleter *d); + bool hasDeleter() {return deleter != nullptr;} + + UElementsAreEqual *setComparer(UElementsAreEqual *c); + + inline void* operator[](int32_t index) const {return elementAt(index);} + + /** + * Removes the element at the given index from this vector and + * transfer ownership of it to the caller. After this call, the + * caller owns the result and must delete it and the vector entry + * at 'index' is removed, shifting all subsequent entries back by + * one index and shortening the size of the vector by one. If the + * index is out of range or if there is no item at the given index + * then 0 is returned and the vector is unchanged. + */ + void* orphanElementAt(int32_t index); + + /** + * Returns true if this vector contains none of the elements + * of the given vector. + * @param other vector to be checked for containment + * @return true if the test condition is met + */ + UBool containsNone(const UVector& other) const; + + /** + * Insert the given object into this vector at its sorted position + * as defined by 'compare'. The current elements are assumed to + * be sorted already. + */ + void sortedInsert(void* obj, UElementComparator *compare, UErrorCode& ec); + + /** + * Insert the given integer into this vector at its sorted position + * as defined by 'compare'. The current elements are assumed to + * be sorted already. + */ + void sortedInsert(int32_t obj, UElementComparator *compare, UErrorCode& ec); + + /** + * Sort the contents of the vector, assuming that the contents of the + * vector are of type int32_t. + */ + void sorti(UErrorCode &ec); + + /** + * Sort the contents of this vector, using a caller-supplied function + * to do the comparisons. (It's confusing that + * UVector's UElementComparator function is different from the + * UComparator function type defined in uarrsort.h) + */ + void sort(UElementComparator *compare, UErrorCode &ec); + + /** + * Stable sort the contents of this vector using a caller-supplied function + * of type UComparator to do the comparison. Provides more flexibility + * than UVector::sort() because an additional user parameter can be passed to + * the comparison function. + */ + void sortWithUComparator(UComparator *compare, const void *context, UErrorCode &ec); + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + virtual UClassID getDynamicClassID() const override; + +private: + int32_t indexOf(UElement key, int32_t startIndex = 0, int8_t hint = 0) const; + + void sortedInsert(UElement e, UElementComparator *compare, UErrorCode& ec); + +public: + // Disallow + UVector(const UVector&) = delete; + + // Disallow + UVector& operator=(const UVector&) = delete; + +}; + + +/** + * Ultralightweight C++ implementation of a `void*` stack + * that is (mostly) compatible with java.util.Stack. As in java, this + * is merely a paper thin layer around UVector. See the UVector + * documentation for further information. + * + * *Design notes* + * + * The element at index `n-1` is (of course) the top of the + * stack. + * + * The poorly named `empty()` method doesn't empty the + * stack; it determines if the stack is empty. + * + * @author Alan Liu + */ +class U_COMMON_API UStack : public UVector { +public: + UStack(UErrorCode &status); + + UStack(int32_t initialCapacity, UErrorCode &status); + + UStack(UObjectDeleter *d, UElementsAreEqual *c, UErrorCode &status); + + UStack(UObjectDeleter *d, UElementsAreEqual *c, int32_t initialCapacity, UErrorCode &status); + + virtual ~UStack(); + + // It's okay not to have a virtual destructor (in UVector) + // because UStack has no special cleanup to do. + + inline UBool empty() const {return isEmpty();} + + inline void* peek() const {return lastElement();} + + inline int32_t peeki() const {return lastElementi();} + + /** + * Pop and return an element from the stack. + * For stacks with a deleter function, the caller takes ownership + * of the popped element. + */ + void* pop(); + + int32_t popi(); + + inline void* push(void* obj, UErrorCode &status) { + if (hasDeleter()) { + adoptElement(obj, status); + return (U_SUCCESS(status)) ? obj : nullptr; + } else { + addElement(obj, status); + return obj; + } + } + + inline int32_t push(int32_t i, UErrorCode &status) { + addElement(i, status); + return i; + } + + /* + If the object o occurs as an item in this stack, + this method returns the 1-based distance from the top of the stack. + */ + int32_t search(void* obj) const; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + virtual UClassID getDynamicClassID() const override; + + // Disallow + UStack(const UStack&) = delete; + + // Disallow + UStack& operator=(const UStack&) = delete; +}; + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/common/uvectr32.cpp b/deps/icu-small/source/common/uvectr32.cpp index 952f51792b3213..609b30d2077338 100644 --- a/deps/icu-small/source/common/uvectr32.cpp +++ b/deps/icu-small/source/common/uvectr32.cpp @@ -1,335 +1,335 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 1999-2015, International Business Machines Corporation and -* others. All Rights Reserved. -****************************************************************************** -* Date Name Description -* 10/22/99 alan Creation. -********************************************************************** -*/ - -#include "uvectr32.h" -#include "cmemory.h" -#include "putilimp.h" - -U_NAMESPACE_BEGIN - -#define DEFAULT_CAPACITY 8 - -/* - * Constants for hinting whether a key is an integer - * or a pointer. If a hint bit is zero, then the associated - * token is assumed to be an integer. This is needed for iSeries - */ - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UVector32) - -UVector32::UVector32(UErrorCode &status) : - count(0), - capacity(0), - maxCapacity(0), - elements(NULL) -{ - _init(DEFAULT_CAPACITY, status); -} - -UVector32::UVector32(int32_t initialCapacity, UErrorCode &status) : - count(0), - capacity(0), - maxCapacity(0), - elements(0) -{ - _init(initialCapacity, status); -} - - - -void UVector32::_init(int32_t initialCapacity, UErrorCode &status) { - // Fix bogus initialCapacity values; avoid malloc(0) - if (initialCapacity < 1) { - initialCapacity = DEFAULT_CAPACITY; - } - if (maxCapacity>0 && maxCapacity (int32_t)(INT32_MAX / sizeof(int32_t))) { - initialCapacity = uprv_min(DEFAULT_CAPACITY, maxCapacity); - } - elements = (int32_t *)uprv_malloc(sizeof(int32_t)*initialCapacity); - if (elements == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - capacity = initialCapacity; - } -} - -UVector32::~UVector32() { - uprv_free(elements); - elements = 0; -} - -/** - * Assign this object to another (make this a copy of 'other'). - */ -void UVector32::assign(const UVector32& other, UErrorCode &ec) { - if (ensureCapacity(other.count, ec)) { - setSize(other.count); - for (int32_t i=0; iindex; --i) { - elements[i] = elements[i-1]; - } - elements[index] = elem; - ++count; - } - /* else index out of range */ -} - -UBool UVector32::containsAll(const UVector32& other) const { - for (int32_t i=0; i= 0) { - return false; - } - } - return true; -} - -UBool UVector32::removeAll(const UVector32& other) { - UBool changed = false; - for (int32_t i=0; i= 0) { - removeElementAt(j); - changed = true; - } - } - return changed; -} - -UBool UVector32::retainAll(const UVector32& other) { - UBool changed = false; - for (int32_t j=size()-1; j>=0; --j) { - int32_t i = other.indexOf(elements[j]); - if (i < 0) { - removeElementAt(j); - changed = true; - } - } - return changed; -} - -void UVector32::removeElementAt(int32_t index) { - if (index >= 0) { - for (int32_t i=index; icount != other.count) { - return false; - } - for (i=0; i= minimumCapacity) { - return true; - } - if (maxCapacity>0 && minimumCapacity>maxCapacity) { - status = U_BUFFER_OVERFLOW_ERROR; - return false; - } - if (capacity > (INT32_MAX - 1) / 2) { // integer overflow check - status = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - int32_t newCap = capacity * 2; - if (newCap < minimumCapacity) { - newCap = minimumCapacity; - } - if (maxCapacity > 0 && newCap > maxCapacity) { - newCap = maxCapacity; - } - if (newCap > (int32_t)(INT32_MAX / sizeof(int32_t))) { // integer overflow check - // We keep the original memory contents on bad minimumCapacity/maxCapacity. - status = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - int32_t* newElems = (int32_t *)uprv_realloc(elements, sizeof(int32_t)*newCap); - if (newElems == NULL) { - // We keep the original contents on the memory failure on realloc. - status = U_MEMORY_ALLOCATION_ERROR; - return false; - } - elements = newElems; - capacity = newCap; - return true; -} - -void UVector32::setMaxCapacity(int32_t limit) { - U_ASSERT(limit >= 0); - if (limit < 0) { - limit = 0; - } - if (limit > (int32_t)(INT32_MAX / sizeof(int32_t))) { // integer overflow check for realloc - // Something is very wrong, don't realloc, leave capacity and maxCapacity unchanged - return; - } - maxCapacity = limit; - if (capacity <= maxCapacity || maxCapacity == 0) { - // Current capacity is within the new limit. - return; - } - - // New maximum capacity is smaller than the current size. - // Realloc the storage to the new, smaller size. - int32_t* newElems = (int32_t *)uprv_realloc(elements, sizeof(int32_t)*maxCapacity); - if (newElems == NULL) { - // Realloc to smaller failed. - // Just keep what we had. No need to call it a failure. - return; - } - elements = newElems; - capacity = maxCapacity; - if (count > capacity) { - count = capacity; - } -} - -/** - * Change the size of this vector as follows: If newSize is smaller, - * then truncate the array, possibly deleting held elements for i >= - * newSize. If newSize is larger, grow the array, filling in new - * slots with NULL. - */ -void UVector32::setSize(int32_t newSize) { - int32_t i; - if (newSize < 0) { - return; - } - if (newSize > count) { - UErrorCode ec = U_ZERO_ERROR; - if (!ensureCapacity(newSize, ec)) { - return; - } - for (i=count; i 0) { - if (elements[probe] > tok) { - max = probe; - } else { - // assert(c <= 0); - min = probe + 1; - } - } - if (ensureCapacity(count + 1, ec)) { - for (int32_t i=count; i>min; --i) { - elements[i] = elements[i-1]; - } - elements[min] = tok; - ++count; - } -} - - - - - -U_NAMESPACE_END - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 1999-2015, International Business Machines Corporation and +* others. All Rights Reserved. +****************************************************************************** +* Date Name Description +* 10/22/99 alan Creation. +********************************************************************** +*/ + +#include "uvectr32.h" +#include "cmemory.h" +#include "putilimp.h" + +U_NAMESPACE_BEGIN + +#define DEFAULT_CAPACITY 8 + +/* + * Constants for hinting whether a key is an integer + * or a pointer. If a hint bit is zero, then the associated + * token is assumed to be an integer. This is needed for iSeries + */ + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UVector32) + +UVector32::UVector32(UErrorCode &status) : + count(0), + capacity(0), + maxCapacity(0), + elements(nullptr) +{ + _init(DEFAULT_CAPACITY, status); +} + +UVector32::UVector32(int32_t initialCapacity, UErrorCode &status) : + count(0), + capacity(0), + maxCapacity(0), + elements(0) +{ + _init(initialCapacity, status); +} + + + +void UVector32::_init(int32_t initialCapacity, UErrorCode &status) { + // Fix bogus initialCapacity values; avoid malloc(0) + if (initialCapacity < 1) { + initialCapacity = DEFAULT_CAPACITY; + } + if (maxCapacity>0 && maxCapacity (int32_t)(INT32_MAX / sizeof(int32_t))) { + initialCapacity = uprv_min(DEFAULT_CAPACITY, maxCapacity); + } + elements = (int32_t *)uprv_malloc(sizeof(int32_t)*initialCapacity); + if (elements == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + capacity = initialCapacity; + } +} + +UVector32::~UVector32() { + uprv_free(elements); + elements = 0; +} + +/** + * Assign this object to another (make this a copy of 'other'). + */ +void UVector32::assign(const UVector32& other, UErrorCode &ec) { + if (ensureCapacity(other.count, ec)) { + setSize(other.count); + for (int32_t i=0; iindex; --i) { + elements[i] = elements[i-1]; + } + elements[index] = elem; + ++count; + } + /* else index out of range */ +} + +UBool UVector32::containsAll(const UVector32& other) const { + for (int32_t i=0; i= 0) { + return false; + } + } + return true; +} + +UBool UVector32::removeAll(const UVector32& other) { + UBool changed = false; + for (int32_t i=0; i= 0) { + removeElementAt(j); + changed = true; + } + } + return changed; +} + +UBool UVector32::retainAll(const UVector32& other) { + UBool changed = false; + for (int32_t j=size()-1; j>=0; --j) { + int32_t i = other.indexOf(elements[j]); + if (i < 0) { + removeElementAt(j); + changed = true; + } + } + return changed; +} + +void UVector32::removeElementAt(int32_t index) { + if (index >= 0) { + for (int32_t i=index; icount != other.count) { + return false; + } + for (i=0; i= minimumCapacity) { + return true; + } + if (maxCapacity>0 && minimumCapacity>maxCapacity) { + status = U_BUFFER_OVERFLOW_ERROR; + return false; + } + if (capacity > (INT32_MAX - 1) / 2) { // integer overflow check + status = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + int32_t newCap = capacity * 2; + if (newCap < minimumCapacity) { + newCap = minimumCapacity; + } + if (maxCapacity > 0 && newCap > maxCapacity) { + newCap = maxCapacity; + } + if (newCap > (int32_t)(INT32_MAX / sizeof(int32_t))) { // integer overflow check + // We keep the original memory contents on bad minimumCapacity/maxCapacity. + status = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + int32_t* newElems = (int32_t *)uprv_realloc(elements, sizeof(int32_t)*newCap); + if (newElems == nullptr) { + // We keep the original contents on the memory failure on realloc. + status = U_MEMORY_ALLOCATION_ERROR; + return false; + } + elements = newElems; + capacity = newCap; + return true; +} + +void UVector32::setMaxCapacity(int32_t limit) { + U_ASSERT(limit >= 0); + if (limit < 0) { + limit = 0; + } + if (limit > (int32_t)(INT32_MAX / sizeof(int32_t))) { // integer overflow check for realloc + // Something is very wrong, don't realloc, leave capacity and maxCapacity unchanged + return; + } + maxCapacity = limit; + if (capacity <= maxCapacity || maxCapacity == 0) { + // Current capacity is within the new limit. + return; + } + + // New maximum capacity is smaller than the current size. + // Realloc the storage to the new, smaller size. + int32_t* newElems = (int32_t *)uprv_realloc(elements, sizeof(int32_t)*maxCapacity); + if (newElems == nullptr) { + // Realloc to smaller failed. + // Just keep what we had. No need to call it a failure. + return; + } + elements = newElems; + capacity = maxCapacity; + if (count > capacity) { + count = capacity; + } +} + +/** + * Change the size of this vector as follows: If newSize is smaller, + * then truncate the array, possibly deleting held elements for i >= + * newSize. If newSize is larger, grow the array, filling in new + * slots with nullptr. + */ +void UVector32::setSize(int32_t newSize) { + int32_t i; + if (newSize < 0) { + return; + } + if (newSize > count) { + UErrorCode ec = U_ZERO_ERROR; + if (!ensureCapacity(newSize, ec)) { + return; + } + for (i=count; i 0) { + if (elements[probe] > tok) { + max = probe; + } else { + // assert(c <= 0); + min = probe + 1; + } + } + if (ensureCapacity(count + 1, ec)) { + for (int32_t i=count; i>min; --i) { + elements[i] = elements[i-1]; + } + elements[min] = tok; + ++count; + } +} + + + + + +U_NAMESPACE_END + diff --git a/deps/icu-small/source/common/uvectr32.h b/deps/icu-small/source/common/uvectr32.h index a7fada38335d2f..3b426eb5849ddd 100644 --- a/deps/icu-small/source/common/uvectr32.h +++ b/deps/icu-small/source/common/uvectr32.h @@ -1,306 +1,306 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ - -// -// UVector32 is a class implementing a vector of 32 bit integers. -// It is similar to UVector, but holds int32_t values rather than pointers. -// Most of the code is unchanged from UVector. -// - -#ifndef UVECTOR32_H -#define UVECTOR32_H - -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "uhash.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - - - -/** - *

Ultralightweight C++ implementation of a void* vector - * that is (mostly) compatible with java.util.Vector. - * - *

This is a very simple implementation, written to satisfy an - * immediate porting need. As such, it is not completely fleshed out, - * and it aims for simplicity and conformity. Nonetheless, it serves - * its purpose (porting code from java that uses java.util.Vector) - * well, and it could be easily made into a more robust vector class. - * - *

Design notes - * - *

There is index bounds checking, but little is done about it. If - * indices are out of bounds, either nothing happens, or zero is - * returned. We do avoid indexing off into the weeds. - * - *

There is detection of out of memory, but the handling is very - * coarse-grained -- similar to UnicodeString's protocol, but even - * coarser. The class contains one static flag that is set - * when any call to new returns zero. This allows the caller - * to use several vectors and make just one check at the end to see if - * a memory failure occurred. This is more efficient than making a - * check after each call on each vector when doing many operations on - * multiple vectors. The single static flag works best when memory - * failures are infrequent, and when recovery options are limited or - * nonexistent. - * - *

To do - * - *

Improve the handling of index out of bounds errors. - * - * @author Alan Liu - */ -class U_COMMON_API UVector32 : public UObject { -private: - int32_t count; - - int32_t capacity; - - int32_t maxCapacity; // Limit beyond which capacity is not permitted to grow. - - int32_t* elements; - -public: - UVector32(UErrorCode &status); - - UVector32(int32_t initialCapacity, UErrorCode &status); - - virtual ~UVector32(); - - /** - * Assign this object to another (make this a copy of 'other'). - * Use the 'assign' function to assign each element. - */ - void assign(const UVector32& other, UErrorCode &ec); - - /** - * Compare this vector with another. They will be considered - * equal if they are of the same size and all elements are equal, - * as compared using this object's comparer. - */ - bool operator==(const UVector32& other) const; - - /** - * Equivalent to !operator==() - */ - inline bool operator!=(const UVector32& other) const; - - //------------------------------------------------------------ - // java.util.Vector API - //------------------------------------------------------------ - - inline void addElement(int32_t elem, UErrorCode &status); - - void setElementAt(int32_t elem, int32_t index); - - void insertElementAt(int32_t elem, int32_t index, UErrorCode &status); - - inline int32_t elementAti(int32_t index) const; - - UBool equals(const UVector32 &other) const; - - inline int32_t lastElementi(void) const; - - int32_t indexOf(int32_t elem, int32_t startIndex = 0) const; - - inline UBool contains(int32_t elem) const; - - UBool containsAll(const UVector32& other) const; - - UBool removeAll(const UVector32& other); - - UBool retainAll(const UVector32& other); - - void removeElementAt(int32_t index); - - void removeAllElements(); - - inline int32_t size(void) const; - - inline UBool isEmpty(void) const; - - // Inline. Use this one for speedy size check. - inline UBool ensureCapacity(int32_t minimumCapacity, UErrorCode &status); - - // Out-of-line, handles actual growth. Called by ensureCapacity() when necessary. - UBool expandCapacity(int32_t minimumCapacity, UErrorCode &status); - - /** - * Change the size of this vector as follows: If newSize is - * smaller, then truncate the array, possibly deleting held - * elements for i >= newSize. If newSize is larger, grow the - * array, filling in new slows with zero. - */ - void setSize(int32_t newSize); - - //------------------------------------------------------------ - // New API - //------------------------------------------------------------ - - /** - * Returns true if this vector contains none of the elements - * of the given vector. - * @param other vector to be checked for containment - * @return true if the test condition is met - */ - UBool containsNone(const UVector32& other) const; - - - /** - * Insert the given integer into this vector at its sorted position. - * The current elements are assumed to be sorted already. - */ - void sortedInsert(int32_t elem, UErrorCode& ec); - - /** - * Returns a pointer to the internal array holding the vector. - */ - inline int32_t *getBuffer() const; - - /** - * Set the maximum allowed buffer capacity for this vector/stack. - * Default with no limit set is unlimited, go until malloc() fails. - * A Limit of zero means unlimited capacity. - * Units are vector elements (32 bits each), not bytes. - */ - void setMaxCapacity(int32_t limit); - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - virtual UClassID getDynamicClassID() const override; - -private: - void _init(int32_t initialCapacity, UErrorCode &status); - - // Disallow - UVector32(const UVector32&) = delete; - - // Disallow - UVector32& operator=(const UVector32&) = delete; - - - // API Functions for Stack operations. - // In the original UVector, these were in a separate derived class, UStack. - // Here in UVector32, they are all together. -public: - inline UBool empty(void) const; // TODO: redundant, same as empty(). Remove it? - - inline int32_t peeki(void) const; - - inline int32_t popi(void); - - inline int32_t push(int32_t i, UErrorCode &status); - - inline int32_t *reserveBlock(int32_t size, UErrorCode &status); - inline int32_t *popFrame(int32_t size); -}; - - -// UVector32 inlines - -inline UBool UVector32::ensureCapacity(int32_t minimumCapacity, UErrorCode &status) { - if ((minimumCapacity >= 0) && (capacity >= minimumCapacity)) { - return true; - } else { - return expandCapacity(minimumCapacity, status); - } -} - -inline int32_t UVector32::elementAti(int32_t index) const { - return (index >= 0 && count > 0 && count - index > 0) ? elements[index] : 0; -} - - -inline void UVector32::addElement(int32_t elem, UErrorCode &status) { - if (ensureCapacity(count + 1, status)) { - elements[count] = elem; - count++; - } -} - -inline int32_t *UVector32::reserveBlock(int32_t size, UErrorCode &status) { - if (ensureCapacity(count+size, status) == false) { - return NULL; - } - int32_t *rp = elements+count; - count += size; - return rp; -} - -inline int32_t *UVector32::popFrame(int32_t size) { - U_ASSERT(count >= size); - count -= size; - if (count < 0) { - count = 0; - } - return elements+count-size; -} - - - -inline int32_t UVector32::size(void) const { - return count; -} - -inline UBool UVector32::isEmpty(void) const { - return count == 0; -} - -inline UBool UVector32::contains(int32_t obj) const { - return indexOf(obj) >= 0; -} - -inline int32_t UVector32::lastElementi(void) const { - return elementAti(count-1); -} - -inline bool UVector32::operator!=(const UVector32& other) const { - return !operator==(other); -} - -inline int32_t *UVector32::getBuffer() const { - return elements; -} - - -// UStack inlines - -inline UBool UVector32::empty(void) const { - return isEmpty(); -} - -inline int32_t UVector32::peeki(void) const { - return lastElementi(); -} - -inline int32_t UVector32::push(int32_t i, UErrorCode &status) { - addElement(i, status); - return i; -} - -inline int32_t UVector32::popi(void) { - int32_t result = 0; - if (count > 0) { - count--; - result = elements[count]; - } - return result; -} - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +// +// UVector32 is a class implementing a vector of 32 bit integers. +// It is similar to UVector, but holds int32_t values rather than pointers. +// Most of the code is unchanged from UVector. +// + +#ifndef UVECTOR32_H +#define UVECTOR32_H + +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "uhash.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + + + +/** + *

Ultralightweight C++ implementation of a void* vector + * that is (mostly) compatible with java.util.Vector. + * + *

This is a very simple implementation, written to satisfy an + * immediate porting need. As such, it is not completely fleshed out, + * and it aims for simplicity and conformity. Nonetheless, it serves + * its purpose (porting code from java that uses java.util.Vector) + * well, and it could be easily made into a more robust vector class. + * + *

Design notes + * + *

There is index bounds checking, but little is done about it. If + * indices are out of bounds, either nothing happens, or zero is + * returned. We do avoid indexing off into the weeds. + * + *

There is detection of out of memory, but the handling is very + * coarse-grained -- similar to UnicodeString's protocol, but even + * coarser. The class contains one static flag that is set + * when any call to new returns zero. This allows the caller + * to use several vectors and make just one check at the end to see if + * a memory failure occurred. This is more efficient than making a + * check after each call on each vector when doing many operations on + * multiple vectors. The single static flag works best when memory + * failures are infrequent, and when recovery options are limited or + * nonexistent. + * + *

To do + * + *

Improve the handling of index out of bounds errors. + * + * @author Alan Liu + */ +class U_COMMON_API UVector32 : public UObject { +private: + int32_t count; + + int32_t capacity; + + int32_t maxCapacity; // Limit beyond which capacity is not permitted to grow. + + int32_t* elements; + +public: + UVector32(UErrorCode &status); + + UVector32(int32_t initialCapacity, UErrorCode &status); + + virtual ~UVector32(); + + /** + * Assign this object to another (make this a copy of 'other'). + * Use the 'assign' function to assign each element. + */ + void assign(const UVector32& other, UErrorCode &ec); + + /** + * Compare this vector with another. They will be considered + * equal if they are of the same size and all elements are equal, + * as compared using this object's comparer. + */ + bool operator==(const UVector32& other) const; + + /** + * Equivalent to !operator==() + */ + inline bool operator!=(const UVector32& other) const; + + //------------------------------------------------------------ + // java.util.Vector API + //------------------------------------------------------------ + + inline void addElement(int32_t elem, UErrorCode &status); + + void setElementAt(int32_t elem, int32_t index); + + void insertElementAt(int32_t elem, int32_t index, UErrorCode &status); + + inline int32_t elementAti(int32_t index) const; + + UBool equals(const UVector32 &other) const; + + inline int32_t lastElementi() const; + + int32_t indexOf(int32_t elem, int32_t startIndex = 0) const; + + inline UBool contains(int32_t elem) const; + + UBool containsAll(const UVector32& other) const; + + UBool removeAll(const UVector32& other); + + UBool retainAll(const UVector32& other); + + void removeElementAt(int32_t index); + + void removeAllElements(); + + inline int32_t size() const; + + inline UBool isEmpty() const; + + // Inline. Use this one for speedy size check. + inline UBool ensureCapacity(int32_t minimumCapacity, UErrorCode &status); + + // Out-of-line, handles actual growth. Called by ensureCapacity() when necessary. + UBool expandCapacity(int32_t minimumCapacity, UErrorCode &status); + + /** + * Change the size of this vector as follows: If newSize is + * smaller, then truncate the array, possibly deleting held + * elements for i >= newSize. If newSize is larger, grow the + * array, filling in new slows with zero. + */ + void setSize(int32_t newSize); + + //------------------------------------------------------------ + // New API + //------------------------------------------------------------ + + /** + * Returns true if this vector contains none of the elements + * of the given vector. + * @param other vector to be checked for containment + * @return true if the test condition is met + */ + UBool containsNone(const UVector32& other) const; + + + /** + * Insert the given integer into this vector at its sorted position. + * The current elements are assumed to be sorted already. + */ + void sortedInsert(int32_t elem, UErrorCode& ec); + + /** + * Returns a pointer to the internal array holding the vector. + */ + inline int32_t *getBuffer() const; + + /** + * Set the maximum allowed buffer capacity for this vector/stack. + * Default with no limit set is unlimited, go until malloc() fails. + * A Limit of zero means unlimited capacity. + * Units are vector elements (32 bits each), not bytes. + */ + void setMaxCapacity(int32_t limit); + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + virtual UClassID getDynamicClassID() const override; + +private: + void _init(int32_t initialCapacity, UErrorCode &status); + + // Disallow + UVector32(const UVector32&) = delete; + + // Disallow + UVector32& operator=(const UVector32&) = delete; + + + // API Functions for Stack operations. + // In the original UVector, these were in a separate derived class, UStack. + // Here in UVector32, they are all together. +public: + inline UBool empty() const; // TODO: redundant, same as empty(). Remove it? + + inline int32_t peeki() const; + + inline int32_t popi(); + + inline int32_t push(int32_t i, UErrorCode &status); + + inline int32_t *reserveBlock(int32_t size, UErrorCode &status); + inline int32_t *popFrame(int32_t size); +}; + + +// UVector32 inlines + +inline UBool UVector32::ensureCapacity(int32_t minimumCapacity, UErrorCode &status) { + if ((minimumCapacity >= 0) && (capacity >= minimumCapacity)) { + return true; + } else { + return expandCapacity(minimumCapacity, status); + } +} + +inline int32_t UVector32::elementAti(int32_t index) const { + return (index >= 0 && count > 0 && count - index > 0) ? elements[index] : 0; +} + + +inline void UVector32::addElement(int32_t elem, UErrorCode &status) { + if (ensureCapacity(count + 1, status)) { + elements[count] = elem; + count++; + } +} + +inline int32_t *UVector32::reserveBlock(int32_t size, UErrorCode &status) { + if (ensureCapacity(count+size, status) == false) { + return nullptr; + } + int32_t *rp = elements+count; + count += size; + return rp; +} + +inline int32_t *UVector32::popFrame(int32_t size) { + U_ASSERT(count >= size); + count -= size; + if (count < 0) { + count = 0; + } + return elements+count-size; +} + + + +inline int32_t UVector32::size() const { + return count; +} + +inline UBool UVector32::isEmpty() const { + return count == 0; +} + +inline UBool UVector32::contains(int32_t obj) const { + return indexOf(obj) >= 0; +} + +inline int32_t UVector32::lastElementi() const { + return elementAti(count-1); +} + +inline bool UVector32::operator!=(const UVector32& other) const { + return !operator==(other); +} + +inline int32_t *UVector32::getBuffer() const { + return elements; +} + + +// UStack inlines + +inline UBool UVector32::empty() const { + return isEmpty(); +} + +inline int32_t UVector32::peeki() const { + return lastElementi(); +} + +inline int32_t UVector32::push(int32_t i, UErrorCode &status) { + addElement(i, status); + return i; +} + +inline int32_t UVector32::popi() { + int32_t result = 0; + if (count > 0) { + count--; + result = elements[count]; + } + return result; +} + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/common/uvectr64.cpp b/deps/icu-small/source/common/uvectr64.cpp index 8bd5cd78393f90..dba328be0db557 100644 --- a/deps/icu-small/source/common/uvectr64.cpp +++ b/deps/icu-small/source/common/uvectr64.cpp @@ -1,214 +1,214 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 1999-2015, International Business Machines Corporation and -* others. All Rights Reserved. -****************************************************************************** -*/ - -#include "uvectr64.h" -#include "cmemory.h" -#include "putilimp.h" - -U_NAMESPACE_BEGIN - -#define DEFAULT_CAPACITY 8 - -/* - * Constants for hinting whether a key is an integer - * or a pointer. If a hint bit is zero, then the associated - * token is assumed to be an integer. This is needed for iSeries - */ - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UVector64) - -UVector64::UVector64(UErrorCode &status) : - count(0), - capacity(0), - maxCapacity(0), - elements(NULL) -{ - _init(DEFAULT_CAPACITY, status); -} - -UVector64::UVector64(int32_t initialCapacity, UErrorCode &status) : - count(0), - capacity(0), - maxCapacity(0), - elements(0) -{ - _init(initialCapacity, status); -} - - - -void UVector64::_init(int32_t initialCapacity, UErrorCode &status) { - // Fix bogus initialCapacity values; avoid malloc(0) - if (initialCapacity < 1) { - initialCapacity = DEFAULT_CAPACITY; - } - if (maxCapacity>0 && maxCapacity (int32_t)(INT32_MAX / sizeof(int64_t))) { - initialCapacity = uprv_min(DEFAULT_CAPACITY, maxCapacity); - } - elements = (int64_t *)uprv_malloc(sizeof(int64_t)*initialCapacity); - if (elements == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - capacity = initialCapacity; - } -} - -UVector64::~UVector64() { - uprv_free(elements); - elements = 0; -} - -/** - * Assign this object to another (make this a copy of 'other'). - */ -void UVector64::assign(const UVector64& other, UErrorCode &ec) { - if (ensureCapacity(other.count, ec)) { - setSize(other.count); - for (int32_t i=0; iindex; --i) { - elements[i] = elements[i-1]; - } - elements[index] = elem; - ++count; - } - /* else index out of range */ -} - -void UVector64::removeAllElements(void) { - count = 0; -} - -UBool UVector64::expandCapacity(int32_t minimumCapacity, UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - if (minimumCapacity < 0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - if (capacity >= minimumCapacity) { - return true; - } - if (maxCapacity>0 && minimumCapacity>maxCapacity) { - status = U_BUFFER_OVERFLOW_ERROR; - return false; - } - if (capacity > (INT32_MAX - 1) / 2) { // integer overflow check - status = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - int32_t newCap = capacity * 2; - if (newCap < minimumCapacity) { - newCap = minimumCapacity; - } - if (maxCapacity > 0 && newCap > maxCapacity) { - newCap = maxCapacity; - } - if (newCap > (int32_t)(INT32_MAX / sizeof(int64_t))) { // integer overflow check - // We keep the original memory contents on bad minimumCapacity/maxCapacity. - status = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - int64_t* newElems = (int64_t *)uprv_realloc(elements, sizeof(int64_t)*newCap); - if (newElems == NULL) { - // We keep the original contents on the memory failure on realloc. - status = U_MEMORY_ALLOCATION_ERROR; - return false; - } - elements = newElems; - capacity = newCap; - return true; -} - -void UVector64::setMaxCapacity(int32_t limit) { - U_ASSERT(limit >= 0); - if (limit < 0) { - limit = 0; - } - if (limit > (int32_t)(INT32_MAX / sizeof(int64_t))) { // integer overflow check for realloc - // Something is very wrong, don't realloc, leave capacity and maxCapacity unchanged - return; - } - maxCapacity = limit; - if (capacity <= maxCapacity || maxCapacity == 0) { - // Current capacity is within the new limit. - return; - } - - // New maximum capacity is smaller than the current size. - // Realloc the storage to the new, smaller size. - int64_t* newElems = (int64_t *)uprv_realloc(elements, sizeof(int64_t)*maxCapacity); - if (newElems == NULL) { - // Realloc to smaller failed. - // Just keep what we had. No need to call it a failure. - return; - } - elements = newElems; - capacity = maxCapacity; - if (count > capacity) { - count = capacity; - } -} - -/** - * Change the size of this vector as follows: If newSize is smaller, - * then truncate the array, possibly deleting held elements for i >= - * newSize. If newSize is larger, grow the array, filling in new - * slots with NULL. - */ -void UVector64::setSize(int32_t newSize) { - int32_t i; - if (newSize < 0) { - return; - } - if (newSize > count) { - UErrorCode ec = U_ZERO_ERROR; - if (!ensureCapacity(newSize, ec)) { - return; - } - for (i=count; i0 && maxCapacity (int32_t)(INT32_MAX / sizeof(int64_t))) { + initialCapacity = uprv_min(DEFAULT_CAPACITY, maxCapacity); + } + elements = (int64_t *)uprv_malloc(sizeof(int64_t)*initialCapacity); + if (elements == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + capacity = initialCapacity; + } +} + +UVector64::~UVector64() { + uprv_free(elements); + elements = 0; +} + +/** + * Assign this object to another (make this a copy of 'other'). + */ +void UVector64::assign(const UVector64& other, UErrorCode &ec) { + if (ensureCapacity(other.count, ec)) { + setSize(other.count); + for (int32_t i=0; iindex; --i) { + elements[i] = elements[i-1]; + } + elements[index] = elem; + ++count; + } + /* else index out of range */ +} + +void UVector64::removeAllElements() { + count = 0; +} + +UBool UVector64::expandCapacity(int32_t minimumCapacity, UErrorCode &status) { + if (U_FAILURE(status)) { + return false; + } + if (minimumCapacity < 0) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + if (capacity >= minimumCapacity) { + return true; + } + if (maxCapacity>0 && minimumCapacity>maxCapacity) { + status = U_BUFFER_OVERFLOW_ERROR; + return false; + } + if (capacity > (INT32_MAX - 1) / 2) { // integer overflow check + status = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + int32_t newCap = capacity * 2; + if (newCap < minimumCapacity) { + newCap = minimumCapacity; + } + if (maxCapacity > 0 && newCap > maxCapacity) { + newCap = maxCapacity; + } + if (newCap > (int32_t)(INT32_MAX / sizeof(int64_t))) { // integer overflow check + // We keep the original memory contents on bad minimumCapacity/maxCapacity. + status = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + int64_t* newElems = (int64_t *)uprv_realloc(elements, sizeof(int64_t)*newCap); + if (newElems == nullptr) { + // We keep the original contents on the memory failure on realloc. + status = U_MEMORY_ALLOCATION_ERROR; + return false; + } + elements = newElems; + capacity = newCap; + return true; +} + +void UVector64::setMaxCapacity(int32_t limit) { + U_ASSERT(limit >= 0); + if (limit < 0) { + limit = 0; + } + if (limit > (int32_t)(INT32_MAX / sizeof(int64_t))) { // integer overflow check for realloc + // Something is very wrong, don't realloc, leave capacity and maxCapacity unchanged + return; + } + maxCapacity = limit; + if (capacity <= maxCapacity || maxCapacity == 0) { + // Current capacity is within the new limit. + return; + } + + // New maximum capacity is smaller than the current size. + // Realloc the storage to the new, smaller size. + int64_t* newElems = (int64_t *)uprv_realloc(elements, sizeof(int64_t)*maxCapacity); + if (newElems == nullptr) { + // Realloc to smaller failed. + // Just keep what we had. No need to call it a failure. + return; + } + elements = newElems; + capacity = maxCapacity; + if (count > capacity) { + count = capacity; + } +} + +/** + * Change the size of this vector as follows: If newSize is smaller, + * then truncate the array, possibly deleting held elements for i >= + * newSize. If newSize is larger, grow the array, filling in new + * slots with nullptr. + */ +void UVector64::setSize(int32_t newSize) { + int32_t i; + if (newSize < 0) { + return; + } + if (newSize > count) { + UErrorCode ec = U_ZERO_ERROR; + if (!ensureCapacity(newSize, ec)) { + return; + } + for (i=count; iUltralightweight C++ implementation of an int64_t vector - * that has a subset of methods from UVector32 - * - *

This is a very simple implementation, written to satisfy an - * immediate porting need. As such, it is not completely fleshed out, - * and it aims for simplicity and conformity. Nonetheless, it serves - * its purpose (porting code from java that uses java.util.Vector) - * well, and it could be easily made into a more robust vector class. - * - *

Design notes - * - *

There is index bounds checking, but little is done about it. If - * indices are out of bounds, either nothing happens, or zero is - * returned. We do avoid indexing off into the weeds. - * - *

There is detection of out of memory, but the handling is very - * coarse-grained -- similar to UnicodeString's protocol, but even - * coarser. The class contains one static flag that is set - * when any call to new returns zero. This allows the caller - * to use several vectors and make just one check at the end to see if - * a memory failure occurred. This is more efficient than making a - * check after each call on each vector when doing many operations on - * multiple vectors. The single static flag works best when memory - * failures are infrequent, and when recovery options are limited or - * nonexistent. - * - *

To do - * - *

Improve the handling of index out of bounds errors. - * - */ -class U_COMMON_API UVector64 : public UObject { -private: - int32_t count; - - int32_t capacity; - - int32_t maxCapacity; // Limit beyond which capacity is not permitted to grow. - - int64_t* elements; - -public: - UVector64(UErrorCode &status); - - UVector64(int32_t initialCapacity, UErrorCode &status); - - virtual ~UVector64(); - - /** - * Assign this object to another (make this a copy of 'other'). - * Use the 'assign' function to assign each element. - */ - void assign(const UVector64& other, UErrorCode &ec); - - /** - * Compare this vector with another. They will be considered - * equal if they are of the same size and all elements are equal, - * as compared using this object's comparer. - */ - bool operator==(const UVector64& other); - - /** - * Equivalent to !operator==() - */ - inline bool operator!=(const UVector64& other); - - //------------------------------------------------------------ - // subset of java.util.Vector API - //------------------------------------------------------------ - - inline void addElement(int64_t elem, UErrorCode &status); - - void setElementAt(int64_t elem, int32_t index); - - void insertElementAt(int64_t elem, int32_t index, UErrorCode &status); - - inline int64_t elementAti(int32_t index) const; - - //UBool equals(const UVector64 &other) const; - - inline int64_t lastElementi(void) const; - - //int32_t indexOf(int64_t elem, int32_t startIndex = 0) const; - - //UBool contains(int64_t elem) const; - - //UBool containsAll(const UVector64& other) const; - - //UBool removeAll(const UVector64& other); - - //UBool retainAll(const UVector64& other); - - //void removeElementAt(int32_t index); - - void removeAllElements(); - - inline int32_t size(void) const; - - inline UBool isEmpty(void) const { return count == 0; } - - // Inline. Use this one for speedy size check. - inline UBool ensureCapacity(int32_t minimumCapacity, UErrorCode &status); - - // Out-of-line, handles actual growth. Called by ensureCapacity() when necessary. - UBool expandCapacity(int32_t minimumCapacity, UErrorCode &status); - - /** - * Change the size of this vector as follows: If newSize is - * smaller, then truncate the array, possibly deleting held - * elements for i >= newSize. If newSize is larger, grow the - * array, filling in new slows with zero. - */ - void setSize(int32_t newSize); - - //------------------------------------------------------------ - // New API - //------------------------------------------------------------ - - //UBool containsNone(const UVector64& other) const; - - - //void sortedInsert(int64_t elem, UErrorCode& ec); - - /** - * Returns a pointer to the internal array holding the vector. - */ - inline int64_t *getBuffer() const; - - /** - * Set the maximum allowed buffer capacity for this vector/stack. - * Default with no limit set is unlimited, go until malloc() fails. - * A Limit of zero means unlimited capacity. - * Units are vector elements (64 bits each), not bytes. - */ - void setMaxCapacity(int32_t limit); - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - virtual UClassID getDynamicClassID() const override; - -private: - void _init(int32_t initialCapacity, UErrorCode &status); - - // Disallow - UVector64(const UVector64&) = delete; - - // Disallow - UVector64& operator=(const UVector64&) = delete; - - - // API Functions for Stack operations. - // In the original UVector, these were in a separate derived class, UStack. - // Here in UVector64, they are all together. -public: - //UBool empty(void) const; // TODO: redundant, same as empty(). Remove it? - - //int64_t peeki(void) const; - - inline int64_t popi(void); - - inline int64_t push(int64_t i, UErrorCode &status); - - inline int64_t *reserveBlock(int32_t size, UErrorCode &status); - inline int64_t *popFrame(int32_t size); -}; - - -// UVector64 inlines - -inline UBool UVector64::ensureCapacity(int32_t minimumCapacity, UErrorCode &status) { - if ((minimumCapacity >= 0) && (capacity >= minimumCapacity)) { - return true; - } else { - return expandCapacity(minimumCapacity, status); - } -} - -inline int64_t UVector64::elementAti(int32_t index) const { - return (0 <= index && index < count) ? elements[index] : 0; -} - - -inline void UVector64::addElement(int64_t elem, UErrorCode &status) { - if (ensureCapacity(count + 1, status)) { - elements[count] = elem; - count++; - } -} - -inline int64_t *UVector64::reserveBlock(int32_t size, UErrorCode &status) { - if (ensureCapacity(count+size, status) == false) { - return NULL; - } - int64_t *rp = elements+count; - count += size; - return rp; -} - -inline int64_t *UVector64::popFrame(int32_t size) { - U_ASSERT(count >= size); - count -= size; - if (count < 0) { - count = 0; - } - return elements+count-size; -} - - - -inline int32_t UVector64::size(void) const { - return count; -} - -inline int64_t UVector64::lastElementi(void) const { - return elementAti(count-1); -} - -inline bool UVector64::operator!=(const UVector64& other) { - return !operator==(other); -} - -inline int64_t *UVector64::getBuffer() const { - return elements; -} - - -// UStack inlines - -inline int64_t UVector64::push(int64_t i, UErrorCode &status) { - addElement(i, status); - return i; -} - -inline int64_t UVector64::popi(void) { - int64_t result = 0; - if (count > 0) { - count--; - result = elements[count]; - } - return result; -} - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +// +// UVector64 is a class implementing a vector of 64 bit integers. +// It is similar to UVector32, but holds int64_t values rather than int32_t. +// Most of the code is unchanged from UVector. +// + +#ifndef UVECTOR64_H +#define UVECTOR64_H + +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "uhash.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + + + +/** + *

Ultralightweight C++ implementation of an int64_t vector + * that has a subset of methods from UVector32 + * + *

This is a very simple implementation, written to satisfy an + * immediate porting need. As such, it is not completely fleshed out, + * and it aims for simplicity and conformity. Nonetheless, it serves + * its purpose (porting code from java that uses java.util.Vector) + * well, and it could be easily made into a more robust vector class. + * + *

Design notes + * + *

There is index bounds checking, but little is done about it. If + * indices are out of bounds, either nothing happens, or zero is + * returned. We do avoid indexing off into the weeds. + * + *

There is detection of out of memory, but the handling is very + * coarse-grained -- similar to UnicodeString's protocol, but even + * coarser. The class contains one static flag that is set + * when any call to new returns zero. This allows the caller + * to use several vectors and make just one check at the end to see if + * a memory failure occurred. This is more efficient than making a + * check after each call on each vector when doing many operations on + * multiple vectors. The single static flag works best when memory + * failures are infrequent, and when recovery options are limited or + * nonexistent. + * + *

To do + * + *

Improve the handling of index out of bounds errors. + * + */ +class U_COMMON_API UVector64 : public UObject { +private: + int32_t count; + + int32_t capacity; + + int32_t maxCapacity; // Limit beyond which capacity is not permitted to grow. + + int64_t* elements; + +public: + UVector64(UErrorCode &status); + + UVector64(int32_t initialCapacity, UErrorCode &status); + + virtual ~UVector64(); + + /** + * Assign this object to another (make this a copy of 'other'). + * Use the 'assign' function to assign each element. + */ + void assign(const UVector64& other, UErrorCode &ec); + + /** + * Compare this vector with another. They will be considered + * equal if they are of the same size and all elements are equal, + * as compared using this object's comparer. + */ + bool operator==(const UVector64& other); + + /** + * Equivalent to !operator==() + */ + inline bool operator!=(const UVector64& other); + + //------------------------------------------------------------ + // subset of java.util.Vector API + //------------------------------------------------------------ + + inline void addElement(int64_t elem, UErrorCode &status); + + void setElementAt(int64_t elem, int32_t index); + + void insertElementAt(int64_t elem, int32_t index, UErrorCode &status); + + inline int64_t elementAti(int32_t index) const; + + //UBool equals(const UVector64 &other) const; + + inline int64_t lastElementi() const; + + //int32_t indexOf(int64_t elem, int32_t startIndex = 0) const; + + //UBool contains(int64_t elem) const; + + //UBool containsAll(const UVector64& other) const; + + //UBool removeAll(const UVector64& other); + + //UBool retainAll(const UVector64& other); + + //void removeElementAt(int32_t index); + + void removeAllElements(); + + inline int32_t size() const; + + inline UBool isEmpty() const { return count == 0; } + + // Inline. Use this one for speedy size check. + inline UBool ensureCapacity(int32_t minimumCapacity, UErrorCode &status); + + // Out-of-line, handles actual growth. Called by ensureCapacity() when necessary. + UBool expandCapacity(int32_t minimumCapacity, UErrorCode &status); + + /** + * Change the size of this vector as follows: If newSize is + * smaller, then truncate the array, possibly deleting held + * elements for i >= newSize. If newSize is larger, grow the + * array, filling in new slows with zero. + */ + void setSize(int32_t newSize); + + //------------------------------------------------------------ + // New API + //------------------------------------------------------------ + + //UBool containsNone(const UVector64& other) const; + + + //void sortedInsert(int64_t elem, UErrorCode& ec); + + /** + * Returns a pointer to the internal array holding the vector. + */ + inline int64_t *getBuffer() const; + + /** + * Set the maximum allowed buffer capacity for this vector/stack. + * Default with no limit set is unlimited, go until malloc() fails. + * A Limit of zero means unlimited capacity. + * Units are vector elements (64 bits each), not bytes. + */ + void setMaxCapacity(int32_t limit); + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + virtual UClassID getDynamicClassID() const override; + +private: + void _init(int32_t initialCapacity, UErrorCode &status); + + // Disallow + UVector64(const UVector64&) = delete; + + // Disallow + UVector64& operator=(const UVector64&) = delete; + + + // API Functions for Stack operations. + // In the original UVector, these were in a separate derived class, UStack. + // Here in UVector64, they are all together. +public: + //UBool empty() const; // TODO: redundant, same as empty(). Remove it? + + //int64_t peeki() const; + + inline int64_t popi(); + + inline int64_t push(int64_t i, UErrorCode &status); + + inline int64_t *reserveBlock(int32_t size, UErrorCode &status); + inline int64_t *popFrame(int32_t size); +}; + + +// UVector64 inlines + +inline UBool UVector64::ensureCapacity(int32_t minimumCapacity, UErrorCode &status) { + if ((minimumCapacity >= 0) && (capacity >= minimumCapacity)) { + return true; + } else { + return expandCapacity(minimumCapacity, status); + } +} + +inline int64_t UVector64::elementAti(int32_t index) const { + return (0 <= index && index < count) ? elements[index] : 0; +} + + +inline void UVector64::addElement(int64_t elem, UErrorCode &status) { + if (ensureCapacity(count + 1, status)) { + elements[count] = elem; + count++; + } +} + +inline int64_t *UVector64::reserveBlock(int32_t size, UErrorCode &status) { + if (ensureCapacity(count+size, status) == false) { + return nullptr; + } + int64_t *rp = elements+count; + count += size; + return rp; +} + +inline int64_t *UVector64::popFrame(int32_t size) { + U_ASSERT(count >= size); + count -= size; + if (count < 0) { + count = 0; + } + return elements+count-size; +} + + + +inline int32_t UVector64::size() const { + return count; +} + +inline int64_t UVector64::lastElementi() const { + return elementAti(count-1); +} + +inline bool UVector64::operator!=(const UVector64& other) { + return !operator==(other); +} + +inline int64_t *UVector64::getBuffer() const { + return elements; +} + + +// UStack inlines + +inline int64_t UVector64::push(int64_t i, UErrorCode &status) { + addElement(i, status); + return i; +} + +inline int64_t UVector64::popi() { + int64_t result = 0; + if (count > 0) { + count--; + result = elements[count]; + } + return result; +} + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/common/wintz.cpp b/deps/icu-small/source/common/wintz.cpp index 1bc08ae6548376..2560471d64ea28 100644 --- a/deps/icu-small/source/common/wintz.cpp +++ b/deps/icu-small/source/common/wintz.cpp @@ -1,331 +1,331 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************** -* Copyright (C) 2005-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************** -* -* File WINTZ.CPP -* -******************************************************************************** -*/ - -#include "unicode/utypes.h" - -#if U_PLATFORM_USES_ONLY_WIN32_API - -#include "wintz.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" - -#include "unicode/ures.h" -#include "unicode/unistr.h" -#include "uresimp.h" - -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -# define VC_EXTRALEAN -# define NOUSER -# define NOSERVICE -# define NOIME -# define NOMCX -#include - -U_NAMESPACE_BEGIN - -// Note these constants and the struct are only used when dealing with the fallback path for RDP sessions. - -// This is the location of the time zones in the registry on Vista+ systems. -// See: https://docs.microsoft.com/windows/win32/api/timezoneapi/ns-timezoneapi-dynamic_time_zone_information -#define WINDOWS_TIMEZONES_REG_KEY_PATH L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones" - -// Max length for a registry key is 255. +1 for null. -// See: https://docs.microsoft.com/windows/win32/sysinfo/registry-element-size-limits -#define WINDOWS_MAX_REG_KEY_LENGTH 256 - -#if U_PLATFORM_HAS_WINUWP_API == 0 - -// This is the layout of the TZI binary value in the registry. -// See: https://docs.microsoft.com/windows/win32/api/timezoneapi/ns-timezoneapi-time_zone_information -typedef struct _REG_TZI_FORMAT { - LONG Bias; - LONG StandardBias; - LONG DaylightBias; - SYSTEMTIME StandardDate; - SYSTEMTIME DaylightDate; -} REG_TZI_FORMAT; - -#endif // U_PLATFORM_HAS_WINUWP_API - -/** -* This is main Windows time zone detection function. -* -* It returns the Windows time zone converted to an ICU time zone as a heap-allocated buffer, or nullptr upon failure. -* -* We use the Win32 API GetDynamicTimeZoneInformation (which is available since Vista) to get the current time zone info, -* as this API returns a non-localized time zone name which can be then mapped to an ICU time zone. -* -* However, in some RDP/terminal services situations, this struct isn't always fully complete, and the TimeZoneKeyName -* field of the struct might be NULL. This can happen with some 3rd party RDP clients, and also when using older versions -* of the RDP protocol, which don't send the newer TimeZoneKeyNamei information and only send the StandardName and DaylightName. -* -* Since these 3rd party clients and older RDP clients only send the pre-Vista time zone information to the server, this means that we -* need to fallback on using the pre-Vista methods to determine the time zone. This unfortunately requires examining the registry directly -* in order to try and determine the current time zone. -* -* Note that this can however still fail in some cases though if the client and server are using different languages, as the StandardName -* that is sent by client is localized in the client's language. However, we must compare this to the names that are on the server, which -* are localized in registry using the server's language. Despite that, this is the best we can do. -* -* Note: This fallback method won't work for the UWP version though, as we can't use the registry APIs in UWP. -* -* Once we have the current Windows time zone, then we can then map it to an ICU time zone ID (~ Olsen ID). -*/ -U_CAPI const char* U_EXPORT2 -uprv_detectWindowsTimeZone() -{ - // We first try to obtain the time zone directly by using the TimeZoneKeyName field of the DYNAMIC_TIME_ZONE_INFORMATION struct. - DYNAMIC_TIME_ZONE_INFORMATION dynamicTZI; - uprv_memset(&dynamicTZI, 0, sizeof(dynamicTZI)); - SYSTEMTIME systemTimeAllZero; - uprv_memset(&systemTimeAllZero, 0, sizeof(systemTimeAllZero)); - - if (GetDynamicTimeZoneInformation(&dynamicTZI) == TIME_ZONE_ID_INVALID) { - return nullptr; - } - - // If the DST setting has been turned off in the Control Panel, then return "Etc/GMT". - // - // Note: This logic is based on how the Control Panel itself determines if DST is 'off' on Windows. - // The code is somewhat convoluted; in a sort of pseudo-code it looks like this: - // - // IF (GetDynamicTimeZoneInformation != TIME_ZONE_ID_INVALID) && (DynamicDaylightTimeDisabled != 0) && - // (StandardDate == DaylightDate) && - // ( - // (TimeZoneKeyName != Empty && StandardDate == 0) || - // (TimeZoneKeyName == Empty && StandardDate != 0) - // ) - // THEN - // DST setting is "Disabled". - // - if (dynamicTZI.DynamicDaylightTimeDisabled != 0 && - uprv_memcmp(&dynamicTZI.StandardDate, &dynamicTZI.DaylightDate, sizeof(dynamicTZI.StandardDate)) == 0 && - ((dynamicTZI.TimeZoneKeyName[0] != L'\0' && uprv_memcmp(&dynamicTZI.StandardDate, &systemTimeAllZero, sizeof(systemTimeAllZero)) == 0) || - (dynamicTZI.TimeZoneKeyName[0] == L'\0' && uprv_memcmp(&dynamicTZI.StandardDate, &systemTimeAllZero, sizeof(systemTimeAllZero)) != 0))) - { - LONG utcOffsetMins = dynamicTZI.Bias; - if (utcOffsetMins == 0) { - return uprv_strdup("Etc/UTC"); - } - - // No way to support when DST is turned off and the offset in minutes is not a multiple of 60. - if (utcOffsetMins % 60 == 0) { - char gmtOffsetTz[11] = {}; // "Etc/GMT+dd" is 11-char long with a terminal null. - // Important note on the sign convention for zones: - // - // From https://en.wikipedia.org/wiki/Tz_database#Area - // "In order to conform with the POSIX style, those zone names beginning with "Etc/GMT" have their sign reversed - // from the standard ISO 8601 convention. In the "Etc" area, zones west of GMT have a positive sign and those - // east have a negative sign in their name (e.g "Etc/GMT-14" is 14 hours ahead of GMT)." - // - // Regarding the POSIX style, from https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html - // "The offset specifies the time value you must add to the local time to get a Coordinated Universal Time value." - // - // However, the Bias value in DYNAMIC_TIME_ZONE_INFORMATION *already* follows the POSIX convention. - // - // From https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/ns-timezoneapi-dynamic_time_zone_information - // "The bias is the difference, in minutes, between Coordinated Universal Time (UTC) and - // local time. All translations between UTC and local time are based on the following formula: - // UTC = local time + bias" - // - // For example, a time zone that is 3 hours ahead of UTC (UTC+03:00) would have a Bias value of -180, and the - // corresponding time zone ID would be "Etc/GMT-3". (So there is no need to negate utcOffsetMins below.) - int ret = snprintf(gmtOffsetTz, UPRV_LENGTHOF(gmtOffsetTz), "Etc/GMT%+ld", utcOffsetMins / 60); - if (ret > 0 && ret < UPRV_LENGTHOF(gmtOffsetTz)) { - return uprv_strdup(gmtOffsetTz); - } - } - } - - // If DST is NOT disabled, but the TimeZoneKeyName field of the struct is NULL, then we may be dealing with a - // RDP/terminal services session where the 'Time Zone Redirection' feature is enabled. However, either the RDP - // client sent the server incomplete info (some 3rd party RDP clients only send the StandardName and DaylightName, - // but do not send the important TimeZoneKeyName), or if the RDP server has not appropriately populated the struct correctly. - // - // In this case we unfortunately have no choice but to fallback to using the pre-Vista method of determining the - // time zone, which requires examining the registry directly. - // - // Note that this can however still fail though if the client and server are using different languages, as the StandardName - // that is sent by client is *localized* in the client's language. However, we must compare this to the names that are - // on the server, which are *localized* in registry using the server's language. - // - // One other note is that this fallback method doesn't work for the UWP version, as we can't use the registry APIs. - - // windowsTimeZoneName will point at timezoneSubKeyName if we had to fallback to using the registry, and we found a match. - WCHAR timezoneSubKeyName[WINDOWS_MAX_REG_KEY_LENGTH]; - WCHAR *windowsTimeZoneName = dynamicTZI.TimeZoneKeyName; - - if (dynamicTZI.TimeZoneKeyName[0] == 0) { - -// We can't use the registry APIs in the UWP version. -#if U_PLATFORM_HAS_WINUWP_API == 1 - (void)timezoneSubKeyName; // suppress unused variable warnings. - return nullptr; -#else - // Open the path to the time zones in the Windows registry. - LONG ret; - HKEY hKeyAllTimeZones = nullptr; - ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, WINDOWS_TIMEZONES_REG_KEY_PATH, 0, KEY_READ, - reinterpret_cast(&hKeyAllTimeZones)); - - if (ret != ERROR_SUCCESS) { - // If we can't open the key, then we can't do much, so fail. - return nullptr; - } - - // Read the number of subkeys under the time zone registry path. - DWORD numTimeZoneSubKeys; - ret = RegQueryInfoKeyW(hKeyAllTimeZones, nullptr, nullptr, nullptr, &numTimeZoneSubKeys, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); - - if (ret != ERROR_SUCCESS) { - RegCloseKey(hKeyAllTimeZones); - return nullptr; - } - - // Examine each of the subkeys to try and find a match for the localized standard name ("Std"). - // - // Note: The name of the time zone subkey itself is not localized, but the "Std" name is localized. This means - // that we could fail to find a match if the RDP client and RDP server are using different languages, but unfortunately - // there isn't much we can do about it. - HKEY hKeyTimeZoneSubKey = nullptr; - ULONG registryValueType; - WCHAR registryStandardName[WINDOWS_MAX_REG_KEY_LENGTH]; - - for (DWORD i = 0; i < numTimeZoneSubKeys; i++) { - // Note: RegEnumKeyExW wants the size of the buffer in characters. - DWORD size = UPRV_LENGTHOF(timezoneSubKeyName); - ret = RegEnumKeyExW(hKeyAllTimeZones, i, timezoneSubKeyName, &size, nullptr, nullptr, nullptr, nullptr); - - if (ret != ERROR_SUCCESS) { - RegCloseKey(hKeyAllTimeZones); - return nullptr; - } - - ret = RegOpenKeyExW(hKeyAllTimeZones, timezoneSubKeyName, 0, KEY_READ, - reinterpret_cast(&hKeyTimeZoneSubKey)); - - if (ret != ERROR_SUCCESS) { - RegCloseKey(hKeyAllTimeZones); - return nullptr; - } - - // Note: RegQueryValueExW wants the size of the buffer in bytes. - size = sizeof(registryStandardName); - ret = RegQueryValueExW(hKeyTimeZoneSubKey, L"Std", nullptr, ®istryValueType, - reinterpret_cast(registryStandardName), &size); - - if (ret != ERROR_SUCCESS || registryValueType != REG_SZ) { - RegCloseKey(hKeyTimeZoneSubKey); - RegCloseKey(hKeyAllTimeZones); - return nullptr; - } - - // Note: wcscmp does an ordinal (byte) comparison. - if (wcscmp(reinterpret_cast(registryStandardName), dynamicTZI.StandardName) == 0) { - // Since we are comparing the *localized* time zone name, it's possible that some languages might use - // the same string for more than one time zone. Thus we need to examine the TZI data in the registry to - // compare the GMT offset (the bias), and the DST transition dates, to ensure it's the same time zone - // as the currently reported one. - REG_TZI_FORMAT registryTziValue; - uprv_memset(®istryTziValue, 0, sizeof(registryTziValue)); - - // Note: RegQueryValueExW wants the size of the buffer in bytes. - DWORD timezoneTziValueSize = sizeof(registryTziValue); - ret = RegQueryValueExW(hKeyTimeZoneSubKey, L"TZI", nullptr, ®istryValueType, - reinterpret_cast(®istryTziValue), &timezoneTziValueSize); - - if (ret == ERROR_SUCCESS) { - if ((dynamicTZI.Bias == registryTziValue.Bias) && - (memcmp((const void *)&dynamicTZI.StandardDate, (const void *)®istryTziValue.StandardDate, sizeof(SYSTEMTIME)) == 0) && - (memcmp((const void *)&dynamicTZI.DaylightDate, (const void *)®istryTziValue.DaylightDate, sizeof(SYSTEMTIME)) == 0)) - { - // We found a matching time zone. - windowsTimeZoneName = timezoneSubKeyName; - break; - } - } - } - RegCloseKey(hKeyTimeZoneSubKey); - hKeyTimeZoneSubKey = nullptr; - } - - if (hKeyTimeZoneSubKey != nullptr) { - RegCloseKey(hKeyTimeZoneSubKey); - } - if (hKeyAllTimeZones != nullptr) { - RegCloseKey(hKeyAllTimeZones); - } -#endif // U_PLATFORM_HAS_WINUWP_API - } - - CharString winTZ; - UErrorCode status = U_ZERO_ERROR; - winTZ.appendInvariantChars(UnicodeString(true, windowsTimeZoneName, -1), status); - - // Map Windows Timezone name (non-localized) to ICU timezone ID (~ Olson timezone id). - StackUResourceBundle winTZBundle; - ures_openDirectFillIn(winTZBundle.getAlias(), nullptr, "windowsZones", &status); - ures_getByKey(winTZBundle.getAlias(), "mapTimezones", winTZBundle.getAlias(), &status); - ures_getByKey(winTZBundle.getAlias(), winTZ.data(), winTZBundle.getAlias(), &status); - - if (U_FAILURE(status)) { - return nullptr; - } - - // Note: Since the ISO 3166 country/region codes are all invariant ASCII chars, we can - // directly downcast from wchar_t to do the conversion. - // We could call the A version of the GetGeoInfo API, but that would be slightly slower than calling the W API, - // as the A version of the API will end up calling MultiByteToWideChar anyways internally. - wchar_t regionCodeW[3] = {}; - char regionCode[3] = {}; // 2 letter ISO 3166 country/region code made entirely of invariant chars. - int geoId = GetUserGeoID(GEOCLASS_NATION); - int regionCodeLen = GetGeoInfoW(geoId, GEO_ISO2, regionCodeW, UPRV_LENGTHOF(regionCodeW), 0); - - const UChar *icuTZ16 = nullptr; - int32_t tzListLen = 0; - - if (regionCodeLen != 0) { - for (int i = 0; i < UPRV_LENGTHOF(regionCodeW); i++) { - regionCode[i] = static_cast(regionCodeW[i]); - } - icuTZ16 = ures_getStringByKey(winTZBundle.getAlias(), regionCode, &tzListLen, &status); - } - if (regionCodeLen == 0 || U_FAILURE(status)) { - // fallback to default "001" (world) - status = U_ZERO_ERROR; - icuTZ16 = ures_getStringByKey(winTZBundle.getAlias(), "001", &tzListLen, &status); - } - - // Note: We want the first entry in the string returned by ures_getStringByKey. - // However this string can be a space delimited list of timezones: - // Ex: "America/New_York America/Detroit America/Indiana/Petersburg ..." - // We need to stop at the first space, so we pass tzLen (instead of tzListLen) to appendInvariantChars below. - int32_t tzLen = 0; - if (tzListLen > 0) { - while (!(icuTZ16[tzLen] == u'\0' || icuTZ16[tzLen] == u' ')) { - tzLen++; - } - } - - // Note: cloneData returns nullptr if the status is a failure, so this - // will return nullptr if the above look-up fails. - CharString icuTZStr; - return icuTZStr.appendInvariantChars(icuTZ16, tzLen, status).cloneData(status); -} - -U_NAMESPACE_END -#endif /* U_PLATFORM_USES_ONLY_WIN32_API */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************** +* Copyright (C) 2005-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************** +* +* File WINTZ.CPP +* +******************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if U_PLATFORM_USES_ONLY_WIN32_API + +#include "wintz.h" +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" + +#include "unicode/ures.h" +#include "unicode/unistr.h" +#include "uresimp.h" + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +# define VC_EXTRALEAN +# define NOUSER +# define NOSERVICE +# define NOIME +# define NOMCX +#include + +U_NAMESPACE_BEGIN + +// Note these constants and the struct are only used when dealing with the fallback path for RDP sessions. + +// This is the location of the time zones in the registry on Vista+ systems. +// See: https://docs.microsoft.com/windows/win32/api/timezoneapi/ns-timezoneapi-dynamic_time_zone_information +#define WINDOWS_TIMEZONES_REG_KEY_PATH L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones" + +// Max length for a registry key is 255. +1 for null. +// See: https://docs.microsoft.com/windows/win32/sysinfo/registry-element-size-limits +#define WINDOWS_MAX_REG_KEY_LENGTH 256 + +#if U_PLATFORM_HAS_WINUWP_API == 0 + +// This is the layout of the TZI binary value in the registry. +// See: https://docs.microsoft.com/windows/win32/api/timezoneapi/ns-timezoneapi-time_zone_information +typedef struct _REG_TZI_FORMAT { + LONG Bias; + LONG StandardBias; + LONG DaylightBias; + SYSTEMTIME StandardDate; + SYSTEMTIME DaylightDate; +} REG_TZI_FORMAT; + +#endif // U_PLATFORM_HAS_WINUWP_API + +/** +* This is main Windows time zone detection function. +* +* It returns the Windows time zone converted to an ICU time zone as a heap-allocated buffer, or nullptr upon failure. +* +* We use the Win32 API GetDynamicTimeZoneInformation (which is available since Vista) to get the current time zone info, +* as this API returns a non-localized time zone name which can be then mapped to an ICU time zone. +* +* However, in some RDP/terminal services situations, this struct isn't always fully complete, and the TimeZoneKeyName +* field of the struct might be nullptr. This can happen with some 3rd party RDP clients, and also when using older versions +* of the RDP protocol, which don't send the newer TimeZoneKeyNamei information and only send the StandardName and DaylightName. +* +* Since these 3rd party clients and older RDP clients only send the pre-Vista time zone information to the server, this means that we +* need to fallback on using the pre-Vista methods to determine the time zone. This unfortunately requires examining the registry directly +* in order to try and determine the current time zone. +* +* Note that this can however still fail in some cases though if the client and server are using different languages, as the StandardName +* that is sent by client is localized in the client's language. However, we must compare this to the names that are on the server, which +* are localized in registry using the server's language. Despite that, this is the best we can do. +* +* Note: This fallback method won't work for the UWP version though, as we can't use the registry APIs in UWP. +* +* Once we have the current Windows time zone, then we can then map it to an ICU time zone ID (~ Olsen ID). +*/ +U_CAPI const char* U_EXPORT2 +uprv_detectWindowsTimeZone() +{ + // We first try to obtain the time zone directly by using the TimeZoneKeyName field of the DYNAMIC_TIME_ZONE_INFORMATION struct. + DYNAMIC_TIME_ZONE_INFORMATION dynamicTZI; + uprv_memset(&dynamicTZI, 0, sizeof(dynamicTZI)); + SYSTEMTIME systemTimeAllZero; + uprv_memset(&systemTimeAllZero, 0, sizeof(systemTimeAllZero)); + + if (GetDynamicTimeZoneInformation(&dynamicTZI) == TIME_ZONE_ID_INVALID) { + return nullptr; + } + + // If the DST setting has been turned off in the Control Panel, then return "Etc/GMT". + // + // Note: This logic is based on how the Control Panel itself determines if DST is 'off' on Windows. + // The code is somewhat convoluted; in a sort of pseudo-code it looks like this: + // + // IF (GetDynamicTimeZoneInformation != TIME_ZONE_ID_INVALID) && (DynamicDaylightTimeDisabled != 0) && + // (StandardDate == DaylightDate) && + // ( + // (TimeZoneKeyName != Empty && StandardDate == 0) || + // (TimeZoneKeyName == Empty && StandardDate != 0) + // ) + // THEN + // DST setting is "Disabled". + // + if (dynamicTZI.DynamicDaylightTimeDisabled != 0 && + uprv_memcmp(&dynamicTZI.StandardDate, &dynamicTZI.DaylightDate, sizeof(dynamicTZI.StandardDate)) == 0 && + ((dynamicTZI.TimeZoneKeyName[0] != L'\0' && uprv_memcmp(&dynamicTZI.StandardDate, &systemTimeAllZero, sizeof(systemTimeAllZero)) == 0) || + (dynamicTZI.TimeZoneKeyName[0] == L'\0' && uprv_memcmp(&dynamicTZI.StandardDate, &systemTimeAllZero, sizeof(systemTimeAllZero)) != 0))) + { + LONG utcOffsetMins = dynamicTZI.Bias; + if (utcOffsetMins == 0) { + return uprv_strdup("Etc/UTC"); + } + + // No way to support when DST is turned off and the offset in minutes is not a multiple of 60. + if (utcOffsetMins % 60 == 0) { + char gmtOffsetTz[11] = {}; // "Etc/GMT+dd" is 11-char long with a terminal null. + // Important note on the sign convention for zones: + // + // From https://en.wikipedia.org/wiki/Tz_database#Area + // "In order to conform with the POSIX style, those zone names beginning with "Etc/GMT" have their sign reversed + // from the standard ISO 8601 convention. In the "Etc" area, zones west of GMT have a positive sign and those + // east have a negative sign in their name (e.g "Etc/GMT-14" is 14 hours ahead of GMT)." + // + // Regarding the POSIX style, from https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + // "The offset specifies the time value you must add to the local time to get a Coordinated Universal Time value." + // + // However, the Bias value in DYNAMIC_TIME_ZONE_INFORMATION *already* follows the POSIX convention. + // + // From https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/ns-timezoneapi-dynamic_time_zone_information + // "The bias is the difference, in minutes, between Coordinated Universal Time (UTC) and + // local time. All translations between UTC and local time are based on the following formula: + // UTC = local time + bias" + // + // For example, a time zone that is 3 hours ahead of UTC (UTC+03:00) would have a Bias value of -180, and the + // corresponding time zone ID would be "Etc/GMT-3". (So there is no need to negate utcOffsetMins below.) + int ret = snprintf(gmtOffsetTz, sizeof(gmtOffsetTz), "Etc/GMT%+ld", utcOffsetMins / 60); + if (ret > 0 && ret < UPRV_LENGTHOF(gmtOffsetTz)) { + return uprv_strdup(gmtOffsetTz); + } + } + } + + // If DST is NOT disabled, but the TimeZoneKeyName field of the struct is nullptr, then we may be dealing with a + // RDP/terminal services session where the 'Time Zone Redirection' feature is enabled. However, either the RDP + // client sent the server incomplete info (some 3rd party RDP clients only send the StandardName and DaylightName, + // but do not send the important TimeZoneKeyName), or if the RDP server has not appropriately populated the struct correctly. + // + // In this case we unfortunately have no choice but to fallback to using the pre-Vista method of determining the + // time zone, which requires examining the registry directly. + // + // Note that this can however still fail though if the client and server are using different languages, as the StandardName + // that is sent by client is *localized* in the client's language. However, we must compare this to the names that are + // on the server, which are *localized* in registry using the server's language. + // + // One other note is that this fallback method doesn't work for the UWP version, as we can't use the registry APIs. + + // windowsTimeZoneName will point at timezoneSubKeyName if we had to fallback to using the registry, and we found a match. + WCHAR timezoneSubKeyName[WINDOWS_MAX_REG_KEY_LENGTH]; + WCHAR *windowsTimeZoneName = dynamicTZI.TimeZoneKeyName; + + if (dynamicTZI.TimeZoneKeyName[0] == 0) { + +// We can't use the registry APIs in the UWP version. +#if U_PLATFORM_HAS_WINUWP_API == 1 + (void)timezoneSubKeyName; // suppress unused variable warnings. + return nullptr; +#else + // Open the path to the time zones in the Windows registry. + LONG ret; + HKEY hKeyAllTimeZones = nullptr; + ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, WINDOWS_TIMEZONES_REG_KEY_PATH, 0, KEY_READ, + reinterpret_cast(&hKeyAllTimeZones)); + + if (ret != ERROR_SUCCESS) { + // If we can't open the key, then we can't do much, so fail. + return nullptr; + } + + // Read the number of subkeys under the time zone registry path. + DWORD numTimeZoneSubKeys; + ret = RegQueryInfoKeyW(hKeyAllTimeZones, nullptr, nullptr, nullptr, &numTimeZoneSubKeys, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + + if (ret != ERROR_SUCCESS) { + RegCloseKey(hKeyAllTimeZones); + return nullptr; + } + + // Examine each of the subkeys to try and find a match for the localized standard name ("Std"). + // + // Note: The name of the time zone subkey itself is not localized, but the "Std" name is localized. This means + // that we could fail to find a match if the RDP client and RDP server are using different languages, but unfortunately + // there isn't much we can do about it. + HKEY hKeyTimeZoneSubKey = nullptr; + ULONG registryValueType; + WCHAR registryStandardName[WINDOWS_MAX_REG_KEY_LENGTH]; + + for (DWORD i = 0; i < numTimeZoneSubKeys; i++) { + // Note: RegEnumKeyExW wants the size of the buffer in characters. + DWORD size = UPRV_LENGTHOF(timezoneSubKeyName); + ret = RegEnumKeyExW(hKeyAllTimeZones, i, timezoneSubKeyName, &size, nullptr, nullptr, nullptr, nullptr); + + if (ret != ERROR_SUCCESS) { + RegCloseKey(hKeyAllTimeZones); + return nullptr; + } + + ret = RegOpenKeyExW(hKeyAllTimeZones, timezoneSubKeyName, 0, KEY_READ, + reinterpret_cast(&hKeyTimeZoneSubKey)); + + if (ret != ERROR_SUCCESS) { + RegCloseKey(hKeyAllTimeZones); + return nullptr; + } + + // Note: RegQueryValueExW wants the size of the buffer in bytes. + size = sizeof(registryStandardName); + ret = RegQueryValueExW(hKeyTimeZoneSubKey, L"Std", nullptr, ®istryValueType, + reinterpret_cast(registryStandardName), &size); + + if (ret != ERROR_SUCCESS || registryValueType != REG_SZ) { + RegCloseKey(hKeyTimeZoneSubKey); + RegCloseKey(hKeyAllTimeZones); + return nullptr; + } + + // Note: wcscmp does an ordinal (byte) comparison. + if (wcscmp(reinterpret_cast(registryStandardName), dynamicTZI.StandardName) == 0) { + // Since we are comparing the *localized* time zone name, it's possible that some languages might use + // the same string for more than one time zone. Thus we need to examine the TZI data in the registry to + // compare the GMT offset (the bias), and the DST transition dates, to ensure it's the same time zone + // as the currently reported one. + REG_TZI_FORMAT registryTziValue; + uprv_memset(®istryTziValue, 0, sizeof(registryTziValue)); + + // Note: RegQueryValueExW wants the size of the buffer in bytes. + DWORD timezoneTziValueSize = sizeof(registryTziValue); + ret = RegQueryValueExW(hKeyTimeZoneSubKey, L"TZI", nullptr, ®istryValueType, + reinterpret_cast(®istryTziValue), &timezoneTziValueSize); + + if (ret == ERROR_SUCCESS) { + if ((dynamicTZI.Bias == registryTziValue.Bias) && + (memcmp((const void *)&dynamicTZI.StandardDate, (const void *)®istryTziValue.StandardDate, sizeof(SYSTEMTIME)) == 0) && + (memcmp((const void *)&dynamicTZI.DaylightDate, (const void *)®istryTziValue.DaylightDate, sizeof(SYSTEMTIME)) == 0)) + { + // We found a matching time zone. + windowsTimeZoneName = timezoneSubKeyName; + break; + } + } + } + RegCloseKey(hKeyTimeZoneSubKey); + hKeyTimeZoneSubKey = nullptr; + } + + if (hKeyTimeZoneSubKey != nullptr) { + RegCloseKey(hKeyTimeZoneSubKey); + } + if (hKeyAllTimeZones != nullptr) { + RegCloseKey(hKeyAllTimeZones); + } +#endif // U_PLATFORM_HAS_WINUWP_API + } + + CharString winTZ; + UErrorCode status = U_ZERO_ERROR; + winTZ.appendInvariantChars(UnicodeString(true, windowsTimeZoneName, -1), status); + + // Map Windows Timezone name (non-localized) to ICU timezone ID (~ Olson timezone id). + StackUResourceBundle winTZBundle; + ures_openDirectFillIn(winTZBundle.getAlias(), nullptr, "windowsZones", &status); + ures_getByKey(winTZBundle.getAlias(), "mapTimezones", winTZBundle.getAlias(), &status); + ures_getByKey(winTZBundle.getAlias(), winTZ.data(), winTZBundle.getAlias(), &status); + + if (U_FAILURE(status)) { + return nullptr; + } + + // Note: Since the ISO 3166 country/region codes are all invariant ASCII chars, we can + // directly downcast from wchar_t to do the conversion. + // We could call the A version of the GetGeoInfo API, but that would be slightly slower than calling the W API, + // as the A version of the API will end up calling MultiByteToWideChar anyways internally. + wchar_t regionCodeW[3] = {}; + char regionCode[3] = {}; // 2 letter ISO 3166 country/region code made entirely of invariant chars. + int geoId = GetUserGeoID(GEOCLASS_NATION); + int regionCodeLen = GetGeoInfoW(geoId, GEO_ISO2, regionCodeW, UPRV_LENGTHOF(regionCodeW), 0); + + const char16_t *icuTZ16 = nullptr; + int32_t tzListLen = 0; + + if (regionCodeLen != 0) { + for (int i = 0; i < UPRV_LENGTHOF(regionCodeW); i++) { + regionCode[i] = static_cast(regionCodeW[i]); + } + icuTZ16 = ures_getStringByKey(winTZBundle.getAlias(), regionCode, &tzListLen, &status); + } + if (regionCodeLen == 0 || U_FAILURE(status)) { + // fallback to default "001" (world) + status = U_ZERO_ERROR; + icuTZ16 = ures_getStringByKey(winTZBundle.getAlias(), "001", &tzListLen, &status); + } + + // Note: We want the first entry in the string returned by ures_getStringByKey. + // However this string can be a space delimited list of timezones: + // Ex: "America/New_York America/Detroit America/Indiana/Petersburg ..." + // We need to stop at the first space, so we pass tzLen (instead of tzListLen) to appendInvariantChars below. + int32_t tzLen = 0; + if (tzListLen > 0) { + while (!(icuTZ16[tzLen] == u'\0' || icuTZ16[tzLen] == u' ')) { + tzLen++; + } + } + + // Note: cloneData returns nullptr if the status is a failure, so this + // will return nullptr if the above look-up fails. + CharString icuTZStr; + return icuTZStr.appendInvariantChars(icuTZ16, tzLen, status).cloneData(status); +} + +U_NAMESPACE_END +#endif /* U_PLATFORM_USES_ONLY_WIN32_API */ diff --git a/deps/icu-small/source/common/wintz.h b/deps/icu-small/source/common/wintz.h index ce9c1e901928d7..719fcbe427768c 100644 --- a/deps/icu-small/source/common/wintz.h +++ b/deps/icu-small/source/common/wintz.h @@ -1,36 +1,36 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************** -* Copyright (C) 2005-2011, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************** -* -* File WINTZ.H -* -******************************************************************************** -*/ - -#ifndef __WINTZ -#define __WINTZ - -#include "unicode/utypes.h" - -#if U_PLATFORM_USES_ONLY_WIN32_API - -/** - * \file - * \brief C API: Utilities for dealing w/ Windows time zones. - */ - -U_CDECL_BEGIN -/* Forward declarations for Windows types... */ -typedef struct _TIME_ZONE_INFORMATION TIME_ZONE_INFORMATION; -U_CDECL_END - -U_CAPI const char* U_EXPORT2 -uprv_detectWindowsTimeZone(); - -#endif /* U_PLATFORM_USES_ONLY_WIN32_API */ - -#endif /* __WINTZ */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************** +* Copyright (C) 2005-2011, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************** +* +* File WINTZ.H +* +******************************************************************************** +*/ + +#ifndef __WINTZ +#define __WINTZ + +#include "unicode/utypes.h" + +#if U_PLATFORM_USES_ONLY_WIN32_API + +/** + * \file + * \brief C API: Utilities for dealing w/ Windows time zones. + */ + +U_CDECL_BEGIN +/* Forward declarations for Windows types... */ +typedef struct _TIME_ZONE_INFORMATION TIME_ZONE_INFORMATION; +U_CDECL_END + +U_CAPI const char* U_EXPORT2 +uprv_detectWindowsTimeZone(); + +#endif /* U_PLATFORM_USES_ONLY_WIN32_API */ + +#endif /* __WINTZ */ diff --git a/deps/icu-small/source/data/in/icudt72l.dat.bz2 b/deps/icu-small/source/data/in/icudt73l.dat.bz2 similarity index 57% rename from deps/icu-small/source/data/in/icudt72l.dat.bz2 rename to deps/icu-small/source/data/in/icudt73l.dat.bz2 index eb26b48458649d..0dc16df033e520 100644 Binary files a/deps/icu-small/source/data/in/icudt72l.dat.bz2 and b/deps/icu-small/source/data/in/icudt73l.dat.bz2 differ diff --git a/deps/icu-small/source/i18n/BUILD.bazel b/deps/icu-small/source/i18n/BUILD.bazel index 2d85cdb180e128..be10695ad1880e 100644 --- a/deps/icu-small/source/i18n/BUILD.bazel +++ b/deps/icu-small/source/i18n/BUILD.bazel @@ -1,130 +1,130 @@ -# © 2021 and later: Unicode, Inc. and others. -# License & terms of use: http://www.unicode.org/copyright.html - -# This file defines Bazel targets for a subset of the ICU4C "i18n" library header and source files. -# The configuration of dependencies among targets is strongly assisted by the -# file in depstest that maintains such information, at -# icu4c/source/test/depstest/dependencies.txt . - -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") - -package( - default_visibility = ["//visibility:public"], -) - -# When compiling code in the `common` dir, the constant -# `U_I18n_IMPLEMENTATION` needs to be defined. See -# https://unicode-org.github.io/icu/userguide/howtouseicu#c-with-your-own-build-system . - -# If linker errors occur, then this may be a sign that the dependencies were -# not specified correctly. Use dependencies.txt in depstest for assistance. See -# https://stackoverflow.com/q/66111709/2077918 . - -cc_library( - name = "headers", - hdrs = glob([ - "unicode/*.h", # public - "*.h", # internal - ]), - # We need to add includes in order to preserve existing source files' - # include directives that use traditional paths, not paths relative to - # Bazel workspace: - # https://stackoverflow.com/a/65635893/2077918 - includes = ["."], - local_defines = [ - "U_I18N_IMPLEMENTATION", - ], -) - -cc_library( - name = "collation", - srcs = [ - "bocsu.cpp", - "coleitr.cpp", - "coll.cpp", - "collation.cpp", - "collationcompare.cpp", - "collationdata.cpp", - "collationdatareader.cpp", - "collationdatawriter.cpp", - "collationfastlatin.cpp", - # collationfcd.cpp is generated by genuca; - # probably hard to build genuca without depending on the old version. - "collationfcd.cpp", - "collationiterator.cpp", - "collationkeys.cpp", - "collationroot.cpp", - "collationrootelements.cpp", - "collationsets.cpp", - "collationsettings.cpp", - "collationtailoring.cpp", - "rulebasedcollator.cpp", - "sortkey.cpp", - "ucol.cpp", - "ucol_res.cpp", - "ucol_sit.cpp", - "ucoleitr.cpp", - "uitercollationiterator.cpp", - "utf16collationiterator.cpp", - "utf8collationiterator.cpp", - ], - includes = ["."], - deps = [ - ":headers", - ":uclean_i18n", - "//icu4c/source/common:bytestream", - "//icu4c/source/common:normalizer2", - "//icu4c/source/common:platform", - "//icu4c/source/common:propname", - "//icu4c/source/common:resourcebundle", - "//icu4c/source/common:service_registration", - "//icu4c/source/common:ucharstrieiterator", - "//icu4c/source/common:uiter", - "//icu4c/source/common:ulist", - "//icu4c/source/common:unifiedcache", - "//icu4c/source/common:uset", - "//icu4c/source/common:usetiter", - "//icu4c/source/common:utrie2", - "//icu4c/source/common:uvector32", - "//icu4c/source/common:uvector64", - ], - local_defines = [ - "U_I18N_IMPLEMENTATION", - ], -) - -cc_library( - name = "collation_builder", - srcs = [ - "collationbuilder.cpp", - "collationdatabuilder.cpp", - "collationfastlatinbuilder.cpp", - "collationruleparser.cpp", - "collationweights.cpp", - ], - includes = ["."], - deps = [ - ":collation", - "//icu4c/source/common:canonical_iterator", - "//icu4c/source/common:ucharstriebuilder", - "//icu4c/source/common:uset_props" - ], - local_defines = [ - "U_I18N_IMPLEMENTATION", - ], -) - -cc_library( - name = "uclean_i18n", - srcs = [ - "ucln_in.cpp", - ], - hdrs = ["ucln_in.h"], - includes = ["."], - deps = [ - "//icu4c/source/common:platform", - ], - local_defines = [ - "U_I18N_IMPLEMENTATION", - ], -) +# © 2021 and later: Unicode, Inc. and others. +# License & terms of use: http://www.unicode.org/copyright.html + +# This file defines Bazel targets for a subset of the ICU4C "i18n" library header and source files. +# The configuration of dependencies among targets is strongly assisted by the +# file in depstest that maintains such information, at +# icu4c/source/test/depstest/dependencies.txt . + +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") + +package( + default_visibility = ["//visibility:public"], +) + +# When compiling code in the `common` dir, the constant +# `U_I18n_IMPLEMENTATION` needs to be defined. See +# https://unicode-org.github.io/icu/userguide/howtouseicu#c-with-your-own-build-system . + +# If linker errors occur, then this may be a sign that the dependencies were +# not specified correctly. Use dependencies.txt in depstest for assistance. See +# https://stackoverflow.com/q/66111709/2077918 . + +cc_library( + name = "headers", + hdrs = glob([ + "unicode/*.h", # public + "*.h", # internal + ]), + # We need to add includes in order to preserve existing source files' + # include directives that use traditional paths, not paths relative to + # Bazel workspace: + # https://stackoverflow.com/a/65635893/2077918 + includes = ["."], + local_defines = [ + "U_I18N_IMPLEMENTATION", + ], +) + +cc_library( + name = "collation", + srcs = [ + "bocsu.cpp", + "coleitr.cpp", + "coll.cpp", + "collation.cpp", + "collationcompare.cpp", + "collationdata.cpp", + "collationdatareader.cpp", + "collationdatawriter.cpp", + "collationfastlatin.cpp", + # collationfcd.cpp is generated by genuca; + # probably hard to build genuca without depending on the old version. + "collationfcd.cpp", + "collationiterator.cpp", + "collationkeys.cpp", + "collationroot.cpp", + "collationrootelements.cpp", + "collationsets.cpp", + "collationsettings.cpp", + "collationtailoring.cpp", + "rulebasedcollator.cpp", + "sortkey.cpp", + "ucol.cpp", + "ucol_res.cpp", + "ucol_sit.cpp", + "ucoleitr.cpp", + "uitercollationiterator.cpp", + "utf16collationiterator.cpp", + "utf8collationiterator.cpp", + ], + includes = ["."], + deps = [ + ":headers", + ":uclean_i18n", + "//icu4c/source/common:bytestream", + "//icu4c/source/common:normalizer2", + "//icu4c/source/common:platform", + "//icu4c/source/common:propname", + "//icu4c/source/common:resourcebundle", + "//icu4c/source/common:service_registration", + "//icu4c/source/common:ucharstrieiterator", + "//icu4c/source/common:uiter", + "//icu4c/source/common:ulist", + "//icu4c/source/common:unifiedcache", + "//icu4c/source/common:uset", + "//icu4c/source/common:usetiter", + "//icu4c/source/common:utrie2", + "//icu4c/source/common:uvector32", + "//icu4c/source/common:uvector64", + ], + local_defines = [ + "U_I18N_IMPLEMENTATION", + ], +) + +cc_library( + name = "collation_builder", + srcs = [ + "collationbuilder.cpp", + "collationdatabuilder.cpp", + "collationfastlatinbuilder.cpp", + "collationruleparser.cpp", + "collationweights.cpp", + ], + includes = ["."], + deps = [ + ":collation", + "//icu4c/source/common:canonical_iterator", + "//icu4c/source/common:ucharstriebuilder", + "//icu4c/source/common:uset_props" + ], + local_defines = [ + "U_I18N_IMPLEMENTATION", + ], +) + +cc_library( + name = "uclean_i18n", + srcs = [ + "ucln_in.cpp", + ], + hdrs = ["ucln_in.h"], + includes = ["."], + deps = [ + "//icu4c/source/common:platform", + ], + local_defines = [ + "U_I18N_IMPLEMENTATION", + ], +) diff --git a/deps/icu-small/source/i18n/alphaindex.cpp b/deps/icu-small/source/i18n/alphaindex.cpp index 16b9d99395b4a5..b99a0eef920db2 100644 --- a/deps/icu-small/source/i18n/alphaindex.cpp +++ b/deps/icu-small/source/i18n/alphaindex.cpp @@ -1,1235 +1,1235 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2009-2014, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/alphaindex.h" -#include "unicode/coll.h" -#include "unicode/localpointer.h" -#include "unicode/normalizer2.h" -#include "unicode/tblcoll.h" -#include "unicode/uchar.h" -#include "unicode/ulocdata.h" -#include "unicode/uniset.h" -#include "unicode/uobject.h" -#include "unicode/usetiter.h" -#include "unicode/utf16.h" - -#include "cmemory.h" -#include "cstring.h" -#include "uassert.h" -#include "uvector.h" -#include "uvectr64.h" - -//#include -//#include - -U_NAMESPACE_BEGIN - -namespace { - -/** - * Prefix string for Chinese index buckets. - * See http://unicode.org/repos/cldr/trunk/specs/ldml/tr35-collation.html#Collation_Indexes - */ -const UChar BASE[1] = { 0xFDD0 }; -const int32_t BASE_LENGTH = 1; - -UBool isOneLabelBetterThanOther(const Normalizer2 &nfkdNormalizer, - const UnicodeString &one, const UnicodeString &other); - -} // namespace - -static int32_t U_CALLCONV -collatorComparator(const void *context, const void *left, const void *right); - -static int32_t U_CALLCONV -recordCompareFn(const void *context, const void *left, const void *right); - -// UVector support function, delete a Record. -static void U_CALLCONV -alphaIndex_deleteRecord(void *obj) { - delete static_cast(obj); -} - -namespace { - -UnicodeString *ownedString(const UnicodeString &s, LocalPointer &owned, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return NULL; } - if (owned.isValid()) { - return owned.orphan(); - } - UnicodeString *p = new UnicodeString(s); - if (p == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - } - return p; -} - -inline UnicodeString *getString(const UVector &list, int32_t i) { - return static_cast(list[i]); -} - -inline AlphabeticIndex::Bucket *getBucket(const UVector &list, int32_t i) { - return static_cast(list[i]); -} - -inline AlphabeticIndex::Record *getRecord(const UVector &list, int32_t i) { - return static_cast(list[i]); -} - -/** - * Like Java Collections.binarySearch(List, String, Comparator). - * - * @return the index>=0 where the item was found, - * or the index<0 for inserting the string at ~index in sorted order - */ -int32_t binarySearch(const UVector &list, const UnicodeString &s, const Collator &coll) { - if (list.size() == 0) { return ~0; } - int32_t start = 0; - int32_t limit = list.size(); - for (;;) { - int32_t i = (start + limit) / 2; - const UnicodeString *si = static_cast(list.elementAt(i)); - UErrorCode errorCode = U_ZERO_ERROR; - UCollationResult cmp = coll.compare(s, *si, errorCode); - if (cmp == UCOL_EQUAL) { - return i; - } else if (cmp < 0) { - if (i == start) { - return ~start; // insert s before *si - } - limit = i; - } else { - if (i == start) { - return ~(start + 1); // insert s after *si - } - start = i; - } - } -} - -} // namespace - -// The BucketList is not in the anonymous namespace because only Clang -// seems to support its use in other classes from there. -// However, we also don't need U_I18N_API because it is not used from outside the i18n library. -class BucketList : public UObject { -public: - BucketList(UVector *bucketList, UVector *publicBucketList) - : bucketList_(bucketList), immutableVisibleList_(publicBucketList) { - int32_t displayIndex = 0; - for (int32_t i = 0; i < publicBucketList->size(); ++i) { - getBucket(*publicBucketList, i)->displayIndex_ = displayIndex++; - } - } - - // The virtual destructor must not be inline. - // See ticket #8454 for details. - virtual ~BucketList(); - - int32_t getBucketCount() const { - return immutableVisibleList_->size(); - } - - int32_t getBucketIndex(const UnicodeString &name, const Collator &collatorPrimaryOnly, - UErrorCode &errorCode) { - // binary search - int32_t start = 0; - int32_t limit = bucketList_->size(); - while ((start + 1) < limit) { - int32_t i = (start + limit) / 2; - const AlphabeticIndex::Bucket *bucket = getBucket(*bucketList_, i); - UCollationResult nameVsBucket = - collatorPrimaryOnly.compare(name, bucket->lowerBoundary_, errorCode); - if (nameVsBucket < 0) { - limit = i; - } else { - start = i; - } - } - const AlphabeticIndex::Bucket *bucket = getBucket(*bucketList_, start); - if (bucket->displayBucket_ != NULL) { - bucket = bucket->displayBucket_; - } - return bucket->displayIndex_; - } - - /** All of the buckets, visible and invisible. */ - UVector *bucketList_; - /** Just the visible buckets. */ - UVector *immutableVisibleList_; -}; - -BucketList::~BucketList() { - delete bucketList_; - if (immutableVisibleList_ != bucketList_) { - delete immutableVisibleList_; - } -} - -AlphabeticIndex::ImmutableIndex::~ImmutableIndex() { - delete buckets_; - delete collatorPrimaryOnly_; -} - -int32_t -AlphabeticIndex::ImmutableIndex::getBucketCount() const { - return buckets_->getBucketCount(); -} - -int32_t -AlphabeticIndex::ImmutableIndex::getBucketIndex( - const UnicodeString &name, UErrorCode &errorCode) const { - return buckets_->getBucketIndex(name, *collatorPrimaryOnly_, errorCode); -} - -const AlphabeticIndex::Bucket * -AlphabeticIndex::ImmutableIndex::getBucket(int32_t index) const { - if (0 <= index && index < buckets_->getBucketCount()) { - return icu::getBucket(*buckets_->immutableVisibleList_, index); - } else { - return NULL; - } -} - -AlphabeticIndex::AlphabeticIndex(const Locale &locale, UErrorCode &status) - : inputList_(NULL), - labelsIterIndex_(-1), itemsIterIndex_(0), currentBucket_(NULL), - maxLabelCount_(99), - initialLabels_(NULL), firstCharsInScripts_(NULL), - collator_(NULL), collatorPrimaryOnly_(NULL), - buckets_(NULL) { - init(&locale, status); -} - - -AlphabeticIndex::AlphabeticIndex(RuleBasedCollator *collator, UErrorCode &status) - : inputList_(NULL), - labelsIterIndex_(-1), itemsIterIndex_(0), currentBucket_(NULL), - maxLabelCount_(99), - initialLabels_(NULL), firstCharsInScripts_(NULL), - collator_(collator), collatorPrimaryOnly_(NULL), - buckets_(NULL) { - init(NULL, status); -} - - - -AlphabeticIndex::~AlphabeticIndex() { - delete collator_; - delete collatorPrimaryOnly_; - delete firstCharsInScripts_; - delete buckets_; - delete inputList_; - delete initialLabels_; -} - - -AlphabeticIndex &AlphabeticIndex::addLabels(const UnicodeSet &additions, UErrorCode &status) { - if (U_FAILURE(status)) { - return *this; - } - initialLabels_->addAll(additions); - clearBuckets(); - return *this; -} - - -AlphabeticIndex &AlphabeticIndex::addLabels(const Locale &locale, UErrorCode &status) { - addIndexExemplars(locale, status); - clearBuckets(); - return *this; -} - - -AlphabeticIndex::ImmutableIndex *AlphabeticIndex::buildImmutableIndex(UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return NULL; } - // In C++, the ImmutableIndex must own its copy of the BucketList, - // even if it contains no records, for proper memory management. - // We could clone the buckets_ if they are not NULL, - // but that would be worth it only if this method is called multiple times, - // or called after using the old-style bucket iterator API. - LocalPointer immutableBucketList(createBucketList(errorCode)); - LocalPointer coll(collatorPrimaryOnly_->clone()); - if (immutableBucketList.isNull() || coll.isNull()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - ImmutableIndex *immIndex = new ImmutableIndex(immutableBucketList.getAlias(), coll.getAlias()); - if (immIndex == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - // The ImmutableIndex adopted its parameter objects. - immutableBucketList.orphan(); - coll.orphan(); - return immIndex; -} - -int32_t AlphabeticIndex::getBucketCount(UErrorCode &status) { - initBuckets(status); - if (U_FAILURE(status)) { - return 0; - } - return buckets_->getBucketCount(); -} - - -int32_t AlphabeticIndex::getRecordCount(UErrorCode &status) { - if (U_FAILURE(status) || inputList_ == NULL) { - return 0; - } - return inputList_->size(); -} - -void AlphabeticIndex::initLabels(UVector &indexCharacters, UErrorCode &errorCode) const { - U_ASSERT(indexCharacters.hasDeleter()); - const Normalizer2 *nfkdNormalizer = Normalizer2::getNFKDInstance(errorCode); - if (U_FAILURE(errorCode)) { return; } - - const UnicodeString &firstScriptBoundary = *getString(*firstCharsInScripts_, 0); - const UnicodeString &overflowBoundary = - *getString(*firstCharsInScripts_, firstCharsInScripts_->size() - 1); - - // We make a sorted array of elements. - // Some of the input may be redundant. - // That is, we might have c, ch, d, where "ch" sorts just like "c", "h". - // We filter out those cases. - UnicodeSetIterator iter(*initialLabels_); - while (U_SUCCESS(errorCode) && iter.next()) { - const UnicodeString *item = &iter.getString(); - LocalPointer ownedItem; - UBool checkDistinct; - int32_t itemLength = item->length(); - if (!item->hasMoreChar32Than(0, itemLength, 1)) { - checkDistinct = false; - } else if(item->charAt(itemLength - 1) == 0x2a && // '*' - item->charAt(itemLength - 2) != 0x2a) { - // Use a label if it is marked with one trailing star, - // even if the label string sorts the same when all contractions are suppressed. - ownedItem.adoptInstead(new UnicodeString(*item, 0, itemLength - 1)); - item = ownedItem.getAlias(); - if (item == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - checkDistinct = false; - } else { - checkDistinct = true; - } - if (collatorPrimaryOnly_->compare(*item, firstScriptBoundary, errorCode) < 0) { - // Ignore a primary-ignorable or non-alphabetic index character. - } else if (collatorPrimaryOnly_->compare(*item, overflowBoundary, errorCode) >= 0) { - // Ignore an index character that will land in the overflow bucket. - } else if (checkDistinct && - collatorPrimaryOnly_->compare(*item, separated(*item), errorCode) == 0) { - // Ignore a multi-code point index character that does not sort distinctly - // from the sequence of its separate characters. - } else { - int32_t insertionPoint = binarySearch(indexCharacters, *item, *collatorPrimaryOnly_); - if (insertionPoint < 0) { - indexCharacters.insertElementAt( - ownedString(*item, ownedItem, errorCode), ~insertionPoint, errorCode); - } else { - const UnicodeString &itemAlreadyIn = *getString(indexCharacters, insertionPoint); - if (isOneLabelBetterThanOther(*nfkdNormalizer, *item, itemAlreadyIn)) { - indexCharacters.setElementAt( - ownedString(*item, ownedItem, errorCode), insertionPoint); - } - } - } - } - if (U_FAILURE(errorCode)) { return; } - - // if the result is still too large, cut down to maxLabelCount_ elements, by removing every nth element - - int32_t size = indexCharacters.size() - 1; - if (size > maxLabelCount_) { - int32_t count = 0; - int32_t old = -1; - for (int32_t i = 0; i < indexCharacters.size();) { - ++count; - int32_t bump = count * maxLabelCount_ / size; - if (bump == old) { - indexCharacters.removeElementAt(i); - } else { - old = bump; - ++i; - } - } - } -} - -namespace { - -const UnicodeString &fixLabel(const UnicodeString ¤t, UnicodeString &temp) { - if (!current.startsWith(BASE, BASE_LENGTH)) { - return current; - } - UChar rest = current.charAt(BASE_LENGTH); - if (0x2800 < rest && rest <= 0x28FF) { // stroke count - int32_t count = rest-0x2800; - temp.setTo((UChar)(0x30 + count % 10)); - if (count >= 10) { - count /= 10; - temp.insert(0, (UChar)(0x30 + count % 10)); - if (count >= 10) { - count /= 10; - temp.insert(0, (UChar)(0x30 + count)); - } - } - return temp.append((UChar)0x5283); - } - return temp.setTo(current, BASE_LENGTH); -} - -UBool hasMultiplePrimaryWeights( - const RuleBasedCollator &coll, uint32_t variableTop, - const UnicodeString &s, UVector64 &ces, UErrorCode &errorCode) { - ces.removeAllElements(); - coll.internalGetCEs(s, ces, errorCode); - if (U_FAILURE(errorCode)) { return false; } - UBool seenPrimary = false; - for (int32_t i = 0; i < ces.size(); ++i) { - int64_t ce = ces.elementAti(i); - uint32_t p = (uint32_t)(ce >> 32); - if (p > variableTop) { - // not primary ignorable - if (seenPrimary) { - return true; - } - seenPrimary = true; - } - } - return false; -} - -} // namespace - -BucketList *AlphabeticIndex::createBucketList(UErrorCode &errorCode) const { - // Initialize indexCharacters. - UVector indexCharacters(errorCode); - indexCharacters.setDeleter(uprv_deleteUObject); - initLabels(indexCharacters, errorCode); - if (U_FAILURE(errorCode)) { return NULL; } - - // Variables for hasMultiplePrimaryWeights(). - UVector64 ces(errorCode); - uint32_t variableTop; - if (collatorPrimaryOnly_->getAttribute(UCOL_ALTERNATE_HANDLING, errorCode) == UCOL_SHIFTED) { - variableTop = collatorPrimaryOnly_->getVariableTop(errorCode); - } else { - variableTop = 0; - } - UBool hasInvisibleBuckets = false; - - // Helper arrays for Chinese Pinyin collation. - Bucket *asciiBuckets[26] = { - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL - }; - Bucket *pinyinBuckets[26] = { - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL - }; - UBool hasPinyin = false; - - LocalPointer bucketList(new UVector(errorCode), errorCode); - if (U_FAILURE(errorCode)) { - return NULL; - } - bucketList->setDeleter(uprv_deleteUObject); - - // underflow bucket - LocalPointer bucket(new Bucket(getUnderflowLabel(), emptyString_, U_ALPHAINDEX_UNDERFLOW), errorCode); - if (U_FAILURE(errorCode)) { - return NULL; - } - bucketList->adoptElement(bucket.orphan(), errorCode); - if (U_FAILURE(errorCode)) { return NULL; } - - UnicodeString temp; - - // fix up the list, adding underflow, additions, overflow - // Insert inflow labels as needed. - int32_t scriptIndex = -1; - const UnicodeString *scriptUpperBoundary = &emptyString_; - for (int32_t i = 0; i < indexCharacters.size(); ++i) { - UnicodeString ¤t = *getString(indexCharacters, i); - if (collatorPrimaryOnly_->compare(current, *scriptUpperBoundary, errorCode) >= 0) { - // We crossed the script boundary into a new script. - const UnicodeString &inflowBoundary = *scriptUpperBoundary; - UBool skippedScript = false; - for (;;) { - scriptUpperBoundary = getString(*firstCharsInScripts_, ++scriptIndex); - if (collatorPrimaryOnly_->compare(current, *scriptUpperBoundary, errorCode) < 0) { - break; - } - skippedScript = true; - } - if (skippedScript && bucketList->size() > 1) { - // We are skipping one or more scripts, - // and we are not just getting out of the underflow label. - bucket.adoptInsteadAndCheckErrorCode( - new Bucket(getInflowLabel(), inflowBoundary, U_ALPHAINDEX_INFLOW), errorCode); - bucketList->adoptElement(bucket.orphan(), errorCode); - if (U_FAILURE(errorCode)) { return nullptr; } - } - } - // Add a bucket with the current label. - bucket.adoptInsteadAndCheckErrorCode( - new Bucket(fixLabel(current, temp), current, U_ALPHAINDEX_NORMAL), errorCode); - bucketList->adoptElement(bucket.orphan(), errorCode); - if (U_FAILURE(errorCode)) { return nullptr; } - // Remember ASCII and Pinyin buckets for Pinyin redirects. - UChar c; - if (current.length() == 1 && 0x41 <= (c = current.charAt(0)) && c <= 0x5A) { // A-Z - asciiBuckets[c - 0x41] = (Bucket *)bucketList->lastElement(); - } else if (current.length() == BASE_LENGTH + 1 && current.startsWith(BASE, BASE_LENGTH) && - 0x41 <= (c = current.charAt(BASE_LENGTH)) && c <= 0x5A) { - pinyinBuckets[c - 0x41] = (Bucket *)bucketList->lastElement(); - hasPinyin = true; - } - // Check for multiple primary weights. - if (!current.startsWith(BASE, BASE_LENGTH) && - hasMultiplePrimaryWeights(*collatorPrimaryOnly_, variableTop, current, - ces, errorCode) && - current.charAt(current.length() - 1) != 0xFFFF /* !current.endsWith("\uffff") */) { - // "AE-ligature" or "Sch" etc. - for (int32_t j = bucketList->size() - 2;; --j) { - Bucket *singleBucket = getBucket(*bucketList, j); - if (singleBucket->labelType_ != U_ALPHAINDEX_NORMAL) { - // There is no single-character bucket since the last - // underflow or inflow label. - break; - } - if (singleBucket->displayBucket_ == NULL && - !hasMultiplePrimaryWeights(*collatorPrimaryOnly_, variableTop, - singleBucket->lowerBoundary_, - ces, errorCode)) { - // Add an invisible bucket that redirects strings greater than the expansion - // to the previous single-character bucket. - // For example, after ... Q R S Sch we add Sch\uFFFF->S - // and after ... Q R S Sch Sch\uFFFF St we add St\uFFFF->S. - bucket.adoptInsteadAndCheckErrorCode(new Bucket(emptyString_, - UnicodeString(current).append((UChar)0xFFFF), - U_ALPHAINDEX_NORMAL), - errorCode); - if (U_FAILURE(errorCode)) { - return NULL; - } - bucket->displayBucket_ = singleBucket; - bucketList->adoptElement(bucket.orphan(), errorCode); - if (U_FAILURE(errorCode)) { return nullptr; } - hasInvisibleBuckets = true; - break; - } - } - } - } - if (U_FAILURE(errorCode)) { return NULL; } - if (bucketList->size() == 1) { - // No real labels, show only the underflow label. - BucketList *bl = new BucketList(bucketList.getAlias(), bucketList.getAlias()); - if (bl == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - bucketList.orphan(); - return bl; - } - // overflow bucket - bucket.adoptInsteadAndCheckErrorCode( - new Bucket(getOverflowLabel(), *scriptUpperBoundary, U_ALPHAINDEX_OVERFLOW), errorCode); - bucketList->adoptElement(bucket.orphan(), errorCode); // final - if (U_FAILURE(errorCode)) { return nullptr; } - - if (hasPinyin) { - // Redirect Pinyin buckets. - Bucket *asciiBucket = NULL; - for (int32_t i = 0; i < 26; ++i) { - if (asciiBuckets[i] != NULL) { - asciiBucket = asciiBuckets[i]; - } - if (pinyinBuckets[i] != NULL && asciiBucket != NULL) { - pinyinBuckets[i]->displayBucket_ = asciiBucket; - hasInvisibleBuckets = true; - } - } - } - - if (U_FAILURE(errorCode)) { return NULL; } - if (!hasInvisibleBuckets) { - BucketList *bl = new BucketList(bucketList.getAlias(), bucketList.getAlias()); - if (bl == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - bucketList.orphan(); - return bl; - } - // Merge inflow buckets that are visually adjacent. - // Iterate backwards: Merge inflow into overflow rather than the other way around. - int32_t i = bucketList->size() - 1; - Bucket *nextBucket = getBucket(*bucketList, i); - while (--i > 0) { - Bucket *bucket = getBucket(*bucketList, i); - if (bucket->displayBucket_ != NULL) { - continue; // skip invisible buckets - } - if (bucket->labelType_ == U_ALPHAINDEX_INFLOW) { - if (nextBucket->labelType_ != U_ALPHAINDEX_NORMAL) { - bucket->displayBucket_ = nextBucket; - continue; - } - } - nextBucket = bucket; - } - - LocalPointer publicBucketList(new UVector(errorCode), errorCode); - if (U_FAILURE(errorCode)) { - return NULL; - } - // Do not call publicBucketList->setDeleter(): - // This vector shares its objects with the bucketList. - for (int32_t j = 0; j < bucketList->size(); ++j) { - Bucket *bucket = getBucket(*bucketList, j); - if (bucket->displayBucket_ == NULL) { - publicBucketList->addElement(bucket, errorCode); - } - } - if (U_FAILURE(errorCode)) { return NULL; } - BucketList *bl = new BucketList(bucketList.getAlias(), publicBucketList.getAlias()); - if (bl == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - bucketList.orphan(); - publicBucketList.orphan(); - return bl; -} - -/** - * Creates an index, and buckets and sorts the list of records into the index. - */ -void AlphabeticIndex::initBuckets(UErrorCode &errorCode) { - if (U_FAILURE(errorCode) || buckets_ != NULL) { - return; - } - buckets_ = createBucketList(errorCode); - if (U_FAILURE(errorCode) || inputList_ == NULL || inputList_->isEmpty()) { - return; - } - - // Sort the records by name. - // Stable sort preserves input order of collation duplicates. - inputList_->sortWithUComparator(recordCompareFn, collator_, errorCode); - - // Now, we traverse all of the input, which is now sorted. - // If the item doesn't go in the current bucket, we find the next bucket that contains it. - // This makes the process order n*log(n), since we just sort the list and then do a linear process. - // However, if the user adds an item at a time and then gets the buckets, this isn't efficient, so - // we need to improve it for that case. - - Bucket *currentBucket = getBucket(*buckets_->bucketList_, 0); - int32_t bucketIndex = 1; - Bucket *nextBucket; - const UnicodeString *upperBoundary; - if (bucketIndex < buckets_->bucketList_->size()) { - nextBucket = getBucket(*buckets_->bucketList_, bucketIndex++); - upperBoundary = &nextBucket->lowerBoundary_; - } else { - nextBucket = NULL; - upperBoundary = NULL; - } - for (int32_t i = 0; i < inputList_->size(); ++i) { - Record *r = getRecord(*inputList_, i); - // if the current bucket isn't the right one, find the one that is - // We have a special flag for the last bucket so that we don't look any further - while (upperBoundary != NULL && - collatorPrimaryOnly_->compare(r->name_, *upperBoundary, errorCode) >= 0) { - currentBucket = nextBucket; - // now reset the boundary that we compare against - if (bucketIndex < buckets_->bucketList_->size()) { - nextBucket = getBucket(*buckets_->bucketList_, bucketIndex++); - upperBoundary = &nextBucket->lowerBoundary_; - } else { - upperBoundary = NULL; - } - } - // now put the record into the bucket. - Bucket *bucket = currentBucket; - if (bucket->displayBucket_ != NULL) { - bucket = bucket->displayBucket_; - } - if (bucket->records_ == NULL) { - LocalPointer records(new UVector(errorCode), errorCode); - if (U_FAILURE(errorCode)) { - return; - } - bucket->records_ = records.orphan(); - } - bucket->records_->addElement(r, errorCode); - } -} - -void AlphabeticIndex::clearBuckets() { - if (buckets_ != NULL) { - delete buckets_; - buckets_ = NULL; - internalResetBucketIterator(); - } -} - -void AlphabeticIndex::internalResetBucketIterator() { - labelsIterIndex_ = -1; - currentBucket_ = NULL; -} - - -void AlphabeticIndex::addIndexExemplars(const Locale &locale, UErrorCode &status) { - LocalULocaleDataPointer uld(ulocdata_open(locale.getName(), &status)); - if (U_FAILURE(status)) { - return; - } - - UnicodeSet exemplars; - ulocdata_getExemplarSet(uld.getAlias(), exemplars.toUSet(), 0, ULOCDATA_ES_INDEX, &status); - if (U_SUCCESS(status)) { - initialLabels_->addAll(exemplars); - return; - } - status = U_ZERO_ERROR; // Clear out U_MISSING_RESOURCE_ERROR - - // The locale data did not include explicit Index characters. - // Synthesize a set of them from the locale's standard exemplar characters. - ulocdata_getExemplarSet(uld.getAlias(), exemplars.toUSet(), 0, ULOCDATA_ES_STANDARD, &status); - if (U_FAILURE(status)) { - return; - } - - // question: should we add auxiliary exemplars? - if (exemplars.containsSome(0x61, 0x7A) /* a-z */ || exemplars.isEmpty()) { - exemplars.add(0x61, 0x7A); - } - if (exemplars.containsSome(0xAC00, 0xD7A3)) { // Hangul syllables - // cut down to small list - exemplars.remove(0xAC00, 0xD7A3). - add(0xAC00).add(0xB098).add(0xB2E4).add(0xB77C). - add(0xB9C8).add(0xBC14).add(0xC0AC).add(0xC544). - add(0xC790).add(0xCC28).add(0xCE74).add(0xD0C0). - add(0xD30C).add(0xD558); - } - if (exemplars.containsSome(0x1200, 0x137F)) { // Ethiopic block - // cut down to small list - // make use of the fact that Ethiopic is allocated in 8's, where - // the base is 0 mod 8. - UnicodeSet ethiopic(UnicodeString(u"[ሀለሐመሠረሰሸቀቈቐቘበቨተቸኀኈነኘአከኰኸዀወዐዘዠየደዸጀገጐጘጠጨጰጸፀፈፐፘ]"), status); - ethiopic.retainAll(exemplars); - exemplars.remove(u'ሀ', 0x137F).addAll(ethiopic); - } - - // Upper-case any that aren't already so. - // (We only do this for synthesized index characters.) - UnicodeSetIterator it(exemplars); - UnicodeString upperC; - while (it.next()) { - const UnicodeString &exemplarC = it.getString(); - upperC = exemplarC; - upperC.toUpper(locale); - initialLabels_->add(upperC); - } -} - -UBool AlphabeticIndex::addChineseIndexCharacters(UErrorCode &errorCode) { - UnicodeSet contractions; - collatorPrimaryOnly_->internalAddContractions(BASE[0], contractions, errorCode); - if (U_FAILURE(errorCode) || contractions.isEmpty()) { return false; } - initialLabels_->addAll(contractions); - UnicodeSetIterator iter(contractions); - while (iter.next()) { - const UnicodeString &s = iter.getString(); - U_ASSERT (s.startsWith(BASE, BASE_LENGTH)); - UChar c = s.charAt(s.length() - 1); - if (0x41 <= c && c <= 0x5A) { // A-Z - // There are Pinyin labels, add ASCII A-Z labels as well. - initialLabels_->add(0x41, 0x5A); // A-Z - break; - } - } - return true; -} - - -/* - * Return the string with interspersed CGJs. Input must have more than 2 codepoints. - */ -static const UChar CGJ = 0x034F; -UnicodeString AlphabeticIndex::separated(const UnicodeString &item) { - UnicodeString result; - if (item.length() == 0) { - return result; - } - int32_t i = 0; - for (;;) { - UChar32 cp = item.char32At(i); - result.append(cp); - i = item.moveIndex32(i, 1); - if (i >= item.length()) { - break; - } - result.append(CGJ); - } - return result; -} - - -bool AlphabeticIndex::operator==(const AlphabeticIndex& /* other */) const { - return false; -} - - -bool AlphabeticIndex::operator!=(const AlphabeticIndex& /* other */) const { - return false; -} - - -const RuleBasedCollator &AlphabeticIndex::getCollator() const { - return *collator_; -} - - -const UnicodeString &AlphabeticIndex::getInflowLabel() const { - return inflowLabel_; -} - -const UnicodeString &AlphabeticIndex::getOverflowLabel() const { - return overflowLabel_; -} - - -const UnicodeString &AlphabeticIndex::getUnderflowLabel() const { - return underflowLabel_; -} - - -AlphabeticIndex &AlphabeticIndex::setInflowLabel(const UnicodeString &label, UErrorCode &/*status*/) { - inflowLabel_ = label; - clearBuckets(); - return *this; -} - - -AlphabeticIndex &AlphabeticIndex::setOverflowLabel(const UnicodeString &label, UErrorCode &/*status*/) { - overflowLabel_ = label; - clearBuckets(); - return *this; -} - - -AlphabeticIndex &AlphabeticIndex::setUnderflowLabel(const UnicodeString &label, UErrorCode &/*status*/) { - underflowLabel_ = label; - clearBuckets(); - return *this; -} - - -int32_t AlphabeticIndex::getMaxLabelCount() const { - return maxLabelCount_; -} - - -AlphabeticIndex &AlphabeticIndex::setMaxLabelCount(int32_t maxLabelCount, UErrorCode &status) { - if (U_FAILURE(status)) { - return *this; - } - if (maxLabelCount <= 0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return *this; - } - maxLabelCount_ = maxLabelCount; - clearBuckets(); - return *this; -} - - -// -// init() - Common code for constructors. -// - -void AlphabeticIndex::init(const Locale *locale, UErrorCode &status) { - if (U_FAILURE(status)) { return; } - if (locale == NULL && collator_ == NULL) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - initialLabels_ = new UnicodeSet(); - if (initialLabels_ == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - inflowLabel_.setTo((UChar)0x2026); // Ellipsis - overflowLabel_ = inflowLabel_; - underflowLabel_ = inflowLabel_; - - if (collator_ == NULL) { - Collator *coll = Collator::createInstance(*locale, status); - if (U_FAILURE(status)) { - delete coll; - return; - } - if (coll == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - collator_ = dynamic_cast(coll); - if (collator_ == NULL) { - delete coll; - status = U_UNSUPPORTED_ERROR; - return; - } - } - collatorPrimaryOnly_ = collator_->clone(); - if (collatorPrimaryOnly_ == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - collatorPrimaryOnly_->setAttribute(UCOL_STRENGTH, UCOL_PRIMARY, status); - firstCharsInScripts_ = firstStringsInScript(status); - if (U_FAILURE(status)) { return; } - firstCharsInScripts_->sortWithUComparator(collatorComparator, collatorPrimaryOnly_, status); - // Guard against a degenerate collator where - // some script boundary strings are primary ignorable. - for (;;) { - if (U_FAILURE(status)) { return; } - if (firstCharsInScripts_->isEmpty()) { - // AlphabeticIndex requires some non-ignorable script boundary strings. - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (collatorPrimaryOnly_->compare( - *static_cast(firstCharsInScripts_->elementAt(0)), - emptyString_, status) == UCOL_EQUAL) { - firstCharsInScripts_->removeElementAt(0); - } else { - break; - } - } - - // Chinese index characters, which are specific to each of the several Chinese tailorings, - // take precedence over the single locale data exemplar set per language. - if (!addChineseIndexCharacters(status) && locale != NULL) { - addIndexExemplars(*locale, status); - } -} - - -// -// Comparison function for UVector sorting with a collator. -// -static int32_t U_CALLCONV -collatorComparator(const void *context, const void *left, const void *right) { - const UElement *leftElement = static_cast(left); - const UElement *rightElement = static_cast(right); - const UnicodeString *leftString = static_cast(leftElement->pointer); - const UnicodeString *rightString = static_cast(rightElement->pointer); - - if (leftString == rightString) { - // Catches case where both are NULL - return 0; - } - if (leftString == NULL) { - return 1; - } - if (rightString == NULL) { - return -1; - } - const Collator *col = static_cast(context); - UErrorCode errorCode = U_ZERO_ERROR; - return col->compare(*leftString, *rightString, errorCode); -} - -// -// Comparison function for UVector sorting with a collator. -// -static int32_t U_CALLCONV -recordCompareFn(const void *context, const void *left, const void *right) { - const UElement *leftElement = static_cast(left); - const UElement *rightElement = static_cast(right); - const AlphabeticIndex::Record *leftRec = static_cast(leftElement->pointer); - const AlphabeticIndex::Record *rightRec = static_cast(rightElement->pointer); - const Collator *col = static_cast(context); - UErrorCode errorCode = U_ZERO_ERROR; - return col->compare(leftRec->name_, rightRec->name_, errorCode); -} - -UVector *AlphabeticIndex::firstStringsInScript(UErrorCode &status) { - if (U_FAILURE(status)) { - return NULL; - } - LocalPointer dest(new UVector(status), status); - if (U_FAILURE(status)) { - return NULL; - } - dest->setDeleter(uprv_deleteUObject); - // Fetch the script-first-primary contractions which are defined in the root collator. - // They all start with U+FDD1. - UnicodeSet set; - collatorPrimaryOnly_->internalAddContractions(0xFDD1, set, status); - if (U_FAILURE(status)) { - return NULL; - } - if (set.isEmpty()) { - status = U_UNSUPPORTED_ERROR; - return NULL; - } - UnicodeSetIterator iter(set); - while (iter.next()) { - const UnicodeString &boundary = iter.getString(); - uint32_t gcMask = U_GET_GC_MASK(boundary.char32At(1)); - if ((gcMask & (U_GC_L_MASK | U_GC_CN_MASK)) == 0) { - // Ignore boundaries for the special reordering groups. - // Take only those for "real scripts" (where the sample character is a Letter, - // and the one for unassigned implicit weights (Cn). - continue; - } - LocalPointer s(new UnicodeString(boundary), status); - dest->adoptElement(s.orphan(), status); - if (U_FAILURE(status)) { - return nullptr; - } - } - return dest.orphan(); -} - - -namespace { - -/** - * Returns true if one index character string is "better" than the other. - * Shorter NFKD is better, and otherwise NFKD-binary-less-than is - * better, and otherwise binary-less-than is better. - */ -UBool isOneLabelBetterThanOther(const Normalizer2 &nfkdNormalizer, - const UnicodeString &one, const UnicodeString &other) { - // This is called with primary-equal strings, but never with one.equals(other). - UErrorCode status = U_ZERO_ERROR; - UnicodeString n1 = nfkdNormalizer.normalize(one, status); - UnicodeString n2 = nfkdNormalizer.normalize(other, status); - if (U_FAILURE(status)) { return false; } - int32_t result = n1.countChar32() - n2.countChar32(); - if (result != 0) { - return result < 0; - } - result = n1.compareCodePointOrder(n2); - if (result != 0) { - return result < 0; - } - return one.compareCodePointOrder(other) < 0; -} - -} // namespace - -// -// Constructor & Destructor for AlphabeticIndex::Record -// -// Records are internal only, instances are not directly surfaced in the public API. -// This class is mostly struct-like, with all public fields. - -AlphabeticIndex::Record::Record(const UnicodeString &name, const void *data) - : name_(name), data_(data) {} - -AlphabeticIndex::Record::~Record() { -} - - -AlphabeticIndex & AlphabeticIndex::addRecord(const UnicodeString &name, const void *data, UErrorCode &status) { - if (U_FAILURE(status)) { - return *this; - } - if (inputList_ == NULL) { - LocalPointer inputList(new UVector(status), status); - if (U_FAILURE(status)) { - return *this; - } - inputList_ = inputList.orphan(); - inputList_->setDeleter(alphaIndex_deleteRecord); - } - LocalPointer r(new Record(name, data), status); - inputList_->adoptElement(r.orphan(), status); - if (U_FAILURE(status)) { - return *this; - } - clearBuckets(); - //std::string ss; - //std::string ss2; - //std::cout << "added record: name = \"" << r->name_.toUTF8String(ss) << "\"" << - // " sortingName = \"" << r->sortingName_.toUTF8String(ss2) << "\"" << std::endl; - return *this; -} - - -AlphabeticIndex &AlphabeticIndex::clearRecords(UErrorCode &status) { - if (U_SUCCESS(status) && inputList_ != NULL && !inputList_->isEmpty()) { - inputList_->removeAllElements(); - clearBuckets(); - } - return *this; -} - -int32_t AlphabeticIndex::getBucketIndex(const UnicodeString &name, UErrorCode &status) { - initBuckets(status); - if (U_FAILURE(status)) { - return 0; - } - return buckets_->getBucketIndex(name, *collatorPrimaryOnly_, status); -} - - -int32_t AlphabeticIndex::getBucketIndex() const { - return labelsIterIndex_; -} - - -UBool AlphabeticIndex::nextBucket(UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - if (buckets_ == NULL && currentBucket_ != NULL) { - status = U_ENUM_OUT_OF_SYNC_ERROR; - return false; - } - initBuckets(status); - if (U_FAILURE(status)) { - return false; - } - ++labelsIterIndex_; - if (labelsIterIndex_ >= buckets_->getBucketCount()) { - labelsIterIndex_ = buckets_->getBucketCount(); - return false; - } - currentBucket_ = getBucket(*buckets_->immutableVisibleList_, labelsIterIndex_); - resetRecordIterator(); - return true; -} - -const UnicodeString &AlphabeticIndex::getBucketLabel() const { - if (currentBucket_ != NULL) { - return currentBucket_->label_; - } else { - return emptyString_; - } -} - - -UAlphabeticIndexLabelType AlphabeticIndex::getBucketLabelType() const { - if (currentBucket_ != NULL) { - return currentBucket_->labelType_; - } else { - return U_ALPHAINDEX_NORMAL; - } -} - - -int32_t AlphabeticIndex::getBucketRecordCount() const { - if (currentBucket_ != NULL && currentBucket_->records_ != NULL) { - return currentBucket_->records_->size(); - } else { - return 0; - } -} - - -AlphabeticIndex &AlphabeticIndex::resetBucketIterator(UErrorCode &status) { - if (U_FAILURE(status)) { - return *this; - } - internalResetBucketIterator(); - return *this; -} - - -UBool AlphabeticIndex::nextRecord(UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - if (currentBucket_ == NULL) { - // We are trying to iterate over the items in a bucket, but there is no - // current bucket from the enumeration of buckets. - status = U_INVALID_STATE_ERROR; - return false; - } - if (buckets_ == NULL) { - status = U_ENUM_OUT_OF_SYNC_ERROR; - return false; - } - if (currentBucket_->records_ == NULL) { - return false; - } - ++itemsIterIndex_; - if (itemsIterIndex_ >= currentBucket_->records_->size()) { - itemsIterIndex_ = currentBucket_->records_->size(); - return false; - } - return true; -} - - -const UnicodeString &AlphabeticIndex::getRecordName() const { - const UnicodeString *retStr = &emptyString_; - if (currentBucket_ != NULL && currentBucket_->records_ != NULL && - itemsIterIndex_ >= 0 && - itemsIterIndex_ < currentBucket_->records_->size()) { - Record *item = static_cast(currentBucket_->records_->elementAt(itemsIterIndex_)); - retStr = &item->name_; - } - return *retStr; -} - -const void *AlphabeticIndex::getRecordData() const { - const void *retPtr = NULL; - if (currentBucket_ != NULL && currentBucket_->records_ != NULL && - itemsIterIndex_ >= 0 && - itemsIterIndex_ < currentBucket_->records_->size()) { - Record *item = static_cast(currentBucket_->records_->elementAt(itemsIterIndex_)); - retPtr = item->data_; - } - return retPtr; -} - - -AlphabeticIndex & AlphabeticIndex::resetRecordIterator() { - itemsIterIndex_ = -1; - return *this; -} - - - -AlphabeticIndex::Bucket::Bucket(const UnicodeString &label, - const UnicodeString &lowerBoundary, - UAlphabeticIndexLabelType type) - : label_(label), lowerBoundary_(lowerBoundary), labelType_(type), - displayBucket_(NULL), displayIndex_(-1), - records_(NULL) { -} - - -AlphabeticIndex::Bucket::~Bucket() { - delete records_; -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2009-2014, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/alphaindex.h" +#include "unicode/coll.h" +#include "unicode/localpointer.h" +#include "unicode/normalizer2.h" +#include "unicode/tblcoll.h" +#include "unicode/uchar.h" +#include "unicode/ulocdata.h" +#include "unicode/uniset.h" +#include "unicode/uobject.h" +#include "unicode/usetiter.h" +#include "unicode/utf16.h" + +#include "cmemory.h" +#include "cstring.h" +#include "uassert.h" +#include "uvector.h" +#include "uvectr64.h" + +//#include +//#include + +U_NAMESPACE_BEGIN + +namespace { + +/** + * Prefix string for Chinese index buckets. + * See http://unicode.org/repos/cldr/trunk/specs/ldml/tr35-collation.html#Collation_Indexes + */ +const char16_t BASE[1] = { 0xFDD0 }; +const int32_t BASE_LENGTH = 1; + +UBool isOneLabelBetterThanOther(const Normalizer2 &nfkdNormalizer, + const UnicodeString &one, const UnicodeString &other); + +} // namespace + +static int32_t U_CALLCONV +collatorComparator(const void *context, const void *left, const void *right); + +static int32_t U_CALLCONV +recordCompareFn(const void *context, const void *left, const void *right); + +// UVector support function, delete a Record. +static void U_CALLCONV +alphaIndex_deleteRecord(void *obj) { + delete static_cast(obj); +} + +namespace { + +UnicodeString *ownedString(const UnicodeString &s, LocalPointer &owned, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return nullptr; } + if (owned.isValid()) { + return owned.orphan(); + } + UnicodeString *p = new UnicodeString(s); + if (p == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + } + return p; +} + +inline UnicodeString *getString(const UVector &list, int32_t i) { + return static_cast(list[i]); +} + +inline AlphabeticIndex::Bucket *getBucket(const UVector &list, int32_t i) { + return static_cast(list[i]); +} + +inline AlphabeticIndex::Record *getRecord(const UVector &list, int32_t i) { + return static_cast(list[i]); +} + +/** + * Like Java Collections.binarySearch(List, String, Comparator). + * + * @return the index>=0 where the item was found, + * or the index<0 for inserting the string at ~index in sorted order + */ +int32_t binarySearch(const UVector &list, const UnicodeString &s, const Collator &coll) { + if (list.size() == 0) { return ~0; } + int32_t start = 0; + int32_t limit = list.size(); + for (;;) { + int32_t i = (start + limit) / 2; + const UnicodeString *si = static_cast(list.elementAt(i)); + UErrorCode errorCode = U_ZERO_ERROR; + UCollationResult cmp = coll.compare(s, *si, errorCode); + if (cmp == UCOL_EQUAL) { + return i; + } else if (cmp < 0) { + if (i == start) { + return ~start; // insert s before *si + } + limit = i; + } else { + if (i == start) { + return ~(start + 1); // insert s after *si + } + start = i; + } + } +} + +} // namespace + +// The BucketList is not in the anonymous namespace because only Clang +// seems to support its use in other classes from there. +// However, we also don't need U_I18N_API because it is not used from outside the i18n library. +class BucketList : public UObject { +public: + BucketList(UVector *bucketList, UVector *publicBucketList) + : bucketList_(bucketList), immutableVisibleList_(publicBucketList) { + int32_t displayIndex = 0; + for (int32_t i = 0; i < publicBucketList->size(); ++i) { + getBucket(*publicBucketList, i)->displayIndex_ = displayIndex++; + } + } + + // The virtual destructor must not be inline. + // See ticket #8454 for details. + virtual ~BucketList(); + + int32_t getBucketCount() const { + return immutableVisibleList_->size(); + } + + int32_t getBucketIndex(const UnicodeString &name, const Collator &collatorPrimaryOnly, + UErrorCode &errorCode) { + // binary search + int32_t start = 0; + int32_t limit = bucketList_->size(); + while ((start + 1) < limit) { + int32_t i = (start + limit) / 2; + const AlphabeticIndex::Bucket *bucket = getBucket(*bucketList_, i); + UCollationResult nameVsBucket = + collatorPrimaryOnly.compare(name, bucket->lowerBoundary_, errorCode); + if (nameVsBucket < 0) { + limit = i; + } else { + start = i; + } + } + const AlphabeticIndex::Bucket *bucket = getBucket(*bucketList_, start); + if (bucket->displayBucket_ != nullptr) { + bucket = bucket->displayBucket_; + } + return bucket->displayIndex_; + } + + /** All of the buckets, visible and invisible. */ + UVector *bucketList_; + /** Just the visible buckets. */ + UVector *immutableVisibleList_; +}; + +BucketList::~BucketList() { + delete bucketList_; + if (immutableVisibleList_ != bucketList_) { + delete immutableVisibleList_; + } +} + +AlphabeticIndex::ImmutableIndex::~ImmutableIndex() { + delete buckets_; + delete collatorPrimaryOnly_; +} + +int32_t +AlphabeticIndex::ImmutableIndex::getBucketCount() const { + return buckets_->getBucketCount(); +} + +int32_t +AlphabeticIndex::ImmutableIndex::getBucketIndex( + const UnicodeString &name, UErrorCode &errorCode) const { + return buckets_->getBucketIndex(name, *collatorPrimaryOnly_, errorCode); +} + +const AlphabeticIndex::Bucket * +AlphabeticIndex::ImmutableIndex::getBucket(int32_t index) const { + if (0 <= index && index < buckets_->getBucketCount()) { + return icu::getBucket(*buckets_->immutableVisibleList_, index); + } else { + return nullptr; + } +} + +AlphabeticIndex::AlphabeticIndex(const Locale &locale, UErrorCode &status) + : inputList_(nullptr), + labelsIterIndex_(-1), itemsIterIndex_(0), currentBucket_(nullptr), + maxLabelCount_(99), + initialLabels_(nullptr), firstCharsInScripts_(nullptr), + collator_(nullptr), collatorPrimaryOnly_(nullptr), + buckets_(nullptr) { + init(&locale, status); +} + + +AlphabeticIndex::AlphabeticIndex(RuleBasedCollator *collator, UErrorCode &status) + : inputList_(nullptr), + labelsIterIndex_(-1), itemsIterIndex_(0), currentBucket_(nullptr), + maxLabelCount_(99), + initialLabels_(nullptr), firstCharsInScripts_(nullptr), + collator_(collator), collatorPrimaryOnly_(nullptr), + buckets_(nullptr) { + init(nullptr, status); +} + + + +AlphabeticIndex::~AlphabeticIndex() { + delete collator_; + delete collatorPrimaryOnly_; + delete firstCharsInScripts_; + delete buckets_; + delete inputList_; + delete initialLabels_; +} + + +AlphabeticIndex &AlphabeticIndex::addLabels(const UnicodeSet &additions, UErrorCode &status) { + if (U_FAILURE(status)) { + return *this; + } + initialLabels_->addAll(additions); + clearBuckets(); + return *this; +} + + +AlphabeticIndex &AlphabeticIndex::addLabels(const Locale &locale, UErrorCode &status) { + addIndexExemplars(locale, status); + clearBuckets(); + return *this; +} + + +AlphabeticIndex::ImmutableIndex *AlphabeticIndex::buildImmutableIndex(UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return nullptr; } + // In C++, the ImmutableIndex must own its copy of the BucketList, + // even if it contains no records, for proper memory management. + // We could clone the buckets_ if they are not nullptr, + // but that would be worth it only if this method is called multiple times, + // or called after using the old-style bucket iterator API. + LocalPointer immutableBucketList(createBucketList(errorCode)); + LocalPointer coll(collatorPrimaryOnly_->clone()); + if (immutableBucketList.isNull() || coll.isNull()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + ImmutableIndex *immIndex = new ImmutableIndex(immutableBucketList.getAlias(), coll.getAlias()); + if (immIndex == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + // The ImmutableIndex adopted its parameter objects. + immutableBucketList.orphan(); + coll.orphan(); + return immIndex; +} + +int32_t AlphabeticIndex::getBucketCount(UErrorCode &status) { + initBuckets(status); + if (U_FAILURE(status)) { + return 0; + } + return buckets_->getBucketCount(); +} + + +int32_t AlphabeticIndex::getRecordCount(UErrorCode &status) { + if (U_FAILURE(status) || inputList_ == nullptr) { + return 0; + } + return inputList_->size(); +} + +void AlphabeticIndex::initLabels(UVector &indexCharacters, UErrorCode &errorCode) const { + U_ASSERT(indexCharacters.hasDeleter()); + const Normalizer2 *nfkdNormalizer = Normalizer2::getNFKDInstance(errorCode); + if (U_FAILURE(errorCode)) { return; } + + const UnicodeString &firstScriptBoundary = *getString(*firstCharsInScripts_, 0); + const UnicodeString &overflowBoundary = + *getString(*firstCharsInScripts_, firstCharsInScripts_->size() - 1); + + // We make a sorted array of elements. + // Some of the input may be redundant. + // That is, we might have c, ch, d, where "ch" sorts just like "c", "h". + // We filter out those cases. + UnicodeSetIterator iter(*initialLabels_); + while (U_SUCCESS(errorCode) && iter.next()) { + const UnicodeString *item = &iter.getString(); + LocalPointer ownedItem; + UBool checkDistinct; + int32_t itemLength = item->length(); + if (!item->hasMoreChar32Than(0, itemLength, 1)) { + checkDistinct = false; + } else if(item->charAt(itemLength - 1) == 0x2a && // '*' + item->charAt(itemLength - 2) != 0x2a) { + // Use a label if it is marked with one trailing star, + // even if the label string sorts the same when all contractions are suppressed. + ownedItem.adoptInstead(new UnicodeString(*item, 0, itemLength - 1)); + item = ownedItem.getAlias(); + if (item == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + checkDistinct = false; + } else { + checkDistinct = true; + } + if (collatorPrimaryOnly_->compare(*item, firstScriptBoundary, errorCode) < 0) { + // Ignore a primary-ignorable or non-alphabetic index character. + } else if (collatorPrimaryOnly_->compare(*item, overflowBoundary, errorCode) >= 0) { + // Ignore an index character that will land in the overflow bucket. + } else if (checkDistinct && + collatorPrimaryOnly_->compare(*item, separated(*item), errorCode) == 0) { + // Ignore a multi-code point index character that does not sort distinctly + // from the sequence of its separate characters. + } else { + int32_t insertionPoint = binarySearch(indexCharacters, *item, *collatorPrimaryOnly_); + if (insertionPoint < 0) { + indexCharacters.insertElementAt( + ownedString(*item, ownedItem, errorCode), ~insertionPoint, errorCode); + } else { + const UnicodeString &itemAlreadyIn = *getString(indexCharacters, insertionPoint); + if (isOneLabelBetterThanOther(*nfkdNormalizer, *item, itemAlreadyIn)) { + indexCharacters.setElementAt( + ownedString(*item, ownedItem, errorCode), insertionPoint); + } + } + } + } + if (U_FAILURE(errorCode)) { return; } + + // if the result is still too large, cut down to maxLabelCount_ elements, by removing every nth element + + int32_t size = indexCharacters.size() - 1; + if (size > maxLabelCount_) { + int32_t count = 0; + int32_t old = -1; + for (int32_t i = 0; i < indexCharacters.size();) { + ++count; + int32_t bump = count * maxLabelCount_ / size; + if (bump == old) { + indexCharacters.removeElementAt(i); + } else { + old = bump; + ++i; + } + } + } +} + +namespace { + +const UnicodeString &fixLabel(const UnicodeString ¤t, UnicodeString &temp) { + if (!current.startsWith(BASE, BASE_LENGTH)) { + return current; + } + char16_t rest = current.charAt(BASE_LENGTH); + if (0x2800 < rest && rest <= 0x28FF) { // stroke count + int32_t count = rest-0x2800; + temp.setTo((char16_t)(0x30 + count % 10)); + if (count >= 10) { + count /= 10; + temp.insert(0, (char16_t)(0x30 + count % 10)); + if (count >= 10) { + count /= 10; + temp.insert(0, (char16_t)(0x30 + count)); + } + } + return temp.append((char16_t)0x5283); + } + return temp.setTo(current, BASE_LENGTH); +} + +UBool hasMultiplePrimaryWeights( + const RuleBasedCollator &coll, uint32_t variableTop, + const UnicodeString &s, UVector64 &ces, UErrorCode &errorCode) { + ces.removeAllElements(); + coll.internalGetCEs(s, ces, errorCode); + if (U_FAILURE(errorCode)) { return false; } + UBool seenPrimary = false; + for (int32_t i = 0; i < ces.size(); ++i) { + int64_t ce = ces.elementAti(i); + uint32_t p = (uint32_t)(ce >> 32); + if (p > variableTop) { + // not primary ignorable + if (seenPrimary) { + return true; + } + seenPrimary = true; + } + } + return false; +} + +} // namespace + +BucketList *AlphabeticIndex::createBucketList(UErrorCode &errorCode) const { + // Initialize indexCharacters. + UVector indexCharacters(errorCode); + indexCharacters.setDeleter(uprv_deleteUObject); + initLabels(indexCharacters, errorCode); + if (U_FAILURE(errorCode)) { return nullptr; } + + // Variables for hasMultiplePrimaryWeights(). + UVector64 ces(errorCode); + uint32_t variableTop; + if (collatorPrimaryOnly_->getAttribute(UCOL_ALTERNATE_HANDLING, errorCode) == UCOL_SHIFTED) { + variableTop = collatorPrimaryOnly_->getVariableTop(errorCode); + } else { + variableTop = 0; + } + UBool hasInvisibleBuckets = false; + + // Helper arrays for Chinese Pinyin collation. + Bucket *asciiBuckets[26] = { + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr + }; + Bucket *pinyinBuckets[26] = { + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr + }; + UBool hasPinyin = false; + + LocalPointer bucketList(new UVector(errorCode), errorCode); + if (U_FAILURE(errorCode)) { + return nullptr; + } + bucketList->setDeleter(uprv_deleteUObject); + + // underflow bucket + LocalPointer bucket(new Bucket(getUnderflowLabel(), emptyString_, U_ALPHAINDEX_UNDERFLOW), errorCode); + if (U_FAILURE(errorCode)) { + return nullptr; + } + bucketList->adoptElement(bucket.orphan(), errorCode); + if (U_FAILURE(errorCode)) { return nullptr; } + + UnicodeString temp; + + // fix up the list, adding underflow, additions, overflow + // Insert inflow labels as needed. + int32_t scriptIndex = -1; + const UnicodeString *scriptUpperBoundary = &emptyString_; + for (int32_t i = 0; i < indexCharacters.size(); ++i) { + UnicodeString ¤t = *getString(indexCharacters, i); + if (collatorPrimaryOnly_->compare(current, *scriptUpperBoundary, errorCode) >= 0) { + // We crossed the script boundary into a new script. + const UnicodeString &inflowBoundary = *scriptUpperBoundary; + UBool skippedScript = false; + for (;;) { + scriptUpperBoundary = getString(*firstCharsInScripts_, ++scriptIndex); + if (collatorPrimaryOnly_->compare(current, *scriptUpperBoundary, errorCode) < 0) { + break; + } + skippedScript = true; + } + if (skippedScript && bucketList->size() > 1) { + // We are skipping one or more scripts, + // and we are not just getting out of the underflow label. + bucket.adoptInsteadAndCheckErrorCode( + new Bucket(getInflowLabel(), inflowBoundary, U_ALPHAINDEX_INFLOW), errorCode); + bucketList->adoptElement(bucket.orphan(), errorCode); + if (U_FAILURE(errorCode)) { return nullptr; } + } + } + // Add a bucket with the current label. + bucket.adoptInsteadAndCheckErrorCode( + new Bucket(fixLabel(current, temp), current, U_ALPHAINDEX_NORMAL), errorCode); + bucketList->adoptElement(bucket.orphan(), errorCode); + if (U_FAILURE(errorCode)) { return nullptr; } + // Remember ASCII and Pinyin buckets for Pinyin redirects. + char16_t c; + if (current.length() == 1 && 0x41 <= (c = current.charAt(0)) && c <= 0x5A) { // A-Z + asciiBuckets[c - 0x41] = (Bucket *)bucketList->lastElement(); + } else if (current.length() == BASE_LENGTH + 1 && current.startsWith(BASE, BASE_LENGTH) && + 0x41 <= (c = current.charAt(BASE_LENGTH)) && c <= 0x5A) { + pinyinBuckets[c - 0x41] = (Bucket *)bucketList->lastElement(); + hasPinyin = true; + } + // Check for multiple primary weights. + if (!current.startsWith(BASE, BASE_LENGTH) && + hasMultiplePrimaryWeights(*collatorPrimaryOnly_, variableTop, current, + ces, errorCode) && + current.charAt(current.length() - 1) != 0xFFFF /* !current.endsWith("\uffff") */) { + // "AE-ligature" or "Sch" etc. + for (int32_t j = bucketList->size() - 2;; --j) { + Bucket *singleBucket = getBucket(*bucketList, j); + if (singleBucket->labelType_ != U_ALPHAINDEX_NORMAL) { + // There is no single-character bucket since the last + // underflow or inflow label. + break; + } + if (singleBucket->displayBucket_ == nullptr && + !hasMultiplePrimaryWeights(*collatorPrimaryOnly_, variableTop, + singleBucket->lowerBoundary_, + ces, errorCode)) { + // Add an invisible bucket that redirects strings greater than the expansion + // to the previous single-character bucket. + // For example, after ... Q R S Sch we add Sch\uFFFF->S + // and after ... Q R S Sch Sch\uFFFF St we add St\uFFFF->S. + bucket.adoptInsteadAndCheckErrorCode(new Bucket(emptyString_, + UnicodeString(current).append((char16_t)0xFFFF), + U_ALPHAINDEX_NORMAL), + errorCode); + if (U_FAILURE(errorCode)) { + return nullptr; + } + bucket->displayBucket_ = singleBucket; + bucketList->adoptElement(bucket.orphan(), errorCode); + if (U_FAILURE(errorCode)) { return nullptr; } + hasInvisibleBuckets = true; + break; + } + } + } + } + if (U_FAILURE(errorCode)) { return nullptr; } + if (bucketList->size() == 1) { + // No real labels, show only the underflow label. + BucketList *bl = new BucketList(bucketList.getAlias(), bucketList.getAlias()); + if (bl == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + bucketList.orphan(); + return bl; + } + // overflow bucket + bucket.adoptInsteadAndCheckErrorCode( + new Bucket(getOverflowLabel(), *scriptUpperBoundary, U_ALPHAINDEX_OVERFLOW), errorCode); + bucketList->adoptElement(bucket.orphan(), errorCode); // final + if (U_FAILURE(errorCode)) { return nullptr; } + + if (hasPinyin) { + // Redirect Pinyin buckets. + Bucket *asciiBucket = nullptr; + for (int32_t i = 0; i < 26; ++i) { + if (asciiBuckets[i] != nullptr) { + asciiBucket = asciiBuckets[i]; + } + if (pinyinBuckets[i] != nullptr && asciiBucket != nullptr) { + pinyinBuckets[i]->displayBucket_ = asciiBucket; + hasInvisibleBuckets = true; + } + } + } + + if (U_FAILURE(errorCode)) { return nullptr; } + if (!hasInvisibleBuckets) { + BucketList *bl = new BucketList(bucketList.getAlias(), bucketList.getAlias()); + if (bl == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + bucketList.orphan(); + return bl; + } + // Merge inflow buckets that are visually adjacent. + // Iterate backwards: Merge inflow into overflow rather than the other way around. + int32_t i = bucketList->size() - 1; + Bucket *nextBucket = getBucket(*bucketList, i); + while (--i > 0) { + Bucket *bucket = getBucket(*bucketList, i); + if (bucket->displayBucket_ != nullptr) { + continue; // skip invisible buckets + } + if (bucket->labelType_ == U_ALPHAINDEX_INFLOW) { + if (nextBucket->labelType_ != U_ALPHAINDEX_NORMAL) { + bucket->displayBucket_ = nextBucket; + continue; + } + } + nextBucket = bucket; + } + + LocalPointer publicBucketList(new UVector(errorCode), errorCode); + if (U_FAILURE(errorCode)) { + return nullptr; + } + // Do not call publicBucketList->setDeleter(): + // This vector shares its objects with the bucketList. + for (int32_t j = 0; j < bucketList->size(); ++j) { + Bucket *bucket = getBucket(*bucketList, j); + if (bucket->displayBucket_ == nullptr) { + publicBucketList->addElement(bucket, errorCode); + } + } + if (U_FAILURE(errorCode)) { return nullptr; } + BucketList *bl = new BucketList(bucketList.getAlias(), publicBucketList.getAlias()); + if (bl == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + bucketList.orphan(); + publicBucketList.orphan(); + return bl; +} + +/** + * Creates an index, and buckets and sorts the list of records into the index. + */ +void AlphabeticIndex::initBuckets(UErrorCode &errorCode) { + if (U_FAILURE(errorCode) || buckets_ != nullptr) { + return; + } + buckets_ = createBucketList(errorCode); + if (U_FAILURE(errorCode) || inputList_ == nullptr || inputList_->isEmpty()) { + return; + } + + // Sort the records by name. + // Stable sort preserves input order of collation duplicates. + inputList_->sortWithUComparator(recordCompareFn, collator_, errorCode); + + // Now, we traverse all of the input, which is now sorted. + // If the item doesn't go in the current bucket, we find the next bucket that contains it. + // This makes the process order n*log(n), since we just sort the list and then do a linear process. + // However, if the user adds an item at a time and then gets the buckets, this isn't efficient, so + // we need to improve it for that case. + + Bucket *currentBucket = getBucket(*buckets_->bucketList_, 0); + int32_t bucketIndex = 1; + Bucket *nextBucket; + const UnicodeString *upperBoundary; + if (bucketIndex < buckets_->bucketList_->size()) { + nextBucket = getBucket(*buckets_->bucketList_, bucketIndex++); + upperBoundary = &nextBucket->lowerBoundary_; + } else { + nextBucket = nullptr; + upperBoundary = nullptr; + } + for (int32_t i = 0; i < inputList_->size(); ++i) { + Record *r = getRecord(*inputList_, i); + // if the current bucket isn't the right one, find the one that is + // We have a special flag for the last bucket so that we don't look any further + while (upperBoundary != nullptr && + collatorPrimaryOnly_->compare(r->name_, *upperBoundary, errorCode) >= 0) { + currentBucket = nextBucket; + // now reset the boundary that we compare against + if (bucketIndex < buckets_->bucketList_->size()) { + nextBucket = getBucket(*buckets_->bucketList_, bucketIndex++); + upperBoundary = &nextBucket->lowerBoundary_; + } else { + upperBoundary = nullptr; + } + } + // now put the record into the bucket. + Bucket *bucket = currentBucket; + if (bucket->displayBucket_ != nullptr) { + bucket = bucket->displayBucket_; + } + if (bucket->records_ == nullptr) { + LocalPointer records(new UVector(errorCode), errorCode); + if (U_FAILURE(errorCode)) { + return; + } + bucket->records_ = records.orphan(); + } + bucket->records_->addElement(r, errorCode); + } +} + +void AlphabeticIndex::clearBuckets() { + if (buckets_ != nullptr) { + delete buckets_; + buckets_ = nullptr; + internalResetBucketIterator(); + } +} + +void AlphabeticIndex::internalResetBucketIterator() { + labelsIterIndex_ = -1; + currentBucket_ = nullptr; +} + + +void AlphabeticIndex::addIndexExemplars(const Locale &locale, UErrorCode &status) { + LocalULocaleDataPointer uld(ulocdata_open(locale.getName(), &status)); + if (U_FAILURE(status)) { + return; + } + + UnicodeSet exemplars; + ulocdata_getExemplarSet(uld.getAlias(), exemplars.toUSet(), 0, ULOCDATA_ES_INDEX, &status); + if (U_SUCCESS(status)) { + initialLabels_->addAll(exemplars); + return; + } + status = U_ZERO_ERROR; // Clear out U_MISSING_RESOURCE_ERROR + + // The locale data did not include explicit Index characters. + // Synthesize a set of them from the locale's standard exemplar characters. + ulocdata_getExemplarSet(uld.getAlias(), exemplars.toUSet(), 0, ULOCDATA_ES_STANDARD, &status); + if (U_FAILURE(status)) { + return; + } + + // question: should we add auxiliary exemplars? + if (exemplars.containsSome(0x61, 0x7A) /* a-z */ || exemplars.isEmpty()) { + exemplars.add(0x61, 0x7A); + } + if (exemplars.containsSome(0xAC00, 0xD7A3)) { // Hangul syllables + // cut down to small list + exemplars.remove(0xAC00, 0xD7A3). + add(0xAC00).add(0xB098).add(0xB2E4).add(0xB77C). + add(0xB9C8).add(0xBC14).add(0xC0AC).add(0xC544). + add(0xC790).add(0xCC28).add(0xCE74).add(0xD0C0). + add(0xD30C).add(0xD558); + } + if (exemplars.containsSome(0x1200, 0x137F)) { // Ethiopic block + // cut down to small list + // make use of the fact that Ethiopic is allocated in 8's, where + // the base is 0 mod 8. + UnicodeSet ethiopic(UnicodeString(u"[ሀለሐመሠረሰሸቀቈቐቘበቨተቸኀኈነኘአከኰኸዀወዐዘዠየደዸጀገጐጘጠጨጰጸፀፈፐፘ]"), status); + ethiopic.retainAll(exemplars); + exemplars.remove(u'ሀ', 0x137F).addAll(ethiopic); + } + + // Upper-case any that aren't already so. + // (We only do this for synthesized index characters.) + UnicodeSetIterator it(exemplars); + UnicodeString upperC; + while (it.next()) { + const UnicodeString &exemplarC = it.getString(); + upperC = exemplarC; + upperC.toUpper(locale); + initialLabels_->add(upperC); + } +} + +UBool AlphabeticIndex::addChineseIndexCharacters(UErrorCode &errorCode) { + UnicodeSet contractions; + collatorPrimaryOnly_->internalAddContractions(BASE[0], contractions, errorCode); + if (U_FAILURE(errorCode) || contractions.isEmpty()) { return false; } + initialLabels_->addAll(contractions); + UnicodeSetIterator iter(contractions); + while (iter.next()) { + const UnicodeString &s = iter.getString(); + U_ASSERT (s.startsWith(BASE, BASE_LENGTH)); + char16_t c = s.charAt(s.length() - 1); + if (0x41 <= c && c <= 0x5A) { // A-Z + // There are Pinyin labels, add ASCII A-Z labels as well. + initialLabels_->add(0x41, 0x5A); // A-Z + break; + } + } + return true; +} + + +/* + * Return the string with interspersed CGJs. Input must have more than 2 codepoints. + */ +static const char16_t CGJ = 0x034F; +UnicodeString AlphabeticIndex::separated(const UnicodeString &item) { + UnicodeString result; + if (item.length() == 0) { + return result; + } + int32_t i = 0; + for (;;) { + UChar32 cp = item.char32At(i); + result.append(cp); + i = item.moveIndex32(i, 1); + if (i >= item.length()) { + break; + } + result.append(CGJ); + } + return result; +} + + +bool AlphabeticIndex::operator==(const AlphabeticIndex& /* other */) const { + return false; +} + + +bool AlphabeticIndex::operator!=(const AlphabeticIndex& /* other */) const { + return false; +} + + +const RuleBasedCollator &AlphabeticIndex::getCollator() const { + return *collator_; +} + + +const UnicodeString &AlphabeticIndex::getInflowLabel() const { + return inflowLabel_; +} + +const UnicodeString &AlphabeticIndex::getOverflowLabel() const { + return overflowLabel_; +} + + +const UnicodeString &AlphabeticIndex::getUnderflowLabel() const { + return underflowLabel_; +} + + +AlphabeticIndex &AlphabeticIndex::setInflowLabel(const UnicodeString &label, UErrorCode &/*status*/) { + inflowLabel_ = label; + clearBuckets(); + return *this; +} + + +AlphabeticIndex &AlphabeticIndex::setOverflowLabel(const UnicodeString &label, UErrorCode &/*status*/) { + overflowLabel_ = label; + clearBuckets(); + return *this; +} + + +AlphabeticIndex &AlphabeticIndex::setUnderflowLabel(const UnicodeString &label, UErrorCode &/*status*/) { + underflowLabel_ = label; + clearBuckets(); + return *this; +} + + +int32_t AlphabeticIndex::getMaxLabelCount() const { + return maxLabelCount_; +} + + +AlphabeticIndex &AlphabeticIndex::setMaxLabelCount(int32_t maxLabelCount, UErrorCode &status) { + if (U_FAILURE(status)) { + return *this; + } + if (maxLabelCount <= 0) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return *this; + } + maxLabelCount_ = maxLabelCount; + clearBuckets(); + return *this; +} + + +// +// init() - Common code for constructors. +// + +void AlphabeticIndex::init(const Locale *locale, UErrorCode &status) { + if (U_FAILURE(status)) { return; } + if (locale == nullptr && collator_ == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + initialLabels_ = new UnicodeSet(); + if (initialLabels_ == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + inflowLabel_.setTo((char16_t)0x2026); // Ellipsis + overflowLabel_ = inflowLabel_; + underflowLabel_ = inflowLabel_; + + if (collator_ == nullptr) { + Collator *coll = Collator::createInstance(*locale, status); + if (U_FAILURE(status)) { + delete coll; + return; + } + if (coll == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + collator_ = dynamic_cast(coll); + if (collator_ == nullptr) { + delete coll; + status = U_UNSUPPORTED_ERROR; + return; + } + } + collatorPrimaryOnly_ = collator_->clone(); + if (collatorPrimaryOnly_ == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + collatorPrimaryOnly_->setAttribute(UCOL_STRENGTH, UCOL_PRIMARY, status); + firstCharsInScripts_ = firstStringsInScript(status); + if (U_FAILURE(status)) { return; } + firstCharsInScripts_->sortWithUComparator(collatorComparator, collatorPrimaryOnly_, status); + // Guard against a degenerate collator where + // some script boundary strings are primary ignorable. + for (;;) { + if (U_FAILURE(status)) { return; } + if (firstCharsInScripts_->isEmpty()) { + // AlphabeticIndex requires some non-ignorable script boundary strings. + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (collatorPrimaryOnly_->compare( + *static_cast(firstCharsInScripts_->elementAt(0)), + emptyString_, status) == UCOL_EQUAL) { + firstCharsInScripts_->removeElementAt(0); + } else { + break; + } + } + + // Chinese index characters, which are specific to each of the several Chinese tailorings, + // take precedence over the single locale data exemplar set per language. + if (!addChineseIndexCharacters(status) && locale != nullptr) { + addIndexExemplars(*locale, status); + } +} + + +// +// Comparison function for UVector sorting with a collator. +// +static int32_t U_CALLCONV +collatorComparator(const void *context, const void *left, const void *right) { + const UElement *leftElement = static_cast(left); + const UElement *rightElement = static_cast(right); + const UnicodeString *leftString = static_cast(leftElement->pointer); + const UnicodeString *rightString = static_cast(rightElement->pointer); + + if (leftString == rightString) { + // Catches case where both are nullptr + return 0; + } + if (leftString == nullptr) { + return 1; + } + if (rightString == nullptr) { + return -1; + } + const Collator *col = static_cast(context); + UErrorCode errorCode = U_ZERO_ERROR; + return col->compare(*leftString, *rightString, errorCode); +} + +// +// Comparison function for UVector sorting with a collator. +// +static int32_t U_CALLCONV +recordCompareFn(const void *context, const void *left, const void *right) { + const UElement *leftElement = static_cast(left); + const UElement *rightElement = static_cast(right); + const AlphabeticIndex::Record *leftRec = static_cast(leftElement->pointer); + const AlphabeticIndex::Record *rightRec = static_cast(rightElement->pointer); + const Collator *col = static_cast(context); + UErrorCode errorCode = U_ZERO_ERROR; + return col->compare(leftRec->name_, rightRec->name_, errorCode); +} + +UVector *AlphabeticIndex::firstStringsInScript(UErrorCode &status) { + if (U_FAILURE(status)) { + return nullptr; + } + LocalPointer dest(new UVector(status), status); + if (U_FAILURE(status)) { + return nullptr; + } + dest->setDeleter(uprv_deleteUObject); + // Fetch the script-first-primary contractions which are defined in the root collator. + // They all start with U+FDD1. + UnicodeSet set; + collatorPrimaryOnly_->internalAddContractions(0xFDD1, set, status); + if (U_FAILURE(status)) { + return nullptr; + } + if (set.isEmpty()) { + status = U_UNSUPPORTED_ERROR; + return nullptr; + } + UnicodeSetIterator iter(set); + while (iter.next()) { + const UnicodeString &boundary = iter.getString(); + uint32_t gcMask = U_GET_GC_MASK(boundary.char32At(1)); + if ((gcMask & (U_GC_L_MASK | U_GC_CN_MASK)) == 0) { + // Ignore boundaries for the special reordering groups. + // Take only those for "real scripts" (where the sample character is a Letter, + // and the one for unassigned implicit weights (Cn). + continue; + } + LocalPointer s(new UnicodeString(boundary), status); + dest->adoptElement(s.orphan(), status); + if (U_FAILURE(status)) { + return nullptr; + } + } + return dest.orphan(); +} + + +namespace { + +/** + * Returns true if one index character string is "better" than the other. + * Shorter NFKD is better, and otherwise NFKD-binary-less-than is + * better, and otherwise binary-less-than is better. + */ +UBool isOneLabelBetterThanOther(const Normalizer2 &nfkdNormalizer, + const UnicodeString &one, const UnicodeString &other) { + // This is called with primary-equal strings, but never with one.equals(other). + UErrorCode status = U_ZERO_ERROR; + UnicodeString n1 = nfkdNormalizer.normalize(one, status); + UnicodeString n2 = nfkdNormalizer.normalize(other, status); + if (U_FAILURE(status)) { return false; } + int32_t result = n1.countChar32() - n2.countChar32(); + if (result != 0) { + return result < 0; + } + result = n1.compareCodePointOrder(n2); + if (result != 0) { + return result < 0; + } + return one.compareCodePointOrder(other) < 0; +} + +} // namespace + +// +// Constructor & Destructor for AlphabeticIndex::Record +// +// Records are internal only, instances are not directly surfaced in the public API. +// This class is mostly struct-like, with all public fields. + +AlphabeticIndex::Record::Record(const UnicodeString &name, const void *data) + : name_(name), data_(data) {} + +AlphabeticIndex::Record::~Record() { +} + + +AlphabeticIndex & AlphabeticIndex::addRecord(const UnicodeString &name, const void *data, UErrorCode &status) { + if (U_FAILURE(status)) { + return *this; + } + if (inputList_ == nullptr) { + LocalPointer inputList(new UVector(status), status); + if (U_FAILURE(status)) { + return *this; + } + inputList_ = inputList.orphan(); + inputList_->setDeleter(alphaIndex_deleteRecord); + } + LocalPointer r(new Record(name, data), status); + inputList_->adoptElement(r.orphan(), status); + if (U_FAILURE(status)) { + return *this; + } + clearBuckets(); + //std::string ss; + //std::string ss2; + //std::cout << "added record: name = \"" << r->name_.toUTF8String(ss) << "\"" << + // " sortingName = \"" << r->sortingName_.toUTF8String(ss2) << "\"" << std::endl; + return *this; +} + + +AlphabeticIndex &AlphabeticIndex::clearRecords(UErrorCode &status) { + if (U_SUCCESS(status) && inputList_ != nullptr && !inputList_->isEmpty()) { + inputList_->removeAllElements(); + clearBuckets(); + } + return *this; +} + +int32_t AlphabeticIndex::getBucketIndex(const UnicodeString &name, UErrorCode &status) { + initBuckets(status); + if (U_FAILURE(status)) { + return 0; + } + return buckets_->getBucketIndex(name, *collatorPrimaryOnly_, status); +} + + +int32_t AlphabeticIndex::getBucketIndex() const { + return labelsIterIndex_; +} + + +UBool AlphabeticIndex::nextBucket(UErrorCode &status) { + if (U_FAILURE(status)) { + return false; + } + if (buckets_ == nullptr && currentBucket_ != nullptr) { + status = U_ENUM_OUT_OF_SYNC_ERROR; + return false; + } + initBuckets(status); + if (U_FAILURE(status)) { + return false; + } + ++labelsIterIndex_; + if (labelsIterIndex_ >= buckets_->getBucketCount()) { + labelsIterIndex_ = buckets_->getBucketCount(); + return false; + } + currentBucket_ = getBucket(*buckets_->immutableVisibleList_, labelsIterIndex_); + resetRecordIterator(); + return true; +} + +const UnicodeString &AlphabeticIndex::getBucketLabel() const { + if (currentBucket_ != nullptr) { + return currentBucket_->label_; + } else { + return emptyString_; + } +} + + +UAlphabeticIndexLabelType AlphabeticIndex::getBucketLabelType() const { + if (currentBucket_ != nullptr) { + return currentBucket_->labelType_; + } else { + return U_ALPHAINDEX_NORMAL; + } +} + + +int32_t AlphabeticIndex::getBucketRecordCount() const { + if (currentBucket_ != nullptr && currentBucket_->records_ != nullptr) { + return currentBucket_->records_->size(); + } else { + return 0; + } +} + + +AlphabeticIndex &AlphabeticIndex::resetBucketIterator(UErrorCode &status) { + if (U_FAILURE(status)) { + return *this; + } + internalResetBucketIterator(); + return *this; +} + + +UBool AlphabeticIndex::nextRecord(UErrorCode &status) { + if (U_FAILURE(status)) { + return false; + } + if (currentBucket_ == nullptr) { + // We are trying to iterate over the items in a bucket, but there is no + // current bucket from the enumeration of buckets. + status = U_INVALID_STATE_ERROR; + return false; + } + if (buckets_ == nullptr) { + status = U_ENUM_OUT_OF_SYNC_ERROR; + return false; + } + if (currentBucket_->records_ == nullptr) { + return false; + } + ++itemsIterIndex_; + if (itemsIterIndex_ >= currentBucket_->records_->size()) { + itemsIterIndex_ = currentBucket_->records_->size(); + return false; + } + return true; +} + + +const UnicodeString &AlphabeticIndex::getRecordName() const { + const UnicodeString *retStr = &emptyString_; + if (currentBucket_ != nullptr && currentBucket_->records_ != nullptr && + itemsIterIndex_ >= 0 && + itemsIterIndex_ < currentBucket_->records_->size()) { + Record *item = static_cast(currentBucket_->records_->elementAt(itemsIterIndex_)); + retStr = &item->name_; + } + return *retStr; +} + +const void *AlphabeticIndex::getRecordData() const { + const void *retPtr = nullptr; + if (currentBucket_ != nullptr && currentBucket_->records_ != nullptr && + itemsIterIndex_ >= 0 && + itemsIterIndex_ < currentBucket_->records_->size()) { + Record *item = static_cast(currentBucket_->records_->elementAt(itemsIterIndex_)); + retPtr = item->data_; + } + return retPtr; +} + + +AlphabeticIndex & AlphabeticIndex::resetRecordIterator() { + itemsIterIndex_ = -1; + return *this; +} + + + +AlphabeticIndex::Bucket::Bucket(const UnicodeString &label, + const UnicodeString &lowerBoundary, + UAlphabeticIndexLabelType type) + : label_(label), lowerBoundary_(lowerBoundary), labelType_(type), + displayBucket_(nullptr), displayIndex_(-1), + records_(nullptr) { +} + + +AlphabeticIndex::Bucket::~Bucket() { + delete records_; +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/anytrans.cpp b/deps/icu-small/source/i18n/anytrans.cpp index e10ff479ddd5dc..0ee44229947d18 100644 --- a/deps/icu-small/source/i18n/anytrans.cpp +++ b/deps/icu-small/source/i18n/anytrans.cpp @@ -1,411 +1,411 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -***************************************************************** -* Copyright (c) 2002-2014, International Business Machines Corporation -* and others. All Rights Reserved. -***************************************************************** -* Date Name Description -* 06/06/2002 aliu Creation. -***************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/uobject.h" -#include "unicode/uscript.h" - -#include "anytrans.h" -#include "hash.h" -#include "mutex.h" -#include "nultrans.h" -#include "putilimp.h" -#include "tridpars.h" -#include "uinvchar.h" -#include "uvector.h" - -//------------------------------------------------------------ -// Constants - -static const UChar TARGET_SEP = 45; // '-' -static const UChar VARIANT_SEP = 47; // '/' -static const UChar ANY[] = {0x41,0x6E,0x79,0}; // "Any" -static const UChar NULL_ID[] = {78,117,108,108,0}; // "Null" -static const UChar LATIN_PIVOT[] = {0x2D,0x4C,0x61,0x74,0x6E,0x3B,0x4C,0x61,0x74,0x6E,0x2D,0}; // "-Latn;Latn-" - -// initial size for an Any-XXXX transform's cache of script-XXXX transforms -// (will grow as necessary, but we don't expect to have source text with more than 7 scripts) -#define ANY_TRANS_CACHE_INIT_SIZE 7 - -//------------------------------------------------------------ - -U_CDECL_BEGIN -/** - * Deleter function for Transliterator*. - */ -static void U_CALLCONV -_deleteTransliterator(void *obj) { - delete (icu::Transliterator*) obj; -} -U_CDECL_END - -//------------------------------------------------------------ - -U_NAMESPACE_BEGIN - -//------------------------------------------------------------ -// ScriptRunIterator - -/** - * Returns a series of ranges corresponding to scripts. They will be - * of the form: - * - * ccccSScSSccccTTcTcccc - c = common, S = first script, T = second - * | | - first run (start, limit) - * | | - second run (start, limit) - * - * That is, the runs will overlap. The reason for this is so that a - * transliterator can consider common characters both before and after - * the scripts. - */ -class ScriptRunIterator : public UMemory { -private: - const Replaceable& text; - int32_t textStart; - int32_t textLimit; - -public: - /** - * The code of the current run, valid after next() returns. May - * be USCRIPT_INVALID_CODE if and only if the entire text is - * COMMON/INHERITED. - */ - UScriptCode scriptCode; - - /** - * The start of the run, inclusive, valid after next() returns. - */ - int32_t start; - - /** - * The end of the run, exclusive, valid after next() returns. - */ - int32_t limit; - - /** - * Constructs a run iterator over the given text from start - * (inclusive) to limit (exclusive). - */ - ScriptRunIterator(const Replaceable& text, int32_t start, int32_t limit); - - /** - * Returns true if there are any more runs. true is always - * returned at least once. Upon return, the caller should - * examine scriptCode, start, and limit. - */ - UBool next(); - - /** - * Adjusts internal indices for a change in the limit index of the - * given delta. A positive delta means the limit has increased. - */ - void adjustLimit(int32_t delta); - -private: - ScriptRunIterator(const ScriptRunIterator &other); // forbid copying of this class - ScriptRunIterator &operator=(const ScriptRunIterator &other); // forbid copying of this class -}; - -ScriptRunIterator::ScriptRunIterator(const Replaceable& theText, - int32_t myStart, int32_t myLimit) : - text(theText) -{ - textStart = myStart; - textLimit = myLimit; - limit = myStart; -} - -UBool ScriptRunIterator::next() { - UChar32 ch; - UScriptCode s; - UErrorCode ec = U_ZERO_ERROR; - - scriptCode = USCRIPT_INVALID_CODE; // don't know script yet - start = limit; - - // Are we done? - if (start == textLimit) { - return false; - } - - // Move start back to include adjacent COMMON or INHERITED - // characters - while (start > textStart) { - ch = text.char32At(start - 1); // look back - s = uscript_getScript(ch, &ec); - if (s == USCRIPT_COMMON || s == USCRIPT_INHERITED) { - --start; - } else { - break; - } - } - - // Move limit ahead to include COMMON, INHERITED, and characters - // of the current script. - while (limit < textLimit) { - ch = text.char32At(limit); // look ahead - s = uscript_getScript(ch, &ec); - if (s != USCRIPT_COMMON && s != USCRIPT_INHERITED) { - if (scriptCode == USCRIPT_INVALID_CODE) { - scriptCode = s; - } else if (s != scriptCode) { - break; - } - } - ++limit; - } - - // Return true even if the entire text is COMMON / INHERITED, in - // which case scriptCode will be USCRIPT_INVALID_CODE. - return true; -} - -void ScriptRunIterator::adjustLimit(int32_t delta) { - limit += delta; - textLimit += delta; -} - -//------------------------------------------------------------ -// AnyTransliterator - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AnyTransliterator) - -AnyTransliterator::AnyTransliterator(const UnicodeString& id, - const UnicodeString& theTarget, - const UnicodeString& theVariant, - UScriptCode theTargetScript, - UErrorCode& ec) : - Transliterator(id, NULL), - targetScript(theTargetScript) -{ - cache = uhash_openSize(uhash_hashLong, uhash_compareLong, NULL, ANY_TRANS_CACHE_INIT_SIZE, &ec); - if (U_FAILURE(ec)) { - return; - } - uhash_setValueDeleter(cache, _deleteTransliterator); - - target = theTarget; - if (theVariant.length() > 0) { - target.append(VARIANT_SEP).append(theVariant); - } -} - -AnyTransliterator::~AnyTransliterator() { - uhash_close(cache); -} - -/** - * Copy constructor. - */ -AnyTransliterator::AnyTransliterator(const AnyTransliterator& o) : - Transliterator(o), - target(o.target), - targetScript(o.targetScript) -{ - // Don't copy the cache contents - UErrorCode ec = U_ZERO_ERROR; - cache = uhash_openSize(uhash_hashLong, uhash_compareLong, NULL, ANY_TRANS_CACHE_INIT_SIZE, &ec); - if (U_FAILURE(ec)) { - return; - } - uhash_setValueDeleter(cache, _deleteTransliterator); -} - -/** - * Transliterator API. - */ -AnyTransliterator* AnyTransliterator::clone() const { - return new AnyTransliterator(*this); -} - -/** - * Implements {@link Transliterator#handleTransliterate}. - */ -void AnyTransliterator::handleTransliterate(Replaceable& text, UTransPosition& pos, - UBool isIncremental) const { - int32_t allStart = pos.start; - int32_t allLimit = pos.limit; - - ScriptRunIterator it(text, pos.contextStart, pos.contextLimit); - - while (it.next()) { - // Ignore runs in the ante context - if (it.limit <= allStart) continue; - - // Try to instantiate transliterator from it.scriptCode to - // our target or target/variant - Transliterator* t = getTransliterator(it.scriptCode); - - if (t == NULL) { - // We have no transliterator. Do nothing, but keep - // pos.start up to date. - pos.start = it.limit; - continue; - } - - // If the run end is before the transliteration limit, do - // a non-incremental transliteration. Otherwise do an - // incremental one. - UBool incremental = isIncremental && (it.limit >= allLimit); - - pos.start = uprv_max(allStart, it.start); - pos.limit = uprv_min(allLimit, it.limit); - int32_t limit = pos.limit; - t->filteredTransliterate(text, pos, incremental); - int32_t delta = pos.limit - limit; - allLimit += delta; - it.adjustLimit(delta); - - // We're done if we enter the post context - if (it.limit >= allLimit) break; - } - - // Restore limit. pos.start is fine where the last transliterator - // left it, or at the end of the last run. - pos.limit = allLimit; -} - -Transliterator* AnyTransliterator::getTransliterator(UScriptCode source) const { - - if (source == targetScript || source == USCRIPT_INVALID_CODE) { - return NULL; - } - - Transliterator* t = NULL; - { - Mutex m(NULL); - t = (Transliterator*) uhash_iget(cache, (int32_t) source); - } - if (t == NULL) { - UErrorCode ec = U_ZERO_ERROR; - UnicodeString sourceName(uscript_getShortName(source), -1, US_INV); - UnicodeString id(sourceName); - id.append(TARGET_SEP).append(target); - - t = Transliterator::createInstance(id, UTRANS_FORWARD, ec); - if (U_FAILURE(ec) || t == NULL) { - delete t; - - // Try to pivot around Latin, our most common script - id = sourceName; - id.append(LATIN_PIVOT, -1).append(target); - t = Transliterator::createInstance(id, UTRANS_FORWARD, ec); - if (U_FAILURE(ec) || t == NULL) { - delete t; - t = NULL; - } - } - - if (t != NULL) { - Transliterator *rt = NULL; - { - Mutex m(NULL); - rt = static_cast (uhash_iget(cache, (int32_t) source)); - if (rt == NULL) { - // Common case, no race to cache this new transliterator. - uhash_iput(cache, (int32_t) source, t, &ec); - } else { - // Race case, some other thread beat us to caching this transliterator. - Transliterator *temp = rt; - rt = t; // Our newly created transliterator that lost the race & now needs deleting. - t = temp; // The transliterator from the cache that we will return. - } - } - delete rt; // will be non-null only in case of races. - } - } - return t; -} - -/** - * Return the script code for a given name, or -1 if not found. - */ -static UScriptCode scriptNameToCode(const UnicodeString& name) { - char buf[128]; - UScriptCode code; - UErrorCode ec = U_ZERO_ERROR; - int32_t nameLen = name.length(); - UBool isInvariant = uprv_isInvariantUString(name.getBuffer(), nameLen); - - if (isInvariant) { - name.extract(0, nameLen, buf, (int32_t)sizeof(buf), US_INV); - buf[127] = 0; // Make sure that we NULL terminate the string. - } - if (!isInvariant || uscript_getCode(buf, &code, 1, &ec) != 1 || U_FAILURE(ec)) - { - code = USCRIPT_INVALID_CODE; - } - return code; -} - -/** - * Registers standard transliterators with the system. Called by - * Transliterator during initialization. Scan all current targets and - * register those that are scripts T as Any-T/V. - */ -void AnyTransliterator::registerIDs() { - - UErrorCode ec = U_ZERO_ERROR; - Hashtable seen(true, ec); - - int32_t sourceCount = Transliterator::_countAvailableSources(); - for (int32_t s=0; s= 1); - for (int32_t v=0; v textStart) { + ch = text.char32At(start - 1); // look back + s = uscript_getScript(ch, &ec); + if (s == USCRIPT_COMMON || s == USCRIPT_INHERITED) { + --start; + } else { + break; + } + } + + // Move limit ahead to include COMMON, INHERITED, and characters + // of the current script. + while (limit < textLimit) { + ch = text.char32At(limit); // look ahead + s = uscript_getScript(ch, &ec); + if (s != USCRIPT_COMMON && s != USCRIPT_INHERITED) { + if (scriptCode == USCRIPT_INVALID_CODE) { + scriptCode = s; + } else if (s != scriptCode) { + break; + } + } + ++limit; + } + + // Return true even if the entire text is COMMON / INHERITED, in + // which case scriptCode will be USCRIPT_INVALID_CODE. + return true; +} + +void ScriptRunIterator::adjustLimit(int32_t delta) { + limit += delta; + textLimit += delta; +} + +//------------------------------------------------------------ +// AnyTransliterator + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AnyTransliterator) + +AnyTransliterator::AnyTransliterator(const UnicodeString& id, + const UnicodeString& theTarget, + const UnicodeString& theVariant, + UScriptCode theTargetScript, + UErrorCode& ec) : + Transliterator(id, nullptr), + targetScript(theTargetScript) +{ + cache = uhash_openSize(uhash_hashLong, uhash_compareLong, nullptr, ANY_TRANS_CACHE_INIT_SIZE, &ec); + if (U_FAILURE(ec)) { + return; + } + uhash_setValueDeleter(cache, _deleteTransliterator); + + target = theTarget; + if (theVariant.length() > 0) { + target.append(VARIANT_SEP).append(theVariant); + } +} + +AnyTransliterator::~AnyTransliterator() { + uhash_close(cache); +} + +/** + * Copy constructor. + */ +AnyTransliterator::AnyTransliterator(const AnyTransliterator& o) : + Transliterator(o), + target(o.target), + targetScript(o.targetScript) +{ + // Don't copy the cache contents + UErrorCode ec = U_ZERO_ERROR; + cache = uhash_openSize(uhash_hashLong, uhash_compareLong, nullptr, ANY_TRANS_CACHE_INIT_SIZE, &ec); + if (U_FAILURE(ec)) { + return; + } + uhash_setValueDeleter(cache, _deleteTransliterator); +} + +/** + * Transliterator API. + */ +AnyTransliterator* AnyTransliterator::clone() const { + return new AnyTransliterator(*this); +} + +/** + * Implements {@link Transliterator#handleTransliterate}. + */ +void AnyTransliterator::handleTransliterate(Replaceable& text, UTransPosition& pos, + UBool isIncremental) const { + int32_t allStart = pos.start; + int32_t allLimit = pos.limit; + + ScriptRunIterator it(text, pos.contextStart, pos.contextLimit); + + while (it.next()) { + // Ignore runs in the ante context + if (it.limit <= allStart) continue; + + // Try to instantiate transliterator from it.scriptCode to + // our target or target/variant + Transliterator* t = getTransliterator(it.scriptCode); + + if (t == nullptr) { + // We have no transliterator. Do nothing, but keep + // pos.start up to date. + pos.start = it.limit; + continue; + } + + // If the run end is before the transliteration limit, do + // a non-incremental transliteration. Otherwise do an + // incremental one. + UBool incremental = isIncremental && (it.limit >= allLimit); + + pos.start = uprv_max(allStart, it.start); + pos.limit = uprv_min(allLimit, it.limit); + int32_t limit = pos.limit; + t->filteredTransliterate(text, pos, incremental); + int32_t delta = pos.limit - limit; + allLimit += delta; + it.adjustLimit(delta); + + // We're done if we enter the post context + if (it.limit >= allLimit) break; + } + + // Restore limit. pos.start is fine where the last transliterator + // left it, or at the end of the last run. + pos.limit = allLimit; +} + +Transliterator* AnyTransliterator::getTransliterator(UScriptCode source) const { + + if (source == targetScript || source == USCRIPT_INVALID_CODE) { + return nullptr; + } + + Transliterator* t = nullptr; + { + Mutex m(nullptr); + t = (Transliterator*) uhash_iget(cache, (int32_t) source); + } + if (t == nullptr) { + UErrorCode ec = U_ZERO_ERROR; + UnicodeString sourceName(uscript_getShortName(source), -1, US_INV); + UnicodeString id(sourceName); + id.append(TARGET_SEP).append(target); + + t = Transliterator::createInstance(id, UTRANS_FORWARD, ec); + if (U_FAILURE(ec) || t == nullptr) { + delete t; + + // Try to pivot around Latin, our most common script + id = sourceName; + id.append(LATIN_PIVOT, -1).append(target); + t = Transliterator::createInstance(id, UTRANS_FORWARD, ec); + if (U_FAILURE(ec) || t == nullptr) { + delete t; + t = nullptr; + } + } + + if (t != nullptr) { + Transliterator *rt = nullptr; + { + Mutex m(nullptr); + rt = static_cast (uhash_iget(cache, (int32_t) source)); + if (rt == nullptr) { + // Common case, no race to cache this new transliterator. + uhash_iput(cache, (int32_t) source, t, &ec); + } else { + // Race case, some other thread beat us to caching this transliterator. + Transliterator *temp = rt; + rt = t; // Our newly created transliterator that lost the race & now needs deleting. + t = temp; // The transliterator from the cache that we will return. + } + } + delete rt; // will be non-null only in case of races. + } + } + return t; +} + +/** + * Return the script code for a given name, or -1 if not found. + */ +static UScriptCode scriptNameToCode(const UnicodeString& name) { + char buf[128]; + UScriptCode code; + UErrorCode ec = U_ZERO_ERROR; + int32_t nameLen = name.length(); + UBool isInvariant = uprv_isInvariantUString(name.getBuffer(), nameLen); + + if (isInvariant) { + name.extract(0, nameLen, buf, (int32_t)sizeof(buf), US_INV); + buf[127] = 0; // Make sure that we nullptr terminate the string. + } + if (!isInvariant || uscript_getCode(buf, &code, 1, &ec) != 1 || U_FAILURE(ec)) + { + code = USCRIPT_INVALID_CODE; + } + return code; +} + +/** + * Registers standard transliterators with the system. Called by + * Transliterator during initialization. Scan all current targets and + * register those that are scripts T as Any-T/V. + */ +void AnyTransliterator::registerIDs() { + + UErrorCode ec = U_ZERO_ERROR; + Hashtable seen(true, ec); + + int32_t sourceCount = Transliterator::_countAvailableSources(); + for (int32_t s=0; s= 1); + for (int32_t v=0; v -#include -#include "unicode/putil.h" -#include "uhash.h" -#include "umutex.h" -#include "ucln_in.h" -#include "putilimp.h" -#include // for toString() - -#if defined (PI) -#undef PI -#endif - -#ifdef U_DEBUG_ASTRO -# include "uresimp.h" // for debugging - -static void debug_astro_loc(const char *f, int32_t l) -{ - fprintf(stderr, "%s:%d: ", f, l); -} - -static void debug_astro_msg(const char *pat, ...) -{ - va_list ap; - va_start(ap, pat); - vfprintf(stderr, pat, ap); - fflush(stderr); -} -#include "unicode/datefmt.h" -#include "unicode/ustring.h" -static const char * debug_astro_date(UDate d) { - static char gStrBuf[1024]; - static DateFormat *df = NULL; - if(df == NULL) { - df = DateFormat::createDateTimeInstance(DateFormat::MEDIUM, DateFormat::MEDIUM, Locale::getUS()); - df->adoptTimeZone(TimeZone::getGMT()->clone()); - } - UnicodeString str; - df->format(d,str); - u_austrncpy(gStrBuf,str.getTerminatedBuffer(),sizeof(gStrBuf)-1); - return gStrBuf; -} - -// must use double parens, i.e.: U_DEBUG_ASTRO_MSG(("four is: %d",4)); -#define U_DEBUG_ASTRO_MSG(x) {debug_astro_loc(__FILE__,__LINE__);debug_astro_msg x;} -#else -#define U_DEBUG_ASTRO_MSG(x) -#endif - -static inline UBool isINVALID(double d) { - return(uprv_isNaN(d)); -} - -static icu::UMutex ccLock; - -U_CDECL_BEGIN -static UBool calendar_astro_cleanup(void) { - return true; -} -U_CDECL_END - -U_NAMESPACE_BEGIN - -/** - * The number of standard hours in one sidereal day. - * Approximately 24.93. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -#define SIDEREAL_DAY (23.93446960027) - -/** - * The number of sidereal hours in one mean solar day. - * Approximately 24.07. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -#define SOLAR_DAY (24.065709816) - -/** - * The average number of solar days from one new moon to the next. This is the time - * it takes for the moon to return the same ecliptic longitude as the sun. - * It is longer than the sidereal month because the sun's longitude increases - * during the year due to the revolution of the earth around the sun. - * Approximately 29.53. - * - * @see #SIDEREAL_MONTH - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -const double CalendarAstronomer::SYNODIC_MONTH = 29.530588853; - -/** - * The average number of days it takes - * for the moon to return to the same ecliptic longitude relative to the - * stellar background. This is referred to as the sidereal month. - * It is shorter than the synodic month due to - * the revolution of the earth around the sun. - * Approximately 27.32. - * - * @see #SYNODIC_MONTH - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -#define SIDEREAL_MONTH 27.32166 - -/** - * The average number number of days between successive vernal equinoxes. - * Due to the precession of the earth's - * axis, this is not precisely the same as the sidereal year. - * Approximately 365.24 - * - * @see #SIDEREAL_YEAR - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -#define TROPICAL_YEAR 365.242191 - -/** - * The average number of days it takes - * for the sun to return to the same position against the fixed stellar - * background. This is the duration of one orbit of the earth about the sun - * as it would appear to an outside observer. - * Due to the precession of the earth's - * axis, this is not precisely the same as the tropical year. - * Approximately 365.25. - * - * @see #TROPICAL_YEAR - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -#define SIDEREAL_YEAR 365.25636 - -//------------------------------------------------------------------------- -// Time-related constants -//------------------------------------------------------------------------- - -/** - * The number of milliseconds in one second. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -#define SECOND_MS U_MILLIS_PER_SECOND - -/** - * The number of milliseconds in one minute. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -#define MINUTE_MS U_MILLIS_PER_MINUTE - -/** - * The number of milliseconds in one hour. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -#define HOUR_MS U_MILLIS_PER_HOUR - -/** - * The number of milliseconds in one day. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -#define DAY_MS U_MILLIS_PER_DAY - -/** - * The start of the julian day numbering scheme used by astronomers, which - * is 1/1/4713 BC (Julian), 12:00 GMT. This is given as the number of milliseconds - * since 1/1/1970 AD (Gregorian), a negative number. - * Note that julian day numbers and - * the Julian calendar are not the same thing. Also note that - * julian days start at noon, not midnight. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -#define JULIAN_EPOCH_MS -210866760000000.0 - - -/** - * Milliseconds value for 0.0 January 2000 AD. - */ -#define EPOCH_2000_MS 946598400000.0 - -//------------------------------------------------------------------------- -// Assorted private data used for conversions -//------------------------------------------------------------------------- - -// My own copies of these so compilers are more likely to optimize them away -const double CalendarAstronomer::PI = 3.14159265358979323846; - -#define CalendarAstronomer_PI2 (CalendarAstronomer::PI*2.0) -#define RAD_HOUR ( 12 / CalendarAstronomer::PI ) // radians -> hours -#define DEG_RAD ( CalendarAstronomer::PI / 180 ) // degrees -> radians -#define RAD_DEG ( 180 / CalendarAstronomer::PI ) // radians -> degrees - -/*** - * Given 'value', add or subtract 'range' until 0 <= 'value' < range. - * The modulus operator. - */ -inline static double normalize(double value, double range) { - return value - range * ClockMath::floorDivide(value, range); -} - -/** - * Normalize an angle so that it's in the range 0 - 2pi. - * For positive angles this is just (angle % 2pi), but the Java - * mod operator doesn't work that way for negative numbers.... - */ -inline static double norm2PI(double angle) { - return normalize(angle, CalendarAstronomer::PI * 2.0); -} - -/** - * Normalize an angle into the range -PI - PI - */ -inline static double normPI(double angle) { - return normalize(angle + CalendarAstronomer::PI, CalendarAstronomer::PI * 2.0) - CalendarAstronomer::PI; -} - -//------------------------------------------------------------------------- -// Constructors -//------------------------------------------------------------------------- - -/** - * Construct a new CalendarAstronomer object that is initialized to - * the current date and time. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -CalendarAstronomer::CalendarAstronomer(): - fTime(Calendar::getNow()), fLongitude(0.0), fLatitude(0.0), fGmtOffset(0.0), moonPosition(0,0), moonPositionSet(false) { - clearCache(); -} - -/** - * Construct a new CalendarAstronomer object that is initialized to - * the specified date and time. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -CalendarAstronomer::CalendarAstronomer(UDate d): fTime(d), fLongitude(0.0), fLatitude(0.0), fGmtOffset(0.0), moonPosition(0,0), moonPositionSet(false) { - clearCache(); -} - -/** - * Construct a new CalendarAstronomer object with the given - * latitude and longitude. The object's time is set to the current - * date and time. - *

- * @param longitude The desired longitude, in degrees east of - * the Greenwich meridian. - * - * @param latitude The desired latitude, in degrees. Positive - * values signify North, negative South. - * - * @see java.util.Date#getTime() - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -CalendarAstronomer::CalendarAstronomer(double longitude, double latitude) : - fTime(Calendar::getNow()), moonPosition(0,0), moonPositionSet(false) { - fLongitude = normPI(longitude * (double)DEG_RAD); - fLatitude = normPI(latitude * (double)DEG_RAD); - fGmtOffset = (double)(fLongitude * 24. * (double)HOUR_MS / (double)CalendarAstronomer_PI2); - clearCache(); -} - -CalendarAstronomer::~CalendarAstronomer() -{ -} - -//------------------------------------------------------------------------- -// Time and date getters and setters -//------------------------------------------------------------------------- - -/** - * Set the current date and time of this CalendarAstronomer object. All - * astronomical calculations are performed based on this time setting. - * - * @param aTime the date and time, expressed as the number of milliseconds since - * 1/1/1970 0:00 GMT (Gregorian). - * - * @see #setDate - * @see #getTime - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -void CalendarAstronomer::setTime(UDate aTime) { - fTime = aTime; - U_DEBUG_ASTRO_MSG(("setTime(%.1lf, %sL)\n", aTime, debug_astro_date(aTime+fGmtOffset))); - clearCache(); -} - -/** - * Set the current date and time of this CalendarAstronomer object. All - * astronomical calculations are performed based on this time setting. - * - * @param jdn the desired time, expressed as a "julian day number", - * which is the number of elapsed days since - * 1/1/4713 BC (Julian), 12:00 GMT. Note that julian day - * numbers start at noon. To get the jdn for - * the corresponding midnight, subtract 0.5. - * - * @see #getJulianDay - * @see #JULIAN_EPOCH_MS - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -void CalendarAstronomer::setJulianDay(double jdn) { - fTime = (double)(jdn * DAY_MS) + JULIAN_EPOCH_MS; - clearCache(); - julianDay = jdn; -} - -/** - * Get the current time of this CalendarAstronomer object, - * represented as the number of milliseconds since - * 1/1/1970 AD 0:00 GMT (Gregorian). - * - * @see #setTime - * @see #getDate - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -UDate CalendarAstronomer::getTime() { - return fTime; -} - -/** - * Get the current time of this CalendarAstronomer object, - * expressed as a "julian day number", which is the number of elapsed - * days since 1/1/4713 BC (Julian), 12:00 GMT. - * - * @see #setJulianDay - * @see #JULIAN_EPOCH_MS - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -double CalendarAstronomer::getJulianDay() { - if (isINVALID(julianDay)) { - julianDay = (fTime - (double)JULIAN_EPOCH_MS) / (double)DAY_MS; - } - return julianDay; -} - -/** - * Return this object's time expressed in julian centuries: - * the number of centuries after 1/1/1900 AD, 12:00 GMT - * - * @see #getJulianDay - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -double CalendarAstronomer::getJulianCentury() { - if (isINVALID(julianCentury)) { - julianCentury = (getJulianDay() - 2415020.0) / 36525.0; - } - return julianCentury; -} - -/** - * Returns the current Greenwich sidereal time, measured in hours - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -double CalendarAstronomer::getGreenwichSidereal() { - if (isINVALID(siderealTime)) { - // See page 86 of "Practical Astronomy with your Calculator", - // by Peter Duffet-Smith, for details on the algorithm. - - double UT = normalize(fTime/(double)HOUR_MS, 24.); - - siderealTime = normalize(getSiderealOffset() + UT*1.002737909, 24.); - } - return siderealTime; -} - -double CalendarAstronomer::getSiderealOffset() { - if (isINVALID(siderealT0)) { - double JD = uprv_floor(getJulianDay() - 0.5) + 0.5; - double S = JD - 2451545.0; - double T = S / 36525.0; - siderealT0 = normalize(6.697374558 + 2400.051336*T + 0.000025862*T*T, 24); - } - return siderealT0; -} - -/** - * Returns the current local sidereal time, measured in hours - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -double CalendarAstronomer::getLocalSidereal() { - return normalize(getGreenwichSidereal() + (fGmtOffset/(double)HOUR_MS), 24.); -} - -/** - * Converts local sidereal time to Universal Time. - * - * @param lst The Local Sidereal Time, in hours since sidereal midnight - * on this object's current date. - * - * @return The corresponding Universal Time, in milliseconds since - * 1 Jan 1970, GMT. - */ -double CalendarAstronomer::lstToUT(double lst) { - // Convert to local mean time - double lt = normalize((lst - getSiderealOffset()) * 0.9972695663, 24); - - // Then find local midnight on this day - double base = (DAY_MS * ClockMath::floorDivide(fTime + fGmtOffset,(double)DAY_MS)) - fGmtOffset; - - //out(" lt =" + lt + " hours"); - //out(" base=" + new Date(base)); - - return base + (long)(lt * HOUR_MS); -} - - -//------------------------------------------------------------------------- -// Coordinate transformations, all based on the current time of this object -//------------------------------------------------------------------------- - -/** - * Convert from ecliptic to equatorial coordinates. - * - * @param ecliptic A point in the sky in ecliptic coordinates. - * @return The corresponding point in equatorial coordinates. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -CalendarAstronomer::Equatorial& CalendarAstronomer::eclipticToEquatorial(CalendarAstronomer::Equatorial& result, const CalendarAstronomer::Ecliptic& ecliptic) -{ - return eclipticToEquatorial(result, ecliptic.longitude, ecliptic.latitude); -} - -/** - * Convert from ecliptic to equatorial coordinates. - * - * @param eclipLong The ecliptic longitude - * @param eclipLat The ecliptic latitude - * - * @return The corresponding point in equatorial coordinates. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -CalendarAstronomer::Equatorial& CalendarAstronomer::eclipticToEquatorial(CalendarAstronomer::Equatorial& result, double eclipLong, double eclipLat) -{ - // See page 42 of "Practical Astronomy with your Calculator", - // by Peter Duffet-Smith, for details on the algorithm. - - double obliq = eclipticObliquity(); - double sinE = ::sin(obliq); - double cosE = cos(obliq); - - double sinL = ::sin(eclipLong); - double cosL = cos(eclipLong); - - double sinB = ::sin(eclipLat); - double cosB = cos(eclipLat); - double tanB = tan(eclipLat); - - result.set(atan2(sinL*cosE - tanB*sinE, cosL), - asin(sinB*cosE + cosB*sinE*sinL) ); - return result; -} - -/** - * Convert from ecliptic longitude to equatorial coordinates. - * - * @param eclipLong The ecliptic longitude - * - * @return The corresponding point in equatorial coordinates. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -CalendarAstronomer::Equatorial& CalendarAstronomer::eclipticToEquatorial(CalendarAstronomer::Equatorial& result, double eclipLong) -{ - return eclipticToEquatorial(result, eclipLong, 0); // TODO: optimize -} - -/** - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -CalendarAstronomer::Horizon& CalendarAstronomer::eclipticToHorizon(CalendarAstronomer::Horizon& result, double eclipLong) -{ - Equatorial equatorial; - eclipticToEquatorial(equatorial, eclipLong); - - double H = getLocalSidereal()*CalendarAstronomer::PI/12 - equatorial.ascension; // Hour-angle - - double sinH = ::sin(H); - double cosH = cos(H); - double sinD = ::sin(equatorial.declination); - double cosD = cos(equatorial.declination); - double sinL = ::sin(fLatitude); - double cosL = cos(fLatitude); - - double altitude = asin(sinD*sinL + cosD*cosL*cosH); - double azimuth = atan2(-cosD*cosL*sinH, sinD - sinL * ::sin(altitude)); - - result.set(azimuth, altitude); - return result; -} - - -//------------------------------------------------------------------------- -// The Sun -//------------------------------------------------------------------------- - -// -// Parameters of the Sun's orbit as of the epoch Jan 0.0 1990 -// Angles are in radians (after multiplying by CalendarAstronomer::PI/180) -// -#define JD_EPOCH 2447891.5 // Julian day of epoch - -#define SUN_ETA_G (279.403303 * CalendarAstronomer::PI/180) // Ecliptic longitude at epoch -#define SUN_OMEGA_G (282.768422 * CalendarAstronomer::PI/180) // Ecliptic longitude of perigee -#define SUN_E 0.016713 // Eccentricity of orbit -//double sunR0 1.495585e8 // Semi-major axis in KM -//double sunTheta0 (0.533128 * CalendarAstronomer::PI/180) // Angular diameter at R0 - -// The following three methods, which compute the sun parameters -// given above for an arbitrary epoch (whatever time the object is -// set to), make only a small difference as compared to using the -// above constants. E.g., Sunset times might differ by ~12 -// seconds. Furthermore, the eta-g computation is befuddled by -// Duffet-Smith's incorrect coefficients (p.86). I've corrected -// the first-order coefficient but the others may be off too - no -// way of knowing without consulting another source. - -// /** -// * Return the sun's ecliptic longitude at perigee for the current time. -// * See Duffett-Smith, p. 86. -// * @return radians -// */ -// private double getSunOmegaG() { -// double T = getJulianCentury(); -// return (281.2208444 + (1.719175 + 0.000452778*T)*T) * DEG_RAD; -// } - -// /** -// * Return the sun's ecliptic longitude for the current time. -// * See Duffett-Smith, p. 86. -// * @return radians -// */ -// private double getSunEtaG() { -// double T = getJulianCentury(); -// //return (279.6966778 + (36000.76892 + 0.0003025*T)*T) * DEG_RAD; -// // -// // The above line is from Duffett-Smith, and yields manifestly wrong -// // results. The below constant is derived empirically to match the -// // constant he gives for the 1990 EPOCH. -// // -// return (279.6966778 + (-0.3262541582718024 + 0.0003025*T)*T) * DEG_RAD; -// } - -// /** -// * Return the sun's eccentricity of orbit for the current time. -// * See Duffett-Smith, p. 86. -// * @return double -// */ -// private double getSunE() { -// double T = getJulianCentury(); -// return 0.01675104 - (0.0000418 + 0.000000126*T)*T; -// } - -/** - * Find the "true anomaly" (longitude) of an object from - * its mean anomaly and the eccentricity of its orbit. This uses - * an iterative solution to Kepler's equation. - * - * @param meanAnomaly The object's longitude calculated as if it were in - * a regular, circular orbit, measured in radians - * from the point of perigee. - * - * @param eccentricity The eccentricity of the orbit - * - * @return The true anomaly (longitude) measured in radians - */ -static double trueAnomaly(double meanAnomaly, double eccentricity) -{ - // First, solve Kepler's equation iteratively - // Duffett-Smith, p.90 - double delta; - double E = meanAnomaly; - do { - delta = E - eccentricity * ::sin(E) - meanAnomaly; - E = E - delta / (1 - eccentricity * ::cos(E)); - } - while (uprv_fabs(delta) > 1e-5); // epsilon = 1e-5 rad - - return 2.0 * ::atan( ::tan(E/2) * ::sqrt( (1+eccentricity) - /(1-eccentricity) ) ); -} - -/** - * The longitude of the sun at the time specified by this object. - * The longitude is measured in radians along the ecliptic - * from the "first point of Aries," the point at which the ecliptic - * crosses the earth's equatorial plane at the vernal equinox. - *

- * Currently, this method uses an approximation of the two-body Kepler's - * equation for the earth and the sun. It does not take into account the - * perturbations caused by the other planets, the moon, etc. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -double CalendarAstronomer::getSunLongitude() -{ - // See page 86 of "Practical Astronomy with your Calculator", - // by Peter Duffet-Smith, for details on the algorithm. - - if (isINVALID(sunLongitude)) { - getSunLongitude(getJulianDay(), sunLongitude, meanAnomalySun); - } - return sunLongitude; -} - -/** - * TODO Make this public when the entire class is package-private. - */ -/*public*/ void CalendarAstronomer::getSunLongitude(double jDay, double &longitude, double &meanAnomaly) -{ - // See page 86 of "Practical Astronomy with your Calculator", - // by Peter Duffet-Smith, for details on the algorithm. - - double day = jDay - JD_EPOCH; // Days since epoch - - // Find the angular distance the sun in a fictitious - // circular orbit has travelled since the epoch. - double epochAngle = norm2PI(CalendarAstronomer_PI2/TROPICAL_YEAR*day); - - // The epoch wasn't at the sun's perigee; find the angular distance - // since perigee, which is called the "mean anomaly" - meanAnomaly = norm2PI(epochAngle + SUN_ETA_G - SUN_OMEGA_G); - - // Now find the "true anomaly", e.g. the real solar longitude - // by solving Kepler's equation for an elliptical orbit - // NOTE: The 3rd ed. of the book lists omega_g and eta_g in different - // equations; omega_g is to be correct. - longitude = norm2PI(trueAnomaly(meanAnomaly, SUN_E) + SUN_OMEGA_G); -} - -/** - * The position of the sun at this object's current date and time, - * in equatorial coordinates. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -CalendarAstronomer::Equatorial& CalendarAstronomer::getSunPosition(CalendarAstronomer::Equatorial& result) { - return eclipticToEquatorial(result, getSunLongitude(), 0); -} - - -/** - * Constant representing the vernal equinox. - * For use with {@link #getSunTime getSunTime}. - * Note: In this case, "vernal" refers to the northern hemisphere's seasons. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -/*double CalendarAstronomer::VERNAL_EQUINOX() { - return 0; -}*/ - -/** - * Constant representing the summer solstice. - * For use with {@link #getSunTime getSunTime}. - * Note: In this case, "summer" refers to the northern hemisphere's seasons. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -double CalendarAstronomer::SUMMER_SOLSTICE() { - return (CalendarAstronomer::PI/2); -} - -/** - * Constant representing the autumnal equinox. - * For use with {@link #getSunTime getSunTime}. - * Note: In this case, "autumn" refers to the northern hemisphere's seasons. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -/*double CalendarAstronomer::AUTUMN_EQUINOX() { - return (CalendarAstronomer::PI); -}*/ - -/** - * Constant representing the winter solstice. - * For use with {@link #getSunTime getSunTime}. - * Note: In this case, "winter" refers to the northern hemisphere's seasons. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -double CalendarAstronomer::WINTER_SOLSTICE() { - return ((CalendarAstronomer::PI*3)/2); -} - -CalendarAstronomer::AngleFunc::~AngleFunc() {} - -/** - * Find the next time at which the sun's ecliptic longitude will have - * the desired value. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -class SunTimeAngleFunc : public CalendarAstronomer::AngleFunc { -public: - virtual ~SunTimeAngleFunc(); - virtual double eval(CalendarAstronomer& a) override { return a.getSunLongitude(); } -}; - -SunTimeAngleFunc::~SunTimeAngleFunc() {} - -UDate CalendarAstronomer::getSunTime(double desired, UBool next) -{ - SunTimeAngleFunc func; - return timeOfAngle( func, - desired, - TROPICAL_YEAR, - MINUTE_MS, - next); -} - -CalendarAstronomer::CoordFunc::~CoordFunc() {} - -class RiseSetCoordFunc : public CalendarAstronomer::CoordFunc { -public: - virtual ~RiseSetCoordFunc(); - virtual void eval(CalendarAstronomer::Equatorial& result, CalendarAstronomer& a) override { a.getSunPosition(result); } -}; - -RiseSetCoordFunc::~RiseSetCoordFunc() {} - -UDate CalendarAstronomer::getSunRiseSet(UBool rise) -{ - UDate t0 = fTime; - - // Make a rough guess: 6am or 6pm local time on the current day - double noon = ClockMath::floorDivide(fTime + fGmtOffset, (double)DAY_MS)*DAY_MS - fGmtOffset + (12*HOUR_MS); - - U_DEBUG_ASTRO_MSG(("Noon=%.2lf, %sL, gmtoff %.2lf\n", noon, debug_astro_date(noon+fGmtOffset), fGmtOffset)); - setTime(noon + ((rise ? -6 : 6) * HOUR_MS)); - U_DEBUG_ASTRO_MSG(("added %.2lf ms as a guess,\n", ((rise ? -6. : 6.) * HOUR_MS))); - - RiseSetCoordFunc func; - double t = riseOrSet(func, - rise, - .533 * DEG_RAD, // Angular Diameter - 34. /60.0 * DEG_RAD, // Refraction correction - MINUTE_MS / 12.); // Desired accuracy - - setTime(t0); - return t; -} - -// Commented out - currently unused. ICU 2.6, Alan -// //------------------------------------------------------------------------- -// // Alternate Sun Rise/Set -// // See Duffett-Smith p.93 -// //------------------------------------------------------------------------- -// -// // This yields worse results (as compared to USNO data) than getSunRiseSet(). -// /** -// * TODO Make this when the entire class is package-private. -// */ -// /*public*/ long getSunRiseSet2(boolean rise) { -// // 1. Calculate coordinates of the sun's center for midnight -// double jd = uprv_floor(getJulianDay() - 0.5) + 0.5; -// double[] sl = getSunLongitude(jd);// double lambda1 = sl[0]; -// Equatorial pos1 = eclipticToEquatorial(lambda1, 0); -// -// // 2. Add ... to lambda to get position 24 hours later -// double lambda2 = lambda1 + 0.985647*DEG_RAD; -// Equatorial pos2 = eclipticToEquatorial(lambda2, 0); -// -// // 3. Calculate LSTs of rising and setting for these two positions -// double tanL = ::tan(fLatitude); -// double H = ::acos(-tanL * ::tan(pos1.declination)); -// double lst1r = (CalendarAstronomer_PI2 + pos1.ascension - H) * 24 / CalendarAstronomer_PI2; -// double lst1s = (pos1.ascension + H) * 24 / CalendarAstronomer_PI2; -// H = ::acos(-tanL * ::tan(pos2.declination)); -// double lst2r = (CalendarAstronomer_PI2-H + pos2.ascension ) * 24 / CalendarAstronomer_PI2; -// double lst2s = (H + pos2.ascension ) * 24 / CalendarAstronomer_PI2; -// if (lst1r > 24) lst1r -= 24; -// if (lst1s > 24) lst1s -= 24; -// if (lst2r > 24) lst2r -= 24; -// if (lst2s > 24) lst2s -= 24; -// -// // 4. Convert LSTs to GSTs. If GST1 > GST2, add 24 to GST2. -// double gst1r = lstToGst(lst1r); -// double gst1s = lstToGst(lst1s); -// double gst2r = lstToGst(lst2r); -// double gst2s = lstToGst(lst2s); -// if (gst1r > gst2r) gst2r += 24; -// if (gst1s > gst2s) gst2s += 24; -// -// // 5. Calculate GST at 0h UT of this date -// double t00 = utToGst(0); -// -// // 6. Calculate GST at 0h on the observer's longitude -// double offset = ::round(fLongitude*12/PI); // p.95 step 6; he _rounds_ to nearest 15 deg. -// double t00p = t00 - offset*1.002737909; -// if (t00p < 0) t00p += 24; // do NOT normalize -// -// // 7. Adjust -// if (gst1r < t00p) { -// gst1r += 24; -// gst2r += 24; -// } -// if (gst1s < t00p) { -// gst1s += 24; -// gst2s += 24; -// } -// -// // 8. -// double gstr = (24.07*gst1r-t00*(gst2r-gst1r))/(24.07+gst1r-gst2r); -// double gsts = (24.07*gst1s-t00*(gst2s-gst1s))/(24.07+gst1s-gst2s); -// -// // 9. Correct for parallax, refraction, and sun's diameter -// double dec = (pos1.declination + pos2.declination) / 2; -// double psi = ::acos(sin(fLatitude) / cos(dec)); -// double x = 0.830725 * DEG_RAD; // parallax+refraction+diameter -// double y = ::asin(sin(x) / ::sin(psi)) * RAD_DEG; -// double delta_t = 240 * y / cos(dec) / 3600; // hours -// -// // 10. Add correction to GSTs, subtract from GSTr -// gstr -= delta_t; -// gsts += delta_t; -// -// // 11. Convert GST to UT and then to local civil time -// double ut = gstToUt(rise ? gstr : gsts); -// //System.out.println((rise?"rise=":"set=") + ut + ", delta_t=" + delta_t); -// long midnight = DAY_MS * (time / DAY_MS); // Find UT midnight on this day -// return midnight + (long) (ut * 3600000); -// } - -// Commented out - currently unused. ICU 2.6, Alan -// /** -// * Convert local sidereal time to Greenwich sidereal time. -// * Section 15. Duffett-Smith p.21 -// * @param lst in hours (0..24) -// * @return GST in hours (0..24) -// */ -// double lstToGst(double lst) { -// double delta = fLongitude * 24 / CalendarAstronomer_PI2; -// return normalize(lst - delta, 24); -// } - -// Commented out - currently unused. ICU 2.6, Alan -// /** -// * Convert UT to GST on this date. -// * Section 12. Duffett-Smith p.17 -// * @param ut in hours -// * @return GST in hours -// */ -// double utToGst(double ut) { -// return normalize(getT0() + ut*1.002737909, 24); -// } - -// Commented out - currently unused. ICU 2.6, Alan -// /** -// * Convert GST to UT on this date. -// * Section 13. Duffett-Smith p.18 -// * @param gst in hours -// * @return UT in hours -// */ -// double gstToUt(double gst) { -// return normalize(gst - getT0(), 24) * 0.9972695663; -// } - -// Commented out - currently unused. ICU 2.6, Alan -// double getT0() { -// // Common computation for UT <=> GST -// -// // Find JD for 0h UT -// double jd = uprv_floor(getJulianDay() - 0.5) + 0.5; -// -// double s = jd - 2451545.0; -// double t = s / 36525.0; -// double t0 = 6.697374558 + (2400.051336 + 0.000025862*t)*t; -// return t0; -// } - -// Commented out - currently unused. ICU 2.6, Alan -// //------------------------------------------------------------------------- -// // Alternate Sun Rise/Set -// // See sci.astro FAQ -// // http://www.faqs.org/faqs/astronomy/faq/part3/section-5.html -// //------------------------------------------------------------------------- -// -// // Note: This method appears to produce inferior accuracy as -// // compared to getSunRiseSet(). -// -// /** -// * TODO Make this when the entire class is package-private. -// */ -// /*public*/ long getSunRiseSet3(boolean rise) { -// -// // Compute day number for 0.0 Jan 2000 epoch -// double d = (double)(time - EPOCH_2000_MS) / DAY_MS; -// -// // Now compute the Local Sidereal Time, LST: -// // -// double LST = 98.9818 + 0.985647352 * d + /*UT*15 + long*/ -// fLongitude*RAD_DEG; -// // -// // (east long. positive). Note that LST is here expressed in degrees, -// // where 15 degrees corresponds to one hour. Since LST really is an angle, -// // it's convenient to use one unit---degrees---throughout. -// -// // COMPUTING THE SUN'S POSITION -// // ---------------------------- -// // -// // To be able to compute the Sun's rise/set times, you need to be able to -// // compute the Sun's position at any time. First compute the "day -// // number" d as outlined above, for the desired moment. Next compute: -// // -// double oblecl = 23.4393 - 3.563E-7 * d; -// // -// double w = 282.9404 + 4.70935E-5 * d; -// double M = 356.0470 + 0.9856002585 * d; -// double e = 0.016709 - 1.151E-9 * d; -// // -// // This is the obliquity of the ecliptic, plus some of the elements of -// // the Sun's apparent orbit (i.e., really the Earth's orbit): w = -// // argument of perihelion, M = mean anomaly, e = eccentricity. -// // Semi-major axis is here assumed to be exactly 1.0 (while not strictly -// // true, this is still an accurate approximation). Next compute E, the -// // eccentric anomaly: -// // -// double E = M + e*(180/PI) * ::sin(M*DEG_RAD) * ( 1.0 + e*cos(M*DEG_RAD) ); -// // -// // where E and M are in degrees. This is it---no further iterations are -// // needed because we know e has a sufficiently small value. Next compute -// // the true anomaly, v, and the distance, r: -// // -// /* r * cos(v) = */ double A = cos(E*DEG_RAD) - e; -// /* r * ::sin(v) = */ double B = ::sqrt(1 - e*e) * ::sin(E*DEG_RAD); -// // -// // and -// // -// // r = sqrt( A*A + B*B ) -// double v = ::atan2( B, A )*RAD_DEG; -// // -// // The Sun's true longitude, slon, can now be computed: -// // -// double slon = v + w; -// // -// // Since the Sun is always at the ecliptic (or at least very very close to -// // it), we can use simplified formulae to convert slon (the Sun's ecliptic -// // longitude) to sRA and sDec (the Sun's RA and Dec): -// // -// // ::sin(slon) * cos(oblecl) -// // tan(sRA) = ------------------------- -// // cos(slon) -// // -// // ::sin(sDec) = ::sin(oblecl) * ::sin(slon) -// // -// // As was the case when computing az, the Azimuth, if possible use an -// // atan2() function to compute sRA. -// -// double sRA = ::atan2(sin(slon*DEG_RAD) * cos(oblecl*DEG_RAD), cos(slon*DEG_RAD))*RAD_DEG; -// -// double sin_sDec = ::sin(oblecl*DEG_RAD) * ::sin(slon*DEG_RAD); -// double sDec = ::asin(sin_sDec)*RAD_DEG; -// -// // COMPUTING RISE AND SET TIMES -// // ---------------------------- -// // -// // To compute when an object rises or sets, you must compute when it -// // passes the meridian and the HA of rise/set. Then the rise time is -// // the meridian time minus HA for rise/set, and the set time is the -// // meridian time plus the HA for rise/set. -// // -// // To find the meridian time, compute the Local Sidereal Time at 0h local -// // time (or 0h UT if you prefer to work in UT) as outlined above---name -// // that quantity LST0. The Meridian Time, MT, will now be: -// // -// // MT = RA - LST0 -// double MT = normalize(sRA - LST, 360); -// // -// // where "RA" is the object's Right Ascension (in degrees!). If negative, -// // add 360 deg to MT. If the object is the Sun, leave the time as it is, -// // but if it's stellar, multiply MT by 365.2422/366.2422, to convert from -// // sidereal to solar time. Now, compute HA for rise/set, name that -// // quantity HA0: -// // -// // ::sin(h0) - ::sin(lat) * ::sin(Dec) -// // cos(HA0) = --------------------------------- -// // cos(lat) * cos(Dec) -// // -// // where h0 is the altitude selected to represent rise/set. For a purely -// // mathematical horizon, set h0 = 0 and simplify to: -// // -// // cos(HA0) = - tan(lat) * tan(Dec) -// // -// // If you want to account for refraction on the atmosphere, set h0 = -35/60 -// // degrees (-35 arc minutes), and if you want to compute the rise/set times -// // for the Sun's upper limb, set h0 = -50/60 (-50 arc minutes). -// // -// double h0 = -50/60 * DEG_RAD; -// -// double HA0 = ::acos( -// (sin(h0) - ::sin(fLatitude) * sin_sDec) / -// (cos(fLatitude) * cos(sDec*DEG_RAD)))*RAD_DEG; -// -// // When HA0 has been computed, leave it as it is for the Sun but multiply -// // by 365.2422/366.2422 for stellar objects, to convert from sidereal to -// // solar time. Finally compute: -// // -// // Rise time = MT - HA0 -// // Set time = MT + HA0 -// // -// // convert the times from degrees to hours by dividing by 15. -// // -// // If you'd like to check that your calculations are accurate or just -// // need a quick result, check the USNO's Sun or Moon Rise/Set Table, -// // . -// -// double result = MT + (rise ? -HA0 : HA0); // in degrees -// -// // Find UT midnight on this day -// long midnight = DAY_MS * (time / DAY_MS); -// -// return midnight + (long) (result * 3600000 / 15); -// } - -//------------------------------------------------------------------------- -// The Moon -//------------------------------------------------------------------------- - -#define moonL0 (318.351648 * CalendarAstronomer::PI/180 ) // Mean long. at epoch -#define moonP0 ( 36.340410 * CalendarAstronomer::PI/180 ) // Mean long. of perigee -#define moonN0 ( 318.510107 * CalendarAstronomer::PI/180 ) // Mean long. of node -#define moonI ( 5.145366 * CalendarAstronomer::PI/180 ) // Inclination of orbit -#define moonE ( 0.054900 ) // Eccentricity of orbit - -// These aren't used right now -#define moonA ( 3.84401e5 ) // semi-major axis (km) -#define moonT0 ( 0.5181 * CalendarAstronomer::PI/180 ) // Angular size at distance A -#define moonPi ( 0.9507 * CalendarAstronomer::PI/180 ) // Parallax at distance A - -/** - * The position of the moon at the time set on this - * object, in equatorial coordinates. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -const CalendarAstronomer::Equatorial& CalendarAstronomer::getMoonPosition() -{ - // - // See page 142 of "Practical Astronomy with your Calculator", - // by Peter Duffet-Smith, for details on the algorithm. - // - if (moonPositionSet == false) { - // Calculate the solar longitude. Has the side effect of - // filling in "meanAnomalySun" as well. - getSunLongitude(); - - // - // Find the # of days since the epoch of our orbital parameters. - // TODO: Convert the time of day portion into ephemeris time - // - double day = getJulianDay() - JD_EPOCH; // Days since epoch - - // Calculate the mean longitude and anomaly of the moon, based on - // a circular orbit. Similar to the corresponding solar calculation. - double meanLongitude = norm2PI(13.1763966*PI/180*day + moonL0); - meanAnomalyMoon = norm2PI(meanLongitude - 0.1114041*PI/180 * day - moonP0); - - // - // Calculate the following corrections: - // Evection: the sun's gravity affects the moon's eccentricity - // Annual Eqn: variation in the effect due to earth-sun distance - // A3: correction factor (for ???) - // - double evection = 1.2739*PI/180 * ::sin(2 * (meanLongitude - sunLongitude) - - meanAnomalyMoon); - double annual = 0.1858*PI/180 * ::sin(meanAnomalySun); - double a3 = 0.3700*PI/180 * ::sin(meanAnomalySun); - - meanAnomalyMoon += evection - annual - a3; - - // - // More correction factors: - // center equation of the center correction - // a4 yet another error correction (???) - // - // TODO: Skip the equation of the center correction and solve Kepler's eqn? - // - double center = 6.2886*PI/180 * ::sin(meanAnomalyMoon); - double a4 = 0.2140*PI/180 * ::sin(2 * meanAnomalyMoon); - - // Now find the moon's corrected longitude - moonLongitude = meanLongitude + evection + center - annual + a4; - - // - // And finally, find the variation, caused by the fact that the sun's - // gravitational pull on the moon varies depending on which side of - // the earth the moon is on - // - double variation = 0.6583*CalendarAstronomer::PI/180 * ::sin(2*(moonLongitude - sunLongitude)); - - moonLongitude += variation; - - // - // What we've calculated so far is the moon's longitude in the plane - // of its own orbit. Now map to the ecliptic to get the latitude - // and longitude. First we need to find the longitude of the ascending - // node, the position on the ecliptic where it is crossed by the moon's - // orbit as it crosses from the southern to the northern hemisphere. - // - double nodeLongitude = norm2PI(moonN0 - 0.0529539*PI/180 * day); - - nodeLongitude -= 0.16*PI/180 * ::sin(meanAnomalySun); - - double y = ::sin(moonLongitude - nodeLongitude); - double x = cos(moonLongitude - nodeLongitude); - - moonEclipLong = ::atan2(y*cos(moonI), x) + nodeLongitude; - double moonEclipLat = ::asin(y * ::sin(moonI)); - - eclipticToEquatorial(moonPosition, moonEclipLong, moonEclipLat); - moonPositionSet = true; - } - return moonPosition; -} - -/** - * The "age" of the moon at the time specified in this object. - * This is really the angle between the - * current ecliptic longitudes of the sun and the moon, - * measured in radians. - * - * @see #getMoonPhase - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -double CalendarAstronomer::getMoonAge() { - // See page 147 of "Practical Astronomy with your Calculator", - // by Peter Duffet-Smith, for details on the algorithm. - // - // Force the moon's position to be calculated. We're going to use - // some the intermediate results cached during that calculation. - // - getMoonPosition(); - - return norm2PI(moonEclipLong - sunLongitude); -} - -/** - * Calculate the phase of the moon at the time set in this object. - * The returned phase is a double in the range - * 0 <= phase < 1, interpreted as follows: - *

    - *
  • 0.00: New moon - *
  • 0.25: First quarter - *
  • 0.50: Full moon - *
  • 0.75: Last quarter - *
- * - * @see #getMoonAge - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -double CalendarAstronomer::getMoonPhase() { - // See page 147 of "Practical Astronomy with your Calculator", - // by Peter Duffet-Smith, for details on the algorithm. - return 0.5 * (1 - cos(getMoonAge())); -} - -/** - * Constant representing a new moon. - * For use with {@link #getMoonTime getMoonTime} - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -const CalendarAstronomer::MoonAge CalendarAstronomer::NEW_MOON() { - return CalendarAstronomer::MoonAge(0); -} - -/** - * Constant representing the moon's first quarter. - * For use with {@link #getMoonTime getMoonTime} - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -/*const CalendarAstronomer::MoonAge CalendarAstronomer::FIRST_QUARTER() { - return CalendarAstronomer::MoonAge(CalendarAstronomer::PI/2); -}*/ - -/** - * Constant representing a full moon. - * For use with {@link #getMoonTime getMoonTime} - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -const CalendarAstronomer::MoonAge CalendarAstronomer::FULL_MOON() { - return CalendarAstronomer::MoonAge(CalendarAstronomer::PI); -} -/** - * Constant representing the moon's last quarter. - * For use with {@link #getMoonTime getMoonTime} - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ - -class MoonTimeAngleFunc : public CalendarAstronomer::AngleFunc { -public: - virtual ~MoonTimeAngleFunc(); - virtual double eval(CalendarAstronomer& a) override { return a.getMoonAge(); } -}; - -MoonTimeAngleFunc::~MoonTimeAngleFunc() {} - -/*const CalendarAstronomer::MoonAge CalendarAstronomer::LAST_QUARTER() { - return CalendarAstronomer::MoonAge((CalendarAstronomer::PI*3)/2); -}*/ - -/** - * Find the next or previous time at which the Moon's ecliptic - * longitude will have the desired value. - *

- * @param desired The desired longitude. - * @param next true if the next occurrence of the phase - * is desired, false for the previous occurrence. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -UDate CalendarAstronomer::getMoonTime(double desired, UBool next) -{ - MoonTimeAngleFunc func; - return timeOfAngle( func, - desired, - SYNODIC_MONTH, - MINUTE_MS, - next); -} - -/** - * Find the next or previous time at which the moon will be in the - * desired phase. - *

- * @param desired The desired phase of the moon. - * @param next true if the next occurrence of the phase - * is desired, false for the previous occurrence. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -UDate CalendarAstronomer::getMoonTime(const CalendarAstronomer::MoonAge& desired, UBool next) { - return getMoonTime(desired.value, next); -} - -class MoonRiseSetCoordFunc : public CalendarAstronomer::CoordFunc { -public: - virtual ~MoonRiseSetCoordFunc(); - virtual void eval(CalendarAstronomer::Equatorial& result, CalendarAstronomer& a) override { result = a.getMoonPosition(); } -}; - -MoonRiseSetCoordFunc::~MoonRiseSetCoordFunc() {} - -/** - * Returns the time (GMT) of sunrise or sunset on the local date to which - * this calendar is currently set. - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -UDate CalendarAstronomer::getMoonRiseSet(UBool rise) -{ - MoonRiseSetCoordFunc func; - return riseOrSet(func, - rise, - .533 * DEG_RAD, // Angular Diameter - 34 /60.0 * DEG_RAD, // Refraction correction - MINUTE_MS); // Desired accuracy -} - -//------------------------------------------------------------------------- -// Interpolation methods for finding the time at which a given event occurs -//------------------------------------------------------------------------- - -UDate CalendarAstronomer::timeOfAngle(AngleFunc& func, double desired, - double periodDays, double epsilon, UBool next) -{ - // Find the value of the function at the current time - double lastAngle = func.eval(*this); - - // Find out how far we are from the desired angle - double deltaAngle = norm2PI(desired - lastAngle) ; - - // Using the average period, estimate the next (or previous) time at - // which the desired angle occurs. - double deltaT = (deltaAngle + (next ? 0.0 : - CalendarAstronomer_PI2 )) * (periodDays*DAY_MS) / CalendarAstronomer_PI2; - - double lastDeltaT = deltaT; // Liu - UDate startTime = fTime; // Liu - - setTime(fTime + uprv_ceil(deltaT)); - - // Now iterate until we get the error below epsilon. Throughout - // this loop we use normPI to get values in the range -Pi to Pi, - // since we're using them as correction factors rather than absolute angles. - do { - // Evaluate the function at the time we've estimated - double angle = func.eval(*this); - - // Find the # of milliseconds per radian at this point on the curve - double factor = uprv_fabs(deltaT / normPI(angle-lastAngle)); - - // Correct the time estimate based on how far off the angle is - deltaT = normPI(desired - angle) * factor; - - // HACK: - // - // If abs(deltaT) begins to diverge we need to quit this loop. - // This only appears to happen when attempting to locate, for - // example, a new moon on the day of the new moon. E.g.: - // - // This result is correct: - // newMoon(7508(Mon Jul 23 00:00:00 CST 1990,false))= - // Sun Jul 22 10:57:41 CST 1990 - // - // But attempting to make the same call a day earlier causes deltaT - // to diverge: - // CalendarAstronomer.timeOfAngle() diverging: 1.348508727575625E9 -> - // 1.3649828540224032E9 - // newMoon(7507(Sun Jul 22 00:00:00 CST 1990,false))= - // Sun Jul 08 13:56:15 CST 1990 - // - // As a temporary solution, we catch this specific condition and - // adjust our start time by one eighth period days (either forward - // or backward) and try again. - // Liu 11/9/00 - if (uprv_fabs(deltaT) > uprv_fabs(lastDeltaT)) { - double delta = uprv_ceil (periodDays * DAY_MS / 8.0); - setTime(startTime + (next ? delta : -delta)); - return timeOfAngle(func, desired, periodDays, epsilon, next); - } - - lastDeltaT = deltaT; - lastAngle = angle; - - setTime(fTime + uprv_ceil(deltaT)); - } - while (uprv_fabs(deltaT) > epsilon); - - return fTime; -} - -UDate CalendarAstronomer::riseOrSet(CoordFunc& func, UBool rise, - double diameter, double refraction, - double epsilon) -{ - Equatorial pos; - double tanL = ::tan(fLatitude); - double deltaT = 0; - int32_t count = 0; - - // - // Calculate the object's position at the current time, then use that - // position to calculate the time of rising or setting. The position - // will be different at that time, so iterate until the error is allowable. - // - U_DEBUG_ASTRO_MSG(("setup rise=%s, dia=%.3lf, ref=%.3lf, eps=%.3lf\n", - rise?"T":"F", diameter, refraction, epsilon)); - do { - // See "Practical Astronomy With Your Calculator, section 33. - func.eval(pos, *this); - double angle = ::acos(-tanL * ::tan(pos.declination)); - double lst = ((rise ? CalendarAstronomer_PI2-angle : angle) + pos.ascension ) * 24 / CalendarAstronomer_PI2; - - // Convert from LST to Universal Time. - UDate newTime = lstToUT( lst ); - - deltaT = newTime - fTime; - setTime(newTime); - U_DEBUG_ASTRO_MSG(("%d] dT=%.3lf, angle=%.3lf, lst=%.3lf, A=%.3lf/D=%.3lf\n", - count, deltaT, angle, lst, pos.ascension, pos.declination)); - } - while (++ count < 5 && uprv_fabs(deltaT) > epsilon); - - // Calculate the correction due to refraction and the object's angular diameter - double cosD = ::cos(pos.declination); - double psi = ::acos(sin(fLatitude) / cosD); - double x = diameter / 2 + refraction; - double y = ::asin(sin(x) / ::sin(psi)); - long delta = (long)((240 * y * RAD_DEG / cosD)*SECOND_MS); - - return fTime + (rise ? -delta : delta); -} - /** - * Return the obliquity of the ecliptic (the angle between the ecliptic - * and the earth's equator) at the current time. This varies due to - * the precession of the earth's axis. - * - * @return the obliquity of the ecliptic relative to the equator, - * measured in radians. - */ -double CalendarAstronomer::eclipticObliquity() { - if (isINVALID(eclipObliquity)) { - const double epoch = 2451545.0; // 2000 AD, January 1.5 - - double T = (getJulianDay() - epoch) / 36525; - - eclipObliquity = 23.439292 - - 46.815/3600 * T - - 0.0006/3600 * T*T - + 0.00181/3600 * T*T*T; - - eclipObliquity *= DEG_RAD; - } - return eclipObliquity; -} - - -//------------------------------------------------------------------------- -// Private data -//------------------------------------------------------------------------- -void CalendarAstronomer::clearCache() { - const double INVALID = uprv_getNaN(); - - julianDay = INVALID; - julianCentury = INVALID; - sunLongitude = INVALID; - meanAnomalySun = INVALID; - moonLongitude = INVALID; - moonEclipLong = INVALID; - meanAnomalyMoon = INVALID; - eclipObliquity = INVALID; - siderealTime = INVALID; - siderealT0 = INVALID; - moonPositionSet = false; -} - -//private static void out(String s) { -// System.out.println(s); -//} - -//private static String deg(double rad) { -// return Double.toString(rad * RAD_DEG); -//} - -//private static String hours(long ms) { -// return Double.toString((double)ms / HOUR_MS) + " hours"; -//} - -/** - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ -/*UDate CalendarAstronomer::local(UDate localMillis) { - // TODO - srl ? - TimeZone *tz = TimeZone::createDefault(); - int32_t rawOffset; - int32_t dstOffset; - UErrorCode status = U_ZERO_ERROR; - tz->getOffset(localMillis, true, rawOffset, dstOffset, status); - delete tz; - return localMillis - rawOffset; -}*/ - -// Debugging functions -UnicodeString CalendarAstronomer::Ecliptic::toString() const -{ -#ifdef U_DEBUG_ASTRO - char tmp[800]; - sprintf(tmp, "[%.5f,%.5f]", longitude*RAD_DEG, latitude*RAD_DEG); - return UnicodeString(tmp, ""); -#else - return UnicodeString(); -#endif -} - -UnicodeString CalendarAstronomer::Equatorial::toString() const -{ -#ifdef U_DEBUG_ASTRO - char tmp[400]; - sprintf(tmp, "%f,%f", - (ascension*RAD_DEG), (declination*RAD_DEG)); - return UnicodeString(tmp, ""); -#else - return UnicodeString(); -#endif -} - -UnicodeString CalendarAstronomer::Horizon::toString() const -{ -#ifdef U_DEBUG_ASTRO - char tmp[800]; - sprintf(tmp, "[%.5f,%.5f]", altitude*RAD_DEG, azimuth*RAD_DEG); - return UnicodeString(tmp, ""); -#else - return UnicodeString(); -#endif -} - - -// static private String radToHms(double angle) { -// int hrs = (int) (angle*RAD_HOUR); -// int min = (int)((angle*RAD_HOUR - hrs) * 60); -// int sec = (int)((angle*RAD_HOUR - hrs - min/60.0) * 3600); - -// return Integer.toString(hrs) + "h" + min + "m" + sec + "s"; -// } - -// static private String radToDms(double angle) { -// int deg = (int) (angle*RAD_DEG); -// int min = (int)((angle*RAD_DEG - deg) * 60); -// int sec = (int)((angle*RAD_DEG - deg - min/60.0) * 3600); - -// return Integer.toString(deg) + "\u00b0" + min + "'" + sec + "\""; -// } - -// =============== Calendar Cache ================ - -void CalendarCache::createCache(CalendarCache** cache, UErrorCode& status) { - ucln_i18n_registerCleanup(UCLN_I18N_ASTRO_CALENDAR, calendar_astro_cleanup); - if(cache == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - *cache = new CalendarCache(32, status); - if(U_FAILURE(status)) { - delete *cache; - *cache = NULL; - } - } -} - -int32_t CalendarCache::get(CalendarCache** cache, int32_t key, UErrorCode &status) { - int32_t res; - - if(U_FAILURE(status)) { - return 0; - } - umtx_lock(&ccLock); - - if(*cache == NULL) { - createCache(cache, status); - if(U_FAILURE(status)) { - umtx_unlock(&ccLock); - return 0; - } - } - - res = uhash_igeti((*cache)->fTable, key); - U_DEBUG_ASTRO_MSG(("%p: GET: [%d] == %d\n", (*cache)->fTable, key, res)); - - umtx_unlock(&ccLock); - return res; -} - -void CalendarCache::put(CalendarCache** cache, int32_t key, int32_t value, UErrorCode &status) { - if(U_FAILURE(status)) { - return; - } - umtx_lock(&ccLock); - - if(*cache == NULL) { - createCache(cache, status); - if(U_FAILURE(status)) { - umtx_unlock(&ccLock); - return; - } - } - - uhash_iputi((*cache)->fTable, key, value, &status); - U_DEBUG_ASTRO_MSG(("%p: PUT: [%d] := %d\n", (*cache)->fTable, key, value)); - - umtx_unlock(&ccLock); -} - -CalendarCache::CalendarCache(int32_t size, UErrorCode &status) { - fTable = uhash_openSize(uhash_hashLong, uhash_compareLong, NULL, size, &status); - U_DEBUG_ASTRO_MSG(("%p: Opening.\n", fTable)); -} - -CalendarCache::~CalendarCache() { - if(fTable != NULL) { - U_DEBUG_ASTRO_MSG(("%p: Closing.\n", fTable)); - uhash_close(fTable); - } -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_FORMATTING +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/************************************************************************ + * Copyright (C) 1996-2012, International Business Machines Corporation + * and others. All Rights Reserved. + ************************************************************************ + * 2003-nov-07 srl Port from Java + */ + +#include "astro.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" +#include +#include +#include "unicode/putil.h" +#include "uhash.h" +#include "umutex.h" +#include "ucln_in.h" +#include "putilimp.h" +#include // for toString() + +#if defined (PI) +#undef PI +#endif + +#ifdef U_DEBUG_ASTRO +# include "uresimp.h" // for debugging + +static void debug_astro_loc(const char *f, int32_t l) +{ + fprintf(stderr, "%s:%d: ", f, l); +} + +static void debug_astro_msg(const char *pat, ...) +{ + va_list ap; + va_start(ap, pat); + vfprintf(stderr, pat, ap); + fflush(stderr); +} +#include "unicode/datefmt.h" +#include "unicode/ustring.h" +static const char * debug_astro_date(UDate d) { + static char gStrBuf[1024]; + static DateFormat *df = nullptr; + if(df == nullptr) { + df = DateFormat::createDateTimeInstance(DateFormat::MEDIUM, DateFormat::MEDIUM, Locale::getUS()); + df->adoptTimeZone(TimeZone::getGMT()->clone()); + } + UnicodeString str; + df->format(d,str); + u_austrncpy(gStrBuf,str.getTerminatedBuffer(),sizeof(gStrBuf)-1); + return gStrBuf; +} + +// must use double parens, i.e.: U_DEBUG_ASTRO_MSG(("four is: %d",4)); +#define U_DEBUG_ASTRO_MSG(x) {debug_astro_loc(__FILE__,__LINE__);debug_astro_msg x;} +#else +#define U_DEBUG_ASTRO_MSG(x) +#endif + +static inline UBool isINVALID(double d) { + return(uprv_isNaN(d)); +} + +static icu::UMutex ccLock; + +U_CDECL_BEGIN +static UBool calendar_astro_cleanup() { + return true; +} +U_CDECL_END + +U_NAMESPACE_BEGIN + +/** + * The number of standard hours in one sidereal day. + * Approximately 24.93. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +#define SIDEREAL_DAY (23.93446960027) + +/** + * The number of sidereal hours in one mean solar day. + * Approximately 24.07. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +#define SOLAR_DAY (24.065709816) + +/** + * The average number of solar days from one new moon to the next. This is the time + * it takes for the moon to return the same ecliptic longitude as the sun. + * It is longer than the sidereal month because the sun's longitude increases + * during the year due to the revolution of the earth around the sun. + * Approximately 29.53. + * + * @see #SIDEREAL_MONTH + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +const double CalendarAstronomer::SYNODIC_MONTH = 29.530588853; + +/** + * The average number of days it takes + * for the moon to return to the same ecliptic longitude relative to the + * stellar background. This is referred to as the sidereal month. + * It is shorter than the synodic month due to + * the revolution of the earth around the sun. + * Approximately 27.32. + * + * @see #SYNODIC_MONTH + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +#define SIDEREAL_MONTH 27.32166 + +/** + * The average number number of days between successive vernal equinoxes. + * Due to the precession of the earth's + * axis, this is not precisely the same as the sidereal year. + * Approximately 365.24 + * + * @see #SIDEREAL_YEAR + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +#define TROPICAL_YEAR 365.242191 + +/** + * The average number of days it takes + * for the sun to return to the same position against the fixed stellar + * background. This is the duration of one orbit of the earth about the sun + * as it would appear to an outside observer. + * Due to the precession of the earth's + * axis, this is not precisely the same as the tropical year. + * Approximately 365.25. + * + * @see #TROPICAL_YEAR + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +#define SIDEREAL_YEAR 365.25636 + +//------------------------------------------------------------------------- +// Time-related constants +//------------------------------------------------------------------------- + +/** + * The number of milliseconds in one second. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +#define SECOND_MS U_MILLIS_PER_SECOND + +/** + * The number of milliseconds in one minute. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +#define MINUTE_MS U_MILLIS_PER_MINUTE + +/** + * The number of milliseconds in one hour. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +#define HOUR_MS U_MILLIS_PER_HOUR + +/** + * The number of milliseconds in one day. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +#define DAY_MS U_MILLIS_PER_DAY + +/** + * The start of the julian day numbering scheme used by astronomers, which + * is 1/1/4713 BC (Julian), 12:00 GMT. This is given as the number of milliseconds + * since 1/1/1970 AD (Gregorian), a negative number. + * Note that julian day numbers and + * the Julian calendar are not the same thing. Also note that + * julian days start at noon, not midnight. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +#define JULIAN_EPOCH_MS -210866760000000.0 + + +/** + * Milliseconds value for 0.0 January 2000 AD. + */ +#define EPOCH_2000_MS 946598400000.0 + +//------------------------------------------------------------------------- +// Assorted private data used for conversions +//------------------------------------------------------------------------- + +// My own copies of these so compilers are more likely to optimize them away +const double CalendarAstronomer::PI = 3.14159265358979323846; + +#define CalendarAstronomer_PI2 (CalendarAstronomer::PI*2.0) +#define RAD_HOUR ( 12 / CalendarAstronomer::PI ) // radians -> hours +#define DEG_RAD ( CalendarAstronomer::PI / 180 ) // degrees -> radians +#define RAD_DEG ( 180 / CalendarAstronomer::PI ) // radians -> degrees + +/*** + * Given 'value', add or subtract 'range' until 0 <= 'value' < range. + * The modulus operator. + */ +inline static double normalize(double value, double range) { + return value - range * ClockMath::floorDivide(value, range); +} + +/** + * Normalize an angle so that it's in the range 0 - 2pi. + * For positive angles this is just (angle % 2pi), but the Java + * mod operator doesn't work that way for negative numbers.... + */ +inline static double norm2PI(double angle) { + return normalize(angle, CalendarAstronomer::PI * 2.0); +} + +/** + * Normalize an angle into the range -PI - PI + */ +inline static double normPI(double angle) { + return normalize(angle + CalendarAstronomer::PI, CalendarAstronomer::PI * 2.0) - CalendarAstronomer::PI; +} + +//------------------------------------------------------------------------- +// Constructors +//------------------------------------------------------------------------- + +/** + * Construct a new CalendarAstronomer object that is initialized to + * the current date and time. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +CalendarAstronomer::CalendarAstronomer(): + fTime(Calendar::getNow()), fLongitude(0.0), fLatitude(0.0), fGmtOffset(0.0), moonPosition(0,0), moonPositionSet(false) { + clearCache(); +} + +/** + * Construct a new CalendarAstronomer object that is initialized to + * the specified date and time. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +CalendarAstronomer::CalendarAstronomer(UDate d): fTime(d), fLongitude(0.0), fLatitude(0.0), fGmtOffset(0.0), moonPosition(0,0), moonPositionSet(false) { + clearCache(); +} + +/** + * Construct a new CalendarAstronomer object with the given + * latitude and longitude. The object's time is set to the current + * date and time. + *

+ * @param longitude The desired longitude, in degrees east of + * the Greenwich meridian. + * + * @param latitude The desired latitude, in degrees. Positive + * values signify North, negative South. + * + * @see java.util.Date#getTime() + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +CalendarAstronomer::CalendarAstronomer(double longitude, double latitude) : + fTime(Calendar::getNow()), moonPosition(0,0), moonPositionSet(false) { + fLongitude = normPI(longitude * (double)DEG_RAD); + fLatitude = normPI(latitude * (double)DEG_RAD); + fGmtOffset = (double)(fLongitude * 24. * (double)HOUR_MS / (double)CalendarAstronomer_PI2); + clearCache(); +} + +CalendarAstronomer::~CalendarAstronomer() +{ +} + +//------------------------------------------------------------------------- +// Time and date getters and setters +//------------------------------------------------------------------------- + +/** + * Set the current date and time of this CalendarAstronomer object. All + * astronomical calculations are performed based on this time setting. + * + * @param aTime the date and time, expressed as the number of milliseconds since + * 1/1/1970 0:00 GMT (Gregorian). + * + * @see #setDate + * @see #getTime + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +void CalendarAstronomer::setTime(UDate aTime) { + fTime = aTime; + U_DEBUG_ASTRO_MSG(("setTime(%.1lf, %sL)\n", aTime, debug_astro_date(aTime+fGmtOffset))); + clearCache(); +} + +/** + * Set the current date and time of this CalendarAstronomer object. All + * astronomical calculations are performed based on this time setting. + * + * @param jdn the desired time, expressed as a "julian day number", + * which is the number of elapsed days since + * 1/1/4713 BC (Julian), 12:00 GMT. Note that julian day + * numbers start at noon. To get the jdn for + * the corresponding midnight, subtract 0.5. + * + * @see #getJulianDay + * @see #JULIAN_EPOCH_MS + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +void CalendarAstronomer::setJulianDay(double jdn) { + fTime = (double)(jdn * DAY_MS) + JULIAN_EPOCH_MS; + clearCache(); + julianDay = jdn; +} + +/** + * Get the current time of this CalendarAstronomer object, + * represented as the number of milliseconds since + * 1/1/1970 AD 0:00 GMT (Gregorian). + * + * @see #setTime + * @see #getDate + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +UDate CalendarAstronomer::getTime() { + return fTime; +} + +/** + * Get the current time of this CalendarAstronomer object, + * expressed as a "julian day number", which is the number of elapsed + * days since 1/1/4713 BC (Julian), 12:00 GMT. + * + * @see #setJulianDay + * @see #JULIAN_EPOCH_MS + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +double CalendarAstronomer::getJulianDay() { + if (isINVALID(julianDay)) { + julianDay = (fTime - (double)JULIAN_EPOCH_MS) / (double)DAY_MS; + } + return julianDay; +} + +/** + * Return this object's time expressed in julian centuries: + * the number of centuries after 1/1/1900 AD, 12:00 GMT + * + * @see #getJulianDay + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +double CalendarAstronomer::getJulianCentury() { + if (isINVALID(julianCentury)) { + julianCentury = (getJulianDay() - 2415020.0) / 36525.0; + } + return julianCentury; +} + +/** + * Returns the current Greenwich sidereal time, measured in hours + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +double CalendarAstronomer::getGreenwichSidereal() { + if (isINVALID(siderealTime)) { + // See page 86 of "Practical Astronomy with your Calculator", + // by Peter Duffet-Smith, for details on the algorithm. + + double UT = normalize(fTime/(double)HOUR_MS, 24.); + + siderealTime = normalize(getSiderealOffset() + UT*1.002737909, 24.); + } + return siderealTime; +} + +double CalendarAstronomer::getSiderealOffset() { + if (isINVALID(siderealT0)) { + double JD = uprv_floor(getJulianDay() - 0.5) + 0.5; + double S = JD - 2451545.0; + double T = S / 36525.0; + siderealT0 = normalize(6.697374558 + 2400.051336*T + 0.000025862*T*T, 24); + } + return siderealT0; +} + +/** + * Returns the current local sidereal time, measured in hours + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +double CalendarAstronomer::getLocalSidereal() { + return normalize(getGreenwichSidereal() + (fGmtOffset/(double)HOUR_MS), 24.); +} + +/** + * Converts local sidereal time to Universal Time. + * + * @param lst The Local Sidereal Time, in hours since sidereal midnight + * on this object's current date. + * + * @return The corresponding Universal Time, in milliseconds since + * 1 Jan 1970, GMT. + */ +double CalendarAstronomer::lstToUT(double lst) { + // Convert to local mean time + double lt = normalize((lst - getSiderealOffset()) * 0.9972695663, 24); + + // Then find local midnight on this day + double base = (DAY_MS * ClockMath::floorDivide(fTime + fGmtOffset,(double)DAY_MS)) - fGmtOffset; + + //out(" lt =" + lt + " hours"); + //out(" base=" + new Date(base)); + + return base + (long)(lt * HOUR_MS); +} + + +//------------------------------------------------------------------------- +// Coordinate transformations, all based on the current time of this object +//------------------------------------------------------------------------- + +/** + * Convert from ecliptic to equatorial coordinates. + * + * @param ecliptic A point in the sky in ecliptic coordinates. + * @return The corresponding point in equatorial coordinates. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +CalendarAstronomer::Equatorial& CalendarAstronomer::eclipticToEquatorial(CalendarAstronomer::Equatorial& result, const CalendarAstronomer::Ecliptic& ecliptic) +{ + return eclipticToEquatorial(result, ecliptic.longitude, ecliptic.latitude); +} + +/** + * Convert from ecliptic to equatorial coordinates. + * + * @param eclipLong The ecliptic longitude + * @param eclipLat The ecliptic latitude + * + * @return The corresponding point in equatorial coordinates. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +CalendarAstronomer::Equatorial& CalendarAstronomer::eclipticToEquatorial(CalendarAstronomer::Equatorial& result, double eclipLong, double eclipLat) +{ + // See page 42 of "Practical Astronomy with your Calculator", + // by Peter Duffet-Smith, for details on the algorithm. + + double obliq = eclipticObliquity(); + double sinE = ::sin(obliq); + double cosE = cos(obliq); + + double sinL = ::sin(eclipLong); + double cosL = cos(eclipLong); + + double sinB = ::sin(eclipLat); + double cosB = cos(eclipLat); + double tanB = tan(eclipLat); + + result.set(atan2(sinL*cosE - tanB*sinE, cosL), + asin(sinB*cosE + cosB*sinE*sinL) ); + return result; +} + +/** + * Convert from ecliptic longitude to equatorial coordinates. + * + * @param eclipLong The ecliptic longitude + * + * @return The corresponding point in equatorial coordinates. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +CalendarAstronomer::Equatorial& CalendarAstronomer::eclipticToEquatorial(CalendarAstronomer::Equatorial& result, double eclipLong) +{ + return eclipticToEquatorial(result, eclipLong, 0); // TODO: optimize +} + +/** + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +CalendarAstronomer::Horizon& CalendarAstronomer::eclipticToHorizon(CalendarAstronomer::Horizon& result, double eclipLong) +{ + Equatorial equatorial; + eclipticToEquatorial(equatorial, eclipLong); + + double H = getLocalSidereal()*CalendarAstronomer::PI/12 - equatorial.ascension; // Hour-angle + + double sinH = ::sin(H); + double cosH = cos(H); + double sinD = ::sin(equatorial.declination); + double cosD = cos(equatorial.declination); + double sinL = ::sin(fLatitude); + double cosL = cos(fLatitude); + + double altitude = asin(sinD*sinL + cosD*cosL*cosH); + double azimuth = atan2(-cosD*cosL*sinH, sinD - sinL * ::sin(altitude)); + + result.set(azimuth, altitude); + return result; +} + + +//------------------------------------------------------------------------- +// The Sun +//------------------------------------------------------------------------- + +// +// Parameters of the Sun's orbit as of the epoch Jan 0.0 1990 +// Angles are in radians (after multiplying by CalendarAstronomer::PI/180) +// +#define JD_EPOCH 2447891.5 // Julian day of epoch + +#define SUN_ETA_G (279.403303 * CalendarAstronomer::PI/180) // Ecliptic longitude at epoch +#define SUN_OMEGA_G (282.768422 * CalendarAstronomer::PI/180) // Ecliptic longitude of perigee +#define SUN_E 0.016713 // Eccentricity of orbit +//double sunR0 1.495585e8 // Semi-major axis in KM +//double sunTheta0 (0.533128 * CalendarAstronomer::PI/180) // Angular diameter at R0 + +// The following three methods, which compute the sun parameters +// given above for an arbitrary epoch (whatever time the object is +// set to), make only a small difference as compared to using the +// above constants. E.g., Sunset times might differ by ~12 +// seconds. Furthermore, the eta-g computation is befuddled by +// Duffet-Smith's incorrect coefficients (p.86). I've corrected +// the first-order coefficient but the others may be off too - no +// way of knowing without consulting another source. + +// /** +// * Return the sun's ecliptic longitude at perigee for the current time. +// * See Duffett-Smith, p. 86. +// * @return radians +// */ +// private double getSunOmegaG() { +// double T = getJulianCentury(); +// return (281.2208444 + (1.719175 + 0.000452778*T)*T) * DEG_RAD; +// } + +// /** +// * Return the sun's ecliptic longitude for the current time. +// * See Duffett-Smith, p. 86. +// * @return radians +// */ +// private double getSunEtaG() { +// double T = getJulianCentury(); +// //return (279.6966778 + (36000.76892 + 0.0003025*T)*T) * DEG_RAD; +// // +// // The above line is from Duffett-Smith, and yields manifestly wrong +// // results. The below constant is derived empirically to match the +// // constant he gives for the 1990 EPOCH. +// // +// return (279.6966778 + (-0.3262541582718024 + 0.0003025*T)*T) * DEG_RAD; +// } + +// /** +// * Return the sun's eccentricity of orbit for the current time. +// * See Duffett-Smith, p. 86. +// * @return double +// */ +// private double getSunE() { +// double T = getJulianCentury(); +// return 0.01675104 - (0.0000418 + 0.000000126*T)*T; +// } + +/** + * Find the "true anomaly" (longitude) of an object from + * its mean anomaly and the eccentricity of its orbit. This uses + * an iterative solution to Kepler's equation. + * + * @param meanAnomaly The object's longitude calculated as if it were in + * a regular, circular orbit, measured in radians + * from the point of perigee. + * + * @param eccentricity The eccentricity of the orbit + * + * @return The true anomaly (longitude) measured in radians + */ +static double trueAnomaly(double meanAnomaly, double eccentricity) +{ + // First, solve Kepler's equation iteratively + // Duffett-Smith, p.90 + double delta; + double E = meanAnomaly; + do { + delta = E - eccentricity * ::sin(E) - meanAnomaly; + E = E - delta / (1 - eccentricity * ::cos(E)); + } + while (uprv_fabs(delta) > 1e-5); // epsilon = 1e-5 rad + + return 2.0 * ::atan( ::tan(E/2) * ::sqrt( (1+eccentricity) + /(1-eccentricity) ) ); +} + +/** + * The longitude of the sun at the time specified by this object. + * The longitude is measured in radians along the ecliptic + * from the "first point of Aries," the point at which the ecliptic + * crosses the earth's equatorial plane at the vernal equinox. + *

+ * Currently, this method uses an approximation of the two-body Kepler's + * equation for the earth and the sun. It does not take into account the + * perturbations caused by the other planets, the moon, etc. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +double CalendarAstronomer::getSunLongitude() +{ + // See page 86 of "Practical Astronomy with your Calculator", + // by Peter Duffet-Smith, for details on the algorithm. + + if (isINVALID(sunLongitude)) { + getSunLongitude(getJulianDay(), sunLongitude, meanAnomalySun); + } + return sunLongitude; +} + +/** + * TODO Make this public when the entire class is package-private. + */ +/*public*/ void CalendarAstronomer::getSunLongitude(double jDay, double &longitude, double &meanAnomaly) +{ + // See page 86 of "Practical Astronomy with your Calculator", + // by Peter Duffet-Smith, for details on the algorithm. + + double day = jDay - JD_EPOCH; // Days since epoch + + // Find the angular distance the sun in a fictitious + // circular orbit has travelled since the epoch. + double epochAngle = norm2PI(CalendarAstronomer_PI2/TROPICAL_YEAR*day); + + // The epoch wasn't at the sun's perigee; find the angular distance + // since perigee, which is called the "mean anomaly" + meanAnomaly = norm2PI(epochAngle + SUN_ETA_G - SUN_OMEGA_G); + + // Now find the "true anomaly", e.g. the real solar longitude + // by solving Kepler's equation for an elliptical orbit + // NOTE: The 3rd ed. of the book lists omega_g and eta_g in different + // equations; omega_g is to be correct. + longitude = norm2PI(trueAnomaly(meanAnomaly, SUN_E) + SUN_OMEGA_G); +} + +/** + * The position of the sun at this object's current date and time, + * in equatorial coordinates. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +CalendarAstronomer::Equatorial& CalendarAstronomer::getSunPosition(CalendarAstronomer::Equatorial& result) { + return eclipticToEquatorial(result, getSunLongitude(), 0); +} + + +/** + * Constant representing the vernal equinox. + * For use with {@link #getSunTime getSunTime}. + * Note: In this case, "vernal" refers to the northern hemisphere's seasons. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +/*double CalendarAstronomer::VERNAL_EQUINOX() { + return 0; +}*/ + +/** + * Constant representing the summer solstice. + * For use with {@link #getSunTime getSunTime}. + * Note: In this case, "summer" refers to the northern hemisphere's seasons. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +double CalendarAstronomer::SUMMER_SOLSTICE() { + return (CalendarAstronomer::PI/2); +} + +/** + * Constant representing the autumnal equinox. + * For use with {@link #getSunTime getSunTime}. + * Note: In this case, "autumn" refers to the northern hemisphere's seasons. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +/*double CalendarAstronomer::AUTUMN_EQUINOX() { + return (CalendarAstronomer::PI); +}*/ + +/** + * Constant representing the winter solstice. + * For use with {@link #getSunTime getSunTime}. + * Note: In this case, "winter" refers to the northern hemisphere's seasons. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +double CalendarAstronomer::WINTER_SOLSTICE() { + return ((CalendarAstronomer::PI*3)/2); +} + +CalendarAstronomer::AngleFunc::~AngleFunc() {} + +/** + * Find the next time at which the sun's ecliptic longitude will have + * the desired value. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +class SunTimeAngleFunc : public CalendarAstronomer::AngleFunc { +public: + virtual ~SunTimeAngleFunc(); + virtual double eval(CalendarAstronomer& a) override { return a.getSunLongitude(); } +}; + +SunTimeAngleFunc::~SunTimeAngleFunc() {} + +UDate CalendarAstronomer::getSunTime(double desired, UBool next) +{ + SunTimeAngleFunc func; + return timeOfAngle( func, + desired, + TROPICAL_YEAR, + MINUTE_MS, + next); +} + +CalendarAstronomer::CoordFunc::~CoordFunc() {} + +class RiseSetCoordFunc : public CalendarAstronomer::CoordFunc { +public: + virtual ~RiseSetCoordFunc(); + virtual void eval(CalendarAstronomer::Equatorial& result, CalendarAstronomer& a) override { a.getSunPosition(result); } +}; + +RiseSetCoordFunc::~RiseSetCoordFunc() {} + +UDate CalendarAstronomer::getSunRiseSet(UBool rise) +{ + UDate t0 = fTime; + + // Make a rough guess: 6am or 6pm local time on the current day + double noon = ClockMath::floorDivide(fTime + fGmtOffset, (double)DAY_MS)*DAY_MS - fGmtOffset + (12*HOUR_MS); + + U_DEBUG_ASTRO_MSG(("Noon=%.2lf, %sL, gmtoff %.2lf\n", noon, debug_astro_date(noon+fGmtOffset), fGmtOffset)); + setTime(noon + ((rise ? -6 : 6) * HOUR_MS)); + U_DEBUG_ASTRO_MSG(("added %.2lf ms as a guess,\n", ((rise ? -6. : 6.) * HOUR_MS))); + + RiseSetCoordFunc func; + double t = riseOrSet(func, + rise, + .533 * DEG_RAD, // Angular Diameter + 34. /60.0 * DEG_RAD, // Refraction correction + MINUTE_MS / 12.); // Desired accuracy + + setTime(t0); + return t; +} + +// Commented out - currently unused. ICU 2.6, Alan +// //------------------------------------------------------------------------- +// // Alternate Sun Rise/Set +// // See Duffett-Smith p.93 +// //------------------------------------------------------------------------- +// +// // This yields worse results (as compared to USNO data) than getSunRiseSet(). +// /** +// * TODO Make this when the entire class is package-private. +// */ +// /*public*/ long getSunRiseSet2(boolean rise) { +// // 1. Calculate coordinates of the sun's center for midnight +// double jd = uprv_floor(getJulianDay() - 0.5) + 0.5; +// double[] sl = getSunLongitude(jd);// double lambda1 = sl[0]; +// Equatorial pos1 = eclipticToEquatorial(lambda1, 0); +// +// // 2. Add ... to lambda to get position 24 hours later +// double lambda2 = lambda1 + 0.985647*DEG_RAD; +// Equatorial pos2 = eclipticToEquatorial(lambda2, 0); +// +// // 3. Calculate LSTs of rising and setting for these two positions +// double tanL = ::tan(fLatitude); +// double H = ::acos(-tanL * ::tan(pos1.declination)); +// double lst1r = (CalendarAstronomer_PI2 + pos1.ascension - H) * 24 / CalendarAstronomer_PI2; +// double lst1s = (pos1.ascension + H) * 24 / CalendarAstronomer_PI2; +// H = ::acos(-tanL * ::tan(pos2.declination)); +// double lst2r = (CalendarAstronomer_PI2-H + pos2.ascension ) * 24 / CalendarAstronomer_PI2; +// double lst2s = (H + pos2.ascension ) * 24 / CalendarAstronomer_PI2; +// if (lst1r > 24) lst1r -= 24; +// if (lst1s > 24) lst1s -= 24; +// if (lst2r > 24) lst2r -= 24; +// if (lst2s > 24) lst2s -= 24; +// +// // 4. Convert LSTs to GSTs. If GST1 > GST2, add 24 to GST2. +// double gst1r = lstToGst(lst1r); +// double gst1s = lstToGst(lst1s); +// double gst2r = lstToGst(lst2r); +// double gst2s = lstToGst(lst2s); +// if (gst1r > gst2r) gst2r += 24; +// if (gst1s > gst2s) gst2s += 24; +// +// // 5. Calculate GST at 0h UT of this date +// double t00 = utToGst(0); +// +// // 6. Calculate GST at 0h on the observer's longitude +// double offset = ::round(fLongitude*12/PI); // p.95 step 6; he _rounds_ to nearest 15 deg. +// double t00p = t00 - offset*1.002737909; +// if (t00p < 0) t00p += 24; // do NOT normalize +// +// // 7. Adjust +// if (gst1r < t00p) { +// gst1r += 24; +// gst2r += 24; +// } +// if (gst1s < t00p) { +// gst1s += 24; +// gst2s += 24; +// } +// +// // 8. +// double gstr = (24.07*gst1r-t00*(gst2r-gst1r))/(24.07+gst1r-gst2r); +// double gsts = (24.07*gst1s-t00*(gst2s-gst1s))/(24.07+gst1s-gst2s); +// +// // 9. Correct for parallax, refraction, and sun's diameter +// double dec = (pos1.declination + pos2.declination) / 2; +// double psi = ::acos(sin(fLatitude) / cos(dec)); +// double x = 0.830725 * DEG_RAD; // parallax+refraction+diameter +// double y = ::asin(sin(x) / ::sin(psi)) * RAD_DEG; +// double delta_t = 240 * y / cos(dec) / 3600; // hours +// +// // 10. Add correction to GSTs, subtract from GSTr +// gstr -= delta_t; +// gsts += delta_t; +// +// // 11. Convert GST to UT and then to local civil time +// double ut = gstToUt(rise ? gstr : gsts); +// //System.out.println((rise?"rise=":"set=") + ut + ", delta_t=" + delta_t); +// long midnight = DAY_MS * (time / DAY_MS); // Find UT midnight on this day +// return midnight + (long) (ut * 3600000); +// } + +// Commented out - currently unused. ICU 2.6, Alan +// /** +// * Convert local sidereal time to Greenwich sidereal time. +// * Section 15. Duffett-Smith p.21 +// * @param lst in hours (0..24) +// * @return GST in hours (0..24) +// */ +// double lstToGst(double lst) { +// double delta = fLongitude * 24 / CalendarAstronomer_PI2; +// return normalize(lst - delta, 24); +// } + +// Commented out - currently unused. ICU 2.6, Alan +// /** +// * Convert UT to GST on this date. +// * Section 12. Duffett-Smith p.17 +// * @param ut in hours +// * @return GST in hours +// */ +// double utToGst(double ut) { +// return normalize(getT0() + ut*1.002737909, 24); +// } + +// Commented out - currently unused. ICU 2.6, Alan +// /** +// * Convert GST to UT on this date. +// * Section 13. Duffett-Smith p.18 +// * @param gst in hours +// * @return UT in hours +// */ +// double gstToUt(double gst) { +// return normalize(gst - getT0(), 24) * 0.9972695663; +// } + +// Commented out - currently unused. ICU 2.6, Alan +// double getT0() { +// // Common computation for UT <=> GST +// +// // Find JD for 0h UT +// double jd = uprv_floor(getJulianDay() - 0.5) + 0.5; +// +// double s = jd - 2451545.0; +// double t = s / 36525.0; +// double t0 = 6.697374558 + (2400.051336 + 0.000025862*t)*t; +// return t0; +// } + +// Commented out - currently unused. ICU 2.6, Alan +// //------------------------------------------------------------------------- +// // Alternate Sun Rise/Set +// // See sci.astro FAQ +// // http://www.faqs.org/faqs/astronomy/faq/part3/section-5.html +// //------------------------------------------------------------------------- +// +// // Note: This method appears to produce inferior accuracy as +// // compared to getSunRiseSet(). +// +// /** +// * TODO Make this when the entire class is package-private. +// */ +// /*public*/ long getSunRiseSet3(boolean rise) { +// +// // Compute day number for 0.0 Jan 2000 epoch +// double d = (double)(time - EPOCH_2000_MS) / DAY_MS; +// +// // Now compute the Local Sidereal Time, LST: +// // +// double LST = 98.9818 + 0.985647352 * d + /*UT*15 + long*/ +// fLongitude*RAD_DEG; +// // +// // (east long. positive). Note that LST is here expressed in degrees, +// // where 15 degrees corresponds to one hour. Since LST really is an angle, +// // it's convenient to use one unit---degrees---throughout. +// +// // COMPUTING THE SUN'S POSITION +// // ---------------------------- +// // +// // To be able to compute the Sun's rise/set times, you need to be able to +// // compute the Sun's position at any time. First compute the "day +// // number" d as outlined above, for the desired moment. Next compute: +// // +// double oblecl = 23.4393 - 3.563E-7 * d; +// // +// double w = 282.9404 + 4.70935E-5 * d; +// double M = 356.0470 + 0.9856002585 * d; +// double e = 0.016709 - 1.151E-9 * d; +// // +// // This is the obliquity of the ecliptic, plus some of the elements of +// // the Sun's apparent orbit (i.e., really the Earth's orbit): w = +// // argument of perihelion, M = mean anomaly, e = eccentricity. +// // Semi-major axis is here assumed to be exactly 1.0 (while not strictly +// // true, this is still an accurate approximation). Next compute E, the +// // eccentric anomaly: +// // +// double E = M + e*(180/PI) * ::sin(M*DEG_RAD) * ( 1.0 + e*cos(M*DEG_RAD) ); +// // +// // where E and M are in degrees. This is it---no further iterations are +// // needed because we know e has a sufficiently small value. Next compute +// // the true anomaly, v, and the distance, r: +// // +// /* r * cos(v) = */ double A = cos(E*DEG_RAD) - e; +// /* r * ::sin(v) = */ double B = ::sqrt(1 - e*e) * ::sin(E*DEG_RAD); +// // +// // and +// // +// // r = sqrt( A*A + B*B ) +// double v = ::atan2( B, A )*RAD_DEG; +// // +// // The Sun's true longitude, slon, can now be computed: +// // +// double slon = v + w; +// // +// // Since the Sun is always at the ecliptic (or at least very very close to +// // it), we can use simplified formulae to convert slon (the Sun's ecliptic +// // longitude) to sRA and sDec (the Sun's RA and Dec): +// // +// // ::sin(slon) * cos(oblecl) +// // tan(sRA) = ------------------------- +// // cos(slon) +// // +// // ::sin(sDec) = ::sin(oblecl) * ::sin(slon) +// // +// // As was the case when computing az, the Azimuth, if possible use an +// // atan2() function to compute sRA. +// +// double sRA = ::atan2(sin(slon*DEG_RAD) * cos(oblecl*DEG_RAD), cos(slon*DEG_RAD))*RAD_DEG; +// +// double sin_sDec = ::sin(oblecl*DEG_RAD) * ::sin(slon*DEG_RAD); +// double sDec = ::asin(sin_sDec)*RAD_DEG; +// +// // COMPUTING RISE AND SET TIMES +// // ---------------------------- +// // +// // To compute when an object rises or sets, you must compute when it +// // passes the meridian and the HA of rise/set. Then the rise time is +// // the meridian time minus HA for rise/set, and the set time is the +// // meridian time plus the HA for rise/set. +// // +// // To find the meridian time, compute the Local Sidereal Time at 0h local +// // time (or 0h UT if you prefer to work in UT) as outlined above---name +// // that quantity LST0. The Meridian Time, MT, will now be: +// // +// // MT = RA - LST0 +// double MT = normalize(sRA - LST, 360); +// // +// // where "RA" is the object's Right Ascension (in degrees!). If negative, +// // add 360 deg to MT. If the object is the Sun, leave the time as it is, +// // but if it's stellar, multiply MT by 365.2422/366.2422, to convert from +// // sidereal to solar time. Now, compute HA for rise/set, name that +// // quantity HA0: +// // +// // ::sin(h0) - ::sin(lat) * ::sin(Dec) +// // cos(HA0) = --------------------------------- +// // cos(lat) * cos(Dec) +// // +// // where h0 is the altitude selected to represent rise/set. For a purely +// // mathematical horizon, set h0 = 0 and simplify to: +// // +// // cos(HA0) = - tan(lat) * tan(Dec) +// // +// // If you want to account for refraction on the atmosphere, set h0 = -35/60 +// // degrees (-35 arc minutes), and if you want to compute the rise/set times +// // for the Sun's upper limb, set h0 = -50/60 (-50 arc minutes). +// // +// double h0 = -50/60 * DEG_RAD; +// +// double HA0 = ::acos( +// (sin(h0) - ::sin(fLatitude) * sin_sDec) / +// (cos(fLatitude) * cos(sDec*DEG_RAD)))*RAD_DEG; +// +// // When HA0 has been computed, leave it as it is for the Sun but multiply +// // by 365.2422/366.2422 for stellar objects, to convert from sidereal to +// // solar time. Finally compute: +// // +// // Rise time = MT - HA0 +// // Set time = MT + HA0 +// // +// // convert the times from degrees to hours by dividing by 15. +// // +// // If you'd like to check that your calculations are accurate or just +// // need a quick result, check the USNO's Sun or Moon Rise/Set Table, +// // . +// +// double result = MT + (rise ? -HA0 : HA0); // in degrees +// +// // Find UT midnight on this day +// long midnight = DAY_MS * (time / DAY_MS); +// +// return midnight + (long) (result * 3600000 / 15); +// } + +//------------------------------------------------------------------------- +// The Moon +//------------------------------------------------------------------------- + +#define moonL0 (318.351648 * CalendarAstronomer::PI/180 ) // Mean long. at epoch +#define moonP0 ( 36.340410 * CalendarAstronomer::PI/180 ) // Mean long. of perigee +#define moonN0 ( 318.510107 * CalendarAstronomer::PI/180 ) // Mean long. of node +#define moonI ( 5.145366 * CalendarAstronomer::PI/180 ) // Inclination of orbit +#define moonE ( 0.054900 ) // Eccentricity of orbit + +// These aren't used right now +#define moonA ( 3.84401e5 ) // semi-major axis (km) +#define moonT0 ( 0.5181 * CalendarAstronomer::PI/180 ) // Angular size at distance A +#define moonPi ( 0.9507 * CalendarAstronomer::PI/180 ) // Parallax at distance A + +/** + * The position of the moon at the time set on this + * object, in equatorial coordinates. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +const CalendarAstronomer::Equatorial& CalendarAstronomer::getMoonPosition() +{ + // + // See page 142 of "Practical Astronomy with your Calculator", + // by Peter Duffet-Smith, for details on the algorithm. + // + if (moonPositionSet == false) { + // Calculate the solar longitude. Has the side effect of + // filling in "meanAnomalySun" as well. + getSunLongitude(); + + // + // Find the # of days since the epoch of our orbital parameters. + // TODO: Convert the time of day portion into ephemeris time + // + double day = getJulianDay() - JD_EPOCH; // Days since epoch + + // Calculate the mean longitude and anomaly of the moon, based on + // a circular orbit. Similar to the corresponding solar calculation. + double meanLongitude = norm2PI(13.1763966*PI/180*day + moonL0); + meanAnomalyMoon = norm2PI(meanLongitude - 0.1114041*PI/180 * day - moonP0); + + // + // Calculate the following corrections: + // Evection: the sun's gravity affects the moon's eccentricity + // Annual Eqn: variation in the effect due to earth-sun distance + // A3: correction factor (for ???) + // + double evection = 1.2739*PI/180 * ::sin(2 * (meanLongitude - sunLongitude) + - meanAnomalyMoon); + double annual = 0.1858*PI/180 * ::sin(meanAnomalySun); + double a3 = 0.3700*PI/180 * ::sin(meanAnomalySun); + + meanAnomalyMoon += evection - annual - a3; + + // + // More correction factors: + // center equation of the center correction + // a4 yet another error correction (???) + // + // TODO: Skip the equation of the center correction and solve Kepler's eqn? + // + double center = 6.2886*PI/180 * ::sin(meanAnomalyMoon); + double a4 = 0.2140*PI/180 * ::sin(2 * meanAnomalyMoon); + + // Now find the moon's corrected longitude + moonLongitude = meanLongitude + evection + center - annual + a4; + + // + // And finally, find the variation, caused by the fact that the sun's + // gravitational pull on the moon varies depending on which side of + // the earth the moon is on + // + double variation = 0.6583*CalendarAstronomer::PI/180 * ::sin(2*(moonLongitude - sunLongitude)); + + moonLongitude += variation; + + // + // What we've calculated so far is the moon's longitude in the plane + // of its own orbit. Now map to the ecliptic to get the latitude + // and longitude. First we need to find the longitude of the ascending + // node, the position on the ecliptic where it is crossed by the moon's + // orbit as it crosses from the southern to the northern hemisphere. + // + double nodeLongitude = norm2PI(moonN0 - 0.0529539*PI/180 * day); + + nodeLongitude -= 0.16*PI/180 * ::sin(meanAnomalySun); + + double y = ::sin(moonLongitude - nodeLongitude); + double x = cos(moonLongitude - nodeLongitude); + + moonEclipLong = ::atan2(y*cos(moonI), x) + nodeLongitude; + double moonEclipLat = ::asin(y * ::sin(moonI)); + + eclipticToEquatorial(moonPosition, moonEclipLong, moonEclipLat); + moonPositionSet = true; + } + return moonPosition; +} + +/** + * The "age" of the moon at the time specified in this object. + * This is really the angle between the + * current ecliptic longitudes of the sun and the moon, + * measured in radians. + * + * @see #getMoonPhase + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +double CalendarAstronomer::getMoonAge() { + // See page 147 of "Practical Astronomy with your Calculator", + // by Peter Duffet-Smith, for details on the algorithm. + // + // Force the moon's position to be calculated. We're going to use + // some the intermediate results cached during that calculation. + // + getMoonPosition(); + + return norm2PI(moonEclipLong - sunLongitude); +} + +/** + * Calculate the phase of the moon at the time set in this object. + * The returned phase is a double in the range + * 0 <= phase < 1, interpreted as follows: + *

    + *
  • 0.00: New moon + *
  • 0.25: First quarter + *
  • 0.50: Full moon + *
  • 0.75: Last quarter + *
+ * + * @see #getMoonAge + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +double CalendarAstronomer::getMoonPhase() { + // See page 147 of "Practical Astronomy with your Calculator", + // by Peter Duffet-Smith, for details on the algorithm. + return 0.5 * (1 - cos(getMoonAge())); +} + +/** + * Constant representing a new moon. + * For use with {@link #getMoonTime getMoonTime} + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +const CalendarAstronomer::MoonAge CalendarAstronomer::NEW_MOON() { + return CalendarAstronomer::MoonAge(0); +} + +/** + * Constant representing the moon's first quarter. + * For use with {@link #getMoonTime getMoonTime} + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +/*const CalendarAstronomer::MoonAge CalendarAstronomer::FIRST_QUARTER() { + return CalendarAstronomer::MoonAge(CalendarAstronomer::PI/2); +}*/ + +/** + * Constant representing a full moon. + * For use with {@link #getMoonTime getMoonTime} + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +const CalendarAstronomer::MoonAge CalendarAstronomer::FULL_MOON() { + return CalendarAstronomer::MoonAge(CalendarAstronomer::PI); +} +/** + * Constant representing the moon's last quarter. + * For use with {@link #getMoonTime getMoonTime} + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ + +class MoonTimeAngleFunc : public CalendarAstronomer::AngleFunc { +public: + virtual ~MoonTimeAngleFunc(); + virtual double eval(CalendarAstronomer& a) override { return a.getMoonAge(); } +}; + +MoonTimeAngleFunc::~MoonTimeAngleFunc() {} + +/*const CalendarAstronomer::MoonAge CalendarAstronomer::LAST_QUARTER() { + return CalendarAstronomer::MoonAge((CalendarAstronomer::PI*3)/2); +}*/ + +/** + * Find the next or previous time at which the Moon's ecliptic + * longitude will have the desired value. + *

+ * @param desired The desired longitude. + * @param next true if the next occurrence of the phase + * is desired, false for the previous occurrence. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +UDate CalendarAstronomer::getMoonTime(double desired, UBool next) +{ + MoonTimeAngleFunc func; + return timeOfAngle( func, + desired, + SYNODIC_MONTH, + MINUTE_MS, + next); +} + +/** + * Find the next or previous time at which the moon will be in the + * desired phase. + *

+ * @param desired The desired phase of the moon. + * @param next true if the next occurrence of the phase + * is desired, false for the previous occurrence. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +UDate CalendarAstronomer::getMoonTime(const CalendarAstronomer::MoonAge& desired, UBool next) { + return getMoonTime(desired.value, next); +} + +class MoonRiseSetCoordFunc : public CalendarAstronomer::CoordFunc { +public: + virtual ~MoonRiseSetCoordFunc(); + virtual void eval(CalendarAstronomer::Equatorial& result, CalendarAstronomer& a) override { result = a.getMoonPosition(); } +}; + +MoonRiseSetCoordFunc::~MoonRiseSetCoordFunc() {} + +/** + * Returns the time (GMT) of sunrise or sunset on the local date to which + * this calendar is currently set. + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +UDate CalendarAstronomer::getMoonRiseSet(UBool rise) +{ + MoonRiseSetCoordFunc func; + return riseOrSet(func, + rise, + .533 * DEG_RAD, // Angular Diameter + 34 /60.0 * DEG_RAD, // Refraction correction + MINUTE_MS); // Desired accuracy +} + +//------------------------------------------------------------------------- +// Interpolation methods for finding the time at which a given event occurs +//------------------------------------------------------------------------- + +UDate CalendarAstronomer::timeOfAngle(AngleFunc& func, double desired, + double periodDays, double epsilon, UBool next) +{ + // Find the value of the function at the current time + double lastAngle = func.eval(*this); + + // Find out how far we are from the desired angle + double deltaAngle = norm2PI(desired - lastAngle) ; + + // Using the average period, estimate the next (or previous) time at + // which the desired angle occurs. + double deltaT = (deltaAngle + (next ? 0.0 : - CalendarAstronomer_PI2 )) * (periodDays*DAY_MS) / CalendarAstronomer_PI2; + + double lastDeltaT = deltaT; // Liu + UDate startTime = fTime; // Liu + + setTime(fTime + uprv_ceil(deltaT)); + + // Now iterate until we get the error below epsilon. Throughout + // this loop we use normPI to get values in the range -Pi to Pi, + // since we're using them as correction factors rather than absolute angles. + do { + // Evaluate the function at the time we've estimated + double angle = func.eval(*this); + + // Find the # of milliseconds per radian at this point on the curve + double factor = uprv_fabs(deltaT / normPI(angle-lastAngle)); + + // Correct the time estimate based on how far off the angle is + deltaT = normPI(desired - angle) * factor; + + // HACK: + // + // If abs(deltaT) begins to diverge we need to quit this loop. + // This only appears to happen when attempting to locate, for + // example, a new moon on the day of the new moon. E.g.: + // + // This result is correct: + // newMoon(7508(Mon Jul 23 00:00:00 CST 1990,false))= + // Sun Jul 22 10:57:41 CST 1990 + // + // But attempting to make the same call a day earlier causes deltaT + // to diverge: + // CalendarAstronomer.timeOfAngle() diverging: 1.348508727575625E9 -> + // 1.3649828540224032E9 + // newMoon(7507(Sun Jul 22 00:00:00 CST 1990,false))= + // Sun Jul 08 13:56:15 CST 1990 + // + // As a temporary solution, we catch this specific condition and + // adjust our start time by one eighth period days (either forward + // or backward) and try again. + // Liu 11/9/00 + if (uprv_fabs(deltaT) > uprv_fabs(lastDeltaT)) { + double delta = uprv_ceil (periodDays * DAY_MS / 8.0); + setTime(startTime + (next ? delta : -delta)); + return timeOfAngle(func, desired, periodDays, epsilon, next); + } + + lastDeltaT = deltaT; + lastAngle = angle; + + setTime(fTime + uprv_ceil(deltaT)); + } + while (uprv_fabs(deltaT) > epsilon); + + return fTime; +} + +UDate CalendarAstronomer::riseOrSet(CoordFunc& func, UBool rise, + double diameter, double refraction, + double epsilon) +{ + Equatorial pos; + double tanL = ::tan(fLatitude); + double deltaT = 0; + int32_t count = 0; + + // + // Calculate the object's position at the current time, then use that + // position to calculate the time of rising or setting. The position + // will be different at that time, so iterate until the error is allowable. + // + U_DEBUG_ASTRO_MSG(("setup rise=%s, dia=%.3lf, ref=%.3lf, eps=%.3lf\n", + rise?"T":"F", diameter, refraction, epsilon)); + do { + // See "Practical Astronomy With Your Calculator, section 33. + func.eval(pos, *this); + double angle = ::acos(-tanL * ::tan(pos.declination)); + double lst = ((rise ? CalendarAstronomer_PI2-angle : angle) + pos.ascension ) * 24 / CalendarAstronomer_PI2; + + // Convert from LST to Universal Time. + UDate newTime = lstToUT( lst ); + + deltaT = newTime - fTime; + setTime(newTime); + U_DEBUG_ASTRO_MSG(("%d] dT=%.3lf, angle=%.3lf, lst=%.3lf, A=%.3lf/D=%.3lf\n", + count, deltaT, angle, lst, pos.ascension, pos.declination)); + } + while (++ count < 5 && uprv_fabs(deltaT) > epsilon); + + // Calculate the correction due to refraction and the object's angular diameter + double cosD = ::cos(pos.declination); + double psi = ::acos(sin(fLatitude) / cosD); + double x = diameter / 2 + refraction; + double y = ::asin(sin(x) / ::sin(psi)); + long delta = (long)((240 * y * RAD_DEG / cosD)*SECOND_MS); + + return fTime + (rise ? -delta : delta); +} + /** + * Return the obliquity of the ecliptic (the angle between the ecliptic + * and the earth's equator) at the current time. This varies due to + * the precession of the earth's axis. + * + * @return the obliquity of the ecliptic relative to the equator, + * measured in radians. + */ +double CalendarAstronomer::eclipticObliquity() { + if (isINVALID(eclipObliquity)) { + const double epoch = 2451545.0; // 2000 AD, January 1.5 + + double T = (getJulianDay() - epoch) / 36525; + + eclipObliquity = 23.439292 + - 46.815/3600 * T + - 0.0006/3600 * T*T + + 0.00181/3600 * T*T*T; + + eclipObliquity *= DEG_RAD; + } + return eclipObliquity; +} + + +//------------------------------------------------------------------------- +// Private data +//------------------------------------------------------------------------- +void CalendarAstronomer::clearCache() { + const double INVALID = uprv_getNaN(); + + julianDay = INVALID; + julianCentury = INVALID; + sunLongitude = INVALID; + meanAnomalySun = INVALID; + moonLongitude = INVALID; + moonEclipLong = INVALID; + meanAnomalyMoon = INVALID; + eclipObliquity = INVALID; + siderealTime = INVALID; + siderealT0 = INVALID; + moonPositionSet = false; +} + +//private static void out(String s) { +// System.out.println(s); +//} + +//private static String deg(double rad) { +// return Double.toString(rad * RAD_DEG); +//} + +//private static String hours(long ms) { +// return Double.toString((double)ms / HOUR_MS) + " hours"; +//} + +/** + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ +/*UDate CalendarAstronomer::local(UDate localMillis) { + // TODO - srl ? + TimeZone *tz = TimeZone::createDefault(); + int32_t rawOffset; + int32_t dstOffset; + UErrorCode status = U_ZERO_ERROR; + tz->getOffset(localMillis, true, rawOffset, dstOffset, status); + delete tz; + return localMillis - rawOffset; +}*/ + +// Debugging functions +UnicodeString CalendarAstronomer::Ecliptic::toString() const +{ +#ifdef U_DEBUG_ASTRO + char tmp[800]; + snprintf(tmp, sizeof(tmp), "[%.5f,%.5f]", longitude*RAD_DEG, latitude*RAD_DEG); + return UnicodeString(tmp, ""); +#else + return UnicodeString(); +#endif +} + +UnicodeString CalendarAstronomer::Equatorial::toString() const +{ +#ifdef U_DEBUG_ASTRO + char tmp[400]; + snprintf(tmp, sizeof(tmp), "%f,%f", + (ascension*RAD_DEG), (declination*RAD_DEG)); + return UnicodeString(tmp, ""); +#else + return UnicodeString(); +#endif +} + +UnicodeString CalendarAstronomer::Horizon::toString() const +{ +#ifdef U_DEBUG_ASTRO + char tmp[800]; + snprintf(tmp, sizeof(tmp), "[%.5f,%.5f]", altitude*RAD_DEG, azimuth*RAD_DEG); + return UnicodeString(tmp, ""); +#else + return UnicodeString(); +#endif +} + + +// static private String radToHms(double angle) { +// int hrs = (int) (angle*RAD_HOUR); +// int min = (int)((angle*RAD_HOUR - hrs) * 60); +// int sec = (int)((angle*RAD_HOUR - hrs - min/60.0) * 3600); + +// return Integer.toString(hrs) + "h" + min + "m" + sec + "s"; +// } + +// static private String radToDms(double angle) { +// int deg = (int) (angle*RAD_DEG); +// int min = (int)((angle*RAD_DEG - deg) * 60); +// int sec = (int)((angle*RAD_DEG - deg - min/60.0) * 3600); + +// return Integer.toString(deg) + "\u00b0" + min + "'" + sec + "\""; +// } + +// =============== Calendar Cache ================ + +void CalendarCache::createCache(CalendarCache** cache, UErrorCode& status) { + ucln_i18n_registerCleanup(UCLN_I18N_ASTRO_CALENDAR, calendar_astro_cleanup); + if(cache == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + *cache = new CalendarCache(32, status); + if(U_FAILURE(status)) { + delete *cache; + *cache = nullptr; + } + } +} + +int32_t CalendarCache::get(CalendarCache** cache, int32_t key, UErrorCode &status) { + int32_t res; + + if(U_FAILURE(status)) { + return 0; + } + umtx_lock(&ccLock); + + if(*cache == nullptr) { + createCache(cache, status); + if(U_FAILURE(status)) { + umtx_unlock(&ccLock); + return 0; + } + } + + res = uhash_igeti((*cache)->fTable, key); + U_DEBUG_ASTRO_MSG(("%p: GET: [%d] == %d\n", (*cache)->fTable, key, res)); + + umtx_unlock(&ccLock); + return res; +} + +void CalendarCache::put(CalendarCache** cache, int32_t key, int32_t value, UErrorCode &status) { + if(U_FAILURE(status)) { + return; + } + umtx_lock(&ccLock); + + if(*cache == nullptr) { + createCache(cache, status); + if(U_FAILURE(status)) { + umtx_unlock(&ccLock); + return; + } + } + + uhash_iputi((*cache)->fTable, key, value, &status); + U_DEBUG_ASTRO_MSG(("%p: PUT: [%d] := %d\n", (*cache)->fTable, key, value)); + + umtx_unlock(&ccLock); +} + +CalendarCache::CalendarCache(int32_t size, UErrorCode &status) { + fTable = uhash_openSize(uhash_hashLong, uhash_compareLong, nullptr, size, &status); + U_DEBUG_ASTRO_MSG(("%p: Opening.\n", fTable)); +} + +CalendarCache::~CalendarCache() { + if(fTable != nullptr) { + U_DEBUG_ASTRO_MSG(("%p: Closing.\n", fTable)); + uhash_close(fTable); + } +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/astro.h b/deps/icu-small/source/i18n/astro.h index 372a79ac6714cc..4798927496a878 100644 --- a/deps/icu-small/source/i18n/astro.h +++ b/deps/icu-small/source/i18n/astro.h @@ -1,757 +1,757 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/************************************************************************ - * Copyright (C) 1996-2008, International Business Machines Corporation * - * and others. All Rights Reserved. * - ************************************************************************ - * 2003-nov-07 srl Port from Java - */ - -#ifndef ASTRO_H -#define ASTRO_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "gregoimp.h" // for Math -#include "unicode/unistr.h" - -U_NAMESPACE_BEGIN - -/** - * CalendarAstronomer is a class that can perform the calculations to - * determine the positions of the sun and moon, the time of sunrise and - * sunset, and other astronomy-related data. The calculations it performs - * are in some cases quite complicated, and this utility class saves you - * the trouble of worrying about them. - *

- * The measurement of time is a very important part of astronomy. Because - * astronomical bodies are constantly in motion, observations are only valid - * at a given moment in time. Accordingly, each CalendarAstronomer - * object has a time property that determines the date - * and time for which its calculations are performed. You can set and - * retrieve this property with {@link #setDate setDate}, {@link #getDate getDate} - * and related methods. - *

- * Almost all of the calculations performed by this class, or by any - * astronomer, are approximations to various degrees of accuracy. The - * calculations in this class are mostly modelled after those described - * in the book - * - * Practical Astronomy With Your Calculator, by Peter J. - * Duffett-Smith, Cambridge University Press, 1990. This is an excellent - * book, and if you want a greater understanding of how these calculations - * are performed it a very good, readable starting point. - *

- * WARNING: This class is very early in its development, and - * it is highly likely that its API will change to some degree in the future. - * At the moment, it basically does just enough to support {@link IslamicCalendar} - * and {@link ChineseCalendar}. - * - * @author Laura Werner - * @author Alan Liu - * @internal - */ -class U_I18N_API CalendarAstronomer : public UMemory { -public: - // some classes - -public: - /** - * Represents the position of an object in the sky relative to the ecliptic, - * the plane of the earth's orbit around the Sun. - * This is a spherical coordinate system in which the latitude - * specifies the position north or south of the plane of the ecliptic. - * The longitude specifies the position along the ecliptic plane - * relative to the "First Point of Aries", which is the Sun's position in the sky - * at the Vernal Equinox. - *

- * Note that Ecliptic objects are immutable and cannot be modified - * once they are constructed. This allows them to be passed and returned by - * value without worrying about whether other code will modify them. - * - * @see CalendarAstronomer.Equatorial - * @see CalendarAstronomer.Horizon - * @internal - */ - class U_I18N_API Ecliptic : public UMemory { - public: - /** - * Constructs an Ecliptic coordinate object. - *

- * @param lat The ecliptic latitude, measured in radians. - * @param lon The ecliptic longitude, measured in radians. - * @internal - */ - Ecliptic(double lat = 0, double lon = 0) { - latitude = lat; - longitude = lon; - } - - /** - * Setter for Ecliptic Coordinate object - * @param lat The ecliptic latitude, measured in radians. - * @param lon The ecliptic longitude, measured in radians. - * @internal - */ - void set(double lat, double lon) { - latitude = lat; - longitude = lon; - } - - /** - * Return a string representation of this object - * @internal - */ - UnicodeString toString() const; - - /** - * The ecliptic latitude, in radians. This specifies an object's - * position north or south of the plane of the ecliptic, - * with positive angles representing north. - * @internal - */ - double latitude; - - /** - * The ecliptic longitude, in radians. - * This specifies an object's position along the ecliptic plane - * relative to the "First Point of Aries", which is the Sun's position - * in the sky at the Vernal Equinox, - * with positive angles representing east. - *

- * A bit of trivia: the first point of Aries is currently in the - * constellation Pisces, due to the precession of the earth's axis. - * @internal - */ - double longitude; - }; - - /** - * Represents the position of an - * object in the sky relative to the plane of the earth's equator. - * The Right Ascension specifies the position east or west - * along the equator, relative to the sun's position at the vernal - * equinox. The Declination is the position north or south - * of the equatorial plane. - *

- * Note that Equatorial objects are immutable and cannot be modified - * once they are constructed. This allows them to be passed and returned by - * value without worrying about whether other code will modify them. - * - * @see CalendarAstronomer.Ecliptic - * @see CalendarAstronomer.Horizon - * @internal - */ - class U_I18N_API Equatorial : public UMemory { - public: - /** - * Constructs an Equatorial coordinate object. - *

- * @param asc The right ascension, measured in radians. - * @param dec The declination, measured in radians. - * @internal - */ - Equatorial(double asc = 0, double dec = 0) - : ascension(asc), declination(dec) { } - - /** - * Setter - * @param asc The right ascension, measured in radians. - * @param dec The declination, measured in radians. - * @internal - */ - void set(double asc, double dec) { - ascension = asc; - declination = dec; - } - - /** - * Return a string representation of this object, with the - * angles measured in degrees. - * @internal - */ - UnicodeString toString() const; - - /** - * Return a string representation of this object with the right ascension - * measured in hours, minutes, and seconds. - * @internal - */ - //String toHmsString() { - //return radToHms(ascension) + "," + radToDms(declination); - //} - - /** - * The right ascension, in radians. - * This is the position east or west along the equator - * relative to the sun's position at the vernal equinox, - * with positive angles representing East. - * @internal - */ - double ascension; - - /** - * The declination, in radians. - * This is the position north or south of the equatorial plane, - * with positive angles representing north. - * @internal - */ - double declination; - }; - - /** - * Represents the position of an object in the sky relative to - * the local horizon. - * The Altitude represents the object's elevation above the horizon, - * with objects below the horizon having a negative altitude. - * The Azimuth is the geographic direction of the object from the - * observer's position, with 0 representing north. The azimuth increases - * clockwise from north. - *

- * Note that Horizon objects are immutable and cannot be modified - * once they are constructed. This allows them to be passed and returned by - * value without worrying about whether other code will modify them. - * - * @see CalendarAstronomer.Ecliptic - * @see CalendarAstronomer.Equatorial - * @internal - */ - class U_I18N_API Horizon : public UMemory { - public: - /** - * Constructs a Horizon coordinate object. - *

- * @param alt The altitude, measured in radians above the horizon. - * @param azim The azimuth, measured in radians clockwise from north. - * @internal - */ - Horizon(double alt=0, double azim=0) - : altitude(alt), azimuth(azim) { } - - /** - * Setter for Ecliptic Coordinate object - * @param alt The altitude, measured in radians above the horizon. - * @param azim The azimuth, measured in radians clockwise from north. - * @internal - */ - void set(double alt, double azim) { - altitude = alt; - azimuth = azim; - } - - /** - * Return a string representation of this object, with the - * angles measured in degrees. - * @internal - */ - UnicodeString toString() const; - - /** - * The object's altitude above the horizon, in radians. - * @internal - */ - double altitude; - - /** - * The object's direction, in radians clockwise from north. - * @internal - */ - double azimuth; - }; - -public: - //------------------------------------------------------------------------- - // Assorted private data used for conversions - //------------------------------------------------------------------------- - - // My own copies of these so compilers are more likely to optimize them away - static const double PI; - - /** - * The average number of solar days from one new moon to the next. This is the time - * it takes for the moon to return the same ecliptic longitude as the sun. - * It is longer than the sidereal month because the sun's longitude increases - * during the year due to the revolution of the earth around the sun. - * Approximately 29.53. - * - * @see #SIDEREAL_MONTH - * @internal - * @deprecated ICU 2.4. This class may be removed or modified. - */ - static const double SYNODIC_MONTH; - - //------------------------------------------------------------------------- - // Constructors - //------------------------------------------------------------------------- - - /** - * Construct a new CalendarAstronomer object that is initialized to - * the current date and time. - * @internal - */ - CalendarAstronomer(); - - /** - * Construct a new CalendarAstronomer object that is initialized to - * the specified date and time. - * @internal - */ - CalendarAstronomer(UDate d); - - /** - * Construct a new CalendarAstronomer object with the given - * latitude and longitude. The object's time is set to the current - * date and time. - *

- * @param longitude The desired longitude, in degrees east of - * the Greenwich meridian. - * - * @param latitude The desired latitude, in degrees. Positive - * values signify North, negative South. - * - * @see java.util.Date#getTime() - * @internal - */ - CalendarAstronomer(double longitude, double latitude); - - /** - * Destructor - * @internal - */ - ~CalendarAstronomer(); - - //------------------------------------------------------------------------- - // Time and date getters and setters - //------------------------------------------------------------------------- - - /** - * Set the current date and time of this CalendarAstronomer object. All - * astronomical calculations are performed based on this time setting. - * - * @param aTime the date and time, expressed as the number of milliseconds since - * 1/1/1970 0:00 GMT (Gregorian). - * - * @see #setDate - * @see #getTime - * @internal - */ - void setTime(UDate aTime); - - - /** - * Set the current date and time of this CalendarAstronomer object. All - * astronomical calculations are performed based on this time setting. - * - * @param aTime the date and time, expressed as the number of milliseconds since - * 1/1/1970 0:00 GMT (Gregorian). - * - * @see #getTime - * @internal - */ - void setDate(UDate aDate) { setTime(aDate); } - - /** - * Set the current date and time of this CalendarAstronomer object. All - * astronomical calculations are performed based on this time setting. - * - * @param jdn the desired time, expressed as a "julian day number", - * which is the number of elapsed days since - * 1/1/4713 BC (Julian), 12:00 GMT. Note that julian day - * numbers start at noon. To get the jdn for - * the corresponding midnight, subtract 0.5. - * - * @see #getJulianDay - * @see #JULIAN_EPOCH_MS - * @internal - */ - void setJulianDay(double jdn); - - /** - * Get the current time of this CalendarAstronomer object, - * represented as the number of milliseconds since - * 1/1/1970 AD 0:00 GMT (Gregorian). - * - * @see #setTime - * @see #getDate - * @internal - */ - UDate getTime(); - - /** - * Get the current time of this CalendarAstronomer object, - * expressed as a "julian day number", which is the number of elapsed - * days since 1/1/4713 BC (Julian), 12:00 GMT. - * - * @see #setJulianDay - * @see #JULIAN_EPOCH_MS - * @internal - */ - double getJulianDay(); - - /** - * Return this object's time expressed in julian centuries: - * the number of centuries after 1/1/1900 AD, 12:00 GMT - * - * @see #getJulianDay - * @internal - */ - double getJulianCentury(); - - /** - * Returns the current Greenwich sidereal time, measured in hours - * @internal - */ - double getGreenwichSidereal(); - -private: - double getSiderealOffset(); -public: - /** - * Returns the current local sidereal time, measured in hours - * @internal - */ - double getLocalSidereal(); - - /** - * Converts local sidereal time to Universal Time. - * - * @param lst The Local Sidereal Time, in hours since sidereal midnight - * on this object's current date. - * - * @return The corresponding Universal Time, in milliseconds since - * 1 Jan 1970, GMT. - */ - //private: - double lstToUT(double lst); - - /** - * - * Convert from ecliptic to equatorial coordinates. - * - * @param ecliptic The ecliptic - * @param result Fillin result - * @return reference to result - */ - Equatorial& eclipticToEquatorial(Equatorial& result, const Ecliptic& ecliptic); - - /** - * Convert from ecliptic to equatorial coordinates. - * - * @param eclipLong The ecliptic longitude - * @param eclipLat The ecliptic latitude - * - * @return The corresponding point in equatorial coordinates. - * @internal - */ - Equatorial& eclipticToEquatorial(Equatorial& result, double eclipLong, double eclipLat); - - /** - * Convert from ecliptic longitude to equatorial coordinates. - * - * @param eclipLong The ecliptic longitude - * - * @return The corresponding point in equatorial coordinates. - * @internal - */ - Equatorial& eclipticToEquatorial(Equatorial& result, double eclipLong) ; - - /** - * @internal - */ - Horizon& eclipticToHorizon(Horizon& result, double eclipLong) ; - - //------------------------------------------------------------------------- - // The Sun - //------------------------------------------------------------------------- - - /** - * The longitude of the sun at the time specified by this object. - * The longitude is measured in radians along the ecliptic - * from the "first point of Aries," the point at which the ecliptic - * crosses the earth's equatorial plane at the vernal equinox. - *

- * Currently, this method uses an approximation of the two-body Kepler's - * equation for the earth and the sun. It does not take into account the - * perturbations caused by the other planets, the moon, etc. - * @internal - */ - double getSunLongitude(); - - /** - * TODO Make this public when the entire class is package-private. - */ - /*public*/ void getSunLongitude(double julianDay, double &longitude, double &meanAnomaly); - - /** - * The position of the sun at this object's current date and time, - * in equatorial coordinates. - * @param result fillin for the result - * @internal - */ - Equatorial& getSunPosition(Equatorial& result); - -public: - /** - * Constant representing the vernal equinox. - * For use with {@link #getSunTime getSunTime}. - * Note: In this case, "vernal" refers to the northern hemisphere's seasons. - * @internal - */ -// static double VERNAL_EQUINOX(); - - /** - * Constant representing the summer solstice. - * For use with {@link #getSunTime getSunTime}. - * Note: In this case, "summer" refers to the northern hemisphere's seasons. - * @internal - */ - static double SUMMER_SOLSTICE(); - - /** - * Constant representing the autumnal equinox. - * For use with {@link #getSunTime getSunTime}. - * Note: In this case, "autumn" refers to the northern hemisphere's seasons. - * @internal - */ -// static double AUTUMN_EQUINOX(); - - /** - * Constant representing the winter solstice. - * For use with {@link #getSunTime getSunTime}. - * Note: In this case, "winter" refers to the northern hemisphere's seasons. - * @internal - */ - static double WINTER_SOLSTICE(); - - /** - * Find the next time at which the sun's ecliptic longitude will have - * the desired value. - * @internal - */ - UDate getSunTime(double desired, UBool next); - - /** - * Returns the time (GMT) of sunrise or sunset on the local date to which - * this calendar is currently set. - * - * NOTE: This method only works well if this object is set to a - * time near local noon. Because of variations between the local - * official time zone and the geographic longitude, the - * computation can flop over into an adjacent day if this object - * is set to a time near local midnight. - * - * @internal - */ - UDate getSunRiseSet(UBool rise); - - //------------------------------------------------------------------------- - // The Moon - //------------------------------------------------------------------------- - - /** - * The position of the moon at the time set on this - * object, in equatorial coordinates. - * @internal - * @return const reference to internal field of calendar astronomer. Do not use outside of the lifetime of this astronomer. - */ - const Equatorial& getMoonPosition(); - - /** - * The "age" of the moon at the time specified in this object. - * This is really the angle between the - * current ecliptic longitudes of the sun and the moon, - * measured in radians. - * - * @see #getMoonPhase - * @internal - */ - double getMoonAge(); - - /** - * Calculate the phase of the moon at the time set in this object. - * The returned phase is a double in the range - * 0 <= phase < 1, interpreted as follows: - *

    - *
  • 0.00: New moon - *
  • 0.25: First quarter - *
  • 0.50: Full moon - *
  • 0.75: Last quarter - *
- * - * @see #getMoonAge - * @internal - */ - double getMoonPhase(); - - class U_I18N_API MoonAge : public UMemory { - public: - MoonAge(double l) - : value(l) { } - void set(double l) { value = l; } - double value; - }; - - /** - * Constant representing a new moon. - * For use with {@link #getMoonTime getMoonTime} - * @internal - */ - static const MoonAge NEW_MOON(); - - /** - * Constant representing the moon's first quarter. - * For use with {@link #getMoonTime getMoonTime} - * @internal - */ -// static const MoonAge FIRST_QUARTER(); - - /** - * Constant representing a full moon. - * For use with {@link #getMoonTime getMoonTime} - * @internal - */ - static const MoonAge FULL_MOON(); - - /** - * Constant representing the moon's last quarter. - * For use with {@link #getMoonTime getMoonTime} - * @internal - */ -// static const MoonAge LAST_QUARTER(); - - /** - * Find the next or previous time at which the Moon's ecliptic - * longitude will have the desired value. - *

- * @param desired The desired longitude. - * @param next true if the next occurrence of the phase - * is desired, false for the previous occurrence. - * @internal - */ - UDate getMoonTime(double desired, UBool next); - UDate getMoonTime(const MoonAge& desired, UBool next); - - /** - * Returns the time (GMT) of sunrise or sunset on the local date to which - * this calendar is currently set. - * @internal - */ - UDate getMoonRiseSet(UBool rise); - - //------------------------------------------------------------------------- - // Interpolation methods for finding the time at which a given event occurs - //------------------------------------------------------------------------- - - // private - class AngleFunc : public UMemory { - public: - virtual double eval(CalendarAstronomer&) = 0; - virtual ~AngleFunc(); - }; - friend class AngleFunc; - - UDate timeOfAngle(AngleFunc& func, double desired, - double periodDays, double epsilon, UBool next); - - class CoordFunc : public UMemory { - public: - virtual void eval(Equatorial& result, CalendarAstronomer&) = 0; - virtual ~CoordFunc(); - }; - friend class CoordFunc; - - double riseOrSet(CoordFunc& func, UBool rise, - double diameter, double refraction, - double epsilon); - - //------------------------------------------------------------------------- - // Other utility methods - //------------------------------------------------------------------------- -private: - - /** - * Return the obliquity of the ecliptic (the angle between the ecliptic - * and the earth's equator) at the current time. This varies due to - * the precession of the earth's axis. - * - * @return the obliquity of the ecliptic relative to the equator, - * measured in radians. - */ - double eclipticObliquity(); - - //------------------------------------------------------------------------- - // Private data - //------------------------------------------------------------------------- -private: - /** - * Current time in milliseconds since 1/1/1970 AD - * @see java.util.Date#getTime - */ - UDate fTime; - - /* These aren't used yet, but they'll be needed for sunset calculations - * and equatorial to horizon coordinate conversions - */ - double fLongitude; - double fLatitude; - double fGmtOffset; - - // - // The following fields are used to cache calculated results for improved - // performance. These values all depend on the current time setting - // of this object, so the clearCache method is provided. - // - - double julianDay; - double julianCentury; - double sunLongitude; - double meanAnomalySun; - double moonLongitude; - double moonEclipLong; - double meanAnomalyMoon; - double eclipObliquity; - double siderealT0; - double siderealTime; - - void clearCache(); - - Equatorial moonPosition; - UBool moonPositionSet; - - /** - * @internal - */ -// UDate local(UDate localMillis); -}; - -U_NAMESPACE_END - -struct UHashtable; - -U_NAMESPACE_BEGIN - -/** - * Cache of month -> julian day - * @internal - */ -class CalendarCache : public UMemory { -public: - static int32_t get(CalendarCache** cache, int32_t key, UErrorCode &status); - static void put(CalendarCache** cache, int32_t key, int32_t value, UErrorCode &status); - virtual ~CalendarCache(); -private: - CalendarCache(int32_t size, UErrorCode& status); - static void createCache(CalendarCache** cache, UErrorCode& status); - /** - * not implemented - */ - CalendarCache(); - UHashtable *fTable; -}; - -U_NAMESPACE_END - -#endif -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/************************************************************************ + * Copyright (C) 1996-2008, International Business Machines Corporation * + * and others. All Rights Reserved. * + ************************************************************************ + * 2003-nov-07 srl Port from Java + */ + +#ifndef ASTRO_H +#define ASTRO_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "gregoimp.h" // for Math +#include "unicode/unistr.h" + +U_NAMESPACE_BEGIN + +/** + * CalendarAstronomer is a class that can perform the calculations to + * determine the positions of the sun and moon, the time of sunrise and + * sunset, and other astronomy-related data. The calculations it performs + * are in some cases quite complicated, and this utility class saves you + * the trouble of worrying about them. + *

+ * The measurement of time is a very important part of astronomy. Because + * astronomical bodies are constantly in motion, observations are only valid + * at a given moment in time. Accordingly, each CalendarAstronomer + * object has a time property that determines the date + * and time for which its calculations are performed. You can set and + * retrieve this property with {@link #setDate setDate}, {@link #getDate getDate} + * and related methods. + *

+ * Almost all of the calculations performed by this class, or by any + * astronomer, are approximations to various degrees of accuracy. The + * calculations in this class are mostly modelled after those described + * in the book + * + * Practical Astronomy With Your Calculator, by Peter J. + * Duffett-Smith, Cambridge University Press, 1990. This is an excellent + * book, and if you want a greater understanding of how these calculations + * are performed it a very good, readable starting point. + *

+ * WARNING: This class is very early in its development, and + * it is highly likely that its API will change to some degree in the future. + * At the moment, it basically does just enough to support {@link IslamicCalendar} + * and {@link ChineseCalendar}. + * + * @author Laura Werner + * @author Alan Liu + * @internal + */ +class U_I18N_API CalendarAstronomer : public UMemory { +public: + // some classes + +public: + /** + * Represents the position of an object in the sky relative to the ecliptic, + * the plane of the earth's orbit around the Sun. + * This is a spherical coordinate system in which the latitude + * specifies the position north or south of the plane of the ecliptic. + * The longitude specifies the position along the ecliptic plane + * relative to the "First Point of Aries", which is the Sun's position in the sky + * at the Vernal Equinox. + *

+ * Note that Ecliptic objects are immutable and cannot be modified + * once they are constructed. This allows them to be passed and returned by + * value without worrying about whether other code will modify them. + * + * @see CalendarAstronomer.Equatorial + * @see CalendarAstronomer.Horizon + * @internal + */ + class U_I18N_API Ecliptic : public UMemory { + public: + /** + * Constructs an Ecliptic coordinate object. + *

+ * @param lat The ecliptic latitude, measured in radians. + * @param lon The ecliptic longitude, measured in radians. + * @internal + */ + Ecliptic(double lat = 0, double lon = 0) { + latitude = lat; + longitude = lon; + } + + /** + * Setter for Ecliptic Coordinate object + * @param lat The ecliptic latitude, measured in radians. + * @param lon The ecliptic longitude, measured in radians. + * @internal + */ + void set(double lat, double lon) { + latitude = lat; + longitude = lon; + } + + /** + * Return a string representation of this object + * @internal + */ + UnicodeString toString() const; + + /** + * The ecliptic latitude, in radians. This specifies an object's + * position north or south of the plane of the ecliptic, + * with positive angles representing north. + * @internal + */ + double latitude; + + /** + * The ecliptic longitude, in radians. + * This specifies an object's position along the ecliptic plane + * relative to the "First Point of Aries", which is the Sun's position + * in the sky at the Vernal Equinox, + * with positive angles representing east. + *

+ * A bit of trivia: the first point of Aries is currently in the + * constellation Pisces, due to the precession of the earth's axis. + * @internal + */ + double longitude; + }; + + /** + * Represents the position of an + * object in the sky relative to the plane of the earth's equator. + * The Right Ascension specifies the position east or west + * along the equator, relative to the sun's position at the vernal + * equinox. The Declination is the position north or south + * of the equatorial plane. + *

+ * Note that Equatorial objects are immutable and cannot be modified + * once they are constructed. This allows them to be passed and returned by + * value without worrying about whether other code will modify them. + * + * @see CalendarAstronomer.Ecliptic + * @see CalendarAstronomer.Horizon + * @internal + */ + class U_I18N_API Equatorial : public UMemory { + public: + /** + * Constructs an Equatorial coordinate object. + *

+ * @param asc The right ascension, measured in radians. + * @param dec The declination, measured in radians. + * @internal + */ + Equatorial(double asc = 0, double dec = 0) + : ascension(asc), declination(dec) { } + + /** + * Setter + * @param asc The right ascension, measured in radians. + * @param dec The declination, measured in radians. + * @internal + */ + void set(double asc, double dec) { + ascension = asc; + declination = dec; + } + + /** + * Return a string representation of this object, with the + * angles measured in degrees. + * @internal + */ + UnicodeString toString() const; + + /** + * Return a string representation of this object with the right ascension + * measured in hours, minutes, and seconds. + * @internal + */ + //String toHmsString() { + //return radToHms(ascension) + "," + radToDms(declination); + //} + + /** + * The right ascension, in radians. + * This is the position east or west along the equator + * relative to the sun's position at the vernal equinox, + * with positive angles representing East. + * @internal + */ + double ascension; + + /** + * The declination, in radians. + * This is the position north or south of the equatorial plane, + * with positive angles representing north. + * @internal + */ + double declination; + }; + + /** + * Represents the position of an object in the sky relative to + * the local horizon. + * The Altitude represents the object's elevation above the horizon, + * with objects below the horizon having a negative altitude. + * The Azimuth is the geographic direction of the object from the + * observer's position, with 0 representing north. The azimuth increases + * clockwise from north. + *

+ * Note that Horizon objects are immutable and cannot be modified + * once they are constructed. This allows them to be passed and returned by + * value without worrying about whether other code will modify them. + * + * @see CalendarAstronomer.Ecliptic + * @see CalendarAstronomer.Equatorial + * @internal + */ + class U_I18N_API Horizon : public UMemory { + public: + /** + * Constructs a Horizon coordinate object. + *

+ * @param alt The altitude, measured in radians above the horizon. + * @param azim The azimuth, measured in radians clockwise from north. + * @internal + */ + Horizon(double alt=0, double azim=0) + : altitude(alt), azimuth(azim) { } + + /** + * Setter for Ecliptic Coordinate object + * @param alt The altitude, measured in radians above the horizon. + * @param azim The azimuth, measured in radians clockwise from north. + * @internal + */ + void set(double alt, double azim) { + altitude = alt; + azimuth = azim; + } + + /** + * Return a string representation of this object, with the + * angles measured in degrees. + * @internal + */ + UnicodeString toString() const; + + /** + * The object's altitude above the horizon, in radians. + * @internal + */ + double altitude; + + /** + * The object's direction, in radians clockwise from north. + * @internal + */ + double azimuth; + }; + +public: + //------------------------------------------------------------------------- + // Assorted private data used for conversions + //------------------------------------------------------------------------- + + // My own copies of these so compilers are more likely to optimize them away + static const double PI; + + /** + * The average number of solar days from one new moon to the next. This is the time + * it takes for the moon to return the same ecliptic longitude as the sun. + * It is longer than the sidereal month because the sun's longitude increases + * during the year due to the revolution of the earth around the sun. + * Approximately 29.53. + * + * @see #SIDEREAL_MONTH + * @internal + * @deprecated ICU 2.4. This class may be removed or modified. + */ + static const double SYNODIC_MONTH; + + //------------------------------------------------------------------------- + // Constructors + //------------------------------------------------------------------------- + + /** + * Construct a new CalendarAstronomer object that is initialized to + * the current date and time. + * @internal + */ + CalendarAstronomer(); + + /** + * Construct a new CalendarAstronomer object that is initialized to + * the specified date and time. + * @internal + */ + CalendarAstronomer(UDate d); + + /** + * Construct a new CalendarAstronomer object with the given + * latitude and longitude. The object's time is set to the current + * date and time. + *

+ * @param longitude The desired longitude, in degrees east of + * the Greenwich meridian. + * + * @param latitude The desired latitude, in degrees. Positive + * values signify North, negative South. + * + * @see java.util.Date#getTime() + * @internal + */ + CalendarAstronomer(double longitude, double latitude); + + /** + * Destructor + * @internal + */ + ~CalendarAstronomer(); + + //------------------------------------------------------------------------- + // Time and date getters and setters + //------------------------------------------------------------------------- + + /** + * Set the current date and time of this CalendarAstronomer object. All + * astronomical calculations are performed based on this time setting. + * + * @param aTime the date and time, expressed as the number of milliseconds since + * 1/1/1970 0:00 GMT (Gregorian). + * + * @see #setDate + * @see #getTime + * @internal + */ + void setTime(UDate aTime); + + + /** + * Set the current date and time of this CalendarAstronomer object. All + * astronomical calculations are performed based on this time setting. + * + * @param aTime the date and time, expressed as the number of milliseconds since + * 1/1/1970 0:00 GMT (Gregorian). + * + * @see #getTime + * @internal + */ + void setDate(UDate aDate) { setTime(aDate); } + + /** + * Set the current date and time of this CalendarAstronomer object. All + * astronomical calculations are performed based on this time setting. + * + * @param jdn the desired time, expressed as a "julian day number", + * which is the number of elapsed days since + * 1/1/4713 BC (Julian), 12:00 GMT. Note that julian day + * numbers start at noon. To get the jdn for + * the corresponding midnight, subtract 0.5. + * + * @see #getJulianDay + * @see #JULIAN_EPOCH_MS + * @internal + */ + void setJulianDay(double jdn); + + /** + * Get the current time of this CalendarAstronomer object, + * represented as the number of milliseconds since + * 1/1/1970 AD 0:00 GMT (Gregorian). + * + * @see #setTime + * @see #getDate + * @internal + */ + UDate getTime(); + + /** + * Get the current time of this CalendarAstronomer object, + * expressed as a "julian day number", which is the number of elapsed + * days since 1/1/4713 BC (Julian), 12:00 GMT. + * + * @see #setJulianDay + * @see #JULIAN_EPOCH_MS + * @internal + */ + double getJulianDay(); + + /** + * Return this object's time expressed in julian centuries: + * the number of centuries after 1/1/1900 AD, 12:00 GMT + * + * @see #getJulianDay + * @internal + */ + double getJulianCentury(); + + /** + * Returns the current Greenwich sidereal time, measured in hours + * @internal + */ + double getGreenwichSidereal(); + +private: + double getSiderealOffset(); +public: + /** + * Returns the current local sidereal time, measured in hours + * @internal + */ + double getLocalSidereal(); + + /** + * Converts local sidereal time to Universal Time. + * + * @param lst The Local Sidereal Time, in hours since sidereal midnight + * on this object's current date. + * + * @return The corresponding Universal Time, in milliseconds since + * 1 Jan 1970, GMT. + */ + //private: + double lstToUT(double lst); + + /** + * + * Convert from ecliptic to equatorial coordinates. + * + * @param ecliptic The ecliptic + * @param result Fillin result + * @return reference to result + */ + Equatorial& eclipticToEquatorial(Equatorial& result, const Ecliptic& ecliptic); + + /** + * Convert from ecliptic to equatorial coordinates. + * + * @param eclipLong The ecliptic longitude + * @param eclipLat The ecliptic latitude + * + * @return The corresponding point in equatorial coordinates. + * @internal + */ + Equatorial& eclipticToEquatorial(Equatorial& result, double eclipLong, double eclipLat); + + /** + * Convert from ecliptic longitude to equatorial coordinates. + * + * @param eclipLong The ecliptic longitude + * + * @return The corresponding point in equatorial coordinates. + * @internal + */ + Equatorial& eclipticToEquatorial(Equatorial& result, double eclipLong) ; + + /** + * @internal + */ + Horizon& eclipticToHorizon(Horizon& result, double eclipLong) ; + + //------------------------------------------------------------------------- + // The Sun + //------------------------------------------------------------------------- + + /** + * The longitude of the sun at the time specified by this object. + * The longitude is measured in radians along the ecliptic + * from the "first point of Aries," the point at which the ecliptic + * crosses the earth's equatorial plane at the vernal equinox. + *

+ * Currently, this method uses an approximation of the two-body Kepler's + * equation for the earth and the sun. It does not take into account the + * perturbations caused by the other planets, the moon, etc. + * @internal + */ + double getSunLongitude(); + + /** + * TODO Make this public when the entire class is package-private. + */ + /*public*/ void getSunLongitude(double julianDay, double &longitude, double &meanAnomaly); + + /** + * The position of the sun at this object's current date and time, + * in equatorial coordinates. + * @param result fillin for the result + * @internal + */ + Equatorial& getSunPosition(Equatorial& result); + +public: + /** + * Constant representing the vernal equinox. + * For use with {@link #getSunTime getSunTime}. + * Note: In this case, "vernal" refers to the northern hemisphere's seasons. + * @internal + */ +// static double VERNAL_EQUINOX(); + + /** + * Constant representing the summer solstice. + * For use with {@link #getSunTime getSunTime}. + * Note: In this case, "summer" refers to the northern hemisphere's seasons. + * @internal + */ + static double SUMMER_SOLSTICE(); + + /** + * Constant representing the autumnal equinox. + * For use with {@link #getSunTime getSunTime}. + * Note: In this case, "autumn" refers to the northern hemisphere's seasons. + * @internal + */ +// static double AUTUMN_EQUINOX(); + + /** + * Constant representing the winter solstice. + * For use with {@link #getSunTime getSunTime}. + * Note: In this case, "winter" refers to the northern hemisphere's seasons. + * @internal + */ + static double WINTER_SOLSTICE(); + + /** + * Find the next time at which the sun's ecliptic longitude will have + * the desired value. + * @internal + */ + UDate getSunTime(double desired, UBool next); + + /** + * Returns the time (GMT) of sunrise or sunset on the local date to which + * this calendar is currently set. + * + * NOTE: This method only works well if this object is set to a + * time near local noon. Because of variations between the local + * official time zone and the geographic longitude, the + * computation can flop over into an adjacent day if this object + * is set to a time near local midnight. + * + * @internal + */ + UDate getSunRiseSet(UBool rise); + + //------------------------------------------------------------------------- + // The Moon + //------------------------------------------------------------------------- + + /** + * The position of the moon at the time set on this + * object, in equatorial coordinates. + * @internal + * @return const reference to internal field of calendar astronomer. Do not use outside of the lifetime of this astronomer. + */ + const Equatorial& getMoonPosition(); + + /** + * The "age" of the moon at the time specified in this object. + * This is really the angle between the + * current ecliptic longitudes of the sun and the moon, + * measured in radians. + * + * @see #getMoonPhase + * @internal + */ + double getMoonAge(); + + /** + * Calculate the phase of the moon at the time set in this object. + * The returned phase is a double in the range + * 0 <= phase < 1, interpreted as follows: + *

    + *
  • 0.00: New moon + *
  • 0.25: First quarter + *
  • 0.50: Full moon + *
  • 0.75: Last quarter + *
+ * + * @see #getMoonAge + * @internal + */ + double getMoonPhase(); + + class U_I18N_API MoonAge : public UMemory { + public: + MoonAge(double l) + : value(l) { } + void set(double l) { value = l; } + double value; + }; + + /** + * Constant representing a new moon. + * For use with {@link #getMoonTime getMoonTime} + * @internal + */ + static const MoonAge NEW_MOON(); + + /** + * Constant representing the moon's first quarter. + * For use with {@link #getMoonTime getMoonTime} + * @internal + */ +// static const MoonAge FIRST_QUARTER(); + + /** + * Constant representing a full moon. + * For use with {@link #getMoonTime getMoonTime} + * @internal + */ + static const MoonAge FULL_MOON(); + + /** + * Constant representing the moon's last quarter. + * For use with {@link #getMoonTime getMoonTime} + * @internal + */ +// static const MoonAge LAST_QUARTER(); + + /** + * Find the next or previous time at which the Moon's ecliptic + * longitude will have the desired value. + *

+ * @param desired The desired longitude. + * @param next true if the next occurrence of the phase + * is desired, false for the previous occurrence. + * @internal + */ + UDate getMoonTime(double desired, UBool next); + UDate getMoonTime(const MoonAge& desired, UBool next); + + /** + * Returns the time (GMT) of sunrise or sunset on the local date to which + * this calendar is currently set. + * @internal + */ + UDate getMoonRiseSet(UBool rise); + + //------------------------------------------------------------------------- + // Interpolation methods for finding the time at which a given event occurs + //------------------------------------------------------------------------- + + // private + class AngleFunc : public UMemory { + public: + virtual double eval(CalendarAstronomer&) = 0; + virtual ~AngleFunc(); + }; + friend class AngleFunc; + + UDate timeOfAngle(AngleFunc& func, double desired, + double periodDays, double epsilon, UBool next); + + class CoordFunc : public UMemory { + public: + virtual void eval(Equatorial& result, CalendarAstronomer&) = 0; + virtual ~CoordFunc(); + }; + friend class CoordFunc; + + double riseOrSet(CoordFunc& func, UBool rise, + double diameter, double refraction, + double epsilon); + + //------------------------------------------------------------------------- + // Other utility methods + //------------------------------------------------------------------------- +private: + + /** + * Return the obliquity of the ecliptic (the angle between the ecliptic + * and the earth's equator) at the current time. This varies due to + * the precession of the earth's axis. + * + * @return the obliquity of the ecliptic relative to the equator, + * measured in radians. + */ + double eclipticObliquity(); + + //------------------------------------------------------------------------- + // Private data + //------------------------------------------------------------------------- +private: + /** + * Current time in milliseconds since 1/1/1970 AD + * @see java.util.Date#getTime + */ + UDate fTime; + + /* These aren't used yet, but they'll be needed for sunset calculations + * and equatorial to horizon coordinate conversions + */ + double fLongitude; + double fLatitude; + double fGmtOffset; + + // + // The following fields are used to cache calculated results for improved + // performance. These values all depend on the current time setting + // of this object, so the clearCache method is provided. + // + + double julianDay; + double julianCentury; + double sunLongitude; + double meanAnomalySun; + double moonLongitude; + double moonEclipLong; + double meanAnomalyMoon; + double eclipObliquity; + double siderealT0; + double siderealTime; + + void clearCache(); + + Equatorial moonPosition; + UBool moonPositionSet; + + /** + * @internal + */ +// UDate local(UDate localMillis); +}; + +U_NAMESPACE_END + +struct UHashtable; + +U_NAMESPACE_BEGIN + +/** + * Cache of month -> julian day + * @internal + */ +class CalendarCache : public UMemory { +public: + static int32_t get(CalendarCache** cache, int32_t key, UErrorCode &status); + static void put(CalendarCache** cache, int32_t key, int32_t value, UErrorCode &status); + virtual ~CalendarCache(); +private: + CalendarCache(int32_t size, UErrorCode& status); + static void createCache(CalendarCache** cache, UErrorCode& status); + /** + * not implemented + */ + CalendarCache(); + UHashtable *fTable; +}; + +U_NAMESPACE_END + +#endif +#endif diff --git a/deps/icu-small/source/i18n/basictz.cpp b/deps/icu-small/source/i18n/basictz.cpp index dfc3aea6cbc41a..8a29d5fe173ee7 100644 --- a/deps/icu-small/source/i18n/basictz.cpp +++ b/deps/icu-small/source/i18n/basictz.cpp @@ -1,539 +1,539 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2007-2013, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/basictz.h" -#include "gregoimp.h" -#include "uvector.h" -#include "cmemory.h" - -U_NAMESPACE_BEGIN - -#define MILLIS_PER_YEAR (365*24*60*60*1000.0) - -BasicTimeZone::BasicTimeZone() -: TimeZone() { -} - -BasicTimeZone::BasicTimeZone(const UnicodeString &id) -: TimeZone(id) { -} - -BasicTimeZone::BasicTimeZone(const BasicTimeZone& source) -: TimeZone(source) { -} - -BasicTimeZone::~BasicTimeZone() { -} - -UBool -BasicTimeZone::hasEquivalentTransitions(const BasicTimeZone& tz, UDate start, UDate end, - UBool ignoreDstAmount, UErrorCode& status) const { - if (U_FAILURE(status)) { - return false; - } - if (hasSameRules(tz)) { - return true; - } - // Check the offsets at the start time - int32_t raw1, raw2, dst1, dst2; - getOffset(start, false, raw1, dst1, status); - if (U_FAILURE(status)) { - return false; - } - tz.getOffset(start, false, raw2, dst2, status); - if (U_FAILURE(status)) { - return false; - } - if (ignoreDstAmount) { - if ((raw1 + dst1 != raw2 + dst2) - || (dst1 != 0 && dst2 == 0) - || (dst1 == 0 && dst2 != 0)) { - return false; - } - } else { - if (raw1 != raw2 || dst1 != dst2) { - return false; - } - } - // Check transitions in the range - UDate time = start; - TimeZoneTransition tr1, tr2; - while (true) { - UBool avail1 = getNextTransition(time, false, tr1); - UBool avail2 = tz.getNextTransition(time, false, tr2); - - if (ignoreDstAmount) { - // Skip a transition which only differ the amount of DST savings - while (true) { - if (avail1 - && tr1.getTime() <= end - && (tr1.getFrom()->getRawOffset() + tr1.getFrom()->getDSTSavings() - == tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings()) - && (tr1.getFrom()->getDSTSavings() != 0 && tr1.getTo()->getDSTSavings() != 0)) { - getNextTransition(tr1.getTime(), false, tr1); - } else { - break; - } - } - while (true) { - if (avail2 - && tr2.getTime() <= end - && (tr2.getFrom()->getRawOffset() + tr2.getFrom()->getDSTSavings() - == tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings()) - && (tr2.getFrom()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() != 0)) { - tz.getNextTransition(tr2.getTime(), false, tr2); - } else { - break; - } - } - } - - UBool inRange1 = (avail1 && tr1.getTime() <= end); - UBool inRange2 = (avail2 && tr2.getTime() <= end); - if (!inRange1 && !inRange2) { - // No more transition in the range - break; - } - if (!inRange1 || !inRange2) { - return false; - } - if (tr1.getTime() != tr2.getTime()) { - return false; - } - if (ignoreDstAmount) { - if (tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings() - != tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings() - || (tr1.getTo()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() == 0) - || (tr1.getTo()->getDSTSavings() == 0 && tr2.getTo()->getDSTSavings() != 0)) { - return false; - } - } else { - if (tr1.getTo()->getRawOffset() != tr2.getTo()->getRawOffset() || - tr1.getTo()->getDSTSavings() != tr2.getTo()->getDSTSavings()) { - return false; - } - } - time = tr1.getTime(); - } - return true; -} - -void -BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial, - AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) const { - initial = NULL; - std = NULL; - dst = NULL; - if (U_FAILURE(status)) { - return; - } - int32_t initialRaw, initialDst; - UnicodeString initialName; - - AnnualTimeZoneRule *ar1 = NULL; - AnnualTimeZoneRule *ar2 = NULL; - UnicodeString name; - - UBool avail; - TimeZoneTransition tr; - // Get the next transition - avail = getNextTransition(date, false, tr); - if (avail) { - tr.getFrom()->getName(initialName); - initialRaw = tr.getFrom()->getRawOffset(); - initialDst = tr.getFrom()->getDSTSavings(); - - // Check if the next transition is either DST->STD or STD->DST and - // within roughly 1 year from the specified date - UDate nextTransitionTime = tr.getTime(); - if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) - || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) - && (date + MILLIS_PER_YEAR > nextTransitionTime)) { - - int32_t year, month, dom, dow, doy, mid; - UDate d; - - // Get local wall time for the next transition time - Grego::timeToFields(nextTransitionTime + initialRaw + initialDst, - year, month, dom, dow, doy, mid); - int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); - // Create DOW rule - DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); - tr.getTo()->getName(name); - - // Note: SimpleTimeZone does not support raw offset change. - // So we always use raw offset of the given time for the rule, - // even raw offset is changed. This will result that the result - // zone to return wrong offset after the transition. - // When we encounter such case, we do not inspect next next - // transition for another rule. - ar1 = new AnnualTimeZoneRule(name, initialRaw, tr.getTo()->getDSTSavings(), - dtr, year, AnnualTimeZoneRule::MAX_YEAR); - - if (tr.getTo()->getRawOffset() == initialRaw) { - // Get the next next transition - avail = getNextTransition(nextTransitionTime, false, tr); - if (avail) { - // Check if the next next transition is either DST->STD or STD->DST - // and within roughly 1 year from the next transition - if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) - || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) - && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) { - - // Get local wall time for the next transition time - Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(), - year, month, dom, dow, doy, mid); - weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); - // Generate another DOW rule - dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); - tr.getTo()->getName(name); - ar2 = new AnnualTimeZoneRule(name, tr.getTo()->getRawOffset(), tr.getTo()->getDSTSavings(), - dtr, year - 1, AnnualTimeZoneRule::MAX_YEAR); - - // Make sure this rule can be applied to the specified date - avail = ar2->getPreviousStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), true, d); - if (!avail || d > date - || initialRaw != tr.getTo()->getRawOffset() - || initialDst != tr.getTo()->getDSTSavings()) { - // We cannot use this rule as the second transition rule - delete ar2; - ar2 = NULL; - } - } - } - } - if (ar2 == NULL) { - // Try previous transition - avail = getPreviousTransition(date, true, tr); - if (avail) { - // Check if the previous transition is either DST->STD or STD->DST. - // The actual transition time does not matter here. - if ((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) - || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) { - - // Generate another DOW rule - Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(), - year, month, dom, dow, doy, mid); - weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); - dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); - tr.getTo()->getName(name); - - // second rule raw/dst offsets should match raw/dst offsets - // at the given time - ar2 = new AnnualTimeZoneRule(name, initialRaw, initialDst, - dtr, ar1->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR); - - // Check if this rule start after the first rule after the specified date - avail = ar2->getNextStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), false, d); - if (!avail || d <= nextTransitionTime) { - // We cannot use this rule as the second transition rule - delete ar2; - ar2 = NULL; - } - } - } - } - if (ar2 == NULL) { - // Cannot find a good pair of AnnualTimeZoneRule - delete ar1; - ar1 = NULL; - } else { - // The initial rule should represent the rule before the previous transition - ar1->getName(initialName); - initialRaw = ar1->getRawOffset(); - initialDst = ar1->getDSTSavings(); - } - } - } - else { - // Try the previous one - avail = getPreviousTransition(date, true, tr); - if (avail) { - tr.getTo()->getName(initialName); - initialRaw = tr.getTo()->getRawOffset(); - initialDst = tr.getTo()->getDSTSavings(); - } else { - // No transitions in the past. Just use the current offsets - getOffset(date, false, initialRaw, initialDst, status); - if (U_FAILURE(status)) { - return; - } - } - } - // Set the initial rule - initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst); - - // Set the standard and daylight saving rules - if (ar1 != NULL && ar2 != NULL) { - if (ar1->getDSTSavings() != 0) { - dst = ar1; - std = ar2; - } else { - std = ar1; - dst = ar2; - } - } -} - -void -BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial, - UVector*& transitionRules, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - - const InitialTimeZoneRule *orgini; - TimeZoneTransition tzt; - bool avail; - int32_t ruleCount; - TimeZoneRule *r = nullptr; - UnicodeString name; - int32_t i; - UDate time, t; - UDate firstStart; - UBool bFinalStd = false, bFinalDst = false; - - initial = nullptr; - transitionRules = nullptr; - - // Original transition rules - ruleCount = countTransitionRules(status); - if (U_FAILURE(status)) { - return; - } - LocalPointer orgRules( - new UVector(uprv_deleteUObject, nullptr, ruleCount, status), status); - if (U_FAILURE(status)) { - return; - } - LocalMemory orgtrs( - static_cast(uprv_malloc(sizeof(TimeZoneRule*)*ruleCount))); - if (orgtrs.isNull()) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - getTimeZoneRules(orgini, &orgtrs[0], ruleCount, status); - if (U_FAILURE(status)) { - return; - } - for (i = 0; i < ruleCount; i++) { - LocalPointer lpRule(orgtrs[i]->clone(), status); - orgRules->adoptElement(lpRule.orphan(), status); - if (U_FAILURE(status)) { - return; - } - } - - avail = getPreviousTransition(start, true, tzt); - if (!avail) { - // No need to filter out rules only applicable to time before the start - initial = orgini->clone(); - if (initial == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - transitionRules = orgRules.orphan(); - return; - } - - LocalMemory done(static_cast(uprv_malloc(sizeof(bool)*ruleCount))); - if (done.isNull()) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - LocalPointer filteredRules( - new UVector(uprv_deleteUObject, nullptr, status), status); - if (U_FAILURE(status)) { - return; - } - - // Create initial rule - tzt.getTo()->getName(name); - LocalPointer res_initial( - new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(), tzt.getTo()->getDSTSavings()), status); - if (U_FAILURE(status)) { - return; - } - - // Mark rules which does not need to be processed - for (i = 0; i < ruleCount; i++) { - r = (TimeZoneRule*)orgRules->elementAt(i); - avail = r->getNextStart(start, res_initial->getRawOffset(), res_initial->getDSTSavings(), false, time); - done[i] = !avail; - } - - time = start; - while (!bFinalStd || !bFinalDst) { - avail = getNextTransition(time, false, tzt); - if (!avail) { - break; - } - UDate updatedTime = tzt.getTime(); - if (updatedTime == time) { - // Can get here if rules for start & end of daylight time have exactly - // the same time. - // TODO: fix getNextTransition() to prevent it? - status = U_INVALID_STATE_ERROR; - return; - } - time = updatedTime; - - const TimeZoneRule *toRule = tzt.getTo(); - for (i = 0; i < ruleCount; i++) { - r = (TimeZoneRule*)orgRules->elementAt(i); - if (*r == *toRule) { - break; - } - } - if (i >= ruleCount) { - // This case should never happen - status = U_INVALID_STATE_ERROR; - return; - } - if (done[i]) { - continue; - } - const TimeArrayTimeZoneRule *tar = dynamic_cast(toRule); - const AnnualTimeZoneRule *ar; - if (tar != NULL) { - // Get the previous raw offset and DST savings before the very first start time - TimeZoneTransition tzt0; - t = start; - while (true) { - avail = getNextTransition(t, false, tzt0); - if (!avail) { - break; - } - if (*(tzt0.getTo()) == *tar) { - break; - } - t = tzt0.getTime(); - } - if (avail) { - // Check if the entire start times to be added - tar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart); - if (firstStart > start) { - // Just add the rule as is - LocalPointer lpTar(tar->clone(), status); - filteredRules->adoptElement(lpTar.orphan(), status); - if (U_FAILURE(status)) { - return; - } - } else { - // Collect transitions after the start time - int32_t startTimes; - DateTimeRule::TimeRuleType timeType; - int32_t idx; - - startTimes = tar->countStartTimes(); - timeType = tar->getTimeType(); - for (idx = 0; idx < startTimes; idx++) { - tar->getStartTimeAt(idx, t); - if (timeType == DateTimeRule::STANDARD_TIME) { - t -= tzt.getFrom()->getRawOffset(); - } - if (timeType == DateTimeRule::WALL_TIME) { - t -= tzt.getFrom()->getDSTSavings(); - } - if (t > start) { - break; - } - } - if (U_FAILURE(status)) { - return; - } - int32_t asize = startTimes - idx; - if (asize > 0) { - LocalMemory newTimes(static_cast(uprv_malloc(sizeof(UDate) * asize))); - if (newTimes.isNull()) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - for (int32_t newidx = 0; newidx < asize; newidx++) { - tar->getStartTimeAt(idx + newidx, newTimes[newidx]); - } - tar->getName(name); - LocalPointer newTar(new TimeArrayTimeZoneRule( - name, tar->getRawOffset(), tar->getDSTSavings(), &newTimes[0], asize, timeType), status); - filteredRules->adoptElement(newTar.orphan(), status); - if (U_FAILURE(status)) { - return; - } - } - } - } - } else if ((ar = dynamic_cast(toRule)) != NULL) { - ar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart); - if (firstStart == tzt.getTime()) { - // Just add the rule as is - LocalPointer arClone(ar->clone(), status); - filteredRules->adoptElement(arClone.orphan(), status); - if (U_FAILURE(status)) { - return; - } - } else { - // Calculate the transition year - int32_t year, month, dom, dow, doy, mid; - Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid); - // Re-create the rule - ar->getName(name); - LocalPointer newAr(new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(), - *(ar->getRule()), year, ar->getEndYear()), status); - filteredRules->adoptElement(newAr.orphan(), status); - if (U_FAILURE(status)) { - return; - } - } - // check if this is a final rule - if (ar->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) { - // After bot final standard and dst rules are processed, - // exit this while loop. - if (ar->getDSTSavings() == 0) { - bFinalStd = true; - } else { - bFinalDst = true; - } - } - } - done[i] = true; - } - - // Set the results - initial = res_initial.orphan(); - transitionRules = filteredRules.orphan(); - return; -} - -void -BasicTimeZone::getOffsetFromLocal(UDate /*date*/, UTimeZoneLocalOption /*nonExistingTimeOpt*/, - UTimeZoneLocalOption /*duplicatedTimeOpt*/, - int32_t& /*rawOffset*/, int32_t& /*dstOffset*/, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - status = U_UNSUPPORTED_ERROR; -} - -void BasicTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt, - int32_t& rawOffset, int32_t& dstOffset, - UErrorCode& status) const { - getOffsetFromLocal(date, (UTimeZoneLocalOption)nonExistingTimeOpt, - (UTimeZoneLocalOption)duplicatedTimeOpt, rawOffset, dstOffset, status); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2007-2013, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/basictz.h" +#include "gregoimp.h" +#include "uvector.h" +#include "cmemory.h" + +U_NAMESPACE_BEGIN + +#define MILLIS_PER_YEAR (365*24*60*60*1000.0) + +BasicTimeZone::BasicTimeZone() +: TimeZone() { +} + +BasicTimeZone::BasicTimeZone(const UnicodeString &id) +: TimeZone(id) { +} + +BasicTimeZone::BasicTimeZone(const BasicTimeZone& source) +: TimeZone(source) { +} + +BasicTimeZone::~BasicTimeZone() { +} + +UBool +BasicTimeZone::hasEquivalentTransitions(const BasicTimeZone& tz, UDate start, UDate end, + UBool ignoreDstAmount, UErrorCode& status) const { + if (U_FAILURE(status)) { + return false; + } + if (hasSameRules(tz)) { + return true; + } + // Check the offsets at the start time + int32_t raw1, raw2, dst1, dst2; + getOffset(start, false, raw1, dst1, status); + if (U_FAILURE(status)) { + return false; + } + tz.getOffset(start, false, raw2, dst2, status); + if (U_FAILURE(status)) { + return false; + } + if (ignoreDstAmount) { + if ((raw1 + dst1 != raw2 + dst2) + || (dst1 != 0 && dst2 == 0) + || (dst1 == 0 && dst2 != 0)) { + return false; + } + } else { + if (raw1 != raw2 || dst1 != dst2) { + return false; + } + } + // Check transitions in the range + UDate time = start; + TimeZoneTransition tr1, tr2; + while (true) { + UBool avail1 = getNextTransition(time, false, tr1); + UBool avail2 = tz.getNextTransition(time, false, tr2); + + if (ignoreDstAmount) { + // Skip a transition which only differ the amount of DST savings + while (true) { + if (avail1 + && tr1.getTime() <= end + && (tr1.getFrom()->getRawOffset() + tr1.getFrom()->getDSTSavings() + == tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings()) + && (tr1.getFrom()->getDSTSavings() != 0 && tr1.getTo()->getDSTSavings() != 0)) { + getNextTransition(tr1.getTime(), false, tr1); + } else { + break; + } + } + while (true) { + if (avail2 + && tr2.getTime() <= end + && (tr2.getFrom()->getRawOffset() + tr2.getFrom()->getDSTSavings() + == tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings()) + && (tr2.getFrom()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() != 0)) { + tz.getNextTransition(tr2.getTime(), false, tr2); + } else { + break; + } + } + } + + UBool inRange1 = (avail1 && tr1.getTime() <= end); + UBool inRange2 = (avail2 && tr2.getTime() <= end); + if (!inRange1 && !inRange2) { + // No more transition in the range + break; + } + if (!inRange1 || !inRange2) { + return false; + } + if (tr1.getTime() != tr2.getTime()) { + return false; + } + if (ignoreDstAmount) { + if (tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings() + != tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings() + || (tr1.getTo()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() == 0) + || (tr1.getTo()->getDSTSavings() == 0 && tr2.getTo()->getDSTSavings() != 0)) { + return false; + } + } else { + if (tr1.getTo()->getRawOffset() != tr2.getTo()->getRawOffset() || + tr1.getTo()->getDSTSavings() != tr2.getTo()->getDSTSavings()) { + return false; + } + } + time = tr1.getTime(); + } + return true; +} + +void +BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial, + AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) const { + initial = nullptr; + std = nullptr; + dst = nullptr; + if (U_FAILURE(status)) { + return; + } + int32_t initialRaw, initialDst; + UnicodeString initialName; + + AnnualTimeZoneRule *ar1 = nullptr; + AnnualTimeZoneRule *ar2 = nullptr; + UnicodeString name; + + UBool avail; + TimeZoneTransition tr; + // Get the next transition + avail = getNextTransition(date, false, tr); + if (avail) { + tr.getFrom()->getName(initialName); + initialRaw = tr.getFrom()->getRawOffset(); + initialDst = tr.getFrom()->getDSTSavings(); + + // Check if the next transition is either DST->STD or STD->DST and + // within roughly 1 year from the specified date + UDate nextTransitionTime = tr.getTime(); + if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) + || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) + && (date + MILLIS_PER_YEAR > nextTransitionTime)) { + + int32_t year, month, dom, dow, doy, mid; + UDate d; + + // Get local wall time for the next transition time + Grego::timeToFields(nextTransitionTime + initialRaw + initialDst, + year, month, dom, dow, doy, mid); + int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); + // Create DOW rule + DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); + tr.getTo()->getName(name); + + // Note: SimpleTimeZone does not support raw offset change. + // So we always use raw offset of the given time for the rule, + // even raw offset is changed. This will result that the result + // zone to return wrong offset after the transition. + // When we encounter such case, we do not inspect next next + // transition for another rule. + ar1 = new AnnualTimeZoneRule(name, initialRaw, tr.getTo()->getDSTSavings(), + dtr, year, AnnualTimeZoneRule::MAX_YEAR); + + if (tr.getTo()->getRawOffset() == initialRaw) { + // Get the next next transition + avail = getNextTransition(nextTransitionTime, false, tr); + if (avail) { + // Check if the next next transition is either DST->STD or STD->DST + // and within roughly 1 year from the next transition + if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) + || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) + && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) { + + // Get local wall time for the next transition time + Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(), + year, month, dom, dow, doy, mid); + weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); + // Generate another DOW rule + dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); + tr.getTo()->getName(name); + ar2 = new AnnualTimeZoneRule(name, tr.getTo()->getRawOffset(), tr.getTo()->getDSTSavings(), + dtr, year - 1, AnnualTimeZoneRule::MAX_YEAR); + + // Make sure this rule can be applied to the specified date + avail = ar2->getPreviousStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), true, d); + if (!avail || d > date + || initialRaw != tr.getTo()->getRawOffset() + || initialDst != tr.getTo()->getDSTSavings()) { + // We cannot use this rule as the second transition rule + delete ar2; + ar2 = nullptr; + } + } + } + } + if (ar2 == nullptr) { + // Try previous transition + avail = getPreviousTransition(date, true, tr); + if (avail) { + // Check if the previous transition is either DST->STD or STD->DST. + // The actual transition time does not matter here. + if ((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) + || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) { + + // Generate another DOW rule + Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(), + year, month, dom, dow, doy, mid); + weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); + dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); + tr.getTo()->getName(name); + + // second rule raw/dst offsets should match raw/dst offsets + // at the given time + ar2 = new AnnualTimeZoneRule(name, initialRaw, initialDst, + dtr, ar1->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR); + + // Check if this rule start after the first rule after the specified date + avail = ar2->getNextStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), false, d); + if (!avail || d <= nextTransitionTime) { + // We cannot use this rule as the second transition rule + delete ar2; + ar2 = nullptr; + } + } + } + } + if (ar2 == nullptr) { + // Cannot find a good pair of AnnualTimeZoneRule + delete ar1; + ar1 = nullptr; + } else { + // The initial rule should represent the rule before the previous transition + ar1->getName(initialName); + initialRaw = ar1->getRawOffset(); + initialDst = ar1->getDSTSavings(); + } + } + } + else { + // Try the previous one + avail = getPreviousTransition(date, true, tr); + if (avail) { + tr.getTo()->getName(initialName); + initialRaw = tr.getTo()->getRawOffset(); + initialDst = tr.getTo()->getDSTSavings(); + } else { + // No transitions in the past. Just use the current offsets + getOffset(date, false, initialRaw, initialDst, status); + if (U_FAILURE(status)) { + return; + } + } + } + // Set the initial rule + initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst); + + // Set the standard and daylight saving rules + if (ar1 != nullptr && ar2 != nullptr) { + if (ar1->getDSTSavings() != 0) { + dst = ar1; + std = ar2; + } else { + std = ar1; + dst = ar2; + } + } +} + +void +BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial, + UVector*& transitionRules, UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + + const InitialTimeZoneRule *orgini; + TimeZoneTransition tzt; + bool avail; + int32_t ruleCount; + TimeZoneRule *r = nullptr; + UnicodeString name; + int32_t i; + UDate time, t; + UDate firstStart; + UBool bFinalStd = false, bFinalDst = false; + + initial = nullptr; + transitionRules = nullptr; + + // Original transition rules + ruleCount = countTransitionRules(status); + if (U_FAILURE(status)) { + return; + } + LocalPointer orgRules( + new UVector(uprv_deleteUObject, nullptr, ruleCount, status), status); + if (U_FAILURE(status)) { + return; + } + LocalMemory orgtrs( + static_cast(uprv_malloc(sizeof(TimeZoneRule*)*ruleCount))); + if (orgtrs.isNull()) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + getTimeZoneRules(orgini, &orgtrs[0], ruleCount, status); + if (U_FAILURE(status)) { + return; + } + for (i = 0; i < ruleCount; i++) { + LocalPointer lpRule(orgtrs[i]->clone(), status); + orgRules->adoptElement(lpRule.orphan(), status); + if (U_FAILURE(status)) { + return; + } + } + + avail = getPreviousTransition(start, true, tzt); + if (!avail) { + // No need to filter out rules only applicable to time before the start + initial = orgini->clone(); + if (initial == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + transitionRules = orgRules.orphan(); + return; + } + + LocalMemory done(static_cast(uprv_malloc(sizeof(bool)*ruleCount))); + if (done.isNull()) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + LocalPointer filteredRules( + new UVector(uprv_deleteUObject, nullptr, status), status); + if (U_FAILURE(status)) { + return; + } + + // Create initial rule + tzt.getTo()->getName(name); + LocalPointer res_initial( + new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(), tzt.getTo()->getDSTSavings()), status); + if (U_FAILURE(status)) { + return; + } + + // Mark rules which does not need to be processed + for (i = 0; i < ruleCount; i++) { + r = (TimeZoneRule*)orgRules->elementAt(i); + avail = r->getNextStart(start, res_initial->getRawOffset(), res_initial->getDSTSavings(), false, time); + done[i] = !avail; + } + + time = start; + while (!bFinalStd || !bFinalDst) { + avail = getNextTransition(time, false, tzt); + if (!avail) { + break; + } + UDate updatedTime = tzt.getTime(); + if (updatedTime == time) { + // Can get here if rules for start & end of daylight time have exactly + // the same time. + // TODO: fix getNextTransition() to prevent it? + status = U_INVALID_STATE_ERROR; + return; + } + time = updatedTime; + + const TimeZoneRule *toRule = tzt.getTo(); + for (i = 0; i < ruleCount; i++) { + r = (TimeZoneRule*)orgRules->elementAt(i); + if (*r == *toRule) { + break; + } + } + if (i >= ruleCount) { + // This case should never happen + status = U_INVALID_STATE_ERROR; + return; + } + if (done[i]) { + continue; + } + const TimeArrayTimeZoneRule *tar = dynamic_cast(toRule); + const AnnualTimeZoneRule *ar; + if (tar != nullptr) { + // Get the previous raw offset and DST savings before the very first start time + TimeZoneTransition tzt0; + t = start; + while (true) { + avail = getNextTransition(t, false, tzt0); + if (!avail) { + break; + } + if (*(tzt0.getTo()) == *tar) { + break; + } + t = tzt0.getTime(); + } + if (avail) { + // Check if the entire start times to be added + tar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart); + if (firstStart > start) { + // Just add the rule as is + LocalPointer lpTar(tar->clone(), status); + filteredRules->adoptElement(lpTar.orphan(), status); + if (U_FAILURE(status)) { + return; + } + } else { + // Collect transitions after the start time + int32_t startTimes; + DateTimeRule::TimeRuleType timeType; + int32_t idx; + + startTimes = tar->countStartTimes(); + timeType = tar->getTimeType(); + for (idx = 0; idx < startTimes; idx++) { + tar->getStartTimeAt(idx, t); + if (timeType == DateTimeRule::STANDARD_TIME) { + t -= tzt.getFrom()->getRawOffset(); + } + if (timeType == DateTimeRule::WALL_TIME) { + t -= tzt.getFrom()->getDSTSavings(); + } + if (t > start) { + break; + } + } + if (U_FAILURE(status)) { + return; + } + int32_t asize = startTimes - idx; + if (asize > 0) { + LocalMemory newTimes(static_cast(uprv_malloc(sizeof(UDate) * asize))); + if (newTimes.isNull()) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + for (int32_t newidx = 0; newidx < asize; newidx++) { + tar->getStartTimeAt(idx + newidx, newTimes[newidx]); + } + tar->getName(name); + LocalPointer newTar(new TimeArrayTimeZoneRule( + name, tar->getRawOffset(), tar->getDSTSavings(), &newTimes[0], asize, timeType), status); + filteredRules->adoptElement(newTar.orphan(), status); + if (U_FAILURE(status)) { + return; + } + } + } + } + } else if ((ar = dynamic_cast(toRule)) != nullptr) { + ar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart); + if (firstStart == tzt.getTime()) { + // Just add the rule as is + LocalPointer arClone(ar->clone(), status); + filteredRules->adoptElement(arClone.orphan(), status); + if (U_FAILURE(status)) { + return; + } + } else { + // Calculate the transition year + int32_t year, month, dom, dow, doy, mid; + Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid); + // Re-create the rule + ar->getName(name); + LocalPointer newAr(new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(), + *(ar->getRule()), year, ar->getEndYear()), status); + filteredRules->adoptElement(newAr.orphan(), status); + if (U_FAILURE(status)) { + return; + } + } + // check if this is a final rule + if (ar->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) { + // After bot final standard and dst rules are processed, + // exit this while loop. + if (ar->getDSTSavings() == 0) { + bFinalStd = true; + } else { + bFinalDst = true; + } + } + } + done[i] = true; + } + + // Set the results + initial = res_initial.orphan(); + transitionRules = filteredRules.orphan(); + return; +} + +void +BasicTimeZone::getOffsetFromLocal(UDate /*date*/, UTimeZoneLocalOption /*nonExistingTimeOpt*/, + UTimeZoneLocalOption /*duplicatedTimeOpt*/, + int32_t& /*rawOffset*/, int32_t& /*dstOffset*/, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + status = U_UNSUPPORTED_ERROR; +} + +void BasicTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt, + int32_t& rawOffset, int32_t& dstOffset, + UErrorCode& status) const { + getOffsetFromLocal(date, (UTimeZoneLocalOption)nonExistingTimeOpt, + (UTimeZoneLocalOption)duplicatedTimeOpt, rawOffset, dstOffset, status); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/bocsu.cpp b/deps/icu-small/source/i18n/bocsu.cpp index 861a76a0427536..e010e2c0e7fea5 100644 --- a/deps/icu-small/source/i18n/bocsu.cpp +++ b/deps/icu-small/source/i18n/bocsu.cpp @@ -1,144 +1,144 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2001-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: bocsu.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* Author: Markus W. Scherer -* -* Modification history: -* 05/18/2001 weiv Made into separate module -*/ - - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/bytestream.h" -#include "unicode/utf16.h" -#include "bocsu.h" - -/* - * encode one difference value -0x10ffff..+0x10ffff in 1..4 bytes, - * preserving lexical order - */ -static uint8_t * -u_writeDiff(int32_t diff, uint8_t *p) { - if(diff>=SLOPE_REACH_NEG_1) { - if(diff<=SLOPE_REACH_POS_1) { - *p++=(uint8_t)(SLOPE_MIDDLE+diff); - } else if(diff<=SLOPE_REACH_POS_2) { - *p++=(uint8_t)(SLOPE_START_POS_2+(diff/SLOPE_TAIL_COUNT)); - *p++=(uint8_t)(SLOPE_MIN+diff%SLOPE_TAIL_COUNT); - } else if(diff<=SLOPE_REACH_POS_3) { - p[2]=(uint8_t)(SLOPE_MIN+diff%SLOPE_TAIL_COUNT); - diff/=SLOPE_TAIL_COUNT; - p[1]=(uint8_t)(SLOPE_MIN+diff%SLOPE_TAIL_COUNT); - *p=(uint8_t)(SLOPE_START_POS_3+(diff/SLOPE_TAIL_COUNT)); - p+=3; - } else { - p[3]=(uint8_t)(SLOPE_MIN+diff%SLOPE_TAIL_COUNT); - diff/=SLOPE_TAIL_COUNT; - p[2]=(uint8_t)(SLOPE_MIN+diff%SLOPE_TAIL_COUNT); - diff/=SLOPE_TAIL_COUNT; - p[1]=(uint8_t)(SLOPE_MIN+diff%SLOPE_TAIL_COUNT); - *p=SLOPE_MAX; - p+=4; - } - } else { - int32_t m; - - if(diff>=SLOPE_REACH_NEG_2) { - NEGDIVMOD(diff, SLOPE_TAIL_COUNT, m); - *p++=(uint8_t)(SLOPE_START_NEG_2+diff); - *p++=(uint8_t)(SLOPE_MIN+m); - } else if(diff>=SLOPE_REACH_NEG_3) { - NEGDIVMOD(diff, SLOPE_TAIL_COUNT, m); - p[2]=(uint8_t)(SLOPE_MIN+m); - NEGDIVMOD(diff, SLOPE_TAIL_COUNT, m); - p[1]=(uint8_t)(SLOPE_MIN+m); - *p=(uint8_t)(SLOPE_START_NEG_3+diff); - p+=3; - } else { - NEGDIVMOD(diff, SLOPE_TAIL_COUNT, m); - p[3]=(uint8_t)(SLOPE_MIN+m); - NEGDIVMOD(diff, SLOPE_TAIL_COUNT, m); - p[2]=(uint8_t)(SLOPE_MIN+m); - NEGDIVMOD(diff, SLOPE_TAIL_COUNT, m); - p[1]=(uint8_t)(SLOPE_MIN+m); - *p=SLOPE_MIN; - p+=4; - } - } - return p; -} - -/* - * Encode the code points of a string as - * a sequence of byte-encoded differences (slope detection), - * preserving lexical order. - * - * Optimize the difference-taking for runs of Unicode text within - * small scripts: - * - * Most small scripts are allocated within aligned 128-blocks of Unicode - * code points. Lexical order is preserved if "prev" is always moved - * into the middle of such a block. - * - * Additionally, "prev" is moved from anywhere in the Unihan - * area into the middle of that area. - * Note that the identical-level run in a sort key is generated from - * NFD text - there are never Hangul characters included. - */ -U_CFUNC UChar32 -u_writeIdenticalLevelRun(UChar32 prev, const UChar *s, int32_t length, icu::ByteSink &sink) { - char scratch[64]; - int32_t capacity; - - int32_t i=0; - while(i=SLOPE_MAX_BYTES in case u_writeDiff() writes that much, - // but we do not want to force the sink.GetAppendBuffer() to allocate - // for a large min_capacity because we might actually only write one byte. - if(capacity<16) { - buffer=scratch; - capacity=(int32_t)sizeof(scratch); - } - p=reinterpret_cast(buffer); - uint8_t *lastSafe=p+capacity-SLOPE_MAX_BYTES; - while(i=0xa000) { - prev=(prev&~0x7f)-SLOPE_REACH_NEG_1; - } else { - /* - * Unihan U+4e00..U+9fa5: - * double-bytes down from the upper end - */ - prev=0x9fff-SLOPE_REACH_POS_2; - } - - UChar32 c; - U16_NEXT(s, i, length, c); - if(c==0xfffe) { - *p++=2; // merge separator - prev=0; - } else { - p=u_writeDiff(c-prev, p); - prev=c; - } - } - sink.Append(buffer, (int32_t)(p-reinterpret_cast(buffer))); - } - return prev; -} - -#endif /* #if !UCONFIG_NO_COLLATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2001-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: bocsu.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* Author: Markus W. Scherer +* +* Modification history: +* 05/18/2001 weiv Made into separate module +*/ + + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/bytestream.h" +#include "unicode/utf16.h" +#include "bocsu.h" + +/* + * encode one difference value -0x10ffff..+0x10ffff in 1..4 bytes, + * preserving lexical order + */ +static uint8_t * +u_writeDiff(int32_t diff, uint8_t *p) { + if(diff>=SLOPE_REACH_NEG_1) { + if(diff<=SLOPE_REACH_POS_1) { + *p++=(uint8_t)(SLOPE_MIDDLE+diff); + } else if(diff<=SLOPE_REACH_POS_2) { + *p++=(uint8_t)(SLOPE_START_POS_2+(diff/SLOPE_TAIL_COUNT)); + *p++=(uint8_t)(SLOPE_MIN+diff%SLOPE_TAIL_COUNT); + } else if(diff<=SLOPE_REACH_POS_3) { + p[2]=(uint8_t)(SLOPE_MIN+diff%SLOPE_TAIL_COUNT); + diff/=SLOPE_TAIL_COUNT; + p[1]=(uint8_t)(SLOPE_MIN+diff%SLOPE_TAIL_COUNT); + *p=(uint8_t)(SLOPE_START_POS_3+(diff/SLOPE_TAIL_COUNT)); + p+=3; + } else { + p[3]=(uint8_t)(SLOPE_MIN+diff%SLOPE_TAIL_COUNT); + diff/=SLOPE_TAIL_COUNT; + p[2]=(uint8_t)(SLOPE_MIN+diff%SLOPE_TAIL_COUNT); + diff/=SLOPE_TAIL_COUNT; + p[1]=(uint8_t)(SLOPE_MIN+diff%SLOPE_TAIL_COUNT); + *p=SLOPE_MAX; + p+=4; + } + } else { + int32_t m; + + if(diff>=SLOPE_REACH_NEG_2) { + NEGDIVMOD(diff, SLOPE_TAIL_COUNT, m); + *p++=(uint8_t)(SLOPE_START_NEG_2+diff); + *p++=(uint8_t)(SLOPE_MIN+m); + } else if(diff>=SLOPE_REACH_NEG_3) { + NEGDIVMOD(diff, SLOPE_TAIL_COUNT, m); + p[2]=(uint8_t)(SLOPE_MIN+m); + NEGDIVMOD(diff, SLOPE_TAIL_COUNT, m); + p[1]=(uint8_t)(SLOPE_MIN+m); + *p=(uint8_t)(SLOPE_START_NEG_3+diff); + p+=3; + } else { + NEGDIVMOD(diff, SLOPE_TAIL_COUNT, m); + p[3]=(uint8_t)(SLOPE_MIN+m); + NEGDIVMOD(diff, SLOPE_TAIL_COUNT, m); + p[2]=(uint8_t)(SLOPE_MIN+m); + NEGDIVMOD(diff, SLOPE_TAIL_COUNT, m); + p[1]=(uint8_t)(SLOPE_MIN+m); + *p=SLOPE_MIN; + p+=4; + } + } + return p; +} + +/* + * Encode the code points of a string as + * a sequence of byte-encoded differences (slope detection), + * preserving lexical order. + * + * Optimize the difference-taking for runs of Unicode text within + * small scripts: + * + * Most small scripts are allocated within aligned 128-blocks of Unicode + * code points. Lexical order is preserved if "prev" is always moved + * into the middle of such a block. + * + * Additionally, "prev" is moved from anywhere in the Unihan + * area into the middle of that area. + * Note that the identical-level run in a sort key is generated from + * NFD text - there are never Hangul characters included. + */ +U_CFUNC UChar32 +u_writeIdenticalLevelRun(UChar32 prev, const char16_t *s, int32_t length, icu::ByteSink &sink) { + char scratch[64]; + int32_t capacity; + + int32_t i=0; + while(i=SLOPE_MAX_BYTES in case u_writeDiff() writes that much, + // but we do not want to force the sink.GetAppendBuffer() to allocate + // for a large min_capacity because we might actually only write one byte. + if(capacity<16) { + buffer=scratch; + capacity=(int32_t)sizeof(scratch); + } + p=reinterpret_cast(buffer); + uint8_t *lastSafe=p+capacity-SLOPE_MAX_BYTES; + while(i=0xa000) { + prev=(prev&~0x7f)-SLOPE_REACH_NEG_1; + } else { + /* + * Unihan U+4e00..U+9fa5: + * double-bytes down from the upper end + */ + prev=0x9fff-SLOPE_REACH_POS_2; + } + + UChar32 c; + U16_NEXT(s, i, length, c); + if(c==0xfffe) { + *p++=2; // merge separator + prev=0; + } else { + p=u_writeDiff(c-prev, p); + prev=c; + } + } + sink.Append(buffer, (int32_t)(p-reinterpret_cast(buffer))); + } + return prev; +} + +#endif /* #if !UCONFIG_NO_COLLATION */ diff --git a/deps/icu-small/source/i18n/bocsu.h b/deps/icu-small/source/i18n/bocsu.h index 631e29aa768b9d..8f3f9311546cbb 100644 --- a/deps/icu-small/source/i18n/bocsu.h +++ b/deps/icu-small/source/i18n/bocsu.h @@ -1,161 +1,161 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2001-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* file name: bocsu.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* Author: Markus W. Scherer -* -* Modification history: -* 05/18/2001 weiv Made into separate module -*/ - -#ifndef BOCSU_H -#define BOCSU_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -U_NAMESPACE_BEGIN - -class ByteSink; - -U_NAMESPACE_END - -/* - * "BOCSU" - * Binary Ordered Compression Scheme for Unicode - * - * Specific application: - * - * Encode a Unicode string for the identical level of a sort key. - * Restrictions: - * - byte stream (unsigned 8-bit bytes) - * - lexical order of the identical-level run must be - * the same as code point order for the string - * - avoid byte values 0, 1, 2 - * - * Method: Slope Detection - * Remember the previous code point (initial 0). - * For each cp in the string, encode the difference to the previous one. - * - * With a compact encoding of differences, this yields good results for - * small scripts and UTF-like results otherwise. - * - * Encoding of differences: - * - Similar to a UTF, encoding the length of the byte sequence in the lead bytes. - * - Does not need to be friendly for decoding or random access - * (trail byte values may overlap with lead/single byte values). - * - The signedness must be encoded as the most significant part. - * - * We encode differences with few bytes if their absolute values are small. - * For correct ordering, we must treat the entire value range -10ffff..+10ffff - * in ascending order, which forbids encoding the sign and the absolute value separately. - * Instead, we split the lead byte range in the middle and encode non-negative values - * going up and negative values going down. - * - * For very small absolute values, the difference is added to a middle byte value - * for single-byte encoded differences. - * For somewhat larger absolute values, the difference is divided by the number - * of byte values available, the modulo is used for one trail byte, and the remainder - * is added to a lead byte avoiding the single-byte range. - * For large absolute values, the difference is similarly encoded in three bytes. - * - * This encoding does not use byte values 0, 1, 2, but uses all other byte values - * for lead/single bytes so that the middle range of single bytes is as large - * as possible. - * Note that the lead byte ranges overlap some, but that the sequences as a whole - * are well ordered. I.e., even if the lead byte is the same for sequences of different - * lengths, the trail bytes establish correct order. - * It would be possible to encode slightly larger ranges for each length (>1) by - * subtracting the lower bound of the range. However, that would also slow down the - * calculation. - * - * For the actual string encoding, an optimization moves the previous code point value - * to the middle of its Unicode script block to minimize the differences in - * same-script text runs. - */ - -/* Do not use byte values 0, 1, 2 because they are separators in sort keys. */ -#define SLOPE_MIN 3 -#define SLOPE_MAX 0xff -#define SLOPE_MIDDLE 0x81 - -#define SLOPE_TAIL_COUNT (SLOPE_MAX-SLOPE_MIN+1) - -#define SLOPE_MAX_BYTES 4 - -/* - * Number of lead bytes: - * 1 middle byte for 0 - * 2*80=160 single bytes for !=0 - * 2*42=84 for double-byte values - * 2*3=6 for 3-byte values - * 2*1=2 for 4-byte values - * - * The sum must be <=SLOPE_TAIL_COUNT. - * - * Why these numbers? - * - There should be >=128 single-byte values to cover 128-blocks - * with small scripts. - * - There should be >=20902 single/double-byte values to cover Unihan. - * - It helps CJK Extension B some if there are 3-byte values that cover - * the distance between them and Unihan. - * This also helps to jump among distant places in the BMP. - * - Four-byte values are necessary to cover the rest of Unicode. - * - * Symmetrical lead byte counts are for convenience. - * With an equal distribution of even and odd differences there is also - * no advantage to asymmetrical lead byte counts. - */ -#define SLOPE_SINGLE 80 -#define SLOPE_LEAD_2 42 -#define SLOPE_LEAD_3 3 -#define SLOPE_LEAD_4 1 - -/* The difference value range for single-byters. */ -#define SLOPE_REACH_POS_1 SLOPE_SINGLE -#define SLOPE_REACH_NEG_1 (-SLOPE_SINGLE) - -/* The difference value range for double-byters. */ -#define SLOPE_REACH_POS_2 (SLOPE_LEAD_2*SLOPE_TAIL_COUNT+(SLOPE_LEAD_2-1)) -#define SLOPE_REACH_NEG_2 (-SLOPE_REACH_POS_2-1) - -/* The difference value range for 3-byters. */ -#define SLOPE_REACH_POS_3 (SLOPE_LEAD_3*SLOPE_TAIL_COUNT*SLOPE_TAIL_COUNT+(SLOPE_LEAD_3-1)*SLOPE_TAIL_COUNT+(SLOPE_TAIL_COUNT-1)) -#define SLOPE_REACH_NEG_3 (-SLOPE_REACH_POS_3-1) - -/* The lead byte start values. */ -#define SLOPE_START_POS_2 (SLOPE_MIDDLE+SLOPE_SINGLE+1) -#define SLOPE_START_POS_3 (SLOPE_START_POS_2+SLOPE_LEAD_2) - -#define SLOPE_START_NEG_2 (SLOPE_MIDDLE+SLOPE_REACH_NEG_1) -#define SLOPE_START_NEG_3 (SLOPE_START_NEG_2-SLOPE_LEAD_2) - -/* - * Integer division and modulo with negative numerators - * yields negative modulo results and quotients that are one more than - * what we need here. - */ -#define NEGDIVMOD(n, d, m) UPRV_BLOCK_MACRO_BEGIN { \ - (m)=(n)%(d); \ - (n)/=(d); \ - if((m)<0) { \ - --(n); \ - (m)+=(d); \ - } \ -} UPRV_BLOCK_MACRO_END - -U_CFUNC UChar32 -u_writeIdenticalLevelRun(UChar32 prev, const UChar *s, int32_t length, icu::ByteSink &sink); - -#endif /* #if !UCONFIG_NO_COLLATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2001-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* file name: bocsu.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* Author: Markus W. Scherer +* +* Modification history: +* 05/18/2001 weiv Made into separate module +*/ + +#ifndef BOCSU_H +#define BOCSU_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +U_NAMESPACE_BEGIN + +class ByteSink; + +U_NAMESPACE_END + +/* + * "BOCSU" + * Binary Ordered Compression Scheme for Unicode + * + * Specific application: + * + * Encode a Unicode string for the identical level of a sort key. + * Restrictions: + * - byte stream (unsigned 8-bit bytes) + * - lexical order of the identical-level run must be + * the same as code point order for the string + * - avoid byte values 0, 1, 2 + * + * Method: Slope Detection + * Remember the previous code point (initial 0). + * For each cp in the string, encode the difference to the previous one. + * + * With a compact encoding of differences, this yields good results for + * small scripts and UTF-like results otherwise. + * + * Encoding of differences: + * - Similar to a UTF, encoding the length of the byte sequence in the lead bytes. + * - Does not need to be friendly for decoding or random access + * (trail byte values may overlap with lead/single byte values). + * - The signedness must be encoded as the most significant part. + * + * We encode differences with few bytes if their absolute values are small. + * For correct ordering, we must treat the entire value range -10ffff..+10ffff + * in ascending order, which forbids encoding the sign and the absolute value separately. + * Instead, we split the lead byte range in the middle and encode non-negative values + * going up and negative values going down. + * + * For very small absolute values, the difference is added to a middle byte value + * for single-byte encoded differences. + * For somewhat larger absolute values, the difference is divided by the number + * of byte values available, the modulo is used for one trail byte, and the remainder + * is added to a lead byte avoiding the single-byte range. + * For large absolute values, the difference is similarly encoded in three bytes. + * + * This encoding does not use byte values 0, 1, 2, but uses all other byte values + * for lead/single bytes so that the middle range of single bytes is as large + * as possible. + * Note that the lead byte ranges overlap some, but that the sequences as a whole + * are well ordered. I.e., even if the lead byte is the same for sequences of different + * lengths, the trail bytes establish correct order. + * It would be possible to encode slightly larger ranges for each length (>1) by + * subtracting the lower bound of the range. However, that would also slow down the + * calculation. + * + * For the actual string encoding, an optimization moves the previous code point value + * to the middle of its Unicode script block to minimize the differences in + * same-script text runs. + */ + +/* Do not use byte values 0, 1, 2 because they are separators in sort keys. */ +#define SLOPE_MIN 3 +#define SLOPE_MAX 0xff +#define SLOPE_MIDDLE 0x81 + +#define SLOPE_TAIL_COUNT (SLOPE_MAX-SLOPE_MIN+1) + +#define SLOPE_MAX_BYTES 4 + +/* + * Number of lead bytes: + * 1 middle byte for 0 + * 2*80=160 single bytes for !=0 + * 2*42=84 for double-byte values + * 2*3=6 for 3-byte values + * 2*1=2 for 4-byte values + * + * The sum must be <=SLOPE_TAIL_COUNT. + * + * Why these numbers? + * - There should be >=128 single-byte values to cover 128-blocks + * with small scripts. + * - There should be >=20902 single/double-byte values to cover Unihan. + * - It helps CJK Extension B some if there are 3-byte values that cover + * the distance between them and Unihan. + * This also helps to jump among distant places in the BMP. + * - Four-byte values are necessary to cover the rest of Unicode. + * + * Symmetrical lead byte counts are for convenience. + * With an equal distribution of even and odd differences there is also + * no advantage to asymmetrical lead byte counts. + */ +#define SLOPE_SINGLE 80 +#define SLOPE_LEAD_2 42 +#define SLOPE_LEAD_3 3 +#define SLOPE_LEAD_4 1 + +/* The difference value range for single-byters. */ +#define SLOPE_REACH_POS_1 SLOPE_SINGLE +#define SLOPE_REACH_NEG_1 (-SLOPE_SINGLE) + +/* The difference value range for double-byters. */ +#define SLOPE_REACH_POS_2 (SLOPE_LEAD_2*SLOPE_TAIL_COUNT+(SLOPE_LEAD_2-1)) +#define SLOPE_REACH_NEG_2 (-SLOPE_REACH_POS_2-1) + +/* The difference value range for 3-byters. */ +#define SLOPE_REACH_POS_3 (SLOPE_LEAD_3*SLOPE_TAIL_COUNT*SLOPE_TAIL_COUNT+(SLOPE_LEAD_3-1)*SLOPE_TAIL_COUNT+(SLOPE_TAIL_COUNT-1)) +#define SLOPE_REACH_NEG_3 (-SLOPE_REACH_POS_3-1) + +/* The lead byte start values. */ +#define SLOPE_START_POS_2 (SLOPE_MIDDLE+SLOPE_SINGLE+1) +#define SLOPE_START_POS_3 (SLOPE_START_POS_2+SLOPE_LEAD_2) + +#define SLOPE_START_NEG_2 (SLOPE_MIDDLE+SLOPE_REACH_NEG_1) +#define SLOPE_START_NEG_3 (SLOPE_START_NEG_2-SLOPE_LEAD_2) + +/* + * Integer division and modulo with negative numerators + * yields negative modulo results and quotients that are one more than + * what we need here. + */ +#define NEGDIVMOD(n, d, m) UPRV_BLOCK_MACRO_BEGIN { \ + (m)=(n)%(d); \ + (n)/=(d); \ + if((m)<0) { \ + --(n); \ + (m)+=(d); \ + } \ +} UPRV_BLOCK_MACRO_END + +U_CFUNC UChar32 +u_writeIdenticalLevelRun(UChar32 prev, const UChar *s, int32_t length, icu::ByteSink &sink); + +#endif /* #if !UCONFIG_NO_COLLATION */ + +#endif diff --git a/deps/icu-small/source/i18n/brktrans.cpp b/deps/icu-small/source/i18n/brktrans.cpp index f0ec8407db2c05..46d116ff7d4c89 100644 --- a/deps/icu-small/source/i18n/brktrans.cpp +++ b/deps/icu-small/source/i18n/brktrans.cpp @@ -1,195 +1,195 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2008-2015, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 05/11/2008 Andy Heninger Port from Java -********************************************************************** -*/ - -#include - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION && !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/brkiter.h" -#include "unicode/localpointer.h" -#include "unicode/uchar.h" -#include "unicode/unifilt.h" -#include "unicode/uniset.h" - -#include "brktrans.h" -#include "cmemory.h" -#include "mutex.h" -#include "uprops.h" -#include "uinvchar.h" -#include "util.h" -#include "uvectr32.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(BreakTransliterator) - -static const UChar SPACE = 32; // ' ' - - -/** - * Constructs a transliterator with the default delimiters '{' and - * '}'. - */ -BreakTransliterator::BreakTransliterator(UnicodeFilter* adoptedFilter) : - Transliterator(UNICODE_STRING("Any-BreakInternal", 17), adoptedFilter), - cachedBI(NULL), cachedBoundaries(NULL), fInsertion(SPACE) { - } - - -/** - * Destructor. - */ -BreakTransliterator::~BreakTransliterator() { -} - -/** - * Copy constructor. - */ -BreakTransliterator::BreakTransliterator(const BreakTransliterator& o) : - Transliterator(o), cachedBI(NULL), cachedBoundaries(NULL), fInsertion(o.fInsertion) { -} - - -/** - * Transliterator API. - */ -BreakTransliterator* BreakTransliterator::clone() const { - return new BreakTransliterator(*this); -} - -/** - * Implements {@link Transliterator#handleTransliterate}. - */ -void BreakTransliterator::handleTransliterate(Replaceable& text, UTransPosition& offsets, - UBool isIncremental ) const { - - UErrorCode status = U_ZERO_ERROR; - LocalPointer bi; - LocalPointer boundaries; - - { - Mutex m; - BreakTransliterator *nonConstThis = const_cast(this); - boundaries = std::move(nonConstThis->cachedBoundaries); - bi = std::move(nonConstThis->cachedBI); - } - if (bi.isNull()) { - bi.adoptInstead(BreakIterator::createWordInstance(Locale::getEnglish(), status)); - } - if (boundaries.isNull()) { - boundaries.adoptInstead(new UVector32(status)); - } - - if (bi.isNull() || boundaries.isNull() || U_FAILURE(status)) { - return; - } - - boundaries->removeAllElements(); - UnicodeString sText = replaceableAsString(text); - bi->setText(sText); - bi->preceding(offsets.start); - - // To make things much easier, we will stack the boundaries, and then insert at the end. - // generally, we won't need too many, since we will be filtered. - - int32_t boundary; - for(boundary = bi->next(); boundary != UBRK_DONE && boundary < offsets.limit; boundary = bi->next()) { - if (boundary == 0) continue; - // HACK: Check to see that preceding item was a letter - - UChar32 cp = sText.char32At(boundary-1); - int type = u_charType(cp); - //System.out.println(Integer.toString(cp,16) + " (before): " + type); - if ((U_MASK(type) & (U_GC_L_MASK | U_GC_M_MASK)) == 0) continue; - - cp = sText.char32At(boundary); - type = u_charType(cp); - //System.out.println(Integer.toString(cp,16) + " (after): " + type); - if ((U_MASK(type) & (U_GC_L_MASK | U_GC_M_MASK)) == 0) continue; - - boundaries->addElement(boundary, status); - // printf("Boundary at %d\n", boundary); - } - - int delta = 0; - int lastBoundary = 0; - - if (boundaries->size() != 0) { // if we found something, adjust - delta = boundaries->size() * fInsertion.length(); - lastBoundary = boundaries->lastElementi(); - - // we do this from the end backwards, so that we don't have to keep updating. - - while (boundaries->size() > 0) { - boundary = boundaries->popi(); - text.handleReplaceBetween(boundary, boundary, fInsertion); - } - } - - // Now fix up the return values - offsets.contextLimit += delta; - offsets.limit += delta; - offsets.start = isIncremental ? lastBoundary + delta : offsets.limit; - - // Return break iterator & boundaries vector to the cache. - { - Mutex m; - BreakTransliterator *nonConstThis = const_cast(this); - if (nonConstThis->cachedBI.isNull()) { - nonConstThis->cachedBI = std::move(bi); - } - if (nonConstThis->cachedBoundaries.isNull()) { - nonConstThis->cachedBoundaries = std::move(boundaries); - } - } - - // TODO: do something with U_FAILURE(status); - // (need to look at transliterators overall, not just here.) -} - -// -// getInsertion() -// -const UnicodeString &BreakTransliterator::getInsertion() const { - return fInsertion; -} - -// -// setInsertion() -// -void BreakTransliterator::setInsertion(const UnicodeString &insertion) { - this->fInsertion = insertion; -} - -// -// replaceableAsString Hack to let break iterators work -// on the replaceable text from transliterators. -// In practice, the only real Replaceable type that we -// will be seeing is UnicodeString, so this function -// will normally be efficient. -// -UnicodeString BreakTransliterator::replaceableAsString(Replaceable &r) { - UnicodeString s; - UnicodeString *rs = dynamic_cast(&r); - if (rs != NULL) { - s = *rs; - } else { - r.extractBetween(0, r.length(), s); - } - return s; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2008-2015, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 05/11/2008 Andy Heninger Port from Java +********************************************************************** +*/ + +#include + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION && !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/brkiter.h" +#include "unicode/localpointer.h" +#include "unicode/uchar.h" +#include "unicode/unifilt.h" +#include "unicode/uniset.h" + +#include "brktrans.h" +#include "cmemory.h" +#include "mutex.h" +#include "uprops.h" +#include "uinvchar.h" +#include "util.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(BreakTransliterator) + +static const char16_t SPACE = 32; // ' ' + + +/** + * Constructs a transliterator with the default delimiters '{' and + * '}'. + */ +BreakTransliterator::BreakTransliterator(UnicodeFilter* adoptedFilter) : + Transliterator(UNICODE_STRING("Any-BreakInternal", 17), adoptedFilter), + cachedBI(nullptr), cachedBoundaries(nullptr), fInsertion(SPACE) { + } + + +/** + * Destructor. + */ +BreakTransliterator::~BreakTransliterator() { +} + +/** + * Copy constructor. + */ +BreakTransliterator::BreakTransliterator(const BreakTransliterator& o) : + Transliterator(o), cachedBI(nullptr), cachedBoundaries(nullptr), fInsertion(o.fInsertion) { +} + + +/** + * Transliterator API. + */ +BreakTransliterator* BreakTransliterator::clone() const { + return new BreakTransliterator(*this); +} + +/** + * Implements {@link Transliterator#handleTransliterate}. + */ +void BreakTransliterator::handleTransliterate(Replaceable& text, UTransPosition& offsets, + UBool isIncremental ) const { + + UErrorCode status = U_ZERO_ERROR; + LocalPointer bi; + LocalPointer boundaries; + + { + Mutex m; + BreakTransliterator *nonConstThis = const_cast(this); + boundaries = std::move(nonConstThis->cachedBoundaries); + bi = std::move(nonConstThis->cachedBI); + } + if (bi.isNull()) { + bi.adoptInstead(BreakIterator::createWordInstance(Locale::getEnglish(), status)); + } + if (boundaries.isNull()) { + boundaries.adoptInstead(new UVector32(status)); + } + + if (bi.isNull() || boundaries.isNull() || U_FAILURE(status)) { + return; + } + + boundaries->removeAllElements(); + UnicodeString sText = replaceableAsString(text); + bi->setText(sText); + bi->preceding(offsets.start); + + // To make things much easier, we will stack the boundaries, and then insert at the end. + // generally, we won't need too many, since we will be filtered. + + int32_t boundary; + for(boundary = bi->next(); boundary != UBRK_DONE && boundary < offsets.limit; boundary = bi->next()) { + if (boundary == 0) continue; + // HACK: Check to see that preceding item was a letter + + UChar32 cp = sText.char32At(boundary-1); + int type = u_charType(cp); + //System.out.println(Integer.toString(cp,16) + " (before): " + type); + if ((U_MASK(type) & (U_GC_L_MASK | U_GC_M_MASK)) == 0) continue; + + cp = sText.char32At(boundary); + type = u_charType(cp); + //System.out.println(Integer.toString(cp,16) + " (after): " + type); + if ((U_MASK(type) & (U_GC_L_MASK | U_GC_M_MASK)) == 0) continue; + + boundaries->addElement(boundary, status); + // printf("Boundary at %d\n", boundary); + } + + int delta = 0; + int lastBoundary = 0; + + if (boundaries->size() != 0) { // if we found something, adjust + delta = boundaries->size() * fInsertion.length(); + lastBoundary = boundaries->lastElementi(); + + // we do this from the end backwards, so that we don't have to keep updating. + + while (boundaries->size() > 0) { + boundary = boundaries->popi(); + text.handleReplaceBetween(boundary, boundary, fInsertion); + } + } + + // Now fix up the return values + offsets.contextLimit += delta; + offsets.limit += delta; + offsets.start = isIncremental ? lastBoundary + delta : offsets.limit; + + // Return break iterator & boundaries vector to the cache. + { + Mutex m; + BreakTransliterator *nonConstThis = const_cast(this); + if (nonConstThis->cachedBI.isNull()) { + nonConstThis->cachedBI = std::move(bi); + } + if (nonConstThis->cachedBoundaries.isNull()) { + nonConstThis->cachedBoundaries = std::move(boundaries); + } + } + + // TODO: do something with U_FAILURE(status); + // (need to look at transliterators overall, not just here.) +} + +// +// getInsertion() +// +const UnicodeString &BreakTransliterator::getInsertion() const { + return fInsertion; +} + +// +// setInsertion() +// +void BreakTransliterator::setInsertion(const UnicodeString &insertion) { + this->fInsertion = insertion; +} + +// +// replaceableAsString Hack to let break iterators work +// on the replaceable text from transliterators. +// In practice, the only real Replaceable type that we +// will be seeing is UnicodeString, so this function +// will normally be efficient. +// +UnicodeString BreakTransliterator::replaceableAsString(Replaceable &r) { + UnicodeString s; + UnicodeString *rs = dynamic_cast(&r); + if (rs != nullptr) { + s = *rs; + } else { + r.extractBetween(0, r.length(), s); + } + return s; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ diff --git a/deps/icu-small/source/i18n/brktrans.h b/deps/icu-small/source/i18n/brktrans.h index 5dcc8c50c02abe..f7b8e9dd458c93 100644 --- a/deps/icu-small/source/i18n/brktrans.h +++ b/deps/icu-small/source/i18n/brktrans.h @@ -1,104 +1,104 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2008-2015, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 05/11/2008 Andy Heninger Ported from Java -********************************************************************** -*/ -#ifndef BRKTRANS_H -#define BRKTRANS_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION && !UCONFIG_NO_BREAK_ITERATION - -#include "unicode/translit.h" - -#include "unicode/localpointer.h" - - -U_NAMESPACE_BEGIN - -class UVector32; - -/** - * A transliterator that pInserts the specified characters at word breaks. - * To restrict it to particular characters, use a filter. - * TODO: this is an internal class, and only temporary. - * Remove it once we have \b notation in Transliterator. - */ -class BreakTransliterator : public Transliterator { -public: - - /** - * Constructs a transliterator. - * @param adoptedFilter the filter for this transliterator. - */ - BreakTransliterator(UnicodeFilter* adoptedFilter = 0); - - /** - * Destructor. - */ - virtual ~BreakTransliterator(); - - /** - * Copy constructor. - */ - BreakTransliterator(const BreakTransliterator&); - - /** - * Transliterator API. - * @return A copy of the object. - */ - virtual BreakTransliterator* clone() const override; - - virtual const UnicodeString &getInsertion() const; - - virtual void setInsertion(const UnicodeString &insertion); - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - virtual UClassID getDynamicClassID() const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); - - protected: - - /** - * Implements {@link Transliterator#handleTransliterate}. - * @param text the buffer holding transliterated and - * untransliterated text - * @param offset the start and limit of the text, the position - * of the cursor, and the start and limit of transliteration. - * @param incremental if true, assume more text may be coming after - * pos.contextLimit. Otherwise, assume the text is complete. - */ - virtual void handleTransliterate(Replaceable& text, UTransPosition& offset, - UBool isIncremental) const override; - - private: - LocalPointer cachedBI; - LocalPointer cachedBoundaries; - UnicodeString fInsertion; - - static UnicodeString replaceableAsString(Replaceable &r); - - /** - * Assignment operator. - */ - BreakTransliterator& operator=(const BreakTransliterator&); -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2008-2015, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 05/11/2008 Andy Heninger Ported from Java +********************************************************************** +*/ +#ifndef BRKTRANS_H +#define BRKTRANS_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION && !UCONFIG_NO_BREAK_ITERATION + +#include "unicode/translit.h" + +#include "unicode/localpointer.h" + + +U_NAMESPACE_BEGIN + +class UVector32; + +/** + * A transliterator that pInserts the specified characters at word breaks. + * To restrict it to particular characters, use a filter. + * TODO: this is an internal class, and only temporary. + * Remove it once we have \b notation in Transliterator. + */ +class BreakTransliterator : public Transliterator { +public: + + /** + * Constructs a transliterator. + * @param adoptedFilter the filter for this transliterator. + */ + BreakTransliterator(UnicodeFilter* adoptedFilter = 0); + + /** + * Destructor. + */ + virtual ~BreakTransliterator(); + + /** + * Copy constructor. + */ + BreakTransliterator(const BreakTransliterator&); + + /** + * Transliterator API. + * @return A copy of the object. + */ + virtual BreakTransliterator* clone() const override; + + virtual const UnicodeString &getInsertion() const; + + virtual void setInsertion(const UnicodeString &insertion); + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + virtual UClassID getDynamicClassID() const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + + protected: + + /** + * Implements {@link Transliterator#handleTransliterate}. + * @param text the buffer holding transliterated and + * untransliterated text + * @param offset the start and limit of the text, the position + * of the cursor, and the start and limit of transliteration. + * @param incremental if true, assume more text may be coming after + * pos.contextLimit. Otherwise, assume the text is complete. + */ + virtual void handleTransliterate(Replaceable& text, UTransPosition& offset, + UBool isIncremental) const override; + + private: + LocalPointer cachedBI; + LocalPointer cachedBoundaries; + UnicodeString fInsertion; + + static UnicodeString replaceableAsString(Replaceable &r); + + /** + * Assignment operator. + */ + BreakTransliterator& operator=(const BreakTransliterator&); +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/buddhcal.cpp b/deps/icu-small/source/i18n/buddhcal.cpp index de304129cb42da..c2b07f210d1d83 100644 --- a/deps/icu-small/source/i18n/buddhcal.cpp +++ b/deps/icu-small/source/i18n/buddhcal.cpp @@ -1,181 +1,174 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2003-2013, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -* -* File BUDDHCAL.CPP -* -* Modification History: -* 05/13/2003 srl copied from gregocal.cpp -* -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "buddhcal.h" -#include "unicode/gregocal.h" -#include "umutex.h" -#include - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(BuddhistCalendar) - -//static const int32_t kMaxEra = 0; // only 1 era - -static const int32_t kBuddhistEraStart = -543; // 544 BC (Gregorian) - -static const int32_t kGregorianEpoch = 1970; // used as the default value of EXTENDED_YEAR - -BuddhistCalendar::BuddhistCalendar(const Locale& aLocale, UErrorCode& success) -: GregorianCalendar(aLocale, success) -{ - setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. -} - -BuddhistCalendar::~BuddhistCalendar() -{ -} - -BuddhistCalendar::BuddhistCalendar(const BuddhistCalendar& source) -: GregorianCalendar(source) -{ -} - -BuddhistCalendar& BuddhistCalendar::operator= ( const BuddhistCalendar& right) -{ - GregorianCalendar::operator=(right); - return *this; -} - -BuddhistCalendar* BuddhistCalendar::clone() const -{ - return new BuddhistCalendar(*this); -} - -const char *BuddhistCalendar::getType() const -{ - return "buddhist"; -} - -int32_t BuddhistCalendar::handleGetExtendedYear() -{ - // EXTENDED_YEAR in BuddhistCalendar is a Gregorian year. - // The default value of EXTENDED_YEAR is 1970 (Buddhist 2513) - int32_t year; - if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { - year = internalGet(UCAL_EXTENDED_YEAR, kGregorianEpoch); - } else { - // extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc - year = internalGet(UCAL_YEAR, kGregorianEpoch - kBuddhistEraStart) - + kBuddhistEraStart; - } - return year; -} - -int32_t BuddhistCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, - - UBool useMonth) const -{ - return GregorianCalendar::handleComputeMonthStart(eyear, month, useMonth); -} - -void BuddhistCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) -{ - GregorianCalendar::handleComputeFields(julianDay, status); - int32_t y = internalGet(UCAL_EXTENDED_YEAR) - kBuddhistEraStart; - internalSet(UCAL_ERA, 0); - internalSet(UCAL_YEAR, y); -} - -int32_t BuddhistCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const -{ - if(field == UCAL_ERA) { - return BE; - } else { - return GregorianCalendar::handleGetLimit(field,limitType); - } -} - -#if 0 -void BuddhistCalendar::timeToFields(UDate theTime, UBool quick, UErrorCode& status) -{ - //Calendar::timeToFields(theTime, quick, status); - - int32_t era = internalGet(UCAL_ERA); - int32_t year = internalGet(UCAL_YEAR); - - if(era == GregorianCalendar::BC) { - year = 1-year; - era = BuddhistCalendar::BE; - } else if(era == GregorianCalendar::AD) { - era = BuddhistCalendar::BE; - } else { - status = U_INTERNAL_PROGRAM_ERROR; - } - - year = year - kBuddhistEraStart; - - internalSet(UCAL_ERA, era); - internalSet(UCAL_YEAR, year); -} -#endif - -/** - * The system maintains a static default century start date. This is initialized - * the first time it is used. Once the system default century date and year - * are set, they do not change. - */ -static UDate gSystemDefaultCenturyStart = DBL_MIN; -static int32_t gSystemDefaultCenturyStartYear = -1; -static icu::UInitOnce gBCInitOnce {}; - - -UBool BuddhistCalendar::haveDefaultCentury() const -{ - return true; -} - -static void U_CALLCONV -initializeSystemDefaultCentury() -{ - // initialize systemDefaultCentury and systemDefaultCenturyYear based - // on the current time. They'll be set to 80 years before - // the current time. - UErrorCode status = U_ZERO_ERROR; - BuddhistCalendar calendar(Locale("@calendar=buddhist"),status); - if (U_SUCCESS(status)) { - calendar.setTime(Calendar::getNow(), status); - calendar.add(UCAL_YEAR, -80, status); - UDate newStart = calendar.getTime(status); - int32_t newYear = calendar.get(UCAL_YEAR, status); - gSystemDefaultCenturyStartYear = newYear; - gSystemDefaultCenturyStart = newStart; - } - // We have no recourse upon failure unless we want to propagate the failure - // out. -} - -UDate BuddhistCalendar::defaultCenturyStart() const -{ - // lazy-evaluate systemDefaultCenturyStart and systemDefaultCenturyStartYear - umtx_initOnce(gBCInitOnce, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStart; -} - -int32_t BuddhistCalendar::defaultCenturyStartYear() const -{ - // lazy-evaluate systemDefaultCenturyStartYear and systemDefaultCenturyStart - umtx_initOnce(gBCInitOnce, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStartYear; -} - - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2003-2013, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* File BUDDHCAL.CPP +* +* Modification History: +* 05/13/2003 srl copied from gregocal.cpp +* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "buddhcal.h" +#include "unicode/gregocal.h" +#include "umutex.h" +#include + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(BuddhistCalendar) + +//static const int32_t kMaxEra = 0; // only 1 era + +static const int32_t kBuddhistEraStart = -543; // 544 BC (Gregorian) + +static const int32_t kGregorianEpoch = 1970; // used as the default value of EXTENDED_YEAR + +BuddhistCalendar::BuddhistCalendar(const Locale& aLocale, UErrorCode& success) +: GregorianCalendar(aLocale, success) +{ + setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. +} + +BuddhistCalendar::~BuddhistCalendar() +{ +} + +BuddhistCalendar::BuddhistCalendar(const BuddhistCalendar& source) +: GregorianCalendar(source) +{ +} + +BuddhistCalendar& BuddhistCalendar::operator= ( const BuddhistCalendar& right) +{ + GregorianCalendar::operator=(right); + return *this; +} + +BuddhistCalendar* BuddhistCalendar::clone() const +{ + return new BuddhistCalendar(*this); +} + +const char *BuddhistCalendar::getType() const +{ + return "buddhist"; +} + +int32_t BuddhistCalendar::handleGetExtendedYear() +{ + // EXTENDED_YEAR in BuddhistCalendar is a Gregorian year. + // The default value of EXTENDED_YEAR is 1970 (Buddhist 2513) + int32_t year; + if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { + year = internalGet(UCAL_EXTENDED_YEAR, kGregorianEpoch); + } else { + // extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc + year = internalGet(UCAL_YEAR, kGregorianEpoch - kBuddhistEraStart) + + kBuddhistEraStart; + } + return year; +} + +void BuddhistCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) +{ + GregorianCalendar::handleComputeFields(julianDay, status); + int32_t y = internalGet(UCAL_EXTENDED_YEAR) - kBuddhistEraStart; + internalSet(UCAL_ERA, 0); + internalSet(UCAL_YEAR, y); +} + +int32_t BuddhistCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const +{ + if(field == UCAL_ERA) { + return BE; + } else { + return GregorianCalendar::handleGetLimit(field,limitType); + } +} + +#if 0 +void BuddhistCalendar::timeToFields(UDate theTime, UBool quick, UErrorCode& status) +{ + //Calendar::timeToFields(theTime, quick, status); + + int32_t era = internalGet(UCAL_ERA); + int32_t year = internalGet(UCAL_YEAR); + + if(era == GregorianCalendar::BC) { + year = 1-year; + era = BuddhistCalendar::BE; + } else if(era == GregorianCalendar::AD) { + era = BuddhistCalendar::BE; + } else { + status = U_INTERNAL_PROGRAM_ERROR; + } + + year = year - kBuddhistEraStart; + + internalSet(UCAL_ERA, era); + internalSet(UCAL_YEAR, year); +} +#endif + +/** + * The system maintains a static default century start date. This is initialized + * the first time it is used. Once the system default century date and year + * are set, they do not change. + */ +static UDate gSystemDefaultCenturyStart = DBL_MIN; +static int32_t gSystemDefaultCenturyStartYear = -1; +static icu::UInitOnce gBCInitOnce {}; + + +UBool BuddhistCalendar::haveDefaultCentury() const +{ + return true; +} + +static void U_CALLCONV +initializeSystemDefaultCentury() +{ + // initialize systemDefaultCentury and systemDefaultCenturyYear based + // on the current time. They'll be set to 80 years before + // the current time. + UErrorCode status = U_ZERO_ERROR; + BuddhistCalendar calendar(Locale("@calendar=buddhist"),status); + if (U_SUCCESS(status)) { + calendar.setTime(Calendar::getNow(), status); + calendar.add(UCAL_YEAR, -80, status); + UDate newStart = calendar.getTime(status); + int32_t newYear = calendar.get(UCAL_YEAR, status); + gSystemDefaultCenturyStartYear = newYear; + gSystemDefaultCenturyStart = newStart; + } + // We have no recourse upon failure unless we want to propagate the failure + // out. +} + +UDate BuddhistCalendar::defaultCenturyStart() const +{ + // lazy-evaluate systemDefaultCenturyStart and systemDefaultCenturyStartYear + umtx_initOnce(gBCInitOnce, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStart; +} + +int32_t BuddhistCalendar::defaultCenturyStartYear() const +{ + // lazy-evaluate systemDefaultCenturyStartYear and systemDefaultCenturyStart + umtx_initOnce(gBCInitOnce, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStartYear; +} + + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/i18n/buddhcal.h b/deps/icu-small/source/i18n/buddhcal.h index 2ef5c524935a85..d8af2cdf14b9f0 100644 --- a/deps/icu-small/source/i18n/buddhcal.h +++ b/deps/icu-small/source/i18n/buddhcal.h @@ -1,202 +1,187 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ******************************************************************************** - * Copyright (C) 2003-2013, International Business Machines Corporation - * and others. All Rights Reserved. - ******************************************************************************** - * - * File BUDDHCAL.H - * - * Modification History: - * - * Date Name Description - * 05/13/2003 srl copied from gregocal.h - ******************************************************************************** - */ - -#ifndef BUDDHCAL_H -#define BUDDHCAL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/calendar.h" -#include "unicode/gregocal.h" - -U_NAMESPACE_BEGIN - -/** - * Concrete class which provides the Buddhist calendar. - *

- * BuddhistCalendar is a subclass of GregorianCalendar - * that numbers years since the birth of the Buddha. This is the civil calendar - * in some predominantly Buddhist countries such as Thailand, and it is used for - * religious purposes elsewhere. - *

- * The Buddhist calendar is identical to the Gregorian calendar in all respects - * except for the year and era. Years are numbered since the birth of the - * Buddha in 543 BC (Gregorian), so that 1 AD (Gregorian) is equivalent to 544 - * BE (Buddhist Era) and 1998 AD is 2541 BE. - *

- * The Buddhist Calendar has only one allowable era: BE. If the - * calendar is not in lenient mode (see setLenient), dates before - * 1/1/1 BE are rejected as an illegal argument. - *

- * @internal - */ -class BuddhistCalendar : public GregorianCalendar { -public: - - /** - * Useful constants for BuddhistCalendar. Only one Era. - * @internal - */ - enum EEras { - BE - }; - - /** - * Constructs a BuddhistCalendar based on the current time in the default time zone - * with the given locale. - * - * @param aLocale The given locale. - * @param success Indicates the status of BuddhistCalendar object construction. - * Returns U_ZERO_ERROR if constructed successfully. - * @internal - */ - BuddhistCalendar(const Locale& aLocale, UErrorCode& success); - - - /** - * Destructor - * @internal - */ - virtual ~BuddhistCalendar(); - - /** - * Copy constructor - * @param source the object to be copied. - * @internal - */ - BuddhistCalendar(const BuddhistCalendar& source); - - /** - * Default assignment operator - * @param right the object to be copied. - * @internal - */ - BuddhistCalendar& operator=(const BuddhistCalendar& right); - - /** - * Create and return a polymorphic copy of this calendar. - * @return return a polymorphic copy of this calendar. - * @internal - */ - virtual BuddhistCalendar* clone() const override; - -public: - /** - * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual - * override. This method is to implement a simple version of RTTI, since not all C++ - * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call - * this method. - * - * @return The class ID for this object. All objects of a given class have the - * same class ID. Objects of other classes have different class IDs. - * @internal - */ - virtual UClassID getDynamicClassID(void) const override; - - /** - * Return the class ID for this class. This is useful only for comparing to a return - * value from getDynamicClassID(). For example: - * - * Base* polymorphic_pointer = createPolymorphicObject(); - * if (polymorphic_pointer->getDynamicClassID() == - * Derived::getStaticClassID()) ... - * - * @return The class ID for all objects of this class. - * @internal - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * return the calendar type, "buddhist". - * - * @return calendar type - * @internal - */ - virtual const char * getType() const override; - -private: - BuddhistCalendar(); // default constructor not implemented - - protected: - /** - * Return the extended year defined by the current fields. This will - * use the UCAL_EXTENDED_YEAR field or the UCAL_YEAR and supra-year fields (such - * as UCAL_ERA) specific to the calendar system, depending on which set of - * fields is newer. - * @return the extended year - * @internal - */ - virtual int32_t handleGetExtendedYear() override; - /** - * Subclasses may override this method to compute several fields - * specific to each calendar system. - * @internal - */ - virtual void handleComputeFields(int32_t julianDay, UErrorCode& status) override; - /** - * Subclass API for defining limits of different types. - * @param field one of the field numbers - * @param limitType one of MINIMUM, GREATEST_MINIMUM, - * LEAST_MAXIMUM, or MAXIMUM - * @internal - */ - virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; - /** - * Return the Julian day number of day before the first day of the - * given month in the given extended year. Subclasses should override - * this method to implement their calendar system. - * @param eyear the extended year - * @param month the zero-based month, or 0 if useMonth is false - * @param useMonth if false, compute the day before the first day of - * the given year, otherwise, compute the day before the first day of - * the given month - * @param return the Julian day number of the day before the first - * day of the given month and year - * @internal - */ - virtual int32_t handleComputeMonthStart(int32_t eyear, int32_t month, - UBool useMonth) const override; - - /** - * Returns true because the Buddhist Calendar does have a default century - * @internal - */ - virtual UBool haveDefaultCentury() const override; - - /** - * Returns the date of the start of the default century - * @return start of century - in milliseconds since epoch, 1970 - * @internal - */ - virtual UDate defaultCenturyStart() const override; - - /** - * Returns the year in which the default century begins - * @internal - */ - virtual int32_t defaultCenturyStartYear() const override; -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -#endif // _GREGOCAL -//eof - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************************** + * Copyright (C) 2003-2013, International Business Machines Corporation + * and others. All Rights Reserved. + ******************************************************************************** + * + * File BUDDHCAL.H + * + * Modification History: + * + * Date Name Description + * 05/13/2003 srl copied from gregocal.h + ******************************************************************************** + */ + +#ifndef BUDDHCAL_H +#define BUDDHCAL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" +#include "unicode/gregocal.h" + +U_NAMESPACE_BEGIN + +/** + * Concrete class which provides the Buddhist calendar. + *

+ * BuddhistCalendar is a subclass of GregorianCalendar + * that numbers years since the birth of the Buddha. This is the civil calendar + * in some predominantly Buddhist countries such as Thailand, and it is used for + * religious purposes elsewhere. + *

+ * The Buddhist calendar is identical to the Gregorian calendar in all respects + * except for the year and era. Years are numbered since the birth of the + * Buddha in 543 BC (Gregorian), so that 1 AD (Gregorian) is equivalent to 544 + * BE (Buddhist Era) and 1998 AD is 2541 BE. + *

+ * The Buddhist Calendar has only one allowable era: BE. If the + * calendar is not in lenient mode (see setLenient), dates before + * 1/1/1 BE are rejected as an illegal argument. + *

+ * @internal + */ +class BuddhistCalendar : public GregorianCalendar { +public: + + /** + * Useful constants for BuddhistCalendar. Only one Era. + * @internal + */ + enum EEras { + BE + }; + + /** + * Constructs a BuddhistCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of BuddhistCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + BuddhistCalendar(const Locale& aLocale, UErrorCode& success); + + + /** + * Destructor + * @internal + */ + virtual ~BuddhistCalendar(); + + /** + * Copy constructor + * @param source the object to be copied. + * @internal + */ + BuddhistCalendar(const BuddhistCalendar& source); + + /** + * Default assignment operator + * @param right the object to be copied. + * @internal + */ + BuddhistCalendar& operator=(const BuddhistCalendar& right); + + /** + * Create and return a polymorphic copy of this calendar. + * @return return a polymorphic copy of this calendar. + * @internal + */ + virtual BuddhistCalendar* clone() const override; + +public: + /** + * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual + * override. This method is to implement a simple version of RTTI, since not all C++ + * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call + * this method. + * + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + + /** + * return the calendar type, "buddhist". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + +private: + BuddhistCalendar(); // default constructor not implemented + + protected: + /** + * Return the extended year defined by the current fields. This will + * use the UCAL_EXTENDED_YEAR field or the UCAL_YEAR and supra-year fields (such + * as UCAL_ERA) specific to the calendar system, depending on which set of + * fields is newer. + * @return the extended year + * @internal + */ + virtual int32_t handleGetExtendedYear() override; + /** + * Subclasses may override this method to compute several fields + * specific to each calendar system. + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode& status) override; + /** + * Subclass API for defining limits of different types. + * @param field one of the field numbers + * @param limitType one of MINIMUM, GREATEST_MINIMUM, + * LEAST_MAXIMUM, or MAXIMUM + * @internal + */ + virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; + + /** + * Returns true because the Buddhist Calendar does have a default century + * @internal + */ + virtual UBool haveDefaultCentury() const override; + + /** + * Returns the date of the start of the default century + * @return start of century - in milliseconds since epoch, 1970 + * @internal + */ + virtual UDate defaultCenturyStart() const override; + + /** + * Returns the year in which the default century begins + * @internal + */ + virtual int32_t defaultCenturyStartYear() const override; +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif // _GREGOCAL +//eof + diff --git a/deps/icu-small/source/i18n/calendar.cpp b/deps/icu-small/source/i18n/calendar.cpp index 7b8b929990f3f0..98ce334a90d1d9 100644 --- a/deps/icu-small/source/i18n/calendar.cpp +++ b/deps/icu-small/source/i18n/calendar.cpp @@ -1,4079 +1,4077 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2016, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -* -* File CALENDAR.CPP -* -* Modification History: -* -* Date Name Description -* 02/03/97 clhuang Creation. -* 04/22/97 aliu Cleaned up, fixed memory leak, made -* setWeekCountData() more robust. -* Moved platform code to TPlatformUtilities. -* 05/01/97 aliu Made equals(), before(), after() arguments const. -* 05/20/97 aliu Changed logic of when to compute fields and time -* to fix bugs. -* 08/12/97 aliu Added equivalentTo. Misc other fixes. -* 07/28/98 stephen Sync up with JDK 1.2 -* 09/02/98 stephen Sync with JDK 1.2 8/31 build (getActualMin/Max) -* 03/17/99 stephen Changed adoptTimeZone() - now fAreFieldsSet is -* set to false to force update of time. -******************************************************************************* -*/ - -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/gregocal.h" -#include "unicode/basictz.h" -#include "unicode/simpletz.h" -#include "unicode/rbtz.h" -#include "unicode/vtzone.h" -#include "gregoimp.h" -#include "buddhcal.h" -#include "taiwncal.h" -#include "japancal.h" -#include "islamcal.h" -#include "hebrwcal.h" -#include "persncal.h" -#include "indiancal.h" -#include "chnsecal.h" -#include "coptccal.h" -#include "dangical.h" -#include "ethpccal.h" -#include "unicode/calendar.h" -#include "cpputils.h" -#include "servloc.h" -#include "ucln_in.h" -#include "cstring.h" -#include "locbased.h" -#include "uresimp.h" -#include "ustrenum.h" -#include "uassert.h" -#include "olsontz.h" -#include "sharedcalendar.h" -#include "unifiedcache.h" -#include "ulocimp.h" - -#if !UCONFIG_NO_SERVICE -static icu::ICULocaleService* gService = NULL; -static icu::UInitOnce gServiceInitOnce {}; - -// INTERNAL - for cleanup -U_CDECL_BEGIN -static UBool calendar_cleanup(void) { -#if !UCONFIG_NO_SERVICE - if (gService) { - delete gService; - gService = NULL; - } - gServiceInitOnce.reset(); -#endif - return true; -} -U_CDECL_END -#endif - -// ------------------------------------------ -// -// Registration -// -//------------------------------------------- -//#define U_DEBUG_CALSVC 1 -// - -#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) - -/** - * fldName was removed as a duplicate implementation. - * use udbg_ services instead, - * which depend on include files and library from ../tools/toolutil, the following circular link: - * CPPFLAGS+=-I$(top_srcdir)/tools/toolutil - * LIBS+=$(LIBICUTOOLUTIL) - */ -#include "udbgutil.h" -#include - -/** -* convert a UCalendarDateFields into a string - for debugging -* @param f field enum -* @return static string to the field name -* @internal -*/ - -const char* fldName(UCalendarDateFields f) { - return udbg_enumName(UDBG_UCalendarDateFields, (int32_t)f); -} - -#if UCAL_DEBUG_DUMP -// from CalendarTest::calToStr - but doesn't modify contents. -void ucal_dump(const Calendar &cal) { - cal.dump(); -} - -void Calendar::dump() const { - int i; - fprintf(stderr, "@calendar=%s, timeset=%c, fieldset=%c, allfields=%c, virtualset=%c, t=%.2f", - getType(), fIsTimeSet?'y':'n', fAreFieldsSet?'y':'n', fAreAllFieldsSet?'y':'n', - fAreFieldsVirtuallySet?'y':'n', - fTime); - - // can add more things here: DST, zone, etc. - fprintf(stderr, "\n"); - for(i = 0;i U_I18N_API -const SharedCalendar *LocaleCacheKey::createObject( - const void * /*unusedCreationContext*/, UErrorCode &status) const { - if (U_FAILURE(status)) { - return nullptr; - } - Calendar *calendar = Calendar::makeInstance(fLoc, status); - if (U_FAILURE(status)) { - return nullptr; - } - SharedCalendar *shared = new SharedCalendar(calendar); - if (shared == nullptr) { - delete calendar; - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - shared->addRef(); - return shared; -} - -static ECalType getCalendarType(const char *s) { - for (int i = 0; gCalTypes[i] != NULL; i++) { - if (uprv_stricmp(s, gCalTypes[i]) == 0) { - return (ECalType)i; - } - } - return CALTYPE_UNKNOWN; -} - -#if !UCONFIG_NO_SERVICE -// Only used with service registration. -static UBool isStandardSupportedKeyword(const char *keyword, UErrorCode& status) { - if(U_FAILURE(status)) { - return false; - } - ECalType calType = getCalendarType(keyword); - return (calType != CALTYPE_UNKNOWN); -} - -// only used with service registration. -static void getCalendarKeyword(const UnicodeString &id, char *targetBuffer, int32_t targetBufferSize) { - UnicodeString calendarKeyword = UNICODE_STRING_SIMPLE("calendar="); - int32_t calKeyLen = calendarKeyword.length(); - int32_t keyLen = 0; - - int32_t keywordIdx = id.indexOf((UChar)0x003D); /* '=' */ - if (id[0] == 0x40/*'@'*/ - && id.compareBetween(1, keywordIdx+1, calendarKeyword, 0, calKeyLen) == 0) - { - keyLen = id.extract(keywordIdx+1, id.length(), targetBuffer, targetBufferSize, US_INV); - } - targetBuffer[keyLen] = 0; -} -#endif - -static ECalType getCalendarTypeForLocale(const char *locid) { - UErrorCode status = U_ZERO_ERROR; - ECalType calType = CALTYPE_UNKNOWN; - - //TODO: ULOC_FULL_NAME is out of date and too small.. - char canonicalName[256]; - - // Canonicalize, so that an old-style variant will be transformed to keywords. - // e.g ja_JP_TRADITIONAL -> ja_JP@calendar=japanese - // NOTE: Since ICU-20187, ja_JP_TRADITIONAL no longer canonicalizes, and - // the Gregorian calendar is returned instead. - int32_t canonicalLen = uloc_canonicalize(locid, canonicalName, sizeof(canonicalName) - 1, &status); - if (U_FAILURE(status)) { - return CALTYPE_GREGORIAN; - } - canonicalName[canonicalLen] = 0; // terminate - - char calTypeBuf[32]; - int32_t calTypeBufLen; - - calTypeBufLen = uloc_getKeywordValue(canonicalName, "calendar", calTypeBuf, sizeof(calTypeBuf) - 1, &status); - if (U_SUCCESS(status)) { - calTypeBuf[calTypeBufLen] = 0; - calType = getCalendarType(calTypeBuf); - if (calType != CALTYPE_UNKNOWN) { - return calType; - } - } - status = U_ZERO_ERROR; - - // when calendar keyword is not available or not supported, read supplementalData - // to get the default calendar type for the locale's region - char region[ULOC_COUNTRY_CAPACITY]; - (void)ulocimp_getRegionForSupplementalData(canonicalName, true, region, sizeof(region), &status); - if (U_FAILURE(status)) { - return CALTYPE_GREGORIAN; - } - - // Read preferred calendar values from supplementalData calendarPreference - UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", &status); - ures_getByKey(rb, "calendarPreferenceData", rb, &status); - UResourceBundle *order = ures_getByKey(rb, region, NULL, &status); - if (status == U_MISSING_RESOURCE_ERROR && rb != NULL) { - status = U_ZERO_ERROR; - order = ures_getByKey(rb, "001", NULL, &status); - } - - calTypeBuf[0] = 0; - if (U_SUCCESS(status) && order != NULL) { - // the first calendar type is the default for the region - int32_t len = 0; - const UChar *uCalType = ures_getStringByIndex(order, 0, &len, &status); - if (len < (int32_t)sizeof(calTypeBuf)) { - u_UCharsToChars(uCalType, calTypeBuf, len); - *(calTypeBuf + len) = 0; // terminate; - calType = getCalendarType(calTypeBuf); - } - } - - ures_close(order); - ures_close(rb); - - if (calType == CALTYPE_UNKNOWN) { - // final fallback - calType = CALTYPE_GREGORIAN; - } - return calType; -} - -static Calendar *createStandardCalendar(ECalType calType, const Locale &loc, UErrorCode& status) { - if (U_FAILURE(status)) { - return nullptr; - } - LocalPointer cal; - - switch (calType) { - case CALTYPE_GREGORIAN: - cal.adoptInsteadAndCheckErrorCode(new GregorianCalendar(loc, status), status); - break; - case CALTYPE_JAPANESE: - cal.adoptInsteadAndCheckErrorCode(new JapaneseCalendar(loc, status), status); - break; - case CALTYPE_BUDDHIST: - cal.adoptInsteadAndCheckErrorCode(new BuddhistCalendar(loc, status), status); - break; - case CALTYPE_ROC: - cal.adoptInsteadAndCheckErrorCode(new TaiwanCalendar(loc, status), status); - break; - case CALTYPE_PERSIAN: - cal.adoptInsteadAndCheckErrorCode(new PersianCalendar(loc, status), status); - break; - case CALTYPE_ISLAMIC_TBLA: - cal.adoptInsteadAndCheckErrorCode(new IslamicCalendar(loc, status, IslamicCalendar::TBLA), status); - break; - case CALTYPE_ISLAMIC_CIVIL: - cal.adoptInsteadAndCheckErrorCode(new IslamicCalendar(loc, status, IslamicCalendar::CIVIL), status); - break; - case CALTYPE_ISLAMIC_RGSA: - // default any region specific not handled individually to islamic - case CALTYPE_ISLAMIC: - cal.adoptInsteadAndCheckErrorCode(new IslamicCalendar(loc, status, IslamicCalendar::ASTRONOMICAL), status); - break; - case CALTYPE_ISLAMIC_UMALQURA: - cal.adoptInsteadAndCheckErrorCode(new IslamicCalendar(loc, status, IslamicCalendar::UMALQURA), status); - break; - case CALTYPE_HEBREW: - cal.adoptInsteadAndCheckErrorCode(new HebrewCalendar(loc, status), status); - break; - case CALTYPE_CHINESE: - cal.adoptInsteadAndCheckErrorCode(new ChineseCalendar(loc, status), status); - break; - case CALTYPE_INDIAN: - cal.adoptInsteadAndCheckErrorCode(new IndianCalendar(loc, status), status); - break; - case CALTYPE_COPTIC: - cal.adoptInsteadAndCheckErrorCode(new CopticCalendar(loc, status), status); - break; - case CALTYPE_ETHIOPIC: - cal.adoptInsteadAndCheckErrorCode(new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_MIHRET_ERA), status); - break; - case CALTYPE_ETHIOPIC_AMETE_ALEM: - cal.adoptInsteadAndCheckErrorCode(new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_ALEM_ERA), status); - break; - case CALTYPE_ISO8601: - cal.adoptInsteadAndCheckErrorCode(new GregorianCalendar(loc, status), status); - if (cal.isValid()) { - cal->setFirstDayOfWeek(UCAL_MONDAY); - cal->setMinimalDaysInFirstWeek(4); - } - break; - case CALTYPE_DANGI: - cal.adoptInsteadAndCheckErrorCode(new DangiCalendar(loc, status), status); - break; - default: - status = U_UNSUPPORTED_ERROR; - } - return cal.orphan(); -} - - -#if !UCONFIG_NO_SERVICE - -// ------------------------------------- - -/** -* a Calendar Factory which creates the "basic" calendar types, that is, those -* shipped with ICU. -*/ -class BasicCalendarFactory : public LocaleKeyFactory { -public: - /** - * @param calendarType static const string (caller owns storage - will be aliased) to calendar type - */ - BasicCalendarFactory() - : LocaleKeyFactory(LocaleKeyFactory::INVISIBLE) { } - - virtual ~BasicCalendarFactory(); - -protected: - //virtual UBool isSupportedID( const UnicodeString& id, UErrorCode& status) const { - // if(U_FAILURE(status)) { - // return false; - // } - // char keyword[ULOC_FULLNAME_CAPACITY]; - // getCalendarKeyword(id, keyword, (int32_t)sizeof(keyword)); - // return isStandardSupportedKeyword(keyword, status); - //} - - virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const override - { - if (U_SUCCESS(status)) { - for(int32_t i=0;gCalTypes[i] != NULL;i++) { - UnicodeString id((UChar)0x40); /* '@' a variant character */ - id.append(UNICODE_STRING_SIMPLE("calendar=")); - id.append(UnicodeString(gCalTypes[i], -1, US_INV)); - result.put(id, (void*)this, status); - } - } - } - - virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const override { - if (U_FAILURE(status)) { - return nullptr; - } -#ifdef U_DEBUG_CALSVC - if(dynamic_cast(&key) == NULL) { - fprintf(stderr, "::create - not a LocaleKey!\n"); - } -#endif - const LocaleKey& lkey = (LocaleKey&)key; - Locale curLoc; // current locale - Locale canLoc; // Canonical locale - - lkey.currentLocale(curLoc); - lkey.canonicalLocale(canLoc); - - char keyword[ULOC_FULLNAME_CAPACITY]; - UnicodeString str; - - key.currentID(str); - getCalendarKeyword(str, keyword, (int32_t) sizeof(keyword)); - -#ifdef U_DEBUG_CALSVC - fprintf(stderr, "BasicCalendarFactory::create() - cur %s, can %s\n", (const char*)curLoc.getName(), (const char*)canLoc.getName()); -#endif - - if(!isStandardSupportedKeyword(keyword,status)) { // Do we handle this type? -#ifdef U_DEBUG_CALSVC - - fprintf(stderr, "BasicCalendarFactory - not handling %s.[%s]\n", (const char*) curLoc.getName(), tmp ); -#endif - return NULL; - } - - return createStandardCalendar(getCalendarType(keyword), canLoc, status); - } -}; - -BasicCalendarFactory::~BasicCalendarFactory() {} - -/** -* A factory which looks up the DefaultCalendar resource to determine which class of calendar to use -*/ - -class DefaultCalendarFactory : public ICUResourceBundleFactory { -public: - DefaultCalendarFactory() : ICUResourceBundleFactory() { } - virtual ~DefaultCalendarFactory(); -protected: - virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const override { - if (U_FAILURE(status)) { - return nullptr; - } - - LocaleKey &lkey = (LocaleKey&)key; - Locale loc; - lkey.currentLocale(loc); - - UnicodeString *ret = new UnicodeString(); - if (ret == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - ret->append((UChar)0x40); // '@' is a variant character - ret->append(UNICODE_STRING("calendar=", 9)); - ret->append(UnicodeString(gCalTypes[getCalendarTypeForLocale(loc.getName())], -1, US_INV)); - } - return ret; - } -}; - -DefaultCalendarFactory::~DefaultCalendarFactory() {} - -// ------------------------------------- -class CalendarService : public ICULocaleService { -public: - CalendarService() - : ICULocaleService(UNICODE_STRING_SIMPLE("Calendar")) - { - UErrorCode status = U_ZERO_ERROR; - registerFactory(new DefaultCalendarFactory(), status); - } - - virtual ~CalendarService(); - - virtual UObject* cloneInstance(UObject* instance) const override { - UnicodeString *s = dynamic_cast(instance); - if(s != NULL) { - return s->clone(); - } else { -#ifdef U_DEBUG_CALSVC_F - UErrorCode status2 = U_ZERO_ERROR; - fprintf(stderr, "Cloning a %s calendar with tz=%ld\n", ((Calendar*)instance)->getType(), ((Calendar*)instance)->get(UCAL_ZONE_OFFSET, status2)); -#endif - return ((Calendar*)instance)->clone(); - } - } - - virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /*actualID*/, UErrorCode& status) const override { - if (U_FAILURE(status)) { - return nullptr; - } - LocaleKey& lkey = (LocaleKey&)key; - //int32_t kind = lkey.kind(); - - Locale loc; - lkey.canonicalLocale(loc); - -#ifdef U_DEBUG_CALSVC - Locale loc2; - lkey.currentLocale(loc2); - fprintf(stderr, "CalSvc:handleDefault for currentLoc %s, canloc %s\n", (const char*)loc.getName(), (const char*)loc2.getName()); -#endif - Calendar *nc = new GregorianCalendar(loc, status); - if (nc == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nc; - } - -#ifdef U_DEBUG_CALSVC - UErrorCode status2 = U_ZERO_ERROR; - fprintf(stderr, "New default calendar has tz=%d\n", ((Calendar*)nc)->get(UCAL_ZONE_OFFSET, status2)); -#endif - return nc; - } - - virtual UBool isDefault() const override { - return countFactories() == 1; - } -}; - -CalendarService::~CalendarService() {} - -// ------------------------------------- - -static inline UBool -isCalendarServiceUsed() { - return !gServiceInitOnce.isReset(); -} - -// ------------------------------------- - -static void U_CALLCONV -initCalendarService(UErrorCode &status) -{ -#ifdef U_DEBUG_CALSVC - fprintf(stderr, "Spinning up Calendar Service\n"); -#endif - if (U_FAILURE(status)) { - return; - } - ucln_i18n_registerCleanup(UCLN_I18N_CALENDAR, calendar_cleanup); - gService = new CalendarService(); - if (gService == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } -#ifdef U_DEBUG_CALSVC - fprintf(stderr, "Registering classes..\n"); -#endif - - // Register all basic instances. - gService->registerFactory(new BasicCalendarFactory(),status); - -#ifdef U_DEBUG_CALSVC - fprintf(stderr, "Done..\n"); -#endif - - if(U_FAILURE(status)) { -#ifdef U_DEBUG_CALSVC - fprintf(stderr, "err (%s) registering classes, deleting service.....\n", u_errorName(status)); -#endif - delete gService; - gService = NULL; - } - } - -static ICULocaleService* -getCalendarService(UErrorCode &status) -{ - umtx_initOnce(gServiceInitOnce, &initCalendarService, status); - return gService; -} - -URegistryKey Calendar::registerFactory(ICUServiceFactory* toAdopt, UErrorCode& status) -{ - return getCalendarService(status)->registerFactory(toAdopt, status); -} - -UBool Calendar::unregister(URegistryKey key, UErrorCode& status) { - return getCalendarService(status)->unregister(key, status); -} -#endif /* UCONFIG_NO_SERVICE */ - -// ------------------------------------- - -static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = { - // Minimum Greatest min Least max Greatest max - {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // ERA - {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR - {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // MONTH - {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_YEAR - {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_MONTH - {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_MONTH - {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_YEAR - { 1, 1, 7, 7 }, // DAY_OF_WEEK - {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_WEEK_IN_MONTH - { 0, 0, 1, 1 }, // AM_PM - { 0, 0, 11, 11 }, // HOUR - { 0, 0, 23, 23 }, // HOUR_OF_DAY - { 0, 0, 59, 59 }, // MINUTE - { 0, 0, 59, 59 }, // SECOND - { 0, 0, 999, 999 }, // MILLISECOND - {-16*kOneHour, -16*kOneHour, 12*kOneHour, 30*kOneHour }, // ZONE_OFFSET - { -1*kOneHour, -1*kOneHour, 2*kOneHour, 2*kOneHour }, // DST_OFFSET - {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR_WOY - { 1, 1, 7, 7 }, // DOW_LOCAL - {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // EXTENDED_YEAR - { -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, // JULIAN_DAY - { 0, 0, 24*kOneHour-1, 24*kOneHour-1 }, // MILLISECONDS_IN_DAY - { 0, 0, 1, 1 }, // IS_LEAP_MONTH -}; - -// Resource bundle tags read by this class -static const char gCalendar[] = "calendar"; -static const char gMonthNames[] = "monthNames"; -static const char gGregorian[] = "gregorian"; - -// Data flow in Calendar -// --------------------- - -// The current time is represented in two ways by Calendar: as UTC -// milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local -// fields such as MONTH, HOUR, AM_PM, etc. It is possible to compute the -// millis from the fields, and vice versa. The data needed to do this -// conversion is encapsulated by a TimeZone object owned by the Calendar. -// The data provided by the TimeZone object may also be overridden if the -// user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class -// keeps track of what information was most recently set by the caller, and -// uses that to compute any other information as needed. - -// If the user sets the fields using set(), the data flow is as follows. -// This is implemented by the Calendar subclass's computeTime() method. -// During this process, certain fields may be ignored. The disambiguation -// algorithm for resolving which fields to pay attention to is described -// above. - -// local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.) -// | -// | Using Calendar-specific algorithm -// V -// local standard millis -// | -// | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET -// V -// UTC millis (in time data member) - -// If the user sets the UTC millis using setTime(), the data flow is as -// follows. This is implemented by the Calendar subclass's computeFields() -// method. - -// UTC millis (in time data member) -// | -// | Using TimeZone getOffset() -// V -// local standard millis -// | -// | Using Calendar-specific algorithm -// V -// local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.) - -// In general, a round trip from fields, through local and UTC millis, and -// back out to fields is made when necessary. This is implemented by the -// complete() method. Resolving a partial set of fields into a UTC millis -// value allows all remaining fields to be generated from that value. If -// the Calendar is lenient, the fields are also renormalized to standard -// ranges when they are regenerated. - -// ------------------------------------- - -Calendar::Calendar(UErrorCode& success) -: UObject(), -fIsTimeSet(false), -fAreFieldsSet(false), -fAreAllFieldsSet(false), -fAreFieldsVirtuallySet(false), -fNextStamp((int32_t)kMinimumUserStamp), -fTime(0), -fLenient(true), -fZone(NULL), -fRepeatedWallTime(UCAL_WALLTIME_LAST), -fSkippedWallTime(UCAL_WALLTIME_LAST) -{ - validLocale[0] = 0; - actualLocale[0] = 0; - clear(); - if (U_FAILURE(success)) { - return; - } - fZone = TimeZone::createDefault(); - if (fZone == NULL) { - success = U_MEMORY_ALLOCATION_ERROR; - } - setWeekData(Locale::getDefault(), NULL, success); -} - -// ------------------------------------- - -Calendar::Calendar(TimeZone* zone, const Locale& aLocale, UErrorCode& success) -: UObject(), -fIsTimeSet(false), -fAreFieldsSet(false), -fAreAllFieldsSet(false), -fAreFieldsVirtuallySet(false), -fNextStamp((int32_t)kMinimumUserStamp), -fTime(0), -fLenient(true), -fZone(NULL), -fRepeatedWallTime(UCAL_WALLTIME_LAST), -fSkippedWallTime(UCAL_WALLTIME_LAST) -{ - validLocale[0] = 0; - actualLocale[0] = 0; - if (U_FAILURE(success)) { - delete zone; - return; - } - if(zone == 0) { -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: ILLEGAL ARG because timezone cannot be 0\n", - __FILE__, __LINE__); -#endif - success = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - clear(); - fZone = zone; - setWeekData(aLocale, NULL, success); -} - -// ------------------------------------- - -Calendar::Calendar(const TimeZone& zone, const Locale& aLocale, UErrorCode& success) -: UObject(), -fIsTimeSet(false), -fAreFieldsSet(false), -fAreAllFieldsSet(false), -fAreFieldsVirtuallySet(false), -fNextStamp((int32_t)kMinimumUserStamp), -fTime(0), -fLenient(true), -fZone(NULL), -fRepeatedWallTime(UCAL_WALLTIME_LAST), -fSkippedWallTime(UCAL_WALLTIME_LAST) -{ - validLocale[0] = 0; - actualLocale[0] = 0; - if (U_FAILURE(success)) { - return; - } - clear(); - fZone = zone.clone(); - if (fZone == NULL) { - success = U_MEMORY_ALLOCATION_ERROR; - } - setWeekData(aLocale, NULL, success); -} - -// ------------------------------------- - -Calendar::~Calendar() -{ - delete fZone; -} - -// ------------------------------------- - -Calendar::Calendar(const Calendar &source) -: UObject(source) -{ - fZone = NULL; - *this = source; -} - -// ------------------------------------- - -Calendar & -Calendar::operator=(const Calendar &right) -{ - if (this != &right) { - uprv_arrayCopy(right.fFields, fFields, UCAL_FIELD_COUNT); - uprv_arrayCopy(right.fIsSet, fIsSet, UCAL_FIELD_COUNT); - uprv_arrayCopy(right.fStamp, fStamp, UCAL_FIELD_COUNT); - fTime = right.fTime; - fIsTimeSet = right.fIsTimeSet; - fAreAllFieldsSet = right.fAreAllFieldsSet; - fAreFieldsSet = right.fAreFieldsSet; - fAreFieldsVirtuallySet = right.fAreFieldsVirtuallySet; - fLenient = right.fLenient; - fRepeatedWallTime = right.fRepeatedWallTime; - fSkippedWallTime = right.fSkippedWallTime; - delete fZone; - fZone = NULL; - if (right.fZone != NULL) { - fZone = right.fZone->clone(); - } - fFirstDayOfWeek = right.fFirstDayOfWeek; - fMinimalDaysInFirstWeek = right.fMinimalDaysInFirstWeek; - fWeekendOnset = right.fWeekendOnset; - fWeekendOnsetMillis = right.fWeekendOnsetMillis; - fWeekendCease = right.fWeekendCease; - fWeekendCeaseMillis = right.fWeekendCeaseMillis; - fNextStamp = right.fNextStamp; - uprv_strncpy(validLocale, right.validLocale, sizeof(validLocale)); - uprv_strncpy(actualLocale, right.actualLocale, sizeof(actualLocale)); - validLocale[sizeof(validLocale)-1] = 0; - actualLocale[sizeof(validLocale)-1] = 0; - } - - return *this; -} - -// ------------------------------------- - -Calendar* U_EXPORT2 -Calendar::createInstance(UErrorCode& success) -{ - return createInstance(TimeZone::createDefault(), Locale::getDefault(), success); -} - -// ------------------------------------- - -Calendar* U_EXPORT2 -Calendar::createInstance(const TimeZone& zone, UErrorCode& success) -{ - return createInstance(zone, Locale::getDefault(), success); -} - -// ------------------------------------- - -Calendar* U_EXPORT2 -Calendar::createInstance(const Locale& aLocale, UErrorCode& success) -{ - return createInstance(TimeZone::forLocaleOrDefault(aLocale), aLocale, success); -} - -// ------------------------------------- Adopting - -// Note: this is the bottleneck that actually calls the service routines. - -Calendar * U_EXPORT2 -Calendar::makeInstance(const Locale& aLocale, UErrorCode& success) { - if (U_FAILURE(success)) { - return NULL; - } - - Locale actualLoc; - UObject* u = NULL; - -#if !UCONFIG_NO_SERVICE - if (isCalendarServiceUsed()) { - u = getCalendarService(success)->get(aLocale, LocaleKey::KIND_ANY, &actualLoc, success); - } - else -#endif - { - u = createStandardCalendar(getCalendarTypeForLocale(aLocale.getName()), aLocale, success); - } - Calendar* c = NULL; - - if(U_FAILURE(success) || !u) { - if(U_SUCCESS(success)) { // Propagate some kind of err - success = U_INTERNAL_PROGRAM_ERROR; - } - return NULL; - } - -#if !UCONFIG_NO_SERVICE - const UnicodeString* str = dynamic_cast(u); - if(str != NULL) { - // It's a unicode string telling us what type of calendar to load ("gregorian", etc) - // Create a Locale over this string - Locale l(""); - LocaleUtility::initLocaleFromName(*str, l); - -#ifdef U_DEBUG_CALSVC - fprintf(stderr, "Calendar::createInstance(%s), looking up [%s]\n", aLocale.getName(), l.getName()); -#endif - - Locale actualLoc2; - delete u; - u = NULL; - - // Don't overwrite actualLoc, since the actual loc from this call - // may be something like "@calendar=gregorian" -- TODO investigate - // further... - c = (Calendar*)getCalendarService(success)->get(l, LocaleKey::KIND_ANY, &actualLoc2, success); - - if(U_FAILURE(success) || !c) { - if(U_SUCCESS(success)) { - success = U_INTERNAL_PROGRAM_ERROR; // Propagate some err - } - return NULL; - } - - str = dynamic_cast(c); - if(str != NULL) { - // recursed! Second lookup returned a UnicodeString. - // Perhaps DefaultCalendar{} was set to another locale. -#ifdef U_DEBUG_CALSVC - char tmp[200]; - // Extract a char* out of it.. - int32_t len = str->length(); - int32_t actLen = sizeof(tmp)-1; - if(len > actLen) { - len = actLen; - } - str->extract(0,len,tmp); - tmp[len]=0; - - fprintf(stderr, "err - recursed, 2nd lookup was unistring %s\n", tmp); -#endif - success = U_MISSING_RESOURCE_ERROR; // requested a calendar type which could NOT be found. - delete c; - return NULL; - } -#ifdef U_DEBUG_CALSVC - fprintf(stderr, "%p: setting week count data to locale %s, actual locale %s\n", c, (const char*)aLocale.getName(), (const char *)actualLoc.getName()); -#endif - c->setWeekData(aLocale, c->getType(), success); // set the correct locale (this was an indirect calendar) - - char keyword[ULOC_FULLNAME_CAPACITY] = ""; - UErrorCode tmpStatus = U_ZERO_ERROR; - l.getKeywordValue("calendar", keyword, ULOC_FULLNAME_CAPACITY, tmpStatus); - if (U_SUCCESS(tmpStatus) && uprv_strcmp(keyword, "iso8601") == 0) { - c->setFirstDayOfWeek(UCAL_MONDAY); - c->setMinimalDaysInFirstWeek(4); - } - } - else -#endif /* UCONFIG_NO_SERVICE */ - { - // a calendar was returned - we assume the factory did the right thing. - c = (Calendar*)u; - } - - return c; -} - -Calendar* U_EXPORT2 -Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& success) -{ - LocalPointer zonePtr(zone); - const SharedCalendar *shared = NULL; - UnifiedCache::getByLocale(aLocale, shared, success); - if (U_FAILURE(success)) { - return NULL; - } - Calendar *c = (*shared)->clone(); - shared->removeRef(); - if (c == NULL) { - success = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - // Now, reset calendar to default state: - c->adoptTimeZone(zonePtr.orphan()); // Set the correct time zone - c->setTimeInMillis(getNow(), success); // let the new calendar have the current time. - - return c; -} - -// ------------------------------------- - -Calendar* U_EXPORT2 -Calendar::createInstance(const TimeZone& zone, const Locale& aLocale, UErrorCode& success) -{ - Calendar* c = createInstance(aLocale, success); - if(U_SUCCESS(success) && c) { - c->setTimeZone(zone); - } - return c; -} - -// ------------------------------------- - -void U_EXPORT2 -Calendar::getCalendarTypeFromLocale( - const Locale &aLocale, - char *typeBuffer, - int32_t typeBufferSize, - UErrorCode &success) { - const SharedCalendar *shared = NULL; - UnifiedCache::getByLocale(aLocale, shared, success); - if (U_FAILURE(success)) { - return; - } - uprv_strncpy(typeBuffer, (*shared)->getType(), typeBufferSize); - shared->removeRef(); - if (typeBuffer[typeBufferSize - 1]) { - success = U_BUFFER_OVERFLOW_ERROR; - } -} - -bool -Calendar::operator==(const Calendar& that) const -{ - UErrorCode status = U_ZERO_ERROR; - return isEquivalentTo(that) && - getTimeInMillis(status) == that.getTimeInMillis(status) && - U_SUCCESS(status); -} - -UBool -Calendar::isEquivalentTo(const Calendar& other) const -{ - return typeid(*this) == typeid(other) && - fLenient == other.fLenient && - fRepeatedWallTime == other.fRepeatedWallTime && - fSkippedWallTime == other.fSkippedWallTime && - fFirstDayOfWeek == other.fFirstDayOfWeek && - fMinimalDaysInFirstWeek == other.fMinimalDaysInFirstWeek && - fWeekendOnset == other.fWeekendOnset && - fWeekendOnsetMillis == other.fWeekendOnsetMillis && - fWeekendCease == other.fWeekendCease && - fWeekendCeaseMillis == other.fWeekendCeaseMillis && - *fZone == *other.fZone; -} - -// ------------------------------------- - -UBool -Calendar::equals(const Calendar& when, UErrorCode& status) const -{ - return (this == &when || - getTime(status) == when.getTime(status)); -} - -// ------------------------------------- - -UBool -Calendar::before(const Calendar& when, UErrorCode& status) const -{ - return (this != &when && - getTimeInMillis(status) < when.getTimeInMillis(status)); -} - -// ------------------------------------- - -UBool -Calendar::after(const Calendar& when, UErrorCode& status) const -{ - return (this != &when && - getTimeInMillis(status) > when.getTimeInMillis(status)); -} - -// ------------------------------------- - - -const Locale* U_EXPORT2 -Calendar::getAvailableLocales(int32_t& count) -{ - return Locale::getAvailableLocales(count); -} - -// ------------------------------------- - -StringEnumeration* U_EXPORT2 -Calendar::getKeywordValuesForLocale(const char* key, - const Locale& locale, UBool commonlyUsed, UErrorCode& status) -{ - // This is a wrapper over ucal_getKeywordValuesForLocale - UEnumeration *uenum = ucal_getKeywordValuesForLocale(key, locale.getName(), - commonlyUsed, &status); - if (U_FAILURE(status)) { - uenum_close(uenum); - return NULL; - } - UStringEnumeration* ustringenum = new UStringEnumeration(uenum); - if (ustringenum == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return ustringenum; -} - -// ------------------------------------- - -UDate U_EXPORT2 -Calendar::getNow() -{ - return uprv_getUTCtime(); // return as milliseconds -} - -// ------------------------------------- - -/** -* Gets this Calendar's current time as a long. -* @return the current time as UTC milliseconds from the epoch. -*/ -double -Calendar::getTimeInMillis(UErrorCode& status) const -{ - if(U_FAILURE(status)) - return 0.0; - - if ( ! fIsTimeSet) - ((Calendar*)this)->updateTime(status); - - /* Test for buffer overflows */ - if(U_FAILURE(status)) { - return 0.0; - } - return fTime; -} - -// ------------------------------------- - -/** -* Sets this Calendar's current time from the given long value. -* A status of U_ILLEGAL_ARGUMENT_ERROR is set when millis is -* outside the range permitted by a Calendar object when not in lenient mode. -* when in lenient mode the out of range values are pinned to their respective min/max. -* @param date the new time in UTC milliseconds from the epoch. -*/ -void -Calendar::setTimeInMillis( double millis, UErrorCode& status ) { - if(U_FAILURE(status)) - return; - - if (millis > MAX_MILLIS) { - if(isLenient()) { - millis = MAX_MILLIS; - } else { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - } else if (millis < MIN_MILLIS) { - if(isLenient()) { - millis = MIN_MILLIS; - } else { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - } - - fTime = millis; - fAreFieldsSet = fAreAllFieldsSet = false; - fIsTimeSet = fAreFieldsVirtuallySet = true; - - for (int32_t i=0; icomplete(status); // Cast away const - return U_SUCCESS(status) ? fFields[field] : 0; -} - -// ------------------------------------- - -void -Calendar::set(UCalendarDateFields field, int32_t value) -{ - if (fAreFieldsVirtuallySet) { - UErrorCode ec = U_ZERO_ERROR; - computeFields(ec); - } - fFields[field] = value; - /* Ensure that the fNextStamp value doesn't go pass max value for int32_t */ - if (fNextStamp == STAMP_MAX) { - recalculateStamp(); - } - fStamp[field] = fNextStamp++; - fIsSet[field] = true; // Remove later - fIsTimeSet = fAreFieldsSet = fAreFieldsVirtuallySet = false; -} - -// ------------------------------------- - -void -Calendar::set(int32_t year, int32_t month, int32_t date) -{ - set(UCAL_YEAR, year); - set(UCAL_MONTH, month); - set(UCAL_DATE, date); -} - -// ------------------------------------- - -void -Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute) -{ - set(UCAL_YEAR, year); - set(UCAL_MONTH, month); - set(UCAL_DATE, date); - set(UCAL_HOUR_OF_DAY, hour); - set(UCAL_MINUTE, minute); -} - -// ------------------------------------- - -void -Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute, int32_t second) -{ - set(UCAL_YEAR, year); - set(UCAL_MONTH, month); - set(UCAL_DATE, date); - set(UCAL_HOUR_OF_DAY, hour); - set(UCAL_MINUTE, minute); - set(UCAL_SECOND, second); -} - -// ------------------------------------- -// For now the full getRelatedYear implementation is here; -// per #10752 move the non-default implementation to subclasses -// (default implementation will do no year adjustment) - -static int32_t gregoYearFromIslamicStart(int32_t year) { - // ad hoc conversion, improve under #10752 - // rough est for now, ok for grego 1846-2138, - // otherwise occasionally wrong (for 3% of years) - int cycle, offset, shift = 0; - if (year >= 1397) { - cycle = (year - 1397) / 67; - offset = (year - 1397) % 67; - shift = 2*cycle + ((offset >= 33)? 1: 0); - } else { - cycle = (year - 1396) / 67 - 1; - offset = -(year - 1396) % 67; - shift = 2*cycle + ((offset <= 33)? 1: 0); - } - return year + 579 - shift; -} - -int32_t Calendar::getRelatedYear(UErrorCode &status) const -{ - if (U_FAILURE(status)) { - return 0; - } - int32_t year = get(UCAL_EXTENDED_YEAR, status); - if (U_FAILURE(status)) { - return 0; - } - // modify for calendar type - ECalType type = getCalendarType(getType()); - switch (type) { - case CALTYPE_PERSIAN: - year += 622; break; - case CALTYPE_HEBREW: - year -= 3760; break; - case CALTYPE_CHINESE: - year -= 2637; break; - case CALTYPE_INDIAN: - year += 79; break; - case CALTYPE_COPTIC: - year += 284; break; - case CALTYPE_ETHIOPIC: - year += 8; break; - case CALTYPE_ETHIOPIC_AMETE_ALEM: - year -=5492; break; - case CALTYPE_DANGI: - year -= 2333; break; - case CALTYPE_ISLAMIC_CIVIL: - case CALTYPE_ISLAMIC: - case CALTYPE_ISLAMIC_UMALQURA: - case CALTYPE_ISLAMIC_TBLA: - case CALTYPE_ISLAMIC_RGSA: - year = gregoYearFromIslamicStart(year); break; - default: - // CALTYPE_GREGORIAN - // CALTYPE_JAPANESE - // CALTYPE_BUDDHIST - // CALTYPE_ROC - // CALTYPE_ISO8601 - // do nothing, EXTENDED_YEAR same as Gregorian - break; - } - return year; -} - -// ------------------------------------- -// For now the full setRelatedYear implementation is here; -// per #10752 move the non-default implementation to subclasses -// (default implementation will do no year adjustment) - -static int32_t firstIslamicStartYearFromGrego(int32_t year) { - // ad hoc conversion, improve under #10752 - // rough est for now, ok for grego 1846-2138, - // otherwise occasionally wrong (for 3% of years) - int cycle, offset, shift = 0; - if (year >= 1977) { - cycle = (year - 1977) / 65; - offset = (year - 1977) % 65; - shift = 2*cycle + ((offset >= 32)? 1: 0); - } else { - cycle = (year - 1976) / 65 - 1; - offset = -(year - 1976) % 65; - shift = 2*cycle + ((offset <= 32)? 1: 0); - } - return year - 579 + shift; -} -void Calendar::setRelatedYear(int32_t year) -{ - // modify for calendar type - ECalType type = getCalendarType(getType()); - switch (type) { - case CALTYPE_PERSIAN: - year -= 622; break; - case CALTYPE_HEBREW: - year += 3760; break; - case CALTYPE_CHINESE: - year += 2637; break; - case CALTYPE_INDIAN: - year -= 79; break; - case CALTYPE_COPTIC: - year -= 284; break; - case CALTYPE_ETHIOPIC: - year -= 8; break; - case CALTYPE_ETHIOPIC_AMETE_ALEM: - year +=5492; break; - case CALTYPE_DANGI: - year += 2333; break; - case CALTYPE_ISLAMIC_CIVIL: - case CALTYPE_ISLAMIC: - case CALTYPE_ISLAMIC_UMALQURA: - case CALTYPE_ISLAMIC_TBLA: - case CALTYPE_ISLAMIC_RGSA: - year = firstIslamicStartYearFromGrego(year); break; - default: - // CALTYPE_GREGORIAN - // CALTYPE_JAPANESE - // CALTYPE_BUDDHIST - // CALTYPE_ROC - // CALTYPE_ISO8601 - // do nothing, EXTENDED_YEAR same as Gregorian - break; - } - // set extended year - set(UCAL_EXTENDED_YEAR, year); -} - -// ------------------------------------- - -void -Calendar::clear() -{ - for (int32_t i=0; i bestStamp) { - bestStamp = fStamp[i]; - } - } - return bestStamp; -} - - -// ------------------------------------- - -void -Calendar::complete(UErrorCode& status) -{ - if (U_FAILURE(status)) { - return; - } - if (!fIsTimeSet) { - updateTime(status); - /* Test for buffer overflows */ - if(U_FAILURE(status)) { - return; - } - } - if (!fAreFieldsSet) { - computeFields(status); // fills in unset fields - /* Test for buffer overflows */ - if(U_FAILURE(status)) { - return; - } - fAreFieldsSet = true; - fAreAllFieldsSet = true; - } -} - -//------------------------------------------------------------------------- -// Protected utility methods for use by subclasses. These are very handy -// for implementing add, roll, and computeFields. -//------------------------------------------------------------------------- - -/** -* Adjust the specified field so that it is within -* the allowable range for the date to which this calendar is set. -* For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH} -* field for a calendar set to April 31 would cause it to be set -* to April 30. -*

-* Subclassing: -*
-* This utility method is intended for use by subclasses that need to implement -* their own overrides of {@link #roll roll} and {@link #add add}. -*

-* Note: -* pinField is implemented in terms of -* {@link #getActualMinimum getActualMinimum} -* and {@link #getActualMaximum getActualMaximum}. If either of those methods uses -* a slow, iterative algorithm for a particular field, it would be -* unwise to attempt to call pinField for that field. If you -* really do need to do so, you should override this method to do -* something more efficient for that field. -*

-* @param field The calendar field whose value should be pinned. -* -* @see #getActualMinimum -* @see #getActualMaximum -* @stable ICU 2.0 -*/ -void Calendar::pinField(UCalendarDateFields field, UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - int32_t max = getActualMaximum(field, status); - int32_t min = getActualMinimum(field, status); - - if (fFields[field] > max) { - set(field, max); - } else if (fFields[field] < min) { - set(field, min); - } -} - - -void Calendar::computeFields(UErrorCode &ec) -{ - if (U_FAILURE(ec)) { - return; - } - // Compute local wall millis - double localMillis = internalGetTime(); - int32_t rawOffset, dstOffset; - getTimeZone().getOffset(localMillis, false, rawOffset, dstOffset, ec); - if (U_FAILURE(ec)) { - return; - } - localMillis += (rawOffset + dstOffset); - - // Mark fields as set. Do this before calling handleComputeFields(). - uint32_t mask = //fInternalSetMask; - (1 << UCAL_ERA) | - (1 << UCAL_YEAR) | - (1 << UCAL_MONTH) | - (1 << UCAL_DAY_OF_MONTH) | // = UCAL_DATE - (1 << UCAL_DAY_OF_YEAR) | - (1 << UCAL_EXTENDED_YEAR); - - for (int32_t i=0; i>= 1; - } - - // We used to check for and correct extreme millis values (near - // Long.MIN_VALUE or Long.MAX_VALUE) here. Such values would cause - // overflows from positive to negative (or vice versa) and had to - // be manually tweaked. We no longer need to do this because we - // have limited the range of supported dates to those that have a - // Julian day that fits into an int. This allows us to implement a - // JULIAN_DAY field and also removes some inelegant code. - Liu - // 11/6/00 - - int32_t millisInDay; - int32_t days = ClockMath::floorDivide(localMillis, kOneDay, &millisInDay); - - internalSet(UCAL_JULIAN_DAY,days + kEpochStartAsJulianDay); - -#if defined (U_DEBUG_CAL) - //fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n", - //__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis); -#endif - - computeGregorianAndDOWFields(fFields[UCAL_JULIAN_DAY], ec); - - // Call framework method to have subclass compute its fields. - // These must include, at a minimum, MONTH, DAY_OF_MONTH, - // EXTENDED_YEAR, YEAR, DAY_OF_YEAR. This method will call internalSet(), - // which will update stamp[]. - handleComputeFields(fFields[UCAL_JULIAN_DAY], ec); - - // Compute week-related fields, based on the subclass-computed - // fields computed by handleComputeFields(). - computeWeekFields(ec); - - // Compute time-related fields. These are independent of the date and - // of the subclass algorithm. They depend only on the local zone - // wall milliseconds in day. - if (U_FAILURE(ec)) { - return; - } - - fFields[UCAL_MILLISECONDS_IN_DAY] = millisInDay; - U_ASSERT(getMinimum(UCAL_MILLISECONDS_IN_DAY) <= - fFields[UCAL_MILLISECONDS_IN_DAY]); - U_ASSERT(fFields[UCAL_MILLISECONDS_IN_DAY] <= - getMaximum(UCAL_MILLISECONDS_IN_DAY)); - - fFields[UCAL_MILLISECOND] = millisInDay % 1000; - U_ASSERT(getMinimum(UCAL_MILLISECOND) <= fFields[UCAL_MILLISECOND]); - U_ASSERT(fFields[UCAL_MILLISECOND] <= getMaximum(UCAL_MILLISECOND)); - - millisInDay /= 1000; - fFields[UCAL_SECOND] = millisInDay % 60; - U_ASSERT(getMinimum(UCAL_SECOND) <= fFields[UCAL_SECOND]); - U_ASSERT(fFields[UCAL_SECOND] <= getMaximum(UCAL_SECOND)); - - millisInDay /= 60; - fFields[UCAL_MINUTE] = millisInDay % 60; - U_ASSERT(getMinimum(UCAL_MINUTE) <= fFields[UCAL_MINUTE]); - U_ASSERT(fFields[UCAL_MINUTE] <= getMaximum(UCAL_MINUTE)); - - millisInDay /= 60; - fFields[UCAL_HOUR_OF_DAY] = millisInDay; - U_ASSERT(getMinimum(UCAL_HOUR_OF_DAY) <= fFields[UCAL_HOUR_OF_DAY]); - U_ASSERT(fFields[UCAL_HOUR_OF_DAY] <= getMaximum(UCAL_HOUR_OF_DAY)); - - fFields[UCAL_AM_PM] = millisInDay / 12; // Assume AM == 0 - U_ASSERT(getMinimum(UCAL_AM_PM) <= fFields[UCAL_AM_PM]); - U_ASSERT(fFields[UCAL_AM_PM] <= getMaximum(UCAL_AM_PM)); - - fFields[UCAL_HOUR] = millisInDay % 12; - U_ASSERT(getMinimum(UCAL_HOUR) <= fFields[UCAL_HOUR]); - U_ASSERT(fFields[UCAL_HOUR] <= getMaximum(UCAL_HOUR)); - - fFields[UCAL_ZONE_OFFSET] = rawOffset; - U_ASSERT(getMinimum(UCAL_ZONE_OFFSET) <= fFields[UCAL_ZONE_OFFSET]); - U_ASSERT(fFields[UCAL_ZONE_OFFSET] <= getMaximum(UCAL_ZONE_OFFSET)); - - fFields[UCAL_DST_OFFSET] = dstOffset; - U_ASSERT(getMinimum(UCAL_DST_OFFSET) <= fFields[UCAL_DST_OFFSET]); - U_ASSERT(fFields[UCAL_DST_OFFSET] <= getMaximum(UCAL_DST_OFFSET)); -} - -uint8_t Calendar::julianDayToDayOfWeek(double julian) -{ - // If julian is negative, then julian%7 will be negative, so we adjust - // accordingly. We add 1 because Julian day 0 is Monday. - int8_t dayOfWeek = (int8_t) uprv_fmod(julian + 1, 7); - - uint8_t result = (uint8_t)(dayOfWeek + ((dayOfWeek < 0) ? (7+UCAL_SUNDAY ) : UCAL_SUNDAY)); - return result; -} - -/** -* Compute the Gregorian calendar year, month, and day of month from -* the given Julian day. These values are not stored in fields, but in -* member variables gregorianXxx. Also compute the DAY_OF_WEEK and -* DOW_LOCAL fields. -*/ -void Calendar::computeGregorianAndDOWFields(int32_t julianDay, UErrorCode &ec) -{ - computeGregorianFields(julianDay, ec); - if (U_FAILURE(ec)) { - return; - } - - // Compute day of week: JD 0 = Monday - int32_t dow = julianDayToDayOfWeek(julianDay); - internalSet(UCAL_DAY_OF_WEEK,dow); - - // Calculate 1-based localized day of week - int32_t dowLocal = dow - getFirstDayOfWeek() + 1; - if (dowLocal < 1) { - dowLocal += 7; - } - internalSet(UCAL_DOW_LOCAL,dowLocal); - fFields[UCAL_DOW_LOCAL] = dowLocal; -} - -/** -* Compute the Gregorian calendar year, month, and day of month from the -* Julian day. These values are not stored in fields, but in member -* variables gregorianXxx. They are used for time zone computations and by -* subclasses that are Gregorian derivatives. Subclasses may call this -* method to perform a Gregorian calendar millis->fields computation. -*/ -void Calendar::computeGregorianFields(int32_t julianDay, UErrorCode& ec) { - if (U_FAILURE(ec)) { - return; - } - int32_t gregorianDayOfWeekUnused; - Grego::dayToFields(julianDay - kEpochStartAsJulianDay, fGregorianYear, fGregorianMonth, fGregorianDayOfMonth, gregorianDayOfWeekUnused, fGregorianDayOfYear); -} - -/** -* Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH, -* DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL from EXTENDED_YEAR, YEAR, -* DAY_OF_WEEK, and DAY_OF_YEAR. The latter fields are computed by the -* subclass based on the calendar system. -* -*

The YEAR_WOY field is computed simplistically. It is equal to YEAR -* most of the time, but at the year boundary it may be adjusted to YEAR-1 -* or YEAR+1 to reflect the overlap of a week into an adjacent year. In -* this case, a simple increment or decrement is performed on YEAR, even -* though this may yield an invalid YEAR value. For instance, if the YEAR -* is part of a calendar system with an N-year cycle field CYCLE, then -* incrementing the YEAR may involve incrementing CYCLE and setting YEAR -* back to 0 or 1. This is not handled by this code, and in fact cannot be -* simply handled without having subclasses define an entire parallel set of -* fields for fields larger than or equal to a year. This additional -* complexity is not warranted, since the intention of the YEAR_WOY field is -* to support ISO 8601 notation, so it will typically be used with a -* proleptic Gregorian calendar, which has no field larger than a year. -*/ -void Calendar::computeWeekFields(UErrorCode &ec) { - if(U_FAILURE(ec)) { - return; - } - int32_t eyear = fFields[UCAL_EXTENDED_YEAR]; - int32_t dayOfWeek = fFields[UCAL_DAY_OF_WEEK]; - int32_t dayOfYear = fFields[UCAL_DAY_OF_YEAR]; - - // WEEK_OF_YEAR start - // Compute the week of the year. For the Gregorian calendar, valid week - // numbers run from 1 to 52 or 53, depending on the year, the first day - // of the week, and the minimal days in the first week. For other - // calendars, the valid range may be different -- it depends on the year - // length. Days at the start of the year may fall into the last week of - // the previous year; days at the end of the year may fall into the - // first week of the next year. ASSUME that the year length is less than - // 7000 days. - int32_t yearOfWeekOfYear = eyear; - int32_t relDow = (dayOfWeek + 7 - getFirstDayOfWeek()) % 7; // 0..6 - int32_t relDowJan1 = (dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek()) % 7; // 0..6 - int32_t woy = (dayOfYear - 1 + relDowJan1) / 7; // 0..53 - if ((7 - relDowJan1) >= getMinimalDaysInFirstWeek()) { - ++woy; - } - - // Adjust for weeks at the year end that overlap into the previous or - // next calendar year. - if (woy == 0) { - // We are the last week of the previous year. - // Check to see if we are in the last week; if so, we need - // to handle the case in which we are the first week of the - // next year. - - int32_t prevDoy = dayOfYear + handleGetYearLength(eyear - 1); - woy = weekNumber(prevDoy, dayOfWeek); - yearOfWeekOfYear--; - } else { - int32_t lastDoy = handleGetYearLength(eyear); - // Fast check: For it to be week 1 of the next year, the DOY - // must be on or after L-5, where L is yearLength(), then it - // cannot possibly be week 1 of the next year: - // L-5 L - // doy: 359 360 361 362 363 364 365 001 - // dow: 1 2 3 4 5 6 7 - if (dayOfYear >= (lastDoy - 5)) { - int32_t lastRelDow = (relDow + lastDoy - dayOfYear) % 7; - if (lastRelDow < 0) { - lastRelDow += 7; - } - if (((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) && - ((dayOfYear + 7 - relDow) > lastDoy)) { - woy = 1; - yearOfWeekOfYear++; - } - } - } - fFields[UCAL_WEEK_OF_YEAR] = woy; - fFields[UCAL_YEAR_WOY] = yearOfWeekOfYear; - // min/max of years are not constrains for caller, so not assert here. - // WEEK_OF_YEAR end - - int32_t dayOfMonth = fFields[UCAL_DAY_OF_MONTH]; - fFields[UCAL_WEEK_OF_MONTH] = weekNumber(dayOfMonth, dayOfWeek); - U_ASSERT(getMinimum(UCAL_WEEK_OF_MONTH) <= fFields[UCAL_WEEK_OF_MONTH]); - U_ASSERT(fFields[UCAL_WEEK_OF_MONTH] <= getMaximum(UCAL_WEEK_OF_MONTH)); - - fFields[UCAL_DAY_OF_WEEK_IN_MONTH] = (dayOfMonth-1) / 7 + 1; - U_ASSERT(getMinimum(UCAL_DAY_OF_WEEK_IN_MONTH) <= - fFields[UCAL_DAY_OF_WEEK_IN_MONTH]); - U_ASSERT(fFields[UCAL_DAY_OF_WEEK_IN_MONTH] <= - getMaximum(UCAL_DAY_OF_WEEK_IN_MONTH)); - -#if defined (U_DEBUG_CAL) - if(fFields[UCAL_DAY_OF_WEEK_IN_MONTH]==0) fprintf(stderr, "%s:%d: DOWIM %d on %g\n", - __FILE__, __LINE__,fFields[UCAL_DAY_OF_WEEK_IN_MONTH], fTime); -#endif -} - - -int32_t Calendar::weekNumber(int32_t desiredDay, int32_t dayOfPeriod, int32_t dayOfWeek) -{ - // Determine the day of the week of the first day of the period - // in question (either a year or a month). Zero represents the - // first day of the week on this calendar. - int32_t periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek() - dayOfPeriod + 1) % 7; - if (periodStartDayOfWeek < 0) periodStartDayOfWeek += 7; - - // Compute the week number. Initially, ignore the first week, which - // may be fractional (or may not be). We add periodStartDayOfWeek in - // order to fill out the first week, if it is fractional. - int32_t weekNo = (desiredDay + periodStartDayOfWeek - 1)/7; - - // If the first week is long enough, then count it. If - // the minimal days in the first week is one, or if the period start - // is zero, we always increment weekNo. - if ((7 - periodStartDayOfWeek) >= getMinimalDaysInFirstWeek()) ++weekNo; - - return weekNo; -} - -void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode& status) -{ - if (U_FAILURE(status)) { - return; - } - internalSet(UCAL_MONTH, getGregorianMonth()); - internalSet(UCAL_DAY_OF_MONTH, getGregorianDayOfMonth()); - internalSet(UCAL_DAY_OF_YEAR, getGregorianDayOfYear()); - int32_t eyear = getGregorianYear(); - internalSet(UCAL_EXTENDED_YEAR, eyear); - int32_t era = GregorianCalendar::AD; - if (eyear < 1) { - era = GregorianCalendar::BC; - eyear = 1 - eyear; - } - internalSet(UCAL_ERA, era); - internalSet(UCAL_YEAR, eyear); -} -// ------------------------------------- - - -void Calendar::roll(EDateFields field, int32_t amount, UErrorCode& status) -{ - roll((UCalendarDateFields)field, amount, status); -} - -void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) -{ - if (amount == 0) { - return; // Nothing to do - } - - complete(status); - - if(U_FAILURE(status)) { - return; - } - switch (field) { - case UCAL_DAY_OF_MONTH: - case UCAL_AM_PM: - case UCAL_MINUTE: - case UCAL_SECOND: - case UCAL_MILLISECOND: - case UCAL_MILLISECONDS_IN_DAY: - case UCAL_ERA: - // These are the standard roll instructions. These work for all - // simple cases, that is, cases in which the limits are fixed, such - // as the hour, the day of the month, and the era. - { - int32_t min = getActualMinimum(field,status); - int32_t max = getActualMaximum(field,status); - int32_t gap = max - min + 1; - - int32_t value = internalGet(field) + amount; - value = (value - min) % gap; - if (value < 0) { - value += gap; - } - value += min; - - set(field, value); - return; - } - - case UCAL_HOUR: - case UCAL_HOUR_OF_DAY: - // Rolling the hour is difficult on the ONSET and CEASE days of - // daylight savings. For example, if the change occurs at - // 2 AM, we have the following progression: - // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst - // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std - // To get around this problem we don't use fields; we manipulate - // the time in millis directly. - { - // Assume min == 0 in calculations below - double start = getTimeInMillis(status); - int32_t oldHour = internalGet(field); - int32_t max = getMaximum(field); - int32_t newHour = (oldHour + amount) % (max + 1); - if (newHour < 0) { - newHour += max + 1; - } - setTimeInMillis(start + kOneHour * (newHour - oldHour),status); - return; - } - - case UCAL_MONTH: - // Rolling the month involves both pinning the final value - // and adjusting the DAY_OF_MONTH if necessary. We only adjust the - // DAY_OF_MONTH if, after updating the MONTH field, it is illegal. - // E.g., .roll(MONTH, 1) -> or . - { - int32_t max = getActualMaximum(UCAL_MONTH, status); - int32_t mon = (internalGet(UCAL_MONTH) + amount) % (max+1); - - if (mon < 0) { - mon += (max + 1); - } - set(UCAL_MONTH, mon); - - // Keep the day of month in range. We don't want to spill over - // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 -> - // mar3. - pinField(UCAL_DAY_OF_MONTH,status); - return; - } - - case UCAL_YEAR: - case UCAL_YEAR_WOY: - { - // * If era==0 and years go backwards in time, change sign of amount. - // * Until we have new API per #9393, we temporarily hardcode knowledge of - // which calendars have era 0 years that go backwards. - UBool era0WithYearsThatGoBackwards = false; - int32_t era = get(UCAL_ERA, status); - if (era == 0) { - const char * calType = getType(); - if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) { - amount = -amount; - era0WithYearsThatGoBackwards = true; - } - } - int32_t newYear = internalGet(field) + amount; - if (era > 0 || newYear >= 1) { - int32_t maxYear = getActualMaximum(field, status); - if (maxYear < 32768) { - // this era has real bounds, roll should wrap years - if (newYear < 1) { - newYear = maxYear - ((-newYear) % maxYear); - } else if (newYear > maxYear) { - newYear = ((newYear - 1) % maxYear) + 1; - } - // else era is unbounded, just pin low year instead of wrapping - } else if (newYear < 1) { - newYear = 1; - } - // else we are in era 0 with newYear < 1; - // calendars with years that go backwards must pin the year value at 0, - // other calendars can have years < 0 in era 0 - } else if (era0WithYearsThatGoBackwards) { - newYear = 1; - } - set(field, newYear); - pinField(UCAL_MONTH,status); - pinField(UCAL_DAY_OF_MONTH,status); - return; - } - - case UCAL_EXTENDED_YEAR: - // Rolling the year can involve pinning the DAY_OF_MONTH. - set(field, internalGet(field) + amount); - pinField(UCAL_MONTH,status); - pinField(UCAL_DAY_OF_MONTH,status); - return; - - case UCAL_WEEK_OF_MONTH: - { - // This is tricky, because during the roll we may have to shift - // to a different day of the week. For example: - - // s m t w r f s - // 1 2 3 4 5 - // 6 7 8 9 10 11 12 - - // When rolling from the 6th or 7th back one week, we go to the - // 1st (assuming that the first partial week counts). The same - // thing happens at the end of the month. - - // The other tricky thing is that we have to figure out whether - // the first partial week actually counts or not, based on the - // minimal first days in the week. And we have to use the - // correct first day of the week to delineate the week - // boundaries. - - // Here's our algorithm. First, we find the real boundaries of - // the month. Then we discard the first partial week if it - // doesn't count in this locale. Then we fill in the ends with - // phantom days, so that the first partial week and the last - // partial week are full weeks. We then have a nice square - // block of weeks. We do the usual rolling within this block, - // as is done elsewhere in this method. If we wind up on one of - // the phantom days that we added, we recognize this and pin to - // the first or the last day of the month. Easy, eh? - - // Normalize the DAY_OF_WEEK so that 0 is the first day of the week - // in this locale. We have dow in 0..6. - int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); - if (dow < 0) dow += 7; - - // Find the day of the week (normalized for locale) for the first - // of the month. - int32_t fdm = (dow - internalGet(UCAL_DAY_OF_MONTH) + 1) % 7; - if (fdm < 0) fdm += 7; - - // Get the first day of the first full week of the month, - // including phantom days, if any. Figure out if the first week - // counts or not; if it counts, then fill in phantom days. If - // not, advance to the first real full week (skip the partial week). - int32_t start; - if ((7 - fdm) < getMinimalDaysInFirstWeek()) - start = 8 - fdm; // Skip the first partial week - else - start = 1 - fdm; // This may be zero or negative - - // Get the day of the week (normalized for locale) for the last - // day of the month. - int32_t monthLen = getActualMaximum(UCAL_DAY_OF_MONTH, status); - int32_t ldm = (monthLen - internalGet(UCAL_DAY_OF_MONTH) + dow) % 7; - // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here. - - // Get the limit day for the blocked-off rectangular month; that - // is, the day which is one past the last day of the month, - // after the month has already been filled in with phantom days - // to fill out the last week. This day has a normalized DOW of 0. - int32_t limit = monthLen + 7 - ldm; - - // Now roll between start and (limit - 1). - int32_t gap = limit - start; - int32_t day_of_month = (internalGet(UCAL_DAY_OF_MONTH) + amount*7 - - start) % gap; - if (day_of_month < 0) day_of_month += gap; - day_of_month += start; - - // Finally, pin to the real start and end of the month. - if (day_of_month < 1) day_of_month = 1; - if (day_of_month > monthLen) day_of_month = monthLen; - - // Set the DAY_OF_MONTH. We rely on the fact that this field - // takes precedence over everything else (since all other fields - // are also set at this point). If this fact changes (if the - // disambiguation algorithm changes) then we will have to unset - // the appropriate fields here so that DAY_OF_MONTH is attended - // to. - set(UCAL_DAY_OF_MONTH, day_of_month); - return; - } - case UCAL_WEEK_OF_YEAR: - { - // This follows the outline of WEEK_OF_MONTH, except it applies - // to the whole year. Please see the comment for WEEK_OF_MONTH - // for general notes. - - // Normalize the DAY_OF_WEEK so that 0 is the first day of the week - // in this locale. We have dow in 0..6. - int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); - if (dow < 0) dow += 7; - - // Find the day of the week (normalized for locale) for the first - // of the year. - int32_t fdy = (dow - internalGet(UCAL_DAY_OF_YEAR) + 1) % 7; - if (fdy < 0) fdy += 7; - - // Get the first day of the first full week of the year, - // including phantom days, if any. Figure out if the first week - // counts or not; if it counts, then fill in phantom days. If - // not, advance to the first real full week (skip the partial week). - int32_t start; - if ((7 - fdy) < getMinimalDaysInFirstWeek()) - start = 8 - fdy; // Skip the first partial week - else - start = 1 - fdy; // This may be zero or negative - - // Get the day of the week (normalized for locale) for the last - // day of the year. - int32_t yearLen = getActualMaximum(UCAL_DAY_OF_YEAR,status); - int32_t ldy = (yearLen - internalGet(UCAL_DAY_OF_YEAR) + dow) % 7; - // We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here. - - // Get the limit day for the blocked-off rectangular year; that - // is, the day which is one past the last day of the year, - // after the year has already been filled in with phantom days - // to fill out the last week. This day has a normalized DOW of 0. - int32_t limit = yearLen + 7 - ldy; - - // Now roll between start and (limit - 1). - int32_t gap = limit - start; - int32_t day_of_year = (internalGet(UCAL_DAY_OF_YEAR) + amount*7 - - start) % gap; - if (day_of_year < 0) day_of_year += gap; - day_of_year += start; - - // Finally, pin to the real start and end of the month. - if (day_of_year < 1) day_of_year = 1; - if (day_of_year > yearLen) day_of_year = yearLen; - - // Make sure that the year and day of year are attended to by - // clearing other fields which would normally take precedence. - // If the disambiguation algorithm is changed, this section will - // have to be updated as well. - set(UCAL_DAY_OF_YEAR, day_of_year); - clear(UCAL_MONTH); - return; - } - case UCAL_DAY_OF_YEAR: - { - // Roll the day of year using millis. Compute the millis for - // the start of the year, and get the length of the year. - double delta = amount * kOneDay; // Scale up from days to millis - double min2 = internalGet(UCAL_DAY_OF_YEAR)-1; - min2 *= kOneDay; - min2 = internalGetTime() - min2; - - // double min2 = internalGetTime() - (internalGet(UCAL_DAY_OF_YEAR) - 1.0) * kOneDay; - double newtime; - - double yearLength = getActualMaximum(UCAL_DAY_OF_YEAR,status); - double oneYear = yearLength; - oneYear *= kOneDay; - newtime = uprv_fmod((internalGetTime() + delta - min2), oneYear); - if (newtime < 0) newtime += oneYear; - setTimeInMillis(newtime + min2, status); - return; - } - case UCAL_DAY_OF_WEEK: - case UCAL_DOW_LOCAL: - { - // Roll the day of week using millis. Compute the millis for - // the start of the week, using the first day of week setting. - // Restrict the millis to [start, start+7days). - double delta = amount * kOneDay; // Scale up from days to millis - // Compute the number of days before the current day in this - // week. This will be a value 0..6. - int32_t leadDays = internalGet(field); - leadDays -= (field == UCAL_DAY_OF_WEEK) ? getFirstDayOfWeek() : 1; - if (leadDays < 0) leadDays += 7; - double min2 = internalGetTime() - leadDays * kOneDay; - double newtime = uprv_fmod((internalGetTime() + delta - min2), kOneWeek); - if (newtime < 0) newtime += kOneWeek; - setTimeInMillis(newtime + min2, status); - return; - } - case UCAL_DAY_OF_WEEK_IN_MONTH: - { - // Roll the day of week in the month using millis. Determine - // the first day of the week in the month, and then the last, - // and then roll within that range. - double delta = amount * kOneWeek; // Scale up from weeks to millis - // Find the number of same days of the week before this one - // in this month. - int32_t preWeeks = (internalGet(UCAL_DAY_OF_MONTH) - 1) / 7; - // Find the number of same days of the week after this one - // in this month. - int32_t postWeeks = (getActualMaximum(UCAL_DAY_OF_MONTH,status) - - internalGet(UCAL_DAY_OF_MONTH)) / 7; - // From these compute the min and gap millis for rolling. - double min2 = internalGetTime() - preWeeks * kOneWeek; - double gap2 = kOneWeek * (preWeeks + postWeeks + 1); // Must add 1! - // Roll within this range - double newtime = uprv_fmod((internalGetTime() + delta - min2), gap2); - if (newtime < 0) newtime += gap2; - setTimeInMillis(newtime + min2, status); - return; - } - case UCAL_JULIAN_DAY: - set(field, internalGet(field) + amount); - return; - default: - // Other fields cannot be rolled by this method -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: ILLEGAL ARG because of roll on non-rollable field %s\n", - __FILE__, __LINE__,fldName(field)); -#endif - status = U_ILLEGAL_ARGUMENT_ERROR; - } -} - -void Calendar::add(EDateFields field, int32_t amount, UErrorCode& status) -{ - Calendar::add((UCalendarDateFields)field, amount, status); -} - -// ------------------------------------- -void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) -{ - if (U_FAILURE(status)) { - return; - } - if (amount == 0) { - return; // Do nothing! - } - - // We handle most fields in the same way. The algorithm is to add - // a computed amount of millis to the current millis. The only - // wrinkle is with DST (and/or a change to the zone's UTC offset, which - // we'll include with DST) -- for some fields, like the DAY_OF_MONTH, - // we don't want the wall time to shift due to changes in DST. If the - // result of the add operation is to move from DST to Standard, or - // vice versa, we need to adjust by an hour forward or back, - // respectively. For such fields we set keepWallTimeInvariant to true. - - // We only adjust the DST for fields larger than an hour. For - // fields smaller than an hour, we cannot adjust for DST without - // causing problems. for instance, if you add one hour to April 5, - // 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an - // illegal value), but then the adjustment sees the change and - // compensates by subtracting an hour. As a result the time - // doesn't advance at all. - - // For some fields larger than a day, such as a UCAL_MONTH, we pin the - // UCAL_DAY_OF_MONTH. This allows .add(UCAL_MONTH, 1) to be - // , rather than => . - - double delta = amount; // delta in ms - UBool keepWallTimeInvariant = true; - - switch (field) { - case UCAL_ERA: - set(field, get(field, status) + amount); - pinField(UCAL_ERA, status); - return; - - case UCAL_YEAR: - case UCAL_YEAR_WOY: - { - // * If era=0 and years go backwards in time, change sign of amount. - // * Until we have new API per #9393, we temporarily hardcode knowledge of - // which calendars have era 0 years that go backwards. - // * Note that for UCAL_YEAR (but not UCAL_YEAR_WOY) we could instead handle - // this by applying the amount to the UCAL_EXTENDED_YEAR field; but since - // we would still need to handle UCAL_YEAR_WOY as below, might as well - // also handle UCAL_YEAR the same way. - int32_t era = get(UCAL_ERA, status); - if (era == 0) { - const char * calType = getType(); - if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) { - amount = -amount; - } - } - } - // Fall through into normal handling - U_FALLTHROUGH; - case UCAL_EXTENDED_YEAR: - case UCAL_MONTH: - { - UBool oldLenient = isLenient(); - setLenient(true); - set(field, get(field, status) + amount); - pinField(UCAL_DAY_OF_MONTH, status); - if(oldLenient==false) { - complete(status); /* force recalculate */ - setLenient(oldLenient); - } - } - return; - - case UCAL_WEEK_OF_YEAR: - case UCAL_WEEK_OF_MONTH: - case UCAL_DAY_OF_WEEK_IN_MONTH: - delta *= kOneWeek; - break; - - case UCAL_AM_PM: - delta *= 12 * kOneHour; - break; - - case UCAL_DAY_OF_MONTH: - case UCAL_DAY_OF_YEAR: - case UCAL_DAY_OF_WEEK: - case UCAL_DOW_LOCAL: - case UCAL_JULIAN_DAY: - delta *= kOneDay; - break; - - case UCAL_HOUR_OF_DAY: - case UCAL_HOUR: - delta *= kOneHour; - keepWallTimeInvariant = false; - break; - - case UCAL_MINUTE: - delta *= kOneMinute; - keepWallTimeInvariant = false; - break; - - case UCAL_SECOND: - delta *= kOneSecond; - keepWallTimeInvariant = false; - break; - - case UCAL_MILLISECOND: - case UCAL_MILLISECONDS_IN_DAY: - keepWallTimeInvariant = false; - break; - - default: -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s not addable", - __FILE__, __LINE__, fldName(field)); -#endif - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - // throw new IllegalArgumentException("Calendar.add(" + fieldName(field) + - // ") not supported"); - } - - // In order to keep the wall time invariant (for fields where this is - // appropriate), check the combined DST & ZONE offset before and - // after the add() operation. If it changes, then adjust the millis - // to compensate. - int32_t prevOffset = 0; - int32_t prevWallTime = 0; - if (keepWallTimeInvariant) { - prevOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status); - prevWallTime = get(UCAL_MILLISECONDS_IN_DAY, status); - } - - setTimeInMillis(getTimeInMillis(status) + delta, status); - - if (keepWallTimeInvariant) { - int32_t newWallTime = get(UCAL_MILLISECONDS_IN_DAY, status); - if (newWallTime != prevWallTime) { - // There is at least one zone transition between the base - // time and the result time. As the result, wall time has - // changed. - UDate t = internalGetTime(); - int32_t newOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status); - if (newOffset != prevOffset) { - // When the difference of the previous UTC offset and - // the new UTC offset exceeds 1 full day, we do not want - // to roll over/back the date. For now, this only happens - // in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452. - int32_t adjAmount = prevOffset - newOffset; - adjAmount = adjAmount >= 0 ? adjAmount % (int32_t)kOneDay : -(-adjAmount % (int32_t)kOneDay); - if (adjAmount != 0) { - setTimeInMillis(t + adjAmount, status); - newWallTime = get(UCAL_MILLISECONDS_IN_DAY, status); - } - if (newWallTime != prevWallTime) { - // The result wall time or adjusted wall time was shifted because - // the target wall time does not exist on the result date. - switch (fSkippedWallTime) { - case UCAL_WALLTIME_FIRST: - if (adjAmount > 0) { - setTimeInMillis(t, status); - } - break; - case UCAL_WALLTIME_LAST: - if (adjAmount < 0) { - setTimeInMillis(t, status); - } - break; - case UCAL_WALLTIME_NEXT_VALID: - UDate tmpT = adjAmount > 0 ? internalGetTime() : t; - UDate immediatePrevTrans; - UBool hasTransition = getImmediatePreviousZoneTransition(tmpT, &immediatePrevTrans, status); - if (U_SUCCESS(status) && hasTransition) { - setTimeInMillis(immediatePrevTrans, status); - } - break; - } - } - } - } - } -} - -// ------------------------------------- -int32_t Calendar::fieldDifference(UDate when, EDateFields field, UErrorCode& status) { - return fieldDifference(when, (UCalendarDateFields) field, status); -} - -int32_t Calendar::fieldDifference(UDate targetMs, UCalendarDateFields field, UErrorCode& ec) { - if (U_FAILURE(ec)) return 0; - int32_t min = 0; - double startMs = getTimeInMillis(ec); - // Always add from the start millis. This accommodates - // operations like adding years from February 29, 2000 up to - // February 29, 2004. If 1, 1, 1, 1 is added to the year - // field, the DOM gets pinned to 28 and stays there, giving an - // incorrect DOM difference of 1. We have to add 1, reset, 2, - // reset, 3, reset, 4. - if (startMs < targetMs) { - int32_t max = 1; - // Find a value that is too large - while (U_SUCCESS(ec)) { - setTimeInMillis(startMs, ec); - add(field, max, ec); - double ms = getTimeInMillis(ec); - if (ms == targetMs) { - return max; - } else if (ms > targetMs) { - break; - } else if (max < INT32_MAX) { - min = max; - max <<= 1; - if (max < 0) { - max = INT32_MAX; - } - } else { - // Field difference too large to fit into int32_t -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n", - __FILE__, __LINE__, fldName(field)); -#endif - ec = U_ILLEGAL_ARGUMENT_ERROR; - } - } - // Do a binary search - while ((max - min) > 1 && U_SUCCESS(ec)) { - int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX - setTimeInMillis(startMs, ec); - add(field, t, ec); - double ms = getTimeInMillis(ec); - if (ms == targetMs) { - return t; - } else if (ms > targetMs) { - max = t; - } else { - min = t; - } - } - } else if (startMs > targetMs) { - int32_t max = -1; - // Find a value that is too small - while (U_SUCCESS(ec)) { - setTimeInMillis(startMs, ec); - add(field, max, ec); - double ms = getTimeInMillis(ec); - if (ms == targetMs) { - return max; - } else if (ms < targetMs) { - break; - } else { - min = max; - max <<= 1; - if (max == 0) { - // Field difference too large to fit into int32_t -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n", - __FILE__, __LINE__, fldName(field)); -#endif - ec = U_ILLEGAL_ARGUMENT_ERROR; - } - } - } - // Do a binary search - while ((min - max) > 1 && U_SUCCESS(ec)) { - int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX - setTimeInMillis(startMs, ec); - add(field, t, ec); - double ms = getTimeInMillis(ec); - if (ms == targetMs) { - return t; - } else if (ms < targetMs) { - max = t; - } else { - min = t; - } - } - } - // Set calendar to end point - setTimeInMillis(startMs, ec); - add(field, min, ec); - - /* Test for buffer overflows */ - if(U_FAILURE(ec)) { - return 0; - } - return min; -} - -// ------------------------------------- - -void -Calendar::adoptTimeZone(TimeZone* zone) -{ - // Do nothing if passed-in zone is NULL - if (zone == NULL) return; - - // fZone should always be non-null - delete fZone; - fZone = zone; - - // if the zone changes, we need to recompute the time fields - fAreFieldsSet = false; -} - -// ------------------------------------- -void -Calendar::setTimeZone(const TimeZone& zone) -{ - adoptTimeZone(zone.clone()); -} - -// ------------------------------------- - -const TimeZone& -Calendar::getTimeZone() const -{ - U_ASSERT(fZone != NULL); - return *fZone; -} - -// ------------------------------------- - -TimeZone* -Calendar::orphanTimeZone() -{ - // we let go of the time zone; the new time zone is the system default time zone - TimeZone *defaultZone = TimeZone::createDefault(); - if (defaultZone == NULL) { - // No error handling available. Must keep fZone non-NULL, there are many unchecked uses. - return NULL; - } - TimeZone *z = fZone; - fZone = defaultZone; - return z; -} - -// ------------------------------------- - -void -Calendar::setLenient(UBool lenient) -{ - fLenient = lenient; -} - -// ------------------------------------- - -UBool -Calendar::isLenient() const -{ - return fLenient; -} - -// ------------------------------------- - -void -Calendar::setRepeatedWallTimeOption(UCalendarWallTimeOption option) -{ - if (option == UCAL_WALLTIME_LAST || option == UCAL_WALLTIME_FIRST) { - fRepeatedWallTime = option; - } -} - -// ------------------------------------- - -UCalendarWallTimeOption -Calendar::getRepeatedWallTimeOption(void) const -{ - return fRepeatedWallTime; -} - -// ------------------------------------- - -void -Calendar::setSkippedWallTimeOption(UCalendarWallTimeOption option) -{ - fSkippedWallTime = option; -} - -// ------------------------------------- - -UCalendarWallTimeOption -Calendar::getSkippedWallTimeOption(void) const -{ - return fSkippedWallTime; -} - -// ------------------------------------- - -void -Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value) -{ - if (fFirstDayOfWeek != value && - value >= UCAL_SUNDAY && value <= UCAL_SATURDAY) { - fFirstDayOfWeek = value; - fAreFieldsSet = false; - } -} - -// ------------------------------------- - -Calendar::EDaysOfWeek -Calendar::getFirstDayOfWeek() const -{ - return (Calendar::EDaysOfWeek)fFirstDayOfWeek; -} - -UCalendarDaysOfWeek -Calendar::getFirstDayOfWeek(UErrorCode & /*status*/) const -{ - return fFirstDayOfWeek; -} -// ------------------------------------- - -void -Calendar::setMinimalDaysInFirstWeek(uint8_t value) -{ - // Values less than 1 have the same effect as 1; values greater - // than 7 have the same effect as 7. However, we normalize values - // so operator== and so forth work. - if (value < 1) { - value = 1; - } else if (value > 7) { - value = 7; - } - if (fMinimalDaysInFirstWeek != value) { - fMinimalDaysInFirstWeek = value; - fAreFieldsSet = false; - } -} - -// ------------------------------------- - -uint8_t -Calendar::getMinimalDaysInFirstWeek() const -{ - return fMinimalDaysInFirstWeek; -} - -// ------------------------------------- -// weekend functions, just dummy implementations for now (for API freeze) - -UCalendarWeekdayType -Calendar::getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const -{ - if (U_FAILURE(status)) { - return UCAL_WEEKDAY; - } - if (dayOfWeek < UCAL_SUNDAY || dayOfWeek > UCAL_SATURDAY) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return UCAL_WEEKDAY; - } - if (fWeekendOnset == fWeekendCease) { - if (dayOfWeek != fWeekendOnset) - return UCAL_WEEKDAY; - return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET; - } - if (fWeekendOnset < fWeekendCease) { - if (dayOfWeek < fWeekendOnset || dayOfWeek > fWeekendCease) { - return UCAL_WEEKDAY; - } - } else { - if (dayOfWeek > fWeekendCease && dayOfWeek < fWeekendOnset) { - return UCAL_WEEKDAY; - } - } - if (dayOfWeek == fWeekendOnset) { - return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET; - } - if (dayOfWeek == fWeekendCease) { - return (fWeekendCeaseMillis >= 86400000) ? UCAL_WEEKEND : UCAL_WEEKEND_CEASE; - } - return UCAL_WEEKEND; -} - -int32_t -Calendar::getWeekendTransition(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const -{ - if (U_FAILURE(status)) { - return 0; - } - if (dayOfWeek == fWeekendOnset) { - return fWeekendOnsetMillis; - } else if (dayOfWeek == fWeekendCease) { - return fWeekendCeaseMillis; - } - status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; -} - -UBool -Calendar::isWeekend(UDate date, UErrorCode &status) const -{ - if (U_FAILURE(status)) { - return false; - } - // clone the calendar so we don't mess with the real one. - Calendar *work = this->clone(); - if (work == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return false; - } - UBool result = false; - work->setTime(date, status); - if (U_SUCCESS(status)) { - result = work->isWeekend(); - } - delete work; - return result; -} - -UBool -Calendar::isWeekend(void) const -{ - UErrorCode status = U_ZERO_ERROR; - UCalendarDaysOfWeek dayOfWeek = (UCalendarDaysOfWeek)get(UCAL_DAY_OF_WEEK, status); - UCalendarWeekdayType dayType = getDayOfWeekType(dayOfWeek, status); - if (U_SUCCESS(status)) { - switch (dayType) { - case UCAL_WEEKDAY: - return false; - case UCAL_WEEKEND: - return true; - case UCAL_WEEKEND_ONSET: - case UCAL_WEEKEND_CEASE: - // Use internalGet() because the above call to get() populated all fields. - { - int32_t millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY); - int32_t transitionMillis = getWeekendTransition(dayOfWeek, status); - if (U_SUCCESS(status)) { - return (dayType == UCAL_WEEKEND_ONSET)? - (millisInDay >= transitionMillis): - (millisInDay < transitionMillis); - } - // else fall through, return false - U_FALLTHROUGH; - } - default: - break; - } - } - return false; -} - -// ------------------------------------- limits - -int32_t -Calendar::getMinimum(EDateFields field) const { - return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MINIMUM); -} - -int32_t -Calendar::getMinimum(UCalendarDateFields field) const -{ - return getLimit(field,UCAL_LIMIT_MINIMUM); -} - -// ------------------------------------- -int32_t -Calendar::getMaximum(EDateFields field) const -{ - return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MAXIMUM); -} - -int32_t -Calendar::getMaximum(UCalendarDateFields field) const -{ - return getLimit(field,UCAL_LIMIT_MAXIMUM); -} - -// ------------------------------------- -int32_t -Calendar::getGreatestMinimum(EDateFields field) const -{ - return getLimit((UCalendarDateFields)field,UCAL_LIMIT_GREATEST_MINIMUM); -} - -int32_t -Calendar::getGreatestMinimum(UCalendarDateFields field) const -{ - return getLimit(field,UCAL_LIMIT_GREATEST_MINIMUM); -} - -// ------------------------------------- -int32_t -Calendar::getLeastMaximum(EDateFields field) const -{ - return getLimit((UCalendarDateFields) field,UCAL_LIMIT_LEAST_MAXIMUM); -} - -int32_t -Calendar::getLeastMaximum(UCalendarDateFields field) const -{ - return getLimit( field,UCAL_LIMIT_LEAST_MAXIMUM); -} - -// ------------------------------------- -int32_t -Calendar::getActualMinimum(EDateFields field, UErrorCode& status) const -{ - return getActualMinimum((UCalendarDateFields) field, status); -} - -int32_t Calendar::getLimit(UCalendarDateFields field, ELimitType limitType) const { - switch (field) { - case UCAL_DAY_OF_WEEK: - case UCAL_AM_PM: - case UCAL_HOUR: - case UCAL_HOUR_OF_DAY: - case UCAL_MINUTE: - case UCAL_SECOND: - case UCAL_MILLISECOND: - case UCAL_ZONE_OFFSET: - case UCAL_DST_OFFSET: - case UCAL_DOW_LOCAL: - case UCAL_JULIAN_DAY: - case UCAL_MILLISECONDS_IN_DAY: - case UCAL_IS_LEAP_MONTH: - return kCalendarLimits[field][limitType]; - - case UCAL_WEEK_OF_MONTH: - { - int32_t limit; - if (limitType == UCAL_LIMIT_MINIMUM) { - limit = getMinimalDaysInFirstWeek() == 1 ? 1 : 0; - } else if (limitType == UCAL_LIMIT_GREATEST_MINIMUM) { - limit = 1; - } else { - int32_t minDaysInFirst = getMinimalDaysInFirstWeek(); - int32_t daysInMonth = handleGetLimit(UCAL_DAY_OF_MONTH, limitType); - if (limitType == UCAL_LIMIT_LEAST_MAXIMUM) { - limit = (daysInMonth + (7 - minDaysInFirst)) / 7; - } else { // limitType == UCAL_LIMIT_MAXIMUM - limit = (daysInMonth + 6 + (7 - minDaysInFirst)) / 7; - } - } - return limit; - } - default: - return handleGetLimit(field, limitType); - } -} - - -int32_t -Calendar::getActualMinimum(UCalendarDateFields field, UErrorCode& status) const -{ - if (U_FAILURE(status)) { - return 0; - } - int32_t fieldValue = getGreatestMinimum(field); - int32_t endValue = getMinimum(field); - - // if we know that the minimum value is always the same, just return it - if (fieldValue == endValue) { - return fieldValue; - } - - // clone the calendar so we don't mess with the real one, and set it to - // accept anything for the field values - Calendar *work = this->clone(); - if (work == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - work->setLenient(true); - - // now try each value from getLeastMaximum() to getMaximum() one by one until - // we get a value that normalizes to another value. The last value that - // normalizes to itself is the actual minimum for the current date - int32_t result = fieldValue; - - do { - work->set(field, fieldValue); - if (work->get(field, status) != fieldValue) { - break; - } - else { - result = fieldValue; - fieldValue--; - } - } while (fieldValue >= endValue); - - delete work; - - /* Test for buffer overflows */ - if(U_FAILURE(status)) { - return 0; - } - return result; -} - -// ------------------------------------- - - - -/** -* Ensure that each field is within its valid range by calling {@link -* #validateField(int)} on each field that has been set. This method -* should only be called if this calendar is not lenient. -* @see #isLenient -* @see #validateField(int) -*/ -void Calendar::validateFields(UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - for (int32_t field = 0; U_SUCCESS(status) && (field < UCAL_FIELD_COUNT); field++) { - if (fStamp[field] >= kMinimumUserStamp) { - validateField((UCalendarDateFields)field, status); - } - } -} - -/** -* Validate a single field of this calendar. Subclasses should -* override this method to validate any calendar-specific fields. -* Generic fields can be handled by -* Calendar.validateField(). -* @see #validateField(int, int, int) -*/ -void Calendar::validateField(UCalendarDateFields field, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - int32_t y; - switch (field) { - case UCAL_DAY_OF_MONTH: - y = handleGetExtendedYear(); - validateField(field, 1, handleGetMonthLength(y, internalGet(UCAL_MONTH)), status); - break; - case UCAL_DAY_OF_YEAR: - y = handleGetExtendedYear(); - validateField(field, 1, handleGetYearLength(y), status); - break; - case UCAL_DAY_OF_WEEK_IN_MONTH: - if (internalGet(field) == 0) { -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: ILLEGAL ARG because DOW in month cannot be 0\n", - __FILE__, __LINE__); -#endif - status = U_ILLEGAL_ARGUMENT_ERROR; // "DAY_OF_WEEK_IN_MONTH cannot be zero" - return; - } - validateField(field, getMinimum(field), getMaximum(field), status); - break; - default: - validateField(field, getMinimum(field), getMaximum(field), status); - break; - } -} - -/** -* Validate a single field of this calendar given its minimum and -* maximum allowed value. If the field is out of range, throw a -* descriptive IllegalArgumentException. Subclasses may -* use this method in their implementation of {@link -* #validateField(int)}. -*/ -void Calendar::validateField(UCalendarDateFields field, int32_t min, int32_t max, UErrorCode& status) -{ - if (U_FAILURE(status)) { - return; - } - int32_t value = fFields[field]; - if (value < min || value > max) { -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: ILLEGAL ARG because of field %s out of range %d..%d at %d\n", - __FILE__, __LINE__,fldName(field),min,max,value); -#endif - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } -} - -// ------------------------- - -const UFieldResolutionTable* Calendar::getFieldResolutionTable() const { - return kDatePrecedence; -} - - -UCalendarDateFields Calendar::newerField(UCalendarDateFields defaultField, UCalendarDateFields alternateField) const -{ - if (fStamp[alternateField] > fStamp[defaultField]) { - return alternateField; - } - return defaultField; -} - -UCalendarDateFields Calendar::resolveFields(const UFieldResolutionTable* precedenceTable) { - int32_t bestField = UCAL_FIELD_COUNT; - int32_t tempBestField; - for (int32_t g=0; precedenceTable[g][0][0] != -1 && (bestField == UCAL_FIELD_COUNT); ++g) { - int32_t bestStamp = kUnset; - for (int32_t l=0; precedenceTable[g][l][0] != -1; ++l) { - int32_t lineStamp = kUnset; - // Skip over first entry if it is negative - for (int32_t i=((precedenceTable[g][l][0]>=kResolveRemap)?1:0); precedenceTable[g][l][i]!=-1; ++i) { - U_ASSERT(precedenceTable[g][l][i] < UCAL_FIELD_COUNT); - int32_t s = fStamp[precedenceTable[g][l][i]]; - // If any field is unset then don't use this line - if (s == kUnset) { - goto linesInGroup; - } else if(s > lineStamp) { - lineStamp = s; - } - } - // Record new maximum stamp & field no. - if (lineStamp > bestStamp) { - tempBestField = precedenceTable[g][l][0]; // First field refers to entire line - if (tempBestField >= kResolveRemap) { - tempBestField &= (kResolveRemap-1); - // This check is needed to resolve some issues with UCAL_YEAR precedence mapping - if (tempBestField != UCAL_DATE || (fStamp[UCAL_WEEK_OF_MONTH] < fStamp[tempBestField])) { - bestField = tempBestField; - } - } else { - bestField = tempBestField; - } - - if (bestField == tempBestField) { - bestStamp = lineStamp; - } - } -linesInGroup: - ; - } - } - return (UCalendarDateFields)bestField; -} - -const UFieldResolutionTable Calendar::kDatePrecedence[] = -{ - { - { UCAL_DAY_OF_MONTH, kResolveSTOP }, - { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP }, - { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, - { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, - { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP }, - { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, - { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, - { UCAL_DAY_OF_YEAR, kResolveSTOP }, - { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_YEAR, kResolveSTOP }, // if YEAR is set over YEAR_WOY use DAY_OF_MONTH - { kResolveRemap | UCAL_WEEK_OF_YEAR, UCAL_YEAR_WOY, kResolveSTOP }, // if YEAR_WOY is set, calc based on WEEK_OF_YEAR - { kResolveSTOP } - }, - { - { UCAL_WEEK_OF_YEAR, kResolveSTOP }, - { UCAL_WEEK_OF_MONTH, kResolveSTOP }, - { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP }, - { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, - { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, - { kResolveSTOP } - }, - {{kResolveSTOP}} -}; - - -const UFieldResolutionTable Calendar::kDOWPrecedence[] = -{ - { - { UCAL_DAY_OF_WEEK,kResolveSTOP, kResolveSTOP }, - { UCAL_DOW_LOCAL,kResolveSTOP, kResolveSTOP }, - {kResolveSTOP} - }, - {{kResolveSTOP}} -}; - -// precedence for calculating a year -const UFieldResolutionTable Calendar::kYearPrecedence[] = -{ - { - { UCAL_YEAR, kResolveSTOP }, - { UCAL_EXTENDED_YEAR, kResolveSTOP }, - { UCAL_YEAR_WOY, UCAL_WEEK_OF_YEAR, kResolveSTOP }, // YEAR_WOY is useless without WEEK_OF_YEAR - { kResolveSTOP } - }, - {{kResolveSTOP}} -}; - - -// ------------------------- - - -void Calendar::computeTime(UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - if (!isLenient()) { - validateFields(status); - if (U_FAILURE(status)) { - return; - } - } - - // Compute the Julian day - int32_t julianDay = computeJulianDay(); - - double millis = Grego::julianDayToMillis(julianDay); - -#if defined (U_DEBUG_CAL) - // int32_t julianInsanityCheck = (int32_t)ClockMath::floorDivide(millis, kOneDay); - // julianInsanityCheck += kEpochStartAsJulianDay; - // if(1 || julianInsanityCheck != julianDay) { - // fprintf(stderr, "%s:%d- D'oh- computed jules %d, to mills (%s)%.lf, recomputed %d\n", - // __FILE__, __LINE__, julianDay, millis<0.0?"NEG":"", millis, julianInsanityCheck); - // } -#endif - - double millisInDay; - - // We only use MILLISECONDS_IN_DAY if it has been set by the user. - // This makes it possible for the caller to set the calendar to a - // time and call clear(MONTH) to reset the MONTH to January. This - // is legacy behavior. Without this, clear(MONTH) has no effect, - // since the internally set JULIAN_DAY is used. - if (fStamp[UCAL_MILLISECONDS_IN_DAY] >= ((int32_t)kMinimumUserStamp) && - newestStamp(UCAL_AM_PM, UCAL_MILLISECOND, kUnset) <= fStamp[UCAL_MILLISECONDS_IN_DAY]) { - millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY); - } else { - millisInDay = computeMillisInDay(); - } - - UDate t = 0; - if (fStamp[UCAL_ZONE_OFFSET] >= ((int32_t)kMinimumUserStamp) || fStamp[UCAL_DST_OFFSET] >= ((int32_t)kMinimumUserStamp)) { - t = millis + millisInDay - (internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET)); - } else { - // Compute the time zone offset and DST offset. There are two potential - // ambiguities here. We'll assume a 2:00 am (wall time) switchover time - // for discussion purposes here. - // - // 1. The positive offset change such as transition into DST. - // Here, a designated time of 2:00 am - 2:59 am does not actually exist. - // For this case, skippedWallTime option specifies the behavior. - // For example, 2:30 am is interpreted as; - // - WALLTIME_LAST(default): 3:30 am (DST) (interpreting 2:30 am as 31 minutes after 1:59 am (STD)) - // - WALLTIME_FIRST: 1:30 am (STD) (interpreting 2:30 am as 30 minutes before 3:00 am (DST)) - // - WALLTIME_NEXT_VALID: 3:00 am (DST) (next valid time after 2:30 am on a wall clock) - // 2. The negative offset change such as transition out of DST. - // Here, a designated time of 1:00 am - 1:59 am can be in standard or DST. Both are valid - // representations (the rep jumps from 1:59:59 DST to 1:00:00 Std). - // For this case, repeatedWallTime option specifies the behavior. - // For example, 1:30 am is interpreted as; - // - WALLTIME_LAST(default): 1:30 am (STD) - latter occurrence - // - WALLTIME_FIRST: 1:30 am (DST) - former occurrence - // - // In addition to above, when calendar is strict (not default), wall time falls into - // the skipped time range will be processed as an error case. - // - // These special cases are mostly handled in #computeZoneOffset(long), except WALLTIME_NEXT_VALID - // at positive offset change. The protected method computeZoneOffset(long) is exposed to Calendar - // subclass implementations and marked as @stable. Strictly speaking, WALLTIME_NEXT_VALID - // should be also handled in the same place, but we cannot change the code flow without deprecating - // the protected method. - // - // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET - // or DST_OFFSET fields; then we use those fields. - - if (!isLenient() || fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID) { - // When strict, invalidate a wall time falls into a skipped wall time range. - // When lenient and skipped wall time option is WALLTIME_NEXT_VALID, - // the result time will be adjusted to the next valid time (on wall clock). - int32_t zoneOffset = computeZoneOffset(millis, millisInDay, status); - UDate tmpTime = millis + millisInDay - zoneOffset; - - int32_t raw, dst; - fZone->getOffset(tmpTime, false, raw, dst, status); - - if (U_SUCCESS(status)) { - // zoneOffset != (raw + dst) only when the given wall time fall into - // a skipped wall time range caused by positive zone offset transition. - if (zoneOffset != (raw + dst)) { - if (!isLenient()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } else { - U_ASSERT(fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID); - // Adjust time to the next valid wall clock time. - // At this point, tmpTime is on or after the zone offset transition causing - // the skipped time range. - UDate immediatePrevTransition; - UBool hasTransition = getImmediatePreviousZoneTransition(tmpTime, &immediatePrevTransition, status); - if (U_SUCCESS(status) && hasTransition) { - t = immediatePrevTransition; - } - } - } else { - t = tmpTime; - } - } - } else { - t = millis + millisInDay - computeZoneOffset(millis, millisInDay, status); - } - } - if (U_SUCCESS(status)) { - internalSetTime(t); - } -} - -/** - * Find the previous zone transition near the given time. - */ -UBool Calendar::getImmediatePreviousZoneTransition(UDate base, UDate *transitionTime, UErrorCode& status) const { - if (U_FAILURE(status)) { - return false; - } - BasicTimeZone *btz = getBasicTimeZone(); - if (btz) { - TimeZoneTransition trans; - UBool hasTransition = btz->getPreviousTransition(base, true, trans); - if (hasTransition) { - *transitionTime = trans.getTime(); - return true; - } else { - // Could not find any transitions. - // Note: This should never happen. - status = U_INTERNAL_PROGRAM_ERROR; - } - } else { - // If not BasicTimeZone, return unsupported error for now. - // TODO: We may support non-BasicTimeZone in future. - status = U_UNSUPPORTED_ERROR; - } - return false; -} - -/** -* Compute the milliseconds in the day from the fields. This is a -* value from 0 to 23:59:59.999 inclusive, unless fields are out of -* range, in which case it can be an arbitrary value. This value -* reflects local zone wall time. -* @stable ICU 2.0 -*/ -double Calendar::computeMillisInDay() { - // Do the time portion of the conversion. - - double millisInDay = 0; - - // Find the best set of fields specifying the time of day. There - // are only two possibilities here; the HOUR_OF_DAY or the - // AM_PM and the HOUR. - int32_t hourOfDayStamp = fStamp[UCAL_HOUR_OF_DAY]; - int32_t hourStamp = (fStamp[UCAL_HOUR] > fStamp[UCAL_AM_PM])?fStamp[UCAL_HOUR]:fStamp[UCAL_AM_PM]; - int32_t bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp; - - // Hours - if (bestStamp != kUnset) { - if (bestStamp == hourOfDayStamp) { - // Don't normalize here; let overflow bump into the next period. - // This is consistent with how we handle other fields. - millisInDay += internalGet(UCAL_HOUR_OF_DAY); - } else { - // Don't normalize here; let overflow bump into the next period. - // This is consistent with how we handle other fields. - millisInDay += internalGet(UCAL_HOUR); - millisInDay += 12 * internalGet(UCAL_AM_PM); // Default works for unset AM_PM - } - } - - // We use the fact that unset == 0; we start with millisInDay - // == HOUR_OF_DAY. - millisInDay *= 60; - millisInDay += internalGet(UCAL_MINUTE); // now have minutes - millisInDay *= 60; - millisInDay += internalGet(UCAL_SECOND); // now have seconds - millisInDay *= 1000; - millisInDay += internalGet(UCAL_MILLISECOND); // now have millis - - return millisInDay; -} - -/** -* This method can assume EXTENDED_YEAR has been set. -* @param millis milliseconds of the date fields -* @param millisInDay milliseconds of the time fields; may be out -* or range. -* @stable ICU 2.0 -*/ -int32_t Calendar::computeZoneOffset(double millis, double millisInDay, UErrorCode &ec) { - if (U_FAILURE(ec)) { - return 0; - } - int32_t rawOffset, dstOffset; - UDate wall = millis + millisInDay; - BasicTimeZone* btz = getBasicTimeZone(); - if (btz) { - UTimeZoneLocalOption duplicatedTimeOpt = (fRepeatedWallTime == UCAL_WALLTIME_FIRST) ? UCAL_TZ_LOCAL_FORMER : UCAL_TZ_LOCAL_LATTER; - UTimeZoneLocalOption nonExistingTimeOpt = (fSkippedWallTime == UCAL_WALLTIME_FIRST) ? UCAL_TZ_LOCAL_LATTER : UCAL_TZ_LOCAL_FORMER; - btz->getOffsetFromLocal(wall, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, ec); - } else { - const TimeZone& tz = getTimeZone(); - // By default, TimeZone::getOffset behaves UCAL_WALLTIME_LAST for both. - tz.getOffset(wall, true, rawOffset, dstOffset, ec); - - UBool sawRecentNegativeShift = false; - if (fRepeatedWallTime == UCAL_WALLTIME_FIRST) { - // Check if the given wall time falls into repeated time range - UDate tgmt = wall - (rawOffset + dstOffset); - - // Any negative zone transition within last 6 hours? - // Note: The maximum historic negative zone transition is -3 hours in the tz database. - // 6 hour window would be sufficient for this purpose. - int32_t tmpRaw, tmpDst; - tz.getOffset(tgmt - 6*60*60*1000, false, tmpRaw, tmpDst, ec); - int32_t offsetDelta = (rawOffset + dstOffset) - (tmpRaw + tmpDst); - - U_ASSERT(offsetDelta < -6*60*60*1000); - if (offsetDelta < 0) { - sawRecentNegativeShift = true; - // Negative shift within last 6 hours. When UCAL_WALLTIME_FIRST is used and the given wall time falls - // into the repeated time range, use offsets before the transition. - // Note: If it does not fall into the repeated time range, offsets remain unchanged below. - tz.getOffset(wall + offsetDelta, true, rawOffset, dstOffset, ec); - } - } - if (!sawRecentNegativeShift && fSkippedWallTime == UCAL_WALLTIME_FIRST) { - // When skipped wall time option is WALLTIME_FIRST, - // recalculate offsets from the resolved time (non-wall). - // When the given wall time falls into skipped wall time, - // the offsets will be based on the zone offsets AFTER - // the transition (which means, earliest possible interpretation). - UDate tgmt = wall - (rawOffset + dstOffset); - tz.getOffset(tgmt, false, rawOffset, dstOffset, ec); - } - } - return rawOffset + dstOffset; -} - -int32_t Calendar::computeJulianDay() -{ - // We want to see if any of the date fields is newer than the - // JULIAN_DAY. If not, then we use JULIAN_DAY. If so, then we do - // the normal resolution. We only use JULIAN_DAY if it has been - // set by the user. This makes it possible for the caller to set - // the calendar to a time and call clear(MONTH) to reset the MONTH - // to January. This is legacy behavior. Without this, - // clear(MONTH) has no effect, since the internally set JULIAN_DAY - // is used. - if (fStamp[UCAL_JULIAN_DAY] >= (int32_t)kMinimumUserStamp) { - int32_t bestStamp = newestStamp(UCAL_ERA, UCAL_DAY_OF_WEEK_IN_MONTH, kUnset); - bestStamp = newestStamp(UCAL_YEAR_WOY, UCAL_EXTENDED_YEAR, bestStamp); - if (bestStamp <= fStamp[UCAL_JULIAN_DAY]) { - return internalGet(UCAL_JULIAN_DAY); - } - } - - UCalendarDateFields bestField = resolveFields(getFieldResolutionTable()); - if (bestField == UCAL_FIELD_COUNT) { - bestField = UCAL_DAY_OF_MONTH; - } - - return handleComputeJulianDay(bestField); -} - -// ------------------------------------------- - -int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField) { - UBool useMonth = (bestField == UCAL_DAY_OF_MONTH || - bestField == UCAL_WEEK_OF_MONTH || - bestField == UCAL_DAY_OF_WEEK_IN_MONTH); - int32_t year; - - if (bestField == UCAL_WEEK_OF_YEAR && newerField(UCAL_YEAR_WOY, UCAL_YEAR) == UCAL_YEAR_WOY) { - year = internalGet(UCAL_YEAR_WOY); - } else { - year = handleGetExtendedYear(); - } - - internalSet(UCAL_EXTENDED_YEAR, year); - -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: bestField= %s - y=%d\n", __FILE__, __LINE__, fldName(bestField), year); -#endif - - // Get the Julian day of the day BEFORE the start of this year. - // If useMonth is true, get the day before the start of the month. - - // give calendar subclass a chance to have a default 'first' month - int32_t month; - - if(isSet(UCAL_MONTH)) { - month = internalGet(UCAL_MONTH); - } else { - month = getDefaultMonthInYear(year); - } - - int32_t julianDay = handleComputeMonthStart(year, useMonth ? month : 0, useMonth); - - if (bestField == UCAL_DAY_OF_MONTH) { - - // give calendar subclass a chance to have a default 'first' dom - int32_t dayOfMonth; - if(isSet(UCAL_DAY_OF_MONTH)) { - dayOfMonth = internalGet(UCAL_DAY_OF_MONTH,1); - } else { - dayOfMonth = getDefaultDayInMonth(year, month); - } - return julianDay + dayOfMonth; - } - - if (bestField == UCAL_DAY_OF_YEAR) { - return julianDay + internalGet(UCAL_DAY_OF_YEAR); - } - - int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw - - // At this point julianDay is the 0-based day BEFORE the first day of - // January 1, year 1 of the given calendar. If julianDay == 0, it - // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian - // or Gregorian). (or it is before the month we are in, if useMonth is True) - - // At this point we need to process the WEEK_OF_MONTH or - // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH. - // First, perform initial shared computations. These locate the - // first week of the period. - - // Get the 0-based localized DOW of day one of the month or year. - // Valid range 0..6. - int32_t first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; - if (first < 0) { - first += 7; - } - - int32_t dowLocal = getLocalDOW(); - - // Find the first target DOW (dowLocal) in the month or year. - // Actually, it may be just before the first of the month or year. - // It will be an integer from -5..7. - int32_t date = 1 - first + dowLocal; - - if (bestField == UCAL_DAY_OF_WEEK_IN_MONTH) { - // Adjust the target DOW to be in the month or year. - if (date < 1) { - date += 7; - } - - // The only trickiness occurs if the day-of-week-in-month is - // negative. - int32_t dim = internalGet(UCAL_DAY_OF_WEEK_IN_MONTH, 1); - if (dim >= 0) { - date += 7*(dim - 1); - - } else { - // Move date to the last of this day-of-week in this month, - // then back up as needed. If dim==-1, we don't back up at - // all. If dim==-2, we back up once, etc. Don't back up - // past the first of the given day-of-week in this month. - // Note that we handle -2, -3, etc. correctly, even though - // values < -1 are technically disallowed. - int32_t m = internalGet(UCAL_MONTH, UCAL_JANUARY); - int32_t monthLength = handleGetMonthLength(year, m); - date += ((monthLength - date) / 7 + dim + 1) * 7; - } - } else { -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d - bf= %s\n", __FILE__, __LINE__, fldName(bestField)); -#endif - - if(bestField == UCAL_WEEK_OF_YEAR) { // ------------------------------------- WOY ------------- - if(!isSet(UCAL_YEAR_WOY) || // YWOY not set at all or - ( (resolveFields(kYearPrecedence) != UCAL_YEAR_WOY) // YWOY doesn't have precedence - && (fStamp[UCAL_YEAR_WOY]!=kInternallySet) ) ) // (excluding where all fields are internally set - then YWOY is used) - { - // need to be sure to stay in 'real' year. - int32_t woy = internalGet(bestField); - - int32_t nextJulianDay = handleComputeMonthStart(year+1, 0, false); // jd of day before jan 1 - int32_t nextFirst = julianDayToDayOfWeek(nextJulianDay + 1) - firstDayOfWeek; - - if (nextFirst < 0) { // 0..6 ldow of Jan 1 - nextFirst += 7; - } - - if(woy==1) { // FIRST WEEK --------------------------------- -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d - woy=%d, yp=%d, nj(%d)=%d, nf=%d", __FILE__, __LINE__, - internalGet(bestField), resolveFields(kYearPrecedence), year+1, - nextJulianDay, nextFirst); - - fprintf(stderr, " next: %d DFW, min=%d \n", (7-nextFirst), getMinimalDaysInFirstWeek() ); -#endif - - // nextFirst is now the localized DOW of Jan 1 of y-woy+1 - if((nextFirst > 0) && // Jan 1 starts on FDOW - (7-nextFirst) >= getMinimalDaysInFirstWeek()) // or enough days in the week - { - // Jan 1 of (yearWoy+1) is in yearWoy+1 - recalculate JD to next year -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d - was going to move JD from %d to %d [d%d]\n", __FILE__, __LINE__, - julianDay, nextJulianDay, (nextJulianDay-julianDay)); -#endif - julianDay = nextJulianDay; - - // recalculate 'first' [0-based local dow of jan 1] - first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; - if (first < 0) { - first += 7; - } - // recalculate date. - date = 1 - first + dowLocal; - } - } else if(woy>=getLeastMaximum(bestField)) { - // could be in the last week- find out if this JD would overstep - int32_t testDate = date; - if ((7 - first) < getMinimalDaysInFirstWeek()) { - testDate += 7; - } - - // Now adjust for the week number. - testDate += 7 * (woy - 1); - -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d)\n", - __FILE__, __LINE__, year, year-1, testDate, julianDay+testDate, nextJulianDay); -#endif - if(julianDay+testDate > nextJulianDay) { // is it past Dec 31? (nextJulianDay is day BEFORE year+1's Jan 1) - // Fire up the calculating engines.. retry YWOY = (year-1) - julianDay = handleComputeMonthStart(year-1, 0, false); // jd before Jan 1 of previous year - first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; // 0 based local dow of first week - - if(first < 0) { // 0..6 - first += 7; - } - date = 1 - first + dowLocal; - -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d - date now %d, jd%d, ywoy%d\n", - __FILE__, __LINE__, date, julianDay, year-1); -#endif - - - } /* correction needed */ - } /* leastmaximum */ - } /* resolvefields(year) != year_woy */ - } /* bestfield != week_of_year */ - - // assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR) - // Adjust for minimal days in first week - if ((7 - first) < getMinimalDaysInFirstWeek()) { - date += 7; - } - - // Now adjust for the week number. - date += 7 * (internalGet(bestField) - 1); - } - - return julianDay + date; -} - -int32_t -Calendar::getDefaultMonthInYear(int32_t /*eyear*/) -{ - return 0; -} - -int32_t -Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/) -{ - return 1; -} - - -int32_t Calendar::getLocalDOW() -{ - // Get zero-based localized DOW, valid range 0..6. This is the DOW - // we are looking for. - int32_t dowLocal = 0; - switch (resolveFields(kDOWPrecedence)) { - case UCAL_DAY_OF_WEEK: - dowLocal = internalGet(UCAL_DAY_OF_WEEK) - fFirstDayOfWeek; - break; - case UCAL_DOW_LOCAL: - dowLocal = internalGet(UCAL_DOW_LOCAL) - 1; - break; - default: - break; - } - dowLocal = dowLocal % 7; - if (dowLocal < 0) { - dowLocal += 7; - } - return dowLocal; -} - -int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy) -{ - // We have UCAL_YEAR_WOY and UCAL_WEEK_OF_YEAR - from those, determine - // what year we fall in, so that other code can set it properly. - // (code borrowed from computeWeekFields and handleComputeJulianDay) - //return yearWoy; - - // First, we need a reliable DOW. - UCalendarDateFields bestField = resolveFields(kDatePrecedence); // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields - - // Now, a local DOW - int32_t dowLocal = getLocalDOW(); // 0..6 - int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw - int32_t jan1Start = handleComputeMonthStart(yearWoy, 0, false); - int32_t nextJan1Start = handleComputeMonthStart(yearWoy+1, 0, false); // next year's Jan1 start - - // At this point julianDay is the 0-based day BEFORE the first day of - // January 1, year 1 of the given calendar. If julianDay == 0, it - // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian - // or Gregorian). (or it is before the month we are in, if useMonth is True) - - // At this point we need to process the WEEK_OF_MONTH or - // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH. - // First, perform initial shared computations. These locate the - // first week of the period. - - // Get the 0-based localized DOW of day one of the month or year. - // Valid range 0..6. - int32_t first = julianDayToDayOfWeek(jan1Start + 1) - firstDayOfWeek; - if (first < 0) { - first += 7; - } - - //// (nextFirst was not used below) - // int32_t nextFirst = julianDayToDayOfWeek(nextJan1Start + 1) - firstDayOfWeek; - // if (nextFirst < 0) { - // nextFirst += 7; - //} - - int32_t minDays = getMinimalDaysInFirstWeek(); - UBool jan1InPrevYear = false; // January 1st in the year of WOY is the 1st week? (i.e. first week is < minimal ) - //UBool nextJan1InPrevYear = false; // January 1st of Year of WOY + 1 is in the first week? - - if((7 - first) < minDays) { - jan1InPrevYear = true; - } - - // if((7 - nextFirst) < minDays) { - // nextJan1InPrevYear = true; - // } - - switch(bestField) { - case UCAL_WEEK_OF_YEAR: - if(woy == 1) { - if(jan1InPrevYear == true) { - // the first week of January is in the previous year - // therefore WOY1 is always solidly within yearWoy - return yearWoy; - } else { - // First WOY is split between two years - if( dowLocal < first) { // we are prior to Jan 1 - return yearWoy-1; // previous year - } else { - return yearWoy; // in this year - } - } - } else if(woy >= getLeastMaximum(bestField)) { - // we _might_ be in the last week.. - int32_t jd = // Calculate JD of our target day: - jan1Start + // JD of Jan 1 - (7-first) + // days in the first week (Jan 1.. ) - (woy-1)*7 + // add the weeks of the year - dowLocal; // the local dow (0..6) of last week - if(jan1InPrevYear==false) { - jd -= 7; // woy already includes Jan 1's week. - } - - if( (jd+1) >= nextJan1Start ) { - // we are in week 52 or 53 etc. - actual year is yearWoy+1 - return yearWoy+1; - } else { - // still in yearWoy; - return yearWoy; - } - } else { - // we're not possibly in the last week -must be ywoy - return yearWoy; - } - - case UCAL_DATE: - if((internalGet(UCAL_MONTH)==0) && - (woy >= getLeastMaximum(UCAL_WEEK_OF_YEAR))) { - return yearWoy+1; // month 0, late woy = in the next year - } else if(woy==1) { - //if(nextJan1InPrevYear) { - if(internalGet(UCAL_MONTH)==0) { - return yearWoy; - } else { - return yearWoy-1; - } - //} - } - - //(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow */ ) { - //within 1st week and in this month.. - //return yearWoy+1; - return yearWoy; - - default: // assume the year is appropriate - return yearWoy; - } -} - -int32_t Calendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const -{ - return handleComputeMonthStart(extendedYear, month+1, true) - - handleComputeMonthStart(extendedYear, month, true); -} - -int32_t Calendar::handleGetYearLength(int32_t eyear) const { - return handleComputeMonthStart(eyear+1, 0, false) - - handleComputeMonthStart(eyear, 0, false); -} - -int32_t -Calendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const -{ - if (U_FAILURE(status)) { - return 0; - } - int32_t result; - switch (field) { - case UCAL_DATE: - { - if(U_FAILURE(status)) return 0; - Calendar *cal = clone(); - if(!cal) { status = U_MEMORY_ALLOCATION_ERROR; return 0; } - cal->setLenient(true); - cal->prepareGetActual(field,false,status); - result = handleGetMonthLength(cal->get(UCAL_EXTENDED_YEAR, status), cal->get(UCAL_MONTH, status)); - delete cal; - } - break; - - case UCAL_DAY_OF_YEAR: - { - if(U_FAILURE(status)) return 0; - Calendar *cal = clone(); - if(!cal) { status = U_MEMORY_ALLOCATION_ERROR; return 0; } - cal->setLenient(true); - cal->prepareGetActual(field,false,status); - result = handleGetYearLength(cal->get(UCAL_EXTENDED_YEAR, status)); - delete cal; - } - break; - - case UCAL_DAY_OF_WEEK: - case UCAL_AM_PM: - case UCAL_HOUR: - case UCAL_HOUR_OF_DAY: - case UCAL_MINUTE: - case UCAL_SECOND: - case UCAL_MILLISECOND: - case UCAL_ZONE_OFFSET: - case UCAL_DST_OFFSET: - case UCAL_DOW_LOCAL: - case UCAL_JULIAN_DAY: - case UCAL_MILLISECONDS_IN_DAY: - // These fields all have fixed minima/maxima - result = getMaximum(field); - break; - - default: - // For all other fields, do it the hard way.... - result = getActualHelper(field, getLeastMaximum(field), getMaximum(field),status); - break; - } - return result; -} - - -/** -* Prepare this calendar for computing the actual minimum or maximum. -* This method modifies this calendar's fields; it is called on a -* temporary calendar. -* -*

Rationale: The semantics of getActualXxx() is to return the -* maximum or minimum value that the given field can take, taking into -* account other relevant fields. In general these other fields are -* larger fields. For example, when computing the actual maximum -* DATE, the current value of DATE itself is ignored, -* as is the value of any field smaller. -* -*

The time fields all have fixed minima and maxima, so we don't -* need to worry about them. This also lets us set the -* MILLISECONDS_IN_DAY to zero to erase any effects the time fields -* might have when computing date fields. -* -*

DAY_OF_WEEK is adjusted specially for the WEEK_OF_MONTH and -* WEEK_OF_YEAR fields to ensure that they are computed correctly. -* @internal -*/ -void Calendar::prepareGetActual(UCalendarDateFields field, UBool isMinimum, UErrorCode &status) -{ - if (U_FAILURE(status)) { - return; - } - set(UCAL_MILLISECONDS_IN_DAY, 0); - - switch (field) { - case UCAL_YEAR: - case UCAL_EXTENDED_YEAR: - set(UCAL_DAY_OF_YEAR, getGreatestMinimum(UCAL_DAY_OF_YEAR)); - break; - - case UCAL_YEAR_WOY: - set(UCAL_WEEK_OF_YEAR, getGreatestMinimum(UCAL_WEEK_OF_YEAR)); - U_FALLTHROUGH; - case UCAL_MONTH: - set(UCAL_DATE, getGreatestMinimum(UCAL_DATE)); - break; - - case UCAL_DAY_OF_WEEK_IN_MONTH: - // For dowim, the maximum occurs for the DOW of the first of the - // month. - set(UCAL_DATE, 1); - set(UCAL_DAY_OF_WEEK, get(UCAL_DAY_OF_WEEK, status)); // Make this user set - break; - - case UCAL_WEEK_OF_MONTH: - case UCAL_WEEK_OF_YEAR: - // If we're counting weeks, set the day of the week to either the - // first or last localized DOW. We know the last week of a month - // or year will contain the first day of the week, and that the - // first week will contain the last DOW. - { - int32_t dow = fFirstDayOfWeek; - if (isMinimum) { - dow = (dow + 6) % 7; // set to last DOW - if (dow < UCAL_SUNDAY) { - dow += 7; - } - } -#if defined (U_DEBUG_CAL) - fprintf(stderr, "prepareGetActualHelper(WOM/WOY) - dow=%d\n", dow); -#endif - set(UCAL_DAY_OF_WEEK, dow); - } - break; - default: - break; - } - - // Do this last to give it the newest time stamp - set(field, getGreatestMinimum(field)); -} - -int32_t Calendar::getActualHelper(UCalendarDateFields field, int32_t startValue, int32_t endValue, UErrorCode &status) const -{ -#if defined (U_DEBUG_CAL) - fprintf(stderr, "getActualHelper(%d,%d .. %d, %s)\n", field, startValue, endValue, u_errorName(status)); -#endif - if (U_FAILURE(status)) { - return 0; - } - if (startValue == endValue) { - // if we know that the maximum value is always the same, just return it - return startValue; - } - - int32_t delta = (endValue > startValue) ? 1 : -1; - - // clone the calendar so we don't mess with the real one, and set it to - // accept anything for the field values - if(U_FAILURE(status)) return startValue; - Calendar *work = clone(); - if(!work) { status = U_MEMORY_ALLOCATION_ERROR; return startValue; } - - // need to resolve time here, otherwise, fields set for actual limit - // may cause conflict with fields previously set (but not yet resolved). - work->complete(status); - - work->setLenient(true); - work->prepareGetActual(field, delta < 0, status); - - // now try each value from the start to the end one by one until - // we get a value that normalizes to another value. The last value that - // normalizes to itself is the actual maximum for the current date - work->set(field, startValue); - - // prepareGetActual sets the first day of week in the same week with - // the first day of a month. Unlike WEEK_OF_YEAR, week number for the - // week which contains days from both previous and current month is - // not unique. For example, last several days in the previous month - // is week 5, and the rest of week is week 1. - int32_t result = startValue; - if ((work->get(field, status) != startValue - && field != UCAL_WEEK_OF_MONTH && delta > 0 ) || U_FAILURE(status)) { -#if defined (U_DEBUG_CAL) - fprintf(stderr, "getActualHelper(fld %d) - got %d (not %d) - %s\n", field, work->get(field,status), startValue, u_errorName(status)); -#endif - } else { - do { - startValue += delta; - work->add(field, delta, status); - if (work->get(field, status) != startValue || U_FAILURE(status)) { -#if defined (U_DEBUG_CAL) - fprintf(stderr, "getActualHelper(fld %d) - got %d (not %d), BREAK - %s\n", field, work->get(field,status), startValue, u_errorName(status)); -#endif - break; - } - result = startValue; - } while (startValue != endValue); - } - delete work; -#if defined (U_DEBUG_CAL) - fprintf(stderr, "getActualHelper(%d) = %d\n", field, result); -#endif - return result; -} - - - - -// ------------------------------------- - -void -Calendar::setWeekData(const Locale& desiredLocale, const char *type, UErrorCode& status) -{ - - if (U_FAILURE(status)) return; - - fFirstDayOfWeek = UCAL_SUNDAY; - fMinimalDaysInFirstWeek = 1; - fWeekendOnset = UCAL_SATURDAY; - fWeekendOnsetMillis = 0; - fWeekendCease = UCAL_SUNDAY; - fWeekendCeaseMillis = 86400000; // 24*60*60*1000 - - // Since week and weekend data is territory based instead of language based, - // we may need to tweak the locale that we are using to try to get the appropriate - // values, using the following logic: - // 1). If the locale has a language but no territory, use the territory as defined by - // the likely subtags. - // 2). If the locale has a script designation then we ignore it, - // then remove it ( i.e. "en_Latn_US" becomes "en_US" ) - - UErrorCode myStatus = U_ZERO_ERROR; - - Locale min(desiredLocale); - min.minimizeSubtags(myStatus); - Locale useLocale; - if ( uprv_strlen(desiredLocale.getCountry()) == 0 || - (uprv_strlen(desiredLocale.getScript()) > 0 && uprv_strlen(min.getScript()) == 0) ) { - myStatus = U_ZERO_ERROR; - Locale max(desiredLocale); - max.addLikelySubtags(myStatus); - useLocale = Locale(max.getLanguage(),max.getCountry()); - } else { - useLocale = desiredLocale; - } - - /* The code here is somewhat of a hack, since week data and weekend data aren't really tied to - a specific calendar, they aren't truly locale data. But this is the only place where valid and - actual locale can be set, so we take a shot at it here by loading a representative resource - from the calendar data. The code used to use the dateTimeElements resource to get first day - of week data, but this was moved to supplemental data under ticket 7755. (JCE) */ - - // Get the monthNames resource bundle for the calendar 'type'. Fallback to gregorian if the resource is not - // found. - LocalUResourceBundlePointer calData(ures_open(NULL, useLocale.getBaseName(), &status)); - ures_getByKey(calData.getAlias(), gCalendar, calData.getAlias(), &status); - - LocalUResourceBundlePointer monthNames; - if (type != NULL && *type != '\0' && uprv_strcmp(type, gGregorian) != 0) { - monthNames.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), type, NULL, &status)); - ures_getByKeyWithFallback(monthNames.getAlias(), gMonthNames, - monthNames.getAlias(), &status); - } - - if (monthNames.isNull() || status == U_MISSING_RESOURCE_ERROR) { - status = U_ZERO_ERROR; - monthNames.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), gGregorian, - monthNames.orphan(), &status)); - ures_getByKeyWithFallback(monthNames.getAlias(), gMonthNames, - monthNames.getAlias(), &status); - } - - if (U_SUCCESS(status)) { - U_LOCALE_BASED(locBased,*this); - locBased.setLocaleIDs(ures_getLocaleByType(monthNames.getAlias(), ULOC_VALID_LOCALE, &status), - ures_getLocaleByType(monthNames.getAlias(), ULOC_ACTUAL_LOCALE, &status)); - } else { - status = U_USING_FALLBACK_WARNING; - return; - } - - char region[ULOC_COUNTRY_CAPACITY]; - (void)ulocimp_getRegionForSupplementalData(desiredLocale.getName(), true, region, sizeof(region), &status); - - // Read week data values from supplementalData week data - UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", &status); - ures_getByKey(rb, "weekData", rb, &status); - UResourceBundle *weekData = ures_getByKey(rb, region, NULL, &status); - if (status == U_MISSING_RESOURCE_ERROR && rb != NULL) { - status = U_ZERO_ERROR; - weekData = ures_getByKey(rb, "001", NULL, &status); - } - - if (U_FAILURE(status)) { - status = U_USING_FALLBACK_WARNING; - } else { - int32_t arrLen; - const int32_t *weekDataArr = ures_getIntVector(weekData,&arrLen,&status); - if( U_SUCCESS(status) && arrLen == 6 - && 1 <= weekDataArr[0] && weekDataArr[0] <= 7 - && 1 <= weekDataArr[1] && weekDataArr[1] <= 7 - && 1 <= weekDataArr[2] && weekDataArr[2] <= 7 - && 1 <= weekDataArr[4] && weekDataArr[4] <= 7) { - fFirstDayOfWeek = (UCalendarDaysOfWeek)weekDataArr[0]; - fMinimalDaysInFirstWeek = (uint8_t)weekDataArr[1]; - fWeekendOnset = (UCalendarDaysOfWeek)weekDataArr[2]; - fWeekendOnsetMillis = weekDataArr[3]; - fWeekendCease = (UCalendarDaysOfWeek)weekDataArr[4]; - fWeekendCeaseMillis = weekDataArr[5]; - } else { - status = U_INVALID_FORMAT_ERROR; - } - } - ures_close(weekData); - ures_close(rb); -} - -/** -* Recompute the time and update the status fields isTimeSet -* and areFieldsSet. Callers should check isTimeSet and only -* call this method if isTimeSet is false. -*/ -void -Calendar::updateTime(UErrorCode& status) -{ - computeTime(status); - if(U_FAILURE(status)) - return; - - // If we are lenient, we need to recompute the fields to normalize - // the values. Also, if we haven't set all the fields yet (i.e., - // in a newly-created object), we need to fill in the fields. [LIU] - if (isLenient() || ! fAreAllFieldsSet) - fAreFieldsSet = false; - - fIsTimeSet = true; - fAreFieldsVirtuallySet = false; -} - -Locale -Calendar::getLocale(ULocDataLocaleType type, UErrorCode& status) const { - U_LOCALE_BASED(locBased, *this); - return locBased.getLocale(type, status); -} - -const char * -Calendar::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const { - U_LOCALE_BASED(locBased, *this); - return locBased.getLocaleID(type, status); -} - -void -Calendar::recalculateStamp() { - int32_t index; - int32_t currentValue; - int32_t j, i; - - fNextStamp = 1; - - for (j = 0; j < UCAL_FIELD_COUNT; j++) { - currentValue = STAMP_MAX; - index = -1; - for (i = 0; i < UCAL_FIELD_COUNT; i++) { - if (fStamp[i] > fNextStamp && fStamp[i] < currentValue) { - currentValue = fStamp[i]; - index = i; - } - } - - if (index >= 0) { - fStamp[index] = ++fNextStamp; - } else { - break; - } - } - fNextStamp++; -} - -// Deprecated function. This doesn't need to be inline. -void -Calendar::internalSet(EDateFields field, int32_t value) -{ - internalSet((UCalendarDateFields) field, value); -} - -BasicTimeZone* -Calendar::getBasicTimeZone(void) const { - if (dynamic_cast(fZone) != NULL - || dynamic_cast(fZone) != NULL - || dynamic_cast(fZone) != NULL - || dynamic_cast(fZone) != NULL) { - return (BasicTimeZone*)fZone; - } - return NULL; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1997-2016, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* File CALENDAR.CPP +* +* Modification History: +* +* Date Name Description +* 02/03/97 clhuang Creation. +* 04/22/97 aliu Cleaned up, fixed memory leak, made +* setWeekCountData() more robust. +* Moved platform code to TPlatformUtilities. +* 05/01/97 aliu Made equals(), before(), after() arguments const. +* 05/20/97 aliu Changed logic of when to compute fields and time +* to fix bugs. +* 08/12/97 aliu Added equivalentTo. Misc other fixes. +* 07/28/98 stephen Sync up with JDK 1.2 +* 09/02/98 stephen Sync with JDK 1.2 8/31 build (getActualMin/Max) +* 03/17/99 stephen Changed adoptTimeZone() - now fAreFieldsSet is +* set to false to force update of time. +******************************************************************************* +*/ + +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/gregocal.h" +#include "unicode/basictz.h" +#include "unicode/simpletz.h" +#include "unicode/rbtz.h" +#include "unicode/vtzone.h" +#include "gregoimp.h" +#include "buddhcal.h" +#include "taiwncal.h" +#include "japancal.h" +#include "islamcal.h" +#include "hebrwcal.h" +#include "persncal.h" +#include "indiancal.h" +#include "iso8601cal.h" +#include "chnsecal.h" +#include "coptccal.h" +#include "dangical.h" +#include "ethpccal.h" +#include "unicode/calendar.h" +#include "cpputils.h" +#include "servloc.h" +#include "ucln_in.h" +#include "cstring.h" +#include "locbased.h" +#include "uresimp.h" +#include "ustrenum.h" +#include "uassert.h" +#include "olsontz.h" +#include "sharedcalendar.h" +#include "unifiedcache.h" +#include "ulocimp.h" + +#if !UCONFIG_NO_SERVICE +static icu::ICULocaleService* gService = nullptr; +static icu::UInitOnce gServiceInitOnce {}; + +// INTERNAL - for cleanup +U_CDECL_BEGIN +static UBool calendar_cleanup() { +#if !UCONFIG_NO_SERVICE + if (gService) { + delete gService; + gService = nullptr; + } + gServiceInitOnce.reset(); +#endif + return true; +} +U_CDECL_END +#endif + +// ------------------------------------------ +// +// Registration +// +//------------------------------------------- +//#define U_DEBUG_CALSVC 1 +// + +#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) + +/** + * fldName was removed as a duplicate implementation. + * use udbg_ services instead, + * which depend on include files and library from ../tools/toolutil, the following circular link: + * CPPFLAGS+=-I$(top_srcdir)/tools/toolutil + * LIBS+=$(LIBICUTOOLUTIL) + */ +#include "udbgutil.h" +#include + +/** +* convert a UCalendarDateFields into a string - for debugging +* @param f field enum +* @return static string to the field name +* @internal +*/ + +const char* fldName(UCalendarDateFields f) { + return udbg_enumName(UDBG_UCalendarDateFields, (int32_t)f); +} + +#if UCAL_DEBUG_DUMP +// from CalendarTest::calToStr - but doesn't modify contents. +void ucal_dump(const Calendar &cal) { + cal.dump(); +} + +void Calendar::dump() const { + int i; + fprintf(stderr, "@calendar=%s, timeset=%c, fieldset=%c, allfields=%c, virtualset=%c, t=%.2f", + getType(), fIsTimeSet?'y':'n', fAreFieldsSet?'y':'n', fAreAllFieldsSet?'y':'n', + fAreFieldsVirtuallySet?'y':'n', + fTime); + + // can add more things here: DST, zone, etc. + fprintf(stderr, "\n"); + for(i = 0;i U_I18N_API +const SharedCalendar *LocaleCacheKey::createObject( + const void * /*unusedCreationContext*/, UErrorCode &status) const { + if (U_FAILURE(status)) { + return nullptr; + } + Calendar *calendar = Calendar::makeInstance(fLoc, status); + if (U_FAILURE(status)) { + return nullptr; + } + SharedCalendar *shared = new SharedCalendar(calendar); + if (shared == nullptr) { + delete calendar; + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + shared->addRef(); + return shared; +} + +static ECalType getCalendarType(const char *s) { + for (int i = 0; gCalTypes[i] != nullptr; i++) { + if (uprv_stricmp(s, gCalTypes[i]) == 0) { + return (ECalType)i; + } + } + return CALTYPE_UNKNOWN; +} + +#if !UCONFIG_NO_SERVICE +// Only used with service registration. +static UBool isStandardSupportedKeyword(const char *keyword, UErrorCode& status) { + if(U_FAILURE(status)) { + return false; + } + ECalType calType = getCalendarType(keyword); + return (calType != CALTYPE_UNKNOWN); +} + +// only used with service registration. +static void getCalendarKeyword(const UnicodeString &id, char *targetBuffer, int32_t targetBufferSize) { + UnicodeString calendarKeyword = UNICODE_STRING_SIMPLE("calendar="); + int32_t calKeyLen = calendarKeyword.length(); + int32_t keyLen = 0; + + int32_t keywordIdx = id.indexOf((char16_t)0x003D); /* '=' */ + if (id[0] == 0x40/*'@'*/ + && id.compareBetween(1, keywordIdx+1, calendarKeyword, 0, calKeyLen) == 0) + { + keyLen = id.extract(keywordIdx+1, id.length(), targetBuffer, targetBufferSize, US_INV); + } + targetBuffer[keyLen] = 0; +} +#endif + +static ECalType getCalendarTypeForLocale(const char *locid) { + UErrorCode status = U_ZERO_ERROR; + ECalType calType = CALTYPE_UNKNOWN; + + //TODO: ULOC_FULL_NAME is out of date and too small.. + char canonicalName[256]; + + // Canonicalize, so that an old-style variant will be transformed to keywords. + // e.g ja_JP_TRADITIONAL -> ja_JP@calendar=japanese + // NOTE: Since ICU-20187, ja_JP_TRADITIONAL no longer canonicalizes, and + // the Gregorian calendar is returned instead. + int32_t canonicalLen = uloc_canonicalize(locid, canonicalName, sizeof(canonicalName) - 1, &status); + if (U_FAILURE(status)) { + return CALTYPE_GREGORIAN; + } + canonicalName[canonicalLen] = 0; // terminate + + char calTypeBuf[32]; + int32_t calTypeBufLen; + + calTypeBufLen = uloc_getKeywordValue(canonicalName, "calendar", calTypeBuf, sizeof(calTypeBuf) - 1, &status); + if (U_SUCCESS(status)) { + calTypeBuf[calTypeBufLen] = 0; + calType = getCalendarType(calTypeBuf); + if (calType != CALTYPE_UNKNOWN) { + return calType; + } + } + status = U_ZERO_ERROR; + + // when calendar keyword is not available or not supported, read supplementalData + // to get the default calendar type for the locale's region + char region[ULOC_COUNTRY_CAPACITY]; + (void)ulocimp_getRegionForSupplementalData(canonicalName, true, region, sizeof(region), &status); + if (U_FAILURE(status)) { + return CALTYPE_GREGORIAN; + } + + // Read preferred calendar values from supplementalData calendarPreference + UResourceBundle *rb = ures_openDirect(nullptr, "supplementalData", &status); + ures_getByKey(rb, "calendarPreferenceData", rb, &status); + UResourceBundle *order = ures_getByKey(rb, region, nullptr, &status); + if (status == U_MISSING_RESOURCE_ERROR && rb != nullptr) { + status = U_ZERO_ERROR; + order = ures_getByKey(rb, "001", nullptr, &status); + } + + calTypeBuf[0] = 0; + if (U_SUCCESS(status) && order != nullptr) { + // the first calendar type is the default for the region + int32_t len = 0; + const char16_t *uCalType = ures_getStringByIndex(order, 0, &len, &status); + if (len < (int32_t)sizeof(calTypeBuf)) { + u_UCharsToChars(uCalType, calTypeBuf, len); + *(calTypeBuf + len) = 0; // terminate; + calType = getCalendarType(calTypeBuf); + } + } + + ures_close(order); + ures_close(rb); + + if (calType == CALTYPE_UNKNOWN) { + // final fallback + calType = CALTYPE_GREGORIAN; + } + return calType; +} + +static Calendar *createStandardCalendar(ECalType calType, const Locale &loc, UErrorCode& status) { + if (U_FAILURE(status)) { + return nullptr; + } + LocalPointer cal; + + switch (calType) { + case CALTYPE_GREGORIAN: + cal.adoptInsteadAndCheckErrorCode(new GregorianCalendar(loc, status), status); + break; + case CALTYPE_JAPANESE: + cal.adoptInsteadAndCheckErrorCode(new JapaneseCalendar(loc, status), status); + break; + case CALTYPE_BUDDHIST: + cal.adoptInsteadAndCheckErrorCode(new BuddhistCalendar(loc, status), status); + break; + case CALTYPE_ROC: + cal.adoptInsteadAndCheckErrorCode(new TaiwanCalendar(loc, status), status); + break; + case CALTYPE_PERSIAN: + cal.adoptInsteadAndCheckErrorCode(new PersianCalendar(loc, status), status); + break; + case CALTYPE_ISLAMIC_TBLA: + cal.adoptInsteadAndCheckErrorCode(new IslamicTBLACalendar(loc, status), status); + break; + case CALTYPE_ISLAMIC_CIVIL: + cal.adoptInsteadAndCheckErrorCode(new IslamicCivilCalendar(loc, status), status); + break; + case CALTYPE_ISLAMIC_RGSA: + cal.adoptInsteadAndCheckErrorCode(new IslamicRGSACalendar(loc, status), status); + break; + case CALTYPE_ISLAMIC: + cal.adoptInsteadAndCheckErrorCode(new IslamicCalendar(loc, status), status); + break; + case CALTYPE_ISLAMIC_UMALQURA: + cal.adoptInsteadAndCheckErrorCode(new IslamicUmalquraCalendar(loc, status), status); + break; + case CALTYPE_HEBREW: + cal.adoptInsteadAndCheckErrorCode(new HebrewCalendar(loc, status), status); + break; + case CALTYPE_CHINESE: + cal.adoptInsteadAndCheckErrorCode(new ChineseCalendar(loc, status), status); + break; + case CALTYPE_INDIAN: + cal.adoptInsteadAndCheckErrorCode(new IndianCalendar(loc, status), status); + break; + case CALTYPE_COPTIC: + cal.adoptInsteadAndCheckErrorCode(new CopticCalendar(loc, status), status); + break; + case CALTYPE_ETHIOPIC: + cal.adoptInsteadAndCheckErrorCode(new EthiopicCalendar(loc, status), status); + break; + case CALTYPE_ETHIOPIC_AMETE_ALEM: + cal.adoptInsteadAndCheckErrorCode(new EthiopicAmeteAlemCalendar(loc, status), status); + break; + case CALTYPE_ISO8601: + cal.adoptInsteadAndCheckErrorCode(new ISO8601Calendar(loc, status), status); + break; + case CALTYPE_DANGI: + cal.adoptInsteadAndCheckErrorCode(new DangiCalendar(loc, status), status); + break; + default: + status = U_UNSUPPORTED_ERROR; + } + return cal.orphan(); +} + + +#if !UCONFIG_NO_SERVICE + +// ------------------------------------- + +/** +* a Calendar Factory which creates the "basic" calendar types, that is, those +* shipped with ICU. +*/ +class BasicCalendarFactory : public LocaleKeyFactory { +public: + /** + * @param calendarType static const string (caller owns storage - will be aliased) to calendar type + */ + BasicCalendarFactory() + : LocaleKeyFactory(LocaleKeyFactory::INVISIBLE) { } + + virtual ~BasicCalendarFactory(); + +protected: + //virtual UBool isSupportedID( const UnicodeString& id, UErrorCode& status) const { + // if(U_FAILURE(status)) { + // return false; + // } + // char keyword[ULOC_FULLNAME_CAPACITY]; + // getCalendarKeyword(id, keyword, (int32_t)sizeof(keyword)); + // return isStandardSupportedKeyword(keyword, status); + //} + + virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const override + { + if (U_SUCCESS(status)) { + for(int32_t i=0;gCalTypes[i] != nullptr;i++) { + UnicodeString id((char16_t)0x40); /* '@' a variant character */ + id.append(UNICODE_STRING_SIMPLE("calendar=")); + id.append(UnicodeString(gCalTypes[i], -1, US_INV)); + result.put(id, (void*)this, status); + } + } + } + + virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const override { + if (U_FAILURE(status)) { + return nullptr; + } +#ifdef U_DEBUG_CALSVC + if(dynamic_cast(&key) == nullptr) { + fprintf(stderr, "::create - not a LocaleKey!\n"); + } +#endif + const LocaleKey* lkey = dynamic_cast(&key); + U_ASSERT(lkey != nullptr); + Locale curLoc; // current locale + Locale canLoc; // Canonical locale + + lkey->currentLocale(curLoc); + lkey->canonicalLocale(canLoc); + + char keyword[ULOC_FULLNAME_CAPACITY]; + UnicodeString str; + + key.currentID(str); + getCalendarKeyword(str, keyword, (int32_t) sizeof(keyword)); + +#ifdef U_DEBUG_CALSVC + fprintf(stderr, "BasicCalendarFactory::create() - cur %s, can %s\n", (const char*)curLoc.getName(), (const char*)canLoc.getName()); +#endif + + if(!isStandardSupportedKeyword(keyword,status)) { // Do we handle this type? +#ifdef U_DEBUG_CALSVC + + fprintf(stderr, "BasicCalendarFactory - not handling %s.[%s]\n", (const char*) curLoc.getName(), tmp ); +#endif + return nullptr; + } + + return createStandardCalendar(getCalendarType(keyword), canLoc, status); + } +}; + +BasicCalendarFactory::~BasicCalendarFactory() {} + +/** +* A factory which looks up the DefaultCalendar resource to determine which class of calendar to use +*/ + +class DefaultCalendarFactory : public ICUResourceBundleFactory { +public: + DefaultCalendarFactory() : ICUResourceBundleFactory() { } + virtual ~DefaultCalendarFactory(); +protected: + virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const override { + if (U_FAILURE(status)) { + return nullptr; + } + + const LocaleKey *lkey = dynamic_cast(&key); + U_ASSERT(lkey != nullptr); + Locale loc; + lkey->currentLocale(loc); + + UnicodeString *ret = new UnicodeString(); + if (ret == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + ret->append((char16_t)0x40); // '@' is a variant character + ret->append(UNICODE_STRING("calendar=", 9)); + ret->append(UnicodeString(gCalTypes[getCalendarTypeForLocale(loc.getName())], -1, US_INV)); + } + return ret; + } +}; + +DefaultCalendarFactory::~DefaultCalendarFactory() {} + +// ------------------------------------- +class CalendarService : public ICULocaleService { +public: + CalendarService() + : ICULocaleService(UNICODE_STRING_SIMPLE("Calendar")) + { + UErrorCode status = U_ZERO_ERROR; + registerFactory(new DefaultCalendarFactory(), status); + } + + virtual ~CalendarService(); + + virtual UObject* cloneInstance(UObject* instance) const override { + UnicodeString *s = dynamic_cast(instance); + if(s != nullptr) { + return s->clone(); + } else { +#ifdef U_DEBUG_CALSVC_F + UErrorCode status2 = U_ZERO_ERROR; + fprintf(stderr, "Cloning a %s calendar with tz=%ld\n", ((Calendar*)instance)->getType(), ((Calendar*)instance)->get(UCAL_ZONE_OFFSET, status2)); +#endif + return ((Calendar*)instance)->clone(); + } + } + + virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /*actualID*/, UErrorCode& status) const override { + if (U_FAILURE(status)) { + return nullptr; + } + LocaleKey& lkey = static_cast(const_cast(key)); + //int32_t kind = lkey.kind(); + + Locale loc; + lkey.canonicalLocale(loc); + +#ifdef U_DEBUG_CALSVC + Locale loc2; + lkey.currentLocale(loc2); + fprintf(stderr, "CalSvc:handleDefault for currentLoc %s, canloc %s\n", (const char*)loc.getName(), (const char*)loc2.getName()); +#endif + Calendar *nc = new GregorianCalendar(loc, status); + if (nc == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nc; + } + +#ifdef U_DEBUG_CALSVC + UErrorCode status2 = U_ZERO_ERROR; + fprintf(stderr, "New default calendar has tz=%d\n", ((Calendar*)nc)->get(UCAL_ZONE_OFFSET, status2)); +#endif + return nc; + } + + virtual UBool isDefault() const override { + return countFactories() == 1; + } +}; + +CalendarService::~CalendarService() {} + +// ------------------------------------- + +static inline UBool +isCalendarServiceUsed() { + return !gServiceInitOnce.isReset(); +} + +// ------------------------------------- + +static void U_CALLCONV +initCalendarService(UErrorCode &status) +{ +#ifdef U_DEBUG_CALSVC + fprintf(stderr, "Spinning up Calendar Service\n"); +#endif + if (U_FAILURE(status)) { + return; + } + ucln_i18n_registerCleanup(UCLN_I18N_CALENDAR, calendar_cleanup); + gService = new CalendarService(); + if (gService == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } +#ifdef U_DEBUG_CALSVC + fprintf(stderr, "Registering classes..\n"); +#endif + + // Register all basic instances. + gService->registerFactory(new BasicCalendarFactory(),status); + +#ifdef U_DEBUG_CALSVC + fprintf(stderr, "Done..\n"); +#endif + + if(U_FAILURE(status)) { +#ifdef U_DEBUG_CALSVC + fprintf(stderr, "err (%s) registering classes, deleting service.....\n", u_errorName(status)); +#endif + delete gService; + gService = nullptr; + } + } + +static ICULocaleService* +getCalendarService(UErrorCode &status) +{ + umtx_initOnce(gServiceInitOnce, &initCalendarService, status); + return gService; +} + +URegistryKey Calendar::registerFactory(ICUServiceFactory* toAdopt, UErrorCode& status) +{ + return getCalendarService(status)->registerFactory(toAdopt, status); +} + +UBool Calendar::unregister(URegistryKey key, UErrorCode& status) { + return getCalendarService(status)->unregister(key, status); +} +#endif /* UCONFIG_NO_SERVICE */ + +// ------------------------------------- + +static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = { + // Minimum Greatest min Least max Greatest max + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // ERA + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // MONTH + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_YEAR + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_MONTH + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_MONTH + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_YEAR + { 1, 1, 7, 7 }, // DAY_OF_WEEK + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_WEEK_IN_MONTH + { 0, 0, 1, 1 }, // AM_PM + { 0, 0, 11, 11 }, // HOUR + { 0, 0, 23, 23 }, // HOUR_OF_DAY + { 0, 0, 59, 59 }, // MINUTE + { 0, 0, 59, 59 }, // SECOND + { 0, 0, 999, 999 }, // MILLISECOND + {-16*kOneHour, -16*kOneHour, 12*kOneHour, 30*kOneHour }, // ZONE_OFFSET + { -1*kOneHour, -1*kOneHour, 2*kOneHour, 2*kOneHour }, // DST_OFFSET + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR_WOY + { 1, 1, 7, 7 }, // DOW_LOCAL + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // EXTENDED_YEAR + { -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, // JULIAN_DAY + { 0, 0, 24*kOneHour-1, 24*kOneHour-1 }, // MILLISECONDS_IN_DAY + { 0, 0, 1, 1 }, // IS_LEAP_MONTH + { 0, 0, 11, 11 } // ORDINAL_MONTH +}; + +// Resource bundle tags read by this class +static const char gCalendar[] = "calendar"; +static const char gMonthNames[] = "monthNames"; +static const char gGregorian[] = "gregorian"; + +// Data flow in Calendar +// --------------------- + +// The current time is represented in two ways by Calendar: as UTC +// milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local +// fields such as MONTH, HOUR, AM_PM, etc. It is possible to compute the +// millis from the fields, and vice versa. The data needed to do this +// conversion is encapsulated by a TimeZone object owned by the Calendar. +// The data provided by the TimeZone object may also be overridden if the +// user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class +// keeps track of what information was most recently set by the caller, and +// uses that to compute any other information as needed. + +// If the user sets the fields using set(), the data flow is as follows. +// This is implemented by the Calendar subclass's computeTime() method. +// During this process, certain fields may be ignored. The disambiguation +// algorithm for resolving which fields to pay attention to is described +// above. + +// local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.) +// | +// | Using Calendar-specific algorithm +// V +// local standard millis +// | +// | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET +// V +// UTC millis (in time data member) + +// If the user sets the UTC millis using setTime(), the data flow is as +// follows. This is implemented by the Calendar subclass's computeFields() +// method. + +// UTC millis (in time data member) +// | +// | Using TimeZone getOffset() +// V +// local standard millis +// | +// | Using Calendar-specific algorithm +// V +// local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.) + +// In general, a round trip from fields, through local and UTC millis, and +// back out to fields is made when necessary. This is implemented by the +// complete() method. Resolving a partial set of fields into a UTC millis +// value allows all remaining fields to be generated from that value. If +// the Calendar is lenient, the fields are also renormalized to standard +// ranges when they are regenerated. + +// ------------------------------------- + +Calendar::Calendar(UErrorCode& success) +: UObject(), +fIsTimeSet(false), +fAreFieldsSet(false), +fAreAllFieldsSet(false), +fAreFieldsVirtuallySet(false), +fNextStamp((int32_t)kMinimumUserStamp), +fTime(0), +fLenient(true), +fZone(nullptr), +fRepeatedWallTime(UCAL_WALLTIME_LAST), +fSkippedWallTime(UCAL_WALLTIME_LAST) +{ + validLocale[0] = 0; + actualLocale[0] = 0; + clear(); + if (U_FAILURE(success)) { + return; + } + fZone = TimeZone::createDefault(); + if (fZone == nullptr) { + success = U_MEMORY_ALLOCATION_ERROR; + } + setWeekData(Locale::getDefault(), nullptr, success); +} + +// ------------------------------------- + +Calendar::Calendar(TimeZone* zone, const Locale& aLocale, UErrorCode& success) +: UObject(), +fIsTimeSet(false), +fAreFieldsSet(false), +fAreAllFieldsSet(false), +fAreFieldsVirtuallySet(false), +fNextStamp((int32_t)kMinimumUserStamp), +fTime(0), +fLenient(true), +fZone(nullptr), +fRepeatedWallTime(UCAL_WALLTIME_LAST), +fSkippedWallTime(UCAL_WALLTIME_LAST) +{ + validLocale[0] = 0; + actualLocale[0] = 0; + if (U_FAILURE(success)) { + delete zone; + return; + } + if(zone == 0) { +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: ILLEGAL ARG because timezone cannot be 0\n", + __FILE__, __LINE__); +#endif + success = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + clear(); + fZone = zone; + setWeekData(aLocale, nullptr, success); +} + +// ------------------------------------- + +Calendar::Calendar(const TimeZone& zone, const Locale& aLocale, UErrorCode& success) +: UObject(), +fIsTimeSet(false), +fAreFieldsSet(false), +fAreAllFieldsSet(false), +fAreFieldsVirtuallySet(false), +fNextStamp((int32_t)kMinimumUserStamp), +fTime(0), +fLenient(true), +fZone(nullptr), +fRepeatedWallTime(UCAL_WALLTIME_LAST), +fSkippedWallTime(UCAL_WALLTIME_LAST) +{ + validLocale[0] = 0; + actualLocale[0] = 0; + if (U_FAILURE(success)) { + return; + } + clear(); + fZone = zone.clone(); + if (fZone == nullptr) { + success = U_MEMORY_ALLOCATION_ERROR; + } + setWeekData(aLocale, nullptr, success); +} + +// ------------------------------------- + +Calendar::~Calendar() +{ + delete fZone; +} + +// ------------------------------------- + +Calendar::Calendar(const Calendar &source) +: UObject(source) +{ + fZone = nullptr; + *this = source; +} + +// ------------------------------------- + +Calendar & +Calendar::operator=(const Calendar &right) +{ + if (this != &right) { + uprv_arrayCopy(right.fFields, fFields, UCAL_FIELD_COUNT); + uprv_arrayCopy(right.fIsSet, fIsSet, UCAL_FIELD_COUNT); + uprv_arrayCopy(right.fStamp, fStamp, UCAL_FIELD_COUNT); + fTime = right.fTime; + fIsTimeSet = right.fIsTimeSet; + fAreAllFieldsSet = right.fAreAllFieldsSet; + fAreFieldsSet = right.fAreFieldsSet; + fAreFieldsVirtuallySet = right.fAreFieldsVirtuallySet; + fLenient = right.fLenient; + fRepeatedWallTime = right.fRepeatedWallTime; + fSkippedWallTime = right.fSkippedWallTime; + delete fZone; + fZone = nullptr; + if (right.fZone != nullptr) { + fZone = right.fZone->clone(); + } + fFirstDayOfWeek = right.fFirstDayOfWeek; + fMinimalDaysInFirstWeek = right.fMinimalDaysInFirstWeek; + fWeekendOnset = right.fWeekendOnset; + fWeekendOnsetMillis = right.fWeekendOnsetMillis; + fWeekendCease = right.fWeekendCease; + fWeekendCeaseMillis = right.fWeekendCeaseMillis; + fNextStamp = right.fNextStamp; + uprv_strncpy(validLocale, right.validLocale, sizeof(validLocale)); + uprv_strncpy(actualLocale, right.actualLocale, sizeof(actualLocale)); + validLocale[sizeof(validLocale)-1] = 0; + actualLocale[sizeof(validLocale)-1] = 0; + } + + return *this; +} + +// ------------------------------------- + +Calendar* U_EXPORT2 +Calendar::createInstance(UErrorCode& success) +{ + return createInstance(TimeZone::createDefault(), Locale::getDefault(), success); +} + +// ------------------------------------- + +Calendar* U_EXPORT2 +Calendar::createInstance(const TimeZone& zone, UErrorCode& success) +{ + return createInstance(zone, Locale::getDefault(), success); +} + +// ------------------------------------- + +Calendar* U_EXPORT2 +Calendar::createInstance(const Locale& aLocale, UErrorCode& success) +{ + return createInstance(TimeZone::forLocaleOrDefault(aLocale), aLocale, success); +} + +// ------------------------------------- Adopting + +// Note: this is the bottleneck that actually calls the service routines. + +Calendar * U_EXPORT2 +Calendar::makeInstance(const Locale& aLocale, UErrorCode& success) { + if (U_FAILURE(success)) { + return nullptr; + } + + Locale actualLoc; + UObject* u = nullptr; + +#if !UCONFIG_NO_SERVICE + if (isCalendarServiceUsed()) { + u = getCalendarService(success)->get(aLocale, LocaleKey::KIND_ANY, &actualLoc, success); + } + else +#endif + { + u = createStandardCalendar(getCalendarTypeForLocale(aLocale.getName()), aLocale, success); + } + Calendar* c = nullptr; + + if(U_FAILURE(success) || !u) { + if(U_SUCCESS(success)) { // Propagate some kind of err + success = U_INTERNAL_PROGRAM_ERROR; + } + return nullptr; + } + +#if !UCONFIG_NO_SERVICE + const UnicodeString* str = dynamic_cast(u); + if(str != nullptr) { + // It's a unicode string telling us what type of calendar to load ("gregorian", etc) + // Create a Locale over this string + Locale l(""); + LocaleUtility::initLocaleFromName(*str, l); + +#ifdef U_DEBUG_CALSVC + fprintf(stderr, "Calendar::createInstance(%s), looking up [%s]\n", aLocale.getName(), l.getName()); +#endif + + Locale actualLoc2; + delete u; + u = nullptr; + + // Don't overwrite actualLoc, since the actual loc from this call + // may be something like "@calendar=gregorian" -- TODO investigate + // further... + c = (Calendar*)getCalendarService(success)->get(l, LocaleKey::KIND_ANY, &actualLoc2, success); + + if(U_FAILURE(success) || !c) { + if(U_SUCCESS(success)) { + success = U_INTERNAL_PROGRAM_ERROR; // Propagate some err + } + return nullptr; + } + + str = dynamic_cast(c); + if(str != nullptr) { + // recursed! Second lookup returned a UnicodeString. + // Perhaps DefaultCalendar{} was set to another locale. +#ifdef U_DEBUG_CALSVC + char tmp[200]; + // Extract a char* out of it.. + int32_t len = str->length(); + int32_t actLen = sizeof(tmp)-1; + if(len > actLen) { + len = actLen; + } + str->extract(0,len,tmp); + tmp[len]=0; + + fprintf(stderr, "err - recursed, 2nd lookup was unistring %s\n", tmp); +#endif + success = U_MISSING_RESOURCE_ERROR; // requested a calendar type which could NOT be found. + delete c; + return nullptr; + } +#ifdef U_DEBUG_CALSVC + fprintf(stderr, "%p: setting week count data to locale %s, actual locale %s\n", c, (const char*)aLocale.getName(), (const char *)actualLoc.getName()); +#endif + c->setWeekData(aLocale, c->getType(), success); // set the correct locale (this was an indirect calendar) + + char keyword[ULOC_FULLNAME_CAPACITY] = ""; + UErrorCode tmpStatus = U_ZERO_ERROR; + l.getKeywordValue("calendar", keyword, ULOC_FULLNAME_CAPACITY, tmpStatus); + if (U_SUCCESS(tmpStatus) && uprv_strcmp(keyword, "iso8601") == 0) { + c->setFirstDayOfWeek(UCAL_MONDAY); + c->setMinimalDaysInFirstWeek(4); + } + } + else +#endif /* UCONFIG_NO_SERVICE */ + { + // a calendar was returned - we assume the factory did the right thing. + c = (Calendar*)u; + } + + return c; +} + +Calendar* U_EXPORT2 +Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& success) +{ + LocalPointer zonePtr(zone); + const SharedCalendar *shared = nullptr; + UnifiedCache::getByLocale(aLocale, shared, success); + if (U_FAILURE(success)) { + return nullptr; + } + Calendar *c = (*shared)->clone(); + shared->removeRef(); + if (c == nullptr) { + success = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + // Now, reset calendar to default state: + c->adoptTimeZone(zonePtr.orphan()); // Set the correct time zone + c->setTimeInMillis(getNow(), success); // let the new calendar have the current time. + + return c; +} + +// ------------------------------------- + +Calendar* U_EXPORT2 +Calendar::createInstance(const TimeZone& zone, const Locale& aLocale, UErrorCode& success) +{ + Calendar* c = createInstance(aLocale, success); + if(U_SUCCESS(success) && c) { + c->setTimeZone(zone); + } + return c; +} + +// ------------------------------------- + +void U_EXPORT2 +Calendar::getCalendarTypeFromLocale( + const Locale &aLocale, + char *typeBuffer, + int32_t typeBufferSize, + UErrorCode &success) { + const SharedCalendar *shared = nullptr; + UnifiedCache::getByLocale(aLocale, shared, success); + if (U_FAILURE(success)) { + return; + } + uprv_strncpy(typeBuffer, (*shared)->getType(), typeBufferSize); + shared->removeRef(); + if (typeBuffer[typeBufferSize - 1]) { + success = U_BUFFER_OVERFLOW_ERROR; + } +} + +bool +Calendar::operator==(const Calendar& that) const +{ + UErrorCode status = U_ZERO_ERROR; + return isEquivalentTo(that) && + getTimeInMillis(status) == that.getTimeInMillis(status) && + U_SUCCESS(status); +} + +UBool +Calendar::isEquivalentTo(const Calendar& other) const +{ + return typeid(*this) == typeid(other) && + fLenient == other.fLenient && + fRepeatedWallTime == other.fRepeatedWallTime && + fSkippedWallTime == other.fSkippedWallTime && + fFirstDayOfWeek == other.fFirstDayOfWeek && + fMinimalDaysInFirstWeek == other.fMinimalDaysInFirstWeek && + fWeekendOnset == other.fWeekendOnset && + fWeekendOnsetMillis == other.fWeekendOnsetMillis && + fWeekendCease == other.fWeekendCease && + fWeekendCeaseMillis == other.fWeekendCeaseMillis && + *fZone == *other.fZone; +} + +// ------------------------------------- + +UBool +Calendar::equals(const Calendar& when, UErrorCode& status) const +{ + return (this == &when || + getTime(status) == when.getTime(status)); +} + +// ------------------------------------- + +UBool +Calendar::before(const Calendar& when, UErrorCode& status) const +{ + return (this != &when && + getTimeInMillis(status) < when.getTimeInMillis(status)); +} + +// ------------------------------------- + +UBool +Calendar::after(const Calendar& when, UErrorCode& status) const +{ + return (this != &when && + getTimeInMillis(status) > when.getTimeInMillis(status)); +} + +// ------------------------------------- + + +const Locale* U_EXPORT2 +Calendar::getAvailableLocales(int32_t& count) +{ + return Locale::getAvailableLocales(count); +} + +// ------------------------------------- + +StringEnumeration* U_EXPORT2 +Calendar::getKeywordValuesForLocale(const char* key, + const Locale& locale, UBool commonlyUsed, UErrorCode& status) +{ + // This is a wrapper over ucal_getKeywordValuesForLocale + UEnumeration *uenum = ucal_getKeywordValuesForLocale(key, locale.getName(), + commonlyUsed, &status); + if (U_FAILURE(status)) { + uenum_close(uenum); + return nullptr; + } + UStringEnumeration* ustringenum = new UStringEnumeration(uenum); + if (ustringenum == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return ustringenum; +} + +// ------------------------------------- + +UDate U_EXPORT2 +Calendar::getNow() +{ + return uprv_getUTCtime(); // return as milliseconds +} + +// ------------------------------------- + +/** +* Gets this Calendar's current time as a long. +* @return the current time as UTC milliseconds from the epoch. +*/ +double +Calendar::getTimeInMillis(UErrorCode& status) const +{ + if(U_FAILURE(status)) + return 0.0; + + if ( ! fIsTimeSet) + ((Calendar*)this)->updateTime(status); + + /* Test for buffer overflows */ + if(U_FAILURE(status)) { + return 0.0; + } + return fTime; +} + +// ------------------------------------- + +/** +* Sets this Calendar's current time from the given long value. +* A status of U_ILLEGAL_ARGUMENT_ERROR is set when millis is +* outside the range permitted by a Calendar object when not in lenient mode. +* when in lenient mode the out of range values are pinned to their respective min/max. +* @param date the new time in UTC milliseconds from the epoch. +*/ +void +Calendar::setTimeInMillis( double millis, UErrorCode& status ) { + if(U_FAILURE(status)) + return; + + if (millis > MAX_MILLIS) { + if(isLenient()) { + millis = MAX_MILLIS; + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + } else if (millis < MIN_MILLIS) { + if(isLenient()) { + millis = MIN_MILLIS; + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + } + + fTime = millis; + fAreFieldsSet = fAreAllFieldsSet = false; + fIsTimeSet = fAreFieldsVirtuallySet = true; + + for (int32_t i=0; icomplete(status); // Cast away const + return U_SUCCESS(status) ? fFields[field] : 0; +} + +// ------------------------------------- + +void +Calendar::set(UCalendarDateFields field, int32_t value) +{ + if (fAreFieldsVirtuallySet) { + UErrorCode ec = U_ZERO_ERROR; + computeFields(ec); + } + fFields[field] = value; + /* Ensure that the fNextStamp value doesn't go pass max value for int32_t */ + if (fNextStamp == STAMP_MAX) { + recalculateStamp(); + } + fStamp[field] = fNextStamp++; + fIsSet[field] = true; // Remove later + fIsTimeSet = fAreFieldsSet = fAreFieldsVirtuallySet = false; +} + +// ------------------------------------- + +void +Calendar::set(int32_t year, int32_t month, int32_t date) +{ + set(UCAL_YEAR, year); + set(UCAL_MONTH, month); + set(UCAL_DATE, date); +} + +// ------------------------------------- + +void +Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute) +{ + set(UCAL_YEAR, year); + set(UCAL_MONTH, month); + set(UCAL_DATE, date); + set(UCAL_HOUR_OF_DAY, hour); + set(UCAL_MINUTE, minute); +} + +// ------------------------------------- + +void +Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute, int32_t second) +{ + set(UCAL_YEAR, year); + set(UCAL_MONTH, month); + set(UCAL_DATE, date); + set(UCAL_HOUR_OF_DAY, hour); + set(UCAL_MINUTE, minute); + set(UCAL_SECOND, second); +} + +// ------------------------------------- +int32_t Calendar::getRelatedYear(UErrorCode &status) const +{ + return get(UCAL_EXTENDED_YEAR, status); +} + +// ------------------------------------- +void Calendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year); +} + +// ------------------------------------- + +void +Calendar::clear() +{ + for (int32_t i=0; i bestStamp) { + bestStamp = fStamp[i]; + } + } + return bestStamp; +} + + +// ------------------------------------- + +void +Calendar::complete(UErrorCode& status) +{ + if (U_FAILURE(status)) { + return; + } + if (!fIsTimeSet) { + updateTime(status); + /* Test for buffer overflows */ + if(U_FAILURE(status)) { + return; + } + } + if (!fAreFieldsSet) { + computeFields(status); // fills in unset fields + /* Test for buffer overflows */ + if(U_FAILURE(status)) { + return; + } + fAreFieldsSet = true; + fAreAllFieldsSet = true; + } +} + +//------------------------------------------------------------------------- +// Protected utility methods for use by subclasses. These are very handy +// for implementing add, roll, and computeFields. +//------------------------------------------------------------------------- + +/** +* Adjust the specified field so that it is within +* the allowable range for the date to which this calendar is set. +* For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH} +* field for a calendar set to April 31 would cause it to be set +* to April 30. +*

+* Subclassing: +*
+* This utility method is intended for use by subclasses that need to implement +* their own overrides of {@link #roll roll} and {@link #add add}. +*

+* Note: +* pinField is implemented in terms of +* {@link #getActualMinimum getActualMinimum} +* and {@link #getActualMaximum getActualMaximum}. If either of those methods uses +* a slow, iterative algorithm for a particular field, it would be +* unwise to attempt to call pinField for that field. If you +* really do need to do so, you should override this method to do +* something more efficient for that field. +*

+* @param field The calendar field whose value should be pinned. +* +* @see #getActualMinimum +* @see #getActualMaximum +* @stable ICU 2.0 +*/ +void Calendar::pinField(UCalendarDateFields field, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + int32_t max = getActualMaximum(field, status); + int32_t min = getActualMinimum(field, status); + + if (fFields[field] > max) { + set(field, max); + } else if (fFields[field] < min) { + set(field, min); + } +} + + +void Calendar::computeFields(UErrorCode &ec) +{ + if (U_FAILURE(ec)) { + return; + } + // Compute local wall millis + double localMillis = internalGetTime(); + int32_t rawOffset, dstOffset; + getTimeZone().getOffset(localMillis, false, rawOffset, dstOffset, ec); + if (U_FAILURE(ec)) { + return; + } + localMillis += (rawOffset + dstOffset); + + // Mark fields as set. Do this before calling handleComputeFields(). + uint32_t mask = //fInternalSetMask; + (1 << UCAL_ERA) | + (1 << UCAL_YEAR) | + (1 << UCAL_MONTH) | + (1 << UCAL_DAY_OF_MONTH) | // = UCAL_DATE + (1 << UCAL_DAY_OF_YEAR) | + (1 << UCAL_EXTENDED_YEAR) | + (1 << UCAL_ORDINAL_MONTH); + + for (int32_t i=0; i>= 1; + } + + // We used to check for and correct extreme millis values (near + // Long.MIN_VALUE or Long.MAX_VALUE) here. Such values would cause + // overflows from positive to negative (or vice versa) and had to + // be manually tweaked. We no longer need to do this because we + // have limited the range of supported dates to those that have a + // Julian day that fits into an int. This allows us to implement a + // JULIAN_DAY field and also removes some inelegant code. - Liu + // 11/6/00 + + int32_t millisInDay; + int32_t days = ClockMath::floorDivide(localMillis, kOneDay, &millisInDay); + + internalSet(UCAL_JULIAN_DAY,days + kEpochStartAsJulianDay); + +#if defined (U_DEBUG_CAL) + //fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n", + //__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis); +#endif + + computeGregorianAndDOWFields(fFields[UCAL_JULIAN_DAY], ec); + + // Call framework method to have subclass compute its fields. + // These must include, at a minimum, MONTH, DAY_OF_MONTH, + // EXTENDED_YEAR, YEAR, DAY_OF_YEAR. This method will call internalSet(), + // which will update stamp[]. + handleComputeFields(fFields[UCAL_JULIAN_DAY], ec); + + // Compute week-related fields, based on the subclass-computed + // fields computed by handleComputeFields(). + computeWeekFields(ec); + + // Compute time-related fields. These are independent of the date and + // of the subclass algorithm. They depend only on the local zone + // wall milliseconds in day. + if (U_FAILURE(ec)) { + return; + } + + fFields[UCAL_MILLISECONDS_IN_DAY] = millisInDay; + U_ASSERT(getMinimum(UCAL_MILLISECONDS_IN_DAY) <= + fFields[UCAL_MILLISECONDS_IN_DAY]); + U_ASSERT(fFields[UCAL_MILLISECONDS_IN_DAY] <= + getMaximum(UCAL_MILLISECONDS_IN_DAY)); + + fFields[UCAL_MILLISECOND] = millisInDay % 1000; + U_ASSERT(getMinimum(UCAL_MILLISECOND) <= fFields[UCAL_MILLISECOND]); + U_ASSERT(fFields[UCAL_MILLISECOND] <= getMaximum(UCAL_MILLISECOND)); + + millisInDay /= 1000; + fFields[UCAL_SECOND] = millisInDay % 60; + U_ASSERT(getMinimum(UCAL_SECOND) <= fFields[UCAL_SECOND]); + U_ASSERT(fFields[UCAL_SECOND] <= getMaximum(UCAL_SECOND)); + + millisInDay /= 60; + fFields[UCAL_MINUTE] = millisInDay % 60; + U_ASSERT(getMinimum(UCAL_MINUTE) <= fFields[UCAL_MINUTE]); + U_ASSERT(fFields[UCAL_MINUTE] <= getMaximum(UCAL_MINUTE)); + + millisInDay /= 60; + fFields[UCAL_HOUR_OF_DAY] = millisInDay; + U_ASSERT(getMinimum(UCAL_HOUR_OF_DAY) <= fFields[UCAL_HOUR_OF_DAY]); + U_ASSERT(fFields[UCAL_HOUR_OF_DAY] <= getMaximum(UCAL_HOUR_OF_DAY)); + + fFields[UCAL_AM_PM] = millisInDay / 12; // Assume AM == 0 + U_ASSERT(getMinimum(UCAL_AM_PM) <= fFields[UCAL_AM_PM]); + U_ASSERT(fFields[UCAL_AM_PM] <= getMaximum(UCAL_AM_PM)); + + fFields[UCAL_HOUR] = millisInDay % 12; + U_ASSERT(getMinimum(UCAL_HOUR) <= fFields[UCAL_HOUR]); + U_ASSERT(fFields[UCAL_HOUR] <= getMaximum(UCAL_HOUR)); + + fFields[UCAL_ZONE_OFFSET] = rawOffset; + U_ASSERT(getMinimum(UCAL_ZONE_OFFSET) <= fFields[UCAL_ZONE_OFFSET]); + U_ASSERT(fFields[UCAL_ZONE_OFFSET] <= getMaximum(UCAL_ZONE_OFFSET)); + + fFields[UCAL_DST_OFFSET] = dstOffset; + U_ASSERT(getMinimum(UCAL_DST_OFFSET) <= fFields[UCAL_DST_OFFSET]); + U_ASSERT(fFields[UCAL_DST_OFFSET] <= getMaximum(UCAL_DST_OFFSET)); +} + +uint8_t Calendar::julianDayToDayOfWeek(double julian) +{ + // If julian is negative, then julian%7 will be negative, so we adjust + // accordingly. We add 1 because Julian day 0 is Monday. + int8_t dayOfWeek = (int8_t) uprv_fmod(julian + 1, 7); + + uint8_t result = (uint8_t)(dayOfWeek + ((dayOfWeek < 0) ? (7+UCAL_SUNDAY ) : UCAL_SUNDAY)); + return result; +} + +/** +* Compute the Gregorian calendar year, month, and day of month from +* the given Julian day. These values are not stored in fields, but in +* member variables gregorianXxx. Also compute the DAY_OF_WEEK and +* DOW_LOCAL fields. +*/ +void Calendar::computeGregorianAndDOWFields(int32_t julianDay, UErrorCode &ec) +{ + computeGregorianFields(julianDay, ec); + if (U_FAILURE(ec)) { + return; + } + + // Compute day of week: JD 0 = Monday + int32_t dow = julianDayToDayOfWeek(julianDay); + internalSet(UCAL_DAY_OF_WEEK,dow); + + // Calculate 1-based localized day of week + int32_t dowLocal = dow - getFirstDayOfWeek() + 1; + if (dowLocal < 1) { + dowLocal += 7; + } + internalSet(UCAL_DOW_LOCAL,dowLocal); + fFields[UCAL_DOW_LOCAL] = dowLocal; +} + +/** +* Compute the Gregorian calendar year, month, and day of month from the +* Julian day. These values are not stored in fields, but in member +* variables gregorianXxx. They are used for time zone computations and by +* subclasses that are Gregorian derivatives. Subclasses may call this +* method to perform a Gregorian calendar millis->fields computation. +*/ +void Calendar::computeGregorianFields(int32_t julianDay, UErrorCode& ec) { + if (U_FAILURE(ec)) { + return; + } + int32_t gregorianDayOfWeekUnused; + Grego::dayToFields(julianDay - kEpochStartAsJulianDay, fGregorianYear, fGregorianMonth, fGregorianDayOfMonth, gregorianDayOfWeekUnused, fGregorianDayOfYear); +} + +/** +* Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH, +* DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL from EXTENDED_YEAR, YEAR, +* DAY_OF_WEEK, and DAY_OF_YEAR. The latter fields are computed by the +* subclass based on the calendar system. +* +*

The YEAR_WOY field is computed simplistically. It is equal to YEAR +* most of the time, but at the year boundary it may be adjusted to YEAR-1 +* or YEAR+1 to reflect the overlap of a week into an adjacent year. In +* this case, a simple increment or decrement is performed on YEAR, even +* though this may yield an invalid YEAR value. For instance, if the YEAR +* is part of a calendar system with an N-year cycle field CYCLE, then +* incrementing the YEAR may involve incrementing CYCLE and setting YEAR +* back to 0 or 1. This is not handled by this code, and in fact cannot be +* simply handled without having subclasses define an entire parallel set of +* fields for fields larger than or equal to a year. This additional +* complexity is not warranted, since the intention of the YEAR_WOY field is +* to support ISO 8601 notation, so it will typically be used with a +* proleptic Gregorian calendar, which has no field larger than a year. +*/ +void Calendar::computeWeekFields(UErrorCode &ec) { + if(U_FAILURE(ec)) { + return; + } + int32_t eyear = fFields[UCAL_EXTENDED_YEAR]; + int32_t dayOfWeek = fFields[UCAL_DAY_OF_WEEK]; + int32_t dayOfYear = fFields[UCAL_DAY_OF_YEAR]; + + // WEEK_OF_YEAR start + // Compute the week of the year. For the Gregorian calendar, valid week + // numbers run from 1 to 52 or 53, depending on the year, the first day + // of the week, and the minimal days in the first week. For other + // calendars, the valid range may be different -- it depends on the year + // length. Days at the start of the year may fall into the last week of + // the previous year; days at the end of the year may fall into the + // first week of the next year. ASSUME that the year length is less than + // 7000 days. + int32_t yearOfWeekOfYear = eyear; + int32_t relDow = (dayOfWeek + 7 - getFirstDayOfWeek()) % 7; // 0..6 + int32_t relDowJan1 = (dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek()) % 7; // 0..6 + int32_t woy = (dayOfYear - 1 + relDowJan1) / 7; // 0..53 + if ((7 - relDowJan1) >= getMinimalDaysInFirstWeek()) { + ++woy; + } + + // Adjust for weeks at the year end that overlap into the previous or + // next calendar year. + if (woy == 0) { + // We are the last week of the previous year. + // Check to see if we are in the last week; if so, we need + // to handle the case in which we are the first week of the + // next year. + + int32_t prevDoy = dayOfYear + handleGetYearLength(eyear - 1); + woy = weekNumber(prevDoy, dayOfWeek); + yearOfWeekOfYear--; + } else { + int32_t lastDoy = handleGetYearLength(eyear); + // Fast check: For it to be week 1 of the next year, the DOY + // must be on or after L-5, where L is yearLength(), then it + // cannot possibly be week 1 of the next year: + // L-5 L + // doy: 359 360 361 362 363 364 365 001 + // dow: 1 2 3 4 5 6 7 + if (dayOfYear >= (lastDoy - 5)) { + int32_t lastRelDow = (relDow + lastDoy - dayOfYear) % 7; + if (lastRelDow < 0) { + lastRelDow += 7; + } + if (((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) && + ((dayOfYear + 7 - relDow) > lastDoy)) { + woy = 1; + yearOfWeekOfYear++; + } + } + } + fFields[UCAL_WEEK_OF_YEAR] = woy; + fFields[UCAL_YEAR_WOY] = yearOfWeekOfYear; + // min/max of years are not constrains for caller, so not assert here. + // WEEK_OF_YEAR end + + int32_t dayOfMonth = fFields[UCAL_DAY_OF_MONTH]; + fFields[UCAL_WEEK_OF_MONTH] = weekNumber(dayOfMonth, dayOfWeek); + U_ASSERT(getMinimum(UCAL_WEEK_OF_MONTH) <= fFields[UCAL_WEEK_OF_MONTH]); + U_ASSERT(fFields[UCAL_WEEK_OF_MONTH] <= getMaximum(UCAL_WEEK_OF_MONTH)); + + fFields[UCAL_DAY_OF_WEEK_IN_MONTH] = (dayOfMonth-1) / 7 + 1; + U_ASSERT(getMinimum(UCAL_DAY_OF_WEEK_IN_MONTH) <= + fFields[UCAL_DAY_OF_WEEK_IN_MONTH]); + U_ASSERT(fFields[UCAL_DAY_OF_WEEK_IN_MONTH] <= + getMaximum(UCAL_DAY_OF_WEEK_IN_MONTH)); + +#if defined (U_DEBUG_CAL) + if(fFields[UCAL_DAY_OF_WEEK_IN_MONTH]==0) fprintf(stderr, "%s:%d: DOWIM %d on %g\n", + __FILE__, __LINE__,fFields[UCAL_DAY_OF_WEEK_IN_MONTH], fTime); +#endif +} + + +int32_t Calendar::weekNumber(int32_t desiredDay, int32_t dayOfPeriod, int32_t dayOfWeek) +{ + // Determine the day of the week of the first day of the period + // in question (either a year or a month). Zero represents the + // first day of the week on this calendar. + int32_t periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek() - dayOfPeriod + 1) % 7; + if (periodStartDayOfWeek < 0) periodStartDayOfWeek += 7; + + // Compute the week number. Initially, ignore the first week, which + // may be fractional (or may not be). We add periodStartDayOfWeek in + // order to fill out the first week, if it is fractional. + int32_t weekNo = (desiredDay + periodStartDayOfWeek - 1)/7; + + // If the first week is long enough, then count it. If + // the minimal days in the first week is one, or if the period start + // is zero, we always increment weekNo. + if ((7 - periodStartDayOfWeek) >= getMinimalDaysInFirstWeek()) ++weekNo; + + return weekNo; +} + +void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode& status) +{ + if (U_FAILURE(status)) { + return; + } + int32_t month = getGregorianMonth(); + internalSet(UCAL_MONTH, month); + internalSet(UCAL_ORDINAL_MONTH, month); + internalSet(UCAL_DAY_OF_MONTH, getGregorianDayOfMonth()); + internalSet(UCAL_DAY_OF_YEAR, getGregorianDayOfYear()); + int32_t eyear = getGregorianYear(); + internalSet(UCAL_EXTENDED_YEAR, eyear); + int32_t era = GregorianCalendar::AD; + if (eyear < 1) { + era = GregorianCalendar::BC; + eyear = 1 - eyear; + } + internalSet(UCAL_ERA, era); + internalSet(UCAL_YEAR, eyear); +} +// ------------------------------------- + + +void Calendar::roll(EDateFields field, int32_t amount, UErrorCode& status) +{ + roll((UCalendarDateFields)field, amount, status); +} + +void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) UPRV_NO_SANITIZE_UNDEFINED { + if (amount == 0) { + return; // Nothing to do + } + + complete(status); + + if(U_FAILURE(status)) { + return; + } + switch (field) { + case UCAL_DAY_OF_MONTH: + case UCAL_AM_PM: + case UCAL_MINUTE: + case UCAL_SECOND: + case UCAL_MILLISECOND: + case UCAL_MILLISECONDS_IN_DAY: + case UCAL_ERA: + // These are the standard roll instructions. These work for all + // simple cases, that is, cases in which the limits are fixed, such + // as the hour, the day of the month, and the era. + { + int32_t min = getActualMinimum(field,status); + int32_t max = getActualMaximum(field,status); + int32_t gap = max - min + 1; + + int32_t value = internalGet(field) + amount; + value = (value - min) % gap; + if (value < 0) { + value += gap; + } + value += min; + + set(field, value); + return; + } + + case UCAL_HOUR: + case UCAL_HOUR_OF_DAY: + // Rolling the hour is difficult on the ONSET and CEASE days of + // daylight savings. For example, if the change occurs at + // 2 AM, we have the following progression: + // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst + // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std + // To get around this problem we don't use fields; we manipulate + // the time in millis directly. + { + // Assume min == 0 in calculations below + double start = getTimeInMillis(status); + int32_t oldHour = internalGet(field); + int32_t max = getMaximum(field); + int32_t newHour = (oldHour + amount) % (max + 1); + if (newHour < 0) { + newHour += max + 1; + } + setTimeInMillis(start + kOneHour * (newHour - oldHour),status); + return; + } + + case UCAL_MONTH: + case UCAL_ORDINAL_MONTH: + // Rolling the month involves both pinning the final value + // and adjusting the DAY_OF_MONTH if necessary. We only adjust the + // DAY_OF_MONTH if, after updating the MONTH field, it is illegal. + // E.g., .roll(MONTH, 1) -> or . + { + int32_t max = getActualMaximum(UCAL_MONTH, status); + int32_t mon = (internalGet(UCAL_MONTH) + amount) % (max+1); + + if (mon < 0) { + mon += (max + 1); + } + set(UCAL_MONTH, mon); + + // Keep the day of month in range. We don't want to spill over + // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 -> + // mar3. + pinField(UCAL_DAY_OF_MONTH,status); + return; + } + + case UCAL_YEAR: + case UCAL_YEAR_WOY: + { + // * If era==0 and years go backwards in time, change sign of amount. + // * Until we have new API per #9393, we temporarily hardcode knowledge of + // which calendars have era 0 years that go backwards. + UBool era0WithYearsThatGoBackwards = false; + int32_t era = get(UCAL_ERA, status); + if (era == 0) { + const char * calType = getType(); + if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) { + amount = -amount; + era0WithYearsThatGoBackwards = true; + } + } + int32_t newYear = internalGet(field) + amount; + if (era > 0 || newYear >= 1) { + int32_t maxYear = getActualMaximum(field, status); + if (maxYear < 32768) { + // this era has real bounds, roll should wrap years + if (newYear < 1) { + newYear = maxYear - ((-newYear) % maxYear); + } else if (newYear > maxYear) { + newYear = ((newYear - 1) % maxYear) + 1; + } + // else era is unbounded, just pin low year instead of wrapping + } else if (newYear < 1) { + newYear = 1; + } + // else we are in era 0 with newYear < 1; + // calendars with years that go backwards must pin the year value at 0, + // other calendars can have years < 0 in era 0 + } else if (era0WithYearsThatGoBackwards) { + newYear = 1; + } + set(field, newYear); + pinField(UCAL_MONTH,status); + pinField(UCAL_ORDINAL_MONTH,status); + pinField(UCAL_DAY_OF_MONTH,status); + return; + } + + case UCAL_EXTENDED_YEAR: + // Rolling the year can involve pinning the DAY_OF_MONTH. + set(field, internalGet(field) + amount); + pinField(UCAL_MONTH,status); + pinField(UCAL_ORDINAL_MONTH,status); + pinField(UCAL_DAY_OF_MONTH,status); + return; + + case UCAL_WEEK_OF_MONTH: + { + // This is tricky, because during the roll we may have to shift + // to a different day of the week. For example: + + // s m t w r f s + // 1 2 3 4 5 + // 6 7 8 9 10 11 12 + + // When rolling from the 6th or 7th back one week, we go to the + // 1st (assuming that the first partial week counts). The same + // thing happens at the end of the month. + + // The other tricky thing is that we have to figure out whether + // the first partial week actually counts or not, based on the + // minimal first days in the week. And we have to use the + // correct first day of the week to delineate the week + // boundaries. + + // Here's our algorithm. First, we find the real boundaries of + // the month. Then we discard the first partial week if it + // doesn't count in this locale. Then we fill in the ends with + // phantom days, so that the first partial week and the last + // partial week are full weeks. We then have a nice square + // block of weeks. We do the usual rolling within this block, + // as is done elsewhere in this method. If we wind up on one of + // the phantom days that we added, we recognize this and pin to + // the first or the last day of the month. Easy, eh? + + // Normalize the DAY_OF_WEEK so that 0 is the first day of the week + // in this locale. We have dow in 0..6. + int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); + if (dow < 0) dow += 7; + + // Find the day of the week (normalized for locale) for the first + // of the month. + int32_t fdm = (dow - internalGet(UCAL_DAY_OF_MONTH) + 1) % 7; + if (fdm < 0) fdm += 7; + + // Get the first day of the first full week of the month, + // including phantom days, if any. Figure out if the first week + // counts or not; if it counts, then fill in phantom days. If + // not, advance to the first real full week (skip the partial week). + int32_t start; + if ((7 - fdm) < getMinimalDaysInFirstWeek()) + start = 8 - fdm; // Skip the first partial week + else + start = 1 - fdm; // This may be zero or negative + + // Get the day of the week (normalized for locale) for the last + // day of the month. + int32_t monthLen = getActualMaximum(UCAL_DAY_OF_MONTH, status); + int32_t ldm = (monthLen - internalGet(UCAL_DAY_OF_MONTH) + dow) % 7; + // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here. + + // Get the limit day for the blocked-off rectangular month; that + // is, the day which is one past the last day of the month, + // after the month has already been filled in with phantom days + // to fill out the last week. This day has a normalized DOW of 0. + int32_t limit = monthLen + 7 - ldm; + + // Now roll between start and (limit - 1). + int32_t gap = limit - start; + int32_t day_of_month = (internalGet(UCAL_DAY_OF_MONTH) + amount*7 - + start) % gap; + if (day_of_month < 0) day_of_month += gap; + day_of_month += start; + + // Finally, pin to the real start and end of the month. + if (day_of_month < 1) day_of_month = 1; + if (day_of_month > monthLen) day_of_month = monthLen; + + // Set the DAY_OF_MONTH. We rely on the fact that this field + // takes precedence over everything else (since all other fields + // are also set at this point). If this fact changes (if the + // disambiguation algorithm changes) then we will have to unset + // the appropriate fields here so that DAY_OF_MONTH is attended + // to. + set(UCAL_DAY_OF_MONTH, day_of_month); + return; + } + case UCAL_WEEK_OF_YEAR: + { + // This follows the outline of WEEK_OF_MONTH, except it applies + // to the whole year. Please see the comment for WEEK_OF_MONTH + // for general notes. + + // Normalize the DAY_OF_WEEK so that 0 is the first day of the week + // in this locale. We have dow in 0..6. + int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); + if (dow < 0) dow += 7; + + // Find the day of the week (normalized for locale) for the first + // of the year. + int32_t fdy = (dow - internalGet(UCAL_DAY_OF_YEAR) + 1) % 7; + if (fdy < 0) fdy += 7; + + // Get the first day of the first full week of the year, + // including phantom days, if any. Figure out if the first week + // counts or not; if it counts, then fill in phantom days. If + // not, advance to the first real full week (skip the partial week). + int32_t start; + if ((7 - fdy) < getMinimalDaysInFirstWeek()) + start = 8 - fdy; // Skip the first partial week + else + start = 1 - fdy; // This may be zero or negative + + // Get the day of the week (normalized for locale) for the last + // day of the year. + int32_t yearLen = getActualMaximum(UCAL_DAY_OF_YEAR,status); + int32_t ldy = (yearLen - internalGet(UCAL_DAY_OF_YEAR) + dow) % 7; + // We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here. + + // Get the limit day for the blocked-off rectangular year; that + // is, the day which is one past the last day of the year, + // after the year has already been filled in with phantom days + // to fill out the last week. This day has a normalized DOW of 0. + int32_t limit = yearLen + 7 - ldy; + + // Now roll between start and (limit - 1). + int32_t gap = limit - start; + int32_t day_of_year = (internalGet(UCAL_DAY_OF_YEAR) + amount*7 - + start) % gap; + if (day_of_year < 0) day_of_year += gap; + day_of_year += start; + + // Finally, pin to the real start and end of the month. + if (day_of_year < 1) day_of_year = 1; + if (day_of_year > yearLen) day_of_year = yearLen; + + // Make sure that the year and day of year are attended to by + // clearing other fields which would normally take precedence. + // If the disambiguation algorithm is changed, this section will + // have to be updated as well. + set(UCAL_DAY_OF_YEAR, day_of_year); + clear(UCAL_MONTH); + clear(UCAL_ORDINAL_MONTH); + return; + } + case UCAL_DAY_OF_YEAR: + { + // Roll the day of year using millis. Compute the millis for + // the start of the year, and get the length of the year. + double delta = amount * kOneDay; // Scale up from days to millis + double min2 = internalGet(UCAL_DAY_OF_YEAR)-1; + min2 *= kOneDay; + min2 = internalGetTime() - min2; + + // double min2 = internalGetTime() - (internalGet(UCAL_DAY_OF_YEAR) - 1.0) * kOneDay; + double newtime; + + double yearLength = getActualMaximum(UCAL_DAY_OF_YEAR,status); + double oneYear = yearLength; + oneYear *= kOneDay; + newtime = uprv_fmod((internalGetTime() + delta - min2), oneYear); + if (newtime < 0) newtime += oneYear; + setTimeInMillis(newtime + min2, status); + return; + } + case UCAL_DAY_OF_WEEK: + case UCAL_DOW_LOCAL: + { + // Roll the day of week using millis. Compute the millis for + // the start of the week, using the first day of week setting. + // Restrict the millis to [start, start+7days). + double delta = amount * kOneDay; // Scale up from days to millis + // Compute the number of days before the current day in this + // week. This will be a value 0..6. + int32_t leadDays = internalGet(field); + leadDays -= (field == UCAL_DAY_OF_WEEK) ? getFirstDayOfWeek() : 1; + if (leadDays < 0) leadDays += 7; + double min2 = internalGetTime() - leadDays * kOneDay; + double newtime = uprv_fmod((internalGetTime() + delta - min2), kOneWeek); + if (newtime < 0) newtime += kOneWeek; + setTimeInMillis(newtime + min2, status); + return; + } + case UCAL_DAY_OF_WEEK_IN_MONTH: + { + // Roll the day of week in the month using millis. Determine + // the first day of the week in the month, and then the last, + // and then roll within that range. + double delta = amount * kOneWeek; // Scale up from weeks to millis + // Find the number of same days of the week before this one + // in this month. + int32_t preWeeks = (internalGet(UCAL_DAY_OF_MONTH) - 1) / 7; + // Find the number of same days of the week after this one + // in this month. + int32_t postWeeks = (getActualMaximum(UCAL_DAY_OF_MONTH,status) - + internalGet(UCAL_DAY_OF_MONTH)) / 7; + // From these compute the min and gap millis for rolling. + double min2 = internalGetTime() - preWeeks * kOneWeek; + double gap2 = kOneWeek * (preWeeks + postWeeks + 1); // Must add 1! + // Roll within this range + double newtime = uprv_fmod((internalGetTime() + delta - min2), gap2); + if (newtime < 0) newtime += gap2; + setTimeInMillis(newtime + min2, status); + return; + } + case UCAL_JULIAN_DAY: + set(field, internalGet(field) + amount); + return; + default: + // Other fields cannot be rolled by this method +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: ILLEGAL ARG because of roll on non-rollable field %s\n", + __FILE__, __LINE__,fldName(field)); +#endif + status = U_ILLEGAL_ARGUMENT_ERROR; + } +} + +void Calendar::add(EDateFields field, int32_t amount, UErrorCode& status) +{ + Calendar::add((UCalendarDateFields)field, amount, status); +} + +// ------------------------------------- +void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) +{ + if (U_FAILURE(status)) { + return; + } + if (amount == 0) { + return; // Do nothing! + } + + // We handle most fields in the same way. The algorithm is to add + // a computed amount of millis to the current millis. The only + // wrinkle is with DST (and/or a change to the zone's UTC offset, which + // we'll include with DST) -- for some fields, like the DAY_OF_MONTH, + // we don't want the wall time to shift due to changes in DST. If the + // result of the add operation is to move from DST to Standard, or + // vice versa, we need to adjust by an hour forward or back, + // respectively. For such fields we set keepWallTimeInvariant to true. + + // We only adjust the DST for fields larger than an hour. For + // fields smaller than an hour, we cannot adjust for DST without + // causing problems. for instance, if you add one hour to April 5, + // 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an + // illegal value), but then the adjustment sees the change and + // compensates by subtracting an hour. As a result the time + // doesn't advance at all. + + // For some fields larger than a day, such as a UCAL_MONTH, we pin the + // UCAL_DAY_OF_MONTH. This allows .add(UCAL_MONTH, 1) to be + // , rather than => . + + double delta = amount; // delta in ms + UBool keepWallTimeInvariant = true; + + switch (field) { + case UCAL_ERA: + set(field, get(field, status) + amount); + pinField(UCAL_ERA, status); + return; + + case UCAL_YEAR: + case UCAL_YEAR_WOY: + { + // * If era=0 and years go backwards in time, change sign of amount. + // * Until we have new API per #9393, we temporarily hardcode knowledge of + // which calendars have era 0 years that go backwards. + // * Note that for UCAL_YEAR (but not UCAL_YEAR_WOY) we could instead handle + // this by applying the amount to the UCAL_EXTENDED_YEAR field; but since + // we would still need to handle UCAL_YEAR_WOY as below, might as well + // also handle UCAL_YEAR the same way. + int32_t era = get(UCAL_ERA, status); + if (era == 0) { + const char * calType = getType(); + if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) { + amount = -amount; + } + } + } + // Fall through into normal handling + U_FALLTHROUGH; + case UCAL_EXTENDED_YEAR: + case UCAL_MONTH: + case UCAL_ORDINAL_MONTH: + { + UBool oldLenient = isLenient(); + setLenient(true); + set(field, get(field, status) + amount); + pinField(UCAL_DAY_OF_MONTH, status); + if(oldLenient==false) { + complete(status); /* force recalculate */ + setLenient(oldLenient); + } + } + return; + + case UCAL_WEEK_OF_YEAR: + case UCAL_WEEK_OF_MONTH: + case UCAL_DAY_OF_WEEK_IN_MONTH: + delta *= kOneWeek; + break; + + case UCAL_AM_PM: + delta *= 12 * kOneHour; + break; + + case UCAL_DAY_OF_MONTH: + case UCAL_DAY_OF_YEAR: + case UCAL_DAY_OF_WEEK: + case UCAL_DOW_LOCAL: + case UCAL_JULIAN_DAY: + delta *= kOneDay; + break; + + case UCAL_HOUR_OF_DAY: + case UCAL_HOUR: + delta *= kOneHour; + keepWallTimeInvariant = false; + break; + + case UCAL_MINUTE: + delta *= kOneMinute; + keepWallTimeInvariant = false; + break; + + case UCAL_SECOND: + delta *= kOneSecond; + keepWallTimeInvariant = false; + break; + + case UCAL_MILLISECOND: + case UCAL_MILLISECONDS_IN_DAY: + keepWallTimeInvariant = false; + break; + + default: +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s not addable", + __FILE__, __LINE__, fldName(field)); +#endif + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + // throw new IllegalArgumentException("Calendar.add(" + fieldName(field) + + // ") not supported"); + } + + // In order to keep the wall time invariant (for fields where this is + // appropriate), check the combined DST & ZONE offset before and + // after the add() operation. If it changes, then adjust the millis + // to compensate. + int32_t prevOffset = 0; + int32_t prevWallTime = 0; + if (keepWallTimeInvariant) { + prevOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status); + prevWallTime = get(UCAL_MILLISECONDS_IN_DAY, status); + } + + setTimeInMillis(getTimeInMillis(status) + delta, status); + + if (keepWallTimeInvariant) { + int32_t newWallTime = get(UCAL_MILLISECONDS_IN_DAY, status); + if (newWallTime != prevWallTime) { + // There is at least one zone transition between the base + // time and the result time. As the result, wall time has + // changed. + UDate t = internalGetTime(); + int32_t newOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status); + if (newOffset != prevOffset) { + // When the difference of the previous UTC offset and + // the new UTC offset exceeds 1 full day, we do not want + // to roll over/back the date. For now, this only happens + // in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452. + int32_t adjAmount = prevOffset - newOffset; + adjAmount = adjAmount >= 0 ? adjAmount % (int32_t)kOneDay : -(-adjAmount % (int32_t)kOneDay); + if (adjAmount != 0) { + setTimeInMillis(t + adjAmount, status); + newWallTime = get(UCAL_MILLISECONDS_IN_DAY, status); + } + if (newWallTime != prevWallTime) { + // The result wall time or adjusted wall time was shifted because + // the target wall time does not exist on the result date. + switch (fSkippedWallTime) { + case UCAL_WALLTIME_FIRST: + if (adjAmount > 0) { + setTimeInMillis(t, status); + } + break; + case UCAL_WALLTIME_LAST: + if (adjAmount < 0) { + setTimeInMillis(t, status); + } + break; + case UCAL_WALLTIME_NEXT_VALID: + UDate tmpT = adjAmount > 0 ? internalGetTime() : t; + UDate immediatePrevTrans; + UBool hasTransition = getImmediatePreviousZoneTransition(tmpT, &immediatePrevTrans, status); + if (U_SUCCESS(status) && hasTransition) { + setTimeInMillis(immediatePrevTrans, status); + } + break; + } + } + } + } + } +} + +// ------------------------------------- +int32_t Calendar::fieldDifference(UDate when, EDateFields field, UErrorCode& status) { + return fieldDifference(when, (UCalendarDateFields) field, status); +} + +int32_t Calendar::fieldDifference(UDate targetMs, UCalendarDateFields field, UErrorCode& ec) { + if (U_FAILURE(ec)) return 0; + int32_t min = 0; + double startMs = getTimeInMillis(ec); + // Always add from the start millis. This accommodates + // operations like adding years from February 29, 2000 up to + // February 29, 2004. If 1, 1, 1, 1 is added to the year + // field, the DOM gets pinned to 28 and stays there, giving an + // incorrect DOM difference of 1. We have to add 1, reset, 2, + // reset, 3, reset, 4. + if (startMs < targetMs) { + int32_t max = 1; + // Find a value that is too large + while (U_SUCCESS(ec)) { + setTimeInMillis(startMs, ec); + add(field, max, ec); + double ms = getTimeInMillis(ec); + if (ms == targetMs) { + return max; + } else if (ms > targetMs) { + break; + } else if (max < INT32_MAX) { + min = max; + max <<= 1; + if (max < 0) { + max = INT32_MAX; + } + } else { + // Field difference too large to fit into int32_t +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n", + __FILE__, __LINE__, fldName(field)); +#endif + ec = U_ILLEGAL_ARGUMENT_ERROR; + } + } + // Do a binary search + while ((max - min) > 1 && U_SUCCESS(ec)) { + int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX + setTimeInMillis(startMs, ec); + add(field, t, ec); + double ms = getTimeInMillis(ec); + if (ms == targetMs) { + return t; + } else if (ms > targetMs) { + max = t; + } else { + min = t; + } + } + } else if (startMs > targetMs) { + int32_t max = -1; + // Find a value that is too small + while (U_SUCCESS(ec)) { + setTimeInMillis(startMs, ec); + add(field, max, ec); + double ms = getTimeInMillis(ec); + if (ms == targetMs) { + return max; + } else if (ms < targetMs) { + break; + } else { + min = max; + max = (int32_t)((uint32_t)(max) << 1); + if (max == 0) { + // Field difference too large to fit into int32_t +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n", + __FILE__, __LINE__, fldName(field)); +#endif + ec = U_ILLEGAL_ARGUMENT_ERROR; + } + } + } + // Do a binary search + while ((min - max) > 1 && U_SUCCESS(ec)) { + int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX + setTimeInMillis(startMs, ec); + add(field, t, ec); + double ms = getTimeInMillis(ec); + if (ms == targetMs) { + return t; + } else if (ms < targetMs) { + max = t; + } else { + min = t; + } + } + } + // Set calendar to end point + setTimeInMillis(startMs, ec); + add(field, min, ec); + + /* Test for buffer overflows */ + if(U_FAILURE(ec)) { + return 0; + } + return min; +} + +// ------------------------------------- + +void +Calendar::adoptTimeZone(TimeZone* zone) +{ + // Do nothing if passed-in zone is nullptr + if (zone == nullptr) return; + + // fZone should always be non-null + delete fZone; + fZone = zone; + + // if the zone changes, we need to recompute the time fields + fAreFieldsSet = false; +} + +// ------------------------------------- +void +Calendar::setTimeZone(const TimeZone& zone) +{ + adoptTimeZone(zone.clone()); +} + +// ------------------------------------- + +const TimeZone& +Calendar::getTimeZone() const +{ + U_ASSERT(fZone != nullptr); + return *fZone; +} + +// ------------------------------------- + +TimeZone* +Calendar::orphanTimeZone() +{ + // we let go of the time zone; the new time zone is the system default time zone + TimeZone *defaultZone = TimeZone::createDefault(); + if (defaultZone == nullptr) { + // No error handling available. Must keep fZone non-nullptr, there are many unchecked uses. + return nullptr; + } + TimeZone *z = fZone; + fZone = defaultZone; + return z; +} + +// ------------------------------------- + +void +Calendar::setLenient(UBool lenient) +{ + fLenient = lenient; +} + +// ------------------------------------- + +UBool +Calendar::isLenient() const +{ + return fLenient; +} + +// ------------------------------------- + +void +Calendar::setRepeatedWallTimeOption(UCalendarWallTimeOption option) +{ + if (option == UCAL_WALLTIME_LAST || option == UCAL_WALLTIME_FIRST) { + fRepeatedWallTime = option; + } +} + +// ------------------------------------- + +UCalendarWallTimeOption +Calendar::getRepeatedWallTimeOption() const +{ + return fRepeatedWallTime; +} + +// ------------------------------------- + +void +Calendar::setSkippedWallTimeOption(UCalendarWallTimeOption option) +{ + fSkippedWallTime = option; +} + +// ------------------------------------- + +UCalendarWallTimeOption +Calendar::getSkippedWallTimeOption() const +{ + return fSkippedWallTime; +} + +// ------------------------------------- + +void +Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value) UPRV_NO_SANITIZE_UNDEFINED { + if (fFirstDayOfWeek != value && + value >= UCAL_SUNDAY && value <= UCAL_SATURDAY) { + fFirstDayOfWeek = value; + fAreFieldsSet = false; + } +} + +// ------------------------------------- + +Calendar::EDaysOfWeek +Calendar::getFirstDayOfWeek() const +{ + return (Calendar::EDaysOfWeek)fFirstDayOfWeek; +} + +UCalendarDaysOfWeek +Calendar::getFirstDayOfWeek(UErrorCode & /*status*/) const +{ + return fFirstDayOfWeek; +} +// ------------------------------------- + +void +Calendar::setMinimalDaysInFirstWeek(uint8_t value) +{ + // Values less than 1 have the same effect as 1; values greater + // than 7 have the same effect as 7. However, we normalize values + // so operator== and so forth work. + if (value < 1) { + value = 1; + } else if (value > 7) { + value = 7; + } + if (fMinimalDaysInFirstWeek != value) { + fMinimalDaysInFirstWeek = value; + fAreFieldsSet = false; + } +} + +// ------------------------------------- + +uint8_t +Calendar::getMinimalDaysInFirstWeek() const +{ + return fMinimalDaysInFirstWeek; +} + +// ------------------------------------- +// weekend functions, just dummy implementations for now (for API freeze) + +UCalendarWeekdayType +Calendar::getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const +{ + if (U_FAILURE(status)) { + return UCAL_WEEKDAY; + } + if (dayOfWeek < UCAL_SUNDAY || dayOfWeek > UCAL_SATURDAY) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return UCAL_WEEKDAY; + } + if (fWeekendOnset == fWeekendCease) { + if (dayOfWeek != fWeekendOnset) + return UCAL_WEEKDAY; + return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET; + } + if (fWeekendOnset < fWeekendCease) { + if (dayOfWeek < fWeekendOnset || dayOfWeek > fWeekendCease) { + return UCAL_WEEKDAY; + } + } else { + if (dayOfWeek > fWeekendCease && dayOfWeek < fWeekendOnset) { + return UCAL_WEEKDAY; + } + } + if (dayOfWeek == fWeekendOnset) { + return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET; + } + if (dayOfWeek == fWeekendCease) { + return (fWeekendCeaseMillis >= 86400000) ? UCAL_WEEKEND : UCAL_WEEKEND_CEASE; + } + return UCAL_WEEKEND; +} + +int32_t +Calendar::getWeekendTransition(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const +{ + if (U_FAILURE(status)) { + return 0; + } + if (dayOfWeek == fWeekendOnset) { + return fWeekendOnsetMillis; + } else if (dayOfWeek == fWeekendCease) { + return fWeekendCeaseMillis; + } + status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; +} + +UBool +Calendar::isWeekend(UDate date, UErrorCode &status) const +{ + if (U_FAILURE(status)) { + return false; + } + // clone the calendar so we don't mess with the real one. + Calendar *work = this->clone(); + if (work == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return false; + } + UBool result = false; + work->setTime(date, status); + if (U_SUCCESS(status)) { + result = work->isWeekend(); + } + delete work; + return result; +} + +UBool +Calendar::isWeekend() const +{ + UErrorCode status = U_ZERO_ERROR; + UCalendarDaysOfWeek dayOfWeek = (UCalendarDaysOfWeek)get(UCAL_DAY_OF_WEEK, status); + UCalendarWeekdayType dayType = getDayOfWeekType(dayOfWeek, status); + if (U_SUCCESS(status)) { + switch (dayType) { + case UCAL_WEEKDAY: + return false; + case UCAL_WEEKEND: + return true; + case UCAL_WEEKEND_ONSET: + case UCAL_WEEKEND_CEASE: + // Use internalGet() because the above call to get() populated all fields. + { + int32_t millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY); + int32_t transitionMillis = getWeekendTransition(dayOfWeek, status); + if (U_SUCCESS(status)) { + return (dayType == UCAL_WEEKEND_ONSET)? + (millisInDay >= transitionMillis): + (millisInDay < transitionMillis); + } + // else fall through, return false + U_FALLTHROUGH; + } + default: + break; + } + } + return false; +} + +// ------------------------------------- limits + +int32_t +Calendar::getMinimum(EDateFields field) const { + return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MINIMUM); +} + +int32_t +Calendar::getMinimum(UCalendarDateFields field) const +{ + return getLimit(field,UCAL_LIMIT_MINIMUM); +} + +// ------------------------------------- +int32_t +Calendar::getMaximum(EDateFields field) const +{ + return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MAXIMUM); +} + +int32_t +Calendar::getMaximum(UCalendarDateFields field) const +{ + return getLimit(field,UCAL_LIMIT_MAXIMUM); +} + +// ------------------------------------- +int32_t +Calendar::getGreatestMinimum(EDateFields field) const +{ + return getLimit((UCalendarDateFields)field,UCAL_LIMIT_GREATEST_MINIMUM); +} + +int32_t +Calendar::getGreatestMinimum(UCalendarDateFields field) const +{ + return getLimit(field,UCAL_LIMIT_GREATEST_MINIMUM); +} + +// ------------------------------------- +int32_t +Calendar::getLeastMaximum(EDateFields field) const +{ + return getLimit((UCalendarDateFields) field,UCAL_LIMIT_LEAST_MAXIMUM); +} + +int32_t +Calendar::getLeastMaximum(UCalendarDateFields field) const +{ + return getLimit( field,UCAL_LIMIT_LEAST_MAXIMUM); +} + +// ------------------------------------- +int32_t +Calendar::getActualMinimum(EDateFields field, UErrorCode& status) const +{ + return getActualMinimum((UCalendarDateFields) field, status); +} + +int32_t Calendar::getLimit(UCalendarDateFields field, ELimitType limitType) const { + switch (field) { + case UCAL_DAY_OF_WEEK: + case UCAL_AM_PM: + case UCAL_HOUR: + case UCAL_HOUR_OF_DAY: + case UCAL_MINUTE: + case UCAL_SECOND: + case UCAL_MILLISECOND: + case UCAL_ZONE_OFFSET: + case UCAL_DST_OFFSET: + case UCAL_DOW_LOCAL: + case UCAL_JULIAN_DAY: + case UCAL_MILLISECONDS_IN_DAY: + case UCAL_IS_LEAP_MONTH: + return kCalendarLimits[field][limitType]; + + case UCAL_WEEK_OF_MONTH: + { + int32_t limit; + if (limitType == UCAL_LIMIT_MINIMUM) { + limit = getMinimalDaysInFirstWeek() == 1 ? 1 : 0; + } else if (limitType == UCAL_LIMIT_GREATEST_MINIMUM) { + limit = 1; + } else { + int32_t minDaysInFirst = getMinimalDaysInFirstWeek(); + int32_t daysInMonth = handleGetLimit(UCAL_DAY_OF_MONTH, limitType); + if (limitType == UCAL_LIMIT_LEAST_MAXIMUM) { + limit = (daysInMonth + (7 - minDaysInFirst)) / 7; + } else { // limitType == UCAL_LIMIT_MAXIMUM + limit = (daysInMonth + 6 + (7 - minDaysInFirst)) / 7; + } + } + return limit; + } + default: + return handleGetLimit(field, limitType); + } +} + +int32_t +Calendar::getActualMinimum(UCalendarDateFields field, UErrorCode& status) const +{ + if (U_FAILURE(status)) { + return 0; + } + int32_t fieldValue = getGreatestMinimum(field); + int32_t endValue = getMinimum(field); + + // if we know that the minimum value is always the same, just return it + if (fieldValue == endValue) { + return fieldValue; + } + + // clone the calendar so we don't mess with the real one, and set it to + // accept anything for the field values + Calendar *work = this->clone(); + if (work == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + work->setLenient(true); + + // now try each value from getLeastMaximum() to getMaximum() one by one until + // we get a value that normalizes to another value. The last value that + // normalizes to itself is the actual minimum for the current date + int32_t result = fieldValue; + + do { + work->set(field, fieldValue); + if (work->get(field, status) != fieldValue) { + break; + } + else { + result = fieldValue; + fieldValue--; + } + } while (fieldValue >= endValue); + + delete work; + + /* Test for buffer overflows */ + if(U_FAILURE(status)) { + return 0; + } + return result; +} + +// ------------------------------------- + +UBool +Calendar::inDaylightTime(UErrorCode& status) const +{ + if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) { + return false; + } + + // Force an update of the state of the Calendar. + ((Calendar*)this)->complete(status); // cast away const + + return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : false); +} + +bool +Calendar::inTemporalLeapYear(UErrorCode& status) const +{ + // Default to Gregorian based leap year rule. + return getActualMaximum(UCAL_DAY_OF_YEAR, status) == 366; +} + +// ------------------------------------- + +static const char * const gTemporalMonthCodes[] = { + "M01", "M02", "M03", "M04", "M05", "M06", + "M07", "M08", "M09", "M10", "M11", "M12", nullptr +}; + +const char* +Calendar::getTemporalMonthCode(UErrorCode& status) const +{ + int32_t month = get(UCAL_MONTH, status); + if (U_FAILURE(status)) return nullptr; + U_ASSERT(month < 12); + U_ASSERT(internalGet(UCAL_IS_LEAP_MONTH) == 0); + return gTemporalMonthCodes[month]; +} + +void +Calendar::setTemporalMonthCode(const char* code, UErrorCode& status ) +{ + if (U_FAILURE(status)) return; + int32_t len = static_cast(uprv_strlen(code)); + if (len == 3 && code[0] == 'M') { + for (int m = 0; gTemporalMonthCodes[m] != nullptr; m++) { + if (uprv_strcmp(code, gTemporalMonthCodes[m]) == 0) { + set(UCAL_MONTH, m); + set(UCAL_IS_LEAP_MONTH, 0); + return; + } + } + } + status = U_ILLEGAL_ARGUMENT_ERROR; +} + +// ------------------------------------- + +/** +* Ensure that each field is within its valid range by calling {@link +* #validateField(int)} on each field that has been set. This method +* should only be called if this calendar is not lenient. +* @see #isLenient +* @see #validateField(int) +*/ +void Calendar::validateFields(UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + for (int32_t field = 0; U_SUCCESS(status) && (field < UCAL_FIELD_COUNT); field++) { + if (fStamp[field] >= kMinimumUserStamp) { + validateField((UCalendarDateFields)field, status); + } + } +} + +/** +* Validate a single field of this calendar. Subclasses should +* override this method to validate any calendar-specific fields. +* Generic fields can be handled by +* Calendar.validateField(). +* @see #validateField(int, int, int) +*/ +void Calendar::validateField(UCalendarDateFields field, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + int32_t y; + switch (field) { + case UCAL_DAY_OF_MONTH: + y = handleGetExtendedYear(); + validateField(field, 1, handleGetMonthLength(y, internalGetMonth()), status); + break; + case UCAL_DAY_OF_YEAR: + y = handleGetExtendedYear(); + validateField(field, 1, handleGetYearLength(y), status); + break; + case UCAL_DAY_OF_WEEK_IN_MONTH: + if (internalGet(field) == 0) { +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: ILLEGAL ARG because DOW in month cannot be 0\n", + __FILE__, __LINE__); +#endif + status = U_ILLEGAL_ARGUMENT_ERROR; // "DAY_OF_WEEK_IN_MONTH cannot be zero" + return; + } + validateField(field, getMinimum(field), getMaximum(field), status); + break; + default: + validateField(field, getMinimum(field), getMaximum(field), status); + break; + } +} + +/** +* Validate a single field of this calendar given its minimum and +* maximum allowed value. If the field is out of range, throw a +* descriptive IllegalArgumentException. Subclasses may +* use this method in their implementation of {@link +* #validateField(int)}. +*/ +void Calendar::validateField(UCalendarDateFields field, int32_t min, int32_t max, UErrorCode& status) +{ + if (U_FAILURE(status)) { + return; + } + int32_t value = fFields[field]; + if (value < min || value > max) { +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: ILLEGAL ARG because of field %s out of range %d..%d at %d\n", + __FILE__, __LINE__,fldName(field),min,max,value); +#endif + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } +} + +// ------------------------- + +const UFieldResolutionTable* Calendar::getFieldResolutionTable() const { + return kDatePrecedence; +} + + +UCalendarDateFields Calendar::newerField(UCalendarDateFields defaultField, UCalendarDateFields alternateField) const +{ + if (fStamp[alternateField] > fStamp[defaultField]) { + return alternateField; + } + return defaultField; +} + +UCalendarDateFields Calendar::resolveFields(const UFieldResolutionTable* precedenceTable) const { + int32_t bestField = UCAL_FIELD_COUNT; + int32_t tempBestField; + for (int32_t g=0; precedenceTable[g][0][0] != -1 && (bestField == UCAL_FIELD_COUNT); ++g) { + int32_t bestStamp = kUnset; + for (int32_t l=0; precedenceTable[g][l][0] != -1; ++l) { + int32_t lineStamp = kUnset; + // Skip over first entry if it is negative + for (int32_t i=((precedenceTable[g][l][0]>=kResolveRemap)?1:0); precedenceTable[g][l][i]!=-1; ++i) { + U_ASSERT(precedenceTable[g][l][i] < UCAL_FIELD_COUNT); + int32_t s = fStamp[precedenceTable[g][l][i]]; + // If any field is unset then don't use this line + if (s == kUnset) { + goto linesInGroup; + } else if(s > lineStamp) { + lineStamp = s; + } + } + // Record new maximum stamp & field no. + if (lineStamp > bestStamp) { + tempBestField = precedenceTable[g][l][0]; // First field refers to entire line + if (tempBestField >= kResolveRemap) { + tempBestField &= (kResolveRemap-1); + // This check is needed to resolve some issues with UCAL_YEAR precedence mapping + if (tempBestField != UCAL_DATE || (fStamp[UCAL_WEEK_OF_MONTH] < fStamp[tempBestField])) { + bestField = tempBestField; + } + } else { + bestField = tempBestField; + } + + if (bestField == tempBestField) { + bestStamp = lineStamp; + } + } +linesInGroup: + ; + } + } + return (UCalendarDateFields)bestField; +} + +const UFieldResolutionTable Calendar::kDatePrecedence[] = +{ + { + { UCAL_DAY_OF_MONTH, kResolveSTOP }, + { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP }, + { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, + { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, + { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP }, + { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, + { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, + { UCAL_DAY_OF_YEAR, kResolveSTOP }, + { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_YEAR, kResolveSTOP }, // if YEAR is set over YEAR_WOY use DAY_OF_MONTH + { kResolveRemap | UCAL_WEEK_OF_YEAR, UCAL_YEAR_WOY, kResolveSTOP }, // if YEAR_WOY is set, calc based on WEEK_OF_YEAR + { kResolveSTOP } + }, + { + { UCAL_WEEK_OF_YEAR, kResolveSTOP }, + { UCAL_WEEK_OF_MONTH, kResolveSTOP }, + { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP }, + { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, + { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, + { kResolveSTOP } + }, + {{kResolveSTOP}} +}; + + +const UFieldResolutionTable Calendar::kMonthPrecedence[] = +{ + { + { UCAL_MONTH,kResolveSTOP, kResolveSTOP }, + { UCAL_ORDINAL_MONTH,kResolveSTOP, kResolveSTOP }, + {kResolveSTOP} + }, + {{kResolveSTOP}} +}; + +const UFieldResolutionTable Calendar::kDOWPrecedence[] = +{ + { + { UCAL_DAY_OF_WEEK,kResolveSTOP, kResolveSTOP }, + { UCAL_DOW_LOCAL,kResolveSTOP, kResolveSTOP }, + {kResolveSTOP} + }, + {{kResolveSTOP}} +}; + +// precedence for calculating a year +const UFieldResolutionTable Calendar::kYearPrecedence[] = +{ + { + { UCAL_YEAR, kResolveSTOP }, + { UCAL_EXTENDED_YEAR, kResolveSTOP }, + { UCAL_YEAR_WOY, UCAL_WEEK_OF_YEAR, kResolveSTOP }, // YEAR_WOY is useless without WEEK_OF_YEAR + { kResolveSTOP } + }, + {{kResolveSTOP}} +}; + + +// ------------------------- + + +void Calendar::computeTime(UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (!isLenient()) { + validateFields(status); + if (U_FAILURE(status)) { + return; + } + } + + // Compute the Julian day + int32_t julianDay = computeJulianDay(); + + double millis = Grego::julianDayToMillis(julianDay); + +#if defined (U_DEBUG_CAL) + // int32_t julianInsanityCheck = (int32_t)ClockMath::floorDivide(millis, kOneDay); + // julianInsanityCheck += kEpochStartAsJulianDay; + // if(1 || julianInsanityCheck != julianDay) { + // fprintf(stderr, "%s:%d- D'oh- computed jules %d, to mills (%s)%.lf, recomputed %d\n", + // __FILE__, __LINE__, julianDay, millis<0.0?"NEG":"", millis, julianInsanityCheck); + // } +#endif + + double millisInDay; + + // We only use MILLISECONDS_IN_DAY if it has been set by the user. + // This makes it possible for the caller to set the calendar to a + // time and call clear(MONTH) to reset the MONTH to January. This + // is legacy behavior. Without this, clear(MONTH) has no effect, + // since the internally set JULIAN_DAY is used. + if (fStamp[UCAL_MILLISECONDS_IN_DAY] >= ((int32_t)kMinimumUserStamp) && + newestStamp(UCAL_AM_PM, UCAL_MILLISECOND, kUnset) <= fStamp[UCAL_MILLISECONDS_IN_DAY]) { + millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY); + } else { + millisInDay = computeMillisInDay(); + } + + UDate t = 0; + if (fStamp[UCAL_ZONE_OFFSET] >= ((int32_t)kMinimumUserStamp) || fStamp[UCAL_DST_OFFSET] >= ((int32_t)kMinimumUserStamp)) { + t = millis + millisInDay - (internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET)); + } else { + // Compute the time zone offset and DST offset. There are two potential + // ambiguities here. We'll assume a 2:00 am (wall time) switchover time + // for discussion purposes here. + // + // 1. The positive offset change such as transition into DST. + // Here, a designated time of 2:00 am - 2:59 am does not actually exist. + // For this case, skippedWallTime option specifies the behavior. + // For example, 2:30 am is interpreted as; + // - WALLTIME_LAST(default): 3:30 am (DST) (interpreting 2:30 am as 31 minutes after 1:59 am (STD)) + // - WALLTIME_FIRST: 1:30 am (STD) (interpreting 2:30 am as 30 minutes before 3:00 am (DST)) + // - WALLTIME_NEXT_VALID: 3:00 am (DST) (next valid time after 2:30 am on a wall clock) + // 2. The negative offset change such as transition out of DST. + // Here, a designated time of 1:00 am - 1:59 am can be in standard or DST. Both are valid + // representations (the rep jumps from 1:59:59 DST to 1:00:00 Std). + // For this case, repeatedWallTime option specifies the behavior. + // For example, 1:30 am is interpreted as; + // - WALLTIME_LAST(default): 1:30 am (STD) - latter occurrence + // - WALLTIME_FIRST: 1:30 am (DST) - former occurrence + // + // In addition to above, when calendar is strict (not default), wall time falls into + // the skipped time range will be processed as an error case. + // + // These special cases are mostly handled in #computeZoneOffset(long), except WALLTIME_NEXT_VALID + // at positive offset change. The protected method computeZoneOffset(long) is exposed to Calendar + // subclass implementations and marked as @stable. Strictly speaking, WALLTIME_NEXT_VALID + // should be also handled in the same place, but we cannot change the code flow without deprecating + // the protected method. + // + // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET + // or DST_OFFSET fields; then we use those fields. + + if (!isLenient() || fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID) { + // When strict, invalidate a wall time falls into a skipped wall time range. + // When lenient and skipped wall time option is WALLTIME_NEXT_VALID, + // the result time will be adjusted to the next valid time (on wall clock). + int32_t zoneOffset = computeZoneOffset(millis, millisInDay, status); + UDate tmpTime = millis + millisInDay - zoneOffset; + + int32_t raw, dst; + fZone->getOffset(tmpTime, false, raw, dst, status); + + if (U_SUCCESS(status)) { + // zoneOffset != (raw + dst) only when the given wall time fall into + // a skipped wall time range caused by positive zone offset transition. + if (zoneOffset != (raw + dst)) { + if (!isLenient()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } else { + U_ASSERT(fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID); + // Adjust time to the next valid wall clock time. + // At this point, tmpTime is on or after the zone offset transition causing + // the skipped time range. + UDate immediatePrevTransition; + UBool hasTransition = getImmediatePreviousZoneTransition(tmpTime, &immediatePrevTransition, status); + if (U_SUCCESS(status) && hasTransition) { + t = immediatePrevTransition; + } + } + } else { + t = tmpTime; + } + } + } else { + t = millis + millisInDay - computeZoneOffset(millis, millisInDay, status); + } + } + if (U_SUCCESS(status)) { + internalSetTime(t); + } +} + +/** + * Find the previous zone transition near the given time. + */ +UBool Calendar::getImmediatePreviousZoneTransition(UDate base, UDate *transitionTime, UErrorCode& status) const { + if (U_FAILURE(status)) { + return false; + } + BasicTimeZone *btz = getBasicTimeZone(); + if (btz) { + TimeZoneTransition trans; + UBool hasTransition = btz->getPreviousTransition(base, true, trans); + if (hasTransition) { + *transitionTime = trans.getTime(); + return true; + } else { + // Could not find any transitions. + // Note: This should never happen. + status = U_INTERNAL_PROGRAM_ERROR; + } + } else { + // If not BasicTimeZone, return unsupported error for now. + // TODO: We may support non-BasicTimeZone in future. + status = U_UNSUPPORTED_ERROR; + } + return false; +} + +/** +* Compute the milliseconds in the day from the fields. This is a +* value from 0 to 23:59:59.999 inclusive, unless fields are out of +* range, in which case it can be an arbitrary value. This value +* reflects local zone wall time. +* @stable ICU 2.0 +*/ +double Calendar::computeMillisInDay() { + // Do the time portion of the conversion. + + double millisInDay = 0; + + // Find the best set of fields specifying the time of day. There + // are only two possibilities here; the HOUR_OF_DAY or the + // AM_PM and the HOUR. + int32_t hourOfDayStamp = fStamp[UCAL_HOUR_OF_DAY]; + int32_t hourStamp = (fStamp[UCAL_HOUR] > fStamp[UCAL_AM_PM])?fStamp[UCAL_HOUR]:fStamp[UCAL_AM_PM]; + int32_t bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp; + + // Hours + if (bestStamp != kUnset) { + if (bestStamp == hourOfDayStamp) { + // Don't normalize here; let overflow bump into the next period. + // This is consistent with how we handle other fields. + millisInDay += internalGet(UCAL_HOUR_OF_DAY); + } else { + // Don't normalize here; let overflow bump into the next period. + // This is consistent with how we handle other fields. + millisInDay += internalGet(UCAL_HOUR); + millisInDay += 12 * internalGet(UCAL_AM_PM); // Default works for unset AM_PM + } + } + + // We use the fact that unset == 0; we start with millisInDay + // == HOUR_OF_DAY. + millisInDay *= 60; + millisInDay += internalGet(UCAL_MINUTE); // now have minutes + millisInDay *= 60; + millisInDay += internalGet(UCAL_SECOND); // now have seconds + millisInDay *= 1000; + millisInDay += internalGet(UCAL_MILLISECOND); // now have millis + + return millisInDay; +} + +/** +* This method can assume EXTENDED_YEAR has been set. +* @param millis milliseconds of the date fields +* @param millisInDay milliseconds of the time fields; may be out +* or range. +* @stable ICU 2.0 +*/ +int32_t Calendar::computeZoneOffset(double millis, double millisInDay, UErrorCode &ec) { + if (U_FAILURE(ec)) { + return 0; + } + int32_t rawOffset, dstOffset; + UDate wall = millis + millisInDay; + BasicTimeZone* btz = getBasicTimeZone(); + if (btz) { + UTimeZoneLocalOption duplicatedTimeOpt = (fRepeatedWallTime == UCAL_WALLTIME_FIRST) ? UCAL_TZ_LOCAL_FORMER : UCAL_TZ_LOCAL_LATTER; + UTimeZoneLocalOption nonExistingTimeOpt = (fSkippedWallTime == UCAL_WALLTIME_FIRST) ? UCAL_TZ_LOCAL_LATTER : UCAL_TZ_LOCAL_FORMER; + btz->getOffsetFromLocal(wall, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, ec); + } else { + const TimeZone& tz = getTimeZone(); + // By default, TimeZone::getOffset behaves UCAL_WALLTIME_LAST for both. + tz.getOffset(wall, true, rawOffset, dstOffset, ec); + + UBool sawRecentNegativeShift = false; + if (fRepeatedWallTime == UCAL_WALLTIME_FIRST) { + // Check if the given wall time falls into repeated time range + UDate tgmt = wall - (rawOffset + dstOffset); + + // Any negative zone transition within last 6 hours? + // Note: The maximum historic negative zone transition is -3 hours in the tz database. + // 6 hour window would be sufficient for this purpose. + int32_t tmpRaw, tmpDst; + tz.getOffset(tgmt - 6*60*60*1000, false, tmpRaw, tmpDst, ec); + int32_t offsetDelta = (rawOffset + dstOffset) - (tmpRaw + tmpDst); + + U_ASSERT(offsetDelta < -6*60*60*1000); + if (offsetDelta < 0) { + sawRecentNegativeShift = true; + // Negative shift within last 6 hours. When UCAL_WALLTIME_FIRST is used and the given wall time falls + // into the repeated time range, use offsets before the transition. + // Note: If it does not fall into the repeated time range, offsets remain unchanged below. + tz.getOffset(wall + offsetDelta, true, rawOffset, dstOffset, ec); + } + } + if (!sawRecentNegativeShift && fSkippedWallTime == UCAL_WALLTIME_FIRST) { + // When skipped wall time option is WALLTIME_FIRST, + // recalculate offsets from the resolved time (non-wall). + // When the given wall time falls into skipped wall time, + // the offsets will be based on the zone offsets AFTER + // the transition (which means, earliest possible interpretation). + UDate tgmt = wall - (rawOffset + dstOffset); + tz.getOffset(tgmt, false, rawOffset, dstOffset, ec); + } + } + return rawOffset + dstOffset; +} + +int32_t Calendar::computeJulianDay() +{ + // We want to see if any of the date fields is newer than the + // JULIAN_DAY. If not, then we use JULIAN_DAY. If so, then we do + // the normal resolution. We only use JULIAN_DAY if it has been + // set by the user. This makes it possible for the caller to set + // the calendar to a time and call clear(MONTH) to reset the MONTH + // to January. This is legacy behavior. Without this, + // clear(MONTH) has no effect, since the internally set JULIAN_DAY + // is used. + if (fStamp[UCAL_JULIAN_DAY] >= (int32_t)kMinimumUserStamp) { + int32_t bestStamp = newestStamp(UCAL_ERA, UCAL_DAY_OF_WEEK_IN_MONTH, kUnset); + bestStamp = newestStamp(UCAL_YEAR_WOY, UCAL_EXTENDED_YEAR, bestStamp); + bestStamp = newestStamp(UCAL_ORDINAL_MONTH, UCAL_ORDINAL_MONTH, bestStamp); + if (bestStamp <= fStamp[UCAL_JULIAN_DAY]) { + return internalGet(UCAL_JULIAN_DAY); + } + } + + UCalendarDateFields bestField = resolveFields(getFieldResolutionTable()); + if (bestField == UCAL_FIELD_COUNT) { + bestField = UCAL_DAY_OF_MONTH; + } + + return handleComputeJulianDay(bestField); +} + +// ------------------------------------------- + +int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField) { + UBool useMonth = (bestField == UCAL_DAY_OF_MONTH || + bestField == UCAL_WEEK_OF_MONTH || + bestField == UCAL_DAY_OF_WEEK_IN_MONTH); + int32_t year; + + if (bestField == UCAL_WEEK_OF_YEAR && newerField(UCAL_YEAR_WOY, UCAL_YEAR) == UCAL_YEAR_WOY) { + year = internalGet(UCAL_YEAR_WOY); + } else { + year = handleGetExtendedYear(); + } + + internalSet(UCAL_EXTENDED_YEAR, year); + +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: bestField= %s - y=%d\n", __FILE__, __LINE__, fldName(bestField), year); +#endif + + // Get the Julian day of the day BEFORE the start of this year. + // If useMonth is true, get the day before the start of the month. + + // give calendar subclass a chance to have a default 'first' month + int32_t month; + + if(isSet(UCAL_MONTH) || isSet(UCAL_ORDINAL_MONTH)) { + month = internalGetMonth(); + } else { + month = getDefaultMonthInYear(year); + } + + int32_t julianDay = handleComputeMonthStart(year, useMonth ? month : 0, useMonth); + + if (bestField == UCAL_DAY_OF_MONTH) { + + // give calendar subclass a chance to have a default 'first' dom + int32_t dayOfMonth; + if(isSet(UCAL_DAY_OF_MONTH)) { + dayOfMonth = internalGet(UCAL_DAY_OF_MONTH,1); + } else { + dayOfMonth = getDefaultDayInMonth(year, month); + } + return julianDay + dayOfMonth; + } + + if (bestField == UCAL_DAY_OF_YEAR) { + return julianDay + internalGet(UCAL_DAY_OF_YEAR); + } + + int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw + + // At this point julianDay is the 0-based day BEFORE the first day of + // January 1, year 1 of the given calendar. If julianDay == 0, it + // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian + // or Gregorian). (or it is before the month we are in, if useMonth is True) + + // At this point we need to process the WEEK_OF_MONTH or + // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH. + // First, perform initial shared computations. These locate the + // first week of the period. + + // Get the 0-based localized DOW of day one of the month or year. + // Valid range 0..6. + int32_t first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; + if (first < 0) { + first += 7; + } + + int32_t dowLocal = getLocalDOW(); + + // Find the first target DOW (dowLocal) in the month or year. + // Actually, it may be just before the first of the month or year. + // It will be an integer from -5..7. + int32_t date = 1 - first + dowLocal; + + if (bestField == UCAL_DAY_OF_WEEK_IN_MONTH) { + // Adjust the target DOW to be in the month or year. + if (date < 1) { + date += 7; + } + + // The only trickiness occurs if the day-of-week-in-month is + // negative. + int32_t dim = internalGet(UCAL_DAY_OF_WEEK_IN_MONTH, 1); + if (dim >= 0) { + date += 7*(dim - 1); + + } else { + // Move date to the last of this day-of-week in this month, + // then back up as needed. If dim==-1, we don't back up at + // all. If dim==-2, we back up once, etc. Don't back up + // past the first of the given day-of-week in this month. + // Note that we handle -2, -3, etc. correctly, even though + // values < -1 are technically disallowed. + int32_t m = internalGetMonth(UCAL_JANUARY); + int32_t monthLength = handleGetMonthLength(year, m); + date += ((monthLength - date) / 7 + dim + 1) * 7; + } + } else { +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d - bf= %s\n", __FILE__, __LINE__, fldName(bestField)); +#endif + + if(bestField == UCAL_WEEK_OF_YEAR) { // ------------------------------------- WOY ------------- + if(!isSet(UCAL_YEAR_WOY) || // YWOY not set at all or + ( (resolveFields(kYearPrecedence) != UCAL_YEAR_WOY) // YWOY doesn't have precedence + && (fStamp[UCAL_YEAR_WOY]!=kInternallySet) ) ) // (excluding where all fields are internally set - then YWOY is used) + { + // need to be sure to stay in 'real' year. + int32_t woy = internalGet(bestField); + + int32_t nextJulianDay = handleComputeMonthStart(year+1, 0, false); // jd of day before jan 1 + int32_t nextFirst = julianDayToDayOfWeek(nextJulianDay + 1) - firstDayOfWeek; + + if (nextFirst < 0) { // 0..6 ldow of Jan 1 + nextFirst += 7; + } + + if(woy==1) { // FIRST WEEK --------------------------------- +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d - woy=%d, yp=%d, nj(%d)=%d, nf=%d", __FILE__, __LINE__, + internalGet(bestField), resolveFields(kYearPrecedence), year+1, + nextJulianDay, nextFirst); + + fprintf(stderr, " next: %d DFW, min=%d \n", (7-nextFirst), getMinimalDaysInFirstWeek() ); +#endif + + // nextFirst is now the localized DOW of Jan 1 of y-woy+1 + if((nextFirst > 0) && // Jan 1 starts on FDOW + (7-nextFirst) >= getMinimalDaysInFirstWeek()) // or enough days in the week + { + // Jan 1 of (yearWoy+1) is in yearWoy+1 - recalculate JD to next year +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d - was going to move JD from %d to %d [d%d]\n", __FILE__, __LINE__, + julianDay, nextJulianDay, (nextJulianDay-julianDay)); +#endif + julianDay = nextJulianDay; + + // recalculate 'first' [0-based local dow of jan 1] + first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; + if (first < 0) { + first += 7; + } + // recalculate date. + date = 1 - first + dowLocal; + } + } else if(woy>=getLeastMaximum(bestField)) { + // could be in the last week- find out if this JD would overstep + int32_t testDate = date; + if ((7 - first) < getMinimalDaysInFirstWeek()) { + testDate += 7; + } + + // Now adjust for the week number. + testDate += 7 * (woy - 1); + +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d)\n", + __FILE__, __LINE__, year, year-1, testDate, julianDay+testDate, nextJulianDay); +#endif + if(julianDay+testDate > nextJulianDay) { // is it past Dec 31? (nextJulianDay is day BEFORE year+1's Jan 1) + // Fire up the calculating engines.. retry YWOY = (year-1) + julianDay = handleComputeMonthStart(year-1, 0, false); // jd before Jan 1 of previous year + first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; // 0 based local dow of first week + + if(first < 0) { // 0..6 + first += 7; + } + date = 1 - first + dowLocal; + +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d - date now %d, jd%d, ywoy%d\n", + __FILE__, __LINE__, date, julianDay, year-1); +#endif + + + } /* correction needed */ + } /* leastmaximum */ + } /* resolvefields(year) != year_woy */ + } /* bestfield != week_of_year */ + + // assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR) + // Adjust for minimal days in first week + if ((7 - first) < getMinimalDaysInFirstWeek()) { + date += 7; + } + + // Now adjust for the week number. + date += 7 * (internalGet(bestField) - 1); + } + + return julianDay + date; +} + +int32_t +Calendar::getDefaultMonthInYear(int32_t /*eyear*/) +{ + return 0; +} + +int32_t +Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/) +{ + return 1; +} + + +int32_t Calendar::getLocalDOW() +{ + // Get zero-based localized DOW, valid range 0..6. This is the DOW + // we are looking for. + int32_t dowLocal = 0; + switch (resolveFields(kDOWPrecedence)) { + case UCAL_DAY_OF_WEEK: + dowLocal = internalGet(UCAL_DAY_OF_WEEK) - fFirstDayOfWeek; + break; + case UCAL_DOW_LOCAL: + dowLocal = internalGet(UCAL_DOW_LOCAL) - 1; + break; + default: + break; + } + dowLocal = dowLocal % 7; + if (dowLocal < 0) { + dowLocal += 7; + } + return dowLocal; +} + +int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy) +{ + // We have UCAL_YEAR_WOY and UCAL_WEEK_OF_YEAR - from those, determine + // what year we fall in, so that other code can set it properly. + // (code borrowed from computeWeekFields and handleComputeJulianDay) + //return yearWoy; + + // First, we need a reliable DOW. + UCalendarDateFields bestField = resolveFields(kDatePrecedence); // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields + + // Now, a local DOW + int32_t dowLocal = getLocalDOW(); // 0..6 + int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw + int32_t jan1Start = handleComputeMonthStart(yearWoy, 0, false); + int32_t nextJan1Start = handleComputeMonthStart(yearWoy+1, 0, false); // next year's Jan1 start + + // At this point julianDay is the 0-based day BEFORE the first day of + // January 1, year 1 of the given calendar. If julianDay == 0, it + // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian + // or Gregorian). (or it is before the month we are in, if useMonth is True) + + // At this point we need to process the WEEK_OF_MONTH or + // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH. + // First, perform initial shared computations. These locate the + // first week of the period. + + // Get the 0-based localized DOW of day one of the month or year. + // Valid range 0..6. + int32_t first = julianDayToDayOfWeek(jan1Start + 1) - firstDayOfWeek; + if (first < 0) { + first += 7; + } + + //// (nextFirst was not used below) + // int32_t nextFirst = julianDayToDayOfWeek(nextJan1Start + 1) - firstDayOfWeek; + // if (nextFirst < 0) { + // nextFirst += 7; + //} + + int32_t minDays = getMinimalDaysInFirstWeek(); + UBool jan1InPrevYear = false; // January 1st in the year of WOY is the 1st week? (i.e. first week is < minimal ) + //UBool nextJan1InPrevYear = false; // January 1st of Year of WOY + 1 is in the first week? + + if((7 - first) < minDays) { + jan1InPrevYear = true; + } + + // if((7 - nextFirst) < minDays) { + // nextJan1InPrevYear = true; + // } + + switch(bestField) { + case UCAL_WEEK_OF_YEAR: + if(woy == 1) { + if(jan1InPrevYear) { + // the first week of January is in the previous year + // therefore WOY1 is always solidly within yearWoy + return yearWoy; + } else { + // First WOY is split between two years + if( dowLocal < first) { // we are prior to Jan 1 + return yearWoy-1; // previous year + } else { + return yearWoy; // in this year + } + } + } else if(woy >= getLeastMaximum(bestField)) { + // we _might_ be in the last week.. + int32_t jd = // Calculate JD of our target day: + jan1Start + // JD of Jan 1 + (7-first) + // days in the first week (Jan 1.. ) + (woy-1)*7 + // add the weeks of the year + dowLocal; // the local dow (0..6) of last week + if(jan1InPrevYear==false) { + jd -= 7; // woy already includes Jan 1's week. + } + + if( (jd+1) >= nextJan1Start ) { + // we are in week 52 or 53 etc. - actual year is yearWoy+1 + return yearWoy+1; + } else { + // still in yearWoy; + return yearWoy; + } + } else { + // we're not possibly in the last week -must be ywoy + return yearWoy; + } + + case UCAL_DATE: + { + int32_t m = internalGetMonth(); + if((m == 0) && + (woy >= getLeastMaximum(UCAL_WEEK_OF_YEAR))) { + return yearWoy+1; // month 0, late woy = in the next year + } else if(woy==1) { + //if(nextJan1InPrevYear) { + if(m == 0) { + return yearWoy; + } else { + return yearWoy-1; + } + //} + } + } + //(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow */ ) { + //within 1st week and in this month.. + //return yearWoy+1; + return yearWoy; + + default: // assume the year is appropriate + return yearWoy; + } +} + +int32_t Calendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const +{ + return handleComputeMonthStart(extendedYear, month+1, true) - + handleComputeMonthStart(extendedYear, month, true); +} + +int32_t Calendar::handleGetYearLength(int32_t eyear) const { + return handleComputeMonthStart(eyear+1, 0, false) - + handleComputeMonthStart(eyear, 0, false); +} + +int32_t +Calendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const +{ + if (U_FAILURE(status)) { + return 0; + } + int32_t result; + switch (field) { + case UCAL_DATE: + { + if(U_FAILURE(status)) return 0; + Calendar *cal = clone(); + if(!cal) { status = U_MEMORY_ALLOCATION_ERROR; return 0; } + cal->setLenient(true); + cal->prepareGetActual(field,false,status); + result = handleGetMonthLength(cal->get(UCAL_EXTENDED_YEAR, status), cal->get(UCAL_MONTH, status)); + delete cal; + } + break; + + case UCAL_DAY_OF_YEAR: + { + if(U_FAILURE(status)) return 0; + Calendar *cal = clone(); + if(!cal) { status = U_MEMORY_ALLOCATION_ERROR; return 0; } + cal->setLenient(true); + cal->prepareGetActual(field,false,status); + result = handleGetYearLength(cal->get(UCAL_EXTENDED_YEAR, status)); + delete cal; + } + break; + + case UCAL_DAY_OF_WEEK: + case UCAL_AM_PM: + case UCAL_HOUR: + case UCAL_HOUR_OF_DAY: + case UCAL_MINUTE: + case UCAL_SECOND: + case UCAL_MILLISECOND: + case UCAL_ZONE_OFFSET: + case UCAL_DST_OFFSET: + case UCAL_DOW_LOCAL: + case UCAL_JULIAN_DAY: + case UCAL_MILLISECONDS_IN_DAY: + // These fields all have fixed minima/maxima + result = getMaximum(field); + break; + + case UCAL_ORDINAL_MONTH: + result = inTemporalLeapYear(status) ? getMaximum(UCAL_ORDINAL_MONTH) : getLeastMaximum(UCAL_ORDINAL_MONTH); + break; + + default: + // For all other fields, do it the hard way.... + result = getActualHelper(field, getLeastMaximum(field), getMaximum(field),status); + break; + } + return result; +} + + +/** +* Prepare this calendar for computing the actual minimum or maximum. +* This method modifies this calendar's fields; it is called on a +* temporary calendar. +* +*

Rationale: The semantics of getActualXxx() is to return the +* maximum or minimum value that the given field can take, taking into +* account other relevant fields. In general these other fields are +* larger fields. For example, when computing the actual maximum +* DATE, the current value of DATE itself is ignored, +* as is the value of any field smaller. +* +*

The time fields all have fixed minima and maxima, so we don't +* need to worry about them. This also lets us set the +* MILLISECONDS_IN_DAY to zero to erase any effects the time fields +* might have when computing date fields. +* +*

DAY_OF_WEEK is adjusted specially for the WEEK_OF_MONTH and +* WEEK_OF_YEAR fields to ensure that they are computed correctly. +* @internal +*/ +void Calendar::prepareGetActual(UCalendarDateFields field, UBool isMinimum, UErrorCode &status) +{ + if (U_FAILURE(status)) { + return; + } + set(UCAL_MILLISECONDS_IN_DAY, 0); + + switch (field) { + case UCAL_YEAR: + case UCAL_EXTENDED_YEAR: + set(UCAL_DAY_OF_YEAR, getGreatestMinimum(UCAL_DAY_OF_YEAR)); + break; + + case UCAL_YEAR_WOY: + set(UCAL_WEEK_OF_YEAR, getGreatestMinimum(UCAL_WEEK_OF_YEAR)); + U_FALLTHROUGH; + case UCAL_MONTH: + set(UCAL_DATE, getGreatestMinimum(UCAL_DATE)); + break; + + case UCAL_DAY_OF_WEEK_IN_MONTH: + // For dowim, the maximum occurs for the DOW of the first of the + // month. + set(UCAL_DATE, 1); + set(UCAL_DAY_OF_WEEK, get(UCAL_DAY_OF_WEEK, status)); // Make this user set + break; + + case UCAL_WEEK_OF_MONTH: + case UCAL_WEEK_OF_YEAR: + // If we're counting weeks, set the day of the week to either the + // first or last localized DOW. We know the last week of a month + // or year will contain the first day of the week, and that the + // first week will contain the last DOW. + { + int32_t dow = fFirstDayOfWeek; + if (isMinimum) { + dow = (dow + 6) % 7; // set to last DOW + if (dow < UCAL_SUNDAY) { + dow += 7; + } + } +#if defined (U_DEBUG_CAL) + fprintf(stderr, "prepareGetActualHelper(WOM/WOY) - dow=%d\n", dow); +#endif + set(UCAL_DAY_OF_WEEK, dow); + } + break; + default: + break; + } + + // Do this last to give it the newest time stamp + set(field, getGreatestMinimum(field)); +} + +int32_t Calendar::getActualHelper(UCalendarDateFields field, int32_t startValue, int32_t endValue, UErrorCode &status) const +{ +#if defined (U_DEBUG_CAL) + fprintf(stderr, "getActualHelper(%d,%d .. %d, %s)\n", field, startValue, endValue, u_errorName(status)); +#endif + if (U_FAILURE(status)) { + return 0; + } + if (startValue == endValue) { + // if we know that the maximum value is always the same, just return it + return startValue; + } + + int32_t delta = (endValue > startValue) ? 1 : -1; + + // clone the calendar so we don't mess with the real one, and set it to + // accept anything for the field values + if(U_FAILURE(status)) return startValue; + Calendar *work = clone(); + if(!work) { status = U_MEMORY_ALLOCATION_ERROR; return startValue; } + + // need to resolve time here, otherwise, fields set for actual limit + // may cause conflict with fields previously set (but not yet resolved). + work->complete(status); + + work->setLenient(true); + work->prepareGetActual(field, delta < 0, status); + + // now try each value from the start to the end one by one until + // we get a value that normalizes to another value. The last value that + // normalizes to itself is the actual maximum for the current date + work->set(field, startValue); + + // prepareGetActual sets the first day of week in the same week with + // the first day of a month. Unlike WEEK_OF_YEAR, week number for the + // week which contains days from both previous and current month is + // not unique. For example, last several days in the previous month + // is week 5, and the rest of week is week 1. + int32_t result = startValue; + if ((work->get(field, status) != startValue + && field != UCAL_WEEK_OF_MONTH && delta > 0 ) || U_FAILURE(status)) { +#if defined (U_DEBUG_CAL) + fprintf(stderr, "getActualHelper(fld %d) - got %d (not %d) - %s\n", field, work->get(field,status), startValue, u_errorName(status)); +#endif + } else { + do { + startValue += delta; + work->add(field, delta, status); + if (work->get(field, status) != startValue || U_FAILURE(status)) { +#if defined (U_DEBUG_CAL) + fprintf(stderr, "getActualHelper(fld %d) - got %d (not %d), BREAK - %s\n", field, work->get(field,status), startValue, u_errorName(status)); +#endif + break; + } + result = startValue; + } while (startValue != endValue); + } + delete work; +#if defined (U_DEBUG_CAL) + fprintf(stderr, "getActualHelper(%d) = %d\n", field, result); +#endif + return result; +} + + + + +// ------------------------------------- + +void +Calendar::setWeekData(const Locale& desiredLocale, const char *type, UErrorCode& status) +{ + + if (U_FAILURE(status)) return; + + fFirstDayOfWeek = UCAL_SUNDAY; + fMinimalDaysInFirstWeek = 1; + fWeekendOnset = UCAL_SATURDAY; + fWeekendOnsetMillis = 0; + fWeekendCease = UCAL_SUNDAY; + fWeekendCeaseMillis = 86400000; // 24*60*60*1000 + + // Since week and weekend data is territory based instead of language based, + // we may need to tweak the locale that we are using to try to get the appropriate + // values, using the following logic: + // 1). If the locale has a language but no territory, use the territory as defined by + // the likely subtags. + // 2). If the locale has a script designation then we ignore it, + // then remove it ( i.e. "en_Latn_US" becomes "en_US" ) + + UErrorCode myStatus = U_ZERO_ERROR; + + Locale min(desiredLocale); + min.minimizeSubtags(myStatus); + Locale useLocale; + if ( uprv_strlen(desiredLocale.getCountry()) == 0 || + (uprv_strlen(desiredLocale.getScript()) > 0 && uprv_strlen(min.getScript()) == 0) ) { + myStatus = U_ZERO_ERROR; + Locale max(desiredLocale); + max.addLikelySubtags(myStatus); + useLocale = Locale(max.getLanguage(),max.getCountry()); + } else { + useLocale = desiredLocale; + } + + /* The code here is somewhat of a hack, since week data and weekend data aren't really tied to + a specific calendar, they aren't truly locale data. But this is the only place where valid and + actual locale can be set, so we take a shot at it here by loading a representative resource + from the calendar data. The code used to use the dateTimeElements resource to get first day + of week data, but this was moved to supplemental data under ticket 7755. (JCE) */ + + // Get the monthNames resource bundle for the calendar 'type'. Fallback to gregorian if the resource is not + // found. + LocalUResourceBundlePointer calData(ures_open(nullptr, useLocale.getBaseName(), &status)); + ures_getByKey(calData.getAlias(), gCalendar, calData.getAlias(), &status); + + LocalUResourceBundlePointer monthNames; + if (type != nullptr && *type != '\0' && uprv_strcmp(type, gGregorian) != 0) { + monthNames.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), type, nullptr, &status)); + ures_getByKeyWithFallback(monthNames.getAlias(), gMonthNames, + monthNames.getAlias(), &status); + } + + if (monthNames.isNull() || status == U_MISSING_RESOURCE_ERROR) { + status = U_ZERO_ERROR; + monthNames.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), gGregorian, + monthNames.orphan(), &status)); + ures_getByKeyWithFallback(monthNames.getAlias(), gMonthNames, + monthNames.getAlias(), &status); + } + + if (U_SUCCESS(status)) { + U_LOCALE_BASED(locBased,*this); + locBased.setLocaleIDs(ures_getLocaleByType(monthNames.getAlias(), ULOC_VALID_LOCALE, &status), + ures_getLocaleByType(monthNames.getAlias(), ULOC_ACTUAL_LOCALE, &status)); + } else { + status = U_USING_FALLBACK_WARNING; + return; + } + + char region[ULOC_COUNTRY_CAPACITY]; + (void)ulocimp_getRegionForSupplementalData(desiredLocale.getName(), true, region, sizeof(region), &status); + + // Read week data values from supplementalData week data + UResourceBundle *rb = ures_openDirect(nullptr, "supplementalData", &status); + ures_getByKey(rb, "weekData", rb, &status); + UResourceBundle *weekData = ures_getByKey(rb, region, nullptr, &status); + if (status == U_MISSING_RESOURCE_ERROR && rb != nullptr) { + status = U_ZERO_ERROR; + weekData = ures_getByKey(rb, "001", nullptr, &status); + } + + if (U_FAILURE(status)) { + status = U_USING_FALLBACK_WARNING; + } else { + int32_t arrLen; + const int32_t *weekDataArr = ures_getIntVector(weekData,&arrLen,&status); + if( U_SUCCESS(status) && arrLen == 6 + && 1 <= weekDataArr[0] && weekDataArr[0] <= 7 + && 1 <= weekDataArr[1] && weekDataArr[1] <= 7 + && 1 <= weekDataArr[2] && weekDataArr[2] <= 7 + && 1 <= weekDataArr[4] && weekDataArr[4] <= 7) { + fFirstDayOfWeek = (UCalendarDaysOfWeek)weekDataArr[0]; + fMinimalDaysInFirstWeek = (uint8_t)weekDataArr[1]; + fWeekendOnset = (UCalendarDaysOfWeek)weekDataArr[2]; + fWeekendOnsetMillis = weekDataArr[3]; + fWeekendCease = (UCalendarDaysOfWeek)weekDataArr[4]; + fWeekendCeaseMillis = weekDataArr[5]; + } else { + status = U_INVALID_FORMAT_ERROR; + } + + // Check if the locale has a "fw" u extension and we honor it if present. + // And we don't change the overal status, as the presence / lack of "fw" is not an error. + UErrorCode fwStatus = U_ZERO_ERROR; + char fwExt[ULOC_FULLNAME_CAPACITY] = ""; + desiredLocale.getKeywordValue("fw", fwExt, ULOC_FULLNAME_CAPACITY, fwStatus); + if (U_SUCCESS(fwStatus)) { + if (uprv_strcmp(fwExt, "sun") == 0) { + fFirstDayOfWeek = UCAL_SUNDAY; + } else if (uprv_strcmp(fwExt, "mon") == 0) { + fFirstDayOfWeek = UCAL_MONDAY; + } else if (uprv_strcmp(fwExt, "tue") == 0) { + fFirstDayOfWeek = UCAL_TUESDAY; + } else if (uprv_strcmp(fwExt, "wed") == 0) { + fFirstDayOfWeek = UCAL_WEDNESDAY; + } else if (uprv_strcmp(fwExt, "thu") == 0) { + fFirstDayOfWeek = UCAL_THURSDAY; + } else if (uprv_strcmp(fwExt, "fri") == 0) { + fFirstDayOfWeek = UCAL_FRIDAY; + } else if (uprv_strcmp(fwExt, "sat") == 0) { + fFirstDayOfWeek = UCAL_SATURDAY; + } + } + } + ures_close(weekData); + ures_close(rb); +} + +/** +* Recompute the time and update the status fields isTimeSet +* and areFieldsSet. Callers should check isTimeSet and only +* call this method if isTimeSet is false. +*/ +void +Calendar::updateTime(UErrorCode& status) +{ + computeTime(status); + if(U_FAILURE(status)) + return; + + // If we are lenient, we need to recompute the fields to normalize + // the values. Also, if we haven't set all the fields yet (i.e., + // in a newly-created object), we need to fill in the fields. [LIU] + if (isLenient() || ! fAreAllFieldsSet) + fAreFieldsSet = false; + + fIsTimeSet = true; + fAreFieldsVirtuallySet = false; +} + +Locale +Calendar::getLocale(ULocDataLocaleType type, UErrorCode& status) const { + U_LOCALE_BASED(locBased, *this); + return locBased.getLocale(type, status); +} + +const char * +Calendar::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const { + U_LOCALE_BASED(locBased, *this); + return locBased.getLocaleID(type, status); +} + +void +Calendar::recalculateStamp() { + int32_t index; + int32_t currentValue; + int32_t j, i; + + fNextStamp = 1; + + for (j = 0; j < UCAL_FIELD_COUNT; j++) { + currentValue = STAMP_MAX; + index = -1; + for (i = 0; i < UCAL_FIELD_COUNT; i++) { + if (fStamp[i] > fNextStamp && fStamp[i] < currentValue) { + currentValue = fStamp[i]; + index = i; + } + } + + if (index >= 0) { + fStamp[index] = ++fNextStamp; + } else { + break; + } + } + fNextStamp++; +} + +// Deprecated function. This doesn't need to be inline. +void +Calendar::internalSet(EDateFields field, int32_t value) +{ + internalSet((UCalendarDateFields) field, value); +} + +int32_t Calendar::internalGetMonth() const { + if (resolveFields(kMonthPrecedence) == UCAL_MONTH) { + return internalGet(UCAL_MONTH); + } + return internalGet(UCAL_ORDINAL_MONTH); +} + +int32_t Calendar::internalGetMonth(int32_t defaultValue) const { + if (resolveFields(kMonthPrecedence) == UCAL_MONTH) { + return internalGet(UCAL_MONTH, defaultValue); + } + return internalGet(UCAL_ORDINAL_MONTH); +} + +BasicTimeZone* +Calendar::getBasicTimeZone() const { + if (dynamic_cast(fZone) != nullptr + || dynamic_cast(fZone) != nullptr + || dynamic_cast(fZone) != nullptr + || dynamic_cast(fZone) != nullptr) { + return (BasicTimeZone*)fZone; + } + return nullptr; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + + +//eof diff --git a/deps/icu-small/source/i18n/casetrn.cpp b/deps/icu-small/source/i18n/casetrn.cpp index a065330cac219e..b9661a1f19160b 100644 --- a/deps/icu-small/source/i18n/casetrn.cpp +++ b/deps/icu-small/source/i18n/casetrn.cpp @@ -1,193 +1,193 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2001-2011, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: casetrn.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004sep03 -* created by: Markus W. Scherer -* -* Implementation class for lower-/upper-/title-casing transliterators. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/uchar.h" -#include "unicode/ustring.h" -#include "unicode/utf.h" -#include "unicode/utf16.h" -#include "tolowtrn.h" -#include "ucase.h" -#include "cpputils.h" - -/* case context iterator using a Replaceable */ -U_CFUNC UChar32 U_CALLCONV -utrans_rep_caseContextIterator(void *context, int8_t dir) -{ - U_NAMESPACE_USE - - UCaseContext *csc=(UCaseContext *)context; - Replaceable *rep=(Replaceable *)csc->p; - UChar32 c; - - if(dir<0) { - /* reset for backward iteration */ - csc->index=csc->cpStart; - csc->dir=dir; - } else if(dir>0) { - /* reset for forward iteration */ - csc->index=csc->cpLimit; - csc->dir=dir; - } else { - /* continue current iteration direction */ - dir=csc->dir; - } - - // automatically adjust start and limit if the Replaceable disagrees - // with the original values - if(dir<0) { - if(csc->startindex) { - c=rep->char32At(csc->index-1); - if(c<0) { - csc->start=csc->index; - } else { - csc->index-=U16_LENGTH(c); - return c; - } - } - } else { - // detect, and store in csc->b1, if we hit the limit - if(csc->indexlimit) { - c=rep->char32At(csc->index); - if(c<0) { - csc->limit=csc->index; - csc->b1=true; - } else { - csc->index+=U16_LENGTH(c); - return c; - } - } else { - csc->b1=true; - } - } - return U_SENTINEL; -} - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(CaseMapTransliterator) - -/** - * Constructs a transliterator. - */ -CaseMapTransliterator::CaseMapTransliterator(const UnicodeString &id, UCaseMapFull *map) : - Transliterator(id, 0), - fMap(map) -{ - // TODO test incremental mode with context-sensitive text (e.g. greek sigma) - // TODO need to call setMaximumContextLength()?! -} - -/** - * Destructor. - */ -CaseMapTransliterator::~CaseMapTransliterator() { -} - -/** - * Copy constructor. - */ -CaseMapTransliterator::CaseMapTransliterator(const CaseMapTransliterator& o) : - Transliterator(o), - fMap(o.fMap) -{ -} - -/** - * Assignment operator. - */ -/*CaseMapTransliterator& CaseMapTransliterator::operator=(const CaseMapTransliterator& o) { - Transliterator::operator=(o); - fMap = o.fMap; - return *this; -}*/ - -/** - * Transliterator API. - */ -/*CaseMapTransliterator* CaseMapTransliterator::clone() const { - return new CaseMapTransliterator(*this); -}*/ - -/** - * Implements {@link Transliterator#handleTransliterate}. - */ -void CaseMapTransliterator::handleTransliterate(Replaceable& text, - UTransPosition& offsets, - UBool isIncremental) const -{ - if (offsets.start >= offsets.limit) { - return; - } - - UCaseContext csc; - uprv_memset(&csc, 0, sizeof(csc)); - csc.p = &text; - csc.start = offsets.contextStart; - csc.limit = offsets.contextLimit; - - UnicodeString tmp; - const UChar *s; - UChar32 c; - int32_t textPos, delta, result; - - for(textPos=offsets.start; textPos=0) { - // replace the current code point with its full case mapping result - // see UCASE_MAX_STRING_LENGTH - if(result<=UCASE_MAX_STRING_LENGTH) { - // string s[result] - tmp.setTo(false, s, result); - delta=result-U16_LENGTH(c); - } else { - // single code point - tmp.setTo(result); - delta=tmp.length()-U16_LENGTH(c); - } - text.handleReplaceBetween(csc.cpStart, textPos, tmp); - if(delta!=0) { - textPos+=delta; - csc.limit=offsets.contextLimit+=delta; - offsets.limit+=delta; - } - } - } - offsets.start=textPos; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2001-2011, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: casetrn.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004sep03 +* created by: Markus W. Scherer +* +* Implementation class for lower-/upper-/title-casing transliterators. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/uchar.h" +#include "unicode/ustring.h" +#include "unicode/utf.h" +#include "unicode/utf16.h" +#include "tolowtrn.h" +#include "ucase.h" +#include "cpputils.h" + +/* case context iterator using a Replaceable */ +U_CFUNC UChar32 U_CALLCONV +utrans_rep_caseContextIterator(void *context, int8_t dir) +{ + U_NAMESPACE_USE + + UCaseContext *csc=(UCaseContext *)context; + Replaceable *rep=(Replaceable *)csc->p; + UChar32 c; + + if(dir<0) { + /* reset for backward iteration */ + csc->index=csc->cpStart; + csc->dir=dir; + } else if(dir>0) { + /* reset for forward iteration */ + csc->index=csc->cpLimit; + csc->dir=dir; + } else { + /* continue current iteration direction */ + dir=csc->dir; + } + + // automatically adjust start and limit if the Replaceable disagrees + // with the original values + if(dir<0) { + if(csc->startindex) { + c=rep->char32At(csc->index-1); + if(c<0) { + csc->start=csc->index; + } else { + csc->index-=U16_LENGTH(c); + return c; + } + } + } else { + // detect, and store in csc->b1, if we hit the limit + if(csc->indexlimit) { + c=rep->char32At(csc->index); + if(c<0) { + csc->limit=csc->index; + csc->b1=true; + } else { + csc->index+=U16_LENGTH(c); + return c; + } + } else { + csc->b1=true; + } + } + return U_SENTINEL; +} + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(CaseMapTransliterator) + +/** + * Constructs a transliterator. + */ +CaseMapTransliterator::CaseMapTransliterator(const UnicodeString &id, UCaseMapFull *map) : + Transliterator(id, 0), + fMap(map) +{ + // TODO test incremental mode with context-sensitive text (e.g. greek sigma) + // TODO need to call setMaximumContextLength()?! +} + +/** + * Destructor. + */ +CaseMapTransliterator::~CaseMapTransliterator() { +} + +/** + * Copy constructor. + */ +CaseMapTransliterator::CaseMapTransliterator(const CaseMapTransliterator& o) : + Transliterator(o), + fMap(o.fMap) +{ +} + +/** + * Assignment operator. + */ +/*CaseMapTransliterator& CaseMapTransliterator::operator=(const CaseMapTransliterator& o) { + Transliterator::operator=(o); + fMap = o.fMap; + return *this; +}*/ + +/** + * Transliterator API. + */ +/*CaseMapTransliterator* CaseMapTransliterator::clone() const { + return new CaseMapTransliterator(*this); +}*/ + +/** + * Implements {@link Transliterator#handleTransliterate}. + */ +void CaseMapTransliterator::handleTransliterate(Replaceable& text, + UTransPosition& offsets, + UBool isIncremental) const +{ + if (offsets.start >= offsets.limit) { + return; + } + + UCaseContext csc; + uprv_memset(&csc, 0, sizeof(csc)); + csc.p = &text; + csc.start = offsets.contextStart; + csc.limit = offsets.contextLimit; + + UnicodeString tmp; + const char16_t *s; + UChar32 c; + int32_t textPos, delta, result; + + for(textPos=offsets.start; textPos=0) { + // replace the current code point with its full case mapping result + // see UCASE_MAX_STRING_LENGTH + if(result<=UCASE_MAX_STRING_LENGTH) { + // string s[result] + tmp.setTo(false, s, result); + delta=result-U16_LENGTH(c); + } else { + // single code point + tmp.setTo(result); + delta=tmp.length()-U16_LENGTH(c); + } + text.handleReplaceBetween(csc.cpStart, textPos, tmp); + if(delta!=0) { + textPos+=delta; + csc.limit=offsets.contextLimit+=delta; + offsets.limit+=delta; + } + } + } + offsets.start=textPos; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ diff --git a/deps/icu-small/source/i18n/casetrn.h b/deps/icu-small/source/i18n/casetrn.h index a00480db60b99a..f676e1be162fde 100644 --- a/deps/icu-small/source/i18n/casetrn.h +++ b/deps/icu-small/source/i18n/casetrn.h @@ -1,105 +1,105 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2001-2008, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: casetrn.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2004sep03 -* created by: Markus W. Scherer -* -* Implementation class for lower-/upper-/title-casing transliterators. -*/ - -#ifndef __CASETRN_H__ -#define __CASETRN_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/translit.h" -#include "ucase.h" - -U_NAMESPACE_BEGIN - -/** - * A transliterator that performs locale-sensitive - * case mapping. - */ -class CaseMapTransliterator : public Transliterator { -public: - /** - * Constructs a transliterator. - * @param loc the given locale. - * @param id the transliterator ID. - * @param map the full case mapping function (see ucase.h) - */ - CaseMapTransliterator(const UnicodeString &id, UCaseMapFull *map); - - /** - * Destructor. - */ - virtual ~CaseMapTransliterator(); - - /** - * Copy constructor. - */ - CaseMapTransliterator(const CaseMapTransliterator&); - - /** - * Transliterator API. - * @return a copy of the object. - */ - virtual CaseMapTransliterator* clone() const override = 0; - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - //virtual UClassID getDynamicClassID() const; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); - -protected: - /** - * Implements {@link Transliterator#handleTransliterate}. - * @param text the buffer holding transliterated and - * untransliterated text - * @param offset the start and limit of the text, the position - * of the cursor, and the start and limit of transliteration. - * @param incremental if true, assume more text may be coming after - * pos.contextLimit. Otherwise, assume the text is complete. - */ - virtual void handleTransliterate(Replaceable& text, - UTransPosition& offsets, - UBool isIncremental) const override; - - UCaseMapFull *fMap; - -private: - /** - * Assignment operator. - */ - CaseMapTransliterator& operator=(const CaseMapTransliterator&); - -}; - -U_NAMESPACE_END - -/** case context iterator using a Replaceable. This must be a C function because it is a callback. */ -U_CFUNC UChar32 U_CALLCONV -utrans_rep_caseContextIterator(void *context, int8_t dir); - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2001-2008, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: casetrn.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2004sep03 +* created by: Markus W. Scherer +* +* Implementation class for lower-/upper-/title-casing transliterators. +*/ + +#ifndef __CASETRN_H__ +#define __CASETRN_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/translit.h" +#include "ucase.h" + +U_NAMESPACE_BEGIN + +/** + * A transliterator that performs locale-sensitive + * case mapping. + */ +class CaseMapTransliterator : public Transliterator { +public: + /** + * Constructs a transliterator. + * @param loc the given locale. + * @param id the transliterator ID. + * @param map the full case mapping function (see ucase.h) + */ + CaseMapTransliterator(const UnicodeString &id, UCaseMapFull *map); + + /** + * Destructor. + */ + virtual ~CaseMapTransliterator(); + + /** + * Copy constructor. + */ + CaseMapTransliterator(const CaseMapTransliterator&); + + /** + * Transliterator API. + * @return a copy of the object. + */ + virtual CaseMapTransliterator* clone() const override = 0; + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + //virtual UClassID getDynamicClassID() const; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + +protected: + /** + * Implements {@link Transliterator#handleTransliterate}. + * @param text the buffer holding transliterated and + * untransliterated text + * @param offset the start and limit of the text, the position + * of the cursor, and the start and limit of transliteration. + * @param incremental if true, assume more text may be coming after + * pos.contextLimit. Otherwise, assume the text is complete. + */ + virtual void handleTransliterate(Replaceable& text, + UTransPosition& offsets, + UBool isIncremental) const override; + + UCaseMapFull *fMap; + +private: + /** + * Assignment operator. + */ + CaseMapTransliterator& operator=(const CaseMapTransliterator&); + +}; + +U_NAMESPACE_END + +/** case context iterator using a Replaceable. This must be a C function because it is a callback. */ +U_CFUNC UChar32 U_CALLCONV +utrans_rep_caseContextIterator(void *context, int8_t dir); + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/cecal.cpp b/deps/icu-small/source/i18n/cecal.cpp index 60e3d4b2657e45..066d7a2a50b9f3 100644 --- a/deps/icu-small/source/i18n/cecal.cpp +++ b/deps/icu-small/source/i18n/cecal.cpp @@ -1,151 +1,158 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2003 - 2009, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "cecal.h" -#include "gregoimp.h" //Math - -U_NAMESPACE_BEGIN - -static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { - // Minimum Greatest Least Maximum - // Minimum Maximum - { 0, 0, 1, 1}, // ERA - { 1, 1, 5000000, 5000000}, // YEAR - { 0, 0, 12, 12}, // MONTH - { 1, 1, 52, 53}, // WEEK_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH - { 1, 1, 5, 30}, // DAY_OF_MONTH - { 1, 1, 365, 366}, // DAY_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK - { -1, -1, 1, 5}, // DAY_OF_WEEK_IN_MONTH - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET - { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL - { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH -}; - -//------------------------------------------------------------------------- -// Constructors... -//------------------------------------------------------------------------- - -CECalendar::CECalendar(const Locale& aLocale, UErrorCode& success) -: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success) -{ - setTimeInMillis(getNow(), success); -} - -CECalendar::CECalendar (const CECalendar& other) -: Calendar(other) -{ -} - -CECalendar::~CECalendar() -{ -} - -CECalendar& -CECalendar::operator=(const CECalendar& right) -{ - Calendar::operator=(right); - return *this; -} - -//------------------------------------------------------------------------- -// Calendar framework -//------------------------------------------------------------------------- - -int32_t -CECalendar::handleComputeMonthStart(int32_t eyear,int32_t emonth, UBool /*useMonth*/) const -{ - return ceToJD(eyear, emonth, 0, getJDEpochOffset()); -} - -int32_t -CECalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const -{ - return LIMITS[field][limitType]; -} - -UBool -CECalendar::inDaylightTime(UErrorCode& status) const -{ - if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) { - return false; - } - - // Force an update of the state of the Calendar. - ((CECalendar*)this)->complete(status); // cast away const - - return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : false); -} - -UBool -CECalendar::haveDefaultCentury() const -{ - return true; -} - -//------------------------------------------------------------------------- -// Calendar system Conversion methods... -//------------------------------------------------------------------------- -int32_t -CECalendar::ceToJD(int32_t year, int32_t month, int32_t date, int32_t jdEpochOffset) -{ - // handle month > 12, < 0 (e.g. from add/set) - if ( month >= 0 ) { - year += month/13; - month %= 13; - } else { - ++month; - year += month/13 - 1; - month = month%13 + 12; - } - return (int32_t) ( - jdEpochOffset // difference from Julian epoch to 1,1,1 - + 365 * year // number of days from years - + ClockMath::floorDivide(year, 4) // extra day of leap year - + 30 * month // number of days from months (months are 0-based) - + date - 1 // number of days for present month (1 based) - ); -} - -void -CECalendar::jdToCE(int32_t julianDay, int32_t jdEpochOffset, int32_t& year, int32_t& month, int32_t& day) -{ - int32_t c4; // number of 4 year cycle (1461 days) - int32_t r4; // remainder of 4 year cycle, always positive - - c4 = ClockMath::floorDivide(julianDay - jdEpochOffset, 1461, &r4); - - year = 4 * c4 + (r4/365 - r4/1460); // 4 * + - - int32_t doy = (r4 == 1460) ? 365 : (r4 % 365); // days in present year - - month = doy / 30; // 30 -> Coptic/Ethiopic month length up to 12th month - day = (doy % 30) + 1; // 1-based days in a month -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2003 - 2009, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "cecal.h" +#include "gregoimp.h" //Math +#include "cstring.h" + +U_NAMESPACE_BEGIN + +static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { + // Minimum Greatest Least Maximum + // Minimum Maximum + { 0, 0, 1, 1}, // ERA + { 1, 1, 5000000, 5000000}, // YEAR + { 0, 0, 12, 12}, // MONTH + { 1, 1, 52, 53}, // WEEK_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH + { 1, 1, 5, 30}, // DAY_OF_MONTH + { 1, 1, 365, 366}, // DAY_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK + { -1, -1, 1, 5}, // DAY_OF_WEEK_IN_MONTH + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET + { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL + { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH + { 0, 0, 12, 12}, // ORDINAL_MONTH +}; + +//------------------------------------------------------------------------- +// Constructors... +//------------------------------------------------------------------------- + +CECalendar::CECalendar(const Locale& aLocale, UErrorCode& success) +: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success) +{ + setTimeInMillis(getNow(), success); +} + +CECalendar::CECalendar (const CECalendar& other) +: Calendar(other) +{ +} + +CECalendar::~CECalendar() +{ +} + +CECalendar& +CECalendar::operator=(const CECalendar& right) +{ + Calendar::operator=(right); + return *this; +} + +//------------------------------------------------------------------------- +// Calendar framework +//------------------------------------------------------------------------- + +int32_t +CECalendar::handleComputeMonthStart(int32_t eyear,int32_t emonth, UBool /*useMonth*/) const +{ + return ceToJD(eyear, emonth, 0, getJDEpochOffset()); +} + +int32_t +CECalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const +{ + return LIMITS[field][limitType]; +} + +UBool +CECalendar::haveDefaultCentury() const +{ + return true; +} + +//------------------------------------------------------------------------- +// Calendar system Conversion methods... +//------------------------------------------------------------------------- +int32_t +CECalendar::ceToJD(int32_t year, int32_t month, int32_t date, int32_t jdEpochOffset) +{ + // handle month > 12, < 0 (e.g. from add/set) + if ( month >= 0 ) { + year += month/13; + month %= 13; + } else { + ++month; + year += month/13 - 1; + month = month%13 + 12; + } + return (int32_t) ( + jdEpochOffset // difference from Julian epoch to 1,1,1 + + 365 * year // number of days from years + + ClockMath::floorDivide(year, 4) // extra day of leap year + + 30 * month // number of days from months (months are 0-based) + + date - 1 // number of days for present month (1 based) + ); +} + +void +CECalendar::jdToCE(int32_t julianDay, int32_t jdEpochOffset, int32_t& year, int32_t& month, int32_t& day) +{ + int32_t c4; // number of 4 year cycle (1461 days) + int32_t r4; // remainder of 4 year cycle, always positive + + c4 = ClockMath::floorDivide(julianDay - jdEpochOffset, 1461, &r4); + + year = 4 * c4 + (r4/365 - r4/1460); // 4 * + + + int32_t doy = (r4 == 1460) ? 365 : (r4 % 365); // days in present year + + month = doy / 30; // 30 -> Coptic/Ethiopic month length up to 12th month + day = (doy % 30) + 1; // 1-based days in a month +} + +static const char* kMonthCode13 = "M13"; + +const char* CECalendar::getTemporalMonthCode(UErrorCode& status) const { + if (get(UCAL_MONTH, status) == 12) return kMonthCode13; + return Calendar::getTemporalMonthCode(status); +} + +void +CECalendar::setTemporalMonthCode(const char* code, UErrorCode& status) { + if (U_FAILURE(status)) return; + if (uprv_strcmp(code, kMonthCode13) == 0) { + set(UCAL_MONTH, 12); + set(UCAL_IS_LEAP_MONTH, 0); + return; + } + Calendar::setTemporalMonthCode(code, status); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ +//eof diff --git a/deps/icu-small/source/i18n/cecal.h b/deps/icu-small/source/i18n/cecal.h index 9ac71f6ba38680..7321078a3903e8 100644 --- a/deps/icu-small/source/i18n/cecal.h +++ b/deps/icu-small/source/i18n/cecal.h @@ -1,136 +1,155 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2003 - 2008, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - -#ifndef CECAL_H -#define CECAL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/calendar.h" - -U_NAMESPACE_BEGIN - -/** - * Base class for EthiopicCalendar and CopticCalendar. - * @internal - */ -class U_I18N_API CECalendar : public Calendar { - -protected: - //------------------------------------------------------------------------- - // Constructors... - //------------------------------------------------------------------------- - - /** - * Constructs a CECalendar based on the current time in the default time zone - * with the given locale with the Julian epoch offiset - * - * @param aLocale The given locale. - * @param success Indicates the status of CECalendar object construction. - * Returns U_ZERO_ERROR if constructed successfully. - * @internal - */ - CECalendar(const Locale& aLocale, UErrorCode& success); - - /** - * Copy Constructor - * @internal - */ - CECalendar (const CECalendar& other); - - /** - * Destructor. - * @internal - */ - virtual ~CECalendar(); - - /** - * Default assignment operator - * @param right Calendar object to be copied - * @internal - */ - CECalendar& operator=(const CECalendar& right); - -protected: - //------------------------------------------------------------------------- - // Calendar framework - //------------------------------------------------------------------------- - - /** - * Return JD of start of given month/extended year - * @internal - */ - virtual int32_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override; - - /** - * Calculate the limit for a specified type of limit and field - * @internal - */ - virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; - - /** - * (Overrides Calendar) Return true if the current date for this Calendar is in - * Daylight Savings Time. Recognizes DST_OFFSET, if it is set. - * - * @param status Fill-in parameter which receives the status of this operation. - * @return True if the current date for this Calendar is in Daylight Savings Time, - * false, otherwise. - * @internal - */ - virtual UBool inDaylightTime(UErrorCode&) const override; - - /** - * Returns true because Coptic/Ethiopic Calendar does have a default century - * @internal - */ - virtual UBool haveDefaultCentury() const override; - -protected: - /** - * The Coptic and Ethiopic calendars differ only in their epochs. - * This method must be implemented by CECalendar subclasses to - * return the date offset from Julian - * @internal - */ - virtual int32_t getJDEpochOffset() const = 0; - - /** - * Convert an Coptic/Ethiopic year, month, and day to a Julian day. - * - * @param year the extended year - * @param month the month - * @param day the day - * @param jdEpochOffset the epoch offset from Julian epoch - * @return Julian day - * @internal - */ - static int32_t ceToJD(int32_t year, int32_t month, int32_t date, - int32_t jdEpochOffset); - - /** - * Convert a Julian day to an Coptic/Ethiopic year, month and day - * - * @param julianDay the Julian day - * @param jdEpochOffset the epoch offset from Julian epoch - * @param year receives the extended year - * @param month receives the month - * @param date receives the day - * @internal - */ - static void jdToCE(int32_t julianDay, int32_t jdEpochOffset, - int32_t& year, int32_t& month, int32_t& day); -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ -#endif -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2003 - 2008, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +*/ + +#ifndef CECAL_H +#define CECAL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" + +U_NAMESPACE_BEGIN + +/** + * Base class for EthiopicCalendar and CopticCalendar. + * @internal + */ +class U_I18N_API CECalendar : public Calendar { + +public: + + /** + * Gets The Temporal monthCode value corresponding to the month for the date. + * The value is a string identifier that starts with the literal grapheme + * "M" followed by two graphemes representing the zero-padded month number + * of the current month in a normal (non-leap) year. For the short thirteen + * month in each year in the CECalendar, the value is "M13". + * + * @param status ICU Error Code + * @return One of 13 possible strings in {"M01".. "M12", "M13"}. + * @draft ICU 73 + */ + virtual const char* getTemporalMonthCode(UErrorCode& status) const override; + + /** + * Sets The Temporal monthCode which is a string identifier that starts + * with the literal grapheme "M" followed by two graphemes representing + * the zero-padded month number of the current month in a normal + * (non-leap) year. For CECalendar calendar, the values + * are "M01" .. "M13" while the "M13" is represent the short thirteen month + * in each year. + * + * @param temporalMonth The value to be set for temporal monthCode. + * @param status ICU Error Code + * + * @draft ICU 73 + */ + virtual void setTemporalMonthCode(const char* code, UErrorCode& status) override; + +protected: + //------------------------------------------------------------------------- + // Constructors... + //------------------------------------------------------------------------- + + /** + * Constructs a CECalendar based on the current time in the default time zone + * with the given locale with the Julian epoch offiset + * + * @param aLocale The given locale. + * @param success Indicates the status of CECalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + CECalendar(const Locale& aLocale, UErrorCode& success); + + /** + * Copy Constructor + * @internal + */ + CECalendar (const CECalendar& other); + + /** + * Destructor. + * @internal + */ + virtual ~CECalendar(); + + /** + * Default assignment operator + * @param right Calendar object to be copied + * @internal + */ + CECalendar& operator=(const CECalendar& right); + +protected: + //------------------------------------------------------------------------- + // Calendar framework + //------------------------------------------------------------------------- + + /** + * Return JD of start of given month/extended year + * @internal + */ + virtual int32_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override; + + /** + * Calculate the limit for a specified type of limit and field + * @internal + */ + virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; + + /** + * Returns true because Coptic/Ethiopic Calendar does have a default century + * @internal + */ + virtual UBool haveDefaultCentury() const override; + +protected: + /** + * The Coptic and Ethiopic calendars differ only in their epochs. + * This method must be implemented by CECalendar subclasses to + * return the date offset from Julian + * @internal + */ + virtual int32_t getJDEpochOffset() const = 0; + + /** + * Convert an Coptic/Ethiopic year, month, and day to a Julian day. + * + * @param year the extended year + * @param month the month + * @param day the day + * @param jdEpochOffset the epoch offset from Julian epoch + * @return Julian day + * @internal + */ + static int32_t ceToJD(int32_t year, int32_t month, int32_t date, + int32_t jdEpochOffset); + + /** + * Convert a Julian day to an Coptic/Ethiopic year, month and day + * + * @param julianDay the Julian day + * @param jdEpochOffset the epoch offset from Julian epoch + * @param year receives the extended year + * @param month receives the month + * @param date receives the day + * @internal + */ + static void jdToCE(int32_t julianDay, int32_t jdEpochOffset, + int32_t& year, int32_t& month, int32_t& day); +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ +#endif +//eof diff --git a/deps/icu-small/source/i18n/chnsecal.cpp b/deps/icu-small/source/i18n/chnsecal.cpp index e48c90eb5af877..d130bf09169213 100644 --- a/deps/icu-small/source/i18n/chnsecal.cpp +++ b/deps/icu-small/source/i18n/chnsecal.cpp @@ -1,902 +1,992 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ****************************************************************************** - * Copyright (C) 2007-2014, International Business Machines Corporation - * and others. All Rights Reserved. - ****************************************************************************** - * - * File CHNSECAL.CPP - * - * Modification History: - * - * Date Name Description - * 9/18/2007 ajmacher ported from java ChineseCalendar - ***************************************************************************** - */ - -#include "chnsecal.h" - -#if !UCONFIG_NO_FORMATTING - -#include "umutex.h" -#include -#include "gregoimp.h" // Math -#include "astro.h" // CalendarAstronomer -#include "unicode/simpletz.h" -#include "uhash.h" -#include "ucln_in.h" - -// Debugging -#ifdef U_DEBUG_CHNSECAL -# include -# include -static void debug_chnsecal_loc(const char *f, int32_t l) -{ - fprintf(stderr, "%s:%d: ", f, l); -} - -static void debug_chnsecal_msg(const char *pat, ...) -{ - va_list ap; - va_start(ap, pat); - vfprintf(stderr, pat, ap); - fflush(stderr); -} -// must use double parens, i.e.: U_DEBUG_CHNSECAL_MSG(("four is: %d",4)); -#define U_DEBUG_CHNSECAL_MSG(x) {debug_chnsecal_loc(__FILE__,__LINE__);debug_chnsecal_msg x;} -#else -#define U_DEBUG_CHNSECAL_MSG(x) -#endif - - -// --- The cache -- -static icu::UMutex astroLock; -static icu::CalendarAstronomer *gChineseCalendarAstro = NULL; - -// Lazy Creation & Access synchronized by class CalendarCache with a mutex. -static icu::CalendarCache *gChineseCalendarWinterSolsticeCache = NULL; -static icu::CalendarCache *gChineseCalendarNewYearCache = NULL; - -static icu::TimeZone *gChineseCalendarZoneAstroCalc = NULL; -static icu::UInitOnce gChineseCalendarZoneAstroCalcInitOnce {}; - -/** - * The start year of the Chinese calendar, the 61st year of the reign - * of Huang Di. Some sources use the first year of his reign, - * resulting in EXTENDED_YEAR values 60 years greater and ERA (cycle) - * values one greater. - */ -static const int32_t CHINESE_EPOCH_YEAR = -2636; // Gregorian year - -/** - * The offset from GMT in milliseconds at which we perform astronomical - * computations. Some sources use a different historically accurate - * offset of GMT+7:45:40 for years before 1929; we do not do this. - */ -static const int32_t CHINA_OFFSET = 8 * kOneHour; - -/** - * Value to be added or subtracted from the local days of a new moon to - * get close to the next or prior new moon, but not cross it. Must be - * >= 1 and < CalendarAstronomer.SYNODIC_MONTH. - */ -static const int32_t SYNODIC_GAP = 25; - - -U_CDECL_BEGIN -static UBool calendar_chinese_cleanup(void) { - if (gChineseCalendarAstro) { - delete gChineseCalendarAstro; - gChineseCalendarAstro = NULL; - } - if (gChineseCalendarWinterSolsticeCache) { - delete gChineseCalendarWinterSolsticeCache; - gChineseCalendarWinterSolsticeCache = NULL; - } - if (gChineseCalendarNewYearCache) { - delete gChineseCalendarNewYearCache; - gChineseCalendarNewYearCache = NULL; - } - if (gChineseCalendarZoneAstroCalc) { - delete gChineseCalendarZoneAstroCalc; - gChineseCalendarZoneAstroCalc = NULL; - } - gChineseCalendarZoneAstroCalcInitOnce.reset(); - return true; -} -U_CDECL_END - -U_NAMESPACE_BEGIN - - -// Implementation of the ChineseCalendar class - - -//------------------------------------------------------------------------- -// Constructors... -//------------------------------------------------------------------------- - - -ChineseCalendar* ChineseCalendar::clone() const { - return new ChineseCalendar(*this); -} - -ChineseCalendar::ChineseCalendar(const Locale& aLocale, UErrorCode& success) -: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success), - isLeapYear(false), - fEpochYear(CHINESE_EPOCH_YEAR), - fZoneAstroCalc(getChineseCalZoneAstroCalc()) -{ - setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. -} - -ChineseCalendar::ChineseCalendar(const Locale& aLocale, int32_t epochYear, - const TimeZone* zoneAstroCalc, UErrorCode &success) -: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success), - isLeapYear(false), - fEpochYear(epochYear), - fZoneAstroCalc(zoneAstroCalc) -{ - setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. -} - -ChineseCalendar::ChineseCalendar(const ChineseCalendar& other) : Calendar(other) { - isLeapYear = other.isLeapYear; - fEpochYear = other.fEpochYear; - fZoneAstroCalc = other.fZoneAstroCalc; -} - -ChineseCalendar::~ChineseCalendar() -{ -} - -const char *ChineseCalendar::getType() const { - return "chinese"; -} - -static void U_CALLCONV initChineseCalZoneAstroCalc() { - gChineseCalendarZoneAstroCalc = new SimpleTimeZone(CHINA_OFFSET, UNICODE_STRING_SIMPLE("CHINA_ZONE") ); - ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup); -} - -const TimeZone* ChineseCalendar::getChineseCalZoneAstroCalc(void) const { - umtx_initOnce(gChineseCalendarZoneAstroCalcInitOnce, &initChineseCalZoneAstroCalc); - return gChineseCalendarZoneAstroCalc; -} - -//------------------------------------------------------------------------- -// Minimum / Maximum access functions -//------------------------------------------------------------------------- - - -static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { - // Minimum Greatest Least Maximum - // Minimum Maximum - { 1, 1, 83333, 83333}, // ERA - { 1, 1, 60, 60}, // YEAR - { 0, 0, 11, 11}, // MONTH - { 1, 1, 50, 55}, // WEEK_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH - { 1, 1, 29, 30}, // DAY_OF_MONTH - { 1, 1, 353, 385}, // DAY_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK - { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET - { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL - { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY - { 0, 0, 1, 1}, // IS_LEAP_MONTH -}; - - -/** -* @draft ICU 2.4 -*/ -int32_t ChineseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { - return LIMITS[field][limitType]; -} - - -//---------------------------------------------------------------------- -// Calendar framework -//---------------------------------------------------------------------- - -/** - * Implement abstract Calendar method to return the extended year - * defined by the current fields. This will use either the ERA and - * YEAR field as the cycle and year-of-cycle, or the EXTENDED_YEAR - * field as the continuous year count, depending on which is newer. - * @stable ICU 2.8 - */ -int32_t ChineseCalendar::handleGetExtendedYear() { - int32_t year; - if (newestStamp(UCAL_ERA, UCAL_YEAR, kUnset) <= fStamp[UCAL_EXTENDED_YEAR]) { - year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 - } else { - int32_t cycle = internalGet(UCAL_ERA, 1) - 1; // 0-based cycle - // adjust to the instance specific epoch - year = cycle * 60 + internalGet(UCAL_YEAR, 1) - (fEpochYear - CHINESE_EPOCH_YEAR); - } - return year; -} - -/** - * Override Calendar method to return the number of days in the given - * extended year and month. - * - *

Note: This method also reads the IS_LEAP_MONTH field to determine - * whether or not the given month is a leap month. - * @stable ICU 2.8 - */ -int32_t ChineseCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { - int32_t thisStart = handleComputeMonthStart(extendedYear, month, true) - - kEpochStartAsJulianDay + 1; // Julian day -> local days - int32_t nextStart = newMoonNear(thisStart + SYNODIC_GAP, true); - return nextStart - thisStart; -} - -/** - * Override Calendar to compute several fields specific to the Chinese - * calendar system. These are: - * - *

  • ERA - *
  • YEAR - *
  • MONTH - *
  • DAY_OF_MONTH - *
  • DAY_OF_YEAR - *
  • EXTENDED_YEAR
- * - * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this - * method is called. The getGregorianXxx() methods return Gregorian - * calendar equivalents for the given Julian day. - * - *

Compute the ChineseCalendar-specific field IS_LEAP_MONTH. - * @stable ICU 2.8 - */ -void ChineseCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) { - - computeChineseFields(julianDay - kEpochStartAsJulianDay, // local days - getGregorianYear(), getGregorianMonth(), - true); // set all fields -} - -/** - * Field resolution table that incorporates IS_LEAP_MONTH. - */ -const UFieldResolutionTable ChineseCalendar::CHINESE_DATE_PRECEDENCE[] = -{ - { - { UCAL_DAY_OF_MONTH, kResolveSTOP }, - { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP }, - { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, - { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, - { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP }, - { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, - { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, - { UCAL_DAY_OF_YEAR, kResolveSTOP }, - { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_IS_LEAP_MONTH, kResolveSTOP }, - { kResolveSTOP } - }, - { - { UCAL_WEEK_OF_YEAR, kResolveSTOP }, - { UCAL_WEEK_OF_MONTH, kResolveSTOP }, - { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP }, - { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, - { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, - { kResolveSTOP } - }, - {{kResolveSTOP}} -}; - -/** - * Override Calendar to add IS_LEAP_MONTH to the field resolution - * table. - * @stable ICU 2.8 - */ -const UFieldResolutionTable* ChineseCalendar::getFieldResolutionTable() const { - return CHINESE_DATE_PRECEDENCE; -} - -/** - * Return the Julian day number of day before the first day of the - * given month in the given extended year. - * - *

Note: This method reads the IS_LEAP_MONTH field to determine - * whether the given month is a leap month. - * @param eyear the extended year - * @param month the zero-based month. The month is also determined - * by reading the IS_LEAP_MONTH field. - * @return the Julian day number of the day before the first - * day of the given month and year - * @stable ICU 2.8 - */ -int32_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const { - - ChineseCalendar *nonConstThis = (ChineseCalendar*)this; // cast away const - - // If the month is out of range, adjust it into range, and - // modify the extended year value accordingly. - if (month < 0 || month > 11) { - double m = month; - eyear += (int32_t)ClockMath::floorDivide(m, 12.0, &m); - month = (int32_t)m; - } - - int32_t gyear = eyear + fEpochYear - 1; // Gregorian year - int32_t theNewYear = newYear(gyear); - int32_t newMoon = newMoonNear(theNewYear + month * 29, true); - - int32_t julianDay = newMoon + kEpochStartAsJulianDay; - - // Save fields for later restoration - int32_t saveMonth = internalGet(UCAL_MONTH); - int32_t saveIsLeapMonth = internalGet(UCAL_IS_LEAP_MONTH); - - // Ignore IS_LEAP_MONTH field if useMonth is false - int32_t isLeapMonth = useMonth ? saveIsLeapMonth : 0; - - UErrorCode status = U_ZERO_ERROR; - nonConstThis->computeGregorianFields(julianDay, status); - if (U_FAILURE(status)) - return 0; - - // This will modify the MONTH and IS_LEAP_MONTH fields (only) - nonConstThis->computeChineseFields(newMoon, getGregorianYear(), - getGregorianMonth(), false); - - if (month != internalGet(UCAL_MONTH) || - isLeapMonth != internalGet(UCAL_IS_LEAP_MONTH)) { - newMoon = newMoonNear(newMoon + SYNODIC_GAP, true); - julianDay = newMoon + kEpochStartAsJulianDay; - } - - nonConstThis->internalSet(UCAL_MONTH, saveMonth); - nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, saveIsLeapMonth); - - return julianDay - 1; -} - - -/** - * Override Calendar to handle leap months properly. - * @stable ICU 2.8 - */ -void ChineseCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) { - switch (field) { - case UCAL_MONTH: - if (amount != 0) { - int32_t dom = get(UCAL_DAY_OF_MONTH, status); - if (U_FAILURE(status)) break; - int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day - if (U_FAILURE(status)) break; - int32_t moon = day - dom + 1; // New moon - offsetMonth(moon, dom, amount); - } - break; - default: - Calendar::add(field, amount, status); - break; - } -} - -/** - * Override Calendar to handle leap months properly. - * @stable ICU 2.8 - */ -void ChineseCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) { - add((UCalendarDateFields)field, amount, status); -} - -/** - * Override Calendar to handle leap months properly. - * @stable ICU 2.8 - */ -void ChineseCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) { - switch (field) { - case UCAL_MONTH: - if (amount != 0) { - int32_t dom = get(UCAL_DAY_OF_MONTH, status); - if (U_FAILURE(status)) break; - int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day - if (U_FAILURE(status)) break; - int32_t moon = day - dom + 1; // New moon (start of this month) - - // Note throughout the following: Months 12 and 1 are never - // followed by a leap month (D&R p. 185). - - // Compute the adjusted month number m. This is zero-based - // value from 0..11 in a non-leap year, and from 0..12 in a - // leap year. - int32_t m = get(UCAL_MONTH, status); // 0-based month - if (U_FAILURE(status)) break; - if (isLeapYear) { // (member variable) - if (get(UCAL_IS_LEAP_MONTH, status) == 1) { - ++m; - } else { - // Check for a prior leap month. (In the - // following, month 0 is the first month of the - // year.) Month 0 is never followed by a leap - // month, and we know month m is not a leap month. - // moon1 will be the start of month 0 if there is - // no leap month between month 0 and month m; - // otherwise it will be the start of month 1. - int moon1 = moon - - (int) (CalendarAstronomer::SYNODIC_MONTH * (m - 0.5)); - moon1 = newMoonNear(moon1, true); - if (isLeapMonthBetween(moon1, moon)) { - ++m; - } - } - if (U_FAILURE(status)) break; - } - - // Now do the standard roll computation on m, with the - // allowed range of 0..n-1, where n is 12 or 13. - int32_t n = isLeapYear ? 13 : 12; // Months in this year - int32_t newM = (m + amount) % n; - if (newM < 0) { - newM += n; - } - - if (newM != m) { - offsetMonth(moon, dom, newM - m); - } - } - break; - default: - Calendar::roll(field, amount, status); - break; - } -} - -void ChineseCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { - roll((UCalendarDateFields)field, amount, status); -} - - -//------------------------------------------------------------------ -// Support methods and constants -//------------------------------------------------------------------ - -/** - * Convert local days to UTC epoch milliseconds. - * This is not an accurate conversion in that getTimezoneOffset - * takes the milliseconds in GMT (not local time). In theory, more - * accurate algorithm can be implemented but practically we do not need - * to go through that complication as long as the historical timezone - * changes did not happen around the 'tricky' new moon (new moon around - * midnight). - * - * @param days days after January 1, 1970 0:00 in the astronomical base zone - * @return milliseconds after January 1, 1970 0:00 GMT - */ -double ChineseCalendar::daysToMillis(double days) const { - double millis = days * (double)kOneDay; - if (fZoneAstroCalc != NULL) { - int32_t rawOffset, dstOffset; - UErrorCode status = U_ZERO_ERROR; - fZoneAstroCalc->getOffset(millis, false, rawOffset, dstOffset, status); - if (U_SUCCESS(status)) { - return millis - (double)(rawOffset + dstOffset); - } - } - return millis - (double)CHINA_OFFSET; -} - -/** - * Convert UTC epoch milliseconds to local days. - * @param millis milliseconds after January 1, 1970 0:00 GMT - * @return days after January 1, 1970 0:00 in the astronomical base zone - */ -double ChineseCalendar::millisToDays(double millis) const { - if (fZoneAstroCalc != NULL) { - int32_t rawOffset, dstOffset; - UErrorCode status = U_ZERO_ERROR; - fZoneAstroCalc->getOffset(millis, false, rawOffset, dstOffset, status); - if (U_SUCCESS(status)) { - return ClockMath::floorDivide(millis + (double)(rawOffset + dstOffset), kOneDay); - } - } - return ClockMath::floorDivide(millis + (double)CHINA_OFFSET, kOneDay); -} - -//------------------------------------------------------------------ -// Astronomical computations -//------------------------------------------------------------------ - - -/** - * Return the major solar term on or after December 15 of the given - * Gregorian year, that is, the winter solstice of the given year. - * Computations are relative to Asia/Shanghai time zone. - * @param gyear a Gregorian year - * @return days after January 1, 1970 0:00 Asia/Shanghai of the - * winter solstice of the given year - */ -int32_t ChineseCalendar::winterSolstice(int32_t gyear) const { - - UErrorCode status = U_ZERO_ERROR; - int32_t cacheValue = CalendarCache::get(&gChineseCalendarWinterSolsticeCache, gyear, status); - - if (cacheValue == 0) { - // In books December 15 is used, but it fails for some years - // using our algorithms, e.g.: 1298 1391 1492 1553 1560. That - // is, winterSolstice(1298) starts search at Dec 14 08:00:00 - // PST 1298 with a final result of Dec 14 10:31:59 PST 1299. - double ms = daysToMillis(Grego::fieldsToDay(gyear, UCAL_DECEMBER, 1)); - - umtx_lock(&astroLock); - if(gChineseCalendarAstro == NULL) { - gChineseCalendarAstro = new CalendarAstronomer(); - ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup); - } - gChineseCalendarAstro->setTime(ms); - UDate solarLong = gChineseCalendarAstro->getSunTime(CalendarAstronomer::WINTER_SOLSTICE(), true); - umtx_unlock(&astroLock); - - // Winter solstice is 270 degrees solar longitude aka Dongzhi - cacheValue = (int32_t)millisToDays(solarLong); - CalendarCache::put(&gChineseCalendarWinterSolsticeCache, gyear, cacheValue, status); - } - if(U_FAILURE(status)) { - cacheValue = 0; - } - return cacheValue; -} - -/** - * Return the closest new moon to the given date, searching either - * forward or backward in time. - * @param days days after January 1, 1970 0:00 Asia/Shanghai - * @param after if true, search for a new moon on or after the given - * date; otherwise, search for a new moon before it - * @return days after January 1, 1970 0:00 Asia/Shanghai of the nearest - * new moon after or before days - */ -int32_t ChineseCalendar::newMoonNear(double days, UBool after) const { - - umtx_lock(&astroLock); - if(gChineseCalendarAstro == NULL) { - gChineseCalendarAstro = new CalendarAstronomer(); - ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup); - } - gChineseCalendarAstro->setTime(daysToMillis(days)); - UDate newMoon = gChineseCalendarAstro->getMoonTime(CalendarAstronomer::NEW_MOON(), after); - umtx_unlock(&astroLock); - - return (int32_t) millisToDays(newMoon); -} - -/** - * Return the nearest integer number of synodic months between - * two dates. - * @param day1 days after January 1, 1970 0:00 Asia/Shanghai - * @param day2 days after January 1, 1970 0:00 Asia/Shanghai - * @return the nearest integer number of months between day1 and day2 - */ -int32_t ChineseCalendar::synodicMonthsBetween(int32_t day1, int32_t day2) const { - double roundme = ((day2 - day1) / CalendarAstronomer::SYNODIC_MONTH); - return (int32_t) (roundme + (roundme >= 0 ? .5 : -.5)); -} - -/** - * Return the major solar term on or before a given date. This - * will be an integer from 1..12, with 1 corresponding to 330 degrees, - * 2 to 0 degrees, 3 to 30 degrees,..., and 12 to 300 degrees. - * @param days days after January 1, 1970 0:00 Asia/Shanghai - */ -int32_t ChineseCalendar::majorSolarTerm(int32_t days) const { - - umtx_lock(&astroLock); - if(gChineseCalendarAstro == NULL) { - gChineseCalendarAstro = new CalendarAstronomer(); - ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup); - } - gChineseCalendarAstro->setTime(daysToMillis(days)); - UDate solarLongitude = gChineseCalendarAstro->getSunLongitude(); - umtx_unlock(&astroLock); - - // Compute (floor(solarLongitude / (pi/6)) + 2) % 12 - int32_t term = ( ((int32_t)(6 * solarLongitude / CalendarAstronomer::PI)) + 2 ) % 12; - if (term < 1) { - term += 12; - } - return term; -} - -/** - * Return true if the given month lacks a major solar term. - * @param newMoon days after January 1, 1970 0:00 Asia/Shanghai of a new - * moon - */ -UBool ChineseCalendar::hasNoMajorSolarTerm(int32_t newMoon) const { - return majorSolarTerm(newMoon) == - majorSolarTerm(newMoonNear(newMoon + SYNODIC_GAP, true)); -} - - -//------------------------------------------------------------------ -// Time to fields -//------------------------------------------------------------------ - -/** - * Return true if there is a leap month on or after month newMoon1 and - * at or before month newMoon2. - * @param newMoon1 days after January 1, 1970 0:00 astronomical base zone - * of a new moon - * @param newMoon2 days after January 1, 1970 0:00 astronomical base zone - * of a new moon - */ -UBool ChineseCalendar::isLeapMonthBetween(int32_t newMoon1, int32_t newMoon2) const { - -#ifdef U_DEBUG_CHNSECAL - // This is only needed to debug the timeOfAngle divergence bug. - // Remove this later. Liu 11/9/00 - if (synodicMonthsBetween(newMoon1, newMoon2) >= 50) { - U_DEBUG_CHNSECAL_MSG(( - "isLeapMonthBetween(%d, %d): Invalid parameters", newMoon1, newMoon2 - )); - } -#endif - - return (newMoon2 >= newMoon1) && - (isLeapMonthBetween(newMoon1, newMoonNear(newMoon2 - SYNODIC_GAP, false)) || - hasNoMajorSolarTerm(newMoon2)); -} - -/** - * Compute fields for the Chinese calendar system. This method can - * either set all relevant fields, as required by - * handleComputeFields(), or it can just set the MONTH and - * IS_LEAP_MONTH fields, as required by - * handleComputeMonthStart(). - * - *

As a side effect, this method sets {@link #isLeapYear}. - * @param days days after January 1, 1970 0:00 astronomical base zone - * of the date to compute fields for - * @param gyear the Gregorian year of the given date - * @param gmonth the Gregorian month of the given date - * @param setAllFields if true, set the EXTENDED_YEAR, ERA, YEAR, - * DAY_OF_MONTH, and DAY_OF_YEAR fields. In either case set the MONTH - * and IS_LEAP_MONTH fields. - */ -void ChineseCalendar::computeChineseFields(int32_t days, int32_t gyear, int32_t gmonth, - UBool setAllFields) { - - // Find the winter solstices before and after the target date. - // These define the boundaries of this Chinese year, specifically, - // the position of month 11, which always contains the solstice. - // We want solsticeBefore <= date < solsticeAfter. - int32_t solsticeBefore; - int32_t solsticeAfter = winterSolstice(gyear); - if (days < solsticeAfter) { - solsticeBefore = winterSolstice(gyear - 1); - } else { - solsticeBefore = solsticeAfter; - solsticeAfter = winterSolstice(gyear + 1); - } - - // Find the start of the month after month 11. This will be either - // the prior month 12 or leap month 11 (very rare). Also find the - // start of the following month 11. - int32_t firstMoon = newMoonNear(solsticeBefore + 1, true); - int32_t lastMoon = newMoonNear(solsticeAfter + 1, false); - int32_t thisMoon = newMoonNear(days + 1, false); // Start of this month - // Note: isLeapYear is a member variable - isLeapYear = synodicMonthsBetween(firstMoon, lastMoon) == 12; - - int32_t month = synodicMonthsBetween(firstMoon, thisMoon); - if (isLeapYear && isLeapMonthBetween(firstMoon, thisMoon)) { - month--; - } - if (month < 1) { - month += 12; - } - - UBool isLeapMonth = isLeapYear && - hasNoMajorSolarTerm(thisMoon) && - !isLeapMonthBetween(firstMoon, newMoonNear(thisMoon - SYNODIC_GAP, false)); - - internalSet(UCAL_MONTH, month-1); // Convert from 1-based to 0-based - internalSet(UCAL_IS_LEAP_MONTH, isLeapMonth?1:0); - - if (setAllFields) { - - // Extended year and cycle year is based on the epoch year - - int32_t extended_year = gyear - fEpochYear; - int cycle_year = gyear - CHINESE_EPOCH_YEAR; - if (month < 11 || - gmonth >= UCAL_JULY) { - extended_year++; - cycle_year++; - } - int32_t dayOfMonth = days - thisMoon + 1; - - internalSet(UCAL_EXTENDED_YEAR, extended_year); - - // 0->0,60 1->1,1 60->1,60 61->2,1 etc. - int32_t yearOfCycle; - int32_t cycle = ClockMath::floorDivide(cycle_year - 1, 60, &yearOfCycle); - internalSet(UCAL_ERA, cycle + 1); - internalSet(UCAL_YEAR, yearOfCycle + 1); - - internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); - - // Days will be before the first new year we compute if this - // date is in month 11, leap 11, 12. There is never a leap 12. - // New year computations are cached so this should be cheap in - // the long run. - int32_t theNewYear = newYear(gyear); - if (days < theNewYear) { - theNewYear = newYear(gyear-1); - } - internalSet(UCAL_DAY_OF_YEAR, days - theNewYear + 1); - } -} - - -//------------------------------------------------------------------ -// Fields to time -//------------------------------------------------------------------ - -/** - * Return the Chinese new year of the given Gregorian year. - * @param gyear a Gregorian year - * @return days after January 1, 1970 0:00 astronomical base zone of the - * Chinese new year of the given year (this will be a new moon) - */ -int32_t ChineseCalendar::newYear(int32_t gyear) const { - UErrorCode status = U_ZERO_ERROR; - int32_t cacheValue = CalendarCache::get(&gChineseCalendarNewYearCache, gyear, status); - - if (cacheValue == 0) { - - int32_t solsticeBefore= winterSolstice(gyear - 1); - int32_t solsticeAfter = winterSolstice(gyear); - int32_t newMoon1 = newMoonNear(solsticeBefore + 1, true); - int32_t newMoon2 = newMoonNear(newMoon1 + SYNODIC_GAP, true); - int32_t newMoon11 = newMoonNear(solsticeAfter + 1, false); - - if (synodicMonthsBetween(newMoon1, newMoon11) == 12 && - (hasNoMajorSolarTerm(newMoon1) || hasNoMajorSolarTerm(newMoon2))) { - cacheValue = newMoonNear(newMoon2 + SYNODIC_GAP, true); - } else { - cacheValue = newMoon2; - } - - CalendarCache::put(&gChineseCalendarNewYearCache, gyear, cacheValue, status); - } - if(U_FAILURE(status)) { - cacheValue = 0; - } - return cacheValue; -} - -/** - * Adjust this calendar to be delta months before or after a given - * start position, pinning the day of month if necessary. The start - * position is given as a local days number for the start of the month - * and a day-of-month. Used by add() and roll(). - * @param newMoon the local days of the first day of the month of the - * start position (days after January 1, 1970 0:00 Asia/Shanghai) - * @param dom the 1-based day-of-month of the start position - * @param delta the number of months to move forward or backward from - * the start position - */ -void ChineseCalendar::offsetMonth(int32_t newMoon, int32_t dom, int32_t delta) { - UErrorCode status = U_ZERO_ERROR; - - // Move to the middle of the month before our target month. - newMoon += (int32_t) (CalendarAstronomer::SYNODIC_MONTH * (delta - 0.5)); - - // Search forward to the target month's new moon - newMoon = newMoonNear(newMoon, true); - - // Find the target dom - int32_t jd = newMoon + kEpochStartAsJulianDay - 1 + dom; - - // Pin the dom. In this calendar all months are 29 or 30 days - // so pinning just means handling dom 30. - if (dom > 29) { - set(UCAL_JULIAN_DAY, jd-1); - // TODO Fix this. We really shouldn't ever have to - // explicitly call complete(). This is either a bug in - // this method, in ChineseCalendar, or in - // Calendar.getActualMaximum(). I suspect the last. - complete(status); - if (U_FAILURE(status)) return; - if (getActualMaximum(UCAL_DAY_OF_MONTH, status) >= dom) { - if (U_FAILURE(status)) return; - set(UCAL_JULIAN_DAY, jd); - } - } else { - set(UCAL_JULIAN_DAY, jd); - } -} - - -UBool -ChineseCalendar::inDaylightTime(UErrorCode& status) const -{ - // copied from GregorianCalendar - if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) - return false; - - // Force an update of the state of the Calendar. - ((ChineseCalendar*)this)->complete(status); // cast away const - - return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : false); -} - -// default century - -static UDate gSystemDefaultCenturyStart = DBL_MIN; -static int32_t gSystemDefaultCenturyStartYear = -1; -static icu::UInitOnce gSystemDefaultCenturyInitOnce {}; - - -UBool ChineseCalendar::haveDefaultCentury() const -{ - return true; -} - -UDate ChineseCalendar::defaultCenturyStart() const -{ - return internalGetDefaultCenturyStart(); -} - -int32_t ChineseCalendar::defaultCenturyStartYear() const -{ - return internalGetDefaultCenturyStartYear(); -} - -static void U_CALLCONV initializeSystemDefaultCentury() -{ - // initialize systemDefaultCentury and systemDefaultCenturyYear based - // on the current time. They'll be set to 80 years before - // the current time. - UErrorCode status = U_ZERO_ERROR; - ChineseCalendar calendar(Locale("@calendar=chinese"),status); - if (U_SUCCESS(status)) { - calendar.setTime(Calendar::getNow(), status); - calendar.add(UCAL_YEAR, -80, status); - gSystemDefaultCenturyStart = calendar.getTime(status); - gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); - } - // We have no recourse upon failure unless we want to propagate the failure - // out. -} - -UDate -ChineseCalendar::internalGetDefaultCenturyStart() const -{ - // lazy-evaluate systemDefaultCenturyStart - umtx_initOnce(gSystemDefaultCenturyInitOnce, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStart; -} - -int32_t -ChineseCalendar::internalGetDefaultCenturyStartYear() const -{ - // lazy-evaluate systemDefaultCenturyStartYear - umtx_initOnce(gSystemDefaultCenturyInitOnce, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStartYear; -} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChineseCalendar) - -U_NAMESPACE_END - -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ****************************************************************************** + * Copyright (C) 2007-2014, International Business Machines Corporation + * and others. All Rights Reserved. + ****************************************************************************** + * + * File CHNSECAL.CPP + * + * Modification History: + * + * Date Name Description + * 9/18/2007 ajmacher ported from java ChineseCalendar + ***************************************************************************** + */ + +#include "chnsecal.h" + +#if !UCONFIG_NO_FORMATTING + +#include "umutex.h" +#include +#include "gregoimp.h" // Math +#include "astro.h" // CalendarAstronomer +#include "unicode/simpletz.h" +#include "uhash.h" +#include "ucln_in.h" +#include "cstring.h" + +// Debugging +#ifdef U_DEBUG_CHNSECAL +# include +# include +static void debug_chnsecal_loc(const char *f, int32_t l) +{ + fprintf(stderr, "%s:%d: ", f, l); +} + +static void debug_chnsecal_msg(const char *pat, ...) +{ + va_list ap; + va_start(ap, pat); + vfprintf(stderr, pat, ap); + fflush(stderr); +} +// must use double parens, i.e.: U_DEBUG_CHNSECAL_MSG(("four is: %d",4)); +#define U_DEBUG_CHNSECAL_MSG(x) {debug_chnsecal_loc(__FILE__,__LINE__);debug_chnsecal_msg x;} +#else +#define U_DEBUG_CHNSECAL_MSG(x) +#endif + + +// --- The cache -- +static icu::UMutex astroLock; +static icu::CalendarAstronomer *gChineseCalendarAstro = nullptr; + +// Lazy Creation & Access synchronized by class CalendarCache with a mutex. +static icu::CalendarCache *gChineseCalendarWinterSolsticeCache = nullptr; +static icu::CalendarCache *gChineseCalendarNewYearCache = nullptr; + +static icu::TimeZone *gChineseCalendarZoneAstroCalc = nullptr; +static icu::UInitOnce gChineseCalendarZoneAstroCalcInitOnce {}; + +/** + * The start year of the Chinese calendar, the 61st year of the reign + * of Huang Di. Some sources use the first year of his reign, + * resulting in EXTENDED_YEAR values 60 years greater and ERA (cycle) + * values one greater. + */ +static const int32_t CHINESE_EPOCH_YEAR = -2636; // Gregorian year + +/** + * The offset from GMT in milliseconds at which we perform astronomical + * computations. Some sources use a different historically accurate + * offset of GMT+7:45:40 for years before 1929; we do not do this. + */ +static const int32_t CHINA_OFFSET = 8 * kOneHour; + +/** + * Value to be added or subtracted from the local days of a new moon to + * get close to the next or prior new moon, but not cross it. Must be + * >= 1 and < CalendarAstronomer.SYNODIC_MONTH. + */ +static const int32_t SYNODIC_GAP = 25; + + +U_CDECL_BEGIN +static UBool calendar_chinese_cleanup() { + if (gChineseCalendarAstro) { + delete gChineseCalendarAstro; + gChineseCalendarAstro = nullptr; + } + if (gChineseCalendarWinterSolsticeCache) { + delete gChineseCalendarWinterSolsticeCache; + gChineseCalendarWinterSolsticeCache = nullptr; + } + if (gChineseCalendarNewYearCache) { + delete gChineseCalendarNewYearCache; + gChineseCalendarNewYearCache = nullptr; + } + if (gChineseCalendarZoneAstroCalc) { + delete gChineseCalendarZoneAstroCalc; + gChineseCalendarZoneAstroCalc = nullptr; + } + gChineseCalendarZoneAstroCalcInitOnce.reset(); + return true; +} +U_CDECL_END + +U_NAMESPACE_BEGIN + + +// Implementation of the ChineseCalendar class + + +//------------------------------------------------------------------------- +// Constructors... +//------------------------------------------------------------------------- + + +ChineseCalendar* ChineseCalendar::clone() const { + return new ChineseCalendar(*this); +} + +ChineseCalendar::ChineseCalendar(const Locale& aLocale, UErrorCode& success) +: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success), + hasLeapMonthBetweenWinterSolstices(false), + fEpochYear(CHINESE_EPOCH_YEAR), + fZoneAstroCalc(getChineseCalZoneAstroCalc()) +{ + setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. +} + +ChineseCalendar::ChineseCalendar(const Locale& aLocale, int32_t epochYear, + const TimeZone* zoneAstroCalc, UErrorCode &success) +: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success), + hasLeapMonthBetweenWinterSolstices(false), + fEpochYear(epochYear), + fZoneAstroCalc(zoneAstroCalc) +{ + setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. +} + +ChineseCalendar::ChineseCalendar(const ChineseCalendar& other) : Calendar(other) { + hasLeapMonthBetweenWinterSolstices = other.hasLeapMonthBetweenWinterSolstices; + fEpochYear = other.fEpochYear; + fZoneAstroCalc = other.fZoneAstroCalc; +} + +ChineseCalendar::~ChineseCalendar() +{ +} + +const char *ChineseCalendar::getType() const { + return "chinese"; +} + +static void U_CALLCONV initChineseCalZoneAstroCalc() { + gChineseCalendarZoneAstroCalc = new SimpleTimeZone(CHINA_OFFSET, UNICODE_STRING_SIMPLE("CHINA_ZONE") ); + ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup); +} + +const TimeZone* ChineseCalendar::getChineseCalZoneAstroCalc() const { + umtx_initOnce(gChineseCalendarZoneAstroCalcInitOnce, &initChineseCalZoneAstroCalc); + return gChineseCalendarZoneAstroCalc; +} + +//------------------------------------------------------------------------- +// Minimum / Maximum access functions +//------------------------------------------------------------------------- + + +static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { + // Minimum Greatest Least Maximum + // Minimum Maximum + { 1, 1, 83333, 83333}, // ERA + { 1, 1, 60, 60}, // YEAR + { 0, 0, 11, 11}, // MONTH + { 1, 1, 50, 55}, // WEEK_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH + { 1, 1, 29, 30}, // DAY_OF_MONTH + { 1, 1, 353, 385}, // DAY_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK + { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET + { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL + { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY + { 0, 0, 1, 1}, // IS_LEAP_MONTH + { 0, 0, 11, 12}, // ORDINAL_MONTH +}; + + +/** +* @draft ICU 2.4 +*/ +int32_t ChineseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { + return LIMITS[field][limitType]; +} + + +//---------------------------------------------------------------------- +// Calendar framework +//---------------------------------------------------------------------- + +/** + * Implement abstract Calendar method to return the extended year + * defined by the current fields. This will use either the ERA and + * YEAR field as the cycle and year-of-cycle, or the EXTENDED_YEAR + * field as the continuous year count, depending on which is newer. + * @stable ICU 2.8 + */ +int32_t ChineseCalendar::handleGetExtendedYear() { + int32_t year; + if (newestStamp(UCAL_ERA, UCAL_YEAR, kUnset) <= fStamp[UCAL_EXTENDED_YEAR]) { + year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 + } else { + int32_t cycle = internalGet(UCAL_ERA, 1) - 1; // 0-based cycle + // adjust to the instance specific epoch + year = cycle * 60 + internalGet(UCAL_YEAR, 1) - (fEpochYear - CHINESE_EPOCH_YEAR); + } + return year; +} + +/** + * Override Calendar method to return the number of days in the given + * extended year and month. + * + *

Note: This method also reads the IS_LEAP_MONTH field to determine + * whether or not the given month is a leap month. + * @stable ICU 2.8 + */ +int32_t ChineseCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { + int32_t thisStart = handleComputeMonthStart(extendedYear, month, true) - + kEpochStartAsJulianDay + 1; // Julian day -> local days + int32_t nextStart = newMoonNear(thisStart + SYNODIC_GAP, true); + return nextStart - thisStart; +} + +/** + * Override Calendar to compute several fields specific to the Chinese + * calendar system. These are: + * + *

  • ERA + *
  • YEAR + *
  • MONTH + *
  • DAY_OF_MONTH + *
  • DAY_OF_YEAR + *
  • EXTENDED_YEAR
+ * + * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this + * method is called. The getGregorianXxx() methods return Gregorian + * calendar equivalents for the given Julian day. + * + *

Compute the ChineseCalendar-specific field IS_LEAP_MONTH. + * @stable ICU 2.8 + */ +void ChineseCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) { + + computeChineseFields(julianDay - kEpochStartAsJulianDay, // local days + getGregorianYear(), getGregorianMonth(), + true); // set all fields +} + +/** + * Field resolution table that incorporates IS_LEAP_MONTH. + */ +const UFieldResolutionTable ChineseCalendar::CHINESE_DATE_PRECEDENCE[] = +{ + { + { UCAL_DAY_OF_MONTH, kResolveSTOP }, + { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP }, + { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, + { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, + { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP }, + { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, + { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, + { UCAL_DAY_OF_YEAR, kResolveSTOP }, + { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_IS_LEAP_MONTH, kResolveSTOP }, + { kResolveSTOP } + }, + { + { UCAL_WEEK_OF_YEAR, kResolveSTOP }, + { UCAL_WEEK_OF_MONTH, kResolveSTOP }, + { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP }, + { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, + { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, + { kResolveSTOP } + }, + {{kResolveSTOP}} +}; + +/** + * Override Calendar to add IS_LEAP_MONTH to the field resolution + * table. + * @stable ICU 2.8 + */ +const UFieldResolutionTable* ChineseCalendar::getFieldResolutionTable() const { + return CHINESE_DATE_PRECEDENCE; +} + +/** + * Return the Julian day number of day before the first day of the + * given month in the given extended year. + * + *

Note: This method reads the IS_LEAP_MONTH field to determine + * whether the given month is a leap month. + * @param eyear the extended year + * @param month the zero-based month. The month is also determined + * by reading the IS_LEAP_MONTH field. + * @return the Julian day number of the day before the first + * day of the given month and year + * @stable ICU 2.8 + */ +int32_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const { + ChineseCalendar *nonConstThis = (ChineseCalendar*)this; // cast away const + + // If the month is out of range, adjust it into range, and + // modify the extended year value accordingly. + if (month < 0 || month > 11) { + double m = month; + eyear += (int32_t)ClockMath::floorDivide(m, 12.0, &m); + month = (int32_t)m; + } + + int32_t gyear = eyear + fEpochYear - 1; // Gregorian year + int32_t theNewYear = newYear(gyear); + int32_t newMoon = newMoonNear(theNewYear + month * 29, true); + + int32_t julianDay = newMoon + kEpochStartAsJulianDay; + + // Save fields for later restoration + int32_t saveMonth = internalGet(UCAL_MONTH); + int32_t saveOrdinalMonth = internalGet(UCAL_ORDINAL_MONTH); + int32_t saveIsLeapMonth = internalGet(UCAL_IS_LEAP_MONTH); + + // Ignore IS_LEAP_MONTH field if useMonth is false + int32_t isLeapMonth = useMonth ? saveIsLeapMonth : 0; + + UErrorCode status = U_ZERO_ERROR; + nonConstThis->computeGregorianFields(julianDay, status); + if (U_FAILURE(status)) + return 0; + + // This will modify the MONTH and IS_LEAP_MONTH fields (only) + nonConstThis->computeChineseFields(newMoon, getGregorianYear(), + getGregorianMonth(), false); + + if (month != internalGet(UCAL_MONTH) || + isLeapMonth != internalGet(UCAL_IS_LEAP_MONTH)) { + newMoon = newMoonNear(newMoon + SYNODIC_GAP, true); + julianDay = newMoon + kEpochStartAsJulianDay; + } + + nonConstThis->internalSet(UCAL_MONTH, saveMonth); + nonConstThis->internalSet(UCAL_ORDINAL_MONTH, saveOrdinalMonth); + nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, saveIsLeapMonth); + return julianDay - 1; +} + + +/** + * Override Calendar to handle leap months properly. + * @stable ICU 2.8 + */ +void ChineseCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) { + switch (field) { + case UCAL_MONTH: + case UCAL_ORDINAL_MONTH: + if (amount != 0) { + int32_t dom = get(UCAL_DAY_OF_MONTH, status); + if (U_FAILURE(status)) break; + int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day + if (U_FAILURE(status)) break; + int32_t moon = day - dom + 1; // New moon + offsetMonth(moon, dom, amount); + } + break; + default: + Calendar::add(field, amount, status); + break; + } +} + +/** + * Override Calendar to handle leap months properly. + * @stable ICU 2.8 + */ +void ChineseCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) { + add((UCalendarDateFields)field, amount, status); +} + +/** + * Override Calendar to handle leap months properly. + * @stable ICU 2.8 + */ +void ChineseCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) { + switch (field) { + case UCAL_MONTH: + case UCAL_ORDINAL_MONTH: + if (amount != 0) { + int32_t dom = get(UCAL_DAY_OF_MONTH, status); + if (U_FAILURE(status)) break; + int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day + if (U_FAILURE(status)) break; + int32_t moon = day - dom + 1; // New moon (start of this month) + + // Note throughout the following: Months 12 and 1 are never + // followed by a leap month (D&R p. 185). + + // Compute the adjusted month number m. This is zero-based + // value from 0..11 in a non-leap year, and from 0..12 in a + // leap year. + int32_t m = get(UCAL_MONTH, status); // 0-based month + if (U_FAILURE(status)) break; + if (hasLeapMonthBetweenWinterSolstices) { // (member variable) + if (get(UCAL_IS_LEAP_MONTH, status) == 1) { + ++m; + } else { + // Check for a prior leap month. (In the + // following, month 0 is the first month of the + // year.) Month 0 is never followed by a leap + // month, and we know month m is not a leap month. + // moon1 will be the start of month 0 if there is + // no leap month between month 0 and month m; + // otherwise it will be the start of month 1. + int moon1 = moon - + (int) (CalendarAstronomer::SYNODIC_MONTH * (m - 0.5)); + moon1 = newMoonNear(moon1, true); + if (isLeapMonthBetween(moon1, moon)) { + ++m; + } + } + if (U_FAILURE(status)) break; + } + + // Now do the standard roll computation on m, with the + // allowed range of 0..n-1, where n is 12 or 13. + int32_t n = hasLeapMonthBetweenWinterSolstices ? 13 : 12; // Months in this year + int32_t newM = (m + amount) % n; + if (newM < 0) { + newM += n; + } + + if (newM != m) { + offsetMonth(moon, dom, newM - m); + } + } + break; + default: + Calendar::roll(field, amount, status); + break; + } +} + +void ChineseCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { + roll((UCalendarDateFields)field, amount, status); +} + + +//------------------------------------------------------------------ +// Support methods and constants +//------------------------------------------------------------------ + +/** + * Convert local days to UTC epoch milliseconds. + * This is not an accurate conversion in that getTimezoneOffset + * takes the milliseconds in GMT (not local time). In theory, more + * accurate algorithm can be implemented but practically we do not need + * to go through that complication as long as the historical timezone + * changes did not happen around the 'tricky' new moon (new moon around + * midnight). + * + * @param days days after January 1, 1970 0:00 in the astronomical base zone + * @return milliseconds after January 1, 1970 0:00 GMT + */ +double ChineseCalendar::daysToMillis(double days) const { + double millis = days * (double)kOneDay; + if (fZoneAstroCalc != nullptr) { + int32_t rawOffset, dstOffset; + UErrorCode status = U_ZERO_ERROR; + fZoneAstroCalc->getOffset(millis, false, rawOffset, dstOffset, status); + if (U_SUCCESS(status)) { + return millis - (double)(rawOffset + dstOffset); + } + } + return millis - (double)CHINA_OFFSET; +} + +/** + * Convert UTC epoch milliseconds to local days. + * @param millis milliseconds after January 1, 1970 0:00 GMT + * @return days after January 1, 1970 0:00 in the astronomical base zone + */ +double ChineseCalendar::millisToDays(double millis) const { + if (fZoneAstroCalc != nullptr) { + int32_t rawOffset, dstOffset; + UErrorCode status = U_ZERO_ERROR; + fZoneAstroCalc->getOffset(millis, false, rawOffset, dstOffset, status); + if (U_SUCCESS(status)) { + return ClockMath::floorDivide(millis + (double)(rawOffset + dstOffset), kOneDay); + } + } + return ClockMath::floorDivide(millis + (double)CHINA_OFFSET, kOneDay); +} + +//------------------------------------------------------------------ +// Astronomical computations +//------------------------------------------------------------------ + + +/** + * Return the major solar term on or after December 15 of the given + * Gregorian year, that is, the winter solstice of the given year. + * Computations are relative to Asia/Shanghai time zone. + * @param gyear a Gregorian year + * @return days after January 1, 1970 0:00 Asia/Shanghai of the + * winter solstice of the given year + */ +int32_t ChineseCalendar::winterSolstice(int32_t gyear) const { + + UErrorCode status = U_ZERO_ERROR; + int32_t cacheValue = CalendarCache::get(&gChineseCalendarWinterSolsticeCache, gyear, status); + + if (cacheValue == 0) { + // In books December 15 is used, but it fails for some years + // using our algorithms, e.g.: 1298 1391 1492 1553 1560. That + // is, winterSolstice(1298) starts search at Dec 14 08:00:00 + // PST 1298 with a final result of Dec 14 10:31:59 PST 1299. + double ms = daysToMillis(Grego::fieldsToDay(gyear, UCAL_DECEMBER, 1)); + + umtx_lock(&astroLock); + if(gChineseCalendarAstro == nullptr) { + gChineseCalendarAstro = new CalendarAstronomer(); + ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup); + } + gChineseCalendarAstro->setTime(ms); + UDate solarLong = gChineseCalendarAstro->getSunTime(CalendarAstronomer::WINTER_SOLSTICE(), true); + umtx_unlock(&astroLock); + + // Winter solstice is 270 degrees solar longitude aka Dongzhi + cacheValue = (int32_t)millisToDays(solarLong); + CalendarCache::put(&gChineseCalendarWinterSolsticeCache, gyear, cacheValue, status); + } + if(U_FAILURE(status)) { + cacheValue = 0; + } + return cacheValue; +} + +/** + * Return the closest new moon to the given date, searching either + * forward or backward in time. + * @param days days after January 1, 1970 0:00 Asia/Shanghai + * @param after if true, search for a new moon on or after the given + * date; otherwise, search for a new moon before it + * @return days after January 1, 1970 0:00 Asia/Shanghai of the nearest + * new moon after or before days + */ +int32_t ChineseCalendar::newMoonNear(double days, UBool after) const { + + umtx_lock(&astroLock); + if(gChineseCalendarAstro == nullptr) { + gChineseCalendarAstro = new CalendarAstronomer(); + ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup); + } + gChineseCalendarAstro->setTime(daysToMillis(days)); + UDate newMoon = gChineseCalendarAstro->getMoonTime(CalendarAstronomer::NEW_MOON(), after); + umtx_unlock(&astroLock); + + return (int32_t) millisToDays(newMoon); +} + +/** + * Return the nearest integer number of synodic months between + * two dates. + * @param day1 days after January 1, 1970 0:00 Asia/Shanghai + * @param day2 days after January 1, 1970 0:00 Asia/Shanghai + * @return the nearest integer number of months between day1 and day2 + */ +int32_t ChineseCalendar::synodicMonthsBetween(int32_t day1, int32_t day2) const { + double roundme = ((day2 - day1) / CalendarAstronomer::SYNODIC_MONTH); + return (int32_t) (roundme + (roundme >= 0 ? .5 : -.5)); +} + +/** + * Return the major solar term on or before a given date. This + * will be an integer from 1..12, with 1 corresponding to 330 degrees, + * 2 to 0 degrees, 3 to 30 degrees,..., and 12 to 300 degrees. + * @param days days after January 1, 1970 0:00 Asia/Shanghai + */ +int32_t ChineseCalendar::majorSolarTerm(int32_t days) const { + + umtx_lock(&astroLock); + if(gChineseCalendarAstro == nullptr) { + gChineseCalendarAstro = new CalendarAstronomer(); + ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup); + } + gChineseCalendarAstro->setTime(daysToMillis(days)); + UDate solarLongitude = gChineseCalendarAstro->getSunLongitude(); + umtx_unlock(&astroLock); + + // Compute (floor(solarLongitude / (pi/6)) + 2) % 12 + int32_t term = ( ((int32_t)(6 * solarLongitude / CalendarAstronomer::PI)) + 2 ) % 12; + if (term < 1) { + term += 12; + } + return term; +} + +/** + * Return true if the given month lacks a major solar term. + * @param newMoon days after January 1, 1970 0:00 Asia/Shanghai of a new + * moon + */ +UBool ChineseCalendar::hasNoMajorSolarTerm(int32_t newMoon) const { + return majorSolarTerm(newMoon) == + majorSolarTerm(newMoonNear(newMoon + SYNODIC_GAP, true)); +} + + +//------------------------------------------------------------------ +// Time to fields +//------------------------------------------------------------------ + +/** + * Return true if there is a leap month on or after month newMoon1 and + * at or before month newMoon2. + * @param newMoon1 days after January 1, 1970 0:00 astronomical base zone + * of a new moon + * @param newMoon2 days after January 1, 1970 0:00 astronomical base zone + * of a new moon + */ +UBool ChineseCalendar::isLeapMonthBetween(int32_t newMoon1, int32_t newMoon2) const { + +#ifdef U_DEBUG_CHNSECAL + // This is only needed to debug the timeOfAngle divergence bug. + // Remove this later. Liu 11/9/00 + if (synodicMonthsBetween(newMoon1, newMoon2) >= 50) { + U_DEBUG_CHNSECAL_MSG(( + "isLeapMonthBetween(%d, %d): Invalid parameters", newMoon1, newMoon2 + )); + } +#endif + + return (newMoon2 >= newMoon1) && + (isLeapMonthBetween(newMoon1, newMoonNear(newMoon2 - SYNODIC_GAP, false)) || + hasNoMajorSolarTerm(newMoon2)); +} + +/** + * Compute fields for the Chinese calendar system. This method can + * either set all relevant fields, as required by + * handleComputeFields(), or it can just set the MONTH and + * IS_LEAP_MONTH fields, as required by + * handleComputeMonthStart(). + * + *

As a side effect, this method sets {@link #hasLeapMonthBetweenWinterSolstices}. + * @param days days after January 1, 1970 0:00 astronomical base zone + * of the date to compute fields for + * @param gyear the Gregorian year of the given date + * @param gmonth the Gregorian month of the given date + * @param setAllFields if true, set the EXTENDED_YEAR, ERA, YEAR, + * DAY_OF_MONTH, and DAY_OF_YEAR fields. In either case set the MONTH + * and IS_LEAP_MONTH fields. + */ +void ChineseCalendar::computeChineseFields(int32_t days, int32_t gyear, int32_t gmonth, + UBool setAllFields) { + // Find the winter solstices before and after the target date. + // These define the boundaries of this Chinese year, specifically, + // the position of month 11, which always contains the solstice. + // We want solsticeBefore <= date < solsticeAfter. + int32_t solsticeBefore; + int32_t solsticeAfter = winterSolstice(gyear); + if (days < solsticeAfter) { + solsticeBefore = winterSolstice(gyear - 1); + } else { + solsticeBefore = solsticeAfter; + solsticeAfter = winterSolstice(gyear + 1); + } + + // Find the start of the month after month 11. This will be either + // the prior month 12 or leap month 11 (very rare). Also find the + // start of the following month 11. + int32_t firstMoon = newMoonNear(solsticeBefore + 1, true); + int32_t lastMoon = newMoonNear(solsticeAfter + 1, false); + int32_t thisMoon = newMoonNear(days + 1, false); // Start of this month + // Note: hasLeapMonthBetweenWinterSolstices is a member variable + hasLeapMonthBetweenWinterSolstices = synodicMonthsBetween(firstMoon, lastMoon) == 12; + + int32_t month = synodicMonthsBetween(firstMoon, thisMoon); + int32_t theNewYear = newYear(gyear); + if (days < theNewYear) { + theNewYear = newYear(gyear-1); + } + if (hasLeapMonthBetweenWinterSolstices && isLeapMonthBetween(firstMoon, thisMoon)) { + month--; + } + if (month < 1) { + month += 12; + } + int32_t ordinalMonth = synodicMonthsBetween(theNewYear, thisMoon); + if (ordinalMonth < 0) { + ordinalMonth += 12; + } + UBool isLeapMonth = hasLeapMonthBetweenWinterSolstices && + hasNoMajorSolarTerm(thisMoon) && + !isLeapMonthBetween(firstMoon, newMoonNear(thisMoon - SYNODIC_GAP, false)); + + internalSet(UCAL_MONTH, month-1); // Convert from 1-based to 0-based + internalSet(UCAL_ORDINAL_MONTH, ordinalMonth); // Convert from 1-based to 0-based + internalSet(UCAL_IS_LEAP_MONTH, isLeapMonth?1:0); + + + if (setAllFields) { + + // Extended year and cycle year is based on the epoch year + + int32_t extended_year = gyear - fEpochYear; + int cycle_year = gyear - CHINESE_EPOCH_YEAR; + if (month < 11 || + gmonth >= UCAL_JULY) { + extended_year++; + cycle_year++; + } + int32_t dayOfMonth = days - thisMoon + 1; + + internalSet(UCAL_EXTENDED_YEAR, extended_year); + + // 0->0,60 1->1,1 60->1,60 61->2,1 etc. + int32_t yearOfCycle; + int32_t cycle = ClockMath::floorDivide(cycle_year - 1, 60, &yearOfCycle); + internalSet(UCAL_ERA, cycle + 1); + internalSet(UCAL_YEAR, yearOfCycle + 1); + + internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); + + // Days will be before the first new year we compute if this + // date is in month 11, leap 11, 12. There is never a leap 12. + // New year computations are cached so this should be cheap in + // the long run. + int32_t theNewYear = newYear(gyear); + if (days < theNewYear) { + theNewYear = newYear(gyear-1); + } + internalSet(UCAL_DAY_OF_YEAR, days - theNewYear + 1); + } +} + + +//------------------------------------------------------------------ +// Fields to time +//------------------------------------------------------------------ + +/** + * Return the Chinese new year of the given Gregorian year. + * @param gyear a Gregorian year + * @return days after January 1, 1970 0:00 astronomical base zone of the + * Chinese new year of the given year (this will be a new moon) + */ +int32_t ChineseCalendar::newYear(int32_t gyear) const { + UErrorCode status = U_ZERO_ERROR; + int32_t cacheValue = CalendarCache::get(&gChineseCalendarNewYearCache, gyear, status); + + if (cacheValue == 0) { + + int32_t solsticeBefore= winterSolstice(gyear - 1); + int32_t solsticeAfter = winterSolstice(gyear); + int32_t newMoon1 = newMoonNear(solsticeBefore + 1, true); + int32_t newMoon2 = newMoonNear(newMoon1 + SYNODIC_GAP, true); + int32_t newMoon11 = newMoonNear(solsticeAfter + 1, false); + + if (synodicMonthsBetween(newMoon1, newMoon11) == 12 && + (hasNoMajorSolarTerm(newMoon1) || hasNoMajorSolarTerm(newMoon2))) { + cacheValue = newMoonNear(newMoon2 + SYNODIC_GAP, true); + } else { + cacheValue = newMoon2; + } + + CalendarCache::put(&gChineseCalendarNewYearCache, gyear, cacheValue, status); + } + if(U_FAILURE(status)) { + cacheValue = 0; + } + return cacheValue; +} + +/** + * Adjust this calendar to be delta months before or after a given + * start position, pinning the day of month if necessary. The start + * position is given as a local days number for the start of the month + * and a day-of-month. Used by add() and roll(). + * @param newMoon the local days of the first day of the month of the + * start position (days after January 1, 1970 0:00 Asia/Shanghai) + * @param dom the 1-based day-of-month of the start position + * @param delta the number of months to move forward or backward from + * the start position + */ +void ChineseCalendar::offsetMonth(int32_t newMoon, int32_t dom, int32_t delta) { + UErrorCode status = U_ZERO_ERROR; + + // Move to the middle of the month before our target month. + newMoon += (int32_t) (CalendarAstronomer::SYNODIC_MONTH * (delta - 0.5)); + + // Search forward to the target month's new moon + newMoon = newMoonNear(newMoon, true); + + // Find the target dom + int32_t jd = newMoon + kEpochStartAsJulianDay - 1 + dom; + + // Pin the dom. In this calendar all months are 29 or 30 days + // so pinning just means handling dom 30. + if (dom > 29) { + set(UCAL_JULIAN_DAY, jd-1); + // TODO Fix this. We really shouldn't ever have to + // explicitly call complete(). This is either a bug in + // this method, in ChineseCalendar, or in + // Calendar.getActualMaximum(). I suspect the last. + complete(status); + if (U_FAILURE(status)) return; + if (getActualMaximum(UCAL_DAY_OF_MONTH, status) >= dom) { + if (U_FAILURE(status)) return; + set(UCAL_JULIAN_DAY, jd); + } + } else { + set(UCAL_JULIAN_DAY, jd); + } +} + +constexpr uint32_t kChineseRelatedYearDiff = -2637; + +int32_t ChineseCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kChineseRelatedYearDiff; +} + +void ChineseCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kChineseRelatedYearDiff); +} + +// default century + +static UDate gSystemDefaultCenturyStart = DBL_MIN; +static int32_t gSystemDefaultCenturyStartYear = -1; +static icu::UInitOnce gSystemDefaultCenturyInitOnce {}; + + +UBool ChineseCalendar::haveDefaultCentury() const +{ + return true; +} + +UDate ChineseCalendar::defaultCenturyStart() const +{ + return internalGetDefaultCenturyStart(); +} + +int32_t ChineseCalendar::defaultCenturyStartYear() const +{ + return internalGetDefaultCenturyStartYear(); +} + +static void U_CALLCONV initializeSystemDefaultCentury() +{ + // initialize systemDefaultCentury and systemDefaultCenturyYear based + // on the current time. They'll be set to 80 years before + // the current time. + UErrorCode status = U_ZERO_ERROR; + ChineseCalendar calendar(Locale("@calendar=chinese"),status); + if (U_SUCCESS(status)) { + calendar.setTime(Calendar::getNow(), status); + calendar.add(UCAL_YEAR, -80, status); + gSystemDefaultCenturyStart = calendar.getTime(status); + gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); + } + // We have no recourse upon failure unless we want to propagate the failure + // out. +} + +UDate +ChineseCalendar::internalGetDefaultCenturyStart() const +{ + // lazy-evaluate systemDefaultCenturyStart + umtx_initOnce(gSystemDefaultCenturyInitOnce, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStart; +} + +int32_t +ChineseCalendar::internalGetDefaultCenturyStartYear() const +{ + // lazy-evaluate systemDefaultCenturyStartYear + umtx_initOnce(gSystemDefaultCenturyInitOnce, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStartYear; +} + +bool +ChineseCalendar::inTemporalLeapYear(UErrorCode &status) const +{ + int32_t days = getActualMaximum(UCAL_DAY_OF_YEAR, status); + if (U_FAILURE(status)) return false; + return days > 360; +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChineseCalendar) + + +static const char * const gTemporalLeapMonthCodes[] = { + "M01L", "M02L", "M03L", "M04L", "M05L", "M06L", + "M07L", "M08L", "M09L", "M10L", "M11L", "M12L", nullptr +}; + +const char* ChineseCalendar::getTemporalMonthCode(UErrorCode &status) const { + // We need to call get, not internalGet, to force the calculation + // from UCAL_ORDINAL_MONTH. + int32_t is_leap = get(UCAL_IS_LEAP_MONTH, status); + if (U_FAILURE(status)) return nullptr; + if (is_leap != 0) { + int32_t month = get(UCAL_MONTH, status); + if (U_FAILURE(status)) return nullptr; + return gTemporalLeapMonthCodes[month]; + } + return Calendar::getTemporalMonthCode(status); +} + +void +ChineseCalendar::setTemporalMonthCode(const char* code, UErrorCode& status ) +{ + if (U_FAILURE(status)) return; + int32_t len = static_cast(uprv_strlen(code)); + if (len != 4 || code[0] != 'M' || code[3] != 'L') { + set(UCAL_IS_LEAP_MONTH, 0); + return Calendar::setTemporalMonthCode(code, status); + } + for (int m = 0; gTemporalLeapMonthCodes[m] != nullptr; m++) { + if (uprv_strcmp(code, gTemporalLeapMonthCodes[m]) == 0) { + set(UCAL_MONTH, m); + set(UCAL_IS_LEAP_MONTH, 1); + return; + } + } + status = U_ILLEGAL_ARGUMENT_ERROR; +} + +int32_t ChineseCalendar::internalGetMonth() const { + if (resolveFields(kMonthPrecedence) == UCAL_MONTH) { + return internalGet(UCAL_MONTH); + } + LocalPointer temp(this->clone()); + temp->set(UCAL_MONTH, 0); + temp->set(UCAL_IS_LEAP_MONTH, 0); + temp->set(UCAL_DATE, 1); + // Calculate the UCAL_MONTH and UCAL_IS_LEAP_MONTH by adding number of + // months. + UErrorCode status = U_ZERO_ERROR; + temp->roll(UCAL_MONTH, internalGet(UCAL_ORDINAL_MONTH), status); + + + ChineseCalendar *nonConstThis = (ChineseCalendar*)this; // cast away const + nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, temp->get(UCAL_IS_LEAP_MONTH, status)); + int32_t month = temp->get(UCAL_MONTH, status); + U_ASSERT(U_SUCCESS(status)); + nonConstThis->internalSet(UCAL_MONTH, month); + return month; +} + +int32_t ChineseCalendar::internalGetMonth(int32_t defaultValue) const { + if (resolveFields(kMonthPrecedence) == UCAL_MONTH) { + return internalGet(UCAL_MONTH, defaultValue); + } + return internalGetMonth(); +} + +U_NAMESPACE_END + +#endif + diff --git a/deps/icu-small/source/i18n/chnsecal.h b/deps/icu-small/source/i18n/chnsecal.h index 488fe169d99c28..9d3b34edaaad2a 100644 --- a/deps/icu-small/source/i18n/chnsecal.h +++ b/deps/icu-small/source/i18n/chnsecal.h @@ -1,283 +1,337 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ***************************************************************************** - * Copyright (C) 2007-2013, International Business Machines Corporation - * and others. All Rights Reserved. - ***************************************************************************** - * - * File CHNSECAL.H - * - * Modification History: - * - * Date Name Description - * 9/18/2007 ajmacher ported from java ChineseCalendar - ***************************************************************************** - */ - -#ifndef CHNSECAL_H -#define CHNSECAL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/calendar.h" -#include "unicode/timezone.h" - -U_NAMESPACE_BEGIN - -/** - * ChineseCalendar is a concrete subclass of {@link Calendar} - * that implements a traditional Chinese calendar. The traditional Chinese - * calendar is a lunisolar calendar: Each month starts on a new moon, and - * the months are numbered according to solar events, specifically, to - * guarantee that month 11 always contains the winter solstice. In order - * to accomplish this, leap months are inserted in certain years. Leap - * months are numbered the same as the month they follow. The decision of - * which month is a leap month depends on the relative movements of the sun - * and moon. - * - *

This class defines one addition field beyond those defined by - * Calendar: The IS_LEAP_MONTH field takes the - * value of 0 for normal months, or 1 for leap months. - * - *

All astronomical computations are performed with respect to a time - * zone of GMT+8:00 and a longitude of 120 degrees east. Although some - * calendars implement a historically more accurate convention of using - * Beijing's local longitude (116 degrees 25 minutes east) and time zone - * (GMT+7:45:40) for dates before 1929, we do not implement this here. - * - *

Years are counted in two different ways in the Chinese calendar. The - * first method is by sequential numbering from the 61st year of the reign - * of Huang Di, 2637 BCE, which is designated year 1 on the Chinese - * calendar. The second method uses 60-year cycles from the same starting - * point, which is designated year 1 of cycle 1. In this class, the - * EXTENDED_YEAR field contains the sequential year count. - * The ERA field contains the cycle number, and the - * YEAR field contains the year of the cycle, a value between - * 1 and 60. - * - *

There is some variation in what is considered the starting point of - * the calendar, with some sources starting in the first year of the reign - * of Huang Di, rather than the 61st. This gives continuous year numbers - * 60 years greater and cycle numbers one greater than what this class - * implements. - * - *

Because ChineseCalendar defines an additional field and - * redefines the way the ERA field is used, it requires a new - * format class, ChineseDateFormat. As always, use the - * methods DateFormat.getXxxInstance(Calendar cal,...) to - * obtain a formatter for this calendar. - * - *

References:

- * - *

- * This class should only be subclassed to implement variants of the Chinese lunar calendar.

- *

- * ChineseCalendar usually should be instantiated using - * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a ULocale - * with the tag "@calendar=chinese".

- * - * @see com.ibm.icu.text.ChineseDateFormat - * @see com.ibm.icu.util.Calendar - * @author Alan Liu - * @internal - */ -class U_I18N_API ChineseCalendar : public Calendar { - public: - //------------------------------------------------------------------------- - // Constructors... - //------------------------------------------------------------------------- - - /** - * Constructs a ChineseCalendar based on the current time in the default time zone - * with the given locale. - * - * @param aLocale The given locale. - * @param success Indicates the status of ChineseCalendar object construction. - * Returns U_ZERO_ERROR if constructed successfully. - * @internal - */ - ChineseCalendar(const Locale& aLocale, UErrorCode &success); - - protected: - - /** - * Constructs a ChineseCalendar based on the current time in the default time zone - * with the given locale, using the specified epoch year and time zone for - * astronomical calculations. - * - * @param aLocale The given locale. - * @param epochYear The epoch year to use for calculation. - * @param zoneAstroCalc The TimeZone to use for astronomical calculations. If null, - * will be set appropriately for Chinese calendar (UTC + 8:00). - * @param success Indicates the status of ChineseCalendar object construction; - * if successful, will not be changed to an error value. - * @internal - */ - ChineseCalendar(const Locale& aLocale, int32_t epochYear, const TimeZone* zoneAstroCalc, UErrorCode &success); - - public: - /** - * Copy Constructor - * @internal - */ - ChineseCalendar(const ChineseCalendar& other); - - /** - * Destructor. - * @internal - */ - virtual ~ChineseCalendar(); - - // clone - virtual ChineseCalendar* clone() const override; - - private: - - //------------------------------------------------------------------------- - // Internal data.... - //------------------------------------------------------------------------- - - UBool isLeapYear; - int32_t fEpochYear; // Start year of this Chinese calendar instance. - const TimeZone* fZoneAstroCalc; // Zone used for the astronomical calculation - // of this Chinese calendar instance. - - //---------------------------------------------------------------------- - // Calendar framework - //---------------------------------------------------------------------- - - protected: - virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; - virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; - virtual int32_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override; - virtual int32_t handleGetExtendedYear() override; - virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; - virtual const UFieldResolutionTable* getFieldResolutionTable() const override; - - public: - virtual void add(UCalendarDateFields field, int32_t amount, UErrorCode &status) override; - virtual void add(EDateFields field, int32_t amount, UErrorCode &status) override; - virtual void roll(UCalendarDateFields field, int32_t amount, UErrorCode &status) override; - virtual void roll(EDateFields field, int32_t amount, UErrorCode &status) override; - - //---------------------------------------------------------------------- - // Internal methods & astronomical calculations - //---------------------------------------------------------------------- - - private: - - static const UFieldResolutionTable CHINESE_DATE_PRECEDENCE[]; - - double daysToMillis(double days) const; - double millisToDays(double millis) const; - virtual int32_t winterSolstice(int32_t gyear) const; - virtual int32_t newMoonNear(double days, UBool after) const; - virtual int32_t synodicMonthsBetween(int32_t day1, int32_t day2) const; - virtual int32_t majorSolarTerm(int32_t days) const; - virtual UBool hasNoMajorSolarTerm(int32_t newMoon) const; - virtual UBool isLeapMonthBetween(int32_t newMoon1, int32_t newMoon2) const; - virtual void computeChineseFields(int32_t days, int32_t gyear, - int32_t gmonth, UBool setAllFields); - virtual int32_t newYear(int32_t gyear) const; - virtual void offsetMonth(int32_t newMoon, int32_t dom, int32_t delta); - const TimeZone* getChineseCalZoneAstroCalc(void) const; - - // UObject stuff - public: - /** - * @return The class ID for this object. All objects of a given class have the - * same class ID. Objects of other classes have different class IDs. - * @internal - */ - virtual UClassID getDynamicClassID(void) const override; - - /** - * Return the class ID for this class. This is useful only for comparing to a return - * value from getDynamicClassID(). For example: - * - * Base* polymorphic_pointer = createPolymorphicObject(); - * if (polymorphic_pointer->getDynamicClassID() == - * Derived::getStaticClassID()) ... - * - * @return The class ID for all objects of this class. - * @internal - */ - static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * return the calendar type, "chinese". - * - * @return calendar type - * @internal - */ - virtual const char * getType() const override; - - - protected: - /** - * (Overrides Calendar) Return true if the current date for this Calendar is in - * Daylight Savings Time. Recognizes DST_OFFSET, if it is set. - * - * @param status Fill-in parameter which receives the status of this operation. - * @return True if the current date for this Calendar is in Daylight Savings Time, - * false, otherwise. - * @internal - */ - virtual UBool inDaylightTime(UErrorCode& status) const override; - - - /** - * Returns true because the Islamic Calendar does have a default century - * @internal - */ - virtual UBool haveDefaultCentury() const override; - - /** - * Returns the date of the start of the default century - * @return start of century - in milliseconds since epoch, 1970 - * @internal - */ - virtual UDate defaultCenturyStart() const override; - - /** - * Returns the year in which the default century begins - * @internal - */ - virtual int32_t defaultCenturyStartYear() const override; - - private: // default century stuff. - - /** - * Returns the beginning date of the 100-year window that dates - * with 2-digit years are considered to fall within. - */ - UDate internalGetDefaultCenturyStart(void) const; - - /** - * Returns the first year of the 100-year window that dates with - * 2-digit years are considered to fall within. - */ - int32_t internalGetDefaultCenturyStartYear(void) const; - - ChineseCalendar() = delete; // default constructor not implemented -}; - -U_NAMESPACE_END - -#endif -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ***************************************************************************** + * Copyright (C) 2007-2013, International Business Machines Corporation + * and others. All Rights Reserved. + ***************************************************************************** + * + * File CHNSECAL.H + * + * Modification History: + * + * Date Name Description + * 9/18/2007 ajmacher ported from java ChineseCalendar + ***************************************************************************** + */ + +#ifndef CHNSECAL_H +#define CHNSECAL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" +#include "unicode/timezone.h" + +U_NAMESPACE_BEGIN + +/** + * ChineseCalendar is a concrete subclass of {@link Calendar} + * that implements a traditional Chinese calendar. The traditional Chinese + * calendar is a lunisolar calendar: Each month starts on a new moon, and + * the months are numbered according to solar events, specifically, to + * guarantee that month 11 always contains the winter solstice. In order + * to accomplish this, leap months are inserted in certain years. Leap + * months are numbered the same as the month they follow. The decision of + * which month is a leap month depends on the relative movements of the sun + * and moon. + * + *

This class defines one addition field beyond those defined by + * Calendar: The IS_LEAP_MONTH field takes the + * value of 0 for normal months, or 1 for leap months. + * + *

All astronomical computations are performed with respect to a time + * zone of GMT+8:00 and a longitude of 120 degrees east. Although some + * calendars implement a historically more accurate convention of using + * Beijing's local longitude (116 degrees 25 minutes east) and time zone + * (GMT+7:45:40) for dates before 1929, we do not implement this here. + * + *

Years are counted in two different ways in the Chinese calendar. The + * first method is by sequential numbering from the 61st year of the reign + * of Huang Di, 2637 BCE, which is designated year 1 on the Chinese + * calendar. The second method uses 60-year cycles from the same starting + * point, which is designated year 1 of cycle 1. In this class, the + * EXTENDED_YEAR field contains the sequential year count. + * The ERA field contains the cycle number, and the + * YEAR field contains the year of the cycle, a value between + * 1 and 60. + * + *

There is some variation in what is considered the starting point of + * the calendar, with some sources starting in the first year of the reign + * of Huang Di, rather than the 61st. This gives continuous year numbers + * 60 years greater and cycle numbers one greater than what this class + * implements. + * + *

Because ChineseCalendar defines an additional field and + * redefines the way the ERA field is used, it requires a new + * format class, ChineseDateFormat. As always, use the + * methods DateFormat.getXxxInstance(Calendar cal,...) to + * obtain a formatter for this calendar. + * + *

References:

+ * + *

+ * This class should only be subclassed to implement variants of the Chinese lunar calendar.

+ *

+ * ChineseCalendar usually should be instantiated using + * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a ULocale + * with the tag "@calendar=chinese".

+ * + * @see com.ibm.icu.text.ChineseDateFormat + * @see com.ibm.icu.util.Calendar + * @author Alan Liu + * @internal + */ +class U_I18N_API ChineseCalendar : public Calendar { + public: + //------------------------------------------------------------------------- + // Constructors... + //------------------------------------------------------------------------- + + /** + * Constructs a ChineseCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of ChineseCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + ChineseCalendar(const Locale& aLocale, UErrorCode &success); + + /** + * Returns true if the date is in a leap year. + * + * @param status ICU Error Code + * @return True if the date in the fields is in a Temporal proposal + * defined leap year. False otherwise. + */ + virtual bool inTemporalLeapYear(UErrorCode &status) const override; + + /** + * Gets The Temporal monthCode value corresponding to the month for the date. + * The value is a string identifier that starts with the literal grapheme + * "M" followed by two graphemes representing the zero-padded month number + * of the current month in a normal (non-leap) year and suffixed by an + * optional literal grapheme "L" if this is a leap month in a lunisolar + * calendar. For Chinese calendars (including Dangi), the values are + * "M01" .. "M12" for non-leap year, and "M01" .. "M12" with one of + * "M01L" .. "M12L" for leap year. + * + * @param status ICU Error Code + * @return One of 24 possible strings in + * {"M01" .. "M12", "M01L" .. "M12L"}. + * @draft ICU 73 + */ + virtual const char* getTemporalMonthCode(UErrorCode &status) const override; + + /** + * Sets The Temporal monthCode which is a string identifier that starts + * with the literal grapheme "M" followed by two graphemes representing + * the zero-padded month number of the current month in a normal + * (non-leap) year and suffixed by an optional literal grapheme "L" if this + * is a leap month in a lunisolar calendar. For Chinese calendars, the values + * are "M01" .. "M12" for non-leap years, and "M01" .. "M12" plus one in + * "M01L" .. "M12L" for leap year. + * + * @param temporalMonth The value to be set for temporal monthCode. One of + * 24 possible strings in {"M01" .. "M12", "M01L" .. "M12L"}. + * @param status ICU Error Code + * + * @draft ICU 73 + */ + virtual void setTemporalMonthCode(const char* code, UErrorCode& status) override; + + protected: + + /** + * Constructs a ChineseCalendar based on the current time in the default time zone + * with the given locale, using the specified epoch year and time zone for + * astronomical calculations. + * + * @param aLocale The given locale. + * @param epochYear The epoch year to use for calculation. + * @param zoneAstroCalc The TimeZone to use for astronomical calculations. If null, + * will be set appropriately for Chinese calendar (UTC + 8:00). + * @param success Indicates the status of ChineseCalendar object construction; + * if successful, will not be changed to an error value. + * @internal + */ + ChineseCalendar(const Locale& aLocale, int32_t epochYear, const TimeZone* zoneAstroCalc, UErrorCode &success); + + public: + /** + * Copy Constructor + * @internal + */ + ChineseCalendar(const ChineseCalendar& other); + + /** + * Destructor. + * @internal + */ + virtual ~ChineseCalendar(); + + // clone + virtual ChineseCalendar* clone() const override; + + private: + + //------------------------------------------------------------------------- + // Internal data.... + //------------------------------------------------------------------------- + + // There is a leap month between the Winter Solstice before and after the + // current date.This is different from leap year because in some year, such as + // 1813 and 2033, the leap month is after the Winter Solstice of that year. So + // this value could be false for a date prior to the Winter Solstice of that + // year but that year still has a leap month and therefor is a leap year. + UBool hasLeapMonthBetweenWinterSolstices; + int32_t fEpochYear; // Start year of this Chinese calendar instance. + const TimeZone* fZoneAstroCalc; // Zone used for the astronomical calculation + // of this Chinese calendar instance. + + //---------------------------------------------------------------------- + // Calendar framework + //---------------------------------------------------------------------- + + protected: + virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; + virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; + virtual int32_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override; + virtual int32_t handleGetExtendedYear() override; + virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; + virtual const UFieldResolutionTable* getFieldResolutionTable() const override; + + public: + virtual void add(UCalendarDateFields field, int32_t amount, UErrorCode &status) override; + virtual void add(EDateFields field, int32_t amount, UErrorCode &status) override; + virtual void roll(UCalendarDateFields field, int32_t amount, UErrorCode &status) override; + virtual void roll(EDateFields field, int32_t amount, UErrorCode &status) override; + + /** + * @return The related Gregorian year; will be obtained by modifying the value + * obtained by get from UCAL_EXTENDED_YEAR field + * @internal + */ + virtual int32_t getRelatedYear(UErrorCode &status) const override; + + /** + * @param year The related Gregorian year to set; will be modified as necessary then + * set in UCAL_EXTENDED_YEAR field + * @internal + */ + virtual void setRelatedYear(int32_t year) override; + + //---------------------------------------------------------------------- + // Internal methods & astronomical calculations + //---------------------------------------------------------------------- + + private: + + static const UFieldResolutionTable CHINESE_DATE_PRECEDENCE[]; + + double daysToMillis(double days) const; + double millisToDays(double millis) const; + virtual int32_t winterSolstice(int32_t gyear) const; + virtual int32_t newMoonNear(double days, UBool after) const; + virtual int32_t synodicMonthsBetween(int32_t day1, int32_t day2) const; + virtual int32_t majorSolarTerm(int32_t days) const; + virtual UBool hasNoMajorSolarTerm(int32_t newMoon) const; + virtual UBool isLeapMonthBetween(int32_t newMoon1, int32_t newMoon2) const; + virtual void computeChineseFields(int32_t days, int32_t gyear, + int32_t gmonth, UBool setAllFields); + virtual int32_t newYear(int32_t gyear) const; + virtual void offsetMonth(int32_t newMoon, int32_t dom, int32_t delta); + const TimeZone* getChineseCalZoneAstroCalc() const; + + // UObject stuff + public: + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * return the calendar type, "chinese". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + protected: + virtual int32_t internalGetMonth(int32_t defaultValue) const override; + + virtual int32_t internalGetMonth() const override; + + protected: + /** + * Returns true because the Islamic Calendar does have a default century + * @internal + */ + virtual UBool haveDefaultCentury() const override; + + /** + * Returns the date of the start of the default century + * @return start of century - in milliseconds since epoch, 1970 + * @internal + */ + virtual UDate defaultCenturyStart() const override; + + /** + * Returns the year in which the default century begins + * @internal + */ + virtual int32_t defaultCenturyStartYear() const override; + + private: // default century stuff. + + /** + * Returns the beginning date of the 100-year window that dates + * with 2-digit years are considered to fall within. + */ + UDate internalGetDefaultCenturyStart() const; + + /** + * Returns the first year of the 100-year window that dates with + * 2-digit years are considered to fall within. + */ + int32_t internalGetDefaultCenturyStartYear() const; + + ChineseCalendar() = delete; // default constructor not implemented +}; + +U_NAMESPACE_END + +#endif +#endif diff --git a/deps/icu-small/source/i18n/choicfmt.cpp b/deps/icu-small/source/i18n/choicfmt.cpp index d06eec35fa2355..ee1c3b6838c62e 100644 --- a/deps/icu-small/source/i18n/choicfmt.cpp +++ b/deps/icu-small/source/i18n/choicfmt.cpp @@ -1,577 +1,577 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2013, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -* -* File CHOICFMT.CPP -* -* Modification History: -* -* Date Name Description -* 02/19/97 aliu Converted from java. -* 03/20/97 helena Finished first cut of implementation and got rid -* of nextDouble/previousDouble and replaced with -* boolean array. -* 4/10/97 aliu Clean up. Modified to work on AIX. -* 06/04/97 helena Fixed applyPattern(), toPattern() and not to include -* wchar.h. -* 07/09/97 helena Made ParsePosition into a class. -* 08/06/97 nos removed overloaded constructor, fixed 'format(array)' -* 07/22/98 stephen JDK 1.2 Sync - removed UBool array (doubleFlags) -* 02/22/99 stephen Removed character literals for EBCDIC safety -******************************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/choicfmt.h" -#include "unicode/numfmt.h" -#include "unicode/locid.h" -#include "cpputils.h" -#include "cstring.h" -#include "messageimpl.h" -#include "putilimp.h" -#include "uassert.h" -#include -#include - -// ***************************************************************************** -// class ChoiceFormat -// ***************************************************************************** - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChoiceFormat) - -// Special characters used by ChoiceFormat. There are two characters -// used interchangeably to indicate <=. Either is parsed, but only -// LESS_EQUAL is generated by toPattern(). -#define SINGLE_QUOTE ((UChar)0x0027) /*'*/ -#define LESS_THAN ((UChar)0x003C) /*<*/ -#define LESS_EQUAL ((UChar)0x0023) /*#*/ -#define LESS_EQUAL2 ((UChar)0x2264) -#define VERTICAL_BAR ((UChar)0x007C) /*|*/ -#define MINUS ((UChar)0x002D) /*-*/ - -static const UChar LEFT_CURLY_BRACE = 0x7B; /*{*/ -static const UChar RIGHT_CURLY_BRACE = 0x7D; /*}*/ - -#ifdef INFINITY -#undef INFINITY -#endif -#define INFINITY ((UChar)0x221E) - -//static const UChar gPositiveInfinity[] = {INFINITY, 0}; -//static const UChar gNegativeInfinity[] = {MINUS, INFINITY, 0}; -#define POSITIVE_INF_STRLEN 1 -#define NEGATIVE_INF_STRLEN 2 - -// ------------------------------------- -// Creates a ChoiceFormat instance based on the pattern. - -ChoiceFormat::ChoiceFormat(const UnicodeString& newPattern, - UErrorCode& status) -: constructorErrorCode(status), - msgPattern(status) -{ - applyPattern(newPattern, status); -} - -// ------------------------------------- -// Creates a ChoiceFormat instance with the limit array and -// format strings for each limit. - -ChoiceFormat::ChoiceFormat(const double* limits, - const UnicodeString* formats, - int32_t cnt ) -: constructorErrorCode(U_ZERO_ERROR), - msgPattern(constructorErrorCode) -{ - setChoices(limits, NULL, formats, cnt, constructorErrorCode); -} - -// ------------------------------------- - -ChoiceFormat::ChoiceFormat(const double* limits, - const UBool* closures, - const UnicodeString* formats, - int32_t cnt ) -: constructorErrorCode(U_ZERO_ERROR), - msgPattern(constructorErrorCode) -{ - setChoices(limits, closures, formats, cnt, constructorErrorCode); -} - -// ------------------------------------- -// copy constructor - -ChoiceFormat::ChoiceFormat(const ChoiceFormat& that) -: NumberFormat(that), - constructorErrorCode(that.constructorErrorCode), - msgPattern(that.msgPattern) -{ -} - -// ------------------------------------- -// Private constructor that creates a -// ChoiceFormat instance based on the -// pattern and populates UParseError - -ChoiceFormat::ChoiceFormat(const UnicodeString& newPattern, - UParseError& parseError, - UErrorCode& status) -: constructorErrorCode(status), - msgPattern(status) -{ - applyPattern(newPattern,parseError, status); -} -// ------------------------------------- - -bool -ChoiceFormat::operator==(const Format& that) const -{ - if (this == &that) return true; - if (!NumberFormat::operator==(that)) return false; - ChoiceFormat& thatAlias = (ChoiceFormat&)that; - return msgPattern == thatAlias.msgPattern; -} - -// ------------------------------------- -// copy constructor - -const ChoiceFormat& -ChoiceFormat::operator=(const ChoiceFormat& that) -{ - if (this != &that) { - NumberFormat::operator=(that); - constructorErrorCode = that.constructorErrorCode; - msgPattern = that.msgPattern; - } - return *this; -} - -// ------------------------------------- - -ChoiceFormat::~ChoiceFormat() -{ -} - -// ------------------------------------- - -/** - * Convert a double value to a string without the overhead of NumberFormat. - */ -UnicodeString& -ChoiceFormat::dtos(double value, - UnicodeString& string) -{ - /* Buffer to contain the digits and any extra formatting stuff. */ - char temp[DBL_DIG + 16]; - char *itrPtr = temp; - char *expPtr; - - sprintf(temp, "%.*g", DBL_DIG, value); - - /* Find and convert the decimal point. - Using setlocale on some machines will cause sprintf to use a comma for certain locales. - */ - while (*itrPtr && (*itrPtr == '-' || isdigit(*itrPtr))) { - itrPtr++; - } - if (*itrPtr != 0 && *itrPtr != 'e') { - /* We reached something that looks like a decimal point. - In case someone used setlocale(), which changes the decimal point. */ - *itrPtr = '.'; - itrPtr++; - } - /* Search for the exponent */ - while (*itrPtr && *itrPtr != 'e') { - itrPtr++; - } - if (*itrPtr == 'e') { - itrPtr++; - /* Verify the exponent sign */ - if (*itrPtr == '+' || *itrPtr == '-') { - itrPtr++; - } - /* Remove leading zeros. You will see this on Windows machines. */ - expPtr = itrPtr; - while (*itrPtr == '0') { - itrPtr++; - } - if (*itrPtr && expPtr != itrPtr) { - /* Shift the exponent without zeros. */ - while (*itrPtr) { - *(expPtr++) = *(itrPtr++); - } - // NULL terminate - *expPtr = 0; - } - } - - string = UnicodeString(temp, -1, US_INV); /* invariant codepage */ - return string; -} - -// ------------------------------------- -// calls the overloaded applyPattern method. - -void -ChoiceFormat::applyPattern(const UnicodeString& pattern, - UErrorCode& status) -{ - msgPattern.parseChoiceStyle(pattern, NULL, status); - constructorErrorCode = status; -} - -// ------------------------------------- -// Applies the pattern to this ChoiceFormat instance. - -void -ChoiceFormat::applyPattern(const UnicodeString& pattern, - UParseError& parseError, - UErrorCode& status) -{ - msgPattern.parseChoiceStyle(pattern, &parseError, status); - constructorErrorCode = status; -} -// ------------------------------------- -// Returns the input pattern string. - -UnicodeString& -ChoiceFormat::toPattern(UnicodeString& result) const -{ - return result = msgPattern.getPatternString(); -} - -// ------------------------------------- -// Sets the limit and format arrays. -void -ChoiceFormat::setChoices( const double* limits, - const UnicodeString* formats, - int32_t cnt ) -{ - UErrorCode errorCode = U_ZERO_ERROR; - setChoices(limits, NULL, formats, cnt, errorCode); -} - -// ------------------------------------- -// Sets the limit and format arrays. -void -ChoiceFormat::setChoices( const double* limits, - const UBool* closures, - const UnicodeString* formats, - int32_t cnt ) -{ - UErrorCode errorCode = U_ZERO_ERROR; - setChoices(limits, closures, formats, cnt, errorCode); -} - -void -ChoiceFormat::setChoices(const double* limits, - const UBool* closures, - const UnicodeString* formats, - int32_t count, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { - return; - } - if (limits == NULL || formats == NULL) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - // Reconstruct the original input pattern. - // Modified version of the pre-ICU 4.8 toPattern() implementation. - UnicodeString result; - for (int32_t i = 0; i < count; ++i) { - if (i != 0) { - result += VERTICAL_BAR; - } - UnicodeString buf; - if (uprv_isPositiveInfinity(limits[i])) { - result += INFINITY; - } else if (uprv_isNegativeInfinity(limits[i])) { - result += MINUS; - result += INFINITY; - } else { - result += dtos(limits[i], buf); - } - if (closures != NULL && closures[i]) { - result += LESS_THAN; - } else { - result += LESS_EQUAL; - } - // Append formats[i], using quotes if there are special - // characters. Single quotes themselves must be escaped in - // either case. - const UnicodeString& text = formats[i]; - int32_t textLength = text.length(); - int32_t nestingLevel = 0; - for (int32_t j = 0; j < textLength; ++j) { - UChar c = text[j]; - if (c == SINGLE_QUOTE && nestingLevel == 0) { - // Double each top-level apostrophe. - result.append(c); - } else if (c == VERTICAL_BAR && nestingLevel == 0) { - // Surround each pipe symbol with apostrophes for quoting. - // If the next character is an apostrophe, then that will be doubled, - // and although the parser will see the apostrophe pairs beginning - // and ending one character earlier than our doubling, the result - // is as desired. - // | -> '|' - // |' -> '|''' - // |'' -> '|''''' etc. - result.append(SINGLE_QUOTE).append(c).append(SINGLE_QUOTE); - continue; // Skip the append(c) at the end of the loop body. - } else if (c == LEFT_CURLY_BRACE) { - ++nestingLevel; - } else if (c == RIGHT_CURLY_BRACE && nestingLevel > 0) { - --nestingLevel; - } - result.append(c); - } - } - // Apply the reconstructed pattern. - applyPattern(result, errorCode); -} - -// ------------------------------------- -// Gets the limit array. - -const double* -ChoiceFormat::getLimits(int32_t& cnt) const -{ - cnt = 0; - return NULL; -} - -// ------------------------------------- -// Gets the closures array. - -const UBool* -ChoiceFormat::getClosures(int32_t& cnt) const -{ - cnt = 0; - return NULL; -} - -// ------------------------------------- -// Gets the format array. - -const UnicodeString* -ChoiceFormat::getFormats(int32_t& cnt) const -{ - cnt = 0; - return NULL; -} - -// ------------------------------------- -// Formats an int64 number, it's actually formatted as -// a double. The returned format string may differ -// from the input number because of this. - -UnicodeString& -ChoiceFormat::format(int64_t number, - UnicodeString& appendTo, - FieldPosition& status) const -{ - return format((double) number, appendTo, status); -} - -// ------------------------------------- -// Formats an int32_t number, it's actually formatted as -// a double. - -UnicodeString& -ChoiceFormat::format(int32_t number, - UnicodeString& appendTo, - FieldPosition& status) const -{ - return format((double) number, appendTo, status); -} - -// ------------------------------------- -// Formats a double number. - -UnicodeString& -ChoiceFormat::format(double number, - UnicodeString& appendTo, - FieldPosition& /*pos*/) const -{ - if (msgPattern.countParts() == 0) { - // No pattern was applied, or it failed. - return appendTo; - } - // Get the appropriate sub-message. - int32_t msgStart = findSubMessage(msgPattern, 0, number); - if (!MessageImpl::jdkAposMode(msgPattern)) { - int32_t patternStart = msgPattern.getPart(msgStart).getLimit(); - int32_t msgLimit = msgPattern.getLimitPartIndex(msgStart); - appendTo.append(msgPattern.getPatternString(), - patternStart, - msgPattern.getPatternIndex(msgLimit) - patternStart); - return appendTo; - } - // JDK compatibility mode: Remove SKIP_SYNTAX. - return MessageImpl::appendSubMessageWithoutSkipSyntax(msgPattern, msgStart, appendTo); -} - -int32_t -ChoiceFormat::findSubMessage(const MessagePattern &pattern, int32_t partIndex, double number) { - int32_t count = pattern.countParts(); - int32_t msgStart; - // Iterate over (ARG_INT|DOUBLE, ARG_SELECTOR, message) tuples - // until ARG_LIMIT or end of choice-only pattern. - // Ignore the first number and selector and start the loop on the first message. - partIndex += 2; - for (;;) { - // Skip but remember the current sub-message. - msgStart = partIndex; - partIndex = pattern.getLimitPartIndex(partIndex); - if (++partIndex >= count) { - // Reached the end of the choice-only pattern. - // Return with the last sub-message. - break; - } - const MessagePattern::Part &part = pattern.getPart(partIndex++); - UMessagePatternPartType type = part.getType(); - if (type == UMSGPAT_PART_TYPE_ARG_LIMIT) { - // Reached the end of the ChoiceFormat style. - // Return with the last sub-message. - break; - } - // part is an ARG_INT or ARG_DOUBLE - U_ASSERT(MessagePattern::Part::hasNumericValue(type)); - double boundary = pattern.getNumericValue(part); - // Fetch the ARG_SELECTOR character. - int32_t selectorIndex = pattern.getPatternIndex(partIndex++); - UChar boundaryChar = pattern.getPatternString().charAt(selectorIndex); - if (boundaryChar == LESS_THAN ? !(number > boundary) : !(number >= boundary)) { - // The number is in the interval between the previous boundary and the current one. - // Return with the sub-message between them. - // The !(a>b) and !(a>=b) comparisons are equivalent to - // (a<=b) and (a= 0) { - int32_t newIndex = start + len; - if (newIndex > furthest) { - furthest = newIndex; - bestNumber = tempNumber; - if (furthest == source.length()) { - break; - } - } - } - partIndex = msgLimit + 1; - } - if (furthest == start) { - pos.setErrorIndex(start); - } else { - pos.setIndex(furthest); - } - return bestNumber; -} - -int32_t -ChoiceFormat::matchStringUntilLimitPart( - const MessagePattern &pattern, int32_t partIndex, int32_t limitPartIndex, - const UnicodeString &source, int32_t sourceOffset) { - int32_t matchingSourceLength = 0; - const UnicodeString &msgString = pattern.getPatternString(); - int32_t prevIndex = pattern.getPart(partIndex).getLimit(); - for (;;) { - const MessagePattern::Part &part = pattern.getPart(++partIndex); - if (partIndex == limitPartIndex || part.getType() == UMSGPAT_PART_TYPE_SKIP_SYNTAX) { - int32_t index = part.getIndex(); - int32_t length = index - prevIndex; - if (length != 0 && 0 != source.compare(sourceOffset, length, msgString, prevIndex, length)) { - return -1; // mismatch - } - matchingSourceLength += length; - if (partIndex == limitPartIndex) { - return matchingSourceLength; - } - prevIndex = part.getLimit(); // SKIP_SYNTAX - } - } -} - -// ------------------------------------- - -ChoiceFormat* -ChoiceFormat::clone() const -{ - ChoiceFormat *aCopy = new ChoiceFormat(*this); - return aCopy; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1997-2013, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* File CHOICFMT.CPP +* +* Modification History: +* +* Date Name Description +* 02/19/97 aliu Converted from java. +* 03/20/97 helena Finished first cut of implementation and got rid +* of nextDouble/previousDouble and replaced with +* boolean array. +* 4/10/97 aliu Clean up. Modified to work on AIX. +* 06/04/97 helena Fixed applyPattern(), toPattern() and not to include +* wchar.h. +* 07/09/97 helena Made ParsePosition into a class. +* 08/06/97 nos removed overloaded constructor, fixed 'format(array)' +* 07/22/98 stephen JDK 1.2 Sync - removed UBool array (doubleFlags) +* 02/22/99 stephen Removed character literals for EBCDIC safety +******************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/choicfmt.h" +#include "unicode/numfmt.h" +#include "unicode/locid.h" +#include "cpputils.h" +#include "cstring.h" +#include "messageimpl.h" +#include "putilimp.h" +#include "uassert.h" +#include +#include + +// ***************************************************************************** +// class ChoiceFormat +// ***************************************************************************** + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChoiceFormat) + +// Special characters used by ChoiceFormat. There are two characters +// used interchangeably to indicate <=. Either is parsed, but only +// LESS_EQUAL is generated by toPattern(). +#define SINGLE_QUOTE ((char16_t)0x0027) /*'*/ +#define LESS_THAN ((char16_t)0x003C) /*<*/ +#define LESS_EQUAL ((char16_t)0x0023) /*#*/ +#define LESS_EQUAL2 ((char16_t)0x2264) +#define VERTICAL_BAR ((char16_t)0x007C) /*|*/ +#define MINUS ((char16_t)0x002D) /*-*/ + +static const char16_t LEFT_CURLY_BRACE = 0x7B; /*{*/ +static const char16_t RIGHT_CURLY_BRACE = 0x7D; /*}*/ + +#ifdef INFINITY +#undef INFINITY +#endif +#define INFINITY ((char16_t)0x221E) + +//static const char16_t gPositiveInfinity[] = {INFINITY, 0}; +//static const char16_t gNegativeInfinity[] = {MINUS, INFINITY, 0}; +#define POSITIVE_INF_STRLEN 1 +#define NEGATIVE_INF_STRLEN 2 + +// ------------------------------------- +// Creates a ChoiceFormat instance based on the pattern. + +ChoiceFormat::ChoiceFormat(const UnicodeString& newPattern, + UErrorCode& status) +: constructorErrorCode(status), + msgPattern(status) +{ + applyPattern(newPattern, status); +} + +// ------------------------------------- +// Creates a ChoiceFormat instance with the limit array and +// format strings for each limit. + +ChoiceFormat::ChoiceFormat(const double* limits, + const UnicodeString* formats, + int32_t cnt ) +: constructorErrorCode(U_ZERO_ERROR), + msgPattern(constructorErrorCode) +{ + setChoices(limits, nullptr, formats, cnt, constructorErrorCode); +} + +// ------------------------------------- + +ChoiceFormat::ChoiceFormat(const double* limits, + const UBool* closures, + const UnicodeString* formats, + int32_t cnt ) +: constructorErrorCode(U_ZERO_ERROR), + msgPattern(constructorErrorCode) +{ + setChoices(limits, closures, formats, cnt, constructorErrorCode); +} + +// ------------------------------------- +// copy constructor + +ChoiceFormat::ChoiceFormat(const ChoiceFormat& that) +: NumberFormat(that), + constructorErrorCode(that.constructorErrorCode), + msgPattern(that.msgPattern) +{ +} + +// ------------------------------------- +// Private constructor that creates a +// ChoiceFormat instance based on the +// pattern and populates UParseError + +ChoiceFormat::ChoiceFormat(const UnicodeString& newPattern, + UParseError& parseError, + UErrorCode& status) +: constructorErrorCode(status), + msgPattern(status) +{ + applyPattern(newPattern,parseError, status); +} +// ------------------------------------- + +bool +ChoiceFormat::operator==(const Format& that) const +{ + if (this == &that) return true; + if (!NumberFormat::operator==(that)) return false; + const ChoiceFormat& thatAlias = static_cast(that); + return msgPattern == thatAlias.msgPattern; +} + +// ------------------------------------- +// copy constructor + +const ChoiceFormat& +ChoiceFormat::operator=(const ChoiceFormat& that) +{ + if (this != &that) { + NumberFormat::operator=(that); + constructorErrorCode = that.constructorErrorCode; + msgPattern = that.msgPattern; + } + return *this; +} + +// ------------------------------------- + +ChoiceFormat::~ChoiceFormat() +{ +} + +// ------------------------------------- + +/** + * Convert a double value to a string without the overhead of NumberFormat. + */ +UnicodeString& +ChoiceFormat::dtos(double value, + UnicodeString& string) +{ + /* Buffer to contain the digits and any extra formatting stuff. */ + char temp[DBL_DIG + 16]; + char *itrPtr = temp; + char *expPtr; + + snprintf(temp, sizeof(temp), "%.*g", DBL_DIG, value); + + /* Find and convert the decimal point. + Using setlocale on some machines will cause snprintf to use a comma for certain locales. + */ + while (*itrPtr && (*itrPtr == '-' || isdigit(*itrPtr))) { + itrPtr++; + } + if (*itrPtr != 0 && *itrPtr != 'e') { + /* We reached something that looks like a decimal point. + In case someone used setlocale(), which changes the decimal point. */ + *itrPtr = '.'; + itrPtr++; + } + /* Search for the exponent */ + while (*itrPtr && *itrPtr != 'e') { + itrPtr++; + } + if (*itrPtr == 'e') { + itrPtr++; + /* Verify the exponent sign */ + if (*itrPtr == '+' || *itrPtr == '-') { + itrPtr++; + } + /* Remove leading zeros. You will see this on Windows machines. */ + expPtr = itrPtr; + while (*itrPtr == '0') { + itrPtr++; + } + if (*itrPtr && expPtr != itrPtr) { + /* Shift the exponent without zeros. */ + while (*itrPtr) { + *(expPtr++) = *(itrPtr++); + } + // NUL terminate + *expPtr = 0; + } + } + + string = UnicodeString(temp, -1, US_INV); /* invariant codepage */ + return string; +} + +// ------------------------------------- +// calls the overloaded applyPattern method. + +void +ChoiceFormat::applyPattern(const UnicodeString& pattern, + UErrorCode& status) +{ + msgPattern.parseChoiceStyle(pattern, nullptr, status); + constructorErrorCode = status; +} + +// ------------------------------------- +// Applies the pattern to this ChoiceFormat instance. + +void +ChoiceFormat::applyPattern(const UnicodeString& pattern, + UParseError& parseError, + UErrorCode& status) +{ + msgPattern.parseChoiceStyle(pattern, &parseError, status); + constructorErrorCode = status; +} +// ------------------------------------- +// Returns the input pattern string. + +UnicodeString& +ChoiceFormat::toPattern(UnicodeString& result) const +{ + return result = msgPattern.getPatternString(); +} + +// ------------------------------------- +// Sets the limit and format arrays. +void +ChoiceFormat::setChoices( const double* limits, + const UnicodeString* formats, + int32_t cnt ) +{ + UErrorCode errorCode = U_ZERO_ERROR; + setChoices(limits, nullptr, formats, cnt, errorCode); +} + +// ------------------------------------- +// Sets the limit and format arrays. +void +ChoiceFormat::setChoices( const double* limits, + const UBool* closures, + const UnicodeString* formats, + int32_t cnt ) +{ + UErrorCode errorCode = U_ZERO_ERROR; + setChoices(limits, closures, formats, cnt, errorCode); +} + +void +ChoiceFormat::setChoices(const double* limits, + const UBool* closures, + const UnicodeString* formats, + int32_t count, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { + return; + } + if (limits == nullptr || formats == nullptr) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + // Reconstruct the original input pattern. + // Modified version of the pre-ICU 4.8 toPattern() implementation. + UnicodeString result; + for (int32_t i = 0; i < count; ++i) { + if (i != 0) { + result += VERTICAL_BAR; + } + UnicodeString buf; + if (uprv_isPositiveInfinity(limits[i])) { + result += INFINITY; + } else if (uprv_isNegativeInfinity(limits[i])) { + result += MINUS; + result += INFINITY; + } else { + result += dtos(limits[i], buf); + } + if (closures != nullptr && closures[i]) { + result += LESS_THAN; + } else { + result += LESS_EQUAL; + } + // Append formats[i], using quotes if there are special + // characters. Single quotes themselves must be escaped in + // either case. + const UnicodeString& text = formats[i]; + int32_t textLength = text.length(); + int32_t nestingLevel = 0; + for (int32_t j = 0; j < textLength; ++j) { + char16_t c = text[j]; + if (c == SINGLE_QUOTE && nestingLevel == 0) { + // Double each top-level apostrophe. + result.append(c); + } else if (c == VERTICAL_BAR && nestingLevel == 0) { + // Surround each pipe symbol with apostrophes for quoting. + // If the next character is an apostrophe, then that will be doubled, + // and although the parser will see the apostrophe pairs beginning + // and ending one character earlier than our doubling, the result + // is as desired. + // | -> '|' + // |' -> '|''' + // |'' -> '|''''' etc. + result.append(SINGLE_QUOTE).append(c).append(SINGLE_QUOTE); + continue; // Skip the append(c) at the end of the loop body. + } else if (c == LEFT_CURLY_BRACE) { + ++nestingLevel; + } else if (c == RIGHT_CURLY_BRACE && nestingLevel > 0) { + --nestingLevel; + } + result.append(c); + } + } + // Apply the reconstructed pattern. + applyPattern(result, errorCode); +} + +// ------------------------------------- +// Gets the limit array. + +const double* +ChoiceFormat::getLimits(int32_t& cnt) const +{ + cnt = 0; + return nullptr; +} + +// ------------------------------------- +// Gets the closures array. + +const UBool* +ChoiceFormat::getClosures(int32_t& cnt) const +{ + cnt = 0; + return nullptr; +} + +// ------------------------------------- +// Gets the format array. + +const UnicodeString* +ChoiceFormat::getFormats(int32_t& cnt) const +{ + cnt = 0; + return nullptr; +} + +// ------------------------------------- +// Formats an int64 number, it's actually formatted as +// a double. The returned format string may differ +// from the input number because of this. + +UnicodeString& +ChoiceFormat::format(int64_t number, + UnicodeString& appendTo, + FieldPosition& status) const +{ + return format((double) number, appendTo, status); +} + +// ------------------------------------- +// Formats an int32_t number, it's actually formatted as +// a double. + +UnicodeString& +ChoiceFormat::format(int32_t number, + UnicodeString& appendTo, + FieldPosition& status) const +{ + return format((double) number, appendTo, status); +} + +// ------------------------------------- +// Formats a double number. + +UnicodeString& +ChoiceFormat::format(double number, + UnicodeString& appendTo, + FieldPosition& /*pos*/) const +{ + if (msgPattern.countParts() == 0) { + // No pattern was applied, or it failed. + return appendTo; + } + // Get the appropriate sub-message. + int32_t msgStart = findSubMessage(msgPattern, 0, number); + if (!MessageImpl::jdkAposMode(msgPattern)) { + int32_t patternStart = msgPattern.getPart(msgStart).getLimit(); + int32_t msgLimit = msgPattern.getLimitPartIndex(msgStart); + appendTo.append(msgPattern.getPatternString(), + patternStart, + msgPattern.getPatternIndex(msgLimit) - patternStart); + return appendTo; + } + // JDK compatibility mode: Remove SKIP_SYNTAX. + return MessageImpl::appendSubMessageWithoutSkipSyntax(msgPattern, msgStart, appendTo); +} + +int32_t +ChoiceFormat::findSubMessage(const MessagePattern &pattern, int32_t partIndex, double number) { + int32_t count = pattern.countParts(); + int32_t msgStart; + // Iterate over (ARG_INT|DOUBLE, ARG_SELECTOR, message) tuples + // until ARG_LIMIT or end of choice-only pattern. + // Ignore the first number and selector and start the loop on the first message. + partIndex += 2; + for (;;) { + // Skip but remember the current sub-message. + msgStart = partIndex; + partIndex = pattern.getLimitPartIndex(partIndex); + if (++partIndex >= count) { + // Reached the end of the choice-only pattern. + // Return with the last sub-message. + break; + } + const MessagePattern::Part &part = pattern.getPart(partIndex++); + UMessagePatternPartType type = part.getType(); + if (type == UMSGPAT_PART_TYPE_ARG_LIMIT) { + // Reached the end of the ChoiceFormat style. + // Return with the last sub-message. + break; + } + // part is an ARG_INT or ARG_DOUBLE + U_ASSERT(MessagePattern::Part::hasNumericValue(type)); + double boundary = pattern.getNumericValue(part); + // Fetch the ARG_SELECTOR character. + int32_t selectorIndex = pattern.getPatternIndex(partIndex++); + char16_t boundaryChar = pattern.getPatternString().charAt(selectorIndex); + if (boundaryChar == LESS_THAN ? !(number > boundary) : !(number >= boundary)) { + // The number is in the interval between the previous boundary and the current one. + // Return with the sub-message between them. + // The !(a>b) and !(a>=b) comparisons are equivalent to + // (a<=b) and (a= 0) { + int32_t newIndex = start + len; + if (newIndex > furthest) { + furthest = newIndex; + bestNumber = tempNumber; + if (furthest == source.length()) { + break; + } + } + } + partIndex = msgLimit + 1; + } + if (furthest == start) { + pos.setErrorIndex(start); + } else { + pos.setIndex(furthest); + } + return bestNumber; +} + +int32_t +ChoiceFormat::matchStringUntilLimitPart( + const MessagePattern &pattern, int32_t partIndex, int32_t limitPartIndex, + const UnicodeString &source, int32_t sourceOffset) { + int32_t matchingSourceLength = 0; + const UnicodeString &msgString = pattern.getPatternString(); + int32_t prevIndex = pattern.getPart(partIndex).getLimit(); + for (;;) { + const MessagePattern::Part &part = pattern.getPart(++partIndex); + if (partIndex == limitPartIndex || part.getType() == UMSGPAT_PART_TYPE_SKIP_SYNTAX) { + int32_t index = part.getIndex(); + int32_t length = index - prevIndex; + if (length != 0 && 0 != source.compare(sourceOffset, length, msgString, prevIndex, length)) { + return -1; // mismatch + } + matchingSourceLength += length; + if (partIndex == limitPartIndex) { + return matchingSourceLength; + } + prevIndex = part.getLimit(); // SKIP_SYNTAX + } + } +} + +// ------------------------------------- + +ChoiceFormat* +ChoiceFormat::clone() const +{ + ChoiceFormat *aCopy = new ChoiceFormat(*this); + return aCopy; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/coleitr.cpp b/deps/icu-small/source/i18n/coleitr.cpp index 05039502734a63..9f2b933b09217b 100644 --- a/deps/icu-small/source/i18n/coleitr.cpp +++ b/deps/icu-small/source/i18n/coleitr.cpp @@ -1,473 +1,473 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1996-2014, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -/* -* File coleitr.cpp -* -* Created by: Helena Shih -* -* Modification History: -* -* Date Name Description -* -* 6/23/97 helena Adding comments to make code more readable. -* 08/03/98 erm Synched with 1.2 version of CollationElementIterator.java -* 12/10/99 aliu Ported Thai collation support from Java. -* 01/25/01 swquek Modified to a C++ wrapper calling C APIs (ucoliter.h) -* 02/19/01 swquek Removed CollationElementIterator() since it is -* private constructor and no calls are made to it -* 2012-2014 markus Rewritten in C++ again. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/chariter.h" -#include "unicode/coleitr.h" -#include "unicode/tblcoll.h" -#include "unicode/ustring.h" -#include "cmemory.h" -#include "collation.h" -#include "collationdata.h" -#include "collationiterator.h" -#include "collationsets.h" -#include "collationtailoring.h" -#include "uassert.h" -#include "uhash.h" -#include "utf16collationiterator.h" -#include "uvectr32.h" - -/* Constants --------------------------------------------------------------- */ - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CollationElementIterator) - -/* CollationElementIterator public constructor/destructor ------------------ */ - -CollationElementIterator::CollationElementIterator( - const CollationElementIterator& other) - : UObject(other), iter_(NULL), rbc_(NULL), otherHalf_(0), dir_(0), offsets_(NULL) { - *this = other; -} - -CollationElementIterator::~CollationElementIterator() -{ - delete iter_; - delete offsets_; -} - -/* CollationElementIterator public methods --------------------------------- */ - -namespace { - -uint32_t getFirstHalf(uint32_t p, uint32_t lower32) { - return (p & 0xffff0000) | ((lower32 >> 16) & 0xff00) | ((lower32 >> 8) & 0xff); -} -uint32_t getSecondHalf(uint32_t p, uint32_t lower32) { - return (p << 16) | ((lower32 >> 8) & 0xff00) | (lower32 & 0x3f); -} -UBool ceNeedsTwoParts(int64_t ce) { - return (ce & INT64_C(0xffff00ff003f)) != 0; -} - -} // namespace - -int32_t CollationElementIterator::getOffset() const -{ - if (dir_ < 0 && offsets_ != NULL && !offsets_->isEmpty()) { - // CollationIterator::previousCE() decrements the CEs length - // while it pops CEs from its internal buffer. - int32_t i = iter_->getCEsLength(); - if (otherHalf_ != 0) { - // Return the trailing CE offset while we are in the middle of a 64-bit CE. - ++i; - } - U_ASSERT(i < offsets_->size()); - return offsets_->elementAti(i); - } - return iter_->getOffset(); -} - -/** -* Get the ordering priority of the next character in the string. -* @return the next character's ordering. Returns NULLORDER if an error has -* occurred or if the end of string has been reached -*/ -int32_t CollationElementIterator::next(UErrorCode& status) -{ - if (U_FAILURE(status)) { return NULLORDER; } - if (dir_ > 1) { - // Continue forward iteration. Test this first. - if (otherHalf_ != 0) { - uint32_t oh = otherHalf_; - otherHalf_ = 0; - return oh; - } - } else if (dir_ == 1) { - // next() after setOffset() - dir_ = 2; - } else if (dir_ == 0) { - // The iter_ is already reset to the start of the text. - dir_ = 2; - } else /* dir_ < 0 */ { - // illegal change of direction - status = U_INVALID_STATE_ERROR; - return NULLORDER; - } - // No need to keep all CEs in the buffer when we iterate. - iter_->clearCEsIfNoneRemaining(); - int64_t ce = iter_->nextCE(status); - if (ce == Collation::NO_CE) { return NULLORDER; } - // Turn the 64-bit CE into two old-style 32-bit CEs, without quaternary bits. - uint32_t p = (uint32_t)(ce >> 32); - uint32_t lower32 = (uint32_t)ce; - uint32_t firstHalf = getFirstHalf(p, lower32); - uint32_t secondHalf = getSecondHalf(p, lower32); - if (secondHalf != 0) { - otherHalf_ = secondHalf | 0xc0; // continuation CE - } - return firstHalf; -} - -bool CollationElementIterator::operator!=( - const CollationElementIterator& other) const -{ - return !(*this == other); -} - -bool CollationElementIterator::operator==( - const CollationElementIterator& that) const -{ - if (this == &that) { - return true; - } - - return - (rbc_ == that.rbc_ || *rbc_ == *that.rbc_) && - otherHalf_ == that.otherHalf_ && - normalizeDir() == that.normalizeDir() && - string_ == that.string_ && - *iter_ == *that.iter_; -} - -/** -* Get the ordering priority of the previous collation element in the string. -* @param status the error code status. -* @return the previous element's ordering. Returns NULLORDER if an error has -* occurred or if the start of string has been reached. -*/ -int32_t CollationElementIterator::previous(UErrorCode& status) -{ - if (U_FAILURE(status)) { return NULLORDER; } - if (dir_ < 0) { - // Continue backwards iteration. Test this first. - if (otherHalf_ != 0) { - uint32_t oh = otherHalf_; - otherHalf_ = 0; - return oh; - } - } else if (dir_ == 0) { - iter_->resetToOffset(string_.length()); - dir_ = -1; - } else if (dir_ == 1) { - // previous() after setOffset() - dir_ = -1; - } else /* dir_ > 1 */ { - // illegal change of direction - status = U_INVALID_STATE_ERROR; - return NULLORDER; - } - if (offsets_ == NULL) { - offsets_ = new UVector32(status); - if (offsets_ == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULLORDER; - } - } - // If we already have expansion CEs, then we also have offsets. - // Otherwise remember the trailing offset in case we need to - // write offsets for an artificial expansion. - int32_t limitOffset = iter_->getCEsLength() == 0 ? iter_->getOffset() : 0; - int64_t ce = iter_->previousCE(*offsets_, status); - if (ce == Collation::NO_CE) { return NULLORDER; } - // Turn the 64-bit CE into two old-style 32-bit CEs, without quaternary bits. - uint32_t p = (uint32_t)(ce >> 32); - uint32_t lower32 = (uint32_t)ce; - uint32_t firstHalf = getFirstHalf(p, lower32); - uint32_t secondHalf = getSecondHalf(p, lower32); - if (secondHalf != 0) { - if (offsets_->isEmpty()) { - // When we convert a single 64-bit CE into two 32-bit CEs, - // we need to make this artificial expansion behave like a normal expansion. - // See CollationIterator::previousCE(). - offsets_->addElement(iter_->getOffset(), status); - offsets_->addElement(limitOffset, status); - } - otherHalf_ = firstHalf; - return secondHalf | 0xc0; // continuation CE - } - return firstHalf; -} - -/** -* Resets the cursor to the beginning of the string. -*/ -void CollationElementIterator::reset() -{ - iter_ ->resetToOffset(0); - otherHalf_ = 0; - dir_ = 0; -} - -void CollationElementIterator::setOffset(int32_t newOffset, - UErrorCode& status) -{ - if (U_FAILURE(status)) { return; } - if (0 < newOffset && newOffset < string_.length()) { - int32_t offset = newOffset; - do { - UChar c = string_.charAt(offset); - if (!rbc_->isUnsafe(c) || - (U16_IS_LEAD(c) && !rbc_->isUnsafe(string_.char32At(offset)))) { - break; - } - // Back up to before this unsafe character. - --offset; - } while (offset > 0); - if (offset < newOffset) { - // We might have backed up more than necessary. - // For example, contractions "ch" and "cu" make both 'h' and 'u' unsafe, - // but for text "chu" setOffset(2) should remain at 2 - // although we initially back up to offset 0. - // Find the last safe offset no greater than newOffset by iterating forward. - int32_t lastSafeOffset = offset; - do { - iter_->resetToOffset(lastSafeOffset); - do { - iter_->nextCE(status); - if (U_FAILURE(status)) { return; } - } while ((offset = iter_->getOffset()) == lastSafeOffset); - if (offset <= newOffset) { - lastSafeOffset = offset; - } - } while (offset < newOffset); - newOffset = lastSafeOffset; - } - } - iter_->resetToOffset(newOffset); - otherHalf_ = 0; - dir_ = 1; -} - -/** -* Sets the source to the new source string. -*/ -void CollationElementIterator::setText(const UnicodeString& source, - UErrorCode& status) -{ - if (U_FAILURE(status)) { - return; - } - - string_ = source; - const UChar *s = string_.getBuffer(); - CollationIterator *newIter; - UBool numeric = rbc_->settings->isNumeric(); - if (rbc_->settings->dontCheckFCD()) { - newIter = new UTF16CollationIterator(rbc_->data, numeric, s, s, s + string_.length()); - } else { - newIter = new FCDUTF16CollationIterator(rbc_->data, numeric, s, s, s + string_.length()); - } - if (newIter == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - delete iter_; - iter_ = newIter; - otherHalf_ = 0; - dir_ = 0; -} - -// Sets the source to the new character iterator. -void CollationElementIterator::setText(CharacterIterator& source, - UErrorCode& status) -{ - if (U_FAILURE(status)) - return; - - source.getText(string_); - setText(string_, status); -} - -int32_t CollationElementIterator::strengthOrder(int32_t order) const -{ - UColAttributeValue s = (UColAttributeValue)rbc_->settings->getStrength(); - // Mask off the unwanted differences. - if (s == UCOL_PRIMARY) { - order &= 0xffff0000; - } - else if (s == UCOL_SECONDARY) { - order &= 0xffffff00; - } - - return order; -} - -/* CollationElementIterator private constructors/destructors --------------- */ - -/** -* This is the "real" constructor for this class; it constructs an iterator -* over the source text using the specified collator -*/ -CollationElementIterator::CollationElementIterator( - const UnicodeString &source, - const RuleBasedCollator *coll, - UErrorCode &status) - : iter_(NULL), rbc_(coll), otherHalf_(0), dir_(0), offsets_(NULL) { - setText(source, status); -} - -/** -* This is the "real" constructor for this class; it constructs an iterator over -* the source text using the specified collator -*/ -CollationElementIterator::CollationElementIterator( - const CharacterIterator &source, - const RuleBasedCollator *coll, - UErrorCode &status) - : iter_(NULL), rbc_(coll), otherHalf_(0), dir_(0), offsets_(NULL) { - // We only call source.getText() which should be const anyway. - setText(const_cast(source), status); -} - -/* CollationElementIterator private methods -------------------------------- */ - -const CollationElementIterator& CollationElementIterator::operator=( - const CollationElementIterator& other) -{ - if (this == &other) { - return *this; - } - - CollationIterator *newIter; - const FCDUTF16CollationIterator *otherFCDIter = - dynamic_cast(other.iter_); - if(otherFCDIter != NULL) { - newIter = new FCDUTF16CollationIterator(*otherFCDIter, string_.getBuffer()); - } else { - const UTF16CollationIterator *otherIter = - dynamic_cast(other.iter_); - if(otherIter != NULL) { - newIter = new UTF16CollationIterator(*otherIter, string_.getBuffer()); - } else { - newIter = NULL; - } - } - if(newIter != NULL) { - delete iter_; - iter_ = newIter; - rbc_ = other.rbc_; - otherHalf_ = other.otherHalf_; - dir_ = other.dir_; - - string_ = other.string_; - } - if(other.dir_ < 0 && other.offsets_ != NULL && !other.offsets_->isEmpty()) { - UErrorCode errorCode = U_ZERO_ERROR; - if(offsets_ == NULL) { - offsets_ = new UVector32(other.offsets_->size(), errorCode); - } - if(offsets_ != NULL) { - offsets_->assign(*other.offsets_, errorCode); - } - } - return *this; -} - -namespace { - -class MaxExpSink : public ContractionsAndExpansions::CESink { -public: - MaxExpSink(UHashtable *h, UErrorCode &ec) : maxExpansions(h), errorCode(ec) {} - virtual ~MaxExpSink(); - virtual void handleCE(int64_t /*ce*/) override {} - virtual void handleExpansion(const int64_t ces[], int32_t length) override { - if (length <= 1) { - // We do not need to add single CEs into the map. - return; - } - int32_t count = 0; // number of CE "halves" - for (int32_t i = 0; i < length; ++i) { - count += ceNeedsTwoParts(ces[i]) ? 2 : 1; - } - // last "half" of the last CE - int64_t ce = ces[length - 1]; - uint32_t p = (uint32_t)(ce >> 32); - uint32_t lower32 = (uint32_t)ce; - uint32_t lastHalf = getSecondHalf(p, lower32); - if (lastHalf == 0) { - lastHalf = getFirstHalf(p, lower32); - U_ASSERT(lastHalf != 0); - } else { - lastHalf |= 0xc0; // old-style continuation CE - } - if (count > uhash_igeti(maxExpansions, (int32_t)lastHalf)) { - uhash_iputi(maxExpansions, (int32_t)lastHalf, count, &errorCode); - } - } - -private: - UHashtable *maxExpansions; - UErrorCode &errorCode; -}; - -MaxExpSink::~MaxExpSink() {} - -} // namespace - -UHashtable * -CollationElementIterator::computeMaxExpansions(const CollationData *data, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return NULL; } - UHashtable *maxExpansions = uhash_open(uhash_hashLong, uhash_compareLong, - uhash_compareLong, &errorCode); - if (U_FAILURE(errorCode)) { return NULL; } - MaxExpSink sink(maxExpansions, errorCode); - ContractionsAndExpansions(NULL, NULL, &sink, true).forData(data, errorCode); - if (U_FAILURE(errorCode)) { - uhash_close(maxExpansions); - return NULL; - } - return maxExpansions; -} - -int32_t -CollationElementIterator::getMaxExpansion(int32_t order) const { - return getMaxExpansion(rbc_->tailoring->maxExpansions, order); -} - -int32_t -CollationElementIterator::getMaxExpansion(const UHashtable *maxExpansions, int32_t order) { - if (order == 0) { return 1; } - int32_t max; - if(maxExpansions != NULL && (max = uhash_igeti(maxExpansions, order)) != 0) { - return max; - } - if ((order & 0xc0) == 0xc0) { - // old-style continuation CE - return 2; - } else { - return 1; - } -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_COLLATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1996-2014, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +*/ + +/* +* File coleitr.cpp +* +* Created by: Helena Shih +* +* Modification History: +* +* Date Name Description +* +* 6/23/97 helena Adding comments to make code more readable. +* 08/03/98 erm Synched with 1.2 version of CollationElementIterator.java +* 12/10/99 aliu Ported Thai collation support from Java. +* 01/25/01 swquek Modified to a C++ wrapper calling C APIs (ucoliter.h) +* 02/19/01 swquek Removed CollationElementIterator() since it is +* private constructor and no calls are made to it +* 2012-2014 markus Rewritten in C++ again. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/chariter.h" +#include "unicode/coleitr.h" +#include "unicode/tblcoll.h" +#include "unicode/ustring.h" +#include "cmemory.h" +#include "collation.h" +#include "collationdata.h" +#include "collationiterator.h" +#include "collationsets.h" +#include "collationtailoring.h" +#include "uassert.h" +#include "uhash.h" +#include "utf16collationiterator.h" +#include "uvectr32.h" + +/* Constants --------------------------------------------------------------- */ + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CollationElementIterator) + +/* CollationElementIterator public constructor/destructor ------------------ */ + +CollationElementIterator::CollationElementIterator( + const CollationElementIterator& other) + : UObject(other), iter_(nullptr), rbc_(nullptr), otherHalf_(0), dir_(0), offsets_(nullptr) { + *this = other; +} + +CollationElementIterator::~CollationElementIterator() +{ + delete iter_; + delete offsets_; +} + +/* CollationElementIterator public methods --------------------------------- */ + +namespace { + +uint32_t getFirstHalf(uint32_t p, uint32_t lower32) { + return (p & 0xffff0000) | ((lower32 >> 16) & 0xff00) | ((lower32 >> 8) & 0xff); +} +uint32_t getSecondHalf(uint32_t p, uint32_t lower32) { + return (p << 16) | ((lower32 >> 8) & 0xff00) | (lower32 & 0x3f); +} +UBool ceNeedsTwoParts(int64_t ce) { + return (ce & INT64_C(0xffff00ff003f)) != 0; +} + +} // namespace + +int32_t CollationElementIterator::getOffset() const +{ + if (dir_ < 0 && offsets_ != nullptr && !offsets_->isEmpty()) { + // CollationIterator::previousCE() decrements the CEs length + // while it pops CEs from its internal buffer. + int32_t i = iter_->getCEsLength(); + if (otherHalf_ != 0) { + // Return the trailing CE offset while we are in the middle of a 64-bit CE. + ++i; + } + U_ASSERT(i < offsets_->size()); + return offsets_->elementAti(i); + } + return iter_->getOffset(); +} + +/** +* Get the ordering priority of the next character in the string. +* @return the next character's ordering. Returns NULLORDER if an error has +* occurred or if the end of string has been reached +*/ +int32_t CollationElementIterator::next(UErrorCode& status) +{ + if (U_FAILURE(status)) { return NULLORDER; } + if (dir_ > 1) { + // Continue forward iteration. Test this first. + if (otherHalf_ != 0) { + uint32_t oh = otherHalf_; + otherHalf_ = 0; + return oh; + } + } else if (dir_ == 1) { + // next() after setOffset() + dir_ = 2; + } else if (dir_ == 0) { + // The iter_ is already reset to the start of the text. + dir_ = 2; + } else /* dir_ < 0 */ { + // illegal change of direction + status = U_INVALID_STATE_ERROR; + return NULLORDER; + } + // No need to keep all CEs in the buffer when we iterate. + iter_->clearCEsIfNoneRemaining(); + int64_t ce = iter_->nextCE(status); + if (ce == Collation::NO_CE) { return NULLORDER; } + // Turn the 64-bit CE into two old-style 32-bit CEs, without quaternary bits. + uint32_t p = (uint32_t)(ce >> 32); + uint32_t lower32 = (uint32_t)ce; + uint32_t firstHalf = getFirstHalf(p, lower32); + uint32_t secondHalf = getSecondHalf(p, lower32); + if (secondHalf != 0) { + otherHalf_ = secondHalf | 0xc0; // continuation CE + } + return firstHalf; +} + +bool CollationElementIterator::operator!=( + const CollationElementIterator& other) const +{ + return !(*this == other); +} + +bool CollationElementIterator::operator==( + const CollationElementIterator& that) const +{ + if (this == &that) { + return true; + } + + return + (rbc_ == that.rbc_ || *rbc_ == *that.rbc_) && + otherHalf_ == that.otherHalf_ && + normalizeDir() == that.normalizeDir() && + string_ == that.string_ && + *iter_ == *that.iter_; +} + +/** +* Get the ordering priority of the previous collation element in the string. +* @param status the error code status. +* @return the previous element's ordering. Returns NULLORDER if an error has +* occurred or if the start of string has been reached. +*/ +int32_t CollationElementIterator::previous(UErrorCode& status) +{ + if (U_FAILURE(status)) { return NULLORDER; } + if (dir_ < 0) { + // Continue backwards iteration. Test this first. + if (otherHalf_ != 0) { + uint32_t oh = otherHalf_; + otherHalf_ = 0; + return oh; + } + } else if (dir_ == 0) { + iter_->resetToOffset(string_.length()); + dir_ = -1; + } else if (dir_ == 1) { + // previous() after setOffset() + dir_ = -1; + } else /* dir_ > 1 */ { + // illegal change of direction + status = U_INVALID_STATE_ERROR; + return NULLORDER; + } + if (offsets_ == nullptr) { + offsets_ = new UVector32(status); + if (offsets_ == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULLORDER; + } + } + // If we already have expansion CEs, then we also have offsets. + // Otherwise remember the trailing offset in case we need to + // write offsets for an artificial expansion. + int32_t limitOffset = iter_->getCEsLength() == 0 ? iter_->getOffset() : 0; + int64_t ce = iter_->previousCE(*offsets_, status); + if (ce == Collation::NO_CE) { return NULLORDER; } + // Turn the 64-bit CE into two old-style 32-bit CEs, without quaternary bits. + uint32_t p = (uint32_t)(ce >> 32); + uint32_t lower32 = (uint32_t)ce; + uint32_t firstHalf = getFirstHalf(p, lower32); + uint32_t secondHalf = getSecondHalf(p, lower32); + if (secondHalf != 0) { + if (offsets_->isEmpty()) { + // When we convert a single 64-bit CE into two 32-bit CEs, + // we need to make this artificial expansion behave like a normal expansion. + // See CollationIterator::previousCE(). + offsets_->addElement(iter_->getOffset(), status); + offsets_->addElement(limitOffset, status); + } + otherHalf_ = firstHalf; + return secondHalf | 0xc0; // continuation CE + } + return firstHalf; +} + +/** +* Resets the cursor to the beginning of the string. +*/ +void CollationElementIterator::reset() +{ + iter_ ->resetToOffset(0); + otherHalf_ = 0; + dir_ = 0; +} + +void CollationElementIterator::setOffset(int32_t newOffset, + UErrorCode& status) +{ + if (U_FAILURE(status)) { return; } + if (0 < newOffset && newOffset < string_.length()) { + int32_t offset = newOffset; + do { + char16_t c = string_.charAt(offset); + if (!rbc_->isUnsafe(c) || + (U16_IS_LEAD(c) && !rbc_->isUnsafe(string_.char32At(offset)))) { + break; + } + // Back up to before this unsafe character. + --offset; + } while (offset > 0); + if (offset < newOffset) { + // We might have backed up more than necessary. + // For example, contractions "ch" and "cu" make both 'h' and 'u' unsafe, + // but for text "chu" setOffset(2) should remain at 2 + // although we initially back up to offset 0. + // Find the last safe offset no greater than newOffset by iterating forward. + int32_t lastSafeOffset = offset; + do { + iter_->resetToOffset(lastSafeOffset); + do { + iter_->nextCE(status); + if (U_FAILURE(status)) { return; } + } while ((offset = iter_->getOffset()) == lastSafeOffset); + if (offset <= newOffset) { + lastSafeOffset = offset; + } + } while (offset < newOffset); + newOffset = lastSafeOffset; + } + } + iter_->resetToOffset(newOffset); + otherHalf_ = 0; + dir_ = 1; +} + +/** +* Sets the source to the new source string. +*/ +void CollationElementIterator::setText(const UnicodeString& source, + UErrorCode& status) +{ + if (U_FAILURE(status)) { + return; + } + + string_ = source; + const char16_t *s = string_.getBuffer(); + CollationIterator *newIter; + UBool numeric = rbc_->settings->isNumeric(); + if (rbc_->settings->dontCheckFCD()) { + newIter = new UTF16CollationIterator(rbc_->data, numeric, s, s, s + string_.length()); + } else { + newIter = new FCDUTF16CollationIterator(rbc_->data, numeric, s, s, s + string_.length()); + } + if (newIter == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + delete iter_; + iter_ = newIter; + otherHalf_ = 0; + dir_ = 0; +} + +// Sets the source to the new character iterator. +void CollationElementIterator::setText(CharacterIterator& source, + UErrorCode& status) +{ + if (U_FAILURE(status)) + return; + + source.getText(string_); + setText(string_, status); +} + +int32_t CollationElementIterator::strengthOrder(int32_t order) const +{ + UColAttributeValue s = (UColAttributeValue)rbc_->settings->getStrength(); + // Mask off the unwanted differences. + if (s == UCOL_PRIMARY) { + order &= 0xffff0000; + } + else if (s == UCOL_SECONDARY) { + order &= 0xffffff00; + } + + return order; +} + +/* CollationElementIterator private constructors/destructors --------------- */ + +/** +* This is the "real" constructor for this class; it constructs an iterator +* over the source text using the specified collator +*/ +CollationElementIterator::CollationElementIterator( + const UnicodeString &source, + const RuleBasedCollator *coll, + UErrorCode &status) + : iter_(nullptr), rbc_(coll), otherHalf_(0), dir_(0), offsets_(nullptr) { + setText(source, status); +} + +/** +* This is the "real" constructor for this class; it constructs an iterator over +* the source text using the specified collator +*/ +CollationElementIterator::CollationElementIterator( + const CharacterIterator &source, + const RuleBasedCollator *coll, + UErrorCode &status) + : iter_(nullptr), rbc_(coll), otherHalf_(0), dir_(0), offsets_(nullptr) { + // We only call source.getText() which should be const anyway. + setText(const_cast(source), status); +} + +/* CollationElementIterator private methods -------------------------------- */ + +const CollationElementIterator& CollationElementIterator::operator=( + const CollationElementIterator& other) +{ + if (this == &other) { + return *this; + } + + CollationIterator *newIter; + const FCDUTF16CollationIterator *otherFCDIter = + dynamic_cast(other.iter_); + if(otherFCDIter != nullptr) { + newIter = new FCDUTF16CollationIterator(*otherFCDIter, string_.getBuffer()); + } else { + const UTF16CollationIterator *otherIter = + dynamic_cast(other.iter_); + if(otherIter != nullptr) { + newIter = new UTF16CollationIterator(*otherIter, string_.getBuffer()); + } else { + newIter = nullptr; + } + } + if(newIter != nullptr) { + delete iter_; + iter_ = newIter; + rbc_ = other.rbc_; + otherHalf_ = other.otherHalf_; + dir_ = other.dir_; + + string_ = other.string_; + } + if(other.dir_ < 0 && other.offsets_ != nullptr && !other.offsets_->isEmpty()) { + UErrorCode errorCode = U_ZERO_ERROR; + if(offsets_ == nullptr) { + offsets_ = new UVector32(other.offsets_->size(), errorCode); + } + if(offsets_ != nullptr) { + offsets_->assign(*other.offsets_, errorCode); + } + } + return *this; +} + +namespace { + +class MaxExpSink : public ContractionsAndExpansions::CESink { +public: + MaxExpSink(UHashtable *h, UErrorCode &ec) : maxExpansions(h), errorCode(ec) {} + virtual ~MaxExpSink(); + virtual void handleCE(int64_t /*ce*/) override {} + virtual void handleExpansion(const int64_t ces[], int32_t length) override { + if (length <= 1) { + // We do not need to add single CEs into the map. + return; + } + int32_t count = 0; // number of CE "halves" + for (int32_t i = 0; i < length; ++i) { + count += ceNeedsTwoParts(ces[i]) ? 2 : 1; + } + // last "half" of the last CE + int64_t ce = ces[length - 1]; + uint32_t p = (uint32_t)(ce >> 32); + uint32_t lower32 = (uint32_t)ce; + uint32_t lastHalf = getSecondHalf(p, lower32); + if (lastHalf == 0) { + lastHalf = getFirstHalf(p, lower32); + U_ASSERT(lastHalf != 0); + } else { + lastHalf |= 0xc0; // old-style continuation CE + } + if (count > uhash_igeti(maxExpansions, (int32_t)lastHalf)) { + uhash_iputi(maxExpansions, (int32_t)lastHalf, count, &errorCode); + } + } + +private: + UHashtable *maxExpansions; + UErrorCode &errorCode; +}; + +MaxExpSink::~MaxExpSink() {} + +} // namespace + +UHashtable * +CollationElementIterator::computeMaxExpansions(const CollationData *data, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return nullptr; } + UHashtable *maxExpansions = uhash_open(uhash_hashLong, uhash_compareLong, + uhash_compareLong, &errorCode); + if (U_FAILURE(errorCode)) { return nullptr; } + MaxExpSink sink(maxExpansions, errorCode); + ContractionsAndExpansions(nullptr, nullptr, &sink, true).forData(data, errorCode); + if (U_FAILURE(errorCode)) { + uhash_close(maxExpansions); + return nullptr; + } + return maxExpansions; +} + +int32_t +CollationElementIterator::getMaxExpansion(int32_t order) const { + return getMaxExpansion(rbc_->tailoring->maxExpansions, order); +} + +int32_t +CollationElementIterator::getMaxExpansion(const UHashtable *maxExpansions, int32_t order) { + if (order == 0) { return 1; } + int32_t max; + if(maxExpansions != nullptr && (max = uhash_igeti(maxExpansions, order)) != 0) { + return max; + } + if ((order & 0xc0) == 0xc0) { + // old-style continuation CE + return 2; + } else { + return 1; + } +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_COLLATION */ diff --git a/deps/icu-small/source/i18n/coll.cpp b/deps/icu-small/source/i18n/coll.cpp index b22a9d58760480..9c5ea2b39c5bdf 100644 --- a/deps/icu-small/source/i18n/coll.cpp +++ b/deps/icu-small/source/i18n/coll.cpp @@ -1,1019 +1,1021 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ****************************************************************************** - * Copyright (C) 1996-2014, International Business Machines Corporation and - * others. All Rights Reserved. - ****************************************************************************** - */ - -/** - * File coll.cpp - * - * Created by: Helena Shih - * - * Modification History: - * - * Date Name Description - * 2/5/97 aliu Modified createDefault to load collation data from - * binary files when possible. Added related methods - * createCollationFromFile, chopLocale, createPathName. - * 2/11/97 aliu Added methods addToCache, findInCache, which implement - * a Collation cache. Modified createDefault to look in - * cache first, and also to store newly created Collation - * objects in the cache. Modified to not use gLocPath. - * 2/12/97 aliu Modified to create objects from RuleBasedCollator cache. - * Moved cache out of Collation class. - * 2/13/97 aliu Moved several methods out of this class and into - * RuleBasedCollator, with modifications. Modified - * createDefault() to call new RuleBasedCollator(Locale&) - * constructor. General clean up and documentation. - * 2/20/97 helena Added clone, operator==, operator!=, operator=, and copy - * constructor. - * 05/06/97 helena Added memory allocation error detection. - * 05/08/97 helena Added createInstance(). - * 6/20/97 helena Java class name change. - * 04/23/99 stephen Removed EDecompositionMode, merged with - * Normalizer::EMode - * 11/23/9 srl Inlining of some critical functions - * 01/29/01 synwee Modified into a C++ wrapper calling C APIs (ucol.h) - * 2012-2014 markus Rewritten in C++ again. - */ - -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/coll.h" -#include "unicode/tblcoll.h" -#include "collationdata.h" -#include "collationroot.h" -#include "collationtailoring.h" -#include "ucol_imp.h" -#include "cstring.h" -#include "cmemory.h" -#include "umutex.h" -#include "servloc.h" -#include "uassert.h" -#include "ustrenum.h" -#include "uresimp.h" -#include "ucln_in.h" - -static icu::Locale* availableLocaleList = NULL; -static int32_t availableLocaleListCount; -#if !UCONFIG_NO_SERVICE -static icu::ICULocaleService* gService = NULL; -static icu::UInitOnce gServiceInitOnce {}; -#endif -static icu::UInitOnce gAvailableLocaleListInitOnce {}; - -/** - * Release all static memory held by collator. - */ -U_CDECL_BEGIN -static UBool U_CALLCONV collator_cleanup(void) { -#if !UCONFIG_NO_SERVICE - if (gService) { - delete gService; - gService = NULL; - } - gServiceInitOnce.reset(); -#endif - if (availableLocaleList) { - delete []availableLocaleList; - availableLocaleList = NULL; - } - availableLocaleListCount = 0; - gAvailableLocaleListInitOnce.reset(); - return true; -} - -U_CDECL_END - -U_NAMESPACE_BEGIN - -#if !UCONFIG_NO_SERVICE - -// ------------------------------------------ -// -// Registration -// - -//------------------------------------------- - -CollatorFactory::~CollatorFactory() {} - -//------------------------------------------- - -UBool -CollatorFactory::visible(void) const { - return true; -} - -//------------------------------------------- - -UnicodeString& -CollatorFactory::getDisplayName(const Locale& objectLocale, - const Locale& displayLocale, - UnicodeString& result) -{ - return objectLocale.getDisplayName(displayLocale, result); -} - -// ------------------------------------- - -class ICUCollatorFactory : public ICUResourceBundleFactory { - public: - ICUCollatorFactory() : ICUResourceBundleFactory(UnicodeString(U_ICUDATA_COLL, -1, US_INV)) { } - virtual ~ICUCollatorFactory(); - protected: - virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const override; -}; - -ICUCollatorFactory::~ICUCollatorFactory() {} - -UObject* -ICUCollatorFactory::create(const ICUServiceKey& key, const ICUService* /* service */, UErrorCode& status) const { - if (handlesKey(key, status)) { - const LocaleKey& lkey = (const LocaleKey&)key; - Locale loc; - // make sure the requested locale is correct - // default LocaleFactory uses currentLocale since that's the one vetted by handlesKey - // but for ICU rb resources we use the actual one since it will fallback again - lkey.canonicalLocale(loc); - - return Collator::makeInstance(loc, status); - } - return NULL; -} - -// ------------------------------------- - -class ICUCollatorService : public ICULocaleService { -public: - ICUCollatorService() - : ICULocaleService(UNICODE_STRING_SIMPLE("Collator")) - { - UErrorCode status = U_ZERO_ERROR; - registerFactory(new ICUCollatorFactory(), status); - } - - virtual ~ICUCollatorService(); - - virtual UObject* cloneInstance(UObject* instance) const override { - return ((Collator*)instance)->clone(); - } - - virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* actualID, UErrorCode& status) const override { - LocaleKey& lkey = (LocaleKey&)key; - if (actualID) { - // Ugly Hack Alert! We return an empty actualID to signal - // to callers that this is a default object, not a "real" - // service-created object. (TODO remove in 3.0) [aliu] - actualID->truncate(0); - } - Locale loc(""); - lkey.canonicalLocale(loc); - return Collator::makeInstance(loc, status); - } - - virtual UObject* getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const override { - UnicodeString ar; - if (actualReturn == NULL) { - actualReturn = &ar; - } - return (Collator*)ICULocaleService::getKey(key, actualReturn, status); - } - - virtual UBool isDefault() const override { - return countFactories() == 1; - } -}; - -ICUCollatorService::~ICUCollatorService() {} - -// ------------------------------------- - -static void U_CALLCONV initService() { - gService = new ICUCollatorService(); - ucln_i18n_registerCleanup(UCLN_I18N_COLLATOR, collator_cleanup); -} - - -static ICULocaleService* -getService(void) -{ - umtx_initOnce(gServiceInitOnce, &initService); - return gService; -} - -// ------------------------------------- - -static inline UBool -hasService(void) -{ - UBool retVal = !gServiceInitOnce.isReset() && (getService() != NULL); - return retVal; -} - -#endif /* UCONFIG_NO_SERVICE */ - -static void U_CALLCONV -initAvailableLocaleList(UErrorCode &status) { - U_ASSERT(availableLocaleListCount == 0); - U_ASSERT(availableLocaleList == NULL); - // for now, there is a hardcoded list, so just walk through that list and set it up. - UResourceBundle *index = NULL; - StackUResourceBundle installed; - int32_t i = 0; - - index = ures_openDirect(U_ICUDATA_COLL, "res_index", &status); - ures_getByKey(index, "InstalledLocales", installed.getAlias(), &status); - - if(U_SUCCESS(status)) { - availableLocaleListCount = ures_getSize(installed.getAlias()); - availableLocaleList = new Locale[availableLocaleListCount]; - - if (availableLocaleList != NULL) { - ures_resetIterator(installed.getAlias()); - while(ures_hasNext(installed.getAlias())) { - const char *tempKey = NULL; - ures_getNextString(installed.getAlias(), NULL, &tempKey, &status); - availableLocaleList[i++] = Locale(tempKey); - } - } - U_ASSERT(availableLocaleListCount == i); - } - ures_close(index); - ucln_i18n_registerCleanup(UCLN_I18N_COLLATOR, collator_cleanup); -} - -static UBool isAvailableLocaleListInitialized(UErrorCode &status) { - umtx_initOnce(gAvailableLocaleListInitOnce, &initAvailableLocaleList, status); - return U_SUCCESS(status); -} - - -// Collator public methods ----------------------------------------------- - -namespace { - -static const struct { - const char *name; - UColAttribute attr; -} collAttributes[] = { - { "colStrength", UCOL_STRENGTH }, - { "colBackwards", UCOL_FRENCH_COLLATION }, - { "colCaseLevel", UCOL_CASE_LEVEL }, - { "colCaseFirst", UCOL_CASE_FIRST }, - { "colAlternate", UCOL_ALTERNATE_HANDLING }, - { "colNormalization", UCOL_NORMALIZATION_MODE }, - { "colNumeric", UCOL_NUMERIC_COLLATION } -}; - -static const struct { - const char *name; - UColAttributeValue value; -} collAttributeValues[] = { - { "primary", UCOL_PRIMARY }, - { "secondary", UCOL_SECONDARY }, - { "tertiary", UCOL_TERTIARY }, - { "quaternary", UCOL_QUATERNARY }, - // Note: Not supporting typo "quarternary" because it was never supported in locale IDs. - { "identical", UCOL_IDENTICAL }, - { "no", UCOL_OFF }, - { "yes", UCOL_ON }, - { "shifted", UCOL_SHIFTED }, - { "non-ignorable", UCOL_NON_IGNORABLE }, - { "lower", UCOL_LOWER_FIRST }, - { "upper", UCOL_UPPER_FIRST } -}; - -static const char *collReorderCodes[UCOL_REORDER_CODE_LIMIT - UCOL_REORDER_CODE_FIRST] = { - "space", "punct", "symbol", "currency", "digit" -}; - -int32_t getReorderCode(const char *s) { - for (int32_t i = 0; i < UPRV_LENGTHOF(collReorderCodes); ++i) { - if (uprv_stricmp(s, collReorderCodes[i]) == 0) { - return UCOL_REORDER_CODE_FIRST + i; - } - } - // Not supporting "others" = UCOL_REORDER_CODE_OTHERS - // as a synonym for Zzzz = USCRIPT_UNKNOWN for now: - // Avoid introducing synonyms/aliases. - return -1; -} - -/** - * Sets collation attributes according to locale keywords. See - * http://www.unicode.org/reports/tr35/tr35-collation.html#Collation_Settings - * - * Using "alias" keywords and values where defined: - * http://www.unicode.org/reports/tr35/tr35.html#Old_Locale_Extension_Syntax - * http://unicode.org/repos/cldr/trunk/common/bcp47/collation.xml - */ -void setAttributesFromKeywords(const Locale &loc, Collator &coll, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { - return; - } - if (uprv_strcmp(loc.getName(), loc.getBaseName()) == 0) { - // No keywords. - return; - } - char value[1024]; // The reordering value could be long. - // Check for collation keywords that were already deprecated - // before any were supported in createInstance() (except for "collation"). - int32_t length = loc.getKeywordValue("colHiraganaQuaternary", value, UPRV_LENGTHOF(value), errorCode); - if (U_FAILURE(errorCode)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (length != 0) { - errorCode = U_UNSUPPORTED_ERROR; - return; - } - length = loc.getKeywordValue("variableTop", value, UPRV_LENGTHOF(value), errorCode); - if (U_FAILURE(errorCode)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (length != 0) { - errorCode = U_UNSUPPORTED_ERROR; - return; - } - // Parse known collation keywords, ignore others. - if (errorCode == U_STRING_NOT_TERMINATED_WARNING) { - errorCode = U_ZERO_ERROR; - } - for (int32_t i = 0; i < UPRV_LENGTHOF(collAttributes); ++i) { - length = loc.getKeywordValue(collAttributes[i].name, value, UPRV_LENGTHOF(value), errorCode); - if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (length == 0) { continue; } - for (int32_t j = 0;; ++j) { - if (j == UPRV_LENGTHOF(collAttributeValues)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (uprv_stricmp(value, collAttributeValues[j].name) == 0) { - coll.setAttribute(collAttributes[i].attr, collAttributeValues[j].value, errorCode); - break; - } - } - } - length = loc.getKeywordValue("colReorder", value, UPRV_LENGTHOF(value), errorCode); - if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (length != 0) { - int32_t codes[USCRIPT_CODE_LIMIT + (UCOL_REORDER_CODE_LIMIT - UCOL_REORDER_CODE_FIRST)]; - int32_t codesLength = 0; - char *scriptName = value; - for (;;) { - if (codesLength == UPRV_LENGTHOF(codes)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - char *limit = scriptName; - char c; - while ((c = *limit) != 0 && c != '-') { ++limit; } - *limit = 0; - int32_t code; - if ((limit - scriptName) == 4) { - // Strict parsing, accept only 4-letter script codes, not long names. - code = u_getPropertyValueEnum(UCHAR_SCRIPT, scriptName); - } else { - code = getReorderCode(scriptName); - } - if (code < 0) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - codes[codesLength++] = code; - if (c == 0) { break; } - scriptName = limit + 1; - } - coll.setReorderCodes(codes, codesLength, errorCode); - } - length = loc.getKeywordValue("kv", value, UPRV_LENGTHOF(value), errorCode); - if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (length != 0) { - int32_t code = getReorderCode(value); - if (code < 0) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - coll.setMaxVariable((UColReorderCode)code, errorCode); - } - if (U_FAILURE(errorCode)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - } -} - -} // namespace - -Collator* U_EXPORT2 Collator::createInstance(UErrorCode& success) -{ - return createInstance(Locale::getDefault(), success); -} - -Collator* U_EXPORT2 Collator::createInstance(const Locale& desiredLocale, - UErrorCode& status) -{ - if (U_FAILURE(status)) - return 0; - if (desiredLocale.isBogus()) { - // Locale constructed from malformed locale ID or language tag. - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - Collator* coll; -#if !UCONFIG_NO_SERVICE - if (hasService()) { - Locale actualLoc; - coll = (Collator*)gService->get(desiredLocale, &actualLoc, status); - } else -#endif - { - coll = makeInstance(desiredLocale, status); - // Either returns NULL with U_FAILURE(status), or non-NULL with U_SUCCESS(status) - } - // The use of *coll in setAttributesFromKeywords can cause the NULL check to be - // optimized out of the delete even though setAttributesFromKeywords returns - // immediately if U_FAILURE(status), so we add a check here. - if (U_FAILURE(status)) { - return NULL; - } - setAttributesFromKeywords(desiredLocale, *coll, status); - if (U_FAILURE(status)) { - delete coll; - return NULL; - } - return coll; -} - - -Collator* Collator::makeInstance(const Locale& desiredLocale, UErrorCode& status) { - const CollationCacheEntry *entry = CollationLoader::loadTailoring(desiredLocale, status); - if (U_SUCCESS(status)) { - Collator *result = new RuleBasedCollator(entry); - if (result != NULL) { - // Both the unified cache's get() and the RBC constructor - // did addRef(). Undo one of them. - entry->removeRef(); - return result; - } - status = U_MEMORY_ALLOCATION_ERROR; - } - if (entry != NULL) { - // Undo the addRef() from the cache.get(). - entry->removeRef(); - } - return NULL; -} - -Collator * -Collator::safeClone() const { - return clone(); -} - -// implement deprecated, previously abstract method -Collator::EComparisonResult Collator::compare(const UnicodeString& source, - const UnicodeString& target) const -{ - UErrorCode ec = U_ZERO_ERROR; - return (EComparisonResult)compare(source, target, ec); -} - -// implement deprecated, previously abstract method -Collator::EComparisonResult Collator::compare(const UnicodeString& source, - const UnicodeString& target, - int32_t length) const -{ - UErrorCode ec = U_ZERO_ERROR; - return (EComparisonResult)compare(source, target, length, ec); -} - -// implement deprecated, previously abstract method -Collator::EComparisonResult Collator::compare(const UChar* source, int32_t sourceLength, - const UChar* target, int32_t targetLength) - const -{ - UErrorCode ec = U_ZERO_ERROR; - return (EComparisonResult)compare(source, sourceLength, target, targetLength, ec); -} - -UCollationResult Collator::compare(UCharIterator &/*sIter*/, - UCharIterator &/*tIter*/, - UErrorCode &status) const { - if(U_SUCCESS(status)) { - // Not implemented in the base class. - status = U_UNSUPPORTED_ERROR; - } - return UCOL_EQUAL; -} - -UCollationResult Collator::compareUTF8(const StringPiece &source, - const StringPiece &target, - UErrorCode &status) const { - if(U_FAILURE(status)) { - return UCOL_EQUAL; - } - UCharIterator sIter, tIter; - uiter_setUTF8(&sIter, source.data(), source.length()); - uiter_setUTF8(&tIter, target.data(), target.length()); - return compare(sIter, tIter, status); -} - -UBool Collator::equals(const UnicodeString& source, - const UnicodeString& target) const -{ - UErrorCode ec = U_ZERO_ERROR; - return (compare(source, target, ec) == UCOL_EQUAL); -} - -UBool Collator::greaterOrEqual(const UnicodeString& source, - const UnicodeString& target) const -{ - UErrorCode ec = U_ZERO_ERROR; - return (compare(source, target, ec) != UCOL_LESS); -} - -UBool Collator::greater(const UnicodeString& source, - const UnicodeString& target) const -{ - UErrorCode ec = U_ZERO_ERROR; - return (compare(source, target, ec) == UCOL_GREATER); -} - -// this API ignores registered collators, since it returns an -// array of indefinite lifetime -const Locale* U_EXPORT2 Collator::getAvailableLocales(int32_t& count) -{ - UErrorCode status = U_ZERO_ERROR; - Locale *result = NULL; - count = 0; - if (isAvailableLocaleListInitialized(status)) - { - result = availableLocaleList; - count = availableLocaleListCount; - } - return result; -} - -UnicodeString& U_EXPORT2 Collator::getDisplayName(const Locale& objectLocale, - const Locale& displayLocale, - UnicodeString& name) -{ -#if !UCONFIG_NO_SERVICE - if (hasService()) { - UnicodeString locNameStr; - LocaleUtility::initNameFromLocale(objectLocale, locNameStr); - return gService->getDisplayName(locNameStr, name, displayLocale); - } -#endif - return objectLocale.getDisplayName(displayLocale, name); -} - -UnicodeString& U_EXPORT2 Collator::getDisplayName(const Locale& objectLocale, - UnicodeString& name) -{ - return getDisplayName(objectLocale, Locale::getDefault(), name); -} - -/* This is useless information */ -/*void Collator::getVersion(UVersionInfo versionInfo) const -{ - if (versionInfo!=NULL) - uprv_memcpy(versionInfo, fVersion, U_MAX_VERSION_LENGTH); -} -*/ - -// UCollator protected constructor destructor ---------------------------- - -/** -* Default constructor. -* Constructor is different from the old default Collator constructor. -* The task for determining the default collation strength and normalization mode -* is left to the child class. -*/ -Collator::Collator() -: UObject() -{ -} - -/** -* Constructor. -* Empty constructor, does not handle the arguments. -* This constructor is done for backward compatibility with 1.7 and 1.8. -* The task for handling the argument collation strength and normalization -* mode is left to the child class. -* @param collationStrength collation strength -* @param decompositionMode -* @deprecated 2.4 use the default constructor instead -*/ -Collator::Collator(UCollationStrength, UNormalizationMode ) -: UObject() -{ -} - -Collator::~Collator() -{ -} - -Collator::Collator(const Collator &other) - : UObject(other) -{ -} - -bool Collator::operator==(const Collator& other) const -{ - // Subclasses: Call this method and then add more specific checks. - return typeid(*this) == typeid(other); -} - -bool Collator::operator!=(const Collator& other) const -{ - return !operator==(other); -} - -int32_t U_EXPORT2 Collator::getBound(const uint8_t *source, - int32_t sourceLength, - UColBoundMode boundType, - uint32_t noOfLevels, - uint8_t *result, - int32_t resultLength, - UErrorCode &status) -{ - return ucol_getBound(source, sourceLength, boundType, noOfLevels, result, resultLength, &status); -} - -void -Collator::setLocales(const Locale& /* requestedLocale */, const Locale& /* validLocale */, const Locale& /*actualLocale*/) { -} - -UnicodeSet *Collator::getTailoredSet(UErrorCode &status) const -{ - if(U_FAILURE(status)) { - return NULL; - } - // everything can be changed - return new UnicodeSet(0, 0x10FFFF); -} - -// ------------------------------------- - -#if !UCONFIG_NO_SERVICE -URegistryKey U_EXPORT2 -Collator::registerInstance(Collator* toAdopt, const Locale& locale, UErrorCode& status) -{ - if (U_SUCCESS(status)) { - // Set the collator locales while registering so that createInstance() - // need not guess whether the collator's locales are already set properly - // (as they are by the data loader). - toAdopt->setLocales(locale, locale, locale); - return getService()->registerInstance(toAdopt, locale, status); - } - return NULL; -} - -// ------------------------------------- - -class CFactory : public LocaleKeyFactory { -private: - CollatorFactory* _delegate; - Hashtable* _ids; - -public: - CFactory(CollatorFactory* delegate, UErrorCode& status) - : LocaleKeyFactory(delegate->visible() ? VISIBLE : INVISIBLE) - , _delegate(delegate) - , _ids(NULL) - { - if (U_SUCCESS(status)) { - int32_t count = 0; - _ids = new Hashtable(status); - if (_ids) { - const UnicodeString * idlist = _delegate->getSupportedIDs(count, status); - for (int i = 0; i < count; ++i) { - _ids->put(idlist[i], (void*)this, status); - if (U_FAILURE(status)) { - delete _ids; - _ids = NULL; - return; - } - } - } else { - status = U_MEMORY_ALLOCATION_ERROR; - } - } - } - - virtual ~CFactory(); - - virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const override; - -protected: - virtual const Hashtable* getSupportedIDs(UErrorCode& status) const override - { - if (U_SUCCESS(status)) { - return _ids; - } - return NULL; - } - - virtual UnicodeString& - getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const override; -}; - -CFactory::~CFactory() -{ - delete _delegate; - delete _ids; -} - -UObject* -CFactory::create(const ICUServiceKey& key, const ICUService* /* service */, UErrorCode& status) const -{ - if (handlesKey(key, status)) { - const LocaleKey& lkey = (const LocaleKey&)key; - Locale validLoc; - lkey.currentLocale(validLoc); - return _delegate->createCollator(validLoc); - } - return NULL; -} - -UnicodeString& -CFactory::getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const -{ - if ((_coverage & 0x1) == 0) { - UErrorCode status = U_ZERO_ERROR; - const Hashtable* ids = getSupportedIDs(status); - if (ids && (ids->get(id) != NULL)) { - Locale loc; - LocaleUtility::initLocaleFromName(id, loc); - return _delegate->getDisplayName(loc, locale, result); - } - } - result.setToBogus(); - return result; -} - -URegistryKey U_EXPORT2 -Collator::registerFactory(CollatorFactory* toAdopt, UErrorCode& status) -{ - if (U_SUCCESS(status)) { - CFactory* f = new CFactory(toAdopt, status); - if (f) { - return getService()->registerFactory(f, status); - } - status = U_MEMORY_ALLOCATION_ERROR; - } - return NULL; -} - -// ------------------------------------- - -UBool U_EXPORT2 -Collator::unregister(URegistryKey key, UErrorCode& status) -{ - if (U_SUCCESS(status)) { - if (hasService()) { - return gService->unregister(key, status); - } - status = U_ILLEGAL_ARGUMENT_ERROR; - } - return false; -} -#endif /* UCONFIG_NO_SERVICE */ - -class CollationLocaleListEnumeration : public StringEnumeration { -private: - int32_t index; -public: - static UClassID U_EXPORT2 getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; -public: - CollationLocaleListEnumeration() - : index(0) - { - // The global variables should already be initialized. - //isAvailableLocaleListInitialized(status); - } - - virtual ~CollationLocaleListEnumeration(); - - virtual StringEnumeration * clone() const override - { - CollationLocaleListEnumeration *result = new CollationLocaleListEnumeration(); - if (result) { - result->index = index; - } - return result; - } - - virtual int32_t count(UErrorCode &/*status*/) const override { - return availableLocaleListCount; - } - - virtual const char* next(int32_t* resultLength, UErrorCode& /*status*/) override { - const char* result; - if(index < availableLocaleListCount) { - result = availableLocaleList[index++].getName(); - if(resultLength != NULL) { - *resultLength = (int32_t)uprv_strlen(result); - } - } else { - if(resultLength != NULL) { - *resultLength = 0; - } - result = NULL; - } - return result; - } - - virtual const UnicodeString* snext(UErrorCode& status) override { - int32_t resultLength = 0; - const char *s = next(&resultLength, status); - return setChars(s, resultLength, status); - } - - virtual void reset(UErrorCode& /*status*/) override { - index = 0; - } -}; - -CollationLocaleListEnumeration::~CollationLocaleListEnumeration() {} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CollationLocaleListEnumeration) - - -// ------------------------------------- - -StringEnumeration* U_EXPORT2 -Collator::getAvailableLocales(void) -{ -#if !UCONFIG_NO_SERVICE - if (hasService()) { - return getService()->getAvailableLocales(); - } -#endif /* UCONFIG_NO_SERVICE */ - UErrorCode status = U_ZERO_ERROR; - if (isAvailableLocaleListInitialized(status)) { - return new CollationLocaleListEnumeration(); - } - return NULL; -} - -StringEnumeration* U_EXPORT2 -Collator::getKeywords(UErrorCode& status) { - return UStringEnumeration::fromUEnumeration( - ucol_getKeywords(&status), status); -} - -StringEnumeration* U_EXPORT2 -Collator::getKeywordValues(const char *keyword, UErrorCode& status) { - return UStringEnumeration::fromUEnumeration( - ucol_getKeywordValues(keyword, &status), status); -} - -StringEnumeration* U_EXPORT2 -Collator::getKeywordValuesForLocale(const char* key, const Locale& locale, - UBool commonlyUsed, UErrorCode& status) { - return UStringEnumeration::fromUEnumeration( - ucol_getKeywordValuesForLocale( - key, locale.getName(), commonlyUsed, &status), - status); -} - -Locale U_EXPORT2 -Collator::getFunctionalEquivalent(const char* keyword, const Locale& locale, - UBool& isAvailable, UErrorCode& status) { - // This is a wrapper over ucol_getFunctionalEquivalent - char loc[ULOC_FULLNAME_CAPACITY]; - /*int32_t len =*/ ucol_getFunctionalEquivalent(loc, sizeof(loc), - keyword, locale.getName(), &isAvailable, &status); - if (U_FAILURE(status)) { - *loc = 0; // root - } - return Locale::createFromName(loc); -} - -Collator::ECollationStrength -Collator::getStrength(void) const { - UErrorCode intStatus = U_ZERO_ERROR; - return (ECollationStrength)getAttribute(UCOL_STRENGTH, intStatus); -} - -void -Collator::setStrength(ECollationStrength newStrength) { - UErrorCode intStatus = U_ZERO_ERROR; - setAttribute(UCOL_STRENGTH, (UColAttributeValue)newStrength, intStatus); -} - -Collator & -Collator::setMaxVariable(UColReorderCode /*group*/, UErrorCode &errorCode) { - if (U_SUCCESS(errorCode)) { - errorCode = U_UNSUPPORTED_ERROR; - } - return *this; -} - -UColReorderCode -Collator::getMaxVariable() const { - return UCOL_REORDER_CODE_PUNCTUATION; -} - -int32_t -Collator::getReorderCodes(int32_t* /* dest*/, - int32_t /* destCapacity*/, - UErrorCode& status) const -{ - if (U_SUCCESS(status)) { - status = U_UNSUPPORTED_ERROR; - } - return 0; -} - -void -Collator::setReorderCodes(const int32_t* /* reorderCodes */, - int32_t /* reorderCodesLength */, - UErrorCode& status) -{ - if (U_SUCCESS(status)) { - status = U_UNSUPPORTED_ERROR; - } -} - -int32_t -Collator::getEquivalentReorderCodes(int32_t reorderCode, - int32_t *dest, int32_t capacity, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - if(capacity < 0 || (dest == NULL && capacity > 0)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - const CollationData *baseData = CollationRoot::getData(errorCode); - if(U_FAILURE(errorCode)) { return 0; } - return baseData->getEquivalentScripts(reorderCode, dest, capacity, errorCode); -} - -int32_t -Collator::internalGetShortDefinitionString(const char * /*locale*/, - char * /*buffer*/, - int32_t /*capacity*/, - UErrorCode &status) const { - if(U_SUCCESS(status)) { - status = U_UNSUPPORTED_ERROR; /* Shouldn't happen, internal function */ - } - return 0; -} - -UCollationResult -Collator::internalCompareUTF8(const char *left, int32_t leftLength, - const char *right, int32_t rightLength, - UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } - if((left == NULL && leftLength != 0) || (right == NULL && rightLength != 0)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return UCOL_EQUAL; - } - return compareUTF8( - StringPiece(left, (leftLength < 0) ? static_cast(uprv_strlen(left)) : leftLength), - StringPiece(right, (rightLength < 0) ? static_cast(uprv_strlen(right)) : rightLength), - errorCode); -} - -int32_t -Collator::internalNextSortKeyPart(UCharIterator * /*iter*/, uint32_t /*state*/[2], - uint8_t * /*dest*/, int32_t /*count*/, UErrorCode &errorCode) const { - if (U_SUCCESS(errorCode)) { - errorCode = U_UNSUPPORTED_ERROR; - } - return 0; -} - -// UCollator private data members ---------------------------------------- - -/* This is useless information */ -/*const UVersionInfo Collator::fVersion = {1, 1, 0, 0};*/ - -// ------------------------------------- - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_COLLATION */ - -/* eof */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ****************************************************************************** + * Copyright (C) 1996-2014, International Business Machines Corporation and + * others. All Rights Reserved. + ****************************************************************************** + */ + +/** + * File coll.cpp + * + * Created by: Helena Shih + * + * Modification History: + * + * Date Name Description + * 2/5/97 aliu Modified createDefault to load collation data from + * binary files when possible. Added related methods + * createCollationFromFile, chopLocale, createPathName. + * 2/11/97 aliu Added methods addToCache, findInCache, which implement + * a Collation cache. Modified createDefault to look in + * cache first, and also to store newly created Collation + * objects in the cache. Modified to not use gLocPath. + * 2/12/97 aliu Modified to create objects from RuleBasedCollator cache. + * Moved cache out of Collation class. + * 2/13/97 aliu Moved several methods out of this class and into + * RuleBasedCollator, with modifications. Modified + * createDefault() to call new RuleBasedCollator(Locale&) + * constructor. General clean up and documentation. + * 2/20/97 helena Added clone, operator==, operator!=, operator=, and copy + * constructor. + * 05/06/97 helena Added memory allocation error detection. + * 05/08/97 helena Added createInstance(). + * 6/20/97 helena Java class name change. + * 04/23/99 stephen Removed EDecompositionMode, merged with + * Normalizer::EMode + * 11/23/9 srl Inlining of some critical functions + * 01/29/01 synwee Modified into a C++ wrapper calling C APIs (ucol.h) + * 2012-2014 markus Rewritten in C++ again. + */ + +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/coll.h" +#include "unicode/tblcoll.h" +#include "collationdata.h" +#include "collationroot.h" +#include "collationtailoring.h" +#include "ucol_imp.h" +#include "cstring.h" +#include "cmemory.h" +#include "umutex.h" +#include "servloc.h" +#include "uassert.h" +#include "ustrenum.h" +#include "uresimp.h" +#include "ucln_in.h" + +static icu::Locale* availableLocaleList = nullptr; +static int32_t availableLocaleListCount; +#if !UCONFIG_NO_SERVICE +static icu::ICULocaleService* gService = nullptr; +static icu::UInitOnce gServiceInitOnce {}; +#endif +static icu::UInitOnce gAvailableLocaleListInitOnce {}; + +/** + * Release all static memory held by collator. + */ +U_CDECL_BEGIN +static UBool U_CALLCONV collator_cleanup() { +#if !UCONFIG_NO_SERVICE + if (gService) { + delete gService; + gService = nullptr; + } + gServiceInitOnce.reset(); +#endif + if (availableLocaleList) { + delete []availableLocaleList; + availableLocaleList = nullptr; + } + availableLocaleListCount = 0; + gAvailableLocaleListInitOnce.reset(); + return true; +} + +U_CDECL_END + +U_NAMESPACE_BEGIN + +#if !UCONFIG_NO_SERVICE + +// ------------------------------------------ +// +// Registration +// + +//------------------------------------------- + +CollatorFactory::~CollatorFactory() {} + +//------------------------------------------- + +UBool +CollatorFactory::visible() const { + return true; +} + +//------------------------------------------- + +UnicodeString& +CollatorFactory::getDisplayName(const Locale& objectLocale, + const Locale& displayLocale, + UnicodeString& result) +{ + return objectLocale.getDisplayName(displayLocale, result); +} + +// ------------------------------------- + +class ICUCollatorFactory : public ICUResourceBundleFactory { + public: + ICUCollatorFactory() : ICUResourceBundleFactory(UnicodeString(U_ICUDATA_COLL, -1, US_INV)) { } + virtual ~ICUCollatorFactory(); + protected: + virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const override; +}; + +ICUCollatorFactory::~ICUCollatorFactory() {} + +UObject* +ICUCollatorFactory::create(const ICUServiceKey& key, const ICUService* /* service */, UErrorCode& status) const { + if (handlesKey(key, status)) { + const LocaleKey& lkey = static_cast(key); + Locale loc; + // make sure the requested locale is correct + // default LocaleFactory uses currentLocale since that's the one vetted by handlesKey + // but for ICU rb resources we use the actual one since it will fallback again + lkey.canonicalLocale(loc); + + return Collator::makeInstance(loc, status); + } + return nullptr; +} + +// ------------------------------------- + +class ICUCollatorService : public ICULocaleService { +public: + ICUCollatorService() + : ICULocaleService(UNICODE_STRING_SIMPLE("Collator")) + { + UErrorCode status = U_ZERO_ERROR; + registerFactory(new ICUCollatorFactory(), status); + } + + virtual ~ICUCollatorService(); + + virtual UObject* cloneInstance(UObject* instance) const override { + return ((Collator*)instance)->clone(); + } + + virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* actualID, UErrorCode& status) const override { + const LocaleKey* lkey = dynamic_cast(&key); + U_ASSERT(lkey != nullptr); + if (actualID) { + // Ugly Hack Alert! We return an empty actualID to signal + // to callers that this is a default object, not a "real" + // service-created object. (TODO remove in 3.0) [aliu] + actualID->truncate(0); + } + Locale loc(""); + lkey->canonicalLocale(loc); + return Collator::makeInstance(loc, status); + } + + virtual UObject* getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const override { + UnicodeString ar; + if (actualReturn == nullptr) { + actualReturn = &ar; + } + return (Collator*)ICULocaleService::getKey(key, actualReturn, status); + } + + virtual UBool isDefault() const override { + return countFactories() == 1; + } +}; + +ICUCollatorService::~ICUCollatorService() {} + +// ------------------------------------- + +static void U_CALLCONV initService() { + gService = new ICUCollatorService(); + ucln_i18n_registerCleanup(UCLN_I18N_COLLATOR, collator_cleanup); +} + + +static ICULocaleService* +getService() +{ + umtx_initOnce(gServiceInitOnce, &initService); + return gService; +} + +// ------------------------------------- + +static inline UBool +hasService() +{ + UBool retVal = !gServiceInitOnce.isReset() && (getService() != nullptr); + return retVal; +} + +#endif /* UCONFIG_NO_SERVICE */ + +static void U_CALLCONV +initAvailableLocaleList(UErrorCode &status) { + U_ASSERT(availableLocaleListCount == 0); + U_ASSERT(availableLocaleList == nullptr); + // for now, there is a hardcoded list, so just walk through that list and set it up. + UResourceBundle *index = nullptr; + StackUResourceBundle installed; + int32_t i = 0; + + index = ures_openDirect(U_ICUDATA_COLL, "res_index", &status); + ures_getByKey(index, "InstalledLocales", installed.getAlias(), &status); + + if(U_SUCCESS(status)) { + availableLocaleListCount = ures_getSize(installed.getAlias()); + availableLocaleList = new Locale[availableLocaleListCount]; + + if (availableLocaleList != nullptr) { + ures_resetIterator(installed.getAlias()); + while(ures_hasNext(installed.getAlias())) { + const char *tempKey = nullptr; + ures_getNextString(installed.getAlias(), nullptr, &tempKey, &status); + availableLocaleList[i++] = Locale(tempKey); + } + } + U_ASSERT(availableLocaleListCount == i); + } + ures_close(index); + ucln_i18n_registerCleanup(UCLN_I18N_COLLATOR, collator_cleanup); +} + +static UBool isAvailableLocaleListInitialized(UErrorCode &status) { + umtx_initOnce(gAvailableLocaleListInitOnce, &initAvailableLocaleList, status); + return U_SUCCESS(status); +} + + +// Collator public methods ----------------------------------------------- + +namespace { + +static const struct { + const char *name; + UColAttribute attr; +} collAttributes[] = { + { "colStrength", UCOL_STRENGTH }, + { "colBackwards", UCOL_FRENCH_COLLATION }, + { "colCaseLevel", UCOL_CASE_LEVEL }, + { "colCaseFirst", UCOL_CASE_FIRST }, + { "colAlternate", UCOL_ALTERNATE_HANDLING }, + { "colNormalization", UCOL_NORMALIZATION_MODE }, + { "colNumeric", UCOL_NUMERIC_COLLATION } +}; + +static const struct { + const char *name; + UColAttributeValue value; +} collAttributeValues[] = { + { "primary", UCOL_PRIMARY }, + { "secondary", UCOL_SECONDARY }, + { "tertiary", UCOL_TERTIARY }, + { "quaternary", UCOL_QUATERNARY }, + // Note: Not supporting typo "quarternary" because it was never supported in locale IDs. + { "identical", UCOL_IDENTICAL }, + { "no", UCOL_OFF }, + { "yes", UCOL_ON }, + { "shifted", UCOL_SHIFTED }, + { "non-ignorable", UCOL_NON_IGNORABLE }, + { "lower", UCOL_LOWER_FIRST }, + { "upper", UCOL_UPPER_FIRST } +}; + +static const char *collReorderCodes[UCOL_REORDER_CODE_LIMIT - UCOL_REORDER_CODE_FIRST] = { + "space", "punct", "symbol", "currency", "digit" +}; + +int32_t getReorderCode(const char *s) { + for (int32_t i = 0; i < UPRV_LENGTHOF(collReorderCodes); ++i) { + if (uprv_stricmp(s, collReorderCodes[i]) == 0) { + return UCOL_REORDER_CODE_FIRST + i; + } + } + // Not supporting "others" = UCOL_REORDER_CODE_OTHERS + // as a synonym for Zzzz = USCRIPT_UNKNOWN for now: + // Avoid introducing synonyms/aliases. + return -1; +} + +/** + * Sets collation attributes according to locale keywords. See + * http://www.unicode.org/reports/tr35/tr35-collation.html#Collation_Settings + * + * Using "alias" keywords and values where defined: + * http://www.unicode.org/reports/tr35/tr35.html#Old_Locale_Extension_Syntax + * http://unicode.org/repos/cldr/trunk/common/bcp47/collation.xml + */ +void setAttributesFromKeywords(const Locale &loc, Collator &coll, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { + return; + } + if (uprv_strcmp(loc.getName(), loc.getBaseName()) == 0) { + // No keywords. + return; + } + char value[1024]; // The reordering value could be long. + // Check for collation keywords that were already deprecated + // before any were supported in createInstance() (except for "collation"). + int32_t length = loc.getKeywordValue("colHiraganaQuaternary", value, UPRV_LENGTHOF(value), errorCode); + if (U_FAILURE(errorCode)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (length != 0) { + errorCode = U_UNSUPPORTED_ERROR; + return; + } + length = loc.getKeywordValue("variableTop", value, UPRV_LENGTHOF(value), errorCode); + if (U_FAILURE(errorCode)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (length != 0) { + errorCode = U_UNSUPPORTED_ERROR; + return; + } + // Parse known collation keywords, ignore others. + if (errorCode == U_STRING_NOT_TERMINATED_WARNING) { + errorCode = U_ZERO_ERROR; + } + for (int32_t i = 0; i < UPRV_LENGTHOF(collAttributes); ++i) { + length = loc.getKeywordValue(collAttributes[i].name, value, UPRV_LENGTHOF(value), errorCode); + if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (length == 0) { continue; } + for (int32_t j = 0;; ++j) { + if (j == UPRV_LENGTHOF(collAttributeValues)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (uprv_stricmp(value, collAttributeValues[j].name) == 0) { + coll.setAttribute(collAttributes[i].attr, collAttributeValues[j].value, errorCode); + break; + } + } + } + length = loc.getKeywordValue("colReorder", value, UPRV_LENGTHOF(value), errorCode); + if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (length != 0) { + int32_t codes[USCRIPT_CODE_LIMIT + (UCOL_REORDER_CODE_LIMIT - UCOL_REORDER_CODE_FIRST)]; + int32_t codesLength = 0; + char *scriptName = value; + for (;;) { + if (codesLength == UPRV_LENGTHOF(codes)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + char *limit = scriptName; + char c; + while ((c = *limit) != 0 && c != '-') { ++limit; } + *limit = 0; + int32_t code; + if ((limit - scriptName) == 4) { + // Strict parsing, accept only 4-letter script codes, not long names. + code = u_getPropertyValueEnum(UCHAR_SCRIPT, scriptName); + } else { + code = getReorderCode(scriptName); + } + if (code < 0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + codes[codesLength++] = code; + if (c == 0) { break; } + scriptName = limit + 1; + } + coll.setReorderCodes(codes, codesLength, errorCode); + } + length = loc.getKeywordValue("kv", value, UPRV_LENGTHOF(value), errorCode); + if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (length != 0) { + int32_t code = getReorderCode(value); + if (code < 0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + coll.setMaxVariable((UColReorderCode)code, errorCode); + } + if (U_FAILURE(errorCode)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } +} + +} // namespace + +Collator* U_EXPORT2 Collator::createInstance(UErrorCode& success) +{ + return createInstance(Locale::getDefault(), success); +} + +Collator* U_EXPORT2 Collator::createInstance(const Locale& desiredLocale, + UErrorCode& status) +{ + if (U_FAILURE(status)) + return 0; + if (desiredLocale.isBogus()) { + // Locale constructed from malformed locale ID or language tag. + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + Collator* coll; +#if !UCONFIG_NO_SERVICE + if (hasService()) { + Locale actualLoc; + coll = (Collator*)gService->get(desiredLocale, &actualLoc, status); + } else +#endif + { + coll = makeInstance(desiredLocale, status); + // Either returns nullptr with U_FAILURE(status), or non-nullptr with U_SUCCESS(status) + } + // The use of *coll in setAttributesFromKeywords can cause the nullptr check to be + // optimized out of the delete even though setAttributesFromKeywords returns + // immediately if U_FAILURE(status), so we add a check here. + if (U_FAILURE(status)) { + return nullptr; + } + setAttributesFromKeywords(desiredLocale, *coll, status); + if (U_FAILURE(status)) { + delete coll; + return nullptr; + } + return coll; +} + + +Collator* Collator::makeInstance(const Locale& desiredLocale, UErrorCode& status) { + const CollationCacheEntry *entry = CollationLoader::loadTailoring(desiredLocale, status); + if (U_SUCCESS(status)) { + Collator *result = new RuleBasedCollator(entry); + if (result != nullptr) { + // Both the unified cache's get() and the RBC constructor + // did addRef(). Undo one of them. + entry->removeRef(); + return result; + } + status = U_MEMORY_ALLOCATION_ERROR; + } + if (entry != nullptr) { + // Undo the addRef() from the cache.get(). + entry->removeRef(); + } + return nullptr; +} + +Collator * +Collator::safeClone() const { + return clone(); +} + +// implement deprecated, previously abstract method +Collator::EComparisonResult Collator::compare(const UnicodeString& source, + const UnicodeString& target) const +{ + UErrorCode ec = U_ZERO_ERROR; + return (EComparisonResult)compare(source, target, ec); +} + +// implement deprecated, previously abstract method +Collator::EComparisonResult Collator::compare(const UnicodeString& source, + const UnicodeString& target, + int32_t length) const +{ + UErrorCode ec = U_ZERO_ERROR; + return (EComparisonResult)compare(source, target, length, ec); +} + +// implement deprecated, previously abstract method +Collator::EComparisonResult Collator::compare(const char16_t* source, int32_t sourceLength, + const char16_t* target, int32_t targetLength) + const +{ + UErrorCode ec = U_ZERO_ERROR; + return (EComparisonResult)compare(source, sourceLength, target, targetLength, ec); +} + +UCollationResult Collator::compare(UCharIterator &/*sIter*/, + UCharIterator &/*tIter*/, + UErrorCode &status) const { + if(U_SUCCESS(status)) { + // Not implemented in the base class. + status = U_UNSUPPORTED_ERROR; + } + return UCOL_EQUAL; +} + +UCollationResult Collator::compareUTF8(const StringPiece &source, + const StringPiece &target, + UErrorCode &status) const { + if(U_FAILURE(status)) { + return UCOL_EQUAL; + } + UCharIterator sIter, tIter; + uiter_setUTF8(&sIter, source.data(), source.length()); + uiter_setUTF8(&tIter, target.data(), target.length()); + return compare(sIter, tIter, status); +} + +UBool Collator::equals(const UnicodeString& source, + const UnicodeString& target) const +{ + UErrorCode ec = U_ZERO_ERROR; + return (compare(source, target, ec) == UCOL_EQUAL); +} + +UBool Collator::greaterOrEqual(const UnicodeString& source, + const UnicodeString& target) const +{ + UErrorCode ec = U_ZERO_ERROR; + return (compare(source, target, ec) != UCOL_LESS); +} + +UBool Collator::greater(const UnicodeString& source, + const UnicodeString& target) const +{ + UErrorCode ec = U_ZERO_ERROR; + return (compare(source, target, ec) == UCOL_GREATER); +} + +// this API ignores registered collators, since it returns an +// array of indefinite lifetime +const Locale* U_EXPORT2 Collator::getAvailableLocales(int32_t& count) +{ + UErrorCode status = U_ZERO_ERROR; + Locale *result = nullptr; + count = 0; + if (isAvailableLocaleListInitialized(status)) + { + result = availableLocaleList; + count = availableLocaleListCount; + } + return result; +} + +UnicodeString& U_EXPORT2 Collator::getDisplayName(const Locale& objectLocale, + const Locale& displayLocale, + UnicodeString& name) +{ +#if !UCONFIG_NO_SERVICE + if (hasService()) { + UnicodeString locNameStr; + LocaleUtility::initNameFromLocale(objectLocale, locNameStr); + return gService->getDisplayName(locNameStr, name, displayLocale); + } +#endif + return objectLocale.getDisplayName(displayLocale, name); +} + +UnicodeString& U_EXPORT2 Collator::getDisplayName(const Locale& objectLocale, + UnicodeString& name) +{ + return getDisplayName(objectLocale, Locale::getDefault(), name); +} + +/* This is useless information */ +/*void Collator::getVersion(UVersionInfo versionInfo) const +{ + if (versionInfo!=nullptr) + uprv_memcpy(versionInfo, fVersion, U_MAX_VERSION_LENGTH); +} +*/ + +// UCollator protected constructor destructor ---------------------------- + +/** +* Default constructor. +* Constructor is different from the old default Collator constructor. +* The task for determining the default collation strength and normalization mode +* is left to the child class. +*/ +Collator::Collator() +: UObject() +{ +} + +/** +* Constructor. +* Empty constructor, does not handle the arguments. +* This constructor is done for backward compatibility with 1.7 and 1.8. +* The task for handling the argument collation strength and normalization +* mode is left to the child class. +* @param collationStrength collation strength +* @param decompositionMode +* @deprecated 2.4 use the default constructor instead +*/ +Collator::Collator(UCollationStrength, UNormalizationMode ) +: UObject() +{ +} + +Collator::~Collator() +{ +} + +Collator::Collator(const Collator &other) + : UObject(other) +{ +} + +bool Collator::operator==(const Collator& other) const +{ + // Subclasses: Call this method and then add more specific checks. + return typeid(*this) == typeid(other); +} + +bool Collator::operator!=(const Collator& other) const +{ + return !operator==(other); +} + +int32_t U_EXPORT2 Collator::getBound(const uint8_t *source, + int32_t sourceLength, + UColBoundMode boundType, + uint32_t noOfLevels, + uint8_t *result, + int32_t resultLength, + UErrorCode &status) +{ + return ucol_getBound(source, sourceLength, boundType, noOfLevels, result, resultLength, &status); +} + +void +Collator::setLocales(const Locale& /* requestedLocale */, const Locale& /* validLocale */, const Locale& /*actualLocale*/) { +} + +UnicodeSet *Collator::getTailoredSet(UErrorCode &status) const +{ + if(U_FAILURE(status)) { + return nullptr; + } + // everything can be changed + return new UnicodeSet(0, 0x10FFFF); +} + +// ------------------------------------- + +#if !UCONFIG_NO_SERVICE +URegistryKey U_EXPORT2 +Collator::registerInstance(Collator* toAdopt, const Locale& locale, UErrorCode& status) +{ + if (U_SUCCESS(status)) { + // Set the collator locales while registering so that createInstance() + // need not guess whether the collator's locales are already set properly + // (as they are by the data loader). + toAdopt->setLocales(locale, locale, locale); + return getService()->registerInstance(toAdopt, locale, status); + } + return nullptr; +} + +// ------------------------------------- + +class CFactory : public LocaleKeyFactory { +private: + CollatorFactory* _delegate; + Hashtable* _ids; + +public: + CFactory(CollatorFactory* delegate, UErrorCode& status) + : LocaleKeyFactory(delegate->visible() ? VISIBLE : INVISIBLE) + , _delegate(delegate) + , _ids(nullptr) + { + if (U_SUCCESS(status)) { + int32_t count = 0; + _ids = new Hashtable(status); + if (_ids) { + const UnicodeString * idlist = _delegate->getSupportedIDs(count, status); + for (int i = 0; i < count; ++i) { + _ids->put(idlist[i], (void*)this, status); + if (U_FAILURE(status)) { + delete _ids; + _ids = nullptr; + return; + } + } + } else { + status = U_MEMORY_ALLOCATION_ERROR; + } + } + } + + virtual ~CFactory(); + + virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const override; + +protected: + virtual const Hashtable* getSupportedIDs(UErrorCode& status) const override + { + if (U_SUCCESS(status)) { + return _ids; + } + return nullptr; + } + + virtual UnicodeString& + getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const override; +}; + +CFactory::~CFactory() +{ + delete _delegate; + delete _ids; +} + +UObject* +CFactory::create(const ICUServiceKey& key, const ICUService* /* service */, UErrorCode& status) const +{ + if (handlesKey(key, status)) { + const LocaleKey* lkey = dynamic_cast(&key); + U_ASSERT(lkey != nullptr); + Locale validLoc; + lkey->currentLocale(validLoc); + return _delegate->createCollator(validLoc); + } + return nullptr; +} + +UnicodeString& +CFactory::getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const +{ + if ((_coverage & 0x1) == 0) { + UErrorCode status = U_ZERO_ERROR; + const Hashtable* ids = getSupportedIDs(status); + if (ids && (ids->get(id) != nullptr)) { + Locale loc; + LocaleUtility::initLocaleFromName(id, loc); + return _delegate->getDisplayName(loc, locale, result); + } + } + result.setToBogus(); + return result; +} + +URegistryKey U_EXPORT2 +Collator::registerFactory(CollatorFactory* toAdopt, UErrorCode& status) +{ + if (U_SUCCESS(status)) { + CFactory* f = new CFactory(toAdopt, status); + if (f) { + return getService()->registerFactory(f, status); + } + status = U_MEMORY_ALLOCATION_ERROR; + } + return nullptr; +} + +// ------------------------------------- + +UBool U_EXPORT2 +Collator::unregister(URegistryKey key, UErrorCode& status) +{ + if (U_SUCCESS(status)) { + if (hasService()) { + return gService->unregister(key, status); + } + status = U_ILLEGAL_ARGUMENT_ERROR; + } + return false; +} +#endif /* UCONFIG_NO_SERVICE */ + +class CollationLocaleListEnumeration : public StringEnumeration { +private: + int32_t index; +public: + static UClassID U_EXPORT2 getStaticClassID(); + virtual UClassID getDynamicClassID() const override; +public: + CollationLocaleListEnumeration() + : index(0) + { + // The global variables should already be initialized. + //isAvailableLocaleListInitialized(status); + } + + virtual ~CollationLocaleListEnumeration(); + + virtual StringEnumeration * clone() const override + { + CollationLocaleListEnumeration *result = new CollationLocaleListEnumeration(); + if (result) { + result->index = index; + } + return result; + } + + virtual int32_t count(UErrorCode &/*status*/) const override { + return availableLocaleListCount; + } + + virtual const char* next(int32_t* resultLength, UErrorCode& /*status*/) override { + const char* result; + if(index < availableLocaleListCount) { + result = availableLocaleList[index++].getName(); + if(resultLength != nullptr) { + *resultLength = (int32_t)uprv_strlen(result); + } + } else { + if(resultLength != nullptr) { + *resultLength = 0; + } + result = nullptr; + } + return result; + } + + virtual const UnicodeString* snext(UErrorCode& status) override { + int32_t resultLength = 0; + const char *s = next(&resultLength, status); + return setChars(s, resultLength, status); + } + + virtual void reset(UErrorCode& /*status*/) override { + index = 0; + } +}; + +CollationLocaleListEnumeration::~CollationLocaleListEnumeration() {} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CollationLocaleListEnumeration) + + +// ------------------------------------- + +StringEnumeration* U_EXPORT2 +Collator::getAvailableLocales() +{ +#if !UCONFIG_NO_SERVICE + if (hasService()) { + return getService()->getAvailableLocales(); + } +#endif /* UCONFIG_NO_SERVICE */ + UErrorCode status = U_ZERO_ERROR; + if (isAvailableLocaleListInitialized(status)) { + return new CollationLocaleListEnumeration(); + } + return nullptr; +} + +StringEnumeration* U_EXPORT2 +Collator::getKeywords(UErrorCode& status) { + return UStringEnumeration::fromUEnumeration( + ucol_getKeywords(&status), status); +} + +StringEnumeration* U_EXPORT2 +Collator::getKeywordValues(const char *keyword, UErrorCode& status) { + return UStringEnumeration::fromUEnumeration( + ucol_getKeywordValues(keyword, &status), status); +} + +StringEnumeration* U_EXPORT2 +Collator::getKeywordValuesForLocale(const char* key, const Locale& locale, + UBool commonlyUsed, UErrorCode& status) { + return UStringEnumeration::fromUEnumeration( + ucol_getKeywordValuesForLocale( + key, locale.getName(), commonlyUsed, &status), + status); +} + +Locale U_EXPORT2 +Collator::getFunctionalEquivalent(const char* keyword, const Locale& locale, + UBool& isAvailable, UErrorCode& status) { + // This is a wrapper over ucol_getFunctionalEquivalent + char loc[ULOC_FULLNAME_CAPACITY]; + /*int32_t len =*/ ucol_getFunctionalEquivalent(loc, sizeof(loc), + keyword, locale.getName(), &isAvailable, &status); + if (U_FAILURE(status)) { + *loc = 0; // root + } + return Locale::createFromName(loc); +} + +Collator::ECollationStrength +Collator::getStrength() const { + UErrorCode intStatus = U_ZERO_ERROR; + return (ECollationStrength)getAttribute(UCOL_STRENGTH, intStatus); +} + +void +Collator::setStrength(ECollationStrength newStrength) { + UErrorCode intStatus = U_ZERO_ERROR; + setAttribute(UCOL_STRENGTH, (UColAttributeValue)newStrength, intStatus); +} + +Collator & +Collator::setMaxVariable(UColReorderCode /*group*/, UErrorCode &errorCode) { + if (U_SUCCESS(errorCode)) { + errorCode = U_UNSUPPORTED_ERROR; + } + return *this; +} + +UColReorderCode +Collator::getMaxVariable() const { + return UCOL_REORDER_CODE_PUNCTUATION; +} + +int32_t +Collator::getReorderCodes(int32_t* /* dest*/, + int32_t /* destCapacity*/, + UErrorCode& status) const +{ + if (U_SUCCESS(status)) { + status = U_UNSUPPORTED_ERROR; + } + return 0; +} + +void +Collator::setReorderCodes(const int32_t* /* reorderCodes */, + int32_t /* reorderCodesLength */, + UErrorCode& status) +{ + if (U_SUCCESS(status)) { + status = U_UNSUPPORTED_ERROR; + } +} + +int32_t +Collator::getEquivalentReorderCodes(int32_t reorderCode, + int32_t *dest, int32_t capacity, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + if(capacity < 0 || (dest == nullptr && capacity > 0)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + const CollationData *baseData = CollationRoot::getData(errorCode); + if(U_FAILURE(errorCode)) { return 0; } + return baseData->getEquivalentScripts(reorderCode, dest, capacity, errorCode); +} + +int32_t +Collator::internalGetShortDefinitionString(const char * /*locale*/, + char * /*buffer*/, + int32_t /*capacity*/, + UErrorCode &status) const { + if(U_SUCCESS(status)) { + status = U_UNSUPPORTED_ERROR; /* Shouldn't happen, internal function */ + } + return 0; +} + +UCollationResult +Collator::internalCompareUTF8(const char *left, int32_t leftLength, + const char *right, int32_t rightLength, + UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } + if((left == nullptr && leftLength != 0) || (right == nullptr && rightLength != 0)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return UCOL_EQUAL; + } + return compareUTF8( + StringPiece(left, (leftLength < 0) ? static_cast(uprv_strlen(left)) : leftLength), + StringPiece(right, (rightLength < 0) ? static_cast(uprv_strlen(right)) : rightLength), + errorCode); +} + +int32_t +Collator::internalNextSortKeyPart(UCharIterator * /*iter*/, uint32_t /*state*/[2], + uint8_t * /*dest*/, int32_t /*count*/, UErrorCode &errorCode) const { + if (U_SUCCESS(errorCode)) { + errorCode = U_UNSUPPORTED_ERROR; + } + return 0; +} + +// UCollator private data members ---------------------------------------- + +/* This is useless information */ +/*const UVersionInfo Collator::fVersion = {1, 1, 0, 0};*/ + +// ------------------------------------- + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_COLLATION */ + +/* eof */ diff --git a/deps/icu-small/source/i18n/collation.cpp b/deps/icu-small/source/i18n/collation.cpp index 705ee12e23cc3b..8fec7621dafd05 100644 --- a/deps/icu-small/source/i18n/collation.cpp +++ b/deps/icu-small/source/i18n/collation.cpp @@ -1,141 +1,141 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collation.cpp -* -* created on: 2010oct27 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "collation.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - -uint32_t -Collation::incTwoBytePrimaryByOffset(uint32_t basePrimary, UBool isCompressible, int32_t offset) { - // Extract the second byte, minus the minimum byte value, - // plus the offset, modulo the number of usable byte values, plus the minimum. - // Reserve the PRIMARY_COMPRESSION_LOW_BYTE and high byte if necessary. - uint32_t primary; - if(isCompressible) { - offset += ((int32_t)(basePrimary >> 16) & 0xff) - 4; - primary = (uint32_t)((offset % 251) + 4) << 16; - offset /= 251; - } else { - offset += ((int32_t)(basePrimary >> 16) & 0xff) - 2; - primary = (uint32_t)((offset % 254) + 2) << 16; - offset /= 254; - } - // First byte, assume no further overflow. - return primary | ((basePrimary & 0xff000000) + (uint32_t)(offset << 24)); -} - -uint32_t -Collation::incThreeBytePrimaryByOffset(uint32_t basePrimary, UBool isCompressible, int32_t offset) { - // Extract the third byte, minus the minimum byte value, - // plus the offset, modulo the number of usable byte values, plus the minimum. - offset += ((int32_t)(basePrimary >> 8) & 0xff) - 2; - uint32_t primary = (uint32_t)((offset % 254) + 2) << 8; - offset /= 254; - // Same with the second byte, - // but reserve the PRIMARY_COMPRESSION_LOW_BYTE and high byte if necessary. - if(isCompressible) { - offset += ((int32_t)(basePrimary >> 16) & 0xff) - 4; - primary |= (uint32_t)((offset % 251) + 4) << 16; - offset /= 251; - } else { - offset += ((int32_t)(basePrimary >> 16) & 0xff) - 2; - primary |= (uint32_t)((offset % 254) + 2) << 16; - offset /= 254; - } - // First byte, assume no further overflow. - return primary | ((basePrimary & 0xff000000) + (uint32_t)(offset << 24)); -} - -uint32_t -Collation::decTwoBytePrimaryByOneStep(uint32_t basePrimary, UBool isCompressible, int32_t step) { - // Extract the second byte, minus the minimum byte value, - // minus the step, modulo the number of usable byte values, plus the minimum. - // Reserve the PRIMARY_COMPRESSION_LOW_BYTE and high byte if necessary. - // Assume no further underflow for the first byte. - U_ASSERT(0 < step && step <= 0x7f); - int32_t byte2 = ((int32_t)(basePrimary >> 16) & 0xff) - step; - if(isCompressible) { - if(byte2 < 4) { - byte2 += 251; - basePrimary -= 0x1000000; - } - } else { - if(byte2 < 2) { - byte2 += 254; - basePrimary -= 0x1000000; - } - } - return (basePrimary & 0xff000000) | ((uint32_t)byte2 << 16); -} - -uint32_t -Collation::decThreeBytePrimaryByOneStep(uint32_t basePrimary, UBool isCompressible, int32_t step) { - // Extract the third byte, minus the minimum byte value, - // minus the step, modulo the number of usable byte values, plus the minimum. - U_ASSERT(0 < step && step <= 0x7f); - int32_t byte3 = ((int32_t)(basePrimary >> 8) & 0xff) - step; - if(byte3 >= 2) { - return (basePrimary & 0xffff0000) | ((uint32_t)byte3 << 8); - } - byte3 += 254; - // Same with the second byte, - // but reserve the PRIMARY_COMPRESSION_LOW_BYTE and high byte if necessary. - int32_t byte2 = ((int32_t)(basePrimary >> 16) & 0xff) - 1; - if(isCompressible) { - if(byte2 < 4) { - byte2 = 0xfe; - basePrimary -= 0x1000000; - } - } else { - if(byte2 < 2) { - byte2 = 0xff; - basePrimary -= 0x1000000; - } - } - // First byte, assume no further underflow. - return (basePrimary & 0xff000000) | ((uint32_t)byte2 << 16) | ((uint32_t)byte3 << 8); -} - -uint32_t -Collation::getThreeBytePrimaryForOffsetData(UChar32 c, int64_t dataCE) { - uint32_t p = (uint32_t)(dataCE >> 32); // three-byte primary pppppp00 - int32_t lower32 = (int32_t)dataCE; // base code point b & step s: bbbbbbss (bit 7: isCompressible) - int32_t offset = (c - (lower32 >> 8)) * (lower32 & 0x7f); // delta * increment - UBool isCompressible = (lower32 & 0x80) != 0; - return Collation::incThreeBytePrimaryByOffset(p, isCompressible, offset); -} - -uint32_t -Collation::unassignedPrimaryFromCodePoint(UChar32 c) { - // Create a gap before U+0000. Use c=-1 for [first unassigned]. - ++c; - // Fourth byte: 18 values, every 14th byte value (gap of 13). - uint32_t primary = 2 + (c % 18) * 14; - c /= 18; - // Third byte: 254 values. - primary |= (2 + (c % 254)) << 8; - c /= 254; - // Second byte: 251 values 04..FE excluding the primary compression bytes. - primary |= (4 + (c % 251)) << 16; - // One lead byte covers all code points (c < 0x1182B4 = 1*251*254*18). - return primary | (UNASSIGNED_IMPLICIT_BYTE << 24); -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collation.cpp +* +* created on: 2010oct27 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "collation.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +uint32_t +Collation::incTwoBytePrimaryByOffset(uint32_t basePrimary, UBool isCompressible, int32_t offset) { + // Extract the second byte, minus the minimum byte value, + // plus the offset, modulo the number of usable byte values, plus the minimum. + // Reserve the PRIMARY_COMPRESSION_LOW_BYTE and high byte if necessary. + uint32_t primary; + if(isCompressible) { + offset += ((int32_t)(basePrimary >> 16) & 0xff) - 4; + primary = (uint32_t)((offset % 251) + 4) << 16; + offset /= 251; + } else { + offset += ((int32_t)(basePrimary >> 16) & 0xff) - 2; + primary = (uint32_t)((offset % 254) + 2) << 16; + offset /= 254; + } + // First byte, assume no further overflow. + return primary | ((basePrimary & 0xff000000) + (uint32_t)(offset << 24)); +} + +uint32_t +Collation::incThreeBytePrimaryByOffset(uint32_t basePrimary, UBool isCompressible, int32_t offset) { + // Extract the third byte, minus the minimum byte value, + // plus the offset, modulo the number of usable byte values, plus the minimum. + offset += ((int32_t)(basePrimary >> 8) & 0xff) - 2; + uint32_t primary = (uint32_t)((offset % 254) + 2) << 8; + offset /= 254; + // Same with the second byte, + // but reserve the PRIMARY_COMPRESSION_LOW_BYTE and high byte if necessary. + if(isCompressible) { + offset += ((int32_t)(basePrimary >> 16) & 0xff) - 4; + primary |= (uint32_t)((offset % 251) + 4) << 16; + offset /= 251; + } else { + offset += ((int32_t)(basePrimary >> 16) & 0xff) - 2; + primary |= (uint32_t)((offset % 254) + 2) << 16; + offset /= 254; + } + // First byte, assume no further overflow. + return primary | ((basePrimary & 0xff000000) + (uint32_t)(offset << 24)); +} + +uint32_t +Collation::decTwoBytePrimaryByOneStep(uint32_t basePrimary, UBool isCompressible, int32_t step) { + // Extract the second byte, minus the minimum byte value, + // minus the step, modulo the number of usable byte values, plus the minimum. + // Reserve the PRIMARY_COMPRESSION_LOW_BYTE and high byte if necessary. + // Assume no further underflow for the first byte. + U_ASSERT(0 < step && step <= 0x7f); + int32_t byte2 = ((int32_t)(basePrimary >> 16) & 0xff) - step; + if(isCompressible) { + if(byte2 < 4) { + byte2 += 251; + basePrimary -= 0x1000000; + } + } else { + if(byte2 < 2) { + byte2 += 254; + basePrimary -= 0x1000000; + } + } + return (basePrimary & 0xff000000) | ((uint32_t)byte2 << 16); +} + +uint32_t +Collation::decThreeBytePrimaryByOneStep(uint32_t basePrimary, UBool isCompressible, int32_t step) { + // Extract the third byte, minus the minimum byte value, + // minus the step, modulo the number of usable byte values, plus the minimum. + U_ASSERT(0 < step && step <= 0x7f); + int32_t byte3 = ((int32_t)(basePrimary >> 8) & 0xff) - step; + if(byte3 >= 2) { + return (basePrimary & 0xffff0000) | ((uint32_t)byte3 << 8); + } + byte3 += 254; + // Same with the second byte, + // but reserve the PRIMARY_COMPRESSION_LOW_BYTE and high byte if necessary. + int32_t byte2 = ((int32_t)(basePrimary >> 16) & 0xff) - 1; + if(isCompressible) { + if(byte2 < 4) { + byte2 = 0xfe; + basePrimary -= 0x1000000; + } + } else { + if(byte2 < 2) { + byte2 = 0xff; + basePrimary -= 0x1000000; + } + } + // First byte, assume no further underflow. + return (basePrimary & 0xff000000) | ((uint32_t)byte2 << 16) | ((uint32_t)byte3 << 8); +} + +uint32_t +Collation::getThreeBytePrimaryForOffsetData(UChar32 c, int64_t dataCE) { + uint32_t p = (uint32_t)(dataCE >> 32); // three-byte primary pppppp00 + int32_t lower32 = (int32_t)dataCE; // base code point b & step s: bbbbbbss (bit 7: isCompressible) + int32_t offset = (c - (lower32 >> 8)) * (lower32 & 0x7f); // delta * increment + UBool isCompressible = (lower32 & 0x80) != 0; + return Collation::incThreeBytePrimaryByOffset(p, isCompressible, offset); +} + +uint32_t +Collation::unassignedPrimaryFromCodePoint(UChar32 c) { + // Create a gap before U+0000. Use c=-1 for [first unassigned]. + ++c; + // Fourth byte: 18 values, every 14th byte value (gap of 13). + uint32_t primary = 2 + (c % 18) * 14; + c /= 18; + // Third byte: 254 values. + primary |= (2 + (c % 254)) << 8; + c /= 254; + // Second byte: 251 values 04..FE excluding the primary compression bytes. + primary |= (4 + (c % 251)) << 16; + // One lead byte covers all code points (c < 0x1182B4 = 1*251*254*18). + return primary | (UNASSIGNED_IMPLICIT_BYTE << 24); +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collation.h b/deps/icu-small/source/i18n/collation.h index 2062ef29467341..974ee0a038aa81 100644 --- a/deps/icu-small/source/i18n/collation.h +++ b/deps/icu-small/source/i18n/collation.h @@ -1,503 +1,503 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collation.h -* -* created on: 2010oct27 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATION_H__ -#define __COLLATION_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -U_NAMESPACE_BEGIN - -/** - * Collation v2 basic definitions and static helper functions. - * - * Data structures except for expansion tables store 32-bit CEs which are - * either specials (see tags below) or are compact forms of 64-bit CEs. - */ -class U_I18N_API Collation { -public: - // Special sort key bytes for all levels. - static const uint8_t TERMINATOR_BYTE = 0; - static const uint8_t LEVEL_SEPARATOR_BYTE = 1; - - /** The secondary/tertiary lower limit for tailoring before any root elements. */ - static const uint32_t BEFORE_WEIGHT16 = 0x0100; - - /** - * Merge-sort-key separator. - * Same as the unique primary and identical-level weights of U+FFFE. - * Must not be used as primary compression low terminator. - * Otherwise usable. - */ - static const uint8_t MERGE_SEPARATOR_BYTE = 2; - static const uint32_t MERGE_SEPARATOR_PRIMARY = 0x02000000; // U+FFFE - static const uint32_t MERGE_SEPARATOR_CE32 = 0x02000505; // U+FFFE - - /** - * Primary compression low terminator, must be greater than MERGE_SEPARATOR_BYTE. - * Reserved value in primary second byte if the lead byte is compressible. - * Otherwise usable in all CE weight bytes. - */ - static const uint8_t PRIMARY_COMPRESSION_LOW_BYTE = 3; - /** - * Primary compression high terminator. - * Reserved value in primary second byte if the lead byte is compressible. - * Otherwise usable in all CE weight bytes. - */ - static const uint8_t PRIMARY_COMPRESSION_HIGH_BYTE = 0xff; - - /** Default secondary/tertiary weight lead byte. */ - static const uint8_t COMMON_BYTE = 5; - static const uint32_t COMMON_WEIGHT16 = 0x0500; - /** Middle 16 bits of a CE with a common secondary weight. */ - static const uint32_t COMMON_SECONDARY_CE = 0x05000000; - /** Lower 16 bits of a CE with a common tertiary weight. */ - static const uint32_t COMMON_TERTIARY_CE = 0x0500; - /** Lower 32 bits of a CE with common secondary and tertiary weights. */ - static const uint32_t COMMON_SEC_AND_TER_CE = 0x05000500; - - static const uint32_t SECONDARY_MASK = 0xffff0000; - static const uint32_t CASE_MASK = 0xc000; - static const uint32_t SECONDARY_AND_CASE_MASK = SECONDARY_MASK | CASE_MASK; - /** Only the 2*6 bits for the pure tertiary weight. */ - static const uint32_t ONLY_TERTIARY_MASK = 0x3f3f; - /** Only the secondary & tertiary bits; no case, no quaternary. */ - static const uint32_t ONLY_SEC_TER_MASK = SECONDARY_MASK | ONLY_TERTIARY_MASK; - /** Case bits and tertiary bits. */ - static const uint32_t CASE_AND_TERTIARY_MASK = CASE_MASK | ONLY_TERTIARY_MASK; - static const uint32_t QUATERNARY_MASK = 0xc0; - /** Case bits and quaternary bits. */ - static const uint32_t CASE_AND_QUATERNARY_MASK = CASE_MASK | QUATERNARY_MASK; - - static const uint8_t UNASSIGNED_IMPLICIT_BYTE = 0xfe; // compressible - /** - * First unassigned: AlphabeticIndex overflow boundary. - * We want a 3-byte primary so that it fits into the root elements table. - * - * This 3-byte primary will not collide with - * any unassigned-implicit 4-byte primaries because - * the first few hundred Unicode code points all have real mappings. - */ - static const uint32_t FIRST_UNASSIGNED_PRIMARY = 0xfe040200; - - static const uint8_t TRAIL_WEIGHT_BYTE = 0xff; // not compressible - static const uint32_t FIRST_TRAILING_PRIMARY = 0xff020200; // [first trailing] - static const uint32_t MAX_PRIMARY = 0xffff0000; // U+FFFF - static const uint32_t MAX_REGULAR_CE32 = 0xffff0505; // U+FFFF - - // CE32 value for U+FFFD as well as illegal UTF-8 byte sequences (which behave like U+FFFD). - // We use the third-highest primary weight for U+FFFD (as in UCA 6.3+). - static const uint32_t FFFD_PRIMARY = MAX_PRIMARY - 0x20000; - static const uint32_t FFFD_CE32 = MAX_REGULAR_CE32 - 0x20000; - - /** - * A CE32 is special if its low byte is this or greater. - * Impossible case bits 11 mark special CE32s. - * This value itself is used to indicate a fallback to the base collator. - */ - static const uint8_t SPECIAL_CE32_LOW_BYTE = 0xc0; - static const uint32_t FALLBACK_CE32 = SPECIAL_CE32_LOW_BYTE; - /** - * Low byte of a long-primary special CE32. - */ - static const uint8_t LONG_PRIMARY_CE32_LOW_BYTE = 0xc1; // SPECIAL_CE32_LOW_BYTE | LONG_PRIMARY_TAG - - static const uint32_t UNASSIGNED_CE32 = 0xffffffff; // Compute an unassigned-implicit CE. - - static const uint32_t NO_CE32 = 1; - - /** No CE: End of input. Only used in runtime code, not stored in data. */ - static const uint32_t NO_CE_PRIMARY = 1; // not a left-adjusted weight - static const uint32_t NO_CE_WEIGHT16 = 0x0100; // weight of LEVEL_SEPARATOR_BYTE - static const int64_t NO_CE = INT64_C(0x101000100); // NO_CE_PRIMARY, NO_CE_WEIGHT16, NO_CE_WEIGHT16 - - /** Sort key levels. */ - enum Level { - /** Unspecified level. */ - NO_LEVEL, - PRIMARY_LEVEL, - SECONDARY_LEVEL, - CASE_LEVEL, - TERTIARY_LEVEL, - QUATERNARY_LEVEL, - IDENTICAL_LEVEL, - /** Beyond sort key bytes. */ - ZERO_LEVEL - }; - - /** - * Sort key level flags: xx_FLAG = 1 << xx_LEVEL. - * In Java, use enum Level with flag() getters, or use EnumSet rather than hand-made bit sets. - */ - static const uint32_t NO_LEVEL_FLAG = 1; - static const uint32_t PRIMARY_LEVEL_FLAG = 2; - static const uint32_t SECONDARY_LEVEL_FLAG = 4; - static const uint32_t CASE_LEVEL_FLAG = 8; - static const uint32_t TERTIARY_LEVEL_FLAG = 0x10; - static const uint32_t QUATERNARY_LEVEL_FLAG = 0x20; - static const uint32_t IDENTICAL_LEVEL_FLAG = 0x40; - static const uint32_t ZERO_LEVEL_FLAG = 0x80; - - /** - * Special-CE32 tags, from bits 3..0 of a special 32-bit CE. - * Bits 31..8 are available for tag-specific data. - * Bits 5..4: Reserved. May be used in the future to indicate lccc!=0 and tccc!=0. - */ - enum { - /** - * Fall back to the base collator. - * This is the tag value in SPECIAL_CE32_LOW_BYTE and FALLBACK_CE32. - * Bits 31..8: Unused, 0. - */ - FALLBACK_TAG = 0, - /** - * Long-primary CE with COMMON_SEC_AND_TER_CE. - * Bits 31..8: Three-byte primary. - */ - LONG_PRIMARY_TAG = 1, - /** - * Long-secondary CE with zero primary. - * Bits 31..16: Secondary weight. - * Bits 15.. 8: Tertiary weight. - */ - LONG_SECONDARY_TAG = 2, - /** - * Unused. - * May be used in the future for single-byte secondary CEs (SHORT_SECONDARY_TAG), - * storing the secondary in bits 31..24, the ccc in bits 23..16, - * and the tertiary in bits 15..8. - */ - RESERVED_TAG_3 = 3, - /** - * Latin mini expansions of two simple CEs [pp, 05, tt] [00, ss, 05]. - * Bits 31..24: Single-byte primary weight pp of the first CE. - * Bits 23..16: Tertiary weight tt of the first CE. - * Bits 15.. 8: Secondary weight ss of the second CE. - */ - LATIN_EXPANSION_TAG = 4, - /** - * Points to one or more simple/long-primary/long-secondary 32-bit CE32s. - * Bits 31..13: Index into uint32_t table. - * Bits 12.. 8: Length=1..31. - */ - EXPANSION32_TAG = 5, - /** - * Points to one or more 64-bit CEs. - * Bits 31..13: Index into CE table. - * Bits 12.. 8: Length=1..31. - */ - EXPANSION_TAG = 6, - /** - * Builder data, used only in the CollationDataBuilder, not in runtime data. - * - * If bit 8 is 0: Builder context, points to a list of context-sensitive mappings. - * Bits 31..13: Index to the builder's list of ConditionalCE32 for this character. - * Bits 12.. 9: Unused, 0. - * - * If bit 8 is 1 (IS_BUILDER_JAMO_CE32): Builder-only jamoCE32 value. - * The builder fetches the Jamo CE32 from the trie. - * Bits 31..13: Jamo code point. - * Bits 12.. 9: Unused, 0. - */ - BUILDER_DATA_TAG = 7, - /** - * Points to prefix trie. - * Bits 31..13: Index into prefix/contraction data. - * Bits 12.. 8: Unused, 0. - */ - PREFIX_TAG = 8, - /** - * Points to contraction data. - * Bits 31..13: Index into prefix/contraction data. - * Bit 12: Unused, 0. - * Bit 11: CONTRACT_HAS_STARTER flag. (Used by ICU4X only.) - * Bit 10: CONTRACT_TRAILING_CCC flag. - * Bit 9: CONTRACT_NEXT_CCC flag. - * Bit 8: CONTRACT_SINGLE_CP_NO_MATCH flag. - */ - CONTRACTION_TAG = 9, - /** - * Decimal digit. - * Bits 31..13: Index into uint32_t table for non-numeric-collation CE32. - * Bit 12: Unused, 0. - * Bits 11.. 8: Digit value 0..9. - */ - DIGIT_TAG = 10, - /** - * Tag for U+0000, for moving the NUL-termination handling - * from the regular fastpath into specials-handling code. - * Bits 31..8: Unused, 0. - */ - U0000_TAG = 11, - /** - * Tag for a Hangul syllable. - * Bits 31..9: Unused, 0. - * Bit 8: HANGUL_NO_SPECIAL_JAMO flag. - */ - HANGUL_TAG = 12, - /** - * Tag for a lead surrogate code unit. - * Optional optimization for UTF-16 string processing. - * Bits 31..10: Unused, 0. - * 9.. 8: =0: All associated supplementary code points are unassigned-implicit. - * =1: All associated supplementary code points fall back to the base data. - * else: (Normally 2) Look up the data for the supplementary code point. - */ - LEAD_SURROGATE_TAG = 13, - /** - * Tag for CEs with primary weights in code point order. - * Bits 31..13: Index into CE table, for one data "CE". - * Bits 12.. 8: Unused, 0. - * - * This data "CE" has the following bit fields: - * Bits 63..32: Three-byte primary pppppp00. - * 31.. 8: Start/base code point of the in-order range. - * 7: Flag isCompressible primary. - * 6.. 0: Per-code point primary-weight increment. - */ - OFFSET_TAG = 14, - /** - * Implicit CE tag. Compute an unassigned-implicit CE. - * All bits are set (UNASSIGNED_CE32=0xffffffff). - */ - IMPLICIT_TAG = 15 - }; - - static UBool isAssignedCE32(uint32_t ce32) { - return ce32 != FALLBACK_CE32 && ce32 != UNASSIGNED_CE32; - } - - /** - * We limit the number of CEs in an expansion - * so that we can use a small number of length bits in the data structure, - * and so that an implementation can copy CEs at runtime without growing a destination buffer. - */ - static const int32_t MAX_EXPANSION_LENGTH = 31; - static const int32_t MAX_INDEX = 0x7ffff; - - /** - * Set if there is no match for the single (no-suffix) character itself. - * This is only possible if there is a prefix. - * In this case, discontiguous contraction matching cannot add combining marks - * starting from an empty suffix. - * The default CE32 is used anyway if there is no suffix match. - */ - static const uint32_t CONTRACT_SINGLE_CP_NO_MATCH = 0x100; - /** Set if the first character of every contraction suffix has lccc!=0. */ - static const uint32_t CONTRACT_NEXT_CCC = 0x200; - /** Set if any contraction suffix ends with lccc!=0. */ - static const uint32_t CONTRACT_TRAILING_CCC = 0x400; - /** Set if any contraction suffix contains a starter. (Used by ICU4X only.) */ - static const uint32_t CONTRACT_HAS_STARTER = 0x800; - - /** For HANGUL_TAG: None of its Jamo CE32s isSpecialCE32(). */ - static const uint32_t HANGUL_NO_SPECIAL_JAMO = 0x100; - - static const uint32_t LEAD_ALL_UNASSIGNED = 0; - static const uint32_t LEAD_ALL_FALLBACK = 0x100; - static const uint32_t LEAD_MIXED = 0x200; - static const uint32_t LEAD_TYPE_MASK = 0x300; - - static uint32_t makeLongPrimaryCE32(uint32_t p) { return p | LONG_PRIMARY_CE32_LOW_BYTE; } - - /** Turns the long-primary CE32 into a primary weight pppppp00. */ - static inline uint32_t primaryFromLongPrimaryCE32(uint32_t ce32) { - return ce32 & 0xffffff00; - } - static inline int64_t ceFromLongPrimaryCE32(uint32_t ce32) { - return ((int64_t)(ce32 & 0xffffff00) << 32) | COMMON_SEC_AND_TER_CE; - } - - static uint32_t makeLongSecondaryCE32(uint32_t lower32) { - return lower32 | SPECIAL_CE32_LOW_BYTE | LONG_SECONDARY_TAG; - } - static inline int64_t ceFromLongSecondaryCE32(uint32_t ce32) { - return ce32 & 0xffffff00; - } - - /** Makes a special CE32 with tag, index and length. */ - static uint32_t makeCE32FromTagIndexAndLength(int32_t tag, int32_t index, int32_t length) { - return (index << 13) | (length << 8) | SPECIAL_CE32_LOW_BYTE | tag; - } - /** Makes a special CE32 with only tag and index. */ - static uint32_t makeCE32FromTagAndIndex(int32_t tag, int32_t index) { - return (index << 13) | SPECIAL_CE32_LOW_BYTE | tag; - } - - static inline UBool isSpecialCE32(uint32_t ce32) { - return (ce32 & 0xff) >= SPECIAL_CE32_LOW_BYTE; - } - - static inline int32_t tagFromCE32(uint32_t ce32) { - return (int32_t)(ce32 & 0xf); - } - - static inline UBool hasCE32Tag(uint32_t ce32, int32_t tag) { - return isSpecialCE32(ce32) && tagFromCE32(ce32) == tag; - } - - static inline UBool isLongPrimaryCE32(uint32_t ce32) { - return hasCE32Tag(ce32, LONG_PRIMARY_TAG); - } - - static UBool isSimpleOrLongCE32(uint32_t ce32) { - return !isSpecialCE32(ce32) || - tagFromCE32(ce32) == LONG_PRIMARY_TAG || - tagFromCE32(ce32) == LONG_SECONDARY_TAG; - } - - /** - * @return true if the ce32 yields one or more CEs without further data lookups - */ - static UBool isSelfContainedCE32(uint32_t ce32) { - return !isSpecialCE32(ce32) || - tagFromCE32(ce32) == LONG_PRIMARY_TAG || - tagFromCE32(ce32) == LONG_SECONDARY_TAG || - tagFromCE32(ce32) == LATIN_EXPANSION_TAG; - } - - static inline UBool isPrefixCE32(uint32_t ce32) { - return hasCE32Tag(ce32, PREFIX_TAG); - } - - static inline UBool isContractionCE32(uint32_t ce32) { - return hasCE32Tag(ce32, CONTRACTION_TAG); - } - - static inline UBool ce32HasContext(uint32_t ce32) { - return isSpecialCE32(ce32) && - (tagFromCE32(ce32) == PREFIX_TAG || - tagFromCE32(ce32) == CONTRACTION_TAG); - } - - /** - * Get the first of the two Latin-expansion CEs encoded in ce32. - * @see LATIN_EXPANSION_TAG - */ - static inline int64_t latinCE0FromCE32(uint32_t ce32) { - return ((int64_t)(ce32 & 0xff000000) << 32) | COMMON_SECONDARY_CE | ((ce32 & 0xff0000) >> 8); - } - - /** - * Get the second of the two Latin-expansion CEs encoded in ce32. - * @see LATIN_EXPANSION_TAG - */ - static inline int64_t latinCE1FromCE32(uint32_t ce32) { - return ((ce32 & 0xff00) << 16) | COMMON_TERTIARY_CE; - } - - /** - * Returns the data index from a special CE32. - */ - static inline int32_t indexFromCE32(uint32_t ce32) { - return (int32_t)(ce32 >> 13); - } - - /** - * Returns the data length from a ce32. - */ - static inline int32_t lengthFromCE32(uint32_t ce32) { - return (ce32 >> 8) & 31; - } - - /** - * Returns the digit value from a DIGIT_TAG ce32. - */ - static inline char digitFromCE32(uint32_t ce32) { - return (char)((ce32 >> 8) & 0xf); - } - - /** Returns a 64-bit CE from a simple CE32 (not special). */ - static inline int64_t ceFromSimpleCE32(uint32_t ce32) { - // normal form ppppsstt -> pppp0000ss00tt00 - // assert (ce32 & 0xff) < SPECIAL_CE32_LOW_BYTE - return ((int64_t)(ce32 & 0xffff0000) << 32) | ((ce32 & 0xff00) << 16) | ((ce32 & 0xff) << 8); - } - - /** Returns a 64-bit CE from a simple/long-primary/long-secondary CE32. */ - static inline int64_t ceFromCE32(uint32_t ce32) { - uint32_t tertiary = ce32 & 0xff; - if(tertiary < SPECIAL_CE32_LOW_BYTE) { - // normal form ppppsstt -> pppp0000ss00tt00 - return ((int64_t)(ce32 & 0xffff0000) << 32) | ((ce32 & 0xff00) << 16) | (tertiary << 8); - } else { - ce32 -= tertiary; - if((tertiary & 0xf) == LONG_PRIMARY_TAG) { - // long-primary form ppppppC1 -> pppppp00050000500 - return ((int64_t)ce32 << 32) | COMMON_SEC_AND_TER_CE; - } else { - // long-secondary form ssssttC2 -> 00000000sssstt00 - // assert (tertiary & 0xf) == LONG_SECONDARY_TAG - return ce32; - } - } - } - - /** Creates a CE from a primary weight. */ - static inline int64_t makeCE(uint32_t p) { - return ((int64_t)p << 32) | COMMON_SEC_AND_TER_CE; - } - /** - * Creates a CE from a primary weight, - * 16-bit secondary/tertiary weights, and a 2-bit quaternary. - */ - static inline int64_t makeCE(uint32_t p, uint32_t s, uint32_t t, uint32_t q) { - return ((int64_t)p << 32) | (s << 16) | t | (q << 6); - } - - /** - * Increments a 2-byte primary by a code point offset. - */ - static uint32_t incTwoBytePrimaryByOffset(uint32_t basePrimary, UBool isCompressible, - int32_t offset); - - /** - * Increments a 3-byte primary by a code point offset. - */ - static uint32_t incThreeBytePrimaryByOffset(uint32_t basePrimary, UBool isCompressible, - int32_t offset); - - /** - * Decrements a 2-byte primary by one range step (1..0x7f). - */ - static uint32_t decTwoBytePrimaryByOneStep(uint32_t basePrimary, UBool isCompressible, int32_t step); - - /** - * Decrements a 3-byte primary by one range step (1..0x7f). - */ - static uint32_t decThreeBytePrimaryByOneStep(uint32_t basePrimary, UBool isCompressible, int32_t step); - - /** - * Computes a 3-byte primary for c's OFFSET_TAG data "CE". - */ - static uint32_t getThreeBytePrimaryForOffsetData(UChar32 c, int64_t dataCE); - - /** - * Returns the unassigned-character implicit primary weight for any valid code point c. - */ - static uint32_t unassignedPrimaryFromCodePoint(UChar32 c); - - static inline int64_t unassignedCEFromCodePoint(UChar32 c) { - return makeCE(unassignedPrimaryFromCodePoint(c)); - } - -private: - Collation() = delete; // No instantiation. -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATION_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collation.h +* +* created on: 2010oct27 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATION_H__ +#define __COLLATION_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +U_NAMESPACE_BEGIN + +/** + * Collation v2 basic definitions and static helper functions. + * + * Data structures except for expansion tables store 32-bit CEs which are + * either specials (see tags below) or are compact forms of 64-bit CEs. + */ +class U_I18N_API Collation { +public: + // Special sort key bytes for all levels. + static const uint8_t TERMINATOR_BYTE = 0; + static const uint8_t LEVEL_SEPARATOR_BYTE = 1; + + /** The secondary/tertiary lower limit for tailoring before any root elements. */ + static const uint32_t BEFORE_WEIGHT16 = 0x0100; + + /** + * Merge-sort-key separator. + * Same as the unique primary and identical-level weights of U+FFFE. + * Must not be used as primary compression low terminator. + * Otherwise usable. + */ + static const uint8_t MERGE_SEPARATOR_BYTE = 2; + static const uint32_t MERGE_SEPARATOR_PRIMARY = 0x02000000; // U+FFFE + static const uint32_t MERGE_SEPARATOR_CE32 = 0x02000505; // U+FFFE + + /** + * Primary compression low terminator, must be greater than MERGE_SEPARATOR_BYTE. + * Reserved value in primary second byte if the lead byte is compressible. + * Otherwise usable in all CE weight bytes. + */ + static const uint8_t PRIMARY_COMPRESSION_LOW_BYTE = 3; + /** + * Primary compression high terminator. + * Reserved value in primary second byte if the lead byte is compressible. + * Otherwise usable in all CE weight bytes. + */ + static const uint8_t PRIMARY_COMPRESSION_HIGH_BYTE = 0xff; + + /** Default secondary/tertiary weight lead byte. */ + static const uint8_t COMMON_BYTE = 5; + static const uint32_t COMMON_WEIGHT16 = 0x0500; + /** Middle 16 bits of a CE with a common secondary weight. */ + static const uint32_t COMMON_SECONDARY_CE = 0x05000000; + /** Lower 16 bits of a CE with a common tertiary weight. */ + static const uint32_t COMMON_TERTIARY_CE = 0x0500; + /** Lower 32 bits of a CE with common secondary and tertiary weights. */ + static const uint32_t COMMON_SEC_AND_TER_CE = 0x05000500; + + static const uint32_t SECONDARY_MASK = 0xffff0000; + static const uint32_t CASE_MASK = 0xc000; + static const uint32_t SECONDARY_AND_CASE_MASK = SECONDARY_MASK | CASE_MASK; + /** Only the 2*6 bits for the pure tertiary weight. */ + static const uint32_t ONLY_TERTIARY_MASK = 0x3f3f; + /** Only the secondary & tertiary bits; no case, no quaternary. */ + static const uint32_t ONLY_SEC_TER_MASK = SECONDARY_MASK | ONLY_TERTIARY_MASK; + /** Case bits and tertiary bits. */ + static const uint32_t CASE_AND_TERTIARY_MASK = CASE_MASK | ONLY_TERTIARY_MASK; + static const uint32_t QUATERNARY_MASK = 0xc0; + /** Case bits and quaternary bits. */ + static const uint32_t CASE_AND_QUATERNARY_MASK = CASE_MASK | QUATERNARY_MASK; + + static const uint8_t UNASSIGNED_IMPLICIT_BYTE = 0xfe; // compressible + /** + * First unassigned: AlphabeticIndex overflow boundary. + * We want a 3-byte primary so that it fits into the root elements table. + * + * This 3-byte primary will not collide with + * any unassigned-implicit 4-byte primaries because + * the first few hundred Unicode code points all have real mappings. + */ + static const uint32_t FIRST_UNASSIGNED_PRIMARY = 0xfe040200; + + static const uint8_t TRAIL_WEIGHT_BYTE = 0xff; // not compressible + static const uint32_t FIRST_TRAILING_PRIMARY = 0xff020200; // [first trailing] + static const uint32_t MAX_PRIMARY = 0xffff0000; // U+FFFF + static const uint32_t MAX_REGULAR_CE32 = 0xffff0505; // U+FFFF + + // CE32 value for U+FFFD as well as illegal UTF-8 byte sequences (which behave like U+FFFD). + // We use the third-highest primary weight for U+FFFD (as in UCA 6.3+). + static const uint32_t FFFD_PRIMARY = MAX_PRIMARY - 0x20000; + static const uint32_t FFFD_CE32 = MAX_REGULAR_CE32 - 0x20000; + + /** + * A CE32 is special if its low byte is this or greater. + * Impossible case bits 11 mark special CE32s. + * This value itself is used to indicate a fallback to the base collator. + */ + static const uint8_t SPECIAL_CE32_LOW_BYTE = 0xc0; + static const uint32_t FALLBACK_CE32 = SPECIAL_CE32_LOW_BYTE; + /** + * Low byte of a long-primary special CE32. + */ + static const uint8_t LONG_PRIMARY_CE32_LOW_BYTE = 0xc1; // SPECIAL_CE32_LOW_BYTE | LONG_PRIMARY_TAG + + static const uint32_t UNASSIGNED_CE32 = 0xffffffff; // Compute an unassigned-implicit CE. + + static const uint32_t NO_CE32 = 1; + + /** No CE: End of input. Only used in runtime code, not stored in data. */ + static const uint32_t NO_CE_PRIMARY = 1; // not a left-adjusted weight + static const uint32_t NO_CE_WEIGHT16 = 0x0100; // weight of LEVEL_SEPARATOR_BYTE + static const int64_t NO_CE = INT64_C(0x101000100); // NO_CE_PRIMARY, NO_CE_WEIGHT16, NO_CE_WEIGHT16 + + /** Sort key levels. */ + enum Level { + /** Unspecified level. */ + NO_LEVEL, + PRIMARY_LEVEL, + SECONDARY_LEVEL, + CASE_LEVEL, + TERTIARY_LEVEL, + QUATERNARY_LEVEL, + IDENTICAL_LEVEL, + /** Beyond sort key bytes. */ + ZERO_LEVEL + }; + + /** + * Sort key level flags: xx_FLAG = 1 << xx_LEVEL. + * In Java, use enum Level with flag() getters, or use EnumSet rather than hand-made bit sets. + */ + static const uint32_t NO_LEVEL_FLAG = 1; + static const uint32_t PRIMARY_LEVEL_FLAG = 2; + static const uint32_t SECONDARY_LEVEL_FLAG = 4; + static const uint32_t CASE_LEVEL_FLAG = 8; + static const uint32_t TERTIARY_LEVEL_FLAG = 0x10; + static const uint32_t QUATERNARY_LEVEL_FLAG = 0x20; + static const uint32_t IDENTICAL_LEVEL_FLAG = 0x40; + static const uint32_t ZERO_LEVEL_FLAG = 0x80; + + /** + * Special-CE32 tags, from bits 3..0 of a special 32-bit CE. + * Bits 31..8 are available for tag-specific data. + * Bits 5..4: Reserved. May be used in the future to indicate lccc!=0 and tccc!=0. + */ + enum { + /** + * Fall back to the base collator. + * This is the tag value in SPECIAL_CE32_LOW_BYTE and FALLBACK_CE32. + * Bits 31..8: Unused, 0. + */ + FALLBACK_TAG = 0, + /** + * Long-primary CE with COMMON_SEC_AND_TER_CE. + * Bits 31..8: Three-byte primary. + */ + LONG_PRIMARY_TAG = 1, + /** + * Long-secondary CE with zero primary. + * Bits 31..16: Secondary weight. + * Bits 15.. 8: Tertiary weight. + */ + LONG_SECONDARY_TAG = 2, + /** + * Unused. + * May be used in the future for single-byte secondary CEs (SHORT_SECONDARY_TAG), + * storing the secondary in bits 31..24, the ccc in bits 23..16, + * and the tertiary in bits 15..8. + */ + RESERVED_TAG_3 = 3, + /** + * Latin mini expansions of two simple CEs [pp, 05, tt] [00, ss, 05]. + * Bits 31..24: Single-byte primary weight pp of the first CE. + * Bits 23..16: Tertiary weight tt of the first CE. + * Bits 15.. 8: Secondary weight ss of the second CE. + */ + LATIN_EXPANSION_TAG = 4, + /** + * Points to one or more simple/long-primary/long-secondary 32-bit CE32s. + * Bits 31..13: Index into uint32_t table. + * Bits 12.. 8: Length=1..31. + */ + EXPANSION32_TAG = 5, + /** + * Points to one or more 64-bit CEs. + * Bits 31..13: Index into CE table. + * Bits 12.. 8: Length=1..31. + */ + EXPANSION_TAG = 6, + /** + * Builder data, used only in the CollationDataBuilder, not in runtime data. + * + * If bit 8 is 0: Builder context, points to a list of context-sensitive mappings. + * Bits 31..13: Index to the builder's list of ConditionalCE32 for this character. + * Bits 12.. 9: Unused, 0. + * + * If bit 8 is 1 (IS_BUILDER_JAMO_CE32): Builder-only jamoCE32 value. + * The builder fetches the Jamo CE32 from the trie. + * Bits 31..13: Jamo code point. + * Bits 12.. 9: Unused, 0. + */ + BUILDER_DATA_TAG = 7, + /** + * Points to prefix trie. + * Bits 31..13: Index into prefix/contraction data. + * Bits 12.. 8: Unused, 0. + */ + PREFIX_TAG = 8, + /** + * Points to contraction data. + * Bits 31..13: Index into prefix/contraction data. + * Bit 12: Unused, 0. + * Bit 11: CONTRACT_HAS_STARTER flag. (Used by ICU4X only.) + * Bit 10: CONTRACT_TRAILING_CCC flag. + * Bit 9: CONTRACT_NEXT_CCC flag. + * Bit 8: CONTRACT_SINGLE_CP_NO_MATCH flag. + */ + CONTRACTION_TAG = 9, + /** + * Decimal digit. + * Bits 31..13: Index into uint32_t table for non-numeric-collation CE32. + * Bit 12: Unused, 0. + * Bits 11.. 8: Digit value 0..9. + */ + DIGIT_TAG = 10, + /** + * Tag for U+0000, for moving the NUL-termination handling + * from the regular fastpath into specials-handling code. + * Bits 31..8: Unused, 0. + */ + U0000_TAG = 11, + /** + * Tag for a Hangul syllable. + * Bits 31..9: Unused, 0. + * Bit 8: HANGUL_NO_SPECIAL_JAMO flag. + */ + HANGUL_TAG = 12, + /** + * Tag for a lead surrogate code unit. + * Optional optimization for UTF-16 string processing. + * Bits 31..10: Unused, 0. + * 9.. 8: =0: All associated supplementary code points are unassigned-implicit. + * =1: All associated supplementary code points fall back to the base data. + * else: (Normally 2) Look up the data for the supplementary code point. + */ + LEAD_SURROGATE_TAG = 13, + /** + * Tag for CEs with primary weights in code point order. + * Bits 31..13: Index into CE table, for one data "CE". + * Bits 12.. 8: Unused, 0. + * + * This data "CE" has the following bit fields: + * Bits 63..32: Three-byte primary pppppp00. + * 31.. 8: Start/base code point of the in-order range. + * 7: Flag isCompressible primary. + * 6.. 0: Per-code point primary-weight increment. + */ + OFFSET_TAG = 14, + /** + * Implicit CE tag. Compute an unassigned-implicit CE. + * All bits are set (UNASSIGNED_CE32=0xffffffff). + */ + IMPLICIT_TAG = 15 + }; + + static UBool isAssignedCE32(uint32_t ce32) { + return ce32 != FALLBACK_CE32 && ce32 != UNASSIGNED_CE32; + } + + /** + * We limit the number of CEs in an expansion + * so that we can use a small number of length bits in the data structure, + * and so that an implementation can copy CEs at runtime without growing a destination buffer. + */ + static const int32_t MAX_EXPANSION_LENGTH = 31; + static const int32_t MAX_INDEX = 0x7ffff; + + /** + * Set if there is no match for the single (no-suffix) character itself. + * This is only possible if there is a prefix. + * In this case, discontiguous contraction matching cannot add combining marks + * starting from an empty suffix. + * The default CE32 is used anyway if there is no suffix match. + */ + static const uint32_t CONTRACT_SINGLE_CP_NO_MATCH = 0x100; + /** Set if the first character of every contraction suffix has lccc!=0. */ + static const uint32_t CONTRACT_NEXT_CCC = 0x200; + /** Set if any contraction suffix ends with lccc!=0. */ + static const uint32_t CONTRACT_TRAILING_CCC = 0x400; + /** Set if any contraction suffix contains a starter. (Used by ICU4X only.) */ + static const uint32_t CONTRACT_HAS_STARTER = 0x800; + + /** For HANGUL_TAG: None of its Jamo CE32s isSpecialCE32(). */ + static const uint32_t HANGUL_NO_SPECIAL_JAMO = 0x100; + + static const uint32_t LEAD_ALL_UNASSIGNED = 0; + static const uint32_t LEAD_ALL_FALLBACK = 0x100; + static const uint32_t LEAD_MIXED = 0x200; + static const uint32_t LEAD_TYPE_MASK = 0x300; + + static uint32_t makeLongPrimaryCE32(uint32_t p) { return p | LONG_PRIMARY_CE32_LOW_BYTE; } + + /** Turns the long-primary CE32 into a primary weight pppppp00. */ + static inline uint32_t primaryFromLongPrimaryCE32(uint32_t ce32) { + return ce32 & 0xffffff00; + } + static inline int64_t ceFromLongPrimaryCE32(uint32_t ce32) { + return ((int64_t)(ce32 & 0xffffff00) << 32) | COMMON_SEC_AND_TER_CE; + } + + static uint32_t makeLongSecondaryCE32(uint32_t lower32) { + return lower32 | SPECIAL_CE32_LOW_BYTE | LONG_SECONDARY_TAG; + } + static inline int64_t ceFromLongSecondaryCE32(uint32_t ce32) { + return ce32 & 0xffffff00; + } + + /** Makes a special CE32 with tag, index and length. */ + static uint32_t makeCE32FromTagIndexAndLength(int32_t tag, int32_t index, int32_t length) { + return (index << 13) | (length << 8) | SPECIAL_CE32_LOW_BYTE | tag; + } + /** Makes a special CE32 with only tag and index. */ + static uint32_t makeCE32FromTagAndIndex(int32_t tag, int32_t index) { + return (index << 13) | SPECIAL_CE32_LOW_BYTE | tag; + } + + static inline UBool isSpecialCE32(uint32_t ce32) { + return (ce32 & 0xff) >= SPECIAL_CE32_LOW_BYTE; + } + + static inline int32_t tagFromCE32(uint32_t ce32) { + return (int32_t)(ce32 & 0xf); + } + + static inline UBool hasCE32Tag(uint32_t ce32, int32_t tag) { + return isSpecialCE32(ce32) && tagFromCE32(ce32) == tag; + } + + static inline UBool isLongPrimaryCE32(uint32_t ce32) { + return hasCE32Tag(ce32, LONG_PRIMARY_TAG); + } + + static UBool isSimpleOrLongCE32(uint32_t ce32) { + return !isSpecialCE32(ce32) || + tagFromCE32(ce32) == LONG_PRIMARY_TAG || + tagFromCE32(ce32) == LONG_SECONDARY_TAG; + } + + /** + * @return true if the ce32 yields one or more CEs without further data lookups + */ + static UBool isSelfContainedCE32(uint32_t ce32) { + return !isSpecialCE32(ce32) || + tagFromCE32(ce32) == LONG_PRIMARY_TAG || + tagFromCE32(ce32) == LONG_SECONDARY_TAG || + tagFromCE32(ce32) == LATIN_EXPANSION_TAG; + } + + static inline UBool isPrefixCE32(uint32_t ce32) { + return hasCE32Tag(ce32, PREFIX_TAG); + } + + static inline UBool isContractionCE32(uint32_t ce32) { + return hasCE32Tag(ce32, CONTRACTION_TAG); + } + + static inline UBool ce32HasContext(uint32_t ce32) { + return isSpecialCE32(ce32) && + (tagFromCE32(ce32) == PREFIX_TAG || + tagFromCE32(ce32) == CONTRACTION_TAG); + } + + /** + * Get the first of the two Latin-expansion CEs encoded in ce32. + * @see LATIN_EXPANSION_TAG + */ + static inline int64_t latinCE0FromCE32(uint32_t ce32) { + return ((int64_t)(ce32 & 0xff000000) << 32) | COMMON_SECONDARY_CE | ((ce32 & 0xff0000) >> 8); + } + + /** + * Get the second of the two Latin-expansion CEs encoded in ce32. + * @see LATIN_EXPANSION_TAG + */ + static inline int64_t latinCE1FromCE32(uint32_t ce32) { + return ((ce32 & 0xff00) << 16) | COMMON_TERTIARY_CE; + } + + /** + * Returns the data index from a special CE32. + */ + static inline int32_t indexFromCE32(uint32_t ce32) { + return (int32_t)(ce32 >> 13); + } + + /** + * Returns the data length from a ce32. + */ + static inline int32_t lengthFromCE32(uint32_t ce32) { + return (ce32 >> 8) & 31; + } + + /** + * Returns the digit value from a DIGIT_TAG ce32. + */ + static inline char digitFromCE32(uint32_t ce32) { + return (char)((ce32 >> 8) & 0xf); + } + + /** Returns a 64-bit CE from a simple CE32 (not special). */ + static inline int64_t ceFromSimpleCE32(uint32_t ce32) { + // normal form ppppsstt -> pppp0000ss00tt00 + // assert (ce32 & 0xff) < SPECIAL_CE32_LOW_BYTE + return ((int64_t)(ce32 & 0xffff0000) << 32) | ((ce32 & 0xff00) << 16) | ((ce32 & 0xff) << 8); + } + + /** Returns a 64-bit CE from a simple/long-primary/long-secondary CE32. */ + static inline int64_t ceFromCE32(uint32_t ce32) { + uint32_t tertiary = ce32 & 0xff; + if(tertiary < SPECIAL_CE32_LOW_BYTE) { + // normal form ppppsstt -> pppp0000ss00tt00 + return ((int64_t)(ce32 & 0xffff0000) << 32) | ((ce32 & 0xff00) << 16) | (tertiary << 8); + } else { + ce32 -= tertiary; + if((tertiary & 0xf) == LONG_PRIMARY_TAG) { + // long-primary form ppppppC1 -> pppppp00050000500 + return ((int64_t)ce32 << 32) | COMMON_SEC_AND_TER_CE; + } else { + // long-secondary form ssssttC2 -> 00000000sssstt00 + // assert (tertiary & 0xf) == LONG_SECONDARY_TAG + return ce32; + } + } + } + + /** Creates a CE from a primary weight. */ + static inline int64_t makeCE(uint32_t p) { + return ((int64_t)p << 32) | COMMON_SEC_AND_TER_CE; + } + /** + * Creates a CE from a primary weight, + * 16-bit secondary/tertiary weights, and a 2-bit quaternary. + */ + static inline int64_t makeCE(uint32_t p, uint32_t s, uint32_t t, uint32_t q) { + return ((int64_t)p << 32) | (s << 16) | t | (q << 6); + } + + /** + * Increments a 2-byte primary by a code point offset. + */ + static uint32_t incTwoBytePrimaryByOffset(uint32_t basePrimary, UBool isCompressible, + int32_t offset); + + /** + * Increments a 3-byte primary by a code point offset. + */ + static uint32_t incThreeBytePrimaryByOffset(uint32_t basePrimary, UBool isCompressible, + int32_t offset); + + /** + * Decrements a 2-byte primary by one range step (1..0x7f). + */ + static uint32_t decTwoBytePrimaryByOneStep(uint32_t basePrimary, UBool isCompressible, int32_t step); + + /** + * Decrements a 3-byte primary by one range step (1..0x7f). + */ + static uint32_t decThreeBytePrimaryByOneStep(uint32_t basePrimary, UBool isCompressible, int32_t step); + + /** + * Computes a 3-byte primary for c's OFFSET_TAG data "CE". + */ + static uint32_t getThreeBytePrimaryForOffsetData(UChar32 c, int64_t dataCE); + + /** + * Returns the unassigned-character implicit primary weight for any valid code point c. + */ + static uint32_t unassignedPrimaryFromCodePoint(UChar32 c); + + static inline int64_t unassignedCEFromCodePoint(UChar32 c) { + return makeCE(unassignedPrimaryFromCodePoint(c)); + } + +private: + Collation() = delete; // No instantiation. +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATION_H__ diff --git a/deps/icu-small/source/i18n/collationbuilder.cpp b/deps/icu-small/source/i18n/collationbuilder.cpp index fbf09a313d60db..70a672abae6689 100644 --- a/deps/icu-small/source/i18n/collationbuilder.cpp +++ b/deps/icu-small/source/i18n/collationbuilder.cpp @@ -1,1723 +1,1723 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationbuilder.cpp -* -* (replaced the former ucol_bld.cpp) -* -* created on: 2013may06 -* created by: Markus W. Scherer -*/ - -#ifdef DEBUG_COLLATION_BUILDER -#include -#endif - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/caniter.h" -#include "unicode/normalizer2.h" -#include "unicode/tblcoll.h" -#include "unicode/parseerr.h" -#include "unicode/uchar.h" -#include "unicode/ucol.h" -#include "unicode/unistr.h" -#include "unicode/usetiter.h" -#include "unicode/utf16.h" -#include "unicode/uversion.h" -#include "cmemory.h" -#include "collation.h" -#include "collationbuilder.h" -#include "collationdata.h" -#include "collationdatabuilder.h" -#include "collationfastlatin.h" -#include "collationroot.h" -#include "collationrootelements.h" -#include "collationruleparser.h" -#include "collationsettings.h" -#include "collationtailoring.h" -#include "collationweights.h" -#include "normalizer2impl.h" -#include "uassert.h" -#include "ucol_imp.h" -#include "utf16collationiterator.h" - -U_NAMESPACE_BEGIN - -namespace { - -class BundleImporter : public CollationRuleParser::Importer { -public: - BundleImporter() {} - virtual ~BundleImporter(); - virtual void getRules( - const char *localeID, const char *collationType, - UnicodeString &rules, - const char *&errorReason, UErrorCode &errorCode) override; -}; - -BundleImporter::~BundleImporter() {} - -void -BundleImporter::getRules( - const char *localeID, const char *collationType, - UnicodeString &rules, - const char *& /*errorReason*/, UErrorCode &errorCode) { - CollationLoader::loadRules(localeID, collationType, rules, errorCode); -} - -} // namespace - -// RuleBasedCollator implementation ---------------------------------------- *** - -// These methods are here, rather than in rulebasedcollator.cpp, -// for modularization: -// Most code using Collator does not need to build a Collator from rules. -// By moving these constructors and helper methods to a separate file, -// most code will not have a static dependency on the builder code. - -RuleBasedCollator::RuleBasedCollator() - : data(NULL), - settings(NULL), - tailoring(NULL), - cacheEntry(NULL), - validLocale(""), - explicitlySetAttributes(0), - actualLocaleIsSameAsValid(false) { -} - -RuleBasedCollator::RuleBasedCollator(const UnicodeString &rules, UErrorCode &errorCode) - : data(NULL), - settings(NULL), - tailoring(NULL), - cacheEntry(NULL), - validLocale(""), - explicitlySetAttributes(0), - actualLocaleIsSameAsValid(false) { - internalBuildTailoring(rules, UCOL_DEFAULT, UCOL_DEFAULT, NULL, NULL, errorCode); -} - -RuleBasedCollator::RuleBasedCollator(const UnicodeString &rules, ECollationStrength strength, - UErrorCode &errorCode) - : data(NULL), - settings(NULL), - tailoring(NULL), - cacheEntry(NULL), - validLocale(""), - explicitlySetAttributes(0), - actualLocaleIsSameAsValid(false) { - internalBuildTailoring(rules, strength, UCOL_DEFAULT, NULL, NULL, errorCode); -} - -RuleBasedCollator::RuleBasedCollator(const UnicodeString &rules, - UColAttributeValue decompositionMode, - UErrorCode &errorCode) - : data(NULL), - settings(NULL), - tailoring(NULL), - cacheEntry(NULL), - validLocale(""), - explicitlySetAttributes(0), - actualLocaleIsSameAsValid(false) { - internalBuildTailoring(rules, UCOL_DEFAULT, decompositionMode, NULL, NULL, errorCode); -} - -RuleBasedCollator::RuleBasedCollator(const UnicodeString &rules, - ECollationStrength strength, - UColAttributeValue decompositionMode, - UErrorCode &errorCode) - : data(NULL), - settings(NULL), - tailoring(NULL), - cacheEntry(NULL), - validLocale(""), - explicitlySetAttributes(0), - actualLocaleIsSameAsValid(false) { - internalBuildTailoring(rules, strength, decompositionMode, NULL, NULL, errorCode); -} - -RuleBasedCollator::RuleBasedCollator(const UnicodeString &rules, - UParseError &parseError, UnicodeString &reason, - UErrorCode &errorCode) - : data(NULL), - settings(NULL), - tailoring(NULL), - cacheEntry(NULL), - validLocale(""), - explicitlySetAttributes(0), - actualLocaleIsSameAsValid(false) { - internalBuildTailoring(rules, UCOL_DEFAULT, UCOL_DEFAULT, &parseError, &reason, errorCode); -} - -void -RuleBasedCollator::internalBuildTailoring(const UnicodeString &rules, - int32_t strength, - UColAttributeValue decompositionMode, - UParseError *outParseError, UnicodeString *outReason, - UErrorCode &errorCode) { - const CollationTailoring *base = CollationRoot::getRoot(errorCode); - if(U_FAILURE(errorCode)) { return; } - if(outReason != NULL) { outReason->remove(); } - CollationBuilder builder(base, errorCode); - UVersionInfo noVersion = { 0, 0, 0, 0 }; - BundleImporter importer; - LocalPointer t(builder.parseAndBuild(rules, noVersion, - &importer, - outParseError, errorCode)); - if(U_FAILURE(errorCode)) { - const char *reason = builder.getErrorReason(); - if(reason != NULL && outReason != NULL) { - *outReason = UnicodeString(reason, -1, US_INV); - } - return; - } - t->actualLocale.setToBogus(); - adoptTailoring(t.orphan(), errorCode); - // Set attributes after building the collator, - // to keep the default settings consistent with the rule string. - if(strength != UCOL_DEFAULT) { - setAttribute(UCOL_STRENGTH, (UColAttributeValue)strength, errorCode); - } - if(decompositionMode != UCOL_DEFAULT) { - setAttribute(UCOL_NORMALIZATION_MODE, decompositionMode, errorCode); - } -} - -// CollationBuilder implementation ----------------------------------------- *** - -CollationBuilder::CollationBuilder(const CollationTailoring *b, UBool icu4xMode, UErrorCode &errorCode) - : nfd(*Normalizer2::getNFDInstance(errorCode)), - fcd(*Normalizer2Factory::getFCDInstance(errorCode)), - nfcImpl(*Normalizer2Factory::getNFCImpl(errorCode)), - base(b), - baseData(b->data), - rootElements(b->data->rootElements, b->data->rootElementsLength), - variableTop(0), - dataBuilder(new CollationDataBuilder(icu4xMode, errorCode)), fastLatinEnabled(true), - icu4xMode(icu4xMode), - errorReason(NULL), - cesLength(0), - rootPrimaryIndexes(errorCode), nodes(errorCode) { - nfcImpl.ensureCanonIterData(errorCode); - if(U_FAILURE(errorCode)) { - errorReason = "CollationBuilder fields initialization failed"; - return; - } - if(dataBuilder == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - dataBuilder->initForTailoring(baseData, errorCode); - if(U_FAILURE(errorCode)) { - errorReason = "CollationBuilder initialization failed"; - } -} - -CollationBuilder::CollationBuilder(const CollationTailoring *b, UErrorCode &errorCode) - : CollationBuilder(b, false, errorCode) -{} - -CollationBuilder::~CollationBuilder() { - delete dataBuilder; -} - -CollationTailoring * -CollationBuilder::parseAndBuild(const UnicodeString &ruleString, - const UVersionInfo rulesVersion, - CollationRuleParser::Importer *importer, - UParseError *outParseError, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return NULL; } - if(baseData->rootElements == NULL) { - errorCode = U_MISSING_RESOURCE_ERROR; - errorReason = "missing root elements data, tailoring not supported"; - return NULL; - } - LocalPointer tailoring(new CollationTailoring(base->settings)); - if(tailoring.isNull() || tailoring->isBogus()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - CollationRuleParser parser(baseData, errorCode); - if(U_FAILURE(errorCode)) { return NULL; } - // Note: This always bases &[last variable] and &[first regular] - // on the root collator's maxVariable/variableTop. - // If we wanted this to change after [maxVariable x], then we would keep - // the tailoring.settings pointer here and read its variableTop when we need it. - // See http://unicode.org/cldr/trac/ticket/6070 - variableTop = base->settings->variableTop; - parser.setSink(this); - parser.setImporter(importer); - CollationSettings &ownedSettings = *SharedObject::copyOnWrite(tailoring->settings); - parser.parse(ruleString, ownedSettings, outParseError, errorCode); - errorReason = parser.getErrorReason(); - if(U_FAILURE(errorCode)) { return NULL; } - if(dataBuilder->hasMappings()) { - makeTailoredCEs(errorCode); - if (!icu4xMode) { - closeOverComposites(errorCode); - } - finalizeCEs(errorCode); - if (!icu4xMode) { - // Copy all of ASCII, and Latin-1 letters, into each tailoring. - optimizeSet.add(0, 0x7f); - optimizeSet.add(0xc0, 0xff); - // Hangul is decomposed on the fly during collation, - // and the tailoring data is always built with HANGUL_TAG specials. - optimizeSet.remove(Hangul::HANGUL_BASE, Hangul::HANGUL_END); - dataBuilder->optimize(optimizeSet, errorCode); - } - tailoring->ensureOwnedData(errorCode); - if(U_FAILURE(errorCode)) { return NULL; } - if(fastLatinEnabled) { dataBuilder->enableFastLatin(); } - dataBuilder->build(*tailoring->ownedData, errorCode); - tailoring->builder = dataBuilder; - dataBuilder = NULL; - } else { - tailoring->data = baseData; - } - if(U_FAILURE(errorCode)) { return NULL; } - ownedSettings.fastLatinOptions = CollationFastLatin::getOptions( - tailoring->data, ownedSettings, - ownedSettings.fastLatinPrimaries, UPRV_LENGTHOF(ownedSettings.fastLatinPrimaries)); - tailoring->rules = ruleString; - tailoring->rules.getTerminatedBuffer(); // ensure NUL-termination - tailoring->setVersion(base->version, rulesVersion); - return tailoring.orphan(); -} - -void -CollationBuilder::addReset(int32_t strength, const UnicodeString &str, - const char *&parserErrorReason, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - U_ASSERT(!str.isEmpty()); - if(str.charAt(0) == CollationRuleParser::POS_LEAD) { - ces[0] = getSpecialResetPosition(str, parserErrorReason, errorCode); - cesLength = 1; - if(U_FAILURE(errorCode)) { return; } - U_ASSERT((ces[0] & Collation::CASE_AND_QUATERNARY_MASK) == 0); - } else { - // normal reset to a character or string - UnicodeString nfdString = nfd.normalize(str, errorCode); - if(U_FAILURE(errorCode)) { - parserErrorReason = "normalizing the reset position"; - return; - } - cesLength = dataBuilder->getCEs(nfdString, ces, 0); - if(cesLength > Collation::MAX_EXPANSION_LENGTH) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - parserErrorReason = "reset position maps to too many collation elements (more than 31)"; - return; - } - } - if(strength == UCOL_IDENTICAL) { return; } // simple reset-at-position - - // &[before strength]position - U_ASSERT(UCOL_PRIMARY <= strength && strength <= UCOL_TERTIARY); - int32_t index = findOrInsertNodeForCEs(strength, parserErrorReason, errorCode); - if(U_FAILURE(errorCode)) { return; } - - int64_t node = nodes.elementAti(index); - // If the index is for a "weaker" node, - // then skip backwards over this and further "weaker" nodes. - while(strengthFromNode(node) > strength) { - index = previousIndexFromNode(node); - node = nodes.elementAti(index); - } - - // Find or insert a node whose index we will put into a temporary CE. - if(strengthFromNode(node) == strength && isTailoredNode(node)) { - // Reset to just before this same-strength tailored node. - index = previousIndexFromNode(node); - } else if(strength == UCOL_PRIMARY) { - // root primary node (has no previous index) - uint32_t p = weight32FromNode(node); - if(p == 0) { - errorCode = U_UNSUPPORTED_ERROR; - parserErrorReason = "reset primary-before ignorable not possible"; - return; - } - if(p <= rootElements.getFirstPrimary()) { - // There is no primary gap between ignorables and the space-first-primary. - errorCode = U_UNSUPPORTED_ERROR; - parserErrorReason = "reset primary-before first non-ignorable not supported"; - return; - } - if(p == Collation::FIRST_TRAILING_PRIMARY) { - // We do not support tailoring to an unassigned-implicit CE. - errorCode = U_UNSUPPORTED_ERROR; - parserErrorReason = "reset primary-before [first trailing] not supported"; - return; - } - p = rootElements.getPrimaryBefore(p, baseData->isCompressiblePrimary(p)); - index = findOrInsertNodeForPrimary(p, errorCode); - // Go to the last node in this list: - // Tailor after the last node between adjacent root nodes. - for(;;) { - node = nodes.elementAti(index); - int32_t nextIndex = nextIndexFromNode(node); - if(nextIndex == 0) { break; } - index = nextIndex; - } - } else { - // &[before 2] or &[before 3] - index = findCommonNode(index, UCOL_SECONDARY); - if(strength >= UCOL_TERTIARY) { - index = findCommonNode(index, UCOL_TERTIARY); - } - // findCommonNode() stayed on the stronger node or moved to - // an explicit common-weight node of the reset-before strength. - node = nodes.elementAti(index); - if(strengthFromNode(node) == strength) { - // Found a same-strength node with an explicit weight. - uint32_t weight16 = weight16FromNode(node); - if(weight16 == 0) { - errorCode = U_UNSUPPORTED_ERROR; - if(strength == UCOL_SECONDARY) { - parserErrorReason = "reset secondary-before secondary ignorable not possible"; - } else { - parserErrorReason = "reset tertiary-before completely ignorable not possible"; - } - return; - } - U_ASSERT(weight16 > Collation::BEFORE_WEIGHT16); - // Reset to just before this node. - // Insert the preceding same-level explicit weight if it is not there already. - // Which explicit weight immediately precedes this one? - weight16 = getWeight16Before(index, node, strength); - // Does this preceding weight have a node? - uint32_t previousWeight16; - int32_t previousIndex = previousIndexFromNode(node); - for(int32_t i = previousIndex;; i = previousIndexFromNode(node)) { - node = nodes.elementAti(i); - int32_t previousStrength = strengthFromNode(node); - if(previousStrength < strength) { - U_ASSERT(weight16 >= Collation::COMMON_WEIGHT16 || i == previousIndex); - // Either the reset element has an above-common weight and - // the parent node provides the implied common weight, - // or the reset element has a weight<=common in the node - // right after the parent, and we need to insert the preceding weight. - previousWeight16 = Collation::COMMON_WEIGHT16; - break; - } else if(previousStrength == strength && !isTailoredNode(node)) { - previousWeight16 = weight16FromNode(node); - break; - } - // Skip weaker nodes and same-level tailored nodes. - } - if(previousWeight16 == weight16) { - // The preceding weight has a node, - // maybe with following weaker or tailored nodes. - // Reset to the last of them. - index = previousIndex; - } else { - // Insert a node with the preceding weight, reset to that. - node = nodeFromWeight16(weight16) | nodeFromStrength(strength); - index = insertNodeBetween(previousIndex, index, node, errorCode); - } - } else { - // Found a stronger node with implied strength-common weight. - uint32_t weight16 = getWeight16Before(index, node, strength); - index = findOrInsertWeakNode(index, weight16, strength, errorCode); - } - // Strength of the temporary CE = strength of its reset position. - // Code above raises an error if the before-strength is stronger. - strength = ceStrength(ces[cesLength - 1]); - } - if(U_FAILURE(errorCode)) { - parserErrorReason = "inserting reset position for &[before n]"; - return; - } - ces[cesLength - 1] = tempCEFromIndexAndStrength(index, strength); -} - -uint32_t -CollationBuilder::getWeight16Before(int32_t index, int64_t node, int32_t level) { - U_ASSERT(strengthFromNode(node) < level || !isTailoredNode(node)); - // Collect the root CE weights if this node is for a root CE. - // If it is not, then return the low non-primary boundary for a tailored CE. - uint32_t t; - if(strengthFromNode(node) == UCOL_TERTIARY) { - t = weight16FromNode(node); - } else { - t = Collation::COMMON_WEIGHT16; // Stronger node with implied common weight. - } - while(strengthFromNode(node) > UCOL_SECONDARY) { - index = previousIndexFromNode(node); - node = nodes.elementAti(index); - } - if(isTailoredNode(node)) { - return Collation::BEFORE_WEIGHT16; - } - uint32_t s; - if(strengthFromNode(node) == UCOL_SECONDARY) { - s = weight16FromNode(node); - } else { - s = Collation::COMMON_WEIGHT16; // Stronger node with implied common weight. - } - while(strengthFromNode(node) > UCOL_PRIMARY) { - index = previousIndexFromNode(node); - node = nodes.elementAti(index); - } - if(isTailoredNode(node)) { - return Collation::BEFORE_WEIGHT16; - } - // [p, s, t] is a root CE. Return the preceding weight for the requested level. - uint32_t p = weight32FromNode(node); - uint32_t weight16; - if(level == UCOL_SECONDARY) { - weight16 = rootElements.getSecondaryBefore(p, s); - } else { - weight16 = rootElements.getTertiaryBefore(p, s, t); - U_ASSERT((weight16 & ~Collation::ONLY_TERTIARY_MASK) == 0); - } - return weight16; -} - -int64_t -CollationBuilder::getSpecialResetPosition(const UnicodeString &str, - const char *&parserErrorReason, UErrorCode &errorCode) { - U_ASSERT(str.length() == 2); - int64_t ce; - int32_t strength = UCOL_PRIMARY; - UBool isBoundary = false; - UChar32 pos = str.charAt(1) - CollationRuleParser::POS_BASE; - U_ASSERT(0 <= pos && pos <= CollationRuleParser::LAST_TRAILING); - switch(pos) { - case CollationRuleParser::FIRST_TERTIARY_IGNORABLE: - // Quaternary CEs are not supported. - // Non-zero quaternary weights are possible only on tertiary or stronger CEs. - return 0; - case CollationRuleParser::LAST_TERTIARY_IGNORABLE: - return 0; - case CollationRuleParser::FIRST_SECONDARY_IGNORABLE: { - // Look for a tailored tertiary node after [0, 0, 0]. - int32_t index = findOrInsertNodeForRootCE(0, UCOL_TERTIARY, errorCode); - if(U_FAILURE(errorCode)) { return 0; } - int64_t node = nodes.elementAti(index); - if((index = nextIndexFromNode(node)) != 0) { - node = nodes.elementAti(index); - U_ASSERT(strengthFromNode(node) <= UCOL_TERTIARY); - if(isTailoredNode(node) && strengthFromNode(node) == UCOL_TERTIARY) { - return tempCEFromIndexAndStrength(index, UCOL_TERTIARY); - } - } - return rootElements.getFirstTertiaryCE(); - // No need to look for nodeHasAnyBefore() on a tertiary node. - } - case CollationRuleParser::LAST_SECONDARY_IGNORABLE: - ce = rootElements.getLastTertiaryCE(); - strength = UCOL_TERTIARY; - break; - case CollationRuleParser::FIRST_PRIMARY_IGNORABLE: { - // Look for a tailored secondary node after [0, 0, *]. - int32_t index = findOrInsertNodeForRootCE(0, UCOL_SECONDARY, errorCode); - if(U_FAILURE(errorCode)) { return 0; } - int64_t node = nodes.elementAti(index); - while((index = nextIndexFromNode(node)) != 0) { - node = nodes.elementAti(index); - strength = strengthFromNode(node); - if(strength < UCOL_SECONDARY) { break; } - if(strength == UCOL_SECONDARY) { - if(isTailoredNode(node)) { - if(nodeHasBefore3(node)) { - index = nextIndexFromNode(nodes.elementAti(nextIndexFromNode(node))); - U_ASSERT(isTailoredNode(nodes.elementAti(index))); - } - return tempCEFromIndexAndStrength(index, UCOL_SECONDARY); - } else { - break; - } - } - } - ce = rootElements.getFirstSecondaryCE(); - strength = UCOL_SECONDARY; - break; - } - case CollationRuleParser::LAST_PRIMARY_IGNORABLE: - ce = rootElements.getLastSecondaryCE(); - strength = UCOL_SECONDARY; - break; - case CollationRuleParser::FIRST_VARIABLE: - ce = rootElements.getFirstPrimaryCE(); - isBoundary = true; // FractionalUCA.txt: FDD1 00A0, SPACE first primary - break; - case CollationRuleParser::LAST_VARIABLE: - ce = rootElements.lastCEWithPrimaryBefore(variableTop + 1); - break; - case CollationRuleParser::FIRST_REGULAR: - ce = rootElements.firstCEWithPrimaryAtLeast(variableTop + 1); - isBoundary = true; // FractionalUCA.txt: FDD1 263A, SYMBOL first primary - break; - case CollationRuleParser::LAST_REGULAR: - // Use the Hani-first-primary rather than the actual last "regular" CE before it, - // for backward compatibility with behavior before the introduction of - // script-first-primary CEs in the root collator. - ce = rootElements.firstCEWithPrimaryAtLeast( - baseData->getFirstPrimaryForGroup(USCRIPT_HAN)); - break; - case CollationRuleParser::FIRST_IMPLICIT: - ce = baseData->getSingleCE(0x4e00, errorCode); - break; - case CollationRuleParser::LAST_IMPLICIT: - // We do not support tailoring to an unassigned-implicit CE. - errorCode = U_UNSUPPORTED_ERROR; - parserErrorReason = "reset to [last implicit] not supported"; - return 0; - case CollationRuleParser::FIRST_TRAILING: - ce = Collation::makeCE(Collation::FIRST_TRAILING_PRIMARY); - isBoundary = true; // trailing first primary (there is no mapping for it) - break; - case CollationRuleParser::LAST_TRAILING: - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - parserErrorReason = "LDML forbids tailoring to U+FFFF"; - return 0; - default: - UPRV_UNREACHABLE_EXIT; - } - - int32_t index = findOrInsertNodeForRootCE(ce, strength, errorCode); - if(U_FAILURE(errorCode)) { return 0; } - int64_t node = nodes.elementAti(index); - if((pos & 1) == 0) { - // even pos = [first xyz] - if(!nodeHasAnyBefore(node) && isBoundary) { - // A first primary boundary is artificially added to FractionalUCA.txt. - // It is reachable via its special contraction, but is not normally used. - // Find the first character tailored after the boundary CE, - // or the first real root CE after it. - if((index = nextIndexFromNode(node)) != 0) { - // If there is a following node, then it must be tailored - // because there are no root CEs with a boundary primary - // and non-common secondary/tertiary weights. - node = nodes.elementAti(index); - U_ASSERT(isTailoredNode(node)); - ce = tempCEFromIndexAndStrength(index, strength); - } else { - U_ASSERT(strength == UCOL_PRIMARY); - uint32_t p = (uint32_t)(ce >> 32); - int32_t pIndex = rootElements.findPrimary(p); - UBool isCompressible = baseData->isCompressiblePrimary(p); - p = rootElements.getPrimaryAfter(p, pIndex, isCompressible); - ce = Collation::makeCE(p); - index = findOrInsertNodeForRootCE(ce, UCOL_PRIMARY, errorCode); - if(U_FAILURE(errorCode)) { return 0; } - node = nodes.elementAti(index); - } - } - if(nodeHasAnyBefore(node)) { - // Get the first node that was tailored before this one at a weaker strength. - if(nodeHasBefore2(node)) { - index = nextIndexFromNode(nodes.elementAti(nextIndexFromNode(node))); - node = nodes.elementAti(index); - } - if(nodeHasBefore3(node)) { - index = nextIndexFromNode(nodes.elementAti(nextIndexFromNode(node))); - } - U_ASSERT(isTailoredNode(nodes.elementAti(index))); - ce = tempCEFromIndexAndStrength(index, strength); - } - } else { - // odd pos = [last xyz] - // Find the last node that was tailored after the [last xyz] - // at a strength no greater than the position's strength. - for(;;) { - int32_t nextIndex = nextIndexFromNode(node); - if(nextIndex == 0) { break; } - int64_t nextNode = nodes.elementAti(nextIndex); - if(strengthFromNode(nextNode) < strength) { break; } - index = nextIndex; - node = nextNode; - } - // Do not make a temporary CE for a root node. - // This last node might be the node for the root CE itself, - // or a node with a common secondary or tertiary weight. - if(isTailoredNode(node)) { - ce = tempCEFromIndexAndStrength(index, strength); - } - } - return ce; -} - -void -CollationBuilder::addRelation(int32_t strength, const UnicodeString &prefix, - const UnicodeString &str, const UnicodeString &extension, - const char *&parserErrorReason, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - UnicodeString nfdPrefix; - if(!prefix.isEmpty()) { - nfd.normalize(prefix, nfdPrefix, errorCode); - if(U_FAILURE(errorCode)) { - parserErrorReason = "normalizing the relation prefix"; - return; - } - } - UnicodeString nfdString = nfd.normalize(str, errorCode); - if(U_FAILURE(errorCode)) { - parserErrorReason = "normalizing the relation string"; - return; - } - - // The runtime code decomposes Hangul syllables on the fly, - // with recursive processing but without making the Jamo pieces visible for matching. - // It does not work with certain types of contextual mappings. - int32_t nfdLength = nfdString.length(); - if(nfdLength >= 2) { - UChar c = nfdString.charAt(0); - if(Hangul::isJamoL(c) || Hangul::isJamoV(c)) { - // While handling a Hangul syllable, contractions starting with Jamo L or V - // would not see the following Jamo of that syllable. - errorCode = U_UNSUPPORTED_ERROR; - parserErrorReason = "contractions starting with conjoining Jamo L or V not supported"; - return; - } - c = nfdString.charAt(nfdLength - 1); - if(Hangul::isJamoL(c) || - (Hangul::isJamoV(c) && Hangul::isJamoL(nfdString.charAt(nfdLength - 2)))) { - // A contraction ending with Jamo L or L+V would require - // generating Hangul syllables in addTailComposites() (588 for a Jamo L), - // or decomposing a following Hangul syllable on the fly, during contraction matching. - errorCode = U_UNSUPPORTED_ERROR; - parserErrorReason = "contractions ending with conjoining Jamo L or L+V not supported"; - return; - } - // A Hangul syllable completely inside a contraction is ok. - } - // Note: If there is a prefix, then the parser checked that - // both the prefix and the string begin with NFC boundaries (not Jamo V or T). - // Therefore: prefix.isEmpty() || !isJamoVOrT(nfdString.charAt(0)) - // (While handling a Hangul syllable, prefixes on Jamo V or T - // would not see the previous Jamo of that syllable.) - - if(strength != UCOL_IDENTICAL) { - // Find the node index after which we insert the new tailored node. - int32_t index = findOrInsertNodeForCEs(strength, parserErrorReason, errorCode); - U_ASSERT(cesLength > 0); - int64_t ce = ces[cesLength - 1]; - if(strength == UCOL_PRIMARY && !isTempCE(ce) && (uint32_t)(ce >> 32) == 0) { - // There is no primary gap between ignorables and the space-first-primary. - errorCode = U_UNSUPPORTED_ERROR; - parserErrorReason = "tailoring primary after ignorables not supported"; - return; - } - if(strength == UCOL_QUATERNARY && ce == 0) { - // The CE data structure does not support non-zero quaternary weights - // on tertiary ignorables. - errorCode = U_UNSUPPORTED_ERROR; - parserErrorReason = "tailoring quaternary after tertiary ignorables not supported"; - return; - } - // Insert the new tailored node. - index = insertTailoredNodeAfter(index, strength, errorCode); - if(U_FAILURE(errorCode)) { - parserErrorReason = "modifying collation elements"; - return; - } - // Strength of the temporary CE: - // The new relation may yield a stronger CE but not a weaker one. - int32_t tempStrength = ceStrength(ce); - if(strength < tempStrength) { tempStrength = strength; } - ces[cesLength - 1] = tempCEFromIndexAndStrength(index, tempStrength); - } - - setCaseBits(nfdString, parserErrorReason, errorCode); - if(U_FAILURE(errorCode)) { return; } - - int32_t cesLengthBeforeExtension = cesLength; - if(!extension.isEmpty()) { - UnicodeString nfdExtension = nfd.normalize(extension, errorCode); - if(U_FAILURE(errorCode)) { - parserErrorReason = "normalizing the relation extension"; - return; - } - cesLength = dataBuilder->getCEs(nfdExtension, ces, cesLength); - if(cesLength > Collation::MAX_EXPANSION_LENGTH) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - parserErrorReason = - "extension string adds too many collation elements (more than 31 total)"; - return; - } - } - uint32_t ce32 = Collation::UNASSIGNED_CE32; - if(!icu4xMode && (prefix != nfdPrefix || str != nfdString) && - !ignorePrefix(prefix, errorCode) && !ignoreString(str, errorCode)) { - // Map from the original input to the CEs. - // We do this in case the canonical closure is incomplete, - // so that it is possible to explicitly provide the missing mappings. - ce32 = addIfDifferent(prefix, str, ces, cesLength, ce32, errorCode); - } - if (!icu4xMode) { - addWithClosure(nfdPrefix, nfdString, ces, cesLength, ce32, errorCode); - } else { - addIfDifferent(nfdPrefix, nfdString, ces, cesLength, ce32, errorCode); - } - if(U_FAILURE(errorCode)) { - parserErrorReason = "writing collation elements"; - return; - } - cesLength = cesLengthBeforeExtension; -} - -int32_t -CollationBuilder::findOrInsertNodeForCEs(int32_t strength, const char *&parserErrorReason, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - U_ASSERT(UCOL_PRIMARY <= strength && strength <= UCOL_QUATERNARY); - - // Find the last CE that is at least as "strong" as the requested difference. - // Note: Stronger is smaller (UCOL_PRIMARY=0). - int64_t ce; - for(;; --cesLength) { - if(cesLength == 0) { - ce = ces[0] = 0; - cesLength = 1; - break; - } else { - ce = ces[cesLength - 1]; - } - if(ceStrength(ce) <= strength) { break; } - } - - if(isTempCE(ce)) { - // No need to findCommonNode() here for lower levels - // because insertTailoredNodeAfter() will do that anyway. - return indexFromTempCE(ce); - } - - // root CE - if((uint8_t)(ce >> 56) == Collation::UNASSIGNED_IMPLICIT_BYTE) { - errorCode = U_UNSUPPORTED_ERROR; - parserErrorReason = "tailoring relative to an unassigned code point not supported"; - return 0; - } - return findOrInsertNodeForRootCE(ce, strength, errorCode); -} - -int32_t -CollationBuilder::findOrInsertNodeForRootCE(int64_t ce, int32_t strength, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - U_ASSERT((uint8_t)(ce >> 56) != Collation::UNASSIGNED_IMPLICIT_BYTE); - - // Find or insert the node for each of the root CE's weights, - // down to the requested level/strength. - // Root CEs must have common=zero quaternary weights (for which we never insert any nodes). - U_ASSERT((ce & 0xc0) == 0); - int32_t index = findOrInsertNodeForPrimary((uint32_t)(ce >> 32), errorCode); - if(strength >= UCOL_SECONDARY) { - uint32_t lower32 = (uint32_t)ce; - index = findOrInsertWeakNode(index, lower32 >> 16, UCOL_SECONDARY, errorCode); - if(strength >= UCOL_TERTIARY) { - index = findOrInsertWeakNode(index, lower32 & Collation::ONLY_TERTIARY_MASK, - UCOL_TERTIARY, errorCode); - } - } - return index; -} - -namespace { - -/** - * Like Java Collections.binarySearch(List, key, Comparator). - * - * @return the index>=0 where the item was found, - * or the index<0 for inserting the string at ~index in sorted order - * (index into rootPrimaryIndexes) - */ -int32_t -binarySearchForRootPrimaryNode(const int32_t *rootPrimaryIndexes, int32_t length, - const int64_t *nodes, uint32_t p) { - if(length == 0) { return ~0; } - int32_t start = 0; - int32_t limit = length; - for (;;) { - int32_t i = (start + limit) / 2; - int64_t node = nodes[rootPrimaryIndexes[i]]; - uint32_t nodePrimary = (uint32_t)(node >> 32); // weight32FromNode(node) - if (p == nodePrimary) { - return i; - } else if (p < nodePrimary) { - if (i == start) { - return ~start; // insert s before i - } - limit = i; - } else { - if (i == start) { - return ~(start + 1); // insert s after i - } - start = i; - } - } -} - -} // namespace - -int32_t -CollationBuilder::findOrInsertNodeForPrimary(uint32_t p, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - - int32_t rootIndex = binarySearchForRootPrimaryNode( - rootPrimaryIndexes.getBuffer(), rootPrimaryIndexes.size(), nodes.getBuffer(), p); - if(rootIndex >= 0) { - return rootPrimaryIndexes.elementAti(rootIndex); - } else { - // Start a new list of nodes with this primary. - int32_t index = nodes.size(); - nodes.addElement(nodeFromWeight32(p), errorCode); - rootPrimaryIndexes.insertElementAt(index, ~rootIndex, errorCode); - return index; - } -} - -int32_t -CollationBuilder::findOrInsertWeakNode(int32_t index, uint32_t weight16, int32_t level, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - U_ASSERT(0 <= index && index < nodes.size()); - U_ASSERT(UCOL_SECONDARY <= level && level <= UCOL_TERTIARY); - - if(weight16 == Collation::COMMON_WEIGHT16) { - return findCommonNode(index, level); - } - - // If this will be the first below-common weight for the parent node, - // then we will also need to insert a common weight after it. - int64_t node = nodes.elementAti(index); - U_ASSERT(strengthFromNode(node) < level); // parent node is stronger - if(weight16 != 0 && weight16 < Collation::COMMON_WEIGHT16) { - int32_t hasThisLevelBefore = level == UCOL_SECONDARY ? HAS_BEFORE2 : HAS_BEFORE3; - if((node & hasThisLevelBefore) == 0) { - // The parent node has an implied level-common weight. - int64_t commonNode = - nodeFromWeight16(Collation::COMMON_WEIGHT16) | nodeFromStrength(level); - if(level == UCOL_SECONDARY) { - // Move the HAS_BEFORE3 flag from the parent node - // to the new secondary common node. - commonNode |= node & HAS_BEFORE3; - node &= ~(int64_t)HAS_BEFORE3; - } - nodes.setElementAt(node | hasThisLevelBefore, index); - // Insert below-common-weight node. - int32_t nextIndex = nextIndexFromNode(node); - node = nodeFromWeight16(weight16) | nodeFromStrength(level); - index = insertNodeBetween(index, nextIndex, node, errorCode); - // Insert common-weight node. - insertNodeBetween(index, nextIndex, commonNode, errorCode); - // Return index of below-common-weight node. - return index; - } - } - - // Find the root CE's weight for this level. - // Postpone insertion if not found: - // Insert the new root node before the next stronger node, - // or before the next root node with the same strength and a larger weight. - int32_t nextIndex; - while((nextIndex = nextIndexFromNode(node)) != 0) { - node = nodes.elementAti(nextIndex); - int32_t nextStrength = strengthFromNode(node); - if(nextStrength <= level) { - // Insert before a stronger node. - if(nextStrength < level) { break; } - // nextStrength == level - if(!isTailoredNode(node)) { - uint32_t nextWeight16 = weight16FromNode(node); - if(nextWeight16 == weight16) { - // Found the node for the root CE up to this level. - return nextIndex; - } - // Insert before a node with a larger same-strength weight. - if(nextWeight16 > weight16) { break; } - } - } - // Skip the next node. - index = nextIndex; - } - node = nodeFromWeight16(weight16) | nodeFromStrength(level); - return insertNodeBetween(index, nextIndex, node, errorCode); -} - -int32_t -CollationBuilder::insertTailoredNodeAfter(int32_t index, int32_t strength, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - U_ASSERT(0 <= index && index < nodes.size()); - if(strength >= UCOL_SECONDARY) { - index = findCommonNode(index, UCOL_SECONDARY); - if(strength >= UCOL_TERTIARY) { - index = findCommonNode(index, UCOL_TERTIARY); - } - } - // Postpone insertion: - // Insert the new node before the next one with a strength at least as strong. - int64_t node = nodes.elementAti(index); - int32_t nextIndex; - while((nextIndex = nextIndexFromNode(node)) != 0) { - node = nodes.elementAti(nextIndex); - if(strengthFromNode(node) <= strength) { break; } - // Skip the next node which has a weaker (larger) strength than the new one. - index = nextIndex; - } - node = IS_TAILORED | nodeFromStrength(strength); - return insertNodeBetween(index, nextIndex, node, errorCode); -} - -int32_t -CollationBuilder::insertNodeBetween(int32_t index, int32_t nextIndex, int64_t node, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - U_ASSERT(previousIndexFromNode(node) == 0); - U_ASSERT(nextIndexFromNode(node) == 0); - U_ASSERT(nextIndexFromNode(nodes.elementAti(index)) == nextIndex); - // Append the new node and link it to the existing nodes. - int32_t newIndex = nodes.size(); - node |= nodeFromPreviousIndex(index) | nodeFromNextIndex(nextIndex); - nodes.addElement(node, errorCode); - if(U_FAILURE(errorCode)) { return 0; } - // nodes[index].nextIndex = newIndex - node = nodes.elementAti(index); - nodes.setElementAt(changeNodeNextIndex(node, newIndex), index); - // nodes[nextIndex].previousIndex = newIndex - if(nextIndex != 0) { - node = nodes.elementAti(nextIndex); - nodes.setElementAt(changeNodePreviousIndex(node, newIndex), nextIndex); - } - return newIndex; -} - -int32_t -CollationBuilder::findCommonNode(int32_t index, int32_t strength) const { - U_ASSERT(UCOL_SECONDARY <= strength && strength <= UCOL_TERTIARY); - int64_t node = nodes.elementAti(index); - if(strengthFromNode(node) >= strength) { - // The current node is no stronger. - return index; - } - if(strength == UCOL_SECONDARY ? !nodeHasBefore2(node) : !nodeHasBefore3(node)) { - // The current node implies the strength-common weight. - return index; - } - index = nextIndexFromNode(node); - node = nodes.elementAti(index); - U_ASSERT(!isTailoredNode(node) && strengthFromNode(node) == strength && - weight16FromNode(node) < Collation::COMMON_WEIGHT16); - // Skip to the explicit common node. - do { - index = nextIndexFromNode(node); - node = nodes.elementAti(index); - U_ASSERT(strengthFromNode(node) >= strength); - } while(isTailoredNode(node) || strengthFromNode(node) > strength || - weight16FromNode(node) < Collation::COMMON_WEIGHT16); - U_ASSERT(weight16FromNode(node) == Collation::COMMON_WEIGHT16); - return index; -} - -void -CollationBuilder::setCaseBits(const UnicodeString &nfdString, - const char *&parserErrorReason, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - int32_t numTailoredPrimaries = 0; - for(int32_t i = 0; i < cesLength; ++i) { - if(ceStrength(ces[i]) == UCOL_PRIMARY) { ++numTailoredPrimaries; } - } - // We should not be able to get too many case bits because - // cesLength<=31==MAX_EXPANSION_LENGTH. - // 31 pairs of case bits fit into an int64_t without setting its sign bit. - U_ASSERT(numTailoredPrimaries <= 31); - - int64_t cases = 0; - if(numTailoredPrimaries > 0) { - const UChar *s = nfdString.getBuffer(); - UTF16CollationIterator baseCEs(baseData, false, s, s, s + nfdString.length()); - int32_t baseCEsLength = baseCEs.fetchCEs(errorCode) - 1; - if(U_FAILURE(errorCode)) { - parserErrorReason = "fetching root CEs for tailored string"; - return; - } - U_ASSERT(baseCEsLength >= 0 && baseCEs.getCE(baseCEsLength) == Collation::NO_CE); - - uint32_t lastCase = 0; - int32_t numBasePrimaries = 0; - for(int32_t i = 0; i < baseCEsLength; ++i) { - int64_t ce = baseCEs.getCE(i); - if((ce >> 32) != 0) { - ++numBasePrimaries; - uint32_t c = ((uint32_t)ce >> 14) & 3; - U_ASSERT(c == 0 || c == 2); // lowercase or uppercase, no mixed case in any base CE - if(numBasePrimaries < numTailoredPrimaries) { - cases |= (int64_t)c << ((numBasePrimaries - 1) * 2); - } else if(numBasePrimaries == numTailoredPrimaries) { - lastCase = c; - } else if(c != lastCase) { - // There are more base primary CEs than tailored primaries. - // Set mixed case if the case bits of the remainder differ. - lastCase = 1; - // Nothing more can change. - break; - } - } - } - if(numBasePrimaries >= numTailoredPrimaries) { - cases |= (int64_t)lastCase << ((numTailoredPrimaries - 1) * 2); - } - } - - for(int32_t i = 0; i < cesLength; ++i) { - int64_t ce = ces[i] & INT64_C(0xffffffffffff3fff); // clear old case bits - int32_t strength = ceStrength(ce); - if(strength == UCOL_PRIMARY) { - ce |= (cases & 3) << 14; - cases >>= 2; - } else if(strength == UCOL_TERTIARY) { - // Tertiary CEs must have uppercase bits. - // See the LDML spec, and comments in class CollationCompare. - ce |= 0x8000; - } - // Tertiary ignorable CEs must have 0 case bits. - // We set 0 case bits for secondary CEs too - // since currently only U+0345 is cased and maps to a secondary CE, - // and it is lowercase. Other secondaries are uncased. - // See [[:Cased:]&[:uca1=:]] where uca1 queries the root primary weight. - ces[i] = ce; - } -} - -void -CollationBuilder::suppressContractions(const UnicodeSet &set, const char *&parserErrorReason, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - dataBuilder->suppressContractions(set, errorCode); - if(U_FAILURE(errorCode)) { - parserErrorReason = "application of [suppressContractions [set]] failed"; - } -} - -void -CollationBuilder::optimize(const UnicodeSet &set, const char *& /* parserErrorReason */, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - optimizeSet.addAll(set); -} - -uint32_t -CollationBuilder::addWithClosure(const UnicodeString &nfdPrefix, const UnicodeString &nfdString, - const int64_t newCEs[], int32_t newCEsLength, uint32_t ce32, - UErrorCode &errorCode) { - // Map from the NFD input to the CEs. - ce32 = addIfDifferent(nfdPrefix, nfdString, newCEs, newCEsLength, ce32, errorCode); - ce32 = addOnlyClosure(nfdPrefix, nfdString, newCEs, newCEsLength, ce32, errorCode); - addTailComposites(nfdPrefix, nfdString, errorCode); - return ce32; -} - -uint32_t -CollationBuilder::addOnlyClosure(const UnicodeString &nfdPrefix, const UnicodeString &nfdString, - const int64_t newCEs[], int32_t newCEsLength, uint32_t ce32, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return ce32; } - - // Map from canonically equivalent input to the CEs. (But not from the all-NFD input.) - if(nfdPrefix.isEmpty()) { - CanonicalIterator stringIter(nfdString, errorCode); - if(U_FAILURE(errorCode)) { return ce32; } - UnicodeString prefix; - for(;;) { - UnicodeString str = stringIter.next(); - if(str.isBogus()) { break; } - if(ignoreString(str, errorCode) || str == nfdString) { continue; } - ce32 = addIfDifferent(prefix, str, newCEs, newCEsLength, ce32, errorCode); - if(U_FAILURE(errorCode)) { return ce32; } - } - } else { - CanonicalIterator prefixIter(nfdPrefix, errorCode); - CanonicalIterator stringIter(nfdString, errorCode); - if(U_FAILURE(errorCode)) { return ce32; } - for(;;) { - UnicodeString prefix = prefixIter.next(); - if(prefix.isBogus()) { break; } - if(ignorePrefix(prefix, errorCode)) { continue; } - UBool samePrefix = prefix == nfdPrefix; - for(;;) { - UnicodeString str = stringIter.next(); - if(str.isBogus()) { break; } - if(ignoreString(str, errorCode) || (samePrefix && str == nfdString)) { continue; } - ce32 = addIfDifferent(prefix, str, newCEs, newCEsLength, ce32, errorCode); - if(U_FAILURE(errorCode)) { return ce32; } - } - stringIter.reset(); - } - } - return ce32; -} - -void -CollationBuilder::addTailComposites(const UnicodeString &nfdPrefix, const UnicodeString &nfdString, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - - // Look for the last starter in the NFD string. - UChar32 lastStarter; - int32_t indexAfterLastStarter = nfdString.length(); - for(;;) { - if(indexAfterLastStarter == 0) { return; } // no starter at all - lastStarter = nfdString.char32At(indexAfterLastStarter - 1); - if(nfd.getCombiningClass(lastStarter) == 0) { break; } - indexAfterLastStarter -= U16_LENGTH(lastStarter); - } - // No closure to Hangul syllables since we decompose them on the fly. - if(Hangul::isJamoL(lastStarter)) { return; } - - // Are there any composites whose decomposition starts with the lastStarter? - // Note: Normalizer2Impl does not currently return start sets for NFC_QC=Maybe characters. - // We might find some more equivalent mappings here if it did. - UnicodeSet composites; - if(!nfcImpl.getCanonStartSet(lastStarter, composites)) { return; } - - UnicodeString decomp; - UnicodeString newNFDString, newString; - int64_t newCEs[Collation::MAX_EXPANSION_LENGTH]; - UnicodeSetIterator iter(composites); - while(iter.next()) { - U_ASSERT(!iter.isString()); - UChar32 composite = iter.getCodepoint(); - nfd.getDecomposition(composite, decomp); - if(!mergeCompositeIntoString(nfdString, indexAfterLastStarter, composite, decomp, - newNFDString, newString, errorCode)) { - continue; - } - int32_t newCEsLength = dataBuilder->getCEs(nfdPrefix, newNFDString, newCEs, 0); - if(newCEsLength > Collation::MAX_EXPANSION_LENGTH) { - // Ignore mappings that we cannot store. - continue; - } - // Note: It is possible that the newCEs do not make use of the mapping - // for which we are adding the tail composites, in which case we might be adding - // unnecessary mappings. - // For example, when we add tail composites for ae^ (^=combining circumflex), - // UCA discontiguous-contraction matching does not find any matches - // for ae_^ (_=any combining diacritic below) *unless* there is also - // a contraction mapping for ae. - // Thus, if there is no ae contraction, then the ae^ mapping is ignored - // while fetching the newCEs for ae_^. - // TODO: Try to detect this effectively. - // (Alternatively, print a warning when prefix contractions are missing.) - - // We do not need an explicit mapping for the NFD strings. - // It is fine if the NFD input collates like this via a sequence of mappings. - // It also saves a little bit of space, and may reduce the set of characters with contractions. - uint32_t ce32 = addIfDifferent(nfdPrefix, newString, - newCEs, newCEsLength, Collation::UNASSIGNED_CE32, errorCode); - if(ce32 != Collation::UNASSIGNED_CE32) { - // was different, was added - addOnlyClosure(nfdPrefix, newNFDString, newCEs, newCEsLength, ce32, errorCode); - } - } -} - -UBool -CollationBuilder::mergeCompositeIntoString(const UnicodeString &nfdString, - int32_t indexAfterLastStarter, - UChar32 composite, const UnicodeString &decomp, - UnicodeString &newNFDString, UnicodeString &newString, - UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return false; } - U_ASSERT(nfdString.char32At(indexAfterLastStarter - 1) == decomp.char32At(0)); - int32_t lastStarterLength = decomp.moveIndex32(0, 1); - if(lastStarterLength == decomp.length()) { - // Singleton decompositions should be found by addWithClosure() - // and the CanonicalIterator, so we can ignore them here. - return false; - } - if(nfdString.compare(indexAfterLastStarter, 0x7fffffff, - decomp, lastStarterLength, 0x7fffffff) == 0) { - // same strings, nothing new to be found here - return false; - } - - // Make new FCD strings that combine a composite, or its decomposition, - // into the nfdString's last starter and the combining marks following it. - // Make an NFD version, and a version with the composite. - newNFDString.setTo(nfdString, 0, indexAfterLastStarter); - newString.setTo(nfdString, 0, indexAfterLastStarter - lastStarterLength).append(composite); - - // The following is related to discontiguous contraction matching, - // but builds only FCD strings (or else returns false). - int32_t sourceIndex = indexAfterLastStarter; - int32_t decompIndex = lastStarterLength; - // Small optimization: We keep the source character across loop iterations - // because we do not always consume it, - // and then need not fetch it again nor look up its combining class again. - UChar32 sourceChar = U_SENTINEL; - // The cc variables need to be declared before the loop so that at the end - // they are set to the last combining classes seen. - uint8_t sourceCC = 0; - uint8_t decompCC = 0; - for(;;) { - if(sourceChar < 0) { - if(sourceIndex >= nfdString.length()) { break; } - sourceChar = nfdString.char32At(sourceIndex); - sourceCC = nfd.getCombiningClass(sourceChar); - U_ASSERT(sourceCC != 0); - } - // We consume a decomposition character in each iteration. - if(decompIndex >= decomp.length()) { break; } - UChar32 decompChar = decomp.char32At(decompIndex); - decompCC = nfd.getCombiningClass(decompChar); - // Compare the two characters and their combining classes. - if(decompCC == 0) { - // Unable to merge because the source contains a non-zero combining mark - // but the composite's decomposition contains another starter. - // The strings would not be equivalent. - return false; - } else if(sourceCC < decompCC) { - // Composite + sourceChar would not be FCD. - return false; - } else if(decompCC < sourceCC) { - newNFDString.append(decompChar); - decompIndex += U16_LENGTH(decompChar); - } else if(decompChar != sourceChar) { - // Blocked because same combining class. - return false; - } else { // match: decompChar == sourceChar - newNFDString.append(decompChar); - decompIndex += U16_LENGTH(decompChar); - sourceIndex += U16_LENGTH(decompChar); - sourceChar = U_SENTINEL; - } - } - // We are at the end of at least one of the two inputs. - if(sourceChar >= 0) { // more characters from nfdString but not from decomp - if(sourceCC < decompCC) { - // Appending the next source character to the composite would not be FCD. - return false; - } - newNFDString.append(nfdString, sourceIndex, 0x7fffffff); - newString.append(nfdString, sourceIndex, 0x7fffffff); - } else if(decompIndex < decomp.length()) { // more characters from decomp, not from nfdString - newNFDString.append(decomp, decompIndex, 0x7fffffff); - } - U_ASSERT(nfd.isNormalized(newNFDString, errorCode)); - U_ASSERT(fcd.isNormalized(newString, errorCode)); - U_ASSERT(nfd.normalize(newString, errorCode) == newNFDString); // canonically equivalent - return true; -} - -UBool -CollationBuilder::ignorePrefix(const UnicodeString &s, UErrorCode &errorCode) const { - // Do not map non-FCD prefixes. - return !isFCD(s, errorCode); -} - -UBool -CollationBuilder::ignoreString(const UnicodeString &s, UErrorCode &errorCode) const { - // Do not map non-FCD strings. - // Do not map strings that start with Hangul syllables: We decompose those on the fly. - return !isFCD(s, errorCode) || Hangul::isHangul(s.charAt(0)); -} - -UBool -CollationBuilder::isFCD(const UnicodeString &s, UErrorCode &errorCode) const { - return U_SUCCESS(errorCode) && fcd.isNormalized(s, errorCode); -} - -void -CollationBuilder::closeOverComposites(UErrorCode &errorCode) { - UnicodeSet composites(UNICODE_STRING_SIMPLE("[:NFD_QC=N:]"), errorCode); // Java: static final - if(U_FAILURE(errorCode)) { return; } - // Hangul is decomposed on the fly during collation. - composites.remove(Hangul::HANGUL_BASE, Hangul::HANGUL_END); - UnicodeString prefix; // empty - UnicodeString nfdString; - UnicodeSetIterator iter(composites); - while(iter.next()) { - U_ASSERT(!iter.isString()); - nfd.getDecomposition(iter.getCodepoint(), nfdString); - cesLength = dataBuilder->getCEs(nfdString, ces, 0); - if(cesLength > Collation::MAX_EXPANSION_LENGTH) { - // Too many CEs from the decomposition (unusual), ignore this composite. - // We could add a capacity parameter to getCEs() and reallocate if necessary. - // However, this can only really happen in contrived cases. - continue; - } - const UnicodeString &composite(iter.getString()); - addIfDifferent(prefix, composite, ces, cesLength, Collation::UNASSIGNED_CE32, errorCode); - } -} - -uint32_t -CollationBuilder::addIfDifferent(const UnicodeString &prefix, const UnicodeString &str, - const int64_t newCEs[], int32_t newCEsLength, uint32_t ce32, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return ce32; } - int64_t oldCEs[Collation::MAX_EXPANSION_LENGTH]; - int32_t oldCEsLength = dataBuilder->getCEs(prefix, str, oldCEs, 0); - if(!sameCEs(newCEs, newCEsLength, oldCEs, oldCEsLength)) { - if(ce32 == Collation::UNASSIGNED_CE32) { - ce32 = dataBuilder->encodeCEs(newCEs, newCEsLength, errorCode); - } - dataBuilder->addCE32(prefix, str, ce32, errorCode); - } - return ce32; -} - -UBool -CollationBuilder::sameCEs(const int64_t ces1[], int32_t ces1Length, - const int64_t ces2[], int32_t ces2Length) { - if(ces1Length != ces2Length) { - return false; - } - U_ASSERT(ces1Length <= Collation::MAX_EXPANSION_LENGTH); - for(int32_t i = 0; i < ces1Length; ++i) { - if(ces1[i] != ces2[i]) { return false; } - } - return true; -} - -#ifdef DEBUG_COLLATION_BUILDER - -uint32_t -alignWeightRight(uint32_t w) { - if(w != 0) { - while((w & 0xff) == 0) { w >>= 8; } - } - return w; -} - -#endif - -void -CollationBuilder::makeTailoredCEs(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - - CollationWeights primaries, secondaries, tertiaries; - int64_t *nodesArray = nodes.getBuffer(); -#ifdef DEBUG_COLLATION_BUILDER - puts("\nCollationBuilder::makeTailoredCEs()"); -#endif - - for(int32_t rpi = 0; rpi < rootPrimaryIndexes.size(); ++rpi) { - int32_t i = rootPrimaryIndexes.elementAti(rpi); - int64_t node = nodesArray[i]; - uint32_t p = weight32FromNode(node); - uint32_t s = p == 0 ? 0 : Collation::COMMON_WEIGHT16; - uint32_t t = s; - uint32_t q = 0; - UBool pIsTailored = false; - UBool sIsTailored = false; - UBool tIsTailored = false; -#ifdef DEBUG_COLLATION_BUILDER - printf("\nprimary %lx\n", (long)alignWeightRight(p)); -#endif - int32_t pIndex = p == 0 ? 0 : rootElements.findPrimary(p); - int32_t nextIndex = nextIndexFromNode(node); - while(nextIndex != 0) { - i = nextIndex; - node = nodesArray[i]; - nextIndex = nextIndexFromNode(node); - int32_t strength = strengthFromNode(node); - if(strength == UCOL_QUATERNARY) { - U_ASSERT(isTailoredNode(node)); -#ifdef DEBUG_COLLATION_BUILDER - printf(" quat+ "); -#endif - if(q == 3) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - errorReason = "quaternary tailoring gap too small"; - return; - } - ++q; - } else { - if(strength == UCOL_TERTIARY) { - if(isTailoredNode(node)) { -#ifdef DEBUG_COLLATION_BUILDER - printf(" ter+ "); -#endif - if(!tIsTailored) { - // First tailored tertiary node for [p, s]. - int32_t tCount = countTailoredNodes(nodesArray, nextIndex, - UCOL_TERTIARY) + 1; - uint32_t tLimit; - if(t == 0) { - // Gap at the beginning of the tertiary CE range. - t = rootElements.getTertiaryBoundary() - 0x100; - tLimit = rootElements.getFirstTertiaryCE() & Collation::ONLY_TERTIARY_MASK; - } else if(!pIsTailored && !sIsTailored) { - // p and s are root weights. - tLimit = rootElements.getTertiaryAfter(pIndex, s, t); - } else if(t == Collation::BEFORE_WEIGHT16) { - tLimit = Collation::COMMON_WEIGHT16; - } else { - // [p, s] is tailored. - U_ASSERT(t == Collation::COMMON_WEIGHT16); - tLimit = rootElements.getTertiaryBoundary(); - } - U_ASSERT(tLimit == 0x4000 || (tLimit & ~Collation::ONLY_TERTIARY_MASK) == 0); - tertiaries.initForTertiary(); - if(!tertiaries.allocWeights(t, tLimit, tCount)) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - errorReason = "tertiary tailoring gap too small"; - return; - } - tIsTailored = true; - } - t = tertiaries.nextWeight(); - U_ASSERT(t != 0xffffffff); - } else { - t = weight16FromNode(node); - tIsTailored = false; -#ifdef DEBUG_COLLATION_BUILDER - printf(" ter %lx\n", (long)alignWeightRight(t)); -#endif - } - } else { - if(strength == UCOL_SECONDARY) { - if(isTailoredNode(node)) { -#ifdef DEBUG_COLLATION_BUILDER - printf(" sec+ "); -#endif - if(!sIsTailored) { - // First tailored secondary node for p. - int32_t sCount = countTailoredNodes(nodesArray, nextIndex, - UCOL_SECONDARY) + 1; - uint32_t sLimit; - if(s == 0) { - // Gap at the beginning of the secondary CE range. - s = rootElements.getSecondaryBoundary() - 0x100; - sLimit = rootElements.getFirstSecondaryCE() >> 16; - } else if(!pIsTailored) { - // p is a root primary. - sLimit = rootElements.getSecondaryAfter(pIndex, s); - } else if(s == Collation::BEFORE_WEIGHT16) { - sLimit = Collation::COMMON_WEIGHT16; - } else { - // p is a tailored primary. - U_ASSERT(s == Collation::COMMON_WEIGHT16); - sLimit = rootElements.getSecondaryBoundary(); - } - if(s == Collation::COMMON_WEIGHT16) { - // Do not tailor into the getSortKey() range of - // compressed common secondaries. - s = rootElements.getLastCommonSecondary(); - } - secondaries.initForSecondary(); - if(!secondaries.allocWeights(s, sLimit, sCount)) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - errorReason = "secondary tailoring gap too small"; -#ifdef DEBUG_COLLATION_BUILDER - printf("!secondaries.allocWeights(%lx, %lx, sCount=%ld)\n", - (long)alignWeightRight(s), (long)alignWeightRight(sLimit), - (long)alignWeightRight(sCount)); -#endif - return; - } - sIsTailored = true; - } - s = secondaries.nextWeight(); - U_ASSERT(s != 0xffffffff); - } else { - s = weight16FromNode(node); - sIsTailored = false; -#ifdef DEBUG_COLLATION_BUILDER - printf(" sec %lx\n", (long)alignWeightRight(s)); -#endif - } - } else /* UCOL_PRIMARY */ { - U_ASSERT(isTailoredNode(node)); -#ifdef DEBUG_COLLATION_BUILDER - printf("pri+ "); -#endif - if(!pIsTailored) { - // First tailored primary node in this list. - int32_t pCount = countTailoredNodes(nodesArray, nextIndex, - UCOL_PRIMARY) + 1; - UBool isCompressible = baseData->isCompressiblePrimary(p); - uint32_t pLimit = - rootElements.getPrimaryAfter(p, pIndex, isCompressible); - primaries.initForPrimary(isCompressible); - if(!primaries.allocWeights(p, pLimit, pCount)) { - errorCode = U_BUFFER_OVERFLOW_ERROR; // TODO: introduce a more specific UErrorCode? - errorReason = "primary tailoring gap too small"; - return; - } - pIsTailored = true; - } - p = primaries.nextWeight(); - U_ASSERT(p != 0xffffffff); - s = Collation::COMMON_WEIGHT16; - sIsTailored = false; - } - t = s == 0 ? 0 : Collation::COMMON_WEIGHT16; - tIsTailored = false; - } - q = 0; - } - if(isTailoredNode(node)) { - nodesArray[i] = Collation::makeCE(p, s, t, q); -#ifdef DEBUG_COLLATION_BUILDER - printf("%016llx\n", (long long)nodesArray[i]); -#endif - } - } - } -} - -int32_t -CollationBuilder::countTailoredNodes(const int64_t *nodesArray, int32_t i, int32_t strength) { - int32_t count = 0; - for(;;) { - if(i == 0) { break; } - int64_t node = nodesArray[i]; - if(strengthFromNode(node) < strength) { break; } - if(strengthFromNode(node) == strength) { - if(isTailoredNode(node)) { - ++count; - } else { - break; - } - } - i = nextIndexFromNode(node); - } - return count; -} - -class CEFinalizer : public CollationDataBuilder::CEModifier { -public: - CEFinalizer(const int64_t *ces) : finalCEs(ces) {} - virtual ~CEFinalizer(); - virtual int64_t modifyCE32(uint32_t ce32) const override { - U_ASSERT(!Collation::isSpecialCE32(ce32)); - if(CollationBuilder::isTempCE32(ce32)) { - // retain case bits - return finalCEs[CollationBuilder::indexFromTempCE32(ce32)] | ((ce32 & 0xc0) << 8); - } else { - return Collation::NO_CE; - } - } - virtual int64_t modifyCE(int64_t ce) const override { - if(CollationBuilder::isTempCE(ce)) { - // retain case bits - return finalCEs[CollationBuilder::indexFromTempCE(ce)] | (ce & 0xc000); - } else { - return Collation::NO_CE; - } - } - -private: - const int64_t *finalCEs; -}; - -CEFinalizer::~CEFinalizer() {} - -void -CollationBuilder::finalizeCEs(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - LocalPointer newBuilder(new CollationDataBuilder(icu4xMode, errorCode), errorCode); - if(U_FAILURE(errorCode)) { - return; - } - newBuilder->initForTailoring(baseData, errorCode); - CEFinalizer finalizer(nodes.getBuffer()); - newBuilder->copyFrom(*dataBuilder, finalizer, errorCode); - if(U_FAILURE(errorCode)) { return; } - delete dataBuilder; - dataBuilder = newBuilder.orphan(); -} - -int32_t -CollationBuilder::ceStrength(int64_t ce) { - return - isTempCE(ce) ? strengthFromTempCE(ce) : - (ce & INT64_C(0xff00000000000000)) != 0 ? UCOL_PRIMARY : - ((uint32_t)ce & 0xff000000) != 0 ? UCOL_SECONDARY : - ce != 0 ? UCOL_TERTIARY : - UCOL_IDENTICAL; -} - -U_NAMESPACE_END - -U_NAMESPACE_USE - -U_CAPI UCollator * U_EXPORT2 -ucol_openRules(const UChar *rules, int32_t rulesLength, - UColAttributeValue normalizationMode, UCollationStrength strength, - UParseError *parseError, UErrorCode *pErrorCode) { - if(U_FAILURE(*pErrorCode)) { return NULL; } - if(rules == NULL && rulesLength != 0) { - *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - RuleBasedCollator *coll = new RuleBasedCollator(); - if(coll == NULL) { - *pErrorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - UnicodeString r((UBool)(rulesLength < 0), rules, rulesLength); - coll->internalBuildTailoring(r, strength, normalizationMode, parseError, NULL, *pErrorCode); - if(U_FAILURE(*pErrorCode)) { - delete coll; - return NULL; - } - return coll->toUCollator(); -} - -static const int32_t internalBufferSize = 512; - -// The @internal ucol_getUnsafeSet() was moved here from ucol_sit.cpp -// because it calls UnicodeSet "builder" code that depends on all Unicode properties, -// and the rest of the collation "runtime" code only depends on normalization. -// This function is not related to the collation builder, -// but it did not seem worth moving it into its own .cpp file, -// nor rewriting it to use lower-level UnicodeSet and Normalizer2Impl methods. -U_CAPI int32_t U_EXPORT2 -ucol_getUnsafeSet( const UCollator *coll, - USet *unsafe, - UErrorCode *status) -{ - UChar buffer[internalBufferSize]; - int32_t len = 0; - - uset_clear(unsafe); - - // cccpattern = "[[:^tccc=0:][:^lccc=0:]]", unfortunately variant - static const UChar cccpattern[25] = { 0x5b, 0x5b, 0x3a, 0x5e, 0x74, 0x63, 0x63, 0x63, 0x3d, 0x30, 0x3a, 0x5d, - 0x5b, 0x3a, 0x5e, 0x6c, 0x63, 0x63, 0x63, 0x3d, 0x30, 0x3a, 0x5d, 0x5d, 0x00 }; - - // add chars that fail the fcd check - uset_applyPattern(unsafe, cccpattern, 24, USET_IGNORE_SPACE, status); - - // add lead/trail surrogates - // (trail surrogates should need to be unsafe only if the caller tests for UTF-16 code *units*, - // not when testing code *points*) - uset_addRange(unsafe, 0xd800, 0xdfff); - - USet *contractions = uset_open(0,0); - - int32_t i = 0, j = 0; - ucol_getContractionsAndExpansions(coll, contractions, NULL, false, status); - int32_t contsSize = uset_size(contractions); - UChar32 c = 0; - // Contraction set consists only of strings - // to get unsafe code points, we need to - // break the strings apart and add them to the unsafe set - for(i = 0; i < contsSize; i++) { - len = uset_getItem(contractions, i, NULL, NULL, buffer, internalBufferSize, status); - if(len > 0) { - j = 0; - while(j < len) { - U16_NEXT(buffer, j, len, c); - if(j < len) { - uset_add(unsafe, c); - } - } - } - } - - uset_close(contractions); - - return uset_size(unsafe); -} - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationbuilder.cpp +* +* (replaced the former ucol_bld.cpp) +* +* created on: 2013may06 +* created by: Markus W. Scherer +*/ + +#ifdef DEBUG_COLLATION_BUILDER +#include +#endif + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/caniter.h" +#include "unicode/normalizer2.h" +#include "unicode/tblcoll.h" +#include "unicode/parseerr.h" +#include "unicode/uchar.h" +#include "unicode/ucol.h" +#include "unicode/unistr.h" +#include "unicode/usetiter.h" +#include "unicode/utf16.h" +#include "unicode/uversion.h" +#include "cmemory.h" +#include "collation.h" +#include "collationbuilder.h" +#include "collationdata.h" +#include "collationdatabuilder.h" +#include "collationfastlatin.h" +#include "collationroot.h" +#include "collationrootelements.h" +#include "collationruleparser.h" +#include "collationsettings.h" +#include "collationtailoring.h" +#include "collationweights.h" +#include "normalizer2impl.h" +#include "uassert.h" +#include "ucol_imp.h" +#include "utf16collationiterator.h" + +U_NAMESPACE_BEGIN + +namespace { + +class BundleImporter : public CollationRuleParser::Importer { +public: + BundleImporter() {} + virtual ~BundleImporter(); + virtual void getRules( + const char *localeID, const char *collationType, + UnicodeString &rules, + const char *&errorReason, UErrorCode &errorCode) override; +}; + +BundleImporter::~BundleImporter() {} + +void +BundleImporter::getRules( + const char *localeID, const char *collationType, + UnicodeString &rules, + const char *& /*errorReason*/, UErrorCode &errorCode) { + CollationLoader::loadRules(localeID, collationType, rules, errorCode); +} + +} // namespace + +// RuleBasedCollator implementation ---------------------------------------- *** + +// These methods are here, rather than in rulebasedcollator.cpp, +// for modularization: +// Most code using Collator does not need to build a Collator from rules. +// By moving these constructors and helper methods to a separate file, +// most code will not have a static dependency on the builder code. + +RuleBasedCollator::RuleBasedCollator() + : data(nullptr), + settings(nullptr), + tailoring(nullptr), + cacheEntry(nullptr), + validLocale(""), + explicitlySetAttributes(0), + actualLocaleIsSameAsValid(false) { +} + +RuleBasedCollator::RuleBasedCollator(const UnicodeString &rules, UErrorCode &errorCode) + : data(nullptr), + settings(nullptr), + tailoring(nullptr), + cacheEntry(nullptr), + validLocale(""), + explicitlySetAttributes(0), + actualLocaleIsSameAsValid(false) { + internalBuildTailoring(rules, UCOL_DEFAULT, UCOL_DEFAULT, nullptr, nullptr, errorCode); +} + +RuleBasedCollator::RuleBasedCollator(const UnicodeString &rules, ECollationStrength strength, + UErrorCode &errorCode) + : data(nullptr), + settings(nullptr), + tailoring(nullptr), + cacheEntry(nullptr), + validLocale(""), + explicitlySetAttributes(0), + actualLocaleIsSameAsValid(false) { + internalBuildTailoring(rules, strength, UCOL_DEFAULT, nullptr, nullptr, errorCode); +} + +RuleBasedCollator::RuleBasedCollator(const UnicodeString &rules, + UColAttributeValue decompositionMode, + UErrorCode &errorCode) + : data(nullptr), + settings(nullptr), + tailoring(nullptr), + cacheEntry(nullptr), + validLocale(""), + explicitlySetAttributes(0), + actualLocaleIsSameAsValid(false) { + internalBuildTailoring(rules, UCOL_DEFAULT, decompositionMode, nullptr, nullptr, errorCode); +} + +RuleBasedCollator::RuleBasedCollator(const UnicodeString &rules, + ECollationStrength strength, + UColAttributeValue decompositionMode, + UErrorCode &errorCode) + : data(nullptr), + settings(nullptr), + tailoring(nullptr), + cacheEntry(nullptr), + validLocale(""), + explicitlySetAttributes(0), + actualLocaleIsSameAsValid(false) { + internalBuildTailoring(rules, strength, decompositionMode, nullptr, nullptr, errorCode); +} + +RuleBasedCollator::RuleBasedCollator(const UnicodeString &rules, + UParseError &parseError, UnicodeString &reason, + UErrorCode &errorCode) + : data(nullptr), + settings(nullptr), + tailoring(nullptr), + cacheEntry(nullptr), + validLocale(""), + explicitlySetAttributes(0), + actualLocaleIsSameAsValid(false) { + internalBuildTailoring(rules, UCOL_DEFAULT, UCOL_DEFAULT, &parseError, &reason, errorCode); +} + +void +RuleBasedCollator::internalBuildTailoring(const UnicodeString &rules, + int32_t strength, + UColAttributeValue decompositionMode, + UParseError *outParseError, UnicodeString *outReason, + UErrorCode &errorCode) { + const CollationTailoring *base = CollationRoot::getRoot(errorCode); + if(U_FAILURE(errorCode)) { return; } + if(outReason != nullptr) { outReason->remove(); } + CollationBuilder builder(base, errorCode); + UVersionInfo noVersion = { 0, 0, 0, 0 }; + BundleImporter importer; + LocalPointer t(builder.parseAndBuild(rules, noVersion, + &importer, + outParseError, errorCode)); + if(U_FAILURE(errorCode)) { + const char *reason = builder.getErrorReason(); + if(reason != nullptr && outReason != nullptr) { + *outReason = UnicodeString(reason, -1, US_INV); + } + return; + } + t->actualLocale.setToBogus(); + adoptTailoring(t.orphan(), errorCode); + // Set attributes after building the collator, + // to keep the default settings consistent with the rule string. + if(strength != UCOL_DEFAULT) { + setAttribute(UCOL_STRENGTH, (UColAttributeValue)strength, errorCode); + } + if(decompositionMode != UCOL_DEFAULT) { + setAttribute(UCOL_NORMALIZATION_MODE, decompositionMode, errorCode); + } +} + +// CollationBuilder implementation ----------------------------------------- *** + +CollationBuilder::CollationBuilder(const CollationTailoring *b, UBool icu4xMode, UErrorCode &errorCode) + : nfd(*Normalizer2::getNFDInstance(errorCode)), + fcd(*Normalizer2Factory::getFCDInstance(errorCode)), + nfcImpl(*Normalizer2Factory::getNFCImpl(errorCode)), + base(b), + baseData(b->data), + rootElements(b->data->rootElements, b->data->rootElementsLength), + variableTop(0), + dataBuilder(new CollationDataBuilder(icu4xMode, errorCode)), fastLatinEnabled(true), + icu4xMode(icu4xMode), + errorReason(nullptr), + cesLength(0), + rootPrimaryIndexes(errorCode), nodes(errorCode) { + nfcImpl.ensureCanonIterData(errorCode); + if(U_FAILURE(errorCode)) { + errorReason = "CollationBuilder fields initialization failed"; + return; + } + if(dataBuilder == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + dataBuilder->initForTailoring(baseData, errorCode); + if(U_FAILURE(errorCode)) { + errorReason = "CollationBuilder initialization failed"; + } +} + +CollationBuilder::CollationBuilder(const CollationTailoring *b, UErrorCode &errorCode) + : CollationBuilder(b, false, errorCode) +{} + +CollationBuilder::~CollationBuilder() { + delete dataBuilder; +} + +CollationTailoring * +CollationBuilder::parseAndBuild(const UnicodeString &ruleString, + const UVersionInfo rulesVersion, + CollationRuleParser::Importer *importer, + UParseError *outParseError, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return nullptr; } + if(baseData->rootElements == nullptr) { + errorCode = U_MISSING_RESOURCE_ERROR; + errorReason = "missing root elements data, tailoring not supported"; + return nullptr; + } + LocalPointer tailoring(new CollationTailoring(base->settings)); + if(tailoring.isNull() || tailoring->isBogus()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + CollationRuleParser parser(baseData, errorCode); + if(U_FAILURE(errorCode)) { return nullptr; } + // Note: This always bases &[last variable] and &[first regular] + // on the root collator's maxVariable/variableTop. + // If we wanted this to change after [maxVariable x], then we would keep + // the tailoring.settings pointer here and read its variableTop when we need it. + // See http://unicode.org/cldr/trac/ticket/6070 + variableTop = base->settings->variableTop; + parser.setSink(this); + parser.setImporter(importer); + CollationSettings &ownedSettings = *SharedObject::copyOnWrite(tailoring->settings); + parser.parse(ruleString, ownedSettings, outParseError, errorCode); + errorReason = parser.getErrorReason(); + if(U_FAILURE(errorCode)) { return nullptr; } + if(dataBuilder->hasMappings()) { + makeTailoredCEs(errorCode); + if (!icu4xMode) { + closeOverComposites(errorCode); + } + finalizeCEs(errorCode); + if (!icu4xMode) { + // Copy all of ASCII, and Latin-1 letters, into each tailoring. + optimizeSet.add(0, 0x7f); + optimizeSet.add(0xc0, 0xff); + // Hangul is decomposed on the fly during collation, + // and the tailoring data is always built with HANGUL_TAG specials. + optimizeSet.remove(Hangul::HANGUL_BASE, Hangul::HANGUL_END); + dataBuilder->optimize(optimizeSet, errorCode); + } + tailoring->ensureOwnedData(errorCode); + if(U_FAILURE(errorCode)) { return nullptr; } + if(fastLatinEnabled) { dataBuilder->enableFastLatin(); } + dataBuilder->build(*tailoring->ownedData, errorCode); + tailoring->builder = dataBuilder; + dataBuilder = nullptr; + } else { + tailoring->data = baseData; + } + if(U_FAILURE(errorCode)) { return nullptr; } + ownedSettings.fastLatinOptions = CollationFastLatin::getOptions( + tailoring->data, ownedSettings, + ownedSettings.fastLatinPrimaries, UPRV_LENGTHOF(ownedSettings.fastLatinPrimaries)); + tailoring->rules = ruleString; + tailoring->rules.getTerminatedBuffer(); // ensure NUL-termination + tailoring->setVersion(base->version, rulesVersion); + return tailoring.orphan(); +} + +void +CollationBuilder::addReset(int32_t strength, const UnicodeString &str, + const char *&parserErrorReason, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + U_ASSERT(!str.isEmpty()); + if(str.charAt(0) == CollationRuleParser::POS_LEAD) { + ces[0] = getSpecialResetPosition(str, parserErrorReason, errorCode); + cesLength = 1; + if(U_FAILURE(errorCode)) { return; } + U_ASSERT((ces[0] & Collation::CASE_AND_QUATERNARY_MASK) == 0); + } else { + // normal reset to a character or string + UnicodeString nfdString = nfd.normalize(str, errorCode); + if(U_FAILURE(errorCode)) { + parserErrorReason = "normalizing the reset position"; + return; + } + cesLength = dataBuilder->getCEs(nfdString, ces, 0); + if(cesLength > Collation::MAX_EXPANSION_LENGTH) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + parserErrorReason = "reset position maps to too many collation elements (more than 31)"; + return; + } + } + if(strength == UCOL_IDENTICAL) { return; } // simple reset-at-position + + // &[before strength]position + U_ASSERT(UCOL_PRIMARY <= strength && strength <= UCOL_TERTIARY); + int32_t index = findOrInsertNodeForCEs(strength, parserErrorReason, errorCode); + if(U_FAILURE(errorCode)) { return; } + + int64_t node = nodes.elementAti(index); + // If the index is for a "weaker" node, + // then skip backwards over this and further "weaker" nodes. + while(strengthFromNode(node) > strength) { + index = previousIndexFromNode(node); + node = nodes.elementAti(index); + } + + // Find or insert a node whose index we will put into a temporary CE. + if(strengthFromNode(node) == strength && isTailoredNode(node)) { + // Reset to just before this same-strength tailored node. + index = previousIndexFromNode(node); + } else if(strength == UCOL_PRIMARY) { + // root primary node (has no previous index) + uint32_t p = weight32FromNode(node); + if(p == 0) { + errorCode = U_UNSUPPORTED_ERROR; + parserErrorReason = "reset primary-before ignorable not possible"; + return; + } + if(p <= rootElements.getFirstPrimary()) { + // There is no primary gap between ignorables and the space-first-primary. + errorCode = U_UNSUPPORTED_ERROR; + parserErrorReason = "reset primary-before first non-ignorable not supported"; + return; + } + if(p == Collation::FIRST_TRAILING_PRIMARY) { + // We do not support tailoring to an unassigned-implicit CE. + errorCode = U_UNSUPPORTED_ERROR; + parserErrorReason = "reset primary-before [first trailing] not supported"; + return; + } + p = rootElements.getPrimaryBefore(p, baseData->isCompressiblePrimary(p)); + index = findOrInsertNodeForPrimary(p, errorCode); + // Go to the last node in this list: + // Tailor after the last node between adjacent root nodes. + for(;;) { + node = nodes.elementAti(index); + int32_t nextIndex = nextIndexFromNode(node); + if(nextIndex == 0) { break; } + index = nextIndex; + } + } else { + // &[before 2] or &[before 3] + index = findCommonNode(index, UCOL_SECONDARY); + if(strength >= UCOL_TERTIARY) { + index = findCommonNode(index, UCOL_TERTIARY); + } + // findCommonNode() stayed on the stronger node or moved to + // an explicit common-weight node of the reset-before strength. + node = nodes.elementAti(index); + if(strengthFromNode(node) == strength) { + // Found a same-strength node with an explicit weight. + uint32_t weight16 = weight16FromNode(node); + if(weight16 == 0) { + errorCode = U_UNSUPPORTED_ERROR; + if(strength == UCOL_SECONDARY) { + parserErrorReason = "reset secondary-before secondary ignorable not possible"; + } else { + parserErrorReason = "reset tertiary-before completely ignorable not possible"; + } + return; + } + U_ASSERT(weight16 > Collation::BEFORE_WEIGHT16); + // Reset to just before this node. + // Insert the preceding same-level explicit weight if it is not there already. + // Which explicit weight immediately precedes this one? + weight16 = getWeight16Before(index, node, strength); + // Does this preceding weight have a node? + uint32_t previousWeight16; + int32_t previousIndex = previousIndexFromNode(node); + for(int32_t i = previousIndex;; i = previousIndexFromNode(node)) { + node = nodes.elementAti(i); + int32_t previousStrength = strengthFromNode(node); + if(previousStrength < strength) { + U_ASSERT(weight16 >= Collation::COMMON_WEIGHT16 || i == previousIndex); + // Either the reset element has an above-common weight and + // the parent node provides the implied common weight, + // or the reset element has a weight<=common in the node + // right after the parent, and we need to insert the preceding weight. + previousWeight16 = Collation::COMMON_WEIGHT16; + break; + } else if(previousStrength == strength && !isTailoredNode(node)) { + previousWeight16 = weight16FromNode(node); + break; + } + // Skip weaker nodes and same-level tailored nodes. + } + if(previousWeight16 == weight16) { + // The preceding weight has a node, + // maybe with following weaker or tailored nodes. + // Reset to the last of them. + index = previousIndex; + } else { + // Insert a node with the preceding weight, reset to that. + node = nodeFromWeight16(weight16) | nodeFromStrength(strength); + index = insertNodeBetween(previousIndex, index, node, errorCode); + } + } else { + // Found a stronger node with implied strength-common weight. + uint32_t weight16 = getWeight16Before(index, node, strength); + index = findOrInsertWeakNode(index, weight16, strength, errorCode); + } + // Strength of the temporary CE = strength of its reset position. + // Code above raises an error if the before-strength is stronger. + strength = ceStrength(ces[cesLength - 1]); + } + if(U_FAILURE(errorCode)) { + parserErrorReason = "inserting reset position for &[before n]"; + return; + } + ces[cesLength - 1] = tempCEFromIndexAndStrength(index, strength); +} + +uint32_t +CollationBuilder::getWeight16Before(int32_t index, int64_t node, int32_t level) { + U_ASSERT(strengthFromNode(node) < level || !isTailoredNode(node)); + // Collect the root CE weights if this node is for a root CE. + // If it is not, then return the low non-primary boundary for a tailored CE. + uint32_t t; + if(strengthFromNode(node) == UCOL_TERTIARY) { + t = weight16FromNode(node); + } else { + t = Collation::COMMON_WEIGHT16; // Stronger node with implied common weight. + } + while(strengthFromNode(node) > UCOL_SECONDARY) { + index = previousIndexFromNode(node); + node = nodes.elementAti(index); + } + if(isTailoredNode(node)) { + return Collation::BEFORE_WEIGHT16; + } + uint32_t s; + if(strengthFromNode(node) == UCOL_SECONDARY) { + s = weight16FromNode(node); + } else { + s = Collation::COMMON_WEIGHT16; // Stronger node with implied common weight. + } + while(strengthFromNode(node) > UCOL_PRIMARY) { + index = previousIndexFromNode(node); + node = nodes.elementAti(index); + } + if(isTailoredNode(node)) { + return Collation::BEFORE_WEIGHT16; + } + // [p, s, t] is a root CE. Return the preceding weight for the requested level. + uint32_t p = weight32FromNode(node); + uint32_t weight16; + if(level == UCOL_SECONDARY) { + weight16 = rootElements.getSecondaryBefore(p, s); + } else { + weight16 = rootElements.getTertiaryBefore(p, s, t); + U_ASSERT((weight16 & ~Collation::ONLY_TERTIARY_MASK) == 0); + } + return weight16; +} + +int64_t +CollationBuilder::getSpecialResetPosition(const UnicodeString &str, + const char *&parserErrorReason, UErrorCode &errorCode) { + U_ASSERT(str.length() == 2); + int64_t ce; + int32_t strength = UCOL_PRIMARY; + UBool isBoundary = false; + UChar32 pos = str.charAt(1) - CollationRuleParser::POS_BASE; + U_ASSERT(0 <= pos && pos <= CollationRuleParser::LAST_TRAILING); + switch(pos) { + case CollationRuleParser::FIRST_TERTIARY_IGNORABLE: + // Quaternary CEs are not supported. + // Non-zero quaternary weights are possible only on tertiary or stronger CEs. + return 0; + case CollationRuleParser::LAST_TERTIARY_IGNORABLE: + return 0; + case CollationRuleParser::FIRST_SECONDARY_IGNORABLE: { + // Look for a tailored tertiary node after [0, 0, 0]. + int32_t index = findOrInsertNodeForRootCE(0, UCOL_TERTIARY, errorCode); + if(U_FAILURE(errorCode)) { return 0; } + int64_t node = nodes.elementAti(index); + if((index = nextIndexFromNode(node)) != 0) { + node = nodes.elementAti(index); + U_ASSERT(strengthFromNode(node) <= UCOL_TERTIARY); + if(isTailoredNode(node) && strengthFromNode(node) == UCOL_TERTIARY) { + return tempCEFromIndexAndStrength(index, UCOL_TERTIARY); + } + } + return rootElements.getFirstTertiaryCE(); + // No need to look for nodeHasAnyBefore() on a tertiary node. + } + case CollationRuleParser::LAST_SECONDARY_IGNORABLE: + ce = rootElements.getLastTertiaryCE(); + strength = UCOL_TERTIARY; + break; + case CollationRuleParser::FIRST_PRIMARY_IGNORABLE: { + // Look for a tailored secondary node after [0, 0, *]. + int32_t index = findOrInsertNodeForRootCE(0, UCOL_SECONDARY, errorCode); + if(U_FAILURE(errorCode)) { return 0; } + int64_t node = nodes.elementAti(index); + while((index = nextIndexFromNode(node)) != 0) { + node = nodes.elementAti(index); + strength = strengthFromNode(node); + if(strength < UCOL_SECONDARY) { break; } + if(strength == UCOL_SECONDARY) { + if(isTailoredNode(node)) { + if(nodeHasBefore3(node)) { + index = nextIndexFromNode(nodes.elementAti(nextIndexFromNode(node))); + U_ASSERT(isTailoredNode(nodes.elementAti(index))); + } + return tempCEFromIndexAndStrength(index, UCOL_SECONDARY); + } else { + break; + } + } + } + ce = rootElements.getFirstSecondaryCE(); + strength = UCOL_SECONDARY; + break; + } + case CollationRuleParser::LAST_PRIMARY_IGNORABLE: + ce = rootElements.getLastSecondaryCE(); + strength = UCOL_SECONDARY; + break; + case CollationRuleParser::FIRST_VARIABLE: + ce = rootElements.getFirstPrimaryCE(); + isBoundary = true; // FractionalUCA.txt: FDD1 00A0, SPACE first primary + break; + case CollationRuleParser::LAST_VARIABLE: + ce = rootElements.lastCEWithPrimaryBefore(variableTop + 1); + break; + case CollationRuleParser::FIRST_REGULAR: + ce = rootElements.firstCEWithPrimaryAtLeast(variableTop + 1); + isBoundary = true; // FractionalUCA.txt: FDD1 263A, SYMBOL first primary + break; + case CollationRuleParser::LAST_REGULAR: + // Use the Hani-first-primary rather than the actual last "regular" CE before it, + // for backward compatibility with behavior before the introduction of + // script-first-primary CEs in the root collator. + ce = rootElements.firstCEWithPrimaryAtLeast( + baseData->getFirstPrimaryForGroup(USCRIPT_HAN)); + break; + case CollationRuleParser::FIRST_IMPLICIT: + ce = baseData->getSingleCE(0x4e00, errorCode); + break; + case CollationRuleParser::LAST_IMPLICIT: + // We do not support tailoring to an unassigned-implicit CE. + errorCode = U_UNSUPPORTED_ERROR; + parserErrorReason = "reset to [last implicit] not supported"; + return 0; + case CollationRuleParser::FIRST_TRAILING: + ce = Collation::makeCE(Collation::FIRST_TRAILING_PRIMARY); + isBoundary = true; // trailing first primary (there is no mapping for it) + break; + case CollationRuleParser::LAST_TRAILING: + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + parserErrorReason = "LDML forbids tailoring to U+FFFF"; + return 0; + default: + UPRV_UNREACHABLE_EXIT; + } + + int32_t index = findOrInsertNodeForRootCE(ce, strength, errorCode); + if(U_FAILURE(errorCode)) { return 0; } + int64_t node = nodes.elementAti(index); + if((pos & 1) == 0) { + // even pos = [first xyz] + if(!nodeHasAnyBefore(node) && isBoundary) { + // A first primary boundary is artificially added to FractionalUCA.txt. + // It is reachable via its special contraction, but is not normally used. + // Find the first character tailored after the boundary CE, + // or the first real root CE after it. + if((index = nextIndexFromNode(node)) != 0) { + // If there is a following node, then it must be tailored + // because there are no root CEs with a boundary primary + // and non-common secondary/tertiary weights. + node = nodes.elementAti(index); + U_ASSERT(isTailoredNode(node)); + ce = tempCEFromIndexAndStrength(index, strength); + } else { + U_ASSERT(strength == UCOL_PRIMARY); + uint32_t p = (uint32_t)(ce >> 32); + int32_t pIndex = rootElements.findPrimary(p); + UBool isCompressible = baseData->isCompressiblePrimary(p); + p = rootElements.getPrimaryAfter(p, pIndex, isCompressible); + ce = Collation::makeCE(p); + index = findOrInsertNodeForRootCE(ce, UCOL_PRIMARY, errorCode); + if(U_FAILURE(errorCode)) { return 0; } + node = nodes.elementAti(index); + } + } + if(nodeHasAnyBefore(node)) { + // Get the first node that was tailored before this one at a weaker strength. + if(nodeHasBefore2(node)) { + index = nextIndexFromNode(nodes.elementAti(nextIndexFromNode(node))); + node = nodes.elementAti(index); + } + if(nodeHasBefore3(node)) { + index = nextIndexFromNode(nodes.elementAti(nextIndexFromNode(node))); + } + U_ASSERT(isTailoredNode(nodes.elementAti(index))); + ce = tempCEFromIndexAndStrength(index, strength); + } + } else { + // odd pos = [last xyz] + // Find the last node that was tailored after the [last xyz] + // at a strength no greater than the position's strength. + for(;;) { + int32_t nextIndex = nextIndexFromNode(node); + if(nextIndex == 0) { break; } + int64_t nextNode = nodes.elementAti(nextIndex); + if(strengthFromNode(nextNode) < strength) { break; } + index = nextIndex; + node = nextNode; + } + // Do not make a temporary CE for a root node. + // This last node might be the node for the root CE itself, + // or a node with a common secondary or tertiary weight. + if(isTailoredNode(node)) { + ce = tempCEFromIndexAndStrength(index, strength); + } + } + return ce; +} + +void +CollationBuilder::addRelation(int32_t strength, const UnicodeString &prefix, + const UnicodeString &str, const UnicodeString &extension, + const char *&parserErrorReason, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + UnicodeString nfdPrefix; + if(!prefix.isEmpty()) { + nfd.normalize(prefix, nfdPrefix, errorCode); + if(U_FAILURE(errorCode)) { + parserErrorReason = "normalizing the relation prefix"; + return; + } + } + UnicodeString nfdString = nfd.normalize(str, errorCode); + if(U_FAILURE(errorCode)) { + parserErrorReason = "normalizing the relation string"; + return; + } + + // The runtime code decomposes Hangul syllables on the fly, + // with recursive processing but without making the Jamo pieces visible for matching. + // It does not work with certain types of contextual mappings. + int32_t nfdLength = nfdString.length(); + if(nfdLength >= 2) { + char16_t c = nfdString.charAt(0); + if(Hangul::isJamoL(c) || Hangul::isJamoV(c)) { + // While handling a Hangul syllable, contractions starting with Jamo L or V + // would not see the following Jamo of that syllable. + errorCode = U_UNSUPPORTED_ERROR; + parserErrorReason = "contractions starting with conjoining Jamo L or V not supported"; + return; + } + c = nfdString.charAt(nfdLength - 1); + if(Hangul::isJamoL(c) || + (Hangul::isJamoV(c) && Hangul::isJamoL(nfdString.charAt(nfdLength - 2)))) { + // A contraction ending with Jamo L or L+V would require + // generating Hangul syllables in addTailComposites() (588 for a Jamo L), + // or decomposing a following Hangul syllable on the fly, during contraction matching. + errorCode = U_UNSUPPORTED_ERROR; + parserErrorReason = "contractions ending with conjoining Jamo L or L+V not supported"; + return; + } + // A Hangul syllable completely inside a contraction is ok. + } + // Note: If there is a prefix, then the parser checked that + // both the prefix and the string begin with NFC boundaries (not Jamo V or T). + // Therefore: prefix.isEmpty() || !isJamoVOrT(nfdString.charAt(0)) + // (While handling a Hangul syllable, prefixes on Jamo V or T + // would not see the previous Jamo of that syllable.) + + if(strength != UCOL_IDENTICAL) { + // Find the node index after which we insert the new tailored node. + int32_t index = findOrInsertNodeForCEs(strength, parserErrorReason, errorCode); + U_ASSERT(cesLength > 0); + int64_t ce = ces[cesLength - 1]; + if(strength == UCOL_PRIMARY && !isTempCE(ce) && (uint32_t)(ce >> 32) == 0) { + // There is no primary gap between ignorables and the space-first-primary. + errorCode = U_UNSUPPORTED_ERROR; + parserErrorReason = "tailoring primary after ignorables not supported"; + return; + } + if(strength == UCOL_QUATERNARY && ce == 0) { + // The CE data structure does not support non-zero quaternary weights + // on tertiary ignorables. + errorCode = U_UNSUPPORTED_ERROR; + parserErrorReason = "tailoring quaternary after tertiary ignorables not supported"; + return; + } + // Insert the new tailored node. + index = insertTailoredNodeAfter(index, strength, errorCode); + if(U_FAILURE(errorCode)) { + parserErrorReason = "modifying collation elements"; + return; + } + // Strength of the temporary CE: + // The new relation may yield a stronger CE but not a weaker one. + int32_t tempStrength = ceStrength(ce); + if(strength < tempStrength) { tempStrength = strength; } + ces[cesLength - 1] = tempCEFromIndexAndStrength(index, tempStrength); + } + + setCaseBits(nfdString, parserErrorReason, errorCode); + if(U_FAILURE(errorCode)) { return; } + + int32_t cesLengthBeforeExtension = cesLength; + if(!extension.isEmpty()) { + UnicodeString nfdExtension = nfd.normalize(extension, errorCode); + if(U_FAILURE(errorCode)) { + parserErrorReason = "normalizing the relation extension"; + return; + } + cesLength = dataBuilder->getCEs(nfdExtension, ces, cesLength); + if(cesLength > Collation::MAX_EXPANSION_LENGTH) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + parserErrorReason = + "extension string adds too many collation elements (more than 31 total)"; + return; + } + } + uint32_t ce32 = Collation::UNASSIGNED_CE32; + if(!icu4xMode && (prefix != nfdPrefix || str != nfdString) && + !ignorePrefix(prefix, errorCode) && !ignoreString(str, errorCode)) { + // Map from the original input to the CEs. + // We do this in case the canonical closure is incomplete, + // so that it is possible to explicitly provide the missing mappings. + ce32 = addIfDifferent(prefix, str, ces, cesLength, ce32, errorCode); + } + if (!icu4xMode) { + addWithClosure(nfdPrefix, nfdString, ces, cesLength, ce32, errorCode); + } else { + addIfDifferent(nfdPrefix, nfdString, ces, cesLength, ce32, errorCode); + } + if(U_FAILURE(errorCode)) { + parserErrorReason = "writing collation elements"; + return; + } + cesLength = cesLengthBeforeExtension; +} + +int32_t +CollationBuilder::findOrInsertNodeForCEs(int32_t strength, const char *&parserErrorReason, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + U_ASSERT(UCOL_PRIMARY <= strength && strength <= UCOL_QUATERNARY); + + // Find the last CE that is at least as "strong" as the requested difference. + // Note: Stronger is smaller (UCOL_PRIMARY=0). + int64_t ce; + for(;; --cesLength) { + if(cesLength == 0) { + ce = ces[0] = 0; + cesLength = 1; + break; + } else { + ce = ces[cesLength - 1]; + } + if(ceStrength(ce) <= strength) { break; } + } + + if(isTempCE(ce)) { + // No need to findCommonNode() here for lower levels + // because insertTailoredNodeAfter() will do that anyway. + return indexFromTempCE(ce); + } + + // root CE + if((uint8_t)(ce >> 56) == Collation::UNASSIGNED_IMPLICIT_BYTE) { + errorCode = U_UNSUPPORTED_ERROR; + parserErrorReason = "tailoring relative to an unassigned code point not supported"; + return 0; + } + return findOrInsertNodeForRootCE(ce, strength, errorCode); +} + +int32_t +CollationBuilder::findOrInsertNodeForRootCE(int64_t ce, int32_t strength, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + U_ASSERT((uint8_t)(ce >> 56) != Collation::UNASSIGNED_IMPLICIT_BYTE); + + // Find or insert the node for each of the root CE's weights, + // down to the requested level/strength. + // Root CEs must have common=zero quaternary weights (for which we never insert any nodes). + U_ASSERT((ce & 0xc0) == 0); + int32_t index = findOrInsertNodeForPrimary((uint32_t)(ce >> 32), errorCode); + if(strength >= UCOL_SECONDARY) { + uint32_t lower32 = (uint32_t)ce; + index = findOrInsertWeakNode(index, lower32 >> 16, UCOL_SECONDARY, errorCode); + if(strength >= UCOL_TERTIARY) { + index = findOrInsertWeakNode(index, lower32 & Collation::ONLY_TERTIARY_MASK, + UCOL_TERTIARY, errorCode); + } + } + return index; +} + +namespace { + +/** + * Like Java Collections.binarySearch(List, key, Comparator). + * + * @return the index>=0 where the item was found, + * or the index<0 for inserting the string at ~index in sorted order + * (index into rootPrimaryIndexes) + */ +int32_t +binarySearchForRootPrimaryNode(const int32_t *rootPrimaryIndexes, int32_t length, + const int64_t *nodes, uint32_t p) { + if(length == 0) { return ~0; } + int32_t start = 0; + int32_t limit = length; + for (;;) { + int32_t i = (start + limit) / 2; + int64_t node = nodes[rootPrimaryIndexes[i]]; + uint32_t nodePrimary = (uint32_t)(node >> 32); // weight32FromNode(node) + if (p == nodePrimary) { + return i; + } else if (p < nodePrimary) { + if (i == start) { + return ~start; // insert s before i + } + limit = i; + } else { + if (i == start) { + return ~(start + 1); // insert s after i + } + start = i; + } + } +} + +} // namespace + +int32_t +CollationBuilder::findOrInsertNodeForPrimary(uint32_t p, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + + int32_t rootIndex = binarySearchForRootPrimaryNode( + rootPrimaryIndexes.getBuffer(), rootPrimaryIndexes.size(), nodes.getBuffer(), p); + if(rootIndex >= 0) { + return rootPrimaryIndexes.elementAti(rootIndex); + } else { + // Start a new list of nodes with this primary. + int32_t index = nodes.size(); + nodes.addElement(nodeFromWeight32(p), errorCode); + rootPrimaryIndexes.insertElementAt(index, ~rootIndex, errorCode); + return index; + } +} + +int32_t +CollationBuilder::findOrInsertWeakNode(int32_t index, uint32_t weight16, int32_t level, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + U_ASSERT(0 <= index && index < nodes.size()); + U_ASSERT(UCOL_SECONDARY <= level && level <= UCOL_TERTIARY); + + if(weight16 == Collation::COMMON_WEIGHT16) { + return findCommonNode(index, level); + } + + // If this will be the first below-common weight for the parent node, + // then we will also need to insert a common weight after it. + int64_t node = nodes.elementAti(index); + U_ASSERT(strengthFromNode(node) < level); // parent node is stronger + if(weight16 != 0 && weight16 < Collation::COMMON_WEIGHT16) { + int32_t hasThisLevelBefore = level == UCOL_SECONDARY ? HAS_BEFORE2 : HAS_BEFORE3; + if((node & hasThisLevelBefore) == 0) { + // The parent node has an implied level-common weight. + int64_t commonNode = + nodeFromWeight16(Collation::COMMON_WEIGHT16) | nodeFromStrength(level); + if(level == UCOL_SECONDARY) { + // Move the HAS_BEFORE3 flag from the parent node + // to the new secondary common node. + commonNode |= node & HAS_BEFORE3; + node &= ~(int64_t)HAS_BEFORE3; + } + nodes.setElementAt(node | hasThisLevelBefore, index); + // Insert below-common-weight node. + int32_t nextIndex = nextIndexFromNode(node); + node = nodeFromWeight16(weight16) | nodeFromStrength(level); + index = insertNodeBetween(index, nextIndex, node, errorCode); + // Insert common-weight node. + insertNodeBetween(index, nextIndex, commonNode, errorCode); + // Return index of below-common-weight node. + return index; + } + } + + // Find the root CE's weight for this level. + // Postpone insertion if not found: + // Insert the new root node before the next stronger node, + // or before the next root node with the same strength and a larger weight. + int32_t nextIndex; + while((nextIndex = nextIndexFromNode(node)) != 0) { + node = nodes.elementAti(nextIndex); + int32_t nextStrength = strengthFromNode(node); + if(nextStrength <= level) { + // Insert before a stronger node. + if(nextStrength < level) { break; } + // nextStrength == level + if(!isTailoredNode(node)) { + uint32_t nextWeight16 = weight16FromNode(node); + if(nextWeight16 == weight16) { + // Found the node for the root CE up to this level. + return nextIndex; + } + // Insert before a node with a larger same-strength weight. + if(nextWeight16 > weight16) { break; } + } + } + // Skip the next node. + index = nextIndex; + } + node = nodeFromWeight16(weight16) | nodeFromStrength(level); + return insertNodeBetween(index, nextIndex, node, errorCode); +} + +int32_t +CollationBuilder::insertTailoredNodeAfter(int32_t index, int32_t strength, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + U_ASSERT(0 <= index && index < nodes.size()); + if(strength >= UCOL_SECONDARY) { + index = findCommonNode(index, UCOL_SECONDARY); + if(strength >= UCOL_TERTIARY) { + index = findCommonNode(index, UCOL_TERTIARY); + } + } + // Postpone insertion: + // Insert the new node before the next one with a strength at least as strong. + int64_t node = nodes.elementAti(index); + int32_t nextIndex; + while((nextIndex = nextIndexFromNode(node)) != 0) { + node = nodes.elementAti(nextIndex); + if(strengthFromNode(node) <= strength) { break; } + // Skip the next node which has a weaker (larger) strength than the new one. + index = nextIndex; + } + node = IS_TAILORED | nodeFromStrength(strength); + return insertNodeBetween(index, nextIndex, node, errorCode); +} + +int32_t +CollationBuilder::insertNodeBetween(int32_t index, int32_t nextIndex, int64_t node, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + U_ASSERT(previousIndexFromNode(node) == 0); + U_ASSERT(nextIndexFromNode(node) == 0); + U_ASSERT(nextIndexFromNode(nodes.elementAti(index)) == nextIndex); + // Append the new node and link it to the existing nodes. + int32_t newIndex = nodes.size(); + node |= nodeFromPreviousIndex(index) | nodeFromNextIndex(nextIndex); + nodes.addElement(node, errorCode); + if(U_FAILURE(errorCode)) { return 0; } + // nodes[index].nextIndex = newIndex + node = nodes.elementAti(index); + nodes.setElementAt(changeNodeNextIndex(node, newIndex), index); + // nodes[nextIndex].previousIndex = newIndex + if(nextIndex != 0) { + node = nodes.elementAti(nextIndex); + nodes.setElementAt(changeNodePreviousIndex(node, newIndex), nextIndex); + } + return newIndex; +} + +int32_t +CollationBuilder::findCommonNode(int32_t index, int32_t strength) const { + U_ASSERT(UCOL_SECONDARY <= strength && strength <= UCOL_TERTIARY); + int64_t node = nodes.elementAti(index); + if(strengthFromNode(node) >= strength) { + // The current node is no stronger. + return index; + } + if(strength == UCOL_SECONDARY ? !nodeHasBefore2(node) : !nodeHasBefore3(node)) { + // The current node implies the strength-common weight. + return index; + } + index = nextIndexFromNode(node); + node = nodes.elementAti(index); + U_ASSERT(!isTailoredNode(node) && strengthFromNode(node) == strength && + weight16FromNode(node) < Collation::COMMON_WEIGHT16); + // Skip to the explicit common node. + do { + index = nextIndexFromNode(node); + node = nodes.elementAti(index); + U_ASSERT(strengthFromNode(node) >= strength); + } while(isTailoredNode(node) || strengthFromNode(node) > strength || + weight16FromNode(node) < Collation::COMMON_WEIGHT16); + U_ASSERT(weight16FromNode(node) == Collation::COMMON_WEIGHT16); + return index; +} + +void +CollationBuilder::setCaseBits(const UnicodeString &nfdString, + const char *&parserErrorReason, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + int32_t numTailoredPrimaries = 0; + for(int32_t i = 0; i < cesLength; ++i) { + if(ceStrength(ces[i]) == UCOL_PRIMARY) { ++numTailoredPrimaries; } + } + // We should not be able to get too many case bits because + // cesLength<=31==MAX_EXPANSION_LENGTH. + // 31 pairs of case bits fit into an int64_t without setting its sign bit. + U_ASSERT(numTailoredPrimaries <= 31); + + int64_t cases = 0; + if(numTailoredPrimaries > 0) { + const char16_t *s = nfdString.getBuffer(); + UTF16CollationIterator baseCEs(baseData, false, s, s, s + nfdString.length()); + int32_t baseCEsLength = baseCEs.fetchCEs(errorCode) - 1; + if(U_FAILURE(errorCode)) { + parserErrorReason = "fetching root CEs for tailored string"; + return; + } + U_ASSERT(baseCEsLength >= 0 && baseCEs.getCE(baseCEsLength) == Collation::NO_CE); + + uint32_t lastCase = 0; + int32_t numBasePrimaries = 0; + for(int32_t i = 0; i < baseCEsLength; ++i) { + int64_t ce = baseCEs.getCE(i); + if((ce >> 32) != 0) { + ++numBasePrimaries; + uint32_t c = ((uint32_t)ce >> 14) & 3; + U_ASSERT(c == 0 || c == 2); // lowercase or uppercase, no mixed case in any base CE + if(numBasePrimaries < numTailoredPrimaries) { + cases |= (int64_t)c << ((numBasePrimaries - 1) * 2); + } else if(numBasePrimaries == numTailoredPrimaries) { + lastCase = c; + } else if(c != lastCase) { + // There are more base primary CEs than tailored primaries. + // Set mixed case if the case bits of the remainder differ. + lastCase = 1; + // Nothing more can change. + break; + } + } + } + if(numBasePrimaries >= numTailoredPrimaries) { + cases |= (int64_t)lastCase << ((numTailoredPrimaries - 1) * 2); + } + } + + for(int32_t i = 0; i < cesLength; ++i) { + int64_t ce = ces[i] & INT64_C(0xffffffffffff3fff); // clear old case bits + int32_t strength = ceStrength(ce); + if(strength == UCOL_PRIMARY) { + ce |= (cases & 3) << 14; + cases >>= 2; + } else if(strength == UCOL_TERTIARY) { + // Tertiary CEs must have uppercase bits. + // See the LDML spec, and comments in class CollationCompare. + ce |= 0x8000; + } + // Tertiary ignorable CEs must have 0 case bits. + // We set 0 case bits for secondary CEs too + // since currently only U+0345 is cased and maps to a secondary CE, + // and it is lowercase. Other secondaries are uncased. + // See [[:Cased:]&[:uca1=:]] where uca1 queries the root primary weight. + ces[i] = ce; + } +} + +void +CollationBuilder::suppressContractions(const UnicodeSet &set, const char *&parserErrorReason, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + dataBuilder->suppressContractions(set, errorCode); + if(U_FAILURE(errorCode)) { + parserErrorReason = "application of [suppressContractions [set]] failed"; + } +} + +void +CollationBuilder::optimize(const UnicodeSet &set, const char *& /* parserErrorReason */, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + optimizeSet.addAll(set); +} + +uint32_t +CollationBuilder::addWithClosure(const UnicodeString &nfdPrefix, const UnicodeString &nfdString, + const int64_t newCEs[], int32_t newCEsLength, uint32_t ce32, + UErrorCode &errorCode) { + // Map from the NFD input to the CEs. + ce32 = addIfDifferent(nfdPrefix, nfdString, newCEs, newCEsLength, ce32, errorCode); + ce32 = addOnlyClosure(nfdPrefix, nfdString, newCEs, newCEsLength, ce32, errorCode); + addTailComposites(nfdPrefix, nfdString, errorCode); + return ce32; +} + +uint32_t +CollationBuilder::addOnlyClosure(const UnicodeString &nfdPrefix, const UnicodeString &nfdString, + const int64_t newCEs[], int32_t newCEsLength, uint32_t ce32, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return ce32; } + + // Map from canonically equivalent input to the CEs. (But not from the all-NFD input.) + if(nfdPrefix.isEmpty()) { + CanonicalIterator stringIter(nfdString, errorCode); + if(U_FAILURE(errorCode)) { return ce32; } + UnicodeString prefix; + for(;;) { + UnicodeString str = stringIter.next(); + if(str.isBogus()) { break; } + if(ignoreString(str, errorCode) || str == nfdString) { continue; } + ce32 = addIfDifferent(prefix, str, newCEs, newCEsLength, ce32, errorCode); + if(U_FAILURE(errorCode)) { return ce32; } + } + } else { + CanonicalIterator prefixIter(nfdPrefix, errorCode); + CanonicalIterator stringIter(nfdString, errorCode); + if(U_FAILURE(errorCode)) { return ce32; } + for(;;) { + UnicodeString prefix = prefixIter.next(); + if(prefix.isBogus()) { break; } + if(ignorePrefix(prefix, errorCode)) { continue; } + UBool samePrefix = prefix == nfdPrefix; + for(;;) { + UnicodeString str = stringIter.next(); + if(str.isBogus()) { break; } + if(ignoreString(str, errorCode) || (samePrefix && str == nfdString)) { continue; } + ce32 = addIfDifferent(prefix, str, newCEs, newCEsLength, ce32, errorCode); + if(U_FAILURE(errorCode)) { return ce32; } + } + stringIter.reset(); + } + } + return ce32; +} + +void +CollationBuilder::addTailComposites(const UnicodeString &nfdPrefix, const UnicodeString &nfdString, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + + // Look for the last starter in the NFD string. + UChar32 lastStarter; + int32_t indexAfterLastStarter = nfdString.length(); + for(;;) { + if(indexAfterLastStarter == 0) { return; } // no starter at all + lastStarter = nfdString.char32At(indexAfterLastStarter - 1); + if(nfd.getCombiningClass(lastStarter) == 0) { break; } + indexAfterLastStarter -= U16_LENGTH(lastStarter); + } + // No closure to Hangul syllables since we decompose them on the fly. + if(Hangul::isJamoL(lastStarter)) { return; } + + // Are there any composites whose decomposition starts with the lastStarter? + // Note: Normalizer2Impl does not currently return start sets for NFC_QC=Maybe characters. + // We might find some more equivalent mappings here if it did. + UnicodeSet composites; + if(!nfcImpl.getCanonStartSet(lastStarter, composites)) { return; } + + UnicodeString decomp; + UnicodeString newNFDString, newString; + int64_t newCEs[Collation::MAX_EXPANSION_LENGTH]; + UnicodeSetIterator iter(composites); + while(iter.next()) { + U_ASSERT(!iter.isString()); + UChar32 composite = iter.getCodepoint(); + nfd.getDecomposition(composite, decomp); + if(!mergeCompositeIntoString(nfdString, indexAfterLastStarter, composite, decomp, + newNFDString, newString, errorCode)) { + continue; + } + int32_t newCEsLength = dataBuilder->getCEs(nfdPrefix, newNFDString, newCEs, 0); + if(newCEsLength > Collation::MAX_EXPANSION_LENGTH) { + // Ignore mappings that we cannot store. + continue; + } + // Note: It is possible that the newCEs do not make use of the mapping + // for which we are adding the tail composites, in which case we might be adding + // unnecessary mappings. + // For example, when we add tail composites for ae^ (^=combining circumflex), + // UCA discontiguous-contraction matching does not find any matches + // for ae_^ (_=any combining diacritic below) *unless* there is also + // a contraction mapping for ae. + // Thus, if there is no ae contraction, then the ae^ mapping is ignored + // while fetching the newCEs for ae_^. + // TODO: Try to detect this effectively. + // (Alternatively, print a warning when prefix contractions are missing.) + + // We do not need an explicit mapping for the NFD strings. + // It is fine if the NFD input collates like this via a sequence of mappings. + // It also saves a little bit of space, and may reduce the set of characters with contractions. + uint32_t ce32 = addIfDifferent(nfdPrefix, newString, + newCEs, newCEsLength, Collation::UNASSIGNED_CE32, errorCode); + if(ce32 != Collation::UNASSIGNED_CE32) { + // was different, was added + addOnlyClosure(nfdPrefix, newNFDString, newCEs, newCEsLength, ce32, errorCode); + } + } +} + +UBool +CollationBuilder::mergeCompositeIntoString(const UnicodeString &nfdString, + int32_t indexAfterLastStarter, + UChar32 composite, const UnicodeString &decomp, + UnicodeString &newNFDString, UnicodeString &newString, + UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return false; } + U_ASSERT(nfdString.char32At(indexAfterLastStarter - 1) == decomp.char32At(0)); + int32_t lastStarterLength = decomp.moveIndex32(0, 1); + if(lastStarterLength == decomp.length()) { + // Singleton decompositions should be found by addWithClosure() + // and the CanonicalIterator, so we can ignore them here. + return false; + } + if(nfdString.compare(indexAfterLastStarter, 0x7fffffff, + decomp, lastStarterLength, 0x7fffffff) == 0) { + // same strings, nothing new to be found here + return false; + } + + // Make new FCD strings that combine a composite, or its decomposition, + // into the nfdString's last starter and the combining marks following it. + // Make an NFD version, and a version with the composite. + newNFDString.setTo(nfdString, 0, indexAfterLastStarter); + newString.setTo(nfdString, 0, indexAfterLastStarter - lastStarterLength).append(composite); + + // The following is related to discontiguous contraction matching, + // but builds only FCD strings (or else returns false). + int32_t sourceIndex = indexAfterLastStarter; + int32_t decompIndex = lastStarterLength; + // Small optimization: We keep the source character across loop iterations + // because we do not always consume it, + // and then need not fetch it again nor look up its combining class again. + UChar32 sourceChar = U_SENTINEL; + // The cc variables need to be declared before the loop so that at the end + // they are set to the last combining classes seen. + uint8_t sourceCC = 0; + uint8_t decompCC = 0; + for(;;) { + if(sourceChar < 0) { + if(sourceIndex >= nfdString.length()) { break; } + sourceChar = nfdString.char32At(sourceIndex); + sourceCC = nfd.getCombiningClass(sourceChar); + U_ASSERT(sourceCC != 0); + } + // We consume a decomposition character in each iteration. + if(decompIndex >= decomp.length()) { break; } + UChar32 decompChar = decomp.char32At(decompIndex); + decompCC = nfd.getCombiningClass(decompChar); + // Compare the two characters and their combining classes. + if(decompCC == 0) { + // Unable to merge because the source contains a non-zero combining mark + // but the composite's decomposition contains another starter. + // The strings would not be equivalent. + return false; + } else if(sourceCC < decompCC) { + // Composite + sourceChar would not be FCD. + return false; + } else if(decompCC < sourceCC) { + newNFDString.append(decompChar); + decompIndex += U16_LENGTH(decompChar); + } else if(decompChar != sourceChar) { + // Blocked because same combining class. + return false; + } else { // match: decompChar == sourceChar + newNFDString.append(decompChar); + decompIndex += U16_LENGTH(decompChar); + sourceIndex += U16_LENGTH(decompChar); + sourceChar = U_SENTINEL; + } + } + // We are at the end of at least one of the two inputs. + if(sourceChar >= 0) { // more characters from nfdString but not from decomp + if(sourceCC < decompCC) { + // Appending the next source character to the composite would not be FCD. + return false; + } + newNFDString.append(nfdString, sourceIndex, 0x7fffffff); + newString.append(nfdString, sourceIndex, 0x7fffffff); + } else if(decompIndex < decomp.length()) { // more characters from decomp, not from nfdString + newNFDString.append(decomp, decompIndex, 0x7fffffff); + } + U_ASSERT(nfd.isNormalized(newNFDString, errorCode)); + U_ASSERT(fcd.isNormalized(newString, errorCode)); + U_ASSERT(nfd.normalize(newString, errorCode) == newNFDString); // canonically equivalent + return true; +} + +UBool +CollationBuilder::ignorePrefix(const UnicodeString &s, UErrorCode &errorCode) const { + // Do not map non-FCD prefixes. + return !isFCD(s, errorCode); +} + +UBool +CollationBuilder::ignoreString(const UnicodeString &s, UErrorCode &errorCode) const { + // Do not map non-FCD strings. + // Do not map strings that start with Hangul syllables: We decompose those on the fly. + return !isFCD(s, errorCode) || Hangul::isHangul(s.charAt(0)); +} + +UBool +CollationBuilder::isFCD(const UnicodeString &s, UErrorCode &errorCode) const { + return U_SUCCESS(errorCode) && fcd.isNormalized(s, errorCode); +} + +void +CollationBuilder::closeOverComposites(UErrorCode &errorCode) { + UnicodeSet composites(UNICODE_STRING_SIMPLE("[:NFD_QC=N:]"), errorCode); // Java: static final + if(U_FAILURE(errorCode)) { return; } + // Hangul is decomposed on the fly during collation. + composites.remove(Hangul::HANGUL_BASE, Hangul::HANGUL_END); + UnicodeString prefix; // empty + UnicodeString nfdString; + UnicodeSetIterator iter(composites); + while(iter.next()) { + U_ASSERT(!iter.isString()); + nfd.getDecomposition(iter.getCodepoint(), nfdString); + cesLength = dataBuilder->getCEs(nfdString, ces, 0); + if(cesLength > Collation::MAX_EXPANSION_LENGTH) { + // Too many CEs from the decomposition (unusual), ignore this composite. + // We could add a capacity parameter to getCEs() and reallocate if necessary. + // However, this can only really happen in contrived cases. + continue; + } + const UnicodeString &composite(iter.getString()); + addIfDifferent(prefix, composite, ces, cesLength, Collation::UNASSIGNED_CE32, errorCode); + } +} + +uint32_t +CollationBuilder::addIfDifferent(const UnicodeString &prefix, const UnicodeString &str, + const int64_t newCEs[], int32_t newCEsLength, uint32_t ce32, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return ce32; } + int64_t oldCEs[Collation::MAX_EXPANSION_LENGTH]; + int32_t oldCEsLength = dataBuilder->getCEs(prefix, str, oldCEs, 0); + if(!sameCEs(newCEs, newCEsLength, oldCEs, oldCEsLength)) { + if(ce32 == Collation::UNASSIGNED_CE32) { + ce32 = dataBuilder->encodeCEs(newCEs, newCEsLength, errorCode); + } + dataBuilder->addCE32(prefix, str, ce32, errorCode); + } + return ce32; +} + +UBool +CollationBuilder::sameCEs(const int64_t ces1[], int32_t ces1Length, + const int64_t ces2[], int32_t ces2Length) { + if(ces1Length != ces2Length) { + return false; + } + U_ASSERT(ces1Length <= Collation::MAX_EXPANSION_LENGTH); + for(int32_t i = 0; i < ces1Length; ++i) { + if(ces1[i] != ces2[i]) { return false; } + } + return true; +} + +#ifdef DEBUG_COLLATION_BUILDER + +uint32_t +alignWeightRight(uint32_t w) { + if(w != 0) { + while((w & 0xff) == 0) { w >>= 8; } + } + return w; +} + +#endif + +void +CollationBuilder::makeTailoredCEs(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + + CollationWeights primaries, secondaries, tertiaries; + int64_t *nodesArray = nodes.getBuffer(); +#ifdef DEBUG_COLLATION_BUILDER + puts("\nCollationBuilder::makeTailoredCEs()"); +#endif + + for(int32_t rpi = 0; rpi < rootPrimaryIndexes.size(); ++rpi) { + int32_t i = rootPrimaryIndexes.elementAti(rpi); + int64_t node = nodesArray[i]; + uint32_t p = weight32FromNode(node); + uint32_t s = p == 0 ? 0 : Collation::COMMON_WEIGHT16; + uint32_t t = s; + uint32_t q = 0; + UBool pIsTailored = false; + UBool sIsTailored = false; + UBool tIsTailored = false; +#ifdef DEBUG_COLLATION_BUILDER + printf("\nprimary %lx\n", (long)alignWeightRight(p)); +#endif + int32_t pIndex = p == 0 ? 0 : rootElements.findPrimary(p); + int32_t nextIndex = nextIndexFromNode(node); + while(nextIndex != 0) { + i = nextIndex; + node = nodesArray[i]; + nextIndex = nextIndexFromNode(node); + int32_t strength = strengthFromNode(node); + if(strength == UCOL_QUATERNARY) { + U_ASSERT(isTailoredNode(node)); +#ifdef DEBUG_COLLATION_BUILDER + printf(" quat+ "); +#endif + if(q == 3) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + errorReason = "quaternary tailoring gap too small"; + return; + } + ++q; + } else { + if(strength == UCOL_TERTIARY) { + if(isTailoredNode(node)) { +#ifdef DEBUG_COLLATION_BUILDER + printf(" ter+ "); +#endif + if(!tIsTailored) { + // First tailored tertiary node for [p, s]. + int32_t tCount = countTailoredNodes(nodesArray, nextIndex, + UCOL_TERTIARY) + 1; + uint32_t tLimit; + if(t == 0) { + // Gap at the beginning of the tertiary CE range. + t = rootElements.getTertiaryBoundary() - 0x100; + tLimit = rootElements.getFirstTertiaryCE() & Collation::ONLY_TERTIARY_MASK; + } else if(!pIsTailored && !sIsTailored) { + // p and s are root weights. + tLimit = rootElements.getTertiaryAfter(pIndex, s, t); + } else if(t == Collation::BEFORE_WEIGHT16) { + tLimit = Collation::COMMON_WEIGHT16; + } else { + // [p, s] is tailored. + U_ASSERT(t == Collation::COMMON_WEIGHT16); + tLimit = rootElements.getTertiaryBoundary(); + } + U_ASSERT(tLimit == 0x4000 || (tLimit & ~Collation::ONLY_TERTIARY_MASK) == 0); + tertiaries.initForTertiary(); + if(!tertiaries.allocWeights(t, tLimit, tCount)) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + errorReason = "tertiary tailoring gap too small"; + return; + } + tIsTailored = true; + } + t = tertiaries.nextWeight(); + U_ASSERT(t != 0xffffffff); + } else { + t = weight16FromNode(node); + tIsTailored = false; +#ifdef DEBUG_COLLATION_BUILDER + printf(" ter %lx\n", (long)alignWeightRight(t)); +#endif + } + } else { + if(strength == UCOL_SECONDARY) { + if(isTailoredNode(node)) { +#ifdef DEBUG_COLLATION_BUILDER + printf(" sec+ "); +#endif + if(!sIsTailored) { + // First tailored secondary node for p. + int32_t sCount = countTailoredNodes(nodesArray, nextIndex, + UCOL_SECONDARY) + 1; + uint32_t sLimit; + if(s == 0) { + // Gap at the beginning of the secondary CE range. + s = rootElements.getSecondaryBoundary() - 0x100; + sLimit = rootElements.getFirstSecondaryCE() >> 16; + } else if(!pIsTailored) { + // p is a root primary. + sLimit = rootElements.getSecondaryAfter(pIndex, s); + } else if(s == Collation::BEFORE_WEIGHT16) { + sLimit = Collation::COMMON_WEIGHT16; + } else { + // p is a tailored primary. + U_ASSERT(s == Collation::COMMON_WEIGHT16); + sLimit = rootElements.getSecondaryBoundary(); + } + if(s == Collation::COMMON_WEIGHT16) { + // Do not tailor into the getSortKey() range of + // compressed common secondaries. + s = rootElements.getLastCommonSecondary(); + } + secondaries.initForSecondary(); + if(!secondaries.allocWeights(s, sLimit, sCount)) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + errorReason = "secondary tailoring gap too small"; +#ifdef DEBUG_COLLATION_BUILDER + printf("!secondaries.allocWeights(%lx, %lx, sCount=%ld)\n", + (long)alignWeightRight(s), (long)alignWeightRight(sLimit), + (long)alignWeightRight(sCount)); +#endif + return; + } + sIsTailored = true; + } + s = secondaries.nextWeight(); + U_ASSERT(s != 0xffffffff); + } else { + s = weight16FromNode(node); + sIsTailored = false; +#ifdef DEBUG_COLLATION_BUILDER + printf(" sec %lx\n", (long)alignWeightRight(s)); +#endif + } + } else /* UCOL_PRIMARY */ { + U_ASSERT(isTailoredNode(node)); +#ifdef DEBUG_COLLATION_BUILDER + printf("pri+ "); +#endif + if(!pIsTailored) { + // First tailored primary node in this list. + int32_t pCount = countTailoredNodes(nodesArray, nextIndex, + UCOL_PRIMARY) + 1; + UBool isCompressible = baseData->isCompressiblePrimary(p); + uint32_t pLimit = + rootElements.getPrimaryAfter(p, pIndex, isCompressible); + primaries.initForPrimary(isCompressible); + if(!primaries.allocWeights(p, pLimit, pCount)) { + errorCode = U_BUFFER_OVERFLOW_ERROR; // TODO: introduce a more specific UErrorCode? + errorReason = "primary tailoring gap too small"; + return; + } + pIsTailored = true; + } + p = primaries.nextWeight(); + U_ASSERT(p != 0xffffffff); + s = Collation::COMMON_WEIGHT16; + sIsTailored = false; + } + t = s == 0 ? 0 : Collation::COMMON_WEIGHT16; + tIsTailored = false; + } + q = 0; + } + if(isTailoredNode(node)) { + nodesArray[i] = Collation::makeCE(p, s, t, q); +#ifdef DEBUG_COLLATION_BUILDER + printf("%016llx\n", (long long)nodesArray[i]); +#endif + } + } + } +} + +int32_t +CollationBuilder::countTailoredNodes(const int64_t *nodesArray, int32_t i, int32_t strength) { + int32_t count = 0; + for(;;) { + if(i == 0) { break; } + int64_t node = nodesArray[i]; + if(strengthFromNode(node) < strength) { break; } + if(strengthFromNode(node) == strength) { + if(isTailoredNode(node)) { + ++count; + } else { + break; + } + } + i = nextIndexFromNode(node); + } + return count; +} + +class CEFinalizer : public CollationDataBuilder::CEModifier { +public: + CEFinalizer(const int64_t *ces) : finalCEs(ces) {} + virtual ~CEFinalizer(); + virtual int64_t modifyCE32(uint32_t ce32) const override { + U_ASSERT(!Collation::isSpecialCE32(ce32)); + if(CollationBuilder::isTempCE32(ce32)) { + // retain case bits + return finalCEs[CollationBuilder::indexFromTempCE32(ce32)] | ((ce32 & 0xc0) << 8); + } else { + return Collation::NO_CE; + } + } + virtual int64_t modifyCE(int64_t ce) const override { + if(CollationBuilder::isTempCE(ce)) { + // retain case bits + return finalCEs[CollationBuilder::indexFromTempCE(ce)] | (ce & 0xc000); + } else { + return Collation::NO_CE; + } + } + +private: + const int64_t *finalCEs; +}; + +CEFinalizer::~CEFinalizer() {} + +void +CollationBuilder::finalizeCEs(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + LocalPointer newBuilder(new CollationDataBuilder(icu4xMode, errorCode), errorCode); + if(U_FAILURE(errorCode)) { + return; + } + newBuilder->initForTailoring(baseData, errorCode); + CEFinalizer finalizer(nodes.getBuffer()); + newBuilder->copyFrom(*dataBuilder, finalizer, errorCode); + if(U_FAILURE(errorCode)) { return; } + delete dataBuilder; + dataBuilder = newBuilder.orphan(); +} + +int32_t +CollationBuilder::ceStrength(int64_t ce) { + return + isTempCE(ce) ? strengthFromTempCE(ce) : + (ce & INT64_C(0xff00000000000000)) != 0 ? UCOL_PRIMARY : + ((uint32_t)ce & 0xff000000) != 0 ? UCOL_SECONDARY : + ce != 0 ? UCOL_TERTIARY : + UCOL_IDENTICAL; +} + +U_NAMESPACE_END + +U_NAMESPACE_USE + +U_CAPI UCollator * U_EXPORT2 +ucol_openRules(const char16_t *rules, int32_t rulesLength, + UColAttributeValue normalizationMode, UCollationStrength strength, + UParseError *parseError, UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { return nullptr; } + if(rules == nullptr && rulesLength != 0) { + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + RuleBasedCollator *coll = new RuleBasedCollator(); + if(coll == nullptr) { + *pErrorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + UnicodeString r((UBool)(rulesLength < 0), rules, rulesLength); + coll->internalBuildTailoring(r, strength, normalizationMode, parseError, nullptr, *pErrorCode); + if(U_FAILURE(*pErrorCode)) { + delete coll; + return nullptr; + } + return coll->toUCollator(); +} + +static const int32_t internalBufferSize = 512; + +// The @internal ucol_getUnsafeSet() was moved here from ucol_sit.cpp +// because it calls UnicodeSet "builder" code that depends on all Unicode properties, +// and the rest of the collation "runtime" code only depends on normalization. +// This function is not related to the collation builder, +// but it did not seem worth moving it into its own .cpp file, +// nor rewriting it to use lower-level UnicodeSet and Normalizer2Impl methods. +U_CAPI int32_t U_EXPORT2 +ucol_getUnsafeSet( const UCollator *coll, + USet *unsafe, + UErrorCode *status) +{ + char16_t buffer[internalBufferSize]; + int32_t len = 0; + + uset_clear(unsafe); + + // cccpattern = "[[:^tccc=0:][:^lccc=0:]]", unfortunately variant + static const char16_t cccpattern[25] = { 0x5b, 0x5b, 0x3a, 0x5e, 0x74, 0x63, 0x63, 0x63, 0x3d, 0x30, 0x3a, 0x5d, + 0x5b, 0x3a, 0x5e, 0x6c, 0x63, 0x63, 0x63, 0x3d, 0x30, 0x3a, 0x5d, 0x5d, 0x00 }; + + // add chars that fail the fcd check + uset_applyPattern(unsafe, cccpattern, 24, USET_IGNORE_SPACE, status); + + // add lead/trail surrogates + // (trail surrogates should need to be unsafe only if the caller tests for UTF-16 code *units*, + // not when testing code *points*) + uset_addRange(unsafe, 0xd800, 0xdfff); + + USet *contractions = uset_open(0,0); + + int32_t i = 0, j = 0; + ucol_getContractionsAndExpansions(coll, contractions, nullptr, false, status); + int32_t contsSize = uset_size(contractions); + UChar32 c = 0; + // Contraction set consists only of strings + // to get unsafe code points, we need to + // break the strings apart and add them to the unsafe set + for(i = 0; i < contsSize; i++) { + len = uset_getItem(contractions, i, nullptr, nullptr, buffer, internalBufferSize, status); + if(len > 0) { + j = 0; + while(j < len) { + U16_NEXT(buffer, j, len, c); + if(j < len) { + uset_add(unsafe, c); + } + } + } + } + + uset_close(contractions); + + return uset_size(unsafe); +} + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationbuilder.h b/deps/icu-small/source/i18n/collationbuilder.h index 22e24ddb813aa2..be377c7c3b1d5d 100644 --- a/deps/icu-small/source/i18n/collationbuilder.h +++ b/deps/icu-small/source/i18n/collationbuilder.h @@ -1,411 +1,411 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationbuilder.h -* -* created on: 2013may06 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONBUILDER_H__ -#define __COLLATIONBUILDER_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/uniset.h" -#include "unicode/unistr.h" -#include "collationrootelements.h" -#include "collationruleparser.h" -#include "uvectr32.h" -#include "uvectr64.h" - -struct UParseError; - -U_NAMESPACE_BEGIN - -struct CollationData; -struct CollationTailoring; - -class CEFinalizer; -class CollationDataBuilder; -class Normalizer2; -class Normalizer2Impl; - -class U_I18N_API CollationBuilder : public CollationRuleParser::Sink { -public: - CollationBuilder(const CollationTailoring *b, UBool icu4xMode, UErrorCode &errorCode); - CollationBuilder(const CollationTailoring *base, UErrorCode &errorCode); - virtual ~CollationBuilder(); - - void disableFastLatin() { fastLatinEnabled = false; } - - CollationTailoring *parseAndBuild(const UnicodeString &ruleString, - const UVersionInfo rulesVersion, - CollationRuleParser::Importer *importer, - UParseError *outParseError, - UErrorCode &errorCode); - - const char *getErrorReason() const { return errorReason; } - -private: - friend class CEFinalizer; - - /** Implements CollationRuleParser::Sink. */ - virtual void addReset(int32_t strength, const UnicodeString &str, - const char *&errorReason, UErrorCode &errorCode) override; - /** - * Returns the secondary or tertiary weight preceding the current node's weight. - * node=nodes[index]. - */ - uint32_t getWeight16Before(int32_t index, int64_t node, int32_t level); - - int64_t getSpecialResetPosition(const UnicodeString &str, - const char *&parserErrorReason, UErrorCode &errorCode); - - /** Implements CollationRuleParser::Sink. */ - virtual void addRelation(int32_t strength, const UnicodeString &prefix, - const UnicodeString &str, const UnicodeString &extension, - const char *&errorReason, UErrorCode &errorCode) override; - - /** - * Picks one of the current CEs and finds or inserts a node in the graph - * for the CE + strength. - */ - int32_t findOrInsertNodeForCEs(int32_t strength, const char *&parserErrorReason, - UErrorCode &errorCode); - int32_t findOrInsertNodeForRootCE(int64_t ce, int32_t strength, UErrorCode &errorCode); - /** Finds or inserts the node for a root CE's primary weight. */ - int32_t findOrInsertNodeForPrimary(uint32_t p, UErrorCode &errorCode); - /** Finds or inserts the node for a secondary or tertiary weight. */ - int32_t findOrInsertWeakNode(int32_t index, uint32_t weight16, int32_t level, - UErrorCode &errorCode); - - /** - * Makes and inserts a new tailored node into the list, after the one at index. - * Skips over nodes of weaker strength to maintain collation order - * ("postpone insertion"). - * @return the new node's index - */ - int32_t insertTailoredNodeAfter(int32_t index, int32_t strength, UErrorCode &errorCode); - - /** - * Inserts a new node into the list, between list-adjacent items. - * The node's previous and next indexes must not be set yet. - * @return the new node's index - */ - int32_t insertNodeBetween(int32_t index, int32_t nextIndex, int64_t node, - UErrorCode &errorCode); - - /** - * Finds the node which implies or contains a common=05 weight of the given strength - * (secondary or tertiary), if the current node is stronger. - * Skips weaker nodes and tailored nodes if the current node is stronger - * and is followed by an explicit-common-weight node. - * Always returns the input index if that node is no stronger than the given strength. - */ - int32_t findCommonNode(int32_t index, int32_t strength) const; - - void setCaseBits(const UnicodeString &nfdString, - const char *&parserErrorReason, UErrorCode &errorCode); - - /** Implements CollationRuleParser::Sink. */ - virtual void suppressContractions(const UnicodeSet &set, const char *&parserErrorReason, - UErrorCode &errorCode) override; - - /** Implements CollationRuleParser::Sink. */ - virtual void optimize(const UnicodeSet &set, const char *&parserErrorReason, - UErrorCode &errorCode) override; - - /** - * Adds the mapping and its canonical closure. - * Takes ce32=dataBuilder->encodeCEs(...) so that the data builder - * need not re-encode the CEs multiple times. - */ - uint32_t addWithClosure(const UnicodeString &nfdPrefix, const UnicodeString &nfdString, - const int64_t newCEs[], int32_t newCEsLength, uint32_t ce32, - UErrorCode &errorCode); - uint32_t addOnlyClosure(const UnicodeString &nfdPrefix, const UnicodeString &nfdString, - const int64_t newCEs[], int32_t newCEsLength, uint32_t ce32, - UErrorCode &errorCode); - void addTailComposites(const UnicodeString &nfdPrefix, const UnicodeString &nfdString, - UErrorCode &errorCode); - UBool mergeCompositeIntoString(const UnicodeString &nfdString, int32_t indexAfterLastStarter, - UChar32 composite, const UnicodeString &decomp, - UnicodeString &newNFDString, UnicodeString &newString, - UErrorCode &errorCode) const; - - UBool ignorePrefix(const UnicodeString &s, UErrorCode &errorCode) const; - UBool ignoreString(const UnicodeString &s, UErrorCode &errorCode) const; - UBool isFCD(const UnicodeString &s, UErrorCode &errorCode) const; - - void closeOverComposites(UErrorCode &errorCode); - - uint32_t addIfDifferent(const UnicodeString &prefix, const UnicodeString &str, - const int64_t newCEs[], int32_t newCEsLength, uint32_t ce32, - UErrorCode &errorCode); - static UBool sameCEs(const int64_t ces1[], int32_t ces1Length, - const int64_t ces2[], int32_t ces2Length); - - /** - * Walks the tailoring graph and overwrites tailored nodes with new CEs. - * After this, the graph is destroyed. - * The nodes array can then be used only as a source of tailored CEs. - */ - void makeTailoredCEs(UErrorCode &errorCode); - /** - * Counts the tailored nodes of the given strength up to the next node - * which is either stronger or has an explicit weight of this strength. - */ - static int32_t countTailoredNodes(const int64_t *nodesArray, int32_t i, int32_t strength); - - /** Replaces temporary CEs with the final CEs they point to. */ - void finalizeCEs(UErrorCode &errorCode); - - /** - * Encodes "temporary CE" data into a CE that fits into the CE32 data structure, - * with 2-byte primary, 1-byte secondary and 6-bit tertiary, - * with valid CE byte values. - * - * The index must not exceed 20 bits (0xfffff). - * The strength must fit into 2 bits (UCOL_PRIMARY..UCOL_QUATERNARY). - * - * Temporary CEs are distinguished from real CEs by their use of - * secondary weights 06..45 which are otherwise reserved for compressed sort keys. - * - * The case bits are unused and available. - */ - static inline int64_t tempCEFromIndexAndStrength(int32_t index, int32_t strength) { - return - // CE byte offsets, to ensure valid CE bytes, and case bits 11 - INT64_C(0x4040000006002000) + - // index bits 19..13 -> primary byte 1 = CE bits 63..56 (byte values 40..BF) - ((int64_t)(index & 0xfe000) << 43) + - // index bits 12..6 -> primary byte 2 = CE bits 55..48 (byte values 40..BF) - ((int64_t)(index & 0x1fc0) << 42) + - // index bits 5..0 -> secondary byte 1 = CE bits 31..24 (byte values 06..45) - ((index & 0x3f) << 24) + - // strength bits 1..0 -> tertiary byte 1 = CE bits 13..8 (byte values 20..23) - (strength << 8); - } - static inline int32_t indexFromTempCE(int64_t tempCE) { - tempCE -= INT64_C(0x4040000006002000); - return - ((int32_t)(tempCE >> 43) & 0xfe000) | - ((int32_t)(tempCE >> 42) & 0x1fc0) | - ((int32_t)(tempCE >> 24) & 0x3f); - } - static inline int32_t strengthFromTempCE(int64_t tempCE) { - return ((int32_t)tempCE >> 8) & 3; - } - static inline UBool isTempCE(int64_t ce) { - uint32_t sec = (uint32_t)ce >> 24; - return 6 <= sec && sec <= 0x45; - } - - static inline int32_t indexFromTempCE32(uint32_t tempCE32) { - tempCE32 -= 0x40400620; - return - ((int32_t)(tempCE32 >> 11) & 0xfe000) | - ((int32_t)(tempCE32 >> 10) & 0x1fc0) | - ((int32_t)(tempCE32 >> 8) & 0x3f); - } - static inline UBool isTempCE32(uint32_t ce32) { - return - (ce32 & 0xff) >= 2 && // not a long-primary/long-secondary CE32 - 6 <= ((ce32 >> 8) & 0xff) && ((ce32 >> 8) & 0xff) <= 0x45; - } - - static int32_t ceStrength(int64_t ce); - - /** At most 1M nodes, limited by the 20 bits in node bit fields. */ - static const int32_t MAX_INDEX = 0xfffff; - /** - * Node bit 6 is set on a primary node if there are nodes - * with secondary values below the common secondary weight (05). - */ - static const int32_t HAS_BEFORE2 = 0x40; - /** - * Node bit 5 is set on a primary or secondary node if there are nodes - * with tertiary values below the common tertiary weight (05). - */ - static const int32_t HAS_BEFORE3 = 0x20; - /** - * Node bit 3 distinguishes a tailored node, which has no weight value, - * from a node with an explicit (root or default) weight. - */ - static const int32_t IS_TAILORED = 8; - - static inline int64_t nodeFromWeight32(uint32_t weight32) { - return (int64_t)weight32 << 32; - } - static inline int64_t nodeFromWeight16(uint32_t weight16) { - return (int64_t)weight16 << 48; - } - static inline int64_t nodeFromPreviousIndex(int32_t previous) { - return (int64_t)previous << 28; - } - static inline int64_t nodeFromNextIndex(int32_t next) { - return next << 8; - } - static inline int64_t nodeFromStrength(int32_t strength) { - return strength; - } - - static inline uint32_t weight32FromNode(int64_t node) { - return (uint32_t)(node >> 32); - } - static inline uint32_t weight16FromNode(int64_t node) { - return (uint32_t)(node >> 48) & 0xffff; - } - static inline int32_t previousIndexFromNode(int64_t node) { - return (int32_t)(node >> 28) & MAX_INDEX; - } - static inline int32_t nextIndexFromNode(int64_t node) { - return ((int32_t)node >> 8) & MAX_INDEX; - } - static inline int32_t strengthFromNode(int64_t node) { - return (int32_t)node & 3; - } - - static inline UBool nodeHasBefore2(int64_t node) { - return (node & HAS_BEFORE2) != 0; - } - static inline UBool nodeHasBefore3(int64_t node) { - return (node & HAS_BEFORE3) != 0; - } - static inline UBool nodeHasAnyBefore(int64_t node) { - return (node & (HAS_BEFORE2 | HAS_BEFORE3)) != 0; - } - static inline UBool isTailoredNode(int64_t node) { - return (node & IS_TAILORED) != 0; - } - - static inline int64_t changeNodePreviousIndex(int64_t node, int32_t previous) { - return (node & INT64_C(0xffff00000fffffff)) | nodeFromPreviousIndex(previous); - } - static inline int64_t changeNodeNextIndex(int64_t node, int32_t next) { - return (node & INT64_C(0xfffffffff00000ff)) | nodeFromNextIndex(next); - } - - const Normalizer2 &nfd, &fcd; - const Normalizer2Impl &nfcImpl; - - const CollationTailoring *base; - const CollationData *baseData; - const CollationRootElements rootElements; - uint32_t variableTop; - - CollationDataBuilder *dataBuilder; - UBool fastLatinEnabled; - UBool icu4xMode; - UnicodeSet optimizeSet; - const char *errorReason; - - int64_t ces[Collation::MAX_EXPANSION_LENGTH]; - int32_t cesLength; - - /** - * Indexes of nodes with root primary weights, sorted by primary. - * Compact form of a TreeMap from root primary to node index. - * - * This is a performance optimization for finding reset positions. - * Without this, we would have to search through the entire nodes list. - * It also allows storing root primary weights in list head nodes, - * without previous index, leaving room in root primary nodes for 32-bit primary weights. - */ - UVector32 rootPrimaryIndexes; - /** - * Data structure for assigning tailored weights and CEs. - * Doubly-linked lists of nodes in mostly collation order. - * Each list starts with a root primary node and ends with a nextIndex of 0. - * - * When there are any nodes in the list, then there is always a root primary node at index 0. - * This allows some code not to have to check explicitly for nextIndex==0. - * - * Root primary nodes have 32-bit weights but do not have previous indexes. - * All other nodes have at most 16-bit weights and do have previous indexes. - * - * Nodes with explicit weights store root collator weights, - * or default weak weights (e.g., secondary 05) for stronger nodes. - * "Tailored" nodes, with the IS_TAILORED bit set, - * do not store explicit weights but rather - * create a difference of a certain strength from the preceding node. - * - * A root node is followed by either - * - a root/default node of the same strength, or - * - a root/default node of the next-weaker strength, or - * - a tailored node of the same strength. - * - * A node of a given strength normally implies "common" weights on weaker levels. - * - * A node with HAS_BEFORE2 must be immediately followed by - * a secondary node with an explicit below-common weight, then a secondary tailored node, - * and later an explicit common-secondary node. - * The below-common weight can be a root weight, - * or it can be BEFORE_WEIGHT16 for tailoring before an implied common weight - * or before the lowest root weight. - * (&[before 2] resets to an explicit secondary node so that - * the following addRelation(secondary) tailors right after that. - * If we did not have this node and instead were to reset on the primary node, - * then addRelation(secondary) would skip forward to the the COMMON_WEIGHT16 node.) - * - * If the flag is not set, then there are no explicit secondary nodes - * with the common or lower weights. - * - * Same for HAS_BEFORE3 for tertiary nodes and weights. - * A node must not have both flags set. - * - * Tailored CEs are initially represented in a CollationDataBuilder as temporary CEs - * which point to stable indexes in this list, - * and temporary CEs stored in a CollationDataBuilder only point to tailored nodes. - * - * A temporary CE in the ces[] array may point to a non-tailored reset-before-position node, - * until the next relation is added. - * - * At the end, the tailored weights are allocated as necessary, - * then the tailored nodes are replaced with final CEs, - * and the CollationData is rewritten by replacing temporary CEs with final ones. - * - * We cannot simply insert new nodes in the middle of the array - * because that would invalidate the indexes stored in existing temporary CEs. - * We need to use a linked graph with stable indexes to existing nodes. - * A doubly-linked list seems easiest to maintain. - * - * Each node is stored as an int64_t, with its fields stored as bit fields. - * - * Root primary node: - * - primary weight: 32 bits 63..32 - * - reserved/unused/zero: 4 bits 31..28 - * - * Weaker root nodes & tailored nodes: - * - a weight: 16 bits 63..48 - * + a root or default weight for a non-tailored node - * + unused/zero for a tailored node - * - index to the previous node: 20 bits 47..28 - * - * All types of nodes: - * - index to the next node: 20 bits 27..8 - * + nextIndex=0 in last node per root-primary list - * - reserved/unused/zero bits: bits 7, 4, 2 - * - HAS_BEFORE2: bit 6 - * - HAS_BEFORE3: bit 5 - * - IS_TAILORED: bit 3 - * - the difference strength (primary/secondary/tertiary/quaternary): 2 bits 1..0 - * - * We could allocate structs with pointers, but we would have to store them - * in a pointer list so that they can be indexed from temporary CEs, - * and they would require more memory allocations. - */ - UVector64 nodes; -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONBUILDER_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationbuilder.h +* +* created on: 2013may06 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONBUILDER_H__ +#define __COLLATIONBUILDER_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/uniset.h" +#include "unicode/unistr.h" +#include "collationrootelements.h" +#include "collationruleparser.h" +#include "uvectr32.h" +#include "uvectr64.h" + +struct UParseError; + +U_NAMESPACE_BEGIN + +struct CollationData; +struct CollationTailoring; + +class CEFinalizer; +class CollationDataBuilder; +class Normalizer2; +class Normalizer2Impl; + +class U_I18N_API CollationBuilder : public CollationRuleParser::Sink { +public: + CollationBuilder(const CollationTailoring *b, UBool icu4xMode, UErrorCode &errorCode); + CollationBuilder(const CollationTailoring *base, UErrorCode &errorCode); + virtual ~CollationBuilder(); + + void disableFastLatin() { fastLatinEnabled = false; } + + CollationTailoring *parseAndBuild(const UnicodeString &ruleString, + const UVersionInfo rulesVersion, + CollationRuleParser::Importer *importer, + UParseError *outParseError, + UErrorCode &errorCode); + + const char *getErrorReason() const { return errorReason; } + +private: + friend class CEFinalizer; + + /** Implements CollationRuleParser::Sink. */ + virtual void addReset(int32_t strength, const UnicodeString &str, + const char *&errorReason, UErrorCode &errorCode) override; + /** + * Returns the secondary or tertiary weight preceding the current node's weight. + * node=nodes[index]. + */ + uint32_t getWeight16Before(int32_t index, int64_t node, int32_t level); + + int64_t getSpecialResetPosition(const UnicodeString &str, + const char *&parserErrorReason, UErrorCode &errorCode); + + /** Implements CollationRuleParser::Sink. */ + virtual void addRelation(int32_t strength, const UnicodeString &prefix, + const UnicodeString &str, const UnicodeString &extension, + const char *&errorReason, UErrorCode &errorCode) override; + + /** + * Picks one of the current CEs and finds or inserts a node in the graph + * for the CE + strength. + */ + int32_t findOrInsertNodeForCEs(int32_t strength, const char *&parserErrorReason, + UErrorCode &errorCode); + int32_t findOrInsertNodeForRootCE(int64_t ce, int32_t strength, UErrorCode &errorCode); + /** Finds or inserts the node for a root CE's primary weight. */ + int32_t findOrInsertNodeForPrimary(uint32_t p, UErrorCode &errorCode); + /** Finds or inserts the node for a secondary or tertiary weight. */ + int32_t findOrInsertWeakNode(int32_t index, uint32_t weight16, int32_t level, + UErrorCode &errorCode); + + /** + * Makes and inserts a new tailored node into the list, after the one at index. + * Skips over nodes of weaker strength to maintain collation order + * ("postpone insertion"). + * @return the new node's index + */ + int32_t insertTailoredNodeAfter(int32_t index, int32_t strength, UErrorCode &errorCode); + + /** + * Inserts a new node into the list, between list-adjacent items. + * The node's previous and next indexes must not be set yet. + * @return the new node's index + */ + int32_t insertNodeBetween(int32_t index, int32_t nextIndex, int64_t node, + UErrorCode &errorCode); + + /** + * Finds the node which implies or contains a common=05 weight of the given strength + * (secondary or tertiary), if the current node is stronger. + * Skips weaker nodes and tailored nodes if the current node is stronger + * and is followed by an explicit-common-weight node. + * Always returns the input index if that node is no stronger than the given strength. + */ + int32_t findCommonNode(int32_t index, int32_t strength) const; + + void setCaseBits(const UnicodeString &nfdString, + const char *&parserErrorReason, UErrorCode &errorCode); + + /** Implements CollationRuleParser::Sink. */ + virtual void suppressContractions(const UnicodeSet &set, const char *&parserErrorReason, + UErrorCode &errorCode) override; + + /** Implements CollationRuleParser::Sink. */ + virtual void optimize(const UnicodeSet &set, const char *&parserErrorReason, + UErrorCode &errorCode) override; + + /** + * Adds the mapping and its canonical closure. + * Takes ce32=dataBuilder->encodeCEs(...) so that the data builder + * need not re-encode the CEs multiple times. + */ + uint32_t addWithClosure(const UnicodeString &nfdPrefix, const UnicodeString &nfdString, + const int64_t newCEs[], int32_t newCEsLength, uint32_t ce32, + UErrorCode &errorCode); + uint32_t addOnlyClosure(const UnicodeString &nfdPrefix, const UnicodeString &nfdString, + const int64_t newCEs[], int32_t newCEsLength, uint32_t ce32, + UErrorCode &errorCode); + void addTailComposites(const UnicodeString &nfdPrefix, const UnicodeString &nfdString, + UErrorCode &errorCode); + UBool mergeCompositeIntoString(const UnicodeString &nfdString, int32_t indexAfterLastStarter, + UChar32 composite, const UnicodeString &decomp, + UnicodeString &newNFDString, UnicodeString &newString, + UErrorCode &errorCode) const; + + UBool ignorePrefix(const UnicodeString &s, UErrorCode &errorCode) const; + UBool ignoreString(const UnicodeString &s, UErrorCode &errorCode) const; + UBool isFCD(const UnicodeString &s, UErrorCode &errorCode) const; + + void closeOverComposites(UErrorCode &errorCode); + + uint32_t addIfDifferent(const UnicodeString &prefix, const UnicodeString &str, + const int64_t newCEs[], int32_t newCEsLength, uint32_t ce32, + UErrorCode &errorCode); + static UBool sameCEs(const int64_t ces1[], int32_t ces1Length, + const int64_t ces2[], int32_t ces2Length); + + /** + * Walks the tailoring graph and overwrites tailored nodes with new CEs. + * After this, the graph is destroyed. + * The nodes array can then be used only as a source of tailored CEs. + */ + void makeTailoredCEs(UErrorCode &errorCode); + /** + * Counts the tailored nodes of the given strength up to the next node + * which is either stronger or has an explicit weight of this strength. + */ + static int32_t countTailoredNodes(const int64_t *nodesArray, int32_t i, int32_t strength); + + /** Replaces temporary CEs with the final CEs they point to. */ + void finalizeCEs(UErrorCode &errorCode); + + /** + * Encodes "temporary CE" data into a CE that fits into the CE32 data structure, + * with 2-byte primary, 1-byte secondary and 6-bit tertiary, + * with valid CE byte values. + * + * The index must not exceed 20 bits (0xfffff). + * The strength must fit into 2 bits (UCOL_PRIMARY..UCOL_QUATERNARY). + * + * Temporary CEs are distinguished from real CEs by their use of + * secondary weights 06..45 which are otherwise reserved for compressed sort keys. + * + * The case bits are unused and available. + */ + static inline int64_t tempCEFromIndexAndStrength(int32_t index, int32_t strength) { + return + // CE byte offsets, to ensure valid CE bytes, and case bits 11 + INT64_C(0x4040000006002000) + + // index bits 19..13 -> primary byte 1 = CE bits 63..56 (byte values 40..BF) + ((int64_t)(index & 0xfe000) << 43) + + // index bits 12..6 -> primary byte 2 = CE bits 55..48 (byte values 40..BF) + ((int64_t)(index & 0x1fc0) << 42) + + // index bits 5..0 -> secondary byte 1 = CE bits 31..24 (byte values 06..45) + ((index & 0x3f) << 24) + + // strength bits 1..0 -> tertiary byte 1 = CE bits 13..8 (byte values 20..23) + (strength << 8); + } + static inline int32_t indexFromTempCE(int64_t tempCE) { + tempCE -= INT64_C(0x4040000006002000); + return + ((int32_t)(tempCE >> 43) & 0xfe000) | + ((int32_t)(tempCE >> 42) & 0x1fc0) | + ((int32_t)(tempCE >> 24) & 0x3f); + } + static inline int32_t strengthFromTempCE(int64_t tempCE) { + return ((int32_t)tempCE >> 8) & 3; + } + static inline UBool isTempCE(int64_t ce) { + uint32_t sec = (uint32_t)ce >> 24; + return 6 <= sec && sec <= 0x45; + } + + static inline int32_t indexFromTempCE32(uint32_t tempCE32) { + tempCE32 -= 0x40400620; + return + ((int32_t)(tempCE32 >> 11) & 0xfe000) | + ((int32_t)(tempCE32 >> 10) & 0x1fc0) | + ((int32_t)(tempCE32 >> 8) & 0x3f); + } + static inline UBool isTempCE32(uint32_t ce32) { + return + (ce32 & 0xff) >= 2 && // not a long-primary/long-secondary CE32 + 6 <= ((ce32 >> 8) & 0xff) && ((ce32 >> 8) & 0xff) <= 0x45; + } + + static int32_t ceStrength(int64_t ce); + + /** At most 1M nodes, limited by the 20 bits in node bit fields. */ + static const int32_t MAX_INDEX = 0xfffff; + /** + * Node bit 6 is set on a primary node if there are nodes + * with secondary values below the common secondary weight (05). + */ + static const int32_t HAS_BEFORE2 = 0x40; + /** + * Node bit 5 is set on a primary or secondary node if there are nodes + * with tertiary values below the common tertiary weight (05). + */ + static const int32_t HAS_BEFORE3 = 0x20; + /** + * Node bit 3 distinguishes a tailored node, which has no weight value, + * from a node with an explicit (root or default) weight. + */ + static const int32_t IS_TAILORED = 8; + + static inline int64_t nodeFromWeight32(uint32_t weight32) { + return (int64_t)weight32 << 32; + } + static inline int64_t nodeFromWeight16(uint32_t weight16) { + return (int64_t)weight16 << 48; + } + static inline int64_t nodeFromPreviousIndex(int32_t previous) { + return (int64_t)previous << 28; + } + static inline int64_t nodeFromNextIndex(int32_t next) { + return next << 8; + } + static inline int64_t nodeFromStrength(int32_t strength) { + return strength; + } + + static inline uint32_t weight32FromNode(int64_t node) { + return (uint32_t)(node >> 32); + } + static inline uint32_t weight16FromNode(int64_t node) { + return (uint32_t)(node >> 48) & 0xffff; + } + static inline int32_t previousIndexFromNode(int64_t node) { + return (int32_t)(node >> 28) & MAX_INDEX; + } + static inline int32_t nextIndexFromNode(int64_t node) { + return ((int32_t)node >> 8) & MAX_INDEX; + } + static inline int32_t strengthFromNode(int64_t node) { + return (int32_t)node & 3; + } + + static inline UBool nodeHasBefore2(int64_t node) { + return (node & HAS_BEFORE2) != 0; + } + static inline UBool nodeHasBefore3(int64_t node) { + return (node & HAS_BEFORE3) != 0; + } + static inline UBool nodeHasAnyBefore(int64_t node) { + return (node & (HAS_BEFORE2 | HAS_BEFORE3)) != 0; + } + static inline UBool isTailoredNode(int64_t node) { + return (node & IS_TAILORED) != 0; + } + + static inline int64_t changeNodePreviousIndex(int64_t node, int32_t previous) { + return (node & INT64_C(0xffff00000fffffff)) | nodeFromPreviousIndex(previous); + } + static inline int64_t changeNodeNextIndex(int64_t node, int32_t next) { + return (node & INT64_C(0xfffffffff00000ff)) | nodeFromNextIndex(next); + } + + const Normalizer2 &nfd, &fcd; + const Normalizer2Impl &nfcImpl; + + const CollationTailoring *base; + const CollationData *baseData; + const CollationRootElements rootElements; + uint32_t variableTop; + + CollationDataBuilder *dataBuilder; + UBool fastLatinEnabled; + UBool icu4xMode; + UnicodeSet optimizeSet; + const char *errorReason; + + int64_t ces[Collation::MAX_EXPANSION_LENGTH]; + int32_t cesLength; + + /** + * Indexes of nodes with root primary weights, sorted by primary. + * Compact form of a TreeMap from root primary to node index. + * + * This is a performance optimization for finding reset positions. + * Without this, we would have to search through the entire nodes list. + * It also allows storing root primary weights in list head nodes, + * without previous index, leaving room in root primary nodes for 32-bit primary weights. + */ + UVector32 rootPrimaryIndexes; + /** + * Data structure for assigning tailored weights and CEs. + * Doubly-linked lists of nodes in mostly collation order. + * Each list starts with a root primary node and ends with a nextIndex of 0. + * + * When there are any nodes in the list, then there is always a root primary node at index 0. + * This allows some code not to have to check explicitly for nextIndex==0. + * + * Root primary nodes have 32-bit weights but do not have previous indexes. + * All other nodes have at most 16-bit weights and do have previous indexes. + * + * Nodes with explicit weights store root collator weights, + * or default weak weights (e.g., secondary 05) for stronger nodes. + * "Tailored" nodes, with the IS_TAILORED bit set, + * do not store explicit weights but rather + * create a difference of a certain strength from the preceding node. + * + * A root node is followed by either + * - a root/default node of the same strength, or + * - a root/default node of the next-weaker strength, or + * - a tailored node of the same strength. + * + * A node of a given strength normally implies "common" weights on weaker levels. + * + * A node with HAS_BEFORE2 must be immediately followed by + * a secondary node with an explicit below-common weight, then a secondary tailored node, + * and later an explicit common-secondary node. + * The below-common weight can be a root weight, + * or it can be BEFORE_WEIGHT16 for tailoring before an implied common weight + * or before the lowest root weight. + * (&[before 2] resets to an explicit secondary node so that + * the following addRelation(secondary) tailors right after that. + * If we did not have this node and instead were to reset on the primary node, + * then addRelation(secondary) would skip forward to the the COMMON_WEIGHT16 node.) + * + * If the flag is not set, then there are no explicit secondary nodes + * with the common or lower weights. + * + * Same for HAS_BEFORE3 for tertiary nodes and weights. + * A node must not have both flags set. + * + * Tailored CEs are initially represented in a CollationDataBuilder as temporary CEs + * which point to stable indexes in this list, + * and temporary CEs stored in a CollationDataBuilder only point to tailored nodes. + * + * A temporary CE in the ces[] array may point to a non-tailored reset-before-position node, + * until the next relation is added. + * + * At the end, the tailored weights are allocated as necessary, + * then the tailored nodes are replaced with final CEs, + * and the CollationData is rewritten by replacing temporary CEs with final ones. + * + * We cannot simply insert new nodes in the middle of the array + * because that would invalidate the indexes stored in existing temporary CEs. + * We need to use a linked graph with stable indexes to existing nodes. + * A doubly-linked list seems easiest to maintain. + * + * Each node is stored as an int64_t, with its fields stored as bit fields. + * + * Root primary node: + * - primary weight: 32 bits 63..32 + * - reserved/unused/zero: 4 bits 31..28 + * + * Weaker root nodes & tailored nodes: + * - a weight: 16 bits 63..48 + * + a root or default weight for a non-tailored node + * + unused/zero for a tailored node + * - index to the previous node: 20 bits 47..28 + * + * All types of nodes: + * - index to the next node: 20 bits 27..8 + * + nextIndex=0 in last node per root-primary list + * - reserved/unused/zero bits: bits 7, 4, 2 + * - HAS_BEFORE2: bit 6 + * - HAS_BEFORE3: bit 5 + * - IS_TAILORED: bit 3 + * - the difference strength (primary/secondary/tertiary/quaternary): 2 bits 1..0 + * + * We could allocate structs with pointers, but we would have to store them + * in a pointer list so that they can be indexed from temporary CEs, + * and they would require more memory allocations. + */ + UVector64 nodes; +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONBUILDER_H__ diff --git a/deps/icu-small/source/i18n/collationcompare.cpp b/deps/icu-small/source/i18n/collationcompare.cpp index d9048afc279699..4e296d55eebbe8 100644 --- a/deps/icu-small/source/i18n/collationcompare.cpp +++ b/deps/icu-small/source/i18n/collationcompare.cpp @@ -1,356 +1,356 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1996-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationcompare.cpp -* -* created on: 2012feb14 with new and old collation code -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/ucol.h" -#include "cmemory.h" -#include "collation.h" -#include "collationcompare.h" -#include "collationiterator.h" -#include "collationsettings.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - -UCollationResult -CollationCompare::compareUpToQuaternary(CollationIterator &left, CollationIterator &right, - const CollationSettings &settings, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } - - int32_t options = settings.options; - uint32_t variableTop; - if((options & CollationSettings::ALTERNATE_MASK) == 0) { - variableTop = 0; - } else { - // +1 so that we can use "<" and primary ignorables test out early. - variableTop = settings.variableTop + 1; - } - UBool anyVariable = false; - - // Fetch CEs, compare primaries, store secondary & tertiary weights. - for(;;) { - // We fetch CEs until we get a non-ignorable primary or reach the end. - uint32_t leftPrimary; - do { - int64_t ce = left.nextCE(errorCode); - leftPrimary = (uint32_t)(ce >> 32); - if(leftPrimary < variableTop && leftPrimary > Collation::MERGE_SEPARATOR_PRIMARY) { - // Variable CE, shift it to quaternary level. - // Ignore all following primary ignorables, and shift further variable CEs. - anyVariable = true; - do { - // Store only the primary of the variable CE. - left.setCurrentCE(ce & INT64_C(0xffffffff00000000)); - for(;;) { - ce = left.nextCE(errorCode); - leftPrimary = (uint32_t)(ce >> 32); - if(leftPrimary == 0) { - left.setCurrentCE(0); - } else { - break; - } - } - } while(leftPrimary < variableTop && - leftPrimary > Collation::MERGE_SEPARATOR_PRIMARY); - } - } while(leftPrimary == 0); - - uint32_t rightPrimary; - do { - int64_t ce = right.nextCE(errorCode); - rightPrimary = (uint32_t)(ce >> 32); - if(rightPrimary < variableTop && rightPrimary > Collation::MERGE_SEPARATOR_PRIMARY) { - // Variable CE, shift it to quaternary level. - // Ignore all following primary ignorables, and shift further variable CEs. - anyVariable = true; - do { - // Store only the primary of the variable CE. - right.setCurrentCE(ce & INT64_C(0xffffffff00000000)); - for(;;) { - ce = right.nextCE(errorCode); - rightPrimary = (uint32_t)(ce >> 32); - if(rightPrimary == 0) { - right.setCurrentCE(0); - } else { - break; - } - } - } while(rightPrimary < variableTop && - rightPrimary > Collation::MERGE_SEPARATOR_PRIMARY); - } - } while(rightPrimary == 0); - - if(leftPrimary != rightPrimary) { - // Return the primary difference, with script reordering. - if(settings.hasReordering()) { - leftPrimary = settings.reorder(leftPrimary); - rightPrimary = settings.reorder(rightPrimary); - } - return (leftPrimary < rightPrimary) ? UCOL_LESS : UCOL_GREATER; - } - if(leftPrimary == Collation::NO_CE_PRIMARY) { break; } - } - if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } - - // Compare the buffered secondary & tertiary weights. - // We might skip the secondary level but continue with the case level - // which is turned on separately. - if(CollationSettings::getStrength(options) >= UCOL_SECONDARY) { - if((options & CollationSettings::BACKWARD_SECONDARY) == 0) { - int32_t leftIndex = 0; - int32_t rightIndex = 0; - for(;;) { - uint32_t leftSecondary; - do { - leftSecondary = ((uint32_t)left.getCE(leftIndex++)) >> 16; - } while(leftSecondary == 0); - - uint32_t rightSecondary; - do { - rightSecondary = ((uint32_t)right.getCE(rightIndex++)) >> 16; - } while(rightSecondary == 0); - - if(leftSecondary != rightSecondary) { - return (leftSecondary < rightSecondary) ? UCOL_LESS : UCOL_GREATER; - } - if(leftSecondary == Collation::NO_CE_WEIGHT16) { break; } - } - } else { - // The backwards secondary level compares secondary weights backwards - // within segments separated by the merge separator (U+FFFE, weight 02). - int32_t leftStart = 0; - int32_t rightStart = 0; - for(;;) { - // Find the merge separator or the NO_CE terminator. - uint32_t p; - int32_t leftLimit = leftStart; - while((p = (uint32_t)(left.getCE(leftLimit) >> 32)) > - Collation::MERGE_SEPARATOR_PRIMARY || - p == 0) { - ++leftLimit; - } - int32_t rightLimit = rightStart; - while((p = (uint32_t)(right.getCE(rightLimit) >> 32)) > - Collation::MERGE_SEPARATOR_PRIMARY || - p == 0) { - ++rightLimit; - } - - // Compare the segments. - int32_t leftIndex = leftLimit; - int32_t rightIndex = rightLimit; - for(;;) { - int32_t leftSecondary = 0; - while(leftSecondary == 0 && leftIndex > leftStart) { - leftSecondary = ((uint32_t)left.getCE(--leftIndex)) >> 16; - } - - int32_t rightSecondary = 0; - while(rightSecondary == 0 && rightIndex > rightStart) { - rightSecondary = ((uint32_t)right.getCE(--rightIndex)) >> 16; - } - - if(leftSecondary != rightSecondary) { - return (leftSecondary < rightSecondary) ? UCOL_LESS : UCOL_GREATER; - } - if(leftSecondary == 0) { break; } - } - - // Did we reach the end of either string? - // Both strings have the same number of merge separators, - // or else there would have been a primary-level difference. - U_ASSERT(left.getCE(leftLimit) == right.getCE(rightLimit)); - if(p == Collation::NO_CE_PRIMARY) { break; } - // Skip both merge separators and continue. - leftStart = leftLimit + 1; - rightStart = rightLimit + 1; - } - } - } - - if((options & CollationSettings::CASE_LEVEL) != 0) { - int32_t strength = CollationSettings::getStrength(options); - int32_t leftIndex = 0; - int32_t rightIndex = 0; - for(;;) { - uint32_t leftCase, leftLower32, rightCase; - if(strength == UCOL_PRIMARY) { - // Primary+caseLevel: Ignore case level weights of primary ignorables. - // Otherwise we would get a-umlaut > a - // which is not desirable for accent-insensitive sorting. - // Check for (lower 32 bits) == 0 as well because variable CEs are stored - // with only primary weights. - int64_t ce; - do { - ce = left.getCE(leftIndex++); - leftCase = (uint32_t)ce; - } while((uint32_t)(ce >> 32) == 0 || leftCase == 0); - leftLower32 = leftCase; - leftCase &= 0xc000; - - do { - ce = right.getCE(rightIndex++); - rightCase = (uint32_t)ce; - } while((uint32_t)(ce >> 32) == 0 || rightCase == 0); - rightCase &= 0xc000; - } else { - // Secondary+caseLevel: By analogy with the above, - // ignore case level weights of secondary ignorables. - // - // Note: A tertiary CE has uppercase case bits (0.0.ut) - // to keep tertiary+caseFirst well-formed. - // - // Tertiary+caseLevel: Also ignore case level weights of secondary ignorables. - // Otherwise a tertiary CE's uppercase would be no greater than - // a primary/secondary CE's uppercase. - // (See UCA well-formedness condition 2.) - // We could construct a special case weight higher than uppercase, - // but it's simpler to always ignore case weights of secondary ignorables, - // turning 0.0.ut into 0.0.0.t. - // (See LDML Collation, Case Parameters.) - do { - leftCase = (uint32_t)left.getCE(leftIndex++); - } while(leftCase <= 0xffff); - leftLower32 = leftCase; - leftCase &= 0xc000; - - do { - rightCase = (uint32_t)right.getCE(rightIndex++); - } while(rightCase <= 0xffff); - rightCase &= 0xc000; - } - - // No need to handle NO_CE and MERGE_SEPARATOR specially: - // There is one case weight for each previous-level weight, - // so level length differences were handled there. - if(leftCase != rightCase) { - if((options & CollationSettings::UPPER_FIRST) == 0) { - return (leftCase < rightCase) ? UCOL_LESS : UCOL_GREATER; - } else { - return (leftCase < rightCase) ? UCOL_GREATER : UCOL_LESS; - } - } - if((leftLower32 >> 16) == Collation::NO_CE_WEIGHT16) { break; } - } - } - if(CollationSettings::getStrength(options) <= UCOL_SECONDARY) { return UCOL_EQUAL; } - - uint32_t tertiaryMask = CollationSettings::getTertiaryMask(options); - - int32_t leftIndex = 0; - int32_t rightIndex = 0; - uint32_t anyQuaternaries = 0; - for(;;) { - uint32_t leftLower32, leftTertiary; - do { - leftLower32 = (uint32_t)left.getCE(leftIndex++); - anyQuaternaries |= leftLower32; - U_ASSERT((leftLower32 & Collation::ONLY_TERTIARY_MASK) != 0 || - (leftLower32 & 0xc0c0) == 0); - leftTertiary = leftLower32 & tertiaryMask; - } while(leftTertiary == 0); - - uint32_t rightLower32, rightTertiary; - do { - rightLower32 = (uint32_t)right.getCE(rightIndex++); - anyQuaternaries |= rightLower32; - U_ASSERT((rightLower32 & Collation::ONLY_TERTIARY_MASK) != 0 || - (rightLower32 & 0xc0c0) == 0); - rightTertiary = rightLower32 & tertiaryMask; - } while(rightTertiary == 0); - - if(leftTertiary != rightTertiary) { - if(CollationSettings::sortsTertiaryUpperCaseFirst(options)) { - // Pass through NO_CE and keep real tertiary weights larger than that. - // Do not change the artificial uppercase weight of a tertiary CE (0.0.ut), - // to keep tertiary CEs well-formed. - // Their case+tertiary weights must be greater than those of - // primary and secondary CEs. - if(leftTertiary > Collation::NO_CE_WEIGHT16) { - if(leftLower32 > 0xffff) { - leftTertiary ^= 0xc000; - } else { - leftTertiary += 0x4000; - } - } - if(rightTertiary > Collation::NO_CE_WEIGHT16) { - if(rightLower32 > 0xffff) { - rightTertiary ^= 0xc000; - } else { - rightTertiary += 0x4000; - } - } - } - return (leftTertiary < rightTertiary) ? UCOL_LESS : UCOL_GREATER; - } - if(leftTertiary == Collation::NO_CE_WEIGHT16) { break; } - } - if(CollationSettings::getStrength(options) <= UCOL_TERTIARY) { return UCOL_EQUAL; } - - if(!anyVariable && (anyQuaternaries & 0xc0) == 0) { - // If there are no "variable" CEs and no non-zero quaternary weights, - // then there are no quaternary differences. - return UCOL_EQUAL; - } - - leftIndex = 0; - rightIndex = 0; - for(;;) { - uint32_t leftQuaternary; - do { - int64_t ce = left.getCE(leftIndex++); - leftQuaternary = (uint32_t)ce & 0xffff; - if(leftQuaternary <= Collation::NO_CE_WEIGHT16) { - // Variable primary or completely ignorable or NO_CE. - leftQuaternary = (uint32_t)(ce >> 32); - } else { - // Regular CE, not tertiary ignorable. - // Preserve the quaternary weight in bits 7..6. - leftQuaternary |= 0xffffff3f; - } - } while(leftQuaternary == 0); - - uint32_t rightQuaternary; - do { - int64_t ce = right.getCE(rightIndex++); - rightQuaternary = (uint32_t)ce & 0xffff; - if(rightQuaternary <= Collation::NO_CE_WEIGHT16) { - // Variable primary or completely ignorable or NO_CE. - rightQuaternary = (uint32_t)(ce >> 32); - } else { - // Regular CE, not tertiary ignorable. - // Preserve the quaternary weight in bits 7..6. - rightQuaternary |= 0xffffff3f; - } - } while(rightQuaternary == 0); - - if(leftQuaternary != rightQuaternary) { - // Return the difference, with script reordering. - if(settings.hasReordering()) { - leftQuaternary = settings.reorder(leftQuaternary); - rightQuaternary = settings.reorder(rightQuaternary); - } - return (leftQuaternary < rightQuaternary) ? UCOL_LESS : UCOL_GREATER; - } - if(leftQuaternary == Collation::NO_CE_PRIMARY) { break; } - } - return UCOL_EQUAL; -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1996-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationcompare.cpp +* +* created on: 2012feb14 with new and old collation code +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/ucol.h" +#include "cmemory.h" +#include "collation.h" +#include "collationcompare.h" +#include "collationiterator.h" +#include "collationsettings.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +UCollationResult +CollationCompare::compareUpToQuaternary(CollationIterator &left, CollationIterator &right, + const CollationSettings &settings, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } + + int32_t options = settings.options; + uint32_t variableTop; + if((options & CollationSettings::ALTERNATE_MASK) == 0) { + variableTop = 0; + } else { + // +1 so that we can use "<" and primary ignorables test out early. + variableTop = settings.variableTop + 1; + } + UBool anyVariable = false; + + // Fetch CEs, compare primaries, store secondary & tertiary weights. + for(;;) { + // We fetch CEs until we get a non-ignorable primary or reach the end. + uint32_t leftPrimary; + do { + int64_t ce = left.nextCE(errorCode); + leftPrimary = (uint32_t)(ce >> 32); + if(leftPrimary < variableTop && leftPrimary > Collation::MERGE_SEPARATOR_PRIMARY) { + // Variable CE, shift it to quaternary level. + // Ignore all following primary ignorables, and shift further variable CEs. + anyVariable = true; + do { + // Store only the primary of the variable CE. + left.setCurrentCE(ce & INT64_C(0xffffffff00000000)); + for(;;) { + ce = left.nextCE(errorCode); + leftPrimary = (uint32_t)(ce >> 32); + if(leftPrimary == 0) { + left.setCurrentCE(0); + } else { + break; + } + } + } while(leftPrimary < variableTop && + leftPrimary > Collation::MERGE_SEPARATOR_PRIMARY); + } + } while(leftPrimary == 0); + + uint32_t rightPrimary; + do { + int64_t ce = right.nextCE(errorCode); + rightPrimary = (uint32_t)(ce >> 32); + if(rightPrimary < variableTop && rightPrimary > Collation::MERGE_SEPARATOR_PRIMARY) { + // Variable CE, shift it to quaternary level. + // Ignore all following primary ignorables, and shift further variable CEs. + anyVariable = true; + do { + // Store only the primary of the variable CE. + right.setCurrentCE(ce & INT64_C(0xffffffff00000000)); + for(;;) { + ce = right.nextCE(errorCode); + rightPrimary = (uint32_t)(ce >> 32); + if(rightPrimary == 0) { + right.setCurrentCE(0); + } else { + break; + } + } + } while(rightPrimary < variableTop && + rightPrimary > Collation::MERGE_SEPARATOR_PRIMARY); + } + } while(rightPrimary == 0); + + if(leftPrimary != rightPrimary) { + // Return the primary difference, with script reordering. + if(settings.hasReordering()) { + leftPrimary = settings.reorder(leftPrimary); + rightPrimary = settings.reorder(rightPrimary); + } + return (leftPrimary < rightPrimary) ? UCOL_LESS : UCOL_GREATER; + } + if(leftPrimary == Collation::NO_CE_PRIMARY) { break; } + } + if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } + + // Compare the buffered secondary & tertiary weights. + // We might skip the secondary level but continue with the case level + // which is turned on separately. + if(CollationSettings::getStrength(options) >= UCOL_SECONDARY) { + if((options & CollationSettings::BACKWARD_SECONDARY) == 0) { + int32_t leftIndex = 0; + int32_t rightIndex = 0; + for(;;) { + uint32_t leftSecondary; + do { + leftSecondary = ((uint32_t)left.getCE(leftIndex++)) >> 16; + } while(leftSecondary == 0); + + uint32_t rightSecondary; + do { + rightSecondary = ((uint32_t)right.getCE(rightIndex++)) >> 16; + } while(rightSecondary == 0); + + if(leftSecondary != rightSecondary) { + return (leftSecondary < rightSecondary) ? UCOL_LESS : UCOL_GREATER; + } + if(leftSecondary == Collation::NO_CE_WEIGHT16) { break; } + } + } else { + // The backwards secondary level compares secondary weights backwards + // within segments separated by the merge separator (U+FFFE, weight 02). + int32_t leftStart = 0; + int32_t rightStart = 0; + for(;;) { + // Find the merge separator or the NO_CE terminator. + uint32_t p; + int32_t leftLimit = leftStart; + while((p = (uint32_t)(left.getCE(leftLimit) >> 32)) > + Collation::MERGE_SEPARATOR_PRIMARY || + p == 0) { + ++leftLimit; + } + int32_t rightLimit = rightStart; + while((p = (uint32_t)(right.getCE(rightLimit) >> 32)) > + Collation::MERGE_SEPARATOR_PRIMARY || + p == 0) { + ++rightLimit; + } + + // Compare the segments. + int32_t leftIndex = leftLimit; + int32_t rightIndex = rightLimit; + for(;;) { + int32_t leftSecondary = 0; + while(leftSecondary == 0 && leftIndex > leftStart) { + leftSecondary = ((uint32_t)left.getCE(--leftIndex)) >> 16; + } + + int32_t rightSecondary = 0; + while(rightSecondary == 0 && rightIndex > rightStart) { + rightSecondary = ((uint32_t)right.getCE(--rightIndex)) >> 16; + } + + if(leftSecondary != rightSecondary) { + return (leftSecondary < rightSecondary) ? UCOL_LESS : UCOL_GREATER; + } + if(leftSecondary == 0) { break; } + } + + // Did we reach the end of either string? + // Both strings have the same number of merge separators, + // or else there would have been a primary-level difference. + U_ASSERT(left.getCE(leftLimit) == right.getCE(rightLimit)); + if(p == Collation::NO_CE_PRIMARY) { break; } + // Skip both merge separators and continue. + leftStart = leftLimit + 1; + rightStart = rightLimit + 1; + } + } + } + + if((options & CollationSettings::CASE_LEVEL) != 0) { + int32_t strength = CollationSettings::getStrength(options); + int32_t leftIndex = 0; + int32_t rightIndex = 0; + for(;;) { + uint32_t leftCase, leftLower32, rightCase; + if(strength == UCOL_PRIMARY) { + // Primary+caseLevel: Ignore case level weights of primary ignorables. + // Otherwise we would get a-umlaut > a + // which is not desirable for accent-insensitive sorting. + // Check for (lower 32 bits) == 0 as well because variable CEs are stored + // with only primary weights. + int64_t ce; + do { + ce = left.getCE(leftIndex++); + leftCase = (uint32_t)ce; + } while((uint32_t)(ce >> 32) == 0 || leftCase == 0); + leftLower32 = leftCase; + leftCase &= 0xc000; + + do { + ce = right.getCE(rightIndex++); + rightCase = (uint32_t)ce; + } while((uint32_t)(ce >> 32) == 0 || rightCase == 0); + rightCase &= 0xc000; + } else { + // Secondary+caseLevel: By analogy with the above, + // ignore case level weights of secondary ignorables. + // + // Note: A tertiary CE has uppercase case bits (0.0.ut) + // to keep tertiary+caseFirst well-formed. + // + // Tertiary+caseLevel: Also ignore case level weights of secondary ignorables. + // Otherwise a tertiary CE's uppercase would be no greater than + // a primary/secondary CE's uppercase. + // (See UCA well-formedness condition 2.) + // We could construct a special case weight higher than uppercase, + // but it's simpler to always ignore case weights of secondary ignorables, + // turning 0.0.ut into 0.0.0.t. + // (See LDML Collation, Case Parameters.) + do { + leftCase = (uint32_t)left.getCE(leftIndex++); + } while(leftCase <= 0xffff); + leftLower32 = leftCase; + leftCase &= 0xc000; + + do { + rightCase = (uint32_t)right.getCE(rightIndex++); + } while(rightCase <= 0xffff); + rightCase &= 0xc000; + } + + // No need to handle NO_CE and MERGE_SEPARATOR specially: + // There is one case weight for each previous-level weight, + // so level length differences were handled there. + if(leftCase != rightCase) { + if((options & CollationSettings::UPPER_FIRST) == 0) { + return (leftCase < rightCase) ? UCOL_LESS : UCOL_GREATER; + } else { + return (leftCase < rightCase) ? UCOL_GREATER : UCOL_LESS; + } + } + if((leftLower32 >> 16) == Collation::NO_CE_WEIGHT16) { break; } + } + } + if(CollationSettings::getStrength(options) <= UCOL_SECONDARY) { return UCOL_EQUAL; } + + uint32_t tertiaryMask = CollationSettings::getTertiaryMask(options); + + int32_t leftIndex = 0; + int32_t rightIndex = 0; + uint32_t anyQuaternaries = 0; + for(;;) { + uint32_t leftLower32, leftTertiary; + do { + leftLower32 = (uint32_t)left.getCE(leftIndex++); + anyQuaternaries |= leftLower32; + U_ASSERT((leftLower32 & Collation::ONLY_TERTIARY_MASK) != 0 || + (leftLower32 & 0xc0c0) == 0); + leftTertiary = leftLower32 & tertiaryMask; + } while(leftTertiary == 0); + + uint32_t rightLower32, rightTertiary; + do { + rightLower32 = (uint32_t)right.getCE(rightIndex++); + anyQuaternaries |= rightLower32; + U_ASSERT((rightLower32 & Collation::ONLY_TERTIARY_MASK) != 0 || + (rightLower32 & 0xc0c0) == 0); + rightTertiary = rightLower32 & tertiaryMask; + } while(rightTertiary == 0); + + if(leftTertiary != rightTertiary) { + if(CollationSettings::sortsTertiaryUpperCaseFirst(options)) { + // Pass through NO_CE and keep real tertiary weights larger than that. + // Do not change the artificial uppercase weight of a tertiary CE (0.0.ut), + // to keep tertiary CEs well-formed. + // Their case+tertiary weights must be greater than those of + // primary and secondary CEs. + if(leftTertiary > Collation::NO_CE_WEIGHT16) { + if(leftLower32 > 0xffff) { + leftTertiary ^= 0xc000; + } else { + leftTertiary += 0x4000; + } + } + if(rightTertiary > Collation::NO_CE_WEIGHT16) { + if(rightLower32 > 0xffff) { + rightTertiary ^= 0xc000; + } else { + rightTertiary += 0x4000; + } + } + } + return (leftTertiary < rightTertiary) ? UCOL_LESS : UCOL_GREATER; + } + if(leftTertiary == Collation::NO_CE_WEIGHT16) { break; } + } + if(CollationSettings::getStrength(options) <= UCOL_TERTIARY) { return UCOL_EQUAL; } + + if(!anyVariable && (anyQuaternaries & 0xc0) == 0) { + // If there are no "variable" CEs and no non-zero quaternary weights, + // then there are no quaternary differences. + return UCOL_EQUAL; + } + + leftIndex = 0; + rightIndex = 0; + for(;;) { + uint32_t leftQuaternary; + do { + int64_t ce = left.getCE(leftIndex++); + leftQuaternary = (uint32_t)ce & 0xffff; + if(leftQuaternary <= Collation::NO_CE_WEIGHT16) { + // Variable primary or completely ignorable or NO_CE. + leftQuaternary = (uint32_t)(ce >> 32); + } else { + // Regular CE, not tertiary ignorable. + // Preserve the quaternary weight in bits 7..6. + leftQuaternary |= 0xffffff3f; + } + } while(leftQuaternary == 0); + + uint32_t rightQuaternary; + do { + int64_t ce = right.getCE(rightIndex++); + rightQuaternary = (uint32_t)ce & 0xffff; + if(rightQuaternary <= Collation::NO_CE_WEIGHT16) { + // Variable primary or completely ignorable or NO_CE. + rightQuaternary = (uint32_t)(ce >> 32); + } else { + // Regular CE, not tertiary ignorable. + // Preserve the quaternary weight in bits 7..6. + rightQuaternary |= 0xffffff3f; + } + } while(rightQuaternary == 0); + + if(leftQuaternary != rightQuaternary) { + // Return the difference, with script reordering. + if(settings.hasReordering()) { + leftQuaternary = settings.reorder(leftQuaternary); + rightQuaternary = settings.reorder(rightQuaternary); + } + return (leftQuaternary < rightQuaternary) ? UCOL_LESS : UCOL_GREATER; + } + if(leftQuaternary == Collation::NO_CE_PRIMARY) { break; } + } + return UCOL_EQUAL; +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationcompare.h b/deps/icu-small/source/i18n/collationcompare.h index 6ad2d067041bee..cba6b230463107 100644 --- a/deps/icu-small/source/i18n/collationcompare.h +++ b/deps/icu-small/source/i18n/collationcompare.h @@ -1,38 +1,38 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1996-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationcompare.h -* -* created on: 2012feb14 with new and old collation code -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONCOMPARE_H__ -#define __COLLATIONCOMPARE_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/ucol.h" - -U_NAMESPACE_BEGIN - -class CollationIterator; -struct CollationSettings; - -class U_I18N_API CollationCompare /* not : public UObject because all methods are static */ { -public: - static UCollationResult compareUpToQuaternary(CollationIterator &left, CollationIterator &right, - const CollationSettings &settings, - UErrorCode &errorCode); -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONCOMPARE_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1996-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationcompare.h +* +* created on: 2012feb14 with new and old collation code +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONCOMPARE_H__ +#define __COLLATIONCOMPARE_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/ucol.h" + +U_NAMESPACE_BEGIN + +class CollationIterator; +struct CollationSettings; + +class U_I18N_API CollationCompare /* not : public UObject because all methods are static */ { +public: + static UCollationResult compareUpToQuaternary(CollationIterator &left, CollationIterator &right, + const CollationSettings &settings, + UErrorCode &errorCode); +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONCOMPARE_H__ diff --git a/deps/icu-small/source/i18n/collationdata.cpp b/deps/icu-small/source/i18n/collationdata.cpp index 1b8b6a76de39cc..c16a268ab73159 100644 --- a/deps/icu-small/source/i18n/collationdata.cpp +++ b/deps/icu-small/source/i18n/collationdata.cpp @@ -1,390 +1,390 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2012-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationdata.cpp -* -* created on: 2012jul28 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/ucol.h" -#include "unicode/udata.h" -#include "unicode/uscript.h" -#include "cmemory.h" -#include "collation.h" -#include "collationdata.h" -#include "uassert.h" -#include "utrie2.h" -#include "uvectr32.h" - -U_NAMESPACE_BEGIN - -uint32_t -CollationData::getIndirectCE32(uint32_t ce32) const { - U_ASSERT(Collation::isSpecialCE32(ce32)); - int32_t tag = Collation::tagFromCE32(ce32); - if(tag == Collation::DIGIT_TAG) { - // Fetch the non-numeric-collation CE32. - ce32 = ce32s[Collation::indexFromCE32(ce32)]; - } else if(tag == Collation::LEAD_SURROGATE_TAG) { - ce32 = Collation::UNASSIGNED_CE32; - } else if(tag == Collation::U0000_TAG) { - // Fetch the normal ce32 for U+0000. - ce32 = ce32s[0]; - } - return ce32; -} - -uint32_t -CollationData::getFinalCE32(uint32_t ce32) const { - if(Collation::isSpecialCE32(ce32)) { - ce32 = getIndirectCE32(ce32); - } - return ce32; -} - -int64_t -CollationData::getSingleCE(UChar32 c, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return 0; } - // Keep parallel with CollationDataBuilder::getSingleCE(). - const CollationData *d; - uint32_t ce32 = getCE32(c); - if(ce32 == Collation::FALLBACK_CE32) { - d = base; - ce32 = base->getCE32(c); - } else { - d = this; - } - while(Collation::isSpecialCE32(ce32)) { - switch(Collation::tagFromCE32(ce32)) { - case Collation::LATIN_EXPANSION_TAG: - case Collation::BUILDER_DATA_TAG: - case Collation::PREFIX_TAG: - case Collation::CONTRACTION_TAG: - case Collation::HANGUL_TAG: - case Collation::LEAD_SURROGATE_TAG: - errorCode = U_UNSUPPORTED_ERROR; - return 0; - case Collation::FALLBACK_TAG: - case Collation::RESERVED_TAG_3: - errorCode = U_INTERNAL_PROGRAM_ERROR; - return 0; - case Collation::LONG_PRIMARY_TAG: - return Collation::ceFromLongPrimaryCE32(ce32); - case Collation::LONG_SECONDARY_TAG: - return Collation::ceFromLongSecondaryCE32(ce32); - case Collation::EXPANSION32_TAG: - if(Collation::lengthFromCE32(ce32) == 1) { - ce32 = d->ce32s[Collation::indexFromCE32(ce32)]; - break; - } else { - errorCode = U_UNSUPPORTED_ERROR; - return 0; - } - case Collation::EXPANSION_TAG: { - if(Collation::lengthFromCE32(ce32) == 1) { - return d->ces[Collation::indexFromCE32(ce32)]; - } else { - errorCode = U_UNSUPPORTED_ERROR; - return 0; - } - } - case Collation::DIGIT_TAG: - // Fetch the non-numeric-collation CE32 and continue. - ce32 = d->ce32s[Collation::indexFromCE32(ce32)]; - break; - case Collation::U0000_TAG: - U_ASSERT(c == 0); - // Fetch the normal ce32 for U+0000 and continue. - ce32 = d->ce32s[0]; - break; - case Collation::OFFSET_TAG: - return d->getCEFromOffsetCE32(c, ce32); - case Collation::IMPLICIT_TAG: - return Collation::unassignedCEFromCodePoint(c); - } - } - return Collation::ceFromSimpleCE32(ce32); -} - -uint32_t -CollationData::getFirstPrimaryForGroup(int32_t script) const { - int32_t index = getScriptIndex(script); - return index == 0 ? 0 : (uint32_t)scriptStarts[index] << 16; -} - -uint32_t -CollationData::getLastPrimaryForGroup(int32_t script) const { - int32_t index = getScriptIndex(script); - if(index == 0) { - return 0; - } - uint32_t limit = scriptStarts[index + 1]; - return (limit << 16) - 1; -} - -int32_t -CollationData::getGroupForPrimary(uint32_t p) const { - p >>= 16; - if(p < scriptStarts[1] || scriptStarts[scriptStartsLength - 1] <= p) { - return -1; - } - int32_t index = 1; - while(p >= scriptStarts[index + 1]) { ++index; } - for(int32_t i = 0; i < numScripts; ++i) { - if(scriptsIndex[i] == index) { - return i; - } - } - for(int32_t i = 0; i < MAX_NUM_SPECIAL_REORDER_CODES; ++i) { - if(scriptsIndex[numScripts + i] == index) { - return UCOL_REORDER_CODE_FIRST + i; - } - } - return -1; -} - -int32_t -CollationData::getScriptIndex(int32_t script) const { - if(script < 0) { - return 0; - } else if(script < numScripts) { - return scriptsIndex[script]; - } else if(script < UCOL_REORDER_CODE_FIRST) { - return 0; - } else { - script -= UCOL_REORDER_CODE_FIRST; - if(script < MAX_NUM_SPECIAL_REORDER_CODES) { - return scriptsIndex[numScripts + script]; - } else { - return 0; - } - } -} - -int32_t -CollationData::getEquivalentScripts(int32_t script, - int32_t dest[], int32_t capacity, - UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return 0; } - int32_t index = getScriptIndex(script); - if(index == 0) { return 0; } - if(script >= UCOL_REORDER_CODE_FIRST) { - // Special groups have no aliases. - if(capacity > 0) { - dest[0] = script; - } else { - errorCode = U_BUFFER_OVERFLOW_ERROR; - } - return 1; - } - - int32_t length = 0; - for(int32_t i = 0; i < numScripts; ++i) { - if(scriptsIndex[i] == index) { - if(length < capacity) { - dest[length] = i; - } - ++length; - } - } - if(length > capacity) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - } - return length; -} - -void -CollationData::makeReorderRanges(const int32_t *reorder, int32_t length, - UVector32 &ranges, UErrorCode &errorCode) const { - makeReorderRanges(reorder, length, false, ranges, errorCode); -} - -void -CollationData::makeReorderRanges(const int32_t *reorder, int32_t length, - UBool latinMustMove, - UVector32 &ranges, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return; } - ranges.removeAllElements(); - if(length == 0 || (length == 1 && reorder[0] == USCRIPT_UNKNOWN)) { - return; - } - - // Maps each script-or-group range to a new lead byte. - uint8_t table[MAX_NUM_SCRIPT_RANGES]; - uprv_memset(table, 0, sizeof(table)); - - { - // Set "don't care" values for reserved ranges. - int32_t index = scriptsIndex[ - numScripts + REORDER_RESERVED_BEFORE_LATIN - UCOL_REORDER_CODE_FIRST]; - if(index != 0) { - table[index] = 0xff; - } - index = scriptsIndex[ - numScripts + REORDER_RESERVED_AFTER_LATIN - UCOL_REORDER_CODE_FIRST]; - if(index != 0) { - table[index] = 0xff; - } - } - - // Never reorder special low and high primary lead bytes. - U_ASSERT(scriptStartsLength >= 2); - U_ASSERT(scriptStarts[0] == 0); - int32_t lowStart = scriptStarts[1]; - U_ASSERT(lowStart == ((Collation::MERGE_SEPARATOR_BYTE + 1) << 8)); - int32_t highLimit = scriptStarts[scriptStartsLength - 1]; - U_ASSERT(highLimit == (Collation::TRAIL_WEIGHT_BYTE << 8)); - - // Get the set of special reorder codes in the input list. - // This supports a fixed number of special reorder codes; - // it works for data with codes beyond UCOL_REORDER_CODE_LIMIT. - uint32_t specials = 0; - for(int32_t i = 0; i < length; ++i) { - int32_t reorderCode = reorder[i] - UCOL_REORDER_CODE_FIRST; - if(0 <= reorderCode && reorderCode < MAX_NUM_SPECIAL_REORDER_CODES) { - specials |= (uint32_t)1 << reorderCode; - } - } - - // Start the reordering with the special low reorder codes that do not occur in the input. - for(int32_t i = 0; i < MAX_NUM_SPECIAL_REORDER_CODES; ++i) { - int32_t index = scriptsIndex[numScripts + i]; - if(index != 0 && (specials & ((uint32_t)1 << i)) == 0) { - lowStart = addLowScriptRange(table, index, lowStart); - } - } - - // Skip the reserved range before Latin if Latin is the first script, - // so that we do not move it unnecessarily. - int32_t skippedReserved = 0; - if(specials == 0 && reorder[0] == USCRIPT_LATIN && !latinMustMove) { - int32_t index = scriptsIndex[USCRIPT_LATIN]; - U_ASSERT(index != 0); - int32_t start = scriptStarts[index]; - U_ASSERT(lowStart <= start); - skippedReserved = start - lowStart; - lowStart = start; - } - - // Reorder according to the input scripts, continuing from the bottom of the primary range. - int32_t originalLength = length; // length will be decremented if "others" is in the list. - UBool hasReorderToEnd = false; - for(int32_t i = 0; i < length;) { - int32_t script = reorder[i++]; - if(script == USCRIPT_UNKNOWN) { - // Put the remaining scripts at the top. - hasReorderToEnd = true; - while(i < length) { - script = reorder[--length]; - if(script == USCRIPT_UNKNOWN || // Must occur at most once. - script == UCOL_REORDER_CODE_DEFAULT) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - int32_t index = getScriptIndex(script); - if(index == 0) { continue; } - if(table[index] != 0) { // Duplicate or equivalent script. - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - highLimit = addHighScriptRange(table, index, highLimit); - } - break; - } - if(script == UCOL_REORDER_CODE_DEFAULT) { - // The default code must be the only one in the list, and that is handled by the caller. - // Otherwise it must not be used. - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - int32_t index = getScriptIndex(script); - if(index == 0) { continue; } - if(table[index] != 0) { // Duplicate or equivalent script. - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - lowStart = addLowScriptRange(table, index, lowStart); - } - - // Put all remaining scripts into the middle. - for(int32_t i = 1; i < scriptStartsLength - 1; ++i) { - int32_t leadByte = table[i]; - if(leadByte != 0) { continue; } - int32_t start = scriptStarts[i]; - if(!hasReorderToEnd && start > lowStart) { - // No need to move this script. - lowStart = start; - } - lowStart = addLowScriptRange(table, i, lowStart); - } - if(lowStart > highLimit) { - if((lowStart - (skippedReserved & 0xff00)) <= highLimit) { - // Try not skipping the before-Latin reserved range. - makeReorderRanges(reorder, originalLength, true, ranges, errorCode); - return; - } - // We need more primary lead bytes than available, despite the reserved ranges. - errorCode = U_BUFFER_OVERFLOW_ERROR; - return; - } - - // Turn lead bytes into a list of (limit, offset) pairs. - // Encode each pair in one list element: - // Upper 16 bits = limit, lower 16 = signed lead byte offset. - int32_t offset = 0; - for(int32_t i = 1;; ++i) { - int32_t nextOffset = offset; - while(i < scriptStartsLength - 1) { - int32_t newLeadByte = table[i]; - if(newLeadByte == 0xff) { - // "Don't care" lead byte for reserved range, continue with current offset. - } else { - nextOffset = newLeadByte - (scriptStarts[i] >> 8); - if(nextOffset != offset) { break; } - } - ++i; - } - if(offset != 0 || i < scriptStartsLength - 1) { - ranges.addElement(((int32_t)scriptStarts[i] << 16) | (offset & 0xffff), errorCode); - } - if(i == scriptStartsLength - 1) { break; } - offset = nextOffset; - } -} - -int32_t -CollationData::addLowScriptRange(uint8_t table[], int32_t index, int32_t lowStart) const { - int32_t start = scriptStarts[index]; - if((start & 0xff) < (lowStart & 0xff)) { - lowStart += 0x100; - } - table[index] = (uint8_t)(lowStart >> 8); - int32_t limit = scriptStarts[index + 1]; - lowStart = ((lowStart & 0xff00) + ((limit & 0xff00) - (start & 0xff00))) | (limit & 0xff); - return lowStart; -} - -int32_t -CollationData::addHighScriptRange(uint8_t table[], int32_t index, int32_t highLimit) const { - int32_t limit = scriptStarts[index + 1]; - if((limit & 0xff) > (highLimit & 0xff)) { - highLimit -= 0x100; - } - int32_t start = scriptStarts[index]; - highLimit = ((highLimit & 0xff00) - ((limit & 0xff00) - (start & 0xff00))) | (start & 0xff); - table[index] = (uint8_t)(highLimit >> 8); - return highLimit; -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2012-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationdata.cpp +* +* created on: 2012jul28 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/ucol.h" +#include "unicode/udata.h" +#include "unicode/uscript.h" +#include "cmemory.h" +#include "collation.h" +#include "collationdata.h" +#include "uassert.h" +#include "utrie2.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +uint32_t +CollationData::getIndirectCE32(uint32_t ce32) const { + U_ASSERT(Collation::isSpecialCE32(ce32)); + int32_t tag = Collation::tagFromCE32(ce32); + if(tag == Collation::DIGIT_TAG) { + // Fetch the non-numeric-collation CE32. + ce32 = ce32s[Collation::indexFromCE32(ce32)]; + } else if(tag == Collation::LEAD_SURROGATE_TAG) { + ce32 = Collation::UNASSIGNED_CE32; + } else if(tag == Collation::U0000_TAG) { + // Fetch the normal ce32 for U+0000. + ce32 = ce32s[0]; + } + return ce32; +} + +uint32_t +CollationData::getFinalCE32(uint32_t ce32) const { + if(Collation::isSpecialCE32(ce32)) { + ce32 = getIndirectCE32(ce32); + } + return ce32; +} + +int64_t +CollationData::getSingleCE(UChar32 c, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return 0; } + // Keep parallel with CollationDataBuilder::getSingleCE(). + const CollationData *d; + uint32_t ce32 = getCE32(c); + if(ce32 == Collation::FALLBACK_CE32) { + d = base; + ce32 = base->getCE32(c); + } else { + d = this; + } + while(Collation::isSpecialCE32(ce32)) { + switch(Collation::tagFromCE32(ce32)) { + case Collation::LATIN_EXPANSION_TAG: + case Collation::BUILDER_DATA_TAG: + case Collation::PREFIX_TAG: + case Collation::CONTRACTION_TAG: + case Collation::HANGUL_TAG: + case Collation::LEAD_SURROGATE_TAG: + errorCode = U_UNSUPPORTED_ERROR; + return 0; + case Collation::FALLBACK_TAG: + case Collation::RESERVED_TAG_3: + errorCode = U_INTERNAL_PROGRAM_ERROR; + return 0; + case Collation::LONG_PRIMARY_TAG: + return Collation::ceFromLongPrimaryCE32(ce32); + case Collation::LONG_SECONDARY_TAG: + return Collation::ceFromLongSecondaryCE32(ce32); + case Collation::EXPANSION32_TAG: + if(Collation::lengthFromCE32(ce32) == 1) { + ce32 = d->ce32s[Collation::indexFromCE32(ce32)]; + break; + } else { + errorCode = U_UNSUPPORTED_ERROR; + return 0; + } + case Collation::EXPANSION_TAG: { + if(Collation::lengthFromCE32(ce32) == 1) { + return d->ces[Collation::indexFromCE32(ce32)]; + } else { + errorCode = U_UNSUPPORTED_ERROR; + return 0; + } + } + case Collation::DIGIT_TAG: + // Fetch the non-numeric-collation CE32 and continue. + ce32 = d->ce32s[Collation::indexFromCE32(ce32)]; + break; + case Collation::U0000_TAG: + U_ASSERT(c == 0); + // Fetch the normal ce32 for U+0000 and continue. + ce32 = d->ce32s[0]; + break; + case Collation::OFFSET_TAG: + return d->getCEFromOffsetCE32(c, ce32); + case Collation::IMPLICIT_TAG: + return Collation::unassignedCEFromCodePoint(c); + } + } + return Collation::ceFromSimpleCE32(ce32); +} + +uint32_t +CollationData::getFirstPrimaryForGroup(int32_t script) const { + int32_t index = getScriptIndex(script); + return index == 0 ? 0 : (uint32_t)scriptStarts[index] << 16; +} + +uint32_t +CollationData::getLastPrimaryForGroup(int32_t script) const { + int32_t index = getScriptIndex(script); + if(index == 0) { + return 0; + } + uint32_t limit = scriptStarts[index + 1]; + return (limit << 16) - 1; +} + +int32_t +CollationData::getGroupForPrimary(uint32_t p) const { + p >>= 16; + if(p < scriptStarts[1] || scriptStarts[scriptStartsLength - 1] <= p) { + return -1; + } + int32_t index = 1; + while(p >= scriptStarts[index + 1]) { ++index; } + for(int32_t i = 0; i < numScripts; ++i) { + if(scriptsIndex[i] == index) { + return i; + } + } + for(int32_t i = 0; i < MAX_NUM_SPECIAL_REORDER_CODES; ++i) { + if(scriptsIndex[numScripts + i] == index) { + return UCOL_REORDER_CODE_FIRST + i; + } + } + return -1; +} + +int32_t +CollationData::getScriptIndex(int32_t script) const { + if(script < 0) { + return 0; + } else if(script < numScripts) { + return scriptsIndex[script]; + } else if(script < UCOL_REORDER_CODE_FIRST) { + return 0; + } else { + script -= UCOL_REORDER_CODE_FIRST; + if(script < MAX_NUM_SPECIAL_REORDER_CODES) { + return scriptsIndex[numScripts + script]; + } else { + return 0; + } + } +} + +int32_t +CollationData::getEquivalentScripts(int32_t script, + int32_t dest[], int32_t capacity, + UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return 0; } + int32_t index = getScriptIndex(script); + if(index == 0) { return 0; } + if(script >= UCOL_REORDER_CODE_FIRST) { + // Special groups have no aliases. + if(capacity > 0) { + dest[0] = script; + } else { + errorCode = U_BUFFER_OVERFLOW_ERROR; + } + return 1; + } + + int32_t length = 0; + for(int32_t i = 0; i < numScripts; ++i) { + if(scriptsIndex[i] == index) { + if(length < capacity) { + dest[length] = i; + } + ++length; + } + } + if(length > capacity) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + } + return length; +} + +void +CollationData::makeReorderRanges(const int32_t *reorder, int32_t length, + UVector32 &ranges, UErrorCode &errorCode) const { + makeReorderRanges(reorder, length, false, ranges, errorCode); +} + +void +CollationData::makeReorderRanges(const int32_t *reorder, int32_t length, + UBool latinMustMove, + UVector32 &ranges, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return; } + ranges.removeAllElements(); + if(length == 0 || (length == 1 && reorder[0] == USCRIPT_UNKNOWN)) { + return; + } + + // Maps each script-or-group range to a new lead byte. + uint8_t table[MAX_NUM_SCRIPT_RANGES]; + uprv_memset(table, 0, sizeof(table)); + + { + // Set "don't care" values for reserved ranges. + int32_t index = scriptsIndex[ + numScripts + REORDER_RESERVED_BEFORE_LATIN - UCOL_REORDER_CODE_FIRST]; + if(index != 0) { + table[index] = 0xff; + } + index = scriptsIndex[ + numScripts + REORDER_RESERVED_AFTER_LATIN - UCOL_REORDER_CODE_FIRST]; + if(index != 0) { + table[index] = 0xff; + } + } + + // Never reorder special low and high primary lead bytes. + U_ASSERT(scriptStartsLength >= 2); + U_ASSERT(scriptStarts[0] == 0); + int32_t lowStart = scriptStarts[1]; + U_ASSERT(lowStart == ((Collation::MERGE_SEPARATOR_BYTE + 1) << 8)); + int32_t highLimit = scriptStarts[scriptStartsLength - 1]; + U_ASSERT(highLimit == (Collation::TRAIL_WEIGHT_BYTE << 8)); + + // Get the set of special reorder codes in the input list. + // This supports a fixed number of special reorder codes; + // it works for data with codes beyond UCOL_REORDER_CODE_LIMIT. + uint32_t specials = 0; + for(int32_t i = 0; i < length; ++i) { + int32_t reorderCode = reorder[i] - UCOL_REORDER_CODE_FIRST; + if(0 <= reorderCode && reorderCode < MAX_NUM_SPECIAL_REORDER_CODES) { + specials |= (uint32_t)1 << reorderCode; + } + } + + // Start the reordering with the special low reorder codes that do not occur in the input. + for(int32_t i = 0; i < MAX_NUM_SPECIAL_REORDER_CODES; ++i) { + int32_t index = scriptsIndex[numScripts + i]; + if(index != 0 && (specials & ((uint32_t)1 << i)) == 0) { + lowStart = addLowScriptRange(table, index, lowStart); + } + } + + // Skip the reserved range before Latin if Latin is the first script, + // so that we do not move it unnecessarily. + int32_t skippedReserved = 0; + if(specials == 0 && reorder[0] == USCRIPT_LATIN && !latinMustMove) { + int32_t index = scriptsIndex[USCRIPT_LATIN]; + U_ASSERT(index != 0); + int32_t start = scriptStarts[index]; + U_ASSERT(lowStart <= start); + skippedReserved = start - lowStart; + lowStart = start; + } + + // Reorder according to the input scripts, continuing from the bottom of the primary range. + int32_t originalLength = length; // length will be decremented if "others" is in the list. + UBool hasReorderToEnd = false; + for(int32_t i = 0; i < length;) { + int32_t script = reorder[i++]; + if(script == USCRIPT_UNKNOWN) { + // Put the remaining scripts at the top. + hasReorderToEnd = true; + while(i < length) { + script = reorder[--length]; + if(script == USCRIPT_UNKNOWN || // Must occur at most once. + script == UCOL_REORDER_CODE_DEFAULT) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + int32_t index = getScriptIndex(script); + if(index == 0) { continue; } + if(table[index] != 0) { // Duplicate or equivalent script. + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + highLimit = addHighScriptRange(table, index, highLimit); + } + break; + } + if(script == UCOL_REORDER_CODE_DEFAULT) { + // The default code must be the only one in the list, and that is handled by the caller. + // Otherwise it must not be used. + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + int32_t index = getScriptIndex(script); + if(index == 0) { continue; } + if(table[index] != 0) { // Duplicate or equivalent script. + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + lowStart = addLowScriptRange(table, index, lowStart); + } + + // Put all remaining scripts into the middle. + for(int32_t i = 1; i < scriptStartsLength - 1; ++i) { + int32_t leadByte = table[i]; + if(leadByte != 0) { continue; } + int32_t start = scriptStarts[i]; + if(!hasReorderToEnd && start > lowStart) { + // No need to move this script. + lowStart = start; + } + lowStart = addLowScriptRange(table, i, lowStart); + } + if(lowStart > highLimit) { + if((lowStart - (skippedReserved & 0xff00)) <= highLimit) { + // Try not skipping the before-Latin reserved range. + makeReorderRanges(reorder, originalLength, true, ranges, errorCode); + return; + } + // We need more primary lead bytes than available, despite the reserved ranges. + errorCode = U_BUFFER_OVERFLOW_ERROR; + return; + } + + // Turn lead bytes into a list of (limit, offset) pairs. + // Encode each pair in one list element: + // Upper 16 bits = limit, lower 16 = signed lead byte offset. + int32_t offset = 0; + for(int32_t i = 1;; ++i) { + int32_t nextOffset = offset; + while(i < scriptStartsLength - 1) { + int32_t newLeadByte = table[i]; + if(newLeadByte == 0xff) { + // "Don't care" lead byte for reserved range, continue with current offset. + } else { + nextOffset = newLeadByte - (scriptStarts[i] >> 8); + if(nextOffset != offset) { break; } + } + ++i; + } + if(offset != 0 || i < scriptStartsLength - 1) { + ranges.addElement(((int32_t)scriptStarts[i] << 16) | (offset & 0xffff), errorCode); + } + if(i == scriptStartsLength - 1) { break; } + offset = nextOffset; + } +} + +int32_t +CollationData::addLowScriptRange(uint8_t table[], int32_t index, int32_t lowStart) const { + int32_t start = scriptStarts[index]; + if((start & 0xff) < (lowStart & 0xff)) { + lowStart += 0x100; + } + table[index] = (uint8_t)(lowStart >> 8); + int32_t limit = scriptStarts[index + 1]; + lowStart = ((lowStart & 0xff00) + ((limit & 0xff00) - (start & 0xff00))) | (limit & 0xff); + return lowStart; +} + +int32_t +CollationData::addHighScriptRange(uint8_t table[], int32_t index, int32_t highLimit) const { + int32_t limit = scriptStarts[index + 1]; + if((limit & 0xff) > (highLimit & 0xff)) { + highLimit -= 0x100; + } + int32_t start = scriptStarts[index]; + highLimit = ((highLimit & 0xff00) - ((limit & 0xff00) - (start & 0xff00))) | (start & 0xff); + table[index] = (uint8_t)(highLimit >> 8); + return highLimit; +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationdata.h b/deps/icu-small/source/i18n/collationdata.h index 71bf17abd0d4db..9fba10c1356046 100644 --- a/deps/icu-small/source/i18n/collationdata.h +++ b/deps/icu-small/source/i18n/collationdata.h @@ -1,258 +1,258 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationdata.h -* -* created on: 2010oct27 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONDATA_H__ -#define __COLLATIONDATA_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/ucol.h" -#include "unicode/uniset.h" -#include "collation.h" -#include "normalizer2impl.h" -#include "utrie2.h" - -struct UDataMemory; - -U_NAMESPACE_BEGIN - -class UVector32; - -/** - * Collation data container. - * Immutable data created by a CollationDataBuilder, or loaded from a file, - * or deserialized from API-provided binary data. - * - * Includes data for the collation base (root/default), aliased if this is not the base. - */ -struct U_I18N_API CollationData : public UMemory { - // Note: The ucadata.icu loader could discover the reserved ranges by setting an array - // parallel with the ranges, and resetting ranges that are indexed. - // The reordering builder code could clone the resulting template array. - static constexpr int32_t REORDER_RESERVED_BEFORE_LATIN = UCOL_REORDER_CODE_FIRST + 14; - static constexpr int32_t REORDER_RESERVED_AFTER_LATIN = REORDER_RESERVED_BEFORE_LATIN + 1; - - static constexpr int32_t MAX_NUM_SPECIAL_REORDER_CODES = 8; - /** C++ only, data reader check scriptStartsLength. */ - static constexpr int32_t MAX_NUM_SCRIPT_RANGES = 256; - - CollationData(const Normalizer2Impl &nfc) - : trie(NULL), - ce32s(NULL), ces(NULL), contexts(NULL), base(NULL), - jamoCE32s(NULL), - nfcImpl(nfc), - numericPrimary(0x12000000), - ce32sLength(0), cesLength(0), contextsLength(0), - compressibleBytes(NULL), - unsafeBackwardSet(NULL), - fastLatinTable(NULL), fastLatinTableLength(0), - numScripts(0), scriptsIndex(NULL), scriptStarts(NULL), scriptStartsLength(0), - rootElements(NULL), rootElementsLength(0) {} - - uint32_t getCE32(UChar32 c) const { - return UTRIE2_GET32(trie, c); - } - - uint32_t getCE32FromSupplementary(UChar32 c) const { - return UTRIE2_GET32_FROM_SUPP(trie, c); - } - - UBool isDigit(UChar32 c) const { - return c < 0x660 ? c <= 0x39 && 0x30 <= c : - Collation::hasCE32Tag(getCE32(c), Collation::DIGIT_TAG); - } - - UBool isUnsafeBackward(UChar32 c, UBool numeric) const { - return unsafeBackwardSet->contains(c) || (numeric && isDigit(c)); - } - - UBool isCompressibleLeadByte(uint32_t b) const { - return compressibleBytes[b]; - } - - inline UBool isCompressiblePrimary(uint32_t p) const { - return isCompressibleLeadByte(p >> 24); - } - - /** - * Returns the CE32 from two contexts words. - * Access to the defaultCE32 for contraction and prefix matching. - */ - static uint32_t readCE32(const UChar *p) { - return ((uint32_t)p[0] << 16) | p[1]; - } - - /** - * Returns the CE32 for an indirect special CE32 (e.g., with DIGIT_TAG). - * Requires that ce32 is special. - */ - uint32_t getIndirectCE32(uint32_t ce32) const; - /** - * Returns the CE32 for an indirect special CE32 (e.g., with DIGIT_TAG), - * if ce32 is special. - */ - uint32_t getFinalCE32(uint32_t ce32) const; - - /** - * Computes a CE from c's ce32 which has the OFFSET_TAG. - */ - int64_t getCEFromOffsetCE32(UChar32 c, uint32_t ce32) const { - int64_t dataCE = ces[Collation::indexFromCE32(ce32)]; - return Collation::makeCE(Collation::getThreeBytePrimaryForOffsetData(c, dataCE)); - } - - /** - * Returns the single CE that c maps to. - * Sets U_UNSUPPORTED_ERROR if c does not map to a single CE. - */ - int64_t getSingleCE(UChar32 c, UErrorCode &errorCode) const; - - /** - * Returns the FCD16 value for code point c. c must be >= 0. - */ - uint16_t getFCD16(UChar32 c) const { - return nfcImpl.getFCD16(c); - } - - /** - * Returns the first primary for the script's reordering group. - * @return the primary with only the first primary lead byte of the group - * (not necessarily an actual root collator primary weight), - * or 0 if the script is unknown - */ - uint32_t getFirstPrimaryForGroup(int32_t script) const; - - /** - * Returns the last primary for the script's reordering group. - * @return the last primary of the group - * (not an actual root collator primary weight), - * or 0 if the script is unknown - */ - uint32_t getLastPrimaryForGroup(int32_t script) const; - - /** - * Finds the reordering group which contains the primary weight. - * @return the first script of the group, or -1 if the weight is beyond the last group - */ - int32_t getGroupForPrimary(uint32_t p) const; - - int32_t getEquivalentScripts(int32_t script, - int32_t dest[], int32_t capacity, UErrorCode &errorCode) const; - - /** - * Writes the permutation of primary-weight ranges - * for the given reordering of scripts and groups. - * The caller checks for illegal arguments and - * takes care of [DEFAULT] and memory allocation. - * - * Each list element will be a (limit, offset) pair as described - * for the CollationSettings::reorderRanges. - * The list will be empty if no ranges are reordered. - */ - void makeReorderRanges(const int32_t *reorder, int32_t length, - UVector32 &ranges, UErrorCode &errorCode) const; - - /** @see jamoCE32s */ - static const int32_t JAMO_CE32S_LENGTH = 19 + 21 + 27; - - /** Main lookup trie. */ - const UTrie2 *trie; - /** - * Array of CE32 values. - * At index 0 there must be CE32(U+0000) - * to support U+0000's special-tag for NUL-termination handling. - */ - const uint32_t *ce32s; - /** Array of CE values for expansions and OFFSET_TAG. */ - const int64_t *ces; - /** Array of prefix and contraction-suffix matching data. */ - const UChar *contexts; - /** Base collation data, or NULL if this data itself is a base. */ - const CollationData *base; - /** - * Simple array of JAMO_CE32S_LENGTH=19+21+27 CE32s, one per canonical Jamo L/V/T. - * They are normally simple CE32s, rarely expansions. - * For fast handling of HANGUL_TAG. - */ - const uint32_t *jamoCE32s; - const Normalizer2Impl &nfcImpl; - /** The single-byte primary weight (xx000000) for numeric collation. */ - uint32_t numericPrimary; - - int32_t ce32sLength; - int32_t cesLength; - int32_t contextsLength; - - /** 256 flags for which primary-weight lead bytes are compressible. */ - const UBool *compressibleBytes; - /** - * Set of code points that are unsafe for starting string comparison after an identical prefix, - * or in backwards CE iteration. - */ - const UnicodeSet *unsafeBackwardSet; - - /** - * Fast Latin table for common-Latin-text string comparisons. - * Data structure see class CollationFastLatin. - */ - const uint16_t *fastLatinTable; - int32_t fastLatinTableLength; - - /** - * Data for scripts and reordering groups. - * Uses include building a reordering permutation table and - * providing script boundaries to AlphabeticIndex. - */ - int32_t numScripts; - /** - * The length of scriptsIndex is numScripts+16. - * It maps from a UScriptCode or a special reorder code to an entry in scriptStarts. - * 16 special reorder codes (not all used) are mapped starting at numScripts. - * Up to MAX_NUM_SPECIAL_REORDER_CODES are codes for special groups like space/punct/digit. - * There are special codes at the end for reorder-reserved primary ranges. - * - * Multiple scripts may share a range and index, for example Hira & Kana. - */ - const uint16_t *scriptsIndex; - /** - * Start primary weight (top 16 bits only) for a group/script/reserved range - * indexed by scriptsIndex. - * The first range (separators & terminators) and the last range (trailing weights) - * are not reorderable, and no scriptsIndex entry points to them. - */ - const uint16_t *scriptStarts; - int32_t scriptStartsLength; - - /** - * Collation elements in the root collator. - * Used by the CollationRootElements class. The data structure is described there. - * NULL in a tailoring. - */ - const uint32_t *rootElements; - int32_t rootElementsLength; - -private: - int32_t getScriptIndex(int32_t script) const; - void makeReorderRanges(const int32_t *reorder, int32_t length, - UBool latinMustMove, - UVector32 &ranges, UErrorCode &errorCode) const; - int32_t addLowScriptRange(uint8_t table[], int32_t index, int32_t lowStart) const; - int32_t addHighScriptRange(uint8_t table[], int32_t index, int32_t highLimit) const; -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONDATA_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationdata.h +* +* created on: 2010oct27 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONDATA_H__ +#define __COLLATIONDATA_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/ucol.h" +#include "unicode/uniset.h" +#include "collation.h" +#include "normalizer2impl.h" +#include "utrie2.h" + +struct UDataMemory; + +U_NAMESPACE_BEGIN + +class UVector32; + +/** + * Collation data container. + * Immutable data created by a CollationDataBuilder, or loaded from a file, + * or deserialized from API-provided binary data. + * + * Includes data for the collation base (root/default), aliased if this is not the base. + */ +struct U_I18N_API CollationData : public UMemory { + // Note: The ucadata.icu loader could discover the reserved ranges by setting an array + // parallel with the ranges, and resetting ranges that are indexed. + // The reordering builder code could clone the resulting template array. + static constexpr int32_t REORDER_RESERVED_BEFORE_LATIN = UCOL_REORDER_CODE_FIRST + 14; + static constexpr int32_t REORDER_RESERVED_AFTER_LATIN = REORDER_RESERVED_BEFORE_LATIN + 1; + + static constexpr int32_t MAX_NUM_SPECIAL_REORDER_CODES = 8; + /** C++ only, data reader check scriptStartsLength. */ + static constexpr int32_t MAX_NUM_SCRIPT_RANGES = 256; + + CollationData(const Normalizer2Impl &nfc) + : trie(nullptr), + ce32s(nullptr), ces(nullptr), contexts(nullptr), base(nullptr), + jamoCE32s(nullptr), + nfcImpl(nfc), + numericPrimary(0x12000000), + ce32sLength(0), cesLength(0), contextsLength(0), + compressibleBytes(nullptr), + unsafeBackwardSet(nullptr), + fastLatinTable(nullptr), fastLatinTableLength(0), + numScripts(0), scriptsIndex(nullptr), scriptStarts(nullptr), scriptStartsLength(0), + rootElements(nullptr), rootElementsLength(0) {} + + uint32_t getCE32(UChar32 c) const { + return UTRIE2_GET32(trie, c); + } + + uint32_t getCE32FromSupplementary(UChar32 c) const { + return UTRIE2_GET32_FROM_SUPP(trie, c); + } + + UBool isDigit(UChar32 c) const { + return c < 0x660 ? c <= 0x39 && 0x30 <= c : + Collation::hasCE32Tag(getCE32(c), Collation::DIGIT_TAG); + } + + UBool isUnsafeBackward(UChar32 c, UBool numeric) const { + return unsafeBackwardSet->contains(c) || (numeric && isDigit(c)); + } + + UBool isCompressibleLeadByte(uint32_t b) const { + return compressibleBytes[b]; + } + + inline UBool isCompressiblePrimary(uint32_t p) const { + return isCompressibleLeadByte(p >> 24); + } + + /** + * Returns the CE32 from two contexts words. + * Access to the defaultCE32 for contraction and prefix matching. + */ + static uint32_t readCE32(const char16_t *p) { + return ((uint32_t)p[0] << 16) | p[1]; + } + + /** + * Returns the CE32 for an indirect special CE32 (e.g., with DIGIT_TAG). + * Requires that ce32 is special. + */ + uint32_t getIndirectCE32(uint32_t ce32) const; + /** + * Returns the CE32 for an indirect special CE32 (e.g., with DIGIT_TAG), + * if ce32 is special. + */ + uint32_t getFinalCE32(uint32_t ce32) const; + + /** + * Computes a CE from c's ce32 which has the OFFSET_TAG. + */ + int64_t getCEFromOffsetCE32(UChar32 c, uint32_t ce32) const { + int64_t dataCE = ces[Collation::indexFromCE32(ce32)]; + return Collation::makeCE(Collation::getThreeBytePrimaryForOffsetData(c, dataCE)); + } + + /** + * Returns the single CE that c maps to. + * Sets U_UNSUPPORTED_ERROR if c does not map to a single CE. + */ + int64_t getSingleCE(UChar32 c, UErrorCode &errorCode) const; + + /** + * Returns the FCD16 value for code point c. c must be >= 0. + */ + uint16_t getFCD16(UChar32 c) const { + return nfcImpl.getFCD16(c); + } + + /** + * Returns the first primary for the script's reordering group. + * @return the primary with only the first primary lead byte of the group + * (not necessarily an actual root collator primary weight), + * or 0 if the script is unknown + */ + uint32_t getFirstPrimaryForGroup(int32_t script) const; + + /** + * Returns the last primary for the script's reordering group. + * @return the last primary of the group + * (not an actual root collator primary weight), + * or 0 if the script is unknown + */ + uint32_t getLastPrimaryForGroup(int32_t script) const; + + /** + * Finds the reordering group which contains the primary weight. + * @return the first script of the group, or -1 if the weight is beyond the last group + */ + int32_t getGroupForPrimary(uint32_t p) const; + + int32_t getEquivalentScripts(int32_t script, + int32_t dest[], int32_t capacity, UErrorCode &errorCode) const; + + /** + * Writes the permutation of primary-weight ranges + * for the given reordering of scripts and groups. + * The caller checks for illegal arguments and + * takes care of [DEFAULT] and memory allocation. + * + * Each list element will be a (limit, offset) pair as described + * for the CollationSettings::reorderRanges. + * The list will be empty if no ranges are reordered. + */ + void makeReorderRanges(const int32_t *reorder, int32_t length, + UVector32 &ranges, UErrorCode &errorCode) const; + + /** @see jamoCE32s */ + static const int32_t JAMO_CE32S_LENGTH = 19 + 21 + 27; + + /** Main lookup trie. */ + const UTrie2 *trie; + /** + * Array of CE32 values. + * At index 0 there must be CE32(U+0000) + * to support U+0000's special-tag for NUL-termination handling. + */ + const uint32_t *ce32s; + /** Array of CE values for expansions and OFFSET_TAG. */ + const int64_t *ces; + /** Array of prefix and contraction-suffix matching data. */ + const char16_t *contexts; + /** Base collation data, or nullptr if this data itself is a base. */ + const CollationData *base; + /** + * Simple array of JAMO_CE32S_LENGTH=19+21+27 CE32s, one per canonical Jamo L/V/T. + * They are normally simple CE32s, rarely expansions. + * For fast handling of HANGUL_TAG. + */ + const uint32_t *jamoCE32s; + const Normalizer2Impl &nfcImpl; + /** The single-byte primary weight (xx000000) for numeric collation. */ + uint32_t numericPrimary; + + int32_t ce32sLength; + int32_t cesLength; + int32_t contextsLength; + + /** 256 flags for which primary-weight lead bytes are compressible. */ + const UBool *compressibleBytes; + /** + * Set of code points that are unsafe for starting string comparison after an identical prefix, + * or in backwards CE iteration. + */ + const UnicodeSet *unsafeBackwardSet; + + /** + * Fast Latin table for common-Latin-text string comparisons. + * Data structure see class CollationFastLatin. + */ + const uint16_t *fastLatinTable; + int32_t fastLatinTableLength; + + /** + * Data for scripts and reordering groups. + * Uses include building a reordering permutation table and + * providing script boundaries to AlphabeticIndex. + */ + int32_t numScripts; + /** + * The length of scriptsIndex is numScripts+16. + * It maps from a UScriptCode or a special reorder code to an entry in scriptStarts. + * 16 special reorder codes (not all used) are mapped starting at numScripts. + * Up to MAX_NUM_SPECIAL_REORDER_CODES are codes for special groups like space/punct/digit. + * There are special codes at the end for reorder-reserved primary ranges. + * + * Multiple scripts may share a range and index, for example Hira & Kana. + */ + const uint16_t *scriptsIndex; + /** + * Start primary weight (top 16 bits only) for a group/script/reserved range + * indexed by scriptsIndex. + * The first range (separators & terminators) and the last range (trailing weights) + * are not reorderable, and no scriptsIndex entry points to them. + */ + const uint16_t *scriptStarts; + int32_t scriptStartsLength; + + /** + * Collation elements in the root collator. + * Used by the CollationRootElements class. The data structure is described there. + * nullptr in a tailoring. + */ + const uint32_t *rootElements; + int32_t rootElementsLength; + +private: + int32_t getScriptIndex(int32_t script) const; + void makeReorderRanges(const int32_t *reorder, int32_t length, + UBool latinMustMove, + UVector32 &ranges, UErrorCode &errorCode) const; + int32_t addLowScriptRange(uint8_t table[], int32_t index, int32_t lowStart) const; + int32_t addHighScriptRange(uint8_t table[], int32_t index, int32_t highLimit) const; +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONDATA_H__ diff --git a/deps/icu-small/source/i18n/collationdatabuilder.cpp b/deps/icu-small/source/i18n/collationdatabuilder.cpp index e7c3da1ea5ebe3..e3c350b8a91c4f 100644 --- a/deps/icu-small/source/i18n/collationdatabuilder.cpp +++ b/deps/icu-small/source/i18n/collationdatabuilder.cpp @@ -1,1683 +1,1683 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2012-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationdatabuilder.cpp -* -* (replaced the former ucol_elm.cpp) -* -* created on: 2012apr01 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/localpointer.h" -#include "unicode/uchar.h" -#include "unicode/ucharstrie.h" -#include "unicode/ucharstriebuilder.h" -#include "unicode/uniset.h" -#include "unicode/unistr.h" -#include "unicode/usetiter.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "collation.h" -#include "collationdata.h" -#include "collationdatabuilder.h" -#include "collationfastlatinbuilder.h" -#include "collationiterator.h" -#include "normalizer2impl.h" -#include "utrie2.h" -#include "uvectr32.h" -#include "uvectr64.h" -#include "uvector.h" - -U_NAMESPACE_BEGIN - -CollationDataBuilder::CEModifier::~CEModifier() {} - -/** - * Build-time context and CE32 for a code point. - * If a code point has contextual mappings, then the default (no-context) mapping - * and all conditional mappings are stored in a singly-linked list - * of ConditionalCE32, sorted by context strings. - * - * Context strings sort by prefix length, then by prefix, then by contraction suffix. - * Context strings must be unique and in ascending order. - */ -struct ConditionalCE32 : public UMemory { - ConditionalCE32() - : context(), - ce32(0), defaultCE32(Collation::NO_CE32), builtCE32(Collation::NO_CE32), - next(-1) {} - ConditionalCE32(const UnicodeString &ct, uint32_t ce) - : context(ct), - ce32(ce), defaultCE32(Collation::NO_CE32), builtCE32(Collation::NO_CE32), - next(-1) {} - - inline UBool hasContext() const { return context.length() > 1; } - inline int32_t prefixLength() const { return context.charAt(0); } - - /** - * "\0" for the first entry for any code point, with its default CE32. - * - * Otherwise one unit with the length of the prefix string, - * then the prefix string, then the contraction suffix. - */ - UnicodeString context; - /** - * CE32 for the code point and its context. - * Can be special (e.g., for an expansion) but not contextual (prefix or contraction tag). - */ - uint32_t ce32; - /** - * Default CE32 for all contexts with this same prefix. - * Initially NO_CE32. Set only while building runtime data structures, - * and only on one of the nodes of a sub-list with the same prefix. - */ - uint32_t defaultCE32; - /** - * CE32 for the built contexts. - * When fetching CEs from the builder, the contexts are built into their runtime form - * so that the normal collation implementation can process them. - * The result is cached in the list head. It is reset when the contexts are modified. - * All of these builtCE32 are invalidated by clearContexts(), - * via incrementing the contextsEra. - */ - uint32_t builtCE32; - /** - * The "era" of building intermediate contexts when the above builtCE32 was set. - * When the array of cached, temporary contexts overflows, then clearContexts() - * removes them all and invalidates the builtCE32 that used to point to built tries. - */ - int32_t era = -1; - /** - * Index of the next ConditionalCE32. - * Negative for the end of the list. - */ - int32_t next; - // Note: We could create a separate class for all of the contextual mappings for - // a code point, with the builtCE32, the era, and a list of the actual mappings. - // The class that represents one mapping would then not need to - // store those fields in each element. -}; - -U_CDECL_BEGIN - -void U_CALLCONV -uprv_deleteConditionalCE32(void *obj) { - delete static_cast(obj); -} - -U_CDECL_END - -/** - * Build-time collation element and character iterator. - * Uses the runtime CollationIterator for fetching CEs for a string - * but reads from the builder's unfinished data structures. - * In particular, this class reads from the unfinished trie - * and has to avoid CollationIterator::nextCE() and redirect other - * calls to data->getCE32() and data->getCE32FromSupplementary(). - * - * We do this so that we need not implement the collation algorithm - * again for the builder and make it behave exactly like the runtime code. - * That would be more difficult to test and maintain than this indirection. - * - * Some CE32 tags (for example, the DIGIT_TAG) do not occur in the builder data, - * so the data accesses from those code paths need not be modified. - * - * This class iterates directly over whole code points - * so that the CollationIterator does not need the finished trie - * for handling the LEAD_SURROGATE_TAG. - */ -class DataBuilderCollationIterator : public CollationIterator { -public: - DataBuilderCollationIterator(CollationDataBuilder &b); - - virtual ~DataBuilderCollationIterator(); - - int32_t fetchCEs(const UnicodeString &str, int32_t start, int64_t ces[], int32_t cesLength); - - virtual void resetToOffset(int32_t newOffset) override; - virtual int32_t getOffset() const override; - - virtual UChar32 nextCodePoint(UErrorCode &errorCode) override; - virtual UChar32 previousCodePoint(UErrorCode &errorCode) override; - -protected: - virtual void forwardNumCodePoints(int32_t num, UErrorCode &errorCode) override; - virtual void backwardNumCodePoints(int32_t num, UErrorCode &errorCode) override; - - virtual uint32_t getDataCE32(UChar32 c) const override; - virtual uint32_t getCE32FromBuilderData(uint32_t ce32, UErrorCode &errorCode) override; - - CollationDataBuilder &builder; - CollationData builderData; - uint32_t jamoCE32s[CollationData::JAMO_CE32S_LENGTH]; - const UnicodeString *s; - int32_t pos; -}; - -DataBuilderCollationIterator::DataBuilderCollationIterator(CollationDataBuilder &b) - : CollationIterator(&builderData, /*numeric=*/ false), - builder(b), builderData(b.nfcImpl), - s(NULL), pos(0) { - builderData.base = builder.base; - // Set all of the jamoCE32s[] to indirection CE32s. - for(int32_t j = 0; j < CollationData::JAMO_CE32S_LENGTH; ++j) { // Count across Jamo types. - UChar32 jamo = CollationDataBuilder::jamoCpFromIndex(j); - jamoCE32s[j] = Collation::makeCE32FromTagAndIndex(Collation::BUILDER_DATA_TAG, jamo) | - CollationDataBuilder::IS_BUILDER_JAMO_CE32; - } - builderData.jamoCE32s = jamoCE32s; -} - -DataBuilderCollationIterator::~DataBuilderCollationIterator() {} - -int32_t -DataBuilderCollationIterator::fetchCEs(const UnicodeString &str, int32_t start, - int64_t ces[], int32_t cesLength) { - // Set the pointers each time, in case they changed due to reallocation. - builderData.ce32s = reinterpret_cast(builder.ce32s.getBuffer()); - builderData.ces = builder.ce64s.getBuffer(); - builderData.contexts = builder.contexts.getBuffer(); - // Modified copy of CollationIterator::nextCE() and CollationIterator::nextCEFromCE32(). - reset(); - s = &str; - pos = start; - UErrorCode errorCode = U_ZERO_ERROR; - while(U_SUCCESS(errorCode) && pos < s->length()) { - // No need to keep all CEs in the iterator buffer. - clearCEs(); - UChar32 c = s->char32At(pos); - pos += U16_LENGTH(c); - uint32_t ce32 = utrie2_get32(builder.trie, c); - const CollationData *d; - if(ce32 == Collation::FALLBACK_CE32) { - d = builder.base; - ce32 = builder.base->getCE32(c); - } else { - d = &builderData; - } - appendCEsFromCE32(d, c, ce32, /*forward=*/ true, errorCode); - U_ASSERT(U_SUCCESS(errorCode)); - for(int32_t i = 0; i < getCEsLength(); ++i) { - int64_t ce = getCE(i); - if(ce != 0) { - if(cesLength < Collation::MAX_EXPANSION_LENGTH) { - ces[cesLength] = ce; - } - ++cesLength; - } - } - } - return cesLength; -} - -void -DataBuilderCollationIterator::resetToOffset(int32_t newOffset) { - reset(); - pos = newOffset; -} - -int32_t -DataBuilderCollationIterator::getOffset() const { - return pos; -} - -UChar32 -DataBuilderCollationIterator::nextCodePoint(UErrorCode & /*errorCode*/) { - if(pos == s->length()) { - return U_SENTINEL; - } - UChar32 c = s->char32At(pos); - pos += U16_LENGTH(c); - return c; -} - -UChar32 -DataBuilderCollationIterator::previousCodePoint(UErrorCode & /*errorCode*/) { - if(pos == 0) { - return U_SENTINEL; - } - UChar32 c = s->char32At(pos - 1); - pos -= U16_LENGTH(c); - return c; -} - -void -DataBuilderCollationIterator::forwardNumCodePoints(int32_t num, UErrorCode & /*errorCode*/) { - pos = s->moveIndex32(pos, num); -} - -void -DataBuilderCollationIterator::backwardNumCodePoints(int32_t num, UErrorCode & /*errorCode*/) { - pos = s->moveIndex32(pos, -num); -} - -uint32_t -DataBuilderCollationIterator::getDataCE32(UChar32 c) const { - return utrie2_get32(builder.trie, c); -} - -uint32_t -DataBuilderCollationIterator::getCE32FromBuilderData(uint32_t ce32, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return 0; } - U_ASSERT(Collation::hasCE32Tag(ce32, Collation::BUILDER_DATA_TAG)); - if((ce32 & CollationDataBuilder::IS_BUILDER_JAMO_CE32) != 0) { - UChar32 jamo = Collation::indexFromCE32(ce32); - return utrie2_get32(builder.trie, jamo); - } else { - ConditionalCE32 *cond = builder.getConditionalCE32ForCE32(ce32); - if (cond == nullptr) { - errorCode = U_INTERNAL_PROGRAM_ERROR; - // TODO: ICU-21531 figure out why this happens. - return 0; - } - if(cond->builtCE32 == Collation::NO_CE32 || cond->era != builder.contextsEra) { - // Build the context-sensitive mappings into their runtime form and cache the result. - cond->builtCE32 = builder.buildContext(cond, errorCode); - if(errorCode == U_BUFFER_OVERFLOW_ERROR) { - errorCode = U_ZERO_ERROR; - builder.clearContexts(); - cond->builtCE32 = builder.buildContext(cond, errorCode); - } - cond->era = builder.contextsEra; - builderData.contexts = builder.contexts.getBuffer(); - } - return cond->builtCE32; - } -} - -// ------------------------------------------------------------------------- *** - -CollationDataBuilder::CollationDataBuilder(UBool icu4xMode, UErrorCode &errorCode) - : nfcImpl(*Normalizer2Factory::getNFCImpl(errorCode)), - base(NULL), baseSettings(NULL), - trie(NULL), - ce32s(errorCode), ce64s(errorCode), conditionalCE32s(errorCode), - modified(false), - icu4xMode(icu4xMode), - fastLatinEnabled(false), fastLatinBuilder(NULL), - collIter(NULL) { - // Reserve the first CE32 for U+0000. - if (!icu4xMode) { - ce32s.addElement(0, errorCode); - } - conditionalCE32s.setDeleter(uprv_deleteConditionalCE32); -} - -CollationDataBuilder::~CollationDataBuilder() { - utrie2_close(trie); - delete fastLatinBuilder; - delete collIter; -} - -void -CollationDataBuilder::initForTailoring(const CollationData *b, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - if(trie != NULL) { - errorCode = U_INVALID_STATE_ERROR; - return; - } - if(b == NULL) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - base = b; - - // For a tailoring, the default is to fall back to the base. - // For ICU4X, use the same value for fallback as for the default - // to avoid having to have different blocks for the two. - trie = utrie2_open(Collation::FALLBACK_CE32, icu4xMode ? Collation::FALLBACK_CE32 : Collation::FFFD_CE32, &errorCode); - - if (!icu4xMode) { - // Set the Latin-1 letters block so that it is allocated first in the data array, - // to try to improve locality of reference when sorting Latin-1 text. - // Do not use utrie2_setRange32() since that will not actually allocate blocks - // that are filled with the default value. - // ASCII (0..7F) is already preallocated anyway. - for(UChar32 c = 0xc0; c <= 0xff; ++c) { - utrie2_set32(trie, c, Collation::FALLBACK_CE32, &errorCode); - } - - // Hangul syllables are not tailorable (except via tailoring Jamos). - // Always set the Hangul tag to help performance. - // Do this here, rather than in buildMappings(), - // so that we see the HANGUL_TAG in various assertions. - uint32_t hangulCE32 = Collation::makeCE32FromTagAndIndex(Collation::HANGUL_TAG, 0); - utrie2_setRange32(trie, Hangul::HANGUL_BASE, Hangul::HANGUL_END, hangulCE32, true, &errorCode); - - // Copy the set contents but don't copy/clone the set as a whole because - // that would copy the isFrozen state too. - unsafeBackwardSet.addAll(*b->unsafeBackwardSet); - } - - if(U_FAILURE(errorCode)) { return; } -} - -UBool -CollationDataBuilder::maybeSetPrimaryRange(UChar32 start, UChar32 end, - uint32_t primary, int32_t step, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return false; } - U_ASSERT(start <= end); - // TODO: Do we need to check what values are currently set for start..end? - // An offset range is worth it only if we can achieve an overlap between - // adjacent UTrie2 blocks of 32 code points each. - // An offset CE is also a little more expensive to look up and compute - // than a simple CE. - // If the range spans at least three UTrie2 block boundaries (> 64 code points), - // then we take it. - // If the range spans one or two block boundaries and there are - // at least 4 code points on either side, then we take it. - // (We could additionally require a minimum range length of, say, 16.) - int32_t blockDelta = (end >> 5) - (start >> 5); - if(2 <= step && step <= 0x7f && - (blockDelta >= 3 || - (blockDelta > 0 && (start & 0x1f) <= 0x1c && (end & 0x1f) >= 3))) { - int64_t dataCE = ((int64_t)primary << 32) | (start << 8) | step; - if(isCompressiblePrimary(primary)) { dataCE |= 0x80; } - int32_t index = addCE(dataCE, errorCode); - if(U_FAILURE(errorCode)) { return 0; } - if(index > Collation::MAX_INDEX) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return 0; - } - uint32_t offsetCE32 = Collation::makeCE32FromTagAndIndex(Collation::OFFSET_TAG, index); - utrie2_setRange32(trie, start, end, offsetCE32, true, &errorCode); - modified = true; - return true; - } else { - return false; - } -} - -uint32_t -CollationDataBuilder::setPrimaryRangeAndReturnNext(UChar32 start, UChar32 end, - uint32_t primary, int32_t step, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - UBool isCompressible = isCompressiblePrimary(primary); - if(maybeSetPrimaryRange(start, end, primary, step, errorCode)) { - return Collation::incThreeBytePrimaryByOffset(primary, isCompressible, - (end - start + 1) * step); - } else { - // Short range: Set individual CE32s. - for(;;) { - utrie2_set32(trie, start, Collation::makeLongPrimaryCE32(primary), &errorCode); - ++start; - primary = Collation::incThreeBytePrimaryByOffset(primary, isCompressible, step); - if(start > end) { return primary; } - } - modified = true; - } -} - -uint32_t -CollationDataBuilder::getCE32FromOffsetCE32(UBool fromBase, UChar32 c, uint32_t ce32) const { - int32_t i = Collation::indexFromCE32(ce32); - int64_t dataCE = fromBase ? base->ces[i] : ce64s.elementAti(i); - uint32_t p = Collation::getThreeBytePrimaryForOffsetData(c, dataCE); - return Collation::makeLongPrimaryCE32(p); -} - -UBool -CollationDataBuilder::isCompressibleLeadByte(uint32_t b) const { - return base->isCompressibleLeadByte(b); -} - -UBool -CollationDataBuilder::isAssigned(UChar32 c) const { - return Collation::isAssignedCE32(utrie2_get32(trie, c)); -} - -uint32_t -CollationDataBuilder::getLongPrimaryIfSingleCE(UChar32 c) const { - uint32_t ce32 = utrie2_get32(trie, c); - if(Collation::isLongPrimaryCE32(ce32)) { - return Collation::primaryFromLongPrimaryCE32(ce32); - } else { - return 0; - } -} - -int64_t -CollationDataBuilder::getSingleCE(UChar32 c, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return 0; } - // Keep parallel with CollationData::getSingleCE(). - UBool fromBase = false; - uint32_t ce32 = utrie2_get32(trie, c); - if(ce32 == Collation::FALLBACK_CE32) { - fromBase = true; - ce32 = base->getCE32(c); - } - while(Collation::isSpecialCE32(ce32)) { - switch(Collation::tagFromCE32(ce32)) { - case Collation::LATIN_EXPANSION_TAG: - case Collation::BUILDER_DATA_TAG: - case Collation::PREFIX_TAG: - case Collation::CONTRACTION_TAG: - case Collation::HANGUL_TAG: - case Collation::LEAD_SURROGATE_TAG: - errorCode = U_UNSUPPORTED_ERROR; - return 0; - case Collation::FALLBACK_TAG: - case Collation::RESERVED_TAG_3: - errorCode = U_INTERNAL_PROGRAM_ERROR; - return 0; - case Collation::LONG_PRIMARY_TAG: - return Collation::ceFromLongPrimaryCE32(ce32); - case Collation::LONG_SECONDARY_TAG: - return Collation::ceFromLongSecondaryCE32(ce32); - case Collation::EXPANSION32_TAG: - if(Collation::lengthFromCE32(ce32) == 1) { - int32_t i = Collation::indexFromCE32(ce32); - ce32 = fromBase ? base->ce32s[i] : ce32s.elementAti(i); - break; - } else { - errorCode = U_UNSUPPORTED_ERROR; - return 0; - } - case Collation::EXPANSION_TAG: { - if(Collation::lengthFromCE32(ce32) == 1) { - int32_t i = Collation::indexFromCE32(ce32); - return fromBase ? base->ces[i] : ce64s.elementAti(i); - } else { - errorCode = U_UNSUPPORTED_ERROR; - return 0; - } - } - case Collation::DIGIT_TAG: - // Fetch the non-numeric-collation CE32 and continue. - ce32 = ce32s.elementAti(Collation::indexFromCE32(ce32)); - break; - case Collation::U0000_TAG: - U_ASSERT(c == 0); - // Fetch the normal ce32 for U+0000 and continue. - ce32 = fromBase ? base->ce32s[0] : ce32s.elementAti(0); - break; - case Collation::OFFSET_TAG: - ce32 = getCE32FromOffsetCE32(fromBase, c, ce32); - break; - case Collation::IMPLICIT_TAG: - return Collation::unassignedCEFromCodePoint(c); - } - } - return Collation::ceFromSimpleCE32(ce32); -} - -int32_t -CollationDataBuilder::addCE(int64_t ce, UErrorCode &errorCode) { - int32_t length = ce64s.size(); - for(int32_t i = 0; i < length; ++i) { - if(ce == ce64s.elementAti(i)) { return i; } - } - ce64s.addElement(ce, errorCode); - return length; -} - -int32_t -CollationDataBuilder::addCE32(uint32_t ce32, UErrorCode &errorCode) { - int32_t length = ce32s.size(); - for(int32_t i = 0; i < length; ++i) { - if(ce32 == (uint32_t)ce32s.elementAti(i)) { return i; } - } - ce32s.addElement((int32_t)ce32, errorCode); - return length; -} - -int32_t -CollationDataBuilder::addConditionalCE32(const UnicodeString &context, uint32_t ce32, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return -1; } - U_ASSERT(!context.isEmpty()); - int32_t index = conditionalCE32s.size(); - if(index > Collation::MAX_INDEX) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return -1; - } - LocalPointer cond(new ConditionalCE32(context, ce32), errorCode); - conditionalCE32s.adoptElement(cond.orphan(), errorCode); - if(U_FAILURE(errorCode)) { - return -1; - } - return index; -} - -void -CollationDataBuilder::add(const UnicodeString &prefix, const UnicodeString &s, - const int64_t ces[], int32_t cesLength, - UErrorCode &errorCode) { - uint32_t ce32 = encodeCEs(ces, cesLength, errorCode); - addCE32(prefix, s, ce32, errorCode); -} - -void -CollationDataBuilder::addCE32(const UnicodeString &prefix, const UnicodeString &s, - uint32_t ce32, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - if(s.isEmpty()) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if(trie == NULL || utrie2_isFrozen(trie)) { - errorCode = U_INVALID_STATE_ERROR; - return; - } - UChar32 c = s.char32At(0); - int32_t cLength = U16_LENGTH(c); - uint32_t oldCE32 = utrie2_get32(trie, c); - UBool hasContext = !prefix.isEmpty() || s.length() > cLength; - - if (icu4xMode) { - if (base && c >= 0x1100 && c < 0x1200) { - // Omit jamo tailorings. - // TODO(https://github.com/unicode-org/icu4x/issues/1941). - } - const Normalizer2* nfdNormalizer = Normalizer2::getNFDInstance(errorCode); - UnicodeString sInNfd; - nfdNormalizer->normalize(s, sInNfd, errorCode); - if (s != sInNfd) { - // s is not in NFD, so it cannot match in ICU4X, since ICU4X only - // does NFD lookups. - // Now check that we're only rejecting known cases. - if (s.length() == 2) { - char16_t second = s.charAt(1); - if (second == 0x0F73 || second == 0x0F75 || second == 0x0F81) { - // Second is a special decomposing Tibetan vowel sign. - // These also get added in the decomposed form, so ignoring - // this instance is OK. - return; - } - if (c == 0xFDD1 && second == 0xAC00) { - // This strange contraction exists in the root and - // doesn't have a decomposed counterpart there. - // This won't match in ICU4X anyway and is very strange: - // Unassigned Arabic presentation form contracting with - // the very first Hangul syllable. Let's ignore this - // explicitly. - return; - } - } - // Unknown case worth investigating if ever found. - errorCode = U_UNSUPPORTED_ERROR; - return; - } - - if (!prefix.isEmpty()) { - UnicodeString prefixInNfd; - nfdNormalizer->normalize(prefix, prefixInNfd, errorCode); - if (prefix != prefixInNfd) { - errorCode = U_UNSUPPORTED_ERROR; - return; - } - - int32_t count = prefix.countChar32(); - if (count > 2) { - // Prefix too long for ICU4X. - errorCode = U_UNSUPPORTED_ERROR; - return; - } - UChar32 utf32[4]; - int32_t len = prefix.toUTF32(utf32, 4, errorCode); - if (len != count) { - errorCode = U_INVALID_STATE_ERROR; - return; - } - UChar32 c = utf32[0]; - if (u_getCombiningClass(c)) { - // Prefix must start with as starter for ICU4X. - errorCode = U_UNSUPPORTED_ERROR; - return; - } - // XXX: Korean searchjl has jamo in prefix, so commenting out this - // check for now. ICU4X currently ignores non-root jamo tables anyway. - // searchjl was added in - // https://unicode-org.atlassian.net/browse/CLDR-3560 - // Contractions were changed to prefixes in - // https://unicode-org.atlassian.net/browse/CLDR-6546 - // - // if ((c >= 0x1100 && c < 0x1200) || (c >= 0xAC00 && c < 0xD7A4)) { - // errorCode = U_UNSUPPORTED_ERROR; - // return; - // } - if ((len > 1) && !(utf32[1] == 0x3099 || utf32[1] == 0x309A)) { - // Second character in prefix, if present, must be a kana voicing mark for ICU4X. - errorCode = U_UNSUPPORTED_ERROR; - return; - } - } - - if (s.length() > cLength) { - // Check that there's no modern Hangul in contractions. - for (int32_t i = 0; i < s.length(); ++i) { - UChar c = s.charAt(i); - if ((c >= 0x1100 && c < 0x1100 + 19) || (c >= 0x1161 && c < 0x1161 + 21) || (c >= 0x11A7 && c < 0x11A7 + 28) || (c >= 0xAC00 && c < 0xD7A4)) { - errorCode = U_UNSUPPORTED_ERROR; - return; - } - } - } - } - - if(oldCE32 == Collation::FALLBACK_CE32) { - // First tailoring for c. - // If c has contextual base mappings or if we add a contextual mapping, - // then copy the base mappings. - // Otherwise we just override the base mapping. - uint32_t baseCE32 = base->getFinalCE32(base->getCE32(c)); - if(hasContext || Collation::ce32HasContext(baseCE32)) { - oldCE32 = copyFromBaseCE32(c, baseCE32, true, errorCode); - utrie2_set32(trie, c, oldCE32, &errorCode); - if(U_FAILURE(errorCode)) { return; } - } - } - if(!hasContext) { - // No prefix, no contraction. - if(!isBuilderContextCE32(oldCE32)) { - utrie2_set32(trie, c, ce32, &errorCode); - } else { - ConditionalCE32 *cond = getConditionalCE32ForCE32(oldCE32); - cond->builtCE32 = Collation::NO_CE32; - cond->ce32 = ce32; - } - } else { - ConditionalCE32 *cond; - if(!isBuilderContextCE32(oldCE32)) { - // Replace the simple oldCE32 with a builder context CE32 - // pointing to a new ConditionalCE32 list head. - int32_t index = addConditionalCE32(UnicodeString((UChar)0), oldCE32, errorCode); - if(U_FAILURE(errorCode)) { return; } - uint32_t contextCE32 = makeBuilderContextCE32(index); - utrie2_set32(trie, c, contextCE32, &errorCode); - contextChars.add(c); - cond = getConditionalCE32(index); - } else { - cond = getConditionalCE32ForCE32(oldCE32); - cond->builtCE32 = Collation::NO_CE32; - } - UnicodeString suffix(s, cLength); - UnicodeString context((UChar)prefix.length()); - context.append(prefix).append(suffix); - unsafeBackwardSet.addAll(suffix); - for(;;) { - // invariant: context > cond->context - int32_t next = cond->next; - if(next < 0) { - // Append a new ConditionalCE32 after cond. - int32_t index = addConditionalCE32(context, ce32, errorCode); - if(U_FAILURE(errorCode)) { return; } - cond->next = index; - break; - } - ConditionalCE32 *nextCond = getConditionalCE32(next); - int8_t cmp = context.compare(nextCond->context); - if(cmp < 0) { - // Insert a new ConditionalCE32 between cond and nextCond. - int32_t index = addConditionalCE32(context, ce32, errorCode); - if(U_FAILURE(errorCode)) { return; } - cond->next = index; - getConditionalCE32(index)->next = next; - break; - } else if(cmp == 0) { - // Same context as before, overwrite its ce32. - nextCond->ce32 = ce32; - break; - } - cond = nextCond; - } - } - modified = true; -} - -uint32_t -CollationDataBuilder::encodeOneCEAsCE32(int64_t ce) { - uint32_t p = (uint32_t)(ce >> 32); - uint32_t lower32 = (uint32_t)ce; - uint32_t t = (uint32_t)(ce & 0xffff); - U_ASSERT((t & 0xc000) != 0xc000); // Impossible case bits 11 mark special CE32s. - if((ce & INT64_C(0xffff00ff00ff)) == 0) { - // normal form ppppsstt - return p | (lower32 >> 16) | (t >> 8); - } else if((ce & INT64_C(0xffffffffff)) == Collation::COMMON_SEC_AND_TER_CE) { - // long-primary form ppppppC1 - return Collation::makeLongPrimaryCE32(p); - } else if(p == 0 && (t & 0xff) == 0) { - // long-secondary form ssssttC2 - return Collation::makeLongSecondaryCE32(lower32); - } - return Collation::NO_CE32; -} - -uint32_t -CollationDataBuilder::encodeOneCE(int64_t ce, UErrorCode &errorCode) { - // Try to encode one CE as one CE32. - uint32_t ce32 = encodeOneCEAsCE32(ce); - if(ce32 != Collation::NO_CE32) { return ce32; } - int32_t index = addCE(ce, errorCode); - if(U_FAILURE(errorCode)) { return 0; } - if(index > Collation::MAX_INDEX) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return 0; - } - return Collation::makeCE32FromTagIndexAndLength(Collation::EXPANSION_TAG, index, 1); -} - -uint32_t -CollationDataBuilder::encodeCEs(const int64_t ces[], int32_t cesLength, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - if(cesLength < 0 || cesLength > Collation::MAX_EXPANSION_LENGTH) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - if(trie == NULL || utrie2_isFrozen(trie)) { - errorCode = U_INVALID_STATE_ERROR; - return 0; - } - if(cesLength == 0) { - // Convenience: We cannot map to nothing, but we can map to a completely ignorable CE. - // Do this here so that callers need not do it. - return encodeOneCEAsCE32(0); - } else if(cesLength == 1) { - return encodeOneCE(ces[0], errorCode); - } else if(cesLength == 2 && !icu4xMode) { - // Try to encode two CEs as one CE32. - // Turn this off for ICU4X, because without the canonical closure - // these are so rare that it doesn't make sense to spend a branch - // on checking this tag when using the data. - int64_t ce0 = ces[0]; - int64_t ce1 = ces[1]; - uint32_t p0 = (uint32_t)(ce0 >> 32); - if((ce0 & INT64_C(0xffffffffff00ff)) == Collation::COMMON_SECONDARY_CE && - (ce1 & INT64_C(0xffffffff00ffffff)) == Collation::COMMON_TERTIARY_CE && - p0 != 0) { - // Latin mini expansion - return - p0 | - (((uint32_t)ce0 & 0xff00u) << 8) | - (uint32_t)(ce1 >> 16) | - Collation::SPECIAL_CE32_LOW_BYTE | - Collation::LATIN_EXPANSION_TAG; - } - } - // Try to encode two or more CEs as CE32s. - int32_t newCE32s[Collation::MAX_EXPANSION_LENGTH]; - for(int32_t i = 0;; ++i) { - if(i == cesLength) { - return encodeExpansion32(newCE32s, cesLength, errorCode); - } - uint32_t ce32 = encodeOneCEAsCE32(ces[i]); - if(ce32 == Collation::NO_CE32) { break; } - newCE32s[i] = (int32_t)ce32; - } - return encodeExpansion(ces, cesLength, errorCode); -} - -uint32_t -CollationDataBuilder::encodeExpansion(const int64_t ces[], int32_t length, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - // See if this sequence of CEs has already been stored. - int64_t first = ces[0]; - int32_t ce64sMax = ce64s.size() - length; - for(int32_t i = 0; i <= ce64sMax; ++i) { - if(first == ce64s.elementAti(i)) { - if(i > Collation::MAX_INDEX) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return 0; - } - for(int32_t j = 1;; ++j) { - if(j == length) { - return Collation::makeCE32FromTagIndexAndLength( - Collation::EXPANSION_TAG, i, length); - } - if(ce64s.elementAti(i + j) != ces[j]) { break; } - } - } - } - // Store the new sequence. - int32_t i = ce64s.size(); - if(i > Collation::MAX_INDEX) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return 0; - } - for(int32_t j = 0; j < length; ++j) { - ce64s.addElement(ces[j], errorCode); - } - return Collation::makeCE32FromTagIndexAndLength(Collation::EXPANSION_TAG, i, length); -} - -uint32_t -CollationDataBuilder::encodeExpansion32(const int32_t newCE32s[], int32_t length, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - // See if this sequence of CE32s has already been stored. - int32_t first = newCE32s[0]; - int32_t ce32sMax = ce32s.size() - length; - for(int32_t i = 0; i <= ce32sMax; ++i) { - if(first == ce32s.elementAti(i)) { - if(i > Collation::MAX_INDEX) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return 0; - } - for(int32_t j = 1;; ++j) { - if(j == length) { - return Collation::makeCE32FromTagIndexAndLength( - Collation::EXPANSION32_TAG, i, length); - } - if(ce32s.elementAti(i + j) != newCE32s[j]) { break; } - } - } - } - // Store the new sequence. - int32_t i = ce32s.size(); - if(i > Collation::MAX_INDEX) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return 0; - } - for(int32_t j = 0; j < length; ++j) { - ce32s.addElement(newCE32s[j], errorCode); - } - return Collation::makeCE32FromTagIndexAndLength(Collation::EXPANSION32_TAG, i, length); -} - -uint32_t -CollationDataBuilder::copyFromBaseCE32(UChar32 c, uint32_t ce32, UBool withContext, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - if(!Collation::isSpecialCE32(ce32)) { return ce32; } - switch(Collation::tagFromCE32(ce32)) { - case Collation::LONG_PRIMARY_TAG: - case Collation::LONG_SECONDARY_TAG: - case Collation::LATIN_EXPANSION_TAG: - // copy as is - break; - case Collation::EXPANSION32_TAG: { - const uint32_t *baseCE32s = base->ce32s + Collation::indexFromCE32(ce32); - int32_t length = Collation::lengthFromCE32(ce32); - ce32 = encodeExpansion32( - reinterpret_cast(baseCE32s), length, errorCode); - break; - } - case Collation::EXPANSION_TAG: { - const int64_t *baseCEs = base->ces + Collation::indexFromCE32(ce32); - int32_t length = Collation::lengthFromCE32(ce32); - ce32 = encodeExpansion(baseCEs, length, errorCode); - break; - } - case Collation::PREFIX_TAG: { - // Flatten prefixes and nested suffixes (contractions) - // into a linear list of ConditionalCE32. - const UChar *p = base->contexts + Collation::indexFromCE32(ce32); - ce32 = CollationData::readCE32(p); // Default if no prefix match. - if(!withContext) { - return copyFromBaseCE32(c, ce32, false, errorCode); - } - ConditionalCE32 head; - UnicodeString context((UChar)0); - int32_t index; - if(Collation::isContractionCE32(ce32)) { - index = copyContractionsFromBaseCE32(context, c, ce32, &head, errorCode); - } else { - ce32 = copyFromBaseCE32(c, ce32, true, errorCode); - head.next = index = addConditionalCE32(context, ce32, errorCode); - } - if(U_FAILURE(errorCode)) { return 0; } - ConditionalCE32 *cond = getConditionalCE32(index); // the last ConditionalCE32 so far - UCharsTrie::Iterator prefixes(p + 2, 0, errorCode); - while(prefixes.next(errorCode)) { - context = prefixes.getString(); - context.reverse(); - context.insert(0, (UChar)context.length()); - ce32 = (uint32_t)prefixes.getValue(); - if(Collation::isContractionCE32(ce32)) { - index = copyContractionsFromBaseCE32(context, c, ce32, cond, errorCode); - } else { - ce32 = copyFromBaseCE32(c, ce32, true, errorCode); - cond->next = index = addConditionalCE32(context, ce32, errorCode); - } - if(U_FAILURE(errorCode)) { return 0; } - cond = getConditionalCE32(index); - } - ce32 = makeBuilderContextCE32(head.next); - contextChars.add(c); - break; - } - case Collation::CONTRACTION_TAG: { - if(!withContext) { - const UChar *p = base->contexts + Collation::indexFromCE32(ce32); - ce32 = CollationData::readCE32(p); // Default if no suffix match. - return copyFromBaseCE32(c, ce32, false, errorCode); - } - ConditionalCE32 head; - UnicodeString context((UChar)0); - copyContractionsFromBaseCE32(context, c, ce32, &head, errorCode); - ce32 = makeBuilderContextCE32(head.next); - contextChars.add(c); - break; - } - case Collation::HANGUL_TAG: - errorCode = U_UNSUPPORTED_ERROR; // We forbid tailoring of Hangul syllables. - break; - case Collation::OFFSET_TAG: - ce32 = getCE32FromOffsetCE32(true, c, ce32); - break; - case Collation::IMPLICIT_TAG: - ce32 = encodeOneCE(Collation::unassignedCEFromCodePoint(c), errorCode); - break; - default: - UPRV_UNREACHABLE_EXIT; // require ce32 == base->getFinalCE32(ce32) - } - return ce32; -} - -int32_t -CollationDataBuilder::copyContractionsFromBaseCE32(UnicodeString &context, UChar32 c, uint32_t ce32, - ConditionalCE32 *cond, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - const UChar *p = base->contexts + Collation::indexFromCE32(ce32); - int32_t index; - if((ce32 & Collation::CONTRACT_SINGLE_CP_NO_MATCH) != 0) { - // No match on the single code point. - // We are underneath a prefix, and the default mapping is just - // a fallback to the mappings for a shorter prefix. - U_ASSERT(context.length() > 1); - index = -1; - } else { - ce32 = CollationData::readCE32(p); // Default if no suffix match. - U_ASSERT(!Collation::isContractionCE32(ce32)); - ce32 = copyFromBaseCE32(c, ce32, true, errorCode); - cond->next = index = addConditionalCE32(context, ce32, errorCode); - if(U_FAILURE(errorCode)) { return 0; } - cond = getConditionalCE32(index); - } - - int32_t suffixStart = context.length(); - UCharsTrie::Iterator suffixes(p + 2, 0, errorCode); - while(suffixes.next(errorCode)) { - context.append(suffixes.getString()); - ce32 = copyFromBaseCE32(c, (uint32_t)suffixes.getValue(), true, errorCode); - cond->next = index = addConditionalCE32(context, ce32, errorCode); - if(U_FAILURE(errorCode)) { return 0; } - // No need to update the unsafeBackwardSet because the tailoring set - // is already a copy of the base set. - cond = getConditionalCE32(index); - context.truncate(suffixStart); - } - U_ASSERT(index >= 0); - return index; -} - -class CopyHelper { -public: - CopyHelper(const CollationDataBuilder &s, CollationDataBuilder &d, - const CollationDataBuilder::CEModifier &m, UErrorCode &initialErrorCode) - : src(s), dest(d), modifier(m), - errorCode(initialErrorCode) {} - - UBool copyRangeCE32(UChar32 start, UChar32 end, uint32_t ce32) { - ce32 = copyCE32(ce32); - utrie2_setRange32(dest.trie, start, end, ce32, true, &errorCode); - if(CollationDataBuilder::isBuilderContextCE32(ce32)) { - dest.contextChars.add(start, end); - } - return U_SUCCESS(errorCode); - } - - uint32_t copyCE32(uint32_t ce32) { - if(!Collation::isSpecialCE32(ce32)) { - int64_t ce = modifier.modifyCE32(ce32); - if(ce != Collation::NO_CE) { - ce32 = dest.encodeOneCE(ce, errorCode); - } - } else { - int32_t tag = Collation::tagFromCE32(ce32); - if(tag == Collation::EXPANSION32_TAG) { - const uint32_t *srcCE32s = reinterpret_cast(src.ce32s.getBuffer()); - srcCE32s += Collation::indexFromCE32(ce32); - int32_t length = Collation::lengthFromCE32(ce32); - // Inspect the source CE32s. Just copy them if none are modified. - // Otherwise copy to modifiedCEs, with modifications. - UBool isModified = false; - for(int32_t i = 0; i < length; ++i) { - ce32 = srcCE32s[i]; - int64_t ce; - if(Collation::isSpecialCE32(ce32) || - (ce = modifier.modifyCE32(ce32)) == Collation::NO_CE) { - if(isModified) { - modifiedCEs[i] = Collation::ceFromCE32(ce32); - } - } else { - if(!isModified) { - for(int32_t j = 0; j < i; ++j) { - modifiedCEs[j] = Collation::ceFromCE32(srcCE32s[j]); - } - isModified = true; - } - modifiedCEs[i] = ce; - } - } - if(isModified) { - ce32 = dest.encodeCEs(modifiedCEs, length, errorCode); - } else { - ce32 = dest.encodeExpansion32( - reinterpret_cast(srcCE32s), length, errorCode); - } - } else if(tag == Collation::EXPANSION_TAG) { - const int64_t *srcCEs = src.ce64s.getBuffer(); - srcCEs += Collation::indexFromCE32(ce32); - int32_t length = Collation::lengthFromCE32(ce32); - // Inspect the source CEs. Just copy them if none are modified. - // Otherwise copy to modifiedCEs, with modifications. - UBool isModified = false; - for(int32_t i = 0; i < length; ++i) { - int64_t srcCE = srcCEs[i]; - int64_t ce = modifier.modifyCE(srcCE); - if(ce == Collation::NO_CE) { - if(isModified) { - modifiedCEs[i] = srcCE; - } - } else { - if(!isModified) { - for(int32_t j = 0; j < i; ++j) { - modifiedCEs[j] = srcCEs[j]; - } - isModified = true; - } - modifiedCEs[i] = ce; - } - } - if(isModified) { - ce32 = dest.encodeCEs(modifiedCEs, length, errorCode); - } else { - ce32 = dest.encodeExpansion(srcCEs, length, errorCode); - } - } else if(tag == Collation::BUILDER_DATA_TAG) { - // Copy the list of ConditionalCE32. - ConditionalCE32 *cond = src.getConditionalCE32ForCE32(ce32); - U_ASSERT(!cond->hasContext()); - int32_t destIndex = dest.addConditionalCE32( - cond->context, copyCE32(cond->ce32), errorCode); - ce32 = CollationDataBuilder::makeBuilderContextCE32(destIndex); - while(cond->next >= 0) { - cond = src.getConditionalCE32(cond->next); - ConditionalCE32 *prevDestCond = dest.getConditionalCE32(destIndex); - destIndex = dest.addConditionalCE32( - cond->context, copyCE32(cond->ce32), errorCode); - int32_t suffixStart = cond->prefixLength() + 1; - dest.unsafeBackwardSet.addAll(cond->context.tempSubString(suffixStart)); - prevDestCond->next = destIndex; - } - } else { - // Just copy long CEs and Latin mini expansions (and other expected values) as is, - // assuming that the modifier would not modify them. - U_ASSERT(tag == Collation::LONG_PRIMARY_TAG || - tag == Collation::LONG_SECONDARY_TAG || - tag == Collation::LATIN_EXPANSION_TAG || - tag == Collation::HANGUL_TAG); - } - } - return ce32; - } - - const CollationDataBuilder &src; - CollationDataBuilder &dest; - const CollationDataBuilder::CEModifier &modifier; - int64_t modifiedCEs[Collation::MAX_EXPANSION_LENGTH]; - UErrorCode errorCode; -}; - -U_CDECL_BEGIN - -static UBool U_CALLCONV -enumRangeForCopy(const void *context, UChar32 start, UChar32 end, uint32_t value) { - return - value == Collation::UNASSIGNED_CE32 || value == Collation::FALLBACK_CE32 || - ((CopyHelper *)context)->copyRangeCE32(start, end, value); -} - -U_CDECL_END - -void -CollationDataBuilder::copyFrom(const CollationDataBuilder &src, const CEModifier &modifier, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - if(trie == NULL || utrie2_isFrozen(trie)) { - errorCode = U_INVALID_STATE_ERROR; - return; - } - CopyHelper helper(src, *this, modifier, errorCode); - utrie2_enum(src.trie, NULL, enumRangeForCopy, &helper); - errorCode = helper.errorCode; - // Update the contextChars and the unsafeBackwardSet while copying, - // in case a character had conditional mappings in the source builder - // and they were removed later. - modified |= src.modified; -} - -void -CollationDataBuilder::optimize(const UnicodeSet &set, UErrorCode &errorCode) { - if(U_FAILURE(errorCode) || set.isEmpty()) { return; } - UnicodeSetIterator iter(set); - while(iter.next() && !iter.isString()) { - UChar32 c = iter.getCodepoint(); - uint32_t ce32 = utrie2_get32(trie, c); - if(ce32 == Collation::FALLBACK_CE32) { - ce32 = base->getFinalCE32(base->getCE32(c)); - ce32 = copyFromBaseCE32(c, ce32, true, errorCode); - utrie2_set32(trie, c, ce32, &errorCode); - } - } - modified = true; -} - -void -CollationDataBuilder::suppressContractions(const UnicodeSet &set, UErrorCode &errorCode) { - if(U_FAILURE(errorCode) || set.isEmpty()) { return; } - UnicodeSetIterator iter(set); - while(iter.next() && !iter.isString()) { - UChar32 c = iter.getCodepoint(); - uint32_t ce32 = utrie2_get32(trie, c); - if(ce32 == Collation::FALLBACK_CE32) { - ce32 = base->getFinalCE32(base->getCE32(c)); - if(Collation::ce32HasContext(ce32)) { - ce32 = copyFromBaseCE32(c, ce32, false /* without context */, errorCode); - utrie2_set32(trie, c, ce32, &errorCode); - } - } else if(isBuilderContextCE32(ce32)) { - ce32 = getConditionalCE32ForCE32(ce32)->ce32; - // Simply abandon the list of ConditionalCE32. - // The caller will copy this builder in the end, - // eliminating unreachable data. - utrie2_set32(trie, c, ce32, &errorCode); - contextChars.remove(c); - } - } - modified = true; -} - -UBool -CollationDataBuilder::getJamoCE32s(uint32_t jamoCE32s[], UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return false; } - UBool anyJamoAssigned = base == NULL; // always set jamoCE32s in the base data - UBool needToCopyFromBase = false; - for(int32_t j = 0; j < CollationData::JAMO_CE32S_LENGTH; ++j) { // Count across Jamo types. - UChar32 jamo = jamoCpFromIndex(j); - UBool fromBase = false; - uint32_t ce32 = utrie2_get32(trie, jamo); - anyJamoAssigned |= Collation::isAssignedCE32(ce32); - // TODO: Try to prevent [optimize [Jamo]] from counting as anyJamoAssigned. - // (As of CLDR 24 [2013] the Korean tailoring does not optimize conjoining Jamo.) - if(ce32 == Collation::FALLBACK_CE32) { - fromBase = true; - ce32 = base->getCE32(jamo); - } - if(Collation::isSpecialCE32(ce32)) { - switch(Collation::tagFromCE32(ce32)) { - case Collation::LONG_PRIMARY_TAG: - case Collation::LONG_SECONDARY_TAG: - case Collation::LATIN_EXPANSION_TAG: - // Copy the ce32 as-is. - break; - case Collation::EXPANSION32_TAG: - case Collation::EXPANSION_TAG: - case Collation::PREFIX_TAG: - case Collation::CONTRACTION_TAG: - if(fromBase) { - // Defer copying until we know if anyJamoAssigned. - ce32 = Collation::FALLBACK_CE32; - needToCopyFromBase = true; - } - break; - case Collation::IMPLICIT_TAG: - // An unassigned Jamo should only occur in tests with incomplete bases. - U_ASSERT(fromBase); - ce32 = Collation::FALLBACK_CE32; - needToCopyFromBase = true; - break; - case Collation::OFFSET_TAG: - ce32 = getCE32FromOffsetCE32(fromBase, jamo, ce32); - break; - case Collation::FALLBACK_TAG: - case Collation::RESERVED_TAG_3: - case Collation::BUILDER_DATA_TAG: - case Collation::DIGIT_TAG: - case Collation::U0000_TAG: - case Collation::HANGUL_TAG: - case Collation::LEAD_SURROGATE_TAG: - errorCode = U_INTERNAL_PROGRAM_ERROR; - return false; - } - } - jamoCE32s[j] = ce32; - } - if(anyJamoAssigned && needToCopyFromBase) { - for(int32_t j = 0; j < CollationData::JAMO_CE32S_LENGTH; ++j) { - if(jamoCE32s[j] == Collation::FALLBACK_CE32) { - UChar32 jamo = jamoCpFromIndex(j); - jamoCE32s[j] = copyFromBaseCE32(jamo, base->getCE32(jamo), - /*withContext=*/ true, errorCode); - } - } - } - return anyJamoAssigned && U_SUCCESS(errorCode); -} - -void -CollationDataBuilder::setDigitTags(UErrorCode &errorCode) { - UnicodeSet digits(UNICODE_STRING_SIMPLE("[:Nd:]"), errorCode); - if(U_FAILURE(errorCode)) { return; } - UnicodeSetIterator iter(digits); - while(iter.next()) { - U_ASSERT(!iter.isString()); - UChar32 c = iter.getCodepoint(); - uint32_t ce32 = utrie2_get32(trie, c); - if(ce32 != Collation::FALLBACK_CE32 && ce32 != Collation::UNASSIGNED_CE32) { - int32_t index = addCE32(ce32, errorCode); - if(U_FAILURE(errorCode)) { return; } - if(index > Collation::MAX_INDEX) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return; - } - ce32 = Collation::makeCE32FromTagIndexAndLength( - Collation::DIGIT_TAG, index, u_charDigitValue(c)); - utrie2_set32(trie, c, ce32, &errorCode); - } - } -} - -U_CDECL_BEGIN - -static UBool U_CALLCONV -enumRangeLeadValue(const void *context, UChar32 /*start*/, UChar32 /*end*/, uint32_t value) { - int32_t *pValue = (int32_t *)context; - if(value == Collation::UNASSIGNED_CE32) { - value = Collation::LEAD_ALL_UNASSIGNED; - } else if(value == Collation::FALLBACK_CE32) { - value = Collation::LEAD_ALL_FALLBACK; - } else { - *pValue = Collation::LEAD_MIXED; - return false; - } - if(*pValue < 0) { - *pValue = (int32_t)value; - } else if(*pValue != (int32_t)value) { - *pValue = Collation::LEAD_MIXED; - return false; - } - return true; -} - -U_CDECL_END - -void -CollationDataBuilder::setLeadSurrogates(UErrorCode &errorCode) { - for(UChar lead = 0xd800; lead < 0xdc00; ++lead) { - int32_t value = -1; - utrie2_enumForLeadSurrogate(trie, lead, NULL, enumRangeLeadValue, &value); - utrie2_set32ForLeadSurrogateCodeUnit( - trie, lead, - Collation::makeCE32FromTagAndIndex(Collation::LEAD_SURROGATE_TAG, 0) | (uint32_t)value, - &errorCode); - } -} - -void -CollationDataBuilder::build(CollationData &data, UErrorCode &errorCode) { - buildMappings(data, errorCode); - if(base != NULL) { - data.numericPrimary = base->numericPrimary; - data.compressibleBytes = base->compressibleBytes; - data.numScripts = base->numScripts; - data.scriptsIndex = base->scriptsIndex; - data.scriptStarts = base->scriptStarts; - data.scriptStartsLength = base->scriptStartsLength; - } - buildFastLatinTable(data, errorCode); -} - -void -CollationDataBuilder::buildMappings(CollationData &data, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - if(trie == NULL || utrie2_isFrozen(trie)) { - errorCode = U_INVALID_STATE_ERROR; - return; - } - - buildContexts(errorCode); - - uint32_t jamoCE32s[CollationData::JAMO_CE32S_LENGTH]; - int32_t jamoIndex = -1; - if(getJamoCE32s(jamoCE32s, errorCode)) { - jamoIndex = ce32s.size(); - for(int32_t i = 0; i < CollationData::JAMO_CE32S_LENGTH; ++i) { - ce32s.addElement((int32_t)jamoCE32s[i], errorCode); - } - // Small optimization: Use a bit in the Hangul ce32 - // to indicate that none of the Jamo CE32s are isSpecialCE32() - // (as it should be in the root collator). - // It allows CollationIterator to avoid recursive function calls and per-Jamo tests. - // In order to still have good trie compression and keep this code simple, - // we only set this flag if a whole block of 588 Hangul syllables starting with - // a common leading consonant (Jamo L) has this property. - UBool isAnyJamoVTSpecial = false; - for(int32_t i = Hangul::JAMO_L_COUNT; i < CollationData::JAMO_CE32S_LENGTH; ++i) { - if(Collation::isSpecialCE32(jamoCE32s[i])) { - isAnyJamoVTSpecial = true; - break; - } - } - uint32_t hangulCE32 = Collation::makeCE32FromTagAndIndex(Collation::HANGUL_TAG, 0); - UChar32 c = Hangul::HANGUL_BASE; - for(int32_t i = 0; i < Hangul::JAMO_L_COUNT; ++i) { // iterate over the Jamo L - uint32_t ce32 = hangulCE32; - if(!isAnyJamoVTSpecial && !Collation::isSpecialCE32(jamoCE32s[i])) { - ce32 |= Collation::HANGUL_NO_SPECIAL_JAMO; - } - UChar32 limit = c + Hangul::JAMO_VT_COUNT; - utrie2_setRange32(trie, c, limit - 1, ce32, true, &errorCode); - c = limit; - } - } else { - // Copy the Hangul CE32s from the base in blocks per Jamo L, - // assuming that HANGUL_NO_SPECIAL_JAMO is set or not set for whole blocks. - for(UChar32 c = Hangul::HANGUL_BASE; c < Hangul::HANGUL_LIMIT;) { - uint32_t ce32 = base->getCE32(c); - U_ASSERT(Collation::hasCE32Tag(ce32, Collation::HANGUL_TAG)); - UChar32 limit = c + Hangul::JAMO_VT_COUNT; - utrie2_setRange32(trie, c, limit - 1, ce32, true, &errorCode); - c = limit; - } - } - - setDigitTags(errorCode); - setLeadSurrogates(errorCode); - - if (!icu4xMode) { - // For U+0000, move its normal ce32 into CE32s[0] and set U0000_TAG. - ce32s.setElementAt((int32_t)utrie2_get32(trie, 0), 0); - utrie2_set32(trie, 0, Collation::makeCE32FromTagAndIndex(Collation::U0000_TAG, 0), &errorCode); - } - - utrie2_freeze(trie, UTRIE2_32_VALUE_BITS, &errorCode); - if(U_FAILURE(errorCode)) { return; } - - // Mark each lead surrogate as "unsafe" - // if any of its 1024 associated supplementary code points is "unsafe". - UChar32 c = 0x10000; - for(UChar lead = 0xd800; lead < 0xdc00; ++lead, c += 0x400) { - if(unsafeBackwardSet.containsSome(c, c + 0x3ff)) { - unsafeBackwardSet.add(lead); - } - } - unsafeBackwardSet.freeze(); - - data.trie = trie; - data.ce32s = reinterpret_cast(ce32s.getBuffer()); - data.ces = ce64s.getBuffer(); - data.contexts = contexts.getBuffer(); - - data.ce32sLength = ce32s.size(); - data.cesLength = ce64s.size(); - data.contextsLength = contexts.length(); - - data.base = base; - if(jamoIndex >= 0) { - data.jamoCE32s = data.ce32s + jamoIndex; - } else { - data.jamoCE32s = base->jamoCE32s; - } - data.unsafeBackwardSet = &unsafeBackwardSet; -} - -void -CollationDataBuilder::clearContexts() { - contexts.remove(); - // Incrementing the contexts build "era" invalidates all of the builtCE32 - // from before this clearContexts() call. - // Simpler than finding and resetting all of those fields. - ++contextsEra; -} - -void -CollationDataBuilder::buildContexts(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - // Ignore abandoned lists and the cached builtCE32, - // and build all contexts from scratch. - clearContexts(); - UnicodeSetIterator iter(contextChars); - while(U_SUCCESS(errorCode) && iter.next()) { - U_ASSERT(!iter.isString()); - UChar32 c = iter.getCodepoint(); - uint32_t ce32 = utrie2_get32(trie, c); - if(!isBuilderContextCE32(ce32)) { - // Impossible: No context data for c in contextChars. - errorCode = U_INTERNAL_PROGRAM_ERROR; - return; - } - ConditionalCE32 *cond = getConditionalCE32ForCE32(ce32); - ce32 = buildContext(cond, errorCode); - utrie2_set32(trie, c, ce32, &errorCode); - } -} - -uint32_t -CollationDataBuilder::buildContext(ConditionalCE32 *head, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - // The list head must have no context. - U_ASSERT(!head->hasContext()); - // The list head must be followed by one or more nodes that all do have context. - U_ASSERT(head->next >= 0); - UCharsTrieBuilder prefixBuilder(errorCode); - UCharsTrieBuilder contractionBuilder(errorCode); - // This outer loop goes from each prefix to the next. - // For each prefix it finds the one or more same-prefix entries (firstCond..lastCond). - // If there are multiple suffixes for the same prefix, - // then an inner loop builds a contraction trie for them. - for(ConditionalCE32 *cond = head;; cond = getConditionalCE32(cond->next)) { - if(U_FAILURE(errorCode)) { return 0; } // early out for memory allocation errors - // After the list head, the prefix or suffix can be empty, but not both. - U_ASSERT(cond == head || cond->hasContext()); - int32_t prefixLength = cond->prefixLength(); - UnicodeString prefix(cond->context, 0, prefixLength + 1); - // Collect all contraction suffixes for one prefix. - ConditionalCE32 *firstCond = cond; - ConditionalCE32 *lastCond; - do { - lastCond = cond; - // Clear the defaultCE32 fields as we go. - // They are left over from building a previous version of this list of contexts. - // - // One of the code paths below may copy a preceding defaultCE32 - // into its emptySuffixCE32. - // If a new suffix has been inserted before what used to be - // the firstCond for its prefix, then that previous firstCond could still - // contain an outdated defaultCE32 from an earlier buildContext() and - // result in an incorrect emptySuffixCE32. - // So we reset all defaultCE32 before reading and setting new values. - cond->defaultCE32 = Collation::NO_CE32; - } while(cond->next >= 0 && - (cond = getConditionalCE32(cond->next))->context.startsWith(prefix)); - uint32_t ce32; - int32_t suffixStart = prefixLength + 1; // == prefix.length() - if(lastCond->context.length() == suffixStart) { - // One prefix without contraction suffix. - U_ASSERT(firstCond == lastCond); - ce32 = lastCond->ce32; - cond = lastCond; - } else { - // Build the contractions trie. - contractionBuilder.clear(); - // Entry for an empty suffix, to be stored before the trie. - uint32_t emptySuffixCE32 = 0; - uint32_t flags = 0; - if(firstCond->context.length() == suffixStart) { - // There is a mapping for the prefix and the single character c. (p|c) - // If no other suffix matches, then we return this value. - emptySuffixCE32 = firstCond->ce32; - cond = getConditionalCE32(firstCond->next); - } else { - // There is no mapping for the prefix and just the single character. - // (There is no p|c, only p|cd, p|ce etc.) - flags |= Collation::CONTRACT_SINGLE_CP_NO_MATCH; - // When the prefix matches but none of the prefix-specific suffixes, - // then we fall back to the mappings with the next-longest prefix, - // and ultimately to mappings with no prefix. - // Each fallback might be another set of contractions. - // For example, if there are mappings for ch, p|cd, p|ce, but not for p|c, - // then in text "pch" we find the ch contraction. - for(cond = head;; cond = getConditionalCE32(cond->next)) { - int32_t length = cond->prefixLength(); - if(length == prefixLength) { break; } - if(cond->defaultCE32 != Collation::NO_CE32 && - (length==0 || prefix.endsWith(cond->context, 1, length))) { - emptySuffixCE32 = cond->defaultCE32; - } - } - cond = firstCond; - } - // Optimization: Set a flag when - // the first character of every contraction suffix has lccc!=0. - // Short-circuits contraction matching when a normal letter follows. - flags |= Collation::CONTRACT_NEXT_CCC; - // Add all of the non-empty suffixes into the contraction trie. - for(;;) { - UnicodeString suffix(cond->context, suffixStart); - uint16_t fcd16 = nfcImpl.getFCD16(suffix.char32At(0)); - if(fcd16 <= 0xff) { - flags &= ~Collation::CONTRACT_NEXT_CCC; - } - fcd16 = nfcImpl.getFCD16(suffix.char32At(suffix.length() - 1)); - if(fcd16 > 0xff) { - // The last suffix character has lccc!=0, allowing for discontiguous contractions. - flags |= Collation::CONTRACT_TRAILING_CCC; - } - if (icu4xMode && (flags & Collation::CONTRACT_HAS_STARTER) == 0) { - for (int32_t i = 0; i < suffix.length();) { - UChar32 c = suffix.char32At(i); - if (!u_getCombiningClass(c)) { - flags |= Collation::CONTRACT_HAS_STARTER; - break; - } - if (c > 0xFFFF) { - i += 2; - } else { - ++i; - } - } - } - contractionBuilder.add(suffix, (int32_t)cond->ce32, errorCode); - if(cond == lastCond) { break; } - cond = getConditionalCE32(cond->next); - } - int32_t index = addContextTrie(emptySuffixCE32, contractionBuilder, errorCode); - if(U_FAILURE(errorCode)) { return 0; } - if(index > Collation::MAX_INDEX) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return 0; - } - ce32 = Collation::makeCE32FromTagAndIndex(Collation::CONTRACTION_TAG, index) | flags; - } - U_ASSERT(cond == lastCond); - firstCond->defaultCE32 = ce32; - if(prefixLength == 0) { - if(cond->next < 0) { - // No non-empty prefixes, only contractions. - return ce32; - } - } else { - prefix.remove(0, 1); // Remove the length unit. - prefix.reverse(); - prefixBuilder.add(prefix, (int32_t)ce32, errorCode); - if(cond->next < 0) { break; } - } - } - U_ASSERT(head->defaultCE32 != Collation::NO_CE32); - int32_t index = addContextTrie(head->defaultCE32, prefixBuilder, errorCode); - if(U_FAILURE(errorCode)) { return 0; } - if(index > Collation::MAX_INDEX) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return 0; - } - return Collation::makeCE32FromTagAndIndex(Collation::PREFIX_TAG, index); -} - -int32_t -CollationDataBuilder::addContextTrie(uint32_t defaultCE32, UCharsTrieBuilder &trieBuilder, - UErrorCode &errorCode) { - UnicodeString context; - context.append((UChar)(defaultCE32 >> 16)).append((UChar)defaultCE32); - UnicodeString trieString; - context.append(trieBuilder.buildUnicodeString(USTRINGTRIE_BUILD_SMALL, trieString, errorCode)); - if(U_FAILURE(errorCode)) { return -1; } - int32_t index = contexts.indexOf(context); - if(index < 0) { - index = contexts.length(); - contexts.append(context); - } - return index; -} - -void -CollationDataBuilder::buildFastLatinTable(CollationData &data, UErrorCode &errorCode) { - if(U_FAILURE(errorCode) || !fastLatinEnabled) { return; } - - delete fastLatinBuilder; - fastLatinBuilder = new CollationFastLatinBuilder(errorCode); - if(fastLatinBuilder == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - if(fastLatinBuilder->forData(data, errorCode)) { - const uint16_t *table = fastLatinBuilder->getTable(); - int32_t length = fastLatinBuilder->lengthOfTable(); - if(base != NULL && length == base->fastLatinTableLength && - uprv_memcmp(table, base->fastLatinTable, length * 2) == 0) { - // Same fast Latin table as in the base, use that one instead. - delete fastLatinBuilder; - fastLatinBuilder = NULL; - table = base->fastLatinTable; - } - data.fastLatinTable = table; - data.fastLatinTableLength = length; - } else { - delete fastLatinBuilder; - fastLatinBuilder = NULL; - } -} - -int32_t -CollationDataBuilder::getCEs(const UnicodeString &s, int64_t ces[], int32_t cesLength) { - return getCEs(s, 0, ces, cesLength); -} - -int32_t -CollationDataBuilder::getCEs(const UnicodeString &prefix, const UnicodeString &s, - int64_t ces[], int32_t cesLength) { - int32_t prefixLength = prefix.length(); - if(prefixLength == 0) { - return getCEs(s, 0, ces, cesLength); - } else { - return getCEs(prefix + s, prefixLength, ces, cesLength); - } -} - -int32_t -CollationDataBuilder::getCEs(const UnicodeString &s, int32_t start, - int64_t ces[], int32_t cesLength) { - if(collIter == NULL) { - collIter = new DataBuilderCollationIterator(*this); - if(collIter == NULL) { return 0; } - } - return collIter->fetchCEs(s, start, ces, cesLength); -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2012-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationdatabuilder.cpp +* +* (replaced the former ucol_elm.cpp) +* +* created on: 2012apr01 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/localpointer.h" +#include "unicode/uchar.h" +#include "unicode/ucharstrie.h" +#include "unicode/ucharstriebuilder.h" +#include "unicode/uniset.h" +#include "unicode/unistr.h" +#include "unicode/usetiter.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "collation.h" +#include "collationdata.h" +#include "collationdatabuilder.h" +#include "collationfastlatinbuilder.h" +#include "collationiterator.h" +#include "normalizer2impl.h" +#include "utrie2.h" +#include "uvectr32.h" +#include "uvectr64.h" +#include "uvector.h" + +U_NAMESPACE_BEGIN + +CollationDataBuilder::CEModifier::~CEModifier() {} + +/** + * Build-time context and CE32 for a code point. + * If a code point has contextual mappings, then the default (no-context) mapping + * and all conditional mappings are stored in a singly-linked list + * of ConditionalCE32, sorted by context strings. + * + * Context strings sort by prefix length, then by prefix, then by contraction suffix. + * Context strings must be unique and in ascending order. + */ +struct ConditionalCE32 : public UMemory { + ConditionalCE32() + : context(), + ce32(0), defaultCE32(Collation::NO_CE32), builtCE32(Collation::NO_CE32), + next(-1) {} + ConditionalCE32(const UnicodeString &ct, uint32_t ce) + : context(ct), + ce32(ce), defaultCE32(Collation::NO_CE32), builtCE32(Collation::NO_CE32), + next(-1) {} + + inline UBool hasContext() const { return context.length() > 1; } + inline int32_t prefixLength() const { return context.charAt(0); } + + /** + * "\0" for the first entry for any code point, with its default CE32. + * + * Otherwise one unit with the length of the prefix string, + * then the prefix string, then the contraction suffix. + */ + UnicodeString context; + /** + * CE32 for the code point and its context. + * Can be special (e.g., for an expansion) but not contextual (prefix or contraction tag). + */ + uint32_t ce32; + /** + * Default CE32 for all contexts with this same prefix. + * Initially NO_CE32. Set only while building runtime data structures, + * and only on one of the nodes of a sub-list with the same prefix. + */ + uint32_t defaultCE32; + /** + * CE32 for the built contexts. + * When fetching CEs from the builder, the contexts are built into their runtime form + * so that the normal collation implementation can process them. + * The result is cached in the list head. It is reset when the contexts are modified. + * All of these builtCE32 are invalidated by clearContexts(), + * via incrementing the contextsEra. + */ + uint32_t builtCE32; + /** + * The "era" of building intermediate contexts when the above builtCE32 was set. + * When the array of cached, temporary contexts overflows, then clearContexts() + * removes them all and invalidates the builtCE32 that used to point to built tries. + */ + int32_t era = -1; + /** + * Index of the next ConditionalCE32. + * Negative for the end of the list. + */ + int32_t next; + // Note: We could create a separate class for all of the contextual mappings for + // a code point, with the builtCE32, the era, and a list of the actual mappings. + // The class that represents one mapping would then not need to + // store those fields in each element. +}; + +U_CDECL_BEGIN + +void U_CALLCONV +uprv_deleteConditionalCE32(void *obj) { + delete static_cast(obj); +} + +U_CDECL_END + +/** + * Build-time collation element and character iterator. + * Uses the runtime CollationIterator for fetching CEs for a string + * but reads from the builder's unfinished data structures. + * In particular, this class reads from the unfinished trie + * and has to avoid CollationIterator::nextCE() and redirect other + * calls to data->getCE32() and data->getCE32FromSupplementary(). + * + * We do this so that we need not implement the collation algorithm + * again for the builder and make it behave exactly like the runtime code. + * That would be more difficult to test and maintain than this indirection. + * + * Some CE32 tags (for example, the DIGIT_TAG) do not occur in the builder data, + * so the data accesses from those code paths need not be modified. + * + * This class iterates directly over whole code points + * so that the CollationIterator does not need the finished trie + * for handling the LEAD_SURROGATE_TAG. + */ +class DataBuilderCollationIterator : public CollationIterator { +public: + DataBuilderCollationIterator(CollationDataBuilder &b); + + virtual ~DataBuilderCollationIterator(); + + int32_t fetchCEs(const UnicodeString &str, int32_t start, int64_t ces[], int32_t cesLength); + + virtual void resetToOffset(int32_t newOffset) override; + virtual int32_t getOffset() const override; + + virtual UChar32 nextCodePoint(UErrorCode &errorCode) override; + virtual UChar32 previousCodePoint(UErrorCode &errorCode) override; + +protected: + virtual void forwardNumCodePoints(int32_t num, UErrorCode &errorCode) override; + virtual void backwardNumCodePoints(int32_t num, UErrorCode &errorCode) override; + + virtual uint32_t getDataCE32(UChar32 c) const override; + virtual uint32_t getCE32FromBuilderData(uint32_t ce32, UErrorCode &errorCode) override; + + CollationDataBuilder &builder; + CollationData builderData; + uint32_t jamoCE32s[CollationData::JAMO_CE32S_LENGTH]; + const UnicodeString *s; + int32_t pos; +}; + +DataBuilderCollationIterator::DataBuilderCollationIterator(CollationDataBuilder &b) + : CollationIterator(&builderData, /*numeric=*/ false), + builder(b), builderData(b.nfcImpl), + s(nullptr), pos(0) { + builderData.base = builder.base; + // Set all of the jamoCE32s[] to indirection CE32s. + for(int32_t j = 0; j < CollationData::JAMO_CE32S_LENGTH; ++j) { // Count across Jamo types. + UChar32 jamo = CollationDataBuilder::jamoCpFromIndex(j); + jamoCE32s[j] = Collation::makeCE32FromTagAndIndex(Collation::BUILDER_DATA_TAG, jamo) | + CollationDataBuilder::IS_BUILDER_JAMO_CE32; + } + builderData.jamoCE32s = jamoCE32s; +} + +DataBuilderCollationIterator::~DataBuilderCollationIterator() {} + +int32_t +DataBuilderCollationIterator::fetchCEs(const UnicodeString &str, int32_t start, + int64_t ces[], int32_t cesLength) { + // Set the pointers each time, in case they changed due to reallocation. + builderData.ce32s = reinterpret_cast(builder.ce32s.getBuffer()); + builderData.ces = builder.ce64s.getBuffer(); + builderData.contexts = builder.contexts.getBuffer(); + // Modified copy of CollationIterator::nextCE() and CollationIterator::nextCEFromCE32(). + reset(); + s = &str; + pos = start; + UErrorCode errorCode = U_ZERO_ERROR; + while(U_SUCCESS(errorCode) && pos < s->length()) { + // No need to keep all CEs in the iterator buffer. + clearCEs(); + UChar32 c = s->char32At(pos); + pos += U16_LENGTH(c); + uint32_t ce32 = utrie2_get32(builder.trie, c); + const CollationData *d; + if(ce32 == Collation::FALLBACK_CE32) { + d = builder.base; + ce32 = builder.base->getCE32(c); + } else { + d = &builderData; + } + appendCEsFromCE32(d, c, ce32, /*forward=*/ true, errorCode); + U_ASSERT(U_SUCCESS(errorCode)); + for(int32_t i = 0; i < getCEsLength(); ++i) { + int64_t ce = getCE(i); + if(ce != 0) { + if(cesLength < Collation::MAX_EXPANSION_LENGTH) { + ces[cesLength] = ce; + } + ++cesLength; + } + } + } + return cesLength; +} + +void +DataBuilderCollationIterator::resetToOffset(int32_t newOffset) { + reset(); + pos = newOffset; +} + +int32_t +DataBuilderCollationIterator::getOffset() const { + return pos; +} + +UChar32 +DataBuilderCollationIterator::nextCodePoint(UErrorCode & /*errorCode*/) { + if(pos == s->length()) { + return U_SENTINEL; + } + UChar32 c = s->char32At(pos); + pos += U16_LENGTH(c); + return c; +} + +UChar32 +DataBuilderCollationIterator::previousCodePoint(UErrorCode & /*errorCode*/) { + if(pos == 0) { + return U_SENTINEL; + } + UChar32 c = s->char32At(pos - 1); + pos -= U16_LENGTH(c); + return c; +} + +void +DataBuilderCollationIterator::forwardNumCodePoints(int32_t num, UErrorCode & /*errorCode*/) { + pos = s->moveIndex32(pos, num); +} + +void +DataBuilderCollationIterator::backwardNumCodePoints(int32_t num, UErrorCode & /*errorCode*/) { + pos = s->moveIndex32(pos, -num); +} + +uint32_t +DataBuilderCollationIterator::getDataCE32(UChar32 c) const { + return utrie2_get32(builder.trie, c); +} + +uint32_t +DataBuilderCollationIterator::getCE32FromBuilderData(uint32_t ce32, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return 0; } + U_ASSERT(Collation::hasCE32Tag(ce32, Collation::BUILDER_DATA_TAG)); + if((ce32 & CollationDataBuilder::IS_BUILDER_JAMO_CE32) != 0) { + UChar32 jamo = Collation::indexFromCE32(ce32); + return utrie2_get32(builder.trie, jamo); + } else { + ConditionalCE32 *cond = builder.getConditionalCE32ForCE32(ce32); + if (cond == nullptr) { + errorCode = U_INTERNAL_PROGRAM_ERROR; + // TODO: ICU-21531 figure out why this happens. + return 0; + } + if(cond->builtCE32 == Collation::NO_CE32 || cond->era != builder.contextsEra) { + // Build the context-sensitive mappings into their runtime form and cache the result. + cond->builtCE32 = builder.buildContext(cond, errorCode); + if(errorCode == U_BUFFER_OVERFLOW_ERROR) { + errorCode = U_ZERO_ERROR; + builder.clearContexts(); + cond->builtCE32 = builder.buildContext(cond, errorCode); + } + cond->era = builder.contextsEra; + builderData.contexts = builder.contexts.getBuffer(); + } + return cond->builtCE32; + } +} + +// ------------------------------------------------------------------------- *** + +CollationDataBuilder::CollationDataBuilder(UBool icu4xMode, UErrorCode &errorCode) + : nfcImpl(*Normalizer2Factory::getNFCImpl(errorCode)), + base(nullptr), baseSettings(nullptr), + trie(nullptr), + ce32s(errorCode), ce64s(errorCode), conditionalCE32s(errorCode), + modified(false), + icu4xMode(icu4xMode), + fastLatinEnabled(false), fastLatinBuilder(nullptr), + collIter(nullptr) { + // Reserve the first CE32 for U+0000. + if (!icu4xMode) { + ce32s.addElement(0, errorCode); + } + conditionalCE32s.setDeleter(uprv_deleteConditionalCE32); +} + +CollationDataBuilder::~CollationDataBuilder() { + utrie2_close(trie); + delete fastLatinBuilder; + delete collIter; +} + +void +CollationDataBuilder::initForTailoring(const CollationData *b, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + if(trie != nullptr) { + errorCode = U_INVALID_STATE_ERROR; + return; + } + if(b == nullptr) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + base = b; + + // For a tailoring, the default is to fall back to the base. + // For ICU4X, use the same value for fallback as for the default + // to avoid having to have different blocks for the two. + trie = utrie2_open(Collation::FALLBACK_CE32, icu4xMode ? Collation::FALLBACK_CE32 : Collation::FFFD_CE32, &errorCode); + + if (!icu4xMode) { + // Set the Latin-1 letters block so that it is allocated first in the data array, + // to try to improve locality of reference when sorting Latin-1 text. + // Do not use utrie2_setRange32() since that will not actually allocate blocks + // that are filled with the default value. + // ASCII (0..7F) is already preallocated anyway. + for(UChar32 c = 0xc0; c <= 0xff; ++c) { + utrie2_set32(trie, c, Collation::FALLBACK_CE32, &errorCode); + } + + // Hangul syllables are not tailorable (except via tailoring Jamos). + // Always set the Hangul tag to help performance. + // Do this here, rather than in buildMappings(), + // so that we see the HANGUL_TAG in various assertions. + uint32_t hangulCE32 = Collation::makeCE32FromTagAndIndex(Collation::HANGUL_TAG, 0); + utrie2_setRange32(trie, Hangul::HANGUL_BASE, Hangul::HANGUL_END, hangulCE32, true, &errorCode); + + // Copy the set contents but don't copy/clone the set as a whole because + // that would copy the isFrozen state too. + unsafeBackwardSet.addAll(*b->unsafeBackwardSet); + } + + if(U_FAILURE(errorCode)) { return; } +} + +UBool +CollationDataBuilder::maybeSetPrimaryRange(UChar32 start, UChar32 end, + uint32_t primary, int32_t step, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return false; } + U_ASSERT(start <= end); + // TODO: Do we need to check what values are currently set for start..end? + // An offset range is worth it only if we can achieve an overlap between + // adjacent UTrie2 blocks of 32 code points each. + // An offset CE is also a little more expensive to look up and compute + // than a simple CE. + // If the range spans at least three UTrie2 block boundaries (> 64 code points), + // then we take it. + // If the range spans one or two block boundaries and there are + // at least 4 code points on either side, then we take it. + // (We could additionally require a minimum range length of, say, 16.) + int32_t blockDelta = (end >> 5) - (start >> 5); + if(2 <= step && step <= 0x7f && + (blockDelta >= 3 || + (blockDelta > 0 && (start & 0x1f) <= 0x1c && (end & 0x1f) >= 3))) { + int64_t dataCE = ((int64_t)primary << 32) | (start << 8) | step; + if(isCompressiblePrimary(primary)) { dataCE |= 0x80; } + int32_t index = addCE(dataCE, errorCode); + if(U_FAILURE(errorCode)) { return 0; } + if(index > Collation::MAX_INDEX) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + uint32_t offsetCE32 = Collation::makeCE32FromTagAndIndex(Collation::OFFSET_TAG, index); + utrie2_setRange32(trie, start, end, offsetCE32, true, &errorCode); + modified = true; + return true; + } else { + return false; + } +} + +uint32_t +CollationDataBuilder::setPrimaryRangeAndReturnNext(UChar32 start, UChar32 end, + uint32_t primary, int32_t step, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + UBool isCompressible = isCompressiblePrimary(primary); + if(maybeSetPrimaryRange(start, end, primary, step, errorCode)) { + return Collation::incThreeBytePrimaryByOffset(primary, isCompressible, + (end - start + 1) * step); + } else { + // Short range: Set individual CE32s. + for(;;) { + utrie2_set32(trie, start, Collation::makeLongPrimaryCE32(primary), &errorCode); + ++start; + primary = Collation::incThreeBytePrimaryByOffset(primary, isCompressible, step); + if(start > end) { return primary; } + } + modified = true; + } +} + +uint32_t +CollationDataBuilder::getCE32FromOffsetCE32(UBool fromBase, UChar32 c, uint32_t ce32) const { + int32_t i = Collation::indexFromCE32(ce32); + int64_t dataCE = fromBase ? base->ces[i] : ce64s.elementAti(i); + uint32_t p = Collation::getThreeBytePrimaryForOffsetData(c, dataCE); + return Collation::makeLongPrimaryCE32(p); +} + +UBool +CollationDataBuilder::isCompressibleLeadByte(uint32_t b) const { + return base->isCompressibleLeadByte(b); +} + +UBool +CollationDataBuilder::isAssigned(UChar32 c) const { + return Collation::isAssignedCE32(utrie2_get32(trie, c)); +} + +uint32_t +CollationDataBuilder::getLongPrimaryIfSingleCE(UChar32 c) const { + uint32_t ce32 = utrie2_get32(trie, c); + if(Collation::isLongPrimaryCE32(ce32)) { + return Collation::primaryFromLongPrimaryCE32(ce32); + } else { + return 0; + } +} + +int64_t +CollationDataBuilder::getSingleCE(UChar32 c, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return 0; } + // Keep parallel with CollationData::getSingleCE(). + UBool fromBase = false; + uint32_t ce32 = utrie2_get32(trie, c); + if(ce32 == Collation::FALLBACK_CE32) { + fromBase = true; + ce32 = base->getCE32(c); + } + while(Collation::isSpecialCE32(ce32)) { + switch(Collation::tagFromCE32(ce32)) { + case Collation::LATIN_EXPANSION_TAG: + case Collation::BUILDER_DATA_TAG: + case Collation::PREFIX_TAG: + case Collation::CONTRACTION_TAG: + case Collation::HANGUL_TAG: + case Collation::LEAD_SURROGATE_TAG: + errorCode = U_UNSUPPORTED_ERROR; + return 0; + case Collation::FALLBACK_TAG: + case Collation::RESERVED_TAG_3: + errorCode = U_INTERNAL_PROGRAM_ERROR; + return 0; + case Collation::LONG_PRIMARY_TAG: + return Collation::ceFromLongPrimaryCE32(ce32); + case Collation::LONG_SECONDARY_TAG: + return Collation::ceFromLongSecondaryCE32(ce32); + case Collation::EXPANSION32_TAG: + if(Collation::lengthFromCE32(ce32) == 1) { + int32_t i = Collation::indexFromCE32(ce32); + ce32 = fromBase ? base->ce32s[i] : ce32s.elementAti(i); + break; + } else { + errorCode = U_UNSUPPORTED_ERROR; + return 0; + } + case Collation::EXPANSION_TAG: { + if(Collation::lengthFromCE32(ce32) == 1) { + int32_t i = Collation::indexFromCE32(ce32); + return fromBase ? base->ces[i] : ce64s.elementAti(i); + } else { + errorCode = U_UNSUPPORTED_ERROR; + return 0; + } + } + case Collation::DIGIT_TAG: + // Fetch the non-numeric-collation CE32 and continue. + ce32 = ce32s.elementAti(Collation::indexFromCE32(ce32)); + break; + case Collation::U0000_TAG: + U_ASSERT(c == 0); + // Fetch the normal ce32 for U+0000 and continue. + ce32 = fromBase ? base->ce32s[0] : ce32s.elementAti(0); + break; + case Collation::OFFSET_TAG: + ce32 = getCE32FromOffsetCE32(fromBase, c, ce32); + break; + case Collation::IMPLICIT_TAG: + return Collation::unassignedCEFromCodePoint(c); + } + } + return Collation::ceFromSimpleCE32(ce32); +} + +int32_t +CollationDataBuilder::addCE(int64_t ce, UErrorCode &errorCode) { + int32_t length = ce64s.size(); + for(int32_t i = 0; i < length; ++i) { + if(ce == ce64s.elementAti(i)) { return i; } + } + ce64s.addElement(ce, errorCode); + return length; +} + +int32_t +CollationDataBuilder::addCE32(uint32_t ce32, UErrorCode &errorCode) { + int32_t length = ce32s.size(); + for(int32_t i = 0; i < length; ++i) { + if(ce32 == (uint32_t)ce32s.elementAti(i)) { return i; } + } + ce32s.addElement((int32_t)ce32, errorCode); + return length; +} + +int32_t +CollationDataBuilder::addConditionalCE32(const UnicodeString &context, uint32_t ce32, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return -1; } + U_ASSERT(!context.isEmpty()); + int32_t index = conditionalCE32s.size(); + if(index > Collation::MAX_INDEX) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return -1; + } + LocalPointer cond(new ConditionalCE32(context, ce32), errorCode); + conditionalCE32s.adoptElement(cond.orphan(), errorCode); + if(U_FAILURE(errorCode)) { + return -1; + } + return index; +} + +void +CollationDataBuilder::add(const UnicodeString &prefix, const UnicodeString &s, + const int64_t ces[], int32_t cesLength, + UErrorCode &errorCode) { + uint32_t ce32 = encodeCEs(ces, cesLength, errorCode); + addCE32(prefix, s, ce32, errorCode); +} + +void +CollationDataBuilder::addCE32(const UnicodeString &prefix, const UnicodeString &s, + uint32_t ce32, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + if(s.isEmpty()) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if(trie == nullptr || utrie2_isFrozen(trie)) { + errorCode = U_INVALID_STATE_ERROR; + return; + } + UChar32 c = s.char32At(0); + int32_t cLength = U16_LENGTH(c); + uint32_t oldCE32 = utrie2_get32(trie, c); + UBool hasContext = !prefix.isEmpty() || s.length() > cLength; + + if (icu4xMode) { + if (base && c >= 0x1100 && c < 0x1200) { + // Omit jamo tailorings. + // TODO(https://github.com/unicode-org/icu4x/issues/1941). + } + const Normalizer2* nfdNormalizer = Normalizer2::getNFDInstance(errorCode); + UnicodeString sInNfd; + nfdNormalizer->normalize(s, sInNfd, errorCode); + if (s != sInNfd) { + // s is not in NFD, so it cannot match in ICU4X, since ICU4X only + // does NFD lookups. + // Now check that we're only rejecting known cases. + if (s.length() == 2) { + char16_t second = s.charAt(1); + if (second == 0x0F73 || second == 0x0F75 || second == 0x0F81) { + // Second is a special decomposing Tibetan vowel sign. + // These also get added in the decomposed form, so ignoring + // this instance is OK. + return; + } + if (c == 0xFDD1 && second == 0xAC00) { + // This strange contraction exists in the root and + // doesn't have a decomposed counterpart there. + // This won't match in ICU4X anyway and is very strange: + // Unassigned Arabic presentation form contracting with + // the very first Hangul syllable. Let's ignore this + // explicitly. + return; + } + } + // Unknown case worth investigating if ever found. + errorCode = U_UNSUPPORTED_ERROR; + return; + } + + if (!prefix.isEmpty()) { + UnicodeString prefixInNfd; + nfdNormalizer->normalize(prefix, prefixInNfd, errorCode); + if (prefix != prefixInNfd) { + errorCode = U_UNSUPPORTED_ERROR; + return; + } + + int32_t count = prefix.countChar32(); + if (count > 2) { + // Prefix too long for ICU4X. + errorCode = U_UNSUPPORTED_ERROR; + return; + } + UChar32 utf32[4]; + int32_t len = prefix.toUTF32(utf32, 4, errorCode); + if (len != count) { + errorCode = U_INVALID_STATE_ERROR; + return; + } + UChar32 c = utf32[0]; + if (u_getCombiningClass(c)) { + // Prefix must start with as starter for ICU4X. + errorCode = U_UNSUPPORTED_ERROR; + return; + } + // XXX: Korean searchjl has jamo in prefix, so commenting out this + // check for now. ICU4X currently ignores non-root jamo tables anyway. + // searchjl was added in + // https://unicode-org.atlassian.net/browse/CLDR-3560 + // Contractions were changed to prefixes in + // https://unicode-org.atlassian.net/browse/CLDR-6546 + // + // if ((c >= 0x1100 && c < 0x1200) || (c >= 0xAC00 && c < 0xD7A4)) { + // errorCode = U_UNSUPPORTED_ERROR; + // return; + // } + if ((len > 1) && !(utf32[1] == 0x3099 || utf32[1] == 0x309A)) { + // Second character in prefix, if present, must be a kana voicing mark for ICU4X. + errorCode = U_UNSUPPORTED_ERROR; + return; + } + } + + if (s.length() > cLength) { + // Check that there's no modern Hangul in contractions. + for (int32_t i = 0; i < s.length(); ++i) { + char16_t c = s.charAt(i); + if ((c >= 0x1100 && c < 0x1100 + 19) || (c >= 0x1161 && c < 0x1161 + 21) || (c >= 0x11A7 && c < 0x11A7 + 28) || (c >= 0xAC00 && c < 0xD7A4)) { + errorCode = U_UNSUPPORTED_ERROR; + return; + } + } + } + } + + if(oldCE32 == Collation::FALLBACK_CE32) { + // First tailoring for c. + // If c has contextual base mappings or if we add a contextual mapping, + // then copy the base mappings. + // Otherwise we just override the base mapping. + uint32_t baseCE32 = base->getFinalCE32(base->getCE32(c)); + if(hasContext || Collation::ce32HasContext(baseCE32)) { + oldCE32 = copyFromBaseCE32(c, baseCE32, true, errorCode); + utrie2_set32(trie, c, oldCE32, &errorCode); + if(U_FAILURE(errorCode)) { return; } + } + } + if(!hasContext) { + // No prefix, no contraction. + if(!isBuilderContextCE32(oldCE32)) { + utrie2_set32(trie, c, ce32, &errorCode); + } else { + ConditionalCE32 *cond = getConditionalCE32ForCE32(oldCE32); + cond->builtCE32 = Collation::NO_CE32; + cond->ce32 = ce32; + } + } else { + ConditionalCE32 *cond; + if(!isBuilderContextCE32(oldCE32)) { + // Replace the simple oldCE32 with a builder context CE32 + // pointing to a new ConditionalCE32 list head. + int32_t index = addConditionalCE32(UnicodeString((char16_t)0), oldCE32, errorCode); + if(U_FAILURE(errorCode)) { return; } + uint32_t contextCE32 = makeBuilderContextCE32(index); + utrie2_set32(trie, c, contextCE32, &errorCode); + contextChars.add(c); + cond = getConditionalCE32(index); + } else { + cond = getConditionalCE32ForCE32(oldCE32); + cond->builtCE32 = Collation::NO_CE32; + } + UnicodeString suffix(s, cLength); + UnicodeString context((char16_t)prefix.length()); + context.append(prefix).append(suffix); + unsafeBackwardSet.addAll(suffix); + for(;;) { + // invariant: context > cond->context + int32_t next = cond->next; + if(next < 0) { + // Append a new ConditionalCE32 after cond. + int32_t index = addConditionalCE32(context, ce32, errorCode); + if(U_FAILURE(errorCode)) { return; } + cond->next = index; + break; + } + ConditionalCE32 *nextCond = getConditionalCE32(next); + int8_t cmp = context.compare(nextCond->context); + if(cmp < 0) { + // Insert a new ConditionalCE32 between cond and nextCond. + int32_t index = addConditionalCE32(context, ce32, errorCode); + if(U_FAILURE(errorCode)) { return; } + cond->next = index; + getConditionalCE32(index)->next = next; + break; + } else if(cmp == 0) { + // Same context as before, overwrite its ce32. + nextCond->ce32 = ce32; + break; + } + cond = nextCond; + } + } + modified = true; +} + +uint32_t +CollationDataBuilder::encodeOneCEAsCE32(int64_t ce) { + uint32_t p = (uint32_t)(ce >> 32); + uint32_t lower32 = (uint32_t)ce; + uint32_t t = (uint32_t)(ce & 0xffff); + U_ASSERT((t & 0xc000) != 0xc000); // Impossible case bits 11 mark special CE32s. + if((ce & INT64_C(0xffff00ff00ff)) == 0) { + // normal form ppppsstt + return p | (lower32 >> 16) | (t >> 8); + } else if((ce & INT64_C(0xffffffffff)) == Collation::COMMON_SEC_AND_TER_CE) { + // long-primary form ppppppC1 + return Collation::makeLongPrimaryCE32(p); + } else if(p == 0 && (t & 0xff) == 0) { + // long-secondary form ssssttC2 + return Collation::makeLongSecondaryCE32(lower32); + } + return Collation::NO_CE32; +} + +uint32_t +CollationDataBuilder::encodeOneCE(int64_t ce, UErrorCode &errorCode) { + // Try to encode one CE as one CE32. + uint32_t ce32 = encodeOneCEAsCE32(ce); + if(ce32 != Collation::NO_CE32) { return ce32; } + int32_t index = addCE(ce, errorCode); + if(U_FAILURE(errorCode)) { return 0; } + if(index > Collation::MAX_INDEX) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + return Collation::makeCE32FromTagIndexAndLength(Collation::EXPANSION_TAG, index, 1); +} + +uint32_t +CollationDataBuilder::encodeCEs(const int64_t ces[], int32_t cesLength, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + if(cesLength < 0 || cesLength > Collation::MAX_EXPANSION_LENGTH) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + if(trie == nullptr || utrie2_isFrozen(trie)) { + errorCode = U_INVALID_STATE_ERROR; + return 0; + } + if(cesLength == 0) { + // Convenience: We cannot map to nothing, but we can map to a completely ignorable CE. + // Do this here so that callers need not do it. + return encodeOneCEAsCE32(0); + } else if(cesLength == 1) { + return encodeOneCE(ces[0], errorCode); + } else if(cesLength == 2 && !icu4xMode) { + // Try to encode two CEs as one CE32. + // Turn this off for ICU4X, because without the canonical closure + // these are so rare that it doesn't make sense to spend a branch + // on checking this tag when using the data. + int64_t ce0 = ces[0]; + int64_t ce1 = ces[1]; + uint32_t p0 = (uint32_t)(ce0 >> 32); + if((ce0 & INT64_C(0xffffffffff00ff)) == Collation::COMMON_SECONDARY_CE && + (ce1 & INT64_C(0xffffffff00ffffff)) == Collation::COMMON_TERTIARY_CE && + p0 != 0) { + // Latin mini expansion + return + p0 | + (((uint32_t)ce0 & 0xff00u) << 8) | + (uint32_t)(ce1 >> 16) | + Collation::SPECIAL_CE32_LOW_BYTE | + Collation::LATIN_EXPANSION_TAG; + } + } + // Try to encode two or more CEs as CE32s. + int32_t newCE32s[Collation::MAX_EXPANSION_LENGTH]; + for(int32_t i = 0;; ++i) { + if(i == cesLength) { + return encodeExpansion32(newCE32s, cesLength, errorCode); + } + uint32_t ce32 = encodeOneCEAsCE32(ces[i]); + if(ce32 == Collation::NO_CE32) { break; } + newCE32s[i] = (int32_t)ce32; + } + return encodeExpansion(ces, cesLength, errorCode); +} + +uint32_t +CollationDataBuilder::encodeExpansion(const int64_t ces[], int32_t length, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + // See if this sequence of CEs has already been stored. + int64_t first = ces[0]; + int32_t ce64sMax = ce64s.size() - length; + for(int32_t i = 0; i <= ce64sMax; ++i) { + if(first == ce64s.elementAti(i)) { + if(i > Collation::MAX_INDEX) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + for(int32_t j = 1;; ++j) { + if(j == length) { + return Collation::makeCE32FromTagIndexAndLength( + Collation::EXPANSION_TAG, i, length); + } + if(ce64s.elementAti(i + j) != ces[j]) { break; } + } + } + } + // Store the new sequence. + int32_t i = ce64s.size(); + if(i > Collation::MAX_INDEX) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + for(int32_t j = 0; j < length; ++j) { + ce64s.addElement(ces[j], errorCode); + } + return Collation::makeCE32FromTagIndexAndLength(Collation::EXPANSION_TAG, i, length); +} + +uint32_t +CollationDataBuilder::encodeExpansion32(const int32_t newCE32s[], int32_t length, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + // See if this sequence of CE32s has already been stored. + int32_t first = newCE32s[0]; + int32_t ce32sMax = ce32s.size() - length; + for(int32_t i = 0; i <= ce32sMax; ++i) { + if(first == ce32s.elementAti(i)) { + if(i > Collation::MAX_INDEX) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + for(int32_t j = 1;; ++j) { + if(j == length) { + return Collation::makeCE32FromTagIndexAndLength( + Collation::EXPANSION32_TAG, i, length); + } + if(ce32s.elementAti(i + j) != newCE32s[j]) { break; } + } + } + } + // Store the new sequence. + int32_t i = ce32s.size(); + if(i > Collation::MAX_INDEX) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + for(int32_t j = 0; j < length; ++j) { + ce32s.addElement(newCE32s[j], errorCode); + } + return Collation::makeCE32FromTagIndexAndLength(Collation::EXPANSION32_TAG, i, length); +} + +uint32_t +CollationDataBuilder::copyFromBaseCE32(UChar32 c, uint32_t ce32, UBool withContext, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + if(!Collation::isSpecialCE32(ce32)) { return ce32; } + switch(Collation::tagFromCE32(ce32)) { + case Collation::LONG_PRIMARY_TAG: + case Collation::LONG_SECONDARY_TAG: + case Collation::LATIN_EXPANSION_TAG: + // copy as is + break; + case Collation::EXPANSION32_TAG: { + const uint32_t *baseCE32s = base->ce32s + Collation::indexFromCE32(ce32); + int32_t length = Collation::lengthFromCE32(ce32); + ce32 = encodeExpansion32( + reinterpret_cast(baseCE32s), length, errorCode); + break; + } + case Collation::EXPANSION_TAG: { + const int64_t *baseCEs = base->ces + Collation::indexFromCE32(ce32); + int32_t length = Collation::lengthFromCE32(ce32); + ce32 = encodeExpansion(baseCEs, length, errorCode); + break; + } + case Collation::PREFIX_TAG: { + // Flatten prefixes and nested suffixes (contractions) + // into a linear list of ConditionalCE32. + const char16_t *p = base->contexts + Collation::indexFromCE32(ce32); + ce32 = CollationData::readCE32(p); // Default if no prefix match. + if(!withContext) { + return copyFromBaseCE32(c, ce32, false, errorCode); + } + ConditionalCE32 head; + UnicodeString context((char16_t)0); + int32_t index; + if(Collation::isContractionCE32(ce32)) { + index = copyContractionsFromBaseCE32(context, c, ce32, &head, errorCode); + } else { + ce32 = copyFromBaseCE32(c, ce32, true, errorCode); + head.next = index = addConditionalCE32(context, ce32, errorCode); + } + if(U_FAILURE(errorCode)) { return 0; } + ConditionalCE32 *cond = getConditionalCE32(index); // the last ConditionalCE32 so far + UCharsTrie::Iterator prefixes(p + 2, 0, errorCode); + while(prefixes.next(errorCode)) { + context = prefixes.getString(); + context.reverse(); + context.insert(0, (char16_t)context.length()); + ce32 = (uint32_t)prefixes.getValue(); + if(Collation::isContractionCE32(ce32)) { + index = copyContractionsFromBaseCE32(context, c, ce32, cond, errorCode); + } else { + ce32 = copyFromBaseCE32(c, ce32, true, errorCode); + cond->next = index = addConditionalCE32(context, ce32, errorCode); + } + if(U_FAILURE(errorCode)) { return 0; } + cond = getConditionalCE32(index); + } + ce32 = makeBuilderContextCE32(head.next); + contextChars.add(c); + break; + } + case Collation::CONTRACTION_TAG: { + if(!withContext) { + const char16_t *p = base->contexts + Collation::indexFromCE32(ce32); + ce32 = CollationData::readCE32(p); // Default if no suffix match. + return copyFromBaseCE32(c, ce32, false, errorCode); + } + ConditionalCE32 head; + UnicodeString context((char16_t)0); + copyContractionsFromBaseCE32(context, c, ce32, &head, errorCode); + ce32 = makeBuilderContextCE32(head.next); + contextChars.add(c); + break; + } + case Collation::HANGUL_TAG: + errorCode = U_UNSUPPORTED_ERROR; // We forbid tailoring of Hangul syllables. + break; + case Collation::OFFSET_TAG: + ce32 = getCE32FromOffsetCE32(true, c, ce32); + break; + case Collation::IMPLICIT_TAG: + ce32 = encodeOneCE(Collation::unassignedCEFromCodePoint(c), errorCode); + break; + default: + UPRV_UNREACHABLE_EXIT; // require ce32 == base->getFinalCE32(ce32) + } + return ce32; +} + +int32_t +CollationDataBuilder::copyContractionsFromBaseCE32(UnicodeString &context, UChar32 c, uint32_t ce32, + ConditionalCE32 *cond, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + const char16_t *p = base->contexts + Collation::indexFromCE32(ce32); + int32_t index; + if((ce32 & Collation::CONTRACT_SINGLE_CP_NO_MATCH) != 0) { + // No match on the single code point. + // We are underneath a prefix, and the default mapping is just + // a fallback to the mappings for a shorter prefix. + U_ASSERT(context.length() > 1); + index = -1; + } else { + ce32 = CollationData::readCE32(p); // Default if no suffix match. + U_ASSERT(!Collation::isContractionCE32(ce32)); + ce32 = copyFromBaseCE32(c, ce32, true, errorCode); + cond->next = index = addConditionalCE32(context, ce32, errorCode); + if(U_FAILURE(errorCode)) { return 0; } + cond = getConditionalCE32(index); + } + + int32_t suffixStart = context.length(); + UCharsTrie::Iterator suffixes(p + 2, 0, errorCode); + while(suffixes.next(errorCode)) { + context.append(suffixes.getString()); + ce32 = copyFromBaseCE32(c, (uint32_t)suffixes.getValue(), true, errorCode); + cond->next = index = addConditionalCE32(context, ce32, errorCode); + if(U_FAILURE(errorCode)) { return 0; } + // No need to update the unsafeBackwardSet because the tailoring set + // is already a copy of the base set. + cond = getConditionalCE32(index); + context.truncate(suffixStart); + } + U_ASSERT(index >= 0); + return index; +} + +class CopyHelper { +public: + CopyHelper(const CollationDataBuilder &s, CollationDataBuilder &d, + const CollationDataBuilder::CEModifier &m, UErrorCode &initialErrorCode) + : src(s), dest(d), modifier(m), + errorCode(initialErrorCode) {} + + UBool copyRangeCE32(UChar32 start, UChar32 end, uint32_t ce32) { + ce32 = copyCE32(ce32); + utrie2_setRange32(dest.trie, start, end, ce32, true, &errorCode); + if(CollationDataBuilder::isBuilderContextCE32(ce32)) { + dest.contextChars.add(start, end); + } + return U_SUCCESS(errorCode); + } + + uint32_t copyCE32(uint32_t ce32) { + if(!Collation::isSpecialCE32(ce32)) { + int64_t ce = modifier.modifyCE32(ce32); + if(ce != Collation::NO_CE) { + ce32 = dest.encodeOneCE(ce, errorCode); + } + } else { + int32_t tag = Collation::tagFromCE32(ce32); + if(tag == Collation::EXPANSION32_TAG) { + const uint32_t *srcCE32s = reinterpret_cast(src.ce32s.getBuffer()); + srcCE32s += Collation::indexFromCE32(ce32); + int32_t length = Collation::lengthFromCE32(ce32); + // Inspect the source CE32s. Just copy them if none are modified. + // Otherwise copy to modifiedCEs, with modifications. + UBool isModified = false; + for(int32_t i = 0; i < length; ++i) { + ce32 = srcCE32s[i]; + int64_t ce; + if(Collation::isSpecialCE32(ce32) || + (ce = modifier.modifyCE32(ce32)) == Collation::NO_CE) { + if(isModified) { + modifiedCEs[i] = Collation::ceFromCE32(ce32); + } + } else { + if(!isModified) { + for(int32_t j = 0; j < i; ++j) { + modifiedCEs[j] = Collation::ceFromCE32(srcCE32s[j]); + } + isModified = true; + } + modifiedCEs[i] = ce; + } + } + if(isModified) { + ce32 = dest.encodeCEs(modifiedCEs, length, errorCode); + } else { + ce32 = dest.encodeExpansion32( + reinterpret_cast(srcCE32s), length, errorCode); + } + } else if(tag == Collation::EXPANSION_TAG) { + const int64_t *srcCEs = src.ce64s.getBuffer(); + srcCEs += Collation::indexFromCE32(ce32); + int32_t length = Collation::lengthFromCE32(ce32); + // Inspect the source CEs. Just copy them if none are modified. + // Otherwise copy to modifiedCEs, with modifications. + UBool isModified = false; + for(int32_t i = 0; i < length; ++i) { + int64_t srcCE = srcCEs[i]; + int64_t ce = modifier.modifyCE(srcCE); + if(ce == Collation::NO_CE) { + if(isModified) { + modifiedCEs[i] = srcCE; + } + } else { + if(!isModified) { + for(int32_t j = 0; j < i; ++j) { + modifiedCEs[j] = srcCEs[j]; + } + isModified = true; + } + modifiedCEs[i] = ce; + } + } + if(isModified) { + ce32 = dest.encodeCEs(modifiedCEs, length, errorCode); + } else { + ce32 = dest.encodeExpansion(srcCEs, length, errorCode); + } + } else if(tag == Collation::BUILDER_DATA_TAG) { + // Copy the list of ConditionalCE32. + ConditionalCE32 *cond = src.getConditionalCE32ForCE32(ce32); + U_ASSERT(!cond->hasContext()); + int32_t destIndex = dest.addConditionalCE32( + cond->context, copyCE32(cond->ce32), errorCode); + ce32 = CollationDataBuilder::makeBuilderContextCE32(destIndex); + while(cond->next >= 0) { + cond = src.getConditionalCE32(cond->next); + ConditionalCE32 *prevDestCond = dest.getConditionalCE32(destIndex); + destIndex = dest.addConditionalCE32( + cond->context, copyCE32(cond->ce32), errorCode); + int32_t suffixStart = cond->prefixLength() + 1; + dest.unsafeBackwardSet.addAll(cond->context.tempSubString(suffixStart)); + prevDestCond->next = destIndex; + } + } else { + // Just copy long CEs and Latin mini expansions (and other expected values) as is, + // assuming that the modifier would not modify them. + U_ASSERT(tag == Collation::LONG_PRIMARY_TAG || + tag == Collation::LONG_SECONDARY_TAG || + tag == Collation::LATIN_EXPANSION_TAG || + tag == Collation::HANGUL_TAG); + } + } + return ce32; + } + + const CollationDataBuilder &src; + CollationDataBuilder &dest; + const CollationDataBuilder::CEModifier &modifier; + int64_t modifiedCEs[Collation::MAX_EXPANSION_LENGTH]; + UErrorCode errorCode; +}; + +U_CDECL_BEGIN + +static UBool U_CALLCONV +enumRangeForCopy(const void *context, UChar32 start, UChar32 end, uint32_t value) { + return + value == Collation::UNASSIGNED_CE32 || value == Collation::FALLBACK_CE32 || + ((CopyHelper *)context)->copyRangeCE32(start, end, value); +} + +U_CDECL_END + +void +CollationDataBuilder::copyFrom(const CollationDataBuilder &src, const CEModifier &modifier, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + if(trie == nullptr || utrie2_isFrozen(trie)) { + errorCode = U_INVALID_STATE_ERROR; + return; + } + CopyHelper helper(src, *this, modifier, errorCode); + utrie2_enum(src.trie, nullptr, enumRangeForCopy, &helper); + errorCode = helper.errorCode; + // Update the contextChars and the unsafeBackwardSet while copying, + // in case a character had conditional mappings in the source builder + // and they were removed later. + modified |= src.modified; +} + +void +CollationDataBuilder::optimize(const UnicodeSet &set, UErrorCode &errorCode) { + if(U_FAILURE(errorCode) || set.isEmpty()) { return; } + UnicodeSetIterator iter(set); + while(iter.next() && !iter.isString()) { + UChar32 c = iter.getCodepoint(); + uint32_t ce32 = utrie2_get32(trie, c); + if(ce32 == Collation::FALLBACK_CE32) { + ce32 = base->getFinalCE32(base->getCE32(c)); + ce32 = copyFromBaseCE32(c, ce32, true, errorCode); + utrie2_set32(trie, c, ce32, &errorCode); + } + } + modified = true; +} + +void +CollationDataBuilder::suppressContractions(const UnicodeSet &set, UErrorCode &errorCode) { + if(U_FAILURE(errorCode) || set.isEmpty()) { return; } + UnicodeSetIterator iter(set); + while(iter.next() && !iter.isString()) { + UChar32 c = iter.getCodepoint(); + uint32_t ce32 = utrie2_get32(trie, c); + if(ce32 == Collation::FALLBACK_CE32) { + ce32 = base->getFinalCE32(base->getCE32(c)); + if(Collation::ce32HasContext(ce32)) { + ce32 = copyFromBaseCE32(c, ce32, false /* without context */, errorCode); + utrie2_set32(trie, c, ce32, &errorCode); + } + } else if(isBuilderContextCE32(ce32)) { + ce32 = getConditionalCE32ForCE32(ce32)->ce32; + // Simply abandon the list of ConditionalCE32. + // The caller will copy this builder in the end, + // eliminating unreachable data. + utrie2_set32(trie, c, ce32, &errorCode); + contextChars.remove(c); + } + } + modified = true; +} + +UBool +CollationDataBuilder::getJamoCE32s(uint32_t jamoCE32s[], UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return false; } + UBool anyJamoAssigned = base == nullptr; // always set jamoCE32s in the base data + UBool needToCopyFromBase = false; + for(int32_t j = 0; j < CollationData::JAMO_CE32S_LENGTH; ++j) { // Count across Jamo types. + UChar32 jamo = jamoCpFromIndex(j); + UBool fromBase = false; + uint32_t ce32 = utrie2_get32(trie, jamo); + anyJamoAssigned |= Collation::isAssignedCE32(ce32); + // TODO: Try to prevent [optimize [Jamo]] from counting as anyJamoAssigned. + // (As of CLDR 24 [2013] the Korean tailoring does not optimize conjoining Jamo.) + if(ce32 == Collation::FALLBACK_CE32) { + fromBase = true; + ce32 = base->getCE32(jamo); + } + if(Collation::isSpecialCE32(ce32)) { + switch(Collation::tagFromCE32(ce32)) { + case Collation::LONG_PRIMARY_TAG: + case Collation::LONG_SECONDARY_TAG: + case Collation::LATIN_EXPANSION_TAG: + // Copy the ce32 as-is. + break; + case Collation::EXPANSION32_TAG: + case Collation::EXPANSION_TAG: + case Collation::PREFIX_TAG: + case Collation::CONTRACTION_TAG: + if(fromBase) { + // Defer copying until we know if anyJamoAssigned. + ce32 = Collation::FALLBACK_CE32; + needToCopyFromBase = true; + } + break; + case Collation::IMPLICIT_TAG: + // An unassigned Jamo should only occur in tests with incomplete bases. + U_ASSERT(fromBase); + ce32 = Collation::FALLBACK_CE32; + needToCopyFromBase = true; + break; + case Collation::OFFSET_TAG: + ce32 = getCE32FromOffsetCE32(fromBase, jamo, ce32); + break; + case Collation::FALLBACK_TAG: + case Collation::RESERVED_TAG_3: + case Collation::BUILDER_DATA_TAG: + case Collation::DIGIT_TAG: + case Collation::U0000_TAG: + case Collation::HANGUL_TAG: + case Collation::LEAD_SURROGATE_TAG: + errorCode = U_INTERNAL_PROGRAM_ERROR; + return false; + } + } + jamoCE32s[j] = ce32; + } + if(anyJamoAssigned && needToCopyFromBase) { + for(int32_t j = 0; j < CollationData::JAMO_CE32S_LENGTH; ++j) { + if(jamoCE32s[j] == Collation::FALLBACK_CE32) { + UChar32 jamo = jamoCpFromIndex(j); + jamoCE32s[j] = copyFromBaseCE32(jamo, base->getCE32(jamo), + /*withContext=*/ true, errorCode); + } + } + } + return anyJamoAssigned && U_SUCCESS(errorCode); +} + +void +CollationDataBuilder::setDigitTags(UErrorCode &errorCode) { + UnicodeSet digits(UNICODE_STRING_SIMPLE("[:Nd:]"), errorCode); + if(U_FAILURE(errorCode)) { return; } + UnicodeSetIterator iter(digits); + while(iter.next()) { + U_ASSERT(!iter.isString()); + UChar32 c = iter.getCodepoint(); + uint32_t ce32 = utrie2_get32(trie, c); + if(ce32 != Collation::FALLBACK_CE32 && ce32 != Collation::UNASSIGNED_CE32) { + int32_t index = addCE32(ce32, errorCode); + if(U_FAILURE(errorCode)) { return; } + if(index > Collation::MAX_INDEX) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return; + } + ce32 = Collation::makeCE32FromTagIndexAndLength( + Collation::DIGIT_TAG, index, u_charDigitValue(c)); + utrie2_set32(trie, c, ce32, &errorCode); + } + } +} + +U_CDECL_BEGIN + +static UBool U_CALLCONV +enumRangeLeadValue(const void *context, UChar32 /*start*/, UChar32 /*end*/, uint32_t value) { + int32_t *pValue = (int32_t *)context; + if(value == Collation::UNASSIGNED_CE32) { + value = Collation::LEAD_ALL_UNASSIGNED; + } else if(value == Collation::FALLBACK_CE32) { + value = Collation::LEAD_ALL_FALLBACK; + } else { + *pValue = Collation::LEAD_MIXED; + return false; + } + if(*pValue < 0) { + *pValue = (int32_t)value; + } else if(*pValue != (int32_t)value) { + *pValue = Collation::LEAD_MIXED; + return false; + } + return true; +} + +U_CDECL_END + +void +CollationDataBuilder::setLeadSurrogates(UErrorCode &errorCode) { + for(char16_t lead = 0xd800; lead < 0xdc00; ++lead) { + int32_t value = -1; + utrie2_enumForLeadSurrogate(trie, lead, nullptr, enumRangeLeadValue, &value); + utrie2_set32ForLeadSurrogateCodeUnit( + trie, lead, + Collation::makeCE32FromTagAndIndex(Collation::LEAD_SURROGATE_TAG, 0) | (uint32_t)value, + &errorCode); + } +} + +void +CollationDataBuilder::build(CollationData &data, UErrorCode &errorCode) { + buildMappings(data, errorCode); + if(base != nullptr) { + data.numericPrimary = base->numericPrimary; + data.compressibleBytes = base->compressibleBytes; + data.numScripts = base->numScripts; + data.scriptsIndex = base->scriptsIndex; + data.scriptStarts = base->scriptStarts; + data.scriptStartsLength = base->scriptStartsLength; + } + buildFastLatinTable(data, errorCode); +} + +void +CollationDataBuilder::buildMappings(CollationData &data, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + if(trie == nullptr || utrie2_isFrozen(trie)) { + errorCode = U_INVALID_STATE_ERROR; + return; + } + + buildContexts(errorCode); + + uint32_t jamoCE32s[CollationData::JAMO_CE32S_LENGTH]; + int32_t jamoIndex = -1; + if(getJamoCE32s(jamoCE32s, errorCode)) { + jamoIndex = ce32s.size(); + for(int32_t i = 0; i < CollationData::JAMO_CE32S_LENGTH; ++i) { + ce32s.addElement((int32_t)jamoCE32s[i], errorCode); + } + // Small optimization: Use a bit in the Hangul ce32 + // to indicate that none of the Jamo CE32s are isSpecialCE32() + // (as it should be in the root collator). + // It allows CollationIterator to avoid recursive function calls and per-Jamo tests. + // In order to still have good trie compression and keep this code simple, + // we only set this flag if a whole block of 588 Hangul syllables starting with + // a common leading consonant (Jamo L) has this property. + UBool isAnyJamoVTSpecial = false; + for(int32_t i = Hangul::JAMO_L_COUNT; i < CollationData::JAMO_CE32S_LENGTH; ++i) { + if(Collation::isSpecialCE32(jamoCE32s[i])) { + isAnyJamoVTSpecial = true; + break; + } + } + uint32_t hangulCE32 = Collation::makeCE32FromTagAndIndex(Collation::HANGUL_TAG, 0); + UChar32 c = Hangul::HANGUL_BASE; + for(int32_t i = 0; i < Hangul::JAMO_L_COUNT; ++i) { // iterate over the Jamo L + uint32_t ce32 = hangulCE32; + if(!isAnyJamoVTSpecial && !Collation::isSpecialCE32(jamoCE32s[i])) { + ce32 |= Collation::HANGUL_NO_SPECIAL_JAMO; + } + UChar32 limit = c + Hangul::JAMO_VT_COUNT; + utrie2_setRange32(trie, c, limit - 1, ce32, true, &errorCode); + c = limit; + } + } else { + // Copy the Hangul CE32s from the base in blocks per Jamo L, + // assuming that HANGUL_NO_SPECIAL_JAMO is set or not set for whole blocks. + for(UChar32 c = Hangul::HANGUL_BASE; c < Hangul::HANGUL_LIMIT;) { + uint32_t ce32 = base->getCE32(c); + U_ASSERT(Collation::hasCE32Tag(ce32, Collation::HANGUL_TAG)); + UChar32 limit = c + Hangul::JAMO_VT_COUNT; + utrie2_setRange32(trie, c, limit - 1, ce32, true, &errorCode); + c = limit; + } + } + + setDigitTags(errorCode); + setLeadSurrogates(errorCode); + + if (!icu4xMode) { + // For U+0000, move its normal ce32 into CE32s[0] and set U0000_TAG. + ce32s.setElementAt((int32_t)utrie2_get32(trie, 0), 0); + utrie2_set32(trie, 0, Collation::makeCE32FromTagAndIndex(Collation::U0000_TAG, 0), &errorCode); + } + + utrie2_freeze(trie, UTRIE2_32_VALUE_BITS, &errorCode); + if(U_FAILURE(errorCode)) { return; } + + // Mark each lead surrogate as "unsafe" + // if any of its 1024 associated supplementary code points is "unsafe". + UChar32 c = 0x10000; + for(char16_t lead = 0xd800; lead < 0xdc00; ++lead, c += 0x400) { + if(unsafeBackwardSet.containsSome(c, c + 0x3ff)) { + unsafeBackwardSet.add(lead); + } + } + unsafeBackwardSet.freeze(); + + data.trie = trie; + data.ce32s = reinterpret_cast(ce32s.getBuffer()); + data.ces = ce64s.getBuffer(); + data.contexts = contexts.getBuffer(); + + data.ce32sLength = ce32s.size(); + data.cesLength = ce64s.size(); + data.contextsLength = contexts.length(); + + data.base = base; + if(jamoIndex >= 0) { + data.jamoCE32s = data.ce32s + jamoIndex; + } else { + data.jamoCE32s = base->jamoCE32s; + } + data.unsafeBackwardSet = &unsafeBackwardSet; +} + +void +CollationDataBuilder::clearContexts() { + contexts.remove(); + // Incrementing the contexts build "era" invalidates all of the builtCE32 + // from before this clearContexts() call. + // Simpler than finding and resetting all of those fields. + ++contextsEra; +} + +void +CollationDataBuilder::buildContexts(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + // Ignore abandoned lists and the cached builtCE32, + // and build all contexts from scratch. + clearContexts(); + UnicodeSetIterator iter(contextChars); + while(U_SUCCESS(errorCode) && iter.next()) { + U_ASSERT(!iter.isString()); + UChar32 c = iter.getCodepoint(); + uint32_t ce32 = utrie2_get32(trie, c); + if(!isBuilderContextCE32(ce32)) { + // Impossible: No context data for c in contextChars. + errorCode = U_INTERNAL_PROGRAM_ERROR; + return; + } + ConditionalCE32 *cond = getConditionalCE32ForCE32(ce32); + ce32 = buildContext(cond, errorCode); + utrie2_set32(trie, c, ce32, &errorCode); + } +} + +uint32_t +CollationDataBuilder::buildContext(ConditionalCE32 *head, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + // The list head must have no context. + U_ASSERT(!head->hasContext()); + // The list head must be followed by one or more nodes that all do have context. + U_ASSERT(head->next >= 0); + UCharsTrieBuilder prefixBuilder(errorCode); + UCharsTrieBuilder contractionBuilder(errorCode); + // This outer loop goes from each prefix to the next. + // For each prefix it finds the one or more same-prefix entries (firstCond..lastCond). + // If there are multiple suffixes for the same prefix, + // then an inner loop builds a contraction trie for them. + for(ConditionalCE32 *cond = head;; cond = getConditionalCE32(cond->next)) { + if(U_FAILURE(errorCode)) { return 0; } // early out for memory allocation errors + // After the list head, the prefix or suffix can be empty, but not both. + U_ASSERT(cond == head || cond->hasContext()); + int32_t prefixLength = cond->prefixLength(); + UnicodeString prefix(cond->context, 0, prefixLength + 1); + // Collect all contraction suffixes for one prefix. + ConditionalCE32 *firstCond = cond; + ConditionalCE32 *lastCond; + do { + lastCond = cond; + // Clear the defaultCE32 fields as we go. + // They are left over from building a previous version of this list of contexts. + // + // One of the code paths below may copy a preceding defaultCE32 + // into its emptySuffixCE32. + // If a new suffix has been inserted before what used to be + // the firstCond for its prefix, then that previous firstCond could still + // contain an outdated defaultCE32 from an earlier buildContext() and + // result in an incorrect emptySuffixCE32. + // So we reset all defaultCE32 before reading and setting new values. + cond->defaultCE32 = Collation::NO_CE32; + } while(cond->next >= 0 && + (cond = getConditionalCE32(cond->next))->context.startsWith(prefix)); + uint32_t ce32; + int32_t suffixStart = prefixLength + 1; // == prefix.length() + if(lastCond->context.length() == suffixStart) { + // One prefix without contraction suffix. + U_ASSERT(firstCond == lastCond); + ce32 = lastCond->ce32; + cond = lastCond; + } else { + // Build the contractions trie. + contractionBuilder.clear(); + // Entry for an empty suffix, to be stored before the trie. + uint32_t emptySuffixCE32 = 0; + uint32_t flags = 0; + if(firstCond->context.length() == suffixStart) { + // There is a mapping for the prefix and the single character c. (p|c) + // If no other suffix matches, then we return this value. + emptySuffixCE32 = firstCond->ce32; + cond = getConditionalCE32(firstCond->next); + } else { + // There is no mapping for the prefix and just the single character. + // (There is no p|c, only p|cd, p|ce etc.) + flags |= Collation::CONTRACT_SINGLE_CP_NO_MATCH; + // When the prefix matches but none of the prefix-specific suffixes, + // then we fall back to the mappings with the next-longest prefix, + // and ultimately to mappings with no prefix. + // Each fallback might be another set of contractions. + // For example, if there are mappings for ch, p|cd, p|ce, but not for p|c, + // then in text "pch" we find the ch contraction. + for(cond = head;; cond = getConditionalCE32(cond->next)) { + int32_t length = cond->prefixLength(); + if(length == prefixLength) { break; } + if(cond->defaultCE32 != Collation::NO_CE32 && + (length==0 || prefix.endsWith(cond->context, 1, length))) { + emptySuffixCE32 = cond->defaultCE32; + } + } + cond = firstCond; + } + // Optimization: Set a flag when + // the first character of every contraction suffix has lccc!=0. + // Short-circuits contraction matching when a normal letter follows. + flags |= Collation::CONTRACT_NEXT_CCC; + // Add all of the non-empty suffixes into the contraction trie. + for(;;) { + UnicodeString suffix(cond->context, suffixStart); + uint16_t fcd16 = nfcImpl.getFCD16(suffix.char32At(0)); + if(fcd16 <= 0xff) { + flags &= ~Collation::CONTRACT_NEXT_CCC; + } + fcd16 = nfcImpl.getFCD16(suffix.char32At(suffix.length() - 1)); + if(fcd16 > 0xff) { + // The last suffix character has lccc!=0, allowing for discontiguous contractions. + flags |= Collation::CONTRACT_TRAILING_CCC; + } + if (icu4xMode && (flags & Collation::CONTRACT_HAS_STARTER) == 0) { + for (int32_t i = 0; i < suffix.length();) { + UChar32 c = suffix.char32At(i); + if (!u_getCombiningClass(c)) { + flags |= Collation::CONTRACT_HAS_STARTER; + break; + } + if (c > 0xFFFF) { + i += 2; + } else { + ++i; + } + } + } + contractionBuilder.add(suffix, (int32_t)cond->ce32, errorCode); + if(cond == lastCond) { break; } + cond = getConditionalCE32(cond->next); + } + int32_t index = addContextTrie(emptySuffixCE32, contractionBuilder, errorCode); + if(U_FAILURE(errorCode)) { return 0; } + if(index > Collation::MAX_INDEX) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + ce32 = Collation::makeCE32FromTagAndIndex(Collation::CONTRACTION_TAG, index) | flags; + } + U_ASSERT(cond == lastCond); + firstCond->defaultCE32 = ce32; + if(prefixLength == 0) { + if(cond->next < 0) { + // No non-empty prefixes, only contractions. + return ce32; + } + } else { + prefix.remove(0, 1); // Remove the length unit. + prefix.reverse(); + prefixBuilder.add(prefix, (int32_t)ce32, errorCode); + if(cond->next < 0) { break; } + } + } + U_ASSERT(head->defaultCE32 != Collation::NO_CE32); + int32_t index = addContextTrie(head->defaultCE32, prefixBuilder, errorCode); + if(U_FAILURE(errorCode)) { return 0; } + if(index > Collation::MAX_INDEX) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + return Collation::makeCE32FromTagAndIndex(Collation::PREFIX_TAG, index); +} + +int32_t +CollationDataBuilder::addContextTrie(uint32_t defaultCE32, UCharsTrieBuilder &trieBuilder, + UErrorCode &errorCode) { + UnicodeString context; + context.append((char16_t)(defaultCE32 >> 16)).append((char16_t)defaultCE32); + UnicodeString trieString; + context.append(trieBuilder.buildUnicodeString(USTRINGTRIE_BUILD_SMALL, trieString, errorCode)); + if(U_FAILURE(errorCode)) { return -1; } + int32_t index = contexts.indexOf(context); + if(index < 0) { + index = contexts.length(); + contexts.append(context); + } + return index; +} + +void +CollationDataBuilder::buildFastLatinTable(CollationData &data, UErrorCode &errorCode) { + if(U_FAILURE(errorCode) || !fastLatinEnabled) { return; } + + delete fastLatinBuilder; + fastLatinBuilder = new CollationFastLatinBuilder(errorCode); + if(fastLatinBuilder == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + if(fastLatinBuilder->forData(data, errorCode)) { + const uint16_t *table = fastLatinBuilder->getTable(); + int32_t length = fastLatinBuilder->lengthOfTable(); + if(base != nullptr && length == base->fastLatinTableLength && + uprv_memcmp(table, base->fastLatinTable, length * 2) == 0) { + // Same fast Latin table as in the base, use that one instead. + delete fastLatinBuilder; + fastLatinBuilder = nullptr; + table = base->fastLatinTable; + } + data.fastLatinTable = table; + data.fastLatinTableLength = length; + } else { + delete fastLatinBuilder; + fastLatinBuilder = nullptr; + } +} + +int32_t +CollationDataBuilder::getCEs(const UnicodeString &s, int64_t ces[], int32_t cesLength) { + return getCEs(s, 0, ces, cesLength); +} + +int32_t +CollationDataBuilder::getCEs(const UnicodeString &prefix, const UnicodeString &s, + int64_t ces[], int32_t cesLength) { + int32_t prefixLength = prefix.length(); + if(prefixLength == 0) { + return getCEs(s, 0, ces, cesLength); + } else { + return getCEs(prefix + s, prefixLength, ces, cesLength); + } +} + +int32_t +CollationDataBuilder::getCEs(const UnicodeString &s, int32_t start, + int64_t ces[], int32_t cesLength) { + if(collIter == nullptr) { + collIter = new DataBuilderCollationIterator(*this); + if(collIter == nullptr) { return 0; } + } + return collIter->fetchCEs(s, start, ces, cesLength); +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationdatabuilder.h b/deps/icu-small/source/i18n/collationdatabuilder.h index cbbd8f264b50ce..5e9c77988b3738 100644 --- a/deps/icu-small/source/i18n/collationdatabuilder.h +++ b/deps/icu-small/source/i18n/collationdatabuilder.h @@ -1,269 +1,269 @@ -// © 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. -******************************************************************************* -* collationdatabuilder.h -* -* created on: 2012apr01 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONDATABUILDER_H__ -#define __COLLATIONDATABUILDER_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/uniset.h" -#include "unicode/unistr.h" -#include "unicode/uversion.h" -#include "collation.h" -#include "collationdata.h" -#include "collationsettings.h" -#include "normalizer2impl.h" -#include "utrie2.h" -#include "uvectr32.h" -#include "uvectr64.h" -#include "uvector.h" - -U_NAMESPACE_BEGIN - -struct ConditionalCE32; - -class CollationFastLatinBuilder; -class CopyHelper; -class DataBuilderCollationIterator; -class UCharsTrieBuilder; - -/** - * Low-level CollationData builder. - * Takes (character, CE) pairs and builds them into runtime data structures. - * Supports characters with context prefixes and contraction suffixes. - */ -class U_I18N_API CollationDataBuilder : public UObject { -public: - /** - * Collation element modifier. Interface class for a modifier - * that changes a tailoring builder's temporary CEs to final CEs. - * Called for every non-special CE32 and every expansion CE. - */ - class CEModifier : public UObject { - public: - virtual ~CEModifier(); - /** Returns a new CE to replace the non-special input CE32, or else Collation::NO_CE. */ - virtual int64_t modifyCE32(uint32_t ce32) const = 0; - /** Returns a new CE to replace the input CE, or else Collation::NO_CE. */ - virtual int64_t modifyCE(int64_t ce) const = 0; - }; - - CollationDataBuilder(UBool icu4xMode, UErrorCode &errorCode); - - virtual ~CollationDataBuilder(); - - void initForTailoring(const CollationData *b, UErrorCode &errorCode); - - virtual UBool isCompressibleLeadByte(uint32_t b) const; - - inline UBool isCompressiblePrimary(uint32_t p) const { - return isCompressibleLeadByte(p >> 24); - } - - /** - * @return true if this builder has mappings (e.g., add() has been called) - */ - UBool hasMappings() const { return modified; } - - /** - * @return true if c has CEs in this builder - */ - UBool isAssigned(UChar32 c) const; - - /** - * @return the three-byte primary if c maps to a single such CE and has no context data, - * otherwise returns 0. - */ - uint32_t getLongPrimaryIfSingleCE(UChar32 c) const; - - /** - * @return the single CE for c. - * Sets an error code if c does not have a single CE. - */ - int64_t getSingleCE(UChar32 c, UErrorCode &errorCode) const; - - void add(const UnicodeString &prefix, const UnicodeString &s, - const int64_t ces[], int32_t cesLength, - UErrorCode &errorCode); - - /** - * Encodes the ces as either the returned ce32 by itself, - * or by storing an expansion, with the returned ce32 referring to that. - * - * add(p, s, ces, cesLength) = addCE32(p, s, encodeCEs(ces, cesLength)) - */ - virtual uint32_t encodeCEs(const int64_t ces[], int32_t cesLength, UErrorCode &errorCode); - void addCE32(const UnicodeString &prefix, const UnicodeString &s, - uint32_t ce32, UErrorCode &errorCode); - - /** - * Sets three-byte-primary CEs for a range of code points in code point order, - * if it is worth doing; otherwise no change is made. - * None of the code points in the range should have complex mappings so far - * (expansions/contractions/prefixes). - * @param start first code point - * @param end last code point (inclusive) - * @param primary primary weight for 'start' - * @param step per-code point primary-weight increment - * @param errorCode ICU in/out error code - * @return true if an OFFSET_TAG range was used for start..end - */ - UBool maybeSetPrimaryRange(UChar32 start, UChar32 end, - uint32_t primary, int32_t step, - UErrorCode &errorCode); - - /** - * Sets three-byte-primary CEs for a range of code points in code point order. - * Sets range values if that is worth doing, or else individual values. - * None of the code points in the range should have complex mappings so far - * (expansions/contractions/prefixes). - * @param start first code point - * @param end last code point (inclusive) - * @param primary primary weight for 'start' - * @param step per-code point primary-weight increment - * @param errorCode ICU in/out error code - * @return the next primary after 'end': start primary incremented by ((end-start)+1)*step - */ - uint32_t setPrimaryRangeAndReturnNext(UChar32 start, UChar32 end, - uint32_t primary, int32_t step, - UErrorCode &errorCode); - - /** - * Copies all mappings from the src builder, with modifications. - * This builder here must not be built yet, and should be empty. - */ - void copyFrom(const CollationDataBuilder &src, const CEModifier &modifier, - UErrorCode &errorCode); - - void optimize(const UnicodeSet &set, UErrorCode &errorCode); - void suppressContractions(const UnicodeSet &set, UErrorCode &errorCode); - - void enableFastLatin() { fastLatinEnabled = true; } - virtual void build(CollationData &data, UErrorCode &errorCode); - - /** - * Looks up CEs for s and appends them to the ces array. - * Does not handle normalization: s should be in FCD form. - * - * Does not write completely ignorable CEs. - * Does not write beyond Collation::MAX_EXPANSION_LENGTH. - * - * @return incremented cesLength - */ - int32_t getCEs(const UnicodeString &s, int64_t ces[], int32_t cesLength); - int32_t getCEs(const UnicodeString &prefix, const UnicodeString &s, - int64_t ces[], int32_t cesLength); - -protected: - friend class CopyHelper; - friend class DataBuilderCollationIterator; - - uint32_t getCE32FromOffsetCE32(UBool fromBase, UChar32 c, uint32_t ce32) const; - - int32_t addCE(int64_t ce, UErrorCode &errorCode); - int32_t addCE32(uint32_t ce32, UErrorCode &errorCode); - int32_t addConditionalCE32(const UnicodeString &context, uint32_t ce32, UErrorCode &errorCode); - - inline ConditionalCE32 *getConditionalCE32(int32_t index) const { - return static_cast(conditionalCE32s[index]); - } - inline ConditionalCE32 *getConditionalCE32ForCE32(uint32_t ce32) const { - return getConditionalCE32(Collation::indexFromCE32(ce32)); - } - - static uint32_t makeBuilderContextCE32(int32_t index) { - return Collation::makeCE32FromTagAndIndex(Collation::BUILDER_DATA_TAG, index); - } - static inline UBool isBuilderContextCE32(uint32_t ce32) { - return Collation::hasCE32Tag(ce32, Collation::BUILDER_DATA_TAG); - } - - static uint32_t encodeOneCEAsCE32(int64_t ce); - uint32_t encodeOneCE(int64_t ce, UErrorCode &errorCode); - uint32_t encodeExpansion(const int64_t ces[], int32_t length, UErrorCode &errorCode); - uint32_t encodeExpansion32(const int32_t newCE32s[], int32_t length, UErrorCode &errorCode); - - uint32_t copyFromBaseCE32(UChar32 c, uint32_t ce32, UBool withContext, UErrorCode &errorCode); - /** - * Copies base contractions to a list of ConditionalCE32. - * Sets cond->next to the index of the first new item - * and returns the index of the last new item. - */ - int32_t copyContractionsFromBaseCE32(UnicodeString &context, UChar32 c, uint32_t ce32, - ConditionalCE32 *cond, UErrorCode &errorCode); - - UBool getJamoCE32s(uint32_t jamoCE32s[], UErrorCode &errorCode); - void setDigitTags(UErrorCode &errorCode); - void setLeadSurrogates(UErrorCode &errorCode); - - void buildMappings(CollationData &data, UErrorCode &errorCode); - - void clearContexts(); - void buildContexts(UErrorCode &errorCode); - uint32_t buildContext(ConditionalCE32 *head, UErrorCode &errorCode); - int32_t addContextTrie(uint32_t defaultCE32, UCharsTrieBuilder &trieBuilder, - UErrorCode &errorCode); - - void buildFastLatinTable(CollationData &data, UErrorCode &errorCode); - - int32_t getCEs(const UnicodeString &s, int32_t start, int64_t ces[], int32_t cesLength); - - static UChar32 jamoCpFromIndex(int32_t i) { - // 0 <= i < CollationData::JAMO_CE32S_LENGTH = 19 + 21 + 27 - if(i < Hangul::JAMO_L_COUNT) { return Hangul::JAMO_L_BASE + i; } - i -= Hangul::JAMO_L_COUNT; - if(i < Hangul::JAMO_V_COUNT) { return Hangul::JAMO_V_BASE + i; } - i -= Hangul::JAMO_V_COUNT; - // i < 27 - return Hangul::JAMO_T_BASE + 1 + i; - } - - /** @see Collation::BUILDER_DATA_TAG */ - static const uint32_t IS_BUILDER_JAMO_CE32 = 0x100; - - const Normalizer2Impl &nfcImpl; - const CollationData *base; - const CollationSettings *baseSettings; - UTrie2 *trie; - UVector32 ce32s; - UVector64 ce64s; - UVector conditionalCE32s; // vector of ConditionalCE32 - // Characters that have context (prefixes or contraction suffixes). - UnicodeSet contextChars; - // Serialized UCharsTrie structures for finalized contexts. - UnicodeString contexts; -private: - /** - * The "era" of building intermediate contexts. - * When the array of cached, temporary contexts overflows, then clearContexts() - * removes them all and invalidates the builtCE32 that used to point to built tries. - * See ConditionalCE32::era. - */ - int32_t contextsEra = 0; -protected: - UnicodeSet unsafeBackwardSet; - UBool modified; - UBool icu4xMode; - - UBool fastLatinEnabled; - CollationFastLatinBuilder *fastLatinBuilder; - - DataBuilderCollationIterator *collIter; -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONDATABUILDER_H__ +// © 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. +******************************************************************************* +* collationdatabuilder.h +* +* created on: 2012apr01 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONDATABUILDER_H__ +#define __COLLATIONDATABUILDER_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/uniset.h" +#include "unicode/unistr.h" +#include "unicode/uversion.h" +#include "collation.h" +#include "collationdata.h" +#include "collationsettings.h" +#include "normalizer2impl.h" +#include "utrie2.h" +#include "uvectr32.h" +#include "uvectr64.h" +#include "uvector.h" + +U_NAMESPACE_BEGIN + +struct ConditionalCE32; + +class CollationFastLatinBuilder; +class CopyHelper; +class DataBuilderCollationIterator; +class UCharsTrieBuilder; + +/** + * Low-level CollationData builder. + * Takes (character, CE) pairs and builds them into runtime data structures. + * Supports characters with context prefixes and contraction suffixes. + */ +class U_I18N_API CollationDataBuilder : public UObject { +public: + /** + * Collation element modifier. Interface class for a modifier + * that changes a tailoring builder's temporary CEs to final CEs. + * Called for every non-special CE32 and every expansion CE. + */ + class CEModifier : public UObject { + public: + virtual ~CEModifier(); + /** Returns a new CE to replace the non-special input CE32, or else Collation::NO_CE. */ + virtual int64_t modifyCE32(uint32_t ce32) const = 0; + /** Returns a new CE to replace the input CE, or else Collation::NO_CE. */ + virtual int64_t modifyCE(int64_t ce) const = 0; + }; + + CollationDataBuilder(UBool icu4xMode, UErrorCode &errorCode); + + virtual ~CollationDataBuilder(); + + void initForTailoring(const CollationData *b, UErrorCode &errorCode); + + virtual UBool isCompressibleLeadByte(uint32_t b) const; + + inline UBool isCompressiblePrimary(uint32_t p) const { + return isCompressibleLeadByte(p >> 24); + } + + /** + * @return true if this builder has mappings (e.g., add() has been called) + */ + UBool hasMappings() const { return modified; } + + /** + * @return true if c has CEs in this builder + */ + UBool isAssigned(UChar32 c) const; + + /** + * @return the three-byte primary if c maps to a single such CE and has no context data, + * otherwise returns 0. + */ + uint32_t getLongPrimaryIfSingleCE(UChar32 c) const; + + /** + * @return the single CE for c. + * Sets an error code if c does not have a single CE. + */ + int64_t getSingleCE(UChar32 c, UErrorCode &errorCode) const; + + void add(const UnicodeString &prefix, const UnicodeString &s, + const int64_t ces[], int32_t cesLength, + UErrorCode &errorCode); + + /** + * Encodes the ces as either the returned ce32 by itself, + * or by storing an expansion, with the returned ce32 referring to that. + * + * add(p, s, ces, cesLength) = addCE32(p, s, encodeCEs(ces, cesLength)) + */ + virtual uint32_t encodeCEs(const int64_t ces[], int32_t cesLength, UErrorCode &errorCode); + void addCE32(const UnicodeString &prefix, const UnicodeString &s, + uint32_t ce32, UErrorCode &errorCode); + + /** + * Sets three-byte-primary CEs for a range of code points in code point order, + * if it is worth doing; otherwise no change is made. + * None of the code points in the range should have complex mappings so far + * (expansions/contractions/prefixes). + * @param start first code point + * @param end last code point (inclusive) + * @param primary primary weight for 'start' + * @param step per-code point primary-weight increment + * @param errorCode ICU in/out error code + * @return true if an OFFSET_TAG range was used for start..end + */ + UBool maybeSetPrimaryRange(UChar32 start, UChar32 end, + uint32_t primary, int32_t step, + UErrorCode &errorCode); + + /** + * Sets three-byte-primary CEs for a range of code points in code point order. + * Sets range values if that is worth doing, or else individual values. + * None of the code points in the range should have complex mappings so far + * (expansions/contractions/prefixes). + * @param start first code point + * @param end last code point (inclusive) + * @param primary primary weight for 'start' + * @param step per-code point primary-weight increment + * @param errorCode ICU in/out error code + * @return the next primary after 'end': start primary incremented by ((end-start)+1)*step + */ + uint32_t setPrimaryRangeAndReturnNext(UChar32 start, UChar32 end, + uint32_t primary, int32_t step, + UErrorCode &errorCode); + + /** + * Copies all mappings from the src builder, with modifications. + * This builder here must not be built yet, and should be empty. + */ + void copyFrom(const CollationDataBuilder &src, const CEModifier &modifier, + UErrorCode &errorCode); + + void optimize(const UnicodeSet &set, UErrorCode &errorCode); + void suppressContractions(const UnicodeSet &set, UErrorCode &errorCode); + + void enableFastLatin() { fastLatinEnabled = true; } + virtual void build(CollationData &data, UErrorCode &errorCode); + + /** + * Looks up CEs for s and appends them to the ces array. + * Does not handle normalization: s should be in FCD form. + * + * Does not write completely ignorable CEs. + * Does not write beyond Collation::MAX_EXPANSION_LENGTH. + * + * @return incremented cesLength + */ + int32_t getCEs(const UnicodeString &s, int64_t ces[], int32_t cesLength); + int32_t getCEs(const UnicodeString &prefix, const UnicodeString &s, + int64_t ces[], int32_t cesLength); + +protected: + friend class CopyHelper; + friend class DataBuilderCollationIterator; + + uint32_t getCE32FromOffsetCE32(UBool fromBase, UChar32 c, uint32_t ce32) const; + + int32_t addCE(int64_t ce, UErrorCode &errorCode); + int32_t addCE32(uint32_t ce32, UErrorCode &errorCode); + int32_t addConditionalCE32(const UnicodeString &context, uint32_t ce32, UErrorCode &errorCode); + + inline ConditionalCE32 *getConditionalCE32(int32_t index) const { + return static_cast(conditionalCE32s[index]); + } + inline ConditionalCE32 *getConditionalCE32ForCE32(uint32_t ce32) const { + return getConditionalCE32(Collation::indexFromCE32(ce32)); + } + + static uint32_t makeBuilderContextCE32(int32_t index) { + return Collation::makeCE32FromTagAndIndex(Collation::BUILDER_DATA_TAG, index); + } + static inline UBool isBuilderContextCE32(uint32_t ce32) { + return Collation::hasCE32Tag(ce32, Collation::BUILDER_DATA_TAG); + } + + static uint32_t encodeOneCEAsCE32(int64_t ce); + uint32_t encodeOneCE(int64_t ce, UErrorCode &errorCode); + uint32_t encodeExpansion(const int64_t ces[], int32_t length, UErrorCode &errorCode); + uint32_t encodeExpansion32(const int32_t newCE32s[], int32_t length, UErrorCode &errorCode); + + uint32_t copyFromBaseCE32(UChar32 c, uint32_t ce32, UBool withContext, UErrorCode &errorCode); + /** + * Copies base contractions to a list of ConditionalCE32. + * Sets cond->next to the index of the first new item + * and returns the index of the last new item. + */ + int32_t copyContractionsFromBaseCE32(UnicodeString &context, UChar32 c, uint32_t ce32, + ConditionalCE32 *cond, UErrorCode &errorCode); + + UBool getJamoCE32s(uint32_t jamoCE32s[], UErrorCode &errorCode); + void setDigitTags(UErrorCode &errorCode); + void setLeadSurrogates(UErrorCode &errorCode); + + void buildMappings(CollationData &data, UErrorCode &errorCode); + + void clearContexts(); + void buildContexts(UErrorCode &errorCode); + uint32_t buildContext(ConditionalCE32 *head, UErrorCode &errorCode); + int32_t addContextTrie(uint32_t defaultCE32, UCharsTrieBuilder &trieBuilder, + UErrorCode &errorCode); + + void buildFastLatinTable(CollationData &data, UErrorCode &errorCode); + + int32_t getCEs(const UnicodeString &s, int32_t start, int64_t ces[], int32_t cesLength); + + static UChar32 jamoCpFromIndex(int32_t i) { + // 0 <= i < CollationData::JAMO_CE32S_LENGTH = 19 + 21 + 27 + if(i < Hangul::JAMO_L_COUNT) { return Hangul::JAMO_L_BASE + i; } + i -= Hangul::JAMO_L_COUNT; + if(i < Hangul::JAMO_V_COUNT) { return Hangul::JAMO_V_BASE + i; } + i -= Hangul::JAMO_V_COUNT; + // i < 27 + return Hangul::JAMO_T_BASE + 1 + i; + } + + /** @see Collation::BUILDER_DATA_TAG */ + static const uint32_t IS_BUILDER_JAMO_CE32 = 0x100; + + const Normalizer2Impl &nfcImpl; + const CollationData *base; + const CollationSettings *baseSettings; + UTrie2 *trie; + UVector32 ce32s; + UVector64 ce64s; + UVector conditionalCE32s; // vector of ConditionalCE32 + // Characters that have context (prefixes or contraction suffixes). + UnicodeSet contextChars; + // Serialized UCharsTrie structures for finalized contexts. + UnicodeString contexts; +private: + /** + * The "era" of building intermediate contexts. + * When the array of cached, temporary contexts overflows, then clearContexts() + * removes them all and invalidates the builtCE32 that used to point to built tries. + * See ConditionalCE32::era. + */ + int32_t contextsEra = 0; +protected: + UnicodeSet unsafeBackwardSet; + UBool modified; + UBool icu4xMode; + + UBool fastLatinEnabled; + CollationFastLatinBuilder *fastLatinBuilder; + + DataBuilderCollationIterator *collIter; +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONDATABUILDER_H__ diff --git a/deps/icu-small/source/i18n/collationdatareader.cpp b/deps/icu-small/source/i18n/collationdatareader.cpp index a96982cd94648a..bc67ebc1a1b64e 100644 --- a/deps/icu-small/source/i18n/collationdatareader.cpp +++ b/deps/icu-small/source/i18n/collationdatareader.cpp @@ -1,482 +1,482 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationdatareader.cpp -* -* created on: 2013feb07 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/ucol.h" -#include "unicode/udata.h" -#include "unicode/uscript.h" -#include "cmemory.h" -#include "collation.h" -#include "collationdata.h" -#include "collationdatareader.h" -#include "collationfastlatin.h" -#include "collationkeys.h" -#include "collationrootelements.h" -#include "collationsettings.h" -#include "collationtailoring.h" -#include "collunsafe.h" -#include "normalizer2impl.h" -#include "uassert.h" -#include "ucmndata.h" -#include "utrie2.h" - -U_NAMESPACE_BEGIN - -namespace { - -int32_t getIndex(const int32_t *indexes, int32_t length, int32_t i) { - return (i < length) ? indexes[i] : -1; -} - -} // namespace - -void -CollationDataReader::read(const CollationTailoring *base, const uint8_t *inBytes, int32_t inLength, - CollationTailoring &tailoring, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - if(base != NULL) { - if(inBytes == NULL || (0 <= inLength && inLength < 24)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - const DataHeader *header = reinterpret_cast(inBytes); - if(!(header->dataHeader.magic1 == 0xda && header->dataHeader.magic2 == 0x27 && - isAcceptable(tailoring.version, NULL, NULL, &header->info))) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - if(base->getUCAVersion() != tailoring.getUCAVersion()) { - errorCode = U_COLLATOR_VERSION_MISMATCH; - return; - } - int32_t headerLength = header->dataHeader.headerSize; - inBytes += headerLength; - if(inLength >= 0) { - inLength -= headerLength; - } - } - - if(inBytes == NULL || (0 <= inLength && inLength < 8)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - const int32_t *inIndexes = reinterpret_cast(inBytes); - int32_t indexesLength = inIndexes[IX_INDEXES_LENGTH]; - if(indexesLength < 2 || (0 <= inLength && inLength < indexesLength * 4)) { - errorCode = U_INVALID_FORMAT_ERROR; // Not enough indexes. - return; - } - - // Assume that the tailoring data is in initial state, - // with NULL pointers and 0 lengths. - - // Set pointers to non-empty data parts. - // Do this in order of their byte offsets. (Should help porting to Java.) - - int32_t index; // one of the indexes[] slots - int32_t offset; // byte offset for the index part - int32_t length; // number of bytes in the index part - - if(indexesLength > IX_TOTAL_SIZE) { - length = inIndexes[IX_TOTAL_SIZE]; - } else if(indexesLength > IX_REORDER_CODES_OFFSET) { - length = inIndexes[indexesLength - 1]; - } else { - length = 0; // only indexes, and inLength was already checked for them - } - if(0 <= inLength && inLength < length) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - - const CollationData *baseData = base == NULL ? NULL : base->data; - const int32_t *reorderCodes = NULL; - int32_t reorderCodesLength = 0; - const uint32_t *reorderRanges = NULL; - int32_t reorderRangesLength = 0; - index = IX_REORDER_CODES_OFFSET; - offset = getIndex(inIndexes, indexesLength, index); - length = getIndex(inIndexes, indexesLength, index + 1) - offset; - if(length >= 4) { - if(baseData == NULL) { - // We assume for collation settings that - // the base data does not have a reordering. - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - reorderCodes = reinterpret_cast(inBytes + offset); - reorderCodesLength = length / 4; - - // The reorderRanges (if any) are the trailing reorderCodes entries. - // Split the array at the boundary. - // Script or reorder codes do not exceed 16-bit values. - // Range limits are stored in the upper 16 bits, and are never 0. - while(reorderRangesLength < reorderCodesLength && - (reorderCodes[reorderCodesLength - reorderRangesLength - 1] & 0xffff0000) != 0) { - ++reorderRangesLength; - } - U_ASSERT(reorderRangesLength < reorderCodesLength); - if(reorderRangesLength != 0) { - reorderCodesLength -= reorderRangesLength; - reorderRanges = reinterpret_cast(reorderCodes + reorderCodesLength); - } - } - - // There should be a reorder table only if there are reorder codes. - // However, when there are reorder codes the reorder table may be omitted to reduce - // the data size. - const uint8_t *reorderTable = NULL; - index = IX_REORDER_TABLE_OFFSET; - offset = getIndex(inIndexes, indexesLength, index); - length = getIndex(inIndexes, indexesLength, index + 1) - offset; - if(length >= 256) { - if(reorderCodesLength == 0) { - errorCode = U_INVALID_FORMAT_ERROR; // Reordering table without reordering codes. - return; - } - reorderTable = inBytes + offset; - } else { - // If we have reorder codes, then build the reorderTable at the end, - // when the CollationData is otherwise complete. - } - - if(baseData != NULL && baseData->numericPrimary != (inIndexes[IX_OPTIONS] & 0xff000000)) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - CollationData *data = NULL; // Remains NULL if there are no mappings. - - index = IX_TRIE_OFFSET; - offset = getIndex(inIndexes, indexesLength, index); - length = getIndex(inIndexes, indexesLength, index + 1) - offset; - if(length >= 8) { - if(!tailoring.ensureOwnedData(errorCode)) { return; } - data = tailoring.ownedData; - data->base = baseData; - data->numericPrimary = inIndexes[IX_OPTIONS] & 0xff000000; - data->trie = tailoring.trie = utrie2_openFromSerialized( - UTRIE2_32_VALUE_BITS, inBytes + offset, length, NULL, - &errorCode); - if(U_FAILURE(errorCode)) { return; } - } else if(baseData != NULL) { - // Use the base data. Only the settings are tailored. - tailoring.data = baseData; - } else { - errorCode = U_INVALID_FORMAT_ERROR; // No mappings. - return; - } - - index = IX_CES_OFFSET; - offset = getIndex(inIndexes, indexesLength, index); - length = getIndex(inIndexes, indexesLength, index + 1) - offset; - if(length >= 8) { - if(data == NULL) { - errorCode = U_INVALID_FORMAT_ERROR; // Tailored ces without tailored trie. - return; - } - data->ces = reinterpret_cast(inBytes + offset); - data->cesLength = length / 8; - } - - index = IX_CE32S_OFFSET; - offset = getIndex(inIndexes, indexesLength, index); - length = getIndex(inIndexes, indexesLength, index + 1) - offset; - if(length >= 4) { - if(data == NULL) { - errorCode = U_INVALID_FORMAT_ERROR; // Tailored ce32s without tailored trie. - return; - } - data->ce32s = reinterpret_cast(inBytes + offset); - data->ce32sLength = length / 4; - } - - int32_t jamoCE32sStart = getIndex(inIndexes, indexesLength, IX_JAMO_CE32S_START); - if(jamoCE32sStart >= 0) { - if(data == NULL || data->ce32s == NULL) { - errorCode = U_INVALID_FORMAT_ERROR; // Index into non-existent ce32s[]. - return; - } - data->jamoCE32s = data->ce32s + jamoCE32sStart; - } else if(data == NULL) { - // Nothing to do. - } else if(baseData != NULL) { - data->jamoCE32s = baseData->jamoCE32s; - } else { - errorCode = U_INVALID_FORMAT_ERROR; // No Jamo CE32s for Hangul processing. - return; - } - - index = IX_ROOT_ELEMENTS_OFFSET; - offset = getIndex(inIndexes, indexesLength, index); - length = getIndex(inIndexes, indexesLength, index + 1) - offset; - if(length >= 4) { - length /= 4; - if(data == NULL || length <= CollationRootElements::IX_SEC_TER_BOUNDARIES) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - data->rootElements = reinterpret_cast(inBytes + offset); - data->rootElementsLength = length; - uint32_t commonSecTer = data->rootElements[CollationRootElements::IX_COMMON_SEC_AND_TER_CE]; - if(commonSecTer != Collation::COMMON_SEC_AND_TER_CE) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - uint32_t secTerBoundaries = data->rootElements[CollationRootElements::IX_SEC_TER_BOUNDARIES]; - if((secTerBoundaries >> 24) < CollationKeys::SEC_COMMON_HIGH) { - // [fixed last secondary common byte] is too low, - // and secondary weights would collide with compressed common secondaries. - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - } - - index = IX_CONTEXTS_OFFSET; - offset = getIndex(inIndexes, indexesLength, index); - length = getIndex(inIndexes, indexesLength, index + 1) - offset; - if(length >= 2) { - if(data == NULL) { - errorCode = U_INVALID_FORMAT_ERROR; // Tailored contexts without tailored trie. - return; - } - data->contexts = reinterpret_cast(inBytes + offset); - data->contextsLength = length / 2; - } - - index = IX_UNSAFE_BWD_OFFSET; - offset = getIndex(inIndexes, indexesLength, index); - length = getIndex(inIndexes, indexesLength, index + 1) - offset; - if(length >= 2) { - if(data == NULL) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - if(baseData == NULL) { -#if defined(COLLUNSAFE_COLL_VERSION) && defined (COLLUNSAFE_SERIALIZE) - tailoring.unsafeBackwardSet = new UnicodeSet(unsafe_serializedData, unsafe_serializedCount, UnicodeSet::kSerialized, errorCode); - if(tailoring.unsafeBackwardSet == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } else if (U_FAILURE(errorCode)) { - return; - } -#else - // Create the unsafe-backward set for the root collator. - // Include all non-zero combining marks and trail surrogates. - // We do this at load time, rather than at build time, - // to simplify Unicode version bootstrapping: - // The root data builder only needs the new FractionalUCA.txt data, - // but it need not be built with a version of ICU already updated to - // the corresponding new Unicode Character Database. - // - // The following is an optimized version of - // new UnicodeSet("[[:^lccc=0:][\\udc00-\\udfff]]"). - // It is faster and requires fewer code dependencies. - tailoring.unsafeBackwardSet = new UnicodeSet(0xdc00, 0xdfff); // trail surrogates - if(tailoring.unsafeBackwardSet == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - data->nfcImpl.addLcccChars(*tailoring.unsafeBackwardSet); -#endif // !COLLUNSAFE_SERIALIZE || !COLLUNSAFE_COLL_VERSION - } else { - // Clone the root collator's set contents. - tailoring.unsafeBackwardSet = static_cast( - baseData->unsafeBackwardSet->cloneAsThawed()); - if(tailoring.unsafeBackwardSet == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - // Add the ranges from the data file to the unsafe-backward set. - USerializedSet sset; - const uint16_t *unsafeData = reinterpret_cast(inBytes + offset); - if(!uset_getSerializedSet(&sset, unsafeData, length / 2)) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - int32_t count = uset_getSerializedRangeCount(&sset); - for(int32_t i = 0; i < count; ++i) { - UChar32 start, end; - uset_getSerializedRange(&sset, i, &start, &end); - tailoring.unsafeBackwardSet->add(start, end); - } - // Mark each lead surrogate as "unsafe" - // if any of its 1024 associated supplementary code points is "unsafe". - UChar32 c = 0x10000; - for(UChar lead = 0xd800; lead < 0xdc00; ++lead, c += 0x400) { - if(!tailoring.unsafeBackwardSet->containsNone(c, c + 0x3ff)) { - tailoring.unsafeBackwardSet->add(lead); - } - } - tailoring.unsafeBackwardSet->freeze(); - data->unsafeBackwardSet = tailoring.unsafeBackwardSet; - } else if(data == NULL) { - // Nothing to do. - } else if(baseData != NULL) { - // No tailoring-specific data: Alias the root collator's set. - data->unsafeBackwardSet = baseData->unsafeBackwardSet; - } else { - errorCode = U_INVALID_FORMAT_ERROR; // No unsafeBackwardSet. - return; - } - - // If the fast Latin format version is different, - // or the version is set to 0 for "no fast Latin table", - // then just always use the normal string comparison path. - if(data != NULL) { - data->fastLatinTable = NULL; - data->fastLatinTableLength = 0; - if(((inIndexes[IX_OPTIONS] >> 16) & 0xff) == CollationFastLatin::VERSION) { - index = IX_FAST_LATIN_TABLE_OFFSET; - offset = getIndex(inIndexes, indexesLength, index); - length = getIndex(inIndexes, indexesLength, index + 1) - offset; - if(length >= 2) { - data->fastLatinTable = reinterpret_cast(inBytes + offset); - data->fastLatinTableLength = length / 2; - if((*data->fastLatinTable >> 8) != CollationFastLatin::VERSION) { - errorCode = U_INVALID_FORMAT_ERROR; // header vs. table version mismatch - return; - } - } else if(baseData != NULL) { - data->fastLatinTable = baseData->fastLatinTable; - data->fastLatinTableLength = baseData->fastLatinTableLength; - } - } - } - - index = IX_SCRIPTS_OFFSET; - offset = getIndex(inIndexes, indexesLength, index); - length = getIndex(inIndexes, indexesLength, index + 1) - offset; - if(length >= 2) { - if(data == NULL) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - const uint16_t *scripts = reinterpret_cast(inBytes + offset); - int32_t scriptsLength = length / 2; - data->numScripts = scripts[0]; - // There must be enough entries for both arrays, including more than two range starts. - data->scriptStartsLength = scriptsLength - (1 + data->numScripts + 16); - if(data->scriptStartsLength <= 2 || - CollationData::MAX_NUM_SCRIPT_RANGES < data->scriptStartsLength) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - data->scriptsIndex = scripts + 1; - data->scriptStarts = scripts + 1 + data->numScripts + 16; - if(!(data->scriptStarts[0] == 0 && - data->scriptStarts[1] == ((Collation::MERGE_SEPARATOR_BYTE + 1) << 8) && - data->scriptStarts[data->scriptStartsLength - 1] == - (Collation::TRAIL_WEIGHT_BYTE << 8))) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - } else if(data == NULL) { - // Nothing to do. - } else if(baseData != NULL) { - data->numScripts = baseData->numScripts; - data->scriptsIndex = baseData->scriptsIndex; - data->scriptStarts = baseData->scriptStarts; - data->scriptStartsLength = baseData->scriptStartsLength; - } - - index = IX_COMPRESSIBLE_BYTES_OFFSET; - offset = getIndex(inIndexes, indexesLength, index); - length = getIndex(inIndexes, indexesLength, index + 1) - offset; - if(length >= 256) { - if(data == NULL) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - data->compressibleBytes = reinterpret_cast(inBytes + offset); - } else if(data == NULL) { - // Nothing to do. - } else if(baseData != NULL) { - data->compressibleBytes = baseData->compressibleBytes; - } else { - errorCode = U_INVALID_FORMAT_ERROR; // No compressibleBytes[]. - return; - } - - const CollationSettings &ts = *tailoring.settings; - int32_t options = inIndexes[IX_OPTIONS] & 0xffff; - uint16_t fastLatinPrimaries[CollationFastLatin::LATIN_LIMIT]; - int32_t fastLatinOptions = CollationFastLatin::getOptions( - tailoring.data, ts, fastLatinPrimaries, UPRV_LENGTHOF(fastLatinPrimaries)); - if(options == ts.options && ts.variableTop != 0 && - reorderCodesLength == ts.reorderCodesLength && - (reorderCodesLength == 0 || - uprv_memcmp(reorderCodes, ts.reorderCodes, reorderCodesLength * 4) == 0) && - fastLatinOptions == ts.fastLatinOptions && - (fastLatinOptions < 0 || - uprv_memcmp(fastLatinPrimaries, ts.fastLatinPrimaries, - sizeof(fastLatinPrimaries)) == 0)) { - return; - } - - CollationSettings *settings = SharedObject::copyOnWrite(tailoring.settings); - if(settings == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - settings->options = options; - // Set variableTop from options and scripts data. - settings->variableTop = tailoring.data->getLastPrimaryForGroup( - UCOL_REORDER_CODE_FIRST + int32_t{settings->getMaxVariable()}); - if(settings->variableTop == 0) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - - if(reorderCodesLength != 0) { - settings->aliasReordering(*baseData, reorderCodes, reorderCodesLength, - reorderRanges, reorderRangesLength, - reorderTable, errorCode); - } - - settings->fastLatinOptions = CollationFastLatin::getOptions( - tailoring.data, *settings, - settings->fastLatinPrimaries, UPRV_LENGTHOF(settings->fastLatinPrimaries)); -} - -UBool U_CALLCONV -CollationDataReader::isAcceptable(void *context, - const char * /* type */, const char * /*name*/, - const UDataInfo *pInfo) { - if( - pInfo->size >= 20 && - pInfo->isBigEndian == U_IS_BIG_ENDIAN && - pInfo->charsetFamily == U_CHARSET_FAMILY && - pInfo->dataFormat[0] == 0x55 && // dataFormat="UCol" - pInfo->dataFormat[1] == 0x43 && - pInfo->dataFormat[2] == 0x6f && - pInfo->dataFormat[3] == 0x6c && - pInfo->formatVersion[0] == 5 - ) { - UVersionInfo *version = static_cast(context); - if(version != NULL) { - uprv_memcpy(version, pInfo->dataVersion, 4); - } - return true; - } else { - return false; - } -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationdatareader.cpp +* +* created on: 2013feb07 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/ucol.h" +#include "unicode/udata.h" +#include "unicode/uscript.h" +#include "cmemory.h" +#include "collation.h" +#include "collationdata.h" +#include "collationdatareader.h" +#include "collationfastlatin.h" +#include "collationkeys.h" +#include "collationrootelements.h" +#include "collationsettings.h" +#include "collationtailoring.h" +#include "collunsafe.h" +#include "normalizer2impl.h" +#include "uassert.h" +#include "ucmndata.h" +#include "utrie2.h" + +U_NAMESPACE_BEGIN + +namespace { + +int32_t getIndex(const int32_t *indexes, int32_t length, int32_t i) { + return (i < length) ? indexes[i] : -1; +} + +} // namespace + +void +CollationDataReader::read(const CollationTailoring *base, const uint8_t *inBytes, int32_t inLength, + CollationTailoring &tailoring, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + if(base != nullptr) { + if(inBytes == nullptr || (0 <= inLength && inLength < 24)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + const DataHeader *header = reinterpret_cast(inBytes); + if(!(header->dataHeader.magic1 == 0xda && header->dataHeader.magic2 == 0x27 && + isAcceptable(tailoring.version, nullptr, nullptr, &header->info))) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + if(base->getUCAVersion() != tailoring.getUCAVersion()) { + errorCode = U_COLLATOR_VERSION_MISMATCH; + return; + } + int32_t headerLength = header->dataHeader.headerSize; + inBytes += headerLength; + if(inLength >= 0) { + inLength -= headerLength; + } + } + + if(inBytes == nullptr || (0 <= inLength && inLength < 8)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + const int32_t *inIndexes = reinterpret_cast(inBytes); + int32_t indexesLength = inIndexes[IX_INDEXES_LENGTH]; + if(indexesLength < 2 || (0 <= inLength && inLength < indexesLength * 4)) { + errorCode = U_INVALID_FORMAT_ERROR; // Not enough indexes. + return; + } + + // Assume that the tailoring data is in initial state, + // with nullptr pointers and 0 lengths. + + // Set pointers to non-empty data parts. + // Do this in order of their byte offsets. (Should help porting to Java.) + + int32_t index; // one of the indexes[] slots + int32_t offset; // byte offset for the index part + int32_t length; // number of bytes in the index part + + if(indexesLength > IX_TOTAL_SIZE) { + length = inIndexes[IX_TOTAL_SIZE]; + } else if(indexesLength > IX_REORDER_CODES_OFFSET) { + length = inIndexes[indexesLength - 1]; + } else { + length = 0; // only indexes, and inLength was already checked for them + } + if(0 <= inLength && inLength < length) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + + const CollationData *baseData = base == nullptr ? nullptr : base->data; + const int32_t *reorderCodes = nullptr; + int32_t reorderCodesLength = 0; + const uint32_t *reorderRanges = nullptr; + int32_t reorderRangesLength = 0; + index = IX_REORDER_CODES_OFFSET; + offset = getIndex(inIndexes, indexesLength, index); + length = getIndex(inIndexes, indexesLength, index + 1) - offset; + if(length >= 4) { + if(baseData == nullptr) { + // We assume for collation settings that + // the base data does not have a reordering. + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + reorderCodes = reinterpret_cast(inBytes + offset); + reorderCodesLength = length / 4; + + // The reorderRanges (if any) are the trailing reorderCodes entries. + // Split the array at the boundary. + // Script or reorder codes do not exceed 16-bit values. + // Range limits are stored in the upper 16 bits, and are never 0. + while(reorderRangesLength < reorderCodesLength && + (reorderCodes[reorderCodesLength - reorderRangesLength - 1] & 0xffff0000) != 0) { + ++reorderRangesLength; + } + U_ASSERT(reorderRangesLength < reorderCodesLength); + if(reorderRangesLength != 0) { + reorderCodesLength -= reorderRangesLength; + reorderRanges = reinterpret_cast(reorderCodes + reorderCodesLength); + } + } + + // There should be a reorder table only if there are reorder codes. + // However, when there are reorder codes the reorder table may be omitted to reduce + // the data size. + const uint8_t *reorderTable = nullptr; + index = IX_REORDER_TABLE_OFFSET; + offset = getIndex(inIndexes, indexesLength, index); + length = getIndex(inIndexes, indexesLength, index + 1) - offset; + if(length >= 256) { + if(reorderCodesLength == 0) { + errorCode = U_INVALID_FORMAT_ERROR; // Reordering table without reordering codes. + return; + } + reorderTable = inBytes + offset; + } else { + // If we have reorder codes, then build the reorderTable at the end, + // when the CollationData is otherwise complete. + } + + if(baseData != nullptr && baseData->numericPrimary != (inIndexes[IX_OPTIONS] & 0xff000000)) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + CollationData *data = nullptr; // Remains nullptr if there are no mappings. + + index = IX_TRIE_OFFSET; + offset = getIndex(inIndexes, indexesLength, index); + length = getIndex(inIndexes, indexesLength, index + 1) - offset; + if(length >= 8) { + if(!tailoring.ensureOwnedData(errorCode)) { return; } + data = tailoring.ownedData; + data->base = baseData; + data->numericPrimary = inIndexes[IX_OPTIONS] & 0xff000000; + data->trie = tailoring.trie = utrie2_openFromSerialized( + UTRIE2_32_VALUE_BITS, inBytes + offset, length, nullptr, + &errorCode); + if(U_FAILURE(errorCode)) { return; } + } else if(baseData != nullptr) { + // Use the base data. Only the settings are tailored. + tailoring.data = baseData; + } else { + errorCode = U_INVALID_FORMAT_ERROR; // No mappings. + return; + } + + index = IX_CES_OFFSET; + offset = getIndex(inIndexes, indexesLength, index); + length = getIndex(inIndexes, indexesLength, index + 1) - offset; + if(length >= 8) { + if(data == nullptr) { + errorCode = U_INVALID_FORMAT_ERROR; // Tailored ces without tailored trie. + return; + } + data->ces = reinterpret_cast(inBytes + offset); + data->cesLength = length / 8; + } + + index = IX_CE32S_OFFSET; + offset = getIndex(inIndexes, indexesLength, index); + length = getIndex(inIndexes, indexesLength, index + 1) - offset; + if(length >= 4) { + if(data == nullptr) { + errorCode = U_INVALID_FORMAT_ERROR; // Tailored ce32s without tailored trie. + return; + } + data->ce32s = reinterpret_cast(inBytes + offset); + data->ce32sLength = length / 4; + } + + int32_t jamoCE32sStart = getIndex(inIndexes, indexesLength, IX_JAMO_CE32S_START); + if(jamoCE32sStart >= 0) { + if(data == nullptr || data->ce32s == nullptr) { + errorCode = U_INVALID_FORMAT_ERROR; // Index into non-existent ce32s[]. + return; + } + data->jamoCE32s = data->ce32s + jamoCE32sStart; + } else if(data == nullptr) { + // Nothing to do. + } else if(baseData != nullptr) { + data->jamoCE32s = baseData->jamoCE32s; + } else { + errorCode = U_INVALID_FORMAT_ERROR; // No Jamo CE32s for Hangul processing. + return; + } + + index = IX_ROOT_ELEMENTS_OFFSET; + offset = getIndex(inIndexes, indexesLength, index); + length = getIndex(inIndexes, indexesLength, index + 1) - offset; + if(length >= 4) { + length /= 4; + if(data == nullptr || length <= CollationRootElements::IX_SEC_TER_BOUNDARIES) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + data->rootElements = reinterpret_cast(inBytes + offset); + data->rootElementsLength = length; + uint32_t commonSecTer = data->rootElements[CollationRootElements::IX_COMMON_SEC_AND_TER_CE]; + if(commonSecTer != Collation::COMMON_SEC_AND_TER_CE) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + uint32_t secTerBoundaries = data->rootElements[CollationRootElements::IX_SEC_TER_BOUNDARIES]; + if((secTerBoundaries >> 24) < CollationKeys::SEC_COMMON_HIGH) { + // [fixed last secondary common byte] is too low, + // and secondary weights would collide with compressed common secondaries. + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + } + + index = IX_CONTEXTS_OFFSET; + offset = getIndex(inIndexes, indexesLength, index); + length = getIndex(inIndexes, indexesLength, index + 1) - offset; + if(length >= 2) { + if(data == nullptr) { + errorCode = U_INVALID_FORMAT_ERROR; // Tailored contexts without tailored trie. + return; + } + data->contexts = reinterpret_cast(inBytes + offset); + data->contextsLength = length / 2; + } + + index = IX_UNSAFE_BWD_OFFSET; + offset = getIndex(inIndexes, indexesLength, index); + length = getIndex(inIndexes, indexesLength, index + 1) - offset; + if(length >= 2) { + if(data == nullptr) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + if(baseData == nullptr) { +#if defined(COLLUNSAFE_COLL_VERSION) && defined (COLLUNSAFE_SERIALIZE) + tailoring.unsafeBackwardSet = new UnicodeSet(unsafe_serializedData, unsafe_serializedCount, UnicodeSet::kSerialized, errorCode); + if(tailoring.unsafeBackwardSet == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } else if (U_FAILURE(errorCode)) { + return; + } +#else + // Create the unsafe-backward set for the root collator. + // Include all non-zero combining marks and trail surrogates. + // We do this at load time, rather than at build time, + // to simplify Unicode version bootstrapping: + // The root data builder only needs the new FractionalUCA.txt data, + // but it need not be built with a version of ICU already updated to + // the corresponding new Unicode Character Database. + // + // The following is an optimized version of + // new UnicodeSet("[[:^lccc=0:][\\udc00-\\udfff]]"). + // It is faster and requires fewer code dependencies. + tailoring.unsafeBackwardSet = new UnicodeSet(0xdc00, 0xdfff); // trail surrogates + if(tailoring.unsafeBackwardSet == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + data->nfcImpl.addLcccChars(*tailoring.unsafeBackwardSet); +#endif // !COLLUNSAFE_SERIALIZE || !COLLUNSAFE_COLL_VERSION + } else { + // Clone the root collator's set contents. + tailoring.unsafeBackwardSet = static_cast( + baseData->unsafeBackwardSet->cloneAsThawed()); + if(tailoring.unsafeBackwardSet == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + // Add the ranges from the data file to the unsafe-backward set. + USerializedSet sset; + const uint16_t *unsafeData = reinterpret_cast(inBytes + offset); + if(!uset_getSerializedSet(&sset, unsafeData, length / 2)) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + int32_t count = uset_getSerializedRangeCount(&sset); + for(int32_t i = 0; i < count; ++i) { + UChar32 start, end; + uset_getSerializedRange(&sset, i, &start, &end); + tailoring.unsafeBackwardSet->add(start, end); + } + // Mark each lead surrogate as "unsafe" + // if any of its 1024 associated supplementary code points is "unsafe". + UChar32 c = 0x10000; + for(char16_t lead = 0xd800; lead < 0xdc00; ++lead, c += 0x400) { + if(!tailoring.unsafeBackwardSet->containsNone(c, c + 0x3ff)) { + tailoring.unsafeBackwardSet->add(lead); + } + } + tailoring.unsafeBackwardSet->freeze(); + data->unsafeBackwardSet = tailoring.unsafeBackwardSet; + } else if(data == nullptr) { + // Nothing to do. + } else if(baseData != nullptr) { + // No tailoring-specific data: Alias the root collator's set. + data->unsafeBackwardSet = baseData->unsafeBackwardSet; + } else { + errorCode = U_INVALID_FORMAT_ERROR; // No unsafeBackwardSet. + return; + } + + // If the fast Latin format version is different, + // or the version is set to 0 for "no fast Latin table", + // then just always use the normal string comparison path. + if(data != nullptr) { + data->fastLatinTable = nullptr; + data->fastLatinTableLength = 0; + if(((inIndexes[IX_OPTIONS] >> 16) & 0xff) == CollationFastLatin::VERSION) { + index = IX_FAST_LATIN_TABLE_OFFSET; + offset = getIndex(inIndexes, indexesLength, index); + length = getIndex(inIndexes, indexesLength, index + 1) - offset; + if(length >= 2) { + data->fastLatinTable = reinterpret_cast(inBytes + offset); + data->fastLatinTableLength = length / 2; + if((*data->fastLatinTable >> 8) != CollationFastLatin::VERSION) { + errorCode = U_INVALID_FORMAT_ERROR; // header vs. table version mismatch + return; + } + } else if(baseData != nullptr) { + data->fastLatinTable = baseData->fastLatinTable; + data->fastLatinTableLength = baseData->fastLatinTableLength; + } + } + } + + index = IX_SCRIPTS_OFFSET; + offset = getIndex(inIndexes, indexesLength, index); + length = getIndex(inIndexes, indexesLength, index + 1) - offset; + if(length >= 2) { + if(data == nullptr) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + const uint16_t *scripts = reinterpret_cast(inBytes + offset); + int32_t scriptsLength = length / 2; + data->numScripts = scripts[0]; + // There must be enough entries for both arrays, including more than two range starts. + data->scriptStartsLength = scriptsLength - (1 + data->numScripts + 16); + if(data->scriptStartsLength <= 2 || + CollationData::MAX_NUM_SCRIPT_RANGES < data->scriptStartsLength) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + data->scriptsIndex = scripts + 1; + data->scriptStarts = scripts + 1 + data->numScripts + 16; + if(!(data->scriptStarts[0] == 0 && + data->scriptStarts[1] == ((Collation::MERGE_SEPARATOR_BYTE + 1) << 8) && + data->scriptStarts[data->scriptStartsLength - 1] == + (Collation::TRAIL_WEIGHT_BYTE << 8))) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + } else if(data == nullptr) { + // Nothing to do. + } else if(baseData != nullptr) { + data->numScripts = baseData->numScripts; + data->scriptsIndex = baseData->scriptsIndex; + data->scriptStarts = baseData->scriptStarts; + data->scriptStartsLength = baseData->scriptStartsLength; + } + + index = IX_COMPRESSIBLE_BYTES_OFFSET; + offset = getIndex(inIndexes, indexesLength, index); + length = getIndex(inIndexes, indexesLength, index + 1) - offset; + if(length >= 256) { + if(data == nullptr) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + data->compressibleBytes = reinterpret_cast(inBytes + offset); + } else if(data == nullptr) { + // Nothing to do. + } else if(baseData != nullptr) { + data->compressibleBytes = baseData->compressibleBytes; + } else { + errorCode = U_INVALID_FORMAT_ERROR; // No compressibleBytes[]. + return; + } + + const CollationSettings &ts = *tailoring.settings; + int32_t options = inIndexes[IX_OPTIONS] & 0xffff; + uint16_t fastLatinPrimaries[CollationFastLatin::LATIN_LIMIT]; + int32_t fastLatinOptions = CollationFastLatin::getOptions( + tailoring.data, ts, fastLatinPrimaries, UPRV_LENGTHOF(fastLatinPrimaries)); + if(options == ts.options && ts.variableTop != 0 && + reorderCodesLength == ts.reorderCodesLength && + (reorderCodesLength == 0 || + uprv_memcmp(reorderCodes, ts.reorderCodes, reorderCodesLength * 4) == 0) && + fastLatinOptions == ts.fastLatinOptions && + (fastLatinOptions < 0 || + uprv_memcmp(fastLatinPrimaries, ts.fastLatinPrimaries, + sizeof(fastLatinPrimaries)) == 0)) { + return; + } + + CollationSettings *settings = SharedObject::copyOnWrite(tailoring.settings); + if(settings == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + settings->options = options; + // Set variableTop from options and scripts data. + settings->variableTop = tailoring.data->getLastPrimaryForGroup( + UCOL_REORDER_CODE_FIRST + int32_t{settings->getMaxVariable()}); + if(settings->variableTop == 0) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + + if(reorderCodesLength != 0) { + settings->aliasReordering(*baseData, reorderCodes, reorderCodesLength, + reorderRanges, reorderRangesLength, + reorderTable, errorCode); + } + + settings->fastLatinOptions = CollationFastLatin::getOptions( + tailoring.data, *settings, + settings->fastLatinPrimaries, UPRV_LENGTHOF(settings->fastLatinPrimaries)); +} + +UBool U_CALLCONV +CollationDataReader::isAcceptable(void *context, + const char * /* type */, const char * /*name*/, + const UDataInfo *pInfo) { + if( + pInfo->size >= 20 && + pInfo->isBigEndian == U_IS_BIG_ENDIAN && + pInfo->charsetFamily == U_CHARSET_FAMILY && + pInfo->dataFormat[0] == 0x55 && // dataFormat="UCol" + pInfo->dataFormat[1] == 0x43 && + pInfo->dataFormat[2] == 0x6f && + pInfo->dataFormat[3] == 0x6c && + pInfo->formatVersion[0] == 5 + ) { + UVersionInfo *version = static_cast(context); + if(version != nullptr) { + uprv_memcpy(version, pInfo->dataVersion, 4); + } + return true; + } else { + return false; + } +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationdatareader.h b/deps/icu-small/source/i18n/collationdatareader.h index 083b57ea5800c1..fa725cfa729f5e 100644 --- a/deps/icu-small/source/i18n/collationdatareader.h +++ b/deps/icu-small/source/i18n/collationdatareader.h @@ -1,253 +1,253 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationdatareader.h -* -* created on: 2013feb07 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONDATAREADER_H__ -#define __COLLATIONDATAREADER_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/udata.h" - -struct UDataMemory; - -U_NAMESPACE_BEGIN - -struct CollationTailoring; - -/** - * Collation binary data reader. - */ -struct U_I18N_API CollationDataReader /* all static */ { - // The following constants are also copied into source/common/ucol_swp.cpp. - // Keep them in sync! - enum { - /** - * Number of int32_t indexes. - * - * Can be 2 if there are only options. - * Can be 7 or 8 if there are only options and a script reordering. - * The loader treats any index>=indexes[IX_INDEXES_LENGTH] as 0. - */ - IX_INDEXES_LENGTH, // 0 - /** - * Bits 31..24: numericPrimary, for numeric collation - * 23..16: fast Latin format version (0 = no fast Latin table) - * 15.. 0: options bit set - */ - IX_OPTIONS, - IX_RESERVED2, - IX_RESERVED3, - - /** Array offset to Jamo CE32s in ce32s[], or <0 if none. */ - IX_JAMO_CE32S_START, // 4 - - // Byte offsets from the start of the data, after the generic header. - // The indexes[] are at byte offset 0, other data follows. - // Each data item is aligned properly. - // The data items should be in descending order of unit size, - // to minimize the need for padding. - // Each item's byte length is given by the difference between its offset and - // the next index/offset value. - /** Byte offset to int32_t reorderCodes[]. */ - IX_REORDER_CODES_OFFSET, - /** - * Byte offset to uint8_t reorderTable[]. - * Empty table if <256 bytes (padding only). - * Otherwise 256 bytes or more (with padding). - */ - IX_REORDER_TABLE_OFFSET, - /** Byte offset to the collation trie. Its length is a multiple of 8 bytes. */ - IX_TRIE_OFFSET, - - IX_RESERVED8_OFFSET, // 8 - /** Byte offset to int64_t ces[]. */ - IX_CES_OFFSET, - IX_RESERVED10_OFFSET, - /** Byte offset to uint32_t ce32s[]. */ - IX_CE32S_OFFSET, - - /** Byte offset to uint32_t rootElements[]. */ - IX_ROOT_ELEMENTS_OFFSET, // 12 - /** Byte offset to UChar *contexts[]. */ - IX_CONTEXTS_OFFSET, - /** Byte offset to uint16_t [] with serialized unsafeBackwardSet. */ - IX_UNSAFE_BWD_OFFSET, - /** Byte offset to uint16_t fastLatinTable[]. */ - IX_FAST_LATIN_TABLE_OFFSET, - - /** Byte offset to uint16_t scripts[]. */ - IX_SCRIPTS_OFFSET, // 16 - /** - * Byte offset to UBool compressibleBytes[]. - * Empty table if <256 bytes (padding only). - * Otherwise 256 bytes or more (with padding). - */ - IX_COMPRESSIBLE_BYTES_OFFSET, - IX_RESERVED18_OFFSET, - IX_TOTAL_SIZE - }; - - static void read(const CollationTailoring *base, const uint8_t *inBytes, int32_t inLength, - CollationTailoring &tailoring, UErrorCode &errorCode); - - static UBool U_CALLCONV - isAcceptable(void *context, const char *type, const char *name, const UDataInfo *pInfo); - -private: - CollationDataReader() = delete; // no constructor -}; - -/* - * Format of collation data (ucadata.icu, binary data in coll/ *.res files). - * Format version 5. - * - * The root collation data is stored in the ucadata.icu file. - * Tailorings are stored inside .res resource bundle files, with a complete file header. - * - * Collation data begins with a standard ICU data file header - * (DataHeader, see ucmndata.h and unicode/udata.h). - * The UDataInfo.dataVersion field contains the UCA and other version numbers, - * see the comments for CollationTailoring.version. - * - * After the header, the file contains the following parts. - * Constants are defined as enum values of the CollationDataReader class. - * See also the Collation class. - * - * int32_t indexes[indexesLength]; - * The indexes array has variable length. - * Some tailorings only need the length and the options, - * others only add reorderCodes and the reorderTable, - * some need to store mappings. - * Only as many indexes are stored as needed to read all of the data. - * - * Index 0: indexesLength - * Index 1: numericPrimary, CollationFastLatin::VERSION, and options: see IX_OPTIONS - * Index 2..3: Unused/reserved/0. - * Index 4: Index into the ce32s array where the CE32s of the conjoining Jamo - * are stored in a short, contiguous part of the ce32s array. - * - * Indexes 5..19 are byte offsets in ascending order. - * Each byte offset marks the start of the next part in the data file, - * and the end of the previous one. - * When two consecutive byte offsets are the same (or too short), - * then the corresponding part is empty. - * Byte offsets are offsets from after the header, - * that is, from the beginning of the indexes[]. - * Each part starts at an offset with proper alignment for its data. - * If necessary, the previous part may include padding bytes to achieve this alignment. - * The last byte offset that is stored in the indexes indicates the total size of the data - * (starting with the indexes). - * - * int32_t reorderCodes[]; -- empty in root - * The list of script and reordering codes. - * - * Beginning with format version 5, this array may optionally - * have trailing entries with a full list of reorder ranges - * as described for CollationSettings::reorderRanges. - * - * Script or reorder codes are first and do not exceed 16-bit values. - * Range limits are stored in the upper 16 bits, and are never 0. - * Split this array into reorder codes and ranges at the first entry - * with non-zero upper 16 bits. - * - * If the ranges are missing but needed for split-reordered primary lead bytes, - * then they are regenerated at load time. - * - * uint8_t reorderTable[256]; -- empty in root; can be longer to include padding bytes - * Primary-weight lead byte permutation table. - * Normally present when the reorderCodes are, but can be built at load time. - * - * Beginning with format version 5, a 0 entry at a non-zero index - * (which is otherwise an illegal value) - * means that the primary lead byte is "split" - * (there are different offsets for primaries that share that lead byte) - * and the reordering offset must be determined via the reorder ranges - * that are either stored as part of the reorderCodes array - * or regenerated at load time. - * - * UTrie2 trie; -- see utrie2_impl.h and utrie2.h - * The trie holds the main collation data. Each code point is mapped to a 32-bit value. - * It encodes a simple collation element (CE) in compact form, unless bits 7..6 are both set, - * in which case it is a special CE32 and contains a 4-bit tag and further data. - * See the Collation class for details. - * - * The trie has a value for each lead surrogate code unit with some bits encoding - * collective properties of the 1024 supplementary characters whose UTF-16 form starts with - * the lead surrogate. See Collation::LEAD_SURROGATE_TAG.. - * - * int64_t ces[]; - * 64-bit CEs and expansions that cannot be stored in a more compact form. - * - * uint32_t ce32s[]; - * CE32s for expansions in compact form, and for characters whose trie values - * contain special data. - * - * uint32_t rootElements[]; -- empty in all tailorings - * Compact storage for all of the CEs that occur in the root collation. - * See the CollationRootElements class. - * - * UChar *contexts[]; - * Serialized UCharsTrie structures with prefix (pre-context) and contraction mappings. - * - * uint16_t unsafeBackwardSet[]; -- see UnicodeSet::serialize() - * Serialized form of characters that are unsafe when iterating backwards, - * and at the end of an identical string prefix. - * Back up to a safe character. - * Lead surrogates are "unsafe" when any of their corresponding supplementary - * code points are unsafe. - * Does not include [:^lccc=0:][:^tccc=0:]. - * For each tailoring, the root unsafeBackwardSet is subtracted. - * (As a result, in many tailorings no set needs to be stored.) - * - * uint16_t fastLatinTable[]; - * Optional optimization for Latin text. - * See the CollationFastLatin class. - * - * uint16_t scripts[]; -- empty in all tailorings - * Format version 5: - * uint16_t numScripts; - * uint16_t scriptsIndex[numScripts+16]; - * uint16_t scriptStarts[]; - * See CollationData::numScripts etc. - * - * Format version 4: - * Table of the reordering groups with their first and last lead bytes, - * and their script and reordering codes. - * See CollationData::scripts. - * - * UBool compressibleBytes[]; -- empty in all tailorings - * Flag for getSortKey(), indicating primary weight lead bytes that are compressible. - * - * ----------------- - * Changes for formatVersion 5 (ICU 55) - * - * Reordering moves single scripts, not groups of scripts. - * Reorder ranges are optionally appended to the reorderCodes, - * and a 0 entry in the reorderTable indicates a split lead byte. - * The scripts data has a new format. - * - * The rootElements may contain secondary and tertiary weights below common=05. - * (Used for small Hiragana letters.) - * Where is occurs, there is also an explicit unit with common secondary & tertiary weights. - * There are no other data structure changes, but builder code needs to be able to handle such data. - * - * The collation element for the merge separator code point U+FFFE - * does not necessarily have special, unique secondary/tertiary weights any more. - */ - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONDATAREADER_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationdatareader.h +* +* created on: 2013feb07 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONDATAREADER_H__ +#define __COLLATIONDATAREADER_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/udata.h" + +struct UDataMemory; + +U_NAMESPACE_BEGIN + +struct CollationTailoring; + +/** + * Collation binary data reader. + */ +struct U_I18N_API CollationDataReader /* all static */ { + // The following constants are also copied into source/common/ucol_swp.cpp. + // Keep them in sync! + enum { + /** + * Number of int32_t indexes. + * + * Can be 2 if there are only options. + * Can be 7 or 8 if there are only options and a script reordering. + * The loader treats any index>=indexes[IX_INDEXES_LENGTH] as 0. + */ + IX_INDEXES_LENGTH, // 0 + /** + * Bits 31..24: numericPrimary, for numeric collation + * 23..16: fast Latin format version (0 = no fast Latin table) + * 15.. 0: options bit set + */ + IX_OPTIONS, + IX_RESERVED2, + IX_RESERVED3, + + /** Array offset to Jamo CE32s in ce32s[], or <0 if none. */ + IX_JAMO_CE32S_START, // 4 + + // Byte offsets from the start of the data, after the generic header. + // The indexes[] are at byte offset 0, other data follows. + // Each data item is aligned properly. + // The data items should be in descending order of unit size, + // to minimize the need for padding. + // Each item's byte length is given by the difference between its offset and + // the next index/offset value. + /** Byte offset to int32_t reorderCodes[]. */ + IX_REORDER_CODES_OFFSET, + /** + * Byte offset to uint8_t reorderTable[]. + * Empty table if <256 bytes (padding only). + * Otherwise 256 bytes or more (with padding). + */ + IX_REORDER_TABLE_OFFSET, + /** Byte offset to the collation trie. Its length is a multiple of 8 bytes. */ + IX_TRIE_OFFSET, + + IX_RESERVED8_OFFSET, // 8 + /** Byte offset to int64_t ces[]. */ + IX_CES_OFFSET, + IX_RESERVED10_OFFSET, + /** Byte offset to uint32_t ce32s[]. */ + IX_CE32S_OFFSET, + + /** Byte offset to uint32_t rootElements[]. */ + IX_ROOT_ELEMENTS_OFFSET, // 12 + /** Byte offset to char16_t *contexts[]. */ + IX_CONTEXTS_OFFSET, + /** Byte offset to uint16_t [] with serialized unsafeBackwardSet. */ + IX_UNSAFE_BWD_OFFSET, + /** Byte offset to uint16_t fastLatinTable[]. */ + IX_FAST_LATIN_TABLE_OFFSET, + + /** Byte offset to uint16_t scripts[]. */ + IX_SCRIPTS_OFFSET, // 16 + /** + * Byte offset to UBool compressibleBytes[]. + * Empty table if <256 bytes (padding only). + * Otherwise 256 bytes or more (with padding). + */ + IX_COMPRESSIBLE_BYTES_OFFSET, + IX_RESERVED18_OFFSET, + IX_TOTAL_SIZE + }; + + static void read(const CollationTailoring *base, const uint8_t *inBytes, int32_t inLength, + CollationTailoring &tailoring, UErrorCode &errorCode); + + static UBool U_CALLCONV + isAcceptable(void *context, const char *type, const char *name, const UDataInfo *pInfo); + +private: + CollationDataReader() = delete; // no constructor +}; + +/* + * Format of collation data (ucadata.icu, binary data in coll/ *.res files). + * Format version 5. + * + * The root collation data is stored in the ucadata.icu file. + * Tailorings are stored inside .res resource bundle files, with a complete file header. + * + * Collation data begins with a standard ICU data file header + * (DataHeader, see ucmndata.h and unicode/udata.h). + * The UDataInfo.dataVersion field contains the UCA and other version numbers, + * see the comments for CollationTailoring.version. + * + * After the header, the file contains the following parts. + * Constants are defined as enum values of the CollationDataReader class. + * See also the Collation class. + * + * int32_t indexes[indexesLength]; + * The indexes array has variable length. + * Some tailorings only need the length and the options, + * others only add reorderCodes and the reorderTable, + * some need to store mappings. + * Only as many indexes are stored as needed to read all of the data. + * + * Index 0: indexesLength + * Index 1: numericPrimary, CollationFastLatin::VERSION, and options: see IX_OPTIONS + * Index 2..3: Unused/reserved/0. + * Index 4: Index into the ce32s array where the CE32s of the conjoining Jamo + * are stored in a short, contiguous part of the ce32s array. + * + * Indexes 5..19 are byte offsets in ascending order. + * Each byte offset marks the start of the next part in the data file, + * and the end of the previous one. + * When two consecutive byte offsets are the same (or too short), + * then the corresponding part is empty. + * Byte offsets are offsets from after the header, + * that is, from the beginning of the indexes[]. + * Each part starts at an offset with proper alignment for its data. + * If necessary, the previous part may include padding bytes to achieve this alignment. + * The last byte offset that is stored in the indexes indicates the total size of the data + * (starting with the indexes). + * + * int32_t reorderCodes[]; -- empty in root + * The list of script and reordering codes. + * + * Beginning with format version 5, this array may optionally + * have trailing entries with a full list of reorder ranges + * as described for CollationSettings::reorderRanges. + * + * Script or reorder codes are first and do not exceed 16-bit values. + * Range limits are stored in the upper 16 bits, and are never 0. + * Split this array into reorder codes and ranges at the first entry + * with non-zero upper 16 bits. + * + * If the ranges are missing but needed for split-reordered primary lead bytes, + * then they are regenerated at load time. + * + * uint8_t reorderTable[256]; -- empty in root; can be longer to include padding bytes + * Primary-weight lead byte permutation table. + * Normally present when the reorderCodes are, but can be built at load time. + * + * Beginning with format version 5, a 0 entry at a non-zero index + * (which is otherwise an illegal value) + * means that the primary lead byte is "split" + * (there are different offsets for primaries that share that lead byte) + * and the reordering offset must be determined via the reorder ranges + * that are either stored as part of the reorderCodes array + * or regenerated at load time. + * + * UTrie2 trie; -- see utrie2_impl.h and utrie2.h + * The trie holds the main collation data. Each code point is mapped to a 32-bit value. + * It encodes a simple collation element (CE) in compact form, unless bits 7..6 are both set, + * in which case it is a special CE32 and contains a 4-bit tag and further data. + * See the Collation class for details. + * + * The trie has a value for each lead surrogate code unit with some bits encoding + * collective properties of the 1024 supplementary characters whose UTF-16 form starts with + * the lead surrogate. See Collation::LEAD_SURROGATE_TAG.. + * + * int64_t ces[]; + * 64-bit CEs and expansions that cannot be stored in a more compact form. + * + * uint32_t ce32s[]; + * CE32s for expansions in compact form, and for characters whose trie values + * contain special data. + * + * uint32_t rootElements[]; -- empty in all tailorings + * Compact storage for all of the CEs that occur in the root collation. + * See the CollationRootElements class. + * + * char16_t *contexts[]; + * Serialized UCharsTrie structures with prefix (pre-context) and contraction mappings. + * + * uint16_t unsafeBackwardSet[]; -- see UnicodeSet::serialize() + * Serialized form of characters that are unsafe when iterating backwards, + * and at the end of an identical string prefix. + * Back up to a safe character. + * Lead surrogates are "unsafe" when any of their corresponding supplementary + * code points are unsafe. + * Does not include [:^lccc=0:][:^tccc=0:]. + * For each tailoring, the root unsafeBackwardSet is subtracted. + * (As a result, in many tailorings no set needs to be stored.) + * + * uint16_t fastLatinTable[]; + * Optional optimization for Latin text. + * See the CollationFastLatin class. + * + * uint16_t scripts[]; -- empty in all tailorings + * Format version 5: + * uint16_t numScripts; + * uint16_t scriptsIndex[numScripts+16]; + * uint16_t scriptStarts[]; + * See CollationData::numScripts etc. + * + * Format version 4: + * Table of the reordering groups with their first and last lead bytes, + * and their script and reordering codes. + * See CollationData::scripts. + * + * UBool compressibleBytes[]; -- empty in all tailorings + * Flag for getSortKey(), indicating primary weight lead bytes that are compressible. + * + * ----------------- + * Changes for formatVersion 5 (ICU 55) + * + * Reordering moves single scripts, not groups of scripts. + * Reorder ranges are optionally appended to the reorderCodes, + * and a 0 entry in the reorderTable indicates a split lead byte. + * The scripts data has a new format. + * + * The rootElements may contain secondary and tertiary weights below common=05. + * (Used for small Hiragana letters.) + * Where is occurs, there is also an explicit unit with common secondary & tertiary weights. + * There are no other data structure changes, but builder code needs to be able to handle such data. + * + * The collation element for the merge separator code point U+FFFE + * does not necessarily have special, unique secondary/tertiary weights any more. + */ + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONDATAREADER_H__ diff --git a/deps/icu-small/source/i18n/collationdatawriter.cpp b/deps/icu-small/source/i18n/collationdatawriter.cpp index b4be7df88781c9..7506882ba4fccc 100644 --- a/deps/icu-small/source/i18n/collationdatawriter.cpp +++ b/deps/icu-small/source/i18n/collationdatawriter.cpp @@ -1,352 +1,352 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationdatawriter.cpp -* -* created on: 2013aug06 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/tblcoll.h" -#include "unicode/udata.h" -#include "unicode/uniset.h" -#include "cmemory.h" -#include "collationdata.h" -#include "collationdatabuilder.h" -#include "collationdatareader.h" -#include "collationdatawriter.h" -#include "collationfastlatin.h" -#include "collationsettings.h" -#include "collationtailoring.h" -#include "uassert.h" -#include "ucmndata.h" - -U_NAMESPACE_BEGIN - -uint8_t * -RuleBasedCollator::cloneRuleData(int32_t &length, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return NULL; } - LocalMemory buffer((uint8_t *)uprv_malloc(20000)); - if(buffer.isNull()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - length = cloneBinary(buffer.getAlias(), 20000, errorCode); - if(errorCode == U_BUFFER_OVERFLOW_ERROR) { - if(buffer.allocateInsteadAndCopy(length, 0) == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - errorCode = U_ZERO_ERROR; - length = cloneBinary(buffer.getAlias(), length, errorCode); - } - if(U_FAILURE(errorCode)) { return NULL; } - return buffer.orphan(); -} - -int32_t -RuleBasedCollator::cloneBinary(uint8_t *dest, int32_t capacity, UErrorCode &errorCode) const { - int32_t indexes[CollationDataReader::IX_TOTAL_SIZE + 1]; - return CollationDataWriter::writeTailoring( - *tailoring, *settings, indexes, dest, capacity, - errorCode); -} - -static const UDataInfo dataInfo = { - sizeof(UDataInfo), - 0, - - U_IS_BIG_ENDIAN, - U_CHARSET_FAMILY, - U_SIZEOF_UCHAR, - 0, - - { 0x55, 0x43, 0x6f, 0x6c }, // dataFormat="UCol" - { 5, 0, 0, 0 }, // formatVersion - { 6, 3, 0, 0 } // dataVersion -}; - -int32_t -CollationDataWriter::writeBase(const CollationData &data, const CollationSettings &settings, - const void *rootElements, int32_t rootElementsLength, - int32_t indexes[], uint8_t *dest, int32_t capacity, - UErrorCode &errorCode) { - return write(true, NULL, - data, settings, - rootElements, rootElementsLength, - indexes, dest, capacity, errorCode); -} - -int32_t -CollationDataWriter::writeTailoring(const CollationTailoring &t, const CollationSettings &settings, - int32_t indexes[], uint8_t *dest, int32_t capacity, - UErrorCode &errorCode) { - return write(false, t.version, - *t.data, settings, - NULL, 0, - indexes, dest, capacity, errorCode); -} - -int32_t -CollationDataWriter::write(UBool isBase, const UVersionInfo dataVersion, - const CollationData &data, const CollationSettings &settings, - const void *rootElements, int32_t rootElementsLength, - int32_t indexes[], uint8_t *dest, int32_t capacity, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - if(capacity < 0 || (capacity > 0 && dest == NULL)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - // Figure out which data items to write before settling on - // the indexes length and writing offsets. - // For any data item, we need to write the start and limit offsets, - // so the indexes length must be at least index-of-start-offset + 2. - int32_t indexesLength; - UBool hasMappings; - UnicodeSet unsafeBackwardSet; - const CollationData *baseData = data.base; - - int32_t fastLatinVersion; - if(data.fastLatinTable != NULL) { - fastLatinVersion = (int32_t)CollationFastLatin::VERSION << 16; - } else { - fastLatinVersion = 0; - } - int32_t fastLatinTableLength = 0; - - if(isBase) { - // For the root collator, we write an even number of indexes - // so that we start with an 8-aligned offset. - indexesLength = CollationDataReader::IX_TOTAL_SIZE + 1; - U_ASSERT(settings.reorderCodesLength == 0); - hasMappings = true; - unsafeBackwardSet = *data.unsafeBackwardSet; - fastLatinTableLength = data.fastLatinTableLength; - } else if(baseData == NULL) { - hasMappings = false; - if(settings.reorderCodesLength == 0) { - // only options - indexesLength = CollationDataReader::IX_OPTIONS + 1; // no limit offset here - } else { - // only options, reorder codes, and the reorder table - indexesLength = CollationDataReader::IX_REORDER_TABLE_OFFSET + 2; - } - } else { - hasMappings = true; - // Tailored mappings, and what else? - // Check in ascending order of optional tailoring data items. - indexesLength = CollationDataReader::IX_CE32S_OFFSET + 2; - if(data.contextsLength != 0) { - indexesLength = CollationDataReader::IX_CONTEXTS_OFFSET + 2; - } - unsafeBackwardSet.addAll(*data.unsafeBackwardSet).removeAll(*baseData->unsafeBackwardSet); - if(!unsafeBackwardSet.isEmpty()) { - indexesLength = CollationDataReader::IX_UNSAFE_BWD_OFFSET + 2; - } - if(data.fastLatinTable != baseData->fastLatinTable) { - fastLatinTableLength = data.fastLatinTableLength; - indexesLength = CollationDataReader::IX_FAST_LATIN_TABLE_OFFSET + 2; - } - } - - UVector32 codesAndRanges(errorCode); - const int32_t *reorderCodes = settings.reorderCodes; - int32_t reorderCodesLength = settings.reorderCodesLength; - if(settings.hasReordering() && - CollationSettings::reorderTableHasSplitBytes(settings.reorderTable)) { - // Rebuild the full list of reorder ranges. - // The list in the settings is truncated for efficiency. - data.makeReorderRanges(reorderCodes, reorderCodesLength, codesAndRanges, errorCode); - // Write the codes, then the ranges. - for(int32_t i = 0; i < reorderCodesLength; ++i) { - codesAndRanges.insertElementAt(reorderCodes[i], i, errorCode); - } - if(U_FAILURE(errorCode)) { return 0; } - reorderCodes = codesAndRanges.getBuffer(); - reorderCodesLength = codesAndRanges.size(); - } - - int32_t headerSize; - if(isBase) { - headerSize = 0; // udata_create() writes the header - } else { - DataHeader header; - header.dataHeader.magic1 = 0xda; - header.dataHeader.magic2 = 0x27; - uprv_memcpy(&header.info, &dataInfo, sizeof(UDataInfo)); - uprv_memcpy(header.info.dataVersion, dataVersion, sizeof(UVersionInfo)); - headerSize = (int32_t)sizeof(header); - U_ASSERT((headerSize & 3) == 0); // multiple of 4 bytes - if(hasMappings && data.cesLength != 0) { - // Sum of the sizes of the data items which are - // not automatically multiples of 8 bytes and which are placed before the CEs. - int32_t sum = headerSize + (indexesLength + reorderCodesLength) * 4; - if((sum & 7) != 0) { - // We need to add padding somewhere so that the 64-bit CEs are 8-aligned. - // We add to the header size here. - // Alternatively, we could increment the indexesLength - // or add a few bytes to the reorderTable. - headerSize += 4; - } - } - header.dataHeader.headerSize = (uint16_t)headerSize; - if(headerSize <= capacity) { - uprv_memcpy(dest, &header, sizeof(header)); - // Write 00 bytes so that the padding is not mistaken for a copyright string. - uprv_memset(dest + sizeof(header), 0, headerSize - (int32_t)sizeof(header)); - dest += headerSize; - capacity -= headerSize; - } else { - dest = NULL; - capacity = 0; - } - } - - indexes[CollationDataReader::IX_INDEXES_LENGTH] = indexesLength; - U_ASSERT((settings.options & ~0xffff) == 0); - indexes[CollationDataReader::IX_OPTIONS] = - data.numericPrimary | fastLatinVersion | settings.options; - indexes[CollationDataReader::IX_RESERVED2] = 0; - indexes[CollationDataReader::IX_RESERVED3] = 0; - - // Byte offsets of data items all start from the start of the indexes. - // We add the headerSize at the very end. - int32_t totalSize = indexesLength * 4; - - if(hasMappings && (isBase || data.jamoCE32s != baseData->jamoCE32s)) { - indexes[CollationDataReader::IX_JAMO_CE32S_START] = static_cast(data.jamoCE32s - data.ce32s); - } else { - indexes[CollationDataReader::IX_JAMO_CE32S_START] = -1; - } - - indexes[CollationDataReader::IX_REORDER_CODES_OFFSET] = totalSize; - totalSize += reorderCodesLength * 4; - - indexes[CollationDataReader::IX_REORDER_TABLE_OFFSET] = totalSize; - if(settings.reorderTable != NULL) { - totalSize += 256; - } - - indexes[CollationDataReader::IX_TRIE_OFFSET] = totalSize; - if(hasMappings) { - UErrorCode errorCode2 = U_ZERO_ERROR; - int32_t length; - if(totalSize < capacity) { - length = utrie2_serialize(data.trie, dest + totalSize, - capacity - totalSize, &errorCode2); - } else { - length = utrie2_serialize(data.trie, NULL, 0, &errorCode2); - } - if(U_FAILURE(errorCode2) && errorCode2 != U_BUFFER_OVERFLOW_ERROR) { - errorCode = errorCode2; - return 0; - } - // The trie size should be a multiple of 8 bytes due to the way - // compactIndex2(UNewTrie2 *trie) currently works. - U_ASSERT((length & 7) == 0); - totalSize += length; - } - - indexes[CollationDataReader::IX_RESERVED8_OFFSET] = totalSize; - indexes[CollationDataReader::IX_CES_OFFSET] = totalSize; - if(hasMappings && data.cesLength != 0) { - U_ASSERT(((headerSize + totalSize) & 7) == 0); - totalSize += data.cesLength * 8; - } - - indexes[CollationDataReader::IX_RESERVED10_OFFSET] = totalSize; - indexes[CollationDataReader::IX_CE32S_OFFSET] = totalSize; - if(hasMappings) { - totalSize += data.ce32sLength * 4; - } - - indexes[CollationDataReader::IX_ROOT_ELEMENTS_OFFSET] = totalSize; - totalSize += rootElementsLength * 4; - - indexes[CollationDataReader::IX_CONTEXTS_OFFSET] = totalSize; - if(hasMappings) { - totalSize += data.contextsLength * 2; - } - - indexes[CollationDataReader::IX_UNSAFE_BWD_OFFSET] = totalSize; - if(hasMappings && !unsafeBackwardSet.isEmpty()) { - UErrorCode errorCode2 = U_ZERO_ERROR; - int32_t length; - if(totalSize < capacity) { - uint16_t *p = reinterpret_cast(dest + totalSize); - length = unsafeBackwardSet.serialize( - p, (capacity - totalSize) / 2, errorCode2); - } else { - length = unsafeBackwardSet.serialize(NULL, 0, errorCode2); - } - if(U_FAILURE(errorCode2) && errorCode2 != U_BUFFER_OVERFLOW_ERROR) { - errorCode = errorCode2; - return 0; - } - totalSize += length * 2; - } - - indexes[CollationDataReader::IX_FAST_LATIN_TABLE_OFFSET] = totalSize; - totalSize += fastLatinTableLength * 2; - - UnicodeString scripts; - indexes[CollationDataReader::IX_SCRIPTS_OFFSET] = totalSize; - if(isBase) { - scripts.append((UChar)data.numScripts); - scripts.append(reinterpret_cast(data.scriptsIndex), data.numScripts + 16); - scripts.append(reinterpret_cast(data.scriptStarts), data.scriptStartsLength); - totalSize += scripts.length() * 2; - } - - indexes[CollationDataReader::IX_COMPRESSIBLE_BYTES_OFFSET] = totalSize; - if(isBase) { - totalSize += 256; - } - - indexes[CollationDataReader::IX_RESERVED18_OFFSET] = totalSize; - indexes[CollationDataReader::IX_TOTAL_SIZE] = totalSize; - - if(totalSize > capacity) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return headerSize + totalSize; - } - - uprv_memcpy(dest, indexes, indexesLength * 4); - copyData(indexes, CollationDataReader::IX_REORDER_CODES_OFFSET, reorderCodes, dest); - copyData(indexes, CollationDataReader::IX_REORDER_TABLE_OFFSET, settings.reorderTable, dest); - // The trie has already been serialized into the dest buffer. - copyData(indexes, CollationDataReader::IX_CES_OFFSET, data.ces, dest); - copyData(indexes, CollationDataReader::IX_CE32S_OFFSET, data.ce32s, dest); - copyData(indexes, CollationDataReader::IX_ROOT_ELEMENTS_OFFSET, rootElements, dest); - copyData(indexes, CollationDataReader::IX_CONTEXTS_OFFSET, data.contexts, dest); - // The unsafeBackwardSet has already been serialized into the dest buffer. - copyData(indexes, CollationDataReader::IX_FAST_LATIN_TABLE_OFFSET, data.fastLatinTable, dest); - copyData(indexes, CollationDataReader::IX_SCRIPTS_OFFSET, scripts.getBuffer(), dest); - copyData(indexes, CollationDataReader::IX_COMPRESSIBLE_BYTES_OFFSET, data.compressibleBytes, dest); - - return headerSize + totalSize; -} - -void -CollationDataWriter::copyData(const int32_t indexes[], int32_t startIndex, - const void *src, uint8_t *dest) { - int32_t start = indexes[startIndex]; - int32_t limit = indexes[startIndex + 1]; - if(start < limit) { - uprv_memcpy(dest + start, src, limit - start); - } -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationdatawriter.cpp +* +* created on: 2013aug06 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/tblcoll.h" +#include "unicode/udata.h" +#include "unicode/uniset.h" +#include "cmemory.h" +#include "collationdata.h" +#include "collationdatabuilder.h" +#include "collationdatareader.h" +#include "collationdatawriter.h" +#include "collationfastlatin.h" +#include "collationsettings.h" +#include "collationtailoring.h" +#include "uassert.h" +#include "ucmndata.h" + +U_NAMESPACE_BEGIN + +uint8_t * +RuleBasedCollator::cloneRuleData(int32_t &length, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return nullptr; } + LocalMemory buffer((uint8_t *)uprv_malloc(20000)); + if(buffer.isNull()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + length = cloneBinary(buffer.getAlias(), 20000, errorCode); + if(errorCode == U_BUFFER_OVERFLOW_ERROR) { + if(buffer.allocateInsteadAndCopy(length, 0) == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + errorCode = U_ZERO_ERROR; + length = cloneBinary(buffer.getAlias(), length, errorCode); + } + if(U_FAILURE(errorCode)) { return nullptr; } + return buffer.orphan(); +} + +int32_t +RuleBasedCollator::cloneBinary(uint8_t *dest, int32_t capacity, UErrorCode &errorCode) const { + int32_t indexes[CollationDataReader::IX_TOTAL_SIZE + 1]; + return CollationDataWriter::writeTailoring( + *tailoring, *settings, indexes, dest, capacity, + errorCode); +} + +static const UDataInfo dataInfo = { + sizeof(UDataInfo), + 0, + + U_IS_BIG_ENDIAN, + U_CHARSET_FAMILY, + U_SIZEOF_UCHAR, + 0, + + { 0x55, 0x43, 0x6f, 0x6c }, // dataFormat="UCol" + { 5, 0, 0, 0 }, // formatVersion + { 6, 3, 0, 0 } // dataVersion +}; + +int32_t +CollationDataWriter::writeBase(const CollationData &data, const CollationSettings &settings, + const void *rootElements, int32_t rootElementsLength, + int32_t indexes[], uint8_t *dest, int32_t capacity, + UErrorCode &errorCode) { + return write(true, nullptr, + data, settings, + rootElements, rootElementsLength, + indexes, dest, capacity, errorCode); +} + +int32_t +CollationDataWriter::writeTailoring(const CollationTailoring &t, const CollationSettings &settings, + int32_t indexes[], uint8_t *dest, int32_t capacity, + UErrorCode &errorCode) { + return write(false, t.version, + *t.data, settings, + nullptr, 0, + indexes, dest, capacity, errorCode); +} + +int32_t +CollationDataWriter::write(UBool isBase, const UVersionInfo dataVersion, + const CollationData &data, const CollationSettings &settings, + const void *rootElements, int32_t rootElementsLength, + int32_t indexes[], uint8_t *dest, int32_t capacity, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + if(capacity < 0 || (capacity > 0 && dest == nullptr)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + // Figure out which data items to write before settling on + // the indexes length and writing offsets. + // For any data item, we need to write the start and limit offsets, + // so the indexes length must be at least index-of-start-offset + 2. + int32_t indexesLength; + UBool hasMappings; + UnicodeSet unsafeBackwardSet; + const CollationData *baseData = data.base; + + int32_t fastLatinVersion; + if(data.fastLatinTable != nullptr) { + fastLatinVersion = (int32_t)CollationFastLatin::VERSION << 16; + } else { + fastLatinVersion = 0; + } + int32_t fastLatinTableLength = 0; + + if(isBase) { + // For the root collator, we write an even number of indexes + // so that we start with an 8-aligned offset. + indexesLength = CollationDataReader::IX_TOTAL_SIZE + 1; + U_ASSERT(settings.reorderCodesLength == 0); + hasMappings = true; + unsafeBackwardSet = *data.unsafeBackwardSet; + fastLatinTableLength = data.fastLatinTableLength; + } else if(baseData == nullptr) { + hasMappings = false; + if(settings.reorderCodesLength == 0) { + // only options + indexesLength = CollationDataReader::IX_OPTIONS + 1; // no limit offset here + } else { + // only options, reorder codes, and the reorder table + indexesLength = CollationDataReader::IX_REORDER_TABLE_OFFSET + 2; + } + } else { + hasMappings = true; + // Tailored mappings, and what else? + // Check in ascending order of optional tailoring data items. + indexesLength = CollationDataReader::IX_CE32S_OFFSET + 2; + if(data.contextsLength != 0) { + indexesLength = CollationDataReader::IX_CONTEXTS_OFFSET + 2; + } + unsafeBackwardSet.addAll(*data.unsafeBackwardSet).removeAll(*baseData->unsafeBackwardSet); + if(!unsafeBackwardSet.isEmpty()) { + indexesLength = CollationDataReader::IX_UNSAFE_BWD_OFFSET + 2; + } + if(data.fastLatinTable != baseData->fastLatinTable) { + fastLatinTableLength = data.fastLatinTableLength; + indexesLength = CollationDataReader::IX_FAST_LATIN_TABLE_OFFSET + 2; + } + } + + UVector32 codesAndRanges(errorCode); + const int32_t *reorderCodes = settings.reorderCodes; + int32_t reorderCodesLength = settings.reorderCodesLength; + if(settings.hasReordering() && + CollationSettings::reorderTableHasSplitBytes(settings.reorderTable)) { + // Rebuild the full list of reorder ranges. + // The list in the settings is truncated for efficiency. + data.makeReorderRanges(reorderCodes, reorderCodesLength, codesAndRanges, errorCode); + // Write the codes, then the ranges. + for(int32_t i = 0; i < reorderCodesLength; ++i) { + codesAndRanges.insertElementAt(reorderCodes[i], i, errorCode); + } + if(U_FAILURE(errorCode)) { return 0; } + reorderCodes = codesAndRanges.getBuffer(); + reorderCodesLength = codesAndRanges.size(); + } + + int32_t headerSize; + if(isBase) { + headerSize = 0; // udata_create() writes the header + } else { + DataHeader header; + header.dataHeader.magic1 = 0xda; + header.dataHeader.magic2 = 0x27; + uprv_memcpy(&header.info, &dataInfo, sizeof(UDataInfo)); + uprv_memcpy(header.info.dataVersion, dataVersion, sizeof(UVersionInfo)); + headerSize = (int32_t)sizeof(header); + U_ASSERT((headerSize & 3) == 0); // multiple of 4 bytes + if(hasMappings && data.cesLength != 0) { + // Sum of the sizes of the data items which are + // not automatically multiples of 8 bytes and which are placed before the CEs. + int32_t sum = headerSize + (indexesLength + reorderCodesLength) * 4; + if((sum & 7) != 0) { + // We need to add padding somewhere so that the 64-bit CEs are 8-aligned. + // We add to the header size here. + // Alternatively, we could increment the indexesLength + // or add a few bytes to the reorderTable. + headerSize += 4; + } + } + header.dataHeader.headerSize = (uint16_t)headerSize; + if(headerSize <= capacity) { + uprv_memcpy(dest, &header, sizeof(header)); + // Write 00 bytes so that the padding is not mistaken for a copyright string. + uprv_memset(dest + sizeof(header), 0, headerSize - (int32_t)sizeof(header)); + dest += headerSize; + capacity -= headerSize; + } else { + dest = nullptr; + capacity = 0; + } + } + + indexes[CollationDataReader::IX_INDEXES_LENGTH] = indexesLength; + U_ASSERT((settings.options & ~0xffff) == 0); + indexes[CollationDataReader::IX_OPTIONS] = + data.numericPrimary | fastLatinVersion | settings.options; + indexes[CollationDataReader::IX_RESERVED2] = 0; + indexes[CollationDataReader::IX_RESERVED3] = 0; + + // Byte offsets of data items all start from the start of the indexes. + // We add the headerSize at the very end. + int32_t totalSize = indexesLength * 4; + + if(hasMappings && (isBase || data.jamoCE32s != baseData->jamoCE32s)) { + indexes[CollationDataReader::IX_JAMO_CE32S_START] = static_cast(data.jamoCE32s - data.ce32s); + } else { + indexes[CollationDataReader::IX_JAMO_CE32S_START] = -1; + } + + indexes[CollationDataReader::IX_REORDER_CODES_OFFSET] = totalSize; + totalSize += reorderCodesLength * 4; + + indexes[CollationDataReader::IX_REORDER_TABLE_OFFSET] = totalSize; + if(settings.reorderTable != nullptr) { + totalSize += 256; + } + + indexes[CollationDataReader::IX_TRIE_OFFSET] = totalSize; + if(hasMappings) { + UErrorCode errorCode2 = U_ZERO_ERROR; + int32_t length; + if(totalSize < capacity) { + length = utrie2_serialize(data.trie, dest + totalSize, + capacity - totalSize, &errorCode2); + } else { + length = utrie2_serialize(data.trie, nullptr, 0, &errorCode2); + } + if(U_FAILURE(errorCode2) && errorCode2 != U_BUFFER_OVERFLOW_ERROR) { + errorCode = errorCode2; + return 0; + } + // The trie size should be a multiple of 8 bytes due to the way + // compactIndex2(UNewTrie2 *trie) currently works. + U_ASSERT((length & 7) == 0); + totalSize += length; + } + + indexes[CollationDataReader::IX_RESERVED8_OFFSET] = totalSize; + indexes[CollationDataReader::IX_CES_OFFSET] = totalSize; + if(hasMappings && data.cesLength != 0) { + U_ASSERT(((headerSize + totalSize) & 7) == 0); + totalSize += data.cesLength * 8; + } + + indexes[CollationDataReader::IX_RESERVED10_OFFSET] = totalSize; + indexes[CollationDataReader::IX_CE32S_OFFSET] = totalSize; + if(hasMappings) { + totalSize += data.ce32sLength * 4; + } + + indexes[CollationDataReader::IX_ROOT_ELEMENTS_OFFSET] = totalSize; + totalSize += rootElementsLength * 4; + + indexes[CollationDataReader::IX_CONTEXTS_OFFSET] = totalSize; + if(hasMappings) { + totalSize += data.contextsLength * 2; + } + + indexes[CollationDataReader::IX_UNSAFE_BWD_OFFSET] = totalSize; + if(hasMappings && !unsafeBackwardSet.isEmpty()) { + UErrorCode errorCode2 = U_ZERO_ERROR; + int32_t length; + if(totalSize < capacity) { + uint16_t *p = reinterpret_cast(dest + totalSize); + length = unsafeBackwardSet.serialize( + p, (capacity - totalSize) / 2, errorCode2); + } else { + length = unsafeBackwardSet.serialize(nullptr, 0, errorCode2); + } + if(U_FAILURE(errorCode2) && errorCode2 != U_BUFFER_OVERFLOW_ERROR) { + errorCode = errorCode2; + return 0; + } + totalSize += length * 2; + } + + indexes[CollationDataReader::IX_FAST_LATIN_TABLE_OFFSET] = totalSize; + totalSize += fastLatinTableLength * 2; + + UnicodeString scripts; + indexes[CollationDataReader::IX_SCRIPTS_OFFSET] = totalSize; + if(isBase) { + scripts.append((char16_t)data.numScripts); + scripts.append(reinterpret_cast(data.scriptsIndex), data.numScripts + 16); + scripts.append(reinterpret_cast(data.scriptStarts), data.scriptStartsLength); + totalSize += scripts.length() * 2; + } + + indexes[CollationDataReader::IX_COMPRESSIBLE_BYTES_OFFSET] = totalSize; + if(isBase) { + totalSize += 256; + } + + indexes[CollationDataReader::IX_RESERVED18_OFFSET] = totalSize; + indexes[CollationDataReader::IX_TOTAL_SIZE] = totalSize; + + if(totalSize > capacity) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return headerSize + totalSize; + } + + uprv_memcpy(dest, indexes, indexesLength * 4); + copyData(indexes, CollationDataReader::IX_REORDER_CODES_OFFSET, reorderCodes, dest); + copyData(indexes, CollationDataReader::IX_REORDER_TABLE_OFFSET, settings.reorderTable, dest); + // The trie has already been serialized into the dest buffer. + copyData(indexes, CollationDataReader::IX_CES_OFFSET, data.ces, dest); + copyData(indexes, CollationDataReader::IX_CE32S_OFFSET, data.ce32s, dest); + copyData(indexes, CollationDataReader::IX_ROOT_ELEMENTS_OFFSET, rootElements, dest); + copyData(indexes, CollationDataReader::IX_CONTEXTS_OFFSET, data.contexts, dest); + // The unsafeBackwardSet has already been serialized into the dest buffer. + copyData(indexes, CollationDataReader::IX_FAST_LATIN_TABLE_OFFSET, data.fastLatinTable, dest); + copyData(indexes, CollationDataReader::IX_SCRIPTS_OFFSET, scripts.getBuffer(), dest); + copyData(indexes, CollationDataReader::IX_COMPRESSIBLE_BYTES_OFFSET, data.compressibleBytes, dest); + + return headerSize + totalSize; +} + +void +CollationDataWriter::copyData(const int32_t indexes[], int32_t startIndex, + const void *src, uint8_t *dest) { + int32_t start = indexes[startIndex]; + int32_t limit = indexes[startIndex + 1]; + if(start < limit) { + uprv_memcpy(dest + start, src, limit - start); + } +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationdatawriter.h b/deps/icu-small/source/i18n/collationdatawriter.h index 6ba9a9c2c766fb..2ea41bcdf05583 100644 --- a/deps/icu-small/source/i18n/collationdatawriter.h +++ b/deps/icu-small/source/i18n/collationdatawriter.h @@ -1,57 +1,57 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationdatawriter.h -* -* created on: 2013aug06 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONDATAWRITER_H__ -#define __COLLATIONDATAWRITER_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -U_NAMESPACE_BEGIN - -struct CollationData; -struct CollationSettings; -struct CollationTailoring; - -/** - * Collation-related code for tools & demos. - */ -class U_I18N_API CollationDataWriter /* all static */ { -public: - static int32_t writeBase(const CollationData &data, const CollationSettings &settings, - const void *rootElements, int32_t rootElementsLength, - int32_t indexes[], uint8_t *dest, int32_t capacity, - UErrorCode &errorCode); - - static int32_t writeTailoring(const CollationTailoring &t, const CollationSettings &settings, - int32_t indexes[], uint8_t *dest, int32_t capacity, - UErrorCode &errorCode); - -private: - CollationDataWriter() = delete; // no constructor - - static int32_t write(UBool isBase, const UVersionInfo dataVersion, - const CollationData &data, const CollationSettings &settings, - const void *rootElements, int32_t rootElementsLength, - int32_t indexes[], uint8_t *dest, int32_t capacity, - UErrorCode &errorCode); - - static void copyData(const int32_t indexes[], int32_t startIndex, - const void *src, uint8_t *dest); -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONDATAWRITER_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationdatawriter.h +* +* created on: 2013aug06 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONDATAWRITER_H__ +#define __COLLATIONDATAWRITER_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +U_NAMESPACE_BEGIN + +struct CollationData; +struct CollationSettings; +struct CollationTailoring; + +/** + * Collation-related code for tools & demos. + */ +class U_I18N_API CollationDataWriter /* all static */ { +public: + static int32_t writeBase(const CollationData &data, const CollationSettings &settings, + const void *rootElements, int32_t rootElementsLength, + int32_t indexes[], uint8_t *dest, int32_t capacity, + UErrorCode &errorCode); + + static int32_t writeTailoring(const CollationTailoring &t, const CollationSettings &settings, + int32_t indexes[], uint8_t *dest, int32_t capacity, + UErrorCode &errorCode); + +private: + CollationDataWriter() = delete; // no constructor + + static int32_t write(UBool isBase, const UVersionInfo dataVersion, + const CollationData &data, const CollationSettings &settings, + const void *rootElements, int32_t rootElementsLength, + int32_t indexes[], uint8_t *dest, int32_t capacity, + UErrorCode &errorCode); + + static void copyData(const int32_t indexes[], int32_t startIndex, + const void *src, uint8_t *dest); +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONDATAWRITER_H__ diff --git a/deps/icu-small/source/i18n/collationfastlatin.cpp b/deps/icu-small/source/i18n/collationfastlatin.cpp index 35cf60e815150b..a2e0c4dd10cac6 100644 --- a/deps/icu-small/source/i18n/collationfastlatin.cpp +++ b/deps/icu-small/source/i18n/collationfastlatin.cpp @@ -1,1099 +1,1099 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationfastlatin.cpp -* -* created on: 2013aug18 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/ucol.h" -#include "collationdata.h" -#include "collationfastlatin.h" -#include "collationsettings.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - -int32_t -CollationFastLatin::getOptions(const CollationData *data, const CollationSettings &settings, - uint16_t *primaries, int32_t capacity) { - const uint16_t *table = data->fastLatinTable; - if(table == NULL) { return -1; } - U_ASSERT(capacity == LATIN_LIMIT); - if(capacity != LATIN_LIMIT) { return -1; } - - uint32_t miniVarTop; - if((settings.options & CollationSettings::ALTERNATE_MASK) == 0) { - // No mini primaries are variable, set a variableTop just below the - // lowest long mini primary. - miniVarTop = MIN_LONG - 1; - } else { - int32_t headerLength = *table & 0xff; - int32_t i = 1 + settings.getMaxVariable(); - if(i >= headerLength) { - return -1; // variableTop >= digits, should not occur - } - miniVarTop = table[i]; - } - - UBool digitsAreReordered = false; - if(settings.hasReordering()) { - uint32_t prevStart = 0; - uint32_t beforeDigitStart = 0; - uint32_t digitStart = 0; - uint32_t afterDigitStart = 0; - for(int32_t group = UCOL_REORDER_CODE_FIRST; - group < UCOL_REORDER_CODE_FIRST + CollationData::MAX_NUM_SPECIAL_REORDER_CODES; - ++group) { - uint32_t start = data->getFirstPrimaryForGroup(group); - start = settings.reorder(start); - if(group == UCOL_REORDER_CODE_DIGIT) { - beforeDigitStart = prevStart; - digitStart = start; - } else if(start != 0) { - if(start < prevStart) { - // The permutation affects the groups up to Latin. - return -1; - } - // In the future, there might be a special group between digits & Latin. - if(digitStart != 0 && afterDigitStart == 0 && prevStart == beforeDigitStart) { - afterDigitStart = start; - } - prevStart = start; - } - } - uint32_t latinStart = data->getFirstPrimaryForGroup(USCRIPT_LATIN); - latinStart = settings.reorder(latinStart); - if(latinStart < prevStart) { - return -1; - } - if(afterDigitStart == 0) { - afterDigitStart = latinStart; - } - if(!(beforeDigitStart < digitStart && digitStart < afterDigitStart)) { - digitsAreReordered = true; - } - } - - table += (table[0] & 0xff); // skip the header - for(UChar32 c = 0; c < LATIN_LIMIT; ++c) { - uint32_t p = table[c]; - if(p >= MIN_SHORT) { - p &= SHORT_PRIMARY_MASK; - } else if(p > miniVarTop) { - p &= LONG_PRIMARY_MASK; - } else { - p = 0; - } - primaries[c] = (uint16_t)p; - } - if(digitsAreReordered || (settings.options & CollationSettings::NUMERIC) != 0) { - // Bail out for digits. - for(UChar32 c = 0x30; c <= 0x39; ++c) { primaries[c] = 0; } - } - - // Shift the miniVarTop above other options. - return ((int32_t)miniVarTop << 16) | settings.options; -} - -int32_t -CollationFastLatin::compareUTF16(const uint16_t *table, const uint16_t *primaries, int32_t options, - const UChar *left, int32_t leftLength, - const UChar *right, int32_t rightLength) { - // This is a modified copy of CollationCompare::compareUpToQuaternary(), - // optimized for common Latin text. - // Keep them in sync! - // Keep compareUTF16() and compareUTF8() in sync very closely! - - U_ASSERT((table[0] >> 8) == VERSION); - table += (table[0] & 0xff); // skip the header - uint32_t variableTop = (uint32_t)options >> 16; // see getOptions() - options &= 0xffff; // needed for CollationSettings::getStrength() to work - - // Check for supported characters, fetch mini CEs, and compare primaries. - int32_t leftIndex = 0, rightIndex = 0; - /** - * Single mini CE or a pair. - * The current mini CE is in the lower 16 bits, the next one is in the upper 16 bits. - * If there is only one, then it is in the lower bits, and the upper bits are 0. - */ - uint32_t leftPair = 0, rightPair = 0; - for(;;) { - // We fetch CEs until we get a non-ignorable primary or reach the end. - while(leftPair == 0) { - if(leftIndex == leftLength) { - leftPair = EOS; - break; - } - UChar32 c = left[leftIndex++]; - if(c <= LATIN_MAX) { - leftPair = primaries[c]; - if(leftPair != 0) { break; } - if(c <= 0x39 && c >= 0x30 && (options & CollationSettings::NUMERIC) != 0) { - return BAIL_OUT_RESULT; - } - leftPair = table[c]; - } else if(PUNCT_START <= c && c < PUNCT_LIMIT) { - leftPair = table[c - PUNCT_START + LATIN_LIMIT]; - } else { - leftPair = lookup(table, c); - } - if(leftPair >= MIN_SHORT) { - leftPair &= SHORT_PRIMARY_MASK; - break; - } else if(leftPair > variableTop) { - leftPair &= LONG_PRIMARY_MASK; - break; - } else { - leftPair = nextPair(table, c, leftPair, left, NULL, leftIndex, leftLength); - if(leftPair == BAIL_OUT) { return BAIL_OUT_RESULT; } - leftPair = getPrimaries(variableTop, leftPair); - } - } - - while(rightPair == 0) { - if(rightIndex == rightLength) { - rightPair = EOS; - break; - } - UChar32 c = right[rightIndex++]; - if(c <= LATIN_MAX) { - rightPair = primaries[c]; - if(rightPair != 0) { break; } - if(c <= 0x39 && c >= 0x30 && (options & CollationSettings::NUMERIC) != 0) { - return BAIL_OUT_RESULT; - } - rightPair = table[c]; - } else if(PUNCT_START <= c && c < PUNCT_LIMIT) { - rightPair = table[c - PUNCT_START + LATIN_LIMIT]; - } else { - rightPair = lookup(table, c); - } - if(rightPair >= MIN_SHORT) { - rightPair &= SHORT_PRIMARY_MASK; - break; - } else if(rightPair > variableTop) { - rightPair &= LONG_PRIMARY_MASK; - break; - } else { - rightPair = nextPair(table, c, rightPair, right, NULL, rightIndex, rightLength); - if(rightPair == BAIL_OUT) { return BAIL_OUT_RESULT; } - rightPair = getPrimaries(variableTop, rightPair); - } - } - - if(leftPair == rightPair) { - if(leftPair == EOS) { break; } - leftPair = rightPair = 0; - continue; - } - uint32_t leftPrimary = leftPair & 0xffff; - uint32_t rightPrimary = rightPair & 0xffff; - if(leftPrimary != rightPrimary) { - // Return the primary difference. - return (leftPrimary < rightPrimary) ? UCOL_LESS : UCOL_GREATER; - } - if(leftPair == EOS) { break; } - leftPair >>= 16; - rightPair >>= 16; - } - // In the following, we need to re-fetch each character because we did not buffer the CEs, - // but we know that the string is well-formed and - // only contains supported characters and mappings. - - // We might skip the secondary level but continue with the case level - // which is turned on separately. - if(CollationSettings::getStrength(options) >= UCOL_SECONDARY) { - leftIndex = rightIndex = 0; - leftPair = rightPair = 0; - for(;;) { - while(leftPair == 0) { - if(leftIndex == leftLength) { - leftPair = EOS; - break; - } - UChar32 c = left[leftIndex++]; - if(c <= LATIN_MAX) { - leftPair = table[c]; - } else if(PUNCT_START <= c && c < PUNCT_LIMIT) { - leftPair = table[c - PUNCT_START + LATIN_LIMIT]; - } else { - leftPair = lookup(table, c); - } - if(leftPair >= MIN_SHORT) { - leftPair = getSecondariesFromOneShortCE(leftPair); - break; - } else if(leftPair > variableTop) { - leftPair = COMMON_SEC_PLUS_OFFSET; - break; - } else { - leftPair = nextPair(table, c, leftPair, left, NULL, leftIndex, leftLength); - leftPair = getSecondaries(variableTop, leftPair); - } - } - - while(rightPair == 0) { - if(rightIndex == rightLength) { - rightPair = EOS; - break; - } - UChar32 c = right[rightIndex++]; - if(c <= LATIN_MAX) { - rightPair = table[c]; - } else if(PUNCT_START <= c && c < PUNCT_LIMIT) { - rightPair = table[c - PUNCT_START + LATIN_LIMIT]; - } else { - rightPair = lookup(table, c); - } - if(rightPair >= MIN_SHORT) { - rightPair = getSecondariesFromOneShortCE(rightPair); - break; - } else if(rightPair > variableTop) { - rightPair = COMMON_SEC_PLUS_OFFSET; - break; - } else { - rightPair = nextPair(table, c, rightPair, right, NULL, rightIndex, rightLength); - rightPair = getSecondaries(variableTop, rightPair); - } - } - - if(leftPair == rightPair) { - if(leftPair == EOS) { break; } - leftPair = rightPair = 0; - continue; - } - uint32_t leftSecondary = leftPair & 0xffff; - uint32_t rightSecondary = rightPair & 0xffff; - if(leftSecondary != rightSecondary) { - if((options & CollationSettings::BACKWARD_SECONDARY) != 0) { - // Full support for backwards secondary requires backwards contraction matching - // and moving backwards between merge separators. - return BAIL_OUT_RESULT; - } - return (leftSecondary < rightSecondary) ? UCOL_LESS : UCOL_GREATER; - } - if(leftPair == EOS) { break; } - leftPair >>= 16; - rightPair >>= 16; - } - } - - if((options & CollationSettings::CASE_LEVEL) != 0) { - UBool strengthIsPrimary = CollationSettings::getStrength(options) == UCOL_PRIMARY; - leftIndex = rightIndex = 0; - leftPair = rightPair = 0; - for(;;) { - while(leftPair == 0) { - if(leftIndex == leftLength) { - leftPair = EOS; - break; - } - UChar32 c = left[leftIndex++]; - leftPair = (c <= LATIN_MAX) ? table[c] : lookup(table, c); - if(leftPair < MIN_LONG) { - leftPair = nextPair(table, c, leftPair, left, NULL, leftIndex, leftLength); - } - leftPair = getCases(variableTop, strengthIsPrimary, leftPair); - } - - while(rightPair == 0) { - if(rightIndex == rightLength) { - rightPair = EOS; - break; - } - UChar32 c = right[rightIndex++]; - rightPair = (c <= LATIN_MAX) ? table[c] : lookup(table, c); - if(rightPair < MIN_LONG) { - rightPair = nextPair(table, c, rightPair, right, NULL, rightIndex, rightLength); - } - rightPair = getCases(variableTop, strengthIsPrimary, rightPair); - } - - if(leftPair == rightPair) { - if(leftPair == EOS) { break; } - leftPair = rightPair = 0; - continue; - } - uint32_t leftCase = leftPair & 0xffff; - uint32_t rightCase = rightPair & 0xffff; - if(leftCase != rightCase) { - if((options & CollationSettings::UPPER_FIRST) == 0) { - return (leftCase < rightCase) ? UCOL_LESS : UCOL_GREATER; - } else { - return (leftCase < rightCase) ? UCOL_GREATER : UCOL_LESS; - } - } - if(leftPair == EOS) { break; } - leftPair >>= 16; - rightPair >>= 16; - } - } - if(CollationSettings::getStrength(options) <= UCOL_SECONDARY) { return UCOL_EQUAL; } - - // Remove the case bits from the tertiary weight when caseLevel is on or caseFirst is off. - UBool withCaseBits = CollationSettings::isTertiaryWithCaseBits(options); - - leftIndex = rightIndex = 0; - leftPair = rightPair = 0; - for(;;) { - while(leftPair == 0) { - if(leftIndex == leftLength) { - leftPair = EOS; - break; - } - UChar32 c = left[leftIndex++]; - leftPair = (c <= LATIN_MAX) ? table[c] : lookup(table, c); - if(leftPair < MIN_LONG) { - leftPair = nextPair(table, c, leftPair, left, NULL, leftIndex, leftLength); - } - leftPair = getTertiaries(variableTop, withCaseBits, leftPair); - } - - while(rightPair == 0) { - if(rightIndex == rightLength) { - rightPair = EOS; - break; - } - UChar32 c = right[rightIndex++]; - rightPair = (c <= LATIN_MAX) ? table[c] : lookup(table, c); - if(rightPair < MIN_LONG) { - rightPair = nextPair(table, c, rightPair, right, NULL, rightIndex, rightLength); - } - rightPair = getTertiaries(variableTop, withCaseBits, rightPair); - } - - if(leftPair == rightPair) { - if(leftPair == EOS) { break; } - leftPair = rightPair = 0; - continue; - } - uint32_t leftTertiary = leftPair & 0xffff; - uint32_t rightTertiary = rightPair & 0xffff; - if(leftTertiary != rightTertiary) { - if(CollationSettings::sortsTertiaryUpperCaseFirst(options)) { - // Pass through EOS and MERGE_WEIGHT - // and keep real tertiary weights larger than the MERGE_WEIGHT. - // Tertiary CEs (secondary ignorables) are not supported in fast Latin. - if(leftTertiary > MERGE_WEIGHT) { - leftTertiary ^= CASE_MASK; - } - if(rightTertiary > MERGE_WEIGHT) { - rightTertiary ^= CASE_MASK; - } - } - return (leftTertiary < rightTertiary) ? UCOL_LESS : UCOL_GREATER; - } - if(leftPair == EOS) { break; } - leftPair >>= 16; - rightPair >>= 16; - } - if(CollationSettings::getStrength(options) <= UCOL_TERTIARY) { return UCOL_EQUAL; } - - leftIndex = rightIndex = 0; - leftPair = rightPair = 0; - for(;;) { - while(leftPair == 0) { - if(leftIndex == leftLength) { - leftPair = EOS; - break; - } - UChar32 c = left[leftIndex++]; - leftPair = (c <= LATIN_MAX) ? table[c] : lookup(table, c); - if(leftPair < MIN_LONG) { - leftPair = nextPair(table, c, leftPair, left, NULL, leftIndex, leftLength); - } - leftPair = getQuaternaries(variableTop, leftPair); - } - - while(rightPair == 0) { - if(rightIndex == rightLength) { - rightPair = EOS; - break; - } - UChar32 c = right[rightIndex++]; - rightPair = (c <= LATIN_MAX) ? table[c] : lookup(table, c); - if(rightPair < MIN_LONG) { - rightPair = nextPair(table, c, rightPair, right, NULL, rightIndex, rightLength); - } - rightPair = getQuaternaries(variableTop, rightPair); - } - - if(leftPair == rightPair) { - if(leftPair == EOS) { break; } - leftPair = rightPair = 0; - continue; - } - uint32_t leftQuaternary = leftPair & 0xffff; - uint32_t rightQuaternary = rightPair & 0xffff; - if(leftQuaternary != rightQuaternary) { - return (leftQuaternary < rightQuaternary) ? UCOL_LESS : UCOL_GREATER; - } - if(leftPair == EOS) { break; } - leftPair >>= 16; - rightPair >>= 16; - } - return UCOL_EQUAL; -} - -int32_t -CollationFastLatin::compareUTF8(const uint16_t *table, const uint16_t *primaries, int32_t options, - const uint8_t *left, int32_t leftLength, - const uint8_t *right, int32_t rightLength) { - // Keep compareUTF16() and compareUTF8() in sync very closely! - - U_ASSERT((table[0] >> 8) == VERSION); - table += (table[0] & 0xff); // skip the header - uint32_t variableTop = (uint32_t)options >> 16; // see RuleBasedCollator::getFastLatinOptions() - options &= 0xffff; // needed for CollationSettings::getStrength() to work - - // Check for supported characters, fetch mini CEs, and compare primaries. - int32_t leftIndex = 0, rightIndex = 0; - /** - * Single mini CE or a pair. - * The current mini CE is in the lower 16 bits, the next one is in the upper 16 bits. - * If there is only one, then it is in the lower bits, and the upper bits are 0. - */ - uint32_t leftPair = 0, rightPair = 0; - // Note: There is no need to assemble the code point. - // We only need to look up the table entry for the character, - // and nextPair() looks for whether c==0. - for(;;) { - // We fetch CEs until we get a non-ignorable primary or reach the end. - while(leftPair == 0) { - if(leftIndex == leftLength) { - leftPair = EOS; - break; - } - UChar32 c = left[leftIndex++]; - uint8_t t; - if(c <= 0x7f) { - leftPair = primaries[c]; - if(leftPair != 0) { break; } - if(c <= 0x39 && c >= 0x30 && (options & CollationSettings::NUMERIC) != 0) { - return BAIL_OUT_RESULT; - } - leftPair = table[c]; - } else if(c <= LATIN_MAX_UTF8_LEAD && 0xc2 <= c && leftIndex != leftLength && - 0x80 <= (t = left[leftIndex]) && t <= 0xbf) { - ++leftIndex; - c = ((c - 0xc2) << 6) + t; - leftPair = primaries[c]; - if(leftPair != 0) { break; } - leftPair = table[c]; - } else { - leftPair = lookupUTF8(table, c, left, leftIndex, leftLength); - } - if(leftPair >= MIN_SHORT) { - leftPair &= SHORT_PRIMARY_MASK; - break; - } else if(leftPair > variableTop) { - leftPair &= LONG_PRIMARY_MASK; - break; - } else { - leftPair = nextPair(table, c, leftPair, NULL, left, leftIndex, leftLength); - if(leftPair == BAIL_OUT) { return BAIL_OUT_RESULT; } - leftPair = getPrimaries(variableTop, leftPair); - } - } - - while(rightPair == 0) { - if(rightIndex == rightLength) { - rightPair = EOS; - break; - } - UChar32 c = right[rightIndex++]; - uint8_t t; - if(c <= 0x7f) { - rightPair = primaries[c]; - if(rightPair != 0) { break; } - if(c <= 0x39 && c >= 0x30 && (options & CollationSettings::NUMERIC) != 0) { - return BAIL_OUT_RESULT; - } - rightPair = table[c]; - } else if(c <= LATIN_MAX_UTF8_LEAD && 0xc2 <= c && rightIndex != rightLength && - 0x80 <= (t = right[rightIndex]) && t <= 0xbf) { - ++rightIndex; - c = ((c - 0xc2) << 6) + t; - rightPair = primaries[c]; - if(rightPair != 0) { break; } - rightPair = table[c]; - } else { - rightPair = lookupUTF8(table, c, right, rightIndex, rightLength); - } - if(rightPair >= MIN_SHORT) { - rightPair &= SHORT_PRIMARY_MASK; - break; - } else if(rightPair > variableTop) { - rightPair &= LONG_PRIMARY_MASK; - break; - } else { - rightPair = nextPair(table, c, rightPair, NULL, right, rightIndex, rightLength); - if(rightPair == BAIL_OUT) { return BAIL_OUT_RESULT; } - rightPair = getPrimaries(variableTop, rightPair); - } - } - - if(leftPair == rightPair) { - if(leftPair == EOS) { break; } - leftPair = rightPair = 0; - continue; - } - uint32_t leftPrimary = leftPair & 0xffff; - uint32_t rightPrimary = rightPair & 0xffff; - if(leftPrimary != rightPrimary) { - // Return the primary difference. - return (leftPrimary < rightPrimary) ? UCOL_LESS : UCOL_GREATER; - } - if(leftPair == EOS) { break; } - leftPair >>= 16; - rightPair >>= 16; - } - // In the following, we need to re-fetch each character because we did not buffer the CEs, - // but we know that the string is well-formed and - // only contains supported characters and mappings. - - // We might skip the secondary level but continue with the case level - // which is turned on separately. - if(CollationSettings::getStrength(options) >= UCOL_SECONDARY) { - leftIndex = rightIndex = 0; - leftPair = rightPair = 0; - for(;;) { - while(leftPair == 0) { - if(leftIndex == leftLength) { - leftPair = EOS; - break; - } - UChar32 c = left[leftIndex++]; - if(c <= 0x7f) { - leftPair = table[c]; - } else if(c <= LATIN_MAX_UTF8_LEAD) { - leftPair = table[((c - 0xc2) << 6) + left[leftIndex++]]; - } else { - leftPair = lookupUTF8Unsafe(table, c, left, leftIndex); - } - if(leftPair >= MIN_SHORT) { - leftPair = getSecondariesFromOneShortCE(leftPair); - break; - } else if(leftPair > variableTop) { - leftPair = COMMON_SEC_PLUS_OFFSET; - break; - } else { - leftPair = nextPair(table, c, leftPair, NULL, left, leftIndex, leftLength); - leftPair = getSecondaries(variableTop, leftPair); - } - } - - while(rightPair == 0) { - if(rightIndex == rightLength) { - rightPair = EOS; - break; - } - UChar32 c = right[rightIndex++]; - if(c <= 0x7f) { - rightPair = table[c]; - } else if(c <= LATIN_MAX_UTF8_LEAD) { - rightPair = table[((c - 0xc2) << 6) + right[rightIndex++]]; - } else { - rightPair = lookupUTF8Unsafe(table, c, right, rightIndex); - } - if(rightPair >= MIN_SHORT) { - rightPair = getSecondariesFromOneShortCE(rightPair); - break; - } else if(rightPair > variableTop) { - rightPair = COMMON_SEC_PLUS_OFFSET; - break; - } else { - rightPair = nextPair(table, c, rightPair, NULL, right, rightIndex, rightLength); - rightPair = getSecondaries(variableTop, rightPair); - } - } - - if(leftPair == rightPair) { - if(leftPair == EOS) { break; } - leftPair = rightPair = 0; - continue; - } - uint32_t leftSecondary = leftPair & 0xffff; - uint32_t rightSecondary = rightPair & 0xffff; - if(leftSecondary != rightSecondary) { - if((options & CollationSettings::BACKWARD_SECONDARY) != 0) { - // Full support for backwards secondary requires backwards contraction matching - // and moving backwards between merge separators. - return BAIL_OUT_RESULT; - } - return (leftSecondary < rightSecondary) ? UCOL_LESS : UCOL_GREATER; - } - if(leftPair == EOS) { break; } - leftPair >>= 16; - rightPair >>= 16; - } - } - - if((options & CollationSettings::CASE_LEVEL) != 0) { - UBool strengthIsPrimary = CollationSettings::getStrength(options) == UCOL_PRIMARY; - leftIndex = rightIndex = 0; - leftPair = rightPair = 0; - for(;;) { - while(leftPair == 0) { - if(leftIndex == leftLength) { - leftPair = EOS; - break; - } - UChar32 c = left[leftIndex++]; - leftPair = (c <= 0x7f) ? table[c] : lookupUTF8Unsafe(table, c, left, leftIndex); - if(leftPair < MIN_LONG) { - leftPair = nextPair(table, c, leftPair, NULL, left, leftIndex, leftLength); - } - leftPair = getCases(variableTop, strengthIsPrimary, leftPair); - } - - while(rightPair == 0) { - if(rightIndex == rightLength) { - rightPair = EOS; - break; - } - UChar32 c = right[rightIndex++]; - rightPair = (c <= 0x7f) ? table[c] : lookupUTF8Unsafe(table, c, right, rightIndex); - if(rightPair < MIN_LONG) { - rightPair = nextPair(table, c, rightPair, NULL, right, rightIndex, rightLength); - } - rightPair = getCases(variableTop, strengthIsPrimary, rightPair); - } - - if(leftPair == rightPair) { - if(leftPair == EOS) { break; } - leftPair = rightPair = 0; - continue; - } - uint32_t leftCase = leftPair & 0xffff; - uint32_t rightCase = rightPair & 0xffff; - if(leftCase != rightCase) { - if((options & CollationSettings::UPPER_FIRST) == 0) { - return (leftCase < rightCase) ? UCOL_LESS : UCOL_GREATER; - } else { - return (leftCase < rightCase) ? UCOL_GREATER : UCOL_LESS; - } - } - if(leftPair == EOS) { break; } - leftPair >>= 16; - rightPair >>= 16; - } - } - if(CollationSettings::getStrength(options) <= UCOL_SECONDARY) { return UCOL_EQUAL; } - - // Remove the case bits from the tertiary weight when caseLevel is on or caseFirst is off. - UBool withCaseBits = CollationSettings::isTertiaryWithCaseBits(options); - - leftIndex = rightIndex = 0; - leftPair = rightPair = 0; - for(;;) { - while(leftPair == 0) { - if(leftIndex == leftLength) { - leftPair = EOS; - break; - } - UChar32 c = left[leftIndex++]; - leftPair = (c <= 0x7f) ? table[c] : lookupUTF8Unsafe(table, c, left, leftIndex); - if(leftPair < MIN_LONG) { - leftPair = nextPair(table, c, leftPair, NULL, left, leftIndex, leftLength); - } - leftPair = getTertiaries(variableTop, withCaseBits, leftPair); - } - - while(rightPair == 0) { - if(rightIndex == rightLength) { - rightPair = EOS; - break; - } - UChar32 c = right[rightIndex++]; - rightPair = (c <= 0x7f) ? table[c] : lookupUTF8Unsafe(table, c, right, rightIndex); - if(rightPair < MIN_LONG) { - rightPair = nextPair(table, c, rightPair, NULL, right, rightIndex, rightLength); - } - rightPair = getTertiaries(variableTop, withCaseBits, rightPair); - } - - if(leftPair == rightPair) { - if(leftPair == EOS) { break; } - leftPair = rightPair = 0; - continue; - } - uint32_t leftTertiary = leftPair & 0xffff; - uint32_t rightTertiary = rightPair & 0xffff; - if(leftTertiary != rightTertiary) { - if(CollationSettings::sortsTertiaryUpperCaseFirst(options)) { - // Pass through EOS and MERGE_WEIGHT - // and keep real tertiary weights larger than the MERGE_WEIGHT. - // Tertiary CEs (secondary ignorables) are not supported in fast Latin. - if(leftTertiary > MERGE_WEIGHT) { - leftTertiary ^= CASE_MASK; - } - if(rightTertiary > MERGE_WEIGHT) { - rightTertiary ^= CASE_MASK; - } - } - return (leftTertiary < rightTertiary) ? UCOL_LESS : UCOL_GREATER; - } - if(leftPair == EOS) { break; } - leftPair >>= 16; - rightPair >>= 16; - } - if(CollationSettings::getStrength(options) <= UCOL_TERTIARY) { return UCOL_EQUAL; } - - leftIndex = rightIndex = 0; - leftPair = rightPair = 0; - for(;;) { - while(leftPair == 0) { - if(leftIndex == leftLength) { - leftPair = EOS; - break; - } - UChar32 c = left[leftIndex++]; - leftPair = (c <= 0x7f) ? table[c] : lookupUTF8Unsafe(table, c, left, leftIndex); - if(leftPair < MIN_LONG) { - leftPair = nextPair(table, c, leftPair, NULL, left, leftIndex, leftLength); - } - leftPair = getQuaternaries(variableTop, leftPair); - } - - while(rightPair == 0) { - if(rightIndex == rightLength) { - rightPair = EOS; - break; - } - UChar32 c = right[rightIndex++]; - rightPair = (c <= 0x7f) ? table[c] : lookupUTF8Unsafe(table, c, right, rightIndex); - if(rightPair < MIN_LONG) { - rightPair = nextPair(table, c, rightPair, NULL, right, rightIndex, rightLength); - } - rightPair = getQuaternaries(variableTop, rightPair); - } - - if(leftPair == rightPair) { - if(leftPair == EOS) { break; } - leftPair = rightPair = 0; - continue; - } - uint32_t leftQuaternary = leftPair & 0xffff; - uint32_t rightQuaternary = rightPair & 0xffff; - if(leftQuaternary != rightQuaternary) { - return (leftQuaternary < rightQuaternary) ? UCOL_LESS : UCOL_GREATER; - } - if(leftPair == EOS) { break; } - leftPair >>= 16; - rightPair >>= 16; - } - return UCOL_EQUAL; -} - -uint32_t -CollationFastLatin::lookup(const uint16_t *table, UChar32 c) { - U_ASSERT(c > LATIN_MAX); - if(PUNCT_START <= c && c < PUNCT_LIMIT) { - return table[c - PUNCT_START + LATIN_LIMIT]; - } else if(c == 0xfffe) { - return MERGE_WEIGHT; - } else if(c == 0xffff) { - return MAX_SHORT | COMMON_SEC | LOWER_CASE | COMMON_TER; - } else { - return BAIL_OUT; - } -} - -uint32_t -CollationFastLatin::lookupUTF8(const uint16_t *table, UChar32 c, - const uint8_t *s8, int32_t &sIndex, int32_t sLength) { - // The caller handled ASCII and valid/supported Latin. - U_ASSERT(c > 0x7f); - int32_t i2 = sIndex + 1; - if(i2 < sLength || sLength < 0) { - uint8_t t1 = s8[sIndex]; - uint8_t t2 = s8[i2]; - sIndex += 2; - if(c == 0xe2 && t1 == 0x80 && 0x80 <= t2 && t2 <= 0xbf) { - return table[(LATIN_LIMIT - 0x80) + t2]; // 2000..203F -> 0180..01BF - } else if(c == 0xef && t1 == 0xbf) { - if(t2 == 0xbe) { - return MERGE_WEIGHT; // U+FFFE - } else if(t2 == 0xbf) { - return MAX_SHORT | COMMON_SEC | LOWER_CASE | COMMON_TER; // U+FFFF - } - } - } - return BAIL_OUT; -} - -uint32_t -CollationFastLatin::lookupUTF8Unsafe(const uint16_t *table, UChar32 c, - const uint8_t *s8, int32_t &sIndex) { - // The caller handled ASCII. - // The string is well-formed and contains only supported characters. - U_ASSERT(c > 0x7f); - if(c <= LATIN_MAX_UTF8_LEAD) { - return table[((c - 0xc2) << 6) + s8[sIndex++]]; // 0080..017F - } - uint8_t t2 = s8[sIndex + 1]; - sIndex += 2; - if(c == 0xe2) { - return table[(LATIN_LIMIT - 0x80) + t2]; // 2000..203F -> 0180..01BF - } else if(t2 == 0xbe) { - return MERGE_WEIGHT; // U+FFFE - } else { - return MAX_SHORT | COMMON_SEC | LOWER_CASE | COMMON_TER; // U+FFFF - } -} - -uint32_t -CollationFastLatin::nextPair(const uint16_t *table, UChar32 c, uint32_t ce, - const UChar *s16, const uint8_t *s8, int32_t &sIndex, int32_t &sLength) { - if(ce >= MIN_LONG || ce < CONTRACTION) { - return ce; // simple or special mini CE - } else if(ce >= EXPANSION) { - int32_t index = NUM_FAST_CHARS + (ce & INDEX_MASK); - return ((uint32_t)table[index + 1] << 16) | table[index]; - } else /* ce >= CONTRACTION */ { - if(c == 0 && sLength < 0) { - sLength = sIndex - 1; - return EOS; - } - // Contraction list: Default mapping followed by - // 0 or more single-character contraction suffix mappings. - int32_t index = NUM_FAST_CHARS + (ce & INDEX_MASK); - if(sIndex != sLength) { - // Read the next character. - int32_t c2; - int32_t nextIndex = sIndex; - if(s16 != NULL) { - c2 = s16[nextIndex++]; - if(c2 > LATIN_MAX) { - if(PUNCT_START <= c2 && c2 < PUNCT_LIMIT) { - c2 = c2 - PUNCT_START + LATIN_LIMIT; // 2000..203F -> 0180..01BF - } else if(c2 == 0xfffe || c2 == 0xffff) { - c2 = -1; // U+FFFE & U+FFFF cannot occur in contractions. - } else { - return BAIL_OUT; - } - } - } else { - c2 = s8[nextIndex++]; - if(c2 > 0x7f) { - uint8_t t; - if(c2 <= 0xc5 && 0xc2 <= c2 && nextIndex != sLength && - 0x80 <= (t = s8[nextIndex]) && t <= 0xbf) { - c2 = ((c2 - 0xc2) << 6) + t; // 0080..017F - ++nextIndex; - } else { - int32_t i2 = nextIndex + 1; - if(i2 < sLength || sLength < 0) { - if(c2 == 0xe2 && s8[nextIndex] == 0x80 && - 0x80 <= (t = s8[i2]) && t <= 0xbf) { - c2 = (LATIN_LIMIT - 0x80) + t; // 2000..203F -> 0180..01BF - } else if(c2 == 0xef && s8[nextIndex] == 0xbf && - ((t = s8[i2]) == 0xbe || t == 0xbf)) { - c2 = -1; // U+FFFE & U+FFFF cannot occur in contractions. - } else { - return BAIL_OUT; - } - } else { - return BAIL_OUT; - } - nextIndex += 2; - } - } - } - if(c2 == 0 && sLength < 0) { - sLength = sIndex; - c2 = -1; - } - // Look for the next character in the contraction suffix list, - // which is in ascending order of single suffix characters. - int32_t i = index; - int32_t head = table[i]; // first skip the default mapping - int32_t x; - do { - i += head >> CONTR_LENGTH_SHIFT; - head = table[i]; - x = head & CONTR_CHAR_MASK; - } while(x < c2); - if(x == c2) { - index = i; - sIndex = nextIndex; - } - } - // Return the CE or CEs for the default or contraction mapping. - int32_t length = table[index] >> CONTR_LENGTH_SHIFT; - if(length == 1) { - return BAIL_OUT; - } - ce = table[index + 1]; - if(length == 2) { - return ce; - } else { - return ((uint32_t)table[index + 2] << 16) | ce; - } - } -} - -uint32_t -CollationFastLatin::getSecondaries(uint32_t variableTop, uint32_t pair) { - if(pair <= 0xffff) { - // one mini CE - if(pair >= MIN_SHORT) { - pair = getSecondariesFromOneShortCE(pair); - } else if(pair > variableTop) { - pair = COMMON_SEC_PLUS_OFFSET; - } else if(pair >= MIN_LONG) { - pair = 0; // variable - } - // else special mini CE - } else { - uint32_t ce = pair & 0xffff; - if(ce >= MIN_SHORT) { - pair = (pair & TWO_SECONDARIES_MASK) + TWO_SEC_OFFSETS; - } else if(ce > variableTop) { - pair = TWO_COMMON_SEC_PLUS_OFFSET; - } else { - U_ASSERT(ce >= MIN_LONG); - pair = 0; // variable - } - } - return pair; -} - -uint32_t -CollationFastLatin::getCases(uint32_t variableTop, UBool strengthIsPrimary, uint32_t pair) { - // Primary+caseLevel: Ignore case level weights of primary ignorables. - // Otherwise: Ignore case level weights of secondary ignorables. - // For details see the comments in the CollationCompare class. - // Tertiary CEs (secondary ignorables) are not supported in fast Latin. - if(pair <= 0xffff) { - // one mini CE - if(pair >= MIN_SHORT) { - // A high secondary weight means we really have two CEs, - // a primary CE and a secondary CE. - uint32_t ce = pair; - pair &= CASE_MASK; // explicit weight of primary CE - if(!strengthIsPrimary && (ce & SECONDARY_MASK) >= MIN_SEC_HIGH) { - pair |= LOWER_CASE << 16; // implied weight of secondary CE - } - } else if(pair > variableTop) { - pair = LOWER_CASE; - } else if(pair >= MIN_LONG) { - pair = 0; // variable - } - // else special mini CE - } else { - // two mini CEs, same primary groups, neither expands like above - uint32_t ce = pair & 0xffff; - if(ce >= MIN_SHORT) { - if(strengthIsPrimary && (pair & (SHORT_PRIMARY_MASK << 16)) == 0) { - pair &= CASE_MASK; - } else { - pair &= TWO_CASES_MASK; - } - } else if(ce > variableTop) { - pair = TWO_LOWER_CASES; - } else { - U_ASSERT(ce >= MIN_LONG); - pair = 0; // variable - } - } - return pair; -} - -uint32_t -CollationFastLatin::getTertiaries(uint32_t variableTop, UBool withCaseBits, uint32_t pair) { - if(pair <= 0xffff) { - // one mini CE - if(pair >= MIN_SHORT) { - // A high secondary weight means we really have two CEs, - // a primary CE and a secondary CE. - uint32_t ce = pair; - if(withCaseBits) { - pair = (pair & CASE_AND_TERTIARY_MASK) + TER_OFFSET; - if((ce & SECONDARY_MASK) >= MIN_SEC_HIGH) { - pair |= (LOWER_CASE | COMMON_TER_PLUS_OFFSET) << 16; - } - } else { - pair = (pair & TERTIARY_MASK) + TER_OFFSET; - if((ce & SECONDARY_MASK) >= MIN_SEC_HIGH) { - pair |= COMMON_TER_PLUS_OFFSET << 16; - } - } - } else if(pair > variableTop) { - pair = (pair & TERTIARY_MASK) + TER_OFFSET; - if(withCaseBits) { - pair |= LOWER_CASE; - } - } else if(pair >= MIN_LONG) { - pair = 0; // variable - } - // else special mini CE - } else { - // two mini CEs, same primary groups, neither expands like above - uint32_t ce = pair & 0xffff; - if(ce >= MIN_SHORT) { - if(withCaseBits) { - pair &= TWO_CASES_MASK | TWO_TERTIARIES_MASK; - } else { - pair &= TWO_TERTIARIES_MASK; - } - pair += TWO_TER_OFFSETS; - } else if(ce > variableTop) { - pair = (pair & TWO_TERTIARIES_MASK) + TWO_TER_OFFSETS; - if(withCaseBits) { - pair |= TWO_LOWER_CASES; - } - } else { - U_ASSERT(ce >= MIN_LONG); - pair = 0; // variable - } - } - return pair; -} - -uint32_t -CollationFastLatin::getQuaternaries(uint32_t variableTop, uint32_t pair) { - // Return the primary weight of a variable CE, - // or the maximum primary weight for a non-variable, not-completely-ignorable CE. - if(pair <= 0xffff) { - // one mini CE - if(pair >= MIN_SHORT) { - // A high secondary weight means we really have two CEs, - // a primary CE and a secondary CE. - if((pair & SECONDARY_MASK) >= MIN_SEC_HIGH) { - pair = TWO_SHORT_PRIMARIES_MASK; - } else { - pair = SHORT_PRIMARY_MASK; - } - } else if(pair > variableTop) { - pair = SHORT_PRIMARY_MASK; - } else if(pair >= MIN_LONG) { - pair &= LONG_PRIMARY_MASK; // variable - } - // else special mini CE - } else { - // two mini CEs, same primary groups, neither expands like above - uint32_t ce = pair & 0xffff; - if(ce > variableTop) { - pair = TWO_SHORT_PRIMARIES_MASK; - } else { - U_ASSERT(ce >= MIN_LONG); - pair &= TWO_LONG_PRIMARIES_MASK; // variable - } - } - return pair; -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationfastlatin.cpp +* +* created on: 2013aug18 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/ucol.h" +#include "collationdata.h" +#include "collationfastlatin.h" +#include "collationsettings.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +int32_t +CollationFastLatin::getOptions(const CollationData *data, const CollationSettings &settings, + uint16_t *primaries, int32_t capacity) { + const uint16_t *table = data->fastLatinTable; + if(table == nullptr) { return -1; } + U_ASSERT(capacity == LATIN_LIMIT); + if(capacity != LATIN_LIMIT) { return -1; } + + uint32_t miniVarTop; + if((settings.options & CollationSettings::ALTERNATE_MASK) == 0) { + // No mini primaries are variable, set a variableTop just below the + // lowest long mini primary. + miniVarTop = MIN_LONG - 1; + } else { + int32_t headerLength = *table & 0xff; + int32_t i = 1 + settings.getMaxVariable(); + if(i >= headerLength) { + return -1; // variableTop >= digits, should not occur + } + miniVarTop = table[i]; + } + + UBool digitsAreReordered = false; + if(settings.hasReordering()) { + uint32_t prevStart = 0; + uint32_t beforeDigitStart = 0; + uint32_t digitStart = 0; + uint32_t afterDigitStart = 0; + for(int32_t group = UCOL_REORDER_CODE_FIRST; + group < UCOL_REORDER_CODE_FIRST + CollationData::MAX_NUM_SPECIAL_REORDER_CODES; + ++group) { + uint32_t start = data->getFirstPrimaryForGroup(group); + start = settings.reorder(start); + if(group == UCOL_REORDER_CODE_DIGIT) { + beforeDigitStart = prevStart; + digitStart = start; + } else if(start != 0) { + if(start < prevStart) { + // The permutation affects the groups up to Latin. + return -1; + } + // In the future, there might be a special group between digits & Latin. + if(digitStart != 0 && afterDigitStart == 0 && prevStart == beforeDigitStart) { + afterDigitStart = start; + } + prevStart = start; + } + } + uint32_t latinStart = data->getFirstPrimaryForGroup(USCRIPT_LATIN); + latinStart = settings.reorder(latinStart); + if(latinStart < prevStart) { + return -1; + } + if(afterDigitStart == 0) { + afterDigitStart = latinStart; + } + if(!(beforeDigitStart < digitStart && digitStart < afterDigitStart)) { + digitsAreReordered = true; + } + } + + table += (table[0] & 0xff); // skip the header + for(UChar32 c = 0; c < LATIN_LIMIT; ++c) { + uint32_t p = table[c]; + if(p >= MIN_SHORT) { + p &= SHORT_PRIMARY_MASK; + } else if(p > miniVarTop) { + p &= LONG_PRIMARY_MASK; + } else { + p = 0; + } + primaries[c] = (uint16_t)p; + } + if(digitsAreReordered || (settings.options & CollationSettings::NUMERIC) != 0) { + // Bail out for digits. + for(UChar32 c = 0x30; c <= 0x39; ++c) { primaries[c] = 0; } + } + + // Shift the miniVarTop above other options. + return ((int32_t)miniVarTop << 16) | settings.options; +} + +int32_t +CollationFastLatin::compareUTF16(const uint16_t *table, const uint16_t *primaries, int32_t options, + const char16_t *left, int32_t leftLength, + const char16_t *right, int32_t rightLength) { + // This is a modified copy of CollationCompare::compareUpToQuaternary(), + // optimized for common Latin text. + // Keep them in sync! + // Keep compareUTF16() and compareUTF8() in sync very closely! + + U_ASSERT((table[0] >> 8) == VERSION); + table += (table[0] & 0xff); // skip the header + uint32_t variableTop = (uint32_t)options >> 16; // see getOptions() + options &= 0xffff; // needed for CollationSettings::getStrength() to work + + // Check for supported characters, fetch mini CEs, and compare primaries. + int32_t leftIndex = 0, rightIndex = 0; + /** + * Single mini CE or a pair. + * The current mini CE is in the lower 16 bits, the next one is in the upper 16 bits. + * If there is only one, then it is in the lower bits, and the upper bits are 0. + */ + uint32_t leftPair = 0, rightPair = 0; + for(;;) { + // We fetch CEs until we get a non-ignorable primary or reach the end. + while(leftPair == 0) { + if(leftIndex == leftLength) { + leftPair = EOS; + break; + } + UChar32 c = left[leftIndex++]; + if(c <= LATIN_MAX) { + leftPair = primaries[c]; + if(leftPair != 0) { break; } + if(c <= 0x39 && c >= 0x30 && (options & CollationSettings::NUMERIC) != 0) { + return BAIL_OUT_RESULT; + } + leftPair = table[c]; + } else if(PUNCT_START <= c && c < PUNCT_LIMIT) { + leftPair = table[c - PUNCT_START + LATIN_LIMIT]; + } else { + leftPair = lookup(table, c); + } + if(leftPair >= MIN_SHORT) { + leftPair &= SHORT_PRIMARY_MASK; + break; + } else if(leftPair > variableTop) { + leftPair &= LONG_PRIMARY_MASK; + break; + } else { + leftPair = nextPair(table, c, leftPair, left, nullptr, leftIndex, leftLength); + if(leftPair == BAIL_OUT) { return BAIL_OUT_RESULT; } + leftPair = getPrimaries(variableTop, leftPair); + } + } + + while(rightPair == 0) { + if(rightIndex == rightLength) { + rightPair = EOS; + break; + } + UChar32 c = right[rightIndex++]; + if(c <= LATIN_MAX) { + rightPair = primaries[c]; + if(rightPair != 0) { break; } + if(c <= 0x39 && c >= 0x30 && (options & CollationSettings::NUMERIC) != 0) { + return BAIL_OUT_RESULT; + } + rightPair = table[c]; + } else if(PUNCT_START <= c && c < PUNCT_LIMIT) { + rightPair = table[c - PUNCT_START + LATIN_LIMIT]; + } else { + rightPair = lookup(table, c); + } + if(rightPair >= MIN_SHORT) { + rightPair &= SHORT_PRIMARY_MASK; + break; + } else if(rightPair > variableTop) { + rightPair &= LONG_PRIMARY_MASK; + break; + } else { + rightPair = nextPair(table, c, rightPair, right, nullptr, rightIndex, rightLength); + if(rightPair == BAIL_OUT) { return BAIL_OUT_RESULT; } + rightPair = getPrimaries(variableTop, rightPair); + } + } + + if(leftPair == rightPair) { + if(leftPair == EOS) { break; } + leftPair = rightPair = 0; + continue; + } + uint32_t leftPrimary = leftPair & 0xffff; + uint32_t rightPrimary = rightPair & 0xffff; + if(leftPrimary != rightPrimary) { + // Return the primary difference. + return (leftPrimary < rightPrimary) ? UCOL_LESS : UCOL_GREATER; + } + if(leftPair == EOS) { break; } + leftPair >>= 16; + rightPair >>= 16; + } + // In the following, we need to re-fetch each character because we did not buffer the CEs, + // but we know that the string is well-formed and + // only contains supported characters and mappings. + + // We might skip the secondary level but continue with the case level + // which is turned on separately. + if(CollationSettings::getStrength(options) >= UCOL_SECONDARY) { + leftIndex = rightIndex = 0; + leftPair = rightPair = 0; + for(;;) { + while(leftPair == 0) { + if(leftIndex == leftLength) { + leftPair = EOS; + break; + } + UChar32 c = left[leftIndex++]; + if(c <= LATIN_MAX) { + leftPair = table[c]; + } else if(PUNCT_START <= c && c < PUNCT_LIMIT) { + leftPair = table[c - PUNCT_START + LATIN_LIMIT]; + } else { + leftPair = lookup(table, c); + } + if(leftPair >= MIN_SHORT) { + leftPair = getSecondariesFromOneShortCE(leftPair); + break; + } else if(leftPair > variableTop) { + leftPair = COMMON_SEC_PLUS_OFFSET; + break; + } else { + leftPair = nextPair(table, c, leftPair, left, nullptr, leftIndex, leftLength); + leftPair = getSecondaries(variableTop, leftPair); + } + } + + while(rightPair == 0) { + if(rightIndex == rightLength) { + rightPair = EOS; + break; + } + UChar32 c = right[rightIndex++]; + if(c <= LATIN_MAX) { + rightPair = table[c]; + } else if(PUNCT_START <= c && c < PUNCT_LIMIT) { + rightPair = table[c - PUNCT_START + LATIN_LIMIT]; + } else { + rightPair = lookup(table, c); + } + if(rightPair >= MIN_SHORT) { + rightPair = getSecondariesFromOneShortCE(rightPair); + break; + } else if(rightPair > variableTop) { + rightPair = COMMON_SEC_PLUS_OFFSET; + break; + } else { + rightPair = nextPair(table, c, rightPair, right, nullptr, rightIndex, rightLength); + rightPair = getSecondaries(variableTop, rightPair); + } + } + + if(leftPair == rightPair) { + if(leftPair == EOS) { break; } + leftPair = rightPair = 0; + continue; + } + uint32_t leftSecondary = leftPair & 0xffff; + uint32_t rightSecondary = rightPair & 0xffff; + if(leftSecondary != rightSecondary) { + if((options & CollationSettings::BACKWARD_SECONDARY) != 0) { + // Full support for backwards secondary requires backwards contraction matching + // and moving backwards between merge separators. + return BAIL_OUT_RESULT; + } + return (leftSecondary < rightSecondary) ? UCOL_LESS : UCOL_GREATER; + } + if(leftPair == EOS) { break; } + leftPair >>= 16; + rightPair >>= 16; + } + } + + if((options & CollationSettings::CASE_LEVEL) != 0) { + UBool strengthIsPrimary = CollationSettings::getStrength(options) == UCOL_PRIMARY; + leftIndex = rightIndex = 0; + leftPair = rightPair = 0; + for(;;) { + while(leftPair == 0) { + if(leftIndex == leftLength) { + leftPair = EOS; + break; + } + UChar32 c = left[leftIndex++]; + leftPair = (c <= LATIN_MAX) ? table[c] : lookup(table, c); + if(leftPair < MIN_LONG) { + leftPair = nextPair(table, c, leftPair, left, nullptr, leftIndex, leftLength); + } + leftPair = getCases(variableTop, strengthIsPrimary, leftPair); + } + + while(rightPair == 0) { + if(rightIndex == rightLength) { + rightPair = EOS; + break; + } + UChar32 c = right[rightIndex++]; + rightPair = (c <= LATIN_MAX) ? table[c] : lookup(table, c); + if(rightPair < MIN_LONG) { + rightPair = nextPair(table, c, rightPair, right, nullptr, rightIndex, rightLength); + } + rightPair = getCases(variableTop, strengthIsPrimary, rightPair); + } + + if(leftPair == rightPair) { + if(leftPair == EOS) { break; } + leftPair = rightPair = 0; + continue; + } + uint32_t leftCase = leftPair & 0xffff; + uint32_t rightCase = rightPair & 0xffff; + if(leftCase != rightCase) { + if((options & CollationSettings::UPPER_FIRST) == 0) { + return (leftCase < rightCase) ? UCOL_LESS : UCOL_GREATER; + } else { + return (leftCase < rightCase) ? UCOL_GREATER : UCOL_LESS; + } + } + if(leftPair == EOS) { break; } + leftPair >>= 16; + rightPair >>= 16; + } + } + if(CollationSettings::getStrength(options) <= UCOL_SECONDARY) { return UCOL_EQUAL; } + + // Remove the case bits from the tertiary weight when caseLevel is on or caseFirst is off. + UBool withCaseBits = CollationSettings::isTertiaryWithCaseBits(options); + + leftIndex = rightIndex = 0; + leftPair = rightPair = 0; + for(;;) { + while(leftPair == 0) { + if(leftIndex == leftLength) { + leftPair = EOS; + break; + } + UChar32 c = left[leftIndex++]; + leftPair = (c <= LATIN_MAX) ? table[c] : lookup(table, c); + if(leftPair < MIN_LONG) { + leftPair = nextPair(table, c, leftPair, left, nullptr, leftIndex, leftLength); + } + leftPair = getTertiaries(variableTop, withCaseBits, leftPair); + } + + while(rightPair == 0) { + if(rightIndex == rightLength) { + rightPair = EOS; + break; + } + UChar32 c = right[rightIndex++]; + rightPair = (c <= LATIN_MAX) ? table[c] : lookup(table, c); + if(rightPair < MIN_LONG) { + rightPair = nextPair(table, c, rightPair, right, nullptr, rightIndex, rightLength); + } + rightPair = getTertiaries(variableTop, withCaseBits, rightPair); + } + + if(leftPair == rightPair) { + if(leftPair == EOS) { break; } + leftPair = rightPair = 0; + continue; + } + uint32_t leftTertiary = leftPair & 0xffff; + uint32_t rightTertiary = rightPair & 0xffff; + if(leftTertiary != rightTertiary) { + if(CollationSettings::sortsTertiaryUpperCaseFirst(options)) { + // Pass through EOS and MERGE_WEIGHT + // and keep real tertiary weights larger than the MERGE_WEIGHT. + // Tertiary CEs (secondary ignorables) are not supported in fast Latin. + if(leftTertiary > MERGE_WEIGHT) { + leftTertiary ^= CASE_MASK; + } + if(rightTertiary > MERGE_WEIGHT) { + rightTertiary ^= CASE_MASK; + } + } + return (leftTertiary < rightTertiary) ? UCOL_LESS : UCOL_GREATER; + } + if(leftPair == EOS) { break; } + leftPair >>= 16; + rightPair >>= 16; + } + if(CollationSettings::getStrength(options) <= UCOL_TERTIARY) { return UCOL_EQUAL; } + + leftIndex = rightIndex = 0; + leftPair = rightPair = 0; + for(;;) { + while(leftPair == 0) { + if(leftIndex == leftLength) { + leftPair = EOS; + break; + } + UChar32 c = left[leftIndex++]; + leftPair = (c <= LATIN_MAX) ? table[c] : lookup(table, c); + if(leftPair < MIN_LONG) { + leftPair = nextPair(table, c, leftPair, left, nullptr, leftIndex, leftLength); + } + leftPair = getQuaternaries(variableTop, leftPair); + } + + while(rightPair == 0) { + if(rightIndex == rightLength) { + rightPair = EOS; + break; + } + UChar32 c = right[rightIndex++]; + rightPair = (c <= LATIN_MAX) ? table[c] : lookup(table, c); + if(rightPair < MIN_LONG) { + rightPair = nextPair(table, c, rightPair, right, nullptr, rightIndex, rightLength); + } + rightPair = getQuaternaries(variableTop, rightPair); + } + + if(leftPair == rightPair) { + if(leftPair == EOS) { break; } + leftPair = rightPair = 0; + continue; + } + uint32_t leftQuaternary = leftPair & 0xffff; + uint32_t rightQuaternary = rightPair & 0xffff; + if(leftQuaternary != rightQuaternary) { + return (leftQuaternary < rightQuaternary) ? UCOL_LESS : UCOL_GREATER; + } + if(leftPair == EOS) { break; } + leftPair >>= 16; + rightPair >>= 16; + } + return UCOL_EQUAL; +} + +int32_t +CollationFastLatin::compareUTF8(const uint16_t *table, const uint16_t *primaries, int32_t options, + const uint8_t *left, int32_t leftLength, + const uint8_t *right, int32_t rightLength) { + // Keep compareUTF16() and compareUTF8() in sync very closely! + + U_ASSERT((table[0] >> 8) == VERSION); + table += (table[0] & 0xff); // skip the header + uint32_t variableTop = (uint32_t)options >> 16; // see RuleBasedCollator::getFastLatinOptions() + options &= 0xffff; // needed for CollationSettings::getStrength() to work + + // Check for supported characters, fetch mini CEs, and compare primaries. + int32_t leftIndex = 0, rightIndex = 0; + /** + * Single mini CE or a pair. + * The current mini CE is in the lower 16 bits, the next one is in the upper 16 bits. + * If there is only one, then it is in the lower bits, and the upper bits are 0. + */ + uint32_t leftPair = 0, rightPair = 0; + // Note: There is no need to assemble the code point. + // We only need to look up the table entry for the character, + // and nextPair() looks for whether c==0. + for(;;) { + // We fetch CEs until we get a non-ignorable primary or reach the end. + while(leftPair == 0) { + if(leftIndex == leftLength) { + leftPair = EOS; + break; + } + UChar32 c = left[leftIndex++]; + uint8_t t; + if(c <= 0x7f) { + leftPair = primaries[c]; + if(leftPair != 0) { break; } + if(c <= 0x39 && c >= 0x30 && (options & CollationSettings::NUMERIC) != 0) { + return BAIL_OUT_RESULT; + } + leftPair = table[c]; + } else if(c <= LATIN_MAX_UTF8_LEAD && 0xc2 <= c && leftIndex != leftLength && + 0x80 <= (t = left[leftIndex]) && t <= 0xbf) { + ++leftIndex; + c = ((c - 0xc2) << 6) + t; + leftPair = primaries[c]; + if(leftPair != 0) { break; } + leftPair = table[c]; + } else { + leftPair = lookupUTF8(table, c, left, leftIndex, leftLength); + } + if(leftPair >= MIN_SHORT) { + leftPair &= SHORT_PRIMARY_MASK; + break; + } else if(leftPair > variableTop) { + leftPair &= LONG_PRIMARY_MASK; + break; + } else { + leftPair = nextPair(table, c, leftPair, nullptr, left, leftIndex, leftLength); + if(leftPair == BAIL_OUT) { return BAIL_OUT_RESULT; } + leftPair = getPrimaries(variableTop, leftPair); + } + } + + while(rightPair == 0) { + if(rightIndex == rightLength) { + rightPair = EOS; + break; + } + UChar32 c = right[rightIndex++]; + uint8_t t; + if(c <= 0x7f) { + rightPair = primaries[c]; + if(rightPair != 0) { break; } + if(c <= 0x39 && c >= 0x30 && (options & CollationSettings::NUMERIC) != 0) { + return BAIL_OUT_RESULT; + } + rightPair = table[c]; + } else if(c <= LATIN_MAX_UTF8_LEAD && 0xc2 <= c && rightIndex != rightLength && + 0x80 <= (t = right[rightIndex]) && t <= 0xbf) { + ++rightIndex; + c = ((c - 0xc2) << 6) + t; + rightPair = primaries[c]; + if(rightPair != 0) { break; } + rightPair = table[c]; + } else { + rightPair = lookupUTF8(table, c, right, rightIndex, rightLength); + } + if(rightPair >= MIN_SHORT) { + rightPair &= SHORT_PRIMARY_MASK; + break; + } else if(rightPair > variableTop) { + rightPair &= LONG_PRIMARY_MASK; + break; + } else { + rightPair = nextPair(table, c, rightPair, nullptr, right, rightIndex, rightLength); + if(rightPair == BAIL_OUT) { return BAIL_OUT_RESULT; } + rightPair = getPrimaries(variableTop, rightPair); + } + } + + if(leftPair == rightPair) { + if(leftPair == EOS) { break; } + leftPair = rightPair = 0; + continue; + } + uint32_t leftPrimary = leftPair & 0xffff; + uint32_t rightPrimary = rightPair & 0xffff; + if(leftPrimary != rightPrimary) { + // Return the primary difference. + return (leftPrimary < rightPrimary) ? UCOL_LESS : UCOL_GREATER; + } + if(leftPair == EOS) { break; } + leftPair >>= 16; + rightPair >>= 16; + } + // In the following, we need to re-fetch each character because we did not buffer the CEs, + // but we know that the string is well-formed and + // only contains supported characters and mappings. + + // We might skip the secondary level but continue with the case level + // which is turned on separately. + if(CollationSettings::getStrength(options) >= UCOL_SECONDARY) { + leftIndex = rightIndex = 0; + leftPair = rightPair = 0; + for(;;) { + while(leftPair == 0) { + if(leftIndex == leftLength) { + leftPair = EOS; + break; + } + UChar32 c = left[leftIndex++]; + if(c <= 0x7f) { + leftPair = table[c]; + } else if(c <= LATIN_MAX_UTF8_LEAD) { + leftPair = table[((c - 0xc2) << 6) + left[leftIndex++]]; + } else { + leftPair = lookupUTF8Unsafe(table, c, left, leftIndex); + } + if(leftPair >= MIN_SHORT) { + leftPair = getSecondariesFromOneShortCE(leftPair); + break; + } else if(leftPair > variableTop) { + leftPair = COMMON_SEC_PLUS_OFFSET; + break; + } else { + leftPair = nextPair(table, c, leftPair, nullptr, left, leftIndex, leftLength); + leftPair = getSecondaries(variableTop, leftPair); + } + } + + while(rightPair == 0) { + if(rightIndex == rightLength) { + rightPair = EOS; + break; + } + UChar32 c = right[rightIndex++]; + if(c <= 0x7f) { + rightPair = table[c]; + } else if(c <= LATIN_MAX_UTF8_LEAD) { + rightPair = table[((c - 0xc2) << 6) + right[rightIndex++]]; + } else { + rightPair = lookupUTF8Unsafe(table, c, right, rightIndex); + } + if(rightPair >= MIN_SHORT) { + rightPair = getSecondariesFromOneShortCE(rightPair); + break; + } else if(rightPair > variableTop) { + rightPair = COMMON_SEC_PLUS_OFFSET; + break; + } else { + rightPair = nextPair(table, c, rightPair, nullptr, right, rightIndex, rightLength); + rightPair = getSecondaries(variableTop, rightPair); + } + } + + if(leftPair == rightPair) { + if(leftPair == EOS) { break; } + leftPair = rightPair = 0; + continue; + } + uint32_t leftSecondary = leftPair & 0xffff; + uint32_t rightSecondary = rightPair & 0xffff; + if(leftSecondary != rightSecondary) { + if((options & CollationSettings::BACKWARD_SECONDARY) != 0) { + // Full support for backwards secondary requires backwards contraction matching + // and moving backwards between merge separators. + return BAIL_OUT_RESULT; + } + return (leftSecondary < rightSecondary) ? UCOL_LESS : UCOL_GREATER; + } + if(leftPair == EOS) { break; } + leftPair >>= 16; + rightPair >>= 16; + } + } + + if((options & CollationSettings::CASE_LEVEL) != 0) { + UBool strengthIsPrimary = CollationSettings::getStrength(options) == UCOL_PRIMARY; + leftIndex = rightIndex = 0; + leftPair = rightPair = 0; + for(;;) { + while(leftPair == 0) { + if(leftIndex == leftLength) { + leftPair = EOS; + break; + } + UChar32 c = left[leftIndex++]; + leftPair = (c <= 0x7f) ? table[c] : lookupUTF8Unsafe(table, c, left, leftIndex); + if(leftPair < MIN_LONG) { + leftPair = nextPair(table, c, leftPair, nullptr, left, leftIndex, leftLength); + } + leftPair = getCases(variableTop, strengthIsPrimary, leftPair); + } + + while(rightPair == 0) { + if(rightIndex == rightLength) { + rightPair = EOS; + break; + } + UChar32 c = right[rightIndex++]; + rightPair = (c <= 0x7f) ? table[c] : lookupUTF8Unsafe(table, c, right, rightIndex); + if(rightPair < MIN_LONG) { + rightPair = nextPair(table, c, rightPair, nullptr, right, rightIndex, rightLength); + } + rightPair = getCases(variableTop, strengthIsPrimary, rightPair); + } + + if(leftPair == rightPair) { + if(leftPair == EOS) { break; } + leftPair = rightPair = 0; + continue; + } + uint32_t leftCase = leftPair & 0xffff; + uint32_t rightCase = rightPair & 0xffff; + if(leftCase != rightCase) { + if((options & CollationSettings::UPPER_FIRST) == 0) { + return (leftCase < rightCase) ? UCOL_LESS : UCOL_GREATER; + } else { + return (leftCase < rightCase) ? UCOL_GREATER : UCOL_LESS; + } + } + if(leftPair == EOS) { break; } + leftPair >>= 16; + rightPair >>= 16; + } + } + if(CollationSettings::getStrength(options) <= UCOL_SECONDARY) { return UCOL_EQUAL; } + + // Remove the case bits from the tertiary weight when caseLevel is on or caseFirst is off. + UBool withCaseBits = CollationSettings::isTertiaryWithCaseBits(options); + + leftIndex = rightIndex = 0; + leftPair = rightPair = 0; + for(;;) { + while(leftPair == 0) { + if(leftIndex == leftLength) { + leftPair = EOS; + break; + } + UChar32 c = left[leftIndex++]; + leftPair = (c <= 0x7f) ? table[c] : lookupUTF8Unsafe(table, c, left, leftIndex); + if(leftPair < MIN_LONG) { + leftPair = nextPair(table, c, leftPair, nullptr, left, leftIndex, leftLength); + } + leftPair = getTertiaries(variableTop, withCaseBits, leftPair); + } + + while(rightPair == 0) { + if(rightIndex == rightLength) { + rightPair = EOS; + break; + } + UChar32 c = right[rightIndex++]; + rightPair = (c <= 0x7f) ? table[c] : lookupUTF8Unsafe(table, c, right, rightIndex); + if(rightPair < MIN_LONG) { + rightPair = nextPair(table, c, rightPair, nullptr, right, rightIndex, rightLength); + } + rightPair = getTertiaries(variableTop, withCaseBits, rightPair); + } + + if(leftPair == rightPair) { + if(leftPair == EOS) { break; } + leftPair = rightPair = 0; + continue; + } + uint32_t leftTertiary = leftPair & 0xffff; + uint32_t rightTertiary = rightPair & 0xffff; + if(leftTertiary != rightTertiary) { + if(CollationSettings::sortsTertiaryUpperCaseFirst(options)) { + // Pass through EOS and MERGE_WEIGHT + // and keep real tertiary weights larger than the MERGE_WEIGHT. + // Tertiary CEs (secondary ignorables) are not supported in fast Latin. + if(leftTertiary > MERGE_WEIGHT) { + leftTertiary ^= CASE_MASK; + } + if(rightTertiary > MERGE_WEIGHT) { + rightTertiary ^= CASE_MASK; + } + } + return (leftTertiary < rightTertiary) ? UCOL_LESS : UCOL_GREATER; + } + if(leftPair == EOS) { break; } + leftPair >>= 16; + rightPair >>= 16; + } + if(CollationSettings::getStrength(options) <= UCOL_TERTIARY) { return UCOL_EQUAL; } + + leftIndex = rightIndex = 0; + leftPair = rightPair = 0; + for(;;) { + while(leftPair == 0) { + if(leftIndex == leftLength) { + leftPair = EOS; + break; + } + UChar32 c = left[leftIndex++]; + leftPair = (c <= 0x7f) ? table[c] : lookupUTF8Unsafe(table, c, left, leftIndex); + if(leftPair < MIN_LONG) { + leftPair = nextPair(table, c, leftPair, nullptr, left, leftIndex, leftLength); + } + leftPair = getQuaternaries(variableTop, leftPair); + } + + while(rightPair == 0) { + if(rightIndex == rightLength) { + rightPair = EOS; + break; + } + UChar32 c = right[rightIndex++]; + rightPair = (c <= 0x7f) ? table[c] : lookupUTF8Unsafe(table, c, right, rightIndex); + if(rightPair < MIN_LONG) { + rightPair = nextPair(table, c, rightPair, nullptr, right, rightIndex, rightLength); + } + rightPair = getQuaternaries(variableTop, rightPair); + } + + if(leftPair == rightPair) { + if(leftPair == EOS) { break; } + leftPair = rightPair = 0; + continue; + } + uint32_t leftQuaternary = leftPair & 0xffff; + uint32_t rightQuaternary = rightPair & 0xffff; + if(leftQuaternary != rightQuaternary) { + return (leftQuaternary < rightQuaternary) ? UCOL_LESS : UCOL_GREATER; + } + if(leftPair == EOS) { break; } + leftPair >>= 16; + rightPair >>= 16; + } + return UCOL_EQUAL; +} + +uint32_t +CollationFastLatin::lookup(const uint16_t *table, UChar32 c) { + U_ASSERT(c > LATIN_MAX); + if(PUNCT_START <= c && c < PUNCT_LIMIT) { + return table[c - PUNCT_START + LATIN_LIMIT]; + } else if(c == 0xfffe) { + return MERGE_WEIGHT; + } else if(c == 0xffff) { + return MAX_SHORT | COMMON_SEC | LOWER_CASE | COMMON_TER; + } else { + return BAIL_OUT; + } +} + +uint32_t +CollationFastLatin::lookupUTF8(const uint16_t *table, UChar32 c, + const uint8_t *s8, int32_t &sIndex, int32_t sLength) { + // The caller handled ASCII and valid/supported Latin. + U_ASSERT(c > 0x7f); + int32_t i2 = sIndex + 1; + if(i2 < sLength || sLength < 0) { + uint8_t t1 = s8[sIndex]; + uint8_t t2 = s8[i2]; + sIndex += 2; + if(c == 0xe2 && t1 == 0x80 && 0x80 <= t2 && t2 <= 0xbf) { + return table[(LATIN_LIMIT - 0x80) + t2]; // 2000..203F -> 0180..01BF + } else if(c == 0xef && t1 == 0xbf) { + if(t2 == 0xbe) { + return MERGE_WEIGHT; // U+FFFE + } else if(t2 == 0xbf) { + return MAX_SHORT | COMMON_SEC | LOWER_CASE | COMMON_TER; // U+FFFF + } + } + } + return BAIL_OUT; +} + +uint32_t +CollationFastLatin::lookupUTF8Unsafe(const uint16_t *table, UChar32 c, + const uint8_t *s8, int32_t &sIndex) { + // The caller handled ASCII. + // The string is well-formed and contains only supported characters. + U_ASSERT(c > 0x7f); + if(c <= LATIN_MAX_UTF8_LEAD) { + return table[((c - 0xc2) << 6) + s8[sIndex++]]; // 0080..017F + } + uint8_t t2 = s8[sIndex + 1]; + sIndex += 2; + if(c == 0xe2) { + return table[(LATIN_LIMIT - 0x80) + t2]; // 2000..203F -> 0180..01BF + } else if(t2 == 0xbe) { + return MERGE_WEIGHT; // U+FFFE + } else { + return MAX_SHORT | COMMON_SEC | LOWER_CASE | COMMON_TER; // U+FFFF + } +} + +uint32_t +CollationFastLatin::nextPair(const uint16_t *table, UChar32 c, uint32_t ce, + const char16_t *s16, const uint8_t *s8, int32_t &sIndex, int32_t &sLength) { + if(ce >= MIN_LONG || ce < CONTRACTION) { + return ce; // simple or special mini CE + } else if(ce >= EXPANSION) { + int32_t index = NUM_FAST_CHARS + (ce & INDEX_MASK); + return ((uint32_t)table[index + 1] << 16) | table[index]; + } else /* ce >= CONTRACTION */ { + if(c == 0 && sLength < 0) { + sLength = sIndex - 1; + return EOS; + } + // Contraction list: Default mapping followed by + // 0 or more single-character contraction suffix mappings. + int32_t index = NUM_FAST_CHARS + (ce & INDEX_MASK); + if(sIndex != sLength) { + // Read the next character. + int32_t c2; + int32_t nextIndex = sIndex; + if(s16 != nullptr) { + c2 = s16[nextIndex++]; + if(c2 > LATIN_MAX) { + if(PUNCT_START <= c2 && c2 < PUNCT_LIMIT) { + c2 = c2 - PUNCT_START + LATIN_LIMIT; // 2000..203F -> 0180..01BF + } else if(c2 == 0xfffe || c2 == 0xffff) { + c2 = -1; // U+FFFE & U+FFFF cannot occur in contractions. + } else { + return BAIL_OUT; + } + } + } else { + c2 = s8[nextIndex++]; + if(c2 > 0x7f) { + uint8_t t; + if(c2 <= 0xc5 && 0xc2 <= c2 && nextIndex != sLength && + 0x80 <= (t = s8[nextIndex]) && t <= 0xbf) { + c2 = ((c2 - 0xc2) << 6) + t; // 0080..017F + ++nextIndex; + } else { + int32_t i2 = nextIndex + 1; + if(i2 < sLength || sLength < 0) { + if(c2 == 0xe2 && s8[nextIndex] == 0x80 && + 0x80 <= (t = s8[i2]) && t <= 0xbf) { + c2 = (LATIN_LIMIT - 0x80) + t; // 2000..203F -> 0180..01BF + } else if(c2 == 0xef && s8[nextIndex] == 0xbf && + ((t = s8[i2]) == 0xbe || t == 0xbf)) { + c2 = -1; // U+FFFE & U+FFFF cannot occur in contractions. + } else { + return BAIL_OUT; + } + } else { + return BAIL_OUT; + } + nextIndex += 2; + } + } + } + if(c2 == 0 && sLength < 0) { + sLength = sIndex; + c2 = -1; + } + // Look for the next character in the contraction suffix list, + // which is in ascending order of single suffix characters. + int32_t i = index; + int32_t head = table[i]; // first skip the default mapping + int32_t x; + do { + i += head >> CONTR_LENGTH_SHIFT; + head = table[i]; + x = head & CONTR_CHAR_MASK; + } while(x < c2); + if(x == c2) { + index = i; + sIndex = nextIndex; + } + } + // Return the CE or CEs for the default or contraction mapping. + int32_t length = table[index] >> CONTR_LENGTH_SHIFT; + if(length == 1) { + return BAIL_OUT; + } + ce = table[index + 1]; + if(length == 2) { + return ce; + } else { + return ((uint32_t)table[index + 2] << 16) | ce; + } + } +} + +uint32_t +CollationFastLatin::getSecondaries(uint32_t variableTop, uint32_t pair) { + if(pair <= 0xffff) { + // one mini CE + if(pair >= MIN_SHORT) { + pair = getSecondariesFromOneShortCE(pair); + } else if(pair > variableTop) { + pair = COMMON_SEC_PLUS_OFFSET; + } else if(pair >= MIN_LONG) { + pair = 0; // variable + } + // else special mini CE + } else { + uint32_t ce = pair & 0xffff; + if(ce >= MIN_SHORT) { + pair = (pair & TWO_SECONDARIES_MASK) + TWO_SEC_OFFSETS; + } else if(ce > variableTop) { + pair = TWO_COMMON_SEC_PLUS_OFFSET; + } else { + U_ASSERT(ce >= MIN_LONG); + pair = 0; // variable + } + } + return pair; +} + +uint32_t +CollationFastLatin::getCases(uint32_t variableTop, UBool strengthIsPrimary, uint32_t pair) { + // Primary+caseLevel: Ignore case level weights of primary ignorables. + // Otherwise: Ignore case level weights of secondary ignorables. + // For details see the comments in the CollationCompare class. + // Tertiary CEs (secondary ignorables) are not supported in fast Latin. + if(pair <= 0xffff) { + // one mini CE + if(pair >= MIN_SHORT) { + // A high secondary weight means we really have two CEs, + // a primary CE and a secondary CE. + uint32_t ce = pair; + pair &= CASE_MASK; // explicit weight of primary CE + if(!strengthIsPrimary && (ce & SECONDARY_MASK) >= MIN_SEC_HIGH) { + pair |= LOWER_CASE << 16; // implied weight of secondary CE + } + } else if(pair > variableTop) { + pair = LOWER_CASE; + } else if(pair >= MIN_LONG) { + pair = 0; // variable + } + // else special mini CE + } else { + // two mini CEs, same primary groups, neither expands like above + uint32_t ce = pair & 0xffff; + if(ce >= MIN_SHORT) { + if(strengthIsPrimary && (pair & (SHORT_PRIMARY_MASK << 16)) == 0) { + pair &= CASE_MASK; + } else { + pair &= TWO_CASES_MASK; + } + } else if(ce > variableTop) { + pair = TWO_LOWER_CASES; + } else { + U_ASSERT(ce >= MIN_LONG); + pair = 0; // variable + } + } + return pair; +} + +uint32_t +CollationFastLatin::getTertiaries(uint32_t variableTop, UBool withCaseBits, uint32_t pair) { + if(pair <= 0xffff) { + // one mini CE + if(pair >= MIN_SHORT) { + // A high secondary weight means we really have two CEs, + // a primary CE and a secondary CE. + uint32_t ce = pair; + if(withCaseBits) { + pair = (pair & CASE_AND_TERTIARY_MASK) + TER_OFFSET; + if((ce & SECONDARY_MASK) >= MIN_SEC_HIGH) { + pair |= (LOWER_CASE | COMMON_TER_PLUS_OFFSET) << 16; + } + } else { + pair = (pair & TERTIARY_MASK) + TER_OFFSET; + if((ce & SECONDARY_MASK) >= MIN_SEC_HIGH) { + pair |= COMMON_TER_PLUS_OFFSET << 16; + } + } + } else if(pair > variableTop) { + pair = (pair & TERTIARY_MASK) + TER_OFFSET; + if(withCaseBits) { + pair |= LOWER_CASE; + } + } else if(pair >= MIN_LONG) { + pair = 0; // variable + } + // else special mini CE + } else { + // two mini CEs, same primary groups, neither expands like above + uint32_t ce = pair & 0xffff; + if(ce >= MIN_SHORT) { + if(withCaseBits) { + pair &= TWO_CASES_MASK | TWO_TERTIARIES_MASK; + } else { + pair &= TWO_TERTIARIES_MASK; + } + pair += TWO_TER_OFFSETS; + } else if(ce > variableTop) { + pair = (pair & TWO_TERTIARIES_MASK) + TWO_TER_OFFSETS; + if(withCaseBits) { + pair |= TWO_LOWER_CASES; + } + } else { + U_ASSERT(ce >= MIN_LONG); + pair = 0; // variable + } + } + return pair; +} + +uint32_t +CollationFastLatin::getQuaternaries(uint32_t variableTop, uint32_t pair) { + // Return the primary weight of a variable CE, + // or the maximum primary weight for a non-variable, not-completely-ignorable CE. + if(pair <= 0xffff) { + // one mini CE + if(pair >= MIN_SHORT) { + // A high secondary weight means we really have two CEs, + // a primary CE and a secondary CE. + if((pair & SECONDARY_MASK) >= MIN_SEC_HIGH) { + pair = TWO_SHORT_PRIMARIES_MASK; + } else { + pair = SHORT_PRIMARY_MASK; + } + } else if(pair > variableTop) { + pair = SHORT_PRIMARY_MASK; + } else if(pair >= MIN_LONG) { + pair &= LONG_PRIMARY_MASK; // variable + } + // else special mini CE + } else { + // two mini CEs, same primary groups, neither expands like above + uint32_t ce = pair & 0xffff; + if(ce > variableTop) { + pair = TWO_SHORT_PRIMARIES_MASK; + } else { + U_ASSERT(ce >= MIN_LONG); + pair &= TWO_LONG_PRIMARIES_MASK; // variable + } + } + return pair; +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationfastlatin.h b/deps/icu-small/source/i18n/collationfastlatin.h index 7013f71ce60416..d58083eed13c1b 100644 --- a/deps/icu-small/source/i18n/collationfastlatin.h +++ b/deps/icu-small/source/i18n/collationfastlatin.h @@ -1,319 +1,319 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationfastlatin.h -* -* created on: 2013aug09 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONFASTLATIN_H__ -#define __COLLATIONFASTLATIN_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -U_NAMESPACE_BEGIN - -struct CollationData; -struct CollationSettings; - -class U_I18N_API CollationFastLatin /* all static */ { -public: - /** - * Fast Latin format version (one byte 1..FF). - * Must be incremented for any runtime-incompatible changes, - * in particular, for changes to any of the following constants. - * - * When the major version number of the main data format changes, - * we can reset this fast Latin version to 1. - */ - static const uint16_t VERSION = 2; - - static const int32_t LATIN_MAX = 0x17f; - static const int32_t LATIN_LIMIT = LATIN_MAX + 1; - - static const int32_t LATIN_MAX_UTF8_LEAD = 0xc5; // UTF-8 lead byte of LATIN_MAX - - static const int32_t PUNCT_START = 0x2000; - static const int32_t PUNCT_LIMIT = 0x2040; - - // excludes U+FFFE & U+FFFF - static const int32_t NUM_FAST_CHARS = LATIN_LIMIT + (PUNCT_LIMIT - PUNCT_START); - - // Note on the supported weight ranges: - // Analysis of UCA 6.3 and CLDR 23 non-search tailorings shows that - // the CEs for characters in the above ranges, excluding expansions with length >2, - // excluding contractions of >2 characters, and other restrictions - // (see the builder's getCEsFromCE32()), - // use at most about 150 primary weights, - // where about 94 primary weights are possibly-variable (space/punct/symbol/currency), - // at most 4 secondary before-common weights, - // at most 4 secondary after-common weights, - // at most 16 secondary high weights (in secondary CEs), and - // at most 4 tertiary after-common weights. - // The following ranges are designed to support slightly more weights than that. - // (en_US_POSIX is unusual: It creates about 64 variable + 116 Latin primaries.) - - // Digits may use long primaries (preserving more short ones) - // or short primaries (faster) without changing this data structure. - // (If we supported numeric collation, then digits would have to have long primaries - // so that special handling does not affect the fast path.) - - static const uint32_t SHORT_PRIMARY_MASK = 0xfc00; // bits 15..10 - static const uint32_t INDEX_MASK = 0x3ff; // bits 9..0 for expansions & contractions - static const uint32_t SECONDARY_MASK = 0x3e0; // bits 9..5 - static const uint32_t CASE_MASK = 0x18; // bits 4..3 - static const uint32_t LONG_PRIMARY_MASK = 0xfff8; // bits 15..3 - static const uint32_t TERTIARY_MASK = 7; // bits 2..0 - static const uint32_t CASE_AND_TERTIARY_MASK = CASE_MASK | TERTIARY_MASK; - - static const uint32_t TWO_SHORT_PRIMARIES_MASK = - (SHORT_PRIMARY_MASK << 16) | SHORT_PRIMARY_MASK; // 0xfc00fc00 - static const uint32_t TWO_LONG_PRIMARIES_MASK = - (LONG_PRIMARY_MASK << 16) | LONG_PRIMARY_MASK; // 0xfff8fff8 - static const uint32_t TWO_SECONDARIES_MASK = - (SECONDARY_MASK << 16) | SECONDARY_MASK; // 0x3e003e0 - static const uint32_t TWO_CASES_MASK = - (CASE_MASK << 16) | CASE_MASK; // 0x180018 - static const uint32_t TWO_TERTIARIES_MASK = - (TERTIARY_MASK << 16) | TERTIARY_MASK; // 0x70007 - - /** - * Contraction with one fast Latin character. - * Use INDEX_MASK to find the start of the contraction list after the fixed table. - * The first entry contains the default mapping. - * Otherwise use CONTR_CHAR_MASK for the contraction character index - * (in ascending order). - * Use CONTR_LENGTH_SHIFT for the length of the entry - * (1=BAIL_OUT, 2=one CE, 3=two CEs). - * - * Also, U+0000 maps to a contraction entry, so that the fast path need not - * check for NUL termination. - * It usually maps to a contraction list with only the completely ignorable default value. - */ - static const uint32_t CONTRACTION = 0x400; - /** - * An expansion encodes two CEs. - * Use INDEX_MASK to find the pair of CEs after the fixed table. - * - * The higher a mini CE value, the easier it is to process. - * For expansions and higher, no context needs to be considered. - */ - static const uint32_t EXPANSION = 0x800; - /** - * Encodes one CE with a long/low mini primary (there are 128). - * All potentially-variable primaries must be in this range, - * to make the short-primary path as fast as possible. - */ - static const uint32_t MIN_LONG = 0xc00; - static const uint32_t LONG_INC = 8; - static const uint32_t MAX_LONG = 0xff8; - /** - * Encodes one CE with a short/high primary (there are 60), - * plus a secondary CE if the secondary weight is high. - * Fast handling: At least all letter primaries should be in this range. - */ - static const uint32_t MIN_SHORT = 0x1000; - static const uint32_t SHORT_INC = 0x400; - /** The highest primary weight is reserved for U+FFFF. */ - static const uint32_t MAX_SHORT = SHORT_PRIMARY_MASK; - - static const uint32_t MIN_SEC_BEFORE = 0; // must add SEC_OFFSET - static const uint32_t SEC_INC = 0x20; - static const uint32_t MAX_SEC_BEFORE = MIN_SEC_BEFORE + 4 * SEC_INC; // 5 before common - static const uint32_t COMMON_SEC = MAX_SEC_BEFORE + SEC_INC; - static const uint32_t MIN_SEC_AFTER = COMMON_SEC + SEC_INC; - static const uint32_t MAX_SEC_AFTER = MIN_SEC_AFTER + 5 * SEC_INC; // 6 after common - static const uint32_t MIN_SEC_HIGH = MAX_SEC_AFTER + SEC_INC; // 20 high secondaries - static const uint32_t MAX_SEC_HIGH = SECONDARY_MASK; - - /** - * Lookup: Add this offset to secondary weights, except for completely ignorable CEs. - * Must be greater than any special value, e.g., MERGE_WEIGHT. - * The exact value is not relevant for the format version. - */ - static const uint32_t SEC_OFFSET = SEC_INC; - static const uint32_t COMMON_SEC_PLUS_OFFSET = COMMON_SEC + SEC_OFFSET; - - static const uint32_t TWO_SEC_OFFSETS = - (SEC_OFFSET << 16) | SEC_OFFSET; // 0x200020 - static const uint32_t TWO_COMMON_SEC_PLUS_OFFSET = - (COMMON_SEC_PLUS_OFFSET << 16) | COMMON_SEC_PLUS_OFFSET; - - static const uint32_t LOWER_CASE = 8; // case bits include this offset - static const uint32_t TWO_LOWER_CASES = (LOWER_CASE << 16) | LOWER_CASE; // 0x80008 - - static const uint32_t COMMON_TER = 0; // must add TER_OFFSET - static const uint32_t MAX_TER_AFTER = 7; // 7 after common - - /** - * Lookup: Add this offset to tertiary weights, except for completely ignorable CEs. - * Must be greater than any special value, e.g., MERGE_WEIGHT. - * Must be greater than case bits as well, so that with combined case+tertiary weights - * plus the offset the tertiary bits does not spill over into the case bits. - * The exact value is not relevant for the format version. - */ - static const uint32_t TER_OFFSET = SEC_OFFSET; - static const uint32_t COMMON_TER_PLUS_OFFSET = COMMON_TER + TER_OFFSET; - - static const uint32_t TWO_TER_OFFSETS = (TER_OFFSET << 16) | TER_OFFSET; - static const uint32_t TWO_COMMON_TER_PLUS_OFFSET = - (COMMON_TER_PLUS_OFFSET << 16) | COMMON_TER_PLUS_OFFSET; - - static const uint32_t MERGE_WEIGHT = 3; - static const uint32_t EOS = 2; // end of string - static const uint32_t BAIL_OUT = 1; - - /** - * Contraction result first word bits 8..0 contain the - * second contraction character, as a char index 0..NUM_FAST_CHARS-1. - * Each contraction list is terminated with a word containing CONTR_CHAR_MASK. - */ - static const uint32_t CONTR_CHAR_MASK = 0x1ff; - /** - * Contraction result first word bits 10..9 contain the result length: - * 1=bail out, 2=one mini CE, 3=two mini CEs - */ - static const uint32_t CONTR_LENGTH_SHIFT = 9; - - /** - * Comparison return value when the regular comparison must be used. - * The exact value is not relevant for the format version. - */ - static const int32_t BAIL_OUT_RESULT = -2; - - static inline int32_t getCharIndex(UChar c) { - if(c <= LATIN_MAX) { - return c; - } else if(PUNCT_START <= c && c < PUNCT_LIMIT) { - return c - (PUNCT_START - LATIN_LIMIT); - } else { - // Not a fast Latin character. - // Note: U+FFFE & U+FFFF are forbidden in tailorings - // and thus do not occur in any contractions. - return -1; - } - } - - /** - * Computes the options value for the compare functions - * and writes the precomputed primary weights. - * Returns -1 if the Latin fastpath is not supported for the data and settings. - * The capacity must be LATIN_LIMIT. - */ - static int32_t getOptions(const CollationData *data, const CollationSettings &settings, - uint16_t *primaries, int32_t capacity); - - static int32_t compareUTF16(const uint16_t *table, const uint16_t *primaries, int32_t options, - const UChar *left, int32_t leftLength, - const UChar *right, int32_t rightLength); - - static int32_t compareUTF8(const uint16_t *table, const uint16_t *primaries, int32_t options, - const uint8_t *left, int32_t leftLength, - const uint8_t *right, int32_t rightLength); - -private: - static uint32_t lookup(const uint16_t *table, UChar32 c); - static uint32_t lookupUTF8(const uint16_t *table, UChar32 c, - const uint8_t *s8, int32_t &sIndex, int32_t sLength); - static uint32_t lookupUTF8Unsafe(const uint16_t *table, UChar32 c, - const uint8_t *s8, int32_t &sIndex); - - static uint32_t nextPair(const uint16_t *table, UChar32 c, uint32_t ce, - const UChar *s16, const uint8_t *s8, int32_t &sIndex, int32_t &sLength); - - static inline uint32_t getPrimaries(uint32_t variableTop, uint32_t pair) { - uint32_t ce = pair & 0xffff; - if(ce >= MIN_SHORT) { return pair & TWO_SHORT_PRIMARIES_MASK; } - if(ce > variableTop) { return pair & TWO_LONG_PRIMARIES_MASK; } - if(ce >= MIN_LONG) { return 0; } // variable - return pair; // special mini CE - } - static inline uint32_t getSecondariesFromOneShortCE(uint32_t ce) { - ce &= SECONDARY_MASK; - if(ce < MIN_SEC_HIGH) { - return ce + SEC_OFFSET; - } else { - return ((ce + SEC_OFFSET) << 16) | COMMON_SEC_PLUS_OFFSET; - } - } - static uint32_t getSecondaries(uint32_t variableTop, uint32_t pair); - static uint32_t getCases(uint32_t variableTop, UBool strengthIsPrimary, uint32_t pair); - static uint32_t getTertiaries(uint32_t variableTop, UBool withCaseBits, uint32_t pair); - static uint32_t getQuaternaries(uint32_t variableTop, uint32_t pair); - -private: - CollationFastLatin() = delete; // no constructor -}; - -/* - * Format of the CollationFastLatin data table. - * CollationFastLatin::VERSION = 2. - * - * This table contains data for a Latin-text collation fastpath. - * The data is stored as an array of uint16_t which contains the following parts. - * - * uint16_t -- version & header length - * Bits 15..8: version, must match the VERSION - * 7..0: length of the header - * - * uint16_t varTops[header length - 1] - * Version 2: - * varTops[m] is the highest CollationFastLatin long-primary weight - * of supported maxVariable group m - * (special reorder group space, punct, symbol, currency). - * - * Version 1: - * Each of these values maps the variable top lead byte of a supported maxVariable group - * to the highest CollationFastLatin long-primary weight. - * The values are stored in ascending order. - * Bits 15..7: max fast-Latin long-primary weight (bits 11..3 shifted left by 4 bits) - * 6..0: regular primary lead byte - * - * uint16_t miniCEs[0x1c0] - * A mini collation element for each character U+0000..U+017F and U+2000..U+203F. - * Each value encodes one or two mini CEs (two are possible if the first one - * has a short mini primary and the second one is a secondary CE, i.e., primary == 0), - * or points to an expansion or to a contraction table. - * U+0000 always has a contraction entry, - * so that NUL-termination need not be tested in the fastpath. - * If the collation elements for a character or contraction cannot be encoded in this format, - * then the BAIL_OUT value is stored. - * For details see the comments for the class constants. - * - * uint16_t expansions[variable length]; - * Expansion mini CEs contain an offset relative to just after the miniCEs table. - * An expansions contains exactly 2 mini CEs. - * - * uint16_t contractions[variable length]; - * Contraction mini CEs contain an offset relative to just after the miniCEs table. - * It points to a list of tuples which map from a contraction suffix character to a result. - * First uint16_t of each tuple: - * Bits 10..9: Length of the result (1..3), see comments on CONTR_LENGTH_SHIFT. - * Bits 8..0: Contraction character, see comments on CONTR_CHAR_MASK. - * This is followed by 0, 1, or 2 uint16_t according to the length. - * Each list is terminated by an entry with CONTR_CHAR_MASK. - * Each list starts with such an entry which also contains the default result - * for when there is no contraction match. - * - * ----------------- - * Changes for version 2 (ICU 55) - * - * Special reorder groups do not necessarily start on whole primary lead bytes any more. - * Therefore, the varTops data has a new format: - * Version 1 stored the lead bytes of the highest root primaries for - * the maxVariable-supported special reorder groups. - * Now the top 16 bits would need to be stored, - * and it is simpler to store only the fast-Latin weights. - */ - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONFASTLATIN_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationfastlatin.h +* +* created on: 2013aug09 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONFASTLATIN_H__ +#define __COLLATIONFASTLATIN_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +U_NAMESPACE_BEGIN + +struct CollationData; +struct CollationSettings; + +class U_I18N_API CollationFastLatin /* all static */ { +public: + /** + * Fast Latin format version (one byte 1..FF). + * Must be incremented for any runtime-incompatible changes, + * in particular, for changes to any of the following constants. + * + * When the major version number of the main data format changes, + * we can reset this fast Latin version to 1. + */ + static const uint16_t VERSION = 2; + + static const int32_t LATIN_MAX = 0x17f; + static const int32_t LATIN_LIMIT = LATIN_MAX + 1; + + static const int32_t LATIN_MAX_UTF8_LEAD = 0xc5; // UTF-8 lead byte of LATIN_MAX + + static const int32_t PUNCT_START = 0x2000; + static const int32_t PUNCT_LIMIT = 0x2040; + + // excludes U+FFFE & U+FFFF + static const int32_t NUM_FAST_CHARS = LATIN_LIMIT + (PUNCT_LIMIT - PUNCT_START); + + // Note on the supported weight ranges: + // Analysis of UCA 6.3 and CLDR 23 non-search tailorings shows that + // the CEs for characters in the above ranges, excluding expansions with length >2, + // excluding contractions of >2 characters, and other restrictions + // (see the builder's getCEsFromCE32()), + // use at most about 150 primary weights, + // where about 94 primary weights are possibly-variable (space/punct/symbol/currency), + // at most 4 secondary before-common weights, + // at most 4 secondary after-common weights, + // at most 16 secondary high weights (in secondary CEs), and + // at most 4 tertiary after-common weights. + // The following ranges are designed to support slightly more weights than that. + // (en_US_POSIX is unusual: It creates about 64 variable + 116 Latin primaries.) + + // Digits may use long primaries (preserving more short ones) + // or short primaries (faster) without changing this data structure. + // (If we supported numeric collation, then digits would have to have long primaries + // so that special handling does not affect the fast path.) + + static const uint32_t SHORT_PRIMARY_MASK = 0xfc00; // bits 15..10 + static const uint32_t INDEX_MASK = 0x3ff; // bits 9..0 for expansions & contractions + static const uint32_t SECONDARY_MASK = 0x3e0; // bits 9..5 + static const uint32_t CASE_MASK = 0x18; // bits 4..3 + static const uint32_t LONG_PRIMARY_MASK = 0xfff8; // bits 15..3 + static const uint32_t TERTIARY_MASK = 7; // bits 2..0 + static const uint32_t CASE_AND_TERTIARY_MASK = CASE_MASK | TERTIARY_MASK; + + static const uint32_t TWO_SHORT_PRIMARIES_MASK = + (SHORT_PRIMARY_MASK << 16) | SHORT_PRIMARY_MASK; // 0xfc00fc00 + static const uint32_t TWO_LONG_PRIMARIES_MASK = + (LONG_PRIMARY_MASK << 16) | LONG_PRIMARY_MASK; // 0xfff8fff8 + static const uint32_t TWO_SECONDARIES_MASK = + (SECONDARY_MASK << 16) | SECONDARY_MASK; // 0x3e003e0 + static const uint32_t TWO_CASES_MASK = + (CASE_MASK << 16) | CASE_MASK; // 0x180018 + static const uint32_t TWO_TERTIARIES_MASK = + (TERTIARY_MASK << 16) | TERTIARY_MASK; // 0x70007 + + /** + * Contraction with one fast Latin character. + * Use INDEX_MASK to find the start of the contraction list after the fixed table. + * The first entry contains the default mapping. + * Otherwise use CONTR_CHAR_MASK for the contraction character index + * (in ascending order). + * Use CONTR_LENGTH_SHIFT for the length of the entry + * (1=BAIL_OUT, 2=one CE, 3=two CEs). + * + * Also, U+0000 maps to a contraction entry, so that the fast path need not + * check for NUL termination. + * It usually maps to a contraction list with only the completely ignorable default value. + */ + static const uint32_t CONTRACTION = 0x400; + /** + * An expansion encodes two CEs. + * Use INDEX_MASK to find the pair of CEs after the fixed table. + * + * The higher a mini CE value, the easier it is to process. + * For expansions and higher, no context needs to be considered. + */ + static const uint32_t EXPANSION = 0x800; + /** + * Encodes one CE with a long/low mini primary (there are 128). + * All potentially-variable primaries must be in this range, + * to make the short-primary path as fast as possible. + */ + static const uint32_t MIN_LONG = 0xc00; + static const uint32_t LONG_INC = 8; + static const uint32_t MAX_LONG = 0xff8; + /** + * Encodes one CE with a short/high primary (there are 60), + * plus a secondary CE if the secondary weight is high. + * Fast handling: At least all letter primaries should be in this range. + */ + static const uint32_t MIN_SHORT = 0x1000; + static const uint32_t SHORT_INC = 0x400; + /** The highest primary weight is reserved for U+FFFF. */ + static const uint32_t MAX_SHORT = SHORT_PRIMARY_MASK; + + static const uint32_t MIN_SEC_BEFORE = 0; // must add SEC_OFFSET + static const uint32_t SEC_INC = 0x20; + static const uint32_t MAX_SEC_BEFORE = MIN_SEC_BEFORE + 4 * SEC_INC; // 5 before common + static const uint32_t COMMON_SEC = MAX_SEC_BEFORE + SEC_INC; + static const uint32_t MIN_SEC_AFTER = COMMON_SEC + SEC_INC; + static const uint32_t MAX_SEC_AFTER = MIN_SEC_AFTER + 5 * SEC_INC; // 6 after common + static const uint32_t MIN_SEC_HIGH = MAX_SEC_AFTER + SEC_INC; // 20 high secondaries + static const uint32_t MAX_SEC_HIGH = SECONDARY_MASK; + + /** + * Lookup: Add this offset to secondary weights, except for completely ignorable CEs. + * Must be greater than any special value, e.g., MERGE_WEIGHT. + * The exact value is not relevant for the format version. + */ + static const uint32_t SEC_OFFSET = SEC_INC; + static const uint32_t COMMON_SEC_PLUS_OFFSET = COMMON_SEC + SEC_OFFSET; + + static const uint32_t TWO_SEC_OFFSETS = + (SEC_OFFSET << 16) | SEC_OFFSET; // 0x200020 + static const uint32_t TWO_COMMON_SEC_PLUS_OFFSET = + (COMMON_SEC_PLUS_OFFSET << 16) | COMMON_SEC_PLUS_OFFSET; + + static const uint32_t LOWER_CASE = 8; // case bits include this offset + static const uint32_t TWO_LOWER_CASES = (LOWER_CASE << 16) | LOWER_CASE; // 0x80008 + + static const uint32_t COMMON_TER = 0; // must add TER_OFFSET + static const uint32_t MAX_TER_AFTER = 7; // 7 after common + + /** + * Lookup: Add this offset to tertiary weights, except for completely ignorable CEs. + * Must be greater than any special value, e.g., MERGE_WEIGHT. + * Must be greater than case bits as well, so that with combined case+tertiary weights + * plus the offset the tertiary bits does not spill over into the case bits. + * The exact value is not relevant for the format version. + */ + static const uint32_t TER_OFFSET = SEC_OFFSET; + static const uint32_t COMMON_TER_PLUS_OFFSET = COMMON_TER + TER_OFFSET; + + static const uint32_t TWO_TER_OFFSETS = (TER_OFFSET << 16) | TER_OFFSET; + static const uint32_t TWO_COMMON_TER_PLUS_OFFSET = + (COMMON_TER_PLUS_OFFSET << 16) | COMMON_TER_PLUS_OFFSET; + + static const uint32_t MERGE_WEIGHT = 3; + static const uint32_t EOS = 2; // end of string + static const uint32_t BAIL_OUT = 1; + + /** + * Contraction result first word bits 8..0 contain the + * second contraction character, as a char index 0..NUM_FAST_CHARS-1. + * Each contraction list is terminated with a word containing CONTR_CHAR_MASK. + */ + static const uint32_t CONTR_CHAR_MASK = 0x1ff; + /** + * Contraction result first word bits 10..9 contain the result length: + * 1=bail out, 2=one mini CE, 3=two mini CEs + */ + static const uint32_t CONTR_LENGTH_SHIFT = 9; + + /** + * Comparison return value when the regular comparison must be used. + * The exact value is not relevant for the format version. + */ + static const int32_t BAIL_OUT_RESULT = -2; + + static inline int32_t getCharIndex(char16_t c) { + if(c <= LATIN_MAX) { + return c; + } else if(PUNCT_START <= c && c < PUNCT_LIMIT) { + return c - (PUNCT_START - LATIN_LIMIT); + } else { + // Not a fast Latin character. + // Note: U+FFFE & U+FFFF are forbidden in tailorings + // and thus do not occur in any contractions. + return -1; + } + } + + /** + * Computes the options value for the compare functions + * and writes the precomputed primary weights. + * Returns -1 if the Latin fastpath is not supported for the data and settings. + * The capacity must be LATIN_LIMIT. + */ + static int32_t getOptions(const CollationData *data, const CollationSettings &settings, + uint16_t *primaries, int32_t capacity); + + static int32_t compareUTF16(const uint16_t *table, const uint16_t *primaries, int32_t options, + const char16_t *left, int32_t leftLength, + const char16_t *right, int32_t rightLength); + + static int32_t compareUTF8(const uint16_t *table, const uint16_t *primaries, int32_t options, + const uint8_t *left, int32_t leftLength, + const uint8_t *right, int32_t rightLength); + +private: + static uint32_t lookup(const uint16_t *table, UChar32 c); + static uint32_t lookupUTF8(const uint16_t *table, UChar32 c, + const uint8_t *s8, int32_t &sIndex, int32_t sLength); + static uint32_t lookupUTF8Unsafe(const uint16_t *table, UChar32 c, + const uint8_t *s8, int32_t &sIndex); + + static uint32_t nextPair(const uint16_t *table, UChar32 c, uint32_t ce, + const char16_t *s16, const uint8_t *s8, int32_t &sIndex, int32_t &sLength); + + static inline uint32_t getPrimaries(uint32_t variableTop, uint32_t pair) { + uint32_t ce = pair & 0xffff; + if(ce >= MIN_SHORT) { return pair & TWO_SHORT_PRIMARIES_MASK; } + if(ce > variableTop) { return pair & TWO_LONG_PRIMARIES_MASK; } + if(ce >= MIN_LONG) { return 0; } // variable + return pair; // special mini CE + } + static inline uint32_t getSecondariesFromOneShortCE(uint32_t ce) { + ce &= SECONDARY_MASK; + if(ce < MIN_SEC_HIGH) { + return ce + SEC_OFFSET; + } else { + return ((ce + SEC_OFFSET) << 16) | COMMON_SEC_PLUS_OFFSET; + } + } + static uint32_t getSecondaries(uint32_t variableTop, uint32_t pair); + static uint32_t getCases(uint32_t variableTop, UBool strengthIsPrimary, uint32_t pair); + static uint32_t getTertiaries(uint32_t variableTop, UBool withCaseBits, uint32_t pair); + static uint32_t getQuaternaries(uint32_t variableTop, uint32_t pair); + +private: + CollationFastLatin() = delete; // no constructor +}; + +/* + * Format of the CollationFastLatin data table. + * CollationFastLatin::VERSION = 2. + * + * This table contains data for a Latin-text collation fastpath. + * The data is stored as an array of uint16_t which contains the following parts. + * + * uint16_t -- version & header length + * Bits 15..8: version, must match the VERSION + * 7..0: length of the header + * + * uint16_t varTops[header length - 1] + * Version 2: + * varTops[m] is the highest CollationFastLatin long-primary weight + * of supported maxVariable group m + * (special reorder group space, punct, symbol, currency). + * + * Version 1: + * Each of these values maps the variable top lead byte of a supported maxVariable group + * to the highest CollationFastLatin long-primary weight. + * The values are stored in ascending order. + * Bits 15..7: max fast-Latin long-primary weight (bits 11..3 shifted left by 4 bits) + * 6..0: regular primary lead byte + * + * uint16_t miniCEs[0x1c0] + * A mini collation element for each character U+0000..U+017F and U+2000..U+203F. + * Each value encodes one or two mini CEs (two are possible if the first one + * has a short mini primary and the second one is a secondary CE, i.e., primary == 0), + * or points to an expansion or to a contraction table. + * U+0000 always has a contraction entry, + * so that NUL-termination need not be tested in the fastpath. + * If the collation elements for a character or contraction cannot be encoded in this format, + * then the BAIL_OUT value is stored. + * For details see the comments for the class constants. + * + * uint16_t expansions[variable length]; + * Expansion mini CEs contain an offset relative to just after the miniCEs table. + * An expansions contains exactly 2 mini CEs. + * + * uint16_t contractions[variable length]; + * Contraction mini CEs contain an offset relative to just after the miniCEs table. + * It points to a list of tuples which map from a contraction suffix character to a result. + * First uint16_t of each tuple: + * Bits 10..9: Length of the result (1..3), see comments on CONTR_LENGTH_SHIFT. + * Bits 8..0: Contraction character, see comments on CONTR_CHAR_MASK. + * This is followed by 0, 1, or 2 uint16_t according to the length. + * Each list is terminated by an entry with CONTR_CHAR_MASK. + * Each list starts with such an entry which also contains the default result + * for when there is no contraction match. + * + * ----------------- + * Changes for version 2 (ICU 55) + * + * Special reorder groups do not necessarily start on whole primary lead bytes any more. + * Therefore, the varTops data has a new format: + * Version 1 stored the lead bytes of the highest root primaries for + * the maxVariable-supported special reorder groups. + * Now the top 16 bits would need to be stored, + * and it is simpler to store only the fast-Latin weights. + */ + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONFASTLATIN_H__ diff --git a/deps/icu-small/source/i18n/collationfastlatinbuilder.cpp b/deps/icu-small/source/i18n/collationfastlatinbuilder.cpp index fc50e9df8edae0..c061f761b08846 100644 --- a/deps/icu-small/source/i18n/collationfastlatinbuilder.cpp +++ b/deps/icu-small/source/i18n/collationfastlatinbuilder.cpp @@ -1,717 +1,717 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationfastlatinbuilder.cpp -* -* created on: 2013aug09 -* created by: Markus W. Scherer -*/ - -#define DEBUG_COLLATION_FAST_LATIN_BUILDER 0 // 0 or 1 or 2 -#if DEBUG_COLLATION_FAST_LATIN_BUILDER -#include -#include -#endif - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/ucol.h" -#include "unicode/ucharstrie.h" -#include "unicode/unistr.h" -#include "unicode/uobject.h" -#include "unicode/uscript.h" -#include "cmemory.h" -#include "collation.h" -#include "collationdata.h" -#include "collationfastlatin.h" -#include "collationfastlatinbuilder.h" -#include "uassert.h" -#include "uvectr64.h" - -U_NAMESPACE_BEGIN - -struct CollationData; - -namespace { - -/** - * Compare two signed int64_t values as if they were unsigned. - */ -int32_t -compareInt64AsUnsigned(int64_t a, int64_t b) { - if((uint64_t)a < (uint64_t)b) { - return -1; - } else if((uint64_t)a > (uint64_t)b) { - return 1; - } else { - return 0; - } -} - -// TODO: Merge this with the near-identical version in collationbasedatabuilder.cpp -/** - * Like Java Collections.binarySearch(List, String, Comparator). - * - * @return the index>=0 where the item was found, - * or the index<0 for inserting the string at ~index in sorted order - */ -int32_t -binarySearch(const int64_t list[], int32_t limit, int64_t ce) { - if (limit == 0) { return ~0; } - int32_t start = 0; - for (;;) { - int32_t i = (start + limit) / 2; - int32_t cmp = compareInt64AsUnsigned(ce, list[i]); - if (cmp == 0) { - return i; - } else if (cmp < 0) { - if (i == start) { - return ~start; // insert ce before i - } - limit = i; - } else { - if (i == start) { - return ~(start + 1); // insert ce after i - } - start = i; - } - } -} - -} // namespace - -CollationFastLatinBuilder::CollationFastLatinBuilder(UErrorCode &errorCode) - : ce0(0), ce1(0), - contractionCEs(errorCode), uniqueCEs(errorCode), - miniCEs(NULL), - firstDigitPrimary(0), firstLatinPrimary(0), lastLatinPrimary(0), - firstShortPrimary(0), shortPrimaryOverflow(false), - headerLength(0) { -} - -CollationFastLatinBuilder::~CollationFastLatinBuilder() { - uprv_free(miniCEs); -} - -UBool -CollationFastLatinBuilder::forData(const CollationData &data, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return false; } - if(!result.isEmpty()) { // This builder is not reusable. - errorCode = U_INVALID_STATE_ERROR; - return false; - } - if(!loadGroups(data, errorCode)) { return false; } - - // Fast handling of digits. - firstShortPrimary = firstDigitPrimary; - getCEs(data, errorCode); - if(!encodeUniqueCEs(errorCode)) { return false; } - if(shortPrimaryOverflow) { - // Give digits long mini primaries, - // so that there are more short primaries for letters. - firstShortPrimary = firstLatinPrimary; - resetCEs(); - getCEs(data, errorCode); - if(!encodeUniqueCEs(errorCode)) { return false; } - } - // Note: If we still have a short-primary overflow but not a long-primary overflow, - // then we could calculate how many more long primaries would fit, - // and set the firstShortPrimary to that many after the current firstShortPrimary, - // and try again. - // However, this might only benefit the en_US_POSIX tailoring, - // and it is simpler to suppress building fast Latin data for it in genrb, - // or by returning false here if shortPrimaryOverflow. - - UBool ok = !shortPrimaryOverflow && - encodeCharCEs(errorCode) && encodeContractions(errorCode); - contractionCEs.removeAllElements(); // might reduce heap memory usage - uniqueCEs.removeAllElements(); - return ok; -} - -UBool -CollationFastLatinBuilder::loadGroups(const CollationData &data, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return false; } - headerLength = 1 + NUM_SPECIAL_GROUPS; - uint32_t r0 = (CollationFastLatin::VERSION << 8) | headerLength; - result.append((UChar)r0); - // The first few reordering groups should be special groups - // (space, punct, ..., digit) followed by Latn, then Grek and other scripts. - for(int32_t i = 0; i < NUM_SPECIAL_GROUPS; ++i) { - lastSpecialPrimaries[i] = data.getLastPrimaryForGroup(UCOL_REORDER_CODE_FIRST + i); - if(lastSpecialPrimaries[i] == 0) { - // missing data - return false; - } - result.append((UChar)0); // reserve a slot for this group - } - - firstDigitPrimary = data.getFirstPrimaryForGroup(UCOL_REORDER_CODE_DIGIT); - firstLatinPrimary = data.getFirstPrimaryForGroup(USCRIPT_LATIN); - lastLatinPrimary = data.getLastPrimaryForGroup(USCRIPT_LATIN); - if(firstDigitPrimary == 0 || firstLatinPrimary == 0) { - // missing data - return false; - } - return true; -} - -UBool -CollationFastLatinBuilder::inSameGroup(uint32_t p, uint32_t q) const { - // Both or neither need to be encoded as short primaries, - // so that we can test only one and use the same bit mask. - if(p >= firstShortPrimary) { - return q >= firstShortPrimary; - } else if(q >= firstShortPrimary) { - return false; - } - // Both or neither must be potentially-variable, - // so that we can test only one and determine if both are variable. - uint32_t lastVariablePrimary = lastSpecialPrimaries[NUM_SPECIAL_GROUPS - 1]; - if(p > lastVariablePrimary) { - return q > lastVariablePrimary; - } else if(q > lastVariablePrimary) { - return false; - } - // Both will be encoded with long mini primaries. - // They must be in the same special reordering group, - // so that we can test only one and determine if both are variable. - U_ASSERT(p != 0 && q != 0); - for(int32_t i = 0;; ++i) { // will terminate - uint32_t lastPrimary = lastSpecialPrimaries[i]; - if(p <= lastPrimary) { - return q <= lastPrimary; - } else if(q <= lastPrimary) { - return false; - } - } -} - -void -CollationFastLatinBuilder::resetCEs() { - contractionCEs.removeAllElements(); - uniqueCEs.removeAllElements(); - shortPrimaryOverflow = false; - result.truncate(headerLength); -} - -void -CollationFastLatinBuilder::getCEs(const CollationData &data, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - int32_t i = 0; - for(UChar c = 0;; ++i, ++c) { - if(c == CollationFastLatin::LATIN_LIMIT) { - c = CollationFastLatin::PUNCT_START; - } else if(c == CollationFastLatin::PUNCT_LIMIT) { - break; - } - const CollationData *d; - uint32_t ce32 = data.getCE32(c); - if(ce32 == Collation::FALLBACK_CE32) { - d = data.base; - ce32 = d->getCE32(c); - } else { - d = &data; - } - if(getCEsFromCE32(*d, c, ce32, errorCode)) { - charCEs[i][0] = ce0; - charCEs[i][1] = ce1; - addUniqueCE(ce0, errorCode); - addUniqueCE(ce1, errorCode); - } else { - // bail out for c - charCEs[i][0] = ce0 = Collation::NO_CE; - charCEs[i][1] = ce1 = 0; - } - if(c == 0 && !isContractionCharCE(ce0)) { - // Always map U+0000 to a contraction. - // Write a contraction list with only a default value if there is no real contraction. - U_ASSERT(contractionCEs.isEmpty()); - addContractionEntry(CollationFastLatin::CONTR_CHAR_MASK, ce0, ce1, errorCode); - charCEs[0][0] = ((int64_t)Collation::NO_CE_PRIMARY << 32) | CONTRACTION_FLAG; - charCEs[0][1] = 0; - } - } - // Terminate the last contraction list. - contractionCEs.addElement(CollationFastLatin::CONTR_CHAR_MASK, errorCode); -} - -UBool -CollationFastLatinBuilder::getCEsFromCE32(const CollationData &data, UChar32 c, uint32_t ce32, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return false; } - ce32 = data.getFinalCE32(ce32); - ce1 = 0; - if(Collation::isSimpleOrLongCE32(ce32)) { - ce0 = Collation::ceFromCE32(ce32); - } else { - switch(Collation::tagFromCE32(ce32)) { - case Collation::LATIN_EXPANSION_TAG: - ce0 = Collation::latinCE0FromCE32(ce32); - ce1 = Collation::latinCE1FromCE32(ce32); - break; - case Collation::EXPANSION32_TAG: { - const uint32_t *ce32s = data.ce32s + Collation::indexFromCE32(ce32); - int32_t length = Collation::lengthFromCE32(ce32); - if(length <= 2) { - ce0 = Collation::ceFromCE32(ce32s[0]); - if(length == 2) { - ce1 = Collation::ceFromCE32(ce32s[1]); - } - break; - } else { - return false; - } - } - case Collation::EXPANSION_TAG: { - const int64_t *ces = data.ces + Collation::indexFromCE32(ce32); - int32_t length = Collation::lengthFromCE32(ce32); - if(length <= 2) { - ce0 = ces[0]; - if(length == 2) { - ce1 = ces[1]; - } - break; - } else { - return false; - } - } - // Note: We could support PREFIX_TAG (assert c>=0) - // by recursing on its default CE32 and checking that none of the prefixes starts - // with a fast Latin character. - // However, currently (2013) there are only the L-before-middle-dot - // prefix mappings in the Latin range, and those would be rejected anyway. - case Collation::CONTRACTION_TAG: - U_ASSERT(c >= 0); - return getCEsFromContractionCE32(data, ce32, errorCode); - case Collation::OFFSET_TAG: - U_ASSERT(c >= 0); - ce0 = data.getCEFromOffsetCE32(c, ce32); - break; - default: - return false; - } - } - // A mapping can be completely ignorable. - if(ce0 == 0) { return ce1 == 0; } - // We do not support an ignorable ce0 unless it is completely ignorable. - uint32_t p0 = (uint32_t)(ce0 >> 32); - if(p0 == 0) { return false; } - // We only support primaries up to the Latin script. - if(p0 > lastLatinPrimary) { return false; } - // We support non-common secondary and case weights only together with short primaries. - uint32_t lower32_0 = (uint32_t)ce0; - if(p0 < firstShortPrimary) { - uint32_t sc0 = lower32_0 & Collation::SECONDARY_AND_CASE_MASK; - if(sc0 != Collation::COMMON_SECONDARY_CE) { return false; } - } - // No below-common tertiary weights. - if((lower32_0 & Collation::ONLY_TERTIARY_MASK) < Collation::COMMON_WEIGHT16) { return false; } - if(ce1 != 0) { - // Both primaries must be in the same group, - // or both must get short mini primaries, - // or a short-primary CE is followed by a secondary CE. - // This is so that we can test the first primary and use the same mask for both, - // and determine for both whether they are variable. - uint32_t p1 = (uint32_t)(ce1 >> 32); - if(p1 == 0 ? p0 < firstShortPrimary : !inSameGroup(p0, p1)) { return false; } - uint32_t lower32_1 = (uint32_t)ce1; - // No tertiary CEs. - if((lower32_1 >> 16) == 0) { return false; } - // We support non-common secondary and case weights - // only for secondary CEs or together with short primaries. - if(p1 != 0 && p1 < firstShortPrimary) { - uint32_t sc1 = lower32_1 & Collation::SECONDARY_AND_CASE_MASK; - if(sc1 != Collation::COMMON_SECONDARY_CE) { return false; } - } - // No below-common tertiary weights. - if((lower32_1 & Collation::ONLY_TERTIARY_MASK) < Collation::COMMON_WEIGHT16) { return false; } - } - // No quaternary weights. - if(((ce0 | ce1) & Collation::QUATERNARY_MASK) != 0) { return false; } - return true; -} - -UBool -CollationFastLatinBuilder::getCEsFromContractionCE32(const CollationData &data, uint32_t ce32, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return false; } - const UChar *p = data.contexts + Collation::indexFromCE32(ce32); - ce32 = CollationData::readCE32(p); // Default if no suffix match. - // Since the original ce32 is not a prefix mapping, - // the default ce32 must not be another contraction. - U_ASSERT(!Collation::isContractionCE32(ce32)); - int32_t contractionIndex = contractionCEs.size(); - if(getCEsFromCE32(data, U_SENTINEL, ce32, errorCode)) { - addContractionEntry(CollationFastLatin::CONTR_CHAR_MASK, ce0, ce1, errorCode); - } else { - // Bail out for c-without-contraction. - addContractionEntry(CollationFastLatin::CONTR_CHAR_MASK, Collation::NO_CE, 0, errorCode); - } - // Handle an encodable contraction unless the next contraction is too long - // and starts with the same character. - int32_t prevX = -1; - UBool addContraction = false; - UCharsTrie::Iterator suffixes(p + 2, 0, errorCode); - while(suffixes.next(errorCode)) { - const UnicodeString &suffix = suffixes.getString(); - int32_t x = CollationFastLatin::getCharIndex(suffix.charAt(0)); - if(x < 0) { continue; } // ignore anything but fast Latin text - if(x == prevX) { - if(addContraction) { - // Bail out for all contractions starting with this character. - addContractionEntry(x, Collation::NO_CE, 0, errorCode); - addContraction = false; - } - continue; - } - if(addContraction) { - addContractionEntry(prevX, ce0, ce1, errorCode); - } - ce32 = (uint32_t)suffixes.getValue(); - if(suffix.length() == 1 && getCEsFromCE32(data, U_SENTINEL, ce32, errorCode)) { - addContraction = true; - } else { - addContractionEntry(x, Collation::NO_CE, 0, errorCode); - addContraction = false; - } - prevX = x; - } - if(addContraction) { - addContractionEntry(prevX, ce0, ce1, errorCode); - } - if(U_FAILURE(errorCode)) { return false; } - // Note: There might not be any fast Latin contractions, but - // we need to enter contraction handling anyway so that we can bail out - // when there is a non-fast-Latin character following. - // For example: Danish &Y<> 32) == Collation::NO_CE_PRIMARY) { return; } - ce &= ~(int64_t)Collation::CASE_MASK; // blank out case bits - int32_t i = binarySearch(uniqueCEs.getBuffer(), uniqueCEs.size(), ce); - if(i < 0) { - uniqueCEs.insertElementAt(ce, ~i, errorCode); - } -} - -uint32_t -CollationFastLatinBuilder::getMiniCE(int64_t ce) const { - ce &= ~(int64_t)Collation::CASE_MASK; // blank out case bits - int32_t index = binarySearch(uniqueCEs.getBuffer(), uniqueCEs.size(), ce); - U_ASSERT(index >= 0); - return miniCEs[index]; -} - -UBool -CollationFastLatinBuilder::encodeUniqueCEs(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return false; } - uprv_free(miniCEs); - miniCEs = (uint16_t *)uprv_malloc(uniqueCEs.size() * 2); - if(miniCEs == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return false; - } - int32_t group = 0; - uint32_t lastGroupPrimary = lastSpecialPrimaries[group]; - // The lowest unique CE must be at least a secondary CE. - U_ASSERT(((uint32_t)uniqueCEs.elementAti(0) >> 16) != 0); - uint32_t prevPrimary = 0; - uint32_t prevSecondary = 0; - uint32_t pri = 0; - uint32_t sec = 0; - uint32_t ter = CollationFastLatin::COMMON_TER; - for(int32_t i = 0; i < uniqueCEs.size(); ++i) { - int64_t ce = uniqueCEs.elementAti(i); - // Note: At least one of the p/s/t weights changes from one unique CE to the next. - // (uniqueCEs does not store case bits.) - uint32_t p = (uint32_t)(ce >> 32); - if(p != prevPrimary) { - while(p > lastGroupPrimary) { - U_ASSERT(pri <= CollationFastLatin::MAX_LONG); - // Set the group's header entry to the - // last "long primary" in or before the group. - result.setCharAt(1 + group, (UChar)pri); - if(++group < NUM_SPECIAL_GROUPS) { - lastGroupPrimary = lastSpecialPrimaries[group]; - } else { - lastGroupPrimary = 0xffffffff; - break; - } - } - if(p < firstShortPrimary) { - if(pri == 0) { - pri = CollationFastLatin::MIN_LONG; - } else if(pri < CollationFastLatin::MAX_LONG) { - pri += CollationFastLatin::LONG_INC; - } else { -#if DEBUG_COLLATION_FAST_LATIN_BUILDER - printf("long-primary overflow for %08x\n", p); -#endif - miniCEs[i] = CollationFastLatin::BAIL_OUT; - continue; - } - } else { - if(pri < CollationFastLatin::MIN_SHORT) { - pri = CollationFastLatin::MIN_SHORT; - } else if(pri < (CollationFastLatin::MAX_SHORT - CollationFastLatin::SHORT_INC)) { - // Reserve the highest primary weight for U+FFFF. - pri += CollationFastLatin::SHORT_INC; - } else { -#if DEBUG_COLLATION_FAST_LATIN_BUILDER - printf("short-primary overflow for %08x\n", p); -#endif - shortPrimaryOverflow = true; - miniCEs[i] = CollationFastLatin::BAIL_OUT; - continue; - } - } - prevPrimary = p; - prevSecondary = Collation::COMMON_WEIGHT16; - sec = CollationFastLatin::COMMON_SEC; - ter = CollationFastLatin::COMMON_TER; - } - uint32_t lower32 = (uint32_t)ce; - uint32_t s = lower32 >> 16; - if(s != prevSecondary) { - if(pri == 0) { - if(sec == 0) { - sec = CollationFastLatin::MIN_SEC_HIGH; - } else if(sec < CollationFastLatin::MAX_SEC_HIGH) { - sec += CollationFastLatin::SEC_INC; - } else { - miniCEs[i] = CollationFastLatin::BAIL_OUT; - continue; - } - prevSecondary = s; - ter = CollationFastLatin::COMMON_TER; - } else if(s < Collation::COMMON_WEIGHT16) { - if(sec == CollationFastLatin::COMMON_SEC) { - sec = CollationFastLatin::MIN_SEC_BEFORE; - } else if(sec < CollationFastLatin::MAX_SEC_BEFORE) { - sec += CollationFastLatin::SEC_INC; - } else { - miniCEs[i] = CollationFastLatin::BAIL_OUT; - continue; - } - } else if(s == Collation::COMMON_WEIGHT16) { - sec = CollationFastLatin::COMMON_SEC; - } else { - if(sec < CollationFastLatin::MIN_SEC_AFTER) { - sec = CollationFastLatin::MIN_SEC_AFTER; - } else if(sec < CollationFastLatin::MAX_SEC_AFTER) { - sec += CollationFastLatin::SEC_INC; - } else { - miniCEs[i] = CollationFastLatin::BAIL_OUT; - continue; - } - } - prevSecondary = s; - ter = CollationFastLatin::COMMON_TER; - } - U_ASSERT((lower32 & Collation::CASE_MASK) == 0); // blanked out in uniqueCEs - uint32_t t = lower32 & Collation::ONLY_TERTIARY_MASK; - if(t > Collation::COMMON_WEIGHT16) { - if(ter < CollationFastLatin::MAX_TER_AFTER) { - ++ter; - } else { - miniCEs[i] = CollationFastLatin::BAIL_OUT; - continue; - } - } - if(CollationFastLatin::MIN_LONG <= pri && pri <= CollationFastLatin::MAX_LONG) { - U_ASSERT(sec == CollationFastLatin::COMMON_SEC); - miniCEs[i] = (uint16_t)(pri | ter); - } else { - miniCEs[i] = (uint16_t)(pri | sec | ter); - } - } -#if DEBUG_COLLATION_FAST_LATIN_BUILDER - printf("last mini primary: %04x\n", pri); -#endif -#if DEBUG_COLLATION_FAST_LATIN_BUILDER >= 2 - for(int32_t i = 0; i < uniqueCEs.size(); ++i) { - int64_t ce = uniqueCEs.elementAti(i); - printf("unique CE 0x%016lx -> 0x%04x\n", ce, miniCEs[i]); - } -#endif - return U_SUCCESS(errorCode); -} - -UBool -CollationFastLatinBuilder::encodeCharCEs(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return false; } - int32_t miniCEsStart = result.length(); - for(int32_t i = 0; i < CollationFastLatin::NUM_FAST_CHARS; ++i) { - result.append((UChar)0); // initialize to completely ignorable - } - int32_t indexBase = result.length(); - for(int32_t i = 0; i < CollationFastLatin::NUM_FAST_CHARS; ++i) { - int64_t ce = charCEs[i][0]; - if(isContractionCharCE(ce)) { continue; } // defer contraction - uint32_t miniCE = encodeTwoCEs(ce, charCEs[i][1]); - if(miniCE > 0xffff) { - // Note: There is a chance that this new expansion is the same as a previous one, - // and if so, then we could reuse the other expansion. - // However, that seems unlikely. - int32_t expansionIndex = result.length() - indexBase; - if(expansionIndex > (int32_t)CollationFastLatin::INDEX_MASK) { - miniCE = CollationFastLatin::BAIL_OUT; - } else { - result.append((UChar)(miniCE >> 16)).append((UChar)miniCE); - miniCE = CollationFastLatin::EXPANSION | expansionIndex; - } - } - result.setCharAt(miniCEsStart + i, (UChar)miniCE); - } - return U_SUCCESS(errorCode); -} - -UBool -CollationFastLatinBuilder::encodeContractions(UErrorCode &errorCode) { - // We encode all contraction lists so that the first word of a list - // terminates the previous list, and we only need one additional terminator at the end. - if(U_FAILURE(errorCode)) { return false; } - int32_t indexBase = headerLength + CollationFastLatin::NUM_FAST_CHARS; - int32_t firstContractionIndex = result.length(); - for(int32_t i = 0; i < CollationFastLatin::NUM_FAST_CHARS; ++i) { - int64_t ce = charCEs[i][0]; - if(!isContractionCharCE(ce)) { continue; } - int32_t contractionIndex = result.length() - indexBase; - if(contractionIndex > (int32_t)CollationFastLatin::INDEX_MASK) { - result.setCharAt(headerLength + i, CollationFastLatin::BAIL_OUT); - continue; - } - UBool firstTriple = true; - for(int32_t index = (int32_t)ce & 0x7fffffff;; index += 3) { - int32_t x = static_cast(contractionCEs.elementAti(index)); - if((uint32_t)x == CollationFastLatin::CONTR_CHAR_MASK && !firstTriple) { break; } - int64_t cce0 = contractionCEs.elementAti(index + 1); - int64_t cce1 = contractionCEs.elementAti(index + 2); - uint32_t miniCE = encodeTwoCEs(cce0, cce1); - if(miniCE == CollationFastLatin::BAIL_OUT) { - result.append((UChar)(x | (1 << CollationFastLatin::CONTR_LENGTH_SHIFT))); - } else if(miniCE <= 0xffff) { - result.append((UChar)(x | (2 << CollationFastLatin::CONTR_LENGTH_SHIFT))); - result.append((UChar)miniCE); - } else { - result.append((UChar)(x | (3 << CollationFastLatin::CONTR_LENGTH_SHIFT))); - result.append((UChar)(miniCE >> 16)).append((UChar)miniCE); - } - firstTriple = false; - } - // Note: There is a chance that this new contraction list is the same as a previous one, - // and if so, then we could truncate the result and reuse the other list. - // However, that seems unlikely. - result.setCharAt(headerLength + i, - (UChar)(CollationFastLatin::CONTRACTION | contractionIndex)); - } - if(result.length() > firstContractionIndex) { - // Terminate the last contraction list. - result.append((UChar)CollationFastLatin::CONTR_CHAR_MASK); - } - if(result.isBogus()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return false; - } -#if DEBUG_COLLATION_FAST_LATIN_BUILDER - printf("** fast Latin %d * 2 = %d bytes\n", result.length(), result.length() * 2); - puts(" header & below-digit groups map"); - int32_t i = 0; - for(; i < headerLength; ++i) { - printf(" %04x", result[i]); - } - printf("\n char mini CEs"); - U_ASSERT(CollationFastLatin::NUM_FAST_CHARS % 16 == 0); - for(; i < indexBase; i += 16) { - UChar32 c = i - headerLength; - if(c >= CollationFastLatin::LATIN_LIMIT) { - c = CollationFastLatin::PUNCT_START + c - CollationFastLatin::LATIN_LIMIT; - } - printf("\n %04x:", c); - for(int32_t j = 0; j < 16; ++j) { - printf(" %04x", result[i + j]); - } - } - printf("\n expansions & contractions"); - for(; i < result.length(); ++i) { - if((i - indexBase) % 16 == 0) { puts(""); } - printf(" %04x", result[i]); - } - puts(""); -#endif - return true; -} - -uint32_t -CollationFastLatinBuilder::encodeTwoCEs(int64_t first, int64_t second) const { - if(first == 0) { - return 0; // completely ignorable - } - if(first == Collation::NO_CE) { - return CollationFastLatin::BAIL_OUT; - } - U_ASSERT((uint32_t)(first >> 32) != Collation::NO_CE_PRIMARY); - - uint32_t miniCE = getMiniCE(first); - if(miniCE == CollationFastLatin::BAIL_OUT) { return miniCE; } - if(miniCE >= CollationFastLatin::MIN_SHORT) { - // Extract & copy the case bits. - // Shift them from normal CE bits 15..14 to mini CE bits 4..3. - uint32_t c = (((uint32_t)first & Collation::CASE_MASK) >> (14 - 3)); - // Only in mini CEs: Ignorable case bits = 0, lowercase = 1. - c += CollationFastLatin::LOWER_CASE; - miniCE |= c; - } - if(second == 0) { return miniCE; } - - uint32_t miniCE1 = getMiniCE(second); - if(miniCE1 == CollationFastLatin::BAIL_OUT) { return miniCE1; } - - uint32_t case1 = (uint32_t)second & Collation::CASE_MASK; - if(miniCE >= CollationFastLatin::MIN_SHORT && - (miniCE & CollationFastLatin::SECONDARY_MASK) == CollationFastLatin::COMMON_SEC) { - // Try to combine the two mini CEs into one. - uint32_t sec1 = miniCE1 & CollationFastLatin::SECONDARY_MASK; - uint32_t ter1 = miniCE1 & CollationFastLatin::TERTIARY_MASK; - if(sec1 >= CollationFastLatin::MIN_SEC_HIGH && case1 == 0 && - ter1 == CollationFastLatin::COMMON_TER) { - // sec1>=sec_high implies pri1==0. - return (miniCE & ~CollationFastLatin::SECONDARY_MASK) | sec1; - } - } - - if(miniCE1 <= CollationFastLatin::SECONDARY_MASK || CollationFastLatin::MIN_SHORT <= miniCE1) { - // Secondary CE, or a CE with a short primary, copy the case bits. - case1 = (case1 >> (14 - 3)) + CollationFastLatin::LOWER_CASE; - miniCE1 |= case1; - } - return (miniCE << 16) | miniCE1; -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationfastlatinbuilder.cpp +* +* created on: 2013aug09 +* created by: Markus W. Scherer +*/ + +#define DEBUG_COLLATION_FAST_LATIN_BUILDER 0 // 0 or 1 or 2 +#if DEBUG_COLLATION_FAST_LATIN_BUILDER +#include +#include +#endif + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/ucol.h" +#include "unicode/ucharstrie.h" +#include "unicode/unistr.h" +#include "unicode/uobject.h" +#include "unicode/uscript.h" +#include "cmemory.h" +#include "collation.h" +#include "collationdata.h" +#include "collationfastlatin.h" +#include "collationfastlatinbuilder.h" +#include "uassert.h" +#include "uvectr64.h" + +U_NAMESPACE_BEGIN + +struct CollationData; + +namespace { + +/** + * Compare two signed int64_t values as if they were unsigned. + */ +int32_t +compareInt64AsUnsigned(int64_t a, int64_t b) { + if((uint64_t)a < (uint64_t)b) { + return -1; + } else if((uint64_t)a > (uint64_t)b) { + return 1; + } else { + return 0; + } +} + +// TODO: Merge this with the near-identical version in collationbasedatabuilder.cpp +/** + * Like Java Collections.binarySearch(List, String, Comparator). + * + * @return the index>=0 where the item was found, + * or the index<0 for inserting the string at ~index in sorted order + */ +int32_t +binarySearch(const int64_t list[], int32_t limit, int64_t ce) { + if (limit == 0) { return ~0; } + int32_t start = 0; + for (;;) { + int32_t i = (start + limit) / 2; + int32_t cmp = compareInt64AsUnsigned(ce, list[i]); + if (cmp == 0) { + return i; + } else if (cmp < 0) { + if (i == start) { + return ~start; // insert ce before i + } + limit = i; + } else { + if (i == start) { + return ~(start + 1); // insert ce after i + } + start = i; + } + } +} + +} // namespace + +CollationFastLatinBuilder::CollationFastLatinBuilder(UErrorCode &errorCode) + : ce0(0), ce1(0), + contractionCEs(errorCode), uniqueCEs(errorCode), + miniCEs(nullptr), + firstDigitPrimary(0), firstLatinPrimary(0), lastLatinPrimary(0), + firstShortPrimary(0), shortPrimaryOverflow(false), + headerLength(0) { +} + +CollationFastLatinBuilder::~CollationFastLatinBuilder() { + uprv_free(miniCEs); +} + +UBool +CollationFastLatinBuilder::forData(const CollationData &data, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return false; } + if(!result.isEmpty()) { // This builder is not reusable. + errorCode = U_INVALID_STATE_ERROR; + return false; + } + if(!loadGroups(data, errorCode)) { return false; } + + // Fast handling of digits. + firstShortPrimary = firstDigitPrimary; + getCEs(data, errorCode); + if(!encodeUniqueCEs(errorCode)) { return false; } + if(shortPrimaryOverflow) { + // Give digits long mini primaries, + // so that there are more short primaries for letters. + firstShortPrimary = firstLatinPrimary; + resetCEs(); + getCEs(data, errorCode); + if(!encodeUniqueCEs(errorCode)) { return false; } + } + // Note: If we still have a short-primary overflow but not a long-primary overflow, + // then we could calculate how many more long primaries would fit, + // and set the firstShortPrimary to that many after the current firstShortPrimary, + // and try again. + // However, this might only benefit the en_US_POSIX tailoring, + // and it is simpler to suppress building fast Latin data for it in genrb, + // or by returning false here if shortPrimaryOverflow. + + UBool ok = !shortPrimaryOverflow && + encodeCharCEs(errorCode) && encodeContractions(errorCode); + contractionCEs.removeAllElements(); // might reduce heap memory usage + uniqueCEs.removeAllElements(); + return ok; +} + +UBool +CollationFastLatinBuilder::loadGroups(const CollationData &data, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return false; } + headerLength = 1 + NUM_SPECIAL_GROUPS; + uint32_t r0 = (CollationFastLatin::VERSION << 8) | headerLength; + result.append((char16_t)r0); + // The first few reordering groups should be special groups + // (space, punct, ..., digit) followed by Latn, then Grek and other scripts. + for(int32_t i = 0; i < NUM_SPECIAL_GROUPS; ++i) { + lastSpecialPrimaries[i] = data.getLastPrimaryForGroup(UCOL_REORDER_CODE_FIRST + i); + if(lastSpecialPrimaries[i] == 0) { + // missing data + return false; + } + result.append((char16_t)0); // reserve a slot for this group + } + + firstDigitPrimary = data.getFirstPrimaryForGroup(UCOL_REORDER_CODE_DIGIT); + firstLatinPrimary = data.getFirstPrimaryForGroup(USCRIPT_LATIN); + lastLatinPrimary = data.getLastPrimaryForGroup(USCRIPT_LATIN); + if(firstDigitPrimary == 0 || firstLatinPrimary == 0) { + // missing data + return false; + } + return true; +} + +UBool +CollationFastLatinBuilder::inSameGroup(uint32_t p, uint32_t q) const { + // Both or neither need to be encoded as short primaries, + // so that we can test only one and use the same bit mask. + if(p >= firstShortPrimary) { + return q >= firstShortPrimary; + } else if(q >= firstShortPrimary) { + return false; + } + // Both or neither must be potentially-variable, + // so that we can test only one and determine if both are variable. + uint32_t lastVariablePrimary = lastSpecialPrimaries[NUM_SPECIAL_GROUPS - 1]; + if(p > lastVariablePrimary) { + return q > lastVariablePrimary; + } else if(q > lastVariablePrimary) { + return false; + } + // Both will be encoded with long mini primaries. + // They must be in the same special reordering group, + // so that we can test only one and determine if both are variable. + U_ASSERT(p != 0 && q != 0); + for(int32_t i = 0;; ++i) { // will terminate + uint32_t lastPrimary = lastSpecialPrimaries[i]; + if(p <= lastPrimary) { + return q <= lastPrimary; + } else if(q <= lastPrimary) { + return false; + } + } +} + +void +CollationFastLatinBuilder::resetCEs() { + contractionCEs.removeAllElements(); + uniqueCEs.removeAllElements(); + shortPrimaryOverflow = false; + result.truncate(headerLength); +} + +void +CollationFastLatinBuilder::getCEs(const CollationData &data, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + int32_t i = 0; + for(char16_t c = 0;; ++i, ++c) { + if(c == CollationFastLatin::LATIN_LIMIT) { + c = CollationFastLatin::PUNCT_START; + } else if(c == CollationFastLatin::PUNCT_LIMIT) { + break; + } + const CollationData *d; + uint32_t ce32 = data.getCE32(c); + if(ce32 == Collation::FALLBACK_CE32) { + d = data.base; + ce32 = d->getCE32(c); + } else { + d = &data; + } + if(getCEsFromCE32(*d, c, ce32, errorCode)) { + charCEs[i][0] = ce0; + charCEs[i][1] = ce1; + addUniqueCE(ce0, errorCode); + addUniqueCE(ce1, errorCode); + } else { + // bail out for c + charCEs[i][0] = ce0 = Collation::NO_CE; + charCEs[i][1] = ce1 = 0; + } + if(c == 0 && !isContractionCharCE(ce0)) { + // Always map U+0000 to a contraction. + // Write a contraction list with only a default value if there is no real contraction. + U_ASSERT(contractionCEs.isEmpty()); + addContractionEntry(CollationFastLatin::CONTR_CHAR_MASK, ce0, ce1, errorCode); + charCEs[0][0] = ((int64_t)Collation::NO_CE_PRIMARY << 32) | CONTRACTION_FLAG; + charCEs[0][1] = 0; + } + } + // Terminate the last contraction list. + contractionCEs.addElement(CollationFastLatin::CONTR_CHAR_MASK, errorCode); +} + +UBool +CollationFastLatinBuilder::getCEsFromCE32(const CollationData &data, UChar32 c, uint32_t ce32, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return false; } + ce32 = data.getFinalCE32(ce32); + ce1 = 0; + if(Collation::isSimpleOrLongCE32(ce32)) { + ce0 = Collation::ceFromCE32(ce32); + } else { + switch(Collation::tagFromCE32(ce32)) { + case Collation::LATIN_EXPANSION_TAG: + ce0 = Collation::latinCE0FromCE32(ce32); + ce1 = Collation::latinCE1FromCE32(ce32); + break; + case Collation::EXPANSION32_TAG: { + const uint32_t *ce32s = data.ce32s + Collation::indexFromCE32(ce32); + int32_t length = Collation::lengthFromCE32(ce32); + if(length <= 2) { + ce0 = Collation::ceFromCE32(ce32s[0]); + if(length == 2) { + ce1 = Collation::ceFromCE32(ce32s[1]); + } + break; + } else { + return false; + } + } + case Collation::EXPANSION_TAG: { + const int64_t *ces = data.ces + Collation::indexFromCE32(ce32); + int32_t length = Collation::lengthFromCE32(ce32); + if(length <= 2) { + ce0 = ces[0]; + if(length == 2) { + ce1 = ces[1]; + } + break; + } else { + return false; + } + } + // Note: We could support PREFIX_TAG (assert c>=0) + // by recursing on its default CE32 and checking that none of the prefixes starts + // with a fast Latin character. + // However, currently (2013) there are only the L-before-middle-dot + // prefix mappings in the Latin range, and those would be rejected anyway. + case Collation::CONTRACTION_TAG: + U_ASSERT(c >= 0); + return getCEsFromContractionCE32(data, ce32, errorCode); + case Collation::OFFSET_TAG: + U_ASSERT(c >= 0); + ce0 = data.getCEFromOffsetCE32(c, ce32); + break; + default: + return false; + } + } + // A mapping can be completely ignorable. + if(ce0 == 0) { return ce1 == 0; } + // We do not support an ignorable ce0 unless it is completely ignorable. + uint32_t p0 = (uint32_t)(ce0 >> 32); + if(p0 == 0) { return false; } + // We only support primaries up to the Latin script. + if(p0 > lastLatinPrimary) { return false; } + // We support non-common secondary and case weights only together with short primaries. + uint32_t lower32_0 = (uint32_t)ce0; + if(p0 < firstShortPrimary) { + uint32_t sc0 = lower32_0 & Collation::SECONDARY_AND_CASE_MASK; + if(sc0 != Collation::COMMON_SECONDARY_CE) { return false; } + } + // No below-common tertiary weights. + if((lower32_0 & Collation::ONLY_TERTIARY_MASK) < Collation::COMMON_WEIGHT16) { return false; } + if(ce1 != 0) { + // Both primaries must be in the same group, + // or both must get short mini primaries, + // or a short-primary CE is followed by a secondary CE. + // This is so that we can test the first primary and use the same mask for both, + // and determine for both whether they are variable. + uint32_t p1 = (uint32_t)(ce1 >> 32); + if(p1 == 0 ? p0 < firstShortPrimary : !inSameGroup(p0, p1)) { return false; } + uint32_t lower32_1 = (uint32_t)ce1; + // No tertiary CEs. + if((lower32_1 >> 16) == 0) { return false; } + // We support non-common secondary and case weights + // only for secondary CEs or together with short primaries. + if(p1 != 0 && p1 < firstShortPrimary) { + uint32_t sc1 = lower32_1 & Collation::SECONDARY_AND_CASE_MASK; + if(sc1 != Collation::COMMON_SECONDARY_CE) { return false; } + } + // No below-common tertiary weights. + if((lower32_1 & Collation::ONLY_TERTIARY_MASK) < Collation::COMMON_WEIGHT16) { return false; } + } + // No quaternary weights. + if(((ce0 | ce1) & Collation::QUATERNARY_MASK) != 0) { return false; } + return true; +} + +UBool +CollationFastLatinBuilder::getCEsFromContractionCE32(const CollationData &data, uint32_t ce32, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return false; } + const char16_t *p = data.contexts + Collation::indexFromCE32(ce32); + ce32 = CollationData::readCE32(p); // Default if no suffix match. + // Since the original ce32 is not a prefix mapping, + // the default ce32 must not be another contraction. + U_ASSERT(!Collation::isContractionCE32(ce32)); + int32_t contractionIndex = contractionCEs.size(); + if(getCEsFromCE32(data, U_SENTINEL, ce32, errorCode)) { + addContractionEntry(CollationFastLatin::CONTR_CHAR_MASK, ce0, ce1, errorCode); + } else { + // Bail out for c-without-contraction. + addContractionEntry(CollationFastLatin::CONTR_CHAR_MASK, Collation::NO_CE, 0, errorCode); + } + // Handle an encodable contraction unless the next contraction is too long + // and starts with the same character. + int32_t prevX = -1; + UBool addContraction = false; + UCharsTrie::Iterator suffixes(p + 2, 0, errorCode); + while(suffixes.next(errorCode)) { + const UnicodeString &suffix = suffixes.getString(); + int32_t x = CollationFastLatin::getCharIndex(suffix.charAt(0)); + if(x < 0) { continue; } // ignore anything but fast Latin text + if(x == prevX) { + if(addContraction) { + // Bail out for all contractions starting with this character. + addContractionEntry(x, Collation::NO_CE, 0, errorCode); + addContraction = false; + } + continue; + } + if(addContraction) { + addContractionEntry(prevX, ce0, ce1, errorCode); + } + ce32 = (uint32_t)suffixes.getValue(); + if(suffix.length() == 1 && getCEsFromCE32(data, U_SENTINEL, ce32, errorCode)) { + addContraction = true; + } else { + addContractionEntry(x, Collation::NO_CE, 0, errorCode); + addContraction = false; + } + prevX = x; + } + if(addContraction) { + addContractionEntry(prevX, ce0, ce1, errorCode); + } + if(U_FAILURE(errorCode)) { return false; } + // Note: There might not be any fast Latin contractions, but + // we need to enter contraction handling anyway so that we can bail out + // when there is a non-fast-Latin character following. + // For example: Danish &Y<> 32) == Collation::NO_CE_PRIMARY) { return; } + ce &= ~(int64_t)Collation::CASE_MASK; // blank out case bits + int32_t i = binarySearch(uniqueCEs.getBuffer(), uniqueCEs.size(), ce); + if(i < 0) { + uniqueCEs.insertElementAt(ce, ~i, errorCode); + } +} + +uint32_t +CollationFastLatinBuilder::getMiniCE(int64_t ce) const { + ce &= ~(int64_t)Collation::CASE_MASK; // blank out case bits + int32_t index = binarySearch(uniqueCEs.getBuffer(), uniqueCEs.size(), ce); + U_ASSERT(index >= 0); + return miniCEs[index]; +} + +UBool +CollationFastLatinBuilder::encodeUniqueCEs(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return false; } + uprv_free(miniCEs); + miniCEs = (uint16_t *)uprv_malloc(uniqueCEs.size() * 2); + if(miniCEs == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return false; + } + int32_t group = 0; + uint32_t lastGroupPrimary = lastSpecialPrimaries[group]; + // The lowest unique CE must be at least a secondary CE. + U_ASSERT(((uint32_t)uniqueCEs.elementAti(0) >> 16) != 0); + uint32_t prevPrimary = 0; + uint32_t prevSecondary = 0; + uint32_t pri = 0; + uint32_t sec = 0; + uint32_t ter = CollationFastLatin::COMMON_TER; + for(int32_t i = 0; i < uniqueCEs.size(); ++i) { + int64_t ce = uniqueCEs.elementAti(i); + // Note: At least one of the p/s/t weights changes from one unique CE to the next. + // (uniqueCEs does not store case bits.) + uint32_t p = (uint32_t)(ce >> 32); + if(p != prevPrimary) { + while(p > lastGroupPrimary) { + U_ASSERT(pri <= CollationFastLatin::MAX_LONG); + // Set the group's header entry to the + // last "long primary" in or before the group. + result.setCharAt(1 + group, (char16_t)pri); + if(++group < NUM_SPECIAL_GROUPS) { + lastGroupPrimary = lastSpecialPrimaries[group]; + } else { + lastGroupPrimary = 0xffffffff; + break; + } + } + if(p < firstShortPrimary) { + if(pri == 0) { + pri = CollationFastLatin::MIN_LONG; + } else if(pri < CollationFastLatin::MAX_LONG) { + pri += CollationFastLatin::LONG_INC; + } else { +#if DEBUG_COLLATION_FAST_LATIN_BUILDER + printf("long-primary overflow for %08x\n", p); +#endif + miniCEs[i] = CollationFastLatin::BAIL_OUT; + continue; + } + } else { + if(pri < CollationFastLatin::MIN_SHORT) { + pri = CollationFastLatin::MIN_SHORT; + } else if(pri < (CollationFastLatin::MAX_SHORT - CollationFastLatin::SHORT_INC)) { + // Reserve the highest primary weight for U+FFFF. + pri += CollationFastLatin::SHORT_INC; + } else { +#if DEBUG_COLLATION_FAST_LATIN_BUILDER + printf("short-primary overflow for %08x\n", p); +#endif + shortPrimaryOverflow = true; + miniCEs[i] = CollationFastLatin::BAIL_OUT; + continue; + } + } + prevPrimary = p; + prevSecondary = Collation::COMMON_WEIGHT16; + sec = CollationFastLatin::COMMON_SEC; + ter = CollationFastLatin::COMMON_TER; + } + uint32_t lower32 = (uint32_t)ce; + uint32_t s = lower32 >> 16; + if(s != prevSecondary) { + if(pri == 0) { + if(sec == 0) { + sec = CollationFastLatin::MIN_SEC_HIGH; + } else if(sec < CollationFastLatin::MAX_SEC_HIGH) { + sec += CollationFastLatin::SEC_INC; + } else { + miniCEs[i] = CollationFastLatin::BAIL_OUT; + continue; + } + prevSecondary = s; + ter = CollationFastLatin::COMMON_TER; + } else if(s < Collation::COMMON_WEIGHT16) { + if(sec == CollationFastLatin::COMMON_SEC) { + sec = CollationFastLatin::MIN_SEC_BEFORE; + } else if(sec < CollationFastLatin::MAX_SEC_BEFORE) { + sec += CollationFastLatin::SEC_INC; + } else { + miniCEs[i] = CollationFastLatin::BAIL_OUT; + continue; + } + } else if(s == Collation::COMMON_WEIGHT16) { + sec = CollationFastLatin::COMMON_SEC; + } else { + if(sec < CollationFastLatin::MIN_SEC_AFTER) { + sec = CollationFastLatin::MIN_SEC_AFTER; + } else if(sec < CollationFastLatin::MAX_SEC_AFTER) { + sec += CollationFastLatin::SEC_INC; + } else { + miniCEs[i] = CollationFastLatin::BAIL_OUT; + continue; + } + } + prevSecondary = s; + ter = CollationFastLatin::COMMON_TER; + } + U_ASSERT((lower32 & Collation::CASE_MASK) == 0); // blanked out in uniqueCEs + uint32_t t = lower32 & Collation::ONLY_TERTIARY_MASK; + if(t > Collation::COMMON_WEIGHT16) { + if(ter < CollationFastLatin::MAX_TER_AFTER) { + ++ter; + } else { + miniCEs[i] = CollationFastLatin::BAIL_OUT; + continue; + } + } + if(CollationFastLatin::MIN_LONG <= pri && pri <= CollationFastLatin::MAX_LONG) { + U_ASSERT(sec == CollationFastLatin::COMMON_SEC); + miniCEs[i] = (uint16_t)(pri | ter); + } else { + miniCEs[i] = (uint16_t)(pri | sec | ter); + } + } +#if DEBUG_COLLATION_FAST_LATIN_BUILDER + printf("last mini primary: %04x\n", pri); +#endif +#if DEBUG_COLLATION_FAST_LATIN_BUILDER >= 2 + for(int32_t i = 0; i < uniqueCEs.size(); ++i) { + int64_t ce = uniqueCEs.elementAti(i); + printf("unique CE 0x%016lx -> 0x%04x\n", ce, miniCEs[i]); + } +#endif + return U_SUCCESS(errorCode); +} + +UBool +CollationFastLatinBuilder::encodeCharCEs(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return false; } + int32_t miniCEsStart = result.length(); + for(int32_t i = 0; i < CollationFastLatin::NUM_FAST_CHARS; ++i) { + result.append((char16_t)0); // initialize to completely ignorable + } + int32_t indexBase = result.length(); + for(int32_t i = 0; i < CollationFastLatin::NUM_FAST_CHARS; ++i) { + int64_t ce = charCEs[i][0]; + if(isContractionCharCE(ce)) { continue; } // defer contraction + uint32_t miniCE = encodeTwoCEs(ce, charCEs[i][1]); + if(miniCE > 0xffff) { + // Note: There is a chance that this new expansion is the same as a previous one, + // and if so, then we could reuse the other expansion. + // However, that seems unlikely. + int32_t expansionIndex = result.length() - indexBase; + if(expansionIndex > (int32_t)CollationFastLatin::INDEX_MASK) { + miniCE = CollationFastLatin::BAIL_OUT; + } else { + result.append((char16_t)(miniCE >> 16)).append((char16_t)miniCE); + miniCE = CollationFastLatin::EXPANSION | expansionIndex; + } + } + result.setCharAt(miniCEsStart + i, (char16_t)miniCE); + } + return U_SUCCESS(errorCode); +} + +UBool +CollationFastLatinBuilder::encodeContractions(UErrorCode &errorCode) { + // We encode all contraction lists so that the first word of a list + // terminates the previous list, and we only need one additional terminator at the end. + if(U_FAILURE(errorCode)) { return false; } + int32_t indexBase = headerLength + CollationFastLatin::NUM_FAST_CHARS; + int32_t firstContractionIndex = result.length(); + for(int32_t i = 0; i < CollationFastLatin::NUM_FAST_CHARS; ++i) { + int64_t ce = charCEs[i][0]; + if(!isContractionCharCE(ce)) { continue; } + int32_t contractionIndex = result.length() - indexBase; + if(contractionIndex > (int32_t)CollationFastLatin::INDEX_MASK) { + result.setCharAt(headerLength + i, CollationFastLatin::BAIL_OUT); + continue; + } + UBool firstTriple = true; + for(int32_t index = (int32_t)ce & 0x7fffffff;; index += 3) { + int32_t x = static_cast(contractionCEs.elementAti(index)); + if((uint32_t)x == CollationFastLatin::CONTR_CHAR_MASK && !firstTriple) { break; } + int64_t cce0 = contractionCEs.elementAti(index + 1); + int64_t cce1 = contractionCEs.elementAti(index + 2); + uint32_t miniCE = encodeTwoCEs(cce0, cce1); + if(miniCE == CollationFastLatin::BAIL_OUT) { + result.append((char16_t)(x | (1 << CollationFastLatin::CONTR_LENGTH_SHIFT))); + } else if(miniCE <= 0xffff) { + result.append((char16_t)(x | (2 << CollationFastLatin::CONTR_LENGTH_SHIFT))); + result.append((char16_t)miniCE); + } else { + result.append((char16_t)(x | (3 << CollationFastLatin::CONTR_LENGTH_SHIFT))); + result.append((char16_t)(miniCE >> 16)).append((char16_t)miniCE); + } + firstTriple = false; + } + // Note: There is a chance that this new contraction list is the same as a previous one, + // and if so, then we could truncate the result and reuse the other list. + // However, that seems unlikely. + result.setCharAt(headerLength + i, + (char16_t)(CollationFastLatin::CONTRACTION | contractionIndex)); + } + if(result.length() > firstContractionIndex) { + // Terminate the last contraction list. + result.append((char16_t)CollationFastLatin::CONTR_CHAR_MASK); + } + if(result.isBogus()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return false; + } +#if DEBUG_COLLATION_FAST_LATIN_BUILDER + printf("** fast Latin %d * 2 = %d bytes\n", result.length(), result.length() * 2); + puts(" header & below-digit groups map"); + int32_t i = 0; + for(; i < headerLength; ++i) { + printf(" %04x", result[i]); + } + printf("\n char mini CEs"); + U_ASSERT(CollationFastLatin::NUM_FAST_CHARS % 16 == 0); + for(; i < indexBase; i += 16) { + UChar32 c = i - headerLength; + if(c >= CollationFastLatin::LATIN_LIMIT) { + c = CollationFastLatin::PUNCT_START + c - CollationFastLatin::LATIN_LIMIT; + } + printf("\n %04x:", c); + for(int32_t j = 0; j < 16; ++j) { + printf(" %04x", result[i + j]); + } + } + printf("\n expansions & contractions"); + for(; i < result.length(); ++i) { + if((i - indexBase) % 16 == 0) { puts(""); } + printf(" %04x", result[i]); + } + puts(""); +#endif + return true; +} + +uint32_t +CollationFastLatinBuilder::encodeTwoCEs(int64_t first, int64_t second) const { + if(first == 0) { + return 0; // completely ignorable + } + if(first == Collation::NO_CE) { + return CollationFastLatin::BAIL_OUT; + } + U_ASSERT((uint32_t)(first >> 32) != Collation::NO_CE_PRIMARY); + + uint32_t miniCE = getMiniCE(first); + if(miniCE == CollationFastLatin::BAIL_OUT) { return miniCE; } + if(miniCE >= CollationFastLatin::MIN_SHORT) { + // Extract & copy the case bits. + // Shift them from normal CE bits 15..14 to mini CE bits 4..3. + uint32_t c = (((uint32_t)first & Collation::CASE_MASK) >> (14 - 3)); + // Only in mini CEs: Ignorable case bits = 0, lowercase = 1. + c += CollationFastLatin::LOWER_CASE; + miniCE |= c; + } + if(second == 0) { return miniCE; } + + uint32_t miniCE1 = getMiniCE(second); + if(miniCE1 == CollationFastLatin::BAIL_OUT) { return miniCE1; } + + uint32_t case1 = (uint32_t)second & Collation::CASE_MASK; + if(miniCE >= CollationFastLatin::MIN_SHORT && + (miniCE & CollationFastLatin::SECONDARY_MASK) == CollationFastLatin::COMMON_SEC) { + // Try to combine the two mini CEs into one. + uint32_t sec1 = miniCE1 & CollationFastLatin::SECONDARY_MASK; + uint32_t ter1 = miniCE1 & CollationFastLatin::TERTIARY_MASK; + if(sec1 >= CollationFastLatin::MIN_SEC_HIGH && case1 == 0 && + ter1 == CollationFastLatin::COMMON_TER) { + // sec1>=sec_high implies pri1==0. + return (miniCE & ~CollationFastLatin::SECONDARY_MASK) | sec1; + } + } + + if(miniCE1 <= CollationFastLatin::SECONDARY_MASK || CollationFastLatin::MIN_SHORT <= miniCE1) { + // Secondary CE, or a CE with a short primary, copy the case bits. + case1 = (case1 >> (14 - 3)) + CollationFastLatin::LOWER_CASE; + miniCE1 |= case1; + } + return (miniCE << 16) | miniCE1; +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationfastlatinbuilder.h b/deps/icu-small/source/i18n/collationfastlatinbuilder.h index 8b63b86815fdc6..d6c394bbcb1419 100644 --- a/deps/icu-small/source/i18n/collationfastlatinbuilder.h +++ b/deps/icu-small/source/i18n/collationfastlatinbuilder.h @@ -1,100 +1,100 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2016, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationfastlatinbuilder.h -* -* created on: 2013aug09 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONFASTLATINBUILDER_H__ -#define __COLLATIONFASTLATINBUILDER_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/ucol.h" -#include "unicode/unistr.h" -#include "unicode/uobject.h" -#include "collation.h" -#include "collationfastlatin.h" -#include "uvectr64.h" - -U_NAMESPACE_BEGIN - -struct CollationData; - -class U_I18N_API CollationFastLatinBuilder : public UObject { -public: - CollationFastLatinBuilder(UErrorCode &errorCode); - ~CollationFastLatinBuilder(); - - UBool forData(const CollationData &data, UErrorCode &errorCode); - - const uint16_t *getTable() const { - return reinterpret_cast(result.getBuffer()); - } - int32_t lengthOfTable() const { return result.length(); } - -private: - // space, punct, symbol, currency (not digit) - enum { NUM_SPECIAL_GROUPS = UCOL_REORDER_CODE_CURRENCY - UCOL_REORDER_CODE_FIRST + 1 }; - - UBool loadGroups(const CollationData &data, UErrorCode &errorCode); - UBool inSameGroup(uint32_t p, uint32_t q) const; - - void resetCEs(); - void getCEs(const CollationData &data, UErrorCode &errorCode); - UBool getCEsFromCE32(const CollationData &data, UChar32 c, uint32_t ce32, - UErrorCode &errorCode); - UBool getCEsFromContractionCE32(const CollationData &data, uint32_t ce32, - UErrorCode &errorCode); - void addContractionEntry(int32_t x, int64_t cce0, int64_t cce1, UErrorCode &errorCode); - void addUniqueCE(int64_t ce, UErrorCode &errorCode); - uint32_t getMiniCE(int64_t ce) const; - UBool encodeUniqueCEs(UErrorCode &errorCode); - UBool encodeCharCEs(UErrorCode &errorCode); - UBool encodeContractions(UErrorCode &errorCode); - uint32_t encodeTwoCEs(int64_t first, int64_t second) const; - - static UBool isContractionCharCE(int64_t ce) { - return (uint32_t)(ce >> 32) == Collation::NO_CE_PRIMARY && ce != Collation::NO_CE; - } - - static const uint32_t CONTRACTION_FLAG = 0x80000000; - - // temporary "buffer" - int64_t ce0, ce1; - - int64_t charCEs[CollationFastLatin::NUM_FAST_CHARS][2]; - - UVector64 contractionCEs; - UVector64 uniqueCEs; - - /** One 16-bit mini CE per unique CE. */ - uint16_t *miniCEs; - - // These are constant for a given root collator. - uint32_t lastSpecialPrimaries[NUM_SPECIAL_GROUPS]; - uint32_t firstDigitPrimary; - uint32_t firstLatinPrimary; - uint32_t lastLatinPrimary; - // This determines the first normal primary weight which is mapped to - // a short mini primary. It must be >=firstDigitPrimary. - uint32_t firstShortPrimary; - - UBool shortPrimaryOverflow; - - UnicodeString result; - int32_t headerLength; -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONFASTLATINBUILDER_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2016, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationfastlatinbuilder.h +* +* created on: 2013aug09 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONFASTLATINBUILDER_H__ +#define __COLLATIONFASTLATINBUILDER_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/ucol.h" +#include "unicode/unistr.h" +#include "unicode/uobject.h" +#include "collation.h" +#include "collationfastlatin.h" +#include "uvectr64.h" + +U_NAMESPACE_BEGIN + +struct CollationData; + +class U_I18N_API CollationFastLatinBuilder : public UObject { +public: + CollationFastLatinBuilder(UErrorCode &errorCode); + ~CollationFastLatinBuilder(); + + UBool forData(const CollationData &data, UErrorCode &errorCode); + + const uint16_t *getTable() const { + return reinterpret_cast(result.getBuffer()); + } + int32_t lengthOfTable() const { return result.length(); } + +private: + // space, punct, symbol, currency (not digit) + enum { NUM_SPECIAL_GROUPS = UCOL_REORDER_CODE_CURRENCY - UCOL_REORDER_CODE_FIRST + 1 }; + + UBool loadGroups(const CollationData &data, UErrorCode &errorCode); + UBool inSameGroup(uint32_t p, uint32_t q) const; + + void resetCEs(); + void getCEs(const CollationData &data, UErrorCode &errorCode); + UBool getCEsFromCE32(const CollationData &data, UChar32 c, uint32_t ce32, + UErrorCode &errorCode); + UBool getCEsFromContractionCE32(const CollationData &data, uint32_t ce32, + UErrorCode &errorCode); + void addContractionEntry(int32_t x, int64_t cce0, int64_t cce1, UErrorCode &errorCode); + void addUniqueCE(int64_t ce, UErrorCode &errorCode); + uint32_t getMiniCE(int64_t ce) const; + UBool encodeUniqueCEs(UErrorCode &errorCode); + UBool encodeCharCEs(UErrorCode &errorCode); + UBool encodeContractions(UErrorCode &errorCode); + uint32_t encodeTwoCEs(int64_t first, int64_t second) const; + + static UBool isContractionCharCE(int64_t ce) { + return (uint32_t)(ce >> 32) == Collation::NO_CE_PRIMARY && ce != Collation::NO_CE; + } + + static const uint32_t CONTRACTION_FLAG = 0x80000000; + + // temporary "buffer" + int64_t ce0, ce1; + + int64_t charCEs[CollationFastLatin::NUM_FAST_CHARS][2]; + + UVector64 contractionCEs; + UVector64 uniqueCEs; + + /** One 16-bit mini CE per unique CE. */ + uint16_t *miniCEs; + + // These are constant for a given root collator. + uint32_t lastSpecialPrimaries[NUM_SPECIAL_GROUPS]; + uint32_t firstDigitPrimary; + uint32_t firstLatinPrimary; + uint32_t lastLatinPrimary; + // This determines the first normal primary weight which is mapped to + // a short mini primary. It must be >=firstDigitPrimary. + uint32_t firstShortPrimary; + + UBool shortPrimaryOverflow; + + UnicodeString result; + int32_t headerLength; +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONFASTLATINBUILDER_H__ diff --git a/deps/icu-small/source/i18n/collationfcd.cpp b/deps/icu-small/source/i18n/collationfcd.cpp index e1f1d0330c480e..f94868790b49a5 100644 --- a/deps/icu-small/source/i18n/collationfcd.cpp +++ b/deps/icu-small/source/i18n/collationfcd.cpp @@ -1,301 +1,301 @@ -// Copyright (C) 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// Copyright (C) 1999-2016, International Business Machines -// Corporation and others. All Rights Reserved. -// -// file name: collationfcd.cpp -// -// machine-generated by: icu/tools/unicode/c/genuca/genuca.cpp - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "collationfcd.h" - -U_NAMESPACE_BEGIN - -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,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,0x14,0,0x15,0x16,0,0x17,0x18,0,0,0x17,0x19,0x1a, -0,0x17,0x19,0,0,0x17,0x19,0,0,0x17,0x19,0,0,0,0x19,0, -0,0x17,0x1b,0,0,0x17,0x19,0,0,0x1c,0x19,0,0,0,0x1d,0, -0,0x1e,0x1f,0,0,0x1e,0x1f,0,0x20,0x21,0,0x22,0x23,0,0x24,0, -0,0x25,0,0,0x19,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0x26,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0x27,0x28,0,0,0,0,0x29,0, -0,0,0,0,0,0x2a,0,0,0,0x13,0,0,0,0,0,0, -0x2b,0,0,0x2c,0,0x2d,0x2e,0,0,0x28,0x2f,0x30,0,0x31,0,0x32, -0,0x33,0,0,0,0,0x34,0x35,0,0,0,0,0,0,1,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0x36,0x37,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0x39,0,0,0,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0x3a,0,0,0x3b,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0x3c,0x3d,0,0,0x3e,0,0,0,0,0,0,0,0, -0x24,0x3f,0,0,0,0,0x2f,0x40,0,0x41,0x42,0,0,0x42,0x43,0, -0,0,0,0,0,0x44,0x45,0x46,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,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x47,0x48,0,0,0,0,0,0,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, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0x1a,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[73]={ -0,0xffffffff,0xffff7fff,0xffff,0xf8,0xfffe0000,0xbfffffff,0xb6,0x7ff0000,0xfffff800,0x10000,0x9fc00000,0x3d9f,0x20000,0xffff0000,0x7ff, -0x200ff800,0xfbc00000,0x3eef,0xe000000,0xff000000,0xfffffc00,0xfffffffb,0x10000000,0x1e2000,0x2000,0x40000000,0x602000,0x18000000,0x400,0x7000000,0xf00, -0x3000000,0x2a00000,0x3c3e0000,0xdf,0x40,0x6800000,0xe0000000,0x300000,0x100000,0x20040000,0x200,0x1800000,0x9fe00001,0xbfff0000,0x7fff,0x10, -0xff800,0xc00,0xc0040,0x800000,0xfff70000,0x31021fd,0x1fff0000,0x1ffe2,0x38000,0x80000000,0xfc00,0x6000000,0x3ff08000,0xc0000000,0x30000,0x1000, -0x3ffff,0x3800,0x80000,1,0xc19d0000,2,0x400000,0xc0000fd,0x7108000 -}; - -const uint8_t CollationFCD::tcccIndex[2048]={ -0,0,0,0,0,0,2,3,4,5,6,7,0,8,9,0xa, -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,0x29,0,0x2a,0x2b,0,0x2c,0x2d,0,0,0x2e,0x2f,0x30, -0,0x31,0x32,0,0,0x2e,0x33,0,0,0x2e,0x34,0,0,0,0x33,0, -0,0x2e,0x35,0,0,0x2e,0x33,0,0,0x36,0x33,0,0,0,0x37,0, -0,0x38,0x39,0,0,0x38,0x39,0,0x3a,0x3b,0,0x3c,0x3d,0,0x3e,0, -0,0x3f,0,0,0x33,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0x40,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0x41,0x42,0,0,0,0,0x43,0, -0,0,0,0,0,0x44,0,0,0,0x28,0,0,0,0,0,0, -0x45,0,0,0x46,0,0x47,0x48,0,0,0x42,0x49,0x4a,0,0x4b,0,0x4c, -0,0x4d,0,0,0,0,0x4e,0x4f,0,0,0,0,0,0,1,1, -1,1,1,1,0x50,1,1,0x51,0x52,1,0x53,0x54,1,0x55,0x56,0x57, -0,0,0,0,0,0,0x58,0x59,0,0x5a,0,0,0x5b,0x5c,0x5d,0, -0x5e,0x5f,0x60,0x61,0x62,0x63,0,0x64,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0x2e,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0x65,0,0,0,0x66,0,0,0,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0x67,0x68,0x69,0x6a,0x68,0x69,0x6b,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0x6c,0x6d,0,0,0x6e,0,0,0,0,0,0,0,0, -0x3e,0x6f,0,0,0,0,0x49,0x70,0,0x71,0x72,0,0,0x72,0x73,0, -0,0,0,0,0,0x74,0x75,0x76,0,0,0,0,0,0,0,0x33, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x77,0x78,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0x40,0x79,0x7a,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[123]={ -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,0x200ff800,0xfbc00000,0x3eef,0xe000000,0xff000000,0xfffffc00,0xfffffffb,0x10120200,0xff1e2000,0x10000000,0xb0002000, -0x40000000,0x10480000,0x4e002000,0x2000,0x30002000,0x602100,0x18000000,0x24000400,0x7000000,0xf00,0x3000000,0x2a00000,0x3d7e0000,0xdf,0x40,0x6800000, -0xe0000000,0x300000,0x100000,0x20040000,0x200,0x1800000,0x9fe00001,0xbfff0000,0x7fff,0x10,0xff800,0xc00,0xc0040,0x800000,0xfff70000,0x31021fd, -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,0x1000, -0x3ffff,0x3800,0x80000,1,0xc19d0000,2,0x400000,0xc0000fd,0x7108000,0x5f7ffc00,0x7fdb -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// Copyright (C) 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// Copyright (C) 1999-2016, International Business Machines +// Corporation and others. All Rights Reserved. +// +// file name: collationfcd.cpp +// +// machine-generated by: icu/tools/unicode/c/genuca/genuca.cpp + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "collationfcd.h" + +U_NAMESPACE_BEGIN + +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,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,0x14,0,0x15,0x16,0,0x17,0x18,0,0,0x17,0x19,0x1a, +0,0x17,0x19,0,0,0x17,0x19,0,0,0x17,0x19,0,0,0,0x19,0, +0,0x17,0x1b,0,0,0x17,0x19,0,0,0x1c,0x19,0,0,0,0x1d,0, +0,0x1e,0x1f,0,0,0x1e,0x1f,0,0x20,0x21,0,0x22,0x23,0,0x24,0, +0,0x25,0,0,0x19,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0x26,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0x27,0x28,0,0,0,0,0x29,0, +0,0,0,0,0,0x2a,0,0,0,0x13,0,0,0,0,0,0, +0x2b,0,0,0x2c,0,0x2d,0x2e,0,0,0x28,0x2f,0x30,0,0x31,0,0x32, +0,0x33,0,0,0,0,0x34,0x35,0,0,0,0,0,0,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0x36,0x37,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0x39,0,0,0,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0x3a,0,0,0x3b,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0x3c,0x3d,0,0,0x3e,0,0,0,0,0,0,0,0, +0x24,0x3f,0,0,0,0,0x2f,0x40,0,0x41,0x42,0,0,0x42,0x43,0, +0,0,0,0,0,0x44,0x45,0x46,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,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x47,0x48,0,0,0,0,0,0,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, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0x1a,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[73]={ +0,0xffffffff,0xffff7fff,0xffff,0xf8,0xfffe0000,0xbfffffff,0xb6,0x7ff0000,0xfffff800,0x10000,0x9fc00000,0x3d9f,0x20000,0xffff0000,0x7ff, +0x200ff800,0xfbc00000,0x3eef,0xe000000,0xff000000,0xfffffc00,0xfffffffb,0x10000000,0x1e2000,0x2000,0x40000000,0x602000,0x18000000,0x400,0x7000000,0xf00, +0x3000000,0x2a00000,0x3c3e0000,0xdf,0x40,0x6800000,0xe0000000,0x300000,0x100000,0x20040000,0x200,0x1800000,0x9fe00001,0xbfff0000,0x7fff,0x10, +0xff800,0xc00,0xc0040,0x800000,0xfff70000,0x31021fd,0x1fff0000,0x1ffe2,0x38000,0x80000000,0xfc00,0x6000000,0x3ff08000,0xc0000000,0x30000,0x1000, +0x3ffff,0x3800,0x80000,1,0xc19d0000,2,0x400000,0xc0000fd,0x7108000 +}; + +const uint8_t CollationFCD::tcccIndex[2048]={ +0,0,0,0,0,0,2,3,4,5,6,7,0,8,9,0xa, +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,0x29,0,0x2a,0x2b,0,0x2c,0x2d,0,0,0x2e,0x2f,0x30, +0,0x31,0x32,0,0,0x2e,0x33,0,0,0x2e,0x34,0,0,0,0x33,0, +0,0x2e,0x35,0,0,0x2e,0x33,0,0,0x36,0x33,0,0,0,0x37,0, +0,0x38,0x39,0,0,0x38,0x39,0,0x3a,0x3b,0,0x3c,0x3d,0,0x3e,0, +0,0x3f,0,0,0x33,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0x40,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0x41,0x42,0,0,0,0,0x43,0, +0,0,0,0,0,0x44,0,0,0,0x28,0,0,0,0,0,0, +0x45,0,0,0x46,0,0x47,0x48,0,0,0x42,0x49,0x4a,0,0x4b,0,0x4c, +0,0x4d,0,0,0,0,0x4e,0x4f,0,0,0,0,0,0,1,1, +1,1,1,1,0x50,1,1,0x51,0x52,1,0x53,0x54,1,0x55,0x56,0x57, +0,0,0,0,0,0,0x58,0x59,0,0x5a,0,0,0x5b,0x5c,0x5d,0, +0x5e,0x5f,0x60,0x61,0x62,0x63,0,0x64,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0x2e,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0x65,0,0,0,0x66,0,0,0,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0x67,0x68,0x69,0x6a,0x68,0x69,0x6b,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0x6c,0x6d,0,0,0x6e,0,0,0,0,0,0,0,0, +0x3e,0x6f,0,0,0,0,0x49,0x70,0,0x71,0x72,0,0,0x72,0x73,0, +0,0,0,0,0,0x74,0x75,0x76,0,0,0,0,0,0,0,0x33, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x77,0x78,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0x40,0x79,0x7a,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[123]={ +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,0x200ff800,0xfbc00000,0x3eef,0xe000000,0xff000000,0xfffffc00,0xfffffffb,0x10120200,0xff1e2000,0x10000000,0xb0002000, +0x40000000,0x10480000,0x4e002000,0x2000,0x30002000,0x602100,0x18000000,0x24000400,0x7000000,0xf00,0x3000000,0x2a00000,0x3d7e0000,0xdf,0x40,0x6800000, +0xe0000000,0x300000,0x100000,0x20040000,0x200,0x1800000,0x9fe00001,0xbfff0000,0x7fff,0x10,0xff800,0xc00,0xc0040,0x800000,0xfff70000,0x31021fd, +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,0x1000, +0x3ffff,0x3800,0x80000,1,0xc19d0000,2,0x400000,0xc0000fd,0x7108000,0x5f7ffc00,0x7fdb +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationfcd.h b/deps/icu-small/source/i18n/collationfcd.h index 9620452b97f01c..90ee34eb5749f3 100644 --- a/deps/icu-small/source/i18n/collationfcd.h +++ b/deps/icu-small/source/i18n/collationfcd.h @@ -1,137 +1,137 @@ -// © 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. -******************************************************************************* -* collationfcd.h -* -* created on: 2012aug18 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONFCD_H__ -#define __COLLATIONFCD_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/utf16.h" - -U_NAMESPACE_BEGIN - -/** - * Data and functions for the FCD check fast path. - * - * The fast path looks at a pair of 16-bit code units and checks - * whether there is an FCD boundary between them; - * there is if the first unit has a trailing ccc=0 (!hasTccc(first)) - * or the second unit has a leading ccc=0 (!hasLccc(second)), - * or both. - * When the fast path finds a possible non-boundary, - * then the FCD check slow path looks at the actual sequence of FCD values. - * - * This is a pure optimization. - * The fast path must at least find all possible non-boundaries. - * If the fast path is too pessimistic, it costs performance. - * - * For a pair of BMP characters, the fast path tests are precise (1 bit per character). - * - * For a supplementary code point, the two units are its lead and trail surrogates. - * We set hasTccc(lead)=true if any of its 1024 associated supplementary code points - * has lccc!=0 or tccc!=0. - * We set hasLccc(trail)=true for all trail surrogates. - * As a result, we leave the fast path if the lead surrogate might start a - * supplementary code point that is not FCD-inert. - * (So the fast path need not detect that there is a surrogate pair, - * nor look ahead to the next full code point.) - * - * hasLccc(lead)=true if any of its 1024 associated supplementary code points - * has lccc!=0, for fast boundary checking between BMP & supplementary. - * - * hasTccc(trail)=false: - * It should only be tested for unpaired trail surrogates which are FCD-inert. - */ -class U_I18N_API CollationFCD { -public: - static inline UBool hasLccc(UChar32 c) { - // assert c <= 0xffff - // c can be negative, e.g., U_SENTINEL from UCharIterator; - // that is handled in the first test. - int32_t i; - return - // U+0300 is the first character with lccc!=0. - c >= 0x300 && - (i = lcccIndex[c >> 5]) != 0 && - (lcccBits[i] & ((uint32_t)1 << (c & 0x1f))) != 0; - } - - static inline UBool hasTccc(UChar32 c) { - // assert c <= 0xffff - // c can be negative, e.g., U_SENTINEL from UCharIterator; - // that is handled in the first test. - int32_t i; - return - // U+00C0 is the first character with tccc!=0. - c >= 0xc0 && - (i = tcccIndex[c >> 5]) != 0 && - (tcccBits[i] & ((uint32_t)1 << (c & 0x1f))) != 0; - } - - static inline UBool mayHaveLccc(UChar32 c) { - // Handles all of Unicode 0..10FFFF. - // c can be negative, e.g., U_SENTINEL. - // U+0300 is the first character with lccc!=0. - if(c < 0x300) { return false; } - if(c > 0xffff) { c = U16_LEAD(c); } - int32_t i; - return - (i = lcccIndex[c >> 5]) != 0 && - (lcccBits[i] & ((uint32_t)1 << (c & 0x1f))) != 0; - } - - /** - * Tibetan composite vowel signs (U+0F73, U+0F75, U+0F81) - * must be decomposed before reaching the core collation code, - * or else some sequences including them, even ones passing the FCD check, - * do not yield canonically equivalent results. - * - * This is a fast and imprecise test. - * - * @param c a code point - * @return true if c is U+0F73, U+0F75 or U+0F81 or one of several other Tibetan characters - */ - static inline UBool maybeTibetanCompositeVowel(UChar32 c) { - return (c & 0x1fff01) == 0xf01; - } - - /** - * Tibetan composite vowel signs (U+0F73, U+0F75, U+0F81) - * must be decomposed before reaching the core collation code, - * or else some sequences including them, even ones passing the FCD check, - * do not yield canonically equivalent results. - * - * They have distinct lccc/tccc combinations: 129/130 or 129/132. - * - * @param fcd16 the FCD value (lccc/tccc combination) of a code point - * @return true if fcd16 is from U+0F73, U+0F75 or U+0F81 - */ - static inline UBool isFCD16OfTibetanCompositeVowel(uint16_t fcd16) { - return fcd16 == 0x8182 || fcd16 == 0x8184; - } - -private: - CollationFCD() = delete; // No instantiation. - - static const uint8_t lcccIndex[2048]; - static const uint8_t tcccIndex[2048]; - static const uint32_t lcccBits[]; - static const uint32_t tcccBits[]; -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONFCD_H__ +// © 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. +******************************************************************************* +* collationfcd.h +* +* created on: 2012aug18 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONFCD_H__ +#define __COLLATIONFCD_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/utf16.h" + +U_NAMESPACE_BEGIN + +/** + * Data and functions for the FCD check fast path. + * + * The fast path looks at a pair of 16-bit code units and checks + * whether there is an FCD boundary between them; + * there is if the first unit has a trailing ccc=0 (!hasTccc(first)) + * or the second unit has a leading ccc=0 (!hasLccc(second)), + * or both. + * When the fast path finds a possible non-boundary, + * then the FCD check slow path looks at the actual sequence of FCD values. + * + * This is a pure optimization. + * The fast path must at least find all possible non-boundaries. + * If the fast path is too pessimistic, it costs performance. + * + * For a pair of BMP characters, the fast path tests are precise (1 bit per character). + * + * For a supplementary code point, the two units are its lead and trail surrogates. + * We set hasTccc(lead)=true if any of its 1024 associated supplementary code points + * has lccc!=0 or tccc!=0. + * We set hasLccc(trail)=true for all trail surrogates. + * As a result, we leave the fast path if the lead surrogate might start a + * supplementary code point that is not FCD-inert. + * (So the fast path need not detect that there is a surrogate pair, + * nor look ahead to the next full code point.) + * + * hasLccc(lead)=true if any of its 1024 associated supplementary code points + * has lccc!=0, for fast boundary checking between BMP & supplementary. + * + * hasTccc(trail)=false: + * It should only be tested for unpaired trail surrogates which are FCD-inert. + */ +class U_I18N_API CollationFCD { +public: + static inline UBool hasLccc(UChar32 c) { + // assert c <= 0xffff + // c can be negative, e.g., U_SENTINEL from UCharIterator; + // that is handled in the first test. + int32_t i; + return + // U+0300 is the first character with lccc!=0. + c >= 0x300 && + (i = lcccIndex[c >> 5]) != 0 && + (lcccBits[i] & ((uint32_t)1 << (c & 0x1f))) != 0; + } + + static inline UBool hasTccc(UChar32 c) { + // assert c <= 0xffff + // c can be negative, e.g., U_SENTINEL from UCharIterator; + // that is handled in the first test. + int32_t i; + return + // U+00C0 is the first character with tccc!=0. + c >= 0xc0 && + (i = tcccIndex[c >> 5]) != 0 && + (tcccBits[i] & ((uint32_t)1 << (c & 0x1f))) != 0; + } + + static inline UBool mayHaveLccc(UChar32 c) { + // Handles all of Unicode 0..10FFFF. + // c can be negative, e.g., U_SENTINEL. + // U+0300 is the first character with lccc!=0. + if(c < 0x300) { return false; } + if(c > 0xffff) { c = U16_LEAD(c); } + int32_t i; + return + (i = lcccIndex[c >> 5]) != 0 && + (lcccBits[i] & ((uint32_t)1 << (c & 0x1f))) != 0; + } + + /** + * Tibetan composite vowel signs (U+0F73, U+0F75, U+0F81) + * must be decomposed before reaching the core collation code, + * or else some sequences including them, even ones passing the FCD check, + * do not yield canonically equivalent results. + * + * This is a fast and imprecise test. + * + * @param c a code point + * @return true if c is U+0F73, U+0F75 or U+0F81 or one of several other Tibetan characters + */ + static inline UBool maybeTibetanCompositeVowel(UChar32 c) { + return (c & 0x1fff01) == 0xf01; + } + + /** + * Tibetan composite vowel signs (U+0F73, U+0F75, U+0F81) + * must be decomposed before reaching the core collation code, + * or else some sequences including them, even ones passing the FCD check, + * do not yield canonically equivalent results. + * + * They have distinct lccc/tccc combinations: 129/130 or 129/132. + * + * @param fcd16 the FCD value (lccc/tccc combination) of a code point + * @return true if fcd16 is from U+0F73, U+0F75 or U+0F81 + */ + static inline UBool isFCD16OfTibetanCompositeVowel(uint16_t fcd16) { + return fcd16 == 0x8182 || fcd16 == 0x8184; + } + +private: + CollationFCD() = delete; // No instantiation. + + static const uint8_t lcccIndex[2048]; + static const uint8_t tcccIndex[2048]; + static const uint32_t lcccBits[]; + static const uint32_t tcccBits[]; +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONFCD_H__ diff --git a/deps/icu-small/source/i18n/collationiterator.cpp b/deps/icu-small/source/i18n/collationiterator.cpp index a47b3d86bea99a..d7d6af5328fd1f 100644 --- a/deps/icu-small/source/i18n/collationiterator.cpp +++ b/deps/icu-small/source/i18n/collationiterator.cpp @@ -1,955 +1,955 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationiterator.cpp -* -* created on: 2010oct27 -* created by: Markus W. Scherer -*/ - -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/ucharstrie.h" -#include "unicode/ustringtrie.h" -#include "charstr.h" -#include "cmemory.h" -#include "collation.h" -#include "collationdata.h" -#include "collationfcd.h" -#include "collationiterator.h" -#include "normalizer2impl.h" -#include "uassert.h" -#include "uvectr32.h" - -U_NAMESPACE_BEGIN - -CollationIterator::CEBuffer::~CEBuffer() {} - -UBool -CollationIterator::CEBuffer::ensureAppendCapacity(int32_t appCap, UErrorCode &errorCode) { - int32_t capacity = buffer.getCapacity(); - if((length + appCap) <= capacity) { return true; } - if(U_FAILURE(errorCode)) { return false; } - do { - if(capacity < 1000) { - capacity *= 4; - } else { - capacity *= 2; - } - } while(capacity < (length + appCap)); - int64_t *p = buffer.resize(capacity, length); - if(p == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return false; - } - return true; -} - -// State of combining marks skipped in discontiguous contraction. -// We create a state object on first use and keep it around deactivated between uses. -class SkippedState : public UMemory { -public: - // Born active but empty. - SkippedState() : pos(0), skipLengthAtMatch(0) {} - void clear() { - oldBuffer.remove(); - pos = 0; - // The newBuffer is reset by setFirstSkipped(). - } - - UBool isEmpty() const { return oldBuffer.isEmpty(); } - - UBool hasNext() const { return pos < oldBuffer.length(); } - - // Requires hasNext(). - UChar32 next() { - UChar32 c = oldBuffer.char32At(pos); - pos += U16_LENGTH(c); - return c; - } - - // Accounts for one more input code point read beyond the end of the marks buffer. - void incBeyond() { - U_ASSERT(!hasNext()); - ++pos; - } - - // Goes backward through the skipped-marks buffer. - // Returns the number of code points read beyond the skipped marks - // that need to be backtracked through normal input. - int32_t backwardNumCodePoints(int32_t n) { - int32_t length = oldBuffer.length(); - int32_t beyond = pos - length; - if(beyond > 0) { - if(beyond >= n) { - // Not back far enough to re-enter the oldBuffer. - pos -= n; - return n; - } else { - // Back out all beyond-oldBuffer code points and re-enter the buffer. - pos = oldBuffer.moveIndex32(length, beyond - n); - return beyond; - } - } else { - // Go backwards from inside the oldBuffer. - pos = oldBuffer.moveIndex32(pos, -n); - return 0; - } - } - - void setFirstSkipped(UChar32 c) { - skipLengthAtMatch = 0; - newBuffer.setTo(c); - } - - void skip(UChar32 c) { - newBuffer.append(c); - } - - void recordMatch() { skipLengthAtMatch = newBuffer.length(); } - - // Replaces the characters we consumed with the newly skipped ones. - void replaceMatch() { - // Note: UnicodeString.replace() pins pos to at most length(). - oldBuffer.replace(0, pos, newBuffer, 0, skipLengthAtMatch); - pos = 0; - } - - void saveTrieState(const UCharsTrie &trie) { trie.saveState(state); } - void resetToTrieState(UCharsTrie &trie) const { trie.resetToState(state); } - -private: - // Combining marks skipped in previous discontiguous-contraction matching. - // After that discontiguous contraction was completed, we start reading them from here. - UnicodeString oldBuffer; - // Combining marks newly skipped in current discontiguous-contraction matching. - // These might have been read from the normal text or from the oldBuffer. - UnicodeString newBuffer; - // Reading index in oldBuffer, - // or counter for how many code points have been read beyond oldBuffer (pos-oldBuffer.length()). - int32_t pos; - // newBuffer.length() at the time of the last matching character. - // When a partial match fails, we back out skipped and partial-matching input characters. - int32_t skipLengthAtMatch; - // We save the trie state before we attempt to match a character, - // so that we can skip it and try the next one. - UCharsTrie::State state; -}; - -CollationIterator::CollationIterator(const CollationIterator &other) - : UObject(other), - trie(other.trie), - data(other.data), - cesIndex(other.cesIndex), - skipped(NULL), - numCpFwd(other.numCpFwd), - isNumeric(other.isNumeric) { - UErrorCode errorCode = U_ZERO_ERROR; - int32_t length = other.ceBuffer.length; - if(length > 0 && ceBuffer.ensureAppendCapacity(length, errorCode)) { - for(int32_t i = 0; i < length; ++i) { - ceBuffer.set(i, other.ceBuffer.get(i)); - } - ceBuffer.length = length; - } else { - cesIndex = 0; - } -} - -CollationIterator::~CollationIterator() { - delete skipped; -} - -bool -CollationIterator::operator==(const CollationIterator &other) const { - // Subclasses: Call this method and then add more specific checks. - // Compare the iterator state but not the collation data (trie & data fields): - // Assume that the caller compares the data. - // Ignore skipped since that should be unused between calls to nextCE(). - // (It only stays around to avoid another memory allocation.) - if(!(typeid(*this) == typeid(other) && - ceBuffer.length == other.ceBuffer.length && - cesIndex == other.cesIndex && - numCpFwd == other.numCpFwd && - isNumeric == other.isNumeric)) { - return false; - } - for(int32_t i = 0; i < ceBuffer.length; ++i) { - if(ceBuffer.get(i) != other.ceBuffer.get(i)) { return false; } - } - return true; -} - -void -CollationIterator::reset() { - cesIndex = ceBuffer.length = 0; - if(skipped != NULL) { skipped->clear(); } -} - -int32_t -CollationIterator::fetchCEs(UErrorCode &errorCode) { - while(U_SUCCESS(errorCode) && nextCE(errorCode) != Collation::NO_CE) { - // No need to loop for each expansion CE. - cesIndex = ceBuffer.length; - } - return ceBuffer.length; -} - -uint32_t -CollationIterator::handleNextCE32(UChar32 &c, UErrorCode &errorCode) { - c = nextCodePoint(errorCode); - return (c < 0) ? Collation::FALLBACK_CE32 : data->getCE32(c); -} - -UChar -CollationIterator::handleGetTrailSurrogate() { - return 0; -} - -UBool -CollationIterator::foundNULTerminator() { - return false; -} - -UBool -CollationIterator::forbidSurrogateCodePoints() const { - return false; -} - -uint32_t -CollationIterator::getDataCE32(UChar32 c) const { - return data->getCE32(c); -} - -uint32_t -CollationIterator::getCE32FromBuilderData(uint32_t /*ce32*/, UErrorCode &errorCode) { - if(U_SUCCESS(errorCode)) { errorCode = U_INTERNAL_PROGRAM_ERROR; } - return 0; -} - -int64_t -CollationIterator::nextCEFromCE32(const CollationData *d, UChar32 c, uint32_t ce32, - UErrorCode &errorCode) { - --ceBuffer.length; // Undo ceBuffer.incLength(). - appendCEsFromCE32(d, c, ce32, true, errorCode); - if(U_SUCCESS(errorCode)) { - return ceBuffer.get(cesIndex++); - } else { - return Collation::NO_CE_PRIMARY; - } -} - -void -CollationIterator::appendCEsFromCE32(const CollationData *d, UChar32 c, uint32_t ce32, - UBool forward, UErrorCode &errorCode) { - while(Collation::isSpecialCE32(ce32)) { - switch(Collation::tagFromCE32(ce32)) { - case Collation::FALLBACK_TAG: - case Collation::RESERVED_TAG_3: - if(U_SUCCESS(errorCode)) { errorCode = U_INTERNAL_PROGRAM_ERROR; } - return; - case Collation::LONG_PRIMARY_TAG: - ceBuffer.append(Collation::ceFromLongPrimaryCE32(ce32), errorCode); - return; - case Collation::LONG_SECONDARY_TAG: - ceBuffer.append(Collation::ceFromLongSecondaryCE32(ce32), errorCode); - return; - case Collation::LATIN_EXPANSION_TAG: - if(ceBuffer.ensureAppendCapacity(2, errorCode)) { - ceBuffer.set(ceBuffer.length, Collation::latinCE0FromCE32(ce32)); - ceBuffer.set(ceBuffer.length + 1, Collation::latinCE1FromCE32(ce32)); - ceBuffer.length += 2; - } - return; - case Collation::EXPANSION32_TAG: { - const uint32_t *ce32s = d->ce32s + Collation::indexFromCE32(ce32); - int32_t length = Collation::lengthFromCE32(ce32); - if(ceBuffer.ensureAppendCapacity(length, errorCode)) { - do { - ceBuffer.appendUnsafe(Collation::ceFromCE32(*ce32s++)); - } while(--length > 0); - } - return; - } - case Collation::EXPANSION_TAG: { - const int64_t *ces = d->ces + Collation::indexFromCE32(ce32); - int32_t length = Collation::lengthFromCE32(ce32); - if(ceBuffer.ensureAppendCapacity(length, errorCode)) { - do { - ceBuffer.appendUnsafe(*ces++); - } while(--length > 0); - } - return; - } - case Collation::BUILDER_DATA_TAG: - ce32 = getCE32FromBuilderData(ce32, errorCode); - if(U_FAILURE(errorCode)) { return; } - if(ce32 == Collation::FALLBACK_CE32) { - d = data->base; - ce32 = d->getCE32(c); - } - break; - case Collation::PREFIX_TAG: - if(forward) { backwardNumCodePoints(1, errorCode); } - ce32 = getCE32FromPrefix(d, ce32, errorCode); - if(forward) { forwardNumCodePoints(1, errorCode); } - break; - case Collation::CONTRACTION_TAG: { - const UChar *p = d->contexts + Collation::indexFromCE32(ce32); - uint32_t defaultCE32 = CollationData::readCE32(p); // Default if no suffix match. - if(!forward) { - // Backward contractions are handled by previousCEUnsafe(). - // c has contractions but they were not found. - ce32 = defaultCE32; - break; - } - UChar32 nextCp; - if(skipped == NULL && numCpFwd < 0) { - // Some portion of nextCE32FromContraction() pulled out here as an ASCII fast path, - // avoiding the function call and the nextSkippedCodePoint() overhead. - nextCp = nextCodePoint(errorCode); - if(nextCp < 0) { - // No more text. - ce32 = defaultCE32; - break; - } else if((ce32 & Collation::CONTRACT_NEXT_CCC) != 0 && - !CollationFCD::mayHaveLccc(nextCp)) { - // All contraction suffixes start with characters with lccc!=0 - // but the next code point has lccc==0. - backwardNumCodePoints(1, errorCode); - ce32 = defaultCE32; - break; - } - } else { - nextCp = nextSkippedCodePoint(errorCode); - if(nextCp < 0) { - // No more text. - ce32 = defaultCE32; - break; - } else if((ce32 & Collation::CONTRACT_NEXT_CCC) != 0 && - !CollationFCD::mayHaveLccc(nextCp)) { - // All contraction suffixes start with characters with lccc!=0 - // but the next code point has lccc==0. - backwardNumSkipped(1, errorCode); - ce32 = defaultCE32; - break; - } - } - ce32 = nextCE32FromContraction(d, ce32, p + 2, defaultCE32, nextCp, errorCode); - if(ce32 == Collation::NO_CE32) { - // CEs from a discontiguous contraction plus the skipped combining marks - // have been appended already. - return; - } - break; - } - case Collation::DIGIT_TAG: - if(isNumeric) { - appendNumericCEs(ce32, forward, errorCode); - return; - } else { - // Fetch the non-numeric-collation CE32 and continue. - ce32 = d->ce32s[Collation::indexFromCE32(ce32)]; - break; - } - case Collation::U0000_TAG: - U_ASSERT(c == 0); - if(forward && foundNULTerminator()) { - // Handle NUL-termination. (Not needed in Java.) - ceBuffer.append(Collation::NO_CE, errorCode); - return; - } else { - // Fetch the normal ce32 for U+0000 and continue. - ce32 = d->ce32s[0]; - break; - } - case Collation::HANGUL_TAG: { - const uint32_t *jamoCE32s = d->jamoCE32s; - c -= Hangul::HANGUL_BASE; - UChar32 t = c % Hangul::JAMO_T_COUNT; - c /= Hangul::JAMO_T_COUNT; - UChar32 v = c % Hangul::JAMO_V_COUNT; - c /= Hangul::JAMO_V_COUNT; - if((ce32 & Collation::HANGUL_NO_SPECIAL_JAMO) != 0) { - // None of the Jamo CE32s are isSpecialCE32(). - // Avoid recursive function calls and per-Jamo tests. - if(ceBuffer.ensureAppendCapacity(t == 0 ? 2 : 3, errorCode)) { - ceBuffer.set(ceBuffer.length, Collation::ceFromCE32(jamoCE32s[c])); - ceBuffer.set(ceBuffer.length + 1, Collation::ceFromCE32(jamoCE32s[19 + v])); - ceBuffer.length += 2; - if(t != 0) { - ceBuffer.appendUnsafe(Collation::ceFromCE32(jamoCE32s[39 + t])); - } - } - return; - } else { - // We should not need to compute each Jamo code point. - // In particular, there should be no offset or implicit ce32. - appendCEsFromCE32(d, U_SENTINEL, jamoCE32s[c], forward, errorCode); - appendCEsFromCE32(d, U_SENTINEL, jamoCE32s[19 + v], forward, errorCode); - if(t == 0) { return; } - // offset 39 = 19 + 21 - 1: - // 19 = JAMO_L_COUNT - // 21 = JAMO_T_COUNT - // -1 = omit t==0 - ce32 = jamoCE32s[39 + t]; - c = U_SENTINEL; - break; - } - } - case Collation::LEAD_SURROGATE_TAG: { - U_ASSERT(forward); // Backward iteration should never see lead surrogate code _unit_ data. - U_ASSERT(U16_IS_LEAD(c)); - UChar trail; - if(U16_IS_TRAIL(trail = handleGetTrailSurrogate())) { - c = U16_GET_SUPPLEMENTARY(c, trail); - ce32 &= Collation::LEAD_TYPE_MASK; - if(ce32 == Collation::LEAD_ALL_UNASSIGNED) { - ce32 = Collation::UNASSIGNED_CE32; // unassigned-implicit - } else if(ce32 == Collation::LEAD_ALL_FALLBACK || - (ce32 = d->getCE32FromSupplementary(c)) == Collation::FALLBACK_CE32) { - // fall back to the base data - d = d->base; - ce32 = d->getCE32FromSupplementary(c); - } - } else { - // c is an unpaired surrogate. - ce32 = Collation::UNASSIGNED_CE32; - } - break; - } - case Collation::OFFSET_TAG: - U_ASSERT(c >= 0); - ceBuffer.append(d->getCEFromOffsetCE32(c, ce32), errorCode); - return; - case Collation::IMPLICIT_TAG: - U_ASSERT(c >= 0); - if(U_IS_SURROGATE(c) && forbidSurrogateCodePoints()) { - ce32 = Collation::FFFD_CE32; - break; - } else { - ceBuffer.append(Collation::unassignedCEFromCodePoint(c), errorCode); - return; - } - } - } - ceBuffer.append(Collation::ceFromSimpleCE32(ce32), errorCode); -} - -uint32_t -CollationIterator::getCE32FromPrefix(const CollationData *d, uint32_t ce32, - UErrorCode &errorCode) { - const UChar *p = d->contexts + Collation::indexFromCE32(ce32); - ce32 = CollationData::readCE32(p); // Default if no prefix match. - p += 2; - // Number of code points read before the original code point. - int32_t lookBehind = 0; - UCharsTrie prefixes(p); - for(;;) { - UChar32 c = previousCodePoint(errorCode); - if(c < 0) { break; } - ++lookBehind; - UStringTrieResult match = prefixes.nextForCodePoint(c); - if(USTRINGTRIE_HAS_VALUE(match)) { - ce32 = (uint32_t)prefixes.getValue(); - } - if(!USTRINGTRIE_HAS_NEXT(match)) { break; } - } - forwardNumCodePoints(lookBehind, errorCode); - return ce32; -} - -UChar32 -CollationIterator::nextSkippedCodePoint(UErrorCode &errorCode) { - if(skipped != NULL && skipped->hasNext()) { return skipped->next(); } - if(numCpFwd == 0) { return U_SENTINEL; } - UChar32 c = nextCodePoint(errorCode); - if(skipped != NULL && !skipped->isEmpty() && c >= 0) { skipped->incBeyond(); } - if(numCpFwd > 0 && c >= 0) { --numCpFwd; } - return c; -} - -void -CollationIterator::backwardNumSkipped(int32_t n, UErrorCode &errorCode) { - if(skipped != NULL && !skipped->isEmpty()) { - n = skipped->backwardNumCodePoints(n); - } - backwardNumCodePoints(n, errorCode); - if(numCpFwd >= 0) { numCpFwd += n; } -} - -uint32_t -CollationIterator::nextCE32FromContraction(const CollationData *d, uint32_t contractionCE32, - const UChar *p, uint32_t ce32, UChar32 c, - UErrorCode &errorCode) { - // c: next code point after the original one - - // Number of code points read beyond the original code point. - // Needed for discontiguous contraction matching. - int32_t lookAhead = 1; - // Number of code points read since the last match (initially only c). - int32_t sinceMatch = 1; - // Normally we only need a contiguous match, - // and therefore need not remember the suffixes state from before a mismatch for retrying. - // If we are already processing skipped combining marks, then we do track the state. - UCharsTrie suffixes(p); - if(skipped != NULL && !skipped->isEmpty()) { skipped->saveTrieState(suffixes); } - UStringTrieResult match = suffixes.firstForCodePoint(c); - for(;;) { - UChar32 nextCp; - if(USTRINGTRIE_HAS_VALUE(match)) { - ce32 = (uint32_t)suffixes.getValue(); - if(!USTRINGTRIE_HAS_NEXT(match) || (c = nextSkippedCodePoint(errorCode)) < 0) { - return ce32; - } - if(skipped != NULL && !skipped->isEmpty()) { skipped->saveTrieState(suffixes); } - sinceMatch = 1; - } else if(match == USTRINGTRIE_NO_MATCH || (nextCp = nextSkippedCodePoint(errorCode)) < 0) { - // No match for c, or partial match (USTRINGTRIE_NO_VALUE) and no further text. - // Back up if necessary, and try a discontiguous contraction. - if((contractionCE32 & Collation::CONTRACT_TRAILING_CCC) != 0 && - // Discontiguous contraction matching extends an existing match. - // If there is no match yet, then there is nothing to do. - ((contractionCE32 & Collation::CONTRACT_SINGLE_CP_NO_MATCH) == 0 || - sinceMatch < lookAhead)) { - // The last character of at least one suffix has lccc!=0, - // allowing for discontiguous contractions. - // UCA S2.1.1 only processes non-starters immediately following - // "a match in the table" (sinceMatch=1). - if(sinceMatch > 1) { - // Return to the state after the last match. - // (Return to sinceMatch=0 and re-fetch the first partially-matched character.) - backwardNumSkipped(sinceMatch, errorCode); - c = nextSkippedCodePoint(errorCode); - lookAhead -= sinceMatch - 1; - sinceMatch = 1; - } - if(d->getFCD16(c) > 0xff) { - return nextCE32FromDiscontiguousContraction( - d, suffixes, ce32, lookAhead, c, errorCode); - } - } - break; - } else { - // Continue after partial match (USTRINGTRIE_NO_VALUE) for c. - // It does not have a result value, therefore it is not itself "a match in the table". - // If a partially-matched c has ccc!=0 then - // it might be skipped in discontiguous contraction. - c = nextCp; - ++sinceMatch; - } - ++lookAhead; - match = suffixes.nextForCodePoint(c); - } - backwardNumSkipped(sinceMatch, errorCode); - return ce32; -} - -uint32_t -CollationIterator::nextCE32FromDiscontiguousContraction( - const CollationData *d, UCharsTrie &suffixes, uint32_t ce32, - int32_t lookAhead, UChar32 c, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - - // UCA section 3.3.2 Contractions: - // Contractions that end with non-starter characters - // are known as discontiguous contractions. - // ... discontiguous contractions must be detected in input text - // whenever the final sequence of non-starter characters could be rearranged - // so as to make a contiguous matching sequence that is canonically equivalent. - - // UCA: http://www.unicode.org/reports/tr10/#S2.1 - // S2.1 Find the longest initial substring S at each point that has a match in the table. - // S2.1.1 If there are any non-starters following S, process each non-starter C. - // S2.1.2 If C is not blocked from S, find if S + C has a match in the table. - // Note: A non-starter in a string is called blocked - // if there is another non-starter of the same canonical combining class or zero - // between it and the last character of canonical combining class 0. - // S2.1.3 If there is a match, replace S by S + C, and remove C. - - // First: Is a discontiguous contraction even possible? - uint16_t fcd16 = d->getFCD16(c); - U_ASSERT(fcd16 > 0xff); // The caller checked this already, as a shortcut. - UChar32 nextCp = nextSkippedCodePoint(errorCode); - if(nextCp < 0) { - // No further text. - backwardNumSkipped(1, errorCode); - return ce32; - } - ++lookAhead; - uint8_t prevCC = (uint8_t)fcd16; - fcd16 = d->getFCD16(nextCp); - if(fcd16 <= 0xff) { - // The next code point after c is a starter (S2.1.1 "process each non-starter"). - backwardNumSkipped(2, errorCode); - return ce32; - } - - // We have read and matched (lookAhead-2) code points, - // read non-matching c and peeked ahead at nextCp. - // Return to the state before the mismatch and continue matching with nextCp. - if(skipped == NULL || skipped->isEmpty()) { - if(skipped == NULL) { - skipped = new SkippedState(); - if(skipped == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - } - suffixes.reset(); - if(lookAhead > 2) { - // Replay the partial match so far. - backwardNumCodePoints(lookAhead, errorCode); - suffixes.firstForCodePoint(nextCodePoint(errorCode)); - for(int32_t i = 3; i < lookAhead; ++i) { - suffixes.nextForCodePoint(nextCodePoint(errorCode)); - } - // Skip c (which did not match) and nextCp (which we will try now). - forwardNumCodePoints(2, errorCode); - } - skipped->saveTrieState(suffixes); - } else { - // Reset to the trie state before the failed match of c. - skipped->resetToTrieState(suffixes); - } - - skipped->setFirstSkipped(c); - // Number of code points read since the last match (at this point: c and nextCp). - int32_t sinceMatch = 2; - c = nextCp; - for(;;) { - UStringTrieResult match; - // "If C is not blocked from S, find if S + C has a match in the table." (S2.1.2) - if(prevCC < (fcd16 >> 8) && USTRINGTRIE_HAS_VALUE(match = suffixes.nextForCodePoint(c))) { - // "If there is a match, replace S by S + C, and remove C." (S2.1.3) - // Keep prevCC unchanged. - ce32 = (uint32_t)suffixes.getValue(); - sinceMatch = 0; - skipped->recordMatch(); - if(!USTRINGTRIE_HAS_NEXT(match)) { break; } - skipped->saveTrieState(suffixes); - } else { - // No match for "S + C", skip C. - skipped->skip(c); - skipped->resetToTrieState(suffixes); - prevCC = (uint8_t)fcd16; - } - if((c = nextSkippedCodePoint(errorCode)) < 0) { break; } - ++sinceMatch; - fcd16 = d->getFCD16(c); - if(fcd16 <= 0xff) { - // The next code point after c is a starter (S2.1.1 "process each non-starter"). - break; - } - } - backwardNumSkipped(sinceMatch, errorCode); - UBool isTopDiscontiguous = skipped->isEmpty(); - skipped->replaceMatch(); - if(isTopDiscontiguous && !skipped->isEmpty()) { - // We did get a match after skipping one or more combining marks, - // and we are not in a recursive discontiguous contraction. - // Append CEs from the contraction ce32 - // and then from the combining marks that we skipped before the match. - c = U_SENTINEL; - for(;;) { - appendCEsFromCE32(d, c, ce32, true, errorCode); - // Fetch CE32s for skipped combining marks from the normal data, with fallback, - // rather than from the CollationData where we found the contraction. - if(!skipped->hasNext()) { break; } - c = skipped->next(); - ce32 = getDataCE32(c); - if(ce32 == Collation::FALLBACK_CE32) { - d = data->base; - ce32 = d->getCE32(c); - } else { - d = data; - } - // Note: A nested discontiguous-contraction match - // replaces consumed combining marks with newly skipped ones - // and resets the reading position to the beginning. - } - skipped->clear(); - ce32 = Collation::NO_CE32; // Signal to the caller that the result is in the ceBuffer. - } - return ce32; -} - -void -CollationIterator::appendNumericCEs(uint32_t ce32, UBool forward, UErrorCode &errorCode) { - // Collect digits. - CharString digits; - if(forward) { - for(;;) { - char digit = Collation::digitFromCE32(ce32); - digits.append(digit, errorCode); - if(numCpFwd == 0) { break; } - UChar32 c = nextCodePoint(errorCode); - if(c < 0) { break; } - ce32 = data->getCE32(c); - if(ce32 == Collation::FALLBACK_CE32) { - ce32 = data->base->getCE32(c); - } - if(!Collation::hasCE32Tag(ce32, Collation::DIGIT_TAG)) { - backwardNumCodePoints(1, errorCode); - break; - } - if(numCpFwd > 0) { --numCpFwd; } - } - } else { - for(;;) { - char digit = Collation::digitFromCE32(ce32); - digits.append(digit, errorCode); - UChar32 c = previousCodePoint(errorCode); - if(c < 0) { break; } - ce32 = data->getCE32(c); - if(ce32 == Collation::FALLBACK_CE32) { - ce32 = data->base->getCE32(c); - } - if(!Collation::hasCE32Tag(ce32, Collation::DIGIT_TAG)) { - forwardNumCodePoints(1, errorCode); - break; - } - } - // Reverse the digit string. - char *p = digits.data(); - char *q = p + digits.length() - 1; - while(p < q) { - char digit = *p; - *p++ = *q; - *q-- = digit; - } - } - if(U_FAILURE(errorCode)) { return; } - int32_t pos = 0; - do { - // Skip leading zeros. - while(pos < (digits.length() - 1) && digits[pos] == 0) { ++pos; } - // Write a sequence of CEs for at most 254 digits at a time. - int32_t segmentLength = digits.length() - pos; - if(segmentLength > 254) { segmentLength = 254; } - appendNumericSegmentCEs(digits.data() + pos, segmentLength, errorCode); - pos += segmentLength; - } while(U_SUCCESS(errorCode) && pos < digits.length()); -} - -void -CollationIterator::appendNumericSegmentCEs(const char *digits, int32_t length, UErrorCode &errorCode) { - U_ASSERT(1 <= length && length <= 254); - U_ASSERT(length == 1 || digits[0] != 0); - uint32_t numericPrimary = data->numericPrimary; - // Note: We use primary byte values 2..255: digits are not compressible. - if(length <= 7) { - // Very dense encoding for small numbers. - int32_t value = digits[0]; - for(int32_t i = 1; i < length; ++i) { - value = value * 10 + digits[i]; - } - // Primary weight second byte values: - // 74 byte values 2.. 75 for small numbers in two-byte primary weights. - // 40 byte values 76..115 for medium numbers in three-byte primary weights. - // 16 byte values 116..131 for large numbers in four-byte primary weights. - // 124 byte values 132..255 for very large numbers with 4..127 digit pairs. - int32_t firstByte = 2; - int32_t numBytes = 74; - if(value < numBytes) { - // Two-byte primary for 0..73, good for day & month numbers etc. - uint32_t primary = numericPrimary | ((firstByte + value) << 16); - ceBuffer.append(Collation::makeCE(primary), errorCode); - return; - } - value -= numBytes; - firstByte += numBytes; - numBytes = 40; - if(value < numBytes * 254) { - // Three-byte primary for 74..10233=74+40*254-1, good for year numbers and more. - uint32_t primary = numericPrimary | - ((firstByte + value / 254) << 16) | ((2 + value % 254) << 8); - ceBuffer.append(Collation::makeCE(primary), errorCode); - return; - } - value -= numBytes * 254; - firstByte += numBytes; - numBytes = 16; - if(value < numBytes * 254 * 254) { - // Four-byte primary for 10234..1042489=10234+16*254*254-1. - uint32_t primary = numericPrimary | (2 + value % 254); - value /= 254; - primary |= (2 + value % 254) << 8; - value /= 254; - primary |= (firstByte + value % 254) << 16; - ceBuffer.append(Collation::makeCE(primary), errorCode); - return; - } - // original value > 1042489 - } - U_ASSERT(length >= 7); - - // The second primary byte value 132..255 indicates the number of digit pairs (4..127), - // then we generate primary bytes with those pairs. - // Omit trailing 00 pairs. - // Decrement the value for the last pair. - - // Set the exponent. 4 pairs->132, 5 pairs->133, ..., 127 pairs->255. - int32_t numPairs = (length + 1) / 2; - uint32_t primary = numericPrimary | ((132 - 4 + numPairs) << 16); - // Find the length without trailing 00 pairs. - while(digits[length - 1] == 0 && digits[length - 2] == 0) { - length -= 2; - } - // Read the first pair. - uint32_t pair; - int32_t pos; - if(length & 1) { - // Only "half a pair" if we have an odd number of digits. - pair = digits[0]; - pos = 1; - } else { - pair = digits[0] * 10 + digits[1]; - pos = 2; - } - pair = 11 + 2 * pair; - // Add the pairs of digits between pos and length. - int32_t shift = 8; - while(pos < length) { - if(shift == 0) { - // Every three pairs/bytes we need to store a 4-byte-primary CE - // and start with a new CE with the '0' primary lead byte. - primary |= pair; - ceBuffer.append(Collation::makeCE(primary), errorCode); - primary = numericPrimary; - shift = 16; - } else { - primary |= pair << shift; - shift -= 8; - } - pair = 11 + 2 * (digits[pos] * 10 + digits[pos + 1]); - pos += 2; - } - primary |= (pair - 1) << shift; - ceBuffer.append(Collation::makeCE(primary), errorCode); -} - -int64_t -CollationIterator::previousCE(UVector32 &offsets, UErrorCode &errorCode) { - if(ceBuffer.length > 0) { - // Return the previous buffered CE. - return ceBuffer.get(--ceBuffer.length); - } - offsets.removeAllElements(); - int32_t limitOffset = getOffset(); - UChar32 c = previousCodePoint(errorCode); - if(c < 0) { return Collation::NO_CE; } - if(data->isUnsafeBackward(c, isNumeric)) { - return previousCEUnsafe(c, offsets, errorCode); - } - // Simple, safe-backwards iteration: - // Get a CE going backwards, handle prefixes but no contractions. - uint32_t ce32 = data->getCE32(c); - const CollationData *d; - if(ce32 == Collation::FALLBACK_CE32) { - d = data->base; - ce32 = d->getCE32(c); - } else { - d = data; - } - if(Collation::isSimpleOrLongCE32(ce32)) { - return Collation::ceFromCE32(ce32); - } - appendCEsFromCE32(d, c, ce32, false, errorCode); - if(U_SUCCESS(errorCode)) { - if(ceBuffer.length > 1) { - offsets.addElement(getOffset(), errorCode); - // For an expansion, the offset of each non-initial CE is the limit offset, - // consistent with forward iteration. - while(offsets.size() <= ceBuffer.length) { - offsets.addElement(limitOffset, errorCode); - } - } - return ceBuffer.get(--ceBuffer.length); - } else { - return Collation::NO_CE_PRIMARY; - } -} - -int64_t -CollationIterator::previousCEUnsafe(UChar32 c, UVector32 &offsets, UErrorCode &errorCode) { - // We just move through the input counting safe and unsafe code points - // without collecting the unsafe-backward substring into a buffer and - // switching to it. - // This is to keep the logic simple. Otherwise we would have to handle - // prefix matching going before the backward buffer, switching - // to iteration and back, etc. - // In the most important case of iterating over a normal string, - // reading from the string itself is already maximally fast. - // The only drawback there is that after getting the CEs we always - // skip backward to the safe character rather than switching out - // of a backwardBuffer. - // But this should not be the common case for previousCE(), - // and correctness and maintainability are more important than - // complex optimizations. - // Find the first safe character before c. - int32_t numBackward = 1; - while((c = previousCodePoint(errorCode)) >= 0) { - ++numBackward; - if(!data->isUnsafeBackward(c, isNumeric)) { - break; - } - } - // Set the forward iteration limit. - // Note: This counts code points. - // We cannot enforce a limit in the middle of a surrogate pair or similar. - numCpFwd = numBackward; - // Reset the forward iterator. - cesIndex = 0; - U_ASSERT(ceBuffer.length == 0); - // Go forward and collect the CEs. - int32_t offset = getOffset(); - while(numCpFwd > 0) { - // nextCE() normally reads one code point. - // Contraction matching and digit specials read more and check numCpFwd. - --numCpFwd; - // Append one or more CEs to the ceBuffer. - (void)nextCE(errorCode); - U_ASSERT(U_FAILURE(errorCode) || ceBuffer.get(ceBuffer.length - 1) != Collation::NO_CE); - // No need to loop for getting each expansion CE from nextCE(). - cesIndex = ceBuffer.length; - // However, we need to write an offset for each CE. - // This is for CollationElementIterator::getOffset() to return - // intermediate offsets from the unsafe-backwards segment. - U_ASSERT(offsets.size() < ceBuffer.length); - offsets.addElement(offset, errorCode); - // For an expansion, the offset of each non-initial CE is the limit offset, - // consistent with forward iteration. - offset = getOffset(); - while(offsets.size() < ceBuffer.length) { - offsets.addElement(offset, errorCode); - } - } - U_ASSERT(offsets.size() == ceBuffer.length); - // End offset corresponding to just after the unsafe-backwards segment. - offsets.addElement(offset, errorCode); - // Reset the forward iteration limit - // and move backward to before the segment for which we fetched CEs. - numCpFwd = -1; - backwardNumCodePoints(numBackward, errorCode); - // Use the collected CEs and return the last one. - cesIndex = 0; // Avoid cesIndex > ceBuffer.length when that gets decremented. - if(U_SUCCESS(errorCode)) { - return ceBuffer.get(--ceBuffer.length); - } else { - return Collation::NO_CE_PRIMARY; - } -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationiterator.cpp +* +* created on: 2010oct27 +* created by: Markus W. Scherer +*/ + +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/ucharstrie.h" +#include "unicode/ustringtrie.h" +#include "charstr.h" +#include "cmemory.h" +#include "collation.h" +#include "collationdata.h" +#include "collationfcd.h" +#include "collationiterator.h" +#include "normalizer2impl.h" +#include "uassert.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +CollationIterator::CEBuffer::~CEBuffer() {} + +UBool +CollationIterator::CEBuffer::ensureAppendCapacity(int32_t appCap, UErrorCode &errorCode) { + int32_t capacity = buffer.getCapacity(); + if((length + appCap) <= capacity) { return true; } + if(U_FAILURE(errorCode)) { return false; } + do { + if(capacity < 1000) { + capacity *= 4; + } else { + capacity *= 2; + } + } while(capacity < (length + appCap)); + int64_t *p = buffer.resize(capacity, length); + if(p == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return false; + } + return true; +} + +// State of combining marks skipped in discontiguous contraction. +// We create a state object on first use and keep it around deactivated between uses. +class SkippedState : public UMemory { +public: + // Born active but empty. + SkippedState() : pos(0), skipLengthAtMatch(0) {} + void clear() { + oldBuffer.remove(); + pos = 0; + // The newBuffer is reset by setFirstSkipped(). + } + + UBool isEmpty() const { return oldBuffer.isEmpty(); } + + UBool hasNext() const { return pos < oldBuffer.length(); } + + // Requires hasNext(). + UChar32 next() { + UChar32 c = oldBuffer.char32At(pos); + pos += U16_LENGTH(c); + return c; + } + + // Accounts for one more input code point read beyond the end of the marks buffer. + void incBeyond() { + U_ASSERT(!hasNext()); + ++pos; + } + + // Goes backward through the skipped-marks buffer. + // Returns the number of code points read beyond the skipped marks + // that need to be backtracked through normal input. + int32_t backwardNumCodePoints(int32_t n) { + int32_t length = oldBuffer.length(); + int32_t beyond = pos - length; + if(beyond > 0) { + if(beyond >= n) { + // Not back far enough to re-enter the oldBuffer. + pos -= n; + return n; + } else { + // Back out all beyond-oldBuffer code points and re-enter the buffer. + pos = oldBuffer.moveIndex32(length, beyond - n); + return beyond; + } + } else { + // Go backwards from inside the oldBuffer. + pos = oldBuffer.moveIndex32(pos, -n); + return 0; + } + } + + void setFirstSkipped(UChar32 c) { + skipLengthAtMatch = 0; + newBuffer.setTo(c); + } + + void skip(UChar32 c) { + newBuffer.append(c); + } + + void recordMatch() { skipLengthAtMatch = newBuffer.length(); } + + // Replaces the characters we consumed with the newly skipped ones. + void replaceMatch() { + // Note: UnicodeString.replace() pins pos to at most length(). + oldBuffer.replace(0, pos, newBuffer, 0, skipLengthAtMatch); + pos = 0; + } + + void saveTrieState(const UCharsTrie &trie) { trie.saveState(state); } + void resetToTrieState(UCharsTrie &trie) const { trie.resetToState(state); } + +private: + // Combining marks skipped in previous discontiguous-contraction matching. + // After that discontiguous contraction was completed, we start reading them from here. + UnicodeString oldBuffer; + // Combining marks newly skipped in current discontiguous-contraction matching. + // These might have been read from the normal text or from the oldBuffer. + UnicodeString newBuffer; + // Reading index in oldBuffer, + // or counter for how many code points have been read beyond oldBuffer (pos-oldBuffer.length()). + int32_t pos; + // newBuffer.length() at the time of the last matching character. + // When a partial match fails, we back out skipped and partial-matching input characters. + int32_t skipLengthAtMatch; + // We save the trie state before we attempt to match a character, + // so that we can skip it and try the next one. + UCharsTrie::State state; +}; + +CollationIterator::CollationIterator(const CollationIterator &other) + : UObject(other), + trie(other.trie), + data(other.data), + cesIndex(other.cesIndex), + skipped(nullptr), + numCpFwd(other.numCpFwd), + isNumeric(other.isNumeric) { + UErrorCode errorCode = U_ZERO_ERROR; + int32_t length = other.ceBuffer.length; + if(length > 0 && ceBuffer.ensureAppendCapacity(length, errorCode)) { + for(int32_t i = 0; i < length; ++i) { + ceBuffer.set(i, other.ceBuffer.get(i)); + } + ceBuffer.length = length; + } else { + cesIndex = 0; + } +} + +CollationIterator::~CollationIterator() { + delete skipped; +} + +bool +CollationIterator::operator==(const CollationIterator &other) const { + // Subclasses: Call this method and then add more specific checks. + // Compare the iterator state but not the collation data (trie & data fields): + // Assume that the caller compares the data. + // Ignore skipped since that should be unused between calls to nextCE(). + // (It only stays around to avoid another memory allocation.) + if(!(typeid(*this) == typeid(other) && + ceBuffer.length == other.ceBuffer.length && + cesIndex == other.cesIndex && + numCpFwd == other.numCpFwd && + isNumeric == other.isNumeric)) { + return false; + } + for(int32_t i = 0; i < ceBuffer.length; ++i) { + if(ceBuffer.get(i) != other.ceBuffer.get(i)) { return false; } + } + return true; +} + +void +CollationIterator::reset() { + cesIndex = ceBuffer.length = 0; + if(skipped != nullptr) { skipped->clear(); } +} + +int32_t +CollationIterator::fetchCEs(UErrorCode &errorCode) { + while(U_SUCCESS(errorCode) && nextCE(errorCode) != Collation::NO_CE) { + // No need to loop for each expansion CE. + cesIndex = ceBuffer.length; + } + return ceBuffer.length; +} + +uint32_t +CollationIterator::handleNextCE32(UChar32 &c, UErrorCode &errorCode) { + c = nextCodePoint(errorCode); + return (c < 0) ? Collation::FALLBACK_CE32 : data->getCE32(c); +} + +char16_t +CollationIterator::handleGetTrailSurrogate() { + return 0; +} + +UBool +CollationIterator::foundNULTerminator() { + return false; +} + +UBool +CollationIterator::forbidSurrogateCodePoints() const { + return false; +} + +uint32_t +CollationIterator::getDataCE32(UChar32 c) const { + return data->getCE32(c); +} + +uint32_t +CollationIterator::getCE32FromBuilderData(uint32_t /*ce32*/, UErrorCode &errorCode) { + if(U_SUCCESS(errorCode)) { errorCode = U_INTERNAL_PROGRAM_ERROR; } + return 0; +} + +int64_t +CollationIterator::nextCEFromCE32(const CollationData *d, UChar32 c, uint32_t ce32, + UErrorCode &errorCode) { + --ceBuffer.length; // Undo ceBuffer.incLength(). + appendCEsFromCE32(d, c, ce32, true, errorCode); + if(U_SUCCESS(errorCode)) { + return ceBuffer.get(cesIndex++); + } else { + return Collation::NO_CE_PRIMARY; + } +} + +void +CollationIterator::appendCEsFromCE32(const CollationData *d, UChar32 c, uint32_t ce32, + UBool forward, UErrorCode &errorCode) { + while(Collation::isSpecialCE32(ce32)) { + switch(Collation::tagFromCE32(ce32)) { + case Collation::FALLBACK_TAG: + case Collation::RESERVED_TAG_3: + if(U_SUCCESS(errorCode)) { errorCode = U_INTERNAL_PROGRAM_ERROR; } + return; + case Collation::LONG_PRIMARY_TAG: + ceBuffer.append(Collation::ceFromLongPrimaryCE32(ce32), errorCode); + return; + case Collation::LONG_SECONDARY_TAG: + ceBuffer.append(Collation::ceFromLongSecondaryCE32(ce32), errorCode); + return; + case Collation::LATIN_EXPANSION_TAG: + if(ceBuffer.ensureAppendCapacity(2, errorCode)) { + ceBuffer.set(ceBuffer.length, Collation::latinCE0FromCE32(ce32)); + ceBuffer.set(ceBuffer.length + 1, Collation::latinCE1FromCE32(ce32)); + ceBuffer.length += 2; + } + return; + case Collation::EXPANSION32_TAG: { + const uint32_t *ce32s = d->ce32s + Collation::indexFromCE32(ce32); + int32_t length = Collation::lengthFromCE32(ce32); + if(ceBuffer.ensureAppendCapacity(length, errorCode)) { + do { + ceBuffer.appendUnsafe(Collation::ceFromCE32(*ce32s++)); + } while(--length > 0); + } + return; + } + case Collation::EXPANSION_TAG: { + const int64_t *ces = d->ces + Collation::indexFromCE32(ce32); + int32_t length = Collation::lengthFromCE32(ce32); + if(ceBuffer.ensureAppendCapacity(length, errorCode)) { + do { + ceBuffer.appendUnsafe(*ces++); + } while(--length > 0); + } + return; + } + case Collation::BUILDER_DATA_TAG: + ce32 = getCE32FromBuilderData(ce32, errorCode); + if(U_FAILURE(errorCode)) { return; } + if(ce32 == Collation::FALLBACK_CE32) { + d = data->base; + ce32 = d->getCE32(c); + } + break; + case Collation::PREFIX_TAG: + if(forward) { backwardNumCodePoints(1, errorCode); } + ce32 = getCE32FromPrefix(d, ce32, errorCode); + if(forward) { forwardNumCodePoints(1, errorCode); } + break; + case Collation::CONTRACTION_TAG: { + const char16_t *p = d->contexts + Collation::indexFromCE32(ce32); + uint32_t defaultCE32 = CollationData::readCE32(p); // Default if no suffix match. + if(!forward) { + // Backward contractions are handled by previousCEUnsafe(). + // c has contractions but they were not found. + ce32 = defaultCE32; + break; + } + UChar32 nextCp; + if(skipped == nullptr && numCpFwd < 0) { + // Some portion of nextCE32FromContraction() pulled out here as an ASCII fast path, + // avoiding the function call and the nextSkippedCodePoint() overhead. + nextCp = nextCodePoint(errorCode); + if(nextCp < 0) { + // No more text. + ce32 = defaultCE32; + break; + } else if((ce32 & Collation::CONTRACT_NEXT_CCC) != 0 && + !CollationFCD::mayHaveLccc(nextCp)) { + // All contraction suffixes start with characters with lccc!=0 + // but the next code point has lccc==0. + backwardNumCodePoints(1, errorCode); + ce32 = defaultCE32; + break; + } + } else { + nextCp = nextSkippedCodePoint(errorCode); + if(nextCp < 0) { + // No more text. + ce32 = defaultCE32; + break; + } else if((ce32 & Collation::CONTRACT_NEXT_CCC) != 0 && + !CollationFCD::mayHaveLccc(nextCp)) { + // All contraction suffixes start with characters with lccc!=0 + // but the next code point has lccc==0. + backwardNumSkipped(1, errorCode); + ce32 = defaultCE32; + break; + } + } + ce32 = nextCE32FromContraction(d, ce32, p + 2, defaultCE32, nextCp, errorCode); + if(ce32 == Collation::NO_CE32) { + // CEs from a discontiguous contraction plus the skipped combining marks + // have been appended already. + return; + } + break; + } + case Collation::DIGIT_TAG: + if(isNumeric) { + appendNumericCEs(ce32, forward, errorCode); + return; + } else { + // Fetch the non-numeric-collation CE32 and continue. + ce32 = d->ce32s[Collation::indexFromCE32(ce32)]; + break; + } + case Collation::U0000_TAG: + U_ASSERT(c == 0); + if(forward && foundNULTerminator()) { + // Handle NUL-termination. (Not needed in Java.) + ceBuffer.append(Collation::NO_CE, errorCode); + return; + } else { + // Fetch the normal ce32 for U+0000 and continue. + ce32 = d->ce32s[0]; + break; + } + case Collation::HANGUL_TAG: { + const uint32_t *jamoCE32s = d->jamoCE32s; + c -= Hangul::HANGUL_BASE; + UChar32 t = c % Hangul::JAMO_T_COUNT; + c /= Hangul::JAMO_T_COUNT; + UChar32 v = c % Hangul::JAMO_V_COUNT; + c /= Hangul::JAMO_V_COUNT; + if((ce32 & Collation::HANGUL_NO_SPECIAL_JAMO) != 0) { + // None of the Jamo CE32s are isSpecialCE32(). + // Avoid recursive function calls and per-Jamo tests. + if(ceBuffer.ensureAppendCapacity(t == 0 ? 2 : 3, errorCode)) { + ceBuffer.set(ceBuffer.length, Collation::ceFromCE32(jamoCE32s[c])); + ceBuffer.set(ceBuffer.length + 1, Collation::ceFromCE32(jamoCE32s[19 + v])); + ceBuffer.length += 2; + if(t != 0) { + ceBuffer.appendUnsafe(Collation::ceFromCE32(jamoCE32s[39 + t])); + } + } + return; + } else { + // We should not need to compute each Jamo code point. + // In particular, there should be no offset or implicit ce32. + appendCEsFromCE32(d, U_SENTINEL, jamoCE32s[c], forward, errorCode); + appendCEsFromCE32(d, U_SENTINEL, jamoCE32s[19 + v], forward, errorCode); + if(t == 0) { return; } + // offset 39 = 19 + 21 - 1: + // 19 = JAMO_L_COUNT + // 21 = JAMO_T_COUNT + // -1 = omit t==0 + ce32 = jamoCE32s[39 + t]; + c = U_SENTINEL; + break; + } + } + case Collation::LEAD_SURROGATE_TAG: { + U_ASSERT(forward); // Backward iteration should never see lead surrogate code _unit_ data. + U_ASSERT(U16_IS_LEAD(c)); + char16_t trail; + if(U16_IS_TRAIL(trail = handleGetTrailSurrogate())) { + c = U16_GET_SUPPLEMENTARY(c, trail); + ce32 &= Collation::LEAD_TYPE_MASK; + if(ce32 == Collation::LEAD_ALL_UNASSIGNED) { + ce32 = Collation::UNASSIGNED_CE32; // unassigned-implicit + } else if(ce32 == Collation::LEAD_ALL_FALLBACK || + (ce32 = d->getCE32FromSupplementary(c)) == Collation::FALLBACK_CE32) { + // fall back to the base data + d = d->base; + ce32 = d->getCE32FromSupplementary(c); + } + } else { + // c is an unpaired surrogate. + ce32 = Collation::UNASSIGNED_CE32; + } + break; + } + case Collation::OFFSET_TAG: + U_ASSERT(c >= 0); + ceBuffer.append(d->getCEFromOffsetCE32(c, ce32), errorCode); + return; + case Collation::IMPLICIT_TAG: + U_ASSERT(c >= 0); + if(U_IS_SURROGATE(c) && forbidSurrogateCodePoints()) { + ce32 = Collation::FFFD_CE32; + break; + } else { + ceBuffer.append(Collation::unassignedCEFromCodePoint(c), errorCode); + return; + } + } + } + ceBuffer.append(Collation::ceFromSimpleCE32(ce32), errorCode); +} + +uint32_t +CollationIterator::getCE32FromPrefix(const CollationData *d, uint32_t ce32, + UErrorCode &errorCode) { + const char16_t *p = d->contexts + Collation::indexFromCE32(ce32); + ce32 = CollationData::readCE32(p); // Default if no prefix match. + p += 2; + // Number of code points read before the original code point. + int32_t lookBehind = 0; + UCharsTrie prefixes(p); + for(;;) { + UChar32 c = previousCodePoint(errorCode); + if(c < 0) { break; } + ++lookBehind; + UStringTrieResult match = prefixes.nextForCodePoint(c); + if(USTRINGTRIE_HAS_VALUE(match)) { + ce32 = (uint32_t)prefixes.getValue(); + } + if(!USTRINGTRIE_HAS_NEXT(match)) { break; } + } + forwardNumCodePoints(lookBehind, errorCode); + return ce32; +} + +UChar32 +CollationIterator::nextSkippedCodePoint(UErrorCode &errorCode) { + if(skipped != nullptr && skipped->hasNext()) { return skipped->next(); } + if(numCpFwd == 0) { return U_SENTINEL; } + UChar32 c = nextCodePoint(errorCode); + if(skipped != nullptr && !skipped->isEmpty() && c >= 0) { skipped->incBeyond(); } + if(numCpFwd > 0 && c >= 0) { --numCpFwd; } + return c; +} + +void +CollationIterator::backwardNumSkipped(int32_t n, UErrorCode &errorCode) { + if(skipped != nullptr && !skipped->isEmpty()) { + n = skipped->backwardNumCodePoints(n); + } + backwardNumCodePoints(n, errorCode); + if(numCpFwd >= 0) { numCpFwd += n; } +} + +uint32_t +CollationIterator::nextCE32FromContraction(const CollationData *d, uint32_t contractionCE32, + const char16_t *p, uint32_t ce32, UChar32 c, + UErrorCode &errorCode) { + // c: next code point after the original one + + // Number of code points read beyond the original code point. + // Needed for discontiguous contraction matching. + int32_t lookAhead = 1; + // Number of code points read since the last match (initially only c). + int32_t sinceMatch = 1; + // Normally we only need a contiguous match, + // and therefore need not remember the suffixes state from before a mismatch for retrying. + // If we are already processing skipped combining marks, then we do track the state. + UCharsTrie suffixes(p); + if(skipped != nullptr && !skipped->isEmpty()) { skipped->saveTrieState(suffixes); } + UStringTrieResult match = suffixes.firstForCodePoint(c); + for(;;) { + UChar32 nextCp; + if(USTRINGTRIE_HAS_VALUE(match)) { + ce32 = (uint32_t)suffixes.getValue(); + if(!USTRINGTRIE_HAS_NEXT(match) || (c = nextSkippedCodePoint(errorCode)) < 0) { + return ce32; + } + if(skipped != nullptr && !skipped->isEmpty()) { skipped->saveTrieState(suffixes); } + sinceMatch = 1; + } else if(match == USTRINGTRIE_NO_MATCH || (nextCp = nextSkippedCodePoint(errorCode)) < 0) { + // No match for c, or partial match (USTRINGTRIE_NO_VALUE) and no further text. + // Back up if necessary, and try a discontiguous contraction. + if((contractionCE32 & Collation::CONTRACT_TRAILING_CCC) != 0 && + // Discontiguous contraction matching extends an existing match. + // If there is no match yet, then there is nothing to do. + ((contractionCE32 & Collation::CONTRACT_SINGLE_CP_NO_MATCH) == 0 || + sinceMatch < lookAhead)) { + // The last character of at least one suffix has lccc!=0, + // allowing for discontiguous contractions. + // UCA S2.1.1 only processes non-starters immediately following + // "a match in the table" (sinceMatch=1). + if(sinceMatch > 1) { + // Return to the state after the last match. + // (Return to sinceMatch=0 and re-fetch the first partially-matched character.) + backwardNumSkipped(sinceMatch, errorCode); + c = nextSkippedCodePoint(errorCode); + lookAhead -= sinceMatch - 1; + sinceMatch = 1; + } + if(d->getFCD16(c) > 0xff) { + return nextCE32FromDiscontiguousContraction( + d, suffixes, ce32, lookAhead, c, errorCode); + } + } + break; + } else { + // Continue after partial match (USTRINGTRIE_NO_VALUE) for c. + // It does not have a result value, therefore it is not itself "a match in the table". + // If a partially-matched c has ccc!=0 then + // it might be skipped in discontiguous contraction. + c = nextCp; + ++sinceMatch; + } + ++lookAhead; + match = suffixes.nextForCodePoint(c); + } + backwardNumSkipped(sinceMatch, errorCode); + return ce32; +} + +uint32_t +CollationIterator::nextCE32FromDiscontiguousContraction( + const CollationData *d, UCharsTrie &suffixes, uint32_t ce32, + int32_t lookAhead, UChar32 c, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + + // UCA section 3.3.2 Contractions: + // Contractions that end with non-starter characters + // are known as discontiguous contractions. + // ... discontiguous contractions must be detected in input text + // whenever the final sequence of non-starter characters could be rearranged + // so as to make a contiguous matching sequence that is canonically equivalent. + + // UCA: http://www.unicode.org/reports/tr10/#S2.1 + // S2.1 Find the longest initial substring S at each point that has a match in the table. + // S2.1.1 If there are any non-starters following S, process each non-starter C. + // S2.1.2 If C is not blocked from S, find if S + C has a match in the table. + // Note: A non-starter in a string is called blocked + // if there is another non-starter of the same canonical combining class or zero + // between it and the last character of canonical combining class 0. + // S2.1.3 If there is a match, replace S by S + C, and remove C. + + // First: Is a discontiguous contraction even possible? + uint16_t fcd16 = d->getFCD16(c); + U_ASSERT(fcd16 > 0xff); // The caller checked this already, as a shortcut. + UChar32 nextCp = nextSkippedCodePoint(errorCode); + if(nextCp < 0) { + // No further text. + backwardNumSkipped(1, errorCode); + return ce32; + } + ++lookAhead; + uint8_t prevCC = (uint8_t)fcd16; + fcd16 = d->getFCD16(nextCp); + if(fcd16 <= 0xff) { + // The next code point after c is a starter (S2.1.1 "process each non-starter"). + backwardNumSkipped(2, errorCode); + return ce32; + } + + // We have read and matched (lookAhead-2) code points, + // read non-matching c and peeked ahead at nextCp. + // Return to the state before the mismatch and continue matching with nextCp. + if(skipped == nullptr || skipped->isEmpty()) { + if(skipped == nullptr) { + skipped = new SkippedState(); + if(skipped == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + } + suffixes.reset(); + if(lookAhead > 2) { + // Replay the partial match so far. + backwardNumCodePoints(lookAhead, errorCode); + suffixes.firstForCodePoint(nextCodePoint(errorCode)); + for(int32_t i = 3; i < lookAhead; ++i) { + suffixes.nextForCodePoint(nextCodePoint(errorCode)); + } + // Skip c (which did not match) and nextCp (which we will try now). + forwardNumCodePoints(2, errorCode); + } + skipped->saveTrieState(suffixes); + } else { + // Reset to the trie state before the failed match of c. + skipped->resetToTrieState(suffixes); + } + + skipped->setFirstSkipped(c); + // Number of code points read since the last match (at this point: c and nextCp). + int32_t sinceMatch = 2; + c = nextCp; + for(;;) { + UStringTrieResult match; + // "If C is not blocked from S, find if S + C has a match in the table." (S2.1.2) + if(prevCC < (fcd16 >> 8) && USTRINGTRIE_HAS_VALUE(match = suffixes.nextForCodePoint(c))) { + // "If there is a match, replace S by S + C, and remove C." (S2.1.3) + // Keep prevCC unchanged. + ce32 = (uint32_t)suffixes.getValue(); + sinceMatch = 0; + skipped->recordMatch(); + if(!USTRINGTRIE_HAS_NEXT(match)) { break; } + skipped->saveTrieState(suffixes); + } else { + // No match for "S + C", skip C. + skipped->skip(c); + skipped->resetToTrieState(suffixes); + prevCC = (uint8_t)fcd16; + } + if((c = nextSkippedCodePoint(errorCode)) < 0) { break; } + ++sinceMatch; + fcd16 = d->getFCD16(c); + if(fcd16 <= 0xff) { + // The next code point after c is a starter (S2.1.1 "process each non-starter"). + break; + } + } + backwardNumSkipped(sinceMatch, errorCode); + UBool isTopDiscontiguous = skipped->isEmpty(); + skipped->replaceMatch(); + if(isTopDiscontiguous && !skipped->isEmpty()) { + // We did get a match after skipping one or more combining marks, + // and we are not in a recursive discontiguous contraction. + // Append CEs from the contraction ce32 + // and then from the combining marks that we skipped before the match. + c = U_SENTINEL; + for(;;) { + appendCEsFromCE32(d, c, ce32, true, errorCode); + // Fetch CE32s for skipped combining marks from the normal data, with fallback, + // rather than from the CollationData where we found the contraction. + if(!skipped->hasNext()) { break; } + c = skipped->next(); + ce32 = getDataCE32(c); + if(ce32 == Collation::FALLBACK_CE32) { + d = data->base; + ce32 = d->getCE32(c); + } else { + d = data; + } + // Note: A nested discontiguous-contraction match + // replaces consumed combining marks with newly skipped ones + // and resets the reading position to the beginning. + } + skipped->clear(); + ce32 = Collation::NO_CE32; // Signal to the caller that the result is in the ceBuffer. + } + return ce32; +} + +void +CollationIterator::appendNumericCEs(uint32_t ce32, UBool forward, UErrorCode &errorCode) { + // Collect digits. + CharString digits; + if(forward) { + for(;;) { + char digit = Collation::digitFromCE32(ce32); + digits.append(digit, errorCode); + if(numCpFwd == 0) { break; } + UChar32 c = nextCodePoint(errorCode); + if(c < 0) { break; } + ce32 = data->getCE32(c); + if(ce32 == Collation::FALLBACK_CE32) { + ce32 = data->base->getCE32(c); + } + if(!Collation::hasCE32Tag(ce32, Collation::DIGIT_TAG)) { + backwardNumCodePoints(1, errorCode); + break; + } + if(numCpFwd > 0) { --numCpFwd; } + } + } else { + for(;;) { + char digit = Collation::digitFromCE32(ce32); + digits.append(digit, errorCode); + UChar32 c = previousCodePoint(errorCode); + if(c < 0) { break; } + ce32 = data->getCE32(c); + if(ce32 == Collation::FALLBACK_CE32) { + ce32 = data->base->getCE32(c); + } + if(!Collation::hasCE32Tag(ce32, Collation::DIGIT_TAG)) { + forwardNumCodePoints(1, errorCode); + break; + } + } + // Reverse the digit string. + char *p = digits.data(); + char *q = p + digits.length() - 1; + while(p < q) { + char digit = *p; + *p++ = *q; + *q-- = digit; + } + } + if(U_FAILURE(errorCode)) { return; } + int32_t pos = 0; + do { + // Skip leading zeros. + while(pos < (digits.length() - 1) && digits[pos] == 0) { ++pos; } + // Write a sequence of CEs for at most 254 digits at a time. + int32_t segmentLength = digits.length() - pos; + if(segmentLength > 254) { segmentLength = 254; } + appendNumericSegmentCEs(digits.data() + pos, segmentLength, errorCode); + pos += segmentLength; + } while(U_SUCCESS(errorCode) && pos < digits.length()); +} + +void +CollationIterator::appendNumericSegmentCEs(const char *digits, int32_t length, UErrorCode &errorCode) { + U_ASSERT(1 <= length && length <= 254); + U_ASSERT(length == 1 || digits[0] != 0); + uint32_t numericPrimary = data->numericPrimary; + // Note: We use primary byte values 2..255: digits are not compressible. + if(length <= 7) { + // Very dense encoding for small numbers. + int32_t value = digits[0]; + for(int32_t i = 1; i < length; ++i) { + value = value * 10 + digits[i]; + } + // Primary weight second byte values: + // 74 byte values 2.. 75 for small numbers in two-byte primary weights. + // 40 byte values 76..115 for medium numbers in three-byte primary weights. + // 16 byte values 116..131 for large numbers in four-byte primary weights. + // 124 byte values 132..255 for very large numbers with 4..127 digit pairs. + int32_t firstByte = 2; + int32_t numBytes = 74; + if(value < numBytes) { + // Two-byte primary for 0..73, good for day & month numbers etc. + uint32_t primary = numericPrimary | ((firstByte + value) << 16); + ceBuffer.append(Collation::makeCE(primary), errorCode); + return; + } + value -= numBytes; + firstByte += numBytes; + numBytes = 40; + if(value < numBytes * 254) { + // Three-byte primary for 74..10233=74+40*254-1, good for year numbers and more. + uint32_t primary = numericPrimary | + ((firstByte + value / 254) << 16) | ((2 + value % 254) << 8); + ceBuffer.append(Collation::makeCE(primary), errorCode); + return; + } + value -= numBytes * 254; + firstByte += numBytes; + numBytes = 16; + if(value < numBytes * 254 * 254) { + // Four-byte primary for 10234..1042489=10234+16*254*254-1. + uint32_t primary = numericPrimary | (2 + value % 254); + value /= 254; + primary |= (2 + value % 254) << 8; + value /= 254; + primary |= (firstByte + value % 254) << 16; + ceBuffer.append(Collation::makeCE(primary), errorCode); + return; + } + // original value > 1042489 + } + U_ASSERT(length >= 7); + + // The second primary byte value 132..255 indicates the number of digit pairs (4..127), + // then we generate primary bytes with those pairs. + // Omit trailing 00 pairs. + // Decrement the value for the last pair. + + // Set the exponent. 4 pairs->132, 5 pairs->133, ..., 127 pairs->255. + int32_t numPairs = (length + 1) / 2; + uint32_t primary = numericPrimary | ((132 - 4 + numPairs) << 16); + // Find the length without trailing 00 pairs. + while(digits[length - 1] == 0 && digits[length - 2] == 0) { + length -= 2; + } + // Read the first pair. + uint32_t pair; + int32_t pos; + if(length & 1) { + // Only "half a pair" if we have an odd number of digits. + pair = digits[0]; + pos = 1; + } else { + pair = digits[0] * 10 + digits[1]; + pos = 2; + } + pair = 11 + 2 * pair; + // Add the pairs of digits between pos and length. + int32_t shift = 8; + while(pos < length) { + if(shift == 0) { + // Every three pairs/bytes we need to store a 4-byte-primary CE + // and start with a new CE with the '0' primary lead byte. + primary |= pair; + ceBuffer.append(Collation::makeCE(primary), errorCode); + primary = numericPrimary; + shift = 16; + } else { + primary |= pair << shift; + shift -= 8; + } + pair = 11 + 2 * (digits[pos] * 10 + digits[pos + 1]); + pos += 2; + } + primary |= (pair - 1) << shift; + ceBuffer.append(Collation::makeCE(primary), errorCode); +} + +int64_t +CollationIterator::previousCE(UVector32 &offsets, UErrorCode &errorCode) { + if(ceBuffer.length > 0) { + // Return the previous buffered CE. + return ceBuffer.get(--ceBuffer.length); + } + offsets.removeAllElements(); + int32_t limitOffset = getOffset(); + UChar32 c = previousCodePoint(errorCode); + if(c < 0) { return Collation::NO_CE; } + if(data->isUnsafeBackward(c, isNumeric)) { + return previousCEUnsafe(c, offsets, errorCode); + } + // Simple, safe-backwards iteration: + // Get a CE going backwards, handle prefixes but no contractions. + uint32_t ce32 = data->getCE32(c); + const CollationData *d; + if(ce32 == Collation::FALLBACK_CE32) { + d = data->base; + ce32 = d->getCE32(c); + } else { + d = data; + } + if(Collation::isSimpleOrLongCE32(ce32)) { + return Collation::ceFromCE32(ce32); + } + appendCEsFromCE32(d, c, ce32, false, errorCode); + if(U_SUCCESS(errorCode)) { + if(ceBuffer.length > 1) { + offsets.addElement(getOffset(), errorCode); + // For an expansion, the offset of each non-initial CE is the limit offset, + // consistent with forward iteration. + while(offsets.size() <= ceBuffer.length) { + offsets.addElement(limitOffset, errorCode); + } + } + return ceBuffer.get(--ceBuffer.length); + } else { + return Collation::NO_CE_PRIMARY; + } +} + +int64_t +CollationIterator::previousCEUnsafe(UChar32 c, UVector32 &offsets, UErrorCode &errorCode) { + // We just move through the input counting safe and unsafe code points + // without collecting the unsafe-backward substring into a buffer and + // switching to it. + // This is to keep the logic simple. Otherwise we would have to handle + // prefix matching going before the backward buffer, switching + // to iteration and back, etc. + // In the most important case of iterating over a normal string, + // reading from the string itself is already maximally fast. + // The only drawback there is that after getting the CEs we always + // skip backward to the safe character rather than switching out + // of a backwardBuffer. + // But this should not be the common case for previousCE(), + // and correctness and maintainability are more important than + // complex optimizations. + // Find the first safe character before c. + int32_t numBackward = 1; + while((c = previousCodePoint(errorCode)) >= 0) { + ++numBackward; + if(!data->isUnsafeBackward(c, isNumeric)) { + break; + } + } + // Set the forward iteration limit. + // Note: This counts code points. + // We cannot enforce a limit in the middle of a surrogate pair or similar. + numCpFwd = numBackward; + // Reset the forward iterator. + cesIndex = 0; + U_ASSERT(ceBuffer.length == 0); + // Go forward and collect the CEs. + int32_t offset = getOffset(); + while(numCpFwd > 0) { + // nextCE() normally reads one code point. + // Contraction matching and digit specials read more and check numCpFwd. + --numCpFwd; + // Append one or more CEs to the ceBuffer. + (void)nextCE(errorCode); + U_ASSERT(U_FAILURE(errorCode) || ceBuffer.get(ceBuffer.length - 1) != Collation::NO_CE); + // No need to loop for getting each expansion CE from nextCE(). + cesIndex = ceBuffer.length; + // However, we need to write an offset for each CE. + // This is for CollationElementIterator::getOffset() to return + // intermediate offsets from the unsafe-backwards segment. + U_ASSERT(offsets.size() < ceBuffer.length); + offsets.addElement(offset, errorCode); + // For an expansion, the offset of each non-initial CE is the limit offset, + // consistent with forward iteration. + offset = getOffset(); + while(offsets.size() < ceBuffer.length) { + offsets.addElement(offset, errorCode); + } + } + U_ASSERT(offsets.size() == ceBuffer.length); + // End offset corresponding to just after the unsafe-backwards segment. + offsets.addElement(offset, errorCode); + // Reset the forward iteration limit + // and move backward to before the segment for which we fetched CEs. + numCpFwd = -1; + backwardNumCodePoints(numBackward, errorCode); + // Use the collected CEs and return the last one. + cesIndex = 0; // Avoid cesIndex > ceBuffer.length when that gets decremented. + if(U_SUCCESS(errorCode)) { + return ceBuffer.get(--ceBuffer.length); + } else { + return Collation::NO_CE_PRIMARY; + } +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationiterator.h b/deps/icu-small/source/i18n/collationiterator.h index 93c119c6b858b5..e328166a9d074d 100644 --- a/deps/icu-small/source/i18n/collationiterator.h +++ b/deps/icu-small/source/i18n/collationiterator.h @@ -1,336 +1,336 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationiterator.h -* -* created on: 2010oct27 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONITERATOR_H__ -#define __COLLATIONITERATOR_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "cmemory.h" -#include "collation.h" -#include "collationdata.h" - -U_NAMESPACE_BEGIN - -class SkippedState; -class UCharsTrie; -class UVector32; - -/* Large enough for CEs of most short strings. */ -#define CEBUFFER_INITIAL_CAPACITY 40 - -// Export an explicit template instantiation of the MaybeStackArray that -// is used as a data member of CEBuffer. -// -// 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 - -/** - * Collation element iterator and abstract character iterator. - * - * When a method returns a code point value, it must be in 0..10FFFF, - * except it can be negative as a sentinel value. - */ -class U_I18N_API CollationIterator : public UObject { -private: - class U_I18N_API CEBuffer { - private: - /** Large enough for CEs of most short strings. */ - static const int32_t INITIAL_CAPACITY = CEBUFFER_INITIAL_CAPACITY; - public: - CEBuffer() : length(0) {} - ~CEBuffer(); - - inline void append(int64_t ce, UErrorCode &errorCode) { - if(length < INITIAL_CAPACITY || ensureAppendCapacity(1, errorCode)) { - buffer[length++] = ce; - } - } - - inline void appendUnsafe(int64_t ce) { - buffer[length++] = ce; - } - - UBool ensureAppendCapacity(int32_t appCap, UErrorCode &errorCode); - - inline UBool incLength(UErrorCode &errorCode) { - // Use INITIAL_CAPACITY for a very simple fastpath. - // (Rather than buffer.getCapacity().) - if(length < INITIAL_CAPACITY || ensureAppendCapacity(1, errorCode)) { - ++length; - return true; - } else { - return false; - } - } - - inline int64_t set(int32_t i, int64_t ce) { - return buffer[i] = ce; - } - inline int64_t get(int32_t i) const { return buffer[i]; } - - const int64_t *getCEs() const { return buffer.getAlias(); } - - int32_t length; - - private: - CEBuffer(const CEBuffer &) = delete; - void operator=(const CEBuffer &) = delete; - - MaybeStackArray buffer; - }; - -public: - CollationIterator(const CollationData *d, UBool numeric) - : trie(d->trie), - data(d), - cesIndex(0), - skipped(NULL), - numCpFwd(-1), - isNumeric(numeric) {} - - virtual ~CollationIterator(); - - virtual bool operator==(const CollationIterator &other) const; - inline bool operator!=(const CollationIterator &other) const { - return !operator==(other); - } - - /** - * Resets the iterator state and sets the position to the specified offset. - * Subclasses must implement, and must call the parent class method, - * or CollationIterator::reset(). - */ - virtual void resetToOffset(int32_t newOffset) = 0; - - virtual int32_t getOffset() const = 0; - - /** - * Returns the next collation element. - */ - inline int64_t nextCE(UErrorCode &errorCode) { - if(cesIndex < ceBuffer.length) { - // Return the next buffered CE. - return ceBuffer.get(cesIndex++); - } - // assert cesIndex == ceBuffer.length; - if(!ceBuffer.incLength(errorCode)) { - return Collation::NO_CE; - } - UChar32 c; - uint32_t ce32 = handleNextCE32(c, errorCode); - uint32_t t = ce32 & 0xff; - if(t < Collation::SPECIAL_CE32_LOW_BYTE) { // Forced-inline of isSpecialCE32(ce32). - // Normal CE from the main data. - // Forced-inline of ceFromSimpleCE32(ce32). - return ceBuffer.set(cesIndex++, - ((int64_t)(ce32 & 0xffff0000) << 32) | ((ce32 & 0xff00) << 16) | (t << 8)); - } - const CollationData *d; - // The compiler should be able to optimize the previous and the following - // comparisons of t with the same constant. - if(t == Collation::SPECIAL_CE32_LOW_BYTE) { - if(c < 0) { - return ceBuffer.set(cesIndex++, Collation::NO_CE); - } - d = data->base; - ce32 = d->getCE32(c); - t = ce32 & 0xff; - if(t < Collation::SPECIAL_CE32_LOW_BYTE) { - // Normal CE from the base data. - return ceBuffer.set(cesIndex++, - ((int64_t)(ce32 & 0xffff0000) << 32) | ((ce32 & 0xff00) << 16) | (t << 8)); - } - } else { - d = data; - } - if(t == Collation::LONG_PRIMARY_CE32_LOW_BYTE) { - // Forced-inline of ceFromLongPrimaryCE32(ce32). - return ceBuffer.set(cesIndex++, - ((int64_t)(ce32 - t) << 32) | Collation::COMMON_SEC_AND_TER_CE); - } - return nextCEFromCE32(d, c, ce32, errorCode); - } - - /** - * Fetches all CEs. - * @return getCEsLength() - */ - int32_t fetchCEs(UErrorCode &errorCode); - - /** - * Overwrites the current CE (the last one returned by nextCE()). - */ - void setCurrentCE(int64_t ce) { - // assert cesIndex > 0; - ceBuffer.set(cesIndex - 1, ce); - } - - /** - * Returns the previous collation element. - */ - int64_t previousCE(UVector32 &offsets, UErrorCode &errorCode); - - inline int32_t getCEsLength() const { - return ceBuffer.length; - } - - inline int64_t getCE(int32_t i) const { - return ceBuffer.get(i); - } - - const int64_t *getCEs() const { - return ceBuffer.getCEs(); - } - - void clearCEs() { - cesIndex = ceBuffer.length = 0; - } - - void clearCEsIfNoneRemaining() { - if(cesIndex == ceBuffer.length) { clearCEs(); } - } - - /** - * Returns the next code point (with post-increment). - * Public for identical-level comparison and for testing. - */ - virtual UChar32 nextCodePoint(UErrorCode &errorCode) = 0; - - /** - * Returns the previous code point (with pre-decrement). - * Public for identical-level comparison and for testing. - */ - virtual UChar32 previousCodePoint(UErrorCode &errorCode) = 0; - -protected: - CollationIterator(const CollationIterator &other); - - void reset(); - - /** - * Returns the next code point and its local CE32 value. - * Returns Collation::FALLBACK_CE32 at the end of the text (c<0) - * or when c's CE32 value is to be looked up in the base data (fallback). - * - * The code point is used for fallbacks, context and implicit weights. - * It is ignored when the returned CE32 is not special (e.g., FFFD_CE32). - */ - virtual uint32_t handleNextCE32(UChar32 &c, UErrorCode &errorCode); - - /** - * Called when handleNextCE32() returns a LEAD_SURROGATE_TAG for a lead surrogate code unit. - * Returns the trail surrogate in that case and advances past it, - * if a trail surrogate follows the lead surrogate. - * Otherwise returns any other code unit and does not advance. - */ - virtual UChar handleGetTrailSurrogate(); - - /** - * Called when handleNextCE32() returns with c==0, to see whether it is a NUL terminator. - * (Not needed in Java.) - */ - virtual UBool foundNULTerminator(); - - /** - * @return false if surrogate code points U+D800..U+DFFF - * map to their own implicit primary weights (for UTF-16), - * or true if they map to CE(U+FFFD) (for UTF-8) - */ - virtual UBool forbidSurrogateCodePoints() const; - - virtual void forwardNumCodePoints(int32_t num, UErrorCode &errorCode) = 0; - - virtual void backwardNumCodePoints(int32_t num, UErrorCode &errorCode) = 0; - - /** - * Returns the CE32 from the data trie. - * Normally the same as data->getCE32(), but overridden in the builder. - * Call this only when the faster data->getCE32() cannot be used. - */ - virtual uint32_t getDataCE32(UChar32 c) const; - - virtual uint32_t getCE32FromBuilderData(uint32_t ce32, UErrorCode &errorCode); - - void appendCEsFromCE32(const CollationData *d, UChar32 c, uint32_t ce32, - UBool forward, UErrorCode &errorCode); - - // Main lookup trie of the data object. - const UTrie2 *trie; - const CollationData *data; - -private: - int64_t nextCEFromCE32(const CollationData *d, UChar32 c, uint32_t ce32, - UErrorCode &errorCode); - - uint32_t getCE32FromPrefix(const CollationData *d, uint32_t ce32, - UErrorCode &errorCode); - - UChar32 nextSkippedCodePoint(UErrorCode &errorCode); - - void backwardNumSkipped(int32_t n, UErrorCode &errorCode); - - uint32_t nextCE32FromContraction( - const CollationData *d, uint32_t contractionCE32, - const UChar *p, uint32_t ce32, UChar32 c, - UErrorCode &errorCode); - - uint32_t nextCE32FromDiscontiguousContraction( - const CollationData *d, UCharsTrie &suffixes, uint32_t ce32, - int32_t lookAhead, UChar32 c, - UErrorCode &errorCode); - - /** - * Returns the previous CE when data->isUnsafeBackward(c, isNumeric). - */ - int64_t previousCEUnsafe(UChar32 c, UVector32 &offsets, UErrorCode &errorCode); - - /** - * Turns a string of digits (bytes 0..9) - * into a sequence of CEs that will sort in numeric order. - * - * Starts from this ce32's digit value and consumes the following/preceding digits. - * The digits string must not be empty and must not have leading zeros. - */ - void appendNumericCEs(uint32_t ce32, UBool forward, UErrorCode &errorCode); - - /** - * Turns 1..254 digits into a sequence of CEs. - * Called by appendNumericCEs() for each segment of at most 254 digits. - */ - void appendNumericSegmentCEs(const char *digits, int32_t length, UErrorCode &errorCode); - - CEBuffer ceBuffer; - int32_t cesIndex; - - SkippedState *skipped; - - // Number of code points to read forward, or -1. - // Used as a forward iteration limit in previousCEUnsafe(). - int32_t numCpFwd; - // Numeric collation (CollationSettings::NUMERIC). - UBool isNumeric; -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONITERATOR_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationiterator.h +* +* created on: 2010oct27 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONITERATOR_H__ +#define __COLLATIONITERATOR_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "cmemory.h" +#include "collation.h" +#include "collationdata.h" + +U_NAMESPACE_BEGIN + +class SkippedState; +class UCharsTrie; +class UVector32; + +/* Large enough for CEs of most short strings. */ +#define CEBUFFER_INITIAL_CAPACITY 40 + +// Export an explicit template instantiation of the MaybeStackArray that +// is used as a data member of CEBuffer. +// +// 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 + +/** + * Collation element iterator and abstract character iterator. + * + * When a method returns a code point value, it must be in 0..10FFFF, + * except it can be negative as a sentinel value. + */ +class U_I18N_API CollationIterator : public UObject { +private: + class U_I18N_API CEBuffer { + private: + /** Large enough for CEs of most short strings. */ + static const int32_t INITIAL_CAPACITY = CEBUFFER_INITIAL_CAPACITY; + public: + CEBuffer() : length(0) {} + ~CEBuffer(); + + inline void append(int64_t ce, UErrorCode &errorCode) { + if(length < INITIAL_CAPACITY || ensureAppendCapacity(1, errorCode)) { + buffer[length++] = ce; + } + } + + inline void appendUnsafe(int64_t ce) { + buffer[length++] = ce; + } + + UBool ensureAppendCapacity(int32_t appCap, UErrorCode &errorCode); + + inline UBool incLength(UErrorCode &errorCode) { + // Use INITIAL_CAPACITY for a very simple fastpath. + // (Rather than buffer.getCapacity().) + if(length < INITIAL_CAPACITY || ensureAppendCapacity(1, errorCode)) { + ++length; + return true; + } else { + return false; + } + } + + inline int64_t set(int32_t i, int64_t ce) { + return buffer[i] = ce; + } + inline int64_t get(int32_t i) const { return buffer[i]; } + + const int64_t *getCEs() const { return buffer.getAlias(); } + + int32_t length; + + private: + CEBuffer(const CEBuffer &) = delete; + void operator=(const CEBuffer &) = delete; + + MaybeStackArray buffer; + }; + +public: + CollationIterator(const CollationData *d, UBool numeric) + : trie(d->trie), + data(d), + cesIndex(0), + skipped(nullptr), + numCpFwd(-1), + isNumeric(numeric) {} + + virtual ~CollationIterator(); + + virtual bool operator==(const CollationIterator &other) const; + inline bool operator!=(const CollationIterator &other) const { + return !operator==(other); + } + + /** + * Resets the iterator state and sets the position to the specified offset. + * Subclasses must implement, and must call the parent class method, + * or CollationIterator::reset(). + */ + virtual void resetToOffset(int32_t newOffset) = 0; + + virtual int32_t getOffset() const = 0; + + /** + * Returns the next collation element. + */ + inline int64_t nextCE(UErrorCode &errorCode) { + if(cesIndex < ceBuffer.length) { + // Return the next buffered CE. + return ceBuffer.get(cesIndex++); + } + // assert cesIndex == ceBuffer.length; + if(!ceBuffer.incLength(errorCode)) { + return Collation::NO_CE; + } + UChar32 c; + uint32_t ce32 = handleNextCE32(c, errorCode); + uint32_t t = ce32 & 0xff; + if(t < Collation::SPECIAL_CE32_LOW_BYTE) { // Forced-inline of isSpecialCE32(ce32). + // Normal CE from the main data. + // Forced-inline of ceFromSimpleCE32(ce32). + return ceBuffer.set(cesIndex++, + ((int64_t)(ce32 & 0xffff0000) << 32) | ((ce32 & 0xff00) << 16) | (t << 8)); + } + const CollationData *d; + // The compiler should be able to optimize the previous and the following + // comparisons of t with the same constant. + if(t == Collation::SPECIAL_CE32_LOW_BYTE) { + if(c < 0) { + return ceBuffer.set(cesIndex++, Collation::NO_CE); + } + d = data->base; + ce32 = d->getCE32(c); + t = ce32 & 0xff; + if(t < Collation::SPECIAL_CE32_LOW_BYTE) { + // Normal CE from the base data. + return ceBuffer.set(cesIndex++, + ((int64_t)(ce32 & 0xffff0000) << 32) | ((ce32 & 0xff00) << 16) | (t << 8)); + } + } else { + d = data; + } + if(t == Collation::LONG_PRIMARY_CE32_LOW_BYTE) { + // Forced-inline of ceFromLongPrimaryCE32(ce32). + return ceBuffer.set(cesIndex++, + ((int64_t)(ce32 - t) << 32) | Collation::COMMON_SEC_AND_TER_CE); + } + return nextCEFromCE32(d, c, ce32, errorCode); + } + + /** + * Fetches all CEs. + * @return getCEsLength() + */ + int32_t fetchCEs(UErrorCode &errorCode); + + /** + * Overwrites the current CE (the last one returned by nextCE()). + */ + void setCurrentCE(int64_t ce) { + // assert cesIndex > 0; + ceBuffer.set(cesIndex - 1, ce); + } + + /** + * Returns the previous collation element. + */ + int64_t previousCE(UVector32 &offsets, UErrorCode &errorCode); + + inline int32_t getCEsLength() const { + return ceBuffer.length; + } + + inline int64_t getCE(int32_t i) const { + return ceBuffer.get(i); + } + + const int64_t *getCEs() const { + return ceBuffer.getCEs(); + } + + void clearCEs() { + cesIndex = ceBuffer.length = 0; + } + + void clearCEsIfNoneRemaining() { + if(cesIndex == ceBuffer.length) { clearCEs(); } + } + + /** + * Returns the next code point (with post-increment). + * Public for identical-level comparison and for testing. + */ + virtual UChar32 nextCodePoint(UErrorCode &errorCode) = 0; + + /** + * Returns the previous code point (with pre-decrement). + * Public for identical-level comparison and for testing. + */ + virtual UChar32 previousCodePoint(UErrorCode &errorCode) = 0; + +protected: + CollationIterator(const CollationIterator &other); + + void reset(); + + /** + * Returns the next code point and its local CE32 value. + * Returns Collation::FALLBACK_CE32 at the end of the text (c<0) + * or when c's CE32 value is to be looked up in the base data (fallback). + * + * The code point is used for fallbacks, context and implicit weights. + * It is ignored when the returned CE32 is not special (e.g., FFFD_CE32). + */ + virtual uint32_t handleNextCE32(UChar32 &c, UErrorCode &errorCode); + + /** + * Called when handleNextCE32() returns a LEAD_SURROGATE_TAG for a lead surrogate code unit. + * Returns the trail surrogate in that case and advances past it, + * if a trail surrogate follows the lead surrogate. + * Otherwise returns any other code unit and does not advance. + */ + virtual char16_t handleGetTrailSurrogate(); + + /** + * Called when handleNextCE32() returns with c==0, to see whether it is a NUL terminator. + * (Not needed in Java.) + */ + virtual UBool foundNULTerminator(); + + /** + * @return false if surrogate code points U+D800..U+DFFF + * map to their own implicit primary weights (for UTF-16), + * or true if they map to CE(U+FFFD) (for UTF-8) + */ + virtual UBool forbidSurrogateCodePoints() const; + + virtual void forwardNumCodePoints(int32_t num, UErrorCode &errorCode) = 0; + + virtual void backwardNumCodePoints(int32_t num, UErrorCode &errorCode) = 0; + + /** + * Returns the CE32 from the data trie. + * Normally the same as data->getCE32(), but overridden in the builder. + * Call this only when the faster data->getCE32() cannot be used. + */ + virtual uint32_t getDataCE32(UChar32 c) const; + + virtual uint32_t getCE32FromBuilderData(uint32_t ce32, UErrorCode &errorCode); + + void appendCEsFromCE32(const CollationData *d, UChar32 c, uint32_t ce32, + UBool forward, UErrorCode &errorCode); + + // Main lookup trie of the data object. + const UTrie2 *trie; + const CollationData *data; + +private: + int64_t nextCEFromCE32(const CollationData *d, UChar32 c, uint32_t ce32, + UErrorCode &errorCode); + + uint32_t getCE32FromPrefix(const CollationData *d, uint32_t ce32, + UErrorCode &errorCode); + + UChar32 nextSkippedCodePoint(UErrorCode &errorCode); + + void backwardNumSkipped(int32_t n, UErrorCode &errorCode); + + uint32_t nextCE32FromContraction( + const CollationData *d, uint32_t contractionCE32, + const char16_t *p, uint32_t ce32, UChar32 c, + UErrorCode &errorCode); + + uint32_t nextCE32FromDiscontiguousContraction( + const CollationData *d, UCharsTrie &suffixes, uint32_t ce32, + int32_t lookAhead, UChar32 c, + UErrorCode &errorCode); + + /** + * Returns the previous CE when data->isUnsafeBackward(c, isNumeric). + */ + int64_t previousCEUnsafe(UChar32 c, UVector32 &offsets, UErrorCode &errorCode); + + /** + * Turns a string of digits (bytes 0..9) + * into a sequence of CEs that will sort in numeric order. + * + * Starts from this ce32's digit value and consumes the following/preceding digits. + * The digits string must not be empty and must not have leading zeros. + */ + void appendNumericCEs(uint32_t ce32, UBool forward, UErrorCode &errorCode); + + /** + * Turns 1..254 digits into a sequence of CEs. + * Called by appendNumericCEs() for each segment of at most 254 digits. + */ + void appendNumericSegmentCEs(const char *digits, int32_t length, UErrorCode &errorCode); + + CEBuffer ceBuffer; + int32_t cesIndex; + + SkippedState *skipped; + + // Number of code points to read forward, or -1. + // Used as a forward iteration limit in previousCEUnsafe(). + int32_t numCpFwd; + // Numeric collation (CollationSettings::NUMERIC). + UBool isNumeric; +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONITERATOR_H__ diff --git a/deps/icu-small/source/i18n/collationkeys.cpp b/deps/icu-small/source/i18n/collationkeys.cpp index c7e0de618de487..0ce5972d14f6dd 100644 --- a/deps/icu-small/source/i18n/collationkeys.cpp +++ b/deps/icu-small/source/i18n/collationkeys.cpp @@ -1,673 +1,673 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2012-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationkeys.cpp -* -* created on: 2012sep02 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/bytestream.h" -#include "collation.h" -#include "collationiterator.h" -#include "collationkeys.h" -#include "collationsettings.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - -SortKeyByteSink::~SortKeyByteSink() {} - -void -SortKeyByteSink::Append(const char *bytes, int32_t n) { - if (n <= 0 || bytes == NULL) { - return; - } - if (ignore_ > 0) { - int32_t ignoreRest = ignore_ - n; - if (ignoreRest >= 0) { - ignore_ = ignoreRest; - return; - } else { - bytes += ignore_; - n = -ignoreRest; - ignore_ = 0; - } - } - int32_t length = appended_; - appended_ += n; - if ((buffer_ + length) == bytes) { - return; // the caller used GetAppendBuffer() and wrote the bytes already - } - int32_t available = capacity_ - length; - if (n <= available) { - uprv_memcpy(buffer_ + length, bytes, n); - } else { - AppendBeyondCapacity(bytes, n, length); - } -} - -char * -SortKeyByteSink::GetAppendBuffer(int32_t min_capacity, - int32_t desired_capacity_hint, - char *scratch, - int32_t scratch_capacity, - int32_t *result_capacity) { - if (min_capacity < 1 || scratch_capacity < min_capacity) { - *result_capacity = 0; - return NULL; - } - if (ignore_ > 0) { - // Do not write ignored bytes right at the end of the buffer. - *result_capacity = scratch_capacity; - return scratch; - } - int32_t available = capacity_ - appended_; - if (available >= min_capacity) { - *result_capacity = available; - return buffer_ + appended_; - } else if (Resize(desired_capacity_hint, appended_)) { - *result_capacity = capacity_ - appended_; - return buffer_ + appended_; - } else { - *result_capacity = scratch_capacity; - return scratch; - } -} - -namespace { - -/** - * uint8_t byte buffer, similar to CharString but simpler. - */ -class SortKeyLevel : public UMemory { -public: - SortKeyLevel() : len(0), ok(true) {} - ~SortKeyLevel() {} - - /** @return false if memory allocation failed */ - UBool isOk() const { return ok; } - UBool isEmpty() const { return len == 0; } - int32_t length() const { return len; } - const uint8_t *data() const { return buffer.getAlias(); } - uint8_t operator[](int32_t index) const { return buffer[index]; } - - uint8_t *data() { return buffer.getAlias(); } - - void appendByte(uint32_t b); - void appendWeight16(uint32_t w); - void appendWeight32(uint32_t w); - void appendReverseWeight16(uint32_t w); - - /** Appends all but the last byte to the sink. The last byte should be the 01 terminator. */ - void appendTo(ByteSink &sink) const { - U_ASSERT(len > 0 && buffer[len - 1] == 1); - sink.Append(reinterpret_cast(buffer.getAlias()), len - 1); - } - -private: - MaybeStackArray buffer; - int32_t len; - UBool ok; - - UBool ensureCapacity(int32_t appendCapacity); - - SortKeyLevel(const SortKeyLevel &other); // forbid copying of this class - SortKeyLevel &operator=(const SortKeyLevel &other); // forbid copying of this class -}; - -void SortKeyLevel::appendByte(uint32_t b) { - if(len < buffer.getCapacity() || ensureCapacity(1)) { - buffer[len++] = (uint8_t)b; - } -} - -void -SortKeyLevel::appendWeight16(uint32_t w) { - U_ASSERT((w & 0xffff) != 0); - uint8_t b0 = (uint8_t)(w >> 8); - uint8_t b1 = (uint8_t)w; - int32_t appendLength = (b1 == 0) ? 1 : 2; - if((len + appendLength) <= buffer.getCapacity() || ensureCapacity(appendLength)) { - buffer[len++] = b0; - if(b1 != 0) { - buffer[len++] = b1; - } - } -} - -void -SortKeyLevel::appendWeight32(uint32_t w) { - U_ASSERT(w != 0); - uint8_t bytes[4] = { (uint8_t)(w >> 24), (uint8_t)(w >> 16), (uint8_t)(w >> 8), (uint8_t)w }; - int32_t appendLength = (bytes[1] == 0) ? 1 : (bytes[2] == 0) ? 2 : (bytes[3] == 0) ? 3 : 4; - if((len + appendLength) <= buffer.getCapacity() || ensureCapacity(appendLength)) { - buffer[len++] = bytes[0]; - if(bytes[1] != 0) { - buffer[len++] = bytes[1]; - if(bytes[2] != 0) { - buffer[len++] = bytes[2]; - if(bytes[3] != 0) { - buffer[len++] = bytes[3]; - } - } - } - } -} - -void -SortKeyLevel::appendReverseWeight16(uint32_t w) { - U_ASSERT((w & 0xffff) != 0); - uint8_t b0 = (uint8_t)(w >> 8); - uint8_t b1 = (uint8_t)w; - int32_t appendLength = (b1 == 0) ? 1 : 2; - if((len + appendLength) <= buffer.getCapacity() || ensureCapacity(appendLength)) { - if(b1 == 0) { - buffer[len++] = b0; - } else { - buffer[len] = b1; - buffer[len + 1] = b0; - len += 2; - } - } -} - -UBool SortKeyLevel::ensureCapacity(int32_t appendCapacity) { - if(!ok) { - return false; - } - int32_t newCapacity = 2 * buffer.getCapacity(); - int32_t altCapacity = len + 2 * appendCapacity; - if (newCapacity < altCapacity) { - newCapacity = altCapacity; - } - if (newCapacity < 200) { - newCapacity = 200; - } - if(buffer.resize(newCapacity, len)==NULL) { - return ok = false; - } - return true; -} - -} // namespace - -CollationKeys::LevelCallback::~LevelCallback() {} - -UBool -CollationKeys::LevelCallback::needToWrite(Collation::Level /*level*/) { return true; } - -/** - * Map from collation strength (UColAttributeValue) - * to a mask of Collation::Level bits up to that strength, - * excluding the CASE_LEVEL which is independent of the strength, - * and excluding IDENTICAL_LEVEL which this function does not write. - */ -static const uint32_t levelMasks[UCOL_STRENGTH_LIMIT] = { - 2, // UCOL_PRIMARY -> PRIMARY_LEVEL - 6, // UCOL_SECONDARY -> up to SECONDARY_LEVEL - 0x16, // UCOL_TERTIARY -> up to TERTIARY_LEVEL - 0x36, // UCOL_QUATERNARY -> up to QUATERNARY_LEVEL - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, - 0x36 // UCOL_IDENTICAL -> up to QUATERNARY_LEVEL -}; - -void -CollationKeys::writeSortKeyUpToQuaternary(CollationIterator &iter, - const UBool *compressibleBytes, - const CollationSettings &settings, - SortKeyByteSink &sink, - Collation::Level minLevel, LevelCallback &callback, - UBool preflight, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - - int32_t options = settings.options; - // Set of levels to process and write. - uint32_t levels = levelMasks[CollationSettings::getStrength(options)]; - if((options & CollationSettings::CASE_LEVEL) != 0) { - levels |= Collation::CASE_LEVEL_FLAG; - } - // Minus the levels below minLevel. - levels &= ~(((uint32_t)1 << minLevel) - 1); - if(levels == 0) { return; } - - uint32_t variableTop; - if((options & CollationSettings::ALTERNATE_MASK) == 0) { - variableTop = 0; - } else { - // +1 so that we can use "<" and primary ignorables test out early. - variableTop = settings.variableTop + 1; - } - - uint32_t tertiaryMask = CollationSettings::getTertiaryMask(options); - - SortKeyLevel cases; - SortKeyLevel secondaries; - SortKeyLevel tertiaries; - SortKeyLevel quaternaries; - - uint32_t prevReorderedPrimary = 0; // 0==no compression - int32_t commonCases = 0; - int32_t commonSecondaries = 0; - int32_t commonTertiaries = 0; - int32_t commonQuaternaries = 0; - - uint32_t prevSecondary = 0; - int32_t secSegmentStart = 0; - - for(;;) { - // No need to keep all CEs in the buffer when we write a sort key. - iter.clearCEsIfNoneRemaining(); - int64_t ce = iter.nextCE(errorCode); - uint32_t p = (uint32_t)(ce >> 32); - if(p < variableTop && p > Collation::MERGE_SEPARATOR_PRIMARY) { - // Variable CE, shift it to quaternary level. - // Ignore all following primary ignorables, and shift further variable CEs. - if(commonQuaternaries != 0) { - --commonQuaternaries; - while(commonQuaternaries >= QUAT_COMMON_MAX_COUNT) { - quaternaries.appendByte(QUAT_COMMON_MIDDLE); - commonQuaternaries -= QUAT_COMMON_MAX_COUNT; - } - // Shifted primary weights are lower than the common weight. - quaternaries.appendByte(QUAT_COMMON_LOW + commonQuaternaries); - commonQuaternaries = 0; - } - do { - if((levels & Collation::QUATERNARY_LEVEL_FLAG) != 0) { - if(settings.hasReordering()) { - p = settings.reorder(p); - } - if((p >> 24) >= QUAT_SHIFTED_LIMIT_BYTE) { - // Prevent shifted primary lead bytes from - // overlapping with the common compression range. - quaternaries.appendByte(QUAT_SHIFTED_LIMIT_BYTE); - } - quaternaries.appendWeight32(p); - } - do { - ce = iter.nextCE(errorCode); - p = (uint32_t)(ce >> 32); - } while(p == 0); - } while(p < variableTop && p > Collation::MERGE_SEPARATOR_PRIMARY); - } - // ce could be primary ignorable, or NO_CE, or the merge separator, - // or a regular primary CE, but it is not variable. - // If ce==NO_CE, then write nothing for the primary level but - // terminate compression on all levels and then exit the loop. - if(p > Collation::NO_CE_PRIMARY && (levels & Collation::PRIMARY_LEVEL_FLAG) != 0) { - // Test the un-reordered primary for compressibility. - UBool isCompressible = compressibleBytes[p >> 24]; - if(settings.hasReordering()) { - p = settings.reorder(p); - } - uint32_t p1 = p >> 24; - if(!isCompressible || p1 != (prevReorderedPrimary >> 24)) { - if(prevReorderedPrimary != 0) { - if(p < prevReorderedPrimary) { - // No primary compression terminator - // at the end of the level or merged segment. - if(p1 > Collation::MERGE_SEPARATOR_BYTE) { - sink.Append(Collation::PRIMARY_COMPRESSION_LOW_BYTE); - } - } else { - sink.Append(Collation::PRIMARY_COMPRESSION_HIGH_BYTE); - } - } - sink.Append(p1); - if(isCompressible) { - prevReorderedPrimary = p; - } else { - prevReorderedPrimary = 0; - } - } - char p2 = (char)(p >> 16); - if(p2 != 0) { - char buffer[3] = { p2, (char)(p >> 8), (char)p }; - sink.Append(buffer, (buffer[1] == 0) ? 1 : (buffer[2] == 0) ? 2 : 3); - } - // Optimization for internalNextSortKeyPart(): - // When the primary level overflows we can stop because we need not - // calculate (preflight) the whole sort key length. - if(!preflight && sink.Overflowed()) { - if(U_SUCCESS(errorCode) && !sink.IsOk()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - } - return; - } - } - - uint32_t lower32 = (uint32_t)ce; - if(lower32 == 0) { continue; } // completely ignorable, no secondary/case/tertiary/quaternary - - if((levels & Collation::SECONDARY_LEVEL_FLAG) != 0) { - uint32_t s = lower32 >> 16; - if(s == 0) { - // secondary ignorable - } else if(s == Collation::COMMON_WEIGHT16 && - ((options & CollationSettings::BACKWARD_SECONDARY) == 0 || - p != Collation::MERGE_SEPARATOR_PRIMARY)) { - // s is a common secondary weight, and - // backwards-secondary is off or the ce is not the merge separator. - ++commonSecondaries; - } else if((options & CollationSettings::BACKWARD_SECONDARY) == 0) { - if(commonSecondaries != 0) { - --commonSecondaries; - while(commonSecondaries >= SEC_COMMON_MAX_COUNT) { - secondaries.appendByte(SEC_COMMON_MIDDLE); - commonSecondaries -= SEC_COMMON_MAX_COUNT; - } - uint32_t b; - if(s < Collation::COMMON_WEIGHT16) { - b = SEC_COMMON_LOW + commonSecondaries; - } else { - b = SEC_COMMON_HIGH - commonSecondaries; - } - secondaries.appendByte(b); - commonSecondaries = 0; - } - secondaries.appendWeight16(s); - } else { - if(commonSecondaries != 0) { - --commonSecondaries; - // Append reverse weights. The level will be re-reversed later. - int32_t remainder = commonSecondaries % SEC_COMMON_MAX_COUNT; - uint32_t b; - if(prevSecondary < Collation::COMMON_WEIGHT16) { - b = SEC_COMMON_LOW + remainder; - } else { - b = SEC_COMMON_HIGH - remainder; - } - secondaries.appendByte(b); - commonSecondaries -= remainder; - // commonSecondaries is now a multiple of SEC_COMMON_MAX_COUNT. - while(commonSecondaries > 0) { // same as >= SEC_COMMON_MAX_COUNT - secondaries.appendByte(SEC_COMMON_MIDDLE); - commonSecondaries -= SEC_COMMON_MAX_COUNT; - } - // commonSecondaries == 0 - } - if(0 < p && p <= Collation::MERGE_SEPARATOR_PRIMARY) { - // The backwards secondary level compares secondary weights backwards - // within segments separated by the merge separator (U+FFFE). - uint8_t *secs = secondaries.data(); - int32_t last = secondaries.length() - 1; - if(secSegmentStart < last) { - uint8_t *q = secs + secSegmentStart; - uint8_t *r = secs + last; - do { - uint8_t b = *q; - *q++ = *r; - *r-- = b; - } while(q < r); - } - secondaries.appendByte(p == Collation::NO_CE_PRIMARY ? - Collation::LEVEL_SEPARATOR_BYTE : Collation::MERGE_SEPARATOR_BYTE); - prevSecondary = 0; - secSegmentStart = secondaries.length(); - } else { - secondaries.appendReverseWeight16(s); - prevSecondary = s; - } - } - } - - if((levels & Collation::CASE_LEVEL_FLAG) != 0) { - if((CollationSettings::getStrength(options) == UCOL_PRIMARY) ? - p == 0 : lower32 <= 0xffff) { - // Primary+caseLevel: Ignore case level weights of primary ignorables. - // Otherwise: Ignore case level weights of secondary ignorables. - // For details see the comments in the CollationCompare class. - } else { - uint32_t c = (lower32 >> 8) & 0xff; // case bits & tertiary lead byte - U_ASSERT((c & 0xc0) != 0xc0); - if((c & 0xc0) == 0 && c > Collation::LEVEL_SEPARATOR_BYTE) { - ++commonCases; - } else { - if((options & CollationSettings::UPPER_FIRST) == 0) { - // lowerFirst: Compress common weights to nibbles 1..7..13, mixed=14, upper=15. - // If there are only common (=lowest) weights in the whole level, - // then we need not write anything. - // Level length differences are handled already on the next-higher level. - if(commonCases != 0 && - (c > Collation::LEVEL_SEPARATOR_BYTE || !cases.isEmpty())) { - --commonCases; - while(commonCases >= CASE_LOWER_FIRST_COMMON_MAX_COUNT) { - cases.appendByte(CASE_LOWER_FIRST_COMMON_MIDDLE << 4); - commonCases -= CASE_LOWER_FIRST_COMMON_MAX_COUNT; - } - uint32_t b; - if(c <= Collation::LEVEL_SEPARATOR_BYTE) { - b = CASE_LOWER_FIRST_COMMON_LOW + commonCases; - } else { - b = CASE_LOWER_FIRST_COMMON_HIGH - commonCases; - } - cases.appendByte(b << 4); - commonCases = 0; - } - if(c > Collation::LEVEL_SEPARATOR_BYTE) { - c = (CASE_LOWER_FIRST_COMMON_HIGH + (c >> 6)) << 4; // 14 or 15 - } - } else { - // upperFirst: Compress common weights to nibbles 3..15, mixed=2, upper=1. - // The compressed common case weights only go up from the "low" value - // because with upperFirst the common weight is the highest one. - if(commonCases != 0) { - --commonCases; - while(commonCases >= CASE_UPPER_FIRST_COMMON_MAX_COUNT) { - cases.appendByte(CASE_UPPER_FIRST_COMMON_LOW << 4); - commonCases -= CASE_UPPER_FIRST_COMMON_MAX_COUNT; - } - cases.appendByte((CASE_UPPER_FIRST_COMMON_LOW + commonCases) << 4); - commonCases = 0; - } - if(c > Collation::LEVEL_SEPARATOR_BYTE) { - c = (CASE_UPPER_FIRST_COMMON_LOW - (c >> 6)) << 4; // 2 or 1 - } - } - // c is a separator byte 01, - // or a left-shifted nibble 0x10, 0x20, ... 0xf0. - cases.appendByte(c); - } - } - } - - if((levels & Collation::TERTIARY_LEVEL_FLAG) != 0) { - uint32_t t = lower32 & tertiaryMask; - U_ASSERT((lower32 & 0xc000) != 0xc000); - if(t == Collation::COMMON_WEIGHT16) { - ++commonTertiaries; - } else if((tertiaryMask & 0x8000) == 0) { - // Tertiary weights without case bits. - // Move lead bytes 06..3F to C6..FF for a large common-weight range. - if(commonTertiaries != 0) { - --commonTertiaries; - while(commonTertiaries >= TER_ONLY_COMMON_MAX_COUNT) { - tertiaries.appendByte(TER_ONLY_COMMON_MIDDLE); - commonTertiaries -= TER_ONLY_COMMON_MAX_COUNT; - } - uint32_t b; - if(t < Collation::COMMON_WEIGHT16) { - b = TER_ONLY_COMMON_LOW + commonTertiaries; - } else { - b = TER_ONLY_COMMON_HIGH - commonTertiaries; - } - tertiaries.appendByte(b); - commonTertiaries = 0; - } - if(t > Collation::COMMON_WEIGHT16) { t += 0xc000; } - tertiaries.appendWeight16(t); - } else if((options & CollationSettings::UPPER_FIRST) == 0) { - // Tertiary weights with caseFirst=lowerFirst. - // Move lead bytes 06..BF to 46..FF for the common-weight range. - if(commonTertiaries != 0) { - --commonTertiaries; - while(commonTertiaries >= TER_LOWER_FIRST_COMMON_MAX_COUNT) { - tertiaries.appendByte(TER_LOWER_FIRST_COMMON_MIDDLE); - commonTertiaries -= TER_LOWER_FIRST_COMMON_MAX_COUNT; - } - uint32_t b; - if(t < Collation::COMMON_WEIGHT16) { - b = TER_LOWER_FIRST_COMMON_LOW + commonTertiaries; - } else { - b = TER_LOWER_FIRST_COMMON_HIGH - commonTertiaries; - } - tertiaries.appendByte(b); - commonTertiaries = 0; - } - if(t > Collation::COMMON_WEIGHT16) { t += 0x4000; } - tertiaries.appendWeight16(t); - } else { - // Tertiary weights with caseFirst=upperFirst. - // Do not change the artificial uppercase weight of a tertiary CE (0.0.ut), - // to keep tertiary CEs well-formed. - // Their case+tertiary weights must be greater than those of - // primary and secondary CEs. - // - // Separator 01 -> 01 (unchanged) - // Lowercase 02..04 -> 82..84 (includes uncased) - // Common weight 05 -> 85..C5 (common-weight compression range) - // Lowercase 06..3F -> C6..FF - // Mixed case 42..7F -> 42..7F - // Uppercase 82..BF -> 02..3F - // Tertiary CE 86..BF -> C6..FF - if(t <= Collation::NO_CE_WEIGHT16) { - // Keep separators unchanged. - } else if(lower32 > 0xffff) { - // Invert case bits of primary & secondary CEs. - t ^= 0xc000; - if(t < (TER_UPPER_FIRST_COMMON_HIGH << 8)) { - t -= 0x4000; - } - } else { - // Keep uppercase bits of tertiary CEs. - U_ASSERT(0x8600 <= t && t <= 0xbfff); - t += 0x4000; - } - if(commonTertiaries != 0) { - --commonTertiaries; - while(commonTertiaries >= TER_UPPER_FIRST_COMMON_MAX_COUNT) { - tertiaries.appendByte(TER_UPPER_FIRST_COMMON_MIDDLE); - commonTertiaries -= TER_UPPER_FIRST_COMMON_MAX_COUNT; - } - uint32_t b; - if(t < (TER_UPPER_FIRST_COMMON_LOW << 8)) { - b = TER_UPPER_FIRST_COMMON_LOW + commonTertiaries; - } else { - b = TER_UPPER_FIRST_COMMON_HIGH - commonTertiaries; - } - tertiaries.appendByte(b); - commonTertiaries = 0; - } - tertiaries.appendWeight16(t); - } - } - - if((levels & Collation::QUATERNARY_LEVEL_FLAG) != 0) { - uint32_t q = lower32 & 0xffff; - if((q & 0xc0) == 0 && q > Collation::NO_CE_WEIGHT16) { - ++commonQuaternaries; - } else if(q == Collation::NO_CE_WEIGHT16 && - (options & CollationSettings::ALTERNATE_MASK) == 0 && - quaternaries.isEmpty()) { - // If alternate=non-ignorable and there are only common quaternary weights, - // then we need not write anything. - // The only weights greater than the merge separator and less than the common weight - // are shifted primary weights, which are not generated for alternate=non-ignorable. - // There are also exactly as many quaternary weights as tertiary weights, - // so level length differences are handled already on tertiary level. - // Any above-common quaternary weight will compare greater regardless. - quaternaries.appendByte(Collation::LEVEL_SEPARATOR_BYTE); - } else { - if(q == Collation::NO_CE_WEIGHT16) { - q = Collation::LEVEL_SEPARATOR_BYTE; - } else { - q = 0xfc + ((q >> 6) & 3); - } - if(commonQuaternaries != 0) { - --commonQuaternaries; - while(commonQuaternaries >= QUAT_COMMON_MAX_COUNT) { - quaternaries.appendByte(QUAT_COMMON_MIDDLE); - commonQuaternaries -= QUAT_COMMON_MAX_COUNT; - } - uint32_t b; - if(q < QUAT_COMMON_LOW) { - b = QUAT_COMMON_LOW + commonQuaternaries; - } else { - b = QUAT_COMMON_HIGH - commonQuaternaries; - } - quaternaries.appendByte(b); - commonQuaternaries = 0; - } - quaternaries.appendByte(q); - } - } - - if((lower32 >> 24) == Collation::LEVEL_SEPARATOR_BYTE) { break; } // ce == NO_CE - } - - if(U_FAILURE(errorCode)) { return; } - - // Append the beyond-primary levels. - UBool ok = true; - if((levels & Collation::SECONDARY_LEVEL_FLAG) != 0) { - if(!callback.needToWrite(Collation::SECONDARY_LEVEL)) { return; } - ok &= secondaries.isOk(); - sink.Append(Collation::LEVEL_SEPARATOR_BYTE); - secondaries.appendTo(sink); - } - - if((levels & Collation::CASE_LEVEL_FLAG) != 0) { - if(!callback.needToWrite(Collation::CASE_LEVEL)) { return; } - ok &= cases.isOk(); - sink.Append(Collation::LEVEL_SEPARATOR_BYTE); - // Write pairs of nibbles as bytes, except separator bytes as themselves. - int32_t length = cases.length() - 1; // Ignore the trailing NO_CE. - uint8_t b = 0; - for(int32_t i = 0; i < length; ++i) { - uint8_t c = (uint8_t)cases[i]; - U_ASSERT((c & 0xf) == 0 && c != 0); - if(b == 0) { - b = c; - } else { - sink.Append(b | (c >> 4)); - b = 0; - } - } - if(b != 0) { - sink.Append(b); - } - } - - if((levels & Collation::TERTIARY_LEVEL_FLAG) != 0) { - if(!callback.needToWrite(Collation::TERTIARY_LEVEL)) { return; } - ok &= tertiaries.isOk(); - sink.Append(Collation::LEVEL_SEPARATOR_BYTE); - tertiaries.appendTo(sink); - } - - if((levels & Collation::QUATERNARY_LEVEL_FLAG) != 0) { - if(!callback.needToWrite(Collation::QUATERNARY_LEVEL)) { return; } - ok &= quaternaries.isOk(); - sink.Append(Collation::LEVEL_SEPARATOR_BYTE); - quaternaries.appendTo(sink); - } - - if(!ok || !sink.IsOk()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - } -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2012-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationkeys.cpp +* +* created on: 2012sep02 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/bytestream.h" +#include "collation.h" +#include "collationiterator.h" +#include "collationkeys.h" +#include "collationsettings.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +SortKeyByteSink::~SortKeyByteSink() {} + +void +SortKeyByteSink::Append(const char *bytes, int32_t n) { + if (n <= 0 || bytes == nullptr) { + return; + } + if (ignore_ > 0) { + int32_t ignoreRest = ignore_ - n; + if (ignoreRest >= 0) { + ignore_ = ignoreRest; + return; + } else { + bytes += ignore_; + n = -ignoreRest; + ignore_ = 0; + } + } + int32_t length = appended_; + appended_ += n; + if ((buffer_ + length) == bytes) { + return; // the caller used GetAppendBuffer() and wrote the bytes already + } + int32_t available = capacity_ - length; + if (n <= available) { + uprv_memcpy(buffer_ + length, bytes, n); + } else { + AppendBeyondCapacity(bytes, n, length); + } +} + +char * +SortKeyByteSink::GetAppendBuffer(int32_t min_capacity, + int32_t desired_capacity_hint, + char *scratch, + int32_t scratch_capacity, + int32_t *result_capacity) { + if (min_capacity < 1 || scratch_capacity < min_capacity) { + *result_capacity = 0; + return nullptr; + } + if (ignore_ > 0) { + // Do not write ignored bytes right at the end of the buffer. + *result_capacity = scratch_capacity; + return scratch; + } + int32_t available = capacity_ - appended_; + if (available >= min_capacity) { + *result_capacity = available; + return buffer_ + appended_; + } else if (Resize(desired_capacity_hint, appended_)) { + *result_capacity = capacity_ - appended_; + return buffer_ + appended_; + } else { + *result_capacity = scratch_capacity; + return scratch; + } +} + +namespace { + +/** + * uint8_t byte buffer, similar to CharString but simpler. + */ +class SortKeyLevel : public UMemory { +public: + SortKeyLevel() : len(0), ok(true) {} + ~SortKeyLevel() {} + + /** @return false if memory allocation failed */ + UBool isOk() const { return ok; } + UBool isEmpty() const { return len == 0; } + int32_t length() const { return len; } + const uint8_t *data() const { return buffer.getAlias(); } + uint8_t operator[](int32_t index) const { return buffer[index]; } + + uint8_t *data() { return buffer.getAlias(); } + + void appendByte(uint32_t b); + void appendWeight16(uint32_t w); + void appendWeight32(uint32_t w); + void appendReverseWeight16(uint32_t w); + + /** Appends all but the last byte to the sink. The last byte should be the 01 terminator. */ + void appendTo(ByteSink &sink) const { + U_ASSERT(len > 0 && buffer[len - 1] == 1); + sink.Append(reinterpret_cast(buffer.getAlias()), len - 1); + } + +private: + MaybeStackArray buffer; + int32_t len; + UBool ok; + + UBool ensureCapacity(int32_t appendCapacity); + + SortKeyLevel(const SortKeyLevel &other); // forbid copying of this class + SortKeyLevel &operator=(const SortKeyLevel &other); // forbid copying of this class +}; + +void SortKeyLevel::appendByte(uint32_t b) { + if(len < buffer.getCapacity() || ensureCapacity(1)) { + buffer[len++] = (uint8_t)b; + } +} + +void +SortKeyLevel::appendWeight16(uint32_t w) { + U_ASSERT((w & 0xffff) != 0); + uint8_t b0 = (uint8_t)(w >> 8); + uint8_t b1 = (uint8_t)w; + int32_t appendLength = (b1 == 0) ? 1 : 2; + if((len + appendLength) <= buffer.getCapacity() || ensureCapacity(appendLength)) { + buffer[len++] = b0; + if(b1 != 0) { + buffer[len++] = b1; + } + } +} + +void +SortKeyLevel::appendWeight32(uint32_t w) { + U_ASSERT(w != 0); + uint8_t bytes[4] = { (uint8_t)(w >> 24), (uint8_t)(w >> 16), (uint8_t)(w >> 8), (uint8_t)w }; + int32_t appendLength = (bytes[1] == 0) ? 1 : (bytes[2] == 0) ? 2 : (bytes[3] == 0) ? 3 : 4; + if((len + appendLength) <= buffer.getCapacity() || ensureCapacity(appendLength)) { + buffer[len++] = bytes[0]; + if(bytes[1] != 0) { + buffer[len++] = bytes[1]; + if(bytes[2] != 0) { + buffer[len++] = bytes[2]; + if(bytes[3] != 0) { + buffer[len++] = bytes[3]; + } + } + } + } +} + +void +SortKeyLevel::appendReverseWeight16(uint32_t w) { + U_ASSERT((w & 0xffff) != 0); + uint8_t b0 = (uint8_t)(w >> 8); + uint8_t b1 = (uint8_t)w; + int32_t appendLength = (b1 == 0) ? 1 : 2; + if((len + appendLength) <= buffer.getCapacity() || ensureCapacity(appendLength)) { + if(b1 == 0) { + buffer[len++] = b0; + } else { + buffer[len] = b1; + buffer[len + 1] = b0; + len += 2; + } + } +} + +UBool SortKeyLevel::ensureCapacity(int32_t appendCapacity) { + if(!ok) { + return false; + } + int32_t newCapacity = 2 * buffer.getCapacity(); + int32_t altCapacity = len + 2 * appendCapacity; + if (newCapacity < altCapacity) { + newCapacity = altCapacity; + } + if (newCapacity < 200) { + newCapacity = 200; + } + if(buffer.resize(newCapacity, len)==nullptr) { + return ok = false; + } + return true; +} + +} // namespace + +CollationKeys::LevelCallback::~LevelCallback() {} + +UBool +CollationKeys::LevelCallback::needToWrite(Collation::Level /*level*/) { return true; } + +/** + * Map from collation strength (UColAttributeValue) + * to a mask of Collation::Level bits up to that strength, + * excluding the CASE_LEVEL which is independent of the strength, + * and excluding IDENTICAL_LEVEL which this function does not write. + */ +static const uint32_t levelMasks[UCOL_STRENGTH_LIMIT] = { + 2, // UCOL_PRIMARY -> PRIMARY_LEVEL + 6, // UCOL_SECONDARY -> up to SECONDARY_LEVEL + 0x16, // UCOL_TERTIARY -> up to TERTIARY_LEVEL + 0x36, // UCOL_QUATERNARY -> up to QUATERNARY_LEVEL + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, + 0x36 // UCOL_IDENTICAL -> up to QUATERNARY_LEVEL +}; + +void +CollationKeys::writeSortKeyUpToQuaternary(CollationIterator &iter, + const UBool *compressibleBytes, + const CollationSettings &settings, + SortKeyByteSink &sink, + Collation::Level minLevel, LevelCallback &callback, + UBool preflight, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + + int32_t options = settings.options; + // Set of levels to process and write. + uint32_t levels = levelMasks[CollationSettings::getStrength(options)]; + if((options & CollationSettings::CASE_LEVEL) != 0) { + levels |= Collation::CASE_LEVEL_FLAG; + } + // Minus the levels below minLevel. + levels &= ~(((uint32_t)1 << minLevel) - 1); + if(levels == 0) { return; } + + uint32_t variableTop; + if((options & CollationSettings::ALTERNATE_MASK) == 0) { + variableTop = 0; + } else { + // +1 so that we can use "<" and primary ignorables test out early. + variableTop = settings.variableTop + 1; + } + + uint32_t tertiaryMask = CollationSettings::getTertiaryMask(options); + + SortKeyLevel cases; + SortKeyLevel secondaries; + SortKeyLevel tertiaries; + SortKeyLevel quaternaries; + + uint32_t prevReorderedPrimary = 0; // 0==no compression + int32_t commonCases = 0; + int32_t commonSecondaries = 0; + int32_t commonTertiaries = 0; + int32_t commonQuaternaries = 0; + + uint32_t prevSecondary = 0; + int32_t secSegmentStart = 0; + + for(;;) { + // No need to keep all CEs in the buffer when we write a sort key. + iter.clearCEsIfNoneRemaining(); + int64_t ce = iter.nextCE(errorCode); + uint32_t p = (uint32_t)(ce >> 32); + if(p < variableTop && p > Collation::MERGE_SEPARATOR_PRIMARY) { + // Variable CE, shift it to quaternary level. + // Ignore all following primary ignorables, and shift further variable CEs. + if(commonQuaternaries != 0) { + --commonQuaternaries; + while(commonQuaternaries >= QUAT_COMMON_MAX_COUNT) { + quaternaries.appendByte(QUAT_COMMON_MIDDLE); + commonQuaternaries -= QUAT_COMMON_MAX_COUNT; + } + // Shifted primary weights are lower than the common weight. + quaternaries.appendByte(QUAT_COMMON_LOW + commonQuaternaries); + commonQuaternaries = 0; + } + do { + if((levels & Collation::QUATERNARY_LEVEL_FLAG) != 0) { + if(settings.hasReordering()) { + p = settings.reorder(p); + } + if((p >> 24) >= QUAT_SHIFTED_LIMIT_BYTE) { + // Prevent shifted primary lead bytes from + // overlapping with the common compression range. + quaternaries.appendByte(QUAT_SHIFTED_LIMIT_BYTE); + } + quaternaries.appendWeight32(p); + } + do { + ce = iter.nextCE(errorCode); + p = (uint32_t)(ce >> 32); + } while(p == 0); + } while(p < variableTop && p > Collation::MERGE_SEPARATOR_PRIMARY); + } + // ce could be primary ignorable, or NO_CE, or the merge separator, + // or a regular primary CE, but it is not variable. + // If ce==NO_CE, then write nothing for the primary level but + // terminate compression on all levels and then exit the loop. + if(p > Collation::NO_CE_PRIMARY && (levels & Collation::PRIMARY_LEVEL_FLAG) != 0) { + // Test the un-reordered primary for compressibility. + UBool isCompressible = compressibleBytes[p >> 24]; + if(settings.hasReordering()) { + p = settings.reorder(p); + } + uint32_t p1 = p >> 24; + if(!isCompressible || p1 != (prevReorderedPrimary >> 24)) { + if(prevReorderedPrimary != 0) { + if(p < prevReorderedPrimary) { + // No primary compression terminator + // at the end of the level or merged segment. + if(p1 > Collation::MERGE_SEPARATOR_BYTE) { + sink.Append(Collation::PRIMARY_COMPRESSION_LOW_BYTE); + } + } else { + sink.Append(Collation::PRIMARY_COMPRESSION_HIGH_BYTE); + } + } + sink.Append(p1); + if(isCompressible) { + prevReorderedPrimary = p; + } else { + prevReorderedPrimary = 0; + } + } + char p2 = (char)(p >> 16); + if(p2 != 0) { + char buffer[3] = { p2, (char)(p >> 8), (char)p }; + sink.Append(buffer, (buffer[1] == 0) ? 1 : (buffer[2] == 0) ? 2 : 3); + } + // Optimization for internalNextSortKeyPart(): + // When the primary level overflows we can stop because we need not + // calculate (preflight) the whole sort key length. + if(!preflight && sink.Overflowed()) { + if(U_SUCCESS(errorCode) && !sink.IsOk()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + } + return; + } + } + + uint32_t lower32 = (uint32_t)ce; + if(lower32 == 0) { continue; } // completely ignorable, no secondary/case/tertiary/quaternary + + if((levels & Collation::SECONDARY_LEVEL_FLAG) != 0) { + uint32_t s = lower32 >> 16; + if(s == 0) { + // secondary ignorable + } else if(s == Collation::COMMON_WEIGHT16 && + ((options & CollationSettings::BACKWARD_SECONDARY) == 0 || + p != Collation::MERGE_SEPARATOR_PRIMARY)) { + // s is a common secondary weight, and + // backwards-secondary is off or the ce is not the merge separator. + ++commonSecondaries; + } else if((options & CollationSettings::BACKWARD_SECONDARY) == 0) { + if(commonSecondaries != 0) { + --commonSecondaries; + while(commonSecondaries >= SEC_COMMON_MAX_COUNT) { + secondaries.appendByte(SEC_COMMON_MIDDLE); + commonSecondaries -= SEC_COMMON_MAX_COUNT; + } + uint32_t b; + if(s < Collation::COMMON_WEIGHT16) { + b = SEC_COMMON_LOW + commonSecondaries; + } else { + b = SEC_COMMON_HIGH - commonSecondaries; + } + secondaries.appendByte(b); + commonSecondaries = 0; + } + secondaries.appendWeight16(s); + } else { + if(commonSecondaries != 0) { + --commonSecondaries; + // Append reverse weights. The level will be re-reversed later. + int32_t remainder = commonSecondaries % SEC_COMMON_MAX_COUNT; + uint32_t b; + if(prevSecondary < Collation::COMMON_WEIGHT16) { + b = SEC_COMMON_LOW + remainder; + } else { + b = SEC_COMMON_HIGH - remainder; + } + secondaries.appendByte(b); + commonSecondaries -= remainder; + // commonSecondaries is now a multiple of SEC_COMMON_MAX_COUNT. + while(commonSecondaries > 0) { // same as >= SEC_COMMON_MAX_COUNT + secondaries.appendByte(SEC_COMMON_MIDDLE); + commonSecondaries -= SEC_COMMON_MAX_COUNT; + } + // commonSecondaries == 0 + } + if(0 < p && p <= Collation::MERGE_SEPARATOR_PRIMARY) { + // The backwards secondary level compares secondary weights backwards + // within segments separated by the merge separator (U+FFFE). + uint8_t *secs = secondaries.data(); + int32_t last = secondaries.length() - 1; + if(secSegmentStart < last) { + uint8_t *q = secs + secSegmentStart; + uint8_t *r = secs + last; + do { + uint8_t b = *q; + *q++ = *r; + *r-- = b; + } while(q < r); + } + secondaries.appendByte(p == Collation::NO_CE_PRIMARY ? + Collation::LEVEL_SEPARATOR_BYTE : Collation::MERGE_SEPARATOR_BYTE); + prevSecondary = 0; + secSegmentStart = secondaries.length(); + } else { + secondaries.appendReverseWeight16(s); + prevSecondary = s; + } + } + } + + if((levels & Collation::CASE_LEVEL_FLAG) != 0) { + if((CollationSettings::getStrength(options) == UCOL_PRIMARY) ? + p == 0 : lower32 <= 0xffff) { + // Primary+caseLevel: Ignore case level weights of primary ignorables. + // Otherwise: Ignore case level weights of secondary ignorables. + // For details see the comments in the CollationCompare class. + } else { + uint32_t c = (lower32 >> 8) & 0xff; // case bits & tertiary lead byte + U_ASSERT((c & 0xc0) != 0xc0); + if((c & 0xc0) == 0 && c > Collation::LEVEL_SEPARATOR_BYTE) { + ++commonCases; + } else { + if((options & CollationSettings::UPPER_FIRST) == 0) { + // lowerFirst: Compress common weights to nibbles 1..7..13, mixed=14, upper=15. + // If there are only common (=lowest) weights in the whole level, + // then we need not write anything. + // Level length differences are handled already on the next-higher level. + if(commonCases != 0 && + (c > Collation::LEVEL_SEPARATOR_BYTE || !cases.isEmpty())) { + --commonCases; + while(commonCases >= CASE_LOWER_FIRST_COMMON_MAX_COUNT) { + cases.appendByte(CASE_LOWER_FIRST_COMMON_MIDDLE << 4); + commonCases -= CASE_LOWER_FIRST_COMMON_MAX_COUNT; + } + uint32_t b; + if(c <= Collation::LEVEL_SEPARATOR_BYTE) { + b = CASE_LOWER_FIRST_COMMON_LOW + commonCases; + } else { + b = CASE_LOWER_FIRST_COMMON_HIGH - commonCases; + } + cases.appendByte(b << 4); + commonCases = 0; + } + if(c > Collation::LEVEL_SEPARATOR_BYTE) { + c = (CASE_LOWER_FIRST_COMMON_HIGH + (c >> 6)) << 4; // 14 or 15 + } + } else { + // upperFirst: Compress common weights to nibbles 3..15, mixed=2, upper=1. + // The compressed common case weights only go up from the "low" value + // because with upperFirst the common weight is the highest one. + if(commonCases != 0) { + --commonCases; + while(commonCases >= CASE_UPPER_FIRST_COMMON_MAX_COUNT) { + cases.appendByte(CASE_UPPER_FIRST_COMMON_LOW << 4); + commonCases -= CASE_UPPER_FIRST_COMMON_MAX_COUNT; + } + cases.appendByte((CASE_UPPER_FIRST_COMMON_LOW + commonCases) << 4); + commonCases = 0; + } + if(c > Collation::LEVEL_SEPARATOR_BYTE) { + c = (CASE_UPPER_FIRST_COMMON_LOW - (c >> 6)) << 4; // 2 or 1 + } + } + // c is a separator byte 01, + // or a left-shifted nibble 0x10, 0x20, ... 0xf0. + cases.appendByte(c); + } + } + } + + if((levels & Collation::TERTIARY_LEVEL_FLAG) != 0) { + uint32_t t = lower32 & tertiaryMask; + U_ASSERT((lower32 & 0xc000) != 0xc000); + if(t == Collation::COMMON_WEIGHT16) { + ++commonTertiaries; + } else if((tertiaryMask & 0x8000) == 0) { + // Tertiary weights without case bits. + // Move lead bytes 06..3F to C6..FF for a large common-weight range. + if(commonTertiaries != 0) { + --commonTertiaries; + while(commonTertiaries >= TER_ONLY_COMMON_MAX_COUNT) { + tertiaries.appendByte(TER_ONLY_COMMON_MIDDLE); + commonTertiaries -= TER_ONLY_COMMON_MAX_COUNT; + } + uint32_t b; + if(t < Collation::COMMON_WEIGHT16) { + b = TER_ONLY_COMMON_LOW + commonTertiaries; + } else { + b = TER_ONLY_COMMON_HIGH - commonTertiaries; + } + tertiaries.appendByte(b); + commonTertiaries = 0; + } + if(t > Collation::COMMON_WEIGHT16) { t += 0xc000; } + tertiaries.appendWeight16(t); + } else if((options & CollationSettings::UPPER_FIRST) == 0) { + // Tertiary weights with caseFirst=lowerFirst. + // Move lead bytes 06..BF to 46..FF for the common-weight range. + if(commonTertiaries != 0) { + --commonTertiaries; + while(commonTertiaries >= TER_LOWER_FIRST_COMMON_MAX_COUNT) { + tertiaries.appendByte(TER_LOWER_FIRST_COMMON_MIDDLE); + commonTertiaries -= TER_LOWER_FIRST_COMMON_MAX_COUNT; + } + uint32_t b; + if(t < Collation::COMMON_WEIGHT16) { + b = TER_LOWER_FIRST_COMMON_LOW + commonTertiaries; + } else { + b = TER_LOWER_FIRST_COMMON_HIGH - commonTertiaries; + } + tertiaries.appendByte(b); + commonTertiaries = 0; + } + if(t > Collation::COMMON_WEIGHT16) { t += 0x4000; } + tertiaries.appendWeight16(t); + } else { + // Tertiary weights with caseFirst=upperFirst. + // Do not change the artificial uppercase weight of a tertiary CE (0.0.ut), + // to keep tertiary CEs well-formed. + // Their case+tertiary weights must be greater than those of + // primary and secondary CEs. + // + // Separator 01 -> 01 (unchanged) + // Lowercase 02..04 -> 82..84 (includes uncased) + // Common weight 05 -> 85..C5 (common-weight compression range) + // Lowercase 06..3F -> C6..FF + // Mixed case 42..7F -> 42..7F + // Uppercase 82..BF -> 02..3F + // Tertiary CE 86..BF -> C6..FF + if(t <= Collation::NO_CE_WEIGHT16) { + // Keep separators unchanged. + } else if(lower32 > 0xffff) { + // Invert case bits of primary & secondary CEs. + t ^= 0xc000; + if(t < (TER_UPPER_FIRST_COMMON_HIGH << 8)) { + t -= 0x4000; + } + } else { + // Keep uppercase bits of tertiary CEs. + U_ASSERT(0x8600 <= t && t <= 0xbfff); + t += 0x4000; + } + if(commonTertiaries != 0) { + --commonTertiaries; + while(commonTertiaries >= TER_UPPER_FIRST_COMMON_MAX_COUNT) { + tertiaries.appendByte(TER_UPPER_FIRST_COMMON_MIDDLE); + commonTertiaries -= TER_UPPER_FIRST_COMMON_MAX_COUNT; + } + uint32_t b; + if(t < (TER_UPPER_FIRST_COMMON_LOW << 8)) { + b = TER_UPPER_FIRST_COMMON_LOW + commonTertiaries; + } else { + b = TER_UPPER_FIRST_COMMON_HIGH - commonTertiaries; + } + tertiaries.appendByte(b); + commonTertiaries = 0; + } + tertiaries.appendWeight16(t); + } + } + + if((levels & Collation::QUATERNARY_LEVEL_FLAG) != 0) { + uint32_t q = lower32 & 0xffff; + if((q & 0xc0) == 0 && q > Collation::NO_CE_WEIGHT16) { + ++commonQuaternaries; + } else if(q == Collation::NO_CE_WEIGHT16 && + (options & CollationSettings::ALTERNATE_MASK) == 0 && + quaternaries.isEmpty()) { + // If alternate=non-ignorable and there are only common quaternary weights, + // then we need not write anything. + // The only weights greater than the merge separator and less than the common weight + // are shifted primary weights, which are not generated for alternate=non-ignorable. + // There are also exactly as many quaternary weights as tertiary weights, + // so level length differences are handled already on tertiary level. + // Any above-common quaternary weight will compare greater regardless. + quaternaries.appendByte(Collation::LEVEL_SEPARATOR_BYTE); + } else { + if(q == Collation::NO_CE_WEIGHT16) { + q = Collation::LEVEL_SEPARATOR_BYTE; + } else { + q = 0xfc + ((q >> 6) & 3); + } + if(commonQuaternaries != 0) { + --commonQuaternaries; + while(commonQuaternaries >= QUAT_COMMON_MAX_COUNT) { + quaternaries.appendByte(QUAT_COMMON_MIDDLE); + commonQuaternaries -= QUAT_COMMON_MAX_COUNT; + } + uint32_t b; + if(q < QUAT_COMMON_LOW) { + b = QUAT_COMMON_LOW + commonQuaternaries; + } else { + b = QUAT_COMMON_HIGH - commonQuaternaries; + } + quaternaries.appendByte(b); + commonQuaternaries = 0; + } + quaternaries.appendByte(q); + } + } + + if((lower32 >> 24) == Collation::LEVEL_SEPARATOR_BYTE) { break; } // ce == NO_CE + } + + if(U_FAILURE(errorCode)) { return; } + + // Append the beyond-primary levels. + UBool ok = true; + if((levels & Collation::SECONDARY_LEVEL_FLAG) != 0) { + if(!callback.needToWrite(Collation::SECONDARY_LEVEL)) { return; } + ok &= secondaries.isOk(); + sink.Append(Collation::LEVEL_SEPARATOR_BYTE); + secondaries.appendTo(sink); + } + + if((levels & Collation::CASE_LEVEL_FLAG) != 0) { + if(!callback.needToWrite(Collation::CASE_LEVEL)) { return; } + ok &= cases.isOk(); + sink.Append(Collation::LEVEL_SEPARATOR_BYTE); + // Write pairs of nibbles as bytes, except separator bytes as themselves. + int32_t length = cases.length() - 1; // Ignore the trailing NO_CE. + uint8_t b = 0; + for(int32_t i = 0; i < length; ++i) { + uint8_t c = (uint8_t)cases[i]; + U_ASSERT((c & 0xf) == 0 && c != 0); + if(b == 0) { + b = c; + } else { + sink.Append(b | (c >> 4)); + b = 0; + } + } + if(b != 0) { + sink.Append(b); + } + } + + if((levels & Collation::TERTIARY_LEVEL_FLAG) != 0) { + if(!callback.needToWrite(Collation::TERTIARY_LEVEL)) { return; } + ok &= tertiaries.isOk(); + sink.Append(Collation::LEVEL_SEPARATOR_BYTE); + tertiaries.appendTo(sink); + } + + if((levels & Collation::QUATERNARY_LEVEL_FLAG) != 0) { + if(!callback.needToWrite(Collation::QUATERNARY_LEVEL)) { return; } + ok &= quaternaries.isOk(); + sink.Append(Collation::LEVEL_SEPARATOR_BYTE); + quaternaries.appendTo(sink); + } + + if(!ok || !sink.IsOk()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + } +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationkeys.h b/deps/icu-small/source/i18n/collationkeys.h index 8dad286dc6461a..43115f7f475412 100644 --- a/deps/icu-small/source/i18n/collationkeys.h +++ b/deps/icu-small/source/i18n/collationkeys.h @@ -1,169 +1,169 @@ -// © 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. -******************************************************************************* -* collationkeys.h -* -* created on: 2012sep02 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONKEYS_H__ -#define __COLLATIONKEYS_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/bytestream.h" -#include "unicode/ucol.h" -#include "charstr.h" -#include "collation.h" - -U_NAMESPACE_BEGIN - -class CollationIterator; -struct CollationDataReader; -struct CollationSettings; - -class SortKeyByteSink : public ByteSink { -public: - SortKeyByteSink(char *dest, int32_t destCapacity) - : buffer_(dest), capacity_(destCapacity), - appended_(0), ignore_(0) {} - virtual ~SortKeyByteSink(); - - void IgnoreBytes(int32_t numIgnore) { ignore_ = numIgnore; } - - virtual void Append(const char *bytes, int32_t n) override; - void Append(uint32_t b) { - if (ignore_ > 0) { - --ignore_; - } else { - if (appended_ < capacity_ || Resize(1, appended_)) { - buffer_[appended_] = (char)b; - } - ++appended_; - } - } - virtual char *GetAppendBuffer(int32_t min_capacity, - int32_t desired_capacity_hint, - char *scratch, int32_t scratch_capacity, - int32_t *result_capacity) override; - int32_t NumberOfBytesAppended() const { return appended_; } - - /** - * @return how many bytes can be appended (including ignored ones) - * without reallocation - */ - int32_t GetRemainingCapacity() const { - // Either ignore_ or appended_ should be 0. - return ignore_ + capacity_ - appended_; - } - - UBool Overflowed() const { return appended_ > capacity_; } - /** @return false if memory allocation failed */ - UBool IsOk() const { return buffer_ != NULL; } - -protected: - virtual void AppendBeyondCapacity(const char *bytes, int32_t n, int32_t length) = 0; - virtual UBool Resize(int32_t appendCapacity, int32_t length) = 0; - - void SetNotOk() { - buffer_ = NULL; - capacity_ = 0; - } - - char *buffer_; - int32_t capacity_; - int32_t appended_; - int32_t ignore_; - -private: - SortKeyByteSink(const SortKeyByteSink &); // copy constructor not implemented - SortKeyByteSink &operator=(const SortKeyByteSink &); // assignment operator not implemented -}; - -class U_I18N_API CollationKeys /* not : public UObject because all methods are static */ { -public: - class LevelCallback : public UMemory { - public: - virtual ~LevelCallback(); - /** - * @param level The next level about to be written to the ByteSink. - * @return true if the level is to be written - * (the base class implementation always returns true) - */ - virtual UBool needToWrite(Collation::Level level); - }; - - /** - * Writes the sort key bytes for minLevel up to the iterator data's strength. - * Optionally writes the case level. - * Stops writing levels when callback.needToWrite(level) returns false. - * Separates levels with the LEVEL_SEPARATOR_BYTE - * but does not write a TERMINATOR_BYTE. - */ - static void writeSortKeyUpToQuaternary(CollationIterator &iter, - const UBool *compressibleBytes, - const CollationSettings &settings, - SortKeyByteSink &sink, - Collation::Level minLevel, LevelCallback &callback, - UBool preflight, UErrorCode &errorCode); -private: - friend struct CollationDataReader; - - CollationKeys() = delete; // no instantiation - - // Secondary level: Compress up to 33 common weights as 05..25 or 25..45. - static const uint32_t SEC_COMMON_LOW = Collation::COMMON_BYTE; - static const uint32_t SEC_COMMON_MIDDLE = SEC_COMMON_LOW + 0x20; - static const uint32_t SEC_COMMON_HIGH = SEC_COMMON_LOW + 0x40; - static const int32_t SEC_COMMON_MAX_COUNT = 0x21; - - // Case level, lowerFirst: Compress up to 7 common weights as 1..7 or 7..13. - static const uint32_t CASE_LOWER_FIRST_COMMON_LOW = 1; - static const uint32_t CASE_LOWER_FIRST_COMMON_MIDDLE = 7; - static const uint32_t CASE_LOWER_FIRST_COMMON_HIGH = 13; - static const int32_t CASE_LOWER_FIRST_COMMON_MAX_COUNT = 7; - - // Case level, upperFirst: Compress up to 13 common weights as 3..15. - static const uint32_t CASE_UPPER_FIRST_COMMON_LOW = 3; - static const uint32_t CASE_UPPER_FIRST_COMMON_HIGH = 15; - static const int32_t CASE_UPPER_FIRST_COMMON_MAX_COUNT = 13; - - // Tertiary level only (no case): Compress up to 97 common weights as 05..65 or 65..C5. - static const uint32_t TER_ONLY_COMMON_LOW = Collation::COMMON_BYTE; - static const uint32_t TER_ONLY_COMMON_MIDDLE = TER_ONLY_COMMON_LOW + 0x60; - static const uint32_t TER_ONLY_COMMON_HIGH = TER_ONLY_COMMON_LOW + 0xc0; - static const int32_t TER_ONLY_COMMON_MAX_COUNT = 0x61; - - // Tertiary with case, lowerFirst: Compress up to 33 common weights as 05..25 or 25..45. - static const uint32_t TER_LOWER_FIRST_COMMON_LOW = Collation::COMMON_BYTE; - static const uint32_t TER_LOWER_FIRST_COMMON_MIDDLE = TER_LOWER_FIRST_COMMON_LOW + 0x20; - static const uint32_t TER_LOWER_FIRST_COMMON_HIGH = TER_LOWER_FIRST_COMMON_LOW + 0x40; - static const int32_t TER_LOWER_FIRST_COMMON_MAX_COUNT = 0x21; - - // Tertiary with case, upperFirst: Compress up to 33 common weights as 85..A5 or A5..C5. - static const uint32_t TER_UPPER_FIRST_COMMON_LOW = Collation::COMMON_BYTE + 0x80; - static const uint32_t TER_UPPER_FIRST_COMMON_MIDDLE = TER_UPPER_FIRST_COMMON_LOW + 0x20; - static const uint32_t TER_UPPER_FIRST_COMMON_HIGH = TER_UPPER_FIRST_COMMON_LOW + 0x40; - static const int32_t TER_UPPER_FIRST_COMMON_MAX_COUNT = 0x21; - - // Quaternary level: Compress up to 113 common weights as 1C..8C or 8C..FC. - static const uint32_t QUAT_COMMON_LOW = 0x1c; - static const uint32_t QUAT_COMMON_MIDDLE = QUAT_COMMON_LOW + 0x70; - static const uint32_t QUAT_COMMON_HIGH = QUAT_COMMON_LOW + 0xE0; - static const int32_t QUAT_COMMON_MAX_COUNT = 0x71; - // Primary weights shifted to quaternary level must be encoded with - // a lead byte below the common-weight compression range. - static const uint32_t QUAT_SHIFTED_LIMIT_BYTE = QUAT_COMMON_LOW - 1; // 0x1b -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONKEYS_H__ +// © 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. +******************************************************************************* +* collationkeys.h +* +* created on: 2012sep02 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONKEYS_H__ +#define __COLLATIONKEYS_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/bytestream.h" +#include "unicode/ucol.h" +#include "charstr.h" +#include "collation.h" + +U_NAMESPACE_BEGIN + +class CollationIterator; +struct CollationDataReader; +struct CollationSettings; + +class SortKeyByteSink : public ByteSink { +public: + SortKeyByteSink(char *dest, int32_t destCapacity) + : buffer_(dest), capacity_(destCapacity), + appended_(0), ignore_(0) {} + virtual ~SortKeyByteSink(); + + void IgnoreBytes(int32_t numIgnore) { ignore_ = numIgnore; } + + virtual void Append(const char *bytes, int32_t n) override; + void Append(uint32_t b) { + if (ignore_ > 0) { + --ignore_; + } else { + if (appended_ < capacity_ || Resize(1, appended_)) { + buffer_[appended_] = (char)b; + } + ++appended_; + } + } + virtual char *GetAppendBuffer(int32_t min_capacity, + int32_t desired_capacity_hint, + char *scratch, int32_t scratch_capacity, + int32_t *result_capacity) override; + int32_t NumberOfBytesAppended() const { return appended_; } + + /** + * @return how many bytes can be appended (including ignored ones) + * without reallocation + */ + int32_t GetRemainingCapacity() const { + // Either ignore_ or appended_ should be 0. + return ignore_ + capacity_ - appended_; + } + + UBool Overflowed() const { return appended_ > capacity_; } + /** @return false if memory allocation failed */ + UBool IsOk() const { return buffer_ != nullptr; } + +protected: + virtual void AppendBeyondCapacity(const char *bytes, int32_t n, int32_t length) = 0; + virtual UBool Resize(int32_t appendCapacity, int32_t length) = 0; + + void SetNotOk() { + buffer_ = nullptr; + capacity_ = 0; + } + + char *buffer_; + int32_t capacity_; + int32_t appended_; + int32_t ignore_; + +private: + SortKeyByteSink(const SortKeyByteSink &); // copy constructor not implemented + SortKeyByteSink &operator=(const SortKeyByteSink &); // assignment operator not implemented +}; + +class U_I18N_API CollationKeys /* not : public UObject because all methods are static */ { +public: + class LevelCallback : public UMemory { + public: + virtual ~LevelCallback(); + /** + * @param level The next level about to be written to the ByteSink. + * @return true if the level is to be written + * (the base class implementation always returns true) + */ + virtual UBool needToWrite(Collation::Level level); + }; + + /** + * Writes the sort key bytes for minLevel up to the iterator data's strength. + * Optionally writes the case level. + * Stops writing levels when callback.needToWrite(level) returns false. + * Separates levels with the LEVEL_SEPARATOR_BYTE + * but does not write a TERMINATOR_BYTE. + */ + static void writeSortKeyUpToQuaternary(CollationIterator &iter, + const UBool *compressibleBytes, + const CollationSettings &settings, + SortKeyByteSink &sink, + Collation::Level minLevel, LevelCallback &callback, + UBool preflight, UErrorCode &errorCode); +private: + friend struct CollationDataReader; + + CollationKeys() = delete; // no instantiation + + // Secondary level: Compress up to 33 common weights as 05..25 or 25..45. + static const uint32_t SEC_COMMON_LOW = Collation::COMMON_BYTE; + static const uint32_t SEC_COMMON_MIDDLE = SEC_COMMON_LOW + 0x20; + static const uint32_t SEC_COMMON_HIGH = SEC_COMMON_LOW + 0x40; + static const int32_t SEC_COMMON_MAX_COUNT = 0x21; + + // Case level, lowerFirst: Compress up to 7 common weights as 1..7 or 7..13. + static const uint32_t CASE_LOWER_FIRST_COMMON_LOW = 1; + static const uint32_t CASE_LOWER_FIRST_COMMON_MIDDLE = 7; + static const uint32_t CASE_LOWER_FIRST_COMMON_HIGH = 13; + static const int32_t CASE_LOWER_FIRST_COMMON_MAX_COUNT = 7; + + // Case level, upperFirst: Compress up to 13 common weights as 3..15. + static const uint32_t CASE_UPPER_FIRST_COMMON_LOW = 3; + static const uint32_t CASE_UPPER_FIRST_COMMON_HIGH = 15; + static const int32_t CASE_UPPER_FIRST_COMMON_MAX_COUNT = 13; + + // Tertiary level only (no case): Compress up to 97 common weights as 05..65 or 65..C5. + static const uint32_t TER_ONLY_COMMON_LOW = Collation::COMMON_BYTE; + static const uint32_t TER_ONLY_COMMON_MIDDLE = TER_ONLY_COMMON_LOW + 0x60; + static const uint32_t TER_ONLY_COMMON_HIGH = TER_ONLY_COMMON_LOW + 0xc0; + static const int32_t TER_ONLY_COMMON_MAX_COUNT = 0x61; + + // Tertiary with case, lowerFirst: Compress up to 33 common weights as 05..25 or 25..45. + static const uint32_t TER_LOWER_FIRST_COMMON_LOW = Collation::COMMON_BYTE; + static const uint32_t TER_LOWER_FIRST_COMMON_MIDDLE = TER_LOWER_FIRST_COMMON_LOW + 0x20; + static const uint32_t TER_LOWER_FIRST_COMMON_HIGH = TER_LOWER_FIRST_COMMON_LOW + 0x40; + static const int32_t TER_LOWER_FIRST_COMMON_MAX_COUNT = 0x21; + + // Tertiary with case, upperFirst: Compress up to 33 common weights as 85..A5 or A5..C5. + static const uint32_t TER_UPPER_FIRST_COMMON_LOW = Collation::COMMON_BYTE + 0x80; + static const uint32_t TER_UPPER_FIRST_COMMON_MIDDLE = TER_UPPER_FIRST_COMMON_LOW + 0x20; + static const uint32_t TER_UPPER_FIRST_COMMON_HIGH = TER_UPPER_FIRST_COMMON_LOW + 0x40; + static const int32_t TER_UPPER_FIRST_COMMON_MAX_COUNT = 0x21; + + // Quaternary level: Compress up to 113 common weights as 1C..8C or 8C..FC. + static const uint32_t QUAT_COMMON_LOW = 0x1c; + static const uint32_t QUAT_COMMON_MIDDLE = QUAT_COMMON_LOW + 0x70; + static const uint32_t QUAT_COMMON_HIGH = QUAT_COMMON_LOW + 0xE0; + static const int32_t QUAT_COMMON_MAX_COUNT = 0x71; + // Primary weights shifted to quaternary level must be encoded with + // a lead byte below the common-weight compression range. + static const uint32_t QUAT_SHIFTED_LIMIT_BYTE = QUAT_COMMON_LOW - 1; // 0x1b +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONKEYS_H__ diff --git a/deps/icu-small/source/i18n/collationroot.cpp b/deps/icu-small/source/i18n/collationroot.cpp index dc88c35a680095..bf5db076f64dca 100644 --- a/deps/icu-small/source/i18n/collationroot.cpp +++ b/deps/icu-small/source/i18n/collationroot.cpp @@ -1,140 +1,140 @@ -// © 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. -******************************************************************************* -* collationroot.cpp -* -* created on: 2012dec17 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/coll.h" -#include "unicode/udata.h" -#include "collation.h" -#include "collationdata.h" -#include "collationdatareader.h" -#include "collationroot.h" -#include "collationsettings.h" -#include "collationtailoring.h" -#include "normalizer2impl.h" -#include "ucln_in.h" -#include "udatamem.h" -#include "umutex.h" -#include "umapfile.h" - -U_NAMESPACE_BEGIN - -namespace { - -static const CollationCacheEntry *rootSingleton = NULL; -static UInitOnce initOnce {}; - -} // namespace - -U_CDECL_BEGIN - -static UBool U_CALLCONV uprv_collation_root_cleanup() { - SharedObject::clearPtr(rootSingleton); - initOnce.reset(); - return true; -} - -U_CDECL_END - -UDataMemory* -CollationRoot::loadFromFile(const char* ucadataPath, UErrorCode &errorCode) { - UDataMemory dataMemory; - UDataMemory *rDataMem = NULL; - if (U_FAILURE(errorCode)) { - return NULL; - } - if (uprv_mapFile(&dataMemory, ucadataPath, &errorCode)) { - if (dataMemory.pHeader->dataHeader.magic1 == 0xda && - dataMemory.pHeader->dataHeader.magic2 == 0x27 && - CollationDataReader::isAcceptable(NULL, "icu", "ucadata", &dataMemory.pHeader->info)) { - rDataMem = UDataMemory_createNewInstance(&errorCode); - if (U_FAILURE(errorCode)) { - return NULL; - } - rDataMem->pHeader = dataMemory.pHeader; - rDataMem->mapAddr = dataMemory.mapAddr; - rDataMem->map = dataMemory.map; - return rDataMem; - } - errorCode = U_INVALID_FORMAT_ERROR; - return NULL; - } - errorCode = U_MISSING_RESOURCE_ERROR; - return NULL; -} - -void U_CALLCONV -CollationRoot::load(const char* ucadataPath, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - LocalPointer t(new CollationTailoring(NULL)); - if(t.isNull() || t->isBogus()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - t->memory = ucadataPath ? CollationRoot::loadFromFile(ucadataPath, errorCode) : - udata_openChoice(U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "coll", - "icu", "ucadata", - CollationDataReader::isAcceptable, - t->version, &errorCode); - if(U_FAILURE(errorCode)) { return; } - const uint8_t *inBytes = static_cast(udata_getMemory(t->memory)); - CollationDataReader::read(NULL, inBytes, udata_getLength(t->memory), *t, errorCode); - if(U_FAILURE(errorCode)) { return; } - ucln_i18n_registerCleanup(UCLN_I18N_COLLATION_ROOT, uprv_collation_root_cleanup); - CollationCacheEntry *entry = new CollationCacheEntry(Locale::getRoot(), t.getAlias()); - if(entry != NULL) { - t.orphan(); // The rootSingleton took ownership of the tailoring. - entry->addRef(); - rootSingleton = entry; - } -} - -const CollationCacheEntry * -CollationRoot::getRootCacheEntry(UErrorCode &errorCode) { - umtx_initOnce(initOnce, CollationRoot::load, static_cast(NULL), errorCode); - if(U_FAILURE(errorCode)) { return NULL; } - return rootSingleton; -} - -const CollationTailoring * -CollationRoot::getRoot(UErrorCode &errorCode) { - umtx_initOnce(initOnce, CollationRoot::load, static_cast(NULL), errorCode); - if(U_FAILURE(errorCode)) { return NULL; } - return rootSingleton->tailoring; -} - -const CollationData * -CollationRoot::getData(UErrorCode &errorCode) { - const CollationTailoring *root = getRoot(errorCode); - if(U_FAILURE(errorCode)) { return NULL; } - return root->data; -} - -const CollationSettings * -CollationRoot::getSettings(UErrorCode &errorCode) { - const CollationTailoring *root = getRoot(errorCode); - if(U_FAILURE(errorCode)) { return NULL; } - return root->settings; -} - -void -CollationRoot::forceLoadFromFile(const char* ucadataPath, UErrorCode &errorCode) { - umtx_initOnce(initOnce, CollationRoot::load, ucadataPath, errorCode); -} - - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 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. +******************************************************************************* +* collationroot.cpp +* +* created on: 2012dec17 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/coll.h" +#include "unicode/udata.h" +#include "collation.h" +#include "collationdata.h" +#include "collationdatareader.h" +#include "collationroot.h" +#include "collationsettings.h" +#include "collationtailoring.h" +#include "normalizer2impl.h" +#include "ucln_in.h" +#include "udatamem.h" +#include "umutex.h" +#include "umapfile.h" + +U_NAMESPACE_BEGIN + +namespace { + +static const CollationCacheEntry *rootSingleton = nullptr; +static UInitOnce initOnce {}; + +} // namespace + +U_CDECL_BEGIN + +static UBool U_CALLCONV uprv_collation_root_cleanup() { + SharedObject::clearPtr(rootSingleton); + initOnce.reset(); + return true; +} + +U_CDECL_END + +UDataMemory* +CollationRoot::loadFromFile(const char* ucadataPath, UErrorCode &errorCode) { + UDataMemory dataMemory; + UDataMemory *rDataMem = nullptr; + if (U_FAILURE(errorCode)) { + return nullptr; + } + if (uprv_mapFile(&dataMemory, ucadataPath, &errorCode)) { + if (dataMemory.pHeader->dataHeader.magic1 == 0xda && + dataMemory.pHeader->dataHeader.magic2 == 0x27 && + CollationDataReader::isAcceptable(nullptr, "icu", "ucadata", &dataMemory.pHeader->info)) { + rDataMem = UDataMemory_createNewInstance(&errorCode); + if (U_FAILURE(errorCode)) { + return nullptr; + } + rDataMem->pHeader = dataMemory.pHeader; + rDataMem->mapAddr = dataMemory.mapAddr; + rDataMem->map = dataMemory.map; + return rDataMem; + } + errorCode = U_INVALID_FORMAT_ERROR; + return nullptr; + } + errorCode = U_MISSING_RESOURCE_ERROR; + return nullptr; +} + +void U_CALLCONV +CollationRoot::load(const char* ucadataPath, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + LocalPointer t(new CollationTailoring(nullptr)); + if(t.isNull() || t->isBogus()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + t->memory = ucadataPath ? CollationRoot::loadFromFile(ucadataPath, errorCode) : + udata_openChoice(U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "coll", + "icu", "ucadata", + CollationDataReader::isAcceptable, + t->version, &errorCode); + if(U_FAILURE(errorCode)) { return; } + const uint8_t *inBytes = static_cast(udata_getMemory(t->memory)); + CollationDataReader::read(nullptr, inBytes, udata_getLength(t->memory), *t, errorCode); + if(U_FAILURE(errorCode)) { return; } + ucln_i18n_registerCleanup(UCLN_I18N_COLLATION_ROOT, uprv_collation_root_cleanup); + CollationCacheEntry *entry = new CollationCacheEntry(Locale::getRoot(), t.getAlias()); + if(entry != nullptr) { + t.orphan(); // The rootSingleton took ownership of the tailoring. + entry->addRef(); + rootSingleton = entry; + } +} + +const CollationCacheEntry * +CollationRoot::getRootCacheEntry(UErrorCode &errorCode) { + umtx_initOnce(initOnce, CollationRoot::load, static_cast(nullptr), errorCode); + if(U_FAILURE(errorCode)) { return nullptr; } + return rootSingleton; +} + +const CollationTailoring * +CollationRoot::getRoot(UErrorCode &errorCode) { + umtx_initOnce(initOnce, CollationRoot::load, static_cast(nullptr), errorCode); + if(U_FAILURE(errorCode)) { return nullptr; } + return rootSingleton->tailoring; +} + +const CollationData * +CollationRoot::getData(UErrorCode &errorCode) { + const CollationTailoring *root = getRoot(errorCode); + if(U_FAILURE(errorCode)) { return nullptr; } + return root->data; +} + +const CollationSettings * +CollationRoot::getSettings(UErrorCode &errorCode) { + const CollationTailoring *root = getRoot(errorCode); + if(U_FAILURE(errorCode)) { return nullptr; } + return root->settings; +} + +void +CollationRoot::forceLoadFromFile(const char* ucadataPath, UErrorCode &errorCode) { + umtx_initOnce(initOnce, CollationRoot::load, ucadataPath, errorCode); +} + + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationroot.h b/deps/icu-small/source/i18n/collationroot.h index b203f612b35a77..27dca21bd3c23b 100644 --- a/deps/icu-small/source/i18n/collationroot.h +++ b/deps/icu-small/source/i18n/collationroot.h @@ -1,48 +1,48 @@ -// © 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. -******************************************************************************* -* collationroot.h -* -* created on: 2012dec17 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONROOT_H__ -#define __COLLATIONROOT_H__ - -#include "unicode/utypes.h" -#include "unicode/udata.h" - -#if !UCONFIG_NO_COLLATION - -U_NAMESPACE_BEGIN - -struct CollationCacheEntry; -struct CollationData; -struct CollationSettings; -struct CollationTailoring; - -/** - * Collation root provider. - */ -class U_I18N_API CollationRoot { // purely static -public: - static const CollationCacheEntry *getRootCacheEntry(UErrorCode &errorCode); - static const CollationTailoring *getRoot(UErrorCode &errorCode); - static const CollationData *getData(UErrorCode &errorCode); - static const CollationSettings *getSettings(UErrorCode &errorCode); - static void U_EXPORT2 forceLoadFromFile(const char* ucadataPath, UErrorCode &errorCode); - -private: - static void U_CALLCONV load(const char* ucadataPath, UErrorCode &errorCode); - static UDataMemory* loadFromFile(const char* ucadataPath, UErrorCode &errorCode); -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONROOT_H__ +// © 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. +******************************************************************************* +* collationroot.h +* +* created on: 2012dec17 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONROOT_H__ +#define __COLLATIONROOT_H__ + +#include "unicode/utypes.h" +#include "unicode/udata.h" + +#if !UCONFIG_NO_COLLATION + +U_NAMESPACE_BEGIN + +struct CollationCacheEntry; +struct CollationData; +struct CollationSettings; +struct CollationTailoring; + +/** + * Collation root provider. + */ +class U_I18N_API CollationRoot { // purely static +public: + static const CollationCacheEntry *getRootCacheEntry(UErrorCode &errorCode); + static const CollationTailoring *getRoot(UErrorCode &errorCode); + static const CollationData *getData(UErrorCode &errorCode); + static const CollationSettings *getSettings(UErrorCode &errorCode); + static void U_EXPORT2 forceLoadFromFile(const char* ucadataPath, UErrorCode &errorCode); + +private: + static void U_CALLCONV load(const char* ucadataPath, UErrorCode &errorCode); + static UDataMemory* loadFromFile(const char* ucadataPath, UErrorCode &errorCode); +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONROOT_H__ diff --git a/deps/icu-small/source/i18n/collationrootelements.cpp b/deps/icu-small/source/i18n/collationrootelements.cpp index 9b46d14144bb3d..a99dacffe0cef5 100644 --- a/deps/icu-small/source/i18n/collationrootelements.cpp +++ b/deps/icu-small/source/i18n/collationrootelements.cpp @@ -1,341 +1,341 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationrootelements.cpp -* -* created on: 2013mar05 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "collation.h" -#include "collationrootelements.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - -int64_t -CollationRootElements::lastCEWithPrimaryBefore(uint32_t p) const { - if(p == 0) { return 0; } - U_ASSERT(p > elements[elements[IX_FIRST_PRIMARY_INDEX]]); - int32_t index = findP(p); - uint32_t q = elements[index]; - uint32_t secTer; - if(p == (q & 0xffffff00)) { - // p == elements[index] is a root primary. Find the CE before it. - // We must not be in a primary range. - U_ASSERT((q & PRIMARY_STEP_MASK) == 0); - secTer = elements[index - 1]; - if((secTer & SEC_TER_DELTA_FLAG) == 0) { - // Primary CE just before p. - p = secTer & 0xffffff00; - secTer = Collation::COMMON_SEC_AND_TER_CE; - } else { - // secTer = last secondary & tertiary for the previous primary - index -= 2; - for(;;) { - p = elements[index]; - if((p & SEC_TER_DELTA_FLAG) == 0) { - p &= 0xffffff00; - break; - } - --index; - } - } - } else { - // p > elements[index] which is the previous primary. - // Find the last secondary & tertiary weights for it. - p = q & 0xffffff00; - secTer = Collation::COMMON_SEC_AND_TER_CE; - for(;;) { - q = elements[++index]; - if((q & SEC_TER_DELTA_FLAG) == 0) { - // We must not be in a primary range. - U_ASSERT((q & PRIMARY_STEP_MASK) == 0); - break; - } - secTer = q; - } - } - return ((int64_t)p << 32) | (secTer & ~SEC_TER_DELTA_FLAG); -} - -int64_t -CollationRootElements::firstCEWithPrimaryAtLeast(uint32_t p) const { - if(p == 0) { return 0; } - int32_t index = findP(p); - if(p != (elements[index] & 0xffffff00)) { - for(;;) { - p = elements[++index]; - if((p & SEC_TER_DELTA_FLAG) == 0) { - // First primary after p. We must not be in a primary range. - U_ASSERT((p & PRIMARY_STEP_MASK) == 0); - break; - } - } - } - // The code above guarantees that p has at most 3 bytes: (p & 0xff) == 0. - return ((int64_t)p << 32) | Collation::COMMON_SEC_AND_TER_CE; -} - -uint32_t -CollationRootElements::getPrimaryBefore(uint32_t p, UBool isCompressible) const { - int32_t index = findPrimary(p); - int32_t step; - uint32_t q = elements[index]; - if(p == (q & 0xffffff00)) { - // Found p itself. Return the previous primary. - // See if p is at the end of a previous range. - step = (int32_t)q & PRIMARY_STEP_MASK; - if(step == 0) { - // p is not at the end of a range. Look for the previous primary. - do { - p = elements[--index]; - } while((p & SEC_TER_DELTA_FLAG) != 0); - return p & 0xffffff00; - } - } else { - // p is in a range, and not at the start. - uint32_t nextElement = elements[index + 1]; - U_ASSERT(isEndOfPrimaryRange(nextElement)); - step = (int32_t)nextElement & PRIMARY_STEP_MASK; - } - // Return the previous range primary. - if((p & 0xffff) == 0) { - return Collation::decTwoBytePrimaryByOneStep(p, isCompressible, step); - } else { - return Collation::decThreeBytePrimaryByOneStep(p, isCompressible, step); - } -} - -uint32_t -CollationRootElements::getSecondaryBefore(uint32_t p, uint32_t s) const { - int32_t index; - uint32_t previousSec, sec; - if(p == 0) { - index = (int32_t)elements[IX_FIRST_SECONDARY_INDEX]; - // Gap at the beginning of the secondary CE range. - previousSec = 0; - sec = elements[index] >> 16; - } else { - index = findPrimary(p) + 1; - previousSec = Collation::BEFORE_WEIGHT16; - sec = getFirstSecTerForPrimary(index) >> 16; - } - U_ASSERT(s >= sec); - while(s > sec) { - previousSec = sec; - U_ASSERT((elements[index] & SEC_TER_DELTA_FLAG) != 0); - sec = elements[index++] >> 16; - } - U_ASSERT(sec == s); - return previousSec; -} - -uint32_t -CollationRootElements::getTertiaryBefore(uint32_t p, uint32_t s, uint32_t t) const { - U_ASSERT((t & ~Collation::ONLY_TERTIARY_MASK) == 0); - int32_t index; - uint32_t previousTer, secTer; - if(p == 0) { - if(s == 0) { - index = (int32_t)elements[IX_FIRST_TERTIARY_INDEX]; - // Gap at the beginning of the tertiary CE range. - previousTer = 0; - } else { - index = (int32_t)elements[IX_FIRST_SECONDARY_INDEX]; - previousTer = Collation::BEFORE_WEIGHT16; - } - secTer = elements[index] & ~SEC_TER_DELTA_FLAG; - } else { - index = findPrimary(p) + 1; - previousTer = Collation::BEFORE_WEIGHT16; - secTer = getFirstSecTerForPrimary(index); - } - uint32_t st = (s << 16) | t; - while(st > secTer) { - if((secTer >> 16) == s) { previousTer = secTer; } - U_ASSERT((elements[index] & SEC_TER_DELTA_FLAG) != 0); - secTer = elements[index++] & ~SEC_TER_DELTA_FLAG; - } - U_ASSERT(secTer == st); - return previousTer & 0xffff; -} - -uint32_t -CollationRootElements::getPrimaryAfter(uint32_t p, int32_t index, UBool isCompressible) const { - U_ASSERT(p == (elements[index] & 0xffffff00) || isEndOfPrimaryRange(elements[index + 1])); - uint32_t q = elements[++index]; - int32_t step; - if((q & SEC_TER_DELTA_FLAG) == 0 && (step = (int32_t)q & PRIMARY_STEP_MASK) != 0) { - // Return the next primary in this range. - if((p & 0xffff) == 0) { - return Collation::incTwoBytePrimaryByOffset(p, isCompressible, step); - } else { - return Collation::incThreeBytePrimaryByOffset(p, isCompressible, step); - } - } else { - // Return the next primary in the list. - while((q & SEC_TER_DELTA_FLAG) != 0) { - q = elements[++index]; - } - U_ASSERT((q & PRIMARY_STEP_MASK) == 0); - return q; - } -} - -uint32_t -CollationRootElements::getSecondaryAfter(int32_t index, uint32_t s) const { - uint32_t secTer; - uint32_t secLimit; - if(index == 0) { - // primary = 0 - U_ASSERT(s != 0); - index = (int32_t)elements[IX_FIRST_SECONDARY_INDEX]; - secTer = elements[index]; - // Gap at the end of the secondary CE range. - secLimit = 0x10000; - } else { - U_ASSERT(index >= (int32_t)elements[IX_FIRST_PRIMARY_INDEX]); - secTer = getFirstSecTerForPrimary(index + 1); - // If this is an explicit sec/ter unit, then it will be read once more. - // Gap for secondaries of primary CEs. - secLimit = getSecondaryBoundary(); - } - for(;;) { - uint32_t sec = secTer >> 16; - if(sec > s) { return sec; } - secTer = elements[++index]; - if((secTer & SEC_TER_DELTA_FLAG) == 0) { return secLimit; } - } -} - -uint32_t -CollationRootElements::getTertiaryAfter(int32_t index, uint32_t s, uint32_t t) const { - uint32_t secTer; - uint32_t terLimit; - if(index == 0) { - // primary = 0 - if(s == 0) { - U_ASSERT(t != 0); - index = (int32_t)elements[IX_FIRST_TERTIARY_INDEX]; - // Gap at the end of the tertiary CE range. - terLimit = 0x4000; - } else { - index = (int32_t)elements[IX_FIRST_SECONDARY_INDEX]; - // Gap for tertiaries of primary/secondary CEs. - terLimit = getTertiaryBoundary(); - } - secTer = elements[index] & ~SEC_TER_DELTA_FLAG; - } else { - U_ASSERT(index >= (int32_t)elements[IX_FIRST_PRIMARY_INDEX]); - secTer = getFirstSecTerForPrimary(index + 1); - // If this is an explicit sec/ter unit, then it will be read once more. - terLimit = getTertiaryBoundary(); - } - uint32_t st = (s << 16) | t; - for(;;) { - if(secTer > st) { - U_ASSERT((secTer >> 16) == s); - return secTer & 0xffff; - } - secTer = elements[++index]; - // No tertiary greater than t for this primary+secondary. - if((secTer & SEC_TER_DELTA_FLAG) == 0 || (secTer >> 16) > s) { return terLimit; } - secTer &= ~SEC_TER_DELTA_FLAG; - } -} - -uint32_t -CollationRootElements::getFirstSecTerForPrimary(int32_t index) const { - uint32_t secTer = elements[index]; - if((secTer & SEC_TER_DELTA_FLAG) == 0) { - // No sec/ter delta. - return Collation::COMMON_SEC_AND_TER_CE; - } - secTer &= ~SEC_TER_DELTA_FLAG; - if(secTer > Collation::COMMON_SEC_AND_TER_CE) { - // Implied sec/ter. - return Collation::COMMON_SEC_AND_TER_CE; - } - // Explicit sec/ter below common/common. - return secTer; -} - -int32_t -CollationRootElements::findPrimary(uint32_t p) const { - // Requirement: p must occur as a root primary. - U_ASSERT((p & 0xff) == 0); // at most a 3-byte primary - int32_t index = findP(p); - // If p is in a range, then we just assume that p is an actual primary in this range. - // (Too cumbersome/expensive to check.) - // Otherwise, it must be an exact match. - U_ASSERT(isEndOfPrimaryRange(elements[index + 1]) || p == (elements[index] & 0xffffff00)); - return index; -} - -int32_t -CollationRootElements::findP(uint32_t p) const { - // p need not occur as a root primary. - // For example, it might be a reordering group boundary. - U_ASSERT((p >> 24) != Collation::UNASSIGNED_IMPLICIT_BYTE); - // modified binary search - int32_t start = (int32_t)elements[IX_FIRST_PRIMARY_INDEX]; - U_ASSERT(p >= elements[start]); - int32_t limit = length - 1; - U_ASSERT(elements[limit] >= PRIMARY_SENTINEL); - U_ASSERT(p < elements[limit]); - while((start + 1) < limit) { - // Invariant: elements[start] and elements[limit] are primaries, - // and elements[start]<=p<=elements[limit]. - int32_t i = (start + limit) / 2; - uint32_t q = elements[i]; - if((q & SEC_TER_DELTA_FLAG) != 0) { - // Find the next primary. - int32_t j = i + 1; - for(;;) { - if(j == limit) { break; } - q = elements[j]; - if((q & SEC_TER_DELTA_FLAG) == 0) { - i = j; - break; - } - ++j; - } - if((q & SEC_TER_DELTA_FLAG) != 0) { - // Find the preceding primary. - j = i - 1; - for(;;) { - if(j == start) { break; } - q = elements[j]; - if((q & SEC_TER_DELTA_FLAG) == 0) { - i = j; - break; - } - --j; - } - if((q & SEC_TER_DELTA_FLAG) != 0) { - // No primary between start and limit. - break; - } - } - } - if(p < (q & 0xffffff00)) { // Reset the "step" bits of a range end primary. - limit = i; - } else { - start = i; - } - } - return start; -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationrootelements.cpp +* +* created on: 2013mar05 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "collation.h" +#include "collationrootelements.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +int64_t +CollationRootElements::lastCEWithPrimaryBefore(uint32_t p) const { + if(p == 0) { return 0; } + U_ASSERT(p > elements[elements[IX_FIRST_PRIMARY_INDEX]]); + int32_t index = findP(p); + uint32_t q = elements[index]; + uint32_t secTer; + if(p == (q & 0xffffff00)) { + // p == elements[index] is a root primary. Find the CE before it. + // We must not be in a primary range. + U_ASSERT((q & PRIMARY_STEP_MASK) == 0); + secTer = elements[index - 1]; + if((secTer & SEC_TER_DELTA_FLAG) == 0) { + // Primary CE just before p. + p = secTer & 0xffffff00; + secTer = Collation::COMMON_SEC_AND_TER_CE; + } else { + // secTer = last secondary & tertiary for the previous primary + index -= 2; + for(;;) { + p = elements[index]; + if((p & SEC_TER_DELTA_FLAG) == 0) { + p &= 0xffffff00; + break; + } + --index; + } + } + } else { + // p > elements[index] which is the previous primary. + // Find the last secondary & tertiary weights for it. + p = q & 0xffffff00; + secTer = Collation::COMMON_SEC_AND_TER_CE; + for(;;) { + q = elements[++index]; + if((q & SEC_TER_DELTA_FLAG) == 0) { + // We must not be in a primary range. + U_ASSERT((q & PRIMARY_STEP_MASK) == 0); + break; + } + secTer = q; + } + } + return ((int64_t)p << 32) | (secTer & ~SEC_TER_DELTA_FLAG); +} + +int64_t +CollationRootElements::firstCEWithPrimaryAtLeast(uint32_t p) const { + if(p == 0) { return 0; } + int32_t index = findP(p); + if(p != (elements[index] & 0xffffff00)) { + for(;;) { + p = elements[++index]; + if((p & SEC_TER_DELTA_FLAG) == 0) { + // First primary after p. We must not be in a primary range. + U_ASSERT((p & PRIMARY_STEP_MASK) == 0); + break; + } + } + } + // The code above guarantees that p has at most 3 bytes: (p & 0xff) == 0. + return ((int64_t)p << 32) | Collation::COMMON_SEC_AND_TER_CE; +} + +uint32_t +CollationRootElements::getPrimaryBefore(uint32_t p, UBool isCompressible) const { + int32_t index = findPrimary(p); + int32_t step; + uint32_t q = elements[index]; + if(p == (q & 0xffffff00)) { + // Found p itself. Return the previous primary. + // See if p is at the end of a previous range. + step = (int32_t)q & PRIMARY_STEP_MASK; + if(step == 0) { + // p is not at the end of a range. Look for the previous primary. + do { + p = elements[--index]; + } while((p & SEC_TER_DELTA_FLAG) != 0); + return p & 0xffffff00; + } + } else { + // p is in a range, and not at the start. + uint32_t nextElement = elements[index + 1]; + U_ASSERT(isEndOfPrimaryRange(nextElement)); + step = (int32_t)nextElement & PRIMARY_STEP_MASK; + } + // Return the previous range primary. + if((p & 0xffff) == 0) { + return Collation::decTwoBytePrimaryByOneStep(p, isCompressible, step); + } else { + return Collation::decThreeBytePrimaryByOneStep(p, isCompressible, step); + } +} + +uint32_t +CollationRootElements::getSecondaryBefore(uint32_t p, uint32_t s) const { + int32_t index; + uint32_t previousSec, sec; + if(p == 0) { + index = (int32_t)elements[IX_FIRST_SECONDARY_INDEX]; + // Gap at the beginning of the secondary CE range. + previousSec = 0; + sec = elements[index] >> 16; + } else { + index = findPrimary(p) + 1; + previousSec = Collation::BEFORE_WEIGHT16; + sec = getFirstSecTerForPrimary(index) >> 16; + } + U_ASSERT(s >= sec); + while(s > sec) { + previousSec = sec; + U_ASSERT((elements[index] & SEC_TER_DELTA_FLAG) != 0); + sec = elements[index++] >> 16; + } + U_ASSERT(sec == s); + return previousSec; +} + +uint32_t +CollationRootElements::getTertiaryBefore(uint32_t p, uint32_t s, uint32_t t) const { + U_ASSERT((t & ~Collation::ONLY_TERTIARY_MASK) == 0); + int32_t index; + uint32_t previousTer, secTer; + if(p == 0) { + if(s == 0) { + index = (int32_t)elements[IX_FIRST_TERTIARY_INDEX]; + // Gap at the beginning of the tertiary CE range. + previousTer = 0; + } else { + index = (int32_t)elements[IX_FIRST_SECONDARY_INDEX]; + previousTer = Collation::BEFORE_WEIGHT16; + } + secTer = elements[index] & ~SEC_TER_DELTA_FLAG; + } else { + index = findPrimary(p) + 1; + previousTer = Collation::BEFORE_WEIGHT16; + secTer = getFirstSecTerForPrimary(index); + } + uint32_t st = (s << 16) | t; + while(st > secTer) { + if((secTer >> 16) == s) { previousTer = secTer; } + U_ASSERT((elements[index] & SEC_TER_DELTA_FLAG) != 0); + secTer = elements[index++] & ~SEC_TER_DELTA_FLAG; + } + U_ASSERT(secTer == st); + return previousTer & 0xffff; +} + +uint32_t +CollationRootElements::getPrimaryAfter(uint32_t p, int32_t index, UBool isCompressible) const { + U_ASSERT(p == (elements[index] & 0xffffff00) || isEndOfPrimaryRange(elements[index + 1])); + uint32_t q = elements[++index]; + int32_t step; + if((q & SEC_TER_DELTA_FLAG) == 0 && (step = (int32_t)q & PRIMARY_STEP_MASK) != 0) { + // Return the next primary in this range. + if((p & 0xffff) == 0) { + return Collation::incTwoBytePrimaryByOffset(p, isCompressible, step); + } else { + return Collation::incThreeBytePrimaryByOffset(p, isCompressible, step); + } + } else { + // Return the next primary in the list. + while((q & SEC_TER_DELTA_FLAG) != 0) { + q = elements[++index]; + } + U_ASSERT((q & PRIMARY_STEP_MASK) == 0); + return q; + } +} + +uint32_t +CollationRootElements::getSecondaryAfter(int32_t index, uint32_t s) const { + uint32_t secTer; + uint32_t secLimit; + if(index == 0) { + // primary = 0 + U_ASSERT(s != 0); + index = (int32_t)elements[IX_FIRST_SECONDARY_INDEX]; + secTer = elements[index]; + // Gap at the end of the secondary CE range. + secLimit = 0x10000; + } else { + U_ASSERT(index >= (int32_t)elements[IX_FIRST_PRIMARY_INDEX]); + secTer = getFirstSecTerForPrimary(index + 1); + // If this is an explicit sec/ter unit, then it will be read once more. + // Gap for secondaries of primary CEs. + secLimit = getSecondaryBoundary(); + } + for(;;) { + uint32_t sec = secTer >> 16; + if(sec > s) { return sec; } + secTer = elements[++index]; + if((secTer & SEC_TER_DELTA_FLAG) == 0) { return secLimit; } + } +} + +uint32_t +CollationRootElements::getTertiaryAfter(int32_t index, uint32_t s, uint32_t t) const { + uint32_t secTer; + uint32_t terLimit; + if(index == 0) { + // primary = 0 + if(s == 0) { + U_ASSERT(t != 0); + index = (int32_t)elements[IX_FIRST_TERTIARY_INDEX]; + // Gap at the end of the tertiary CE range. + terLimit = 0x4000; + } else { + index = (int32_t)elements[IX_FIRST_SECONDARY_INDEX]; + // Gap for tertiaries of primary/secondary CEs. + terLimit = getTertiaryBoundary(); + } + secTer = elements[index] & ~SEC_TER_DELTA_FLAG; + } else { + U_ASSERT(index >= (int32_t)elements[IX_FIRST_PRIMARY_INDEX]); + secTer = getFirstSecTerForPrimary(index + 1); + // If this is an explicit sec/ter unit, then it will be read once more. + terLimit = getTertiaryBoundary(); + } + uint32_t st = (s << 16) | t; + for(;;) { + if(secTer > st) { + U_ASSERT((secTer >> 16) == s); + return secTer & 0xffff; + } + secTer = elements[++index]; + // No tertiary greater than t for this primary+secondary. + if((secTer & SEC_TER_DELTA_FLAG) == 0 || (secTer >> 16) > s) { return terLimit; } + secTer &= ~SEC_TER_DELTA_FLAG; + } +} + +uint32_t +CollationRootElements::getFirstSecTerForPrimary(int32_t index) const { + uint32_t secTer = elements[index]; + if((secTer & SEC_TER_DELTA_FLAG) == 0) { + // No sec/ter delta. + return Collation::COMMON_SEC_AND_TER_CE; + } + secTer &= ~SEC_TER_DELTA_FLAG; + if(secTer > Collation::COMMON_SEC_AND_TER_CE) { + // Implied sec/ter. + return Collation::COMMON_SEC_AND_TER_CE; + } + // Explicit sec/ter below common/common. + return secTer; +} + +int32_t +CollationRootElements::findPrimary(uint32_t p) const { + // Requirement: p must occur as a root primary. + U_ASSERT((p & 0xff) == 0); // at most a 3-byte primary + int32_t index = findP(p); + // If p is in a range, then we just assume that p is an actual primary in this range. + // (Too cumbersome/expensive to check.) + // Otherwise, it must be an exact match. + U_ASSERT(isEndOfPrimaryRange(elements[index + 1]) || p == (elements[index] & 0xffffff00)); + return index; +} + +int32_t +CollationRootElements::findP(uint32_t p) const { + // p need not occur as a root primary. + // For example, it might be a reordering group boundary. + U_ASSERT((p >> 24) != Collation::UNASSIGNED_IMPLICIT_BYTE); + // modified binary search + int32_t start = (int32_t)elements[IX_FIRST_PRIMARY_INDEX]; + U_ASSERT(p >= elements[start]); + int32_t limit = length - 1; + U_ASSERT(elements[limit] >= PRIMARY_SENTINEL); + U_ASSERT(p < elements[limit]); + while((start + 1) < limit) { + // Invariant: elements[start] and elements[limit] are primaries, + // and elements[start]<=p<=elements[limit]. + int32_t i = (start + limit) / 2; + uint32_t q = elements[i]; + if((q & SEC_TER_DELTA_FLAG) != 0) { + // Find the next primary. + int32_t j = i + 1; + for(;;) { + if(j == limit) { break; } + q = elements[j]; + if((q & SEC_TER_DELTA_FLAG) == 0) { + i = j; + break; + } + ++j; + } + if((q & SEC_TER_DELTA_FLAG) != 0) { + // Find the preceding primary. + j = i - 1; + for(;;) { + if(j == start) { break; } + q = elements[j]; + if((q & SEC_TER_DELTA_FLAG) == 0) { + i = j; + break; + } + --j; + } + if((q & SEC_TER_DELTA_FLAG) != 0) { + // No primary between start and limit. + break; + } + } + } + if(p < (q & 0xffffff00)) { // Reset the "step" bits of a range end primary. + limit = i; + } else { + start = i; + } + } + return start; +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationrootelements.h b/deps/icu-small/source/i18n/collationrootelements.h index 7836d8d83b4da7..8b9a4b9998931a 100644 --- a/deps/icu-small/source/i18n/collationrootelements.h +++ b/deps/icu-small/source/i18n/collationrootelements.h @@ -1,276 +1,276 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationrootelements.h -* -* created on: 2013mar01 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONROOTELEMENTS_H__ -#define __COLLATIONROOTELEMENTS_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/uobject.h" -#include "collation.h" - -U_NAMESPACE_BEGIN - -/** - * Container and access methods for collation elements and weights - * that occur in the root collator. - * Needed for finding boundaries for building a tailoring. - * - * This class takes and returns 16-bit secondary and tertiary weights. - */ -class U_I18N_API CollationRootElements : public UMemory { -public: - CollationRootElements(const uint32_t *rootElements, int32_t rootElementsLength) - : elements(rootElements), length(rootElementsLength) {} - - /** - * Higher than any root primary. - */ - static const uint32_t PRIMARY_SENTINEL = 0xffffff00; - - /** - * Flag in a root element, set if the element contains secondary & tertiary weights, - * rather than a primary. - */ - static const uint32_t SEC_TER_DELTA_FLAG = 0x80; - /** - * Mask for getting the primary range step value from a primary-range-end element. - */ - static const uint8_t PRIMARY_STEP_MASK = 0x7f; - - enum { - /** - * Index of the first CE with a non-zero tertiary weight. - * Same as the start of the compact root elements table. - */ - IX_FIRST_TERTIARY_INDEX, - /** - * Index of the first CE with a non-zero secondary weight. - */ - IX_FIRST_SECONDARY_INDEX, - /** - * Index of the first CE with a non-zero primary weight. - */ - IX_FIRST_PRIMARY_INDEX, - /** - * Must match Collation::COMMON_SEC_AND_TER_CE. - */ - IX_COMMON_SEC_AND_TER_CE, - /** - * Secondary & tertiary boundaries. - * Bits 31..24: [fixed last secondary common byte 45] - * Bits 23..16: [fixed first ignorable secondary byte 80] - * Bits 15.. 8: reserved, 0 - * Bits 7.. 0: [fixed first ignorable tertiary byte 3C] - */ - IX_SEC_TER_BOUNDARIES, - /** - * The current number of indexes. - * Currently the same as elements[IX_FIRST_TERTIARY_INDEX]. - */ - IX_COUNT - }; - - /** - * Returns the boundary between tertiary weights of primary/secondary CEs - * and those of tertiary CEs. - * This is the upper limit for tertiaries of primary/secondary CEs. - * This minus one is the lower limit for tertiaries of tertiary CEs. - */ - uint32_t getTertiaryBoundary() const { - return (elements[IX_SEC_TER_BOUNDARIES] << 8) & 0xff00; - } - - /** - * Returns the first assigned tertiary CE. - */ - uint32_t getFirstTertiaryCE() const { - return elements[elements[IX_FIRST_TERTIARY_INDEX]] & ~SEC_TER_DELTA_FLAG; - } - - /** - * Returns the last assigned tertiary CE. - */ - uint32_t getLastTertiaryCE() const { - return elements[elements[IX_FIRST_SECONDARY_INDEX] - 1] & ~SEC_TER_DELTA_FLAG; - } - - /** - * Returns the last common secondary weight. - * This is the lower limit for secondaries of primary CEs. - */ - uint32_t getLastCommonSecondary() const { - return (elements[IX_SEC_TER_BOUNDARIES] >> 16) & 0xff00; - } - - /** - * Returns the boundary between secondary weights of primary CEs - * and those of secondary CEs. - * This is the upper limit for secondaries of primary CEs. - * This minus one is the lower limit for secondaries of secondary CEs. - */ - uint32_t getSecondaryBoundary() const { - return (elements[IX_SEC_TER_BOUNDARIES] >> 8) & 0xff00; - } - - /** - * Returns the first assigned secondary CE. - */ - uint32_t getFirstSecondaryCE() const { - return elements[elements[IX_FIRST_SECONDARY_INDEX]] & ~SEC_TER_DELTA_FLAG; - } - - /** - * Returns the last assigned secondary CE. - */ - uint32_t getLastSecondaryCE() const { - return elements[elements[IX_FIRST_PRIMARY_INDEX] - 1] & ~SEC_TER_DELTA_FLAG; - } - - /** - * Returns the first assigned primary weight. - */ - uint32_t getFirstPrimary() const { - return elements[elements[IX_FIRST_PRIMARY_INDEX]]; // step=0: cannot be a range end - } - - /** - * Returns the first assigned primary CE. - */ - int64_t getFirstPrimaryCE() const { - return Collation::makeCE(getFirstPrimary()); - } - - /** - * Returns the last root CE with a primary weight before p. - * Intended only for reordering group boundaries. - */ - int64_t lastCEWithPrimaryBefore(uint32_t p) const; - - /** - * Returns the first root CE with a primary weight of at least p. - * Intended only for reordering group boundaries. - */ - int64_t firstCEWithPrimaryAtLeast(uint32_t p) const; - - /** - * Returns the primary weight before p. - * p must be greater than the first root primary. - */ - uint32_t getPrimaryBefore(uint32_t p, UBool isCompressible) const; - - /** Returns the secondary weight before [p, s]. */ - uint32_t getSecondaryBefore(uint32_t p, uint32_t s) const; - - /** Returns the tertiary weight before [p, s, t]. */ - uint32_t getTertiaryBefore(uint32_t p, uint32_t s, uint32_t t) const; - - /** - * Finds the index of the input primary. - * p must occur as a root primary, and must not be 0. - */ - int32_t findPrimary(uint32_t p) const; - - /** - * Returns the primary weight after p where index=findPrimary(p). - * p must be at least the first root primary. - */ - uint32_t getPrimaryAfter(uint32_t p, int32_t index, UBool isCompressible) const; - /** - * Returns the secondary weight after [p, s] where index=findPrimary(p) - * except use index=0 for p=0. - * - * Must return a weight for every root [p, s] as well as for every weight - * returned by getSecondaryBefore(). If p!=0 then s can be BEFORE_WEIGHT16. - * - * Exception: [0, 0] is handled by the CollationBuilder: - * Both its lower and upper boundaries are special. - */ - uint32_t getSecondaryAfter(int32_t index, uint32_t s) const; - /** - * Returns the tertiary weight after [p, s, t] where index=findPrimary(p) - * except use index=0 for p=0. - * - * Must return a weight for every root [p, s, t] as well as for every weight - * returned by getTertiaryBefore(). If s!=0 then t can be BEFORE_WEIGHT16. - * - * Exception: [0, 0, 0] is handled by the CollationBuilder: - * Both its lower and upper boundaries are special. - */ - uint32_t getTertiaryAfter(int32_t index, uint32_t s, uint32_t t) const; - -private: - /** - * Returns the first secondary & tertiary weights for p where index=findPrimary(p)+1. - */ - uint32_t getFirstSecTerForPrimary(int32_t index) const; - - /** - * Finds the largest index i where elements[i]<=p. - * Requires first primary<=p<0xffffff00 (PRIMARY_SENTINEL). - * Does not require that p is a root collator primary. - */ - int32_t findP(uint32_t p) const; - - static inline UBool isEndOfPrimaryRange(uint32_t q) { - return (q & SEC_TER_DELTA_FLAG) == 0 && (q & PRIMARY_STEP_MASK) != 0; - } - - /** - * Data structure: - * - * The first few entries are indexes, up to elements[IX_FIRST_TERTIARY_INDEX]. - * See the comments on the IX_ constants. - * - * All other elements are a compact form of the root collator CEs - * in mostly collation order. - * - * A sequence of one or more root CEs with the same primary weight is stored as - * one element with the primary weight, with the SEC_TER_DELTA_FLAG flag not set, - * followed by elements with only the secondary/tertiary weights, - * each with that flag set. - * If the lowest secondary/tertiary combination is Collation::COMMON_SEC_AND_TER_CE, - * then the element for that combination is omitted. - * - * Note: If the first actual secondary/tertiary combination is higher than - * Collation::COMMON_SEC_AND_TER_CE (which is unusual), - * the runtime code will assume anyway that Collation::COMMON_SEC_AND_TER_CE is present. - * - * A range of only-primary CEs with a consistent "step" increment - * from each primary to the next may be stored as a range. - * Only the first and last primary are stored, and the last has the step - * value in the low bits (PRIMARY_STEP_MASK). - * - * An range-end element may also either start a new range or be followed by - * elements with secondary/tertiary deltas. - * - * A primary element that is not a range end has zero step bits. - * - * There is no element for the completely ignorable CE (all weights 0). - * - * Before elements[IX_FIRST_PRIMARY_INDEX], all elements are secondary/tertiary deltas, - * for all of the ignorable root CEs. - * - * There are no elements for unassigned-implicit primary CEs. - * All primaries stored here are at most 3 bytes long. - */ - const uint32_t *elements; - int32_t length; -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONROOTELEMENTS_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationrootelements.h +* +* created on: 2013mar01 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONROOTELEMENTS_H__ +#define __COLLATIONROOTELEMENTS_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/uobject.h" +#include "collation.h" + +U_NAMESPACE_BEGIN + +/** + * Container and access methods for collation elements and weights + * that occur in the root collator. + * Needed for finding boundaries for building a tailoring. + * + * This class takes and returns 16-bit secondary and tertiary weights. + */ +class U_I18N_API CollationRootElements : public UMemory { +public: + CollationRootElements(const uint32_t *rootElements, int32_t rootElementsLength) + : elements(rootElements), length(rootElementsLength) {} + + /** + * Higher than any root primary. + */ + static const uint32_t PRIMARY_SENTINEL = 0xffffff00; + + /** + * Flag in a root element, set if the element contains secondary & tertiary weights, + * rather than a primary. + */ + static const uint32_t SEC_TER_DELTA_FLAG = 0x80; + /** + * Mask for getting the primary range step value from a primary-range-end element. + */ + static const uint8_t PRIMARY_STEP_MASK = 0x7f; + + enum { + /** + * Index of the first CE with a non-zero tertiary weight. + * Same as the start of the compact root elements table. + */ + IX_FIRST_TERTIARY_INDEX, + /** + * Index of the first CE with a non-zero secondary weight. + */ + IX_FIRST_SECONDARY_INDEX, + /** + * Index of the first CE with a non-zero primary weight. + */ + IX_FIRST_PRIMARY_INDEX, + /** + * Must match Collation::COMMON_SEC_AND_TER_CE. + */ + IX_COMMON_SEC_AND_TER_CE, + /** + * Secondary & tertiary boundaries. + * Bits 31..24: [fixed last secondary common byte 45] + * Bits 23..16: [fixed first ignorable secondary byte 80] + * Bits 15.. 8: reserved, 0 + * Bits 7.. 0: [fixed first ignorable tertiary byte 3C] + */ + IX_SEC_TER_BOUNDARIES, + /** + * The current number of indexes. + * Currently the same as elements[IX_FIRST_TERTIARY_INDEX]. + */ + IX_COUNT + }; + + /** + * Returns the boundary between tertiary weights of primary/secondary CEs + * and those of tertiary CEs. + * This is the upper limit for tertiaries of primary/secondary CEs. + * This minus one is the lower limit for tertiaries of tertiary CEs. + */ + uint32_t getTertiaryBoundary() const { + return (elements[IX_SEC_TER_BOUNDARIES] << 8) & 0xff00; + } + + /** + * Returns the first assigned tertiary CE. + */ + uint32_t getFirstTertiaryCE() const { + return elements[elements[IX_FIRST_TERTIARY_INDEX]] & ~SEC_TER_DELTA_FLAG; + } + + /** + * Returns the last assigned tertiary CE. + */ + uint32_t getLastTertiaryCE() const { + return elements[elements[IX_FIRST_SECONDARY_INDEX] - 1] & ~SEC_TER_DELTA_FLAG; + } + + /** + * Returns the last common secondary weight. + * This is the lower limit for secondaries of primary CEs. + */ + uint32_t getLastCommonSecondary() const { + return (elements[IX_SEC_TER_BOUNDARIES] >> 16) & 0xff00; + } + + /** + * Returns the boundary between secondary weights of primary CEs + * and those of secondary CEs. + * This is the upper limit for secondaries of primary CEs. + * This minus one is the lower limit for secondaries of secondary CEs. + */ + uint32_t getSecondaryBoundary() const { + return (elements[IX_SEC_TER_BOUNDARIES] >> 8) & 0xff00; + } + + /** + * Returns the first assigned secondary CE. + */ + uint32_t getFirstSecondaryCE() const { + return elements[elements[IX_FIRST_SECONDARY_INDEX]] & ~SEC_TER_DELTA_FLAG; + } + + /** + * Returns the last assigned secondary CE. + */ + uint32_t getLastSecondaryCE() const { + return elements[elements[IX_FIRST_PRIMARY_INDEX] - 1] & ~SEC_TER_DELTA_FLAG; + } + + /** + * Returns the first assigned primary weight. + */ + uint32_t getFirstPrimary() const { + return elements[elements[IX_FIRST_PRIMARY_INDEX]]; // step=0: cannot be a range end + } + + /** + * Returns the first assigned primary CE. + */ + int64_t getFirstPrimaryCE() const { + return Collation::makeCE(getFirstPrimary()); + } + + /** + * Returns the last root CE with a primary weight before p. + * Intended only for reordering group boundaries. + */ + int64_t lastCEWithPrimaryBefore(uint32_t p) const; + + /** + * Returns the first root CE with a primary weight of at least p. + * Intended only for reordering group boundaries. + */ + int64_t firstCEWithPrimaryAtLeast(uint32_t p) const; + + /** + * Returns the primary weight before p. + * p must be greater than the first root primary. + */ + uint32_t getPrimaryBefore(uint32_t p, UBool isCompressible) const; + + /** Returns the secondary weight before [p, s]. */ + uint32_t getSecondaryBefore(uint32_t p, uint32_t s) const; + + /** Returns the tertiary weight before [p, s, t]. */ + uint32_t getTertiaryBefore(uint32_t p, uint32_t s, uint32_t t) const; + + /** + * Finds the index of the input primary. + * p must occur as a root primary, and must not be 0. + */ + int32_t findPrimary(uint32_t p) const; + + /** + * Returns the primary weight after p where index=findPrimary(p). + * p must be at least the first root primary. + */ + uint32_t getPrimaryAfter(uint32_t p, int32_t index, UBool isCompressible) const; + /** + * Returns the secondary weight after [p, s] where index=findPrimary(p) + * except use index=0 for p=0. + * + * Must return a weight for every root [p, s] as well as for every weight + * returned by getSecondaryBefore(). If p!=0 then s can be BEFORE_WEIGHT16. + * + * Exception: [0, 0] is handled by the CollationBuilder: + * Both its lower and upper boundaries are special. + */ + uint32_t getSecondaryAfter(int32_t index, uint32_t s) const; + /** + * Returns the tertiary weight after [p, s, t] where index=findPrimary(p) + * except use index=0 for p=0. + * + * Must return a weight for every root [p, s, t] as well as for every weight + * returned by getTertiaryBefore(). If s!=0 then t can be BEFORE_WEIGHT16. + * + * Exception: [0, 0, 0] is handled by the CollationBuilder: + * Both its lower and upper boundaries are special. + */ + uint32_t getTertiaryAfter(int32_t index, uint32_t s, uint32_t t) const; + +private: + /** + * Returns the first secondary & tertiary weights for p where index=findPrimary(p)+1. + */ + uint32_t getFirstSecTerForPrimary(int32_t index) const; + + /** + * Finds the largest index i where elements[i]<=p. + * Requires first primary<=p<0xffffff00 (PRIMARY_SENTINEL). + * Does not require that p is a root collator primary. + */ + int32_t findP(uint32_t p) const; + + static inline UBool isEndOfPrimaryRange(uint32_t q) { + return (q & SEC_TER_DELTA_FLAG) == 0 && (q & PRIMARY_STEP_MASK) != 0; + } + + /** + * Data structure: + * + * The first few entries are indexes, up to elements[IX_FIRST_TERTIARY_INDEX]. + * See the comments on the IX_ constants. + * + * All other elements are a compact form of the root collator CEs + * in mostly collation order. + * + * A sequence of one or more root CEs with the same primary weight is stored as + * one element with the primary weight, with the SEC_TER_DELTA_FLAG flag not set, + * followed by elements with only the secondary/tertiary weights, + * each with that flag set. + * If the lowest secondary/tertiary combination is Collation::COMMON_SEC_AND_TER_CE, + * then the element for that combination is omitted. + * + * Note: If the first actual secondary/tertiary combination is higher than + * Collation::COMMON_SEC_AND_TER_CE (which is unusual), + * the runtime code will assume anyway that Collation::COMMON_SEC_AND_TER_CE is present. + * + * A range of only-primary CEs with a consistent "step" increment + * from each primary to the next may be stored as a range. + * Only the first and last primary are stored, and the last has the step + * value in the low bits (PRIMARY_STEP_MASK). + * + * An range-end element may also either start a new range or be followed by + * elements with secondary/tertiary deltas. + * + * A primary element that is not a range end has zero step bits. + * + * There is no element for the completely ignorable CE (all weights 0). + * + * Before elements[IX_FIRST_PRIMARY_INDEX], all elements are secondary/tertiary deltas, + * for all of the ignorable root CEs. + * + * There are no elements for unassigned-implicit primary CEs. + * All primaries stored here are at most 3 bytes long. + */ + const uint32_t *elements; + int32_t length; +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONROOTELEMENTS_H__ diff --git a/deps/icu-small/source/i18n/collationruleparser.cpp b/deps/icu-small/source/i18n/collationruleparser.cpp index 7fb95c0b2b597d..a387a36b63be8c 100644 --- a/deps/icu-small/source/i18n/collationruleparser.cpp +++ b/deps/icu-small/source/i18n/collationruleparser.cpp @@ -1,881 +1,881 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationruleparser.cpp -* -* (replaced the former ucol_tok.cpp) -* -* created on: 2013apr10 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/normalizer2.h" -#include "unicode/parseerr.h" -#include "unicode/uchar.h" -#include "unicode/ucol.h" -#include "unicode/uloc.h" -#include "unicode/unistr.h" -#include "unicode/utf16.h" -#include "charstr.h" -#include "cmemory.h" -#include "collation.h" -#include "collationdata.h" -#include "collationruleparser.h" -#include "collationsettings.h" -#include "collationtailoring.h" -#include "cstring.h" -#include "patternprops.h" -#include "uassert.h" -#include "uvectr32.h" - -U_NAMESPACE_BEGIN - -namespace { - -static const UChar BEFORE[] = { 0x5b, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0 }; // "[before" -const int32_t BEFORE_LENGTH = 7; - -} // namespace - -CollationRuleParser::Sink::~Sink() {} - -void -CollationRuleParser::Sink::suppressContractions(const UnicodeSet &, const char *&, UErrorCode &) {} - -void -CollationRuleParser::Sink::optimize(const UnicodeSet &, const char *&, UErrorCode &) {} - -CollationRuleParser::Importer::~Importer() {} - -CollationRuleParser::CollationRuleParser(const CollationData *base, UErrorCode &errorCode) - : nfd(*Normalizer2::getNFDInstance(errorCode)), - nfc(*Normalizer2::getNFCInstance(errorCode)), - rules(NULL), baseData(base), settings(NULL), - parseError(NULL), errorReason(NULL), - sink(NULL), importer(NULL), - ruleIndex(0) { -} - -CollationRuleParser::~CollationRuleParser() { -} - -void -CollationRuleParser::parse(const UnicodeString &ruleString, - CollationSettings &outSettings, - UParseError *outParseError, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - settings = &outSettings; - parseError = outParseError; - if(parseError != NULL) { - parseError->line = 0; - parseError->offset = -1; - parseError->preContext[0] = 0; - parseError->postContext[0] = 0; - } - errorReason = NULL; - parse(ruleString, errorCode); -} - -void -CollationRuleParser::parse(const UnicodeString &ruleString, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - rules = &ruleString; - ruleIndex = 0; - - while(ruleIndex < rules->length()) { - UChar c = rules->charAt(ruleIndex); - if(PatternProps::isWhiteSpace(c)) { - ++ruleIndex; - continue; - } - switch(c) { - case 0x26: // '&' - parseRuleChain(errorCode); - break; - case 0x5b: // '[' - parseSetting(errorCode); - break; - case 0x23: // '#' starts a comment, until the end of the line - ruleIndex = skipComment(ruleIndex + 1); - break; - case 0x40: // '@' is equivalent to [backwards 2] - settings->setFlag(CollationSettings::BACKWARD_SECONDARY, - UCOL_ON, 0, errorCode); - ++ruleIndex; - break; - case 0x21: // '!' used to turn on Thai/Lao character reversal - // Accept but ignore. The root collator has contractions - // that are equivalent to the character reversal, where appropriate. - ++ruleIndex; - break; - default: - setParseError("expected a reset or setting or comment", errorCode); - break; - } - if(U_FAILURE(errorCode)) { return; } - } -} - -void -CollationRuleParser::parseRuleChain(UErrorCode &errorCode) { - int32_t resetStrength = parseResetAndPosition(errorCode); - UBool isFirstRelation = true; - for(;;) { - int32_t result = parseRelationOperator(errorCode); - if(U_FAILURE(errorCode)) { return; } - if(result < 0) { - if(ruleIndex < rules->length() && rules->charAt(ruleIndex) == 0x23) { - // '#' starts a comment, until the end of the line - ruleIndex = skipComment(ruleIndex + 1); - continue; - } - if(isFirstRelation) { - setParseError("reset not followed by a relation", errorCode); - } - return; - } - int32_t strength = result & STRENGTH_MASK; - if(resetStrength < UCOL_IDENTICAL) { - // reset-before rule chain - if(isFirstRelation) { - if(strength != resetStrength) { - setParseError("reset-before strength differs from its first relation", errorCode); - return; - } - } else { - if(strength < resetStrength) { - setParseError("reset-before strength followed by a stronger relation", errorCode); - return; - } - } - } - int32_t i = ruleIndex + (result >> OFFSET_SHIFT); // skip over the relation operator - if((result & STARRED_FLAG) == 0) { - parseRelationStrings(strength, i, errorCode); - } else { - parseStarredCharacters(strength, i, errorCode); - } - if(U_FAILURE(errorCode)) { return; } - isFirstRelation = false; - } -} - -int32_t -CollationRuleParser::parseResetAndPosition(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return UCOL_DEFAULT; } - int32_t i = skipWhiteSpace(ruleIndex + 1); - int32_t j; - UChar c; - int32_t resetStrength; - if(rules->compare(i, BEFORE_LENGTH, BEFORE, 0, BEFORE_LENGTH) == 0 && - (j = i + BEFORE_LENGTH) < rules->length() && - PatternProps::isWhiteSpace(rules->charAt(j)) && - ((j = skipWhiteSpace(j + 1)) + 1) < rules->length() && - 0x31 <= (c = rules->charAt(j)) && c <= 0x33 && - rules->charAt(j + 1) == 0x5d) { - // &[before n] with n=1 or 2 or 3 - resetStrength = UCOL_PRIMARY + (c - 0x31); - i = skipWhiteSpace(j + 2); - } else { - resetStrength = UCOL_IDENTICAL; - } - if(i >= rules->length()) { - setParseError("reset without position", errorCode); - return UCOL_DEFAULT; - } - UnicodeString str; - if(rules->charAt(i) == 0x5b) { // '[' - i = parseSpecialPosition(i, str, errorCode); - } else { - i = parseTailoringString(i, str, errorCode); - } - sink->addReset(resetStrength, str, errorReason, errorCode); - if(U_FAILURE(errorCode)) { setErrorContext(); } - ruleIndex = i; - return resetStrength; -} - -int32_t -CollationRuleParser::parseRelationOperator(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return UCOL_DEFAULT; } - ruleIndex = skipWhiteSpace(ruleIndex); - if(ruleIndex >= rules->length()) { return UCOL_DEFAULT; } - int32_t strength; - int32_t i = ruleIndex; - UChar c = rules->charAt(i++); - switch(c) { - case 0x3c: // '<' - if(i < rules->length() && rules->charAt(i) == 0x3c) { // << - ++i; - if(i < rules->length() && rules->charAt(i) == 0x3c) { // <<< - ++i; - if(i < rules->length() && rules->charAt(i) == 0x3c) { // <<<< - ++i; - strength = UCOL_QUATERNARY; - } else { - strength = UCOL_TERTIARY; - } - } else { - strength = UCOL_SECONDARY; - } - } else { - strength = UCOL_PRIMARY; - } - if(i < rules->length() && rules->charAt(i) == 0x2a) { // '*' - ++i; - strength |= STARRED_FLAG; - } - break; - case 0x3b: // ';' same as << - strength = UCOL_SECONDARY; - break; - case 0x2c: // ',' same as <<< - strength = UCOL_TERTIARY; - break; - case 0x3d: // '=' - strength = UCOL_IDENTICAL; - if(i < rules->length() && rules->charAt(i) == 0x2a) { // '*' - ++i; - strength |= STARRED_FLAG; - } - break; - default: - return UCOL_DEFAULT; - } - return ((i - ruleIndex) << OFFSET_SHIFT) | strength; -} - -void -CollationRuleParser::parseRelationStrings(int32_t strength, int32_t i, UErrorCode &errorCode) { - // Parse - // prefix | str / extension - // where prefix and extension are optional. - UnicodeString prefix, str, extension; - i = parseTailoringString(i, str, errorCode); - if(U_FAILURE(errorCode)) { return; } - UChar next = (i < rules->length()) ? rules->charAt(i) : 0; - if(next == 0x7c) { // '|' separates the context prefix from the string. - prefix = str; - i = parseTailoringString(i + 1, str, errorCode); - if(U_FAILURE(errorCode)) { return; } - next = (i < rules->length()) ? rules->charAt(i) : 0; - } - if(next == 0x2f) { // '/' separates the string from the extension. - i = parseTailoringString(i + 1, extension, errorCode); - } - if(!prefix.isEmpty()) { - UChar32 prefix0 = prefix.char32At(0); - UChar32 c = str.char32At(0); - if(!nfc.hasBoundaryBefore(prefix0) || !nfc.hasBoundaryBefore(c)) { - setParseError("in 'prefix|str', prefix and str must each start with an NFC boundary", - errorCode); - return; - } - } - sink->addRelation(strength, prefix, str, extension, errorReason, errorCode); - if(U_FAILURE(errorCode)) { setErrorContext(); } - ruleIndex = i; -} - -void -CollationRuleParser::parseStarredCharacters(int32_t strength, int32_t i, UErrorCode &errorCode) { - UnicodeString empty, raw; - i = parseString(skipWhiteSpace(i), raw, errorCode); - if(U_FAILURE(errorCode)) { return; } - if(raw.isEmpty()) { - setParseError("missing starred-relation string", errorCode); - return; - } - UChar32 prev = -1; - int32_t j = 0; - for(;;) { - while(j < raw.length()) { - UChar32 c = raw.char32At(j); - if(!nfd.isInert(c)) { - setParseError("starred-relation string is not all NFD-inert", errorCode); - return; - } - sink->addRelation(strength, empty, UnicodeString(c), empty, errorReason, errorCode); - if(U_FAILURE(errorCode)) { - setErrorContext(); - return; - } - j += U16_LENGTH(c); - prev = c; - } - if(i >= rules->length() || rules->charAt(i) != 0x2d) { // '-' - break; - } - if(prev < 0) { - setParseError("range without start in starred-relation string", errorCode); - return; - } - i = parseString(i + 1, raw, errorCode); - if(U_FAILURE(errorCode)) { return; } - if(raw.isEmpty()) { - setParseError("range without end in starred-relation string", errorCode); - return; - } - UChar32 c = raw.char32At(0); - if(c < prev) { - setParseError("range start greater than end in starred-relation string", errorCode); - return; - } - // range prev-c - UnicodeString s; - while(++prev <= c) { - if(!nfd.isInert(prev)) { - setParseError("starred-relation string range is not all NFD-inert", errorCode); - return; - } - if(U_IS_SURROGATE(prev)) { - setParseError("starred-relation string range contains a surrogate", errorCode); - return; - } - if(0xfffd <= prev && prev <= 0xffff) { - setParseError("starred-relation string range contains U+FFFD, U+FFFE or U+FFFF", errorCode); - return; - } - s.setTo(prev); - sink->addRelation(strength, empty, s, empty, errorReason, errorCode); - if(U_FAILURE(errorCode)) { - setErrorContext(); - return; - } - } - prev = -1; - j = U16_LENGTH(c); - } - ruleIndex = skipWhiteSpace(i); -} - -int32_t -CollationRuleParser::parseTailoringString(int32_t i, UnicodeString &raw, UErrorCode &errorCode) { - i = parseString(skipWhiteSpace(i), raw, errorCode); - if(U_SUCCESS(errorCode) && raw.isEmpty()) { - setParseError("missing relation string", errorCode); - } - return skipWhiteSpace(i); -} - -int32_t -CollationRuleParser::parseString(int32_t i, UnicodeString &raw, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return i; } - raw.remove(); - while(i < rules->length()) { - UChar32 c = rules->charAt(i++); - if(isSyntaxChar(c)) { - if(c == 0x27) { // apostrophe - if(i < rules->length() && rules->charAt(i) == 0x27) { - // Double apostrophe, encodes a single one. - raw.append((UChar)0x27); - ++i; - continue; - } - // Quote literal text until the next single apostrophe. - for(;;) { - if(i == rules->length()) { - setParseError("quoted literal text missing terminating apostrophe", errorCode); - return i; - } - c = rules->charAt(i++); - if(c == 0x27) { - if(i < rules->length() && rules->charAt(i) == 0x27) { - // Double apostrophe inside quoted literal text, - // still encodes a single apostrophe. - ++i; - } else { - break; - } - } - raw.append((UChar)c); - } - } else if(c == 0x5c) { // backslash - if(i == rules->length()) { - setParseError("backslash escape at the end of the rule string", errorCode); - return i; - } - c = rules->char32At(i); - raw.append(c); - i += U16_LENGTH(c); - } else { - // Any other syntax character terminates a string. - --i; - break; - } - } else if(PatternProps::isWhiteSpace(c)) { - // Unquoted white space terminates a string. - --i; - break; - } else { - raw.append((UChar)c); - } - } - for(int32_t j = 0; j < raw.length();) { - UChar32 c = raw.char32At(j); - if(U_IS_SURROGATE(c)) { - setParseError("string contains an unpaired surrogate", errorCode); - return i; - } - if(0xfffd <= c && c <= 0xffff) { - setParseError("string contains U+FFFD, U+FFFE or U+FFFF", errorCode); - return i; - } - j += U16_LENGTH(c); - } - return i; -} - -namespace { - -static const char *const positions[] = { - "first tertiary ignorable", - "last tertiary ignorable", - "first secondary ignorable", - "last secondary ignorable", - "first primary ignorable", - "last primary ignorable", - "first variable", - "last variable", - "first regular", - "last regular", - "first implicit", - "last implicit", - "first trailing", - "last trailing" -}; - -} // namespace - -int32_t -CollationRuleParser::parseSpecialPosition(int32_t i, UnicodeString &str, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - UnicodeString raw; - int32_t j = readWords(i + 1, raw); - if(j > i && rules->charAt(j) == 0x5d && !raw.isEmpty()) { // words end with ] - ++j; - for(int32_t pos = 0; pos < UPRV_LENGTHOF(positions); ++pos) { - if(raw == UnicodeString(positions[pos], -1, US_INV)) { - str.setTo((UChar)POS_LEAD).append((UChar)(POS_BASE + pos)); - return j; - } - } - if(raw == UNICODE_STRING_SIMPLE("top")) { - str.setTo((UChar)POS_LEAD).append((UChar)(POS_BASE + LAST_REGULAR)); - return j; - } - if(raw == UNICODE_STRING_SIMPLE("variable top")) { - str.setTo((UChar)POS_LEAD).append((UChar)(POS_BASE + LAST_VARIABLE)); - return j; - } - } - setParseError("not a valid special reset position", errorCode); - return i; -} - -void -CollationRuleParser::parseSetting(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - UnicodeString raw; - int32_t i = ruleIndex + 1; - int32_t j = readWords(i, raw); - if(j <= i || raw.isEmpty()) { - setParseError("expected a setting/option at '['", errorCode); - } - if(rules->charAt(j) == 0x5d) { // words end with ] - ++j; - if(raw.startsWith(UNICODE_STRING_SIMPLE("reorder")) && - (raw.length() == 7 || raw.charAt(7) == 0x20)) { - parseReordering(raw, errorCode); - ruleIndex = j; - return; - } - if(raw == UNICODE_STRING_SIMPLE("backwards 2")) { - settings->setFlag(CollationSettings::BACKWARD_SECONDARY, - UCOL_ON, 0, errorCode); - ruleIndex = j; - return; - } - UnicodeString v; - int32_t valueIndex = raw.lastIndexOf((UChar)0x20); - if(valueIndex >= 0) { - v.setTo(raw, valueIndex + 1); - raw.truncate(valueIndex); - } - if(raw == UNICODE_STRING_SIMPLE("strength") && v.length() == 1) { - int32_t value = UCOL_DEFAULT; - UChar c = v.charAt(0); - if(0x31 <= c && c <= 0x34) { // 1..4 - value = UCOL_PRIMARY + (c - 0x31); - } else if(c == 0x49) { // 'I' - value = UCOL_IDENTICAL; - } - if(value != UCOL_DEFAULT) { - settings->setStrength(value, 0, errorCode); - ruleIndex = j; - return; - } - } else if(raw == UNICODE_STRING_SIMPLE("alternate")) { - UColAttributeValue value = UCOL_DEFAULT; - if(v == UNICODE_STRING_SIMPLE("non-ignorable")) { - value = UCOL_NON_IGNORABLE; - } else if(v == UNICODE_STRING_SIMPLE("shifted")) { - value = UCOL_SHIFTED; - } - if(value != UCOL_DEFAULT) { - settings->setAlternateHandling(value, 0, errorCode); - ruleIndex = j; - return; - } - } else if(raw == UNICODE_STRING_SIMPLE("maxVariable")) { - int32_t value = UCOL_DEFAULT; - if(v == UNICODE_STRING_SIMPLE("space")) { - value = CollationSettings::MAX_VAR_SPACE; - } else if(v == UNICODE_STRING_SIMPLE("punct")) { - value = CollationSettings::MAX_VAR_PUNCT; - } else if(v == UNICODE_STRING_SIMPLE("symbol")) { - value = CollationSettings::MAX_VAR_SYMBOL; - } else if(v == UNICODE_STRING_SIMPLE("currency")) { - value = CollationSettings::MAX_VAR_CURRENCY; - } - if(value != UCOL_DEFAULT) { - settings->setMaxVariable(value, 0, errorCode); - settings->variableTop = baseData->getLastPrimaryForGroup( - UCOL_REORDER_CODE_FIRST + value); - U_ASSERT(settings->variableTop != 0); - ruleIndex = j; - return; - } - } else if(raw == UNICODE_STRING_SIMPLE("caseFirst")) { - UColAttributeValue value = UCOL_DEFAULT; - if(v == UNICODE_STRING_SIMPLE("off")) { - value = UCOL_OFF; - } else if(v == UNICODE_STRING_SIMPLE("lower")) { - value = UCOL_LOWER_FIRST; - } else if(v == UNICODE_STRING_SIMPLE("upper")) { - value = UCOL_UPPER_FIRST; - } - if(value != UCOL_DEFAULT) { - settings->setCaseFirst(value, 0, errorCode); - ruleIndex = j; - return; - } - } else if(raw == UNICODE_STRING_SIMPLE("caseLevel")) { - UColAttributeValue value = getOnOffValue(v); - if(value != UCOL_DEFAULT) { - settings->setFlag(CollationSettings::CASE_LEVEL, value, 0, errorCode); - ruleIndex = j; - return; - } - } else if(raw == UNICODE_STRING_SIMPLE("normalization")) { - UColAttributeValue value = getOnOffValue(v); - if(value != UCOL_DEFAULT) { - settings->setFlag(CollationSettings::CHECK_FCD, value, 0, errorCode); - ruleIndex = j; - return; - } - } else if(raw == UNICODE_STRING_SIMPLE("numericOrdering")) { - UColAttributeValue value = getOnOffValue(v); - if(value != UCOL_DEFAULT) { - settings->setFlag(CollationSettings::NUMERIC, value, 0, errorCode); - ruleIndex = j; - return; - } - } else if(raw == UNICODE_STRING_SIMPLE("hiraganaQ")) { - UColAttributeValue value = getOnOffValue(v); - if(value != UCOL_DEFAULT) { - if(value == UCOL_ON) { - setParseError("[hiraganaQ on] is not supported", errorCode); - } - ruleIndex = j; - return; - } - } else if(raw == UNICODE_STRING_SIMPLE("import")) { - CharString lang; - lang.appendInvariantChars(v, errorCode); - if(errorCode == U_MEMORY_ALLOCATION_ERROR) { return; } - // BCP 47 language tag -> ICU locale ID - char localeID[ULOC_FULLNAME_CAPACITY]; - int32_t parsedLength; - int32_t length = uloc_forLanguageTag(lang.data(), localeID, ULOC_FULLNAME_CAPACITY, - &parsedLength, &errorCode); - if(U_FAILURE(errorCode) || - parsedLength != lang.length() || length >= ULOC_FULLNAME_CAPACITY) { - errorCode = U_ZERO_ERROR; - setParseError("expected language tag in [import langTag]", errorCode); - return; - } - // localeID minus all keywords - char baseID[ULOC_FULLNAME_CAPACITY]; - length = uloc_getBaseName(localeID, baseID, ULOC_FULLNAME_CAPACITY, &errorCode); - if(U_FAILURE(errorCode) || length >= ULOC_KEYWORDS_CAPACITY) { - errorCode = U_ZERO_ERROR; - setParseError("expected language tag in [import langTag]", errorCode); - return; - } - if(length == 0) { - uprv_strcpy(baseID, "root"); - } else if(*baseID == '_') { - uprv_memmove(baseID + 3, baseID, length + 1); - uprv_memcpy(baseID, "und", 3); - } - // @collation=type, or length=0 if not specified - char collationType[ULOC_KEYWORDS_CAPACITY]; - length = uloc_getKeywordValue(localeID, "collation", - collationType, ULOC_KEYWORDS_CAPACITY, - &errorCode); - if(U_FAILURE(errorCode) || length >= ULOC_KEYWORDS_CAPACITY) { - errorCode = U_ZERO_ERROR; - setParseError("expected language tag in [import langTag]", errorCode); - return; - } - if(importer == NULL) { - setParseError("[import langTag] is not supported", errorCode); - } else { - UnicodeString importedRules; - importer->getRules(baseID, length > 0 ? collationType : "standard", - importedRules, errorReason, errorCode); - if(U_FAILURE(errorCode)) { - if(errorReason == NULL) { - errorReason = "[import langTag] failed"; - } - setErrorContext(); - return; - } - const UnicodeString *outerRules = rules; - int32_t outerRuleIndex = ruleIndex; - parse(importedRules, errorCode); - if(U_FAILURE(errorCode)) { - if(parseError != NULL) { - parseError->offset = outerRuleIndex; - } - } - rules = outerRules; - ruleIndex = j; - } - return; - } - } else if(rules->charAt(j) == 0x5b) { // words end with [ - UnicodeSet set; - j = parseUnicodeSet(j, set, errorCode); - if(U_FAILURE(errorCode)) { return; } - if(raw == UNICODE_STRING_SIMPLE("optimize")) { - sink->optimize(set, errorReason, errorCode); - if(U_FAILURE(errorCode)) { setErrorContext(); } - ruleIndex = j; - return; - } else if(raw == UNICODE_STRING_SIMPLE("suppressContractions")) { - sink->suppressContractions(set, errorReason, errorCode); - if(U_FAILURE(errorCode)) { setErrorContext(); } - ruleIndex = j; - return; - } - } - setParseError("not a valid setting/option", errorCode); -} - -void -CollationRuleParser::parseReordering(const UnicodeString &raw, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - int32_t i = 7; // after "reorder" - if(i == raw.length()) { - // empty [reorder] with no codes - settings->resetReordering(); - return; - } - // Parse the codes in [reorder aa bb cc]. - UVector32 reorderCodes(errorCode); - if(U_FAILURE(errorCode)) { return; } - CharString word; - while(i < raw.length()) { - ++i; // skip the word-separating space - int32_t limit = raw.indexOf((UChar)0x20, i); - if(limit < 0) { limit = raw.length(); } - word.clear().appendInvariantChars(raw.tempSubStringBetween(i, limit), errorCode); - if(U_FAILURE(errorCode)) { return; } - int32_t code = getReorderCode(word.data()); - if(code < 0) { - setParseError("unknown script or reorder code", errorCode); - return; - } - reorderCodes.addElement(code, errorCode); - if(U_FAILURE(errorCode)) { return; } - i = limit; - } - settings->setReordering(*baseData, reorderCodes.getBuffer(), reorderCodes.size(), errorCode); -} - -static const char *const gSpecialReorderCodes[] = { - "space", "punct", "symbol", "currency", "digit" -}; - -int32_t -CollationRuleParser::getReorderCode(const char *word) { - for(int32_t i = 0; i < UPRV_LENGTHOF(gSpecialReorderCodes); ++i) { - if(uprv_stricmp(word, gSpecialReorderCodes[i]) == 0) { - return UCOL_REORDER_CODE_FIRST + i; - } - } - int32_t script = u_getPropertyValueEnum(UCHAR_SCRIPT, word); - if(script >= 0) { - return script; - } - if(uprv_stricmp(word, "others") == 0) { - return UCOL_REORDER_CODE_OTHERS; // same as Zzzz = USCRIPT_UNKNOWN - } - return -1; -} - -UColAttributeValue -CollationRuleParser::getOnOffValue(const UnicodeString &s) { - if(s == UNICODE_STRING_SIMPLE("on")) { - return UCOL_ON; - } else if(s == UNICODE_STRING_SIMPLE("off")) { - return UCOL_OFF; - } else { - return UCOL_DEFAULT; - } -} - -int32_t -CollationRuleParser::parseUnicodeSet(int32_t i, UnicodeSet &set, UErrorCode &errorCode) { - // Collect a UnicodeSet pattern between a balanced pair of [brackets]. - int32_t level = 0; - int32_t j = i; - for(;;) { - if(j == rules->length()) { - setParseError("unbalanced UnicodeSet pattern brackets", errorCode); - return j; - } - UChar c = rules->charAt(j++); - if(c == 0x5b) { // '[' - ++level; - } else if(c == 0x5d) { // ']' - if(--level == 0) { break; } - } - } - set.applyPattern(rules->tempSubStringBetween(i, j), errorCode); - if(U_FAILURE(errorCode)) { - errorCode = U_ZERO_ERROR; - setParseError("not a valid UnicodeSet pattern", errorCode); - return j; - } - j = skipWhiteSpace(j); - if(j == rules->length() || rules->charAt(j) != 0x5d) { - setParseError("missing option-terminating ']' after UnicodeSet pattern", errorCode); - return j; - } - return ++j; -} - -int32_t -CollationRuleParser::readWords(int32_t i, UnicodeString &raw) const { - static const UChar sp = 0x20; - raw.remove(); - i = skipWhiteSpace(i); - for(;;) { - if(i >= rules->length()) { return 0; } - UChar c = rules->charAt(i); - if(isSyntaxChar(c) && c != 0x2d && c != 0x5f) { // syntax except -_ - if(raw.isEmpty()) { return i; } - if(raw.endsWith(&sp, 1)) { // remove trailing space - raw.truncate(raw.length() - 1); - } - return i; - } - if(PatternProps::isWhiteSpace(c)) { - raw.append(sp); - i = skipWhiteSpace(i + 1); - } else { - raw.append(c); - ++i; - } - } -} - -int32_t -CollationRuleParser::skipComment(int32_t i) const { - // skip to past the newline - while(i < rules->length()) { - UChar c = rules->charAt(i++); - // LF or FF or CR or NEL or LS or PS - if(c == 0xa || c == 0xc || c == 0xd || c == 0x85 || c == 0x2028 || c == 0x2029) { - // Unicode Newline Guidelines: "A readline function should stop at NLF, LS, FF, or PS." - // NLF (new line function) = CR or LF or CR+LF or NEL. - // No need to collect all of CR+LF because a following LF will be ignored anyway. - break; - } - } - return i; -} - -void -CollationRuleParser::setParseError(const char *reason, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - // Error code consistent with the old parser (from ca. 2001), - // rather than U_PARSE_ERROR; - errorCode = U_INVALID_FORMAT_ERROR; - errorReason = reason; - if(parseError != NULL) { setErrorContext(); } -} - -void -CollationRuleParser::setErrorContext() { - if(parseError == NULL) { return; } - - // Note: This relies on the calling code maintaining the ruleIndex - // at a position that is useful for debugging. - // For example, at the beginning of a reset or relation etc. - parseError->offset = ruleIndex; - parseError->line = 0; // We are not counting line numbers. - - // before ruleIndex - int32_t start = ruleIndex - (U_PARSE_CONTEXT_LEN - 1); - if(start < 0) { - start = 0; - } else if(start > 0 && U16_IS_TRAIL(rules->charAt(start))) { - ++start; - } - int32_t length = ruleIndex - start; - rules->extract(start, length, parseError->preContext); - parseError->preContext[length] = 0; - - // starting from ruleIndex - length = rules->length() - ruleIndex; - if(length >= U_PARSE_CONTEXT_LEN) { - length = U_PARSE_CONTEXT_LEN - 1; - if(U16_IS_LEAD(rules->charAt(ruleIndex + length - 1))) { - --length; - } - } - rules->extract(ruleIndex, length, parseError->postContext); - parseError->postContext[length] = 0; -} - -UBool -CollationRuleParser::isSyntaxChar(UChar32 c) { - return 0x21 <= c && c <= 0x7e && - (c <= 0x2f || (0x3a <= c && c <= 0x40) || - (0x5b <= c && c <= 0x60) || (0x7b <= c)); -} - -int32_t -CollationRuleParser::skipWhiteSpace(int32_t i) const { - while(i < rules->length() && PatternProps::isWhiteSpace(rules->charAt(i))) { - ++i; - } - return i; -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationruleparser.cpp +* +* (replaced the former ucol_tok.cpp) +* +* created on: 2013apr10 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/normalizer2.h" +#include "unicode/parseerr.h" +#include "unicode/uchar.h" +#include "unicode/ucol.h" +#include "unicode/uloc.h" +#include "unicode/unistr.h" +#include "unicode/utf16.h" +#include "charstr.h" +#include "cmemory.h" +#include "collation.h" +#include "collationdata.h" +#include "collationruleparser.h" +#include "collationsettings.h" +#include "collationtailoring.h" +#include "cstring.h" +#include "patternprops.h" +#include "uassert.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +namespace { + +static const char16_t BEFORE[] = { 0x5b, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0 }; // "[before" +const int32_t BEFORE_LENGTH = 7; + +} // namespace + +CollationRuleParser::Sink::~Sink() {} + +void +CollationRuleParser::Sink::suppressContractions(const UnicodeSet &, const char *&, UErrorCode &) {} + +void +CollationRuleParser::Sink::optimize(const UnicodeSet &, const char *&, UErrorCode &) {} + +CollationRuleParser::Importer::~Importer() {} + +CollationRuleParser::CollationRuleParser(const CollationData *base, UErrorCode &errorCode) + : nfd(*Normalizer2::getNFDInstance(errorCode)), + nfc(*Normalizer2::getNFCInstance(errorCode)), + rules(nullptr), baseData(base), settings(nullptr), + parseError(nullptr), errorReason(nullptr), + sink(nullptr), importer(nullptr), + ruleIndex(0) { +} + +CollationRuleParser::~CollationRuleParser() { +} + +void +CollationRuleParser::parse(const UnicodeString &ruleString, + CollationSettings &outSettings, + UParseError *outParseError, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + settings = &outSettings; + parseError = outParseError; + if(parseError != nullptr) { + parseError->line = 0; + parseError->offset = -1; + parseError->preContext[0] = 0; + parseError->postContext[0] = 0; + } + errorReason = nullptr; + parse(ruleString, errorCode); +} + +void +CollationRuleParser::parse(const UnicodeString &ruleString, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + rules = &ruleString; + ruleIndex = 0; + + while(ruleIndex < rules->length()) { + char16_t c = rules->charAt(ruleIndex); + if(PatternProps::isWhiteSpace(c)) { + ++ruleIndex; + continue; + } + switch(c) { + case 0x26: // '&' + parseRuleChain(errorCode); + break; + case 0x5b: // '[' + parseSetting(errorCode); + break; + case 0x23: // '#' starts a comment, until the end of the line + ruleIndex = skipComment(ruleIndex + 1); + break; + case 0x40: // '@' is equivalent to [backwards 2] + settings->setFlag(CollationSettings::BACKWARD_SECONDARY, + UCOL_ON, 0, errorCode); + ++ruleIndex; + break; + case 0x21: // '!' used to turn on Thai/Lao character reversal + // Accept but ignore. The root collator has contractions + // that are equivalent to the character reversal, where appropriate. + ++ruleIndex; + break; + default: + setParseError("expected a reset or setting or comment", errorCode); + break; + } + if(U_FAILURE(errorCode)) { return; } + } +} + +void +CollationRuleParser::parseRuleChain(UErrorCode &errorCode) { + int32_t resetStrength = parseResetAndPosition(errorCode); + UBool isFirstRelation = true; + for(;;) { + int32_t result = parseRelationOperator(errorCode); + if(U_FAILURE(errorCode)) { return; } + if(result < 0) { + if(ruleIndex < rules->length() && rules->charAt(ruleIndex) == 0x23) { + // '#' starts a comment, until the end of the line + ruleIndex = skipComment(ruleIndex + 1); + continue; + } + if(isFirstRelation) { + setParseError("reset not followed by a relation", errorCode); + } + return; + } + int32_t strength = result & STRENGTH_MASK; + if(resetStrength < UCOL_IDENTICAL) { + // reset-before rule chain + if(isFirstRelation) { + if(strength != resetStrength) { + setParseError("reset-before strength differs from its first relation", errorCode); + return; + } + } else { + if(strength < resetStrength) { + setParseError("reset-before strength followed by a stronger relation", errorCode); + return; + } + } + } + int32_t i = ruleIndex + (result >> OFFSET_SHIFT); // skip over the relation operator + if((result & STARRED_FLAG) == 0) { + parseRelationStrings(strength, i, errorCode); + } else { + parseStarredCharacters(strength, i, errorCode); + } + if(U_FAILURE(errorCode)) { return; } + isFirstRelation = false; + } +} + +int32_t +CollationRuleParser::parseResetAndPosition(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return UCOL_DEFAULT; } + int32_t i = skipWhiteSpace(ruleIndex + 1); + int32_t j; + char16_t c; + int32_t resetStrength; + if(rules->compare(i, BEFORE_LENGTH, BEFORE, 0, BEFORE_LENGTH) == 0 && + (j = i + BEFORE_LENGTH) < rules->length() && + PatternProps::isWhiteSpace(rules->charAt(j)) && + ((j = skipWhiteSpace(j + 1)) + 1) < rules->length() && + 0x31 <= (c = rules->charAt(j)) && c <= 0x33 && + rules->charAt(j + 1) == 0x5d) { + // &[before n] with n=1 or 2 or 3 + resetStrength = UCOL_PRIMARY + (c - 0x31); + i = skipWhiteSpace(j + 2); + } else { + resetStrength = UCOL_IDENTICAL; + } + if(i >= rules->length()) { + setParseError("reset without position", errorCode); + return UCOL_DEFAULT; + } + UnicodeString str; + if(rules->charAt(i) == 0x5b) { // '[' + i = parseSpecialPosition(i, str, errorCode); + } else { + i = parseTailoringString(i, str, errorCode); + } + sink->addReset(resetStrength, str, errorReason, errorCode); + if(U_FAILURE(errorCode)) { setErrorContext(); } + ruleIndex = i; + return resetStrength; +} + +int32_t +CollationRuleParser::parseRelationOperator(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return UCOL_DEFAULT; } + ruleIndex = skipWhiteSpace(ruleIndex); + if(ruleIndex >= rules->length()) { return UCOL_DEFAULT; } + int32_t strength; + int32_t i = ruleIndex; + char16_t c = rules->charAt(i++); + switch(c) { + case 0x3c: // '<' + if(i < rules->length() && rules->charAt(i) == 0x3c) { // << + ++i; + if(i < rules->length() && rules->charAt(i) == 0x3c) { // <<< + ++i; + if(i < rules->length() && rules->charAt(i) == 0x3c) { // <<<< + ++i; + strength = UCOL_QUATERNARY; + } else { + strength = UCOL_TERTIARY; + } + } else { + strength = UCOL_SECONDARY; + } + } else { + strength = UCOL_PRIMARY; + } + if(i < rules->length() && rules->charAt(i) == 0x2a) { // '*' + ++i; + strength |= STARRED_FLAG; + } + break; + case 0x3b: // ';' same as << + strength = UCOL_SECONDARY; + break; + case 0x2c: // ',' same as <<< + strength = UCOL_TERTIARY; + break; + case 0x3d: // '=' + strength = UCOL_IDENTICAL; + if(i < rules->length() && rules->charAt(i) == 0x2a) { // '*' + ++i; + strength |= STARRED_FLAG; + } + break; + default: + return UCOL_DEFAULT; + } + return ((i - ruleIndex) << OFFSET_SHIFT) | strength; +} + +void +CollationRuleParser::parseRelationStrings(int32_t strength, int32_t i, UErrorCode &errorCode) { + // Parse + // prefix | str / extension + // where prefix and extension are optional. + UnicodeString prefix, str, extension; + i = parseTailoringString(i, str, errorCode); + if(U_FAILURE(errorCode)) { return; } + char16_t next = (i < rules->length()) ? rules->charAt(i) : 0; + if(next == 0x7c) { // '|' separates the context prefix from the string. + prefix = str; + i = parseTailoringString(i + 1, str, errorCode); + if(U_FAILURE(errorCode)) { return; } + next = (i < rules->length()) ? rules->charAt(i) : 0; + } + if(next == 0x2f) { // '/' separates the string from the extension. + i = parseTailoringString(i + 1, extension, errorCode); + } + if(!prefix.isEmpty()) { + UChar32 prefix0 = prefix.char32At(0); + UChar32 c = str.char32At(0); + if(!nfc.hasBoundaryBefore(prefix0) || !nfc.hasBoundaryBefore(c)) { + setParseError("in 'prefix|str', prefix and str must each start with an NFC boundary", + errorCode); + return; + } + } + sink->addRelation(strength, prefix, str, extension, errorReason, errorCode); + if(U_FAILURE(errorCode)) { setErrorContext(); } + ruleIndex = i; +} + +void +CollationRuleParser::parseStarredCharacters(int32_t strength, int32_t i, UErrorCode &errorCode) { + UnicodeString empty, raw; + i = parseString(skipWhiteSpace(i), raw, errorCode); + if(U_FAILURE(errorCode)) { return; } + if(raw.isEmpty()) { + setParseError("missing starred-relation string", errorCode); + return; + } + UChar32 prev = -1; + int32_t j = 0; + for(;;) { + while(j < raw.length()) { + UChar32 c = raw.char32At(j); + if(!nfd.isInert(c)) { + setParseError("starred-relation string is not all NFD-inert", errorCode); + return; + } + sink->addRelation(strength, empty, UnicodeString(c), empty, errorReason, errorCode); + if(U_FAILURE(errorCode)) { + setErrorContext(); + return; + } + j += U16_LENGTH(c); + prev = c; + } + if(i >= rules->length() || rules->charAt(i) != 0x2d) { // '-' + break; + } + if(prev < 0) { + setParseError("range without start in starred-relation string", errorCode); + return; + } + i = parseString(i + 1, raw, errorCode); + if(U_FAILURE(errorCode)) { return; } + if(raw.isEmpty()) { + setParseError("range without end in starred-relation string", errorCode); + return; + } + UChar32 c = raw.char32At(0); + if(c < prev) { + setParseError("range start greater than end in starred-relation string", errorCode); + return; + } + // range prev-c + UnicodeString s; + while(++prev <= c) { + if(!nfd.isInert(prev)) { + setParseError("starred-relation string range is not all NFD-inert", errorCode); + return; + } + if(U_IS_SURROGATE(prev)) { + setParseError("starred-relation string range contains a surrogate", errorCode); + return; + } + if(0xfffd <= prev && prev <= 0xffff) { + setParseError("starred-relation string range contains U+FFFD, U+FFFE or U+FFFF", errorCode); + return; + } + s.setTo(prev); + sink->addRelation(strength, empty, s, empty, errorReason, errorCode); + if(U_FAILURE(errorCode)) { + setErrorContext(); + return; + } + } + prev = -1; + j = U16_LENGTH(c); + } + ruleIndex = skipWhiteSpace(i); +} + +int32_t +CollationRuleParser::parseTailoringString(int32_t i, UnicodeString &raw, UErrorCode &errorCode) { + i = parseString(skipWhiteSpace(i), raw, errorCode); + if(U_SUCCESS(errorCode) && raw.isEmpty()) { + setParseError("missing relation string", errorCode); + } + return skipWhiteSpace(i); +} + +int32_t +CollationRuleParser::parseString(int32_t i, UnicodeString &raw, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return i; } + raw.remove(); + while(i < rules->length()) { + UChar32 c = rules->charAt(i++); + if(isSyntaxChar(c)) { + if(c == 0x27) { // apostrophe + if(i < rules->length() && rules->charAt(i) == 0x27) { + // Double apostrophe, encodes a single one. + raw.append((char16_t)0x27); + ++i; + continue; + } + // Quote literal text until the next single apostrophe. + for(;;) { + if(i == rules->length()) { + setParseError("quoted literal text missing terminating apostrophe", errorCode); + return i; + } + c = rules->charAt(i++); + if(c == 0x27) { + if(i < rules->length() && rules->charAt(i) == 0x27) { + // Double apostrophe inside quoted literal text, + // still encodes a single apostrophe. + ++i; + } else { + break; + } + } + raw.append((char16_t)c); + } + } else if(c == 0x5c) { // backslash + if(i == rules->length()) { + setParseError("backslash escape at the end of the rule string", errorCode); + return i; + } + c = rules->char32At(i); + raw.append(c); + i += U16_LENGTH(c); + } else { + // Any other syntax character terminates a string. + --i; + break; + } + } else if(PatternProps::isWhiteSpace(c)) { + // Unquoted white space terminates a string. + --i; + break; + } else { + raw.append((char16_t)c); + } + } + for(int32_t j = 0; j < raw.length();) { + UChar32 c = raw.char32At(j); + if(U_IS_SURROGATE(c)) { + setParseError("string contains an unpaired surrogate", errorCode); + return i; + } + if(0xfffd <= c && c <= 0xffff) { + setParseError("string contains U+FFFD, U+FFFE or U+FFFF", errorCode); + return i; + } + j += U16_LENGTH(c); + } + return i; +} + +namespace { + +static const char *const positions[] = { + "first tertiary ignorable", + "last tertiary ignorable", + "first secondary ignorable", + "last secondary ignorable", + "first primary ignorable", + "last primary ignorable", + "first variable", + "last variable", + "first regular", + "last regular", + "first implicit", + "last implicit", + "first trailing", + "last trailing" +}; + +} // namespace + +int32_t +CollationRuleParser::parseSpecialPosition(int32_t i, UnicodeString &str, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + UnicodeString raw; + int32_t j = readWords(i + 1, raw); + if(j > i && rules->charAt(j) == 0x5d && !raw.isEmpty()) { // words end with ] + ++j; + for(int32_t pos = 0; pos < UPRV_LENGTHOF(positions); ++pos) { + if(raw == UnicodeString(positions[pos], -1, US_INV)) { + str.setTo((char16_t)POS_LEAD).append((char16_t)(POS_BASE + pos)); + return j; + } + } + if(raw == UNICODE_STRING_SIMPLE("top")) { + str.setTo((char16_t)POS_LEAD).append((char16_t)(POS_BASE + LAST_REGULAR)); + return j; + } + if(raw == UNICODE_STRING_SIMPLE("variable top")) { + str.setTo((char16_t)POS_LEAD).append((char16_t)(POS_BASE + LAST_VARIABLE)); + return j; + } + } + setParseError("not a valid special reset position", errorCode); + return i; +} + +void +CollationRuleParser::parseSetting(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + UnicodeString raw; + int32_t i = ruleIndex + 1; + int32_t j = readWords(i, raw); + if(j <= i || raw.isEmpty()) { + setParseError("expected a setting/option at '['", errorCode); + } + if(rules->charAt(j) == 0x5d) { // words end with ] + ++j; + if(raw.startsWith(UNICODE_STRING_SIMPLE("reorder")) && + (raw.length() == 7 || raw.charAt(7) == 0x20)) { + parseReordering(raw, errorCode); + ruleIndex = j; + return; + } + if(raw == UNICODE_STRING_SIMPLE("backwards 2")) { + settings->setFlag(CollationSettings::BACKWARD_SECONDARY, + UCOL_ON, 0, errorCode); + ruleIndex = j; + return; + } + UnicodeString v; + int32_t valueIndex = raw.lastIndexOf((char16_t)0x20); + if(valueIndex >= 0) { + v.setTo(raw, valueIndex + 1); + raw.truncate(valueIndex); + } + if(raw == UNICODE_STRING_SIMPLE("strength") && v.length() == 1) { + int32_t value = UCOL_DEFAULT; + char16_t c = v.charAt(0); + if(0x31 <= c && c <= 0x34) { // 1..4 + value = UCOL_PRIMARY + (c - 0x31); + } else if(c == 0x49) { // 'I' + value = UCOL_IDENTICAL; + } + if(value != UCOL_DEFAULT) { + settings->setStrength(value, 0, errorCode); + ruleIndex = j; + return; + } + } else if(raw == UNICODE_STRING_SIMPLE("alternate")) { + UColAttributeValue value = UCOL_DEFAULT; + if(v == UNICODE_STRING_SIMPLE("non-ignorable")) { + value = UCOL_NON_IGNORABLE; + } else if(v == UNICODE_STRING_SIMPLE("shifted")) { + value = UCOL_SHIFTED; + } + if(value != UCOL_DEFAULT) { + settings->setAlternateHandling(value, 0, errorCode); + ruleIndex = j; + return; + } + } else if(raw == UNICODE_STRING_SIMPLE("maxVariable")) { + int32_t value = UCOL_DEFAULT; + if(v == UNICODE_STRING_SIMPLE("space")) { + value = CollationSettings::MAX_VAR_SPACE; + } else if(v == UNICODE_STRING_SIMPLE("punct")) { + value = CollationSettings::MAX_VAR_PUNCT; + } else if(v == UNICODE_STRING_SIMPLE("symbol")) { + value = CollationSettings::MAX_VAR_SYMBOL; + } else if(v == UNICODE_STRING_SIMPLE("currency")) { + value = CollationSettings::MAX_VAR_CURRENCY; + } + if(value != UCOL_DEFAULT) { + settings->setMaxVariable(value, 0, errorCode); + settings->variableTop = baseData->getLastPrimaryForGroup( + UCOL_REORDER_CODE_FIRST + value); + U_ASSERT(settings->variableTop != 0); + ruleIndex = j; + return; + } + } else if(raw == UNICODE_STRING_SIMPLE("caseFirst")) { + UColAttributeValue value = UCOL_DEFAULT; + if(v == UNICODE_STRING_SIMPLE("off")) { + value = UCOL_OFF; + } else if(v == UNICODE_STRING_SIMPLE("lower")) { + value = UCOL_LOWER_FIRST; + } else if(v == UNICODE_STRING_SIMPLE("upper")) { + value = UCOL_UPPER_FIRST; + } + if(value != UCOL_DEFAULT) { + settings->setCaseFirst(value, 0, errorCode); + ruleIndex = j; + return; + } + } else if(raw == UNICODE_STRING_SIMPLE("caseLevel")) { + UColAttributeValue value = getOnOffValue(v); + if(value != UCOL_DEFAULT) { + settings->setFlag(CollationSettings::CASE_LEVEL, value, 0, errorCode); + ruleIndex = j; + return; + } + } else if(raw == UNICODE_STRING_SIMPLE("normalization")) { + UColAttributeValue value = getOnOffValue(v); + if(value != UCOL_DEFAULT) { + settings->setFlag(CollationSettings::CHECK_FCD, value, 0, errorCode); + ruleIndex = j; + return; + } + } else if(raw == UNICODE_STRING_SIMPLE("numericOrdering")) { + UColAttributeValue value = getOnOffValue(v); + if(value != UCOL_DEFAULT) { + settings->setFlag(CollationSettings::NUMERIC, value, 0, errorCode); + ruleIndex = j; + return; + } + } else if(raw == UNICODE_STRING_SIMPLE("hiraganaQ")) { + UColAttributeValue value = getOnOffValue(v); + if(value != UCOL_DEFAULT) { + if(value == UCOL_ON) { + setParseError("[hiraganaQ on] is not supported", errorCode); + } + ruleIndex = j; + return; + } + } else if(raw == UNICODE_STRING_SIMPLE("import")) { + CharString lang; + lang.appendInvariantChars(v, errorCode); + if(errorCode == U_MEMORY_ALLOCATION_ERROR) { return; } + // BCP 47 language tag -> ICU locale ID + char localeID[ULOC_FULLNAME_CAPACITY]; + int32_t parsedLength; + int32_t length = uloc_forLanguageTag(lang.data(), localeID, ULOC_FULLNAME_CAPACITY, + &parsedLength, &errorCode); + if(U_FAILURE(errorCode) || + parsedLength != lang.length() || length >= ULOC_FULLNAME_CAPACITY) { + errorCode = U_ZERO_ERROR; + setParseError("expected language tag in [import langTag]", errorCode); + return; + } + // localeID minus all keywords + char baseID[ULOC_FULLNAME_CAPACITY]; + length = uloc_getBaseName(localeID, baseID, ULOC_FULLNAME_CAPACITY, &errorCode); + if(U_FAILURE(errorCode) || length >= ULOC_KEYWORDS_CAPACITY) { + errorCode = U_ZERO_ERROR; + setParseError("expected language tag in [import langTag]", errorCode); + return; + } + if(length == 0) { + uprv_strcpy(baseID, "root"); + } else if(*baseID == '_') { + uprv_memmove(baseID + 3, baseID, length + 1); + uprv_memcpy(baseID, "und", 3); + } + // @collation=type, or length=0 if not specified + char collationType[ULOC_KEYWORDS_CAPACITY]; + length = uloc_getKeywordValue(localeID, "collation", + collationType, ULOC_KEYWORDS_CAPACITY, + &errorCode); + if(U_FAILURE(errorCode) || length >= ULOC_KEYWORDS_CAPACITY) { + errorCode = U_ZERO_ERROR; + setParseError("expected language tag in [import langTag]", errorCode); + return; + } + if(importer == nullptr) { + setParseError("[import langTag] is not supported", errorCode); + } else { + UnicodeString importedRules; + importer->getRules(baseID, length > 0 ? collationType : "standard", + importedRules, errorReason, errorCode); + if(U_FAILURE(errorCode)) { + if(errorReason == nullptr) { + errorReason = "[import langTag] failed"; + } + setErrorContext(); + return; + } + const UnicodeString *outerRules = rules; + int32_t outerRuleIndex = ruleIndex; + parse(importedRules, errorCode); + if(U_FAILURE(errorCode)) { + if(parseError != nullptr) { + parseError->offset = outerRuleIndex; + } + } + rules = outerRules; + ruleIndex = j; + } + return; + } + } else if(rules->charAt(j) == 0x5b) { // words end with [ + UnicodeSet set; + j = parseUnicodeSet(j, set, errorCode); + if(U_FAILURE(errorCode)) { return; } + if(raw == UNICODE_STRING_SIMPLE("optimize")) { + sink->optimize(set, errorReason, errorCode); + if(U_FAILURE(errorCode)) { setErrorContext(); } + ruleIndex = j; + return; + } else if(raw == UNICODE_STRING_SIMPLE("suppressContractions")) { + sink->suppressContractions(set, errorReason, errorCode); + if(U_FAILURE(errorCode)) { setErrorContext(); } + ruleIndex = j; + return; + } + } + setParseError("not a valid setting/option", errorCode); +} + +void +CollationRuleParser::parseReordering(const UnicodeString &raw, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + int32_t i = 7; // after "reorder" + if(i == raw.length()) { + // empty [reorder] with no codes + settings->resetReordering(); + return; + } + // Parse the codes in [reorder aa bb cc]. + UVector32 reorderCodes(errorCode); + if(U_FAILURE(errorCode)) { return; } + CharString word; + while(i < raw.length()) { + ++i; // skip the word-separating space + int32_t limit = raw.indexOf((char16_t)0x20, i); + if(limit < 0) { limit = raw.length(); } + word.clear().appendInvariantChars(raw.tempSubStringBetween(i, limit), errorCode); + if(U_FAILURE(errorCode)) { return; } + int32_t code = getReorderCode(word.data()); + if(code < 0) { + setParseError("unknown script or reorder code", errorCode); + return; + } + reorderCodes.addElement(code, errorCode); + if(U_FAILURE(errorCode)) { return; } + i = limit; + } + settings->setReordering(*baseData, reorderCodes.getBuffer(), reorderCodes.size(), errorCode); +} + +static const char *const gSpecialReorderCodes[] = { + "space", "punct", "symbol", "currency", "digit" +}; + +int32_t +CollationRuleParser::getReorderCode(const char *word) { + for(int32_t i = 0; i < UPRV_LENGTHOF(gSpecialReorderCodes); ++i) { + if(uprv_stricmp(word, gSpecialReorderCodes[i]) == 0) { + return UCOL_REORDER_CODE_FIRST + i; + } + } + int32_t script = u_getPropertyValueEnum(UCHAR_SCRIPT, word); + if(script >= 0) { + return script; + } + if(uprv_stricmp(word, "others") == 0) { + return UCOL_REORDER_CODE_OTHERS; // same as Zzzz = USCRIPT_UNKNOWN + } + return -1; +} + +UColAttributeValue +CollationRuleParser::getOnOffValue(const UnicodeString &s) { + if(s == UNICODE_STRING_SIMPLE("on")) { + return UCOL_ON; + } else if(s == UNICODE_STRING_SIMPLE("off")) { + return UCOL_OFF; + } else { + return UCOL_DEFAULT; + } +} + +int32_t +CollationRuleParser::parseUnicodeSet(int32_t i, UnicodeSet &set, UErrorCode &errorCode) { + // Collect a UnicodeSet pattern between a balanced pair of [brackets]. + int32_t level = 0; + int32_t j = i; + for(;;) { + if(j == rules->length()) { + setParseError("unbalanced UnicodeSet pattern brackets", errorCode); + return j; + } + char16_t c = rules->charAt(j++); + if(c == 0x5b) { // '[' + ++level; + } else if(c == 0x5d) { // ']' + if(--level == 0) { break; } + } + } + set.applyPattern(rules->tempSubStringBetween(i, j), errorCode); + if(U_FAILURE(errorCode)) { + errorCode = U_ZERO_ERROR; + setParseError("not a valid UnicodeSet pattern", errorCode); + return j; + } + j = skipWhiteSpace(j); + if(j == rules->length() || rules->charAt(j) != 0x5d) { + setParseError("missing option-terminating ']' after UnicodeSet pattern", errorCode); + return j; + } + return ++j; +} + +int32_t +CollationRuleParser::readWords(int32_t i, UnicodeString &raw) const { + static const char16_t sp = 0x20; + raw.remove(); + i = skipWhiteSpace(i); + for(;;) { + if(i >= rules->length()) { return 0; } + char16_t c = rules->charAt(i); + if(isSyntaxChar(c) && c != 0x2d && c != 0x5f) { // syntax except -_ + if(raw.isEmpty()) { return i; } + if(raw.endsWith(&sp, 1)) { // remove trailing space + raw.truncate(raw.length() - 1); + } + return i; + } + if(PatternProps::isWhiteSpace(c)) { + raw.append(sp); + i = skipWhiteSpace(i + 1); + } else { + raw.append(c); + ++i; + } + } +} + +int32_t +CollationRuleParser::skipComment(int32_t i) const { + // skip to past the newline + while(i < rules->length()) { + char16_t c = rules->charAt(i++); + // LF or FF or CR or NEL or LS or PS + if(c == 0xa || c == 0xc || c == 0xd || c == 0x85 || c == 0x2028 || c == 0x2029) { + // Unicode Newline Guidelines: "A readline function should stop at NLF, LS, FF, or PS." + // NLF (new line function) = CR or LF or CR+LF or NEL. + // No need to collect all of CR+LF because a following LF will be ignored anyway. + break; + } + } + return i; +} + +void +CollationRuleParser::setParseError(const char *reason, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + // Error code consistent with the old parser (from ca. 2001), + // rather than U_PARSE_ERROR; + errorCode = U_INVALID_FORMAT_ERROR; + errorReason = reason; + if(parseError != nullptr) { setErrorContext(); } +} + +void +CollationRuleParser::setErrorContext() { + if(parseError == nullptr) { return; } + + // Note: This relies on the calling code maintaining the ruleIndex + // at a position that is useful for debugging. + // For example, at the beginning of a reset or relation etc. + parseError->offset = ruleIndex; + parseError->line = 0; // We are not counting line numbers. + + // before ruleIndex + int32_t start = ruleIndex - (U_PARSE_CONTEXT_LEN - 1); + if(start < 0) { + start = 0; + } else if(start > 0 && U16_IS_TRAIL(rules->charAt(start))) { + ++start; + } + int32_t length = ruleIndex - start; + rules->extract(start, length, parseError->preContext); + parseError->preContext[length] = 0; + + // starting from ruleIndex + length = rules->length() - ruleIndex; + if(length >= U_PARSE_CONTEXT_LEN) { + length = U_PARSE_CONTEXT_LEN - 1; + if(U16_IS_LEAD(rules->charAt(ruleIndex + length - 1))) { + --length; + } + } + rules->extract(ruleIndex, length, parseError->postContext); + parseError->postContext[length] = 0; +} + +UBool +CollationRuleParser::isSyntaxChar(UChar32 c) { + return 0x21 <= c && c <= 0x7e && + (c <= 0x2f || (0x3a <= c && c <= 0x40) || + (0x5b <= c && c <= 0x60) || (0x7b <= c)); +} + +int32_t +CollationRuleParser::skipWhiteSpace(int32_t i) const { + while(i < rules->length() && PatternProps::isWhiteSpace(rules->charAt(i))) { + ++i; + } + return i; +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationruleparser.h b/deps/icu-small/source/i18n/collationruleparser.h index e124881fcf0a2f..0cdd4223836039 100644 --- a/deps/icu-small/source/i18n/collationruleparser.h +++ b/deps/icu-small/source/i18n/collationruleparser.h @@ -1,197 +1,197 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationruleparser.h -* -* created on: 2013apr10 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONRULEPARSER_H__ -#define __COLLATIONRULEPARSER_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/ucol.h" -#include "unicode/uniset.h" -#include "unicode/unistr.h" - -struct UParseError; - -U_NAMESPACE_BEGIN - -struct CollationData; -struct CollationTailoring; - -class Locale; -class Normalizer2; - -struct CollationSettings; - -class U_I18N_API CollationRuleParser : public UMemory { -public: - /** Special reset positions. */ - enum Position { - FIRST_TERTIARY_IGNORABLE, - LAST_TERTIARY_IGNORABLE, - FIRST_SECONDARY_IGNORABLE, - LAST_SECONDARY_IGNORABLE, - FIRST_PRIMARY_IGNORABLE, - LAST_PRIMARY_IGNORABLE, - FIRST_VARIABLE, - LAST_VARIABLE, - FIRST_REGULAR, - LAST_REGULAR, - FIRST_IMPLICIT, - LAST_IMPLICIT, - FIRST_TRAILING, - LAST_TRAILING - }; - - /** - * First character of contractions that encode special reset positions. - * U+FFFE cannot be tailored via rule syntax. - * - * The second contraction character is POS_BASE + Position. - */ - static const UChar POS_LEAD = 0xfffe; - /** - * Base for the second character of contractions that encode special reset positions. - * Braille characters U+28xx are printable and normalization-inert. - * @see POS_LEAD - */ - static const UChar POS_BASE = 0x2800; - - class U_I18N_API Sink : public UObject { - public: - virtual ~Sink(); - /** - * Adds a reset. - * strength=UCOL_IDENTICAL for &str. - * strength=UCOL_PRIMARY/UCOL_SECONDARY/UCOL_TERTIARY for &[before n]str where n=1/2/3. - */ - virtual void addReset(int32_t strength, const UnicodeString &str, - const char *&errorReason, UErrorCode &errorCode) = 0; - /** - * Adds a relation with strength and prefix | str / extension. - */ - virtual void addRelation(int32_t strength, const UnicodeString &prefix, - const UnicodeString &str, const UnicodeString &extension, - const char *&errorReason, UErrorCode &errorCode) = 0; - - virtual void suppressContractions(const UnicodeSet &set, const char *&errorReason, - UErrorCode &errorCode); - - virtual void optimize(const UnicodeSet &set, const char *&errorReason, - UErrorCode &errorCode); - }; - - class U_I18N_API Importer : public UObject { - public: - virtual ~Importer(); - virtual void getRules( - const char *localeID, const char *collationType, - UnicodeString &rules, - const char *&errorReason, UErrorCode &errorCode) = 0; - }; - - /** - * Constructor. - * The Sink must be set before parsing. - * The Importer can be set, otherwise [import locale] syntax is not supported. - */ - CollationRuleParser(const CollationData *base, UErrorCode &errorCode); - ~CollationRuleParser(); - - /** - * Sets the pointer to a Sink object. - * The pointer is aliased: Pointer copy without cloning or taking ownership. - */ - void setSink(Sink *sinkAlias) { - sink = sinkAlias; - } - - /** - * Sets the pointer to an Importer object. - * The pointer is aliased: Pointer copy without cloning or taking ownership. - */ - void setImporter(Importer *importerAlias) { - importer = importerAlias; - } - - void parse(const UnicodeString &ruleString, - CollationSettings &outSettings, - UParseError *outParseError, - UErrorCode &errorCode); - - const char *getErrorReason() const { return errorReason; } - - /** - * Gets a script or reorder code from its string representation. - * @return the script/reorder code, or - * -1 if not recognized - */ - static int32_t getReorderCode(const char *word); - -private: - /** UCOL_PRIMARY=0 .. UCOL_IDENTICAL=15 */ - static const int32_t STRENGTH_MASK = 0xf; - static const int32_t STARRED_FLAG = 0x10; - static const int32_t OFFSET_SHIFT = 8; - - void parse(const UnicodeString &ruleString, UErrorCode &errorCode); - void parseRuleChain(UErrorCode &errorCode); - int32_t parseResetAndPosition(UErrorCode &errorCode); - int32_t parseRelationOperator(UErrorCode &errorCode); - void parseRelationStrings(int32_t strength, int32_t i, UErrorCode &errorCode); - void parseStarredCharacters(int32_t strength, int32_t i, UErrorCode &errorCode); - int32_t parseTailoringString(int32_t i, UnicodeString &raw, UErrorCode &errorCode); - int32_t parseString(int32_t i, UnicodeString &raw, UErrorCode &errorCode); - - /** - * Sets str to a contraction of U+FFFE and (U+2800 + Position). - * @return rule index after the special reset position - */ - int32_t parseSpecialPosition(int32_t i, UnicodeString &str, UErrorCode &errorCode); - void parseSetting(UErrorCode &errorCode); - void parseReordering(const UnicodeString &raw, UErrorCode &errorCode); - static UColAttributeValue getOnOffValue(const UnicodeString &s); - - int32_t parseUnicodeSet(int32_t i, UnicodeSet &set, UErrorCode &errorCode); - int32_t readWords(int32_t i, UnicodeString &raw) const; - int32_t skipComment(int32_t i) const; - - void setParseError(const char *reason, UErrorCode &errorCode); - void setErrorContext(); - - /** - * ASCII [:P:] and [:S:]: - * [\u0021-\u002F \u003A-\u0040 \u005B-\u0060 \u007B-\u007E] - */ - static UBool isSyntaxChar(UChar32 c); - int32_t skipWhiteSpace(int32_t i) const; - - const Normalizer2 &nfd, &nfc; - - const UnicodeString *rules; - const CollationData *const baseData; - CollationSettings *settings; - UParseError *parseError; - const char *errorReason; - - Sink *sink; - Importer *importer; - - int32_t ruleIndex; -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONRULEPARSER_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationruleparser.h +* +* created on: 2013apr10 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONRULEPARSER_H__ +#define __COLLATIONRULEPARSER_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/ucol.h" +#include "unicode/uniset.h" +#include "unicode/unistr.h" + +struct UParseError; + +U_NAMESPACE_BEGIN + +struct CollationData; +struct CollationTailoring; + +class Locale; +class Normalizer2; + +struct CollationSettings; + +class U_I18N_API CollationRuleParser : public UMemory { +public: + /** Special reset positions. */ + enum Position { + FIRST_TERTIARY_IGNORABLE, + LAST_TERTIARY_IGNORABLE, + FIRST_SECONDARY_IGNORABLE, + LAST_SECONDARY_IGNORABLE, + FIRST_PRIMARY_IGNORABLE, + LAST_PRIMARY_IGNORABLE, + FIRST_VARIABLE, + LAST_VARIABLE, + FIRST_REGULAR, + LAST_REGULAR, + FIRST_IMPLICIT, + LAST_IMPLICIT, + FIRST_TRAILING, + LAST_TRAILING + }; + + /** + * First character of contractions that encode special reset positions. + * U+FFFE cannot be tailored via rule syntax. + * + * The second contraction character is POS_BASE + Position. + */ + static const char16_t POS_LEAD = 0xfffe; + /** + * Base for the second character of contractions that encode special reset positions. + * Braille characters U+28xx are printable and normalization-inert. + * @see POS_LEAD + */ + static const char16_t POS_BASE = 0x2800; + + class U_I18N_API Sink : public UObject { + public: + virtual ~Sink(); + /** + * Adds a reset. + * strength=UCOL_IDENTICAL for &str. + * strength=UCOL_PRIMARY/UCOL_SECONDARY/UCOL_TERTIARY for &[before n]str where n=1/2/3. + */ + virtual void addReset(int32_t strength, const UnicodeString &str, + const char *&errorReason, UErrorCode &errorCode) = 0; + /** + * Adds a relation with strength and prefix | str / extension. + */ + virtual void addRelation(int32_t strength, const UnicodeString &prefix, + const UnicodeString &str, const UnicodeString &extension, + const char *&errorReason, UErrorCode &errorCode) = 0; + + virtual void suppressContractions(const UnicodeSet &set, const char *&errorReason, + UErrorCode &errorCode); + + virtual void optimize(const UnicodeSet &set, const char *&errorReason, + UErrorCode &errorCode); + }; + + class U_I18N_API Importer : public UObject { + public: + virtual ~Importer(); + virtual void getRules( + const char *localeID, const char *collationType, + UnicodeString &rules, + const char *&errorReason, UErrorCode &errorCode) = 0; + }; + + /** + * Constructor. + * The Sink must be set before parsing. + * The Importer can be set, otherwise [import locale] syntax is not supported. + */ + CollationRuleParser(const CollationData *base, UErrorCode &errorCode); + ~CollationRuleParser(); + + /** + * Sets the pointer to a Sink object. + * The pointer is aliased: Pointer copy without cloning or taking ownership. + */ + void setSink(Sink *sinkAlias) { + sink = sinkAlias; + } + + /** + * Sets the pointer to an Importer object. + * The pointer is aliased: Pointer copy without cloning or taking ownership. + */ + void setImporter(Importer *importerAlias) { + importer = importerAlias; + } + + void parse(const UnicodeString &ruleString, + CollationSettings &outSettings, + UParseError *outParseError, + UErrorCode &errorCode); + + const char *getErrorReason() const { return errorReason; } + + /** + * Gets a script or reorder code from its string representation. + * @return the script/reorder code, or + * -1 if not recognized + */ + static int32_t getReorderCode(const char *word); + +private: + /** UCOL_PRIMARY=0 .. UCOL_IDENTICAL=15 */ + static const int32_t STRENGTH_MASK = 0xf; + static const int32_t STARRED_FLAG = 0x10; + static const int32_t OFFSET_SHIFT = 8; + + void parse(const UnicodeString &ruleString, UErrorCode &errorCode); + void parseRuleChain(UErrorCode &errorCode); + int32_t parseResetAndPosition(UErrorCode &errorCode); + int32_t parseRelationOperator(UErrorCode &errorCode); + void parseRelationStrings(int32_t strength, int32_t i, UErrorCode &errorCode); + void parseStarredCharacters(int32_t strength, int32_t i, UErrorCode &errorCode); + int32_t parseTailoringString(int32_t i, UnicodeString &raw, UErrorCode &errorCode); + int32_t parseString(int32_t i, UnicodeString &raw, UErrorCode &errorCode); + + /** + * Sets str to a contraction of U+FFFE and (U+2800 + Position). + * @return rule index after the special reset position + */ + int32_t parseSpecialPosition(int32_t i, UnicodeString &str, UErrorCode &errorCode); + void parseSetting(UErrorCode &errorCode); + void parseReordering(const UnicodeString &raw, UErrorCode &errorCode); + static UColAttributeValue getOnOffValue(const UnicodeString &s); + + int32_t parseUnicodeSet(int32_t i, UnicodeSet &set, UErrorCode &errorCode); + int32_t readWords(int32_t i, UnicodeString &raw) const; + int32_t skipComment(int32_t i) const; + + void setParseError(const char *reason, UErrorCode &errorCode); + void setErrorContext(); + + /** + * ASCII [:P:] and [:S:]: + * [\u0021-\u002F \u003A-\u0040 \u005B-\u0060 \u007B-\u007E] + */ + static UBool isSyntaxChar(UChar32 c); + int32_t skipWhiteSpace(int32_t i) const; + + const Normalizer2 &nfd, &nfc; + + const UnicodeString *rules; + const CollationData *const baseData; + CollationSettings *settings; + UParseError *parseError; + const char *errorReason; + + Sink *sink; + Importer *importer; + + int32_t ruleIndex; +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONRULEPARSER_H__ diff --git a/deps/icu-small/source/i18n/collationsets.cpp b/deps/icu-small/source/i18n/collationsets.cpp index b23c5e318d7c4f..66902d62d01f73 100644 --- a/deps/icu-small/source/i18n/collationsets.cpp +++ b/deps/icu-small/source/i18n/collationsets.cpp @@ -1,612 +1,612 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationsets.cpp -* -* created on: 2013feb09 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/ucharstrie.h" -#include "unicode/uniset.h" -#include "unicode/unistr.h" -#include "unicode/ustringtrie.h" -#include "collation.h" -#include "collationdata.h" -#include "collationsets.h" -#include "normalizer2impl.h" -#include "uassert.h" -#include "utf16collationiterator.h" -#include "utrie2.h" - -U_NAMESPACE_BEGIN - -U_CDECL_BEGIN - -static UBool U_CALLCONV -enumTailoredRange(const void *context, UChar32 start, UChar32 end, uint32_t ce32) { - if(ce32 == Collation::FALLBACK_CE32) { - return true; // fallback to base, not tailored - } - TailoredSet *ts = (TailoredSet *)context; - return ts->handleCE32(start, end, ce32); -} - -U_CDECL_END - -void -TailoredSet::forData(const CollationData *d, UErrorCode &ec) { - if(U_FAILURE(ec)) { return; } - errorCode = ec; // Preserve info & warning codes. - data = d; - baseData = d->base; - U_ASSERT(baseData != NULL); - utrie2_enum(data->trie, NULL, enumTailoredRange, this); - ec = errorCode; -} - -UBool -TailoredSet::handleCE32(UChar32 start, UChar32 end, uint32_t ce32) { - U_ASSERT(ce32 != Collation::FALLBACK_CE32); - if(Collation::isSpecialCE32(ce32)) { - ce32 = data->getIndirectCE32(ce32); - if(ce32 == Collation::FALLBACK_CE32) { - return U_SUCCESS(errorCode); - } - } - do { - uint32_t baseCE32 = baseData->getFinalCE32(baseData->getCE32(start)); - // Do not just continue if ce32 == baseCE32 because - // contractions and expansions in different data objects - // normally differ even if they have the same data offsets. - if(Collation::isSelfContainedCE32(ce32) && Collation::isSelfContainedCE32(baseCE32)) { - // fastpath - if(ce32 != baseCE32) { - tailored->add(start); - } - } else { - compare(start, ce32, baseCE32); - } - } while(++start <= end); - return U_SUCCESS(errorCode); -} - -void -TailoredSet::compare(UChar32 c, uint32_t ce32, uint32_t baseCE32) { - if(Collation::isPrefixCE32(ce32)) { - const UChar *p = data->contexts + Collation::indexFromCE32(ce32); - ce32 = data->getFinalCE32(CollationData::readCE32(p)); - if(Collation::isPrefixCE32(baseCE32)) { - const UChar *q = baseData->contexts + Collation::indexFromCE32(baseCE32); - baseCE32 = baseData->getFinalCE32(CollationData::readCE32(q)); - comparePrefixes(c, p + 2, q + 2); - } else { - addPrefixes(data, c, p + 2); - } - } else if(Collation::isPrefixCE32(baseCE32)) { - const UChar *q = baseData->contexts + Collation::indexFromCE32(baseCE32); - baseCE32 = baseData->getFinalCE32(CollationData::readCE32(q)); - addPrefixes(baseData, c, q + 2); - } - - if(Collation::isContractionCE32(ce32)) { - const UChar *p = data->contexts + Collation::indexFromCE32(ce32); - if((ce32 & Collation::CONTRACT_SINGLE_CP_NO_MATCH) != 0) { - ce32 = Collation::NO_CE32; - } else { - ce32 = data->getFinalCE32(CollationData::readCE32(p)); - } - if(Collation::isContractionCE32(baseCE32)) { - const UChar *q = baseData->contexts + Collation::indexFromCE32(baseCE32); - if((baseCE32 & Collation::CONTRACT_SINGLE_CP_NO_MATCH) != 0) { - baseCE32 = Collation::NO_CE32; - } else { - baseCE32 = baseData->getFinalCE32(CollationData::readCE32(q)); - } - compareContractions(c, p + 2, q + 2); - } else { - addContractions(c, p + 2); - } - } else if(Collation::isContractionCE32(baseCE32)) { - const UChar *q = baseData->contexts + Collation::indexFromCE32(baseCE32); - baseCE32 = baseData->getFinalCE32(CollationData::readCE32(q)); - addContractions(c, q + 2); - } - - int32_t tag; - if(Collation::isSpecialCE32(ce32)) { - tag = Collation::tagFromCE32(ce32); - U_ASSERT(tag != Collation::PREFIX_TAG); - U_ASSERT(tag != Collation::CONTRACTION_TAG); - // Currently, the tailoring data builder does not write offset tags. - // They might be useful for saving space, - // but they would complicate the builder, - // and in tailorings we assume that performance of tailored characters is more important. - U_ASSERT(tag != Collation::OFFSET_TAG); - } else { - tag = -1; - } - int32_t baseTag; - if(Collation::isSpecialCE32(baseCE32)) { - baseTag = Collation::tagFromCE32(baseCE32); - U_ASSERT(baseTag != Collation::PREFIX_TAG); - U_ASSERT(baseTag != Collation::CONTRACTION_TAG); - } else { - baseTag = -1; - } - - // Non-contextual mappings, expansions, etc. - if(baseTag == Collation::OFFSET_TAG) { - // We might be comparing a tailoring CE which is a copy of - // a base offset-tag CE, via the [optimize [set]] syntax - // or when a single-character mapping was copied for tailored contractions. - // Offset tags always result in long-primary CEs, - // with common secondary/tertiary weights. - if(!Collation::isLongPrimaryCE32(ce32)) { - add(c); - return; - } - int64_t dataCE = baseData->ces[Collation::indexFromCE32(baseCE32)]; - uint32_t p = Collation::getThreeBytePrimaryForOffsetData(c, dataCE); - if(Collation::primaryFromLongPrimaryCE32(ce32) != p) { - add(c); - return; - } - } - - if(tag != baseTag) { - add(c); - return; - } - - if(tag == Collation::EXPANSION32_TAG) { - const uint32_t *ce32s = data->ce32s + Collation::indexFromCE32(ce32); - int32_t length = Collation::lengthFromCE32(ce32); - - const uint32_t *baseCE32s = baseData->ce32s + Collation::indexFromCE32(baseCE32); - int32_t baseLength = Collation::lengthFromCE32(baseCE32); - - if(length != baseLength) { - add(c); - return; - } - for(int32_t i = 0; i < length; ++i) { - if(ce32s[i] != baseCE32s[i]) { - add(c); - break; - } - } - } else if(tag == Collation::EXPANSION_TAG) { - const int64_t *ces = data->ces + Collation::indexFromCE32(ce32); - int32_t length = Collation::lengthFromCE32(ce32); - - const int64_t *baseCEs = baseData->ces + Collation::indexFromCE32(baseCE32); - int32_t baseLength = Collation::lengthFromCE32(baseCE32); - - if(length != baseLength) { - add(c); - return; - } - for(int32_t i = 0; i < length; ++i) { - if(ces[i] != baseCEs[i]) { - add(c); - break; - } - } - } else if(tag == Collation::HANGUL_TAG) { - UChar jamos[3]; - int32_t length = Hangul::decompose(c, jamos); - if(tailored->contains(jamos[0]) || tailored->contains(jamos[1]) || - (length == 3 && tailored->contains(jamos[2]))) { - add(c); - } - } else if(ce32 != baseCE32) { - add(c); - } -} - -void -TailoredSet::comparePrefixes(UChar32 c, const UChar *p, const UChar *q) { - // Parallel iteration over prefixes of both tables. - UCharsTrie::Iterator prefixes(p, 0, errorCode); - UCharsTrie::Iterator basePrefixes(q, 0, errorCode); - const UnicodeString *tp = NULL; // Tailoring prefix. - const UnicodeString *bp = NULL; // Base prefix. - // Use a string with a U+FFFF as the limit sentinel. - // U+FFFF is untailorable and will not occur in prefixes. - UnicodeString none((UChar)0xffff); - for(;;) { - if(tp == NULL) { - if(prefixes.next(errorCode)) { - tp = &prefixes.getString(); - } else { - tp = &none; - } - } - if(bp == NULL) { - if(basePrefixes.next(errorCode)) { - bp = &basePrefixes.getString(); - } else { - bp = &none; - } - } - if(tp == &none && bp == &none) { break; } - int32_t cmp = tp->compare(*bp); - if(cmp < 0) { - // tp occurs in the tailoring but not in the base. - addPrefix(data, *tp, c, (uint32_t)prefixes.getValue()); - tp = NULL; - } else if(cmp > 0) { - // bp occurs in the base but not in the tailoring. - addPrefix(baseData, *bp, c, (uint32_t)basePrefixes.getValue()); - bp = NULL; - } else { - setPrefix(*tp); - compare(c, (uint32_t)prefixes.getValue(), (uint32_t)basePrefixes.getValue()); - resetPrefix(); - tp = NULL; - bp = NULL; - } - } -} - -void -TailoredSet::compareContractions(UChar32 c, const UChar *p, const UChar *q) { - // Parallel iteration over suffixes of both tables. - UCharsTrie::Iterator suffixes(p, 0, errorCode); - UCharsTrie::Iterator baseSuffixes(q, 0, errorCode); - const UnicodeString *ts = NULL; // Tailoring suffix. - const UnicodeString *bs = NULL; // Base suffix. - // Use a string with two U+FFFF as the limit sentinel. - // U+FFFF is untailorable and will not occur in contractions except maybe - // as a single suffix character for a root-collator boundary contraction. - UnicodeString none((UChar)0xffff); - none.append((UChar)0xffff); - for(;;) { - if(ts == NULL) { - if(suffixes.next(errorCode)) { - ts = &suffixes.getString(); - } else { - ts = &none; - } - } - if(bs == NULL) { - if(baseSuffixes.next(errorCode)) { - bs = &baseSuffixes.getString(); - } else { - bs = &none; - } - } - if(ts == &none && bs == &none) { break; } - int32_t cmp = ts->compare(*bs); - if(cmp < 0) { - // ts occurs in the tailoring but not in the base. - addSuffix(c, *ts); - ts = NULL; - } else if(cmp > 0) { - // bs occurs in the base but not in the tailoring. - addSuffix(c, *bs); - bs = NULL; - } else { - suffix = ts; - compare(c, (uint32_t)suffixes.getValue(), (uint32_t)baseSuffixes.getValue()); - suffix = NULL; - ts = NULL; - bs = NULL; - } - } -} - -void -TailoredSet::addPrefixes(const CollationData *d, UChar32 c, const UChar *p) { - UCharsTrie::Iterator prefixes(p, 0, errorCode); - while(prefixes.next(errorCode)) { - addPrefix(d, prefixes.getString(), c, (uint32_t)prefixes.getValue()); - } -} - -void -TailoredSet::addPrefix(const CollationData *d, const UnicodeString &pfx, UChar32 c, uint32_t ce32) { - setPrefix(pfx); - ce32 = d->getFinalCE32(ce32); - if(Collation::isContractionCE32(ce32)) { - const UChar *p = d->contexts + Collation::indexFromCE32(ce32); - addContractions(c, p + 2); - } - tailored->add(UnicodeString(unreversedPrefix).append(c)); - resetPrefix(); -} - -void -TailoredSet::addContractions(UChar32 c, const UChar *p) { - UCharsTrie::Iterator suffixes(p, 0, errorCode); - while(suffixes.next(errorCode)) { - addSuffix(c, suffixes.getString()); - } -} - -void -TailoredSet::addSuffix(UChar32 c, const UnicodeString &sfx) { - tailored->add(UnicodeString(unreversedPrefix).append(c).append(sfx)); -} - -void -TailoredSet::add(UChar32 c) { - if(unreversedPrefix.isEmpty() && suffix == NULL) { - tailored->add(c); - } else { - UnicodeString s(unreversedPrefix); - s.append(c); - if(suffix != NULL) { - s.append(*suffix); - } - tailored->add(s); - } -} - -ContractionsAndExpansions::CESink::~CESink() {} - -U_CDECL_BEGIN - -static UBool U_CALLCONV -enumCnERange(const void *context, UChar32 start, UChar32 end, uint32_t ce32) { - ContractionsAndExpansions *cne = (ContractionsAndExpansions *)context; - if(cne->checkTailored == 0) { - // There is no tailoring. - // No need to collect nor check the tailored set. - } else if(cne->checkTailored < 0) { - // Collect the set of code points with mappings in the tailoring data. - if(ce32 == Collation::FALLBACK_CE32) { - return true; // fallback to base, not tailored - } else { - cne->tailored.add(start, end); - } - // checkTailored > 0: Exclude tailored ranges from the base data enumeration. - } else if(start == end) { - if(cne->tailored.contains(start)) { - return true; - } - } else if(cne->tailored.containsSome(start, end)) { - cne->ranges.set(start, end).removeAll(cne->tailored); - int32_t count = cne->ranges.getRangeCount(); - for(int32_t i = 0; i < count; ++i) { - cne->handleCE32(cne->ranges.getRangeStart(i), cne->ranges.getRangeEnd(i), ce32); - } - return U_SUCCESS(cne->errorCode); - } - cne->handleCE32(start, end, ce32); - return U_SUCCESS(cne->errorCode); -} - -U_CDECL_END - -void -ContractionsAndExpansions::forData(const CollationData *d, UErrorCode &ec) { - if(U_FAILURE(ec)) { return; } - errorCode = ec; // Preserve info & warning codes. - // Add all from the data, can be tailoring or base. - if(d->base != NULL) { - checkTailored = -1; - } - data = d; - utrie2_enum(data->trie, NULL, enumCnERange, this); - if(d->base == NULL || U_FAILURE(errorCode)) { - ec = errorCode; - return; - } - // Add all from the base data but only for un-tailored code points. - tailored.freeze(); - checkTailored = 1; - data = d->base; - utrie2_enum(data->trie, NULL, enumCnERange, this); - ec = errorCode; -} - -void -ContractionsAndExpansions::forCodePoint(const CollationData *d, UChar32 c, UErrorCode &ec) { - if(U_FAILURE(ec)) { return; } - errorCode = ec; // Preserve info & warning codes. - uint32_t ce32 = d->getCE32(c); - if(ce32 == Collation::FALLBACK_CE32) { - d = d->base; - ce32 = d->getCE32(c); - } - data = d; - handleCE32(c, c, ce32); - ec = errorCode; -} - -void -ContractionsAndExpansions::handleCE32(UChar32 start, UChar32 end, uint32_t ce32) { - for(;;) { - if((ce32 & 0xff) < Collation::SPECIAL_CE32_LOW_BYTE) { - // !isSpecialCE32() - if(sink != NULL) { - sink->handleCE(Collation::ceFromSimpleCE32(ce32)); - } - return; - } - switch(Collation::tagFromCE32(ce32)) { - case Collation::FALLBACK_TAG: - return; - case Collation::RESERVED_TAG_3: - case Collation::BUILDER_DATA_TAG: - case Collation::LEAD_SURROGATE_TAG: - if(U_SUCCESS(errorCode)) { errorCode = U_INTERNAL_PROGRAM_ERROR; } - return; - case Collation::LONG_PRIMARY_TAG: - if(sink != NULL) { - sink->handleCE(Collation::ceFromLongPrimaryCE32(ce32)); - } - return; - case Collation::LONG_SECONDARY_TAG: - if(sink != NULL) { - sink->handleCE(Collation::ceFromLongSecondaryCE32(ce32)); - } - return; - case Collation::LATIN_EXPANSION_TAG: - if(sink != NULL) { - ces[0] = Collation::latinCE0FromCE32(ce32); - ces[1] = Collation::latinCE1FromCE32(ce32); - sink->handleExpansion(ces, 2); - } - // Optimization: If we have a prefix, - // then the relevant strings have been added already. - if(unreversedPrefix.isEmpty()) { - addExpansions(start, end); - } - return; - case Collation::EXPANSION32_TAG: - if(sink != NULL) { - const uint32_t *ce32s = data->ce32s + Collation::indexFromCE32(ce32); - int32_t length = Collation::lengthFromCE32(ce32); - for(int32_t i = 0; i < length; ++i) { - ces[i] = Collation::ceFromCE32(*ce32s++); - } - sink->handleExpansion(ces, length); - } - // Optimization: If we have a prefix, - // then the relevant strings have been added already. - if(unreversedPrefix.isEmpty()) { - addExpansions(start, end); - } - return; - case Collation::EXPANSION_TAG: - if(sink != NULL) { - int32_t length = Collation::lengthFromCE32(ce32); - sink->handleExpansion(data->ces + Collation::indexFromCE32(ce32), length); - } - // Optimization: If we have a prefix, - // then the relevant strings have been added already. - if(unreversedPrefix.isEmpty()) { - addExpansions(start, end); - } - return; - case Collation::PREFIX_TAG: - handlePrefixes(start, end, ce32); - return; - case Collation::CONTRACTION_TAG: - handleContractions(start, end, ce32); - return; - case Collation::DIGIT_TAG: - // Fetch the non-numeric-collation CE32 and continue. - ce32 = data->ce32s[Collation::indexFromCE32(ce32)]; - break; - case Collation::U0000_TAG: - U_ASSERT(start == 0 && end == 0); - // Fetch the normal ce32 for U+0000 and continue. - ce32 = data->ce32s[0]; - break; - case Collation::HANGUL_TAG: - if(sink != NULL) { - // TODO: This should be optimized, - // especially if [start..end] is the complete Hangul range. (assert that) - UTF16CollationIterator iter(data, false, NULL, NULL, NULL); - UChar hangul[1] = { 0 }; - for(UChar32 c = start; c <= end; ++c) { - hangul[0] = (UChar)c; - iter.setText(hangul, hangul + 1); - int32_t length = iter.fetchCEs(errorCode); - if(U_FAILURE(errorCode)) { return; } - // Ignore the terminating non-CE. - U_ASSERT(length >= 2 && iter.getCE(length - 1) == Collation::NO_CE); - sink->handleExpansion(iter.getCEs(), length - 1); - } - } - // Optimization: If we have a prefix, - // then the relevant strings have been added already. - if(unreversedPrefix.isEmpty()) { - addExpansions(start, end); - } - return; - case Collation::OFFSET_TAG: - // Currently no need to send offset CEs to the sink. - return; - case Collation::IMPLICIT_TAG: - // Currently no need to send implicit CEs to the sink. - return; - } - } -} - -void -ContractionsAndExpansions::handlePrefixes( - UChar32 start, UChar32 end, uint32_t ce32) { - const UChar *p = data->contexts + Collation::indexFromCE32(ce32); - ce32 = CollationData::readCE32(p); // Default if no prefix match. - handleCE32(start, end, ce32); - if(!addPrefixes) { return; } - UCharsTrie::Iterator prefixes(p + 2, 0, errorCode); - while(prefixes.next(errorCode)) { - setPrefix(prefixes.getString()); - // Prefix/pre-context mappings are special kinds of contractions - // that always yield expansions. - addStrings(start, end, contractions); - addStrings(start, end, expansions); - handleCE32(start, end, (uint32_t)prefixes.getValue()); - } - resetPrefix(); -} - -void -ContractionsAndExpansions::handleContractions( - UChar32 start, UChar32 end, uint32_t ce32) { - const UChar *p = data->contexts + Collation::indexFromCE32(ce32); - if((ce32 & Collation::CONTRACT_SINGLE_CP_NO_MATCH) != 0) { - // No match on the single code point. - // We are underneath a prefix, and the default mapping is just - // a fallback to the mappings for a shorter prefix. - U_ASSERT(!unreversedPrefix.isEmpty()); - } else { - ce32 = CollationData::readCE32(p); // Default if no suffix match. - U_ASSERT(!Collation::isContractionCE32(ce32)); - handleCE32(start, end, ce32); - } - UCharsTrie::Iterator suffixes(p + 2, 0, errorCode); - while(suffixes.next(errorCode)) { - suffix = &suffixes.getString(); - addStrings(start, end, contractions); - if(!unreversedPrefix.isEmpty()) { - addStrings(start, end, expansions); - } - handleCE32(start, end, (uint32_t)suffixes.getValue()); - } - suffix = NULL; -} - -void -ContractionsAndExpansions::addExpansions(UChar32 start, UChar32 end) { - if(unreversedPrefix.isEmpty() && suffix == NULL) { - if(expansions != NULL) { - expansions->add(start, end); - } - } else { - addStrings(start, end, expansions); - } -} - -void -ContractionsAndExpansions::addStrings(UChar32 start, UChar32 end, UnicodeSet *set) { - if(set == NULL) { return; } - UnicodeString s(unreversedPrefix); - do { - s.append(start); - if(suffix != NULL) { - s.append(*suffix); - } - set->add(s); - s.truncate(unreversedPrefix.length()); - } while(++start <= end); -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationsets.cpp +* +* created on: 2013feb09 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/ucharstrie.h" +#include "unicode/uniset.h" +#include "unicode/unistr.h" +#include "unicode/ustringtrie.h" +#include "collation.h" +#include "collationdata.h" +#include "collationsets.h" +#include "normalizer2impl.h" +#include "uassert.h" +#include "utf16collationiterator.h" +#include "utrie2.h" + +U_NAMESPACE_BEGIN + +U_CDECL_BEGIN + +static UBool U_CALLCONV +enumTailoredRange(const void *context, UChar32 start, UChar32 end, uint32_t ce32) { + if(ce32 == Collation::FALLBACK_CE32) { + return true; // fallback to base, not tailored + } + TailoredSet *ts = (TailoredSet *)context; + return ts->handleCE32(start, end, ce32); +} + +U_CDECL_END + +void +TailoredSet::forData(const CollationData *d, UErrorCode &ec) { + if(U_FAILURE(ec)) { return; } + errorCode = ec; // Preserve info & warning codes. + data = d; + baseData = d->base; + U_ASSERT(baseData != nullptr); + utrie2_enum(data->trie, nullptr, enumTailoredRange, this); + ec = errorCode; +} + +UBool +TailoredSet::handleCE32(UChar32 start, UChar32 end, uint32_t ce32) { + U_ASSERT(ce32 != Collation::FALLBACK_CE32); + if(Collation::isSpecialCE32(ce32)) { + ce32 = data->getIndirectCE32(ce32); + if(ce32 == Collation::FALLBACK_CE32) { + return U_SUCCESS(errorCode); + } + } + do { + uint32_t baseCE32 = baseData->getFinalCE32(baseData->getCE32(start)); + // Do not just continue if ce32 == baseCE32 because + // contractions and expansions in different data objects + // normally differ even if they have the same data offsets. + if(Collation::isSelfContainedCE32(ce32) && Collation::isSelfContainedCE32(baseCE32)) { + // fastpath + if(ce32 != baseCE32) { + tailored->add(start); + } + } else { + compare(start, ce32, baseCE32); + } + } while(++start <= end); + return U_SUCCESS(errorCode); +} + +void +TailoredSet::compare(UChar32 c, uint32_t ce32, uint32_t baseCE32) { + if(Collation::isPrefixCE32(ce32)) { + const char16_t *p = data->contexts + Collation::indexFromCE32(ce32); + ce32 = data->getFinalCE32(CollationData::readCE32(p)); + if(Collation::isPrefixCE32(baseCE32)) { + const char16_t *q = baseData->contexts + Collation::indexFromCE32(baseCE32); + baseCE32 = baseData->getFinalCE32(CollationData::readCE32(q)); + comparePrefixes(c, p + 2, q + 2); + } else { + addPrefixes(data, c, p + 2); + } + } else if(Collation::isPrefixCE32(baseCE32)) { + const char16_t *q = baseData->contexts + Collation::indexFromCE32(baseCE32); + baseCE32 = baseData->getFinalCE32(CollationData::readCE32(q)); + addPrefixes(baseData, c, q + 2); + } + + if(Collation::isContractionCE32(ce32)) { + const char16_t *p = data->contexts + Collation::indexFromCE32(ce32); + if((ce32 & Collation::CONTRACT_SINGLE_CP_NO_MATCH) != 0) { + ce32 = Collation::NO_CE32; + } else { + ce32 = data->getFinalCE32(CollationData::readCE32(p)); + } + if(Collation::isContractionCE32(baseCE32)) { + const char16_t *q = baseData->contexts + Collation::indexFromCE32(baseCE32); + if((baseCE32 & Collation::CONTRACT_SINGLE_CP_NO_MATCH) != 0) { + baseCE32 = Collation::NO_CE32; + } else { + baseCE32 = baseData->getFinalCE32(CollationData::readCE32(q)); + } + compareContractions(c, p + 2, q + 2); + } else { + addContractions(c, p + 2); + } + } else if(Collation::isContractionCE32(baseCE32)) { + const char16_t *q = baseData->contexts + Collation::indexFromCE32(baseCE32); + baseCE32 = baseData->getFinalCE32(CollationData::readCE32(q)); + addContractions(c, q + 2); + } + + int32_t tag; + if(Collation::isSpecialCE32(ce32)) { + tag = Collation::tagFromCE32(ce32); + U_ASSERT(tag != Collation::PREFIX_TAG); + U_ASSERT(tag != Collation::CONTRACTION_TAG); + // Currently, the tailoring data builder does not write offset tags. + // They might be useful for saving space, + // but they would complicate the builder, + // and in tailorings we assume that performance of tailored characters is more important. + U_ASSERT(tag != Collation::OFFSET_TAG); + } else { + tag = -1; + } + int32_t baseTag; + if(Collation::isSpecialCE32(baseCE32)) { + baseTag = Collation::tagFromCE32(baseCE32); + U_ASSERT(baseTag != Collation::PREFIX_TAG); + U_ASSERT(baseTag != Collation::CONTRACTION_TAG); + } else { + baseTag = -1; + } + + // Non-contextual mappings, expansions, etc. + if(baseTag == Collation::OFFSET_TAG) { + // We might be comparing a tailoring CE which is a copy of + // a base offset-tag CE, via the [optimize [set]] syntax + // or when a single-character mapping was copied for tailored contractions. + // Offset tags always result in long-primary CEs, + // with common secondary/tertiary weights. + if(!Collation::isLongPrimaryCE32(ce32)) { + add(c); + return; + } + int64_t dataCE = baseData->ces[Collation::indexFromCE32(baseCE32)]; + uint32_t p = Collation::getThreeBytePrimaryForOffsetData(c, dataCE); + if(Collation::primaryFromLongPrimaryCE32(ce32) != p) { + add(c); + return; + } + } + + if(tag != baseTag) { + add(c); + return; + } + + if(tag == Collation::EXPANSION32_TAG) { + const uint32_t *ce32s = data->ce32s + Collation::indexFromCE32(ce32); + int32_t length = Collation::lengthFromCE32(ce32); + + const uint32_t *baseCE32s = baseData->ce32s + Collation::indexFromCE32(baseCE32); + int32_t baseLength = Collation::lengthFromCE32(baseCE32); + + if(length != baseLength) { + add(c); + return; + } + for(int32_t i = 0; i < length; ++i) { + if(ce32s[i] != baseCE32s[i]) { + add(c); + break; + } + } + } else if(tag == Collation::EXPANSION_TAG) { + const int64_t *ces = data->ces + Collation::indexFromCE32(ce32); + int32_t length = Collation::lengthFromCE32(ce32); + + const int64_t *baseCEs = baseData->ces + Collation::indexFromCE32(baseCE32); + int32_t baseLength = Collation::lengthFromCE32(baseCE32); + + if(length != baseLength) { + add(c); + return; + } + for(int32_t i = 0; i < length; ++i) { + if(ces[i] != baseCEs[i]) { + add(c); + break; + } + } + } else if(tag == Collation::HANGUL_TAG) { + char16_t jamos[3]; + int32_t length = Hangul::decompose(c, jamos); + if(tailored->contains(jamos[0]) || tailored->contains(jamos[1]) || + (length == 3 && tailored->contains(jamos[2]))) { + add(c); + } + } else if(ce32 != baseCE32) { + add(c); + } +} + +void +TailoredSet::comparePrefixes(UChar32 c, const char16_t *p, const char16_t *q) { + // Parallel iteration over prefixes of both tables. + UCharsTrie::Iterator prefixes(p, 0, errorCode); + UCharsTrie::Iterator basePrefixes(q, 0, errorCode); + const UnicodeString *tp = nullptr; // Tailoring prefix. + const UnicodeString *bp = nullptr; // Base prefix. + // Use a string with a U+FFFF as the limit sentinel. + // U+FFFF is untailorable and will not occur in prefixes. + UnicodeString none((char16_t)0xffff); + for(;;) { + if(tp == nullptr) { + if(prefixes.next(errorCode)) { + tp = &prefixes.getString(); + } else { + tp = &none; + } + } + if(bp == nullptr) { + if(basePrefixes.next(errorCode)) { + bp = &basePrefixes.getString(); + } else { + bp = &none; + } + } + if(tp == &none && bp == &none) { break; } + int32_t cmp = tp->compare(*bp); + if(cmp < 0) { + // tp occurs in the tailoring but not in the base. + addPrefix(data, *tp, c, (uint32_t)prefixes.getValue()); + tp = nullptr; + } else if(cmp > 0) { + // bp occurs in the base but not in the tailoring. + addPrefix(baseData, *bp, c, (uint32_t)basePrefixes.getValue()); + bp = nullptr; + } else { + setPrefix(*tp); + compare(c, (uint32_t)prefixes.getValue(), (uint32_t)basePrefixes.getValue()); + resetPrefix(); + tp = nullptr; + bp = nullptr; + } + } +} + +void +TailoredSet::compareContractions(UChar32 c, const char16_t *p, const char16_t *q) { + // Parallel iteration over suffixes of both tables. + UCharsTrie::Iterator suffixes(p, 0, errorCode); + UCharsTrie::Iterator baseSuffixes(q, 0, errorCode); + const UnicodeString *ts = nullptr; // Tailoring suffix. + const UnicodeString *bs = nullptr; // Base suffix. + // Use a string with two U+FFFF as the limit sentinel. + // U+FFFF is untailorable and will not occur in contractions except maybe + // as a single suffix character for a root-collator boundary contraction. + UnicodeString none((char16_t)0xffff); + none.append((char16_t)0xffff); + for(;;) { + if(ts == nullptr) { + if(suffixes.next(errorCode)) { + ts = &suffixes.getString(); + } else { + ts = &none; + } + } + if(bs == nullptr) { + if(baseSuffixes.next(errorCode)) { + bs = &baseSuffixes.getString(); + } else { + bs = &none; + } + } + if(ts == &none && bs == &none) { break; } + int32_t cmp = ts->compare(*bs); + if(cmp < 0) { + // ts occurs in the tailoring but not in the base. + addSuffix(c, *ts); + ts = nullptr; + } else if(cmp > 0) { + // bs occurs in the base but not in the tailoring. + addSuffix(c, *bs); + bs = nullptr; + } else { + suffix = ts; + compare(c, (uint32_t)suffixes.getValue(), (uint32_t)baseSuffixes.getValue()); + suffix = nullptr; + ts = nullptr; + bs = nullptr; + } + } +} + +void +TailoredSet::addPrefixes(const CollationData *d, UChar32 c, const char16_t *p) { + UCharsTrie::Iterator prefixes(p, 0, errorCode); + while(prefixes.next(errorCode)) { + addPrefix(d, prefixes.getString(), c, (uint32_t)prefixes.getValue()); + } +} + +void +TailoredSet::addPrefix(const CollationData *d, const UnicodeString &pfx, UChar32 c, uint32_t ce32) { + setPrefix(pfx); + ce32 = d->getFinalCE32(ce32); + if(Collation::isContractionCE32(ce32)) { + const char16_t *p = d->contexts + Collation::indexFromCE32(ce32); + addContractions(c, p + 2); + } + tailored->add(UnicodeString(unreversedPrefix).append(c)); + resetPrefix(); +} + +void +TailoredSet::addContractions(UChar32 c, const char16_t *p) { + UCharsTrie::Iterator suffixes(p, 0, errorCode); + while(suffixes.next(errorCode)) { + addSuffix(c, suffixes.getString()); + } +} + +void +TailoredSet::addSuffix(UChar32 c, const UnicodeString &sfx) { + tailored->add(UnicodeString(unreversedPrefix).append(c).append(sfx)); +} + +void +TailoredSet::add(UChar32 c) { + if(unreversedPrefix.isEmpty() && suffix == nullptr) { + tailored->add(c); + } else { + UnicodeString s(unreversedPrefix); + s.append(c); + if(suffix != nullptr) { + s.append(*suffix); + } + tailored->add(s); + } +} + +ContractionsAndExpansions::CESink::~CESink() {} + +U_CDECL_BEGIN + +static UBool U_CALLCONV +enumCnERange(const void *context, UChar32 start, UChar32 end, uint32_t ce32) { + ContractionsAndExpansions *cne = (ContractionsAndExpansions *)context; + if(cne->checkTailored == 0) { + // There is no tailoring. + // No need to collect nor check the tailored set. + } else if(cne->checkTailored < 0) { + // Collect the set of code points with mappings in the tailoring data. + if(ce32 == Collation::FALLBACK_CE32) { + return true; // fallback to base, not tailored + } else { + cne->tailored.add(start, end); + } + // checkTailored > 0: Exclude tailored ranges from the base data enumeration. + } else if(start == end) { + if(cne->tailored.contains(start)) { + return true; + } + } else if(cne->tailored.containsSome(start, end)) { + cne->ranges.set(start, end).removeAll(cne->tailored); + int32_t count = cne->ranges.getRangeCount(); + for(int32_t i = 0; i < count; ++i) { + cne->handleCE32(cne->ranges.getRangeStart(i), cne->ranges.getRangeEnd(i), ce32); + } + return U_SUCCESS(cne->errorCode); + } + cne->handleCE32(start, end, ce32); + return U_SUCCESS(cne->errorCode); +} + +U_CDECL_END + +void +ContractionsAndExpansions::forData(const CollationData *d, UErrorCode &ec) { + if(U_FAILURE(ec)) { return; } + errorCode = ec; // Preserve info & warning codes. + // Add all from the data, can be tailoring or base. + if(d->base != nullptr) { + checkTailored = -1; + } + data = d; + utrie2_enum(data->trie, nullptr, enumCnERange, this); + if(d->base == nullptr || U_FAILURE(errorCode)) { + ec = errorCode; + return; + } + // Add all from the base data but only for un-tailored code points. + tailored.freeze(); + checkTailored = 1; + data = d->base; + utrie2_enum(data->trie, nullptr, enumCnERange, this); + ec = errorCode; +} + +void +ContractionsAndExpansions::forCodePoint(const CollationData *d, UChar32 c, UErrorCode &ec) { + if(U_FAILURE(ec)) { return; } + errorCode = ec; // Preserve info & warning codes. + uint32_t ce32 = d->getCE32(c); + if(ce32 == Collation::FALLBACK_CE32) { + d = d->base; + ce32 = d->getCE32(c); + } + data = d; + handleCE32(c, c, ce32); + ec = errorCode; +} + +void +ContractionsAndExpansions::handleCE32(UChar32 start, UChar32 end, uint32_t ce32) { + for(;;) { + if((ce32 & 0xff) < Collation::SPECIAL_CE32_LOW_BYTE) { + // !isSpecialCE32() + if(sink != nullptr) { + sink->handleCE(Collation::ceFromSimpleCE32(ce32)); + } + return; + } + switch(Collation::tagFromCE32(ce32)) { + case Collation::FALLBACK_TAG: + return; + case Collation::RESERVED_TAG_3: + case Collation::BUILDER_DATA_TAG: + case Collation::LEAD_SURROGATE_TAG: + if(U_SUCCESS(errorCode)) { errorCode = U_INTERNAL_PROGRAM_ERROR; } + return; + case Collation::LONG_PRIMARY_TAG: + if(sink != nullptr) { + sink->handleCE(Collation::ceFromLongPrimaryCE32(ce32)); + } + return; + case Collation::LONG_SECONDARY_TAG: + if(sink != nullptr) { + sink->handleCE(Collation::ceFromLongSecondaryCE32(ce32)); + } + return; + case Collation::LATIN_EXPANSION_TAG: + if(sink != nullptr) { + ces[0] = Collation::latinCE0FromCE32(ce32); + ces[1] = Collation::latinCE1FromCE32(ce32); + sink->handleExpansion(ces, 2); + } + // Optimization: If we have a prefix, + // then the relevant strings have been added already. + if(unreversedPrefix.isEmpty()) { + addExpansions(start, end); + } + return; + case Collation::EXPANSION32_TAG: + if(sink != nullptr) { + const uint32_t *ce32s = data->ce32s + Collation::indexFromCE32(ce32); + int32_t length = Collation::lengthFromCE32(ce32); + for(int32_t i = 0; i < length; ++i) { + ces[i] = Collation::ceFromCE32(*ce32s++); + } + sink->handleExpansion(ces, length); + } + // Optimization: If we have a prefix, + // then the relevant strings have been added already. + if(unreversedPrefix.isEmpty()) { + addExpansions(start, end); + } + return; + case Collation::EXPANSION_TAG: + if(sink != nullptr) { + int32_t length = Collation::lengthFromCE32(ce32); + sink->handleExpansion(data->ces + Collation::indexFromCE32(ce32), length); + } + // Optimization: If we have a prefix, + // then the relevant strings have been added already. + if(unreversedPrefix.isEmpty()) { + addExpansions(start, end); + } + return; + case Collation::PREFIX_TAG: + handlePrefixes(start, end, ce32); + return; + case Collation::CONTRACTION_TAG: + handleContractions(start, end, ce32); + return; + case Collation::DIGIT_TAG: + // Fetch the non-numeric-collation CE32 and continue. + ce32 = data->ce32s[Collation::indexFromCE32(ce32)]; + break; + case Collation::U0000_TAG: + U_ASSERT(start == 0 && end == 0); + // Fetch the normal ce32 for U+0000 and continue. + ce32 = data->ce32s[0]; + break; + case Collation::HANGUL_TAG: + if(sink != nullptr) { + // TODO: This should be optimized, + // especially if [start..end] is the complete Hangul range. (assert that) + UTF16CollationIterator iter(data, false, nullptr, nullptr, nullptr); + char16_t hangul[1] = { 0 }; + for(UChar32 c = start; c <= end; ++c) { + hangul[0] = (char16_t)c; + iter.setText(hangul, hangul + 1); + int32_t length = iter.fetchCEs(errorCode); + if(U_FAILURE(errorCode)) { return; } + // Ignore the terminating non-CE. + U_ASSERT(length >= 2 && iter.getCE(length - 1) == Collation::NO_CE); + sink->handleExpansion(iter.getCEs(), length - 1); + } + } + // Optimization: If we have a prefix, + // then the relevant strings have been added already. + if(unreversedPrefix.isEmpty()) { + addExpansions(start, end); + } + return; + case Collation::OFFSET_TAG: + // Currently no need to send offset CEs to the sink. + return; + case Collation::IMPLICIT_TAG: + // Currently no need to send implicit CEs to the sink. + return; + } + } +} + +void +ContractionsAndExpansions::handlePrefixes( + UChar32 start, UChar32 end, uint32_t ce32) { + const char16_t *p = data->contexts + Collation::indexFromCE32(ce32); + ce32 = CollationData::readCE32(p); // Default if no prefix match. + handleCE32(start, end, ce32); + if(!addPrefixes) { return; } + UCharsTrie::Iterator prefixes(p + 2, 0, errorCode); + while(prefixes.next(errorCode)) { + setPrefix(prefixes.getString()); + // Prefix/pre-context mappings are special kinds of contractions + // that always yield expansions. + addStrings(start, end, contractions); + addStrings(start, end, expansions); + handleCE32(start, end, (uint32_t)prefixes.getValue()); + } + resetPrefix(); +} + +void +ContractionsAndExpansions::handleContractions( + UChar32 start, UChar32 end, uint32_t ce32) { + const char16_t *p = data->contexts + Collation::indexFromCE32(ce32); + if((ce32 & Collation::CONTRACT_SINGLE_CP_NO_MATCH) != 0) { + // No match on the single code point. + // We are underneath a prefix, and the default mapping is just + // a fallback to the mappings for a shorter prefix. + U_ASSERT(!unreversedPrefix.isEmpty()); + } else { + ce32 = CollationData::readCE32(p); // Default if no suffix match. + U_ASSERT(!Collation::isContractionCE32(ce32)); + handleCE32(start, end, ce32); + } + UCharsTrie::Iterator suffixes(p + 2, 0, errorCode); + while(suffixes.next(errorCode)) { + suffix = &suffixes.getString(); + addStrings(start, end, contractions); + if(!unreversedPrefix.isEmpty()) { + addStrings(start, end, expansions); + } + handleCE32(start, end, (uint32_t)suffixes.getValue()); + } + suffix = nullptr; +} + +void +ContractionsAndExpansions::addExpansions(UChar32 start, UChar32 end) { + if(unreversedPrefix.isEmpty() && suffix == nullptr) { + if(expansions != nullptr) { + expansions->add(start, end); + } + } else { + addStrings(start, end, expansions); + } +} + +void +ContractionsAndExpansions::addStrings(UChar32 start, UChar32 end, UnicodeSet *set) { + if(set == nullptr) { return; } + UnicodeString s(unreversedPrefix); + do { + s.append(start); + if(suffix != nullptr) { + s.append(*suffix); + } + set->add(s); + s.truncate(unreversedPrefix.length()); + } while(++start <= end); +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationsets.h b/deps/icu-small/source/i18n/collationsets.h index aed41f7ac8d119..bb9a1d6115e397 100644 --- a/deps/icu-small/source/i18n/collationsets.h +++ b/deps/icu-small/source/i18n/collationsets.h @@ -1,144 +1,144 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationsets.h -* -* created on: 2013feb09 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONSETS_H__ -#define __COLLATIONSETS_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/uniset.h" -#include "collation.h" - -U_NAMESPACE_BEGIN - -struct CollationData; - -/** - * Finds the set of characters and strings that sort differently in the tailoring - * from the base data. - * - * Every mapping in the tailoring needs to be compared to the base, - * because some mappings are copied for optimization, and - * all contractions for a character are copied if any contractions for that character - * are added, modified or removed. - * - * It might be simpler to re-parse the rule string, but: - * - That would require duplicating some of the from-rules builder code. - * - That would make the runtime code depend on the builder. - * - That would only work if we have the rule string, and we allow users to - * omit the rule string from data files. - */ -class TailoredSet : public UMemory { -public: - TailoredSet(UnicodeSet *t) - : data(NULL), baseData(NULL), - tailored(t), - suffix(NULL), - errorCode(U_ZERO_ERROR) {} - - void forData(const CollationData *d, UErrorCode &errorCode); - - /** - * @return U_SUCCESS(errorCode) in C++, void in Java - * @internal only public for access by callback - */ - UBool handleCE32(UChar32 start, UChar32 end, uint32_t ce32); - -private: - void compare(UChar32 c, uint32_t ce32, uint32_t baseCE32); - void comparePrefixes(UChar32 c, const UChar *p, const UChar *q); - void compareContractions(UChar32 c, const UChar *p, const UChar *q); - - void addPrefixes(const CollationData *d, UChar32 c, const UChar *p); - void addPrefix(const CollationData *d, const UnicodeString &pfx, UChar32 c, uint32_t ce32); - void addContractions(UChar32 c, const UChar *p); - void addSuffix(UChar32 c, const UnicodeString &sfx); - void add(UChar32 c); - - /** Prefixes are reversed in the data structure. */ - void setPrefix(const UnicodeString &pfx) { - unreversedPrefix = pfx; - unreversedPrefix.reverse(); - } - void resetPrefix() { - unreversedPrefix.remove(); - } - - const CollationData *data; - const CollationData *baseData; - UnicodeSet *tailored; - UnicodeString unreversedPrefix; - const UnicodeString *suffix; - UErrorCode errorCode; -}; - -class ContractionsAndExpansions : public UMemory { -public: - class CESink : public UMemory { - public: - virtual ~CESink(); - virtual void handleCE(int64_t ce) = 0; - virtual void handleExpansion(const int64_t ces[], int32_t length) = 0; - }; - - ContractionsAndExpansions(UnicodeSet *con, UnicodeSet *exp, CESink *s, UBool prefixes) - : data(NULL), - contractions(con), expansions(exp), - sink(s), - addPrefixes(prefixes), - checkTailored(0), - suffix(NULL), - errorCode(U_ZERO_ERROR) {} - - void forData(const CollationData *d, UErrorCode &errorCode); - void forCodePoint(const CollationData *d, UChar32 c, UErrorCode &ec); - - // all following: @internal, only public for access by callback - - void handleCE32(UChar32 start, UChar32 end, uint32_t ce32); - - void handlePrefixes(UChar32 start, UChar32 end, uint32_t ce32); - void handleContractions(UChar32 start, UChar32 end, uint32_t ce32); - - void addExpansions(UChar32 start, UChar32 end); - void addStrings(UChar32 start, UChar32 end, UnicodeSet *set); - - /** Prefixes are reversed in the data structure. */ - void setPrefix(const UnicodeString &pfx) { - unreversedPrefix = pfx; - unreversedPrefix.reverse(); - } - void resetPrefix() { - unreversedPrefix.remove(); - } - - const CollationData *data; - UnicodeSet *contractions; - UnicodeSet *expansions; - CESink *sink; - UBool addPrefixes; - int8_t checkTailored; // -1: collected tailored +1: exclude tailored - UnicodeSet tailored; - UnicodeSet ranges; - UnicodeString unreversedPrefix; - const UnicodeString *suffix; - int64_t ces[Collation::MAX_EXPANSION_LENGTH]; - UErrorCode errorCode; -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONSETS_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationsets.h +* +* created on: 2013feb09 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONSETS_H__ +#define __COLLATIONSETS_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/uniset.h" +#include "collation.h" + +U_NAMESPACE_BEGIN + +struct CollationData; + +/** + * Finds the set of characters and strings that sort differently in the tailoring + * from the base data. + * + * Every mapping in the tailoring needs to be compared to the base, + * because some mappings are copied for optimization, and + * all contractions for a character are copied if any contractions for that character + * are added, modified or removed. + * + * It might be simpler to re-parse the rule string, but: + * - That would require duplicating some of the from-rules builder code. + * - That would make the runtime code depend on the builder. + * - That would only work if we have the rule string, and we allow users to + * omit the rule string from data files. + */ +class TailoredSet : public UMemory { +public: + TailoredSet(UnicodeSet *t) + : data(nullptr), baseData(nullptr), + tailored(t), + suffix(nullptr), + errorCode(U_ZERO_ERROR) {} + + void forData(const CollationData *d, UErrorCode &errorCode); + + /** + * @return U_SUCCESS(errorCode) in C++, void in Java + * @internal only public for access by callback + */ + UBool handleCE32(UChar32 start, UChar32 end, uint32_t ce32); + +private: + void compare(UChar32 c, uint32_t ce32, uint32_t baseCE32); + void comparePrefixes(UChar32 c, const char16_t *p, const char16_t *q); + void compareContractions(UChar32 c, const char16_t *p, const char16_t *q); + + void addPrefixes(const CollationData *d, UChar32 c, const char16_t *p); + void addPrefix(const CollationData *d, const UnicodeString &pfx, UChar32 c, uint32_t ce32); + void addContractions(UChar32 c, const char16_t *p); + void addSuffix(UChar32 c, const UnicodeString &sfx); + void add(UChar32 c); + + /** Prefixes are reversed in the data structure. */ + void setPrefix(const UnicodeString &pfx) { + unreversedPrefix = pfx; + unreversedPrefix.reverse(); + } + void resetPrefix() { + unreversedPrefix.remove(); + } + + const CollationData *data; + const CollationData *baseData; + UnicodeSet *tailored; + UnicodeString unreversedPrefix; + const UnicodeString *suffix; + UErrorCode errorCode; +}; + +class ContractionsAndExpansions : public UMemory { +public: + class CESink : public UMemory { + public: + virtual ~CESink(); + virtual void handleCE(int64_t ce) = 0; + virtual void handleExpansion(const int64_t ces[], int32_t length) = 0; + }; + + ContractionsAndExpansions(UnicodeSet *con, UnicodeSet *exp, CESink *s, UBool prefixes) + : data(nullptr), + contractions(con), expansions(exp), + sink(s), + addPrefixes(prefixes), + checkTailored(0), + suffix(nullptr), + errorCode(U_ZERO_ERROR) {} + + void forData(const CollationData *d, UErrorCode &errorCode); + void forCodePoint(const CollationData *d, UChar32 c, UErrorCode &ec); + + // all following: @internal, only public for access by callback + + void handleCE32(UChar32 start, UChar32 end, uint32_t ce32); + + void handlePrefixes(UChar32 start, UChar32 end, uint32_t ce32); + void handleContractions(UChar32 start, UChar32 end, uint32_t ce32); + + void addExpansions(UChar32 start, UChar32 end); + void addStrings(UChar32 start, UChar32 end, UnicodeSet *set); + + /** Prefixes are reversed in the data structure. */ + void setPrefix(const UnicodeString &pfx) { + unreversedPrefix = pfx; + unreversedPrefix.reverse(); + } + void resetPrefix() { + unreversedPrefix.remove(); + } + + const CollationData *data; + UnicodeSet *contractions; + UnicodeSet *expansions; + CESink *sink; + UBool addPrefixes; + int8_t checkTailored; // -1: collected tailored +1: exclude tailored + UnicodeSet tailored; + UnicodeSet ranges; + UnicodeString unreversedPrefix; + const UnicodeString *suffix; + int64_t ces[Collation::MAX_EXPANSION_LENGTH]; + UErrorCode errorCode; +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONSETS_H__ diff --git a/deps/icu-small/source/i18n/collationsettings.cpp b/deps/icu-small/source/i18n/collationsettings.cpp index fe051880b81ff2..e08d1e7086aff8 100644 --- a/deps/icu-small/source/i18n/collationsettings.cpp +++ b/deps/icu-small/source/i18n/collationsettings.cpp @@ -1,377 +1,377 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationsettings.cpp -* -* created on: 2013feb07 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/ucol.h" -#include "cmemory.h" -#include "collation.h" -#include "collationdata.h" -#include "collationsettings.h" -#include "sharedobject.h" -#include "uassert.h" -#include "umutex.h" -#include "uvectr32.h" - -U_NAMESPACE_BEGIN - -CollationSettings::CollationSettings(const CollationSettings &other) - : SharedObject(other), - options(other.options), variableTop(other.variableTop), - reorderTable(NULL), - minHighNoReorder(other.minHighNoReorder), - reorderRanges(NULL), reorderRangesLength(0), - reorderCodes(NULL), reorderCodesLength(0), reorderCodesCapacity(0), - fastLatinOptions(other.fastLatinOptions) { - UErrorCode errorCode = U_ZERO_ERROR; - copyReorderingFrom(other, errorCode); - if(fastLatinOptions >= 0) { - uprv_memcpy(fastLatinPrimaries, other.fastLatinPrimaries, sizeof(fastLatinPrimaries)); - } -} - -CollationSettings::~CollationSettings() { - if(reorderCodesCapacity != 0) { - uprv_free(const_cast(reorderCodes)); - } -} - -bool -CollationSettings::operator==(const CollationSettings &other) const { - if(options != other.options) { return false; } - if((options & ALTERNATE_MASK) != 0 && variableTop != other.variableTop) { return false; } - if(reorderCodesLength != other.reorderCodesLength) { return false; } - for(int32_t i = 0; i < reorderCodesLength; ++i) { - if(reorderCodes[i] != other.reorderCodes[i]) { return false; } - } - return true; -} - -int32_t -CollationSettings::hashCode() const { - int32_t h = options << 8; - if((options & ALTERNATE_MASK) != 0) { h ^= variableTop; } - h ^= reorderCodesLength; - for(int32_t i = 0; i < reorderCodesLength; ++i) { - h ^= (reorderCodes[i] << i); - } - return h; -} - -void -CollationSettings::resetReordering() { - // When we turn off reordering, we want to set a NULL permutation - // rather than a no-op permutation. - // Keep the memory via reorderCodes and its capacity. - reorderTable = NULL; - minHighNoReorder = 0; - reorderRangesLength = 0; - reorderCodesLength = 0; -} - -void -CollationSettings::aliasReordering(const CollationData &data, const int32_t *codes, int32_t length, - const uint32_t *ranges, int32_t rangesLength, - const uint8_t *table, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - if(table != NULL && - (rangesLength == 0 ? - !reorderTableHasSplitBytes(table) : - rangesLength >= 2 && - // The first offset must be 0. The last offset must not be 0. - (ranges[0] & 0xffff) == 0 && (ranges[rangesLength - 1] & 0xffff) != 0)) { - // We need to release the memory before setting the alias pointer. - if(reorderCodesCapacity != 0) { - uprv_free(const_cast(reorderCodes)); - reorderCodesCapacity = 0; - } - reorderTable = table; - reorderCodes = codes; - reorderCodesLength = length; - // Drop ranges before the first split byte. They are reordered by the table. - // This then speeds up reordering of the remaining ranges. - int32_t firstSplitByteRangeIndex = 0; - while(firstSplitByteRangeIndex < rangesLength && - (ranges[firstSplitByteRangeIndex] & 0xff0000) == 0) { - // The second byte of the primary limit is 0. - ++firstSplitByteRangeIndex; - } - if(firstSplitByteRangeIndex == rangesLength) { - U_ASSERT(!reorderTableHasSplitBytes(table)); - minHighNoReorder = 0; - reorderRanges = NULL; - reorderRangesLength = 0; - } else { - U_ASSERT(table[ranges[firstSplitByteRangeIndex] >> 24] == 0); - minHighNoReorder = ranges[rangesLength - 1] & 0xffff0000; - reorderRanges = ranges + firstSplitByteRangeIndex; - reorderRangesLength = rangesLength - firstSplitByteRangeIndex; - } - return; - } - // Regenerate missing data. - setReordering(data, codes, length, errorCode); -} - -void -CollationSettings::setReordering(const CollationData &data, - const int32_t *codes, int32_t codesLength, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - if(codesLength == 0 || (codesLength == 1 && codes[0] == UCOL_REORDER_CODE_NONE)) { - resetReordering(); - return; - } - UVector32 rangesList(errorCode); - data.makeReorderRanges(codes, codesLength, rangesList, errorCode); - if(U_FAILURE(errorCode)) { return; } - int32_t rangesLength = rangesList.size(); - if(rangesLength == 0) { - resetReordering(); - return; - } - const uint32_t *ranges = reinterpret_cast(rangesList.getBuffer()); - // ranges[] contains at least two (limit, offset) pairs. - // The first offset must be 0. The last offset must not be 0. - // Separators (at the low end) and trailing weights (at the high end) - // are never reordered. - U_ASSERT(rangesLength >= 2); - U_ASSERT((ranges[0] & 0xffff) == 0 && (ranges[rangesLength - 1] & 0xffff) != 0); - minHighNoReorder = ranges[rangesLength - 1] & 0xffff0000; - - // Write the lead byte permutation table. - // Set a 0 for each lead byte that has a range boundary in the middle. - uint8_t table[256]; - int32_t b = 0; - int32_t firstSplitByteRangeIndex = -1; - for(int32_t i = 0; i < rangesLength; ++i) { - uint32_t pair = ranges[i]; - int32_t limit1 = (int32_t)(pair >> 24); - while(b < limit1) { - table[b] = (uint8_t)(b + pair); - ++b; - } - // Check the second byte of the limit. - if((pair & 0xff0000) != 0) { - table[limit1] = 0; - b = limit1 + 1; - if(firstSplitByteRangeIndex < 0) { - firstSplitByteRangeIndex = i; - } - } - } - while(b <= 0xff) { - table[b] = (uint8_t)b; - ++b; - } - if(firstSplitByteRangeIndex < 0) { - // The lead byte permutation table alone suffices for reordering. - rangesLength = 0; - } else { - // Remove the ranges below the first split byte. - ranges += firstSplitByteRangeIndex; - rangesLength -= firstSplitByteRangeIndex; - } - setReorderArrays(codes, codesLength, ranges, rangesLength, table, errorCode); -} - -void -CollationSettings::setReorderArrays(const int32_t *codes, int32_t codesLength, - const uint32_t *ranges, int32_t rangesLength, - const uint8_t *table, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - int32_t *ownedCodes; - int32_t totalLength = codesLength + rangesLength; - U_ASSERT(totalLength > 0); - if(totalLength <= reorderCodesCapacity) { - ownedCodes = const_cast(reorderCodes); - } else { - // Allocate one memory block for the codes, the ranges, and the 16-aligned table. - int32_t capacity = (totalLength + 3) & ~3; // round up to a multiple of 4 ints - ownedCodes = (int32_t *)uprv_malloc(capacity * 4 + 256); - if(ownedCodes == NULL) { - resetReordering(); - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - if(reorderCodesCapacity != 0) { - uprv_free(const_cast(reorderCodes)); - } - reorderCodes = ownedCodes; - reorderCodesCapacity = capacity; - } - uprv_memcpy(ownedCodes + reorderCodesCapacity, table, 256); - uprv_memcpy(ownedCodes, codes, codesLength * 4); - uprv_memcpy(ownedCodes + codesLength, ranges, rangesLength * 4); - reorderTable = reinterpret_cast(reorderCodes + reorderCodesCapacity); - reorderCodesLength = codesLength; - reorderRanges = reinterpret_cast(ownedCodes) + codesLength; - reorderRangesLength = rangesLength; -} - -void -CollationSettings::copyReorderingFrom(const CollationSettings &other, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - if(!other.hasReordering()) { - resetReordering(); - return; - } - minHighNoReorder = other.minHighNoReorder; - if(other.reorderCodesCapacity == 0) { - // The reorder arrays are aliased to memory-mapped data. - reorderTable = other.reorderTable; - reorderRanges = other.reorderRanges; - reorderRangesLength = other.reorderRangesLength; - reorderCodes = other.reorderCodes; - reorderCodesLength = other.reorderCodesLength; - } else { - setReorderArrays(other.reorderCodes, other.reorderCodesLength, - other.reorderRanges, other.reorderRangesLength, - other.reorderTable, errorCode); - } -} - -UBool -CollationSettings::reorderTableHasSplitBytes(const uint8_t table[256]) { - U_ASSERT(table[0] == 0); - for(int32_t i = 1; i < 256; ++i) { - if(table[i] == 0) { - return true; - } - } - return false; -} - -uint32_t -CollationSettings::reorderEx(uint32_t p) const { - if(p >= minHighNoReorder) { return p; } - // Round up p so that its lower 16 bits are >= any offset bits. - // Then compare q directly with (limit, offset) pairs. - uint32_t q = p | 0xffff; - uint32_t r; - const uint32_t *ranges = reorderRanges; - while(q >= (r = *ranges)) { ++ranges; } - return p + (r << 24); -} - -void -CollationSettings::setStrength(int32_t value, int32_t defaultOptions, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - int32_t noStrength = options & ~STRENGTH_MASK; - switch(value) { - case UCOL_PRIMARY: - case UCOL_SECONDARY: - case UCOL_TERTIARY: - case UCOL_QUATERNARY: - case UCOL_IDENTICAL: - options = noStrength | (value << STRENGTH_SHIFT); - break; - case UCOL_DEFAULT: - options = noStrength | (defaultOptions & STRENGTH_MASK); - break; - default: - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - break; - } -} - -void -CollationSettings::setFlag(int32_t bit, UColAttributeValue value, - int32_t defaultOptions, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - switch(value) { - case UCOL_ON: - options |= bit; - break; - case UCOL_OFF: - options &= ~bit; - break; - case UCOL_DEFAULT: - options = (options & ~bit) | (defaultOptions & bit); - break; - default: - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - break; - } -} - -void -CollationSettings::setCaseFirst(UColAttributeValue value, - int32_t defaultOptions, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - int32_t noCaseFirst = options & ~CASE_FIRST_AND_UPPER_MASK; - switch(value) { - case UCOL_OFF: - options = noCaseFirst; - break; - case UCOL_LOWER_FIRST: - options = noCaseFirst | CASE_FIRST; - break; - case UCOL_UPPER_FIRST: - options = noCaseFirst | CASE_FIRST_AND_UPPER_MASK; - break; - case UCOL_DEFAULT: - options = noCaseFirst | (defaultOptions & CASE_FIRST_AND_UPPER_MASK); - break; - default: - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - break; - } -} - -void -CollationSettings::setAlternateHandling(UColAttributeValue value, - int32_t defaultOptions, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - int32_t noAlternate = options & ~ALTERNATE_MASK; - switch(value) { - case UCOL_NON_IGNORABLE: - options = noAlternate; - break; - case UCOL_SHIFTED: - options = noAlternate | SHIFTED; - break; - case UCOL_DEFAULT: - options = noAlternate | (defaultOptions & ALTERNATE_MASK); - break; - default: - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - break; - } -} - -void -CollationSettings::setMaxVariable(int32_t value, int32_t defaultOptions, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - int32_t noMax = options & ~MAX_VARIABLE_MASK; - switch(value) { - case MAX_VAR_SPACE: - case MAX_VAR_PUNCT: - case MAX_VAR_SYMBOL: - case MAX_VAR_CURRENCY: - options = noMax | (value << MAX_VARIABLE_SHIFT); - break; - case UCOL_DEFAULT: - options = noMax | (defaultOptions & MAX_VARIABLE_MASK); - break; - default: - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - break; - } -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationsettings.cpp +* +* created on: 2013feb07 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/ucol.h" +#include "cmemory.h" +#include "collation.h" +#include "collationdata.h" +#include "collationsettings.h" +#include "sharedobject.h" +#include "uassert.h" +#include "umutex.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +CollationSettings::CollationSettings(const CollationSettings &other) + : SharedObject(other), + options(other.options), variableTop(other.variableTop), + reorderTable(nullptr), + minHighNoReorder(other.minHighNoReorder), + reorderRanges(nullptr), reorderRangesLength(0), + reorderCodes(nullptr), reorderCodesLength(0), reorderCodesCapacity(0), + fastLatinOptions(other.fastLatinOptions) { + UErrorCode errorCode = U_ZERO_ERROR; + copyReorderingFrom(other, errorCode); + if(fastLatinOptions >= 0) { + uprv_memcpy(fastLatinPrimaries, other.fastLatinPrimaries, sizeof(fastLatinPrimaries)); + } +} + +CollationSettings::~CollationSettings() { + if(reorderCodesCapacity != 0) { + uprv_free(const_cast(reorderCodes)); + } +} + +bool +CollationSettings::operator==(const CollationSettings &other) const { + if(options != other.options) { return false; } + if((options & ALTERNATE_MASK) != 0 && variableTop != other.variableTop) { return false; } + if(reorderCodesLength != other.reorderCodesLength) { return false; } + for(int32_t i = 0; i < reorderCodesLength; ++i) { + if(reorderCodes[i] != other.reorderCodes[i]) { return false; } + } + return true; +} + +int32_t +CollationSettings::hashCode() const { + int32_t h = options << 8; + if((options & ALTERNATE_MASK) != 0) { h ^= variableTop; } + h ^= reorderCodesLength; + for(int32_t i = 0; i < reorderCodesLength; ++i) { + h ^= (reorderCodes[i] << i); + } + return h; +} + +void +CollationSettings::resetReordering() { + // When we turn off reordering, we want to set a nullptr permutation + // rather than a no-op permutation. + // Keep the memory via reorderCodes and its capacity. + reorderTable = nullptr; + minHighNoReorder = 0; + reorderRangesLength = 0; + reorderCodesLength = 0; +} + +void +CollationSettings::aliasReordering(const CollationData &data, const int32_t *codes, int32_t length, + const uint32_t *ranges, int32_t rangesLength, + const uint8_t *table, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + if(table != nullptr && + (rangesLength == 0 ? + !reorderTableHasSplitBytes(table) : + rangesLength >= 2 && + // The first offset must be 0. The last offset must not be 0. + (ranges[0] & 0xffff) == 0 && (ranges[rangesLength - 1] & 0xffff) != 0)) { + // We need to release the memory before setting the alias pointer. + if(reorderCodesCapacity != 0) { + uprv_free(const_cast(reorderCodes)); + reorderCodesCapacity = 0; + } + reorderTable = table; + reorderCodes = codes; + reorderCodesLength = length; + // Drop ranges before the first split byte. They are reordered by the table. + // This then speeds up reordering of the remaining ranges. + int32_t firstSplitByteRangeIndex = 0; + while(firstSplitByteRangeIndex < rangesLength && + (ranges[firstSplitByteRangeIndex] & 0xff0000) == 0) { + // The second byte of the primary limit is 0. + ++firstSplitByteRangeIndex; + } + if(firstSplitByteRangeIndex == rangesLength) { + U_ASSERT(!reorderTableHasSplitBytes(table)); + minHighNoReorder = 0; + reorderRanges = nullptr; + reorderRangesLength = 0; + } else { + U_ASSERT(table[ranges[firstSplitByteRangeIndex] >> 24] == 0); + minHighNoReorder = ranges[rangesLength - 1] & 0xffff0000; + reorderRanges = ranges + firstSplitByteRangeIndex; + reorderRangesLength = rangesLength - firstSplitByteRangeIndex; + } + return; + } + // Regenerate missing data. + setReordering(data, codes, length, errorCode); +} + +void +CollationSettings::setReordering(const CollationData &data, + const int32_t *codes, int32_t codesLength, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + if(codesLength == 0 || (codesLength == 1 && codes[0] == UCOL_REORDER_CODE_NONE)) { + resetReordering(); + return; + } + UVector32 rangesList(errorCode); + data.makeReorderRanges(codes, codesLength, rangesList, errorCode); + if(U_FAILURE(errorCode)) { return; } + int32_t rangesLength = rangesList.size(); + if(rangesLength == 0) { + resetReordering(); + return; + } + const uint32_t *ranges = reinterpret_cast(rangesList.getBuffer()); + // ranges[] contains at least two (limit, offset) pairs. + // The first offset must be 0. The last offset must not be 0. + // Separators (at the low end) and trailing weights (at the high end) + // are never reordered. + U_ASSERT(rangesLength >= 2); + U_ASSERT((ranges[0] & 0xffff) == 0 && (ranges[rangesLength - 1] & 0xffff) != 0); + minHighNoReorder = ranges[rangesLength - 1] & 0xffff0000; + + // Write the lead byte permutation table. + // Set a 0 for each lead byte that has a range boundary in the middle. + uint8_t table[256]; + int32_t b = 0; + int32_t firstSplitByteRangeIndex = -1; + for(int32_t i = 0; i < rangesLength; ++i) { + uint32_t pair = ranges[i]; + int32_t limit1 = (int32_t)(pair >> 24); + while(b < limit1) { + table[b] = (uint8_t)(b + pair); + ++b; + } + // Check the second byte of the limit. + if((pair & 0xff0000) != 0) { + table[limit1] = 0; + b = limit1 + 1; + if(firstSplitByteRangeIndex < 0) { + firstSplitByteRangeIndex = i; + } + } + } + while(b <= 0xff) { + table[b] = (uint8_t)b; + ++b; + } + if(firstSplitByteRangeIndex < 0) { + // The lead byte permutation table alone suffices for reordering. + rangesLength = 0; + } else { + // Remove the ranges below the first split byte. + ranges += firstSplitByteRangeIndex; + rangesLength -= firstSplitByteRangeIndex; + } + setReorderArrays(codes, codesLength, ranges, rangesLength, table, errorCode); +} + +void +CollationSettings::setReorderArrays(const int32_t *codes, int32_t codesLength, + const uint32_t *ranges, int32_t rangesLength, + const uint8_t *table, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + int32_t *ownedCodes; + int32_t totalLength = codesLength + rangesLength; + U_ASSERT(totalLength > 0); + if(totalLength <= reorderCodesCapacity) { + ownedCodes = const_cast(reorderCodes); + } else { + // Allocate one memory block for the codes, the ranges, and the 16-aligned table. + int32_t capacity = (totalLength + 3) & ~3; // round up to a multiple of 4 ints + ownedCodes = (int32_t *)uprv_malloc(capacity * 4 + 256); + if(ownedCodes == nullptr) { + resetReordering(); + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + if(reorderCodesCapacity != 0) { + uprv_free(const_cast(reorderCodes)); + } + reorderCodes = ownedCodes; + reorderCodesCapacity = capacity; + } + uprv_memcpy(ownedCodes + reorderCodesCapacity, table, 256); + uprv_memcpy(ownedCodes, codes, codesLength * 4); + uprv_memcpy(ownedCodes + codesLength, ranges, rangesLength * 4); + reorderTable = reinterpret_cast(reorderCodes + reorderCodesCapacity); + reorderCodesLength = codesLength; + reorderRanges = reinterpret_cast(ownedCodes) + codesLength; + reorderRangesLength = rangesLength; +} + +void +CollationSettings::copyReorderingFrom(const CollationSettings &other, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + if(!other.hasReordering()) { + resetReordering(); + return; + } + minHighNoReorder = other.minHighNoReorder; + if(other.reorderCodesCapacity == 0) { + // The reorder arrays are aliased to memory-mapped data. + reorderTable = other.reorderTable; + reorderRanges = other.reorderRanges; + reorderRangesLength = other.reorderRangesLength; + reorderCodes = other.reorderCodes; + reorderCodesLength = other.reorderCodesLength; + } else { + setReorderArrays(other.reorderCodes, other.reorderCodesLength, + other.reorderRanges, other.reorderRangesLength, + other.reorderTable, errorCode); + } +} + +UBool +CollationSettings::reorderTableHasSplitBytes(const uint8_t table[256]) { + U_ASSERT(table[0] == 0); + for(int32_t i = 1; i < 256; ++i) { + if(table[i] == 0) { + return true; + } + } + return false; +} + +uint32_t +CollationSettings::reorderEx(uint32_t p) const { + if(p >= minHighNoReorder) { return p; } + // Round up p so that its lower 16 bits are >= any offset bits. + // Then compare q directly with (limit, offset) pairs. + uint32_t q = p | 0xffff; + uint32_t r; + const uint32_t *ranges = reorderRanges; + while(q >= (r = *ranges)) { ++ranges; } + return p + (r << 24); +} + +void +CollationSettings::setStrength(int32_t value, int32_t defaultOptions, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + int32_t noStrength = options & ~STRENGTH_MASK; + switch(value) { + case UCOL_PRIMARY: + case UCOL_SECONDARY: + case UCOL_TERTIARY: + case UCOL_QUATERNARY: + case UCOL_IDENTICAL: + options = noStrength | (value << STRENGTH_SHIFT); + break; + case UCOL_DEFAULT: + options = noStrength | (defaultOptions & STRENGTH_MASK); + break; + default: + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + break; + } +} + +void +CollationSettings::setFlag(int32_t bit, UColAttributeValue value, + int32_t defaultOptions, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + switch(value) { + case UCOL_ON: + options |= bit; + break; + case UCOL_OFF: + options &= ~bit; + break; + case UCOL_DEFAULT: + options = (options & ~bit) | (defaultOptions & bit); + break; + default: + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + break; + } +} + +void +CollationSettings::setCaseFirst(UColAttributeValue value, + int32_t defaultOptions, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + int32_t noCaseFirst = options & ~CASE_FIRST_AND_UPPER_MASK; + switch(value) { + case UCOL_OFF: + options = noCaseFirst; + break; + case UCOL_LOWER_FIRST: + options = noCaseFirst | CASE_FIRST; + break; + case UCOL_UPPER_FIRST: + options = noCaseFirst | CASE_FIRST_AND_UPPER_MASK; + break; + case UCOL_DEFAULT: + options = noCaseFirst | (defaultOptions & CASE_FIRST_AND_UPPER_MASK); + break; + default: + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + break; + } +} + +void +CollationSettings::setAlternateHandling(UColAttributeValue value, + int32_t defaultOptions, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + int32_t noAlternate = options & ~ALTERNATE_MASK; + switch(value) { + case UCOL_NON_IGNORABLE: + options = noAlternate; + break; + case UCOL_SHIFTED: + options = noAlternate | SHIFTED; + break; + case UCOL_DEFAULT: + options = noAlternate | (defaultOptions & ALTERNATE_MASK); + break; + default: + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + break; + } +} + +void +CollationSettings::setMaxVariable(int32_t value, int32_t defaultOptions, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + int32_t noMax = options & ~MAX_VARIABLE_MASK; + switch(value) { + case MAX_VAR_SPACE: + case MAX_VAR_PUNCT: + case MAX_VAR_SYMBOL: + case MAX_VAR_CURRENCY: + options = noMax | (value << MAX_VARIABLE_SHIFT); + break; + case UCOL_DEFAULT: + options = noMax | (defaultOptions & MAX_VARIABLE_MASK); + break; + default: + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + break; + } +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationsettings.h b/deps/icu-small/source/i18n/collationsettings.h index 3da8f6214f64ee..23bffb19ba5bec 100644 --- a/deps/icu-small/source/i18n/collationsettings.h +++ b/deps/icu-small/source/i18n/collationsettings.h @@ -1,274 +1,274 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationsettings.h -* -* created on: 2013feb07 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONSETTINGS_H__ -#define __COLLATIONSETTINGS_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/ucol.h" -#include "collation.h" -#include "sharedobject.h" -#include "umutex.h" - -U_NAMESPACE_BEGIN - -struct CollationData; - -/** - * Collation settings/options/attributes. - * These are the values that can be changed via API. - */ -struct U_I18N_API CollationSettings : public SharedObject { - /** - * Options bit 0: Perform the FCD check on the input text and deliver normalized text. - */ - static const int32_t CHECK_FCD = 1; - /** - * Options bit 1: Numeric collation. - * Also known as CODAN = COllate Digits As Numbers. - * - * Treat digit sequences as numbers with CE sequences in numeric order, - * rather than returning a normal CE for each digit. - */ - static const int32_t NUMERIC = 2; - /** - * "Shifted" alternate handling, see ALTERNATE_MASK. - */ - static const int32_t SHIFTED = 4; - /** - * Options bits 3..2: Alternate-handling mask. 0 for non-ignorable. - * Reserve values 8 and 0xc for shift-trimmed and blanked. - */ - static const int32_t ALTERNATE_MASK = 0xc; - /** - * Options bits 6..4: The 3-bit maxVariable value bit field is shifted by this value. - */ - static const int32_t MAX_VARIABLE_SHIFT = 4; - /** maxVariable options bit mask before shifting. */ - static const int32_t MAX_VARIABLE_MASK = 0x70; - /** Options bit 7: Reserved/unused/0. */ - /** - * Options bit 8: Sort uppercase first if caseLevel or caseFirst is on. - */ - static const int32_t UPPER_FIRST = 0x100; - /** - * Options bit 9: Keep the case bits in the tertiary weight (they trump other tertiary values) - * unless case level is on (when they are *moved* into the separate case level). - * By default, the case bits are removed from the tertiary weight (ignored). - * - * When CASE_FIRST is off, UPPER_FIRST must be off too, corresponding to - * the tri-value UCOL_CASE_FIRST attribute: UCOL_OFF vs. UCOL_LOWER_FIRST vs. UCOL_UPPER_FIRST. - */ - static const int32_t CASE_FIRST = 0x200; - /** - * Options bit mask for caseFirst and upperFirst, before shifting. - * Same value as caseFirst==upperFirst. - */ - static const int32_t CASE_FIRST_AND_UPPER_MASK = CASE_FIRST | UPPER_FIRST; - /** - * Options bit 10: Insert the case level between the secondary and tertiary levels. - */ - static const int32_t CASE_LEVEL = 0x400; - /** - * Options bit 11: Compare secondary weights backwards. ("French secondary") - */ - static const int32_t BACKWARD_SECONDARY = 0x800; - /** - * Options bits 15..12: The 4-bit strength value bit field is shifted by this value. - * It is the top used bit field in the options. (No need to mask after shifting.) - */ - static const int32_t STRENGTH_SHIFT = 12; - /** Strength options bit mask before shifting. */ - static const int32_t STRENGTH_MASK = 0xf000; - - /** maxVariable values */ - enum MaxVariable { - MAX_VAR_SPACE, - MAX_VAR_PUNCT, - MAX_VAR_SYMBOL, - MAX_VAR_CURRENCY - }; - - CollationSettings() - : options((UCOL_DEFAULT_STRENGTH << STRENGTH_SHIFT) | - (MAX_VAR_PUNCT << MAX_VARIABLE_SHIFT)), - variableTop(0), - reorderTable(NULL), - minHighNoReorder(0), - reorderRanges(NULL), reorderRangesLength(0), - reorderCodes(NULL), reorderCodesLength(0), reorderCodesCapacity(0), - fastLatinOptions(-1) {} - - CollationSettings(const CollationSettings &other); - virtual ~CollationSettings(); - - bool operator==(const CollationSettings &other) const; - - inline bool operator!=(const CollationSettings &other) const { - return !operator==(other); - } - - int32_t hashCode() const; - - void resetReordering(); - void aliasReordering(const CollationData &data, const int32_t *codes, int32_t length, - const uint32_t *ranges, int32_t rangesLength, - const uint8_t *table, UErrorCode &errorCode); - void setReordering(const CollationData &data, const int32_t *codes, int32_t codesLength, - UErrorCode &errorCode); - void copyReorderingFrom(const CollationSettings &other, UErrorCode &errorCode); - - inline UBool hasReordering() const { return reorderTable != NULL; } - static UBool reorderTableHasSplitBytes(const uint8_t table[256]); - inline uint32_t reorder(uint32_t p) const { - uint8_t b = reorderTable[p >> 24]; - if(b != 0 || p <= Collation::NO_CE_PRIMARY) { - return ((uint32_t)b << 24) | (p & 0xffffff); - } else { - return reorderEx(p); - } - } - - void setStrength(int32_t value, int32_t defaultOptions, UErrorCode &errorCode); - - static int32_t getStrength(int32_t options) { - return options >> STRENGTH_SHIFT; - } - - int32_t getStrength() const { - return getStrength(options); - } - - /** Sets the options bit for an on/off attribute. */ - void setFlag(int32_t bit, UColAttributeValue value, - int32_t defaultOptions, UErrorCode &errorCode); - - UColAttributeValue getFlag(int32_t bit) const { - return ((options & bit) != 0) ? UCOL_ON : UCOL_OFF; - } - - void setCaseFirst(UColAttributeValue value, int32_t defaultOptions, UErrorCode &errorCode); - - UColAttributeValue getCaseFirst() const { - int32_t option = options & CASE_FIRST_AND_UPPER_MASK; - return (option == 0) ? UCOL_OFF : - (option == CASE_FIRST) ? UCOL_LOWER_FIRST : UCOL_UPPER_FIRST; - } - - void setAlternateHandling(UColAttributeValue value, - int32_t defaultOptions, UErrorCode &errorCode); - - UColAttributeValue getAlternateHandling() const { - return ((options & ALTERNATE_MASK) == 0) ? UCOL_NON_IGNORABLE : UCOL_SHIFTED; - } - - void setMaxVariable(int32_t value, int32_t defaultOptions, UErrorCode &errorCode); - - MaxVariable getMaxVariable() const { - return (MaxVariable)((options & MAX_VARIABLE_MASK) >> MAX_VARIABLE_SHIFT); - } - - /** - * Include case bits in the tertiary level if caseLevel=off and caseFirst!=off. - */ - static inline UBool isTertiaryWithCaseBits(int32_t options) { - return (options & (CASE_LEVEL | CASE_FIRST)) == CASE_FIRST; - } - static uint32_t getTertiaryMask(int32_t options) { - // Remove the case bits from the tertiary weight when caseLevel is on or caseFirst is off. - return isTertiaryWithCaseBits(options) ? - Collation::CASE_AND_TERTIARY_MASK : Collation::ONLY_TERTIARY_MASK; - } - - static UBool sortsTertiaryUpperCaseFirst(int32_t options) { - // On tertiary level, consider case bits and sort uppercase first - // if caseLevel is off and caseFirst==upperFirst. - return (options & (CASE_LEVEL | CASE_FIRST_AND_UPPER_MASK)) == CASE_FIRST_AND_UPPER_MASK; - } - - inline UBool dontCheckFCD() const { - return (options & CHECK_FCD) == 0; - } - - inline UBool hasBackwardSecondary() const { - return (options & BACKWARD_SECONDARY) != 0; - } - - inline UBool isNumeric() const { - return (options & NUMERIC) != 0; - } - - /** CHECK_FCD etc. */ - int32_t options; - /** Variable-top primary weight. */ - uint32_t variableTop; - /** - * 256-byte table for reordering permutation of primary lead bytes; NULL if no reordering. - * A 0 entry at a non-zero index means that the primary lead byte is "split" - * (there are different offsets for primaries that share that lead byte) - * and the reordering offset must be determined via the reorderRanges. - */ - const uint8_t *reorderTable; - /** Limit of last reordered range. 0 if no reordering or no split bytes. */ - uint32_t minHighNoReorder; - /** - * Primary-weight ranges for script reordering, - * to be used by reorder(p) for split-reordered primary lead bytes. - * - * Each entry is a (limit, offset) pair. - * The upper 16 bits of the entry are the upper 16 bits of the - * exclusive primary limit of a range. - * Primaries between the previous limit and this one have their lead bytes - * modified by the signed offset (-0xff..+0xff) stored in the lower 16 bits. - * - * CollationData::makeReorderRanges() writes a full list where the first range - * (at least for terminators and separators) has a 0 offset. - * The last range has a non-zero offset. - * minHighNoReorder is set to the limit of that last range. - * - * In the settings object, the initial ranges before the first split lead byte - * are omitted for efficiency; they are handled by reorder(p) via the reorderTable. - * If there are no split-reordered lead bytes, then no ranges are needed. - */ - const uint32_t *reorderRanges; - int32_t reorderRangesLength; - /** Array of reorder codes; ignored if reorderCodesLength == 0. */ - const int32_t *reorderCodes; - /** Number of reorder codes; 0 if no reordering. */ - int32_t reorderCodesLength; - /** - * Capacity of reorderCodes. - * If 0, then the codes, the ranges, and the table are aliases. - * Otherwise, this object owns the memory via the reorderCodes pointer; - * the codes, the ranges, and the table are in the same memory block, in that order. - */ - int32_t reorderCodesCapacity; - - /** Options for CollationFastLatin. Negative if disabled. */ - int32_t fastLatinOptions; - uint16_t fastLatinPrimaries[0x180]; - -private: - void setReorderArrays(const int32_t *codes, int32_t codesLength, - const uint32_t *ranges, int32_t rangesLength, - const uint8_t *table, UErrorCode &errorCode); - uint32_t reorderEx(uint32_t p) const; -}; - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONSETTINGS_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationsettings.h +* +* created on: 2013feb07 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONSETTINGS_H__ +#define __COLLATIONSETTINGS_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/ucol.h" +#include "collation.h" +#include "sharedobject.h" +#include "umutex.h" + +U_NAMESPACE_BEGIN + +struct CollationData; + +/** + * Collation settings/options/attributes. + * These are the values that can be changed via API. + */ +struct U_I18N_API CollationSettings : public SharedObject { + /** + * Options bit 0: Perform the FCD check on the input text and deliver normalized text. + */ + static const int32_t CHECK_FCD = 1; + /** + * Options bit 1: Numeric collation. + * Also known as CODAN = COllate Digits As Numbers. + * + * Treat digit sequences as numbers with CE sequences in numeric order, + * rather than returning a normal CE for each digit. + */ + static const int32_t NUMERIC = 2; + /** + * "Shifted" alternate handling, see ALTERNATE_MASK. + */ + static const int32_t SHIFTED = 4; + /** + * Options bits 3..2: Alternate-handling mask. 0 for non-ignorable. + * Reserve values 8 and 0xc for shift-trimmed and blanked. + */ + static const int32_t ALTERNATE_MASK = 0xc; + /** + * Options bits 6..4: The 3-bit maxVariable value bit field is shifted by this value. + */ + static const int32_t MAX_VARIABLE_SHIFT = 4; + /** maxVariable options bit mask before shifting. */ + static const int32_t MAX_VARIABLE_MASK = 0x70; + /** Options bit 7: Reserved/unused/0. */ + /** + * Options bit 8: Sort uppercase first if caseLevel or caseFirst is on. + */ + static const int32_t UPPER_FIRST = 0x100; + /** + * Options bit 9: Keep the case bits in the tertiary weight (they trump other tertiary values) + * unless case level is on (when they are *moved* into the separate case level). + * By default, the case bits are removed from the tertiary weight (ignored). + * + * When CASE_FIRST is off, UPPER_FIRST must be off too, corresponding to + * the tri-value UCOL_CASE_FIRST attribute: UCOL_OFF vs. UCOL_LOWER_FIRST vs. UCOL_UPPER_FIRST. + */ + static const int32_t CASE_FIRST = 0x200; + /** + * Options bit mask for caseFirst and upperFirst, before shifting. + * Same value as caseFirst==upperFirst. + */ + static const int32_t CASE_FIRST_AND_UPPER_MASK = CASE_FIRST | UPPER_FIRST; + /** + * Options bit 10: Insert the case level between the secondary and tertiary levels. + */ + static const int32_t CASE_LEVEL = 0x400; + /** + * Options bit 11: Compare secondary weights backwards. ("French secondary") + */ + static const int32_t BACKWARD_SECONDARY = 0x800; + /** + * Options bits 15..12: The 4-bit strength value bit field is shifted by this value. + * It is the top used bit field in the options. (No need to mask after shifting.) + */ + static const int32_t STRENGTH_SHIFT = 12; + /** Strength options bit mask before shifting. */ + static const int32_t STRENGTH_MASK = 0xf000; + + /** maxVariable values */ + enum MaxVariable { + MAX_VAR_SPACE, + MAX_VAR_PUNCT, + MAX_VAR_SYMBOL, + MAX_VAR_CURRENCY + }; + + CollationSettings() + : options((UCOL_DEFAULT_STRENGTH << STRENGTH_SHIFT) | + (MAX_VAR_PUNCT << MAX_VARIABLE_SHIFT)), + variableTop(0), + reorderTable(nullptr), + minHighNoReorder(0), + reorderRanges(nullptr), reorderRangesLength(0), + reorderCodes(nullptr), reorderCodesLength(0), reorderCodesCapacity(0), + fastLatinOptions(-1) {} + + CollationSettings(const CollationSettings &other); + virtual ~CollationSettings(); + + bool operator==(const CollationSettings &other) const; + + inline bool operator!=(const CollationSettings &other) const { + return !operator==(other); + } + + int32_t hashCode() const; + + void resetReordering(); + void aliasReordering(const CollationData &data, const int32_t *codes, int32_t length, + const uint32_t *ranges, int32_t rangesLength, + const uint8_t *table, UErrorCode &errorCode); + void setReordering(const CollationData &data, const int32_t *codes, int32_t codesLength, + UErrorCode &errorCode); + void copyReorderingFrom(const CollationSettings &other, UErrorCode &errorCode); + + inline UBool hasReordering() const { return reorderTable != nullptr; } + static UBool reorderTableHasSplitBytes(const uint8_t table[256]); + inline uint32_t reorder(uint32_t p) const { + uint8_t b = reorderTable[p >> 24]; + if(b != 0 || p <= Collation::NO_CE_PRIMARY) { + return ((uint32_t)b << 24) | (p & 0xffffff); + } else { + return reorderEx(p); + } + } + + void setStrength(int32_t value, int32_t defaultOptions, UErrorCode &errorCode); + + static int32_t getStrength(int32_t options) { + return options >> STRENGTH_SHIFT; + } + + int32_t getStrength() const { + return getStrength(options); + } + + /** Sets the options bit for an on/off attribute. */ + void setFlag(int32_t bit, UColAttributeValue value, + int32_t defaultOptions, UErrorCode &errorCode); + + UColAttributeValue getFlag(int32_t bit) const { + return ((options & bit) != 0) ? UCOL_ON : UCOL_OFF; + } + + void setCaseFirst(UColAttributeValue value, int32_t defaultOptions, UErrorCode &errorCode); + + UColAttributeValue getCaseFirst() const { + int32_t option = options & CASE_FIRST_AND_UPPER_MASK; + return (option == 0) ? UCOL_OFF : + (option == CASE_FIRST) ? UCOL_LOWER_FIRST : UCOL_UPPER_FIRST; + } + + void setAlternateHandling(UColAttributeValue value, + int32_t defaultOptions, UErrorCode &errorCode); + + UColAttributeValue getAlternateHandling() const { + return ((options & ALTERNATE_MASK) == 0) ? UCOL_NON_IGNORABLE : UCOL_SHIFTED; + } + + void setMaxVariable(int32_t value, int32_t defaultOptions, UErrorCode &errorCode); + + MaxVariable getMaxVariable() const { + return (MaxVariable)((options & MAX_VARIABLE_MASK) >> MAX_VARIABLE_SHIFT); + } + + /** + * Include case bits in the tertiary level if caseLevel=off and caseFirst!=off. + */ + static inline UBool isTertiaryWithCaseBits(int32_t options) { + return (options & (CASE_LEVEL | CASE_FIRST)) == CASE_FIRST; + } + static uint32_t getTertiaryMask(int32_t options) { + // Remove the case bits from the tertiary weight when caseLevel is on or caseFirst is off. + return isTertiaryWithCaseBits(options) ? + Collation::CASE_AND_TERTIARY_MASK : Collation::ONLY_TERTIARY_MASK; + } + + static UBool sortsTertiaryUpperCaseFirst(int32_t options) { + // On tertiary level, consider case bits and sort uppercase first + // if caseLevel is off and caseFirst==upperFirst. + return (options & (CASE_LEVEL | CASE_FIRST_AND_UPPER_MASK)) == CASE_FIRST_AND_UPPER_MASK; + } + + inline UBool dontCheckFCD() const { + return (options & CHECK_FCD) == 0; + } + + inline UBool hasBackwardSecondary() const { + return (options & BACKWARD_SECONDARY) != 0; + } + + inline UBool isNumeric() const { + return (options & NUMERIC) != 0; + } + + /** CHECK_FCD etc. */ + int32_t options; + /** Variable-top primary weight. */ + uint32_t variableTop; + /** + * 256-byte table for reordering permutation of primary lead bytes; nullptr if no reordering. + * A 0 entry at a non-zero index means that the primary lead byte is "split" + * (there are different offsets for primaries that share that lead byte) + * and the reordering offset must be determined via the reorderRanges. + */ + const uint8_t *reorderTable; + /** Limit of last reordered range. 0 if no reordering or no split bytes. */ + uint32_t minHighNoReorder; + /** + * Primary-weight ranges for script reordering, + * to be used by reorder(p) for split-reordered primary lead bytes. + * + * Each entry is a (limit, offset) pair. + * The upper 16 bits of the entry are the upper 16 bits of the + * exclusive primary limit of a range. + * Primaries between the previous limit and this one have their lead bytes + * modified by the signed offset (-0xff..+0xff) stored in the lower 16 bits. + * + * CollationData::makeReorderRanges() writes a full list where the first range + * (at least for terminators and separators) has a 0 offset. + * The last range has a non-zero offset. + * minHighNoReorder is set to the limit of that last range. + * + * In the settings object, the initial ranges before the first split lead byte + * are omitted for efficiency; they are handled by reorder(p) via the reorderTable. + * If there are no split-reordered lead bytes, then no ranges are needed. + */ + const uint32_t *reorderRanges; + int32_t reorderRangesLength; + /** Array of reorder codes; ignored if reorderCodesLength == 0. */ + const int32_t *reorderCodes; + /** Number of reorder codes; 0 if no reordering. */ + int32_t reorderCodesLength; + /** + * Capacity of reorderCodes. + * If 0, then the codes, the ranges, and the table are aliases. + * Otherwise, this object owns the memory via the reorderCodes pointer; + * the codes, the ranges, and the table are in the same memory block, in that order. + */ + int32_t reorderCodesCapacity; + + /** Options for CollationFastLatin. Negative if disabled. */ + int32_t fastLatinOptions; + uint16_t fastLatinPrimaries[0x180]; + +private: + void setReorderArrays(const int32_t *codes, int32_t codesLength, + const uint32_t *ranges, int32_t rangesLength, + const uint8_t *table, UErrorCode &errorCode); + uint32_t reorderEx(uint32_t p) const; +}; + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONSETTINGS_H__ diff --git a/deps/icu-small/source/i18n/collationtailoring.cpp b/deps/icu-small/source/i18n/collationtailoring.cpp index 440414c43365b2..26bdac018f3f0e 100644 --- a/deps/icu-small/source/i18n/collationtailoring.cpp +++ b/deps/icu-small/source/i18n/collationtailoring.cpp @@ -1,113 +1,113 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationtailoring.cpp -* -* created on: 2013mar12 -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/udata.h" -#include "unicode/unistr.h" -#include "unicode/ures.h" -#include "unicode/uversion.h" -#include "unicode/uvernum.h" -#include "cmemory.h" -#include "collationdata.h" -#include "collationsettings.h" -#include "collationtailoring.h" -#include "normalizer2impl.h" -#include "uassert.h" -#include "uhash.h" -#include "umutex.h" -#include "utrie2.h" - -U_NAMESPACE_BEGIN - -CollationTailoring::CollationTailoring(const CollationSettings *baseSettings) - : data(NULL), settings(baseSettings), - actualLocale(""), - ownedData(NULL), - builder(NULL), memory(NULL), bundle(NULL), - trie(NULL), unsafeBackwardSet(NULL), - maxExpansions(NULL) { - if(baseSettings != NULL) { - U_ASSERT(baseSettings->reorderCodesLength == 0); - U_ASSERT(baseSettings->reorderTable == NULL); - U_ASSERT(baseSettings->minHighNoReorder == 0); - } else { - settings = new CollationSettings(); - } - if(settings != NULL) { - settings->addRef(); - } - rules.getTerminatedBuffer(); // ensure NUL-termination - version[0] = version[1] = version[2] = version[3] = 0; - maxExpansionsInitOnce.reset(); -} - -CollationTailoring::~CollationTailoring() { - SharedObject::clearPtr(settings); - delete ownedData; - delete builder; - udata_close(memory); - ures_close(bundle); - utrie2_close(trie); - delete unsafeBackwardSet; - uhash_close(maxExpansions); - maxExpansionsInitOnce.reset(); -} - -UBool -CollationTailoring::ensureOwnedData(UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return false; } - if(ownedData == NULL) { - const Normalizer2Impl *nfcImpl = Normalizer2Factory::getNFCImpl(errorCode); - if(U_FAILURE(errorCode)) { return false; } - ownedData = new CollationData(*nfcImpl); - if(ownedData == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return false; - } - } - data = ownedData; - return true; -} - -void -CollationTailoring::makeBaseVersion(const UVersionInfo ucaVersion, UVersionInfo version) { - version[0] = UCOL_BUILDER_VERSION; - version[1] = (ucaVersion[0] << 3) + ucaVersion[1]; - version[2] = ucaVersion[2] << 6; - version[3] = 0; -} - -void -CollationTailoring::setVersion(const UVersionInfo baseVersion, const UVersionInfo rulesVersion) { - version[0] = UCOL_BUILDER_VERSION; - version[1] = baseVersion[1]; - version[2] = (baseVersion[2] & 0xc0) + ((rulesVersion[0] + (rulesVersion[0] >> 6)) & 0x3f); - version[3] = (rulesVersion[1] << 3) + (rulesVersion[1] >> 5) + rulesVersion[2] + - (rulesVersion[3] << 4) + (rulesVersion[3] >> 4); -} - -int32_t -CollationTailoring::getUCAVersion() const { - return ((int32_t)version[1] << 4) | (version[2] >> 6); -} - -CollationCacheEntry::~CollationCacheEntry() { - SharedObject::clearPtr(tailoring); -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationtailoring.cpp +* +* created on: 2013mar12 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/udata.h" +#include "unicode/unistr.h" +#include "unicode/ures.h" +#include "unicode/uversion.h" +#include "unicode/uvernum.h" +#include "cmemory.h" +#include "collationdata.h" +#include "collationsettings.h" +#include "collationtailoring.h" +#include "normalizer2impl.h" +#include "uassert.h" +#include "uhash.h" +#include "umutex.h" +#include "utrie2.h" + +U_NAMESPACE_BEGIN + +CollationTailoring::CollationTailoring(const CollationSettings *baseSettings) + : data(nullptr), settings(baseSettings), + actualLocale(""), + ownedData(nullptr), + builder(nullptr), memory(nullptr), bundle(nullptr), + trie(nullptr), unsafeBackwardSet(nullptr), + maxExpansions(nullptr) { + if(baseSettings != nullptr) { + U_ASSERT(baseSettings->reorderCodesLength == 0); + U_ASSERT(baseSettings->reorderTable == nullptr); + U_ASSERT(baseSettings->minHighNoReorder == 0); + } else { + settings = new CollationSettings(); + } + if(settings != nullptr) { + settings->addRef(); + } + rules.getTerminatedBuffer(); // ensure NUL-termination + version[0] = version[1] = version[2] = version[3] = 0; + maxExpansionsInitOnce.reset(); +} + +CollationTailoring::~CollationTailoring() { + SharedObject::clearPtr(settings); + delete ownedData; + delete builder; + udata_close(memory); + ures_close(bundle); + utrie2_close(trie); + delete unsafeBackwardSet; + uhash_close(maxExpansions); + maxExpansionsInitOnce.reset(); +} + +UBool +CollationTailoring::ensureOwnedData(UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return false; } + if(ownedData == nullptr) { + const Normalizer2Impl *nfcImpl = Normalizer2Factory::getNFCImpl(errorCode); + if(U_FAILURE(errorCode)) { return false; } + ownedData = new CollationData(*nfcImpl); + if(ownedData == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return false; + } + } + data = ownedData; + return true; +} + +void +CollationTailoring::makeBaseVersion(const UVersionInfo ucaVersion, UVersionInfo version) { + version[0] = UCOL_BUILDER_VERSION; + version[1] = (ucaVersion[0] << 3) + ucaVersion[1]; + version[2] = ucaVersion[2] << 6; + version[3] = 0; +} + +void +CollationTailoring::setVersion(const UVersionInfo baseVersion, const UVersionInfo rulesVersion) { + version[0] = UCOL_BUILDER_VERSION; + version[1] = baseVersion[1]; + version[2] = (baseVersion[2] & 0xc0) + ((rulesVersion[0] + (rulesVersion[0] >> 6)) & 0x3f); + version[3] = (rulesVersion[1] << 3) + (rulesVersion[1] >> 5) + rulesVersion[2] + + (rulesVersion[3] << 4) + (rulesVersion[3] >> 4); +} + +int32_t +CollationTailoring::getUCAVersion() const { + return ((int32_t)version[1] << 4) | (version[2] >> 6); +} + +CollationCacheEntry::~CollationCacheEntry() { + SharedObject::clearPtr(tailoring); +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/collationtailoring.h b/deps/icu-small/source/i18n/collationtailoring.h index a6143c1c269dfb..c6d69ee997ec40 100644 --- a/deps/icu-small/source/i18n/collationtailoring.h +++ b/deps/icu-small/source/i18n/collationtailoring.h @@ -1,117 +1,117 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013-2014, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* collationtailoring.h -* -* created on: 2013mar12 -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONTAILORING_H__ -#define __COLLATIONTAILORING_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/locid.h" -#include "unicode/unistr.h" -#include "unicode/uversion.h" -#include "collationsettings.h" -#include "uhash.h" -#include "umutex.h" -#include "unifiedcache.h" - - -struct UDataMemory; -struct UResourceBundle; -struct UTrie2; - -U_NAMESPACE_BEGIN - -struct CollationData; - -class UnicodeSet; - -/** - * Collation tailoring data & settings. - * This is a container of values for a collation tailoring - * built from rules or deserialized from binary data. - * - * It is logically immutable: Do not modify its values. - * The fields are public for convenience. - * - * It is shared, reference-counted, and auto-deleted; see SharedObject. - */ -struct U_I18N_API CollationTailoring : public SharedObject { - CollationTailoring(const CollationSettings *baseSettings); - virtual ~CollationTailoring(); - - /** - * Returns true if the constructor could not initialize properly. - */ - UBool isBogus() { return settings == NULL; } - - UBool ensureOwnedData(UErrorCode &errorCode); - - static void makeBaseVersion(const UVersionInfo ucaVersion, UVersionInfo version); - void setVersion(const UVersionInfo baseVersion, const UVersionInfo rulesVersion); - int32_t getUCAVersion() const; - - // data for sorting etc. - const CollationData *data; // == base data or ownedData - const CollationSettings *settings; // reference-counted - UnicodeString rules; - // The locale is bogus when built from rules or constructed from a binary blob. - // It can then be set by the service registration code which is thread-safe. - mutable Locale actualLocale; - // UCA version u.v.w & rules version r.s.t.q: - // version[0]: builder version (runtime version is mixed in at runtime) - // version[1]: bits 7..3=u, bits 2..0=v - // version[2]: bits 7..6=w, bits 5..0=r - // version[3]= (s<<5)+(s>>3)+t+(q<<4)+(q>>4) - UVersionInfo version; - - // owned objects - CollationData *ownedData; - UObject *builder; - UDataMemory *memory; - UResourceBundle *bundle; - UTrie2 *trie; - UnicodeSet *unsafeBackwardSet; - mutable UHashtable *maxExpansions; - mutable UInitOnce maxExpansionsInitOnce; - -private: - /** - * No copy constructor: A CollationTailoring cannot be copied. - * It is immutable, and the data trie cannot be copied either. - */ - CollationTailoring(const CollationTailoring &other) = delete; -}; - -struct U_I18N_API CollationCacheEntry : public SharedObject { - CollationCacheEntry(const Locale &loc, const CollationTailoring *t) - : validLocale(loc), tailoring(t) { - if(t != NULL) { - t->addRef(); - } - } - ~CollationCacheEntry(); - - Locale validLocale; - const CollationTailoring *tailoring; -}; - -template<> U_I18N_API -const CollationCacheEntry * -LocaleCacheKey::createObject(const void *creationContext, - UErrorCode &errorCode) const; -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION -#endif // __COLLATIONTAILORING_H__ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013-2014, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* collationtailoring.h +* +* created on: 2013mar12 +* created by: Markus W. Scherer +*/ + +#ifndef __COLLATIONTAILORING_H__ +#define __COLLATIONTAILORING_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/locid.h" +#include "unicode/unistr.h" +#include "unicode/uversion.h" +#include "collationsettings.h" +#include "uhash.h" +#include "umutex.h" +#include "unifiedcache.h" + + +struct UDataMemory; +struct UResourceBundle; +struct UTrie2; + +U_NAMESPACE_BEGIN + +struct CollationData; + +class UnicodeSet; + +/** + * Collation tailoring data & settings. + * This is a container of values for a collation tailoring + * built from rules or deserialized from binary data. + * + * It is logically immutable: Do not modify its values. + * The fields are public for convenience. + * + * It is shared, reference-counted, and auto-deleted; see SharedObject. + */ +struct U_I18N_API CollationTailoring : public SharedObject { + CollationTailoring(const CollationSettings *baseSettings); + virtual ~CollationTailoring(); + + /** + * Returns true if the constructor could not initialize properly. + */ + UBool isBogus() { return settings == nullptr; } + + UBool ensureOwnedData(UErrorCode &errorCode); + + static void makeBaseVersion(const UVersionInfo ucaVersion, UVersionInfo version); + void setVersion(const UVersionInfo baseVersion, const UVersionInfo rulesVersion); + int32_t getUCAVersion() const; + + // data for sorting etc. + const CollationData *data; // == base data or ownedData + const CollationSettings *settings; // reference-counted + UnicodeString rules; + // The locale is bogus when built from rules or constructed from a binary blob. + // It can then be set by the service registration code which is thread-safe. + mutable Locale actualLocale; + // UCA version u.v.w & rules version r.s.t.q: + // version[0]: builder version (runtime version is mixed in at runtime) + // version[1]: bits 7..3=u, bits 2..0=v + // version[2]: bits 7..6=w, bits 5..0=r + // version[3]= (s<<5)+(s>>3)+t+(q<<4)+(q>>4) + UVersionInfo version; + + // owned objects + CollationData *ownedData; + UObject *builder; + UDataMemory *memory; + UResourceBundle *bundle; + UTrie2 *trie; + UnicodeSet *unsafeBackwardSet; + mutable UHashtable *maxExpansions; + mutable UInitOnce maxExpansionsInitOnce; + +private: + /** + * No copy constructor: A CollationTailoring cannot be copied. + * It is immutable, and the data trie cannot be copied either. + */ + CollationTailoring(const CollationTailoring &other) = delete; +}; + +struct U_I18N_API CollationCacheEntry : public SharedObject { + CollationCacheEntry(const Locale &loc, const CollationTailoring *t) + : validLocale(loc), tailoring(t) { + if(t != nullptr) { + t->addRef(); + } + } + ~CollationCacheEntry(); + + Locale validLocale; + const CollationTailoring *tailoring; +}; + +template<> U_I18N_API +const CollationCacheEntry * +LocaleCacheKey::createObject(const void *creationContext, + UErrorCode &errorCode) const; +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION +#endif // __COLLATIONTAILORING_H__ diff --git a/deps/icu-small/source/i18n/collationweights.cpp b/deps/icu-small/source/i18n/collationweights.cpp index 02d0268f53b090..d05e495488c0d4 100644 --- a/deps/icu-small/source/i18n/collationweights.cpp +++ b/deps/icu-small/source/i18n/collationweights.cpp @@ -1,570 +1,570 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1999-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: collationweights.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2001mar08 as ucol_wgt.cpp -* created by: Markus W. Scherer -* -* This file contains code for allocating n collation element weights -* between two exclusive limits. -* It is used only internally by the collation tailoring builder. -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "cmemory.h" -#include "collation.h" -#include "collationweights.h" -#include "uarrsort.h" -#include "uassert.h" - -#ifdef UCOL_DEBUG -# include -#endif - -U_NAMESPACE_BEGIN - -/* collation element weight allocation -------------------------------------- */ - -/* helper functions for CE weights */ - -static inline uint32_t -getWeightTrail(uint32_t weight, int32_t length) { - return (uint32_t)(weight>>(8*(4-length)))&0xff; -} - -static inline uint32_t -setWeightTrail(uint32_t weight, int32_t length, uint32_t trail) { - length=8*(4-length); - return (uint32_t)((weight&(0xffffff00<>idx; - } else { - // Do not use uint32_t>>32 because on some platforms that does not shift at all - // while we need it to become 0. - // PowerPC: 0xffffffff>>32 = 0 (wanted) - // x86: 0xffffffff>>32 = 0xffffffff (not wanted) - // - // ANSI C99 6.5.7 Bitwise shift operators: - // "If the value of the right operand is negative - // or is greater than or equal to the width of the promoted left operand, - // the behavior is undefined." - mask=0; - } - idx=32-idx; - mask|=0xffffff00< 0); - } - } -} - -uint32_t -CollationWeights::incWeightByOffset(uint32_t weight, int32_t length, int32_t offset) const { - for(;;) { - offset += getWeightByte(weight, length); - if((uint32_t)offset <= maxBytes[length]) { - return setWeightByte(weight, length, offset); - } else { - // Split the offset between this byte and the previous one. - offset -= minBytes[length]; - weight = setWeightByte(weight, length, minBytes[length] + offset % countBytes(length)); - offset /= countBytes(length); - --length; - U_ASSERT(length > 0); - } - } -} - -void -CollationWeights::lengthenRange(WeightRange &range) const { - int32_t length=range.length+1; - range.start=setWeightTrail(range.start, length, minBytes[length]); - range.end=setWeightTrail(range.end, length, maxBytes[length]); - range.count*=countBytes(length); - range.length=length; -} - -/* for uprv_sortArray: sort ranges in weight order */ -static int32_t U_CALLCONV -compareRanges(const void * /*context*/, const void *left, const void *right) { - uint32_t l, r; - - l=((const CollationWeights::WeightRange *)left)->start; - r=((const CollationWeights::WeightRange *)right)->start; - if(lr) { - return 1; - } else { - return 0; - } -} - -UBool -CollationWeights::getWeightRanges(uint32_t lowerLimit, uint32_t upperLimit) { - U_ASSERT(lowerLimit != 0); - U_ASSERT(upperLimit != 0); - - /* get the lengths of the limits */ - int32_t lowerLength=lengthOfWeight(lowerLimit); - int32_t upperLength=lengthOfWeight(upperLimit); - -#ifdef UCOL_DEBUG - printf("length of lower limit 0x%08lx is %ld\n", lowerLimit, lowerLength); - printf("length of upper limit 0x%08lx is %ld\n", upperLimit, upperLength); -#endif - U_ASSERT(lowerLength>=middleLength); - // Permit upperLength=upperLimit) { -#ifdef UCOL_DEBUG - printf("error: no space between lower & upper limits\n"); -#endif - return false; - } - - /* check that neither is a prefix of the other */ - if(lowerLength=upperLimit has caught it */ - - WeightRange lower[5], middle, upper[5]; /* [0] and [1] are not used - this simplifies indexing */ - uprv_memset(lower, 0, sizeof(lower)); - uprv_memset(&middle, 0, sizeof(middle)); - uprv_memset(upper, 0, sizeof(upper)); - - /* - * With the limit lengths of 1..4, there are up to 7 ranges for allocation: - * range minimum length - * lower[4] 4 - * lower[3] 3 - * lower[2] 2 - * middle 1 - * upper[2] 2 - * upper[3] 3 - * upper[4] 4 - * - * We are now going to calculate up to 7 ranges. - * Some of them will typically overlap, so we will then have to merge and eliminate ranges. - */ - uint32_t weight=lowerLimit; - for(int32_t length=lowerLength; length>middleLength; --length) { - uint32_t trail=getWeightTrail(weight, length); - if(trailmiddleLength; --length) { - uint32_t trail=getWeightTrail(weight, length); - if(trail>minBytes[length]) { - upper[length].start=setWeightTrail(weight, length, minBytes[length]); - upper[length].end=decWeightTrail(weight, length); - upper[length].length=length; - upper[length].count=trail-minBytes[length]; - } - weight=truncateWeight(weight, length-1); - } - middle.end=decWeightTrail(weight, middleLength); - - /* set the middle range */ - middle.length=middleLength; - if(middle.end>=middle.start) { - middle.count=(int32_t)((middle.end-middle.start)>>(8*(4-middleLength)))+1; - } else { - /* no middle range, eliminate overlaps */ - for(int32_t length=4; length>middleLength; --length) { - if(lower[length].count>0 && upper[length].count>0) { - // Note: The lowerEnd and upperStart weights are versions of - // lowerLimit and upperLimit (which are lowerLimitupperStart) { - // These two lower and upper ranges collide. - // Since lowerLimitupperStart is only possible - // if the leading bytes are equal - // and lastByte(lowerEnd)>lastByte(upperStart). - U_ASSERT(truncateWeight(lowerEnd, length-1)== - truncateWeight(upperStart, length-1)); - // Intersect these two ranges. - lower[length].end=upper[length].end; - lower[length].count= - (int32_t)getWeightTrail(lower[length].end, length)- - (int32_t)getWeightTrail(lower[length].start, length)+1; - // count might be <=0 in which case there is no room, - // and the range-collecting code below will ignore this range. - merged=true; - } else if(lowerEnd==upperStart) { - // Not possible, unless minByte==maxByte which is not allowed. - U_ASSERT(minBytes[length]countBytes - merged=true; - } - } - if(merged) { - // Remove all shorter ranges. - // There was no room available for them between the ranges we just merged. - upper[length].count=0; - while(--length>middleLength) { - lower[length].count=upper[length].count=0; - } - break; - } - } - } - } - -#ifdef UCOL_DEBUG - /* print ranges */ - for(int32_t length=4; length>=2; --length) { - if(lower[length].count>0) { - printf("lower[%ld] .start=0x%08lx .end=0x%08lx .count=%ld\n", length, lower[length].start, lower[length].end, lower[length].count); - } - } - if(middle.count>0) { - printf("middle .start=0x%08lx .end=0x%08lx .count=%ld\n", middle.start, middle.end, middle.count); - } - for(int32_t length=2; length<=4; ++length) { - if(upper[length].count>0) { - printf("upper[%ld] .start=0x%08lx .end=0x%08lx .count=%ld\n", length, upper[length].start, upper[length].end, upper[length].count); - } - } -#endif - - /* copy the ranges, shortest first, into the result array */ - rangeCount=0; - if(middle.count>0) { - uprv_memcpy(ranges, &middle, sizeof(WeightRange)); - rangeCount=1; - } - for(int32_t length=middleLength+1; length<=4; ++length) { - /* copy upper first so that later the middle range is more likely the first one to use */ - if(upper[length].count>0) { - uprv_memcpy(ranges+rangeCount, upper+length, sizeof(WeightRange)); - ++rangeCount; - } - if(lower[length].count>0) { - uprv_memcpy(ranges+rangeCount, lower+length, sizeof(WeightRange)); - ++rangeCount; - } - } - return rangeCount>0; -} - -UBool -CollationWeights::allocWeightsInShortRanges(int32_t n, int32_t minLength) { - // See if the first few minLength and minLength+1 ranges have enough weights. - for(int32_t i = 0; i < rangeCount && ranges[i].length <= (minLength + 1); ++i) { - if(n <= ranges[i].count) { - // Use the first few minLength and minLength+1 ranges. - if(ranges[i].length > minLength) { - // Reduce the number of weights from the last minLength+1 range - // which might sort before some minLength ranges, - // so that we use all weights in the minLength ranges. - ranges[i].count = n; - } - rangeCount = i + 1; -#ifdef UCOL_DEBUG - printf("take first %ld ranges\n", rangeCount); -#endif - - if(rangeCount>1) { - /* sort the ranges by weight values */ - UErrorCode errorCode=U_ZERO_ERROR; - uprv_sortArray(ranges, rangeCount, sizeof(WeightRange), - compareRanges, NULL, false, &errorCode); - /* ignore error code: we know that the internal sort function will not fail here */ - } - return true; - } - n -= ranges[i].count; // still >0 - } - return false; -} - -UBool -CollationWeights::allocWeightsInMinLengthRanges(int32_t n, int32_t minLength) { - // See if the minLength ranges have enough weights - // when we split one and lengthen the following ones. - int32_t count = 0; - int32_t minLengthRangeCount; - for(minLengthRangeCount = 0; - minLengthRangeCount < rangeCount && - ranges[minLengthRangeCount].length == minLength; - ++minLengthRangeCount) { - count += ranges[minLengthRangeCount].count; - } - - int32_t nextCountBytes = countBytes(minLength + 1); - if(n > count * nextCountBytes) { return false; } - - // Use the minLength ranges. Merge them, and then split again as necessary. - uint32_t start = ranges[0].start; - uint32_t end = ranges[0].end; - for(int32_t i = 1; i < minLengthRangeCount; ++i) { - if(ranges[i].start < start) { start = ranges[i].start; } - if(ranges[i].end > end) { end = ranges[i].end; } - } - - // Calculate how to split the range between minLength (count1) and minLength+1 (count2). - // Goal: - // count1 + count2 * nextCountBytes = n - // count1 + count2 = count - // These turn into - // (count - count2) + count2 * nextCountBytes = n - // and then into the following count1 & count2 computations. - int32_t count2 = (n - count) / (nextCountBytes - 1); // number of weights to be lengthened - int32_t count1 = count - count2; // number of minLength weights - if(count2 == 0 || (count1 + count2 * nextCountBytes) < n) { - // round up - ++count2; - --count1; - U_ASSERT((count1 + count2 * nextCountBytes) >= n); - } - - ranges[0].start = start; - - if(count1 == 0) { - // Make one long range. - ranges[0].end = end; - ranges[0].count = count; - lengthenRange(ranges[0]); - rangeCount = 1; - } else { - // Split the range, lengthen the second part. -#ifdef UCOL_DEBUG - printf("split the range number %ld (out of %ld minLength ranges) by %ld:%ld\n", - splitRange, rangeCount, count1, count2); -#endif - - // Next start = start + count1. First end = 1 before that. - ranges[0].end = incWeightByOffset(start, minLength, count1 - 1); - ranges[0].count = count1; - - ranges[1].start = incWeight(ranges[0].end, minLength); - ranges[1].end = end; - ranges[1].length = minLength; // +1 when lengthened - ranges[1].count = count2; // *countBytes when lengthened - lengthenRange(ranges[1]); - rangeCount = 2; - } - return true; -} - -/* - * call getWeightRanges and then determine heuristically - * which ranges to use for a given number of weights between (excluding) - * two limits - */ -UBool -CollationWeights::allocWeights(uint32_t lowerLimit, uint32_t upperLimit, int32_t n) { -#ifdef UCOL_DEBUG - puts(""); -#endif - - if(!getWeightRanges(lowerLimit, upperLimit)) { -#ifdef UCOL_DEBUG - printf("error: unable to get Weight ranges\n"); -#endif - return false; - } - - /* try until we find suitably large ranges */ - for(;;) { - /* get the smallest number of bytes in a range */ - int32_t minLength=ranges[0].length; - - if(allocWeightsInShortRanges(n, minLength)) { break; } - - if(minLength == 4) { -#ifdef UCOL_DEBUG - printf("error: the maximum number of %ld weights is insufficient for n=%ld\n", - minLengthCount, n); -#endif - return false; - } - - if(allocWeightsInMinLengthRanges(n, minLength)) { break; } - - /* no good match, lengthen all minLength ranges and iterate */ -#ifdef UCOL_DEBUG - printf("lengthen the short ranges from %ld bytes to %ld and iterate\n", minLength, minLength+1); -#endif - for(int32_t i=0; i= rangeCount) { - return 0xffffffff; - } else { - /* get the next weight */ - WeightRange &range = ranges[rangeIndex]; - uint32_t weight = range.start; - if(--range.count == 0) { - /* this range is finished */ - ++rangeIndex; - } else { - /* increment the weight for the next value */ - range.start = incWeight(weight, range.length); - U_ASSERT(range.start <= range.end); - } - - return weight; - } -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_COLLATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 1999-2015, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: collationweights.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2001mar08 as ucol_wgt.cpp +* created by: Markus W. Scherer +* +* This file contains code for allocating n collation element weights +* between two exclusive limits. +* It is used only internally by the collation tailoring builder. +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "cmemory.h" +#include "collation.h" +#include "collationweights.h" +#include "uarrsort.h" +#include "uassert.h" + +#ifdef UCOL_DEBUG +# include +#endif + +U_NAMESPACE_BEGIN + +/* collation element weight allocation -------------------------------------- */ + +/* helper functions for CE weights */ + +static inline uint32_t +getWeightTrail(uint32_t weight, int32_t length) { + return (uint32_t)(weight>>(8*(4-length)))&0xff; +} + +static inline uint32_t +setWeightTrail(uint32_t weight, int32_t length, uint32_t trail) { + length=8*(4-length); + return (uint32_t)((weight&(0xffffff00<>idx; + } else { + // Do not use uint32_t>>32 because on some platforms that does not shift at all + // while we need it to become 0. + // PowerPC: 0xffffffff>>32 = 0 (wanted) + // x86: 0xffffffff>>32 = 0xffffffff (not wanted) + // + // ANSI C99 6.5.7 Bitwise shift operators: + // "If the value of the right operand is negative + // or is greater than or equal to the width of the promoted left operand, + // the behavior is undefined." + mask=0; + } + idx=32-idx; + mask|=0xffffff00< 0); + } + } +} + +uint32_t +CollationWeights::incWeightByOffset(uint32_t weight, int32_t length, int32_t offset) const { + for(;;) { + offset += getWeightByte(weight, length); + if((uint32_t)offset <= maxBytes[length]) { + return setWeightByte(weight, length, offset); + } else { + // Split the offset between this byte and the previous one. + offset -= minBytes[length]; + weight = setWeightByte(weight, length, minBytes[length] + offset % countBytes(length)); + offset /= countBytes(length); + --length; + U_ASSERT(length > 0); + } + } +} + +void +CollationWeights::lengthenRange(WeightRange &range) const { + int32_t length=range.length+1; + range.start=setWeightTrail(range.start, length, minBytes[length]); + range.end=setWeightTrail(range.end, length, maxBytes[length]); + range.count*=countBytes(length); + range.length=length; +} + +/* for uprv_sortArray: sort ranges in weight order */ +static int32_t U_CALLCONV +compareRanges(const void * /*context*/, const void *left, const void *right) { + uint32_t l, r; + + l=((const CollationWeights::WeightRange *)left)->start; + r=((const CollationWeights::WeightRange *)right)->start; + if(lr) { + return 1; + } else { + return 0; + } +} + +UBool +CollationWeights::getWeightRanges(uint32_t lowerLimit, uint32_t upperLimit) { + U_ASSERT(lowerLimit != 0); + U_ASSERT(upperLimit != 0); + + /* get the lengths of the limits */ + int32_t lowerLength=lengthOfWeight(lowerLimit); + int32_t upperLength=lengthOfWeight(upperLimit); + +#ifdef UCOL_DEBUG + printf("length of lower limit 0x%08lx is %ld\n", lowerLimit, lowerLength); + printf("length of upper limit 0x%08lx is %ld\n", upperLimit, upperLength); +#endif + U_ASSERT(lowerLength>=middleLength); + // Permit upperLength=upperLimit) { +#ifdef UCOL_DEBUG + printf("error: no space between lower & upper limits\n"); +#endif + return false; + } + + /* check that neither is a prefix of the other */ + if(lowerLength=upperLimit has caught it */ + + WeightRange lower[5], middle, upper[5]; /* [0] and [1] are not used - this simplifies indexing */ + uprv_memset(lower, 0, sizeof(lower)); + uprv_memset(&middle, 0, sizeof(middle)); + uprv_memset(upper, 0, sizeof(upper)); + + /* + * With the limit lengths of 1..4, there are up to 7 ranges for allocation: + * range minimum length + * lower[4] 4 + * lower[3] 3 + * lower[2] 2 + * middle 1 + * upper[2] 2 + * upper[3] 3 + * upper[4] 4 + * + * We are now going to calculate up to 7 ranges. + * Some of them will typically overlap, so we will then have to merge and eliminate ranges. + */ + uint32_t weight=lowerLimit; + for(int32_t length=lowerLength; length>middleLength; --length) { + uint32_t trail=getWeightTrail(weight, length); + if(trailmiddleLength; --length) { + uint32_t trail=getWeightTrail(weight, length); + if(trail>minBytes[length]) { + upper[length].start=setWeightTrail(weight, length, minBytes[length]); + upper[length].end=decWeightTrail(weight, length); + upper[length].length=length; + upper[length].count=trail-minBytes[length]; + } + weight=truncateWeight(weight, length-1); + } + middle.end=decWeightTrail(weight, middleLength); + + /* set the middle range */ + middle.length=middleLength; + if(middle.end>=middle.start) { + middle.count=(int32_t)((middle.end-middle.start)>>(8*(4-middleLength)))+1; + } else { + /* no middle range, eliminate overlaps */ + for(int32_t length=4; length>middleLength; --length) { + if(lower[length].count>0 && upper[length].count>0) { + // Note: The lowerEnd and upperStart weights are versions of + // lowerLimit and upperLimit (which are lowerLimitupperStart) { + // These two lower and upper ranges collide. + // Since lowerLimitupperStart is only possible + // if the leading bytes are equal + // and lastByte(lowerEnd)>lastByte(upperStart). + U_ASSERT(truncateWeight(lowerEnd, length-1)== + truncateWeight(upperStart, length-1)); + // Intersect these two ranges. + lower[length].end=upper[length].end; + lower[length].count= + (int32_t)getWeightTrail(lower[length].end, length)- + (int32_t)getWeightTrail(lower[length].start, length)+1; + // count might be <=0 in which case there is no room, + // and the range-collecting code below will ignore this range. + merged=true; + } else if(lowerEnd==upperStart) { + // Not possible, unless minByte==maxByte which is not allowed. + U_ASSERT(minBytes[length]countBytes + merged=true; + } + } + if(merged) { + // Remove all shorter ranges. + // There was no room available for them between the ranges we just merged. + upper[length].count=0; + while(--length>middleLength) { + lower[length].count=upper[length].count=0; + } + break; + } + } + } + } + +#ifdef UCOL_DEBUG + /* print ranges */ + for(int32_t length=4; length>=2; --length) { + if(lower[length].count>0) { + printf("lower[%ld] .start=0x%08lx .end=0x%08lx .count=%ld\n", length, lower[length].start, lower[length].end, lower[length].count); + } + } + if(middle.count>0) { + printf("middle .start=0x%08lx .end=0x%08lx .count=%ld\n", middle.start, middle.end, middle.count); + } + for(int32_t length=2; length<=4; ++length) { + if(upper[length].count>0) { + printf("upper[%ld] .start=0x%08lx .end=0x%08lx .count=%ld\n", length, upper[length].start, upper[length].end, upper[length].count); + } + } +#endif + + /* copy the ranges, shortest first, into the result array */ + rangeCount=0; + if(middle.count>0) { + uprv_memcpy(ranges, &middle, sizeof(WeightRange)); + rangeCount=1; + } + for(int32_t length=middleLength+1; length<=4; ++length) { + /* copy upper first so that later the middle range is more likely the first one to use */ + if(upper[length].count>0) { + uprv_memcpy(ranges+rangeCount, upper+length, sizeof(WeightRange)); + ++rangeCount; + } + if(lower[length].count>0) { + uprv_memcpy(ranges+rangeCount, lower+length, sizeof(WeightRange)); + ++rangeCount; + } + } + return rangeCount>0; +} + +UBool +CollationWeights::allocWeightsInShortRanges(int32_t n, int32_t minLength) { + // See if the first few minLength and minLength+1 ranges have enough weights. + for(int32_t i = 0; i < rangeCount && ranges[i].length <= (minLength + 1); ++i) { + if(n <= ranges[i].count) { + // Use the first few minLength and minLength+1 ranges. + if(ranges[i].length > minLength) { + // Reduce the number of weights from the last minLength+1 range + // which might sort before some minLength ranges, + // so that we use all weights in the minLength ranges. + ranges[i].count = n; + } + rangeCount = i + 1; +#ifdef UCOL_DEBUG + printf("take first %ld ranges\n", rangeCount); +#endif + + if(rangeCount>1) { + /* sort the ranges by weight values */ + UErrorCode errorCode=U_ZERO_ERROR; + uprv_sortArray(ranges, rangeCount, sizeof(WeightRange), + compareRanges, nullptr, false, &errorCode); + /* ignore error code: we know that the internal sort function will not fail here */ + } + return true; + } + n -= ranges[i].count; // still >0 + } + return false; +} + +UBool +CollationWeights::allocWeightsInMinLengthRanges(int32_t n, int32_t minLength) { + // See if the minLength ranges have enough weights + // when we split one and lengthen the following ones. + int32_t count = 0; + int32_t minLengthRangeCount; + for(minLengthRangeCount = 0; + minLengthRangeCount < rangeCount && + ranges[minLengthRangeCount].length == minLength; + ++minLengthRangeCount) { + count += ranges[minLengthRangeCount].count; + } + + int32_t nextCountBytes = countBytes(minLength + 1); + if(n > count * nextCountBytes) { return false; } + + // Use the minLength ranges. Merge them, and then split again as necessary. + uint32_t start = ranges[0].start; + uint32_t end = ranges[0].end; + for(int32_t i = 1; i < minLengthRangeCount; ++i) { + if(ranges[i].start < start) { start = ranges[i].start; } + if(ranges[i].end > end) { end = ranges[i].end; } + } + + // Calculate how to split the range between minLength (count1) and minLength+1 (count2). + // Goal: + // count1 + count2 * nextCountBytes = n + // count1 + count2 = count + // These turn into + // (count - count2) + count2 * nextCountBytes = n + // and then into the following count1 & count2 computations. + int32_t count2 = (n - count) / (nextCountBytes - 1); // number of weights to be lengthened + int32_t count1 = count - count2; // number of minLength weights + if(count2 == 0 || (count1 + count2 * nextCountBytes) < n) { + // round up + ++count2; + --count1; + U_ASSERT((count1 + count2 * nextCountBytes) >= n); + } + + ranges[0].start = start; + + if(count1 == 0) { + // Make one long range. + ranges[0].end = end; + ranges[0].count = count; + lengthenRange(ranges[0]); + rangeCount = 1; + } else { + // Split the range, lengthen the second part. +#ifdef UCOL_DEBUG + printf("split the range number %ld (out of %ld minLength ranges) by %ld:%ld\n", + splitRange, rangeCount, count1, count2); +#endif + + // Next start = start + count1. First end = 1 before that. + ranges[0].end = incWeightByOffset(start, minLength, count1 - 1); + ranges[0].count = count1; + + ranges[1].start = incWeight(ranges[0].end, minLength); + ranges[1].end = end; + ranges[1].length = minLength; // +1 when lengthened + ranges[1].count = count2; // *countBytes when lengthened + lengthenRange(ranges[1]); + rangeCount = 2; + } + return true; +} + +/* + * call getWeightRanges and then determine heuristically + * which ranges to use for a given number of weights between (excluding) + * two limits + */ +UBool +CollationWeights::allocWeights(uint32_t lowerLimit, uint32_t upperLimit, int32_t n) { +#ifdef UCOL_DEBUG + puts(""); +#endif + + if(!getWeightRanges(lowerLimit, upperLimit)) { +#ifdef UCOL_DEBUG + printf("error: unable to get Weight ranges\n"); +#endif + return false; + } + + /* try until we find suitably large ranges */ + for(;;) { + /* get the smallest number of bytes in a range */ + int32_t minLength=ranges[0].length; + + if(allocWeightsInShortRanges(n, minLength)) { break; } + + if(minLength == 4) { +#ifdef UCOL_DEBUG + printf("error: the maximum number of %ld weights is insufficient for n=%ld\n", + minLengthCount, n); +#endif + return false; + } + + if(allocWeightsInMinLengthRanges(n, minLength)) { break; } + + /* no good match, lengthen all minLength ranges and iterate */ +#ifdef UCOL_DEBUG + printf("lengthen the short ranges from %ld bytes to %ld and iterate\n", minLength, minLength+1); +#endif + for(int32_t i=0; i= rangeCount) { + return 0xffffffff; + } else { + /* get the next weight */ + WeightRange &range = ranges[rangeIndex]; + uint32_t weight = range.start; + if(--range.count == 0) { + /* this range is finished */ + ++rangeIndex; + } else { + /* increment the weight for the next value */ + range.start = incWeight(weight, range.length); + U_ASSERT(range.start <= range.end); + } + + return weight; + } +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_COLLATION */ diff --git a/deps/icu-small/source/i18n/collationweights.h b/deps/icu-small/source/i18n/collationweights.h index 0d20b927b2698c..306852079b94b6 100644 --- a/deps/icu-small/source/i18n/collationweights.h +++ b/deps/icu-small/source/i18n/collationweights.h @@ -1,113 +1,113 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 1999-2014, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: collationweights.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2001mar08 as ucol_wgt.h -* created by: Markus W. Scherer -*/ - -#ifndef __COLLATIONWEIGHTS_H__ -#define __COLLATIONWEIGHTS_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/uobject.h" - -U_NAMESPACE_BEGIN - -/** - * Allocates n collation element weights between two exclusive limits. - * Used only internally by the collation tailoring builder. - */ -class U_I18N_API CollationWeights : public UMemory { -public: - CollationWeights(); - - static inline int32_t lengthOfWeight(uint32_t weight) { - if((weight&0xffffff)==0) { - return 1; - } else if((weight&0xffff)==0) { - return 2; - } else if((weight&0xff)==0) { - return 3; - } else { - return 4; - } - } - - void initForPrimary(UBool compressible); - void initForSecondary(); - void initForTertiary(); - - /** - * Determine heuristically - * what ranges to use for a given number of weights between (excluding) - * two limits. - * - * @param lowerLimit A collation element weight; the ranges will be filled to cover - * weights greater than this one. - * @param upperLimit A collation element weight; the ranges will be filled to cover - * weights less than this one. - * @param n The number of collation element weights w necessary such that - * lowerLimitproperties.compactStyle = style; - fields->properties.groupingSize = -2; // do not forward grouping information - fields->properties.minimumGroupingDigits = 2; - touch(status); -} - -CompactDecimalFormat::CompactDecimalFormat(const CompactDecimalFormat& source) = default; - -CompactDecimalFormat::~CompactDecimalFormat() = default; - -CompactDecimalFormat& CompactDecimalFormat::operator=(const CompactDecimalFormat& rhs) { - DecimalFormat::operator=(rhs); - return *this; -} - -CompactDecimalFormat* CompactDecimalFormat::clone() const { - return new CompactDecimalFormat(*this); -} - -void -CompactDecimalFormat::parse( - const UnicodeString& /* text */, - Formattable& /* result */, - ParsePosition& /* parsePosition */) const { -} - -void -CompactDecimalFormat::parse( - const UnicodeString& /* text */, - Formattable& /* result */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; -} - -CurrencyAmount* -CompactDecimalFormat::parseCurrency( - const UnicodeString& /* text */, - ParsePosition& /* pos */) const { - return nullptr; -} - - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 "unicode/compactdecimalformat.h" +#include "number_mapper.h" +#include "number_decimfmtprops.h" + +using namespace icu; + + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CompactDecimalFormat) + + +CompactDecimalFormat* +CompactDecimalFormat::createInstance(const Locale& inLocale, UNumberCompactStyle style, + UErrorCode& status) { + return new CompactDecimalFormat(inLocale, style, 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); +} + +CompactDecimalFormat::CompactDecimalFormat(const CompactDecimalFormat& source) = default; + +CompactDecimalFormat::~CompactDecimalFormat() = default; + +CompactDecimalFormat& CompactDecimalFormat::operator=(const CompactDecimalFormat& rhs) { + DecimalFormat::operator=(rhs); + return *this; +} + +CompactDecimalFormat* CompactDecimalFormat::clone() const { + return new CompactDecimalFormat(*this); +} + +void +CompactDecimalFormat::parse( + const UnicodeString& /* text */, + Formattable& /* result */, + ParsePosition& /* parsePosition */) const { +} + +void +CompactDecimalFormat::parse( + const UnicodeString& /* text */, + Formattable& /* result */, + UErrorCode& status) const { + status = U_UNSUPPORTED_ERROR; +} + +CurrencyAmount* +CompactDecimalFormat::parseCurrency( + const UnicodeString& /* text */, + ParsePosition& /* pos */) const { + return nullptr; +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/coptccal.cpp b/deps/icu-small/source/i18n/coptccal.cpp index 0be700ca0138d7..f67fc4083e6a21 100644 --- a/deps/icu-small/source/i18n/coptccal.cpp +++ b/deps/icu-small/source/i18n/coptccal.cpp @@ -1,164 +1,182 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2003 - 2013, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "umutex.h" -#include "coptccal.h" -#include "cecal.h" -#include - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CopticCalendar) - -static const int32_t COPTIC_JD_EPOCH_OFFSET = 1824665; - -//------------------------------------------------------------------------- -// Constructors... -//------------------------------------------------------------------------- - -CopticCalendar::CopticCalendar(const Locale& aLocale, UErrorCode& success) -: CECalendar(aLocale, success) -{ -} - -CopticCalendar::CopticCalendar (const CopticCalendar& other) -: CECalendar(other) -{ -} - -CopticCalendar::~CopticCalendar() -{ -} - -CopticCalendar* -CopticCalendar::clone() const -{ - return new CopticCalendar(*this); -} - -const char* -CopticCalendar::getType() const -{ - return "coptic"; -} - -//------------------------------------------------------------------------- -// Calendar framework -//------------------------------------------------------------------------- - -int32_t -CopticCalendar::handleGetExtendedYear() -{ - int32_t eyear; - if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { - eyear = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 - } else { - // The year defaults to the epoch start, the era to CE - int32_t era = internalGet(UCAL_ERA, CE); - if (era == BCE) { - eyear = 1 - internalGet(UCAL_YEAR, 1); // Convert to extended year - } else { - eyear = internalGet(UCAL_YEAR, 1); // Default to year 1 - } - } - return eyear; -} - -void -CopticCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) -{ - int32_t eyear, month, day, era, year; - jdToCE(julianDay, getJDEpochOffset(), eyear, month, day); - - if (eyear <= 0) { - era = BCE; - year = 1 - eyear; - } else { - era = CE; - year = eyear; - } - - internalSet(UCAL_EXTENDED_YEAR, eyear); - internalSet(UCAL_ERA, era); - internalSet(UCAL_YEAR, year); - internalSet(UCAL_MONTH, month); - internalSet(UCAL_DATE, day); - internalSet(UCAL_DAY_OF_YEAR, (30 * month) + day); -} - -/** - * The system maintains a static default century start date and Year. They are - * initialized the first time they are used. Once the system default century date - * and year are set, they do not change. - */ -static UDate gSystemDefaultCenturyStart = DBL_MIN; -static int32_t gSystemDefaultCenturyStartYear = -1; -static icu::UInitOnce gSystemDefaultCenturyInit {}; - - -static void U_CALLCONV initializeSystemDefaultCentury() { - UErrorCode status = U_ZERO_ERROR; - CopticCalendar calendar(Locale("@calendar=coptic"), status); - if (U_SUCCESS(status)) { - calendar.setTime(Calendar::getNow(), status); - calendar.add(UCAL_YEAR, -80, status); - gSystemDefaultCenturyStart = calendar.getTime(status); - gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); - } - // We have no recourse upon failure unless we want to propagate the failure - // out. -} - -UDate -CopticCalendar::defaultCenturyStart() const -{ - // lazy-evaluate systemDefaultCenturyStart - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStart; -} - -int32_t -CopticCalendar::defaultCenturyStartYear() const -{ - // lazy-evaluate systemDefaultCenturyStart - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStartYear; -} - - -int32_t -CopticCalendar::getJDEpochOffset() const -{ - return COPTIC_JD_EPOCH_OFFSET; -} - - -#if 0 -// We do not want to introduce this API in ICU4C. -// It was accidentally introduced in ICU4J as a public API. - -//------------------------------------------------------------------------- -// Calendar system Conversion methods... -//------------------------------------------------------------------------- - -int32_t -CopticCalendar::copticToJD(int32_t year, int32_t month, int32_t day) -{ - return CECalendar::ceToJD(year, month, day, COPTIC_JD_EPOCH_OFFSET); -} -#endif - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2003 - 2013, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "umutex.h" +#include "coptccal.h" +#include "cecal.h" +#include + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CopticCalendar) + +static const int32_t COPTIC_JD_EPOCH_OFFSET = 1824665; + +//------------------------------------------------------------------------- +// Constructors... +//------------------------------------------------------------------------- + +CopticCalendar::CopticCalendar(const Locale& aLocale, UErrorCode& success) +: CECalendar(aLocale, success) +{ +} + +CopticCalendar::CopticCalendar (const CopticCalendar& other) +: CECalendar(other) +{ +} + +CopticCalendar::~CopticCalendar() +{ +} + +CopticCalendar* +CopticCalendar::clone() const +{ + return new CopticCalendar(*this); +} + +const char* +CopticCalendar::getType() const +{ + return "coptic"; +} + +//------------------------------------------------------------------------- +// Calendar framework +//------------------------------------------------------------------------- + +int32_t +CopticCalendar::handleGetExtendedYear() +{ + int32_t eyear; + if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { + eyear = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 + } else { + // The year defaults to the epoch start, the era to CE + int32_t era = internalGet(UCAL_ERA, CE); + if (era == BCE) { + eyear = 1 - internalGet(UCAL_YEAR, 1); // Convert to extended year + } else { + eyear = internalGet(UCAL_YEAR, 1); // Default to year 1 + } + } + return eyear; +} + +void +CopticCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) +{ + int32_t eyear, month, day, era, year; + jdToCE(julianDay, getJDEpochOffset(), eyear, month, day); + + if (eyear <= 0) { + era = BCE; + year = 1 - eyear; + } else { + era = CE; + year = eyear; + } + + internalSet(UCAL_EXTENDED_YEAR, eyear); + internalSet(UCAL_ERA, era); + internalSet(UCAL_YEAR, year); + internalSet(UCAL_MONTH, month); + internalSet(UCAL_ORDINAL_MONTH, month); + internalSet(UCAL_DATE, day); + internalSet(UCAL_DAY_OF_YEAR, (30 * month) + day); +} + +constexpr uint32_t kCopticRelatedYearDiff = 284; + +int32_t CopticCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kCopticRelatedYearDiff; +} + +void CopticCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kCopticRelatedYearDiff); +} + +/** + * The system maintains a static default century start date and Year. They are + * initialized the first time they are used. Once the system default century date + * and year are set, they do not change. + */ +static UDate gSystemDefaultCenturyStart = DBL_MIN; +static int32_t gSystemDefaultCenturyStartYear = -1; +static icu::UInitOnce gSystemDefaultCenturyInit {}; + + +static void U_CALLCONV initializeSystemDefaultCentury() { + UErrorCode status = U_ZERO_ERROR; + CopticCalendar calendar(Locale("@calendar=coptic"), status); + if (U_SUCCESS(status)) { + calendar.setTime(Calendar::getNow(), status); + calendar.add(UCAL_YEAR, -80, status); + gSystemDefaultCenturyStart = calendar.getTime(status); + gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); + } + // We have no recourse upon failure unless we want to propagate the failure + // out. +} + +UDate +CopticCalendar::defaultCenturyStart() const +{ + // lazy-evaluate systemDefaultCenturyStart + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStart; +} + +int32_t +CopticCalendar::defaultCenturyStartYear() const +{ + // lazy-evaluate systemDefaultCenturyStart + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStartYear; +} + + +int32_t +CopticCalendar::getJDEpochOffset() const +{ + return COPTIC_JD_EPOCH_OFFSET; +} + + +#if 0 +// We do not want to introduce this API in ICU4C. +// It was accidentally introduced in ICU4J as a public API. + +//------------------------------------------------------------------------- +// Calendar system Conversion methods... +//------------------------------------------------------------------------- + +int32_t +CopticCalendar::copticToJD(int32_t year, int32_t month, int32_t day) +{ + return CECalendar::ceToJD(year, month, day, COPTIC_JD_EPOCH_OFFSET); +} +#endif + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ +//eof diff --git a/deps/icu-small/source/i18n/coptccal.h b/deps/icu-small/source/i18n/coptccal.h index 5c51af04ca0527..e668b347a259f9 100644 --- a/deps/icu-small/source/i18n/coptccal.h +++ b/deps/icu-small/source/i18n/coptccal.h @@ -1,244 +1,258 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2003 - 2013, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - -#ifndef COPTCCAL_H -#define COPTCCAL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/calendar.h" -#include "cecal.h" - -U_NAMESPACE_BEGIN - -/** - * Implement the Coptic calendar system. - * @internal - */ -class CopticCalendar : public CECalendar { - -public: - /** - * Useful constants for CopticCalendar. - * @internal - */ - enum EMonths { - /** - * Constant for ωογτ/تﻮﺗ, - * the 1st month of the Coptic year. - */ - TOUT, - - /** - * Constant for Παοπι/ﻪﺑﺎﺑ, - * the 2nd month of the Coptic year. - */ - BABA, - - /** - * Constant for Αθορ/رﻮﺗﺎﻫ, - * the 3rd month of the Coptic year. - */ - HATOR, - - /** - * Constant for Χοιακ/ﻚﻬﻴﻛ, - * the 4th month of the Coptic year. - */ - KIAHK, - - /** - * Constant for Τωβι/طﻮﺒﻫ, - * the 5th month of the Coptic year. - */ - TOBA, - - /** - * Constant for Μεϣιρ/ﺮﻴﺸﻣأ, - * the 6th month of the Coptic year. - */ - AMSHIR, - - /** - * Constant for Παρεμϩατ/تﺎﻬﻣﺮﺑ, - * the 7th month of the Coptic year. - */ - BARAMHAT, - - /** - * Constant for Φαρμοθι/هدﻮﻣﺮﺑ, - * the 8th month of the Coptic year. - */ - BARAMOUDA, - - /** - * Constant for Παϣαν/ﺲﻨﺸﺑ, - * the 9th month of the Coptic year. - */ - BASHANS, - - /** - * Constant for Παωνι/ﻪﻧؤﻮﺑ, - * the 10th month of the Coptic year. - */ - PAONA, - - /** - * Constant for Επηπ/ﺐﻴﺑأ, - * the 11th month of the Coptic year. - */ - EPEP, - - /** - * Constant for Μεϲωρη/ىﺮﺴﻣ, - * the 12th month of the Coptic year. - */ - MESRA, - - /** - * Constant for Πικογϫι - * μαβοτ/ﺮﻴﻐﺼﻟا - * ﺮﻬﺸﻟا, - * the 13th month of the Coptic year. - */ - NASIE - }; - - enum EEras { - BCE, // Before the epoch - CE // After the epoch - }; - - /** - * Constructs a CopticCalendar based on the current time in the default time zone - * with the given locale. - * - * @param aLocale The given locale. - * @param success Indicates the status of CopticCalendar object construction. - * Returns U_ZERO_ERROR if constructed successfully. - * @internal - */ - CopticCalendar(const Locale& aLocale, UErrorCode& success); - - /** - * Copy Constructor - * @internal - */ - CopticCalendar (const CopticCalendar& other); - - /** - * Destructor. - * @internal - */ - virtual ~CopticCalendar(); - - /** - * Create and return a polymorphic copy of this calendar. - * @return return a polymorphic copy of this calendar. - * @internal - */ - virtual CopticCalendar* clone() const override; - - /** - * return the calendar type, "coptic" - * @return calendar type - * @internal - */ - const char * getType() const override; - -protected: - //------------------------------------------------------------------------- - // Calendar framework - //------------------------------------------------------------------------- - - /** - * Return the extended year defined by the current fields. - * @internal - */ - virtual int32_t handleGetExtendedYear() override; - - /** - * Compute fields from the JD - * @internal - */ - virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; - - /** - * Returns the date of the start of the default century - * @return start of century - in milliseconds since epoch, 1970 - * @internal - */ - virtual UDate defaultCenturyStart() const override; - - /** - * Returns the year in which the default century begins - * @internal - */ - virtual int32_t defaultCenturyStartYear() const override; - - /** - * Return the date offset from Julian - * @internal - */ - virtual int32_t getJDEpochOffset() const override; - - -public: - /** - * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual - * override. This method is to implement a simple version of RTTI, since not all C++ - * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call - * this method. - * - * @return The class ID for this object. All objects of a given class have the - * same class ID. Objects of other classes have different class IDs. - * @internal - */ - virtual UClassID getDynamicClassID(void) const override; - - /** - * Return the class ID for this class. This is useful only for comparing to a return - * value from getDynamicClassID(). For example: - * - * Base* polymorphic_pointer = createPolymorphicObject(); - * if (polymorphic_pointer->getDynamicClassID() == - * Derived::getStaticClassID()) ... - * - * @return The class ID for all objects of this class. - * @internal - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(void); - -#if 0 - // We do not want to introduce this API in ICU4C. - // It was accidentally introduced in ICU4J as a public API. -public: - //------------------------------------------------------------------------- - // Calendar system Conversion methods... - //------------------------------------------------------------------------- - /** - * Convert an Coptic year, month, and day to a Julian day. - * - * @param year the extended year - * @param month the month - * @param day the day - * @return Julian day - * @internal - */ - static int32_t copticToJD(int32_t year, int32_t month, int32_t day); -#endif -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ -#endif /* COPTCCAL_H */ -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2003 - 2013, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +*/ + +#ifndef COPTCCAL_H +#define COPTCCAL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" +#include "cecal.h" + +U_NAMESPACE_BEGIN + +/** + * Implement the Coptic calendar system. + * @internal + */ +class CopticCalendar : public CECalendar { + +public: + /** + * Useful constants for CopticCalendar. + * @internal + */ + enum EMonths { + /** + * Constant for ωογτ/تﻮﺗ, + * the 1st month of the Coptic year. + */ + TOUT, + + /** + * Constant for Παοπι/ﻪﺑﺎﺑ, + * the 2nd month of the Coptic year. + */ + BABA, + + /** + * Constant for Αθορ/رﻮﺗﺎﻫ, + * the 3rd month of the Coptic year. + */ + HATOR, + + /** + * Constant for Χοιακ/ﻚﻬﻴﻛ, + * the 4th month of the Coptic year. + */ + KIAHK, + + /** + * Constant for Τωβι/طﻮﺒﻫ, + * the 5th month of the Coptic year. + */ + TOBA, + + /** + * Constant for Μεϣιρ/ﺮﻴﺸﻣأ, + * the 6th month of the Coptic year. + */ + AMSHIR, + + /** + * Constant for Παρεμϩατ/تﺎﻬﻣﺮﺑ, + * the 7th month of the Coptic year. + */ + BARAMHAT, + + /** + * Constant for Φαρμοθι/هدﻮﻣﺮﺑ, + * the 8th month of the Coptic year. + */ + BARAMOUDA, + + /** + * Constant for Παϣαν/ﺲﻨﺸﺑ, + * the 9th month of the Coptic year. + */ + BASHANS, + + /** + * Constant for Παωνι/ﻪﻧؤﻮﺑ, + * the 10th month of the Coptic year. + */ + PAONA, + + /** + * Constant for Επηπ/ﺐﻴﺑأ, + * the 11th month of the Coptic year. + */ + EPEP, + + /** + * Constant for Μεϲωρη/ىﺮﺴﻣ, + * the 12th month of the Coptic year. + */ + MESRA, + + /** + * Constant for Πικογϫι + * μαβοτ/ﺮﻴﻐﺼﻟا + * ﺮﻬﺸﻟا, + * the 13th month of the Coptic year. + */ + NASIE + }; + + enum EEras { + BCE, // Before the epoch + CE // After the epoch + }; + + /** + * Constructs a CopticCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of CopticCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + CopticCalendar(const Locale& aLocale, UErrorCode& success); + + /** + * Copy Constructor + * @internal + */ + CopticCalendar (const CopticCalendar& other); + + /** + * Destructor. + * @internal + */ + virtual ~CopticCalendar(); + + /** + * Create and return a polymorphic copy of this calendar. + * @return return a polymorphic copy of this calendar. + * @internal + */ + virtual CopticCalendar* clone() const override; + + /** + * return the calendar type, "coptic" + * @return calendar type + * @internal + */ + const char * getType() const override; + + /** + * @return The related Gregorian year; will be obtained by modifying the value + * obtained by get from UCAL_EXTENDED_YEAR field + * @internal + */ + virtual int32_t getRelatedYear(UErrorCode &status) const override; + + /** + * @param year The related Gregorian year to set; will be modified as necessary then + * set in UCAL_EXTENDED_YEAR field + * @internal + */ + virtual void setRelatedYear(int32_t year) override; + +protected: + //------------------------------------------------------------------------- + // Calendar framework + //------------------------------------------------------------------------- + + /** + * Return the extended year defined by the current fields. + * @internal + */ + virtual int32_t handleGetExtendedYear() override; + + /** + * Compute fields from the JD + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; + + /** + * Returns the date of the start of the default century + * @return start of century - in milliseconds since epoch, 1970 + * @internal + */ + virtual UDate defaultCenturyStart() const override; + + /** + * Returns the year in which the default century begins + * @internal + */ + virtual int32_t defaultCenturyStartYear() const override; + + /** + * Return the date offset from Julian + * @internal + */ + virtual int32_t getJDEpochOffset() const override; + + +public: + /** + * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual + * override. This method is to implement a simple version of RTTI, since not all C++ + * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call + * this method. + * + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + +#if 0 + // We do not want to introduce this API in ICU4C. + // It was accidentally introduced in ICU4J as a public API. +public: + //------------------------------------------------------------------------- + // Calendar system Conversion methods... + //------------------------------------------------------------------------- + /** + * Convert an Coptic year, month, and day to a Julian day. + * + * @param year the extended year + * @param month the month + * @param day the day + * @return Julian day + * @internal + */ + static int32_t copticToJD(int32_t year, int32_t month, int32_t day); +#endif +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ +#endif /* COPTCCAL_H */ +//eof diff --git a/deps/icu-small/source/i18n/cpdtrans.cpp b/deps/icu-small/source/i18n/cpdtrans.cpp index f2b1c36a338083..2bc36e12746d30 100644 --- a/deps/icu-small/source/i18n/cpdtrans.cpp +++ b/deps/icu-small/source/i18n/cpdtrans.cpp @@ -1,617 +1,617 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/17/99 aliu Creation. -********************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/unifilt.h" -#include "unicode/uniset.h" -#include "cpdtrans.h" -#include "uvector.h" -#include "tridpars.h" -#include "cmemory.h" - -// keep in sync with Transliterator -//static const UChar ID_SEP = 0x002D; /*-*/ -static const UChar ID_DELIM = 0x003B; /*;*/ -static const UChar NEWLINE = 10; - -static const UChar COLON_COLON[] = {0x3A, 0x3A, 0}; //"::" - -U_NAMESPACE_BEGIN - -const UChar CompoundTransliterator::PASS_STRING[] = { 0x0025, 0x0050, 0x0061, 0x0073, 0x0073, 0 }; // "%Pass" - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CompoundTransliterator) - -/** - * Constructs a new compound transliterator given an array of - * transliterators. The array of transliterators may be of any - * length, including zero or one, however, useful compound - * transliterators have at least two components. - * @param transliterators array of Transliterator - * objects - * @param transliteratorCount The number of - * Transliterator objects in transliterators. - * @param filter the filter. Any character for which - * filter.contains() returns false will not be - * altered by this transliterator. If filter is - * null then no filtering is applied. - */ -CompoundTransliterator::CompoundTransliterator( - Transliterator* const transliterators[], - int32_t transliteratorCount, - UnicodeFilter* adoptedFilter) : - Transliterator(joinIDs(transliterators, transliteratorCount), adoptedFilter), - trans(0), count(0), numAnonymousRBTs(0) { - setTransliterators(transliterators, transliteratorCount); -} - -/** - * Splits an ID of the form "ID;ID;..." into a compound using each - * of the IDs. - * @param id of above form - * @param forward if false, does the list in reverse order, and - * takes the inverse of each ID. - */ -CompoundTransliterator::CompoundTransliterator(const UnicodeString& id, - UTransDirection direction, - UnicodeFilter* adoptedFilter, - UParseError& /*parseError*/, - UErrorCode& status) : - Transliterator(id, adoptedFilter), - trans(0), numAnonymousRBTs(0) { - // TODO add code for parseError...currently unused, but - // later may be used by parsing code... - init(id, direction, true, status); -} - -CompoundTransliterator::CompoundTransliterator(const UnicodeString& id, - UParseError& /*parseError*/, - UErrorCode& status) : - Transliterator(id, 0), // set filter to 0 here! - trans(0), numAnonymousRBTs(0) { - // TODO add code for parseError...currently unused, but - // later may be used by parsing code... - init(id, UTRANS_FORWARD, true, status); -} - - -/** - * Private constructor for use of TransliteratorAlias - */ -CompoundTransliterator::CompoundTransliterator(const UnicodeString& newID, - UVector& list, - UnicodeFilter* adoptedFilter, - int32_t anonymousRBTs, - UParseError& /*parseError*/, - UErrorCode& status) : - Transliterator(newID, adoptedFilter), - trans(0), numAnonymousRBTs(anonymousRBTs) -{ - init(list, UTRANS_FORWARD, false, status); -} - -/** - * Private constructor for Transliterator from a vector of - * transliterators. The caller is responsible for fixing up the - * ID. - */ -CompoundTransliterator::CompoundTransliterator(UVector& list, - UParseError& /*parseError*/, - UErrorCode& status) : - Transliterator(UnicodeString(), NULL), - trans(0), numAnonymousRBTs(0) -{ - // TODO add code for parseError...currently unused, but - // later may be used by parsing code... - init(list, UTRANS_FORWARD, false, status); - // assume caller will fixup ID -} - -CompoundTransliterator::CompoundTransliterator(UVector& list, - int32_t anonymousRBTs, - UParseError& /*parseError*/, - UErrorCode& status) : - Transliterator(UnicodeString(), NULL), - trans(0), numAnonymousRBTs(anonymousRBTs) -{ - init(list, UTRANS_FORWARD, false, status); -} - -/** - * Finish constructing a transliterator: only to be called by - * constructors. Before calling init(), set trans and filter to NULL. - * @param id the id containing ';'-separated entries - * @param direction either FORWARD or REVERSE - * @param idSplitPoint the index into id at which the - * adoptedSplitTransliterator should be inserted, if there is one, or - * -1 if there is none. - * @param adoptedSplitTransliterator a transliterator to be inserted - * before the entry at offset idSplitPoint in the id string. May be - * NULL to insert no entry. - * @param fixReverseID if true, then reconstruct the ID of reverse - * entries by calling getID() of component entries. Some constructors - * do not require this because they apply a facade ID anyway. - * @param status the error code indicating success or failure - */ -void CompoundTransliterator::init(const UnicodeString& id, - UTransDirection direction, - UBool fixReverseID, - UErrorCode& status) { - // assert(trans == 0); - - if (U_FAILURE(status)) { - return; - } - - UVector list(status); - UnicodeSet* compoundFilter = NULL; - UnicodeString regenID; - if (!TransliteratorIDParser::parseCompoundID(id, direction, - regenID, list, compoundFilter)) { - status = U_INVALID_ID; - delete compoundFilter; - return; - } - - TransliteratorIDParser::instantiateList(list, status); - - init(list, direction, fixReverseID, status); - - if (compoundFilter != NULL) { - adoptFilter(compoundFilter); - } -} - -/** - * Finish constructing a transliterator: only to be called by - * constructors. Before calling init(), set trans and filter to NULL. - * @param list a vector of transliterator objects to be adopted. It - * should NOT be empty. The list should be in declared order. That - * is, it should be in the FORWARD order; if direction is REVERSE then - * the list order will be reversed. - * @param direction either FORWARD or REVERSE - * @param fixReverseID if true, then reconstruct the ID of reverse - * entries by calling getID() of component entries. Some constructors - * do not require this because they apply a facade ID anyway. - * @param status the error code indicating success or failure - */ -void CompoundTransliterator::init(UVector& list, - UTransDirection direction, - UBool fixReverseID, - UErrorCode& status) { - // assert(trans == 0); - - // Allocate array - if (U_SUCCESS(status)) { - count = list.size(); - trans = (Transliterator **)uprv_malloc(count * sizeof(Transliterator *)); - /* test for NULL */ - if (trans == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - - if (U_FAILURE(status) || trans == 0) { - // assert(trans == 0); - return; - } - - // Move the transliterators from the vector into an array. - // Reverse the order if necessary. - int32_t i; - for (i=0; i 0) { - newID.append(ID_DELIM); - } - newID.append(trans[i]->getID()); - } - setID(newID); - } - - computeMaximumContextLength(); -} - -/** - * Return the IDs of the given list of transliterators, concatenated - * with ID_DELIM delimiting them. Equivalent to the perlish expression - * join(ID_DELIM, map($_.getID(), transliterators). - */ -UnicodeString CompoundTransliterator::joinIDs(Transliterator* const transliterators[], - int32_t transCount) { - UnicodeString id; - for (int32_t i=0; i 0) { - id.append(ID_DELIM); - } - id.append(transliterators[i]->getID()); - } - return id; // Return temporary -} - -/** - * Copy constructor. - */ -CompoundTransliterator::CompoundTransliterator(const CompoundTransliterator& t) : - Transliterator(t), trans(0), count(0), numAnonymousRBTs(-1) { - *this = t; -} - -/** - * Destructor - */ -CompoundTransliterator::~CompoundTransliterator() { - freeTransliterators(); -} - -void CompoundTransliterator::freeTransliterators(void) { - if (trans != 0) { - for (int32_t i=0; i count) { - if (trans != NULL) { - uprv_free(trans); - } - trans = (Transliterator **)uprv_malloc(t.count * sizeof(Transliterator *)); - } - count = t.count; - if (trans != NULL) { - for (i=0; iclone(); - if (trans[i] == NULL) { - failed = true; - break; - } - } - } - - // if memory allocation failed delete backwards trans array - if (failed && i > 0) { - int32_t n; - for (n = i-1; n >= 0; n--) { - uprv_free(trans[n]); - trans[n] = NULL; - } - } - numAnonymousRBTs = t.numAnonymousRBTs; - return *this; -} - -/** - * Transliterator API. - */ -CompoundTransliterator* CompoundTransliterator::clone() const { - return new CompoundTransliterator(*this); -} - -/** - * Returns the number of transliterators in this chain. - * @return number of transliterators in this chain. - */ -int32_t CompoundTransliterator::getCount(void) const { - return count; -} - -/** - * Returns the transliterator at the given index in this chain. - * @param index index into chain, from 0 to getCount() - 1 - * @return transliterator at the given index - */ -const Transliterator& CompoundTransliterator::getTransliterator(int32_t index) const { - return *trans[index]; -} - -void CompoundTransliterator::setTransliterators(Transliterator* const transliterators[], - int32_t transCount) { - Transliterator** a = (Transliterator **)uprv_malloc(transCount * sizeof(Transliterator *)); - if (a == NULL) { - return; - } - int32_t i = 0; - UBool failed = false; - for (i=0; iclone(); - if (a[i] == NULL) { - failed = true; - break; - } - } - if (failed && i > 0) { - int32_t n; - for (n = i-1; n >= 0; n--) { - uprv_free(a[n]); - a[n] = NULL; - } - return; - } - adoptTransliterators(a, transCount); -} - -void CompoundTransliterator::adoptTransliterators(Transliterator* adoptedTransliterators[], - int32_t transCount) { - // First free trans[] and set count to zero. Once this is done, - // orphan the filter. Set up the new trans[]. - freeTransliterators(); - trans = adoptedTransliterators; - count = transCount; - computeMaximumContextLength(); - setID(joinIDs(trans, count)); -} - -/** - * Append c to buf, unless buf is empty or buf already ends in c. - */ -static void _smartAppend(UnicodeString& buf, UChar c) { - if (buf.length() != 0 && - buf.charAt(buf.length() - 1) != c) { - buf.append(c); - } -} - -UnicodeString& CompoundTransliterator::toRules(UnicodeString& rulesSource, - UBool escapeUnprintable) const { - // We do NOT call toRules() on our component transliterators, in - // general. If we have several rule-based transliterators, this - // yields a concatenation of the rules -- not what we want. We do - // handle compound RBT transliterators specially -- those for which - // compoundRBTIndex >= 0. For the transliterator at compoundRBTIndex, - // we do call toRules() recursively. - rulesSource.truncate(0); - if (numAnonymousRBTs >= 1 && getFilter() != NULL) { - // If we are a compound RBT and if we have a global - // filter, then emit it at the top. - UnicodeString pat; - rulesSource.append(COLON_COLON, 2).append(getFilter()->toPattern(pat, escapeUnprintable)).append(ID_DELIM); - } - for (int32_t i=0; igetID().startsWith(PASS_STRING, 5)) { - trans[i]->toRules(rule, escapeUnprintable); - if (numAnonymousRBTs > 1 && i > 0 && trans[i - 1]->getID().startsWith(PASS_STRING, 5)) - rule = UNICODE_STRING_SIMPLE("::Null;") + rule; - - // we also use toRules() on CompoundTransliterators (which we - // check for by looking for a semicolon in the ID)-- this gets - // the list of their child transliterators output in the right - // format - } else if (trans[i]->getID().indexOf(ID_DELIM) >= 0) { - trans[i]->toRules(rule, escapeUnprintable); - - // for everything else, use Transliterator::toRules() - } else { - trans[i]->Transliterator::toRules(rule, escapeUnprintable); - } - _smartAppend(rulesSource, NEWLINE); - rulesSource.append(rule); - _smartAppend(rulesSource, ID_DELIM); - } - return rulesSource; -} - -/** - * Implement Transliterator framework - */ -void CompoundTransliterator::handleGetSourceSet(UnicodeSet& result) const { - UnicodeSet set; - result.clear(); - for (int32_t i=0; igetSourceSet(set)); - // Take the example of Hiragana-Latin. This is really - // Hiragana-Katakana; Katakana-Latin. The source set of - // these two is roughly [:Hiragana:] and [:Katakana:]. - // But the source set for the entire transliterator is - // actually [:Hiragana:] ONLY -- that is, the first - // non-empty source set. - - // This is a heuristic, and not 100% reliable. - if (!result.isEmpty()) { - break; - } - } -} - -/** - * Override Transliterator framework - */ -UnicodeSet& CompoundTransliterator::getTargetSet(UnicodeSet& result) const { - UnicodeSet set; - result.clear(); - for (int32_t i=0; igetTargetSet(set)); - } - return result; -} - -/** - * Implements {@link Transliterator#handleTransliterate}. - */ -void CompoundTransliterator::handleTransliterate(Replaceable& text, UTransPosition& index, - UBool incremental) const { - /* Call each transliterator with the same contextStart and - * start, but with the limit as modified - * by preceding transliterators. The start index must be - * reset for each transliterator to give each a chance to - * transliterate the text. The initial contextStart index is known - * to still point to the same place after each transliterator - * is called because each transliterator will not change the - * text between contextStart and the initial start index. - * - * IMPORTANT: After the first transliterator, each subsequent - * transliterator only gets to transliterate text committed by - * preceding transliterators; that is, the start (output - * value) of transliterator i becomes the limit (input value) - * of transliterator i+1. Finally, the overall limit is fixed - * up before we return. - * - * Assumptions we make here: - * (1) contextStart <= start <= limit <= contextLimit <= text.length() - * (2) start <= start' <= limit' ;cursor doesn't move back - * (3) start <= limit' ;text before cursor unchanged - * - start' is the value of start after calling handleKT - * - limit' is the value of limit after calling handleKT - */ - - /** - * Example: 3 transliterators. This example illustrates the - * mechanics we need to implement. C, S, and L are the contextStart, - * start, and limit. gl is the globalLimit. contextLimit is - * equal to limit throughout. - * - * 1. h-u, changes hex to Unicode - * - * 4 7 a d 0 4 7 a - * abc/u0061/u => abca/u - * C S L C S L gl=f->a - * - * 2. upup, changes "x" to "XX" - * - * 4 7 a 4 7 a - * abca/u => abcAA/u - * C SL C S - * L gl=a->b - * 3. u-h, changes Unicode to hex - * - * 4 7 a 4 7 a d 0 3 - * abcAA/u => abc/u0041/u0041/u - * C S L C S - * L gl=b->15 - * 4. return - * - * 4 7 a d 0 3 - * abc/u0041/u0041/u - * C S L - */ - - if (count < 1) { - index.start = index.limit; - return; // Short circuit for empty compound transliterators - } - - // compoundLimit is the limit value for the entire compound - // operation. We overwrite index.limit with the previous - // index.start. After each transliteration, we update - // compoundLimit for insertions or deletions that have happened. - int32_t compoundLimit = index.limit; - - // compoundStart is the start for the entire compound - // operation. - int32_t compoundStart = index.start; - - int32_t delta = 0; // delta in length - - // Give each transliterator a crack at the run of characters. - // See comments at the top of the method for more detail. - for (int32_t i=0; ifilteredTransliterate(text, index, incremental); - - // In a properly written transliterator, start == limit after - // handleTransliterate() returns when incremental is false. - // Catch cases where the subclass doesn't do this, and throw - // an exception. (Just pinning start to limit is a bad idea, - // because what's probably happening is that the subclass - // isn't transliterating all the way to the end, and it should - // in non-incremental mode.) - if (!incremental && index.start != index.limit) { - // We can't throw an exception, so just fudge things - index.start = index.limit; - } - - // Cumulative delta for insertions/deletions - delta += index.limit - limit; - - if (incremental) { - // In the incremental case, only allow subsequent - // transliterators to modify what has already been - // completely processed by prior transliterators. In the - // non-incrmental case, allow each transliterator to - // process the entire text. - index.limit = index.start; - } - } - - compoundLimit += delta; - - // Start is good where it is -- where the last transliterator left - // it. Limit needs to be put back where it was, modulo - // adjustments for deletions/insertions. - index.limit = compoundLimit; -} - -/** - * Sets the length of the longest context required by this transliterator. - * This is preceding context. - */ -void CompoundTransliterator::computeMaximumContextLength(void) { - int32_t max = 0; - for (int32_t i=0; igetMaximumContextLength(); - if (len > max) { - max = len; - } - } - setMaximumContextLength(max); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -/* eof */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/17/99 aliu Creation. +********************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/unifilt.h" +#include "unicode/uniset.h" +#include "cpdtrans.h" +#include "uvector.h" +#include "tridpars.h" +#include "cmemory.h" + +// keep in sync with Transliterator +//static const char16_t ID_SEP = 0x002D; /*-*/ +static const char16_t ID_DELIM = 0x003B; /*;*/ +static const char16_t NEWLINE = 10; + +static const char16_t COLON_COLON[] = {0x3A, 0x3A, 0}; //"::" + +U_NAMESPACE_BEGIN + +const char16_t CompoundTransliterator::PASS_STRING[] = { 0x0025, 0x0050, 0x0061, 0x0073, 0x0073, 0 }; // "%Pass" + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CompoundTransliterator) + +/** + * Constructs a new compound transliterator given an array of + * transliterators. The array of transliterators may be of any + * length, including zero or one, however, useful compound + * transliterators have at least two components. + * @param transliterators array of Transliterator + * objects + * @param transliteratorCount The number of + * Transliterator objects in transliterators. + * @param filter the filter. Any character for which + * filter.contains() returns false will not be + * altered by this transliterator. If filter is + * null then no filtering is applied. + */ +CompoundTransliterator::CompoundTransliterator( + Transliterator* const transliterators[], + int32_t transliteratorCount, + UnicodeFilter* adoptedFilter) : + Transliterator(joinIDs(transliterators, transliteratorCount), adoptedFilter), + trans(0), count(0), numAnonymousRBTs(0) { + setTransliterators(transliterators, transliteratorCount); +} + +/** + * Splits an ID of the form "ID;ID;..." into a compound using each + * of the IDs. + * @param id of above form + * @param forward if false, does the list in reverse order, and + * takes the inverse of each ID. + */ +CompoundTransliterator::CompoundTransliterator(const UnicodeString& id, + UTransDirection direction, + UnicodeFilter* adoptedFilter, + UParseError& /*parseError*/, + UErrorCode& status) : + Transliterator(id, adoptedFilter), + trans(0), numAnonymousRBTs(0) { + // TODO add code for parseError...currently unused, but + // later may be used by parsing code... + init(id, direction, true, status); +} + +CompoundTransliterator::CompoundTransliterator(const UnicodeString& id, + UParseError& /*parseError*/, + UErrorCode& status) : + Transliterator(id, 0), // set filter to 0 here! + trans(0), numAnonymousRBTs(0) { + // TODO add code for parseError...currently unused, but + // later may be used by parsing code... + init(id, UTRANS_FORWARD, true, status); +} + + +/** + * Private constructor for use of TransliteratorAlias + */ +CompoundTransliterator::CompoundTransliterator(const UnicodeString& newID, + UVector& list, + UnicodeFilter* adoptedFilter, + int32_t anonymousRBTs, + UParseError& /*parseError*/, + UErrorCode& status) : + Transliterator(newID, adoptedFilter), + trans(0), numAnonymousRBTs(anonymousRBTs) +{ + init(list, UTRANS_FORWARD, false, status); +} + +/** + * Private constructor for Transliterator from a vector of + * transliterators. The caller is responsible for fixing up the + * ID. + */ +CompoundTransliterator::CompoundTransliterator(UVector& list, + UParseError& /*parseError*/, + UErrorCode& status) : + Transliterator(UnicodeString(), nullptr), + trans(0), numAnonymousRBTs(0) +{ + // TODO add code for parseError...currently unused, but + // later may be used by parsing code... + init(list, UTRANS_FORWARD, false, status); + // assume caller will fixup ID +} + +CompoundTransliterator::CompoundTransliterator(UVector& list, + int32_t anonymousRBTs, + UParseError& /*parseError*/, + UErrorCode& status) : + Transliterator(UnicodeString(), nullptr), + trans(0), numAnonymousRBTs(anonymousRBTs) +{ + init(list, UTRANS_FORWARD, false, status); +} + +/** + * Finish constructing a transliterator: only to be called by + * constructors. Before calling init(), set trans and filter to nullptr. + * @param id the id containing ';'-separated entries + * @param direction either FORWARD or REVERSE + * @param idSplitPoint the index into id at which the + * adoptedSplitTransliterator should be inserted, if there is one, or + * -1 if there is none. + * @param adoptedSplitTransliterator a transliterator to be inserted + * before the entry at offset idSplitPoint in the id string. May be + * nullptr to insert no entry. + * @param fixReverseID if true, then reconstruct the ID of reverse + * entries by calling getID() of component entries. Some constructors + * do not require this because they apply a facade ID anyway. + * @param status the error code indicating success or failure + */ +void CompoundTransliterator::init(const UnicodeString& id, + UTransDirection direction, + UBool fixReverseID, + UErrorCode& status) { + // assert(trans == 0); + + if (U_FAILURE(status)) { + return; + } + + UVector list(status); + UnicodeSet* compoundFilter = nullptr; + UnicodeString regenID; + if (!TransliteratorIDParser::parseCompoundID(id, direction, + regenID, list, compoundFilter)) { + status = U_INVALID_ID; + delete compoundFilter; + return; + } + + TransliteratorIDParser::instantiateList(list, status); + + init(list, direction, fixReverseID, status); + + if (compoundFilter != nullptr) { + adoptFilter(compoundFilter); + } +} + +/** + * Finish constructing a transliterator: only to be called by + * constructors. Before calling init(), set trans and filter to nullptr. + * @param list a vector of transliterator objects to be adopted. It + * should NOT be empty. The list should be in declared order. That + * is, it should be in the FORWARD order; if direction is REVERSE then + * the list order will be reversed. + * @param direction either FORWARD or REVERSE + * @param fixReverseID if true, then reconstruct the ID of reverse + * entries by calling getID() of component entries. Some constructors + * do not require this because they apply a facade ID anyway. + * @param status the error code indicating success or failure + */ +void CompoundTransliterator::init(UVector& list, + UTransDirection direction, + UBool fixReverseID, + UErrorCode& status) { + // assert(trans == 0); + + // Allocate array + if (U_SUCCESS(status)) { + count = list.size(); + trans = (Transliterator **)uprv_malloc(count * sizeof(Transliterator *)); + /* test for nullptr */ + if (trans == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + + if (U_FAILURE(status) || trans == 0) { + // assert(trans == 0); + return; + } + + // Move the transliterators from the vector into an array. + // Reverse the order if necessary. + int32_t i; + for (i=0; i 0) { + newID.append(ID_DELIM); + } + newID.append(trans[i]->getID()); + } + setID(newID); + } + + computeMaximumContextLength(); +} + +/** + * Return the IDs of the given list of transliterators, concatenated + * with ID_DELIM delimiting them. Equivalent to the perlish expression + * join(ID_DELIM, map($_.getID(), transliterators). + */ +UnicodeString CompoundTransliterator::joinIDs(Transliterator* const transliterators[], + int32_t transCount) { + UnicodeString id; + for (int32_t i=0; i 0) { + id.append(ID_DELIM); + } + id.append(transliterators[i]->getID()); + } + return id; // Return temporary +} + +/** + * Copy constructor. + */ +CompoundTransliterator::CompoundTransliterator(const CompoundTransliterator& t) : + Transliterator(t), trans(0), count(0), numAnonymousRBTs(-1) { + *this = t; +} + +/** + * Destructor + */ +CompoundTransliterator::~CompoundTransliterator() { + freeTransliterators(); +} + +void CompoundTransliterator::freeTransliterators() { + if (trans != 0) { + for (int32_t i=0; i count) { + if (trans != nullptr) { + uprv_free(trans); + } + trans = (Transliterator **)uprv_malloc(t.count * sizeof(Transliterator *)); + } + count = t.count; + if (trans != nullptr) { + for (i=0; iclone(); + if (trans[i] == nullptr) { + failed = true; + break; + } + } + } + + // if memory allocation failed delete backwards trans array + if (failed && i > 0) { + int32_t n; + for (n = i-1; n >= 0; n--) { + uprv_free(trans[n]); + trans[n] = nullptr; + } + } + numAnonymousRBTs = t.numAnonymousRBTs; + return *this; +} + +/** + * Transliterator API. + */ +CompoundTransliterator* CompoundTransliterator::clone() const { + return new CompoundTransliterator(*this); +} + +/** + * Returns the number of transliterators in this chain. + * @return number of transliterators in this chain. + */ +int32_t CompoundTransliterator::getCount() const { + return count; +} + +/** + * Returns the transliterator at the given index in this chain. + * @param index index into chain, from 0 to getCount() - 1 + * @return transliterator at the given index + */ +const Transliterator& CompoundTransliterator::getTransliterator(int32_t index) const { + return *trans[index]; +} + +void CompoundTransliterator::setTransliterators(Transliterator* const transliterators[], + int32_t transCount) { + Transliterator** a = (Transliterator **)uprv_malloc(transCount * sizeof(Transliterator *)); + if (a == nullptr) { + return; + } + int32_t i = 0; + UBool failed = false; + for (i=0; iclone(); + if (a[i] == nullptr) { + failed = true; + break; + } + } + if (failed && i > 0) { + int32_t n; + for (n = i-1; n >= 0; n--) { + uprv_free(a[n]); + a[n] = nullptr; + } + return; + } + adoptTransliterators(a, transCount); +} + +void CompoundTransliterator::adoptTransliterators(Transliterator* adoptedTransliterators[], + int32_t transCount) { + // First free trans[] and set count to zero. Once this is done, + // orphan the filter. Set up the new trans[]. + freeTransliterators(); + trans = adoptedTransliterators; + count = transCount; + computeMaximumContextLength(); + setID(joinIDs(trans, count)); +} + +/** + * Append c to buf, unless buf is empty or buf already ends in c. + */ +static void _smartAppend(UnicodeString& buf, char16_t c) { + if (buf.length() != 0 && + buf.charAt(buf.length() - 1) != c) { + buf.append(c); + } +} + +UnicodeString& CompoundTransliterator::toRules(UnicodeString& rulesSource, + UBool escapeUnprintable) const { + // We do NOT call toRules() on our component transliterators, in + // general. If we have several rule-based transliterators, this + // yields a concatenation of the rules -- not what we want. We do + // handle compound RBT transliterators specially -- those for which + // compoundRBTIndex >= 0. For the transliterator at compoundRBTIndex, + // we do call toRules() recursively. + rulesSource.truncate(0); + if (numAnonymousRBTs >= 1 && getFilter() != nullptr) { + // If we are a compound RBT and if we have a global + // filter, then emit it at the top. + UnicodeString pat; + rulesSource.append(COLON_COLON, 2).append(getFilter()->toPattern(pat, escapeUnprintable)).append(ID_DELIM); + } + for (int32_t i=0; igetID().startsWith(PASS_STRING, 5)) { + trans[i]->toRules(rule, escapeUnprintable); + if (numAnonymousRBTs > 1 && i > 0 && trans[i - 1]->getID().startsWith(PASS_STRING, 5)) + rule = UNICODE_STRING_SIMPLE("::Null;") + rule; + + // we also use toRules() on CompoundTransliterators (which we + // check for by looking for a semicolon in the ID)-- this gets + // the list of their child transliterators output in the right + // format + } else if (trans[i]->getID().indexOf(ID_DELIM) >= 0) { + trans[i]->toRules(rule, escapeUnprintable); + + // for everything else, use Transliterator::toRules() + } else { + trans[i]->Transliterator::toRules(rule, escapeUnprintable); + } + _smartAppend(rulesSource, NEWLINE); + rulesSource.append(rule); + _smartAppend(rulesSource, ID_DELIM); + } + return rulesSource; +} + +/** + * Implement Transliterator framework + */ +void CompoundTransliterator::handleGetSourceSet(UnicodeSet& result) const { + UnicodeSet set; + result.clear(); + for (int32_t i=0; igetSourceSet(set)); + // Take the example of Hiragana-Latin. This is really + // Hiragana-Katakana; Katakana-Latin. The source set of + // these two is roughly [:Hiragana:] and [:Katakana:]. + // But the source set for the entire transliterator is + // actually [:Hiragana:] ONLY -- that is, the first + // non-empty source set. + + // This is a heuristic, and not 100% reliable. + if (!result.isEmpty()) { + break; + } + } +} + +/** + * Override Transliterator framework + */ +UnicodeSet& CompoundTransliterator::getTargetSet(UnicodeSet& result) const { + UnicodeSet set; + result.clear(); + for (int32_t i=0; igetTargetSet(set)); + } + return result; +} + +/** + * Implements {@link Transliterator#handleTransliterate}. + */ +void CompoundTransliterator::handleTransliterate(Replaceable& text, UTransPosition& index, + UBool incremental) const { + /* Call each transliterator with the same contextStart and + * start, but with the limit as modified + * by preceding transliterators. The start index must be + * reset for each transliterator to give each a chance to + * transliterate the text. The initial contextStart index is known + * to still point to the same place after each transliterator + * is called because each transliterator will not change the + * text between contextStart and the initial start index. + * + * IMPORTANT: After the first transliterator, each subsequent + * transliterator only gets to transliterate text committed by + * preceding transliterators; that is, the start (output + * value) of transliterator i becomes the limit (input value) + * of transliterator i+1. Finally, the overall limit is fixed + * up before we return. + * + * Assumptions we make here: + * (1) contextStart <= start <= limit <= contextLimit <= text.length() + * (2) start <= start' <= limit' ;cursor doesn't move back + * (3) start <= limit' ;text before cursor unchanged + * - start' is the value of start after calling handleKT + * - limit' is the value of limit after calling handleKT + */ + + /** + * Example: 3 transliterators. This example illustrates the + * mechanics we need to implement. C, S, and L are the contextStart, + * start, and limit. gl is the globalLimit. contextLimit is + * equal to limit throughout. + * + * 1. h-u, changes hex to Unicode + * + * 4 7 a d 0 4 7 a + * abc/u0061/u => abca/u + * C S L C S L gl=f->a + * + * 2. upup, changes "x" to "XX" + * + * 4 7 a 4 7 a + * abca/u => abcAA/u + * C SL C S + * L gl=a->b + * 3. u-h, changes Unicode to hex + * + * 4 7 a 4 7 a d 0 3 + * abcAA/u => abc/u0041/u0041/u + * C S L C S + * L gl=b->15 + * 4. return + * + * 4 7 a d 0 3 + * abc/u0041/u0041/u + * C S L + */ + + if (count < 1) { + index.start = index.limit; + return; // Short circuit for empty compound transliterators + } + + // compoundLimit is the limit value for the entire compound + // operation. We overwrite index.limit with the previous + // index.start. After each transliteration, we update + // compoundLimit for insertions or deletions that have happened. + int32_t compoundLimit = index.limit; + + // compoundStart is the start for the entire compound + // operation. + int32_t compoundStart = index.start; + + int32_t delta = 0; // delta in length + + // Give each transliterator a crack at the run of characters. + // See comments at the top of the method for more detail. + for (int32_t i=0; ifilteredTransliterate(text, index, incremental); + + // In a properly written transliterator, start == limit after + // handleTransliterate() returns when incremental is false. + // Catch cases where the subclass doesn't do this, and throw + // an exception. (Just pinning start to limit is a bad idea, + // because what's probably happening is that the subclass + // isn't transliterating all the way to the end, and it should + // in non-incremental mode.) + if (!incremental && index.start != index.limit) { + // We can't throw an exception, so just fudge things + index.start = index.limit; + } + + // Cumulative delta for insertions/deletions + delta += index.limit - limit; + + if (incremental) { + // In the incremental case, only allow subsequent + // transliterators to modify what has already been + // completely processed by prior transliterators. In the + // non-incrmental case, allow each transliterator to + // process the entire text. + index.limit = index.start; + } + } + + compoundLimit += delta; + + // Start is good where it is -- where the last transliterator left + // it. Limit needs to be put back where it was, modulo + // adjustments for deletions/insertions. + index.limit = compoundLimit; +} + +/** + * Sets the length of the longest context required by this transliterator. + * This is preceding context. + */ +void CompoundTransliterator::computeMaximumContextLength() { + int32_t max = 0; + for (int32_t i=0; igetMaximumContextLength(); + if (len > max) { + max = len; + } + } + setMaximumContextLength(max); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +/* eof */ diff --git a/deps/icu-small/source/i18n/cpdtrans.h b/deps/icu-small/source/i18n/cpdtrans.h index af60cb827e3040..7191fbfd1d001b 100644 --- a/deps/icu-small/source/i18n/cpdtrans.h +++ b/deps/icu-small/source/i18n/cpdtrans.h @@ -1,232 +1,232 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/17/99 aliu Creation. -********************************************************************** -*/ -#ifndef CPDTRANS_H -#define CPDTRANS_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/translit.h" - -U_NAMESPACE_BEGIN - -class U_COMMON_API UVector; -class TransliteratorRegistry; - -/** - * A transliterator that is composed of two or more other - * transliterator objects linked together. For example, if one - * transliterator transliterates from script A to script B, and - * another transliterates from script B to script C, the two may be - * combined to form a new transliterator from A to C. - * - *

Composed transliterators may not behave as expected. For - * example, inverses may not combine to form the identity - * transliterator. See the class documentation for {@link - * Transliterator} for details. - * - * @author Alan Liu - */ -class U_I18N_API CompoundTransliterator : public Transliterator { - - Transliterator** trans; - - int32_t count; - - int32_t numAnonymousRBTs; - -public: - - /** - * Constructs a new compound transliterator given an array of - * transliterators. The array of transliterators may be of any - * length, including zero or one, however, useful compound - * transliterators have at least two components. - * @param transliterators array of Transliterator - * objects - * @param transliteratorCount The number of - * Transliterator objects in transliterators. - * @param adoptedFilter the filter. Any character for which - * filter.contains() returns false will not be - * altered by this transliterator. If filter is - * null then no filtering is applied. - */ - CompoundTransliterator(Transliterator* const transliterators[], - int32_t transliteratorCount, - UnicodeFilter* adoptedFilter = 0); - - /** - * Constructs a new compound transliterator. - * @param id compound ID - * @param dir either UTRANS_FORWARD or UTRANS_REVERSE - * @param adoptedFilter a global filter for this compound transliterator - * or NULL - */ - CompoundTransliterator(const UnicodeString& id, - UTransDirection dir, - UnicodeFilter* adoptedFilter, - UParseError& parseError, - UErrorCode& status); - - /** - * Constructs a new compound transliterator in the FORWARD - * direction with a NULL filter. - */ - CompoundTransliterator(const UnicodeString& id, - UParseError& parseError, - UErrorCode& status); - /** - * Destructor. - */ - virtual ~CompoundTransliterator(); - - /** - * Copy constructor. - */ - CompoundTransliterator(const CompoundTransliterator&); - - /** - * Transliterator API. - */ - virtual CompoundTransliterator* clone() const override; - - /** - * Returns the number of transliterators in this chain. - * @return number of transliterators in this chain. - */ - virtual int32_t getCount(void) const; - - /** - * Returns the transliterator at the given index in this chain. - * @param idx index into chain, from 0 to getCount() - 1 - * @return transliterator at the given index - */ - virtual const Transliterator& getTransliterator(int32_t idx) const; - - /** - * Sets the transliterators. - */ - void setTransliterators(Transliterator* const transliterators[], - int32_t count); - - /** - * Adopts the transliterators. - */ - void adoptTransliterators(Transliterator* adoptedTransliterators[], - int32_t count); - - /** - * Override Transliterator: - * Create a rule string that can be passed to createFromRules() - * to recreate this transliterator. - * @param result the string to receive the rules. Previous - * contents will be deleted. - * @param escapeUnprintable if true then convert unprintable - * character to their hex escape representations, \uxxxx or - * \Uxxxxxxxx. Unprintable characters are those other than - * U+000A, U+0020..U+007E. - */ - virtual UnicodeString& toRules(UnicodeString& result, - UBool escapeUnprintable) const override; - - protected: - /** - * Implement Transliterator framework - */ - virtual void handleGetSourceSet(UnicodeSet& result) const override; - - public: - /** - * Override Transliterator framework - */ - virtual UnicodeSet& getTargetSet(UnicodeSet& result) const override; - -protected: - /** - * Implements {@link Transliterator#handleTransliterate}. - */ - virtual void handleTransliterate(Replaceable& text, UTransPosition& idx, - UBool incremental) const override; - -public: - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - virtual UClassID getDynamicClassID() const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /* @internal */ - static const UChar PASS_STRING[]; - -private: - - friend class Transliterator; - friend class TransliteratorAlias; // to access private ct - - /** - * Assignment operator. - */ - CompoundTransliterator& operator=(const CompoundTransliterator&); - - /** - * Private constructor for Transliterator. - */ - CompoundTransliterator(const UnicodeString& ID, - UVector& list, - UnicodeFilter* adoptedFilter, - int32_t numAnonymousRBTs, - UParseError& parseError, - UErrorCode& status); - - CompoundTransliterator(UVector& list, - UParseError& parseError, - UErrorCode& status); - - CompoundTransliterator(UVector& list, - int32_t anonymousRBTs, - UParseError& parseError, - UErrorCode& status); - - void init(const UnicodeString& id, - UTransDirection direction, - UBool fixReverseID, - UErrorCode& status); - - void init(UVector& list, - UTransDirection direction, - UBool fixReverseID, - UErrorCode& status); - - /** - * Return the IDs of the given list of transliterators, concatenated - * with ';' delimiting them. Equivalent to the perlish expression - * join(';', map($_.getID(), transliterators). - */ - UnicodeString joinIDs(Transliterator* const transliterators[], - int32_t transCount); - - void freeTransliterators(void); - - void computeMaximumContextLength(void); -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/17/99 aliu Creation. +********************************************************************** +*/ +#ifndef CPDTRANS_H +#define CPDTRANS_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/translit.h" + +U_NAMESPACE_BEGIN + +class U_COMMON_API UVector; +class TransliteratorRegistry; + +/** + * A transliterator that is composed of two or more other + * transliterator objects linked together. For example, if one + * transliterator transliterates from script A to script B, and + * another transliterates from script B to script C, the two may be + * combined to form a new transliterator from A to C. + * + *

Composed transliterators may not behave as expected. For + * example, inverses may not combine to form the identity + * transliterator. See the class documentation for {@link + * Transliterator} for details. + * + * @author Alan Liu + */ +class U_I18N_API CompoundTransliterator : public Transliterator { + + Transliterator** trans; + + int32_t count; + + int32_t numAnonymousRBTs; + +public: + + /** + * Constructs a new compound transliterator given an array of + * transliterators. The array of transliterators may be of any + * length, including zero or one, however, useful compound + * transliterators have at least two components. + * @param transliterators array of Transliterator + * objects + * @param transliteratorCount The number of + * Transliterator objects in transliterators. + * @param adoptedFilter the filter. Any character for which + * filter.contains() returns false will not be + * altered by this transliterator. If filter is + * null then no filtering is applied. + */ + CompoundTransliterator(Transliterator* const transliterators[], + int32_t transliteratorCount, + UnicodeFilter* adoptedFilter = 0); + + /** + * Constructs a new compound transliterator. + * @param id compound ID + * @param dir either UTRANS_FORWARD or UTRANS_REVERSE + * @param adoptedFilter a global filter for this compound transliterator + * or nullptr + */ + CompoundTransliterator(const UnicodeString& id, + UTransDirection dir, + UnicodeFilter* adoptedFilter, + UParseError& parseError, + UErrorCode& status); + + /** + * Constructs a new compound transliterator in the FORWARD + * direction with a nullptr filter. + */ + CompoundTransliterator(const UnicodeString& id, + UParseError& parseError, + UErrorCode& status); + /** + * Destructor. + */ + virtual ~CompoundTransliterator(); + + /** + * Copy constructor. + */ + CompoundTransliterator(const CompoundTransliterator&); + + /** + * Transliterator API. + */ + virtual CompoundTransliterator* clone() const override; + + /** + * Returns the number of transliterators in this chain. + * @return number of transliterators in this chain. + */ + virtual int32_t getCount() const; + + /** + * Returns the transliterator at the given index in this chain. + * @param idx index into chain, from 0 to getCount() - 1 + * @return transliterator at the given index + */ + virtual const Transliterator& getTransliterator(int32_t idx) const; + + /** + * Sets the transliterators. + */ + void setTransliterators(Transliterator* const transliterators[], + int32_t count); + + /** + * Adopts the transliterators. + */ + void adoptTransliterators(Transliterator* adoptedTransliterators[], + int32_t count); + + /** + * Override Transliterator: + * Create a rule string that can be passed to createFromRules() + * to recreate this transliterator. + * @param result the string to receive the rules. Previous + * contents will be deleted. + * @param escapeUnprintable if true then convert unprintable + * character to their hex escape representations, \uxxxx or + * \Uxxxxxxxx. Unprintable characters are those other than + * U+000A, U+0020..U+007E. + */ + virtual UnicodeString& toRules(UnicodeString& result, + UBool escapeUnprintable) const override; + + protected: + /** + * Implement Transliterator framework + */ + virtual void handleGetSourceSet(UnicodeSet& result) const override; + + public: + /** + * Override Transliterator framework + */ + virtual UnicodeSet& getTargetSet(UnicodeSet& result) const override; + +protected: + /** + * Implements {@link Transliterator#handleTransliterate}. + */ + virtual void handleTransliterate(Replaceable& text, UTransPosition& idx, + UBool incremental) const override; + +public: + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + virtual UClassID getDynamicClassID() const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /* @internal */ + static const char16_t PASS_STRING[]; + +private: + + friend class Transliterator; + friend class TransliteratorAlias; // to access private ct + + /** + * Assignment operator. + */ + CompoundTransliterator& operator=(const CompoundTransliterator&); + + /** + * Private constructor for Transliterator. + */ + CompoundTransliterator(const UnicodeString& ID, + UVector& list, + UnicodeFilter* adoptedFilter, + int32_t numAnonymousRBTs, + UParseError& parseError, + UErrorCode& status); + + CompoundTransliterator(UVector& list, + UParseError& parseError, + UErrorCode& status); + + CompoundTransliterator(UVector& list, + int32_t anonymousRBTs, + UParseError& parseError, + UErrorCode& status); + + void init(const UnicodeString& id, + UTransDirection direction, + UBool fixReverseID, + UErrorCode& status); + + void init(UVector& list, + UTransDirection direction, + UBool fixReverseID, + UErrorCode& status); + + /** + * Return the IDs of the given list of transliterators, concatenated + * with ';' delimiting them. Equivalent to the perlish expression + * join(';', map($_.getID(), transliterators). + */ + UnicodeString joinIDs(Transliterator* const transliterators[], + int32_t transCount); + + void freeTransliterators(); + + void computeMaximumContextLength(); +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/csdetect.cpp b/deps/icu-small/source/i18n/csdetect.cpp index 0b22d4dc2ae9b7..6fd93d09e4ae8e 100644 --- a/deps/icu-small/source/i18n/csdetect.cpp +++ b/deps/icu-small/source/i18n/csdetect.cpp @@ -1,492 +1,492 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2016, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/ucsdet.h" - -#include "csdetect.h" -#include "csmatch.h" -#include "uenumimp.h" - -#include "cmemory.h" -#include "cstring.h" -#include "umutex.h" -#include "ucln_in.h" -#include "uarrsort.h" -#include "inputext.h" -#include "csrsbcs.h" -#include "csrmbcs.h" -#include "csrutf8.h" -#include "csrucode.h" -#include "csr2022.h" - -#define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type)) -#define DELETE_ARRAY(array) uprv_free((void *) (array)) - -U_NAMESPACE_BEGIN - -struct CSRecognizerInfo : public UMemory { - CSRecognizerInfo(CharsetRecognizer *recognizer, UBool isDefaultEnabled) - : recognizer(recognizer), isDefaultEnabled(isDefaultEnabled) {} - - ~CSRecognizerInfo() {delete recognizer;} - - CharsetRecognizer *recognizer; - UBool isDefaultEnabled; -}; - -U_NAMESPACE_END - -static icu::CSRecognizerInfo **fCSRecognizers = NULL; -static icu::UInitOnce gCSRecognizersInitOnce {}; -static int32_t fCSRecognizers_size = 0; - -U_CDECL_BEGIN -static UBool U_CALLCONV csdet_cleanup(void) -{ - U_NAMESPACE_USE - if (fCSRecognizers != NULL) { - for(int32_t r = 0; r < fCSRecognizers_size; r += 1) { - delete fCSRecognizers[r]; - fCSRecognizers[r] = NULL; - } - - DELETE_ARRAY(fCSRecognizers); - fCSRecognizers = NULL; - fCSRecognizers_size = 0; - } - gCSRecognizersInitOnce.reset(); - - return true; -} - -static int32_t U_CALLCONV -charsetMatchComparator(const void * /*context*/, const void *left, const void *right) -{ - U_NAMESPACE_USE - - const CharsetMatch **csm_l = (const CharsetMatch **) left; - const CharsetMatch **csm_r = (const CharsetMatch **) right; - - // NOTE: compare is backwards to sort from highest to lowest. - return (*csm_r)->getConfidence() - (*csm_l)->getConfidence(); -} - -static void U_CALLCONV initRecognizers(UErrorCode &status) { - U_NAMESPACE_USE - ucln_i18n_registerCleanup(UCLN_I18N_CSDET, csdet_cleanup); - CSRecognizerInfo *tempArray[] = { - new CSRecognizerInfo(new CharsetRecog_UTF8(), true), - - new CSRecognizerInfo(new CharsetRecog_UTF_16_BE(), true), - new CSRecognizerInfo(new CharsetRecog_UTF_16_LE(), true), - new CSRecognizerInfo(new CharsetRecog_UTF_32_BE(), true), - new CSRecognizerInfo(new CharsetRecog_UTF_32_LE(), true), - - new CSRecognizerInfo(new CharsetRecog_8859_1(), true), - new CSRecognizerInfo(new CharsetRecog_8859_2(), true), - new CSRecognizerInfo(new CharsetRecog_8859_5_ru(), true), - new CSRecognizerInfo(new CharsetRecog_8859_6_ar(), true), - new CSRecognizerInfo(new CharsetRecog_8859_7_el(), true), - new CSRecognizerInfo(new CharsetRecog_8859_8_I_he(), true), - new CSRecognizerInfo(new CharsetRecog_8859_8_he(), true), - new CSRecognizerInfo(new CharsetRecog_windows_1251(), true), - new CSRecognizerInfo(new CharsetRecog_windows_1256(), true), - new CSRecognizerInfo(new CharsetRecog_KOI8_R(), true), - new CSRecognizerInfo(new CharsetRecog_8859_9_tr(), true), - new CSRecognizerInfo(new CharsetRecog_sjis(), true), - new CSRecognizerInfo(new CharsetRecog_gb_18030(), true), - new CSRecognizerInfo(new CharsetRecog_euc_jp(), true), - new CSRecognizerInfo(new CharsetRecog_euc_kr(), true), - new CSRecognizerInfo(new CharsetRecog_big5(), true), - - new CSRecognizerInfo(new CharsetRecog_2022JP(), true), -#if !UCONFIG_ONLY_HTML_CONVERSION - new CSRecognizerInfo(new CharsetRecog_2022KR(), true), - new CSRecognizerInfo(new CharsetRecog_2022CN(), true), - - new CSRecognizerInfo(new CharsetRecog_IBM424_he_rtl(), false), - new CSRecognizerInfo(new CharsetRecog_IBM424_he_ltr(), false), - new CSRecognizerInfo(new CharsetRecog_IBM420_ar_rtl(), false), - new CSRecognizerInfo(new CharsetRecog_IBM420_ar_ltr(), false) -#endif - }; - int32_t rCount = UPRV_LENGTHOF(tempArray); - - fCSRecognizers = NEW_ARRAY(CSRecognizerInfo *, rCount); - - if (fCSRecognizers == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } - else { - fCSRecognizers_size = rCount; - for (int32_t r = 0; r < rCount; r += 1) { - fCSRecognizers[r] = tempArray[r]; - if (fCSRecognizers[r] == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } - } - } -} - -U_CDECL_END - -U_NAMESPACE_BEGIN - -void CharsetDetector::setRecognizers(UErrorCode &status) -{ - umtx_initOnce(gCSRecognizersInitOnce, &initRecognizers, status); -} - -CharsetDetector::CharsetDetector(UErrorCode &status) - : textIn(new InputText(status)), resultArray(NULL), - resultCount(0), fStripTags(false), fFreshTextSet(false), - fEnabledRecognizers(NULL) -{ - if (U_FAILURE(status)) { - return; - } - - setRecognizers(status); - - if (U_FAILURE(status)) { - return; - } - - resultArray = (CharsetMatch **)uprv_malloc(sizeof(CharsetMatch *)*fCSRecognizers_size); - - if (resultArray == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - for(int32_t i = 0; i < fCSRecognizers_size; i += 1) { - resultArray[i] = new CharsetMatch(); - - if (resultArray[i] == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - } -} - -CharsetDetector::~CharsetDetector() -{ - delete textIn; - - for(int32_t i = 0; i < fCSRecognizers_size; i += 1) { - delete resultArray[i]; - } - - uprv_free(resultArray); - - if (fEnabledRecognizers) { - uprv_free(fEnabledRecognizers); - } -} - -void CharsetDetector::setText(const char *in, int32_t len) -{ - textIn->setText(in, len); - fFreshTextSet = true; -} - -UBool CharsetDetector::setStripTagsFlag(UBool flag) -{ - UBool temp = fStripTags; - fStripTags = flag; - fFreshTextSet = true; - return temp; -} - -UBool CharsetDetector::getStripTagsFlag() const -{ - return fStripTags; -} - -void CharsetDetector::setDeclaredEncoding(const char *encoding, int32_t len) const -{ - textIn->setDeclaredEncoding(encoding,len); -} - -int32_t CharsetDetector::getDetectableCount() -{ - UErrorCode status = U_ZERO_ERROR; - - setRecognizers(status); - - return fCSRecognizers_size; -} - -const CharsetMatch *CharsetDetector::detect(UErrorCode &status) -{ - int32_t maxMatchesFound = 0; - - detectAll(maxMatchesFound, status); - - if(maxMatchesFound > 0) { - return resultArray[0]; - } else { - return NULL; - } -} - -const CharsetMatch * const *CharsetDetector::detectAll(int32_t &maxMatchesFound, UErrorCode &status) -{ - if(!textIn->isSet()) { - status = U_MISSING_RESOURCE_ERROR;// TODO: Need to set proper status code for input text not set - - return NULL; - } else if (fFreshTextSet) { - CharsetRecognizer *csr; - int32_t i; - - textIn->MungeInput(fStripTags); - - // Iterate over all possible charsets, remember all that - // give a match quality > 0. - resultCount = 0; - for (i = 0; i < fCSRecognizers_size; i += 1) { - csr = fCSRecognizers[i]->recognizer; - if (csr->match(textIn, resultArray[resultCount])) { - resultCount++; - } - } - - if (resultCount > 1) { - uprv_sortArray(resultArray, resultCount, sizeof resultArray[0], charsetMatchComparator, NULL, true, &status); - } - fFreshTextSet = false; - } - - maxMatchesFound = resultCount; - - if (maxMatchesFound == 0) { - status = U_INVALID_CHAR_FOUND; - return NULL; - } - - return resultArray; -} - -void CharsetDetector::setDetectableCharset(const char *encoding, UBool enabled, UErrorCode &status) -{ - if (U_FAILURE(status)) { - return; - } - - int32_t modIdx = -1; - UBool isDefaultVal = false; - for (int32_t i = 0; i < fCSRecognizers_size; i++) { - CSRecognizerInfo *csrinfo = fCSRecognizers[i]; - if (uprv_strcmp(csrinfo->recognizer->getName(), encoding) == 0) { - modIdx = i; - isDefaultVal = (csrinfo->isDefaultEnabled == enabled); - break; - } - } - if (modIdx < 0) { - // No matching encoding found - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - if (fEnabledRecognizers == NULL && !isDefaultVal) { - // Create an array storing the non default setting - fEnabledRecognizers = NEW_ARRAY(UBool, fCSRecognizers_size); - if (fEnabledRecognizers == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - // Initialize the array with default info - for (int32_t i = 0; i < fCSRecognizers_size; i++) { - fEnabledRecognizers[i] = fCSRecognizers[i]->isDefaultEnabled; - } - } - - if (fEnabledRecognizers != NULL) { - fEnabledRecognizers[modIdx] = enabled; - } -} - -/*const char *CharsetDetector::getCharsetName(int32_t index, UErrorCode &status) const -{ - if( index > fCSRecognizers_size-1 || index < 0) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - - return 0; - } else { - return fCSRecognizers[index]->getName(); - } -}*/ - -U_NAMESPACE_END - -U_CDECL_BEGIN -typedef struct { - int32_t currIndex; - UBool all; - UBool *enabledRecognizers; -} Context; - - - -static void U_CALLCONV -enumClose(UEnumeration *en) { - if(en->context != NULL) { - DELETE_ARRAY(en->context); - } - - DELETE_ARRAY(en); -} - -static int32_t U_CALLCONV -enumCount(UEnumeration *en, UErrorCode *) { - if (((Context *)en->context)->all) { - // ucsdet_getAllDetectableCharsets, all charset detector names - return fCSRecognizers_size; - } - - // Otherwise, ucsdet_getDetectableCharsets - only enabled ones - int32_t count = 0; - UBool *enabledArray = ((Context *)en->context)->enabledRecognizers; - if (enabledArray != NULL) { - // custom set - for (int32_t i = 0; i < fCSRecognizers_size; i++) { - if (enabledArray[i]) { - count++; - } - } - } else { - // default set - for (int32_t i = 0; i < fCSRecognizers_size; i++) { - if (fCSRecognizers[i]->isDefaultEnabled) { - count++; - } - } - } - return count; -} - -static const char* U_CALLCONV -enumNext(UEnumeration *en, int32_t *resultLength, UErrorCode * /*status*/) { - const char *currName = NULL; - - if (((Context *)en->context)->currIndex < fCSRecognizers_size) { - if (((Context *)en->context)->all) { - // ucsdet_getAllDetectableCharsets, all charset detector names - currName = fCSRecognizers[((Context *)en->context)->currIndex]->recognizer->getName(); - ((Context *)en->context)->currIndex++; - } else { - // ucsdet_getDetectableCharsets - UBool *enabledArray = ((Context *)en->context)->enabledRecognizers; - if (enabledArray != NULL) { - // custom set - while (currName == NULL && ((Context *)en->context)->currIndex < fCSRecognizers_size) { - if (enabledArray[((Context *)en->context)->currIndex]) { - currName = fCSRecognizers[((Context *)en->context)->currIndex]->recognizer->getName(); - } - ((Context *)en->context)->currIndex++; - } - } else { - // default set - while (currName == NULL && ((Context *)en->context)->currIndex < fCSRecognizers_size) { - if (fCSRecognizers[((Context *)en->context)->currIndex]->isDefaultEnabled) { - currName = fCSRecognizers[((Context *)en->context)->currIndex]->recognizer->getName(); - } - ((Context *)en->context)->currIndex++; - } - } - } - } - - if(resultLength != NULL) { - *resultLength = currName == NULL ? 0 : (int32_t)uprv_strlen(currName); - } - - return currName; -} - - -static void U_CALLCONV -enumReset(UEnumeration *en, UErrorCode *) { - ((Context *)en->context)->currIndex = 0; -} - -static const UEnumeration gCSDetEnumeration = { - NULL, - NULL, - enumClose, - enumCount, - uenum_unextDefault, - enumNext, - enumReset -}; - -U_CDECL_END - -U_NAMESPACE_BEGIN - -UEnumeration * CharsetDetector::getAllDetectableCharsets(UErrorCode &status) -{ - - /* Initialize recognized charsets. */ - setRecognizers(status); - - if(U_FAILURE(status)) { - return 0; - } - - UEnumeration *en = NEW_ARRAY(UEnumeration, 1); - if (en == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - memcpy(en, &gCSDetEnumeration, sizeof(UEnumeration)); - en->context = (void*)NEW_ARRAY(Context, 1); - if (en->context == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - DELETE_ARRAY(en); - return 0; - } - uprv_memset(en->context, 0, sizeof(Context)); - ((Context*)en->context)->all = true; - return en; -} - -UEnumeration * CharsetDetector::getDetectableCharsets(UErrorCode &status) const -{ - if(U_FAILURE(status)) { - return 0; - } - - UEnumeration *en = NEW_ARRAY(UEnumeration, 1); - if (en == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - memcpy(en, &gCSDetEnumeration, sizeof(UEnumeration)); - en->context = (void*)NEW_ARRAY(Context, 1); - if (en->context == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - DELETE_ARRAY(en); - return 0; - } - uprv_memset(en->context, 0, sizeof(Context)); - ((Context*)en->context)->all = false; - ((Context*)en->context)->enabledRecognizers = fEnabledRecognizers; - return en; -} - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2016, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/ucsdet.h" + +#include "csdetect.h" +#include "csmatch.h" +#include "uenumimp.h" + +#include "cmemory.h" +#include "cstring.h" +#include "umutex.h" +#include "ucln_in.h" +#include "uarrsort.h" +#include "inputext.h" +#include "csrsbcs.h" +#include "csrmbcs.h" +#include "csrutf8.h" +#include "csrucode.h" +#include "csr2022.h" + +#define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type)) +#define DELETE_ARRAY(array) uprv_free((void *) (array)) + +U_NAMESPACE_BEGIN + +struct CSRecognizerInfo : public UMemory { + CSRecognizerInfo(CharsetRecognizer *recognizer, UBool isDefaultEnabled) + : recognizer(recognizer), isDefaultEnabled(isDefaultEnabled) {} + + ~CSRecognizerInfo() {delete recognizer;} + + CharsetRecognizer *recognizer; + UBool isDefaultEnabled; +}; + +U_NAMESPACE_END + +static icu::CSRecognizerInfo **fCSRecognizers = nullptr; +static icu::UInitOnce gCSRecognizersInitOnce {}; +static int32_t fCSRecognizers_size = 0; + +U_CDECL_BEGIN +static UBool U_CALLCONV csdet_cleanup() +{ + U_NAMESPACE_USE + if (fCSRecognizers != nullptr) { + for(int32_t r = 0; r < fCSRecognizers_size; r += 1) { + delete fCSRecognizers[r]; + fCSRecognizers[r] = nullptr; + } + + DELETE_ARRAY(fCSRecognizers); + fCSRecognizers = nullptr; + fCSRecognizers_size = 0; + } + gCSRecognizersInitOnce.reset(); + + return true; +} + +static int32_t U_CALLCONV +charsetMatchComparator(const void * /*context*/, const void *left, const void *right) +{ + U_NAMESPACE_USE + + const CharsetMatch **csm_l = (const CharsetMatch **) left; + const CharsetMatch **csm_r = (const CharsetMatch **) right; + + // NOTE: compare is backwards to sort from highest to lowest. + return (*csm_r)->getConfidence() - (*csm_l)->getConfidence(); +} + +static void U_CALLCONV initRecognizers(UErrorCode &status) { + U_NAMESPACE_USE + ucln_i18n_registerCleanup(UCLN_I18N_CSDET, csdet_cleanup); + CSRecognizerInfo *tempArray[] = { + new CSRecognizerInfo(new CharsetRecog_UTF8(), true), + + new CSRecognizerInfo(new CharsetRecog_UTF_16_BE(), true), + new CSRecognizerInfo(new CharsetRecog_UTF_16_LE(), true), + new CSRecognizerInfo(new CharsetRecog_UTF_32_BE(), true), + new CSRecognizerInfo(new CharsetRecog_UTF_32_LE(), true), + + new CSRecognizerInfo(new CharsetRecog_8859_1(), true), + new CSRecognizerInfo(new CharsetRecog_8859_2(), true), + new CSRecognizerInfo(new CharsetRecog_8859_5_ru(), true), + new CSRecognizerInfo(new CharsetRecog_8859_6_ar(), true), + new CSRecognizerInfo(new CharsetRecog_8859_7_el(), true), + new CSRecognizerInfo(new CharsetRecog_8859_8_I_he(), true), + new CSRecognizerInfo(new CharsetRecog_8859_8_he(), true), + new CSRecognizerInfo(new CharsetRecog_windows_1251(), true), + new CSRecognizerInfo(new CharsetRecog_windows_1256(), true), + new CSRecognizerInfo(new CharsetRecog_KOI8_R(), true), + new CSRecognizerInfo(new CharsetRecog_8859_9_tr(), true), + new CSRecognizerInfo(new CharsetRecog_sjis(), true), + new CSRecognizerInfo(new CharsetRecog_gb_18030(), true), + new CSRecognizerInfo(new CharsetRecog_euc_jp(), true), + new CSRecognizerInfo(new CharsetRecog_euc_kr(), true), + new CSRecognizerInfo(new CharsetRecog_big5(), true), + + new CSRecognizerInfo(new CharsetRecog_2022JP(), true), +#if !UCONFIG_ONLY_HTML_CONVERSION + new CSRecognizerInfo(new CharsetRecog_2022KR(), true), + new CSRecognizerInfo(new CharsetRecog_2022CN(), true), + + new CSRecognizerInfo(new CharsetRecog_IBM424_he_rtl(), false), + new CSRecognizerInfo(new CharsetRecog_IBM424_he_ltr(), false), + new CSRecognizerInfo(new CharsetRecog_IBM420_ar_rtl(), false), + new CSRecognizerInfo(new CharsetRecog_IBM420_ar_ltr(), false) +#endif + }; + int32_t rCount = UPRV_LENGTHOF(tempArray); + + fCSRecognizers = NEW_ARRAY(CSRecognizerInfo *, rCount); + + if (fCSRecognizers == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + else { + fCSRecognizers_size = rCount; + for (int32_t r = 0; r < rCount; r += 1) { + fCSRecognizers[r] = tempArray[r]; + if (fCSRecognizers[r] == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + } + } +} + +U_CDECL_END + +U_NAMESPACE_BEGIN + +void CharsetDetector::setRecognizers(UErrorCode &status) +{ + umtx_initOnce(gCSRecognizersInitOnce, &initRecognizers, status); +} + +CharsetDetector::CharsetDetector(UErrorCode &status) + : textIn(new InputText(status)), resultArray(nullptr), + resultCount(0), fStripTags(false), fFreshTextSet(false), + fEnabledRecognizers(nullptr) +{ + if (U_FAILURE(status)) { + return; + } + + setRecognizers(status); + + if (U_FAILURE(status)) { + return; + } + + resultArray = (CharsetMatch **)uprv_malloc(sizeof(CharsetMatch *)*fCSRecognizers_size); + + if (resultArray == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + for(int32_t i = 0; i < fCSRecognizers_size; i += 1) { + resultArray[i] = new CharsetMatch(); + + if (resultArray[i] == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + break; + } + } +} + +CharsetDetector::~CharsetDetector() +{ + delete textIn; + + for(int32_t i = 0; i < fCSRecognizers_size; i += 1) { + delete resultArray[i]; + } + + uprv_free(resultArray); + + if (fEnabledRecognizers) { + uprv_free(fEnabledRecognizers); + } +} + +void CharsetDetector::setText(const char *in, int32_t len) +{ + textIn->setText(in, len); + fFreshTextSet = true; +} + +UBool CharsetDetector::setStripTagsFlag(UBool flag) +{ + UBool temp = fStripTags; + fStripTags = flag; + fFreshTextSet = true; + return temp; +} + +UBool CharsetDetector::getStripTagsFlag() const +{ + return fStripTags; +} + +void CharsetDetector::setDeclaredEncoding(const char *encoding, int32_t len) const +{ + textIn->setDeclaredEncoding(encoding,len); +} + +int32_t CharsetDetector::getDetectableCount() +{ + UErrorCode status = U_ZERO_ERROR; + + setRecognizers(status); + + return fCSRecognizers_size; +} + +const CharsetMatch *CharsetDetector::detect(UErrorCode &status) +{ + int32_t maxMatchesFound = 0; + + detectAll(maxMatchesFound, status); + + if(maxMatchesFound > 0) { + return resultArray[0]; + } else { + return nullptr; + } +} + +const CharsetMatch * const *CharsetDetector::detectAll(int32_t &maxMatchesFound, UErrorCode &status) +{ + if(!textIn->isSet()) { + status = U_MISSING_RESOURCE_ERROR;// TODO: Need to set proper status code for input text not set + + return nullptr; + } else if (fFreshTextSet) { + CharsetRecognizer *csr; + int32_t i; + + textIn->MungeInput(fStripTags); + + // Iterate over all possible charsets, remember all that + // give a match quality > 0. + resultCount = 0; + for (i = 0; i < fCSRecognizers_size; i += 1) { + csr = fCSRecognizers[i]->recognizer; + if (csr->match(textIn, resultArray[resultCount])) { + resultCount++; + } + } + + if (resultCount > 1) { + uprv_sortArray(resultArray, resultCount, sizeof resultArray[0], charsetMatchComparator, nullptr, true, &status); + } + fFreshTextSet = false; + } + + maxMatchesFound = resultCount; + + if (maxMatchesFound == 0) { + status = U_INVALID_CHAR_FOUND; + return nullptr; + } + + return resultArray; +} + +void CharsetDetector::setDetectableCharset(const char *encoding, UBool enabled, UErrorCode &status) +{ + if (U_FAILURE(status)) { + return; + } + + int32_t modIdx = -1; + UBool isDefaultVal = false; + for (int32_t i = 0; i < fCSRecognizers_size; i++) { + CSRecognizerInfo *csrinfo = fCSRecognizers[i]; + if (uprv_strcmp(csrinfo->recognizer->getName(), encoding) == 0) { + modIdx = i; + isDefaultVal = (csrinfo->isDefaultEnabled == enabled); + break; + } + } + if (modIdx < 0) { + // No matching encoding found + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + if (fEnabledRecognizers == nullptr && !isDefaultVal) { + // Create an array storing the non default setting + fEnabledRecognizers = NEW_ARRAY(UBool, fCSRecognizers_size); + if (fEnabledRecognizers == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + // Initialize the array with default info + for (int32_t i = 0; i < fCSRecognizers_size; i++) { + fEnabledRecognizers[i] = fCSRecognizers[i]->isDefaultEnabled; + } + } + + if (fEnabledRecognizers != nullptr) { + fEnabledRecognizers[modIdx] = enabled; + } +} + +/*const char *CharsetDetector::getCharsetName(int32_t index, UErrorCode &status) const +{ + if( index > fCSRecognizers_size-1 || index < 0) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + + return 0; + } else { + return fCSRecognizers[index]->getName(); + } +}*/ + +U_NAMESPACE_END + +U_CDECL_BEGIN +typedef struct { + int32_t currIndex; + UBool all; + UBool *enabledRecognizers; +} Context; + + + +static void U_CALLCONV +enumClose(UEnumeration *en) { + if(en->context != nullptr) { + DELETE_ARRAY(en->context); + } + + DELETE_ARRAY(en); +} + +static int32_t U_CALLCONV +enumCount(UEnumeration *en, UErrorCode *) { + if (((Context *)en->context)->all) { + // ucsdet_getAllDetectableCharsets, all charset detector names + return fCSRecognizers_size; + } + + // Otherwise, ucsdet_getDetectableCharsets - only enabled ones + int32_t count = 0; + UBool *enabledArray = ((Context *)en->context)->enabledRecognizers; + if (enabledArray != nullptr) { + // custom set + for (int32_t i = 0; i < fCSRecognizers_size; i++) { + if (enabledArray[i]) { + count++; + } + } + } else { + // default set + for (int32_t i = 0; i < fCSRecognizers_size; i++) { + if (fCSRecognizers[i]->isDefaultEnabled) { + count++; + } + } + } + return count; +} + +static const char* U_CALLCONV +enumNext(UEnumeration *en, int32_t *resultLength, UErrorCode * /*status*/) { + const char *currName = nullptr; + + if (((Context *)en->context)->currIndex < fCSRecognizers_size) { + if (((Context *)en->context)->all) { + // ucsdet_getAllDetectableCharsets, all charset detector names + currName = fCSRecognizers[((Context *)en->context)->currIndex]->recognizer->getName(); + ((Context *)en->context)->currIndex++; + } else { + // ucsdet_getDetectableCharsets + UBool *enabledArray = ((Context *)en->context)->enabledRecognizers; + if (enabledArray != nullptr) { + // custom set + while (currName == nullptr && ((Context *)en->context)->currIndex < fCSRecognizers_size) { + if (enabledArray[((Context *)en->context)->currIndex]) { + currName = fCSRecognizers[((Context *)en->context)->currIndex]->recognizer->getName(); + } + ((Context *)en->context)->currIndex++; + } + } else { + // default set + while (currName == nullptr && ((Context *)en->context)->currIndex < fCSRecognizers_size) { + if (fCSRecognizers[((Context *)en->context)->currIndex]->isDefaultEnabled) { + currName = fCSRecognizers[((Context *)en->context)->currIndex]->recognizer->getName(); + } + ((Context *)en->context)->currIndex++; + } + } + } + } + + if(resultLength != nullptr) { + *resultLength = currName == nullptr ? 0 : (int32_t)uprv_strlen(currName); + } + + return currName; +} + + +static void U_CALLCONV +enumReset(UEnumeration *en, UErrorCode *) { + ((Context *)en->context)->currIndex = 0; +} + +static const UEnumeration gCSDetEnumeration = { + nullptr, + nullptr, + enumClose, + enumCount, + uenum_unextDefault, + enumNext, + enumReset +}; + +U_CDECL_END + +U_NAMESPACE_BEGIN + +UEnumeration * CharsetDetector::getAllDetectableCharsets(UErrorCode &status) +{ + + /* Initialize recognized charsets. */ + setRecognizers(status); + + if(U_FAILURE(status)) { + return 0; + } + + UEnumeration *en = NEW_ARRAY(UEnumeration, 1); + if (en == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + memcpy(en, &gCSDetEnumeration, sizeof(UEnumeration)); + en->context = (void*)NEW_ARRAY(Context, 1); + if (en->context == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + DELETE_ARRAY(en); + return 0; + } + uprv_memset(en->context, 0, sizeof(Context)); + ((Context*)en->context)->all = true; + return en; +} + +UEnumeration * CharsetDetector::getDetectableCharsets(UErrorCode &status) const +{ + if(U_FAILURE(status)) { + return 0; + } + + UEnumeration *en = NEW_ARRAY(UEnumeration, 1); + if (en == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + memcpy(en, &gCSDetEnumeration, sizeof(UEnumeration)); + en->context = (void*)NEW_ARRAY(Context, 1); + if (en->context == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + DELETE_ARRAY(en); + return 0; + } + uprv_memset(en->context, 0, sizeof(Context)); + ((Context*)en->context)->all = false; + ((Context*)en->context)->enabledRecognizers = fEnabledRecognizers; + return en; +} + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/i18n/csdetect.h b/deps/icu-small/source/i18n/csdetect.h index d4bfa75eef25fe..56d9224464ab01 100644 --- a/deps/icu-small/source/i18n/csdetect.h +++ b/deps/icu-small/source/i18n/csdetect.h @@ -1,69 +1,69 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2016, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#ifndef __CSDETECT_H -#define __CSDETECT_H - -#include "unicode/uobject.h" - -#if !UCONFIG_NO_CONVERSION - -#include "unicode/uenum.h" - -U_NAMESPACE_BEGIN - -class InputText; -class CharsetRecognizer; -class CharsetMatch; - -class CharsetDetector : public UMemory -{ -private: - InputText *textIn; - CharsetMatch **resultArray; - int32_t resultCount; - UBool fStripTags; // If true, setText() will strip tags from input text. - UBool fFreshTextSet; - static void setRecognizers(UErrorCode &status); - - UBool *fEnabledRecognizers; // If not null, active set of charset recognizers had - // been changed from the default. The array index is - // corresponding to fCSRecognizers. See setDetectableCharset(). - -public: - CharsetDetector(UErrorCode &status); - - ~CharsetDetector(); - - void setText(const char *in, int32_t len); - - const CharsetMatch * const *detectAll(int32_t &maxMatchesFound, UErrorCode &status); - - const CharsetMatch *detect(UErrorCode& status); - - void setDeclaredEncoding(const char *encoding, int32_t len) const; - - UBool setStripTagsFlag(UBool flag); - - UBool getStripTagsFlag() const; - -// const char *getCharsetName(int32_t index, UErrorCode& status) const; - - static int32_t getDetectableCount(); - - - static UEnumeration * getAllDetectableCharsets(UErrorCode &status); - UEnumeration * getDetectableCharsets(UErrorCode &status) const; - void setDetectableCharset(const char *encoding, UBool enabled, UErrorCode &status); -}; - -U_NAMESPACE_END - -#endif -#endif /* __CSDETECT_H */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2016, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#ifndef __CSDETECT_H +#define __CSDETECT_H + +#include "unicode/uobject.h" + +#if !UCONFIG_NO_CONVERSION + +#include "unicode/uenum.h" + +U_NAMESPACE_BEGIN + +class InputText; +class CharsetRecognizer; +class CharsetMatch; + +class CharsetDetector : public UMemory +{ +private: + InputText *textIn; + CharsetMatch **resultArray; + int32_t resultCount; + UBool fStripTags; // If true, setText() will strip tags from input text. + UBool fFreshTextSet; + static void setRecognizers(UErrorCode &status); + + UBool *fEnabledRecognizers; // If not null, active set of charset recognizers had + // been changed from the default. The array index is + // corresponding to fCSRecognizers. See setDetectableCharset(). + +public: + CharsetDetector(UErrorCode &status); + + ~CharsetDetector(); + + void setText(const char *in, int32_t len); + + const CharsetMatch * const *detectAll(int32_t &maxMatchesFound, UErrorCode &status); + + const CharsetMatch *detect(UErrorCode& status); + + void setDeclaredEncoding(const char *encoding, int32_t len) const; + + UBool setStripTagsFlag(UBool flag); + + UBool getStripTagsFlag() const; + +// const char *getCharsetName(int32_t index, UErrorCode& status) const; + + static int32_t getDetectableCount(); + + + static UEnumeration * getAllDetectableCharsets(UErrorCode &status); + UEnumeration * getDetectableCharsets(UErrorCode &status) const; + void setDetectableCharset(const char *encoding, UBool enabled, UErrorCode &status); +}; + +U_NAMESPACE_END + +#endif +#endif /* __CSDETECT_H */ diff --git a/deps/icu-small/source/i18n/csmatch.cpp b/deps/icu-small/source/i18n/csmatch.cpp index 83bf5316656d6b..588f2a015d8721 100644 --- a/deps/icu-small/source/i18n/csmatch.cpp +++ b/deps/icu-small/source/i18n/csmatch.cpp @@ -1,73 +1,73 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2012, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION -#include "unicode/unistr.h" -#include "unicode/ucnv.h" - -#include "csmatch.h" - -#include "csrecog.h" -#include "inputext.h" - -U_NAMESPACE_BEGIN - -CharsetMatch::CharsetMatch() - : textIn(NULL), confidence(0), fCharsetName(NULL), fLang(NULL) -{ - // nothing else to do. -} - -void CharsetMatch::set(InputText *input, const CharsetRecognizer *cr, int32_t conf, - const char *csName, const char *lang) -{ - textIn = input; - confidence = conf; - fCharsetName = csName; - fLang = lang; - if (cr != NULL) { - if (fCharsetName == NULL) { - fCharsetName = cr->getName(); - } - if (fLang == NULL) { - fLang = cr->getLanguage(); - } - } -} - -const char* CharsetMatch::getName()const -{ - return fCharsetName; -} - -const char* CharsetMatch::getLanguage()const -{ - return fLang; -} - -int32_t CharsetMatch::getConfidence()const -{ - return confidence; -} - -int32_t CharsetMatch::getUChars(UChar *buf, int32_t cap, UErrorCode *status) const -{ - UConverter *conv = ucnv_open(getName(), status); - int32_t result = ucnv_toUChars(conv, buf, cap, (const char *) textIn->fRawInput, textIn->fRawLength, status); - - ucnv_close(conv); - - return result; -} - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2012, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION +#include "unicode/unistr.h" +#include "unicode/ucnv.h" + +#include "csmatch.h" + +#include "csrecog.h" +#include "inputext.h" + +U_NAMESPACE_BEGIN + +CharsetMatch::CharsetMatch() + : textIn(nullptr), confidence(0), fCharsetName(nullptr), fLang(nullptr) +{ + // nothing else to do. +} + +void CharsetMatch::set(InputText *input, const CharsetRecognizer *cr, int32_t conf, + const char *csName, const char *lang) +{ + textIn = input; + confidence = conf; + fCharsetName = csName; + fLang = lang; + if (cr != nullptr) { + if (fCharsetName == nullptr) { + fCharsetName = cr->getName(); + } + if (fLang == nullptr) { + fLang = cr->getLanguage(); + } + } +} + +const char* CharsetMatch::getName()const +{ + return fCharsetName; +} + +const char* CharsetMatch::getLanguage()const +{ + return fLang; +} + +int32_t CharsetMatch::getConfidence()const +{ + return confidence; +} + +int32_t CharsetMatch::getUChars(char16_t *buf, int32_t cap, UErrorCode *status) const +{ + UConverter *conv = ucnv_open(getName(), status); + int32_t result = ucnv_toUChars(conv, buf, cap, (const char *) textIn->fRawInput, textIn->fRawLength, status); + + ucnv_close(conv); + + return result; +} + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/i18n/csmatch.h b/deps/icu-small/source/i18n/csmatch.h index fe379ceea7e203..b336042ceb80f3 100644 --- a/deps/icu-small/source/i18n/csmatch.h +++ b/deps/icu-small/source/i18n/csmatch.h @@ -1,71 +1,71 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2012, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#ifndef __CSMATCH_H -#define __CSMATCH_H - -#include "unicode/uobject.h" - -#if !UCONFIG_NO_CONVERSION - -U_NAMESPACE_BEGIN - -class InputText; -class CharsetRecognizer; - -/* - * CharsetMatch represents the results produced by one Charset Recognizer for one input text - * Any confidence > 0 indicates a possible match, meaning that the input bytes - * are at least legal. - * - * The full results of a detect are represented by an array of these - * CharsetMatch objects, each representing a possible matching charset. - * - * Note that a single charset recognizer may detect multiple closely related - * charsets, and set different names depending on the exact input bytes seen. - */ -class CharsetMatch : public UMemory -{ - private: - InputText *textIn; - int32_t confidence; - const char *fCharsetName; - const char *fLang; - - public: - CharsetMatch(); - - /** - * fully set the state of this CharsetMatch. - * Called by the CharsetRecognizers to record match results. - * Default (NULL) parameters for names will be filled by calling the - * corresponding getters on the recognizer. - */ - void set(InputText *input, - const CharsetRecognizer *cr, - int32_t conf, - const char *csName=NULL, - const char *lang=NULL); - - /** - * Return the name of the charset for this Match - */ - const char *getName() const; - - const char *getLanguage()const; - - int32_t getConfidence()const; - - int32_t getUChars(UChar *buf, int32_t cap, UErrorCode *status) const; -}; - -U_NAMESPACE_END - -#endif -#endif /* __CSMATCH_H */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2012, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#ifndef __CSMATCH_H +#define __CSMATCH_H + +#include "unicode/uobject.h" + +#if !UCONFIG_NO_CONVERSION + +U_NAMESPACE_BEGIN + +class InputText; +class CharsetRecognizer; + +/* + * CharsetMatch represents the results produced by one Charset Recognizer for one input text + * Any confidence > 0 indicates a possible match, meaning that the input bytes + * are at least legal. + * + * The full results of a detect are represented by an array of these + * CharsetMatch objects, each representing a possible matching charset. + * + * Note that a single charset recognizer may detect multiple closely related + * charsets, and set different names depending on the exact input bytes seen. + */ +class CharsetMatch : public UMemory +{ + private: + InputText *textIn; + int32_t confidence; + const char *fCharsetName; + const char *fLang; + + public: + CharsetMatch(); + + /** + * fully set the state of this CharsetMatch. + * Called by the CharsetRecognizers to record match results. + * Default (nullptr) parameters for names will be filled by calling the + * corresponding getters on the recognizer. + */ + void set(InputText *input, + const CharsetRecognizer *cr, + int32_t conf, + const char *csName=nullptr, + const char *lang=nullptr); + + /** + * Return the name of the charset for this Match + */ + const char *getName() const; + + const char *getLanguage()const; + + int32_t getConfidence()const; + + int32_t getUChars(char16_t *buf, int32_t cap, UErrorCode *status) const; +}; + +U_NAMESPACE_END + +#endif +#endif /* __CSMATCH_H */ diff --git a/deps/icu-small/source/i18n/csr2022.cpp b/deps/icu-small/source/i18n/csr2022.cpp index e064c426a2a26c..1fd37b150c0ff5 100644 --- a/deps/icu-small/source/i18n/csr2022.cpp +++ b/deps/icu-small/source/i18n/csr2022.cpp @@ -1,195 +1,195 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2016, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "cmemory.h" -#include "cstring.h" - -#include "csr2022.h" -#include "csmatch.h" - -U_NAMESPACE_BEGIN - -/** - * Matching function shared among the 2022 detectors JP, CN and KR - * Counts up the number of legal and unrecognized escape sequences in - * the sample of text, and computes a score based on the total number & - * the proportion that fit the encoding. - * - * - * @param text the byte buffer containing text to analyse - * @param textLen the size of the text in the byte. - * @param escapeSequences the byte escape sequences to test for. - * @return match quality, in the range of 0-100. - */ -int32_t CharsetRecog_2022::match_2022(const uint8_t *text, int32_t textLen, const uint8_t escapeSequences[][5], int32_t escapeSequences_length) const -{ - int32_t i, j; - int32_t escN; - int32_t hits = 0; - int32_t misses = 0; - int32_t shifts = 0; - int32_t quality; - - i = 0; - while(i < textLen) { - if(text[i] == 0x1B) { - escN = 0; - while(escN < escapeSequences_length) { - const uint8_t *seq = escapeSequences[escN]; - int32_t seq_length = (int32_t)uprv_strlen((const char *) seq); - - if (textLen-i >= seq_length) { - j = 1; - while(j < seq_length) { - if(seq[j] != text[i+j]) { - goto checkEscapes; - } - - j += 1; - } - - hits += 1; - i += seq_length-1; - goto scanInput; - } - // else we ran out of string to compare this time. -checkEscapes: - escN += 1; - } - - misses += 1; - } - - if( text[i]== 0x0e || text[i] == 0x0f){ - shifts += 1; - } - -scanInput: - i += 1; - } - - if (hits == 0) { - return 0; - } - - // - // Initial quality is based on relative proportion of recognized vs. - // unrecognized escape sequences. - // All good: quality = 100; - // half or less good: quality = 0; - // linear inbetween. - quality = (100*hits - 100*misses) / (hits + misses); - - // Back off quality if there were too few escape sequences seen. - // Include shifts in this computation, so that KR does not get penalized - // for having only a single Escape sequence, but many shifts. - if (hits+shifts < 5) { - quality -= (5-(hits+shifts))*10; - } - - if (quality < 0) { - quality = 0; - } - - return quality; -} - - -static const uint8_t escapeSequences_2022JP[][5] = { - {0x1b, 0x24, 0x28, 0x43, 0x00}, // KS X 1001:1992 - {0x1b, 0x24, 0x28, 0x44, 0x00}, // JIS X 212-1990 - {0x1b, 0x24, 0x40, 0x00, 0x00}, // JIS C 6226-1978 - {0x1b, 0x24, 0x41, 0x00, 0x00}, // GB 2312-80 - {0x1b, 0x24, 0x42, 0x00, 0x00}, // JIS X 208-1983 - {0x1b, 0x26, 0x40, 0x00, 0x00}, // JIS X 208 1990, 1997 - {0x1b, 0x28, 0x42, 0x00, 0x00}, // ASCII - {0x1b, 0x28, 0x48, 0x00, 0x00}, // JIS-Roman - {0x1b, 0x28, 0x49, 0x00, 0x00}, // Half-width katakana - {0x1b, 0x28, 0x4a, 0x00, 0x00}, // JIS-Roman - {0x1b, 0x2e, 0x41, 0x00, 0x00}, // ISO 8859-1 - {0x1b, 0x2e, 0x46, 0x00, 0x00} // ISO 8859-7 -}; - -#if !UCONFIG_ONLY_HTML_CONVERSION -static const uint8_t escapeSequences_2022KR[][5] = { - {0x1b, 0x24, 0x29, 0x43, 0x00} -}; - -static const uint8_t escapeSequences_2022CN[][5] = { - {0x1b, 0x24, 0x29, 0x41, 0x00}, // GB 2312-80 - {0x1b, 0x24, 0x29, 0x47, 0x00}, // CNS 11643-1992 Plane 1 - {0x1b, 0x24, 0x2A, 0x48, 0x00}, // CNS 11643-1992 Plane 2 - {0x1b, 0x24, 0x29, 0x45, 0x00}, // ISO-IR-165 - {0x1b, 0x24, 0x2B, 0x49, 0x00}, // CNS 11643-1992 Plane 3 - {0x1b, 0x24, 0x2B, 0x4A, 0x00}, // CNS 11643-1992 Plane 4 - {0x1b, 0x24, 0x2B, 0x4B, 0x00}, // CNS 11643-1992 Plane 5 - {0x1b, 0x24, 0x2B, 0x4C, 0x00}, // CNS 11643-1992 Plane 6 - {0x1b, 0x24, 0x2B, 0x4D, 0x00}, // CNS 11643-1992 Plane 7 - {0x1b, 0x4e, 0x00, 0x00, 0x00}, // SS2 - {0x1b, 0x4f, 0x00, 0x00, 0x00}, // SS3 -}; -#endif - -CharsetRecog_2022JP::~CharsetRecog_2022JP() {} - -const char *CharsetRecog_2022JP::getName() const { - return "ISO-2022-JP"; -} - -UBool CharsetRecog_2022JP::match(InputText *textIn, CharsetMatch *results) const { - int32_t confidence = match_2022(textIn->fInputBytes, - textIn->fInputLen, - escapeSequences_2022JP, - UPRV_LENGTHOF(escapeSequences_2022JP)); - results->set(textIn, this, confidence); - return (confidence > 0); -} - -#if !UCONFIG_ONLY_HTML_CONVERSION -CharsetRecog_2022KR::~CharsetRecog_2022KR() {} - -const char *CharsetRecog_2022KR::getName() const { - return "ISO-2022-KR"; -} - -UBool CharsetRecog_2022KR::match(InputText *textIn, CharsetMatch *results) const { - int32_t confidence = match_2022(textIn->fInputBytes, - textIn->fInputLen, - escapeSequences_2022KR, - UPRV_LENGTHOF(escapeSequences_2022KR)); - results->set(textIn, this, confidence); - return (confidence > 0); -} - -CharsetRecog_2022CN::~CharsetRecog_2022CN() {} - -const char *CharsetRecog_2022CN::getName() const { - return "ISO-2022-CN"; -} - -UBool CharsetRecog_2022CN::match(InputText *textIn, CharsetMatch *results) const { - int32_t confidence = match_2022(textIn->fInputBytes, - textIn->fInputLen, - escapeSequences_2022CN, - UPRV_LENGTHOF(escapeSequences_2022CN)); - results->set(textIn, this, confidence); - return (confidence > 0); -} -#endif - -CharsetRecog_2022::~CharsetRecog_2022() { - // nothing to do -} - -U_NAMESPACE_END -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2016, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "cmemory.h" +#include "cstring.h" + +#include "csr2022.h" +#include "csmatch.h" + +U_NAMESPACE_BEGIN + +/** + * Matching function shared among the 2022 detectors JP, CN and KR + * Counts up the number of legal and unrecognized escape sequences in + * the sample of text, and computes a score based on the total number & + * the proportion that fit the encoding. + * + * + * @param text the byte buffer containing text to analyse + * @param textLen the size of the text in the byte. + * @param escapeSequences the byte escape sequences to test for. + * @return match quality, in the range of 0-100. + */ +int32_t CharsetRecog_2022::match_2022(const uint8_t *text, int32_t textLen, const uint8_t escapeSequences[][5], int32_t escapeSequences_length) const +{ + int32_t i, j; + int32_t escN; + int32_t hits = 0; + int32_t misses = 0; + int32_t shifts = 0; + int32_t quality; + + i = 0; + while(i < textLen) { + if(text[i] == 0x1B) { + escN = 0; + while(escN < escapeSequences_length) { + const uint8_t *seq = escapeSequences[escN]; + int32_t seq_length = (int32_t)uprv_strlen((const char *) seq); + + if (textLen-i >= seq_length) { + j = 1; + while(j < seq_length) { + if(seq[j] != text[i+j]) { + goto checkEscapes; + } + + j += 1; + } + + hits += 1; + i += seq_length-1; + goto scanInput; + } + // else we ran out of string to compare this time. +checkEscapes: + escN += 1; + } + + misses += 1; + } + + if( text[i]== 0x0e || text[i] == 0x0f){ + shifts += 1; + } + +scanInput: + i += 1; + } + + if (hits == 0) { + return 0; + } + + // + // Initial quality is based on relative proportion of recognized vs. + // unrecognized escape sequences. + // All good: quality = 100; + // half or less good: quality = 0; + // linear inbetween. + quality = (100*hits - 100*misses) / (hits + misses); + + // Back off quality if there were too few escape sequences seen. + // Include shifts in this computation, so that KR does not get penalized + // for having only a single Escape sequence, but many shifts. + if (hits+shifts < 5) { + quality -= (5-(hits+shifts))*10; + } + + if (quality < 0) { + quality = 0; + } + + return quality; +} + + +static const uint8_t escapeSequences_2022JP[][5] = { + {0x1b, 0x24, 0x28, 0x43, 0x00}, // KS X 1001:1992 + {0x1b, 0x24, 0x28, 0x44, 0x00}, // JIS X 212-1990 + {0x1b, 0x24, 0x40, 0x00, 0x00}, // JIS C 6226-1978 + {0x1b, 0x24, 0x41, 0x00, 0x00}, // GB 2312-80 + {0x1b, 0x24, 0x42, 0x00, 0x00}, // JIS X 208-1983 + {0x1b, 0x26, 0x40, 0x00, 0x00}, // JIS X 208 1990, 1997 + {0x1b, 0x28, 0x42, 0x00, 0x00}, // ASCII + {0x1b, 0x28, 0x48, 0x00, 0x00}, // JIS-Roman + {0x1b, 0x28, 0x49, 0x00, 0x00}, // Half-width katakana + {0x1b, 0x28, 0x4a, 0x00, 0x00}, // JIS-Roman + {0x1b, 0x2e, 0x41, 0x00, 0x00}, // ISO 8859-1 + {0x1b, 0x2e, 0x46, 0x00, 0x00} // ISO 8859-7 +}; + +#if !UCONFIG_ONLY_HTML_CONVERSION +static const uint8_t escapeSequences_2022KR[][5] = { + {0x1b, 0x24, 0x29, 0x43, 0x00} +}; + +static const uint8_t escapeSequences_2022CN[][5] = { + {0x1b, 0x24, 0x29, 0x41, 0x00}, // GB 2312-80 + {0x1b, 0x24, 0x29, 0x47, 0x00}, // CNS 11643-1992 Plane 1 + {0x1b, 0x24, 0x2A, 0x48, 0x00}, // CNS 11643-1992 Plane 2 + {0x1b, 0x24, 0x29, 0x45, 0x00}, // ISO-IR-165 + {0x1b, 0x24, 0x2B, 0x49, 0x00}, // CNS 11643-1992 Plane 3 + {0x1b, 0x24, 0x2B, 0x4A, 0x00}, // CNS 11643-1992 Plane 4 + {0x1b, 0x24, 0x2B, 0x4B, 0x00}, // CNS 11643-1992 Plane 5 + {0x1b, 0x24, 0x2B, 0x4C, 0x00}, // CNS 11643-1992 Plane 6 + {0x1b, 0x24, 0x2B, 0x4D, 0x00}, // CNS 11643-1992 Plane 7 + {0x1b, 0x4e, 0x00, 0x00, 0x00}, // SS2 + {0x1b, 0x4f, 0x00, 0x00, 0x00}, // SS3 +}; +#endif + +CharsetRecog_2022JP::~CharsetRecog_2022JP() {} + +const char *CharsetRecog_2022JP::getName() const { + return "ISO-2022-JP"; +} + +UBool CharsetRecog_2022JP::match(InputText *textIn, CharsetMatch *results) const { + int32_t confidence = match_2022(textIn->fInputBytes, + textIn->fInputLen, + escapeSequences_2022JP, + UPRV_LENGTHOF(escapeSequences_2022JP)); + results->set(textIn, this, confidence); + return (confidence > 0); +} + +#if !UCONFIG_ONLY_HTML_CONVERSION +CharsetRecog_2022KR::~CharsetRecog_2022KR() {} + +const char *CharsetRecog_2022KR::getName() const { + return "ISO-2022-KR"; +} + +UBool CharsetRecog_2022KR::match(InputText *textIn, CharsetMatch *results) const { + int32_t confidence = match_2022(textIn->fInputBytes, + textIn->fInputLen, + escapeSequences_2022KR, + UPRV_LENGTHOF(escapeSequences_2022KR)); + results->set(textIn, this, confidence); + return (confidence > 0); +} + +CharsetRecog_2022CN::~CharsetRecog_2022CN() {} + +const char *CharsetRecog_2022CN::getName() const { + return "ISO-2022-CN"; +} + +UBool CharsetRecog_2022CN::match(InputText *textIn, CharsetMatch *results) const { + int32_t confidence = match_2022(textIn->fInputBytes, + textIn->fInputLen, + escapeSequences_2022CN, + UPRV_LENGTHOF(escapeSequences_2022CN)); + results->set(textIn, this, confidence); + return (confidence > 0); +} +#endif + +CharsetRecog_2022::~CharsetRecog_2022() { + // nothing to do +} + +U_NAMESPACE_END +#endif diff --git a/deps/icu-small/source/i18n/csr2022.h b/deps/icu-small/source/i18n/csr2022.h index 4418728f0ec05a..15f9892d81ca05 100644 --- a/deps/icu-small/source/i18n/csr2022.h +++ b/deps/icu-small/source/i18n/csr2022.h @@ -1,95 +1,95 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2015, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#ifndef __CSR2022_H -#define __CSR2022_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "csrecog.h" - -U_NAMESPACE_BEGIN - -class CharsetMatch; - -/** - * class CharsetRecog_2022 part of the ICU charset detection implementation. - * This is a superclass for the individual detectors for - * each of the detectable members of the ISO 2022 family - * of encodings. - * - * The separate classes are nested within this class. - * - * @internal - */ -class CharsetRecog_2022 : public CharsetRecognizer -{ - -public: - virtual ~CharsetRecog_2022() = 0; - -protected: - - /** - * Matching function shared among the 2022 detectors JP, CN and KR - * Counts up the number of legal an unrecognized escape sequences in - * the sample of text, and computes a score based on the total number & - * the proportion that fit the encoding. - * - * - * @param text the byte buffer containing text to analyse - * @param textLen the size of the text in the byte. - * @param escapeSequences the byte escape sequences to test for. - * @return match quality, in the range of 0-100. - */ - int32_t match_2022(const uint8_t *text, - int32_t textLen, - const uint8_t escapeSequences[][5], - int32_t escapeSequences_length) const; - -}; - -class CharsetRecog_2022JP :public CharsetRecog_2022 -{ -public: - virtual ~CharsetRecog_2022JP(); - - const char *getName() const override; - - UBool match(InputText *textIn, CharsetMatch *results) const override; -}; - -#if !UCONFIG_ONLY_HTML_CONVERSION -class CharsetRecog_2022KR :public CharsetRecog_2022 { -public: - virtual ~CharsetRecog_2022KR(); - - const char *getName() const override; - - UBool match(InputText *textIn, CharsetMatch *results) const override; - -}; - -class CharsetRecog_2022CN :public CharsetRecog_2022 -{ -public: - virtual ~CharsetRecog_2022CN(); - - const char* getName() const override; - - UBool match(InputText *textIn, CharsetMatch *results) const override; -}; -#endif - -U_NAMESPACE_END - -#endif -#endif /* __CSR2022_H */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2015, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#ifndef __CSR2022_H +#define __CSR2022_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "csrecog.h" + +U_NAMESPACE_BEGIN + +class CharsetMatch; + +/** + * class CharsetRecog_2022 part of the ICU charset detection implementation. + * This is a superclass for the individual detectors for + * each of the detectable members of the ISO 2022 family + * of encodings. + * + * The separate classes are nested within this class. + * + * @internal + */ +class CharsetRecog_2022 : public CharsetRecognizer +{ + +public: + virtual ~CharsetRecog_2022() = 0; + +protected: + + /** + * Matching function shared among the 2022 detectors JP, CN and KR + * Counts up the number of legal an unrecognized escape sequences in + * the sample of text, and computes a score based on the total number & + * the proportion that fit the encoding. + * + * + * @param text the byte buffer containing text to analyse + * @param textLen the size of the text in the byte. + * @param escapeSequences the byte escape sequences to test for. + * @return match quality, in the range of 0-100. + */ + int32_t match_2022(const uint8_t *text, + int32_t textLen, + const uint8_t escapeSequences[][5], + int32_t escapeSequences_length) const; + +}; + +class CharsetRecog_2022JP :public CharsetRecog_2022 +{ +public: + virtual ~CharsetRecog_2022JP(); + + const char *getName() const override; + + UBool match(InputText *textIn, CharsetMatch *results) const override; +}; + +#if !UCONFIG_ONLY_HTML_CONVERSION +class CharsetRecog_2022KR :public CharsetRecog_2022 { +public: + virtual ~CharsetRecog_2022KR(); + + const char *getName() const override; + + UBool match(InputText *textIn, CharsetMatch *results) const override; + +}; + +class CharsetRecog_2022CN :public CharsetRecog_2022 +{ +public: + virtual ~CharsetRecog_2022CN(); + + const char* getName() const override; + + UBool match(InputText *textIn, CharsetMatch *results) const override; +}; +#endif + +U_NAMESPACE_END + +#endif +#endif /* __CSR2022_H */ diff --git a/deps/icu-small/source/i18n/csrecog.cpp b/deps/icu-small/source/i18n/csrecog.cpp index 31fce5dd012f52..fca34f5ce5a1ce 100644 --- a/deps/icu-small/source/i18n/csrecog.cpp +++ b/deps/icu-small/source/i18n/csrecog.cpp @@ -1,30 +1,30 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2006, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "csrecog.h" - -U_NAMESPACE_BEGIN - -CharsetRecognizer::~CharsetRecognizer() -{ - // nothing to do. -} - -const char *CharsetRecognizer::getLanguage() const -{ - return ""; -} - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2006, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "csrecog.h" + +U_NAMESPACE_BEGIN + +CharsetRecognizer::~CharsetRecognizer() +{ + // nothing to do. +} + +const char *CharsetRecognizer::getLanguage() const +{ + return ""; +} + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/i18n/csrecog.h b/deps/icu-small/source/i18n/csrecog.h index 944a5007fe5f9a..021cd6f4a23d31 100644 --- a/deps/icu-small/source/i18n/csrecog.h +++ b/deps/icu-small/source/i18n/csrecog.h @@ -1,57 +1,57 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2012, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#ifndef __CSRECOG_H -#define __CSRECOG_H - -#include "unicode/uobject.h" - -#if !UCONFIG_NO_CONVERSION - -#include "inputext.h" - -U_NAMESPACE_BEGIN - -class CharsetMatch; - -class CharsetRecognizer : public UMemory -{ - public: - /** - * Get the IANA name of this charset. - * Note that some recognizers can recognize more than one charset, but that this API - * assumes just one name per recognizer. - * TODO: need to account for multiple names in public API that enumerates over the - * known detectable charsets. - * @return the charset name. - */ - virtual const char *getName() const = 0; - - /** - * Get the ISO language code for this charset. - * @return the language code, or null if the language cannot be determined. - */ - virtual const char *getLanguage() const; - - /* - * Try the given input text against this Charset, and fill in the results object - * with the quality of the match plus other information related to the match. - * - * Return true if the the input bytes are a potential match, and - * false if the input data is not compatible with, or illegal in this charset. - */ - virtual UBool match(InputText *textIn, CharsetMatch *results) const = 0; - - virtual ~CharsetRecognizer(); -}; - -U_NAMESPACE_END - -#endif -#endif /* __CSRECOG_H */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2012, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#ifndef __CSRECOG_H +#define __CSRECOG_H + +#include "unicode/uobject.h" + +#if !UCONFIG_NO_CONVERSION + +#include "inputext.h" + +U_NAMESPACE_BEGIN + +class CharsetMatch; + +class CharsetRecognizer : public UMemory +{ + public: + /** + * Get the IANA name of this charset. + * Note that some recognizers can recognize more than one charset, but that this API + * assumes just one name per recognizer. + * TODO: need to account for multiple names in public API that enumerates over the + * known detectable charsets. + * @return the charset name. + */ + virtual const char *getName() const = 0; + + /** + * Get the ISO language code for this charset. + * @return the language code, or null if the language cannot be determined. + */ + virtual const char *getLanguage() const; + + /* + * Try the given input text against this Charset, and fill in the results object + * with the quality of the match plus other information related to the match. + * + * Return true if the the input bytes are a potential match, and + * false if the input data is not compatible with, or illegal in this charset. + */ + virtual UBool match(InputText *textIn, CharsetMatch *results) const = 0; + + virtual ~CharsetRecognizer(); +}; + +U_NAMESPACE_END + +#endif +#endif /* __CSRECOG_H */ diff --git a/deps/icu-small/source/i18n/csrmbcs.cpp b/deps/icu-small/source/i18n/csrmbcs.cpp index ec346b5fb3f4c4..ddf56d035c866b 100644 --- a/deps/icu-small/source/i18n/csrmbcs.cpp +++ b/deps/icu-small/source/i18n/csrmbcs.cpp @@ -1,527 +1,527 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2016, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "cmemory.h" -#include "csmatch.h" -#include "csrmbcs.h" - -#include - -U_NAMESPACE_BEGIN - -#define min(x,y) (((x)<(y))?(x):(y)) - -static const uint16_t commonChars_sjis [] = { -// TODO: This set of data comes from the character frequency- -// of-occurrence analysis tool. The data needs to be moved -// into a resource and loaded from there. -0x8140, 0x8141, 0x8142, 0x8145, 0x815b, 0x8169, 0x816a, 0x8175, 0x8176, 0x82a0, -0x82a2, 0x82a4, 0x82a9, 0x82aa, 0x82ab, 0x82ad, 0x82af, 0x82b1, 0x82b3, 0x82b5, -0x82b7, 0x82bd, 0x82be, 0x82c1, 0x82c4, 0x82c5, 0x82c6, 0x82c8, 0x82c9, 0x82cc, -0x82cd, 0x82dc, 0x82e0, 0x82e7, 0x82e8, 0x82e9, 0x82ea, 0x82f0, 0x82f1, 0x8341, -0x8343, 0x834e, 0x834f, 0x8358, 0x835e, 0x8362, 0x8367, 0x8375, 0x8376, 0x8389, -0x838a, 0x838b, 0x838d, 0x8393, 0x8e96, 0x93fa, 0x95aa}; - -static const uint16_t commonChars_euc_jp[] = { -// TODO: This set of data comes from the character frequency- -// of-occurrence analysis tool. The data needs to be moved -// into a resource and loaded from there. -0xa1a1, 0xa1a2, 0xa1a3, 0xa1a6, 0xa1bc, 0xa1ca, 0xa1cb, 0xa1d6, 0xa1d7, 0xa4a2, -0xa4a4, 0xa4a6, 0xa4a8, 0xa4aa, 0xa4ab, 0xa4ac, 0xa4ad, 0xa4af, 0xa4b1, 0xa4b3, -0xa4b5, 0xa4b7, 0xa4b9, 0xa4bb, 0xa4bd, 0xa4bf, 0xa4c0, 0xa4c1, 0xa4c3, 0xa4c4, -0xa4c6, 0xa4c7, 0xa4c8, 0xa4c9, 0xa4ca, 0xa4cb, 0xa4ce, 0xa4cf, 0xa4d0, 0xa4de, -0xa4df, 0xa4e1, 0xa4e2, 0xa4e4, 0xa4e8, 0xa4e9, 0xa4ea, 0xa4eb, 0xa4ec, 0xa4ef, -0xa4f2, 0xa4f3, 0xa5a2, 0xa5a3, 0xa5a4, 0xa5a6, 0xa5a7, 0xa5aa, 0xa5ad, 0xa5af, -0xa5b0, 0xa5b3, 0xa5b5, 0xa5b7, 0xa5b8, 0xa5b9, 0xa5bf, 0xa5c3, 0xa5c6, 0xa5c7, -0xa5c8, 0xa5c9, 0xa5cb, 0xa5d0, 0xa5d5, 0xa5d6, 0xa5d7, 0xa5de, 0xa5e0, 0xa5e1, -0xa5e5, 0xa5e9, 0xa5ea, 0xa5eb, 0xa5ec, 0xa5ed, 0xa5f3, 0xb8a9, 0xb9d4, 0xbaee, -0xbbc8, 0xbef0, 0xbfb7, 0xc4ea, 0xc6fc, 0xc7bd, 0xcab8, 0xcaf3, 0xcbdc, 0xcdd1}; - -static const uint16_t commonChars_euc_kr[] = { -// TODO: This set of data comes from the character frequency- -// of-occurrence analysis tool. The data needs to be moved -// into a resource and loaded from there. -0xb0a1, 0xb0b3, 0xb0c5, 0xb0cd, 0xb0d4, 0xb0e6, 0xb0ed, 0xb0f8, 0xb0fa, 0xb0fc, -0xb1b8, 0xb1b9, 0xb1c7, 0xb1d7, 0xb1e2, 0xb3aa, 0xb3bb, 0xb4c2, 0xb4cf, 0xb4d9, -0xb4eb, 0xb5a5, 0xb5b5, 0xb5bf, 0xb5c7, 0xb5e9, 0xb6f3, 0xb7af, 0xb7c2, 0xb7ce, -0xb8a6, 0xb8ae, 0xb8b6, 0xb8b8, 0xb8bb, 0xb8e9, 0xb9ab, 0xb9ae, 0xb9cc, 0xb9ce, -0xb9fd, 0xbab8, 0xbace, 0xbad0, 0xbaf1, 0xbbe7, 0xbbf3, 0xbbfd, 0xbcad, 0xbcba, -0xbcd2, 0xbcf6, 0xbdba, 0xbdc0, 0xbdc3, 0xbdc5, 0xbec6, 0xbec8, 0xbedf, 0xbeee, -0xbef8, 0xbefa, 0xbfa1, 0xbfa9, 0xbfc0, 0xbfe4, 0xbfeb, 0xbfec, 0xbff8, 0xc0a7, -0xc0af, 0xc0b8, 0xc0ba, 0xc0bb, 0xc0bd, 0xc0c7, 0xc0cc, 0xc0ce, 0xc0cf, 0xc0d6, -0xc0da, 0xc0e5, 0xc0fb, 0xc0fc, 0xc1a4, 0xc1a6, 0xc1b6, 0xc1d6, 0xc1df, 0xc1f6, -0xc1f8, 0xc4a1, 0xc5cd, 0xc6ae, 0xc7cf, 0xc7d1, 0xc7d2, 0xc7d8, 0xc7e5, 0xc8ad}; - -static const uint16_t commonChars_big5[] = { -// TODO: This set of data comes from the character frequency- -// of-occurrence analysis tool. The data needs to be moved -// into a resource and loaded from there. -0xa140, 0xa141, 0xa142, 0xa143, 0xa147, 0xa149, 0xa175, 0xa176, 0xa440, 0xa446, -0xa447, 0xa448, 0xa451, 0xa454, 0xa457, 0xa464, 0xa46a, 0xa46c, 0xa477, 0xa4a3, -0xa4a4, 0xa4a7, 0xa4c1, 0xa4ce, 0xa4d1, 0xa4df, 0xa4e8, 0xa4fd, 0xa540, 0xa548, -0xa558, 0xa569, 0xa5cd, 0xa5e7, 0xa657, 0xa661, 0xa662, 0xa668, 0xa670, 0xa6a8, -0xa6b3, 0xa6b9, 0xa6d3, 0xa6db, 0xa6e6, 0xa6f2, 0xa740, 0xa751, 0xa759, 0xa7da, -0xa8a3, 0xa8a5, 0xa8ad, 0xa8d1, 0xa8d3, 0xa8e4, 0xa8fc, 0xa9c0, 0xa9d2, 0xa9f3, -0xaa6b, 0xaaba, 0xaabe, 0xaacc, 0xaafc, 0xac47, 0xac4f, 0xacb0, 0xacd2, 0xad59, -0xaec9, 0xafe0, 0xb0ea, 0xb16f, 0xb2b3, 0xb2c4, 0xb36f, 0xb44c, 0xb44e, 0xb54c, -0xb5a5, 0xb5bd, 0xb5d0, 0xb5d8, 0xb671, 0xb7ed, 0xb867, 0xb944, 0xbad8, 0xbb44, -0xbba1, 0xbdd1, 0xc2c4, 0xc3b9, 0xc440, 0xc45f}; - -static const uint16_t commonChars_gb_18030[] = { -// TODO: This set of data comes from the character frequency- -// of-occurrence analysis tool. The data needs to be moved -// into a resource and loaded from there. -0xa1a1, 0xa1a2, 0xa1a3, 0xa1a4, 0xa1b0, 0xa1b1, 0xa1f1, 0xa1f3, 0xa3a1, 0xa3ac, -0xa3ba, 0xb1a8, 0xb1b8, 0xb1be, 0xb2bb, 0xb3c9, 0xb3f6, 0xb4f3, 0xb5bd, 0xb5c4, -0xb5e3, 0xb6af, 0xb6d4, 0xb6e0, 0xb7a2, 0xb7a8, 0xb7bd, 0xb7d6, 0xb7dd, 0xb8b4, -0xb8df, 0xb8f6, 0xb9ab, 0xb9c9, 0xb9d8, 0xb9fa, 0xb9fd, 0xbacd, 0xbba7, 0xbbd6, -0xbbe1, 0xbbfa, 0xbcbc, 0xbcdb, 0xbcfe, 0xbdcc, 0xbecd, 0xbedd, 0xbfb4, 0xbfc6, -0xbfc9, 0xc0b4, 0xc0ed, 0xc1cb, 0xc2db, 0xc3c7, 0xc4dc, 0xc4ea, 0xc5cc, 0xc6f7, -0xc7f8, 0xc8ab, 0xc8cb, 0xc8d5, 0xc8e7, 0xc9cf, 0xc9fa, 0xcab1, 0xcab5, 0xcac7, -0xcad0, 0xcad6, 0xcaf5, 0xcafd, 0xccec, 0xcdf8, 0xceaa, 0xcec4, 0xced2, 0xcee5, -0xcfb5, 0xcfc2, 0xcfd6, 0xd0c2, 0xd0c5, 0xd0d0, 0xd0d4, 0xd1a7, 0xd2aa, 0xd2b2, -0xd2b5, 0xd2bb, 0xd2d4, 0xd3c3, 0xd3d0, 0xd3fd, 0xd4c2, 0xd4da, 0xd5e2, 0xd6d0}; - -static int32_t binarySearch(const uint16_t *array, int32_t len, uint16_t value) -{ - int32_t start = 0, end = len-1; - int32_t mid = (start+end)/2; - - while(start <= end) { - if(array[mid] == value) { - return mid; - } - - if(array[mid] < value){ - start = mid+1; - } else { - end = mid-1; - } - - mid = (start+end)/2; - } - - return -1; -} - -IteratedChar::IteratedChar() : -charValue(0), index(-1), nextIndex(0), error(false), done(false) -{ - // nothing else to do. -} - -/*void IteratedChar::reset() -{ - charValue = 0; - index = -1; - nextIndex = 0; - error = false; - done = false; -}*/ - -int32_t IteratedChar::nextByte(InputText *det) -{ - if (nextIndex >= det->fRawLength) { - done = true; - - return -1; - } - - return det->fRawInput[nextIndex++]; -} - -CharsetRecog_mbcs::~CharsetRecog_mbcs() -{ - // nothing to do. -} - -int32_t CharsetRecog_mbcs::match_mbcs(InputText *det, const uint16_t commonChars[], int32_t commonCharsLen) const { - int32_t doubleByteCharCount = 0; - int32_t commonCharCount = 0; - int32_t badCharCount = 0; - int32_t totalCharCount = 0; - int32_t confidence = 0; - IteratedChar iter; - - while (nextChar(&iter, det)) { - totalCharCount++; - - if (iter.error) { - badCharCount++; - } else { - if (iter.charValue > 0xFF) { - doubleByteCharCount++; - - if (commonChars != 0) { - if (binarySearch(commonChars, commonCharsLen, static_cast(iter.charValue)) >= 0){ - commonCharCount += 1; - } - } - } - } - - - if (badCharCount >= 2 && badCharCount*5 >= doubleByteCharCount) { - // Bail out early if the byte data is not matching the encoding scheme. - // break detectBlock; - return confidence; - } - } - - if (doubleByteCharCount <= 10 && badCharCount == 0) { - // Not many multi-byte chars. - if (doubleByteCharCount == 0 && totalCharCount < 10) { - // There weren't any multibyte sequences, and there was a low density of non-ASCII single bytes. - // We don't have enough data to have any confidence. - // Statistical analysis of single byte non-ASCII characters would probably help here. - confidence = 0; - } - else { - // ASCII or ISO file? It's probably not our encoding, - // but is not incompatible with our encoding, so don't give it a zero. - confidence = 10; - } - - return confidence; - } - - // - // No match if there are too many characters that don't fit the encoding scheme. - // (should we have zero tolerance for these?) - // - if (doubleByteCharCount < 20*badCharCount) { - confidence = 0; - - return confidence; - } - - if (commonChars == 0) { - // We have no statistics on frequently occurring characters. - // Assess confidence purely on having a reasonable number of - // multi-byte characters (the more the better) - confidence = 30 + doubleByteCharCount - 20*badCharCount; - - if (confidence > 100) { - confidence = 100; - } - } else { - // - // Frequency of occurrence statistics exist. - // - - double maxVal = log((double)doubleByteCharCount / 4); /*(float)?*/ - double scaleFactor = 90.0 / maxVal; - confidence = (int32_t)(log((double)commonCharCount+1) * scaleFactor + 10.0); - - confidence = min(confidence, 100); - } - - if (confidence < 0) { - confidence = 0; - } - - return confidence; -} - -CharsetRecog_sjis::~CharsetRecog_sjis() -{ - // nothing to do -} - -UBool CharsetRecog_sjis::nextChar(IteratedChar* it, InputText* det) const { - it->index = it->nextIndex; - it->error = false; - - int32_t firstByte = it->charValue = it->nextByte(det); - - if (firstByte < 0) { - return false; - } - - if (firstByte <= 0x7F || (firstByte > 0xA0 && firstByte <= 0xDF)) { - return true; - } - - int32_t secondByte = it->nextByte(det); - if (secondByte >= 0) { - it->charValue = (firstByte << 8) | secondByte; - } - // else we'll handle the error later. - - if (! ((secondByte >= 0x40 && secondByte <= 0x7F) || (secondByte >= 0x80 && secondByte <= 0xFE))) { - // Illegal second byte value. - it->error = true; - } - - return true; -} - -UBool CharsetRecog_sjis::match(InputText* det, CharsetMatch *results) const { - int32_t confidence = match_mbcs(det, commonChars_sjis, UPRV_LENGTHOF(commonChars_sjis)); - results->set(det, this, confidence); - return (confidence > 0); -} - -const char *CharsetRecog_sjis::getName() const -{ - return "Shift_JIS"; -} - -const char *CharsetRecog_sjis::getLanguage() const -{ - return "ja"; -} - -CharsetRecog_euc::~CharsetRecog_euc() -{ - // nothing to do -} - -UBool CharsetRecog_euc::nextChar(IteratedChar* it, InputText* det) const { - int32_t firstByte = 0; - int32_t secondByte = 0; - int32_t thirdByte = 0; - - it->index = it->nextIndex; - it->error = false; - firstByte = it->charValue = it->nextByte(det); - - if (firstByte < 0) { - // Ran off the end of the input data - return false; - } - - if (firstByte <= 0x8D) { - // single byte char - return true; - } - - secondByte = it->nextByte(det); - if (secondByte >= 0) { - it->charValue = (it->charValue << 8) | secondByte; - } - // else we'll handle the error later. - - if (firstByte >= 0xA1 && firstByte <= 0xFE) { - // Two byte Char - if (secondByte < 0xA1) { - it->error = true; - } - - return true; - } - - if (firstByte == 0x8E) { - // Code Set 2. - // In EUC-JP, total char size is 2 bytes, only one byte of actual char value. - // In EUC-TW, total char size is 4 bytes, three bytes contribute to char value. - // We don't know which we've got. - // Treat it like EUC-JP. If the data really was EUC-TW, the following two - // bytes will look like a well formed 2 byte char. - if (secondByte < 0xA1) { - it->error = true; - } - - return true; - } - - if (firstByte == 0x8F) { - // Code set 3. - // Three byte total char size, two bytes of actual char value. - thirdByte = it->nextByte(det); - it->charValue = (it->charValue << 8) | thirdByte; - - if (thirdByte < 0xa1) { - // Bad second byte or ran off the end of the input data with a non-ASCII first byte. - it->error = true; - } - } - - return true; - -} - -CharsetRecog_euc_jp::~CharsetRecog_euc_jp() -{ - // nothing to do -} - -const char *CharsetRecog_euc_jp::getName() const -{ - return "EUC-JP"; -} - -const char *CharsetRecog_euc_jp::getLanguage() const -{ - return "ja"; -} - -UBool CharsetRecog_euc_jp::match(InputText *det, CharsetMatch *results) const -{ - int32_t confidence = match_mbcs(det, commonChars_euc_jp, UPRV_LENGTHOF(commonChars_euc_jp)); - results->set(det, this, confidence); - return (confidence > 0); -} - -CharsetRecog_euc_kr::~CharsetRecog_euc_kr() -{ - // nothing to do -} - -const char *CharsetRecog_euc_kr::getName() const -{ - return "EUC-KR"; -} - -const char *CharsetRecog_euc_kr::getLanguage() const -{ - return "ko"; -} - -UBool CharsetRecog_euc_kr::match(InputText *det, CharsetMatch *results) const -{ - int32_t confidence = match_mbcs(det, commonChars_euc_kr, UPRV_LENGTHOF(commonChars_euc_kr)); - results->set(det, this, confidence); - return (confidence > 0); -} - -CharsetRecog_big5::~CharsetRecog_big5() -{ - // nothing to do -} - -UBool CharsetRecog_big5::nextChar(IteratedChar* it, InputText* det) const -{ - int32_t firstByte; - - it->index = it->nextIndex; - it->error = false; - firstByte = it->charValue = it->nextByte(det); - - if (firstByte < 0) { - return false; - } - - if (firstByte <= 0x7F || firstByte == 0xFF) { - // single byte character. - return true; - } - - int32_t secondByte = it->nextByte(det); - if (secondByte >= 0) { - it->charValue = (it->charValue << 8) | secondByte; - } - // else we'll handle the error later. - - if (secondByte < 0x40 || secondByte == 0x7F || secondByte == 0xFF) { - it->error = true; - } - - return true; -} - -const char *CharsetRecog_big5::getName() const -{ - return "Big5"; -} - -const char *CharsetRecog_big5::getLanguage() const -{ - return "zh"; -} - -UBool CharsetRecog_big5::match(InputText *det, CharsetMatch *results) const -{ - int32_t confidence = match_mbcs(det, commonChars_big5, UPRV_LENGTHOF(commonChars_big5)); - results->set(det, this, confidence); - return (confidence > 0); -} - -CharsetRecog_gb_18030::~CharsetRecog_gb_18030() -{ - // nothing to do -} - -UBool CharsetRecog_gb_18030::nextChar(IteratedChar* it, InputText* det) const { - int32_t firstByte = 0; - int32_t secondByte = 0; - int32_t thirdByte = 0; - int32_t fourthByte = 0; - - it->index = it->nextIndex; - it->error = false; - firstByte = it->charValue = it->nextByte(det); - - if (firstByte < 0) { - // Ran off the end of the input data - return false; - } - - if (firstByte <= 0x80) { - // single byte char - return true; - } - - secondByte = it->nextByte(det); - if (secondByte >= 0) { - it->charValue = (it->charValue << 8) | secondByte; - } - // else we'll handle the error later. - - if (firstByte >= 0x81 && firstByte <= 0xFE) { - // Two byte Char - if ((secondByte >= 0x40 && secondByte <= 0x7E) || (secondByte >=80 && secondByte <= 0xFE)) { - return true; - } - - // Four byte char - if (secondByte >= 0x30 && secondByte <= 0x39) { - thirdByte = it->nextByte(det); - - if (thirdByte >= 0x81 && thirdByte <= 0xFE) { - fourthByte = it->nextByte(det); - - if (fourthByte >= 0x30 && fourthByte <= 0x39) { - it->charValue = (it->charValue << 16) | (thirdByte << 8) | fourthByte; - - return true; - } - } - } - - // Something wasn't valid, or we ran out of data (-1). - it->error = true; - } - - return true; -} - -const char *CharsetRecog_gb_18030::getName() const -{ - return "GB18030"; -} - -const char *CharsetRecog_gb_18030::getLanguage() const -{ - return "zh"; -} - -UBool CharsetRecog_gb_18030::match(InputText *det, CharsetMatch *results) const -{ - int32_t confidence = match_mbcs(det, commonChars_gb_18030, UPRV_LENGTHOF(commonChars_gb_18030)); - results->set(det, this, confidence); - return (confidence > 0); -} - -U_NAMESPACE_END -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2016, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "cmemory.h" +#include "csmatch.h" +#include "csrmbcs.h" + +#include + +U_NAMESPACE_BEGIN + +#define min(x,y) (((x)<(y))?(x):(y)) + +static const uint16_t commonChars_sjis [] = { +// TODO: This set of data comes from the character frequency- +// of-occurrence analysis tool. The data needs to be moved +// into a resource and loaded from there. +0x8140, 0x8141, 0x8142, 0x8145, 0x815b, 0x8169, 0x816a, 0x8175, 0x8176, 0x82a0, +0x82a2, 0x82a4, 0x82a9, 0x82aa, 0x82ab, 0x82ad, 0x82af, 0x82b1, 0x82b3, 0x82b5, +0x82b7, 0x82bd, 0x82be, 0x82c1, 0x82c4, 0x82c5, 0x82c6, 0x82c8, 0x82c9, 0x82cc, +0x82cd, 0x82dc, 0x82e0, 0x82e7, 0x82e8, 0x82e9, 0x82ea, 0x82f0, 0x82f1, 0x8341, +0x8343, 0x834e, 0x834f, 0x8358, 0x835e, 0x8362, 0x8367, 0x8375, 0x8376, 0x8389, +0x838a, 0x838b, 0x838d, 0x8393, 0x8e96, 0x93fa, 0x95aa}; + +static const uint16_t commonChars_euc_jp[] = { +// TODO: This set of data comes from the character frequency- +// of-occurrence analysis tool. The data needs to be moved +// into a resource and loaded from there. +0xa1a1, 0xa1a2, 0xa1a3, 0xa1a6, 0xa1bc, 0xa1ca, 0xa1cb, 0xa1d6, 0xa1d7, 0xa4a2, +0xa4a4, 0xa4a6, 0xa4a8, 0xa4aa, 0xa4ab, 0xa4ac, 0xa4ad, 0xa4af, 0xa4b1, 0xa4b3, +0xa4b5, 0xa4b7, 0xa4b9, 0xa4bb, 0xa4bd, 0xa4bf, 0xa4c0, 0xa4c1, 0xa4c3, 0xa4c4, +0xa4c6, 0xa4c7, 0xa4c8, 0xa4c9, 0xa4ca, 0xa4cb, 0xa4ce, 0xa4cf, 0xa4d0, 0xa4de, +0xa4df, 0xa4e1, 0xa4e2, 0xa4e4, 0xa4e8, 0xa4e9, 0xa4ea, 0xa4eb, 0xa4ec, 0xa4ef, +0xa4f2, 0xa4f3, 0xa5a2, 0xa5a3, 0xa5a4, 0xa5a6, 0xa5a7, 0xa5aa, 0xa5ad, 0xa5af, +0xa5b0, 0xa5b3, 0xa5b5, 0xa5b7, 0xa5b8, 0xa5b9, 0xa5bf, 0xa5c3, 0xa5c6, 0xa5c7, +0xa5c8, 0xa5c9, 0xa5cb, 0xa5d0, 0xa5d5, 0xa5d6, 0xa5d7, 0xa5de, 0xa5e0, 0xa5e1, +0xa5e5, 0xa5e9, 0xa5ea, 0xa5eb, 0xa5ec, 0xa5ed, 0xa5f3, 0xb8a9, 0xb9d4, 0xbaee, +0xbbc8, 0xbef0, 0xbfb7, 0xc4ea, 0xc6fc, 0xc7bd, 0xcab8, 0xcaf3, 0xcbdc, 0xcdd1}; + +static const uint16_t commonChars_euc_kr[] = { +// TODO: This set of data comes from the character frequency- +// of-occurrence analysis tool. The data needs to be moved +// into a resource and loaded from there. +0xb0a1, 0xb0b3, 0xb0c5, 0xb0cd, 0xb0d4, 0xb0e6, 0xb0ed, 0xb0f8, 0xb0fa, 0xb0fc, +0xb1b8, 0xb1b9, 0xb1c7, 0xb1d7, 0xb1e2, 0xb3aa, 0xb3bb, 0xb4c2, 0xb4cf, 0xb4d9, +0xb4eb, 0xb5a5, 0xb5b5, 0xb5bf, 0xb5c7, 0xb5e9, 0xb6f3, 0xb7af, 0xb7c2, 0xb7ce, +0xb8a6, 0xb8ae, 0xb8b6, 0xb8b8, 0xb8bb, 0xb8e9, 0xb9ab, 0xb9ae, 0xb9cc, 0xb9ce, +0xb9fd, 0xbab8, 0xbace, 0xbad0, 0xbaf1, 0xbbe7, 0xbbf3, 0xbbfd, 0xbcad, 0xbcba, +0xbcd2, 0xbcf6, 0xbdba, 0xbdc0, 0xbdc3, 0xbdc5, 0xbec6, 0xbec8, 0xbedf, 0xbeee, +0xbef8, 0xbefa, 0xbfa1, 0xbfa9, 0xbfc0, 0xbfe4, 0xbfeb, 0xbfec, 0xbff8, 0xc0a7, +0xc0af, 0xc0b8, 0xc0ba, 0xc0bb, 0xc0bd, 0xc0c7, 0xc0cc, 0xc0ce, 0xc0cf, 0xc0d6, +0xc0da, 0xc0e5, 0xc0fb, 0xc0fc, 0xc1a4, 0xc1a6, 0xc1b6, 0xc1d6, 0xc1df, 0xc1f6, +0xc1f8, 0xc4a1, 0xc5cd, 0xc6ae, 0xc7cf, 0xc7d1, 0xc7d2, 0xc7d8, 0xc7e5, 0xc8ad}; + +static const uint16_t commonChars_big5[] = { +// TODO: This set of data comes from the character frequency- +// of-occurrence analysis tool. The data needs to be moved +// into a resource and loaded from there. +0xa140, 0xa141, 0xa142, 0xa143, 0xa147, 0xa149, 0xa175, 0xa176, 0xa440, 0xa446, +0xa447, 0xa448, 0xa451, 0xa454, 0xa457, 0xa464, 0xa46a, 0xa46c, 0xa477, 0xa4a3, +0xa4a4, 0xa4a7, 0xa4c1, 0xa4ce, 0xa4d1, 0xa4df, 0xa4e8, 0xa4fd, 0xa540, 0xa548, +0xa558, 0xa569, 0xa5cd, 0xa5e7, 0xa657, 0xa661, 0xa662, 0xa668, 0xa670, 0xa6a8, +0xa6b3, 0xa6b9, 0xa6d3, 0xa6db, 0xa6e6, 0xa6f2, 0xa740, 0xa751, 0xa759, 0xa7da, +0xa8a3, 0xa8a5, 0xa8ad, 0xa8d1, 0xa8d3, 0xa8e4, 0xa8fc, 0xa9c0, 0xa9d2, 0xa9f3, +0xaa6b, 0xaaba, 0xaabe, 0xaacc, 0xaafc, 0xac47, 0xac4f, 0xacb0, 0xacd2, 0xad59, +0xaec9, 0xafe0, 0xb0ea, 0xb16f, 0xb2b3, 0xb2c4, 0xb36f, 0xb44c, 0xb44e, 0xb54c, +0xb5a5, 0xb5bd, 0xb5d0, 0xb5d8, 0xb671, 0xb7ed, 0xb867, 0xb944, 0xbad8, 0xbb44, +0xbba1, 0xbdd1, 0xc2c4, 0xc3b9, 0xc440, 0xc45f}; + +static const uint16_t commonChars_gb_18030[] = { +// TODO: This set of data comes from the character frequency- +// of-occurrence analysis tool. The data needs to be moved +// into a resource and loaded from there. +0xa1a1, 0xa1a2, 0xa1a3, 0xa1a4, 0xa1b0, 0xa1b1, 0xa1f1, 0xa1f3, 0xa3a1, 0xa3ac, +0xa3ba, 0xb1a8, 0xb1b8, 0xb1be, 0xb2bb, 0xb3c9, 0xb3f6, 0xb4f3, 0xb5bd, 0xb5c4, +0xb5e3, 0xb6af, 0xb6d4, 0xb6e0, 0xb7a2, 0xb7a8, 0xb7bd, 0xb7d6, 0xb7dd, 0xb8b4, +0xb8df, 0xb8f6, 0xb9ab, 0xb9c9, 0xb9d8, 0xb9fa, 0xb9fd, 0xbacd, 0xbba7, 0xbbd6, +0xbbe1, 0xbbfa, 0xbcbc, 0xbcdb, 0xbcfe, 0xbdcc, 0xbecd, 0xbedd, 0xbfb4, 0xbfc6, +0xbfc9, 0xc0b4, 0xc0ed, 0xc1cb, 0xc2db, 0xc3c7, 0xc4dc, 0xc4ea, 0xc5cc, 0xc6f7, +0xc7f8, 0xc8ab, 0xc8cb, 0xc8d5, 0xc8e7, 0xc9cf, 0xc9fa, 0xcab1, 0xcab5, 0xcac7, +0xcad0, 0xcad6, 0xcaf5, 0xcafd, 0xccec, 0xcdf8, 0xceaa, 0xcec4, 0xced2, 0xcee5, +0xcfb5, 0xcfc2, 0xcfd6, 0xd0c2, 0xd0c5, 0xd0d0, 0xd0d4, 0xd1a7, 0xd2aa, 0xd2b2, +0xd2b5, 0xd2bb, 0xd2d4, 0xd3c3, 0xd3d0, 0xd3fd, 0xd4c2, 0xd4da, 0xd5e2, 0xd6d0}; + +static int32_t binarySearch(const uint16_t *array, int32_t len, uint16_t value) +{ + int32_t start = 0, end = len-1; + int32_t mid = (start+end)/2; + + while(start <= end) { + if(array[mid] == value) { + return mid; + } + + if(array[mid] < value){ + start = mid+1; + } else { + end = mid-1; + } + + mid = (start+end)/2; + } + + return -1; +} + +IteratedChar::IteratedChar() : +charValue(0), index(-1), nextIndex(0), error(false), done(false) +{ + // nothing else to do. +} + +/*void IteratedChar::reset() +{ + charValue = 0; + index = -1; + nextIndex = 0; + error = false; + done = false; +}*/ + +int32_t IteratedChar::nextByte(InputText *det) +{ + if (nextIndex >= det->fRawLength) { + done = true; + + return -1; + } + + return det->fRawInput[nextIndex++]; +} + +CharsetRecog_mbcs::~CharsetRecog_mbcs() +{ + // nothing to do. +} + +int32_t CharsetRecog_mbcs::match_mbcs(InputText *det, const uint16_t commonChars[], int32_t commonCharsLen) const { + int32_t doubleByteCharCount = 0; + int32_t commonCharCount = 0; + int32_t badCharCount = 0; + int32_t totalCharCount = 0; + int32_t confidence = 0; + IteratedChar iter; + + while (nextChar(&iter, det)) { + totalCharCount++; + + if (iter.error) { + badCharCount++; + } else { + if (iter.charValue > 0xFF) { + doubleByteCharCount++; + + if (commonChars != 0) { + if (binarySearch(commonChars, commonCharsLen, static_cast(iter.charValue)) >= 0){ + commonCharCount += 1; + } + } + } + } + + + if (badCharCount >= 2 && badCharCount*5 >= doubleByteCharCount) { + // Bail out early if the byte data is not matching the encoding scheme. + // break detectBlock; + return confidence; + } + } + + if (doubleByteCharCount <= 10 && badCharCount == 0) { + // Not many multi-byte chars. + if (doubleByteCharCount == 0 && totalCharCount < 10) { + // There weren't any multibyte sequences, and there was a low density of non-ASCII single bytes. + // We don't have enough data to have any confidence. + // Statistical analysis of single byte non-ASCII characters would probably help here. + confidence = 0; + } + else { + // ASCII or ISO file? It's probably not our encoding, + // but is not incompatible with our encoding, so don't give it a zero. + confidence = 10; + } + + return confidence; + } + + // + // No match if there are too many characters that don't fit the encoding scheme. + // (should we have zero tolerance for these?) + // + if (doubleByteCharCount < 20*badCharCount) { + confidence = 0; + + return confidence; + } + + if (commonChars == 0) { + // We have no statistics on frequently occurring characters. + // Assess confidence purely on having a reasonable number of + // multi-byte characters (the more the better) + confidence = 30 + doubleByteCharCount - 20*badCharCount; + + if (confidence > 100) { + confidence = 100; + } + } else { + // + // Frequency of occurrence statistics exist. + // + + double maxVal = log((double)doubleByteCharCount / 4); /*(float)?*/ + double scaleFactor = 90.0 / maxVal; + confidence = (int32_t)(log((double)commonCharCount+1) * scaleFactor + 10.0); + + confidence = min(confidence, 100); + } + + if (confidence < 0) { + confidence = 0; + } + + return confidence; +} + +CharsetRecog_sjis::~CharsetRecog_sjis() +{ + // nothing to do +} + +UBool CharsetRecog_sjis::nextChar(IteratedChar* it, InputText* det) const { + it->index = it->nextIndex; + it->error = false; + + int32_t firstByte = it->charValue = it->nextByte(det); + + if (firstByte < 0) { + return false; + } + + if (firstByte <= 0x7F || (firstByte > 0xA0 && firstByte <= 0xDF)) { + return true; + } + + int32_t secondByte = it->nextByte(det); + if (secondByte >= 0) { + it->charValue = (firstByte << 8) | secondByte; + } + // else we'll handle the error later. + + if (! ((secondByte >= 0x40 && secondByte <= 0x7F) || (secondByte >= 0x80 && secondByte <= 0xFE))) { + // Illegal second byte value. + it->error = true; + } + + return true; +} + +UBool CharsetRecog_sjis::match(InputText* det, CharsetMatch *results) const { + int32_t confidence = match_mbcs(det, commonChars_sjis, UPRV_LENGTHOF(commonChars_sjis)); + results->set(det, this, confidence); + return (confidence > 0); +} + +const char *CharsetRecog_sjis::getName() const +{ + return "Shift_JIS"; +} + +const char *CharsetRecog_sjis::getLanguage() const +{ + return "ja"; +} + +CharsetRecog_euc::~CharsetRecog_euc() +{ + // nothing to do +} + +UBool CharsetRecog_euc::nextChar(IteratedChar* it, InputText* det) const { + int32_t firstByte = 0; + int32_t secondByte = 0; + int32_t thirdByte = 0; + + it->index = it->nextIndex; + it->error = false; + firstByte = it->charValue = it->nextByte(det); + + if (firstByte < 0) { + // Ran off the end of the input data + return false; + } + + if (firstByte <= 0x8D) { + // single byte char + return true; + } + + secondByte = it->nextByte(det); + if (secondByte >= 0) { + it->charValue = (it->charValue << 8) | secondByte; + } + // else we'll handle the error later. + + if (firstByte >= 0xA1 && firstByte <= 0xFE) { + // Two byte Char + if (secondByte < 0xA1) { + it->error = true; + } + + return true; + } + + if (firstByte == 0x8E) { + // Code Set 2. + // In EUC-JP, total char size is 2 bytes, only one byte of actual char value. + // In EUC-TW, total char size is 4 bytes, three bytes contribute to char value. + // We don't know which we've got. + // Treat it like EUC-JP. If the data really was EUC-TW, the following two + // bytes will look like a well formed 2 byte char. + if (secondByte < 0xA1) { + it->error = true; + } + + return true; + } + + if (firstByte == 0x8F) { + // Code set 3. + // Three byte total char size, two bytes of actual char value. + thirdByte = it->nextByte(det); + it->charValue = (it->charValue << 8) | thirdByte; + + if (thirdByte < 0xa1) { + // Bad second byte or ran off the end of the input data with a non-ASCII first byte. + it->error = true; + } + } + + return true; + +} + +CharsetRecog_euc_jp::~CharsetRecog_euc_jp() +{ + // nothing to do +} + +const char *CharsetRecog_euc_jp::getName() const +{ + return "EUC-JP"; +} + +const char *CharsetRecog_euc_jp::getLanguage() const +{ + return "ja"; +} + +UBool CharsetRecog_euc_jp::match(InputText *det, CharsetMatch *results) const +{ + int32_t confidence = match_mbcs(det, commonChars_euc_jp, UPRV_LENGTHOF(commonChars_euc_jp)); + results->set(det, this, confidence); + return (confidence > 0); +} + +CharsetRecog_euc_kr::~CharsetRecog_euc_kr() +{ + // nothing to do +} + +const char *CharsetRecog_euc_kr::getName() const +{ + return "EUC-KR"; +} + +const char *CharsetRecog_euc_kr::getLanguage() const +{ + return "ko"; +} + +UBool CharsetRecog_euc_kr::match(InputText *det, CharsetMatch *results) const +{ + int32_t confidence = match_mbcs(det, commonChars_euc_kr, UPRV_LENGTHOF(commonChars_euc_kr)); + results->set(det, this, confidence); + return (confidence > 0); +} + +CharsetRecog_big5::~CharsetRecog_big5() +{ + // nothing to do +} + +UBool CharsetRecog_big5::nextChar(IteratedChar* it, InputText* det) const +{ + int32_t firstByte; + + it->index = it->nextIndex; + it->error = false; + firstByte = it->charValue = it->nextByte(det); + + if (firstByte < 0) { + return false; + } + + if (firstByte <= 0x7F || firstByte == 0xFF) { + // single byte character. + return true; + } + + int32_t secondByte = it->nextByte(det); + if (secondByte >= 0) { + it->charValue = (it->charValue << 8) | secondByte; + } + // else we'll handle the error later. + + if (secondByte < 0x40 || secondByte == 0x7F || secondByte == 0xFF) { + it->error = true; + } + + return true; +} + +const char *CharsetRecog_big5::getName() const +{ + return "Big5"; +} + +const char *CharsetRecog_big5::getLanguage() const +{ + return "zh"; +} + +UBool CharsetRecog_big5::match(InputText *det, CharsetMatch *results) const +{ + int32_t confidence = match_mbcs(det, commonChars_big5, UPRV_LENGTHOF(commonChars_big5)); + results->set(det, this, confidence); + return (confidence > 0); +} + +CharsetRecog_gb_18030::~CharsetRecog_gb_18030() +{ + // nothing to do +} + +UBool CharsetRecog_gb_18030::nextChar(IteratedChar* it, InputText* det) const { + int32_t firstByte = 0; + int32_t secondByte = 0; + int32_t thirdByte = 0; + int32_t fourthByte = 0; + + it->index = it->nextIndex; + it->error = false; + firstByte = it->charValue = it->nextByte(det); + + if (firstByte < 0) { + // Ran off the end of the input data + return false; + } + + if (firstByte <= 0x80) { + // single byte char + return true; + } + + secondByte = it->nextByte(det); + if (secondByte >= 0) { + it->charValue = (it->charValue << 8) | secondByte; + } + // else we'll handle the error later. + + if (firstByte >= 0x81 && firstByte <= 0xFE) { + // Two byte Char + if ((secondByte >= 0x40 && secondByte <= 0x7E) || (secondByte >=80 && secondByte <= 0xFE)) { + return true; + } + + // Four byte char + if (secondByte >= 0x30 && secondByte <= 0x39) { + thirdByte = it->nextByte(det); + + if (thirdByte >= 0x81 && thirdByte <= 0xFE) { + fourthByte = it->nextByte(det); + + if (fourthByte >= 0x30 && fourthByte <= 0x39) { + it->charValue = (it->charValue << 16) | (thirdByte << 8) | fourthByte; + + return true; + } + } + } + + // Something wasn't valid, or we ran out of data (-1). + it->error = true; + } + + return true; +} + +const char *CharsetRecog_gb_18030::getName() const +{ + return "GB18030"; +} + +const char *CharsetRecog_gb_18030::getLanguage() const +{ + return "zh"; +} + +UBool CharsetRecog_gb_18030::match(InputText *det, CharsetMatch *results) const +{ + int32_t confidence = match_mbcs(det, commonChars_gb_18030, UPRV_LENGTHOF(commonChars_gb_18030)); + results->set(det, this, confidence); + return (confidence > 0); +} + +U_NAMESPACE_END +#endif diff --git a/deps/icu-small/source/i18n/csrmbcs.h b/deps/icu-small/source/i18n/csrmbcs.h index ff7fc4e2a785f0..edc1c0dd700f72 100644 --- a/deps/icu-small/source/i18n/csrmbcs.h +++ b/deps/icu-small/source/i18n/csrmbcs.h @@ -1,207 +1,207 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2012, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#ifndef __CSRMBCS_H -#define __CSRMBCS_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "csrecog.h" - -U_NAMESPACE_BEGIN - -// "Character" iterated character class. -// Recognizers for specific mbcs encodings make their "characters" available -// by providing a nextChar() function that fills in an instance of IteratedChar -// with the next char from the input. -// The returned characters are not converted to Unicode, but remain as the raw -// bytes (concatenated into an int) from the codepage data. -// -// For Asian charsets, use the raw input rather than the input that has been -// stripped of markup. Detection only considers multi-byte chars, effectively -// stripping markup anyway, and double byte chars do occur in markup too. -// -class IteratedChar : public UMemory -{ -public: - uint32_t charValue; // 1-4 bytes from the raw input data - int32_t index; - int32_t nextIndex; - UBool error; - UBool done; - -public: - IteratedChar(); - //void reset(); - int32_t nextByte(InputText* det); -}; - - -class CharsetRecog_mbcs : public CharsetRecognizer { - -protected: - /** - * Test the match of this charset with the input text data - * which is obtained via the CharsetDetector object. - * - * @param det The CharsetDetector, which contains the input text - * to be checked for being in this charset. - * @return Two values packed into one int (Damn java, anyhow) - *
- * bits 0-7: the match confidence, ranging from 0-100 - *
- * bits 8-15: The match reason, an enum-like value. - */ - int32_t match_mbcs(InputText* det, const uint16_t commonChars[], int32_t commonCharsLen) const; - -public: - - virtual ~CharsetRecog_mbcs(); - - /** - * Get the IANA name of this charset. - * @return the charset name. - */ - - const char *getName() const override = 0; - const char *getLanguage() const override = 0; - UBool match(InputText* input, CharsetMatch *results) const override = 0; - - /** - * Get the next character (however many bytes it is) from the input data - * Subclasses for specific charset encodings must implement this function - * to get characters according to the rules of their encoding scheme. - * - * This function is not a method of class IteratedChar only because - * that would require a lot of extra derived classes, which is awkward. - * @param it The IteratedChar "struct" into which the returned char is placed. - * @param det The charset detector, which is needed to get at the input byte data - * being iterated over. - * @return True if a character was returned, false at end of input. - */ - virtual UBool nextChar(IteratedChar *it, InputText *textIn) const = 0; - -}; - - -/** - * Shift-JIS charset recognizer. - * - */ -class CharsetRecog_sjis : public CharsetRecog_mbcs { -public: - virtual ~CharsetRecog_sjis(); - - UBool nextChar(IteratedChar *it, InputText *det) const override; - - UBool match(InputText* input, CharsetMatch *results) const override; - - const char *getName() const override; - const char *getLanguage() const override; - -}; - - -/** - * EUC charset recognizers. One abstract class that provides the common function - * for getting the next character according to the EUC encoding scheme, - * and nested derived classes for EUC_KR, EUC_JP, EUC_CN. - * - */ -class CharsetRecog_euc : public CharsetRecog_mbcs -{ -public: - virtual ~CharsetRecog_euc(); - - const char *getName() const override = 0; - const char *getLanguage() const override = 0; - - UBool match(InputText* input, CharsetMatch *results) const override = 0; - /* - * (non-Javadoc) - * Get the next character value for EUC based encodings. - * Character "value" is simply the raw bytes that make up the character - * packed into an int. - */ - UBool nextChar(IteratedChar *it, InputText *det) const override; -}; - -/** - * The charset recognize for EUC-JP. A singleton instance of this class - * is created and kept by the public CharsetDetector class - */ -class CharsetRecog_euc_jp : public CharsetRecog_euc -{ -public: - virtual ~CharsetRecog_euc_jp(); - - const char *getName() const override; - const char *getLanguage() const override; - - UBool match(InputText* input, CharsetMatch *results) const override; -}; - -/** - * The charset recognize for EUC-KR. A singleton instance of this class - * is created and kept by the public CharsetDetector class - */ -class CharsetRecog_euc_kr : public CharsetRecog_euc -{ -public: - virtual ~CharsetRecog_euc_kr(); - - const char *getName() const override; - const char *getLanguage() const override; - - UBool match(InputText* input, CharsetMatch *results) const override; -}; - -/** - * - * Big5 charset recognizer. - * - */ -class CharsetRecog_big5 : public CharsetRecog_mbcs -{ -public: - virtual ~CharsetRecog_big5(); - - UBool nextChar(IteratedChar* it, InputText* det) const override; - - const char *getName() const override; - const char *getLanguage() const override; - - UBool match(InputText* input, CharsetMatch *results) const override; -}; - - -/** - * - * GB-18030 recognizer. Uses simplified Chinese statistics. - * - */ -class CharsetRecog_gb_18030 : public CharsetRecog_mbcs -{ -public: - virtual ~CharsetRecog_gb_18030(); - - UBool nextChar(IteratedChar* it, InputText* det) const override; - - const char *getName() const override; - const char *getLanguage() const override; - - UBool match(InputText* input, CharsetMatch *results) const override; -}; - -U_NAMESPACE_END - -#endif -#endif /* __CSRMBCS_H */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2012, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#ifndef __CSRMBCS_H +#define __CSRMBCS_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "csrecog.h" + +U_NAMESPACE_BEGIN + +// "Character" iterated character class. +// Recognizers for specific mbcs encodings make their "characters" available +// by providing a nextChar() function that fills in an instance of IteratedChar +// with the next char from the input. +// The returned characters are not converted to Unicode, but remain as the raw +// bytes (concatenated into an int) from the codepage data. +// +// For Asian charsets, use the raw input rather than the input that has been +// stripped of markup. Detection only considers multi-byte chars, effectively +// stripping markup anyway, and double byte chars do occur in markup too. +// +class IteratedChar : public UMemory +{ +public: + uint32_t charValue; // 1-4 bytes from the raw input data + int32_t index; + int32_t nextIndex; + UBool error; + UBool done; + +public: + IteratedChar(); + //void reset(); + int32_t nextByte(InputText* det); +}; + + +class CharsetRecog_mbcs : public CharsetRecognizer { + +protected: + /** + * Test the match of this charset with the input text data + * which is obtained via the CharsetDetector object. + * + * @param det The CharsetDetector, which contains the input text + * to be checked for being in this charset. + * @return Two values packed into one int (Damn java, anyhow) + *
+ * bits 0-7: the match confidence, ranging from 0-100 + *
+ * bits 8-15: The match reason, an enum-like value. + */ + int32_t match_mbcs(InputText* det, const uint16_t commonChars[], int32_t commonCharsLen) const; + +public: + + virtual ~CharsetRecog_mbcs(); + + /** + * Get the IANA name of this charset. + * @return the charset name. + */ + + const char *getName() const override = 0; + const char *getLanguage() const override = 0; + UBool match(InputText* input, CharsetMatch *results) const override = 0; + + /** + * Get the next character (however many bytes it is) from the input data + * Subclasses for specific charset encodings must implement this function + * to get characters according to the rules of their encoding scheme. + * + * This function is not a method of class IteratedChar only because + * that would require a lot of extra derived classes, which is awkward. + * @param it The IteratedChar "struct" into which the returned char is placed. + * @param det The charset detector, which is needed to get at the input byte data + * being iterated over. + * @return True if a character was returned, false at end of input. + */ + virtual UBool nextChar(IteratedChar *it, InputText *textIn) const = 0; + +}; + + +/** + * Shift-JIS charset recognizer. + * + */ +class CharsetRecog_sjis : public CharsetRecog_mbcs { +public: + virtual ~CharsetRecog_sjis(); + + UBool nextChar(IteratedChar *it, InputText *det) const override; + + UBool match(InputText* input, CharsetMatch *results) const override; + + const char *getName() const override; + const char *getLanguage() const override; + +}; + + +/** + * EUC charset recognizers. One abstract class that provides the common function + * for getting the next character according to the EUC encoding scheme, + * and nested derived classes for EUC_KR, EUC_JP, EUC_CN. + * + */ +class CharsetRecog_euc : public CharsetRecog_mbcs +{ +public: + virtual ~CharsetRecog_euc(); + + const char *getName() const override = 0; + const char *getLanguage() const override = 0; + + UBool match(InputText* input, CharsetMatch *results) const override = 0; + /* + * (non-Javadoc) + * Get the next character value for EUC based encodings. + * Character "value" is simply the raw bytes that make up the character + * packed into an int. + */ + UBool nextChar(IteratedChar *it, InputText *det) const override; +}; + +/** + * The charset recognize for EUC-JP. A singleton instance of this class + * is created and kept by the public CharsetDetector class + */ +class CharsetRecog_euc_jp : public CharsetRecog_euc +{ +public: + virtual ~CharsetRecog_euc_jp(); + + const char *getName() const override; + const char *getLanguage() const override; + + UBool match(InputText* input, CharsetMatch *results) const override; +}; + +/** + * The charset recognize for EUC-KR. A singleton instance of this class + * is created and kept by the public CharsetDetector class + */ +class CharsetRecog_euc_kr : public CharsetRecog_euc +{ +public: + virtual ~CharsetRecog_euc_kr(); + + const char *getName() const override; + const char *getLanguage() const override; + + UBool match(InputText* input, CharsetMatch *results) const override; +}; + +/** + * + * Big5 charset recognizer. + * + */ +class CharsetRecog_big5 : public CharsetRecog_mbcs +{ +public: + virtual ~CharsetRecog_big5(); + + UBool nextChar(IteratedChar* it, InputText* det) const override; + + const char *getName() const override; + const char *getLanguage() const override; + + UBool match(InputText* input, CharsetMatch *results) const override; +}; + + +/** + * + * GB-18030 recognizer. Uses simplified Chinese statistics. + * + */ +class CharsetRecog_gb_18030 : public CharsetRecog_mbcs +{ +public: + virtual ~CharsetRecog_gb_18030(); + + UBool nextChar(IteratedChar* it, InputText* det) const override; + + const char *getName() const override; + const char *getLanguage() const override; + + UBool match(InputText* input, CharsetMatch *results) const override; +}; + +U_NAMESPACE_END + +#endif +#endif /* __CSRMBCS_H */ diff --git a/deps/icu-small/source/i18n/csrsbcs.cpp b/deps/icu-small/source/i18n/csrsbcs.cpp index 92af9b5291b9ff..9b58b91f3d4c8c 100644 --- a/deps/icu-small/source/i18n/csrsbcs.cpp +++ b/deps/icu-small/source/i18n/csrsbcs.cpp @@ -1,1271 +1,1271 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2016, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#include "unicode/utypes.h" - -#include "cmemory.h" - -#if !UCONFIG_NO_CONVERSION -#include "csrsbcs.h" -#include "csmatch.h" - -#define N_GRAM_SIZE 3 -#define N_GRAM_MASK 0xFFFFFF - -U_NAMESPACE_BEGIN - -NGramParser::NGramParser(const int32_t *theNgramList, const uint8_t *theCharMap) - : ngram(0), byteIndex(0) -{ - ngramList = theNgramList; - charMap = theCharMap; - - ngramCount = hitCount = 0; -} - -NGramParser::~NGramParser() -{ -} - -/* - * Binary search for value in table, which must have exactly 64 entries. - */ - -int32_t NGramParser::search(const int32_t *table, int32_t value) -{ - int32_t index = 0; - - if (table[index + 32] <= value) { - index += 32; - } - - if (table[index + 16] <= value) { - index += 16; - } - - if (table[index + 8] <= value) { - index += 8; - } - - if (table[index + 4] <= value) { - index += 4; - } - - if (table[index + 2] <= value) { - index += 2; - } - - if (table[index + 1] <= value) { - index += 1; - } - - if (table[index] > value) { - index -= 1; - } - - if (index < 0 || table[index] != value) { - return -1; - } - - return index; -} - -void NGramParser::lookup(int32_t thisNgram) -{ - ngramCount += 1; - - if (search(ngramList, thisNgram) >= 0) { - hitCount += 1; - } - -} - -void NGramParser::addByte(int32_t b) -{ - ngram = ((ngram << 8) + b) & N_GRAM_MASK; - lookup(ngram); -} - -int32_t NGramParser::nextByte(InputText *det) -{ - if (byteIndex >= det->fInputLen) { - return -1; - } - - return det->fInputBytes[byteIndex++]; -} - -void NGramParser::parseCharacters(InputText *det) -{ - int32_t b; - bool ignoreSpace = false; - - while ((b = nextByte(det)) >= 0) { - uint8_t mb = charMap[b]; - - // TODO: 0x20 might not be a space in all character sets... - if (mb != 0) { - if (!(mb == 0x20 && ignoreSpace)) { - addByte(mb); - } - - ignoreSpace = (mb == 0x20); - } - } -} - -int32_t NGramParser::parse(InputText *det) -{ - parseCharacters(det); - - // TODO: Is this OK? The buffer could have ended in the middle of a word... - addByte(0x20); - - double rawPercent = (double) hitCount / (double) ngramCount; - - // if (rawPercent <= 2.0) { - // return 0; - // } - - // TODO - This is a bit of a hack to take care of a case - // were we were getting a confidence of 135... - if (rawPercent > 0.33) { - return 98; - } - - return (int32_t) (rawPercent * 300.0); -} - -#if !UCONFIG_ONLY_HTML_CONVERSION -static const uint8_t unshapeMap_IBM420[] = { -/* -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F */ -/* 0- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 1- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 2- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 3- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 4- */ 0x40, 0x40, 0x42, 0x42, 0x44, 0x45, 0x46, 0x47, 0x47, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, -/* 5- */ 0x50, 0x49, 0x52, 0x53, 0x54, 0x55, 0x56, 0x56, 0x58, 0x58, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, -/* 6- */ 0x60, 0x61, 0x62, 0x63, 0x63, 0x65, 0x65, 0x67, 0x67, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, -/* 7- */ 0x69, 0x71, 0x71, 0x73, 0x74, 0x75, 0x76, 0x77, 0x77, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, -/* 8- */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x80, 0x8B, 0x8B, 0x8D, 0x8D, 0x8F, -/* 9- */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A, 0x9A, 0x9E, 0x9E, -/* A- */ 0x9E, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0x9E, 0xAB, 0xAB, 0xAD, 0xAD, 0xAF, -/* B- */ 0xAF, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xB1, 0xBB, 0xBB, 0xBD, 0xBD, 0xBF, -/* C- */ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xBF, 0xCC, 0xBF, 0xCE, 0xCF, -/* D- */ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDA, 0xDC, 0xDC, 0xDC, 0xDF, -/* E- */ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, -/* F- */ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, -}; - -NGramParser_IBM420::NGramParser_IBM420(const int32_t *theNgramList, const uint8_t *theCharMap):NGramParser(theNgramList, theCharMap) -{ - alef = 0x00; -} - -NGramParser_IBM420::~NGramParser_IBM420() {} - -int32_t NGramParser_IBM420::isLamAlef(int32_t b) -{ - if(b == 0xB2 || b == 0xB3){ - return 0x47; - }else if(b == 0xB4 || b == 0xB5){ - return 0x49; - }else if(b == 0xB8 || b == 0xB9){ - return 0x56; - }else - return 0x00; -} - -/* -* Arabic shaping needs to be done manually. Cannot call ArabicShaping class -* because CharsetDetector is dealing with bytes not Unicode code points. We could -* convert the bytes to Unicode code points but that would leave us dependent -* on CharsetICU which we try to avoid. IBM420 converter amongst different versions -* of JDK can produce different results and therefore is also avoided. -*/ -int32_t NGramParser_IBM420::nextByte(InputText *det) -{ - - if (byteIndex >= det->fInputLen || det->fInputBytes[byteIndex] == 0) { - return -1; - } - int next; - - alef = isLamAlef(det->fInputBytes[byteIndex]); - if(alef != 0x00) - next = 0xB1 & 0xFF; - else - next = unshapeMap_IBM420[det->fInputBytes[byteIndex]& 0xFF] & 0xFF; - - byteIndex++; - - return next; -} - -void NGramParser_IBM420::parseCharacters(InputText *det) -{ - int32_t b; - bool ignoreSpace = false; - - while ((b = nextByte(det)) >= 0) { - uint8_t mb = charMap[b]; - - // TODO: 0x20 might not be a space in all character sets... - if (mb != 0) { - if (!(mb == 0x20 && ignoreSpace)) { - addByte(mb); - } - ignoreSpace = (mb == 0x20); - } - - if(alef != 0x00){ - mb = charMap[alef & 0xFF]; - - // TODO: 0x20 might not be a space in all character sets... - if (mb != 0) { - if (!(mb == 0x20 && ignoreSpace)) { - addByte(mb); - } - - ignoreSpace = (mb == 0x20); - } - - } - } -} -#endif - -CharsetRecog_sbcs::CharsetRecog_sbcs() -{ - // nothing else to do -} - -CharsetRecog_sbcs::~CharsetRecog_sbcs() -{ - // nothing to do -} - -int32_t CharsetRecog_sbcs::match_sbcs(InputText *det, const int32_t ngrams[], const uint8_t byteMap[]) const -{ - NGramParser parser(ngrams, byteMap); - int32_t result; - - result = parser.parse(det); - - return result; -} - -static const uint8_t charMap_8859_1[] = { - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0xAA, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0xB5, 0x20, 0x20, - 0x20, 0x20, 0xBA, 0x20, 0x20, 0x20, 0x20, 0x20, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, - 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xDF, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, - 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, -}; - -static const uint8_t charMap_8859_2[] = { - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0xB1, 0x20, 0xB3, 0x20, 0xB5, 0xB6, 0x20, - 0x20, 0xB9, 0xBA, 0xBB, 0xBC, 0x20, 0xBE, 0xBF, - 0x20, 0xB1, 0x20, 0xB3, 0x20, 0xB5, 0xB6, 0xB7, - 0x20, 0xB9, 0xBA, 0xBB, 0xBC, 0x20, 0xBE, 0xBF, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, - 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xDF, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, - 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x20, -}; - -static const uint8_t charMap_8859_5[] = { - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x20, 0xFE, 0xFF, - 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, - 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, - 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0x20, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x20, 0xFE, 0xFF, -}; - -static const uint8_t charMap_8859_6[] = { - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, - 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, - 0xD8, 0xD9, 0xDA, 0x20, 0x20, 0x20, 0x20, 0x20, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, -}; - -static const uint8_t charMap_8859_7[] = { - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0xA1, 0xA2, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDC, 0x20, - 0xDD, 0xDE, 0xDF, 0x20, 0xFC, 0x20, 0xFD, 0xFE, - 0xC0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0x20, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, 0xFB, 0xDC, 0xDD, 0xDE, 0xDF, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x20, -}; - -static const uint8_t charMap_8859_8[] = { - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0xB5, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, 0x20, 0x20, 0x20, 0x20, 0x20, -}; - -static const uint8_t charMap_8859_9[] = { - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0xAA, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0xB5, 0x20, 0x20, - 0x20, 0x20, 0xBA, 0x20, 0x20, 0x20, 0x20, 0x20, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, - 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x69, 0xFE, 0xDF, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, - 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, -}; - -static const int32_t ngrams_windows_1251[] = { - 0x20E220, 0x20E2EE, 0x20E4EE, 0x20E7E0, 0x20E820, 0x20EAE0, 0x20EAEE, 0x20EDE0, 0x20EDE5, 0x20EEE1, 0x20EFEE, 0x20EFF0, 0x20F0E0, 0x20F1EE, 0x20F1F2, 0x20F2EE, - 0x20F7F2, 0x20FDF2, 0xE0EDE8, 0xE0F2FC, 0xE3EE20, 0xE5EBFC, 0xE5EDE8, 0xE5F1F2, 0xE5F220, 0xE820EF, 0xE8E520, 0xE8E820, 0xE8FF20, 0xEBE5ED, 0xEBE820, 0xEBFCED, - 0xEDE020, 0xEDE520, 0xEDE8E5, 0xEDE8FF, 0xEDEE20, 0xEDEEE2, 0xEE20E2, 0xEE20EF, 0xEE20F1, 0xEEE220, 0xEEE2E0, 0xEEE3EE, 0xEEE920, 0xEEEBFC, 0xEEEC20, 0xEEF1F2, - 0xEFEEEB, 0xEFF0E5, 0xEFF0E8, 0xEFF0EE, 0xF0E0E2, 0xF0E5E4, 0xF1F2E0, 0xF1F2E2, 0xF1F2E8, 0xF1FF20, 0xF2E5EB, 0xF2EE20, 0xF2EEF0, 0xF2FC20, 0xF7F2EE, 0xFBF520, -}; - -static const uint8_t charMap_windows_1251[] = { - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x90, 0x83, 0x20, 0x83, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x9A, 0x20, 0x9C, 0x9D, 0x9E, 0x9F, - 0x90, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x9A, 0x20, 0x9C, 0x9D, 0x9E, 0x9F, - 0x20, 0xA2, 0xA2, 0xBC, 0x20, 0xB4, 0x20, 0x20, - 0xB8, 0x20, 0xBA, 0x20, 0x20, 0x20, 0x20, 0xBF, - 0x20, 0x20, 0xB3, 0xB3, 0xB4, 0xB5, 0x20, 0x20, - 0xB8, 0x20, 0xBA, 0x20, 0xBC, 0xBE, 0xBE, 0xBF, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, -}; - -static const int32_t ngrams_windows_1256[] = { - 0x20C7E1, 0x20C7E4, 0x20C8C7, 0x20DAE1, 0x20DDED, 0x20E1E1, 0x20E3E4, 0x20E6C7, 0xC720C7, 0xC7C120, 0xC7CA20, 0xC7D120, 0xC7E120, 0xC7E1C3, 0xC7E1C7, 0xC7E1C8, - 0xC7E1CA, 0xC7E1CC, 0xC7E1CD, 0xC7E1CF, 0xC7E1D3, 0xC7E1DA, 0xC7E1DE, 0xC7E1E3, 0xC7E1E6, 0xC7E1ED, 0xC7E320, 0xC7E420, 0xC7E4CA, 0xC820C7, 0xC920C7, 0xC920DD, - 0xC920E1, 0xC920E3, 0xC920E6, 0xCA20C7, 0xCF20C7, 0xCFC920, 0xD120C7, 0xD1C920, 0xD320C7, 0xDA20C7, 0xDAE1EC, 0xDDED20, 0xE120C7, 0xE1C920, 0xE1EC20, 0xE1ED20, - 0xE320C7, 0xE3C720, 0xE3C920, 0xE3E420, 0xE420C7, 0xE520C7, 0xE5C720, 0xE6C7E1, 0xE6E420, 0xEC20C7, 0xED20C7, 0xED20E3, 0xED20E6, 0xEDC920, 0xEDD120, 0xEDE420, -}; - -static const uint8_t charMap_windows_1256[] = { - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x81, 0x20, 0x83, 0x20, 0x20, 0x20, 0x20, - 0x88, 0x20, 0x8A, 0x20, 0x9C, 0x8D, 0x8E, 0x8F, - 0x90, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x98, 0x20, 0x9A, 0x20, 0x9C, 0x20, 0x20, 0x9F, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0xAA, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0xB5, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, - 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0x20, - 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0x20, 0x20, 0x20, 0x20, 0xF4, 0x20, 0x20, 0x20, - 0x20, 0xF9, 0x20, 0xFB, 0xFC, 0x20, 0x20, 0xFF, -}; - -static const int32_t ngrams_KOI8_R[] = { - 0x20C4CF, 0x20C920, 0x20CBC1, 0x20CBCF, 0x20CEC1, 0x20CEC5, 0x20CFC2, 0x20D0CF, 0x20D0D2, 0x20D2C1, 0x20D3CF, 0x20D3D4, 0x20D4CF, 0x20D720, 0x20D7CF, 0x20DAC1, - 0x20DCD4, 0x20DED4, 0xC1CEC9, 0xC1D4D8, 0xC5CCD8, 0xC5CEC9, 0xC5D3D4, 0xC5D420, 0xC7CF20, 0xC920D0, 0xC9C520, 0xC9C920, 0xC9D120, 0xCCC5CE, 0xCCC920, 0xCCD8CE, - 0xCEC120, 0xCEC520, 0xCEC9C5, 0xCEC9D1, 0xCECF20, 0xCECFD7, 0xCF20D0, 0xCF20D3, 0xCF20D7, 0xCFC7CF, 0xCFCA20, 0xCFCCD8, 0xCFCD20, 0xCFD3D4, 0xCFD720, 0xCFD7C1, - 0xD0CFCC, 0xD0D2C5, 0xD0D2C9, 0xD0D2CF, 0xD2C1D7, 0xD2C5C4, 0xD3D120, 0xD3D4C1, 0xD3D4C9, 0xD3D4D7, 0xD4C5CC, 0xD4CF20, 0xD4CFD2, 0xD4D820, 0xD9C820, 0xDED4CF, -}; - -static const uint8_t charMap_KOI8_R[] = { - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0xA3, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0xA3, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, - 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, - 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, - 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, - 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, - 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, -}; - -#if !UCONFIG_ONLY_HTML_CONVERSION -static const int32_t ngrams_IBM424_he_rtl[] = { - 0x404146, 0x404148, 0x404151, 0x404171, 0x404251, 0x404256, 0x404541, 0x404546, 0x404551, 0x404556, 0x404562, 0x404569, 0x404571, 0x405441, 0x405445, 0x405641, - 0x406254, 0x406954, 0x417140, 0x454041, 0x454042, 0x454045, 0x454054, 0x454056, 0x454069, 0x454641, 0x464140, 0x465540, 0x465740, 0x466840, 0x467140, 0x514045, - 0x514540, 0x514671, 0x515155, 0x515540, 0x515740, 0x516840, 0x517140, 0x544041, 0x544045, 0x544140, 0x544540, 0x554041, 0x554042, 0x554045, 0x554054, 0x554056, - 0x554069, 0x564540, 0x574045, 0x584540, 0x585140, 0x585155, 0x625440, 0x684045, 0x685155, 0x695440, 0x714041, 0x714042, 0x714045, 0x714054, 0x714056, 0x714069, -}; - -static const int32_t ngrams_IBM424_he_ltr[] = { - 0x404146, 0x404154, 0x404551, 0x404554, 0x404556, 0x404558, 0x405158, 0x405462, 0x405469, 0x405546, 0x405551, 0x405746, 0x405751, 0x406846, 0x406851, 0x407141, - 0x407146, 0x407151, 0x414045, 0x414054, 0x414055, 0x414071, 0x414540, 0x414645, 0x415440, 0x415640, 0x424045, 0x424055, 0x424071, 0x454045, 0x454051, 0x454054, - 0x454055, 0x454057, 0x454068, 0x454071, 0x455440, 0x464140, 0x464540, 0x484140, 0x514140, 0x514240, 0x514540, 0x544045, 0x544055, 0x544071, 0x546240, 0x546940, - 0x555151, 0x555158, 0x555168, 0x564045, 0x564055, 0x564071, 0x564240, 0x564540, 0x624540, 0x694045, 0x694055, 0x694071, 0x694540, 0x714140, 0x714540, 0x714651, -}; - -static const uint8_t charMap_IBM424_he[] = { -/* -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F */ -/* 0- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 1- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 2- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 3- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 4- */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 5- */ 0x40, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 6- */ 0x40, 0x40, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 7- */ 0x40, 0x71, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x40, 0x40, -/* 8- */ 0x40, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 9- */ 0x40, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* A- */ 0xA0, 0x40, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* B- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* C- */ 0x40, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* D- */ 0x40, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* E- */ 0x40, 0x40, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* F- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -}; - -static const int32_t ngrams_IBM420_ar_rtl[] = { - 0x4056B1, 0x4056BD, 0x405856, 0x409AB1, 0x40ABDC, 0x40B1B1, 0x40BBBD, 0x40CF56, 0x564056, 0x564640, 0x566340, 0x567540, 0x56B140, 0x56B149, 0x56B156, 0x56B158, - 0x56B163, 0x56B167, 0x56B169, 0x56B173, 0x56B178, 0x56B19A, 0x56B1AD, 0x56B1BB, 0x56B1CF, 0x56B1DC, 0x56BB40, 0x56BD40, 0x56BD63, 0x584056, 0x624056, 0x6240AB, - 0x6240B1, 0x6240BB, 0x6240CF, 0x634056, 0x734056, 0x736240, 0x754056, 0x756240, 0x784056, 0x9A4056, 0x9AB1DA, 0xABDC40, 0xB14056, 0xB16240, 0xB1DA40, 0xB1DC40, - 0xBB4056, 0xBB5640, 0xBB6240, 0xBBBD40, 0xBD4056, 0xBF4056, 0xBF5640, 0xCF56B1, 0xCFBD40, 0xDA4056, 0xDC4056, 0xDC40BB, 0xDC40CF, 0xDC6240, 0xDC7540, 0xDCBD40, -}; - -static const int32_t ngrams_IBM420_ar_ltr[] = { - 0x404656, 0x4056BB, 0x4056BF, 0x406273, 0x406275, 0x4062B1, 0x4062BB, 0x4062DC, 0x406356, 0x407556, 0x4075DC, 0x40B156, 0x40BB56, 0x40BD56, 0x40BDBB, 0x40BDCF, - 0x40BDDC, 0x40DAB1, 0x40DCAB, 0x40DCB1, 0x49B156, 0x564056, 0x564058, 0x564062, 0x564063, 0x564073, 0x564075, 0x564078, 0x56409A, 0x5640B1, 0x5640BB, 0x5640BD, - 0x5640BF, 0x5640DA, 0x5640DC, 0x565840, 0x56B156, 0x56CF40, 0x58B156, 0x63B156, 0x63BD56, 0x67B156, 0x69B156, 0x73B156, 0x78B156, 0x9AB156, 0xAB4062, 0xADB156, - 0xB14062, 0xB15640, 0xB156CF, 0xB19A40, 0xB1B140, 0xBB4062, 0xBB40DC, 0xBBB156, 0xBD5640, 0xBDBB40, 0xCF4062, 0xCF40DC, 0xCFB156, 0xDAB19A, 0xDCAB40, 0xDCB156 -}; - -static const uint8_t charMap_IBM420_ar[]= { -/* -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F */ -/* 0- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 1- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 2- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 3- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 4- */ 0x40, 0x40, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 5- */ 0x40, 0x51, 0x52, 0x40, 0x40, 0x55, 0x56, 0x57, 0x58, 0x59, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 6- */ 0x40, 0x40, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 7- */ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, -/* 8- */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, -/* 9- */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, -/* A- */ 0xA0, 0x40, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, -/* B- */ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0x40, 0x40, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, -/* C- */ 0x40, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x40, 0xCB, 0x40, 0xCD, 0x40, 0xCF, -/* D- */ 0x40, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, -/* E- */ 0x40, 0x40, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xEA, 0xEB, 0x40, 0xED, 0xEE, 0xEF, -/* F- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xFB, 0xFC, 0xFD, 0xFE, 0x40, -}; -#endif - -//ISO-8859-1,2,5,6,7,8,9 Ngrams - -struct NGramsPlusLang { - const int32_t ngrams[64]; - const char * lang; -}; - -static const NGramsPlusLang ngrams_8859_1[] = { - { - { - 0x206120, 0x20616E, 0x206265, 0x20636F, 0x20666F, 0x206861, 0x206865, 0x20696E, 0x206D61, 0x206F66, 0x207072, 0x207265, 0x207361, 0x207374, 0x207468, 0x20746F, - 0x207768, 0x616964, 0x616C20, 0x616E20, 0x616E64, 0x617320, 0x617420, 0x617465, 0x617469, 0x642061, 0x642074, 0x652061, 0x652073, 0x652074, 0x656420, 0x656E74, - 0x657220, 0x657320, 0x666F72, 0x686174, 0x686520, 0x686572, 0x696420, 0x696E20, 0x696E67, 0x696F6E, 0x697320, 0x6E2061, 0x6E2074, 0x6E6420, 0x6E6720, 0x6E7420, - 0x6F6620, 0x6F6E20, 0x6F7220, 0x726520, 0x727320, 0x732061, 0x732074, 0x736169, 0x737420, 0x742074, 0x746572, 0x746861, 0x746865, 0x74696F, 0x746F20, 0x747320, - }, - "en" - }, - { - { - 0x206166, 0x206174, 0x206465, 0x20656E, 0x206572, 0x20666F, 0x206861, 0x206920, 0x206D65, 0x206F67, 0x2070E5, 0x207369, 0x207374, 0x207469, 0x207669, 0x616620, - 0x616E20, 0x616E64, 0x617220, 0x617420, 0x646520, 0x64656E, 0x646572, 0x646574, 0x652073, 0x656420, 0x656465, 0x656E20, 0x656E64, 0x657220, 0x657265, 0x657320, - 0x657420, 0x666F72, 0x676520, 0x67656E, 0x676572, 0x696765, 0x696C20, 0x696E67, 0x6B6520, 0x6B6B65, 0x6C6572, 0x6C6967, 0x6C6C65, 0x6D6564, 0x6E6465, 0x6E6520, - 0x6E6720, 0x6E6765, 0x6F6720, 0x6F6D20, 0x6F7220, 0x70E520, 0x722064, 0x722065, 0x722073, 0x726520, 0x737465, 0x742073, 0x746520, 0x746572, 0x74696C, 0x766572, - }, - "da" - }, - { - { - 0x20616E, 0x206175, 0x206265, 0x206461, 0x206465, 0x206469, 0x206569, 0x206765, 0x206861, 0x20696E, 0x206D69, 0x207363, 0x207365, 0x20756E, 0x207665, 0x20766F, - 0x207765, 0x207A75, 0x626572, 0x636820, 0x636865, 0x636874, 0x646173, 0x64656E, 0x646572, 0x646965, 0x652064, 0x652073, 0x65696E, 0x656974, 0x656E20, 0x657220, - 0x657320, 0x67656E, 0x68656E, 0x687420, 0x696368, 0x696520, 0x696E20, 0x696E65, 0x697420, 0x6C6963, 0x6C6C65, 0x6E2061, 0x6E2064, 0x6E2073, 0x6E6420, 0x6E6465, - 0x6E6520, 0x6E6720, 0x6E6765, 0x6E7465, 0x722064, 0x726465, 0x726569, 0x736368, 0x737465, 0x742064, 0x746520, 0x74656E, 0x746572, 0x756E64, 0x756E67, 0x766572, - }, - "de" - }, - { - { - 0x206120, 0x206361, 0x20636F, 0x206465, 0x20656C, 0x20656E, 0x206573, 0x20696E, 0x206C61, 0x206C6F, 0x207061, 0x20706F, 0x207072, 0x207175, 0x207265, 0x207365, - 0x20756E, 0x207920, 0x612063, 0x612064, 0x612065, 0x61206C, 0x612070, 0x616369, 0x61646F, 0x616C20, 0x617220, 0x617320, 0x6369F3, 0x636F6E, 0x646520, 0x64656C, - 0x646F20, 0x652064, 0x652065, 0x65206C, 0x656C20, 0x656E20, 0x656E74, 0x657320, 0x657374, 0x69656E, 0x69F36E, 0x6C6120, 0x6C6F73, 0x6E2065, 0x6E7465, 0x6F2064, - 0x6F2065, 0x6F6E20, 0x6F7220, 0x6F7320, 0x706172, 0x717565, 0x726120, 0x726573, 0x732064, 0x732065, 0x732070, 0x736520, 0x746520, 0x746F20, 0x756520, 0xF36E20, - }, - "es" - }, - { - { - 0x206175, 0x20636F, 0x206461, 0x206465, 0x206475, 0x20656E, 0x206574, 0x206C61, 0x206C65, 0x207061, 0x20706F, 0x207072, 0x207175, 0x207365, 0x20736F, 0x20756E, - 0x20E020, 0x616E74, 0x617469, 0x636520, 0x636F6E, 0x646520, 0x646573, 0x647520, 0x652061, 0x652063, 0x652064, 0x652065, 0x65206C, 0x652070, 0x652073, 0x656E20, - 0x656E74, 0x657220, 0x657320, 0x657420, 0x657572, 0x696F6E, 0x697320, 0x697420, 0x6C6120, 0x6C6520, 0x6C6573, 0x6D656E, 0x6E2064, 0x6E6520, 0x6E7320, 0x6E7420, - 0x6F6E20, 0x6F6E74, 0x6F7572, 0x717565, 0x72206C, 0x726520, 0x732061, 0x732064, 0x732065, 0x73206C, 0x732070, 0x742064, 0x746520, 0x74696F, 0x756520, 0x757220, - }, - "fr" - }, - { - { - 0x20616C, 0x206368, 0x20636F, 0x206465, 0x206469, 0x206520, 0x20696C, 0x20696E, 0x206C61, 0x207065, 0x207072, 0x20756E, 0x612063, 0x612064, 0x612070, 0x612073, - 0x61746F, 0x636865, 0x636F6E, 0x64656C, 0x646920, 0x652061, 0x652063, 0x652064, 0x652069, 0x65206C, 0x652070, 0x652073, 0x656C20, 0x656C6C, 0x656E74, 0x657220, - 0x686520, 0x692061, 0x692063, 0x692064, 0x692073, 0x696120, 0x696C20, 0x696E20, 0x696F6E, 0x6C6120, 0x6C6520, 0x6C6920, 0x6C6C61, 0x6E6520, 0x6E6920, 0x6E6F20, - 0x6E7465, 0x6F2061, 0x6F2064, 0x6F2069, 0x6F2073, 0x6F6E20, 0x6F6E65, 0x706572, 0x726120, 0x726520, 0x736920, 0x746120, 0x746520, 0x746920, 0x746F20, 0x7A696F, - }, - "it" - }, - { - { - 0x20616C, 0x206265, 0x206461, 0x206465, 0x206469, 0x206565, 0x20656E, 0x206765, 0x206865, 0x20696E, 0x206D61, 0x206D65, 0x206F70, 0x207465, 0x207661, 0x207665, - 0x20766F, 0x207765, 0x207A69, 0x61616E, 0x616172, 0x616E20, 0x616E64, 0x617220, 0x617420, 0x636874, 0x646520, 0x64656E, 0x646572, 0x652062, 0x652076, 0x65656E, - 0x656572, 0x656E20, 0x657220, 0x657273, 0x657420, 0x67656E, 0x686574, 0x696520, 0x696E20, 0x696E67, 0x697320, 0x6E2062, 0x6E2064, 0x6E2065, 0x6E2068, 0x6E206F, - 0x6E2076, 0x6E6465, 0x6E6720, 0x6F6E64, 0x6F6F72, 0x6F7020, 0x6F7220, 0x736368, 0x737465, 0x742064, 0x746520, 0x74656E, 0x746572, 0x76616E, 0x766572, 0x766F6F, - }, - "nl" - }, - { - { - 0x206174, 0x206176, 0x206465, 0x20656E, 0x206572, 0x20666F, 0x206861, 0x206920, 0x206D65, 0x206F67, 0x2070E5, 0x207365, 0x20736B, 0x20736F, 0x207374, 0x207469, - 0x207669, 0x20E520, 0x616E64, 0x617220, 0x617420, 0x646520, 0x64656E, 0x646574, 0x652073, 0x656420, 0x656E20, 0x656E65, 0x657220, 0x657265, 0x657420, 0x657474, - 0x666F72, 0x67656E, 0x696B6B, 0x696C20, 0x696E67, 0x6B6520, 0x6B6B65, 0x6C6520, 0x6C6C65, 0x6D6564, 0x6D656E, 0x6E2073, 0x6E6520, 0x6E6720, 0x6E6765, 0x6E6E65, - 0x6F6720, 0x6F6D20, 0x6F7220, 0x70E520, 0x722073, 0x726520, 0x736F6D, 0x737465, 0x742073, 0x746520, 0x74656E, 0x746572, 0x74696C, 0x747420, 0x747465, 0x766572, - }, - "no" - }, - { - { - 0x206120, 0x20636F, 0x206461, 0x206465, 0x20646F, 0x206520, 0x206573, 0x206D61, 0x206E6F, 0x206F20, 0x207061, 0x20706F, 0x207072, 0x207175, 0x207265, 0x207365, - 0x20756D, 0x612061, 0x612063, 0x612064, 0x612070, 0x616465, 0x61646F, 0x616C20, 0x617220, 0x617261, 0x617320, 0x636F6D, 0x636F6E, 0x646120, 0x646520, 0x646F20, - 0x646F73, 0x652061, 0x652064, 0x656D20, 0x656E74, 0x657320, 0x657374, 0x696120, 0x696361, 0x6D656E, 0x6E7465, 0x6E746F, 0x6F2061, 0x6F2063, 0x6F2064, 0x6F2065, - 0x6F2070, 0x6F7320, 0x706172, 0x717565, 0x726120, 0x726573, 0x732061, 0x732064, 0x732065, 0x732070, 0x737461, 0x746520, 0x746F20, 0x756520, 0xE36F20, 0xE7E36F, - }, - "pt" - }, - { - { - 0x206174, 0x206176, 0x206465, 0x20656E, 0x2066F6, 0x206861, 0x206920, 0x20696E, 0x206B6F, 0x206D65, 0x206F63, 0x2070E5, 0x20736B, 0x20736F, 0x207374, 0x207469, - 0x207661, 0x207669, 0x20E472, 0x616465, 0x616E20, 0x616E64, 0x617220, 0x617474, 0x636820, 0x646520, 0x64656E, 0x646572, 0x646574, 0x656420, 0x656E20, 0x657220, - 0x657420, 0x66F672, 0x67656E, 0x696C6C, 0x696E67, 0x6B6120, 0x6C6C20, 0x6D6564, 0x6E2073, 0x6E6120, 0x6E6465, 0x6E6720, 0x6E6765, 0x6E696E, 0x6F6368, 0x6F6D20, - 0x6F6E20, 0x70E520, 0x722061, 0x722073, 0x726120, 0x736B61, 0x736F6D, 0x742073, 0x746120, 0x746520, 0x746572, 0x74696C, 0x747420, 0x766172, 0xE47220, 0xF67220, - }, - "sv" - } -}; - - -static const NGramsPlusLang ngrams_8859_2[] = { - { - { - 0x206120, 0x206279, 0x20646F, 0x206A65, 0x206E61, 0x206E65, 0x206F20, 0x206F64, 0x20706F, 0x207072, 0x2070F8, 0x20726F, 0x207365, 0x20736F, 0x207374, 0x20746F, - 0x207620, 0x207679, 0x207A61, 0x612070, 0x636520, 0x636820, 0x652070, 0x652073, 0x652076, 0x656D20, 0x656EED, 0x686F20, 0x686F64, 0x697374, 0x6A6520, 0x6B7465, - 0x6C6520, 0x6C6920, 0x6E6120, 0x6EE920, 0x6EEC20, 0x6EED20, 0x6F2070, 0x6F646E, 0x6F6A69, 0x6F7374, 0x6F7520, 0x6F7661, 0x706F64, 0x706F6A, 0x70726F, 0x70F865, - 0x736520, 0x736F75, 0x737461, 0x737469, 0x73746E, 0x746572, 0x746EED, 0x746F20, 0x752070, 0xBE6520, 0xE16EED, 0xE9686F, 0xED2070, 0xED2073, 0xED6D20, 0xF86564, - }, - "cs" - }, - { - { - 0x206120, 0x20617A, 0x206265, 0x206567, 0x20656C, 0x206665, 0x206861, 0x20686F, 0x206973, 0x206B65, 0x206B69, 0x206BF6, 0x206C65, 0x206D61, 0x206D65, 0x206D69, - 0x206E65, 0x20737A, 0x207465, 0x20E973, 0x612061, 0x61206B, 0x61206D, 0x612073, 0x616B20, 0x616E20, 0x617A20, 0x62616E, 0x62656E, 0x656779, 0x656B20, 0x656C20, - 0x656C65, 0x656D20, 0x656E20, 0x657265, 0x657420, 0x657465, 0x657474, 0x677920, 0x686F67, 0x696E74, 0x697320, 0x6B2061, 0x6BF67A, 0x6D6567, 0x6D696E, 0x6E2061, - 0x6E616B, 0x6E656B, 0x6E656D, 0x6E7420, 0x6F6779, 0x732061, 0x737A65, 0x737A74, 0x737AE1, 0x73E967, 0x742061, 0x747420, 0x74E173, 0x7A6572, 0xE16E20, 0xE97320, - }, - "hu" - }, - { - { - 0x20637A, 0x20646F, 0x206920, 0x206A65, 0x206B6F, 0x206D61, 0x206D69, 0x206E61, 0x206E69, 0x206F64, 0x20706F, 0x207072, 0x207369, 0x207720, 0x207769, 0x207779, - 0x207A20, 0x207A61, 0x612070, 0x612077, 0x616E69, 0x636820, 0x637A65, 0x637A79, 0x646F20, 0x647A69, 0x652070, 0x652073, 0x652077, 0x65207A, 0x65676F, 0x656A20, - 0x656D20, 0x656E69, 0x676F20, 0x696120, 0x696520, 0x69656A, 0x6B6120, 0x6B6920, 0x6B6965, 0x6D6965, 0x6E6120, 0x6E6961, 0x6E6965, 0x6F2070, 0x6F7761, 0x6F7769, - 0x706F6C, 0x707261, 0x70726F, 0x70727A, 0x727A65, 0x727A79, 0x7369EA, 0x736B69, 0x737461, 0x776965, 0x796368, 0x796D20, 0x7A6520, 0x7A6965, 0x7A7920, 0xF37720, - }, - "pl" - }, - { - { - 0x206120, 0x206163, 0x206361, 0x206365, 0x20636F, 0x206375, 0x206465, 0x206469, 0x206C61, 0x206D61, 0x207065, 0x207072, 0x207365, 0x2073E3, 0x20756E, 0x20BA69, - 0x20EE6E, 0x612063, 0x612064, 0x617265, 0x617420, 0x617465, 0x617520, 0x636172, 0x636F6E, 0x637520, 0x63E320, 0x646520, 0x652061, 0x652063, 0x652064, 0x652070, - 0x652073, 0x656120, 0x656920, 0x656C65, 0x656E74, 0x657374, 0x692061, 0x692063, 0x692064, 0x692070, 0x696520, 0x696920, 0x696E20, 0x6C6120, 0x6C6520, 0x6C6F72, - 0x6C7569, 0x6E6520, 0x6E7472, 0x6F7220, 0x70656E, 0x726520, 0x726561, 0x727520, 0x73E320, 0x746520, 0x747275, 0x74E320, 0x756920, 0x756C20, 0xBA6920, 0xEE6E20, - }, - "ro" - } -}; - -static const int32_t ngrams_8859_5_ru[] = { - 0x20D220, 0x20D2DE, 0x20D4DE, 0x20D7D0, 0x20D820, 0x20DAD0, 0x20DADE, 0x20DDD0, 0x20DDD5, 0x20DED1, 0x20DFDE, 0x20DFE0, 0x20E0D0, 0x20E1DE, 0x20E1E2, 0x20E2DE, - 0x20E7E2, 0x20EDE2, 0xD0DDD8, 0xD0E2EC, 0xD3DE20, 0xD5DBEC, 0xD5DDD8, 0xD5E1E2, 0xD5E220, 0xD820DF, 0xD8D520, 0xD8D820, 0xD8EF20, 0xDBD5DD, 0xDBD820, 0xDBECDD, - 0xDDD020, 0xDDD520, 0xDDD8D5, 0xDDD8EF, 0xDDDE20, 0xDDDED2, 0xDE20D2, 0xDE20DF, 0xDE20E1, 0xDED220, 0xDED2D0, 0xDED3DE, 0xDED920, 0xDEDBEC, 0xDEDC20, 0xDEE1E2, - 0xDFDEDB, 0xDFE0D5, 0xDFE0D8, 0xDFE0DE, 0xE0D0D2, 0xE0D5D4, 0xE1E2D0, 0xE1E2D2, 0xE1E2D8, 0xE1EF20, 0xE2D5DB, 0xE2DE20, 0xE2DEE0, 0xE2EC20, 0xE7E2DE, 0xEBE520, -}; - -static const int32_t ngrams_8859_6_ar[] = { - 0x20C7E4, 0x20C7E6, 0x20C8C7, 0x20D9E4, 0x20E1EA, 0x20E4E4, 0x20E5E6, 0x20E8C7, 0xC720C7, 0xC7C120, 0xC7CA20, 0xC7D120, 0xC7E420, 0xC7E4C3, 0xC7E4C7, 0xC7E4C8, - 0xC7E4CA, 0xC7E4CC, 0xC7E4CD, 0xC7E4CF, 0xC7E4D3, 0xC7E4D9, 0xC7E4E2, 0xC7E4E5, 0xC7E4E8, 0xC7E4EA, 0xC7E520, 0xC7E620, 0xC7E6CA, 0xC820C7, 0xC920C7, 0xC920E1, - 0xC920E4, 0xC920E5, 0xC920E8, 0xCA20C7, 0xCF20C7, 0xCFC920, 0xD120C7, 0xD1C920, 0xD320C7, 0xD920C7, 0xD9E4E9, 0xE1EA20, 0xE420C7, 0xE4C920, 0xE4E920, 0xE4EA20, - 0xE520C7, 0xE5C720, 0xE5C920, 0xE5E620, 0xE620C7, 0xE720C7, 0xE7C720, 0xE8C7E4, 0xE8E620, 0xE920C7, 0xEA20C7, 0xEA20E5, 0xEA20E8, 0xEAC920, 0xEAD120, 0xEAE620, -}; - -static const int32_t ngrams_8859_7_el[] = { - 0x20E1ED, 0x20E1F0, 0x20E3E9, 0x20E4E9, 0x20E5F0, 0x20E720, 0x20EAE1, 0x20ECE5, 0x20EDE1, 0x20EF20, 0x20F0E1, 0x20F0EF, 0x20F0F1, 0x20F3F4, 0x20F3F5, 0x20F4E7, - 0x20F4EF, 0xDFE120, 0xE120E1, 0xE120F4, 0xE1E920, 0xE1ED20, 0xE1F0FC, 0xE1F220, 0xE3E9E1, 0xE5E920, 0xE5F220, 0xE720F4, 0xE7ED20, 0xE7F220, 0xE920F4, 0xE9E120, - 0xE9EADE, 0xE9F220, 0xEAE1E9, 0xEAE1F4, 0xECE520, 0xED20E1, 0xED20E5, 0xED20F0, 0xEDE120, 0xEFF220, 0xEFF520, 0xF0EFF5, 0xF0F1EF, 0xF0FC20, 0xF220E1, 0xF220E5, - 0xF220EA, 0xF220F0, 0xF220F4, 0xF3E520, 0xF3E720, 0xF3F4EF, 0xF4E120, 0xF4E1E9, 0xF4E7ED, 0xF4E7F2, 0xF4E9EA, 0xF4EF20, 0xF4EFF5, 0xF4F9ED, 0xF9ED20, 0xFEED20, -}; - -static const int32_t ngrams_8859_8_I_he[] = { - 0x20E0E5, 0x20E0E7, 0x20E0E9, 0x20E0FA, 0x20E1E9, 0x20E1EE, 0x20E4E0, 0x20E4E5, 0x20E4E9, 0x20E4EE, 0x20E4F2, 0x20E4F9, 0x20E4FA, 0x20ECE0, 0x20ECE4, 0x20EEE0, - 0x20F2EC, 0x20F9EC, 0xE0FA20, 0xE420E0, 0xE420E1, 0xE420E4, 0xE420EC, 0xE420EE, 0xE420F9, 0xE4E5E0, 0xE5E020, 0xE5ED20, 0xE5EF20, 0xE5F820, 0xE5FA20, 0xE920E4, - 0xE9E420, 0xE9E5FA, 0xE9E9ED, 0xE9ED20, 0xE9EF20, 0xE9F820, 0xE9FA20, 0xEC20E0, 0xEC20E4, 0xECE020, 0xECE420, 0xED20E0, 0xED20E1, 0xED20E4, 0xED20EC, 0xED20EE, - 0xED20F9, 0xEEE420, 0xEF20E4, 0xF0E420, 0xF0E920, 0xF0E9ED, 0xF2EC20, 0xF820E4, 0xF8E9ED, 0xF9EC20, 0xFA20E0, 0xFA20E1, 0xFA20E4, 0xFA20EC, 0xFA20EE, 0xFA20F9, -}; - -static const int32_t ngrams_8859_8_he[] = { - 0x20E0E5, 0x20E0EC, 0x20E4E9, 0x20E4EC, 0x20E4EE, 0x20E4F0, 0x20E9F0, 0x20ECF2, 0x20ECF9, 0x20EDE5, 0x20EDE9, 0x20EFE5, 0x20EFE9, 0x20F8E5, 0x20F8E9, 0x20FAE0, - 0x20FAE5, 0x20FAE9, 0xE020E4, 0xE020EC, 0xE020ED, 0xE020FA, 0xE0E420, 0xE0E5E4, 0xE0EC20, 0xE0EE20, 0xE120E4, 0xE120ED, 0xE120FA, 0xE420E4, 0xE420E9, 0xE420EC, - 0xE420ED, 0xE420EF, 0xE420F8, 0xE420FA, 0xE4EC20, 0xE5E020, 0xE5E420, 0xE7E020, 0xE9E020, 0xE9E120, 0xE9E420, 0xEC20E4, 0xEC20ED, 0xEC20FA, 0xECF220, 0xECF920, - 0xEDE9E9, 0xEDE9F0, 0xEDE9F8, 0xEE20E4, 0xEE20ED, 0xEE20FA, 0xEEE120, 0xEEE420, 0xF2E420, 0xF920E4, 0xF920ED, 0xF920FA, 0xF9E420, 0xFAE020, 0xFAE420, 0xFAE5E9, -}; - -static const int32_t ngrams_8859_9_tr[] = { - 0x206261, 0x206269, 0x206275, 0x206461, 0x206465, 0x206765, 0x206861, 0x20696C, 0x206B61, 0x206B6F, 0x206D61, 0x206F6C, 0x207361, 0x207461, 0x207665, 0x207961, - 0x612062, 0x616B20, 0x616C61, 0x616D61, 0x616E20, 0x616EFD, 0x617220, 0x617261, 0x6172FD, 0x6173FD, 0x617961, 0x626972, 0x646120, 0x646520, 0x646920, 0x652062, - 0x65206B, 0x656469, 0x656E20, 0x657220, 0x657269, 0x657369, 0x696C65, 0x696E20, 0x696E69, 0x697220, 0x6C616E, 0x6C6172, 0x6C6520, 0x6C6572, 0x6E2061, 0x6E2062, - 0x6E206B, 0x6E6461, 0x6E6465, 0x6E6520, 0x6E6920, 0x6E696E, 0x6EFD20, 0x72696E, 0x72FD6E, 0x766520, 0x796120, 0x796F72, 0xFD6E20, 0xFD6E64, 0xFD6EFD, 0xFDF0FD, -}; - -CharsetRecog_8859_1::~CharsetRecog_8859_1() -{ - // nothing to do -} - -UBool CharsetRecog_8859_1::match(InputText *textIn, CharsetMatch *results) const { - const char *name = textIn->fC1Bytes? "windows-1252" : "ISO-8859-1"; - uint32_t i; - int32_t bestConfidenceSoFar = -1; - for (i=0; i < UPRV_LENGTHOF(ngrams_8859_1) ; i++) { - const int32_t *ngrams = ngrams_8859_1[i].ngrams; - const char *lang = ngrams_8859_1[i].lang; - int32_t confidence = match_sbcs(textIn, ngrams, charMap_8859_1); - if (confidence > bestConfidenceSoFar) { - results->set(textIn, this, confidence, name, lang); - bestConfidenceSoFar = confidence; - } - } - return (bestConfidenceSoFar > 0); -} - -const char *CharsetRecog_8859_1::getName() const -{ - return "ISO-8859-1"; -} - - -CharsetRecog_8859_2::~CharsetRecog_8859_2() -{ - // nothing to do -} - -UBool CharsetRecog_8859_2::match(InputText *textIn, CharsetMatch *results) const { - const char *name = textIn->fC1Bytes? "windows-1250" : "ISO-8859-2"; - uint32_t i; - int32_t bestConfidenceSoFar = -1; - for (i=0; i < UPRV_LENGTHOF(ngrams_8859_2) ; i++) { - const int32_t *ngrams = ngrams_8859_2[i].ngrams; - const char *lang = ngrams_8859_2[i].lang; - int32_t confidence = match_sbcs(textIn, ngrams, charMap_8859_2); - if (confidence > bestConfidenceSoFar) { - results->set(textIn, this, confidence, name, lang); - bestConfidenceSoFar = confidence; - } - } - return (bestConfidenceSoFar > 0); -} - -const char *CharsetRecog_8859_2::getName() const -{ - return "ISO-8859-2"; -} - - -CharsetRecog_8859_5::~CharsetRecog_8859_5() -{ - // nothing to do -} - -const char *CharsetRecog_8859_5::getName() const -{ - return "ISO-8859-5"; -} - -CharsetRecog_8859_5_ru::~CharsetRecog_8859_5_ru() -{ - // nothing to do -} - -const char *CharsetRecog_8859_5_ru::getLanguage() const -{ - return "ru"; -} - -UBool CharsetRecog_8859_5_ru::match(InputText *textIn, CharsetMatch *results) const -{ - int32_t confidence = match_sbcs(textIn, ngrams_8859_5_ru, charMap_8859_5); - results->set(textIn, this, confidence); - return (confidence > 0); -} - -CharsetRecog_8859_6::~CharsetRecog_8859_6() -{ - // nothing to do -} - -const char *CharsetRecog_8859_6::getName() const -{ - return "ISO-8859-6"; -} - -CharsetRecog_8859_6_ar::~CharsetRecog_8859_6_ar() -{ - // nothing to do -} - -const char *CharsetRecog_8859_6_ar::getLanguage() const -{ - return "ar"; -} - -UBool CharsetRecog_8859_6_ar::match(InputText *textIn, CharsetMatch *results) const -{ - int32_t confidence = match_sbcs(textIn, ngrams_8859_6_ar, charMap_8859_6); - results->set(textIn, this, confidence); - return (confidence > 0); -} - -CharsetRecog_8859_7::~CharsetRecog_8859_7() -{ - // nothing to do -} - -const char *CharsetRecog_8859_7::getName() const -{ - return "ISO-8859-7"; -} - -CharsetRecog_8859_7_el::~CharsetRecog_8859_7_el() -{ - // nothing to do -} - -const char *CharsetRecog_8859_7_el::getLanguage() const -{ - return "el"; -} - -UBool CharsetRecog_8859_7_el::match(InputText *textIn, CharsetMatch *results) const -{ - const char *name = textIn->fC1Bytes? "windows-1253" : "ISO-8859-7"; - int32_t confidence = match_sbcs(textIn, ngrams_8859_7_el, charMap_8859_7); - results->set(textIn, this, confidence, name, "el"); - return (confidence > 0); -} - -CharsetRecog_8859_8::~CharsetRecog_8859_8() -{ - // nothing to do -} - -const char *CharsetRecog_8859_8::getName() const -{ - return "ISO-8859-8"; -} - -CharsetRecog_8859_8_I_he::~CharsetRecog_8859_8_I_he () -{ - // nothing to do -} - -const char *CharsetRecog_8859_8_I_he::getName() const -{ - return "ISO-8859-8-I"; -} - -const char *CharsetRecog_8859_8_I_he::getLanguage() const -{ - return "he"; -} - -UBool CharsetRecog_8859_8_I_he::match(InputText *textIn, CharsetMatch *results) const -{ - const char *name = textIn->fC1Bytes? "windows-1255" : "ISO-8859-8-I"; - int32_t confidence = match_sbcs(textIn, ngrams_8859_8_I_he, charMap_8859_8); - results->set(textIn, this, confidence, name, "he"); - return (confidence > 0); -} - -CharsetRecog_8859_8_he::~CharsetRecog_8859_8_he() -{ - // od ot gnihton -} - -const char *CharsetRecog_8859_8_he::getLanguage() const -{ - return "he"; -} - -UBool CharsetRecog_8859_8_he::match(InputText *textIn, CharsetMatch *results) const -{ - const char *name = textIn->fC1Bytes? "windows-1255" : "ISO-8859-8"; - int32_t confidence = match_sbcs(textIn, ngrams_8859_8_he, charMap_8859_8); - results->set(textIn, this, confidence, name, "he"); - return (confidence > 0); -} - -CharsetRecog_8859_9::~CharsetRecog_8859_9() -{ - // nothing to do -} - -const char *CharsetRecog_8859_9::getName() const -{ - return "ISO-8859-9"; -} - -CharsetRecog_8859_9_tr::~CharsetRecog_8859_9_tr () -{ - // nothing to do -} - -const char *CharsetRecog_8859_9_tr::getLanguage() const -{ - return "tr"; -} - -UBool CharsetRecog_8859_9_tr::match(InputText *textIn, CharsetMatch *results) const -{ - const char *name = textIn->fC1Bytes? "windows-1254" : "ISO-8859-9"; - int32_t confidence = match_sbcs(textIn, ngrams_8859_9_tr, charMap_8859_9); - results->set(textIn, this, confidence, name, "tr"); - return (confidence > 0); -} - -CharsetRecog_windows_1256::~CharsetRecog_windows_1256() -{ - // nothing to do -} - -const char *CharsetRecog_windows_1256::getName() const -{ - return "windows-1256"; -} - -const char *CharsetRecog_windows_1256::getLanguage() const -{ - return "ar"; -} - -UBool CharsetRecog_windows_1256::match(InputText *textIn, CharsetMatch *results) const -{ - int32_t confidence = match_sbcs(textIn, ngrams_windows_1256, charMap_windows_1256); - results->set(textIn, this, confidence); - return (confidence > 0); -} - -CharsetRecog_windows_1251::~CharsetRecog_windows_1251() -{ - // nothing to do -} - -const char *CharsetRecog_windows_1251::getName() const -{ - return "windows-1251"; -} - -const char *CharsetRecog_windows_1251::getLanguage() const -{ - return "ru"; -} - -UBool CharsetRecog_windows_1251::match(InputText *textIn, CharsetMatch *results) const -{ - int32_t confidence = match_sbcs(textIn, ngrams_windows_1251, charMap_windows_1251); - results->set(textIn, this, confidence); - return (confidence > 0); -} - -CharsetRecog_KOI8_R::~CharsetRecog_KOI8_R() -{ - // nothing to do -} - -const char *CharsetRecog_KOI8_R::getName() const -{ - return "KOI8-R"; -} - -const char *CharsetRecog_KOI8_R::getLanguage() const -{ - return "ru"; -} - -UBool CharsetRecog_KOI8_R::match(InputText *textIn, CharsetMatch *results) const -{ - int32_t confidence = match_sbcs(textIn, ngrams_KOI8_R, charMap_KOI8_R); - results->set(textIn, this, confidence); - return (confidence > 0); -} - -#if !UCONFIG_ONLY_HTML_CONVERSION -CharsetRecog_IBM424_he::~CharsetRecog_IBM424_he() -{ - // nothing to do -} - -const char *CharsetRecog_IBM424_he::getLanguage() const -{ - return "he"; -} - -CharsetRecog_IBM424_he_rtl::~CharsetRecog_IBM424_he_rtl() -{ - // nothing to do -} - -const char *CharsetRecog_IBM424_he_rtl::getName() const -{ - return "IBM424_rtl"; -} - -UBool CharsetRecog_IBM424_he_rtl::match(InputText *textIn, CharsetMatch *results) const -{ - int32_t confidence = match_sbcs(textIn, ngrams_IBM424_he_rtl, charMap_IBM424_he); - results->set(textIn, this, confidence); - return (confidence > 0); -} - -CharsetRecog_IBM424_he_ltr::~CharsetRecog_IBM424_he_ltr() -{ - // nothing to do -} - -const char *CharsetRecog_IBM424_he_ltr::getName() const -{ - return "IBM424_ltr"; -} - -UBool CharsetRecog_IBM424_he_ltr::match(InputText *textIn, CharsetMatch *results) const -{ - int32_t confidence = match_sbcs(textIn, ngrams_IBM424_he_ltr, charMap_IBM424_he); - results->set(textIn, this, confidence); - return (confidence > 0); -} - -CharsetRecog_IBM420_ar::~CharsetRecog_IBM420_ar() -{ - // nothing to do -} - -const char *CharsetRecog_IBM420_ar::getLanguage() const -{ - return "ar"; -} - - -int32_t CharsetRecog_IBM420_ar::match_sbcs(InputText *det, const int32_t ngrams[], const uint8_t byteMap[]) const -{ - NGramParser_IBM420 parser(ngrams, byteMap); - int32_t result; - - result = parser.parse(det); - - return result; -} - -CharsetRecog_IBM420_ar_rtl::~CharsetRecog_IBM420_ar_rtl() -{ - // nothing to do -} - -const char *CharsetRecog_IBM420_ar_rtl::getName() const -{ - return "IBM420_rtl"; -} - -UBool CharsetRecog_IBM420_ar_rtl::match(InputText *textIn, CharsetMatch *results) const -{ - int32_t confidence = match_sbcs(textIn, ngrams_IBM420_ar_rtl, charMap_IBM420_ar); - results->set(textIn, this, confidence); - return (confidence > 0); -} - -CharsetRecog_IBM420_ar_ltr::~CharsetRecog_IBM420_ar_ltr() -{ - // nothing to do -} - -const char *CharsetRecog_IBM420_ar_ltr::getName() const -{ - return "IBM420_ltr"; -} - -UBool CharsetRecog_IBM420_ar_ltr::match(InputText *textIn, CharsetMatch *results) const -{ - int32_t confidence = match_sbcs(textIn, ngrams_IBM420_ar_ltr, charMap_IBM420_ar); - results->set(textIn, this, confidence); - return (confidence > 0); -} -#endif - -U_NAMESPACE_END -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2016, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#include "unicode/utypes.h" + +#include "cmemory.h" + +#if !UCONFIG_NO_CONVERSION +#include "csrsbcs.h" +#include "csmatch.h" + +#define N_GRAM_SIZE 3 +#define N_GRAM_MASK 0xFFFFFF + +U_NAMESPACE_BEGIN + +NGramParser::NGramParser(const int32_t *theNgramList, const uint8_t *theCharMap) + : ngram(0), byteIndex(0) +{ + ngramList = theNgramList; + charMap = theCharMap; + + ngramCount = hitCount = 0; +} + +NGramParser::~NGramParser() +{ +} + +/* + * Binary search for value in table, which must have exactly 64 entries. + */ + +int32_t NGramParser::search(const int32_t *table, int32_t value) +{ + int32_t index = 0; + + if (table[index + 32] <= value) { + index += 32; + } + + if (table[index + 16] <= value) { + index += 16; + } + + if (table[index + 8] <= value) { + index += 8; + } + + if (table[index + 4] <= value) { + index += 4; + } + + if (table[index + 2] <= value) { + index += 2; + } + + if (table[index + 1] <= value) { + index += 1; + } + + if (table[index] > value) { + index -= 1; + } + + if (index < 0 || table[index] != value) { + return -1; + } + + return index; +} + +void NGramParser::lookup(int32_t thisNgram) +{ + ngramCount += 1; + + if (search(ngramList, thisNgram) >= 0) { + hitCount += 1; + } + +} + +void NGramParser::addByte(int32_t b) +{ + ngram = ((ngram << 8) + b) & N_GRAM_MASK; + lookup(ngram); +} + +int32_t NGramParser::nextByte(InputText *det) +{ + if (byteIndex >= det->fInputLen) { + return -1; + } + + return det->fInputBytes[byteIndex++]; +} + +void NGramParser::parseCharacters(InputText *det) +{ + int32_t b; + bool ignoreSpace = false; + + while ((b = nextByte(det)) >= 0) { + uint8_t mb = charMap[b]; + + // TODO: 0x20 might not be a space in all character sets... + if (mb != 0) { + if (!(mb == 0x20 && ignoreSpace)) { + addByte(mb); + } + + ignoreSpace = (mb == 0x20); + } + } +} + +int32_t NGramParser::parse(InputText *det) +{ + parseCharacters(det); + + // TODO: Is this OK? The buffer could have ended in the middle of a word... + addByte(0x20); + + double rawPercent = (double) hitCount / (double) ngramCount; + + // if (rawPercent <= 2.0) { + // return 0; + // } + + // TODO - This is a bit of a hack to take care of a case + // were we were getting a confidence of 135... + if (rawPercent > 0.33) { + return 98; + } + + return (int32_t) (rawPercent * 300.0); +} + +#if !UCONFIG_ONLY_HTML_CONVERSION +static const uint8_t unshapeMap_IBM420[] = { +/* -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F */ +/* 0- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 1- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 2- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 3- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 4- */ 0x40, 0x40, 0x42, 0x42, 0x44, 0x45, 0x46, 0x47, 0x47, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, +/* 5- */ 0x50, 0x49, 0x52, 0x53, 0x54, 0x55, 0x56, 0x56, 0x58, 0x58, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, +/* 6- */ 0x60, 0x61, 0x62, 0x63, 0x63, 0x65, 0x65, 0x67, 0x67, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, +/* 7- */ 0x69, 0x71, 0x71, 0x73, 0x74, 0x75, 0x76, 0x77, 0x77, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, +/* 8- */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x80, 0x8B, 0x8B, 0x8D, 0x8D, 0x8F, +/* 9- */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A, 0x9A, 0x9E, 0x9E, +/* A- */ 0x9E, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0x9E, 0xAB, 0xAB, 0xAD, 0xAD, 0xAF, +/* B- */ 0xAF, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xB1, 0xBB, 0xBB, 0xBD, 0xBD, 0xBF, +/* C- */ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xBF, 0xCC, 0xBF, 0xCE, 0xCF, +/* D- */ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDA, 0xDC, 0xDC, 0xDC, 0xDF, +/* E- */ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, +/* F- */ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, +}; + +NGramParser_IBM420::NGramParser_IBM420(const int32_t *theNgramList, const uint8_t *theCharMap):NGramParser(theNgramList, theCharMap) +{ + alef = 0x00; +} + +NGramParser_IBM420::~NGramParser_IBM420() {} + +int32_t NGramParser_IBM420::isLamAlef(int32_t b) +{ + if(b == 0xB2 || b == 0xB3){ + return 0x47; + }else if(b == 0xB4 || b == 0xB5){ + return 0x49; + }else if(b == 0xB8 || b == 0xB9){ + return 0x56; + }else + return 0x00; +} + +/* +* Arabic shaping needs to be done manually. Cannot call ArabicShaping class +* because CharsetDetector is dealing with bytes not Unicode code points. We could +* convert the bytes to Unicode code points but that would leave us dependent +* on CharsetICU which we try to avoid. IBM420 converter amongst different versions +* of JDK can produce different results and therefore is also avoided. +*/ +int32_t NGramParser_IBM420::nextByte(InputText *det) +{ + + if (byteIndex >= det->fInputLen || det->fInputBytes[byteIndex] == 0) { + return -1; + } + int next; + + alef = isLamAlef(det->fInputBytes[byteIndex]); + if(alef != 0x00) + next = 0xB1 & 0xFF; + else + next = unshapeMap_IBM420[det->fInputBytes[byteIndex]& 0xFF] & 0xFF; + + byteIndex++; + + return next; +} + +void NGramParser_IBM420::parseCharacters(InputText *det) +{ + int32_t b; + bool ignoreSpace = false; + + while ((b = nextByte(det)) >= 0) { + uint8_t mb = charMap[b]; + + // TODO: 0x20 might not be a space in all character sets... + if (mb != 0) { + if (!(mb == 0x20 && ignoreSpace)) { + addByte(mb); + } + ignoreSpace = (mb == 0x20); + } + + if(alef != 0x00){ + mb = charMap[alef & 0xFF]; + + // TODO: 0x20 might not be a space in all character sets... + if (mb != 0) { + if (!(mb == 0x20 && ignoreSpace)) { + addByte(mb); + } + + ignoreSpace = (mb == 0x20); + } + + } + } +} +#endif + +CharsetRecog_sbcs::CharsetRecog_sbcs() +{ + // nothing else to do +} + +CharsetRecog_sbcs::~CharsetRecog_sbcs() +{ + // nothing to do +} + +int32_t CharsetRecog_sbcs::match_sbcs(InputText *det, const int32_t ngrams[], const uint8_t byteMap[]) const +{ + NGramParser parser(ngrams, byteMap); + int32_t result; + + result = parser.parse(det); + + return result; +} + +static const uint8_t charMap_8859_1[] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0xAA, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0xB5, 0x20, 0x20, + 0x20, 0x20, 0xBA, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, +}; + +static const uint8_t charMap_8859_2[] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0xB1, 0x20, 0xB3, 0x20, 0xB5, 0xB6, 0x20, + 0x20, 0xB9, 0xBA, 0xBB, 0xBC, 0x20, 0xBE, 0xBF, + 0x20, 0xB1, 0x20, 0xB3, 0x20, 0xB5, 0xB6, 0xB7, + 0x20, 0xB9, 0xBA, 0xBB, 0xBC, 0x20, 0xBE, 0xBF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x20, +}; + +static const uint8_t charMap_8859_5[] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x20, 0xFE, 0xFF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0x20, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x20, 0xFE, 0xFF, +}; + +static const uint8_t charMap_8859_6[] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + 0xD8, 0xD9, 0xDA, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +}; + +static const uint8_t charMap_8859_7[] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0xA1, 0xA2, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDC, 0x20, + 0xDD, 0xDE, 0xDF, 0x20, 0xFC, 0x20, 0xFD, 0xFE, + 0xC0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0x20, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x20, +}; + +static const uint8_t charMap_8859_8[] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0xB5, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0x20, 0x20, 0x20, 0x20, 0x20, +}; + +static const uint8_t charMap_8859_9[] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0xAA, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0xB5, 0x20, 0x20, + 0x20, 0x20, 0xBA, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x69, 0xFE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, +}; + +static const int32_t ngrams_windows_1251[] = { + 0x20E220, 0x20E2EE, 0x20E4EE, 0x20E7E0, 0x20E820, 0x20EAE0, 0x20EAEE, 0x20EDE0, 0x20EDE5, 0x20EEE1, 0x20EFEE, 0x20EFF0, 0x20F0E0, 0x20F1EE, 0x20F1F2, 0x20F2EE, + 0x20F7F2, 0x20FDF2, 0xE0EDE8, 0xE0F2FC, 0xE3EE20, 0xE5EBFC, 0xE5EDE8, 0xE5F1F2, 0xE5F220, 0xE820EF, 0xE8E520, 0xE8E820, 0xE8FF20, 0xEBE5ED, 0xEBE820, 0xEBFCED, + 0xEDE020, 0xEDE520, 0xEDE8E5, 0xEDE8FF, 0xEDEE20, 0xEDEEE2, 0xEE20E2, 0xEE20EF, 0xEE20F1, 0xEEE220, 0xEEE2E0, 0xEEE3EE, 0xEEE920, 0xEEEBFC, 0xEEEC20, 0xEEF1F2, + 0xEFEEEB, 0xEFF0E5, 0xEFF0E8, 0xEFF0EE, 0xF0E0E2, 0xF0E5E4, 0xF1F2E0, 0xF1F2E2, 0xF1F2E8, 0xF1FF20, 0xF2E5EB, 0xF2EE20, 0xF2EEF0, 0xF2FC20, 0xF7F2EE, 0xFBF520, +}; + +static const uint8_t charMap_windows_1251[] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x90, 0x83, 0x20, 0x83, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x9A, 0x20, 0x9C, 0x9D, 0x9E, 0x9F, + 0x90, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x9A, 0x20, 0x9C, 0x9D, 0x9E, 0x9F, + 0x20, 0xA2, 0xA2, 0xBC, 0x20, 0xB4, 0x20, 0x20, + 0xB8, 0x20, 0xBA, 0x20, 0x20, 0x20, 0x20, 0xBF, + 0x20, 0x20, 0xB3, 0xB3, 0xB4, 0xB5, 0x20, 0x20, + 0xB8, 0x20, 0xBA, 0x20, 0xBC, 0xBE, 0xBE, 0xBF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, +}; + +static const int32_t ngrams_windows_1256[] = { + 0x20C7E1, 0x20C7E4, 0x20C8C7, 0x20DAE1, 0x20DDED, 0x20E1E1, 0x20E3E4, 0x20E6C7, 0xC720C7, 0xC7C120, 0xC7CA20, 0xC7D120, 0xC7E120, 0xC7E1C3, 0xC7E1C7, 0xC7E1C8, + 0xC7E1CA, 0xC7E1CC, 0xC7E1CD, 0xC7E1CF, 0xC7E1D3, 0xC7E1DA, 0xC7E1DE, 0xC7E1E3, 0xC7E1E6, 0xC7E1ED, 0xC7E320, 0xC7E420, 0xC7E4CA, 0xC820C7, 0xC920C7, 0xC920DD, + 0xC920E1, 0xC920E3, 0xC920E6, 0xCA20C7, 0xCF20C7, 0xCFC920, 0xD120C7, 0xD1C920, 0xD320C7, 0xDA20C7, 0xDAE1EC, 0xDDED20, 0xE120C7, 0xE1C920, 0xE1EC20, 0xE1ED20, + 0xE320C7, 0xE3C720, 0xE3C920, 0xE3E420, 0xE420C7, 0xE520C7, 0xE5C720, 0xE6C7E1, 0xE6E420, 0xEC20C7, 0xED20C7, 0xED20E3, 0xED20E6, 0xEDC920, 0xEDD120, 0xEDE420, +}; + +static const uint8_t charMap_windows_1256[] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x81, 0x20, 0x83, 0x20, 0x20, 0x20, 0x20, + 0x88, 0x20, 0x8A, 0x20, 0x9C, 0x8D, 0x8E, 0x8F, + 0x90, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x98, 0x20, 0x9A, 0x20, 0x9C, 0x20, 0x20, 0x9F, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0xAA, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0xB5, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0x20, + 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0x20, 0x20, 0x20, 0x20, 0xF4, 0x20, 0x20, 0x20, + 0x20, 0xF9, 0x20, 0xFB, 0xFC, 0x20, 0x20, 0xFF, +}; + +static const int32_t ngrams_KOI8_R[] = { + 0x20C4CF, 0x20C920, 0x20CBC1, 0x20CBCF, 0x20CEC1, 0x20CEC5, 0x20CFC2, 0x20D0CF, 0x20D0D2, 0x20D2C1, 0x20D3CF, 0x20D3D4, 0x20D4CF, 0x20D720, 0x20D7CF, 0x20DAC1, + 0x20DCD4, 0x20DED4, 0xC1CEC9, 0xC1D4D8, 0xC5CCD8, 0xC5CEC9, 0xC5D3D4, 0xC5D420, 0xC7CF20, 0xC920D0, 0xC9C520, 0xC9C920, 0xC9D120, 0xCCC5CE, 0xCCC920, 0xCCD8CE, + 0xCEC120, 0xCEC520, 0xCEC9C5, 0xCEC9D1, 0xCECF20, 0xCECFD7, 0xCF20D0, 0xCF20D3, 0xCF20D7, 0xCFC7CF, 0xCFCA20, 0xCFCCD8, 0xCFCD20, 0xCFD3D4, 0xCFD720, 0xCFD7C1, + 0xD0CFCC, 0xD0D2C5, 0xD0D2C9, 0xD0D2CF, 0xD2C1D7, 0xD2C5C4, 0xD3D120, 0xD3D4C1, 0xD3D4C9, 0xD3D4D7, 0xD4C5CC, 0xD4CF20, 0xD4CFD2, 0xD4D820, 0xD9C820, 0xDED4CF, +}; + +static const uint8_t charMap_KOI8_R[] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0xA3, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0xA3, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, +}; + +#if !UCONFIG_ONLY_HTML_CONVERSION +static const int32_t ngrams_IBM424_he_rtl[] = { + 0x404146, 0x404148, 0x404151, 0x404171, 0x404251, 0x404256, 0x404541, 0x404546, 0x404551, 0x404556, 0x404562, 0x404569, 0x404571, 0x405441, 0x405445, 0x405641, + 0x406254, 0x406954, 0x417140, 0x454041, 0x454042, 0x454045, 0x454054, 0x454056, 0x454069, 0x454641, 0x464140, 0x465540, 0x465740, 0x466840, 0x467140, 0x514045, + 0x514540, 0x514671, 0x515155, 0x515540, 0x515740, 0x516840, 0x517140, 0x544041, 0x544045, 0x544140, 0x544540, 0x554041, 0x554042, 0x554045, 0x554054, 0x554056, + 0x554069, 0x564540, 0x574045, 0x584540, 0x585140, 0x585155, 0x625440, 0x684045, 0x685155, 0x695440, 0x714041, 0x714042, 0x714045, 0x714054, 0x714056, 0x714069, +}; + +static const int32_t ngrams_IBM424_he_ltr[] = { + 0x404146, 0x404154, 0x404551, 0x404554, 0x404556, 0x404558, 0x405158, 0x405462, 0x405469, 0x405546, 0x405551, 0x405746, 0x405751, 0x406846, 0x406851, 0x407141, + 0x407146, 0x407151, 0x414045, 0x414054, 0x414055, 0x414071, 0x414540, 0x414645, 0x415440, 0x415640, 0x424045, 0x424055, 0x424071, 0x454045, 0x454051, 0x454054, + 0x454055, 0x454057, 0x454068, 0x454071, 0x455440, 0x464140, 0x464540, 0x484140, 0x514140, 0x514240, 0x514540, 0x544045, 0x544055, 0x544071, 0x546240, 0x546940, + 0x555151, 0x555158, 0x555168, 0x564045, 0x564055, 0x564071, 0x564240, 0x564540, 0x624540, 0x694045, 0x694055, 0x694071, 0x694540, 0x714140, 0x714540, 0x714651, +}; + +static const uint8_t charMap_IBM424_he[] = { +/* -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F */ +/* 0- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 1- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 2- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 3- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 4- */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 5- */ 0x40, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 6- */ 0x40, 0x40, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 7- */ 0x40, 0x71, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x40, 0x40, +/* 8- */ 0x40, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 9- */ 0x40, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* A- */ 0xA0, 0x40, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* B- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* C- */ 0x40, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* D- */ 0x40, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* E- */ 0x40, 0x40, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* F- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +}; + +static const int32_t ngrams_IBM420_ar_rtl[] = { + 0x4056B1, 0x4056BD, 0x405856, 0x409AB1, 0x40ABDC, 0x40B1B1, 0x40BBBD, 0x40CF56, 0x564056, 0x564640, 0x566340, 0x567540, 0x56B140, 0x56B149, 0x56B156, 0x56B158, + 0x56B163, 0x56B167, 0x56B169, 0x56B173, 0x56B178, 0x56B19A, 0x56B1AD, 0x56B1BB, 0x56B1CF, 0x56B1DC, 0x56BB40, 0x56BD40, 0x56BD63, 0x584056, 0x624056, 0x6240AB, + 0x6240B1, 0x6240BB, 0x6240CF, 0x634056, 0x734056, 0x736240, 0x754056, 0x756240, 0x784056, 0x9A4056, 0x9AB1DA, 0xABDC40, 0xB14056, 0xB16240, 0xB1DA40, 0xB1DC40, + 0xBB4056, 0xBB5640, 0xBB6240, 0xBBBD40, 0xBD4056, 0xBF4056, 0xBF5640, 0xCF56B1, 0xCFBD40, 0xDA4056, 0xDC4056, 0xDC40BB, 0xDC40CF, 0xDC6240, 0xDC7540, 0xDCBD40, +}; + +static const int32_t ngrams_IBM420_ar_ltr[] = { + 0x404656, 0x4056BB, 0x4056BF, 0x406273, 0x406275, 0x4062B1, 0x4062BB, 0x4062DC, 0x406356, 0x407556, 0x4075DC, 0x40B156, 0x40BB56, 0x40BD56, 0x40BDBB, 0x40BDCF, + 0x40BDDC, 0x40DAB1, 0x40DCAB, 0x40DCB1, 0x49B156, 0x564056, 0x564058, 0x564062, 0x564063, 0x564073, 0x564075, 0x564078, 0x56409A, 0x5640B1, 0x5640BB, 0x5640BD, + 0x5640BF, 0x5640DA, 0x5640DC, 0x565840, 0x56B156, 0x56CF40, 0x58B156, 0x63B156, 0x63BD56, 0x67B156, 0x69B156, 0x73B156, 0x78B156, 0x9AB156, 0xAB4062, 0xADB156, + 0xB14062, 0xB15640, 0xB156CF, 0xB19A40, 0xB1B140, 0xBB4062, 0xBB40DC, 0xBBB156, 0xBD5640, 0xBDBB40, 0xCF4062, 0xCF40DC, 0xCFB156, 0xDAB19A, 0xDCAB40, 0xDCB156 +}; + +static const uint8_t charMap_IBM420_ar[]= { +/* -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F */ +/* 0- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 1- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 2- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 3- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 4- */ 0x40, 0x40, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 5- */ 0x40, 0x51, 0x52, 0x40, 0x40, 0x55, 0x56, 0x57, 0x58, 0x59, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 6- */ 0x40, 0x40, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 7- */ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, +/* 8- */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, +/* 9- */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, +/* A- */ 0xA0, 0x40, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, +/* B- */ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0x40, 0x40, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, +/* C- */ 0x40, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x40, 0xCB, 0x40, 0xCD, 0x40, 0xCF, +/* D- */ 0x40, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, +/* E- */ 0x40, 0x40, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xEA, 0xEB, 0x40, 0xED, 0xEE, 0xEF, +/* F- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xFB, 0xFC, 0xFD, 0xFE, 0x40, +}; +#endif + +//ISO-8859-1,2,5,6,7,8,9 Ngrams + +struct NGramsPlusLang { + const int32_t ngrams[64]; + const char * lang; +}; + +static const NGramsPlusLang ngrams_8859_1[] = { + { + { + 0x206120, 0x20616E, 0x206265, 0x20636F, 0x20666F, 0x206861, 0x206865, 0x20696E, 0x206D61, 0x206F66, 0x207072, 0x207265, 0x207361, 0x207374, 0x207468, 0x20746F, + 0x207768, 0x616964, 0x616C20, 0x616E20, 0x616E64, 0x617320, 0x617420, 0x617465, 0x617469, 0x642061, 0x642074, 0x652061, 0x652073, 0x652074, 0x656420, 0x656E74, + 0x657220, 0x657320, 0x666F72, 0x686174, 0x686520, 0x686572, 0x696420, 0x696E20, 0x696E67, 0x696F6E, 0x697320, 0x6E2061, 0x6E2074, 0x6E6420, 0x6E6720, 0x6E7420, + 0x6F6620, 0x6F6E20, 0x6F7220, 0x726520, 0x727320, 0x732061, 0x732074, 0x736169, 0x737420, 0x742074, 0x746572, 0x746861, 0x746865, 0x74696F, 0x746F20, 0x747320, + }, + "en" + }, + { + { + 0x206166, 0x206174, 0x206465, 0x20656E, 0x206572, 0x20666F, 0x206861, 0x206920, 0x206D65, 0x206F67, 0x2070E5, 0x207369, 0x207374, 0x207469, 0x207669, 0x616620, + 0x616E20, 0x616E64, 0x617220, 0x617420, 0x646520, 0x64656E, 0x646572, 0x646574, 0x652073, 0x656420, 0x656465, 0x656E20, 0x656E64, 0x657220, 0x657265, 0x657320, + 0x657420, 0x666F72, 0x676520, 0x67656E, 0x676572, 0x696765, 0x696C20, 0x696E67, 0x6B6520, 0x6B6B65, 0x6C6572, 0x6C6967, 0x6C6C65, 0x6D6564, 0x6E6465, 0x6E6520, + 0x6E6720, 0x6E6765, 0x6F6720, 0x6F6D20, 0x6F7220, 0x70E520, 0x722064, 0x722065, 0x722073, 0x726520, 0x737465, 0x742073, 0x746520, 0x746572, 0x74696C, 0x766572, + }, + "da" + }, + { + { + 0x20616E, 0x206175, 0x206265, 0x206461, 0x206465, 0x206469, 0x206569, 0x206765, 0x206861, 0x20696E, 0x206D69, 0x207363, 0x207365, 0x20756E, 0x207665, 0x20766F, + 0x207765, 0x207A75, 0x626572, 0x636820, 0x636865, 0x636874, 0x646173, 0x64656E, 0x646572, 0x646965, 0x652064, 0x652073, 0x65696E, 0x656974, 0x656E20, 0x657220, + 0x657320, 0x67656E, 0x68656E, 0x687420, 0x696368, 0x696520, 0x696E20, 0x696E65, 0x697420, 0x6C6963, 0x6C6C65, 0x6E2061, 0x6E2064, 0x6E2073, 0x6E6420, 0x6E6465, + 0x6E6520, 0x6E6720, 0x6E6765, 0x6E7465, 0x722064, 0x726465, 0x726569, 0x736368, 0x737465, 0x742064, 0x746520, 0x74656E, 0x746572, 0x756E64, 0x756E67, 0x766572, + }, + "de" + }, + { + { + 0x206120, 0x206361, 0x20636F, 0x206465, 0x20656C, 0x20656E, 0x206573, 0x20696E, 0x206C61, 0x206C6F, 0x207061, 0x20706F, 0x207072, 0x207175, 0x207265, 0x207365, + 0x20756E, 0x207920, 0x612063, 0x612064, 0x612065, 0x61206C, 0x612070, 0x616369, 0x61646F, 0x616C20, 0x617220, 0x617320, 0x6369F3, 0x636F6E, 0x646520, 0x64656C, + 0x646F20, 0x652064, 0x652065, 0x65206C, 0x656C20, 0x656E20, 0x656E74, 0x657320, 0x657374, 0x69656E, 0x69F36E, 0x6C6120, 0x6C6F73, 0x6E2065, 0x6E7465, 0x6F2064, + 0x6F2065, 0x6F6E20, 0x6F7220, 0x6F7320, 0x706172, 0x717565, 0x726120, 0x726573, 0x732064, 0x732065, 0x732070, 0x736520, 0x746520, 0x746F20, 0x756520, 0xF36E20, + }, + "es" + }, + { + { + 0x206175, 0x20636F, 0x206461, 0x206465, 0x206475, 0x20656E, 0x206574, 0x206C61, 0x206C65, 0x207061, 0x20706F, 0x207072, 0x207175, 0x207365, 0x20736F, 0x20756E, + 0x20E020, 0x616E74, 0x617469, 0x636520, 0x636F6E, 0x646520, 0x646573, 0x647520, 0x652061, 0x652063, 0x652064, 0x652065, 0x65206C, 0x652070, 0x652073, 0x656E20, + 0x656E74, 0x657220, 0x657320, 0x657420, 0x657572, 0x696F6E, 0x697320, 0x697420, 0x6C6120, 0x6C6520, 0x6C6573, 0x6D656E, 0x6E2064, 0x6E6520, 0x6E7320, 0x6E7420, + 0x6F6E20, 0x6F6E74, 0x6F7572, 0x717565, 0x72206C, 0x726520, 0x732061, 0x732064, 0x732065, 0x73206C, 0x732070, 0x742064, 0x746520, 0x74696F, 0x756520, 0x757220, + }, + "fr" + }, + { + { + 0x20616C, 0x206368, 0x20636F, 0x206465, 0x206469, 0x206520, 0x20696C, 0x20696E, 0x206C61, 0x207065, 0x207072, 0x20756E, 0x612063, 0x612064, 0x612070, 0x612073, + 0x61746F, 0x636865, 0x636F6E, 0x64656C, 0x646920, 0x652061, 0x652063, 0x652064, 0x652069, 0x65206C, 0x652070, 0x652073, 0x656C20, 0x656C6C, 0x656E74, 0x657220, + 0x686520, 0x692061, 0x692063, 0x692064, 0x692073, 0x696120, 0x696C20, 0x696E20, 0x696F6E, 0x6C6120, 0x6C6520, 0x6C6920, 0x6C6C61, 0x6E6520, 0x6E6920, 0x6E6F20, + 0x6E7465, 0x6F2061, 0x6F2064, 0x6F2069, 0x6F2073, 0x6F6E20, 0x6F6E65, 0x706572, 0x726120, 0x726520, 0x736920, 0x746120, 0x746520, 0x746920, 0x746F20, 0x7A696F, + }, + "it" + }, + { + { + 0x20616C, 0x206265, 0x206461, 0x206465, 0x206469, 0x206565, 0x20656E, 0x206765, 0x206865, 0x20696E, 0x206D61, 0x206D65, 0x206F70, 0x207465, 0x207661, 0x207665, + 0x20766F, 0x207765, 0x207A69, 0x61616E, 0x616172, 0x616E20, 0x616E64, 0x617220, 0x617420, 0x636874, 0x646520, 0x64656E, 0x646572, 0x652062, 0x652076, 0x65656E, + 0x656572, 0x656E20, 0x657220, 0x657273, 0x657420, 0x67656E, 0x686574, 0x696520, 0x696E20, 0x696E67, 0x697320, 0x6E2062, 0x6E2064, 0x6E2065, 0x6E2068, 0x6E206F, + 0x6E2076, 0x6E6465, 0x6E6720, 0x6F6E64, 0x6F6F72, 0x6F7020, 0x6F7220, 0x736368, 0x737465, 0x742064, 0x746520, 0x74656E, 0x746572, 0x76616E, 0x766572, 0x766F6F, + }, + "nl" + }, + { + { + 0x206174, 0x206176, 0x206465, 0x20656E, 0x206572, 0x20666F, 0x206861, 0x206920, 0x206D65, 0x206F67, 0x2070E5, 0x207365, 0x20736B, 0x20736F, 0x207374, 0x207469, + 0x207669, 0x20E520, 0x616E64, 0x617220, 0x617420, 0x646520, 0x64656E, 0x646574, 0x652073, 0x656420, 0x656E20, 0x656E65, 0x657220, 0x657265, 0x657420, 0x657474, + 0x666F72, 0x67656E, 0x696B6B, 0x696C20, 0x696E67, 0x6B6520, 0x6B6B65, 0x6C6520, 0x6C6C65, 0x6D6564, 0x6D656E, 0x6E2073, 0x6E6520, 0x6E6720, 0x6E6765, 0x6E6E65, + 0x6F6720, 0x6F6D20, 0x6F7220, 0x70E520, 0x722073, 0x726520, 0x736F6D, 0x737465, 0x742073, 0x746520, 0x74656E, 0x746572, 0x74696C, 0x747420, 0x747465, 0x766572, + }, + "no" + }, + { + { + 0x206120, 0x20636F, 0x206461, 0x206465, 0x20646F, 0x206520, 0x206573, 0x206D61, 0x206E6F, 0x206F20, 0x207061, 0x20706F, 0x207072, 0x207175, 0x207265, 0x207365, + 0x20756D, 0x612061, 0x612063, 0x612064, 0x612070, 0x616465, 0x61646F, 0x616C20, 0x617220, 0x617261, 0x617320, 0x636F6D, 0x636F6E, 0x646120, 0x646520, 0x646F20, + 0x646F73, 0x652061, 0x652064, 0x656D20, 0x656E74, 0x657320, 0x657374, 0x696120, 0x696361, 0x6D656E, 0x6E7465, 0x6E746F, 0x6F2061, 0x6F2063, 0x6F2064, 0x6F2065, + 0x6F2070, 0x6F7320, 0x706172, 0x717565, 0x726120, 0x726573, 0x732061, 0x732064, 0x732065, 0x732070, 0x737461, 0x746520, 0x746F20, 0x756520, 0xE36F20, 0xE7E36F, + }, + "pt" + }, + { + { + 0x206174, 0x206176, 0x206465, 0x20656E, 0x2066F6, 0x206861, 0x206920, 0x20696E, 0x206B6F, 0x206D65, 0x206F63, 0x2070E5, 0x20736B, 0x20736F, 0x207374, 0x207469, + 0x207661, 0x207669, 0x20E472, 0x616465, 0x616E20, 0x616E64, 0x617220, 0x617474, 0x636820, 0x646520, 0x64656E, 0x646572, 0x646574, 0x656420, 0x656E20, 0x657220, + 0x657420, 0x66F672, 0x67656E, 0x696C6C, 0x696E67, 0x6B6120, 0x6C6C20, 0x6D6564, 0x6E2073, 0x6E6120, 0x6E6465, 0x6E6720, 0x6E6765, 0x6E696E, 0x6F6368, 0x6F6D20, + 0x6F6E20, 0x70E520, 0x722061, 0x722073, 0x726120, 0x736B61, 0x736F6D, 0x742073, 0x746120, 0x746520, 0x746572, 0x74696C, 0x747420, 0x766172, 0xE47220, 0xF67220, + }, + "sv" + } +}; + + +static const NGramsPlusLang ngrams_8859_2[] = { + { + { + 0x206120, 0x206279, 0x20646F, 0x206A65, 0x206E61, 0x206E65, 0x206F20, 0x206F64, 0x20706F, 0x207072, 0x2070F8, 0x20726F, 0x207365, 0x20736F, 0x207374, 0x20746F, + 0x207620, 0x207679, 0x207A61, 0x612070, 0x636520, 0x636820, 0x652070, 0x652073, 0x652076, 0x656D20, 0x656EED, 0x686F20, 0x686F64, 0x697374, 0x6A6520, 0x6B7465, + 0x6C6520, 0x6C6920, 0x6E6120, 0x6EE920, 0x6EEC20, 0x6EED20, 0x6F2070, 0x6F646E, 0x6F6A69, 0x6F7374, 0x6F7520, 0x6F7661, 0x706F64, 0x706F6A, 0x70726F, 0x70F865, + 0x736520, 0x736F75, 0x737461, 0x737469, 0x73746E, 0x746572, 0x746EED, 0x746F20, 0x752070, 0xBE6520, 0xE16EED, 0xE9686F, 0xED2070, 0xED2073, 0xED6D20, 0xF86564, + }, + "cs" + }, + { + { + 0x206120, 0x20617A, 0x206265, 0x206567, 0x20656C, 0x206665, 0x206861, 0x20686F, 0x206973, 0x206B65, 0x206B69, 0x206BF6, 0x206C65, 0x206D61, 0x206D65, 0x206D69, + 0x206E65, 0x20737A, 0x207465, 0x20E973, 0x612061, 0x61206B, 0x61206D, 0x612073, 0x616B20, 0x616E20, 0x617A20, 0x62616E, 0x62656E, 0x656779, 0x656B20, 0x656C20, + 0x656C65, 0x656D20, 0x656E20, 0x657265, 0x657420, 0x657465, 0x657474, 0x677920, 0x686F67, 0x696E74, 0x697320, 0x6B2061, 0x6BF67A, 0x6D6567, 0x6D696E, 0x6E2061, + 0x6E616B, 0x6E656B, 0x6E656D, 0x6E7420, 0x6F6779, 0x732061, 0x737A65, 0x737A74, 0x737AE1, 0x73E967, 0x742061, 0x747420, 0x74E173, 0x7A6572, 0xE16E20, 0xE97320, + }, + "hu" + }, + { + { + 0x20637A, 0x20646F, 0x206920, 0x206A65, 0x206B6F, 0x206D61, 0x206D69, 0x206E61, 0x206E69, 0x206F64, 0x20706F, 0x207072, 0x207369, 0x207720, 0x207769, 0x207779, + 0x207A20, 0x207A61, 0x612070, 0x612077, 0x616E69, 0x636820, 0x637A65, 0x637A79, 0x646F20, 0x647A69, 0x652070, 0x652073, 0x652077, 0x65207A, 0x65676F, 0x656A20, + 0x656D20, 0x656E69, 0x676F20, 0x696120, 0x696520, 0x69656A, 0x6B6120, 0x6B6920, 0x6B6965, 0x6D6965, 0x6E6120, 0x6E6961, 0x6E6965, 0x6F2070, 0x6F7761, 0x6F7769, + 0x706F6C, 0x707261, 0x70726F, 0x70727A, 0x727A65, 0x727A79, 0x7369EA, 0x736B69, 0x737461, 0x776965, 0x796368, 0x796D20, 0x7A6520, 0x7A6965, 0x7A7920, 0xF37720, + }, + "pl" + }, + { + { + 0x206120, 0x206163, 0x206361, 0x206365, 0x20636F, 0x206375, 0x206465, 0x206469, 0x206C61, 0x206D61, 0x207065, 0x207072, 0x207365, 0x2073E3, 0x20756E, 0x20BA69, + 0x20EE6E, 0x612063, 0x612064, 0x617265, 0x617420, 0x617465, 0x617520, 0x636172, 0x636F6E, 0x637520, 0x63E320, 0x646520, 0x652061, 0x652063, 0x652064, 0x652070, + 0x652073, 0x656120, 0x656920, 0x656C65, 0x656E74, 0x657374, 0x692061, 0x692063, 0x692064, 0x692070, 0x696520, 0x696920, 0x696E20, 0x6C6120, 0x6C6520, 0x6C6F72, + 0x6C7569, 0x6E6520, 0x6E7472, 0x6F7220, 0x70656E, 0x726520, 0x726561, 0x727520, 0x73E320, 0x746520, 0x747275, 0x74E320, 0x756920, 0x756C20, 0xBA6920, 0xEE6E20, + }, + "ro" + } +}; + +static const int32_t ngrams_8859_5_ru[] = { + 0x20D220, 0x20D2DE, 0x20D4DE, 0x20D7D0, 0x20D820, 0x20DAD0, 0x20DADE, 0x20DDD0, 0x20DDD5, 0x20DED1, 0x20DFDE, 0x20DFE0, 0x20E0D0, 0x20E1DE, 0x20E1E2, 0x20E2DE, + 0x20E7E2, 0x20EDE2, 0xD0DDD8, 0xD0E2EC, 0xD3DE20, 0xD5DBEC, 0xD5DDD8, 0xD5E1E2, 0xD5E220, 0xD820DF, 0xD8D520, 0xD8D820, 0xD8EF20, 0xDBD5DD, 0xDBD820, 0xDBECDD, + 0xDDD020, 0xDDD520, 0xDDD8D5, 0xDDD8EF, 0xDDDE20, 0xDDDED2, 0xDE20D2, 0xDE20DF, 0xDE20E1, 0xDED220, 0xDED2D0, 0xDED3DE, 0xDED920, 0xDEDBEC, 0xDEDC20, 0xDEE1E2, + 0xDFDEDB, 0xDFE0D5, 0xDFE0D8, 0xDFE0DE, 0xE0D0D2, 0xE0D5D4, 0xE1E2D0, 0xE1E2D2, 0xE1E2D8, 0xE1EF20, 0xE2D5DB, 0xE2DE20, 0xE2DEE0, 0xE2EC20, 0xE7E2DE, 0xEBE520, +}; + +static const int32_t ngrams_8859_6_ar[] = { + 0x20C7E4, 0x20C7E6, 0x20C8C7, 0x20D9E4, 0x20E1EA, 0x20E4E4, 0x20E5E6, 0x20E8C7, 0xC720C7, 0xC7C120, 0xC7CA20, 0xC7D120, 0xC7E420, 0xC7E4C3, 0xC7E4C7, 0xC7E4C8, + 0xC7E4CA, 0xC7E4CC, 0xC7E4CD, 0xC7E4CF, 0xC7E4D3, 0xC7E4D9, 0xC7E4E2, 0xC7E4E5, 0xC7E4E8, 0xC7E4EA, 0xC7E520, 0xC7E620, 0xC7E6CA, 0xC820C7, 0xC920C7, 0xC920E1, + 0xC920E4, 0xC920E5, 0xC920E8, 0xCA20C7, 0xCF20C7, 0xCFC920, 0xD120C7, 0xD1C920, 0xD320C7, 0xD920C7, 0xD9E4E9, 0xE1EA20, 0xE420C7, 0xE4C920, 0xE4E920, 0xE4EA20, + 0xE520C7, 0xE5C720, 0xE5C920, 0xE5E620, 0xE620C7, 0xE720C7, 0xE7C720, 0xE8C7E4, 0xE8E620, 0xE920C7, 0xEA20C7, 0xEA20E5, 0xEA20E8, 0xEAC920, 0xEAD120, 0xEAE620, +}; + +static const int32_t ngrams_8859_7_el[] = { + 0x20E1ED, 0x20E1F0, 0x20E3E9, 0x20E4E9, 0x20E5F0, 0x20E720, 0x20EAE1, 0x20ECE5, 0x20EDE1, 0x20EF20, 0x20F0E1, 0x20F0EF, 0x20F0F1, 0x20F3F4, 0x20F3F5, 0x20F4E7, + 0x20F4EF, 0xDFE120, 0xE120E1, 0xE120F4, 0xE1E920, 0xE1ED20, 0xE1F0FC, 0xE1F220, 0xE3E9E1, 0xE5E920, 0xE5F220, 0xE720F4, 0xE7ED20, 0xE7F220, 0xE920F4, 0xE9E120, + 0xE9EADE, 0xE9F220, 0xEAE1E9, 0xEAE1F4, 0xECE520, 0xED20E1, 0xED20E5, 0xED20F0, 0xEDE120, 0xEFF220, 0xEFF520, 0xF0EFF5, 0xF0F1EF, 0xF0FC20, 0xF220E1, 0xF220E5, + 0xF220EA, 0xF220F0, 0xF220F4, 0xF3E520, 0xF3E720, 0xF3F4EF, 0xF4E120, 0xF4E1E9, 0xF4E7ED, 0xF4E7F2, 0xF4E9EA, 0xF4EF20, 0xF4EFF5, 0xF4F9ED, 0xF9ED20, 0xFEED20, +}; + +static const int32_t ngrams_8859_8_I_he[] = { + 0x20E0E5, 0x20E0E7, 0x20E0E9, 0x20E0FA, 0x20E1E9, 0x20E1EE, 0x20E4E0, 0x20E4E5, 0x20E4E9, 0x20E4EE, 0x20E4F2, 0x20E4F9, 0x20E4FA, 0x20ECE0, 0x20ECE4, 0x20EEE0, + 0x20F2EC, 0x20F9EC, 0xE0FA20, 0xE420E0, 0xE420E1, 0xE420E4, 0xE420EC, 0xE420EE, 0xE420F9, 0xE4E5E0, 0xE5E020, 0xE5ED20, 0xE5EF20, 0xE5F820, 0xE5FA20, 0xE920E4, + 0xE9E420, 0xE9E5FA, 0xE9E9ED, 0xE9ED20, 0xE9EF20, 0xE9F820, 0xE9FA20, 0xEC20E0, 0xEC20E4, 0xECE020, 0xECE420, 0xED20E0, 0xED20E1, 0xED20E4, 0xED20EC, 0xED20EE, + 0xED20F9, 0xEEE420, 0xEF20E4, 0xF0E420, 0xF0E920, 0xF0E9ED, 0xF2EC20, 0xF820E4, 0xF8E9ED, 0xF9EC20, 0xFA20E0, 0xFA20E1, 0xFA20E4, 0xFA20EC, 0xFA20EE, 0xFA20F9, +}; + +static const int32_t ngrams_8859_8_he[] = { + 0x20E0E5, 0x20E0EC, 0x20E4E9, 0x20E4EC, 0x20E4EE, 0x20E4F0, 0x20E9F0, 0x20ECF2, 0x20ECF9, 0x20EDE5, 0x20EDE9, 0x20EFE5, 0x20EFE9, 0x20F8E5, 0x20F8E9, 0x20FAE0, + 0x20FAE5, 0x20FAE9, 0xE020E4, 0xE020EC, 0xE020ED, 0xE020FA, 0xE0E420, 0xE0E5E4, 0xE0EC20, 0xE0EE20, 0xE120E4, 0xE120ED, 0xE120FA, 0xE420E4, 0xE420E9, 0xE420EC, + 0xE420ED, 0xE420EF, 0xE420F8, 0xE420FA, 0xE4EC20, 0xE5E020, 0xE5E420, 0xE7E020, 0xE9E020, 0xE9E120, 0xE9E420, 0xEC20E4, 0xEC20ED, 0xEC20FA, 0xECF220, 0xECF920, + 0xEDE9E9, 0xEDE9F0, 0xEDE9F8, 0xEE20E4, 0xEE20ED, 0xEE20FA, 0xEEE120, 0xEEE420, 0xF2E420, 0xF920E4, 0xF920ED, 0xF920FA, 0xF9E420, 0xFAE020, 0xFAE420, 0xFAE5E9, +}; + +static const int32_t ngrams_8859_9_tr[] = { + 0x206261, 0x206269, 0x206275, 0x206461, 0x206465, 0x206765, 0x206861, 0x20696C, 0x206B61, 0x206B6F, 0x206D61, 0x206F6C, 0x207361, 0x207461, 0x207665, 0x207961, + 0x612062, 0x616B20, 0x616C61, 0x616D61, 0x616E20, 0x616EFD, 0x617220, 0x617261, 0x6172FD, 0x6173FD, 0x617961, 0x626972, 0x646120, 0x646520, 0x646920, 0x652062, + 0x65206B, 0x656469, 0x656E20, 0x657220, 0x657269, 0x657369, 0x696C65, 0x696E20, 0x696E69, 0x697220, 0x6C616E, 0x6C6172, 0x6C6520, 0x6C6572, 0x6E2061, 0x6E2062, + 0x6E206B, 0x6E6461, 0x6E6465, 0x6E6520, 0x6E6920, 0x6E696E, 0x6EFD20, 0x72696E, 0x72FD6E, 0x766520, 0x796120, 0x796F72, 0xFD6E20, 0xFD6E64, 0xFD6EFD, 0xFDF0FD, +}; + +CharsetRecog_8859_1::~CharsetRecog_8859_1() +{ + // nothing to do +} + +UBool CharsetRecog_8859_1::match(InputText *textIn, CharsetMatch *results) const { + const char *name = textIn->fC1Bytes? "windows-1252" : "ISO-8859-1"; + uint32_t i; + int32_t bestConfidenceSoFar = -1; + for (i=0; i < UPRV_LENGTHOF(ngrams_8859_1) ; i++) { + const int32_t *ngrams = ngrams_8859_1[i].ngrams; + const char *lang = ngrams_8859_1[i].lang; + int32_t confidence = match_sbcs(textIn, ngrams, charMap_8859_1); + if (confidence > bestConfidenceSoFar) { + results->set(textIn, this, confidence, name, lang); + bestConfidenceSoFar = confidence; + } + } + return (bestConfidenceSoFar > 0); +} + +const char *CharsetRecog_8859_1::getName() const +{ + return "ISO-8859-1"; +} + + +CharsetRecog_8859_2::~CharsetRecog_8859_2() +{ + // nothing to do +} + +UBool CharsetRecog_8859_2::match(InputText *textIn, CharsetMatch *results) const { + const char *name = textIn->fC1Bytes? "windows-1250" : "ISO-8859-2"; + uint32_t i; + int32_t bestConfidenceSoFar = -1; + for (i=0; i < UPRV_LENGTHOF(ngrams_8859_2) ; i++) { + const int32_t *ngrams = ngrams_8859_2[i].ngrams; + const char *lang = ngrams_8859_2[i].lang; + int32_t confidence = match_sbcs(textIn, ngrams, charMap_8859_2); + if (confidence > bestConfidenceSoFar) { + results->set(textIn, this, confidence, name, lang); + bestConfidenceSoFar = confidence; + } + } + return (bestConfidenceSoFar > 0); +} + +const char *CharsetRecog_8859_2::getName() const +{ + return "ISO-8859-2"; +} + + +CharsetRecog_8859_5::~CharsetRecog_8859_5() +{ + // nothing to do +} + +const char *CharsetRecog_8859_5::getName() const +{ + return "ISO-8859-5"; +} + +CharsetRecog_8859_5_ru::~CharsetRecog_8859_5_ru() +{ + // nothing to do +} + +const char *CharsetRecog_8859_5_ru::getLanguage() const +{ + return "ru"; +} + +UBool CharsetRecog_8859_5_ru::match(InputText *textIn, CharsetMatch *results) const +{ + int32_t confidence = match_sbcs(textIn, ngrams_8859_5_ru, charMap_8859_5); + results->set(textIn, this, confidence); + return (confidence > 0); +} + +CharsetRecog_8859_6::~CharsetRecog_8859_6() +{ + // nothing to do +} + +const char *CharsetRecog_8859_6::getName() const +{ + return "ISO-8859-6"; +} + +CharsetRecog_8859_6_ar::~CharsetRecog_8859_6_ar() +{ + // nothing to do +} + +const char *CharsetRecog_8859_6_ar::getLanguage() const +{ + return "ar"; +} + +UBool CharsetRecog_8859_6_ar::match(InputText *textIn, CharsetMatch *results) const +{ + int32_t confidence = match_sbcs(textIn, ngrams_8859_6_ar, charMap_8859_6); + results->set(textIn, this, confidence); + return (confidence > 0); +} + +CharsetRecog_8859_7::~CharsetRecog_8859_7() +{ + // nothing to do +} + +const char *CharsetRecog_8859_7::getName() const +{ + return "ISO-8859-7"; +} + +CharsetRecog_8859_7_el::~CharsetRecog_8859_7_el() +{ + // nothing to do +} + +const char *CharsetRecog_8859_7_el::getLanguage() const +{ + return "el"; +} + +UBool CharsetRecog_8859_7_el::match(InputText *textIn, CharsetMatch *results) const +{ + const char *name = textIn->fC1Bytes? "windows-1253" : "ISO-8859-7"; + int32_t confidence = match_sbcs(textIn, ngrams_8859_7_el, charMap_8859_7); + results->set(textIn, this, confidence, name, "el"); + return (confidence > 0); +} + +CharsetRecog_8859_8::~CharsetRecog_8859_8() +{ + // nothing to do +} + +const char *CharsetRecog_8859_8::getName() const +{ + return "ISO-8859-8"; +} + +CharsetRecog_8859_8_I_he::~CharsetRecog_8859_8_I_he () +{ + // nothing to do +} + +const char *CharsetRecog_8859_8_I_he::getName() const +{ + return "ISO-8859-8-I"; +} + +const char *CharsetRecog_8859_8_I_he::getLanguage() const +{ + return "he"; +} + +UBool CharsetRecog_8859_8_I_he::match(InputText *textIn, CharsetMatch *results) const +{ + const char *name = textIn->fC1Bytes? "windows-1255" : "ISO-8859-8-I"; + int32_t confidence = match_sbcs(textIn, ngrams_8859_8_I_he, charMap_8859_8); + results->set(textIn, this, confidence, name, "he"); + return (confidence > 0); +} + +CharsetRecog_8859_8_he::~CharsetRecog_8859_8_he() +{ + // od ot gnihton +} + +const char *CharsetRecog_8859_8_he::getLanguage() const +{ + return "he"; +} + +UBool CharsetRecog_8859_8_he::match(InputText *textIn, CharsetMatch *results) const +{ + const char *name = textIn->fC1Bytes? "windows-1255" : "ISO-8859-8"; + int32_t confidence = match_sbcs(textIn, ngrams_8859_8_he, charMap_8859_8); + results->set(textIn, this, confidence, name, "he"); + return (confidence > 0); +} + +CharsetRecog_8859_9::~CharsetRecog_8859_9() +{ + // nothing to do +} + +const char *CharsetRecog_8859_9::getName() const +{ + return "ISO-8859-9"; +} + +CharsetRecog_8859_9_tr::~CharsetRecog_8859_9_tr () +{ + // nothing to do +} + +const char *CharsetRecog_8859_9_tr::getLanguage() const +{ + return "tr"; +} + +UBool CharsetRecog_8859_9_tr::match(InputText *textIn, CharsetMatch *results) const +{ + const char *name = textIn->fC1Bytes? "windows-1254" : "ISO-8859-9"; + int32_t confidence = match_sbcs(textIn, ngrams_8859_9_tr, charMap_8859_9); + results->set(textIn, this, confidence, name, "tr"); + return (confidence > 0); +} + +CharsetRecog_windows_1256::~CharsetRecog_windows_1256() +{ + // nothing to do +} + +const char *CharsetRecog_windows_1256::getName() const +{ + return "windows-1256"; +} + +const char *CharsetRecog_windows_1256::getLanguage() const +{ + return "ar"; +} + +UBool CharsetRecog_windows_1256::match(InputText *textIn, CharsetMatch *results) const +{ + int32_t confidence = match_sbcs(textIn, ngrams_windows_1256, charMap_windows_1256); + results->set(textIn, this, confidence); + return (confidence > 0); +} + +CharsetRecog_windows_1251::~CharsetRecog_windows_1251() +{ + // nothing to do +} + +const char *CharsetRecog_windows_1251::getName() const +{ + return "windows-1251"; +} + +const char *CharsetRecog_windows_1251::getLanguage() const +{ + return "ru"; +} + +UBool CharsetRecog_windows_1251::match(InputText *textIn, CharsetMatch *results) const +{ + int32_t confidence = match_sbcs(textIn, ngrams_windows_1251, charMap_windows_1251); + results->set(textIn, this, confidence); + return (confidence > 0); +} + +CharsetRecog_KOI8_R::~CharsetRecog_KOI8_R() +{ + // nothing to do +} + +const char *CharsetRecog_KOI8_R::getName() const +{ + return "KOI8-R"; +} + +const char *CharsetRecog_KOI8_R::getLanguage() const +{ + return "ru"; +} + +UBool CharsetRecog_KOI8_R::match(InputText *textIn, CharsetMatch *results) const +{ + int32_t confidence = match_sbcs(textIn, ngrams_KOI8_R, charMap_KOI8_R); + results->set(textIn, this, confidence); + return (confidence > 0); +} + +#if !UCONFIG_ONLY_HTML_CONVERSION +CharsetRecog_IBM424_he::~CharsetRecog_IBM424_he() +{ + // nothing to do +} + +const char *CharsetRecog_IBM424_he::getLanguage() const +{ + return "he"; +} + +CharsetRecog_IBM424_he_rtl::~CharsetRecog_IBM424_he_rtl() +{ + // nothing to do +} + +const char *CharsetRecog_IBM424_he_rtl::getName() const +{ + return "IBM424_rtl"; +} + +UBool CharsetRecog_IBM424_he_rtl::match(InputText *textIn, CharsetMatch *results) const +{ + int32_t confidence = match_sbcs(textIn, ngrams_IBM424_he_rtl, charMap_IBM424_he); + results->set(textIn, this, confidence); + return (confidence > 0); +} + +CharsetRecog_IBM424_he_ltr::~CharsetRecog_IBM424_he_ltr() +{ + // nothing to do +} + +const char *CharsetRecog_IBM424_he_ltr::getName() const +{ + return "IBM424_ltr"; +} + +UBool CharsetRecog_IBM424_he_ltr::match(InputText *textIn, CharsetMatch *results) const +{ + int32_t confidence = match_sbcs(textIn, ngrams_IBM424_he_ltr, charMap_IBM424_he); + results->set(textIn, this, confidence); + return (confidence > 0); +} + +CharsetRecog_IBM420_ar::~CharsetRecog_IBM420_ar() +{ + // nothing to do +} + +const char *CharsetRecog_IBM420_ar::getLanguage() const +{ + return "ar"; +} + + +int32_t CharsetRecog_IBM420_ar::match_sbcs(InputText *det, const int32_t ngrams[], const uint8_t byteMap[]) const +{ + NGramParser_IBM420 parser(ngrams, byteMap); + int32_t result; + + result = parser.parse(det); + + return result; +} + +CharsetRecog_IBM420_ar_rtl::~CharsetRecog_IBM420_ar_rtl() +{ + // nothing to do +} + +const char *CharsetRecog_IBM420_ar_rtl::getName() const +{ + return "IBM420_rtl"; +} + +UBool CharsetRecog_IBM420_ar_rtl::match(InputText *textIn, CharsetMatch *results) const +{ + int32_t confidence = match_sbcs(textIn, ngrams_IBM420_ar_rtl, charMap_IBM420_ar); + results->set(textIn, this, confidence); + return (confidence > 0); +} + +CharsetRecog_IBM420_ar_ltr::~CharsetRecog_IBM420_ar_ltr() +{ + // nothing to do +} + +const char *CharsetRecog_IBM420_ar_ltr::getName() const +{ + return "IBM420_ltr"; +} + +UBool CharsetRecog_IBM420_ar_ltr::match(InputText *textIn, CharsetMatch *results) const +{ + int32_t confidence = match_sbcs(textIn, ngrams_IBM420_ar_ltr, charMap_IBM420_ar); + results->set(textIn, this, confidence); + return (confidence > 0); +} +#endif + +U_NAMESPACE_END +#endif + diff --git a/deps/icu-small/source/i18n/csrsbcs.h b/deps/icu-small/source/i18n/csrsbcs.h index 96f982c59bd14b..dc553c8aa852cf 100644 --- a/deps/icu-small/source/i18n/csrsbcs.h +++ b/deps/icu-small/source/i18n/csrsbcs.h @@ -1,295 +1,295 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2015, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#ifndef __CSRSBCS_H -#define __CSRSBCS_H - -#include "unicode/uobject.h" - -#if !UCONFIG_NO_CONVERSION - -#include "csrecog.h" - -U_NAMESPACE_BEGIN - -class NGramParser : public UMemory -{ -private: - int32_t ngram; - const int32_t *ngramList; - - int32_t ngramCount; - int32_t hitCount; - -protected: - int32_t byteIndex; - const uint8_t *charMap; - - void addByte(int32_t b); - -public: - NGramParser(const int32_t *theNgramList, const uint8_t *theCharMap); - virtual ~NGramParser(); - -private: - /* - * Binary search for value in table, which must have exactly 64 entries. - */ - int32_t search(const int32_t *table, int32_t value); - - void lookup(int32_t thisNgram); - - virtual int32_t nextByte(InputText *det); - virtual void parseCharacters(InputText *det); - -public: - int32_t parse(InputText *det); - -}; - -#if !UCONFIG_ONLY_HTML_CONVERSION -class NGramParser_IBM420 : public NGramParser -{ -public: - NGramParser_IBM420(const int32_t *theNgramList, const uint8_t *theCharMap); - ~NGramParser_IBM420(); - -private: - int32_t alef; - int32_t isLamAlef(int32_t b); - int32_t nextByte(InputText *det) override; - void parseCharacters(InputText *det) override; -}; -#endif - - -class CharsetRecog_sbcs : public CharsetRecognizer -{ -public: - CharsetRecog_sbcs(); - virtual ~CharsetRecog_sbcs(); - virtual const char *getName() const override = 0; - virtual UBool match(InputText *det, CharsetMatch *results) const override = 0; - virtual int32_t match_sbcs(InputText *det, const int32_t ngrams[], const uint8_t charMap[]) const; -}; - -class CharsetRecog_8859_1 : public CharsetRecog_sbcs -{ -public: - virtual ~CharsetRecog_8859_1(); - const char *getName() const override; - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - -class CharsetRecog_8859_2 : public CharsetRecog_sbcs -{ -public: - virtual ~CharsetRecog_8859_2(); - const char *getName() const override; - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - -class CharsetRecog_8859_5 : public CharsetRecog_sbcs -{ -public: - virtual ~CharsetRecog_8859_5(); - const char *getName() const override; -}; - -class CharsetRecog_8859_6 : public CharsetRecog_sbcs -{ -public: - virtual ~CharsetRecog_8859_6(); - - const char *getName() const override; -}; - -class CharsetRecog_8859_7 : public CharsetRecog_sbcs -{ -public: - virtual ~CharsetRecog_8859_7(); - - const char *getName() const override; -}; - -class CharsetRecog_8859_8 : public CharsetRecog_sbcs -{ -public: - virtual ~CharsetRecog_8859_8(); - - virtual const char *getName() const override; -}; - -class CharsetRecog_8859_9 : public CharsetRecog_sbcs -{ -public: - virtual ~CharsetRecog_8859_9(); - - const char *getName() const override; -}; - - - -class CharsetRecog_8859_5_ru : public CharsetRecog_8859_5 -{ -public: - virtual ~CharsetRecog_8859_5_ru(); - - const char *getLanguage() const override; - - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - -class CharsetRecog_8859_6_ar : public CharsetRecog_8859_6 -{ -public: - virtual ~CharsetRecog_8859_6_ar(); - - const char *getLanguage() const override; - - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - -class CharsetRecog_8859_7_el : public CharsetRecog_8859_7 -{ -public: - virtual ~CharsetRecog_8859_7_el(); - - const char *getLanguage() const override; - - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - -class CharsetRecog_8859_8_I_he : public CharsetRecog_8859_8 -{ -public: - virtual ~CharsetRecog_8859_8_I_he(); - - const char *getName() const override; - - const char *getLanguage() const override; - - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - -class CharsetRecog_8859_8_he : public CharsetRecog_8859_8 -{ -public: - virtual ~CharsetRecog_8859_8_he (); - - const char *getLanguage() const override; - - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - -class CharsetRecog_8859_9_tr : public CharsetRecog_8859_9 -{ -public: - virtual ~CharsetRecog_8859_9_tr (); - - const char *getLanguage() const override; - - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - -class CharsetRecog_windows_1256 : public CharsetRecog_sbcs -{ -public: - virtual ~CharsetRecog_windows_1256(); - - const char *getName() const override; - - const char *getLanguage() const override; - - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - -class CharsetRecog_windows_1251 : public CharsetRecog_sbcs -{ -public: - virtual ~CharsetRecog_windows_1251(); - - const char *getName() const override; - - const char *getLanguage() const override; - - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - - -class CharsetRecog_KOI8_R : public CharsetRecog_sbcs -{ -public: - virtual ~CharsetRecog_KOI8_R(); - - const char *getName() const override; - - const char *getLanguage() const override; - - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - -#if !UCONFIG_ONLY_HTML_CONVERSION -class CharsetRecog_IBM424_he : public CharsetRecog_sbcs -{ -public: - virtual ~CharsetRecog_IBM424_he(); - - const char *getLanguage() const override; -}; - -class CharsetRecog_IBM424_he_rtl : public CharsetRecog_IBM424_he { -public: - virtual ~CharsetRecog_IBM424_he_rtl(); - - const char *getName() const override; - - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - -class CharsetRecog_IBM424_he_ltr : public CharsetRecog_IBM424_he { - virtual ~CharsetRecog_IBM424_he_ltr(); - - const char *getName() const override; - - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - -class CharsetRecog_IBM420_ar : public CharsetRecog_sbcs -{ -public: - virtual ~CharsetRecog_IBM420_ar(); - - const char *getLanguage() const override; - int32_t match_sbcs(InputText *det, const int32_t ngrams[], const uint8_t charMap[]) const override; - -}; - -class CharsetRecog_IBM420_ar_rtl : public CharsetRecog_IBM420_ar { -public: - virtual ~CharsetRecog_IBM420_ar_rtl(); - - const char *getName() const override; - - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; - -class CharsetRecog_IBM420_ar_ltr : public CharsetRecog_IBM420_ar { - virtual ~CharsetRecog_IBM420_ar_ltr(); - - const char *getName() const override; - - virtual UBool match(InputText *det, CharsetMatch *results) const override; -}; -#endif - -U_NAMESPACE_END - -#endif /* !UCONFIG_NO_CONVERSION */ -#endif /* __CSRSBCS_H */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2015, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#ifndef __CSRSBCS_H +#define __CSRSBCS_H + +#include "unicode/uobject.h" + +#if !UCONFIG_NO_CONVERSION + +#include "csrecog.h" + +U_NAMESPACE_BEGIN + +class NGramParser : public UMemory +{ +private: + int32_t ngram; + const int32_t *ngramList; + + int32_t ngramCount; + int32_t hitCount; + +protected: + int32_t byteIndex; + const uint8_t *charMap; + + void addByte(int32_t b); + +public: + NGramParser(const int32_t *theNgramList, const uint8_t *theCharMap); + virtual ~NGramParser(); + +private: + /* + * Binary search for value in table, which must have exactly 64 entries. + */ + int32_t search(const int32_t *table, int32_t value); + + void lookup(int32_t thisNgram); + + virtual int32_t nextByte(InputText *det); + virtual void parseCharacters(InputText *det); + +public: + int32_t parse(InputText *det); + +}; + +#if !UCONFIG_ONLY_HTML_CONVERSION +class NGramParser_IBM420 : public NGramParser +{ +public: + NGramParser_IBM420(const int32_t *theNgramList, const uint8_t *theCharMap); + ~NGramParser_IBM420(); + +private: + int32_t alef; + int32_t isLamAlef(int32_t b); + int32_t nextByte(InputText *det) override; + void parseCharacters(InputText *det) override; +}; +#endif + + +class CharsetRecog_sbcs : public CharsetRecognizer +{ +public: + CharsetRecog_sbcs(); + virtual ~CharsetRecog_sbcs(); + virtual const char *getName() const override = 0; + virtual UBool match(InputText *det, CharsetMatch *results) const override = 0; + virtual int32_t match_sbcs(InputText *det, const int32_t ngrams[], const uint8_t charMap[]) const; +}; + +class CharsetRecog_8859_1 : public CharsetRecog_sbcs +{ +public: + virtual ~CharsetRecog_8859_1(); + const char *getName() const override; + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + +class CharsetRecog_8859_2 : public CharsetRecog_sbcs +{ +public: + virtual ~CharsetRecog_8859_2(); + const char *getName() const override; + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + +class CharsetRecog_8859_5 : public CharsetRecog_sbcs +{ +public: + virtual ~CharsetRecog_8859_5(); + const char *getName() const override; +}; + +class CharsetRecog_8859_6 : public CharsetRecog_sbcs +{ +public: + virtual ~CharsetRecog_8859_6(); + + const char *getName() const override; +}; + +class CharsetRecog_8859_7 : public CharsetRecog_sbcs +{ +public: + virtual ~CharsetRecog_8859_7(); + + const char *getName() const override; +}; + +class CharsetRecog_8859_8 : public CharsetRecog_sbcs +{ +public: + virtual ~CharsetRecog_8859_8(); + + virtual const char *getName() const override; +}; + +class CharsetRecog_8859_9 : public CharsetRecog_sbcs +{ +public: + virtual ~CharsetRecog_8859_9(); + + const char *getName() const override; +}; + + + +class CharsetRecog_8859_5_ru : public CharsetRecog_8859_5 +{ +public: + virtual ~CharsetRecog_8859_5_ru(); + + const char *getLanguage() const override; + + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + +class CharsetRecog_8859_6_ar : public CharsetRecog_8859_6 +{ +public: + virtual ~CharsetRecog_8859_6_ar(); + + const char *getLanguage() const override; + + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + +class CharsetRecog_8859_7_el : public CharsetRecog_8859_7 +{ +public: + virtual ~CharsetRecog_8859_7_el(); + + const char *getLanguage() const override; + + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + +class CharsetRecog_8859_8_I_he : public CharsetRecog_8859_8 +{ +public: + virtual ~CharsetRecog_8859_8_I_he(); + + const char *getName() const override; + + const char *getLanguage() const override; + + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + +class CharsetRecog_8859_8_he : public CharsetRecog_8859_8 +{ +public: + virtual ~CharsetRecog_8859_8_he (); + + const char *getLanguage() const override; + + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + +class CharsetRecog_8859_9_tr : public CharsetRecog_8859_9 +{ +public: + virtual ~CharsetRecog_8859_9_tr (); + + const char *getLanguage() const override; + + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + +class CharsetRecog_windows_1256 : public CharsetRecog_sbcs +{ +public: + virtual ~CharsetRecog_windows_1256(); + + const char *getName() const override; + + const char *getLanguage() const override; + + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + +class CharsetRecog_windows_1251 : public CharsetRecog_sbcs +{ +public: + virtual ~CharsetRecog_windows_1251(); + + const char *getName() const override; + + const char *getLanguage() const override; + + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + + +class CharsetRecog_KOI8_R : public CharsetRecog_sbcs +{ +public: + virtual ~CharsetRecog_KOI8_R(); + + const char *getName() const override; + + const char *getLanguage() const override; + + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + +#if !UCONFIG_ONLY_HTML_CONVERSION +class CharsetRecog_IBM424_he : public CharsetRecog_sbcs +{ +public: + virtual ~CharsetRecog_IBM424_he(); + + const char *getLanguage() const override; +}; + +class CharsetRecog_IBM424_he_rtl : public CharsetRecog_IBM424_he { +public: + virtual ~CharsetRecog_IBM424_he_rtl(); + + const char *getName() const override; + + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + +class CharsetRecog_IBM424_he_ltr : public CharsetRecog_IBM424_he { + virtual ~CharsetRecog_IBM424_he_ltr(); + + const char *getName() const override; + + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + +class CharsetRecog_IBM420_ar : public CharsetRecog_sbcs +{ +public: + virtual ~CharsetRecog_IBM420_ar(); + + const char *getLanguage() const override; + int32_t match_sbcs(InputText *det, const int32_t ngrams[], const uint8_t charMap[]) const override; + +}; + +class CharsetRecog_IBM420_ar_rtl : public CharsetRecog_IBM420_ar { +public: + virtual ~CharsetRecog_IBM420_ar_rtl(); + + const char *getName() const override; + + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; + +class CharsetRecog_IBM420_ar_ltr : public CharsetRecog_IBM420_ar { + virtual ~CharsetRecog_IBM420_ar_ltr(); + + const char *getName() const override; + + virtual UBool match(InputText *det, CharsetMatch *results) const override; +}; +#endif + +U_NAMESPACE_END + +#endif /* !UCONFIG_NO_CONVERSION */ +#endif /* __CSRSBCS_H */ diff --git a/deps/icu-small/source/i18n/csrucode.cpp b/deps/icu-small/source/i18n/csrucode.cpp index e0a64aa949a894..c822b318ca3ec6 100644 --- a/deps/icu-small/source/i18n/csrucode.cpp +++ b/deps/icu-small/source/i18n/csrucode.cpp @@ -1,200 +1,200 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2013, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "csrucode.h" -#include "csmatch.h" - -U_NAMESPACE_BEGIN - -CharsetRecog_Unicode::~CharsetRecog_Unicode() -{ - // nothing to do -} - -CharsetRecog_UTF_16_BE::~CharsetRecog_UTF_16_BE() -{ - // nothing to do -} - -const char *CharsetRecog_UTF_16_BE::getName() const -{ - return "UTF-16BE"; -} - -// UTF-16 confidence calculation. Very simple minded, but better than nothing. -// Any 8 bit non-control characters bump the confidence up. These have a zero high byte, -// and are very likely to be UTF-16, although they could also be part of a UTF-32 code. -// NULs are a contra-indication, they will appear commonly if the actual encoding is UTF-32. -// NULs should be rare in actual text. - -static int32_t adjustConfidence(UChar codeUnit, int32_t confidence) { - if (codeUnit == 0) { - confidence -= 10; - } else if ((codeUnit >= 0x20 && codeUnit <= 0xff) || codeUnit == 0x0a) { - confidence += 10; - } - if (confidence < 0) { - confidence = 0; - } else if (confidence > 100) { - confidence = 100; - } - return confidence; -} - - -UBool CharsetRecog_UTF_16_BE::match(InputText* textIn, CharsetMatch *results) const -{ - const uint8_t *input = textIn->fRawInput; - int32_t confidence = 10; - int32_t length = textIn->fRawLength; - - int32_t bytesToCheck = (length > 30) ? 30 : length; - for (int32_t charIndex=0; charIndexset(textIn, this, confidence); - return (confidence > 0); -} - -CharsetRecog_UTF_16_LE::~CharsetRecog_UTF_16_LE() -{ - // nothing to do -} - -const char *CharsetRecog_UTF_16_LE::getName() const -{ - return "UTF-16LE"; -} - -UBool CharsetRecog_UTF_16_LE::match(InputText* textIn, CharsetMatch *results) const -{ - const uint8_t *input = textIn->fRawInput; - int32_t confidence = 10; - int32_t length = textIn->fRawLength; - - int32_t bytesToCheck = (length > 30) ? 30 : length; - for (int32_t charIndex=0; charIndex= 4 && input[2] == 0 && input[3] == 0) { - confidence = 0; // UTF-32 BOM - } - break; - } - confidence = adjustConfidence(codeUnit, confidence); - if (confidence == 0 || confidence == 100) { - break; - } - } - if (bytesToCheck < 4 && confidence < 100) { - confidence = 0; - } - results->set(textIn, this, confidence); - return (confidence > 0); -} - -CharsetRecog_UTF_32::~CharsetRecog_UTF_32() -{ - // nothing to do -} - -UBool CharsetRecog_UTF_32::match(InputText* textIn, CharsetMatch *results) const -{ - const uint8_t *input = textIn->fRawInput; - int32_t limit = (textIn->fRawLength / 4) * 4; - int32_t numValid = 0; - int32_t numInvalid = 0; - bool hasBOM = false; - int32_t confidence = 0; - - if (limit > 0 && getChar(input, 0) == 0x0000FEFFUL) { - hasBOM = true; - } - - for(int32_t i = 0; i < limit; i += 4) { - int32_t ch = getChar(input, i); - - if (ch < 0 || ch >= 0x10FFFF || (ch >= 0xD800 && ch <= 0xDFFF)) { - numInvalid += 1; - } else { - numValid += 1; - } - } - - - // Cook up some sort of confidence score, based on presence of a BOM - // and the existence of valid and/or invalid multi-byte sequences. - if (hasBOM && numInvalid==0) { - confidence = 100; - } else if (hasBOM && numValid > numInvalid*10) { - confidence = 80; - } else if (numValid > 3 && numInvalid == 0) { - confidence = 100; - } else if (numValid > 0 && numInvalid == 0) { - confidence = 80; - } else if (numValid > numInvalid*10) { - // Probably corrupt UTF-32BE data. Valid sequences aren't likely by chance. - confidence = 25; - } - - results->set(textIn, this, confidence); - return (confidence > 0); -} - -CharsetRecog_UTF_32_BE::~CharsetRecog_UTF_32_BE() -{ - // nothing to do -} - -const char *CharsetRecog_UTF_32_BE::getName() const -{ - return "UTF-32BE"; -} - -int32_t CharsetRecog_UTF_32_BE::getChar(const uint8_t *input, int32_t index) const -{ - return input[index + 0] << 24 | input[index + 1] << 16 | - input[index + 2] << 8 | input[index + 3]; -} - -CharsetRecog_UTF_32_LE::~CharsetRecog_UTF_32_LE() -{ - // nothing to do -} - -const char *CharsetRecog_UTF_32_LE::getName() const -{ - return "UTF-32LE"; -} - -int32_t CharsetRecog_UTF_32_LE::getChar(const uint8_t *input, int32_t index) const -{ - return input[index + 3] << 24 | input[index + 2] << 16 | - input[index + 1] << 8 | input[index + 0]; -} - -U_NAMESPACE_END -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2013, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "csrucode.h" +#include "csmatch.h" + +U_NAMESPACE_BEGIN + +CharsetRecog_Unicode::~CharsetRecog_Unicode() +{ + // nothing to do +} + +CharsetRecog_UTF_16_BE::~CharsetRecog_UTF_16_BE() +{ + // nothing to do +} + +const char *CharsetRecog_UTF_16_BE::getName() const +{ + return "UTF-16BE"; +} + +// UTF-16 confidence calculation. Very simple minded, but better than nothing. +// Any 8 bit non-control characters bump the confidence up. These have a zero high byte, +// and are very likely to be UTF-16, although they could also be part of a UTF-32 code. +// NULs are a contra-indication, they will appear commonly if the actual encoding is UTF-32. +// NULs should be rare in actual text. + +static int32_t adjustConfidence(char16_t codeUnit, int32_t confidence) { + if (codeUnit == 0) { + confidence -= 10; + } else if ((codeUnit >= 0x20 && codeUnit <= 0xff) || codeUnit == 0x0a) { + confidence += 10; + } + if (confidence < 0) { + confidence = 0; + } else if (confidence > 100) { + confidence = 100; + } + return confidence; +} + + +UBool CharsetRecog_UTF_16_BE::match(InputText* textIn, CharsetMatch *results) const +{ + const uint8_t *input = textIn->fRawInput; + int32_t confidence = 10; + int32_t length = textIn->fRawLength; + + int32_t bytesToCheck = (length > 30) ? 30 : length; + for (int32_t charIndex=0; charIndexset(textIn, this, confidence); + return (confidence > 0); +} + +CharsetRecog_UTF_16_LE::~CharsetRecog_UTF_16_LE() +{ + // nothing to do +} + +const char *CharsetRecog_UTF_16_LE::getName() const +{ + return "UTF-16LE"; +} + +UBool CharsetRecog_UTF_16_LE::match(InputText* textIn, CharsetMatch *results) const +{ + const uint8_t *input = textIn->fRawInput; + int32_t confidence = 10; + int32_t length = textIn->fRawLength; + + int32_t bytesToCheck = (length > 30) ? 30 : length; + for (int32_t charIndex=0; charIndex= 4 && input[2] == 0 && input[3] == 0) { + confidence = 0; // UTF-32 BOM + } + break; + } + confidence = adjustConfidence(codeUnit, confidence); + if (confidence == 0 || confidence == 100) { + break; + } + } + if (bytesToCheck < 4 && confidence < 100) { + confidence = 0; + } + results->set(textIn, this, confidence); + return (confidence > 0); +} + +CharsetRecog_UTF_32::~CharsetRecog_UTF_32() +{ + // nothing to do +} + +UBool CharsetRecog_UTF_32::match(InputText* textIn, CharsetMatch *results) const +{ + const uint8_t *input = textIn->fRawInput; + int32_t limit = (textIn->fRawLength / 4) * 4; + int32_t numValid = 0; + int32_t numInvalid = 0; + bool hasBOM = false; + int32_t confidence = 0; + + if (limit > 0 && getChar(input, 0) == 0x0000FEFFUL) { + hasBOM = true; + } + + for(int32_t i = 0; i < limit; i += 4) { + int32_t ch = getChar(input, i); + + if (ch < 0 || ch >= 0x10FFFF || (ch >= 0xD800 && ch <= 0xDFFF)) { + numInvalid += 1; + } else { + numValid += 1; + } + } + + + // Cook up some sort of confidence score, based on presence of a BOM + // and the existence of valid and/or invalid multi-byte sequences. + if (hasBOM && numInvalid==0) { + confidence = 100; + } else if (hasBOM && numValid > numInvalid*10) { + confidence = 80; + } else if (numValid > 3 && numInvalid == 0) { + confidence = 100; + } else if (numValid > 0 && numInvalid == 0) { + confidence = 80; + } else if (numValid > numInvalid*10) { + // Probably corrupt UTF-32BE data. Valid sequences aren't likely by chance. + confidence = 25; + } + + results->set(textIn, this, confidence); + return (confidence > 0); +} + +CharsetRecog_UTF_32_BE::~CharsetRecog_UTF_32_BE() +{ + // nothing to do +} + +const char *CharsetRecog_UTF_32_BE::getName() const +{ + return "UTF-32BE"; +} + +int32_t CharsetRecog_UTF_32_BE::getChar(const uint8_t *input, int32_t index) const +{ + return input[index + 0] << 24 | input[index + 1] << 16 | + input[index + 2] << 8 | input[index + 3]; +} + +CharsetRecog_UTF_32_LE::~CharsetRecog_UTF_32_LE() +{ + // nothing to do +} + +const char *CharsetRecog_UTF_32_LE::getName() const +{ + return "UTF-32LE"; +} + +int32_t CharsetRecog_UTF_32_LE::getChar(const uint8_t *input, int32_t index) const +{ + return input[index + 3] << 24 | input[index + 2] << 16 | + input[index + 1] << 8 | input[index + 0]; +} + +U_NAMESPACE_END +#endif + diff --git a/deps/icu-small/source/i18n/csrucode.h b/deps/icu-small/source/i18n/csrucode.h index 78e08d22f11d10..e930f5442f513f 100644 --- a/deps/icu-small/source/i18n/csrucode.h +++ b/deps/icu-small/source/i18n/csrucode.h @@ -1,108 +1,108 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2012, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#ifndef __CSRUCODE_H -#define __CSRUCODE_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "csrecog.h" - -U_NAMESPACE_BEGIN - -/** - * This class matches UTF-16 and UTF-32, both big- and little-endian. The - * BOM will be used if it is present. - * - * @internal - */ -class CharsetRecog_Unicode : public CharsetRecognizer -{ - -public: - - virtual ~CharsetRecog_Unicode(); - /* (non-Javadoc) - * @see com.ibm.icu.text.CharsetRecognizer#getName() - */ - const char* getName() const override = 0; - - /* (non-Javadoc) - * @see com.ibm.icu.text.CharsetRecognizer#match(com.ibm.icu.text.CharsetDetector) - */ - UBool match(InputText* textIn, CharsetMatch *results) const override = 0; -}; - - -class CharsetRecog_UTF_16_BE : public CharsetRecog_Unicode -{ -public: - - virtual ~CharsetRecog_UTF_16_BE(); - - const char *getName() const override; - - UBool match(InputText* textIn, CharsetMatch *results) const override; -}; - -class CharsetRecog_UTF_16_LE : public CharsetRecog_Unicode -{ -public: - - virtual ~CharsetRecog_UTF_16_LE(); - - const char *getName() const override; - - UBool match(InputText* textIn, CharsetMatch *results) const override; -}; - -class CharsetRecog_UTF_32 : public CharsetRecog_Unicode -{ -protected: - virtual int32_t getChar(const uint8_t *input, int32_t index) const = 0; -public: - - virtual ~CharsetRecog_UTF_32(); - - const char* getName() const override = 0; - - UBool match(InputText* textIn, CharsetMatch *results) const override; -}; - - -class CharsetRecog_UTF_32_BE : public CharsetRecog_UTF_32 -{ -protected: - int32_t getChar(const uint8_t *input, int32_t index) const override; - -public: - - virtual ~CharsetRecog_UTF_32_BE(); - - const char *getName() const override; -}; - - -class CharsetRecog_UTF_32_LE : public CharsetRecog_UTF_32 -{ -protected: - int32_t getChar(const uint8_t *input, int32_t index) const override; - -public: - virtual ~CharsetRecog_UTF_32_LE(); - - const char* getName() const override; -}; - -U_NAMESPACE_END - -#endif -#endif /* __CSRUCODE_H */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2012, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#ifndef __CSRUCODE_H +#define __CSRUCODE_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "csrecog.h" + +U_NAMESPACE_BEGIN + +/** + * This class matches UTF-16 and UTF-32, both big- and little-endian. The + * BOM will be used if it is present. + * + * @internal + */ +class CharsetRecog_Unicode : public CharsetRecognizer +{ + +public: + + virtual ~CharsetRecog_Unicode(); + /* (non-Javadoc) + * @see com.ibm.icu.text.CharsetRecognizer#getName() + */ + const char* getName() const override = 0; + + /* (non-Javadoc) + * @see com.ibm.icu.text.CharsetRecognizer#match(com.ibm.icu.text.CharsetDetector) + */ + UBool match(InputText* textIn, CharsetMatch *results) const override = 0; +}; + + +class CharsetRecog_UTF_16_BE : public CharsetRecog_Unicode +{ +public: + + virtual ~CharsetRecog_UTF_16_BE(); + + const char *getName() const override; + + UBool match(InputText* textIn, CharsetMatch *results) const override; +}; + +class CharsetRecog_UTF_16_LE : public CharsetRecog_Unicode +{ +public: + + virtual ~CharsetRecog_UTF_16_LE(); + + const char *getName() const override; + + UBool match(InputText* textIn, CharsetMatch *results) const override; +}; + +class CharsetRecog_UTF_32 : public CharsetRecog_Unicode +{ +protected: + virtual int32_t getChar(const uint8_t *input, int32_t index) const = 0; +public: + + virtual ~CharsetRecog_UTF_32(); + + const char* getName() const override = 0; + + UBool match(InputText* textIn, CharsetMatch *results) const override; +}; + + +class CharsetRecog_UTF_32_BE : public CharsetRecog_UTF_32 +{ +protected: + int32_t getChar(const uint8_t *input, int32_t index) const override; + +public: + + virtual ~CharsetRecog_UTF_32_BE(); + + const char *getName() const override; +}; + + +class CharsetRecog_UTF_32_LE : public CharsetRecog_UTF_32 +{ +protected: + int32_t getChar(const uint8_t *input, int32_t index) const override; + +public: + virtual ~CharsetRecog_UTF_32_LE(); + + const char* getName() const override; +}; + +U_NAMESPACE_END + +#endif +#endif /* __CSRUCODE_H */ diff --git a/deps/icu-small/source/i18n/csrutf8.cpp b/deps/icu-small/source/i18n/csrutf8.cpp index f114f097224a91..9caa81199a3c8f 100644 --- a/deps/icu-small/source/i18n/csrutf8.cpp +++ b/deps/icu-small/source/i18n/csrutf8.cpp @@ -1,111 +1,111 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2014, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "csrutf8.h" -#include "csmatch.h" - -U_NAMESPACE_BEGIN - -CharsetRecog_UTF8::~CharsetRecog_UTF8() -{ - // nothing to do -} - -const char *CharsetRecog_UTF8::getName() const -{ - return "UTF-8"; -} - -UBool CharsetRecog_UTF8::match(InputText* input, CharsetMatch *results) const { - bool hasBOM = false; - int32_t numValid = 0; - int32_t numInvalid = 0; - const uint8_t *inputBytes = input->fRawInput; - int32_t i; - int32_t trailBytes = 0; - int32_t confidence; - - if (input->fRawLength >= 3 && - inputBytes[0] == 0xEF && inputBytes[1] == 0xBB && inputBytes[2] == 0xBF) { - hasBOM = true; - } - - // Scan for multi-byte sequences - for (i=0; i < input->fRawLength; i += 1) { - int32_t b = inputBytes[i]; - - if ((b & 0x80) == 0) { - continue; // ASCII - } - - // Hi bit on char found. Figure out how long the sequence should be - if ((b & 0x0E0) == 0x0C0) { - trailBytes = 1; - } else if ((b & 0x0F0) == 0x0E0) { - trailBytes = 2; - } else if ((b & 0x0F8) == 0xF0) { - trailBytes = 3; - } else { - numInvalid += 1; - continue; - } - - // Verify that we've got the right number of trail bytes in the sequence - for (;;) { - i += 1; - - if (i >= input->fRawLength) { - break; - } - - b = inputBytes[i]; - - if ((b & 0xC0) != 0x080) { - numInvalid += 1; - break; - } - - if (--trailBytes == 0) { - numValid += 1; - break; - } - } - - } - - // Cook up some sort of confidence score, based on presence of a BOM - // and the existence of valid and/or invalid multi-byte sequences. - confidence = 0; - if (hasBOM && numInvalid == 0) { - confidence = 100; - } else if (hasBOM && numValid > numInvalid*10) { - confidence = 80; - } else if (numValid > 3 && numInvalid == 0) { - confidence = 100; - } else if (numValid > 0 && numInvalid == 0) { - confidence = 80; - } else if (numValid == 0 && numInvalid == 0) { - // Plain ASCII. Confidence must be > 10, it's more likely than UTF-16, which - // accepts ASCII with confidence = 10. - confidence = 15; - } else if (numValid > numInvalid*10) { - // Probably corrupt utf-8 data. Valid sequences aren't likely by chance. - confidence = 25; - } - - results->set(input, this, confidence); - return (confidence > 0); -} - -U_NAMESPACE_END -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2014, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "csrutf8.h" +#include "csmatch.h" + +U_NAMESPACE_BEGIN + +CharsetRecog_UTF8::~CharsetRecog_UTF8() +{ + // nothing to do +} + +const char *CharsetRecog_UTF8::getName() const +{ + return "UTF-8"; +} + +UBool CharsetRecog_UTF8::match(InputText* input, CharsetMatch *results) const { + bool hasBOM = false; + int32_t numValid = 0; + int32_t numInvalid = 0; + const uint8_t *inputBytes = input->fRawInput; + int32_t i; + int32_t trailBytes = 0; + int32_t confidence; + + if (input->fRawLength >= 3 && + inputBytes[0] == 0xEF && inputBytes[1] == 0xBB && inputBytes[2] == 0xBF) { + hasBOM = true; + } + + // Scan for multi-byte sequences + for (i=0; i < input->fRawLength; i += 1) { + int32_t b = inputBytes[i]; + + if ((b & 0x80) == 0) { + continue; // ASCII + } + + // Hi bit on char found. Figure out how long the sequence should be + if ((b & 0x0E0) == 0x0C0) { + trailBytes = 1; + } else if ((b & 0x0F0) == 0x0E0) { + trailBytes = 2; + } else if ((b & 0x0F8) == 0xF0) { + trailBytes = 3; + } else { + numInvalid += 1; + continue; + } + + // Verify that we've got the right number of trail bytes in the sequence + for (;;) { + i += 1; + + if (i >= input->fRawLength) { + break; + } + + b = inputBytes[i]; + + if ((b & 0xC0) != 0x080) { + numInvalid += 1; + break; + } + + if (--trailBytes == 0) { + numValid += 1; + break; + } + } + + } + + // Cook up some sort of confidence score, based on presence of a BOM + // and the existence of valid and/or invalid multi-byte sequences. + confidence = 0; + if (hasBOM && numInvalid == 0) { + confidence = 100; + } else if (hasBOM && numValid > numInvalid*10) { + confidence = 80; + } else if (numValid > 3 && numInvalid == 0) { + confidence = 100; + } else if (numValid > 0 && numInvalid == 0) { + confidence = 80; + } else if (numValid == 0 && numInvalid == 0) { + // Plain ASCII. Confidence must be > 10, it's more likely than UTF-16, which + // accepts ASCII with confidence = 10. + confidence = 15; + } else if (numValid > numInvalid*10) { + // Probably corrupt utf-8 data. Valid sequences aren't likely by chance. + confidence = 25; + } + + results->set(input, this, confidence); + return (confidence > 0); +} + +U_NAMESPACE_END +#endif diff --git a/deps/icu-small/source/i18n/csrutf8.h b/deps/icu-small/source/i18n/csrutf8.h index bcfb38ac951de7..fdfab033d6415b 100644 --- a/deps/icu-small/source/i18n/csrutf8.h +++ b/deps/icu-small/source/i18n/csrutf8.h @@ -1,44 +1,44 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2012, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#ifndef __CSRUTF8_H -#define __CSRUTF8_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "csrecog.h" - -U_NAMESPACE_BEGIN - -/** - * Charset recognizer for UTF-8 - * - * @internal - */ -class CharsetRecog_UTF8: public CharsetRecognizer { - - public: - - virtual ~CharsetRecog_UTF8(); - - const char *getName() const override; - - /* (non-Javadoc) - * @see com.ibm.icu.text.CharsetRecognizer#match(com.ibm.icu.text.CharsetDetector) - */ - UBool match(InputText *input, CharsetMatch *results) const override; - -}; - -U_NAMESPACE_END - -#endif -#endif /* __CSRUTF8_H */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2005-2012, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + */ + +#ifndef __CSRUTF8_H +#define __CSRUTF8_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_CONVERSION + +#include "csrecog.h" + +U_NAMESPACE_BEGIN + +/** + * Charset recognizer for UTF-8 + * + * @internal + */ +class CharsetRecog_UTF8: public CharsetRecognizer { + + public: + + virtual ~CharsetRecog_UTF8(); + + const char *getName() const override; + + /* (non-Javadoc) + * @see com.ibm.icu.text.CharsetRecognizer#match(com.ibm.icu.text.CharsetDetector) + */ + UBool match(InputText *input, CharsetMatch *results) const override; + +}; + +U_NAMESPACE_END + +#endif +#endif /* __CSRUTF8_H */ diff --git a/deps/icu-small/source/i18n/curramt.cpp b/deps/icu-small/source/i18n/curramt.cpp index 6fd2ea2fda5d0d..357a40ead2de44 100644 --- a/deps/icu-small/source/i18n/curramt.cpp +++ b/deps/icu-small/source/i18n/curramt.cpp @@ -1,52 +1,56 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2004, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: April 26, 2004 -* Since: ICU 3.0 -********************************************************************** -*/ -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/curramt.h" -#include "unicode/currunit.h" - -U_NAMESPACE_BEGIN - -CurrencyAmount::CurrencyAmount(const Formattable& amount, ConstChar16Ptr isoCode, - UErrorCode& ec) : - Measure(amount, new CurrencyUnit(isoCode, ec), ec) { -} - -CurrencyAmount::CurrencyAmount(double amount, ConstChar16Ptr isoCode, - UErrorCode& ec) : - Measure(Formattable(amount), new CurrencyUnit(isoCode, ec), ec) { -} - -CurrencyAmount::CurrencyAmount(const CurrencyAmount& other) : - Measure(other) { -} - -CurrencyAmount& CurrencyAmount::operator=(const CurrencyAmount& other) { - Measure::operator=(other); - return *this; -} - -CurrencyAmount* CurrencyAmount::clone() const { - return new CurrencyAmount(*this); -} - -CurrencyAmount::~CurrencyAmount() { -} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyAmount) - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_FORMATTING +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2004, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: April 26, 2004 +* Since: ICU 3.0 +********************************************************************** +*/ +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/curramt.h" +#include "unicode/currunit.h" + +U_NAMESPACE_BEGIN + +CurrencyAmount::CurrencyAmount(const Formattable& amount, ConstChar16Ptr isoCode, + UErrorCode& ec) : + Measure(amount, new CurrencyUnit(isoCode, ec), ec) { +} + +CurrencyAmount::CurrencyAmount(double amount, ConstChar16Ptr isoCode, + UErrorCode& ec) : + Measure(Formattable(amount), new CurrencyUnit(isoCode, ec), ec) { +} + +CurrencyAmount::CurrencyAmount(const CurrencyAmount& other) : + Measure(other) { +} + +CurrencyAmount& CurrencyAmount::operator=(const CurrencyAmount& other) { + Measure::operator=(other); + return *this; +} + +CurrencyAmount* CurrencyAmount::clone() const { + return new CurrencyAmount(*this); +} + +CurrencyAmount::~CurrencyAmount() { +} + +const CurrencyUnit& CurrencyAmount::getCurrency() const { + return static_cast(getUnit()); +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyAmount) + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/currfmt.cpp b/deps/icu-small/source/i18n/currfmt.cpp index 0ad0492ee7afbc..804e55405b51ca 100644 --- a/deps/icu-small/source/i18n/currfmt.cpp +++ b/deps/icu-small/source/i18n/currfmt.cpp @@ -1,62 +1,62 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2004-2014 International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: April 20, 2004 -* Since: ICU 3.0 -********************************************************************** -*/ -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "currfmt.h" -#include "unicode/numfmt.h" -#include "unicode/curramt.h" - -U_NAMESPACE_BEGIN - -CurrencyFormat::CurrencyFormat(const Locale& locale, UErrorCode& ec) : - MeasureFormat(locale, UMEASFMT_WIDTH_WIDE, ec) -{ -} - -CurrencyFormat::CurrencyFormat(const CurrencyFormat& other) : - MeasureFormat(other) -{ -} - -CurrencyFormat::~CurrencyFormat() { -} - -CurrencyFormat* CurrencyFormat::clone() const { - return new CurrencyFormat(*this); -} - -UnicodeString& CurrencyFormat::format(const Formattable& obj, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode& ec) const -{ - return getCurrencyFormatInternal().format(obj, appendTo, pos, ec); -} - -void CurrencyFormat::parseObject(const UnicodeString& source, - Formattable& result, - ParsePosition& pos) const -{ - CurrencyAmount* currAmt = getCurrencyFormatInternal().parseCurrency(source, pos); - if (currAmt != NULL) { - result.adoptObject(currAmt); - } -} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyFormat) - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2004-2014 International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: April 20, 2004 +* Since: ICU 3.0 +********************************************************************** +*/ +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "currfmt.h" +#include "unicode/numfmt.h" +#include "unicode/curramt.h" + +U_NAMESPACE_BEGIN + +CurrencyFormat::CurrencyFormat(const Locale& locale, UErrorCode& ec) : + MeasureFormat(locale, UMEASFMT_WIDTH_WIDE, ec) +{ +} + +CurrencyFormat::CurrencyFormat(const CurrencyFormat& other) : + MeasureFormat(other) +{ +} + +CurrencyFormat::~CurrencyFormat() { +} + +CurrencyFormat* CurrencyFormat::clone() const { + return new CurrencyFormat(*this); +} + +UnicodeString& CurrencyFormat::format(const Formattable& obj, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& ec) const +{ + return getCurrencyFormatInternal().format(obj, appendTo, pos, ec); +} + +void CurrencyFormat::parseObject(const UnicodeString& source, + Formattable& result, + ParsePosition& pos) const +{ + CurrencyAmount* currAmt = getCurrencyFormatInternal().parseCurrency(source, pos); + if (currAmt != nullptr) { + result.adoptObject(currAmt); + } +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyFormat) + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/currfmt.h b/deps/icu-small/source/i18n/currfmt.h index 2a75cae2bb39b1..f611c5c9f2b07e 100644 --- a/deps/icu-small/source/i18n/currfmt.h +++ b/deps/icu-small/source/i18n/currfmt.h @@ -1,94 +1,94 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2004-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: April 20, 2004 -* Since: ICU 3.0 -********************************************************************** -*/ -#ifndef CURRENCYFORMAT_H -#define CURRENCYFORMAT_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/measfmt.h" - -U_NAMESPACE_BEGIN - -class NumberFormat; - -/** - * Temporary internal concrete subclass of MeasureFormat implementing - * parsing and formatting of currency amount objects. This class is - * likely to be redesigned and rewritten in the near future. - * - *

This class currently delegates to DecimalFormat for parsing and - * formatting. - * - * @see MeasureFormat - * @author Alan Liu - * @internal - */ -class CurrencyFormat : public MeasureFormat { - - public: - - /** - * Construct a CurrencyFormat for the given locale. - */ - CurrencyFormat(const Locale& locale, UErrorCode& ec); - - /** - * Copy constructor. - */ - CurrencyFormat(const CurrencyFormat& other); - - /** - * Destructor. - */ - virtual ~CurrencyFormat(); - - /** - * Override Format API. - */ - virtual CurrencyFormat* clone() const override; - - - using MeasureFormat::format; - - /** - * Override Format API. - */ - virtual UnicodeString& format(const Formattable& obj, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode& ec) const override; - - /** - * Override Format API. - */ - virtual void parseObject(const UnicodeString& source, - Formattable& result, - ParsePosition& pos) const override; - - /** - * Override Format API. - */ - virtual UClassID getDynamicClassID() const override; - - /** - * Returns the class ID for this class. - */ - static UClassID U_EXPORT2 getStaticClassID(); -}; - -U_NAMESPACE_END - -#endif // #if !UCONFIG_NO_FORMATTING -#endif // #ifndef CURRENCYFORMAT_H +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2004-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: April 20, 2004 +* Since: ICU 3.0 +********************************************************************** +*/ +#ifndef CURRENCYFORMAT_H +#define CURRENCYFORMAT_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/measfmt.h" + +U_NAMESPACE_BEGIN + +class NumberFormat; + +/** + * Temporary internal concrete subclass of MeasureFormat implementing + * parsing and formatting of currency amount objects. This class is + * likely to be redesigned and rewritten in the near future. + * + *

This class currently delegates to DecimalFormat for parsing and + * formatting. + * + * @see MeasureFormat + * @author Alan Liu + * @internal + */ +class CurrencyFormat : public MeasureFormat { + + public: + + /** + * Construct a CurrencyFormat for the given locale. + */ + CurrencyFormat(const Locale& locale, UErrorCode& ec); + + /** + * Copy constructor. + */ + CurrencyFormat(const CurrencyFormat& other); + + /** + * Destructor. + */ + virtual ~CurrencyFormat(); + + /** + * Override Format API. + */ + virtual CurrencyFormat* clone() const override; + + + using MeasureFormat::format; + + /** + * Override Format API. + */ + virtual UnicodeString& format(const Formattable& obj, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& ec) const override; + + /** + * Override Format API. + */ + virtual void parseObject(const UnicodeString& source, + Formattable& result, + ParsePosition& pos) const override; + + /** + * Override Format API. + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Returns the class ID for this class. + */ + static UClassID U_EXPORT2 getStaticClassID(); +}; + +U_NAMESPACE_END + +#endif // #if !UCONFIG_NO_FORMATTING +#endif // #ifndef CURRENCYFORMAT_H diff --git a/deps/icu-small/source/i18n/currpinf.cpp b/deps/icu-small/source/i18n/currpinf.cpp index 1a1c58027170c6..32527ce778ada8 100644 --- a/deps/icu-small/source/i18n/currpinf.cpp +++ b/deps/icu-small/source/i18n/currpinf.cpp @@ -1,441 +1,441 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ******************************************************************************* - * Copyright (C) 2009-2014, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************************* - */ - -#include "unicode/currpinf.h" - -#if !UCONFIG_NO_FORMATTING - -//#define CURRENCY_PLURAL_INFO_DEBUG 1 - -#ifdef CURRENCY_PLURAL_INFO_DEBUG -#include -#endif - -#include "unicode/locid.h" -#include "unicode/plurrule.h" -#include "unicode/strenum.h" -#include "unicode/ures.h" -#include "unicode/numsys.h" -#include "cstring.h" -#include "hash.h" -#include "uresimp.h" -#include "ureslocs.h" - -U_NAMESPACE_BEGIN - -static const UChar gNumberPatternSeparator = 0x3B; // ; - -U_CDECL_BEGIN - -/** - * @internal ICU 4.2 - */ -static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2); - -UBool -U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2) { - const UnicodeString* affix_1 = (UnicodeString*)val1.pointer; - const UnicodeString* affix_2 = (UnicodeString*)val2.pointer; - return *affix_1 == *affix_2; -} - -U_CDECL_END - - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo) - -static const UChar gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0}; -static const UChar gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0}; -static const UChar gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0}; -static const UChar gPart0[] = {0x7B, 0x30, 0x7D, 0}; -static const UChar gPart1[] = {0x7B, 0x31, 0x7D, 0}; - -static const char gNumberElementsTag[]="NumberElements"; -static const char gLatnTag[]="latn"; -static const char gPatternsTag[]="patterns"; -static const char gDecimalFormatTag[]="decimalFormat"; -static const char gCurrUnitPtnTag[]="CurrencyUnitPatterns"; - -CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status) -: fPluralCountToCurrencyUnitPattern(nullptr), - fPluralRules(nullptr), - fLocale(nullptr), - fInternalStatus(U_ZERO_ERROR) { - initialize(Locale::getDefault(), status); -} - -CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status) -: fPluralCountToCurrencyUnitPattern(nullptr), - fPluralRules(nullptr), - fLocale(nullptr), - fInternalStatus(U_ZERO_ERROR) { - initialize(locale, status); -} - -CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info) -: UObject(info), - fPluralCountToCurrencyUnitPattern(nullptr), - fPluralRules(nullptr), - fLocale(nullptr), - fInternalStatus(U_ZERO_ERROR) { - *this = info; -} - -CurrencyPluralInfo& -CurrencyPluralInfo::operator=(const CurrencyPluralInfo& info) { - if (this == &info) { - return *this; - } - - fInternalStatus = info.fInternalStatus; - if (U_FAILURE(fInternalStatus)) { - // bail out early if the object we were copying from was already 'invalid'. - return *this; - } - - deleteHash(fPluralCountToCurrencyUnitPattern); - fPluralCountToCurrencyUnitPattern = initHash(fInternalStatus); - copyHash(info.fPluralCountToCurrencyUnitPattern, - fPluralCountToCurrencyUnitPattern, fInternalStatus); - if ( U_FAILURE(fInternalStatus) ) { - return *this; - } - - delete fPluralRules; - fPluralRules = nullptr; - delete fLocale; - fLocale = nullptr; - - if (info.fPluralRules != nullptr) { - fPluralRules = info.fPluralRules->clone(); - if (fPluralRules == nullptr) { - fInternalStatus = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - } - if (info.fLocale != nullptr) { - fLocale = info.fLocale->clone(); - if (fLocale == nullptr) { - // Note: If clone had an error parameter, then we could check/set that instead. - fInternalStatus = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - // If the other locale wasn't bogus, but our clone'd locale is bogus, then OOM happened - // during the call to clone(). - if (!info.fLocale->isBogus() && fLocale->isBogus()) { - fInternalStatus = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - } - return *this; -} - -CurrencyPluralInfo::~CurrencyPluralInfo() { - deleteHash(fPluralCountToCurrencyUnitPattern); - fPluralCountToCurrencyUnitPattern = nullptr; - delete fPluralRules; - delete fLocale; - fPluralRules = nullptr; - fLocale = nullptr; -} - -bool -CurrencyPluralInfo::operator==(const CurrencyPluralInfo& info) const { -#ifdef CURRENCY_PLURAL_INFO_DEBUG - if (*fPluralRules == *info.fPluralRules) { - std::cout << "same plural rules\n"; - } - if (*fLocale == *info.fLocale) { - std::cout << "same locale\n"; - } - if (fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern)) { - std::cout << "same pattern\n"; - } -#endif - return *fPluralRules == *info.fPluralRules && - *fLocale == *info.fLocale && - fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern); -} - - -CurrencyPluralInfo* -CurrencyPluralInfo::clone() const { - CurrencyPluralInfo* newObj = new CurrencyPluralInfo(*this); - // Since clone doesn't have a 'status' parameter, the best we can do is return nullptr - // if the new object was not full constructed properly (an error occurred). - if (newObj != nullptr && U_FAILURE(newObj->fInternalStatus)) { - delete newObj; - newObj = nullptr; - } - return newObj; -} - -const PluralRules* -CurrencyPluralInfo::getPluralRules() const { - return fPluralRules; -} - -UnicodeString& -CurrencyPluralInfo::getCurrencyPluralPattern(const UnicodeString& pluralCount, - UnicodeString& result) const { - const UnicodeString* currencyPluralPattern = - (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(pluralCount); - if (currencyPluralPattern == nullptr) { - // fall back to "other" - if (pluralCount.compare(gPluralCountOther, 5)) { - currencyPluralPattern = - (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(UnicodeString(true, gPluralCountOther, 5)); - } - if (currencyPluralPattern == nullptr) { - // no currencyUnitPatterns defined, - // fallback to predefined default. - // This should never happen when ICU resource files are - // available, since currencyUnitPattern of "other" is always - // defined in root. - result = UnicodeString(gDefaultCurrencyPluralPattern); - return result; - } - } - result = *currencyPluralPattern; - return result; -} - -const Locale& -CurrencyPluralInfo::getLocale() const { - return *fLocale; -} - -void -CurrencyPluralInfo::setPluralRules(const UnicodeString& ruleDescription, - UErrorCode& status) { - if (U_SUCCESS(status)) { - delete fPluralRules; - fPluralRules = PluralRules::createRules(ruleDescription, status); - } -} - -void -CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString& pluralCount, - const UnicodeString& pattern, - UErrorCode& status) { - if (U_SUCCESS(status)) { - UnicodeString* oldValue = static_cast( - fPluralCountToCurrencyUnitPattern->get(pluralCount)); - delete oldValue; - LocalPointer p(new UnicodeString(pattern), status); - if (U_SUCCESS(status)) { - // the p object allocated above will be owned by fPluralCountToCurrencyUnitPattern - // after the call to put(), even if the method returns failure. - fPluralCountToCurrencyUnitPattern->put(pluralCount, p.orphan(), status); - } - } -} - -void -CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) { - initialize(loc, status); -} - -void -CurrencyPluralInfo::initialize(const Locale& loc, UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - delete fLocale; - fLocale = nullptr; - delete fPluralRules; - fPluralRules = nullptr; - - fLocale = loc.clone(); - if (fLocale == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - // If the locale passed in wasn't bogus, but our clone'd locale is bogus, then OOM happened - // during the call to loc.clone(). - if (!loc.isBogus() && fLocale->isBogus()) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - fPluralRules = PluralRules::forLocale(loc, status); - setupCurrencyPluralPattern(loc, status); -} - -void -CurrencyPluralInfo::setupCurrencyPluralPattern(const Locale& loc, UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - - deleteHash(fPluralCountToCurrencyUnitPattern); - fPluralCountToCurrencyUnitPattern = initHash(status); - if (U_FAILURE(status)) { - return; - } - - LocalPointer ns(NumberingSystem::createInstance(loc, status), status); - if (U_FAILURE(status)) { - return; - } - UErrorCode ec = U_ZERO_ERROR; - LocalUResourceBundlePointer rb(ures_open(nullptr, loc.getName(), &ec)); - LocalUResourceBundlePointer numElements(ures_getByKeyWithFallback(rb.getAlias(), gNumberElementsTag, nullptr, &ec)); - ures_getByKeyWithFallback(numElements.getAlias(), ns->getName(), rb.getAlias(), &ec); - ures_getByKeyWithFallback(rb.getAlias(), gPatternsTag, rb.getAlias(), &ec); - int32_t ptnLen; - const UChar* numberStylePattern = ures_getStringByKeyWithFallback(rb.getAlias(), gDecimalFormatTag, &ptnLen, &ec); - // Fall back to "latn" if num sys specific pattern isn't there. - if ( ec == U_MISSING_RESOURCE_ERROR && (uprv_strcmp(ns->getName(), gLatnTag) != 0)) { - ec = U_ZERO_ERROR; - ures_getByKeyWithFallback(numElements.getAlias(), gLatnTag, rb.getAlias(), &ec); - ures_getByKeyWithFallback(rb.getAlias(), gPatternsTag, rb.getAlias(), &ec); - numberStylePattern = ures_getStringByKeyWithFallback(rb.getAlias(), gDecimalFormatTag, &ptnLen, &ec); - } - int32_t numberStylePatternLen = ptnLen; - const UChar* negNumberStylePattern = nullptr; - int32_t negNumberStylePatternLen = 0; - // TODO: Java - // parse to check whether there is ";" separator in the numberStylePattern - UBool hasSeparator = false; - if (U_SUCCESS(ec)) { - for (int32_t styleCharIndex = 0; styleCharIndex < ptnLen; ++styleCharIndex) { - if (numberStylePattern[styleCharIndex] == gNumberPatternSeparator) { - hasSeparator = true; - // split the number style pattern into positive and negative - negNumberStylePattern = numberStylePattern + styleCharIndex + 1; - negNumberStylePatternLen = ptnLen - styleCharIndex - 1; - numberStylePatternLen = styleCharIndex; - } - } - } - - if (U_FAILURE(ec)) { - // If OOM occurred during the above code, then we want to report that back to the caller. - if (ec == U_MEMORY_ALLOCATION_ERROR) { - status = ec; - } - return; - } - - LocalUResourceBundlePointer currRb(ures_open(U_ICUDATA_CURR, loc.getName(), &ec)); - LocalUResourceBundlePointer currencyRes(ures_getByKeyWithFallback(currRb.getAlias(), gCurrUnitPtnTag, nullptr, &ec)); - -#ifdef CURRENCY_PLURAL_INFO_DEBUG - std::cout << "in set up\n"; -#endif - LocalPointer keywords(fPluralRules->getKeywords(ec), ec); - if (U_SUCCESS(ec)) { - const char* pluralCount; - while (((pluralCount = keywords->next(nullptr, ec)) != nullptr) && U_SUCCESS(ec)) { - int32_t ptnLength; - UErrorCode err = U_ZERO_ERROR; - const UChar* patternChars = ures_getStringByKeyWithFallback(currencyRes.getAlias(), pluralCount, &ptnLength, &err); - if (err == U_MEMORY_ALLOCATION_ERROR || patternChars == nullptr) { - ec = err; - break; - } - if (U_SUCCESS(err) && ptnLength > 0) { - UnicodeString* pattern = new UnicodeString(patternChars, ptnLength); - if (pattern == nullptr) { - ec = U_MEMORY_ALLOCATION_ERROR; - break; - } -#ifdef CURRENCY_PLURAL_INFO_DEBUG - char result_1[1000]; - pattern->extract(0, pattern->length(), result_1, "UTF-8"); - std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n"; -#endif - pattern->findAndReplace(UnicodeString(true, gPart0, 3), - UnicodeString(numberStylePattern, numberStylePatternLen)); - pattern->findAndReplace(UnicodeString(true, gPart1, 3), UnicodeString(true, gTripleCurrencySign, 3)); - - if (hasSeparator) { - UnicodeString negPattern(patternChars, ptnLength); - negPattern.findAndReplace(UnicodeString(true, gPart0, 3), - UnicodeString(negNumberStylePattern, negNumberStylePatternLen)); - negPattern.findAndReplace(UnicodeString(true, gPart1, 3), UnicodeString(true, gTripleCurrencySign, 3)); - pattern->append(gNumberPatternSeparator); - pattern->append(negPattern); - } -#ifdef CURRENCY_PLURAL_INFO_DEBUG - pattern->extract(0, pattern->length(), result_1, "UTF-8"); - std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n"; -#endif - // the 'pattern' object allocated above will be owned by the fPluralCountToCurrencyUnitPattern after the call to - // put(), even if the method returns failure. - fPluralCountToCurrencyUnitPattern->put(UnicodeString(pluralCount, -1, US_INV), pattern, status); - } - } - } - // If OOM occurred during the above code, then we want to report that back to the caller. - if (ec == U_MEMORY_ALLOCATION_ERROR) { - status = ec; - } -} - -void -CurrencyPluralInfo::deleteHash(Hashtable* hTable) { - if ( hTable == nullptr ) { - return; - } - int32_t pos = UHASH_FIRST; - const UHashElement* element = nullptr; - while ( (element = hTable->nextElement(pos)) != nullptr ) { - const UHashTok valueTok = element->value; - const UnicodeString* value = (UnicodeString*)valueTok.pointer; - delete value; - } - delete hTable; - hTable = nullptr; -} - -Hashtable* -CurrencyPluralInfo::initHash(UErrorCode& status) { - if (U_FAILURE(status)) { - return nullptr; - } - LocalPointer hTable(new Hashtable(true, status), status); - if (U_FAILURE(status)) { - return nullptr; - } - hTable->setValueComparator(ValueComparator); - return hTable.orphan(); -} - -void -CurrencyPluralInfo::copyHash(const Hashtable* source, - Hashtable* target, - UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - int32_t pos = UHASH_FIRST; - const UHashElement* element = nullptr; - if (source) { - while ( (element = source->nextElement(pos)) != nullptr ) { - const UHashTok keyTok = element->key; - const UnicodeString* key = (UnicodeString*)keyTok.pointer; - const UHashTok valueTok = element->value; - const UnicodeString* value = (UnicodeString*)valueTok.pointer; - LocalPointer copy(new UnicodeString(*value), status); - if (U_FAILURE(status)) { - return; - } - // The HashTable owns the 'copy' object after the call to put(). - target->put(UnicodeString(*key), copy.orphan(), status); - if (U_FAILURE(status)) { - return; - } - } - } -} - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************************* + * Copyright (C) 2009-2014, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************************* + */ + +#include "unicode/currpinf.h" + +#if !UCONFIG_NO_FORMATTING + +//#define CURRENCY_PLURAL_INFO_DEBUG 1 + +#ifdef CURRENCY_PLURAL_INFO_DEBUG +#include +#endif + +#include "unicode/locid.h" +#include "unicode/plurrule.h" +#include "unicode/strenum.h" +#include "unicode/ures.h" +#include "unicode/numsys.h" +#include "cstring.h" +#include "hash.h" +#include "uresimp.h" +#include "ureslocs.h" + +U_NAMESPACE_BEGIN + +static const char16_t gNumberPatternSeparator = 0x3B; // ; + +U_CDECL_BEGIN + +/** + * @internal ICU 4.2 + */ +static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2); + +UBool +U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2) { + const UnicodeString* affix_1 = (UnicodeString*)val1.pointer; + const UnicodeString* affix_2 = (UnicodeString*)val2.pointer; + return *affix_1 == *affix_2; +} + +U_CDECL_END + + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo) + +static const char16_t gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0}; +static const char16_t gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0}; +static const char16_t gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0}; +static const char16_t gPart0[] = {0x7B, 0x30, 0x7D, 0}; +static const char16_t gPart1[] = {0x7B, 0x31, 0x7D, 0}; + +static const char gNumberElementsTag[]="NumberElements"; +static const char gLatnTag[]="latn"; +static const char gPatternsTag[]="patterns"; +static const char gDecimalFormatTag[]="decimalFormat"; +static const char gCurrUnitPtnTag[]="CurrencyUnitPatterns"; + +CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status) +: fPluralCountToCurrencyUnitPattern(nullptr), + fPluralRules(nullptr), + fLocale(nullptr), + fInternalStatus(U_ZERO_ERROR) { + initialize(Locale::getDefault(), status); +} + +CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status) +: fPluralCountToCurrencyUnitPattern(nullptr), + fPluralRules(nullptr), + fLocale(nullptr), + fInternalStatus(U_ZERO_ERROR) { + initialize(locale, status); +} + +CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info) +: UObject(info), + fPluralCountToCurrencyUnitPattern(nullptr), + fPluralRules(nullptr), + fLocale(nullptr), + fInternalStatus(U_ZERO_ERROR) { + *this = info; +} + +CurrencyPluralInfo& +CurrencyPluralInfo::operator=(const CurrencyPluralInfo& info) { + if (this == &info) { + return *this; + } + + fInternalStatus = info.fInternalStatus; + if (U_FAILURE(fInternalStatus)) { + // bail out early if the object we were copying from was already 'invalid'. + return *this; + } + + deleteHash(fPluralCountToCurrencyUnitPattern); + fPluralCountToCurrencyUnitPattern = initHash(fInternalStatus); + copyHash(info.fPluralCountToCurrencyUnitPattern, + fPluralCountToCurrencyUnitPattern, fInternalStatus); + if ( U_FAILURE(fInternalStatus) ) { + return *this; + } + + delete fPluralRules; + fPluralRules = nullptr; + delete fLocale; + fLocale = nullptr; + + if (info.fPluralRules != nullptr) { + fPluralRules = info.fPluralRules->clone(); + if (fPluralRules == nullptr) { + fInternalStatus = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + } + if (info.fLocale != nullptr) { + fLocale = info.fLocale->clone(); + if (fLocale == nullptr) { + // Note: If clone had an error parameter, then we could check/set that instead. + fInternalStatus = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + // If the other locale wasn't bogus, but our clone'd locale is bogus, then OOM happened + // during the call to clone(). + if (!info.fLocale->isBogus() && fLocale->isBogus()) { + fInternalStatus = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + } + return *this; +} + +CurrencyPluralInfo::~CurrencyPluralInfo() { + deleteHash(fPluralCountToCurrencyUnitPattern); + fPluralCountToCurrencyUnitPattern = nullptr; + delete fPluralRules; + delete fLocale; + fPluralRules = nullptr; + fLocale = nullptr; +} + +bool +CurrencyPluralInfo::operator==(const CurrencyPluralInfo& info) const { +#ifdef CURRENCY_PLURAL_INFO_DEBUG + if (*fPluralRules == *info.fPluralRules) { + std::cout << "same plural rules\n"; + } + if (*fLocale == *info.fLocale) { + std::cout << "same locale\n"; + } + if (fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern)) { + std::cout << "same pattern\n"; + } +#endif + return *fPluralRules == *info.fPluralRules && + *fLocale == *info.fLocale && + fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern); +} + + +CurrencyPluralInfo* +CurrencyPluralInfo::clone() const { + CurrencyPluralInfo* newObj = new CurrencyPluralInfo(*this); + // Since clone doesn't have a 'status' parameter, the best we can do is return nullptr + // if the new object was not full constructed properly (an error occurred). + if (newObj != nullptr && U_FAILURE(newObj->fInternalStatus)) { + delete newObj; + newObj = nullptr; + } + return newObj; +} + +const PluralRules* +CurrencyPluralInfo::getPluralRules() const { + return fPluralRules; +} + +UnicodeString& +CurrencyPluralInfo::getCurrencyPluralPattern(const UnicodeString& pluralCount, + UnicodeString& result) const { + const UnicodeString* currencyPluralPattern = + (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(pluralCount); + if (currencyPluralPattern == nullptr) { + // fall back to "other" + if (pluralCount.compare(gPluralCountOther, 5)) { + currencyPluralPattern = + (UnicodeString*)fPluralCountToCurrencyUnitPattern->get(UnicodeString(true, gPluralCountOther, 5)); + } + if (currencyPluralPattern == nullptr) { + // no currencyUnitPatterns defined, + // fallback to predefined default. + // This should never happen when ICU resource files are + // available, since currencyUnitPattern of "other" is always + // defined in root. + result = UnicodeString(gDefaultCurrencyPluralPattern); + return result; + } + } + result = *currencyPluralPattern; + return result; +} + +const Locale& +CurrencyPluralInfo::getLocale() const { + return *fLocale; +} + +void +CurrencyPluralInfo::setPluralRules(const UnicodeString& ruleDescription, + UErrorCode& status) { + if (U_SUCCESS(status)) { + delete fPluralRules; + fPluralRules = PluralRules::createRules(ruleDescription, status); + } +} + +void +CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString& pluralCount, + const UnicodeString& pattern, + UErrorCode& status) { + if (U_SUCCESS(status)) { + UnicodeString* oldValue = static_cast( + fPluralCountToCurrencyUnitPattern->get(pluralCount)); + delete oldValue; + LocalPointer p(new UnicodeString(pattern), status); + if (U_SUCCESS(status)) { + // the p object allocated above will be owned by fPluralCountToCurrencyUnitPattern + // after the call to put(), even if the method returns failure. + fPluralCountToCurrencyUnitPattern->put(pluralCount, p.orphan(), status); + } + } +} + +void +CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) { + initialize(loc, status); +} + +void +CurrencyPluralInfo::initialize(const Locale& loc, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + delete fLocale; + fLocale = nullptr; + delete fPluralRules; + fPluralRules = nullptr; + + fLocale = loc.clone(); + if (fLocale == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + // If the locale passed in wasn't bogus, but our clone'd locale is bogus, then OOM happened + // during the call to loc.clone(). + if (!loc.isBogus() && fLocale->isBogus()) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + fPluralRules = PluralRules::forLocale(loc, status); + setupCurrencyPluralPattern(loc, status); +} + +void +CurrencyPluralInfo::setupCurrencyPluralPattern(const Locale& loc, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + + deleteHash(fPluralCountToCurrencyUnitPattern); + fPluralCountToCurrencyUnitPattern = initHash(status); + if (U_FAILURE(status)) { + return; + } + + LocalPointer ns(NumberingSystem::createInstance(loc, status), status); + if (U_FAILURE(status)) { + return; + } + UErrorCode ec = U_ZERO_ERROR; + LocalUResourceBundlePointer rb(ures_open(nullptr, loc.getName(), &ec)); + LocalUResourceBundlePointer numElements(ures_getByKeyWithFallback(rb.getAlias(), gNumberElementsTag, nullptr, &ec)); + ures_getByKeyWithFallback(numElements.getAlias(), ns->getName(), rb.getAlias(), &ec); + ures_getByKeyWithFallback(rb.getAlias(), gPatternsTag, rb.getAlias(), &ec); + int32_t ptnLen; + const char16_t* numberStylePattern = ures_getStringByKeyWithFallback(rb.getAlias(), gDecimalFormatTag, &ptnLen, &ec); + // Fall back to "latn" if num sys specific pattern isn't there. + if ( ec == U_MISSING_RESOURCE_ERROR && (uprv_strcmp(ns->getName(), gLatnTag) != 0)) { + ec = U_ZERO_ERROR; + ures_getByKeyWithFallback(numElements.getAlias(), gLatnTag, rb.getAlias(), &ec); + ures_getByKeyWithFallback(rb.getAlias(), gPatternsTag, rb.getAlias(), &ec); + numberStylePattern = ures_getStringByKeyWithFallback(rb.getAlias(), gDecimalFormatTag, &ptnLen, &ec); + } + int32_t numberStylePatternLen = ptnLen; + const char16_t* negNumberStylePattern = nullptr; + int32_t negNumberStylePatternLen = 0; + // TODO: Java + // parse to check whether there is ";" separator in the numberStylePattern + UBool hasSeparator = false; + if (U_SUCCESS(ec)) { + for (int32_t styleCharIndex = 0; styleCharIndex < ptnLen; ++styleCharIndex) { + if (numberStylePattern[styleCharIndex] == gNumberPatternSeparator) { + hasSeparator = true; + // split the number style pattern into positive and negative + negNumberStylePattern = numberStylePattern + styleCharIndex + 1; + negNumberStylePatternLen = ptnLen - styleCharIndex - 1; + numberStylePatternLen = styleCharIndex; + } + } + } + + if (U_FAILURE(ec)) { + // If OOM occurred during the above code, then we want to report that back to the caller. + if (ec == U_MEMORY_ALLOCATION_ERROR) { + status = ec; + } + return; + } + + LocalUResourceBundlePointer currRb(ures_open(U_ICUDATA_CURR, loc.getName(), &ec)); + LocalUResourceBundlePointer currencyRes(ures_getByKeyWithFallback(currRb.getAlias(), gCurrUnitPtnTag, nullptr, &ec)); + +#ifdef CURRENCY_PLURAL_INFO_DEBUG + std::cout << "in set up\n"; +#endif + LocalPointer keywords(fPluralRules->getKeywords(ec), ec); + if (U_SUCCESS(ec)) { + const char* pluralCount; + while (((pluralCount = keywords->next(nullptr, ec)) != nullptr) && U_SUCCESS(ec)) { + int32_t ptnLength; + UErrorCode err = U_ZERO_ERROR; + const char16_t* patternChars = ures_getStringByKeyWithFallback(currencyRes.getAlias(), pluralCount, &ptnLength, &err); + if (err == U_MEMORY_ALLOCATION_ERROR || patternChars == nullptr) { + ec = err; + break; + } + if (U_SUCCESS(err) && ptnLength > 0) { + UnicodeString* pattern = new UnicodeString(patternChars, ptnLength); + if (pattern == nullptr) { + ec = U_MEMORY_ALLOCATION_ERROR; + break; + } +#ifdef CURRENCY_PLURAL_INFO_DEBUG + char result_1[1000]; + pattern->extract(0, pattern->length(), result_1, "UTF-8"); + std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n"; +#endif + pattern->findAndReplace(UnicodeString(true, gPart0, 3), + UnicodeString(numberStylePattern, numberStylePatternLen)); + pattern->findAndReplace(UnicodeString(true, gPart1, 3), UnicodeString(true, gTripleCurrencySign, 3)); + + if (hasSeparator) { + UnicodeString negPattern(patternChars, ptnLength); + negPattern.findAndReplace(UnicodeString(true, gPart0, 3), + UnicodeString(negNumberStylePattern, negNumberStylePatternLen)); + negPattern.findAndReplace(UnicodeString(true, gPart1, 3), UnicodeString(true, gTripleCurrencySign, 3)); + pattern->append(gNumberPatternSeparator); + pattern->append(negPattern); + } +#ifdef CURRENCY_PLURAL_INFO_DEBUG + pattern->extract(0, pattern->length(), result_1, "UTF-8"); + std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n"; +#endif + // the 'pattern' object allocated above will be owned by the fPluralCountToCurrencyUnitPattern after the call to + // put(), even if the method returns failure. + fPluralCountToCurrencyUnitPattern->put(UnicodeString(pluralCount, -1, US_INV), pattern, status); + } + } + } + // If OOM occurred during the above code, then we want to report that back to the caller. + if (ec == U_MEMORY_ALLOCATION_ERROR) { + status = ec; + } +} + +void +CurrencyPluralInfo::deleteHash(Hashtable* hTable) { + if ( hTable == nullptr ) { + return; + } + int32_t pos = UHASH_FIRST; + const UHashElement* element = nullptr; + while ( (element = hTable->nextElement(pos)) != nullptr ) { + const UHashTok valueTok = element->value; + const UnicodeString* value = (UnicodeString*)valueTok.pointer; + delete value; + } + delete hTable; + hTable = nullptr; +} + +Hashtable* +CurrencyPluralInfo::initHash(UErrorCode& status) { + if (U_FAILURE(status)) { + return nullptr; + } + LocalPointer hTable(new Hashtable(true, status), status); + if (U_FAILURE(status)) { + return nullptr; + } + hTable->setValueComparator(ValueComparator); + return hTable.orphan(); +} + +void +CurrencyPluralInfo::copyHash(const Hashtable* source, + Hashtable* target, + UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + int32_t pos = UHASH_FIRST; + const UHashElement* element = nullptr; + if (source) { + while ( (element = source->nextElement(pos)) != nullptr ) { + const UHashTok keyTok = element->key; + const UnicodeString* key = (UnicodeString*)keyTok.pointer; + const UHashTok valueTok = element->value; + const UnicodeString* value = (UnicodeString*)valueTok.pointer; + LocalPointer copy(new UnicodeString(*value), status); + if (U_FAILURE(status)) { + return; + } + // The HashTable owns the 'copy' object after the call to put(). + target->put(UnicodeString(*key), copy.orphan(), status); + if (U_FAILURE(status)) { + return; + } + } + } +} + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/i18n/currunit.cpp b/deps/icu-small/source/i18n/currunit.cpp index 92bcf1268ac387..fdf0b05759f5c6 100644 --- a/deps/icu-small/source/i18n/currunit.cpp +++ b/deps/icu-small/source/i18n/currunit.cpp @@ -1,126 +1,126 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2004-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: April 26, 2004 -* Since: ICU 3.0 -********************************************************************** -*/ -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/currunit.h" -#include "unicode/ustring.h" -#include "unicode/uchar.h" -#include "cstring.h" -#include "uinvchar.h" -#include "charstr.h" -#include "ustr_imp.h" -#include "measunit_impl.h" - -U_NAMESPACE_BEGIN - -CurrencyUnit::CurrencyUnit(ConstChar16Ptr _isoCode, UErrorCode& ec) { - // 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. - // However, we allow a NUL-terminated empty string, which should have the same behavior as nullptr. - // Consider NUL-terminated strings of length 1 or 2 as invalid. - bool useDefault = false; - if (U_FAILURE(ec) || _isoCode == nullptr || _isoCode[0] == 0) { - useDefault = true; - } else if (_isoCode[1] == 0 || _isoCode[2] == 0) { - useDefault = true; - ec = U_ILLEGAL_ARGUMENT_ERROR; - } else if (!uprv_isInvariantUString(_isoCode, 3)) { - // TODO: Perform a more strict ASCII check like in ICU4J isAlpha3Code? - useDefault = true; - ec = U_INVARIANT_CONVERSION_ERROR; - } else { - for (int32_t i=0; i<3; i++) { - isoCode[i] = u_asciiToUpper(_isoCode[i]); - } - isoCode[3] = 0; - } - if (useDefault) { - uprv_memcpy(isoCode, kDefaultCurrency, sizeof(UChar) * 4); - } - char simpleIsoCode[4]; - u_UCharsToChars(isoCode, simpleIsoCode, 4); - initCurrency(simpleIsoCode); -} - -CurrencyUnit::CurrencyUnit(StringPiece _isoCode, UErrorCode& ec) { - // Note: unlike the old constructor, reject empty arguments with an error. - char isoCodeBuffer[4]; - const char* isoCodeToUse; - // uprv_memchr checks that the string contains no internal NULs - if (_isoCode.length() != 3 || uprv_memchr(_isoCode.data(), 0, 3) != nullptr) { - isoCodeToUse = kDefaultCurrency8; - ec = U_ILLEGAL_ARGUMENT_ERROR; - } else if (!uprv_isInvariantString(_isoCode.data(), 3)) { - // TODO: Perform a more strict ASCII check like in ICU4J isAlpha3Code? - isoCodeToUse = kDefaultCurrency8; - ec = U_INVARIANT_CONVERSION_ERROR; - } else { - // Have to use isoCodeBuffer to ensure the string is NUL-terminated - for (int32_t i=0; i<3; i++) { - isoCodeBuffer[i] = uprv_toupper(_isoCode.data()[i]); - } - isoCodeBuffer[3] = 0; - isoCodeToUse = isoCodeBuffer; - } - u_charsToUChars(isoCodeToUse, isoCode, 4); - initCurrency(isoCodeToUse); -} - -CurrencyUnit::CurrencyUnit(const CurrencyUnit& other) : MeasureUnit(other) { - u_strcpy(isoCode, other.isoCode); -} - -CurrencyUnit::CurrencyUnit(const MeasureUnit& other, UErrorCode& ec) : MeasureUnit(other) { - // Make sure this is a currency. - // OK to hard-code the string because we are comparing against another hard-coded string. - if (uprv_strcmp("currency", getType()) != 0) { - ec = U_ILLEGAL_ARGUMENT_ERROR; - isoCode[0] = 0; - } else { - // Get the ISO Code from the subtype field. - u_charsToUChars(getSubtype(), isoCode, 4); - isoCode[3] = 0; // make 100% sure it is NUL-terminated - } -} - -CurrencyUnit::CurrencyUnit() : MeasureUnit() { - u_strcpy(isoCode, kDefaultCurrency); - char simpleIsoCode[4]; - u_UCharsToChars(isoCode, simpleIsoCode, 4); - initCurrency(simpleIsoCode); -} - -CurrencyUnit& CurrencyUnit::operator=(const CurrencyUnit& other) { - if (this == &other) { - return *this; - } - MeasureUnit::operator=(other); - u_strcpy(isoCode, other.isoCode); - return *this; -} - -CurrencyUnit* CurrencyUnit::clone() const { - return new CurrencyUnit(*this); -} - -CurrencyUnit::~CurrencyUnit() { -} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyUnit) - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_FORMATTING +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2004-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: April 26, 2004 +* Since: ICU 3.0 +********************************************************************** +*/ +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/currunit.h" +#include "unicode/ustring.h" +#include "unicode/uchar.h" +#include "cstring.h" +#include "uinvchar.h" +#include "charstr.h" +#include "ustr_imp.h" +#include "measunit_impl.h" + +U_NAMESPACE_BEGIN + +CurrencyUnit::CurrencyUnit(ConstChar16Ptr _isoCode, UErrorCode& ec) { + // 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. + // However, we allow a NUL-terminated empty string, which should have the same behavior as nullptr. + // Consider NUL-terminated strings of length 1 or 2 as invalid. + bool useDefault = false; + if (U_FAILURE(ec) || _isoCode == nullptr || _isoCode[0] == 0) { + useDefault = true; + } else if (_isoCode[1] == 0 || _isoCode[2] == 0) { + useDefault = true; + ec = U_ILLEGAL_ARGUMENT_ERROR; + } else if (!uprv_isInvariantUString(_isoCode, 3)) { + // TODO: Perform a more strict ASCII check like in ICU4J isAlpha3Code? + useDefault = true; + ec = U_INVARIANT_CONVERSION_ERROR; + } else { + for (int32_t i=0; i<3; i++) { + isoCode[i] = u_asciiToUpper(_isoCode[i]); + } + isoCode[3] = 0; + } + if (useDefault) { + uprv_memcpy(isoCode, kDefaultCurrency, sizeof(char16_t) * 4); + } + char simpleIsoCode[4]; + u_UCharsToChars(isoCode, simpleIsoCode, 4); + initCurrency(simpleIsoCode); +} + +CurrencyUnit::CurrencyUnit(StringPiece _isoCode, UErrorCode& ec) { + // Note: unlike the old constructor, reject empty arguments with an error. + char isoCodeBuffer[4]; + const char* isoCodeToUse; + // uprv_memchr checks that the string contains no internal NULs + if (_isoCode.length() != 3 || uprv_memchr(_isoCode.data(), 0, 3) != nullptr) { + isoCodeToUse = kDefaultCurrency8; + ec = U_ILLEGAL_ARGUMENT_ERROR; + } else if (!uprv_isInvariantString(_isoCode.data(), 3)) { + // TODO: Perform a more strict ASCII check like in ICU4J isAlpha3Code? + isoCodeToUse = kDefaultCurrency8; + ec = U_INVARIANT_CONVERSION_ERROR; + } else { + // Have to use isoCodeBuffer to ensure the string is NUL-terminated + for (int32_t i=0; i<3; i++) { + isoCodeBuffer[i] = uprv_toupper(_isoCode.data()[i]); + } + isoCodeBuffer[3] = 0; + isoCodeToUse = isoCodeBuffer; + } + u_charsToUChars(isoCodeToUse, isoCode, 4); + initCurrency(isoCodeToUse); +} + +CurrencyUnit::CurrencyUnit(const CurrencyUnit& other) : MeasureUnit(other) { + u_strcpy(isoCode, other.isoCode); +} + +CurrencyUnit::CurrencyUnit(const MeasureUnit& other, UErrorCode& ec) : MeasureUnit(other) { + // Make sure this is a currency. + // OK to hard-code the string because we are comparing against another hard-coded string. + if (uprv_strcmp("currency", getType()) != 0) { + ec = U_ILLEGAL_ARGUMENT_ERROR; + isoCode[0] = 0; + } else { + // Get the ISO Code from the subtype field. + u_charsToUChars(getSubtype(), isoCode, 4); + isoCode[3] = 0; // make 100% sure it is NUL-terminated + } +} + +CurrencyUnit::CurrencyUnit() : MeasureUnit() { + u_strcpy(isoCode, kDefaultCurrency); + char simpleIsoCode[4]; + u_UCharsToChars(isoCode, simpleIsoCode, 4); + initCurrency(simpleIsoCode); +} + +CurrencyUnit& CurrencyUnit::operator=(const CurrencyUnit& other) { + if (this == &other) { + return *this; + } + MeasureUnit::operator=(other); + u_strcpy(isoCode, other.isoCode); + return *this; +} + +CurrencyUnit* CurrencyUnit::clone() const { + return new CurrencyUnit(*this); +} + +CurrencyUnit::~CurrencyUnit() { +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyUnit) + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/dangical.cpp b/deps/icu-small/source/i18n/dangical.cpp index 59cdc661dac817..09a03ffeb6f33b 100644 --- a/deps/icu-small/source/i18n/dangical.cpp +++ b/deps/icu-small/source/i18n/dangical.cpp @@ -1,150 +1,167 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ****************************************************************************** - * Copyright (C) 2013, International Business Machines Corporation - * and others. All Rights Reserved. - ****************************************************************************** - * - * File DANGICAL.CPP - ***************************************************************************** - */ - -#include "chnsecal.h" -#include "dangical.h" - -#if !UCONFIG_NO_FORMATTING - -#include "gregoimp.h" // Math -#include "uassert.h" -#include "ucln_in.h" -#include "umutex.h" -#include "unicode/rbtz.h" -#include "unicode/tzrule.h" - -// --- The cache -- -static icu::TimeZone *gDangiCalendarZoneAstroCalc = NULL; -static icu::UInitOnce gDangiCalendarInitOnce {}; - -/** - * The start year of the Korean traditional calendar (Dan-gi) is the inaugural - * year of Dan-gun (BC 2333). - */ -static const int32_t DANGI_EPOCH_YEAR = -2332; // Gregorian year - -U_CDECL_BEGIN -static UBool calendar_dangi_cleanup(void) { - if (gDangiCalendarZoneAstroCalc) { - delete gDangiCalendarZoneAstroCalc; - gDangiCalendarZoneAstroCalc = NULL; - } - gDangiCalendarInitOnce.reset(); - return true; -} -U_CDECL_END - -U_NAMESPACE_BEGIN - -// Implementation of the DangiCalendar class - -//------------------------------------------------------------------------- -// Constructors... -//------------------------------------------------------------------------- - -DangiCalendar::DangiCalendar(const Locale& aLocale, UErrorCode& success) -: ChineseCalendar(aLocale, DANGI_EPOCH_YEAR, getDangiCalZoneAstroCalc(success), success) -{ -} - -DangiCalendar::DangiCalendar (const DangiCalendar& other) -: ChineseCalendar(other) -{ -} - -DangiCalendar::~DangiCalendar() -{ -} - -DangiCalendar* -DangiCalendar::clone() const -{ - return new DangiCalendar(*this); -} - -const char *DangiCalendar::getType() const { - return "dangi"; -} - -/** - * The time zone used for performing astronomical computations for - * Dangi calendar. In Korea various timezones have been used historically - * (cf. http://www.math.snu.ac.kr/~kye/others/lunar.html): - * - * - 1908/04/01: GMT+8 - * 1908/04/01 - 1911/12/31: GMT+8.5 - * 1912/01/01 - 1954/03/20: GMT+9 - * 1954/03/21 - 1961/08/09: GMT+8.5 - * 1961/08/10 - : GMT+9 - * - * Note that, in 1908-1911, the government did not apply the timezone change - * but used GMT+8. In addition, 1954-1961's timezone change does not affect - * the lunar date calculation. Therefore, the following simpler rule works: - * - * -1911: GMT+8 - * 1912-: GMT+9 - * - * Unfortunately, our astronomer's approximation doesn't agree with the - * references (http://www.math.snu.ac.kr/~kye/others/lunar.html and - * http://astro.kasi.re.kr/Life/ConvertSolarLunarForm.aspx?MenuID=115) - * in 1897/7/30. So the following ad hoc fix is used here: - * - * -1896: GMT+8 - * 1897: GMT+7 - * 1898-1911: GMT+8 - * 1912- : GMT+9 - */ -static void U_CALLCONV initDangiCalZoneAstroCalc(UErrorCode &status) { - U_ASSERT(gDangiCalendarZoneAstroCalc == nullptr); - const UDate millis1897[] = { (UDate)((1897 - 1970) * 365 * kOneDay) }; // some days of error is not a problem here - const UDate millis1898[] = { (UDate)((1898 - 1970) * 365 * kOneDay) }; // some days of error is not a problem here - const UDate millis1912[] = { (UDate)((1912 - 1970) * 365 * kOneDay) }; // this doesn't create an issue for 1911/12/20 - LocalPointer initialTimeZone(new InitialTimeZoneRule( - UnicodeString(u"GMT+8"), 8*kOneHour, 0), status); - - LocalPointer rule1897(new TimeArrayTimeZoneRule( - UnicodeString(u"Korean 1897"), 7*kOneHour, 0, millis1897, 1, DateTimeRule::STANDARD_TIME), status); - - LocalPointer rule1898to1911(new TimeArrayTimeZoneRule( - UnicodeString(u"Korean 1898-1911"), 8*kOneHour, 0, millis1898, 1, DateTimeRule::STANDARD_TIME), status); - - LocalPointer ruleFrom1912(new TimeArrayTimeZoneRule( - UnicodeString(u"Korean 1912-"), 9*kOneHour, 0, millis1912, 1, DateTimeRule::STANDARD_TIME), status); - - LocalPointer dangiCalZoneAstroCalc(new RuleBasedTimeZone( - UnicodeString(u"KOREA_ZONE"), initialTimeZone.orphan()), status); // adopts initialTimeZone - - if (U_FAILURE(status)) { - return; - } - dangiCalZoneAstroCalc->addTransitionRule(rule1897.orphan(), status); // adopts rule1897 - dangiCalZoneAstroCalc->addTransitionRule(rule1898to1911.orphan(), status); - dangiCalZoneAstroCalc->addTransitionRule(ruleFrom1912.orphan(), status); - dangiCalZoneAstroCalc->complete(status); - if (U_SUCCESS(status)) { - gDangiCalendarZoneAstroCalc = dangiCalZoneAstroCalc.orphan(); - } - ucln_i18n_registerCleanup(UCLN_I18N_DANGI_CALENDAR, calendar_dangi_cleanup); -} - -const TimeZone* DangiCalendar::getDangiCalZoneAstroCalc(UErrorCode &status) const { - umtx_initOnce(gDangiCalendarInitOnce, &initDangiCalZoneAstroCalc, status); - return gDangiCalendarZoneAstroCalc; -} - - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DangiCalendar) - -U_NAMESPACE_END - -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ****************************************************************************** + * Copyright (C) 2013, International Business Machines Corporation + * and others. All Rights Reserved. + ****************************************************************************** + * + * File DANGICAL.CPP + ***************************************************************************** + */ + +#include "chnsecal.h" +#include "dangical.h" + +#if !UCONFIG_NO_FORMATTING + +#include "gregoimp.h" // Math +#include "uassert.h" +#include "ucln_in.h" +#include "umutex.h" +#include "unicode/rbtz.h" +#include "unicode/tzrule.h" + +// --- The cache -- +static icu::TimeZone *gDangiCalendarZoneAstroCalc = nullptr; +static icu::UInitOnce gDangiCalendarInitOnce {}; + +/** + * The start year of the Korean traditional calendar (Dan-gi) is the inaugural + * year of Dan-gun (BC 2333). + */ +static const int32_t DANGI_EPOCH_YEAR = -2332; // Gregorian year + +U_CDECL_BEGIN +static UBool calendar_dangi_cleanup() { + if (gDangiCalendarZoneAstroCalc) { + delete gDangiCalendarZoneAstroCalc; + gDangiCalendarZoneAstroCalc = nullptr; + } + gDangiCalendarInitOnce.reset(); + return true; +} +U_CDECL_END + +U_NAMESPACE_BEGIN + +// Implementation of the DangiCalendar class + +//------------------------------------------------------------------------- +// Constructors... +//------------------------------------------------------------------------- + +DangiCalendar::DangiCalendar(const Locale& aLocale, UErrorCode& success) +: ChineseCalendar(aLocale, DANGI_EPOCH_YEAR, getDangiCalZoneAstroCalc(success), success) +{ +} + +DangiCalendar::DangiCalendar (const DangiCalendar& other) +: ChineseCalendar(other) +{ +} + +DangiCalendar::~DangiCalendar() +{ +} + +DangiCalendar* +DangiCalendar::clone() const +{ + return new DangiCalendar(*this); +} + +const char *DangiCalendar::getType() const { + return "dangi"; +} + +/** + * The time zone used for performing astronomical computations for + * Dangi calendar. In Korea various timezones have been used historically + * (cf. http://www.math.snu.ac.kr/~kye/others/lunar.html): + * + * - 1908/04/01: GMT+8 + * 1908/04/01 - 1911/12/31: GMT+8.5 + * 1912/01/01 - 1954/03/20: GMT+9 + * 1954/03/21 - 1961/08/09: GMT+8.5 + * 1961/08/10 - : GMT+9 + * + * Note that, in 1908-1911, the government did not apply the timezone change + * but used GMT+8. In addition, 1954-1961's timezone change does not affect + * the lunar date calculation. Therefore, the following simpler rule works: + * + * -1911: GMT+8 + * 1912-: GMT+9 + * + * Unfortunately, our astronomer's approximation doesn't agree with the + * references (http://www.math.snu.ac.kr/~kye/others/lunar.html and + * http://astro.kasi.re.kr/Life/ConvertSolarLunarForm.aspx?MenuID=115) + * in 1897/7/30. So the following ad hoc fix is used here: + * + * -1896: GMT+8 + * 1897: GMT+7 + * 1898-1911: GMT+8 + * 1912- : GMT+9 + */ +static void U_CALLCONV initDangiCalZoneAstroCalc(UErrorCode &status) { + U_ASSERT(gDangiCalendarZoneAstroCalc == nullptr); + const UDate millis1897[] = { (UDate)((1897 - 1970) * 365 * kOneDay) }; // some days of error is not a problem here + const UDate millis1898[] = { (UDate)((1898 - 1970) * 365 * kOneDay) }; // some days of error is not a problem here + const UDate millis1912[] = { (UDate)((1912 - 1970) * 365 * kOneDay) }; // this doesn't create an issue for 1911/12/20 + LocalPointer initialTimeZone(new InitialTimeZoneRule( + UnicodeString(u"GMT+8"), 8*kOneHour, 0), status); + + LocalPointer rule1897(new TimeArrayTimeZoneRule( + UnicodeString(u"Korean 1897"), 7*kOneHour, 0, millis1897, 1, DateTimeRule::STANDARD_TIME), status); + + LocalPointer rule1898to1911(new TimeArrayTimeZoneRule( + UnicodeString(u"Korean 1898-1911"), 8*kOneHour, 0, millis1898, 1, DateTimeRule::STANDARD_TIME), status); + + LocalPointer ruleFrom1912(new TimeArrayTimeZoneRule( + UnicodeString(u"Korean 1912-"), 9*kOneHour, 0, millis1912, 1, DateTimeRule::STANDARD_TIME), status); + + LocalPointer dangiCalZoneAstroCalc(new RuleBasedTimeZone( + UnicodeString(u"KOREA_ZONE"), initialTimeZone.orphan()), status); // adopts initialTimeZone + + if (U_FAILURE(status)) { + return; + } + dangiCalZoneAstroCalc->addTransitionRule(rule1897.orphan(), status); // adopts rule1897 + dangiCalZoneAstroCalc->addTransitionRule(rule1898to1911.orphan(), status); + dangiCalZoneAstroCalc->addTransitionRule(ruleFrom1912.orphan(), status); + dangiCalZoneAstroCalc->complete(status); + if (U_SUCCESS(status)) { + gDangiCalendarZoneAstroCalc = dangiCalZoneAstroCalc.orphan(); + } + ucln_i18n_registerCleanup(UCLN_I18N_DANGI_CALENDAR, calendar_dangi_cleanup); +} + +const TimeZone* DangiCalendar::getDangiCalZoneAstroCalc(UErrorCode &status) const { + umtx_initOnce(gDangiCalendarInitOnce, &initDangiCalZoneAstroCalc, status); + return gDangiCalendarZoneAstroCalc; +} + +constexpr uint32_t kDangiRelatedYearDiff = -2333; + +int32_t DangiCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kDangiRelatedYearDiff; +} + +void DangiCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kDangiRelatedYearDiff); +} + + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DangiCalendar) + +U_NAMESPACE_END + +#endif + diff --git a/deps/icu-small/source/i18n/dangical.h b/deps/icu-small/source/i18n/dangical.h index 9d0437264ef175..09633177240b8b 100644 --- a/deps/icu-small/source/i18n/dangical.h +++ b/deps/icu-small/source/i18n/dangical.h @@ -1,121 +1,135 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ***************************************************************************** - * Copyright (C) 2013, International Business Machines Corporation - * and others. All Rights Reserved. - ***************************************************************************** - * - * File DANGICAL.H - ***************************************************************************** - */ - -#ifndef DANGICAL_H -#define DANGICAL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/calendar.h" -#include "unicode/timezone.h" -#include "chnsecal.h" - -U_NAMESPACE_BEGIN - -/** - *

DangiCalendar is a concrete subclass of {@link Calendar} - * that implements a traditional Korean lunisolar calendar.

- * - *

DangiCalendar usually should be instantiated using - * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a ULocale - * with the tag "@calendar=dangi".

- * - * @internal - */ -class DangiCalendar : public ChineseCalendar { - public: - //------------------------------------------------------------------------- - // Constructors... - //------------------------------------------------------------------------- - - /** - * Constructs a DangiCalendar based on the current time in the default time zone - * with the given locale. - * - * @param aLocale The given locale. - * @param success Indicates the status of DangiCalendar object construction. - * Returns U_ZERO_ERROR if constructed successfully. - * @internal - */ - DangiCalendar(const Locale& aLocale, UErrorCode &success); - - /** - * Copy Constructor - * @internal - */ - DangiCalendar(const DangiCalendar& other); - - /** - * Destructor. - * @internal - */ - virtual ~DangiCalendar(); - - /** - * Clone. - * @internal - */ - virtual DangiCalendar* clone() const override; - - //---------------------------------------------------------------------- - // Internal methods & astronomical calculations - //---------------------------------------------------------------------- - - private: - - const TimeZone* getDangiCalZoneAstroCalc(UErrorCode &status) const; - - // UObject stuff - public: - /** - * @return The class ID for this object. All objects of a given class have the - * same class ID. Objects of other classes have different class IDs. - * @internal - */ - virtual UClassID getDynamicClassID(void) const override; - - /** - * Return the class ID for this class. This is useful only for comparing to a return - * value from getDynamicClassID(). For example: - * - * Base* polymorphic_pointer = createPolymorphicObject(); - * if (polymorphic_pointer->getDynamicClassID() == - * Derived::getStaticClassID()) ... - * - * @return The class ID for all objects of this class. - * @internal - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * return the calendar type, "dangi". - * - * @return calendar type - * @internal - */ - const char * getType() const override; - - - private: - - DangiCalendar(); // default constructor not implemented -}; - -U_NAMESPACE_END - -#endif -#endif - - - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ***************************************************************************** + * Copyright (C) 2013, International Business Machines Corporation + * and others. All Rights Reserved. + ***************************************************************************** + * + * File DANGICAL.H + ***************************************************************************** + */ + +#ifndef DANGICAL_H +#define DANGICAL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" +#include "unicode/timezone.h" +#include "chnsecal.h" + +U_NAMESPACE_BEGIN + +/** + *

DangiCalendar is a concrete subclass of {@link Calendar} + * that implements a traditional Korean lunisolar calendar.

+ * + *

DangiCalendar usually should be instantiated using + * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a ULocale + * with the tag "@calendar=dangi".

+ * + * @internal + */ +class DangiCalendar : public ChineseCalendar { + public: + //------------------------------------------------------------------------- + // Constructors... + //------------------------------------------------------------------------- + + /** + * Constructs a DangiCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of DangiCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + DangiCalendar(const Locale& aLocale, UErrorCode &success); + + /** + * Copy Constructor + * @internal + */ + DangiCalendar(const DangiCalendar& other); + + /** + * Destructor. + * @internal + */ + virtual ~DangiCalendar(); + + /** + * Clone. + * @internal + */ + virtual DangiCalendar* clone() const override; + + //---------------------------------------------------------------------- + // Internal methods & astronomical calculations + //---------------------------------------------------------------------- + + /** + * @return The related Gregorian year; will be obtained by modifying the value + * obtained by get from UCAL_EXTENDED_YEAR field + * @internal + */ + virtual int32_t getRelatedYear(UErrorCode &status) const override; + + /** + * @param year The related Gregorian year to set; will be modified as necessary then + * set in UCAL_EXTENDED_YEAR field + * @internal + */ + virtual void setRelatedYear(int32_t year) override; + + private: + + const TimeZone* getDangiCalZoneAstroCalc(UErrorCode &status) const; + + // UObject stuff + public: + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + + /** + * return the calendar type, "dangi". + * + * @return calendar type + * @internal + */ + const char * getType() const override; + + + private: + + DangiCalendar(); // default constructor not implemented +}; + +U_NAMESPACE_END + +#endif +#endif + + + diff --git a/deps/icu-small/source/i18n/datefmt.cpp b/deps/icu-small/source/i18n/datefmt.cpp index 2638cbf14dec12..aefae9529b99b9 100644 --- a/deps/icu-small/source/i18n/datefmt.cpp +++ b/deps/icu-small/source/i18n/datefmt.cpp @@ -1,752 +1,751 @@ -// © 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 DATEFMT.CPP - * - * Modification History: - * - * Date Name Description - * 02/19/97 aliu Converted from java. - * 03/31/97 aliu Modified extensively to work with 50 locales. - * 04/01/97 aliu Added support for centuries. - * 08/12/97 aliu Fixed operator== to use Calendar::equivalentTo. - * 07/20/98 stephen Changed ParsePosition initialization - ******************************************************************************** - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/ures.h" -#include "unicode/datefmt.h" -#include "unicode/smpdtfmt.h" -#include "unicode/dtptngen.h" -#include "unicode/udisplaycontext.h" -#include "reldtfmt.h" -#include "sharedobject.h" -#include "unifiedcache.h" -#include "uarrsort.h" - -#include "cstring.h" -#include "windtfmt.h" - -#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) -#include -#endif - -// ***************************************************************************** -// class DateFormat -// ***************************************************************************** - -U_NAMESPACE_BEGIN - -class DateFmtBestPattern : public SharedObject { -public: - UnicodeString fPattern; - - DateFmtBestPattern(const UnicodeString &pattern) - : fPattern(pattern) { } - ~DateFmtBestPattern(); -}; - -DateFmtBestPattern::~DateFmtBestPattern() { -} - -template<> -const DateFmtBestPattern *LocaleCacheKey::createObject( - const void * /*creationContext*/, UErrorCode &status) const { - status = U_UNSUPPORTED_ERROR; - return NULL; -} - -class DateFmtBestPatternKey : public LocaleCacheKey { -private: - UnicodeString fSkeleton; -protected: - virtual bool equals(const CacheKeyBase &other) const override { - if (!LocaleCacheKey::equals(other)) { - return false; - } - // We know that this and other are of same class if we get this far. - return operator==(static_cast(other)); - } -public: - DateFmtBestPatternKey( - const Locale &loc, - const UnicodeString &skeleton, - UErrorCode &status) - : LocaleCacheKey(loc), - fSkeleton(DateTimePatternGenerator::staticGetSkeleton(skeleton, status)) { } - DateFmtBestPatternKey(const DateFmtBestPatternKey &other) : - LocaleCacheKey(other), - fSkeleton(other.fSkeleton) { } - virtual ~DateFmtBestPatternKey(); - virtual int32_t hashCode() const override { - return (int32_t)(37u * (uint32_t)LocaleCacheKey::hashCode() + (uint32_t)fSkeleton.hashCode()); - } - inline bool operator==(const DateFmtBestPatternKey &other) const { - return fSkeleton == other.fSkeleton; - } - virtual CacheKeyBase *clone() const override { - return new DateFmtBestPatternKey(*this); - } - virtual const DateFmtBestPattern *createObject( - const void * /*unused*/, UErrorCode &status) const override { - LocalPointer dtpg( - DateTimePatternGenerator::createInstance(fLoc, status)); - if (U_FAILURE(status)) { - return NULL; - } - - LocalPointer pattern( - new DateFmtBestPattern( - dtpg->getBestPattern(fSkeleton, status)), - status); - if (U_FAILURE(status)) { - return NULL; - } - DateFmtBestPattern *result = pattern.orphan(); - result->addRef(); - return result; - } -}; - -DateFmtBestPatternKey::~DateFmtBestPatternKey() { } - - -DateFormat::DateFormat() -: fCalendar(0), - fNumberFormat(0), - fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) -{ -} - -//---------------------------------------------------------------------- - -DateFormat::DateFormat(const DateFormat& other) -: Format(other), - fCalendar(0), - fNumberFormat(0), - fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) -{ - *this = other; -} - -//---------------------------------------------------------------------- - -DateFormat& DateFormat::operator=(const DateFormat& other) -{ - if (this != &other) - { - delete fCalendar; - delete fNumberFormat; - if(other.fCalendar) { - fCalendar = other.fCalendar->clone(); - } else { - fCalendar = NULL; - } - if(other.fNumberFormat) { - fNumberFormat = other.fNumberFormat->clone(); - } else { - fNumberFormat = NULL; - } - fBoolFlags = other.fBoolFlags; - fCapitalizationContext = other.fCapitalizationContext; - } - return *this; -} - -//---------------------------------------------------------------------- - -DateFormat::~DateFormat() -{ - delete fCalendar; - delete fNumberFormat; -} - -//---------------------------------------------------------------------- - -bool -DateFormat::operator==(const Format& other) const -{ - // This protected comparison operator should only be called by subclasses - // which have confirmed that the other object being compared against is - // an instance of a sublcass of DateFormat. THIS IS IMPORTANT. - - // Format::operator== guarantees that this cast is safe - DateFormat* fmt = (DateFormat*)&other; - - return (this == fmt) || - (Format::operator==(other) && - fCalendar&&(fCalendar->isEquivalentTo(*fmt->fCalendar)) && - (fNumberFormat && *fNumberFormat == *fmt->fNumberFormat) && - (fCapitalizationContext == fmt->fCapitalizationContext) ); -} - -//---------------------------------------------------------------------- - -UnicodeString& -DateFormat::format(const Formattable& obj, - UnicodeString& appendTo, - FieldPosition& fieldPosition, - UErrorCode& status) const -{ - if (U_FAILURE(status)) return appendTo; - - // if the type of the Formattable is double or long, treat it as if it were a Date - UDate date = 0; - switch (obj.getType()) - { - case Formattable::kDate: - date = obj.getDate(); - break; - case Formattable::kDouble: - date = (UDate)obj.getDouble(); - break; - case Formattable::kLong: - date = (UDate)obj.getLong(); - break; - default: - status = U_ILLEGAL_ARGUMENT_ERROR; - return appendTo; - } - - // Is this right? - //if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex()) - // status = U_ILLEGAL_ARGUMENT_ERROR; - - return format(date, appendTo, fieldPosition); -} - -//---------------------------------------------------------------------- - -UnicodeString& -DateFormat::format(const Formattable& obj, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const -{ - if (U_FAILURE(status)) return appendTo; - - // if the type of the Formattable is double or long, treat it as if it were a Date - UDate date = 0; - switch (obj.getType()) - { - case Formattable::kDate: - date = obj.getDate(); - break; - case Formattable::kDouble: - date = (UDate)obj.getDouble(); - break; - case Formattable::kLong: - date = (UDate)obj.getLong(); - break; - default: - status = U_ILLEGAL_ARGUMENT_ERROR; - return appendTo; - } - - // Is this right? - //if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex()) - // status = U_ILLEGAL_ARGUMENT_ERROR; - - return format(date, appendTo, posIter, status); -} - -//---------------------------------------------------------------------- - -// Default implementation for backwards compatibility, subclasses should implement. -UnicodeString& -DateFormat::format(Calendar& /* unused cal */, - UnicodeString& appendTo, - FieldPositionIterator* /* unused posIter */, - UErrorCode& status) const { - if (U_SUCCESS(status)) { - status = U_UNSUPPORTED_ERROR; - } - return appendTo; -} - -//---------------------------------------------------------------------- - -UnicodeString& -DateFormat::format(UDate date, UnicodeString& appendTo, FieldPosition& fieldPosition) const { - if (fCalendar != NULL) { - // Use a clone of our calendar instance - Calendar* calClone = fCalendar->clone(); - if (calClone != NULL) { - UErrorCode ec = U_ZERO_ERROR; - calClone->setTime(date, ec); - if (U_SUCCESS(ec)) { - format(*calClone, appendTo, fieldPosition); - } - delete calClone; - } - } - return appendTo; -} - -//---------------------------------------------------------------------- - -UnicodeString& -DateFormat::format(UDate date, UnicodeString& appendTo, FieldPositionIterator* posIter, - UErrorCode& status) const { - if (fCalendar != NULL) { - Calendar* calClone = fCalendar->clone(); - if (calClone != NULL) { - calClone->setTime(date, status); - if (U_SUCCESS(status)) { - format(*calClone, appendTo, posIter, status); - } - delete calClone; - } - } - return appendTo; -} - -//---------------------------------------------------------------------- - -UnicodeString& -DateFormat::format(UDate date, UnicodeString& appendTo) const -{ - // Note that any error information is just lost. That's okay - // for this convenience method. - FieldPosition fpos(FieldPosition::DONT_CARE); - return format(date, appendTo, fpos); -} - -//---------------------------------------------------------------------- - -UDate -DateFormat::parse(const UnicodeString& text, - ParsePosition& pos) const -{ - UDate d = 0; // Error return UDate is 0 (the epoch) - if (fCalendar != NULL) { - Calendar* calClone = fCalendar->clone(); - if (calClone != NULL) { - int32_t start = pos.getIndex(); - calClone->clear(); - parse(text, *calClone, pos); - if (pos.getIndex() != start) { - UErrorCode ec = U_ZERO_ERROR; - d = calClone->getTime(ec); - if (U_FAILURE(ec)) { - // We arrive here if fCalendar => calClone is non-lenient and - // there is an out-of-range field. We don't know which field - // was illegal so we set the error index to the start. - pos.setIndex(start); - pos.setErrorIndex(start); - d = 0; - } - } - delete calClone; - } - } - return d; -} - -//---------------------------------------------------------------------- - -UDate -DateFormat::parse(const UnicodeString& text, - UErrorCode& status) const -{ - if (U_FAILURE(status)) return 0; - - ParsePosition pos(0); - UDate result = parse(text, pos); - if (pos.getIndex() == 0) { -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d - - failed to parse - err index %d\n" - , __FILE__, __LINE__, pos.getErrorIndex() ); -#endif - status = U_ILLEGAL_ARGUMENT_ERROR; - } - return result; -} - -//---------------------------------------------------------------------- - -void -DateFormat::parseObject(const UnicodeString& source, - Formattable& result, - ParsePosition& pos) const -{ - result.setDate(parse(source, pos)); -} - -//---------------------------------------------------------------------- - -DateFormat* U_EXPORT2 -DateFormat::createTimeInstance(DateFormat::EStyle style, - const Locale& aLocale) -{ - return createDateTimeInstance(kNone, style, aLocale); -} - -//---------------------------------------------------------------------- - -DateFormat* U_EXPORT2 -DateFormat::createDateInstance(DateFormat::EStyle style, - const Locale& aLocale) -{ - return createDateTimeInstance(style, kNone, aLocale); -} - -//---------------------------------------------------------------------- - -DateFormat* U_EXPORT2 -DateFormat::createDateTimeInstance(EStyle dateStyle, - EStyle timeStyle, - const Locale& aLocale) -{ - if(dateStyle != kNone) - { - dateStyle = (EStyle) (dateStyle + kDateOffset); - } - return create(timeStyle, dateStyle, aLocale); -} - -//---------------------------------------------------------------------- - -DateFormat* U_EXPORT2 -DateFormat::createInstance() -{ - return createDateTimeInstance(kShort, kShort, Locale::getDefault()); -} - -//---------------------------------------------------------------------- - -UnicodeString U_EXPORT2 -DateFormat::getBestPattern( - const Locale &locale, - const UnicodeString &skeleton, - UErrorCode &status) { - UnifiedCache *cache = UnifiedCache::getInstance(status); - if (U_FAILURE(status)) { - return UnicodeString(); - } - DateFmtBestPatternKey key(locale, skeleton, status); - const DateFmtBestPattern *patternPtr = NULL; - cache->get(key, patternPtr, status); - if (U_FAILURE(status)) { - return UnicodeString(); - } - UnicodeString result(patternPtr->fPattern); - patternPtr->removeRef(); - return result; -} - -DateFormat* U_EXPORT2 -DateFormat::createInstanceForSkeleton( - Calendar *calendarToAdopt, - const UnicodeString& skeleton, - const Locale &locale, - UErrorCode &status) { - LocalPointer calendar(calendarToAdopt); - if (U_FAILURE(status)) { - return NULL; - } - if (calendar.isNull()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - Locale localeWithCalendar = locale; - localeWithCalendar.setKeywordValue("calendar", calendar->getType(), status); - if (U_FAILURE(status)) { - return NULL; - } - DateFormat *result = createInstanceForSkeleton(skeleton, localeWithCalendar, status); - if (U_FAILURE(status)) { - return NULL; - } - result->adoptCalendar(calendar.orphan()); - return result; -} - -DateFormat* U_EXPORT2 -DateFormat::createInstanceForSkeleton( - const UnicodeString& skeleton, - const Locale &locale, - UErrorCode &status) { - if (U_FAILURE(status)) { - return NULL; - } - LocalPointer df( - new SimpleDateFormat( - getBestPattern(locale, skeleton, status), - locale, status), - status); - return U_SUCCESS(status) ? df.orphan() : NULL; -} - -DateFormat* U_EXPORT2 -DateFormat::createInstanceForSkeleton( - const UnicodeString& skeleton, - UErrorCode &status) { - return createInstanceForSkeleton( - skeleton, Locale::getDefault(), status); -} - -//---------------------------------------------------------------------- - -DateFormat* U_EXPORT2 -DateFormat::create(EStyle timeStyle, EStyle dateStyle, const Locale& locale) -{ - UErrorCode status = U_ZERO_ERROR; -#if U_PLATFORM_USES_ONLY_WIN32_API - char buffer[8]; - int32_t count = locale.getKeywordValue("compat", buffer, sizeof(buffer), status); - - // if the locale has "@compat=host", create a host-specific DateFormat... - if (count > 0 && uprv_strcmp(buffer, "host") == 0) { - Win32DateFormat *f = new Win32DateFormat(timeStyle, dateStyle, locale, status); - - if (U_SUCCESS(status)) { - return f; - } - - delete f; - } -#endif - - // is it relative? - if(/*((timeStyle!=UDAT_NONE)&&(timeStyle & UDAT_RELATIVE)) || */((dateStyle!=kNone)&&((dateStyle-kDateOffset) & UDAT_RELATIVE))) { - RelativeDateFormat *r = new RelativeDateFormat((UDateFormatStyle)timeStyle, (UDateFormatStyle)(dateStyle-kDateOffset), locale, status); - if(U_SUCCESS(status)) return r; - delete r; - status = U_ZERO_ERROR; - } - - // Try to create a SimpleDateFormat of the desired style. - SimpleDateFormat *f = new SimpleDateFormat(timeStyle, dateStyle, locale, status); - if (U_SUCCESS(status)) return f; - delete f; - - // If that fails, try to create a format using the default pattern and - // the DateFormatSymbols for this locale. - status = U_ZERO_ERROR; - f = new SimpleDateFormat(locale, status); - if (U_SUCCESS(status)) return f; - delete f; - - // This should never really happen, because the preceding constructor - // should always succeed. If the resource data is unavailable, a last - // resort object should be returned. - return 0; -} - -//---------------------------------------------------------------------- - -const Locale* U_EXPORT2 -DateFormat::getAvailableLocales(int32_t& count) -{ - // Get the list of installed locales. - // Even if root has the correct date format for this locale, - // it's still a valid locale (we don't worry about data fallbacks). - return Locale::getAvailableLocales(count); -} - -//---------------------------------------------------------------------- - -void -DateFormat::adoptCalendar(Calendar* newCalendar) -{ - delete fCalendar; - fCalendar = newCalendar; -} - -//---------------------------------------------------------------------- -void -DateFormat::setCalendar(const Calendar& newCalendar) -{ - Calendar* newCalClone = newCalendar.clone(); - if (newCalClone != NULL) { - adoptCalendar(newCalClone); - } -} - -//---------------------------------------------------------------------- - -const Calendar* -DateFormat::getCalendar() const -{ - return fCalendar; -} - -//---------------------------------------------------------------------- - -void -DateFormat::adoptNumberFormat(NumberFormat* newNumberFormat) -{ - delete fNumberFormat; - fNumberFormat = newNumberFormat; - newNumberFormat->setParseIntegerOnly(true); - newNumberFormat->setGroupingUsed(false); -} -//---------------------------------------------------------------------- - -void -DateFormat::setNumberFormat(const NumberFormat& newNumberFormat) -{ - NumberFormat* newNumFmtClone = newNumberFormat.clone(); - if (newNumFmtClone != NULL) { - adoptNumberFormat(newNumFmtClone); - } -} - -//---------------------------------------------------------------------- - -const NumberFormat* -DateFormat::getNumberFormat() const -{ - return fNumberFormat; -} - -//---------------------------------------------------------------------- - -void -DateFormat::adoptTimeZone(TimeZone* zone) -{ - if (fCalendar != NULL) { - fCalendar->adoptTimeZone(zone); - } -} -//---------------------------------------------------------------------- - -void -DateFormat::setTimeZone(const TimeZone& zone) -{ - if (fCalendar != NULL) { - fCalendar->setTimeZone(zone); - } -} - -//---------------------------------------------------------------------- - -const TimeZone& -DateFormat::getTimeZone() const -{ - if (fCalendar != NULL) { - return fCalendar->getTimeZone(); - } - // If calendar doesn't exists, create default timezone. - // fCalendar is rarely null - return *(TimeZone::createDefault()); -} - -//---------------------------------------------------------------------- - -void -DateFormat::setLenient(UBool lenient) -{ - if (fCalendar != NULL) { - fCalendar->setLenient(lenient); - } - UErrorCode status = U_ZERO_ERROR; - setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, lenient, status); - setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, lenient, status); -} - -//---------------------------------------------------------------------- - -UBool -DateFormat::isLenient() const -{ - UBool lenient = true; - if (fCalendar != NULL) { - lenient = fCalendar->isLenient(); - } - UErrorCode status = U_ZERO_ERROR; - return lenient - && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status) - && getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status); -} - -void -DateFormat::setCalendarLenient(UBool lenient) -{ - if (fCalendar != NULL) { - fCalendar->setLenient(lenient); - } -} - -//---------------------------------------------------------------------- - -UBool -DateFormat::isCalendarLenient() const -{ - if (fCalendar != NULL) { - return fCalendar->isLenient(); - } - // fCalendar is rarely null - return false; -} - - -//---------------------------------------------------------------------- - - -void DateFormat::setContext(UDisplayContext value, UErrorCode& status) -{ - if (U_FAILURE(status)) - return; - if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) { - fCapitalizationContext = value; - } else { - status = U_ILLEGAL_ARGUMENT_ERROR; - } -} - - -//---------------------------------------------------------------------- - - -UDisplayContext DateFormat::getContext(UDisplayContextType type, UErrorCode& status) const -{ - if (U_FAILURE(status)) - return (UDisplayContext)0; - if (type != UDISPCTX_TYPE_CAPITALIZATION) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return (UDisplayContext)0; - } - return fCapitalizationContext; -} - - -//---------------------------------------------------------------------- - - -DateFormat& -DateFormat::setBooleanAttribute(UDateFormatBooleanAttribute attr, - UBool newValue, - UErrorCode &status) { - if(!fBoolFlags.isValidValue(newValue)) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } else { - fBoolFlags.set(attr, newValue); - } - - return *this; -} - -//---------------------------------------------------------------------- - -UBool -DateFormat::getBooleanAttribute(UDateFormatBooleanAttribute attr, UErrorCode &/*status*/) const { - - return static_cast(fBoolFlags.get(attr)); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 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 DATEFMT.CPP + * + * Modification History: + * + * Date Name Description + * 02/19/97 aliu Converted from java. + * 03/31/97 aliu Modified extensively to work with 50 locales. + * 04/01/97 aliu Added support for centuries. + * 08/12/97 aliu Fixed operator== to use Calendar::equivalentTo. + * 07/20/98 stephen Changed ParsePosition initialization + ******************************************************************************** + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/ures.h" +#include "unicode/datefmt.h" +#include "unicode/smpdtfmt.h" +#include "unicode/dtptngen.h" +#include "unicode/udisplaycontext.h" +#include "reldtfmt.h" +#include "sharedobject.h" +#include "unifiedcache.h" +#include "uarrsort.h" + +#include "cstring.h" +#include "windtfmt.h" + +#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) +#include +#endif + +// ***************************************************************************** +// class DateFormat +// ***************************************************************************** + +U_NAMESPACE_BEGIN + +class DateFmtBestPattern : public SharedObject { +public: + UnicodeString fPattern; + + DateFmtBestPattern(const UnicodeString &pattern) + : fPattern(pattern) { } + ~DateFmtBestPattern(); +}; + +DateFmtBestPattern::~DateFmtBestPattern() { +} + +template<> +const DateFmtBestPattern *LocaleCacheKey::createObject( + const void * /*creationContext*/, UErrorCode &status) const { + status = U_UNSUPPORTED_ERROR; + return nullptr; +} + +class DateFmtBestPatternKey : public LocaleCacheKey { +private: + UnicodeString fSkeleton; +protected: + virtual bool equals(const CacheKeyBase &other) const override { + if (!LocaleCacheKey::equals(other)) { + return false; + } + // We know that this and other are of same class if we get this far. + return operator==(static_cast(other)); + } +public: + DateFmtBestPatternKey( + const Locale &loc, + const UnicodeString &skeleton, + UErrorCode &status) + : LocaleCacheKey(loc), + fSkeleton(DateTimePatternGenerator::staticGetSkeleton(skeleton, status)) { } + DateFmtBestPatternKey(const DateFmtBestPatternKey &other) : + LocaleCacheKey(other), + fSkeleton(other.fSkeleton) { } + virtual ~DateFmtBestPatternKey(); + virtual int32_t hashCode() const override { + return (int32_t)(37u * (uint32_t)LocaleCacheKey::hashCode() + (uint32_t)fSkeleton.hashCode()); + } + inline bool operator==(const DateFmtBestPatternKey &other) const { + return fSkeleton == other.fSkeleton; + } + virtual CacheKeyBase *clone() const override { + return new DateFmtBestPatternKey(*this); + } + virtual const DateFmtBestPattern *createObject( + const void * /*unused*/, UErrorCode &status) const override { + LocalPointer dtpg( + DateTimePatternGenerator::createInstance(fLoc, status)); + if (U_FAILURE(status)) { + return nullptr; + } + + LocalPointer pattern( + new DateFmtBestPattern( + dtpg->getBestPattern(fSkeleton, status)), + status); + if (U_FAILURE(status)) { + return nullptr; + } + DateFmtBestPattern *result = pattern.orphan(); + result->addRef(); + return result; + } +}; + +DateFmtBestPatternKey::~DateFmtBestPatternKey() { } + + +DateFormat::DateFormat() +: fCalendar(0), + fNumberFormat(0), + fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) +{ +} + +//---------------------------------------------------------------------- + +DateFormat::DateFormat(const DateFormat& other) +: Format(other), + fCalendar(0), + fNumberFormat(0), + fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) +{ + *this = other; +} + +//---------------------------------------------------------------------- + +DateFormat& DateFormat::operator=(const DateFormat& other) +{ + if (this != &other) + { + delete fCalendar; + delete fNumberFormat; + if(other.fCalendar) { + fCalendar = other.fCalendar->clone(); + } else { + fCalendar = nullptr; + } + if(other.fNumberFormat) { + fNumberFormat = other.fNumberFormat->clone(); + } else { + fNumberFormat = nullptr; + } + fBoolFlags = other.fBoolFlags; + fCapitalizationContext = other.fCapitalizationContext; + } + return *this; +} + +//---------------------------------------------------------------------- + +DateFormat::~DateFormat() +{ + delete fCalendar; + delete fNumberFormat; +} + +//---------------------------------------------------------------------- + +bool +DateFormat::operator==(const Format& other) const +{ + if (this == &other) { + return true; + } + if (!(Format::operator==(other))) { + return false; + } + // Format::operator== guarantees that this cast is safe + DateFormat* fmt = (DateFormat*)&other; + return fCalendar&&(fCalendar->isEquivalentTo(*fmt->fCalendar)) && + (fNumberFormat && *fNumberFormat == *fmt->fNumberFormat) && + (fCapitalizationContext == fmt->fCapitalizationContext); +} + +//---------------------------------------------------------------------- + +UnicodeString& +DateFormat::format(const Formattable& obj, + UnicodeString& appendTo, + FieldPosition& fieldPosition, + UErrorCode& status) const +{ + if (U_FAILURE(status)) return appendTo; + + // if the type of the Formattable is double or long, treat it as if it were a Date + UDate date = 0; + switch (obj.getType()) + { + case Formattable::kDate: + date = obj.getDate(); + break; + case Formattable::kDouble: + date = (UDate)obj.getDouble(); + break; + case Formattable::kLong: + date = (UDate)obj.getLong(); + break; + default: + status = U_ILLEGAL_ARGUMENT_ERROR; + return appendTo; + } + + // Is this right? + //if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex()) + // status = U_ILLEGAL_ARGUMENT_ERROR; + + return format(date, appendTo, fieldPosition); +} + +//---------------------------------------------------------------------- + +UnicodeString& +DateFormat::format(const Formattable& obj, + UnicodeString& appendTo, + FieldPositionIterator* posIter, + UErrorCode& status) const +{ + if (U_FAILURE(status)) return appendTo; + + // if the type of the Formattable is double or long, treat it as if it were a Date + UDate date = 0; + switch (obj.getType()) + { + case Formattable::kDate: + date = obj.getDate(); + break; + case Formattable::kDouble: + date = (UDate)obj.getDouble(); + break; + case Formattable::kLong: + date = (UDate)obj.getLong(); + break; + default: + status = U_ILLEGAL_ARGUMENT_ERROR; + return appendTo; + } + + // Is this right? + //if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex()) + // status = U_ILLEGAL_ARGUMENT_ERROR; + + return format(date, appendTo, posIter, status); +} + +//---------------------------------------------------------------------- + +// Default implementation for backwards compatibility, subclasses should implement. +UnicodeString& +DateFormat::format(Calendar& /* unused cal */, + UnicodeString& appendTo, + FieldPositionIterator* /* unused posIter */, + UErrorCode& status) const { + if (U_SUCCESS(status)) { + status = U_UNSUPPORTED_ERROR; + } + return appendTo; +} + +//---------------------------------------------------------------------- + +UnicodeString& +DateFormat::format(UDate date, UnicodeString& appendTo, FieldPosition& fieldPosition) const { + if (fCalendar != nullptr) { + // Use a clone of our calendar instance + Calendar* calClone = fCalendar->clone(); + if (calClone != nullptr) { + UErrorCode ec = U_ZERO_ERROR; + calClone->setTime(date, ec); + if (U_SUCCESS(ec)) { + format(*calClone, appendTo, fieldPosition); + } + delete calClone; + } + } + return appendTo; +} + +//---------------------------------------------------------------------- + +UnicodeString& +DateFormat::format(UDate date, UnicodeString& appendTo, FieldPositionIterator* posIter, + UErrorCode& status) const { + if (fCalendar != nullptr) { + Calendar* calClone = fCalendar->clone(); + if (calClone != nullptr) { + calClone->setTime(date, status); + if (U_SUCCESS(status)) { + format(*calClone, appendTo, posIter, status); + } + delete calClone; + } + } + return appendTo; +} + +//---------------------------------------------------------------------- + +UnicodeString& +DateFormat::format(UDate date, UnicodeString& appendTo) const +{ + // Note that any error information is just lost. That's okay + // for this convenience method. + FieldPosition fpos(FieldPosition::DONT_CARE); + return format(date, appendTo, fpos); +} + +//---------------------------------------------------------------------- + +UDate +DateFormat::parse(const UnicodeString& text, + ParsePosition& pos) const +{ + UDate d = 0; // Error return UDate is 0 (the epoch) + if (fCalendar != nullptr) { + Calendar* calClone = fCalendar->clone(); + if (calClone != nullptr) { + int32_t start = pos.getIndex(); + calClone->clear(); + parse(text, *calClone, pos); + if (pos.getIndex() != start) { + UErrorCode ec = U_ZERO_ERROR; + d = calClone->getTime(ec); + if (U_FAILURE(ec)) { + // We arrive here if fCalendar => calClone is non-lenient and + // there is an out-of-range field. We don't know which field + // was illegal so we set the error index to the start. + pos.setIndex(start); + pos.setErrorIndex(start); + d = 0; + } + } + delete calClone; + } + } + return d; +} + +//---------------------------------------------------------------------- + +UDate +DateFormat::parse(const UnicodeString& text, + UErrorCode& status) const +{ + if (U_FAILURE(status)) return 0; + + ParsePosition pos(0); + UDate result = parse(text, pos); + if (pos.getIndex() == 0) { +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d - - failed to parse - err index %d\n" + , __FILE__, __LINE__, pos.getErrorIndex() ); +#endif + status = U_ILLEGAL_ARGUMENT_ERROR; + } + return result; +} + +//---------------------------------------------------------------------- + +void +DateFormat::parseObject(const UnicodeString& source, + Formattable& result, + ParsePosition& pos) const +{ + result.setDate(parse(source, pos)); +} + +//---------------------------------------------------------------------- + +DateFormat* U_EXPORT2 +DateFormat::createTimeInstance(DateFormat::EStyle style, + const Locale& aLocale) +{ + return createDateTimeInstance(kNone, style, aLocale); +} + +//---------------------------------------------------------------------- + +DateFormat* U_EXPORT2 +DateFormat::createDateInstance(DateFormat::EStyle style, + const Locale& aLocale) +{ + return createDateTimeInstance(style, kNone, aLocale); +} + +//---------------------------------------------------------------------- + +DateFormat* U_EXPORT2 +DateFormat::createDateTimeInstance(EStyle dateStyle, + EStyle timeStyle, + const Locale& aLocale) +{ + if(dateStyle != kNone) + { + dateStyle = (EStyle) (dateStyle + kDateOffset); + } + return create(timeStyle, dateStyle, aLocale); +} + +//---------------------------------------------------------------------- + +DateFormat* U_EXPORT2 +DateFormat::createInstance() +{ + return createDateTimeInstance(kShort, kShort, Locale::getDefault()); +} + +//---------------------------------------------------------------------- + +UnicodeString U_EXPORT2 +DateFormat::getBestPattern( + const Locale &locale, + const UnicodeString &skeleton, + UErrorCode &status) { + UnifiedCache *cache = UnifiedCache::getInstance(status); + if (U_FAILURE(status)) { + return UnicodeString(); + } + DateFmtBestPatternKey key(locale, skeleton, status); + const DateFmtBestPattern *patternPtr = nullptr; + cache->get(key, patternPtr, status); + if (U_FAILURE(status)) { + return UnicodeString(); + } + UnicodeString result(patternPtr->fPattern); + patternPtr->removeRef(); + return result; +} + +DateFormat* U_EXPORT2 +DateFormat::createInstanceForSkeleton( + Calendar *calendarToAdopt, + const UnicodeString& skeleton, + const Locale &locale, + UErrorCode &status) { + LocalPointer calendar(calendarToAdopt); + if (U_FAILURE(status)) { + return nullptr; + } + if (calendar.isNull()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + Locale localeWithCalendar = locale; + localeWithCalendar.setKeywordValue("calendar", calendar->getType(), status); + if (U_FAILURE(status)) { + return nullptr; + } + DateFormat *result = createInstanceForSkeleton(skeleton, localeWithCalendar, status); + if (U_FAILURE(status)) { + return nullptr; + } + result->adoptCalendar(calendar.orphan()); + return result; +} + +DateFormat* U_EXPORT2 +DateFormat::createInstanceForSkeleton( + const UnicodeString& skeleton, + const Locale &locale, + UErrorCode &status) { + if (U_FAILURE(status)) { + return nullptr; + } + LocalPointer df( + new SimpleDateFormat( + getBestPattern(locale, skeleton, status), + locale, status), + status); + return U_SUCCESS(status) ? df.orphan() : nullptr; +} + +DateFormat* U_EXPORT2 +DateFormat::createInstanceForSkeleton( + const UnicodeString& skeleton, + UErrorCode &status) { + return createInstanceForSkeleton( + skeleton, Locale::getDefault(), status); +} + +//---------------------------------------------------------------------- + +DateFormat* U_EXPORT2 +DateFormat::create(EStyle timeStyle, EStyle dateStyle, const Locale& locale) +{ + UErrorCode status = U_ZERO_ERROR; +#if U_PLATFORM_USES_ONLY_WIN32_API + char buffer[8]; + int32_t count = locale.getKeywordValue("compat", buffer, sizeof(buffer), status); + + // if the locale has "@compat=host", create a host-specific DateFormat... + if (count > 0 && uprv_strcmp(buffer, "host") == 0) { + Win32DateFormat *f = new Win32DateFormat(timeStyle, dateStyle, locale, status); + + if (U_SUCCESS(status)) { + return f; + } + + delete f; + } +#endif + + // is it relative? + if(/*((timeStyle!=UDAT_NONE)&&(timeStyle & UDAT_RELATIVE)) || */((dateStyle!=kNone)&&((dateStyle-kDateOffset) & UDAT_RELATIVE))) { + RelativeDateFormat *r = new RelativeDateFormat((UDateFormatStyle)timeStyle, (UDateFormatStyle)(dateStyle-kDateOffset), locale, status); + if(U_SUCCESS(status)) return r; + delete r; + status = U_ZERO_ERROR; + } + + // Try to create a SimpleDateFormat of the desired style. + SimpleDateFormat *f = new SimpleDateFormat(timeStyle, dateStyle, locale, status); + if (U_SUCCESS(status)) return f; + delete f; + + // If that fails, try to create a format using the default pattern and + // the DateFormatSymbols for this locale. + status = U_ZERO_ERROR; + f = new SimpleDateFormat(locale, status); + if (U_SUCCESS(status)) return f; + delete f; + + // This should never really happen, because the preceding constructor + // should always succeed. If the resource data is unavailable, a last + // resort object should be returned. + return 0; +} + +//---------------------------------------------------------------------- + +const Locale* U_EXPORT2 +DateFormat::getAvailableLocales(int32_t& count) +{ + // Get the list of installed locales. + // Even if root has the correct date format for this locale, + // it's still a valid locale (we don't worry about data fallbacks). + return Locale::getAvailableLocales(count); +} + +//---------------------------------------------------------------------- + +void +DateFormat::adoptCalendar(Calendar* newCalendar) +{ + delete fCalendar; + fCalendar = newCalendar; +} + +//---------------------------------------------------------------------- +void +DateFormat::setCalendar(const Calendar& newCalendar) +{ + Calendar* newCalClone = newCalendar.clone(); + if (newCalClone != nullptr) { + adoptCalendar(newCalClone); + } +} + +//---------------------------------------------------------------------- + +const Calendar* +DateFormat::getCalendar() const +{ + return fCalendar; +} + +//---------------------------------------------------------------------- + +void +DateFormat::adoptNumberFormat(NumberFormat* newNumberFormat) +{ + delete fNumberFormat; + fNumberFormat = newNumberFormat; + newNumberFormat->setParseIntegerOnly(true); + newNumberFormat->setGroupingUsed(false); +} +//---------------------------------------------------------------------- + +void +DateFormat::setNumberFormat(const NumberFormat& newNumberFormat) +{ + NumberFormat* newNumFmtClone = newNumberFormat.clone(); + if (newNumFmtClone != nullptr) { + adoptNumberFormat(newNumFmtClone); + } +} + +//---------------------------------------------------------------------- + +const NumberFormat* +DateFormat::getNumberFormat() const +{ + return fNumberFormat; +} + +//---------------------------------------------------------------------- + +void +DateFormat::adoptTimeZone(TimeZone* zone) +{ + if (fCalendar != nullptr) { + fCalendar->adoptTimeZone(zone); + } +} +//---------------------------------------------------------------------- + +void +DateFormat::setTimeZone(const TimeZone& zone) +{ + if (fCalendar != nullptr) { + fCalendar->setTimeZone(zone); + } +} + +//---------------------------------------------------------------------- + +const TimeZone& +DateFormat::getTimeZone() const +{ + if (fCalendar != nullptr) { + return fCalendar->getTimeZone(); + } + // If calendar doesn't exists, create default timezone. + // fCalendar is rarely null + return *(TimeZone::createDefault()); +} + +//---------------------------------------------------------------------- + +void +DateFormat::setLenient(UBool lenient) +{ + if (fCalendar != nullptr) { + fCalendar->setLenient(lenient); + } + UErrorCode status = U_ZERO_ERROR; + setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, lenient, status); + setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, lenient, status); +} + +//---------------------------------------------------------------------- + +UBool +DateFormat::isLenient() const +{ + UBool lenient = true; + if (fCalendar != nullptr) { + lenient = fCalendar->isLenient(); + } + UErrorCode status = U_ZERO_ERROR; + return lenient + && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status) + && getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status); +} + +void +DateFormat::setCalendarLenient(UBool lenient) +{ + if (fCalendar != nullptr) { + fCalendar->setLenient(lenient); + } +} + +//---------------------------------------------------------------------- + +UBool +DateFormat::isCalendarLenient() const +{ + if (fCalendar != nullptr) { + return fCalendar->isLenient(); + } + // fCalendar is rarely null + return false; +} + + +//---------------------------------------------------------------------- + + +void DateFormat::setContext(UDisplayContext value, UErrorCode& status) +{ + if (U_FAILURE(status)) + return; + if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) { + fCapitalizationContext = value; + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + } +} + + +//---------------------------------------------------------------------- + + +UDisplayContext DateFormat::getContext(UDisplayContextType type, UErrorCode& status) const +{ + if (U_FAILURE(status)) + return (UDisplayContext)0; + if (type != UDISPCTX_TYPE_CAPITALIZATION) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return (UDisplayContext)0; + } + return fCapitalizationContext; +} + + +//---------------------------------------------------------------------- + + +DateFormat& +DateFormat::setBooleanAttribute(UDateFormatBooleanAttribute attr, + UBool newValue, + UErrorCode &status) { + if(!fBoolFlags.isValidValue(newValue)) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } else { + fBoolFlags.set(attr, newValue); + } + + return *this; +} + +//---------------------------------------------------------------------- + +UBool +DateFormat::getBooleanAttribute(UDateFormatBooleanAttribute attr, UErrorCode &/*status*/) const { + + return static_cast(fBoolFlags.get(attr)); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/dayperiodrules.cpp b/deps/icu-small/source/i18n/dayperiodrules.cpp index 3ef822842de46b..684a555cfc0678 100644 --- a/deps/icu-small/source/i18n/dayperiodrules.cpp +++ b/deps/icu-small/source/i18n/dayperiodrules.cpp @@ -1,515 +1,515 @@ -// © 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. -******************************************************************************* -* dayperiodrules.cpp -* -* created on: 2016-01-20 -* created by: kazede -*/ - -#include "dayperiodrules.h" - -#include "unicode/ures.h" -#include "charstr.h" -#include "cstring.h" -#include "ucln_in.h" -#include "uhash.h" -#include "umutex.h" -#include "uresimp.h" - - -U_NAMESPACE_BEGIN - -namespace { - -struct DayPeriodRulesData : public UMemory { - DayPeriodRulesData() : localeToRuleSetNumMap(NULL), rules(NULL), maxRuleSetNum(0) {} - - UHashtable *localeToRuleSetNumMap; - DayPeriodRules *rules; - int32_t maxRuleSetNum; -} *data = NULL; - -enum CutoffType { - CUTOFF_TYPE_UNKNOWN = -1, - CUTOFF_TYPE_BEFORE, - CUTOFF_TYPE_AFTER, // TODO: AFTER is deprecated in CLDR 29. Remove. - CUTOFF_TYPE_FROM, - CUTOFF_TYPE_AT -}; - -} // namespace - -struct DayPeriodRulesDataSink : public ResourceSink { - DayPeriodRulesDataSink() { - for (int32_t i = 0; i < UPRV_LENGTHOF(cutoffs); ++i) { cutoffs[i] = 0; } - } - virtual ~DayPeriodRulesDataSink(); - - virtual void put(const char *key, ResourceValue &value, UBool, UErrorCode &errorCode) override { - ResourceTable dayPeriodData = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - - for (int32_t i = 0; dayPeriodData.getKeyAndValue(i, key, value); ++i) { - if (uprv_strcmp(key, "locales") == 0) { - ResourceTable locales = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - - for (int32_t j = 0; locales.getKeyAndValue(j, key, value); ++j) { - UnicodeString setNum_str = value.getUnicodeString(errorCode); - int32_t setNum = parseSetNum(setNum_str, errorCode); - uhash_puti(data->localeToRuleSetNumMap, const_cast(key), setNum, &errorCode); - } - } else if (uprv_strcmp(key, "rules") == 0) { - // Allocate one more than needed to skip [0]. See comment in parseSetNum(). - data->rules = new DayPeriodRules[data->maxRuleSetNum + 1]; - if (data->rules == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - ResourceTable rules = value.getTable(errorCode); - processRules(rules, key, value, errorCode); - if (U_FAILURE(errorCode)) { return; } - } - } - } - - void processRules(const ResourceTable &rules, const char *key, - ResourceValue &value, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return; } - - for (int32_t i = 0; rules.getKeyAndValue(i, key, value); ++i) { - ruleSetNum = parseSetNum(key, errorCode); - ResourceTable ruleSet = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - - for (int32_t j = 0; ruleSet.getKeyAndValue(j, key, value); ++j) { - period = DayPeriodRules::getDayPeriodFromString(key); - if (period == DayPeriodRules::DAYPERIOD_UNKNOWN) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - ResourceTable periodDefinition = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - - for (int32_t k = 0; periodDefinition.getKeyAndValue(k, key, value); ++k) { - if (value.getType() == URES_STRING) { - // Key-value pairs (e.g. before{6:00}). - CutoffType type = getCutoffTypeFromString(key); - addCutoff(type, value.getUnicodeString(errorCode), errorCode); - if (U_FAILURE(errorCode)) { return; } - } else { - // Arrays (e.g. before{6:00, 24:00}). - cutoffType = getCutoffTypeFromString(key); - ResourceArray cutoffArray = value.getArray(errorCode); - if (U_FAILURE(errorCode)) { return; } - - int32_t length = cutoffArray.getSize(); - for (int32_t l = 0; l < length; ++l) { - cutoffArray.getValue(l, value); - addCutoff(cutoffType, value.getUnicodeString(errorCode), errorCode); - if (U_FAILURE(errorCode)) { return; } - } - } - } - setDayPeriodForHoursFromCutoffs(errorCode); - for (int32_t k = 0; k < UPRV_LENGTHOF(cutoffs); ++k) { - cutoffs[k] = 0; - } - } - - if (!data->rules[ruleSetNum].allHoursAreSet()) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - } - } - - // Members. - int32_t cutoffs[25]; // [0] thru [24]: 24 is allowed in "before 24". - - // "Path" to data. - int32_t ruleSetNum; - DayPeriodRules::DayPeriod period; - CutoffType cutoffType; - - // Helpers. - static int32_t parseSetNum(const UnicodeString &setNumStr, UErrorCode &errorCode) { - CharString cs; - cs.appendInvariantChars(setNumStr, errorCode); - return parseSetNum(cs.data(), errorCode); - } - - static int32_t parseSetNum(const char *setNumStr, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return -1; } - - if (uprv_strncmp(setNumStr, "set", 3) != 0) { - errorCode = U_INVALID_FORMAT_ERROR; - return -1; - } - - int32_t i = 3; - int32_t setNum = 0; - while (setNumStr[i] != 0) { - int32_t digit = setNumStr[i] - '0'; - if (digit < 0 || 9 < digit) { - errorCode = U_INVALID_FORMAT_ERROR; - return -1; - } - setNum = 10 * setNum + digit; - ++i; - } - - // Rule set number must not be zero. (0 is used to indicate "not found" by hashmap.) - // Currently ICU data conveniently starts numbering rule sets from 1. - if (setNum == 0) { - errorCode = U_INVALID_FORMAT_ERROR; - return -1; - } else { - return setNum; - } - } - - void addCutoff(CutoffType type, const UnicodeString &hour_str, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return; } - - if (type == CUTOFF_TYPE_UNKNOWN) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - - int32_t hour = parseHour(hour_str, errorCode); - if (U_FAILURE(errorCode)) { return; } - - cutoffs[hour] |= 1 << type; - } - - // Translate the cutoffs[] array to day period rules. - void setDayPeriodForHoursFromCutoffs(UErrorCode &errorCode) { - DayPeriodRules &rule = data->rules[ruleSetNum]; - - for (int32_t startHour = 0; startHour <= 24; ++startHour) { - // AT cutoffs must be either midnight or noon. - if (cutoffs[startHour] & (1 << CUTOFF_TYPE_AT)) { - if (startHour == 0 && period == DayPeriodRules::DAYPERIOD_MIDNIGHT) { - rule.fHasMidnight = true; - } else if (startHour == 12 && period == DayPeriodRules::DAYPERIOD_NOON) { - rule.fHasNoon = true; - } else { - errorCode = U_INVALID_FORMAT_ERROR; // Bad data. - return; - } - } - - // FROM/AFTER and BEFORE must come in a pair. - if (cutoffs[startHour] & (1 << CUTOFF_TYPE_FROM) || - cutoffs[startHour] & (1 << CUTOFF_TYPE_AFTER)) { - for (int32_t hour = startHour + 1;; ++hour) { - if (hour == startHour) { - // We've gone around the array once and can't find a BEFORE. - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - if (hour == 25) { hour = 0; } - if (cutoffs[hour] & (1 << CUTOFF_TYPE_BEFORE)) { - rule.add(startHour, hour, period); - break; - } - } - } - } - } - - // Translate "before" to CUTOFF_TYPE_BEFORE, for example. - static CutoffType getCutoffTypeFromString(const char *type_str) { - if (uprv_strcmp(type_str, "from") == 0) { - return CUTOFF_TYPE_FROM; - } else if (uprv_strcmp(type_str, "before") == 0) { - return CUTOFF_TYPE_BEFORE; - } else if (uprv_strcmp(type_str, "after") == 0) { - return CUTOFF_TYPE_AFTER; - } else if (uprv_strcmp(type_str, "at") == 0) { - return CUTOFF_TYPE_AT; - } else { - return CUTOFF_TYPE_UNKNOWN; - } - } - - // Gets the numerical value of the hour from the Unicode string. - static int32_t parseHour(const UnicodeString &time, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { - return 0; - } - - int32_t hourLimit = time.length() - 3; - // `time` must look like "x:00" or "xx:00". - // If length is wrong or `time` doesn't end with ":00", error out. - if ((hourLimit != 1 && hourLimit != 2) || - time[hourLimit] != 0x3A || time[hourLimit + 1] != 0x30 || - time[hourLimit + 2] != 0x30) { - errorCode = U_INVALID_FORMAT_ERROR; - return 0; - } - - // If `time` doesn't begin with a number in [0, 24], error out. - // Note: "24:00" is possible in "before 24:00". - int32_t hour = time[0] - 0x30; - if (hour < 0 || 9 < hour) { - errorCode = U_INVALID_FORMAT_ERROR; - return 0; - } - if (hourLimit == 2) { - int32_t hourDigit2 = time[1] - 0x30; - if (hourDigit2 < 0 || 9 < hourDigit2) { - errorCode = U_INVALID_FORMAT_ERROR; - return 0; - } - hour = hour * 10 + hourDigit2; - if (hour > 24) { - errorCode = U_INVALID_FORMAT_ERROR; - return 0; - } - } - - return hour; - } -}; // struct DayPeriodRulesDataSink - -struct DayPeriodRulesCountSink : public ResourceSink { - virtual ~DayPeriodRulesCountSink(); - - virtual void put(const char *key, ResourceValue &value, UBool, UErrorCode &errorCode) override { - ResourceTable rules = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - - for (int32_t i = 0; rules.getKeyAndValue(i, key, value); ++i) { - int32_t setNum = DayPeriodRulesDataSink::parseSetNum(key, errorCode); - if (setNum > data->maxRuleSetNum) { - data->maxRuleSetNum = setNum; - } - } - } -}; - -// Out-of-line virtual destructors. -DayPeriodRulesDataSink::~DayPeriodRulesDataSink() {} -DayPeriodRulesCountSink::~DayPeriodRulesCountSink() {} - -namespace { - -UInitOnce initOnce {}; - -U_CFUNC UBool U_CALLCONV dayPeriodRulesCleanup() { - delete[] data->rules; - uhash_close(data->localeToRuleSetNumMap); - delete data; - data = NULL; - return true; -} - -} // namespace - -void U_CALLCONV DayPeriodRules::load(UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { - return; - } - - data = new DayPeriodRulesData(); - data->localeToRuleSetNumMap = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &errorCode); - LocalUResourceBundlePointer rb_dayPeriods(ures_openDirect(NULL, "dayPeriods", &errorCode)); - - // Get the largest rule set number (so we allocate enough objects). - DayPeriodRulesCountSink countSink; - ures_getAllItemsWithFallback(rb_dayPeriods.getAlias(), "rules", countSink, errorCode); - - // Populate rules. - DayPeriodRulesDataSink sink; - ures_getAllItemsWithFallback(rb_dayPeriods.getAlias(), "", sink, errorCode); - - ucln_i18n_registerCleanup(UCLN_I18N_DAYPERIODRULES, dayPeriodRulesCleanup); -} - -const DayPeriodRules *DayPeriodRules::getInstance(const Locale &locale, UErrorCode &errorCode) { - umtx_initOnce(initOnce, DayPeriodRules::load, errorCode); - - // If the entire day period rules data doesn't conform to spec (even if the part we want - // does), return NULL. - if(U_FAILURE(errorCode)) { return NULL; } - - const char *localeCode = locale.getBaseName(); - char name[ULOC_FULLNAME_CAPACITY]; - char parentName[ULOC_FULLNAME_CAPACITY]; - - if (uprv_strlen(localeCode) < ULOC_FULLNAME_CAPACITY) { - uprv_strcpy(name, localeCode); - - // Treat empty string as root. - if (*name == '\0') { - uprv_strcpy(name, "root"); - } - } else { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return NULL; - } - - int32_t ruleSetNum = 0; // NB there is no rule set 0 and 0 is returned upon lookup failure. - while (*name != '\0') { - ruleSetNum = uhash_geti(data->localeToRuleSetNumMap, name); - if (ruleSetNum == 0) { - // name and parentName can't be the same pointer, so fill in parent then copy to child. - uloc_getParent(name, parentName, ULOC_FULLNAME_CAPACITY, &errorCode); - if (*parentName == '\0') { - // Saves a lookup in the hash table. - break; - } - uprv_strcpy(name, parentName); - } else { - break; - } - } - - if (ruleSetNum <= 0 || data->rules[ruleSetNum].getDayPeriodForHour(0) == DAYPERIOD_UNKNOWN) { - // If day period for hour 0 is UNKNOWN then day period for all hours are UNKNOWN. - // Data doesn't exist even with fallback. - return NULL; - } else { - return &data->rules[ruleSetNum]; - } -} - -DayPeriodRules::DayPeriodRules() : fHasMidnight(false), fHasNoon(false) { - for (int32_t i = 0; i < 24; ++i) { - fDayPeriodForHour[i] = DayPeriodRules::DAYPERIOD_UNKNOWN; - } -} - -double DayPeriodRules::getMidPointForDayPeriod( - DayPeriodRules::DayPeriod dayPeriod, UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { return -1; } - - int32_t startHour = getStartHourForDayPeriod(dayPeriod, errorCode); - int32_t endHour = getEndHourForDayPeriod(dayPeriod, errorCode); - // Can't obtain startHour or endHour; bail out. - if (U_FAILURE(errorCode)) { return -1; } - - double midPoint = (startHour + endHour) / 2.0; - - if (startHour > endHour) { - // dayPeriod wraps around midnight. Shift midPoint by 12 hours, in the direction that - // lands it in [0, 24). - midPoint += 12; - if (midPoint >= 24) { - midPoint -= 24; - } - } - - return midPoint; -} - -int32_t DayPeriodRules::getStartHourForDayPeriod( - DayPeriodRules::DayPeriod dayPeriod, UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { return -1; } - - if (dayPeriod == DAYPERIOD_MIDNIGHT) { return 0; } - if (dayPeriod == DAYPERIOD_NOON) { return 12; } - - if (fDayPeriodForHour[0] == dayPeriod && fDayPeriodForHour[23] == dayPeriod) { - // dayPeriod wraps around midnight. Start hour is later than end hour. - for (int32_t i = 22; i >= 1; --i) { - if (fDayPeriodForHour[i] != dayPeriod) { - return (i + 1); - } - } - } else { - for (int32_t i = 0; i <= 23; ++i) { - if (fDayPeriodForHour[i] == dayPeriod) { - return i; - } - } - } - - // dayPeriod doesn't exist in rule set; set error and exit. - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return -1; -} - -int32_t DayPeriodRules::getEndHourForDayPeriod( - DayPeriodRules::DayPeriod dayPeriod, UErrorCode &errorCode) const { - if (U_FAILURE(errorCode)) { return -1; } - - if (dayPeriod == DAYPERIOD_MIDNIGHT) { return 0; } - if (dayPeriod == DAYPERIOD_NOON) { return 12; } - - if (fDayPeriodForHour[0] == dayPeriod && fDayPeriodForHour[23] == dayPeriod) { - // dayPeriod wraps around midnight. End hour is before start hour. - for (int32_t i = 1; i <= 22; ++i) { - if (fDayPeriodForHour[i] != dayPeriod) { - // i o'clock is when a new period starts, therefore when the old period ends. - return i; - } - } - } else { - for (int32_t i = 23; i >= 0; --i) { - if (fDayPeriodForHour[i] == dayPeriod) { - return (i + 1); - } - } - } - - // dayPeriod doesn't exist in rule set; set error and exit. - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return -1; -} - -DayPeriodRules::DayPeriod DayPeriodRules::getDayPeriodFromString(const char *type_str) { - if (uprv_strcmp(type_str, "midnight") == 0) { - return DAYPERIOD_MIDNIGHT; - } else if (uprv_strcmp(type_str, "noon") == 0) { - return DAYPERIOD_NOON; - } else if (uprv_strcmp(type_str, "morning1") == 0) { - return DAYPERIOD_MORNING1; - } else if (uprv_strcmp(type_str, "afternoon1") == 0) { - return DAYPERIOD_AFTERNOON1; - } else if (uprv_strcmp(type_str, "evening1") == 0) { - return DAYPERIOD_EVENING1; - } else if (uprv_strcmp(type_str, "night1") == 0) { - return DAYPERIOD_NIGHT1; - } else if (uprv_strcmp(type_str, "morning2") == 0) { - return DAYPERIOD_MORNING2; - } else if (uprv_strcmp(type_str, "afternoon2") == 0) { - return DAYPERIOD_AFTERNOON2; - } else if (uprv_strcmp(type_str, "evening2") == 0) { - return DAYPERIOD_EVENING2; - } else if (uprv_strcmp(type_str, "night2") == 0) { - return DAYPERIOD_NIGHT2; - } else if (uprv_strcmp(type_str, "am") == 0) { - return DAYPERIOD_AM; - } else if (uprv_strcmp(type_str, "pm") == 0) { - return DAYPERIOD_PM; - } else { - return DAYPERIOD_UNKNOWN; - } -} - -void DayPeriodRules::add(int32_t startHour, int32_t limitHour, DayPeriod period) { - for (int32_t i = startHour; i != limitHour; ++i) { - if (i == 24) { i = 0; } - fDayPeriodForHour[i] = period; - } -} - -UBool DayPeriodRules::allHoursAreSet() { - for (int32_t i = 0; i < 24; ++i) { - if (fDayPeriodForHour[i] == DAYPERIOD_UNKNOWN) { return false; } - } - - return true; -} - - - -U_NAMESPACE_END +// © 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. +******************************************************************************* +* dayperiodrules.cpp +* +* created on: 2016-01-20 +* created by: kazede +*/ + +#include "dayperiodrules.h" + +#include "unicode/ures.h" +#include "charstr.h" +#include "cstring.h" +#include "ucln_in.h" +#include "uhash.h" +#include "umutex.h" +#include "uresimp.h" + + +U_NAMESPACE_BEGIN + +namespace { + +struct DayPeriodRulesData : public UMemory { + DayPeriodRulesData() : localeToRuleSetNumMap(nullptr), rules(nullptr), maxRuleSetNum(0) {} + + UHashtable *localeToRuleSetNumMap; + DayPeriodRules *rules; + int32_t maxRuleSetNum; +} *data = nullptr; + +enum CutoffType { + CUTOFF_TYPE_UNKNOWN = -1, + CUTOFF_TYPE_BEFORE, + CUTOFF_TYPE_AFTER, // TODO: AFTER is deprecated in CLDR 29. Remove. + CUTOFF_TYPE_FROM, + CUTOFF_TYPE_AT +}; + +} // namespace + +struct DayPeriodRulesDataSink : public ResourceSink { + DayPeriodRulesDataSink() { + for (int32_t i = 0; i < UPRV_LENGTHOF(cutoffs); ++i) { cutoffs[i] = 0; } + } + virtual ~DayPeriodRulesDataSink(); + + virtual void put(const char *key, ResourceValue &value, UBool, UErrorCode &errorCode) override { + ResourceTable dayPeriodData = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + + for (int32_t i = 0; dayPeriodData.getKeyAndValue(i, key, value); ++i) { + if (uprv_strcmp(key, "locales") == 0) { + ResourceTable locales = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + + for (int32_t j = 0; locales.getKeyAndValue(j, key, value); ++j) { + UnicodeString setNum_str = value.getUnicodeString(errorCode); + int32_t setNum = parseSetNum(setNum_str, errorCode); + uhash_puti(data->localeToRuleSetNumMap, const_cast(key), setNum, &errorCode); + } + } else if (uprv_strcmp(key, "rules") == 0) { + // Allocate one more than needed to skip [0]. See comment in parseSetNum(). + data->rules = new DayPeriodRules[data->maxRuleSetNum + 1]; + if (data->rules == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + ResourceTable rules = value.getTable(errorCode); + processRules(rules, key, value, errorCode); + if (U_FAILURE(errorCode)) { return; } + } + } + } + + void processRules(const ResourceTable &rules, const char *key, + ResourceValue &value, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return; } + + for (int32_t i = 0; rules.getKeyAndValue(i, key, value); ++i) { + ruleSetNum = parseSetNum(key, errorCode); + ResourceTable ruleSet = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + + for (int32_t j = 0; ruleSet.getKeyAndValue(j, key, value); ++j) { + period = DayPeriodRules::getDayPeriodFromString(key); + if (period == DayPeriodRules::DAYPERIOD_UNKNOWN) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + ResourceTable periodDefinition = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + + for (int32_t k = 0; periodDefinition.getKeyAndValue(k, key, value); ++k) { + if (value.getType() == URES_STRING) { + // Key-value pairs (e.g. before{6:00}). + CutoffType type = getCutoffTypeFromString(key); + addCutoff(type, value.getUnicodeString(errorCode), errorCode); + if (U_FAILURE(errorCode)) { return; } + } else { + // Arrays (e.g. before{6:00, 24:00}). + cutoffType = getCutoffTypeFromString(key); + ResourceArray cutoffArray = value.getArray(errorCode); + if (U_FAILURE(errorCode)) { return; } + + int32_t length = cutoffArray.getSize(); + for (int32_t l = 0; l < length; ++l) { + cutoffArray.getValue(l, value); + addCutoff(cutoffType, value.getUnicodeString(errorCode), errorCode); + if (U_FAILURE(errorCode)) { return; } + } + } + } + setDayPeriodForHoursFromCutoffs(errorCode); + for (int32_t k = 0; k < UPRV_LENGTHOF(cutoffs); ++k) { + cutoffs[k] = 0; + } + } + + if (!data->rules[ruleSetNum].allHoursAreSet()) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + } + } + + // Members. + int32_t cutoffs[25]; // [0] thru [24]: 24 is allowed in "before 24". + + // "Path" to data. + int32_t ruleSetNum; + DayPeriodRules::DayPeriod period; + CutoffType cutoffType; + + // Helpers. + static int32_t parseSetNum(const UnicodeString &setNumStr, UErrorCode &errorCode) { + CharString cs; + cs.appendInvariantChars(setNumStr, errorCode); + return parseSetNum(cs.data(), errorCode); + } + + static int32_t parseSetNum(const char *setNumStr, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return -1; } + + if (uprv_strncmp(setNumStr, "set", 3) != 0) { + errorCode = U_INVALID_FORMAT_ERROR; + return -1; + } + + int32_t i = 3; + int32_t setNum = 0; + while (setNumStr[i] != 0) { + int32_t digit = setNumStr[i] - '0'; + if (digit < 0 || 9 < digit) { + errorCode = U_INVALID_FORMAT_ERROR; + return -1; + } + setNum = 10 * setNum + digit; + ++i; + } + + // Rule set number must not be zero. (0 is used to indicate "not found" by hashmap.) + // Currently ICU data conveniently starts numbering rule sets from 1. + if (setNum == 0) { + errorCode = U_INVALID_FORMAT_ERROR; + return -1; + } else { + return setNum; + } + } + + void addCutoff(CutoffType type, const UnicodeString &hour_str, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return; } + + if (type == CUTOFF_TYPE_UNKNOWN) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + + int32_t hour = parseHour(hour_str, errorCode); + if (U_FAILURE(errorCode)) { return; } + + cutoffs[hour] |= 1 << type; + } + + // Translate the cutoffs[] array to day period rules. + void setDayPeriodForHoursFromCutoffs(UErrorCode &errorCode) { + DayPeriodRules &rule = data->rules[ruleSetNum]; + + for (int32_t startHour = 0; startHour <= 24; ++startHour) { + // AT cutoffs must be either midnight or noon. + if (cutoffs[startHour] & (1 << CUTOFF_TYPE_AT)) { + if (startHour == 0 && period == DayPeriodRules::DAYPERIOD_MIDNIGHT) { + rule.fHasMidnight = true; + } else if (startHour == 12 && period == DayPeriodRules::DAYPERIOD_NOON) { + rule.fHasNoon = true; + } else { + errorCode = U_INVALID_FORMAT_ERROR; // Bad data. + return; + } + } + + // FROM/AFTER and BEFORE must come in a pair. + if (cutoffs[startHour] & (1 << CUTOFF_TYPE_FROM) || + cutoffs[startHour] & (1 << CUTOFF_TYPE_AFTER)) { + for (int32_t hour = startHour + 1;; ++hour) { + if (hour == startHour) { + // We've gone around the array once and can't find a BEFORE. + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + if (hour == 25) { hour = 0; } + if (cutoffs[hour] & (1 << CUTOFF_TYPE_BEFORE)) { + rule.add(startHour, hour, period); + break; + } + } + } + } + } + + // Translate "before" to CUTOFF_TYPE_BEFORE, for example. + static CutoffType getCutoffTypeFromString(const char *type_str) { + if (uprv_strcmp(type_str, "from") == 0) { + return CUTOFF_TYPE_FROM; + } else if (uprv_strcmp(type_str, "before") == 0) { + return CUTOFF_TYPE_BEFORE; + } else if (uprv_strcmp(type_str, "after") == 0) { + return CUTOFF_TYPE_AFTER; + } else if (uprv_strcmp(type_str, "at") == 0) { + return CUTOFF_TYPE_AT; + } else { + return CUTOFF_TYPE_UNKNOWN; + } + } + + // Gets the numerical value of the hour from the Unicode string. + static int32_t parseHour(const UnicodeString &time, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { + return 0; + } + + int32_t hourLimit = time.length() - 3; + // `time` must look like "x:00" or "xx:00". + // If length is wrong or `time` doesn't end with ":00", error out. + if ((hourLimit != 1 && hourLimit != 2) || + time[hourLimit] != 0x3A || time[hourLimit + 1] != 0x30 || + time[hourLimit + 2] != 0x30) { + errorCode = U_INVALID_FORMAT_ERROR; + return 0; + } + + // If `time` doesn't begin with a number in [0, 24], error out. + // Note: "24:00" is possible in "before 24:00". + int32_t hour = time[0] - 0x30; + if (hour < 0 || 9 < hour) { + errorCode = U_INVALID_FORMAT_ERROR; + return 0; + } + if (hourLimit == 2) { + int32_t hourDigit2 = time[1] - 0x30; + if (hourDigit2 < 0 || 9 < hourDigit2) { + errorCode = U_INVALID_FORMAT_ERROR; + return 0; + } + hour = hour * 10 + hourDigit2; + if (hour > 24) { + errorCode = U_INVALID_FORMAT_ERROR; + return 0; + } + } + + return hour; + } +}; // struct DayPeriodRulesDataSink + +struct DayPeriodRulesCountSink : public ResourceSink { + virtual ~DayPeriodRulesCountSink(); + + virtual void put(const char *key, ResourceValue &value, UBool, UErrorCode &errorCode) override { + ResourceTable rules = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + + for (int32_t i = 0; rules.getKeyAndValue(i, key, value); ++i) { + int32_t setNum = DayPeriodRulesDataSink::parseSetNum(key, errorCode); + if (setNum > data->maxRuleSetNum) { + data->maxRuleSetNum = setNum; + } + } + } +}; + +// Out-of-line virtual destructors. +DayPeriodRulesDataSink::~DayPeriodRulesDataSink() {} +DayPeriodRulesCountSink::~DayPeriodRulesCountSink() {} + +namespace { + +UInitOnce initOnce {}; + +U_CFUNC UBool U_CALLCONV dayPeriodRulesCleanup() { + delete[] data->rules; + uhash_close(data->localeToRuleSetNumMap); + delete data; + data = nullptr; + return true; +} + +} // namespace + +void U_CALLCONV DayPeriodRules::load(UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { + return; + } + + data = new DayPeriodRulesData(); + data->localeToRuleSetNumMap = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &errorCode); + LocalUResourceBundlePointer rb_dayPeriods(ures_openDirect(nullptr, "dayPeriods", &errorCode)); + + // Get the largest rule set number (so we allocate enough objects). + DayPeriodRulesCountSink countSink; + ures_getAllItemsWithFallback(rb_dayPeriods.getAlias(), "rules", countSink, errorCode); + + // Populate rules. + DayPeriodRulesDataSink sink; + ures_getAllItemsWithFallback(rb_dayPeriods.getAlias(), "", sink, errorCode); + + ucln_i18n_registerCleanup(UCLN_I18N_DAYPERIODRULES, dayPeriodRulesCleanup); +} + +const DayPeriodRules *DayPeriodRules::getInstance(const Locale &locale, UErrorCode &errorCode) { + umtx_initOnce(initOnce, DayPeriodRules::load, errorCode); + + // If the entire day period rules data doesn't conform to spec (even if the part we want + // does), return nullptr. + if(U_FAILURE(errorCode)) { return nullptr; } + + const char *localeCode = locale.getBaseName(); + char name[ULOC_FULLNAME_CAPACITY]; + char parentName[ULOC_FULLNAME_CAPACITY]; + + if (uprv_strlen(localeCode) < ULOC_FULLNAME_CAPACITY) { + uprv_strcpy(name, localeCode); + + // Treat empty string as root. + if (*name == '\0') { + uprv_strcpy(name, "root"); + } + } else { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return nullptr; + } + + int32_t ruleSetNum = 0; // NB there is no rule set 0 and 0 is returned upon lookup failure. + while (*name != '\0') { + ruleSetNum = uhash_geti(data->localeToRuleSetNumMap, name); + if (ruleSetNum == 0) { + // name and parentName can't be the same pointer, so fill in parent then copy to child. + uloc_getParent(name, parentName, ULOC_FULLNAME_CAPACITY, &errorCode); + if (*parentName == '\0') { + // Saves a lookup in the hash table. + break; + } + uprv_strcpy(name, parentName); + } else { + break; + } + } + + if (ruleSetNum <= 0 || data->rules[ruleSetNum].getDayPeriodForHour(0) == DAYPERIOD_UNKNOWN) { + // If day period for hour 0 is UNKNOWN then day period for all hours are UNKNOWN. + // Data doesn't exist even with fallback. + return nullptr; + } else { + return &data->rules[ruleSetNum]; + } +} + +DayPeriodRules::DayPeriodRules() : fHasMidnight(false), fHasNoon(false) { + for (int32_t i = 0; i < 24; ++i) { + fDayPeriodForHour[i] = DayPeriodRules::DAYPERIOD_UNKNOWN; + } +} + +double DayPeriodRules::getMidPointForDayPeriod( + DayPeriodRules::DayPeriod dayPeriod, UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { return -1; } + + int32_t startHour = getStartHourForDayPeriod(dayPeriod, errorCode); + int32_t endHour = getEndHourForDayPeriod(dayPeriod, errorCode); + // Can't obtain startHour or endHour; bail out. + if (U_FAILURE(errorCode)) { return -1; } + + double midPoint = (startHour + endHour) / 2.0; + + if (startHour > endHour) { + // dayPeriod wraps around midnight. Shift midPoint by 12 hours, in the direction that + // lands it in [0, 24). + midPoint += 12; + if (midPoint >= 24) { + midPoint -= 24; + } + } + + return midPoint; +} + +int32_t DayPeriodRules::getStartHourForDayPeriod( + DayPeriodRules::DayPeriod dayPeriod, UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { return -1; } + + if (dayPeriod == DAYPERIOD_MIDNIGHT) { return 0; } + if (dayPeriod == DAYPERIOD_NOON) { return 12; } + + if (fDayPeriodForHour[0] == dayPeriod && fDayPeriodForHour[23] == dayPeriod) { + // dayPeriod wraps around midnight. Start hour is later than end hour. + for (int32_t i = 22; i >= 1; --i) { + if (fDayPeriodForHour[i] != dayPeriod) { + return (i + 1); + } + } + } else { + for (int32_t i = 0; i <= 23; ++i) { + if (fDayPeriodForHour[i] == dayPeriod) { + return i; + } + } + } + + // dayPeriod doesn't exist in rule set; set error and exit. + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return -1; +} + +int32_t DayPeriodRules::getEndHourForDayPeriod( + DayPeriodRules::DayPeriod dayPeriod, UErrorCode &errorCode) const { + if (U_FAILURE(errorCode)) { return -1; } + + if (dayPeriod == DAYPERIOD_MIDNIGHT) { return 0; } + if (dayPeriod == DAYPERIOD_NOON) { return 12; } + + if (fDayPeriodForHour[0] == dayPeriod && fDayPeriodForHour[23] == dayPeriod) { + // dayPeriod wraps around midnight. End hour is before start hour. + for (int32_t i = 1; i <= 22; ++i) { + if (fDayPeriodForHour[i] != dayPeriod) { + // i o'clock is when a new period starts, therefore when the old period ends. + return i; + } + } + } else { + for (int32_t i = 23; i >= 0; --i) { + if (fDayPeriodForHour[i] == dayPeriod) { + return (i + 1); + } + } + } + + // dayPeriod doesn't exist in rule set; set error and exit. + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return -1; +} + +DayPeriodRules::DayPeriod DayPeriodRules::getDayPeriodFromString(const char *type_str) { + if (uprv_strcmp(type_str, "midnight") == 0) { + return DAYPERIOD_MIDNIGHT; + } else if (uprv_strcmp(type_str, "noon") == 0) { + return DAYPERIOD_NOON; + } else if (uprv_strcmp(type_str, "morning1") == 0) { + return DAYPERIOD_MORNING1; + } else if (uprv_strcmp(type_str, "afternoon1") == 0) { + return DAYPERIOD_AFTERNOON1; + } else if (uprv_strcmp(type_str, "evening1") == 0) { + return DAYPERIOD_EVENING1; + } else if (uprv_strcmp(type_str, "night1") == 0) { + return DAYPERIOD_NIGHT1; + } else if (uprv_strcmp(type_str, "morning2") == 0) { + return DAYPERIOD_MORNING2; + } else if (uprv_strcmp(type_str, "afternoon2") == 0) { + return DAYPERIOD_AFTERNOON2; + } else if (uprv_strcmp(type_str, "evening2") == 0) { + return DAYPERIOD_EVENING2; + } else if (uprv_strcmp(type_str, "night2") == 0) { + return DAYPERIOD_NIGHT2; + } else if (uprv_strcmp(type_str, "am") == 0) { + return DAYPERIOD_AM; + } else if (uprv_strcmp(type_str, "pm") == 0) { + return DAYPERIOD_PM; + } else { + return DAYPERIOD_UNKNOWN; + } +} + +void DayPeriodRules::add(int32_t startHour, int32_t limitHour, DayPeriod period) { + for (int32_t i = startHour; i != limitHour; ++i) { + if (i == 24) { i = 0; } + fDayPeriodForHour[i] = period; + } +} + +UBool DayPeriodRules::allHoursAreSet() { + for (int32_t i = 0; i < 24; ++i) { + if (fDayPeriodForHour[i] == DAYPERIOD_UNKNOWN) { return false; } + } + + return true; +} + + + +U_NAMESPACE_END diff --git a/deps/icu-small/source/i18n/dayperiodrules.h b/deps/icu-small/source/i18n/dayperiodrules.h index 4bfca762b8079b..3295a2dd678a26 100644 --- a/deps/icu-small/source/i18n/dayperiodrules.h +++ b/deps/icu-small/source/i18n/dayperiodrules.h @@ -1,89 +1,89 @@ -// © 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. -******************************************************************************* -* dayperiodrules.h -* -* created on: 2016-01-20 -* created by: kazede -*/ - -#ifndef DAYPERIODRULES_H -#define DAYPERIODRULES_H - -#include "unicode/locid.h" -#include "unicode/unistr.h" -#include "unicode/uobject.h" -#include "unicode/utypes.h" -#include "resource.h" -#include "uhash.h" - - - -U_NAMESPACE_BEGIN - -struct DayPeriodRulesDataSink; - -class DayPeriodRules : public UMemory { - friend struct DayPeriodRulesDataSink; -public: - enum DayPeriod { - DAYPERIOD_UNKNOWN = -1, - DAYPERIOD_MIDNIGHT, - DAYPERIOD_NOON, - DAYPERIOD_MORNING1, - DAYPERIOD_AFTERNOON1, - DAYPERIOD_EVENING1, - DAYPERIOD_NIGHT1, - DAYPERIOD_MORNING2, - DAYPERIOD_AFTERNOON2, - DAYPERIOD_EVENING2, - DAYPERIOD_NIGHT2, - DAYPERIOD_AM, - DAYPERIOD_PM - }; - - static const DayPeriodRules *getInstance(const Locale &locale, UErrorCode &errorCode); - - UBool hasMidnight() const { return fHasMidnight; } - UBool hasNoon() const { return fHasNoon; } - DayPeriod getDayPeriodForHour(int32_t hour) const { return fDayPeriodForHour[hour]; } - - // Returns the center of dayPeriod. Half hours are indicated with a .5 . - double getMidPointForDayPeriod(DayPeriod dayPeriod, UErrorCode &errorCode) const; - -private: - DayPeriodRules(); - - // Translates "morning1" to DAYPERIOD_MORNING1, for example. - static DayPeriod getDayPeriodFromString(const char *type_str); - - static void U_CALLCONV load(UErrorCode &errorCode); - - // Sets period type for all hours in [startHour, limitHour). - void add(int32_t startHour, int32_t limitHour, DayPeriod period); - - // Returns true if for all i, DayPeriodForHour[i] has a type other than UNKNOWN. - // Values of HasNoon and HasMidnight do not affect the return value. - UBool allHoursAreSet(); - - // Returns the hour that starts dayPeriod. Returns 0 for MIDNIGHT and 12 for NOON. - int32_t getStartHourForDayPeriod(DayPeriod dayPeriod, UErrorCode &errorCode) const; - - // Returns the hour that ends dayPeriod, i.e. that starts the next period. - // E.g. if fDayPeriodForHour[13] thru [16] are AFTERNOON1, then this function returns 17 if - // queried with AFTERNOON1. - // Returns 0 for MIDNIGHT and 12 for NOON. - int32_t getEndHourForDayPeriod(DayPeriod dayPeriod, UErrorCode &errorCode) const; - - UBool fHasMidnight; - UBool fHasNoon; - DayPeriod fDayPeriodForHour[24]; -}; - -U_NAMESPACE_END - -#endif /* DAYPERIODRULES_H */ +// © 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. +******************************************************************************* +* dayperiodrules.h +* +* created on: 2016-01-20 +* created by: kazede +*/ + +#ifndef DAYPERIODRULES_H +#define DAYPERIODRULES_H + +#include "unicode/locid.h" +#include "unicode/unistr.h" +#include "unicode/uobject.h" +#include "unicode/utypes.h" +#include "resource.h" +#include "uhash.h" + + + +U_NAMESPACE_BEGIN + +struct DayPeriodRulesDataSink; + +class DayPeriodRules : public UMemory { + friend struct DayPeriodRulesDataSink; +public: + enum DayPeriod { + DAYPERIOD_UNKNOWN = -1, + DAYPERIOD_MIDNIGHT, + DAYPERIOD_NOON, + DAYPERIOD_MORNING1, + DAYPERIOD_AFTERNOON1, + DAYPERIOD_EVENING1, + DAYPERIOD_NIGHT1, + DAYPERIOD_MORNING2, + DAYPERIOD_AFTERNOON2, + DAYPERIOD_EVENING2, + DAYPERIOD_NIGHT2, + DAYPERIOD_AM, + DAYPERIOD_PM + }; + + static const DayPeriodRules *getInstance(const Locale &locale, UErrorCode &errorCode); + + UBool hasMidnight() const { return fHasMidnight; } + UBool hasNoon() const { return fHasNoon; } + DayPeriod getDayPeriodForHour(int32_t hour) const { return fDayPeriodForHour[hour]; } + + // Returns the center of dayPeriod. Half hours are indicated with a .5 . + double getMidPointForDayPeriod(DayPeriod dayPeriod, UErrorCode &errorCode) const; + +private: + DayPeriodRules(); + + // Translates "morning1" to DAYPERIOD_MORNING1, for example. + static DayPeriod getDayPeriodFromString(const char *type_str); + + static void U_CALLCONV load(UErrorCode &errorCode); + + // Sets period type for all hours in [startHour, limitHour). + void add(int32_t startHour, int32_t limitHour, DayPeriod period); + + // Returns true if for all i, DayPeriodForHour[i] has a type other than UNKNOWN. + // Values of HasNoon and HasMidnight do not affect the return value. + UBool allHoursAreSet(); + + // Returns the hour that starts dayPeriod. Returns 0 for MIDNIGHT and 12 for NOON. + int32_t getStartHourForDayPeriod(DayPeriod dayPeriod, UErrorCode &errorCode) const; + + // Returns the hour that ends dayPeriod, i.e. that starts the next period. + // E.g. if fDayPeriodForHour[13] thru [16] are AFTERNOON1, then this function returns 17 if + // queried with AFTERNOON1. + // Returns 0 for MIDNIGHT and 12 for NOON. + int32_t getEndHourForDayPeriod(DayPeriod dayPeriod, UErrorCode &errorCode) const; + + UBool fHasMidnight; + UBool fHasNoon; + DayPeriod fDayPeriodForHour[24]; +}; + +U_NAMESPACE_END + +#endif /* DAYPERIODRULES_H */ diff --git a/deps/icu-small/source/i18n/dcfmtsym.cpp b/deps/icu-small/source/i18n/dcfmtsym.cpp index 5d06c189fbef05..af9c5745198524 100644 --- a/deps/icu-small/source/i18n/dcfmtsym.cpp +++ b/deps/icu-small/source/i18n/dcfmtsym.cpp @@ -1,599 +1,603 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File DCFMTSYM.CPP -* -* Modification History: -* -* Date Name Description -* 02/19/97 aliu Converted from java. -* 03/18/97 clhuang Implemented with C++ APIs. -* 03/27/97 helena Updated to pass the simple test after code review. -* 08/26/97 aliu Added currency/intl currency symbol support. -* 07/20/98 stephen Slightly modified initialization of monetarySeparator -******************************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/dcfmtsym.h" -#include "unicode/ures.h" -#include "unicode/decimfmt.h" -#include "unicode/ucurr.h" -#include "unicode/choicfmt.h" -#include "unicode/unistr.h" -#include "unicode/numsys.h" -#include "unicode/unum.h" -#include "unicode/utf16.h" -#include "ucurrimp.h" -#include "cstring.h" -#include "locbased.h" -#include "uresimp.h" -#include "ureslocs.h" -#include "charstr.h" -#include "uassert.h" - -// ***************************************************************************** -// class DecimalFormatSymbols -// ***************************************************************************** - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormatSymbols) - -static const char gNumberElements[] = "NumberElements"; -static const char gCurrencySpacingTag[] = "currencySpacing"; -static const char gBeforeCurrencyTag[] = "beforeCurrency"; -static const char gAfterCurrencyTag[] = "afterCurrency"; -static const char gCurrencyMatchTag[] = "currencyMatch"; -static const char gCurrencySudMatchTag[] = "surroundingMatch"; -static const char gCurrencyInsertBtnTag[] = "insertBetween"; -static const char gLatn[] = "latn"; -static const char gSymbols[] = "symbols"; -static const char gNumberElementsLatnSymbols[] = "NumberElements/latn/symbols"; - -static const UChar INTL_CURRENCY_SYMBOL_STR[] = {0xa4, 0xa4, 0}; - -// List of field names to be loaded from the data files. -// These are parallel with the enum ENumberFormatSymbol in unicode/dcfmtsym.h. -static const char *gNumberElementKeys[DecimalFormatSymbols::kFormatSymbolCount] = { - "decimal", - "group", - 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 */ - "minusSign", - "plusSign", - NULL, /* currency symbol - Wait until we know the currency before loading from CLDR */ - NULL, /* intl currency symbol - Wait until we know the currency before loading from CLDR */ - "currencyDecimal", - "exponential", - "perMille", - NULL, /* Escape padding character - not in CLDR */ - "infinity", - "nan", - NULL, /* Significant digit symbol - not in CLDR */ - "currencyGroup", - NULL, /* one digit - get it from the numbering system */ - NULL, /* two digit - get it from the numbering system */ - NULL, /* three digit - get it from the numbering system */ - NULL, /* four digit - get it from the numbering system */ - NULL, /* five digit - get it from the numbering system */ - NULL, /* six digit - get it from the numbering system */ - NULL, /* seven digit - get it from the numbering system */ - NULL, /* eight digit - get it from the numbering system */ - NULL, /* nine digit - get it from the numbering system */ - "superscriptingExponent", /* Multiplication (x) symbol for exponents */ - "approximatelySign" /* Approximately sign symbol */ -}; - -// ------------------------------------- -// Initializes this with the decimal format symbols in the default locale. - -DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status) - : UObject(), locale(), currPattern(NULL) { - initialize(locale, status, true); -} - -// ------------------------------------- -// Initializes this with the decimal format symbols in the desired locale. - -DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status) - : UObject(), locale(loc), currPattern(NULL) { - initialize(locale, status); -} - -DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, const NumberingSystem& ns, UErrorCode& status) - : UObject(), locale(loc), currPattern(NULL) { - initialize(locale, status, false, &ns); -} - -DecimalFormatSymbols::DecimalFormatSymbols() - : UObject(), locale(Locale::getRoot()), currPattern(NULL) { - *validLocale = *actualLocale = 0; - initialize(); -} - -DecimalFormatSymbols* -DecimalFormatSymbols::createWithLastResortData(UErrorCode& status) { - if (U_FAILURE(status)) { return NULL; } - DecimalFormatSymbols* sym = new DecimalFormatSymbols(); - if (sym == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return sym; -} - -// ------------------------------------- - -DecimalFormatSymbols::~DecimalFormatSymbols() -{ -} - -// ------------------------------------- -// copy constructor - -DecimalFormatSymbols::DecimalFormatSymbols(const DecimalFormatSymbols &source) - : UObject(source) -{ - *this = source; -} - -// ------------------------------------- -// assignment operator - -DecimalFormatSymbols& -DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs) -{ - if (this != &rhs) { - for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) { - // fastCopyFrom is safe, see docs on fSymbols - fSymbols[(ENumberFormatSymbol)i].fastCopyFrom(rhs.fSymbols[(ENumberFormatSymbol)i]); - } - for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) { - currencySpcBeforeSym[i].fastCopyFrom(rhs.currencySpcBeforeSym[i]); - currencySpcAfterSym[i].fastCopyFrom(rhs.currencySpcAfterSym[i]); - } - locale = rhs.locale; - uprv_strcpy(validLocale, rhs.validLocale); - uprv_strcpy(actualLocale, rhs.actualLocale); - fIsCustomCurrencySymbol = rhs.fIsCustomCurrencySymbol; - fIsCustomIntlCurrencySymbol = rhs.fIsCustomIntlCurrencySymbol; - fCodePointZero = rhs.fCodePointZero; - currPattern = rhs.currPattern; - } - return *this; -} - -// ------------------------------------- - -bool -DecimalFormatSymbols::operator==(const DecimalFormatSymbols& that) const -{ - if (this == &that) { - return true; - } - if (fIsCustomCurrencySymbol != that.fIsCustomCurrencySymbol) { - return false; - } - if (fIsCustomIntlCurrencySymbol != that.fIsCustomIntlCurrencySymbol) { - return false; - } - for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) { - if(fSymbols[(ENumberFormatSymbol)i] != that.fSymbols[(ENumberFormatSymbol)i]) { - return false; - } - } - for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) { - if(currencySpcBeforeSym[i] != that.currencySpcBeforeSym[i]) { - return false; - } - if(currencySpcAfterSym[i] != that.currencySpcAfterSym[i]) { - return false; - } - } - // No need to check fCodePointZero since it is based on fSymbols - return locale == that.locale && - uprv_strcmp(validLocale, that.validLocale) == 0 && - uprv_strcmp(actualLocale, that.actualLocale) == 0; -} - -// ------------------------------------- - -namespace { - -/** - * Sink for enumerating all of the decimal format symbols (more specifically, anything - * under the "NumberElements.symbols" tree). - * - * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root): - * Only store a value if it is still missing, that is, it has not been overridden. - */ -struct DecFmtSymDataSink : public ResourceSink { - - // Destination for data, modified via setters. - DecimalFormatSymbols& dfs; - // Boolean array of whether or not we have seen a particular symbol yet. - // Can't simply check fSymbols because it is pre-populated with defaults. - UBool seenSymbol[DecimalFormatSymbols::kFormatSymbolCount]; - - // Constructor/Destructor - DecFmtSymDataSink(DecimalFormatSymbols& _dfs) : dfs(_dfs) { - uprv_memset(seenSymbol, false, sizeof(seenSymbol)); - } - virtual ~DecFmtSymDataSink(); - - virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, - UErrorCode &errorCode) override { - ResourceTable symbolsTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int32_t j = 0; symbolsTable.getKeyAndValue(j, key, value); ++j) { - for (int32_t i=0; i nsLocal; - if (ns == nullptr) { - // Use the numbering system according to the locale. - // Save it into a LocalPointer so it gets cleaned up. - nsLocal.adoptInstead(NumberingSystem::createInstance(loc, status)); - ns = nsLocal.getAlias(); - } - const char *nsName; - if (U_SUCCESS(status) && ns->getRadix() == 10 && !ns->isAlgorithmic()) { - nsName = ns->getName(); - UnicodeString digitString(ns->getDescription()); - int32_t digitIndex = 0; - UChar32 digit = digitString.char32At(0); - fSymbols[kZeroDigitSymbol].setTo(digit); - for (int32_t i = kOneDigitSymbol; i <= kNineDigitSymbol; ++i) { - digitIndex += U16_LENGTH(digit); - digit = digitString.char32At(digitIndex); - fSymbols[i].setTo(digit); - } - } else { - nsName = gLatn; - } - - // Open resource bundles - const char* locStr = loc.getName(); - LocalUResourceBundlePointer resource(ures_open(NULL, locStr, &status)); - LocalUResourceBundlePointer numberElementsRes( - ures_getByKeyWithFallback(resource.getAlias(), gNumberElements, NULL, &status)); - - if (U_FAILURE(status)) { - if ( useLastResortData ) { - status = U_USING_DEFAULT_WARNING; - initialize(); - } - return; - } - - // Set locale IDs - // TODO: Is there a way to do this without depending on the resource bundle instance? - U_LOCALE_BASED(locBased, *this); - locBased.setLocaleIDs( - ures_getLocaleByType( - numberElementsRes.getAlias(), - ULOC_VALID_LOCALE, &status), - ures_getLocaleByType( - numberElementsRes.getAlias(), - ULOC_ACTUAL_LOCALE, &status)); - - // Now load the rest of the data from the data sink. - // Start with loading this nsName if it is not Latin. - DecFmtSymDataSink sink(*this); - if (uprv_strcmp(nsName, gLatn) != 0) { - CharString path; - path.append(gNumberElements, status) - .append('/', status) - .append(nsName, status) - .append('/', status) - .append(gSymbols, status); - ures_getAllItemsWithFallback(resource.getAlias(), path.data(), sink, status); - - // If no symbols exist for the given nsName and resource bundle, silently ignore - // and fall back to Latin. - if (status == U_MISSING_RESOURCE_ERROR) { - status = U_ZERO_ERROR; - } else if (U_FAILURE(status)) { - return; - } - } - - // Continue with Latin if necessary. - if (!sink.seenAll()) { - ures_getAllItemsWithFallback(resource.getAlias(), gNumberElementsLatnSymbols, sink, status); - if (U_FAILURE(status)) { return; } - } - - // Let the monetary number separators equal the default number separators if necessary. - sink.resolveMissingMonetarySeparators(fSymbols); - - // Resolve codePointZero - UChar32 tempCodePointZero = -1; - for (int32_t i=0; i<=9; i++) { - const UnicodeString& stringDigit = getConstDigitSymbol(i); - if (stringDigit.countChar32() != 1) { - tempCodePointZero = -1; - break; - } - UChar32 cp = stringDigit.char32At(0); - if (i == 0) { - tempCodePointZero = cp; - } else if (cp != tempCodePointZero + i) { - tempCodePointZero = -1; - break; - } - } - fCodePointZero = tempCodePointZero; - - // Get the default currency from the currency API. - UErrorCode internalStatus = U_ZERO_ERROR; // don't propagate failures out - UChar curriso[4]; - UnicodeString tempStr; - int32_t currisoLength = ucurr_forLocale(locStr, curriso, UPRV_LENGTHOF(curriso), &internalStatus); - if (U_SUCCESS(internalStatus) && currisoLength == 3) { - setCurrency(curriso, status); - } else { - setCurrency(nullptr, status); - } - - // Currency Spacing. - LocalUResourceBundlePointer currencyResource(ures_open(U_ICUDATA_CURR, locStr, &status)); - CurrencySpacingSink currencySink(*this); - ures_getAllItemsWithFallback(currencyResource.getAlias(), gCurrencySpacingTag, currencySink, status); - currencySink.resolveMissing(); - if (U_FAILURE(status)) { return; } -} - -void -DecimalFormatSymbols::initialize() { - /* - * These strings used to be in static arrays, but the HP/UX aCC compiler - * cannot initialize a static array with class constructors. - * markus 2000may25 - */ - fSymbols[kDecimalSeparatorSymbol] = (UChar)0x2e; // '.' decimal separator - fSymbols[kGroupingSeparatorSymbol].remove(); // group (thousands) separator - fSymbols[kPatternSeparatorSymbol] = (UChar)0x3b; // ';' pattern separator - fSymbols[kPercentSymbol] = (UChar)0x25; // '%' percent sign - fSymbols[kZeroDigitSymbol] = (UChar)0x30; // '0' native 0 digit - fSymbols[kOneDigitSymbol] = (UChar)0x31; // '1' native 1 digit - fSymbols[kTwoDigitSymbol] = (UChar)0x32; // '2' native 2 digit - fSymbols[kThreeDigitSymbol] = (UChar)0x33; // '3' native 3 digit - fSymbols[kFourDigitSymbol] = (UChar)0x34; // '4' native 4 digit - fSymbols[kFiveDigitSymbol] = (UChar)0x35; // '5' native 5 digit - fSymbols[kSixDigitSymbol] = (UChar)0x36; // '6' native 6 digit - fSymbols[kSevenDigitSymbol] = (UChar)0x37; // '7' native 7 digit - fSymbols[kEightDigitSymbol] = (UChar)0x38; // '8' native 8 digit - fSymbols[kNineDigitSymbol] = (UChar)0x39; // '9' native 9 digit - fSymbols[kDigitSymbol] = (UChar)0x23; // '#' pattern digit - fSymbols[kPlusSignSymbol] = (UChar)0x002b; // '+' plus sign - fSymbols[kMinusSignSymbol] = (UChar)0x2d; // '-' minus sign - fSymbols[kCurrencySymbol] = (UChar)0xa4; // 'OX' currency symbol - fSymbols[kIntlCurrencySymbol].setTo(true, INTL_CURRENCY_SYMBOL_STR, 2); - fSymbols[kMonetarySeparatorSymbol] = (UChar)0x2e; // '.' monetary decimal separator - fSymbols[kExponentialSymbol] = (UChar)0x45; // 'E' exponential - fSymbols[kPerMillSymbol] = (UChar)0x2030; // '%o' per mill - fSymbols[kPadEscapeSymbol] = (UChar)0x2a; // '*' pad escape symbol - fSymbols[kInfinitySymbol] = (UChar)0x221e; // 'oo' infinite - fSymbols[kNaNSymbol] = (UChar)0xfffd; // SUB NaN - fSymbols[kSignificantDigitSymbol] = (UChar)0x0040; // '@' significant digit - fSymbols[kMonetaryGroupingSeparatorSymbol].remove(); // - fSymbols[kExponentMultiplicationSymbol] = (UChar)0xd7; // 'x' multiplication symbol for exponents - fSymbols[kApproximatelySignSymbol] = u'~'; // '~' approximately sign - fIsCustomCurrencySymbol = false; - fIsCustomIntlCurrencySymbol = false; - fCodePointZero = 0x30; - U_ASSERT(fCodePointZero == fSymbols[kZeroDigitSymbol].char32At(0)); - currPattern = nullptr; - -} - -void DecimalFormatSymbols::setCurrency(const UChar* currency, UErrorCode& status) { - // TODO: If this method is made public: - // - Adopt ICU4J behavior of not allowing currency to be null. - // - Also verify that the length of currency is 3. - if (!currency) { - return; - } - - UnicodeString tempStr; - uprv_getStaticCurrencyName(currency, locale.getName(), tempStr, status); - if (U_SUCCESS(status)) { - fSymbols[kIntlCurrencySymbol].setTo(currency, 3); - fSymbols[kCurrencySymbol] = tempStr; - } - - char cc[4]={0}; - u_UCharsToChars(currency, cc, 3); - - /* An explicit currency was requested */ - // TODO(ICU-13297): Move this data loading logic into a centralized place - UErrorCode localStatus = U_ZERO_ERROR; - LocalUResourceBundlePointer rbTop(ures_open(U_ICUDATA_CURR, locale.getName(), &localStatus)); - LocalUResourceBundlePointer rb( - ures_getByKeyWithFallback(rbTop.getAlias(), "Currencies", NULL, &localStatus)); - ures_getByKeyWithFallback(rb.getAlias(), cc, rb.getAlias(), &localStatus); - if(U_SUCCESS(localStatus) && ures_getSize(rb.getAlias())>2) { // the length is 3 if more data is present - ures_getByIndex(rb.getAlias(), 2, rb.getAlias(), &localStatus); - int32_t currPatternLen = 0; - currPattern = - ures_getStringByIndex(rb.getAlias(), (int32_t)0, &currPatternLen, &localStatus); - UnicodeString decimalSep = - ures_getUnicodeStringByIndex(rb.getAlias(), (int32_t)1, &localStatus); - UnicodeString groupingSep = - ures_getUnicodeStringByIndex(rb.getAlias(), (int32_t)2, &localStatus); - if(U_SUCCESS(localStatus)){ - fSymbols[kMonetaryGroupingSeparatorSymbol] = groupingSep; - fSymbols[kMonetarySeparatorSymbol] = decimalSep; - //pattern.setTo(true, currPattern, currPatternLen); - } - } - /* else An explicit currency was requested and is unknown or locale data is malformed. */ - /* ucurr_* API will get the correct value later on. */ -} - -Locale -DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const { - U_LOCALE_BASED(locBased, *this); - return locBased.getLocale(type, status); -} - -const UnicodeString& -DecimalFormatSymbols::getPatternForCurrencySpacing(UCurrencySpacing type, - UBool beforeCurrency, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return fNoSymbol; // always empty. - } - if (beforeCurrency) { - return currencySpcBeforeSym[(int32_t)type]; - } else { - return currencySpcAfterSym[(int32_t)type]; - } -} - -void -DecimalFormatSymbols::setPatternForCurrencySpacing(UCurrencySpacing type, - UBool beforeCurrency, - const UnicodeString& pattern) { - if (beforeCurrency) { - currencySpcBeforeSym[(int32_t)type] = pattern; - } else { - currencySpcAfterSym[(int32_t)type] = pattern; - } -} -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1997-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DCFMTSYM.CPP +* +* Modification History: +* +* Date Name Description +* 02/19/97 aliu Converted from java. +* 03/18/97 clhuang Implemented with C++ APIs. +* 03/27/97 helena Updated to pass the simple test after code review. +* 08/26/97 aliu Added currency/intl currency symbol support. +* 07/20/98 stephen Slightly modified initialization of monetarySeparator +******************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/dcfmtsym.h" +#include "unicode/ures.h" +#include "unicode/decimfmt.h" +#include "unicode/ucurr.h" +#include "unicode/choicfmt.h" +#include "unicode/unistr.h" +#include "unicode/numsys.h" +#include "unicode/unum.h" +#include "unicode/utf16.h" +#include "ucurrimp.h" +#include "cstring.h" +#include "locbased.h" +#include "uresimp.h" +#include "ureslocs.h" +#include "charstr.h" +#include "uassert.h" + +// ***************************************************************************** +// class DecimalFormatSymbols +// ***************************************************************************** + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormatSymbols) + +static const char gNumberElements[] = "NumberElements"; +static const char gCurrencySpacingTag[] = "currencySpacing"; +static const char gBeforeCurrencyTag[] = "beforeCurrency"; +static const char gAfterCurrencyTag[] = "afterCurrency"; +static const char gCurrencyMatchTag[] = "currencyMatch"; +static const char gCurrencySudMatchTag[] = "surroundingMatch"; +static const char gCurrencyInsertBtnTag[] = "insertBetween"; +static const char gLatn[] = "latn"; +static const char gSymbols[] = "symbols"; +static const char gNumberElementsLatnSymbols[] = "NumberElements/latn/symbols"; + +static const char16_t INTL_CURRENCY_SYMBOL_STR[] = {0xa4, 0xa4, 0}; + +// List of field names to be loaded from the data files. +// These are parallel with the enum ENumberFormatSymbol in unicode/dcfmtsym.h. +static const char *gNumberElementKeys[DecimalFormatSymbols::kFormatSymbolCount] = { + "decimal", + "group", + nullptr, /* #11897: the symbol is NOT the pattern separator symbol */ + "percentSign", + nullptr, /* Native zero digit is deprecated from CLDR - get it from the numbering system */ + nullptr, /* Pattern digit character is deprecated from CLDR - use # by default always */ + "minusSign", + "plusSign", + nullptr, /* currency symbol - Wait until we know the currency before loading from CLDR */ + nullptr, /* intl currency symbol - Wait until we know the currency before loading from CLDR */ + "currencyDecimal", + "exponential", + "perMille", + nullptr, /* Escape padding character - not in CLDR */ + "infinity", + "nan", + nullptr, /* Significant digit symbol - not in CLDR */ + "currencyGroup", + nullptr, /* one digit - get it from the numbering system */ + nullptr, /* two digit - get it from the numbering system */ + nullptr, /* three digit - get it from the numbering system */ + nullptr, /* four digit - get it from the numbering system */ + nullptr, /* five digit - get it from the numbering system */ + nullptr, /* six digit - get it from the numbering system */ + nullptr, /* seven digit - get it from the numbering system */ + nullptr, /* eight digit - get it from the numbering system */ + nullptr, /* nine digit - get it from the numbering system */ + "superscriptingExponent", /* Multiplication (x) symbol for exponents */ + "approximatelySign" /* Approximately sign symbol */ +}; + +// ------------------------------------- +// Initializes this with the decimal format symbols in the default locale. + +DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status) + : UObject(), locale() { + initialize(locale, status, true); +} + +// ------------------------------------- +// Initializes this with the decimal format symbols in the desired locale. + +DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status) + : UObject(), locale(loc) { + initialize(locale, status); +} + +DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, const NumberingSystem& ns, UErrorCode& status) + : UObject(), locale(loc) { + initialize(locale, status, false, &ns); +} + +DecimalFormatSymbols::DecimalFormatSymbols() + : UObject(), locale(Locale::getRoot()) { + *validLocale = *actualLocale = 0; + initialize(); +} + +DecimalFormatSymbols* +DecimalFormatSymbols::createWithLastResortData(UErrorCode& status) { + if (U_FAILURE(status)) { return nullptr; } + DecimalFormatSymbols* sym = new DecimalFormatSymbols(); + if (sym == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return sym; +} + +// ------------------------------------- + +DecimalFormatSymbols::~DecimalFormatSymbols() +{ +} + +// ------------------------------------- +// copy constructor + +DecimalFormatSymbols::DecimalFormatSymbols(const DecimalFormatSymbols &source) + : UObject(source) +{ + *this = source; +} + +// ------------------------------------- +// assignment operator + +DecimalFormatSymbols& +DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs) +{ + if (this != &rhs) { + for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) { + // fastCopyFrom is safe, see docs on fSymbols + fSymbols[(ENumberFormatSymbol)i].fastCopyFrom(rhs.fSymbols[(ENumberFormatSymbol)i]); + } + for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) { + currencySpcBeforeSym[i].fastCopyFrom(rhs.currencySpcBeforeSym[i]); + currencySpcAfterSym[i].fastCopyFrom(rhs.currencySpcAfterSym[i]); + } + locale = rhs.locale; + uprv_strcpy(validLocale, rhs.validLocale); + uprv_strcpy(actualLocale, rhs.actualLocale); + fIsCustomCurrencySymbol = rhs.fIsCustomCurrencySymbol; + fIsCustomIntlCurrencySymbol = rhs.fIsCustomIntlCurrencySymbol; + fCodePointZero = rhs.fCodePointZero; + currPattern = rhs.currPattern; + uprv_strcpy(nsName, rhs.nsName); + } + return *this; +} + +// ------------------------------------- + +bool +DecimalFormatSymbols::operator==(const DecimalFormatSymbols& that) const +{ + if (this == &that) { + return true; + } + if (fIsCustomCurrencySymbol != that.fIsCustomCurrencySymbol) { + return false; + } + if (fIsCustomIntlCurrencySymbol != that.fIsCustomIntlCurrencySymbol) { + return false; + } + for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) { + if(fSymbols[(ENumberFormatSymbol)i] != that.fSymbols[(ENumberFormatSymbol)i]) { + return false; + } + } + for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) { + if(currencySpcBeforeSym[i] != that.currencySpcBeforeSym[i]) { + return false; + } + if(currencySpcAfterSym[i] != that.currencySpcAfterSym[i]) { + return false; + } + } + // No need to check fCodePointZero since it is based on fSymbols + return locale == that.locale && + uprv_strcmp(validLocale, that.validLocale) == 0 && + uprv_strcmp(actualLocale, that.actualLocale) == 0; +} + +// ------------------------------------- + +namespace { + +/** + * Sink for enumerating all of the decimal format symbols (more specifically, anything + * under the "NumberElements.symbols" tree). + * + * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root): + * Only store a value if it is still missing, that is, it has not been overridden. + */ +struct DecFmtSymDataSink : public ResourceSink { + + // Destination for data, modified via setters. + DecimalFormatSymbols& dfs; + // Boolean array of whether or not we have seen a particular symbol yet. + // Can't simply check fSymbols because it is pre-populated with defaults. + UBool seenSymbol[DecimalFormatSymbols::kFormatSymbolCount]; + + // Constructor/Destructor + DecFmtSymDataSink(DecimalFormatSymbols& _dfs) : dfs(_dfs) { + uprv_memset(seenSymbol, false, sizeof(seenSymbol)); + } + virtual ~DecFmtSymDataSink(); + + virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, + UErrorCode &errorCode) override { + ResourceTable symbolsTable = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + for (int32_t j = 0; symbolsTable.getKeyAndValue(j, key, value); ++j) { + for (int32_t i=0; i nsLocal; + if (ns == nullptr) { + // Use the numbering system according to the locale. + // Save it into a LocalPointer so it gets cleaned up. + nsLocal.adoptInstead(NumberingSystem::createInstance(loc, status)); + ns = nsLocal.getAlias(); + } + const char *nsName; + if (U_SUCCESS(status) && ns->getRadix() == 10 && !ns->isAlgorithmic()) { + nsName = ns->getName(); + UnicodeString digitString(ns->getDescription()); + int32_t digitIndex = 0; + UChar32 digit = digitString.char32At(0); + fSymbols[kZeroDigitSymbol].setTo(digit); + for (int32_t i = kOneDigitSymbol; i <= kNineDigitSymbol; ++i) { + digitIndex += U16_LENGTH(digit); + digit = digitString.char32At(digitIndex); + fSymbols[i].setTo(digit); + } + } else { + nsName = gLatn; + } + uprv_strcpy(this->nsName, nsName); + + // Open resource bundles + const char* locStr = loc.getName(); + LocalUResourceBundlePointer resource(ures_open(nullptr, locStr, &status)); + LocalUResourceBundlePointer numberElementsRes( + ures_getByKeyWithFallback(resource.getAlias(), gNumberElements, nullptr, &status)); + + if (U_FAILURE(status)) { + if ( useLastResortData ) { + status = U_USING_DEFAULT_WARNING; + initialize(); + } + return; + } + + // Set locale IDs + // TODO: Is there a way to do this without depending on the resource bundle instance? + U_LOCALE_BASED(locBased, *this); + locBased.setLocaleIDs( + ures_getLocaleByType( + numberElementsRes.getAlias(), + ULOC_VALID_LOCALE, &status), + ures_getLocaleByType( + numberElementsRes.getAlias(), + ULOC_ACTUAL_LOCALE, &status)); + + // Now load the rest of the data from the data sink. + // Start with loading this nsName if it is not Latin. + DecFmtSymDataSink sink(*this); + if (uprv_strcmp(nsName, gLatn) != 0) { + CharString path; + path.append(gNumberElements, status) + .append('/', status) + .append(nsName, status) + .append('/', status) + .append(gSymbols, status); + ures_getAllItemsWithFallback(resource.getAlias(), path.data(), sink, status); + + // If no symbols exist for the given nsName and resource bundle, silently ignore + // and fall back to Latin. + if (status == U_MISSING_RESOURCE_ERROR) { + status = U_ZERO_ERROR; + } else if (U_FAILURE(status)) { + return; + } + } + + // Continue with Latin if necessary. + if (!sink.seenAll()) { + ures_getAllItemsWithFallback(resource.getAlias(), gNumberElementsLatnSymbols, sink, status); + if (U_FAILURE(status)) { return; } + } + + // Let the monetary number separators equal the default number separators if necessary. + sink.resolveMissingMonetarySeparators(fSymbols); + + // Resolve codePointZero + UChar32 tempCodePointZero = -1; + for (int32_t i=0; i<=9; i++) { + const UnicodeString& stringDigit = getConstDigitSymbol(i); + if (stringDigit.countChar32() != 1) { + tempCodePointZero = -1; + break; + } + UChar32 cp = stringDigit.char32At(0); + if (i == 0) { + tempCodePointZero = cp; + } else if (cp != tempCodePointZero + i) { + tempCodePointZero = -1; + break; + } + } + fCodePointZero = tempCodePointZero; + + // Get the default currency from the currency API. + UErrorCode internalStatus = U_ZERO_ERROR; // don't propagate failures out + char16_t curriso[4]; + UnicodeString tempStr; + int32_t currisoLength = ucurr_forLocale(locStr, curriso, UPRV_LENGTHOF(curriso), &internalStatus); + if (U_SUCCESS(internalStatus) && currisoLength == 3) { + setCurrency(curriso, status); + } else { + setCurrency(nullptr, status); + } + + // Currency Spacing. + LocalUResourceBundlePointer currencyResource(ures_open(U_ICUDATA_CURR, locStr, &status)); + CurrencySpacingSink currencySink(*this); + ures_getAllItemsWithFallback(currencyResource.getAlias(), gCurrencySpacingTag, currencySink, status); + currencySink.resolveMissing(); + if (U_FAILURE(status)) { return; } +} + +void +DecimalFormatSymbols::initialize() { + /* + * These strings used to be in static arrays, but the HP/UX aCC compiler + * cannot initialize a static array with class constructors. + * markus 2000may25 + */ + fSymbols[kDecimalSeparatorSymbol] = (char16_t)0x2e; // '.' decimal separator + fSymbols[kGroupingSeparatorSymbol].remove(); // group (thousands) separator + fSymbols[kPatternSeparatorSymbol] = (char16_t)0x3b; // ';' pattern separator + fSymbols[kPercentSymbol] = (char16_t)0x25; // '%' percent sign + fSymbols[kZeroDigitSymbol] = (char16_t)0x30; // '0' native 0 digit + fSymbols[kOneDigitSymbol] = (char16_t)0x31; // '1' native 1 digit + fSymbols[kTwoDigitSymbol] = (char16_t)0x32; // '2' native 2 digit + fSymbols[kThreeDigitSymbol] = (char16_t)0x33; // '3' native 3 digit + fSymbols[kFourDigitSymbol] = (char16_t)0x34; // '4' native 4 digit + fSymbols[kFiveDigitSymbol] = (char16_t)0x35; // '5' native 5 digit + fSymbols[kSixDigitSymbol] = (char16_t)0x36; // '6' native 6 digit + fSymbols[kSevenDigitSymbol] = (char16_t)0x37; // '7' native 7 digit + fSymbols[kEightDigitSymbol] = (char16_t)0x38; // '8' native 8 digit + fSymbols[kNineDigitSymbol] = (char16_t)0x39; // '9' native 9 digit + fSymbols[kDigitSymbol] = (char16_t)0x23; // '#' pattern digit + fSymbols[kPlusSignSymbol] = (char16_t)0x002b; // '+' plus sign + fSymbols[kMinusSignSymbol] = (char16_t)0x2d; // '-' minus sign + fSymbols[kCurrencySymbol] = (char16_t)0xa4; // 'OX' currency symbol + fSymbols[kIntlCurrencySymbol].setTo(true, INTL_CURRENCY_SYMBOL_STR, 2); + fSymbols[kMonetarySeparatorSymbol] = (char16_t)0x2e; // '.' monetary decimal separator + fSymbols[kExponentialSymbol] = (char16_t)0x45; // 'E' exponential + fSymbols[kPerMillSymbol] = (char16_t)0x2030; // '%o' per mill + fSymbols[kPadEscapeSymbol] = (char16_t)0x2a; // '*' pad escape symbol + fSymbols[kInfinitySymbol] = (char16_t)0x221e; // 'oo' infinite + fSymbols[kNaNSymbol] = (char16_t)0xfffd; // SUB NaN + fSymbols[kSignificantDigitSymbol] = (char16_t)0x0040; // '@' significant digit + fSymbols[kMonetaryGroupingSeparatorSymbol].remove(); // + fSymbols[kExponentMultiplicationSymbol] = (char16_t)0xd7; // 'x' multiplication symbol for exponents + fSymbols[kApproximatelySignSymbol] = u'~'; // '~' approximately sign + fIsCustomCurrencySymbol = false; + fIsCustomIntlCurrencySymbol = false; + fCodePointZero = 0x30; + U_ASSERT(fCodePointZero == fSymbols[kZeroDigitSymbol].char32At(0)); + currPattern = nullptr; + nsName[0] = 0; +} + +void DecimalFormatSymbols::setCurrency(const char16_t* currency, UErrorCode& status) { + // TODO: If this method is made public: + // - Adopt ICU4J behavior of not allowing currency to be null. + // - Also verify that the length of currency is 3. + if (!currency) { + return; + } + + UnicodeString tempStr; + uprv_getStaticCurrencyName(currency, locale.getName(), tempStr, status); + if (U_SUCCESS(status)) { + fSymbols[kIntlCurrencySymbol].setTo(currency, 3); + fSymbols[kCurrencySymbol] = tempStr; + } + + char cc[4]={0}; + u_UCharsToChars(currency, cc, 3); + + /* An explicit currency was requested */ + // TODO(ICU-13297): Move this data loading logic into a centralized place + UErrorCode localStatus = U_ZERO_ERROR; + LocalUResourceBundlePointer rbTop(ures_open(U_ICUDATA_CURR, locale.getName(), &localStatus)); + LocalUResourceBundlePointer rb( + ures_getByKeyWithFallback(rbTop.getAlias(), "Currencies", nullptr, &localStatus)); + ures_getByKeyWithFallback(rb.getAlias(), cc, rb.getAlias(), &localStatus); + if(U_SUCCESS(localStatus) && ures_getSize(rb.getAlias())>2) { // the length is 3 if more data is present + ures_getByIndex(rb.getAlias(), 2, rb.getAlias(), &localStatus); + int32_t currPatternLen = 0; + currPattern = + ures_getStringByIndex(rb.getAlias(), (int32_t)0, &currPatternLen, &localStatus); + UnicodeString decimalSep = + ures_getUnicodeStringByIndex(rb.getAlias(), (int32_t)1, &localStatus); + UnicodeString groupingSep = + ures_getUnicodeStringByIndex(rb.getAlias(), (int32_t)2, &localStatus); + if(U_SUCCESS(localStatus)){ + fSymbols[kMonetaryGroupingSeparatorSymbol] = groupingSep; + fSymbols[kMonetarySeparatorSymbol] = decimalSep; + //pattern.setTo(true, currPattern, currPatternLen); + } + } + /* else An explicit currency was requested and is unknown or locale data is malformed. */ + /* ucurr_* API will get the correct value later on. */ +} + +Locale +DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const { + U_LOCALE_BASED(locBased, *this); + return locBased.getLocale(type, status); +} + +const UnicodeString& +DecimalFormatSymbols::getPatternForCurrencySpacing(UCurrencySpacing type, + UBool beforeCurrency, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return fNoSymbol; // always empty. + } + if (beforeCurrency) { + return currencySpcBeforeSym[(int32_t)type]; + } else { + return currencySpcAfterSym[(int32_t)type]; + } +} + +void +DecimalFormatSymbols::setPatternForCurrencySpacing(UCurrencySpacing type, + UBool beforeCurrency, + const UnicodeString& pattern) { + if (beforeCurrency) { + currencySpcBeforeSym[(int32_t)type] = pattern; + } else { + currencySpcAfterSym[(int32_t)type] = pattern; + } +} +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/decContext.cpp b/deps/icu-small/source/i18n/decContext.cpp index 421d65b43f99d2..5cf1f925a15b96 100644 --- a/deps/icu-small/source/i18n/decContext.cpp +++ b/deps/icu-small/source/i18n/decContext.cpp @@ -1,432 +1,432 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* ------------------------------------------------------------------ */ -/* Decimal Context module */ -/* ------------------------------------------------------------------ */ -/* Copyright (c) IBM Corporation, 2000-2012. All rights reserved. */ -/* */ -/* This software is made available under the terms of the */ -/* ICU License -- ICU 1.8.1 and later. */ -/* */ -/* The description and User's Guide ("The decNumber C Library") for */ -/* this software is called decNumber.pdf. This document is */ -/* available, together with arithmetic and format specifications, */ -/* testcases, and Web links, on the General Decimal Arithmetic page. */ -/* */ -/* Please send comments, suggestions, and corrections to the author: */ -/* mfc@uk.ibm.com */ -/* Mike Cowlishaw, IBM Fellow */ -/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ -/* ------------------------------------------------------------------ */ -/* This module comprises the routines for handling arithmetic */ -/* context structures. */ -/* ------------------------------------------------------------------ */ - -#include /* for strcmp */ -#include /* for printf if DECCHECK */ -#include "decContext.h" /* context and base types */ -#include "decNumberLocal.h" /* decNumber local types, etc. */ - -#if 0 /* ICU: No need to test endianness at runtime. */ -/* compile-time endian tester [assumes sizeof(Int)>1] */ -static const Int mfcone=1; /* constant 1 */ -static const Flag *mfctop=(Flag *)&mfcone; /* -> top byte */ -#define LITEND *mfctop /* named flag; 1=little-endian */ -#endif - -/* ------------------------------------------------------------------ */ -/* decContextClearStatus -- clear bits in current status */ -/* */ -/* context is the context structure to be queried */ -/* mask indicates the bits to be cleared (the status bit that */ -/* corresponds to each 1 bit in the mask is cleared) */ -/* returns context */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -U_CAPI decContext * U_EXPORT2 uprv_decContextClearStatus(decContext *context, uInt mask) { - context->status&=~mask; - return context; - } /* decContextClearStatus */ - -/* ------------------------------------------------------------------ */ -/* decContextDefault -- initialize a context structure */ -/* */ -/* context is the structure to be initialized */ -/* kind selects the required set of default values, one of: */ -/* DEC_INIT_BASE -- select ANSI X3-274 defaults */ -/* DEC_INIT_DECIMAL32 -- select IEEE 754 defaults, 32-bit */ -/* DEC_INIT_DECIMAL64 -- select IEEE 754 defaults, 64-bit */ -/* DEC_INIT_DECIMAL128 -- select IEEE 754 defaults, 128-bit */ -/* For any other value a valid context is returned, but with */ -/* Invalid_operation set in the status field. */ -/* returns a context structure with the appropriate initial values. */ -/* ------------------------------------------------------------------ */ -U_CAPI decContext * U_EXPORT2 uprv_decContextDefault(decContext *context, Int kind) { - /* set defaults... */ - context->digits=9; /* 9 digits */ - context->emax=DEC_MAX_EMAX; /* 9-digit exponents */ - context->emin=DEC_MIN_EMIN; /* .. balanced */ - context->round=DEC_ROUND_HALF_UP; /* 0.5 rises */ - context->traps=DEC_Errors; /* all but informational */ - context->status=0; /* cleared */ - context->clamp=0; /* no clamping */ - #if DECSUBSET - context->extended=0; /* cleared */ - #endif - switch (kind) { - case DEC_INIT_BASE: - /* [use defaults] */ - break; - case DEC_INIT_DECIMAL32: - context->digits=7; /* digits */ - context->emax=96; /* Emax */ - context->emin=-95; /* Emin */ - context->round=DEC_ROUND_HALF_EVEN; /* 0.5 to nearest even */ - context->traps=0; /* no traps set */ - context->clamp=1; /* clamp exponents */ - #if DECSUBSET - context->extended=1; /* set */ - #endif - break; - case DEC_INIT_DECIMAL64: - context->digits=16; /* digits */ - context->emax=384; /* Emax */ - context->emin=-383; /* Emin */ - context->round=DEC_ROUND_HALF_EVEN; /* 0.5 to nearest even */ - context->traps=0; /* no traps set */ - context->clamp=1; /* clamp exponents */ - #if DECSUBSET - context->extended=1; /* set */ - #endif - break; - case DEC_INIT_DECIMAL128: - context->digits=34; /* digits */ - context->emax=6144; /* Emax */ - context->emin=-6143; /* Emin */ - context->round=DEC_ROUND_HALF_EVEN; /* 0.5 to nearest even */ - context->traps=0; /* no traps set */ - context->clamp=1; /* clamp exponents */ - #if DECSUBSET - context->extended=1; /* set */ - #endif - break; - - default: /* invalid Kind */ - /* use defaults, and .. */ - uprv_decContextSetStatus(context, DEC_Invalid_operation); /* trap */ - } - - return context;} /* decContextDefault */ - -/* ------------------------------------------------------------------ */ -/* decContextGetRounding -- return current rounding mode */ -/* */ -/* context is the context structure to be queried */ -/* returns the rounding mode */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -U_CAPI enum rounding U_EXPORT2 uprv_decContextGetRounding(decContext *context) { - return context->round; - } /* decContextGetRounding */ - -/* ------------------------------------------------------------------ */ -/* decContextGetStatus -- return current status */ -/* */ -/* context is the context structure to be queried */ -/* returns status */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -U_CAPI uInt U_EXPORT2 uprv_decContextGetStatus(decContext *context) { - return context->status; - } /* decContextGetStatus */ - -/* ------------------------------------------------------------------ */ -/* decContextRestoreStatus -- restore bits in current status */ -/* */ -/* context is the context structure to be updated */ -/* newstatus is the source for the bits to be restored */ -/* mask indicates the bits to be restored (the status bit that */ -/* corresponds to each 1 bit in the mask is set to the value of */ -/* the corresponding bit in newstatus) */ -/* returns context */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -U_CAPI decContext * U_EXPORT2 uprv_decContextRestoreStatus(decContext *context, - uInt newstatus, uInt mask) { - context->status&=~mask; /* clear the selected bits */ - context->status|=(mask&newstatus); /* or in the new bits */ - return context; - } /* decContextRestoreStatus */ - -/* ------------------------------------------------------------------ */ -/* decContextSaveStatus -- save bits in current status */ -/* */ -/* context is the context structure to be queried */ -/* mask indicates the bits to be saved (the status bits that */ -/* correspond to each 1 bit in the mask are saved) */ -/* returns the AND of the mask and the current status */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -U_CAPI uInt U_EXPORT2 uprv_decContextSaveStatus(decContext *context, uInt mask) { - return context->status&mask; - } /* decContextSaveStatus */ - -/* ------------------------------------------------------------------ */ -/* decContextSetRounding -- set current rounding mode */ -/* */ -/* context is the context structure to be updated */ -/* newround is the value which will replace the current mode */ -/* returns context */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -U_CAPI decContext * U_EXPORT2 uprv_decContextSetRounding(decContext *context, - enum rounding newround) { - context->round=newround; - return context; - } /* decContextSetRounding */ - -/* ------------------------------------------------------------------ */ -/* decContextSetStatus -- set status and raise trap if appropriate */ -/* */ -/* context is the context structure to be updated */ -/* status is the DEC_ exception code */ -/* returns the context structure */ -/* */ -/* Control may never return from this routine, if there is a signal */ -/* handler and it takes a long jump. */ -/* ------------------------------------------------------------------ */ -U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatus(decContext *context, uInt status) { - context->status|=status; -#if 0 /* ICU: Do not raise signals. */ - if (status & context->traps) raise(SIGFPE); -#endif - return context;} /* decContextSetStatus */ - -/* ------------------------------------------------------------------ */ -/* decContextSetStatusFromString -- set status from a string + trap */ -/* */ -/* context is the context structure to be updated */ -/* string is a string exactly equal to one that might be returned */ -/* by decContextStatusToString */ -/* */ -/* The status bit corresponding to the string is set, and a trap */ -/* is raised if appropriate. */ -/* */ -/* returns the context structure, unless the string is equal to */ -/* DEC_Condition_MU or is not recognized. In these cases NULL is */ -/* returned. */ -/* ------------------------------------------------------------------ */ -U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatusFromString(decContext *context, - const char *string) { - if (strcmp(string, DEC_Condition_CS)==0) - return uprv_decContextSetStatus(context, DEC_Conversion_syntax); - if (strcmp(string, DEC_Condition_DZ)==0) - return uprv_decContextSetStatus(context, DEC_Division_by_zero); - if (strcmp(string, DEC_Condition_DI)==0) - return uprv_decContextSetStatus(context, DEC_Division_impossible); - if (strcmp(string, DEC_Condition_DU)==0) - return uprv_decContextSetStatus(context, DEC_Division_undefined); - if (strcmp(string, DEC_Condition_IE)==0) - return uprv_decContextSetStatus(context, DEC_Inexact); - if (strcmp(string, DEC_Condition_IS)==0) - return uprv_decContextSetStatus(context, DEC_Insufficient_storage); - if (strcmp(string, DEC_Condition_IC)==0) - return uprv_decContextSetStatus(context, DEC_Invalid_context); - if (strcmp(string, DEC_Condition_IO)==0) - return uprv_decContextSetStatus(context, DEC_Invalid_operation); - #if DECSUBSET - if (strcmp(string, DEC_Condition_LD)==0) - return uprv_decContextSetStatus(context, DEC_Lost_digits); - #endif - if (strcmp(string, DEC_Condition_OV)==0) - return uprv_decContextSetStatus(context, DEC_Overflow); - if (strcmp(string, DEC_Condition_PA)==0) - return uprv_decContextSetStatus(context, DEC_Clamped); - if (strcmp(string, DEC_Condition_RO)==0) - return uprv_decContextSetStatus(context, DEC_Rounded); - if (strcmp(string, DEC_Condition_SU)==0) - return uprv_decContextSetStatus(context, DEC_Subnormal); - if (strcmp(string, DEC_Condition_UN)==0) - return uprv_decContextSetStatus(context, DEC_Underflow); - if (strcmp(string, DEC_Condition_ZE)==0) - return context; - return NULL; /* Multiple status, or unknown */ - } /* decContextSetStatusFromString */ - -/* ------------------------------------------------------------------ */ -/* decContextSetStatusFromStringQuiet -- set status from a string */ -/* */ -/* context is the context structure to be updated */ -/* string is a string exactly equal to one that might be returned */ -/* by decContextStatusToString */ -/* */ -/* The status bit corresponding to the string is set; no trap is */ -/* raised. */ -/* */ -/* returns the context structure, unless the string is equal to */ -/* DEC_Condition_MU or is not recognized. In these cases NULL is */ -/* returned. */ -/* ------------------------------------------------------------------ */ -U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatusFromStringQuiet(decContext *context, - const char *string) { - if (strcmp(string, DEC_Condition_CS)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Conversion_syntax); - if (strcmp(string, DEC_Condition_DZ)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Division_by_zero); - if (strcmp(string, DEC_Condition_DI)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Division_impossible); - if (strcmp(string, DEC_Condition_DU)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Division_undefined); - if (strcmp(string, DEC_Condition_IE)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Inexact); - if (strcmp(string, DEC_Condition_IS)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Insufficient_storage); - if (strcmp(string, DEC_Condition_IC)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Invalid_context); - if (strcmp(string, DEC_Condition_IO)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Invalid_operation); - #if DECSUBSET - if (strcmp(string, DEC_Condition_LD)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Lost_digits); - #endif - if (strcmp(string, DEC_Condition_OV)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Overflow); - if (strcmp(string, DEC_Condition_PA)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Clamped); - if (strcmp(string, DEC_Condition_RO)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Rounded); - if (strcmp(string, DEC_Condition_SU)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Subnormal); - if (strcmp(string, DEC_Condition_UN)==0) - return uprv_decContextSetStatusQuiet(context, DEC_Underflow); - if (strcmp(string, DEC_Condition_ZE)==0) - return context; - return NULL; /* Multiple status, or unknown */ - } /* decContextSetStatusFromStringQuiet */ - -/* ------------------------------------------------------------------ */ -/* decContextSetStatusQuiet -- set status without trap */ -/* */ -/* context is the context structure to be updated */ -/* status is the DEC_ exception code */ -/* returns the context structure */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatusQuiet(decContext *context, uInt status) { - context->status|=status; - return context;} /* decContextSetStatusQuiet */ - -/* ------------------------------------------------------------------ */ -/* decContextStatusToString -- convert status flags to a string */ -/* */ -/* context is a context with valid status field */ -/* */ -/* returns a constant string describing the condition. If multiple */ -/* (or no) flags are set, a generic constant message is returned. */ -/* ------------------------------------------------------------------ */ -U_CAPI const char * U_EXPORT2 uprv_decContextStatusToString(const decContext *context) { - Int status=context->status; - - /* test the five IEEE first, as some of the others are ambiguous when */ - /* DECEXTFLAG=0 */ - if (status==DEC_Invalid_operation ) return DEC_Condition_IO; - if (status==DEC_Division_by_zero ) return DEC_Condition_DZ; - if (status==DEC_Overflow ) return DEC_Condition_OV; - if (status==DEC_Underflow ) return DEC_Condition_UN; - if (status==DEC_Inexact ) return DEC_Condition_IE; - - if (status==DEC_Division_impossible ) return DEC_Condition_DI; - if (status==DEC_Division_undefined ) return DEC_Condition_DU; - if (status==DEC_Rounded ) return DEC_Condition_RO; - if (status==DEC_Clamped ) return DEC_Condition_PA; - if (status==DEC_Subnormal ) return DEC_Condition_SU; - if (status==DEC_Conversion_syntax ) return DEC_Condition_CS; - if (status==DEC_Insufficient_storage ) return DEC_Condition_IS; - if (status==DEC_Invalid_context ) return DEC_Condition_IC; - #if DECSUBSET - if (status==DEC_Lost_digits ) return DEC_Condition_LD; - #endif - if (status==0 ) return DEC_Condition_ZE; - return DEC_Condition_MU; /* Multiple errors */ - } /* decContextStatusToString */ - -/* ------------------------------------------------------------------ */ -/* decContextTestEndian -- test whether DECLITEND is set correctly */ -/* */ -/* quiet is 1 to suppress message; 0 otherwise */ -/* returns 0 if DECLITEND is correct */ -/* 1 if DECLITEND is incorrect and should be 1 */ -/* -1 if DECLITEND is incorrect and should be 0 */ -/* */ -/* A message is displayed if the return value is not 0 and quiet==0. */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -#if 0 /* ICU: Unused function. Anyway, do not call printf(). */ -U_CAPI Int U_EXPORT2 uprv_decContextTestEndian(Flag quiet) { - Int res=0; /* optimist */ - uInt dle=(uInt)DECLITEND; /* unsign */ - if (dle>1) dle=1; /* ensure 0 or 1 */ - - if (LITEND!=DECLITEND) { - const char *adj; - if (!quiet) { - if (LITEND) adj="little"; - else adj="big"; - printf("Warning: DECLITEND is set to %d, but this computer appears to be %s-endian\n", - DECLITEND, adj); - } - res=(Int)LITEND-dle; - } - return res; - } /* decContextTestEndian */ -#endif - -/* ------------------------------------------------------------------ */ -/* decContextTestSavedStatus -- test bits in saved status */ -/* */ -/* oldstatus is the status word to be tested */ -/* mask indicates the bits to be tested (the oldstatus bits that */ -/* correspond to each 1 bit in the mask are tested) */ -/* returns 1 if any of the tested bits are 1, or 0 otherwise */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -U_CAPI uInt U_EXPORT2 uprv_decContextTestSavedStatus(uInt oldstatus, uInt mask) { - return (oldstatus&mask)!=0; - } /* decContextTestSavedStatus */ - -/* ------------------------------------------------------------------ */ -/* decContextTestStatus -- test bits in current status */ -/* */ -/* context is the context structure to be updated */ -/* mask indicates the bits to be tested (the status bits that */ -/* correspond to each 1 bit in the mask are tested) */ -/* returns 1 if any of the tested bits are 1, or 0 otherwise */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -U_CAPI uInt U_EXPORT2 uprv_decContextTestStatus(decContext *context, uInt mask) { - return (context->status&mask)!=0; - } /* decContextTestStatus */ - -/* ------------------------------------------------------------------ */ -/* decContextZeroStatus -- clear all status bits */ -/* */ -/* context is the context structure to be updated */ -/* returns context */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -U_CAPI decContext * U_EXPORT2 uprv_decContextZeroStatus(decContext *context) { - context->status=0; - return context; - } /* decContextZeroStatus */ - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* ------------------------------------------------------------------ */ +/* Decimal Context module */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000-2012. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This module comprises the routines for handling arithmetic */ +/* context structures. */ +/* ------------------------------------------------------------------ */ + +#include /* for strcmp */ +#include /* for printf if DECCHECK */ +#include "decContext.h" /* context and base types */ +#include "decNumberLocal.h" /* decNumber local types, etc. */ + +#if 0 /* ICU: No need to test endianness at runtime. */ +/* compile-time endian tester [assumes sizeof(Int)>1] */ +static const Int mfcone=1; /* constant 1 */ +static const Flag *mfctop=(Flag *)&mfcone; /* -> top byte */ +#define LITEND *mfctop /* named flag; 1=little-endian */ +#endif + +/* ------------------------------------------------------------------ */ +/* decContextClearStatus -- clear bits in current status */ +/* */ +/* context is the context structure to be queried */ +/* mask indicates the bits to be cleared (the status bit that */ +/* corresponds to each 1 bit in the mask is cleared) */ +/* returns context */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +U_CAPI decContext * U_EXPORT2 uprv_decContextClearStatus(decContext *context, uInt mask) { + context->status&=~mask; + return context; + } /* decContextClearStatus */ + +/* ------------------------------------------------------------------ */ +/* decContextDefault -- initialize a context structure */ +/* */ +/* context is the structure to be initialized */ +/* kind selects the required set of default values, one of: */ +/* DEC_INIT_BASE -- select ANSI X3-274 defaults */ +/* DEC_INIT_DECIMAL32 -- select IEEE 754 defaults, 32-bit */ +/* DEC_INIT_DECIMAL64 -- select IEEE 754 defaults, 64-bit */ +/* DEC_INIT_DECIMAL128 -- select IEEE 754 defaults, 128-bit */ +/* For any other value a valid context is returned, but with */ +/* Invalid_operation set in the status field. */ +/* returns a context structure with the appropriate initial values. */ +/* ------------------------------------------------------------------ */ +U_CAPI decContext * U_EXPORT2 uprv_decContextDefault(decContext *context, Int kind) { + /* set defaults... */ + context->digits=9; /* 9 digits */ + context->emax=DEC_MAX_EMAX; /* 9-digit exponents */ + context->emin=DEC_MIN_EMIN; /* .. balanced */ + context->round=DEC_ROUND_HALF_UP; /* 0.5 rises */ + context->traps=DEC_Errors; /* all but informational */ + context->status=0; /* cleared */ + context->clamp=0; /* no clamping */ + #if DECSUBSET + context->extended=0; /* cleared */ + #endif + switch (kind) { + case DEC_INIT_BASE: + /* [use defaults] */ + break; + case DEC_INIT_DECIMAL32: + context->digits=7; /* digits */ + context->emax=96; /* Emax */ + context->emin=-95; /* Emin */ + context->round=DEC_ROUND_HALF_EVEN; /* 0.5 to nearest even */ + context->traps=0; /* no traps set */ + context->clamp=1; /* clamp exponents */ + #if DECSUBSET + context->extended=1; /* set */ + #endif + break; + case DEC_INIT_DECIMAL64: + context->digits=16; /* digits */ + context->emax=384; /* Emax */ + context->emin=-383; /* Emin */ + context->round=DEC_ROUND_HALF_EVEN; /* 0.5 to nearest even */ + context->traps=0; /* no traps set */ + context->clamp=1; /* clamp exponents */ + #if DECSUBSET + context->extended=1; /* set */ + #endif + break; + case DEC_INIT_DECIMAL128: + context->digits=34; /* digits */ + context->emax=6144; /* Emax */ + context->emin=-6143; /* Emin */ + context->round=DEC_ROUND_HALF_EVEN; /* 0.5 to nearest even */ + context->traps=0; /* no traps set */ + context->clamp=1; /* clamp exponents */ + #if DECSUBSET + context->extended=1; /* set */ + #endif + break; + + default: /* invalid Kind */ + /* use defaults, and .. */ + uprv_decContextSetStatus(context, DEC_Invalid_operation); /* trap */ + } + + return context;} /* decContextDefault */ + +/* ------------------------------------------------------------------ */ +/* decContextGetRounding -- return current rounding mode */ +/* */ +/* context is the context structure to be queried */ +/* returns the rounding mode */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +U_CAPI enum rounding U_EXPORT2 uprv_decContextGetRounding(decContext *context) { + return context->round; + } /* decContextGetRounding */ + +/* ------------------------------------------------------------------ */ +/* decContextGetStatus -- return current status */ +/* */ +/* context is the context structure to be queried */ +/* returns status */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +U_CAPI uInt U_EXPORT2 uprv_decContextGetStatus(decContext *context) { + return context->status; + } /* decContextGetStatus */ + +/* ------------------------------------------------------------------ */ +/* decContextRestoreStatus -- restore bits in current status */ +/* */ +/* context is the context structure to be updated */ +/* newstatus is the source for the bits to be restored */ +/* mask indicates the bits to be restored (the status bit that */ +/* corresponds to each 1 bit in the mask is set to the value of */ +/* the corresponding bit in newstatus) */ +/* returns context */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +U_CAPI decContext * U_EXPORT2 uprv_decContextRestoreStatus(decContext *context, + uInt newstatus, uInt mask) { + context->status&=~mask; /* clear the selected bits */ + context->status|=(mask&newstatus); /* or in the new bits */ + return context; + } /* decContextRestoreStatus */ + +/* ------------------------------------------------------------------ */ +/* decContextSaveStatus -- save bits in current status */ +/* */ +/* context is the context structure to be queried */ +/* mask indicates the bits to be saved (the status bits that */ +/* correspond to each 1 bit in the mask are saved) */ +/* returns the AND of the mask and the current status */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +U_CAPI uInt U_EXPORT2 uprv_decContextSaveStatus(decContext *context, uInt mask) { + return context->status&mask; + } /* decContextSaveStatus */ + +/* ------------------------------------------------------------------ */ +/* decContextSetRounding -- set current rounding mode */ +/* */ +/* context is the context structure to be updated */ +/* newround is the value which will replace the current mode */ +/* returns context */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +U_CAPI decContext * U_EXPORT2 uprv_decContextSetRounding(decContext *context, + enum rounding newround) { + context->round=newround; + return context; + } /* decContextSetRounding */ + +/* ------------------------------------------------------------------ */ +/* decContextSetStatus -- set status and raise trap if appropriate */ +/* */ +/* context is the context structure to be updated */ +/* status is the DEC_ exception code */ +/* returns the context structure */ +/* */ +/* Control may never return from this routine, if there is a signal */ +/* handler and it takes a long jump. */ +/* ------------------------------------------------------------------ */ +U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatus(decContext *context, uInt status) { + context->status|=status; +#if 0 /* ICU: Do not raise signals. */ + if (status & context->traps) raise(SIGFPE); +#endif + return context;} /* decContextSetStatus */ + +/* ------------------------------------------------------------------ */ +/* decContextSetStatusFromString -- set status from a string + trap */ +/* */ +/* context is the context structure to be updated */ +/* string is a string exactly equal to one that might be returned */ +/* by decContextStatusToString */ +/* */ +/* The status bit corresponding to the string is set, and a trap */ +/* is raised if appropriate. */ +/* */ +/* returns the context structure, unless the string is equal to */ +/* DEC_Condition_MU or is not recognized. In these cases nullptr is */ +/* returned. */ +/* ------------------------------------------------------------------ */ +U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatusFromString(decContext *context, + const char *string) { + if (strcmp(string, DEC_Condition_CS)==0) + return uprv_decContextSetStatus(context, DEC_Conversion_syntax); + if (strcmp(string, DEC_Condition_DZ)==0) + return uprv_decContextSetStatus(context, DEC_Division_by_zero); + if (strcmp(string, DEC_Condition_DI)==0) + return uprv_decContextSetStatus(context, DEC_Division_impossible); + if (strcmp(string, DEC_Condition_DU)==0) + return uprv_decContextSetStatus(context, DEC_Division_undefined); + if (strcmp(string, DEC_Condition_IE)==0) + return uprv_decContextSetStatus(context, DEC_Inexact); + if (strcmp(string, DEC_Condition_IS)==0) + return uprv_decContextSetStatus(context, DEC_Insufficient_storage); + if (strcmp(string, DEC_Condition_IC)==0) + return uprv_decContextSetStatus(context, DEC_Invalid_context); + if (strcmp(string, DEC_Condition_IO)==0) + return uprv_decContextSetStatus(context, DEC_Invalid_operation); + #if DECSUBSET + if (strcmp(string, DEC_Condition_LD)==0) + return uprv_decContextSetStatus(context, DEC_Lost_digits); + #endif + if (strcmp(string, DEC_Condition_OV)==0) + return uprv_decContextSetStatus(context, DEC_Overflow); + if (strcmp(string, DEC_Condition_PA)==0) + return uprv_decContextSetStatus(context, DEC_Clamped); + if (strcmp(string, DEC_Condition_RO)==0) + return uprv_decContextSetStatus(context, DEC_Rounded); + if (strcmp(string, DEC_Condition_SU)==0) + return uprv_decContextSetStatus(context, DEC_Subnormal); + if (strcmp(string, DEC_Condition_UN)==0) + return uprv_decContextSetStatus(context, DEC_Underflow); + if (strcmp(string, DEC_Condition_ZE)==0) + return context; + return nullptr; /* Multiple status, or unknown */ + } /* decContextSetStatusFromString */ + +/* ------------------------------------------------------------------ */ +/* decContextSetStatusFromStringQuiet -- set status from a string */ +/* */ +/* context is the context structure to be updated */ +/* string is a string exactly equal to one that might be returned */ +/* by decContextStatusToString */ +/* */ +/* The status bit corresponding to the string is set; no trap is */ +/* raised. */ +/* */ +/* returns the context structure, unless the string is equal to */ +/* DEC_Condition_MU or is not recognized. In these cases nullptr is */ +/* returned. */ +/* ------------------------------------------------------------------ */ +U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatusFromStringQuiet(decContext *context, + const char *string) { + if (strcmp(string, DEC_Condition_CS)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Conversion_syntax); + if (strcmp(string, DEC_Condition_DZ)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Division_by_zero); + if (strcmp(string, DEC_Condition_DI)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Division_impossible); + if (strcmp(string, DEC_Condition_DU)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Division_undefined); + if (strcmp(string, DEC_Condition_IE)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Inexact); + if (strcmp(string, DEC_Condition_IS)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Insufficient_storage); + if (strcmp(string, DEC_Condition_IC)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Invalid_context); + if (strcmp(string, DEC_Condition_IO)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Invalid_operation); + #if DECSUBSET + if (strcmp(string, DEC_Condition_LD)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Lost_digits); + #endif + if (strcmp(string, DEC_Condition_OV)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Overflow); + if (strcmp(string, DEC_Condition_PA)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Clamped); + if (strcmp(string, DEC_Condition_RO)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Rounded); + if (strcmp(string, DEC_Condition_SU)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Subnormal); + if (strcmp(string, DEC_Condition_UN)==0) + return uprv_decContextSetStatusQuiet(context, DEC_Underflow); + if (strcmp(string, DEC_Condition_ZE)==0) + return context; + return nullptr; /* Multiple status, or unknown */ + } /* decContextSetStatusFromStringQuiet */ + +/* ------------------------------------------------------------------ */ +/* decContextSetStatusQuiet -- set status without trap */ +/* */ +/* context is the context structure to be updated */ +/* status is the DEC_ exception code */ +/* returns the context structure */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatusQuiet(decContext *context, uInt status) { + context->status|=status; + return context;} /* decContextSetStatusQuiet */ + +/* ------------------------------------------------------------------ */ +/* decContextStatusToString -- convert status flags to a string */ +/* */ +/* context is a context with valid status field */ +/* */ +/* returns a constant string describing the condition. If multiple */ +/* (or no) flags are set, a generic constant message is returned. */ +/* ------------------------------------------------------------------ */ +U_CAPI const char * U_EXPORT2 uprv_decContextStatusToString(const decContext *context) { + Int status=context->status; + + /* test the five IEEE first, as some of the others are ambiguous when */ + /* DECEXTFLAG=0 */ + if (status==DEC_Invalid_operation ) return DEC_Condition_IO; + if (status==DEC_Division_by_zero ) return DEC_Condition_DZ; + if (status==DEC_Overflow ) return DEC_Condition_OV; + if (status==DEC_Underflow ) return DEC_Condition_UN; + if (status==DEC_Inexact ) return DEC_Condition_IE; + + if (status==DEC_Division_impossible ) return DEC_Condition_DI; + if (status==DEC_Division_undefined ) return DEC_Condition_DU; + if (status==DEC_Rounded ) return DEC_Condition_RO; + if (status==DEC_Clamped ) return DEC_Condition_PA; + if (status==DEC_Subnormal ) return DEC_Condition_SU; + if (status==DEC_Conversion_syntax ) return DEC_Condition_CS; + if (status==DEC_Insufficient_storage ) return DEC_Condition_IS; + if (status==DEC_Invalid_context ) return DEC_Condition_IC; + #if DECSUBSET + if (status==DEC_Lost_digits ) return DEC_Condition_LD; + #endif + if (status==0 ) return DEC_Condition_ZE; + return DEC_Condition_MU; /* Multiple errors */ + } /* decContextStatusToString */ + +/* ------------------------------------------------------------------ */ +/* decContextTestEndian -- test whether DECLITEND is set correctly */ +/* */ +/* quiet is 1 to suppress message; 0 otherwise */ +/* returns 0 if DECLITEND is correct */ +/* 1 if DECLITEND is incorrect and should be 1 */ +/* -1 if DECLITEND is incorrect and should be 0 */ +/* */ +/* A message is displayed if the return value is not 0 and quiet==0. */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +#if 0 /* ICU: Unused function. Anyway, do not call printf(). */ +U_CAPI Int U_EXPORT2 uprv_decContextTestEndian(Flag quiet) { + Int res=0; /* optimist */ + uInt dle=(uInt)DECLITEND; /* unsign */ + if (dle>1) dle=1; /* ensure 0 or 1 */ + + if (LITEND!=DECLITEND) { + const char *adj; + if (!quiet) { + if (LITEND) adj="little"; + else adj="big"; + printf("Warning: DECLITEND is set to %d, but this computer appears to be %s-endian\n", + DECLITEND, adj); + } + res=(Int)LITEND-dle; + } + return res; + } /* decContextTestEndian */ +#endif + +/* ------------------------------------------------------------------ */ +/* decContextTestSavedStatus -- test bits in saved status */ +/* */ +/* oldstatus is the status word to be tested */ +/* mask indicates the bits to be tested (the oldstatus bits that */ +/* correspond to each 1 bit in the mask are tested) */ +/* returns 1 if any of the tested bits are 1, or 0 otherwise */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +U_CAPI uInt U_EXPORT2 uprv_decContextTestSavedStatus(uInt oldstatus, uInt mask) { + return (oldstatus&mask)!=0; + } /* decContextTestSavedStatus */ + +/* ------------------------------------------------------------------ */ +/* decContextTestStatus -- test bits in current status */ +/* */ +/* context is the context structure to be updated */ +/* mask indicates the bits to be tested (the status bits that */ +/* correspond to each 1 bit in the mask are tested) */ +/* returns 1 if any of the tested bits are 1, or 0 otherwise */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +U_CAPI uInt U_EXPORT2 uprv_decContextTestStatus(decContext *context, uInt mask) { + return (context->status&mask)!=0; + } /* decContextTestStatus */ + +/* ------------------------------------------------------------------ */ +/* decContextZeroStatus -- clear all status bits */ +/* */ +/* context is the context structure to be updated */ +/* returns context */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +U_CAPI decContext * U_EXPORT2 uprv_decContextZeroStatus(decContext *context) { + context->status=0; + return context; + } /* decContextZeroStatus */ + diff --git a/deps/icu-small/source/i18n/decContext.h b/deps/icu-small/source/i18n/decContext.h index 91c6739739dcdc..fee6373a59c461 100644 --- a/deps/icu-small/source/i18n/decContext.h +++ b/deps/icu-small/source/i18n/decContext.h @@ -1,269 +1,269 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* ------------------------------------------------------------------ */ -/* Decimal Context module header */ -/* ------------------------------------------------------------------ */ -/* Copyright (c) IBM Corporation, 2000-2011. All rights reserved. */ -/* */ -/* This software is made available under the terms of the */ -/* ICU License -- ICU 1.8.1 and later. */ -/* */ -/* The description and User's Guide ("The decNumber C Library") for */ -/* this software is called decNumber.pdf. This document is */ -/* available, together with arithmetic and format specifications, */ -/* testcases, and Web links, on the General Decimal Arithmetic page. */ -/* */ -/* Please send comments, suggestions, and corrections to the author: */ -/* mfc@uk.ibm.com */ -/* Mike Cowlishaw, IBM Fellow */ -/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ -/* ------------------------------------------------------------------ */ - -/* Modified version, for use from within ICU. - * Renamed public functions, to avoid an unwanted export of the - * standard names from the ICU library. - * - * Use ICU's uprv_malloc() and uprv_free() - * - * Revert comment syntax to plain C - * - * Remove a few compiler warnings. - */ -#include "unicode/utypes.h" -#include "putilimp.h" - -/* */ -/* Context variables must always have valid values: */ -/* */ -/* status -- [any bits may be cleared, but not set, by user] */ -/* round -- must be one of the enumerated rounding modes */ -/* */ -/* The following variables are implied for fixed size formats (i.e., */ -/* they are ignored) but should still be set correctly in case used */ -/* with decNumber functions: */ -/* */ -/* clamp -- must be either 0 or 1 */ -/* digits -- must be in the range 1 through 999999999 */ -/* emax -- must be in the range 0 through 999999999 */ -/* emin -- must be in the range 0 through -999999999 */ -/* extended -- must be either 0 or 1 [present only if DECSUBSET] */ -/* traps -- only defined bits may be set */ -/* */ -/* ------------------------------------------------------------------ */ - -#if !defined(DECCONTEXT) - #define DECCONTEXT - #define DECCNAME "decContext" /* Short name */ - #define DECCFULLNAME "Decimal Context Descriptor" /* Verbose name */ - #define DECCAUTHOR "Mike Cowlishaw" /* Who to blame */ - - #if !defined(int32_t) -/* #include */ /* C99 standard integers */ - #endif - #include /* for printf, etc. */ - #include /* for traps */ - - /* Extended flags setting -- set this to 0 to use only IEEE flags */ - #if !defined(DECEXTFLAG) - #define DECEXTFLAG 1 /* 1=enable extended flags */ - #endif - - /* Conditional code flag -- set this to 0 for best performance */ - #if !defined(DECSUBSET) - #define DECSUBSET 0 /* 1=enable subset arithmetic */ - #endif - - /* Context for operations, with associated constants */ - enum rounding { - DEC_ROUND_CEILING, /* round towards +infinity */ - DEC_ROUND_UP, /* round away from 0 */ - DEC_ROUND_HALF_UP, /* 0.5 rounds up */ - DEC_ROUND_HALF_EVEN, /* 0.5 rounds to nearest even */ - DEC_ROUND_HALF_DOWN, /* 0.5 rounds down */ - DEC_ROUND_DOWN, /* round towards 0 (truncate) */ - DEC_ROUND_FLOOR, /* round towards -infinity */ - DEC_ROUND_05UP, /* round for reround */ - DEC_ROUND_MAX /* enum must be less than this */ - }; - #define DEC_ROUND_DEFAULT DEC_ROUND_HALF_EVEN; - - typedef struct { - int32_t digits; /* working precision */ - int32_t emax; /* maximum positive exponent */ - int32_t emin; /* minimum negative exponent */ - enum rounding round; /* rounding mode */ - uint32_t traps; /* trap-enabler flags */ - uint32_t status; /* status flags */ - uint8_t clamp; /* flag: apply IEEE exponent clamp */ - #if DECSUBSET - uint8_t extended; /* flag: special-values allowed */ - #endif - } decContext; - - /* Maxima and Minima for context settings */ - #define DEC_MAX_DIGITS 999999999 - #define DEC_MIN_DIGITS 1 - #define DEC_MAX_EMAX 999999999 - #define DEC_MIN_EMAX 0 - #define DEC_MAX_EMIN 0 - #define DEC_MIN_EMIN -999999999 - #define DEC_MAX_MATH 999999 /* max emax, etc., for math funcs. */ - - /* Classifications for decimal numbers, aligned with 754 (note that */ - /* 'normal' and 'subnormal' are meaningful only with a decContext */ - /* or a fixed size format). */ - enum decClass { - DEC_CLASS_SNAN, - DEC_CLASS_QNAN, - DEC_CLASS_NEG_INF, - DEC_CLASS_NEG_NORMAL, - DEC_CLASS_NEG_SUBNORMAL, - DEC_CLASS_NEG_ZERO, - DEC_CLASS_POS_ZERO, - DEC_CLASS_POS_SUBNORMAL, - DEC_CLASS_POS_NORMAL, - DEC_CLASS_POS_INF - }; - /* Strings for the decClasses */ - #define DEC_ClassString_SN "sNaN" - #define DEC_ClassString_QN "NaN" - #define DEC_ClassString_NI "-Infinity" - #define DEC_ClassString_NN "-Normal" - #define DEC_ClassString_NS "-Subnormal" - #define DEC_ClassString_NZ "-Zero" - #define DEC_ClassString_PZ "+Zero" - #define DEC_ClassString_PS "+Subnormal" - #define DEC_ClassString_PN "+Normal" - #define DEC_ClassString_PI "+Infinity" - #define DEC_ClassString_UN "Invalid" - - /* Trap-enabler and Status flags (exceptional conditions), and */ - /* their names. The top byte is reserved for internal use */ - #if DECEXTFLAG - /* Extended flags */ - #define DEC_Conversion_syntax 0x00000001 - #define DEC_Division_by_zero 0x00000002 - #define DEC_Division_impossible 0x00000004 - #define DEC_Division_undefined 0x00000008 - #define DEC_Insufficient_storage 0x00000010 /* [when malloc fails] */ - #define DEC_Inexact 0x00000020 - #define DEC_Invalid_context 0x00000040 - #define DEC_Invalid_operation 0x00000080 - #if DECSUBSET - #define DEC_Lost_digits 0x00000100 - #endif - #define DEC_Overflow 0x00000200 - #define DEC_Clamped 0x00000400 - #define DEC_Rounded 0x00000800 - #define DEC_Subnormal 0x00001000 - #define DEC_Underflow 0x00002000 - #else - /* IEEE flags only */ - #define DEC_Conversion_syntax 0x00000010 - #define DEC_Division_by_zero 0x00000002 - #define DEC_Division_impossible 0x00000010 - #define DEC_Division_undefined 0x00000010 - #define DEC_Insufficient_storage 0x00000010 /* [when malloc fails] */ - #define DEC_Inexact 0x00000001 - #define DEC_Invalid_context 0x00000010 - #define DEC_Invalid_operation 0x00000010 - #if DECSUBSET - #define DEC_Lost_digits 0x00000000 - #endif - #define DEC_Overflow 0x00000008 - #define DEC_Clamped 0x00000000 - #define DEC_Rounded 0x00000000 - #define DEC_Subnormal 0x00000000 - #define DEC_Underflow 0x00000004 - #endif - - /* IEEE 754 groupings for the flags */ - /* [DEC_Clamped, DEC_Lost_digits, DEC_Rounded, and DEC_Subnormal */ - /* are not in IEEE 754] */ - #define DEC_IEEE_754_Division_by_zero (DEC_Division_by_zero) - #if DECSUBSET - #define DEC_IEEE_754_Inexact (DEC_Inexact | DEC_Lost_digits) - #else - #define DEC_IEEE_754_Inexact (DEC_Inexact) - #endif - #define DEC_IEEE_754_Invalid_operation (DEC_Conversion_syntax | \ - DEC_Division_impossible | \ - DEC_Division_undefined | \ - DEC_Insufficient_storage | \ - DEC_Invalid_context | \ - DEC_Invalid_operation) - #define DEC_IEEE_754_Overflow (DEC_Overflow) - #define DEC_IEEE_754_Underflow (DEC_Underflow) - - /* flags which are normally errors (result is qNaN, infinite, or 0) */ - #define DEC_Errors (DEC_IEEE_754_Division_by_zero | \ - DEC_IEEE_754_Invalid_operation | \ - DEC_IEEE_754_Overflow | DEC_IEEE_754_Underflow) - /* flags which cause a result to become qNaN */ - #define DEC_NaNs DEC_IEEE_754_Invalid_operation - - /* flags which are normally for information only (finite results) */ - #if DECSUBSET - #define DEC_Information (DEC_Clamped | DEC_Rounded | DEC_Inexact \ - | DEC_Lost_digits) - #else - #define DEC_Information (DEC_Clamped | DEC_Rounded | DEC_Inexact) - #endif - - /* IEEE 854 names (for compatibility with older decNumber versions) */ - #define DEC_IEEE_854_Division_by_zero DEC_IEEE_754_Division_by_zero - #define DEC_IEEE_854_Inexact DEC_IEEE_754_Inexact - #define DEC_IEEE_854_Invalid_operation DEC_IEEE_754_Invalid_operation - #define DEC_IEEE_854_Overflow DEC_IEEE_754_Overflow - #define DEC_IEEE_854_Underflow DEC_IEEE_754_Underflow - - /* Name strings for the exceptional conditions */ - #define DEC_Condition_CS "Conversion syntax" - #define DEC_Condition_DZ "Division by zero" - #define DEC_Condition_DI "Division impossible" - #define DEC_Condition_DU "Division undefined" - #define DEC_Condition_IE "Inexact" - #define DEC_Condition_IS "Insufficient storage" - #define DEC_Condition_IC "Invalid context" - #define DEC_Condition_IO "Invalid operation" - #if DECSUBSET - #define DEC_Condition_LD "Lost digits" - #endif - #define DEC_Condition_OV "Overflow" - #define DEC_Condition_PA "Clamped" - #define DEC_Condition_RO "Rounded" - #define DEC_Condition_SU "Subnormal" - #define DEC_Condition_UN "Underflow" - #define DEC_Condition_ZE "No status" - #define DEC_Condition_MU "Multiple status" - #define DEC_Condition_Length 21 /* length of the longest string, */ - /* including terminator */ - - /* Initialization descriptors, used by decContextDefault */ - #define DEC_INIT_BASE 0 - #define DEC_INIT_DECIMAL32 32 - #define DEC_INIT_DECIMAL64 64 - #define DEC_INIT_DECIMAL128 128 - /* Synonyms */ - #define DEC_INIT_DECSINGLE DEC_INIT_DECIMAL32 - #define DEC_INIT_DECDOUBLE DEC_INIT_DECIMAL64 - #define DEC_INIT_DECQUAD DEC_INIT_DECIMAL128 - - /* decContext routines */ - U_CAPI decContext * U_EXPORT2 uprv_decContextClearStatus(decContext *, uint32_t); - U_CAPI decContext * U_EXPORT2 uprv_decContextDefault(decContext *, int32_t); - U_CAPI enum rounding U_EXPORT2 uprv_decContextGetRounding(decContext *); - U_CAPI uint32_t U_EXPORT2 uprv_decContextGetStatus(decContext *); - U_CAPI decContext * U_EXPORT2 uprv_decContextRestoreStatus(decContext *, uint32_t, uint32_t); - U_CAPI uint32_t U_EXPORT2 uprv_decContextSaveStatus(decContext *, uint32_t); - U_CAPI decContext * U_EXPORT2 uprv_decContextSetRounding(decContext *, enum rounding); - U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatus(decContext *, uint32_t); - U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatusFromString(decContext *, const char *); - U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatusFromStringQuiet(decContext *, const char *); - U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatusQuiet(decContext *, uint32_t); - U_CAPI const char * U_EXPORT2 uprv_decContextStatusToString(const decContext *); - U_CAPI uint32_t U_EXPORT2 uprv_decContextTestSavedStatus(uint32_t, uint32_t); - U_CAPI uint32_t U_EXPORT2 uprv_decContextTestStatus(decContext *, uint32_t); - U_CAPI decContext * U_EXPORT2 uprv_decContextZeroStatus(decContext *); - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* ------------------------------------------------------------------ */ +/* Decimal Context module header */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000-2011. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ + +/* Modified version, for use from within ICU. + * Renamed public functions, to avoid an unwanted export of the + * standard names from the ICU library. + * + * Use ICU's uprv_malloc() and uprv_free() + * + * Revert comment syntax to plain C + * + * Remove a few compiler warnings. + */ +#include "unicode/utypes.h" +#include "putilimp.h" + +/* */ +/* Context variables must always have valid values: */ +/* */ +/* status -- [any bits may be cleared, but not set, by user] */ +/* round -- must be one of the enumerated rounding modes */ +/* */ +/* The following variables are implied for fixed size formats (i.e., */ +/* they are ignored) but should still be set correctly in case used */ +/* with decNumber functions: */ +/* */ +/* clamp -- must be either 0 or 1 */ +/* digits -- must be in the range 1 through 999999999 */ +/* emax -- must be in the range 0 through 999999999 */ +/* emin -- must be in the range 0 through -999999999 */ +/* extended -- must be either 0 or 1 [present only if DECSUBSET] */ +/* traps -- only defined bits may be set */ +/* */ +/* ------------------------------------------------------------------ */ + +#if !defined(DECCONTEXT) + #define DECCONTEXT + #define DECCNAME "decContext" /* Short name */ + #define DECCFULLNAME "Decimal Context Descriptor" /* Verbose name */ + #define DECCAUTHOR "Mike Cowlishaw" /* Who to blame */ + + #if !defined(int32_t) +/* #include */ /* C99 standard integers */ + #endif + #include /* for printf, etc. */ + #include /* for traps */ + + /* Extended flags setting -- set this to 0 to use only IEEE flags */ + #if !defined(DECEXTFLAG) + #define DECEXTFLAG 1 /* 1=enable extended flags */ + #endif + + /* Conditional code flag -- set this to 0 for best performance */ + #if !defined(DECSUBSET) + #define DECSUBSET 0 /* 1=enable subset arithmetic */ + #endif + + /* Context for operations, with associated constants */ + enum rounding { + DEC_ROUND_CEILING, /* round towards +infinity */ + DEC_ROUND_UP, /* round away from 0 */ + DEC_ROUND_HALF_UP, /* 0.5 rounds up */ + DEC_ROUND_HALF_EVEN, /* 0.5 rounds to nearest even */ + DEC_ROUND_HALF_DOWN, /* 0.5 rounds down */ + DEC_ROUND_DOWN, /* round towards 0 (truncate) */ + DEC_ROUND_FLOOR, /* round towards -infinity */ + DEC_ROUND_05UP, /* round for reround */ + DEC_ROUND_MAX /* enum must be less than this */ + }; + #define DEC_ROUND_DEFAULT DEC_ROUND_HALF_EVEN; + + typedef struct { + int32_t digits; /* working precision */ + int32_t emax; /* maximum positive exponent */ + int32_t emin; /* minimum negative exponent */ + enum rounding round; /* rounding mode */ + uint32_t traps; /* trap-enabler flags */ + uint32_t status; /* status flags */ + uint8_t clamp; /* flag: apply IEEE exponent clamp */ + #if DECSUBSET + uint8_t extended; /* flag: special-values allowed */ + #endif + } decContext; + + /* Maxima and Minima for context settings */ + #define DEC_MAX_DIGITS 999999999 + #define DEC_MIN_DIGITS 1 + #define DEC_MAX_EMAX 999999999 + #define DEC_MIN_EMAX 0 + #define DEC_MAX_EMIN 0 + #define DEC_MIN_EMIN -999999999 + #define DEC_MAX_MATH 999999 /* max emax, etc., for math funcs. */ + + /* Classifications for decimal numbers, aligned with 754 (note that */ + /* 'normal' and 'subnormal' are meaningful only with a decContext */ + /* or a fixed size format). */ + enum decClass { + DEC_CLASS_SNAN, + DEC_CLASS_QNAN, + DEC_CLASS_NEG_INF, + DEC_CLASS_NEG_NORMAL, + DEC_CLASS_NEG_SUBNORMAL, + DEC_CLASS_NEG_ZERO, + DEC_CLASS_POS_ZERO, + DEC_CLASS_POS_SUBNORMAL, + DEC_CLASS_POS_NORMAL, + DEC_CLASS_POS_INF + }; + /* Strings for the decClasses */ + #define DEC_ClassString_SN "sNaN" + #define DEC_ClassString_QN "NaN" + #define DEC_ClassString_NI "-Infinity" + #define DEC_ClassString_NN "-Normal" + #define DEC_ClassString_NS "-Subnormal" + #define DEC_ClassString_NZ "-Zero" + #define DEC_ClassString_PZ "+Zero" + #define DEC_ClassString_PS "+Subnormal" + #define DEC_ClassString_PN "+Normal" + #define DEC_ClassString_PI "+Infinity" + #define DEC_ClassString_UN "Invalid" + + /* Trap-enabler and Status flags (exceptional conditions), and */ + /* their names. The top byte is reserved for internal use */ + #if DECEXTFLAG + /* Extended flags */ + #define DEC_Conversion_syntax 0x00000001 + #define DEC_Division_by_zero 0x00000002 + #define DEC_Division_impossible 0x00000004 + #define DEC_Division_undefined 0x00000008 + #define DEC_Insufficient_storage 0x00000010 /* [when malloc fails] */ + #define DEC_Inexact 0x00000020 + #define DEC_Invalid_context 0x00000040 + #define DEC_Invalid_operation 0x00000080 + #if DECSUBSET + #define DEC_Lost_digits 0x00000100 + #endif + #define DEC_Overflow 0x00000200 + #define DEC_Clamped 0x00000400 + #define DEC_Rounded 0x00000800 + #define DEC_Subnormal 0x00001000 + #define DEC_Underflow 0x00002000 + #else + /* IEEE flags only */ + #define DEC_Conversion_syntax 0x00000010 + #define DEC_Division_by_zero 0x00000002 + #define DEC_Division_impossible 0x00000010 + #define DEC_Division_undefined 0x00000010 + #define DEC_Insufficient_storage 0x00000010 /* [when malloc fails] */ + #define DEC_Inexact 0x00000001 + #define DEC_Invalid_context 0x00000010 + #define DEC_Invalid_operation 0x00000010 + #if DECSUBSET + #define DEC_Lost_digits 0x00000000 + #endif + #define DEC_Overflow 0x00000008 + #define DEC_Clamped 0x00000000 + #define DEC_Rounded 0x00000000 + #define DEC_Subnormal 0x00000000 + #define DEC_Underflow 0x00000004 + #endif + + /* IEEE 754 groupings for the flags */ + /* [DEC_Clamped, DEC_Lost_digits, DEC_Rounded, and DEC_Subnormal */ + /* are not in IEEE 754] */ + #define DEC_IEEE_754_Division_by_zero (DEC_Division_by_zero) + #if DECSUBSET + #define DEC_IEEE_754_Inexact (DEC_Inexact | DEC_Lost_digits) + #else + #define DEC_IEEE_754_Inexact (DEC_Inexact) + #endif + #define DEC_IEEE_754_Invalid_operation (DEC_Conversion_syntax | \ + DEC_Division_impossible | \ + DEC_Division_undefined | \ + DEC_Insufficient_storage | \ + DEC_Invalid_context | \ + DEC_Invalid_operation) + #define DEC_IEEE_754_Overflow (DEC_Overflow) + #define DEC_IEEE_754_Underflow (DEC_Underflow) + + /* flags which are normally errors (result is qNaN, infinite, or 0) */ + #define DEC_Errors (DEC_IEEE_754_Division_by_zero | \ + DEC_IEEE_754_Invalid_operation | \ + DEC_IEEE_754_Overflow | DEC_IEEE_754_Underflow) + /* flags which cause a result to become qNaN */ + #define DEC_NaNs DEC_IEEE_754_Invalid_operation + + /* flags which are normally for information only (finite results) */ + #if DECSUBSET + #define DEC_Information (DEC_Clamped | DEC_Rounded | DEC_Inexact \ + | DEC_Lost_digits) + #else + #define DEC_Information (DEC_Clamped | DEC_Rounded | DEC_Inexact) + #endif + + /* IEEE 854 names (for compatibility with older decNumber versions) */ + #define DEC_IEEE_854_Division_by_zero DEC_IEEE_754_Division_by_zero + #define DEC_IEEE_854_Inexact DEC_IEEE_754_Inexact + #define DEC_IEEE_854_Invalid_operation DEC_IEEE_754_Invalid_operation + #define DEC_IEEE_854_Overflow DEC_IEEE_754_Overflow + #define DEC_IEEE_854_Underflow DEC_IEEE_754_Underflow + + /* Name strings for the exceptional conditions */ + #define DEC_Condition_CS "Conversion syntax" + #define DEC_Condition_DZ "Division by zero" + #define DEC_Condition_DI "Division impossible" + #define DEC_Condition_DU "Division undefined" + #define DEC_Condition_IE "Inexact" + #define DEC_Condition_IS "Insufficient storage" + #define DEC_Condition_IC "Invalid context" + #define DEC_Condition_IO "Invalid operation" + #if DECSUBSET + #define DEC_Condition_LD "Lost digits" + #endif + #define DEC_Condition_OV "Overflow" + #define DEC_Condition_PA "Clamped" + #define DEC_Condition_RO "Rounded" + #define DEC_Condition_SU "Subnormal" + #define DEC_Condition_UN "Underflow" + #define DEC_Condition_ZE "No status" + #define DEC_Condition_MU "Multiple status" + #define DEC_Condition_Length 21 /* length of the longest string, */ + /* including terminator */ + + /* Initialization descriptors, used by decContextDefault */ + #define DEC_INIT_BASE 0 + #define DEC_INIT_DECIMAL32 32 + #define DEC_INIT_DECIMAL64 64 + #define DEC_INIT_DECIMAL128 128 + /* Synonyms */ + #define DEC_INIT_DECSINGLE DEC_INIT_DECIMAL32 + #define DEC_INIT_DECDOUBLE DEC_INIT_DECIMAL64 + #define DEC_INIT_DECQUAD DEC_INIT_DECIMAL128 + + /* decContext routines */ + U_CAPI decContext * U_EXPORT2 uprv_decContextClearStatus(decContext *, uint32_t); + U_CAPI decContext * U_EXPORT2 uprv_decContextDefault(decContext *, int32_t); + U_CAPI enum rounding U_EXPORT2 uprv_decContextGetRounding(decContext *); + U_CAPI uint32_t U_EXPORT2 uprv_decContextGetStatus(decContext *); + U_CAPI decContext * U_EXPORT2 uprv_decContextRestoreStatus(decContext *, uint32_t, uint32_t); + U_CAPI uint32_t U_EXPORT2 uprv_decContextSaveStatus(decContext *, uint32_t); + U_CAPI decContext * U_EXPORT2 uprv_decContextSetRounding(decContext *, enum rounding); + U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatus(decContext *, uint32_t); + U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatusFromString(decContext *, const char *); + U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatusFromStringQuiet(decContext *, const char *); + U_CAPI decContext * U_EXPORT2 uprv_decContextSetStatusQuiet(decContext *, uint32_t); + U_CAPI const char * U_EXPORT2 uprv_decContextStatusToString(const decContext *); + U_CAPI uint32_t U_EXPORT2 uprv_decContextTestSavedStatus(uint32_t, uint32_t); + U_CAPI uint32_t U_EXPORT2 uprv_decContextTestStatus(decContext *, uint32_t); + U_CAPI decContext * U_EXPORT2 uprv_decContextZeroStatus(decContext *); + +#endif diff --git a/deps/icu-small/source/i18n/decNumber.cpp b/deps/icu-small/source/i18n/decNumber.cpp index 71477d8202cbed..c2092bb6b2630e 100644 --- a/deps/icu-small/source/i18n/decNumber.cpp +++ b/deps/icu-small/source/i18n/decNumber.cpp @@ -1,8190 +1,8190 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* ------------------------------------------------------------------ */ -/* Decimal Number arithmetic module */ -/* ------------------------------------------------------------------ */ -/* Copyright (c) IBM Corporation, 2000-2014. All rights reserved. */ -/* */ -/* This software is made available under the terms of the */ -/* ICU License -- ICU 1.8.1 and later. */ -/* */ -/* The description and User's Guide ("The decNumber C Library") for */ -/* this software is called decNumber.pdf. This document is */ -/* available, together with arithmetic and format specifications, */ -/* testcases, and Web links, on the General Decimal Arithmetic page. */ -/* */ -/* Please send comments, suggestions, and corrections to the author: */ -/* mfc@uk.ibm.com */ -/* Mike Cowlishaw, IBM Fellow */ -/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ -/* ------------------------------------------------------------------ */ - -/* Modified version, for use from within ICU. - * Renamed public functions, to avoid an unwanted export of the - * standard names from the ICU library. - * - * Use ICU's uprv_malloc() and uprv_free() - * - * Revert comment syntax to plain C - * - * Remove a few compiler warnings. - */ - -/* This module comprises the routines for arbitrary-precision General */ -/* Decimal Arithmetic as defined in the specification which may be */ -/* found on the General Decimal Arithmetic pages. It implements both */ -/* the full ('extended') arithmetic and the simpler ('subset') */ -/* arithmetic. */ -/* */ -/* Usage notes: */ -/* */ -/* 1. This code is ANSI C89 except: */ -/* */ -/* a) C99 line comments (double forward slash) are used. (Most C */ -/* compilers accept these. If yours does not, a simple script */ -/* can be used to convert them to ANSI C comments.) */ -/* */ -/* b) Types from C99 stdint.h are used. If you do not have this */ -/* header file, see the User's Guide section of the decNumber */ -/* documentation; this lists the necessary definitions. */ -/* */ -/* c) If DECDPUN>4 or DECUSE64=1, the C99 64-bit int64_t and */ -/* uint64_t types may be used. To avoid these, set DECUSE64=0 */ -/* and DECDPUN<=4 (see documentation). */ -/* */ -/* The code also conforms to C99 restrictions; in particular, */ -/* strict aliasing rules are observed. */ -/* */ -/* 2. The decNumber format which this library uses is optimized for */ -/* efficient processing of relatively short numbers; in particular */ -/* it allows the use of fixed sized structures and minimizes copy */ -/* and move operations. It does, however, support arbitrary */ -/* precision (up to 999,999,999 digits) and arbitrary exponent */ -/* range (Emax in the range 0 through 999,999,999 and Emin in the */ -/* range -999,999,999 through 0). Mathematical functions (for */ -/* example decNumberExp) as identified below are restricted more */ -/* tightly: digits, emax, and -emin in the context must be <= */ -/* DEC_MAX_MATH (999999), and their operand(s) must be within */ -/* these bounds. */ -/* */ -/* 3. Logical functions are further restricted; their operands must */ -/* be finite, positive, have an exponent of zero, and all digits */ -/* must be either 0 or 1. The result will only contain digits */ -/* which are 0 or 1 (and will have exponent=0 and a sign of 0). */ -/* */ -/* 4. Operands to operator functions are never modified unless they */ -/* are also specified to be the result number (which is always */ -/* permitted). Other than that case, operands must not overlap. */ -/* */ -/* 5. Error handling: the type of the error is ORed into the status */ -/* flags in the current context (decContext structure). The */ -/* SIGFPE signal is then raised if the corresponding trap-enabler */ -/* flag in the decContext is set (is 1). */ -/* */ -/* It is the responsibility of the caller to clear the status */ -/* flags as required. */ -/* */ -/* The result of any routine which returns a number will always */ -/* be a valid number (which may be a special value, such as an */ -/* Infinity or NaN). */ -/* */ -/* 6. The decNumber format is not an exchangeable concrete */ -/* representation as it comprises fields which may be machine- */ -/* dependent (packed or unpacked, or special length, for example). */ -/* Canonical conversions to and from strings are provided; other */ -/* conversions are available in separate modules. */ -/* */ -/* 7. Normally, input operands are assumed to be valid. Set DECCHECK */ -/* to 1 for extended operand checking (including NULL operands). */ -/* Results are undefined if a badly-formed structure (or a NULL */ -/* pointer to a structure) is provided, though with DECCHECK */ -/* enabled the operator routines are protected against exceptions. */ -/* (Except if the result pointer is NULL, which is unrecoverable.) */ -/* */ -/* However, the routines will never cause exceptions if they are */ -/* given well-formed operands, even if the value of the operands */ -/* is inappropriate for the operation and DECCHECK is not set. */ -/* (Except for SIGFPE, as and where documented.) */ -/* */ -/* 8. Subset arithmetic is available only if DECSUBSET is set to 1. */ -/* ------------------------------------------------------------------ */ -/* Implementation notes for maintenance of this module: */ -/* */ -/* 1. Storage leak protection: Routines which use malloc are not */ -/* permitted to use return for fastpath or error exits (i.e., */ -/* they follow strict structured programming conventions). */ -/* Instead they have a do{}while(0); construct surrounding the */ -/* code which is protected -- break may be used to exit this. */ -/* Other routines can safely use the return statement inline. */ -/* */ -/* Storage leak accounting can be enabled using DECALLOC. */ -/* */ -/* 2. All loops use the for(;;) construct. Any do construct does */ -/* not loop; it is for allocation protection as just described. */ -/* */ -/* 3. Setting status in the context must always be the very last */ -/* action in a routine, as non-0 status may raise a trap and hence */ -/* the call to set status may not return (if the handler uses long */ -/* jump). Therefore all cleanup must be done first. In general, */ -/* to achieve this status is accumulated and is only applied just */ -/* before return by calling decContextSetStatus (via decStatus). */ -/* */ -/* Routines which allocate storage cannot, in general, use the */ -/* 'top level' routines which could cause a non-returning */ -/* transfer of control. The decXxxxOp routines are safe (do not */ -/* call decStatus even if traps are set in the context) and should */ -/* be used instead (they are also a little faster). */ -/* */ -/* 4. Exponent checking is minimized by allowing the exponent to */ -/* grow outside its limits during calculations, provided that */ -/* the decFinalize function is called later. Multiplication and */ -/* division, and intermediate calculations in exponentiation, */ -/* require more careful checks because of the risk of 31-bit */ -/* overflow (the most negative valid exponent is -1999999997, for */ -/* a 999999999-digit number with adjusted exponent of -999999999). */ -/* */ -/* 5. Rounding is deferred until finalization of results, with any */ -/* 'off to the right' data being represented as a single digit */ -/* residue (in the range -1 through 9). This avoids any double- */ -/* rounding when more than one shortening takes place (for */ -/* example, when a result is subnormal). */ -/* */ -/* 6. The digits count is allowed to rise to a multiple of DECDPUN */ -/* during many operations, so whole Units are handled and exact */ -/* accounting of digits is not needed. The correct digits value */ -/* is found by decGetDigits, which accounts for leading zeros. */ -/* This must be called before any rounding if the number of digits */ -/* is not known exactly. */ -/* */ -/* 7. The multiply-by-reciprocal 'trick' is used for partitioning */ -/* numbers up to four digits, using appropriate constants. This */ -/* is not useful for longer numbers because overflow of 32 bits */ -/* would lead to 4 multiplies, which is almost as expensive as */ -/* a divide (unless a floating-point or 64-bit multiply is */ -/* assumed to be available). */ -/* */ -/* 8. Unusual abbreviations that may be used in the commentary: */ -/* lhs -- left hand side (operand, of an operation) */ -/* lsd -- least significant digit (of coefficient) */ -/* lsu -- least significant Unit (of coefficient) */ -/* msd -- most significant digit (of coefficient) */ -/* msi -- most significant item (in an array) */ -/* msu -- most significant Unit (of coefficient) */ -/* rhs -- right hand side (operand, of an operation) */ -/* +ve -- positive */ -/* -ve -- negative */ -/* ** -- raise to the power */ -/* ------------------------------------------------------------------ */ - -#include /* for malloc, free, etc. */ -/* #include */ /* for printf [if needed] */ -#include /* for strcpy */ -#include /* for lower */ -#include "cmemory.h" /* for uprv_malloc, etc., in ICU */ -#include "decNumber.h" /* base number library */ -#include "decNumberLocal.h" /* decNumber local types, etc. */ -#include "uassert.h" - -/* Constants */ -/* Public lookup table used by the D2U macro */ -static const uByte d2utable[DECMAXD2U+1]=D2UTABLE; - -#define DECVERB 1 /* set to 1 for verbose DECCHECK */ -#define powers DECPOWERS /* old internal name */ - -/* Local constants */ -#define DIVIDE 0x80 /* Divide operators */ -#define REMAINDER 0x40 /* .. */ -#define DIVIDEINT 0x20 /* .. */ -#define REMNEAR 0x10 /* .. */ -#define COMPARE 0x01 /* Compare operators */ -#define COMPMAX 0x02 /* .. */ -#define COMPMIN 0x03 /* .. */ -#define COMPTOTAL 0x04 /* .. */ -#define COMPNAN 0x05 /* .. [NaN processing] */ -#define COMPSIG 0x06 /* .. [signaling COMPARE] */ -#define COMPMAXMAG 0x07 /* .. */ -#define COMPMINMAG 0x08 /* .. */ - -#define DEC_sNaN 0x40000000 /* local status: sNaN signal */ -#define BADINT (Int)0x80000000 /* most-negative Int; error indicator */ -/* Next two indicate an integer >= 10**6, and its parity (bottom bit) */ -#define BIGEVEN (Int)0x80000002 -#define BIGODD (Int)0x80000003 - -static const Unit uarrone[1]={1}; /* Unit array of 1, used for incrementing */ - -/* ------------------------------------------------------------------ */ -/* round-for-reround digits */ -/* ------------------------------------------------------------------ */ -#if 0 -static const uByte DECSTICKYTAB[10]={1,1,2,3,4,6,6,7,8,9}; /* used if sticky */ -#endif - -/* ------------------------------------------------------------------ */ -/* Powers of ten (powers[n]==10**n, 0<=n<=9) */ -/* ------------------------------------------------------------------ */ -static const uInt DECPOWERS[10]={1, 10, 100, 1000, 10000, 100000, 1000000, - 10000000, 100000000, 1000000000}; - - -/* Granularity-dependent code */ -#if DECDPUN<=4 - #define eInt Int /* extended integer */ - #define ueInt uInt /* unsigned extended integer */ - /* Constant multipliers for divide-by-power-of five using reciprocal */ - /* multiply, after removing powers of 2 by shifting, and final shift */ - /* of 17 [we only need up to **4] */ - static const uInt multies[]={131073, 26215, 5243, 1049, 210}; - /* QUOT10 -- macro to return the quotient of unit u divided by 10**n */ - #define QUOT10(u, n) ((((uInt)(u)>>(n))*multies[n])>>17) -#else - /* For DECDPUN>4 non-ANSI-89 64-bit types are needed. */ - #if !DECUSE64 - #error decNumber.c: DECUSE64 must be 1 when DECDPUN>4 - #endif - #define eInt Long /* extended integer */ - #define ueInt uLong /* unsigned extended integer */ -#endif - -/* Local routines */ -static decNumber * decAddOp(decNumber *, const decNumber *, const decNumber *, - decContext *, uByte, uInt *); -static Flag decBiStr(const char *, const char *, const char *); -static uInt decCheckMath(const decNumber *, decContext *, uInt *); -static void decApplyRound(decNumber *, decContext *, Int, uInt *); -static Int decCompare(const decNumber *lhs, const decNumber *rhs, Flag); -static decNumber * decCompareOp(decNumber *, const decNumber *, - const decNumber *, decContext *, - Flag, uInt *); -static void decCopyFit(decNumber *, const decNumber *, decContext *, - Int *, uInt *); -static decNumber * decDecap(decNumber *, Int); -static decNumber * decDivideOp(decNumber *, const decNumber *, - const decNumber *, decContext *, Flag, uInt *); -static decNumber * decExpOp(decNumber *, const decNumber *, - decContext *, uInt *); -static void decFinalize(decNumber *, decContext *, Int *, uInt *); -static Int decGetDigits(Unit *, Int); -static Int decGetInt(const decNumber *); -static decNumber * decLnOp(decNumber *, const decNumber *, - decContext *, uInt *); -static decNumber * decMultiplyOp(decNumber *, const decNumber *, - const decNumber *, decContext *, - uInt *); -static decNumber * decNaNs(decNumber *, const decNumber *, - const decNumber *, decContext *, uInt *); -static decNumber * decQuantizeOp(decNumber *, const decNumber *, - const decNumber *, decContext *, Flag, - uInt *); -static void decReverse(Unit *, Unit *); -static void decSetCoeff(decNumber *, decContext *, const Unit *, - Int, Int *, uInt *); -static void decSetMaxValue(decNumber *, decContext *); -static void decSetOverflow(decNumber *, decContext *, uInt *); -static void decSetSubnormal(decNumber *, decContext *, Int *, uInt *); -static Int decShiftToLeast(Unit *, Int, Int); -static Int decShiftToMost(Unit *, Int, Int); -static void decStatus(decNumber *, uInt, decContext *); -static void decToString(const decNumber *, char[], Flag); -static decNumber * decTrim(decNumber *, decContext *, Flag, Flag, Int *); -static Int decUnitAddSub(const Unit *, Int, const Unit *, Int, Int, - Unit *, Int); -static Int decUnitCompare(const Unit *, Int, const Unit *, Int, Int); - -#if !DECSUBSET -/* decFinish == decFinalize when no subset arithmetic needed */ -#define decFinish(a,b,c,d) decFinalize(a,b,c,d) -#else -static void decFinish(decNumber *, decContext *, Int *, uInt *); -static decNumber * decRoundOperand(const decNumber *, decContext *, uInt *); -#endif - -/* Local macros */ -/* masked special-values bits */ -#define SPECIALARG (rhs->bits & DECSPECIAL) -#define SPECIALARGS ((lhs->bits | rhs->bits) & DECSPECIAL) - -/* For use in ICU */ -#define malloc(a) uprv_malloc(a) -#define free(a) uprv_free(a) - -/* Diagnostic macros, etc. */ -#if DECALLOC -/* Handle malloc/free accounting. If enabled, our accountable routines */ -/* are used; otherwise the code just goes straight to the system malloc */ -/* and free routines. */ -#define malloc(a) decMalloc(a) -#define free(a) decFree(a) -#define DECFENCE 0x5a /* corruption detector */ -/* 'Our' malloc and free: */ -static void *decMalloc(size_t); -static void decFree(void *); -uInt decAllocBytes=0; /* count of bytes allocated */ -/* Note that DECALLOC code only checks for storage buffer overflow. */ -/* To check for memory leaks, the decAllocBytes variable must be */ -/* checked to be 0 at appropriate times (e.g., after the test */ -/* harness completes a set of tests). This checking may be unreliable */ -/* if the testing is done in a multi-thread environment. */ -#endif - -#if DECCHECK -/* Optional checking routines. Enabling these means that decNumber */ -/* and decContext operands to operator routines are checked for */ -/* correctness. This roughly doubles the execution time of the */ -/* fastest routines (and adds 600+ bytes), so should not normally be */ -/* used in 'production'. */ -/* decCheckInexact is used to check that inexact results have a full */ -/* complement of digits (where appropriate -- this is not the case */ -/* for Quantize, for example) */ -#define DECUNRESU ((decNumber *)(void *)0xffffffff) -#define DECUNUSED ((const decNumber *)(void *)0xffffffff) -#define DECUNCONT ((decContext *)(void *)(0xffffffff)) -static Flag decCheckOperands(decNumber *, const decNumber *, - const decNumber *, decContext *); -static Flag decCheckNumber(const decNumber *); -static void decCheckInexact(const decNumber *, decContext *); -#endif - -#if DECTRACE || DECCHECK -/* Optional trace/debugging routines (may or may not be used) */ -void decNumberShow(const decNumber *); /* displays the components of a number */ -static void decDumpAr(char, const Unit *, Int); -#endif - -/* ================================================================== */ -/* Conversions */ -/* ================================================================== */ - -/* ------------------------------------------------------------------ */ -/* from-int32 -- conversion from Int or uInt */ -/* */ -/* dn is the decNumber to receive the integer */ -/* in or uin is the integer to be converted */ -/* returns dn */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberFromInt32(decNumber *dn, Int in) { - uInt unsig; - if (in>=0) unsig=in; - else { /* negative (possibly BADINT) */ - if (in==BADINT) unsig=(uInt)1073741824*2; /* special case */ - else unsig=-in; /* invert */ - } - /* in is now positive */ - uprv_decNumberFromUInt32(dn, unsig); - if (in<0) dn->bits=DECNEG; /* sign needed */ - return dn; - } /* decNumberFromInt32 */ - -U_CAPI decNumber * U_EXPORT2 uprv_decNumberFromUInt32(decNumber *dn, uInt uin) { - Unit *up; /* work pointer */ - uprv_decNumberZero(dn); /* clean */ - if (uin==0) return dn; /* [or decGetDigits bad call] */ - for (up=dn->lsu; uin>0; up++) { - *up=(Unit)(uin%(DECDPUNMAX+1)); - uin=uin/(DECDPUNMAX+1); - } - dn->digits=decGetDigits(dn->lsu, static_cast(up - dn->lsu)); - return dn; - } /* decNumberFromUInt32 */ - -/* ------------------------------------------------------------------ */ -/* to-int32 -- conversion to Int or uInt */ -/* */ -/* dn is the decNumber to convert */ -/* set is the context for reporting errors */ -/* returns the converted decNumber, or 0 if Invalid is set */ -/* */ -/* Invalid is set if the decNumber does not have exponent==0 or if */ -/* it is a NaN, Infinite, or out-of-range. */ -/* ------------------------------------------------------------------ */ -U_CAPI Int U_EXPORT2 uprv_decNumberToInt32(const decNumber *dn, decContext *set) { - #if DECCHECK - if (decCheckOperands(DECUNRESU, DECUNUSED, dn, set)) return 0; - #endif - - /* special or too many digits, or bad exponent */ - if (dn->bits&DECSPECIAL || dn->digits>10 || dn->exponent!=0) ; /* bad */ - else { /* is a finite integer with 10 or fewer digits */ - Int d; /* work */ - const Unit *up; /* .. */ - uInt hi=0, lo; /* .. */ - up=dn->lsu; /* -> lsu */ - lo=*up; /* get 1 to 9 digits */ - #if DECDPUN>1 /* split to higher */ - hi=lo/10; - lo=lo%10; - #endif - up++; - /* collect remaining Units, if any, into hi */ - for (d=DECDPUN; ddigits; up++, d+=DECDPUN) hi+=*up*powers[d-1]; - /* now low has the lsd, hi the remainder */ - if (hi>214748364 || (hi==214748364 && lo>7)) { /* out of range? */ - /* most-negative is a reprieve */ - if (dn->bits&DECNEG && hi==214748364 && lo==8) return 0x80000000; - /* bad -- drop through */ - } - else { /* in-range always */ - Int i=X10(hi)+lo; - if (dn->bits&DECNEG) return -i; - return i; - } - } /* integer */ - uprv_decContextSetStatus(set, DEC_Invalid_operation); /* [may not return] */ - return 0; - } /* decNumberToInt32 */ - -U_CAPI uInt U_EXPORT2 uprv_decNumberToUInt32(const decNumber *dn, decContext *set) { - #if DECCHECK - if (decCheckOperands(DECUNRESU, DECUNUSED, dn, set)) return 0; - #endif - /* special or too many digits, or bad exponent, or negative (<0) */ - if (dn->bits&DECSPECIAL || dn->digits>10 || dn->exponent!=0 - || (dn->bits&DECNEG && !ISZERO(dn))); /* bad */ - else { /* is a finite integer with 10 or fewer digits */ - Int d; /* work */ - const Unit *up; /* .. */ - uInt hi=0, lo; /* .. */ - up=dn->lsu; /* -> lsu */ - lo=*up; /* get 1 to 9 digits */ - #if DECDPUN>1 /* split to higher */ - hi=lo/10; - lo=lo%10; - #endif - up++; - /* collect remaining Units, if any, into hi */ - for (d=DECDPUN; ddigits; up++, d+=DECDPUN) hi+=*up*powers[d-1]; - - /* now low has the lsd, hi the remainder */ - if (hi>429496729 || (hi==429496729 && lo>5)) ; /* no reprieve possible */ - else return X10(hi)+lo; - } /* integer */ - uprv_decContextSetStatus(set, DEC_Invalid_operation); /* [may not return] */ - return 0; - } /* decNumberToUInt32 */ - -/* ------------------------------------------------------------------ */ -/* to-scientific-string -- conversion to numeric string */ -/* to-engineering-string -- conversion to numeric string */ -/* */ -/* decNumberToString(dn, string); */ -/* decNumberToEngString(dn, string); */ -/* */ -/* dn is the decNumber to convert */ -/* string is the string where the result will be laid out */ -/* */ -/* string must be at least dn->digits+14 characters long */ -/* */ -/* No error is possible, and no status can be set. */ -/* ------------------------------------------------------------------ */ -U_CAPI char * U_EXPORT2 uprv_decNumberToString(const decNumber *dn, char *string){ - decToString(dn, string, 0); - return string; - } /* DecNumberToString */ - -U_CAPI char * U_EXPORT2 uprv_decNumberToEngString(const decNumber *dn, char *string){ - decToString(dn, string, 1); - return string; - } /* DecNumberToEngString */ - -/* ------------------------------------------------------------------ */ -/* to-number -- conversion from numeric string */ -/* */ -/* decNumberFromString -- convert string to decNumber */ -/* dn -- the number structure to fill */ -/* chars[] -- the string to convert ('\0' terminated) */ -/* set -- the context used for processing any error, */ -/* determining the maximum precision available */ -/* (set.digits), determining the maximum and minimum */ -/* exponent (set.emax and set.emin), determining if */ -/* extended values are allowed, and checking the */ -/* rounding mode if overflow occurs or rounding is */ -/* needed. */ -/* */ -/* The length of the coefficient and the size of the exponent are */ -/* checked by this routine, so the correct error (Underflow or */ -/* Overflow) can be reported or rounding applied, as necessary. */ -/* */ -/* If bad syntax is detected, the result will be a quiet NaN. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberFromString(decNumber *dn, const char chars[], - decContext *set) { - Int exponent=0; /* working exponent [assume 0] */ - uByte bits=0; /* working flags [assume +ve] */ - Unit *res; /* where result will be built */ - Unit resbuff[SD2U(DECBUFFER+9)];/* local buffer in case need temporary */ - /* [+9 allows for ln() constants] */ - Unit *allocres=NULL; /* -> allocated result, iff allocated */ - Int d=0; /* count of digits found in decimal part */ - const char *dotchar=NULL; /* where dot was found */ - const char *cfirst=chars; /* -> first character of decimal part */ - const char *last=NULL; /* -> last digit of decimal part */ - const char *c; /* work */ - Unit *up; /* .. */ - #if DECDPUN>1 - Int cut, out; /* .. */ - #endif - Int residue; /* rounding residue */ - uInt status=0; /* error code */ - - #if DECCHECK - if (decCheckOperands(DECUNRESU, DECUNUSED, DECUNUSED, set)) - return uprv_decNumberZero(dn); - #endif - - do { /* status & malloc protection */ - for (c=chars;; c++) { /* -> input character */ - if (*c>='0' && *c<='9') { /* test for Arabic digit */ - last=c; - d++; /* count of real digits */ - continue; /* still in decimal part */ - } - if (*c=='.' && dotchar==NULL) { /* first '.' */ - dotchar=c; /* record offset into decimal part */ - if (c==cfirst) cfirst++; /* first digit must follow */ - continue;} - if (c==chars) { /* first in string... */ - if (*c=='-') { /* valid - sign */ - cfirst++; - bits=DECNEG; - continue;} - if (*c=='+') { /* valid + sign */ - cfirst++; - continue;} - } - /* *c is not a digit, or a valid +, -, or '.' */ - break; - } /* c */ - - if (last==NULL) { /* no digits yet */ - status=DEC_Conversion_syntax;/* assume the worst */ - if (*c=='\0') break; /* and no more to come... */ - #if DECSUBSET - /* if subset then infinities and NaNs are not allowed */ - if (!set->extended) break; /* hopeless */ - #endif - /* Infinities and NaNs are possible, here */ - if (dotchar!=NULL) break; /* .. unless had a dot */ - uprv_decNumberZero(dn); /* be optimistic */ - if (decBiStr(c, "infinity", "INFINITY") - || decBiStr(c, "inf", "INF")) { - dn->bits=bits | DECINF; - status=0; /* is OK */ - break; /* all done */ - } - /* a NaN expected */ - /* 2003.09.10 NaNs are now permitted to have a sign */ - dn->bits=bits | DECNAN; /* assume simple NaN */ - if (*c=='s' || *c=='S') { /* looks like an sNaN */ - c++; - dn->bits=bits | DECSNAN; - } - if (*c!='n' && *c!='N') break; /* check caseless "NaN" */ - c++; - if (*c!='a' && *c!='A') break; /* .. */ - c++; - if (*c!='n' && *c!='N') break; /* .. */ - c++; - /* now either nothing, or nnnn payload, expected */ - /* -> start of integer and skip leading 0s [including plain 0] */ - for (cfirst=c; *cfirst=='0';) cfirst++; - if (*cfirst=='\0') { /* "NaN" or "sNaN", maybe with all 0s */ - status=0; /* it's good */ - break; /* .. */ - } - /* something other than 0s; setup last and d as usual [no dots] */ - for (c=cfirst;; c++, d++) { - if (*c<'0' || *c>'9') break; /* test for Arabic digit */ - last=c; - } - if (*c!='\0') break; /* not all digits */ - if (d>set->digits-1) { - /* [NB: payload in a decNumber can be full length unless */ - /* clamped, in which case can only be digits-1] */ - if (set->clamp) break; - if (d>set->digits) break; - } /* too many digits? */ - /* good; drop through to convert the integer to coefficient */ - status=0; /* syntax is OK */ - bits=dn->bits; /* for copy-back */ - } /* last==NULL */ - - else if (*c!='\0') { /* more to process... */ - /* had some digits; exponent is only valid sequence now */ - Flag nege; /* 1=negative exponent */ - const char *firstexp; /* -> first significant exponent digit */ - status=DEC_Conversion_syntax;/* assume the worst */ - if (*c!='e' && *c!='E') break; - /* Found 'e' or 'E' -- now process explicit exponent */ - /* 1998.07.11: sign no longer required */ - nege=0; - c++; /* to (possible) sign */ - if (*c=='-') {nege=1; c++;} - else if (*c=='+') c++; - if (*c=='\0') break; - - for (; *c=='0' && *(c+1)!='\0';) c++; /* strip insignificant zeros */ - firstexp=c; /* save exponent digit place */ - uInt uexponent = 0; /* Avoid undefined behavior on signed int overflow */ - for (; ;c++) { - if (*c<'0' || *c>'9') break; /* not a digit */ - uexponent=X10(uexponent)+(uInt)*c-(uInt)'0'; - } /* c */ - exponent = (Int)uexponent; - /* if not now on a '\0', *c must not be a digit */ - if (*c!='\0') break; - - /* (this next test must be after the syntax checks) */ - /* if it was too long the exponent may have wrapped, so check */ - /* carefully and set it to a certain overflow if wrap possible */ - if (c>=firstexp+9+1) { - if (c>firstexp+9+1 || *firstexp>'1') exponent=DECNUMMAXE*2; - /* [up to 1999999999 is OK, for example 1E-1000000998] */ - } - if (nege) exponent=-exponent; /* was negative */ - status=0; /* is OK */ - } /* stuff after digits */ - - /* Here when whole string has been inspected; syntax is good */ - /* cfirst->first digit (never dot), last->last digit (ditto) */ - - /* strip leading zeros/dot [leave final 0 if all 0's] */ - if (*cfirst=='0') { /* [cfirst has stepped over .] */ - for (c=cfirst; cextended) { - uprv_decNumberZero(dn); /* clean result */ - break; /* [could be return] */ - } - #endif - } /* at least one leading 0 */ - - /* Handle decimal point... */ - if (dotchar!=NULL && dotchar(last-dotchar); /* adjust exponent */ - /* [we can now ignore the .] */ - - /* OK, the digits string is good. Assemble in the decNumber, or in */ - /* a temporary units array if rounding is needed */ - if (d<=set->digits) res=dn->lsu; /* fits into supplied decNumber */ - else { /* rounding needed */ - Int needbytes=D2U(d)*sizeof(Unit);/* bytes needed */ - res=resbuff; /* assume use local buffer */ - if (needbytes>(Int)sizeof(resbuff)) { /* too big for local */ - allocres=(Unit *)malloc(needbytes); - if (allocres==NULL) {status|=DEC_Insufficient_storage; break;} - res=allocres; - } - } - /* res now -> number lsu, buffer, or allocated storage for Unit array */ - - /* Place the coefficient into the selected Unit array */ - /* [this is often 70% of the cost of this function when DECDPUN>1] */ - #if DECDPUN>1 - out=0; /* accumulator */ - up=res+D2U(d)-1; /* -> msu */ - cut=d-(up-res)*DECDPUN; /* digits in top unit */ - for (c=cfirst;; c++) { /* along the digits */ - if (*c=='.') continue; /* ignore '.' [don't decrement cut] */ - out=X10(out)+(Int)*c-(Int)'0'; - if (c==last) break; /* done [never get to trailing '.'] */ - cut--; - if (cut>0) continue; /* more for this unit */ - *up=(Unit)out; /* write unit */ - up--; /* prepare for unit below.. */ - cut=DECDPUN; /* .. */ - out=0; /* .. */ - } /* c */ - *up=(Unit)out; /* write lsu */ - - #else - /* DECDPUN==1 */ - up=res; /* -> lsu */ - for (c=last; c>=cfirst; c--) { /* over each character, from least */ - if (*c=='.') continue; /* ignore . [don't step up] */ - *up=(Unit)((Int)*c-(Int)'0'); - up++; - } /* c */ - #endif - - dn->bits=bits; - dn->exponent=exponent; - dn->digits=d; - - /* if not in number (too long) shorten into the number */ - if (d>set->digits) { - residue=0; - decSetCoeff(dn, set, res, d, &residue, &status); - /* always check for overflow or subnormal and round as needed */ - decFinalize(dn, set, &residue, &status); - } - else { /* no rounding, but may still have overflow or subnormal */ - /* [these tests are just for performance; finalize repeats them] */ - if ((dn->exponent-1emin-dn->digits) - || (dn->exponent-1>set->emax-set->digits)) { - residue=0; - decFinalize(dn, set, &residue, &status); - } - } - /* decNumberShow(dn); */ - } while(0); /* [for break] */ - - if (allocres!=NULL) free(allocres); /* drop any storage used */ - if (status!=0) decStatus(dn, status, set); - return dn; - } /* decNumberFromString */ - -/* ================================================================== */ -/* Operators */ -/* ================================================================== */ - -/* ------------------------------------------------------------------ */ -/* decNumberAbs -- absolute value operator */ -/* */ -/* This computes C = abs(A) */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* set is the context */ -/* */ -/* See also decNumberCopyAbs for a quiet bitwise version of this. */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -/* This has the same effect as decNumberPlus unless A is negative, */ -/* in which case it has the same effect as decNumberMinus. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberAbs(decNumber *res, const decNumber *rhs, - decContext *set) { - decNumber dzero; /* for 0 */ - uInt status=0; /* accumulator */ - - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - uprv_decNumberZero(&dzero); /* set 0 */ - dzero.exponent=rhs->exponent; /* [no coefficient expansion] */ - decAddOp(res, &dzero, rhs, set, (uByte)(rhs->bits & DECNEG), &status); - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberAbs */ - -/* ------------------------------------------------------------------ */ -/* decNumberAdd -- add two Numbers */ -/* */ -/* This computes C = A + B */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X+X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -/* This just calls the routine shared with Subtract */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberAdd(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decAddOp(res, lhs, rhs, set, 0, &status); - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberAdd */ - -/* ------------------------------------------------------------------ */ -/* decNumberAnd -- AND two Numbers, digitwise */ -/* */ -/* This computes C = A & B */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X&X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context (used for result length and error report) */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* Logical function restrictions apply (see above); a NaN is */ -/* returned with Invalid_operation if a restriction is violated. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberAnd(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - const Unit *ua, *ub; /* -> operands */ - const Unit *msua, *msub; /* -> operand msus */ - Unit *uc, *msuc; /* -> result and its msu */ - Int msudigs; /* digits in res msu */ - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - if (lhs->exponent!=0 || decNumberIsSpecial(lhs) || decNumberIsNegative(lhs) - || rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) { - decStatus(res, DEC_Invalid_operation, set); - return res; - } - - /* operands are valid */ - ua=lhs->lsu; /* bottom-up */ - ub=rhs->lsu; /* .. */ - uc=res->lsu; /* .. */ - msua=ua+D2U(lhs->digits)-1; /* -> msu of lhs */ - msub=ub+D2U(rhs->digits)-1; /* -> msu of rhs */ - msuc=uc+D2U(set->digits)-1; /* -> msu of result */ - msudigs=MSUDIGITS(set->digits); /* [faster than remainder] */ - for (; uc<=msuc; ua++, ub++, uc++) { /* Unit loop */ - Unit a, b; /* extract units */ - if (ua>msua) a=0; - else a=*ua; - if (ub>msub) b=0; - else b=*ub; - *uc=0; /* can now write back */ - if (a|b) { /* maybe 1 bits to examine */ - Int i, j; - *uc=0; /* can now write back */ - /* This loop could be unrolled and/or use BIN2BCD tables */ - for (i=0; i1) { - decStatus(res, DEC_Invalid_operation, set); - return res; - } - if (uc==msuc && i==msudigs-1) break; /* just did final digit */ - } /* each digit */ - } /* both OK */ - } /* each unit */ - /* [here uc-1 is the msu of the result] */ - res->digits=decGetDigits(res->lsu, static_cast(uc - res->lsu)); - res->exponent=0; /* integer */ - res->bits=0; /* sign=0 */ - return res; /* [no status to set] */ - } /* decNumberAnd */ - -/* ------------------------------------------------------------------ */ -/* decNumberCompare -- compare two Numbers */ -/* */ -/* This computes C = A ? B */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for one digit (or NaN). */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompare(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decCompareOp(res, lhs, rhs, set, COMPARE, &status); - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberCompare */ - -/* ------------------------------------------------------------------ */ -/* decNumberCompareSignal -- compare, signalling on all NaNs */ -/* */ -/* This computes C = A ? B */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for one digit (or NaN). */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompareSignal(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decCompareOp(res, lhs, rhs, set, COMPSIG, &status); - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberCompareSignal */ - -/* ------------------------------------------------------------------ */ -/* decNumberCompareTotal -- compare two Numbers, using total ordering */ -/* */ -/* This computes C = A ? B, under total ordering */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for one digit; the result will always be one of */ -/* -1, 0, or 1. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompareTotal(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decCompareOp(res, lhs, rhs, set, COMPTOTAL, &status); - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberCompareTotal */ - -/* ------------------------------------------------------------------ */ -/* decNumberCompareTotalMag -- compare, total ordering of magnitudes */ -/* */ -/* This computes C = |A| ? |B|, under total ordering */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for one digit; the result will always be one of */ -/* -1, 0, or 1. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompareTotalMag(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - uInt needbytes; /* for space calculations */ - decNumber bufa[D2N(DECBUFFER+1)];/* +1 in case DECBUFFER=0 */ - decNumber *allocbufa=NULL; /* -> allocated bufa, iff allocated */ - decNumber bufb[D2N(DECBUFFER+1)]; - decNumber *allocbufb=NULL; /* -> allocated bufb, iff allocated */ - decNumber *a, *b; /* temporary pointers */ - - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - do { /* protect allocated storage */ - /* if either is negative, take a copy and absolute */ - if (decNumberIsNegative(lhs)) { /* lhs<0 */ - a=bufa; - needbytes=sizeof(decNumber)+(D2U(lhs->digits)-1)*sizeof(Unit); - if (needbytes>sizeof(bufa)) { /* need malloc space */ - allocbufa=(decNumber *)malloc(needbytes); - if (allocbufa==NULL) { /* hopeless -- abandon */ - status|=DEC_Insufficient_storage; - break;} - a=allocbufa; /* use the allocated space */ - } - uprv_decNumberCopy(a, lhs); /* copy content */ - a->bits&=~DECNEG; /* .. and clear the sign */ - lhs=a; /* use copy from here on */ - } - if (decNumberIsNegative(rhs)) { /* rhs<0 */ - b=bufb; - needbytes=sizeof(decNumber)+(D2U(rhs->digits)-1)*sizeof(Unit); - if (needbytes>sizeof(bufb)) { /* need malloc space */ - allocbufb=(decNumber *)malloc(needbytes); - if (allocbufb==NULL) { /* hopeless -- abandon */ - status|=DEC_Insufficient_storage; - break;} - b=allocbufb; /* use the allocated space */ - } - uprv_decNumberCopy(b, rhs); /* copy content */ - b->bits&=~DECNEG; /* .. and clear the sign */ - rhs=b; /* use copy from here on */ - } - decCompareOp(res, lhs, rhs, set, COMPTOTAL, &status); - } while(0); /* end protected */ - - if (allocbufa!=NULL) free(allocbufa); /* drop any storage used */ - if (allocbufb!=NULL) free(allocbufb); /* .. */ - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberCompareTotalMag */ - -/* ------------------------------------------------------------------ */ -/* decNumberDivide -- divide one number by another */ -/* */ -/* This computes C = A / B */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X/X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberDivide(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decDivideOp(res, lhs, rhs, set, DIVIDE, &status); - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberDivide */ - -/* ------------------------------------------------------------------ */ -/* decNumberDivideInteger -- divide and return integer quotient */ -/* */ -/* This computes C = A # B, where # is the integer divide operator */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X#X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberDivideInteger(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decDivideOp(res, lhs, rhs, set, DIVIDEINT, &status); - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberDivideInteger */ - -/* ------------------------------------------------------------------ */ -/* decNumberExp -- exponentiation */ -/* */ -/* This computes C = exp(A) */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* set is the context; note that rounding mode has no effect */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* Mathematical function restrictions apply (see above); a NaN is */ -/* returned with Invalid_operation if a restriction is violated. */ -/* */ -/* Finite results will always be full precision and Inexact, except */ -/* when A is a zero or -Infinity (giving 1 or 0 respectively). */ -/* */ -/* An Inexact result is rounded using DEC_ROUND_HALF_EVEN; it will */ -/* almost always be correctly rounded, but may be up to 1 ulp in */ -/* error in rare cases. */ -/* ------------------------------------------------------------------ */ -/* This is a wrapper for decExpOp which can handle the slightly wider */ -/* (double) range needed by Ln (which has to be able to calculate */ -/* exp(-a) where a can be the tiniest number (Ntiny). */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberExp(decNumber *res, const decNumber *rhs, - decContext *set) { - uInt status=0; /* accumulator */ - #if DECSUBSET - decNumber *allocrhs=NULL; /* non-NULL if rounded rhs allocated */ - #endif - - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - /* Check restrictions; these restrictions ensure that if h=8 (see */ - /* decExpOp) then the result will either overflow or underflow to 0. */ - /* Other math functions restrict the input range, too, for inverses. */ - /* If not violated then carry out the operation. */ - if (!decCheckMath(rhs, set, &status)) do { /* protect allocation */ - #if DECSUBSET - if (!set->extended) { - /* reduce operand and set lostDigits status, as needed */ - if (rhs->digits>set->digits) { - allocrhs=decRoundOperand(rhs, set, &status); - if (allocrhs==NULL) break; - rhs=allocrhs; - } - } - #endif - decExpOp(res, rhs, set, &status); - } while(0); /* end protected */ - - #if DECSUBSET - if (allocrhs !=NULL) free(allocrhs); /* drop any storage used */ - #endif - /* apply significant status */ - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberExp */ - -/* ------------------------------------------------------------------ */ -/* decNumberFMA -- fused multiply add */ -/* */ -/* This computes D = (A * B) + C with only one rounding */ -/* */ -/* res is D, the result. D may be A or B or C (e.g., X=FMA(X,X,X)) */ -/* lhs is A */ -/* rhs is B */ -/* fhs is C [far hand side] */ -/* set is the context */ -/* */ -/* Mathematical function restrictions apply (see above); a NaN is */ -/* returned with Invalid_operation if a restriction is violated. */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberFMA(decNumber *res, const decNumber *lhs, - const decNumber *rhs, const decNumber *fhs, - decContext *set) { - uInt status=0; /* accumulator */ - decContext dcmul; /* context for the multiplication */ - uInt needbytes; /* for space calculations */ - decNumber bufa[D2N(DECBUFFER*2+1)]; - decNumber *allocbufa=NULL; /* -> allocated bufa, iff allocated */ - decNumber *acc; /* accumulator pointer */ - decNumber dzero; /* work */ - - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - if (decCheckOperands(res, fhs, DECUNUSED, set)) return res; - #endif - - do { /* protect allocated storage */ - #if DECSUBSET - if (!set->extended) { /* [undefined if subset] */ - status|=DEC_Invalid_operation; - break;} - #endif - /* Check math restrictions [these ensure no overflow or underflow] */ - if ((!decNumberIsSpecial(lhs) && decCheckMath(lhs, set, &status)) - || (!decNumberIsSpecial(rhs) && decCheckMath(rhs, set, &status)) - || (!decNumberIsSpecial(fhs) && decCheckMath(fhs, set, &status))) break; - /* set up context for multiply */ - dcmul=*set; - dcmul.digits=lhs->digits+rhs->digits; /* just enough */ - /* [The above may be an over-estimate for subset arithmetic, but that's OK] */ - dcmul.emax=DEC_MAX_EMAX; /* effectively unbounded .. */ - dcmul.emin=DEC_MIN_EMIN; /* [thanks to Math restrictions] */ - /* set up decNumber space to receive the result of the multiply */ - acc=bufa; /* may fit */ - needbytes=sizeof(decNumber)+(D2U(dcmul.digits)-1)*sizeof(Unit); - if (needbytes>sizeof(bufa)) { /* need malloc space */ - allocbufa=(decNumber *)malloc(needbytes); - if (allocbufa==NULL) { /* hopeless -- abandon */ - status|=DEC_Insufficient_storage; - break;} - acc=allocbufa; /* use the allocated space */ - } - /* multiply with extended range and necessary precision */ - /*printf("emin=%ld\n", dcmul.emin); */ - decMultiplyOp(acc, lhs, rhs, &dcmul, &status); - /* Only Invalid operation (from sNaN or Inf * 0) is possible in */ - /* status; if either is seen than ignore fhs (in case it is */ - /* another sNaN) and set acc to NaN unless we had an sNaN */ - /* [decMultiplyOp leaves that to caller] */ - /* Note sNaN has to go through addOp to shorten payload if */ - /* necessary */ - if ((status&DEC_Invalid_operation)!=0) { - if (!(status&DEC_sNaN)) { /* but be true invalid */ - uprv_decNumberZero(res); /* acc not yet set */ - res->bits=DECNAN; - break; - } - uprv_decNumberZero(&dzero); /* make 0 (any non-NaN would do) */ - fhs=&dzero; /* use that */ - } - #if DECCHECK - else { /* multiply was OK */ - if (status!=0) printf("Status=%08lx after FMA multiply\n", (LI)status); - } - #endif - /* add the third operand and result -> res, and all is done */ - decAddOp(res, acc, fhs, set, 0, &status); - } while(0); /* end protected */ - - if (allocbufa!=NULL) free(allocbufa); /* drop any storage used */ - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberFMA */ - -/* ------------------------------------------------------------------ */ -/* decNumberInvert -- invert a Number, digitwise */ -/* */ -/* This computes C = ~A */ -/* */ -/* res is C, the result. C may be A (e.g., X=~X) */ -/* rhs is A */ -/* set is the context (used for result length and error report) */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* Logical function restrictions apply (see above); a NaN is */ -/* returned with Invalid_operation if a restriction is violated. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberInvert(decNumber *res, const decNumber *rhs, - decContext *set) { - const Unit *ua, *msua; /* -> operand and its msu */ - Unit *uc, *msuc; /* -> result and its msu */ - Int msudigs; /* digits in res msu */ - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - if (rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) { - decStatus(res, DEC_Invalid_operation, set); - return res; - } - /* operand is valid */ - ua=rhs->lsu; /* bottom-up */ - uc=res->lsu; /* .. */ - msua=ua+D2U(rhs->digits)-1; /* -> msu of rhs */ - msuc=uc+D2U(set->digits)-1; /* -> msu of result */ - msudigs=MSUDIGITS(set->digits); /* [faster than remainder] */ - for (; uc<=msuc; ua++, uc++) { /* Unit loop */ - Unit a; /* extract unit */ - Int i, j; /* work */ - if (ua>msua) a=0; - else a=*ua; - *uc=0; /* can now write back */ - /* always need to examine all bits in rhs */ - /* This loop could be unrolled and/or use BIN2BCD tables */ - for (i=0; i1) { - decStatus(res, DEC_Invalid_operation, set); - return res; - } - if (uc==msuc && i==msudigs-1) break; /* just did final digit */ - } /* each digit */ - } /* each unit */ - /* [here uc-1 is the msu of the result] */ - res->digits=decGetDigits(res->lsu, static_cast(uc - res->lsu)); - res->exponent=0; /* integer */ - res->bits=0; /* sign=0 */ - return res; /* [no status to set] */ - } /* decNumberInvert */ - -/* ------------------------------------------------------------------ */ -/* decNumberLn -- natural logarithm */ -/* */ -/* This computes C = ln(A) */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* set is the context; note that rounding mode has no effect */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* Notable cases: */ -/* A<0 -> Invalid */ -/* A=0 -> -Infinity (Exact) */ -/* A=+Infinity -> +Infinity (Exact) */ -/* A=1 exactly -> 0 (Exact) */ -/* */ -/* Mathematical function restrictions apply (see above); a NaN is */ -/* returned with Invalid_operation if a restriction is violated. */ -/* */ -/* An Inexact result is rounded using DEC_ROUND_HALF_EVEN; it will */ -/* almost always be correctly rounded, but may be up to 1 ulp in */ -/* error in rare cases. */ -/* ------------------------------------------------------------------ */ -/* This is a wrapper for decLnOp which can handle the slightly wider */ -/* (+11) range needed by Ln, Log10, etc. (which may have to be able */ -/* to calculate at p+e+2). */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberLn(decNumber *res, const decNumber *rhs, - decContext *set) { - uInt status=0; /* accumulator */ - #if DECSUBSET - decNumber *allocrhs=NULL; /* non-NULL if rounded rhs allocated */ - #endif - - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - /* Check restrictions; this is a math function; if not violated */ - /* then carry out the operation. */ - if (!decCheckMath(rhs, set, &status)) do { /* protect allocation */ - #if DECSUBSET - if (!set->extended) { - /* reduce operand and set lostDigits status, as needed */ - if (rhs->digits>set->digits) { - allocrhs=decRoundOperand(rhs, set, &status); - if (allocrhs==NULL) break; - rhs=allocrhs; - } - /* special check in subset for rhs=0 */ - if (ISZERO(rhs)) { /* +/- zeros -> error */ - status|=DEC_Invalid_operation; - break;} - } /* extended=0 */ - #endif - decLnOp(res, rhs, set, &status); - } while(0); /* end protected */ - - #if DECSUBSET - if (allocrhs !=NULL) free(allocrhs); /* drop any storage used */ - #endif - /* apply significant status */ - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberLn */ - -/* ------------------------------------------------------------------ */ -/* decNumberLogB - get adjusted exponent, by 754 rules */ -/* */ -/* This computes C = adjustedexponent(A) */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* set is the context, used only for digits and status */ -/* */ -/* C must have space for 10 digits (A might have 10**9 digits and */ -/* an exponent of +999999999, or one digit and an exponent of */ -/* -1999999999). */ -/* */ -/* This returns the adjusted exponent of A after (in theory) padding */ -/* with zeros on the right to set->digits digits while keeping the */ -/* same value. The exponent is not limited by emin/emax. */ -/* */ -/* Notable cases: */ -/* A<0 -> Use |A| */ -/* A=0 -> -Infinity (Division by zero) */ -/* A=Infinite -> +Infinity (Exact) */ -/* A=1 exactly -> 0 (Exact) */ -/* NaNs are propagated as usual */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberLogB(decNumber *res, const decNumber *rhs, - decContext *set) { - uInt status=0; /* accumulator */ - - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - /* NaNs as usual; Infinities return +Infinity; 0->oops */ - if (decNumberIsNaN(rhs)) decNaNs(res, rhs, NULL, set, &status); - else if (decNumberIsInfinite(rhs)) uprv_decNumberCopyAbs(res, rhs); - else if (decNumberIsZero(rhs)) { - uprv_decNumberZero(res); /* prepare for Infinity */ - res->bits=DECNEG|DECINF; /* -Infinity */ - status|=DEC_Division_by_zero; /* as per 754 */ - } - else { /* finite non-zero */ - Int ae=rhs->exponent+rhs->digits-1; /* adjusted exponent */ - uprv_decNumberFromInt32(res, ae); /* lay it out */ - } - - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberLogB */ - -/* ------------------------------------------------------------------ */ -/* decNumberLog10 -- logarithm in base 10 */ -/* */ -/* This computes C = log10(A) */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* set is the context; note that rounding mode has no effect */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* Notable cases: */ -/* A<0 -> Invalid */ -/* A=0 -> -Infinity (Exact) */ -/* A=+Infinity -> +Infinity (Exact) */ -/* A=10**n (if n is an integer) -> n (Exact) */ -/* */ -/* Mathematical function restrictions apply (see above); a NaN is */ -/* returned with Invalid_operation if a restriction is violated. */ -/* */ -/* An Inexact result is rounded using DEC_ROUND_HALF_EVEN; it will */ -/* almost always be correctly rounded, but may be up to 1 ulp in */ -/* error in rare cases. */ -/* ------------------------------------------------------------------ */ -/* This calculates ln(A)/ln(10) using appropriate precision. For */ -/* ln(A) this is the max(p, rhs->digits + t) + 3, where p is the */ -/* requested digits and t is the number of digits in the exponent */ -/* (maximum 6). For ln(10) it is p + 3; this is often handled by the */ -/* fastpath in decLnOp. The final division is done to the requested */ -/* precision. */ -/* ------------------------------------------------------------------ */ -#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 406 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" -#endif -U_CAPI decNumber * U_EXPORT2 uprv_decNumberLog10(decNumber *res, const decNumber *rhs, - decContext *set) { - uInt status=0, ignore=0; /* status accumulators */ - uInt needbytes; /* for space calculations */ - Int p; /* working precision */ - Int t; /* digits in exponent of A */ - - /* buffers for a and b working decimals */ - /* (adjustment calculator, same size) */ - decNumber bufa[D2N(DECBUFFER+2)]; - decNumber *allocbufa=NULL; /* -> allocated bufa, iff allocated */ - decNumber *a=bufa; /* temporary a */ - decNumber bufb[D2N(DECBUFFER+2)]; - decNumber *allocbufb=NULL; /* -> allocated bufb, iff allocated */ - decNumber *b=bufb; /* temporary b */ - decNumber bufw[D2N(10)]; /* working 2-10 digit number */ - decNumber *w=bufw; /* .. */ - #if DECSUBSET - decNumber *allocrhs=NULL; /* non-NULL if rounded rhs allocated */ - #endif - - decContext aset; /* working context */ - - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - /* Check restrictions; this is a math function; if not violated */ - /* then carry out the operation. */ - if (!decCheckMath(rhs, set, &status)) do { /* protect malloc */ - #if DECSUBSET - if (!set->extended) { - /* reduce operand and set lostDigits status, as needed */ - if (rhs->digits>set->digits) { - allocrhs=decRoundOperand(rhs, set, &status); - if (allocrhs==NULL) break; - rhs=allocrhs; - } - /* special check in subset for rhs=0 */ - if (ISZERO(rhs)) { /* +/- zeros -> error */ - status|=DEC_Invalid_operation; - break;} - } /* extended=0 */ - #endif - - uprv_decContextDefault(&aset, DEC_INIT_DECIMAL64); /* clean context */ - - /* handle exact powers of 10; only check if +ve finite */ - if (!(rhs->bits&(DECNEG|DECSPECIAL)) && !ISZERO(rhs)) { - Int residue=0; /* (no residue) */ - uInt copystat=0; /* clean status */ - - /* round to a single digit... */ - aset.digits=1; - decCopyFit(w, rhs, &aset, &residue, ©stat); /* copy & shorten */ - /* if exact and the digit is 1, rhs is a power of 10 */ - if (!(copystat&DEC_Inexact) && w->lsu[0]==1) { - /* the exponent, conveniently, is the power of 10; making */ - /* this the result needs a little care as it might not fit, */ - /* so first convert it into the working number, and then move */ - /* to res */ - uprv_decNumberFromInt32(w, w->exponent); - residue=0; - decCopyFit(res, w, set, &residue, &status); /* copy & round */ - decFinish(res, set, &residue, &status); /* cleanup/set flags */ - break; - } /* not a power of 10 */ - } /* not a candidate for exact */ - - /* simplify the information-content calculation to use 'total */ - /* number of digits in a, including exponent' as compared to the */ - /* requested digits, as increasing this will only rarely cost an */ - /* iteration in ln(a) anyway */ - t=6; /* it can never be >6 */ - - /* allocate space when needed... */ - p=(rhs->digits+t>set->digits?rhs->digits+t:set->digits)+3; - needbytes=sizeof(decNumber)+(D2U(p)-1)*sizeof(Unit); - if (needbytes>sizeof(bufa)) { /* need malloc space */ - allocbufa=(decNumber *)malloc(needbytes); - if (allocbufa==NULL) { /* hopeless -- abandon */ - status|=DEC_Insufficient_storage; - break;} - a=allocbufa; /* use the allocated space */ - } - aset.digits=p; /* as calculated */ - aset.emax=DEC_MAX_MATH; /* usual bounds */ - aset.emin=-DEC_MAX_MATH; /* .. */ - aset.clamp=0; /* and no concrete format */ - decLnOp(a, rhs, &aset, &status); /* a=ln(rhs) */ - - /* skip the division if the result so far is infinite, NaN, or */ - /* zero, or there was an error; note NaN from sNaN needs copy */ - if (status&DEC_NaNs && !(status&DEC_sNaN)) break; - if (a->bits&DECSPECIAL || ISZERO(a)) { - uprv_decNumberCopy(res, a); /* [will fit] */ - break;} - - /* for ln(10) an extra 3 digits of precision are needed */ - p=set->digits+3; - needbytes=sizeof(decNumber)+(D2U(p)-1)*sizeof(Unit); - if (needbytes>sizeof(bufb)) { /* need malloc space */ - allocbufb=(decNumber *)malloc(needbytes); - if (allocbufb==NULL) { /* hopeless -- abandon */ - status|=DEC_Insufficient_storage; - break;} - b=allocbufb; /* use the allocated space */ - } - uprv_decNumberZero(w); /* set up 10... */ - #if DECDPUN==1 - w->lsu[1]=1; w->lsu[0]=0; /* .. */ - #else - w->lsu[0]=10; /* .. */ - #endif - w->digits=2; /* .. */ - - aset.digits=p; - decLnOp(b, w, &aset, &ignore); /* b=ln(10) */ - - aset.digits=set->digits; /* for final divide */ - decDivideOp(res, a, b, &aset, DIVIDE, &status); /* into result */ - } while(0); /* [for break] */ - - if (allocbufa!=NULL) free(allocbufa); /* drop any storage used */ - if (allocbufb!=NULL) free(allocbufb); /* .. */ - #if DECSUBSET - if (allocrhs !=NULL) free(allocrhs); /* .. */ - #endif - /* apply significant status */ - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberLog10 */ -#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 406 -#pragma GCC diagnostic pop -#endif - -/* ------------------------------------------------------------------ */ -/* decNumberMax -- compare two Numbers and return the maximum */ -/* */ -/* This computes C = A ? B, returning the maximum by 754 rules */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberMax(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decCompareOp(res, lhs, rhs, set, COMPMAX, &status); - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberMax */ - -/* ------------------------------------------------------------------ */ -/* decNumberMaxMag -- compare and return the maximum by magnitude */ -/* */ -/* This computes C = A ? B, returning the maximum by 754 rules */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberMaxMag(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decCompareOp(res, lhs, rhs, set, COMPMAXMAG, &status); - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberMaxMag */ - -/* ------------------------------------------------------------------ */ -/* decNumberMin -- compare two Numbers and return the minimum */ -/* */ -/* This computes C = A ? B, returning the minimum by 754 rules */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberMin(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decCompareOp(res, lhs, rhs, set, COMPMIN, &status); - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberMin */ - -/* ------------------------------------------------------------------ */ -/* decNumberMinMag -- compare and return the minimum by magnitude */ -/* */ -/* This computes C = A ? B, returning the minimum by 754 rules */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberMinMag(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decCompareOp(res, lhs, rhs, set, COMPMINMAG, &status); - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberMinMag */ - -/* ------------------------------------------------------------------ */ -/* decNumberMinus -- prefix minus operator */ -/* */ -/* This computes C = 0 - A */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* set is the context */ -/* */ -/* See also decNumberCopyNegate for a quiet bitwise version of this. */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -/* Simply use AddOp for the subtract, which will do the necessary. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberMinus(decNumber *res, const decNumber *rhs, - decContext *set) { - decNumber dzero; - uInt status=0; /* accumulator */ - - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - uprv_decNumberZero(&dzero); /* make 0 */ - dzero.exponent=rhs->exponent; /* [no coefficient expansion] */ - decAddOp(res, &dzero, rhs, set, DECNEG, &status); - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberMinus */ - -/* ------------------------------------------------------------------ */ -/* decNumberNextMinus -- next towards -Infinity */ -/* */ -/* This computes C = A - infinitesimal, rounded towards -Infinity */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* set is the context */ -/* */ -/* This is a generalization of 754 NextDown. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberNextMinus(decNumber *res, const decNumber *rhs, - decContext *set) { - decNumber dtiny; /* constant */ - decContext workset=*set; /* work */ - uInt status=0; /* accumulator */ - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - /* +Infinity is the special case */ - if ((rhs->bits&(DECINF|DECNEG))==DECINF) { - decSetMaxValue(res, set); /* is +ve */ - /* there is no status to set */ - return res; - } - uprv_decNumberZero(&dtiny); /* start with 0 */ - dtiny.lsu[0]=1; /* make number that is .. */ - dtiny.exponent=DEC_MIN_EMIN-1; /* .. smaller than tiniest */ - workset.round=DEC_ROUND_FLOOR; - decAddOp(res, rhs, &dtiny, &workset, DECNEG, &status); - status&=DEC_Invalid_operation|DEC_sNaN; /* only sNaN Invalid please */ - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberNextMinus */ - -/* ------------------------------------------------------------------ */ -/* decNumberNextPlus -- next towards +Infinity */ -/* */ -/* This computes C = A + infinitesimal, rounded towards +Infinity */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* set is the context */ -/* */ -/* This is a generalization of 754 NextUp. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberNextPlus(decNumber *res, const decNumber *rhs, - decContext *set) { - decNumber dtiny; /* constant */ - decContext workset=*set; /* work */ - uInt status=0; /* accumulator */ - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - /* -Infinity is the special case */ - if ((rhs->bits&(DECINF|DECNEG))==(DECINF|DECNEG)) { - decSetMaxValue(res, set); - res->bits=DECNEG; /* negative */ - /* there is no status to set */ - return res; - } - uprv_decNumberZero(&dtiny); /* start with 0 */ - dtiny.lsu[0]=1; /* make number that is .. */ - dtiny.exponent=DEC_MIN_EMIN-1; /* .. smaller than tiniest */ - workset.round=DEC_ROUND_CEILING; - decAddOp(res, rhs, &dtiny, &workset, 0, &status); - status&=DEC_Invalid_operation|DEC_sNaN; /* only sNaN Invalid please */ - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberNextPlus */ - -/* ------------------------------------------------------------------ */ -/* decNumberNextToward -- next towards rhs */ -/* */ -/* This computes C = A +/- infinitesimal, rounded towards */ -/* +/-Infinity in the direction of B, as per 754-1985 nextafter */ -/* modified during revision but dropped from 754-2008. */ -/* */ -/* res is C, the result. C may be A or B. */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* This is a generalization of 754-1985 NextAfter. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberNextToward(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - decNumber dtiny; /* constant */ - decContext workset=*set; /* work */ - Int result; /* .. */ - uInt status=0; /* accumulator */ - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) { - decNaNs(res, lhs, rhs, set, &status); - } - else { /* Is numeric, so no chance of sNaN Invalid, etc. */ - result=decCompare(lhs, rhs, 0); /* sign matters */ - if (result==BADINT) status|=DEC_Insufficient_storage; /* rare */ - else { /* valid compare */ - if (result==0) uprv_decNumberCopySign(res, lhs, rhs); /* easy */ - else { /* differ: need NextPlus or NextMinus */ - uByte sub; /* add or subtract */ - if (result<0) { /* lhsbits&(DECINF|DECNEG))==(DECINF|DECNEG)) { - decSetMaxValue(res, set); - res->bits=DECNEG; /* negative */ - return res; /* there is no status to set */ - } - workset.round=DEC_ROUND_CEILING; - sub=0; /* add, please */ - } /* plus */ - else { /* lhs>rhs, do nextminus */ - /* +Infinity is the special case */ - if ((lhs->bits&(DECINF|DECNEG))==DECINF) { - decSetMaxValue(res, set); - return res; /* there is no status to set */ - } - workset.round=DEC_ROUND_FLOOR; - sub=DECNEG; /* subtract, please */ - } /* minus */ - uprv_decNumberZero(&dtiny); /* start with 0 */ - dtiny.lsu[0]=1; /* make number that is .. */ - dtiny.exponent=DEC_MIN_EMIN-1; /* .. smaller than tiniest */ - decAddOp(res, lhs, &dtiny, &workset, sub, &status); /* + or - */ - /* turn off exceptions if the result is a normal number */ - /* (including Nmin), otherwise let all status through */ - if (uprv_decNumberIsNormal(res, set)) status=0; - } /* unequal */ - } /* compare OK */ - } /* numeric */ - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberNextToward */ - -/* ------------------------------------------------------------------ */ -/* decNumberOr -- OR two Numbers, digitwise */ -/* */ -/* This computes C = A | B */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X|X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context (used for result length and error report) */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* Logical function restrictions apply (see above); a NaN is */ -/* returned with Invalid_operation if a restriction is violated. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberOr(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - const Unit *ua, *ub; /* -> operands */ - const Unit *msua, *msub; /* -> operand msus */ - Unit *uc, *msuc; /* -> result and its msu */ - Int msudigs; /* digits in res msu */ - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - if (lhs->exponent!=0 || decNumberIsSpecial(lhs) || decNumberIsNegative(lhs) - || rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) { - decStatus(res, DEC_Invalid_operation, set); - return res; - } - /* operands are valid */ - ua=lhs->lsu; /* bottom-up */ - ub=rhs->lsu; /* .. */ - uc=res->lsu; /* .. */ - msua=ua+D2U(lhs->digits)-1; /* -> msu of lhs */ - msub=ub+D2U(rhs->digits)-1; /* -> msu of rhs */ - msuc=uc+D2U(set->digits)-1; /* -> msu of result */ - msudigs=MSUDIGITS(set->digits); /* [faster than remainder] */ - for (; uc<=msuc; ua++, ub++, uc++) { /* Unit loop */ - Unit a, b; /* extract units */ - if (ua>msua) a=0; - else a=*ua; - if (ub>msub) b=0; - else b=*ub; - *uc=0; /* can now write back */ - if (a|b) { /* maybe 1 bits to examine */ - Int i, j; - /* This loop could be unrolled and/or use BIN2BCD tables */ - for (i=0; i1) { - decStatus(res, DEC_Invalid_operation, set); - return res; - } - if (uc==msuc && i==msudigs-1) break; /* just did final digit */ - } /* each digit */ - } /* non-zero */ - } /* each unit */ - /* [here uc-1 is the msu of the result] */ - res->digits=decGetDigits(res->lsu, static_cast(uc-res->lsu)); - res->exponent=0; /* integer */ - res->bits=0; /* sign=0 */ - return res; /* [no status to set] */ - } /* decNumberOr */ - -/* ------------------------------------------------------------------ */ -/* decNumberPlus -- prefix plus operator */ -/* */ -/* This computes C = 0 + A */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* set is the context */ -/* */ -/* See also decNumberCopy for a quiet bitwise version of this. */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -/* This simply uses AddOp; Add will take fast path after preparing A. */ -/* Performance is a concern here, as this routine is often used to */ -/* check operands and apply rounding and overflow/underflow testing. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberPlus(decNumber *res, const decNumber *rhs, - decContext *set) { - decNumber dzero; - uInt status=0; /* accumulator */ - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - uprv_decNumberZero(&dzero); /* make 0 */ - dzero.exponent=rhs->exponent; /* [no coefficient expansion] */ - decAddOp(res, &dzero, rhs, set, 0, &status); - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberPlus */ - -/* ------------------------------------------------------------------ */ -/* decNumberMultiply -- multiply two Numbers */ -/* */ -/* This computes C = A x B */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X+X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberMultiply(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decMultiplyOp(res, lhs, rhs, set, &status); - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberMultiply */ - -/* ------------------------------------------------------------------ */ -/* decNumberPower -- raise a number to a power */ -/* */ -/* This computes C = A ** B */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X**X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* Mathematical function restrictions apply (see above); a NaN is */ -/* returned with Invalid_operation if a restriction is violated. */ -/* */ -/* However, if 1999999997<=B<=999999999 and B is an integer then the */ -/* restrictions on A and the context are relaxed to the usual bounds, */ -/* for compatibility with the earlier (integer power only) version */ -/* of this function. */ -/* */ -/* When B is an integer, the result may be exact, even if rounded. */ -/* */ -/* The final result is rounded according to the context; it will */ -/* almost always be correctly rounded, but may be up to 1 ulp in */ -/* error in rare cases. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberPower(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - #if DECSUBSET - decNumber *alloclhs=NULL; /* non-NULL if rounded lhs allocated */ - decNumber *allocrhs=NULL; /* .., rhs */ - #endif - decNumber *allocdac=NULL; /* -> allocated acc buffer, iff used */ - decNumber *allocinv=NULL; /* -> allocated 1/x buffer, iff used */ - Int reqdigits=set->digits; /* requested DIGITS */ - Int n; /* rhs in binary */ - Flag rhsint=0; /* 1 if rhs is an integer */ - Flag useint=0; /* 1 if can use integer calculation */ - Flag isoddint=0; /* 1 if rhs is an integer and odd */ - Int i; /* work */ - #if DECSUBSET - Int dropped; /* .. */ - #endif - uInt needbytes; /* buffer size needed */ - Flag seenbit; /* seen a bit while powering */ - Int residue=0; /* rounding residue */ - uInt status=0; /* accumulators */ - uByte bits=0; /* result sign if errors */ - decContext aset; /* working context */ - decNumber dnOne; /* work value 1... */ - /* local accumulator buffer [a decNumber, with digits+elength+1 digits] */ - decNumber dacbuff[D2N(DECBUFFER+9)]; - decNumber *dac=dacbuff; /* -> result accumulator */ - /* same again for possible 1/lhs calculation */ - decNumber invbuff[D2N(DECBUFFER+9)]; - - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - do { /* protect allocated storage */ - #if DECSUBSET - if (!set->extended) { /* reduce operands and set status, as needed */ - if (lhs->digits>reqdigits) { - alloclhs=decRoundOperand(lhs, set, &status); - if (alloclhs==NULL) break; - lhs=alloclhs; - } - if (rhs->digits>reqdigits) { - allocrhs=decRoundOperand(rhs, set, &status); - if (allocrhs==NULL) break; - rhs=allocrhs; - } - } - #endif - /* [following code does not require input rounding] */ - - /* handle NaNs and rhs Infinity (lhs infinity is harder) */ - if (SPECIALARGS) { - if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) { /* NaNs */ - decNaNs(res, lhs, rhs, set, &status); - break;} - if (decNumberIsInfinite(rhs)) { /* rhs Infinity */ - Flag rhsneg=rhs->bits&DECNEG; /* save rhs sign */ - if (decNumberIsNegative(lhs) /* lhs<0 */ - && !decNumberIsZero(lhs)) /* .. */ - status|=DEC_Invalid_operation; - else { /* lhs >=0 */ - uprv_decNumberZero(&dnOne); /* set up 1 */ - dnOne.lsu[0]=1; - uprv_decNumberCompare(dac, lhs, &dnOne, set); /* lhs ? 1 */ - uprv_decNumberZero(res); /* prepare for 0/1/Infinity */ - if (decNumberIsNegative(dac)) { /* lhs<1 */ - if (rhsneg) res->bits|=DECINF; /* +Infinity [else is +0] */ - } - else if (dac->lsu[0]==0) { /* lhs=1 */ - /* 1**Infinity is inexact, so return fully-padded 1.0000 */ - Int shift=set->digits-1; - *res->lsu=1; /* was 0, make int 1 */ - res->digits=decShiftToMost(res->lsu, 1, shift); - res->exponent=-shift; /* make 1.0000... */ - status|=DEC_Inexact|DEC_Rounded; /* deemed inexact */ - } - else { /* lhs>1 */ - if (!rhsneg) res->bits|=DECINF; /* +Infinity [else is +0] */ - } - } /* lhs>=0 */ - break;} - /* [lhs infinity drops through] */ - } /* specials */ - - /* Original rhs may be an integer that fits and is in range */ - n=decGetInt(rhs); - if (n!=BADINT) { /* it is an integer */ - rhsint=1; /* record the fact for 1**n */ - isoddint=(Flag)n&1; /* [works even if big] */ - if (n!=BIGEVEN && n!=BIGODD) /* can use integer path? */ - useint=1; /* looks good */ - } - - if (decNumberIsNegative(lhs) /* -x .. */ - && isoddint) bits=DECNEG; /* .. to an odd power */ - - /* handle LHS infinity */ - if (decNumberIsInfinite(lhs)) { /* [NaNs already handled] */ - uByte rbits=rhs->bits; /* save */ - uprv_decNumberZero(res); /* prepare */ - if (n==0) *res->lsu=1; /* [-]Inf**0 => 1 */ - else { - /* -Inf**nonint -> error */ - if (!rhsint && decNumberIsNegative(lhs)) { - status|=DEC_Invalid_operation; /* -Inf**nonint is error */ - break;} - if (!(rbits & DECNEG)) bits|=DECINF; /* was not a **-n */ - /* [otherwise will be 0 or -0] */ - res->bits=bits; - } - break;} - - /* similarly handle LHS zero */ - if (decNumberIsZero(lhs)) { - if (n==0) { /* 0**0 => Error */ - #if DECSUBSET - if (!set->extended) { /* [unless subset] */ - uprv_decNumberZero(res); - *res->lsu=1; /* return 1 */ - break;} - #endif - status|=DEC_Invalid_operation; - } - else { /* 0**x */ - uByte rbits=rhs->bits; /* save */ - if (rbits & DECNEG) { /* was a 0**(-n) */ - #if DECSUBSET - if (!set->extended) { /* [bad if subset] */ - status|=DEC_Invalid_operation; - break;} - #endif - bits|=DECINF; - } - uprv_decNumberZero(res); /* prepare */ - /* [otherwise will be 0 or -0] */ - res->bits=bits; - } - break;} - - /* here both lhs and rhs are finite; rhs==0 is handled in the */ - /* integer path. Next handle the non-integer cases */ - if (!useint) { /* non-integral rhs */ - /* any -ve lhs is bad, as is either operand or context out of */ - /* bounds */ - if (decNumberIsNegative(lhs)) { - status|=DEC_Invalid_operation; - break;} - if (decCheckMath(lhs, set, &status) - || decCheckMath(rhs, set, &status)) break; /* variable status */ - - uprv_decContextDefault(&aset, DEC_INIT_DECIMAL64); /* clean context */ - aset.emax=DEC_MAX_MATH; /* usual bounds */ - aset.emin=-DEC_MAX_MATH; /* .. */ - aset.clamp=0; /* and no concrete format */ - - /* calculate the result using exp(ln(lhs)*rhs), which can */ - /* all be done into the accumulator, dac. The precision needed */ - /* is enough to contain the full information in the lhs (which */ - /* is the total digits, including exponent), or the requested */ - /* precision, if larger, + 4; 6 is used for the exponent */ - /* maximum length, and this is also used when it is shorter */ - /* than the requested digits as it greatly reduces the >0.5 ulp */ - /* cases at little cost (because Ln doubles digits each */ - /* iteration so a few extra digits rarely causes an extra */ - /* iteration) */ - aset.digits=MAXI(lhs->digits, set->digits)+6+4; - } /* non-integer rhs */ - - else { /* rhs is in-range integer */ - if (n==0) { /* x**0 = 1 */ - /* (0**0 was handled above) */ - uprv_decNumberZero(res); /* result=1 */ - *res->lsu=1; /* .. */ - break;} - /* rhs is a non-zero integer */ - if (n<0) n=-n; /* use abs(n) */ - - aset=*set; /* clone the context */ - aset.round=DEC_ROUND_HALF_EVEN; /* internally use balanced */ - /* calculate the working DIGITS */ - aset.digits=reqdigits+(rhs->digits+rhs->exponent)+2; - #if DECSUBSET - if (!set->extended) aset.digits--; /* use classic precision */ - #endif - /* it's an error if this is more than can be handled */ - if (aset.digits>DECNUMMAXP) {status|=DEC_Invalid_operation; break;} - } /* integer path */ - - /* aset.digits is the count of digits for the accumulator needed */ - /* if accumulator is too long for local storage, then allocate */ - needbytes=sizeof(decNumber)+(D2U(aset.digits)-1)*sizeof(Unit); - /* [needbytes also used below if 1/lhs needed] */ - if (needbytes>sizeof(dacbuff)) { - allocdac=(decNumber *)malloc(needbytes); - if (allocdac==NULL) { /* hopeless -- abandon */ - status|=DEC_Insufficient_storage; - break;} - dac=allocdac; /* use the allocated space */ - } - /* here, aset is set up and accumulator is ready for use */ - - if (!useint) { /* non-integral rhs */ - /* x ** y; special-case x=1 here as it will otherwise always */ - /* reduce to integer 1; decLnOp has a fastpath which detects */ - /* the case of x=1 */ - decLnOp(dac, lhs, &aset, &status); /* dac=ln(lhs) */ - /* [no error possible, as lhs 0 already handled] */ - if (ISZERO(dac)) { /* x==1, 1.0, etc. */ - /* need to return fully-padded 1.0000 etc., but rhsint->1 */ - *dac->lsu=1; /* was 0, make int 1 */ - if (!rhsint) { /* add padding */ - Int shift=set->digits-1; - dac->digits=decShiftToMost(dac->lsu, 1, shift); - dac->exponent=-shift; /* make 1.0000... */ - status|=DEC_Inexact|DEC_Rounded; /* deemed inexact */ - } - } - else { - decMultiplyOp(dac, dac, rhs, &aset, &status); /* dac=dac*rhs */ - decExpOp(dac, dac, &aset, &status); /* dac=exp(dac) */ - } - /* and drop through for final rounding */ - } /* non-integer rhs */ - - else { /* carry on with integer */ - uprv_decNumberZero(dac); /* acc=1 */ - *dac->lsu=1; /* .. */ - - /* if a negative power the constant 1 is needed, and if not subset */ - /* invert the lhs now rather than inverting the result later */ - if (decNumberIsNegative(rhs)) { /* was a **-n [hence digits>0] */ - decNumber *inv=invbuff; /* assume use fixed buffer */ - uprv_decNumberCopy(&dnOne, dac); /* dnOne=1; [needed now or later] */ - #if DECSUBSET - if (set->extended) { /* need to calculate 1/lhs */ - #endif - /* divide lhs into 1, putting result in dac [dac=1/dac] */ - decDivideOp(dac, &dnOne, lhs, &aset, DIVIDE, &status); - /* now locate or allocate space for the inverted lhs */ - if (needbytes>sizeof(invbuff)) { - allocinv=(decNumber *)malloc(needbytes); - if (allocinv==NULL) { /* hopeless -- abandon */ - status|=DEC_Insufficient_storage; - break;} - inv=allocinv; /* use the allocated space */ - } - /* [inv now points to big-enough buffer or allocated storage] */ - uprv_decNumberCopy(inv, dac); /* copy the 1/lhs */ - uprv_decNumberCopy(dac, &dnOne); /* restore acc=1 */ - lhs=inv; /* .. and go forward with new lhs */ - #if DECSUBSET - } - #endif - } - - /* Raise-to-the-power loop... */ - seenbit=0; /* set once a 1-bit is encountered */ - for (i=1;;i++){ /* for each bit [top bit ignored] */ - /* abandon if had overflow or terminal underflow */ - if (status & (DEC_Overflow|DEC_Underflow)) { /* interesting? */ - if (status&DEC_Overflow || ISZERO(dac)) break; - } - /* [the following two lines revealed an optimizer bug in a C++ */ - /* compiler, with symptom: 5**3 -> 25, when n=n+n was used] */ - n=n<<1; /* move next bit to testable position */ - if (n<0) { /* top bit is set */ - seenbit=1; /* OK, significant bit seen */ - decMultiplyOp(dac, dac, lhs, &aset, &status); /* dac=dac*x */ - } - if (i==31) break; /* that was the last bit */ - if (!seenbit) continue; /* no need to square 1 */ - decMultiplyOp(dac, dac, dac, &aset, &status); /* dac=dac*dac [square] */ - } /*i*/ /* 32 bits */ - - /* complete internal overflow or underflow processing */ - if (status & (DEC_Overflow|DEC_Underflow)) { - #if DECSUBSET - /* If subset, and power was negative, reverse the kind of -erflow */ - /* [1/x not yet done] */ - if (!set->extended && decNumberIsNegative(rhs)) { - if (status & DEC_Overflow) - status^=DEC_Overflow | DEC_Underflow | DEC_Subnormal; - else { /* trickier -- Underflow may or may not be set */ - status&=~(DEC_Underflow | DEC_Subnormal); /* [one or both] */ - status|=DEC_Overflow; - } - } - #endif - dac->bits=(dac->bits & ~DECNEG) | bits; /* force correct sign */ - /* round subnormals [to set.digits rather than aset.digits] */ - /* or set overflow result similarly as required */ - decFinalize(dac, set, &residue, &status); - uprv_decNumberCopy(res, dac); /* copy to result (is now OK length) */ - break; - } - - #if DECSUBSET - if (!set->extended && /* subset math */ - decNumberIsNegative(rhs)) { /* was a **-n [hence digits>0] */ - /* so divide result into 1 [dac=1/dac] */ - decDivideOp(dac, &dnOne, dac, &aset, DIVIDE, &status); - } - #endif - } /* rhs integer path */ - - /* reduce result to the requested length and copy to result */ - decCopyFit(res, dac, set, &residue, &status); - decFinish(res, set, &residue, &status); /* final cleanup */ - #if DECSUBSET - if (!set->extended) decTrim(res, set, 0, 1, &dropped); /* trailing zeros */ - #endif - } while(0); /* end protected */ - - if (allocdac!=NULL) free(allocdac); /* drop any storage used */ - if (allocinv!=NULL) free(allocinv); /* .. */ - #if DECSUBSET - if (alloclhs!=NULL) free(alloclhs); /* .. */ - if (allocrhs!=NULL) free(allocrhs); /* .. */ - #endif - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberPower */ - -/* ------------------------------------------------------------------ */ -/* decNumberQuantize -- force exponent to requested value */ -/* */ -/* This computes C = op(A, B), where op adjusts the coefficient */ -/* of C (by rounding or shifting) such that the exponent (-scale) */ -/* of C has exponent of B. The numerical value of C will equal A, */ -/* except for the effects of any rounding that occurred. */ -/* */ -/* res is C, the result. C may be A or B */ -/* lhs is A, the number to adjust */ -/* rhs is B, the number with exponent to match */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* Unless there is an error or the result is infinite, the exponent */ -/* after the operation is guaranteed to be equal to that of B. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberQuantize(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decQuantizeOp(res, lhs, rhs, set, 1, &status); - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberQuantize */ - -/* ------------------------------------------------------------------ */ -/* decNumberReduce -- remove trailing zeros */ -/* */ -/* This computes C = 0 + A, and normalizes the result */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -/* Previously known as Normalize */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberNormalize(decNumber *res, const decNumber *rhs, - decContext *set) { - return uprv_decNumberReduce(res, rhs, set); - } /* decNumberNormalize */ - -U_CAPI decNumber * U_EXPORT2 uprv_decNumberReduce(decNumber *res, const decNumber *rhs, - decContext *set) { - #if DECSUBSET - decNumber *allocrhs=NULL; /* non-NULL if rounded rhs allocated */ - #endif - uInt status=0; /* as usual */ - Int residue=0; /* as usual */ - Int dropped; /* work */ - - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - do { /* protect allocated storage */ - #if DECSUBSET - if (!set->extended) { - /* reduce operand and set lostDigits status, as needed */ - if (rhs->digits>set->digits) { - allocrhs=decRoundOperand(rhs, set, &status); - if (allocrhs==NULL) break; - rhs=allocrhs; - } - } - #endif - /* [following code does not require input rounding] */ - - /* Infinities copy through; NaNs need usual treatment */ - if (decNumberIsNaN(rhs)) { - decNaNs(res, rhs, NULL, set, &status); - break; - } - - /* reduce result to the requested length and copy to result */ - decCopyFit(res, rhs, set, &residue, &status); /* copy & round */ - decFinish(res, set, &residue, &status); /* cleanup/set flags */ - decTrim(res, set, 1, 0, &dropped); /* normalize in place */ - /* [may clamp] */ - } while(0); /* end protected */ - - #if DECSUBSET - if (allocrhs !=NULL) free(allocrhs); /* .. */ - #endif - if (status!=0) decStatus(res, status, set);/* then report status */ - return res; - } /* decNumberReduce */ - -/* ------------------------------------------------------------------ */ -/* decNumberRescale -- force exponent to requested value */ -/* */ -/* This computes C = op(A, B), where op adjusts the coefficient */ -/* of C (by rounding or shifting) such that the exponent (-scale) */ -/* of C has the value B. The numerical value of C will equal A, */ -/* except for the effects of any rounding that occurred. */ -/* */ -/* res is C, the result. C may be A or B */ -/* lhs is A, the number to adjust */ -/* rhs is B, the requested exponent */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* Unless there is an error or the result is infinite, the exponent */ -/* after the operation is guaranteed to be equal to B. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberRescale(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decQuantizeOp(res, lhs, rhs, set, 0, &status); - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberRescale */ - -/* ------------------------------------------------------------------ */ -/* decNumberRemainder -- divide and return remainder */ -/* */ -/* This computes C = A % B */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X%X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberRemainder(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decDivideOp(res, lhs, rhs, set, REMAINDER, &status); - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberRemainder */ - -/* ------------------------------------------------------------------ */ -/* decNumberRemainderNear -- divide and return remainder from nearest */ -/* */ -/* This computes C = A % B, where % is the IEEE remainder operator */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X%X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberRemainderNear(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - decDivideOp(res, lhs, rhs, set, REMNEAR, &status); - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberRemainderNear */ - -/* ------------------------------------------------------------------ */ -/* decNumberRotate -- rotate the coefficient of a Number left/right */ -/* */ -/* This computes C = A rot B (in base ten and rotating set->digits */ -/* digits). */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=XrotX) */ -/* lhs is A */ -/* rhs is B, the number of digits to rotate (-ve to right) */ -/* set is the context */ -/* */ -/* The digits of the coefficient of A are rotated to the left (if B */ -/* is positive) or to the right (if B is negative) without adjusting */ -/* the exponent or the sign of A. If lhs->digits is less than */ -/* set->digits the coefficient is padded with zeros on the left */ -/* before the rotate. Any leading zeros in the result are removed */ -/* as usual. */ -/* */ -/* B must be an integer (q=0) and in the range -set->digits through */ -/* +set->digits. */ -/* C must have space for set->digits digits. */ -/* NaNs are propagated as usual. Infinities are unaffected (but */ -/* B must be valid). No status is set unless B is invalid or an */ -/* operand is an sNaN. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberRotate(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - Int rotate; /* rhs as an Int */ - - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - /* NaNs propagate as normal */ - if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) - decNaNs(res, lhs, rhs, set, &status); - /* rhs must be an integer */ - else if (decNumberIsInfinite(rhs) || rhs->exponent!=0) - status=DEC_Invalid_operation; - else { /* both numeric, rhs is an integer */ - rotate=decGetInt(rhs); /* [cannot fail] */ - if (rotate==BADINT /* something bad .. */ - || rotate==BIGODD || rotate==BIGEVEN /* .. very big .. */ - || abs(rotate)>set->digits) /* .. or out of range */ - status=DEC_Invalid_operation; - else { /* rhs is OK */ - uprv_decNumberCopy(res, lhs); - /* convert -ve rotate to equivalent positive rotation */ - if (rotate<0) rotate=set->digits+rotate; - if (rotate!=0 && rotate!=set->digits /* zero or full rotation */ - && !decNumberIsInfinite(res)) { /* lhs was infinite */ - /* left-rotate to do; 0 < rotate < set->digits */ - uInt units, shift; /* work */ - uInt msudigits; /* digits in result msu */ - Unit *msu=res->lsu+D2U(res->digits)-1; /* current msu */ - Unit *msumax=res->lsu+D2U(set->digits)-1; /* rotation msu */ - for (msu++; msu<=msumax; msu++) *msu=0; /* ensure high units=0 */ - res->digits=set->digits; /* now full-length */ - msudigits=MSUDIGITS(res->digits); /* actual digits in msu */ - - /* rotation here is done in-place, in three steps */ - /* 1. shift all to least up to one unit to unit-align final */ - /* lsd [any digits shifted out are rotated to the left, */ - /* abutted to the original msd (which may require split)] */ - /* */ - /* [if there are no whole units left to rotate, the */ - /* rotation is now complete] */ - /* */ - /* 2. shift to least, from below the split point only, so that */ - /* the final msd is in the right place in its Unit [any */ - /* digits shifted out will fit exactly in the current msu, */ - /* left aligned, no split required] */ - /* */ - /* 3. rotate all the units by reversing left part, right */ - /* part, and then whole */ - /* */ - /* example: rotate right 8 digits (2 units + 2), DECDPUN=3. */ - /* */ - /* start: 00a bcd efg hij klm npq */ - /* */ - /* 1a 000 0ab cde fgh|ijk lmn [pq saved] */ - /* 1b 00p qab cde fgh|ijk lmn */ - /* */ - /* 2a 00p qab cde fgh|00i jkl [mn saved] */ - /* 2b mnp qab cde fgh|00i jkl */ - /* */ - /* 3a fgh cde qab mnp|00i jkl */ - /* 3b fgh cde qab mnp|jkl 00i */ - /* 3c 00i jkl mnp qab cde fgh */ - - /* Step 1: amount to shift is the partial right-rotate count */ - rotate=set->digits-rotate; /* make it right-rotate */ - units=rotate/DECDPUN; /* whole units to rotate */ - shift=rotate%DECDPUN; /* left-over digits count */ - if (shift>0) { /* not an exact number of units */ - uInt save=res->lsu[0]%powers[shift]; /* save low digit(s) */ - decShiftToLeast(res->lsu, D2U(res->digits), shift); - if (shift>msudigits) { /* msumax-1 needs >0 digits */ - uInt rem=save%powers[shift-msudigits];/* split save */ - *msumax=(Unit)(save/powers[shift-msudigits]); /* and insert */ - *(msumax-1)=*(msumax-1) - +(Unit)(rem*powers[DECDPUN-(shift-msudigits)]); /* .. */ - } - else { /* all fits in msumax */ - *msumax=*msumax+(Unit)(save*powers[msudigits-shift]); /* [maybe *1] */ - } - } /* digits shift needed */ - - /* If whole units to rotate... */ - if (units>0) { /* some to do */ - /* Step 2: the units to touch are the whole ones in rotate, */ - /* if any, and the shift is DECDPUN-msudigits (which may be */ - /* 0, again) */ - shift=DECDPUN-msudigits; - if (shift>0) { /* not an exact number of units */ - uInt save=res->lsu[0]%powers[shift]; /* save low digit(s) */ - decShiftToLeast(res->lsu, units, shift); - *msumax=*msumax+(Unit)(save*powers[msudigits]); - } /* partial shift needed */ - - /* Step 3: rotate the units array using triple reverse */ - /* (reversing is easy and fast) */ - decReverse(res->lsu+units, msumax); /* left part */ - decReverse(res->lsu, res->lsu+units-1); /* right part */ - decReverse(res->lsu, msumax); /* whole */ - } /* whole units to rotate */ - /* the rotation may have left an undetermined number of zeros */ - /* on the left, so true length needs to be calculated */ - res->digits=decGetDigits(res->lsu, static_cast(msumax-res->lsu+1)); - } /* rotate needed */ - } /* rhs OK */ - } /* numerics */ - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberRotate */ - -/* ------------------------------------------------------------------ */ -/* decNumberSameQuantum -- test for equal exponents */ -/* */ -/* res is the result number, which will contain either 0 or 1 */ -/* lhs is a number to test */ -/* rhs is the second (usually a pattern) */ -/* */ -/* No errors are possible and no context is needed. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberSameQuantum(decNumber *res, const decNumber *lhs, - const decNumber *rhs) { - Unit ret=0; /* return value */ - - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, DECUNCONT)) return res; - #endif - - if (SPECIALARGS) { - if (decNumberIsNaN(lhs) && decNumberIsNaN(rhs)) ret=1; - else if (decNumberIsInfinite(lhs) && decNumberIsInfinite(rhs)) ret=1; - /* [anything else with a special gives 0] */ - } - else if (lhs->exponent==rhs->exponent) ret=1; - - uprv_decNumberZero(res); /* OK to overwrite an operand now */ - *res->lsu=ret; - return res; - } /* decNumberSameQuantum */ - -/* ------------------------------------------------------------------ */ -/* decNumberScaleB -- multiply by a power of 10 */ -/* */ -/* This computes C = A x 10**B where B is an integer (q=0) with */ -/* maximum magnitude 2*(emax+digits) */ -/* */ -/* res is C, the result. C may be A or B */ -/* lhs is A, the number to adjust */ -/* rhs is B, the requested power of ten to use */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* The result may underflow or overflow. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberScaleB(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - Int reqexp; /* requested exponent change [B] */ - uInt status=0; /* accumulator */ - Int residue; /* work */ - - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - /* Handle special values except lhs infinite */ - if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) - decNaNs(res, lhs, rhs, set, &status); - /* rhs must be an integer */ - else if (decNumberIsInfinite(rhs) || rhs->exponent!=0) - status=DEC_Invalid_operation; - else { - /* lhs is a number; rhs is a finite with q==0 */ - reqexp=decGetInt(rhs); /* [cannot fail] */ - if (reqexp==BADINT /* something bad .. */ - || reqexp==BIGODD || reqexp==BIGEVEN /* .. very big .. */ - || abs(reqexp)>(2*(set->digits+set->emax))) /* .. or out of range */ - status=DEC_Invalid_operation; - else { /* rhs is OK */ - uprv_decNumberCopy(res, lhs); /* all done if infinite lhs */ - if (!decNumberIsInfinite(res)) { /* prepare to scale */ - res->exponent+=reqexp; /* adjust the exponent */ - residue=0; - decFinalize(res, set, &residue, &status); /* .. and check */ - } /* finite LHS */ - } /* rhs OK */ - } /* rhs finite */ - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberScaleB */ - -/* ------------------------------------------------------------------ */ -/* decNumberShift -- shift the coefficient of a Number left or right */ -/* */ -/* This computes C = A << B or C = A >> -B (in base ten). */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X<digits through */ -/* +set->digits. */ -/* C must have space for set->digits digits. */ -/* NaNs are propagated as usual. Infinities are unaffected (but */ -/* B must be valid). No status is set unless B is invalid or an */ -/* operand is an sNaN. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberShift(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - Int shift; /* rhs as an Int */ - - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - /* NaNs propagate as normal */ - if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) - decNaNs(res, lhs, rhs, set, &status); - /* rhs must be an integer */ - else if (decNumberIsInfinite(rhs) || rhs->exponent!=0) - status=DEC_Invalid_operation; - else { /* both numeric, rhs is an integer */ - shift=decGetInt(rhs); /* [cannot fail] */ - if (shift==BADINT /* something bad .. */ - || shift==BIGODD || shift==BIGEVEN /* .. very big .. */ - || abs(shift)>set->digits) /* .. or out of range */ - status=DEC_Invalid_operation; - else { /* rhs is OK */ - uprv_decNumberCopy(res, lhs); - if (shift!=0 && !decNumberIsInfinite(res)) { /* something to do */ - if (shift>0) { /* to left */ - if (shift==set->digits) { /* removing all */ - *res->lsu=0; /* so place 0 */ - res->digits=1; /* .. */ - } - else { /* */ - /* first remove leading digits if necessary */ - if (res->digits+shift>set->digits) { - decDecap(res, res->digits+shift-set->digits); - /* that updated res->digits; may have gone to 1 (for a */ - /* single digit or for zero */ - } - if (res->digits>1 || *res->lsu) /* if non-zero.. */ - res->digits=decShiftToMost(res->lsu, res->digits, shift); - } /* partial left */ - } /* left */ - else { /* to right */ - if (-shift>=res->digits) { /* discarding all */ - *res->lsu=0; /* so place 0 */ - res->digits=1; /* .. */ - } - else { - decShiftToLeast(res->lsu, D2U(res->digits), -shift); - res->digits-=(-shift); - } - } /* to right */ - } /* non-0 non-Inf shift */ - } /* rhs OK */ - } /* numerics */ - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberShift */ - -/* ------------------------------------------------------------------ */ -/* decNumberSquareRoot -- square root operator */ -/* */ -/* This computes C = squareroot(A) */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* set is the context; note that rounding mode has no effect */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -/* This uses the following varying-precision algorithm in: */ -/* */ -/* Properly Rounded Variable Precision Square Root, T. E. Hull and */ -/* A. Abrham, ACM Transactions on Mathematical Software, Vol 11 #3, */ -/* pp229-237, ACM, September 1985. */ -/* */ -/* The square-root is calculated using Newton's method, after which */ -/* a check is made to ensure the result is correctly rounded. */ -/* */ -/* % [Reformatted original Numerical Turing source code follows.] */ -/* function sqrt(x : real) : real */ -/* % sqrt(x) returns the properly rounded approximation to the square */ -/* % root of x, in the precision of the calling environment, or it */ -/* % fails if x < 0. */ -/* % t e hull and a abrham, august, 1984 */ -/* if x <= 0 then */ -/* if x < 0 then */ -/* assert false */ -/* else */ -/* result 0 */ -/* end if */ -/* end if */ -/* var f := setexp(x, 0) % fraction part of x [0.1 <= x < 1] */ -/* var e := getexp(x) % exponent part of x */ -/* var approx : real */ -/* if e mod 2 = 0 then */ -/* approx := .259 + .819 * f % approx to root of f */ -/* else */ -/* f := f/l0 % adjustments */ -/* e := e + 1 % for odd */ -/* approx := .0819 + 2.59 * f % exponent */ -/* end if */ -/* */ -/* var p:= 3 */ -/* const maxp := currentprecision + 2 */ -/* loop */ -/* p := min(2*p - 2, maxp) % p = 4,6,10, . . . , maxp */ -/* precision p */ -/* approx := .5 * (approx + f/approx) */ -/* exit when p = maxp */ -/* end loop */ -/* */ -/* % approx is now within 1 ulp of the properly rounded square root */ -/* % of f; to ensure proper rounding, compare squares of (approx - */ -/* % l/2 ulp) and (approx + l/2 ulp) with f. */ -/* p := currentprecision */ -/* begin */ -/* precision p + 2 */ -/* const approxsubhalf := approx - setexp(.5, -p) */ -/* if mulru(approxsubhalf, approxsubhalf) > f then */ -/* approx := approx - setexp(.l, -p + 1) */ -/* else */ -/* const approxaddhalf := approx + setexp(.5, -p) */ -/* if mulrd(approxaddhalf, approxaddhalf) < f then */ -/* approx := approx + setexp(.l, -p + 1) */ -/* end if */ -/* end if */ -/* end */ -/* result setexp(approx, e div 2) % fix exponent */ -/* end sqrt */ -/* ------------------------------------------------------------------ */ -#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 406 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" -#endif -U_CAPI decNumber * U_EXPORT2 uprv_decNumberSquareRoot(decNumber *res, const decNumber *rhs, - decContext *set) { - decContext workset, approxset; /* work contexts */ - decNumber dzero; /* used for constant zero */ - Int maxp; /* largest working precision */ - Int workp; /* working precision */ - Int residue=0; /* rounding residue */ - uInt status=0, ignore=0; /* status accumulators */ - uInt rstatus; /* .. */ - Int exp; /* working exponent */ - Int ideal; /* ideal (preferred) exponent */ - Int needbytes; /* work */ - Int dropped; /* .. */ - - #if DECSUBSET - decNumber *allocrhs=NULL; /* non-NULL if rounded rhs allocated */ - #endif - /* buffer for f [needs +1 in case DECBUFFER 0] */ - decNumber buff[D2N(DECBUFFER+1)]; - /* buffer for a [needs +2 to match likely maxp] */ - decNumber bufa[D2N(DECBUFFER+2)]; - /* buffer for temporary, b [must be same size as a] */ - decNumber bufb[D2N(DECBUFFER+2)]; - decNumber *allocbuff=NULL; /* -> allocated buff, iff allocated */ - decNumber *allocbufa=NULL; /* -> allocated bufa, iff allocated */ - decNumber *allocbufb=NULL; /* -> allocated bufb, iff allocated */ - decNumber *f=buff; /* reduced fraction */ - decNumber *a=bufa; /* approximation to result */ - decNumber *b=bufb; /* intermediate result */ - /* buffer for temporary variable, up to 3 digits */ - decNumber buft[D2N(3)]; - decNumber *t=buft; /* up-to-3-digit constant or work */ - - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - do { /* protect allocated storage */ - #if DECSUBSET - if (!set->extended) { - /* reduce operand and set lostDigits status, as needed */ - if (rhs->digits>set->digits) { - allocrhs=decRoundOperand(rhs, set, &status); - if (allocrhs==NULL) break; - /* [Note: 'f' allocation below could reuse this buffer if */ - /* used, but as this is rare they are kept separate for clarity.] */ - rhs=allocrhs; - } - } - #endif - /* [following code does not require input rounding] */ - - /* handle infinities and NaNs */ - if (SPECIALARG) { - if (decNumberIsInfinite(rhs)) { /* an infinity */ - if (decNumberIsNegative(rhs)) status|=DEC_Invalid_operation; - else uprv_decNumberCopy(res, rhs); /* +Infinity */ - } - else decNaNs(res, rhs, NULL, set, &status); /* a NaN */ - break; - } - - /* calculate the ideal (preferred) exponent [floor(exp/2)] */ - /* [It would be nicer to write: ideal=rhs->exponent>>1, but this */ - /* generates a compiler warning. Generated code is the same.] */ - ideal=(rhs->exponent&~1)/2; /* target */ - - /* handle zeros */ - if (ISZERO(rhs)) { - uprv_decNumberCopy(res, rhs); /* could be 0 or -0 */ - res->exponent=ideal; /* use the ideal [safe] */ - /* use decFinish to clamp any out-of-range exponent, etc. */ - decFinish(res, set, &residue, &status); - break; - } - - /* any other -x is an oops */ - if (decNumberIsNegative(rhs)) { - status|=DEC_Invalid_operation; - break; - } - - /* space is needed for three working variables */ - /* f -- the same precision as the RHS, reduced to 0.01->0.99... */ - /* a -- Hull's approximation -- precision, when assigned, is */ - /* currentprecision+1 or the input argument precision, */ - /* whichever is larger (+2 for use as temporary) */ - /* b -- intermediate temporary result (same size as a) */ - /* if any is too long for local storage, then allocate */ - workp=MAXI(set->digits+1, rhs->digits); /* actual rounding precision */ - workp=MAXI(workp, 7); /* at least 7 for low cases */ - maxp=workp+2; /* largest working precision */ - - needbytes=sizeof(decNumber)+(D2U(rhs->digits)-1)*sizeof(Unit); - if (needbytes>(Int)sizeof(buff)) { - allocbuff=(decNumber *)malloc(needbytes); - if (allocbuff==NULL) { /* hopeless -- abandon */ - status|=DEC_Insufficient_storage; - break;} - f=allocbuff; /* use the allocated space */ - } - /* a and b both need to be able to hold a maxp-length number */ - needbytes=sizeof(decNumber)+(D2U(maxp)-1)*sizeof(Unit); - if (needbytes>(Int)sizeof(bufa)) { /* [same applies to b] */ - allocbufa=(decNumber *)malloc(needbytes); - allocbufb=(decNumber *)malloc(needbytes); - if (allocbufa==NULL || allocbufb==NULL) { /* hopeless */ - status|=DEC_Insufficient_storage; - break;} - a=allocbufa; /* use the allocated spaces */ - b=allocbufb; /* .. */ - } - - /* copy rhs -> f, save exponent, and reduce so 0.1 <= f < 1 */ - uprv_decNumberCopy(f, rhs); - exp=f->exponent+f->digits; /* adjusted to Hull rules */ - f->exponent=-(f->digits); /* to range */ - - /* set up working context */ - uprv_decContextDefault(&workset, DEC_INIT_DECIMAL64); - workset.emax=DEC_MAX_EMAX; - workset.emin=DEC_MIN_EMIN; - - /* [Until further notice, no error is possible and status bits */ - /* (Rounded, etc.) should be ignored, not accumulated.] */ - - /* Calculate initial approximation, and allow for odd exponent */ - workset.digits=workp; /* p for initial calculation */ - t->bits=0; t->digits=3; - a->bits=0; a->digits=3; - if ((exp & 1)==0) { /* even exponent */ - /* Set t=0.259, a=0.819 */ - t->exponent=-3; - a->exponent=-3; - #if DECDPUN>=3 - t->lsu[0]=259; - a->lsu[0]=819; - #elif DECDPUN==2 - t->lsu[0]=59; t->lsu[1]=2; - a->lsu[0]=19; a->lsu[1]=8; - #else - t->lsu[0]=9; t->lsu[1]=5; t->lsu[2]=2; - a->lsu[0]=9; a->lsu[1]=1; a->lsu[2]=8; - #endif - } - else { /* odd exponent */ - /* Set t=0.0819, a=2.59 */ - f->exponent--; /* f=f/10 */ - exp++; /* e=e+1 */ - t->exponent=-4; - a->exponent=-2; - #if DECDPUN>=3 - t->lsu[0]=819; - a->lsu[0]=259; - #elif DECDPUN==2 - t->lsu[0]=19; t->lsu[1]=8; - a->lsu[0]=59; a->lsu[1]=2; - #else - t->lsu[0]=9; t->lsu[1]=1; t->lsu[2]=8; - a->lsu[0]=9; a->lsu[1]=5; a->lsu[2]=2; - #endif - } - - decMultiplyOp(a, a, f, &workset, &ignore); /* a=a*f */ - decAddOp(a, a, t, &workset, 0, &ignore); /* ..+t */ - /* [a is now the initial approximation for sqrt(f), calculated with */ - /* currentprecision, which is also a's precision.] */ - - /* the main calculation loop */ - uprv_decNumberZero(&dzero); /* make 0 */ - uprv_decNumberZero(t); /* set t = 0.5 */ - t->lsu[0]=5; /* .. */ - t->exponent=-1; /* .. */ - workset.digits=3; /* initial p */ - for (; workset.digitsexponent+=exp/2; /* set correct exponent */ - rstatus=0; /* clear status */ - residue=0; /* .. and accumulator */ - decCopyFit(a, a, &approxset, &residue, &rstatus); /* reduce (if needed) */ - decFinish(a, &approxset, &residue, &rstatus); /* clean and finalize */ - - /* Overflow was possible if the input exponent was out-of-range, */ - /* in which case quit */ - if (rstatus&DEC_Overflow) { - status=rstatus; /* use the status as-is */ - uprv_decNumberCopy(res, a); /* copy to result */ - break; - } - - /* Preserve status except Inexact/Rounded */ - status|=(rstatus & ~(DEC_Rounded|DEC_Inexact)); - - /* Carry out the Hull correction */ - a->exponent-=exp/2; /* back to 0.1->1 */ - - /* a is now at final precision and within 1 ulp of the properly */ - /* rounded square root of f; to ensure proper rounding, compare */ - /* squares of (a - l/2 ulp) and (a + l/2 ulp) with f. */ - /* Here workset.digits=maxp and t=0.5, and a->digits determines */ - /* the ulp */ - workset.digits--; /* maxp-1 is OK now */ - t->exponent=-a->digits-1; /* make 0.5 ulp */ - decAddOp(b, a, t, &workset, DECNEG, &ignore); /* b = a - 0.5 ulp */ - workset.round=DEC_ROUND_UP; - decMultiplyOp(b, b, b, &workset, &ignore); /* b = mulru(b, b) */ - decCompareOp(b, f, b, &workset, COMPARE, &ignore); /* b ? f, reversed */ - if (decNumberIsNegative(b)) { /* f < b [i.e., b > f] */ - /* this is the more common adjustment, though both are rare */ - t->exponent++; /* make 1.0 ulp */ - t->lsu[0]=1; /* .. */ - decAddOp(a, a, t, &workset, DECNEG, &ignore); /* a = a - 1 ulp */ - /* assign to approx [round to length] */ - approxset.emin-=exp/2; /* adjust to match a */ - approxset.emax-=exp/2; - decAddOp(a, &dzero, a, &approxset, 0, &ignore); - } - else { - decAddOp(b, a, t, &workset, 0, &ignore); /* b = a + 0.5 ulp */ - workset.round=DEC_ROUND_DOWN; - decMultiplyOp(b, b, b, &workset, &ignore); /* b = mulrd(b, b) */ - decCompareOp(b, b, f, &workset, COMPARE, &ignore); /* b ? f */ - if (decNumberIsNegative(b)) { /* b < f */ - t->exponent++; /* make 1.0 ulp */ - t->lsu[0]=1; /* .. */ - decAddOp(a, a, t, &workset, 0, &ignore); /* a = a + 1 ulp */ - /* assign to approx [round to length] */ - approxset.emin-=exp/2; /* adjust to match a */ - approxset.emax-=exp/2; - decAddOp(a, &dzero, a, &approxset, 0, &ignore); - } - } - /* [no errors are possible in the above, and rounding/inexact during */ - /* estimation are irrelevant, so status was not accumulated] */ - - /* Here, 0.1 <= a < 1 (still), so adjust back */ - a->exponent+=exp/2; /* set correct exponent */ - - /* count droppable zeros [after any subnormal rounding] by */ - /* trimming a copy */ - uprv_decNumberCopy(b, a); - decTrim(b, set, 1, 1, &dropped); /* [drops trailing zeros] */ - - /* Set Inexact and Rounded. The answer can only be exact if */ - /* it is short enough so that squaring it could fit in workp */ - /* digits, so this is the only (relatively rare) condition that */ - /* a careful check is needed */ - if (b->digits*2-1 > workp) { /* cannot fit */ - status|=DEC_Inexact|DEC_Rounded; - } - else { /* could be exact/unrounded */ - uInt mstatus=0; /* local status */ - decMultiplyOp(b, b, b, &workset, &mstatus); /* try the multiply */ - if (mstatus&DEC_Overflow) { /* result just won't fit */ - status|=DEC_Inexact|DEC_Rounded; - } - else { /* plausible */ - decCompareOp(t, b, rhs, &workset, COMPARE, &mstatus); /* b ? rhs */ - if (!ISZERO(t)) status|=DEC_Inexact|DEC_Rounded; /* not equal */ - else { /* is Exact */ - /* here, dropped is the count of trailing zeros in 'a' */ - /* use closest exponent to ideal... */ - Int todrop=ideal-a->exponent; /* most that can be dropped */ - if (todrop<0) status|=DEC_Rounded; /* ideally would add 0s */ - else { /* unrounded */ - /* there are some to drop, but emax may not allow all */ - Int maxexp=set->emax-set->digits+1; - Int maxdrop=maxexp-a->exponent; - if (todrop>maxdrop && set->clamp) { /* apply clamping */ - todrop=maxdrop; - status|=DEC_Clamped; - } - if (dropped0) { /* have some to drop */ - decShiftToLeast(a->lsu, D2U(a->digits), todrop); - a->exponent+=todrop; /* maintain numerical value */ - a->digits-=todrop; /* new length */ - } - } - } - } - } - - /* double-check Underflow, as perhaps the result could not have */ - /* been subnormal (initial argument too big), or it is now Exact */ - if (status&DEC_Underflow) { - Int ae=rhs->exponent+rhs->digits-1; /* adjusted exponent */ - /* check if truly subnormal */ - #if DECEXTFLAG /* DEC_Subnormal too */ - if (ae>=set->emin*2) status&=~(DEC_Subnormal|DEC_Underflow); - #else - if (ae>=set->emin*2) status&=~DEC_Underflow; - #endif - /* check if truly inexact */ - if (!(status&DEC_Inexact)) status&=~DEC_Underflow; - } - - uprv_decNumberCopy(res, a); /* a is now the result */ - } while(0); /* end protected */ - - if (allocbuff!=NULL) free(allocbuff); /* drop any storage used */ - if (allocbufa!=NULL) free(allocbufa); /* .. */ - if (allocbufb!=NULL) free(allocbufb); /* .. */ - #if DECSUBSET - if (allocrhs !=NULL) free(allocrhs); /* .. */ - #endif - if (status!=0) decStatus(res, status, set);/* then report status */ - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberSquareRoot */ -#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 406 -#pragma GCC diagnostic pop -#endif - -/* ------------------------------------------------------------------ */ -/* decNumberSubtract -- subtract two Numbers */ -/* */ -/* This computes C = A - B */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X-X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* */ -/* C must have space for set->digits digits. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberSubtract(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - uInt status=0; /* accumulator */ - - decAddOp(res, lhs, rhs, set, DECNEG, &status); - if (status!=0) decStatus(res, status, set); - #if DECCHECK - decCheckInexact(res, set); - #endif - return res; - } /* decNumberSubtract */ - -/* ------------------------------------------------------------------ */ -/* decNumberToIntegralExact -- round-to-integral-value with InExact */ -/* decNumberToIntegralValue -- round-to-integral-value */ -/* */ -/* res is the result */ -/* rhs is input number */ -/* set is the context */ -/* */ -/* res must have space for any value of rhs. */ -/* */ -/* This implements the IEEE special operators and therefore treats */ -/* special values as valid. For finite numbers it returns */ -/* rescale(rhs, 0) if rhs->exponent is <0. */ -/* Otherwise the result is rhs (so no error is possible, except for */ -/* sNaN). */ -/* */ -/* The context is used for rounding mode and status after sNaN, but */ -/* the digits setting is ignored. The Exact version will signal */ -/* Inexact if the result differs numerically from rhs; the other */ -/* never signals Inexact. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberToIntegralExact(decNumber *res, const decNumber *rhs, - decContext *set) { - decNumber dn; - decContext workset; /* working context */ - uInt status=0; /* accumulator */ - - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - /* handle infinities and NaNs */ - if (SPECIALARG) { - if (decNumberIsInfinite(rhs)) uprv_decNumberCopy(res, rhs); /* an Infinity */ - else decNaNs(res, rhs, NULL, set, &status); /* a NaN */ - } - else { /* finite */ - /* have a finite number; no error possible (res must be big enough) */ - if (rhs->exponent>=0) return uprv_decNumberCopy(res, rhs); - /* that was easy, but if negative exponent there is work to do... */ - workset=*set; /* clone rounding, etc. */ - workset.digits=rhs->digits; /* no length rounding */ - workset.traps=0; /* no traps */ - uprv_decNumberZero(&dn); /* make a number with exponent 0 */ - uprv_decNumberQuantize(res, rhs, &dn, &workset); - status|=workset.status; - } - if (status!=0) decStatus(res, status, set); - return res; - } /* decNumberToIntegralExact */ - -U_CAPI decNumber * U_EXPORT2 uprv_decNumberToIntegralValue(decNumber *res, const decNumber *rhs, - decContext *set) { - decContext workset=*set; /* working context */ - workset.traps=0; /* no traps */ - uprv_decNumberToIntegralExact(res, rhs, &workset); - /* this never affects set, except for sNaNs; NaN will have been set */ - /* or propagated already, so no need to call decStatus */ - set->status|=workset.status&DEC_Invalid_operation; - return res; - } /* decNumberToIntegralValue */ - -/* ------------------------------------------------------------------ */ -/* decNumberXor -- XOR two Numbers, digitwise */ -/* */ -/* This computes C = A ^ B */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X^X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context (used for result length and error report) */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* Logical function restrictions apply (see above); a NaN is */ -/* returned with Invalid_operation if a restriction is violated. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberXor(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - const Unit *ua, *ub; /* -> operands */ - const Unit *msua, *msub; /* -> operand msus */ - Unit *uc, *msuc; /* -> result and its msu */ - Int msudigs; /* digits in res msu */ - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - if (lhs->exponent!=0 || decNumberIsSpecial(lhs) || decNumberIsNegative(lhs) - || rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) { - decStatus(res, DEC_Invalid_operation, set); - return res; - } - /* operands are valid */ - ua=lhs->lsu; /* bottom-up */ - ub=rhs->lsu; /* .. */ - uc=res->lsu; /* .. */ - msua=ua+D2U(lhs->digits)-1; /* -> msu of lhs */ - msub=ub+D2U(rhs->digits)-1; /* -> msu of rhs */ - msuc=uc+D2U(set->digits)-1; /* -> msu of result */ - msudigs=MSUDIGITS(set->digits); /* [faster than remainder] */ - for (; uc<=msuc; ua++, ub++, uc++) { /* Unit loop */ - Unit a, b; /* extract units */ - if (ua>msua) a=0; - else a=*ua; - if (ub>msub) b=0; - else b=*ub; - *uc=0; /* can now write back */ - if (a|b) { /* maybe 1 bits to examine */ - Int i, j; - /* This loop could be unrolled and/or use BIN2BCD tables */ - for (i=0; i1) { - decStatus(res, DEC_Invalid_operation, set); - return res; - } - if (uc==msuc && i==msudigs-1) break; /* just did final digit */ - } /* each digit */ - } /* non-zero */ - } /* each unit */ - /* [here uc-1 is the msu of the result] */ - res->digits=decGetDigits(res->lsu, static_cast(uc-res->lsu)); - res->exponent=0; /* integer */ - res->bits=0; /* sign=0 */ - return res; /* [no status to set] */ - } /* decNumberXor */ - - -/* ================================================================== */ -/* Utility routines */ -/* ================================================================== */ - -/* ------------------------------------------------------------------ */ -/* decNumberClass -- return the decClass of a decNumber */ -/* dn -- the decNumber to test */ -/* set -- the context to use for Emin */ -/* returns the decClass enum */ -/* ------------------------------------------------------------------ */ -enum decClass uprv_decNumberClass(const decNumber *dn, decContext *set) { - if (decNumberIsSpecial(dn)) { - if (decNumberIsQNaN(dn)) return DEC_CLASS_QNAN; - if (decNumberIsSNaN(dn)) return DEC_CLASS_SNAN; - /* must be an infinity */ - if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_INF; - return DEC_CLASS_POS_INF; - } - /* is finite */ - if (uprv_decNumberIsNormal(dn, set)) { /* most common */ - if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_NORMAL; - return DEC_CLASS_POS_NORMAL; - } - /* is subnormal or zero */ - if (decNumberIsZero(dn)) { /* most common */ - if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_ZERO; - return DEC_CLASS_POS_ZERO; - } - if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_SUBNORMAL; - return DEC_CLASS_POS_SUBNORMAL; - } /* decNumberClass */ - -/* ------------------------------------------------------------------ */ -/* decNumberClassToString -- convert decClass to a string */ -/* */ -/* eclass is a valid decClass */ -/* returns a constant string describing the class (max 13+1 chars) */ -/* ------------------------------------------------------------------ */ -const char *uprv_decNumberClassToString(enum decClass eclass) { - if (eclass==DEC_CLASS_POS_NORMAL) return DEC_ClassString_PN; - if (eclass==DEC_CLASS_NEG_NORMAL) return DEC_ClassString_NN; - if (eclass==DEC_CLASS_POS_ZERO) return DEC_ClassString_PZ; - if (eclass==DEC_CLASS_NEG_ZERO) return DEC_ClassString_NZ; - if (eclass==DEC_CLASS_POS_SUBNORMAL) return DEC_ClassString_PS; - if (eclass==DEC_CLASS_NEG_SUBNORMAL) return DEC_ClassString_NS; - if (eclass==DEC_CLASS_POS_INF) return DEC_ClassString_PI; - if (eclass==DEC_CLASS_NEG_INF) return DEC_ClassString_NI; - if (eclass==DEC_CLASS_QNAN) return DEC_ClassString_QN; - if (eclass==DEC_CLASS_SNAN) return DEC_ClassString_SN; - return DEC_ClassString_UN; /* Unknown */ - } /* decNumberClassToString */ - -/* ------------------------------------------------------------------ */ -/* decNumberCopy -- copy a number */ -/* */ -/* dest is the target decNumber */ -/* src is the source decNumber */ -/* returns dest */ -/* */ -/* (dest==src is allowed and is a no-op) */ -/* All fields are updated as required. This is a utility operation, */ -/* so special values are unchanged and no error is possible. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopy(decNumber *dest, const decNumber *src) { - - #if DECCHECK - if (src==NULL) return uprv_decNumberZero(dest); - #endif - - if (dest==src) return dest; /* no copy required */ - - /* Use explicit assignments here as structure assignment could copy */ - /* more than just the lsu (for small DECDPUN). This would not affect */ - /* the value of the results, but could disturb test harness spill */ - /* checking. */ - dest->bits=src->bits; - dest->exponent=src->exponent; - dest->digits=src->digits; - dest->lsu[0]=src->lsu[0]; - if (src->digits>DECDPUN) { /* more Units to come */ - const Unit *smsup, *s; /* work */ - Unit *d; /* .. */ - /* memcpy for the remaining Units would be safe as they cannot */ - /* overlap. However, this explicit loop is faster in short cases. */ - d=dest->lsu+1; /* -> first destination */ - smsup=src->lsu+D2U(src->digits); /* -> source msu+1 */ - for (s=src->lsu+1; sdigits digits. */ -/* No exception or error can occur; this is a quiet bitwise operation.*/ -/* See also decNumberAbs for a checking version of this. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopyAbs(decNumber *res, const decNumber *rhs) { - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, DECUNCONT)) return res; - #endif - uprv_decNumberCopy(res, rhs); - res->bits&=~DECNEG; /* turn off sign */ - return res; - } /* decNumberCopyAbs */ - -/* ------------------------------------------------------------------ */ -/* decNumberCopyNegate -- quiet negate value operator */ -/* */ -/* This sets C = negate(A) */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* */ -/* C must have space for set->digits digits. */ -/* No exception or error can occur; this is a quiet bitwise operation.*/ -/* See also decNumberMinus for a checking version of this. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopyNegate(decNumber *res, const decNumber *rhs) { - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, DECUNCONT)) return res; - #endif - uprv_decNumberCopy(res, rhs); - res->bits^=DECNEG; /* invert the sign */ - return res; - } /* decNumberCopyNegate */ - -/* ------------------------------------------------------------------ */ -/* decNumberCopySign -- quiet copy and set sign operator */ -/* */ -/* This sets C = A with the sign of B */ -/* */ -/* res is C, the result. C may be A */ -/* lhs is A */ -/* rhs is B */ -/* */ -/* C must have space for set->digits digits. */ -/* No exception or error can occur; this is a quiet bitwise operation.*/ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopySign(decNumber *res, const decNumber *lhs, - const decNumber *rhs) { - uByte sign; /* rhs sign */ - #if DECCHECK - if (decCheckOperands(res, DECUNUSED, rhs, DECUNCONT)) return res; - #endif - sign=rhs->bits & DECNEG; /* save sign bit */ - uprv_decNumberCopy(res, lhs); - res->bits&=~DECNEG; /* clear the sign */ - res->bits|=sign; /* set from rhs */ - return res; - } /* decNumberCopySign */ - -/* ------------------------------------------------------------------ */ -/* decNumberGetBCD -- get the coefficient in BCD8 */ -/* dn is the source decNumber */ -/* bcd is the uInt array that will receive dn->digits BCD bytes, */ -/* most-significant at offset 0 */ -/* returns bcd */ -/* */ -/* bcd must have at least dn->digits bytes. No error is possible; if */ -/* dn is a NaN or Infinite, digits must be 1 and the coefficient 0. */ -/* ------------------------------------------------------------------ */ -U_CAPI uByte * U_EXPORT2 uprv_decNumberGetBCD(const decNumber *dn, uByte *bcd) { - uByte *ub=bcd+dn->digits-1; /* -> lsd */ - const Unit *up=dn->lsu; /* Unit pointer, -> lsu */ - - #if DECDPUN==1 /* trivial simple copy */ - for (; ub>=bcd; ub--, up++) *ub=*up; - #else /* chopping needed */ - uInt u=*up; /* work */ - uInt cut=DECDPUN; /* downcounter through unit */ - for (; ub>=bcd; ub--) { - *ub=(uByte)(u%10); /* [*6554 trick inhibits, here] */ - u=u/10; - cut--; - if (cut>0) continue; /* more in this unit */ - up++; - u=*up; - cut=DECDPUN; - } - #endif - return bcd; - } /* decNumberGetBCD */ - -/* ------------------------------------------------------------------ */ -/* decNumberSetBCD -- set (replace) the coefficient from BCD8 */ -/* dn is the target decNumber */ -/* bcd is the uInt array that will source n BCD bytes, most- */ -/* significant at offset 0 */ -/* n is the number of digits in the source BCD array (bcd) */ -/* returns dn */ -/* */ -/* dn must have space for at least n digits. No error is possible; */ -/* if dn is a NaN, or Infinite, or is to become a zero, n must be 1 */ -/* and bcd[0] zero. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberSetBCD(decNumber *dn, const uByte *bcd, uInt n) { - Unit *up=dn->lsu+D2U(dn->digits)-1; /* -> msu [target pointer] */ - const uByte *ub=bcd; /* -> source msd */ - - #if DECDPUN==1 /* trivial simple copy */ - for (; ub=dn->lsu; up--) { /* each Unit from msu */ - *up=0; /* will take <=DECDPUN digits */ - for (; cut>0; ub++, cut--) *up=X10(*up)+*ub; - cut=DECDPUN; /* next Unit has all digits */ - } - #endif - dn->digits=n; /* set digit count */ - return dn; - } /* decNumberSetBCD */ - -/* ------------------------------------------------------------------ */ -/* decNumberIsNormal -- test normality of a decNumber */ -/* dn is the decNumber to test */ -/* set is the context to use for Emin */ -/* returns 1 if |dn| is finite and >=Nmin, 0 otherwise */ -/* ------------------------------------------------------------------ */ -Int uprv_decNumberIsNormal(const decNumber *dn, decContext *set) { - Int ae; /* adjusted exponent */ - #if DECCHECK - if (decCheckOperands(DECUNRESU, DECUNUSED, dn, set)) return 0; - #endif - - if (decNumberIsSpecial(dn)) return 0; /* not finite */ - if (decNumberIsZero(dn)) return 0; /* not non-zero */ - - ae=dn->exponent+dn->digits-1; /* adjusted exponent */ - if (aeemin) return 0; /* is subnormal */ - return 1; - } /* decNumberIsNormal */ - -/* ------------------------------------------------------------------ */ -/* decNumberIsSubnormal -- test subnormality of a decNumber */ -/* dn is the decNumber to test */ -/* set is the context to use for Emin */ -/* returns 1 if |dn| is finite, non-zero, and exponent+dn->digits-1; /* adjusted exponent */ - if (aeemin) return 1; /* is subnormal */ - return 0; - } /* decNumberIsSubnormal */ - -/* ------------------------------------------------------------------ */ -/* decNumberTrim -- remove insignificant zeros */ -/* */ -/* dn is the number to trim */ -/* returns dn */ -/* */ -/* All fields are updated as required. This is a utility operation, */ -/* so special values are unchanged and no error is possible. The */ -/* zeros are removed unconditionally. */ -/* ------------------------------------------------------------------ */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberTrim(decNumber *dn) { - Int dropped; /* work */ - decContext set; /* .. */ - #if DECCHECK - if (decCheckOperands(DECUNRESU, DECUNUSED, dn, DECUNCONT)) return dn; - #endif - uprv_decContextDefault(&set, DEC_INIT_BASE); /* clamp=0 */ - return decTrim(dn, &set, 0, 1, &dropped); - } /* decNumberTrim */ - -/* ------------------------------------------------------------------ */ -/* decNumberVersion -- return the name and version of this module */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -const char * uprv_decNumberVersion(void) { - return DECVERSION; - } /* decNumberVersion */ - -/* ------------------------------------------------------------------ */ -/* decNumberZero -- set a number to 0 */ -/* */ -/* dn is the number to set, with space for one digit */ -/* returns dn */ -/* */ -/* No error is possible. */ -/* ------------------------------------------------------------------ */ -/* Memset is not used as it is much slower in some environments. */ -U_CAPI decNumber * U_EXPORT2 uprv_decNumberZero(decNumber *dn) { - - #if DECCHECK - if (decCheckOperands(dn, DECUNUSED, DECUNUSED, DECUNCONT)) return dn; - #endif - - dn->bits=0; - dn->exponent=0; - dn->digits=1; - dn->lsu[0]=0; - return dn; - } /* decNumberZero */ - -/* ================================================================== */ -/* Local routines */ -/* ================================================================== */ - -/* ------------------------------------------------------------------ */ -/* decToString -- lay out a number into a string */ -/* */ -/* dn is the number to lay out */ -/* string is where to lay out the number */ -/* eng is 1 if Engineering, 0 if Scientific */ -/* */ -/* string must be at least dn->digits+14 characters long */ -/* No error is possible. */ -/* */ -/* Note that this routine can generate a -0 or 0.000. These are */ -/* never generated in subset to-number or arithmetic, but can occur */ -/* in non-subset arithmetic (e.g., -1*0 or 1.234-1.234). */ -/* ------------------------------------------------------------------ */ -/* If DECCHECK is enabled the string "?" is returned if a number is */ -/* invalid. */ -static void decToString(const decNumber *dn, char *string, Flag eng) { - Int exp=dn->exponent; /* local copy */ - Int e; /* E-part value */ - Int pre; /* digits before the '.' */ - Int cut; /* for counting digits in a Unit */ - char *c=string; /* work [output pointer] */ - const Unit *up=dn->lsu+D2U(dn->digits)-1; /* -> msu [input pointer] */ - uInt u, pow; /* work */ - - #if DECCHECK - if (decCheckOperands(DECUNRESU, dn, DECUNUSED, DECUNCONT)) { - strcpy(string, "?"); - return;} - #endif - - if (decNumberIsNegative(dn)) { /* Negatives get a minus */ - *c='-'; - c++; - } - if (dn->bits&DECSPECIAL) { /* Is a special value */ - if (decNumberIsInfinite(dn)) { - strcpy(c, "Inf"); - strcpy(c+3, "inity"); - return;} - /* a NaN */ - if (dn->bits&DECSNAN) { /* signalling NaN */ - *c='s'; - c++; - } - strcpy(c, "NaN"); - c+=3; /* step past */ - /* if not a clean non-zero coefficient, that's all there is in a */ - /* NaN string */ - if (exp!=0 || (*dn->lsu==0 && dn->digits==1)) return; - /* [drop through to add integer] */ - } - - /* calculate how many digits in msu, and hence first cut */ - cut=MSUDIGITS(dn->digits); /* [faster than remainder] */ - cut--; /* power of ten for digit */ - - if (exp==0) { /* simple integer [common fastpath] */ - for (;up>=dn->lsu; up--) { /* each Unit from msu */ - u=*up; /* contains DECDPUN digits to lay out */ - for (; cut>=0; c++, cut--) TODIGIT(u, cut, c, pow); - cut=DECDPUN-1; /* next Unit has all digits */ - } - *c='\0'; /* terminate the string */ - return;} - - /* non-0 exponent -- assume plain form */ - pre=dn->digits+exp; /* digits before '.' */ - e=0; /* no E */ - if ((exp>0) || (pre<-5)) { /* need exponential form */ - e=exp+dn->digits-1; /* calculate E value */ - pre=1; /* assume one digit before '.' */ - if (eng && (e!=0)) { /* engineering: may need to adjust */ - Int adj; /* adjustment */ - /* The C remainder operator is undefined for negative numbers, so */ - /* a positive remainder calculation must be used here */ - if (e<0) { - adj=(-e)%3; - if (adj!=0) adj=3-adj; - } - else { /* e>0 */ - adj=e%3; - } - e=e-adj; - /* if dealing with zero still produce an exponent which is a */ - /* multiple of three, as expected, but there will only be the */ - /* one zero before the E, still. Otherwise note the padding. */ - if (!ISZERO(dn)) pre+=adj; - else { /* is zero */ - if (adj!=0) { /* 0.00Esnn needed */ - e=e+3; - pre=-(2-adj); - } - } /* zero */ - } /* eng */ - } /* need exponent */ - - /* lay out the digits of the coefficient, adding 0s and . as needed */ - u=*up; - if (pre>0) { /* xxx.xxx or xx00 (engineering) form */ - Int n=pre; - for (; pre>0; pre--, c++, cut--) { - if (cut<0) { /* need new Unit */ - if (up==dn->lsu) break; /* out of input digits (pre>digits) */ - up--; - cut=DECDPUN-1; - u=*up; - } - TODIGIT(u, cut, c, pow); - } - if (ndigits) { /* more to come, after '.' */ - *c='.'; c++; - for (;; c++, cut--) { - if (cut<0) { /* need new Unit */ - if (up==dn->lsu) break; /* out of input digits */ - up--; - cut=DECDPUN-1; - u=*up; - } - TODIGIT(u, cut, c, pow); - } - } - else for (; pre>0; pre--, c++) *c='0'; /* 0 padding (for engineering) needed */ - } - else { /* 0.xxx or 0.000xxx form */ - *c='0'; c++; - *c='.'; c++; - for (; pre<0; pre++, c++) *c='0'; /* add any 0's after '.' */ - for (; ; c++, cut--) { - if (cut<0) { /* need new Unit */ - if (up==dn->lsu) break; /* out of input digits */ - up--; - cut=DECDPUN-1; - u=*up; - } - TODIGIT(u, cut, c, pow); - } - } - - /* Finally add the E-part, if needed. It will never be 0, has a - base maximum and minimum of +999999999 through -999999999, but - could range down to -1999999998 for abnormal numbers */ - if (e!=0) { - Flag had=0; /* 1=had non-zero */ - *c='E'; c++; - *c='+'; c++; /* assume positive */ - u=e; /* .. */ - if (e<0) { - *(c-1)='-'; /* oops, need - */ - u=-e; /* uInt, please */ - } - /* lay out the exponent [_itoa or equivalent is not ANSI C] */ - for (cut=9; cut>=0; cut--) { - TODIGIT(u, cut, c, pow); - if (*c=='0' && !had) continue; /* skip leading zeros */ - had=1; /* had non-0 */ - c++; /* step for next */ - } /* cut */ - } - *c='\0'; /* terminate the string (all paths) */ - return; - } /* decToString */ - -/* ------------------------------------------------------------------ */ -/* decAddOp -- add/subtract operation */ -/* */ -/* This computes C = A + B */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X+X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* negate is DECNEG if rhs should be negated, or 0 otherwise */ -/* status accumulates status for the caller */ -/* */ -/* C must have space for set->digits digits. */ -/* Inexact in status must be 0 for correct Exact zero sign in result */ -/* ------------------------------------------------------------------ */ -/* If possible, the coefficient is calculated directly into C. */ -/* However, if: */ -/* -- a digits+1 calculation is needed because the numbers are */ -/* unaligned and span more than set->digits digits */ -/* -- a carry to digits+1 digits looks possible */ -/* -- C is the same as A or B, and the result would destructively */ -/* overlap the A or B coefficient */ -/* then the result must be calculated into a temporary buffer. In */ -/* this case a local (stack) buffer is used if possible, and only if */ -/* too long for that does malloc become the final resort. */ -/* */ -/* Misalignment is handled as follows: */ -/* Apad: (AExp>BExp) Swap operands and proceed as for BExp>AExp. */ -/* BPad: Apply the padding by a combination of shifting (whole */ -/* units) and multiplication (part units). */ -/* */ -/* Addition, especially x=x+1, is speed-critical. */ -/* The static buffer is larger than might be expected to allow for */ -/* calls from higher-level functions (notable exp). */ -/* ------------------------------------------------------------------ */ -static decNumber * decAddOp(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set, - uByte negate, uInt *status) { - #if DECSUBSET - decNumber *alloclhs=NULL; /* non-NULL if rounded lhs allocated */ - decNumber *allocrhs=NULL; /* .., rhs */ - #endif - Int rhsshift; /* working shift (in Units) */ - Int maxdigits; /* longest logical length */ - Int mult; /* multiplier */ - Int residue; /* rounding accumulator */ - uByte bits; /* result bits */ - Flag diffsign; /* non-0 if arguments have different sign */ - Unit *acc; /* accumulator for result */ - Unit accbuff[SD2U(DECBUFFER*2+20)]; /* local buffer [*2+20 reduces many */ - /* allocations when called from */ - /* other operations, notable exp] */ - Unit *allocacc=NULL; /* -> allocated acc buffer, iff allocated */ - Int reqdigits=set->digits; /* local copy; requested DIGITS */ - Int padding; /* work */ - - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - do { /* protect allocated storage */ - #if DECSUBSET - if (!set->extended) { - /* reduce operands and set lostDigits status, as needed */ - if (lhs->digits>reqdigits) { - alloclhs=decRoundOperand(lhs, set, status); - if (alloclhs==NULL) break; - lhs=alloclhs; - } - if (rhs->digits>reqdigits) { - allocrhs=decRoundOperand(rhs, set, status); - if (allocrhs==NULL) break; - rhs=allocrhs; - } - } - #endif - /* [following code does not require input rounding] */ - - /* note whether signs differ [used all paths] */ - diffsign=(Flag)((lhs->bits^rhs->bits^negate)&DECNEG); - - /* handle infinities and NaNs */ - if (SPECIALARGS) { /* a special bit set */ - if (SPECIALARGS & (DECSNAN | DECNAN)) /* a NaN */ - decNaNs(res, lhs, rhs, set, status); - else { /* one or two infinities */ - if (decNumberIsInfinite(lhs)) { /* LHS is infinity */ - /* two infinities with different signs is invalid */ - if (decNumberIsInfinite(rhs) && diffsign) { - *status|=DEC_Invalid_operation; - break; - } - bits=lhs->bits & DECNEG; /* get sign from LHS */ - } - else bits=(rhs->bits^negate) & DECNEG;/* RHS must be Infinity */ - bits|=DECINF; - uprv_decNumberZero(res); - res->bits=bits; /* set +/- infinity */ - } /* an infinity */ - break; - } - - /* Quick exit for add 0s; return the non-0, modified as need be */ - if (ISZERO(lhs)) { - Int adjust; /* work */ - Int lexp=lhs->exponent; /* save in case LHS==RES */ - bits=lhs->bits; /* .. */ - residue=0; /* clear accumulator */ - decCopyFit(res, rhs, set, &residue, status); /* copy (as needed) */ - res->bits^=negate; /* flip if rhs was negated */ - #if DECSUBSET - if (set->extended) { /* exponents on zeros count */ - #endif - /* exponent will be the lower of the two */ - adjust=lexp-res->exponent; /* adjustment needed [if -ve] */ - if (ISZERO(res)) { /* both 0: special IEEE 754 rules */ - if (adjust<0) res->exponent=lexp; /* set exponent */ - /* 0-0 gives +0 unless rounding to -infinity, and -0-0 gives -0 */ - if (diffsign) { - if (set->round!=DEC_ROUND_FLOOR) res->bits=0; - else res->bits=DECNEG; /* preserve 0 sign */ - } - } - else { /* non-0 res */ - if (adjust<0) { /* 0-padding needed */ - if ((res->digits-adjust)>set->digits) { - adjust=res->digits-set->digits; /* to fit exactly */ - *status|=DEC_Rounded; /* [but exact] */ - } - res->digits=decShiftToMost(res->lsu, res->digits, -adjust); - res->exponent+=adjust; /* set the exponent. */ - } - } /* non-0 res */ - #if DECSUBSET - } /* extended */ - #endif - decFinish(res, set, &residue, status); /* clean and finalize */ - break;} - - if (ISZERO(rhs)) { /* [lhs is non-zero] */ - Int adjust; /* work */ - Int rexp=rhs->exponent; /* save in case RHS==RES */ - bits=rhs->bits; /* be clean */ - residue=0; /* clear accumulator */ - decCopyFit(res, lhs, set, &residue, status); /* copy (as needed) */ - #if DECSUBSET - if (set->extended) { /* exponents on zeros count */ - #endif - /* exponent will be the lower of the two */ - /* [0-0 case handled above] */ - adjust=rexp-res->exponent; /* adjustment needed [if -ve] */ - if (adjust<0) { /* 0-padding needed */ - if ((res->digits-adjust)>set->digits) { - adjust=res->digits-set->digits; /* to fit exactly */ - *status|=DEC_Rounded; /* [but exact] */ - } - res->digits=decShiftToMost(res->lsu, res->digits, -adjust); - res->exponent+=adjust; /* set the exponent. */ - } - #if DECSUBSET - } /* extended */ - #endif - decFinish(res, set, &residue, status); /* clean and finalize */ - break;} - - /* [NB: both fastpath and mainpath code below assume these cases */ - /* (notably 0-0) have already been handled] */ - - /* calculate the padding needed to align the operands */ - padding=rhs->exponent-lhs->exponent; - - /* Fastpath cases where the numbers are aligned and normal, the RHS */ - /* is all in one unit, no operand rounding is needed, and no carry, */ - /* lengthening, or borrow is needed */ - if (padding==0 - && rhs->digits<=DECDPUN - && rhs->exponent>=set->emin /* [some normals drop through] */ - && rhs->exponent<=set->emax-set->digits+1 /* [could clamp] */ - && rhs->digits<=reqdigits - && lhs->digits<=reqdigits) { - Int partial=*lhs->lsu; - if (!diffsign) { /* adding */ - partial+=*rhs->lsu; - if ((partial<=DECDPUNMAX) /* result fits in unit */ - && (lhs->digits>=DECDPUN || /* .. and no digits-count change */ - partial<(Int)powers[lhs->digits])) { /* .. */ - if (res!=lhs) uprv_decNumberCopy(res, lhs); /* not in place */ - *res->lsu=(Unit)partial; /* [copy could have overwritten RHS] */ - break; - } - /* else drop out for careful add */ - } - else { /* signs differ */ - partial-=*rhs->lsu; - if (partial>0) { /* no borrow needed, and non-0 result */ - if (res!=lhs) uprv_decNumberCopy(res, lhs); /* not in place */ - *res->lsu=(Unit)partial; - /* this could have reduced digits [but result>0] */ - res->digits=decGetDigits(res->lsu, D2U(res->digits)); - break; - } - /* else drop out for careful subtract */ - } - } - - /* Now align (pad) the lhs or rhs so they can be added or */ - /* subtracted, as necessary. If one number is much larger than */ - /* the other (that is, if in plain form there is a least one */ - /* digit between the lowest digit of one and the highest of the */ - /* other) padding with up to DIGITS-1 trailing zeros may be */ - /* needed; then apply rounding (as exotic rounding modes may be */ - /* affected by the residue). */ - rhsshift=0; /* rhs shift to left (padding) in Units */ - bits=lhs->bits; /* assume sign is that of LHS */ - mult=1; /* likely multiplier */ - - /* [if padding==0 the operands are aligned; no padding is needed] */ - if (padding!=0) { - /* some padding needed; always pad the RHS, as any required */ - /* padding can then be effected by a simple combination of */ - /* shifts and a multiply */ - Flag swapped=0; - if (padding<0) { /* LHS needs the padding */ - const decNumber *t; - padding=-padding; /* will be +ve */ - bits=(uByte)(rhs->bits^negate); /* assumed sign is now that of RHS */ - t=lhs; lhs=rhs; rhs=t; - swapped=1; - } - - /* If, after pad, rhs would be longer than lhs by digits+1 or */ - /* more then lhs cannot affect the answer, except as a residue, */ - /* so only need to pad up to a length of DIGITS+1. */ - if (rhs->digits+padding > lhs->digits+reqdigits+1) { - /* The RHS is sufficient */ - /* for residue use the relative sign indication... */ - Int shift=reqdigits-rhs->digits; /* left shift needed */ - residue=1; /* residue for rounding */ - if (diffsign) residue=-residue; /* signs differ */ - /* copy, shortening if necessary */ - decCopyFit(res, rhs, set, &residue, status); - /* if it was already shorter, then need to pad with zeros */ - if (shift>0) { - res->digits=decShiftToMost(res->lsu, res->digits, shift); - res->exponent-=shift; /* adjust the exponent. */ - } - /* flip the result sign if unswapped and rhs was negated */ - if (!swapped) res->bits^=negate; - decFinish(res, set, &residue, status); /* done */ - break;} - - /* LHS digits may affect result */ - rhsshift=D2U(padding+1)-1; /* this much by Unit shift .. */ - mult=powers[padding-(rhsshift*DECDPUN)]; /* .. this by multiplication */ - } /* padding needed */ - - if (diffsign) mult=-mult; /* signs differ */ - - /* determine the longer operand */ - maxdigits=rhs->digits+padding; /* virtual length of RHS */ - if (lhs->digits>maxdigits) maxdigits=lhs->digits; - - /* Decide on the result buffer to use; if possible place directly */ - /* into result. */ - acc=res->lsu; /* assume add direct to result */ - /* If destructive overlap, or the number is too long, or a carry or */ - /* borrow to DIGITS+1 might be possible, a buffer must be used. */ - /* [Might be worth more sophisticated tests when maxdigits==reqdigits] */ - if ((maxdigits>=reqdigits) /* is, or could be, too large */ - || (res==rhs && rhsshift>0)) { /* destructive overlap */ - /* buffer needed, choose it; units for maxdigits digits will be */ - /* needed, +1 Unit for carry or borrow */ - Int need=D2U(maxdigits)+1; - acc=accbuff; /* assume use local buffer */ - if (need*sizeof(Unit)>sizeof(accbuff)) { - /* printf("malloc add %ld %ld\n", need, sizeof(accbuff)); */ - allocacc=(Unit *)malloc(need*sizeof(Unit)); - if (allocacc==NULL) { /* hopeless -- abandon */ - *status|=DEC_Insufficient_storage; - break;} - acc=allocacc; - } - } - - res->bits=(uByte)(bits&DECNEG); /* it's now safe to overwrite.. */ - res->exponent=lhs->exponent; /* .. operands (even if aliased) */ - - #if DECTRACE - decDumpAr('A', lhs->lsu, D2U(lhs->digits)); - decDumpAr('B', rhs->lsu, D2U(rhs->digits)); - printf(" :h: %ld %ld\n", rhsshift, mult); - #endif - - /* add [A+B*m] or subtract [A+B*(-m)] */ - U_ASSERT(rhs->digits > 0); - U_ASSERT(lhs->digits > 0); - res->digits=decUnitAddSub(lhs->lsu, D2U(lhs->digits), - rhs->lsu, D2U(rhs->digits), - rhsshift, acc, mult) - *DECDPUN; /* [units -> digits] */ - if (res->digits<0) { /* borrowed... */ - res->digits=-res->digits; - res->bits^=DECNEG; /* flip the sign */ - } - #if DECTRACE - decDumpAr('+', acc, D2U(res->digits)); - #endif - - /* If a buffer was used the result must be copied back, possibly */ - /* shortening. (If no buffer was used then the result must have */ - /* fit, so can't need rounding and residue must be 0.) */ - residue=0; /* clear accumulator */ - if (acc!=res->lsu) { - #if DECSUBSET - if (set->extended) { /* round from first significant digit */ - #endif - /* remove leading zeros that were added due to rounding up to */ - /* integral Units -- before the test for rounding. */ - if (res->digits>reqdigits) - res->digits=decGetDigits(acc, D2U(res->digits)); - decSetCoeff(res, set, acc, res->digits, &residue, status); - #if DECSUBSET - } - else { /* subset arithmetic rounds from original significant digit */ - /* May have an underestimate. This only occurs when both */ - /* numbers fit in DECDPUN digits and are padding with a */ - /* negative multiple (-10, -100...) and the top digit(s) become */ - /* 0. (This only matters when using X3.274 rules where the */ - /* leading zero could be included in the rounding.) */ - if (res->digitsdigits))=0; /* ensure leading 0 is there */ - res->digits=maxdigits; - } - else { - /* remove leading zeros that added due to rounding up to */ - /* integral Units (but only those in excess of the original */ - /* maxdigits length, unless extended) before test for rounding. */ - if (res->digits>reqdigits) { - res->digits=decGetDigits(acc, D2U(res->digits)); - if (res->digitsdigits=maxdigits; - } - } - decSetCoeff(res, set, acc, res->digits, &residue, status); - /* Now apply rounding if needed before removing leading zeros. */ - /* This is safe because subnormals are not a possibility */ - if (residue!=0) { - decApplyRound(res, set, residue, status); - residue=0; /* did what needed to be done */ - } - } /* subset */ - #endif - } /* used buffer */ - - /* strip leading zeros [these were left on in case of subset subtract] */ - res->digits=decGetDigits(res->lsu, D2U(res->digits)); - - /* apply checks and rounding */ - decFinish(res, set, &residue, status); - - /* "When the sum of two operands with opposite signs is exactly */ - /* zero, the sign of that sum shall be '+' in all rounding modes */ - /* except round toward -Infinity, in which mode that sign shall be */ - /* '-'." [Subset zeros also never have '-', set by decFinish.] */ - if (ISZERO(res) && diffsign - #if DECSUBSET - && set->extended - #endif - && (*status&DEC_Inexact)==0) { - if (set->round==DEC_ROUND_FLOOR) res->bits|=DECNEG; /* sign - */ - else res->bits&=~DECNEG; /* sign + */ - } - } while(0); /* end protected */ - - if (allocacc!=NULL) free(allocacc); /* drop any storage used */ - #if DECSUBSET - if (allocrhs!=NULL) free(allocrhs); /* .. */ - if (alloclhs!=NULL) free(alloclhs); /* .. */ - #endif - return res; - } /* decAddOp */ - -/* ------------------------------------------------------------------ */ -/* decDivideOp -- division operation */ -/* */ -/* This routine performs the calculations for all four division */ -/* operators (divide, divideInteger, remainder, remainderNear). */ -/* */ -/* C=A op B */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X/X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* op is DIVIDE, DIVIDEINT, REMAINDER, or REMNEAR respectively. */ -/* status is the usual accumulator */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* ------------------------------------------------------------------ */ -/* The underlying algorithm of this routine is the same as in the */ -/* 1981 S/370 implementation, that is, non-restoring long division */ -/* with bi-unit (rather than bi-digit) estimation for each unit */ -/* multiplier. In this pseudocode overview, complications for the */ -/* Remainder operators and division residues for exact rounding are */ -/* omitted for clarity. */ -/* */ -/* Prepare operands and handle special values */ -/* Test for x/0 and then 0/x */ -/* Exp =Exp1 - Exp2 */ -/* Exp =Exp +len(var1) -len(var2) */ -/* Sign=Sign1 * Sign2 */ -/* Pad accumulator (Var1) to double-length with 0's (pad1) */ -/* Pad Var2 to same length as Var1 */ -/* msu2pair/plus=1st 2 or 1 units of var2, +1 to allow for round */ -/* have=0 */ -/* Do until (have=digits+1 OR residue=0) */ -/* if exp<0 then if integer divide/residue then leave */ -/* this_unit=0 */ -/* Do forever */ -/* compare numbers */ -/* if <0 then leave inner_loop */ -/* if =0 then (* quick exit without subtract *) do */ -/* this_unit=this_unit+1; output this_unit */ -/* leave outer_loop; end */ -/* Compare lengths of numbers (mantissae): */ -/* If same then tops2=msu2pair -- {units 1&2 of var2} */ -/* else tops2=msu2plus -- {0, unit 1 of var2} */ -/* tops1=first_unit_of_Var1*10**DECDPUN +second_unit_of_var1 */ -/* mult=tops1/tops2 -- Good and safe guess at divisor */ -/* if mult=0 then mult=1 */ -/* this_unit=this_unit+mult */ -/* subtract */ -/* end inner_loop */ -/* if have\=0 | this_unit\=0 then do */ -/* output this_unit */ -/* have=have+1; end */ -/* var2=var2/10 */ -/* exp=exp-1 */ -/* end outer_loop */ -/* exp=exp+1 -- set the proper exponent */ -/* if have=0 then generate answer=0 */ -/* Return (Result is defined by Var1) */ -/* */ -/* ------------------------------------------------------------------ */ -/* Two working buffers are needed during the division; one (digits+ */ -/* 1) to accumulate the result, and the other (up to 2*digits+1) for */ -/* long subtractions. These are acc and var1 respectively. */ -/* var1 is a copy of the lhs coefficient, var2 is the rhs coefficient.*/ -/* The static buffers may be larger than might be expected to allow */ -/* for calls from higher-level functions (notable exp). */ -/* ------------------------------------------------------------------ */ -static decNumber * decDivideOp(decNumber *res, - const decNumber *lhs, const decNumber *rhs, - decContext *set, Flag op, uInt *status) { - #if DECSUBSET - decNumber *alloclhs=NULL; /* non-NULL if rounded lhs allocated */ - decNumber *allocrhs=NULL; /* .., rhs */ - #endif - Unit accbuff[SD2U(DECBUFFER+DECDPUN+10)]; /* local buffer */ - Unit *acc=accbuff; /* -> accumulator array for result */ - Unit *allocacc=NULL; /* -> allocated buffer, iff allocated */ - Unit *accnext; /* -> where next digit will go */ - Int acclength; /* length of acc needed [Units] */ - Int accunits; /* count of units accumulated */ - Int accdigits; /* count of digits accumulated */ - - Unit varbuff[SD2U(DECBUFFER*2+DECDPUN)]; /* buffer for var1 */ - Unit *var1=varbuff; /* -> var1 array for long subtraction */ - Unit *varalloc=NULL; /* -> allocated buffer, iff used */ - Unit *msu1; /* -> msu of var1 */ - - const Unit *var2; /* -> var2 array */ - const Unit *msu2; /* -> msu of var2 */ - Int msu2plus; /* msu2 plus one [does not vary] */ - eInt msu2pair; /* msu2 pair plus one [does not vary] */ - - Int var1units, var2units; /* actual lengths */ - Int var2ulen; /* logical length (units) */ - Int var1initpad=0; /* var1 initial padding (digits) */ - Int maxdigits; /* longest LHS or required acc length */ - Int mult; /* multiplier for subtraction */ - Unit thisunit; /* current unit being accumulated */ - Int residue; /* for rounding */ - Int reqdigits=set->digits; /* requested DIGITS */ - Int exponent; /* working exponent */ - Int maxexponent=0; /* DIVIDE maximum exponent if unrounded */ - uByte bits; /* working sign */ - Unit *target; /* work */ - const Unit *source; /* .. */ - uInt const *pow; /* .. */ - Int shift, cut; /* .. */ - #if DECSUBSET - Int dropped; /* work */ - #endif - - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - do { /* protect allocated storage */ - #if DECSUBSET - if (!set->extended) { - /* reduce operands and set lostDigits status, as needed */ - if (lhs->digits>reqdigits) { - alloclhs=decRoundOperand(lhs, set, status); - if (alloclhs==NULL) break; - lhs=alloclhs; - } - if (rhs->digits>reqdigits) { - allocrhs=decRoundOperand(rhs, set, status); - if (allocrhs==NULL) break; - rhs=allocrhs; - } - } - #endif - /* [following code does not require input rounding] */ - - bits=(lhs->bits^rhs->bits)&DECNEG; /* assumed sign for divisions */ - - /* handle infinities and NaNs */ - if (SPECIALARGS) { /* a special bit set */ - if (SPECIALARGS & (DECSNAN | DECNAN)) { /* one or two NaNs */ - decNaNs(res, lhs, rhs, set, status); - break; - } - /* one or two infinities */ - if (decNumberIsInfinite(lhs)) { /* LHS (dividend) is infinite */ - if (decNumberIsInfinite(rhs) || /* two infinities are invalid .. */ - op & (REMAINDER | REMNEAR)) { /* as is remainder of infinity */ - *status|=DEC_Invalid_operation; - break; - } - /* [Note that infinity/0 raises no exceptions] */ - uprv_decNumberZero(res); - res->bits=bits|DECINF; /* set +/- infinity */ - break; - } - else { /* RHS (divisor) is infinite */ - residue=0; - if (op&(REMAINDER|REMNEAR)) { - /* result is [finished clone of] lhs */ - decCopyFit(res, lhs, set, &residue, status); - } - else { /* a division */ - uprv_decNumberZero(res); - res->bits=bits; /* set +/- zero */ - /* for DIVIDEINT the exponent is always 0. For DIVIDE, result */ - /* is a 0 with infinitely negative exponent, clamped to minimum */ - if (op&DIVIDE) { - res->exponent=set->emin-set->digits+1; - *status|=DEC_Clamped; - } - } - decFinish(res, set, &residue, status); - break; - } - } - - /* handle 0 rhs (x/0) */ - if (ISZERO(rhs)) { /* x/0 is always exceptional */ - if (ISZERO(lhs)) { - uprv_decNumberZero(res); /* [after lhs test] */ - *status|=DEC_Division_undefined;/* 0/0 will become NaN */ - } - else { - uprv_decNumberZero(res); - if (op&(REMAINDER|REMNEAR)) *status|=DEC_Invalid_operation; - else { - *status|=DEC_Division_by_zero; /* x/0 */ - res->bits=bits|DECINF; /* .. is +/- Infinity */ - } - } - break;} - - /* handle 0 lhs (0/x) */ - if (ISZERO(lhs)) { /* 0/x [x!=0] */ - #if DECSUBSET - if (!set->extended) uprv_decNumberZero(res); - else { - #endif - if (op&DIVIDE) { - residue=0; - exponent=lhs->exponent-rhs->exponent; /* ideal exponent */ - uprv_decNumberCopy(res, lhs); /* [zeros always fit] */ - res->bits=bits; /* sign as computed */ - res->exponent=exponent; /* exponent, too */ - decFinalize(res, set, &residue, status); /* check exponent */ - } - else if (op&DIVIDEINT) { - uprv_decNumberZero(res); /* integer 0 */ - res->bits=bits; /* sign as computed */ - } - else { /* a remainder */ - exponent=rhs->exponent; /* [save in case overwrite] */ - uprv_decNumberCopy(res, lhs); /* [zeros always fit] */ - if (exponentexponent) res->exponent=exponent; /* use lower */ - } - #if DECSUBSET - } - #endif - break;} - - /* Precalculate exponent. This starts off adjusted (and hence fits */ - /* in 31 bits) and becomes the usual unadjusted exponent as the */ - /* division proceeds. The order of evaluation is important, here, */ - /* to avoid wrap. */ - exponent=(lhs->exponent+lhs->digits)-(rhs->exponent+rhs->digits); - - /* If the working exponent is -ve, then some quick exits are */ - /* possible because the quotient is known to be <1 */ - /* [for REMNEAR, it needs to be < -1, as -0.5 could need work] */ - if (exponent<0 && !(op==DIVIDE)) { - if (op&DIVIDEINT) { - uprv_decNumberZero(res); /* integer part is 0 */ - #if DECSUBSET - if (set->extended) - #endif - res->bits=bits; /* set +/- zero */ - break;} - /* fastpath remainders so long as the lhs has the smaller */ - /* (or equal) exponent */ - if (lhs->exponent<=rhs->exponent) { - if (op&REMAINDER || exponent<-1) { - /* It is REMAINDER or safe REMNEAR; result is [finished */ - /* clone of] lhs (r = x - 0*y) */ - residue=0; - decCopyFit(res, lhs, set, &residue, status); - decFinish(res, set, &residue, status); - break; - } - /* [unsafe REMNEAR drops through] */ - } - } /* fastpaths */ - - /* Long (slow) division is needed; roll up the sleeves... */ - - /* The accumulator will hold the quotient of the division. */ - /* If it needs to be too long for stack storage, then allocate. */ - acclength=D2U(reqdigits+DECDPUN); /* in Units */ - if (acclength*sizeof(Unit)>sizeof(accbuff)) { - /* printf("malloc dvacc %ld units\n", acclength); */ - allocacc=(Unit *)malloc(acclength*sizeof(Unit)); - if (allocacc==NULL) { /* hopeless -- abandon */ - *status|=DEC_Insufficient_storage; - break;} - acc=allocacc; /* use the allocated space */ - } - - /* var1 is the padded LHS ready for subtractions. */ - /* If it needs to be too long for stack storage, then allocate. */ - /* The maximum units needed for var1 (long subtraction) is: */ - /* Enough for */ - /* (rhs->digits+reqdigits-1) -- to allow full slide to right */ - /* or (lhs->digits) -- to allow for long lhs */ - /* whichever is larger */ - /* +1 -- for rounding of slide to right */ - /* +1 -- for leading 0s */ - /* +1 -- for pre-adjust if a remainder or DIVIDEINT */ - /* [Note: unused units do not participate in decUnitAddSub data] */ - maxdigits=rhs->digits+reqdigits-1; - if (lhs->digits>maxdigits) maxdigits=lhs->digits; - var1units=D2U(maxdigits)+2; - /* allocate a guard unit above msu1 for REMAINDERNEAR */ - if (!(op&DIVIDE)) var1units++; - if ((var1units+1)*sizeof(Unit)>sizeof(varbuff)) { - /* printf("malloc dvvar %ld units\n", var1units+1); */ - varalloc=(Unit *)malloc((var1units+1)*sizeof(Unit)); - if (varalloc==NULL) { /* hopeless -- abandon */ - *status|=DEC_Insufficient_storage; - break;} - var1=varalloc; /* use the allocated space */ - } - - /* Extend the lhs and rhs to full long subtraction length. The lhs */ - /* is truly extended into the var1 buffer, with 0 padding, so a */ - /* subtract in place is always possible. The rhs (var2) has */ - /* virtual padding (implemented by decUnitAddSub). */ - /* One guard unit was allocated above msu1 for rem=rem+rem in */ - /* REMAINDERNEAR. */ - msu1=var1+var1units-1; /* msu of var1 */ - source=lhs->lsu+D2U(lhs->digits)-1; /* msu of input array */ - for (target=msu1; source>=lhs->lsu; source--, target--) *target=*source; - for (; target>=var1; target--) *target=0; - - /* rhs (var2) is left-aligned with var1 at the start */ - var2ulen=var1units; /* rhs logical length (units) */ - var2units=D2U(rhs->digits); /* rhs actual length (units) */ - var2=rhs->lsu; /* -> rhs array */ - msu2=var2+var2units-1; /* -> msu of var2 [never changes] */ - /* now set up the variables which will be used for estimating the */ - /* multiplication factor. If these variables are not exact, add */ - /* 1 to make sure that the multiplier is never overestimated. */ - msu2plus=*msu2; /* it's value .. */ - if (var2units>1) msu2plus++; /* .. +1 if any more */ - msu2pair=(eInt)*msu2*(DECDPUNMAX+1);/* top two pair .. */ - if (var2units>1) { /* .. [else treat 2nd as 0] */ - msu2pair+=*(msu2-1); /* .. */ - if (var2units>2) msu2pair++; /* .. +1 if any more */ - } - - /* The calculation is working in units, which may have leading zeros, */ - /* but the exponent was calculated on the assumption that they are */ - /* both left-aligned. Adjust the exponent to compensate: add the */ - /* number of leading zeros in var1 msu and subtract those in var2 msu. */ - /* [This is actually done by counting the digits and negating, as */ - /* lead1=DECDPUN-digits1, and similarly for lead2.] */ - for (pow=&powers[1]; *msu1>=*pow; pow++) exponent--; - for (pow=&powers[1]; *msu2>=*pow; pow++) exponent++; - - /* Now, if doing an integer divide or remainder, ensure that */ - /* the result will be Unit-aligned. To do this, shift the var1 */ - /* accumulator towards least if need be. (It's much easier to */ - /* do this now than to reassemble the residue afterwards, if */ - /* doing a remainder.) Also ensure the exponent is not negative. */ - if (!(op&DIVIDE)) { - Unit *u; /* work */ - /* save the initial 'false' padding of var1, in digits */ - var1initpad=(var1units-D2U(lhs->digits))*DECDPUN; - /* Determine the shift to do. */ - if (exponent<0) cut=-exponent; - else cut=DECDPUN-exponent%DECDPUN; - decShiftToLeast(var1, var1units, cut); - exponent+=cut; /* maintain numerical value */ - var1initpad-=cut; /* .. and reduce padding */ - /* clean any most-significant units which were just emptied */ - for (u=msu1; cut>=DECDPUN; cut-=DECDPUN, u--) *u=0; - } /* align */ - else { /* is DIVIDE */ - maxexponent=lhs->exponent-rhs->exponent; /* save */ - /* optimization: if the first iteration will just produce 0, */ - /* preadjust to skip it [valid for DIVIDE only] */ - if (*msu1<*msu2) { - var2ulen--; /* shift down */ - exponent-=DECDPUN; /* update the exponent */ - } - } - - /* ---- start the long-division loops ------------------------------ */ - accunits=0; /* no units accumulated yet */ - accdigits=0; /* .. or digits */ - accnext=acc+acclength-1; /* -> msu of acc [NB: allows digits+1] */ - for (;;) { /* outer forever loop */ - thisunit=0; /* current unit assumed 0 */ - /* find the next unit */ - for (;;) { /* inner forever loop */ - /* strip leading zero units [from either pre-adjust or from */ - /* subtract last time around]. Leave at least one unit. */ - for (; *msu1==0 && msu1>var1; msu1--) var1units--; - - if (var1units msu */ - for (pv1=msu1; ; pv1--, pv2--) { - /* v1=*pv1 -- always OK */ - v2=0; /* assume in padding */ - if (pv2>=var2) v2=*pv2; /* in range */ - if (*pv1!=v2) break; /* no longer the same */ - if (pv1==var1) break; /* done; leave pv1 as is */ - } - /* here when all inspected or a difference seen */ - if (*pv1v2. Prepare for real subtraction; the lengths are equal */ - /* Estimate the multiplier (there's always a msu1-1)... */ - /* Bring in two units of var2 to provide a good estimate. */ - mult=(Int)(((eInt)*msu1*(DECDPUNMAX+1)+*(msu1-1))/msu2pair); - } /* lengths the same */ - else { /* var1units > var2ulen, so subtraction is safe */ - /* The var2 msu is one unit towards the lsu of the var1 msu, */ - /* so only one unit for var2 can be used. */ - mult=(Int)(((eInt)*msu1*(DECDPUNMAX+1)+*(msu1-1))/msu2plus); - } - if (mult==0) mult=1; /* must always be at least 1 */ - /* subtraction needed; var1 is > var2 */ - thisunit=(Unit)(thisunit+mult); /* accumulate */ - /* subtract var1-var2, into var1; only the overlap needs */ - /* processing, as this is an in-place calculation */ - shift=var2ulen-var2units; - #if DECTRACE - decDumpAr('1', &var1[shift], var1units-shift); - decDumpAr('2', var2, var2units); - printf("m=%ld\n", -mult); - #endif - decUnitAddSub(&var1[shift], var1units-shift, - var2, var2units, 0, - &var1[shift], -mult); - #if DECTRACE - decDumpAr('#', &var1[shift], var1units-shift); - #endif - /* var1 now probably has leading zeros; these are removed at the */ - /* top of the inner loop. */ - } /* inner loop */ - - /* The next unit has been calculated in full; unless it's a */ - /* leading zero, add to acc */ - if (accunits!=0 || thisunit!=0) { /* is first or non-zero */ - *accnext=thisunit; /* store in accumulator */ - /* account exactly for the new digits */ - if (accunits==0) { - accdigits++; /* at least one */ - for (pow=&powers[1]; thisunit>=*pow; pow++) accdigits++; - } - else accdigits+=DECDPUN; - accunits++; /* update count */ - accnext--; /* ready for next */ - if (accdigits>reqdigits) break; /* have enough digits */ - } - - /* if the residue is zero, the operation is done (unless divide */ - /* or divideInteger and still not enough digits yet) */ - if (*var1==0 && var1units==1) { /* residue is 0 */ - if (op&(REMAINDER|REMNEAR)) break; - if ((op&DIVIDE) && (exponent<=maxexponent)) break; - /* [drop through if divideInteger] */ - } - /* also done enough if calculating remainder or integer */ - /* divide and just did the last ('units') unit */ - if (exponent==0 && !(op&DIVIDE)) break; - - /* to get here, var1 is less than var2, so divide var2 by the per- */ - /* Unit power of ten and go for the next digit */ - var2ulen--; /* shift down */ - exponent-=DECDPUN; /* update the exponent */ - } /* outer loop */ - - /* ---- division is complete --------------------------------------- */ - /* here: acc has at least reqdigits+1 of good results (or fewer */ - /* if early stop), starting at accnext+1 (its lsu) */ - /* var1 has any residue at the stopping point */ - /* accunits is the number of digits collected in acc */ - if (accunits==0) { /* acc is 0 */ - accunits=1; /* show have a unit .. */ - accdigits=1; /* .. */ - *accnext=0; /* .. whose value is 0 */ - } - else accnext++; /* back to last placed */ - /* accnext now -> lowest unit of result */ - - residue=0; /* assume no residue */ - if (op&DIVIDE) { - /* record the presence of any residue, for rounding */ - if (*var1!=0 || var1units>1) residue=1; - else { /* no residue */ - /* Had an exact division; clean up spurious trailing 0s. */ - /* There will be at most DECDPUN-1, from the final multiply, */ - /* and then only if the result is non-0 (and even) and the */ - /* exponent is 'loose'. */ - #if DECDPUN>1 - Unit lsu=*accnext; - if (!(lsu&0x01) && (lsu!=0)) { - /* count the trailing zeros */ - Int drop=0; - for (;; drop++) { /* [will terminate because lsu!=0] */ - if (exponent>=maxexponent) break; /* don't chop real 0s */ - #if DECDPUN<=4 - if ((lsu-QUOT10(lsu, drop+1) - *powers[drop+1])!=0) break; /* found non-0 digit */ - #else - if (lsu%powers[drop+1]!=0) break; /* found non-0 digit */ - #endif - exponent++; - } - if (drop>0) { - accunits=decShiftToLeast(accnext, accunits, drop); - accdigits=decGetDigits(accnext, accunits); - accunits=D2U(accdigits); - /* [exponent was adjusted in the loop] */ - } - } /* neither odd nor 0 */ - #endif - } /* exact divide */ - } /* divide */ - else /* op!=DIVIDE */ { - /* check for coefficient overflow */ - if (accdigits+exponent>reqdigits) { - *status|=DEC_Division_impossible; - break; - } - if (op & (REMAINDER|REMNEAR)) { - /* [Here, the exponent will be 0, because var1 was adjusted */ - /* appropriately.] */ - Int postshift; /* work */ - Flag wasodd=0; /* integer was odd */ - Unit *quotlsu; /* for save */ - Int quotdigits; /* .. */ - - bits=lhs->bits; /* remainder sign is always as lhs */ - - /* Fastpath when residue is truly 0 is worthwhile [and */ - /* simplifies the code below] */ - if (*var1==0 && var1units==1) { /* residue is 0 */ - Int exp=lhs->exponent; /* save min(exponents) */ - if (rhs->exponentexponent; - uprv_decNumberZero(res); /* 0 coefficient */ - #if DECSUBSET - if (set->extended) - #endif - res->exponent=exp; /* .. with proper exponent */ - res->bits=(uByte)(bits&DECNEG); /* [cleaned] */ - decFinish(res, set, &residue, status); /* might clamp */ - break; - } - /* note if the quotient was odd */ - if (*accnext & 0x01) wasodd=1; /* acc is odd */ - quotlsu=accnext; /* save in case need to reinspect */ - quotdigits=accdigits; /* .. */ - - /* treat the residue, in var1, as the value to return, via acc */ - /* calculate the unused zero digits. This is the smaller of: */ - /* var1 initial padding (saved above) */ - /* var2 residual padding, which happens to be given by: */ - postshift=var1initpad+exponent-lhs->exponent+rhs->exponent; - /* [the 'exponent' term accounts for the shifts during divide] */ - if (var1initpadexponent; /* exponent is smaller of lhs & rhs */ - if (rhs->exponentexponent; - - /* Now correct the result if doing remainderNear; if it */ - /* (looking just at coefficients) is > rhs/2, or == rhs/2 and */ - /* the integer was odd then the result should be rem-rhs. */ - if (op&REMNEAR) { - Int compare, tarunits; /* work */ - Unit *up; /* .. */ - /* calculate remainder*2 into the var1 buffer (which has */ - /* 'headroom' of an extra unit and hence enough space) */ - /* [a dedicated 'double' loop would be faster, here] */ - tarunits=decUnitAddSub(accnext, accunits, accnext, accunits, - 0, accnext, 1); - /* decDumpAr('r', accnext, tarunits); */ - - /* Here, accnext (var1) holds tarunits Units with twice the */ - /* remainder's coefficient, which must now be compared to the */ - /* RHS. The remainder's exponent may be smaller than the RHS's. */ - compare=decUnitCompare(accnext, tarunits, rhs->lsu, D2U(rhs->digits), - rhs->exponent-exponent); - if (compare==BADINT) { /* deep trouble */ - *status|=DEC_Insufficient_storage; - break;} - - /* now restore the remainder by dividing by two; the lsu */ - /* is known to be even. */ - for (up=accnext; up0 || (compare==0 && wasodd)) { /* adjustment needed */ - Int exp, expunits, exprem; /* work */ - /* This is effectively causing round-up of the quotient, */ - /* so if it was the rare case where it was full and all */ - /* nines, it would overflow and hence division-impossible */ - /* should be raised */ - Flag allnines=0; /* 1 if quotient all nines */ - if (quotdigits==reqdigits) { /* could be borderline */ - for (up=quotlsu; ; up++) { - if (quotdigits>DECDPUN) { - if (*up!=DECDPUNMAX) break;/* non-nines */ - } - else { /* this is the last Unit */ - if (*up==powers[quotdigits]-1) allnines=1; - break; - } - quotdigits-=DECDPUN; /* checked those digits */ - } /* up */ - } /* borderline check */ - if (allnines) { - *status|=DEC_Division_impossible; - break;} - - /* rem-rhs is needed; the sign will invert. Again, var1 */ - /* can safely be used for the working Units array. */ - exp=rhs->exponent-exponent; /* RHS padding needed */ - /* Calculate units and remainder from exponent. */ - expunits=exp/DECDPUN; - exprem=exp%DECDPUN; - /* subtract [A+B*(-m)]; the result will always be negative */ - accunits=-decUnitAddSub(accnext, accunits, - rhs->lsu, D2U(rhs->digits), - expunits, accnext, -(Int)powers[exprem]); - accdigits=decGetDigits(accnext, accunits); /* count digits exactly */ - accunits=D2U(accdigits); /* and recalculate the units for copy */ - /* [exponent is as for original remainder] */ - bits^=DECNEG; /* flip the sign */ - } - } /* REMNEAR */ - } /* REMAINDER or REMNEAR */ - } /* not DIVIDE */ - - /* Set exponent and bits */ - res->exponent=exponent; - res->bits=(uByte)(bits&DECNEG); /* [cleaned] */ - - /* Now the coefficient. */ - decSetCoeff(res, set, accnext, accdigits, &residue, status); - - decFinish(res, set, &residue, status); /* final cleanup */ - - #if DECSUBSET - /* If a divide then strip trailing zeros if subset [after round] */ - if (!set->extended && (op==DIVIDE)) decTrim(res, set, 0, 1, &dropped); - #endif - } while(0); /* end protected */ - - if (varalloc!=NULL) free(varalloc); /* drop any storage used */ - if (allocacc!=NULL) free(allocacc); /* .. */ - #if DECSUBSET - if (allocrhs!=NULL) free(allocrhs); /* .. */ - if (alloclhs!=NULL) free(alloclhs); /* .. */ - #endif - return res; - } /* decDivideOp */ - -/* ------------------------------------------------------------------ */ -/* decMultiplyOp -- multiplication operation */ -/* */ -/* This routine performs the multiplication C=A x B. */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X*X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* status is the usual accumulator */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* ------------------------------------------------------------------ */ -/* 'Classic' multiplication is used rather than Karatsuba, as the */ -/* latter would give only a minor improvement for the short numbers */ -/* expected to be handled most (and uses much more memory). */ -/* */ -/* There are two major paths here: the general-purpose ('old code') */ -/* path which handles all DECDPUN values, and a fastpath version */ -/* which is used if 64-bit ints are available, DECDPUN<=4, and more */ -/* than two calls to decUnitAddSub would be made. */ -/* */ -/* The fastpath version lumps units together into 8-digit or 9-digit */ -/* chunks, and also uses a lazy carry strategy to minimise expensive */ -/* 64-bit divisions. The chunks are then broken apart again into */ -/* units for continuing processing. Despite this overhead, the */ -/* fastpath can speed up some 16-digit operations by 10x (and much */ -/* more for higher-precision calculations). */ -/* */ -/* A buffer always has to be used for the accumulator; in the */ -/* fastpath, buffers are also always needed for the chunked copies of */ -/* of the operand coefficients. */ -/* Static buffers are larger than needed just for multiply, to allow */ -/* for calls from other operations (notably exp). */ -/* ------------------------------------------------------------------ */ -#define FASTMUL (DECUSE64 && DECDPUN<5) -static decNumber * decMultiplyOp(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set, - uInt *status) { - Int accunits; /* Units of accumulator in use */ - Int exponent; /* work */ - Int residue=0; /* rounding residue */ - uByte bits; /* result sign */ - Unit *acc; /* -> accumulator Unit array */ - Int needbytes; /* size calculator */ - void *allocacc=NULL; /* -> allocated accumulator, iff allocated */ - Unit accbuff[SD2U(DECBUFFER*4+1)]; /* buffer (+1 for DECBUFFER==0, */ - /* *4 for calls from other operations) */ - const Unit *mer, *mermsup; /* work */ - Int madlength; /* Units in multiplicand */ - Int shift; /* Units to shift multiplicand by */ - - #if FASTMUL - /* if DECDPUN is 1 or 3 work in base 10**9, otherwise */ - /* (DECDPUN is 2 or 4) then work in base 10**8 */ - #if DECDPUN & 1 /* odd */ - #define FASTBASE 1000000000 /* base */ - #define FASTDIGS 9 /* digits in base */ - #define FASTLAZY 18 /* carry resolution point [1->18] */ - #else - #define FASTBASE 100000000 - #define FASTDIGS 8 - #define FASTLAZY 1844 /* carry resolution point [1->1844] */ - #endif - /* three buffers are used, two for chunked copies of the operands */ - /* (base 10**8 or base 10**9) and one base 2**64 accumulator with */ - /* lazy carry evaluation */ - uInt zlhibuff[(DECBUFFER*2+1)/8+1]; /* buffer (+1 for DECBUFFER==0) */ - uInt *zlhi=zlhibuff; /* -> lhs array */ - uInt *alloclhi=NULL; /* -> allocated buffer, iff allocated */ - uInt zrhibuff[(DECBUFFER*2+1)/8+1]; /* buffer (+1 for DECBUFFER==0) */ - uInt *zrhi=zrhibuff; /* -> rhs array */ - uInt *allocrhi=NULL; /* -> allocated buffer, iff allocated */ - uLong zaccbuff[(DECBUFFER*2+1)/4+2]; /* buffer (+1 for DECBUFFER==0) */ - /* [allocacc is shared for both paths, as only one will run] */ - uLong *zacc=zaccbuff; /* -> accumulator array for exact result */ - #if DECDPUN==1 - Int zoff; /* accumulator offset */ - #endif - uInt *lip, *rip; /* item pointers */ - uInt *lmsi, *rmsi; /* most significant items */ - Int ilhs, irhs, iacc; /* item counts in the arrays */ - Int lazy; /* lazy carry counter */ - uLong lcarry; /* uLong carry */ - uInt carry; /* carry (NB not uLong) */ - Int count; /* work */ - const Unit *cup; /* .. */ - Unit *up; /* .. */ - uLong *lp; /* .. */ - Int p; /* .. */ - #endif - - #if DECSUBSET - decNumber *alloclhs=NULL; /* -> allocated buffer, iff allocated */ - decNumber *allocrhs=NULL; /* -> allocated buffer, iff allocated */ - #endif - - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - /* precalculate result sign */ - bits=(uByte)((lhs->bits^rhs->bits)&DECNEG); - - /* handle infinities and NaNs */ - if (SPECIALARGS) { /* a special bit set */ - if (SPECIALARGS & (DECSNAN | DECNAN)) { /* one or two NaNs */ - decNaNs(res, lhs, rhs, set, status); - return res;} - /* one or two infinities; Infinity * 0 is invalid */ - if (((lhs->bits & DECINF)==0 && ISZERO(lhs)) - ||((rhs->bits & DECINF)==0 && ISZERO(rhs))) { - *status|=DEC_Invalid_operation; - return res;} - uprv_decNumberZero(res); - res->bits=bits|DECINF; /* infinity */ - return res;} - - /* For best speed, as in DMSRCN [the original Rexx numerics */ - /* module], use the shorter number as the multiplier (rhs) and */ - /* the longer as the multiplicand (lhs) to minimise the number of */ - /* adds (partial products) */ - if (lhs->digitsdigits) { /* swap... */ - const decNumber *hold=lhs; - lhs=rhs; - rhs=hold; - } - - do { /* protect allocated storage */ - #if DECSUBSET - if (!set->extended) { - /* reduce operands and set lostDigits status, as needed */ - if (lhs->digits>set->digits) { - alloclhs=decRoundOperand(lhs, set, status); - if (alloclhs==NULL) break; - lhs=alloclhs; - } - if (rhs->digits>set->digits) { - allocrhs=decRoundOperand(rhs, set, status); - if (allocrhs==NULL) break; - rhs=allocrhs; - } - } - #endif - /* [following code does not require input rounding] */ - - #if FASTMUL /* fastpath can be used */ - /* use the fast path if there are enough digits in the shorter */ - /* operand to make the setup and takedown worthwhile */ - #define NEEDTWO (DECDPUN*2) /* within two decUnitAddSub calls */ - if (rhs->digits>NEEDTWO) { /* use fastpath... */ - /* calculate the number of elements in each array */ - ilhs=(lhs->digits+FASTDIGS-1)/FASTDIGS; /* [ceiling] */ - irhs=(rhs->digits+FASTDIGS-1)/FASTDIGS; /* .. */ - iacc=ilhs+irhs; - - /* allocate buffers if required, as usual */ - needbytes=ilhs*sizeof(uInt); - if (needbytes>(Int)sizeof(zlhibuff)) { - alloclhi=(uInt *)malloc(needbytes); - zlhi=alloclhi;} - needbytes=irhs*sizeof(uInt); - if (needbytes>(Int)sizeof(zrhibuff)) { - allocrhi=(uInt *)malloc(needbytes); - zrhi=allocrhi;} - - /* Allocating the accumulator space needs a special case when */ - /* DECDPUN=1 because when converting the accumulator to Units */ - /* after the multiplication each 8-byte item becomes 9 1-byte */ - /* units. Therefore iacc extra bytes are needed at the front */ - /* (rounded up to a multiple of 8 bytes), and the uLong */ - /* accumulator starts offset the appropriate number of units */ - /* to the right to avoid overwrite during the unchunking. */ - - /* Make sure no signed int overflow below. This is always true */ - /* if the given numbers have less digits than DEC_MAX_DIGITS. */ - U_ASSERT((uint32_t)iacc <= INT32_MAX/sizeof(uLong)); - needbytes=iacc*sizeof(uLong); - #if DECDPUN==1 - zoff=(iacc+7)/8; /* items to offset by */ - needbytes+=zoff*8; - #endif - if (needbytes>(Int)sizeof(zaccbuff)) { - allocacc=(uLong *)malloc(needbytes); - zacc=(uLong *)allocacc;} - if (zlhi==NULL||zrhi==NULL||zacc==NULL) { - *status|=DEC_Insufficient_storage; - break;} - - acc=(Unit *)zacc; /* -> target Unit array */ - #if DECDPUN==1 - zacc+=zoff; /* start uLong accumulator to right */ - #endif - - /* assemble the chunked copies of the left and right sides */ - for (count=lhs->digits, cup=lhs->lsu, lip=zlhi; count>0; lip++) - for (p=0, *lip=0; p0; - p+=DECDPUN, cup++, count-=DECDPUN) - *lip+=*cup*powers[p]; - lmsi=lip-1; /* save -> msi */ - for (count=rhs->digits, cup=rhs->lsu, rip=zrhi; count>0; rip++) - for (p=0, *rip=0; p0; - p+=DECDPUN, cup++, count-=DECDPUN) - *rip+=*cup*powers[p]; - rmsi=rip-1; /* save -> msi */ - - /* zero the accumulator */ - for (lp=zacc; lp0 && rip!=rmsi) continue; - lazy=FASTLAZY; /* reset delay count */ - /* spin up the accumulator resolving overflows */ - for (lp=zacc; lp(up-acc); /* count of units */ - } - else { /* here to use units directly, without chunking ['old code'] */ - #endif - - /* if accumulator will be too long for local storage, then allocate */ - acc=accbuff; /* -> assume buffer for accumulator */ - needbytes=(D2U(lhs->digits)+D2U(rhs->digits))*sizeof(Unit); - if (needbytes>(Int)sizeof(accbuff)) { - allocacc=(Unit *)malloc(needbytes); - if (allocacc==NULL) {*status|=DEC_Insufficient_storage; break;} - acc=(Unit *)allocacc; /* use the allocated space */ - } - - /* Now the main long multiplication loop */ - /* Unlike the equivalent in the IBM Java implementation, there */ - /* is no advantage in calculating from msu to lsu. So, do it */ - /* by the book, as it were. */ - /* Each iteration calculates ACC=ACC+MULTAND*MULT */ - accunits=1; /* accumulator starts at '0' */ - *acc=0; /* .. (lsu=0) */ - shift=0; /* no multiplicand shift at first */ - madlength=D2U(lhs->digits); /* this won't change */ - mermsup=rhs->lsu+D2U(rhs->digits); /* -> msu+1 of multiplier */ - - for (mer=rhs->lsu; merlsu, madlength, 0, - &acc[shift], *mer) - + shift; - else { /* extend acc with a 0; it will be used shortly */ - *(acc+accunits)=0; /* [this avoids length of <=0 later] */ - accunits++; - } - /* multiply multiplicand by 10**DECDPUN for next Unit to left */ - shift++; /* add this for 'logical length' */ - } /* n */ - #if FASTMUL - } /* unchunked units */ - #endif - /* common end-path */ - #if DECTRACE - decDumpAr('*', acc, accunits); /* Show exact result */ - #endif - - /* acc now contains the exact result of the multiplication, */ - /* possibly with a leading zero unit; build the decNumber from */ - /* it, noting if any residue */ - res->bits=bits; /* set sign */ - res->digits=decGetDigits(acc, accunits); /* count digits exactly */ - - /* There can be a 31-bit wrap in calculating the exponent. */ - /* This can only happen if both input exponents are negative and */ - /* both their magnitudes are large. If there was a wrap, set a */ - /* safe very negative exponent, from which decFinalize() will */ - /* raise a hard underflow shortly. */ - exponent=lhs->exponent+rhs->exponent; /* calculate exponent */ - if (lhs->exponent<0 && rhs->exponent<0 && exponent>0) - exponent=-2*DECNUMMAXE; /* force underflow */ - res->exponent=exponent; /* OK to overwrite now */ - - - /* Set the coefficient. If any rounding, residue records */ - decSetCoeff(res, set, acc, res->digits, &residue, status); - decFinish(res, set, &residue, status); /* final cleanup */ - } while(0); /* end protected */ - - if (allocacc!=NULL) free(allocacc); /* drop any storage used */ - #if DECSUBSET - if (allocrhs!=NULL) free(allocrhs); /* .. */ - if (alloclhs!=NULL) free(alloclhs); /* .. */ - #endif - #if FASTMUL - if (allocrhi!=NULL) free(allocrhi); /* .. */ - if (alloclhi!=NULL) free(alloclhi); /* .. */ - #endif - return res; - } /* decMultiplyOp */ - -/* ------------------------------------------------------------------ */ -/* decExpOp -- effect exponentiation */ -/* */ -/* This computes C = exp(A) */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* set is the context; note that rounding mode has no effect */ -/* */ -/* C must have space for set->digits digits. status is updated but */ -/* not set. */ -/* */ -/* Restrictions: */ -/* */ -/* digits, emax, and -emin in the context must be less than */ -/* 2*DEC_MAX_MATH (1999998), and the rhs must be within these */ -/* bounds or a zero. This is an internal routine, so these */ -/* restrictions are contractual and not enforced. */ -/* */ -/* A finite result is rounded using DEC_ROUND_HALF_EVEN; it will */ -/* almost always be correctly rounded, but may be up to 1 ulp in */ -/* error in rare cases. */ -/* */ -/* Finite results will always be full precision and Inexact, except */ -/* when A is a zero or -Infinity (giving 1 or 0 respectively). */ -/* ------------------------------------------------------------------ */ -/* This approach used here is similar to the algorithm described in */ -/* */ -/* Variable Precision Exponential Function, T. E. Hull and */ -/* A. Abrham, ACM Transactions on Mathematical Software, Vol 12 #2, */ -/* pp79-91, ACM, June 1986. */ -/* */ -/* with the main difference being that the iterations in the series */ -/* evaluation are terminated dynamically (which does not require the */ -/* extra variable-precision variables which are expensive in this */ -/* context). */ -/* */ -/* The error analysis in Hull & Abrham's paper applies except for the */ -/* round-off error accumulation during the series evaluation. This */ -/* code does not precalculate the number of iterations and so cannot */ -/* use Horner's scheme. Instead, the accumulation is done at double- */ -/* precision, which ensures that the additions of the terms are exact */ -/* and do not accumulate round-off (and any round-off errors in the */ -/* terms themselves move 'to the right' faster than they can */ -/* accumulate). This code also extends the calculation by allowing, */ -/* in the spirit of other decNumber operators, the input to be more */ -/* precise than the result (the precision used is based on the more */ -/* precise of the input or requested result). */ -/* */ -/* Implementation notes: */ -/* */ -/* 1. This is separated out as decExpOp so it can be called from */ -/* other Mathematical functions (notably Ln) with a wider range */ -/* than normal. In particular, it can handle the slightly wider */ -/* (double) range needed by Ln (which has to be able to calculate */ -/* exp(-x) where x can be the tiniest number (Ntiny). */ -/* */ -/* 2. Normalizing x to be <=0.1 (instead of <=1) reduces loop */ -/* iterations by approximately a third with additional (although */ -/* diminishing) returns as the range is reduced to even smaller */ -/* fractions. However, h (the power of 10 used to correct the */ -/* result at the end, see below) must be kept <=8 as otherwise */ -/* the final result cannot be computed. Hence the leverage is a */ -/* sliding value (8-h), where potentially the range is reduced */ -/* more for smaller values. */ -/* */ -/* The leverage that can be applied in this way is severely */ -/* limited by the cost of the raise-to-the power at the end, */ -/* which dominates when the number of iterations is small (less */ -/* than ten) or when rhs is short. As an example, the adjustment */ -/* x**10,000,000 needs 31 multiplications, all but one full-width. */ -/* */ -/* 3. The restrictions (especially precision) could be raised with */ -/* care, but the full decNumber range seems very hard within the */ -/* 32-bit limits. */ -/* */ -/* 4. The working precisions for the static buffers are twice the */ -/* obvious size to allow for calls from decNumberPower. */ -/* ------------------------------------------------------------------ */ -decNumber * decExpOp(decNumber *res, const decNumber *rhs, - decContext *set, uInt *status) { - uInt ignore=0; /* working status */ - Int h; /* adjusted exponent for 0.xxxx */ - Int p; /* working precision */ - Int residue; /* rounding residue */ - uInt needbytes; /* for space calculations */ - const decNumber *x=rhs; /* (may point to safe copy later) */ - decContext aset, tset, dset; /* working contexts */ - Int comp; /* work */ - - /* the argument is often copied to normalize it, so (unusually) it */ - /* is treated like other buffers, using DECBUFFER, +1 in case */ - /* DECBUFFER is 0 */ - decNumber bufr[D2N(DECBUFFER*2+1)]; - decNumber *allocrhs=NULL; /* non-NULL if rhs buffer allocated */ - - /* the working precision will be no more than set->digits+8+1 */ - /* so for on-stack buffers DECBUFFER+9 is used, +1 in case DECBUFFER */ - /* is 0 (and twice that for the accumulator) */ - - /* buffer for t, term (working precision plus) */ - decNumber buft[D2N(DECBUFFER*2+9+1)]; - decNumber *allocbuft=NULL; /* -> allocated buft, iff allocated */ - decNumber *t=buft; /* term */ - /* buffer for a, accumulator (working precision * 2), at least 9 */ - decNumber bufa[D2N(DECBUFFER*4+18+1)]; - decNumber *allocbufa=NULL; /* -> allocated bufa, iff allocated */ - decNumber *a=bufa; /* accumulator */ - /* decNumber for the divisor term; this needs at most 9 digits */ - /* and so can be fixed size [16 so can use standard context] */ - decNumber bufd[D2N(16)]; - decNumber *d=bufd; /* divisor */ - decNumber numone; /* constant 1 */ - - #if DECCHECK - Int iterations=0; /* for later sanity check */ - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - do { /* protect allocated storage */ - if (SPECIALARG) { /* handle infinities and NaNs */ - if (decNumberIsInfinite(rhs)) { /* an infinity */ - if (decNumberIsNegative(rhs)) /* -Infinity -> +0 */ - uprv_decNumberZero(res); - else uprv_decNumberCopy(res, rhs); /* +Infinity -> self */ - } - else decNaNs(res, rhs, NULL, set, status); /* a NaN */ - break;} - - if (ISZERO(rhs)) { /* zeros -> exact 1 */ - uprv_decNumberZero(res); /* make clean 1 */ - *res->lsu=1; /* .. */ - break;} /* [no status to set] */ - - /* e**x when 0 < x < 0.66 is < 1+3x/2, hence can fast-path */ - /* positive and negative tiny cases which will result in inexact */ - /* 1. This also allows the later add-accumulate to always be */ - /* exact (because its length will never be more than twice the */ - /* working precision). */ - /* The comparator (tiny) needs just one digit, so use the */ - /* decNumber d for it (reused as the divisor, etc., below); its */ - /* exponent is such that if x is positive it will have */ - /* set->digits-1 zeros between the decimal point and the digit, */ - /* which is 4, and if x is negative one more zero there as the */ - /* more precise result will be of the form 0.9999999 rather than */ - /* 1.0000001. Hence, tiny will be 0.0000004 if digits=7 and x>0 */ - /* or 0.00000004 if digits=7 and x<0. If RHS not larger than */ - /* this then the result will be 1.000000 */ - uprv_decNumberZero(d); /* clean */ - *d->lsu=4; /* set 4 .. */ - d->exponent=-set->digits; /* * 10**(-d) */ - if (decNumberIsNegative(rhs)) d->exponent--; /* negative case */ - comp=decCompare(d, rhs, 1); /* signless compare */ - if (comp==BADINT) { - *status|=DEC_Insufficient_storage; - break;} - if (comp>=0) { /* rhs < d */ - Int shift=set->digits-1; - uprv_decNumberZero(res); /* set 1 */ - *res->lsu=1; /* .. */ - res->digits=decShiftToMost(res->lsu, 1, shift); - res->exponent=-shift; /* make 1.0000... */ - *status|=DEC_Inexact | DEC_Rounded; /* .. inexactly */ - break;} /* tiny */ - - /* set up the context to be used for calculating a, as this is */ - /* used on both paths below */ - uprv_decContextDefault(&aset, DEC_INIT_DECIMAL64); - /* accumulator bounds are as requested (could underflow) */ - aset.emax=set->emax; /* usual bounds */ - aset.emin=set->emin; /* .. */ - aset.clamp=0; /* and no concrete format */ - - /* calculate the adjusted (Hull & Abrham) exponent (where the */ - /* decimal point is just to the left of the coefficient msd) */ - h=rhs->exponent+rhs->digits; - /* if h>8 then 10**h cannot be calculated safely; however, when */ - /* h=8 then exp(|rhs|) will be at least exp(1E+7) which is at */ - /* least 6.59E+4342944, so (due to the restriction on Emax/Emin) */ - /* overflow (or underflow to 0) is guaranteed -- so this case can */ - /* be handled by simply forcing the appropriate excess */ - if (h>8) { /* overflow/underflow */ - /* set up here so Power call below will over or underflow to */ - /* zero; set accumulator to either 2 or 0.02 */ - /* [stack buffer for a is always big enough for this] */ - uprv_decNumberZero(a); - *a->lsu=2; /* not 1 but < exp(1) */ - if (decNumberIsNegative(rhs)) a->exponent=-2; /* make 0.02 */ - h=8; /* clamp so 10**h computable */ - p=9; /* set a working precision */ - } - else { /* h<=8 */ - Int maxlever=(rhs->digits>8?1:0); - /* [could/should increase this for precisions >40 or so, too] */ - - /* if h is 8, cannot normalize to a lower upper limit because */ - /* the final result will not be computable (see notes above), */ - /* but leverage can be applied whenever h is less than 8. */ - /* Apply as much as possible, up to a MAXLEVER digits, which */ - /* sets the tradeoff against the cost of the later a**(10**h). */ - /* As h is increased, the working precision below also */ - /* increases to compensate for the "constant digits at the */ - /* front" effect. */ - Int lever=MINI(8-h, maxlever); /* leverage attainable */ - Int use=-rhs->digits-lever; /* exponent to use for RHS */ - h+=lever; /* apply leverage selected */ - if (h<0) { /* clamp */ - use+=h; /* [may end up subnormal] */ - h=0; - } - /* Take a copy of RHS if it needs normalization (true whenever x>=1) */ - if (rhs->exponent!=use) { - decNumber *newrhs=bufr; /* assume will fit on stack */ - needbytes=sizeof(decNumber)+(D2U(rhs->digits)-1)*sizeof(Unit); - if (needbytes>sizeof(bufr)) { /* need malloc space */ - allocrhs=(decNumber *)malloc(needbytes); - if (allocrhs==NULL) { /* hopeless -- abandon */ - *status|=DEC_Insufficient_storage; - break;} - newrhs=allocrhs; /* use the allocated space */ - } - uprv_decNumberCopy(newrhs, rhs); /* copy to safe space */ - newrhs->exponent=use; /* normalize; now <1 */ - x=newrhs; /* ready for use */ - /* decNumberShow(x); */ - } - - /* Now use the usual power series to evaluate exp(x). The */ - /* series starts as 1 + x + x^2/2 ... so prime ready for the */ - /* third term by setting the term variable t=x, the accumulator */ - /* a=1, and the divisor d=2. */ - - /* First determine the working precision. From Hull & Abrham */ - /* this is set->digits+h+2. However, if x is 'over-precise' we */ - /* need to allow for all its digits to potentially participate */ - /* (consider an x where all the excess digits are 9s) so in */ - /* this case use x->digits+h+2 */ - p=MAXI(x->digits, set->digits)+h+2; /* [h<=8] */ - - /* a and t are variable precision, and depend on p, so space */ - /* must be allocated for them if necessary */ - - /* the accumulator needs to be able to hold 2p digits so that */ - /* the additions on the second and subsequent iterations are */ - /* sufficiently exact. */ - needbytes=sizeof(decNumber)+(D2U(p*2)-1)*sizeof(Unit); - if (needbytes>sizeof(bufa)) { /* need malloc space */ - allocbufa=(decNumber *)malloc(needbytes); - if (allocbufa==NULL) { /* hopeless -- abandon */ - *status|=DEC_Insufficient_storage; - break;} - a=allocbufa; /* use the allocated space */ - } - /* the term needs to be able to hold p digits (which is */ - /* guaranteed to be larger than x->digits, so the initial copy */ - /* is safe); it may also be used for the raise-to-power */ - /* calculation below, which needs an extra two digits */ - needbytes=sizeof(decNumber)+(D2U(p+2)-1)*sizeof(Unit); - if (needbytes>sizeof(buft)) { /* need malloc space */ - allocbuft=(decNumber *)malloc(needbytes); - if (allocbuft==NULL) { /* hopeless -- abandon */ - *status|=DEC_Insufficient_storage; - break;} - t=allocbuft; /* use the allocated space */ - } - - uprv_decNumberCopy(t, x); /* term=x */ - uprv_decNumberZero(a); *a->lsu=1; /* accumulator=1 */ - uprv_decNumberZero(d); *d->lsu=2; /* divisor=2 */ - uprv_decNumberZero(&numone); *numone.lsu=1; /* constant 1 for increment */ - - /* set up the contexts for calculating a, t, and d */ - uprv_decContextDefault(&tset, DEC_INIT_DECIMAL64); - dset=tset; - /* accumulator bounds are set above, set precision now */ - aset.digits=p*2; /* double */ - /* term bounds avoid any underflow or overflow */ - tset.digits=p; - tset.emin=DEC_MIN_EMIN; /* [emax is plenty] */ - /* [dset.digits=16, etc., are sufficient] */ - - /* finally ready to roll */ - for (;;) { - #if DECCHECK - iterations++; - #endif - /* only the status from the accumulation is interesting */ - /* [but it should remain unchanged after first add] */ - decAddOp(a, a, t, &aset, 0, status); /* a=a+t */ - decMultiplyOp(t, t, x, &tset, &ignore); /* t=t*x */ - decDivideOp(t, t, d, &tset, DIVIDE, &ignore); /* t=t/d */ - /* the iteration ends when the term cannot affect the result, */ - /* if rounded to p digits, which is when its value is smaller */ - /* than the accumulator by p+1 digits. There must also be */ - /* full precision in a. */ - if (((a->digits+a->exponent)>=(t->digits+t->exponent+p+1)) - && (a->digits>=p)) break; - decAddOp(d, d, &numone, &dset, 0, &ignore); /* d=d+1 */ - } /* iterate */ - - #if DECCHECK - /* just a sanity check; comment out test to show always */ - if (iterations>p+3) - printf("Exp iterations=%ld, status=%08lx, p=%ld, d=%ld\n", - (LI)iterations, (LI)*status, (LI)p, (LI)x->digits); - #endif - } /* h<=8 */ - - /* apply postconditioning: a=a**(10**h) -- this is calculated */ - /* at a slightly higher precision than Hull & Abrham suggest */ - if (h>0) { - Int seenbit=0; /* set once a 1-bit is seen */ - Int i; /* counter */ - Int n=powers[h]; /* always positive */ - aset.digits=p+2; /* sufficient precision */ - /* avoid the overhead and many extra digits of decNumberPower */ - /* as all that is needed is the short 'multipliers' loop; here */ - /* accumulate the answer into t */ - uprv_decNumberZero(t); *t->lsu=1; /* acc=1 */ - for (i=1;;i++){ /* for each bit [top bit ignored] */ - /* abandon if have had overflow or terminal underflow */ - if (*status & (DEC_Overflow|DEC_Underflow)) { /* interesting? */ - if (*status&DEC_Overflow || ISZERO(t)) break;} - n=n<<1; /* move next bit to testable position */ - if (n<0) { /* top bit is set */ - seenbit=1; /* OK, have a significant bit */ - decMultiplyOp(t, t, a, &aset, status); /* acc=acc*x */ - } - if (i==31) break; /* that was the last bit */ - if (!seenbit) continue; /* no need to square 1 */ - decMultiplyOp(t, t, t, &aset, status); /* acc=acc*acc [square] */ - } /*i*/ /* 32 bits */ - /* decNumberShow(t); */ - a=t; /* and carry on using t instead of a */ - } - - /* Copy and round the result to res */ - residue=1; /* indicate dirt to right .. */ - if (ISZERO(a)) residue=0; /* .. unless underflowed to 0 */ - aset.digits=set->digits; /* [use default rounding] */ - decCopyFit(res, a, &aset, &residue, status); /* copy & shorten */ - decFinish(res, set, &residue, status); /* cleanup/set flags */ - } while(0); /* end protected */ - - if (allocrhs !=NULL) free(allocrhs); /* drop any storage used */ - if (allocbufa!=NULL) free(allocbufa); /* .. */ - if (allocbuft!=NULL) free(allocbuft); /* .. */ - /* [status is handled by caller] */ - return res; - } /* decExpOp */ - -/* ------------------------------------------------------------------ */ -/* Initial-estimate natural logarithm table */ -/* */ -/* LNnn -- 90-entry 16-bit table for values from .10 through .99. */ -/* The result is a 4-digit encode of the coefficient (c=the */ -/* top 14 bits encoding 0-9999) and a 2-digit encode of the */ -/* exponent (e=the bottom 2 bits encoding 0-3) */ -/* */ -/* The resulting value is given by: */ -/* */ -/* v = -c * 10**(-e-3) */ -/* */ -/* where e and c are extracted from entry k = LNnn[x-10] */ -/* where x is truncated (NB) into the range 10 through 99, */ -/* and then c = k>>2 and e = k&3. */ -/* ------------------------------------------------------------------ */ -static const uShort LNnn[90]={9016, 8652, 8316, 8008, 7724, 7456, 7208, - 6972, 6748, 6540, 6340, 6148, 5968, 5792, 5628, 5464, 5312, - 5164, 5020, 4884, 4748, 4620, 4496, 4376, 4256, 4144, 4032, - 39233, 38181, 37157, 36157, 35181, 34229, 33297, 32389, 31501, 30629, - 29777, 28945, 28129, 27329, 26545, 25777, 25021, 24281, 23553, 22837, - 22137, 21445, 20769, 20101, 19445, 18801, 18165, 17541, 16925, 16321, - 15721, 15133, 14553, 13985, 13421, 12865, 12317, 11777, 11241, 10717, - 10197, 9685, 9177, 8677, 8185, 7697, 7213, 6737, 6269, 5801, - 5341, 4889, 4437, 39930, 35534, 31186, 26886, 22630, 18418, 14254, - 10130, 6046, 20055}; - -/* ------------------------------------------------------------------ */ -/* decLnOp -- effect natural logarithm */ -/* */ -/* This computes C = ln(A) */ -/* */ -/* res is C, the result. C may be A */ -/* rhs is A */ -/* set is the context; note that rounding mode has no effect */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* Notable cases: */ -/* A<0 -> Invalid */ -/* A=0 -> -Infinity (Exact) */ -/* A=+Infinity -> +Infinity (Exact) */ -/* A=1 exactly -> 0 (Exact) */ -/* */ -/* Restrictions (as for Exp): */ -/* */ -/* digits, emax, and -emin in the context must be less than */ -/* DEC_MAX_MATH+11 (1000010), and the rhs must be within these */ -/* bounds or a zero. This is an internal routine, so these */ -/* restrictions are contractual and not enforced. */ -/* */ -/* A finite result is rounded using DEC_ROUND_HALF_EVEN; it will */ -/* almost always be correctly rounded, but may be up to 1 ulp in */ -/* error in rare cases. */ -/* ------------------------------------------------------------------ */ -/* The result is calculated using Newton's method, with each */ -/* iteration calculating a' = a + x * exp(-a) - 1. See, for example, */ -/* Epperson 1989. */ -/* */ -/* The iteration ends when the adjustment x*exp(-a)-1 is tiny enough. */ -/* This has to be calculated at the sum of the precision of x and the */ -/* working precision. */ -/* */ -/* Implementation notes: */ -/* */ -/* 1. This is separated out as decLnOp so it can be called from */ -/* other Mathematical functions (e.g., Log 10) with a wider range */ -/* than normal. In particular, it can handle the slightly wider */ -/* (+9+2) range needed by a power function. */ -/* */ -/* 2. The speed of this function is about 10x slower than exp, as */ -/* it typically needs 4-6 iterations for short numbers, and the */ -/* extra precision needed adds a squaring effect, twice. */ -/* */ -/* 3. Fastpaths are included for ln(10) and ln(2), up to length 40, */ -/* as these are common requests. ln(10) is used by log10(x). */ -/* */ -/* 4. An iteration might be saved by widening the LNnn table, and */ -/* would certainly save at least one if it were made ten times */ -/* bigger, too (for truncated fractions 0.100 through 0.999). */ -/* However, for most practical evaluations, at least four or five */ -/* iterations will be needed -- so this would only speed up by */ -/* 20-25% and that probably does not justify increasing the table */ -/* size. */ -/* */ -/* 5. The static buffers are larger than might be expected to allow */ -/* for calls from decNumberPower. */ -/* ------------------------------------------------------------------ */ -#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 406 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" -#endif -decNumber * decLnOp(decNumber *res, const decNumber *rhs, - decContext *set, uInt *status) { - uInt ignore=0; /* working status accumulator */ - uInt needbytes; /* for space calculations */ - Int residue; /* rounding residue */ - Int r; /* rhs=f*10**r [see below] */ - Int p; /* working precision */ - Int pp; /* precision for iteration */ - Int t; /* work */ - - /* buffers for a (accumulator, typically precision+2) and b */ - /* (adjustment calculator, same size) */ - decNumber bufa[D2N(DECBUFFER+12)]; - decNumber *allocbufa=NULL; /* -> allocated bufa, iff allocated */ - decNumber *a=bufa; /* accumulator/work */ - decNumber bufb[D2N(DECBUFFER*2+2)]; - decNumber *allocbufb=NULL; /* -> allocated bufa, iff allocated */ - decNumber *b=bufb; /* adjustment/work */ - - decNumber numone; /* constant 1 */ - decNumber cmp; /* work */ - decContext aset, bset; /* working contexts */ - - #if DECCHECK - Int iterations=0; /* for later sanity check */ - if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; - #endif - - do { /* protect allocated storage */ - if (SPECIALARG) { /* handle infinities and NaNs */ - if (decNumberIsInfinite(rhs)) { /* an infinity */ - if (decNumberIsNegative(rhs)) /* -Infinity -> error */ - *status|=DEC_Invalid_operation; - else uprv_decNumberCopy(res, rhs); /* +Infinity -> self */ - } - else decNaNs(res, rhs, NULL, set, status); /* a NaN */ - break;} - - if (ISZERO(rhs)) { /* +/- zeros -> -Infinity */ - uprv_decNumberZero(res); /* make clean */ - res->bits=DECINF|DECNEG; /* set - infinity */ - break;} /* [no status to set] */ - - /* Non-zero negatives are bad... */ - if (decNumberIsNegative(rhs)) { /* -x -> error */ - *status|=DEC_Invalid_operation; - break;} - - /* Here, rhs is positive, finite, and in range */ - - /* lookaside fastpath code for ln(2) and ln(10) at common lengths */ - if (rhs->exponent==0 && set->digits<=40) { - #if DECDPUN==1 - if (rhs->lsu[0]==0 && rhs->lsu[1]==1 && rhs->digits==2) { /* ln(10) */ - #else - if (rhs->lsu[0]==10 && rhs->digits==2) { /* ln(10) */ - #endif - aset=*set; aset.round=DEC_ROUND_HALF_EVEN; - #define LN10 "2.302585092994045684017991454684364207601" - uprv_decNumberFromString(res, LN10, &aset); - *status|=(DEC_Inexact | DEC_Rounded); /* is inexact */ - break;} - if (rhs->lsu[0]==2 && rhs->digits==1) { /* ln(2) */ - aset=*set; aset.round=DEC_ROUND_HALF_EVEN; - #define LN2 "0.6931471805599453094172321214581765680755" - uprv_decNumberFromString(res, LN2, &aset); - *status|=(DEC_Inexact | DEC_Rounded); - break;} - } /* integer and short */ - - /* Determine the working precision. This is normally the */ - /* requested precision + 2, with a minimum of 9. However, if */ - /* the rhs is 'over-precise' then allow for all its digits to */ - /* potentially participate (consider an rhs where all the excess */ - /* digits are 9s) so in this case use rhs->digits+2. */ - p=MAXI(rhs->digits, MAXI(set->digits, 7))+2; - - /* Allocate space for the accumulator and the high-precision */ - /* adjustment calculator, if necessary. The accumulator must */ - /* be able to hold p digits, and the adjustment up to */ - /* rhs->digits+p digits. They are also made big enough for 16 */ - /* digits so that they can be used for calculating the initial */ - /* estimate. */ - needbytes=sizeof(decNumber)+(D2U(MAXI(p,16))-1)*sizeof(Unit); - if (needbytes>sizeof(bufa)) { /* need malloc space */ - allocbufa=(decNumber *)malloc(needbytes); - if (allocbufa==NULL) { /* hopeless -- abandon */ - *status|=DEC_Insufficient_storage; - break;} - a=allocbufa; /* use the allocated space */ - } - pp=p+rhs->digits; - needbytes=sizeof(decNumber)+(D2U(MAXI(pp,16))-1)*sizeof(Unit); - if (needbytes>sizeof(bufb)) { /* need malloc space */ - allocbufb=(decNumber *)malloc(needbytes); - if (allocbufb==NULL) { /* hopeless -- abandon */ - *status|=DEC_Insufficient_storage; - break;} - b=allocbufb; /* use the allocated space */ - } - - /* Prepare an initial estimate in acc. Calculate this by */ - /* considering the coefficient of x to be a normalized fraction, */ - /* f, with the decimal point at far left and multiplied by */ - /* 10**r. Then, rhs=f*10**r and 0.1<=f<1, and */ - /* ln(x) = ln(f) + ln(10)*r */ - /* Get the initial estimate for ln(f) from a small lookup */ - /* table (see above) indexed by the first two digits of f, */ - /* truncated. */ - - uprv_decContextDefault(&aset, DEC_INIT_DECIMAL64); /* 16-digit extended */ - r=rhs->exponent+rhs->digits; /* 'normalised' exponent */ - uprv_decNumberFromInt32(a, r); /* a=r */ - uprv_decNumberFromInt32(b, 2302585); /* b=ln(10) (2.302585) */ - b->exponent=-6; /* .. */ - decMultiplyOp(a, a, b, &aset, &ignore); /* a=a*b */ - /* now get top two digits of rhs into b by simple truncate and */ - /* force to integer */ - residue=0; /* (no residue) */ - aset.digits=2; aset.round=DEC_ROUND_DOWN; - decCopyFit(b, rhs, &aset, &residue, &ignore); /* copy & shorten */ - b->exponent=0; /* make integer */ - t=decGetInt(b); /* [cannot fail] */ - if (t<10) t=X10(t); /* adjust single-digit b */ - t=LNnn[t-10]; /* look up ln(b) */ - uprv_decNumberFromInt32(b, t>>2); /* b=ln(b) coefficient */ - b->exponent=-(t&3)-3; /* set exponent */ - b->bits=DECNEG; /* ln(0.10)->ln(0.99) always -ve */ - aset.digits=16; aset.round=DEC_ROUND_HALF_EVEN; /* restore */ - decAddOp(a, a, b, &aset, 0, &ignore); /* acc=a+b */ - /* the initial estimate is now in a, with up to 4 digits correct. */ - /* When rhs is at or near Nmax the estimate will be low, so we */ - /* will approach it from below, avoiding overflow when calling exp. */ - - uprv_decNumberZero(&numone); *numone.lsu=1; /* constant 1 for adjustment */ - - /* accumulator bounds are as requested (could underflow, but */ - /* cannot overflow) */ - aset.emax=set->emax; - aset.emin=set->emin; - aset.clamp=0; /* no concrete format */ - /* set up a context to be used for the multiply and subtract */ - bset=aset; - bset.emax=DEC_MAX_MATH*2; /* use double bounds for the */ - bset.emin=-DEC_MAX_MATH*2; /* adjustment calculation */ - /* [see decExpOp call below] */ - /* for each iteration double the number of digits to calculate, */ - /* up to a maximum of p */ - pp=9; /* initial precision */ - /* [initially 9 as then the sequence starts 7+2, 16+2, and */ - /* 34+2, which is ideal for standard-sized numbers] */ - aset.digits=pp; /* working context */ - bset.digits=pp+rhs->digits; /* wider context */ - for (;;) { /* iterate */ - #if DECCHECK - iterations++; - if (iterations>24) break; /* consider 9 * 2**24 */ - #endif - /* calculate the adjustment (exp(-a)*x-1) into b. This is a */ - /* catastrophic subtraction but it really is the difference */ - /* from 1 that is of interest. */ - /* Use the internal entry point to Exp as it allows the double */ - /* range for calculating exp(-a) when a is the tiniest subnormal. */ - a->bits^=DECNEG; /* make -a */ - decExpOp(b, a, &bset, &ignore); /* b=exp(-a) */ - a->bits^=DECNEG; /* restore sign of a */ - /* now multiply by rhs and subtract 1, at the wider precision */ - decMultiplyOp(b, b, rhs, &bset, &ignore); /* b=b*rhs */ - decAddOp(b, b, &numone, &bset, DECNEG, &ignore); /* b=b-1 */ - - /* the iteration ends when the adjustment cannot affect the */ - /* result by >=0.5 ulp (at the requested digits), which */ - /* is when its value is smaller than the accumulator by */ - /* set->digits+1 digits (or it is zero) -- this is a looser */ - /* requirement than for Exp because all that happens to the */ - /* accumulator after this is the final rounding (but note that */ - /* there must also be full precision in a, or a=0). */ - - if (decNumberIsZero(b) || - (a->digits+a->exponent)>=(b->digits+b->exponent+set->digits+1)) { - if (a->digits==p) break; - if (decNumberIsZero(a)) { - decCompareOp(&cmp, rhs, &numone, &aset, COMPARE, &ignore); /* rhs=1 ? */ - if (cmp.lsu[0]==0) a->exponent=0; /* yes, exact 0 */ - else *status|=(DEC_Inexact | DEC_Rounded); /* no, inexact */ - break; - } - /* force padding if adjustment has gone to 0 before full length */ - if (decNumberIsZero(b)) b->exponent=a->exponent-p; - } - - /* not done yet ... */ - decAddOp(a, a, b, &aset, 0, &ignore); /* a=a+b for next estimate */ - if (pp==p) continue; /* precision is at maximum */ - /* lengthen the next calculation */ - pp=pp*2; /* double precision */ - if (pp>p) pp=p; /* clamp to maximum */ - aset.digits=pp; /* working context */ - bset.digits=pp+rhs->digits; /* wider context */ - } /* Newton's iteration */ - - #if DECCHECK - /* just a sanity check; remove the test to show always */ - if (iterations>24) - printf("Ln iterations=%ld, status=%08lx, p=%ld, d=%ld\n", - (LI)iterations, (LI)*status, (LI)p, (LI)rhs->digits); - #endif - - /* Copy and round the result to res */ - residue=1; /* indicate dirt to right */ - if (ISZERO(a)) residue=0; /* .. unless underflowed to 0 */ - aset.digits=set->digits; /* [use default rounding] */ - decCopyFit(res, a, &aset, &residue, status); /* copy & shorten */ - decFinish(res, set, &residue, status); /* cleanup/set flags */ - } while(0); /* end protected */ - - if (allocbufa!=NULL) free(allocbufa); /* drop any storage used */ - if (allocbufb!=NULL) free(allocbufb); /* .. */ - /* [status is handled by caller] */ - return res; - } /* decLnOp */ -#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 406 -#pragma GCC diagnostic pop -#endif - -/* ------------------------------------------------------------------ */ -/* decQuantizeOp -- force exponent to requested value */ -/* */ -/* This computes C = op(A, B), where op adjusts the coefficient */ -/* of C (by rounding or shifting) such that the exponent (-scale) */ -/* of C has the value B or matches the exponent of B. */ -/* The numerical value of C will equal A, except for the effects of */ -/* any rounding that occurred. */ -/* */ -/* res is C, the result. C may be A or B */ -/* lhs is A, the number to adjust */ -/* rhs is B, the requested exponent */ -/* set is the context */ -/* quant is 1 for quantize or 0 for rescale */ -/* status is the status accumulator (this can be called without */ -/* risk of control loss) */ -/* */ -/* C must have space for set->digits digits. */ -/* */ -/* Unless there is an error or the result is infinite, the exponent */ -/* after the operation is guaranteed to be that requested. */ -/* ------------------------------------------------------------------ */ -static decNumber * decQuantizeOp(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set, - Flag quant, uInt *status) { - #if DECSUBSET - decNumber *alloclhs=NULL; /* non-NULL if rounded lhs allocated */ - decNumber *allocrhs=NULL; /* .., rhs */ - #endif - const decNumber *inrhs=rhs; /* save original rhs */ - Int reqdigits=set->digits; /* requested DIGITS */ - Int reqexp; /* requested exponent [-scale] */ - Int residue=0; /* rounding residue */ - Int etiny=set->emin-(reqdigits-1); - - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - do { /* protect allocated storage */ - #if DECSUBSET - if (!set->extended) { - /* reduce operands and set lostDigits status, as needed */ - if (lhs->digits>reqdigits) { - alloclhs=decRoundOperand(lhs, set, status); - if (alloclhs==NULL) break; - lhs=alloclhs; - } - if (rhs->digits>reqdigits) { /* [this only checks lostDigits] */ - allocrhs=decRoundOperand(rhs, set, status); - if (allocrhs==NULL) break; - rhs=allocrhs; - } - } - #endif - /* [following code does not require input rounding] */ - - /* Handle special values */ - if (SPECIALARGS) { - /* NaNs get usual processing */ - if (SPECIALARGS & (DECSNAN | DECNAN)) - decNaNs(res, lhs, rhs, set, status); - /* one infinity but not both is bad */ - else if ((lhs->bits ^ rhs->bits) & DECINF) - *status|=DEC_Invalid_operation; - /* both infinity: return lhs */ - else uprv_decNumberCopy(res, lhs); /* [nop if in place] */ - break; - } - - /* set requested exponent */ - if (quant) reqexp=inrhs->exponent; /* quantize -- match exponents */ - else { /* rescale -- use value of rhs */ - /* Original rhs must be an integer that fits and is in range, */ - /* which could be from -1999999997 to +999999999, thanks to */ - /* subnormals */ - reqexp=decGetInt(inrhs); /* [cannot fail] */ - } - - #if DECSUBSET - if (!set->extended) etiny=set->emin; /* no subnormals */ - #endif - - if (reqexp==BADINT /* bad (rescale only) or .. */ - || reqexp==BIGODD || reqexp==BIGEVEN /* very big (ditto) or .. */ - || (reqexpset->emax)) { /* > emax */ - *status|=DEC_Invalid_operation; - break;} - - /* the RHS has been processed, so it can be overwritten now if necessary */ - if (ISZERO(lhs)) { /* zero coefficient unchanged */ - uprv_decNumberCopy(res, lhs); /* [nop if in place] */ - res->exponent=reqexp; /* .. just set exponent */ - #if DECSUBSET - if (!set->extended) res->bits=0; /* subset specification; no -0 */ - #endif - } - else { /* non-zero lhs */ - Int adjust=reqexp-lhs->exponent; /* digit adjustment needed */ - /* if adjusted coefficient will definitely not fit, give up now */ - if ((lhs->digits-adjust)>reqdigits) { - *status|=DEC_Invalid_operation; - break; - } - - if (adjust>0) { /* increasing exponent */ - /* this will decrease the length of the coefficient by adjust */ - /* digits, and must round as it does so */ - decContext workset; /* work */ - workset=*set; /* clone rounding, etc. */ - workset.digits=lhs->digits-adjust; /* set requested length */ - /* [note that the latter can be <1, here] */ - decCopyFit(res, lhs, &workset, &residue, status); /* fit to result */ - decApplyRound(res, &workset, residue, status); /* .. and round */ - residue=0; /* [used] */ - /* If just rounded a 999s case, exponent will be off by one; */ - /* adjust back (after checking space), if so. */ - if (res->exponent>reqexp) { - /* re-check needed, e.g., for quantize(0.9999, 0.001) under */ - /* set->digits==3 */ - if (res->digits==reqdigits) { /* cannot shift by 1 */ - *status&=~(DEC_Inexact | DEC_Rounded); /* [clean these] */ - *status|=DEC_Invalid_operation; - break; - } - res->digits=decShiftToMost(res->lsu, res->digits, 1); /* shift */ - res->exponent--; /* (re)adjust the exponent. */ - } - #if DECSUBSET - if (ISZERO(res) && !set->extended) res->bits=0; /* subset; no -0 */ - #endif - } /* increase */ - else /* adjust<=0 */ { /* decreasing or = exponent */ - /* this will increase the length of the coefficient by -adjust */ - /* digits, by adding zero or more trailing zeros; this is */ - /* already checked for fit, above */ - uprv_decNumberCopy(res, lhs); /* [it will fit] */ - /* if padding needed (adjust<0), add it now... */ - if (adjust<0) { - res->digits=decShiftToMost(res->lsu, res->digits, -adjust); - res->exponent+=adjust; /* adjust the exponent */ - } - } /* decrease */ - } /* non-zero */ - - /* Check for overflow [do not use Finalize in this case, as an */ - /* overflow here is a "don't fit" situation] */ - if (res->exponent>set->emax-res->digits+1) { /* too big */ - *status|=DEC_Invalid_operation; - break; - } - else { - decFinalize(res, set, &residue, status); /* set subnormal flags */ - *status&=~DEC_Underflow; /* suppress Underflow [as per 754] */ - } - } while(0); /* end protected */ - - #if DECSUBSET - if (allocrhs!=NULL) free(allocrhs); /* drop any storage used */ - if (alloclhs!=NULL) free(alloclhs); /* .. */ - #endif - return res; - } /* decQuantizeOp */ - -/* ------------------------------------------------------------------ */ -/* decCompareOp -- compare, min, or max two Numbers */ -/* */ -/* This computes C = A ? B and carries out one of four operations: */ -/* COMPARE -- returns the signum (as a number) giving the */ -/* result of a comparison unless one or both */ -/* operands is a NaN (in which case a NaN results) */ -/* COMPSIG -- as COMPARE except that a quiet NaN raises */ -/* Invalid operation. */ -/* COMPMAX -- returns the larger of the operands, using the */ -/* 754 maxnum operation */ -/* COMPMAXMAG -- ditto, comparing absolute values */ -/* COMPMIN -- the 754 minnum operation */ -/* COMPMINMAG -- ditto, comparing absolute values */ -/* COMTOTAL -- returns the signum (as a number) giving the */ -/* result of a comparison using 754 total ordering */ -/* */ -/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ -/* lhs is A */ -/* rhs is B */ -/* set is the context */ -/* op is the operation flag */ -/* status is the usual accumulator */ -/* */ -/* C must have space for one digit for COMPARE or set->digits for */ -/* COMPMAX, COMPMIN, COMPMAXMAG, or COMPMINMAG. */ -/* ------------------------------------------------------------------ */ -/* The emphasis here is on speed for common cases, and avoiding */ -/* coefficient comparison if possible. */ -/* ------------------------------------------------------------------ */ -static decNumber * decCompareOp(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set, - Flag op, uInt *status) { - #if DECSUBSET - decNumber *alloclhs=NULL; /* non-NULL if rounded lhs allocated */ - decNumber *allocrhs=NULL; /* .., rhs */ - #endif - Int result=0; /* default result value */ - uByte merged; /* work */ - - #if DECCHECK - if (decCheckOperands(res, lhs, rhs, set)) return res; - #endif - - do { /* protect allocated storage */ - #if DECSUBSET - if (!set->extended) { - /* reduce operands and set lostDigits status, as needed */ - if (lhs->digits>set->digits) { - alloclhs=decRoundOperand(lhs, set, status); - if (alloclhs==NULL) {result=BADINT; break;} - lhs=alloclhs; - } - if (rhs->digits>set->digits) { - allocrhs=decRoundOperand(rhs, set, status); - if (allocrhs==NULL) {result=BADINT; break;} - rhs=allocrhs; - } - } - #endif - /* [following code does not require input rounding] */ - - /* If total ordering then handle differing signs 'up front' */ - if (op==COMPTOTAL) { /* total ordering */ - if (decNumberIsNegative(lhs) && !decNumberIsNegative(rhs)) { - result=-1; - break; - } - if (!decNumberIsNegative(lhs) && decNumberIsNegative(rhs)) { - result=+1; - break; - } - } - - /* handle NaNs specially; let infinities drop through */ - /* This assumes sNaN (even just one) leads to NaN. */ - merged=(lhs->bits | rhs->bits) & (DECSNAN | DECNAN); - if (merged) { /* a NaN bit set */ - if (op==COMPARE); /* result will be NaN */ - else if (op==COMPSIG) /* treat qNaN as sNaN */ - *status|=DEC_Invalid_operation | DEC_sNaN; - else if (op==COMPTOTAL) { /* total ordering, always finite */ - /* signs are known to be the same; compute the ordering here */ - /* as if the signs are both positive, then invert for negatives */ - if (!decNumberIsNaN(lhs)) result=-1; - else if (!decNumberIsNaN(rhs)) result=+1; - /* here if both NaNs */ - else if (decNumberIsSNaN(lhs) && decNumberIsQNaN(rhs)) result=-1; - else if (decNumberIsQNaN(lhs) && decNumberIsSNaN(rhs)) result=+1; - else { /* both NaN or both sNaN */ - /* now it just depends on the payload */ - result=decUnitCompare(lhs->lsu, D2U(lhs->digits), - rhs->lsu, D2U(rhs->digits), 0); - /* [Error not possible, as these are 'aligned'] */ - } /* both same NaNs */ - if (decNumberIsNegative(lhs)) result=-result; - break; - } /* total order */ - - else if (merged & DECSNAN); /* sNaN -> qNaN */ - else { /* here if MIN or MAX and one or two quiet NaNs */ - /* min or max -- 754 rules ignore single NaN */ - if (!decNumberIsNaN(lhs) || !decNumberIsNaN(rhs)) { - /* just one NaN; force choice to be the non-NaN operand */ - op=COMPMAX; - if (lhs->bits & DECNAN) result=-1; /* pick rhs */ - else result=+1; /* pick lhs */ - break; - } - } /* max or min */ - op=COMPNAN; /* use special path */ - decNaNs(res, lhs, rhs, set, status); /* propagate NaN */ - break; - } - /* have numbers */ - if (op==COMPMAXMAG || op==COMPMINMAG) result=decCompare(lhs, rhs, 1); - else result=decCompare(lhs, rhs, 0); /* sign matters */ - } while(0); /* end protected */ - - if (result==BADINT) *status|=DEC_Insufficient_storage; /* rare */ - else { - if (op==COMPARE || op==COMPSIG ||op==COMPTOTAL) { /* returning signum */ - if (op==COMPTOTAL && result==0) { - /* operands are numerically equal or same NaN (and same sign, */ - /* tested first); if identical, leave result 0 */ - if (lhs->exponent!=rhs->exponent) { - if (lhs->exponentexponent) result=-1; - else result=+1; - if (decNumberIsNegative(lhs)) result=-result; - } /* lexp!=rexp */ - } /* total-order by exponent */ - uprv_decNumberZero(res); /* [always a valid result] */ - if (result!=0) { /* must be -1 or +1 */ - *res->lsu=1; - if (result<0) res->bits=DECNEG; - } - } - else if (op==COMPNAN); /* special, drop through */ - else { /* MAX or MIN, non-NaN result */ - Int residue=0; /* rounding accumulator */ - /* choose the operand for the result */ - const decNumber *choice; - if (result==0) { /* operands are numerically equal */ - /* choose according to sign then exponent (see 754) */ - uByte slhs=(lhs->bits & DECNEG); - uByte srhs=(rhs->bits & DECNEG); - #if DECSUBSET - if (!set->extended) { /* subset: force left-hand */ - op=COMPMAX; - result=+1; - } - else - #endif - if (slhs!=srhs) { /* signs differ */ - if (slhs) result=-1; /* rhs is max */ - else result=+1; /* lhs is max */ - } - else if (slhs && srhs) { /* both negative */ - if (lhs->exponentexponent) result=+1; - else result=-1; - /* [if equal, use lhs, technically identical] */ - } - else { /* both positive */ - if (lhs->exponent>rhs->exponent) result=+1; - else result=-1; - /* [ditto] */ - } - } /* numerically equal */ - /* here result will be non-0; reverse if looking for MIN */ - if (op==COMPMIN || op==COMPMINMAG) result=-result; - choice=(result>0 ? lhs : rhs); /* choose */ - /* copy chosen to result, rounding if need be */ - decCopyFit(res, choice, set, &residue, status); - decFinish(res, set, &residue, status); - } - } - #if DECSUBSET - if (allocrhs!=NULL) free(allocrhs); /* free any storage used */ - if (alloclhs!=NULL) free(alloclhs); /* .. */ - #endif - return res; - } /* decCompareOp */ - -/* ------------------------------------------------------------------ */ -/* decCompare -- compare two decNumbers by numerical value */ -/* */ -/* This routine compares A ? B without altering them. */ -/* */ -/* Arg1 is A, a decNumber which is not a NaN */ -/* Arg2 is B, a decNumber which is not a NaN */ -/* Arg3 is 1 for a sign-independent compare, 0 otherwise */ -/* */ -/* returns -1, 0, or 1 for AB, or BADINT if failure */ -/* (the only possible failure is an allocation error) */ -/* ------------------------------------------------------------------ */ -static Int decCompare(const decNumber *lhs, const decNumber *rhs, - Flag abs_c) { - Int result; /* result value */ - Int sigr; /* rhs signum */ - Int compare; /* work */ - - result=1; /* assume signum(lhs) */ - if (ISZERO(lhs)) result=0; - if (abs_c) { - if (ISZERO(rhs)) return result; /* LHS wins or both 0 */ - /* RHS is non-zero */ - if (result==0) return -1; /* LHS is 0; RHS wins */ - /* [here, both non-zero, result=1] */ - } - else { /* signs matter */ - if (result && decNumberIsNegative(lhs)) result=-1; - sigr=1; /* compute signum(rhs) */ - if (ISZERO(rhs)) sigr=0; - else if (decNumberIsNegative(rhs)) sigr=-1; - if (result > sigr) return +1; /* L > R, return 1 */ - if (result < sigr) return -1; /* L < R, return -1 */ - if (result==0) return 0; /* both 0 */ - } - - /* signums are the same; both are non-zero */ - if ((lhs->bits | rhs->bits) & DECINF) { /* one or more infinities */ - if (decNumberIsInfinite(rhs)) { - if (decNumberIsInfinite(lhs)) result=0;/* both infinite */ - else result=-result; /* only rhs infinite */ - } - return result; - } - /* must compare the coefficients, allowing for exponents */ - if (lhs->exponent>rhs->exponent) { /* LHS exponent larger */ - /* swap sides, and sign */ - const decNumber *temp=lhs; - lhs=rhs; - rhs=temp; - result=-result; - } - compare=decUnitCompare(lhs->lsu, D2U(lhs->digits), - rhs->lsu, D2U(rhs->digits), - rhs->exponent-lhs->exponent); - if (compare!=BADINT) compare*=result; /* comparison succeeded */ - return compare; - } /* decCompare */ - -/* ------------------------------------------------------------------ */ -/* decUnitCompare -- compare two >=0 integers in Unit arrays */ -/* */ -/* This routine compares A ? B*10**E where A and B are unit arrays */ -/* A is a plain integer */ -/* B has an exponent of E (which must be non-negative) */ -/* */ -/* Arg1 is A first Unit (lsu) */ -/* Arg2 is A length in Units */ -/* Arg3 is B first Unit (lsu) */ -/* Arg4 is B length in Units */ -/* Arg5 is E (0 if the units are aligned) */ -/* */ -/* returns -1, 0, or 1 for AB, or BADINT if failure */ -/* (the only possible failure is an allocation error, which can */ -/* only occur if E!=0) */ -/* ------------------------------------------------------------------ */ -static Int decUnitCompare(const Unit *a, Int alength, - const Unit *b, Int blength, Int exp) { - Unit *acc; /* accumulator for result */ - Unit accbuff[SD2U(DECBUFFER*2+1)]; /* local buffer */ - Unit *allocacc=NULL; /* -> allocated acc buffer, iff allocated */ - Int accunits, need; /* units in use or needed for acc */ - const Unit *l, *r, *u; /* work */ - Int expunits, exprem, result; /* .. */ - - if (exp==0) { /* aligned; fastpath */ - if (alength>blength) return 1; - if (alength=a; l--, r--) { - if (*l>*r) return 1; - if (*l<*r) return -1; - } - return 0; /* all units match */ - } /* aligned */ - - /* Unaligned. If one is >1 unit longer than the other, padded */ - /* approximately, then can return easily */ - if (alength>blength+(Int)D2U(exp)) return 1; - if (alength+1sizeof(accbuff)) { - allocacc=(Unit *)malloc(need*sizeof(Unit)); - if (allocacc==NULL) return BADINT; /* hopeless -- abandon */ - acc=allocacc; - } - /* Calculate units and remainder from exponent. */ - expunits=exp/DECDPUN; - exprem=exp%DECDPUN; - /* subtract [A+B*(-m)] */ - accunits=decUnitAddSub(a, alength, b, blength, expunits, acc, - -(Int)powers[exprem]); - /* [UnitAddSub result may have leading zeros, even on zero] */ - if (accunits<0) result=-1; /* negative result */ - else { /* non-negative result */ - /* check units of the result before freeing any storage */ - for (u=acc; u=0 integers in Unit arrays */ -/* */ -/* This routine performs the calculation: */ -/* */ -/* C=A+(B*M) */ -/* */ -/* Where M is in the range -DECDPUNMAX through +DECDPUNMAX. */ -/* */ -/* A may be shorter or longer than B. */ -/* */ -/* Leading zeros are not removed after a calculation. The result is */ -/* either the same length as the longer of A and B (adding any */ -/* shift), or one Unit longer than that (if a Unit carry occurred). */ -/* */ -/* A and B content are not altered unless C is also A or B. */ -/* C may be the same array as A or B, but only if no zero padding is */ -/* requested (that is, C may be B only if bshift==0). */ -/* C is filled from the lsu; only those units necessary to complete */ -/* the calculation are referenced. */ -/* */ -/* Arg1 is A first Unit (lsu) */ -/* Arg2 is A length in Units */ -/* Arg3 is B first Unit (lsu) */ -/* Arg4 is B length in Units */ -/* Arg5 is B shift in Units (>=0; pads with 0 units if positive) */ -/* Arg6 is C first Unit (lsu) */ -/* Arg7 is M, the multiplier */ -/* */ -/* returns the count of Units written to C, which will be non-zero */ -/* and negated if the result is negative. That is, the sign of the */ -/* returned Int is the sign of the result (positive for zero) and */ -/* the absolute value of the Int is the count of Units. */ -/* */ -/* It is the caller's responsibility to make sure that C size is */ -/* safe, allowing space if necessary for a one-Unit carry. */ -/* */ -/* This routine is severely performance-critical; *any* change here */ -/* must be measured (timed) to assure no performance degradation. */ -/* In particular, trickery here tends to be counter-productive, as */ -/* increased complexity of code hurts register optimizations on */ -/* register-poor architectures. Avoiding divisions is nearly */ -/* always a Good Idea, however. */ -/* */ -/* Special thanks to Rick McGuire (IBM Cambridge, MA) and Dave Clark */ -/* (IBM Warwick, UK) for some of the ideas used in this routine. */ -/* ------------------------------------------------------------------ */ -static Int decUnitAddSub(const Unit *a, Int alength, - const Unit *b, Int blength, Int bshift, - Unit *c, Int m) { - const Unit *alsu=a; /* A lsu [need to remember it] */ - Unit *clsu=c; /* C ditto */ - Unit *minC; /* low water mark for C */ - Unit *maxC; /* high water mark for C */ - eInt carry=0; /* carry integer (could be Long) */ - Int add; /* work */ - #if DECDPUN<=4 /* myriadal, millenary, etc. */ - Int est; /* estimated quotient */ - #endif - - #if DECTRACE - if (alength<1 || blength<1) - printf("decUnitAddSub: alen blen m %ld %ld [%ld]\n", alength, blength, m); - #endif - - maxC=c+alength; /* A is usually the longer */ - minC=c+blength; /* .. and B the shorter */ - if (bshift!=0) { /* B is shifted; low As copy across */ - minC+=bshift; - /* if in place [common], skip copy unless there's a gap [rare] */ - if (a==c && bshift<=alength) { - c+=bshift; - a+=bshift; - } - else for (; cmaxC) { /* swap */ - Unit *hold=minC; - minC=maxC; - maxC=hold; - } - - /* For speed, do the addition as two loops; the first where both A */ - /* and B contribute, and the second (if necessary) where only one or */ - /* other of the numbers contribute. */ - /* Carry handling is the same (i.e., duplicated) in each case. */ - for (; c=0) { - est=(((ueInt)carry>>11)*53687)>>18; - *c=(Unit)(carry-est*(DECDPUNMAX+1)); /* remainder */ - carry=est; /* likely quotient [89%] */ - if (*c>11)*53687)>>18; - *c=(Unit)(carry-est*(DECDPUNMAX+1)); - carry=est-(DECDPUNMAX+1); /* correctly negative */ - if (*c=0) { - est=(((ueInt)carry>>3)*16777)>>21; - *c=(Unit)(carry-est*(DECDPUNMAX+1)); /* remainder */ - carry=est; /* likely quotient [99%] */ - if (*c>3)*16777)>>21; - *c=(Unit)(carry-est*(DECDPUNMAX+1)); - carry=est-(DECDPUNMAX+1); /* correctly negative */ - if (*c=0) { - est=QUOT10(carry, DECDPUN); - *c=(Unit)(carry-est*(DECDPUNMAX+1)); /* remainder */ - carry=est; /* quotient */ - continue; - } - /* negative case */ - carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); /* make positive */ - est=QUOT10(carry, DECDPUN); - *c=(Unit)(carry-est*(DECDPUNMAX+1)); - carry=est-(DECDPUNMAX+1); /* correctly negative */ - #else - /* remainder operator is undefined if negative, so must test */ - if ((ueInt)carry<(DECDPUNMAX+1)*2) { /* fastpath carry +1 */ - *c=(Unit)(carry-(DECDPUNMAX+1)); /* [helps additions] */ - carry=1; - continue; - } - if (carry>=0) { - *c=(Unit)(carry%(DECDPUNMAX+1)); - carry=carry/(DECDPUNMAX+1); - continue; - } - /* negative case */ - carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); /* make positive */ - *c=(Unit)(carry%(DECDPUNMAX+1)); - carry=carry/(DECDPUNMAX+1)-(DECDPUNMAX+1); - #endif - } /* c */ - - /* now may have one or other to complete */ - /* [pretest to avoid loop setup/shutdown] */ - if (cDECDPUNMAX */ - #if DECDPUN==4 /* use divide-by-multiply */ - if (carry>=0) { - est=(((ueInt)carry>>11)*53687)>>18; - *c=(Unit)(carry-est*(DECDPUNMAX+1)); /* remainder */ - carry=est; /* likely quotient [79.7%] */ - if (*c>11)*53687)>>18; - *c=(Unit)(carry-est*(DECDPUNMAX+1)); - carry=est-(DECDPUNMAX+1); /* correctly negative */ - if (*c=0) { - est=(((ueInt)carry>>3)*16777)>>21; - *c=(Unit)(carry-est*(DECDPUNMAX+1)); /* remainder */ - carry=est; /* likely quotient [99%] */ - if (*c>3)*16777)>>21; - *c=(Unit)(carry-est*(DECDPUNMAX+1)); - carry=est-(DECDPUNMAX+1); /* correctly negative */ - if (*c=0) { - est=QUOT10(carry, DECDPUN); - *c=(Unit)(carry-est*(DECDPUNMAX+1)); /* remainder */ - carry=est; /* quotient */ - continue; - } - /* negative case */ - carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); /* make positive */ - est=QUOT10(carry, DECDPUN); - *c=(Unit)(carry-est*(DECDPUNMAX+1)); - carry=est-(DECDPUNMAX+1); /* correctly negative */ - #else - if ((ueInt)carry<(DECDPUNMAX+1)*2){ /* fastpath carry 1 */ - *c=(Unit)(carry-(DECDPUNMAX+1)); - carry=1; - continue; - } - /* remainder operator is undefined if negative, so must test */ - if (carry>=0) { - *c=(Unit)(carry%(DECDPUNMAX+1)); - carry=carry/(DECDPUNMAX+1); - continue; - } - /* negative case */ - carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); /* make positive */ - *c=(Unit)(carry%(DECDPUNMAX+1)); - carry=carry/(DECDPUNMAX+1)-(DECDPUNMAX+1); - #endif - } /* c */ - - /* OK, all A and B processed; might still have carry or borrow */ - /* return number of Units in the result, negated if a borrow */ - if (carry==0) return static_cast(c-clsu); /* no carry, so no more to do */ - if (carry>0) { /* positive carry */ - *c=(Unit)carry; /* place as new unit */ - c++; /* .. */ - return static_cast(c-clsu); - } - /* -ve carry: it's a borrow; complement needed */ - add=1; /* temporary carry... */ - for (c=clsu; c(clsu-c); /* -ve result indicates borrowed */ - } /* decUnitAddSub */ - -/* ------------------------------------------------------------------ */ -/* decTrim -- trim trailing zeros or normalize */ -/* */ -/* dn is the number to trim or normalize */ -/* set is the context to use to check for clamp */ -/* all is 1 to remove all trailing zeros, 0 for just fraction ones */ -/* noclamp is 1 to unconditional (unclamped) trim */ -/* dropped returns the number of discarded trailing zeros */ -/* returns dn */ -/* */ -/* If clamp is set in the context then the number of zeros trimmed */ -/* may be limited if the exponent is high. */ -/* All fields are updated as required. This is a utility operation, */ -/* so special values are unchanged and no error is possible. */ -/* ------------------------------------------------------------------ */ -static decNumber * decTrim(decNumber *dn, decContext *set, Flag all, - Flag noclamp, Int *dropped) { - Int d, exp; /* work */ - uInt cut; /* .. */ - Unit *up; /* -> current Unit */ - - #if DECCHECK - if (decCheckOperands(dn, DECUNUSED, DECUNUSED, DECUNCONT)) return dn; - #endif - - *dropped=0; /* assume no zeros dropped */ - if ((dn->bits & DECSPECIAL) /* fast exit if special .. */ - || (*dn->lsu & 0x01)) return dn; /* .. or odd */ - if (ISZERO(dn)) { /* .. or 0 */ - dn->exponent=0; /* (sign is preserved) */ - return dn; - } - - /* have a finite number which is even */ - exp=dn->exponent; - cut=1; /* digit (1-DECDPUN) in Unit */ - up=dn->lsu; /* -> current Unit */ - for (d=0; ddigits-1; d++) { /* [don't strip the final digit] */ - /* slice by powers */ - #if DECDPUN<=4 - uInt quot=QUOT10(*up, cut); - if ((*up-quot*powers[cut])!=0) break; /* found non-0 digit */ - #else - if (*up%powers[cut]!=0) break; /* found non-0 digit */ - #endif - /* have a trailing 0 */ - if (!all) { /* trimming */ - /* [if exp>0 then all trailing 0s are significant for trim] */ - if (exp<=0) { /* if digit might be significant */ - if (exp==0) break; /* then quit */ - exp++; /* next digit might be significant */ - } - } - cut++; /* next power */ - if (cut>DECDPUN) { /* need new Unit */ - up++; - cut=1; - } - } /* d */ - if (d==0) return dn; /* none to drop */ - - /* may need to limit drop if clamping */ - if (set->clamp && !noclamp) { - Int maxd=set->emax-set->digits+1-dn->exponent; - if (maxd<=0) return dn; /* nothing possible */ - if (d>maxd) d=maxd; - } - - /* effect the drop */ - decShiftToLeast(dn->lsu, D2U(dn->digits), d); - dn->exponent+=d; /* maintain numerical value */ - dn->digits-=d; /* new length */ - *dropped=d; /* report the count */ - return dn; - } /* decTrim */ - -/* ------------------------------------------------------------------ */ -/* decReverse -- reverse a Unit array in place */ -/* */ -/* ulo is the start of the array */ -/* uhi is the end of the array (highest Unit to include) */ -/* */ -/* The units ulo through uhi are reversed in place (if the number */ -/* of units is odd, the middle one is untouched). Note that the */ -/* digit(s) in each unit are unaffected. */ -/* ------------------------------------------------------------------ */ -static void decReverse(Unit *ulo, Unit *uhi) { - Unit temp; - for (; ulo=uar; source--, target--) *target=*source; - } - else { - first=uar+D2U(digits+shift)-1; /* where msu of source will end up */ - for (; source>=uar; source--, target--) { - /* split the source Unit and accumulate remainder for next */ - #if DECDPUN<=4 - uInt quot=QUOT10(*source, cut); - uInt rem=*source-quot*powers[cut]; - next+=quot; - #else - uInt rem=*source%powers[cut]; - next+=*source/powers[cut]; - #endif - if (target<=first) *target=(Unit)next; /* write to target iff valid */ - next=rem*powers[DECDPUN-cut]; /* save remainder for next Unit */ - } - } /* shift-move */ - - /* propagate any partial unit to one below and clear the rest */ - for (; target>=uar; target--) { - *target=(Unit)next; - next=0; - } - return digits+shift; - } /* decShiftToMost */ - -/* ------------------------------------------------------------------ */ -/* decShiftToLeast -- shift digits in array towards least significant */ -/* */ -/* uar is the array */ -/* units is length of the array, in units */ -/* shift is the number of digits to remove from the lsu end; it */ -/* must be zero or positive and <= than units*DECDPUN. */ -/* */ -/* returns the new length of the integer in the array, in units */ -/* */ -/* Removed digits are discarded (lost). Units not required to hold */ -/* the final result are unchanged. */ -/* ------------------------------------------------------------------ */ -static Int decShiftToLeast(Unit *uar, Int units, Int shift) { - Unit *target, *up; /* work */ - Int cut, count; /* work */ - Int quot, rem; /* for division */ - - if (shift==0) return units; /* [fastpath] nothing to do */ - if (shift==units*DECDPUN) { /* [fastpath] little to do */ - *uar=0; /* all digits cleared gives zero */ - return 1; /* leaves just the one */ - } - - target=uar; /* both paths */ - cut=MSUDIGITS(shift); - if (cut==DECDPUN) { /* unit-boundary case; easy */ - up=uar+D2U(shift); - for (; up(target-uar); - } - - /* messier */ - up=uar+D2U(shift-cut); /* source; correct to whole Units */ - count=units*DECDPUN-shift; /* the maximum new length */ - #if DECDPUN<=4 - quot=QUOT10(*up, cut); - #else - quot=*up/powers[cut]; - #endif - for (; ; target++) { - *target=(Unit)quot; - count-=(DECDPUN-cut); - if (count<=0) break; - up++; - quot=*up; - #if DECDPUN<=4 - quot=QUOT10(quot, cut); - rem=*up-quot*powers[cut]; - #else - rem=quot%powers[cut]; - quot=quot/powers[cut]; - #endif - *target=(Unit)(*target+rem*powers[DECDPUN-cut]); - count-=cut; - if (count<=0) break; - } - return static_cast(target-uar+1); - } /* decShiftToLeast */ - -#if DECSUBSET -/* ------------------------------------------------------------------ */ -/* decRoundOperand -- round an operand [used for subset only] */ -/* */ -/* dn is the number to round (dn->digits is > set->digits) */ -/* set is the relevant context */ -/* status is the status accumulator */ -/* */ -/* returns an allocated decNumber with the rounded result. */ -/* */ -/* lostDigits and other status may be set by this. */ -/* */ -/* Since the input is an operand, it must not be modified. */ -/* Instead, return an allocated decNumber, rounded as required. */ -/* It is the caller's responsibility to free the allocated storage. */ -/* */ -/* If no storage is available then the result cannot be used, so NULL */ -/* is returned. */ -/* ------------------------------------------------------------------ */ -static decNumber *decRoundOperand(const decNumber *dn, decContext *set, - uInt *status) { - decNumber *res; /* result structure */ - uInt newstatus=0; /* status from round */ - Int residue=0; /* rounding accumulator */ - - /* Allocate storage for the returned decNumber, big enough for the */ - /* length specified by the context */ - res=(decNumber *)malloc(sizeof(decNumber) - +(D2U(set->digits)-1)*sizeof(Unit)); - if (res==NULL) { - *status|=DEC_Insufficient_storage; - return NULL; - } - decCopyFit(res, dn, set, &residue, &newstatus); - decApplyRound(res, set, residue, &newstatus); - - /* If that set Inexact then "lost digits" is raised... */ - if (newstatus & DEC_Inexact) newstatus|=DEC_Lost_digits; - *status|=newstatus; - return res; - } /* decRoundOperand */ -#endif - -/* ------------------------------------------------------------------ */ -/* decCopyFit -- copy a number, truncating the coefficient if needed */ -/* */ -/* dest is the target decNumber */ -/* src is the source decNumber */ -/* set is the context [used for length (digits) and rounding mode] */ -/* residue is the residue accumulator */ -/* status contains the current status to be updated */ -/* */ -/* (dest==src is allowed and will be a no-op if fits) */ -/* All fields are updated as required. */ -/* ------------------------------------------------------------------ */ -static void decCopyFit(decNumber *dest, const decNumber *src, - decContext *set, Int *residue, uInt *status) { - dest->bits=src->bits; - dest->exponent=src->exponent; - decSetCoeff(dest, set, src->lsu, src->digits, residue, status); - } /* decCopyFit */ - -/* ------------------------------------------------------------------ */ -/* decSetCoeff -- set the coefficient of a number */ -/* */ -/* dn is the number whose coefficient array is to be set. */ -/* It must have space for set->digits digits */ -/* set is the context [for size] */ -/* lsu -> lsu of the source coefficient [may be dn->lsu] */ -/* len is digits in the source coefficient [may be dn->digits] */ -/* residue is the residue accumulator. This has values as in */ -/* decApplyRound, and will be unchanged unless the */ -/* target size is less than len. In this case, the */ -/* coefficient is truncated and the residue is updated to */ -/* reflect the previous residue and the dropped digits. */ -/* status is the status accumulator, as usual */ -/* */ -/* The coefficient may already be in the number, or it can be an */ -/* external intermediate array. If it is in the number, lsu must == */ -/* dn->lsu and len must == dn->digits. */ -/* */ -/* Note that the coefficient length (len) may be < set->digits, and */ -/* in this case this merely copies the coefficient (or is a no-op */ -/* if dn->lsu==lsu). */ -/* */ -/* Note also that (only internally, from decQuantizeOp and */ -/* decSetSubnormal) the value of set->digits may be less than one, */ -/* indicating a round to left. This routine handles that case */ -/* correctly; caller ensures space. */ -/* */ -/* dn->digits, dn->lsu (and as required), and dn->exponent are */ -/* updated as necessary. dn->bits (sign) is unchanged. */ -/* */ -/* DEC_Rounded status is set if any digits are discarded. */ -/* DEC_Inexact status is set if any non-zero digits are discarded, or */ -/* incoming residue was non-0 (implies rounded) */ -/* ------------------------------------------------------------------ */ -/* mapping array: maps 0-9 to canonical residues, so that a residue */ -/* can be adjusted in the range [-1, +1] and achieve correct rounding */ -/* 0 1 2 3 4 5 6 7 8 9 */ -static const uByte resmap[10]={0, 3, 3, 3, 3, 5, 7, 7, 7, 7}; -static void decSetCoeff(decNumber *dn, decContext *set, const Unit *lsu, - Int len, Int *residue, uInt *status) { - Int discard; /* number of digits to discard */ - uInt cut; /* cut point in Unit */ - const Unit *up; /* work */ - Unit *target; /* .. */ - Int count; /* .. */ - #if DECDPUN<=4 - uInt temp; /* .. */ - #endif - - discard=len-set->digits; /* digits to discard */ - if (discard<=0) { /* no digits are being discarded */ - if (dn->lsu!=lsu) { /* copy needed */ - /* copy the coefficient array to the result number; no shift needed */ - count=len; /* avoids D2U */ - up=lsu; - for (target=dn->lsu; count>0; target++, up++, count-=DECDPUN) - *target=*up; - dn->digits=len; /* set the new length */ - } - /* dn->exponent and residue are unchanged, record any inexactitude */ - if (*residue!=0) *status|=(DEC_Inexact | DEC_Rounded); - return; - } - - /* some digits must be discarded ... */ - dn->exponent+=discard; /* maintain numerical value */ - *status|=DEC_Rounded; /* accumulate Rounded status */ - if (*residue>1) *residue=1; /* previous residue now to right, so reduce */ - - if (discard>len) { /* everything, +1, is being discarded */ - /* guard digit is 0 */ - /* residue is all the number [NB could be all 0s] */ - if (*residue<=0) { /* not already positive */ - count=len; /* avoids D2U */ - for (up=lsu; count>0; up++, count-=DECDPUN) if (*up!=0) { /* found non-0 */ - *residue=1; - break; /* no need to check any others */ - } - } - if (*residue!=0) *status|=DEC_Inexact; /* record inexactitude */ - *dn->lsu=0; /* coefficient will now be 0 */ - dn->digits=1; /* .. */ - return; - } /* total discard */ - - /* partial discard [most common case] */ - /* here, at least the first (most significant) discarded digit exists */ - - /* spin up the number, noting residue during the spin, until get to */ - /* the Unit with the first discarded digit. When reach it, extract */ - /* it and remember its position */ - count=0; - for (up=lsu;; up++) { - count+=DECDPUN; - if (count>=discard) break; /* full ones all checked */ - if (*up!=0) *residue=1; - } /* up */ - - /* here up -> Unit with first discarded digit */ - cut=discard-(count-DECDPUN)-1; - if (cut==DECDPUN-1) { /* unit-boundary case (fast) */ - Unit half=(Unit)powers[DECDPUN]>>1; - /* set residue directly */ - if (*up>=half) { - if (*up>half) *residue=7; - else *residue+=5; /* add sticky bit */ - } - else { /* digits<=0) { /* special for Quantize/Subnormal :-( */ - *dn->lsu=0; /* .. result is 0 */ - dn->digits=1; /* .. */ - } - else { /* shift to least */ - count=set->digits; /* now digits to end up with */ - dn->digits=count; /* set the new length */ - up++; /* move to next */ - /* on unit boundary, so shift-down copy loop is simple */ - for (target=dn->lsu; count>0; target++, up++, count-=DECDPUN) - *target=*up; - } - } /* unit-boundary case */ - - else { /* discard digit is in low digit(s), and not top digit */ - uInt discard1; /* first discarded digit */ - uInt quot, rem; /* for divisions */ - if (cut==0) quot=*up; /* is at bottom of unit */ - else /* cut>0 */ { /* it's not at bottom of unit */ - #if DECDPUN<=4 - U_ASSERT(/* cut >= 0 &&*/ cut <= 4); - quot=QUOT10(*up, cut); - rem=*up-quot*powers[cut]; - #else - rem=*up%powers[cut]; - quot=*up/powers[cut]; - #endif - if (rem!=0) *residue=1; - } - /* discard digit is now at bottom of quot */ - #if DECDPUN<=4 - temp=(quot*6554)>>16; /* fast /10 */ - /* Vowels algorithm here not a win (9 instructions) */ - discard1=quot-X10(temp); - quot=temp; - #else - discard1=quot%10; - quot=quot/10; - #endif - /* here, discard1 is the guard digit, and residue is everything */ - /* else [use mapping array to accumulate residue safely] */ - *residue+=resmap[discard1]; - cut++; /* update cut */ - /* here: up -> Unit of the array with bottom digit */ - /* cut is the division point for each Unit */ - /* quot holds the uncut high-order digits for the current unit */ - if (set->digits<=0) { /* special for Quantize/Subnormal :-( */ - *dn->lsu=0; /* .. result is 0 */ - dn->digits=1; /* .. */ - } - else { /* shift to least needed */ - count=set->digits; /* now digits to end up with */ - dn->digits=count; /* set the new length */ - /* shift-copy the coefficient array to the result number */ - for (target=dn->lsu; ; target++) { - *target=(Unit)quot; - count-=(DECDPUN-cut); - if (count<=0) break; - up++; - quot=*up; - #if DECDPUN<=4 - quot=QUOT10(quot, cut); - rem=*up-quot*powers[cut]; - #else - rem=quot%powers[cut]; - quot=quot/powers[cut]; - #endif - *target=(Unit)(*target+rem*powers[DECDPUN-cut]); - count-=cut; - if (count<=0) break; - } /* shift-copy loop */ - } /* shift to least */ - } /* not unit boundary */ - - if (*residue!=0) *status|=DEC_Inexact; /* record inexactitude */ - return; - } /* decSetCoeff */ - -/* ------------------------------------------------------------------ */ -/* decApplyRound -- apply pending rounding to a number */ -/* */ -/* dn is the number, with space for set->digits digits */ -/* set is the context [for size and rounding mode] */ -/* residue indicates pending rounding, being any accumulated */ -/* guard and sticky information. It may be: */ -/* 6-9: rounding digit is >5 */ -/* 5: rounding digit is exactly half-way */ -/* 1-4: rounding digit is <5 and >0 */ -/* 0: the coefficient is exact */ -/* -1: as 1, but the hidden digits are subtractive, that */ -/* is, of the opposite sign to dn. In this case the */ -/* coefficient must be non-0. This case occurs when */ -/* subtracting a small number (which can be reduced to */ -/* a sticky bit); see decAddOp. */ -/* status is the status accumulator, as usual */ -/* */ -/* This routine applies rounding while keeping the length of the */ -/* coefficient constant. The exponent and status are unchanged */ -/* except if: */ -/* */ -/* -- the coefficient was increased and is all nines (in which */ -/* case Overflow could occur, and is handled directly here so */ -/* the caller does not need to re-test for overflow) */ -/* */ -/* -- the coefficient was decreased and becomes all nines (in which */ -/* case Underflow could occur, and is also handled directly). */ -/* */ -/* All fields in dn are updated as required. */ -/* */ -/* ------------------------------------------------------------------ */ -static void decApplyRound(decNumber *dn, decContext *set, Int residue, - uInt *status) { - Int bump; /* 1 if coefficient needs to be incremented */ - /* -1 if coefficient needs to be decremented */ - - if (residue==0) return; /* nothing to apply */ - - bump=0; /* assume a smooth ride */ - - /* now decide whether, and how, to round, depending on mode */ - switch (set->round) { - case DEC_ROUND_05UP: { /* round zero or five up (for reround) */ - /* This is the same as DEC_ROUND_DOWN unless there is a */ - /* positive residue and the lsd of dn is 0 or 5, in which case */ - /* it is bumped; when residue is <0, the number is therefore */ - /* bumped down unless the final digit was 1 or 6 (in which */ - /* case it is bumped down and then up -- a no-op) */ - Int lsd5=*dn->lsu%5; /* get lsd and quintate */ - if (residue<0 && lsd5!=1) bump=-1; - else if (residue>0 && lsd5==0) bump=1; - /* [bump==1 could be applied directly; use common path for clarity] */ - break;} /* r-05 */ - - case DEC_ROUND_DOWN: { - /* no change, except if negative residue */ - if (residue<0) bump=-1; - break;} /* r-d */ - - case DEC_ROUND_HALF_DOWN: { - if (residue>5) bump=1; - break;} /* r-h-d */ - - case DEC_ROUND_HALF_EVEN: { - if (residue>5) bump=1; /* >0.5 goes up */ - else if (residue==5) { /* exactly 0.5000... */ - /* 0.5 goes up iff [new] lsd is odd */ - if (*dn->lsu & 0x01) bump=1; - } - break;} /* r-h-e */ - - case DEC_ROUND_HALF_UP: { - if (residue>=5) bump=1; - break;} /* r-h-u */ - - case DEC_ROUND_UP: { - if (residue>0) bump=1; - break;} /* r-u */ - - case DEC_ROUND_CEILING: { - /* same as _UP for positive numbers, and as _DOWN for negatives */ - /* [negative residue cannot occur on 0] */ - if (decNumberIsNegative(dn)) { - if (residue<0) bump=-1; - } - else { - if (residue>0) bump=1; - } - break;} /* r-c */ - - case DEC_ROUND_FLOOR: { - /* same as _UP for negative numbers, and as _DOWN for positive */ - /* [negative residue cannot occur on 0] */ - if (!decNumberIsNegative(dn)) { - if (residue<0) bump=-1; - } - else { - if (residue>0) bump=1; - } - break;} /* r-f */ - - default: { /* e.g., DEC_ROUND_MAX */ - *status|=DEC_Invalid_context; - #if DECTRACE || (DECCHECK && DECVERB) - printf("Unknown rounding mode: %d\n", set->round); - #endif - break;} - } /* switch */ - - /* now bump the number, up or down, if need be */ - if (bump==0) return; /* no action required */ - - /* Simply use decUnitAddSub unless bumping up and the number is */ - /* all nines. In this special case set to 100... explicitly */ - /* and adjust the exponent by one (as otherwise could overflow */ - /* the array) */ - /* Similarly handle all-nines result if bumping down. */ - if (bump>0) { - Unit *up; /* work */ - uInt count=dn->digits; /* digits to be checked */ - for (up=dn->lsu; ; up++) { - if (count<=DECDPUN) { - /* this is the last Unit (the msu) */ - if (*up!=powers[count]-1) break; /* not still 9s */ - /* here if it, too, is all nines */ - *up=(Unit)powers[count-1]; /* here 999 -> 100 etc. */ - for (up=up-1; up>=dn->lsu; up--) *up=0; /* others all to 0 */ - dn->exponent++; /* and bump exponent */ - /* [which, very rarely, could cause Overflow...] */ - if ((dn->exponent+dn->digits)>set->emax+1) { - decSetOverflow(dn, set, status); - } - return; /* done */ - } - /* a full unit to check, with more to come */ - if (*up!=DECDPUNMAX) break; /* not still 9s */ - count-=DECDPUN; - } /* up */ - } /* bump>0 */ - else { /* -1 */ - /* here checking for a pre-bump of 1000... (leading 1, all */ - /* other digits zero) */ - Unit *up, *sup; /* work */ - uInt count=dn->digits; /* digits to be checked */ - for (up=dn->lsu; ; up++) { - if (count<=DECDPUN) { - /* this is the last Unit (the msu) */ - if (*up!=powers[count-1]) break; /* not 100.. */ - /* here if have the 1000... case */ - sup=up; /* save msu pointer */ - *up=(Unit)powers[count]-1; /* here 100 in msu -> 999 */ - /* others all to all-nines, too */ - for (up=up-1; up>=dn->lsu; up--) *up=(Unit)powers[DECDPUN]-1; - dn->exponent--; /* and bump exponent */ - - /* iff the number was at the subnormal boundary (exponent=etiny) */ - /* then the exponent is now out of range, so it will in fact get */ - /* clamped to etiny and the final 9 dropped. */ - /* printf(">> emin=%d exp=%d sdig=%d\n", set->emin, */ - /* dn->exponent, set->digits); */ - if (dn->exponent+1==set->emin-set->digits+1) { - if (count==1 && dn->digits==1) *sup=0; /* here 9 -> 0[.9] */ - else { - *sup=(Unit)powers[count-1]-1; /* here 999.. in msu -> 99.. */ - dn->digits--; - } - dn->exponent++; - *status|=DEC_Underflow | DEC_Subnormal | DEC_Inexact | DEC_Rounded; - } - return; /* done */ - } - - /* a full unit to check, with more to come */ - if (*up!=0) break; /* not still 0s */ - count-=DECDPUN; - } /* up */ - - } /* bump<0 */ - - /* Actual bump needed. Do it. */ - decUnitAddSub(dn->lsu, D2U(dn->digits), uarrone, 1, 0, dn->lsu, bump); - } /* decApplyRound */ - -#if DECSUBSET -/* ------------------------------------------------------------------ */ -/* decFinish -- finish processing a number */ -/* */ -/* dn is the number */ -/* set is the context */ -/* residue is the rounding accumulator (as in decApplyRound) */ -/* status is the accumulator */ -/* */ -/* This finishes off the current number by: */ -/* 1. If not extended: */ -/* a. Converting a zero result to clean '0' */ -/* b. Reducing positive exponents to 0, if would fit in digits */ -/* 2. Checking for overflow and subnormals (always) */ -/* Note this is just Finalize when no subset arithmetic. */ -/* All fields are updated as required. */ -/* ------------------------------------------------------------------ */ -static void decFinish(decNumber *dn, decContext *set, Int *residue, - uInt *status) { - if (!set->extended) { - if ISZERO(dn) { /* value is zero */ - dn->exponent=0; /* clean exponent .. */ - dn->bits=0; /* .. and sign */ - return; /* no error possible */ - } - if (dn->exponent>=0) { /* non-negative exponent */ - /* >0; reduce to integer if possible */ - if (set->digits >= (dn->exponent+dn->digits)) { - dn->digits=decShiftToMost(dn->lsu, dn->digits, dn->exponent); - dn->exponent=0; - } - } - } /* !extended */ - - decFinalize(dn, set, residue, status); - } /* decFinish */ -#endif - -/* ------------------------------------------------------------------ */ -/* decFinalize -- final check, clamp, and round of a number */ -/* */ -/* dn is the number */ -/* set is the context */ -/* residue is the rounding accumulator (as in decApplyRound) */ -/* status is the status accumulator */ -/* */ -/* This finishes off the current number by checking for subnormal */ -/* results, applying any pending rounding, checking for overflow, */ -/* and applying any clamping. */ -/* Underflow and overflow conditions are raised as appropriate. */ -/* All fields are updated as required. */ -/* ------------------------------------------------------------------ */ -static void decFinalize(decNumber *dn, decContext *set, Int *residue, - uInt *status) { - Int shift; /* shift needed if clamping */ - Int tinyexp=set->emin-dn->digits+1; /* precalculate subnormal boundary */ - - /* Must be careful, here, when checking the exponent as the */ - /* adjusted exponent could overflow 31 bits [because it may already */ - /* be up to twice the expected]. */ - - /* First test for subnormal. This must be done before any final */ - /* round as the result could be rounded to Nmin or 0. */ - if (dn->exponent<=tinyexp) { /* prefilter */ - Int comp; - decNumber nmin; - /* A very nasty case here is dn == Nmin and residue<0 */ - if (dn->exponentemin; - comp=decCompare(dn, &nmin, 1); /* (signless compare) */ - if (comp==BADINT) { /* oops */ - *status|=DEC_Insufficient_storage; /* abandon... */ - return; - } - if (*residue<0 && comp==0) { /* neg residue and dn==Nmin */ - decApplyRound(dn, set, *residue, status); /* might force down */ - decSetSubnormal(dn, set, residue, status); - return; - } - } - - /* now apply any pending round (this could raise overflow). */ - if (*residue!=0) decApplyRound(dn, set, *residue, status); - - /* Check for overflow [redundant in the 'rare' case] or clamp */ - if (dn->exponent<=set->emax-set->digits+1) return; /* neither needed */ - - - /* here when might have an overflow or clamp to do */ - if (dn->exponent>set->emax-dn->digits+1) { /* too big */ - decSetOverflow(dn, set, status); - return; - } - /* here when the result is normal but in clamp range */ - if (!set->clamp) return; - - /* here when need to apply the IEEE exponent clamp (fold-down) */ - shift=dn->exponent-(set->emax-set->digits+1); - - /* shift coefficient (if non-zero) */ - if (!ISZERO(dn)) { - dn->digits=decShiftToMost(dn->lsu, dn->digits, shift); - } - dn->exponent-=shift; /* adjust the exponent to match */ - *status|=DEC_Clamped; /* and record the dirty deed */ - return; - } /* decFinalize */ - -/* ------------------------------------------------------------------ */ -/* decSetOverflow -- set number to proper overflow value */ -/* */ -/* dn is the number (used for sign [only] and result) */ -/* set is the context [used for the rounding mode, etc.] */ -/* status contains the current status to be updated */ -/* */ -/* This sets the sign of a number and sets its value to either */ -/* Infinity or the maximum finite value, depending on the sign of */ -/* dn and the rounding mode, following IEEE 754 rules. */ -/* ------------------------------------------------------------------ */ -static void decSetOverflow(decNumber *dn, decContext *set, uInt *status) { - Flag needmax=0; /* result is maximum finite value */ - uByte sign=dn->bits&DECNEG; /* clean and save sign bit */ - - if (ISZERO(dn)) { /* zero does not overflow magnitude */ - Int emax=set->emax; /* limit value */ - if (set->clamp) emax-=set->digits-1; /* lower if clamping */ - if (dn->exponent>emax) { /* clamp required */ - dn->exponent=emax; - *status|=DEC_Clamped; - } - return; - } - - uprv_decNumberZero(dn); - switch (set->round) { - case DEC_ROUND_DOWN: { - needmax=1; /* never Infinity */ - break;} /* r-d */ - case DEC_ROUND_05UP: { - needmax=1; /* never Infinity */ - break;} /* r-05 */ - case DEC_ROUND_CEILING: { - if (sign) needmax=1; /* Infinity if non-negative */ - break;} /* r-c */ - case DEC_ROUND_FLOOR: { - if (!sign) needmax=1; /* Infinity if negative */ - break;} /* r-f */ - default: break; /* Infinity in all other cases */ - } - if (needmax) { - decSetMaxValue(dn, set); - dn->bits=sign; /* set sign */ - } - else dn->bits=sign|DECINF; /* Value is +/-Infinity */ - *status|=DEC_Overflow | DEC_Inexact | DEC_Rounded; - } /* decSetOverflow */ - -/* ------------------------------------------------------------------ */ -/* decSetMaxValue -- set number to +Nmax (maximum normal value) */ -/* */ -/* dn is the number to set */ -/* set is the context [used for digits and emax] */ -/* */ -/* This sets the number to the maximum positive value. */ -/* ------------------------------------------------------------------ */ -static void decSetMaxValue(decNumber *dn, decContext *set) { - Unit *up; /* work */ - Int count=set->digits; /* nines to add */ - dn->digits=count; - /* fill in all nines to set maximum value */ - for (up=dn->lsu; ; up++) { - if (count>DECDPUN) *up=DECDPUNMAX; /* unit full o'nines */ - else { /* this is the msu */ - *up=(Unit)(powers[count]-1); - break; - } - count-=DECDPUN; /* filled those digits */ - } /* up */ - dn->bits=0; /* + sign */ - dn->exponent=set->emax-set->digits+1; - } /* decSetMaxValue */ - -/* ------------------------------------------------------------------ */ -/* decSetSubnormal -- process value whose exponent is extended) { - uprv_decNumberZero(dn); - /* always full overflow */ - *status|=DEC_Underflow | DEC_Subnormal | DEC_Inexact | DEC_Rounded; - return; - } - #endif - - /* Full arithmetic -- allow subnormals, rounded to minimum exponent */ - /* (Etiny) if needed */ - etiny=set->emin-(set->digits-1); /* smallest allowed exponent */ - - if ISZERO(dn) { /* value is zero */ - /* residue can never be non-zero here */ - #if DECCHECK - if (*residue!=0) { - printf("++ Subnormal 0 residue %ld\n", (LI)*residue); - *status|=DEC_Invalid_operation; - } - #endif - if (dn->exponentexponent=etiny; - *status|=DEC_Clamped; - } - return; - } - - *status|=DEC_Subnormal; /* have a non-zero subnormal */ - adjust=etiny-dn->exponent; /* calculate digits to remove */ - if (adjust<=0) { /* not out of range; unrounded */ - /* residue can never be non-zero here, except in the Nmin-residue */ - /* case (which is a subnormal result), so can take fast-path here */ - /* it may already be inexact (from setting the coefficient) */ - if (*status&DEC_Inexact) *status|=DEC_Underflow; - return; - } - - /* adjust>0, so need to rescale the result so exponent becomes Etiny */ - /* [this code is similar to that in rescale] */ - workset=*set; /* clone rounding, etc. */ - workset.digits=dn->digits-adjust; /* set requested length */ - workset.emin-=adjust; /* and adjust emin to match */ - /* [note that the latter can be <1, here, similar to Rescale case] */ - decSetCoeff(dn, &workset, dn->lsu, dn->digits, residue, status); - decApplyRound(dn, &workset, *residue, status); - - /* Use 754 default rule: Underflow is set iff Inexact */ - /* [independent of whether trapped] */ - if (*status&DEC_Inexact) *status|=DEC_Underflow; - - /* if rounded up a 999s case, exponent will be off by one; adjust */ - /* back if so [it will fit, because it was shortened earlier] */ - if (dn->exponent>etiny) { - dn->digits=decShiftToMost(dn->lsu, dn->digits, 1); - dn->exponent--; /* (re)adjust the exponent. */ - } - - /* if rounded to zero, it is by definition clamped... */ - if (ISZERO(dn)) *status|=DEC_Clamped; - } /* decSetSubnormal */ - -/* ------------------------------------------------------------------ */ -/* decCheckMath - check entry conditions for a math function */ -/* */ -/* This checks the context and the operand */ -/* */ -/* rhs is the operand to check */ -/* set is the context to check */ -/* status is unchanged if both are good */ -/* */ -/* returns non-zero if status is changed, 0 otherwise */ -/* */ -/* Restrictions enforced: */ -/* */ -/* digits, emax, and -emin in the context must be less than */ -/* DEC_MAX_MATH (999999), and A must be within these bounds if */ -/* non-zero. Invalid_operation is set in the status if a */ -/* restriction is violated. */ -/* ------------------------------------------------------------------ */ -static uInt decCheckMath(const decNumber *rhs, decContext *set, - uInt *status) { - uInt save=*status; /* record */ - if (set->digits>DEC_MAX_MATH - || set->emax>DEC_MAX_MATH - || -set->emin>DEC_MAX_MATH) *status|=DEC_Invalid_context; - else if ((rhs->digits>DEC_MAX_MATH - || rhs->exponent+rhs->digits>DEC_MAX_MATH+1 - || rhs->exponent+rhs->digits<2*(1-DEC_MAX_MATH)) - && !ISZERO(rhs)) *status|=DEC_Invalid_operation; - return (*status!=save); - } /* decCheckMath */ - -/* ------------------------------------------------------------------ */ -/* decGetInt -- get integer from a number */ -/* */ -/* dn is the number [which will not be altered] */ -/* */ -/* returns one of: */ -/* BADINT if there is a non-zero fraction */ -/* the converted integer */ -/* BIGEVEN if the integer is even and magnitude > 2*10**9 */ -/* BIGODD if the integer is odd and magnitude > 2*10**9 */ -/* */ -/* This checks and gets a whole number from the input decNumber. */ -/* The sign can be determined from dn by the caller when BIGEVEN or */ -/* BIGODD is returned. */ -/* ------------------------------------------------------------------ */ -static Int decGetInt(const decNumber *dn) { - Int theInt; /* result accumulator */ - const Unit *up; /* work */ - Int got; /* digits (real or not) processed */ - Int ilength=dn->digits+dn->exponent; /* integral length */ - Flag neg=decNumberIsNegative(dn); /* 1 if -ve */ - - /* The number must be an integer that fits in 10 digits */ - /* Assert, here, that 10 is enough for any rescale Etiny */ - #if DEC_MAX_EMAX > 999999999 - #error GetInt may need updating [for Emax] - #endif - #if DEC_MIN_EMIN < -999999999 - #error GetInt may need updating [for Emin] - #endif - if (ISZERO(dn)) return 0; /* zeros are OK, with any exponent */ - - up=dn->lsu; /* ready for lsu */ - theInt=0; /* ready to accumulate */ - if (dn->exponent>=0) { /* relatively easy */ - /* no fractional part [usual]; allow for positive exponent */ - got=dn->exponent; - } - else { /* -ve exponent; some fractional part to check and discard */ - Int count=-dn->exponent; /* digits to discard */ - /* spin up whole units until reach the Unit with the unit digit */ - for (; count>=DECDPUN; up++) { - if (*up!=0) return BADINT; /* non-zero Unit to discard */ - count-=DECDPUN; - } - if (count==0) got=0; /* [a multiple of DECDPUN] */ - else { /* [not multiple of DECDPUN] */ - Int rem; /* work */ - /* slice off fraction digits and check for non-zero */ - #if DECDPUN<=4 - theInt=QUOT10(*up, count); - rem=*up-theInt*powers[count]; - #else - rem=*up%powers[count]; /* slice off discards */ - theInt=*up/powers[count]; - #endif - if (rem!=0) return BADINT; /* non-zero fraction */ - /* it looks good */ - got=DECDPUN-count; /* number of digits so far */ - up++; /* ready for next */ - } - } - /* now it's known there's no fractional part */ - - /* tricky code now, to accumulate up to 9.3 digits */ - if (got==0) {theInt=*up; got+=DECDPUN; up++;} /* ensure lsu is there */ - - if (ilength<11) { - Int save=theInt; - /* collect any remaining unit(s) */ - for (; got1999999997) ilength=11; - else if (!neg && theInt>999999999) ilength=11; - if (ilength==11) theInt=save; /* restore correct low bit */ - } - } - - if (ilength>10) { /* too big */ - if (theInt&1) return BIGODD; /* bottom bit 1 */ - return BIGEVEN; /* bottom bit 0 */ - } - - if (neg) theInt=-theInt; /* apply sign */ - return theInt; - } /* decGetInt */ - -/* ------------------------------------------------------------------ */ -/* decDecap -- decapitate the coefficient of a number */ -/* */ -/* dn is the number to be decapitated */ -/* drop is the number of digits to be removed from the left of dn; */ -/* this must be <= dn->digits (if equal, the coefficient is */ -/* set to 0) */ -/* */ -/* Returns dn; dn->digits will be <= the initial digits less drop */ -/* (after removing drop digits there may be leading zero digits */ -/* which will also be removed). Only dn->lsu and dn->digits change. */ -/* ------------------------------------------------------------------ */ -static decNumber *decDecap(decNumber *dn, Int drop) { - Unit *msu; /* -> target cut point */ - Int cut; /* work */ - if (drop>=dn->digits) { /* losing the whole thing */ - #if DECCHECK - if (drop>dn->digits) - printf("decDecap called with drop>digits [%ld>%ld]\n", - (LI)drop, (LI)dn->digits); - #endif - dn->lsu[0]=0; - dn->digits=1; - return dn; - } - msu=dn->lsu+D2U(dn->digits-drop)-1; /* -> likely msu */ - cut=MSUDIGITS(dn->digits-drop); /* digits to be in use in msu */ - if (cut!=DECDPUN) *msu%=powers[cut]; /* clear left digits */ - /* that may have left leading zero digits, so do a proper count... */ - dn->digits=decGetDigits(dn->lsu, static_cast(msu-dn->lsu+1)); - return dn; - } /* decDecap */ - -/* ------------------------------------------------------------------ */ -/* decBiStr -- compare string with pairwise options */ -/* */ -/* targ is the string to compare */ -/* str1 is one of the strings to compare against (length may be 0) */ -/* str2 is the other; it must be the same length as str1 */ -/* */ -/* returns 1 if strings compare equal, (that is, it is the same */ -/* length as str1 and str2, and each character of targ is in either */ -/* str1 or str2 in the corresponding position), or 0 otherwise */ -/* */ -/* This is used for generic caseless compare, including the awkward */ -/* case of the Turkish dotted and dotless Is. Use as (for example): */ -/* if (decBiStr(test, "mike", "MIKE")) ... */ -/* ------------------------------------------------------------------ */ -static Flag decBiStr(const char *targ, const char *str1, const char *str2) { - for (;;targ++, str1++, str2++) { - if (*targ!=*str1 && *targ!=*str2) return 0; - /* *targ has a match in one (or both, if terminator) */ - if (*targ=='\0') break; - } /* forever */ - return 1; - } /* decBiStr */ - -/* ------------------------------------------------------------------ */ -/* decNaNs -- handle NaN operand or operands */ -/* */ -/* res is the result number */ -/* lhs is the first operand */ -/* rhs is the second operand, or NULL if none */ -/* context is used to limit payload length */ -/* status contains the current status */ -/* returns res in case convenient */ -/* */ -/* Called when one or both operands is a NaN, and propagates the */ -/* appropriate result to res. When an sNaN is found, it is changed */ -/* to a qNaN and Invalid operation is set. */ -/* ------------------------------------------------------------------ */ -static decNumber * decNaNs(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set, - uInt *status) { - /* This decision tree ends up with LHS being the source pointer, */ - /* and status updated if need be */ - if (lhs->bits & DECSNAN) - *status|=DEC_Invalid_operation | DEC_sNaN; - else if (rhs==NULL); - else if (rhs->bits & DECSNAN) { - lhs=rhs; - *status|=DEC_Invalid_operation | DEC_sNaN; - } - else if (lhs->bits & DECNAN); - else lhs=rhs; - - /* propagate the payload */ - if (lhs->digits<=set->digits) uprv_decNumberCopy(res, lhs); /* easy */ - else { /* too long */ - const Unit *ul; - Unit *ur, *uresp1; - /* copy safe number of units, then decapitate */ - res->bits=lhs->bits; /* need sign etc. */ - uresp1=res->lsu+D2U(set->digits); - for (ur=res->lsu, ul=lhs->lsu; urdigits=D2U(set->digits)*DECDPUN; - /* maybe still too long */ - if (res->digits>set->digits) decDecap(res, res->digits-set->digits); - } - - res->bits&=~DECSNAN; /* convert any sNaN to NaN, while */ - res->bits|=DECNAN; /* .. preserving sign */ - res->exponent=0; /* clean exponent */ - /* [coefficient was copied/decapitated] */ - return res; - } /* decNaNs */ - -/* ------------------------------------------------------------------ */ -/* decStatus -- apply non-zero status */ -/* */ -/* dn is the number to set if error */ -/* status contains the current status (not yet in context) */ -/* set is the context */ -/* */ -/* If the status is an error status, the number is set to a NaN, */ -/* unless the error was an overflow, divide-by-zero, or underflow, */ -/* in which case the number will have already been set. */ -/* */ -/* The context status is then updated with the new status. Note that */ -/* this may raise a signal, so control may never return from this */ -/* routine (hence resources must be recovered before it is called). */ -/* ------------------------------------------------------------------ */ -static void decStatus(decNumber *dn, uInt status, decContext *set) { - if (status & DEC_NaNs) { /* error status -> NaN */ - /* if cause was an sNaN, clear and propagate [NaN is already set up] */ - if (status & DEC_sNaN) status&=~DEC_sNaN; - else { - uprv_decNumberZero(dn); /* other error: clean throughout */ - dn->bits=DECNAN; /* and make a quiet NaN */ - } - } - uprv_decContextSetStatus(set, status); /* [may not return] */ - return; - } /* decStatus */ - -/* ------------------------------------------------------------------ */ -/* decGetDigits -- count digits in a Units array */ -/* */ -/* uar is the Unit array holding the number (this is often an */ -/* accumulator of some sort) */ -/* len is the length of the array in units [>=1] */ -/* */ -/* returns the number of (significant) digits in the array */ -/* */ -/* All leading zeros are excluded, except the last if the array has */ -/* only zero Units. */ -/* ------------------------------------------------------------------ */ -/* This may be called twice during some operations. */ -static Int decGetDigits(Unit *uar, Int len) { - Unit *up=uar+(len-1); /* -> msu */ - Int digits=(len-1)*DECDPUN+1; /* possible digits excluding msu */ - #if DECDPUN>4 - uInt const *pow; /* work */ - #endif - /* (at least 1 in final msu) */ - #if DECCHECK - if (len<1) printf("decGetDigits called with len<1 [%ld]\n", (LI)len); - #endif - - for (; up>=uar; up--) { - if (*up==0) { /* unit is all 0s */ - if (digits==1) break; /* a zero has one digit */ - digits-=DECDPUN; /* adjust for 0 unit */ - continue;} - /* found the first (most significant) non-zero Unit */ - #if DECDPUN>1 /* not done yet */ - if (*up<10) break; /* is 1-9 */ - digits++; - #if DECDPUN>2 /* not done yet */ - if (*up<100) break; /* is 10-99 */ - digits++; - #if DECDPUN>3 /* not done yet */ - if (*up<1000) break; /* is 100-999 */ - digits++; - #if DECDPUN>4 /* count the rest ... */ - for (pow=&powers[4]; *up>=*pow; pow++) digits++; - #endif - #endif - #endif - #endif - break; - } /* up */ - return digits; - } /* decGetDigits */ - -#if DECTRACE | DECCHECK -/* ------------------------------------------------------------------ */ -/* decNumberShow -- display a number [debug aid] */ -/* dn is the number to show */ -/* */ -/* Shows: sign, exponent, coefficient (msu first), digits */ -/* or: sign, special-value */ -/* ------------------------------------------------------------------ */ -/* this is public so other modules can use it */ -void uprv_decNumberShow(const decNumber *dn) { - const Unit *up; /* work */ - uInt u, d; /* .. */ - Int cut; /* .. */ - char isign='+'; /* main sign */ - if (dn==NULL) { - printf("NULL\n"); - return;} - if (decNumberIsNegative(dn)) isign='-'; - printf(" >> %c ", isign); - if (dn->bits&DECSPECIAL) { /* Is a special value */ - if (decNumberIsInfinite(dn)) printf("Infinity"); - else { /* a NaN */ - if (dn->bits&DECSNAN) printf("sNaN"); /* signalling NaN */ - else printf("NaN"); - } - /* if coefficient and exponent are 0, no more to do */ - if (dn->exponent==0 && dn->digits==1 && *dn->lsu==0) { - printf("\n"); - return;} - /* drop through to report other information */ - printf(" "); - } - - /* now carefully display the coefficient */ - up=dn->lsu+D2U(dn->digits)-1; /* msu */ - printf("%ld", (LI)*up); - for (up=up-1; up>=dn->lsu; up--) { - u=*up; - printf(":"); - for (cut=DECDPUN-1; cut>=0; cut--) { - d=u/powers[cut]; - u-=d*powers[cut]; - printf("%ld", (LI)d); - } /* cut */ - } /* up */ - if (dn->exponent!=0) { - char esign='+'; - if (dn->exponent<0) esign='-'; - printf(" E%c%ld", esign, (LI)abs(dn->exponent)); - } - printf(" [%ld]\n", (LI)dn->digits); - } /* decNumberShow */ -#endif - -#if DECTRACE || DECCHECK -/* ------------------------------------------------------------------ */ -/* decDumpAr -- display a unit array [debug/check aid] */ -/* name is a single-character tag name */ -/* ar is the array to display */ -/* len is the length of the array in Units */ -/* ------------------------------------------------------------------ */ -static void decDumpAr(char name, const Unit *ar, Int len) { - Int i; - const char *spec; - #if DECDPUN==9 - spec="%09d "; - #elif DECDPUN==8 - spec="%08d "; - #elif DECDPUN==7 - spec="%07d "; - #elif DECDPUN==6 - spec="%06d "; - #elif DECDPUN==5 - spec="%05d "; - #elif DECDPUN==4 - spec="%04d "; - #elif DECDPUN==3 - spec="%03d "; - #elif DECDPUN==2 - spec="%02d "; - #else - spec="%d "; - #endif - printf(" :%c: ", name); - for (i=len-1; i>=0; i--) { - if (i==len-1) printf("%ld ", (LI)ar[i]); - else printf(spec, ar[i]); - } - printf("\n"); - return;} -#endif - -#if DECCHECK -/* ------------------------------------------------------------------ */ -/* decCheckOperands -- check operand(s) to a routine */ -/* res is the result structure (not checked; it will be set to */ -/* quiet NaN if error found (and it is not NULL)) */ -/* lhs is the first operand (may be DECUNRESU) */ -/* rhs is the second (may be DECUNUSED) */ -/* set is the context (may be DECUNCONT) */ -/* returns 0 if both operands, and the context are clean, or 1 */ -/* otherwise (in which case the context will show an error, */ -/* unless NULL). Note that res is not cleaned; caller should */ -/* handle this so res=NULL case is safe. */ -/* The caller is expected to abandon immediately if 1 is returned. */ -/* ------------------------------------------------------------------ */ -static Flag decCheckOperands(decNumber *res, const decNumber *lhs, - const decNumber *rhs, decContext *set) { - Flag bad=0; - if (set==NULL) { /* oops; hopeless */ - #if DECTRACE || DECVERB - printf("Reference to context is NULL.\n"); - #endif - bad=1; - return 1;} - else if (set!=DECUNCONT - && (set->digits<1 || set->round>=DEC_ROUND_MAX)) { - bad=1; - #if DECTRACE || DECVERB - printf("Bad context [digits=%ld round=%ld].\n", - (LI)set->digits, (LI)set->round); - #endif - } - else { - if (res==NULL) { - bad=1; - #if DECTRACE - /* this one not DECVERB as standard tests include NULL */ - printf("Reference to result is NULL.\n"); - #endif - } - if (!bad && lhs!=DECUNUSED) bad=(decCheckNumber(lhs)); - if (!bad && rhs!=DECUNUSED) bad=(decCheckNumber(rhs)); - } - if (bad) { - if (set!=DECUNCONT) uprv_decContextSetStatus(set, DEC_Invalid_operation); - if (res!=DECUNRESU && res!=NULL) { - uprv_decNumberZero(res); - res->bits=DECNAN; /* qNaN */ - } - } - return bad; - } /* decCheckOperands */ - -/* ------------------------------------------------------------------ */ -/* decCheckNumber -- check a number */ -/* dn is the number to check */ -/* returns 0 if the number is clean, or 1 otherwise */ -/* */ -/* The number is considered valid if it could be a result from some */ -/* operation in some valid context. */ -/* ------------------------------------------------------------------ */ -static Flag decCheckNumber(const decNumber *dn) { - const Unit *up; /* work */ - uInt maxuint; /* .. */ - Int ae, d, digits; /* .. */ - Int emin, emax; /* .. */ - - if (dn==NULL) { /* hopeless */ - #if DECTRACE - /* this one not DECVERB as standard tests include NULL */ - printf("Reference to decNumber is NULL.\n"); - #endif - return 1;} - - /* check special values */ - if (dn->bits & DECSPECIAL) { - if (dn->exponent!=0) { - #if DECTRACE || DECVERB - printf("Exponent %ld (not 0) for a special value [%02x].\n", - (LI)dn->exponent, dn->bits); - #endif - return 1;} - - /* 2003.09.08: NaNs may now have coefficients, so next tests Inf only */ - if (decNumberIsInfinite(dn)) { - if (dn->digits!=1) { - #if DECTRACE || DECVERB - printf("Digits %ld (not 1) for an infinity.\n", (LI)dn->digits); - #endif - return 1;} - if (*dn->lsu!=0) { - #if DECTRACE || DECVERB - printf("LSU %ld (not 0) for an infinity.\n", (LI)*dn->lsu); - #endif - decDumpAr('I', dn->lsu, D2U(dn->digits)); - return 1;} - } /* Inf */ - /* 2002.12.26: negative NaNs can now appear through proposed IEEE */ - /* concrete formats (decimal64, etc.). */ - return 0; - } - - /* check the coefficient */ - if (dn->digits<1 || dn->digits>DECNUMMAXP) { - #if DECTRACE || DECVERB - printf("Digits %ld in number.\n", (LI)dn->digits); - #endif - return 1;} - - d=dn->digits; - - for (up=dn->lsu; d>0; up++) { - if (d>DECDPUN) maxuint=DECDPUNMAX; - else { /* reached the msu */ - maxuint=powers[d]-1; - if (dn->digits>1 && *upmaxuint) { - #if DECTRACE || DECVERB - printf("Bad Unit [%08lx] in %ld-digit number at offset %ld [maxuint %ld].\n", - (LI)*up, (LI)dn->digits, (LI)(up-dn->lsu), (LI)maxuint); - #endif - return 1;} - d-=DECDPUN; - } - - /* check the exponent. Note that input operands can have exponents */ - /* which are out of the set->emin/set->emax and set->digits range */ - /* (just as they can have more digits than set->digits). */ - ae=dn->exponent+dn->digits-1; /* adjusted exponent */ - emax=DECNUMMAXE; - emin=DECNUMMINE; - digits=DECNUMMAXP; - if (ae+emax) { - #if DECTRACE || DECVERB - printf("Adjusted exponent overflow [%ld].\n", (LI)ae); - uprv_decNumberShow(dn); - #endif - return 1;} - - return 0; /* it's OK */ - } /* decCheckNumber */ - -/* ------------------------------------------------------------------ */ -/* decCheckInexact -- check a normal finite inexact result has digits */ -/* dn is the number to check */ -/* set is the context (for status and precision) */ -/* sets Invalid operation, etc., if some digits are missing */ -/* [this check is not made for DECSUBSET compilation or when */ -/* subnormal is not set] */ -/* ------------------------------------------------------------------ */ -static void decCheckInexact(const decNumber *dn, decContext *set) { - #if !DECSUBSET && DECEXTFLAG - if ((set->status & (DEC_Inexact|DEC_Subnormal))==DEC_Inexact - && (set->digits!=dn->digits) && !(dn->bits & DECSPECIAL)) { - #if DECTRACE || DECVERB - printf("Insufficient digits [%ld] on normal Inexact result.\n", - (LI)dn->digits); - uprv_decNumberShow(dn); - #endif - uprv_decContextSetStatus(set, DEC_Invalid_operation); - } - #else - /* next is a noop for quiet compiler */ - if (dn!=NULL && dn->digits==0) set->status|=DEC_Invalid_operation; - #endif - return; - } /* decCheckInexact */ -#endif - -#if DECALLOC -#undef malloc -#undef free -/* ------------------------------------------------------------------ */ -/* decMalloc -- accountable allocation routine */ -/* n is the number of bytes to allocate */ -/* */ -/* Semantics is the same as the stdlib malloc routine, but bytes */ -/* allocated are accounted for globally, and corruption fences are */ -/* added before and after the 'actual' storage. */ -/* ------------------------------------------------------------------ */ -/* This routine allocates storage with an extra twelve bytes; 8 are */ -/* at the start and hold: */ -/* 0-3 the original length requested */ -/* 4-7 buffer corruption detection fence (DECFENCE, x4) */ -/* The 4 bytes at the end also hold a corruption fence (DECFENCE, x4) */ -/* ------------------------------------------------------------------ */ -static void *decMalloc(size_t n) { - uInt size=n+12; /* true size */ - void *alloc; /* -> allocated storage */ - uByte *b, *b0; /* work */ - uInt uiwork; /* for macros */ - - alloc=malloc(size); /* -> allocated storage */ - if (alloc==NULL) return NULL; /* out of strorage */ - b0=(uByte *)alloc; /* as bytes */ - decAllocBytes+=n; /* account for storage */ - UBFROMUI(alloc, n); /* save n */ - /* printf(" alloc ++ dAB: %ld (%ld)\n", (LI)decAllocBytes, (LI)n); */ - for (b=b0+4; b play area */ - } /* decMalloc */ - -/* ------------------------------------------------------------------ */ -/* decFree -- accountable free routine */ -/* alloc is the storage to free */ -/* */ -/* Semantics is the same as the stdlib malloc routine, except that */ -/* the global storage accounting is updated and the fences are */ -/* checked to ensure that no routine has written 'out of bounds'. */ -/* ------------------------------------------------------------------ */ -/* This routine first checks that the fences have not been corrupted. */ -/* It then frees the storage using the 'truw' storage address (that */ -/* is, offset by 8). */ -/* ------------------------------------------------------------------ */ -static void decFree(void *alloc) { - uInt n; /* original length */ - uByte *b, *b0; /* work */ - uInt uiwork; /* for macros */ - - if (alloc==NULL) return; /* allowed; it's a nop */ - b0=(uByte *)alloc; /* as bytes */ - b0-=8; /* -> true start of storage */ - n=UBTOUI(b0); /* lift length */ - for (b=b0+4; b4 or DECUSE64=1, the C99 64-bit int64_t and */ +/* uint64_t types may be used. To avoid these, set DECUSE64=0 */ +/* and DECDPUN<=4 (see documentation). */ +/* */ +/* The code also conforms to C99 restrictions; in particular, */ +/* strict aliasing rules are observed. */ +/* */ +/* 2. The decNumber format which this library uses is optimized for */ +/* efficient processing of relatively short numbers; in particular */ +/* it allows the use of fixed sized structures and minimizes copy */ +/* and move operations. It does, however, support arbitrary */ +/* precision (up to 999,999,999 digits) and arbitrary exponent */ +/* range (Emax in the range 0 through 999,999,999 and Emin in the */ +/* range -999,999,999 through 0). Mathematical functions (for */ +/* example decNumberExp) as identified below are restricted more */ +/* tightly: digits, emax, and -emin in the context must be <= */ +/* DEC_MAX_MATH (999999), and their operand(s) must be within */ +/* these bounds. */ +/* */ +/* 3. Logical functions are further restricted; their operands must */ +/* be finite, positive, have an exponent of zero, and all digits */ +/* must be either 0 or 1. The result will only contain digits */ +/* which are 0 or 1 (and will have exponent=0 and a sign of 0). */ +/* */ +/* 4. Operands to operator functions are never modified unless they */ +/* are also specified to be the result number (which is always */ +/* permitted). Other than that case, operands must not overlap. */ +/* */ +/* 5. Error handling: the type of the error is ORed into the status */ +/* flags in the current context (decContext structure). The */ +/* SIGFPE signal is then raised if the corresponding trap-enabler */ +/* flag in the decContext is set (is 1). */ +/* */ +/* It is the responsibility of the caller to clear the status */ +/* flags as required. */ +/* */ +/* The result of any routine which returns a number will always */ +/* be a valid number (which may be a special value, such as an */ +/* Infinity or NaN). */ +/* */ +/* 6. The decNumber format is not an exchangeable concrete */ +/* representation as it comprises fields which may be machine- */ +/* dependent (packed or unpacked, or special length, for example). */ +/* Canonical conversions to and from strings are provided; other */ +/* conversions are available in separate modules. */ +/* */ +/* 7. Normally, input operands are assumed to be valid. Set DECCHECK */ +/* to 1 for extended operand checking (including nullptr operands). */ +/* Results are undefined if a badly-formed structure (or a nullptr */ +/* pointer to a structure) is provided, though with DECCHECK */ +/* enabled the operator routines are protected against exceptions. */ +/* (Except if the result pointer is nullptr, which is unrecoverable.) */ +/* */ +/* However, the routines will never cause exceptions if they are */ +/* given well-formed operands, even if the value of the operands */ +/* is inappropriate for the operation and DECCHECK is not set. */ +/* (Except for SIGFPE, as and where documented.) */ +/* */ +/* 8. Subset arithmetic is available only if DECSUBSET is set to 1. */ +/* ------------------------------------------------------------------ */ +/* Implementation notes for maintenance of this module: */ +/* */ +/* 1. Storage leak protection: Routines which use malloc are not */ +/* permitted to use return for fastpath or error exits (i.e., */ +/* they follow strict structured programming conventions). */ +/* Instead they have a do{}while(0); construct surrounding the */ +/* code which is protected -- break may be used to exit this. */ +/* Other routines can safely use the return statement inline. */ +/* */ +/* Storage leak accounting can be enabled using DECALLOC. */ +/* */ +/* 2. All loops use the for(;;) construct. Any do construct does */ +/* not loop; it is for allocation protection as just described. */ +/* */ +/* 3. Setting status in the context must always be the very last */ +/* action in a routine, as non-0 status may raise a trap and hence */ +/* the call to set status may not return (if the handler uses long */ +/* jump). Therefore all cleanup must be done first. In general, */ +/* to achieve this status is accumulated and is only applied just */ +/* before return by calling decContextSetStatus (via decStatus). */ +/* */ +/* Routines which allocate storage cannot, in general, use the */ +/* 'top level' routines which could cause a non-returning */ +/* transfer of control. The decXxxxOp routines are safe (do not */ +/* call decStatus even if traps are set in the context) and should */ +/* be used instead (they are also a little faster). */ +/* */ +/* 4. Exponent checking is minimized by allowing the exponent to */ +/* grow outside its limits during calculations, provided that */ +/* the decFinalize function is called later. Multiplication and */ +/* division, and intermediate calculations in exponentiation, */ +/* require more careful checks because of the risk of 31-bit */ +/* overflow (the most negative valid exponent is -1999999997, for */ +/* a 999999999-digit number with adjusted exponent of -999999999). */ +/* */ +/* 5. Rounding is deferred until finalization of results, with any */ +/* 'off to the right' data being represented as a single digit */ +/* residue (in the range -1 through 9). This avoids any double- */ +/* rounding when more than one shortening takes place (for */ +/* example, when a result is subnormal). */ +/* */ +/* 6. The digits count is allowed to rise to a multiple of DECDPUN */ +/* during many operations, so whole Units are handled and exact */ +/* accounting of digits is not needed. The correct digits value */ +/* is found by decGetDigits, which accounts for leading zeros. */ +/* This must be called before any rounding if the number of digits */ +/* is not known exactly. */ +/* */ +/* 7. The multiply-by-reciprocal 'trick' is used for partitioning */ +/* numbers up to four digits, using appropriate constants. This */ +/* is not useful for longer numbers because overflow of 32 bits */ +/* would lead to 4 multiplies, which is almost as expensive as */ +/* a divide (unless a floating-point or 64-bit multiply is */ +/* assumed to be available). */ +/* */ +/* 8. Unusual abbreviations that may be used in the commentary: */ +/* lhs -- left hand side (operand, of an operation) */ +/* lsd -- least significant digit (of coefficient) */ +/* lsu -- least significant Unit (of coefficient) */ +/* msd -- most significant digit (of coefficient) */ +/* msi -- most significant item (in an array) */ +/* msu -- most significant Unit (of coefficient) */ +/* rhs -- right hand side (operand, of an operation) */ +/* +ve -- positive */ +/* -ve -- negative */ +/* ** -- raise to the power */ +/* ------------------------------------------------------------------ */ + +#include /* for malloc, free, etc. */ +/* #include */ /* for printf [if needed] */ +#include /* for strcpy */ +#include /* for lower */ +#include "cmemory.h" /* for uprv_malloc, etc., in ICU */ +#include "decNumber.h" /* base number library */ +#include "decNumberLocal.h" /* decNumber local types, etc. */ +#include "uassert.h" + +/* Constants */ +/* Public lookup table used by the D2U macro */ +static const uByte d2utable[DECMAXD2U+1]=D2UTABLE; + +#define DECVERB 1 /* set to 1 for verbose DECCHECK */ +#define powers DECPOWERS /* old internal name */ + +/* Local constants */ +#define DIVIDE 0x80 /* Divide operators */ +#define REMAINDER 0x40 /* .. */ +#define DIVIDEINT 0x20 /* .. */ +#define REMNEAR 0x10 /* .. */ +#define COMPARE 0x01 /* Compare operators */ +#define COMPMAX 0x02 /* .. */ +#define COMPMIN 0x03 /* .. */ +#define COMPTOTAL 0x04 /* .. */ +#define COMPNAN 0x05 /* .. [NaN processing] */ +#define COMPSIG 0x06 /* .. [signaling COMPARE] */ +#define COMPMAXMAG 0x07 /* .. */ +#define COMPMINMAG 0x08 /* .. */ + +#define DEC_sNaN 0x40000000 /* local status: sNaN signal */ +#define BADINT (Int)0x80000000 /* most-negative Int; error indicator */ +/* Next two indicate an integer >= 10**6, and its parity (bottom bit) */ +#define BIGEVEN (Int)0x80000002 +#define BIGODD (Int)0x80000003 + +static const Unit uarrone[1]={1}; /* Unit array of 1, used for incrementing */ + +/* ------------------------------------------------------------------ */ +/* round-for-reround digits */ +/* ------------------------------------------------------------------ */ +#if 0 +static const uByte DECSTICKYTAB[10]={1,1,2,3,4,6,6,7,8,9}; /* used if sticky */ +#endif + +/* ------------------------------------------------------------------ */ +/* Powers of ten (powers[n]==10**n, 0<=n<=9) */ +/* ------------------------------------------------------------------ */ +static const uInt DECPOWERS[10]={1, 10, 100, 1000, 10000, 100000, 1000000, + 10000000, 100000000, 1000000000}; + + +/* Granularity-dependent code */ +#if DECDPUN<=4 + #define eInt Int /* extended integer */ + #define ueInt uInt /* unsigned extended integer */ + /* Constant multipliers for divide-by-power-of five using reciprocal */ + /* multiply, after removing powers of 2 by shifting, and final shift */ + /* of 17 [we only need up to **4] */ + static const uInt multies[]={131073, 26215, 5243, 1049, 210}; + /* QUOT10 -- macro to return the quotient of unit u divided by 10**n */ + #define QUOT10(u, n) ((((uInt)(u)>>(n))*multies[n])>>17) +#else + /* For DECDPUN>4 non-ANSI-89 64-bit types are needed. */ + #if !DECUSE64 + #error decNumber.c: DECUSE64 must be 1 when DECDPUN>4 + #endif + #define eInt Long /* extended integer */ + #define ueInt uLong /* unsigned extended integer */ +#endif + +/* Local routines */ +static decNumber * decAddOp(decNumber *, const decNumber *, const decNumber *, + decContext *, uByte, uInt *); +static Flag decBiStr(const char *, const char *, const char *); +static uInt decCheckMath(const decNumber *, decContext *, uInt *); +static void decApplyRound(decNumber *, decContext *, Int, uInt *); +static Int decCompare(const decNumber *lhs, const decNumber *rhs, Flag); +static decNumber * decCompareOp(decNumber *, const decNumber *, + const decNumber *, decContext *, + Flag, uInt *); +static void decCopyFit(decNumber *, const decNumber *, decContext *, + Int *, uInt *); +static decNumber * decDecap(decNumber *, Int); +static decNumber * decDivideOp(decNumber *, const decNumber *, + const decNumber *, decContext *, Flag, uInt *); +static decNumber * decExpOp(decNumber *, const decNumber *, + decContext *, uInt *); +static void decFinalize(decNumber *, decContext *, Int *, uInt *); +static Int decGetDigits(Unit *, Int); +static Int decGetInt(const decNumber *); +static decNumber * decLnOp(decNumber *, const decNumber *, + decContext *, uInt *); +static decNumber * decMultiplyOp(decNumber *, const decNumber *, + const decNumber *, decContext *, + uInt *); +static decNumber * decNaNs(decNumber *, const decNumber *, + const decNumber *, decContext *, uInt *); +static decNumber * decQuantizeOp(decNumber *, const decNumber *, + const decNumber *, decContext *, Flag, + uInt *); +static void decReverse(Unit *, Unit *); +static void decSetCoeff(decNumber *, decContext *, const Unit *, + Int, Int *, uInt *); +static void decSetMaxValue(decNumber *, decContext *); +static void decSetOverflow(decNumber *, decContext *, uInt *); +static void decSetSubnormal(decNumber *, decContext *, Int *, uInt *); +static Int decShiftToLeast(Unit *, Int, Int); +static Int decShiftToMost(Unit *, Int, Int); +static void decStatus(decNumber *, uInt, decContext *); +static void decToString(const decNumber *, char[], Flag); +static decNumber * decTrim(decNumber *, decContext *, Flag, Flag, Int *); +static Int decUnitAddSub(const Unit *, Int, const Unit *, Int, Int, + Unit *, Int); +static Int decUnitCompare(const Unit *, Int, const Unit *, Int, Int); + +#if !DECSUBSET +/* decFinish == decFinalize when no subset arithmetic needed */ +#define decFinish(a,b,c,d) decFinalize(a,b,c,d) +#else +static void decFinish(decNumber *, decContext *, Int *, uInt *); +static decNumber * decRoundOperand(const decNumber *, decContext *, uInt *); +#endif + +/* Local macros */ +/* masked special-values bits */ +#define SPECIALARG (rhs->bits & DECSPECIAL) +#define SPECIALARGS ((lhs->bits | rhs->bits) & DECSPECIAL) + +/* For use in ICU */ +#define malloc(a) uprv_malloc(a) +#define free(a) uprv_free(a) + +/* Diagnostic macros, etc. */ +#if DECALLOC +/* Handle malloc/free accounting. If enabled, our accountable routines */ +/* are used; otherwise the code just goes straight to the system malloc */ +/* and free routines. */ +#define malloc(a) decMalloc(a) +#define free(a) decFree(a) +#define DECFENCE 0x5a /* corruption detector */ +/* 'Our' malloc and free: */ +static void *decMalloc(size_t); +static void decFree(void *); +uInt decAllocBytes=0; /* count of bytes allocated */ +/* Note that DECALLOC code only checks for storage buffer overflow. */ +/* To check for memory leaks, the decAllocBytes variable must be */ +/* checked to be 0 at appropriate times (e.g., after the test */ +/* harness completes a set of tests). This checking may be unreliable */ +/* if the testing is done in a multi-thread environment. */ +#endif + +#if DECCHECK +/* Optional checking routines. Enabling these means that decNumber */ +/* and decContext operands to operator routines are checked for */ +/* correctness. This roughly doubles the execution time of the */ +/* fastest routines (and adds 600+ bytes), so should not normally be */ +/* used in 'production'. */ +/* decCheckInexact is used to check that inexact results have a full */ +/* complement of digits (where appropriate -- this is not the case */ +/* for Quantize, for example) */ +#define DECUNRESU ((decNumber *)(void *)0xffffffff) +#define DECUNUSED ((const decNumber *)(void *)0xffffffff) +#define DECUNCONT ((decContext *)(void *)(0xffffffff)) +static Flag decCheckOperands(decNumber *, const decNumber *, + const decNumber *, decContext *); +static Flag decCheckNumber(const decNumber *); +static void decCheckInexact(const decNumber *, decContext *); +#endif + +#if DECTRACE || DECCHECK +/* Optional trace/debugging routines (may or may not be used) */ +void decNumberShow(const decNumber *); /* displays the components of a number */ +static void decDumpAr(char, const Unit *, Int); +#endif + +/* ================================================================== */ +/* Conversions */ +/* ================================================================== */ + +/* ------------------------------------------------------------------ */ +/* from-int32 -- conversion from Int or uInt */ +/* */ +/* dn is the decNumber to receive the integer */ +/* in or uin is the integer to be converted */ +/* returns dn */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberFromInt32(decNumber *dn, Int in) { + uInt unsig; + if (in>=0) unsig=in; + else { /* negative (possibly BADINT) */ + if (in==BADINT) unsig=(uInt)1073741824*2; /* special case */ + else unsig=-in; /* invert */ + } + /* in is now positive */ + uprv_decNumberFromUInt32(dn, unsig); + if (in<0) dn->bits=DECNEG; /* sign needed */ + return dn; + } /* decNumberFromInt32 */ + +U_CAPI decNumber * U_EXPORT2 uprv_decNumberFromUInt32(decNumber *dn, uInt uin) { + Unit *up; /* work pointer */ + uprv_decNumberZero(dn); /* clean */ + if (uin==0) return dn; /* [or decGetDigits bad call] */ + for (up=dn->lsu; uin>0; up++) { + *up=(Unit)(uin%(DECDPUNMAX+1)); + uin=uin/(DECDPUNMAX+1); + } + dn->digits=decGetDigits(dn->lsu, static_cast(up - dn->lsu)); + return dn; + } /* decNumberFromUInt32 */ + +/* ------------------------------------------------------------------ */ +/* to-int32 -- conversion to Int or uInt */ +/* */ +/* dn is the decNumber to convert */ +/* set is the context for reporting errors */ +/* returns the converted decNumber, or 0 if Invalid is set */ +/* */ +/* Invalid is set if the decNumber does not have exponent==0 or if */ +/* it is a NaN, Infinite, or out-of-range. */ +/* ------------------------------------------------------------------ */ +U_CAPI Int U_EXPORT2 uprv_decNumberToInt32(const decNumber *dn, decContext *set) { + #if DECCHECK + if (decCheckOperands(DECUNRESU, DECUNUSED, dn, set)) return 0; + #endif + + /* special or too many digits, or bad exponent */ + if (dn->bits&DECSPECIAL || dn->digits>10 || dn->exponent!=0) ; /* bad */ + else { /* is a finite integer with 10 or fewer digits */ + Int d; /* work */ + const Unit *up; /* .. */ + uInt hi=0, lo; /* .. */ + up=dn->lsu; /* -> lsu */ + lo=*up; /* get 1 to 9 digits */ + #if DECDPUN>1 /* split to higher */ + hi=lo/10; + lo=lo%10; + #endif + up++; + /* collect remaining Units, if any, into hi */ + for (d=DECDPUN; ddigits; up++, d+=DECDPUN) hi+=*up*powers[d-1]; + /* now low has the lsd, hi the remainder */ + if (hi>214748364 || (hi==214748364 && lo>7)) { /* out of range? */ + /* most-negative is a reprieve */ + if (dn->bits&DECNEG && hi==214748364 && lo==8) return 0x80000000; + /* bad -- drop through */ + } + else { /* in-range always */ + Int i=X10(hi)+lo; + if (dn->bits&DECNEG) return -i; + return i; + } + } /* integer */ + uprv_decContextSetStatus(set, DEC_Invalid_operation); /* [may not return] */ + return 0; + } /* decNumberToInt32 */ + +U_CAPI uInt U_EXPORT2 uprv_decNumberToUInt32(const decNumber *dn, decContext *set) { + #if DECCHECK + if (decCheckOperands(DECUNRESU, DECUNUSED, dn, set)) return 0; + #endif + /* special or too many digits, or bad exponent, or negative (<0) */ + if (dn->bits&DECSPECIAL || dn->digits>10 || dn->exponent!=0 + || (dn->bits&DECNEG && !ISZERO(dn))); /* bad */ + else { /* is a finite integer with 10 or fewer digits */ + Int d; /* work */ + const Unit *up; /* .. */ + uInt hi=0, lo; /* .. */ + up=dn->lsu; /* -> lsu */ + lo=*up; /* get 1 to 9 digits */ + #if DECDPUN>1 /* split to higher */ + hi=lo/10; + lo=lo%10; + #endif + up++; + /* collect remaining Units, if any, into hi */ + for (d=DECDPUN; ddigits; up++, d+=DECDPUN) hi+=*up*powers[d-1]; + + /* now low has the lsd, hi the remainder */ + if (hi>429496729 || (hi==429496729 && lo>5)) ; /* no reprieve possible */ + else return X10(hi)+lo; + } /* integer */ + uprv_decContextSetStatus(set, DEC_Invalid_operation); /* [may not return] */ + return 0; + } /* decNumberToUInt32 */ + +/* ------------------------------------------------------------------ */ +/* to-scientific-string -- conversion to numeric string */ +/* to-engineering-string -- conversion to numeric string */ +/* */ +/* decNumberToString(dn, string); */ +/* decNumberToEngString(dn, string); */ +/* */ +/* dn is the decNumber to convert */ +/* string is the string where the result will be laid out */ +/* */ +/* string must be at least dn->digits+14 characters long */ +/* */ +/* No error is possible, and no status can be set. */ +/* ------------------------------------------------------------------ */ +U_CAPI char * U_EXPORT2 uprv_decNumberToString(const decNumber *dn, char *string){ + decToString(dn, string, 0); + return string; + } /* DecNumberToString */ + +U_CAPI char * U_EXPORT2 uprv_decNumberToEngString(const decNumber *dn, char *string){ + decToString(dn, string, 1); + return string; + } /* DecNumberToEngString */ + +/* ------------------------------------------------------------------ */ +/* to-number -- conversion from numeric string */ +/* */ +/* decNumberFromString -- convert string to decNumber */ +/* dn -- the number structure to fill */ +/* chars[] -- the string to convert ('\0' terminated) */ +/* set -- the context used for processing any error, */ +/* determining the maximum precision available */ +/* (set.digits), determining the maximum and minimum */ +/* exponent (set.emax and set.emin), determining if */ +/* extended values are allowed, and checking the */ +/* rounding mode if overflow occurs or rounding is */ +/* needed. */ +/* */ +/* The length of the coefficient and the size of the exponent are */ +/* checked by this routine, so the correct error (Underflow or */ +/* Overflow) can be reported or rounding applied, as necessary. */ +/* */ +/* If bad syntax is detected, the result will be a quiet NaN. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberFromString(decNumber *dn, const char chars[], + decContext *set) { + Int exponent=0; /* working exponent [assume 0] */ + uByte bits=0; /* working flags [assume +ve] */ + Unit *res; /* where result will be built */ + Unit resbuff[SD2U(DECBUFFER+9)];/* local buffer in case need temporary */ + /* [+9 allows for ln() constants] */ + Unit *allocres=nullptr; /* -> allocated result, iff allocated */ + Int d=0; /* count of digits found in decimal part */ + const char *dotchar=nullptr; /* where dot was found */ + const char *cfirst=chars; /* -> first character of decimal part */ + const char *last=nullptr; /* -> last digit of decimal part */ + const char *c; /* work */ + Unit *up; /* .. */ + #if DECDPUN>1 + Int cut, out; /* .. */ + #endif + Int residue; /* rounding residue */ + uInt status=0; /* error code */ + + #if DECCHECK + if (decCheckOperands(DECUNRESU, DECUNUSED, DECUNUSED, set)) + return uprv_decNumberZero(dn); + #endif + + do { /* status & malloc protection */ + for (c=chars;; c++) { /* -> input character */ + if (*c>='0' && *c<='9') { /* test for Arabic digit */ + last=c; + d++; /* count of real digits */ + continue; /* still in decimal part */ + } + if (*c=='.' && dotchar==nullptr) { /* first '.' */ + dotchar=c; /* record offset into decimal part */ + if (c==cfirst) cfirst++; /* first digit must follow */ + continue;} + if (c==chars) { /* first in string... */ + if (*c=='-') { /* valid - sign */ + cfirst++; + bits=DECNEG; + continue;} + if (*c=='+') { /* valid + sign */ + cfirst++; + continue;} + } + /* *c is not a digit, or a valid +, -, or '.' */ + break; + } /* c */ + + if (last==nullptr) { /* no digits yet */ + status=DEC_Conversion_syntax;/* assume the worst */ + if (*c=='\0') break; /* and no more to come... */ + #if DECSUBSET + /* if subset then infinities and NaNs are not allowed */ + if (!set->extended) break; /* hopeless */ + #endif + /* Infinities and NaNs are possible, here */ + if (dotchar!=nullptr) break; /* .. unless had a dot */ + uprv_decNumberZero(dn); /* be optimistic */ + if (decBiStr(c, "infinity", "INFINITY") + || decBiStr(c, "inf", "INF")) { + dn->bits=bits | DECINF; + status=0; /* is OK */ + break; /* all done */ + } + /* a NaN expected */ + /* 2003.09.10 NaNs are now permitted to have a sign */ + dn->bits=bits | DECNAN; /* assume simple NaN */ + if (*c=='s' || *c=='S') { /* looks like an sNaN */ + c++; + dn->bits=bits | DECSNAN; + } + if (*c!='n' && *c!='N') break; /* check caseless "NaN" */ + c++; + if (*c!='a' && *c!='A') break; /* .. */ + c++; + if (*c!='n' && *c!='N') break; /* .. */ + c++; + /* now either nothing, or nnnn payload, expected */ + /* -> start of integer and skip leading 0s [including plain 0] */ + for (cfirst=c; *cfirst=='0';) cfirst++; + if (*cfirst=='\0') { /* "NaN" or "sNaN", maybe with all 0s */ + status=0; /* it's good */ + break; /* .. */ + } + /* something other than 0s; setup last and d as usual [no dots] */ + for (c=cfirst;; c++, d++) { + if (*c<'0' || *c>'9') break; /* test for Arabic digit */ + last=c; + } + if (*c!='\0') break; /* not all digits */ + if (d>set->digits-1) { + /* [NB: payload in a decNumber can be full length unless */ + /* clamped, in which case can only be digits-1] */ + if (set->clamp) break; + if (d>set->digits) break; + } /* too many digits? */ + /* good; drop through to convert the integer to coefficient */ + status=0; /* syntax is OK */ + bits=dn->bits; /* for copy-back */ + } /* last==nullptr */ + + else if (*c!='\0') { /* more to process... */ + /* had some digits; exponent is only valid sequence now */ + Flag nege; /* 1=negative exponent */ + const char *firstexp; /* -> first significant exponent digit */ + status=DEC_Conversion_syntax;/* assume the worst */ + if (*c!='e' && *c!='E') break; + /* Found 'e' or 'E' -- now process explicit exponent */ + /* 1998.07.11: sign no longer required */ + nege=0; + c++; /* to (possible) sign */ + if (*c=='-') {nege=1; c++;} + else if (*c=='+') c++; + if (*c=='\0') break; + + for (; *c=='0' && *(c+1)!='\0';) c++; /* strip insignificant zeros */ + firstexp=c; /* save exponent digit place */ + uInt uexponent = 0; /* Avoid undefined behavior on signed int overflow */ + for (; ;c++) { + if (*c<'0' || *c>'9') break; /* not a digit */ + uexponent=X10(uexponent)+(uInt)*c-(uInt)'0'; + } /* c */ + exponent = (Int)uexponent; + /* if not now on a '\0', *c must not be a digit */ + if (*c!='\0') break; + + /* (this next test must be after the syntax checks) */ + /* if it was too long the exponent may have wrapped, so check */ + /* carefully and set it to a certain overflow if wrap possible */ + if (c>=firstexp+9+1) { + if (c>firstexp+9+1 || *firstexp>'1') exponent=DECNUMMAXE*2; + /* [up to 1999999999 is OK, for example 1E-1000000998] */ + } + if (nege) exponent=-exponent; /* was negative */ + status=0; /* is OK */ + } /* stuff after digits */ + + /* Here when whole string has been inspected; syntax is good */ + /* cfirst->first digit (never dot), last->last digit (ditto) */ + + /* strip leading zeros/dot [leave final 0 if all 0's] */ + if (*cfirst=='0') { /* [cfirst has stepped over .] */ + for (c=cfirst; cextended) { + uprv_decNumberZero(dn); /* clean result */ + break; /* [could be return] */ + } + #endif + } /* at least one leading 0 */ + + /* Handle decimal point... */ + if (dotchar!=nullptr && dotchar(last-dotchar); /* adjust exponent */ + /* [we can now ignore the .] */ + + /* OK, the digits string is good. Assemble in the decNumber, or in */ + /* a temporary units array if rounding is needed */ + if (d<=set->digits) res=dn->lsu; /* fits into supplied decNumber */ + else { /* rounding needed */ + Int needbytes=D2U(d)*sizeof(Unit);/* bytes needed */ + res=resbuff; /* assume use local buffer */ + if (needbytes>(Int)sizeof(resbuff)) { /* too big for local */ + allocres=(Unit *)malloc(needbytes); + if (allocres==nullptr) {status|=DEC_Insufficient_storage; break;} + res=allocres; + } + } + /* res now -> number lsu, buffer, or allocated storage for Unit array */ + + /* Place the coefficient into the selected Unit array */ + /* [this is often 70% of the cost of this function when DECDPUN>1] */ + #if DECDPUN>1 + out=0; /* accumulator */ + up=res+D2U(d)-1; /* -> msu */ + cut=d-(up-res)*DECDPUN; /* digits in top unit */ + for (c=cfirst;; c++) { /* along the digits */ + if (*c=='.') continue; /* ignore '.' [don't decrement cut] */ + out=X10(out)+(Int)*c-(Int)'0'; + if (c==last) break; /* done [never get to trailing '.'] */ + cut--; + if (cut>0) continue; /* more for this unit */ + *up=(Unit)out; /* write unit */ + up--; /* prepare for unit below.. */ + cut=DECDPUN; /* .. */ + out=0; /* .. */ + } /* c */ + *up=(Unit)out; /* write lsu */ + + #else + /* DECDPUN==1 */ + up=res; /* -> lsu */ + for (c=last; c>=cfirst; c--) { /* over each character, from least */ + if (*c=='.') continue; /* ignore . [don't step up] */ + *up=(Unit)((Int)*c-(Int)'0'); + up++; + } /* c */ + #endif + + dn->bits=bits; + dn->exponent=exponent; + dn->digits=d; + + /* if not in number (too long) shorten into the number */ + if (d>set->digits) { + residue=0; + decSetCoeff(dn, set, res, d, &residue, &status); + /* always check for overflow or subnormal and round as needed */ + decFinalize(dn, set, &residue, &status); + } + else { /* no rounding, but may still have overflow or subnormal */ + /* [these tests are just for performance; finalize repeats them] */ + if ((dn->exponent-1emin-dn->digits) + || (dn->exponent-1>set->emax-set->digits)) { + residue=0; + decFinalize(dn, set, &residue, &status); + } + } + /* decNumberShow(dn); */ + } while(0); /* [for break] */ + + if (allocres!=nullptr) free(allocres); /* drop any storage used */ + if (status!=0) decStatus(dn, status, set); + return dn; + } /* decNumberFromString */ + +/* ================================================================== */ +/* Operators */ +/* ================================================================== */ + +/* ------------------------------------------------------------------ */ +/* decNumberAbs -- absolute value operator */ +/* */ +/* This computes C = abs(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context */ +/* */ +/* See also decNumberCopyAbs for a quiet bitwise version of this. */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +/* This has the same effect as decNumberPlus unless A is negative, */ +/* in which case it has the same effect as decNumberMinus. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberAbs(decNumber *res, const decNumber *rhs, + decContext *set) { + decNumber dzero; /* for 0 */ + uInt status=0; /* accumulator */ + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + uprv_decNumberZero(&dzero); /* set 0 */ + dzero.exponent=rhs->exponent; /* [no coefficient expansion] */ + decAddOp(res, &dzero, rhs, set, (uByte)(rhs->bits & DECNEG), &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberAbs */ + +/* ------------------------------------------------------------------ */ +/* decNumberAdd -- add two Numbers */ +/* */ +/* This computes C = A + B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X+X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +/* This just calls the routine shared with Subtract */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberAdd(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decAddOp(res, lhs, rhs, set, 0, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberAdd */ + +/* ------------------------------------------------------------------ */ +/* decNumberAnd -- AND two Numbers, digitwise */ +/* */ +/* This computes C = A & B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X&X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context (used for result length and error report) */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Logical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberAnd(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + const Unit *ua, *ub; /* -> operands */ + const Unit *msua, *msub; /* -> operand msus */ + Unit *uc, *msuc; /* -> result and its msu */ + Int msudigs; /* digits in res msu */ + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + if (lhs->exponent!=0 || decNumberIsSpecial(lhs) || decNumberIsNegative(lhs) + || rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + + /* operands are valid */ + ua=lhs->lsu; /* bottom-up */ + ub=rhs->lsu; /* .. */ + uc=res->lsu; /* .. */ + msua=ua+D2U(lhs->digits)-1; /* -> msu of lhs */ + msub=ub+D2U(rhs->digits)-1; /* -> msu of rhs */ + msuc=uc+D2U(set->digits)-1; /* -> msu of result */ + msudigs=MSUDIGITS(set->digits); /* [faster than remainder] */ + for (; uc<=msuc; ua++, ub++, uc++) { /* Unit loop */ + Unit a, b; /* extract units */ + if (ua>msua) a=0; + else a=*ua; + if (ub>msub) b=0; + else b=*ub; + *uc=0; /* can now write back */ + if (a|b) { /* maybe 1 bits to examine */ + Int i, j; + *uc=0; /* can now write back */ + /* This loop could be unrolled and/or use BIN2BCD tables */ + for (i=0; i1) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + if (uc==msuc && i==msudigs-1) break; /* just did final digit */ + } /* each digit */ + } /* both OK */ + } /* each unit */ + /* [here uc-1 is the msu of the result] */ + res->digits=decGetDigits(res->lsu, static_cast(uc - res->lsu)); + res->exponent=0; /* integer */ + res->bits=0; /* sign=0 */ + return res; /* [no status to set] */ + } /* decNumberAnd */ + +/* ------------------------------------------------------------------ */ +/* decNumberCompare -- compare two Numbers */ +/* */ +/* This computes C = A ? B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for one digit (or NaN). */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompare(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decCompareOp(res, lhs, rhs, set, COMPARE, &status); + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberCompare */ + +/* ------------------------------------------------------------------ */ +/* decNumberCompareSignal -- compare, signalling on all NaNs */ +/* */ +/* This computes C = A ? B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for one digit (or NaN). */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompareSignal(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decCompareOp(res, lhs, rhs, set, COMPSIG, &status); + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberCompareSignal */ + +/* ------------------------------------------------------------------ */ +/* decNumberCompareTotal -- compare two Numbers, using total ordering */ +/* */ +/* This computes C = A ? B, under total ordering */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for one digit; the result will always be one of */ +/* -1, 0, or 1. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompareTotal(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decCompareOp(res, lhs, rhs, set, COMPTOTAL, &status); + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberCompareTotal */ + +/* ------------------------------------------------------------------ */ +/* decNumberCompareTotalMag -- compare, total ordering of magnitudes */ +/* */ +/* This computes C = |A| ? |B|, under total ordering */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for one digit; the result will always be one of */ +/* -1, 0, or 1. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompareTotalMag(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + uInt needbytes; /* for space calculations */ + decNumber bufa[D2N(DECBUFFER+1)];/* +1 in case DECBUFFER=0 */ + decNumber *allocbufa=nullptr; /* -> allocated bufa, iff allocated */ + decNumber bufb[D2N(DECBUFFER+1)]; + decNumber *allocbufb=nullptr; /* -> allocated bufb, iff allocated */ + decNumber *a, *b; /* temporary pointers */ + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + do { /* protect allocated storage */ + /* if either is negative, take a copy and absolute */ + if (decNumberIsNegative(lhs)) { /* lhs<0 */ + a=bufa; + needbytes=sizeof(decNumber)+(D2U(lhs->digits)-1)*sizeof(Unit); + if (needbytes>sizeof(bufa)) { /* need malloc space */ + allocbufa=(decNumber *)malloc(needbytes); + if (allocbufa==nullptr) { /* hopeless -- abandon */ + status|=DEC_Insufficient_storage; + break;} + a=allocbufa; /* use the allocated space */ + } + uprv_decNumberCopy(a, lhs); /* copy content */ + a->bits&=~DECNEG; /* .. and clear the sign */ + lhs=a; /* use copy from here on */ + } + if (decNumberIsNegative(rhs)) { /* rhs<0 */ + b=bufb; + needbytes=sizeof(decNumber)+(D2U(rhs->digits)-1)*sizeof(Unit); + if (needbytes>sizeof(bufb)) { /* need malloc space */ + allocbufb=(decNumber *)malloc(needbytes); + if (allocbufb==nullptr) { /* hopeless -- abandon */ + status|=DEC_Insufficient_storage; + break;} + b=allocbufb; /* use the allocated space */ + } + uprv_decNumberCopy(b, rhs); /* copy content */ + b->bits&=~DECNEG; /* .. and clear the sign */ + rhs=b; /* use copy from here on */ + } + decCompareOp(res, lhs, rhs, set, COMPTOTAL, &status); + } while(0); /* end protected */ + + if (allocbufa!=nullptr) free(allocbufa); /* drop any storage used */ + if (allocbufb!=nullptr) free(allocbufb); /* .. */ + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberCompareTotalMag */ + +/* ------------------------------------------------------------------ */ +/* decNumberDivide -- divide one number by another */ +/* */ +/* This computes C = A / B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X/X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberDivide(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decDivideOp(res, lhs, rhs, set, DIVIDE, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberDivide */ + +/* ------------------------------------------------------------------ */ +/* decNumberDivideInteger -- divide and return integer quotient */ +/* */ +/* This computes C = A # B, where # is the integer divide operator */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X#X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberDivideInteger(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decDivideOp(res, lhs, rhs, set, DIVIDEINT, &status); + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberDivideInteger */ + +/* ------------------------------------------------------------------ */ +/* decNumberExp -- exponentiation */ +/* */ +/* This computes C = exp(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context; note that rounding mode has no effect */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Mathematical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* */ +/* Finite results will always be full precision and Inexact, except */ +/* when A is a zero or -Infinity (giving 1 or 0 respectively). */ +/* */ +/* An Inexact result is rounded using DEC_ROUND_HALF_EVEN; it will */ +/* almost always be correctly rounded, but may be up to 1 ulp in */ +/* error in rare cases. */ +/* ------------------------------------------------------------------ */ +/* This is a wrapper for decExpOp which can handle the slightly wider */ +/* (double) range needed by Ln (which has to be able to calculate */ +/* exp(-a) where a can be the tiniest number (Ntiny). */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberExp(decNumber *res, const decNumber *rhs, + decContext *set) { + uInt status=0; /* accumulator */ + #if DECSUBSET + decNumber *allocrhs=nullptr; /* non-nullptr if rounded rhs allocated */ + #endif + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + /* Check restrictions; these restrictions ensure that if h=8 (see */ + /* decExpOp) then the result will either overflow or underflow to 0. */ + /* Other math functions restrict the input range, too, for inverses. */ + /* If not violated then carry out the operation. */ + if (!decCheckMath(rhs, set, &status)) do { /* protect allocation */ + #if DECSUBSET + if (!set->extended) { + /* reduce operand and set lostDigits status, as needed */ + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, &status); + if (allocrhs==nullptr) break; + rhs=allocrhs; + } + } + #endif + decExpOp(res, rhs, set, &status); + } while(0); /* end protected */ + + #if DECSUBSET + if (allocrhs !=nullptr) free(allocrhs); /* drop any storage used */ + #endif + /* apply significant status */ + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberExp */ + +/* ------------------------------------------------------------------ */ +/* decNumberFMA -- fused multiply add */ +/* */ +/* This computes D = (A * B) + C with only one rounding */ +/* */ +/* res is D, the result. D may be A or B or C (e.g., X=FMA(X,X,X)) */ +/* lhs is A */ +/* rhs is B */ +/* fhs is C [far hand side] */ +/* set is the context */ +/* */ +/* Mathematical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberFMA(decNumber *res, const decNumber *lhs, + const decNumber *rhs, const decNumber *fhs, + decContext *set) { + uInt status=0; /* accumulator */ + decContext dcmul; /* context for the multiplication */ + uInt needbytes; /* for space calculations */ + decNumber bufa[D2N(DECBUFFER*2+1)]; + decNumber *allocbufa=nullptr; /* -> allocated bufa, iff allocated */ + decNumber *acc; /* accumulator pointer */ + decNumber dzero; /* work */ + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + if (decCheckOperands(res, fhs, DECUNUSED, set)) return res; + #endif + + do { /* protect allocated storage */ + #if DECSUBSET + if (!set->extended) { /* [undefined if subset] */ + status|=DEC_Invalid_operation; + break;} + #endif + /* Check math restrictions [these ensure no overflow or underflow] */ + if ((!decNumberIsSpecial(lhs) && decCheckMath(lhs, set, &status)) + || (!decNumberIsSpecial(rhs) && decCheckMath(rhs, set, &status)) + || (!decNumberIsSpecial(fhs) && decCheckMath(fhs, set, &status))) break; + /* set up context for multiply */ + dcmul=*set; + dcmul.digits=lhs->digits+rhs->digits; /* just enough */ + /* [The above may be an over-estimate for subset arithmetic, but that's OK] */ + dcmul.emax=DEC_MAX_EMAX; /* effectively unbounded .. */ + dcmul.emin=DEC_MIN_EMIN; /* [thanks to Math restrictions] */ + /* set up decNumber space to receive the result of the multiply */ + acc=bufa; /* may fit */ + needbytes=sizeof(decNumber)+(D2U(dcmul.digits)-1)*sizeof(Unit); + if (needbytes>sizeof(bufa)) { /* need malloc space */ + allocbufa=(decNumber *)malloc(needbytes); + if (allocbufa==nullptr) { /* hopeless -- abandon */ + status|=DEC_Insufficient_storage; + break;} + acc=allocbufa; /* use the allocated space */ + } + /* multiply with extended range and necessary precision */ + /*printf("emin=%ld\n", dcmul.emin); */ + decMultiplyOp(acc, lhs, rhs, &dcmul, &status); + /* Only Invalid operation (from sNaN or Inf * 0) is possible in */ + /* status; if either is seen than ignore fhs (in case it is */ + /* another sNaN) and set acc to NaN unless we had an sNaN */ + /* [decMultiplyOp leaves that to caller] */ + /* Note sNaN has to go through addOp to shorten payload if */ + /* necessary */ + if ((status&DEC_Invalid_operation)!=0) { + if (!(status&DEC_sNaN)) { /* but be true invalid */ + uprv_decNumberZero(res); /* acc not yet set */ + res->bits=DECNAN; + break; + } + uprv_decNumberZero(&dzero); /* make 0 (any non-NaN would do) */ + fhs=&dzero; /* use that */ + } + #if DECCHECK + else { /* multiply was OK */ + if (status!=0) printf("Status=%08lx after FMA multiply\n", (LI)status); + } + #endif + /* add the third operand and result -> res, and all is done */ + decAddOp(res, acc, fhs, set, 0, &status); + } while(0); /* end protected */ + + if (allocbufa!=nullptr) free(allocbufa); /* drop any storage used */ + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberFMA */ + +/* ------------------------------------------------------------------ */ +/* decNumberInvert -- invert a Number, digitwise */ +/* */ +/* This computes C = ~A */ +/* */ +/* res is C, the result. C may be A (e.g., X=~X) */ +/* rhs is A */ +/* set is the context (used for result length and error report) */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Logical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberInvert(decNumber *res, const decNumber *rhs, + decContext *set) { + const Unit *ua, *msua; /* -> operand and its msu */ + Unit *uc, *msuc; /* -> result and its msu */ + Int msudigs; /* digits in res msu */ + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + if (rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + /* operand is valid */ + ua=rhs->lsu; /* bottom-up */ + uc=res->lsu; /* .. */ + msua=ua+D2U(rhs->digits)-1; /* -> msu of rhs */ + msuc=uc+D2U(set->digits)-1; /* -> msu of result */ + msudigs=MSUDIGITS(set->digits); /* [faster than remainder] */ + for (; uc<=msuc; ua++, uc++) { /* Unit loop */ + Unit a; /* extract unit */ + Int i, j; /* work */ + if (ua>msua) a=0; + else a=*ua; + *uc=0; /* can now write back */ + /* always need to examine all bits in rhs */ + /* This loop could be unrolled and/or use BIN2BCD tables */ + for (i=0; i1) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + if (uc==msuc && i==msudigs-1) break; /* just did final digit */ + } /* each digit */ + } /* each unit */ + /* [here uc-1 is the msu of the result] */ + res->digits=decGetDigits(res->lsu, static_cast(uc - res->lsu)); + res->exponent=0; /* integer */ + res->bits=0; /* sign=0 */ + return res; /* [no status to set] */ + } /* decNumberInvert */ + +/* ------------------------------------------------------------------ */ +/* decNumberLn -- natural logarithm */ +/* */ +/* This computes C = ln(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context; note that rounding mode has no effect */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Notable cases: */ +/* A<0 -> Invalid */ +/* A=0 -> -Infinity (Exact) */ +/* A=+Infinity -> +Infinity (Exact) */ +/* A=1 exactly -> 0 (Exact) */ +/* */ +/* Mathematical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* */ +/* An Inexact result is rounded using DEC_ROUND_HALF_EVEN; it will */ +/* almost always be correctly rounded, but may be up to 1 ulp in */ +/* error in rare cases. */ +/* ------------------------------------------------------------------ */ +/* This is a wrapper for decLnOp which can handle the slightly wider */ +/* (+11) range needed by Ln, Log10, etc. (which may have to be able */ +/* to calculate at p+e+2). */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberLn(decNumber *res, const decNumber *rhs, + decContext *set) { + uInt status=0; /* accumulator */ + #if DECSUBSET + decNumber *allocrhs=nullptr; /* non-nullptr if rounded rhs allocated */ + #endif + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + /* Check restrictions; this is a math function; if not violated */ + /* then carry out the operation. */ + if (!decCheckMath(rhs, set, &status)) do { /* protect allocation */ + #if DECSUBSET + if (!set->extended) { + /* reduce operand and set lostDigits status, as needed */ + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, &status); + if (allocrhs==nullptr) break; + rhs=allocrhs; + } + /* special check in subset for rhs=0 */ + if (ISZERO(rhs)) { /* +/- zeros -> error */ + status|=DEC_Invalid_operation; + break;} + } /* extended=0 */ + #endif + decLnOp(res, rhs, set, &status); + } while(0); /* end protected */ + + #if DECSUBSET + if (allocrhs !=nullptr) free(allocrhs); /* drop any storage used */ + #endif + /* apply significant status */ + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberLn */ + +/* ------------------------------------------------------------------ */ +/* decNumberLogB - get adjusted exponent, by 754 rules */ +/* */ +/* This computes C = adjustedexponent(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context, used only for digits and status */ +/* */ +/* C must have space for 10 digits (A might have 10**9 digits and */ +/* an exponent of +999999999, or one digit and an exponent of */ +/* -1999999999). */ +/* */ +/* This returns the adjusted exponent of A after (in theory) padding */ +/* with zeros on the right to set->digits digits while keeping the */ +/* same value. The exponent is not limited by emin/emax. */ +/* */ +/* Notable cases: */ +/* A<0 -> Use |A| */ +/* A=0 -> -Infinity (Division by zero) */ +/* A=Infinite -> +Infinity (Exact) */ +/* A=1 exactly -> 0 (Exact) */ +/* NaNs are propagated as usual */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberLogB(decNumber *res, const decNumber *rhs, + decContext *set) { + uInt status=0; /* accumulator */ + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + /* NaNs as usual; Infinities return +Infinity; 0->oops */ + if (decNumberIsNaN(rhs)) decNaNs(res, rhs, nullptr, set, &status); + else if (decNumberIsInfinite(rhs)) uprv_decNumberCopyAbs(res, rhs); + else if (decNumberIsZero(rhs)) { + uprv_decNumberZero(res); /* prepare for Infinity */ + res->bits=DECNEG|DECINF; /* -Infinity */ + status|=DEC_Division_by_zero; /* as per 754 */ + } + else { /* finite non-zero */ + Int ae=rhs->exponent+rhs->digits-1; /* adjusted exponent */ + uprv_decNumberFromInt32(res, ae); /* lay it out */ + } + + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberLogB */ + +/* ------------------------------------------------------------------ */ +/* decNumberLog10 -- logarithm in base 10 */ +/* */ +/* This computes C = log10(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context; note that rounding mode has no effect */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Notable cases: */ +/* A<0 -> Invalid */ +/* A=0 -> -Infinity (Exact) */ +/* A=+Infinity -> +Infinity (Exact) */ +/* A=10**n (if n is an integer) -> n (Exact) */ +/* */ +/* Mathematical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* */ +/* An Inexact result is rounded using DEC_ROUND_HALF_EVEN; it will */ +/* almost always be correctly rounded, but may be up to 1 ulp in */ +/* error in rare cases. */ +/* ------------------------------------------------------------------ */ +/* This calculates ln(A)/ln(10) using appropriate precision. For */ +/* ln(A) this is the max(p, rhs->digits + t) + 3, where p is the */ +/* requested digits and t is the number of digits in the exponent */ +/* (maximum 6). For ln(10) it is p + 3; this is often handled by the */ +/* fastpath in decLnOp. The final division is done to the requested */ +/* precision. */ +/* ------------------------------------------------------------------ */ +#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 406 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif +U_CAPI decNumber * U_EXPORT2 uprv_decNumberLog10(decNumber *res, const decNumber *rhs, + decContext *set) { + uInt status=0, ignore=0; /* status accumulators */ + uInt needbytes; /* for space calculations */ + Int p; /* working precision */ + Int t; /* digits in exponent of A */ + + /* buffers for a and b working decimals */ + /* (adjustment calculator, same size) */ + decNumber bufa[D2N(DECBUFFER+2)]; + decNumber *allocbufa=nullptr; /* -> allocated bufa, iff allocated */ + decNumber *a=bufa; /* temporary a */ + decNumber bufb[D2N(DECBUFFER+2)]; + decNumber *allocbufb=nullptr; /* -> allocated bufb, iff allocated */ + decNumber *b=bufb; /* temporary b */ + decNumber bufw[D2N(10)]; /* working 2-10 digit number */ + decNumber *w=bufw; /* .. */ + #if DECSUBSET + decNumber *allocrhs=nullptr; /* non-nullptr if rounded rhs allocated */ + #endif + + decContext aset; /* working context */ + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + /* Check restrictions; this is a math function; if not violated */ + /* then carry out the operation. */ + if (!decCheckMath(rhs, set, &status)) do { /* protect malloc */ + #if DECSUBSET + if (!set->extended) { + /* reduce operand and set lostDigits status, as needed */ + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, &status); + if (allocrhs==nullptr) break; + rhs=allocrhs; + } + /* special check in subset for rhs=0 */ + if (ISZERO(rhs)) { /* +/- zeros -> error */ + status|=DEC_Invalid_operation; + break;} + } /* extended=0 */ + #endif + + uprv_decContextDefault(&aset, DEC_INIT_DECIMAL64); /* clean context */ + + /* handle exact powers of 10; only check if +ve finite */ + if (!(rhs->bits&(DECNEG|DECSPECIAL)) && !ISZERO(rhs)) { + Int residue=0; /* (no residue) */ + uInt copystat=0; /* clean status */ + + /* round to a single digit... */ + aset.digits=1; + decCopyFit(w, rhs, &aset, &residue, ©stat); /* copy & shorten */ + /* if exact and the digit is 1, rhs is a power of 10 */ + if (!(copystat&DEC_Inexact) && w->lsu[0]==1) { + /* the exponent, conveniently, is the power of 10; making */ + /* this the result needs a little care as it might not fit, */ + /* so first convert it into the working number, and then move */ + /* to res */ + uprv_decNumberFromInt32(w, w->exponent); + residue=0; + decCopyFit(res, w, set, &residue, &status); /* copy & round */ + decFinish(res, set, &residue, &status); /* cleanup/set flags */ + break; + } /* not a power of 10 */ + } /* not a candidate for exact */ + + /* simplify the information-content calculation to use 'total */ + /* number of digits in a, including exponent' as compared to the */ + /* requested digits, as increasing this will only rarely cost an */ + /* iteration in ln(a) anyway */ + t=6; /* it can never be >6 */ + + /* allocate space when needed... */ + p=(rhs->digits+t>set->digits?rhs->digits+t:set->digits)+3; + needbytes=sizeof(decNumber)+(D2U(p)-1)*sizeof(Unit); + if (needbytes>sizeof(bufa)) { /* need malloc space */ + allocbufa=(decNumber *)malloc(needbytes); + if (allocbufa==nullptr) { /* hopeless -- abandon */ + status|=DEC_Insufficient_storage; + break;} + a=allocbufa; /* use the allocated space */ + } + aset.digits=p; /* as calculated */ + aset.emax=DEC_MAX_MATH; /* usual bounds */ + aset.emin=-DEC_MAX_MATH; /* .. */ + aset.clamp=0; /* and no concrete format */ + decLnOp(a, rhs, &aset, &status); /* a=ln(rhs) */ + + /* skip the division if the result so far is infinite, NaN, or */ + /* zero, or there was an error; note NaN from sNaN needs copy */ + if (status&DEC_NaNs && !(status&DEC_sNaN)) break; + if (a->bits&DECSPECIAL || ISZERO(a)) { + uprv_decNumberCopy(res, a); /* [will fit] */ + break;} + + /* for ln(10) an extra 3 digits of precision are needed */ + p=set->digits+3; + needbytes=sizeof(decNumber)+(D2U(p)-1)*sizeof(Unit); + if (needbytes>sizeof(bufb)) { /* need malloc space */ + allocbufb=(decNumber *)malloc(needbytes); + if (allocbufb==nullptr) { /* hopeless -- abandon */ + status|=DEC_Insufficient_storage; + break;} + b=allocbufb; /* use the allocated space */ + } + uprv_decNumberZero(w); /* set up 10... */ + #if DECDPUN==1 + w->lsu[1]=1; w->lsu[0]=0; /* .. */ + #else + w->lsu[0]=10; /* .. */ + #endif + w->digits=2; /* .. */ + + aset.digits=p; + decLnOp(b, w, &aset, &ignore); /* b=ln(10) */ + + aset.digits=set->digits; /* for final divide */ + decDivideOp(res, a, b, &aset, DIVIDE, &status); /* into result */ + } while(0); /* [for break] */ + + if (allocbufa!=nullptr) free(allocbufa); /* drop any storage used */ + if (allocbufb!=nullptr) free(allocbufb); /* .. */ + #if DECSUBSET + if (allocrhs !=nullptr) free(allocrhs); /* .. */ + #endif + /* apply significant status */ + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberLog10 */ +#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 406 +#pragma GCC diagnostic pop +#endif + +/* ------------------------------------------------------------------ */ +/* decNumberMax -- compare two Numbers and return the maximum */ +/* */ +/* This computes C = A ? B, returning the maximum by 754 rules */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberMax(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decCompareOp(res, lhs, rhs, set, COMPMAX, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberMax */ + +/* ------------------------------------------------------------------ */ +/* decNumberMaxMag -- compare and return the maximum by magnitude */ +/* */ +/* This computes C = A ? B, returning the maximum by 754 rules */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberMaxMag(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decCompareOp(res, lhs, rhs, set, COMPMAXMAG, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberMaxMag */ + +/* ------------------------------------------------------------------ */ +/* decNumberMin -- compare two Numbers and return the minimum */ +/* */ +/* This computes C = A ? B, returning the minimum by 754 rules */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberMin(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decCompareOp(res, lhs, rhs, set, COMPMIN, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberMin */ + +/* ------------------------------------------------------------------ */ +/* decNumberMinMag -- compare and return the minimum by magnitude */ +/* */ +/* This computes C = A ? B, returning the minimum by 754 rules */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberMinMag(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decCompareOp(res, lhs, rhs, set, COMPMINMAG, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberMinMag */ + +/* ------------------------------------------------------------------ */ +/* decNumberMinus -- prefix minus operator */ +/* */ +/* This computes C = 0 - A */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context */ +/* */ +/* See also decNumberCopyNegate for a quiet bitwise version of this. */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +/* Simply use AddOp for the subtract, which will do the necessary. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberMinus(decNumber *res, const decNumber *rhs, + decContext *set) { + decNumber dzero; + uInt status=0; /* accumulator */ + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + uprv_decNumberZero(&dzero); /* make 0 */ + dzero.exponent=rhs->exponent; /* [no coefficient expansion] */ + decAddOp(res, &dzero, rhs, set, DECNEG, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberMinus */ + +/* ------------------------------------------------------------------ */ +/* decNumberNextMinus -- next towards -Infinity */ +/* */ +/* This computes C = A - infinitesimal, rounded towards -Infinity */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context */ +/* */ +/* This is a generalization of 754 NextDown. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberNextMinus(decNumber *res, const decNumber *rhs, + decContext *set) { + decNumber dtiny; /* constant */ + decContext workset=*set; /* work */ + uInt status=0; /* accumulator */ + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + /* +Infinity is the special case */ + if ((rhs->bits&(DECINF|DECNEG))==DECINF) { + decSetMaxValue(res, set); /* is +ve */ + /* there is no status to set */ + return res; + } + uprv_decNumberZero(&dtiny); /* start with 0 */ + dtiny.lsu[0]=1; /* make number that is .. */ + dtiny.exponent=DEC_MIN_EMIN-1; /* .. smaller than tiniest */ + workset.round=DEC_ROUND_FLOOR; + decAddOp(res, rhs, &dtiny, &workset, DECNEG, &status); + status&=DEC_Invalid_operation|DEC_sNaN; /* only sNaN Invalid please */ + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberNextMinus */ + +/* ------------------------------------------------------------------ */ +/* decNumberNextPlus -- next towards +Infinity */ +/* */ +/* This computes C = A + infinitesimal, rounded towards +Infinity */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context */ +/* */ +/* This is a generalization of 754 NextUp. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberNextPlus(decNumber *res, const decNumber *rhs, + decContext *set) { + decNumber dtiny; /* constant */ + decContext workset=*set; /* work */ + uInt status=0; /* accumulator */ + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + /* -Infinity is the special case */ + if ((rhs->bits&(DECINF|DECNEG))==(DECINF|DECNEG)) { + decSetMaxValue(res, set); + res->bits=DECNEG; /* negative */ + /* there is no status to set */ + return res; + } + uprv_decNumberZero(&dtiny); /* start with 0 */ + dtiny.lsu[0]=1; /* make number that is .. */ + dtiny.exponent=DEC_MIN_EMIN-1; /* .. smaller than tiniest */ + workset.round=DEC_ROUND_CEILING; + decAddOp(res, rhs, &dtiny, &workset, 0, &status); + status&=DEC_Invalid_operation|DEC_sNaN; /* only sNaN Invalid please */ + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberNextPlus */ + +/* ------------------------------------------------------------------ */ +/* decNumberNextToward -- next towards rhs */ +/* */ +/* This computes C = A +/- infinitesimal, rounded towards */ +/* +/-Infinity in the direction of B, as per 754-1985 nextafter */ +/* modified during revision but dropped from 754-2008. */ +/* */ +/* res is C, the result. C may be A or B. */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* This is a generalization of 754-1985 NextAfter. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberNextToward(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + decNumber dtiny; /* constant */ + decContext workset=*set; /* work */ + Int result; /* .. */ + uInt status=0; /* accumulator */ + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) { + decNaNs(res, lhs, rhs, set, &status); + } + else { /* Is numeric, so no chance of sNaN Invalid, etc. */ + result=decCompare(lhs, rhs, 0); /* sign matters */ + if (result==BADINT) status|=DEC_Insufficient_storage; /* rare */ + else { /* valid compare */ + if (result==0) uprv_decNumberCopySign(res, lhs, rhs); /* easy */ + else { /* differ: need NextPlus or NextMinus */ + uByte sub; /* add or subtract */ + if (result<0) { /* lhsbits&(DECINF|DECNEG))==(DECINF|DECNEG)) { + decSetMaxValue(res, set); + res->bits=DECNEG; /* negative */ + return res; /* there is no status to set */ + } + workset.round=DEC_ROUND_CEILING; + sub=0; /* add, please */ + } /* plus */ + else { /* lhs>rhs, do nextminus */ + /* +Infinity is the special case */ + if ((lhs->bits&(DECINF|DECNEG))==DECINF) { + decSetMaxValue(res, set); + return res; /* there is no status to set */ + } + workset.round=DEC_ROUND_FLOOR; + sub=DECNEG; /* subtract, please */ + } /* minus */ + uprv_decNumberZero(&dtiny); /* start with 0 */ + dtiny.lsu[0]=1; /* make number that is .. */ + dtiny.exponent=DEC_MIN_EMIN-1; /* .. smaller than tiniest */ + decAddOp(res, lhs, &dtiny, &workset, sub, &status); /* + or - */ + /* turn off exceptions if the result is a normal number */ + /* (including Nmin), otherwise let all status through */ + if (uprv_decNumberIsNormal(res, set)) status=0; + } /* unequal */ + } /* compare OK */ + } /* numeric */ + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberNextToward */ + +/* ------------------------------------------------------------------ */ +/* decNumberOr -- OR two Numbers, digitwise */ +/* */ +/* This computes C = A | B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X|X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context (used for result length and error report) */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Logical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberOr(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + const Unit *ua, *ub; /* -> operands */ + const Unit *msua, *msub; /* -> operand msus */ + Unit *uc, *msuc; /* -> result and its msu */ + Int msudigs; /* digits in res msu */ + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + if (lhs->exponent!=0 || decNumberIsSpecial(lhs) || decNumberIsNegative(lhs) + || rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + /* operands are valid */ + ua=lhs->lsu; /* bottom-up */ + ub=rhs->lsu; /* .. */ + uc=res->lsu; /* .. */ + msua=ua+D2U(lhs->digits)-1; /* -> msu of lhs */ + msub=ub+D2U(rhs->digits)-1; /* -> msu of rhs */ + msuc=uc+D2U(set->digits)-1; /* -> msu of result */ + msudigs=MSUDIGITS(set->digits); /* [faster than remainder] */ + for (; uc<=msuc; ua++, ub++, uc++) { /* Unit loop */ + Unit a, b; /* extract units */ + if (ua>msua) a=0; + else a=*ua; + if (ub>msub) b=0; + else b=*ub; + *uc=0; /* can now write back */ + if (a|b) { /* maybe 1 bits to examine */ + Int i, j; + /* This loop could be unrolled and/or use BIN2BCD tables */ + for (i=0; i1) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + if (uc==msuc && i==msudigs-1) break; /* just did final digit */ + } /* each digit */ + } /* non-zero */ + } /* each unit */ + /* [here uc-1 is the msu of the result] */ + res->digits=decGetDigits(res->lsu, static_cast(uc-res->lsu)); + res->exponent=0; /* integer */ + res->bits=0; /* sign=0 */ + return res; /* [no status to set] */ + } /* decNumberOr */ + +/* ------------------------------------------------------------------ */ +/* decNumberPlus -- prefix plus operator */ +/* */ +/* This computes C = 0 + A */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context */ +/* */ +/* See also decNumberCopy for a quiet bitwise version of this. */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +/* This simply uses AddOp; Add will take fast path after preparing A. */ +/* Performance is a concern here, as this routine is often used to */ +/* check operands and apply rounding and overflow/underflow testing. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberPlus(decNumber *res, const decNumber *rhs, + decContext *set) { + decNumber dzero; + uInt status=0; /* accumulator */ + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + uprv_decNumberZero(&dzero); /* make 0 */ + dzero.exponent=rhs->exponent; /* [no coefficient expansion] */ + decAddOp(res, &dzero, rhs, set, 0, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberPlus */ + +/* ------------------------------------------------------------------ */ +/* decNumberMultiply -- multiply two Numbers */ +/* */ +/* This computes C = A x B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X+X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberMultiply(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decMultiplyOp(res, lhs, rhs, set, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberMultiply */ + +/* ------------------------------------------------------------------ */ +/* decNumberPower -- raise a number to a power */ +/* */ +/* This computes C = A ** B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X**X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Mathematical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* */ +/* However, if 1999999997<=B<=999999999 and B is an integer then the */ +/* restrictions on A and the context are relaxed to the usual bounds, */ +/* for compatibility with the earlier (integer power only) version */ +/* of this function. */ +/* */ +/* When B is an integer, the result may be exact, even if rounded. */ +/* */ +/* The final result is rounded according to the context; it will */ +/* almost always be correctly rounded, but may be up to 1 ulp in */ +/* error in rare cases. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberPower(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + #if DECSUBSET + decNumber *alloclhs=nullptr; /* non-nullptr if rounded lhs allocated */ + decNumber *allocrhs=nullptr; /* .., rhs */ + #endif + decNumber *allocdac=nullptr; /* -> allocated acc buffer, iff used */ + decNumber *allocinv=nullptr; /* -> allocated 1/x buffer, iff used */ + Int reqdigits=set->digits; /* requested DIGITS */ + Int n; /* rhs in binary */ + Flag rhsint=0; /* 1 if rhs is an integer */ + Flag useint=0; /* 1 if can use integer calculation */ + Flag isoddint=0; /* 1 if rhs is an integer and odd */ + Int i; /* work */ + #if DECSUBSET + Int dropped; /* .. */ + #endif + uInt needbytes; /* buffer size needed */ + Flag seenbit; /* seen a bit while powering */ + Int residue=0; /* rounding residue */ + uInt status=0; /* accumulators */ + uByte bits=0; /* result sign if errors */ + decContext aset; /* working context */ + decNumber dnOne; /* work value 1... */ + /* local accumulator buffer [a decNumber, with digits+elength+1 digits] */ + decNumber dacbuff[D2N(DECBUFFER+9)]; + decNumber *dac=dacbuff; /* -> result accumulator */ + /* same again for possible 1/lhs calculation */ + decNumber invbuff[D2N(DECBUFFER+9)]; + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + do { /* protect allocated storage */ + #if DECSUBSET + if (!set->extended) { /* reduce operands and set status, as needed */ + if (lhs->digits>reqdigits) { + alloclhs=decRoundOperand(lhs, set, &status); + if (alloclhs==nullptr) break; + lhs=alloclhs; + } + if (rhs->digits>reqdigits) { + allocrhs=decRoundOperand(rhs, set, &status); + if (allocrhs==nullptr) break; + rhs=allocrhs; + } + } + #endif + /* [following code does not require input rounding] */ + + /* handle NaNs and rhs Infinity (lhs infinity is harder) */ + if (SPECIALARGS) { + if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) { /* NaNs */ + decNaNs(res, lhs, rhs, set, &status); + break;} + if (decNumberIsInfinite(rhs)) { /* rhs Infinity */ + Flag rhsneg=rhs->bits&DECNEG; /* save rhs sign */ + if (decNumberIsNegative(lhs) /* lhs<0 */ + && !decNumberIsZero(lhs)) /* .. */ + status|=DEC_Invalid_operation; + else { /* lhs >=0 */ + uprv_decNumberZero(&dnOne); /* set up 1 */ + dnOne.lsu[0]=1; + uprv_decNumberCompare(dac, lhs, &dnOne, set); /* lhs ? 1 */ + uprv_decNumberZero(res); /* prepare for 0/1/Infinity */ + if (decNumberIsNegative(dac)) { /* lhs<1 */ + if (rhsneg) res->bits|=DECINF; /* +Infinity [else is +0] */ + } + else if (dac->lsu[0]==0) { /* lhs=1 */ + /* 1**Infinity is inexact, so return fully-padded 1.0000 */ + Int shift=set->digits-1; + *res->lsu=1; /* was 0, make int 1 */ + res->digits=decShiftToMost(res->lsu, 1, shift); + res->exponent=-shift; /* make 1.0000... */ + status|=DEC_Inexact|DEC_Rounded; /* deemed inexact */ + } + else { /* lhs>1 */ + if (!rhsneg) res->bits|=DECINF; /* +Infinity [else is +0] */ + } + } /* lhs>=0 */ + break;} + /* [lhs infinity drops through] */ + } /* specials */ + + /* Original rhs may be an integer that fits and is in range */ + n=decGetInt(rhs); + if (n!=BADINT) { /* it is an integer */ + rhsint=1; /* record the fact for 1**n */ + isoddint=(Flag)n&1; /* [works even if big] */ + if (n!=BIGEVEN && n!=BIGODD) /* can use integer path? */ + useint=1; /* looks good */ + } + + if (decNumberIsNegative(lhs) /* -x .. */ + && isoddint) bits=DECNEG; /* .. to an odd power */ + + /* handle LHS infinity */ + if (decNumberIsInfinite(lhs)) { /* [NaNs already handled] */ + uByte rbits=rhs->bits; /* save */ + uprv_decNumberZero(res); /* prepare */ + if (n==0) *res->lsu=1; /* [-]Inf**0 => 1 */ + else { + /* -Inf**nonint -> error */ + if (!rhsint && decNumberIsNegative(lhs)) { + status|=DEC_Invalid_operation; /* -Inf**nonint is error */ + break;} + if (!(rbits & DECNEG)) bits|=DECINF; /* was not a **-n */ + /* [otherwise will be 0 or -0] */ + res->bits=bits; + } + break;} + + /* similarly handle LHS zero */ + if (decNumberIsZero(lhs)) { + if (n==0) { /* 0**0 => Error */ + #if DECSUBSET + if (!set->extended) { /* [unless subset] */ + uprv_decNumberZero(res); + *res->lsu=1; /* return 1 */ + break;} + #endif + status|=DEC_Invalid_operation; + } + else { /* 0**x */ + uByte rbits=rhs->bits; /* save */ + if (rbits & DECNEG) { /* was a 0**(-n) */ + #if DECSUBSET + if (!set->extended) { /* [bad if subset] */ + status|=DEC_Invalid_operation; + break;} + #endif + bits|=DECINF; + } + uprv_decNumberZero(res); /* prepare */ + /* [otherwise will be 0 or -0] */ + res->bits=bits; + } + break;} + + /* here both lhs and rhs are finite; rhs==0 is handled in the */ + /* integer path. Next handle the non-integer cases */ + if (!useint) { /* non-integral rhs */ + /* any -ve lhs is bad, as is either operand or context out of */ + /* bounds */ + if (decNumberIsNegative(lhs)) { + status|=DEC_Invalid_operation; + break;} + if (decCheckMath(lhs, set, &status) + || decCheckMath(rhs, set, &status)) break; /* variable status */ + + uprv_decContextDefault(&aset, DEC_INIT_DECIMAL64); /* clean context */ + aset.emax=DEC_MAX_MATH; /* usual bounds */ + aset.emin=-DEC_MAX_MATH; /* .. */ + aset.clamp=0; /* and no concrete format */ + + /* calculate the result using exp(ln(lhs)*rhs), which can */ + /* all be done into the accumulator, dac. The precision needed */ + /* is enough to contain the full information in the lhs (which */ + /* is the total digits, including exponent), or the requested */ + /* precision, if larger, + 4; 6 is used for the exponent */ + /* maximum length, and this is also used when it is shorter */ + /* than the requested digits as it greatly reduces the >0.5 ulp */ + /* cases at little cost (because Ln doubles digits each */ + /* iteration so a few extra digits rarely causes an extra */ + /* iteration) */ + aset.digits=MAXI(lhs->digits, set->digits)+6+4; + } /* non-integer rhs */ + + else { /* rhs is in-range integer */ + if (n==0) { /* x**0 = 1 */ + /* (0**0 was handled above) */ + uprv_decNumberZero(res); /* result=1 */ + *res->lsu=1; /* .. */ + break;} + /* rhs is a non-zero integer */ + if (n<0) n=-n; /* use abs(n) */ + + aset=*set; /* clone the context */ + aset.round=DEC_ROUND_HALF_EVEN; /* internally use balanced */ + /* calculate the working DIGITS */ + aset.digits=reqdigits+(rhs->digits+rhs->exponent)+2; + #if DECSUBSET + if (!set->extended) aset.digits--; /* use classic precision */ + #endif + /* it's an error if this is more than can be handled */ + if (aset.digits>DECNUMMAXP) {status|=DEC_Invalid_operation; break;} + } /* integer path */ + + /* aset.digits is the count of digits for the accumulator needed */ + /* if accumulator is too long for local storage, then allocate */ + needbytes=sizeof(decNumber)+(D2U(aset.digits)-1)*sizeof(Unit); + /* [needbytes also used below if 1/lhs needed] */ + if (needbytes>sizeof(dacbuff)) { + allocdac=(decNumber *)malloc(needbytes); + if (allocdac==nullptr) { /* hopeless -- abandon */ + status|=DEC_Insufficient_storage; + break;} + dac=allocdac; /* use the allocated space */ + } + /* here, aset is set up and accumulator is ready for use */ + + if (!useint) { /* non-integral rhs */ + /* x ** y; special-case x=1 here as it will otherwise always */ + /* reduce to integer 1; decLnOp has a fastpath which detects */ + /* the case of x=1 */ + decLnOp(dac, lhs, &aset, &status); /* dac=ln(lhs) */ + /* [no error possible, as lhs 0 already handled] */ + if (ISZERO(dac)) { /* x==1, 1.0, etc. */ + /* need to return fully-padded 1.0000 etc., but rhsint->1 */ + *dac->lsu=1; /* was 0, make int 1 */ + if (!rhsint) { /* add padding */ + Int shift=set->digits-1; + dac->digits=decShiftToMost(dac->lsu, 1, shift); + dac->exponent=-shift; /* make 1.0000... */ + status|=DEC_Inexact|DEC_Rounded; /* deemed inexact */ + } + } + else { + decMultiplyOp(dac, dac, rhs, &aset, &status); /* dac=dac*rhs */ + decExpOp(dac, dac, &aset, &status); /* dac=exp(dac) */ + } + /* and drop through for final rounding */ + } /* non-integer rhs */ + + else { /* carry on with integer */ + uprv_decNumberZero(dac); /* acc=1 */ + *dac->lsu=1; /* .. */ + + /* if a negative power the constant 1 is needed, and if not subset */ + /* invert the lhs now rather than inverting the result later */ + if (decNumberIsNegative(rhs)) { /* was a **-n [hence digits>0] */ + decNumber *inv=invbuff; /* assume use fixed buffer */ + uprv_decNumberCopy(&dnOne, dac); /* dnOne=1; [needed now or later] */ + #if DECSUBSET + if (set->extended) { /* need to calculate 1/lhs */ + #endif + /* divide lhs into 1, putting result in dac [dac=1/dac] */ + decDivideOp(dac, &dnOne, lhs, &aset, DIVIDE, &status); + /* now locate or allocate space for the inverted lhs */ + if (needbytes>sizeof(invbuff)) { + allocinv=(decNumber *)malloc(needbytes); + if (allocinv==nullptr) { /* hopeless -- abandon */ + status|=DEC_Insufficient_storage; + break;} + inv=allocinv; /* use the allocated space */ + } + /* [inv now points to big-enough buffer or allocated storage] */ + uprv_decNumberCopy(inv, dac); /* copy the 1/lhs */ + uprv_decNumberCopy(dac, &dnOne); /* restore acc=1 */ + lhs=inv; /* .. and go forward with new lhs */ + #if DECSUBSET + } + #endif + } + + /* Raise-to-the-power loop... */ + seenbit=0; /* set once a 1-bit is encountered */ + for (i=1;;i++){ /* for each bit [top bit ignored] */ + /* abandon if had overflow or terminal underflow */ + if (status & (DEC_Overflow|DEC_Underflow)) { /* interesting? */ + if (status&DEC_Overflow || ISZERO(dac)) break; + } + /* [the following two lines revealed an optimizer bug in a C++ */ + /* compiler, with symptom: 5**3 -> 25, when n=n+n was used] */ + n=n<<1; /* move next bit to testable position */ + if (n<0) { /* top bit is set */ + seenbit=1; /* OK, significant bit seen */ + decMultiplyOp(dac, dac, lhs, &aset, &status); /* dac=dac*x */ + } + if (i==31) break; /* that was the last bit */ + if (!seenbit) continue; /* no need to square 1 */ + decMultiplyOp(dac, dac, dac, &aset, &status); /* dac=dac*dac [square] */ + } /*i*/ /* 32 bits */ + + /* complete internal overflow or underflow processing */ + if (status & (DEC_Overflow|DEC_Underflow)) { + #if DECSUBSET + /* If subset, and power was negative, reverse the kind of -erflow */ + /* [1/x not yet done] */ + if (!set->extended && decNumberIsNegative(rhs)) { + if (status & DEC_Overflow) + status^=DEC_Overflow | DEC_Underflow | DEC_Subnormal; + else { /* trickier -- Underflow may or may not be set */ + status&=~(DEC_Underflow | DEC_Subnormal); /* [one or both] */ + status|=DEC_Overflow; + } + } + #endif + dac->bits=(dac->bits & ~DECNEG) | bits; /* force correct sign */ + /* round subnormals [to set.digits rather than aset.digits] */ + /* or set overflow result similarly as required */ + decFinalize(dac, set, &residue, &status); + uprv_decNumberCopy(res, dac); /* copy to result (is now OK length) */ + break; + } + + #if DECSUBSET + if (!set->extended && /* subset math */ + decNumberIsNegative(rhs)) { /* was a **-n [hence digits>0] */ + /* so divide result into 1 [dac=1/dac] */ + decDivideOp(dac, &dnOne, dac, &aset, DIVIDE, &status); + } + #endif + } /* rhs integer path */ + + /* reduce result to the requested length and copy to result */ + decCopyFit(res, dac, set, &residue, &status); + decFinish(res, set, &residue, &status); /* final cleanup */ + #if DECSUBSET + if (!set->extended) decTrim(res, set, 0, 1, &dropped); /* trailing zeros */ + #endif + } while(0); /* end protected */ + + if (allocdac!=nullptr) free(allocdac); /* drop any storage used */ + if (allocinv!=nullptr) free(allocinv); /* .. */ + #if DECSUBSET + if (alloclhs!=nullptr) free(alloclhs); /* .. */ + if (allocrhs!=nullptr) free(allocrhs); /* .. */ + #endif + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberPower */ + +/* ------------------------------------------------------------------ */ +/* decNumberQuantize -- force exponent to requested value */ +/* */ +/* This computes C = op(A, B), where op adjusts the coefficient */ +/* of C (by rounding or shifting) such that the exponent (-scale) */ +/* of C has exponent of B. The numerical value of C will equal A, */ +/* except for the effects of any rounding that occurred. */ +/* */ +/* res is C, the result. C may be A or B */ +/* lhs is A, the number to adjust */ +/* rhs is B, the number with exponent to match */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Unless there is an error or the result is infinite, the exponent */ +/* after the operation is guaranteed to be equal to that of B. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberQuantize(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decQuantizeOp(res, lhs, rhs, set, 1, &status); + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberQuantize */ + +/* ------------------------------------------------------------------ */ +/* decNumberReduce -- remove trailing zeros */ +/* */ +/* This computes C = 0 + A, and normalizes the result */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +/* Previously known as Normalize */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberNormalize(decNumber *res, const decNumber *rhs, + decContext *set) { + return uprv_decNumberReduce(res, rhs, set); + } /* decNumberNormalize */ + +U_CAPI decNumber * U_EXPORT2 uprv_decNumberReduce(decNumber *res, const decNumber *rhs, + decContext *set) { + #if DECSUBSET + decNumber *allocrhs=nullptr; /* non-nullptr if rounded rhs allocated */ + #endif + uInt status=0; /* as usual */ + Int residue=0; /* as usual */ + Int dropped; /* work */ + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + do { /* protect allocated storage */ + #if DECSUBSET + if (!set->extended) { + /* reduce operand and set lostDigits status, as needed */ + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, &status); + if (allocrhs==nullptr) break; + rhs=allocrhs; + } + } + #endif + /* [following code does not require input rounding] */ + + /* Infinities copy through; NaNs need usual treatment */ + if (decNumberIsNaN(rhs)) { + decNaNs(res, rhs, nullptr, set, &status); + break; + } + + /* reduce result to the requested length and copy to result */ + decCopyFit(res, rhs, set, &residue, &status); /* copy & round */ + decFinish(res, set, &residue, &status); /* cleanup/set flags */ + decTrim(res, set, 1, 0, &dropped); /* normalize in place */ + /* [may clamp] */ + } while(0); /* end protected */ + + #if DECSUBSET + if (allocrhs !=nullptr) free(allocrhs); /* .. */ + #endif + if (status!=0) decStatus(res, status, set);/* then report status */ + return res; + } /* decNumberReduce */ + +/* ------------------------------------------------------------------ */ +/* decNumberRescale -- force exponent to requested value */ +/* */ +/* This computes C = op(A, B), where op adjusts the coefficient */ +/* of C (by rounding or shifting) such that the exponent (-scale) */ +/* of C has the value B. The numerical value of C will equal A, */ +/* except for the effects of any rounding that occurred. */ +/* */ +/* res is C, the result. C may be A or B */ +/* lhs is A, the number to adjust */ +/* rhs is B, the requested exponent */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Unless there is an error or the result is infinite, the exponent */ +/* after the operation is guaranteed to be equal to B. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberRescale(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decQuantizeOp(res, lhs, rhs, set, 0, &status); + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberRescale */ + +/* ------------------------------------------------------------------ */ +/* decNumberRemainder -- divide and return remainder */ +/* */ +/* This computes C = A % B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X%X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberRemainder(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decDivideOp(res, lhs, rhs, set, REMAINDER, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberRemainder */ + +/* ------------------------------------------------------------------ */ +/* decNumberRemainderNear -- divide and return remainder from nearest */ +/* */ +/* This computes C = A % B, where % is the IEEE remainder operator */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X%X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberRemainderNear(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + decDivideOp(res, lhs, rhs, set, REMNEAR, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberRemainderNear */ + +/* ------------------------------------------------------------------ */ +/* decNumberRotate -- rotate the coefficient of a Number left/right */ +/* */ +/* This computes C = A rot B (in base ten and rotating set->digits */ +/* digits). */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=XrotX) */ +/* lhs is A */ +/* rhs is B, the number of digits to rotate (-ve to right) */ +/* set is the context */ +/* */ +/* The digits of the coefficient of A are rotated to the left (if B */ +/* is positive) or to the right (if B is negative) without adjusting */ +/* the exponent or the sign of A. If lhs->digits is less than */ +/* set->digits the coefficient is padded with zeros on the left */ +/* before the rotate. Any leading zeros in the result are removed */ +/* as usual. */ +/* */ +/* B must be an integer (q=0) and in the range -set->digits through */ +/* +set->digits. */ +/* C must have space for set->digits digits. */ +/* NaNs are propagated as usual. Infinities are unaffected (but */ +/* B must be valid). No status is set unless B is invalid or an */ +/* operand is an sNaN. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberRotate(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + Int rotate; /* rhs as an Int */ + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + /* NaNs propagate as normal */ + if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) + decNaNs(res, lhs, rhs, set, &status); + /* rhs must be an integer */ + else if (decNumberIsInfinite(rhs) || rhs->exponent!=0) + status=DEC_Invalid_operation; + else { /* both numeric, rhs is an integer */ + rotate=decGetInt(rhs); /* [cannot fail] */ + if (rotate==BADINT /* something bad .. */ + || rotate==BIGODD || rotate==BIGEVEN /* .. very big .. */ + || abs(rotate)>set->digits) /* .. or out of range */ + status=DEC_Invalid_operation; + else { /* rhs is OK */ + uprv_decNumberCopy(res, lhs); + /* convert -ve rotate to equivalent positive rotation */ + if (rotate<0) rotate=set->digits+rotate; + if (rotate!=0 && rotate!=set->digits /* zero or full rotation */ + && !decNumberIsInfinite(res)) { /* lhs was infinite */ + /* left-rotate to do; 0 < rotate < set->digits */ + uInt units, shift; /* work */ + uInt msudigits; /* digits in result msu */ + Unit *msu=res->lsu+D2U(res->digits)-1; /* current msu */ + Unit *msumax=res->lsu+D2U(set->digits)-1; /* rotation msu */ + for (msu++; msu<=msumax; msu++) *msu=0; /* ensure high units=0 */ + res->digits=set->digits; /* now full-length */ + msudigits=MSUDIGITS(res->digits); /* actual digits in msu */ + + /* rotation here is done in-place, in three steps */ + /* 1. shift all to least up to one unit to unit-align final */ + /* lsd [any digits shifted out are rotated to the left, */ + /* abutted to the original msd (which may require split)] */ + /* */ + /* [if there are no whole units left to rotate, the */ + /* rotation is now complete] */ + /* */ + /* 2. shift to least, from below the split point only, so that */ + /* the final msd is in the right place in its Unit [any */ + /* digits shifted out will fit exactly in the current msu, */ + /* left aligned, no split required] */ + /* */ + /* 3. rotate all the units by reversing left part, right */ + /* part, and then whole */ + /* */ + /* example: rotate right 8 digits (2 units + 2), DECDPUN=3. */ + /* */ + /* start: 00a bcd efg hij klm npq */ + /* */ + /* 1a 000 0ab cde fgh|ijk lmn [pq saved] */ + /* 1b 00p qab cde fgh|ijk lmn */ + /* */ + /* 2a 00p qab cde fgh|00i jkl [mn saved] */ + /* 2b mnp qab cde fgh|00i jkl */ + /* */ + /* 3a fgh cde qab mnp|00i jkl */ + /* 3b fgh cde qab mnp|jkl 00i */ + /* 3c 00i jkl mnp qab cde fgh */ + + /* Step 1: amount to shift is the partial right-rotate count */ + rotate=set->digits-rotate; /* make it right-rotate */ + units=rotate/DECDPUN; /* whole units to rotate */ + shift=rotate%DECDPUN; /* left-over digits count */ + if (shift>0) { /* not an exact number of units */ + uInt save=res->lsu[0]%powers[shift]; /* save low digit(s) */ + decShiftToLeast(res->lsu, D2U(res->digits), shift); + if (shift>msudigits) { /* msumax-1 needs >0 digits */ + uInt rem=save%powers[shift-msudigits];/* split save */ + *msumax=(Unit)(save/powers[shift-msudigits]); /* and insert */ + *(msumax-1)=*(msumax-1) + +(Unit)(rem*powers[DECDPUN-(shift-msudigits)]); /* .. */ + } + else { /* all fits in msumax */ + *msumax=*msumax+(Unit)(save*powers[msudigits-shift]); /* [maybe *1] */ + } + } /* digits shift needed */ + + /* If whole units to rotate... */ + if (units>0) { /* some to do */ + /* Step 2: the units to touch are the whole ones in rotate, */ + /* if any, and the shift is DECDPUN-msudigits (which may be */ + /* 0, again) */ + shift=DECDPUN-msudigits; + if (shift>0) { /* not an exact number of units */ + uInt save=res->lsu[0]%powers[shift]; /* save low digit(s) */ + decShiftToLeast(res->lsu, units, shift); + *msumax=*msumax+(Unit)(save*powers[msudigits]); + } /* partial shift needed */ + + /* Step 3: rotate the units array using triple reverse */ + /* (reversing is easy and fast) */ + decReverse(res->lsu+units, msumax); /* left part */ + decReverse(res->lsu, res->lsu+units-1); /* right part */ + decReverse(res->lsu, msumax); /* whole */ + } /* whole units to rotate */ + /* the rotation may have left an undetermined number of zeros */ + /* on the left, so true length needs to be calculated */ + res->digits=decGetDigits(res->lsu, static_cast(msumax-res->lsu+1)); + } /* rotate needed */ + } /* rhs OK */ + } /* numerics */ + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberRotate */ + +/* ------------------------------------------------------------------ */ +/* decNumberSameQuantum -- test for equal exponents */ +/* */ +/* res is the result number, which will contain either 0 or 1 */ +/* lhs is a number to test */ +/* rhs is the second (usually a pattern) */ +/* */ +/* No errors are possible and no context is needed. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberSameQuantum(decNumber *res, const decNumber *lhs, + const decNumber *rhs) { + Unit ret=0; /* return value */ + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, DECUNCONT)) return res; + #endif + + if (SPECIALARGS) { + if (decNumberIsNaN(lhs) && decNumberIsNaN(rhs)) ret=1; + else if (decNumberIsInfinite(lhs) && decNumberIsInfinite(rhs)) ret=1; + /* [anything else with a special gives 0] */ + } + else if (lhs->exponent==rhs->exponent) ret=1; + + uprv_decNumberZero(res); /* OK to overwrite an operand now */ + *res->lsu=ret; + return res; + } /* decNumberSameQuantum */ + +/* ------------------------------------------------------------------ */ +/* decNumberScaleB -- multiply by a power of 10 */ +/* */ +/* This computes C = A x 10**B where B is an integer (q=0) with */ +/* maximum magnitude 2*(emax+digits) */ +/* */ +/* res is C, the result. C may be A or B */ +/* lhs is A, the number to adjust */ +/* rhs is B, the requested power of ten to use */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* The result may underflow or overflow. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberScaleB(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + Int reqexp; /* requested exponent change [B] */ + uInt status=0; /* accumulator */ + Int residue; /* work */ + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + /* Handle special values except lhs infinite */ + if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) + decNaNs(res, lhs, rhs, set, &status); + /* rhs must be an integer */ + else if (decNumberIsInfinite(rhs) || rhs->exponent!=0) + status=DEC_Invalid_operation; + else { + /* lhs is a number; rhs is a finite with q==0 */ + reqexp=decGetInt(rhs); /* [cannot fail] */ + if (reqexp==BADINT /* something bad .. */ + || reqexp==BIGODD || reqexp==BIGEVEN /* .. very big .. */ + || abs(reqexp)>(2*(set->digits+set->emax))) /* .. or out of range */ + status=DEC_Invalid_operation; + else { /* rhs is OK */ + uprv_decNumberCopy(res, lhs); /* all done if infinite lhs */ + if (!decNumberIsInfinite(res)) { /* prepare to scale */ + res->exponent+=reqexp; /* adjust the exponent */ + residue=0; + decFinalize(res, set, &residue, &status); /* .. and check */ + } /* finite LHS */ + } /* rhs OK */ + } /* rhs finite */ + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberScaleB */ + +/* ------------------------------------------------------------------ */ +/* decNumberShift -- shift the coefficient of a Number left or right */ +/* */ +/* This computes C = A << B or C = A >> -B (in base ten). */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X<digits through */ +/* +set->digits. */ +/* C must have space for set->digits digits. */ +/* NaNs are propagated as usual. Infinities are unaffected (but */ +/* B must be valid). No status is set unless B is invalid or an */ +/* operand is an sNaN. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberShift(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + Int shift; /* rhs as an Int */ + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + /* NaNs propagate as normal */ + if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) + decNaNs(res, lhs, rhs, set, &status); + /* rhs must be an integer */ + else if (decNumberIsInfinite(rhs) || rhs->exponent!=0) + status=DEC_Invalid_operation; + else { /* both numeric, rhs is an integer */ + shift=decGetInt(rhs); /* [cannot fail] */ + if (shift==BADINT /* something bad .. */ + || shift==BIGODD || shift==BIGEVEN /* .. very big .. */ + || abs(shift)>set->digits) /* .. or out of range */ + status=DEC_Invalid_operation; + else { /* rhs is OK */ + uprv_decNumberCopy(res, lhs); + if (shift!=0 && !decNumberIsInfinite(res)) { /* something to do */ + if (shift>0) { /* to left */ + if (shift==set->digits) { /* removing all */ + *res->lsu=0; /* so place 0 */ + res->digits=1; /* .. */ + } + else { /* */ + /* first remove leading digits if necessary */ + if (res->digits+shift>set->digits) { + decDecap(res, res->digits+shift-set->digits); + /* that updated res->digits; may have gone to 1 (for a */ + /* single digit or for zero */ + } + if (res->digits>1 || *res->lsu) /* if non-zero.. */ + res->digits=decShiftToMost(res->lsu, res->digits, shift); + } /* partial left */ + } /* left */ + else { /* to right */ + if (-shift>=res->digits) { /* discarding all */ + *res->lsu=0; /* so place 0 */ + res->digits=1; /* .. */ + } + else { + decShiftToLeast(res->lsu, D2U(res->digits), -shift); + res->digits-=(-shift); + } + } /* to right */ + } /* non-0 non-Inf shift */ + } /* rhs OK */ + } /* numerics */ + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberShift */ + +/* ------------------------------------------------------------------ */ +/* decNumberSquareRoot -- square root operator */ +/* */ +/* This computes C = squareroot(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context; note that rounding mode has no effect */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +/* This uses the following varying-precision algorithm in: */ +/* */ +/* Properly Rounded Variable Precision Square Root, T. E. Hull and */ +/* A. Abrham, ACM Transactions on Mathematical Software, Vol 11 #3, */ +/* pp229-237, ACM, September 1985. */ +/* */ +/* The square-root is calculated using Newton's method, after which */ +/* a check is made to ensure the result is correctly rounded. */ +/* */ +/* % [Reformatted original Numerical Turing source code follows.] */ +/* function sqrt(x : real) : real */ +/* % sqrt(x) returns the properly rounded approximation to the square */ +/* % root of x, in the precision of the calling environment, or it */ +/* % fails if x < 0. */ +/* % t e hull and a abrham, august, 1984 */ +/* if x <= 0 then */ +/* if x < 0 then */ +/* assert false */ +/* else */ +/* result 0 */ +/* end if */ +/* end if */ +/* var f := setexp(x, 0) % fraction part of x [0.1 <= x < 1] */ +/* var e := getexp(x) % exponent part of x */ +/* var approx : real */ +/* if e mod 2 = 0 then */ +/* approx := .259 + .819 * f % approx to root of f */ +/* else */ +/* f := f/l0 % adjustments */ +/* e := e + 1 % for odd */ +/* approx := .0819 + 2.59 * f % exponent */ +/* end if */ +/* */ +/* var p:= 3 */ +/* const maxp := currentprecision + 2 */ +/* loop */ +/* p := min(2*p - 2, maxp) % p = 4,6,10, . . . , maxp */ +/* precision p */ +/* approx := .5 * (approx + f/approx) */ +/* exit when p = maxp */ +/* end loop */ +/* */ +/* % approx is now within 1 ulp of the properly rounded square root */ +/* % of f; to ensure proper rounding, compare squares of (approx - */ +/* % l/2 ulp) and (approx + l/2 ulp) with f. */ +/* p := currentprecision */ +/* begin */ +/* precision p + 2 */ +/* const approxsubhalf := approx - setexp(.5, -p) */ +/* if mulru(approxsubhalf, approxsubhalf) > f then */ +/* approx := approx - setexp(.l, -p + 1) */ +/* else */ +/* const approxaddhalf := approx + setexp(.5, -p) */ +/* if mulrd(approxaddhalf, approxaddhalf) < f then */ +/* approx := approx + setexp(.l, -p + 1) */ +/* end if */ +/* end if */ +/* end */ +/* result setexp(approx, e div 2) % fix exponent */ +/* end sqrt */ +/* ------------------------------------------------------------------ */ +#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 406 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif +U_CAPI decNumber * U_EXPORT2 uprv_decNumberSquareRoot(decNumber *res, const decNumber *rhs, + decContext *set) { + decContext workset, approxset; /* work contexts */ + decNumber dzero; /* used for constant zero */ + Int maxp; /* largest working precision */ + Int workp; /* working precision */ + Int residue=0; /* rounding residue */ + uInt status=0, ignore=0; /* status accumulators */ + uInt rstatus; /* .. */ + Int exp; /* working exponent */ + Int ideal; /* ideal (preferred) exponent */ + Int needbytes; /* work */ + Int dropped; /* .. */ + + #if DECSUBSET + decNumber *allocrhs=nullptr; /* non-nullptr if rounded rhs allocated */ + #endif + /* buffer for f [needs +1 in case DECBUFFER 0] */ + decNumber buff[D2N(DECBUFFER+1)]; + /* buffer for a [needs +2 to match likely maxp] */ + decNumber bufa[D2N(DECBUFFER+2)]; + /* buffer for temporary, b [must be same size as a] */ + decNumber bufb[D2N(DECBUFFER+2)]; + decNumber *allocbuff=nullptr; /* -> allocated buff, iff allocated */ + decNumber *allocbufa=nullptr; /* -> allocated bufa, iff allocated */ + decNumber *allocbufb=nullptr; /* -> allocated bufb, iff allocated */ + decNumber *f=buff; /* reduced fraction */ + decNumber *a=bufa; /* approximation to result */ + decNumber *b=bufb; /* intermediate result */ + /* buffer for temporary variable, up to 3 digits */ + decNumber buft[D2N(3)]; + decNumber *t=buft; /* up-to-3-digit constant or work */ + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + do { /* protect allocated storage */ + #if DECSUBSET + if (!set->extended) { + /* reduce operand and set lostDigits status, as needed */ + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, &status); + if (allocrhs==nullptr) break; + /* [Note: 'f' allocation below could reuse this buffer if */ + /* used, but as this is rare they are kept separate for clarity.] */ + rhs=allocrhs; + } + } + #endif + /* [following code does not require input rounding] */ + + /* handle infinities and NaNs */ + if (SPECIALARG) { + if (decNumberIsInfinite(rhs)) { /* an infinity */ + if (decNumberIsNegative(rhs)) status|=DEC_Invalid_operation; + else uprv_decNumberCopy(res, rhs); /* +Infinity */ + } + else decNaNs(res, rhs, nullptr, set, &status); /* a NaN */ + break; + } + + /* calculate the ideal (preferred) exponent [floor(exp/2)] */ + /* [It would be nicer to write: ideal=rhs->exponent>>1, but this */ + /* generates a compiler warning. Generated code is the same.] */ + ideal=(rhs->exponent&~1)/2; /* target */ + + /* handle zeros */ + if (ISZERO(rhs)) { + uprv_decNumberCopy(res, rhs); /* could be 0 or -0 */ + res->exponent=ideal; /* use the ideal [safe] */ + /* use decFinish to clamp any out-of-range exponent, etc. */ + decFinish(res, set, &residue, &status); + break; + } + + /* any other -x is an oops */ + if (decNumberIsNegative(rhs)) { + status|=DEC_Invalid_operation; + break; + } + + /* space is needed for three working variables */ + /* f -- the same precision as the RHS, reduced to 0.01->0.99... */ + /* a -- Hull's approximation -- precision, when assigned, is */ + /* currentprecision+1 or the input argument precision, */ + /* whichever is larger (+2 for use as temporary) */ + /* b -- intermediate temporary result (same size as a) */ + /* if any is too long for local storage, then allocate */ + workp=MAXI(set->digits+1, rhs->digits); /* actual rounding precision */ + workp=MAXI(workp, 7); /* at least 7 for low cases */ + maxp=workp+2; /* largest working precision */ + + needbytes=sizeof(decNumber)+(D2U(rhs->digits)-1)*sizeof(Unit); + if (needbytes>(Int)sizeof(buff)) { + allocbuff=(decNumber *)malloc(needbytes); + if (allocbuff==nullptr) { /* hopeless -- abandon */ + status|=DEC_Insufficient_storage; + break;} + f=allocbuff; /* use the allocated space */ + } + /* a and b both need to be able to hold a maxp-length number */ + needbytes=sizeof(decNumber)+(D2U(maxp)-1)*sizeof(Unit); + if (needbytes>(Int)sizeof(bufa)) { /* [same applies to b] */ + allocbufa=(decNumber *)malloc(needbytes); + allocbufb=(decNumber *)malloc(needbytes); + if (allocbufa==nullptr || allocbufb==nullptr) { /* hopeless */ + status|=DEC_Insufficient_storage; + break;} + a=allocbufa; /* use the allocated spaces */ + b=allocbufb; /* .. */ + } + + /* copy rhs -> f, save exponent, and reduce so 0.1 <= f < 1 */ + uprv_decNumberCopy(f, rhs); + exp=f->exponent+f->digits; /* adjusted to Hull rules */ + f->exponent=-(f->digits); /* to range */ + + /* set up working context */ + uprv_decContextDefault(&workset, DEC_INIT_DECIMAL64); + workset.emax=DEC_MAX_EMAX; + workset.emin=DEC_MIN_EMIN; + + /* [Until further notice, no error is possible and status bits */ + /* (Rounded, etc.) should be ignored, not accumulated.] */ + + /* Calculate initial approximation, and allow for odd exponent */ + workset.digits=workp; /* p for initial calculation */ + t->bits=0; t->digits=3; + a->bits=0; a->digits=3; + if ((exp & 1)==0) { /* even exponent */ + /* Set t=0.259, a=0.819 */ + t->exponent=-3; + a->exponent=-3; + #if DECDPUN>=3 + t->lsu[0]=259; + a->lsu[0]=819; + #elif DECDPUN==2 + t->lsu[0]=59; t->lsu[1]=2; + a->lsu[0]=19; a->lsu[1]=8; + #else + t->lsu[0]=9; t->lsu[1]=5; t->lsu[2]=2; + a->lsu[0]=9; a->lsu[1]=1; a->lsu[2]=8; + #endif + } + else { /* odd exponent */ + /* Set t=0.0819, a=2.59 */ + f->exponent--; /* f=f/10 */ + exp++; /* e=e+1 */ + t->exponent=-4; + a->exponent=-2; + #if DECDPUN>=3 + t->lsu[0]=819; + a->lsu[0]=259; + #elif DECDPUN==2 + t->lsu[0]=19; t->lsu[1]=8; + a->lsu[0]=59; a->lsu[1]=2; + #else + t->lsu[0]=9; t->lsu[1]=1; t->lsu[2]=8; + a->lsu[0]=9; a->lsu[1]=5; a->lsu[2]=2; + #endif + } + + decMultiplyOp(a, a, f, &workset, &ignore); /* a=a*f */ + decAddOp(a, a, t, &workset, 0, &ignore); /* ..+t */ + /* [a is now the initial approximation for sqrt(f), calculated with */ + /* currentprecision, which is also a's precision.] */ + + /* the main calculation loop */ + uprv_decNumberZero(&dzero); /* make 0 */ + uprv_decNumberZero(t); /* set t = 0.5 */ + t->lsu[0]=5; /* .. */ + t->exponent=-1; /* .. */ + workset.digits=3; /* initial p */ + for (; workset.digitsexponent+=exp/2; /* set correct exponent */ + rstatus=0; /* clear status */ + residue=0; /* .. and accumulator */ + decCopyFit(a, a, &approxset, &residue, &rstatus); /* reduce (if needed) */ + decFinish(a, &approxset, &residue, &rstatus); /* clean and finalize */ + + /* Overflow was possible if the input exponent was out-of-range, */ + /* in which case quit */ + if (rstatus&DEC_Overflow) { + status=rstatus; /* use the status as-is */ + uprv_decNumberCopy(res, a); /* copy to result */ + break; + } + + /* Preserve status except Inexact/Rounded */ + status|=(rstatus & ~(DEC_Rounded|DEC_Inexact)); + + /* Carry out the Hull correction */ + a->exponent-=exp/2; /* back to 0.1->1 */ + + /* a is now at final precision and within 1 ulp of the properly */ + /* rounded square root of f; to ensure proper rounding, compare */ + /* squares of (a - l/2 ulp) and (a + l/2 ulp) with f. */ + /* Here workset.digits=maxp and t=0.5, and a->digits determines */ + /* the ulp */ + workset.digits--; /* maxp-1 is OK now */ + t->exponent=-a->digits-1; /* make 0.5 ulp */ + decAddOp(b, a, t, &workset, DECNEG, &ignore); /* b = a - 0.5 ulp */ + workset.round=DEC_ROUND_UP; + decMultiplyOp(b, b, b, &workset, &ignore); /* b = mulru(b, b) */ + decCompareOp(b, f, b, &workset, COMPARE, &ignore); /* b ? f, reversed */ + if (decNumberIsNegative(b)) { /* f < b [i.e., b > f] */ + /* this is the more common adjustment, though both are rare */ + t->exponent++; /* make 1.0 ulp */ + t->lsu[0]=1; /* .. */ + decAddOp(a, a, t, &workset, DECNEG, &ignore); /* a = a - 1 ulp */ + /* assign to approx [round to length] */ + approxset.emin-=exp/2; /* adjust to match a */ + approxset.emax-=exp/2; + decAddOp(a, &dzero, a, &approxset, 0, &ignore); + } + else { + decAddOp(b, a, t, &workset, 0, &ignore); /* b = a + 0.5 ulp */ + workset.round=DEC_ROUND_DOWN; + decMultiplyOp(b, b, b, &workset, &ignore); /* b = mulrd(b, b) */ + decCompareOp(b, b, f, &workset, COMPARE, &ignore); /* b ? f */ + if (decNumberIsNegative(b)) { /* b < f */ + t->exponent++; /* make 1.0 ulp */ + t->lsu[0]=1; /* .. */ + decAddOp(a, a, t, &workset, 0, &ignore); /* a = a + 1 ulp */ + /* assign to approx [round to length] */ + approxset.emin-=exp/2; /* adjust to match a */ + approxset.emax-=exp/2; + decAddOp(a, &dzero, a, &approxset, 0, &ignore); + } + } + /* [no errors are possible in the above, and rounding/inexact during */ + /* estimation are irrelevant, so status was not accumulated] */ + + /* Here, 0.1 <= a < 1 (still), so adjust back */ + a->exponent+=exp/2; /* set correct exponent */ + + /* count droppable zeros [after any subnormal rounding] by */ + /* trimming a copy */ + uprv_decNumberCopy(b, a); + decTrim(b, set, 1, 1, &dropped); /* [drops trailing zeros] */ + + /* Set Inexact and Rounded. The answer can only be exact if */ + /* it is short enough so that squaring it could fit in workp */ + /* digits, so this is the only (relatively rare) condition that */ + /* a careful check is needed */ + if (b->digits*2-1 > workp) { /* cannot fit */ + status|=DEC_Inexact|DEC_Rounded; + } + else { /* could be exact/unrounded */ + uInt mstatus=0; /* local status */ + decMultiplyOp(b, b, b, &workset, &mstatus); /* try the multiply */ + if (mstatus&DEC_Overflow) { /* result just won't fit */ + status|=DEC_Inexact|DEC_Rounded; + } + else { /* plausible */ + decCompareOp(t, b, rhs, &workset, COMPARE, &mstatus); /* b ? rhs */ + if (!ISZERO(t)) status|=DEC_Inexact|DEC_Rounded; /* not equal */ + else { /* is Exact */ + /* here, dropped is the count of trailing zeros in 'a' */ + /* use closest exponent to ideal... */ + Int todrop=ideal-a->exponent; /* most that can be dropped */ + if (todrop<0) status|=DEC_Rounded; /* ideally would add 0s */ + else { /* unrounded */ + /* there are some to drop, but emax may not allow all */ + Int maxexp=set->emax-set->digits+1; + Int maxdrop=maxexp-a->exponent; + if (todrop>maxdrop && set->clamp) { /* apply clamping */ + todrop=maxdrop; + status|=DEC_Clamped; + } + if (dropped0) { /* have some to drop */ + decShiftToLeast(a->lsu, D2U(a->digits), todrop); + a->exponent+=todrop; /* maintain numerical value */ + a->digits-=todrop; /* new length */ + } + } + } + } + } + + /* double-check Underflow, as perhaps the result could not have */ + /* been subnormal (initial argument too big), or it is now Exact */ + if (status&DEC_Underflow) { + Int ae=rhs->exponent+rhs->digits-1; /* adjusted exponent */ + /* check if truly subnormal */ + #if DECEXTFLAG /* DEC_Subnormal too */ + if (ae>=set->emin*2) status&=~(DEC_Subnormal|DEC_Underflow); + #else + if (ae>=set->emin*2) status&=~DEC_Underflow; + #endif + /* check if truly inexact */ + if (!(status&DEC_Inexact)) status&=~DEC_Underflow; + } + + uprv_decNumberCopy(res, a); /* a is now the result */ + } while(0); /* end protected */ + + if (allocbuff!=nullptr) free(allocbuff); /* drop any storage used */ + if (allocbufa!=nullptr) free(allocbufa); /* .. */ + if (allocbufb!=nullptr) free(allocbufb); /* .. */ + #if DECSUBSET + if (allocrhs !=nullptr) free(allocrhs); /* .. */ + #endif + if (status!=0) decStatus(res, status, set);/* then report status */ + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberSquareRoot */ +#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 406 +#pragma GCC diagnostic pop +#endif + +/* ------------------------------------------------------------------ */ +/* decNumberSubtract -- subtract two Numbers */ +/* */ +/* This computes C = A - B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X-X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberSubtract(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; /* accumulator */ + + decAddOp(res, lhs, rhs, set, DECNEG, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } /* decNumberSubtract */ + +/* ------------------------------------------------------------------ */ +/* decNumberToIntegralExact -- round-to-integral-value with InExact */ +/* decNumberToIntegralValue -- round-to-integral-value */ +/* */ +/* res is the result */ +/* rhs is input number */ +/* set is the context */ +/* */ +/* res must have space for any value of rhs. */ +/* */ +/* This implements the IEEE special operators and therefore treats */ +/* special values as valid. For finite numbers it returns */ +/* rescale(rhs, 0) if rhs->exponent is <0. */ +/* Otherwise the result is rhs (so no error is possible, except for */ +/* sNaN). */ +/* */ +/* The context is used for rounding mode and status after sNaN, but */ +/* the digits setting is ignored. The Exact version will signal */ +/* Inexact if the result differs numerically from rhs; the other */ +/* never signals Inexact. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberToIntegralExact(decNumber *res, const decNumber *rhs, + decContext *set) { + decNumber dn; + decContext workset; /* working context */ + uInt status=0; /* accumulator */ + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + /* handle infinities and NaNs */ + if (SPECIALARG) { + if (decNumberIsInfinite(rhs)) uprv_decNumberCopy(res, rhs); /* an Infinity */ + else decNaNs(res, rhs, nullptr, set, &status); /* a NaN */ + } + else { /* finite */ + /* have a finite number; no error possible (res must be big enough) */ + if (rhs->exponent>=0) return uprv_decNumberCopy(res, rhs); + /* that was easy, but if negative exponent there is work to do... */ + workset=*set; /* clone rounding, etc. */ + workset.digits=rhs->digits; /* no length rounding */ + workset.traps=0; /* no traps */ + uprv_decNumberZero(&dn); /* make a number with exponent 0 */ + uprv_decNumberQuantize(res, rhs, &dn, &workset); + status|=workset.status; + } + if (status!=0) decStatus(res, status, set); + return res; + } /* decNumberToIntegralExact */ + +U_CAPI decNumber * U_EXPORT2 uprv_decNumberToIntegralValue(decNumber *res, const decNumber *rhs, + decContext *set) { + decContext workset=*set; /* working context */ + workset.traps=0; /* no traps */ + uprv_decNumberToIntegralExact(res, rhs, &workset); + /* this never affects set, except for sNaNs; NaN will have been set */ + /* or propagated already, so no need to call decStatus */ + set->status|=workset.status&DEC_Invalid_operation; + return res; + } /* decNumberToIntegralValue */ + +/* ------------------------------------------------------------------ */ +/* decNumberXor -- XOR two Numbers, digitwise */ +/* */ +/* This computes C = A ^ B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X^X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context (used for result length and error report) */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Logical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberXor(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + const Unit *ua, *ub; /* -> operands */ + const Unit *msua, *msub; /* -> operand msus */ + Unit *uc, *msuc; /* -> result and its msu */ + Int msudigs; /* digits in res msu */ + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + if (lhs->exponent!=0 || decNumberIsSpecial(lhs) || decNumberIsNegative(lhs) + || rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + /* operands are valid */ + ua=lhs->lsu; /* bottom-up */ + ub=rhs->lsu; /* .. */ + uc=res->lsu; /* .. */ + msua=ua+D2U(lhs->digits)-1; /* -> msu of lhs */ + msub=ub+D2U(rhs->digits)-1; /* -> msu of rhs */ + msuc=uc+D2U(set->digits)-1; /* -> msu of result */ + msudigs=MSUDIGITS(set->digits); /* [faster than remainder] */ + for (; uc<=msuc; ua++, ub++, uc++) { /* Unit loop */ + Unit a, b; /* extract units */ + if (ua>msua) a=0; + else a=*ua; + if (ub>msub) b=0; + else b=*ub; + *uc=0; /* can now write back */ + if (a|b) { /* maybe 1 bits to examine */ + Int i, j; + /* This loop could be unrolled and/or use BIN2BCD tables */ + for (i=0; i1) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + if (uc==msuc && i==msudigs-1) break; /* just did final digit */ + } /* each digit */ + } /* non-zero */ + } /* each unit */ + /* [here uc-1 is the msu of the result] */ + res->digits=decGetDigits(res->lsu, static_cast(uc-res->lsu)); + res->exponent=0; /* integer */ + res->bits=0; /* sign=0 */ + return res; /* [no status to set] */ + } /* decNumberXor */ + + +/* ================================================================== */ +/* Utility routines */ +/* ================================================================== */ + +/* ------------------------------------------------------------------ */ +/* decNumberClass -- return the decClass of a decNumber */ +/* dn -- the decNumber to test */ +/* set -- the context to use for Emin */ +/* returns the decClass enum */ +/* ------------------------------------------------------------------ */ +enum decClass uprv_decNumberClass(const decNumber *dn, decContext *set) { + if (decNumberIsSpecial(dn)) { + if (decNumberIsQNaN(dn)) return DEC_CLASS_QNAN; + if (decNumberIsSNaN(dn)) return DEC_CLASS_SNAN; + /* must be an infinity */ + if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_INF; + return DEC_CLASS_POS_INF; + } + /* is finite */ + if (uprv_decNumberIsNormal(dn, set)) { /* most common */ + if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_NORMAL; + return DEC_CLASS_POS_NORMAL; + } + /* is subnormal or zero */ + if (decNumberIsZero(dn)) { /* most common */ + if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_ZERO; + return DEC_CLASS_POS_ZERO; + } + if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_SUBNORMAL; + return DEC_CLASS_POS_SUBNORMAL; + } /* decNumberClass */ + +/* ------------------------------------------------------------------ */ +/* decNumberClassToString -- convert decClass to a string */ +/* */ +/* eclass is a valid decClass */ +/* returns a constant string describing the class (max 13+1 chars) */ +/* ------------------------------------------------------------------ */ +const char *uprv_decNumberClassToString(enum decClass eclass) { + if (eclass==DEC_CLASS_POS_NORMAL) return DEC_ClassString_PN; + if (eclass==DEC_CLASS_NEG_NORMAL) return DEC_ClassString_NN; + if (eclass==DEC_CLASS_POS_ZERO) return DEC_ClassString_PZ; + if (eclass==DEC_CLASS_NEG_ZERO) return DEC_ClassString_NZ; + if (eclass==DEC_CLASS_POS_SUBNORMAL) return DEC_ClassString_PS; + if (eclass==DEC_CLASS_NEG_SUBNORMAL) return DEC_ClassString_NS; + if (eclass==DEC_CLASS_POS_INF) return DEC_ClassString_PI; + if (eclass==DEC_CLASS_NEG_INF) return DEC_ClassString_NI; + if (eclass==DEC_CLASS_QNAN) return DEC_ClassString_QN; + if (eclass==DEC_CLASS_SNAN) return DEC_ClassString_SN; + return DEC_ClassString_UN; /* Unknown */ + } /* decNumberClassToString */ + +/* ------------------------------------------------------------------ */ +/* decNumberCopy -- copy a number */ +/* */ +/* dest is the target decNumber */ +/* src is the source decNumber */ +/* returns dest */ +/* */ +/* (dest==src is allowed and is a no-op) */ +/* All fields are updated as required. This is a utility operation, */ +/* so special values are unchanged and no error is possible. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopy(decNumber *dest, const decNumber *src) { + + #if DECCHECK + if (src==nullptr) return uprv_decNumberZero(dest); + #endif + + if (dest==src) return dest; /* no copy required */ + + /* Use explicit assignments here as structure assignment could copy */ + /* more than just the lsu (for small DECDPUN). This would not affect */ + /* the value of the results, but could disturb test harness spill */ + /* checking. */ + dest->bits=src->bits; + dest->exponent=src->exponent; + dest->digits=src->digits; + dest->lsu[0]=src->lsu[0]; + if (src->digits>DECDPUN) { /* more Units to come */ + const Unit *smsup, *s; /* work */ + Unit *d; /* .. */ + /* memcpy for the remaining Units would be safe as they cannot */ + /* overlap. However, this explicit loop is faster in short cases. */ + d=dest->lsu+1; /* -> first destination */ + smsup=src->lsu+D2U(src->digits); /* -> source msu+1 */ + for (s=src->lsu+1; sdigits digits. */ +/* No exception or error can occur; this is a quiet bitwise operation.*/ +/* See also decNumberAbs for a checking version of this. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopyAbs(decNumber *res, const decNumber *rhs) { + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, DECUNCONT)) return res; + #endif + uprv_decNumberCopy(res, rhs); + res->bits&=~DECNEG; /* turn off sign */ + return res; + } /* decNumberCopyAbs */ + +/* ------------------------------------------------------------------ */ +/* decNumberCopyNegate -- quiet negate value operator */ +/* */ +/* This sets C = negate(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* */ +/* C must have space for set->digits digits. */ +/* No exception or error can occur; this is a quiet bitwise operation.*/ +/* See also decNumberMinus for a checking version of this. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopyNegate(decNumber *res, const decNumber *rhs) { + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, DECUNCONT)) return res; + #endif + uprv_decNumberCopy(res, rhs); + res->bits^=DECNEG; /* invert the sign */ + return res; + } /* decNumberCopyNegate */ + +/* ------------------------------------------------------------------ */ +/* decNumberCopySign -- quiet copy and set sign operator */ +/* */ +/* This sets C = A with the sign of B */ +/* */ +/* res is C, the result. C may be A */ +/* lhs is A */ +/* rhs is B */ +/* */ +/* C must have space for set->digits digits. */ +/* No exception or error can occur; this is a quiet bitwise operation.*/ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopySign(decNumber *res, const decNumber *lhs, + const decNumber *rhs) { + uByte sign; /* rhs sign */ + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, DECUNCONT)) return res; + #endif + sign=rhs->bits & DECNEG; /* save sign bit */ + uprv_decNumberCopy(res, lhs); + res->bits&=~DECNEG; /* clear the sign */ + res->bits|=sign; /* set from rhs */ + return res; + } /* decNumberCopySign */ + +/* ------------------------------------------------------------------ */ +/* decNumberGetBCD -- get the coefficient in BCD8 */ +/* dn is the source decNumber */ +/* bcd is the uInt array that will receive dn->digits BCD bytes, */ +/* most-significant at offset 0 */ +/* returns bcd */ +/* */ +/* bcd must have at least dn->digits bytes. No error is possible; if */ +/* dn is a NaN or Infinite, digits must be 1 and the coefficient 0. */ +/* ------------------------------------------------------------------ */ +U_CAPI uByte * U_EXPORT2 uprv_decNumberGetBCD(const decNumber *dn, uByte *bcd) { + uByte *ub=bcd+dn->digits-1; /* -> lsd */ + const Unit *up=dn->lsu; /* Unit pointer, -> lsu */ + + #if DECDPUN==1 /* trivial simple copy */ + for (; ub>=bcd; ub--, up++) *ub=*up; + #else /* chopping needed */ + uInt u=*up; /* work */ + uInt cut=DECDPUN; /* downcounter through unit */ + for (; ub>=bcd; ub--) { + *ub=(uByte)(u%10); /* [*6554 trick inhibits, here] */ + u=u/10; + cut--; + if (cut>0) continue; /* more in this unit */ + up++; + u=*up; + cut=DECDPUN; + } + #endif + return bcd; + } /* decNumberGetBCD */ + +/* ------------------------------------------------------------------ */ +/* decNumberSetBCD -- set (replace) the coefficient from BCD8 */ +/* dn is the target decNumber */ +/* bcd is the uInt array that will source n BCD bytes, most- */ +/* significant at offset 0 */ +/* n is the number of digits in the source BCD array (bcd) */ +/* returns dn */ +/* */ +/* dn must have space for at least n digits. No error is possible; */ +/* if dn is a NaN, or Infinite, or is to become a zero, n must be 1 */ +/* and bcd[0] zero. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberSetBCD(decNumber *dn, const uByte *bcd, uInt n) { + Unit *up=dn->lsu+D2U(dn->digits)-1; /* -> msu [target pointer] */ + const uByte *ub=bcd; /* -> source msd */ + + #if DECDPUN==1 /* trivial simple copy */ + for (; ub=dn->lsu; up--) { /* each Unit from msu */ + *up=0; /* will take <=DECDPUN digits */ + for (; cut>0; ub++, cut--) *up=X10(*up)+*ub; + cut=DECDPUN; /* next Unit has all digits */ + } + #endif + dn->digits=n; /* set digit count */ + return dn; + } /* decNumberSetBCD */ + +/* ------------------------------------------------------------------ */ +/* decNumberIsNormal -- test normality of a decNumber */ +/* dn is the decNumber to test */ +/* set is the context to use for Emin */ +/* returns 1 if |dn| is finite and >=Nmin, 0 otherwise */ +/* ------------------------------------------------------------------ */ +Int uprv_decNumberIsNormal(const decNumber *dn, decContext *set) { + Int ae; /* adjusted exponent */ + #if DECCHECK + if (decCheckOperands(DECUNRESU, DECUNUSED, dn, set)) return 0; + #endif + + if (decNumberIsSpecial(dn)) return 0; /* not finite */ + if (decNumberIsZero(dn)) return 0; /* not non-zero */ + + ae=dn->exponent+dn->digits-1; /* adjusted exponent */ + if (aeemin) return 0; /* is subnormal */ + return 1; + } /* decNumberIsNormal */ + +/* ------------------------------------------------------------------ */ +/* decNumberIsSubnormal -- test subnormality of a decNumber */ +/* dn is the decNumber to test */ +/* set is the context to use for Emin */ +/* returns 1 if |dn| is finite, non-zero, and exponent+dn->digits-1; /* adjusted exponent */ + if (aeemin) return 1; /* is subnormal */ + return 0; + } /* decNumberIsSubnormal */ + +/* ------------------------------------------------------------------ */ +/* decNumberTrim -- remove insignificant zeros */ +/* */ +/* dn is the number to trim */ +/* returns dn */ +/* */ +/* All fields are updated as required. This is a utility operation, */ +/* so special values are unchanged and no error is possible. The */ +/* zeros are removed unconditionally. */ +/* ------------------------------------------------------------------ */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberTrim(decNumber *dn) { + Int dropped; /* work */ + decContext set; /* .. */ + #if DECCHECK + if (decCheckOperands(DECUNRESU, DECUNUSED, dn, DECUNCONT)) return dn; + #endif + uprv_decContextDefault(&set, DEC_INIT_BASE); /* clamp=0 */ + return decTrim(dn, &set, 0, 1, &dropped); + } /* decNumberTrim */ + +/* ------------------------------------------------------------------ */ +/* decNumberVersion -- return the name and version of this module */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +const char * uprv_decNumberVersion() { + return DECVERSION; + } /* decNumberVersion */ + +/* ------------------------------------------------------------------ */ +/* decNumberZero -- set a number to 0 */ +/* */ +/* dn is the number to set, with space for one digit */ +/* returns dn */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +/* Memset is not used as it is much slower in some environments. */ +U_CAPI decNumber * U_EXPORT2 uprv_decNumberZero(decNumber *dn) { + + #if DECCHECK + if (decCheckOperands(dn, DECUNUSED, DECUNUSED, DECUNCONT)) return dn; + #endif + + dn->bits=0; + dn->exponent=0; + dn->digits=1; + dn->lsu[0]=0; + return dn; + } /* decNumberZero */ + +/* ================================================================== */ +/* Local routines */ +/* ================================================================== */ + +/* ------------------------------------------------------------------ */ +/* decToString -- lay out a number into a string */ +/* */ +/* dn is the number to lay out */ +/* string is where to lay out the number */ +/* eng is 1 if Engineering, 0 if Scientific */ +/* */ +/* string must be at least dn->digits+14 characters long */ +/* No error is possible. */ +/* */ +/* Note that this routine can generate a -0 or 0.000. These are */ +/* never generated in subset to-number or arithmetic, but can occur */ +/* in non-subset arithmetic (e.g., -1*0 or 1.234-1.234). */ +/* ------------------------------------------------------------------ */ +/* If DECCHECK is enabled the string "?" is returned if a number is */ +/* invalid. */ +static void decToString(const decNumber *dn, char *string, Flag eng) { + Int exp=dn->exponent; /* local copy */ + Int e; /* E-part value */ + Int pre; /* digits before the '.' */ + Int cut; /* for counting digits in a Unit */ + char *c=string; /* work [output pointer] */ + const Unit *up=dn->lsu+D2U(dn->digits)-1; /* -> msu [input pointer] */ + uInt u, pow; /* work */ + + #if DECCHECK + if (decCheckOperands(DECUNRESU, dn, DECUNUSED, DECUNCONT)) { + strcpy(string, "?"); + return;} + #endif + + if (decNumberIsNegative(dn)) { /* Negatives get a minus */ + *c='-'; + c++; + } + if (dn->bits&DECSPECIAL) { /* Is a special value */ + if (decNumberIsInfinite(dn)) { + strcpy(c, "Inf"); + strcpy(c+3, "inity"); + return;} + /* a NaN */ + if (dn->bits&DECSNAN) { /* signalling NaN */ + *c='s'; + c++; + } + strcpy(c, "NaN"); + c+=3; /* step past */ + /* if not a clean non-zero coefficient, that's all there is in a */ + /* NaN string */ + if (exp!=0 || (*dn->lsu==0 && dn->digits==1)) return; + /* [drop through to add integer] */ + } + + /* calculate how many digits in msu, and hence first cut */ + cut=MSUDIGITS(dn->digits); /* [faster than remainder] */ + cut--; /* power of ten for digit */ + + if (exp==0) { /* simple integer [common fastpath] */ + for (;up>=dn->lsu; up--) { /* each Unit from msu */ + u=*up; /* contains DECDPUN digits to lay out */ + for (; cut>=0; c++, cut--) TODIGIT(u, cut, c, pow); + cut=DECDPUN-1; /* next Unit has all digits */ + } + *c='\0'; /* terminate the string */ + return;} + + /* non-0 exponent -- assume plain form */ + pre=dn->digits+exp; /* digits before '.' */ + e=0; /* no E */ + if ((exp>0) || (pre<-5)) { /* need exponential form */ + e=exp+dn->digits-1; /* calculate E value */ + pre=1; /* assume one digit before '.' */ + if (eng && (e!=0)) { /* engineering: may need to adjust */ + Int adj; /* adjustment */ + /* The C remainder operator is undefined for negative numbers, so */ + /* a positive remainder calculation must be used here */ + if (e<0) { + adj=(-e)%3; + if (adj!=0) adj=3-adj; + } + else { /* e>0 */ + adj=e%3; + } + e=e-adj; + /* if dealing with zero still produce an exponent which is a */ + /* multiple of three, as expected, but there will only be the */ + /* one zero before the E, still. Otherwise note the padding. */ + if (!ISZERO(dn)) pre+=adj; + else { /* is zero */ + if (adj!=0) { /* 0.00Esnn needed */ + e=e+3; + pre=-(2-adj); + } + } /* zero */ + } /* eng */ + } /* need exponent */ + + /* lay out the digits of the coefficient, adding 0s and . as needed */ + u=*up; + if (pre>0) { /* xxx.xxx or xx00 (engineering) form */ + Int n=pre; + for (; pre>0; pre--, c++, cut--) { + if (cut<0) { /* need new Unit */ + if (up==dn->lsu) break; /* out of input digits (pre>digits) */ + up--; + cut=DECDPUN-1; + u=*up; + } + TODIGIT(u, cut, c, pow); + } + if (ndigits) { /* more to come, after '.' */ + *c='.'; c++; + for (;; c++, cut--) { + if (cut<0) { /* need new Unit */ + if (up==dn->lsu) break; /* out of input digits */ + up--; + cut=DECDPUN-1; + u=*up; + } + TODIGIT(u, cut, c, pow); + } + } + else for (; pre>0; pre--, c++) *c='0'; /* 0 padding (for engineering) needed */ + } + else { /* 0.xxx or 0.000xxx form */ + *c='0'; c++; + *c='.'; c++; + for (; pre<0; pre++, c++) *c='0'; /* add any 0's after '.' */ + for (; ; c++, cut--) { + if (cut<0) { /* need new Unit */ + if (up==dn->lsu) break; /* out of input digits */ + up--; + cut=DECDPUN-1; + u=*up; + } + TODIGIT(u, cut, c, pow); + } + } + + /* Finally add the E-part, if needed. It will never be 0, has a + base maximum and minimum of +999999999 through -999999999, but + could range down to -1999999998 for abnormal numbers */ + if (e!=0) { + Flag had=0; /* 1=had non-zero */ + *c='E'; c++; + *c='+'; c++; /* assume positive */ + u=e; /* .. */ + if (e<0) { + *(c-1)='-'; /* oops, need - */ + u=-e; /* uInt, please */ + } + /* lay out the exponent [_itoa or equivalent is not ANSI C] */ + for (cut=9; cut>=0; cut--) { + TODIGIT(u, cut, c, pow); + if (*c=='0' && !had) continue; /* skip leading zeros */ + had=1; /* had non-0 */ + c++; /* step for next */ + } /* cut */ + } + *c='\0'; /* terminate the string (all paths) */ + return; + } /* decToString */ + +/* ------------------------------------------------------------------ */ +/* decAddOp -- add/subtract operation */ +/* */ +/* This computes C = A + B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X+X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* negate is DECNEG if rhs should be negated, or 0 otherwise */ +/* status accumulates status for the caller */ +/* */ +/* C must have space for set->digits digits. */ +/* Inexact in status must be 0 for correct Exact zero sign in result */ +/* ------------------------------------------------------------------ */ +/* If possible, the coefficient is calculated directly into C. */ +/* However, if: */ +/* -- a digits+1 calculation is needed because the numbers are */ +/* unaligned and span more than set->digits digits */ +/* -- a carry to digits+1 digits looks possible */ +/* -- C is the same as A or B, and the result would destructively */ +/* overlap the A or B coefficient */ +/* then the result must be calculated into a temporary buffer. In */ +/* this case a local (stack) buffer is used if possible, and only if */ +/* too long for that does malloc become the final resort. */ +/* */ +/* Misalignment is handled as follows: */ +/* Apad: (AExp>BExp) Swap operands and proceed as for BExp>AExp. */ +/* BPad: Apply the padding by a combination of shifting (whole */ +/* units) and multiplication (part units). */ +/* */ +/* Addition, especially x=x+1, is speed-critical. */ +/* The static buffer is larger than might be expected to allow for */ +/* calls from higher-level functions (notable exp). */ +/* ------------------------------------------------------------------ */ +static decNumber * decAddOp(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set, + uByte negate, uInt *status) { + #if DECSUBSET + decNumber *alloclhs=nullptr; /* non-nullptr if rounded lhs allocated */ + decNumber *allocrhs=nullptr; /* .., rhs */ + #endif + Int rhsshift; /* working shift (in Units) */ + Int maxdigits; /* longest logical length */ + Int mult; /* multiplier */ + Int residue; /* rounding accumulator */ + uByte bits; /* result bits */ + Flag diffsign; /* non-0 if arguments have different sign */ + Unit *acc; /* accumulator for result */ + Unit accbuff[SD2U(DECBUFFER*2+20)]; /* local buffer [*2+20 reduces many */ + /* allocations when called from */ + /* other operations, notable exp] */ + Unit *allocacc=nullptr; /* -> allocated acc buffer, iff allocated */ + Int reqdigits=set->digits; /* local copy; requested DIGITS */ + Int padding; /* work */ + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + do { /* protect allocated storage */ + #if DECSUBSET + if (!set->extended) { + /* reduce operands and set lostDigits status, as needed */ + if (lhs->digits>reqdigits) { + alloclhs=decRoundOperand(lhs, set, status); + if (alloclhs==nullptr) break; + lhs=alloclhs; + } + if (rhs->digits>reqdigits) { + allocrhs=decRoundOperand(rhs, set, status); + if (allocrhs==nullptr) break; + rhs=allocrhs; + } + } + #endif + /* [following code does not require input rounding] */ + + /* note whether signs differ [used all paths] */ + diffsign=(Flag)((lhs->bits^rhs->bits^negate)&DECNEG); + + /* handle infinities and NaNs */ + if (SPECIALARGS) { /* a special bit set */ + if (SPECIALARGS & (DECSNAN | DECNAN)) /* a NaN */ + decNaNs(res, lhs, rhs, set, status); + else { /* one or two infinities */ + if (decNumberIsInfinite(lhs)) { /* LHS is infinity */ + /* two infinities with different signs is invalid */ + if (decNumberIsInfinite(rhs) && diffsign) { + *status|=DEC_Invalid_operation; + break; + } + bits=lhs->bits & DECNEG; /* get sign from LHS */ + } + else bits=(rhs->bits^negate) & DECNEG;/* RHS must be Infinity */ + bits|=DECINF; + uprv_decNumberZero(res); + res->bits=bits; /* set +/- infinity */ + } /* an infinity */ + break; + } + + /* Quick exit for add 0s; return the non-0, modified as need be */ + if (ISZERO(lhs)) { + Int adjust; /* work */ + Int lexp=lhs->exponent; /* save in case LHS==RES */ + bits=lhs->bits; /* .. */ + residue=0; /* clear accumulator */ + decCopyFit(res, rhs, set, &residue, status); /* copy (as needed) */ + res->bits^=negate; /* flip if rhs was negated */ + #if DECSUBSET + if (set->extended) { /* exponents on zeros count */ + #endif + /* exponent will be the lower of the two */ + adjust=lexp-res->exponent; /* adjustment needed [if -ve] */ + if (ISZERO(res)) { /* both 0: special IEEE 754 rules */ + if (adjust<0) res->exponent=lexp; /* set exponent */ + /* 0-0 gives +0 unless rounding to -infinity, and -0-0 gives -0 */ + if (diffsign) { + if (set->round!=DEC_ROUND_FLOOR) res->bits=0; + else res->bits=DECNEG; /* preserve 0 sign */ + } + } + else { /* non-0 res */ + if (adjust<0) { /* 0-padding needed */ + if ((res->digits-adjust)>set->digits) { + adjust=res->digits-set->digits; /* to fit exactly */ + *status|=DEC_Rounded; /* [but exact] */ + } + res->digits=decShiftToMost(res->lsu, res->digits, -adjust); + res->exponent+=adjust; /* set the exponent. */ + } + } /* non-0 res */ + #if DECSUBSET + } /* extended */ + #endif + decFinish(res, set, &residue, status); /* clean and finalize */ + break;} + + if (ISZERO(rhs)) { /* [lhs is non-zero] */ + Int adjust; /* work */ + Int rexp=rhs->exponent; /* save in case RHS==RES */ + bits=rhs->bits; /* be clean */ + residue=0; /* clear accumulator */ + decCopyFit(res, lhs, set, &residue, status); /* copy (as needed) */ + #if DECSUBSET + if (set->extended) { /* exponents on zeros count */ + #endif + /* exponent will be the lower of the two */ + /* [0-0 case handled above] */ + adjust=rexp-res->exponent; /* adjustment needed [if -ve] */ + if (adjust<0) { /* 0-padding needed */ + if ((res->digits-adjust)>set->digits) { + adjust=res->digits-set->digits; /* to fit exactly */ + *status|=DEC_Rounded; /* [but exact] */ + } + res->digits=decShiftToMost(res->lsu, res->digits, -adjust); + res->exponent+=adjust; /* set the exponent. */ + } + #if DECSUBSET + } /* extended */ + #endif + decFinish(res, set, &residue, status); /* clean and finalize */ + break;} + + /* [NB: both fastpath and mainpath code below assume these cases */ + /* (notably 0-0) have already been handled] */ + + /* calculate the padding needed to align the operands */ + padding=rhs->exponent-lhs->exponent; + + /* Fastpath cases where the numbers are aligned and normal, the RHS */ + /* is all in one unit, no operand rounding is needed, and no carry, */ + /* lengthening, or borrow is needed */ + if (padding==0 + && rhs->digits<=DECDPUN + && rhs->exponent>=set->emin /* [some normals drop through] */ + && rhs->exponent<=set->emax-set->digits+1 /* [could clamp] */ + && rhs->digits<=reqdigits + && lhs->digits<=reqdigits) { + Int partial=*lhs->lsu; + if (!diffsign) { /* adding */ + partial+=*rhs->lsu; + if ((partial<=DECDPUNMAX) /* result fits in unit */ + && (lhs->digits>=DECDPUN || /* .. and no digits-count change */ + partial<(Int)powers[lhs->digits])) { /* .. */ + if (res!=lhs) uprv_decNumberCopy(res, lhs); /* not in place */ + *res->lsu=(Unit)partial; /* [copy could have overwritten RHS] */ + break; + } + /* else drop out for careful add */ + } + else { /* signs differ */ + partial-=*rhs->lsu; + if (partial>0) { /* no borrow needed, and non-0 result */ + if (res!=lhs) uprv_decNumberCopy(res, lhs); /* not in place */ + *res->lsu=(Unit)partial; + /* this could have reduced digits [but result>0] */ + res->digits=decGetDigits(res->lsu, D2U(res->digits)); + break; + } + /* else drop out for careful subtract */ + } + } + + /* Now align (pad) the lhs or rhs so they can be added or */ + /* subtracted, as necessary. If one number is much larger than */ + /* the other (that is, if in plain form there is a least one */ + /* digit between the lowest digit of one and the highest of the */ + /* other) padding with up to DIGITS-1 trailing zeros may be */ + /* needed; then apply rounding (as exotic rounding modes may be */ + /* affected by the residue). */ + rhsshift=0; /* rhs shift to left (padding) in Units */ + bits=lhs->bits; /* assume sign is that of LHS */ + mult=1; /* likely multiplier */ + + /* [if padding==0 the operands are aligned; no padding is needed] */ + if (padding!=0) { + /* some padding needed; always pad the RHS, as any required */ + /* padding can then be effected by a simple combination of */ + /* shifts and a multiply */ + Flag swapped=0; + if (padding<0) { /* LHS needs the padding */ + const decNumber *t; + padding=-padding; /* will be +ve */ + bits=(uByte)(rhs->bits^negate); /* assumed sign is now that of RHS */ + t=lhs; lhs=rhs; rhs=t; + swapped=1; + } + + /* If, after pad, rhs would be longer than lhs by digits+1 or */ + /* more then lhs cannot affect the answer, except as a residue, */ + /* so only need to pad up to a length of DIGITS+1. */ + if (rhs->digits+padding > lhs->digits+reqdigits+1) { + /* The RHS is sufficient */ + /* for residue use the relative sign indication... */ + Int shift=reqdigits-rhs->digits; /* left shift needed */ + residue=1; /* residue for rounding */ + if (diffsign) residue=-residue; /* signs differ */ + /* copy, shortening if necessary */ + decCopyFit(res, rhs, set, &residue, status); + /* if it was already shorter, then need to pad with zeros */ + if (shift>0) { + res->digits=decShiftToMost(res->lsu, res->digits, shift); + res->exponent-=shift; /* adjust the exponent. */ + } + /* flip the result sign if unswapped and rhs was negated */ + if (!swapped) res->bits^=negate; + decFinish(res, set, &residue, status); /* done */ + break;} + + /* LHS digits may affect result */ + rhsshift=D2U(padding+1)-1; /* this much by Unit shift .. */ + mult=powers[padding-(rhsshift*DECDPUN)]; /* .. this by multiplication */ + } /* padding needed */ + + if (diffsign) mult=-mult; /* signs differ */ + + /* determine the longer operand */ + maxdigits=rhs->digits+padding; /* virtual length of RHS */ + if (lhs->digits>maxdigits) maxdigits=lhs->digits; + + /* Decide on the result buffer to use; if possible place directly */ + /* into result. */ + acc=res->lsu; /* assume add direct to result */ + /* If destructive overlap, or the number is too long, or a carry or */ + /* borrow to DIGITS+1 might be possible, a buffer must be used. */ + /* [Might be worth more sophisticated tests when maxdigits==reqdigits] */ + if ((maxdigits>=reqdigits) /* is, or could be, too large */ + || (res==rhs && rhsshift>0)) { /* destructive overlap */ + /* buffer needed, choose it; units for maxdigits digits will be */ + /* needed, +1 Unit for carry or borrow */ + Int need=D2U(maxdigits)+1; + acc=accbuff; /* assume use local buffer */ + if (need*sizeof(Unit)>sizeof(accbuff)) { + /* printf("malloc add %ld %ld\n", need, sizeof(accbuff)); */ + allocacc=(Unit *)malloc(need*sizeof(Unit)); + if (allocacc==nullptr) { /* hopeless -- abandon */ + *status|=DEC_Insufficient_storage; + break;} + acc=allocacc; + } + } + + res->bits=(uByte)(bits&DECNEG); /* it's now safe to overwrite.. */ + res->exponent=lhs->exponent; /* .. operands (even if aliased) */ + + #if DECTRACE + decDumpAr('A', lhs->lsu, D2U(lhs->digits)); + decDumpAr('B', rhs->lsu, D2U(rhs->digits)); + printf(" :h: %ld %ld\n", rhsshift, mult); + #endif + + /* add [A+B*m] or subtract [A+B*(-m)] */ + U_ASSERT(rhs->digits > 0); + U_ASSERT(lhs->digits > 0); + res->digits=decUnitAddSub(lhs->lsu, D2U(lhs->digits), + rhs->lsu, D2U(rhs->digits), + rhsshift, acc, mult) + *DECDPUN; /* [units -> digits] */ + if (res->digits<0) { /* borrowed... */ + res->digits=-res->digits; + res->bits^=DECNEG; /* flip the sign */ + } + #if DECTRACE + decDumpAr('+', acc, D2U(res->digits)); + #endif + + /* If a buffer was used the result must be copied back, possibly */ + /* shortening. (If no buffer was used then the result must have */ + /* fit, so can't need rounding and residue must be 0.) */ + residue=0; /* clear accumulator */ + if (acc!=res->lsu) { + #if DECSUBSET + if (set->extended) { /* round from first significant digit */ + #endif + /* remove leading zeros that were added due to rounding up to */ + /* integral Units -- before the test for rounding. */ + if (res->digits>reqdigits) + res->digits=decGetDigits(acc, D2U(res->digits)); + decSetCoeff(res, set, acc, res->digits, &residue, status); + #if DECSUBSET + } + else { /* subset arithmetic rounds from original significant digit */ + /* May have an underestimate. This only occurs when both */ + /* numbers fit in DECDPUN digits and are padding with a */ + /* negative multiple (-10, -100...) and the top digit(s) become */ + /* 0. (This only matters when using X3.274 rules where the */ + /* leading zero could be included in the rounding.) */ + if (res->digitsdigits))=0; /* ensure leading 0 is there */ + res->digits=maxdigits; + } + else { + /* remove leading zeros that added due to rounding up to */ + /* integral Units (but only those in excess of the original */ + /* maxdigits length, unless extended) before test for rounding. */ + if (res->digits>reqdigits) { + res->digits=decGetDigits(acc, D2U(res->digits)); + if (res->digitsdigits=maxdigits; + } + } + decSetCoeff(res, set, acc, res->digits, &residue, status); + /* Now apply rounding if needed before removing leading zeros. */ + /* This is safe because subnormals are not a possibility */ + if (residue!=0) { + decApplyRound(res, set, residue, status); + residue=0; /* did what needed to be done */ + } + } /* subset */ + #endif + } /* used buffer */ + + /* strip leading zeros [these were left on in case of subset subtract] */ + res->digits=decGetDigits(res->lsu, D2U(res->digits)); + + /* apply checks and rounding */ + decFinish(res, set, &residue, status); + + /* "When the sum of two operands with opposite signs is exactly */ + /* zero, the sign of that sum shall be '+' in all rounding modes */ + /* except round toward -Infinity, in which mode that sign shall be */ + /* '-'." [Subset zeros also never have '-', set by decFinish.] */ + if (ISZERO(res) && diffsign + #if DECSUBSET + && set->extended + #endif + && (*status&DEC_Inexact)==0) { + if (set->round==DEC_ROUND_FLOOR) res->bits|=DECNEG; /* sign - */ + else res->bits&=~DECNEG; /* sign + */ + } + } while(0); /* end protected */ + + if (allocacc!=nullptr) free(allocacc); /* drop any storage used */ + #if DECSUBSET + if (allocrhs!=nullptr) free(allocrhs); /* .. */ + if (alloclhs!=nullptr) free(alloclhs); /* .. */ + #endif + return res; + } /* decAddOp */ + +/* ------------------------------------------------------------------ */ +/* decDivideOp -- division operation */ +/* */ +/* This routine performs the calculations for all four division */ +/* operators (divide, divideInteger, remainder, remainderNear). */ +/* */ +/* C=A op B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X/X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* op is DIVIDE, DIVIDEINT, REMAINDER, or REMNEAR respectively. */ +/* status is the usual accumulator */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* ------------------------------------------------------------------ */ +/* The underlying algorithm of this routine is the same as in the */ +/* 1981 S/370 implementation, that is, non-restoring long division */ +/* with bi-unit (rather than bi-digit) estimation for each unit */ +/* multiplier. In this pseudocode overview, complications for the */ +/* Remainder operators and division residues for exact rounding are */ +/* omitted for clarity. */ +/* */ +/* Prepare operands and handle special values */ +/* Test for x/0 and then 0/x */ +/* Exp =Exp1 - Exp2 */ +/* Exp =Exp +len(var1) -len(var2) */ +/* Sign=Sign1 * Sign2 */ +/* Pad accumulator (Var1) to double-length with 0's (pad1) */ +/* Pad Var2 to same length as Var1 */ +/* msu2pair/plus=1st 2 or 1 units of var2, +1 to allow for round */ +/* have=0 */ +/* Do until (have=digits+1 OR residue=0) */ +/* if exp<0 then if integer divide/residue then leave */ +/* this_unit=0 */ +/* Do forever */ +/* compare numbers */ +/* if <0 then leave inner_loop */ +/* if =0 then (* quick exit without subtract *) do */ +/* this_unit=this_unit+1; output this_unit */ +/* leave outer_loop; end */ +/* Compare lengths of numbers (mantissae): */ +/* If same then tops2=msu2pair -- {units 1&2 of var2} */ +/* else tops2=msu2plus -- {0, unit 1 of var2} */ +/* tops1=first_unit_of_Var1*10**DECDPUN +second_unit_of_var1 */ +/* mult=tops1/tops2 -- Good and safe guess at divisor */ +/* if mult=0 then mult=1 */ +/* this_unit=this_unit+mult */ +/* subtract */ +/* end inner_loop */ +/* if have\=0 | this_unit\=0 then do */ +/* output this_unit */ +/* have=have+1; end */ +/* var2=var2/10 */ +/* exp=exp-1 */ +/* end outer_loop */ +/* exp=exp+1 -- set the proper exponent */ +/* if have=0 then generate answer=0 */ +/* Return (Result is defined by Var1) */ +/* */ +/* ------------------------------------------------------------------ */ +/* Two working buffers are needed during the division; one (digits+ */ +/* 1) to accumulate the result, and the other (up to 2*digits+1) for */ +/* long subtractions. These are acc and var1 respectively. */ +/* var1 is a copy of the lhs coefficient, var2 is the rhs coefficient.*/ +/* The static buffers may be larger than might be expected to allow */ +/* for calls from higher-level functions (notable exp). */ +/* ------------------------------------------------------------------ */ +static decNumber * decDivideOp(decNumber *res, + const decNumber *lhs, const decNumber *rhs, + decContext *set, Flag op, uInt *status) { + #if DECSUBSET + decNumber *alloclhs=nullptr; /* non-nullptr if rounded lhs allocated */ + decNumber *allocrhs=nullptr; /* .., rhs */ + #endif + Unit accbuff[SD2U(DECBUFFER+DECDPUN+10)]; /* local buffer */ + Unit *acc=accbuff; /* -> accumulator array for result */ + Unit *allocacc=nullptr; /* -> allocated buffer, iff allocated */ + Unit *accnext; /* -> where next digit will go */ + Int acclength; /* length of acc needed [Units] */ + Int accunits; /* count of units accumulated */ + Int accdigits; /* count of digits accumulated */ + + Unit varbuff[SD2U(DECBUFFER*2+DECDPUN)]; /* buffer for var1 */ + Unit *var1=varbuff; /* -> var1 array for long subtraction */ + Unit *varalloc=nullptr; /* -> allocated buffer, iff used */ + Unit *msu1; /* -> msu of var1 */ + + const Unit *var2; /* -> var2 array */ + const Unit *msu2; /* -> msu of var2 */ + Int msu2plus; /* msu2 plus one [does not vary] */ + eInt msu2pair; /* msu2 pair plus one [does not vary] */ + + Int var1units, var2units; /* actual lengths */ + Int var2ulen; /* logical length (units) */ + Int var1initpad=0; /* var1 initial padding (digits) */ + Int maxdigits; /* longest LHS or required acc length */ + Int mult; /* multiplier for subtraction */ + Unit thisunit; /* current unit being accumulated */ + Int residue; /* for rounding */ + Int reqdigits=set->digits; /* requested DIGITS */ + Int exponent; /* working exponent */ + Int maxexponent=0; /* DIVIDE maximum exponent if unrounded */ + uByte bits; /* working sign */ + Unit *target; /* work */ + const Unit *source; /* .. */ + uInt const *pow; /* .. */ + Int shift, cut; /* .. */ + #if DECSUBSET + Int dropped; /* work */ + #endif + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + do { /* protect allocated storage */ + #if DECSUBSET + if (!set->extended) { + /* reduce operands and set lostDigits status, as needed */ + if (lhs->digits>reqdigits) { + alloclhs=decRoundOperand(lhs, set, status); + if (alloclhs==nullptr) break; + lhs=alloclhs; + } + if (rhs->digits>reqdigits) { + allocrhs=decRoundOperand(rhs, set, status); + if (allocrhs==nullptr) break; + rhs=allocrhs; + } + } + #endif + /* [following code does not require input rounding] */ + + bits=(lhs->bits^rhs->bits)&DECNEG; /* assumed sign for divisions */ + + /* handle infinities and NaNs */ + if (SPECIALARGS) { /* a special bit set */ + if (SPECIALARGS & (DECSNAN | DECNAN)) { /* one or two NaNs */ + decNaNs(res, lhs, rhs, set, status); + break; + } + /* one or two infinities */ + if (decNumberIsInfinite(lhs)) { /* LHS (dividend) is infinite */ + if (decNumberIsInfinite(rhs) || /* two infinities are invalid .. */ + op & (REMAINDER | REMNEAR)) { /* as is remainder of infinity */ + *status|=DEC_Invalid_operation; + break; + } + /* [Note that infinity/0 raises no exceptions] */ + uprv_decNumberZero(res); + res->bits=bits|DECINF; /* set +/- infinity */ + break; + } + else { /* RHS (divisor) is infinite */ + residue=0; + if (op&(REMAINDER|REMNEAR)) { + /* result is [finished clone of] lhs */ + decCopyFit(res, lhs, set, &residue, status); + } + else { /* a division */ + uprv_decNumberZero(res); + res->bits=bits; /* set +/- zero */ + /* for DIVIDEINT the exponent is always 0. For DIVIDE, result */ + /* is a 0 with infinitely negative exponent, clamped to minimum */ + if (op&DIVIDE) { + res->exponent=set->emin-set->digits+1; + *status|=DEC_Clamped; + } + } + decFinish(res, set, &residue, status); + break; + } + } + + /* handle 0 rhs (x/0) */ + if (ISZERO(rhs)) { /* x/0 is always exceptional */ + if (ISZERO(lhs)) { + uprv_decNumberZero(res); /* [after lhs test] */ + *status|=DEC_Division_undefined;/* 0/0 will become NaN */ + } + else { + uprv_decNumberZero(res); + if (op&(REMAINDER|REMNEAR)) *status|=DEC_Invalid_operation; + else { + *status|=DEC_Division_by_zero; /* x/0 */ + res->bits=bits|DECINF; /* .. is +/- Infinity */ + } + } + break;} + + /* handle 0 lhs (0/x) */ + if (ISZERO(lhs)) { /* 0/x [x!=0] */ + #if DECSUBSET + if (!set->extended) uprv_decNumberZero(res); + else { + #endif + if (op&DIVIDE) { + residue=0; + exponent=lhs->exponent-rhs->exponent; /* ideal exponent */ + uprv_decNumberCopy(res, lhs); /* [zeros always fit] */ + res->bits=bits; /* sign as computed */ + res->exponent=exponent; /* exponent, too */ + decFinalize(res, set, &residue, status); /* check exponent */ + } + else if (op&DIVIDEINT) { + uprv_decNumberZero(res); /* integer 0 */ + res->bits=bits; /* sign as computed */ + } + else { /* a remainder */ + exponent=rhs->exponent; /* [save in case overwrite] */ + uprv_decNumberCopy(res, lhs); /* [zeros always fit] */ + if (exponentexponent) res->exponent=exponent; /* use lower */ + } + #if DECSUBSET + } + #endif + break;} + + /* Precalculate exponent. This starts off adjusted (and hence fits */ + /* in 31 bits) and becomes the usual unadjusted exponent as the */ + /* division proceeds. The order of evaluation is important, here, */ + /* to avoid wrap. */ + exponent=(lhs->exponent+lhs->digits)-(rhs->exponent+rhs->digits); + + /* If the working exponent is -ve, then some quick exits are */ + /* possible because the quotient is known to be <1 */ + /* [for REMNEAR, it needs to be < -1, as -0.5 could need work] */ + if (exponent<0 && !(op==DIVIDE)) { + if (op&DIVIDEINT) { + uprv_decNumberZero(res); /* integer part is 0 */ + #if DECSUBSET + if (set->extended) + #endif + res->bits=bits; /* set +/- zero */ + break;} + /* fastpath remainders so long as the lhs has the smaller */ + /* (or equal) exponent */ + if (lhs->exponent<=rhs->exponent) { + if (op&REMAINDER || exponent<-1) { + /* It is REMAINDER or safe REMNEAR; result is [finished */ + /* clone of] lhs (r = x - 0*y) */ + residue=0; + decCopyFit(res, lhs, set, &residue, status); + decFinish(res, set, &residue, status); + break; + } + /* [unsafe REMNEAR drops through] */ + } + } /* fastpaths */ + + /* Long (slow) division is needed; roll up the sleeves... */ + + /* The accumulator will hold the quotient of the division. */ + /* If it needs to be too long for stack storage, then allocate. */ + acclength=D2U(reqdigits+DECDPUN); /* in Units */ + if (acclength*sizeof(Unit)>sizeof(accbuff)) { + /* printf("malloc dvacc %ld units\n", acclength); */ + allocacc=(Unit *)malloc(acclength*sizeof(Unit)); + if (allocacc==nullptr) { /* hopeless -- abandon */ + *status|=DEC_Insufficient_storage; + break;} + acc=allocacc; /* use the allocated space */ + } + + /* var1 is the padded LHS ready for subtractions. */ + /* If it needs to be too long for stack storage, then allocate. */ + /* The maximum units needed for var1 (long subtraction) is: */ + /* Enough for */ + /* (rhs->digits+reqdigits-1) -- to allow full slide to right */ + /* or (lhs->digits) -- to allow for long lhs */ + /* whichever is larger */ + /* +1 -- for rounding of slide to right */ + /* +1 -- for leading 0s */ + /* +1 -- for pre-adjust if a remainder or DIVIDEINT */ + /* [Note: unused units do not participate in decUnitAddSub data] */ + maxdigits=rhs->digits+reqdigits-1; + if (lhs->digits>maxdigits) maxdigits=lhs->digits; + var1units=D2U(maxdigits)+2; + /* allocate a guard unit above msu1 for REMAINDERNEAR */ + if (!(op&DIVIDE)) var1units++; + if ((var1units+1)*sizeof(Unit)>sizeof(varbuff)) { + /* printf("malloc dvvar %ld units\n", var1units+1); */ + varalloc=(Unit *)malloc((var1units+1)*sizeof(Unit)); + if (varalloc==nullptr) { /* hopeless -- abandon */ + *status|=DEC_Insufficient_storage; + break;} + var1=varalloc; /* use the allocated space */ + } + + /* Extend the lhs and rhs to full long subtraction length. The lhs */ + /* is truly extended into the var1 buffer, with 0 padding, so a */ + /* subtract in place is always possible. The rhs (var2) has */ + /* virtual padding (implemented by decUnitAddSub). */ + /* One guard unit was allocated above msu1 for rem=rem+rem in */ + /* REMAINDERNEAR. */ + msu1=var1+var1units-1; /* msu of var1 */ + source=lhs->lsu+D2U(lhs->digits)-1; /* msu of input array */ + for (target=msu1; source>=lhs->lsu; source--, target--) *target=*source; + for (; target>=var1; target--) *target=0; + + /* rhs (var2) is left-aligned with var1 at the start */ + var2ulen=var1units; /* rhs logical length (units) */ + var2units=D2U(rhs->digits); /* rhs actual length (units) */ + var2=rhs->lsu; /* -> rhs array */ + msu2=var2+var2units-1; /* -> msu of var2 [never changes] */ + /* now set up the variables which will be used for estimating the */ + /* multiplication factor. If these variables are not exact, add */ + /* 1 to make sure that the multiplier is never overestimated. */ + msu2plus=*msu2; /* it's value .. */ + if (var2units>1) msu2plus++; /* .. +1 if any more */ + msu2pair=(eInt)*msu2*(DECDPUNMAX+1);/* top two pair .. */ + if (var2units>1) { /* .. [else treat 2nd as 0] */ + msu2pair+=*(msu2-1); /* .. */ + if (var2units>2) msu2pair++; /* .. +1 if any more */ + } + + /* The calculation is working in units, which may have leading zeros, */ + /* but the exponent was calculated on the assumption that they are */ + /* both left-aligned. Adjust the exponent to compensate: add the */ + /* number of leading zeros in var1 msu and subtract those in var2 msu. */ + /* [This is actually done by counting the digits and negating, as */ + /* lead1=DECDPUN-digits1, and similarly for lead2.] */ + for (pow=&powers[1]; *msu1>=*pow; pow++) exponent--; + for (pow=&powers[1]; *msu2>=*pow; pow++) exponent++; + + /* Now, if doing an integer divide or remainder, ensure that */ + /* the result will be Unit-aligned. To do this, shift the var1 */ + /* accumulator towards least if need be. (It's much easier to */ + /* do this now than to reassemble the residue afterwards, if */ + /* doing a remainder.) Also ensure the exponent is not negative. */ + if (!(op&DIVIDE)) { + Unit *u; /* work */ + /* save the initial 'false' padding of var1, in digits */ + var1initpad=(var1units-D2U(lhs->digits))*DECDPUN; + /* Determine the shift to do. */ + if (exponent<0) cut=-exponent; + else cut=DECDPUN-exponent%DECDPUN; + decShiftToLeast(var1, var1units, cut); + exponent+=cut; /* maintain numerical value */ + var1initpad-=cut; /* .. and reduce padding */ + /* clean any most-significant units which were just emptied */ + for (u=msu1; cut>=DECDPUN; cut-=DECDPUN, u--) *u=0; + } /* align */ + else { /* is DIVIDE */ + maxexponent=lhs->exponent-rhs->exponent; /* save */ + /* optimization: if the first iteration will just produce 0, */ + /* preadjust to skip it [valid for DIVIDE only] */ + if (*msu1<*msu2) { + var2ulen--; /* shift down */ + exponent-=DECDPUN; /* update the exponent */ + } + } + + /* ---- start the long-division loops ------------------------------ */ + accunits=0; /* no units accumulated yet */ + accdigits=0; /* .. or digits */ + accnext=acc+acclength-1; /* -> msu of acc [NB: allows digits+1] */ + for (;;) { /* outer forever loop */ + thisunit=0; /* current unit assumed 0 */ + /* find the next unit */ + for (;;) { /* inner forever loop */ + /* strip leading zero units [from either pre-adjust or from */ + /* subtract last time around]. Leave at least one unit. */ + for (; *msu1==0 && msu1>var1; msu1--) var1units--; + + if (var1units msu */ + for (pv1=msu1; ; pv1--, pv2--) { + /* v1=*pv1 -- always OK */ + v2=0; /* assume in padding */ + if (pv2>=var2) v2=*pv2; /* in range */ + if (*pv1!=v2) break; /* no longer the same */ + if (pv1==var1) break; /* done; leave pv1 as is */ + } + /* here when all inspected or a difference seen */ + if (*pv1v2. Prepare for real subtraction; the lengths are equal */ + /* Estimate the multiplier (there's always a msu1-1)... */ + /* Bring in two units of var2 to provide a good estimate. */ + mult=(Int)(((eInt)*msu1*(DECDPUNMAX+1)+*(msu1-1))/msu2pair); + } /* lengths the same */ + else { /* var1units > var2ulen, so subtraction is safe */ + /* The var2 msu is one unit towards the lsu of the var1 msu, */ + /* so only one unit for var2 can be used. */ + mult=(Int)(((eInt)*msu1*(DECDPUNMAX+1)+*(msu1-1))/msu2plus); + } + if (mult==0) mult=1; /* must always be at least 1 */ + /* subtraction needed; var1 is > var2 */ + thisunit=(Unit)(thisunit+mult); /* accumulate */ + /* subtract var1-var2, into var1; only the overlap needs */ + /* processing, as this is an in-place calculation */ + shift=var2ulen-var2units; + #if DECTRACE + decDumpAr('1', &var1[shift], var1units-shift); + decDumpAr('2', var2, var2units); + printf("m=%ld\n", -mult); + #endif + decUnitAddSub(&var1[shift], var1units-shift, + var2, var2units, 0, + &var1[shift], -mult); + #if DECTRACE + decDumpAr('#', &var1[shift], var1units-shift); + #endif + /* var1 now probably has leading zeros; these are removed at the */ + /* top of the inner loop. */ + } /* inner loop */ + + /* The next unit has been calculated in full; unless it's a */ + /* leading zero, add to acc */ + if (accunits!=0 || thisunit!=0) { /* is first or non-zero */ + *accnext=thisunit; /* store in accumulator */ + /* account exactly for the new digits */ + if (accunits==0) { + accdigits++; /* at least one */ + for (pow=&powers[1]; thisunit>=*pow; pow++) accdigits++; + } + else accdigits+=DECDPUN; + accunits++; /* update count */ + accnext--; /* ready for next */ + if (accdigits>reqdigits) break; /* have enough digits */ + } + + /* if the residue is zero, the operation is done (unless divide */ + /* or divideInteger and still not enough digits yet) */ + if (*var1==0 && var1units==1) { /* residue is 0 */ + if (op&(REMAINDER|REMNEAR)) break; + if ((op&DIVIDE) && (exponent<=maxexponent)) break; + /* [drop through if divideInteger] */ + } + /* also done enough if calculating remainder or integer */ + /* divide and just did the last ('units') unit */ + if (exponent==0 && !(op&DIVIDE)) break; + + /* to get here, var1 is less than var2, so divide var2 by the per- */ + /* Unit power of ten and go for the next digit */ + var2ulen--; /* shift down */ + exponent-=DECDPUN; /* update the exponent */ + } /* outer loop */ + + /* ---- division is complete --------------------------------------- */ + /* here: acc has at least reqdigits+1 of good results (or fewer */ + /* if early stop), starting at accnext+1 (its lsu) */ + /* var1 has any residue at the stopping point */ + /* accunits is the number of digits collected in acc */ + if (accunits==0) { /* acc is 0 */ + accunits=1; /* show have a unit .. */ + accdigits=1; /* .. */ + *accnext=0; /* .. whose value is 0 */ + } + else accnext++; /* back to last placed */ + /* accnext now -> lowest unit of result */ + + residue=0; /* assume no residue */ + if (op&DIVIDE) { + /* record the presence of any residue, for rounding */ + if (*var1!=0 || var1units>1) residue=1; + else { /* no residue */ + /* Had an exact division; clean up spurious trailing 0s. */ + /* There will be at most DECDPUN-1, from the final multiply, */ + /* and then only if the result is non-0 (and even) and the */ + /* exponent is 'loose'. */ + #if DECDPUN>1 + Unit lsu=*accnext; + if (!(lsu&0x01) && (lsu!=0)) { + /* count the trailing zeros */ + Int drop=0; + for (;; drop++) { /* [will terminate because lsu!=0] */ + if (exponent>=maxexponent) break; /* don't chop real 0s */ + #if DECDPUN<=4 + if ((lsu-QUOT10(lsu, drop+1) + *powers[drop+1])!=0) break; /* found non-0 digit */ + #else + if (lsu%powers[drop+1]!=0) break; /* found non-0 digit */ + #endif + exponent++; + } + if (drop>0) { + accunits=decShiftToLeast(accnext, accunits, drop); + accdigits=decGetDigits(accnext, accunits); + accunits=D2U(accdigits); + /* [exponent was adjusted in the loop] */ + } + } /* neither odd nor 0 */ + #endif + } /* exact divide */ + } /* divide */ + else /* op!=DIVIDE */ { + /* check for coefficient overflow */ + if (accdigits+exponent>reqdigits) { + *status|=DEC_Division_impossible; + break; + } + if (op & (REMAINDER|REMNEAR)) { + /* [Here, the exponent will be 0, because var1 was adjusted */ + /* appropriately.] */ + Int postshift; /* work */ + Flag wasodd=0; /* integer was odd */ + Unit *quotlsu; /* for save */ + Int quotdigits; /* .. */ + + bits=lhs->bits; /* remainder sign is always as lhs */ + + /* Fastpath when residue is truly 0 is worthwhile [and */ + /* simplifies the code below] */ + if (*var1==0 && var1units==1) { /* residue is 0 */ + Int exp=lhs->exponent; /* save min(exponents) */ + if (rhs->exponentexponent; + uprv_decNumberZero(res); /* 0 coefficient */ + #if DECSUBSET + if (set->extended) + #endif + res->exponent=exp; /* .. with proper exponent */ + res->bits=(uByte)(bits&DECNEG); /* [cleaned] */ + decFinish(res, set, &residue, status); /* might clamp */ + break; + } + /* note if the quotient was odd */ + if (*accnext & 0x01) wasodd=1; /* acc is odd */ + quotlsu=accnext; /* save in case need to reinspect */ + quotdigits=accdigits; /* .. */ + + /* treat the residue, in var1, as the value to return, via acc */ + /* calculate the unused zero digits. This is the smaller of: */ + /* var1 initial padding (saved above) */ + /* var2 residual padding, which happens to be given by: */ + postshift=var1initpad+exponent-lhs->exponent+rhs->exponent; + /* [the 'exponent' term accounts for the shifts during divide] */ + if (var1initpadexponent; /* exponent is smaller of lhs & rhs */ + if (rhs->exponentexponent; + + /* Now correct the result if doing remainderNear; if it */ + /* (looking just at coefficients) is > rhs/2, or == rhs/2 and */ + /* the integer was odd then the result should be rem-rhs. */ + if (op&REMNEAR) { + Int compare, tarunits; /* work */ + Unit *up; /* .. */ + /* calculate remainder*2 into the var1 buffer (which has */ + /* 'headroom' of an extra unit and hence enough space) */ + /* [a dedicated 'double' loop would be faster, here] */ + tarunits=decUnitAddSub(accnext, accunits, accnext, accunits, + 0, accnext, 1); + /* decDumpAr('r', accnext, tarunits); */ + + /* Here, accnext (var1) holds tarunits Units with twice the */ + /* remainder's coefficient, which must now be compared to the */ + /* RHS. The remainder's exponent may be smaller than the RHS's. */ + compare=decUnitCompare(accnext, tarunits, rhs->lsu, D2U(rhs->digits), + rhs->exponent-exponent); + if (compare==BADINT) { /* deep trouble */ + *status|=DEC_Insufficient_storage; + break;} + + /* now restore the remainder by dividing by two; the lsu */ + /* is known to be even. */ + for (up=accnext; up0 || (compare==0 && wasodd)) { /* adjustment needed */ + Int exp, expunits, exprem; /* work */ + /* This is effectively causing round-up of the quotient, */ + /* so if it was the rare case where it was full and all */ + /* nines, it would overflow and hence division-impossible */ + /* should be raised */ + Flag allnines=0; /* 1 if quotient all nines */ + if (quotdigits==reqdigits) { /* could be borderline */ + for (up=quotlsu; ; up++) { + if (quotdigits>DECDPUN) { + if (*up!=DECDPUNMAX) break;/* non-nines */ + } + else { /* this is the last Unit */ + if (*up==powers[quotdigits]-1) allnines=1; + break; + } + quotdigits-=DECDPUN; /* checked those digits */ + } /* up */ + } /* borderline check */ + if (allnines) { + *status|=DEC_Division_impossible; + break;} + + /* rem-rhs is needed; the sign will invert. Again, var1 */ + /* can safely be used for the working Units array. */ + exp=rhs->exponent-exponent; /* RHS padding needed */ + /* Calculate units and remainder from exponent. */ + expunits=exp/DECDPUN; + exprem=exp%DECDPUN; + /* subtract [A+B*(-m)]; the result will always be negative */ + accunits=-decUnitAddSub(accnext, accunits, + rhs->lsu, D2U(rhs->digits), + expunits, accnext, -(Int)powers[exprem]); + accdigits=decGetDigits(accnext, accunits); /* count digits exactly */ + accunits=D2U(accdigits); /* and recalculate the units for copy */ + /* [exponent is as for original remainder] */ + bits^=DECNEG; /* flip the sign */ + } + } /* REMNEAR */ + } /* REMAINDER or REMNEAR */ + } /* not DIVIDE */ + + /* Set exponent and bits */ + res->exponent=exponent; + res->bits=(uByte)(bits&DECNEG); /* [cleaned] */ + + /* Now the coefficient. */ + decSetCoeff(res, set, accnext, accdigits, &residue, status); + + decFinish(res, set, &residue, status); /* final cleanup */ + + #if DECSUBSET + /* If a divide then strip trailing zeros if subset [after round] */ + if (!set->extended && (op==DIVIDE)) decTrim(res, set, 0, 1, &dropped); + #endif + } while(0); /* end protected */ + + if (varalloc!=nullptr) free(varalloc); /* drop any storage used */ + if (allocacc!=nullptr) free(allocacc); /* .. */ + #if DECSUBSET + if (allocrhs!=nullptr) free(allocrhs); /* .. */ + if (alloclhs!=nullptr) free(alloclhs); /* .. */ + #endif + return res; + } /* decDivideOp */ + +/* ------------------------------------------------------------------ */ +/* decMultiplyOp -- multiplication operation */ +/* */ +/* This routine performs the multiplication C=A x B. */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X*X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* status is the usual accumulator */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* ------------------------------------------------------------------ */ +/* 'Classic' multiplication is used rather than Karatsuba, as the */ +/* latter would give only a minor improvement for the short numbers */ +/* expected to be handled most (and uses much more memory). */ +/* */ +/* There are two major paths here: the general-purpose ('old code') */ +/* path which handles all DECDPUN values, and a fastpath version */ +/* which is used if 64-bit ints are available, DECDPUN<=4, and more */ +/* than two calls to decUnitAddSub would be made. */ +/* */ +/* The fastpath version lumps units together into 8-digit or 9-digit */ +/* chunks, and also uses a lazy carry strategy to minimise expensive */ +/* 64-bit divisions. The chunks are then broken apart again into */ +/* units for continuing processing. Despite this overhead, the */ +/* fastpath can speed up some 16-digit operations by 10x (and much */ +/* more for higher-precision calculations). */ +/* */ +/* A buffer always has to be used for the accumulator; in the */ +/* fastpath, buffers are also always needed for the chunked copies of */ +/* of the operand coefficients. */ +/* Static buffers are larger than needed just for multiply, to allow */ +/* for calls from other operations (notably exp). */ +/* ------------------------------------------------------------------ */ +#define FASTMUL (DECUSE64 && DECDPUN<5) +static decNumber * decMultiplyOp(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set, + uInt *status) { + Int accunits; /* Units of accumulator in use */ + Int exponent; /* work */ + Int residue=0; /* rounding residue */ + uByte bits; /* result sign */ + Unit *acc; /* -> accumulator Unit array */ + Int needbytes; /* size calculator */ + void *allocacc=nullptr; /* -> allocated accumulator, iff allocated */ + Unit accbuff[SD2U(DECBUFFER*4+1)]; /* buffer (+1 for DECBUFFER==0, */ + /* *4 for calls from other operations) */ + const Unit *mer, *mermsup; /* work */ + Int madlength; /* Units in multiplicand */ + Int shift; /* Units to shift multiplicand by */ + + #if FASTMUL + /* if DECDPUN is 1 or 3 work in base 10**9, otherwise */ + /* (DECDPUN is 2 or 4) then work in base 10**8 */ + #if DECDPUN & 1 /* odd */ + #define FASTBASE 1000000000 /* base */ + #define FASTDIGS 9 /* digits in base */ + #define FASTLAZY 18 /* carry resolution point [1->18] */ + #else + #define FASTBASE 100000000 + #define FASTDIGS 8 + #define FASTLAZY 1844 /* carry resolution point [1->1844] */ + #endif + /* three buffers are used, two for chunked copies of the operands */ + /* (base 10**8 or base 10**9) and one base 2**64 accumulator with */ + /* lazy carry evaluation */ + uInt zlhibuff[(DECBUFFER*2+1)/8+1]; /* buffer (+1 for DECBUFFER==0) */ + uInt *zlhi=zlhibuff; /* -> lhs array */ + uInt *alloclhi=nullptr; /* -> allocated buffer, iff allocated */ + uInt zrhibuff[(DECBUFFER*2+1)/8+1]; /* buffer (+1 for DECBUFFER==0) */ + uInt *zrhi=zrhibuff; /* -> rhs array */ + uInt *allocrhi=nullptr; /* -> allocated buffer, iff allocated */ + uLong zaccbuff[(DECBUFFER*2+1)/4+2]; /* buffer (+1 for DECBUFFER==0) */ + /* [allocacc is shared for both paths, as only one will run] */ + uLong *zacc=zaccbuff; /* -> accumulator array for exact result */ + #if DECDPUN==1 + Int zoff; /* accumulator offset */ + #endif + uInt *lip, *rip; /* item pointers */ + uInt *lmsi, *rmsi; /* most significant items */ + Int ilhs, irhs, iacc; /* item counts in the arrays */ + Int lazy; /* lazy carry counter */ + uLong lcarry; /* uLong carry */ + uInt carry; /* carry (NB not uLong) */ + Int count; /* work */ + const Unit *cup; /* .. */ + Unit *up; /* .. */ + uLong *lp; /* .. */ + Int p; /* .. */ + #endif + + #if DECSUBSET + decNumber *alloclhs=nullptr; /* -> allocated buffer, iff allocated */ + decNumber *allocrhs=nullptr; /* -> allocated buffer, iff allocated */ + #endif + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + /* precalculate result sign */ + bits=(uByte)((lhs->bits^rhs->bits)&DECNEG); + + /* handle infinities and NaNs */ + if (SPECIALARGS) { /* a special bit set */ + if (SPECIALARGS & (DECSNAN | DECNAN)) { /* one or two NaNs */ + decNaNs(res, lhs, rhs, set, status); + return res;} + /* one or two infinities; Infinity * 0 is invalid */ + if (((lhs->bits & DECINF)==0 && ISZERO(lhs)) + ||((rhs->bits & DECINF)==0 && ISZERO(rhs))) { + *status|=DEC_Invalid_operation; + return res;} + uprv_decNumberZero(res); + res->bits=bits|DECINF; /* infinity */ + return res;} + + /* For best speed, as in DMSRCN [the original Rexx numerics */ + /* module], use the shorter number as the multiplier (rhs) and */ + /* the longer as the multiplicand (lhs) to minimise the number of */ + /* adds (partial products) */ + if (lhs->digitsdigits) { /* swap... */ + const decNumber *hold=lhs; + lhs=rhs; + rhs=hold; + } + + do { /* protect allocated storage */ + #if DECSUBSET + if (!set->extended) { + /* reduce operands and set lostDigits status, as needed */ + if (lhs->digits>set->digits) { + alloclhs=decRoundOperand(lhs, set, status); + if (alloclhs==nullptr) break; + lhs=alloclhs; + } + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, status); + if (allocrhs==nullptr) break; + rhs=allocrhs; + } + } + #endif + /* [following code does not require input rounding] */ + + #if FASTMUL /* fastpath can be used */ + /* use the fast path if there are enough digits in the shorter */ + /* operand to make the setup and takedown worthwhile */ + #define NEEDTWO (DECDPUN*2) /* within two decUnitAddSub calls */ + if (rhs->digits>NEEDTWO) { /* use fastpath... */ + /* calculate the number of elements in each array */ + ilhs=(lhs->digits+FASTDIGS-1)/FASTDIGS; /* [ceiling] */ + irhs=(rhs->digits+FASTDIGS-1)/FASTDIGS; /* .. */ + iacc=ilhs+irhs; + + /* allocate buffers if required, as usual */ + needbytes=ilhs*sizeof(uInt); + if (needbytes>(Int)sizeof(zlhibuff)) { + alloclhi=(uInt *)malloc(needbytes); + zlhi=alloclhi;} + needbytes=irhs*sizeof(uInt); + if (needbytes>(Int)sizeof(zrhibuff)) { + allocrhi=(uInt *)malloc(needbytes); + zrhi=allocrhi;} + + /* Allocating the accumulator space needs a special case when */ + /* DECDPUN=1 because when converting the accumulator to Units */ + /* after the multiplication each 8-byte item becomes 9 1-byte */ + /* units. Therefore iacc extra bytes are needed at the front */ + /* (rounded up to a multiple of 8 bytes), and the uLong */ + /* accumulator starts offset the appropriate number of units */ + /* to the right to avoid overwrite during the unchunking. */ + + /* Make sure no signed int overflow below. This is always true */ + /* if the given numbers have less digits than DEC_MAX_DIGITS. */ + U_ASSERT((uint32_t)iacc <= INT32_MAX/sizeof(uLong)); + needbytes=iacc*sizeof(uLong); + #if DECDPUN==1 + zoff=(iacc+7)/8; /* items to offset by */ + needbytes+=zoff*8; + #endif + if (needbytes>(Int)sizeof(zaccbuff)) { + allocacc=(uLong *)malloc(needbytes); + zacc=(uLong *)allocacc;} + if (zlhi==nullptr||zrhi==nullptr||zacc==nullptr) { + *status|=DEC_Insufficient_storage; + break;} + + acc=(Unit *)zacc; /* -> target Unit array */ + #if DECDPUN==1 + zacc+=zoff; /* start uLong accumulator to right */ + #endif + + /* assemble the chunked copies of the left and right sides */ + for (count=lhs->digits, cup=lhs->lsu, lip=zlhi; count>0; lip++) + for (p=0, *lip=0; p0; + p+=DECDPUN, cup++, count-=DECDPUN) + *lip+=*cup*powers[p]; + lmsi=lip-1; /* save -> msi */ + for (count=rhs->digits, cup=rhs->lsu, rip=zrhi; count>0; rip++) + for (p=0, *rip=0; p0; + p+=DECDPUN, cup++, count-=DECDPUN) + *rip+=*cup*powers[p]; + rmsi=rip-1; /* save -> msi */ + + /* zero the accumulator */ + for (lp=zacc; lp0 && rip!=rmsi) continue; + lazy=FASTLAZY; /* reset delay count */ + /* spin up the accumulator resolving overflows */ + for (lp=zacc; lp(up-acc); /* count of units */ + } + else { /* here to use units directly, without chunking ['old code'] */ + #endif + + /* if accumulator will be too long for local storage, then allocate */ + acc=accbuff; /* -> assume buffer for accumulator */ + needbytes=(D2U(lhs->digits)+D2U(rhs->digits))*sizeof(Unit); + if (needbytes>(Int)sizeof(accbuff)) { + allocacc=(Unit *)malloc(needbytes); + if (allocacc==nullptr) {*status|=DEC_Insufficient_storage; break;} + acc=(Unit *)allocacc; /* use the allocated space */ + } + + /* Now the main long multiplication loop */ + /* Unlike the equivalent in the IBM Java implementation, there */ + /* is no advantage in calculating from msu to lsu. So, do it */ + /* by the book, as it were. */ + /* Each iteration calculates ACC=ACC+MULTAND*MULT */ + accunits=1; /* accumulator starts at '0' */ + *acc=0; /* .. (lsu=0) */ + shift=0; /* no multiplicand shift at first */ + madlength=D2U(lhs->digits); /* this won't change */ + mermsup=rhs->lsu+D2U(rhs->digits); /* -> msu+1 of multiplier */ + + for (mer=rhs->lsu; merlsu, madlength, 0, + &acc[shift], *mer) + + shift; + else { /* extend acc with a 0; it will be used shortly */ + *(acc+accunits)=0; /* [this avoids length of <=0 later] */ + accunits++; + } + /* multiply multiplicand by 10**DECDPUN for next Unit to left */ + shift++; /* add this for 'logical length' */ + } /* n */ + #if FASTMUL + } /* unchunked units */ + #endif + /* common end-path */ + #if DECTRACE + decDumpAr('*', acc, accunits); /* Show exact result */ + #endif + + /* acc now contains the exact result of the multiplication, */ + /* possibly with a leading zero unit; build the decNumber from */ + /* it, noting if any residue */ + res->bits=bits; /* set sign */ + res->digits=decGetDigits(acc, accunits); /* count digits exactly */ + + /* There can be a 31-bit wrap in calculating the exponent. */ + /* This can only happen if both input exponents are negative and */ + /* both their magnitudes are large. If there was a wrap, set a */ + /* safe very negative exponent, from which decFinalize() will */ + /* raise a hard underflow shortly. */ + exponent=lhs->exponent+rhs->exponent; /* calculate exponent */ + if (lhs->exponent<0 && rhs->exponent<0 && exponent>0) + exponent=-2*DECNUMMAXE; /* force underflow */ + res->exponent=exponent; /* OK to overwrite now */ + + + /* Set the coefficient. If any rounding, residue records */ + decSetCoeff(res, set, acc, res->digits, &residue, status); + decFinish(res, set, &residue, status); /* final cleanup */ + } while(0); /* end protected */ + + if (allocacc!=nullptr) free(allocacc); /* drop any storage used */ + #if DECSUBSET + if (allocrhs!=nullptr) free(allocrhs); /* .. */ + if (alloclhs!=nullptr) free(alloclhs); /* .. */ + #endif + #if FASTMUL + if (allocrhi!=nullptr) free(allocrhi); /* .. */ + if (alloclhi!=nullptr) free(alloclhi); /* .. */ + #endif + return res; + } /* decMultiplyOp */ + +/* ------------------------------------------------------------------ */ +/* decExpOp -- effect exponentiation */ +/* */ +/* This computes C = exp(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context; note that rounding mode has no effect */ +/* */ +/* C must have space for set->digits digits. status is updated but */ +/* not set. */ +/* */ +/* Restrictions: */ +/* */ +/* digits, emax, and -emin in the context must be less than */ +/* 2*DEC_MAX_MATH (1999998), and the rhs must be within these */ +/* bounds or a zero. This is an internal routine, so these */ +/* restrictions are contractual and not enforced. */ +/* */ +/* A finite result is rounded using DEC_ROUND_HALF_EVEN; it will */ +/* almost always be correctly rounded, but may be up to 1 ulp in */ +/* error in rare cases. */ +/* */ +/* Finite results will always be full precision and Inexact, except */ +/* when A is a zero or -Infinity (giving 1 or 0 respectively). */ +/* ------------------------------------------------------------------ */ +/* This approach used here is similar to the algorithm described in */ +/* */ +/* Variable Precision Exponential Function, T. E. Hull and */ +/* A. Abrham, ACM Transactions on Mathematical Software, Vol 12 #2, */ +/* pp79-91, ACM, June 1986. */ +/* */ +/* with the main difference being that the iterations in the series */ +/* evaluation are terminated dynamically (which does not require the */ +/* extra variable-precision variables which are expensive in this */ +/* context). */ +/* */ +/* The error analysis in Hull & Abrham's paper applies except for the */ +/* round-off error accumulation during the series evaluation. This */ +/* code does not precalculate the number of iterations and so cannot */ +/* use Horner's scheme. Instead, the accumulation is done at double- */ +/* precision, which ensures that the additions of the terms are exact */ +/* and do not accumulate round-off (and any round-off errors in the */ +/* terms themselves move 'to the right' faster than they can */ +/* accumulate). This code also extends the calculation by allowing, */ +/* in the spirit of other decNumber operators, the input to be more */ +/* precise than the result (the precision used is based on the more */ +/* precise of the input or requested result). */ +/* */ +/* Implementation notes: */ +/* */ +/* 1. This is separated out as decExpOp so it can be called from */ +/* other Mathematical functions (notably Ln) with a wider range */ +/* than normal. In particular, it can handle the slightly wider */ +/* (double) range needed by Ln (which has to be able to calculate */ +/* exp(-x) where x can be the tiniest number (Ntiny). */ +/* */ +/* 2. Normalizing x to be <=0.1 (instead of <=1) reduces loop */ +/* iterations by approximately a third with additional (although */ +/* diminishing) returns as the range is reduced to even smaller */ +/* fractions. However, h (the power of 10 used to correct the */ +/* result at the end, see below) must be kept <=8 as otherwise */ +/* the final result cannot be computed. Hence the leverage is a */ +/* sliding value (8-h), where potentially the range is reduced */ +/* more for smaller values. */ +/* */ +/* The leverage that can be applied in this way is severely */ +/* limited by the cost of the raise-to-the power at the end, */ +/* which dominates when the number of iterations is small (less */ +/* than ten) or when rhs is short. As an example, the adjustment */ +/* x**10,000,000 needs 31 multiplications, all but one full-width. */ +/* */ +/* 3. The restrictions (especially precision) could be raised with */ +/* care, but the full decNumber range seems very hard within the */ +/* 32-bit limits. */ +/* */ +/* 4. The working precisions for the static buffers are twice the */ +/* obvious size to allow for calls from decNumberPower. */ +/* ------------------------------------------------------------------ */ +decNumber * decExpOp(decNumber *res, const decNumber *rhs, + decContext *set, uInt *status) { + uInt ignore=0; /* working status */ + Int h; /* adjusted exponent for 0.xxxx */ + Int p; /* working precision */ + Int residue; /* rounding residue */ + uInt needbytes; /* for space calculations */ + const decNumber *x=rhs; /* (may point to safe copy later) */ + decContext aset, tset, dset; /* working contexts */ + Int comp; /* work */ + + /* the argument is often copied to normalize it, so (unusually) it */ + /* is treated like other buffers, using DECBUFFER, +1 in case */ + /* DECBUFFER is 0 */ + decNumber bufr[D2N(DECBUFFER*2+1)]; + decNumber *allocrhs=nullptr; /* non-nullptr if rhs buffer allocated */ + + /* the working precision will be no more than set->digits+8+1 */ + /* so for on-stack buffers DECBUFFER+9 is used, +1 in case DECBUFFER */ + /* is 0 (and twice that for the accumulator) */ + + /* buffer for t, term (working precision plus) */ + decNumber buft[D2N(DECBUFFER*2+9+1)]; + decNumber *allocbuft=nullptr; /* -> allocated buft, iff allocated */ + decNumber *t=buft; /* term */ + /* buffer for a, accumulator (working precision * 2), at least 9 */ + decNumber bufa[D2N(DECBUFFER*4+18+1)]; + decNumber *allocbufa=nullptr; /* -> allocated bufa, iff allocated */ + decNumber *a=bufa; /* accumulator */ + /* decNumber for the divisor term; this needs at most 9 digits */ + /* and so can be fixed size [16 so can use standard context] */ + decNumber bufd[D2N(16)]; + decNumber *d=bufd; /* divisor */ + decNumber numone; /* constant 1 */ + + #if DECCHECK + Int iterations=0; /* for later sanity check */ + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + do { /* protect allocated storage */ + if (SPECIALARG) { /* handle infinities and NaNs */ + if (decNumberIsInfinite(rhs)) { /* an infinity */ + if (decNumberIsNegative(rhs)) /* -Infinity -> +0 */ + uprv_decNumberZero(res); + else uprv_decNumberCopy(res, rhs); /* +Infinity -> self */ + } + else decNaNs(res, rhs, nullptr, set, status); /* a NaN */ + break;} + + if (ISZERO(rhs)) { /* zeros -> exact 1 */ + uprv_decNumberZero(res); /* make clean 1 */ + *res->lsu=1; /* .. */ + break;} /* [no status to set] */ + + /* e**x when 0 < x < 0.66 is < 1+3x/2, hence can fast-path */ + /* positive and negative tiny cases which will result in inexact */ + /* 1. This also allows the later add-accumulate to always be */ + /* exact (because its length will never be more than twice the */ + /* working precision). */ + /* The comparator (tiny) needs just one digit, so use the */ + /* decNumber d for it (reused as the divisor, etc., below); its */ + /* exponent is such that if x is positive it will have */ + /* set->digits-1 zeros between the decimal point and the digit, */ + /* which is 4, and if x is negative one more zero there as the */ + /* more precise result will be of the form 0.9999999 rather than */ + /* 1.0000001. Hence, tiny will be 0.0000004 if digits=7 and x>0 */ + /* or 0.00000004 if digits=7 and x<0. If RHS not larger than */ + /* this then the result will be 1.000000 */ + uprv_decNumberZero(d); /* clean */ + *d->lsu=4; /* set 4 .. */ + d->exponent=-set->digits; /* * 10**(-d) */ + if (decNumberIsNegative(rhs)) d->exponent--; /* negative case */ + comp=decCompare(d, rhs, 1); /* signless compare */ + if (comp==BADINT) { + *status|=DEC_Insufficient_storage; + break;} + if (comp>=0) { /* rhs < d */ + Int shift=set->digits-1; + uprv_decNumberZero(res); /* set 1 */ + *res->lsu=1; /* .. */ + res->digits=decShiftToMost(res->lsu, 1, shift); + res->exponent=-shift; /* make 1.0000... */ + *status|=DEC_Inexact | DEC_Rounded; /* .. inexactly */ + break;} /* tiny */ + + /* set up the context to be used for calculating a, as this is */ + /* used on both paths below */ + uprv_decContextDefault(&aset, DEC_INIT_DECIMAL64); + /* accumulator bounds are as requested (could underflow) */ + aset.emax=set->emax; /* usual bounds */ + aset.emin=set->emin; /* .. */ + aset.clamp=0; /* and no concrete format */ + + /* calculate the adjusted (Hull & Abrham) exponent (where the */ + /* decimal point is just to the left of the coefficient msd) */ + h=rhs->exponent+rhs->digits; + /* if h>8 then 10**h cannot be calculated safely; however, when */ + /* h=8 then exp(|rhs|) will be at least exp(1E+7) which is at */ + /* least 6.59E+4342944, so (due to the restriction on Emax/Emin) */ + /* overflow (or underflow to 0) is guaranteed -- so this case can */ + /* be handled by simply forcing the appropriate excess */ + if (h>8) { /* overflow/underflow */ + /* set up here so Power call below will over or underflow to */ + /* zero; set accumulator to either 2 or 0.02 */ + /* [stack buffer for a is always big enough for this] */ + uprv_decNumberZero(a); + *a->lsu=2; /* not 1 but < exp(1) */ + if (decNumberIsNegative(rhs)) a->exponent=-2; /* make 0.02 */ + h=8; /* clamp so 10**h computable */ + p=9; /* set a working precision */ + } + else { /* h<=8 */ + Int maxlever=(rhs->digits>8?1:0); + /* [could/should increase this for precisions >40 or so, too] */ + + /* if h is 8, cannot normalize to a lower upper limit because */ + /* the final result will not be computable (see notes above), */ + /* but leverage can be applied whenever h is less than 8. */ + /* Apply as much as possible, up to a MAXLEVER digits, which */ + /* sets the tradeoff against the cost of the later a**(10**h). */ + /* As h is increased, the working precision below also */ + /* increases to compensate for the "constant digits at the */ + /* front" effect. */ + Int lever=MINI(8-h, maxlever); /* leverage attainable */ + Int use=-rhs->digits-lever; /* exponent to use for RHS */ + h+=lever; /* apply leverage selected */ + if (h<0) { /* clamp */ + use+=h; /* [may end up subnormal] */ + h=0; + } + /* Take a copy of RHS if it needs normalization (true whenever x>=1) */ + if (rhs->exponent!=use) { + decNumber *newrhs=bufr; /* assume will fit on stack */ + needbytes=sizeof(decNumber)+(D2U(rhs->digits)-1)*sizeof(Unit); + if (needbytes>sizeof(bufr)) { /* need malloc space */ + allocrhs=(decNumber *)malloc(needbytes); + if (allocrhs==nullptr) { /* hopeless -- abandon */ + *status|=DEC_Insufficient_storage; + break;} + newrhs=allocrhs; /* use the allocated space */ + } + uprv_decNumberCopy(newrhs, rhs); /* copy to safe space */ + newrhs->exponent=use; /* normalize; now <1 */ + x=newrhs; /* ready for use */ + /* decNumberShow(x); */ + } + + /* Now use the usual power series to evaluate exp(x). The */ + /* series starts as 1 + x + x^2/2 ... so prime ready for the */ + /* third term by setting the term variable t=x, the accumulator */ + /* a=1, and the divisor d=2. */ + + /* First determine the working precision. From Hull & Abrham */ + /* this is set->digits+h+2. However, if x is 'over-precise' we */ + /* need to allow for all its digits to potentially participate */ + /* (consider an x where all the excess digits are 9s) so in */ + /* this case use x->digits+h+2 */ + p=MAXI(x->digits, set->digits)+h+2; /* [h<=8] */ + + /* a and t are variable precision, and depend on p, so space */ + /* must be allocated for them if necessary */ + + /* the accumulator needs to be able to hold 2p digits so that */ + /* the additions on the second and subsequent iterations are */ + /* sufficiently exact. */ + needbytes=sizeof(decNumber)+(D2U(p*2)-1)*sizeof(Unit); + if (needbytes>sizeof(bufa)) { /* need malloc space */ + allocbufa=(decNumber *)malloc(needbytes); + if (allocbufa==nullptr) { /* hopeless -- abandon */ + *status|=DEC_Insufficient_storage; + break;} + a=allocbufa; /* use the allocated space */ + } + /* the term needs to be able to hold p digits (which is */ + /* guaranteed to be larger than x->digits, so the initial copy */ + /* is safe); it may also be used for the raise-to-power */ + /* calculation below, which needs an extra two digits */ + needbytes=sizeof(decNumber)+(D2U(p+2)-1)*sizeof(Unit); + if (needbytes>sizeof(buft)) { /* need malloc space */ + allocbuft=(decNumber *)malloc(needbytes); + if (allocbuft==nullptr) { /* hopeless -- abandon */ + *status|=DEC_Insufficient_storage; + break;} + t=allocbuft; /* use the allocated space */ + } + + uprv_decNumberCopy(t, x); /* term=x */ + uprv_decNumberZero(a); *a->lsu=1; /* accumulator=1 */ + uprv_decNumberZero(d); *d->lsu=2; /* divisor=2 */ + uprv_decNumberZero(&numone); *numone.lsu=1; /* constant 1 for increment */ + + /* set up the contexts for calculating a, t, and d */ + uprv_decContextDefault(&tset, DEC_INIT_DECIMAL64); + dset=tset; + /* accumulator bounds are set above, set precision now */ + aset.digits=p*2; /* double */ + /* term bounds avoid any underflow or overflow */ + tset.digits=p; + tset.emin=DEC_MIN_EMIN; /* [emax is plenty] */ + /* [dset.digits=16, etc., are sufficient] */ + + /* finally ready to roll */ + for (;;) { + #if DECCHECK + iterations++; + #endif + /* only the status from the accumulation is interesting */ + /* [but it should remain unchanged after first add] */ + decAddOp(a, a, t, &aset, 0, status); /* a=a+t */ + decMultiplyOp(t, t, x, &tset, &ignore); /* t=t*x */ + decDivideOp(t, t, d, &tset, DIVIDE, &ignore); /* t=t/d */ + /* the iteration ends when the term cannot affect the result, */ + /* if rounded to p digits, which is when its value is smaller */ + /* than the accumulator by p+1 digits. There must also be */ + /* full precision in a. */ + if (((a->digits+a->exponent)>=(t->digits+t->exponent+p+1)) + && (a->digits>=p)) break; + decAddOp(d, d, &numone, &dset, 0, &ignore); /* d=d+1 */ + } /* iterate */ + + #if DECCHECK + /* just a sanity check; comment out test to show always */ + if (iterations>p+3) + printf("Exp iterations=%ld, status=%08lx, p=%ld, d=%ld\n", + (LI)iterations, (LI)*status, (LI)p, (LI)x->digits); + #endif + } /* h<=8 */ + + /* apply postconditioning: a=a**(10**h) -- this is calculated */ + /* at a slightly higher precision than Hull & Abrham suggest */ + if (h>0) { + Int seenbit=0; /* set once a 1-bit is seen */ + Int i; /* counter */ + Int n=powers[h]; /* always positive */ + aset.digits=p+2; /* sufficient precision */ + /* avoid the overhead and many extra digits of decNumberPower */ + /* as all that is needed is the short 'multipliers' loop; here */ + /* accumulate the answer into t */ + uprv_decNumberZero(t); *t->lsu=1; /* acc=1 */ + for (i=1;;i++){ /* for each bit [top bit ignored] */ + /* abandon if have had overflow or terminal underflow */ + if (*status & (DEC_Overflow|DEC_Underflow)) { /* interesting? */ + if (*status&DEC_Overflow || ISZERO(t)) break;} + n=n<<1; /* move next bit to testable position */ + if (n<0) { /* top bit is set */ + seenbit=1; /* OK, have a significant bit */ + decMultiplyOp(t, t, a, &aset, status); /* acc=acc*x */ + } + if (i==31) break; /* that was the last bit */ + if (!seenbit) continue; /* no need to square 1 */ + decMultiplyOp(t, t, t, &aset, status); /* acc=acc*acc [square] */ + } /*i*/ /* 32 bits */ + /* decNumberShow(t); */ + a=t; /* and carry on using t instead of a */ + } + + /* Copy and round the result to res */ + residue=1; /* indicate dirt to right .. */ + if (ISZERO(a)) residue=0; /* .. unless underflowed to 0 */ + aset.digits=set->digits; /* [use default rounding] */ + decCopyFit(res, a, &aset, &residue, status); /* copy & shorten */ + decFinish(res, set, &residue, status); /* cleanup/set flags */ + } while(0); /* end protected */ + + if (allocrhs !=nullptr) free(allocrhs); /* drop any storage used */ + if (allocbufa!=nullptr) free(allocbufa); /* .. */ + if (allocbuft!=nullptr) free(allocbuft); /* .. */ + /* [status is handled by caller] */ + return res; + } /* decExpOp */ + +/* ------------------------------------------------------------------ */ +/* Initial-estimate natural logarithm table */ +/* */ +/* LNnn -- 90-entry 16-bit table for values from .10 through .99. */ +/* The result is a 4-digit encode of the coefficient (c=the */ +/* top 14 bits encoding 0-9999) and a 2-digit encode of the */ +/* exponent (e=the bottom 2 bits encoding 0-3) */ +/* */ +/* The resulting value is given by: */ +/* */ +/* v = -c * 10**(-e-3) */ +/* */ +/* where e and c are extracted from entry k = LNnn[x-10] */ +/* where x is truncated (NB) into the range 10 through 99, */ +/* and then c = k>>2 and e = k&3. */ +/* ------------------------------------------------------------------ */ +static const uShort LNnn[90]={9016, 8652, 8316, 8008, 7724, 7456, 7208, + 6972, 6748, 6540, 6340, 6148, 5968, 5792, 5628, 5464, 5312, + 5164, 5020, 4884, 4748, 4620, 4496, 4376, 4256, 4144, 4032, + 39233, 38181, 37157, 36157, 35181, 34229, 33297, 32389, 31501, 30629, + 29777, 28945, 28129, 27329, 26545, 25777, 25021, 24281, 23553, 22837, + 22137, 21445, 20769, 20101, 19445, 18801, 18165, 17541, 16925, 16321, + 15721, 15133, 14553, 13985, 13421, 12865, 12317, 11777, 11241, 10717, + 10197, 9685, 9177, 8677, 8185, 7697, 7213, 6737, 6269, 5801, + 5341, 4889, 4437, 39930, 35534, 31186, 26886, 22630, 18418, 14254, + 10130, 6046, 20055}; + +/* ------------------------------------------------------------------ */ +/* decLnOp -- effect natural logarithm */ +/* */ +/* This computes C = ln(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context; note that rounding mode has no effect */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Notable cases: */ +/* A<0 -> Invalid */ +/* A=0 -> -Infinity (Exact) */ +/* A=+Infinity -> +Infinity (Exact) */ +/* A=1 exactly -> 0 (Exact) */ +/* */ +/* Restrictions (as for Exp): */ +/* */ +/* digits, emax, and -emin in the context must be less than */ +/* DEC_MAX_MATH+11 (1000010), and the rhs must be within these */ +/* bounds or a zero. This is an internal routine, so these */ +/* restrictions are contractual and not enforced. */ +/* */ +/* A finite result is rounded using DEC_ROUND_HALF_EVEN; it will */ +/* almost always be correctly rounded, but may be up to 1 ulp in */ +/* error in rare cases. */ +/* ------------------------------------------------------------------ */ +/* The result is calculated using Newton's method, with each */ +/* iteration calculating a' = a + x * exp(-a) - 1. See, for example, */ +/* Epperson 1989. */ +/* */ +/* The iteration ends when the adjustment x*exp(-a)-1 is tiny enough. */ +/* This has to be calculated at the sum of the precision of x and the */ +/* working precision. */ +/* */ +/* Implementation notes: */ +/* */ +/* 1. This is separated out as decLnOp so it can be called from */ +/* other Mathematical functions (e.g., Log 10) with a wider range */ +/* than normal. In particular, it can handle the slightly wider */ +/* (+9+2) range needed by a power function. */ +/* */ +/* 2. The speed of this function is about 10x slower than exp, as */ +/* it typically needs 4-6 iterations for short numbers, and the */ +/* extra precision needed adds a squaring effect, twice. */ +/* */ +/* 3. Fastpaths are included for ln(10) and ln(2), up to length 40, */ +/* as these are common requests. ln(10) is used by log10(x). */ +/* */ +/* 4. An iteration might be saved by widening the LNnn table, and */ +/* would certainly save at least one if it were made ten times */ +/* bigger, too (for truncated fractions 0.100 through 0.999). */ +/* However, for most practical evaluations, at least four or five */ +/* iterations will be needed -- so this would only speed up by */ +/* 20-25% and that probably does not justify increasing the table */ +/* size. */ +/* */ +/* 5. The static buffers are larger than might be expected to allow */ +/* for calls from decNumberPower. */ +/* ------------------------------------------------------------------ */ +#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 406 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif +decNumber * decLnOp(decNumber *res, const decNumber *rhs, + decContext *set, uInt *status) { + uInt ignore=0; /* working status accumulator */ + uInt needbytes; /* for space calculations */ + Int residue; /* rounding residue */ + Int r; /* rhs=f*10**r [see below] */ + Int p; /* working precision */ + Int pp; /* precision for iteration */ + Int t; /* work */ + + /* buffers for a (accumulator, typically precision+2) and b */ + /* (adjustment calculator, same size) */ + decNumber bufa[D2N(DECBUFFER+12)]; + decNumber *allocbufa=nullptr; /* -> allocated bufa, iff allocated */ + decNumber *a=bufa; /* accumulator/work */ + decNumber bufb[D2N(DECBUFFER*2+2)]; + decNumber *allocbufb=nullptr; /* -> allocated bufa, iff allocated */ + decNumber *b=bufb; /* adjustment/work */ + + decNumber numone; /* constant 1 */ + decNumber cmp; /* work */ + decContext aset, bset; /* working contexts */ + + #if DECCHECK + Int iterations=0; /* for later sanity check */ + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + do { /* protect allocated storage */ + if (SPECIALARG) { /* handle infinities and NaNs */ + if (decNumberIsInfinite(rhs)) { /* an infinity */ + if (decNumberIsNegative(rhs)) /* -Infinity -> error */ + *status|=DEC_Invalid_operation; + else uprv_decNumberCopy(res, rhs); /* +Infinity -> self */ + } + else decNaNs(res, rhs, nullptr, set, status); /* a NaN */ + break;} + + if (ISZERO(rhs)) { /* +/- zeros -> -Infinity */ + uprv_decNumberZero(res); /* make clean */ + res->bits=DECINF|DECNEG; /* set - infinity */ + break;} /* [no status to set] */ + + /* Non-zero negatives are bad... */ + if (decNumberIsNegative(rhs)) { /* -x -> error */ + *status|=DEC_Invalid_operation; + break;} + + /* Here, rhs is positive, finite, and in range */ + + /* lookaside fastpath code for ln(2) and ln(10) at common lengths */ + if (rhs->exponent==0 && set->digits<=40) { + #if DECDPUN==1 + if (rhs->lsu[0]==0 && rhs->lsu[1]==1 && rhs->digits==2) { /* ln(10) */ + #else + if (rhs->lsu[0]==10 && rhs->digits==2) { /* ln(10) */ + #endif + aset=*set; aset.round=DEC_ROUND_HALF_EVEN; + #define LN10 "2.302585092994045684017991454684364207601" + uprv_decNumberFromString(res, LN10, &aset); + *status|=(DEC_Inexact | DEC_Rounded); /* is inexact */ + break;} + if (rhs->lsu[0]==2 && rhs->digits==1) { /* ln(2) */ + aset=*set; aset.round=DEC_ROUND_HALF_EVEN; + #define LN2 "0.6931471805599453094172321214581765680755" + uprv_decNumberFromString(res, LN2, &aset); + *status|=(DEC_Inexact | DEC_Rounded); + break;} + } /* integer and short */ + + /* Determine the working precision. This is normally the */ + /* requested precision + 2, with a minimum of 9. However, if */ + /* the rhs is 'over-precise' then allow for all its digits to */ + /* potentially participate (consider an rhs where all the excess */ + /* digits are 9s) so in this case use rhs->digits+2. */ + p=MAXI(rhs->digits, MAXI(set->digits, 7))+2; + + /* Allocate space for the accumulator and the high-precision */ + /* adjustment calculator, if necessary. The accumulator must */ + /* be able to hold p digits, and the adjustment up to */ + /* rhs->digits+p digits. They are also made big enough for 16 */ + /* digits so that they can be used for calculating the initial */ + /* estimate. */ + needbytes=sizeof(decNumber)+(D2U(MAXI(p,16))-1)*sizeof(Unit); + if (needbytes>sizeof(bufa)) { /* need malloc space */ + allocbufa=(decNumber *)malloc(needbytes); + if (allocbufa==nullptr) { /* hopeless -- abandon */ + *status|=DEC_Insufficient_storage; + break;} + a=allocbufa; /* use the allocated space */ + } + pp=p+rhs->digits; + needbytes=sizeof(decNumber)+(D2U(MAXI(pp,16))-1)*sizeof(Unit); + if (needbytes>sizeof(bufb)) { /* need malloc space */ + allocbufb=(decNumber *)malloc(needbytes); + if (allocbufb==nullptr) { /* hopeless -- abandon */ + *status|=DEC_Insufficient_storage; + break;} + b=allocbufb; /* use the allocated space */ + } + + /* Prepare an initial estimate in acc. Calculate this by */ + /* considering the coefficient of x to be a normalized fraction, */ + /* f, with the decimal point at far left and multiplied by */ + /* 10**r. Then, rhs=f*10**r and 0.1<=f<1, and */ + /* ln(x) = ln(f) + ln(10)*r */ + /* Get the initial estimate for ln(f) from a small lookup */ + /* table (see above) indexed by the first two digits of f, */ + /* truncated. */ + + uprv_decContextDefault(&aset, DEC_INIT_DECIMAL64); /* 16-digit extended */ + r=rhs->exponent+rhs->digits; /* 'normalised' exponent */ + uprv_decNumberFromInt32(a, r); /* a=r */ + uprv_decNumberFromInt32(b, 2302585); /* b=ln(10) (2.302585) */ + b->exponent=-6; /* .. */ + decMultiplyOp(a, a, b, &aset, &ignore); /* a=a*b */ + /* now get top two digits of rhs into b by simple truncate and */ + /* force to integer */ + residue=0; /* (no residue) */ + aset.digits=2; aset.round=DEC_ROUND_DOWN; + decCopyFit(b, rhs, &aset, &residue, &ignore); /* copy & shorten */ + b->exponent=0; /* make integer */ + t=decGetInt(b); /* [cannot fail] */ + if (t<10) t=X10(t); /* adjust single-digit b */ + t=LNnn[t-10]; /* look up ln(b) */ + uprv_decNumberFromInt32(b, t>>2); /* b=ln(b) coefficient */ + b->exponent=-(t&3)-3; /* set exponent */ + b->bits=DECNEG; /* ln(0.10)->ln(0.99) always -ve */ + aset.digits=16; aset.round=DEC_ROUND_HALF_EVEN; /* restore */ + decAddOp(a, a, b, &aset, 0, &ignore); /* acc=a+b */ + /* the initial estimate is now in a, with up to 4 digits correct. */ + /* When rhs is at or near Nmax the estimate will be low, so we */ + /* will approach it from below, avoiding overflow when calling exp. */ + + uprv_decNumberZero(&numone); *numone.lsu=1; /* constant 1 for adjustment */ + + /* accumulator bounds are as requested (could underflow, but */ + /* cannot overflow) */ + aset.emax=set->emax; + aset.emin=set->emin; + aset.clamp=0; /* no concrete format */ + /* set up a context to be used for the multiply and subtract */ + bset=aset; + bset.emax=DEC_MAX_MATH*2; /* use double bounds for the */ + bset.emin=-DEC_MAX_MATH*2; /* adjustment calculation */ + /* [see decExpOp call below] */ + /* for each iteration double the number of digits to calculate, */ + /* up to a maximum of p */ + pp=9; /* initial precision */ + /* [initially 9 as then the sequence starts 7+2, 16+2, and */ + /* 34+2, which is ideal for standard-sized numbers] */ + aset.digits=pp; /* working context */ + bset.digits=pp+rhs->digits; /* wider context */ + for (;;) { /* iterate */ + #if DECCHECK + iterations++; + if (iterations>24) break; /* consider 9 * 2**24 */ + #endif + /* calculate the adjustment (exp(-a)*x-1) into b. This is a */ + /* catastrophic subtraction but it really is the difference */ + /* from 1 that is of interest. */ + /* Use the internal entry point to Exp as it allows the double */ + /* range for calculating exp(-a) when a is the tiniest subnormal. */ + a->bits^=DECNEG; /* make -a */ + decExpOp(b, a, &bset, &ignore); /* b=exp(-a) */ + a->bits^=DECNEG; /* restore sign of a */ + /* now multiply by rhs and subtract 1, at the wider precision */ + decMultiplyOp(b, b, rhs, &bset, &ignore); /* b=b*rhs */ + decAddOp(b, b, &numone, &bset, DECNEG, &ignore); /* b=b-1 */ + + /* the iteration ends when the adjustment cannot affect the */ + /* result by >=0.5 ulp (at the requested digits), which */ + /* is when its value is smaller than the accumulator by */ + /* set->digits+1 digits (or it is zero) -- this is a looser */ + /* requirement than for Exp because all that happens to the */ + /* accumulator after this is the final rounding (but note that */ + /* there must also be full precision in a, or a=0). */ + + if (decNumberIsZero(b) || + (a->digits+a->exponent)>=(b->digits+b->exponent+set->digits+1)) { + if (a->digits==p) break; + if (decNumberIsZero(a)) { + decCompareOp(&cmp, rhs, &numone, &aset, COMPARE, &ignore); /* rhs=1 ? */ + if (cmp.lsu[0]==0) a->exponent=0; /* yes, exact 0 */ + else *status|=(DEC_Inexact | DEC_Rounded); /* no, inexact */ + break; + } + /* force padding if adjustment has gone to 0 before full length */ + if (decNumberIsZero(b)) b->exponent=a->exponent-p; + } + + /* not done yet ... */ + decAddOp(a, a, b, &aset, 0, &ignore); /* a=a+b for next estimate */ + if (pp==p) continue; /* precision is at maximum */ + /* lengthen the next calculation */ + pp=pp*2; /* double precision */ + if (pp>p) pp=p; /* clamp to maximum */ + aset.digits=pp; /* working context */ + bset.digits=pp+rhs->digits; /* wider context */ + } /* Newton's iteration */ + + #if DECCHECK + /* just a sanity check; remove the test to show always */ + if (iterations>24) + printf("Ln iterations=%ld, status=%08lx, p=%ld, d=%ld\n", + (LI)iterations, (LI)*status, (LI)p, (LI)rhs->digits); + #endif + + /* Copy and round the result to res */ + residue=1; /* indicate dirt to right */ + if (ISZERO(a)) residue=0; /* .. unless underflowed to 0 */ + aset.digits=set->digits; /* [use default rounding] */ + decCopyFit(res, a, &aset, &residue, status); /* copy & shorten */ + decFinish(res, set, &residue, status); /* cleanup/set flags */ + } while(0); /* end protected */ + + if (allocbufa!=nullptr) free(allocbufa); /* drop any storage used */ + if (allocbufb!=nullptr) free(allocbufb); /* .. */ + /* [status is handled by caller] */ + return res; + } /* decLnOp */ +#if defined(__clang__) || U_GCC_MAJOR_MINOR >= 406 +#pragma GCC diagnostic pop +#endif + +/* ------------------------------------------------------------------ */ +/* decQuantizeOp -- force exponent to requested value */ +/* */ +/* This computes C = op(A, B), where op adjusts the coefficient */ +/* of C (by rounding or shifting) such that the exponent (-scale) */ +/* of C has the value B or matches the exponent of B. */ +/* The numerical value of C will equal A, except for the effects of */ +/* any rounding that occurred. */ +/* */ +/* res is C, the result. C may be A or B */ +/* lhs is A, the number to adjust */ +/* rhs is B, the requested exponent */ +/* set is the context */ +/* quant is 1 for quantize or 0 for rescale */ +/* status is the status accumulator (this can be called without */ +/* risk of control loss) */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Unless there is an error or the result is infinite, the exponent */ +/* after the operation is guaranteed to be that requested. */ +/* ------------------------------------------------------------------ */ +static decNumber * decQuantizeOp(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set, + Flag quant, uInt *status) { + #if DECSUBSET + decNumber *alloclhs=nullptr; /* non-nullptr if rounded lhs allocated */ + decNumber *allocrhs=nullptr; /* .., rhs */ + #endif + const decNumber *inrhs=rhs; /* save original rhs */ + Int reqdigits=set->digits; /* requested DIGITS */ + Int reqexp; /* requested exponent [-scale] */ + Int residue=0; /* rounding residue */ + Int etiny=set->emin-(reqdigits-1); + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + do { /* protect allocated storage */ + #if DECSUBSET + if (!set->extended) { + /* reduce operands and set lostDigits status, as needed */ + if (lhs->digits>reqdigits) { + alloclhs=decRoundOperand(lhs, set, status); + if (alloclhs==nullptr) break; + lhs=alloclhs; + } + if (rhs->digits>reqdigits) { /* [this only checks lostDigits] */ + allocrhs=decRoundOperand(rhs, set, status); + if (allocrhs==nullptr) break; + rhs=allocrhs; + } + } + #endif + /* [following code does not require input rounding] */ + + /* Handle special values */ + if (SPECIALARGS) { + /* NaNs get usual processing */ + if (SPECIALARGS & (DECSNAN | DECNAN)) + decNaNs(res, lhs, rhs, set, status); + /* one infinity but not both is bad */ + else if ((lhs->bits ^ rhs->bits) & DECINF) + *status|=DEC_Invalid_operation; + /* both infinity: return lhs */ + else uprv_decNumberCopy(res, lhs); /* [nop if in place] */ + break; + } + + /* set requested exponent */ + if (quant) reqexp=inrhs->exponent; /* quantize -- match exponents */ + else { /* rescale -- use value of rhs */ + /* Original rhs must be an integer that fits and is in range, */ + /* which could be from -1999999997 to +999999999, thanks to */ + /* subnormals */ + reqexp=decGetInt(inrhs); /* [cannot fail] */ + } + + #if DECSUBSET + if (!set->extended) etiny=set->emin; /* no subnormals */ + #endif + + if (reqexp==BADINT /* bad (rescale only) or .. */ + || reqexp==BIGODD || reqexp==BIGEVEN /* very big (ditto) or .. */ + || (reqexpset->emax)) { /* > emax */ + *status|=DEC_Invalid_operation; + break;} + + /* the RHS has been processed, so it can be overwritten now if necessary */ + if (ISZERO(lhs)) { /* zero coefficient unchanged */ + uprv_decNumberCopy(res, lhs); /* [nop if in place] */ + res->exponent=reqexp; /* .. just set exponent */ + #if DECSUBSET + if (!set->extended) res->bits=0; /* subset specification; no -0 */ + #endif + } + else { /* non-zero lhs */ + Int adjust=reqexp-lhs->exponent; /* digit adjustment needed */ + /* if adjusted coefficient will definitely not fit, give up now */ + if ((lhs->digits-adjust)>reqdigits) { + *status|=DEC_Invalid_operation; + break; + } + + if (adjust>0) { /* increasing exponent */ + /* this will decrease the length of the coefficient by adjust */ + /* digits, and must round as it does so */ + decContext workset; /* work */ + workset=*set; /* clone rounding, etc. */ + workset.digits=lhs->digits-adjust; /* set requested length */ + /* [note that the latter can be <1, here] */ + decCopyFit(res, lhs, &workset, &residue, status); /* fit to result */ + decApplyRound(res, &workset, residue, status); /* .. and round */ + residue=0; /* [used] */ + /* If just rounded a 999s case, exponent will be off by one; */ + /* adjust back (after checking space), if so. */ + if (res->exponent>reqexp) { + /* re-check needed, e.g., for quantize(0.9999, 0.001) under */ + /* set->digits==3 */ + if (res->digits==reqdigits) { /* cannot shift by 1 */ + *status&=~(DEC_Inexact | DEC_Rounded); /* [clean these] */ + *status|=DEC_Invalid_operation; + break; + } + res->digits=decShiftToMost(res->lsu, res->digits, 1); /* shift */ + res->exponent--; /* (re)adjust the exponent. */ + } + #if DECSUBSET + if (ISZERO(res) && !set->extended) res->bits=0; /* subset; no -0 */ + #endif + } /* increase */ + else /* adjust<=0 */ { /* decreasing or = exponent */ + /* this will increase the length of the coefficient by -adjust */ + /* digits, by adding zero or more trailing zeros; this is */ + /* already checked for fit, above */ + uprv_decNumberCopy(res, lhs); /* [it will fit] */ + /* if padding needed (adjust<0), add it now... */ + if (adjust<0) { + res->digits=decShiftToMost(res->lsu, res->digits, -adjust); + res->exponent+=adjust; /* adjust the exponent */ + } + } /* decrease */ + } /* non-zero */ + + /* Check for overflow [do not use Finalize in this case, as an */ + /* overflow here is a "don't fit" situation] */ + if (res->exponent>set->emax-res->digits+1) { /* too big */ + *status|=DEC_Invalid_operation; + break; + } + else { + decFinalize(res, set, &residue, status); /* set subnormal flags */ + *status&=~DEC_Underflow; /* suppress Underflow [as per 754] */ + } + } while(0); /* end protected */ + + #if DECSUBSET + if (allocrhs!=nullptr) free(allocrhs); /* drop any storage used */ + if (alloclhs!=nullptr) free(alloclhs); /* .. */ + #endif + return res; + } /* decQuantizeOp */ + +/* ------------------------------------------------------------------ */ +/* decCompareOp -- compare, min, or max two Numbers */ +/* */ +/* This computes C = A ? B and carries out one of four operations: */ +/* COMPARE -- returns the signum (as a number) giving the */ +/* result of a comparison unless one or both */ +/* operands is a NaN (in which case a NaN results) */ +/* COMPSIG -- as COMPARE except that a quiet NaN raises */ +/* Invalid operation. */ +/* COMPMAX -- returns the larger of the operands, using the */ +/* 754 maxnum operation */ +/* COMPMAXMAG -- ditto, comparing absolute values */ +/* COMPMIN -- the 754 minnum operation */ +/* COMPMINMAG -- ditto, comparing absolute values */ +/* COMTOTAL -- returns the signum (as a number) giving the */ +/* result of a comparison using 754 total ordering */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* op is the operation flag */ +/* status is the usual accumulator */ +/* */ +/* C must have space for one digit for COMPARE or set->digits for */ +/* COMPMAX, COMPMIN, COMPMAXMAG, or COMPMINMAG. */ +/* ------------------------------------------------------------------ */ +/* The emphasis here is on speed for common cases, and avoiding */ +/* coefficient comparison if possible. */ +/* ------------------------------------------------------------------ */ +static decNumber * decCompareOp(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set, + Flag op, uInt *status) { + #if DECSUBSET + decNumber *alloclhs=nullptr; /* non-nullptr if rounded lhs allocated */ + decNumber *allocrhs=nullptr; /* .., rhs */ + #endif + Int result=0; /* default result value */ + uByte merged; /* work */ + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + do { /* protect allocated storage */ + #if DECSUBSET + if (!set->extended) { + /* reduce operands and set lostDigits status, as needed */ + if (lhs->digits>set->digits) { + alloclhs=decRoundOperand(lhs, set, status); + if (alloclhs==nullptr) {result=BADINT; break;} + lhs=alloclhs; + } + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, status); + if (allocrhs==nullptr) {result=BADINT; break;} + rhs=allocrhs; + } + } + #endif + /* [following code does not require input rounding] */ + + /* If total ordering then handle differing signs 'up front' */ + if (op==COMPTOTAL) { /* total ordering */ + if (decNumberIsNegative(lhs) && !decNumberIsNegative(rhs)) { + result=-1; + break; + } + if (!decNumberIsNegative(lhs) && decNumberIsNegative(rhs)) { + result=+1; + break; + } + } + + /* handle NaNs specially; let infinities drop through */ + /* This assumes sNaN (even just one) leads to NaN. */ + merged=(lhs->bits | rhs->bits) & (DECSNAN | DECNAN); + if (merged) { /* a NaN bit set */ + if (op==COMPARE); /* result will be NaN */ + else if (op==COMPSIG) /* treat qNaN as sNaN */ + *status|=DEC_Invalid_operation | DEC_sNaN; + else if (op==COMPTOTAL) { /* total ordering, always finite */ + /* signs are known to be the same; compute the ordering here */ + /* as if the signs are both positive, then invert for negatives */ + if (!decNumberIsNaN(lhs)) result=-1; + else if (!decNumberIsNaN(rhs)) result=+1; + /* here if both NaNs */ + else if (decNumberIsSNaN(lhs) && decNumberIsQNaN(rhs)) result=-1; + else if (decNumberIsQNaN(lhs) && decNumberIsSNaN(rhs)) result=+1; + else { /* both NaN or both sNaN */ + /* now it just depends on the payload */ + result=decUnitCompare(lhs->lsu, D2U(lhs->digits), + rhs->lsu, D2U(rhs->digits), 0); + /* [Error not possible, as these are 'aligned'] */ + } /* both same NaNs */ + if (decNumberIsNegative(lhs)) result=-result; + break; + } /* total order */ + + else if (merged & DECSNAN); /* sNaN -> qNaN */ + else { /* here if MIN or MAX and one or two quiet NaNs */ + /* min or max -- 754 rules ignore single NaN */ + if (!decNumberIsNaN(lhs) || !decNumberIsNaN(rhs)) { + /* just one NaN; force choice to be the non-NaN operand */ + op=COMPMAX; + if (lhs->bits & DECNAN) result=-1; /* pick rhs */ + else result=+1; /* pick lhs */ + break; + } + } /* max or min */ + op=COMPNAN; /* use special path */ + decNaNs(res, lhs, rhs, set, status); /* propagate NaN */ + break; + } + /* have numbers */ + if (op==COMPMAXMAG || op==COMPMINMAG) result=decCompare(lhs, rhs, 1); + else result=decCompare(lhs, rhs, 0); /* sign matters */ + } while(0); /* end protected */ + + if (result==BADINT) *status|=DEC_Insufficient_storage; /* rare */ + else { + if (op==COMPARE || op==COMPSIG ||op==COMPTOTAL) { /* returning signum */ + if (op==COMPTOTAL && result==0) { + /* operands are numerically equal or same NaN (and same sign, */ + /* tested first); if identical, leave result 0 */ + if (lhs->exponent!=rhs->exponent) { + if (lhs->exponentexponent) result=-1; + else result=+1; + if (decNumberIsNegative(lhs)) result=-result; + } /* lexp!=rexp */ + } /* total-order by exponent */ + uprv_decNumberZero(res); /* [always a valid result] */ + if (result!=0) { /* must be -1 or +1 */ + *res->lsu=1; + if (result<0) res->bits=DECNEG; + } + } + else if (op==COMPNAN); /* special, drop through */ + else { /* MAX or MIN, non-NaN result */ + Int residue=0; /* rounding accumulator */ + /* choose the operand for the result */ + const decNumber *choice; + if (result==0) { /* operands are numerically equal */ + /* choose according to sign then exponent (see 754) */ + uByte slhs=(lhs->bits & DECNEG); + uByte srhs=(rhs->bits & DECNEG); + #if DECSUBSET + if (!set->extended) { /* subset: force left-hand */ + op=COMPMAX; + result=+1; + } + else + #endif + if (slhs!=srhs) { /* signs differ */ + if (slhs) result=-1; /* rhs is max */ + else result=+1; /* lhs is max */ + } + else if (slhs && srhs) { /* both negative */ + if (lhs->exponentexponent) result=+1; + else result=-1; + /* [if equal, use lhs, technically identical] */ + } + else { /* both positive */ + if (lhs->exponent>rhs->exponent) result=+1; + else result=-1; + /* [ditto] */ + } + } /* numerically equal */ + /* here result will be non-0; reverse if looking for MIN */ + if (op==COMPMIN || op==COMPMINMAG) result=-result; + choice=(result>0 ? lhs : rhs); /* choose */ + /* copy chosen to result, rounding if need be */ + decCopyFit(res, choice, set, &residue, status); + decFinish(res, set, &residue, status); + } + } + #if DECSUBSET + if (allocrhs!=nullptr) free(allocrhs); /* free any storage used */ + if (alloclhs!=nullptr) free(alloclhs); /* .. */ + #endif + return res; + } /* decCompareOp */ + +/* ------------------------------------------------------------------ */ +/* decCompare -- compare two decNumbers by numerical value */ +/* */ +/* This routine compares A ? B without altering them. */ +/* */ +/* Arg1 is A, a decNumber which is not a NaN */ +/* Arg2 is B, a decNumber which is not a NaN */ +/* Arg3 is 1 for a sign-independent compare, 0 otherwise */ +/* */ +/* returns -1, 0, or 1 for AB, or BADINT if failure */ +/* (the only possible failure is an allocation error) */ +/* ------------------------------------------------------------------ */ +static Int decCompare(const decNumber *lhs, const decNumber *rhs, + Flag abs_c) { + Int result; /* result value */ + Int sigr; /* rhs signum */ + Int compare; /* work */ + + result=1; /* assume signum(lhs) */ + if (ISZERO(lhs)) result=0; + if (abs_c) { + if (ISZERO(rhs)) return result; /* LHS wins or both 0 */ + /* RHS is non-zero */ + if (result==0) return -1; /* LHS is 0; RHS wins */ + /* [here, both non-zero, result=1] */ + } + else { /* signs matter */ + if (result && decNumberIsNegative(lhs)) result=-1; + sigr=1; /* compute signum(rhs) */ + if (ISZERO(rhs)) sigr=0; + else if (decNumberIsNegative(rhs)) sigr=-1; + if (result > sigr) return +1; /* L > R, return 1 */ + if (result < sigr) return -1; /* L < R, return -1 */ + if (result==0) return 0; /* both 0 */ + } + + /* signums are the same; both are non-zero */ + if ((lhs->bits | rhs->bits) & DECINF) { /* one or more infinities */ + if (decNumberIsInfinite(rhs)) { + if (decNumberIsInfinite(lhs)) result=0;/* both infinite */ + else result=-result; /* only rhs infinite */ + } + return result; + } + /* must compare the coefficients, allowing for exponents */ + if (lhs->exponent>rhs->exponent) { /* LHS exponent larger */ + /* swap sides, and sign */ + const decNumber *temp=lhs; + lhs=rhs; + rhs=temp; + result=-result; + } + compare=decUnitCompare(lhs->lsu, D2U(lhs->digits), + rhs->lsu, D2U(rhs->digits), + rhs->exponent-lhs->exponent); + if (compare!=BADINT) compare*=result; /* comparison succeeded */ + return compare; + } /* decCompare */ + +/* ------------------------------------------------------------------ */ +/* decUnitCompare -- compare two >=0 integers in Unit arrays */ +/* */ +/* This routine compares A ? B*10**E where A and B are unit arrays */ +/* A is a plain integer */ +/* B has an exponent of E (which must be non-negative) */ +/* */ +/* Arg1 is A first Unit (lsu) */ +/* Arg2 is A length in Units */ +/* Arg3 is B first Unit (lsu) */ +/* Arg4 is B length in Units */ +/* Arg5 is E (0 if the units are aligned) */ +/* */ +/* returns -1, 0, or 1 for AB, or BADINT if failure */ +/* (the only possible failure is an allocation error, which can */ +/* only occur if E!=0) */ +/* ------------------------------------------------------------------ */ +static Int decUnitCompare(const Unit *a, Int alength, + const Unit *b, Int blength, Int exp) { + Unit *acc; /* accumulator for result */ + Unit accbuff[SD2U(DECBUFFER*2+1)]; /* local buffer */ + Unit *allocacc=nullptr; /* -> allocated acc buffer, iff allocated */ + Int accunits, need; /* units in use or needed for acc */ + const Unit *l, *r, *u; /* work */ + Int expunits, exprem, result; /* .. */ + + if (exp==0) { /* aligned; fastpath */ + if (alength>blength) return 1; + if (alength=a; l--, r--) { + if (*l>*r) return 1; + if (*l<*r) return -1; + } + return 0; /* all units match */ + } /* aligned */ + + /* Unaligned. If one is >1 unit longer than the other, padded */ + /* approximately, then can return easily */ + if (alength>blength+(Int)D2U(exp)) return 1; + if (alength+1sizeof(accbuff)) { + allocacc=(Unit *)malloc(need*sizeof(Unit)); + if (allocacc==nullptr) return BADINT; /* hopeless -- abandon */ + acc=allocacc; + } + /* Calculate units and remainder from exponent. */ + expunits=exp/DECDPUN; + exprem=exp%DECDPUN; + /* subtract [A+B*(-m)] */ + accunits=decUnitAddSub(a, alength, b, blength, expunits, acc, + -(Int)powers[exprem]); + /* [UnitAddSub result may have leading zeros, even on zero] */ + if (accunits<0) result=-1; /* negative result */ + else { /* non-negative result */ + /* check units of the result before freeing any storage */ + for (u=acc; u=0 integers in Unit arrays */ +/* */ +/* This routine performs the calculation: */ +/* */ +/* C=A+(B*M) */ +/* */ +/* Where M is in the range -DECDPUNMAX through +DECDPUNMAX. */ +/* */ +/* A may be shorter or longer than B. */ +/* */ +/* Leading zeros are not removed after a calculation. The result is */ +/* either the same length as the longer of A and B (adding any */ +/* shift), or one Unit longer than that (if a Unit carry occurred). */ +/* */ +/* A and B content are not altered unless C is also A or B. */ +/* C may be the same array as A or B, but only if no zero padding is */ +/* requested (that is, C may be B only if bshift==0). */ +/* C is filled from the lsu; only those units necessary to complete */ +/* the calculation are referenced. */ +/* */ +/* Arg1 is A first Unit (lsu) */ +/* Arg2 is A length in Units */ +/* Arg3 is B first Unit (lsu) */ +/* Arg4 is B length in Units */ +/* Arg5 is B shift in Units (>=0; pads with 0 units if positive) */ +/* Arg6 is C first Unit (lsu) */ +/* Arg7 is M, the multiplier */ +/* */ +/* returns the count of Units written to C, which will be non-zero */ +/* and negated if the result is negative. That is, the sign of the */ +/* returned Int is the sign of the result (positive for zero) and */ +/* the absolute value of the Int is the count of Units. */ +/* */ +/* It is the caller's responsibility to make sure that C size is */ +/* safe, allowing space if necessary for a one-Unit carry. */ +/* */ +/* This routine is severely performance-critical; *any* change here */ +/* must be measured (timed) to assure no performance degradation. */ +/* In particular, trickery here tends to be counter-productive, as */ +/* increased complexity of code hurts register optimizations on */ +/* register-poor architectures. Avoiding divisions is nearly */ +/* always a Good Idea, however. */ +/* */ +/* Special thanks to Rick McGuire (IBM Cambridge, MA) and Dave Clark */ +/* (IBM Warwick, UK) for some of the ideas used in this routine. */ +/* ------------------------------------------------------------------ */ +static Int decUnitAddSub(const Unit *a, Int alength, + const Unit *b, Int blength, Int bshift, + Unit *c, Int m) { + const Unit *alsu=a; /* A lsu [need to remember it] */ + Unit *clsu=c; /* C ditto */ + Unit *minC; /* low water mark for C */ + Unit *maxC; /* high water mark for C */ + eInt carry=0; /* carry integer (could be Long) */ + Int add; /* work */ + #if DECDPUN<=4 /* myriadal, millenary, etc. */ + Int est; /* estimated quotient */ + #endif + + #if DECTRACE + if (alength<1 || blength<1) + printf("decUnitAddSub: alen blen m %ld %ld [%ld]\n", alength, blength, m); + #endif + + maxC=c+alength; /* A is usually the longer */ + minC=c+blength; /* .. and B the shorter */ + if (bshift!=0) { /* B is shifted; low As copy across */ + minC+=bshift; + /* if in place [common], skip copy unless there's a gap [rare] */ + if (a==c && bshift<=alength) { + c+=bshift; + a+=bshift; + } + else for (; cmaxC) { /* swap */ + Unit *hold=minC; + minC=maxC; + maxC=hold; + } + + /* For speed, do the addition as two loops; the first where both A */ + /* and B contribute, and the second (if necessary) where only one or */ + /* other of the numbers contribute. */ + /* Carry handling is the same (i.e., duplicated) in each case. */ + for (; c=0) { + est=(((ueInt)carry>>11)*53687)>>18; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); /* remainder */ + carry=est; /* likely quotient [89%] */ + if (*c>11)*53687)>>18; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); + carry=est-(DECDPUNMAX+1); /* correctly negative */ + if (*c=0) { + est=(((ueInt)carry>>3)*16777)>>21; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); /* remainder */ + carry=est; /* likely quotient [99%] */ + if (*c>3)*16777)>>21; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); + carry=est-(DECDPUNMAX+1); /* correctly negative */ + if (*c=0) { + est=QUOT10(carry, DECDPUN); + *c=(Unit)(carry-est*(DECDPUNMAX+1)); /* remainder */ + carry=est; /* quotient */ + continue; + } + /* negative case */ + carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); /* make positive */ + est=QUOT10(carry, DECDPUN); + *c=(Unit)(carry-est*(DECDPUNMAX+1)); + carry=est-(DECDPUNMAX+1); /* correctly negative */ + #else + /* remainder operator is undefined if negative, so must test */ + if ((ueInt)carry<(DECDPUNMAX+1)*2) { /* fastpath carry +1 */ + *c=(Unit)(carry-(DECDPUNMAX+1)); /* [helps additions] */ + carry=1; + continue; + } + if (carry>=0) { + *c=(Unit)(carry%(DECDPUNMAX+1)); + carry=carry/(DECDPUNMAX+1); + continue; + } + /* negative case */ + carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); /* make positive */ + *c=(Unit)(carry%(DECDPUNMAX+1)); + carry=carry/(DECDPUNMAX+1)-(DECDPUNMAX+1); + #endif + } /* c */ + + /* now may have one or other to complete */ + /* [pretest to avoid loop setup/shutdown] */ + if (cDECDPUNMAX */ + #if DECDPUN==4 /* use divide-by-multiply */ + if (carry>=0) { + est=(((ueInt)carry>>11)*53687)>>18; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); /* remainder */ + carry=est; /* likely quotient [79.7%] */ + if (*c>11)*53687)>>18; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); + carry=est-(DECDPUNMAX+1); /* correctly negative */ + if (*c=0) { + est=(((ueInt)carry>>3)*16777)>>21; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); /* remainder */ + carry=est; /* likely quotient [99%] */ + if (*c>3)*16777)>>21; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); + carry=est-(DECDPUNMAX+1); /* correctly negative */ + if (*c=0) { + est=QUOT10(carry, DECDPUN); + *c=(Unit)(carry-est*(DECDPUNMAX+1)); /* remainder */ + carry=est; /* quotient */ + continue; + } + /* negative case */ + carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); /* make positive */ + est=QUOT10(carry, DECDPUN); + *c=(Unit)(carry-est*(DECDPUNMAX+1)); + carry=est-(DECDPUNMAX+1); /* correctly negative */ + #else + if ((ueInt)carry<(DECDPUNMAX+1)*2){ /* fastpath carry 1 */ + *c=(Unit)(carry-(DECDPUNMAX+1)); + carry=1; + continue; + } + /* remainder operator is undefined if negative, so must test */ + if (carry>=0) { + *c=(Unit)(carry%(DECDPUNMAX+1)); + carry=carry/(DECDPUNMAX+1); + continue; + } + /* negative case */ + carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); /* make positive */ + *c=(Unit)(carry%(DECDPUNMAX+1)); + carry=carry/(DECDPUNMAX+1)-(DECDPUNMAX+1); + #endif + } /* c */ + + /* OK, all A and B processed; might still have carry or borrow */ + /* return number of Units in the result, negated if a borrow */ + if (carry==0) return static_cast(c-clsu); /* no carry, so no more to do */ + if (carry>0) { /* positive carry */ + *c=(Unit)carry; /* place as new unit */ + c++; /* .. */ + return static_cast(c-clsu); + } + /* -ve carry: it's a borrow; complement needed */ + add=1; /* temporary carry... */ + for (c=clsu; c(clsu-c); /* -ve result indicates borrowed */ + } /* decUnitAddSub */ + +/* ------------------------------------------------------------------ */ +/* decTrim -- trim trailing zeros or normalize */ +/* */ +/* dn is the number to trim or normalize */ +/* set is the context to use to check for clamp */ +/* all is 1 to remove all trailing zeros, 0 for just fraction ones */ +/* noclamp is 1 to unconditional (unclamped) trim */ +/* dropped returns the number of discarded trailing zeros */ +/* returns dn */ +/* */ +/* If clamp is set in the context then the number of zeros trimmed */ +/* may be limited if the exponent is high. */ +/* All fields are updated as required. This is a utility operation, */ +/* so special values are unchanged and no error is possible. */ +/* ------------------------------------------------------------------ */ +static decNumber * decTrim(decNumber *dn, decContext *set, Flag all, + Flag noclamp, Int *dropped) { + Int d, exp; /* work */ + uInt cut; /* .. */ + Unit *up; /* -> current Unit */ + + #if DECCHECK + if (decCheckOperands(dn, DECUNUSED, DECUNUSED, DECUNCONT)) return dn; + #endif + + *dropped=0; /* assume no zeros dropped */ + if ((dn->bits & DECSPECIAL) /* fast exit if special .. */ + || (*dn->lsu & 0x01)) return dn; /* .. or odd */ + if (ISZERO(dn)) { /* .. or 0 */ + dn->exponent=0; /* (sign is preserved) */ + return dn; + } + + /* have a finite number which is even */ + exp=dn->exponent; + cut=1; /* digit (1-DECDPUN) in Unit */ + up=dn->lsu; /* -> current Unit */ + for (d=0; ddigits-1; d++) { /* [don't strip the final digit] */ + /* slice by powers */ + #if DECDPUN<=4 + uInt quot=QUOT10(*up, cut); + if ((*up-quot*powers[cut])!=0) break; /* found non-0 digit */ + #else + if (*up%powers[cut]!=0) break; /* found non-0 digit */ + #endif + /* have a trailing 0 */ + if (!all) { /* trimming */ + /* [if exp>0 then all trailing 0s are significant for trim] */ + if (exp<=0) { /* if digit might be significant */ + if (exp==0) break; /* then quit */ + exp++; /* next digit might be significant */ + } + } + cut++; /* next power */ + if (cut>DECDPUN) { /* need new Unit */ + up++; + cut=1; + } + } /* d */ + if (d==0) return dn; /* none to drop */ + + /* may need to limit drop if clamping */ + if (set->clamp && !noclamp) { + Int maxd=set->emax-set->digits+1-dn->exponent; + if (maxd<=0) return dn; /* nothing possible */ + if (d>maxd) d=maxd; + } + + /* effect the drop */ + decShiftToLeast(dn->lsu, D2U(dn->digits), d); + dn->exponent+=d; /* maintain numerical value */ + dn->digits-=d; /* new length */ + *dropped=d; /* report the count */ + return dn; + } /* decTrim */ + +/* ------------------------------------------------------------------ */ +/* decReverse -- reverse a Unit array in place */ +/* */ +/* ulo is the start of the array */ +/* uhi is the end of the array (highest Unit to include) */ +/* */ +/* The units ulo through uhi are reversed in place (if the number */ +/* of units is odd, the middle one is untouched). Note that the */ +/* digit(s) in each unit are unaffected. */ +/* ------------------------------------------------------------------ */ +static void decReverse(Unit *ulo, Unit *uhi) { + Unit temp; + for (; ulo=uar; source--, target--) *target=*source; + } + else { + first=uar+D2U(digits+shift)-1; /* where msu of source will end up */ + for (; source>=uar; source--, target--) { + /* split the source Unit and accumulate remainder for next */ + #if DECDPUN<=4 + uInt quot=QUOT10(*source, cut); + uInt rem=*source-quot*powers[cut]; + next+=quot; + #else + uInt rem=*source%powers[cut]; + next+=*source/powers[cut]; + #endif + if (target<=first) *target=(Unit)next; /* write to target iff valid */ + next=rem*powers[DECDPUN-cut]; /* save remainder for next Unit */ + } + } /* shift-move */ + + /* propagate any partial unit to one below and clear the rest */ + for (; target>=uar; target--) { + *target=(Unit)next; + next=0; + } + return digits+shift; + } /* decShiftToMost */ + +/* ------------------------------------------------------------------ */ +/* decShiftToLeast -- shift digits in array towards least significant */ +/* */ +/* uar is the array */ +/* units is length of the array, in units */ +/* shift is the number of digits to remove from the lsu end; it */ +/* must be zero or positive and <= than units*DECDPUN. */ +/* */ +/* returns the new length of the integer in the array, in units */ +/* */ +/* Removed digits are discarded (lost). Units not required to hold */ +/* the final result are unchanged. */ +/* ------------------------------------------------------------------ */ +static Int decShiftToLeast(Unit *uar, Int units, Int shift) { + Unit *target, *up; /* work */ + Int cut, count; /* work */ + Int quot, rem; /* for division */ + + if (shift==0) return units; /* [fastpath] nothing to do */ + if (shift==units*DECDPUN) { /* [fastpath] little to do */ + *uar=0; /* all digits cleared gives zero */ + return 1; /* leaves just the one */ + } + + target=uar; /* both paths */ + cut=MSUDIGITS(shift); + if (cut==DECDPUN) { /* unit-boundary case; easy */ + up=uar+D2U(shift); + for (; up(target-uar); + } + + /* messier */ + up=uar+D2U(shift-cut); /* source; correct to whole Units */ + count=units*DECDPUN-shift; /* the maximum new length */ + #if DECDPUN<=4 + quot=QUOT10(*up, cut); + #else + quot=*up/powers[cut]; + #endif + for (; ; target++) { + *target=(Unit)quot; + count-=(DECDPUN-cut); + if (count<=0) break; + up++; + quot=*up; + #if DECDPUN<=4 + quot=QUOT10(quot, cut); + rem=*up-quot*powers[cut]; + #else + rem=quot%powers[cut]; + quot=quot/powers[cut]; + #endif + *target=(Unit)(*target+rem*powers[DECDPUN-cut]); + count-=cut; + if (count<=0) break; + } + return static_cast(target-uar+1); + } /* decShiftToLeast */ + +#if DECSUBSET +/* ------------------------------------------------------------------ */ +/* decRoundOperand -- round an operand [used for subset only] */ +/* */ +/* dn is the number to round (dn->digits is > set->digits) */ +/* set is the relevant context */ +/* status is the status accumulator */ +/* */ +/* returns an allocated decNumber with the rounded result. */ +/* */ +/* lostDigits and other status may be set by this. */ +/* */ +/* Since the input is an operand, it must not be modified. */ +/* Instead, return an allocated decNumber, rounded as required. */ +/* It is the caller's responsibility to free the allocated storage. */ +/* */ +/* If no storage is available then the result cannot be used, so nullptr */ +/* is returned. */ +/* ------------------------------------------------------------------ */ +static decNumber *decRoundOperand(const decNumber *dn, decContext *set, + uInt *status) { + decNumber *res; /* result structure */ + uInt newstatus=0; /* status from round */ + Int residue=0; /* rounding accumulator */ + + /* Allocate storage for the returned decNumber, big enough for the */ + /* length specified by the context */ + res=(decNumber *)malloc(sizeof(decNumber) + +(D2U(set->digits)-1)*sizeof(Unit)); + if (res==nullptr) { + *status|=DEC_Insufficient_storage; + return nullptr; + } + decCopyFit(res, dn, set, &residue, &newstatus); + decApplyRound(res, set, residue, &newstatus); + + /* If that set Inexact then "lost digits" is raised... */ + if (newstatus & DEC_Inexact) newstatus|=DEC_Lost_digits; + *status|=newstatus; + return res; + } /* decRoundOperand */ +#endif + +/* ------------------------------------------------------------------ */ +/* decCopyFit -- copy a number, truncating the coefficient if needed */ +/* */ +/* dest is the target decNumber */ +/* src is the source decNumber */ +/* set is the context [used for length (digits) and rounding mode] */ +/* residue is the residue accumulator */ +/* status contains the current status to be updated */ +/* */ +/* (dest==src is allowed and will be a no-op if fits) */ +/* All fields are updated as required. */ +/* ------------------------------------------------------------------ */ +static void decCopyFit(decNumber *dest, const decNumber *src, + decContext *set, Int *residue, uInt *status) { + dest->bits=src->bits; + dest->exponent=src->exponent; + decSetCoeff(dest, set, src->lsu, src->digits, residue, status); + } /* decCopyFit */ + +/* ------------------------------------------------------------------ */ +/* decSetCoeff -- set the coefficient of a number */ +/* */ +/* dn is the number whose coefficient array is to be set. */ +/* It must have space for set->digits digits */ +/* set is the context [for size] */ +/* lsu -> lsu of the source coefficient [may be dn->lsu] */ +/* len is digits in the source coefficient [may be dn->digits] */ +/* residue is the residue accumulator. This has values as in */ +/* decApplyRound, and will be unchanged unless the */ +/* target size is less than len. In this case, the */ +/* coefficient is truncated and the residue is updated to */ +/* reflect the previous residue and the dropped digits. */ +/* status is the status accumulator, as usual */ +/* */ +/* The coefficient may already be in the number, or it can be an */ +/* external intermediate array. If it is in the number, lsu must == */ +/* dn->lsu and len must == dn->digits. */ +/* */ +/* Note that the coefficient length (len) may be < set->digits, and */ +/* in this case this merely copies the coefficient (or is a no-op */ +/* if dn->lsu==lsu). */ +/* */ +/* Note also that (only internally, from decQuantizeOp and */ +/* decSetSubnormal) the value of set->digits may be less than one, */ +/* indicating a round to left. This routine handles that case */ +/* correctly; caller ensures space. */ +/* */ +/* dn->digits, dn->lsu (and as required), and dn->exponent are */ +/* updated as necessary. dn->bits (sign) is unchanged. */ +/* */ +/* DEC_Rounded status is set if any digits are discarded. */ +/* DEC_Inexact status is set if any non-zero digits are discarded, or */ +/* incoming residue was non-0 (implies rounded) */ +/* ------------------------------------------------------------------ */ +/* mapping array: maps 0-9 to canonical residues, so that a residue */ +/* can be adjusted in the range [-1, +1] and achieve correct rounding */ +/* 0 1 2 3 4 5 6 7 8 9 */ +static const uByte resmap[10]={0, 3, 3, 3, 3, 5, 7, 7, 7, 7}; +static void decSetCoeff(decNumber *dn, decContext *set, const Unit *lsu, + Int len, Int *residue, uInt *status) { + Int discard; /* number of digits to discard */ + uInt cut; /* cut point in Unit */ + const Unit *up; /* work */ + Unit *target; /* .. */ + Int count; /* .. */ + #if DECDPUN<=4 + uInt temp; /* .. */ + #endif + + discard=len-set->digits; /* digits to discard */ + if (discard<=0) { /* no digits are being discarded */ + if (dn->lsu!=lsu) { /* copy needed */ + /* copy the coefficient array to the result number; no shift needed */ + count=len; /* avoids D2U */ + up=lsu; + for (target=dn->lsu; count>0; target++, up++, count-=DECDPUN) + *target=*up; + dn->digits=len; /* set the new length */ + } + /* dn->exponent and residue are unchanged, record any inexactitude */ + if (*residue!=0) *status|=(DEC_Inexact | DEC_Rounded); + return; + } + + /* some digits must be discarded ... */ + dn->exponent+=discard; /* maintain numerical value */ + *status|=DEC_Rounded; /* accumulate Rounded status */ + if (*residue>1) *residue=1; /* previous residue now to right, so reduce */ + + if (discard>len) { /* everything, +1, is being discarded */ + /* guard digit is 0 */ + /* residue is all the number [NB could be all 0s] */ + if (*residue<=0) { /* not already positive */ + count=len; /* avoids D2U */ + for (up=lsu; count>0; up++, count-=DECDPUN) if (*up!=0) { /* found non-0 */ + *residue=1; + break; /* no need to check any others */ + } + } + if (*residue!=0) *status|=DEC_Inexact; /* record inexactitude */ + *dn->lsu=0; /* coefficient will now be 0 */ + dn->digits=1; /* .. */ + return; + } /* total discard */ + + /* partial discard [most common case] */ + /* here, at least the first (most significant) discarded digit exists */ + + /* spin up the number, noting residue during the spin, until get to */ + /* the Unit with the first discarded digit. When reach it, extract */ + /* it and remember its position */ + count=0; + for (up=lsu;; up++) { + count+=DECDPUN; + if (count>=discard) break; /* full ones all checked */ + if (*up!=0) *residue=1; + } /* up */ + + /* here up -> Unit with first discarded digit */ + cut=discard-(count-DECDPUN)-1; + if (cut==DECDPUN-1) { /* unit-boundary case (fast) */ + Unit half=(Unit)powers[DECDPUN]>>1; + /* set residue directly */ + if (*up>=half) { + if (*up>half) *residue=7; + else *residue+=5; /* add sticky bit */ + } + else { /* digits<=0) { /* special for Quantize/Subnormal :-( */ + *dn->lsu=0; /* .. result is 0 */ + dn->digits=1; /* .. */ + } + else { /* shift to least */ + count=set->digits; /* now digits to end up with */ + dn->digits=count; /* set the new length */ + up++; /* move to next */ + /* on unit boundary, so shift-down copy loop is simple */ + for (target=dn->lsu; count>0; target++, up++, count-=DECDPUN) + *target=*up; + } + } /* unit-boundary case */ + + else { /* discard digit is in low digit(s), and not top digit */ + uInt discard1; /* first discarded digit */ + uInt quot, rem; /* for divisions */ + if (cut==0) quot=*up; /* is at bottom of unit */ + else /* cut>0 */ { /* it's not at bottom of unit */ + #if DECDPUN<=4 + U_ASSERT(/* cut >= 0 &&*/ cut <= 4); + quot=QUOT10(*up, cut); + rem=*up-quot*powers[cut]; + #else + rem=*up%powers[cut]; + quot=*up/powers[cut]; + #endif + if (rem!=0) *residue=1; + } + /* discard digit is now at bottom of quot */ + #if DECDPUN<=4 + temp=(quot*6554)>>16; /* fast /10 */ + /* Vowels algorithm here not a win (9 instructions) */ + discard1=quot-X10(temp); + quot=temp; + #else + discard1=quot%10; + quot=quot/10; + #endif + /* here, discard1 is the guard digit, and residue is everything */ + /* else [use mapping array to accumulate residue safely] */ + *residue+=resmap[discard1]; + cut++; /* update cut */ + /* here: up -> Unit of the array with bottom digit */ + /* cut is the division point for each Unit */ + /* quot holds the uncut high-order digits for the current unit */ + if (set->digits<=0) { /* special for Quantize/Subnormal :-( */ + *dn->lsu=0; /* .. result is 0 */ + dn->digits=1; /* .. */ + } + else { /* shift to least needed */ + count=set->digits; /* now digits to end up with */ + dn->digits=count; /* set the new length */ + /* shift-copy the coefficient array to the result number */ + for (target=dn->lsu; ; target++) { + *target=(Unit)quot; + count-=(DECDPUN-cut); + if (count<=0) break; + up++; + quot=*up; + #if DECDPUN<=4 + quot=QUOT10(quot, cut); + rem=*up-quot*powers[cut]; + #else + rem=quot%powers[cut]; + quot=quot/powers[cut]; + #endif + *target=(Unit)(*target+rem*powers[DECDPUN-cut]); + count-=cut; + if (count<=0) break; + } /* shift-copy loop */ + } /* shift to least */ + } /* not unit boundary */ + + if (*residue!=0) *status|=DEC_Inexact; /* record inexactitude */ + return; + } /* decSetCoeff */ + +/* ------------------------------------------------------------------ */ +/* decApplyRound -- apply pending rounding to a number */ +/* */ +/* dn is the number, with space for set->digits digits */ +/* set is the context [for size and rounding mode] */ +/* residue indicates pending rounding, being any accumulated */ +/* guard and sticky information. It may be: */ +/* 6-9: rounding digit is >5 */ +/* 5: rounding digit is exactly half-way */ +/* 1-4: rounding digit is <5 and >0 */ +/* 0: the coefficient is exact */ +/* -1: as 1, but the hidden digits are subtractive, that */ +/* is, of the opposite sign to dn. In this case the */ +/* coefficient must be non-0. This case occurs when */ +/* subtracting a small number (which can be reduced to */ +/* a sticky bit); see decAddOp. */ +/* status is the status accumulator, as usual */ +/* */ +/* This routine applies rounding while keeping the length of the */ +/* coefficient constant. The exponent and status are unchanged */ +/* except if: */ +/* */ +/* -- the coefficient was increased and is all nines (in which */ +/* case Overflow could occur, and is handled directly here so */ +/* the caller does not need to re-test for overflow) */ +/* */ +/* -- the coefficient was decreased and becomes all nines (in which */ +/* case Underflow could occur, and is also handled directly). */ +/* */ +/* All fields in dn are updated as required. */ +/* */ +/* ------------------------------------------------------------------ */ +static void decApplyRound(decNumber *dn, decContext *set, Int residue, + uInt *status) { + Int bump; /* 1 if coefficient needs to be incremented */ + /* -1 if coefficient needs to be decremented */ + + if (residue==0) return; /* nothing to apply */ + + bump=0; /* assume a smooth ride */ + + /* now decide whether, and how, to round, depending on mode */ + switch (set->round) { + case DEC_ROUND_05UP: { /* round zero or five up (for reround) */ + /* This is the same as DEC_ROUND_DOWN unless there is a */ + /* positive residue and the lsd of dn is 0 or 5, in which case */ + /* it is bumped; when residue is <0, the number is therefore */ + /* bumped down unless the final digit was 1 or 6 (in which */ + /* case it is bumped down and then up -- a no-op) */ + Int lsd5=*dn->lsu%5; /* get lsd and quintate */ + if (residue<0 && lsd5!=1) bump=-1; + else if (residue>0 && lsd5==0) bump=1; + /* [bump==1 could be applied directly; use common path for clarity] */ + break;} /* r-05 */ + + case DEC_ROUND_DOWN: { + /* no change, except if negative residue */ + if (residue<0) bump=-1; + break;} /* r-d */ + + case DEC_ROUND_HALF_DOWN: { + if (residue>5) bump=1; + break;} /* r-h-d */ + + case DEC_ROUND_HALF_EVEN: { + if (residue>5) bump=1; /* >0.5 goes up */ + else if (residue==5) { /* exactly 0.5000... */ + /* 0.5 goes up iff [new] lsd is odd */ + if (*dn->lsu & 0x01) bump=1; + } + break;} /* r-h-e */ + + case DEC_ROUND_HALF_UP: { + if (residue>=5) bump=1; + break;} /* r-h-u */ + + case DEC_ROUND_UP: { + if (residue>0) bump=1; + break;} /* r-u */ + + case DEC_ROUND_CEILING: { + /* same as _UP for positive numbers, and as _DOWN for negatives */ + /* [negative residue cannot occur on 0] */ + if (decNumberIsNegative(dn)) { + if (residue<0) bump=-1; + } + else { + if (residue>0) bump=1; + } + break;} /* r-c */ + + case DEC_ROUND_FLOOR: { + /* same as _UP for negative numbers, and as _DOWN for positive */ + /* [negative residue cannot occur on 0] */ + if (!decNumberIsNegative(dn)) { + if (residue<0) bump=-1; + } + else { + if (residue>0) bump=1; + } + break;} /* r-f */ + + default: { /* e.g., DEC_ROUND_MAX */ + *status|=DEC_Invalid_context; + #if DECTRACE || (DECCHECK && DECVERB) + printf("Unknown rounding mode: %d\n", set->round); + #endif + break;} + } /* switch */ + + /* now bump the number, up or down, if need be */ + if (bump==0) return; /* no action required */ + + /* Simply use decUnitAddSub unless bumping up and the number is */ + /* all nines. In this special case set to 100... explicitly */ + /* and adjust the exponent by one (as otherwise could overflow */ + /* the array) */ + /* Similarly handle all-nines result if bumping down. */ + if (bump>0) { + Unit *up; /* work */ + uInt count=dn->digits; /* digits to be checked */ + for (up=dn->lsu; ; up++) { + if (count<=DECDPUN) { + /* this is the last Unit (the msu) */ + if (*up!=powers[count]-1) break; /* not still 9s */ + /* here if it, too, is all nines */ + *up=(Unit)powers[count-1]; /* here 999 -> 100 etc. */ + for (up=up-1; up>=dn->lsu; up--) *up=0; /* others all to 0 */ + dn->exponent++; /* and bump exponent */ + /* [which, very rarely, could cause Overflow...] */ + if ((dn->exponent+dn->digits)>set->emax+1) { + decSetOverflow(dn, set, status); + } + return; /* done */ + } + /* a full unit to check, with more to come */ + if (*up!=DECDPUNMAX) break; /* not still 9s */ + count-=DECDPUN; + } /* up */ + } /* bump>0 */ + else { /* -1 */ + /* here checking for a pre-bump of 1000... (leading 1, all */ + /* other digits zero) */ + Unit *up, *sup; /* work */ + uInt count=dn->digits; /* digits to be checked */ + for (up=dn->lsu; ; up++) { + if (count<=DECDPUN) { + /* this is the last Unit (the msu) */ + if (*up!=powers[count-1]) break; /* not 100.. */ + /* here if have the 1000... case */ + sup=up; /* save msu pointer */ + *up=(Unit)powers[count]-1; /* here 100 in msu -> 999 */ + /* others all to all-nines, too */ + for (up=up-1; up>=dn->lsu; up--) *up=(Unit)powers[DECDPUN]-1; + dn->exponent--; /* and bump exponent */ + + /* iff the number was at the subnormal boundary (exponent=etiny) */ + /* then the exponent is now out of range, so it will in fact get */ + /* clamped to etiny and the final 9 dropped. */ + /* printf(">> emin=%d exp=%d sdig=%d\n", set->emin, */ + /* dn->exponent, set->digits); */ + if (dn->exponent+1==set->emin-set->digits+1) { + if (count==1 && dn->digits==1) *sup=0; /* here 9 -> 0[.9] */ + else { + *sup=(Unit)powers[count-1]-1; /* here 999.. in msu -> 99.. */ + dn->digits--; + } + dn->exponent++; + *status|=DEC_Underflow | DEC_Subnormal | DEC_Inexact | DEC_Rounded; + } + return; /* done */ + } + + /* a full unit to check, with more to come */ + if (*up!=0) break; /* not still 0s */ + count-=DECDPUN; + } /* up */ + + } /* bump<0 */ + + /* Actual bump needed. Do it. */ + decUnitAddSub(dn->lsu, D2U(dn->digits), uarrone, 1, 0, dn->lsu, bump); + } /* decApplyRound */ + +#if DECSUBSET +/* ------------------------------------------------------------------ */ +/* decFinish -- finish processing a number */ +/* */ +/* dn is the number */ +/* set is the context */ +/* residue is the rounding accumulator (as in decApplyRound) */ +/* status is the accumulator */ +/* */ +/* This finishes off the current number by: */ +/* 1. If not extended: */ +/* a. Converting a zero result to clean '0' */ +/* b. Reducing positive exponents to 0, if would fit in digits */ +/* 2. Checking for overflow and subnormals (always) */ +/* Note this is just Finalize when no subset arithmetic. */ +/* All fields are updated as required. */ +/* ------------------------------------------------------------------ */ +static void decFinish(decNumber *dn, decContext *set, Int *residue, + uInt *status) { + if (!set->extended) { + if ISZERO(dn) { /* value is zero */ + dn->exponent=0; /* clean exponent .. */ + dn->bits=0; /* .. and sign */ + return; /* no error possible */ + } + if (dn->exponent>=0) { /* non-negative exponent */ + /* >0; reduce to integer if possible */ + if (set->digits >= (dn->exponent+dn->digits)) { + dn->digits=decShiftToMost(dn->lsu, dn->digits, dn->exponent); + dn->exponent=0; + } + } + } /* !extended */ + + decFinalize(dn, set, residue, status); + } /* decFinish */ +#endif + +/* ------------------------------------------------------------------ */ +/* decFinalize -- final check, clamp, and round of a number */ +/* */ +/* dn is the number */ +/* set is the context */ +/* residue is the rounding accumulator (as in decApplyRound) */ +/* status is the status accumulator */ +/* */ +/* This finishes off the current number by checking for subnormal */ +/* results, applying any pending rounding, checking for overflow, */ +/* and applying any clamping. */ +/* Underflow and overflow conditions are raised as appropriate. */ +/* All fields are updated as required. */ +/* ------------------------------------------------------------------ */ +static void decFinalize(decNumber *dn, decContext *set, Int *residue, + uInt *status) { + Int shift; /* shift needed if clamping */ + Int tinyexp=set->emin-dn->digits+1; /* precalculate subnormal boundary */ + + /* Must be careful, here, when checking the exponent as the */ + /* adjusted exponent could overflow 31 bits [because it may already */ + /* be up to twice the expected]. */ + + /* First test for subnormal. This must be done before any final */ + /* round as the result could be rounded to Nmin or 0. */ + if (dn->exponent<=tinyexp) { /* prefilter */ + Int comp; + decNumber nmin; + /* A very nasty case here is dn == Nmin and residue<0 */ + if (dn->exponentemin; + comp=decCompare(dn, &nmin, 1); /* (signless compare) */ + if (comp==BADINT) { /* oops */ + *status|=DEC_Insufficient_storage; /* abandon... */ + return; + } + if (*residue<0 && comp==0) { /* neg residue and dn==Nmin */ + decApplyRound(dn, set, *residue, status); /* might force down */ + decSetSubnormal(dn, set, residue, status); + return; + } + } + + /* now apply any pending round (this could raise overflow). */ + if (*residue!=0) decApplyRound(dn, set, *residue, status); + + /* Check for overflow [redundant in the 'rare' case] or clamp */ + if (dn->exponent<=set->emax-set->digits+1) return; /* neither needed */ + + + /* here when might have an overflow or clamp to do */ + if (dn->exponent>set->emax-dn->digits+1) { /* too big */ + decSetOverflow(dn, set, status); + return; + } + /* here when the result is normal but in clamp range */ + if (!set->clamp) return; + + /* here when need to apply the IEEE exponent clamp (fold-down) */ + shift=dn->exponent-(set->emax-set->digits+1); + + /* shift coefficient (if non-zero) */ + if (!ISZERO(dn)) { + dn->digits=decShiftToMost(dn->lsu, dn->digits, shift); + } + dn->exponent-=shift; /* adjust the exponent to match */ + *status|=DEC_Clamped; /* and record the dirty deed */ + return; + } /* decFinalize */ + +/* ------------------------------------------------------------------ */ +/* decSetOverflow -- set number to proper overflow value */ +/* */ +/* dn is the number (used for sign [only] and result) */ +/* set is the context [used for the rounding mode, etc.] */ +/* status contains the current status to be updated */ +/* */ +/* This sets the sign of a number and sets its value to either */ +/* Infinity or the maximum finite value, depending on the sign of */ +/* dn and the rounding mode, following IEEE 754 rules. */ +/* ------------------------------------------------------------------ */ +static void decSetOverflow(decNumber *dn, decContext *set, uInt *status) { + Flag needmax=0; /* result is maximum finite value */ + uByte sign=dn->bits&DECNEG; /* clean and save sign bit */ + + if (ISZERO(dn)) { /* zero does not overflow magnitude */ + Int emax=set->emax; /* limit value */ + if (set->clamp) emax-=set->digits-1; /* lower if clamping */ + if (dn->exponent>emax) { /* clamp required */ + dn->exponent=emax; + *status|=DEC_Clamped; + } + return; + } + + uprv_decNumberZero(dn); + switch (set->round) { + case DEC_ROUND_DOWN: { + needmax=1; /* never Infinity */ + break;} /* r-d */ + case DEC_ROUND_05UP: { + needmax=1; /* never Infinity */ + break;} /* r-05 */ + case DEC_ROUND_CEILING: { + if (sign) needmax=1; /* Infinity if non-negative */ + break;} /* r-c */ + case DEC_ROUND_FLOOR: { + if (!sign) needmax=1; /* Infinity if negative */ + break;} /* r-f */ + default: break; /* Infinity in all other cases */ + } + if (needmax) { + decSetMaxValue(dn, set); + dn->bits=sign; /* set sign */ + } + else dn->bits=sign|DECINF; /* Value is +/-Infinity */ + *status|=DEC_Overflow | DEC_Inexact | DEC_Rounded; + } /* decSetOverflow */ + +/* ------------------------------------------------------------------ */ +/* decSetMaxValue -- set number to +Nmax (maximum normal value) */ +/* */ +/* dn is the number to set */ +/* set is the context [used for digits and emax] */ +/* */ +/* This sets the number to the maximum positive value. */ +/* ------------------------------------------------------------------ */ +static void decSetMaxValue(decNumber *dn, decContext *set) { + Unit *up; /* work */ + Int count=set->digits; /* nines to add */ + dn->digits=count; + /* fill in all nines to set maximum value */ + for (up=dn->lsu; ; up++) { + if (count>DECDPUN) *up=DECDPUNMAX; /* unit full o'nines */ + else { /* this is the msu */ + *up=(Unit)(powers[count]-1); + break; + } + count-=DECDPUN; /* filled those digits */ + } /* up */ + dn->bits=0; /* + sign */ + dn->exponent=set->emax-set->digits+1; + } /* decSetMaxValue */ + +/* ------------------------------------------------------------------ */ +/* decSetSubnormal -- process value whose exponent is extended) { + uprv_decNumberZero(dn); + /* always full overflow */ + *status|=DEC_Underflow | DEC_Subnormal | DEC_Inexact | DEC_Rounded; + return; + } + #endif + + /* Full arithmetic -- allow subnormals, rounded to minimum exponent */ + /* (Etiny) if needed */ + etiny=set->emin-(set->digits-1); /* smallest allowed exponent */ + + if ISZERO(dn) { /* value is zero */ + /* residue can never be non-zero here */ + #if DECCHECK + if (*residue!=0) { + printf("++ Subnormal 0 residue %ld\n", (LI)*residue); + *status|=DEC_Invalid_operation; + } + #endif + if (dn->exponentexponent=etiny; + *status|=DEC_Clamped; + } + return; + } + + *status|=DEC_Subnormal; /* have a non-zero subnormal */ + adjust=etiny-dn->exponent; /* calculate digits to remove */ + if (adjust<=0) { /* not out of range; unrounded */ + /* residue can never be non-zero here, except in the Nmin-residue */ + /* case (which is a subnormal result), so can take fast-path here */ + /* it may already be inexact (from setting the coefficient) */ + if (*status&DEC_Inexact) *status|=DEC_Underflow; + return; + } + + /* adjust>0, so need to rescale the result so exponent becomes Etiny */ + /* [this code is similar to that in rescale] */ + workset=*set; /* clone rounding, etc. */ + workset.digits=dn->digits-adjust; /* set requested length */ + workset.emin-=adjust; /* and adjust emin to match */ + /* [note that the latter can be <1, here, similar to Rescale case] */ + decSetCoeff(dn, &workset, dn->lsu, dn->digits, residue, status); + decApplyRound(dn, &workset, *residue, status); + + /* Use 754 default rule: Underflow is set iff Inexact */ + /* [independent of whether trapped] */ + if (*status&DEC_Inexact) *status|=DEC_Underflow; + + /* if rounded up a 999s case, exponent will be off by one; adjust */ + /* back if so [it will fit, because it was shortened earlier] */ + if (dn->exponent>etiny) { + dn->digits=decShiftToMost(dn->lsu, dn->digits, 1); + dn->exponent--; /* (re)adjust the exponent. */ + } + + /* if rounded to zero, it is by definition clamped... */ + if (ISZERO(dn)) *status|=DEC_Clamped; + } /* decSetSubnormal */ + +/* ------------------------------------------------------------------ */ +/* decCheckMath - check entry conditions for a math function */ +/* */ +/* This checks the context and the operand */ +/* */ +/* rhs is the operand to check */ +/* set is the context to check */ +/* status is unchanged if both are good */ +/* */ +/* returns non-zero if status is changed, 0 otherwise */ +/* */ +/* Restrictions enforced: */ +/* */ +/* digits, emax, and -emin in the context must be less than */ +/* DEC_MAX_MATH (999999), and A must be within these bounds if */ +/* non-zero. Invalid_operation is set in the status if a */ +/* restriction is violated. */ +/* ------------------------------------------------------------------ */ +static uInt decCheckMath(const decNumber *rhs, decContext *set, + uInt *status) { + uInt save=*status; /* record */ + if (set->digits>DEC_MAX_MATH + || set->emax>DEC_MAX_MATH + || -set->emin>DEC_MAX_MATH) *status|=DEC_Invalid_context; + else if ((rhs->digits>DEC_MAX_MATH + || rhs->exponent+rhs->digits>DEC_MAX_MATH+1 + || rhs->exponent+rhs->digits<2*(1-DEC_MAX_MATH)) + && !ISZERO(rhs)) *status|=DEC_Invalid_operation; + return (*status!=save); + } /* decCheckMath */ + +/* ------------------------------------------------------------------ */ +/* decGetInt -- get integer from a number */ +/* */ +/* dn is the number [which will not be altered] */ +/* */ +/* returns one of: */ +/* BADINT if there is a non-zero fraction */ +/* the converted integer */ +/* BIGEVEN if the integer is even and magnitude > 2*10**9 */ +/* BIGODD if the integer is odd and magnitude > 2*10**9 */ +/* */ +/* This checks and gets a whole number from the input decNumber. */ +/* The sign can be determined from dn by the caller when BIGEVEN or */ +/* BIGODD is returned. */ +/* ------------------------------------------------------------------ */ +static Int decGetInt(const decNumber *dn) { + Int theInt; /* result accumulator */ + const Unit *up; /* work */ + Int got; /* digits (real or not) processed */ + Int ilength=dn->digits+dn->exponent; /* integral length */ + Flag neg=decNumberIsNegative(dn); /* 1 if -ve */ + + /* The number must be an integer that fits in 10 digits */ + /* Assert, here, that 10 is enough for any rescale Etiny */ + #if DEC_MAX_EMAX > 999999999 + #error GetInt may need updating [for Emax] + #endif + #if DEC_MIN_EMIN < -999999999 + #error GetInt may need updating [for Emin] + #endif + if (ISZERO(dn)) return 0; /* zeros are OK, with any exponent */ + + up=dn->lsu; /* ready for lsu */ + theInt=0; /* ready to accumulate */ + if (dn->exponent>=0) { /* relatively easy */ + /* no fractional part [usual]; allow for positive exponent */ + got=dn->exponent; + } + else { /* -ve exponent; some fractional part to check and discard */ + Int count=-dn->exponent; /* digits to discard */ + /* spin up whole units until reach the Unit with the unit digit */ + for (; count>=DECDPUN; up++) { + if (*up!=0) return BADINT; /* non-zero Unit to discard */ + count-=DECDPUN; + } + if (count==0) got=0; /* [a multiple of DECDPUN] */ + else { /* [not multiple of DECDPUN] */ + Int rem; /* work */ + /* slice off fraction digits and check for non-zero */ + #if DECDPUN<=4 + theInt=QUOT10(*up, count); + rem=*up-theInt*powers[count]; + #else + rem=*up%powers[count]; /* slice off discards */ + theInt=*up/powers[count]; + #endif + if (rem!=0) return BADINT; /* non-zero fraction */ + /* it looks good */ + got=DECDPUN-count; /* number of digits so far */ + up++; /* ready for next */ + } + } + /* now it's known there's no fractional part */ + + /* tricky code now, to accumulate up to 9.3 digits */ + if (got==0) {theInt=*up; got+=DECDPUN; up++;} /* ensure lsu is there */ + + if (ilength<11) { + Int save=theInt; + /* collect any remaining unit(s) */ + for (; got1999999997) ilength=11; + else if (!neg && theInt>999999999) ilength=11; + if (ilength==11) theInt=save; /* restore correct low bit */ + } + } + + if (ilength>10) { /* too big */ + if (theInt&1) return BIGODD; /* bottom bit 1 */ + return BIGEVEN; /* bottom bit 0 */ + } + + if (neg) theInt=-theInt; /* apply sign */ + return theInt; + } /* decGetInt */ + +/* ------------------------------------------------------------------ */ +/* decDecap -- decapitate the coefficient of a number */ +/* */ +/* dn is the number to be decapitated */ +/* drop is the number of digits to be removed from the left of dn; */ +/* this must be <= dn->digits (if equal, the coefficient is */ +/* set to 0) */ +/* */ +/* Returns dn; dn->digits will be <= the initial digits less drop */ +/* (after removing drop digits there may be leading zero digits */ +/* which will also be removed). Only dn->lsu and dn->digits change. */ +/* ------------------------------------------------------------------ */ +static decNumber *decDecap(decNumber *dn, Int drop) { + Unit *msu; /* -> target cut point */ + Int cut; /* work */ + if (drop>=dn->digits) { /* losing the whole thing */ + #if DECCHECK + if (drop>dn->digits) + printf("decDecap called with drop>digits [%ld>%ld]\n", + (LI)drop, (LI)dn->digits); + #endif + dn->lsu[0]=0; + dn->digits=1; + return dn; + } + msu=dn->lsu+D2U(dn->digits-drop)-1; /* -> likely msu */ + cut=MSUDIGITS(dn->digits-drop); /* digits to be in use in msu */ + if (cut!=DECDPUN) *msu%=powers[cut]; /* clear left digits */ + /* that may have left leading zero digits, so do a proper count... */ + dn->digits=decGetDigits(dn->lsu, static_cast(msu-dn->lsu+1)); + return dn; + } /* decDecap */ + +/* ------------------------------------------------------------------ */ +/* decBiStr -- compare string with pairwise options */ +/* */ +/* targ is the string to compare */ +/* str1 is one of the strings to compare against (length may be 0) */ +/* str2 is the other; it must be the same length as str1 */ +/* */ +/* returns 1 if strings compare equal, (that is, it is the same */ +/* length as str1 and str2, and each character of targ is in either */ +/* str1 or str2 in the corresponding position), or 0 otherwise */ +/* */ +/* This is used for generic caseless compare, including the awkward */ +/* case of the Turkish dotted and dotless Is. Use as (for example): */ +/* if (decBiStr(test, "mike", "MIKE")) ... */ +/* ------------------------------------------------------------------ */ +static Flag decBiStr(const char *targ, const char *str1, const char *str2) { + for (;;targ++, str1++, str2++) { + if (*targ!=*str1 && *targ!=*str2) return 0; + /* *targ has a match in one (or both, if terminator) */ + if (*targ=='\0') break; + } /* forever */ + return 1; + } /* decBiStr */ + +/* ------------------------------------------------------------------ */ +/* decNaNs -- handle NaN operand or operands */ +/* */ +/* res is the result number */ +/* lhs is the first operand */ +/* rhs is the second operand, or nullptr if none */ +/* context is used to limit payload length */ +/* status contains the current status */ +/* returns res in case convenient */ +/* */ +/* Called when one or both operands is a NaN, and propagates the */ +/* appropriate result to res. When an sNaN is found, it is changed */ +/* to a qNaN and Invalid operation is set. */ +/* ------------------------------------------------------------------ */ +static decNumber * decNaNs(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set, + uInt *status) { + /* This decision tree ends up with LHS being the source pointer, */ + /* and status updated if need be */ + if (lhs->bits & DECSNAN) + *status|=DEC_Invalid_operation | DEC_sNaN; + else if (rhs==nullptr); + else if (rhs->bits & DECSNAN) { + lhs=rhs; + *status|=DEC_Invalid_operation | DEC_sNaN; + } + else if (lhs->bits & DECNAN); + else lhs=rhs; + + /* propagate the payload */ + if (lhs->digits<=set->digits) uprv_decNumberCopy(res, lhs); /* easy */ + else { /* too long */ + const Unit *ul; + Unit *ur, *uresp1; + /* copy safe number of units, then decapitate */ + res->bits=lhs->bits; /* need sign etc. */ + uresp1=res->lsu+D2U(set->digits); + for (ur=res->lsu, ul=lhs->lsu; urdigits=D2U(set->digits)*DECDPUN; + /* maybe still too long */ + if (res->digits>set->digits) decDecap(res, res->digits-set->digits); + } + + res->bits&=~DECSNAN; /* convert any sNaN to NaN, while */ + res->bits|=DECNAN; /* .. preserving sign */ + res->exponent=0; /* clean exponent */ + /* [coefficient was copied/decapitated] */ + return res; + } /* decNaNs */ + +/* ------------------------------------------------------------------ */ +/* decStatus -- apply non-zero status */ +/* */ +/* dn is the number to set if error */ +/* status contains the current status (not yet in context) */ +/* set is the context */ +/* */ +/* If the status is an error status, the number is set to a NaN, */ +/* unless the error was an overflow, divide-by-zero, or underflow, */ +/* in which case the number will have already been set. */ +/* */ +/* The context status is then updated with the new status. Note that */ +/* this may raise a signal, so control may never return from this */ +/* routine (hence resources must be recovered before it is called). */ +/* ------------------------------------------------------------------ */ +static void decStatus(decNumber *dn, uInt status, decContext *set) { + if (status & DEC_NaNs) { /* error status -> NaN */ + /* if cause was an sNaN, clear and propagate [NaN is already set up] */ + if (status & DEC_sNaN) status&=~DEC_sNaN; + else { + uprv_decNumberZero(dn); /* other error: clean throughout */ + dn->bits=DECNAN; /* and make a quiet NaN */ + } + } + uprv_decContextSetStatus(set, status); /* [may not return] */ + return; + } /* decStatus */ + +/* ------------------------------------------------------------------ */ +/* decGetDigits -- count digits in a Units array */ +/* */ +/* uar is the Unit array holding the number (this is often an */ +/* accumulator of some sort) */ +/* len is the length of the array in units [>=1] */ +/* */ +/* returns the number of (significant) digits in the array */ +/* */ +/* All leading zeros are excluded, except the last if the array has */ +/* only zero Units. */ +/* ------------------------------------------------------------------ */ +/* This may be called twice during some operations. */ +static Int decGetDigits(Unit *uar, Int len) { + Unit *up=uar+(len-1); /* -> msu */ + Int digits=(len-1)*DECDPUN+1; /* possible digits excluding msu */ + #if DECDPUN>4 + uInt const *pow; /* work */ + #endif + /* (at least 1 in final msu) */ + #if DECCHECK + if (len<1) printf("decGetDigits called with len<1 [%ld]\n", (LI)len); + #endif + + for (; up>=uar; up--) { + if (*up==0) { /* unit is all 0s */ + if (digits==1) break; /* a zero has one digit */ + digits-=DECDPUN; /* adjust for 0 unit */ + continue;} + /* found the first (most significant) non-zero Unit */ + #if DECDPUN>1 /* not done yet */ + if (*up<10) break; /* is 1-9 */ + digits++; + #if DECDPUN>2 /* not done yet */ + if (*up<100) break; /* is 10-99 */ + digits++; + #if DECDPUN>3 /* not done yet */ + if (*up<1000) break; /* is 100-999 */ + digits++; + #if DECDPUN>4 /* count the rest ... */ + for (pow=&powers[4]; *up>=*pow; pow++) digits++; + #endif + #endif + #endif + #endif + break; + } /* up */ + return digits; + } /* decGetDigits */ + +#if DECTRACE | DECCHECK +/* ------------------------------------------------------------------ */ +/* decNumberShow -- display a number [debug aid] */ +/* dn is the number to show */ +/* */ +/* Shows: sign, exponent, coefficient (msu first), digits */ +/* or: sign, special-value */ +/* ------------------------------------------------------------------ */ +/* this is public so other modules can use it */ +void uprv_decNumberShow(const decNumber *dn) { + const Unit *up; /* work */ + uInt u, d; /* .. */ + Int cut; /* .. */ + char isign='+'; /* main sign */ + if (dn==nullptr) { + printf("nullptr\n"); + return;} + if (decNumberIsNegative(dn)) isign='-'; + printf(" >> %c ", isign); + if (dn->bits&DECSPECIAL) { /* Is a special value */ + if (decNumberIsInfinite(dn)) printf("Infinity"); + else { /* a NaN */ + if (dn->bits&DECSNAN) printf("sNaN"); /* signalling NaN */ + else printf("NaN"); + } + /* if coefficient and exponent are 0, no more to do */ + if (dn->exponent==0 && dn->digits==1 && *dn->lsu==0) { + printf("\n"); + return;} + /* drop through to report other information */ + printf(" "); + } + + /* now carefully display the coefficient */ + up=dn->lsu+D2U(dn->digits)-1; /* msu */ + printf("%ld", (LI)*up); + for (up=up-1; up>=dn->lsu; up--) { + u=*up; + printf(":"); + for (cut=DECDPUN-1; cut>=0; cut--) { + d=u/powers[cut]; + u-=d*powers[cut]; + printf("%ld", (LI)d); + } /* cut */ + } /* up */ + if (dn->exponent!=0) { + char esign='+'; + if (dn->exponent<0) esign='-'; + printf(" E%c%ld", esign, (LI)abs(dn->exponent)); + } + printf(" [%ld]\n", (LI)dn->digits); + } /* decNumberShow */ +#endif + +#if DECTRACE || DECCHECK +/* ------------------------------------------------------------------ */ +/* decDumpAr -- display a unit array [debug/check aid] */ +/* name is a single-character tag name */ +/* ar is the array to display */ +/* len is the length of the array in Units */ +/* ------------------------------------------------------------------ */ +static void decDumpAr(char name, const Unit *ar, Int len) { + Int i; + const char *spec; + #if DECDPUN==9 + spec="%09d "; + #elif DECDPUN==8 + spec="%08d "; + #elif DECDPUN==7 + spec="%07d "; + #elif DECDPUN==6 + spec="%06d "; + #elif DECDPUN==5 + spec="%05d "; + #elif DECDPUN==4 + spec="%04d "; + #elif DECDPUN==3 + spec="%03d "; + #elif DECDPUN==2 + spec="%02d "; + #else + spec="%d "; + #endif + printf(" :%c: ", name); + for (i=len-1; i>=0; i--) { + if (i==len-1) printf("%ld ", (LI)ar[i]); + else printf(spec, ar[i]); + } + printf("\n"); + return;} +#endif + +#if DECCHECK +/* ------------------------------------------------------------------ */ +/* decCheckOperands -- check operand(s) to a routine */ +/* res is the result structure (not checked; it will be set to */ +/* quiet NaN if error found (and it is not nullptr)) */ +/* lhs is the first operand (may be DECUNRESU) */ +/* rhs is the second (may be DECUNUSED) */ +/* set is the context (may be DECUNCONT) */ +/* returns 0 if both operands, and the context are clean, or 1 */ +/* otherwise (in which case the context will show an error, */ +/* unless nullptr). Note that res is not cleaned; caller should */ +/* handle this so res=nullptr case is safe. */ +/* The caller is expected to abandon immediately if 1 is returned. */ +/* ------------------------------------------------------------------ */ +static Flag decCheckOperands(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + Flag bad=0; + if (set==nullptr) { /* oops; hopeless */ + #if DECTRACE || DECVERB + printf("Reference to context is nullptr.\n"); + #endif + bad=1; + return 1;} + else if (set!=DECUNCONT + && (set->digits<1 || set->round>=DEC_ROUND_MAX)) { + bad=1; + #if DECTRACE || DECVERB + printf("Bad context [digits=%ld round=%ld].\n", + (LI)set->digits, (LI)set->round); + #endif + } + else { + if (res==nullptr) { + bad=1; + #if DECTRACE + /* this one not DECVERB as standard tests include nullptr */ + printf("Reference to result is nullptr.\n"); + #endif + } + if (!bad && lhs!=DECUNUSED) bad=(decCheckNumber(lhs)); + if (!bad && rhs!=DECUNUSED) bad=(decCheckNumber(rhs)); + } + if (bad) { + if (set!=DECUNCONT) uprv_decContextSetStatus(set, DEC_Invalid_operation); + if (res!=DECUNRESU && res!=nullptr) { + uprv_decNumberZero(res); + res->bits=DECNAN; /* qNaN */ + } + } + return bad; + } /* decCheckOperands */ + +/* ------------------------------------------------------------------ */ +/* decCheckNumber -- check a number */ +/* dn is the number to check */ +/* returns 0 if the number is clean, or 1 otherwise */ +/* */ +/* The number is considered valid if it could be a result from some */ +/* operation in some valid context. */ +/* ------------------------------------------------------------------ */ +static Flag decCheckNumber(const decNumber *dn) { + const Unit *up; /* work */ + uInt maxuint; /* .. */ + Int ae, d, digits; /* .. */ + Int emin, emax; /* .. */ + + if (dn==nullptr) { /* hopeless */ + #if DECTRACE + /* this one not DECVERB as standard tests include nullptr */ + printf("Reference to decNumber is nullptr.\n"); + #endif + return 1;} + + /* check special values */ + if (dn->bits & DECSPECIAL) { + if (dn->exponent!=0) { + #if DECTRACE || DECVERB + printf("Exponent %ld (not 0) for a special value [%02x].\n", + (LI)dn->exponent, dn->bits); + #endif + return 1;} + + /* 2003.09.08: NaNs may now have coefficients, so next tests Inf only */ + if (decNumberIsInfinite(dn)) { + if (dn->digits!=1) { + #if DECTRACE || DECVERB + printf("Digits %ld (not 1) for an infinity.\n", (LI)dn->digits); + #endif + return 1;} + if (*dn->lsu!=0) { + #if DECTRACE || DECVERB + printf("LSU %ld (not 0) for an infinity.\n", (LI)*dn->lsu); + #endif + decDumpAr('I', dn->lsu, D2U(dn->digits)); + return 1;} + } /* Inf */ + /* 2002.12.26: negative NaNs can now appear through proposed IEEE */ + /* concrete formats (decimal64, etc.). */ + return 0; + } + + /* check the coefficient */ + if (dn->digits<1 || dn->digits>DECNUMMAXP) { + #if DECTRACE || DECVERB + printf("Digits %ld in number.\n", (LI)dn->digits); + #endif + return 1;} + + d=dn->digits; + + for (up=dn->lsu; d>0; up++) { + if (d>DECDPUN) maxuint=DECDPUNMAX; + else { /* reached the msu */ + maxuint=powers[d]-1; + if (dn->digits>1 && *upmaxuint) { + #if DECTRACE || DECVERB + printf("Bad Unit [%08lx] in %ld-digit number at offset %ld [maxuint %ld].\n", + (LI)*up, (LI)dn->digits, (LI)(up-dn->lsu), (LI)maxuint); + #endif + return 1;} + d-=DECDPUN; + } + + /* check the exponent. Note that input operands can have exponents */ + /* which are out of the set->emin/set->emax and set->digits range */ + /* (just as they can have more digits than set->digits). */ + ae=dn->exponent+dn->digits-1; /* adjusted exponent */ + emax=DECNUMMAXE; + emin=DECNUMMINE; + digits=DECNUMMAXP; + if (ae+emax) { + #if DECTRACE || DECVERB + printf("Adjusted exponent overflow [%ld].\n", (LI)ae); + uprv_decNumberShow(dn); + #endif + return 1;} + + return 0; /* it's OK */ + } /* decCheckNumber */ + +/* ------------------------------------------------------------------ */ +/* decCheckInexact -- check a normal finite inexact result has digits */ +/* dn is the number to check */ +/* set is the context (for status and precision) */ +/* sets Invalid operation, etc., if some digits are missing */ +/* [this check is not made for DECSUBSET compilation or when */ +/* subnormal is not set] */ +/* ------------------------------------------------------------------ */ +static void decCheckInexact(const decNumber *dn, decContext *set) { + #if !DECSUBSET && DECEXTFLAG + if ((set->status & (DEC_Inexact|DEC_Subnormal))==DEC_Inexact + && (set->digits!=dn->digits) && !(dn->bits & DECSPECIAL)) { + #if DECTRACE || DECVERB + printf("Insufficient digits [%ld] on normal Inexact result.\n", + (LI)dn->digits); + uprv_decNumberShow(dn); + #endif + uprv_decContextSetStatus(set, DEC_Invalid_operation); + } + #else + /* next is a noop for quiet compiler */ + if (dn!=nullptr && dn->digits==0) set->status|=DEC_Invalid_operation; + #endif + return; + } /* decCheckInexact */ +#endif + +#if DECALLOC +#undef malloc +#undef free +/* ------------------------------------------------------------------ */ +/* decMalloc -- accountable allocation routine */ +/* n is the number of bytes to allocate */ +/* */ +/* Semantics is the same as the stdlib malloc routine, but bytes */ +/* allocated are accounted for globally, and corruption fences are */ +/* added before and after the 'actual' storage. */ +/* ------------------------------------------------------------------ */ +/* This routine allocates storage with an extra twelve bytes; 8 are */ +/* at the start and hold: */ +/* 0-3 the original length requested */ +/* 4-7 buffer corruption detection fence (DECFENCE, x4) */ +/* The 4 bytes at the end also hold a corruption fence (DECFENCE, x4) */ +/* ------------------------------------------------------------------ */ +static void *decMalloc(size_t n) { + uInt size=n+12; /* true size */ + void *alloc; /* -> allocated storage */ + uByte *b, *b0; /* work */ + uInt uiwork; /* for macros */ + + alloc=malloc(size); /* -> allocated storage */ + if (alloc==nullptr) return nullptr; /* out of strorage */ + b0=(uByte *)alloc; /* as bytes */ + decAllocBytes+=n; /* account for storage */ + UBFROMUI(alloc, n); /* save n */ + /* printf(" alloc ++ dAB: %ld (%ld)\n", (LI)decAllocBytes, (LI)n); */ + for (b=b0+4; b play area */ + } /* decMalloc */ + +/* ------------------------------------------------------------------ */ +/* decFree -- accountable free routine */ +/* alloc is the storage to free */ +/* */ +/* Semantics is the same as the stdlib malloc routine, except that */ +/* the global storage accounting is updated and the fences are */ +/* checked to ensure that no routine has written 'out of bounds'. */ +/* ------------------------------------------------------------------ */ +/* This routine first checks that the fences have not been corrupted. */ +/* It then frees the storage using the 'truw' storage address (that */ +/* is, offset by 8). */ +/* ------------------------------------------------------------------ */ +static void decFree(void *alloc) { + uInt n; /* original length */ + uByte *b, *b0; /* work */ + uInt uiwork; /* for macros */ + + if (alloc==nullptr) return; /* allowed; it's a nop */ + b0=(uByte *)alloc; /* as bytes */ + b0-=8; /* -> true start of storage */ + n=UBTOUI(b0); /* lift length */ + for (b=b0+4; b0 */ - /* and <10; 3 or powers of 2 are best]. */ - - /* DECNUMDIGITS is the default number of digits that can be held in */ - /* the structure. If undefined, 1 is assumed and it is assumed */ - /* that the structure will be immediately followed by extra space, */ - /* as required. DECNUMDIGITS is always >0. */ - #if !defined(DECNUMDIGITS) - #define DECNUMDIGITS 1 - #endif - - /* The size (integer data type) of each unit is determined by the */ - /* number of digits it will hold. */ - #if DECDPUN<=2 - #define decNumberUnit uint8_t - #elif DECDPUN<=4 - #define decNumberUnit uint16_t - #else - #define decNumberUnit uint32_t - #endif - /* The number of units needed is ceil(DECNUMDIGITS/DECDPUN) */ - #define DECNUMUNITS ((DECNUMDIGITS+DECDPUN-1)/DECDPUN) - - /* The data structure... */ - typedef struct { - int32_t digits; /* Count of digits in the coefficient; >0 */ - int32_t exponent; /* Unadjusted exponent, unbiased, in */ - /* range: -1999999997 through 999999999 */ - uint8_t bits; /* Indicator bits (see above) */ - /* Coefficient, from least significant unit */ - decNumberUnit lsu[DECNUMUNITS]; - } decNumber; - - /* Notes: */ - /* 1. If digits is > DECDPUN then there will one or more */ - /* decNumberUnits immediately following the first element of lsu.*/ - /* These contain the remaining (more significant) digits of the */ - /* number, and may be in the lsu array, or may be guaranteed by */ - /* some other mechanism (such as being contained in another */ - /* structure, or being overlaid on dynamically allocated */ - /* storage). */ - /* */ - /* Each integer of the coefficient (except potentially the last) */ - /* contains DECDPUN digits (e.g., a value in the range 0 through */ - /* 99999999 if DECDPUN is 8, or 0 through 999 if DECDPUN is 3). */ - /* */ - /* 2. A decNumber converted to a string may need up to digits+14 */ - /* characters. The worst cases (non-exponential and exponential */ - /* formats) are -0.00000{9...}# and -9.{9...}E+999999999# */ - /* (where # is '\0') */ - - - /* ---------------------------------------------------------------- */ - /* decNumber public functions and macros */ - /* ---------------------------------------------------------------- */ - /* Conversions */ - U_CAPI decNumber * U_EXPORT2 uprv_decNumberFromInt32(decNumber *, int32_t); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberFromUInt32(decNumber *, uint32_t); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberFromString(decNumber *, const char *, decContext *); - U_CAPI char * U_EXPORT2 uprv_decNumberToString(const decNumber *, char *); - U_CAPI char * U_EXPORT2 uprv_decNumberToEngString(const decNumber *, char *); - U_CAPI uint32_t U_EXPORT2 uprv_decNumberToUInt32(const decNumber *, decContext *); - U_CAPI int32_t U_EXPORT2 uprv_decNumberToInt32(const decNumber *, decContext *); - U_CAPI uint8_t * U_EXPORT2 uprv_decNumberGetBCD(const decNumber *, uint8_t *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberSetBCD(decNumber *, const uint8_t *, uint32_t); - - /* Operators and elementary functions */ - U_CAPI decNumber * U_EXPORT2 uprv_decNumberAbs(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberAdd(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberAnd(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompare(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompareSignal(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompareTotal(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompareTotalMag(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberDivide(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberDivideInteger(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberExp(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberFMA(decNumber *, const decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberInvert(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberLn(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberLogB(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberLog10(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberMax(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberMaxMag(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberMin(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberMinMag(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberMinus(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberMultiply(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberNormalize(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberOr(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberPlus(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberPower(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberQuantize(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberReduce(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberRemainder(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberRemainderNear(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberRescale(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberRotate(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberSameQuantum(decNumber *, const decNumber *, const decNumber *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberScaleB(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberShift(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberSquareRoot(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberSubtract(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberToIntegralExact(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberToIntegralValue(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberXor(decNumber *, const decNumber *, const decNumber *, decContext *); - - /* Utilities */ - enum decClass uprv_decNumberClass(const decNumber *, decContext *); - U_CAPI const char * U_EXPORT2 uprv_decNumberClassToString(enum decClass); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopy(decNumber *, const decNumber *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopyAbs(decNumber *, const decNumber *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopyNegate(decNumber *, const decNumber *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopySign(decNumber *, const decNumber *, const decNumber *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberNextMinus(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberNextPlus(decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberNextToward(decNumber *, const decNumber *, const decNumber *, decContext *); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberTrim(decNumber *); - U_CAPI const char * U_EXPORT2 uprv_decNumberVersion(void); - U_CAPI decNumber * U_EXPORT2 uprv_decNumberZero(decNumber *); - - /* Functions for testing decNumbers (normality depends on context) */ - U_CAPI int32_t U_EXPORT2 uprv_decNumberIsNormal(const decNumber *, decContext *); - U_CAPI int32_t U_EXPORT2 uprv_decNumberIsSubnormal(const decNumber *, decContext *); - - /* Macros for testing decNumber *dn */ - #define decNumberIsCanonical(dn) (1) /* All decNumbers are saintly */ - #define decNumberIsFinite(dn) (((dn)->bits&DECSPECIAL)==0) - #define decNumberIsInfinite(dn) (((dn)->bits&DECINF)!=0) - #define decNumberIsNaN(dn) (((dn)->bits&(DECNAN|DECSNAN))!=0) - #define decNumberIsNegative(dn) (((dn)->bits&DECNEG)!=0) - #define decNumberIsQNaN(dn) (((dn)->bits&(DECNAN))!=0) - #define decNumberIsSNaN(dn) (((dn)->bits&(DECSNAN))!=0) - #define decNumberIsSpecial(dn) (((dn)->bits&DECSPECIAL)!=0) - #define decNumberIsZero(dn) (*(dn)->lsu==0 \ - && (dn)->digits==1 \ - && (((dn)->bits&DECSPECIAL)==0)) - #define decNumberRadix(dn) (10) - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* ------------------------------------------------------------------ */ +/* Decimal Number arithmetic module header */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000-2010. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ + +/* Modified version, for use from within ICU. + * Renamed public functions, to avoid an unwanted export of the + * standard names from the ICU library. + * + * Use ICU's uprv_malloc() and uprv_free() + * + * Revert comment syntax to plain C + * + * Remove a few compiler warnings. + */ + +#if !defined(DECNUMBER) + #define DECNUMBER + #define DECNAME "decNumber" /* Short name */ + #define DECFULLNAME "Decimal Number Module" /* Verbose name */ + #define DECAUTHOR "Mike Cowlishaw" /* Who to blame */ + + #if !defined(DECCONTEXT) + #include "decContext.h" + #endif + + /* Bit settings for decNumber.bits */ + #define DECNEG 0x80 /* Sign; 1=negative, 0=positive or zero */ + #define DECINF 0x40 /* 1=Infinity */ + #define DECNAN 0x20 /* 1=NaN */ + #define DECSNAN 0x10 /* 1=sNaN */ + /* The remaining bits are reserved; they must be 0 */ + #define DECSPECIAL (DECINF|DECNAN|DECSNAN) /* any special value */ + + /* Define the decNumber data structure. The size and shape of the */ + /* units array in the structure is determined by the following */ + /* constant. This must not be changed without recompiling the */ + /* decNumber library modules. */ + + /* For ICU, use one digit per byte, to make it easier to emulate the + * old DigitList interface on top of a decNumber + */ + #define DECDPUN 1 /* DECimal Digits Per UNit [must be >0 */ + /* and <10; 3 or powers of 2 are best]. */ + + /* DECNUMDIGITS is the default number of digits that can be held in */ + /* the structure. If undefined, 1 is assumed and it is assumed */ + /* that the structure will be immediately followed by extra space, */ + /* as required. DECNUMDIGITS is always >0. */ + #if !defined(DECNUMDIGITS) + #define DECNUMDIGITS 1 + #endif + + /* The size (integer data type) of each unit is determined by the */ + /* number of digits it will hold. */ + #if DECDPUN<=2 + #define decNumberUnit uint8_t + #elif DECDPUN<=4 + #define decNumberUnit uint16_t + #else + #define decNumberUnit uint32_t + #endif + /* The number of units needed is ceil(DECNUMDIGITS/DECDPUN) */ + #define DECNUMUNITS ((DECNUMDIGITS+DECDPUN-1)/DECDPUN) + + /* The data structure... */ + typedef struct { + int32_t digits; /* Count of digits in the coefficient; >0 */ + int32_t exponent; /* Unadjusted exponent, unbiased, in */ + /* range: -1999999997 through 999999999 */ + uint8_t bits; /* Indicator bits (see above) */ + /* Coefficient, from least significant unit */ + decNumberUnit lsu[DECNUMUNITS]; + } decNumber; + + /* Notes: */ + /* 1. If digits is > DECDPUN then there will one or more */ + /* decNumberUnits immediately following the first element of lsu.*/ + /* These contain the remaining (more significant) digits of the */ + /* number, and may be in the lsu array, or may be guaranteed by */ + /* some other mechanism (such as being contained in another */ + /* structure, or being overlaid on dynamically allocated */ + /* storage). */ + /* */ + /* Each integer of the coefficient (except potentially the last) */ + /* contains DECDPUN digits (e.g., a value in the range 0 through */ + /* 99999999 if DECDPUN is 8, or 0 through 999 if DECDPUN is 3). */ + /* */ + /* 2. A decNumber converted to a string may need up to digits+14 */ + /* characters. The worst cases (non-exponential and exponential */ + /* formats) are -0.00000{9...}# and -9.{9...}E+999999999# */ + /* (where # is '\0') */ + + + /* ---------------------------------------------------------------- */ + /* decNumber public functions and macros */ + /* ---------------------------------------------------------------- */ + /* Conversions */ + U_CAPI decNumber * U_EXPORT2 uprv_decNumberFromInt32(decNumber *, int32_t); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberFromUInt32(decNumber *, uint32_t); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberFromString(decNumber *, const char *, decContext *); + U_CAPI char * U_EXPORT2 uprv_decNumberToString(const decNumber *, char *); + U_CAPI char * U_EXPORT2 uprv_decNumberToEngString(const decNumber *, char *); + U_CAPI uint32_t U_EXPORT2 uprv_decNumberToUInt32(const decNumber *, decContext *); + U_CAPI int32_t U_EXPORT2 uprv_decNumberToInt32(const decNumber *, decContext *); + U_CAPI uint8_t * U_EXPORT2 uprv_decNumberGetBCD(const decNumber *, uint8_t *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberSetBCD(decNumber *, const uint8_t *, uint32_t); + + /* Operators and elementary functions */ + U_CAPI decNumber * U_EXPORT2 uprv_decNumberAbs(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberAdd(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberAnd(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompare(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompareSignal(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompareTotal(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberCompareTotalMag(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberDivide(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberDivideInteger(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberExp(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberFMA(decNumber *, const decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberInvert(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberLn(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberLogB(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberLog10(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberMax(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberMaxMag(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberMin(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberMinMag(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberMinus(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberMultiply(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberNormalize(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberOr(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberPlus(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberPower(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberQuantize(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberReduce(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberRemainder(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberRemainderNear(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberRescale(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberRotate(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberSameQuantum(decNumber *, const decNumber *, const decNumber *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberScaleB(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberShift(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberSquareRoot(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberSubtract(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberToIntegralExact(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberToIntegralValue(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberXor(decNumber *, const decNumber *, const decNumber *, decContext *); + + /* Utilities */ + enum decClass uprv_decNumberClass(const decNumber *, decContext *); + U_CAPI const char * U_EXPORT2 uprv_decNumberClassToString(enum decClass); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopy(decNumber *, const decNumber *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopyAbs(decNumber *, const decNumber *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopyNegate(decNumber *, const decNumber *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberCopySign(decNumber *, const decNumber *, const decNumber *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberNextMinus(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberNextPlus(decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberNextToward(decNumber *, const decNumber *, const decNumber *, decContext *); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberTrim(decNumber *); + U_CAPI const char * U_EXPORT2 uprv_decNumberVersion(); + U_CAPI decNumber * U_EXPORT2 uprv_decNumberZero(decNumber *); + + /* Functions for testing decNumbers (normality depends on context) */ + U_CAPI int32_t U_EXPORT2 uprv_decNumberIsNormal(const decNumber *, decContext *); + U_CAPI int32_t U_EXPORT2 uprv_decNumberIsSubnormal(const decNumber *, decContext *); + + /* Macros for testing decNumber *dn */ + #define decNumberIsCanonical(dn) (1) /* All decNumbers are saintly */ + #define decNumberIsFinite(dn) (((dn)->bits&DECSPECIAL)==0) + #define decNumberIsInfinite(dn) (((dn)->bits&DECINF)!=0) + #define decNumberIsNaN(dn) (((dn)->bits&(DECNAN|DECSNAN))!=0) + #define decNumberIsNegative(dn) (((dn)->bits&DECNEG)!=0) + #define decNumberIsQNaN(dn) (((dn)->bits&(DECNAN))!=0) + #define decNumberIsSNaN(dn) (((dn)->bits&(DECSNAN))!=0) + #define decNumberIsSpecial(dn) (((dn)->bits&DECSPECIAL)!=0) + #define decNumberIsZero(dn) (*(dn)->lsu==0 \ + && (dn)->digits==1 \ + && (((dn)->bits&DECSPECIAL)==0)) + #define decNumberRadix(dn) (10) + +#endif diff --git a/deps/icu-small/source/i18n/decNumberLocal.h b/deps/icu-small/source/i18n/decNumberLocal.h index 1c5a79b7021f0c..2c79d61f8eb88a 100644 --- a/deps/icu-small/source/i18n/decNumberLocal.h +++ b/deps/icu-small/source/i18n/decNumberLocal.h @@ -1,728 +1,728 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* ------------------------------------------------------------------ */ -/* decNumber package local type, tuning, and macro definitions */ -/* ------------------------------------------------------------------ */ -/* Copyright (c) IBM Corporation, 2000-2016. All rights reserved. */ -/* */ -/* This software is made available under the terms of the */ -/* ICU License -- ICU 1.8.1 and later. */ -/* */ -/* The description and User's Guide ("The decNumber C Library") for */ -/* this software is called decNumber.pdf. This document is */ -/* available, together with arithmetic and format specifications, */ -/* testcases, and Web links, on the General Decimal Arithmetic page. */ -/* */ -/* Please send comments, suggestions, and corrections to the author: */ -/* mfc@uk.ibm.com */ -/* Mike Cowlishaw, IBM Fellow */ -/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ -/* ------------------------------------------------------------------ */ -/* This header file is included by all modules in the decNumber */ -/* library, and contains local type definitions, tuning parameters, */ -/* etc. It should not need to be used by application programs. */ -/* decNumber.h or one of decDouble (etc.) must be included first. */ -/* ------------------------------------------------------------------ */ - -#if !defined(DECNUMBERLOC) - #define DECNUMBERLOC - #define DECVERSION "decNumber 3.61" /* Package Version [16 max.] */ - #define DECNLAUTHOR "Mike Cowlishaw" /* Who to blame */ - - #include /* for abs */ - #include /* for memset, strcpy */ - #include "decContext.h" - - /* Conditional code flag -- set this to match hardware platform */ - #if !defined(DECLITEND) - #define DECLITEND 1 /* 1=little-endian, 0=big-endian */ - #endif - - /* Conditional code flag -- set this to 1 for best performance */ - #if !defined(DECUSE64) - #define DECUSE64 1 /* 1=use int64s, 0=int32 & smaller only */ - #endif - - /* Conditional check flags -- set these to 0 for best performance */ - #if !defined(DECCHECK) - #define DECCHECK 0 /* 1 to enable robust checking */ - #endif - #if !defined(DECALLOC) - #define DECALLOC 0 /* 1 to enable memory accounting */ - #endif - #if !defined(DECTRACE) - #define DECTRACE 0 /* 1 to trace certain internals, etc. */ - #endif - - /* Tuning parameter for decNumber (arbitrary precision) module */ - #if !defined(DECBUFFER) - #define DECBUFFER 36 /* Size basis for local buffers. This */ - /* should be a common maximum precision */ - /* rounded up to a multiple of 4; must */ - /* be zero or positive. */ - #endif - - /* ---------------------------------------------------------------- */ - /* Definitions for all modules (general-purpose) */ - /* ---------------------------------------------------------------- */ - - /* Local names for common types -- for safety, decNumber modules do */ - /* not use int or long directly. */ - #define Flag uint8_t - #define Byte int8_t - #define uByte uint8_t - #define Short int16_t - #define uShort uint16_t - #define Int int32_t - #define uInt uint32_t - #define Unit decNumberUnit - #if DECUSE64 - #define Long int64_t - #define uLong uint64_t - #endif - - /* Development-use definitions */ - typedef long int LI; /* for printf arguments only */ - #define DECNOINT 0 /* 1 to check no internal use of 'int' */ - /* or stdint types */ - #if DECNOINT - /* if these interfere with your C includes, do not set DECNOINT */ - #define int ? /* enable to ensure that plain C 'int' */ - #define long ?? /* .. or 'long' types are not used */ - #endif - - /* LONGMUL32HI -- set w=(u*v)>>32, where w, u, and v are uInts */ - /* (that is, sets w to be the high-order word of the 64-bit result; */ - /* the low-order word is simply u*v.) */ - /* This version is derived from Knuth via Hacker's Delight; */ - /* it seems to optimize better than some others tried */ - #define LONGMUL32HI(w, u, v) { \ - uInt u0, u1, v0, v1, w0, w1, w2, t; \ - u0=u & 0xffff; u1=u>>16; \ - v0=v & 0xffff; v1=v>>16; \ - w0=u0*v0; \ - t=u1*v0 + (w0>>16); \ - w1=t & 0xffff; w2=t>>16; \ - w1=u0*v1 + w1; \ - (w)=u1*v1 + w2 + (w1>>16);} - - /* ROUNDUP -- round an integer up to a multiple of n */ - #define ROUNDUP(i, n) ((((i)+(n)-1)/n)*n) - #define ROUNDUP4(i) (((i)+3)&~3) /* special for n=4 */ - - /* ROUNDDOWN -- round an integer down to a multiple of n */ - #define ROUNDDOWN(i, n) (((i)/n)*n) - #define ROUNDDOWN4(i) ((i)&~3) /* special for n=4 */ - - /* References to multi-byte sequences under different sizes; these */ - /* require locally declared variables, but do not violate strict */ - /* aliasing or alignment (as did the UINTAT simple cast to uInt). */ - /* Variables needed are uswork, uiwork, etc. [so do not use at same */ - /* level in an expression, e.g., UBTOUI(x)==UBTOUI(y) may fail]. */ - - /* Return a uInt, etc., from bytes starting at a char* or uByte* */ - #define UBTOUS(b) (memcpy((void *)&uswork, b, 2), uswork) - #define UBTOUI(b) (memcpy((void *)&uiwork, b, 4), uiwork) - - /* Store a uInt, etc., into bytes starting at a char* or uByte*. */ - /* Returns i, evaluated, for convenience; has to use uiwork because */ - /* i may be an expression. */ - #define UBFROMUS(b, i) (uswork=(i), memcpy(b, (void *)&uswork, 2), uswork) - #define UBFROMUI(b, i) (uiwork=(i), memcpy(b, (void *)&uiwork, 4), uiwork) - - /* X10 and X100 -- multiply integer i by 10 or 100 */ - /* [shifts are usually faster than multiply; could be conditional] */ - #define X10(i) (((i)<<1)+((i)<<3)) - #define X100(i) (((i)<<2)+((i)<<5)+((i)<<6)) - - /* MAXI and MINI -- general max & min (not in ANSI) for integers */ - #define MAXI(x,y) ((x)<(y)?(y):(x)) - #define MINI(x,y) ((x)>(y)?(y):(x)) - - /* Useful constants */ - #define BILLION 1000000000 /* 10**9 */ - /* CHARMASK: 0x30303030 for ASCII/UTF8; 0xF0F0F0F0 for EBCDIC */ - #define CHARMASK ((((((((uInt)'0')<<8)+'0')<<8)+'0')<<8)+'0') - - - /* ---------------------------------------------------------------- */ - /* Definitions for arbitrary-precision modules (only valid after */ - /* decNumber.h has been included) */ - /* ---------------------------------------------------------------- */ - - /* Limits and constants */ - #define DECNUMMAXP 999999999 /* maximum precision code can handle */ - #define DECNUMMAXE 999999999 /* maximum adjusted exponent ditto */ - #define DECNUMMINE -999999999 /* minimum adjusted exponent ditto */ - #if (DECNUMMAXP != DEC_MAX_DIGITS) - #error Maximum digits mismatch - #endif - #if (DECNUMMAXE != DEC_MAX_EMAX) - #error Maximum exponent mismatch - #endif - #if (DECNUMMINE != DEC_MIN_EMIN) - #error Minimum exponent mismatch - #endif - - /* Set DECDPUNMAX -- the maximum integer that fits in DECDPUN */ - /* digits, and D2UTABLE -- the initializer for the D2U table */ - #ifndef DECDPUN - // no-op - #elif DECDPUN==1 - #define DECDPUNMAX 9 - #define D2UTABLE {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17, \ - 18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, \ - 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, \ - 48,49} - #elif DECDPUN==2 - #define DECDPUNMAX 99 - #define D2UTABLE {0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10, \ - 11,11,12,12,13,13,14,14,15,15,16,16,17,17,18, \ - 18,19,19,20,20,21,21,22,22,23,23,24,24,25} - #elif DECDPUN==3 - #define DECDPUNMAX 999 - #define D2UTABLE {0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7,7, \ - 8,8,8,9,9,9,10,10,10,11,11,11,12,12,12,13,13, \ - 13,14,14,14,15,15,15,16,16,16,17} - #elif DECDPUN==4 - #define DECDPUNMAX 9999 - #define D2UTABLE {0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6, \ - 6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11, \ - 11,11,11,12,12,12,12,13} - #elif DECDPUN==5 - #define DECDPUNMAX 99999 - #define D2UTABLE {0,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5, \ - 5,5,5,5,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8,9,9,9, \ - 9,9,10,10,10,10} - #elif DECDPUN==6 - #define DECDPUNMAX 999999 - #define D2UTABLE {0,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4, \ - 4,4,4,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,7,7,8, \ - 8,8,8,8,8,9} - #elif DECDPUN==7 - #define DECDPUNMAX 9999999 - #define D2UTABLE {0,1,1,1,1,1,1,1,2,2,2,2,2,2,2,3,3,3,3,3,3,3, \ - 4,4,4,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7, \ - 7,7,7,7,7,7} - #elif DECDPUN==8 - #define DECDPUNMAX 99999999 - #define D2UTABLE {0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3, \ - 3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,6,6,6, \ - 6,6,6,6,6,7} - #elif DECDPUN==9 - #define DECDPUNMAX 999999999 - #define D2UTABLE {0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,3, \ - 3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5, \ - 5,5,6,6,6,6} - #else - #error DECDPUN must be in the range 1-9 - #endif - - /* ----- Shared data (in decNumber.c) ----- */ - /* Public lookup table used by the D2U macro (see below) */ - #define DECMAXD2U 49 - /*extern const uByte d2utable[DECMAXD2U+1];*/ - - /* ----- Macros ----- */ - /* ISZERO -- return true if decNumber dn is a zero */ - /* [performance-critical in some situations] */ - #define ISZERO(dn) decNumberIsZero(dn) /* now just a local name */ - - /* D2U -- return the number of Units needed to hold d digits */ - /* (runtime version, with table lookaside for small d) */ - #if defined(DECDPUN) && DECDPUN==8 - #define D2U(d) ((unsigned)((d)<=DECMAXD2U?d2utable[d]:((d)+7)>>3)) - #elif defined(DECDPUN) && DECDPUN==4 - #define D2U(d) ((unsigned)((d)<=DECMAXD2U?d2utable[d]:((d)+3)>>2)) - #else - #define D2U(d) ((d)<=DECMAXD2U?d2utable[d]:((d)+DECDPUN-1)/DECDPUN) - #endif - /* SD2U -- static D2U macro (for compile-time calculation) */ - #define SD2U(d) (((d)+DECDPUN-1)/DECDPUN) - - /* MSUDIGITS -- returns digits in msu, from digits, calculated */ - /* using D2U */ - #define MSUDIGITS(d) ((d)-(D2U(d)-1)*DECDPUN) - - /* D2N -- return the number of decNumber structs that would be */ - /* needed to contain that number of digits (and the initial */ - /* decNumber struct) safely. Note that one Unit is included in the */ - /* initial structure. Used for allocating space that is aligned on */ - /* a decNumber struct boundary. */ - #define D2N(d) \ - ((((SD2U(d)-1)*sizeof(Unit))+sizeof(decNumber)*2-1)/sizeof(decNumber)) - - /* TODIGIT -- macro to remove the leading digit from the unsigned */ - /* integer u at column cut (counting from the right, LSD=0) and */ - /* place it as an ASCII character into the character pointed to by */ - /* c. Note that cut must be <= 9, and the maximum value for u is */ - /* 2,000,000,000 (as is needed for negative exponents of */ - /* subnormals). The unsigned integer pow is used as a temporary */ - /* variable. */ - #define TODIGIT(u, cut, c, pow) UPRV_BLOCK_MACRO_BEGIN { \ - *(c)='0'; \ - pow=DECPOWERS[cut]*2; \ - if ((u)>pow) { \ - pow*=4; \ - if ((u)>=pow) {(u)-=pow; *(c)+=8;} \ - pow/=2; \ - if ((u)>=pow) {(u)-=pow; *(c)+=4;} \ - pow/=2; \ - } \ - if ((u)>=pow) {(u)-=pow; *(c)+=2;} \ - pow/=2; \ - if ((u)>=pow) {(u)-=pow; *(c)+=1;} \ - } UPRV_BLOCK_MACRO_END - - /* ---------------------------------------------------------------- */ - /* Definitions for fixed-precision modules (only valid after */ - /* decSingle.h, decDouble.h, or decQuad.h has been included) */ - /* ---------------------------------------------------------------- */ - - /* bcdnum -- a structure describing a format-independent finite */ - /* number, whose coefficient is a string of bcd8 uBytes */ - typedef struct { - uByte *msd; /* -> most significant digit */ - uByte *lsd; /* -> least ditto */ - uInt sign; /* 0=positive, DECFLOAT_Sign=negative */ - Int exponent; /* Unadjusted signed exponent (q), or */ - /* DECFLOAT_NaN etc. for a special */ - } bcdnum; - - /* Test if exponent or bcdnum exponent must be a special, etc. */ - #define EXPISSPECIAL(exp) ((exp)>=DECFLOAT_MinSp) - #define EXPISINF(exp) (exp==DECFLOAT_Inf) - #define EXPISNAN(exp) (exp==DECFLOAT_qNaN || exp==DECFLOAT_sNaN) - #define NUMISSPECIAL(num) (EXPISSPECIAL((num)->exponent)) - - /* Refer to a 32-bit word or byte in a decFloat (df) by big-endian */ - /* (array) notation (the 0 word or byte contains the sign bit), */ - /* automatically adjusting for endianness; similarly address a word */ - /* in the next-wider format (decFloatWider, or dfw) */ - #define DECWORDS (DECBYTES/4) - #define DECWWORDS (DECWBYTES/4) - #if DECLITEND - #define DFBYTE(df, off) ((df)->bytes[DECBYTES-1-(off)]) - #define DFWORD(df, off) ((df)->words[DECWORDS-1-(off)]) - #define DFWWORD(dfw, off) ((dfw)->words[DECWWORDS-1-(off)]) - #else - #define DFBYTE(df, off) ((df)->bytes[off]) - #define DFWORD(df, off) ((df)->words[off]) - #define DFWWORD(dfw, off) ((dfw)->words[off]) - #endif - - /* Tests for sign or specials, directly on DECFLOATs */ - #define DFISSIGNED(df) (DFWORD(df, 0)&0x80000000) - #define DFISSPECIAL(df) ((DFWORD(df, 0)&0x78000000)==0x78000000) - #define DFISINF(df) ((DFWORD(df, 0)&0x7c000000)==0x78000000) - #define DFISNAN(df) ((DFWORD(df, 0)&0x7c000000)==0x7c000000) - #define DFISQNAN(df) ((DFWORD(df, 0)&0x7e000000)==0x7c000000) - #define DFISSNAN(df) ((DFWORD(df, 0)&0x7e000000)==0x7e000000) - - /* Shared lookup tables */ - extern const uInt DECCOMBMSD[64]; /* Combination field -> MSD */ - extern const uInt DECCOMBFROM[48]; /* exp+msd -> Combination */ - - /* Private generic (utility) routine */ - #if DECCHECK || DECTRACE - extern void decShowNum(const bcdnum *, const char *); - #endif - - /* Format-dependent macros and constants */ - #if defined(DECPMAX) - - /* Useful constants */ - #define DECPMAX9 (ROUNDUP(DECPMAX, 9)/9) /* 'Pmax' in 10**9s */ - /* Top words for a zero */ - #define SINGLEZERO 0x22500000 - #define DOUBLEZERO 0x22380000 - #define QUADZERO 0x22080000 - /* [ZEROWORD is defined to be one of these in the DFISZERO macro] */ - - /* Format-dependent common tests: */ - /* DFISZERO -- test for (any) zero */ - /* DFISCCZERO -- test for coefficient continuation being zero */ - /* DFISCC01 -- test for coefficient contains only 0s and 1s */ - /* DFISINT -- test for finite and exponent q=0 */ - /* DFISUINT01 -- test for sign=0, finite, exponent q=0, and */ - /* MSD=0 or 1 */ - /* ZEROWORD is also defined here. */ - /* In DFISZERO the first test checks the least-significant word */ - /* (most likely to be non-zero); the penultimate tests MSD and */ - /* DPDs in the signword, and the final test excludes specials and */ - /* MSD>7. DFISINT similarly has to allow for the two forms of */ - /* MSD codes. DFISUINT01 only has to allow for one form of MSD */ - /* code. */ - #if DECPMAX==7 - #define ZEROWORD SINGLEZERO - /* [test macros not needed except for Zero] */ - #define DFISZERO(df) ((DFWORD(df, 0)&0x1c0fffff)==0 \ - && (DFWORD(df, 0)&0x60000000)!=0x60000000) - #elif DECPMAX==16 - #define ZEROWORD DOUBLEZERO - #define DFISZERO(df) ((DFWORD(df, 1)==0 \ - && (DFWORD(df, 0)&0x1c03ffff)==0 \ - && (DFWORD(df, 0)&0x60000000)!=0x60000000)) - #define DFISINT(df) ((DFWORD(df, 0)&0x63fc0000)==0x22380000 \ - ||(DFWORD(df, 0)&0x7bfc0000)==0x6a380000) - #define DFISUINT01(df) ((DFWORD(df, 0)&0xfbfc0000)==0x22380000) - #define DFISCCZERO(df) (DFWORD(df, 1)==0 \ - && (DFWORD(df, 0)&0x0003ffff)==0) - #define DFISCC01(df) ((DFWORD(df, 0)&~0xfffc9124)==0 \ - && (DFWORD(df, 1)&~0x49124491)==0) - #elif DECPMAX==34 - #define ZEROWORD QUADZERO - #define DFISZERO(df) ((DFWORD(df, 3)==0 \ - && DFWORD(df, 2)==0 \ - && DFWORD(df, 1)==0 \ - && (DFWORD(df, 0)&0x1c003fff)==0 \ - && (DFWORD(df, 0)&0x60000000)!=0x60000000)) - #define DFISINT(df) ((DFWORD(df, 0)&0x63ffc000)==0x22080000 \ - ||(DFWORD(df, 0)&0x7bffc000)==0x6a080000) - #define DFISUINT01(df) ((DFWORD(df, 0)&0xfbffc000)==0x22080000) - #define DFISCCZERO(df) (DFWORD(df, 3)==0 \ - && DFWORD(df, 2)==0 \ - && DFWORD(df, 1)==0 \ - && (DFWORD(df, 0)&0x00003fff)==0) - - #define DFISCC01(df) ((DFWORD(df, 0)&~0xffffc912)==0 \ - && (DFWORD(df, 1)&~0x44912449)==0 \ - && (DFWORD(df, 2)&~0x12449124)==0 \ - && (DFWORD(df, 3)&~0x49124491)==0) - #endif - - /* Macros to test if a certain 10 bits of a uInt or pair of uInts */ - /* are a canonical declet [higher or lower bits are ignored]. */ - /* declet is at offset 0 (from the right) in a uInt: */ - #define CANONDPD(dpd) (((dpd)&0x300)==0 || ((dpd)&0x6e)!=0x6e) - /* declet is at offset k (a multiple of 2) in a uInt: */ - #define CANONDPDOFF(dpd, k) (((dpd)&(0x300<<(k)))==0 \ - || ((dpd)&(((uInt)0x6e)<<(k)))!=(((uInt)0x6e)<<(k))) - /* declet is at offset k (a multiple of 2) in a pair of uInts: */ - /* [the top 2 bits will always be in the more-significant uInt] */ - #define CANONDPDTWO(hi, lo, k) (((hi)&(0x300>>(32-(k))))==0 \ - || ((hi)&(0x6e>>(32-(k))))!=(0x6e>>(32-(k))) \ - || ((lo)&(((uInt)0x6e)<<(k)))!=(((uInt)0x6e)<<(k))) - - /* Macro to test whether a full-length (length DECPMAX) BCD8 */ - /* coefficient, starting at uByte u, is all zeros */ - /* Test just the LSWord first, then the remainder as a sequence */ - /* of tests in order to avoid same-level use of UBTOUI */ - #if DECPMAX==7 - #define ISCOEFFZERO(u) ( \ - UBTOUI((u)+DECPMAX-4)==0 \ - && UBTOUS((u)+DECPMAX-6)==0 \ - && *(u)==0) - #elif DECPMAX==16 - #define ISCOEFFZERO(u) ( \ - UBTOUI((u)+DECPMAX-4)==0 \ - && UBTOUI((u)+DECPMAX-8)==0 \ - && UBTOUI((u)+DECPMAX-12)==0 \ - && UBTOUI(u)==0) - #elif DECPMAX==34 - #define ISCOEFFZERO(u) ( \ - UBTOUI((u)+DECPMAX-4)==0 \ - && UBTOUI((u)+DECPMAX-8)==0 \ - && UBTOUI((u)+DECPMAX-12)==0 \ - && UBTOUI((u)+DECPMAX-16)==0 \ - && UBTOUI((u)+DECPMAX-20)==0 \ - && UBTOUI((u)+DECPMAX-24)==0 \ - && UBTOUI((u)+DECPMAX-28)==0 \ - && UBTOUI((u)+DECPMAX-32)==0 \ - && UBTOUS(u)==0) - #endif - - /* Macros and masks for the exponent continuation field and MSD */ - /* Get the exponent continuation from a decFloat *df as an Int */ - #define GETECON(df) ((Int)((DFWORD((df), 0)&0x03ffffff)>>(32-6-DECECONL))) - /* Ditto, from the next-wider format */ - #define GETWECON(df) ((Int)((DFWWORD((df), 0)&0x03ffffff)>>(32-6-DECWECONL))) - /* Get the biased exponent similarly */ - #define GETEXP(df) ((Int)(DECCOMBEXP[DFWORD((df), 0)>>26]+GETECON(df))) - /* Get the unbiased exponent similarly */ - #define GETEXPUN(df) ((Int)GETEXP(df)-DECBIAS) - /* Get the MSD similarly (as uInt) */ - #define GETMSD(df) (DECCOMBMSD[DFWORD((df), 0)>>26]) - - /* Compile-time computes of the exponent continuation field masks */ - /* full exponent continuation field: */ - #define ECONMASK ((0x03ffffff>>(32-6-DECECONL))<<(32-6-DECECONL)) - /* same, not including its first digit (the qNaN/sNaN selector): */ - #define ECONNANMASK ((0x01ffffff>>(32-6-DECECONL))<<(32-6-DECECONL)) - - /* Macros to decode the coefficient in a finite decFloat *df into */ - /* a BCD string (uByte *bcdin) of length DECPMAX uBytes. */ - - /* In-line sequence to convert least significant 10 bits of uInt */ - /* dpd to three BCD8 digits starting at uByte u. Note that an */ - /* extra byte is written to the right of the three digits because */ - /* four bytes are moved at a time for speed; the alternative */ - /* macro moves exactly three bytes (usually slower). */ - #define dpd2bcd8(u, dpd) memcpy(u, &DPD2BCD8[((dpd)&0x3ff)*4], 4) - #define dpd2bcd83(u, dpd) memcpy(u, &DPD2BCD8[((dpd)&0x3ff)*4], 3) - - /* Decode the declets. After extracting each one, it is decoded */ - /* to BCD8 using a table lookup (also used for variable-length */ - /* decode). Each DPD decode is 3 bytes BCD8 plus a one-byte */ - /* length which is not used, here). Fixed-length 4-byte moves */ - /* are fast, however, almost everywhere, and so are used except */ - /* for the final three bytes (to avoid overrun). The code below */ - /* is 36 instructions for Doubles and about 70 for Quads, even */ - /* on IA32. */ - - /* Two macros are defined for each format: */ - /* GETCOEFF extracts the coefficient of the current format */ - /* GETWCOEFF extracts the coefficient of the next-wider format. */ - /* The latter is a copy of the next-wider GETCOEFF using DFWWORD. */ - - #if DECPMAX==7 - #define GETCOEFF(df, bcd) { \ - uInt sourhi=DFWORD(df, 0); \ - *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ - dpd2bcd8(bcd+1, sourhi>>10); \ - dpd2bcd83(bcd+4, sourhi);} - #define GETWCOEFF(df, bcd) { \ - uInt sourhi=DFWWORD(df, 0); \ - uInt sourlo=DFWWORD(df, 1); \ - *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ - dpd2bcd8(bcd+1, sourhi>>8); \ - dpd2bcd8(bcd+4, (sourhi<<2) | (sourlo>>30)); \ - dpd2bcd8(bcd+7, sourlo>>20); \ - dpd2bcd8(bcd+10, sourlo>>10); \ - dpd2bcd83(bcd+13, sourlo);} - - #elif DECPMAX==16 - #define GETCOEFF(df, bcd) { \ - uInt sourhi=DFWORD(df, 0); \ - uInt sourlo=DFWORD(df, 1); \ - *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ - dpd2bcd8(bcd+1, sourhi>>8); \ - dpd2bcd8(bcd+4, (sourhi<<2) | (sourlo>>30)); \ - dpd2bcd8(bcd+7, sourlo>>20); \ - dpd2bcd8(bcd+10, sourlo>>10); \ - dpd2bcd83(bcd+13, sourlo);} - #define GETWCOEFF(df, bcd) { \ - uInt sourhi=DFWWORD(df, 0); \ - uInt sourmh=DFWWORD(df, 1); \ - uInt sourml=DFWWORD(df, 2); \ - uInt sourlo=DFWWORD(df, 3); \ - *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ - dpd2bcd8(bcd+1, sourhi>>4); \ - dpd2bcd8(bcd+4, ((sourhi)<<6) | (sourmh>>26)); \ - dpd2bcd8(bcd+7, sourmh>>16); \ - dpd2bcd8(bcd+10, sourmh>>6); \ - dpd2bcd8(bcd+13, ((sourmh)<<4) | (sourml>>28)); \ - dpd2bcd8(bcd+16, sourml>>18); \ - dpd2bcd8(bcd+19, sourml>>8); \ - dpd2bcd8(bcd+22, ((sourml)<<2) | (sourlo>>30)); \ - dpd2bcd8(bcd+25, sourlo>>20); \ - dpd2bcd8(bcd+28, sourlo>>10); \ - dpd2bcd83(bcd+31, sourlo);} - - #elif DECPMAX==34 - #define GETCOEFF(df, bcd) { \ - uInt sourhi=DFWORD(df, 0); \ - uInt sourmh=DFWORD(df, 1); \ - uInt sourml=DFWORD(df, 2); \ - uInt sourlo=DFWORD(df, 3); \ - *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ - dpd2bcd8(bcd+1, sourhi>>4); \ - dpd2bcd8(bcd+4, ((sourhi)<<6) | (sourmh>>26)); \ - dpd2bcd8(bcd+7, sourmh>>16); \ - dpd2bcd8(bcd+10, sourmh>>6); \ - dpd2bcd8(bcd+13, ((sourmh)<<4) | (sourml>>28)); \ - dpd2bcd8(bcd+16, sourml>>18); \ - dpd2bcd8(bcd+19, sourml>>8); \ - dpd2bcd8(bcd+22, ((sourml)<<2) | (sourlo>>30)); \ - dpd2bcd8(bcd+25, sourlo>>20); \ - dpd2bcd8(bcd+28, sourlo>>10); \ - dpd2bcd83(bcd+31, sourlo);} - - #define GETWCOEFF(df, bcd) {??} /* [should never be used] */ - #endif - - /* Macros to decode the coefficient in a finite decFloat *df into */ - /* a base-billion uInt array, with the least-significant */ - /* 0-999999999 'digit' at offset 0. */ - - /* Decode the declets. After extracting each one, it is decoded */ - /* to binary using a table lookup. Three tables are used; one */ - /* the usual DPD to binary, the other two pre-multiplied by 1000 */ - /* and 1000000 to avoid multiplication during decode. These */ - /* tables can also be used for multiplying up the MSD as the DPD */ - /* code for 0 through 9 is the identity. */ - #define DPD2BIN0 DPD2BIN /* for prettier code */ - - #if DECPMAX==7 - #define GETCOEFFBILL(df, buf) { \ - uInt sourhi=DFWORD(df, 0); \ - (buf)[0]=DPD2BIN0[sourhi&0x3ff] \ - +DPD2BINK[(sourhi>>10)&0x3ff] \ - +DPD2BINM[DECCOMBMSD[sourhi>>26]];} - - #elif DECPMAX==16 - #define GETCOEFFBILL(df, buf) { \ - uInt sourhi, sourlo; \ - sourlo=DFWORD(df, 1); \ - (buf)[0]=DPD2BIN0[sourlo&0x3ff] \ - +DPD2BINK[(sourlo>>10)&0x3ff] \ - +DPD2BINM[(sourlo>>20)&0x3ff]; \ - sourhi=DFWORD(df, 0); \ - (buf)[1]=DPD2BIN0[((sourhi<<2) | (sourlo>>30))&0x3ff] \ - +DPD2BINK[(sourhi>>8)&0x3ff] \ - +DPD2BINM[DECCOMBMSD[sourhi>>26]];} - - #elif DECPMAX==34 - #define GETCOEFFBILL(df, buf) { \ - uInt sourhi, sourmh, sourml, sourlo; \ - sourlo=DFWORD(df, 3); \ - (buf)[0]=DPD2BIN0[sourlo&0x3ff] \ - +DPD2BINK[(sourlo>>10)&0x3ff] \ - +DPD2BINM[(sourlo>>20)&0x3ff]; \ - sourml=DFWORD(df, 2); \ - (buf)[1]=DPD2BIN0[((sourml<<2) | (sourlo>>30))&0x3ff] \ - +DPD2BINK[(sourml>>8)&0x3ff] \ - +DPD2BINM[(sourml>>18)&0x3ff]; \ - sourmh=DFWORD(df, 1); \ - (buf)[2]=DPD2BIN0[((sourmh<<4) | (sourml>>28))&0x3ff] \ - +DPD2BINK[(sourmh>>6)&0x3ff] \ - +DPD2BINM[(sourmh>>16)&0x3ff]; \ - sourhi=DFWORD(df, 0); \ - (buf)[3]=DPD2BIN0[((sourhi<<6) | (sourmh>>26))&0x3ff] \ - +DPD2BINK[(sourhi>>4)&0x3ff] \ - +DPD2BINM[DECCOMBMSD[sourhi>>26]];} - - #endif - - /* Macros to decode the coefficient in a finite decFloat *df into */ - /* a base-thousand uInt array (of size DECLETS+1, to allow for */ - /* the MSD), with the least-significant 0-999 'digit' at offset 0.*/ - - /* Decode the declets. After extracting each one, it is decoded */ - /* to binary using a table lookup. */ - #if DECPMAX==7 - #define GETCOEFFTHOU(df, buf) { \ - uInt sourhi=DFWORD(df, 0); \ - (buf)[0]=DPD2BIN[sourhi&0x3ff]; \ - (buf)[1]=DPD2BIN[(sourhi>>10)&0x3ff]; \ - (buf)[2]=DECCOMBMSD[sourhi>>26];} - - #elif DECPMAX==16 - #define GETCOEFFTHOU(df, buf) { \ - uInt sourhi, sourlo; \ - sourlo=DFWORD(df, 1); \ - (buf)[0]=DPD2BIN[sourlo&0x3ff]; \ - (buf)[1]=DPD2BIN[(sourlo>>10)&0x3ff]; \ - (buf)[2]=DPD2BIN[(sourlo>>20)&0x3ff]; \ - sourhi=DFWORD(df, 0); \ - (buf)[3]=DPD2BIN[((sourhi<<2) | (sourlo>>30))&0x3ff]; \ - (buf)[4]=DPD2BIN[(sourhi>>8)&0x3ff]; \ - (buf)[5]=DECCOMBMSD[sourhi>>26];} - - #elif DECPMAX==34 - #define GETCOEFFTHOU(df, buf) { \ - uInt sourhi, sourmh, sourml, sourlo; \ - sourlo=DFWORD(df, 3); \ - (buf)[0]=DPD2BIN[sourlo&0x3ff]; \ - (buf)[1]=DPD2BIN[(sourlo>>10)&0x3ff]; \ - (buf)[2]=DPD2BIN[(sourlo>>20)&0x3ff]; \ - sourml=DFWORD(df, 2); \ - (buf)[3]=DPD2BIN[((sourml<<2) | (sourlo>>30))&0x3ff]; \ - (buf)[4]=DPD2BIN[(sourml>>8)&0x3ff]; \ - (buf)[5]=DPD2BIN[(sourml>>18)&0x3ff]; \ - sourmh=DFWORD(df, 1); \ - (buf)[6]=DPD2BIN[((sourmh<<4) | (sourml>>28))&0x3ff]; \ - (buf)[7]=DPD2BIN[(sourmh>>6)&0x3ff]; \ - (buf)[8]=DPD2BIN[(sourmh>>16)&0x3ff]; \ - sourhi=DFWORD(df, 0); \ - (buf)[9]=DPD2BIN[((sourhi<<6) | (sourmh>>26))&0x3ff]; \ - (buf)[10]=DPD2BIN[(sourhi>>4)&0x3ff]; \ - (buf)[11]=DECCOMBMSD[sourhi>>26];} - #endif - - - /* Macros to decode the coefficient in a finite decFloat *df and */ - /* add to a base-thousand uInt array (as for GETCOEFFTHOU). */ - /* After the addition then most significant 'digit' in the array */ - /* might have a value larger then 10 (with a maximum of 19). */ - #if DECPMAX==7 - #define ADDCOEFFTHOU(df, buf) { \ - uInt sourhi=DFWORD(df, 0); \ - (buf)[0]+=DPD2BIN[sourhi&0x3ff]; \ - if (buf[0]>999) {buf[0]-=1000; buf[1]++;} \ - (buf)[1]+=DPD2BIN[(sourhi>>10)&0x3ff]; \ - if (buf[1]>999) {buf[1]-=1000; buf[2]++;} \ - (buf)[2]+=DECCOMBMSD[sourhi>>26];} - - #elif DECPMAX==16 - #define ADDCOEFFTHOU(df, buf) { \ - uInt sourhi, sourlo; \ - sourlo=DFWORD(df, 1); \ - (buf)[0]+=DPD2BIN[sourlo&0x3ff]; \ - if (buf[0]>999) {buf[0]-=1000; buf[1]++;} \ - (buf)[1]+=DPD2BIN[(sourlo>>10)&0x3ff]; \ - if (buf[1]>999) {buf[1]-=1000; buf[2]++;} \ - (buf)[2]+=DPD2BIN[(sourlo>>20)&0x3ff]; \ - if (buf[2]>999) {buf[2]-=1000; buf[3]++;} \ - sourhi=DFWORD(df, 0); \ - (buf)[3]+=DPD2BIN[((sourhi<<2) | (sourlo>>30))&0x3ff]; \ - if (buf[3]>999) {buf[3]-=1000; buf[4]++;} \ - (buf)[4]+=DPD2BIN[(sourhi>>8)&0x3ff]; \ - if (buf[4]>999) {buf[4]-=1000; buf[5]++;} \ - (buf)[5]+=DECCOMBMSD[sourhi>>26];} - - #elif DECPMAX==34 - #define ADDCOEFFTHOU(df, buf) { \ - uInt sourhi, sourmh, sourml, sourlo; \ - sourlo=DFWORD(df, 3); \ - (buf)[0]+=DPD2BIN[sourlo&0x3ff]; \ - if (buf[0]>999) {buf[0]-=1000; buf[1]++;} \ - (buf)[1]+=DPD2BIN[(sourlo>>10)&0x3ff]; \ - if (buf[1]>999) {buf[1]-=1000; buf[2]++;} \ - (buf)[2]+=DPD2BIN[(sourlo>>20)&0x3ff]; \ - if (buf[2]>999) {buf[2]-=1000; buf[3]++;} \ - sourml=DFWORD(df, 2); \ - (buf)[3]+=DPD2BIN[((sourml<<2) | (sourlo>>30))&0x3ff]; \ - if (buf[3]>999) {buf[3]-=1000; buf[4]++;} \ - (buf)[4]+=DPD2BIN[(sourml>>8)&0x3ff]; \ - if (buf[4]>999) {buf[4]-=1000; buf[5]++;} \ - (buf)[5]+=DPD2BIN[(sourml>>18)&0x3ff]; \ - if (buf[5]>999) {buf[5]-=1000; buf[6]++;} \ - sourmh=DFWORD(df, 1); \ - (buf)[6]+=DPD2BIN[((sourmh<<4) | (sourml>>28))&0x3ff]; \ - if (buf[6]>999) {buf[6]-=1000; buf[7]++;} \ - (buf)[7]+=DPD2BIN[(sourmh>>6)&0x3ff]; \ - if (buf[7]>999) {buf[7]-=1000; buf[8]++;} \ - (buf)[8]+=DPD2BIN[(sourmh>>16)&0x3ff]; \ - if (buf[8]>999) {buf[8]-=1000; buf[9]++;} \ - sourhi=DFWORD(df, 0); \ - (buf)[9]+=DPD2BIN[((sourhi<<6) | (sourmh>>26))&0x3ff]; \ - if (buf[9]>999) {buf[9]-=1000; buf[10]++;} \ - (buf)[10]+=DPD2BIN[(sourhi>>4)&0x3ff]; \ - if (buf[10]>999) {buf[10]-=1000; buf[11]++;} \ - (buf)[11]+=DECCOMBMSD[sourhi>>26];} - #endif - - - /* Set a decFloat to the maximum positive finite number (Nmax) */ - #if DECPMAX==7 - #define DFSETNMAX(df) \ - {DFWORD(df, 0)=0x77f3fcff;} - #elif DECPMAX==16 - #define DFSETNMAX(df) \ - {DFWORD(df, 0)=0x77fcff3f; \ - DFWORD(df, 1)=0xcff3fcff;} - #elif DECPMAX==34 - #define DFSETNMAX(df) \ - {DFWORD(df, 0)=0x77ffcff3; \ - DFWORD(df, 1)=0xfcff3fcf; \ - DFWORD(df, 2)=0xf3fcff3f; \ - DFWORD(df, 3)=0xcff3fcff;} - #endif - - /* [end of format-dependent macros and constants] */ - #endif - -#else - #error decNumberLocal included more than once -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* ------------------------------------------------------------------ */ +/* decNumber package local type, tuning, and macro definitions */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000-2016. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This header file is included by all modules in the decNumber */ +/* library, and contains local type definitions, tuning parameters, */ +/* etc. It should not need to be used by application programs. */ +/* decNumber.h or one of decDouble (etc.) must be included first. */ +/* ------------------------------------------------------------------ */ + +#if !defined(DECNUMBERLOC) + #define DECNUMBERLOC + #define DECVERSION "decNumber 3.61" /* Package Version [16 max.] */ + #define DECNLAUTHOR "Mike Cowlishaw" /* Who to blame */ + + #include /* for abs */ + #include /* for memset, strcpy */ + #include "decContext.h" + + /* Conditional code flag -- set this to match hardware platform */ + #if !defined(DECLITEND) + #define DECLITEND 1 /* 1=little-endian, 0=big-endian */ + #endif + + /* Conditional code flag -- set this to 1 for best performance */ + #if !defined(DECUSE64) + #define DECUSE64 1 /* 1=use int64s, 0=int32 & smaller only */ + #endif + + /* Conditional check flags -- set these to 0 for best performance */ + #if !defined(DECCHECK) + #define DECCHECK 0 /* 1 to enable robust checking */ + #endif + #if !defined(DECALLOC) + #define DECALLOC 0 /* 1 to enable memory accounting */ + #endif + #if !defined(DECTRACE) + #define DECTRACE 0 /* 1 to trace certain internals, etc. */ + #endif + + /* Tuning parameter for decNumber (arbitrary precision) module */ + #if !defined(DECBUFFER) + #define DECBUFFER 36 /* Size basis for local buffers. This */ + /* should be a common maximum precision */ + /* rounded up to a multiple of 4; must */ + /* be zero or positive. */ + #endif + + /* ---------------------------------------------------------------- */ + /* Definitions for all modules (general-purpose) */ + /* ---------------------------------------------------------------- */ + + /* Local names for common types -- for safety, decNumber modules do */ + /* not use int or long directly. */ + #define Flag uint8_t + #define Byte int8_t + #define uByte uint8_t + #define Short int16_t + #define uShort uint16_t + #define Int int32_t + #define uInt uint32_t + #define Unit decNumberUnit + #if DECUSE64 + #define Long int64_t + #define uLong uint64_t + #endif + + /* Development-use definitions */ + typedef long int LI; /* for printf arguments only */ + #define DECNOINT 0 /* 1 to check no internal use of 'int' */ + /* or stdint types */ + #if DECNOINT + /* if these interfere with your C includes, do not set DECNOINT */ + #define int ? /* enable to ensure that plain C 'int' */ + #define long ?? /* .. or 'long' types are not used */ + #endif + + /* LONGMUL32HI -- set w=(u*v)>>32, where w, u, and v are uInts */ + /* (that is, sets w to be the high-order word of the 64-bit result; */ + /* the low-order word is simply u*v.) */ + /* This version is derived from Knuth via Hacker's Delight; */ + /* it seems to optimize better than some others tried */ + #define LONGMUL32HI(w, u, v) { \ + uInt u0, u1, v0, v1, w0, w1, w2, t; \ + u0=u & 0xffff; u1=u>>16; \ + v0=v & 0xffff; v1=v>>16; \ + w0=u0*v0; \ + t=u1*v0 + (w0>>16); \ + w1=t & 0xffff; w2=t>>16; \ + w1=u0*v1 + w1; \ + (w)=u1*v1 + w2 + (w1>>16);} + + /* ROUNDUP -- round an integer up to a multiple of n */ + #define ROUNDUP(i, n) ((((i)+(n)-1)/n)*n) + #define ROUNDUP4(i) (((i)+3)&~3) /* special for n=4 */ + + /* ROUNDDOWN -- round an integer down to a multiple of n */ + #define ROUNDDOWN(i, n) (((i)/n)*n) + #define ROUNDDOWN4(i) ((i)&~3) /* special for n=4 */ + + /* References to multi-byte sequences under different sizes; these */ + /* require locally declared variables, but do not violate strict */ + /* aliasing or alignment (as did the UINTAT simple cast to uInt). */ + /* Variables needed are uswork, uiwork, etc. [so do not use at same */ + /* level in an expression, e.g., UBTOUI(x)==UBTOUI(y) may fail]. */ + + /* Return a uInt, etc., from bytes starting at a char* or uByte* */ + #define UBTOUS(b) (memcpy((void *)&uswork, b, 2), uswork) + #define UBTOUI(b) (memcpy((void *)&uiwork, b, 4), uiwork) + + /* Store a uInt, etc., into bytes starting at a char* or uByte*. */ + /* Returns i, evaluated, for convenience; has to use uiwork because */ + /* i may be an expression. */ + #define UBFROMUS(b, i) (uswork=(i), memcpy(b, (void *)&uswork, 2), uswork) + #define UBFROMUI(b, i) (uiwork=(i), memcpy(b, (void *)&uiwork, 4), uiwork) + + /* X10 and X100 -- multiply integer i by 10 or 100 */ + /* [shifts are usually faster than multiply; could be conditional] */ + #define X10(i) (((i)<<1)+((i)<<3)) + #define X100(i) (((i)<<2)+((i)<<5)+((i)<<6)) + + /* MAXI and MINI -- general max & min (not in ANSI) for integers */ + #define MAXI(x,y) ((x)<(y)?(y):(x)) + #define MINI(x,y) ((x)>(y)?(y):(x)) + + /* Useful constants */ + #define BILLION 1000000000 /* 10**9 */ + /* CHARMASK: 0x30303030 for ASCII/UTF8; 0xF0F0F0F0 for EBCDIC */ + #define CHARMASK ((((((((uInt)'0')<<8)+'0')<<8)+'0')<<8)+'0') + + + /* ---------------------------------------------------------------- */ + /* Definitions for arbitrary-precision modules (only valid after */ + /* decNumber.h has been included) */ + /* ---------------------------------------------------------------- */ + + /* Limits and constants */ + #define DECNUMMAXP 999999999 /* maximum precision code can handle */ + #define DECNUMMAXE 999999999 /* maximum adjusted exponent ditto */ + #define DECNUMMINE -999999999 /* minimum adjusted exponent ditto */ + #if (DECNUMMAXP != DEC_MAX_DIGITS) + #error Maximum digits mismatch + #endif + #if (DECNUMMAXE != DEC_MAX_EMAX) + #error Maximum exponent mismatch + #endif + #if (DECNUMMINE != DEC_MIN_EMIN) + #error Minimum exponent mismatch + #endif + + /* Set DECDPUNMAX -- the maximum integer that fits in DECDPUN */ + /* digits, and D2UTABLE -- the initializer for the D2U table */ + #ifndef DECDPUN + // no-op + #elif DECDPUN==1 + #define DECDPUNMAX 9 + #define D2UTABLE {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17, \ + 18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, \ + 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, \ + 48,49} + #elif DECDPUN==2 + #define DECDPUNMAX 99 + #define D2UTABLE {0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10, \ + 11,11,12,12,13,13,14,14,15,15,16,16,17,17,18, \ + 18,19,19,20,20,21,21,22,22,23,23,24,24,25} + #elif DECDPUN==3 + #define DECDPUNMAX 999 + #define D2UTABLE {0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7,7, \ + 8,8,8,9,9,9,10,10,10,11,11,11,12,12,12,13,13, \ + 13,14,14,14,15,15,15,16,16,16,17} + #elif DECDPUN==4 + #define DECDPUNMAX 9999 + #define D2UTABLE {0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6, \ + 6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11, \ + 11,11,11,12,12,12,12,13} + #elif DECDPUN==5 + #define DECDPUNMAX 99999 + #define D2UTABLE {0,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5, \ + 5,5,5,5,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8,9,9,9, \ + 9,9,10,10,10,10} + #elif DECDPUN==6 + #define DECDPUNMAX 999999 + #define D2UTABLE {0,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4, \ + 4,4,4,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,7,7,8, \ + 8,8,8,8,8,9} + #elif DECDPUN==7 + #define DECDPUNMAX 9999999 + #define D2UTABLE {0,1,1,1,1,1,1,1,2,2,2,2,2,2,2,3,3,3,3,3,3,3, \ + 4,4,4,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7, \ + 7,7,7,7,7,7} + #elif DECDPUN==8 + #define DECDPUNMAX 99999999 + #define D2UTABLE {0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3, \ + 3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,6,6,6, \ + 6,6,6,6,6,7} + #elif DECDPUN==9 + #define DECDPUNMAX 999999999 + #define D2UTABLE {0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,3, \ + 3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5, \ + 5,5,6,6,6,6} + #else + #error DECDPUN must be in the range 1-9 + #endif + + /* ----- Shared data (in decNumber.c) ----- */ + /* Public lookup table used by the D2U macro (see below) */ + #define DECMAXD2U 49 + /*extern const uByte d2utable[DECMAXD2U+1];*/ + + /* ----- Macros ----- */ + /* ISZERO -- return true if decNumber dn is a zero */ + /* [performance-critical in some situations] */ + #define ISZERO(dn) decNumberIsZero(dn) /* now just a local name */ + + /* D2U -- return the number of Units needed to hold d digits */ + /* (runtime version, with table lookaside for small d) */ + #if defined(DECDPUN) && DECDPUN==8 + #define D2U(d) ((unsigned)((d)<=DECMAXD2U?d2utable[d]:((d)+7)>>3)) + #elif defined(DECDPUN) && DECDPUN==4 + #define D2U(d) ((unsigned)((d)<=DECMAXD2U?d2utable[d]:((d)+3)>>2)) + #else + #define D2U(d) ((d)<=DECMAXD2U?d2utable[d]:((d)+DECDPUN-1)/DECDPUN) + #endif + /* SD2U -- static D2U macro (for compile-time calculation) */ + #define SD2U(d) (((d)+DECDPUN-1)/DECDPUN) + + /* MSUDIGITS -- returns digits in msu, from digits, calculated */ + /* using D2U */ + #define MSUDIGITS(d) ((d)-(D2U(d)-1)*DECDPUN) + + /* D2N -- return the number of decNumber structs that would be */ + /* needed to contain that number of digits (and the initial */ + /* decNumber struct) safely. Note that one Unit is included in the */ + /* initial structure. Used for allocating space that is aligned on */ + /* a decNumber struct boundary. */ + #define D2N(d) \ + ((((SD2U(d)-1)*sizeof(Unit))+sizeof(decNumber)*2-1)/sizeof(decNumber)) + + /* TODIGIT -- macro to remove the leading digit from the unsigned */ + /* integer u at column cut (counting from the right, LSD=0) and */ + /* place it as an ASCII character into the character pointed to by */ + /* c. Note that cut must be <= 9, and the maximum value for u is */ + /* 2,000,000,000 (as is needed for negative exponents of */ + /* subnormals). The unsigned integer pow is used as a temporary */ + /* variable. */ + #define TODIGIT(u, cut, c, pow) UPRV_BLOCK_MACRO_BEGIN { \ + *(c)='0'; \ + pow=DECPOWERS[cut]*2; \ + if ((u)>pow) { \ + pow*=4; \ + if ((u)>=pow) {(u)-=pow; *(c)+=8;} \ + pow/=2; \ + if ((u)>=pow) {(u)-=pow; *(c)+=4;} \ + pow/=2; \ + } \ + if ((u)>=pow) {(u)-=pow; *(c)+=2;} \ + pow/=2; \ + if ((u)>=pow) {(u)-=pow; *(c)+=1;} \ + } UPRV_BLOCK_MACRO_END + + /* ---------------------------------------------------------------- */ + /* Definitions for fixed-precision modules (only valid after */ + /* decSingle.h, decDouble.h, or decQuad.h has been included) */ + /* ---------------------------------------------------------------- */ + + /* bcdnum -- a structure describing a format-independent finite */ + /* number, whose coefficient is a string of bcd8 uBytes */ + typedef struct { + uByte *msd; /* -> most significant digit */ + uByte *lsd; /* -> least ditto */ + uInt sign; /* 0=positive, DECFLOAT_Sign=negative */ + Int exponent; /* Unadjusted signed exponent (q), or */ + /* DECFLOAT_NaN etc. for a special */ + } bcdnum; + + /* Test if exponent or bcdnum exponent must be a special, etc. */ + #define EXPISSPECIAL(exp) ((exp)>=DECFLOAT_MinSp) + #define EXPISINF(exp) (exp==DECFLOAT_Inf) + #define EXPISNAN(exp) (exp==DECFLOAT_qNaN || exp==DECFLOAT_sNaN) + #define NUMISSPECIAL(num) (EXPISSPECIAL((num)->exponent)) + + /* Refer to a 32-bit word or byte in a decFloat (df) by big-endian */ + /* (array) notation (the 0 word or byte contains the sign bit), */ + /* automatically adjusting for endianness; similarly address a word */ + /* in the next-wider format (decFloatWider, or dfw) */ + #define DECWORDS (DECBYTES/4) + #define DECWWORDS (DECWBYTES/4) + #if DECLITEND + #define DFBYTE(df, off) ((df)->bytes[DECBYTES-1-(off)]) + #define DFWORD(df, off) ((df)->words[DECWORDS-1-(off)]) + #define DFWWORD(dfw, off) ((dfw)->words[DECWWORDS-1-(off)]) + #else + #define DFBYTE(df, off) ((df)->bytes[off]) + #define DFWORD(df, off) ((df)->words[off]) + #define DFWWORD(dfw, off) ((dfw)->words[off]) + #endif + + /* Tests for sign or specials, directly on DECFLOATs */ + #define DFISSIGNED(df) (DFWORD(df, 0)&0x80000000) + #define DFISSPECIAL(df) ((DFWORD(df, 0)&0x78000000)==0x78000000) + #define DFISINF(df) ((DFWORD(df, 0)&0x7c000000)==0x78000000) + #define DFISNAN(df) ((DFWORD(df, 0)&0x7c000000)==0x7c000000) + #define DFISQNAN(df) ((DFWORD(df, 0)&0x7e000000)==0x7c000000) + #define DFISSNAN(df) ((DFWORD(df, 0)&0x7e000000)==0x7e000000) + + /* Shared lookup tables */ + extern const uInt DECCOMBMSD[64]; /* Combination field -> MSD */ + extern const uInt DECCOMBFROM[48]; /* exp+msd -> Combination */ + + /* Private generic (utility) routine */ + #if DECCHECK || DECTRACE + extern void decShowNum(const bcdnum *, const char *); + #endif + + /* Format-dependent macros and constants */ + #if defined(DECPMAX) + + /* Useful constants */ + #define DECPMAX9 (ROUNDUP(DECPMAX, 9)/9) /* 'Pmax' in 10**9s */ + /* Top words for a zero */ + #define SINGLEZERO 0x22500000 + #define DOUBLEZERO 0x22380000 + #define QUADZERO 0x22080000 + /* [ZEROWORD is defined to be one of these in the DFISZERO macro] */ + + /* Format-dependent common tests: */ + /* DFISZERO -- test for (any) zero */ + /* DFISCCZERO -- test for coefficient continuation being zero */ + /* DFISCC01 -- test for coefficient contains only 0s and 1s */ + /* DFISINT -- test for finite and exponent q=0 */ + /* DFISUINT01 -- test for sign=0, finite, exponent q=0, and */ + /* MSD=0 or 1 */ + /* ZEROWORD is also defined here. */ + /* In DFISZERO the first test checks the least-significant word */ + /* (most likely to be non-zero); the penultimate tests MSD and */ + /* DPDs in the signword, and the final test excludes specials and */ + /* MSD>7. DFISINT similarly has to allow for the two forms of */ + /* MSD codes. DFISUINT01 only has to allow for one form of MSD */ + /* code. */ + #if DECPMAX==7 + #define ZEROWORD SINGLEZERO + /* [test macros not needed except for Zero] */ + #define DFISZERO(df) ((DFWORD(df, 0)&0x1c0fffff)==0 \ + && (DFWORD(df, 0)&0x60000000)!=0x60000000) + #elif DECPMAX==16 + #define ZEROWORD DOUBLEZERO + #define DFISZERO(df) ((DFWORD(df, 1)==0 \ + && (DFWORD(df, 0)&0x1c03ffff)==0 \ + && (DFWORD(df, 0)&0x60000000)!=0x60000000)) + #define DFISINT(df) ((DFWORD(df, 0)&0x63fc0000)==0x22380000 \ + ||(DFWORD(df, 0)&0x7bfc0000)==0x6a380000) + #define DFISUINT01(df) ((DFWORD(df, 0)&0xfbfc0000)==0x22380000) + #define DFISCCZERO(df) (DFWORD(df, 1)==0 \ + && (DFWORD(df, 0)&0x0003ffff)==0) + #define DFISCC01(df) ((DFWORD(df, 0)&~0xfffc9124)==0 \ + && (DFWORD(df, 1)&~0x49124491)==0) + #elif DECPMAX==34 + #define ZEROWORD QUADZERO + #define DFISZERO(df) ((DFWORD(df, 3)==0 \ + && DFWORD(df, 2)==0 \ + && DFWORD(df, 1)==0 \ + && (DFWORD(df, 0)&0x1c003fff)==0 \ + && (DFWORD(df, 0)&0x60000000)!=0x60000000)) + #define DFISINT(df) ((DFWORD(df, 0)&0x63ffc000)==0x22080000 \ + ||(DFWORD(df, 0)&0x7bffc000)==0x6a080000) + #define DFISUINT01(df) ((DFWORD(df, 0)&0xfbffc000)==0x22080000) + #define DFISCCZERO(df) (DFWORD(df, 3)==0 \ + && DFWORD(df, 2)==0 \ + && DFWORD(df, 1)==0 \ + && (DFWORD(df, 0)&0x00003fff)==0) + + #define DFISCC01(df) ((DFWORD(df, 0)&~0xffffc912)==0 \ + && (DFWORD(df, 1)&~0x44912449)==0 \ + && (DFWORD(df, 2)&~0x12449124)==0 \ + && (DFWORD(df, 3)&~0x49124491)==0) + #endif + + /* Macros to test if a certain 10 bits of a uInt or pair of uInts */ + /* are a canonical declet [higher or lower bits are ignored]. */ + /* declet is at offset 0 (from the right) in a uInt: */ + #define CANONDPD(dpd) (((dpd)&0x300)==0 || ((dpd)&0x6e)!=0x6e) + /* declet is at offset k (a multiple of 2) in a uInt: */ + #define CANONDPDOFF(dpd, k) (((dpd)&(0x300<<(k)))==0 \ + || ((dpd)&(((uInt)0x6e)<<(k)))!=(((uInt)0x6e)<<(k))) + /* declet is at offset k (a multiple of 2) in a pair of uInts: */ + /* [the top 2 bits will always be in the more-significant uInt] */ + #define CANONDPDTWO(hi, lo, k) (((hi)&(0x300>>(32-(k))))==0 \ + || ((hi)&(0x6e>>(32-(k))))!=(0x6e>>(32-(k))) \ + || ((lo)&(((uInt)0x6e)<<(k)))!=(((uInt)0x6e)<<(k))) + + /* Macro to test whether a full-length (length DECPMAX) BCD8 */ + /* coefficient, starting at uByte u, is all zeros */ + /* Test just the LSWord first, then the remainder as a sequence */ + /* of tests in order to avoid same-level use of UBTOUI */ + #if DECPMAX==7 + #define ISCOEFFZERO(u) ( \ + UBTOUI((u)+DECPMAX-4)==0 \ + && UBTOUS((u)+DECPMAX-6)==0 \ + && *(u)==0) + #elif DECPMAX==16 + #define ISCOEFFZERO(u) ( \ + UBTOUI((u)+DECPMAX-4)==0 \ + && UBTOUI((u)+DECPMAX-8)==0 \ + && UBTOUI((u)+DECPMAX-12)==0 \ + && UBTOUI(u)==0) + #elif DECPMAX==34 + #define ISCOEFFZERO(u) ( \ + UBTOUI((u)+DECPMAX-4)==0 \ + && UBTOUI((u)+DECPMAX-8)==0 \ + && UBTOUI((u)+DECPMAX-12)==0 \ + && UBTOUI((u)+DECPMAX-16)==0 \ + && UBTOUI((u)+DECPMAX-20)==0 \ + && UBTOUI((u)+DECPMAX-24)==0 \ + && UBTOUI((u)+DECPMAX-28)==0 \ + && UBTOUI((u)+DECPMAX-32)==0 \ + && UBTOUS(u)==0) + #endif + + /* Macros and masks for the exponent continuation field and MSD */ + /* Get the exponent continuation from a decFloat *df as an Int */ + #define GETECON(df) ((Int)((DFWORD((df), 0)&0x03ffffff)>>(32-6-DECECONL))) + /* Ditto, from the next-wider format */ + #define GETWECON(df) ((Int)((DFWWORD((df), 0)&0x03ffffff)>>(32-6-DECWECONL))) + /* Get the biased exponent similarly */ + #define GETEXP(df) ((Int)(DECCOMBEXP[DFWORD((df), 0)>>26]+GETECON(df))) + /* Get the unbiased exponent similarly */ + #define GETEXPUN(df) ((Int)GETEXP(df)-DECBIAS) + /* Get the MSD similarly (as uInt) */ + #define GETMSD(df) (DECCOMBMSD[DFWORD((df), 0)>>26]) + + /* Compile-time computes of the exponent continuation field masks */ + /* full exponent continuation field: */ + #define ECONMASK ((0x03ffffff>>(32-6-DECECONL))<<(32-6-DECECONL)) + /* same, not including its first digit (the qNaN/sNaN selector): */ + #define ECONNANMASK ((0x01ffffff>>(32-6-DECECONL))<<(32-6-DECECONL)) + + /* Macros to decode the coefficient in a finite decFloat *df into */ + /* a BCD string (uByte *bcdin) of length DECPMAX uBytes. */ + + /* In-line sequence to convert least significant 10 bits of uInt */ + /* dpd to three BCD8 digits starting at uByte u. Note that an */ + /* extra byte is written to the right of the three digits because */ + /* four bytes are moved at a time for speed; the alternative */ + /* macro moves exactly three bytes (usually slower). */ + #define dpd2bcd8(u, dpd) memcpy(u, &DPD2BCD8[((dpd)&0x3ff)*4], 4) + #define dpd2bcd83(u, dpd) memcpy(u, &DPD2BCD8[((dpd)&0x3ff)*4], 3) + + /* Decode the declets. After extracting each one, it is decoded */ + /* to BCD8 using a table lookup (also used for variable-length */ + /* decode). Each DPD decode is 3 bytes BCD8 plus a one-byte */ + /* length which is not used, here). Fixed-length 4-byte moves */ + /* are fast, however, almost everywhere, and so are used except */ + /* for the final three bytes (to avoid overrun). The code below */ + /* is 36 instructions for Doubles and about 70 for Quads, even */ + /* on IA32. */ + + /* Two macros are defined for each format: */ + /* GETCOEFF extracts the coefficient of the current format */ + /* GETWCOEFF extracts the coefficient of the next-wider format. */ + /* The latter is a copy of the next-wider GETCOEFF using DFWWORD. */ + + #if DECPMAX==7 + #define GETCOEFF(df, bcd) { \ + uInt sourhi=DFWORD(df, 0); \ + *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ + dpd2bcd8(bcd+1, sourhi>>10); \ + dpd2bcd83(bcd+4, sourhi);} + #define GETWCOEFF(df, bcd) { \ + uInt sourhi=DFWWORD(df, 0); \ + uInt sourlo=DFWWORD(df, 1); \ + *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ + dpd2bcd8(bcd+1, sourhi>>8); \ + dpd2bcd8(bcd+4, (sourhi<<2) | (sourlo>>30)); \ + dpd2bcd8(bcd+7, sourlo>>20); \ + dpd2bcd8(bcd+10, sourlo>>10); \ + dpd2bcd83(bcd+13, sourlo);} + + #elif DECPMAX==16 + #define GETCOEFF(df, bcd) { \ + uInt sourhi=DFWORD(df, 0); \ + uInt sourlo=DFWORD(df, 1); \ + *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ + dpd2bcd8(bcd+1, sourhi>>8); \ + dpd2bcd8(bcd+4, (sourhi<<2) | (sourlo>>30)); \ + dpd2bcd8(bcd+7, sourlo>>20); \ + dpd2bcd8(bcd+10, sourlo>>10); \ + dpd2bcd83(bcd+13, sourlo);} + #define GETWCOEFF(df, bcd) { \ + uInt sourhi=DFWWORD(df, 0); \ + uInt sourmh=DFWWORD(df, 1); \ + uInt sourml=DFWWORD(df, 2); \ + uInt sourlo=DFWWORD(df, 3); \ + *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ + dpd2bcd8(bcd+1, sourhi>>4); \ + dpd2bcd8(bcd+4, ((sourhi)<<6) | (sourmh>>26)); \ + dpd2bcd8(bcd+7, sourmh>>16); \ + dpd2bcd8(bcd+10, sourmh>>6); \ + dpd2bcd8(bcd+13, ((sourmh)<<4) | (sourml>>28)); \ + dpd2bcd8(bcd+16, sourml>>18); \ + dpd2bcd8(bcd+19, sourml>>8); \ + dpd2bcd8(bcd+22, ((sourml)<<2) | (sourlo>>30)); \ + dpd2bcd8(bcd+25, sourlo>>20); \ + dpd2bcd8(bcd+28, sourlo>>10); \ + dpd2bcd83(bcd+31, sourlo);} + + #elif DECPMAX==34 + #define GETCOEFF(df, bcd) { \ + uInt sourhi=DFWORD(df, 0); \ + uInt sourmh=DFWORD(df, 1); \ + uInt sourml=DFWORD(df, 2); \ + uInt sourlo=DFWORD(df, 3); \ + *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ + dpd2bcd8(bcd+1, sourhi>>4); \ + dpd2bcd8(bcd+4, ((sourhi)<<6) | (sourmh>>26)); \ + dpd2bcd8(bcd+7, sourmh>>16); \ + dpd2bcd8(bcd+10, sourmh>>6); \ + dpd2bcd8(bcd+13, ((sourmh)<<4) | (sourml>>28)); \ + dpd2bcd8(bcd+16, sourml>>18); \ + dpd2bcd8(bcd+19, sourml>>8); \ + dpd2bcd8(bcd+22, ((sourml)<<2) | (sourlo>>30)); \ + dpd2bcd8(bcd+25, sourlo>>20); \ + dpd2bcd8(bcd+28, sourlo>>10); \ + dpd2bcd83(bcd+31, sourlo);} + + #define GETWCOEFF(df, bcd) {??} /* [should never be used] */ + #endif + + /* Macros to decode the coefficient in a finite decFloat *df into */ + /* a base-billion uInt array, with the least-significant */ + /* 0-999999999 'digit' at offset 0. */ + + /* Decode the declets. After extracting each one, it is decoded */ + /* to binary using a table lookup. Three tables are used; one */ + /* the usual DPD to binary, the other two pre-multiplied by 1000 */ + /* and 1000000 to avoid multiplication during decode. These */ + /* tables can also be used for multiplying up the MSD as the DPD */ + /* code for 0 through 9 is the identity. */ + #define DPD2BIN0 DPD2BIN /* for prettier code */ + + #if DECPMAX==7 + #define GETCOEFFBILL(df, buf) { \ + uInt sourhi=DFWORD(df, 0); \ + (buf)[0]=DPD2BIN0[sourhi&0x3ff] \ + +DPD2BINK[(sourhi>>10)&0x3ff] \ + +DPD2BINM[DECCOMBMSD[sourhi>>26]];} + + #elif DECPMAX==16 + #define GETCOEFFBILL(df, buf) { \ + uInt sourhi, sourlo; \ + sourlo=DFWORD(df, 1); \ + (buf)[0]=DPD2BIN0[sourlo&0x3ff] \ + +DPD2BINK[(sourlo>>10)&0x3ff] \ + +DPD2BINM[(sourlo>>20)&0x3ff]; \ + sourhi=DFWORD(df, 0); \ + (buf)[1]=DPD2BIN0[((sourhi<<2) | (sourlo>>30))&0x3ff] \ + +DPD2BINK[(sourhi>>8)&0x3ff] \ + +DPD2BINM[DECCOMBMSD[sourhi>>26]];} + + #elif DECPMAX==34 + #define GETCOEFFBILL(df, buf) { \ + uInt sourhi, sourmh, sourml, sourlo; \ + sourlo=DFWORD(df, 3); \ + (buf)[0]=DPD2BIN0[sourlo&0x3ff] \ + +DPD2BINK[(sourlo>>10)&0x3ff] \ + +DPD2BINM[(sourlo>>20)&0x3ff]; \ + sourml=DFWORD(df, 2); \ + (buf)[1]=DPD2BIN0[((sourml<<2) | (sourlo>>30))&0x3ff] \ + +DPD2BINK[(sourml>>8)&0x3ff] \ + +DPD2BINM[(sourml>>18)&0x3ff]; \ + sourmh=DFWORD(df, 1); \ + (buf)[2]=DPD2BIN0[((sourmh<<4) | (sourml>>28))&0x3ff] \ + +DPD2BINK[(sourmh>>6)&0x3ff] \ + +DPD2BINM[(sourmh>>16)&0x3ff]; \ + sourhi=DFWORD(df, 0); \ + (buf)[3]=DPD2BIN0[((sourhi<<6) | (sourmh>>26))&0x3ff] \ + +DPD2BINK[(sourhi>>4)&0x3ff] \ + +DPD2BINM[DECCOMBMSD[sourhi>>26]];} + + #endif + + /* Macros to decode the coefficient in a finite decFloat *df into */ + /* a base-thousand uInt array (of size DECLETS+1, to allow for */ + /* the MSD), with the least-significant 0-999 'digit' at offset 0.*/ + + /* Decode the declets. After extracting each one, it is decoded */ + /* to binary using a table lookup. */ + #if DECPMAX==7 + #define GETCOEFFTHOU(df, buf) { \ + uInt sourhi=DFWORD(df, 0); \ + (buf)[0]=DPD2BIN[sourhi&0x3ff]; \ + (buf)[1]=DPD2BIN[(sourhi>>10)&0x3ff]; \ + (buf)[2]=DECCOMBMSD[sourhi>>26];} + + #elif DECPMAX==16 + #define GETCOEFFTHOU(df, buf) { \ + uInt sourhi, sourlo; \ + sourlo=DFWORD(df, 1); \ + (buf)[0]=DPD2BIN[sourlo&0x3ff]; \ + (buf)[1]=DPD2BIN[(sourlo>>10)&0x3ff]; \ + (buf)[2]=DPD2BIN[(sourlo>>20)&0x3ff]; \ + sourhi=DFWORD(df, 0); \ + (buf)[3]=DPD2BIN[((sourhi<<2) | (sourlo>>30))&0x3ff]; \ + (buf)[4]=DPD2BIN[(sourhi>>8)&0x3ff]; \ + (buf)[5]=DECCOMBMSD[sourhi>>26];} + + #elif DECPMAX==34 + #define GETCOEFFTHOU(df, buf) { \ + uInt sourhi, sourmh, sourml, sourlo; \ + sourlo=DFWORD(df, 3); \ + (buf)[0]=DPD2BIN[sourlo&0x3ff]; \ + (buf)[1]=DPD2BIN[(sourlo>>10)&0x3ff]; \ + (buf)[2]=DPD2BIN[(sourlo>>20)&0x3ff]; \ + sourml=DFWORD(df, 2); \ + (buf)[3]=DPD2BIN[((sourml<<2) | (sourlo>>30))&0x3ff]; \ + (buf)[4]=DPD2BIN[(sourml>>8)&0x3ff]; \ + (buf)[5]=DPD2BIN[(sourml>>18)&0x3ff]; \ + sourmh=DFWORD(df, 1); \ + (buf)[6]=DPD2BIN[((sourmh<<4) | (sourml>>28))&0x3ff]; \ + (buf)[7]=DPD2BIN[(sourmh>>6)&0x3ff]; \ + (buf)[8]=DPD2BIN[(sourmh>>16)&0x3ff]; \ + sourhi=DFWORD(df, 0); \ + (buf)[9]=DPD2BIN[((sourhi<<6) | (sourmh>>26))&0x3ff]; \ + (buf)[10]=DPD2BIN[(sourhi>>4)&0x3ff]; \ + (buf)[11]=DECCOMBMSD[sourhi>>26];} + #endif + + + /* Macros to decode the coefficient in a finite decFloat *df and */ + /* add to a base-thousand uInt array (as for GETCOEFFTHOU). */ + /* After the addition then most significant 'digit' in the array */ + /* might have a value larger then 10 (with a maximum of 19). */ + #if DECPMAX==7 + #define ADDCOEFFTHOU(df, buf) { \ + uInt sourhi=DFWORD(df, 0); \ + (buf)[0]+=DPD2BIN[sourhi&0x3ff]; \ + if (buf[0]>999) {buf[0]-=1000; buf[1]++;} \ + (buf)[1]+=DPD2BIN[(sourhi>>10)&0x3ff]; \ + if (buf[1]>999) {buf[1]-=1000; buf[2]++;} \ + (buf)[2]+=DECCOMBMSD[sourhi>>26];} + + #elif DECPMAX==16 + #define ADDCOEFFTHOU(df, buf) { \ + uInt sourhi, sourlo; \ + sourlo=DFWORD(df, 1); \ + (buf)[0]+=DPD2BIN[sourlo&0x3ff]; \ + if (buf[0]>999) {buf[0]-=1000; buf[1]++;} \ + (buf)[1]+=DPD2BIN[(sourlo>>10)&0x3ff]; \ + if (buf[1]>999) {buf[1]-=1000; buf[2]++;} \ + (buf)[2]+=DPD2BIN[(sourlo>>20)&0x3ff]; \ + if (buf[2]>999) {buf[2]-=1000; buf[3]++;} \ + sourhi=DFWORD(df, 0); \ + (buf)[3]+=DPD2BIN[((sourhi<<2) | (sourlo>>30))&0x3ff]; \ + if (buf[3]>999) {buf[3]-=1000; buf[4]++;} \ + (buf)[4]+=DPD2BIN[(sourhi>>8)&0x3ff]; \ + if (buf[4]>999) {buf[4]-=1000; buf[5]++;} \ + (buf)[5]+=DECCOMBMSD[sourhi>>26];} + + #elif DECPMAX==34 + #define ADDCOEFFTHOU(df, buf) { \ + uInt sourhi, sourmh, sourml, sourlo; \ + sourlo=DFWORD(df, 3); \ + (buf)[0]+=DPD2BIN[sourlo&0x3ff]; \ + if (buf[0]>999) {buf[0]-=1000; buf[1]++;} \ + (buf)[1]+=DPD2BIN[(sourlo>>10)&0x3ff]; \ + if (buf[1]>999) {buf[1]-=1000; buf[2]++;} \ + (buf)[2]+=DPD2BIN[(sourlo>>20)&0x3ff]; \ + if (buf[2]>999) {buf[2]-=1000; buf[3]++;} \ + sourml=DFWORD(df, 2); \ + (buf)[3]+=DPD2BIN[((sourml<<2) | (sourlo>>30))&0x3ff]; \ + if (buf[3]>999) {buf[3]-=1000; buf[4]++;} \ + (buf)[4]+=DPD2BIN[(sourml>>8)&0x3ff]; \ + if (buf[4]>999) {buf[4]-=1000; buf[5]++;} \ + (buf)[5]+=DPD2BIN[(sourml>>18)&0x3ff]; \ + if (buf[5]>999) {buf[5]-=1000; buf[6]++;} \ + sourmh=DFWORD(df, 1); \ + (buf)[6]+=DPD2BIN[((sourmh<<4) | (sourml>>28))&0x3ff]; \ + if (buf[6]>999) {buf[6]-=1000; buf[7]++;} \ + (buf)[7]+=DPD2BIN[(sourmh>>6)&0x3ff]; \ + if (buf[7]>999) {buf[7]-=1000; buf[8]++;} \ + (buf)[8]+=DPD2BIN[(sourmh>>16)&0x3ff]; \ + if (buf[8]>999) {buf[8]-=1000; buf[9]++;} \ + sourhi=DFWORD(df, 0); \ + (buf)[9]+=DPD2BIN[((sourhi<<6) | (sourmh>>26))&0x3ff]; \ + if (buf[9]>999) {buf[9]-=1000; buf[10]++;} \ + (buf)[10]+=DPD2BIN[(sourhi>>4)&0x3ff]; \ + if (buf[10]>999) {buf[10]-=1000; buf[11]++;} \ + (buf)[11]+=DECCOMBMSD[sourhi>>26];} + #endif + + + /* Set a decFloat to the maximum positive finite number (Nmax) */ + #if DECPMAX==7 + #define DFSETNMAX(df) \ + {DFWORD(df, 0)=0x77f3fcff;} + #elif DECPMAX==16 + #define DFSETNMAX(df) \ + {DFWORD(df, 0)=0x77fcff3f; \ + DFWORD(df, 1)=0xcff3fcff;} + #elif DECPMAX==34 + #define DFSETNMAX(df) \ + {DFWORD(df, 0)=0x77ffcff3; \ + DFWORD(df, 1)=0xfcff3fcf; \ + DFWORD(df, 2)=0xf3fcff3f; \ + DFWORD(df, 3)=0xcff3fcff;} + #endif + + /* [end of format-dependent macros and constants] */ + #endif + +#else + #error decNumberLocal included more than once +#endif diff --git a/deps/icu-small/source/i18n/decimfmt.cpp b/deps/icu-small/source/i18n/decimfmt.cpp index bca33366792705..43ea95170911d2 100644 --- a/deps/icu-small/source/i18n/decimfmt.cpp +++ b/deps/icu-small/source/i18n/decimfmt.cpp @@ -1,1892 +1,1892 @@ -// © 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 -#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 VS2015 warns C4805 when comparing bool with UBool, VS2017 no longer emits this warning. -// 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 UBOOL_TO_BOOL(b) b -#endif - - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormat) - - -DecimalFormat::DecimalFormat(UErrorCode& status) - : DecimalFormat(nullptr, status) { - if (U_FAILURE(status)) { return; } - // 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) { - if (U_FAILURE(status)) { return; } - setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status); - touch(status); -} - -DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt, - UErrorCode& status) - : DecimalFormat(symbolsToAdopt, status) { - if (U_FAILURE(status)) { return; } - setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status); - touch(status); -} - -DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt, - UNumberFormatStyle style, UErrorCode& status) - : DecimalFormat(symbolsToAdopt, status) { - if (U_FAILURE(status)) { return; } - // 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 { - setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, 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); -} - -DecimalFormat::DecimalFormat(const DecimalFormatSymbols* symbolsToAdopt, UErrorCode& status) { - // we must take ownership of symbolsToAdopt, even in a failure case. - LocalPointer adoptedSymbols(symbolsToAdopt); - if (U_FAILURE(status)) { - return; - } - fields = new DecimalFormatFields(); - if (fields == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (adoptedSymbols.isNull()) { - fields->symbols.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(status), status); - } else { - fields->symbols.adoptInsteadAndCheckErrorCode(adoptedSymbols.orphan(), status); - } - if (U_FAILURE(status)) { - delete fields; - fields = nullptr; - } -} - -#if UCONFIG_HAVE_PARSEALLINPUT - -void DecimalFormat::setParseAllInput(UNumberFormatAttributeValue value) { - if (fields == nullptr) { return; } - 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; } - - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - 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_SCALE: - setMultiplierScale(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 - - case UNUM_PARSE_NO_EXPONENT: - setParseNoExponent((UBool) newValue); - break; - - case UNUM_PARSE_DECIMAL_MARK_REQUIRED: - setDecimalPatternMatchRequired((UBool) newValue); - break; - - 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; - - case UNUM_SIGN_ALWAYS_SHOWN: - setSignAlwaysShown(static_cast(newValue)); - break; - - case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: - setFormatFailIfMoreThanMaxDigits(static_cast(newValue)); - break; - - default: - status = U_UNSUPPORTED_ERROR; - break; - } - return *this; -} - -int32_t DecimalFormat::getAttribute(UNumberFormatAttribute attr, UErrorCode& status) const { - if (U_FAILURE(status)) { return -1; } - - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - 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(); - - case UNUM_MAX_INTEGER_DIGITS: - return getMaximumIntegerDigits(); - - case UNUM_MIN_INTEGER_DIGITS: - return getMinimumIntegerDigits(); - - case UNUM_INTEGER_DIGITS: - // TBD: what should this return? - return getMinimumIntegerDigits(); - - case UNUM_MAX_FRACTION_DIGITS: - return getMaximumFractionDigits(); - - 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(); - - case UNUM_MAX_SIGNIFICANT_DIGITS: - return getMaximumSignificantDigits(); - - case UNUM_MIN_SIGNIFICANT_DIGITS: - return getMinimumSignificantDigits(); - - case UNUM_MULTIPLIER: - return getMultiplier(); - - case UNUM_SCALE: - return getMultiplierScale(); - - case UNUM_GROUPING_SIZE: - return getGroupingSize(); - - case UNUM_ROUNDING_MODE: - return getRoundingMode(); - - case UNUM_FORMAT_WIDTH: - return getFormatWidth(); - - case UNUM_PADDING_POSITION: - return getPadPosition(); - - case UNUM_SECONDARY_GROUPING_SIZE: - return getSecondaryGroupingSize(); - - case UNUM_PARSE_NO_EXPONENT: - return isParseNoExponent(); - - case UNUM_PARSE_DECIMAL_MARK_REQUIRED: - return isDecimalPatternMatchRequired(); - - case UNUM_CURRENCY_USAGE: - return getCurrencyUsage(); - - case UNUM_MINIMUM_GROUPING_DIGITS: - return getMinimumGroupingDigits(); - - case UNUM_PARSE_CASE_SENSITIVE: - return isParseCaseSensitive(); - - case UNUM_SIGN_ALWAYS_SHOWN: - return isSignAlwaysShown(); - - case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: - return isFormatFailIfMoreThanMaxDigits(); - - default: - status = U_UNSUPPORTED_ERROR; - break; - } - - return -1; /* undefined */ -} - -void DecimalFormat::setGroupingUsed(UBool enabled) { - if (fields == nullptr) { - return; - } - if (UBOOL_TO_BOOL(enabled) == fields->properties.groupingUsed) { return; } - NumberFormat::setGroupingUsed(enabled); // to set field for compatibility - fields->properties.groupingUsed = enabled; - touchNoError(); -} - -void DecimalFormat::setParseIntegerOnly(UBool value) { - if (fields == nullptr) { - return; - } - if (UBOOL_TO_BOOL(value) == fields->properties.parseIntegerOnly) { return; } - NumberFormat::setParseIntegerOnly(value); // to set field for compatibility - fields->properties.parseIntegerOnly = value; - touchNoError(); -} - -void DecimalFormat::setLenient(UBool enable) { - if (fields == nullptr) { - return; - } - 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(); -} - -DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt, - UParseError&, UErrorCode& status) - : DecimalFormat(symbolsToAdopt, status) { - if (U_FAILURE(status)) { return; } - // TODO: What is parseError for? - setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status); - touch(status); -} - -DecimalFormat::DecimalFormat(const UnicodeString& pattern, const DecimalFormatSymbols& symbols, - UErrorCode& status) - : DecimalFormat(nullptr, status) { - if (U_FAILURE(status)) { return; } - LocalPointer dfs(new DecimalFormatSymbols(symbols), status); - if (U_FAILURE(status)) { - // If we failed to allocate DecimalFormatSymbols, then release fields and its members. - // We must have a fully complete fields object, we cannot have partially populated members. - delete fields; - fields = nullptr; - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - fields->symbols.adoptInstead(dfs.orphan()); - setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status); - touch(status); -} - -DecimalFormat::DecimalFormat(const DecimalFormat& source) : NumberFormat(source) { - // If the object that we are copying from is invalid, no point in going further. - if (source.fields == nullptr) { - return; - } - // 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(source.fields->properties); - if (fields == nullptr) { - return; // no way to report an error. - } - UErrorCode status = U_ZERO_ERROR; - fields->symbols.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(*source.getDecimalFormatSymbols()), status); - // In order to simplify error handling logic in the various getters/setters/etc, we do not allow - // any partially populated DecimalFormatFields object. We must have a fully complete fields object - // or else we set it to nullptr. - if (U_FAILURE(status)) { - delete fields; - fields = nullptr; - return; - } - touch(status); -} - -DecimalFormat& DecimalFormat::operator=(const DecimalFormat& rhs) { - // guard against self-assignment - if (this == &rhs) { - return *this; - } - // Make sure both objects are valid. - if (fields == nullptr || rhs.fields == nullptr) { - return *this; // unfortunately, no way to report an error. - } - fields->properties = rhs.fields->properties; - fields->exportedProperties.clear(); - UErrorCode status = U_ZERO_ERROR; - LocalPointer dfs(new DecimalFormatSymbols(*rhs.getDecimalFormatSymbols()), status); - if (U_FAILURE(status)) { - // We failed to allocate DecimalFormatSymbols, release fields and its members. - // We must have a fully complete fields object, we cannot have partially populated members. - delete fields; - fields = nullptr; - return *this; - } - fields->symbols.adoptInstead(dfs.orphan()); - touch(status); - - return *this; -} - -DecimalFormat::~DecimalFormat() { - if (fields == nullptr) { return; } - - delete fields->atomicParser.exchange(nullptr); - delete fields->atomicCurrencyParser.exchange(nullptr); - delete fields; -} - -DecimalFormat* DecimalFormat::clone() const { - // can only clone valid objects. - if (fields == nullptr) { - return nullptr; - } - LocalPointer df(new DecimalFormat(*this)); - if (df.isValid() && df->fields != nullptr) { - return df.orphan(); - } - return nullptr; -} - -bool DecimalFormat::operator==(const Format& other) const { - auto* otherDF = dynamic_cast(&other); - if (otherDF == nullptr) { - return false; - } - // If either object is in an invalid state, prevent dereferencing nullptr below. - // Additionally, invalid objects should not be considered equal to anything. - if (fields == nullptr || otherDF->fields == nullptr) { - return false; - } - return fields->properties == otherDF->fields->properties && *getDecimalFormatSymbols() == *otherDF->getDecimalFormatSymbols(); -} - -UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos) const { - if (fields == nullptr) { - appendTo.setToBogus(); - return appendTo; - } - if (pos.getField() == FieldPosition::DONT_CARE && fastFormatDouble(number, appendTo)) { - return appendTo; - } - UErrorCode localStatus = U_ZERO_ERROR; - UFormattedNumberData output; - output.quantity.setToDouble(number); - fields->formatter.formatImpl(&output, localStatus); - fieldPositionHelper(output, pos, appendTo.length(), localStatus); - auto appendable = UnicodeStringAppendable(appendTo); - output.appendTo(appendable, localStatus); - return appendTo; -} - -UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return appendTo; // don't overwrite status if it's already a failure. - } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - appendTo.setToBogus(); - return appendTo; - } - if (pos.getField() == FieldPosition::DONT_CARE && fastFormatDouble(number, appendTo)) { - return appendTo; - } - UFormattedNumberData output; - output.quantity.setToDouble(number); - fields->formatter.formatImpl(&output, status); - fieldPositionHelper(output, pos, appendTo.length(), status); - auto appendable = UnicodeStringAppendable(appendTo); - output.appendTo(appendable, status); - return appendTo; -} - -UnicodeString& -DecimalFormat::format(double number, UnicodeString& appendTo, FieldPositionIterator* posIter, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return appendTo; // don't overwrite status if it's already a failure. - } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - appendTo.setToBogus(); - return appendTo; - } - if (posIter == nullptr && fastFormatDouble(number, appendTo)) { - return appendTo; - } - UFormattedNumberData output; - output.quantity.setToDouble(number); - fields->formatter.formatImpl(&output, status); - fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); - auto appendable = UnicodeStringAppendable(appendTo); - output.appendTo(appendable, status); - return appendTo; -} - -UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos) const { - return format(static_cast (number), appendTo, pos); -} - -UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos, - UErrorCode& status) const { - return format(static_cast (number), appendTo, pos, status); -} - -UnicodeString& -DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPositionIterator* posIter, - UErrorCode& status) const { - return format(static_cast (number), appendTo, posIter, status); -} - -UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos) const { - if (fields == nullptr) { - appendTo.setToBogus(); - return appendTo; - } - if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) { - return appendTo; - } - UErrorCode localStatus = U_ZERO_ERROR; - UFormattedNumberData output; - output.quantity.setToLong(number); - fields->formatter.formatImpl(&output, localStatus); - fieldPositionHelper(output, pos, appendTo.length(), localStatus); - auto appendable = UnicodeStringAppendable(appendTo); - output.appendTo(appendable, localStatus); - return appendTo; -} - -UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return appendTo; // don't overwrite status if it's already a failure. - } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - appendTo.setToBogus(); - return appendTo; - } - if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) { - return appendTo; - } - UFormattedNumberData output; - output.quantity.setToLong(number); - fields->formatter.formatImpl(&output, status); - fieldPositionHelper(output, pos, appendTo.length(), status); - auto appendable = UnicodeStringAppendable(appendTo); - output.appendTo(appendable, status); - return appendTo; -} - -UnicodeString& -DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPositionIterator* posIter, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return appendTo; // don't overwrite status if it's already a failure. - } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - appendTo.setToBogus(); - return appendTo; - } - if (posIter == nullptr && fastFormatInt64(number, appendTo)) { - return appendTo; - } - UFormattedNumberData output; - output.quantity.setToLong(number); - fields->formatter.formatImpl(&output, status); - fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); - auto appendable = UnicodeStringAppendable(appendTo); - output.appendTo(appendable, status); - return appendTo; -} - -UnicodeString& -DecimalFormat::format(StringPiece number, UnicodeString& appendTo, FieldPositionIterator* posIter, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return appendTo; // don't overwrite status if it's already a failure. - } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - appendTo.setToBogus(); - return appendTo; - } - UFormattedNumberData output; - output.quantity.setToDecNumber(number, status); - fields->formatter.formatImpl(&output, status); - fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); - auto appendable = UnicodeStringAppendable(appendTo); - output.appendTo(appendable, status); - return appendTo; -} - -UnicodeString& DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, - FieldPositionIterator* posIter, UErrorCode& status) const { - if (U_FAILURE(status)) { - return appendTo; // don't overwrite status if it's already a failure. - } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - appendTo.setToBogus(); - return appendTo; - } - UFormattedNumberData output; - output.quantity = number; - fields->formatter.formatImpl(&output, status); - fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); - auto appendable = UnicodeStringAppendable(appendTo); - output.appendTo(appendable, status); - return appendTo; -} - -UnicodeString& -DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, FieldPosition& pos, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return appendTo; // don't overwrite status if it's already a failure. - } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - appendTo.setToBogus(); - return appendTo; - } - UFormattedNumberData output; - output.quantity = number; - fields->formatter.formatImpl(&output, status); - fieldPositionHelper(output, pos, appendTo.length(), status); - auto appendable = UnicodeStringAppendable(appendTo); - output.appendTo(appendable, status); - return appendTo; -} - -void DecimalFormat::parse(const UnicodeString& text, Formattable& output, - ParsePosition& parsePosition) const { - if (fields == nullptr) { - return; - } - if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) { - if (parsePosition.getIndex() == text.length()) { - // If there is nothing to parse, it is an error - parsePosition.setErrorIndex(parsePosition.getIndex()); - } - return; - } - - 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; // unfortunately no way to report back the error. - } - parser->parse(text, startIndex, true, result, status); - if (U_FAILURE(status)) { - return; // unfortunately no way to report back the error. - } - // 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); - } -} - -CurrencyAmount* DecimalFormat::parseCurrency(const UnicodeString& text, ParsePosition& parsePosition) const { - if (fields == nullptr) { - return nullptr; - } - if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) { - return nullptr; - } - - 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); - if (U_FAILURE(status)) { - return nullptr; - } - // 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()); - LocalPointer currencyAmount( - new CurrencyAmount(formattable, result.currencyCode, status), status); - if (U_FAILURE(status)) { - return nullptr; - } - return currencyAmount.orphan(); - } else { - parsePosition.setErrorIndex(startIndex + result.charEnd); - return nullptr; - } -} - -const DecimalFormatSymbols* DecimalFormat::getDecimalFormatSymbols(void) const { - if (fields == nullptr) { - return nullptr; - } - if (!fields->symbols.isNull()) { - return fields->symbols.getAlias(); - } else { - return fields->formatter.getDecimalFormatSymbols(); - } -} - -void DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) { - if (symbolsToAdopt == nullptr) { - return; // do not allow caller to set fields->symbols to NULL - } - // we must take ownership of symbolsToAdopt, even in a failure case. - LocalPointer dfs(symbolsToAdopt); - if (fields == nullptr) { - return; - } - fields->symbols.adoptInstead(dfs.orphan()); - touchNoError(); -} - -void DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) { - if (fields == nullptr) { - return; - } - UErrorCode status = U_ZERO_ERROR; - LocalPointer dfs(new DecimalFormatSymbols(symbols), status); - if (U_FAILURE(status)) { - // We failed to allocate DecimalFormatSymbols, release fields and its members. - // We must have a fully complete fields object, we cannot have partially populated members. - delete fields; - fields = nullptr; - return; - } - fields->symbols.adoptInstead(dfs.orphan()); - touchNoError(); -} - -const CurrencyPluralInfo* DecimalFormat::getCurrencyPluralInfo(void) const { - if (fields == nullptr) { - return nullptr; - } - return fields->properties.currencyPluralInfo.fPtr.getAlias(); -} - -void DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo* toAdopt) { - // TODO: should we guard against nullptr input, like in adoptDecimalFormatSymbols? - // we must take ownership of toAdopt, even in a failure case. - LocalPointer cpi(toAdopt); - if (fields == nullptr) { - return; - } - fields->properties.currencyPluralInfo.fPtr.adoptInstead(cpi.orphan()); - touchNoError(); -} - -void DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) { - if (fields == nullptr) { - return; - } - if (fields->properties.currencyPluralInfo.fPtr.isNull()) { - // Note: clone() can fail with OOM error, but we have no way to report it. :( - fields->properties.currencyPluralInfo.fPtr.adoptInstead(info.clone()); - } else { - *fields->properties.currencyPluralInfo.fPtr = info; // copy-assignment operator - } - touchNoError(); -} - -UnicodeString& DecimalFormat::getPositivePrefix(UnicodeString& result) const { - if (fields == nullptr) { - result.setToBogus(); - return result; - } - UErrorCode status = U_ZERO_ERROR; - fields->formatter.getAffixImpl(true, false, result, status); - if (U_FAILURE(status)) { result.setToBogus(); } - return result; -} - -void DecimalFormat::setPositivePrefix(const UnicodeString& newValue) { - if (fields == nullptr) { - return; - } - if (newValue == fields->properties.positivePrefix) { return; } - fields->properties.positivePrefix = newValue; - touchNoError(); -} - -UnicodeString& DecimalFormat::getNegativePrefix(UnicodeString& result) const { - if (fields == nullptr) { - result.setToBogus(); - return result; - } - UErrorCode status = U_ZERO_ERROR; - fields->formatter.getAffixImpl(true, true, result, status); - if (U_FAILURE(status)) { result.setToBogus(); } - return result; -} - -void DecimalFormat::setNegativePrefix(const UnicodeString& newValue) { - if (fields == nullptr) { - return; - } - if (newValue == fields->properties.negativePrefix) { return; } - fields->properties.negativePrefix = newValue; - touchNoError(); -} - -UnicodeString& DecimalFormat::getPositiveSuffix(UnicodeString& result) const { - if (fields == nullptr) { - result.setToBogus(); - return result; - } - UErrorCode status = U_ZERO_ERROR; - fields->formatter.getAffixImpl(false, false, result, status); - if (U_FAILURE(status)) { result.setToBogus(); } - return result; -} - -void DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) { - if (fields == nullptr) { - return; - } - if (newValue == fields->properties.positiveSuffix) { return; } - fields->properties.positiveSuffix = newValue; - touchNoError(); -} - -UnicodeString& DecimalFormat::getNegativeSuffix(UnicodeString& result) const { - if (fields == nullptr) { - result.setToBogus(); - return result; - } - UErrorCode status = U_ZERO_ERROR; - fields->formatter.getAffixImpl(false, true, result, status); - if (U_FAILURE(status)) { result.setToBogus(); } - return result; -} - -void DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) { - if (fields == nullptr) { - return; - } - if (newValue == fields->properties.negativeSuffix) { return; } - fields->properties.negativeSuffix = newValue; - touchNoError(); -} - -UBool DecimalFormat::isSignAlwaysShown() const { - // Not much we can do to report an error. - if (fields == nullptr) { - return DecimalFormatProperties::getDefault().signAlwaysShown; - } - return fields->properties.signAlwaysShown; -} - -void DecimalFormat::setSignAlwaysShown(UBool value) { - if (fields == nullptr) { return; } - if (UBOOL_TO_BOOL(value) == fields->properties.signAlwaysShown) { return; } - fields->properties.signAlwaysShown = value; - touchNoError(); -} - -int32_t DecimalFormat::getMultiplier(void) const { - const DecimalFormatProperties *dfp; - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - dfp = &(DecimalFormatProperties::getDefault()); - } else { - dfp = &fields->properties; - } - if (dfp->multiplier != 1) { - return dfp->multiplier; - } else if (dfp->magnitudeMultiplier != 0) { - return static_cast(uprv_pow10(dfp->magnitudeMultiplier)); - } else { - return 1; - } -} - -void DecimalFormat::setMultiplier(int32_t multiplier) { - if (fields == nullptr) { - return; - } - if (multiplier == 0) { - multiplier = 1; // one being the benign default value for a multiplier. - } - - // 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; - } - value = temp; - } - if (delta != -1) { - fields->properties.magnitudeMultiplier = delta; - fields->properties.multiplier = 1; - } else { - fields->properties.magnitudeMultiplier = 0; - fields->properties.multiplier = multiplier; - } - touchNoError(); -} - -int32_t DecimalFormat::getMultiplierScale() const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return DecimalFormatProperties::getDefault().multiplierScale; - } - return fields->properties.multiplierScale; -} - -void DecimalFormat::setMultiplierScale(int32_t newValue) { - if (fields == nullptr) { return; } - if (newValue == fields->properties.multiplierScale) { return; } - fields->properties.multiplierScale = newValue; - touchNoError(); -} - -double DecimalFormat::getRoundingIncrement(void) const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return DecimalFormatProperties::getDefault().roundingIncrement; - } - return fields->exportedProperties.roundingIncrement; -} - -void DecimalFormat::setRoundingIncrement(double newValue) { - if (fields == nullptr) { return; } - if (newValue == fields->properties.roundingIncrement) { return; } - fields->properties.roundingIncrement = newValue; - touchNoError(); -} - -ERoundingMode DecimalFormat::getRoundingMode(void) const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return static_cast(DecimalFormatProperties::getDefault().roundingMode.getNoError()); - } - // UNumberFormatRoundingMode and ERoundingMode have the same values. - return static_cast(fields->exportedProperties.roundingMode.getNoError()); -} - -void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) { - if (fields == nullptr) { return; } - auto uRoundingMode = static_cast(roundingMode); - if (!fields->properties.roundingMode.isNull() && uRoundingMode == fields->properties.roundingMode.getNoError()) { - return; - } - NumberFormat::setMaximumIntegerDigits(roundingMode); // to set field for compatibility - fields->properties.roundingMode = uRoundingMode; - touchNoError(); -} - -int32_t DecimalFormat::getFormatWidth(void) const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return DecimalFormatProperties::getDefault().formatWidth; - } - return fields->properties.formatWidth; -} - -void DecimalFormat::setFormatWidth(int32_t width) { - if (fields == nullptr) { return; } - if (width == fields->properties.formatWidth) { return; } - fields->properties.formatWidth = width; - touchNoError(); -} - -UnicodeString DecimalFormat::getPadCharacterString() const { - if (fields == nullptr || fields->properties.padString.isBogus()) { - // Readonly-alias the static string kFallbackPaddingString - return {true, kFallbackPaddingString, -1}; - } else { - return fields->properties.padString; - } -} - -void DecimalFormat::setPadCharacter(const UnicodeString& padChar) { - if (fields == nullptr) { return; } - if (padChar == fields->properties.padString) { return; } - if (padChar.length() > 0) { - fields->properties.padString = UnicodeString(padChar.char32At(0)); - } else { - fields->properties.padString.setToBogus(); - } - touchNoError(); -} - -EPadPosition DecimalFormat::getPadPosition(void) const { - if (fields == nullptr || fields->properties.padPosition.isNull()) { - return EPadPosition::kPadBeforePrefix; - } else { - // UNumberFormatPadPosition and EPadPosition have the same values. - return static_cast(fields->properties.padPosition.getNoError()); - } -} - -void DecimalFormat::setPadPosition(EPadPosition padPos) { - if (fields == nullptr) { return; } - auto uPadPos = static_cast(padPos); - if (!fields->properties.padPosition.isNull() && uPadPos == fields->properties.padPosition.getNoError()) { - return; - } - fields->properties.padPosition = uPadPos; - touchNoError(); -} - -UBool DecimalFormat::isScientificNotation(void) const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return (DecimalFormatProperties::getDefault().minimumExponentDigits != -1); - } - return (fields->properties.minimumExponentDigits != -1); -} - -void DecimalFormat::setScientificNotation(UBool useScientific) { - if (fields == nullptr) { return; } - int32_t minExp = useScientific ? 1 : -1; - if (fields->properties.minimumExponentDigits == minExp) { return; } - if (useScientific) { - fields->properties.minimumExponentDigits = 1; - } else { - fields->properties.minimumExponentDigits = -1; - } - touchNoError(); -} - -int8_t DecimalFormat::getMinimumExponentDigits(void) const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return static_cast(DecimalFormatProperties::getDefault().minimumExponentDigits); - } - return static_cast(fields->properties.minimumExponentDigits); -} - -void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) { - if (fields == nullptr) { return; } - if (minExpDig == fields->properties.minimumExponentDigits) { return; } - fields->properties.minimumExponentDigits = minExpDig; - touchNoError(); -} - -UBool DecimalFormat::isExponentSignAlwaysShown(void) const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return DecimalFormatProperties::getDefault().exponentSignAlwaysShown; - } - return fields->properties.exponentSignAlwaysShown; -} - -void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) { - if (fields == nullptr) { return; } - if (UBOOL_TO_BOOL(expSignAlways) == fields->properties.exponentSignAlwaysShown) { return; } - fields->properties.exponentSignAlwaysShown = expSignAlways; - touchNoError(); -} - -int32_t DecimalFormat::getGroupingSize(void) const { - int32_t groupingSize; - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - groupingSize = DecimalFormatProperties::getDefault().groupingSize; - } else { - groupingSize = fields->properties.groupingSize; - } - if (groupingSize < 0) { - return 0; - } - return groupingSize; -} - -void DecimalFormat::setGroupingSize(int32_t newValue) { - if (fields == nullptr) { return; } - if (newValue == fields->properties.groupingSize) { return; } - fields->properties.groupingSize = newValue; - touchNoError(); -} - -int32_t DecimalFormat::getSecondaryGroupingSize(void) const { - int32_t grouping2; - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - grouping2 = DecimalFormatProperties::getDefault().secondaryGroupingSize; - } else { - grouping2 = fields->properties.secondaryGroupingSize; - } - if (grouping2 < 0) { - return 0; - } - return grouping2; -} - -void DecimalFormat::setSecondaryGroupingSize(int32_t newValue) { - if (fields == nullptr) { return; } - if (newValue == fields->properties.secondaryGroupingSize) { return; } - fields->properties.secondaryGroupingSize = newValue; - touchNoError(); -} - -int32_t DecimalFormat::getMinimumGroupingDigits() const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return DecimalFormatProperties::getDefault().minimumGroupingDigits; - } - return fields->properties.minimumGroupingDigits; -} - -void DecimalFormat::setMinimumGroupingDigits(int32_t newValue) { - if (fields == nullptr) { return; } - if (newValue == fields->properties.minimumGroupingDigits) { return; } - fields->properties.minimumGroupingDigits = newValue; - touchNoError(); -} - -UBool DecimalFormat::isDecimalSeparatorAlwaysShown(void) const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return DecimalFormatProperties::getDefault().decimalSeparatorAlwaysShown; - } - return fields->properties.decimalSeparatorAlwaysShown; -} - -void DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) { - if (fields == nullptr) { return; } - if (UBOOL_TO_BOOL(newValue) == fields->properties.decimalSeparatorAlwaysShown) { return; } - fields->properties.decimalSeparatorAlwaysShown = newValue; - touchNoError(); -} - -UBool DecimalFormat::isDecimalPatternMatchRequired(void) const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return DecimalFormatProperties::getDefault().decimalPatternMatchRequired; - } - return fields->properties.decimalPatternMatchRequired; -} - -void DecimalFormat::setDecimalPatternMatchRequired(UBool newValue) { - if (fields == nullptr) { return; } - if (UBOOL_TO_BOOL(newValue) == fields->properties.decimalPatternMatchRequired) { return; } - fields->properties.decimalPatternMatchRequired = newValue; - touchNoError(); -} - -UBool DecimalFormat::isParseNoExponent() const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return DecimalFormatProperties::getDefault().parseNoExponent; - } - return fields->properties.parseNoExponent; -} - -void DecimalFormat::setParseNoExponent(UBool value) { - if (fields == nullptr) { return; } - if (UBOOL_TO_BOOL(value) == fields->properties.parseNoExponent) { return; } - fields->properties.parseNoExponent = value; - touchNoError(); -} - -UBool DecimalFormat::isParseCaseSensitive() const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return DecimalFormatProperties::getDefault().parseCaseSensitive; - } - return fields->properties.parseCaseSensitive; -} - -void DecimalFormat::setParseCaseSensitive(UBool value) { - if (fields == nullptr) { return; } - if (UBOOL_TO_BOOL(value) == fields->properties.parseCaseSensitive) { return; } - fields->properties.parseCaseSensitive = value; - touchNoError(); -} - -UBool DecimalFormat::isFormatFailIfMoreThanMaxDigits() const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return DecimalFormatProperties::getDefault().formatFailIfMoreThanMaxDigits; - } - return fields->properties.formatFailIfMoreThanMaxDigits; -} - -void DecimalFormat::setFormatFailIfMoreThanMaxDigits(UBool value) { - if (fields == nullptr) { return; } - if (UBOOL_TO_BOOL(value) == fields->properties.formatFailIfMoreThanMaxDigits) { return; } - fields->properties.formatFailIfMoreThanMaxDigits = value; - touchNoError(); -} - -UnicodeString& DecimalFormat::toPattern(UnicodeString& result) const { - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - result.setToBogus(); - return result; - } - // 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() || - tprops.currencyAsDecimal || - 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; - } - result = PatternStringUtils::propertiesToPatternString(tprops, localStatus); - return result; -} - -UnicodeString& DecimalFormat::toLocalizedPattern(UnicodeString& result) const { - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - result.setToBogus(); - return result; - } - ErrorCode localStatus; - result = toPattern(result); - result = PatternStringUtils::convertLocalized(result, *getDecimalFormatSymbols(), true, localStatus); - return result; -} - -void DecimalFormat::applyPattern(const UnicodeString& pattern, UParseError&, UErrorCode& status) { - // TODO: What is parseError for? - applyPattern(pattern, status); -} - -void DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) { - // don't overwrite status if it's already a failure. - if (U_FAILURE(status)) { return; } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - 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& localizedPattern, UErrorCode& status) { - // don't overwrite status if it's already a failure. - if (U_FAILURE(status)) { return; } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - UnicodeString pattern = PatternStringUtils::convertLocalized( - localizedPattern, *getDecimalFormatSymbols(), false, status); - applyPattern(pattern, status); -} - -void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) { - if (fields == nullptr) { return; } - 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(); -} - -void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) { - if (fields == nullptr) { return; } - 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(); -} - -void DecimalFormat::setMaximumFractionDigits(int32_t newValue) { - if (fields == nullptr) { return; } - if (newValue == fields->properties.maximumFractionDigits) { return; } - // cap for backward compatibility, formerly 340, now 999 - if (newValue > kMaxIntFracSig) { - newValue = kMaxIntFracSig; - } - // 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(); -} - -void DecimalFormat::setMinimumFractionDigits(int32_t newValue) { - if (fields == nullptr) { return; } - 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 { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return DecimalFormatProperties::getDefault().minimumSignificantDigits; - } - return fields->exportedProperties.minimumSignificantDigits; -} - -int32_t DecimalFormat::getMaximumSignificantDigits() const { - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - return DecimalFormatProperties::getDefault().maximumSignificantDigits; - } - return fields->exportedProperties.maximumSignificantDigits; -} - -void DecimalFormat::setMinimumSignificantDigits(int32_t value) { - if (fields == nullptr) { return; } - if (value == fields->properties.minimumSignificantDigits) { return; } - int32_t max = fields->properties.maximumSignificantDigits; - if (max >= 0 && max < value) { - fields->properties.maximumSignificantDigits = value; - } - fields->properties.minimumSignificantDigits = value; - touchNoError(); -} - -void DecimalFormat::setMaximumSignificantDigits(int32_t value) { - if (fields == nullptr) { return; } - if (value == fields->properties.maximumSignificantDigits) { return; } - int32_t min = fields->properties.minimumSignificantDigits; - if (min >= 0 && min > value) { - fields->properties.minimumSignificantDigits = value; - } - fields->properties.maximumSignificantDigits = value; - touchNoError(); -} - -UBool DecimalFormat::areSignificantDigitsUsed() const { - const DecimalFormatProperties* dfp; - // Not much we can do to report an error. - if (fields == nullptr) { - // Fallback to using the default instance of DecimalFormatProperties. - dfp = &(DecimalFormatProperties::getDefault()); - } else { - dfp = &fields->properties; - } - return dfp->minimumSignificantDigits != -1 || dfp->maximumSignificantDigits != -1; -} - -void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits) { - if (fields == nullptr) { return; } - - // These are the default values from the old implementation. - if (useSignificantDigits) { - if (fields->properties.minimumSignificantDigits != -1 || - fields->properties.maximumSignificantDigits != -1) { - return; - } - } else { - if (fields->properties.minimumSignificantDigits == -1 && - fields->properties.maximumSignificantDigits == -1) { - return; - } - } - int32_t minSig = useSignificantDigits ? 1 : -1; - int32_t maxSig = useSignificantDigits ? 6 : -1; - fields->properties.minimumSignificantDigits = minSig; - fields->properties.maximumSignificantDigits = maxSig; - touchNoError(); -} - -void DecimalFormat::setCurrency(const char16_t* theCurrency, UErrorCode& ec) { - // don't overwrite ec if it's already a failure. - if (U_FAILURE(ec)) { return; } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - ec = U_MEMORY_ALLOCATION_ERROR; - return; - } - CurrencyUnit currencyUnit(theCurrency, ec); - if (U_FAILURE(ec)) { return; } - if (!fields->properties.currency.isNull() && fields->properties.currency.getNoError() == currencyUnit) { - return; - } - NumberFormat::setCurrency(theCurrency, ec); // to set field for compatibility - fields->properties.currency = currencyUnit; - // In Java, the DecimalFormatSymbols is mutable. Why not in C++? - LocalPointer newSymbols(new DecimalFormatSymbols(*getDecimalFormatSymbols()), ec); - newSymbols->setCurrency(currencyUnit.getISOCurrency(), ec); - fields->symbols.adoptInsteadAndCheckErrorCode(newSymbols.orphan(), ec); - touch(ec); -} - -void DecimalFormat::setCurrency(const char16_t* theCurrency) { - ErrorCode localStatus; - setCurrency(theCurrency, localStatus); -} - -void DecimalFormat::setCurrencyUsage(UCurrencyUsage newUsage, UErrorCode* ec) { - // don't overwrite ec if it's already a failure. - if (U_FAILURE(*ec)) { return; } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - *ec = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (!fields->properties.currencyUsage.isNull() && newUsage == fields->properties.currencyUsage.getNoError()) { - return; - } - fields->properties.currencyUsage = newUsage; - touch(*ec); -} - -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 == nullptr || fields->properties.currencyUsage.isNull()) { - return UCURR_USAGE_STANDARD; - } - return fields->properties.currencyUsage.getNoError(); -} - -void -DecimalFormat::formatToDecimalQuantity(double number, DecimalQuantity& output, UErrorCode& status) const { - // don't overwrite status if it's already a failure. - if (U_FAILURE(status)) { return; } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - fields->formatter.formatDouble(number, status).getDecimalQuantity(output, status); -} - -void DecimalFormat::formatToDecimalQuantity(const Formattable& number, DecimalQuantity& output, - UErrorCode& status) const { - // don't overwrite status if it's already a failure. - if (U_FAILURE(status)) { return; } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - UFormattedNumberData obj; - number.populateDecimalQuantity(obj.quantity, status); - fields->formatter.formatImpl(&obj, status); - output = std::move(obj.quantity); -} - -const number::LocalizedNumberFormatter* DecimalFormat::toNumberFormatter(UErrorCode& status) const { - // We sometimes need to return nullptr here (see ICU-20380) - if (U_FAILURE(status)) { return nullptr; } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - return &fields->formatter; -} - -/** Rebuilds the formatter object from the property bag. */ -void DecimalFormat::touch(UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - if (fields == nullptr) { - // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. - // For regular construction, the caller should have checked the status variable for errors. - // For copy construction, there is unfortunately nothing to report the error, so we need to guard against - // this possible bad state here and set the status to an error. - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - // In C++, fields->symbols (or, if it's null, the DecimalFormatSymbols owned by the underlying LocalizedNumberFormatter) - // is the source of truth for the locale. - const DecimalFormatSymbols* symbols = getDecimalFormatSymbols(); - Locale locale = symbols->getLocale(); - - // Note: The formatter is relatively cheap to create, and we need it to populate fields->exportedProperties, - // so automatically recompute 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? - - // Since memory has already been allocated for the formatter, we can move assign a stack-allocated object - // and don't need to call new. (Which is slower and could possibly fail). - // [Note that "symbols" above might point to the DecimalFormatSymbols object owned by fields->formatter. - // That's okay, because NumberPropertyMapper::create() will clone it before fields->formatter's assignment - // operator deletes it. But it does mean that "symbols" can't be counted on to be good after this line.] - fields->formatter = NumberPropertyMapper::create( - fields->properties, *symbols, fields->warehouse, fields->exportedProperties, status - ).locale(locale); - fields->symbols.adoptInstead(nullptr); // the fields->symbols property is only temporary, until we can copy it into a new LocalizedNumberFormatter - - // Do this after fields->exportedProperties are set up - setupFastFormat(); - - // Delete the parsers if they were made previously - delete fields->atomicParser.exchange(nullptr); - delete fields->atomicCurrencyParser.exchange(nullptr); - - // 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); -} - -void DecimalFormat::touchNoError() { - UErrorCode localStatus = U_ZERO_ERROR; - touch(localStatus); -} - -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); - } -} - -const numparse::impl::NumberParserImpl* DecimalFormat::getParser(UErrorCode& status) const { - // TODO: Move this into umutex.h? (similar logic also in numrange_fluent.cpp) - // See ICU-20146 - - if (U_FAILURE(status)) { - return nullptr; - } - - // First try to get the pre-computed parser - auto* ptr = fields->atomicParser.load(); - if (ptr != nullptr) { - return ptr; - } - - // Try computing the parser on our own - auto* temp = NumberParserImpl::createParserFromProperties(fields->properties, *getDecimalFormatSymbols(), false, status); - if (U_FAILURE(status)) { - return nullptr; - } - if (temp == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - - // 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; - } -} - -const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorCode& status) const { - if (U_FAILURE(status)) { return nullptr; } - - // First try to get the pre-computed parser - auto* ptr = fields->atomicCurrencyParser.load(); - if (ptr != nullptr) { - return ptr; - } - - // Try computing the parser on our own - auto* temp = NumberParserImpl::createParserFromProperties(fields->properties, *getDecimalFormatSymbols(), true, status); - if (temp == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - // although we may still dereference, call sites should be guarded - } - - // 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; - } -} - -void -DecimalFormat::fieldPositionHelper( - const UFormattedNumberData& formatted, - FieldPosition& fieldPosition, - int32_t offset, - UErrorCode& status) { - if (U_FAILURE(status)) { return; } - // 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); - } -} - -void -DecimalFormat::fieldPositionIteratorHelper( - const UFormattedNumberData& formatted, - FieldPositionIterator* fpi, - int32_t offset, - UErrorCode& status) { - if (U_SUCCESS(status) && (fpi != nullptr)) { - FieldPositionIteratorHandler fpih(fpi, status); - fpih.setShift(offset); - formatted.getAllFieldPositions(fpih, status); - } -} - -// To debug fast-format, change void(x) to printf(x) -#define trace(x) void(x) - -void DecimalFormat::setupFastFormat() { - // Check the majority of properties: - if (!fields->properties.equalsDefaultExceptFastFormat()) { - trace("no fast format: equality\n"); - fields->canUseFastFormat = false; - return; - } - - // 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; - } - - const DecimalFormatSymbols* symbols = getDecimalFormatSymbols(); - - // 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 = symbols->getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); - if (groupingUsed && (unusualGroupingSize || groupingString.length() != 1)) { - trace("no fast format: grouping\n"); - fields->canUseFastFormat = false; - return; - } - - // 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; - } - - // 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; - } - - // Other symbols: - const UnicodeString& minusSignString = symbols->getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); - UChar32 codePointZero = symbols->getCodePointZero(); - if (minusSignString.length() != 1 || U16_LENGTH(codePointZero) != 1) { - trace("no fast format: symbols\n"); - fields->canUseFastFormat = false; - return; - } - - // 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); -} - -bool DecimalFormat::fastFormatDouble(double input, UnicodeString& output) const { - if (!fields->canUseFastFormat) { - return false; - } - if (std::isnan(input) - || uprv_trunc(input) != input - || input <= INT32_MIN - || input > INT32_MAX) { - return false; - } - doFastFormatInt32(static_cast(input), std::signbit(input), output); - return true; -} - -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; -} - -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; - int8_t minInt = (fields->fastData.minInt < 1)? 1: fields->fastData.minInt; - for (int8_t i = 0; i < fields->fastData.maxInt && (input != 0 || i < 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 /* #if !UCONFIG_NO_FORMATTING */ +// © 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 +#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 VS2015 warns C4805 when comparing bool with UBool, VS2017 no longer emits this warning. +// 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 UBOOL_TO_BOOL(b) b +#endif + + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormat) + + +DecimalFormat::DecimalFormat(UErrorCode& status) + : DecimalFormat(nullptr, status) { + if (U_FAILURE(status)) { return; } + // 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) { + if (U_FAILURE(status)) { return; } + setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status); + touch(status); +} + +DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt, + UErrorCode& status) + : DecimalFormat(symbolsToAdopt, status) { + if (U_FAILURE(status)) { return; } + setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status); + touch(status); +} + +DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt, + UNumberFormatStyle style, UErrorCode& status) + : DecimalFormat(symbolsToAdopt, status) { + if (U_FAILURE(status)) { return; } + // 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 { + setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, 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); +} + +DecimalFormat::DecimalFormat(const DecimalFormatSymbols* symbolsToAdopt, UErrorCode& status) { + // we must take ownership of symbolsToAdopt, even in a failure case. + LocalPointer adoptedSymbols(symbolsToAdopt); + if (U_FAILURE(status)) { + return; + } + fields = new DecimalFormatFields(); + if (fields == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (adoptedSymbols.isNull()) { + fields->symbols.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(status), status); + } else { + fields->symbols.adoptInsteadAndCheckErrorCode(adoptedSymbols.orphan(), status); + } + if (U_FAILURE(status)) { + delete fields; + fields = nullptr; + } +} + +#if UCONFIG_HAVE_PARSEALLINPUT + +void DecimalFormat::setParseAllInput(UNumberFormatAttributeValue value) { + if (fields == nullptr) { return; } + 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; } + + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + 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_SCALE: + setMultiplierScale(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 + + case UNUM_PARSE_NO_EXPONENT: + setParseNoExponent((UBool) newValue); + break; + + case UNUM_PARSE_DECIMAL_MARK_REQUIRED: + setDecimalPatternMatchRequired((UBool) newValue); + break; + + 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; + + case UNUM_SIGN_ALWAYS_SHOWN: + setSignAlwaysShown(static_cast(newValue)); + break; + + case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: + setFormatFailIfMoreThanMaxDigits(static_cast(newValue)); + break; + + default: + status = U_UNSUPPORTED_ERROR; + break; + } + return *this; +} + +int32_t DecimalFormat::getAttribute(UNumberFormatAttribute attr, UErrorCode& status) const { + if (U_FAILURE(status)) { return -1; } + + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + 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(); + + case UNUM_MAX_INTEGER_DIGITS: + return getMaximumIntegerDigits(); + + case UNUM_MIN_INTEGER_DIGITS: + return getMinimumIntegerDigits(); + + case UNUM_INTEGER_DIGITS: + // TBD: what should this return? + return getMinimumIntegerDigits(); + + case UNUM_MAX_FRACTION_DIGITS: + return getMaximumFractionDigits(); + + 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(); + + case UNUM_MAX_SIGNIFICANT_DIGITS: + return getMaximumSignificantDigits(); + + case UNUM_MIN_SIGNIFICANT_DIGITS: + return getMinimumSignificantDigits(); + + case UNUM_MULTIPLIER: + return getMultiplier(); + + case UNUM_SCALE: + return getMultiplierScale(); + + case UNUM_GROUPING_SIZE: + return getGroupingSize(); + + case UNUM_ROUNDING_MODE: + return getRoundingMode(); + + case UNUM_FORMAT_WIDTH: + return getFormatWidth(); + + case UNUM_PADDING_POSITION: + return getPadPosition(); + + case UNUM_SECONDARY_GROUPING_SIZE: + return getSecondaryGroupingSize(); + + case UNUM_PARSE_NO_EXPONENT: + return isParseNoExponent(); + + case UNUM_PARSE_DECIMAL_MARK_REQUIRED: + return isDecimalPatternMatchRequired(); + + case UNUM_CURRENCY_USAGE: + return getCurrencyUsage(); + + case UNUM_MINIMUM_GROUPING_DIGITS: + return getMinimumGroupingDigits(); + + case UNUM_PARSE_CASE_SENSITIVE: + return isParseCaseSensitive(); + + case UNUM_SIGN_ALWAYS_SHOWN: + return isSignAlwaysShown(); + + case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: + return isFormatFailIfMoreThanMaxDigits(); + + default: + status = U_UNSUPPORTED_ERROR; + break; + } + + return -1; /* undefined */ +} + +void DecimalFormat::setGroupingUsed(UBool enabled) { + if (fields == nullptr) { + return; + } + if (UBOOL_TO_BOOL(enabled) == fields->properties.groupingUsed) { return; } + NumberFormat::setGroupingUsed(enabled); // to set field for compatibility + fields->properties.groupingUsed = enabled; + touchNoError(); +} + +void DecimalFormat::setParseIntegerOnly(UBool value) { + if (fields == nullptr) { + return; + } + if (UBOOL_TO_BOOL(value) == fields->properties.parseIntegerOnly) { return; } + NumberFormat::setParseIntegerOnly(value); // to set field for compatibility + fields->properties.parseIntegerOnly = value; + touchNoError(); +} + +void DecimalFormat::setLenient(UBool enable) { + if (fields == nullptr) { + return; + } + 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(); +} + +DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt, + UParseError&, UErrorCode& status) + : DecimalFormat(symbolsToAdopt, status) { + if (U_FAILURE(status)) { return; } + // TODO: What is parseError for? + setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status); + touch(status); +} + +DecimalFormat::DecimalFormat(const UnicodeString& pattern, const DecimalFormatSymbols& symbols, + UErrorCode& status) + : DecimalFormat(nullptr, status) { + if (U_FAILURE(status)) { return; } + LocalPointer dfs(new DecimalFormatSymbols(symbols), status); + if (U_FAILURE(status)) { + // If we failed to allocate DecimalFormatSymbols, then release fields and its members. + // We must have a fully complete fields object, we cannot have partially populated members. + delete fields; + fields = nullptr; + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + fields->symbols.adoptInstead(dfs.orphan()); + setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status); + touch(status); +} + +DecimalFormat::DecimalFormat(const DecimalFormat& source) : NumberFormat(source) { + // If the object that we are copying from is invalid, no point in going further. + if (source.fields == nullptr) { + return; + } + // 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(source.fields->properties); + if (fields == nullptr) { + return; // no way to report an error. + } + UErrorCode status = U_ZERO_ERROR; + fields->symbols.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(*source.getDecimalFormatSymbols()), status); + // In order to simplify error handling logic in the various getters/setters/etc, we do not allow + // any partially populated DecimalFormatFields object. We must have a fully complete fields object + // or else we set it to nullptr. + if (U_FAILURE(status)) { + delete fields; + fields = nullptr; + return; + } + touch(status); +} + +DecimalFormat& DecimalFormat::operator=(const DecimalFormat& rhs) { + // guard against self-assignment + if (this == &rhs) { + return *this; + } + // Make sure both objects are valid. + if (fields == nullptr || rhs.fields == nullptr) { + return *this; // unfortunately, no way to report an error. + } + fields->properties = rhs.fields->properties; + fields->exportedProperties.clear(); + UErrorCode status = U_ZERO_ERROR; + LocalPointer dfs(new DecimalFormatSymbols(*rhs.getDecimalFormatSymbols()), status); + if (U_FAILURE(status)) { + // We failed to allocate DecimalFormatSymbols, release fields and its members. + // We must have a fully complete fields object, we cannot have partially populated members. + delete fields; + fields = nullptr; + return *this; + } + fields->symbols.adoptInstead(dfs.orphan()); + touch(status); + + return *this; +} + +DecimalFormat::~DecimalFormat() { + if (fields == nullptr) { return; } + + delete fields->atomicParser.exchange(nullptr); + delete fields->atomicCurrencyParser.exchange(nullptr); + delete fields; +} + +DecimalFormat* DecimalFormat::clone() const { + // can only clone valid objects. + if (fields == nullptr) { + return nullptr; + } + LocalPointer df(new DecimalFormat(*this)); + if (df.isValid() && df->fields != nullptr) { + return df.orphan(); + } + return nullptr; +} + +bool DecimalFormat::operator==(const Format& other) const { + auto* otherDF = dynamic_cast(&other); + if (otherDF == nullptr) { + return false; + } + // If either object is in an invalid state, prevent dereferencing nullptr below. + // Additionally, invalid objects should not be considered equal to anything. + if (fields == nullptr || otherDF->fields == nullptr) { + return false; + } + return fields->properties == otherDF->fields->properties && *getDecimalFormatSymbols() == *otherDF->getDecimalFormatSymbols(); +} + +UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos) const { + if (fields == nullptr) { + appendTo.setToBogus(); + return appendTo; + } + if (pos.getField() == FieldPosition::DONT_CARE && fastFormatDouble(number, appendTo)) { + return appendTo; + } + UErrorCode localStatus = U_ZERO_ERROR; + UFormattedNumberData output; + output.quantity.setToDouble(number); + fields->formatter.formatImpl(&output, localStatus); + fieldPositionHelper(output, pos, appendTo.length(), localStatus); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable, localStatus); + return appendTo; +} + +UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return appendTo; // don't overwrite status if it's already a failure. + } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + appendTo.setToBogus(); + return appendTo; + } + if (pos.getField() == FieldPosition::DONT_CARE && fastFormatDouble(number, appendTo)) { + return appendTo; + } + UFormattedNumberData output; + output.quantity.setToDouble(number); + fields->formatter.formatImpl(&output, status); + fieldPositionHelper(output, pos, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable, status); + return appendTo; +} + +UnicodeString& +DecimalFormat::format(double number, UnicodeString& appendTo, FieldPositionIterator* posIter, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return appendTo; // don't overwrite status if it's already a failure. + } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + appendTo.setToBogus(); + return appendTo; + } + if (posIter == nullptr && fastFormatDouble(number, appendTo)) { + return appendTo; + } + UFormattedNumberData output; + output.quantity.setToDouble(number); + fields->formatter.formatImpl(&output, status); + fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable, status); + return appendTo; +} + +UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos) const { + return format(static_cast (number), appendTo, pos); +} + +UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos, + UErrorCode& status) const { + return format(static_cast (number), appendTo, pos, status); +} + +UnicodeString& +DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPositionIterator* posIter, + UErrorCode& status) const { + return format(static_cast (number), appendTo, posIter, status); +} + +UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos) const { + if (fields == nullptr) { + appendTo.setToBogus(); + return appendTo; + } + if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) { + return appendTo; + } + UErrorCode localStatus = U_ZERO_ERROR; + UFormattedNumberData output; + output.quantity.setToLong(number); + fields->formatter.formatImpl(&output, localStatus); + fieldPositionHelper(output, pos, appendTo.length(), localStatus); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable, localStatus); + return appendTo; +} + +UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return appendTo; // don't overwrite status if it's already a failure. + } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + appendTo.setToBogus(); + return appendTo; + } + if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) { + return appendTo; + } + UFormattedNumberData output; + output.quantity.setToLong(number); + fields->formatter.formatImpl(&output, status); + fieldPositionHelper(output, pos, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable, status); + return appendTo; +} + +UnicodeString& +DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPositionIterator* posIter, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return appendTo; // don't overwrite status if it's already a failure. + } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + appendTo.setToBogus(); + return appendTo; + } + if (posIter == nullptr && fastFormatInt64(number, appendTo)) { + return appendTo; + } + UFormattedNumberData output; + output.quantity.setToLong(number); + fields->formatter.formatImpl(&output, status); + fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable, status); + return appendTo; +} + +UnicodeString& +DecimalFormat::format(StringPiece number, UnicodeString& appendTo, FieldPositionIterator* posIter, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return appendTo; // don't overwrite status if it's already a failure. + } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + appendTo.setToBogus(); + return appendTo; + } + UFormattedNumberData output; + output.quantity.setToDecNumber(number, status); + fields->formatter.formatImpl(&output, status); + fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable, status); + return appendTo; +} + +UnicodeString& DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, + FieldPositionIterator* posIter, UErrorCode& status) const { + if (U_FAILURE(status)) { + return appendTo; // don't overwrite status if it's already a failure. + } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + appendTo.setToBogus(); + return appendTo; + } + UFormattedNumberData output; + output.quantity = number; + fields->formatter.formatImpl(&output, status); + fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable, status); + return appendTo; +} + +UnicodeString& +DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, FieldPosition& pos, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return appendTo; // don't overwrite status if it's already a failure. + } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + appendTo.setToBogus(); + return appendTo; + } + UFormattedNumberData output; + output.quantity = number; + fields->formatter.formatImpl(&output, status); + fieldPositionHelper(output, pos, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable, status); + return appendTo; +} + +void DecimalFormat::parse(const UnicodeString& text, Formattable& output, + ParsePosition& parsePosition) const { + if (fields == nullptr) { + return; + } + if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) { + if (parsePosition.getIndex() == text.length()) { + // If there is nothing to parse, it is an error + parsePosition.setErrorIndex(parsePosition.getIndex()); + } + return; + } + + 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; // unfortunately no way to report back the error. + } + parser->parse(text, startIndex, true, result, status); + if (U_FAILURE(status)) { + return; // unfortunately no way to report back the error. + } + // 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); + } +} + +CurrencyAmount* DecimalFormat::parseCurrency(const UnicodeString& text, ParsePosition& parsePosition) const { + if (fields == nullptr) { + return nullptr; + } + if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) { + return nullptr; + } + + 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); + if (U_FAILURE(status)) { + return nullptr; + } + // 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()); + LocalPointer currencyAmount( + new CurrencyAmount(formattable, result.currencyCode, status), status); + if (U_FAILURE(status)) { + return nullptr; + } + return currencyAmount.orphan(); + } else { + parsePosition.setErrorIndex(startIndex + result.charEnd); + return nullptr; + } +} + +const DecimalFormatSymbols* DecimalFormat::getDecimalFormatSymbols() const { + if (fields == nullptr) { + return nullptr; + } + if (!fields->symbols.isNull()) { + return fields->symbols.getAlias(); + } else { + return fields->formatter.getDecimalFormatSymbols(); + } +} + +void DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) { + if (symbolsToAdopt == nullptr) { + return; // do not allow caller to set fields->symbols to nullptr + } + // we must take ownership of symbolsToAdopt, even in a failure case. + LocalPointer dfs(symbolsToAdopt); + if (fields == nullptr) { + return; + } + fields->symbols.adoptInstead(dfs.orphan()); + touchNoError(); +} + +void DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) { + if (fields == nullptr) { + return; + } + UErrorCode status = U_ZERO_ERROR; + LocalPointer dfs(new DecimalFormatSymbols(symbols), status); + if (U_FAILURE(status)) { + // We failed to allocate DecimalFormatSymbols, release fields and its members. + // We must have a fully complete fields object, we cannot have partially populated members. + delete fields; + fields = nullptr; + return; + } + fields->symbols.adoptInstead(dfs.orphan()); + touchNoError(); +} + +const CurrencyPluralInfo* DecimalFormat::getCurrencyPluralInfo() const { + if (fields == nullptr) { + return nullptr; + } + return fields->properties.currencyPluralInfo.fPtr.getAlias(); +} + +void DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo* toAdopt) { + // TODO: should we guard against nullptr input, like in adoptDecimalFormatSymbols? + // we must take ownership of toAdopt, even in a failure case. + LocalPointer cpi(toAdopt); + if (fields == nullptr) { + return; + } + fields->properties.currencyPluralInfo.fPtr.adoptInstead(cpi.orphan()); + touchNoError(); +} + +void DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) { + if (fields == nullptr) { + return; + } + if (fields->properties.currencyPluralInfo.fPtr.isNull()) { + // Note: clone() can fail with OOM error, but we have no way to report it. :( + fields->properties.currencyPluralInfo.fPtr.adoptInstead(info.clone()); + } else { + *fields->properties.currencyPluralInfo.fPtr = info; // copy-assignment operator + } + touchNoError(); +} + +UnicodeString& DecimalFormat::getPositivePrefix(UnicodeString& result) const { + if (fields == nullptr) { + result.setToBogus(); + return result; + } + UErrorCode status = U_ZERO_ERROR; + fields->formatter.getAffixImpl(true, false, result, status); + if (U_FAILURE(status)) { result.setToBogus(); } + return result; +} + +void DecimalFormat::setPositivePrefix(const UnicodeString& newValue) { + if (fields == nullptr) { + return; + } + if (newValue == fields->properties.positivePrefix) { return; } + fields->properties.positivePrefix = newValue; + touchNoError(); +} + +UnicodeString& DecimalFormat::getNegativePrefix(UnicodeString& result) const { + if (fields == nullptr) { + result.setToBogus(); + return result; + } + UErrorCode status = U_ZERO_ERROR; + fields->formatter.getAffixImpl(true, true, result, status); + if (U_FAILURE(status)) { result.setToBogus(); } + return result; +} + +void DecimalFormat::setNegativePrefix(const UnicodeString& newValue) { + if (fields == nullptr) { + return; + } + if (newValue == fields->properties.negativePrefix) { return; } + fields->properties.negativePrefix = newValue; + touchNoError(); +} + +UnicodeString& DecimalFormat::getPositiveSuffix(UnicodeString& result) const { + if (fields == nullptr) { + result.setToBogus(); + return result; + } + UErrorCode status = U_ZERO_ERROR; + fields->formatter.getAffixImpl(false, false, result, status); + if (U_FAILURE(status)) { result.setToBogus(); } + return result; +} + +void DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) { + if (fields == nullptr) { + return; + } + if (newValue == fields->properties.positiveSuffix) { return; } + fields->properties.positiveSuffix = newValue; + touchNoError(); +} + +UnicodeString& DecimalFormat::getNegativeSuffix(UnicodeString& result) const { + if (fields == nullptr) { + result.setToBogus(); + return result; + } + UErrorCode status = U_ZERO_ERROR; + fields->formatter.getAffixImpl(false, true, result, status); + if (U_FAILURE(status)) { result.setToBogus(); } + return result; +} + +void DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) { + if (fields == nullptr) { + return; + } + if (newValue == fields->properties.negativeSuffix) { return; } + fields->properties.negativeSuffix = newValue; + touchNoError(); +} + +UBool DecimalFormat::isSignAlwaysShown() const { + // Not much we can do to report an error. + if (fields == nullptr) { + return DecimalFormatProperties::getDefault().signAlwaysShown; + } + return fields->properties.signAlwaysShown; +} + +void DecimalFormat::setSignAlwaysShown(UBool value) { + if (fields == nullptr) { return; } + if (UBOOL_TO_BOOL(value) == fields->properties.signAlwaysShown) { return; } + fields->properties.signAlwaysShown = value; + touchNoError(); +} + +int32_t DecimalFormat::getMultiplier() const { + const DecimalFormatProperties *dfp; + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + dfp = &(DecimalFormatProperties::getDefault()); + } else { + dfp = &fields->properties; + } + if (dfp->multiplier != 1) { + return dfp->multiplier; + } else if (dfp->magnitudeMultiplier != 0) { + return static_cast(uprv_pow10(dfp->magnitudeMultiplier)); + } else { + return 1; + } +} + +void DecimalFormat::setMultiplier(int32_t multiplier) { + if (fields == nullptr) { + return; + } + if (multiplier == 0) { + multiplier = 1; // one being the benign default value for a multiplier. + } + + // 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; + } + value = temp; + } + if (delta != -1) { + fields->properties.magnitudeMultiplier = delta; + fields->properties.multiplier = 1; + } else { + fields->properties.magnitudeMultiplier = 0; + fields->properties.multiplier = multiplier; + } + touchNoError(); +} + +int32_t DecimalFormat::getMultiplierScale() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return DecimalFormatProperties::getDefault().multiplierScale; + } + return fields->properties.multiplierScale; +} + +void DecimalFormat::setMultiplierScale(int32_t newValue) { + if (fields == nullptr) { return; } + if (newValue == fields->properties.multiplierScale) { return; } + fields->properties.multiplierScale = newValue; + touchNoError(); +} + +double DecimalFormat::getRoundingIncrement() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return DecimalFormatProperties::getDefault().roundingIncrement; + } + return fields->exportedProperties.roundingIncrement; +} + +void DecimalFormat::setRoundingIncrement(double newValue) { + if (fields == nullptr) { return; } + if (newValue == fields->properties.roundingIncrement) { return; } + fields->properties.roundingIncrement = newValue; + touchNoError(); +} + +ERoundingMode DecimalFormat::getRoundingMode() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return static_cast(DecimalFormatProperties::getDefault().roundingMode.getNoError()); + } + // UNumberFormatRoundingMode and ERoundingMode have the same values. + return static_cast(fields->exportedProperties.roundingMode.getNoError()); +} + +void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) UPRV_NO_SANITIZE_UNDEFINED { + if (fields == nullptr) { return; } + auto uRoundingMode = static_cast(roundingMode); + if (!fields->properties.roundingMode.isNull() && uRoundingMode == fields->properties.roundingMode.getNoError()) { + return; + } + NumberFormat::setMaximumIntegerDigits(roundingMode); // to set field for compatibility + fields->properties.roundingMode = uRoundingMode; + touchNoError(); +} + +int32_t DecimalFormat::getFormatWidth() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return DecimalFormatProperties::getDefault().formatWidth; + } + return fields->properties.formatWidth; +} + +void DecimalFormat::setFormatWidth(int32_t width) { + if (fields == nullptr) { return; } + if (width == fields->properties.formatWidth) { return; } + fields->properties.formatWidth = width; + touchNoError(); +} + +UnicodeString DecimalFormat::getPadCharacterString() const { + if (fields == nullptr || fields->properties.padString.isBogus()) { + // Readonly-alias the static string kFallbackPaddingString + return {true, kFallbackPaddingString, -1}; + } else { + return fields->properties.padString; + } +} + +void DecimalFormat::setPadCharacter(const UnicodeString& padChar) { + if (fields == nullptr) { return; } + if (padChar == fields->properties.padString) { return; } + if (padChar.length() > 0) { + fields->properties.padString = UnicodeString(padChar.char32At(0)); + } else { + fields->properties.padString.setToBogus(); + } + touchNoError(); +} + +EPadPosition DecimalFormat::getPadPosition() const { + if (fields == nullptr || fields->properties.padPosition.isNull()) { + return EPadPosition::kPadBeforePrefix; + } else { + // UNumberFormatPadPosition and EPadPosition have the same values. + return static_cast(fields->properties.padPosition.getNoError()); + } +} + +void DecimalFormat::setPadPosition(EPadPosition padPos) { + if (fields == nullptr) { return; } + auto uPadPos = static_cast(padPos); + if (!fields->properties.padPosition.isNull() && uPadPos == fields->properties.padPosition.getNoError()) { + return; + } + fields->properties.padPosition = uPadPos; + touchNoError(); +} + +UBool DecimalFormat::isScientificNotation() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return (DecimalFormatProperties::getDefault().minimumExponentDigits != -1); + } + return (fields->properties.minimumExponentDigits != -1); +} + +void DecimalFormat::setScientificNotation(UBool useScientific) { + if (fields == nullptr) { return; } + int32_t minExp = useScientific ? 1 : -1; + if (fields->properties.minimumExponentDigits == minExp) { return; } + if (useScientific) { + fields->properties.minimumExponentDigits = 1; + } else { + fields->properties.minimumExponentDigits = -1; + } + touchNoError(); +} + +int8_t DecimalFormat::getMinimumExponentDigits() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return static_cast(DecimalFormatProperties::getDefault().minimumExponentDigits); + } + return static_cast(fields->properties.minimumExponentDigits); +} + +void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) { + if (fields == nullptr) { return; } + if (minExpDig == fields->properties.minimumExponentDigits) { return; } + fields->properties.minimumExponentDigits = minExpDig; + touchNoError(); +} + +UBool DecimalFormat::isExponentSignAlwaysShown() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return DecimalFormatProperties::getDefault().exponentSignAlwaysShown; + } + return fields->properties.exponentSignAlwaysShown; +} + +void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) { + if (fields == nullptr) { return; } + if (UBOOL_TO_BOOL(expSignAlways) == fields->properties.exponentSignAlwaysShown) { return; } + fields->properties.exponentSignAlwaysShown = expSignAlways; + touchNoError(); +} + +int32_t DecimalFormat::getGroupingSize() const { + int32_t groupingSize; + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + groupingSize = DecimalFormatProperties::getDefault().groupingSize; + } else { + groupingSize = fields->properties.groupingSize; + } + if (groupingSize < 0) { + return 0; + } + return groupingSize; +} + +void DecimalFormat::setGroupingSize(int32_t newValue) { + if (fields == nullptr) { return; } + if (newValue == fields->properties.groupingSize) { return; } + fields->properties.groupingSize = newValue; + touchNoError(); +} + +int32_t DecimalFormat::getSecondaryGroupingSize() const { + int32_t grouping2; + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + grouping2 = DecimalFormatProperties::getDefault().secondaryGroupingSize; + } else { + grouping2 = fields->properties.secondaryGroupingSize; + } + if (grouping2 < 0) { + return 0; + } + return grouping2; +} + +void DecimalFormat::setSecondaryGroupingSize(int32_t newValue) { + if (fields == nullptr) { return; } + if (newValue == fields->properties.secondaryGroupingSize) { return; } + fields->properties.secondaryGroupingSize = newValue; + touchNoError(); +} + +int32_t DecimalFormat::getMinimumGroupingDigits() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return DecimalFormatProperties::getDefault().minimumGroupingDigits; + } + return fields->properties.minimumGroupingDigits; +} + +void DecimalFormat::setMinimumGroupingDigits(int32_t newValue) { + if (fields == nullptr) { return; } + if (newValue == fields->properties.minimumGroupingDigits) { return; } + fields->properties.minimumGroupingDigits = newValue; + touchNoError(); +} + +UBool DecimalFormat::isDecimalSeparatorAlwaysShown() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return DecimalFormatProperties::getDefault().decimalSeparatorAlwaysShown; + } + return fields->properties.decimalSeparatorAlwaysShown; +} + +void DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) { + if (fields == nullptr) { return; } + if (UBOOL_TO_BOOL(newValue) == fields->properties.decimalSeparatorAlwaysShown) { return; } + fields->properties.decimalSeparatorAlwaysShown = newValue; + touchNoError(); +} + +UBool DecimalFormat::isDecimalPatternMatchRequired() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return DecimalFormatProperties::getDefault().decimalPatternMatchRequired; + } + return fields->properties.decimalPatternMatchRequired; +} + +void DecimalFormat::setDecimalPatternMatchRequired(UBool newValue) { + if (fields == nullptr) { return; } + if (UBOOL_TO_BOOL(newValue) == fields->properties.decimalPatternMatchRequired) { return; } + fields->properties.decimalPatternMatchRequired = newValue; + touchNoError(); +} + +UBool DecimalFormat::isParseNoExponent() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return DecimalFormatProperties::getDefault().parseNoExponent; + } + return fields->properties.parseNoExponent; +} + +void DecimalFormat::setParseNoExponent(UBool value) { + if (fields == nullptr) { return; } + if (UBOOL_TO_BOOL(value) == fields->properties.parseNoExponent) { return; } + fields->properties.parseNoExponent = value; + touchNoError(); +} + +UBool DecimalFormat::isParseCaseSensitive() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return DecimalFormatProperties::getDefault().parseCaseSensitive; + } + return fields->properties.parseCaseSensitive; +} + +void DecimalFormat::setParseCaseSensitive(UBool value) { + if (fields == nullptr) { return; } + if (UBOOL_TO_BOOL(value) == fields->properties.parseCaseSensitive) { return; } + fields->properties.parseCaseSensitive = value; + touchNoError(); +} + +UBool DecimalFormat::isFormatFailIfMoreThanMaxDigits() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return DecimalFormatProperties::getDefault().formatFailIfMoreThanMaxDigits; + } + return fields->properties.formatFailIfMoreThanMaxDigits; +} + +void DecimalFormat::setFormatFailIfMoreThanMaxDigits(UBool value) { + if (fields == nullptr) { return; } + if (UBOOL_TO_BOOL(value) == fields->properties.formatFailIfMoreThanMaxDigits) { return; } + fields->properties.formatFailIfMoreThanMaxDigits = value; + touchNoError(); +} + +UnicodeString& DecimalFormat::toPattern(UnicodeString& result) const { + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + result.setToBogus(); + return result; + } + // 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() || + tprops.currencyAsDecimal || + 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; + } + result = PatternStringUtils::propertiesToPatternString(tprops, localStatus); + return result; +} + +UnicodeString& DecimalFormat::toLocalizedPattern(UnicodeString& result) const { + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + result.setToBogus(); + return result; + } + ErrorCode localStatus; + result = toPattern(result); + result = PatternStringUtils::convertLocalized(result, *getDecimalFormatSymbols(), true, localStatus); + return result; +} + +void DecimalFormat::applyPattern(const UnicodeString& pattern, UParseError&, UErrorCode& status) { + // TODO: What is parseError for? + applyPattern(pattern, status); +} + +void DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) { + // don't overwrite status if it's already a failure. + if (U_FAILURE(status)) { return; } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + 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& localizedPattern, UErrorCode& status) { + // don't overwrite status if it's already a failure. + if (U_FAILURE(status)) { return; } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + UnicodeString pattern = PatternStringUtils::convertLocalized( + localizedPattern, *getDecimalFormatSymbols(), false, status); + applyPattern(pattern, status); +} + +void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) { + if (fields == nullptr) { return; } + 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(); +} + +void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) { + if (fields == nullptr) { return; } + 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(); +} + +void DecimalFormat::setMaximumFractionDigits(int32_t newValue) { + if (fields == nullptr) { return; } + if (newValue == fields->properties.maximumFractionDigits) { return; } + // cap for backward compatibility, formerly 340, now 999 + if (newValue > kMaxIntFracSig) { + newValue = kMaxIntFracSig; + } + // 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(); +} + +void DecimalFormat::setMinimumFractionDigits(int32_t newValue) { + if (fields == nullptr) { return; } + 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 { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return DecimalFormatProperties::getDefault().minimumSignificantDigits; + } + return fields->exportedProperties.minimumSignificantDigits; +} + +int32_t DecimalFormat::getMaximumSignificantDigits() const { + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + return DecimalFormatProperties::getDefault().maximumSignificantDigits; + } + return fields->exportedProperties.maximumSignificantDigits; +} + +void DecimalFormat::setMinimumSignificantDigits(int32_t value) { + if (fields == nullptr) { return; } + if (value == fields->properties.minimumSignificantDigits) { return; } + int32_t max = fields->properties.maximumSignificantDigits; + if (max >= 0 && max < value) { + fields->properties.maximumSignificantDigits = value; + } + fields->properties.minimumSignificantDigits = value; + touchNoError(); +} + +void DecimalFormat::setMaximumSignificantDigits(int32_t value) { + if (fields == nullptr) { return; } + if (value == fields->properties.maximumSignificantDigits) { return; } + int32_t min = fields->properties.minimumSignificantDigits; + if (min >= 0 && min > value) { + fields->properties.minimumSignificantDigits = value; + } + fields->properties.maximumSignificantDigits = value; + touchNoError(); +} + +UBool DecimalFormat::areSignificantDigitsUsed() const { + const DecimalFormatProperties* dfp; + // Not much we can do to report an error. + if (fields == nullptr) { + // Fallback to using the default instance of DecimalFormatProperties. + dfp = &(DecimalFormatProperties::getDefault()); + } else { + dfp = &fields->properties; + } + return dfp->minimumSignificantDigits != -1 || dfp->maximumSignificantDigits != -1; +} + +void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits) { + if (fields == nullptr) { return; } + + // These are the default values from the old implementation. + if (useSignificantDigits) { + if (fields->properties.minimumSignificantDigits != -1 || + fields->properties.maximumSignificantDigits != -1) { + return; + } + } else { + if (fields->properties.minimumSignificantDigits == -1 && + fields->properties.maximumSignificantDigits == -1) { + return; + } + } + int32_t minSig = useSignificantDigits ? 1 : -1; + int32_t maxSig = useSignificantDigits ? 6 : -1; + fields->properties.minimumSignificantDigits = minSig; + fields->properties.maximumSignificantDigits = maxSig; + touchNoError(); +} + +void DecimalFormat::setCurrency(const char16_t* theCurrency, UErrorCode& ec) { + // don't overwrite ec if it's already a failure. + if (U_FAILURE(ec)) { return; } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + ec = U_MEMORY_ALLOCATION_ERROR; + return; + } + CurrencyUnit currencyUnit(theCurrency, ec); + if (U_FAILURE(ec)) { return; } + if (!fields->properties.currency.isNull() && fields->properties.currency.getNoError() == currencyUnit) { + return; + } + NumberFormat::setCurrency(theCurrency, ec); // to set field for compatibility + fields->properties.currency = currencyUnit; + // In Java, the DecimalFormatSymbols is mutable. Why not in C++? + LocalPointer newSymbols(new DecimalFormatSymbols(*getDecimalFormatSymbols()), ec); + newSymbols->setCurrency(currencyUnit.getISOCurrency(), ec); + fields->symbols.adoptInsteadAndCheckErrorCode(newSymbols.orphan(), ec); + touch(ec); +} + +void DecimalFormat::setCurrency(const char16_t* theCurrency) { + ErrorCode localStatus; + setCurrency(theCurrency, localStatus); +} + +void DecimalFormat::setCurrencyUsage(UCurrencyUsage newUsage, UErrorCode* ec) { + // don't overwrite ec if it's already a failure. + if (U_FAILURE(*ec)) { return; } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + *ec = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (!fields->properties.currencyUsage.isNull() && newUsage == fields->properties.currencyUsage.getNoError()) { + return; + } + fields->properties.currencyUsage = newUsage; + touch(*ec); +} + +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 == nullptr || fields->properties.currencyUsage.isNull()) { + return UCURR_USAGE_STANDARD; + } + return fields->properties.currencyUsage.getNoError(); +} + +void +DecimalFormat::formatToDecimalQuantity(double number, DecimalQuantity& output, UErrorCode& status) const { + // don't overwrite status if it's already a failure. + if (U_FAILURE(status)) { return; } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + fields->formatter.formatDouble(number, status).getDecimalQuantity(output, status); +} + +void DecimalFormat::formatToDecimalQuantity(const Formattable& number, DecimalQuantity& output, + UErrorCode& status) const { + // don't overwrite status if it's already a failure. + if (U_FAILURE(status)) { return; } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + UFormattedNumberData obj; + number.populateDecimalQuantity(obj.quantity, status); + fields->formatter.formatImpl(&obj, status); + output = std::move(obj.quantity); +} + +const number::LocalizedNumberFormatter* DecimalFormat::toNumberFormatter(UErrorCode& status) const { + // We sometimes need to return nullptr here (see ICU-20380) + if (U_FAILURE(status)) { return nullptr; } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + return &fields->formatter; +} + +/** Rebuilds the formatter object from the property bag. */ +void DecimalFormat::touch(UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (fields == nullptr) { + // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. + // For regular construction, the caller should have checked the status variable for errors. + // For copy construction, there is unfortunately nothing to report the error, so we need to guard against + // this possible bad state here and set the status to an error. + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + // In C++, fields->symbols (or, if it's null, the DecimalFormatSymbols owned by the underlying LocalizedNumberFormatter) + // is the source of truth for the locale. + const DecimalFormatSymbols* symbols = getDecimalFormatSymbols(); + Locale locale = symbols->getLocale(); + + // Note: The formatter is relatively cheap to create, and we need it to populate fields->exportedProperties, + // so automatically recompute 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? + + // Since memory has already been allocated for the formatter, we can move assign a stack-allocated object + // and don't need to call new. (Which is slower and could possibly fail). + // [Note that "symbols" above might point to the DecimalFormatSymbols object owned by fields->formatter. + // That's okay, because NumberPropertyMapper::create() will clone it before fields->formatter's assignment + // operator deletes it. But it does mean that "symbols" can't be counted on to be good after this line.] + fields->formatter = NumberPropertyMapper::create( + fields->properties, *symbols, fields->warehouse, fields->exportedProperties, status + ).locale(locale); + fields->symbols.adoptInstead(nullptr); // the fields->symbols property is only temporary, until we can copy it into a new LocalizedNumberFormatter + + // Do this after fields->exportedProperties are set up + setupFastFormat(); + + // Delete the parsers if they were made previously + delete fields->atomicParser.exchange(nullptr); + delete fields->atomicCurrencyParser.exchange(nullptr); + + // 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); +} + +void DecimalFormat::touchNoError() { + UErrorCode localStatus = U_ZERO_ERROR; + touch(localStatus); +} + +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); + } +} + +const numparse::impl::NumberParserImpl* DecimalFormat::getParser(UErrorCode& status) const { + // TODO: Move this into umutex.h? (similar logic also in numrange_fluent.cpp) + // See ICU-20146 + + if (U_FAILURE(status)) { + return nullptr; + } + + // First try to get the pre-computed parser + auto* ptr = fields->atomicParser.load(); + if (ptr != nullptr) { + return ptr; + } + + // Try computing the parser on our own + auto* temp = NumberParserImpl::createParserFromProperties(fields->properties, *getDecimalFormatSymbols(), false, status); + if (U_FAILURE(status)) { + return nullptr; + } + if (temp == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + // 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; + } +} + +const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorCode& status) const { + if (U_FAILURE(status)) { return nullptr; } + + // First try to get the pre-computed parser + auto* ptr = fields->atomicCurrencyParser.load(); + if (ptr != nullptr) { + return ptr; + } + + // Try computing the parser on our own + auto* temp = NumberParserImpl::createParserFromProperties(fields->properties, *getDecimalFormatSymbols(), true, status); + if (temp == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + // although we may still dereference, call sites should be guarded + } + + // 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; + } +} + +void +DecimalFormat::fieldPositionHelper( + const UFormattedNumberData& formatted, + FieldPosition& fieldPosition, + int32_t offset, + UErrorCode& status) { + if (U_FAILURE(status)) { return; } + // 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); + } +} + +void +DecimalFormat::fieldPositionIteratorHelper( + const UFormattedNumberData& formatted, + FieldPositionIterator* fpi, + int32_t offset, + UErrorCode& status) { + if (U_SUCCESS(status) && (fpi != nullptr)) { + FieldPositionIteratorHandler fpih(fpi, status); + fpih.setShift(offset); + formatted.getAllFieldPositions(fpih, status); + } +} + +// To debug fast-format, change void(x) to printf(x) +#define trace(x) void(x) + +void DecimalFormat::setupFastFormat() { + // Check the majority of properties: + if (!fields->properties.equalsDefaultExceptFastFormat()) { + trace("no fast format: equality\n"); + fields->canUseFastFormat = false; + return; + } + + // 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; + } + + const DecimalFormatSymbols* symbols = getDecimalFormatSymbols(); + + // 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 = symbols->getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); + if (groupingUsed && (unusualGroupingSize || groupingString.length() != 1)) { + trace("no fast format: grouping\n"); + fields->canUseFastFormat = false; + return; + } + + // 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; + } + + // 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; + } + + // Other symbols: + const UnicodeString& minusSignString = symbols->getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); + UChar32 codePointZero = symbols->getCodePointZero(); + if (minusSignString.length() != 1 || U16_LENGTH(codePointZero) != 1) { + trace("no fast format: symbols\n"); + fields->canUseFastFormat = false; + return; + } + + // 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); +} + +bool DecimalFormat::fastFormatDouble(double input, UnicodeString& output) const { + if (!fields->canUseFastFormat) { + return false; + } + if (std::isnan(input) + || uprv_trunc(input) != input + || input <= INT32_MIN + || input > INT32_MAX) { + return false; + } + doFastFormatInt32(static_cast(input), std::signbit(input), output); + return true; +} + +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; +} + +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; + int8_t minInt = (fields->fastData.minInt < 1)? 1: fields->fastData.minInt; + for (int8_t i = 0; i < fields->fastData.maxInt && (input != 0 || i < 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 /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/displayoptions.cpp b/deps/icu-small/source/i18n/displayoptions.cpp index bb49e6033f3c9f..41635cb0a7802f 100644 --- a/deps/icu-small/source/i18n/displayoptions.cpp +++ b/deps/icu-small/source/i18n/displayoptions.cpp @@ -1,167 +1,167 @@ -// © 2022 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/displayoptions.h" -#include "unicode/udisplayoptions.h" -#include "cstring.h" - -U_NAMESPACE_BEGIN - -DisplayOptions::Builder DisplayOptions::builder() { return DisplayOptions::Builder(); } - -DisplayOptions::Builder DisplayOptions::copyToBuilder() const { return Builder(*this); } - -DisplayOptions::DisplayOptions(const Builder &builder) { - grammaticalCase = builder.grammaticalCase; - nounClass = builder.nounClass; - pluralCategory = builder.pluralCategory; - capitalization = builder.capitalization; - nameStyle = builder.nameStyle; - displayLength = builder.displayLength; - substituteHandling = builder.substituteHandling; -} - -DisplayOptions::Builder::Builder() { - // Sets default values. - grammaticalCase = UDISPOPT_GRAMMATICAL_CASE_UNDEFINED; - nounClass = UDISPOPT_NOUN_CLASS_UNDEFINED; - pluralCategory = UDISPOPT_PLURAL_CATEGORY_UNDEFINED; - capitalization = UDISPOPT_CAPITALIZATION_UNDEFINED; - nameStyle = UDISPOPT_NAME_STYLE_UNDEFINED; - displayLength = UDISPOPT_DISPLAY_LENGTH_UNDEFINED; - substituteHandling = UDISPOPT_SUBSTITUTE_HANDLING_UNDEFINED; -} - -DisplayOptions::Builder::Builder(const DisplayOptions &displayOptions) { - grammaticalCase = displayOptions.grammaticalCase; - nounClass = displayOptions.nounClass; - pluralCategory = displayOptions.pluralCategory; - capitalization = displayOptions.capitalization; - nameStyle = displayOptions.nameStyle; - displayLength = displayOptions.displayLength; - substituteHandling = displayOptions.substituteHandling; -} - -U_NAMESPACE_END - -// C API ------------------------------------------------------------------- *** - -U_NAMESPACE_USE - -namespace { - -const char *grammaticalCaseIds[] = { - "undefined", // 0 - "ablative", // 1 - "accusative", // 2 - "comitative", // 3 - "dative", // 4 - "ergative", // 5 - "genitive", // 6 - "instrumental", // 7 - "locative", // 8 - "locative_copulative", // 9 - "nominative", // 10 - "oblique", // 11 - "prepositional", // 12 - "sociative", // 13 - "vocative", // 14 -}; - -} // namespace - -U_CAPI const char * U_EXPORT2 -udispopt_getGrammaticalCaseIdentifier(UDisplayOptionsGrammaticalCase grammaticalCase) { - if (grammaticalCase >= 0 && grammaticalCase < UPRV_LENGTHOF(grammaticalCaseIds)) { - return grammaticalCaseIds[grammaticalCase]; - } - - return grammaticalCaseIds[0]; -} - -U_CAPI UDisplayOptionsGrammaticalCase U_EXPORT2 -udispopt_fromGrammaticalCaseIdentifier(const char *identifier) { - for (int32_t i = 0; i < UPRV_LENGTHOF(grammaticalCaseIds); i++) { - if (uprv_strcmp(identifier, grammaticalCaseIds[i]) == 0) { - return static_cast(i); - } - } - - return UDISPOPT_GRAMMATICAL_CASE_UNDEFINED; -} - -namespace { - -const char *pluralCategoryIds[] = { - "undefined", // 0 - "zero", // 1 - "one", // 2 - "two", // 3 - "few", // 4 - "many", // 5 - "other", // 6 -}; - -} // namespace - -U_CAPI const char * U_EXPORT2 -udispopt_getPluralCategoryIdentifier(UDisplayOptionsPluralCategory pluralCategory) { - if (pluralCategory >= 0 && pluralCategory < UPRV_LENGTHOF(pluralCategoryIds)) { - return pluralCategoryIds[pluralCategory]; - } - - return pluralCategoryIds[0]; -} - -U_CAPI UDisplayOptionsPluralCategory U_EXPORT2 -udispopt_fromPluralCategoryIdentifier(const char *identifier) { - for (int32_t i = 0; i < UPRV_LENGTHOF(pluralCategoryIds); i++) { - if (uprv_strcmp(identifier, pluralCategoryIds[i]) == 0) { - return static_cast(i); - } - } - - return UDISPOPT_PLURAL_CATEGORY_UNDEFINED; -} - -namespace { - -const char *nounClassIds[] = { - "undefined", // 0 - "other", // 1 - "neuter", // 2 - "feminine", // 3 - "masculine", // 4 - "animate", // 5 - "inanimate", // 6 - "personal", // 7 - "common", // 8 -}; - -} // namespace - -U_CAPI const char * U_EXPORT2 -udispopt_getNounClassIdentifier(UDisplayOptionsNounClass nounClass) { - if (nounClass >= 0 && nounClass < UPRV_LENGTHOF(nounClassIds)) { - return nounClassIds[nounClass]; - } - - return nounClassIds[0]; -} - -U_CAPI UDisplayOptionsNounClass U_EXPORT2 -udispopt_fromNounClassIdentifier(const char *identifier) { - for (int32_t i = 0; i < UPRV_LENGTHOF(nounClassIds); i++) { - if (uprv_strcmp(identifier, nounClassIds[i]) == 0) { - return static_cast(i); - } - } - - return UDISPOPT_NOUN_CLASS_UNDEFINED; -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 2022 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/displayoptions.h" +#include "unicode/udisplayoptions.h" +#include "cstring.h" + +U_NAMESPACE_BEGIN + +DisplayOptions::Builder DisplayOptions::builder() { return DisplayOptions::Builder(); } + +DisplayOptions::Builder DisplayOptions::copyToBuilder() const { return Builder(*this); } + +DisplayOptions::DisplayOptions(const Builder &builder) { + grammaticalCase = builder.grammaticalCase; + nounClass = builder.nounClass; + pluralCategory = builder.pluralCategory; + capitalization = builder.capitalization; + nameStyle = builder.nameStyle; + displayLength = builder.displayLength; + substituteHandling = builder.substituteHandling; +} + +DisplayOptions::Builder::Builder() { + // Sets default values. + grammaticalCase = UDISPOPT_GRAMMATICAL_CASE_UNDEFINED; + nounClass = UDISPOPT_NOUN_CLASS_UNDEFINED; + pluralCategory = UDISPOPT_PLURAL_CATEGORY_UNDEFINED; + capitalization = UDISPOPT_CAPITALIZATION_UNDEFINED; + nameStyle = UDISPOPT_NAME_STYLE_UNDEFINED; + displayLength = UDISPOPT_DISPLAY_LENGTH_UNDEFINED; + substituteHandling = UDISPOPT_SUBSTITUTE_HANDLING_UNDEFINED; +} + +DisplayOptions::Builder::Builder(const DisplayOptions &displayOptions) { + grammaticalCase = displayOptions.grammaticalCase; + nounClass = displayOptions.nounClass; + pluralCategory = displayOptions.pluralCategory; + capitalization = displayOptions.capitalization; + nameStyle = displayOptions.nameStyle; + displayLength = displayOptions.displayLength; + substituteHandling = displayOptions.substituteHandling; +} + +U_NAMESPACE_END + +// C API ------------------------------------------------------------------- *** + +U_NAMESPACE_USE + +namespace { + +const char *grammaticalCaseIds[] = { + "undefined", // 0 + "ablative", // 1 + "accusative", // 2 + "comitative", // 3 + "dative", // 4 + "ergative", // 5 + "genitive", // 6 + "instrumental", // 7 + "locative", // 8 + "locative_copulative", // 9 + "nominative", // 10 + "oblique", // 11 + "prepositional", // 12 + "sociative", // 13 + "vocative", // 14 +}; + +} // namespace + +U_CAPI const char * U_EXPORT2 +udispopt_getGrammaticalCaseIdentifier(UDisplayOptionsGrammaticalCase grammaticalCase) { + if (grammaticalCase >= 0 && grammaticalCase < UPRV_LENGTHOF(grammaticalCaseIds)) { + return grammaticalCaseIds[grammaticalCase]; + } + + return grammaticalCaseIds[0]; +} + +U_CAPI UDisplayOptionsGrammaticalCase U_EXPORT2 +udispopt_fromGrammaticalCaseIdentifier(const char *identifier) { + for (int32_t i = 0; i < UPRV_LENGTHOF(grammaticalCaseIds); i++) { + if (uprv_strcmp(identifier, grammaticalCaseIds[i]) == 0) { + return static_cast(i); + } + } + + return UDISPOPT_GRAMMATICAL_CASE_UNDEFINED; +} + +namespace { + +const char *pluralCategoryIds[] = { + "undefined", // 0 + "zero", // 1 + "one", // 2 + "two", // 3 + "few", // 4 + "many", // 5 + "other", // 6 +}; + +} // namespace + +U_CAPI const char * U_EXPORT2 +udispopt_getPluralCategoryIdentifier(UDisplayOptionsPluralCategory pluralCategory) { + if (pluralCategory >= 0 && pluralCategory < UPRV_LENGTHOF(pluralCategoryIds)) { + return pluralCategoryIds[pluralCategory]; + } + + return pluralCategoryIds[0]; +} + +U_CAPI UDisplayOptionsPluralCategory U_EXPORT2 +udispopt_fromPluralCategoryIdentifier(const char *identifier) { + for (int32_t i = 0; i < UPRV_LENGTHOF(pluralCategoryIds); i++) { + if (uprv_strcmp(identifier, pluralCategoryIds[i]) == 0) { + return static_cast(i); + } + } + + return UDISPOPT_PLURAL_CATEGORY_UNDEFINED; +} + +namespace { + +const char *nounClassIds[] = { + "undefined", // 0 + "other", // 1 + "neuter", // 2 + "feminine", // 3 + "masculine", // 4 + "animate", // 5 + "inanimate", // 6 + "personal", // 7 + "common", // 8 +}; + +} // namespace + +U_CAPI const char * U_EXPORT2 +udispopt_getNounClassIdentifier(UDisplayOptionsNounClass nounClass) { + if (nounClass >= 0 && nounClass < UPRV_LENGTHOF(nounClassIds)) { + return nounClassIds[nounClass]; + } + + return nounClassIds[0]; +} + +U_CAPI UDisplayOptionsNounClass U_EXPORT2 +udispopt_fromNounClassIdentifier(const char *identifier) { + for (int32_t i = 0; i < UPRV_LENGTHOF(nounClassIds); i++) { + if (uprv_strcmp(identifier, nounClassIds[i]) == 0) { + return static_cast(i); + } + } + + return UDISPOPT_NOUN_CLASS_UNDEFINED; +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/double-conversion-bignum-dtoa.cpp b/deps/icu-small/source/i18n/double-conversion-bignum-dtoa.cpp index 638e9cb0469750..4efd33cd5ec516 100644 --- a/deps/icu-small/source/i18n/double-conversion-bignum-dtoa.cpp +++ b/deps/icu-small/source/i18n/double-conversion-bignum-dtoa.cpp @@ -1,659 +1,659 @@ -// © 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 - -// ICU PATCH: Customize header file paths for ICU. - -#include "double-conversion-bignum-dtoa.h" - -#include "double-conversion-bignum.h" -#include "double-conversion-ieee.h" - -// ICU PATCH: Wrap in ICU namespace -U_NAMESPACE_BEGIN - -namespace double_conversion { - -static int NormalizedExponent(uint64_t significand, int exponent) { - DOUBLE_CONVERSION_ASSERT(significand != 0); - while ((significand & Double::kHiddenBit) == 0) { - significand = significand << 1; - exponent = exponent - 1; - } - return exponent; -} - - -// Forward declarations: -// Returns an estimation of k such that 10^(k-1) <= v < 10^k. -static int EstimatePower(int exponent); -// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator -// and denominator. -static void InitialScaledStartValues(uint64_t significand, - int exponent, - bool lower_boundary_is_closer, - int estimated_power, - bool need_boundary_deltas, - Bignum* numerator, - Bignum* denominator, - Bignum* delta_minus, - Bignum* delta_plus); -// Multiplies numerator/denominator so that its values lies in the range 1-10. -// Returns decimal_point s.t. -// v = numerator'/denominator' * 10^(decimal_point-1) -// where numerator' and denominator' are the values of numerator and -// denominator after the call to this function. -static void FixupMultiply10(int estimated_power, bool is_even, - int* decimal_point, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus); -// Generates digits from the left to the right and stops when the generated -// digits yield the shortest decimal representation of v. -static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus, - bool is_even, - Vector buffer, int* length); -// Generates 'requested_digits' after the decimal point. -static void BignumToFixed(int requested_digits, int* decimal_point, - Bignum* numerator, Bignum* denominator, - Vector buffer, int* length); -// Generates 'count' digits of numerator/denominator. -// Once 'count' digits have been produced rounds the result depending on the -// remainder (remainders of exactly .5 round upwards). Might update the -// decimal_point when rounding up (for example for 0.9999). -static void GenerateCountedDigits(int count, int* decimal_point, - Bignum* numerator, Bignum* denominator, - Vector buffer, int* length); - - -void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, - Vector buffer, int* length, int* decimal_point) { - DOUBLE_CONVERSION_ASSERT(v > 0); - DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial()); - uint64_t significand; - int exponent; - bool lower_boundary_is_closer; - if (mode == BIGNUM_DTOA_SHORTEST_SINGLE) { - float f = static_cast(v); - DOUBLE_CONVERSION_ASSERT(f == v); - significand = Single(f).Significand(); - exponent = Single(f).Exponent(); - lower_boundary_is_closer = Single(f).LowerBoundaryIsCloser(); - } else { - significand = Double(v).Significand(); - exponent = Double(v).Exponent(); - lower_boundary_is_closer = Double(v).LowerBoundaryIsCloser(); - } - bool need_boundary_deltas = - (mode == BIGNUM_DTOA_SHORTEST || mode == BIGNUM_DTOA_SHORTEST_SINGLE); - - bool is_even = (significand & 1) == 0; - int normalized_exponent = NormalizedExponent(significand, exponent); - // estimated_power might be too low by 1. - int estimated_power = EstimatePower(normalized_exponent); - - // Shortcut for Fixed. - // The requested digits correspond to the digits after the point. If the - // number is much too small, then there is no need in trying to get any - // digits. - if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { - buffer[0] = '\0'; - *length = 0; - // Set decimal-point to -requested_digits. This is what Gay does. - // Note that it should not have any effect anyways since the string is - // empty. - *decimal_point = -requested_digits; - return; - } - - Bignum numerator; - Bignum denominator; - Bignum delta_minus; - Bignum delta_plus; - // Make sure the bignum can grow large enough. The smallest double equals - // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. - // The maximum double is 1.7976931348623157e308 which needs fewer than - // 308*4 binary digits. - DOUBLE_CONVERSION_ASSERT(Bignum::kMaxSignificantBits >= 324*4); - InitialScaledStartValues(significand, exponent, lower_boundary_is_closer, - estimated_power, need_boundary_deltas, - &numerator, &denominator, - &delta_minus, &delta_plus); - // We now have v = (numerator / denominator) * 10^estimated_power. - FixupMultiply10(estimated_power, is_even, decimal_point, - &numerator, &denominator, - &delta_minus, &delta_plus); - // We now have v = (numerator / denominator) * 10^(decimal_point-1), and - // 1 <= (numerator + delta_plus) / denominator < 10 - switch (mode) { - case BIGNUM_DTOA_SHORTEST: - case BIGNUM_DTOA_SHORTEST_SINGLE: - GenerateShortestDigits(&numerator, &denominator, - &delta_minus, &delta_plus, - is_even, buffer, length); - break; - case BIGNUM_DTOA_FIXED: - BignumToFixed(requested_digits, decimal_point, - &numerator, &denominator, - buffer, length); - break; - case BIGNUM_DTOA_PRECISION: - GenerateCountedDigits(requested_digits, decimal_point, - &numerator, &denominator, - buffer, length); - break; - default: - DOUBLE_CONVERSION_UNREACHABLE(); - } - buffer[*length] = '\0'; -} - - -// The procedure starts generating digits from the left to the right and stops -// when the generated digits yield the shortest decimal representation of v. A -// decimal representation of v is a number lying closer to v than to any other -// double, so it converts to v when read. -// -// This is true if d, the decimal representation, is between m- and m+, the -// upper and lower boundaries. d must be strictly between them if !is_even. -// m- := (numerator - delta_minus) / denominator -// m+ := (numerator + delta_plus) / denominator -// -// Precondition: 0 <= (numerator+delta_plus) / denominator < 10. -// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit -// will be produced. This should be the standard precondition. -static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus, - bool is_even, - Vector buffer, int* length) { - // Small optimization: if delta_minus and delta_plus are the same just reuse - // one of the two bignums. - if (Bignum::Equal(*delta_minus, *delta_plus)) { - delta_plus = delta_minus; - } - *length = 0; - for (;;) { - uint16_t digit; - digit = numerator->DivideModuloIntBignum(*denominator); - DOUBLE_CONVERSION_ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. - // digit = numerator / denominator (integer division). - // numerator = numerator % denominator. - buffer[(*length)++] = static_cast(digit + '0'); - - // Can we stop already? - // If the remainder of the division is less than the distance to the lower - // boundary we can stop. In this case we simply round down (discarding the - // remainder). - // Similarly we test if we can round up (using the upper boundary). - bool in_delta_room_minus; - bool in_delta_room_plus; - if (is_even) { - in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus); - } else { - in_delta_room_minus = Bignum::Less(*numerator, *delta_minus); - } - if (is_even) { - in_delta_room_plus = - Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; - } else { - in_delta_room_plus = - Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; - } - if (!in_delta_room_minus && !in_delta_room_plus) { - // Prepare for next iteration. - numerator->Times10(); - delta_minus->Times10(); - // We optimized delta_plus to be equal to delta_minus (if they share the - // same value). So don't multiply delta_plus if they point to the same - // object. - if (delta_minus != delta_plus) { - delta_plus->Times10(); - } - } else if (in_delta_room_minus && in_delta_room_plus) { - // Let's see if 2*numerator < denominator. - // If yes, then the next digit would be < 5 and we can round down. - int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator); - if (compare < 0) { - // Remaining digits are less than .5. -> Round down (== do nothing). - } else if (compare > 0) { - // Remaining digits are more than .5 of denominator. -> Round up. - // Note that the last digit could not be a '9' as otherwise the whole - // loop would have stopped earlier. - // We still have an assert here in case the preconditions were not - // satisfied. - DOUBLE_CONVERSION_ASSERT(buffer[(*length) - 1] != '9'); - buffer[(*length) - 1]++; - } else { - // Halfway case. - // TODO(floitsch): need a way to solve half-way cases. - // For now let's round towards even (since this is what Gay seems to - // do). - - if ((buffer[(*length) - 1] - '0') % 2 == 0) { - // Round down => Do nothing. - } else { - DOUBLE_CONVERSION_ASSERT(buffer[(*length) - 1] != '9'); - buffer[(*length) - 1]++; - } - } - return; - } else if (in_delta_room_minus) { - // Round down (== do nothing). - return; - } else { // in_delta_room_plus - // Round up. - // Note again that the last digit could not be '9' since this would have - // stopped the loop earlier. - // We still have an DOUBLE_CONVERSION_ASSERT here, in case the preconditions were not - // satisfied. - DOUBLE_CONVERSION_ASSERT(buffer[(*length) -1] != '9'); - buffer[(*length) - 1]++; - return; - } - } -} - - -// Let v = numerator / denominator < 10. -// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point) -// from left to right. Once 'count' digits have been produced we decide whether -// to round up or down. Remainders of exactly .5 round upwards. Numbers such -// as 9.999999 propagate a carry all the way, and change the -// exponent (decimal_point), when rounding upwards. -static void GenerateCountedDigits(int count, int* decimal_point, - Bignum* numerator, Bignum* denominator, - Vector buffer, int* length) { - DOUBLE_CONVERSION_ASSERT(count >= 0); - for (int i = 0; i < count - 1; ++i) { - uint16_t digit; - digit = numerator->DivideModuloIntBignum(*denominator); - DOUBLE_CONVERSION_ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. - // digit = numerator / denominator (integer division). - // numerator = numerator % denominator. - buffer[i] = static_cast(digit + '0'); - // Prepare for next iteration. - numerator->Times10(); - } - // Generate the last digit. - uint16_t digit; - digit = numerator->DivideModuloIntBignum(*denominator); - if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { - digit++; - } - DOUBLE_CONVERSION_ASSERT(digit <= 10); - buffer[count - 1] = static_cast(digit + '0'); - // Correct bad digits (in case we had a sequence of '9's). Propagate the - // carry until we hat a non-'9' or til we reach the first digit. - for (int i = count - 1; i > 0; --i) { - if (buffer[i] != '0' + 10) break; - buffer[i] = '0'; - buffer[i - 1]++; - } - if (buffer[0] == '0' + 10) { - // Propagate a carry past the top place. - buffer[0] = '1'; - (*decimal_point)++; - } - *length = count; -} - - -// Generates 'requested_digits' after the decimal point. It might omit -// trailing '0's. If the input number is too small then no digits at all are -// generated (ex.: 2 fixed digits for 0.00001). -// -// Input verifies: 1 <= (numerator + delta) / denominator < 10. -static void BignumToFixed(int requested_digits, int* decimal_point, - Bignum* numerator, Bignum* denominator, - Vector buffer, int* length) { - // Note that we have to look at more than just the requested_digits, since - // a number could be rounded up. Example: v=0.5 with requested_digits=0. - // Even though the power of v equals 0 we can't just stop here. - if (-(*decimal_point) > requested_digits) { - // The number is definitively too small. - // Ex: 0.001 with requested_digits == 1. - // Set decimal-point to -requested_digits. This is what Gay does. - // Note that it should not have any effect anyways since the string is - // empty. - *decimal_point = -requested_digits; - *length = 0; - return; - } else if (-(*decimal_point) == requested_digits) { - // We only need to verify if the number rounds down or up. - // Ex: 0.04 and 0.06 with requested_digits == 1. - DOUBLE_CONVERSION_ASSERT(*decimal_point == -requested_digits); - // Initially the fraction lies in range (1, 10]. Multiply the denominator - // by 10 so that we can compare more easily. - denominator->Times10(); - if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { - // If the fraction is >= 0.5 then we have to include the rounded - // digit. - buffer[0] = '1'; - *length = 1; - (*decimal_point)++; - } else { - // Note that we caught most of similar cases earlier. - *length = 0; - } - return; - } else { - // The requested digits correspond to the digits after the point. - // The variable 'needed_digits' includes the digits before the point. - int needed_digits = (*decimal_point) + requested_digits; - GenerateCountedDigits(needed_digits, decimal_point, - numerator, denominator, - buffer, length); - } -} - - -// Returns an estimation of k such that 10^(k-1) <= v < 10^k where -// v = f * 2^exponent and 2^52 <= f < 2^53. -// v is hence a normalized double with the given exponent. The output is an -// approximation for the exponent of the decimal approximation .digits * 10^k. -// -// The result might undershoot by 1 in which case 10^k <= v < 10^k+1. -// Note: this property holds for v's upper boundary m+ too. -// 10^k <= m+ < 10^k+1. -// (see explanation below). -// -// Examples: -// EstimatePower(0) => 16 -// EstimatePower(-52) => 0 -// -// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0. -static int EstimatePower(int exponent) { - // This function estimates log10 of v where v = f*2^e (with e == exponent). - // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)). - // Note that f is bounded by its container size. Let p = 53 (the double's - // significand size). Then 2^(p-1) <= f < 2^p. - // - // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close - // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)). - // The computed number undershoots by less than 0.631 (when we compute log3 - // and not log10). - // - // Optimization: since we only need an approximated result this computation - // can be performed on 64 bit integers. On x86/x64 architecture the speedup is - // not really measurable, though. - // - // Since we want to avoid overshooting we decrement by 1e10 so that - // floating-point imprecisions don't affect us. - // - // Explanation for v's boundary m+: the computation takes advantage of - // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement - // (even for denormals where the delta can be much more important). - - const double k1Log10 = 0.30102999566398114; // 1/lg(10) - - // For doubles len(f) == 53 (don't forget the hidden bit). - const int kSignificandSize = Double::kSignificandSize; - double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); - return static_cast(estimate); -} - - -// See comments for InitialScaledStartValues. -static void InitialScaledStartValuesPositiveExponent( - uint64_t significand, int exponent, - int estimated_power, bool need_boundary_deltas, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus) { - // A positive exponent implies a positive power. - DOUBLE_CONVERSION_ASSERT(estimated_power >= 0); - // Since the estimated_power is positive we simply multiply the denominator - // by 10^estimated_power. - - // numerator = v. - numerator->AssignUInt64(significand); - numerator->ShiftLeft(exponent); - // denominator = 10^estimated_power. - denominator->AssignPowerUInt16(10, estimated_power); - - if (need_boundary_deltas) { - // Introduce a common denominator so that the deltas to the boundaries are - // integers. - denominator->ShiftLeft(1); - numerator->ShiftLeft(1); - // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common - // denominator (of 2) delta_plus equals 2^e. - delta_plus->AssignUInt16(1); - delta_plus->ShiftLeft(exponent); - // Same for delta_minus. The adjustments if f == 2^p-1 are done later. - delta_minus->AssignUInt16(1); - delta_minus->ShiftLeft(exponent); - } -} - - -// See comments for InitialScaledStartValues -static void InitialScaledStartValuesNegativeExponentPositivePower( - uint64_t significand, int exponent, - int estimated_power, bool need_boundary_deltas, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus) { - // v = f * 2^e with e < 0, and with estimated_power >= 0. - // This means that e is close to 0 (have a look at how estimated_power is - // computed). - - // numerator = significand - // since v = significand * 2^exponent this is equivalent to - // numerator = v * / 2^-exponent - numerator->AssignUInt64(significand); - // denominator = 10^estimated_power * 2^-exponent (with exponent < 0) - denominator->AssignPowerUInt16(10, estimated_power); - denominator->ShiftLeft(-exponent); - - if (need_boundary_deltas) { - // Introduce a common denominator so that the deltas to the boundaries are - // integers. - denominator->ShiftLeft(1); - numerator->ShiftLeft(1); - // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common - // denominator (of 2) delta_plus equals 2^e. - // Given that the denominator already includes v's exponent the distance - // to the boundaries is simply 1. - delta_plus->AssignUInt16(1); - // Same for delta_minus. The adjustments if f == 2^p-1 are done later. - delta_minus->AssignUInt16(1); - } -} - - -// See comments for InitialScaledStartValues -static void InitialScaledStartValuesNegativeExponentNegativePower( - uint64_t significand, int exponent, - int estimated_power, bool need_boundary_deltas, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus) { - // Instead of multiplying the denominator with 10^estimated_power we - // multiply all values (numerator and deltas) by 10^-estimated_power. - - // Use numerator as temporary container for power_ten. - Bignum* power_ten = numerator; - power_ten->AssignPowerUInt16(10, -estimated_power); - - if (need_boundary_deltas) { - // Since power_ten == numerator we must make a copy of 10^estimated_power - // before we complete the computation of the numerator. - // delta_plus = delta_minus = 10^estimated_power - delta_plus->AssignBignum(*power_ten); - delta_minus->AssignBignum(*power_ten); - } - - // numerator = significand * 2 * 10^-estimated_power - // since v = significand * 2^exponent this is equivalent to - // numerator = v * 10^-estimated_power * 2 * 2^-exponent. - // Remember: numerator has been abused as power_ten. So no need to assign it - // to itself. - DOUBLE_CONVERSION_ASSERT(numerator == power_ten); - numerator->MultiplyByUInt64(significand); - - // denominator = 2 * 2^-exponent with exponent < 0. - denominator->AssignUInt16(1); - denominator->ShiftLeft(-exponent); - - if (need_boundary_deltas) { - // Introduce a common denominator so that the deltas to the boundaries are - // integers. - numerator->ShiftLeft(1); - denominator->ShiftLeft(1); - // With this shift the boundaries have their correct value, since - // delta_plus = 10^-estimated_power, and - // delta_minus = 10^-estimated_power. - // These assignments have been done earlier. - // The adjustments if f == 2^p-1 (lower boundary is closer) are done later. - } -} - - -// Let v = significand * 2^exponent. -// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator -// and denominator. The functions GenerateShortestDigits and -// GenerateCountedDigits will then convert this ratio to its decimal -// representation d, with the required accuracy. -// Then d * 10^estimated_power is the representation of v. -// (Note: the fraction and the estimated_power might get adjusted before -// generating the decimal representation.) -// -// The initial start values consist of: -// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power. -// - a scaled (common) denominator. -// optionally (used by GenerateShortestDigits to decide if it has the shortest -// decimal converting back to v): -// - v - m-: the distance to the lower boundary. -// - m+ - v: the distance to the upper boundary. -// -// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator. -// -// Let ep == estimated_power, then the returned values will satisfy: -// v / 10^ep = numerator / denominator. -// v's boundaries m- and m+: -// m- / 10^ep == v / 10^ep - delta_minus / denominator -// m+ / 10^ep == v / 10^ep + delta_plus / denominator -// Or in other words: -// m- == v - delta_minus * 10^ep / denominator; -// m+ == v + delta_plus * 10^ep / denominator; -// -// Since 10^(k-1) <= v < 10^k (with k == estimated_power) -// or 10^k <= v < 10^(k+1) -// we then have 0.1 <= numerator/denominator < 1 -// or 1 <= numerator/denominator < 10 -// -// It is then easy to kickstart the digit-generation routine. -// -// The boundary-deltas are only filled if the mode equals BIGNUM_DTOA_SHORTEST -// or BIGNUM_DTOA_SHORTEST_SINGLE. - -static void InitialScaledStartValues(uint64_t significand, - int exponent, - bool lower_boundary_is_closer, - int estimated_power, - bool need_boundary_deltas, - Bignum* numerator, - Bignum* denominator, - Bignum* delta_minus, - Bignum* delta_plus) { - if (exponent >= 0) { - InitialScaledStartValuesPositiveExponent( - significand, exponent, estimated_power, need_boundary_deltas, - numerator, denominator, delta_minus, delta_plus); - } else if (estimated_power >= 0) { - InitialScaledStartValuesNegativeExponentPositivePower( - significand, exponent, estimated_power, need_boundary_deltas, - numerator, denominator, delta_minus, delta_plus); - } else { - InitialScaledStartValuesNegativeExponentNegativePower( - significand, exponent, estimated_power, need_boundary_deltas, - numerator, denominator, delta_minus, delta_plus); - } - - if (need_boundary_deltas && lower_boundary_is_closer) { - // The lower boundary is closer at half the distance of "normal" numbers. - // Increase the common denominator and adapt all but the delta_minus. - denominator->ShiftLeft(1); // *2 - numerator->ShiftLeft(1); // *2 - delta_plus->ShiftLeft(1); // *2 - } -} - - -// This routine multiplies numerator/denominator so that its values lies in the -// range 1-10. That is after a call to this function we have: -// 1 <= (numerator + delta_plus) /denominator < 10. -// Let numerator the input before modification and numerator' the argument -// after modification, then the output-parameter decimal_point is such that -// numerator / denominator * 10^estimated_power == -// numerator' / denominator' * 10^(decimal_point - 1) -// In some cases estimated_power was too low, and this is already the case. We -// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k == -// estimated_power) but do not touch the numerator or denominator. -// Otherwise the routine multiplies the numerator and the deltas by 10. -static void FixupMultiply10(int estimated_power, bool is_even, - int* decimal_point, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus) { - bool in_range; - if (is_even) { - // For IEEE doubles half-way cases (in decimal system numbers ending with 5) - // are rounded to the closest floating-point number with even significand. - in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; - } else { - in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; - } - if (in_range) { - // Since numerator + delta_plus >= denominator we already have - // 1 <= numerator/denominator < 10. Simply update the estimated_power. - *decimal_point = estimated_power + 1; - } else { - *decimal_point = estimated_power; - numerator->Times10(); - if (Bignum::Equal(*delta_minus, *delta_plus)) { - delta_minus->Times10(); - delta_plus->AssignBignum(*delta_minus); - } else { - delta_minus->Times10(); - delta_plus->Times10(); - } - } -} - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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 + +// ICU PATCH: Customize header file paths for ICU. + +#include "double-conversion-bignum-dtoa.h" + +#include "double-conversion-bignum.h" +#include "double-conversion-ieee.h" + +// ICU PATCH: Wrap in ICU namespace +U_NAMESPACE_BEGIN + +namespace double_conversion { + +static int NormalizedExponent(uint64_t significand, int exponent) { + DOUBLE_CONVERSION_ASSERT(significand != 0); + while ((significand & Double::kHiddenBit) == 0) { + significand = significand << 1; + exponent = exponent - 1; + } + return exponent; +} + + +// Forward declarations: +// Returns an estimation of k such that 10^(k-1) <= v < 10^k. +static int EstimatePower(int exponent); +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. +static void InitialScaledStartValues(uint64_t significand, + int exponent, + bool lower_boundary_is_closer, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus); +// Multiplies numerator/denominator so that its values lies in the range 1-10. +// Returns decimal_point s.t. +// v = numerator'/denominator' * 10^(decimal_point-1) +// where numerator' and denominator' are the values of numerator and +// denominator after the call to this function. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus); +// Generates digits from the left to the right and stops when the generated +// digits yield the shortest decimal representation of v. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector buffer, int* length); +// Generates 'requested_digits' after the decimal point. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector buffer, int* length); +// Generates 'count' digits of numerator/denominator. +// Once 'count' digits have been produced rounds the result depending on the +// remainder (remainders of exactly .5 round upwards). Might update the +// decimal_point when rounding up (for example for 0.9999). +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector buffer, int* length); + + +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector buffer, int* length, int* decimal_point) { + DOUBLE_CONVERSION_ASSERT(v > 0); + DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial()); + uint64_t significand; + int exponent; + bool lower_boundary_is_closer; + if (mode == BIGNUM_DTOA_SHORTEST_SINGLE) { + float f = static_cast(v); + DOUBLE_CONVERSION_ASSERT(f == v); + significand = Single(f).Significand(); + exponent = Single(f).Exponent(); + lower_boundary_is_closer = Single(f).LowerBoundaryIsCloser(); + } else { + significand = Double(v).Significand(); + exponent = Double(v).Exponent(); + lower_boundary_is_closer = Double(v).LowerBoundaryIsCloser(); + } + bool need_boundary_deltas = + (mode == BIGNUM_DTOA_SHORTEST || mode == BIGNUM_DTOA_SHORTEST_SINGLE); + + bool is_even = (significand & 1) == 0; + int normalized_exponent = NormalizedExponent(significand, exponent); + // estimated_power might be too low by 1. + int estimated_power = EstimatePower(normalized_exponent); + + // Shortcut for Fixed. + // The requested digits correspond to the digits after the point. If the + // number is much too small, then there is no need in trying to get any + // digits. + if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { + buffer[0] = '\0'; + *length = 0; + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + return; + } + + Bignum numerator; + Bignum denominator; + Bignum delta_minus; + Bignum delta_plus; + // Make sure the bignum can grow large enough. The smallest double equals + // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. + // The maximum double is 1.7976931348623157e308 which needs fewer than + // 308*4 binary digits. + DOUBLE_CONVERSION_ASSERT(Bignum::kMaxSignificantBits >= 324*4); + InitialScaledStartValues(significand, exponent, lower_boundary_is_closer, + estimated_power, need_boundary_deltas, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^estimated_power. + FixupMultiply10(estimated_power, is_even, decimal_point, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^(decimal_point-1), and + // 1 <= (numerator + delta_plus) / denominator < 10 + switch (mode) { + case BIGNUM_DTOA_SHORTEST: + case BIGNUM_DTOA_SHORTEST_SINGLE: + GenerateShortestDigits(&numerator, &denominator, + &delta_minus, &delta_plus, + is_even, buffer, length); + break; + case BIGNUM_DTOA_FIXED: + BignumToFixed(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + case BIGNUM_DTOA_PRECISION: + GenerateCountedDigits(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + default: + DOUBLE_CONVERSION_UNREACHABLE(); + } + buffer[*length] = '\0'; +} + + +// The procedure starts generating digits from the left to the right and stops +// when the generated digits yield the shortest decimal representation of v. A +// decimal representation of v is a number lying closer to v than to any other +// double, so it converts to v when read. +// +// This is true if d, the decimal representation, is between m- and m+, the +// upper and lower boundaries. d must be strictly between them if !is_even. +// m- := (numerator - delta_minus) / denominator +// m+ := (numerator + delta_plus) / denominator +// +// Precondition: 0 <= (numerator+delta_plus) / denominator < 10. +// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit +// will be produced. This should be the standard precondition. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector buffer, int* length) { + // Small optimization: if delta_minus and delta_plus are the same just reuse + // one of the two bignums. + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_plus = delta_minus; + } + *length = 0; + for (;;) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + DOUBLE_CONVERSION_ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[(*length)++] = static_cast(digit + '0'); + + // Can we stop already? + // If the remainder of the division is less than the distance to the lower + // boundary we can stop. In this case we simply round down (discarding the + // remainder). + // Similarly we test if we can round up (using the upper boundary). + bool in_delta_room_minus; + bool in_delta_room_plus; + if (is_even) { + in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus); + } else { + in_delta_room_minus = Bignum::Less(*numerator, *delta_minus); + } + if (is_even) { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (!in_delta_room_minus && !in_delta_room_plus) { + // Prepare for next iteration. + numerator->Times10(); + delta_minus->Times10(); + // We optimized delta_plus to be equal to delta_minus (if they share the + // same value). So don't multiply delta_plus if they point to the same + // object. + if (delta_minus != delta_plus) { + delta_plus->Times10(); + } + } else if (in_delta_room_minus && in_delta_room_plus) { + // Let's see if 2*numerator < denominator. + // If yes, then the next digit would be < 5 and we can round down. + int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator); + if (compare < 0) { + // Remaining digits are less than .5. -> Round down (== do nothing). + } else if (compare > 0) { + // Remaining digits are more than .5 of denominator. -> Round up. + // Note that the last digit could not be a '9' as otherwise the whole + // loop would have stopped earlier. + // We still have an assert here in case the preconditions were not + // satisfied. + DOUBLE_CONVERSION_ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } else { + // Halfway case. + // TODO(floitsch): need a way to solve half-way cases. + // For now let's round towards even (since this is what Gay seems to + // do). + + if ((buffer[(*length) - 1] - '0') % 2 == 0) { + // Round down => Do nothing. + } else { + DOUBLE_CONVERSION_ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } + } + return; + } else if (in_delta_room_minus) { + // Round down (== do nothing). + return; + } else { // in_delta_room_plus + // Round up. + // Note again that the last digit could not be '9' since this would have + // stopped the loop earlier. + // We still have an DOUBLE_CONVERSION_ASSERT here, in case the preconditions were not + // satisfied. + DOUBLE_CONVERSION_ASSERT(buffer[(*length) -1] != '9'); + buffer[(*length) - 1]++; + return; + } + } +} + + +// Let v = numerator / denominator < 10. +// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point) +// from left to right. Once 'count' digits have been produced we decide whether +// to round up or down. Remainders of exactly .5 round upwards. Numbers such +// as 9.999999 propagate a carry all the way, and change the +// exponent (decimal_point), when rounding upwards. +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector buffer, int* length) { + DOUBLE_CONVERSION_ASSERT(count >= 0); + for (int i = 0; i < count - 1; ++i) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + DOUBLE_CONVERSION_ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[i] = static_cast(digit + '0'); + // Prepare for next iteration. + numerator->Times10(); + } + // Generate the last digit. + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + digit++; + } + DOUBLE_CONVERSION_ASSERT(digit <= 10); + buffer[count - 1] = static_cast(digit + '0'); + // Correct bad digits (in case we had a sequence of '9's). Propagate the + // carry until we hat a non-'9' or til we reach the first digit. + for (int i = count - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + if (buffer[0] == '0' + 10) { + // Propagate a carry past the top place. + buffer[0] = '1'; + (*decimal_point)++; + } + *length = count; +} + + +// Generates 'requested_digits' after the decimal point. It might omit +// trailing '0's. If the input number is too small then no digits at all are +// generated (ex.: 2 fixed digits for 0.00001). +// +// Input verifies: 1 <= (numerator + delta) / denominator < 10. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector buffer, int* length) { + // Note that we have to look at more than just the requested_digits, since + // a number could be rounded up. Example: v=0.5 with requested_digits=0. + // Even though the power of v equals 0 we can't just stop here. + if (-(*decimal_point) > requested_digits) { + // The number is definitively too small. + // Ex: 0.001 with requested_digits == 1. + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + *length = 0; + return; + } else if (-(*decimal_point) == requested_digits) { + // We only need to verify if the number rounds down or up. + // Ex: 0.04 and 0.06 with requested_digits == 1. + DOUBLE_CONVERSION_ASSERT(*decimal_point == -requested_digits); + // Initially the fraction lies in range (1, 10]. Multiply the denominator + // by 10 so that we can compare more easily. + denominator->Times10(); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + // If the fraction is >= 0.5 then we have to include the rounded + // digit. + buffer[0] = '1'; + *length = 1; + (*decimal_point)++; + } else { + // Note that we caught most of similar cases earlier. + *length = 0; + } + return; + } else { + // The requested digits correspond to the digits after the point. + // The variable 'needed_digits' includes the digits before the point. + int needed_digits = (*decimal_point) + requested_digits; + GenerateCountedDigits(needed_digits, decimal_point, + numerator, denominator, + buffer, length); + } +} + + +// Returns an estimation of k such that 10^(k-1) <= v < 10^k where +// v = f * 2^exponent and 2^52 <= f < 2^53. +// v is hence a normalized double with the given exponent. The output is an +// approximation for the exponent of the decimal approximation .digits * 10^k. +// +// The result might undershoot by 1 in which case 10^k <= v < 10^k+1. +// Note: this property holds for v's upper boundary m+ too. +// 10^k <= m+ < 10^k+1. +// (see explanation below). +// +// Examples: +// EstimatePower(0) => 16 +// EstimatePower(-52) => 0 +// +// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0. +static int EstimatePower(int exponent) { + // This function estimates log10 of v where v = f*2^e (with e == exponent). + // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)). + // Note that f is bounded by its container size. Let p = 53 (the double's + // significand size). Then 2^(p-1) <= f < 2^p. + // + // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close + // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)). + // The computed number undershoots by less than 0.631 (when we compute log3 + // and not log10). + // + // Optimization: since we only need an approximated result this computation + // can be performed on 64 bit integers. On x86/x64 architecture the speedup is + // not really measurable, though. + // + // Since we want to avoid overshooting we decrement by 1e10 so that + // floating-point imprecisions don't affect us. + // + // Explanation for v's boundary m+: the computation takes advantage of + // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement + // (even for denormals where the delta can be much more important). + + const double k1Log10 = 0.30102999566398114; // 1/lg(10) + + // For doubles len(f) == 53 (don't forget the hidden bit). + const int kSignificandSize = Double::kSignificandSize; + double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); + return static_cast(estimate); +} + + +// See comments for InitialScaledStartValues. +static void InitialScaledStartValuesPositiveExponent( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // A positive exponent implies a positive power. + DOUBLE_CONVERSION_ASSERT(estimated_power >= 0); + // Since the estimated_power is positive we simply multiply the denominator + // by 10^estimated_power. + + // numerator = v. + numerator->AssignUInt64(significand); + numerator->ShiftLeft(exponent); + // denominator = 10^estimated_power. + denominator->AssignPowerUInt16(10, estimated_power); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + delta_plus->AssignUInt16(1); + delta_plus->ShiftLeft(exponent); + // Same for delta_minus. The adjustments if f == 2^p-1 are done later. + delta_minus->AssignUInt16(1); + delta_minus->ShiftLeft(exponent); + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentPositivePower( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // v = f * 2^e with e < 0, and with estimated_power >= 0. + // This means that e is close to 0 (have a look at how estimated_power is + // computed). + + // numerator = significand + // since v = significand * 2^exponent this is equivalent to + // numerator = v * / 2^-exponent + numerator->AssignUInt64(significand); + // denominator = 10^estimated_power * 2^-exponent (with exponent < 0) + denominator->AssignPowerUInt16(10, estimated_power); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + // Given that the denominator already includes v's exponent the distance + // to the boundaries is simply 1. + delta_plus->AssignUInt16(1); + // Same for delta_minus. The adjustments if f == 2^p-1 are done later. + delta_minus->AssignUInt16(1); + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentNegativePower( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // Instead of multiplying the denominator with 10^estimated_power we + // multiply all values (numerator and deltas) by 10^-estimated_power. + + // Use numerator as temporary container for power_ten. + Bignum* power_ten = numerator; + power_ten->AssignPowerUInt16(10, -estimated_power); + + if (need_boundary_deltas) { + // Since power_ten == numerator we must make a copy of 10^estimated_power + // before we complete the computation of the numerator. + // delta_plus = delta_minus = 10^estimated_power + delta_plus->AssignBignum(*power_ten); + delta_minus->AssignBignum(*power_ten); + } + + // numerator = significand * 2 * 10^-estimated_power + // since v = significand * 2^exponent this is equivalent to + // numerator = v * 10^-estimated_power * 2 * 2^-exponent. + // Remember: numerator has been abused as power_ten. So no need to assign it + // to itself. + DOUBLE_CONVERSION_ASSERT(numerator == power_ten); + numerator->MultiplyByUInt64(significand); + + // denominator = 2 * 2^-exponent with exponent < 0. + denominator->AssignUInt16(1); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + numerator->ShiftLeft(1); + denominator->ShiftLeft(1); + // With this shift the boundaries have their correct value, since + // delta_plus = 10^-estimated_power, and + // delta_minus = 10^-estimated_power. + // These assignments have been done earlier. + // The adjustments if f == 2^p-1 (lower boundary is closer) are done later. + } +} + + +// Let v = significand * 2^exponent. +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. The functions GenerateShortestDigits and +// GenerateCountedDigits will then convert this ratio to its decimal +// representation d, with the required accuracy. +// Then d * 10^estimated_power is the representation of v. +// (Note: the fraction and the estimated_power might get adjusted before +// generating the decimal representation.) +// +// The initial start values consist of: +// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power. +// - a scaled (common) denominator. +// optionally (used by GenerateShortestDigits to decide if it has the shortest +// decimal converting back to v): +// - v - m-: the distance to the lower boundary. +// - m+ - v: the distance to the upper boundary. +// +// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator. +// +// Let ep == estimated_power, then the returned values will satisfy: +// v / 10^ep = numerator / denominator. +// v's boundaries m- and m+: +// m- / 10^ep == v / 10^ep - delta_minus / denominator +// m+ / 10^ep == v / 10^ep + delta_plus / denominator +// Or in other words: +// m- == v - delta_minus * 10^ep / denominator; +// m+ == v + delta_plus * 10^ep / denominator; +// +// Since 10^(k-1) <= v < 10^k (with k == estimated_power) +// or 10^k <= v < 10^(k+1) +// we then have 0.1 <= numerator/denominator < 1 +// or 1 <= numerator/denominator < 10 +// +// It is then easy to kickstart the digit-generation routine. +// +// The boundary-deltas are only filled if the mode equals BIGNUM_DTOA_SHORTEST +// or BIGNUM_DTOA_SHORTEST_SINGLE. + +static void InitialScaledStartValues(uint64_t significand, + int exponent, + bool lower_boundary_is_closer, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + if (exponent >= 0) { + InitialScaledStartValuesPositiveExponent( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else if (estimated_power >= 0) { + InitialScaledStartValuesNegativeExponentPositivePower( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else { + InitialScaledStartValuesNegativeExponentNegativePower( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } + + if (need_boundary_deltas && lower_boundary_is_closer) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the common denominator and adapt all but the delta_minus. + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } +} + + +// This routine multiplies numerator/denominator so that its values lies in the +// range 1-10. That is after a call to this function we have: +// 1 <= (numerator + delta_plus) /denominator < 10. +// Let numerator the input before modification and numerator' the argument +// after modification, then the output-parameter decimal_point is such that +// numerator / denominator * 10^estimated_power == +// numerator' / denominator' * 10^(decimal_point - 1) +// In some cases estimated_power was too low, and this is already the case. We +// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k == +// estimated_power) but do not touch the numerator or denominator. +// Otherwise the routine multiplies the numerator and the deltas by 10. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + bool in_range; + if (is_even) { + // For IEEE doubles half-way cases (in decimal system numbers ending with 5) + // are rounded to the closest floating-point number with even significand. + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (in_range) { + // Since numerator + delta_plus >= denominator we already have + // 1 <= numerator/denominator < 10. Simply update the estimated_power. + *decimal_point = estimated_power + 1; + } else { + *decimal_point = estimated_power; + numerator->Times10(); + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_minus->Times10(); + delta_plus->AssignBignum(*delta_minus); + } else { + delta_minus->Times10(); + delta_plus->Times10(); + } + } +} + +} // 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-bignum-dtoa.h b/deps/icu-small/source/i18n/double-conversion-bignum-dtoa.h index edc21b0f2e4d5e..374094690ac940 100644 --- a/deps/icu-small/source/i18n/double-conversion-bignum-dtoa.h +++ b/deps/icu-small/source/i18n/double-conversion-bignum-dtoa.h @@ -1,102 +1,102 @@ -// © 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_BIGNUM_DTOA_H_ -#define DOUBLE_CONVERSION_BIGNUM_DTOA_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 { - -enum BignumDtoaMode { - // Return the shortest correct representation. - // For example the output of 0.299999999999999988897 is (the less accurate but - // correct) 0.3. - BIGNUM_DTOA_SHORTEST, - // Same as BIGNUM_DTOA_SHORTEST but for single-precision floats. - BIGNUM_DTOA_SHORTEST_SINGLE, - // Return a fixed number of digits after the decimal point. - // For instance fixed(0.1, 4) becomes 0.1000 - // If the input number is big, the output will be big. - BIGNUM_DTOA_FIXED, - // Return a fixed number of digits, no matter what the exponent is. - BIGNUM_DTOA_PRECISION -}; - -// Converts the given double 'v' to ascii. -// The result should be interpreted as buffer * 10^(point-length). -// The buffer will be null-terminated. -// -// The input v must be > 0 and different from NaN, and Infinity. -// -// The output depends on the given mode: -// - SHORTEST: produce the least amount of digits for which the internal -// identity requirement is still satisfied. If the digits are printed -// (together with the correct exponent) then reading this number will give -// 'v' again. The buffer will choose the representation that is closest to -// 'v'. If there are two at the same distance, than the number is round up. -// In this mode the 'requested_digits' parameter is ignored. -// - FIXED: produces digits necessary to print a given number with -// 'requested_digits' digits after the decimal point. The produced digits -// might be too short in which case the caller has to fill the gaps with '0's. -// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. -// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns -// buffer="2", point=0. -// Note: the length of the returned buffer has no meaning wrt the significance -// of its digits. That is, just because it contains '0's does not mean that -// any other digit would not satisfy the internal identity requirement. -// - PRECISION: produces 'requested_digits' where the first digit is not '0'. -// Even though the length of produced digits usually equals -// 'requested_digits', the function is allowed to return fewer digits, in -// which case the caller has to fill the missing digits with '0's. -// Halfway cases are again rounded up. -// 'BignumDtoa' expects the given buffer to be big enough to hold all digits -// and a terminating null-character. -void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, - Vector buffer, int* length, int* point); - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END - -#endif // DOUBLE_CONVERSION_BIGNUM_DTOA_H_ -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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_BIGNUM_DTOA_H_ +#define DOUBLE_CONVERSION_BIGNUM_DTOA_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 { + +enum BignumDtoaMode { + // Return the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate but + // correct) 0.3. + BIGNUM_DTOA_SHORTEST, + // Same as BIGNUM_DTOA_SHORTEST but for single-precision floats. + BIGNUM_DTOA_SHORTEST_SINGLE, + // Return a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + BIGNUM_DTOA_FIXED, + // Return a fixed number of digits, no matter what the exponent is. + BIGNUM_DTOA_PRECISION +}; + +// Converts the given double 'v' to ascii. +// The result should be interpreted as buffer * 10^(point-length). +// The buffer will be null-terminated. +// +// The input v must be > 0 and different from NaN, and Infinity. +// +// The output depends on the given mode: +// - SHORTEST: produce the least amount of digits for which the internal +// identity requirement is still satisfied. If the digits are printed +// (together with the correct exponent) then reading this number will give +// 'v' again. The buffer will choose the representation that is closest to +// 'v'. If there are two at the same distance, than the number is round up. +// In this mode the 'requested_digits' parameter is ignored. +// - FIXED: produces digits necessary to print a given number with +// 'requested_digits' digits after the decimal point. The produced digits +// might be too short in which case the caller has to fill the gaps with '0's. +// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. +// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns +// buffer="2", point=0. +// Note: the length of the returned buffer has no meaning wrt the significance +// of its digits. That is, just because it contains '0's does not mean that +// any other digit would not satisfy the internal identity requirement. +// - PRECISION: produces 'requested_digits' where the first digit is not '0'. +// Even though the length of produced digits usually equals +// 'requested_digits', the function is allowed to return fewer digits, in +// which case the caller has to fill the missing digits with '0's. +// Halfway cases are again rounded up. +// 'BignumDtoa' expects the given buffer to be big enough to hold all digits +// and a terminating null-character. +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector buffer, int* length, int* point); + +} // namespace double_conversion + +// ICU PATCH: Close ICU namespace +U_NAMESPACE_END + +#endif // DOUBLE_CONVERSION_BIGNUM_DTOA_H_ +#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/double-conversion-bignum.cpp b/deps/icu-small/source/i18n/double-conversion-bignum.cpp index d2b701a21d8220..f7c01dabe7ec02 100644 --- a/deps/icu-small/source/i18n/double-conversion-bignum.cpp +++ b/deps/icu-small/source/i18n/double-conversion-bignum.cpp @@ -1,815 +1,815 @@ -// © 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. - -#include "double-conversion-bignum.h" -#include "double-conversion-utils.h" - -// ICU PATCH: Wrap in ICU namespace -U_NAMESPACE_BEGIN - -namespace double_conversion { - -Bignum::Chunk& Bignum::RawBigit(const int index) { - DOUBLE_CONVERSION_ASSERT(static_cast(index) < kBigitCapacity); - return bigits_buffer_[index]; -} - - -const Bignum::Chunk& Bignum::RawBigit(const int index) const { - DOUBLE_CONVERSION_ASSERT(static_cast(index) < kBigitCapacity); - return bigits_buffer_[index]; -} - - -template -static int BitSize(const S value) { - (void) value; // Mark variable as used. - return 8 * sizeof(value); -} - -// Guaranteed to lie in one Bigit. -void Bignum::AssignUInt16(const uint16_t value) { - DOUBLE_CONVERSION_ASSERT(kBigitSize >= BitSize(value)); - Zero(); - if (value > 0) { - RawBigit(0) = value; - used_bigits_ = 1; - } -} - - -void Bignum::AssignUInt64(uint64_t value) { - Zero(); - for(int i = 0; value > 0; ++i) { - RawBigit(i) = value & kBigitMask; - value >>= kBigitSize; - ++used_bigits_; - } -} - - -void Bignum::AssignBignum(const Bignum& other) { - exponent_ = other.exponent_; - for (int i = 0; i < other.used_bigits_; ++i) { - RawBigit(i) = other.RawBigit(i); - } - used_bigits_ = other.used_bigits_; -} - - -static uint64_t ReadUInt64(const Vector buffer, - const int from, - const int digits_to_read) { - uint64_t result = 0; - for (int i = from; i < from + digits_to_read; ++i) { - const int digit = buffer[i] - '0'; - DOUBLE_CONVERSION_ASSERT(0 <= digit && digit <= 9); - result = result * 10 + digit; - } - return result; -} - - -void Bignum::AssignDecimalString(const Vector value) { - // 2^64 = 18446744073709551616 > 10^19 - static const int kMaxUint64DecimalDigits = 19; - Zero(); - int length = value.length(); - unsigned pos = 0; - // Let's just say that each digit needs 4 bits. - while (length >= kMaxUint64DecimalDigits) { - const uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); - pos += kMaxUint64DecimalDigits; - length -= kMaxUint64DecimalDigits; - MultiplyByPowerOfTen(kMaxUint64DecimalDigits); - AddUInt64(digits); - } - const uint64_t digits = ReadUInt64(value, pos, length); - MultiplyByPowerOfTen(length); - AddUInt64(digits); - Clamp(); -} - - -static uint64_t HexCharValue(const int c) { - if ('0' <= c && c <= '9') { - return c - '0'; - } - if ('a' <= c && c <= 'f') { - return 10 + c - 'a'; - } - DOUBLE_CONVERSION_ASSERT('A' <= c && c <= 'F'); - return 10 + c - 'A'; -} - - -// Unlike AssignDecimalString(), this function is "only" used -// for unit-tests and therefore not performance critical. -void Bignum::AssignHexString(Vector value) { - Zero(); - // Required capacity could be reduced by ignoring leading zeros. - EnsureCapacity(((value.length() * 4) + kBigitSize - 1) / kBigitSize); - DOUBLE_CONVERSION_ASSERT(sizeof(uint64_t) * 8 >= kBigitSize + 4); // TODO: static_assert - // Accumulates converted hex digits until at least kBigitSize bits. - // Works with non-factor-of-four kBigitSizes. - uint64_t tmp = 0; - for (int cnt = 0; !value.is_empty(); value.pop_back()) { - tmp |= (HexCharValue(value.last()) << cnt); - if ((cnt += 4) >= kBigitSize) { - RawBigit(used_bigits_++) = (tmp & kBigitMask); - cnt -= kBigitSize; - tmp >>= kBigitSize; - } - } - if (tmp > 0) { - DOUBLE_CONVERSION_ASSERT(tmp <= kBigitMask); - RawBigit(used_bigits_++) = static_cast(tmp & kBigitMask); - } - Clamp(); -} - - -void Bignum::AddUInt64(const uint64_t operand) { - if (operand == 0) { - return; - } - Bignum other; - other.AssignUInt64(operand); - AddBignum(other); -} - - -void Bignum::AddBignum(const Bignum& other) { - DOUBLE_CONVERSION_ASSERT(IsClamped()); - DOUBLE_CONVERSION_ASSERT(other.IsClamped()); - - // If this has a greater exponent than other append zero-bigits to this. - // After this call exponent_ <= other.exponent_. - Align(other); - - // There are two possibilities: - // aaaaaaaaaaa 0000 (where the 0s represent a's exponent) - // bbbbb 00000000 - // ---------------- - // ccccccccccc 0000 - // or - // aaaaaaaaaa 0000 - // bbbbbbbbb 0000000 - // ----------------- - // cccccccccccc 0000 - // In both cases we might need a carry bigit. - - EnsureCapacity(1 + (std::max)(BigitLength(), other.BigitLength()) - exponent_); - Chunk carry = 0; - int bigit_pos = other.exponent_ - exponent_; - DOUBLE_CONVERSION_ASSERT(bigit_pos >= 0); - for (int i = used_bigits_; i < bigit_pos; ++i) { - RawBigit(i) = 0; - } - for (int i = 0; i < other.used_bigits_; ++i) { - const Chunk my = (bigit_pos < used_bigits_) ? RawBigit(bigit_pos) : 0; - const Chunk sum = my + other.RawBigit(i) + carry; - RawBigit(bigit_pos) = sum & kBigitMask; - carry = sum >> kBigitSize; - ++bigit_pos; - } - while (carry != 0) { - const Chunk my = (bigit_pos < used_bigits_) ? RawBigit(bigit_pos) : 0; - const Chunk sum = my + carry; - RawBigit(bigit_pos) = sum & kBigitMask; - carry = sum >> kBigitSize; - ++bigit_pos; - } - used_bigits_ = static_cast(std::max(bigit_pos, static_cast(used_bigits_))); - DOUBLE_CONVERSION_ASSERT(IsClamped()); -} - - -void Bignum::SubtractBignum(const Bignum& other) { - DOUBLE_CONVERSION_ASSERT(IsClamped()); - DOUBLE_CONVERSION_ASSERT(other.IsClamped()); - // We require this to be bigger than other. - DOUBLE_CONVERSION_ASSERT(LessEqual(other, *this)); - - Align(other); - - const int offset = other.exponent_ - exponent_; - Chunk borrow = 0; - int i; - for (i = 0; i < other.used_bigits_; ++i) { - DOUBLE_CONVERSION_ASSERT((borrow == 0) || (borrow == 1)); - const Chunk difference = RawBigit(i + offset) - other.RawBigit(i) - borrow; - RawBigit(i + offset) = difference & kBigitMask; - borrow = difference >> (kChunkSize - 1); - } - while (borrow != 0) { - const Chunk difference = RawBigit(i + offset) - borrow; - RawBigit(i + offset) = difference & kBigitMask; - borrow = difference >> (kChunkSize - 1); - ++i; - } - Clamp(); -} - - -void Bignum::ShiftLeft(const int shift_amount) { - if (used_bigits_ == 0) { - return; - } - exponent_ += static_cast(shift_amount / kBigitSize); - const int local_shift = shift_amount % kBigitSize; - EnsureCapacity(used_bigits_ + 1); - BigitsShiftLeft(local_shift); -} - - -void Bignum::MultiplyByUInt32(const uint32_t factor) { - if (factor == 1) { - return; - } - if (factor == 0) { - Zero(); - return; - } - if (used_bigits_ == 0) { - return; - } - // The product of a bigit with the factor is of size kBigitSize + 32. - // Assert that this number + 1 (for the carry) fits into double chunk. - DOUBLE_CONVERSION_ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); - DoubleChunk carry = 0; - for (int i = 0; i < used_bigits_; ++i) { - const DoubleChunk product = static_cast(factor) * RawBigit(i) + carry; - RawBigit(i) = static_cast(product & kBigitMask); - carry = (product >> kBigitSize); - } - while (carry != 0) { - EnsureCapacity(used_bigits_ + 1); - RawBigit(used_bigits_) = carry & kBigitMask; - used_bigits_++; - carry >>= kBigitSize; - } -} - - -void Bignum::MultiplyByUInt64(const uint64_t factor) { - if (factor == 1) { - return; - } - if (factor == 0) { - Zero(); - return; - } - if (used_bigits_ == 0) { - return; - } - DOUBLE_CONVERSION_ASSERT(kBigitSize < 32); - uint64_t carry = 0; - const uint64_t low = factor & 0xFFFFFFFF; - const uint64_t high = factor >> 32; - for (int i = 0; i < used_bigits_; ++i) { - const uint64_t product_low = low * RawBigit(i); - const uint64_t product_high = high * RawBigit(i); - const uint64_t tmp = (carry & kBigitMask) + product_low; - RawBigit(i) = tmp & kBigitMask; - carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + - (product_high << (32 - kBigitSize)); - } - while (carry != 0) { - EnsureCapacity(used_bigits_ + 1); - RawBigit(used_bigits_) = carry & kBigitMask; - used_bigits_++; - carry >>= kBigitSize; - } -} - - -void Bignum::MultiplyByPowerOfTen(const int exponent) { - static const uint64_t kFive27 = DOUBLE_CONVERSION_UINT64_2PART_C(0x6765c793, fa10079d); - static const uint16_t kFive1 = 5; - static const uint16_t kFive2 = kFive1 * 5; - static const uint16_t kFive3 = kFive2 * 5; - static const uint16_t kFive4 = kFive3 * 5; - static const uint16_t kFive5 = kFive4 * 5; - static const uint16_t kFive6 = kFive5 * 5; - static const uint32_t kFive7 = kFive6 * 5; - static const uint32_t kFive8 = kFive7 * 5; - static const uint32_t kFive9 = kFive8 * 5; - static const uint32_t kFive10 = kFive9 * 5; - static const uint32_t kFive11 = kFive10 * 5; - static const uint32_t kFive12 = kFive11 * 5; - static const uint32_t kFive13 = kFive12 * 5; - static const uint32_t kFive1_to_12[] = - { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6, - kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 }; - - DOUBLE_CONVERSION_ASSERT(exponent >= 0); - - if (exponent == 0) { - return; - } - if (used_bigits_ == 0) { - return; - } - // We shift by exponent at the end just before returning. - int remaining_exponent = exponent; - while (remaining_exponent >= 27) { - MultiplyByUInt64(kFive27); - remaining_exponent -= 27; - } - while (remaining_exponent >= 13) { - MultiplyByUInt32(kFive13); - remaining_exponent -= 13; - } - if (remaining_exponent > 0) { - MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]); - } - ShiftLeft(exponent); -} - - -void Bignum::Square() { - DOUBLE_CONVERSION_ASSERT(IsClamped()); - const int product_length = 2 * used_bigits_; - EnsureCapacity(product_length); - - // Comba multiplication: compute each column separately. - // Example: r = a2a1a0 * b2b1b0. - // r = 1 * a0b0 + - // 10 * (a1b0 + a0b1) + - // 100 * (a2b0 + a1b1 + a0b2) + - // 1000 * (a2b1 + a1b2) + - // 10000 * a2b2 - // - // In the worst case we have to accumulate nb-digits products of digit*digit. - // - // Assert that the additional number of bits in a DoubleChunk are enough to - // sum up used_digits of Bigit*Bigit. - if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_bigits_) { - DOUBLE_CONVERSION_UNIMPLEMENTED(); - } - DoubleChunk accumulator = 0; - // First shift the digits so we don't overwrite them. - const int copy_offset = used_bigits_; - for (int i = 0; i < used_bigits_; ++i) { - RawBigit(copy_offset + i) = RawBigit(i); - } - // We have two loops to avoid some 'if's in the loop. - for (int i = 0; i < used_bigits_; ++i) { - // Process temporary digit i with power i. - // The sum of the two indices must be equal to i. - int bigit_index1 = i; - int bigit_index2 = 0; - // Sum all of the sub-products. - while (bigit_index1 >= 0) { - const Chunk chunk1 = RawBigit(copy_offset + bigit_index1); - const Chunk chunk2 = RawBigit(copy_offset + bigit_index2); - accumulator += static_cast(chunk1) * chunk2; - bigit_index1--; - bigit_index2++; - } - RawBigit(i) = static_cast(accumulator) & kBigitMask; - accumulator >>= kBigitSize; - } - for (int i = used_bigits_; i < product_length; ++i) { - int bigit_index1 = used_bigits_ - 1; - int bigit_index2 = i - bigit_index1; - // Invariant: sum of both indices is again equal to i. - // Inner loop runs 0 times on last iteration, emptying accumulator. - while (bigit_index2 < used_bigits_) { - const Chunk chunk1 = RawBigit(copy_offset + bigit_index1); - const Chunk chunk2 = RawBigit(copy_offset + bigit_index2); - accumulator += static_cast(chunk1) * chunk2; - bigit_index1--; - bigit_index2++; - } - // The overwritten RawBigit(i) will never be read in further loop iterations, - // because bigit_index1 and bigit_index2 are always greater - // than i - used_bigits_. - RawBigit(i) = static_cast(accumulator) & kBigitMask; - accumulator >>= kBigitSize; - } - // Since the result was guaranteed to lie inside the number the - // accumulator must be 0 now. - DOUBLE_CONVERSION_ASSERT(accumulator == 0); - - // Don't forget to update the used_digits and the exponent. - used_bigits_ = static_cast(product_length); - exponent_ *= 2; - Clamp(); -} - - -void Bignum::AssignPowerUInt16(uint16_t base, const int power_exponent) { - DOUBLE_CONVERSION_ASSERT(base != 0); - DOUBLE_CONVERSION_ASSERT(power_exponent >= 0); - if (power_exponent == 0) { - AssignUInt16(1); - return; - } - Zero(); - int shifts = 0; - // We expect base to be in range 2-32, and most often to be 10. - // It does not make much sense to implement different algorithms for counting - // the bits. - while ((base & 1) == 0) { - base >>= 1; - shifts++; - } - int bit_size = 0; - int tmp_base = base; - while (tmp_base != 0) { - tmp_base >>= 1; - bit_size++; - } - const int final_size = bit_size * power_exponent; - // 1 extra bigit for the shifting, and one for rounded final_size. - EnsureCapacity(final_size / kBigitSize + 2); - - // Left to Right exponentiation. - int mask = 1; - while (power_exponent >= mask) mask <<= 1; - - // The mask is now pointing to the bit above the most significant 1-bit of - // power_exponent. - // Get rid of first 1-bit; - mask >>= 2; - uint64_t this_value = base; - - bool delayed_multiplication = false; - const uint64_t max_32bits = 0xFFFFFFFF; - while (mask != 0 && this_value <= max_32bits) { - this_value = this_value * this_value; - // Verify that there is enough space in this_value to perform the - // multiplication. The first bit_size bits must be 0. - if ((power_exponent & mask) != 0) { - DOUBLE_CONVERSION_ASSERT(bit_size > 0); - const uint64_t base_bits_mask = - ~((static_cast(1) << (64 - bit_size)) - 1); - const bool high_bits_zero = (this_value & base_bits_mask) == 0; - if (high_bits_zero) { - this_value *= base; - } else { - delayed_multiplication = true; - } - } - mask >>= 1; - } - AssignUInt64(this_value); - if (delayed_multiplication) { - MultiplyByUInt32(base); - } - - // Now do the same thing as a bignum. - while (mask != 0) { - Square(); - if ((power_exponent & mask) != 0) { - MultiplyByUInt32(base); - } - mask >>= 1; - } - - // And finally add the saved shifts. - ShiftLeft(shifts * power_exponent); -} - - -// Precondition: this/other < 16bit. -uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { - DOUBLE_CONVERSION_ASSERT(IsClamped()); - DOUBLE_CONVERSION_ASSERT(other.IsClamped()); - DOUBLE_CONVERSION_ASSERT(other.used_bigits_ > 0); - - // Easy case: if we have less digits than the divisor than the result is 0. - // Note: this handles the case where this == 0, too. - if (BigitLength() < other.BigitLength()) { - return 0; - } - - Align(other); - - uint16_t result = 0; - - // Start by removing multiples of 'other' until both numbers have the same - // number of digits. - while (BigitLength() > other.BigitLength()) { - // This naive approach is extremely inefficient if `this` divided by other - // is big. This function is implemented for doubleToString where - // the result should be small (less than 10). - DOUBLE_CONVERSION_ASSERT(other.RawBigit(other.used_bigits_ - 1) >= ((1 << kBigitSize) / 16)); - DOUBLE_CONVERSION_ASSERT(RawBigit(used_bigits_ - 1) < 0x10000); - // Remove the multiples of the first digit. - // Example this = 23 and other equals 9. -> Remove 2 multiples. - result += static_cast(RawBigit(used_bigits_ - 1)); - SubtractTimes(other, RawBigit(used_bigits_ - 1)); - } - - DOUBLE_CONVERSION_ASSERT(BigitLength() == other.BigitLength()); - - // Both bignums are at the same length now. - // Since other has more than 0 digits we know that the access to - // RawBigit(used_bigits_ - 1) is safe. - const Chunk this_bigit = RawBigit(used_bigits_ - 1); - const Chunk other_bigit = other.RawBigit(other.used_bigits_ - 1); - - if (other.used_bigits_ == 1) { - // Shortcut for easy (and common) case. - int quotient = this_bigit / other_bigit; - RawBigit(used_bigits_ - 1) = this_bigit - other_bigit * quotient; - DOUBLE_CONVERSION_ASSERT(quotient < 0x10000); - result += static_cast(quotient); - Clamp(); - return result; - } - - const int division_estimate = this_bigit / (other_bigit + 1); - DOUBLE_CONVERSION_ASSERT(division_estimate < 0x10000); - result += static_cast(division_estimate); - SubtractTimes(other, division_estimate); - - if (other_bigit * (division_estimate + 1) > this_bigit) { - // No need to even try to subtract. Even if other's remaining digits were 0 - // another subtraction would be too much. - return result; - } - - while (LessEqual(other, *this)) { - SubtractBignum(other); - result++; - } - return result; -} - - -template -static int SizeInHexChars(S number) { - DOUBLE_CONVERSION_ASSERT(number > 0); - int result = 0; - while (number != 0) { - number >>= 4; - result++; - } - return result; -} - - -static char HexCharOfValue(const int value) { - DOUBLE_CONVERSION_ASSERT(0 <= value && value <= 16); - if (value < 10) { - return static_cast(value + '0'); - } - return static_cast(value - 10 + 'A'); -} - - -bool Bignum::ToHexString(char* buffer, const int buffer_size) const { - DOUBLE_CONVERSION_ASSERT(IsClamped()); - // Each bigit must be printable as separate hex-character. - DOUBLE_CONVERSION_ASSERT(kBigitSize % 4 == 0); - static const int kHexCharsPerBigit = kBigitSize / 4; - - if (used_bigits_ == 0) { - if (buffer_size < 2) { - return false; - } - buffer[0] = '0'; - buffer[1] = '\0'; - return true; - } - // We add 1 for the terminating '\0' character. - const int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + - SizeInHexChars(RawBigit(used_bigits_ - 1)) + 1; - if (needed_chars > buffer_size) { - return false; - } - int string_index = needed_chars - 1; - buffer[string_index--] = '\0'; - for (int i = 0; i < exponent_; ++i) { - for (int j = 0; j < kHexCharsPerBigit; ++j) { - buffer[string_index--] = '0'; - } - } - for (int i = 0; i < used_bigits_ - 1; ++i) { - Chunk current_bigit = RawBigit(i); - for (int j = 0; j < kHexCharsPerBigit; ++j) { - buffer[string_index--] = HexCharOfValue(current_bigit & 0xF); - current_bigit >>= 4; - } - } - // And finally the last bigit. - Chunk most_significant_bigit = RawBigit(used_bigits_ - 1); - while (most_significant_bigit != 0) { - buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF); - most_significant_bigit >>= 4; - } - return true; -} - - -Bignum::Chunk Bignum::BigitOrZero(const int index) const { - if (index >= BigitLength()) { - return 0; - } - if (index < exponent_) { - return 0; - } - return RawBigit(index - exponent_); -} - - -int Bignum::Compare(const Bignum& a, const Bignum& b) { - DOUBLE_CONVERSION_ASSERT(a.IsClamped()); - DOUBLE_CONVERSION_ASSERT(b.IsClamped()); - const int bigit_length_a = a.BigitLength(); - const int bigit_length_b = b.BigitLength(); - if (bigit_length_a < bigit_length_b) { - return -1; - } - if (bigit_length_a > bigit_length_b) { - return +1; - } - for (int i = bigit_length_a - 1; i >= (std::min)(a.exponent_, b.exponent_); --i) { - const Chunk bigit_a = a.BigitOrZero(i); - const Chunk bigit_b = b.BigitOrZero(i); - if (bigit_a < bigit_b) { - return -1; - } - if (bigit_a > bigit_b) { - return +1; - } - // Otherwise they are equal up to this digit. Try the next digit. - } - return 0; -} - - -int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { - DOUBLE_CONVERSION_ASSERT(a.IsClamped()); - DOUBLE_CONVERSION_ASSERT(b.IsClamped()); - DOUBLE_CONVERSION_ASSERT(c.IsClamped()); - if (a.BigitLength() < b.BigitLength()) { - return PlusCompare(b, a, c); - } - if (a.BigitLength() + 1 < c.BigitLength()) { - return -1; - } - if (a.BigitLength() > c.BigitLength()) { - return +1; - } - // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than - // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one - // of 'a'. - if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) { - return -1; - } - - Chunk borrow = 0; - // Starting at min_exponent all digits are == 0. So no need to compare them. - const int min_exponent = (std::min)((std::min)(a.exponent_, b.exponent_), c.exponent_); - for (int i = c.BigitLength() - 1; i >= min_exponent; --i) { - const Chunk chunk_a = a.BigitOrZero(i); - const Chunk chunk_b = b.BigitOrZero(i); - const Chunk chunk_c = c.BigitOrZero(i); - const Chunk sum = chunk_a + chunk_b; - if (sum > chunk_c + borrow) { - return +1; - } else { - borrow = chunk_c + borrow - sum; - if (borrow > 1) { - return -1; - } - borrow <<= kBigitSize; - } - } - if (borrow == 0) { - return 0; - } - return -1; -} - - -void Bignum::Clamp() { - while (used_bigits_ > 0 && RawBigit(used_bigits_ - 1) == 0) { - used_bigits_--; - } - if (used_bigits_ == 0) { - // Zero. - exponent_ = 0; - } -} - - -void Bignum::Align(const Bignum& other) { - if (exponent_ > other.exponent_) { - // If "X" represents a "hidden" bigit (by the exponent) then we are in the - // following case (a == this, b == other): - // a: aaaaaaXXXX or a: aaaaaXXX - // b: bbbbbbX b: bbbbbbbbXX - // We replace some of the hidden digits (X) of a with 0 digits. - // a: aaaaaa000X or a: aaaaa0XX - const int zero_bigits = exponent_ - other.exponent_; - EnsureCapacity(used_bigits_ + zero_bigits); - for (int i = used_bigits_ - 1; i >= 0; --i) { - RawBigit(i + zero_bigits) = RawBigit(i); - } - for (int i = 0; i < zero_bigits; ++i) { - RawBigit(i) = 0; - } - used_bigits_ += static_cast(zero_bigits); - exponent_ -= static_cast(zero_bigits); - - DOUBLE_CONVERSION_ASSERT(used_bigits_ >= 0); - DOUBLE_CONVERSION_ASSERT(exponent_ >= 0); - } -} - - -void Bignum::BigitsShiftLeft(const int shift_amount) { - DOUBLE_CONVERSION_ASSERT(shift_amount < kBigitSize); - DOUBLE_CONVERSION_ASSERT(shift_amount >= 0); - Chunk carry = 0; - for (int i = 0; i < used_bigits_; ++i) { - const Chunk new_carry = RawBigit(i) >> (kBigitSize - shift_amount); - RawBigit(i) = ((RawBigit(i) << shift_amount) + carry) & kBigitMask; - carry = new_carry; - } - if (carry != 0) { - RawBigit(used_bigits_) = carry; - used_bigits_++; - } -} - - -void Bignum::SubtractTimes(const Bignum& other, const int factor) { - DOUBLE_CONVERSION_ASSERT(exponent_ <= other.exponent_); - if (factor < 3) { - for (int i = 0; i < factor; ++i) { - SubtractBignum(other); - } - return; - } - Chunk borrow = 0; - const int exponent_diff = other.exponent_ - exponent_; - for (int i = 0; i < other.used_bigits_; ++i) { - const DoubleChunk product = static_cast(factor) * other.RawBigit(i); - const DoubleChunk remove = borrow + product; - const Chunk difference = RawBigit(i + exponent_diff) - (remove & kBigitMask); - RawBigit(i + exponent_diff) = difference & kBigitMask; - borrow = static_cast((difference >> (kChunkSize - 1)) + - (remove >> kBigitSize)); - } - for (int i = other.used_bigits_ + exponent_diff; i < used_bigits_; ++i) { - if (borrow == 0) { - return; - } - const Chunk difference = RawBigit(i) - borrow; - RawBigit(i) = difference & kBigitMask; - borrow = difference >> (kChunkSize - 1); - } - Clamp(); -} - - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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. + +#include "double-conversion-bignum.h" +#include "double-conversion-utils.h" + +// ICU PATCH: Wrap in ICU namespace +U_NAMESPACE_BEGIN + +namespace double_conversion { + +Bignum::Chunk& Bignum::RawBigit(const int index) { + DOUBLE_CONVERSION_ASSERT(static_cast(index) < kBigitCapacity); + return bigits_buffer_[index]; +} + + +const Bignum::Chunk& Bignum::RawBigit(const int index) const { + DOUBLE_CONVERSION_ASSERT(static_cast(index) < kBigitCapacity); + return bigits_buffer_[index]; +} + + +template +static int BitSize(const S value) { + (void) value; // Mark variable as used. + return 8 * sizeof(value); +} + +// Guaranteed to lie in one Bigit. +void Bignum::AssignUInt16(const uint16_t value) { + DOUBLE_CONVERSION_ASSERT(kBigitSize >= BitSize(value)); + Zero(); + if (value > 0) { + RawBigit(0) = value; + used_bigits_ = 1; + } +} + + +void Bignum::AssignUInt64(uint64_t value) { + Zero(); + for(int i = 0; value > 0; ++i) { + RawBigit(i) = value & kBigitMask; + value >>= kBigitSize; + ++used_bigits_; + } +} + + +void Bignum::AssignBignum(const Bignum& other) { + exponent_ = other.exponent_; + for (int i = 0; i < other.used_bigits_; ++i) { + RawBigit(i) = other.RawBigit(i); + } + used_bigits_ = other.used_bigits_; +} + + +static uint64_t ReadUInt64(const Vector buffer, + const int from, + const int digits_to_read) { + uint64_t result = 0; + for (int i = from; i < from + digits_to_read; ++i) { + const int digit = buffer[i] - '0'; + DOUBLE_CONVERSION_ASSERT(0 <= digit && digit <= 9); + result = result * 10 + digit; + } + return result; +} + + +void Bignum::AssignDecimalString(const Vector value) { + // 2^64 = 18446744073709551616 > 10^19 + static const int kMaxUint64DecimalDigits = 19; + Zero(); + int length = value.length(); + unsigned pos = 0; + // Let's just say that each digit needs 4 bits. + while (length >= kMaxUint64DecimalDigits) { + const uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); + pos += kMaxUint64DecimalDigits; + length -= kMaxUint64DecimalDigits; + MultiplyByPowerOfTen(kMaxUint64DecimalDigits); + AddUInt64(digits); + } + const uint64_t digits = ReadUInt64(value, pos, length); + MultiplyByPowerOfTen(length); + AddUInt64(digits); + Clamp(); +} + + +static uint64_t HexCharValue(const int c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } + if ('a' <= c && c <= 'f') { + return 10 + c - 'a'; + } + DOUBLE_CONVERSION_ASSERT('A' <= c && c <= 'F'); + return 10 + c - 'A'; +} + + +// Unlike AssignDecimalString(), this function is "only" used +// for unit-tests and therefore not performance critical. +void Bignum::AssignHexString(Vector value) { + Zero(); + // Required capacity could be reduced by ignoring leading zeros. + EnsureCapacity(((value.length() * 4) + kBigitSize - 1) / kBigitSize); + DOUBLE_CONVERSION_ASSERT(sizeof(uint64_t) * 8 >= kBigitSize + 4); // TODO: static_assert + // Accumulates converted hex digits until at least kBigitSize bits. + // Works with non-factor-of-four kBigitSizes. + uint64_t tmp = 0; + for (int cnt = 0; !value.is_empty(); value.pop_back()) { + tmp |= (HexCharValue(value.last()) << cnt); + if ((cnt += 4) >= kBigitSize) { + RawBigit(used_bigits_++) = (tmp & kBigitMask); + cnt -= kBigitSize; + tmp >>= kBigitSize; + } + } + if (tmp > 0) { + DOUBLE_CONVERSION_ASSERT(tmp <= kBigitMask); + RawBigit(used_bigits_++) = static_cast(tmp & kBigitMask); + } + Clamp(); +} + + +void Bignum::AddUInt64(const uint64_t operand) { + if (operand == 0) { + return; + } + Bignum other; + other.AssignUInt64(operand); + AddBignum(other); +} + + +void Bignum::AddBignum(const Bignum& other) { + DOUBLE_CONVERSION_ASSERT(IsClamped()); + DOUBLE_CONVERSION_ASSERT(other.IsClamped()); + + // If this has a greater exponent than other append zero-bigits to this. + // After this call exponent_ <= other.exponent_. + Align(other); + + // There are two possibilities: + // aaaaaaaaaaa 0000 (where the 0s represent a's exponent) + // bbbbb 00000000 + // ---------------- + // ccccccccccc 0000 + // or + // aaaaaaaaaa 0000 + // bbbbbbbbb 0000000 + // ----------------- + // cccccccccccc 0000 + // In both cases we might need a carry bigit. + + EnsureCapacity(1 + (std::max)(BigitLength(), other.BigitLength()) - exponent_); + Chunk carry = 0; + int bigit_pos = other.exponent_ - exponent_; + DOUBLE_CONVERSION_ASSERT(bigit_pos >= 0); + for (int i = used_bigits_; i < bigit_pos; ++i) { + RawBigit(i) = 0; + } + for (int i = 0; i < other.used_bigits_; ++i) { + const Chunk my = (bigit_pos < used_bigits_) ? RawBigit(bigit_pos) : 0; + const Chunk sum = my + other.RawBigit(i) + carry; + RawBigit(bigit_pos) = sum & kBigitMask; + carry = sum >> kBigitSize; + ++bigit_pos; + } + while (carry != 0) { + const Chunk my = (bigit_pos < used_bigits_) ? RawBigit(bigit_pos) : 0; + const Chunk sum = my + carry; + RawBigit(bigit_pos) = sum & kBigitMask; + carry = sum >> kBigitSize; + ++bigit_pos; + } + used_bigits_ = static_cast(std::max(bigit_pos, static_cast(used_bigits_))); + DOUBLE_CONVERSION_ASSERT(IsClamped()); +} + + +void Bignum::SubtractBignum(const Bignum& other) { + DOUBLE_CONVERSION_ASSERT(IsClamped()); + DOUBLE_CONVERSION_ASSERT(other.IsClamped()); + // We require this to be bigger than other. + DOUBLE_CONVERSION_ASSERT(LessEqual(other, *this)); + + Align(other); + + const int offset = other.exponent_ - exponent_; + Chunk borrow = 0; + int i; + for (i = 0; i < other.used_bigits_; ++i) { + DOUBLE_CONVERSION_ASSERT((borrow == 0) || (borrow == 1)); + const Chunk difference = RawBigit(i + offset) - other.RawBigit(i) - borrow; + RawBigit(i + offset) = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + } + while (borrow != 0) { + const Chunk difference = RawBigit(i + offset) - borrow; + RawBigit(i + offset) = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); +} + + +void Bignum::ShiftLeft(const int shift_amount) { + if (used_bigits_ == 0) { + return; + } + exponent_ += static_cast(shift_amount / kBigitSize); + const int local_shift = shift_amount % kBigitSize; + EnsureCapacity(used_bigits_ + 1); + BigitsShiftLeft(local_shift); +} + + +void Bignum::MultiplyByUInt32(const uint32_t factor) { + if (factor == 1) { + return; + } + if (factor == 0) { + Zero(); + return; + } + if (used_bigits_ == 0) { + return; + } + // The product of a bigit with the factor is of size kBigitSize + 32. + // Assert that this number + 1 (for the carry) fits into double chunk. + DOUBLE_CONVERSION_ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); + DoubleChunk carry = 0; + for (int i = 0; i < used_bigits_; ++i) { + const DoubleChunk product = static_cast(factor) * RawBigit(i) + carry; + RawBigit(i) = static_cast(product & kBigitMask); + carry = (product >> kBigitSize); + } + while (carry != 0) { + EnsureCapacity(used_bigits_ + 1); + RawBigit(used_bigits_) = carry & kBigitMask; + used_bigits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByUInt64(const uint64_t factor) { + if (factor == 1) { + return; + } + if (factor == 0) { + Zero(); + return; + } + if (used_bigits_ == 0) { + return; + } + DOUBLE_CONVERSION_ASSERT(kBigitSize < 32); + uint64_t carry = 0; + const uint64_t low = factor & 0xFFFFFFFF; + const uint64_t high = factor >> 32; + for (int i = 0; i < used_bigits_; ++i) { + const uint64_t product_low = low * RawBigit(i); + const uint64_t product_high = high * RawBigit(i); + const uint64_t tmp = (carry & kBigitMask) + product_low; + RawBigit(i) = tmp & kBigitMask; + carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + + (product_high << (32 - kBigitSize)); + } + while (carry != 0) { + EnsureCapacity(used_bigits_ + 1); + RawBigit(used_bigits_) = carry & kBigitMask; + used_bigits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByPowerOfTen(const int exponent) { + static const uint64_t kFive27 = DOUBLE_CONVERSION_UINT64_2PART_C(0x6765c793, fa10079d); + static const uint16_t kFive1 = 5; + static const uint16_t kFive2 = kFive1 * 5; + static const uint16_t kFive3 = kFive2 * 5; + static const uint16_t kFive4 = kFive3 * 5; + static const uint16_t kFive5 = kFive4 * 5; + static const uint16_t kFive6 = kFive5 * 5; + static const uint32_t kFive7 = kFive6 * 5; + static const uint32_t kFive8 = kFive7 * 5; + static const uint32_t kFive9 = kFive8 * 5; + static const uint32_t kFive10 = kFive9 * 5; + static const uint32_t kFive11 = kFive10 * 5; + static const uint32_t kFive12 = kFive11 * 5; + static const uint32_t kFive13 = kFive12 * 5; + static const uint32_t kFive1_to_12[] = + { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6, + kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 }; + + DOUBLE_CONVERSION_ASSERT(exponent >= 0); + + if (exponent == 0) { + return; + } + if (used_bigits_ == 0) { + return; + } + // We shift by exponent at the end just before returning. + int remaining_exponent = exponent; + while (remaining_exponent >= 27) { + MultiplyByUInt64(kFive27); + remaining_exponent -= 27; + } + while (remaining_exponent >= 13) { + MultiplyByUInt32(kFive13); + remaining_exponent -= 13; + } + if (remaining_exponent > 0) { + MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]); + } + ShiftLeft(exponent); +} + + +void Bignum::Square() { + DOUBLE_CONVERSION_ASSERT(IsClamped()); + const int product_length = 2 * used_bigits_; + EnsureCapacity(product_length); + + // Comba multiplication: compute each column separately. + // Example: r = a2a1a0 * b2b1b0. + // r = 1 * a0b0 + + // 10 * (a1b0 + a0b1) + + // 100 * (a2b0 + a1b1 + a0b2) + + // 1000 * (a2b1 + a1b2) + + // 10000 * a2b2 + // + // In the worst case we have to accumulate nb-digits products of digit*digit. + // + // Assert that the additional number of bits in a DoubleChunk are enough to + // sum up used_digits of Bigit*Bigit. + if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_bigits_) { + DOUBLE_CONVERSION_UNIMPLEMENTED(); + } + DoubleChunk accumulator = 0; + // First shift the digits so we don't overwrite them. + const int copy_offset = used_bigits_; + for (int i = 0; i < used_bigits_; ++i) { + RawBigit(copy_offset + i) = RawBigit(i); + } + // We have two loops to avoid some 'if's in the loop. + for (int i = 0; i < used_bigits_; ++i) { + // Process temporary digit i with power i. + // The sum of the two indices must be equal to i. + int bigit_index1 = i; + int bigit_index2 = 0; + // Sum all of the sub-products. + while (bigit_index1 >= 0) { + const Chunk chunk1 = RawBigit(copy_offset + bigit_index1); + const Chunk chunk2 = RawBigit(copy_offset + bigit_index2); + accumulator += static_cast(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + RawBigit(i) = static_cast(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + for (int i = used_bigits_; i < product_length; ++i) { + int bigit_index1 = used_bigits_ - 1; + int bigit_index2 = i - bigit_index1; + // Invariant: sum of both indices is again equal to i. + // Inner loop runs 0 times on last iteration, emptying accumulator. + while (bigit_index2 < used_bigits_) { + const Chunk chunk1 = RawBigit(copy_offset + bigit_index1); + const Chunk chunk2 = RawBigit(copy_offset + bigit_index2); + accumulator += static_cast(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + // The overwritten RawBigit(i) will never be read in further loop iterations, + // because bigit_index1 and bigit_index2 are always greater + // than i - used_bigits_. + RawBigit(i) = static_cast(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + // Since the result was guaranteed to lie inside the number the + // accumulator must be 0 now. + DOUBLE_CONVERSION_ASSERT(accumulator == 0); + + // Don't forget to update the used_digits and the exponent. + used_bigits_ = static_cast(product_length); + exponent_ *= 2; + Clamp(); +} + + +void Bignum::AssignPowerUInt16(uint16_t base, const int power_exponent) { + DOUBLE_CONVERSION_ASSERT(base != 0); + DOUBLE_CONVERSION_ASSERT(power_exponent >= 0); + if (power_exponent == 0) { + AssignUInt16(1); + return; + } + Zero(); + int shifts = 0; + // We expect base to be in range 2-32, and most often to be 10. + // It does not make much sense to implement different algorithms for counting + // the bits. + while ((base & 1) == 0) { + base >>= 1; + shifts++; + } + int bit_size = 0; + int tmp_base = base; + while (tmp_base != 0) { + tmp_base >>= 1; + bit_size++; + } + const int final_size = bit_size * power_exponent; + // 1 extra bigit for the shifting, and one for rounded final_size. + EnsureCapacity(final_size / kBigitSize + 2); + + // Left to Right exponentiation. + int mask = 1; + while (power_exponent >= mask) mask <<= 1; + + // The mask is now pointing to the bit above the most significant 1-bit of + // power_exponent. + // Get rid of first 1-bit; + mask >>= 2; + uint64_t this_value = base; + + bool delayed_multiplication = false; + const uint64_t max_32bits = 0xFFFFFFFF; + while (mask != 0 && this_value <= max_32bits) { + this_value = this_value * this_value; + // Verify that there is enough space in this_value to perform the + // multiplication. The first bit_size bits must be 0. + if ((power_exponent & mask) != 0) { + DOUBLE_CONVERSION_ASSERT(bit_size > 0); + const uint64_t base_bits_mask = + ~((static_cast(1) << (64 - bit_size)) - 1); + const bool high_bits_zero = (this_value & base_bits_mask) == 0; + if (high_bits_zero) { + this_value *= base; + } else { + delayed_multiplication = true; + } + } + mask >>= 1; + } + AssignUInt64(this_value); + if (delayed_multiplication) { + MultiplyByUInt32(base); + } + + // Now do the same thing as a bignum. + while (mask != 0) { + Square(); + if ((power_exponent & mask) != 0) { + MultiplyByUInt32(base); + } + mask >>= 1; + } + + // And finally add the saved shifts. + ShiftLeft(shifts * power_exponent); +} + + +// Precondition: this/other < 16bit. +uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { + DOUBLE_CONVERSION_ASSERT(IsClamped()); + DOUBLE_CONVERSION_ASSERT(other.IsClamped()); + DOUBLE_CONVERSION_ASSERT(other.used_bigits_ > 0); + + // Easy case: if we have less digits than the divisor than the result is 0. + // Note: this handles the case where this == 0, too. + if (BigitLength() < other.BigitLength()) { + return 0; + } + + Align(other); + + uint16_t result = 0; + + // Start by removing multiples of 'other' until both numbers have the same + // number of digits. + while (BigitLength() > other.BigitLength()) { + // This naive approach is extremely inefficient if `this` divided by other + // is big. This function is implemented for doubleToString where + // the result should be small (less than 10). + DOUBLE_CONVERSION_ASSERT(other.RawBigit(other.used_bigits_ - 1) >= ((1 << kBigitSize) / 16)); + DOUBLE_CONVERSION_ASSERT(RawBigit(used_bigits_ - 1) < 0x10000); + // Remove the multiples of the first digit. + // Example this = 23 and other equals 9. -> Remove 2 multiples. + result += static_cast(RawBigit(used_bigits_ - 1)); + SubtractTimes(other, RawBigit(used_bigits_ - 1)); + } + + DOUBLE_CONVERSION_ASSERT(BigitLength() == other.BigitLength()); + + // Both bignums are at the same length now. + // Since other has more than 0 digits we know that the access to + // RawBigit(used_bigits_ - 1) is safe. + const Chunk this_bigit = RawBigit(used_bigits_ - 1); + const Chunk other_bigit = other.RawBigit(other.used_bigits_ - 1); + + if (other.used_bigits_ == 1) { + // Shortcut for easy (and common) case. + int quotient = this_bigit / other_bigit; + RawBigit(used_bigits_ - 1) = this_bigit - other_bigit * quotient; + DOUBLE_CONVERSION_ASSERT(quotient < 0x10000); + result += static_cast(quotient); + Clamp(); + return result; + } + + const int division_estimate = this_bigit / (other_bigit + 1); + DOUBLE_CONVERSION_ASSERT(division_estimate < 0x10000); + result += static_cast(division_estimate); + SubtractTimes(other, division_estimate); + + if (other_bigit * (division_estimate + 1) > this_bigit) { + // No need to even try to subtract. Even if other's remaining digits were 0 + // another subtraction would be too much. + return result; + } + + while (LessEqual(other, *this)) { + SubtractBignum(other); + result++; + } + return result; +} + + +template +static int SizeInHexChars(S number) { + DOUBLE_CONVERSION_ASSERT(number > 0); + int result = 0; + while (number != 0) { + number >>= 4; + result++; + } + return result; +} + + +static char HexCharOfValue(const int value) { + DOUBLE_CONVERSION_ASSERT(0 <= value && value <= 16); + if (value < 10) { + return static_cast(value + '0'); + } + return static_cast(value - 10 + 'A'); +} + + +bool Bignum::ToHexString(char* buffer, const int buffer_size) const { + DOUBLE_CONVERSION_ASSERT(IsClamped()); + // Each bigit must be printable as separate hex-character. + DOUBLE_CONVERSION_ASSERT(kBigitSize % 4 == 0); + static const int kHexCharsPerBigit = kBigitSize / 4; + + if (used_bigits_ == 0) { + if (buffer_size < 2) { + return false; + } + buffer[0] = '0'; + buffer[1] = '\0'; + return true; + } + // We add 1 for the terminating '\0' character. + const int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + + SizeInHexChars(RawBigit(used_bigits_ - 1)) + 1; + if (needed_chars > buffer_size) { + return false; + } + int string_index = needed_chars - 1; + buffer[string_index--] = '\0'; + for (int i = 0; i < exponent_; ++i) { + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = '0'; + } + } + for (int i = 0; i < used_bigits_ - 1; ++i) { + Chunk current_bigit = RawBigit(i); + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = HexCharOfValue(current_bigit & 0xF); + current_bigit >>= 4; + } + } + // And finally the last bigit. + Chunk most_significant_bigit = RawBigit(used_bigits_ - 1); + while (most_significant_bigit != 0) { + buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF); + most_significant_bigit >>= 4; + } + return true; +} + + +Bignum::Chunk Bignum::BigitOrZero(const int index) const { + if (index >= BigitLength()) { + return 0; + } + if (index < exponent_) { + return 0; + } + return RawBigit(index - exponent_); +} + + +int Bignum::Compare(const Bignum& a, const Bignum& b) { + DOUBLE_CONVERSION_ASSERT(a.IsClamped()); + DOUBLE_CONVERSION_ASSERT(b.IsClamped()); + const int bigit_length_a = a.BigitLength(); + const int bigit_length_b = b.BigitLength(); + if (bigit_length_a < bigit_length_b) { + return -1; + } + if (bigit_length_a > bigit_length_b) { + return +1; + } + for (int i = bigit_length_a - 1; i >= (std::min)(a.exponent_, b.exponent_); --i) { + const Chunk bigit_a = a.BigitOrZero(i); + const Chunk bigit_b = b.BigitOrZero(i); + if (bigit_a < bigit_b) { + return -1; + } + if (bigit_a > bigit_b) { + return +1; + } + // Otherwise they are equal up to this digit. Try the next digit. + } + return 0; +} + + +int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { + DOUBLE_CONVERSION_ASSERT(a.IsClamped()); + DOUBLE_CONVERSION_ASSERT(b.IsClamped()); + DOUBLE_CONVERSION_ASSERT(c.IsClamped()); + if (a.BigitLength() < b.BigitLength()) { + return PlusCompare(b, a, c); + } + if (a.BigitLength() + 1 < c.BigitLength()) { + return -1; + } + if (a.BigitLength() > c.BigitLength()) { + return +1; + } + // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than + // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one + // of 'a'. + if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) { + return -1; + } + + Chunk borrow = 0; + // Starting at min_exponent all digits are == 0. So no need to compare them. + const int min_exponent = (std::min)((std::min)(a.exponent_, b.exponent_), c.exponent_); + for (int i = c.BigitLength() - 1; i >= min_exponent; --i) { + const Chunk chunk_a = a.BigitOrZero(i); + const Chunk chunk_b = b.BigitOrZero(i); + const Chunk chunk_c = c.BigitOrZero(i); + const Chunk sum = chunk_a + chunk_b; + if (sum > chunk_c + borrow) { + return +1; + } else { + borrow = chunk_c + borrow - sum; + if (borrow > 1) { + return -1; + } + borrow <<= kBigitSize; + } + } + if (borrow == 0) { + return 0; + } + return -1; +} + + +void Bignum::Clamp() { + while (used_bigits_ > 0 && RawBigit(used_bigits_ - 1) == 0) { + used_bigits_--; + } + if (used_bigits_ == 0) { + // Zero. + exponent_ = 0; + } +} + + +void Bignum::Align(const Bignum& other) { + if (exponent_ > other.exponent_) { + // If "X" represents a "hidden" bigit (by the exponent) then we are in the + // following case (a == this, b == other): + // a: aaaaaaXXXX or a: aaaaaXXX + // b: bbbbbbX b: bbbbbbbbXX + // We replace some of the hidden digits (X) of a with 0 digits. + // a: aaaaaa000X or a: aaaaa0XX + const int zero_bigits = exponent_ - other.exponent_; + EnsureCapacity(used_bigits_ + zero_bigits); + for (int i = used_bigits_ - 1; i >= 0; --i) { + RawBigit(i + zero_bigits) = RawBigit(i); + } + for (int i = 0; i < zero_bigits; ++i) { + RawBigit(i) = 0; + } + used_bigits_ += static_cast(zero_bigits); + exponent_ -= static_cast(zero_bigits); + + DOUBLE_CONVERSION_ASSERT(used_bigits_ >= 0); + DOUBLE_CONVERSION_ASSERT(exponent_ >= 0); + } +} + + +void Bignum::BigitsShiftLeft(const int shift_amount) { + DOUBLE_CONVERSION_ASSERT(shift_amount < kBigitSize); + DOUBLE_CONVERSION_ASSERT(shift_amount >= 0); + Chunk carry = 0; + for (int i = 0; i < used_bigits_; ++i) { + const Chunk new_carry = RawBigit(i) >> (kBigitSize - shift_amount); + RawBigit(i) = ((RawBigit(i) << shift_amount) + carry) & kBigitMask; + carry = new_carry; + } + if (carry != 0) { + RawBigit(used_bigits_) = carry; + used_bigits_++; + } +} + + +void Bignum::SubtractTimes(const Bignum& other, const int factor) { + DOUBLE_CONVERSION_ASSERT(exponent_ <= other.exponent_); + if (factor < 3) { + for (int i = 0; i < factor; ++i) { + SubtractBignum(other); + } + return; + } + Chunk borrow = 0; + const int exponent_diff = other.exponent_ - exponent_; + for (int i = 0; i < other.used_bigits_; ++i) { + const DoubleChunk product = static_cast(factor) * other.RawBigit(i); + const DoubleChunk remove = borrow + product; + const Chunk difference = RawBigit(i + exponent_diff) - (remove & kBigitMask); + RawBigit(i + exponent_diff) = difference & kBigitMask; + borrow = static_cast((difference >> (kChunkSize - 1)) + + (remove >> kBigitSize)); + } + for (int i = other.used_bigits_ + exponent_diff; i < used_bigits_; ++i) { + if (borrow == 0) { + return; + } + const Chunk difference = RawBigit(i) - borrow; + RawBigit(i) = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + } + Clamp(); +} + + +} // 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-bignum.h b/deps/icu-small/source/i18n/double-conversion-bignum.h index bae900a15a7420..a0d6301fd7df1a 100644 --- a/deps/icu-small/source/i18n/double-conversion-bignum.h +++ b/deps/icu-small/source/i18n/double-conversion-bignum.h @@ -1,170 +1,170 @@ -// © 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_BIGNUM_H_ -#define DOUBLE_CONVERSION_BIGNUM_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 { - -class Bignum { - public: - // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately. - // This bignum can encode much bigger numbers, since it contains an - // exponent. - static const int kMaxSignificantBits = 3584; - - Bignum() : used_bigits_(0), exponent_(0) {} - - void AssignUInt16(const uint16_t value); - void AssignUInt64(uint64_t value); - void AssignBignum(const Bignum& other); - - void AssignDecimalString(const Vector value); - void AssignHexString(const Vector value); - - void AssignPowerUInt16(uint16_t base, const int exponent); - - void AddUInt64(const uint64_t operand); - void AddBignum(const Bignum& other); - // Precondition: this >= other. - void SubtractBignum(const Bignum& other); - - void Square(); - void ShiftLeft(const int shift_amount); - void MultiplyByUInt32(const uint32_t factor); - void MultiplyByUInt64(const uint64_t factor); - void MultiplyByPowerOfTen(const int exponent); - void Times10() { return MultiplyByUInt32(10); } - // Pseudocode: - // int result = this / other; - // this = this % other; - // In the worst case this function is in O(this/other). - uint16_t DivideModuloIntBignum(const Bignum& other); - - bool ToHexString(char* buffer, const int buffer_size) const; - - // Returns - // -1 if a < b, - // 0 if a == b, and - // +1 if a > b. - static int Compare(const Bignum& a, const Bignum& b); - static bool Equal(const Bignum& a, const Bignum& b) { - return Compare(a, b) == 0; - } - static bool LessEqual(const Bignum& a, const Bignum& b) { - return Compare(a, b) <= 0; - } - static bool Less(const Bignum& a, const Bignum& b) { - return Compare(a, b) < 0; - } - // Returns Compare(a + b, c); - static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c); - // Returns a + b == c - static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) { - return PlusCompare(a, b, c) == 0; - } - // Returns a + b <= c - static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) { - return PlusCompare(a, b, c) <= 0; - } - // Returns a + b < c - static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) { - return PlusCompare(a, b, c) < 0; - } - private: - typedef uint32_t Chunk; - typedef uint64_t DoubleChunk; - - static const int kChunkSize = sizeof(Chunk) * 8; - static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8; - // With bigit size of 28 we loose some bits, but a double still fits easily - // into two chunks, and more importantly we can use the Comba multiplication. - static const int kBigitSize = 28; - static const Chunk kBigitMask = (1 << kBigitSize) - 1; - // Every instance allocates kBigitLength chunks on the stack. Bignums cannot - // grow. There are no checks if the stack-allocated space is sufficient. - static const int kBigitCapacity = kMaxSignificantBits / kBigitSize; - - static void EnsureCapacity(const int size) { - if (size > kBigitCapacity) { - DOUBLE_CONVERSION_UNREACHABLE(); - } - } - void Align(const Bignum& other); - void Clamp(); - bool IsClamped() const { - return used_bigits_ == 0 || RawBigit(used_bigits_ - 1) != 0; - } - void Zero() { - used_bigits_ = 0; - exponent_ = 0; - } - // Requires this to have enough capacity (no tests done). - // Updates used_bigits_ if necessary. - // shift_amount must be < kBigitSize. - void BigitsShiftLeft(const int shift_amount); - // BigitLength includes the "hidden" bigits encoded in the exponent. - int BigitLength() const { return used_bigits_ + exponent_; } - Chunk& RawBigit(const int index); - const Chunk& RawBigit(const int index) const; - Chunk BigitOrZero(const int index) const; - void SubtractTimes(const Bignum& other, const int factor); - - // The Bignum's value is value(bigits_buffer_) * 2^(exponent_ * kBigitSize), - // where the value of the buffer consists of the lower kBigitSize bits of - // the first used_bigits_ Chunks in bigits_buffer_, first chunk has lowest - // significant bits. - int16_t used_bigits_; - int16_t exponent_; - Chunk bigits_buffer_[kBigitCapacity]; - - DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(Bignum); -}; - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END - -#endif // DOUBLE_CONVERSION_BIGNUM_H_ -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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_BIGNUM_H_ +#define DOUBLE_CONVERSION_BIGNUM_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 { + +class Bignum { + public: + // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately. + // This bignum can encode much bigger numbers, since it contains an + // exponent. + static const int kMaxSignificantBits = 3584; + + Bignum() : used_bigits_(0), exponent_(0) {} + + void AssignUInt16(const uint16_t value); + void AssignUInt64(uint64_t value); + void AssignBignum(const Bignum& other); + + void AssignDecimalString(const Vector value); + void AssignHexString(const Vector value); + + void AssignPowerUInt16(uint16_t base, const int exponent); + + void AddUInt64(const uint64_t operand); + void AddBignum(const Bignum& other); + // Precondition: this >= other. + void SubtractBignum(const Bignum& other); + + void Square(); + void ShiftLeft(const int shift_amount); + void MultiplyByUInt32(const uint32_t factor); + void MultiplyByUInt64(const uint64_t factor); + void MultiplyByPowerOfTen(const int exponent); + void Times10() { return MultiplyByUInt32(10); } + // Pseudocode: + // int result = this / other; + // this = this % other; + // In the worst case this function is in O(this/other). + uint16_t DivideModuloIntBignum(const Bignum& other); + + bool ToHexString(char* buffer, const int buffer_size) const; + + // Returns + // -1 if a < b, + // 0 if a == b, and + // +1 if a > b. + static int Compare(const Bignum& a, const Bignum& b); + static bool Equal(const Bignum& a, const Bignum& b) { + return Compare(a, b) == 0; + } + static bool LessEqual(const Bignum& a, const Bignum& b) { + return Compare(a, b) <= 0; + } + static bool Less(const Bignum& a, const Bignum& b) { + return Compare(a, b) < 0; + } + // Returns Compare(a + b, c); + static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c); + // Returns a + b == c + static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) == 0; + } + // Returns a + b <= c + static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) <= 0; + } + // Returns a + b < c + static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) < 0; + } + private: + typedef uint32_t Chunk; + typedef uint64_t DoubleChunk; + + static const int kChunkSize = sizeof(Chunk) * 8; + static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8; + // With bigit size of 28 we loose some bits, but a double still fits easily + // into two chunks, and more importantly we can use the Comba multiplication. + static const int kBigitSize = 28; + static const Chunk kBigitMask = (1 << kBigitSize) - 1; + // Every instance allocates kBigitLength chunks on the stack. Bignums cannot + // grow. There are no checks if the stack-allocated space is sufficient. + static const int kBigitCapacity = kMaxSignificantBits / kBigitSize; + + static void EnsureCapacity(const int size) { + if (size > kBigitCapacity) { + DOUBLE_CONVERSION_UNREACHABLE(); + } + } + void Align(const Bignum& other); + void Clamp(); + bool IsClamped() const { + return used_bigits_ == 0 || RawBigit(used_bigits_ - 1) != 0; + } + void Zero() { + used_bigits_ = 0; + exponent_ = 0; + } + // Requires this to have enough capacity (no tests done). + // Updates used_bigits_ if necessary. + // shift_amount must be < kBigitSize. + void BigitsShiftLeft(const int shift_amount); + // BigitLength includes the "hidden" bigits encoded in the exponent. + int BigitLength() const { return used_bigits_ + exponent_; } + Chunk& RawBigit(const int index); + const Chunk& RawBigit(const int index) const; + Chunk BigitOrZero(const int index) const; + void SubtractTimes(const Bignum& other, const int factor); + + // The Bignum's value is value(bigits_buffer_) * 2^(exponent_ * kBigitSize), + // where the value of the buffer consists of the lower kBigitSize bits of + // the first used_bigits_ Chunks in bigits_buffer_, first chunk has lowest + // significant bits. + int16_t used_bigits_; + int16_t exponent_; + Chunk bigits_buffer_[kBigitCapacity]; + + DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(Bignum); +}; + +} // namespace double_conversion + +// ICU PATCH: Close ICU namespace +U_NAMESPACE_END + +#endif // DOUBLE_CONVERSION_BIGNUM_H_ +#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/double-conversion-cached-powers.cpp b/deps/icu-small/source/i18n/double-conversion-cached-powers.cpp index 3bc35c8aaf81dc..e1abf9ed103566 100644 --- a/deps/icu-small/source/i18n/double-conversion-cached-powers.cpp +++ b/deps/icu-small/source/i18n/double-conversion-cached-powers.cpp @@ -1,193 +1,193 @@ -// © 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 2006-2008 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 -#include - -// ICU PATCH: Customize header file paths for ICU. - -#include "double-conversion-utils.h" - -#include "double-conversion-cached-powers.h" - -// ICU PATCH: Wrap in ICU namespace -U_NAMESPACE_BEGIN - -namespace double_conversion { - -namespace PowersOfTenCache { - -struct CachedPower { - uint64_t significand; - int16_t binary_exponent; - int16_t decimal_exponent; -}; - -static const CachedPower kCachedPowers[] = { - {DOUBLE_CONVERSION_UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xef340a98, 172aace5), -715, -196}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xaa242499, 697392d3), -183, -36}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xd1b71758, e219652c), -77, -4}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x9c400000, 00000000), -50, 4}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x813f3978, f8940984), 30, 28}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x924d692c, a61be758), 269, 100}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xa59bc234, db398c25), 508, 172}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xd01fef10, a657842c), 800, 260}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332}, - {DOUBLE_CONVERSION_UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340}, -}; - -static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent. -static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) - -void GetCachedPowerForBinaryExponentRange( - int min_exponent, - int max_exponent, - DiyFp* power, - int* decimal_exponent) { - int kQ = DiyFp::kSignificandSize; - double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10); - int foo = kCachedPowersOffset; - int index = - (foo + static_cast(k) - 1) / kDecimalExponentDistance + 1; - DOUBLE_CONVERSION_ASSERT(0 <= index && index < static_cast(DOUBLE_CONVERSION_ARRAY_SIZE(kCachedPowers))); - CachedPower cached_power = kCachedPowers[index]; - DOUBLE_CONVERSION_ASSERT(min_exponent <= cached_power.binary_exponent); - (void) max_exponent; // Mark variable as used. - DOUBLE_CONVERSION_ASSERT(cached_power.binary_exponent <= max_exponent); - *decimal_exponent = cached_power.decimal_exponent; - *power = DiyFp(cached_power.significand, cached_power.binary_exponent); -} - - -void GetCachedPowerForDecimalExponent(int requested_exponent, - DiyFp* power, - int* found_exponent) { - DOUBLE_CONVERSION_ASSERT(kMinDecimalExponent <= requested_exponent); - DOUBLE_CONVERSION_ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance); - int index = - (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance; - CachedPower cached_power = kCachedPowers[index]; - *power = DiyFp(cached_power.significand, cached_power.binary_exponent); - *found_exponent = cached_power.decimal_exponent; - DOUBLE_CONVERSION_ASSERT(*found_exponent <= requested_exponent); - DOUBLE_CONVERSION_ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance); -} - -} // namespace PowersOfTenCache - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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 2006-2008 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 +#include + +// ICU PATCH: Customize header file paths for ICU. + +#include "double-conversion-utils.h" + +#include "double-conversion-cached-powers.h" + +// ICU PATCH: Wrap in ICU namespace +U_NAMESPACE_BEGIN + +namespace double_conversion { + +namespace PowersOfTenCache { + +struct CachedPower { + uint64_t significand; + int16_t binary_exponent; + int16_t decimal_exponent; +}; + +static const CachedPower kCachedPowers[] = { + {DOUBLE_CONVERSION_UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xef340a98, 172aace5), -715, -196}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xaa242499, 697392d3), -183, -36}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xd1b71758, e219652c), -77, -4}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9c400000, 00000000), -50, 4}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x813f3978, f8940984), 30, 28}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x924d692c, a61be758), 269, 100}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xa59bc234, db398c25), 508, 172}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xd01fef10, a657842c), 800, 260}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332}, + {DOUBLE_CONVERSION_UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340}, +}; + +static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent. +static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) + +void GetCachedPowerForBinaryExponentRange( + int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent) { + int kQ = DiyFp::kSignificandSize; + double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10); + int foo = kCachedPowersOffset; + int index = + (foo + static_cast(k) - 1) / kDecimalExponentDistance + 1; + DOUBLE_CONVERSION_ASSERT(0 <= index && index < static_cast(DOUBLE_CONVERSION_ARRAY_SIZE(kCachedPowers))); + CachedPower cached_power = kCachedPowers[index]; + DOUBLE_CONVERSION_ASSERT(min_exponent <= cached_power.binary_exponent); + (void) max_exponent; // Mark variable as used. + DOUBLE_CONVERSION_ASSERT(cached_power.binary_exponent <= max_exponent); + *decimal_exponent = cached_power.decimal_exponent; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); +} + + +void GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent) { + DOUBLE_CONVERSION_ASSERT(kMinDecimalExponent <= requested_exponent); + DOUBLE_CONVERSION_ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance); + int index = + (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance; + CachedPower cached_power = kCachedPowers[index]; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); + *found_exponent = cached_power.decimal_exponent; + DOUBLE_CONVERSION_ASSERT(*found_exponent <= requested_exponent); + DOUBLE_CONVERSION_ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance); +} + +} // namespace PowersOfTenCache + +} // 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-cached-powers.h b/deps/icu-small/source/i18n/double-conversion-cached-powers.h index ade27baef8b0c7..a4ba6c78e0b25e 100644 --- a/deps/icu-small/source/i18n/double-conversion-cached-powers.h +++ b/deps/icu-small/source/i18n/double-conversion-cached-powers.h @@ -1,82 +1,82 @@ -// © 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_CACHED_POWERS_H_ -#define DOUBLE_CONVERSION_CACHED_POWERS_H_ - -// ICU PATCH: Customize header file paths for ICU. - -#include "double-conversion-diy-fp.h" - -// ICU PATCH: Wrap in ICU namespace -U_NAMESPACE_BEGIN - -namespace double_conversion { - -namespace PowersOfTenCache { - - // Not all powers of ten are cached. The decimal exponent of two neighboring - // cached numbers will differ by kDecimalExponentDistance. - static const int kDecimalExponentDistance = 8; - - static const int kMinDecimalExponent = -348; - static const int kMaxDecimalExponent = 340; - - // Returns a cached power-of-ten with a binary exponent in the range - // [min_exponent; max_exponent] (boundaries included). - void GetCachedPowerForBinaryExponentRange(int min_exponent, - int max_exponent, - DiyFp* power, - int* decimal_exponent); - - // Returns a cached power of ten x ~= 10^k such that - // k <= decimal_exponent < k + kCachedPowersDecimalDistance. - // The given decimal_exponent must satisfy - // kMinDecimalExponent <= requested_exponent, and - // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance. - void GetCachedPowerForDecimalExponent(int requested_exponent, - DiyFp* power, - int* found_exponent); - -} // namespace PowersOfTenCache - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END - -#endif // DOUBLE_CONVERSION_CACHED_POWERS_H_ -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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_CACHED_POWERS_H_ +#define DOUBLE_CONVERSION_CACHED_POWERS_H_ + +// ICU PATCH: Customize header file paths for ICU. + +#include "double-conversion-diy-fp.h" + +// ICU PATCH: Wrap in ICU namespace +U_NAMESPACE_BEGIN + +namespace double_conversion { + +namespace PowersOfTenCache { + + // Not all powers of ten are cached. The decimal exponent of two neighboring + // cached numbers will differ by kDecimalExponentDistance. + static const int kDecimalExponentDistance = 8; + + static const int kMinDecimalExponent = -348; + static const int kMaxDecimalExponent = 340; + + // Returns a cached power-of-ten with a binary exponent in the range + // [min_exponent; max_exponent] (boundaries included). + void GetCachedPowerForBinaryExponentRange(int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent); + + // Returns a cached power of ten x ~= 10^k such that + // k <= decimal_exponent < k + kCachedPowersDecimalDistance. + // The given decimal_exponent must satisfy + // kMinDecimalExponent <= requested_exponent, and + // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance. + void GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent); + +} // namespace PowersOfTenCache + +} // namespace double_conversion + +// ICU PATCH: Close ICU namespace +U_NAMESPACE_END + +#endif // DOUBLE_CONVERSION_CACHED_POWERS_H_ +#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/double-conversion-diy-fp.h b/deps/icu-small/source/i18n/double-conversion-diy-fp.h index 5820abedf35405..14dfdf4bd3516f 100644 --- a/deps/icu-small/source/i18n/double-conversion-diy-fp.h +++ b/deps/icu-small/source/i18n/double-conversion-diy-fp.h @@ -1,155 +1,155 @@ -// © 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_DIY_FP_H_ -#define DOUBLE_CONVERSION_DIY_FP_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 { - -// This "Do It Yourself Floating Point" class implements a floating-point number -// with a uint64 significand and an int exponent. Normalized DiyFp numbers will -// have the most significant bit of the significand set. -// Multiplication and Subtraction do not normalize their results. -// DiyFp store only non-negative numbers and are not designed to contain special -// doubles (NaN and Infinity). -class DiyFp { - public: - static const int kSignificandSize = 64; - - DiyFp() : f_(0), e_(0) {} - DiyFp(const uint64_t significand, const int32_t exponent) : f_(significand), e_(exponent) {} - - // this -= other. - // The exponents of both numbers must be the same and the significand of this - // must be greater or equal than the significand of other. - // The result will not be normalized. - void Subtract(const DiyFp& other) { - DOUBLE_CONVERSION_ASSERT(e_ == other.e_); - DOUBLE_CONVERSION_ASSERT(f_ >= other.f_); - f_ -= other.f_; - } - - // Returns a - b. - // The exponents of both numbers must be the same and a must be greater - // or equal than b. The result will not be normalized. - static DiyFp Minus(const DiyFp& a, const DiyFp& b) { - DiyFp result = a; - result.Subtract(b); - return result; - } - - // this *= other. - void Multiply(const DiyFp& other) { - // Simply "emulates" a 128 bit multiplication. - // However: the resulting number only contains 64 bits. The least - // significant 64 bits are only used for rounding the most significant 64 - // bits. - const uint64_t kM32 = 0xFFFFFFFFU; - const uint64_t a = f_ >> 32; - const uint64_t b = f_ & kM32; - const uint64_t c = other.f_ >> 32; - const uint64_t d = other.f_ & kM32; - const uint64_t ac = a * c; - const uint64_t bc = b * c; - const uint64_t ad = a * d; - const uint64_t bd = b * d; - // By adding 1U << 31 to tmp we round the final result. - // Halfway cases will be rounded up. - const uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32) + (1U << 31); - e_ += other.e_ + 64; - f_ = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); - } - - // returns a * b; - static DiyFp Times(const DiyFp& a, const DiyFp& b) { - DiyFp result = a; - result.Multiply(b); - return result; - } - - void Normalize() { - DOUBLE_CONVERSION_ASSERT(f_ != 0); - uint64_t significand = f_; - int32_t exponent = e_; - - // This method is mainly called for normalizing boundaries. In general, - // boundaries need to be shifted by 10 bits, and we optimize for this case. - const uint64_t k10MSBits = DOUBLE_CONVERSION_UINT64_2PART_C(0xFFC00000, 00000000); - while ((significand & k10MSBits) == 0) { - significand <<= 10; - exponent -= 10; - } - while ((significand & kUint64MSB) == 0) { - significand <<= 1; - exponent--; - } - f_ = significand; - e_ = exponent; - } - - static DiyFp Normalize(const DiyFp& a) { - DiyFp result = a; - result.Normalize(); - return result; - } - - uint64_t f() const { return f_; } - int32_t e() const { return e_; } - - void set_f(uint64_t new_value) { f_ = new_value; } - void set_e(int32_t new_value) { e_ = new_value; } - - private: - static const uint64_t kUint64MSB = DOUBLE_CONVERSION_UINT64_2PART_C(0x80000000, 00000000); - - uint64_t f_; - int32_t e_; -}; - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END - -#endif // DOUBLE_CONVERSION_DIY_FP_H_ -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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_DIY_FP_H_ +#define DOUBLE_CONVERSION_DIY_FP_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 { + +// This "Do It Yourself Floating Point" class implements a floating-point number +// with a uint64 significand and an int exponent. Normalized DiyFp numbers will +// have the most significant bit of the significand set. +// Multiplication and Subtraction do not normalize their results. +// DiyFp store only non-negative numbers and are not designed to contain special +// doubles (NaN and Infinity). +class DiyFp { + public: + static const int kSignificandSize = 64; + + DiyFp() : f_(0), e_(0) {} + DiyFp(const uint64_t significand, const int32_t exponent) : f_(significand), e_(exponent) {} + + // this -= other. + // The exponents of both numbers must be the same and the significand of this + // must be greater or equal than the significand of other. + // The result will not be normalized. + void Subtract(const DiyFp& other) { + DOUBLE_CONVERSION_ASSERT(e_ == other.e_); + DOUBLE_CONVERSION_ASSERT(f_ >= other.f_); + f_ -= other.f_; + } + + // Returns a - b. + // The exponents of both numbers must be the same and a must be greater + // or equal than b. The result will not be normalized. + static DiyFp Minus(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Subtract(b); + return result; + } + + // this *= other. + void Multiply(const DiyFp& other) { + // Simply "emulates" a 128 bit multiplication. + // However: the resulting number only contains 64 bits. The least + // significant 64 bits are only used for rounding the most significant 64 + // bits. + const uint64_t kM32 = 0xFFFFFFFFU; + const uint64_t a = f_ >> 32; + const uint64_t b = f_ & kM32; + const uint64_t c = other.f_ >> 32; + const uint64_t d = other.f_ & kM32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + // By adding 1U << 31 to tmp we round the final result. + // Halfway cases will be rounded up. + const uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32) + (1U << 31); + e_ += other.e_ + 64; + f_ = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + } + + // returns a * b; + static DiyFp Times(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Multiply(b); + return result; + } + + void Normalize() { + DOUBLE_CONVERSION_ASSERT(f_ != 0); + uint64_t significand = f_; + int32_t exponent = e_; + + // This method is mainly called for normalizing boundaries. In general, + // boundaries need to be shifted by 10 bits, and we optimize for this case. + const uint64_t k10MSBits = DOUBLE_CONVERSION_UINT64_2PART_C(0xFFC00000, 00000000); + while ((significand & k10MSBits) == 0) { + significand <<= 10; + exponent -= 10; + } + while ((significand & kUint64MSB) == 0) { + significand <<= 1; + exponent--; + } + f_ = significand; + e_ = exponent; + } + + static DiyFp Normalize(const DiyFp& a) { + DiyFp result = a; + result.Normalize(); + return result; + } + + uint64_t f() const { return f_; } + int32_t e() const { return e_; } + + void set_f(uint64_t new_value) { f_ = new_value; } + void set_e(int32_t new_value) { e_ = new_value; } + + private: + static const uint64_t kUint64MSB = DOUBLE_CONVERSION_UINT64_2PART_C(0x80000000, 00000000); + + uint64_t f_; + int32_t e_; +}; + +} // namespace double_conversion + +// ICU PATCH: Close ICU namespace +U_NAMESPACE_END + +#endif // DOUBLE_CONVERSION_DIY_FP_H_ +#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/double-conversion-double-to-string.cpp b/deps/icu-small/source/i18n/double-conversion-double-to-string.cpp index 5ee6d2b8e878c9..89fab398db550a 100644 --- a/deps/icu-small/source/i18n/double-conversion-double-to-string.cpp +++ b/deps/icu-small/source/i18n/double-conversion-double-to-string.cpp @@ -1,462 +1,462 @@ -// © 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 -#include - -// ICU PATCH: Customize header file paths for ICU. -// The file fixed-dtoa.h is not needed. - -#include "double-conversion-double-to-string.h" - -#include "double-conversion-bignum-dtoa.h" -#include "double-conversion-fast-dtoa.h" -#include "double-conversion-ieee.h" -#include "double-conversion-utils.h" - -// ICU PATCH: Wrap in ICU namespace -U_NAMESPACE_BEGIN - -namespace double_conversion { - -#if 0 // not needed for ICU -const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { - int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN; - static DoubleToStringConverter converter(flags, - "Infinity", - "NaN", - 'e', - -6, 21, - 6, 0); - return converter; -} - - -bool DoubleToStringConverter::HandleSpecialValues( - double value, - StringBuilder* result_builder) const { - Double double_inspect(value); - if (double_inspect.IsInfinite()) { - if (infinity_symbol_ == DOUBLE_CONVERSION_NULLPTR) return false; - if (value < 0) { - result_builder->AddCharacter('-'); - } - result_builder->AddString(infinity_symbol_); - return true; - } - if (double_inspect.IsNan()) { - if (nan_symbol_ == DOUBLE_CONVERSION_NULLPTR) return false; - result_builder->AddString(nan_symbol_); - return true; - } - return false; -} - - -void DoubleToStringConverter::CreateExponentialRepresentation( - const char* decimal_digits, - int length, - int exponent, - StringBuilder* result_builder) const { - DOUBLE_CONVERSION_ASSERT(length != 0); - result_builder->AddCharacter(decimal_digits[0]); - if (length != 1) { - result_builder->AddCharacter('.'); - result_builder->AddSubstring(&decimal_digits[1], length-1); - } - result_builder->AddCharacter(exponent_character_); - if (exponent < 0) { - result_builder->AddCharacter('-'); - exponent = -exponent; - } else { - if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { - result_builder->AddCharacter('+'); - } - } - DOUBLE_CONVERSION_ASSERT(exponent < 1e4); - // Changing this constant requires updating the comment of DoubleToStringConverter constructor - const int kMaxExponentLength = 5; - char buffer[kMaxExponentLength + 1]; - buffer[kMaxExponentLength] = '\0'; - int first_char_pos = kMaxExponentLength; - if (exponent == 0) { - buffer[--first_char_pos] = '0'; - } else { - while (exponent > 0) { - buffer[--first_char_pos] = '0' + (exponent % 10); - exponent /= 10; - } - } - // Add prefix '0' to make exponent width >= min(min_exponent_with_, kMaxExponentLength) - // For example: convert 1e+9 -> 1e+09, if min_exponent_with_ is set to 2 - while(kMaxExponentLength - first_char_pos < std::min(min_exponent_width_, kMaxExponentLength)) { - buffer[--first_char_pos] = '0'; - } - result_builder->AddSubstring(&buffer[first_char_pos], - kMaxExponentLength - first_char_pos); -} - - -void DoubleToStringConverter::CreateDecimalRepresentation( - const char* decimal_digits, - int length, - int decimal_point, - int digits_after_point, - StringBuilder* result_builder) const { - // Create a representation that is padded with zeros if needed. - if (decimal_point <= 0) { - // "0.00000decimal_rep" or "0.000decimal_rep00". - result_builder->AddCharacter('0'); - if (digits_after_point > 0) { - result_builder->AddCharacter('.'); - result_builder->AddPadding('0', -decimal_point); - DOUBLE_CONVERSION_ASSERT(length <= digits_after_point - (-decimal_point)); - result_builder->AddSubstring(decimal_digits, length); - int remaining_digits = digits_after_point - (-decimal_point) - length; - result_builder->AddPadding('0', remaining_digits); - } - } else if (decimal_point >= length) { - // "decimal_rep0000.00000" or "decimal_rep.0000". - result_builder->AddSubstring(decimal_digits, length); - result_builder->AddPadding('0', decimal_point - length); - if (digits_after_point > 0) { - result_builder->AddCharacter('.'); - result_builder->AddPadding('0', digits_after_point); - } - } else { - // "decima.l_rep000". - DOUBLE_CONVERSION_ASSERT(digits_after_point > 0); - result_builder->AddSubstring(decimal_digits, decimal_point); - result_builder->AddCharacter('.'); - DOUBLE_CONVERSION_ASSERT(length - decimal_point <= digits_after_point); - result_builder->AddSubstring(&decimal_digits[decimal_point], - length - decimal_point); - int remaining_digits = digits_after_point - (length - decimal_point); - result_builder->AddPadding('0', remaining_digits); - } - if (digits_after_point == 0) { - if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { - result_builder->AddCharacter('.'); - } - if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { - result_builder->AddCharacter('0'); - } - } -} - - -bool DoubleToStringConverter::ToShortestIeeeNumber( - double value, - StringBuilder* result_builder, - DoubleToStringConverter::DtoaMode mode) const { - DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE); - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - int decimal_point; - bool sign; - const int kDecimalRepCapacity = kBase10MaximalLength + 1; - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - - DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - - bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - int exponent = decimal_point - 1; - if ((decimal_in_shortest_low_ <= exponent) && - (exponent < decimal_in_shortest_high_)) { - CreateDecimalRepresentation(decimal_rep, decimal_rep_length, - decimal_point, - (std::max)(0, decimal_rep_length - decimal_point), - result_builder); - } else { - CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, - result_builder); - } - return true; -} - - -bool DoubleToStringConverter::ToFixed(double value, - int requested_digits, - StringBuilder* result_builder) const { - DOUBLE_CONVERSION_ASSERT(kMaxFixedDigitsBeforePoint == 60); - const double kFirstNonFixed = 1e60; - - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - if (requested_digits > kMaxFixedDigitsAfterPoint) return false; - if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false; - - // Find a sufficiently precise decimal representation of n. - int decimal_point; - bool sign; - // Add space for the '\0' byte. - const int kDecimalRepCapacity = - kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - DoubleToAscii(value, FIXED, requested_digits, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - - bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, - requested_digits, result_builder); - return true; -} - - -bool DoubleToStringConverter::ToExponential( - double value, - int requested_digits, - StringBuilder* result_builder) const { - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - if (requested_digits < -1) return false; - if (requested_digits > kMaxExponentialDigits) return false; - - int decimal_point; - bool sign; - // Add space for digit before the decimal point and the '\0' character. - const int kDecimalRepCapacity = kMaxExponentialDigits + 2; - DOUBLE_CONVERSION_ASSERT(kDecimalRepCapacity > kBase10MaximalLength); - char decimal_rep[kDecimalRepCapacity]; -#ifndef NDEBUG - // Problem: there is an assert in StringBuilder::AddSubstring() that - // will pass this buffer to strlen(), and this buffer is not generally - // null-terminated. - memset(decimal_rep, 0, sizeof(decimal_rep)); -#endif - int decimal_rep_length; - - if (requested_digits == -1) { - DoubleToAscii(value, SHORTEST, 0, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - } else { - DoubleToAscii(value, PRECISION, requested_digits + 1, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= requested_digits + 1); - - for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { - decimal_rep[i] = '0'; - } - decimal_rep_length = requested_digits + 1; - } - - bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - int exponent = decimal_point - 1; - CreateExponentialRepresentation(decimal_rep, - decimal_rep_length, - exponent, - result_builder); - return true; -} - - -bool DoubleToStringConverter::ToPrecision(double value, - int precision, - StringBuilder* result_builder) const { - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { - return false; - } - - // Find a sufficiently precise decimal representation of n. - int decimal_point; - bool sign; - // Add one for the terminating null character. - const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - - DoubleToAscii(value, PRECISION, precision, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= precision); - - bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - // The exponent if we print the number as x.xxeyyy. That is with the - // decimal point after the first digit. - int exponent = decimal_point - 1; - - int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; - bool as_exponential = - (-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || - (decimal_point - precision + extra_zero > - max_trailing_padding_zeroes_in_precision_mode_); - if ((flags_ & NO_TRAILING_ZERO) != 0) { - // Truncate trailing zeros that occur after the decimal point (if exponential, - // that is everything after the first digit). - int stop = as_exponential ? 1 : std::max(1, decimal_point); - while (decimal_rep_length > stop && decimal_rep[decimal_rep_length - 1] == '0') { - --decimal_rep_length; - } - // Clamp precision to avoid the code below re-adding the zeros. - precision = std::min(precision, decimal_rep_length); - } - if (as_exponential) { - // Fill buffer to contain 'precision' digits. - // Usually the buffer is already at the correct length, but 'DoubleToAscii' - // is allowed to return less characters. - for (int i = decimal_rep_length; i < precision; ++i) { - decimal_rep[i] = '0'; - } - - CreateExponentialRepresentation(decimal_rep, - precision, - exponent, - result_builder); - } else { - CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, - (std::max)(0, precision - decimal_point), - result_builder); - } - return true; -} -#endif // not needed for ICU - - -static BignumDtoaMode DtoaToBignumDtoaMode( - DoubleToStringConverter::DtoaMode dtoa_mode) { - switch (dtoa_mode) { - case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST; - case DoubleToStringConverter::SHORTEST_SINGLE: - return BIGNUM_DTOA_SHORTEST_SINGLE; - case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED; - case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION; - default: - DOUBLE_CONVERSION_UNREACHABLE(); - } -} - - -void DoubleToStringConverter::DoubleToAscii(double v, - DtoaMode mode, - int requested_digits, - char* buffer, - int buffer_length, - bool* sign, - int* length, - int* point) { - Vector vector(buffer, buffer_length); - DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial()); - DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0); - - if (Double(v).Sign() < 0) { - *sign = true; - v = -v; - } else { - *sign = false; - } - - if (mode == PRECISION && requested_digits == 0) { - vector[0] = '\0'; - *length = 0; - return; - } - - if (v == 0) { - vector[0] = '0'; - vector[1] = '\0'; - *length = 1; - *point = 1; - return; - } - - bool fast_worked; - switch (mode) { - case SHORTEST: - fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); - break; -#if 0 // not needed for ICU - case SHORTEST_SINGLE: - fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0, - vector, length, point); - break; - case FIXED: - fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); - break; - case PRECISION: - fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, - vector, length, point); - break; -#endif // not needed for ICU - default: - fast_worked = false; - DOUBLE_CONVERSION_UNREACHABLE(); - } - if (fast_worked) return; - - // If the fast dtoa didn't succeed use the slower bignum version. - BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); - BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); - vector[*length] = '\0'; -} - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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 +#include + +// ICU PATCH: Customize header file paths for ICU. +// The file fixed-dtoa.h is not needed. + +#include "double-conversion-double-to-string.h" + +#include "double-conversion-bignum-dtoa.h" +#include "double-conversion-fast-dtoa.h" +#include "double-conversion-ieee.h" +#include "double-conversion-utils.h" + +// ICU PATCH: Wrap in ICU namespace +U_NAMESPACE_BEGIN + +namespace double_conversion { + +#if 0 // not needed for ICU +const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { + int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN; + static DoubleToStringConverter converter(flags, + "Infinity", + "NaN", + 'e', + -6, 21, + 6, 0); + return converter; +} + + +bool DoubleToStringConverter::HandleSpecialValues( + double value, + StringBuilder* result_builder) const { + Double double_inspect(value); + if (double_inspect.IsInfinite()) { + if (infinity_symbol_ == DOUBLE_CONVERSION_NULLPTR) return false; + if (value < 0) { + result_builder->AddCharacter('-'); + } + result_builder->AddString(infinity_symbol_); + return true; + } + if (double_inspect.IsNan()) { + if (nan_symbol_ == DOUBLE_CONVERSION_NULLPTR) return false; + result_builder->AddString(nan_symbol_); + return true; + } + return false; +} + + +void DoubleToStringConverter::CreateExponentialRepresentation( + const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const { + DOUBLE_CONVERSION_ASSERT(length != 0); + result_builder->AddCharacter(decimal_digits[0]); + if (length != 1) { + result_builder->AddCharacter('.'); + result_builder->AddSubstring(&decimal_digits[1], length-1); + } + result_builder->AddCharacter(exponent_character_); + if (exponent < 0) { + result_builder->AddCharacter('-'); + exponent = -exponent; + } else { + if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { + result_builder->AddCharacter('+'); + } + } + DOUBLE_CONVERSION_ASSERT(exponent < 1e4); + // Changing this constant requires updating the comment of DoubleToStringConverter constructor + const int kMaxExponentLength = 5; + char buffer[kMaxExponentLength + 1]; + buffer[kMaxExponentLength] = '\0'; + int first_char_pos = kMaxExponentLength; + if (exponent == 0) { + buffer[--first_char_pos] = '0'; + } else { + while (exponent > 0) { + buffer[--first_char_pos] = '0' + (exponent % 10); + exponent /= 10; + } + } + // Add prefix '0' to make exponent width >= min(min_exponent_with_, kMaxExponentLength) + // For example: convert 1e+9 -> 1e+09, if min_exponent_with_ is set to 2 + while(kMaxExponentLength - first_char_pos < std::min(min_exponent_width_, kMaxExponentLength)) { + buffer[--first_char_pos] = '0'; + } + result_builder->AddSubstring(&buffer[first_char_pos], + kMaxExponentLength - first_char_pos); +} + + +void DoubleToStringConverter::CreateDecimalRepresentation( + const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const { + // Create a representation that is padded with zeros if needed. + if (decimal_point <= 0) { + // "0.00000decimal_rep" or "0.000decimal_rep00". + result_builder->AddCharacter('0'); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', -decimal_point); + DOUBLE_CONVERSION_ASSERT(length <= digits_after_point - (-decimal_point)); + result_builder->AddSubstring(decimal_digits, length); + int remaining_digits = digits_after_point - (-decimal_point) - length; + result_builder->AddPadding('0', remaining_digits); + } + } else if (decimal_point >= length) { + // "decimal_rep0000.00000" or "decimal_rep.0000". + result_builder->AddSubstring(decimal_digits, length); + result_builder->AddPadding('0', decimal_point - length); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', digits_after_point); + } + } else { + // "decima.l_rep000". + DOUBLE_CONVERSION_ASSERT(digits_after_point > 0); + result_builder->AddSubstring(decimal_digits, decimal_point); + result_builder->AddCharacter('.'); + DOUBLE_CONVERSION_ASSERT(length - decimal_point <= digits_after_point); + result_builder->AddSubstring(&decimal_digits[decimal_point], + length - decimal_point); + int remaining_digits = digits_after_point - (length - decimal_point); + result_builder->AddPadding('0', remaining_digits); + } + if (digits_after_point == 0) { + if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { + result_builder->AddCharacter('.'); + } + if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { + result_builder->AddCharacter('0'); + } + } +} + + +bool DoubleToStringConverter::ToShortestIeeeNumber( + double value, + StringBuilder* result_builder, + DoubleToStringConverter::DtoaMode mode) const { + DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE); + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + int decimal_point; + bool sign; + const int kDecimalRepCapacity = kBase10MaximalLength + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + if ((decimal_in_shortest_low_ <= exponent) && + (exponent < decimal_in_shortest_high_)) { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, + decimal_point, + (std::max)(0, decimal_rep_length - decimal_point), + result_builder); + } else { + CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, + result_builder); + } + return true; +} + + +bool DoubleToStringConverter::ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const { + DOUBLE_CONVERSION_ASSERT(kMaxFixedDigitsBeforePoint == 60); + const double kFirstNonFixed = 1e60; + + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits > kMaxFixedDigitsAfterPoint) return false; + if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false; + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add space for the '\0' byte. + const int kDecimalRepCapacity = + kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + DoubleToAscii(value, FIXED, requested_digits, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + requested_digits, result_builder); + return true; +} + + +bool DoubleToStringConverter::ToExponential( + double value, + int requested_digits, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits < -1) return false; + if (requested_digits > kMaxExponentialDigits) return false; + + int decimal_point; + bool sign; + // Add space for digit before the decimal point and the '\0' character. + const int kDecimalRepCapacity = kMaxExponentialDigits + 2; + DOUBLE_CONVERSION_ASSERT(kDecimalRepCapacity > kBase10MaximalLength); + char decimal_rep[kDecimalRepCapacity]; +#ifndef NDEBUG + // Problem: there is an assert in StringBuilder::AddSubstring() that + // will pass this buffer to strlen(), and this buffer is not generally + // null-terminated. + memset(decimal_rep, 0, sizeof(decimal_rep)); +#endif + int decimal_rep_length; + + if (requested_digits == -1) { + DoubleToAscii(value, SHORTEST, 0, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + } else { + DoubleToAscii(value, PRECISION, requested_digits + 1, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= requested_digits + 1); + + for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { + decimal_rep[i] = '0'; + } + decimal_rep_length = requested_digits + 1; + } + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + CreateExponentialRepresentation(decimal_rep, + decimal_rep_length, + exponent, + result_builder); + return true; +} + + +bool DoubleToStringConverter::ToPrecision(double value, + int precision, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { + return false; + } + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add one for the terminating null character. + const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, PRECISION, precision, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= precision); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + // The exponent if we print the number as x.xxeyyy. That is with the + // decimal point after the first digit. + int exponent = decimal_point - 1; + + int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; + bool as_exponential = + (-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || + (decimal_point - precision + extra_zero > + max_trailing_padding_zeroes_in_precision_mode_); + if ((flags_ & NO_TRAILING_ZERO) != 0) { + // Truncate trailing zeros that occur after the decimal point (if exponential, + // that is everything after the first digit). + int stop = as_exponential ? 1 : std::max(1, decimal_point); + while (decimal_rep_length > stop && decimal_rep[decimal_rep_length - 1] == '0') { + --decimal_rep_length; + } + // Clamp precision to avoid the code below re-adding the zeros. + precision = std::min(precision, decimal_rep_length); + } + if (as_exponential) { + // Fill buffer to contain 'precision' digits. + // Usually the buffer is already at the correct length, but 'DoubleToAscii' + // is allowed to return less characters. + for (int i = decimal_rep_length; i < precision; ++i) { + decimal_rep[i] = '0'; + } + + CreateExponentialRepresentation(decimal_rep, + precision, + exponent, + result_builder); + } else { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + (std::max)(0, precision - decimal_point), + result_builder); + } + return true; +} +#endif // not needed for ICU + + +static BignumDtoaMode DtoaToBignumDtoaMode( + DoubleToStringConverter::DtoaMode dtoa_mode) { + switch (dtoa_mode) { + case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST; + case DoubleToStringConverter::SHORTEST_SINGLE: + return BIGNUM_DTOA_SHORTEST_SINGLE; + case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED; + case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION; + default: + DOUBLE_CONVERSION_UNREACHABLE(); + } +} + + +void DoubleToStringConverter::DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point) { + Vector vector(buffer, buffer_length); + DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial()); + DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0); + + if (Double(v).Sign() < 0) { + *sign = true; + v = -v; + } else { + *sign = false; + } + + if (mode == PRECISION && requested_digits == 0) { + vector[0] = '\0'; + *length = 0; + return; + } + + if (v == 0) { + vector[0] = '0'; + vector[1] = '\0'; + *length = 1; + *point = 1; + return; + } + + bool fast_worked; + switch (mode) { + case SHORTEST: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); + break; +#if 0 // not needed for ICU + case SHORTEST_SINGLE: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0, + vector, length, point); + break; + case FIXED: + fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); + break; + case PRECISION: + fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, + vector, length, point); + break; +#endif // not needed for ICU + default: + fast_worked = false; + DOUBLE_CONVERSION_UNREACHABLE(); + } + if (fast_worked) return; + + // If the fast dtoa didn't succeed use the slower bignum version. + BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); + BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); + vector[*length] = '\0'; +} + +} // 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-double-to-string.h b/deps/icu-small/source/i18n/double-conversion-double-to-string.h index 1fae2e87715a45..b2bd8d11819310 100644 --- a/deps/icu-small/source/i18n/double-conversion-double-to-string.h +++ b/deps/icu-small/source/i18n/double-conversion-double-to-string.h @@ -1,468 +1,468 @@ -// © 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 2012 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_DOUBLE_TO_STRING_H_ -#define DOUBLE_CONVERSION_DOUBLE_TO_STRING_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 { - -class DoubleToStringConverter { - public: - // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint - // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the - // function returns false. - static const int kMaxFixedDigitsBeforePoint = 60; - static const int kMaxFixedDigitsAfterPoint = 100; - - // When calling ToExponential with a requested_digits - // parameter > kMaxExponentialDigits then the function returns false. - static const int kMaxExponentialDigits = 120; - - // When calling ToPrecision with a requested_digits - // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits - // then the function returns false. - static const int kMinPrecisionDigits = 1; - static const int kMaxPrecisionDigits = 120; - - // The maximal number of digits that are needed to emit a double in base 10. - // A higher precision can be achieved by using more digits, but the shortest - // accurate representation of any double will never use more digits than - // kBase10MaximalLength. - // Note that DoubleToAscii null-terminates its input. So the given buffer - // should be at least kBase10MaximalLength + 1 characters long. - static const int kBase10MaximalLength = 17; - - // The maximal number of digits that are needed to emit a single in base 10. - // A higher precision can be achieved by using more digits, but the shortest - // accurate representation of any single will never use more digits than - // kBase10MaximalLengthSingle. - static const int kBase10MaximalLengthSingle = 9; - - // The length of the longest string that 'ToShortest' can produce when the - // converter is instantiated with EcmaScript defaults (see - // 'EcmaScriptConverter') - // This value does not include the trailing '\0' character. - // This amount of characters is needed for negative values that hit the - // 'decimal_in_shortest_low' limit. For example: "-0.0000033333333333333333" - static const int kMaxCharsEcmaScriptShortest = 25; - -#if 0 // not needed for ICU - enum Flags { - NO_FLAGS = 0, - EMIT_POSITIVE_EXPONENT_SIGN = 1, - EMIT_TRAILING_DECIMAL_POINT = 2, - EMIT_TRAILING_ZERO_AFTER_POINT = 4, - UNIQUE_ZERO = 8, - NO_TRAILING_ZERO = 16 - }; - - // Flags should be a bit-or combination of the possible Flags-enum. - // - NO_FLAGS: no special flags. - // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent - // form, emits a '+' for positive exponents. Example: 1.2e+2. - // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is - // converted into decimal format then a trailing decimal point is appended. - // Example: 2345.0 is converted to "2345.". - // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point - // emits a trailing '0'-character. This flag requires the - // EMIT_TRAILING_DECIMAL_POINT flag. - // Example: 2345.0 is converted to "2345.0". - // - UNIQUE_ZERO: "-0.0" is converted to "0.0". - // - NO_TRAILING_ZERO: Trailing zeros are removed from the fractional portion - // of the result in precision mode. Matches printf's %g. - // When EMIT_TRAILING_ZERO_AFTER_POINT is also given, one trailing zero is - // preserved. - // - // Infinity symbol and nan_symbol provide the string representation for these - // special values. If the string is NULL and the special value is encountered - // then the conversion functions return false. - // - // The exponent_character is used in exponential representations. It is - // usually 'e' or 'E'. - // - // When converting to the shortest representation the converter will - // represent input numbers in decimal format if they are in the interval - // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[ - // (lower boundary included, greater boundary excluded). - // Example: with decimal_in_shortest_low = -6 and - // decimal_in_shortest_high = 21: - // ToShortest(0.000001) -> "0.000001" - // ToShortest(0.0000001) -> "1e-7" - // ToShortest(111111111111111111111.0) -> "111111111111111110000" - // ToShortest(100000000000000000000.0) -> "100000000000000000000" - // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" - // - // When converting to precision mode the converter may add - // max_leading_padding_zeroes before returning the number in exponential - // format. - // Example with max_leading_padding_zeroes_in_precision_mode = 6. - // ToPrecision(0.0000012345, 2) -> "0.0000012" - // ToPrecision(0.00000012345, 2) -> "1.2e-7" - // Similarly the converter may add up to - // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid - // returning an exponential representation. A zero added by the - // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. - // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: - // ToPrecision(230.0, 2) -> "230" - // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. - // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. - // - // The min_exponent_width is used for exponential representations. - // The converter adds leading '0's to the exponent until the exponent - // is at least min_exponent_width digits long. - // The min_exponent_width is clamped to 5. - // As such, the exponent may never have more than 5 digits in total. - DoubleToStringConverter(int flags, - const char* infinity_symbol, - const char* nan_symbol, - char exponent_character, - int decimal_in_shortest_low, - int decimal_in_shortest_high, - int max_leading_padding_zeroes_in_precision_mode, - int max_trailing_padding_zeroes_in_precision_mode, - int min_exponent_width = 0) - : flags_(flags), - infinity_symbol_(infinity_symbol), - nan_symbol_(nan_symbol), - exponent_character_(exponent_character), - decimal_in_shortest_low_(decimal_in_shortest_low), - decimal_in_shortest_high_(decimal_in_shortest_high), - max_leading_padding_zeroes_in_precision_mode_( - max_leading_padding_zeroes_in_precision_mode), - max_trailing_padding_zeroes_in_precision_mode_( - max_trailing_padding_zeroes_in_precision_mode), - min_exponent_width_(min_exponent_width) { - // When 'trailing zero after the point' is set, then 'trailing point' - // must be set too. - DOUBLE_CONVERSION_ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || - !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); - } - - // Returns a converter following the EcmaScript specification. - // - // Flags: UNIQUE_ZERO and EMIT_POSITIVE_EXPONENT_SIGN. - // Special values: "Infinity" and "NaN". - // Lower case 'e' for exponential values. - // decimal_in_shortest_low: -6 - // decimal_in_shortest_high: 21 - // max_leading_padding_zeroes_in_precision_mode: 6 - // max_trailing_padding_zeroes_in_precision_mode: 0 - static const DoubleToStringConverter& EcmaScriptConverter(); - - // Computes the shortest string of digits that correctly represent the input - // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high - // (see constructor) it then either returns a decimal representation, or an - // exponential representation. - // Example with decimal_in_shortest_low = -6, - // decimal_in_shortest_high = 21, - // EMIT_POSITIVE_EXPONENT_SIGN activated, and - // EMIT_TRAILING_DECIMAL_POINT deactivated: - // ToShortest(0.000001) -> "0.000001" - // ToShortest(0.0000001) -> "1e-7" - // ToShortest(111111111111111111111.0) -> "111111111111111110000" - // ToShortest(100000000000000000000.0) -> "100000000000000000000" - // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" - // - // Note: the conversion may round the output if the returned string - // is accurate enough to uniquely identify the input-number. - // For example the most precise representation of the double 9e59 equals - // "899999999999999918767229449717619953810131273674690656206848", but - // the converter will return the shorter (but still correct) "9e59". - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except when the input value is special and no infinity_symbol or - // nan_symbol has been given to the constructor. - // - // The length of the longest result is the maximum of the length of the - // following string representations (each with possible examples): - // - NaN and negative infinity: "NaN", "-Infinity", "-inf". - // - -10^(decimal_in_shortest_high - 1): - // "-100000000000000000000", "-1000000000000000.0" - // - the longest string in range [0; -10^decimal_in_shortest_low]. Generally, - // this string is 3 + kBase10MaximalLength - decimal_in_shortest_low. - // (Sign, '0', decimal point, padding zeroes for decimal_in_shortest_low, - // and the significant digits). - // "-0.0000033333333333333333", "-0.0012345678901234567" - // - the longest exponential representation. (A negative number with - // kBase10MaximalLength significant digits). - // "-1.7976931348623157e+308", "-1.7976931348623157E308" - // In addition, the buffer must be able to hold the trailing '\0' character. - bool ToShortest(double value, StringBuilder* result_builder) const { - return ToShortestIeeeNumber(value, result_builder, SHORTEST); - } - - // Same as ToShortest, but for single-precision floats. - bool ToShortestSingle(float value, StringBuilder* result_builder) const { - return ToShortestIeeeNumber(value, result_builder, SHORTEST_SINGLE); - } - - - // Computes a decimal representation with a fixed number of digits after the - // decimal point. The last emitted digit is rounded. - // - // Examples: - // ToFixed(3.12, 1) -> "3.1" - // ToFixed(3.1415, 3) -> "3.142" - // ToFixed(1234.56789, 4) -> "1234.5679" - // ToFixed(1.23, 5) -> "1.23000" - // ToFixed(0.1, 4) -> "0.1000" - // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00" - // ToFixed(0.1, 30) -> "0.100000000000000005551115123126" - // ToFixed(0.1, 17) -> "0.10000000000000001" - // - // If requested_digits equals 0, then the tail of the result depends on - // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT. - // Examples, for requested_digits == 0, - // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be - // - false and false: then 123.45 -> 123 - // 0.678 -> 1 - // - true and false: then 123.45 -> 123. - // 0.678 -> 1. - // - true and true: then 123.45 -> 123.0 - // 0.678 -> 1.0 - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except for the following cases: - // - the input value is special and no infinity_symbol or nan_symbol has - // been provided to the constructor, - // - 'value' > 10^kMaxFixedDigitsBeforePoint, or - // - 'requested_digits' > kMaxFixedDigitsAfterPoint. - // The last two conditions imply that the result for non-special values never - // contains more than - // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters - // (one additional character for the sign, and one for the decimal point). - // In addition, the buffer must be able to hold the trailing '\0' character. - bool ToFixed(double value, - int requested_digits, - StringBuilder* result_builder) const; - - // Computes a representation in exponential format with requested_digits - // after the decimal point. The last emitted digit is rounded. - // If requested_digits equals -1, then the shortest exponential representation - // is computed. - // - // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and - // exponent_character set to 'e'. - // ToExponential(3.12, 1) -> "3.1e0" - // ToExponential(5.0, 3) -> "5.000e0" - // ToExponential(0.001, 2) -> "1.00e-3" - // ToExponential(3.1415, -1) -> "3.1415e0" - // ToExponential(3.1415, 4) -> "3.1415e0" - // ToExponential(3.1415, 3) -> "3.142e0" - // ToExponential(123456789000000, 3) -> "1.235e14" - // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30" - // ToExponential(1000000000000000019884624838656.0, 32) -> - // "1.00000000000000001988462483865600e30" - // ToExponential(1234, 0) -> "1e3" - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except for the following cases: - // - the input value is special and no infinity_symbol or nan_symbol has - // been provided to the constructor, - // - 'requested_digits' > kMaxExponentialDigits. - // - // The last condition implies that the result never contains more than - // kMaxExponentialDigits + 8 characters (the sign, the digit before the - // decimal point, the decimal point, the exponent character, the - // exponent's sign, and at most 3 exponent digits). - // In addition, the buffer must be able to hold the trailing '\0' character. - bool ToExponential(double value, - int requested_digits, - StringBuilder* result_builder) const; - - - // Computes 'precision' leading digits of the given 'value' and returns them - // either in exponential or decimal format, depending on - // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the - // constructor). - // The last computed digit is rounded. - // - // Example with max_leading_padding_zeroes_in_precision_mode = 6. - // ToPrecision(0.0000012345, 2) -> "0.0000012" - // ToPrecision(0.00000012345, 2) -> "1.2e-7" - // Similarly the converter may add up to - // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid - // returning an exponential representation. A zero added by the - // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. - // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: - // ToPrecision(230.0, 2) -> "230" - // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. - // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. - // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no - // EMIT_TRAILING_ZERO_AFTER_POINT: - // ToPrecision(123450.0, 6) -> "123450" - // ToPrecision(123450.0, 5) -> "123450" - // ToPrecision(123450.0, 4) -> "123500" - // ToPrecision(123450.0, 3) -> "123000" - // ToPrecision(123450.0, 2) -> "1.2e5" - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except for the following cases: - // - the input value is special and no infinity_symbol or nan_symbol has - // been provided to the constructor, - // - precision < kMinPericisionDigits - // - precision > kMaxPrecisionDigits - // - // The last condition implies that the result never contains more than - // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the - // exponent character, the exponent's sign, and at most 3 exponent digits). - // In addition, the buffer must be able to hold the trailing '\0' character. - bool ToPrecision(double value, - int precision, - StringBuilder* result_builder) const; -#endif // not needed for ICU - - enum DtoaMode { - // Produce the shortest correct representation. - // For example the output of 0.299999999999999988897 is (the less accurate - // but correct) 0.3. - SHORTEST, - // Same as SHORTEST, but for single-precision floats. - SHORTEST_SINGLE, - // Produce a fixed number of digits after the decimal point. - // For instance fixed(0.1, 4) becomes 0.1000 - // If the input number is big, the output will be big. - FIXED, - // Fixed number of digits (independent of the decimal point). - PRECISION - }; - - // Converts the given double 'v' to digit characters. 'v' must not be NaN, - // +Infinity, or -Infinity. In SHORTEST_SINGLE-mode this restriction also - // applies to 'v' after it has been casted to a single-precision float. That - // is, in this mode static_cast(v) must not be NaN, +Infinity or - // -Infinity. - // - // The result should be interpreted as buffer * 10^(point-length). - // - // The digits are written to the buffer in the platform's charset, which is - // often UTF-8 (with ASCII-range digits) but may be another charset, such - // as EBCDIC. - // - // The output depends on the given mode: - // - SHORTEST: produce the least amount of digits for which the internal - // identity requirement is still satisfied. If the digits are printed - // (together with the correct exponent) then reading this number will give - // 'v' again. The buffer will choose the representation that is closest to - // 'v'. If there are two at the same distance, than the one farther away - // from 0 is chosen (halfway cases - ending with 5 - are rounded up). - // In this mode the 'requested_digits' parameter is ignored. - // - SHORTEST_SINGLE: same as SHORTEST but with single-precision. - // - FIXED: produces digits necessary to print a given number with - // 'requested_digits' digits after the decimal point. The produced digits - // might be too short in which case the caller has to fill the remainder - // with '0's. - // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. - // Halfway cases are rounded towards +/-Infinity (away from 0). The call - // toFixed(0.15, 2) thus returns buffer="2", point=0. - // The returned buffer may contain digits that would be truncated from the - // shortest representation of the input. - // - PRECISION: produces 'requested_digits' where the first digit is not '0'. - // Even though the length of produced digits usually equals - // 'requested_digits', the function is allowed to return fewer digits, in - // which case the caller has to fill the missing digits with '0's. - // Halfway cases are again rounded away from 0. - // DoubleToAscii expects the given buffer to be big enough to hold all - // digits and a terminating null-character. In SHORTEST-mode it expects a - // buffer of at least kBase10MaximalLength + 1. In all other modes the - // requested_digits parameter and the padding-zeroes limit the size of the - // output. Don't forget the decimal point, the exponent character and the - // terminating null-character when computing the maximal output size. - // The given length is only used in debug mode to ensure the buffer is big - // enough. - // ICU PATCH: Export this as U_I18N_API for unit tests. - static void U_I18N_API DoubleToAscii(double v, - DtoaMode mode, - int requested_digits, - char* buffer, - int buffer_length, - bool* sign, - int* length, - int* point); - -#if 0 // not needed for ICU - private: - // Implementation for ToShortest and ToShortestSingle. - bool ToShortestIeeeNumber(double value, - StringBuilder* result_builder, - DtoaMode mode) const; - - // If the value is a special value (NaN or Infinity) constructs the - // corresponding string using the configured infinity/nan-symbol. - // If either of them is NULL or the value is not special then the - // function returns false. - bool HandleSpecialValues(double value, StringBuilder* result_builder) const; - // Constructs an exponential representation (i.e. 1.234e56). - // The given exponent assumes a decimal point after the first decimal digit. - void CreateExponentialRepresentation(const char* decimal_digits, - int length, - int exponent, - StringBuilder* result_builder) const; - // Creates a decimal representation (i.e 1234.5678). - void CreateDecimalRepresentation(const char* decimal_digits, - int length, - int decimal_point, - int digits_after_point, - StringBuilder* result_builder) const; - - const int flags_; - const char* const infinity_symbol_; - const char* const nan_symbol_; - const char exponent_character_; - const int decimal_in_shortest_low_; - const int decimal_in_shortest_high_; - const int max_leading_padding_zeroes_in_precision_mode_; - const int max_trailing_padding_zeroes_in_precision_mode_; - const int min_exponent_width_; -#endif // not needed for ICU - - DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); -}; - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END - -#endif // DOUBLE_CONVERSION_DOUBLE_TO_STRING_H_ -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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 2012 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_DOUBLE_TO_STRING_H_ +#define DOUBLE_CONVERSION_DOUBLE_TO_STRING_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 { + +class DoubleToStringConverter { + public: + // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint + // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the + // function returns false. + static const int kMaxFixedDigitsBeforePoint = 60; + static const int kMaxFixedDigitsAfterPoint = 100; + + // When calling ToExponential with a requested_digits + // parameter > kMaxExponentialDigits then the function returns false. + static const int kMaxExponentialDigits = 120; + + // When calling ToPrecision with a requested_digits + // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits + // then the function returns false. + static const int kMinPrecisionDigits = 1; + static const int kMaxPrecisionDigits = 120; + + // The maximal number of digits that are needed to emit a double in base 10. + // A higher precision can be achieved by using more digits, but the shortest + // accurate representation of any double will never use more digits than + // kBase10MaximalLength. + // Note that DoubleToAscii null-terminates its input. So the given buffer + // should be at least kBase10MaximalLength + 1 characters long. + static const int kBase10MaximalLength = 17; + + // The maximal number of digits that are needed to emit a single in base 10. + // A higher precision can be achieved by using more digits, but the shortest + // accurate representation of any single will never use more digits than + // kBase10MaximalLengthSingle. + static const int kBase10MaximalLengthSingle = 9; + + // The length of the longest string that 'ToShortest' can produce when the + // converter is instantiated with EcmaScript defaults (see + // 'EcmaScriptConverter') + // This value does not include the trailing '\0' character. + // This amount of characters is needed for negative values that hit the + // 'decimal_in_shortest_low' limit. For example: "-0.0000033333333333333333" + static const int kMaxCharsEcmaScriptShortest = 25; + +#if 0 // not needed for ICU + enum Flags { + NO_FLAGS = 0, + EMIT_POSITIVE_EXPONENT_SIGN = 1, + EMIT_TRAILING_DECIMAL_POINT = 2, + EMIT_TRAILING_ZERO_AFTER_POINT = 4, + UNIQUE_ZERO = 8, + NO_TRAILING_ZERO = 16 + }; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent + // form, emits a '+' for positive exponents. Example: 1.2e+2. + // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is + // converted into decimal format then a trailing decimal point is appended. + // Example: 2345.0 is converted to "2345.". + // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point + // emits a trailing '0'-character. This flag requires the + // EMIT_TRAILING_DECIMAL_POINT flag. + // Example: 2345.0 is converted to "2345.0". + // - UNIQUE_ZERO: "-0.0" is converted to "0.0". + // - NO_TRAILING_ZERO: Trailing zeros are removed from the fractional portion + // of the result in precision mode. Matches printf's %g. + // When EMIT_TRAILING_ZERO_AFTER_POINT is also given, one trailing zero is + // preserved. + // + // Infinity symbol and nan_symbol provide the string representation for these + // special values. If the string is nullptr and the special value is encountered + // then the conversion functions return false. + // + // The exponent_character is used in exponential representations. It is + // usually 'e' or 'E'. + // + // When converting to the shortest representation the converter will + // represent input numbers in decimal format if they are in the interval + // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[ + // (lower boundary included, greater boundary excluded). + // Example: with decimal_in_shortest_low = -6 and + // decimal_in_shortest_high = 21: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // When converting to precision mode the converter may add + // max_leading_padding_zeroes before returning the number in exponential + // format. + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarly the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + // + // The min_exponent_width is used for exponential representations. + // The converter adds leading '0's to the exponent until the exponent + // is at least min_exponent_width digits long. + // The min_exponent_width is clamped to 5. + // As such, the exponent may never have more than 5 digits in total. + DoubleToStringConverter(int flags, + const char* infinity_symbol, + const char* nan_symbol, + char exponent_character, + int decimal_in_shortest_low, + int decimal_in_shortest_high, + int max_leading_padding_zeroes_in_precision_mode, + int max_trailing_padding_zeroes_in_precision_mode, + int min_exponent_width = 0) + : flags_(flags), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol), + exponent_character_(exponent_character), + decimal_in_shortest_low_(decimal_in_shortest_low), + decimal_in_shortest_high_(decimal_in_shortest_high), + max_leading_padding_zeroes_in_precision_mode_( + max_leading_padding_zeroes_in_precision_mode), + max_trailing_padding_zeroes_in_precision_mode_( + max_trailing_padding_zeroes_in_precision_mode), + min_exponent_width_(min_exponent_width) { + // When 'trailing zero after the point' is set, then 'trailing point' + // must be set too. + DOUBLE_CONVERSION_ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || + !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); + } + + // Returns a converter following the EcmaScript specification. + // + // Flags: UNIQUE_ZERO and EMIT_POSITIVE_EXPONENT_SIGN. + // Special values: "Infinity" and "NaN". + // Lower case 'e' for exponential values. + // decimal_in_shortest_low: -6 + // decimal_in_shortest_high: 21 + // max_leading_padding_zeroes_in_precision_mode: 6 + // max_trailing_padding_zeroes_in_precision_mode: 0 + static const DoubleToStringConverter& EcmaScriptConverter(); + + // Computes the shortest string of digits that correctly represent the input + // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high + // (see constructor) it then either returns a decimal representation, or an + // exponential representation. + // Example with decimal_in_shortest_low = -6, + // decimal_in_shortest_high = 21, + // EMIT_POSITIVE_EXPONENT_SIGN activated, and + // EMIT_TRAILING_DECIMAL_POINT deactivated: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // Note: the conversion may round the output if the returned string + // is accurate enough to uniquely identify the input-number. + // For example the most precise representation of the double 9e59 equals + // "899999999999999918767229449717619953810131273674690656206848", but + // the converter will return the shorter (but still correct) "9e59". + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except when the input value is special and no infinity_symbol or + // nan_symbol has been given to the constructor. + // + // The length of the longest result is the maximum of the length of the + // following string representations (each with possible examples): + // - NaN and negative infinity: "NaN", "-Infinity", "-inf". + // - -10^(decimal_in_shortest_high - 1): + // "-100000000000000000000", "-1000000000000000.0" + // - the longest string in range [0; -10^decimal_in_shortest_low]. Generally, + // this string is 3 + kBase10MaximalLength - decimal_in_shortest_low. + // (Sign, '0', decimal point, padding zeroes for decimal_in_shortest_low, + // and the significant digits). + // "-0.0000033333333333333333", "-0.0012345678901234567" + // - the longest exponential representation. (A negative number with + // kBase10MaximalLength significant digits). + // "-1.7976931348623157e+308", "-1.7976931348623157E308" + // In addition, the buffer must be able to hold the trailing '\0' character. + bool ToShortest(double value, StringBuilder* result_builder) const { + return ToShortestIeeeNumber(value, result_builder, SHORTEST); + } + + // Same as ToShortest, but for single-precision floats. + bool ToShortestSingle(float value, StringBuilder* result_builder) const { + return ToShortestIeeeNumber(value, result_builder, SHORTEST_SINGLE); + } + + + // Computes a decimal representation with a fixed number of digits after the + // decimal point. The last emitted digit is rounded. + // + // Examples: + // ToFixed(3.12, 1) -> "3.1" + // ToFixed(3.1415, 3) -> "3.142" + // ToFixed(1234.56789, 4) -> "1234.5679" + // ToFixed(1.23, 5) -> "1.23000" + // ToFixed(0.1, 4) -> "0.1000" + // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00" + // ToFixed(0.1, 30) -> "0.100000000000000005551115123126" + // ToFixed(0.1, 17) -> "0.10000000000000001" + // + // If requested_digits equals 0, then the tail of the result depends on + // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples, for requested_digits == 0, + // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be + // - false and false: then 123.45 -> 123 + // 0.678 -> 1 + // - true and false: then 123.45 -> 123. + // 0.678 -> 1. + // - true and true: then 123.45 -> 123.0 + // 0.678 -> 1.0 + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'value' > 10^kMaxFixedDigitsBeforePoint, or + // - 'requested_digits' > kMaxFixedDigitsAfterPoint. + // The last two conditions imply that the result for non-special values never + // contains more than + // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters + // (one additional character for the sign, and one for the decimal point). + // In addition, the buffer must be able to hold the trailing '\0' character. + bool ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const; + + // Computes a representation in exponential format with requested_digits + // after the decimal point. The last emitted digit is rounded. + // If requested_digits equals -1, then the shortest exponential representation + // is computed. + // + // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and + // exponent_character set to 'e'. + // ToExponential(3.12, 1) -> "3.1e0" + // ToExponential(5.0, 3) -> "5.000e0" + // ToExponential(0.001, 2) -> "1.00e-3" + // ToExponential(3.1415, -1) -> "3.1415e0" + // ToExponential(3.1415, 4) -> "3.1415e0" + // ToExponential(3.1415, 3) -> "3.142e0" + // ToExponential(123456789000000, 3) -> "1.235e14" + // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30" + // ToExponential(1000000000000000019884624838656.0, 32) -> + // "1.00000000000000001988462483865600e30" + // ToExponential(1234, 0) -> "1e3" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'requested_digits' > kMaxExponentialDigits. + // + // The last condition implies that the result never contains more than + // kMaxExponentialDigits + 8 characters (the sign, the digit before the + // decimal point, the decimal point, the exponent character, the + // exponent's sign, and at most 3 exponent digits). + // In addition, the buffer must be able to hold the trailing '\0' character. + bool ToExponential(double value, + int requested_digits, + StringBuilder* result_builder) const; + + + // Computes 'precision' leading digits of the given 'value' and returns them + // either in exponential or decimal format, depending on + // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the + // constructor). + // The last computed digit is rounded. + // + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarly the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no + // EMIT_TRAILING_ZERO_AFTER_POINT: + // ToPrecision(123450.0, 6) -> "123450" + // ToPrecision(123450.0, 5) -> "123450" + // ToPrecision(123450.0, 4) -> "123500" + // ToPrecision(123450.0, 3) -> "123000" + // ToPrecision(123450.0, 2) -> "1.2e5" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - precision < kMinPericisionDigits + // - precision > kMaxPrecisionDigits + // + // The last condition implies that the result never contains more than + // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the + // exponent character, the exponent's sign, and at most 3 exponent digits). + // In addition, the buffer must be able to hold the trailing '\0' character. + bool ToPrecision(double value, + int precision, + StringBuilder* result_builder) const; +#endif // not needed for ICU + + enum DtoaMode { + // Produce the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate + // but correct) 0.3. + SHORTEST, + // Same as SHORTEST, but for single-precision floats. + SHORTEST_SINGLE, + // Produce a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + FIXED, + // Fixed number of digits (independent of the decimal point). + PRECISION + }; + + // Converts the given double 'v' to digit characters. 'v' must not be NaN, + // +Infinity, or -Infinity. In SHORTEST_SINGLE-mode this restriction also + // applies to 'v' after it has been casted to a single-precision float. That + // is, in this mode static_cast(v) must not be NaN, +Infinity or + // -Infinity. + // + // The result should be interpreted as buffer * 10^(point-length). + // + // The digits are written to the buffer in the platform's charset, which is + // often UTF-8 (with ASCII-range digits) but may be another charset, such + // as EBCDIC. + // + // The output depends on the given mode: + // - SHORTEST: produce the least amount of digits for which the internal + // identity requirement is still satisfied. If the digits are printed + // (together with the correct exponent) then reading this number will give + // 'v' again. The buffer will choose the representation that is closest to + // 'v'. If there are two at the same distance, than the one farther away + // from 0 is chosen (halfway cases - ending with 5 - are rounded up). + // In this mode the 'requested_digits' parameter is ignored. + // - SHORTEST_SINGLE: same as SHORTEST but with single-precision. + // - FIXED: produces digits necessary to print a given number with + // 'requested_digits' digits after the decimal point. The produced digits + // might be too short in which case the caller has to fill the remainder + // with '0's. + // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. + // Halfway cases are rounded towards +/-Infinity (away from 0). The call + // toFixed(0.15, 2) thus returns buffer="2", point=0. + // The returned buffer may contain digits that would be truncated from the + // shortest representation of the input. + // - PRECISION: produces 'requested_digits' where the first digit is not '0'. + // Even though the length of produced digits usually equals + // 'requested_digits', the function is allowed to return fewer digits, in + // which case the caller has to fill the missing digits with '0's. + // Halfway cases are again rounded away from 0. + // DoubleToAscii expects the given buffer to be big enough to hold all + // digits and a terminating null-character. In SHORTEST-mode it expects a + // buffer of at least kBase10MaximalLength + 1. In all other modes the + // requested_digits parameter and the padding-zeroes limit the size of the + // output. Don't forget the decimal point, the exponent character and the + // terminating null-character when computing the maximal output size. + // The given length is only used in debug mode to ensure the buffer is big + // enough. + // ICU PATCH: Export this as U_I18N_API for unit tests. + static void U_I18N_API DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point); + +#if 0 // not needed for ICU + private: + // Implementation for ToShortest and ToShortestSingle. + bool ToShortestIeeeNumber(double value, + StringBuilder* result_builder, + DtoaMode mode) const; + + // If the value is a special value (NaN or Infinity) constructs the + // corresponding string using the configured infinity/nan-symbol. + // If either of them is nullptr or the value is not special then the + // function returns false. + bool HandleSpecialValues(double value, StringBuilder* result_builder) const; + // Constructs an exponential representation (i.e. 1.234e56). + // The given exponent assumes a decimal point after the first decimal digit. + void CreateExponentialRepresentation(const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const; + // Creates a decimal representation (i.e 1234.5678). + void CreateDecimalRepresentation(const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const; + + const int flags_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + const char exponent_character_; + const int decimal_in_shortest_low_; + const int decimal_in_shortest_high_; + const int max_leading_padding_zeroes_in_precision_mode_; + const int max_trailing_padding_zeroes_in_precision_mode_; + const int min_exponent_width_; +#endif // not needed for ICU + + DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); +}; + +} // namespace double_conversion + +// ICU PATCH: Close ICU namespace +U_NAMESPACE_END + +#endif // DOUBLE_CONVERSION_DOUBLE_TO_STRING_H_ +#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/double-conversion-fast-dtoa.cpp b/deps/icu-small/source/i18n/double-conversion-fast-dtoa.cpp index 06e4cf12559da0..6d4ca3e11d3493 100644 --- a/deps/icu-small/source/i18n/double-conversion-fast-dtoa.cpp +++ b/deps/icu-small/source/i18n/double-conversion-fast-dtoa.cpp @@ -1,683 +1,683 @@ -// © 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 2012 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 - -// ICU PATCH: Customize header file paths for ICU. - -#include "double-conversion-fast-dtoa.h" - -#include "double-conversion-cached-powers.h" -#include "double-conversion-diy-fp.h" -#include "double-conversion-ieee.h" - -// ICU PATCH: Wrap in ICU namespace -U_NAMESPACE_BEGIN - -namespace double_conversion { - -// The minimal and maximal target exponent define the range of w's binary -// exponent, where 'w' is the result of multiplying the input by a cached power -// of ten. -// -// A different range might be chosen on a different platform, to optimize digit -// generation, but a smaller range requires more powers of ten to be cached. -static const int kMinimalTargetExponent = -60; -static const int kMaximalTargetExponent = -32; - - -// Adjusts the last digit of the generated number, and screens out generated -// solutions that may be inaccurate. A solution may be inaccurate if it is -// outside the safe interval, or if we cannot prove that it is closer to the -// input than a neighboring representation of the same length. -// -// Input: * buffer containing the digits of too_high / 10^kappa -// * the buffer's length -// * distance_too_high_w == (too_high - w).f() * unit -// * unsafe_interval == (too_high - too_low).f() * unit -// * rest = (too_high - buffer * 10^kappa).f() * unit -// * ten_kappa = 10^kappa * unit -// * unit = the common multiplier -// Output: returns true if the buffer is guaranteed to contain the closest -// representable number to the input. -// Modifies the generated digits in the buffer to approach (round towards) w. -static bool RoundWeed(Vector buffer, - int length, - uint64_t distance_too_high_w, - uint64_t unsafe_interval, - uint64_t rest, - uint64_t ten_kappa, - uint64_t unit) { - uint64_t small_distance = distance_too_high_w - unit; - uint64_t big_distance = distance_too_high_w + unit; - // Let w_low = too_high - big_distance, and - // w_high = too_high - small_distance. - // Note: w_low < w < w_high - // - // The real w (* unit) must lie somewhere inside the interval - // ]w_low; w_high[ (often written as "(w_low; w_high)") - - // Basically the buffer currently contains a number in the unsafe interval - // ]too_low; too_high[ with too_low < w < too_high - // - // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ^v 1 unit ^ ^ ^ ^ - // boundary_high --------------------- . . . . - // ^v 1 unit . . . . - // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . . - // . . ^ . . - // . big_distance . . . - // . . . . rest - // small_distance . . . . - // v . . . . - // w_high - - - - - - - - - - - - - - - - - - . . . . - // ^v 1 unit . . . . - // w ---------------------------------------- . . . . - // ^v 1 unit v . . . - // w_low - - - - - - - - - - - - - - - - - - - - - . . . - // . . v - // buffer --------------------------------------------------+-------+-------- - // . . - // safe_interval . - // v . - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . - // ^v 1 unit . - // boundary_low ------------------------- unsafe_interval - // ^v 1 unit v - // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - // - // Note that the value of buffer could lie anywhere inside the range too_low - // to too_high. - // - // boundary_low, boundary_high and w are approximations of the real boundaries - // and v (the input number). They are guaranteed to be precise up to one unit. - // In fact the error is guaranteed to be strictly less than one unit. - // - // Anything that lies outside the unsafe interval is guaranteed not to round - // to v when read again. - // Anything that lies inside the safe interval is guaranteed to round to v - // when read again. - // If the number inside the buffer lies inside the unsafe interval but not - // inside the safe interval then we simply do not know and bail out (returning - // false). - // - // Similarly we have to take into account the imprecision of 'w' when finding - // the closest representation of 'w'. If we have two potential - // representations, and one is closer to both w_low and w_high, then we know - // it is closer to the actual value v. - // - // By generating the digits of too_high we got the largest (closest to - // too_high) buffer that is still in the unsafe interval. In the case where - // w_high < buffer < too_high we try to decrement the buffer. - // This way the buffer approaches (rounds towards) w. - // There are 3 conditions that stop the decrementation process: - // 1) the buffer is already below w_high - // 2) decrementing the buffer would make it leave the unsafe interval - // 3) decrementing the buffer would yield a number below w_high and farther - // away than the current number. In other words: - // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high - // Instead of using the buffer directly we use its distance to too_high. - // Conceptually rest ~= too_high - buffer - // We need to do the following tests in this order to avoid over- and - // underflows. - DOUBLE_CONVERSION_ASSERT(rest <= unsafe_interval); - while (rest < small_distance && // Negated condition 1 - unsafe_interval - rest >= ten_kappa && // Negated condition 2 - (rest + ten_kappa < small_distance || // buffer{-1} > w_high - small_distance - rest >= rest + ten_kappa - small_distance)) { - buffer[length - 1]--; - rest += ten_kappa; - } - - // We have approached w+ as much as possible. We now test if approaching w- - // would require changing the buffer. If yes, then we have two possible - // representations close to w, but we cannot decide which one is closer. - if (rest < big_distance && - unsafe_interval - rest >= ten_kappa && - (rest + ten_kappa < big_distance || - big_distance - rest > rest + ten_kappa - big_distance)) { - return false; - } - - // Weeding test. - // The safe interval is [too_low + 2 ulp; too_high - 2 ulp] - // Since too_low = too_high - unsafe_interval this is equivalent to - // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp] - // Conceptually we have: rest ~= too_high - buffer - return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit); -} - - -// Rounds the buffer upwards if the result is closer to v by possibly adding -// 1 to the buffer. If the precision of the calculation is not sufficient to -// round correctly, return false. -// The rounding might shift the whole buffer in which case the kappa is -// adjusted. For example "99", kappa = 3 might become "10", kappa = 4. -// -// If 2*rest > ten_kappa then the buffer needs to be round up. -// rest can have an error of +/- 1 unit. This function accounts for the -// imprecision and returns false, if the rounding direction cannot be -// unambiguously determined. -// -// Precondition: rest < ten_kappa. -static bool RoundWeedCounted(Vector buffer, - int length, - uint64_t rest, - uint64_t ten_kappa, - uint64_t unit, - int* kappa) { - DOUBLE_CONVERSION_ASSERT(rest < ten_kappa); - // The following tests are done in a specific order to avoid overflows. They - // will work correctly with any uint64 values of rest < ten_kappa and unit. - // - // If the unit is too big, then we don't know which way to round. For example - // a unit of 50 means that the real number lies within rest +/- 50. If - // 10^kappa == 40 then there is no way to tell which way to round. - if (unit >= ten_kappa) return false; - // Even if unit is just half the size of 10^kappa we are already completely - // lost. (And after the previous test we know that the expression will not - // over/underflow.) - if (ten_kappa - unit <= unit) return false; - // If 2 * (rest + unit) <= 10^kappa we can safely round down. - if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) { - return true; - } - // If 2 * (rest - unit) >= 10^kappa, then we can safely round up. - if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) { - // Increment the last digit recursively until we find a non '9' digit. - buffer[length - 1]++; - for (int i = length - 1; i > 0; --i) { - if (buffer[i] != '0' + 10) break; - buffer[i] = '0'; - buffer[i - 1]++; - } - // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the - // exception of the first digit all digits are now '0'. Simply switch the - // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and - // the power (the kappa) is increased. - if (buffer[0] == '0' + 10) { - buffer[0] = '1'; - (*kappa) += 1; - } - return true; - } - return false; -} - -// Returns the biggest power of ten that is less than or equal to the given -// number. We furthermore receive the maximum number of bits 'number' has. -// -// Returns power == 10^(exponent_plus_one-1) such that -// power <= number < power * 10. -// If number_bits == 0 then 0^(0-1) is returned. -// The number of bits must be <= 32. -// Precondition: number < (1 << (number_bits + 1)). - -// Inspired by the method for finding an integer log base 10 from here: -// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 -static unsigned int const kSmallPowersOfTen[] = - {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, - 1000000000}; - -static void BiggestPowerTen(uint32_t number, - int number_bits, - uint32_t* power, - int* exponent_plus_one) { - DOUBLE_CONVERSION_ASSERT(number < (1u << (number_bits + 1))); - // 1233/4096 is approximately 1/lg(10). - int exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12); - // We increment to skip over the first entry in the kPowersOf10 table. - // Note: kPowersOf10[i] == 10^(i-1). - exponent_plus_one_guess++; - // We don't have any guarantees that 2^number_bits <= number. - if (number < kSmallPowersOfTen[exponent_plus_one_guess]) { - exponent_plus_one_guess--; - } - *power = kSmallPowersOfTen[exponent_plus_one_guess]; - *exponent_plus_one = exponent_plus_one_guess; -} - -// Generates the digits of input number w. -// w is a floating-point number (DiyFp), consisting of a significand and an -// exponent. Its exponent is bounded by kMinimalTargetExponent and -// kMaximalTargetExponent. -// Hence -60 <= w.e() <= -32. -// -// Returns false if it fails, in which case the generated digits in the buffer -// should not be used. -// Preconditions: -// * low, w and high are correct up to 1 ulp (unit in the last place). That -// is, their error must be less than a unit of their last digits. -// * low.e() == w.e() == high.e() -// * low < w < high, and taking into account their error: low~ <= high~ -// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent -// Postconditions: returns false if procedure fails. -// otherwise: -// * buffer is not null-terminated, but len contains the number of digits. -// * buffer contains the shortest possible decimal digit-sequence -// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the -// correct values of low and high (without their error). -// * if more than one decimal representation gives the minimal number of -// decimal digits then the one closest to W (where W is the correct value -// of w) is chosen. -// Remark: this procedure takes into account the imprecision of its input -// numbers. If the precision is not enough to guarantee all the postconditions -// then false is returned. This usually happens rarely (~0.5%). -// -// Say, for the sake of example, that -// w.e() == -48, and w.f() == 0x1234567890abcdef -// w's value can be computed by w.f() * 2^w.e() -// We can obtain w's integral digits by simply shifting w.f() by -w.e(). -// -> w's integral part is 0x1234 -// w's fractional part is therefore 0x567890abcdef. -// Printing w's integral part is easy (simply print 0x1234 in decimal). -// In order to print its fraction we repeatedly multiply the fraction by 10 and -// get each digit. Example the first digit after the point would be computed by -// (0x567890abcdef * 10) >> 48. -> 3 -// The whole thing becomes slightly more complicated because we want to stop -// once we have enough digits. That is, once the digits inside the buffer -// represent 'w' we can stop. Everything inside the interval low - high -// represents w. However we have to pay attention to low, high and w's -// imprecision. -static bool DigitGen(DiyFp low, - DiyFp w, - DiyFp high, - Vector buffer, - int* length, - int* kappa) { - DOUBLE_CONVERSION_ASSERT(low.e() == w.e() && w.e() == high.e()); - DOUBLE_CONVERSION_ASSERT(low.f() + 1 <= high.f() - 1); - DOUBLE_CONVERSION_ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); - // low, w and high are imprecise, but by less than one ulp (unit in the last - // place). - // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that - // the new numbers are outside of the interval we want the final - // representation to lie in. - // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield - // numbers that are certain to lie in the interval. We will use this fact - // later on. - // We will now start by generating the digits within the uncertain - // interval. Later we will weed out representations that lie outside the safe - // interval and thus _might_ lie outside the correct interval. - uint64_t unit = 1; - DiyFp too_low = DiyFp(low.f() - unit, low.e()); - DiyFp too_high = DiyFp(high.f() + unit, high.e()); - // too_low and too_high are guaranteed to lie outside the interval we want the - // generated number in. - DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low); - // We now cut the input number into two parts: the integral digits and the - // fractionals. We will not write any decimal separator though, but adapt - // kappa instead. - // Reminder: we are currently computing the digits (stored inside the buffer) - // such that: too_low < buffer * 10^kappa < too_high - // We use too_high for the digit_generation and stop as soon as possible. - // If we stop early we effectively round down. - DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); - // Division by one is a shift. - uint32_t integrals = static_cast(too_high.f() >> -one.e()); - // Modulo by one is an and. - uint64_t fractionals = too_high.f() & (one.f() - 1); - uint32_t divisor; - int divisor_exponent_plus_one; - BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), - &divisor, &divisor_exponent_plus_one); - *kappa = divisor_exponent_plus_one; - *length = 0; - // Loop invariant: buffer = too_high / 10^kappa (integer division) - // The invariant holds for the first iteration: kappa has been initialized - // with the divisor exponent + 1. And the divisor is the biggest power of ten - // that is smaller than integrals. - while (*kappa > 0) { - int digit = integrals / divisor; - DOUBLE_CONVERSION_ASSERT(digit <= 9); - buffer[*length] = static_cast('0' + digit); - (*length)++; - integrals %= divisor; - (*kappa)--; - // Note that kappa now equals the exponent of the divisor and that the - // invariant thus holds again. - uint64_t rest = - (static_cast(integrals) << -one.e()) + fractionals; - // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e()) - // Reminder: unsafe_interval.e() == one.e() - if (rest < unsafe_interval.f()) { - // Rounding down (by not emitting the remaining digits) yields a number - // that lies within the unsafe interval. - return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(), - unsafe_interval.f(), rest, - static_cast(divisor) << -one.e(), unit); - } - divisor /= 10; - } - - // The integrals have been generated. We are at the point of the decimal - // separator. In the following loop we simply multiply the remaining digits by - // 10 and divide by one. We just need to pay attention to multiply associated - // data (like the interval or 'unit'), too. - // Note that the multiplication by 10 does not overflow, because w.e >= -60 - // and thus one.e >= -60. - DOUBLE_CONVERSION_ASSERT(one.e() >= -60); - DOUBLE_CONVERSION_ASSERT(fractionals < one.f()); - DOUBLE_CONVERSION_ASSERT(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); - for (;;) { - fractionals *= 10; - unit *= 10; - unsafe_interval.set_f(unsafe_interval.f() * 10); - // Integer division by one. - int digit = static_cast(fractionals >> -one.e()); - DOUBLE_CONVERSION_ASSERT(digit <= 9); - buffer[*length] = static_cast('0' + digit); - (*length)++; - fractionals &= one.f() - 1; // Modulo by one. - (*kappa)--; - if (fractionals < unsafe_interval.f()) { - return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit, - unsafe_interval.f(), fractionals, one.f(), unit); - } - } -} - - - -// Generates (at most) requested_digits digits of input number w. -// w is a floating-point number (DiyFp), consisting of a significand and an -// exponent. Its exponent is bounded by kMinimalTargetExponent and -// kMaximalTargetExponent. -// Hence -60 <= w.e() <= -32. -// -// Returns false if it fails, in which case the generated digits in the buffer -// should not be used. -// Preconditions: -// * w is correct up to 1 ulp (unit in the last place). That -// is, its error must be strictly less than a unit of its last digit. -// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent -// -// Postconditions: returns false if procedure fails. -// otherwise: -// * buffer is not null-terminated, but length contains the number of -// digits. -// * the representation in buffer is the most precise representation of -// requested_digits digits. -// * buffer contains at most requested_digits digits of w. If there are less -// than requested_digits digits then some trailing '0's have been removed. -// * kappa is such that -// w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2. -// -// Remark: This procedure takes into account the imprecision of its input -// numbers. If the precision is not enough to guarantee all the postconditions -// then false is returned. This usually happens rarely, but the failure-rate -// increases with higher requested_digits. -static bool DigitGenCounted(DiyFp w, - int requested_digits, - Vector buffer, - int* length, - int* kappa) { - DOUBLE_CONVERSION_ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); - DOUBLE_CONVERSION_ASSERT(kMinimalTargetExponent >= -60); - DOUBLE_CONVERSION_ASSERT(kMaximalTargetExponent <= -32); - // w is assumed to have an error less than 1 unit. Whenever w is scaled we - // also scale its error. - uint64_t w_error = 1; - // We cut the input number into two parts: the integral digits and the - // fractional digits. We don't emit any decimal separator, but adapt kappa - // instead. Example: instead of writing "1.2" we put "12" into the buffer and - // increase kappa by 1. - DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); - // Division by one is a shift. - uint32_t integrals = static_cast(w.f() >> -one.e()); - // Modulo by one is an and. - uint64_t fractionals = w.f() & (one.f() - 1); - uint32_t divisor; - int divisor_exponent_plus_one; - BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), - &divisor, &divisor_exponent_plus_one); - *kappa = divisor_exponent_plus_one; - *length = 0; - - // Loop invariant: buffer = w / 10^kappa (integer division) - // The invariant holds for the first iteration: kappa has been initialized - // with the divisor exponent + 1. And the divisor is the biggest power of ten - // that is smaller than 'integrals'. - while (*kappa > 0) { - int digit = integrals / divisor; - DOUBLE_CONVERSION_ASSERT(digit <= 9); - buffer[*length] = static_cast('0' + digit); - (*length)++; - requested_digits--; - integrals %= divisor; - (*kappa)--; - // Note that kappa now equals the exponent of the divisor and that the - // invariant thus holds again. - if (requested_digits == 0) break; - divisor /= 10; - } - - if (requested_digits == 0) { - uint64_t rest = - (static_cast(integrals) << -one.e()) + fractionals; - return RoundWeedCounted(buffer, *length, rest, - static_cast(divisor) << -one.e(), w_error, - kappa); - } - - // The integrals have been generated. We are at the point of the decimal - // separator. In the following loop we simply multiply the remaining digits by - // 10 and divide by one. We just need to pay attention to multiply associated - // data (the 'unit'), too. - // Note that the multiplication by 10 does not overflow, because w.e >= -60 - // and thus one.e >= -60. - DOUBLE_CONVERSION_ASSERT(one.e() >= -60); - DOUBLE_CONVERSION_ASSERT(fractionals < one.f()); - DOUBLE_CONVERSION_ASSERT(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); - while (requested_digits > 0 && fractionals > w_error) { - fractionals *= 10; - w_error *= 10; - // Integer division by one. - int digit = static_cast(fractionals >> -one.e()); - DOUBLE_CONVERSION_ASSERT(digit <= 9); - buffer[*length] = static_cast('0' + digit); - (*length)++; - requested_digits--; - fractionals &= one.f() - 1; // Modulo by one. - (*kappa)--; - } - if (requested_digits != 0) return false; - return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error, - kappa); -} - - -// Provides a decimal representation of v. -// Returns true if it succeeds, otherwise the result cannot be trusted. -// There will be *length digits inside the buffer (not null-terminated). -// If the function returns true then -// v == (double) (buffer * 10^decimal_exponent). -// The digits in the buffer are the shortest representation possible: no -// 0.09999999999999999 instead of 0.1. The shorter representation will even be -// chosen even if the longer one would be closer to v. -// The last digit will be closest to the actual v. That is, even if several -// digits might correctly yield 'v' when read again, the closest will be -// computed. -static bool Grisu3(double v, - FastDtoaMode mode, - Vector buffer, - int* length, - int* decimal_exponent) { - DiyFp w = Double(v).AsNormalizedDiyFp(); - // boundary_minus and boundary_plus are the boundaries between v and its - // closest floating-point neighbors. Any number strictly between - // boundary_minus and boundary_plus will round to v when convert to a double. - // Grisu3 will never output representations that lie exactly on a boundary. - DiyFp boundary_minus, boundary_plus; - if (mode == FAST_DTOA_SHORTEST) { - Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus); - } else { - DOUBLE_CONVERSION_ASSERT(mode == FAST_DTOA_SHORTEST_SINGLE); - float single_v = static_cast(v); - Single(single_v).NormalizedBoundaries(&boundary_minus, &boundary_plus); - } - DOUBLE_CONVERSION_ASSERT(boundary_plus.e() == w.e()); - DiyFp ten_mk; // Cached power of ten: 10^-k - int mk; // -k - int ten_mk_minimal_binary_exponent = - kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); - int ten_mk_maximal_binary_exponent = - kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); - PowersOfTenCache::GetCachedPowerForBinaryExponentRange( - ten_mk_minimal_binary_exponent, - ten_mk_maximal_binary_exponent, - &ten_mk, &mk); - DOUBLE_CONVERSION_ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + - DiyFp::kSignificandSize) && - (kMaximalTargetExponent >= w.e() + ten_mk.e() + - DiyFp::kSignificandSize)); - // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a - // 64 bit significand and ten_mk is thus only precise up to 64 bits. - - // The DiyFp::Times procedure rounds its result, and ten_mk is approximated - // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now - // off by a small amount. - // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. - // In other words: let f = scaled_w.f() and e = scaled_w.e(), then - // (f-1) * 2^e < w*10^k < (f+1) * 2^e - DiyFp scaled_w = DiyFp::Times(w, ten_mk); - DOUBLE_CONVERSION_ASSERT(scaled_w.e() == - boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize); - // In theory it would be possible to avoid some recomputations by computing - // the difference between w and boundary_minus/plus (a power of 2) and to - // compute scaled_boundary_minus/plus by subtracting/adding from - // scaled_w. However the code becomes much less readable and the speed - // enhancements are not terrific. - DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk); - DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk); - - // DigitGen will generate the digits of scaled_w. Therefore we have - // v == (double) (scaled_w * 10^-mk). - // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an - // integer than it will be updated. For instance if scaled_w == 1.23 then - // the buffer will be filled with "123" and the decimal_exponent will be - // decreased by 2. - int kappa; - bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, - buffer, length, &kappa); - *decimal_exponent = -mk + kappa; - return result; -} - - -// The "counted" version of grisu3 (see above) only generates requested_digits -// number of digits. This version does not generate the shortest representation, -// and with enough requested digits 0.1 will at some point print as 0.9999999... -// Grisu3 is too imprecise for real halfway cases (1.5 will not work) and -// therefore the rounding strategy for halfway cases is irrelevant. -static bool Grisu3Counted(double v, - int requested_digits, - Vector buffer, - int* length, - int* decimal_exponent) { - DiyFp w = Double(v).AsNormalizedDiyFp(); - DiyFp ten_mk; // Cached power of ten: 10^-k - int mk; // -k - int ten_mk_minimal_binary_exponent = - kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); - int ten_mk_maximal_binary_exponent = - kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); - PowersOfTenCache::GetCachedPowerForBinaryExponentRange( - ten_mk_minimal_binary_exponent, - ten_mk_maximal_binary_exponent, - &ten_mk, &mk); - DOUBLE_CONVERSION_ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + - DiyFp::kSignificandSize) && - (kMaximalTargetExponent >= w.e() + ten_mk.e() + - DiyFp::kSignificandSize)); - // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a - // 64 bit significand and ten_mk is thus only precise up to 64 bits. - - // The DiyFp::Times procedure rounds its result, and ten_mk is approximated - // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now - // off by a small amount. - // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. - // In other words: let f = scaled_w.f() and e = scaled_w.e(), then - // (f-1) * 2^e < w*10^k < (f+1) * 2^e - DiyFp scaled_w = DiyFp::Times(w, ten_mk); - - // We now have (double) (scaled_w * 10^-mk). - // DigitGen will generate the first requested_digits digits of scaled_w and - // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It - // will not always be exactly the same since DigitGenCounted only produces a - // limited number of digits.) - int kappa; - bool result = DigitGenCounted(scaled_w, requested_digits, - buffer, length, &kappa); - *decimal_exponent = -mk + kappa; - return result; -} - - -bool FastDtoa(double v, - FastDtoaMode mode, - int requested_digits, - Vector buffer, - int* length, - int* decimal_point) { - DOUBLE_CONVERSION_ASSERT(v > 0); - DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial()); - - bool result = false; - int decimal_exponent = 0; - switch (mode) { - case FAST_DTOA_SHORTEST: - case FAST_DTOA_SHORTEST_SINGLE: - result = Grisu3(v, mode, buffer, length, &decimal_exponent); - break; - case FAST_DTOA_PRECISION: - result = Grisu3Counted(v, requested_digits, - buffer, length, &decimal_exponent); - break; - default: - DOUBLE_CONVERSION_UNREACHABLE(); - } - if (result) { - *decimal_point = *length + decimal_exponent; - buffer[*length] = '\0'; - } - return result; -} - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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 2012 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 + +// ICU PATCH: Customize header file paths for ICU. + +#include "double-conversion-fast-dtoa.h" + +#include "double-conversion-cached-powers.h" +#include "double-conversion-diy-fp.h" +#include "double-conversion-ieee.h" + +// ICU PATCH: Wrap in ICU namespace +U_NAMESPACE_BEGIN + +namespace double_conversion { + +// The minimal and maximal target exponent define the range of w's binary +// exponent, where 'w' is the result of multiplying the input by a cached power +// of ten. +// +// A different range might be chosen on a different platform, to optimize digit +// generation, but a smaller range requires more powers of ten to be cached. +static const int kMinimalTargetExponent = -60; +static const int kMaximalTargetExponent = -32; + + +// Adjusts the last digit of the generated number, and screens out generated +// solutions that may be inaccurate. A solution may be inaccurate if it is +// outside the safe interval, or if we cannot prove that it is closer to the +// input than a neighboring representation of the same length. +// +// Input: * buffer containing the digits of too_high / 10^kappa +// * the buffer's length +// * distance_too_high_w == (too_high - w).f() * unit +// * unsafe_interval == (too_high - too_low).f() * unit +// * rest = (too_high - buffer * 10^kappa).f() * unit +// * ten_kappa = 10^kappa * unit +// * unit = the common multiplier +// Output: returns true if the buffer is guaranteed to contain the closest +// representable number to the input. +// Modifies the generated digits in the buffer to approach (round towards) w. +static bool RoundWeed(Vector buffer, + int length, + uint64_t distance_too_high_w, + uint64_t unsafe_interval, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit) { + uint64_t small_distance = distance_too_high_w - unit; + uint64_t big_distance = distance_too_high_w + unit; + // Let w_low = too_high - big_distance, and + // w_high = too_high - small_distance. + // Note: w_low < w < w_high + // + // The real w (* unit) must lie somewhere inside the interval + // ]w_low; w_high[ (often written as "(w_low; w_high)") + + // Basically the buffer currently contains a number in the unsafe interval + // ]too_low; too_high[ with too_low < w < too_high + // + // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // ^v 1 unit ^ ^ ^ ^ + // boundary_high --------------------- . . . . + // ^v 1 unit . . . . + // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . . + // . . ^ . . + // . big_distance . . . + // . . . . rest + // small_distance . . . . + // v . . . . + // w_high - - - - - - - - - - - - - - - - - - . . . . + // ^v 1 unit . . . . + // w ---------------------------------------- . . . . + // ^v 1 unit v . . . + // w_low - - - - - - - - - - - - - - - - - - - - - . . . + // . . v + // buffer --------------------------------------------------+-------+-------- + // . . + // safe_interval . + // v . + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + // ^v 1 unit . + // boundary_low ------------------------- unsafe_interval + // ^v 1 unit v + // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // + // + // Note that the value of buffer could lie anywhere inside the range too_low + // to too_high. + // + // boundary_low, boundary_high and w are approximations of the real boundaries + // and v (the input number). They are guaranteed to be precise up to one unit. + // In fact the error is guaranteed to be strictly less than one unit. + // + // Anything that lies outside the unsafe interval is guaranteed not to round + // to v when read again. + // Anything that lies inside the safe interval is guaranteed to round to v + // when read again. + // If the number inside the buffer lies inside the unsafe interval but not + // inside the safe interval then we simply do not know and bail out (returning + // false). + // + // Similarly we have to take into account the imprecision of 'w' when finding + // the closest representation of 'w'. If we have two potential + // representations, and one is closer to both w_low and w_high, then we know + // it is closer to the actual value v. + // + // By generating the digits of too_high we got the largest (closest to + // too_high) buffer that is still in the unsafe interval. In the case where + // w_high < buffer < too_high we try to decrement the buffer. + // This way the buffer approaches (rounds towards) w. + // There are 3 conditions that stop the decrementation process: + // 1) the buffer is already below w_high + // 2) decrementing the buffer would make it leave the unsafe interval + // 3) decrementing the buffer would yield a number below w_high and farther + // away than the current number. In other words: + // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high + // Instead of using the buffer directly we use its distance to too_high. + // Conceptually rest ~= too_high - buffer + // We need to do the following tests in this order to avoid over- and + // underflows. + DOUBLE_CONVERSION_ASSERT(rest <= unsafe_interval); + while (rest < small_distance && // Negated condition 1 + unsafe_interval - rest >= ten_kappa && // Negated condition 2 + (rest + ten_kappa < small_distance || // buffer{-1} > w_high + small_distance - rest >= rest + ten_kappa - small_distance)) { + buffer[length - 1]--; + rest += ten_kappa; + } + + // We have approached w+ as much as possible. We now test if approaching w- + // would require changing the buffer. If yes, then we have two possible + // representations close to w, but we cannot decide which one is closer. + if (rest < big_distance && + unsafe_interval - rest >= ten_kappa && + (rest + ten_kappa < big_distance || + big_distance - rest > rest + ten_kappa - big_distance)) { + return false; + } + + // Weeding test. + // The safe interval is [too_low + 2 ulp; too_high - 2 ulp] + // Since too_low = too_high - unsafe_interval this is equivalent to + // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp] + // Conceptually we have: rest ~= too_high - buffer + return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit); +} + + +// Rounds the buffer upwards if the result is closer to v by possibly adding +// 1 to the buffer. If the precision of the calculation is not sufficient to +// round correctly, return false. +// The rounding might shift the whole buffer in which case the kappa is +// adjusted. For example "99", kappa = 3 might become "10", kappa = 4. +// +// If 2*rest > ten_kappa then the buffer needs to be round up. +// rest can have an error of +/- 1 unit. This function accounts for the +// imprecision and returns false, if the rounding direction cannot be +// unambiguously determined. +// +// Precondition: rest < ten_kappa. +static bool RoundWeedCounted(Vector buffer, + int length, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit, + int* kappa) { + DOUBLE_CONVERSION_ASSERT(rest < ten_kappa); + // The following tests are done in a specific order to avoid overflows. They + // will work correctly with any uint64 values of rest < ten_kappa and unit. + // + // If the unit is too big, then we don't know which way to round. For example + // a unit of 50 means that the real number lies within rest +/- 50. If + // 10^kappa == 40 then there is no way to tell which way to round. + if (unit >= ten_kappa) return false; + // Even if unit is just half the size of 10^kappa we are already completely + // lost. (And after the previous test we know that the expression will not + // over/underflow.) + if (ten_kappa - unit <= unit) return false; + // If 2 * (rest + unit) <= 10^kappa we can safely round down. + if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) { + return true; + } + // If 2 * (rest - unit) >= 10^kappa, then we can safely round up. + if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) { + // Increment the last digit recursively until we find a non '9' digit. + buffer[length - 1]++; + for (int i = length - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the + // exception of the first digit all digits are now '0'. Simply switch the + // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and + // the power (the kappa) is increased. + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*kappa) += 1; + } + return true; + } + return false; +} + +// Returns the biggest power of ten that is less than or equal to the given +// number. We furthermore receive the maximum number of bits 'number' has. +// +// Returns power == 10^(exponent_plus_one-1) such that +// power <= number < power * 10. +// If number_bits == 0 then 0^(0-1) is returned. +// The number of bits must be <= 32. +// Precondition: number < (1 << (number_bits + 1)). + +// Inspired by the method for finding an integer log base 10 from here: +// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 +static unsigned int const kSmallPowersOfTen[] = + {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, + 1000000000}; + +static void BiggestPowerTen(uint32_t number, + int number_bits, + uint32_t* power, + int* exponent_plus_one) { + DOUBLE_CONVERSION_ASSERT(number < (1u << (number_bits + 1))); + // 1233/4096 is approximately 1/lg(10). + int exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12); + // We increment to skip over the first entry in the kPowersOf10 table. + // Note: kPowersOf10[i] == 10^(i-1). + exponent_plus_one_guess++; + // We don't have any guarantees that 2^number_bits <= number. + if (number < kSmallPowersOfTen[exponent_plus_one_guess]) { + exponent_plus_one_guess--; + } + *power = kSmallPowersOfTen[exponent_plus_one_guess]; + *exponent_plus_one = exponent_plus_one_guess; +} + +// Generates the digits of input number w. +// w is a floating-point number (DiyFp), consisting of a significand and an +// exponent. Its exponent is bounded by kMinimalTargetExponent and +// kMaximalTargetExponent. +// Hence -60 <= w.e() <= -32. +// +// Returns false if it fails, in which case the generated digits in the buffer +// should not be used. +// Preconditions: +// * low, w and high are correct up to 1 ulp (unit in the last place). That +// is, their error must be less than a unit of their last digits. +// * low.e() == w.e() == high.e() +// * low < w < high, and taking into account their error: low~ <= high~ +// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent +// Postconditions: returns false if procedure fails. +// otherwise: +// * buffer is not null-terminated, but len contains the number of digits. +// * buffer contains the shortest possible decimal digit-sequence +// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the +// correct values of low and high (without their error). +// * if more than one decimal representation gives the minimal number of +// decimal digits then the one closest to W (where W is the correct value +// of w) is chosen. +// Remark: this procedure takes into account the imprecision of its input +// numbers. If the precision is not enough to guarantee all the postconditions +// then false is returned. This usually happens rarely (~0.5%). +// +// Say, for the sake of example, that +// w.e() == -48, and w.f() == 0x1234567890abcdef +// w's value can be computed by w.f() * 2^w.e() +// We can obtain w's integral digits by simply shifting w.f() by -w.e(). +// -> w's integral part is 0x1234 +// w's fractional part is therefore 0x567890abcdef. +// Printing w's integral part is easy (simply print 0x1234 in decimal). +// In order to print its fraction we repeatedly multiply the fraction by 10 and +// get each digit. Example the first digit after the point would be computed by +// (0x567890abcdef * 10) >> 48. -> 3 +// The whole thing becomes slightly more complicated because we want to stop +// once we have enough digits. That is, once the digits inside the buffer +// represent 'w' we can stop. Everything inside the interval low - high +// represents w. However we have to pay attention to low, high and w's +// imprecision. +static bool DigitGen(DiyFp low, + DiyFp w, + DiyFp high, + Vector buffer, + int* length, + int* kappa) { + DOUBLE_CONVERSION_ASSERT(low.e() == w.e() && w.e() == high.e()); + DOUBLE_CONVERSION_ASSERT(low.f() + 1 <= high.f() - 1); + DOUBLE_CONVERSION_ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + // low, w and high are imprecise, but by less than one ulp (unit in the last + // place). + // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that + // the new numbers are outside of the interval we want the final + // representation to lie in. + // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield + // numbers that are certain to lie in the interval. We will use this fact + // later on. + // We will now start by generating the digits within the uncertain + // interval. Later we will weed out representations that lie outside the safe + // interval and thus _might_ lie outside the correct interval. + uint64_t unit = 1; + DiyFp too_low = DiyFp(low.f() - unit, low.e()); + DiyFp too_high = DiyFp(high.f() + unit, high.e()); + // too_low and too_high are guaranteed to lie outside the interval we want the + // generated number in. + DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low); + // We now cut the input number into two parts: the integral digits and the + // fractionals. We will not write any decimal separator though, but adapt + // kappa instead. + // Reminder: we are currently computing the digits (stored inside the buffer) + // such that: too_low < buffer * 10^kappa < too_high + // We use too_high for the digit_generation and stop as soon as possible. + // If we stop early we effectively round down. + DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast(too_high.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = too_high.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent_plus_one; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent_plus_one); + *kappa = divisor_exponent_plus_one; + *length = 0; + // Loop invariant: buffer = too_high / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than integrals. + while (*kappa > 0) { + int digit = integrals / divisor; + DOUBLE_CONVERSION_ASSERT(digit <= 9); + buffer[*length] = static_cast('0' + digit); + (*length)++; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + uint64_t rest = + (static_cast(integrals) << -one.e()) + fractionals; + // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e()) + // Reminder: unsafe_interval.e() == one.e() + if (rest < unsafe_interval.f()) { + // Rounding down (by not emitting the remaining digits) yields a number + // that lies within the unsafe interval. + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(), + unsafe_interval.f(), rest, + static_cast(divisor) << -one.e(), unit); + } + divisor /= 10; + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (like the interval or 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + DOUBLE_CONVERSION_ASSERT(one.e() >= -60); + DOUBLE_CONVERSION_ASSERT(fractionals < one.f()); + DOUBLE_CONVERSION_ASSERT(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + for (;;) { + fractionals *= 10; + unit *= 10; + unsafe_interval.set_f(unsafe_interval.f() * 10); + // Integer division by one. + int digit = static_cast(fractionals >> -one.e()); + DOUBLE_CONVERSION_ASSERT(digit <= 9); + buffer[*length] = static_cast('0' + digit); + (*length)++; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + if (fractionals < unsafe_interval.f()) { + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit, + unsafe_interval.f(), fractionals, one.f(), unit); + } + } +} + + + +// Generates (at most) requested_digits digits of input number w. +// w is a floating-point number (DiyFp), consisting of a significand and an +// exponent. Its exponent is bounded by kMinimalTargetExponent and +// kMaximalTargetExponent. +// Hence -60 <= w.e() <= -32. +// +// Returns false if it fails, in which case the generated digits in the buffer +// should not be used. +// Preconditions: +// * w is correct up to 1 ulp (unit in the last place). That +// is, its error must be strictly less than a unit of its last digit. +// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent +// +// Postconditions: returns false if procedure fails. +// otherwise: +// * buffer is not null-terminated, but length contains the number of +// digits. +// * the representation in buffer is the most precise representation of +// requested_digits digits. +// * buffer contains at most requested_digits digits of w. If there are less +// than requested_digits digits then some trailing '0's have been removed. +// * kappa is such that +// w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2. +// +// Remark: This procedure takes into account the imprecision of its input +// numbers. If the precision is not enough to guarantee all the postconditions +// then false is returned. This usually happens rarely, but the failure-rate +// increases with higher requested_digits. +static bool DigitGenCounted(DiyFp w, + int requested_digits, + Vector buffer, + int* length, + int* kappa) { + DOUBLE_CONVERSION_ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + DOUBLE_CONVERSION_ASSERT(kMinimalTargetExponent >= -60); + DOUBLE_CONVERSION_ASSERT(kMaximalTargetExponent <= -32); + // w is assumed to have an error less than 1 unit. Whenever w is scaled we + // also scale its error. + uint64_t w_error = 1; + // We cut the input number into two parts: the integral digits and the + // fractional digits. We don't emit any decimal separator, but adapt kappa + // instead. Example: instead of writing "1.2" we put "12" into the buffer and + // increase kappa by 1. + DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast(w.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = w.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent_plus_one; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent_plus_one); + *kappa = divisor_exponent_plus_one; + *length = 0; + + // Loop invariant: buffer = w / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than 'integrals'. + while (*kappa > 0) { + int digit = integrals / divisor; + DOUBLE_CONVERSION_ASSERT(digit <= 9); + buffer[*length] = static_cast('0' + digit); + (*length)++; + requested_digits--; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + if (requested_digits == 0) break; + divisor /= 10; + } + + if (requested_digits == 0) { + uint64_t rest = + (static_cast(integrals) << -one.e()) + fractionals; + return RoundWeedCounted(buffer, *length, rest, + static_cast(divisor) << -one.e(), w_error, + kappa); + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (the 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + DOUBLE_CONVERSION_ASSERT(one.e() >= -60); + DOUBLE_CONVERSION_ASSERT(fractionals < one.f()); + DOUBLE_CONVERSION_ASSERT(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + while (requested_digits > 0 && fractionals > w_error) { + fractionals *= 10; + w_error *= 10; + // Integer division by one. + int digit = static_cast(fractionals >> -one.e()); + DOUBLE_CONVERSION_ASSERT(digit <= 9); + buffer[*length] = static_cast('0' + digit); + (*length)++; + requested_digits--; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + } + if (requested_digits != 0) return false; + return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error, + kappa); +} + + +// Provides a decimal representation of v. +// Returns true if it succeeds, otherwise the result cannot be trusted. +// There will be *length digits inside the buffer (not null-terminated). +// If the function returns true then +// v == (double) (buffer * 10^decimal_exponent). +// The digits in the buffer are the shortest representation possible: no +// 0.09999999999999999 instead of 0.1. The shorter representation will even be +// chosen even if the longer one would be closer to v. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the closest will be +// computed. +static bool Grisu3(double v, + FastDtoaMode mode, + Vector buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + // boundary_minus and boundary_plus are the boundaries between v and its + // closest floating-point neighbors. Any number strictly between + // boundary_minus and boundary_plus will round to v when convert to a double. + // Grisu3 will never output representations that lie exactly on a boundary. + DiyFp boundary_minus, boundary_plus; + if (mode == FAST_DTOA_SHORTEST) { + Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + } else { + DOUBLE_CONVERSION_ASSERT(mode == FAST_DTOA_SHORTEST_SINGLE); + float single_v = static_cast(v); + Single(single_v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + } + DOUBLE_CONVERSION_ASSERT(boundary_plus.e() == w.e()); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + DOUBLE_CONVERSION_ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + DOUBLE_CONVERSION_ASSERT(scaled_w.e() == + boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize); + // In theory it would be possible to avoid some recomputations by computing + // the difference between w and boundary_minus/plus (a power of 2) and to + // compute scaled_boundary_minus/plus by subtracting/adding from + // scaled_w. However the code becomes much less readable and the speed + // enhancements are not terrific. + DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk); + DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk); + + // DigitGen will generate the digits of scaled_w. Therefore we have + // v == (double) (scaled_w * 10^-mk). + // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an + // integer than it will be updated. For instance if scaled_w == 1.23 then + // the buffer will be filled with "123" and the decimal_exponent will be + // decreased by 2. + int kappa; + bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + + +// The "counted" version of grisu3 (see above) only generates requested_digits +// number of digits. This version does not generate the shortest representation, +// and with enough requested digits 0.1 will at some point print as 0.9999999... +// Grisu3 is too imprecise for real halfway cases (1.5 will not work) and +// therefore the rounding strategy for halfway cases is irrelevant. +static bool Grisu3Counted(double v, + int requested_digits, + Vector buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + DOUBLE_CONVERSION_ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + + // We now have (double) (scaled_w * 10^-mk). + // DigitGen will generate the first requested_digits digits of scaled_w and + // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It + // will not always be exactly the same since DigitGenCounted only produces a + // limited number of digits.) + int kappa; + bool result = DigitGenCounted(scaled_w, requested_digits, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + + +bool FastDtoa(double v, + FastDtoaMode mode, + int requested_digits, + Vector buffer, + int* length, + int* decimal_point) { + DOUBLE_CONVERSION_ASSERT(v > 0); + DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial()); + + bool result = false; + int decimal_exponent = 0; + switch (mode) { + case FAST_DTOA_SHORTEST: + case FAST_DTOA_SHORTEST_SINGLE: + result = Grisu3(v, mode, buffer, length, &decimal_exponent); + break; + case FAST_DTOA_PRECISION: + result = Grisu3Counted(v, requested_digits, + buffer, length, &decimal_exponent); + break; + default: + DOUBLE_CONVERSION_UNREACHABLE(); + } + if (result) { + *decimal_point = *length + decimal_exponent; + buffer[*length] = '\0'; + } + return result; +} + +} // 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-fast-dtoa.h b/deps/icu-small/source/i18n/double-conversion-fast-dtoa.h index 58a6470052c63c..e845644a3581fb 100644 --- a/deps/icu-small/source/i18n/double-conversion-fast-dtoa.h +++ b/deps/icu-small/source/i18n/double-conversion-fast-dtoa.h @@ -1,106 +1,106 @@ -// © 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_FAST_DTOA_H_ -#define DOUBLE_CONVERSION_FAST_DTOA_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 { - -enum FastDtoaMode { - // Computes the shortest representation of the given input. The returned - // result will be the most accurate number of this length. Longer - // representations might be more accurate. - FAST_DTOA_SHORTEST, - // Same as FAST_DTOA_SHORTEST but for single-precision floats. - FAST_DTOA_SHORTEST_SINGLE, - // Computes a representation where the precision (number of digits) is - // given as input. The precision is independent of the decimal point. - FAST_DTOA_PRECISION -}; - -// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not -// include the terminating '\0' character. -static const int kFastDtoaMaximalLength = 17; -// Same for single-precision numbers. -static const int kFastDtoaMaximalSingleLength = 9; - -// Provides a decimal representation of v. -// The result should be interpreted as buffer * 10^(point - length). -// -// Precondition: -// * v must be a strictly positive finite double. -// -// Returns true if it succeeds, otherwise the result can not be trusted. -// There will be *length digits inside the buffer followed by a null terminator. -// If the function returns true and mode equals -// - FAST_DTOA_SHORTEST, then -// the parameter requested_digits is ignored. -// The result satisfies -// v == (double) (buffer * 10^(point - length)). -// The digits in the buffer are the shortest representation possible. E.g. -// if 0.099999999999 and 0.1 represent the same double then "1" is returned -// with point = 0. -// The last digit will be closest to the actual v. That is, even if several -// digits might correctly yield 'v' when read again, the buffer will contain -// the one closest to v. -// - FAST_DTOA_PRECISION, then -// the buffer contains requested_digits digits. -// the difference v - (buffer * 10^(point-length)) is closest to zero for -// all possible representations of requested_digits digits. -// If there are two values that are equally close, then FastDtoa returns -// false. -// For both modes the buffer must be large enough to hold the result. -bool FastDtoa(double d, - FastDtoaMode mode, - int requested_digits, - Vector buffer, - int* length, - int* decimal_point); - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END - -#endif // DOUBLE_CONVERSION_FAST_DTOA_H_ -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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_FAST_DTOA_H_ +#define DOUBLE_CONVERSION_FAST_DTOA_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 { + +enum FastDtoaMode { + // Computes the shortest representation of the given input. The returned + // result will be the most accurate number of this length. Longer + // representations might be more accurate. + FAST_DTOA_SHORTEST, + // Same as FAST_DTOA_SHORTEST but for single-precision floats. + FAST_DTOA_SHORTEST_SINGLE, + // Computes a representation where the precision (number of digits) is + // given as input. The precision is independent of the decimal point. + FAST_DTOA_PRECISION +}; + +// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not +// include the terminating '\0' character. +static const int kFastDtoaMaximalLength = 17; +// Same for single-precision numbers. +static const int kFastDtoaMaximalSingleLength = 9; + +// Provides a decimal representation of v. +// The result should be interpreted as buffer * 10^(point - length). +// +// Precondition: +// * v must be a strictly positive finite double. +// +// Returns true if it succeeds, otherwise the result can not be trusted. +// There will be *length digits inside the buffer followed by a null terminator. +// If the function returns true and mode equals +// - FAST_DTOA_SHORTEST, then +// the parameter requested_digits is ignored. +// The result satisfies +// v == (double) (buffer * 10^(point - length)). +// The digits in the buffer are the shortest representation possible. E.g. +// if 0.099999999999 and 0.1 represent the same double then "1" is returned +// with point = 0. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the buffer will contain +// the one closest to v. +// - FAST_DTOA_PRECISION, then +// the buffer contains requested_digits digits. +// the difference v - (buffer * 10^(point-length)) is closest to zero for +// all possible representations of requested_digits digits. +// If there are two values that are equally close, then FastDtoa returns +// false. +// For both modes the buffer must be large enough to hold the result. +bool FastDtoa(double d, + FastDtoaMode mode, + int requested_digits, + Vector buffer, + int* length, + int* decimal_point); + +} // namespace double_conversion + +// ICU PATCH: Close ICU namespace +U_NAMESPACE_END + +#endif // DOUBLE_CONVERSION_FAST_DTOA_H_ +#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/double-conversion-ieee.h b/deps/icu-small/source/i18n/double-conversion-ieee.h index 2940acb16981e8..46d488165a8af7 100644 --- a/deps/icu-small/source/i18n/double-conversion-ieee.h +++ b/deps/icu-small/source/i18n/double-conversion-ieee.h @@ -1,465 +1,465 @@ -// © 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 2012 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_DOUBLE_H_ -#define DOUBLE_CONVERSION_DOUBLE_H_ - -// ICU PATCH: Customize header file paths for ICU. - -#include "double-conversion-diy-fp.h" - -// ICU PATCH: Wrap in ICU namespace -U_NAMESPACE_BEGIN - -namespace double_conversion { - -// We assume that doubles and uint64_t have the same endianness. -static uint64_t double_to_uint64(double d) { return BitCast(d); } -static double uint64_to_double(uint64_t d64) { return BitCast(d64); } -static uint32_t float_to_uint32(float f) { return BitCast(f); } -static float uint32_to_float(uint32_t d32) { return BitCast(d32); } - -// Helper functions for doubles. -class Double { - public: - static const uint64_t kSignMask = DOUBLE_CONVERSION_UINT64_2PART_C(0x80000000, 00000000); - static const uint64_t kExponentMask = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF00000, 00000000); - static const uint64_t kSignificandMask = DOUBLE_CONVERSION_UINT64_2PART_C(0x000FFFFF, FFFFFFFF); - static const uint64_t kHiddenBit = DOUBLE_CONVERSION_UINT64_2PART_C(0x00100000, 00000000); - static const uint64_t kQuietNanBit = DOUBLE_CONVERSION_UINT64_2PART_C(0x00080000, 00000000); - static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. - static const int kSignificandSize = 53; - static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; - static const int kMaxExponent = 0x7FF - kExponentBias; - - Double() : d64_(0) {} - explicit Double(double d) : d64_(double_to_uint64(d)) {} - explicit Double(uint64_t d64) : d64_(d64) {} - explicit Double(DiyFp diy_fp) - : d64_(DiyFpToUint64(diy_fp)) {} - - // The value encoded by this Double must be greater or equal to +0.0. - // It must not be special (infinity, or NaN). - DiyFp AsDiyFp() const { - DOUBLE_CONVERSION_ASSERT(Sign() > 0); - DOUBLE_CONVERSION_ASSERT(!IsSpecial()); - return DiyFp(Significand(), Exponent()); - } - - // The value encoded by this Double must be strictly greater than 0. - DiyFp AsNormalizedDiyFp() const { - DOUBLE_CONVERSION_ASSERT(value() > 0.0); - uint64_t f = Significand(); - int e = Exponent(); - - // The current double could be a denormal. - while ((f & kHiddenBit) == 0) { - f <<= 1; - e--; - } - // Do the final shifts in one go. - f <<= DiyFp::kSignificandSize - kSignificandSize; - e -= DiyFp::kSignificandSize - kSignificandSize; - return DiyFp(f, e); - } - - // Returns the double's bit as uint64. - uint64_t AsUint64() const { - return d64_; - } - - // Returns the next greater double. Returns +infinity on input +infinity. - double NextDouble() const { - if (d64_ == kInfinity) return Double(kInfinity).value(); - if (Sign() < 0 && Significand() == 0) { - // -0.0 - return 0.0; - } - if (Sign() < 0) { - return Double(d64_ - 1).value(); - } else { - return Double(d64_ + 1).value(); - } - } - - double PreviousDouble() const { - if (d64_ == (kInfinity | kSignMask)) return -Infinity(); - if (Sign() < 0) { - return Double(d64_ + 1).value(); - } else { - if (Significand() == 0) return -0.0; - return Double(d64_ - 1).value(); - } - } - - int Exponent() const { - if (IsDenormal()) return kDenormalExponent; - - uint64_t d64 = AsUint64(); - int biased_e = - static_cast((d64 & kExponentMask) >> kPhysicalSignificandSize); - return biased_e - kExponentBias; - } - - uint64_t Significand() const { - uint64_t d64 = AsUint64(); - uint64_t significand = d64 & kSignificandMask; - if (!IsDenormal()) { - return significand + kHiddenBit; - } else { - return significand; - } - } - - // Returns true if the double is a denormal. - bool IsDenormal() const { - uint64_t d64 = AsUint64(); - return (d64 & kExponentMask) == 0; - } - - // We consider denormals not to be special. - // Hence only Infinity and NaN are special. - bool IsSpecial() const { - uint64_t d64 = AsUint64(); - return (d64 & kExponentMask) == kExponentMask; - } - - bool IsNan() const { - uint64_t d64 = AsUint64(); - return ((d64 & kExponentMask) == kExponentMask) && - ((d64 & kSignificandMask) != 0); - } - - bool IsQuietNan() const { -#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) - return IsNan() && ((AsUint64() & kQuietNanBit) == 0); -#else - return IsNan() && ((AsUint64() & kQuietNanBit) != 0); -#endif - } - - bool IsSignalingNan() const { -#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) - return IsNan() && ((AsUint64() & kQuietNanBit) != 0); -#else - return IsNan() && ((AsUint64() & kQuietNanBit) == 0); -#endif - } - - - bool IsInfinite() const { - uint64_t d64 = AsUint64(); - return ((d64 & kExponentMask) == kExponentMask) && - ((d64 & kSignificandMask) == 0); - } - - int Sign() const { - uint64_t d64 = AsUint64(); - return (d64 & kSignMask) == 0? 1: -1; - } - - // Precondition: the value encoded by this Double must be greater or equal - // than +0.0. - DiyFp UpperBoundary() const { - DOUBLE_CONVERSION_ASSERT(Sign() > 0); - return DiyFp(Significand() * 2 + 1, Exponent() - 1); - } - - // Computes the two boundaries of this. - // The bigger boundary (m_plus) is normalized. The lower boundary has the same - // exponent as m_plus. - // Precondition: the value encoded by this Double must be greater than 0. - void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { - DOUBLE_CONVERSION_ASSERT(value() > 0.0); - DiyFp v = this->AsDiyFp(); - DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); - DiyFp m_minus; - if (LowerBoundaryIsCloser()) { - m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); - } else { - m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); - } - m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); - m_minus.set_e(m_plus.e()); - *out_m_plus = m_plus; - *out_m_minus = m_minus; - } - - bool LowerBoundaryIsCloser() const { - // The boundary is closer if the significand is of the form f == 2^p-1 then - // the lower boundary is closer. - // Think of v = 1000e10 and v- = 9999e9. - // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but - // at a distance of 1e8. - // The only exception is for the smallest normal: the largest denormal is - // at the same distance as its successor. - // Note: denormals have the same exponent as the smallest normals. - bool physical_significand_is_zero = ((AsUint64() & kSignificandMask) == 0); - return physical_significand_is_zero && (Exponent() != kDenormalExponent); - } - - double value() const { return uint64_to_double(d64_); } - - // Returns the significand size for a given order of magnitude. - // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude. - // This function returns the number of significant binary digits v will have - // once it's encoded into a double. In almost all cases this is equal to - // kSignificandSize. The only exceptions are denormals. They start with - // leading zeroes and their effective significand-size is hence smaller. - static int SignificandSizeForOrderOfMagnitude(int order) { - if (order >= (kDenormalExponent + kSignificandSize)) { - return kSignificandSize; - } - if (order <= kDenormalExponent) return 0; - return order - kDenormalExponent; - } - - static double Infinity() { - return Double(kInfinity).value(); - } - - static double NaN() { - return Double(kNaN).value(); - } - - private: - static const int kDenormalExponent = -kExponentBias + 1; - static const uint64_t kInfinity = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF00000, 00000000); -#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) - static const uint64_t kNaN = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF7FFFF, FFFFFFFF); -#else - static const uint64_t kNaN = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF80000, 00000000); -#endif - - - const uint64_t d64_; - - static uint64_t DiyFpToUint64(DiyFp diy_fp) { - uint64_t significand = diy_fp.f(); - int exponent = diy_fp.e(); - while (significand > kHiddenBit + kSignificandMask) { - significand >>= 1; - exponent++; - } - if (exponent >= kMaxExponent) { - return kInfinity; - } - if (exponent < kDenormalExponent) { - return 0; - } - while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) { - significand <<= 1; - exponent--; - } - uint64_t biased_exponent; - if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) { - biased_exponent = 0; - } else { - biased_exponent = static_cast(exponent + kExponentBias); - } - return (significand & kSignificandMask) | - (biased_exponent << kPhysicalSignificandSize); - } - - DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(Double); -}; - -class Single { - public: - static const uint32_t kSignMask = 0x80000000; - static const uint32_t kExponentMask = 0x7F800000; - static const uint32_t kSignificandMask = 0x007FFFFF; - static const uint32_t kHiddenBit = 0x00800000; - static const uint32_t kQuietNanBit = 0x00400000; - static const int kPhysicalSignificandSize = 23; // Excludes the hidden bit. - static const int kSignificandSize = 24; - - Single() : d32_(0) {} - explicit Single(float f) : d32_(float_to_uint32(f)) {} - explicit Single(uint32_t d32) : d32_(d32) {} - - // The value encoded by this Single must be greater or equal to +0.0. - // It must not be special (infinity, or NaN). - DiyFp AsDiyFp() const { - DOUBLE_CONVERSION_ASSERT(Sign() > 0); - DOUBLE_CONVERSION_ASSERT(!IsSpecial()); - return DiyFp(Significand(), Exponent()); - } - - // Returns the single's bit as uint64. - uint32_t AsUint32() const { - return d32_; - } - - int Exponent() const { - if (IsDenormal()) return kDenormalExponent; - - uint32_t d32 = AsUint32(); - int biased_e = - static_cast((d32 & kExponentMask) >> kPhysicalSignificandSize); - return biased_e - kExponentBias; - } - - uint32_t Significand() const { - uint32_t d32 = AsUint32(); - uint32_t significand = d32 & kSignificandMask; - if (!IsDenormal()) { - return significand + kHiddenBit; - } else { - return significand; - } - } - - // Returns true if the single is a denormal. - bool IsDenormal() const { - uint32_t d32 = AsUint32(); - return (d32 & kExponentMask) == 0; - } - - // We consider denormals not to be special. - // Hence only Infinity and NaN are special. - bool IsSpecial() const { - uint32_t d32 = AsUint32(); - return (d32 & kExponentMask) == kExponentMask; - } - - bool IsNan() const { - uint32_t d32 = AsUint32(); - return ((d32 & kExponentMask) == kExponentMask) && - ((d32 & kSignificandMask) != 0); - } - - bool IsQuietNan() const { -#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) - return IsNan() && ((AsUint32() & kQuietNanBit) == 0); -#else - return IsNan() && ((AsUint32() & kQuietNanBit) != 0); -#endif - } - - bool IsSignalingNan() const { -#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) - return IsNan() && ((AsUint32() & kQuietNanBit) != 0); -#else - return IsNan() && ((AsUint32() & kQuietNanBit) == 0); -#endif - } - - - bool IsInfinite() const { - uint32_t d32 = AsUint32(); - return ((d32 & kExponentMask) == kExponentMask) && - ((d32 & kSignificandMask) == 0); - } - - int Sign() const { - uint32_t d32 = AsUint32(); - return (d32 & kSignMask) == 0? 1: -1; - } - - // Computes the two boundaries of this. - // The bigger boundary (m_plus) is normalized. The lower boundary has the same - // exponent as m_plus. - // Precondition: the value encoded by this Single must be greater than 0. - void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { - DOUBLE_CONVERSION_ASSERT(value() > 0.0); - DiyFp v = this->AsDiyFp(); - DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); - DiyFp m_minus; - if (LowerBoundaryIsCloser()) { - m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); - } else { - m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); - } - m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); - m_minus.set_e(m_plus.e()); - *out_m_plus = m_plus; - *out_m_minus = m_minus; - } - - // Precondition: the value encoded by this Single must be greater or equal - // than +0.0. - DiyFp UpperBoundary() const { - DOUBLE_CONVERSION_ASSERT(Sign() > 0); - return DiyFp(Significand() * 2 + 1, Exponent() - 1); - } - - bool LowerBoundaryIsCloser() const { - // The boundary is closer if the significand is of the form f == 2^p-1 then - // the lower boundary is closer. - // Think of v = 1000e10 and v- = 9999e9. - // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but - // at a distance of 1e8. - // The only exception is for the smallest normal: the largest denormal is - // at the same distance as its successor. - // Note: denormals have the same exponent as the smallest normals. - bool physical_significand_is_zero = ((AsUint32() & kSignificandMask) == 0); - return physical_significand_is_zero && (Exponent() != kDenormalExponent); - } - - float value() const { return uint32_to_float(d32_); } - - static float Infinity() { - return Single(kInfinity).value(); - } - - static float NaN() { - return Single(kNaN).value(); - } - - private: - static const int kExponentBias = 0x7F + kPhysicalSignificandSize; - static const int kDenormalExponent = -kExponentBias + 1; - static const int kMaxExponent = 0xFF - kExponentBias; - static const uint32_t kInfinity = 0x7F800000; -#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) - static const uint32_t kNaN = 0x7FBFFFFF; -#else - static const uint32_t kNaN = 0x7FC00000; -#endif - - const uint32_t d32_; - - DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(Single); -}; - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END - -#endif // DOUBLE_CONVERSION_DOUBLE_H_ -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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 2012 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_DOUBLE_H_ +#define DOUBLE_CONVERSION_DOUBLE_H_ + +// ICU PATCH: Customize header file paths for ICU. + +#include "double-conversion-diy-fp.h" + +// ICU PATCH: Wrap in ICU namespace +U_NAMESPACE_BEGIN + +namespace double_conversion { + +// We assume that doubles and uint64_t have the same endianness. +static uint64_t double_to_uint64(double d) { return BitCast(d); } +static double uint64_to_double(uint64_t d64) { return BitCast(d64); } +static uint32_t float_to_uint32(float f) { return BitCast(f); } +static float uint32_to_float(uint32_t d32) { return BitCast(d32); } + +// Helper functions for doubles. +class Double { + public: + static const uint64_t kSignMask = DOUBLE_CONVERSION_UINT64_2PART_C(0x80000000, 00000000); + static const uint64_t kExponentMask = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kSignificandMask = DOUBLE_CONVERSION_UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + static const uint64_t kHiddenBit = DOUBLE_CONVERSION_UINT64_2PART_C(0x00100000, 00000000); + static const uint64_t kQuietNanBit = DOUBLE_CONVERSION_UINT64_2PART_C(0x00080000, 00000000); + static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. + static const int kSignificandSize = 53; + static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; + static const int kMaxExponent = 0x7FF - kExponentBias; + + Double() : d64_(0) {} + explicit Double(double d) : d64_(double_to_uint64(d)) {} + explicit Double(uint64_t d64) : d64_(d64) {} + explicit Double(DiyFp diy_fp) + : d64_(DiyFpToUint64(diy_fp)) {} + + // The value encoded by this Double must be greater or equal to +0.0. + // It must not be special (infinity, or NaN). + DiyFp AsDiyFp() const { + DOUBLE_CONVERSION_ASSERT(Sign() > 0); + DOUBLE_CONVERSION_ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + // The value encoded by this Double must be strictly greater than 0. + DiyFp AsNormalizedDiyFp() const { + DOUBLE_CONVERSION_ASSERT(value() > 0.0); + uint64_t f = Significand(); + int e = Exponent(); + + // The current double could be a denormal. + while ((f & kHiddenBit) == 0) { + f <<= 1; + e--; + } + // Do the final shifts in one go. + f <<= DiyFp::kSignificandSize - kSignificandSize; + e -= DiyFp::kSignificandSize - kSignificandSize; + return DiyFp(f, e); + } + + // Returns the double's bit as uint64. + uint64_t AsUint64() const { + return d64_; + } + + // Returns the next greater double. Returns +infinity on input +infinity. + double NextDouble() const { + if (d64_ == kInfinity) return Double(kInfinity).value(); + if (Sign() < 0 && Significand() == 0) { + // -0.0 + return 0.0; + } + if (Sign() < 0) { + return Double(d64_ - 1).value(); + } else { + return Double(d64_ + 1).value(); + } + } + + double PreviousDouble() const { + if (d64_ == (kInfinity | kSignMask)) return -Infinity(); + if (Sign() < 0) { + return Double(d64_ + 1).value(); + } else { + if (Significand() == 0) return -0.0; + return Double(d64_ - 1).value(); + } + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint64_t d64 = AsUint64(); + int biased_e = + static_cast((d64 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint64_t Significand() const { + uint64_t d64 = AsUint64(); + uint64_t significand = d64 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the double is a denormal. + bool IsDenormal() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) != 0); + } + + bool IsQuietNan() const { +#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) + return IsNan() && ((AsUint64() & kQuietNanBit) == 0); +#else + return IsNan() && ((AsUint64() & kQuietNanBit) != 0); +#endif + } + + bool IsSignalingNan() const { +#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) + return IsNan() && ((AsUint64() & kQuietNanBit) != 0); +#else + return IsNan() && ((AsUint64() & kQuietNanBit) == 0); +#endif + } + + + bool IsInfinite() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) == 0); + } + + int Sign() const { + uint64_t d64 = AsUint64(); + return (d64 & kSignMask) == 0? 1: -1; + } + + // Precondition: the value encoded by this Double must be greater or equal + // than +0.0. + DiyFp UpperBoundary() const { + DOUBLE_CONVERSION_ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } + + // Computes the two boundaries of this. + // The bigger boundary (m_plus) is normalized. The lower boundary has the same + // exponent as m_plus. + // Precondition: the value encoded by this Double must be greater than 0. + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + DOUBLE_CONVERSION_ASSERT(value() > 0.0); + DiyFp v = this->AsDiyFp(); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (LowerBoundaryIsCloser()) { + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + bool LowerBoundaryIsCloser() const { + // The boundary is closer if the significand is of the form f == 2^p-1 then + // the lower boundary is closer. + // Think of v = 1000e10 and v- = 9999e9. + // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but + // at a distance of 1e8. + // The only exception is for the smallest normal: the largest denormal is + // at the same distance as its successor. + // Note: denormals have the same exponent as the smallest normals. + bool physical_significand_is_zero = ((AsUint64() & kSignificandMask) == 0); + return physical_significand_is_zero && (Exponent() != kDenormalExponent); + } + + double value() const { return uint64_to_double(d64_); } + + // Returns the significand size for a given order of magnitude. + // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude. + // This function returns the number of significant binary digits v will have + // once it's encoded into a double. In almost all cases this is equal to + // kSignificandSize. The only exceptions are denormals. They start with + // leading zeroes and their effective significand-size is hence smaller. + static int SignificandSizeForOrderOfMagnitude(int order) { + if (order >= (kDenormalExponent + kSignificandSize)) { + return kSignificandSize; + } + if (order <= kDenormalExponent) return 0; + return order - kDenormalExponent; + } + + static double Infinity() { + return Double(kInfinity).value(); + } + + static double NaN() { + return Double(kNaN).value(); + } + + private: + static const int kDenormalExponent = -kExponentBias + 1; + static const uint64_t kInfinity = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF00000, 00000000); +#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) + static const uint64_t kNaN = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF7FFFF, FFFFFFFF); +#else + static const uint64_t kNaN = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF80000, 00000000); +#endif + + + const uint64_t d64_; + + static uint64_t DiyFpToUint64(DiyFp diy_fp) { + uint64_t significand = diy_fp.f(); + int exponent = diy_fp.e(); + while (significand > kHiddenBit + kSignificandMask) { + significand >>= 1; + exponent++; + } + if (exponent >= kMaxExponent) { + return kInfinity; + } + if (exponent < kDenormalExponent) { + return 0; + } + while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) { + significand <<= 1; + exponent--; + } + uint64_t biased_exponent; + if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) { + biased_exponent = 0; + } else { + biased_exponent = static_cast(exponent + kExponentBias); + } + return (significand & kSignificandMask) | + (biased_exponent << kPhysicalSignificandSize); + } + + DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(Double); +}; + +class Single { + public: + static const uint32_t kSignMask = 0x80000000; + static const uint32_t kExponentMask = 0x7F800000; + static const uint32_t kSignificandMask = 0x007FFFFF; + static const uint32_t kHiddenBit = 0x00800000; + static const uint32_t kQuietNanBit = 0x00400000; + static const int kPhysicalSignificandSize = 23; // Excludes the hidden bit. + static const int kSignificandSize = 24; + + Single() : d32_(0) {} + explicit Single(float f) : d32_(float_to_uint32(f)) {} + explicit Single(uint32_t d32) : d32_(d32) {} + + // The value encoded by this Single must be greater or equal to +0.0. + // It must not be special (infinity, or NaN). + DiyFp AsDiyFp() const { + DOUBLE_CONVERSION_ASSERT(Sign() > 0); + DOUBLE_CONVERSION_ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + // Returns the single's bit as uint64. + uint32_t AsUint32() const { + return d32_; + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint32_t d32 = AsUint32(); + int biased_e = + static_cast((d32 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint32_t Significand() const { + uint32_t d32 = AsUint32(); + uint32_t significand = d32 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the single is a denormal. + bool IsDenormal() const { + uint32_t d32 = AsUint32(); + return (d32 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint32_t d32 = AsUint32(); + return (d32 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint32_t d32 = AsUint32(); + return ((d32 & kExponentMask) == kExponentMask) && + ((d32 & kSignificandMask) != 0); + } + + bool IsQuietNan() const { +#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) + return IsNan() && ((AsUint32() & kQuietNanBit) == 0); +#else + return IsNan() && ((AsUint32() & kQuietNanBit) != 0); +#endif + } + + bool IsSignalingNan() const { +#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) + return IsNan() && ((AsUint32() & kQuietNanBit) != 0); +#else + return IsNan() && ((AsUint32() & kQuietNanBit) == 0); +#endif + } + + + bool IsInfinite() const { + uint32_t d32 = AsUint32(); + return ((d32 & kExponentMask) == kExponentMask) && + ((d32 & kSignificandMask) == 0); + } + + int Sign() const { + uint32_t d32 = AsUint32(); + return (d32 & kSignMask) == 0? 1: -1; + } + + // Computes the two boundaries of this. + // The bigger boundary (m_plus) is normalized. The lower boundary has the same + // exponent as m_plus. + // Precondition: the value encoded by this Single must be greater than 0. + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + DOUBLE_CONVERSION_ASSERT(value() > 0.0); + DiyFp v = this->AsDiyFp(); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (LowerBoundaryIsCloser()) { + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + // Precondition: the value encoded by this Single must be greater or equal + // than +0.0. + DiyFp UpperBoundary() const { + DOUBLE_CONVERSION_ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } + + bool LowerBoundaryIsCloser() const { + // The boundary is closer if the significand is of the form f == 2^p-1 then + // the lower boundary is closer. + // Think of v = 1000e10 and v- = 9999e9. + // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but + // at a distance of 1e8. + // The only exception is for the smallest normal: the largest denormal is + // at the same distance as its successor. + // Note: denormals have the same exponent as the smallest normals. + bool physical_significand_is_zero = ((AsUint32() & kSignificandMask) == 0); + return physical_significand_is_zero && (Exponent() != kDenormalExponent); + } + + float value() const { return uint32_to_float(d32_); } + + static float Infinity() { + return Single(kInfinity).value(); + } + + static float NaN() { + return Single(kNaN).value(); + } + + private: + static const int kExponentBias = 0x7F + kPhysicalSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + static const int kMaxExponent = 0xFF - kExponentBias; + static const uint32_t kInfinity = 0x7F800000; +#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) + static const uint32_t kNaN = 0x7FBFFFFF; +#else + static const uint32_t kNaN = 0x7FC00000; +#endif + + const uint32_t d32_; + + DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(Single); +}; + +} // namespace double_conversion + +// ICU PATCH: Close ICU namespace +U_NAMESPACE_END + +#endif // DOUBLE_CONVERSION_DOUBLE_H_ +#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/double-conversion-string-to-double.cpp b/deps/icu-small/source/i18n/double-conversion-string-to-double.cpp index 727fff24e17aca..dddb0f9470941a 100644 --- a/deps/icu-small/source/i18n/double-conversion-string-to-double.cpp +++ b/deps/icu-small/source/i18n/double-conversion-string-to-double.cpp @@ -1,843 +1,843 @@ -// © 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 - -// ICU PATCH: Do not include std::locale. - -#include -// #include -#include - -// ICU PATCH: Customize header file paths for ICU. - -#include "double-conversion-string-to-double.h" - -#include "double-conversion-ieee.h" -#include "double-conversion-strtod.h" -#include "double-conversion-utils.h" - -// ICU PATCH: Wrap in ICU namespace -U_NAMESPACE_BEGIN - -#ifdef _MSC_VER -# if _MSC_VER >= 1900 -// Fix MSVC >= 2015 (_MSC_VER == 1900) warning -// C4244: 'argument': conversion from 'const uc16' to 'char', possible loss of data -// against Advance and friends, when instantiated with **it as char, not uc16. - __pragma(warning(disable: 4244)) -# endif -# if _MSC_VER <= 1700 // VS2012, see IsDecimalDigitForRadix warning fix, below -# define VS2012_RADIXWARN -# endif -#endif - -namespace double_conversion { - -namespace { - -inline char ToLower(char ch) { -#if 0 // do not include std::locale in ICU - static const std::ctype& cType = - std::use_facet >(std::locale::classic()); - return cType.tolower(ch); -#else - (void)ch; - DOUBLE_CONVERSION_UNREACHABLE(); -#endif -} - -inline char Pass(char ch) { - return ch; -} - -template -static inline bool ConsumeSubStringImpl(Iterator* current, - Iterator end, - const char* substring, - Converter converter) { - DOUBLE_CONVERSION_ASSERT(converter(**current) == *substring); - for (substring++; *substring != '\0'; substring++) { - ++*current; - if (*current == end || converter(**current) != *substring) { - return false; - } - } - ++*current; - return true; -} - -// Consumes the given substring from the iterator. -// Returns false, if the substring does not match. -template -static bool ConsumeSubString(Iterator* current, - Iterator end, - const char* substring, - bool allow_case_insensitivity) { - if (allow_case_insensitivity) { - return ConsumeSubStringImpl(current, end, substring, ToLower); - } else { - return ConsumeSubStringImpl(current, end, substring, Pass); - } -} - -// Consumes first character of the str is equal to ch -inline bool ConsumeFirstCharacter(char ch, - const char* str, - bool case_insensitivity) { - return case_insensitivity ? ToLower(ch) == str[0] : ch == str[0]; -} -} // namespace - -// Maximum number of significant digits in decimal representation. -// The longest possible double in decimal representation is -// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074 -// (768 digits). If we parse a number whose first digits are equal to a -// mean of 2 adjacent doubles (that could have up to 769 digits) the result -// must be rounded to the bigger one unless the tail consists of zeros, so -// we don't need to preserve all the digits. -const int kMaxSignificantDigits = 772; - - -static const char kWhitespaceTable7[] = { 32, 13, 10, 9, 11, 12 }; -static const int kWhitespaceTable7Length = DOUBLE_CONVERSION_ARRAY_SIZE(kWhitespaceTable7); - - -static const uc16 kWhitespaceTable16[] = { - 160, 8232, 8233, 5760, 6158, 8192, 8193, 8194, 8195, - 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8239, 8287, 12288, 65279 -}; -static const int kWhitespaceTable16Length = DOUBLE_CONVERSION_ARRAY_SIZE(kWhitespaceTable16); - - -static bool isWhitespace(int x) { - if (x < 128) { - for (int i = 0; i < kWhitespaceTable7Length; i++) { - if (kWhitespaceTable7[i] == x) return true; - } - } else { - for (int i = 0; i < kWhitespaceTable16Length; i++) { - if (kWhitespaceTable16[i] == x) return true; - } - } - return false; -} - - -// Returns true if a nonspace found and false if the end has reached. -template -static inline bool AdvanceToNonspace(Iterator* current, Iterator end) { - while (*current != end) { - if (!isWhitespace(**current)) return true; - ++*current; - } - return false; -} - - -static bool isDigit(int x, int radix) { - return (x >= '0' && x <= '9' && x < '0' + radix) - || (radix > 10 && x >= 'a' && x < 'a' + radix - 10) - || (radix > 10 && x >= 'A' && x < 'A' + radix - 10); -} - - -static double SignedZero(bool sign) { - return sign ? -0.0 : 0.0; -} - - -// Returns true if 'c' is a decimal digit that is valid for the given radix. -// -// The function is small and could be inlined, but VS2012 emitted a warning -// because it constant-propagated the radix and concluded that the last -// condition was always true. Moving it into a separate function and -// suppressing optimisation keeps the compiler from warning. -#ifdef VS2012_RADIXWARN -#pragma optimize("",off) -static bool IsDecimalDigitForRadix(int c, int radix) { - return '0' <= c && c <= '9' && (c - '0') < radix; -} -#pragma optimize("",on) -#else -static bool inline IsDecimalDigitForRadix(int c, int radix) { - return '0' <= c && c <= '9' && (c - '0') < radix; -} -#endif -// Returns true if 'c' is a character digit that is valid for the given radix. -// The 'a_character' should be 'a' or 'A'. -// -// The function is small and could be inlined, but VS2012 emitted a warning -// because it constant-propagated the radix and concluded that the first -// condition was always false. By moving it into a separate function the -// compiler wouldn't warn anymore. -static bool IsCharacterDigitForRadix(int c, int radix, char a_character) { - return radix > 10 && c >= a_character && c < a_character + radix - 10; -} - -// Returns true, when the iterator is equal to end. -template -static bool Advance (Iterator* it, uc16 separator, int base, Iterator& end) { - if (separator == StringToDoubleConverter::kNoSeparator) { - ++(*it); - return *it == end; - } - if (!isDigit(**it, base)) { - ++(*it); - return *it == end; - } - ++(*it); - if (*it == end) return true; - if (*it + 1 == end) return false; - if (**it == separator && isDigit(*(*it + 1), base)) { - ++(*it); - } - return *it == end; -} - -// Checks whether the string in the range start-end is a hex-float string. -// This function assumes that the leading '0x'/'0X' is already consumed. -// -// Hex float strings are of one of the following forms: -// - hex_digits+ 'p' ('+'|'-')? exponent_digits+ -// - hex_digits* '.' hex_digits+ 'p' ('+'|'-')? exponent_digits+ -// - hex_digits+ '.' 'p' ('+'|'-')? exponent_digits+ -template -static bool IsHexFloatString(Iterator start, - Iterator end, - uc16 separator, - bool allow_trailing_junk) { - DOUBLE_CONVERSION_ASSERT(start != end); - - Iterator current = start; - - bool saw_digit = false; - while (isDigit(*current, 16)) { - saw_digit = true; - if (Advance(¤t, separator, 16, end)) return false; - } - if (*current == '.') { - if (Advance(¤t, separator, 16, end)) return false; - while (isDigit(*current, 16)) { - saw_digit = true; - if (Advance(¤t, separator, 16, end)) return false; - } - } - if (!saw_digit) return false; - if (*current != 'p' && *current != 'P') return false; - if (Advance(¤t, separator, 16, end)) return false; - if (*current == '+' || *current == '-') { - if (Advance(¤t, separator, 16, end)) return false; - } - if (!isDigit(*current, 10)) return false; - if (Advance(¤t, separator, 16, end)) return true; - while (isDigit(*current, 10)) { - if (Advance(¤t, separator, 16, end)) return true; - } - return allow_trailing_junk || !AdvanceToNonspace(¤t, end); -} - - -// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end. -// -// If parse_as_hex_float is true, then the string must be a valid -// hex-float. -template -static double RadixStringToIeee(Iterator* current, - Iterator end, - bool sign, - uc16 separator, - bool parse_as_hex_float, - bool allow_trailing_junk, - double junk_string_value, - bool read_as_double, - bool* result_is_junk) { - DOUBLE_CONVERSION_ASSERT(*current != end); - DOUBLE_CONVERSION_ASSERT(!parse_as_hex_float || - IsHexFloatString(*current, end, separator, allow_trailing_junk)); - - const int kDoubleSize = Double::kSignificandSize; - const int kSingleSize = Single::kSignificandSize; - const int kSignificandSize = read_as_double? kDoubleSize: kSingleSize; - - *result_is_junk = true; - - int64_t number = 0; - int exponent = 0; - const int radix = (1 << radix_log_2); - // Whether we have encountered a '.' and are parsing the decimal digits. - // Only relevant if parse_as_hex_float is true. - bool post_decimal = false; - - // Skip leading 0s. - while (**current == '0') { - if (Advance(current, separator, radix, end)) { - *result_is_junk = false; - return SignedZero(sign); - } - } - - while (true) { - int digit; - if (IsDecimalDigitForRadix(**current, radix)) { - digit = static_cast(**current) - '0'; - if (post_decimal) exponent -= radix_log_2; - } else if (IsCharacterDigitForRadix(**current, radix, 'a')) { - digit = static_cast(**current) - 'a' + 10; - if (post_decimal) exponent -= radix_log_2; - } else if (IsCharacterDigitForRadix(**current, radix, 'A')) { - digit = static_cast(**current) - 'A' + 10; - if (post_decimal) exponent -= radix_log_2; - } else if (parse_as_hex_float && **current == '.') { - post_decimal = true; - Advance(current, separator, radix, end); - DOUBLE_CONVERSION_ASSERT(*current != end); - continue; - } else if (parse_as_hex_float && (**current == 'p' || **current == 'P')) { - break; - } else { - if (allow_trailing_junk || !AdvanceToNonspace(current, end)) { - break; - } else { - return junk_string_value; - } - } - - number = number * radix + digit; - int overflow = static_cast(number >> kSignificandSize); - if (overflow != 0) { - // Overflow occurred. Need to determine which direction to round the - // result. - int overflow_bits_count = 1; - while (overflow > 1) { - overflow_bits_count++; - overflow >>= 1; - } - - int dropped_bits_mask = ((1 << overflow_bits_count) - 1); - int dropped_bits = static_cast(number) & dropped_bits_mask; - number >>= overflow_bits_count; - exponent += overflow_bits_count; - - bool zero_tail = true; - for (;;) { - if (Advance(current, separator, radix, end)) break; - if (parse_as_hex_float && **current == '.') { - // Just run over the '.'. We are just trying to see whether there is - // a non-zero digit somewhere. - Advance(current, separator, radix, end); - DOUBLE_CONVERSION_ASSERT(*current != end); - post_decimal = true; - } - if (!isDigit(**current, radix)) break; - zero_tail = zero_tail && **current == '0'; - if (!post_decimal) exponent += radix_log_2; - } - - if (!parse_as_hex_float && - !allow_trailing_junk && - AdvanceToNonspace(current, end)) { - return junk_string_value; - } - - int middle_value = (1 << (overflow_bits_count - 1)); - if (dropped_bits > middle_value) { - number++; // Rounding up. - } else if (dropped_bits == middle_value) { - // Rounding to even to consistency with decimals: half-way case rounds - // up if significant part is odd and down otherwise. - if ((number & 1) != 0 || !zero_tail) { - number++; // Rounding up. - } - } - - // Rounding up may cause overflow. - if ((number & ((int64_t)1 << kSignificandSize)) != 0) { - exponent++; - number >>= 1; - } - break; - } - if (Advance(current, separator, radix, end)) break; - } - - DOUBLE_CONVERSION_ASSERT(number < ((int64_t)1 << kSignificandSize)); - DOUBLE_CONVERSION_ASSERT(static_cast(static_cast(number)) == number); - - *result_is_junk = false; - - if (parse_as_hex_float) { - DOUBLE_CONVERSION_ASSERT(**current == 'p' || **current == 'P'); - Advance(current, separator, radix, end); - DOUBLE_CONVERSION_ASSERT(*current != end); - bool is_negative = false; - if (**current == '+') { - Advance(current, separator, radix, end); - DOUBLE_CONVERSION_ASSERT(*current != end); - } else if (**current == '-') { - is_negative = true; - Advance(current, separator, radix, end); - DOUBLE_CONVERSION_ASSERT(*current != end); - } - int written_exponent = 0; - while (IsDecimalDigitForRadix(**current, 10)) { - // No need to read exponents if they are too big. That could potentially overflow - // the `written_exponent` variable. - if (abs(written_exponent) <= 100 * Double::kMaxExponent) { - written_exponent = 10 * written_exponent + **current - '0'; - } - if (Advance(current, separator, radix, end)) break; - } - if (is_negative) written_exponent = -written_exponent; - exponent += written_exponent; - } - - if (exponent == 0 || number == 0) { - if (sign) { - if (number == 0) return -0.0; - number = -number; - } - return static_cast(number); - } - - DOUBLE_CONVERSION_ASSERT(number != 0); - double result = Double(DiyFp(number, exponent)).value(); - return sign ? -result : result; -} - -template -double StringToDoubleConverter::StringToIeee( - Iterator input, - int length, - bool read_as_double, - int* processed_characters_count) const { - Iterator current = input; - Iterator end = input + length; - - *processed_characters_count = 0; - - const bool allow_trailing_junk = (flags_ & ALLOW_TRAILING_JUNK) != 0; - const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0; - const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0; - const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0; - const bool allow_case_insensitivity = (flags_ & ALLOW_CASE_INSENSITIVITY) != 0; - - // To make sure that iterator dereferencing is valid the following - // convention is used: - // 1. Each '++current' statement is followed by check for equality to 'end'. - // 2. If AdvanceToNonspace returned false then current == end. - // 3. If 'current' becomes equal to 'end' the function returns or goes to - // 'parsing_done'. - // 4. 'current' is not dereferenced after the 'parsing_done' label. - // 5. Code before 'parsing_done' may rely on 'current != end'. - if (current == end) return empty_string_value_; - - if (allow_leading_spaces || allow_trailing_spaces) { - if (!AdvanceToNonspace(¤t, end)) { - *processed_characters_count = static_cast(current - input); - return empty_string_value_; - } - if (!allow_leading_spaces && (input != current)) { - // No leading spaces allowed, but AdvanceToNonspace moved forward. - return junk_string_value_; - } - } - - // Exponent will be adjusted if insignificant digits of the integer part - // or insignificant leading zeros of the fractional part are dropped. - int exponent = 0; - int significant_digits = 0; - int insignificant_digits = 0; - bool nonzero_digit_dropped = false; - - bool sign = false; - - if (*current == '+' || *current == '-') { - sign = (*current == '-'); - ++current; - Iterator next_non_space = current; - // Skip following spaces (if allowed). - if (!AdvanceToNonspace(&next_non_space, end)) return junk_string_value_; - if (!allow_spaces_after_sign && (current != next_non_space)) { - return junk_string_value_; - } - current = next_non_space; - } - - if (infinity_symbol_ != DOUBLE_CONVERSION_NULLPTR) { - if (ConsumeFirstCharacter(*current, infinity_symbol_, allow_case_insensitivity)) { - if (!ConsumeSubString(¤t, end, infinity_symbol_, allow_case_insensitivity)) { - return junk_string_value_; - } - - if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { - return junk_string_value_; - } - if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { - return junk_string_value_; - } - - *processed_characters_count = static_cast(current - input); - return sign ? -Double::Infinity() : Double::Infinity(); - } - } - - if (nan_symbol_ != DOUBLE_CONVERSION_NULLPTR) { - if (ConsumeFirstCharacter(*current, nan_symbol_, allow_case_insensitivity)) { - if (!ConsumeSubString(¤t, end, nan_symbol_, allow_case_insensitivity)) { - return junk_string_value_; - } - - if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { - return junk_string_value_; - } - if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { - return junk_string_value_; - } - - *processed_characters_count = static_cast(current - input); - return sign ? -Double::NaN() : Double::NaN(); - } - } - - bool leading_zero = false; - if (*current == '0') { - if (Advance(¤t, separator_, 10, end)) { - *processed_characters_count = static_cast(current - input); - return SignedZero(sign); - } - - leading_zero = true; - - // It could be hexadecimal value. - if (((flags_ & ALLOW_HEX) || (flags_ & ALLOW_HEX_FLOATS)) && - (*current == 'x' || *current == 'X')) { - ++current; - - if (current == end) return junk_string_value_; // "0x" - - bool parse_as_hex_float = (flags_ & ALLOW_HEX_FLOATS) && - IsHexFloatString(current, end, separator_, allow_trailing_junk); - - if (!parse_as_hex_float && !isDigit(*current, 16)) { - return junk_string_value_; - } - - bool result_is_junk; - double result = RadixStringToIeee<4>(¤t, - end, - sign, - separator_, - parse_as_hex_float, - allow_trailing_junk, - junk_string_value_, - read_as_double, - &result_is_junk); - if (!result_is_junk) { - if (allow_trailing_spaces) AdvanceToNonspace(¤t, end); - *processed_characters_count = static_cast(current - input); - } - return result; - } - - // Ignore leading zeros in the integer part. - while (*current == '0') { - if (Advance(¤t, separator_, 10, end)) { - *processed_characters_count = static_cast(current - input); - return SignedZero(sign); - } - } - } - - bool octal = leading_zero && (flags_ & ALLOW_OCTALS) != 0; - - // The longest form of simplified number is: "-.1eXXX\0". - const int kBufferSize = kMaxSignificantDigits + 10; - DOUBLE_CONVERSION_STACK_UNINITIALIZED char - buffer[kBufferSize]; // NOLINT: size is known at compile time. - int buffer_pos = 0; - - // Copy significant digits of the integer part (if any) to the buffer. - while (*current >= '0' && *current <= '9') { - if (significant_digits < kMaxSignificantDigits) { - DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize); - buffer[buffer_pos++] = static_cast(*current); - significant_digits++; - // Will later check if it's an octal in the buffer. - } else { - insignificant_digits++; // Move the digit into the exponential part. - nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; - } - octal = octal && *current < '8'; - if (Advance(¤t, separator_, 10, end)) goto parsing_done; - } - - if (significant_digits == 0) { - octal = false; - } - - if (*current == '.') { - if (octal && !allow_trailing_junk) return junk_string_value_; - if (octal) goto parsing_done; - - if (Advance(¤t, separator_, 10, end)) { - if (significant_digits == 0 && !leading_zero) { - return junk_string_value_; - } else { - goto parsing_done; - } - } - - if (significant_digits == 0) { - // octal = false; - // Integer part consists of 0 or is absent. Significant digits start after - // leading zeros (if any). - while (*current == '0') { - if (Advance(¤t, separator_, 10, end)) { - *processed_characters_count = static_cast(current - input); - return SignedZero(sign); - } - exponent--; // Move this 0 into the exponent. - } - } - - // There is a fractional part. - // We don't emit a '.', but adjust the exponent instead. - while (*current >= '0' && *current <= '9') { - if (significant_digits < kMaxSignificantDigits) { - DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize); - buffer[buffer_pos++] = static_cast(*current); - significant_digits++; - exponent--; - } else { - // Ignore insignificant digits in the fractional part. - nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; - } - if (Advance(¤t, separator_, 10, end)) goto parsing_done; - } - } - - if (!leading_zero && exponent == 0 && significant_digits == 0) { - // If leading_zeros is true then the string contains zeros. - // If exponent < 0 then string was [+-]\.0*... - // If significant_digits != 0 the string is not equal to 0. - // Otherwise there are no digits in the string. - return junk_string_value_; - } - - // Parse exponential part. - if (*current == 'e' || *current == 'E') { - if (octal && !allow_trailing_junk) return junk_string_value_; - if (octal) goto parsing_done; - Iterator junk_begin = current; - ++current; - if (current == end) { - if (allow_trailing_junk) { - current = junk_begin; - goto parsing_done; - } else { - return junk_string_value_; - } - } - char exponen_sign = '+'; - if (*current == '+' || *current == '-') { - exponen_sign = static_cast(*current); - ++current; - if (current == end) { - if (allow_trailing_junk) { - current = junk_begin; - goto parsing_done; - } else { - return junk_string_value_; - } - } - } - - if (current == end || *current < '0' || *current > '9') { - if (allow_trailing_junk) { - current = junk_begin; - goto parsing_done; - } else { - return junk_string_value_; - } - } - - const int max_exponent = INT_MAX / 2; - DOUBLE_CONVERSION_ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); - int num = 0; - do { - // Check overflow. - int digit = *current - '0'; - if (num >= max_exponent / 10 - && !(num == max_exponent / 10 && digit <= max_exponent % 10)) { - num = max_exponent; - } else { - num = num * 10 + digit; - } - ++current; - } while (current != end && *current >= '0' && *current <= '9'); - - exponent += (exponen_sign == '-' ? -num : num); - } - - if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { - return junk_string_value_; - } - if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { - return junk_string_value_; - } - if (allow_trailing_spaces) { - AdvanceToNonspace(¤t, end); - } - - parsing_done: - exponent += insignificant_digits; - - if (octal) { - double result; - bool result_is_junk; - char* start = buffer; - result = RadixStringToIeee<3>(&start, - buffer + buffer_pos, - sign, - separator_, - false, // Don't parse as hex_float. - allow_trailing_junk, - junk_string_value_, - read_as_double, - &result_is_junk); - DOUBLE_CONVERSION_ASSERT(!result_is_junk); - *processed_characters_count = static_cast(current - input); - return result; - } - - if (nonzero_digit_dropped) { - buffer[buffer_pos++] = '1'; - exponent--; - } - - DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize); - buffer[buffer_pos] = '\0'; - - // Code above ensures there are no leading zeros and the buffer has fewer than - // kMaxSignificantDecimalDigits characters. Trim trailing zeros. - Vector chars(buffer, buffer_pos); - chars = TrimTrailingZeros(chars); - exponent += buffer_pos - chars.length(); - - double converted; - if (read_as_double) { - converted = StrtodTrimmed(chars, exponent); - } else { - converted = StrtofTrimmed(chars, exponent); - } - *processed_characters_count = static_cast(current - input); - return sign? -converted: converted; -} - - -double StringToDoubleConverter::StringToDouble( - const char* buffer, - int length, - int* processed_characters_count) const { - return StringToIeee(buffer, length, true, processed_characters_count); -} - - -double StringToDoubleConverter::StringToDouble( - const uc16* buffer, - int length, - int* processed_characters_count) const { - return StringToIeee(buffer, length, true, processed_characters_count); -} - - -float StringToDoubleConverter::StringToFloat( - const char* buffer, - int length, - int* processed_characters_count) const { - return static_cast(StringToIeee(buffer, length, false, - processed_characters_count)); -} - - -float StringToDoubleConverter::StringToFloat( - const uc16* buffer, - int length, - int* processed_characters_count) const { - return static_cast(StringToIeee(buffer, length, false, - processed_characters_count)); -} - - -template<> -double StringToDoubleConverter::StringTo( - const char* buffer, - int length, - int* processed_characters_count) const { - return StringToDouble(buffer, length, processed_characters_count); -} - - -template<> -float StringToDoubleConverter::StringTo( - const char* buffer, - int length, - int* processed_characters_count) const { - return StringToFloat(buffer, length, processed_characters_count); -} - - -template<> -double StringToDoubleConverter::StringTo( - const uc16* buffer, - int length, - int* processed_characters_count) const { - return StringToDouble(buffer, length, processed_characters_count); -} - - -template<> -float StringToDoubleConverter::StringTo( - const uc16* buffer, - int length, - int* processed_characters_count) const { - return StringToFloat(buffer, length, processed_characters_count); -} - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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 + +// ICU PATCH: Do not include std::locale. + +#include +// #include +#include + +// ICU PATCH: Customize header file paths for ICU. + +#include "double-conversion-string-to-double.h" + +#include "double-conversion-ieee.h" +#include "double-conversion-strtod.h" +#include "double-conversion-utils.h" + +// ICU PATCH: Wrap in ICU namespace +U_NAMESPACE_BEGIN + +#ifdef _MSC_VER +# if _MSC_VER >= 1900 +// Fix MSVC >= 2015 (_MSC_VER == 1900) warning +// C4244: 'argument': conversion from 'const uc16' to 'char', possible loss of data +// against Advance and friends, when instantiated with **it as char, not uc16. + __pragma(warning(disable: 4244)) +# endif +# if _MSC_VER <= 1700 // VS2012, see IsDecimalDigitForRadix warning fix, below +# define VS2012_RADIXWARN +# endif +#endif + +namespace double_conversion { + +namespace { + +inline char ToLower(char ch) { +#if 0 // do not include std::locale in ICU + static const std::ctype& cType = + std::use_facet >(std::locale::classic()); + return cType.tolower(ch); +#else + (void)ch; + DOUBLE_CONVERSION_UNREACHABLE(); +#endif +} + +inline char Pass(char ch) { + return ch; +} + +template +static inline bool ConsumeSubStringImpl(Iterator* current, + Iterator end, + const char* substring, + Converter converter) { + DOUBLE_CONVERSION_ASSERT(converter(**current) == *substring); + for (substring++; *substring != '\0'; substring++) { + ++*current; + if (*current == end || converter(**current) != *substring) { + return false; + } + } + ++*current; + return true; +} + +// Consumes the given substring from the iterator. +// Returns false, if the substring does not match. +template +static bool ConsumeSubString(Iterator* current, + Iterator end, + const char* substring, + bool allow_case_insensitivity) { + if (allow_case_insensitivity) { + return ConsumeSubStringImpl(current, end, substring, ToLower); + } else { + return ConsumeSubStringImpl(current, end, substring, Pass); + } +} + +// Consumes first character of the str is equal to ch +inline bool ConsumeFirstCharacter(char ch, + const char* str, + bool case_insensitivity) { + return case_insensitivity ? ToLower(ch) == str[0] : ch == str[0]; +} +} // namespace + +// Maximum number of significant digits in decimal representation. +// The longest possible double in decimal representation is +// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074 +// (768 digits). If we parse a number whose first digits are equal to a +// mean of 2 adjacent doubles (that could have up to 769 digits) the result +// must be rounded to the bigger one unless the tail consists of zeros, so +// we don't need to preserve all the digits. +const int kMaxSignificantDigits = 772; + + +static const char kWhitespaceTable7[] = { 32, 13, 10, 9, 11, 12 }; +static const int kWhitespaceTable7Length = DOUBLE_CONVERSION_ARRAY_SIZE(kWhitespaceTable7); + + +static const uc16 kWhitespaceTable16[] = { + 160, 8232, 8233, 5760, 6158, 8192, 8193, 8194, 8195, + 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8239, 8287, 12288, 65279 +}; +static const int kWhitespaceTable16Length = DOUBLE_CONVERSION_ARRAY_SIZE(kWhitespaceTable16); + + +static bool isWhitespace(int x) { + if (x < 128) { + for (int i = 0; i < kWhitespaceTable7Length; i++) { + if (kWhitespaceTable7[i] == x) return true; + } + } else { + for (int i = 0; i < kWhitespaceTable16Length; i++) { + if (kWhitespaceTable16[i] == x) return true; + } + } + return false; +} + + +// Returns true if a nonspace found and false if the end has reached. +template +static inline bool AdvanceToNonspace(Iterator* current, Iterator end) { + while (*current != end) { + if (!isWhitespace(**current)) return true; + ++*current; + } + return false; +} + + +static bool isDigit(int x, int radix) { + return (x >= '0' && x <= '9' && x < '0' + radix) + || (radix > 10 && x >= 'a' && x < 'a' + radix - 10) + || (radix > 10 && x >= 'A' && x < 'A' + radix - 10); +} + + +static double SignedZero(bool sign) { + return sign ? -0.0 : 0.0; +} + + +// Returns true if 'c' is a decimal digit that is valid for the given radix. +// +// The function is small and could be inlined, but VS2012 emitted a warning +// because it constant-propagated the radix and concluded that the last +// condition was always true. Moving it into a separate function and +// suppressing optimisation keeps the compiler from warning. +#ifdef VS2012_RADIXWARN +#pragma optimize("",off) +static bool IsDecimalDigitForRadix(int c, int radix) { + return '0' <= c && c <= '9' && (c - '0') < radix; +} +#pragma optimize("",on) +#else +static bool inline IsDecimalDigitForRadix(int c, int radix) { + return '0' <= c && c <= '9' && (c - '0') < radix; +} +#endif +// Returns true if 'c' is a character digit that is valid for the given radix. +// The 'a_character' should be 'a' or 'A'. +// +// The function is small and could be inlined, but VS2012 emitted a warning +// because it constant-propagated the radix and concluded that the first +// condition was always false. By moving it into a separate function the +// compiler wouldn't warn anymore. +static bool IsCharacterDigitForRadix(int c, int radix, char a_character) { + return radix > 10 && c >= a_character && c < a_character + radix - 10; +} + +// Returns true, when the iterator is equal to end. +template +static bool Advance (Iterator* it, uc16 separator, int base, Iterator& end) { + if (separator == StringToDoubleConverter::kNoSeparator) { + ++(*it); + return *it == end; + } + if (!isDigit(**it, base)) { + ++(*it); + return *it == end; + } + ++(*it); + if (*it == end) return true; + if (*it + 1 == end) return false; + if (**it == separator && isDigit(*(*it + 1), base)) { + ++(*it); + } + return *it == end; +} + +// Checks whether the string in the range start-end is a hex-float string. +// This function assumes that the leading '0x'/'0X' is already consumed. +// +// Hex float strings are of one of the following forms: +// - hex_digits+ 'p' ('+'|'-')? exponent_digits+ +// - hex_digits* '.' hex_digits+ 'p' ('+'|'-')? exponent_digits+ +// - hex_digits+ '.' 'p' ('+'|'-')? exponent_digits+ +template +static bool IsHexFloatString(Iterator start, + Iterator end, + uc16 separator, + bool allow_trailing_junk) { + DOUBLE_CONVERSION_ASSERT(start != end); + + Iterator current = start; + + bool saw_digit = false; + while (isDigit(*current, 16)) { + saw_digit = true; + if (Advance(¤t, separator, 16, end)) return false; + } + if (*current == '.') { + if (Advance(¤t, separator, 16, end)) return false; + while (isDigit(*current, 16)) { + saw_digit = true; + if (Advance(¤t, separator, 16, end)) return false; + } + } + if (!saw_digit) return false; + if (*current != 'p' && *current != 'P') return false; + if (Advance(¤t, separator, 16, end)) return false; + if (*current == '+' || *current == '-') { + if (Advance(¤t, separator, 16, end)) return false; + } + if (!isDigit(*current, 10)) return false; + if (Advance(¤t, separator, 16, end)) return true; + while (isDigit(*current, 10)) { + if (Advance(¤t, separator, 16, end)) return true; + } + return allow_trailing_junk || !AdvanceToNonspace(¤t, end); +} + + +// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end. +// +// If parse_as_hex_float is true, then the string must be a valid +// hex-float. +template +static double RadixStringToIeee(Iterator* current, + Iterator end, + bool sign, + uc16 separator, + bool parse_as_hex_float, + bool allow_trailing_junk, + double junk_string_value, + bool read_as_double, + bool* result_is_junk) { + DOUBLE_CONVERSION_ASSERT(*current != end); + DOUBLE_CONVERSION_ASSERT(!parse_as_hex_float || + IsHexFloatString(*current, end, separator, allow_trailing_junk)); + + const int kDoubleSize = Double::kSignificandSize; + const int kSingleSize = Single::kSignificandSize; + const int kSignificandSize = read_as_double? kDoubleSize: kSingleSize; + + *result_is_junk = true; + + int64_t number = 0; + int exponent = 0; + const int radix = (1 << radix_log_2); + // Whether we have encountered a '.' and are parsing the decimal digits. + // Only relevant if parse_as_hex_float is true. + bool post_decimal = false; + + // Skip leading 0s. + while (**current == '0') { + if (Advance(current, separator, radix, end)) { + *result_is_junk = false; + return SignedZero(sign); + } + } + + while (true) { + int digit; + if (IsDecimalDigitForRadix(**current, radix)) { + digit = static_cast(**current) - '0'; + if (post_decimal) exponent -= radix_log_2; + } else if (IsCharacterDigitForRadix(**current, radix, 'a')) { + digit = static_cast(**current) - 'a' + 10; + if (post_decimal) exponent -= radix_log_2; + } else if (IsCharacterDigitForRadix(**current, radix, 'A')) { + digit = static_cast(**current) - 'A' + 10; + if (post_decimal) exponent -= radix_log_2; + } else if (parse_as_hex_float && **current == '.') { + post_decimal = true; + Advance(current, separator, radix, end); + DOUBLE_CONVERSION_ASSERT(*current != end); + continue; + } else if (parse_as_hex_float && (**current == 'p' || **current == 'P')) { + break; + } else { + if (allow_trailing_junk || !AdvanceToNonspace(current, end)) { + break; + } else { + return junk_string_value; + } + } + + number = number * radix + digit; + int overflow = static_cast(number >> kSignificandSize); + if (overflow != 0) { + // Overflow occurred. Need to determine which direction to round the + // result. + int overflow_bits_count = 1; + while (overflow > 1) { + overflow_bits_count++; + overflow >>= 1; + } + + int dropped_bits_mask = ((1 << overflow_bits_count) - 1); + int dropped_bits = static_cast(number) & dropped_bits_mask; + number >>= overflow_bits_count; + exponent += overflow_bits_count; + + bool zero_tail = true; + for (;;) { + if (Advance(current, separator, radix, end)) break; + if (parse_as_hex_float && **current == '.') { + // Just run over the '.'. We are just trying to see whether there is + // a non-zero digit somewhere. + Advance(current, separator, radix, end); + DOUBLE_CONVERSION_ASSERT(*current != end); + post_decimal = true; + } + if (!isDigit(**current, radix)) break; + zero_tail = zero_tail && **current == '0'; + if (!post_decimal) exponent += radix_log_2; + } + + if (!parse_as_hex_float && + !allow_trailing_junk && + AdvanceToNonspace(current, end)) { + return junk_string_value; + } + + int middle_value = (1 << (overflow_bits_count - 1)); + if (dropped_bits > middle_value) { + number++; // Rounding up. + } else if (dropped_bits == middle_value) { + // Rounding to even to consistency with decimals: half-way case rounds + // up if significant part is odd and down otherwise. + if ((number & 1) != 0 || !zero_tail) { + number++; // Rounding up. + } + } + + // Rounding up may cause overflow. + if ((number & ((int64_t)1 << kSignificandSize)) != 0) { + exponent++; + number >>= 1; + } + break; + } + if (Advance(current, separator, radix, end)) break; + } + + DOUBLE_CONVERSION_ASSERT(number < ((int64_t)1 << kSignificandSize)); + DOUBLE_CONVERSION_ASSERT(static_cast(static_cast(number)) == number); + + *result_is_junk = false; + + if (parse_as_hex_float) { + DOUBLE_CONVERSION_ASSERT(**current == 'p' || **current == 'P'); + Advance(current, separator, radix, end); + DOUBLE_CONVERSION_ASSERT(*current != end); + bool is_negative = false; + if (**current == '+') { + Advance(current, separator, radix, end); + DOUBLE_CONVERSION_ASSERT(*current != end); + } else if (**current == '-') { + is_negative = true; + Advance(current, separator, radix, end); + DOUBLE_CONVERSION_ASSERT(*current != end); + } + int written_exponent = 0; + while (IsDecimalDigitForRadix(**current, 10)) { + // No need to read exponents if they are too big. That could potentially overflow + // the `written_exponent` variable. + if (abs(written_exponent) <= 100 * Double::kMaxExponent) { + written_exponent = 10 * written_exponent + **current - '0'; + } + if (Advance(current, separator, radix, end)) break; + } + if (is_negative) written_exponent = -written_exponent; + exponent += written_exponent; + } + + if (exponent == 0 || number == 0) { + if (sign) { + if (number == 0) return -0.0; + number = -number; + } + return static_cast(number); + } + + DOUBLE_CONVERSION_ASSERT(number != 0); + double result = Double(DiyFp(number, exponent)).value(); + return sign ? -result : result; +} + +template +double StringToDoubleConverter::StringToIeee( + Iterator input, + int length, + bool read_as_double, + int* processed_characters_count) const { + Iterator current = input; + Iterator end = input + length; + + *processed_characters_count = 0; + + const bool allow_trailing_junk = (flags_ & ALLOW_TRAILING_JUNK) != 0; + const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0; + const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0; + const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0; + const bool allow_case_insensitivity = (flags_ & ALLOW_CASE_INSENSITIVITY) != 0; + + // To make sure that iterator dereferencing is valid the following + // convention is used: + // 1. Each '++current' statement is followed by check for equality to 'end'. + // 2. If AdvanceToNonspace returned false then current == end. + // 3. If 'current' becomes equal to 'end' the function returns or goes to + // 'parsing_done'. + // 4. 'current' is not dereferenced after the 'parsing_done' label. + // 5. Code before 'parsing_done' may rely on 'current != end'. + if (current == end) return empty_string_value_; + + if (allow_leading_spaces || allow_trailing_spaces) { + if (!AdvanceToNonspace(¤t, end)) { + *processed_characters_count = static_cast(current - input); + return empty_string_value_; + } + if (!allow_leading_spaces && (input != current)) { + // No leading spaces allowed, but AdvanceToNonspace moved forward. + return junk_string_value_; + } + } + + // Exponent will be adjusted if insignificant digits of the integer part + // or insignificant leading zeros of the fractional part are dropped. + int exponent = 0; + int significant_digits = 0; + int insignificant_digits = 0; + bool nonzero_digit_dropped = false; + + bool sign = false; + + if (*current == '+' || *current == '-') { + sign = (*current == '-'); + ++current; + Iterator next_non_space = current; + // Skip following spaces (if allowed). + if (!AdvanceToNonspace(&next_non_space, end)) return junk_string_value_; + if (!allow_spaces_after_sign && (current != next_non_space)) { + return junk_string_value_; + } + current = next_non_space; + } + + if (infinity_symbol_ != DOUBLE_CONVERSION_NULLPTR) { + if (ConsumeFirstCharacter(*current, infinity_symbol_, allow_case_insensitivity)) { + if (!ConsumeSubString(¤t, end, infinity_symbol_, allow_case_insensitivity)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + *processed_characters_count = static_cast(current - input); + return sign ? -Double::Infinity() : Double::Infinity(); + } + } + + if (nan_symbol_ != DOUBLE_CONVERSION_NULLPTR) { + if (ConsumeFirstCharacter(*current, nan_symbol_, allow_case_insensitivity)) { + if (!ConsumeSubString(¤t, end, nan_symbol_, allow_case_insensitivity)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + *processed_characters_count = static_cast(current - input); + return sign ? -Double::NaN() : Double::NaN(); + } + } + + bool leading_zero = false; + if (*current == '0') { + if (Advance(¤t, separator_, 10, end)) { + *processed_characters_count = static_cast(current - input); + return SignedZero(sign); + } + + leading_zero = true; + + // It could be hexadecimal value. + if (((flags_ & ALLOW_HEX) || (flags_ & ALLOW_HEX_FLOATS)) && + (*current == 'x' || *current == 'X')) { + ++current; + + if (current == end) return junk_string_value_; // "0x" + + bool parse_as_hex_float = (flags_ & ALLOW_HEX_FLOATS) && + IsHexFloatString(current, end, separator_, allow_trailing_junk); + + if (!parse_as_hex_float && !isDigit(*current, 16)) { + return junk_string_value_; + } + + bool result_is_junk; + double result = RadixStringToIeee<4>(¤t, + end, + sign, + separator_, + parse_as_hex_float, + allow_trailing_junk, + junk_string_value_, + read_as_double, + &result_is_junk); + if (!result_is_junk) { + if (allow_trailing_spaces) AdvanceToNonspace(¤t, end); + *processed_characters_count = static_cast(current - input); + } + return result; + } + + // Ignore leading zeros in the integer part. + while (*current == '0') { + if (Advance(¤t, separator_, 10, end)) { + *processed_characters_count = static_cast(current - input); + return SignedZero(sign); + } + } + } + + bool octal = leading_zero && (flags_ & ALLOW_OCTALS) != 0; + + // The longest form of simplified number is: "-.1eXXX\0". + const int kBufferSize = kMaxSignificantDigits + 10; + DOUBLE_CONVERSION_STACK_UNINITIALIZED char + buffer[kBufferSize]; // NOLINT: size is known at compile time. + int buffer_pos = 0; + + // Copy significant digits of the integer part (if any) to the buffer. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast(*current); + significant_digits++; + // Will later check if it's an octal in the buffer. + } else { + insignificant_digits++; // Move the digit into the exponential part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + octal = octal && *current < '8'; + if (Advance(¤t, separator_, 10, end)) goto parsing_done; + } + + if (significant_digits == 0) { + octal = false; + } + + if (*current == '.') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + + if (Advance(¤t, separator_, 10, end)) { + if (significant_digits == 0 && !leading_zero) { + return junk_string_value_; + } else { + goto parsing_done; + } + } + + if (significant_digits == 0) { + // octal = false; + // Integer part consists of 0 or is absent. Significant digits start after + // leading zeros (if any). + while (*current == '0') { + if (Advance(¤t, separator_, 10, end)) { + *processed_characters_count = static_cast(current - input); + return SignedZero(sign); + } + exponent--; // Move this 0 into the exponent. + } + } + + // There is a fractional part. + // We don't emit a '.', but adjust the exponent instead. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast(*current); + significant_digits++; + exponent--; + } else { + // Ignore insignificant digits in the fractional part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + if (Advance(¤t, separator_, 10, end)) goto parsing_done; + } + } + + if (!leading_zero && exponent == 0 && significant_digits == 0) { + // If leading_zeros is true then the string contains zeros. + // If exponent < 0 then string was [+-]\.0*... + // If significant_digits != 0 the string is not equal to 0. + // Otherwise there are no digits in the string. + return junk_string_value_; + } + + // Parse exponential part. + if (*current == 'e' || *current == 'E') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + Iterator junk_begin = current; + ++current; + if (current == end) { + if (allow_trailing_junk) { + current = junk_begin; + goto parsing_done; + } else { + return junk_string_value_; + } + } + char exponen_sign = '+'; + if (*current == '+' || *current == '-') { + exponen_sign = static_cast(*current); + ++current; + if (current == end) { + if (allow_trailing_junk) { + current = junk_begin; + goto parsing_done; + } else { + return junk_string_value_; + } + } + } + + if (current == end || *current < '0' || *current > '9') { + if (allow_trailing_junk) { + current = junk_begin; + goto parsing_done; + } else { + return junk_string_value_; + } + } + + const int max_exponent = INT_MAX / 2; + DOUBLE_CONVERSION_ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); + int num = 0; + do { + // Check overflow. + int digit = *current - '0'; + if (num >= max_exponent / 10 + && !(num == max_exponent / 10 && digit <= max_exponent % 10)) { + num = max_exponent; + } else { + num = num * 10 + digit; + } + ++current; + } while (current != end && *current >= '0' && *current <= '9'); + + exponent += (exponen_sign == '-' ? -num : num); + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + if (allow_trailing_spaces) { + AdvanceToNonspace(¤t, end); + } + + parsing_done: + exponent += insignificant_digits; + + if (octal) { + double result; + bool result_is_junk; + char* start = buffer; + result = RadixStringToIeee<3>(&start, + buffer + buffer_pos, + sign, + separator_, + false, // Don't parse as hex_float. + allow_trailing_junk, + junk_string_value_, + read_as_double, + &result_is_junk); + DOUBLE_CONVERSION_ASSERT(!result_is_junk); + *processed_characters_count = static_cast(current - input); + return result; + } + + if (nonzero_digit_dropped) { + buffer[buffer_pos++] = '1'; + exponent--; + } + + DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos] = '\0'; + + // Code above ensures there are no leading zeros and the buffer has fewer than + // kMaxSignificantDecimalDigits characters. Trim trailing zeros. + Vector chars(buffer, buffer_pos); + chars = TrimTrailingZeros(chars); + exponent += buffer_pos - chars.length(); + + double converted; + if (read_as_double) { + converted = StrtodTrimmed(chars, exponent); + } else { + converted = StrtofTrimmed(chars, exponent); + } + *processed_characters_count = static_cast(current - input); + return sign? -converted: converted; +} + + +double StringToDoubleConverter::StringToDouble( + const char* buffer, + int length, + int* processed_characters_count) const { + return StringToIeee(buffer, length, true, processed_characters_count); +} + + +double StringToDoubleConverter::StringToDouble( + const uc16* buffer, + int length, + int* processed_characters_count) const { + return StringToIeee(buffer, length, true, processed_characters_count); +} + + +float StringToDoubleConverter::StringToFloat( + const char* buffer, + int length, + int* processed_characters_count) const { + return static_cast(StringToIeee(buffer, length, false, + processed_characters_count)); +} + + +float StringToDoubleConverter::StringToFloat( + const uc16* buffer, + int length, + int* processed_characters_count) const { + return static_cast(StringToIeee(buffer, length, false, + processed_characters_count)); +} + + +template<> +double StringToDoubleConverter::StringTo( + const char* buffer, + int length, + int* processed_characters_count) const { + return StringToDouble(buffer, length, processed_characters_count); +} + + +template<> +float StringToDoubleConverter::StringTo( + const char* buffer, + int length, + int* processed_characters_count) const { + return StringToFloat(buffer, length, processed_characters_count); +} + + +template<> +double StringToDoubleConverter::StringTo( + const uc16* buffer, + int length, + int* processed_characters_count) const { + return StringToDouble(buffer, length, processed_characters_count); +} + + +template<> +float StringToDoubleConverter::StringTo( + const uc16* buffer, + int length, + int* processed_characters_count) const { + return StringToFloat(buffer, length, processed_characters_count); +} + +} // 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-string-to-double.h b/deps/icu-small/source/i18n/double-conversion-string-to-double.h index 9f6f5307111ac5..c00ea3f4572ca4 100644 --- a/deps/icu-small/source/i18n/double-conversion-string-to-double.h +++ b/deps/icu-small/source/i18n/double-conversion-string-to-double.h @@ -1,256 +1,256 @@ -// © 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 2012 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_STRING_TO_DOUBLE_H_ -#define DOUBLE_CONVERSION_STRING_TO_DOUBLE_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 { - -class StringToDoubleConverter { - public: - // Enumeration for allowing octals and ignoring junk when converting - // strings to numbers. - enum Flags { - NO_FLAGS = 0, - ALLOW_HEX = 1, - ALLOW_OCTALS = 2, - ALLOW_TRAILING_JUNK = 4, - ALLOW_LEADING_SPACES = 8, - ALLOW_TRAILING_SPACES = 16, - ALLOW_SPACES_AFTER_SIGN = 32, - ALLOW_CASE_INSENSITIVITY = 64, - ALLOW_CASE_INSENSIBILITY = 64, // Deprecated - ALLOW_HEX_FLOATS = 128, - }; - - static const uc16 kNoSeparator = '\0'; - - // Flags should be a bit-or combination of the possible Flags-enum. - // - NO_FLAGS: no special flags. - // - ALLOW_HEX: recognizes the prefix "0x". Hex numbers may only be integers. - // Ex: StringToDouble("0x1234") -> 4660.0 - // In StringToDouble("0x1234.56") the characters ".56" are trailing - // junk. The result of the call is hence dependent on - // the ALLOW_TRAILING_JUNK flag and/or the junk value. - // With this flag "0x" is a junk-string. Even with ALLOW_TRAILING_JUNK, - // the string will not be parsed as "0" followed by junk. - // - // - ALLOW_OCTALS: recognizes the prefix "0" for octals: - // If a sequence of octal digits starts with '0', then the number is - // read as octal integer. Octal numbers may only be integers. - // Ex: StringToDouble("01234") -> 668.0 - // StringToDouble("012349") -> 12349.0 // Not a sequence of octal - // // digits. - // In StringToDouble("01234.56") the characters ".56" are trailing - // junk. The result of the call is hence dependent on - // the ALLOW_TRAILING_JUNK flag and/or the junk value. - // In StringToDouble("01234e56") the characters "e56" are trailing - // junk, too. - // - ALLOW_TRAILING_JUNK: ignore trailing characters that are not part of - // a double literal. - // - ALLOW_LEADING_SPACES: skip over leading whitespace, including spaces, - // new-lines, and tabs. - // - ALLOW_TRAILING_SPACES: ignore trailing whitespace. - // - ALLOW_SPACES_AFTER_SIGN: ignore whitespace after the sign. - // Ex: StringToDouble("- 123.2") -> -123.2. - // StringToDouble("+ 123.2") -> 123.2 - // - ALLOW_CASE_INSENSITIVITY: ignore case of characters for special values: - // infinity and nan. - // - ALLOW_HEX_FLOATS: allows hexadecimal float literals. - // This *must* start with "0x" and separate the exponent with "p". - // Examples: 0x1.2p3 == 9.0 - // 0x10.1p0 == 16.0625 - // ALLOW_HEX and ALLOW_HEX_FLOATS are indented. - // - // empty_string_value is returned when an empty string is given as input. - // If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string - // containing only spaces is converted to the 'empty_string_value', too. - // - // junk_string_value is returned when - // a) ALLOW_TRAILING_JUNK is not set, and a junk character (a character not - // part of a double-literal) is found. - // b) ALLOW_TRAILING_JUNK is set, but the string does not start with a - // double literal. - // - // infinity_symbol and nan_symbol are strings that are used to detect - // inputs that represent infinity and NaN. They can be null, in which case - // they are ignored. - // The conversion routine first reads any possible signs. Then it compares the - // following character of the input-string with the first character of - // the infinity, and nan-symbol. If either matches, the function assumes, that - // a match has been found, and expects the following input characters to match - // the remaining characters of the special-value symbol. - // This means that the following restrictions apply to special-value symbols: - // - they must not start with signs ('+', or '-'), - // - they must not have the same first character. - // - they must not start with digits. - // - // If the separator character is not kNoSeparator, then that specific - // character is ignored when in between two valid digits of the significant. - // It is not allowed to appear in the exponent. - // It is not allowed to lead or trail the number. - // It is not allowed to appear twice next to each other. - // - // Examples: - // flags = ALLOW_HEX | ALLOW_TRAILING_JUNK, - // empty_string_value = 0.0, - // junk_string_value = NaN, - // infinity_symbol = "infinity", - // nan_symbol = "nan": - // StringToDouble("0x1234") -> 4660.0. - // StringToDouble("0x1234K") -> 4660.0. - // StringToDouble("") -> 0.0 // empty_string_value. - // StringToDouble(" ") -> NaN // junk_string_value. - // StringToDouble(" 1") -> NaN // junk_string_value. - // StringToDouble("0x") -> NaN // junk_string_value. - // StringToDouble("-123.45") -> -123.45. - // StringToDouble("--123.45") -> NaN // junk_string_value. - // StringToDouble("123e45") -> 123e45. - // StringToDouble("123E45") -> 123e45. - // StringToDouble("123e+45") -> 123e45. - // StringToDouble("123E-45") -> 123e-45. - // StringToDouble("123e") -> 123.0 // trailing junk ignored. - // StringToDouble("123e-") -> 123.0 // trailing junk ignored. - // StringToDouble("+NaN") -> NaN // NaN string literal. - // StringToDouble("-infinity") -> -inf. // infinity literal. - // StringToDouble("Infinity") -> NaN // junk_string_value. - // - // flags = ALLOW_OCTAL | ALLOW_LEADING_SPACES, - // empty_string_value = 0.0, - // junk_string_value = NaN, - // infinity_symbol = NULL, - // nan_symbol = NULL: - // StringToDouble("0x1234") -> NaN // junk_string_value. - // StringToDouble("01234") -> 668.0. - // StringToDouble("") -> 0.0 // empty_string_value. - // StringToDouble(" ") -> 0.0 // empty_string_value. - // StringToDouble(" 1") -> 1.0 - // StringToDouble("0x") -> NaN // junk_string_value. - // StringToDouble("0123e45") -> NaN // junk_string_value. - // StringToDouble("01239E45") -> 1239e45. - // StringToDouble("-infinity") -> NaN // junk_string_value. - // StringToDouble("NaN") -> NaN // junk_string_value. - // - // flags = NO_FLAGS, - // separator = ' ': - // StringToDouble("1 2 3 4") -> 1234.0 - // StringToDouble("1 2") -> NaN // junk_string_value - // StringToDouble("1 000 000.0") -> 1000000.0 - // StringToDouble("1.000 000") -> 1.0 - // StringToDouble("1.0e1 000") -> NaN // junk_string_value - StringToDoubleConverter(int flags, - double empty_string_value, - double junk_string_value, - const char* infinity_symbol, - const char* nan_symbol, - uc16 separator = kNoSeparator) - : flags_(flags), - empty_string_value_(empty_string_value), - junk_string_value_(junk_string_value), - infinity_symbol_(infinity_symbol), - nan_symbol_(nan_symbol), - separator_(separator) { - } - - // Performs the conversion. - // The output parameter 'processed_characters_count' is set to the number - // of characters that have been processed to read the number. - // Spaces than are processed with ALLOW_{LEADING|TRAILING}_SPACES are included - // in the 'processed_characters_count'. Trailing junk is never included. - double StringToDouble(const char* buffer, - int length, - int* processed_characters_count) const; - - // Same as StringToDouble above but for 16 bit characters. - double StringToDouble(const uc16* buffer, - int length, - int* processed_characters_count) const; - - // Same as StringToDouble but reads a float. - // Note that this is not equivalent to static_cast(StringToDouble(...)) - // due to potential double-rounding. - float StringToFloat(const char* buffer, - int length, - int* processed_characters_count) const; - - // Same as StringToFloat above but for 16 bit characters. - float StringToFloat(const uc16* buffer, - int length, - int* processed_characters_count) const; - - // Same as StringToDouble for T = double, and StringToFloat for T = float. - template - T StringTo(const char* buffer, - int length, - int* processed_characters_count) const; - - // Same as StringTo above but for 16 bit characters. - template - T StringTo(const uc16* buffer, - int length, - int* processed_characters_count) const; - - private: - const int flags_; - const double empty_string_value_; - const double junk_string_value_; - const char* const infinity_symbol_; - const char* const nan_symbol_; - const uc16 separator_; - - template - double StringToIeee(Iterator start_pointer, - int length, - bool read_as_double, - int* processed_characters_count) const; - - DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); -}; - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END - -#endif // DOUBLE_CONVERSION_STRING_TO_DOUBLE_H_ -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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 2012 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_STRING_TO_DOUBLE_H_ +#define DOUBLE_CONVERSION_STRING_TO_DOUBLE_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 { + +class StringToDoubleConverter { + public: + // Enumeration for allowing octals and ignoring junk when converting + // strings to numbers. + enum Flags { + NO_FLAGS = 0, + ALLOW_HEX = 1, + ALLOW_OCTALS = 2, + ALLOW_TRAILING_JUNK = 4, + ALLOW_LEADING_SPACES = 8, + ALLOW_TRAILING_SPACES = 16, + ALLOW_SPACES_AFTER_SIGN = 32, + ALLOW_CASE_INSENSITIVITY = 64, + ALLOW_CASE_INSENSIBILITY = 64, // Deprecated + ALLOW_HEX_FLOATS = 128, + }; + + static const uc16 kNoSeparator = '\0'; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - ALLOW_HEX: recognizes the prefix "0x". Hex numbers may only be integers. + // Ex: StringToDouble("0x1234") -> 4660.0 + // In StringToDouble("0x1234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // With this flag "0x" is a junk-string. Even with ALLOW_TRAILING_JUNK, + // the string will not be parsed as "0" followed by junk. + // + // - ALLOW_OCTALS: recognizes the prefix "0" for octals: + // If a sequence of octal digits starts with '0', then the number is + // read as octal integer. Octal numbers may only be integers. + // Ex: StringToDouble("01234") -> 668.0 + // StringToDouble("012349") -> 12349.0 // Not a sequence of octal + // // digits. + // In StringToDouble("01234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // In StringToDouble("01234e56") the characters "e56" are trailing + // junk, too. + // - ALLOW_TRAILING_JUNK: ignore trailing characters that are not part of + // a double literal. + // - ALLOW_LEADING_SPACES: skip over leading whitespace, including spaces, + // new-lines, and tabs. + // - ALLOW_TRAILING_SPACES: ignore trailing whitespace. + // - ALLOW_SPACES_AFTER_SIGN: ignore whitespace after the sign. + // Ex: StringToDouble("- 123.2") -> -123.2. + // StringToDouble("+ 123.2") -> 123.2 + // - ALLOW_CASE_INSENSITIVITY: ignore case of characters for special values: + // infinity and nan. + // - ALLOW_HEX_FLOATS: allows hexadecimal float literals. + // This *must* start with "0x" and separate the exponent with "p". + // Examples: 0x1.2p3 == 9.0 + // 0x10.1p0 == 16.0625 + // ALLOW_HEX and ALLOW_HEX_FLOATS are indented. + // + // empty_string_value is returned when an empty string is given as input. + // If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string + // containing only spaces is converted to the 'empty_string_value', too. + // + // junk_string_value is returned when + // a) ALLOW_TRAILING_JUNK is not set, and a junk character (a character not + // part of a double-literal) is found. + // b) ALLOW_TRAILING_JUNK is set, but the string does not start with a + // double literal. + // + // infinity_symbol and nan_symbol are strings that are used to detect + // inputs that represent infinity and NaN. They can be null, in which case + // they are ignored. + // The conversion routine first reads any possible signs. Then it compares the + // following character of the input-string with the first character of + // the infinity, and nan-symbol. If either matches, the function assumes, that + // a match has been found, and expects the following input characters to match + // the remaining characters of the special-value symbol. + // This means that the following restrictions apply to special-value symbols: + // - they must not start with signs ('+', or '-'), + // - they must not have the same first character. + // - they must not start with digits. + // + // If the separator character is not kNoSeparator, then that specific + // character is ignored when in between two valid digits of the significant. + // It is not allowed to appear in the exponent. + // It is not allowed to lead or trail the number. + // It is not allowed to appear twice next to each other. + // + // Examples: + // flags = ALLOW_HEX | ALLOW_TRAILING_JUNK, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = "infinity", + // nan_symbol = "nan": + // StringToDouble("0x1234") -> 4660.0. + // StringToDouble("0x1234K") -> 4660.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> NaN // junk_string_value. + // StringToDouble(" 1") -> NaN // junk_string_value. + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("-123.45") -> -123.45. + // StringToDouble("--123.45") -> NaN // junk_string_value. + // StringToDouble("123e45") -> 123e45. + // StringToDouble("123E45") -> 123e45. + // StringToDouble("123e+45") -> 123e45. + // StringToDouble("123E-45") -> 123e-45. + // StringToDouble("123e") -> 123.0 // trailing junk ignored. + // StringToDouble("123e-") -> 123.0 // trailing junk ignored. + // StringToDouble("+NaN") -> NaN // NaN string literal. + // StringToDouble("-infinity") -> -inf. // infinity literal. + // StringToDouble("Infinity") -> NaN // junk_string_value. + // + // flags = ALLOW_OCTAL | ALLOW_LEADING_SPACES, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = nullptr, + // nan_symbol = nullptr: + // StringToDouble("0x1234") -> NaN // junk_string_value. + // StringToDouble("01234") -> 668.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> 0.0 // empty_string_value. + // StringToDouble(" 1") -> 1.0 + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("0123e45") -> NaN // junk_string_value. + // StringToDouble("01239E45") -> 1239e45. + // StringToDouble("-infinity") -> NaN // junk_string_value. + // StringToDouble("NaN") -> NaN // junk_string_value. + // + // flags = NO_FLAGS, + // separator = ' ': + // StringToDouble("1 2 3 4") -> 1234.0 + // StringToDouble("1 2") -> NaN // junk_string_value + // StringToDouble("1 000 000.0") -> 1000000.0 + // StringToDouble("1.000 000") -> 1.0 + // StringToDouble("1.0e1 000") -> NaN // junk_string_value + StringToDoubleConverter(int flags, + double empty_string_value, + double junk_string_value, + const char* infinity_symbol, + const char* nan_symbol, + uc16 separator = kNoSeparator) + : flags_(flags), + empty_string_value_(empty_string_value), + junk_string_value_(junk_string_value), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol), + separator_(separator) { + } + + // Performs the conversion. + // The output parameter 'processed_characters_count' is set to the number + // of characters that have been processed to read the number. + // Spaces than are processed with ALLOW_{LEADING|TRAILING}_SPACES are included + // in the 'processed_characters_count'. Trailing junk is never included. + double StringToDouble(const char* buffer, + int length, + int* processed_characters_count) const; + + // Same as StringToDouble above but for 16 bit characters. + double StringToDouble(const uc16* buffer, + int length, + int* processed_characters_count) const; + + // Same as StringToDouble but reads a float. + // Note that this is not equivalent to static_cast(StringToDouble(...)) + // due to potential double-rounding. + float StringToFloat(const char* buffer, + int length, + int* processed_characters_count) const; + + // Same as StringToFloat above but for 16 bit characters. + float StringToFloat(const uc16* buffer, + int length, + int* processed_characters_count) const; + + // Same as StringToDouble for T = double, and StringToFloat for T = float. + template + T StringTo(const char* buffer, + int length, + int* processed_characters_count) const; + + // Same as StringTo above but for 16 bit characters. + template + T StringTo(const uc16* buffer, + int length, + int* processed_characters_count) const; + + private: + const int flags_; + const double empty_string_value_; + const double junk_string_value_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + const uc16 separator_; + + template + double StringToIeee(Iterator start_pointer, + int length, + bool read_as_double, + int* processed_characters_count) const; + + DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); +}; + +} // namespace double_conversion + +// ICU PATCH: Close ICU namespace +U_NAMESPACE_END + +#endif // DOUBLE_CONVERSION_STRING_TO_DOUBLE_H_ +#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/double-conversion-strtod.cpp b/deps/icu-small/source/i18n/double-conversion-strtod.cpp index eea8203281a26d..353a92f157f1af 100644 --- a/deps/icu-small/source/i18n/double-conversion-strtod.cpp +++ b/deps/icu-small/source/i18n/double-conversion-strtod.cpp @@ -1,628 +1,628 @@ -// © 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. - -#include "double-conversion-bignum.h" -#include "double-conversion-cached-powers.h" -#include "double-conversion-ieee.h" -#include "double-conversion-strtod.h" - -// ICU PATCH: Wrap in ICU namespace -U_NAMESPACE_BEGIN - -namespace double_conversion { - -#if defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) -// 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; -#endif // #if defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) -// 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 = DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); - - -#if defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) -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 = DOUBLE_CONVERSION_ARRAY_SIZE(exact_powers_of_ten); -#endif // #if defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) - -// 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 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'. - DOUBLE_CONVERSION_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. - DOUBLE_CONVERSION_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'; - DOUBLE_CONVERSION_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) - // Avoid "unused parameter" warnings - (void) trimmed; - (void) exponent; - (void) result; - // 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. - return false; -#else - 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)); - DOUBLE_CONVERSION_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)); - DOUBLE_CONVERSION_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)); - DOUBLE_CONVERSION_ASSERT(read_digits == trimmed.length()); - *result *= exact_powers_of_ten[remaining_digits]; - *result *= exact_powers_of_ten[exponent - remaining_digits]; - return true; - } - } - return false; -#endif -} - - -// Returns 10^exponent as an exact DiyFp. -// The given exponent must be in the range [1; kDecimalExponentDistance[. -static DiyFp AdjustmentPowerOfTen(int exponent) { - DOUBLE_CONVERSION_ASSERT(0 < exponent); - DOUBLE_CONVERSION_ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); - // Simply hardcode the remaining powers for the given decimal exponent - // distance. - DOUBLE_CONVERSION_ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); - switch (exponent) { - case 1: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xa0000000, 00000000), -60); - case 2: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xc8000000, 00000000), -57); - case 3: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xfa000000, 00000000), -54); - case 4: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0x9c400000, 00000000), -50); - case 5: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xc3500000, 00000000), -47); - case 6: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xf4240000, 00000000), -44); - case 7: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0x98968000, 00000000), -40); - default: - DOUBLE_CONVERSION_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(); - - DOUBLE_CONVERSION_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. - DOUBLE_CONVERSION_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. - DOUBLE_CONVERSION_ASSERT(DiyFp::kSignificandSize == 64); - DOUBLE_CONVERSION_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) { - DOUBLE_CONVERSION_ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); - DOUBLE_CONVERSION_ASSERT(buffer.length() + exponent > kMinDecimalPower); - DOUBLE_CONVERSION_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... - DOUBLE_CONVERSION_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; -} - -#if U_DEBUG // needed for ICU only in debug mode -static bool IsDigit(const char d) { - return ('0' <= d) && (d <= '9'); -} - -static bool IsNonZeroDigit(const char d) { - return ('1' <= d) && (d <= '9'); -} - -#ifdef __has_cpp_attribute -#if __has_cpp_attribute(maybe_unused) -[[maybe_unused]] -#endif -#endif -static bool AssertTrimmedDigits(const Vector& buffer) { - for(int i = 0; i < buffer.length(); ++i) { - if(!IsDigit(buffer[i])) { - return false; - } - } - return (buffer.length() == 0) || (IsNonZeroDigit(buffer[0]) && IsNonZeroDigit(buffer[buffer.length()-1])); -} -#endif // needed for ICU only in debug mode - -double StrtodTrimmed(Vector trimmed, int exponent) { - DOUBLE_CONVERSION_ASSERT(trimmed.length() <= kMaxSignificantDecimalDigits); - DOUBLE_CONVERSION_ASSERT(AssertTrimmedDigits(trimmed)); - double guess; - const 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(); - } -} - -double Strtod(Vector buffer, int exponent) { - char copy_buffer[kMaxSignificantDecimalDigits]; - Vector trimmed; - int updated_exponent; - TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, - &trimmed, &updated_exponent); - return StrtodTrimmed(trimmed, updated_exponent); -} - -static float SanitizedDoubletof(double d) { - DOUBLE_CONVERSION_ASSERT(d >= 0.0); - // ASAN has a sanitize check that disallows casting doubles to floats if - // they are too big. - // https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#available-checks - // The behavior should be covered by IEEE 754, but some projects use this - // flag, so work around it. - float max_finite = 3.4028234663852885981170418348451692544e+38; - // The half-way point between the max-finite and infinity value. - // Since infinity has an even significand everything equal or greater than - // this value should become infinity. - double half_max_finite_infinity = - 3.40282356779733661637539395458142568448e+38; - if (d >= max_finite) { - if (d >= half_max_finite_infinity) { - return Single::Infinity(); - } else { - return max_finite; - } - } else { - return static_cast(d); - } -} - -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; - return StrtofTrimmed(trimmed, exponent); -} - -float StrtofTrimmed(Vector trimmed, int exponent) { - DOUBLE_CONVERSION_ASSERT(trimmed.length() <= kMaxSignificantDecimalDigits); - DOUBLE_CONVERSION_ASSERT(AssertTrimmedDigits(trimmed)); - - double double_guess; - bool is_correct = ComputeGuess(trimmed, exponent, &double_guess); - - float float_guess = SanitizedDoubletof(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 neighbors 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 = SanitizedDoubletof(double_previous); - float f2 = float_guess; - float f3 = SanitizedDoubletof(double_next); - float f4; - if (is_correct) { - f4 = f3; - } else { - double double_next2 = Double(double_next).NextDouble(); - f4 = SanitizedDoubletof(double_next2); - } - (void) f2; // Mark variable as used. - DOUBLE_CONVERSION_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; - } - - DOUBLE_CONVERSION_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 candidates (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 +// © 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. + +#include "double-conversion-bignum.h" +#include "double-conversion-cached-powers.h" +#include "double-conversion-ieee.h" +#include "double-conversion-strtod.h" + +// ICU PATCH: Wrap in ICU namespace +U_NAMESPACE_BEGIN + +namespace double_conversion { + +#if defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) +// 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; +#endif // #if defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) +// 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 = DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); + + +#if defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) +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 = DOUBLE_CONVERSION_ARRAY_SIZE(exact_powers_of_ten); +#endif // #if defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) + +// 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 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'. + DOUBLE_CONVERSION_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. + DOUBLE_CONVERSION_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'; + DOUBLE_CONVERSION_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) + // Avoid "unused parameter" warnings + (void) trimmed; + (void) exponent; + (void) result; + // 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. + return false; +#else + 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)); + DOUBLE_CONVERSION_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)); + DOUBLE_CONVERSION_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)); + DOUBLE_CONVERSION_ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[remaining_digits]; + *result *= exact_powers_of_ten[exponent - remaining_digits]; + return true; + } + } + return false; +#endif +} + + +// Returns 10^exponent as an exact DiyFp. +// The given exponent must be in the range [1; kDecimalExponentDistance[. +static DiyFp AdjustmentPowerOfTen(int exponent) { + DOUBLE_CONVERSION_ASSERT(0 < exponent); + DOUBLE_CONVERSION_ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); + // Simply hardcode the remaining powers for the given decimal exponent + // distance. + DOUBLE_CONVERSION_ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); + switch (exponent) { + case 1: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xa0000000, 00000000), -60); + case 2: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xc8000000, 00000000), -57); + case 3: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xfa000000, 00000000), -54); + case 4: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0x9c400000, 00000000), -50); + case 5: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xc3500000, 00000000), -47); + case 6: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xf4240000, 00000000), -44); + case 7: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0x98968000, 00000000), -40); + default: + DOUBLE_CONVERSION_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(); + + DOUBLE_CONVERSION_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. + DOUBLE_CONVERSION_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. + DOUBLE_CONVERSION_ASSERT(DiyFp::kSignificandSize == 64); + DOUBLE_CONVERSION_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) { + DOUBLE_CONVERSION_ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); + DOUBLE_CONVERSION_ASSERT(buffer.length() + exponent > kMinDecimalPower); + DOUBLE_CONVERSION_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... + DOUBLE_CONVERSION_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; +} + +#if U_DEBUG // needed for ICU only in debug mode +static bool IsDigit(const char d) { + return ('0' <= d) && (d <= '9'); +} + +static bool IsNonZeroDigit(const char d) { + return ('1' <= d) && (d <= '9'); +} + +#ifdef __has_cpp_attribute +#if __has_cpp_attribute(maybe_unused) +[[maybe_unused]] +#endif +#endif +static bool AssertTrimmedDigits(const Vector& buffer) { + for(int i = 0; i < buffer.length(); ++i) { + if(!IsDigit(buffer[i])) { + return false; + } + } + return (buffer.length() == 0) || (IsNonZeroDigit(buffer[0]) && IsNonZeroDigit(buffer[buffer.length()-1])); +} +#endif // needed for ICU only in debug mode + +double StrtodTrimmed(Vector trimmed, int exponent) { + DOUBLE_CONVERSION_ASSERT(trimmed.length() <= kMaxSignificantDecimalDigits); + DOUBLE_CONVERSION_ASSERT(AssertTrimmedDigits(trimmed)); + double guess; + const 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(); + } +} + +double Strtod(Vector buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector trimmed; + int updated_exponent; + TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, + &trimmed, &updated_exponent); + return StrtodTrimmed(trimmed, updated_exponent); +} + +static float SanitizedDoubletof(double d) { + DOUBLE_CONVERSION_ASSERT(d >= 0.0); + // ASAN has a sanitize check that disallows casting doubles to floats if + // they are too big. + // https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#available-checks + // The behavior should be covered by IEEE 754, but some projects use this + // flag, so work around it. + float max_finite = 3.4028234663852885981170418348451692544e+38; + // The half-way point between the max-finite and infinity value. + // Since infinity has an even significand everything equal or greater than + // this value should become infinity. + double half_max_finite_infinity = + 3.40282356779733661637539395458142568448e+38; + if (d >= max_finite) { + if (d >= half_max_finite_infinity) { + return Single::Infinity(); + } else { + return max_finite; + } + } else { + return static_cast(d); + } +} + +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; + return StrtofTrimmed(trimmed, exponent); +} + +float StrtofTrimmed(Vector trimmed, int exponent) { + DOUBLE_CONVERSION_ASSERT(trimmed.length() <= kMaxSignificantDecimalDigits); + DOUBLE_CONVERSION_ASSERT(AssertTrimmedDigits(trimmed)); + + double double_guess; + bool is_correct = ComputeGuess(trimmed, exponent, &double_guess); + + float float_guess = SanitizedDoubletof(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 neighbors 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 = SanitizedDoubletof(double_previous); + float f2 = float_guess; + float f3 = SanitizedDoubletof(double_next); + float f4; + if (is_correct) { + f4 = f3; + } else { + double double_next2 = Double(double_next).NextDouble(); + f4 = SanitizedDoubletof(double_next2); + } + (void) f2; // Mark variable as used. + DOUBLE_CONVERSION_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; + } + + DOUBLE_CONVERSION_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 candidates (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 index abfe00a333102c..258c966f48d0d7 100644 --- a/deps/icu-small/source/i18n/double-conversion-strtod.h +++ b/deps/icu-small/source/i18n/double-conversion-strtod.h @@ -1,82 +1,82 @@ -// © 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); - -// Same as Strtod, but assumes that 'trimmed' is already trimmed, as if run -// through TrimAndCut. That is, 'trimmed' must have no leading or trailing -// zeros, must not be a lone zero, and must not have 'too many' digits. -double StrtodTrimmed(Vector trimmed, int exponent); - -// Same as Strtof, but assumes that 'trimmed' is already trimmed, as if run -// through TrimAndCut. That is, 'trimmed' must have no leading or trailing -// zeros, must not be a lone zero, and must not have 'too many' digits. -float StrtofTrimmed(Vector trimmed, int exponent); - -inline 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); -} - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END - -#endif // DOUBLE_CONVERSION_STRTOD_H_ -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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); + +// Same as Strtod, but assumes that 'trimmed' is already trimmed, as if run +// through TrimAndCut. That is, 'trimmed' must have no leading or trailing +// zeros, must not be a lone zero, and must not have 'too many' digits. +double StrtodTrimmed(Vector trimmed, int exponent); + +// Same as Strtof, but assumes that 'trimmed' is already trimmed, as if run +// through TrimAndCut. That is, 'trimmed' must have no leading or trailing +// zeros, must not be a lone zero, and must not have 'too many' digits. +float StrtofTrimmed(Vector trimmed, int exponent); + +inline 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); +} + +} // 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 303668f9317994..95f8796720cef2 100644 --- a/deps/icu-small/source/i18n/double-conversion-utils.h +++ b/deps/icu-small/source/i18n/double-conversion-utils.h @@ -1,435 +1,435 @@ -// © 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_UTILS_H_ -#define DOUBLE_CONVERSION_UTILS_H_ - -// Use DOUBLE_CONVERSION_NON_PREFIXED_MACROS to get unprefixed macros as was -// the case in double-conversion releases prior to 3.1.6 - -#include -#include - -// For pre-C++11 compatibility -#if __cplusplus >= 201103L -#define DOUBLE_CONVERSION_NULLPTR nullptr -#else -#define DOUBLE_CONVERSION_NULLPTR NULL -#endif - -// ICU PATCH: Use U_ASSERT instead of -#include "uassert.h" -#ifndef DOUBLE_CONVERSION_ASSERT -#define DOUBLE_CONVERSION_ASSERT(condition) \ - U_ASSERT(condition) -#endif -#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(ASSERT) -#define ASSERT DOUBLE_CONVERSION_ASSERT -#endif - -#ifndef DOUBLE_CONVERSION_UNIMPLEMENTED -#define DOUBLE_CONVERSION_UNIMPLEMENTED() (abort()) -#endif -#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(UNIMPLEMENTED) -#define UNIMPLEMENTED DOUBLE_CONVERSION_UNIMPLEMENTED -#endif - -#ifndef DOUBLE_CONVERSION_NO_RETURN -#ifdef _MSC_VER -#define DOUBLE_CONVERSION_NO_RETURN __declspec(noreturn) -#else -#define DOUBLE_CONVERSION_NO_RETURN __attribute__((noreturn)) -#endif -#endif -#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(NO_RETURN) -#define NO_RETURN DOUBLE_CONVERSION_NO_RETURN -#endif - -#ifndef DOUBLE_CONVERSION_UNREACHABLE -#ifdef _MSC_VER -void DOUBLE_CONVERSION_NO_RETURN abort_noreturn(); -inline void abort_noreturn() { abort(); } -#define DOUBLE_CONVERSION_UNREACHABLE() (abort_noreturn()) -#else -#define DOUBLE_CONVERSION_UNREACHABLE() (abort()) -#endif -#endif -#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(UNREACHABLE) -#define UNREACHABLE DOUBLE_CONVERSION_UNREACHABLE -#endif - -// Not all compilers support __has_attribute and combining a check for both -// ifdef and __has_attribute on the same preprocessor line isn't portable. -#ifdef __has_attribute -# define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) __has_attribute(x) -#else -# define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) 0 -#endif - -#ifndef DOUBLE_CONVERSION_UNUSED -#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(unused) -#define DOUBLE_CONVERSION_UNUSED __attribute__((unused)) -#else -#define DOUBLE_CONVERSION_UNUSED -#endif -#endif -#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(UNUSED) -#define UNUSED DOUBLE_CONVERSION_UNUSED -#endif - -#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(uninitialized) -#define DOUBLE_CONVERSION_STACK_UNINITIALIZED __attribute__((uninitialized)) -#else -#define DOUBLE_CONVERSION_STACK_UNINITIALIZED -#endif -#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(STACK_UNINITIALIZED) -#define STACK_UNINITIALIZED DOUBLE_CONVERSION_STACK_UNINITIALIZED -#endif - -// Double operations detection based on target architecture. -// Linux uses a 80bit wide floating point stack on x86. This induces double -// rounding, which in turn leads to wrong results. -// An easy way to test if the floating-point operations are correct is to -// evaluate: 89255.0/1e22. If the floating-point stack is 64 bits wide then -// the result is equal to 89255e-22. -// The best way to test this, is to create a division-function and to compare -// the output of the division with the expected result. (Inlining must be -// disabled.) -// On Linux,x86 89255e-22 != Div_double(89255.0/1e22) -// -// For example: -/* -// -- in div.c -double Div_double(double x, double y) { return x / y; } - -// -- in main.c -double Div_double(double x, double y); // Forward declaration. - -int main(int argc, char** argv) { - return Div_double(89255.0, 1e22) == 89255e-22; -} -*/ -// Run as follows ./main || echo "correct" -// -// If it prints "correct" then the architecture should be here, in the "correct" section. -#if defined(_M_X64) || defined(__x86_64__) || \ - defined(__ARMEL__) || defined(__avr32__) || defined(_M_ARM) || defined(_M_ARM64) || \ - defined(__hppa__) || defined(__ia64__) || \ - defined(__mips__) || \ - defined(__loongarch__) || \ - defined(__nios2__) || defined(__ghs) || \ - defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) || \ - defined(_POWER) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \ - defined(__sparc__) || defined(__sparc) || defined(__s390__) || \ - defined(__SH4__) || defined(__alpha__) || \ - defined(_MIPS_ARCH_MIPS32R2) || defined(__ARMEB__) ||\ - defined(__AARCH64EL__) || defined(__aarch64__) || defined(__AARCH64EB__) || \ - defined(__riscv) || defined(__e2k__) || \ - defined(__or1k__) || defined(__arc__) || defined(__ARC64__) || \ - defined(__microblaze__) || defined(__XTENSA__) || \ - defined(__EMSCRIPTEN__) || defined(__wasm32__) -#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 -#elif defined(__mc68000__) || \ - defined(__pnacl__) || defined(__native_client__) -#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS -#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) -#if defined(_WIN32) -// Windows uses a 64bit wide floating point stack. -#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 -#else -#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS -#endif // _WIN32 -#else -#error Target architecture was not detected as supported by Double-Conversion. -#endif -#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(CORRECT_DOUBLE_OPERATIONS) -#define CORRECT_DOUBLE_OPERATIONS DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS -#endif - -#if defined(_WIN32) && !defined(__MINGW32__) - -typedef signed char int8_t; -typedef unsigned char uint8_t; -typedef short int16_t; // NOLINT -typedef unsigned short uint16_t; // NOLINT -typedef int int32_t; -typedef unsigned int uint32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -// intptr_t and friends are defined in crtdefs.h through stdio.h. - -#else - -#include - -#endif - -typedef uint16_t uc16; - -// The following macro works on both 32 and 64-bit platforms. -// Usage: instead of writing 0x1234567890123456 -// write DOUBLE_CONVERSION_UINT64_2PART_C(0x12345678,90123456); -#define DOUBLE_CONVERSION_UINT64_2PART_C(a, b) (((static_cast(a) << 32) + 0x##b##u)) -#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(UINT64_2PART_C) -#define UINT64_2PART_C DOUBLE_CONVERSION_UINT64_2PART_C -#endif - -// The expression DOUBLE_CONVERSION_ARRAY_SIZE(a) is a compile-time constant of type -// size_t which represents the number of elements of the given -// array. You should only use DOUBLE_CONVERSION_ARRAY_SIZE on statically allocated -// arrays. -#ifndef DOUBLE_CONVERSION_ARRAY_SIZE -#define DOUBLE_CONVERSION_ARRAY_SIZE(a) \ - ((sizeof(a) / sizeof(*(a))) / \ - static_cast(!(sizeof(a) % sizeof(*(a))))) -#endif -#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(ARRAY_SIZE) -#define ARRAY_SIZE DOUBLE_CONVERSION_ARRAY_SIZE -#endif - -// A macro to disallow the evil copy constructor and operator= functions -// This should be used in the private: declarations for a class -#ifndef DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN -#define DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) -#endif -#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(DC_DISALLOW_COPY_AND_ASSIGN) -#define DC_DISALLOW_COPY_AND_ASSIGN DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN -#endif - -// A macro to disallow all the implicit constructors, namely the -// default constructor, copy constructor and operator= functions. -// -// This should be used in the private: declarations for a class -// that wants to prevent anyone from instantiating it. This is -// especially useful for classes containing only static methods. -#ifndef DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS -#define DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ - TypeName(); \ - DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(TypeName) -#endif -#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(DC_DISALLOW_IMPLICIT_CONSTRUCTORS) -#define DC_DISALLOW_IMPLICIT_CONSTRUCTORS DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS -#endif - -// ICU PATCH: Wrap in ICU namespace -U_NAMESPACE_BEGIN - -namespace double_conversion { - -inline int StrLength(const char* string) { - size_t length = strlen(string); - DOUBLE_CONVERSION_ASSERT(length == static_cast(static_cast(length))); - return static_cast(length); -} - -// This is a simplified version of V8's Vector class. -template -class Vector { - public: - Vector() : start_(DOUBLE_CONVERSION_NULLPTR), length_(0) {} - Vector(T* data, int len) : start_(data), length_(len) { - DOUBLE_CONVERSION_ASSERT(len == 0 || (len > 0 && data != DOUBLE_CONVERSION_NULLPTR)); - } - - // Returns a vector using the same backing storage as this one, - // spanning from and including 'from', to but not including 'to'. - Vector SubVector(int from, int to) { - DOUBLE_CONVERSION_ASSERT(to <= length_); - DOUBLE_CONVERSION_ASSERT(from < to); - DOUBLE_CONVERSION_ASSERT(0 <= from); - return Vector(start() + from, to - from); - } - - // Returns the length of the vector. - int length() const { return length_; } - - // Returns whether or not the vector is empty. - bool is_empty() const { return length_ == 0; } - - // Returns the pointer to the start of the data in the vector. - T* start() const { return start_; } - - // Access individual vector elements - checks bounds in debug mode. - T& operator[](int index) const { - DOUBLE_CONVERSION_ASSERT(0 <= index && index < length_); - return start_[index]; - } - - T& first() { return start_[0]; } - - T& last() { return start_[length_ - 1]; } - - void pop_back() { - DOUBLE_CONVERSION_ASSERT(!is_empty()); - --length_; - } - - private: - T* start_; - int length_; -}; - - -// Helper class for building result strings in a character buffer. The -// purpose of the class is to use safe operations that checks the -// buffer bounds on all operations in debug mode. -class StringBuilder { - public: - StringBuilder(char* buffer, int buffer_size) - : buffer_(buffer, buffer_size), position_(0) { } - - ~StringBuilder() { if (!is_finalized()) Finalize(); } - - int size() const { return buffer_.length(); } - - // Get the current position in the builder. - int position() const { - DOUBLE_CONVERSION_ASSERT(!is_finalized()); - return position_; - } - - // Reset the position. - void Reset() { position_ = 0; } - - // Add a single character to the builder. It is not allowed to add - // 0-characters; use the Finalize() method to terminate the string - // instead. - void AddCharacter(char c) { - DOUBLE_CONVERSION_ASSERT(c != '\0'); - DOUBLE_CONVERSION_ASSERT(!is_finalized() && position_ < buffer_.length()); - buffer_[position_++] = c; - } - - // Add an entire string to the builder. Uses strlen() internally to - // compute the length of the input string. - void AddString(const char* s) { - AddSubstring(s, StrLength(s)); - } - - // Add the first 'n' characters of the given string 's' to the - // builder. The input string must have enough characters. - void AddSubstring(const char* s, int n) { - DOUBLE_CONVERSION_ASSERT(!is_finalized() && position_ + n < buffer_.length()); - DOUBLE_CONVERSION_ASSERT(static_cast(n) <= strlen(s)); - memmove(&buffer_[position_], s, static_cast(n)); - position_ += n; - } - - - // Add character padding to the builder. If count is non-positive, - // nothing is added to the builder. - void AddPadding(char c, int count) { - for (int i = 0; i < count; i++) { - AddCharacter(c); - } - } - - // Finalize the string by 0-terminating it and returning the buffer. - char* Finalize() { - DOUBLE_CONVERSION_ASSERT(!is_finalized() && position_ < buffer_.length()); - buffer_[position_] = '\0'; - // Make sure nobody managed to add a 0-character to the - // buffer while building the string. - DOUBLE_CONVERSION_ASSERT(strlen(buffer_.start()) == static_cast(position_)); - position_ = -1; - DOUBLE_CONVERSION_ASSERT(is_finalized()); - return buffer_.start(); - } - - private: - Vector buffer_; - int position_; - - bool is_finalized() const { return position_ < 0; } - - DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); -}; - -// The type-based aliasing rule allows the compiler to assume that pointers of -// different types (for some definition of different) never alias each other. -// Thus the following code does not work: -// -// float f = foo(); -// int fbits = *(int*)(&f); -// -// The compiler 'knows' that the int pointer can't refer to f since the types -// don't match, so the compiler may cache f in a register, leaving random data -// in fbits. Using C++ style casts makes no difference, however a pointer to -// char data is assumed to alias any other pointer. This is the 'memcpy -// exception'. -// -// Bit_cast uses the memcpy exception to move the bits from a variable of one -// type of a variable of another type. Of course the end result is likely to -// be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005) -// will completely optimize BitCast away. -// -// There is an additional use for BitCast. -// Recent gccs will warn when they see casts that may result in breakage due to -// the type-based aliasing rule. If you have checked that there is no breakage -// you can use BitCast to cast one pointer type to another. This confuses gcc -// enough that it can no longer see that you have cast one pointer type to -// another thus avoiding the warning. -template -Dest BitCast(const Source& source) { - // Compile time assertion: sizeof(Dest) == sizeof(Source) - // A compile error here means your Dest and Source have different sizes. -#if __cplusplus >= 201103L - static_assert(sizeof(Dest) == sizeof(Source), - "source and destination size mismatch"); -#else - DOUBLE_CONVERSION_UNUSED - typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1]; -#endif - - Dest dest; - memmove(&dest, &source, sizeof(dest)); - return dest; -} - -template -Dest BitCast(Source* source) { - return BitCast(reinterpret_cast(source)); -} - -} // namespace double_conversion - -// ICU PATCH: Close ICU namespace -U_NAMESPACE_END - -#endif // DOUBLE_CONVERSION_UTILS_H_ -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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_UTILS_H_ +#define DOUBLE_CONVERSION_UTILS_H_ + +// Use DOUBLE_CONVERSION_NON_PREFIXED_MACROS to get unprefixed macros as was +// the case in double-conversion releases prior to 3.1.6 + +#include +#include + +// For pre-C++11 compatibility +#if __cplusplus >= 201103L +#define DOUBLE_CONVERSION_NULLPTR nullptr +#else +#define DOUBLE_CONVERSION_NULLPTR NULL +#endif + +// ICU PATCH: Use U_ASSERT instead of +#include "uassert.h" +#ifndef DOUBLE_CONVERSION_ASSERT +#define DOUBLE_CONVERSION_ASSERT(condition) \ + U_ASSERT(condition) +#endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(ASSERT) +#define ASSERT DOUBLE_CONVERSION_ASSERT +#endif + +#ifndef DOUBLE_CONVERSION_UNIMPLEMENTED +#define DOUBLE_CONVERSION_UNIMPLEMENTED() (abort()) +#endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(UNIMPLEMENTED) +#define UNIMPLEMENTED DOUBLE_CONVERSION_UNIMPLEMENTED +#endif + +#ifndef DOUBLE_CONVERSION_NO_RETURN +#ifdef _MSC_VER +#define DOUBLE_CONVERSION_NO_RETURN __declspec(noreturn) +#else +#define DOUBLE_CONVERSION_NO_RETURN __attribute__((noreturn)) +#endif +#endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(NO_RETURN) +#define NO_RETURN DOUBLE_CONVERSION_NO_RETURN +#endif + +#ifndef DOUBLE_CONVERSION_UNREACHABLE +#ifdef _MSC_VER +void DOUBLE_CONVERSION_NO_RETURN abort_noreturn(); +inline void abort_noreturn() { abort(); } +#define DOUBLE_CONVERSION_UNREACHABLE() (abort_noreturn()) +#else +#define DOUBLE_CONVERSION_UNREACHABLE() (abort()) +#endif +#endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(UNREACHABLE) +#define UNREACHABLE DOUBLE_CONVERSION_UNREACHABLE +#endif + +// Not all compilers support __has_attribute and combining a check for both +// ifdef and __has_attribute on the same preprocessor line isn't portable. +#ifdef __has_attribute +# define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) __has_attribute(x) +#else +# define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) 0 +#endif + +#ifndef DOUBLE_CONVERSION_UNUSED +#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(unused) +#define DOUBLE_CONVERSION_UNUSED __attribute__((unused)) +#else +#define DOUBLE_CONVERSION_UNUSED +#endif +#endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(UNUSED) +#define UNUSED DOUBLE_CONVERSION_UNUSED +#endif + +#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(uninitialized) +#define DOUBLE_CONVERSION_STACK_UNINITIALIZED __attribute__((uninitialized)) +#else +#define DOUBLE_CONVERSION_STACK_UNINITIALIZED +#endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(STACK_UNINITIALIZED) +#define STACK_UNINITIALIZED DOUBLE_CONVERSION_STACK_UNINITIALIZED +#endif + +// Double operations detection based on target architecture. +// Linux uses a 80bit wide floating point stack on x86. This induces double +// rounding, which in turn leads to wrong results. +// An easy way to test if the floating-point operations are correct is to +// evaluate: 89255.0/1e22. If the floating-point stack is 64 bits wide then +// the result is equal to 89255e-22. +// The best way to test this, is to create a division-function and to compare +// the output of the division with the expected result. (Inlining must be +// disabled.) +// On Linux,x86 89255e-22 != Div_double(89255.0/1e22) +// +// For example: +/* +// -- in div.c +double Div_double(double x, double y) { return x / y; } + +// -- in main.c +double Div_double(double x, double y); // Forward declaration. + +int main(int argc, char** argv) { + return Div_double(89255.0, 1e22) == 89255e-22; +} +*/ +// Run as follows ./main || echo "correct" +// +// If it prints "correct" then the architecture should be here, in the "correct" section. +#if defined(_M_X64) || defined(__x86_64__) || \ + defined(__ARMEL__) || defined(__avr32__) || defined(_M_ARM) || defined(_M_ARM64) || \ + defined(__hppa__) || defined(__ia64__) || \ + defined(__mips__) || \ + defined(__loongarch__) || \ + defined(__nios2__) || defined(__ghs) || \ + defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) || \ + defined(_POWER) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \ + defined(__sparc__) || defined(__sparc) || defined(__s390__) || \ + defined(__SH4__) || defined(__alpha__) || \ + defined(_MIPS_ARCH_MIPS32R2) || defined(__ARMEB__) ||\ + defined(__AARCH64EL__) || defined(__aarch64__) || defined(__AARCH64EB__) || \ + defined(__riscv) || defined(__e2k__) || \ + defined(__or1k__) || defined(__arc__) || defined(__ARC64__) || \ + defined(__microblaze__) || defined(__XTENSA__) || \ + defined(__EMSCRIPTEN__) || defined(__wasm32__) +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#elif defined(__mc68000__) || \ + defined(__pnacl__) || defined(__native_client__) +#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS +#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) +#if defined(_WIN32) +// Windows uses a 64bit wide floating point stack. +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#else +#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS +#endif // _WIN32 +#else +#error Target architecture was not detected as supported by Double-Conversion. +#endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(CORRECT_DOUBLE_OPERATIONS) +#define CORRECT_DOUBLE_OPERATIONS DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS +#endif + +#if defined(_WIN32) && !defined(__MINGW32__) + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; // NOLINT +typedef unsigned short uint16_t; // NOLINT +typedef int int32_t; +typedef unsigned int uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +// intptr_t and friends are defined in crtdefs.h through stdio.h. + +#else + +#include + +#endif + +typedef uint16_t uc16; + +// The following macro works on both 32 and 64-bit platforms. +// Usage: instead of writing 0x1234567890123456 +// write DOUBLE_CONVERSION_UINT64_2PART_C(0x12345678,90123456); +#define DOUBLE_CONVERSION_UINT64_2PART_C(a, b) (((static_cast(a) << 32) + 0x##b##u)) +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(UINT64_2PART_C) +#define UINT64_2PART_C DOUBLE_CONVERSION_UINT64_2PART_C +#endif + +// The expression DOUBLE_CONVERSION_ARRAY_SIZE(a) is a compile-time constant of type +// size_t which represents the number of elements of the given +// array. You should only use DOUBLE_CONVERSION_ARRAY_SIZE on statically allocated +// arrays. +#ifndef DOUBLE_CONVERSION_ARRAY_SIZE +#define DOUBLE_CONVERSION_ARRAY_SIZE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast(!(sizeof(a) % sizeof(*(a))))) +#endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(ARRAY_SIZE) +#define ARRAY_SIZE DOUBLE_CONVERSION_ARRAY_SIZE +#endif + +// A macro to disallow the evil copy constructor and operator= functions +// This should be used in the private: declarations for a class +#ifndef DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN +#define DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(DC_DISALLOW_COPY_AND_ASSIGN) +#define DC_DISALLOW_COPY_AND_ASSIGN DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN +#endif + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#ifndef DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS +#define DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(TypeName) +#endif +#if defined(DOUBLE_CONVERSION_NON_PREFIXED_MACROS) && !defined(DC_DISALLOW_IMPLICIT_CONSTRUCTORS) +#define DC_DISALLOW_IMPLICIT_CONSTRUCTORS DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS +#endif + +// ICU PATCH: Wrap in ICU namespace +U_NAMESPACE_BEGIN + +namespace double_conversion { + +inline int StrLength(const char* string) { + size_t length = strlen(string); + DOUBLE_CONVERSION_ASSERT(length == static_cast(static_cast(length))); + return static_cast(length); +} + +// This is a simplified version of V8's Vector class. +template +class Vector { + public: + Vector() : start_(DOUBLE_CONVERSION_NULLPTR), length_(0) {} + Vector(T* data, int len) : start_(data), length_(len) { + DOUBLE_CONVERSION_ASSERT(len == 0 || (len > 0 && data != DOUBLE_CONVERSION_NULLPTR)); + } + + // Returns a vector using the same backing storage as this one, + // spanning from and including 'from', to but not including 'to'. + Vector SubVector(int from, int to) { + DOUBLE_CONVERSION_ASSERT(to <= length_); + DOUBLE_CONVERSION_ASSERT(from < to); + DOUBLE_CONVERSION_ASSERT(0 <= from); + return Vector(start() + from, to - from); + } + + // Returns the length of the vector. + int length() const { return length_; } + + // Returns whether or not the vector is empty. + bool is_empty() const { return length_ == 0; } + + // Returns the pointer to the start of the data in the vector. + T* start() const { return start_; } + + // Access individual vector elements - checks bounds in debug mode. + T& operator[](int index) const { + DOUBLE_CONVERSION_ASSERT(0 <= index && index < length_); + return start_[index]; + } + + T& first() { return start_[0]; } + + T& last() { return start_[length_ - 1]; } + + void pop_back() { + DOUBLE_CONVERSION_ASSERT(!is_empty()); + --length_; + } + + private: + T* start_; + int length_; +}; + + +// Helper class for building result strings in a character buffer. The +// purpose of the class is to use safe operations that checks the +// buffer bounds on all operations in debug mode. +class StringBuilder { + public: + StringBuilder(char* buffer, int buffer_size) + : buffer_(buffer, buffer_size), position_(0) { } + + ~StringBuilder() { if (!is_finalized()) Finalize(); } + + int size() const { return buffer_.length(); } + + // Get the current position in the builder. + int position() const { + DOUBLE_CONVERSION_ASSERT(!is_finalized()); + return position_; + } + + // Reset the position. + void Reset() { position_ = 0; } + + // Add a single character to the builder. It is not allowed to add + // 0-characters; use the Finalize() method to terminate the string + // instead. + void AddCharacter(char c) { + DOUBLE_CONVERSION_ASSERT(c != '\0'); + DOUBLE_CONVERSION_ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_++] = c; + } + + // Add an entire string to the builder. Uses strlen() internally to + // compute the length of the input string. + void AddString(const char* s) { + AddSubstring(s, StrLength(s)); + } + + // Add the first 'n' characters of the given string 's' to the + // builder. The input string must have enough characters. + void AddSubstring(const char* s, int n) { + DOUBLE_CONVERSION_ASSERT(!is_finalized() && position_ + n < buffer_.length()); + DOUBLE_CONVERSION_ASSERT(static_cast(n) <= strlen(s)); + memmove(&buffer_[position_], s, static_cast(n)); + position_ += n; + } + + + // Add character padding to the builder. If count is non-positive, + // nothing is added to the builder. + void AddPadding(char c, int count) { + for (int i = 0; i < count; i++) { + AddCharacter(c); + } + } + + // Finalize the string by 0-terminating it and returning the buffer. + char* Finalize() { + DOUBLE_CONVERSION_ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_] = '\0'; + // Make sure nobody managed to add a 0-character to the + // buffer while building the string. + DOUBLE_CONVERSION_ASSERT(strlen(buffer_.start()) == static_cast(position_)); + position_ = -1; + DOUBLE_CONVERSION_ASSERT(is_finalized()); + return buffer_.start(); + } + + private: + Vector buffer_; + int position_; + + bool is_finalized() const { return position_ < 0; } + + DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); +}; + +// The type-based aliasing rule allows the compiler to assume that pointers of +// different types (for some definition of different) never alias each other. +// Thus the following code does not work: +// +// float f = foo(); +// int fbits = *(int*)(&f); +// +// The compiler 'knows' that the int pointer can't refer to f since the types +// don't match, so the compiler may cache f in a register, leaving random data +// in fbits. Using C++ style casts makes no difference, however a pointer to +// char data is assumed to alias any other pointer. This is the 'memcpy +// exception'. +// +// Bit_cast uses the memcpy exception to move the bits from a variable of one +// type of a variable of another type. Of course the end result is likely to +// be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005) +// will completely optimize BitCast away. +// +// There is an additional use for BitCast. +// Recent gccs will warn when they see casts that may result in breakage due to +// the type-based aliasing rule. If you have checked that there is no breakage +// you can use BitCast to cast one pointer type to another. This confuses gcc +// enough that it can no longer see that you have cast one pointer type to +// another thus avoiding the warning. +template +Dest BitCast(const Source& source) { + // Compile time assertion: sizeof(Dest) == sizeof(Source) + // A compile error here means your Dest and Source have different sizes. +#if __cplusplus >= 201103L + static_assert(sizeof(Dest) == sizeof(Source), + "source and destination size mismatch"); +#else + DOUBLE_CONVERSION_UNUSED + typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1]; +#endif + + Dest dest; + memmove(&dest, &source, sizeof(dest)); + return dest; +} + +template +Dest BitCast(Source* source) { + return BitCast(reinterpret_cast(source)); +} + +} // namespace double_conversion + +// ICU PATCH: Close ICU namespace +U_NAMESPACE_END + +#endif // DOUBLE_CONVERSION_UTILS_H_ +#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/double-conversion.h b/deps/icu-small/source/i18n/double-conversion.h index eddc38763becdc..b930e018046a9a 100644 --- a/deps/icu-small/source/i18n/double-conversion.h +++ b/deps/icu-small/source/i18n/double-conversion.h @@ -1,46 +1,46 @@ -// © 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 2012 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_DOUBLE_CONVERSION_H_ -#define DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ - -// ICU PATCH: Customize header file paths for ICU. - -#include "double-conversion-string-to-double.h" -#include "double-conversion-double-to-string.h" - -#endif // DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ -#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING +// © 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 2012 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_DOUBLE_CONVERSION_H_ +#define DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ + +// ICU PATCH: Customize header file paths for ICU. + +#include "double-conversion-string-to-double.h" +#include "double-conversion-double-to-string.h" + +#endif // DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ +#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/dt_impl.h b/deps/icu-small/source/i18n/dt_impl.h index a4058c69244d0d..72b0a418ac223f 100644 --- a/deps/icu-small/source/i18n/dt_impl.h +++ b/deps/icu-small/source/i18n/dt_impl.h @@ -1,92 +1,92 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2007-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File dt_impl.h -* -******************************************************************************* -*/ - - -#ifndef DT_IMPL_H__ -#define DT_IMPL_H__ - -/** - * \file - * \brief C++ API: Defines macros for interval format implementation - */ - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/unistr.h" - - -#define QUOTE ((UChar)0x0027) -#define LOW_LINE ((UChar)0x005F) -#define COLON ((UChar)0x003A) -#define LEFT_CURLY_BRACKET ((UChar)0x007B) -#define RIGHT_CURLY_BRACKET ((UChar)0x007D) -#define SPACE ((UChar)0x0020) -#define EN_DASH ((UChar)0x2013) -#define SOLIDUS ((UChar)0x002F) -#define PERCENT ((UChar)0x0025) - -#define DIGIT_ZERO ((UChar)0x0030) -#define DIGIT_ONE ((UChar)0x0031) - -#define LOW_A ((UChar)0x0061) -#define LOW_B ((UChar)0x0062) -#define LOW_C ((UChar)0x0063) -#define LOW_D ((UChar)0x0064) -#define LOW_E ((UChar)0x0065) -#define LOW_F ((UChar)0x0066) -#define LOW_G ((UChar)0x0067) -#define LOW_H ((UChar)0x0068) -#define LOW_I ((UChar)0x0069) -#define LOW_J ((UChar)0x006a) -#define LOW_K ((UChar)0x006B) -#define LOW_L ((UChar)0x006C) -#define LOW_M ((UChar)0x006D) -#define LOW_N ((UChar)0x006E) -#define LOW_O ((UChar)0x006F) -#define LOW_P ((UChar)0x0070) -#define LOW_Q ((UChar)0x0071) -#define LOW_R ((UChar)0x0072) -#define LOW_S ((UChar)0x0073) -#define LOW_T ((UChar)0x0074) -#define LOW_U ((UChar)0x0075) -#define LOW_V ((UChar)0x0076) -#define LOW_W ((UChar)0x0077) -#define LOW_Y ((UChar)0x0079) -#define LOW_Z ((UChar)0x007A) - -#define CAP_A ((UChar)0x0041) -#define CAP_C ((UChar)0x0043) -#define CAP_D ((UChar)0x0044) -#define CAP_E ((UChar)0x0045) -#define CAP_F ((UChar)0x0046) -#define CAP_G ((UChar)0x0047) -#define CAP_H ((UChar)0x0048) -#define CAP_K ((UChar)0x004B) -#define CAP_L ((UChar)0x004C) -#define CAP_M ((UChar)0x004D) -#define CAP_N ((UChar)0x004E) -#define CAP_O ((UChar)0x004F) -#define CAP_P ((UChar)0x0050) -#define CAP_Q ((UChar)0x0051) -#define CAP_S ((UChar)0x0053) -#define CAP_T ((UChar)0x0054) -#define CAP_U ((UChar)0x0055) -#define CAP_V ((UChar)0x0056) -#define CAP_W ((UChar)0x0057) -#define CAP_Y ((UChar)0x0059) -#define CAP_Z ((UChar)0x005A) - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -#endif -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2007-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File dt_impl.h +* +******************************************************************************* +*/ + + +#ifndef DT_IMPL_H__ +#define DT_IMPL_H__ + +/** + * \file + * \brief C++ API: Defines macros for interval format implementation + */ + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/unistr.h" + + +#define QUOTE ((char16_t)0x0027) +#define LOW_LINE ((char16_t)0x005F) +#define COLON ((char16_t)0x003A) +#define LEFT_CURLY_BRACKET ((char16_t)0x007B) +#define RIGHT_CURLY_BRACKET ((char16_t)0x007D) +#define SPACE ((char16_t)0x0020) +#define EN_DASH ((char16_t)0x2013) +#define SOLIDUS ((char16_t)0x002F) +#define PERCENT ((char16_t)0x0025) + +#define DIGIT_ZERO ((char16_t)0x0030) +#define DIGIT_ONE ((char16_t)0x0031) + +#define LOW_A ((char16_t)0x0061) +#define LOW_B ((char16_t)0x0062) +#define LOW_C ((char16_t)0x0063) +#define LOW_D ((char16_t)0x0064) +#define LOW_E ((char16_t)0x0065) +#define LOW_F ((char16_t)0x0066) +#define LOW_G ((char16_t)0x0067) +#define LOW_H ((char16_t)0x0068) +#define LOW_I ((char16_t)0x0069) +#define LOW_J ((char16_t)0x006a) +#define LOW_K ((char16_t)0x006B) +#define LOW_L ((char16_t)0x006C) +#define LOW_M ((char16_t)0x006D) +#define LOW_N ((char16_t)0x006E) +#define LOW_O ((char16_t)0x006F) +#define LOW_P ((char16_t)0x0070) +#define LOW_Q ((char16_t)0x0071) +#define LOW_R ((char16_t)0x0072) +#define LOW_S ((char16_t)0x0073) +#define LOW_T ((char16_t)0x0074) +#define LOW_U ((char16_t)0x0075) +#define LOW_V ((char16_t)0x0076) +#define LOW_W ((char16_t)0x0077) +#define LOW_Y ((char16_t)0x0079) +#define LOW_Z ((char16_t)0x007A) + +#define CAP_A ((char16_t)0x0041) +#define CAP_C ((char16_t)0x0043) +#define CAP_D ((char16_t)0x0044) +#define CAP_E ((char16_t)0x0045) +#define CAP_F ((char16_t)0x0046) +#define CAP_G ((char16_t)0x0047) +#define CAP_H ((char16_t)0x0048) +#define CAP_K ((char16_t)0x004B) +#define CAP_L ((char16_t)0x004C) +#define CAP_M ((char16_t)0x004D) +#define CAP_N ((char16_t)0x004E) +#define CAP_O ((char16_t)0x004F) +#define CAP_P ((char16_t)0x0050) +#define CAP_Q ((char16_t)0x0051) +#define CAP_S ((char16_t)0x0053) +#define CAP_T ((char16_t)0x0054) +#define CAP_U ((char16_t)0x0055) +#define CAP_V ((char16_t)0x0056) +#define CAP_W ((char16_t)0x0057) +#define CAP_Y ((char16_t)0x0059) +#define CAP_Z ((char16_t)0x005A) + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif +//eof diff --git a/deps/icu-small/source/i18n/dtfmtsym.cpp b/deps/icu-small/source/i18n/dtfmtsym.cpp index 4c449761249003..61ef9beab13e79 100644 --- a/deps/icu-small/source/i18n/dtfmtsym.cpp +++ b/deps/icu-small/source/i18n/dtfmtsym.cpp @@ -1,2516 +1,2556 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2016, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -* -* File DTFMTSYM.CPP -* -* Modification History: -* -* Date Name Description -* 02/19/97 aliu Converted from java. -* 07/21/98 stephen Added getZoneIndex -* Changed weekdays/short weekdays to be one-based -* 06/14/99 stephen Removed SimpleDateFormat::fgTimeZoneDataSuffix -* 11/16/99 weiv Added 'Y' and 'e' to fgPatternChars -* 03/27/00 weiv Keeping resource bundle around! -* 06/30/05 emmons Added eraNames, narrow month/day, standalone context -* 10/12/05 emmons Added setters for eraNames, month/day by width/context -******************************************************************************* -*/ - -#include - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING -#include "unicode/ustring.h" -#include "unicode/localpointer.h" -#include "unicode/dtfmtsym.h" -#include "unicode/smpdtfmt.h" -#include "unicode/msgfmt.h" -#include "unicode/numsys.h" -#include "unicode/tznames.h" -#include "cpputils.h" -#include "umutex.h" -#include "cmemory.h" -#include "cstring.h" -#include "charstr.h" -#include "dt_impl.h" -#include "locbased.h" -#include "gregoimp.h" -#include "hash.h" -#include "uassert.h" -#include "uresimp.h" -#include "ureslocs.h" -#include "uvector.h" -#include "shareddateformatsymbols.h" -#include "unicode/calendar.h" -#include "unifiedcache.h" - -// ***************************************************************************** -// class DateFormatSymbols -// ***************************************************************************** - -/** - * These are static arrays we use only in the case where we have no - * resource data. - */ - -#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR -#define PATTERN_CHARS_LEN 38 -#else -#define PATTERN_CHARS_LEN 37 -#endif - -/** - * Unlocalized date-time pattern characters. For example: 'y', 'd', etc. All - * locales use the same these unlocalized pattern characters. - */ -static const UChar gPatternChars[] = { - // if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR: - // GyMdkHmsSEDFwWahKzYeugAZvcLQqVUOXxrbB: - // else: - // GyMdkHmsSEDFwWahKzYeugAZvcLQqVUOXxrbB - - 0x47, 0x79, 0x4D, 0x64, 0x6B, 0x48, 0x6D, 0x73, 0x53, 0x45, - 0x44, 0x46, 0x77, 0x57, 0x61, 0x68, 0x4B, 0x7A, 0x59, 0x65, - 0x75, 0x67, 0x41, 0x5A, 0x76, 0x63, 0x4c, 0x51, 0x71, 0x56, - 0x55, 0x4F, 0x58, 0x78, 0x72, 0x62, 0x42, -#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR - 0x3a, -#endif - 0 -}; - -//------------------------------------------------------ -// Strings of last resort. These are only used if we have no resource -// files. They aren't designed for actual use, just for backup. - -// These are the month names and abbreviations of last resort. -static const UChar gLastResortMonthNames[13][3] = -{ - {0x0030, 0x0031, 0x0000}, /* "01" */ - {0x0030, 0x0032, 0x0000}, /* "02" */ - {0x0030, 0x0033, 0x0000}, /* "03" */ - {0x0030, 0x0034, 0x0000}, /* "04" */ - {0x0030, 0x0035, 0x0000}, /* "05" */ - {0x0030, 0x0036, 0x0000}, /* "06" */ - {0x0030, 0x0037, 0x0000}, /* "07" */ - {0x0030, 0x0038, 0x0000}, /* "08" */ - {0x0030, 0x0039, 0x0000}, /* "09" */ - {0x0031, 0x0030, 0x0000}, /* "10" */ - {0x0031, 0x0031, 0x0000}, /* "11" */ - {0x0031, 0x0032, 0x0000}, /* "12" */ - {0x0031, 0x0033, 0x0000} /* "13" */ -}; - -// These are the weekday names and abbreviations of last resort. -static const UChar gLastResortDayNames[8][2] = -{ - {0x0030, 0x0000}, /* "0" */ - {0x0031, 0x0000}, /* "1" */ - {0x0032, 0x0000}, /* "2" */ - {0x0033, 0x0000}, /* "3" */ - {0x0034, 0x0000}, /* "4" */ - {0x0035, 0x0000}, /* "5" */ - {0x0036, 0x0000}, /* "6" */ - {0x0037, 0x0000} /* "7" */ -}; - -// These are the quarter names and abbreviations of last resort. -static const UChar gLastResortQuarters[4][2] = -{ - {0x0031, 0x0000}, /* "1" */ - {0x0032, 0x0000}, /* "2" */ - {0x0033, 0x0000}, /* "3" */ - {0x0034, 0x0000}, /* "4" */ -}; - -// These are the am/pm and BC/AD markers of last resort. -static const UChar gLastResortAmPmMarkers[2][3] = -{ - {0x0041, 0x004D, 0x0000}, /* "AM" */ - {0x0050, 0x004D, 0x0000} /* "PM" */ -}; - -static const UChar gLastResortEras[2][3] = -{ - {0x0042, 0x0043, 0x0000}, /* "BC" */ - {0x0041, 0x0044, 0x0000} /* "AD" */ -}; - -/* Sizes for the last resort string arrays */ -typedef enum LastResortSize { - kMonthNum = 13, - kMonthLen = 3, - - kDayNum = 8, - kDayLen = 2, - - kAmPmNum = 2, - kAmPmLen = 3, - - kQuarterNum = 4, - kQuarterLen = 2, - - kEraNum = 2, - kEraLen = 3, - - kZoneNum = 5, - kZoneLen = 4, - - kGmtHourNum = 4, - kGmtHourLen = 10 -} LastResortSize; - -U_NAMESPACE_BEGIN - -SharedDateFormatSymbols::~SharedDateFormatSymbols() { -} - -template<> U_I18N_API -const SharedDateFormatSymbols * - LocaleCacheKey::createObject( - const void * /*unusedContext*/, UErrorCode &status) const { - char type[256]; - Calendar::getCalendarTypeFromLocale(fLoc, type, UPRV_LENGTHOF(type), status); - if (U_FAILURE(status)) { - return NULL; - } - SharedDateFormatSymbols *shared - = new SharedDateFormatSymbols(fLoc, type, status); - if (shared == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - if (U_FAILURE(status)) { - delete shared; - return NULL; - } - shared->addRef(); - return shared; -} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateFormatSymbols) - -#define kSUPPLEMENTAL "supplementalData" - -/** - * These are the tags we expect to see in normal resource bundle files associated - * with a locale and calendar - */ -static const char gCalendarTag[]="calendar"; -static const char gGregorianTag[]="gregorian"; -static const char gErasTag[]="eras"; -static const char gCyclicNameSetsTag[]="cyclicNameSets"; -static const char gNameSetYearsTag[]="years"; -static const char gNameSetZodiacsTag[]="zodiacs"; -static const char gMonthNamesTag[]="monthNames"; -static const char gMonthPatternsTag[]="monthPatterns"; -static const char gDayNamesTag[]="dayNames"; -static const char gNamesWideTag[]="wide"; -static const char gNamesAbbrTag[]="abbreviated"; -static const char gNamesShortTag[]="short"; -static const char gNamesNarrowTag[]="narrow"; -static const char gNamesAllTag[]="all"; -static const char gNamesFormatTag[]="format"; -static const char gNamesStandaloneTag[]="stand-alone"; -static const char gNamesNumericTag[]="numeric"; -static const char gAmPmMarkersTag[]="AmPmMarkers"; -static const char gAmPmMarkersAbbrTag[]="AmPmMarkersAbbr"; -static const char gAmPmMarkersNarrowTag[]="AmPmMarkersNarrow"; -static const char gQuartersTag[]="quarters"; -static const char gNumberElementsTag[]="NumberElements"; -static const char gSymbolsTag[]="symbols"; -static const char gTimeSeparatorTag[]="timeSeparator"; -static const char gDayPeriodTag[]="dayPeriod"; - -// static const char gZoneStringsTag[]="zoneStrings"; - -// static const char gLocalPatternCharsTag[]="localPatternChars"; - -static const char gContextTransformsTag[]="contextTransforms"; - -/** - * Jitterbug 2974: MSVC has a bug whereby new X[0] behaves badly. - * Work around this. - */ -static inline UnicodeString* newUnicodeStringArray(size_t count) { - return new UnicodeString[count ? count : 1]; -} - -//------------------------------------------------------ - -DateFormatSymbols * U_EXPORT2 -DateFormatSymbols::createForLocale( - const Locale& locale, UErrorCode &status) { - const SharedDateFormatSymbols *shared = NULL; - UnifiedCache::getByLocale(locale, shared, status); - if (U_FAILURE(status)) { - return NULL; - } - DateFormatSymbols *result = new DateFormatSymbols(shared->get()); - shared->removeRef(); - if (result == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - return result; -} - -DateFormatSymbols::DateFormatSymbols(const Locale& locale, - UErrorCode& status) - : UObject() -{ - initializeData(locale, NULL, status); -} - -DateFormatSymbols::DateFormatSymbols(UErrorCode& status) - : UObject() -{ - initializeData(Locale::getDefault(), NULL, status, true); -} - - -DateFormatSymbols::DateFormatSymbols(const Locale& locale, - const char *type, - UErrorCode& status) - : UObject() -{ - initializeData(locale, type, status); -} - -DateFormatSymbols::DateFormatSymbols(const char *type, UErrorCode& status) - : UObject() -{ - initializeData(Locale::getDefault(), type, status, true); -} - -DateFormatSymbols::DateFormatSymbols(const DateFormatSymbols& other) - : UObject(other) -{ - copyData(other); -} - -void -DateFormatSymbols::assignArray(UnicodeString*& dstArray, - int32_t& dstCount, - const UnicodeString* srcArray, - int32_t srcCount) -{ - // assignArray() is only called by copyData() and initializeData(), which in turn - // implements the copy constructor and the assignment operator. - // All strings in a DateFormatSymbols object are created in one of the following - // three ways that all allow to safely use UnicodeString::fastCopyFrom(): - // - readonly-aliases from resource bundles - // - readonly-aliases or allocated strings from constants - // - safely cloned strings (with owned buffers) from setXYZ() functions - // - // Note that this is true for as long as DateFormatSymbols can be constructed - // only from a locale bundle or set via the cloning API, - // *and* for as long as all the strings are in *private* fields, preventing - // a subclass from creating these strings in an "unsafe" way (with respect to fastCopyFrom()). - dstCount = srcCount; - dstArray = newUnicodeStringArray(srcCount); - if(dstArray != NULL) { - int32_t i; - for(i=0; i= 0; i--) { - delete[] fZoneStrings[i]; - } - uprv_free(fZoneStrings); - fZoneStrings = NULL; - } -} - -/** - * Copy all of the other's data to this. - */ -void -DateFormatSymbols::copyData(const DateFormatSymbols& other) { - UErrorCode status = U_ZERO_ERROR; - U_LOCALE_BASED(locBased, *this); - locBased.setLocaleIDs( - other.getLocale(ULOC_VALID_LOCALE, status), - other.getLocale(ULOC_ACTUAL_LOCALE, status)); - assignArray(fEras, fErasCount, other.fEras, other.fErasCount); - assignArray(fEraNames, fEraNamesCount, other.fEraNames, other.fEraNamesCount); - assignArray(fNarrowEras, fNarrowErasCount, other.fNarrowEras, other.fNarrowErasCount); - assignArray(fMonths, fMonthsCount, other.fMonths, other.fMonthsCount); - assignArray(fShortMonths, fShortMonthsCount, other.fShortMonths, other.fShortMonthsCount); - assignArray(fNarrowMonths, fNarrowMonthsCount, other.fNarrowMonths, other.fNarrowMonthsCount); - assignArray(fStandaloneMonths, fStandaloneMonthsCount, other.fStandaloneMonths, other.fStandaloneMonthsCount); - assignArray(fStandaloneShortMonths, fStandaloneShortMonthsCount, other.fStandaloneShortMonths, other.fStandaloneShortMonthsCount); - assignArray(fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount, other.fStandaloneNarrowMonths, other.fStandaloneNarrowMonthsCount); - assignArray(fWeekdays, fWeekdaysCount, other.fWeekdays, other.fWeekdaysCount); - assignArray(fShortWeekdays, fShortWeekdaysCount, other.fShortWeekdays, other.fShortWeekdaysCount); - assignArray(fShorterWeekdays, fShorterWeekdaysCount, other.fShorterWeekdays, other.fShorterWeekdaysCount); - assignArray(fNarrowWeekdays, fNarrowWeekdaysCount, other.fNarrowWeekdays, other.fNarrowWeekdaysCount); - assignArray(fStandaloneWeekdays, fStandaloneWeekdaysCount, other.fStandaloneWeekdays, other.fStandaloneWeekdaysCount); - assignArray(fStandaloneShortWeekdays, fStandaloneShortWeekdaysCount, other.fStandaloneShortWeekdays, other.fStandaloneShortWeekdaysCount); - assignArray(fStandaloneShorterWeekdays, fStandaloneShorterWeekdaysCount, other.fStandaloneShorterWeekdays, other.fStandaloneShorterWeekdaysCount); - assignArray(fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount, other.fStandaloneNarrowWeekdays, other.fStandaloneNarrowWeekdaysCount); - assignArray(fAmPms, fAmPmsCount, other.fAmPms, other.fAmPmsCount); - assignArray(fNarrowAmPms, fNarrowAmPmsCount, other.fNarrowAmPms, other.fNarrowAmPmsCount ); - fTimeSeparator.fastCopyFrom(other.fTimeSeparator); // fastCopyFrom() - see assignArray comments - assignArray(fQuarters, fQuartersCount, other.fQuarters, other.fQuartersCount); - assignArray(fShortQuarters, fShortQuartersCount, other.fShortQuarters, other.fShortQuartersCount); - assignArray(fNarrowQuarters, fNarrowQuartersCount, other.fNarrowQuarters, other.fNarrowQuartersCount); - assignArray(fStandaloneQuarters, fStandaloneQuartersCount, other.fStandaloneQuarters, other.fStandaloneQuartersCount); - assignArray(fStandaloneShortQuarters, fStandaloneShortQuartersCount, other.fStandaloneShortQuarters, other.fStandaloneShortQuartersCount); - assignArray(fStandaloneNarrowQuarters, fStandaloneNarrowQuartersCount, other.fStandaloneNarrowQuarters, other.fStandaloneNarrowQuartersCount); - assignArray(fWideDayPeriods, fWideDayPeriodsCount, - other.fWideDayPeriods, other.fWideDayPeriodsCount); - assignArray(fNarrowDayPeriods, fNarrowDayPeriodsCount, - other.fNarrowDayPeriods, other.fNarrowDayPeriodsCount); - assignArray(fAbbreviatedDayPeriods, fAbbreviatedDayPeriodsCount, - other.fAbbreviatedDayPeriods, other.fAbbreviatedDayPeriodsCount); - assignArray(fStandaloneWideDayPeriods, fStandaloneWideDayPeriodsCount, - other.fStandaloneWideDayPeriods, other.fStandaloneWideDayPeriodsCount); - assignArray(fStandaloneNarrowDayPeriods, fStandaloneNarrowDayPeriodsCount, - other.fStandaloneNarrowDayPeriods, other.fStandaloneNarrowDayPeriodsCount); - assignArray(fStandaloneAbbreviatedDayPeriods, fStandaloneAbbreviatedDayPeriodsCount, - other.fStandaloneAbbreviatedDayPeriods, other.fStandaloneAbbreviatedDayPeriodsCount); - if (other.fLeapMonthPatterns != NULL) { - assignArray(fLeapMonthPatterns, fLeapMonthPatternsCount, other.fLeapMonthPatterns, other.fLeapMonthPatternsCount); - } else { - fLeapMonthPatterns = NULL; - fLeapMonthPatternsCount = 0; - } - if (other.fShortYearNames != NULL) { - assignArray(fShortYearNames, fShortYearNamesCount, other.fShortYearNames, other.fShortYearNamesCount); - } else { - fShortYearNames = NULL; - fShortYearNamesCount = 0; - } - if (other.fShortZodiacNames != NULL) { - assignArray(fShortZodiacNames, fShortZodiacNamesCount, other.fShortZodiacNames, other.fShortZodiacNamesCount); - } else { - fShortZodiacNames = NULL; - fShortZodiacNamesCount = 0; - } - - if (other.fZoneStrings != NULL) { - fZoneStringsColCount = other.fZoneStringsColCount; - fZoneStringsRowCount = other.fZoneStringsRowCount; - createZoneStrings((const UnicodeString**)other.fZoneStrings); - - } else { - fZoneStrings = NULL; - fZoneStringsColCount = 0; - fZoneStringsRowCount = 0; - } - fZSFLocale = other.fZSFLocale; - // Other zone strings data is created on demand - fLocaleZoneStrings = NULL; - - // fastCopyFrom() - see assignArray comments - fLocalPatternChars.fastCopyFrom(other.fLocalPatternChars); - - uprv_memcpy(fCapitalization, other.fCapitalization, sizeof(fCapitalization)); -} - -/** - * Assignment operator. - */ -DateFormatSymbols& DateFormatSymbols::operator=(const DateFormatSymbols& other) -{ - if (this == &other) { return *this; } // self-assignment: no-op - dispose(); - copyData(other); - - return *this; -} - -DateFormatSymbols::~DateFormatSymbols() -{ - dispose(); -} - -void DateFormatSymbols::dispose() -{ - delete[] fEras; - delete[] fEraNames; - delete[] fNarrowEras; - delete[] fMonths; - delete[] fShortMonths; - delete[] fNarrowMonths; - delete[] fStandaloneMonths; - delete[] fStandaloneShortMonths; - delete[] fStandaloneNarrowMonths; - delete[] fWeekdays; - delete[] fShortWeekdays; - delete[] fShorterWeekdays; - delete[] fNarrowWeekdays; - delete[] fStandaloneWeekdays; - delete[] fStandaloneShortWeekdays; - delete[] fStandaloneShorterWeekdays; - delete[] fStandaloneNarrowWeekdays; - delete[] fAmPms; - delete[] fNarrowAmPms; - delete[] fQuarters; - delete[] fShortQuarters; - delete[] fNarrowQuarters; - delete[] fStandaloneQuarters; - delete[] fStandaloneShortQuarters; - delete[] fStandaloneNarrowQuarters; - delete[] fLeapMonthPatterns; - delete[] fShortYearNames; - delete[] fShortZodiacNames; - delete[] fAbbreviatedDayPeriods; - delete[] fWideDayPeriods; - delete[] fNarrowDayPeriods; - delete[] fStandaloneAbbreviatedDayPeriods; - delete[] fStandaloneWideDayPeriods; - delete[] fStandaloneNarrowDayPeriods; - - disposeZoneStrings(); -} - -void DateFormatSymbols::disposeZoneStrings() -{ - if (fZoneStrings) { - for (int32_t row = 0; row < fZoneStringsRowCount; ++row) { - delete[] fZoneStrings[row]; - } - uprv_free(fZoneStrings); - } - if (fLocaleZoneStrings) { - for (int32_t row = 0; row < fZoneStringsRowCount; ++row) { - delete[] fLocaleZoneStrings[row]; - } - uprv_free(fLocaleZoneStrings); - } - - fZoneStrings = NULL; - fLocaleZoneStrings = NULL; - fZoneStringsRowCount = 0; - fZoneStringsColCount = 0; -} - -UBool -DateFormatSymbols::arrayCompare(const UnicodeString* array1, - const UnicodeString* array2, - int32_t count) -{ - if (array1 == array2) return true; - while (count>0) - { - --count; - if (array1[count] != array2[count]) return false; - } - return true; -} - -bool -DateFormatSymbols::operator==(const DateFormatSymbols& other) const -{ - // First do cheap comparisons - if (this == &other) { - return true; - } - if (fErasCount == other.fErasCount && - fEraNamesCount == other.fEraNamesCount && - fNarrowErasCount == other.fNarrowErasCount && - fMonthsCount == other.fMonthsCount && - fShortMonthsCount == other.fShortMonthsCount && - fNarrowMonthsCount == other.fNarrowMonthsCount && - fStandaloneMonthsCount == other.fStandaloneMonthsCount && - fStandaloneShortMonthsCount == other.fStandaloneShortMonthsCount && - fStandaloneNarrowMonthsCount == other.fStandaloneNarrowMonthsCount && - fWeekdaysCount == other.fWeekdaysCount && - fShortWeekdaysCount == other.fShortWeekdaysCount && - fShorterWeekdaysCount == other.fShorterWeekdaysCount && - fNarrowWeekdaysCount == other.fNarrowWeekdaysCount && - fStandaloneWeekdaysCount == other.fStandaloneWeekdaysCount && - fStandaloneShortWeekdaysCount == other.fStandaloneShortWeekdaysCount && - fStandaloneShorterWeekdaysCount == other.fStandaloneShorterWeekdaysCount && - fStandaloneNarrowWeekdaysCount == other.fStandaloneNarrowWeekdaysCount && - fAmPmsCount == other.fAmPmsCount && - fNarrowAmPmsCount == other.fNarrowAmPmsCount && - fQuartersCount == other.fQuartersCount && - fShortQuartersCount == other.fShortQuartersCount && - fNarrowQuartersCount == other.fNarrowQuartersCount && - fStandaloneQuartersCount == other.fStandaloneQuartersCount && - fStandaloneShortQuartersCount == other.fStandaloneShortQuartersCount && - fStandaloneNarrowQuartersCount == other.fStandaloneNarrowQuartersCount && - fLeapMonthPatternsCount == other.fLeapMonthPatternsCount && - fShortYearNamesCount == other.fShortYearNamesCount && - fShortZodiacNamesCount == other.fShortZodiacNamesCount && - fAbbreviatedDayPeriodsCount == other.fAbbreviatedDayPeriodsCount && - fWideDayPeriodsCount == other.fWideDayPeriodsCount && - fNarrowDayPeriodsCount == other.fNarrowDayPeriodsCount && - fStandaloneAbbreviatedDayPeriodsCount == other.fStandaloneAbbreviatedDayPeriodsCount && - fStandaloneWideDayPeriodsCount == other.fStandaloneWideDayPeriodsCount && - fStandaloneNarrowDayPeriodsCount == other.fStandaloneNarrowDayPeriodsCount && - (uprv_memcmp(fCapitalization, other.fCapitalization, sizeof(fCapitalization))==0)) - { - // Now compare the arrays themselves - if (arrayCompare(fEras, other.fEras, fErasCount) && - arrayCompare(fEraNames, other.fEraNames, fEraNamesCount) && - arrayCompare(fNarrowEras, other.fNarrowEras, fNarrowErasCount) && - arrayCompare(fMonths, other.fMonths, fMonthsCount) && - arrayCompare(fShortMonths, other.fShortMonths, fShortMonthsCount) && - arrayCompare(fNarrowMonths, other.fNarrowMonths, fNarrowMonthsCount) && - arrayCompare(fStandaloneMonths, other.fStandaloneMonths, fStandaloneMonthsCount) && - arrayCompare(fStandaloneShortMonths, other.fStandaloneShortMonths, fStandaloneShortMonthsCount) && - arrayCompare(fStandaloneNarrowMonths, other.fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount) && - arrayCompare(fWeekdays, other.fWeekdays, fWeekdaysCount) && - arrayCompare(fShortWeekdays, other.fShortWeekdays, fShortWeekdaysCount) && - arrayCompare(fShorterWeekdays, other.fShorterWeekdays, fShorterWeekdaysCount) && - arrayCompare(fNarrowWeekdays, other.fNarrowWeekdays, fNarrowWeekdaysCount) && - arrayCompare(fStandaloneWeekdays, other.fStandaloneWeekdays, fStandaloneWeekdaysCount) && - arrayCompare(fStandaloneShortWeekdays, other.fStandaloneShortWeekdays, fStandaloneShortWeekdaysCount) && - arrayCompare(fStandaloneShorterWeekdays, other.fStandaloneShorterWeekdays, fStandaloneShorterWeekdaysCount) && - arrayCompare(fStandaloneNarrowWeekdays, other.fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount) && - arrayCompare(fAmPms, other.fAmPms, fAmPmsCount) && - arrayCompare(fNarrowAmPms, other.fNarrowAmPms, fNarrowAmPmsCount) && - fTimeSeparator == other.fTimeSeparator && - arrayCompare(fQuarters, other.fQuarters, fQuartersCount) && - arrayCompare(fShortQuarters, other.fShortQuarters, fShortQuartersCount) && - arrayCompare(fNarrowQuarters, other.fNarrowQuarters, fNarrowQuartersCount) && - arrayCompare(fStandaloneQuarters, other.fStandaloneQuarters, fStandaloneQuartersCount) && - arrayCompare(fStandaloneShortQuarters, other.fStandaloneShortQuarters, fStandaloneShortQuartersCount) && - arrayCompare(fStandaloneNarrowQuarters, other.fStandaloneNarrowQuarters, fStandaloneNarrowQuartersCount) && - arrayCompare(fLeapMonthPatterns, other.fLeapMonthPatterns, fLeapMonthPatternsCount) && - arrayCompare(fShortYearNames, other.fShortYearNames, fShortYearNamesCount) && - arrayCompare(fShortZodiacNames, other.fShortZodiacNames, fShortZodiacNamesCount) && - arrayCompare(fAbbreviatedDayPeriods, other.fAbbreviatedDayPeriods, fAbbreviatedDayPeriodsCount) && - arrayCompare(fWideDayPeriods, other.fWideDayPeriods, fWideDayPeriodsCount) && - arrayCompare(fNarrowDayPeriods, other.fNarrowDayPeriods, fNarrowDayPeriodsCount) && - arrayCompare(fStandaloneAbbreviatedDayPeriods, other.fStandaloneAbbreviatedDayPeriods, - fStandaloneAbbreviatedDayPeriodsCount) && - arrayCompare(fStandaloneWideDayPeriods, other.fStandaloneWideDayPeriods, - fStandaloneWideDayPeriodsCount) && - arrayCompare(fStandaloneNarrowDayPeriods, other.fStandaloneNarrowDayPeriods, - fStandaloneWideDayPeriodsCount)) - { - // Compare the contents of fZoneStrings - if (fZoneStrings == NULL && other.fZoneStrings == NULL) { - if (fZSFLocale == other.fZSFLocale) { - return true; - } - } else if (fZoneStrings != NULL && other.fZoneStrings != NULL) { - if (fZoneStringsRowCount == other.fZoneStringsRowCount - && fZoneStringsColCount == other.fZoneStringsColCount) { - bool cmpres = true; - for (int32_t i = 0; (i < fZoneStringsRowCount) && cmpres; i++) { - cmpres = arrayCompare(fZoneStrings[i], other.fZoneStrings[i], fZoneStringsColCount); - } - return cmpres; - } - } - return false; - } - } - return false; -} - -//------------------------------------------------------ - -const UnicodeString* -DateFormatSymbols::getEras(int32_t &count) const -{ - count = fErasCount; - return fEras; -} - -const UnicodeString* -DateFormatSymbols::getEraNames(int32_t &count) const -{ - count = fEraNamesCount; - return fEraNames; -} - -const UnicodeString* -DateFormatSymbols::getNarrowEras(int32_t &count) const -{ - count = fNarrowErasCount; - return fNarrowEras; -} - -const UnicodeString* -DateFormatSymbols::getMonths(int32_t &count) const -{ - count = fMonthsCount; - return fMonths; -} - -const UnicodeString* -DateFormatSymbols::getShortMonths(int32_t &count) const -{ - count = fShortMonthsCount; - return fShortMonths; -} - -const UnicodeString* -DateFormatSymbols::getMonths(int32_t &count, DtContextType context, DtWidthType width ) const -{ - UnicodeString *returnValue = NULL; - - switch (context) { - case FORMAT : - switch(width) { - case WIDE : - count = fMonthsCount; - returnValue = fMonths; - break; - case ABBREVIATED : - case SHORT : // no month data for this, defaults to ABBREVIATED - count = fShortMonthsCount; - returnValue = fShortMonths; - break; - case NARROW : - count = fNarrowMonthsCount; - returnValue = fNarrowMonths; - break; - case DT_WIDTH_COUNT : - break; - } - break; - case STANDALONE : - switch(width) { - case WIDE : - count = fStandaloneMonthsCount; - returnValue = fStandaloneMonths; - break; - case ABBREVIATED : - case SHORT : // no month data for this, defaults to ABBREVIATED - count = fStandaloneShortMonthsCount; - returnValue = fStandaloneShortMonths; - break; - case NARROW : - count = fStandaloneNarrowMonthsCount; - returnValue = fStandaloneNarrowMonths; - break; - case DT_WIDTH_COUNT : - break; - } - break; - case DT_CONTEXT_COUNT : - break; - } - return returnValue; -} - -const UnicodeString* -DateFormatSymbols::getWeekdays(int32_t &count) const -{ - count = fWeekdaysCount; - return fWeekdays; -} - -const UnicodeString* -DateFormatSymbols::getShortWeekdays(int32_t &count) const -{ - count = fShortWeekdaysCount; - return fShortWeekdays; -} - -const UnicodeString* -DateFormatSymbols::getWeekdays(int32_t &count, DtContextType context, DtWidthType width) const -{ - UnicodeString *returnValue = NULL; - switch (context) { - case FORMAT : - switch(width) { - case WIDE : - count = fWeekdaysCount; - returnValue = fWeekdays; - break; - case ABBREVIATED : - count = fShortWeekdaysCount; - returnValue = fShortWeekdays; - break; - case SHORT : - count = fShorterWeekdaysCount; - returnValue = fShorterWeekdays; - break; - case NARROW : - count = fNarrowWeekdaysCount; - returnValue = fNarrowWeekdays; - break; - case DT_WIDTH_COUNT : - break; - } - break; - case STANDALONE : - switch(width) { - case WIDE : - count = fStandaloneWeekdaysCount; - returnValue = fStandaloneWeekdays; - break; - case ABBREVIATED : - count = fStandaloneShortWeekdaysCount; - returnValue = fStandaloneShortWeekdays; - break; - case SHORT : - count = fStandaloneShorterWeekdaysCount; - returnValue = fStandaloneShorterWeekdays; - break; - case NARROW : - count = fStandaloneNarrowWeekdaysCount; - returnValue = fStandaloneNarrowWeekdays; - break; - case DT_WIDTH_COUNT : - break; - } - break; - case DT_CONTEXT_COUNT : - break; - } - return returnValue; -} - -const UnicodeString* -DateFormatSymbols::getQuarters(int32_t &count, DtContextType context, DtWidthType width ) const -{ - UnicodeString *returnValue = NULL; - - switch (context) { - case FORMAT : - switch(width) { - case WIDE : - count = fQuartersCount; - returnValue = fQuarters; - break; - case ABBREVIATED : - case SHORT : // no quarter data for this, defaults to ABBREVIATED - count = fShortQuartersCount; - returnValue = fShortQuarters; - break; - case NARROW : - count = fNarrowQuartersCount; - returnValue = fNarrowQuarters; - break; - case DT_WIDTH_COUNT : - break; - } - break; - case STANDALONE : - switch(width) { - case WIDE : - count = fStandaloneQuartersCount; - returnValue = fStandaloneQuarters; - break; - case ABBREVIATED : - case SHORT : // no quarter data for this, defaults to ABBREVIATED - count = fStandaloneShortQuartersCount; - returnValue = fStandaloneShortQuarters; - break; - case NARROW : - count = fStandaloneNarrowQuartersCount; - returnValue = fStandaloneNarrowQuarters; - break; - case DT_WIDTH_COUNT : - break; - } - break; - case DT_CONTEXT_COUNT : - break; - } - return returnValue; -} - -UnicodeString& -DateFormatSymbols::getTimeSeparatorString(UnicodeString& result) const -{ - // fastCopyFrom() - see assignArray comments - return result.fastCopyFrom(fTimeSeparator); -} - -const UnicodeString* -DateFormatSymbols::getAmPmStrings(int32_t &count) const -{ - count = fAmPmsCount; - return fAmPms; -} - -const UnicodeString* -DateFormatSymbols::getLeapMonthPatterns(int32_t &count) const -{ - count = fLeapMonthPatternsCount; - return fLeapMonthPatterns; -} - -const UnicodeString* -DateFormatSymbols::getYearNames(int32_t& count, - DtContextType /*ignored*/, DtWidthType /*ignored*/) const -{ - count = fShortYearNamesCount; - return fShortYearNames; -} - -void -DateFormatSymbols::setYearNames(const UnicodeString* yearNames, int32_t count, - DtContextType context, DtWidthType width) -{ - if (context == FORMAT && width == ABBREVIATED) { - if (fShortYearNames) { - delete[] fShortYearNames; - } - fShortYearNames = newUnicodeStringArray(count); - uprv_arrayCopy(yearNames, fShortYearNames, count); - fShortYearNamesCount = count; - } -} - -const UnicodeString* -DateFormatSymbols::getZodiacNames(int32_t& count, - DtContextType /*ignored*/, DtWidthType /*ignored*/) const -{ - count = fShortZodiacNamesCount; - return fShortZodiacNames; -} - -void -DateFormatSymbols::setZodiacNames(const UnicodeString* zodiacNames, int32_t count, - DtContextType context, DtWidthType width) -{ - if (context == FORMAT && width == ABBREVIATED) { - if (fShortZodiacNames) { - delete[] fShortZodiacNames; - } - fShortZodiacNames = newUnicodeStringArray(count); - uprv_arrayCopy(zodiacNames, fShortZodiacNames, count); - fShortZodiacNamesCount = count; - } -} - -//------------------------------------------------------ - -void -DateFormatSymbols::setEras(const UnicodeString* erasArray, int32_t count) -{ - // delete the old list if we own it - if (fEras) - delete[] fEras; - - // we always own the new list, which we create here (we duplicate rather - // than adopting the list passed in) - fEras = newUnicodeStringArray(count); - uprv_arrayCopy(erasArray,fEras, count); - fErasCount = count; -} - -void -DateFormatSymbols::setEraNames(const UnicodeString* eraNamesArray, int32_t count) -{ - // delete the old list if we own it - if (fEraNames) - delete[] fEraNames; - - // we always own the new list, which we create here (we duplicate rather - // than adopting the list passed in) - fEraNames = newUnicodeStringArray(count); - uprv_arrayCopy(eraNamesArray,fEraNames, count); - fEraNamesCount = count; -} - -void -DateFormatSymbols::setNarrowEras(const UnicodeString* narrowErasArray, int32_t count) -{ - // delete the old list if we own it - if (fNarrowEras) - delete[] fNarrowEras; - - // we always own the new list, which we create here (we duplicate rather - // than adopting the list passed in) - fNarrowEras = newUnicodeStringArray(count); - uprv_arrayCopy(narrowErasArray,fNarrowEras, count); - fNarrowErasCount = count; -} - -void -DateFormatSymbols::setMonths(const UnicodeString* monthsArray, int32_t count) -{ - // delete the old list if we own it - if (fMonths) - delete[] fMonths; - - // we always own the new list, which we create here (we duplicate rather - // than adopting the list passed in) - fMonths = newUnicodeStringArray(count); - uprv_arrayCopy( monthsArray,fMonths,count); - fMonthsCount = count; -} - -void -DateFormatSymbols::setShortMonths(const UnicodeString* shortMonthsArray, int32_t count) -{ - // delete the old list if we own it - if (fShortMonths) - delete[] fShortMonths; - - // we always own the new list, which we create here (we duplicate rather - // than adopting the list passed in) - fShortMonths = newUnicodeStringArray(count); - uprv_arrayCopy(shortMonthsArray,fShortMonths, count); - fShortMonthsCount = count; -} - -void -DateFormatSymbols::setMonths(const UnicodeString* monthsArray, int32_t count, DtContextType context, DtWidthType width) -{ - // delete the old list if we own it - // we always own the new list, which we create here (we duplicate rather - // than adopting the list passed in) - - switch (context) { - case FORMAT : - switch (width) { - case WIDE : - if (fMonths) - delete[] fMonths; - fMonths = newUnicodeStringArray(count); - uprv_arrayCopy( monthsArray,fMonths,count); - fMonthsCount = count; - break; - case ABBREVIATED : - if (fShortMonths) - delete[] fShortMonths; - fShortMonths = newUnicodeStringArray(count); - uprv_arrayCopy( monthsArray,fShortMonths,count); - fShortMonthsCount = count; - break; - case NARROW : - if (fNarrowMonths) - delete[] fNarrowMonths; - fNarrowMonths = newUnicodeStringArray(count); - uprv_arrayCopy( monthsArray,fNarrowMonths,count); - fNarrowMonthsCount = count; - break; - default : - break; - } - break; - case STANDALONE : - switch (width) { - case WIDE : - if (fStandaloneMonths) - delete[] fStandaloneMonths; - fStandaloneMonths = newUnicodeStringArray(count); - uprv_arrayCopy( monthsArray,fStandaloneMonths,count); - fStandaloneMonthsCount = count; - break; - case ABBREVIATED : - if (fStandaloneShortMonths) - delete[] fStandaloneShortMonths; - fStandaloneShortMonths = newUnicodeStringArray(count); - uprv_arrayCopy( monthsArray,fStandaloneShortMonths,count); - fStandaloneShortMonthsCount = count; - break; - case NARROW : - if (fStandaloneNarrowMonths) - delete[] fStandaloneNarrowMonths; - fStandaloneNarrowMonths = newUnicodeStringArray(count); - uprv_arrayCopy( monthsArray,fStandaloneNarrowMonths,count); - fStandaloneNarrowMonthsCount = count; - break; - default : - break; - } - break; - case DT_CONTEXT_COUNT : - break; - } -} - -void DateFormatSymbols::setWeekdays(const UnicodeString* weekdaysArray, int32_t count) -{ - // delete the old list if we own it - if (fWeekdays) - delete[] fWeekdays; - - // we always own the new list, which we create here (we duplicate rather - // than adopting the list passed in) - fWeekdays = newUnicodeStringArray(count); - uprv_arrayCopy(weekdaysArray,fWeekdays,count); - fWeekdaysCount = count; -} - -void -DateFormatSymbols::setShortWeekdays(const UnicodeString* shortWeekdaysArray, int32_t count) -{ - // delete the old list if we own it - if (fShortWeekdays) - delete[] fShortWeekdays; - - // we always own the new list, which we create here (we duplicate rather - // than adopting the list passed in) - fShortWeekdays = newUnicodeStringArray(count); - uprv_arrayCopy(shortWeekdaysArray, fShortWeekdays, count); - fShortWeekdaysCount = count; -} - -void -DateFormatSymbols::setWeekdays(const UnicodeString* weekdaysArray, int32_t count, DtContextType context, DtWidthType width) -{ - // delete the old list if we own it - // we always own the new list, which we create here (we duplicate rather - // than adopting the list passed in) - - switch (context) { - case FORMAT : - switch (width) { - case WIDE : - if (fWeekdays) - delete[] fWeekdays; - fWeekdays = newUnicodeStringArray(count); - uprv_arrayCopy(weekdaysArray, fWeekdays, count); - fWeekdaysCount = count; - break; - case ABBREVIATED : - if (fShortWeekdays) - delete[] fShortWeekdays; - fShortWeekdays = newUnicodeStringArray(count); - uprv_arrayCopy(weekdaysArray, fShortWeekdays, count); - fShortWeekdaysCount = count; - break; - case SHORT : - if (fShorterWeekdays) - delete[] fShorterWeekdays; - fShorterWeekdays = newUnicodeStringArray(count); - uprv_arrayCopy(weekdaysArray, fShorterWeekdays, count); - fShorterWeekdaysCount = count; - break; - case NARROW : - if (fNarrowWeekdays) - delete[] fNarrowWeekdays; - fNarrowWeekdays = newUnicodeStringArray(count); - uprv_arrayCopy(weekdaysArray, fNarrowWeekdays, count); - fNarrowWeekdaysCount = count; - break; - case DT_WIDTH_COUNT : - break; - } - break; - case STANDALONE : - switch (width) { - case WIDE : - if (fStandaloneWeekdays) - delete[] fStandaloneWeekdays; - fStandaloneWeekdays = newUnicodeStringArray(count); - uprv_arrayCopy(weekdaysArray, fStandaloneWeekdays, count); - fStandaloneWeekdaysCount = count; - break; - case ABBREVIATED : - if (fStandaloneShortWeekdays) - delete[] fStandaloneShortWeekdays; - fStandaloneShortWeekdays = newUnicodeStringArray(count); - uprv_arrayCopy(weekdaysArray, fStandaloneShortWeekdays, count); - fStandaloneShortWeekdaysCount = count; - break; - case SHORT : - if (fStandaloneShorterWeekdays) - delete[] fStandaloneShorterWeekdays; - fStandaloneShorterWeekdays = newUnicodeStringArray(count); - uprv_arrayCopy(weekdaysArray, fStandaloneShorterWeekdays, count); - fStandaloneShorterWeekdaysCount = count; - break; - case NARROW : - if (fStandaloneNarrowWeekdays) - delete[] fStandaloneNarrowWeekdays; - fStandaloneNarrowWeekdays = newUnicodeStringArray(count); - uprv_arrayCopy(weekdaysArray, fStandaloneNarrowWeekdays, count); - fStandaloneNarrowWeekdaysCount = count; - break; - case DT_WIDTH_COUNT : - break; - } - break; - case DT_CONTEXT_COUNT : - break; - } -} - -void -DateFormatSymbols::setQuarters(const UnicodeString* quartersArray, int32_t count, DtContextType context, DtWidthType width) -{ - // delete the old list if we own it - // we always own the new list, which we create here (we duplicate rather - // than adopting the list passed in) - - switch (context) { - case FORMAT : - switch (width) { - case WIDE : - if (fQuarters) - delete[] fQuarters; - fQuarters = newUnicodeStringArray(count); - uprv_arrayCopy( quartersArray,fQuarters,count); - fQuartersCount = count; - break; - case ABBREVIATED : - if (fShortQuarters) - delete[] fShortQuarters; - fShortQuarters = newUnicodeStringArray(count); - uprv_arrayCopy( quartersArray,fShortQuarters,count); - fShortQuartersCount = count; - break; - case NARROW : - if (fNarrowQuarters) - delete[] fNarrowQuarters; - fNarrowQuarters = newUnicodeStringArray(count); - uprv_arrayCopy( quartersArray,fNarrowQuarters,count); - fNarrowQuartersCount = count; - break; - default : - break; - } - break; - case STANDALONE : - switch (width) { - case WIDE : - if (fStandaloneQuarters) - delete[] fStandaloneQuarters; - fStandaloneQuarters = newUnicodeStringArray(count); - uprv_arrayCopy( quartersArray,fStandaloneQuarters,count); - fStandaloneQuartersCount = count; - break; - case ABBREVIATED : - if (fStandaloneShortQuarters) - delete[] fStandaloneShortQuarters; - fStandaloneShortQuarters = newUnicodeStringArray(count); - uprv_arrayCopy( quartersArray,fStandaloneShortQuarters,count); - fStandaloneShortQuartersCount = count; - break; - case NARROW : - if (fStandaloneNarrowQuarters) - delete[] fStandaloneNarrowQuarters; - fStandaloneNarrowQuarters = newUnicodeStringArray(count); - uprv_arrayCopy( quartersArray,fStandaloneNarrowQuarters,count); - fStandaloneNarrowQuartersCount = count; - break; - default : - break; - } - break; - case DT_CONTEXT_COUNT : - break; - } -} - -void -DateFormatSymbols::setAmPmStrings(const UnicodeString* amPmsArray, int32_t count) -{ - // delete the old list if we own it - if (fAmPms) delete[] fAmPms; - - // we always own the new list, which we create here (we duplicate rather - // than adopting the list passed in) - fAmPms = newUnicodeStringArray(count); - uprv_arrayCopy(amPmsArray,fAmPms,count); - fAmPmsCount = count; -} - -void -DateFormatSymbols::setTimeSeparatorString(const UnicodeString& newTimeSeparator) -{ - fTimeSeparator = newTimeSeparator; -} - -const UnicodeString** -DateFormatSymbols::getZoneStrings(int32_t& rowCount, int32_t& columnCount) const -{ - const UnicodeString **result = NULL; - static UMutex LOCK; - - umtx_lock(&LOCK); - if (fZoneStrings == NULL) { - if (fLocaleZoneStrings == NULL) { - ((DateFormatSymbols*)this)->initZoneStringsArray(); - } - result = (const UnicodeString**)fLocaleZoneStrings; - } else { - result = (const UnicodeString**)fZoneStrings; - } - rowCount = fZoneStringsRowCount; - columnCount = fZoneStringsColCount; - umtx_unlock(&LOCK); - - return result; -} - -// For now, we include all zones -#define ZONE_SET UCAL_ZONE_TYPE_ANY - -// This code must be called within a synchronized block -void -DateFormatSymbols::initZoneStringsArray(void) { - if (fZoneStrings != NULL || fLocaleZoneStrings != NULL) { - return; - } - - UErrorCode status = U_ZERO_ERROR; - - StringEnumeration *tzids = NULL; - UnicodeString ** zarray = NULL; - TimeZoneNames *tzNames = NULL; - int32_t rows = 0; - - static const UTimeZoneNameType TYPES[] = { - UTZNM_LONG_STANDARD, UTZNM_SHORT_STANDARD, - UTZNM_LONG_DAYLIGHT, UTZNM_SHORT_DAYLIGHT - }; - static const int32_t NUM_TYPES = 4; - - do { // dummy do-while - - tzids = TimeZone::createTimeZoneIDEnumeration(ZONE_SET, NULL, NULL, status); - rows = tzids->count(status); - if (U_FAILURE(status)) { - break; - } - - // Allocate array - int32_t size = rows * sizeof(UnicodeString*); - zarray = (UnicodeString**)uprv_malloc(size); - if (zarray == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - uprv_memset(zarray, 0, size); - - tzNames = TimeZoneNames::createInstance(fZSFLocale, status); - tzNames->loadAllDisplayNames(status); - if (U_FAILURE(status)) { break; } - - const UnicodeString *tzid; - int32_t i = 0; - UDate now = Calendar::getNow(); - UnicodeString tzDispName; - - while ((tzid = tzids->snext(status)) != 0) { - if (U_FAILURE(status)) { - break; - } - - zarray[i] = new UnicodeString[5]; - if (zarray[i] == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - - zarray[i][0].setTo(*tzid); - tzNames->getDisplayNames(*tzid, TYPES, NUM_TYPES, now, zarray[i]+1, status); - i++; - } - - } while (false); - - if (U_FAILURE(status)) { - if (zarray) { - for (int32_t i = 0; i < rows; i++) { - if (zarray[i]) { - delete[] zarray[i]; - } - } - uprv_free(zarray); - zarray = NULL; - } - } - - if (tzNames) { - delete tzNames; - } - if (tzids) { - delete tzids; - } - - fLocaleZoneStrings = zarray; - fZoneStringsRowCount = rows; - fZoneStringsColCount = 1 + NUM_TYPES; -} - -void -DateFormatSymbols::setZoneStrings(const UnicodeString* const *strings, int32_t rowCount, int32_t columnCount) -{ - // since deleting a 2-d array is a pain in the butt, we offload that task to - // a separate function - disposeZoneStrings(); - // we always own the new list, which we create here (we duplicate rather - // than adopting the list passed in) - fZoneStringsRowCount = rowCount; - fZoneStringsColCount = columnCount; - createZoneStrings((const UnicodeString**)strings); -} - -//------------------------------------------------------ - -const char16_t * U_EXPORT2 -DateFormatSymbols::getPatternUChars(void) -{ - return gPatternChars; -} - -UDateFormatField U_EXPORT2 -DateFormatSymbols::getPatternCharIndex(UChar c) { - const UChar *p = u_strchr(gPatternChars, c); - if (p == NULL) { - return UDAT_FIELD_COUNT; - } else { - return static_cast(p - gPatternChars); - } -} - -static const uint64_t kNumericFieldsAlways = - ((uint64_t)1 << UDAT_YEAR_FIELD) | // y - ((uint64_t)1 << UDAT_DATE_FIELD) | // d - ((uint64_t)1 << UDAT_HOUR_OF_DAY1_FIELD) | // k - ((uint64_t)1 << UDAT_HOUR_OF_DAY0_FIELD) | // H - ((uint64_t)1 << UDAT_MINUTE_FIELD) | // m - ((uint64_t)1 << UDAT_SECOND_FIELD) | // s - ((uint64_t)1 << UDAT_FRACTIONAL_SECOND_FIELD) | // S - ((uint64_t)1 << UDAT_DAY_OF_YEAR_FIELD) | // D - ((uint64_t)1 << UDAT_DAY_OF_WEEK_IN_MONTH_FIELD) | // F - ((uint64_t)1 << UDAT_WEEK_OF_YEAR_FIELD) | // w - ((uint64_t)1 << UDAT_WEEK_OF_MONTH_FIELD) | // W - ((uint64_t)1 << UDAT_HOUR1_FIELD) | // h - ((uint64_t)1 << UDAT_HOUR0_FIELD) | // K - ((uint64_t)1 << UDAT_YEAR_WOY_FIELD) | // Y - ((uint64_t)1 << UDAT_EXTENDED_YEAR_FIELD) | // u - ((uint64_t)1 << UDAT_JULIAN_DAY_FIELD) | // g - ((uint64_t)1 << UDAT_MILLISECONDS_IN_DAY_FIELD) | // A - ((uint64_t)1 << UDAT_RELATED_YEAR_FIELD); // r - -static const uint64_t kNumericFieldsForCount12 = - ((uint64_t)1 << UDAT_MONTH_FIELD) | // M or MM - ((uint64_t)1 << UDAT_DOW_LOCAL_FIELD) | // e or ee - ((uint64_t)1 << UDAT_STANDALONE_DAY_FIELD) | // c or cc - ((uint64_t)1 << UDAT_STANDALONE_MONTH_FIELD) | // L or LL - ((uint64_t)1 << UDAT_QUARTER_FIELD) | // Q or QQ - ((uint64_t)1 << UDAT_STANDALONE_QUARTER_FIELD); // q or qq - -UBool U_EXPORT2 -DateFormatSymbols::isNumericField(UDateFormatField f, int32_t count) { - if (f == UDAT_FIELD_COUNT) { - return false; - } - uint64_t flag = ((uint64_t)1 << f); - return ((kNumericFieldsAlways & flag) != 0 || ((kNumericFieldsForCount12 & flag) != 0 && count < 3)); -} - -UBool U_EXPORT2 -DateFormatSymbols::isNumericPatternChar(UChar c, int32_t count) { - return isNumericField(getPatternCharIndex(c), count); -} - -//------------------------------------------------------ - -UnicodeString& -DateFormatSymbols::getLocalPatternChars(UnicodeString& result) const -{ - // fastCopyFrom() - see assignArray comments - return result.fastCopyFrom(fLocalPatternChars); -} - -//------------------------------------------------------ - -void -DateFormatSymbols::setLocalPatternChars(const UnicodeString& newLocalPatternChars) -{ - fLocalPatternChars = newLocalPatternChars; -} - -//------------------------------------------------------ - -namespace { - -// Constants declarations -static const UChar kCalendarAliasPrefixUChar[] = { - SOLIDUS, CAP_L, CAP_O, CAP_C, CAP_A, CAP_L, CAP_E, SOLIDUS, - LOW_C, LOW_A, LOW_L, LOW_E, LOW_N, LOW_D, LOW_A, LOW_R, SOLIDUS -}; -static const UChar kGregorianTagUChar[] = { - LOW_G, LOW_R, LOW_E, LOW_G, LOW_O, LOW_R, LOW_I, LOW_A, LOW_N -}; -static const UChar kVariantTagUChar[] = { - PERCENT, LOW_V, LOW_A, LOW_R, LOW_I, LOW_A, LOW_N, LOW_T -}; -static const UChar kLeapTagUChar[] = { - LOW_L, LOW_E, LOW_A, LOW_P -}; -static const UChar kCyclicNameSetsTagUChar[] = { - LOW_C, LOW_Y, LOW_C, LOW_L, LOW_I, LOW_C, CAP_N, LOW_A, LOW_M, LOW_E, CAP_S, LOW_E, LOW_T, LOW_S -}; -static const UChar kYearsTagUChar[] = { - SOLIDUS, LOW_Y, LOW_E, LOW_A, LOW_R, LOW_S -}; -static const UChar kZodiacsUChar[] = { - SOLIDUS, LOW_Z, LOW_O, LOW_D, LOW_I, LOW_A, LOW_C, LOW_S -}; -static const UChar kDayPartsTagUChar[] = { - SOLIDUS, LOW_D, LOW_A, LOW_Y, CAP_P, LOW_A, LOW_R, LOW_T, LOW_S -}; -static const UChar kFormatTagUChar[] = { - SOLIDUS, LOW_F, LOW_O, LOW_R, LOW_M, LOW_A, LOW_T -}; -static const UChar kAbbrTagUChar[] = { - SOLIDUS, LOW_A, LOW_B, LOW_B, LOW_R, LOW_E, LOW_V, LOW_I, LOW_A, LOW_T, LOW_E, LOW_D -}; - -// ResourceSink to enumerate all calendar resources -struct CalendarDataSink : public ResourceSink { - - // Enum which specifies the type of alias received, or no alias - enum AliasType { - SAME_CALENDAR, - DIFFERENT_CALENDAR, - GREGORIAN, - NONE - }; - - // Data structures to store resources from the current resource bundle - Hashtable arrays; - Hashtable arraySizes; - Hashtable maps; - /** - * Whenever there are aliases, the same object will be added twice to 'map'. - * To avoid double deletion, 'maps' won't take ownership of the objects. Instead, - * 'mapRefs' will own them and will delete them when CalendarDataSink is deleted. - */ - MemoryPool mapRefs; - - // Paths and the aliases they point to - UVector aliasPathPairs; - - // Current and next calendar resource table which should be loaded - UnicodeString currentCalendarType; - UnicodeString nextCalendarType; - - // Resources to visit when enumerating fallback calendars - LocalPointer resourcesToVisit; - - // Alias' relative path populated whenever an alias is read - UnicodeString aliasRelativePath; - - // Initializes CalendarDataSink with default values - CalendarDataSink(UErrorCode& status) - : arrays(false, status), arraySizes(false, status), maps(false, status), - mapRefs(), - aliasPathPairs(uprv_deleteUObject, uhash_compareUnicodeString, status), - currentCalendarType(), nextCalendarType(), - resourcesToVisit(NULL), aliasRelativePath() { - if (U_FAILURE(status)) { return; } - } - virtual ~CalendarDataSink(); - - // Configure the CalendarSink to visit all the resources - void visitAllResources() { - resourcesToVisit.adoptInstead(NULL); - } - - // Actions to be done before enumerating - void preEnumerate(const UnicodeString &calendarType) { - currentCalendarType = calendarType; - nextCalendarType.setToBogus(); - aliasPathPairs.removeAllElements(); - } - - virtual void put(const char *key, ResourceValue &value, UBool, UErrorCode &errorCode) override { - if (U_FAILURE(errorCode)) { return; } - U_ASSERT(!currentCalendarType.isEmpty()); - - // Stores the resources to visit on the next calendar. - LocalPointer resourcesToVisitNext(NULL); - ResourceTable calendarData = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - - // Enumerate all resources for this calendar - for (int i = 0; calendarData.getKeyAndValue(i, key, value); i++) { - UnicodeString keyUString(key, -1, US_INV); - - // == Handle aliases == - AliasType aliasType = processAliasFromValue(keyUString, value, errorCode); - if (U_FAILURE(errorCode)) { return; } - if (aliasType == GREGORIAN) { - // Ignore aliases to the gregorian calendar, all of its resources will be loaded anyway. - continue; - - } else if (aliasType == DIFFERENT_CALENDAR) { - // Whenever an alias to the next calendar (except gregorian) is encountered, register the - // calendar type it's pointing to - if (resourcesToVisitNext.isNull()) { - resourcesToVisitNext - .adoptInsteadAndCheckErrorCode(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, errorCode), - errorCode); - if (U_FAILURE(errorCode)) { return; } - } - LocalPointer aliasRelativePathCopy(aliasRelativePath.clone(), errorCode); - resourcesToVisitNext->adoptElement(aliasRelativePathCopy.orphan(), errorCode); - if (U_FAILURE(errorCode)) { return; } - continue; - - } else if (aliasType == SAME_CALENDAR) { - // Register same-calendar alias - if (arrays.get(aliasRelativePath) == NULL && maps.get(aliasRelativePath) == NULL) { - LocalPointer aliasRelativePathCopy(aliasRelativePath.clone(), errorCode); - aliasPathPairs.adoptElement(aliasRelativePathCopy.orphan(), errorCode); - if (U_FAILURE(errorCode)) { return; } - LocalPointer keyUStringCopy(keyUString.clone(), errorCode); - aliasPathPairs.adoptElement(keyUStringCopy.orphan(), errorCode); - if (U_FAILURE(errorCode)) { return; } - } - continue; - } - - // Only visit the resources that were referenced by an alias on the previous calendar - // (AmPmMarkersAbbr is an exception). - if (!resourcesToVisit.isNull() && !resourcesToVisit->isEmpty() && !resourcesToVisit->contains(&keyUString) - && uprv_strcmp(key, gAmPmMarkersAbbrTag) != 0) { continue; } - - // == Handle data == - if (uprv_strcmp(key, gAmPmMarkersTag) == 0 - || uprv_strcmp(key, gAmPmMarkersAbbrTag) == 0 - || uprv_strcmp(key, gAmPmMarkersNarrowTag) == 0) { - if (arrays.get(keyUString) == NULL) { - ResourceArray resourceArray = value.getArray(errorCode); - int32_t arraySize = resourceArray.getSize(); - LocalArray stringArray(new UnicodeString[arraySize], errorCode); - value.getStringArray(stringArray.getAlias(), arraySize, errorCode); - arrays.put(keyUString, stringArray.orphan(), errorCode); - arraySizes.puti(keyUString, arraySize, errorCode); - if (U_FAILURE(errorCode)) { return; } - } - } else if (uprv_strcmp(key, gErasTag) == 0 - || uprv_strcmp(key, gDayNamesTag) == 0 - || uprv_strcmp(key, gMonthNamesTag) == 0 - || uprv_strcmp(key, gQuartersTag) == 0 - || uprv_strcmp(key, gDayPeriodTag) == 0 - || uprv_strcmp(key, gMonthPatternsTag) == 0 - || uprv_strcmp(key, gCyclicNameSetsTag) == 0) { - processResource(keyUString, key, value, errorCode); - } - } - - // Apply same-calendar aliases - UBool modified; - do { - modified = false; - for (int32_t i = 0; i < aliasPathPairs.size();) { - UBool mod = false; - UnicodeString *alias = (UnicodeString*)aliasPathPairs[i]; - UnicodeString *aliasArray; - Hashtable *aliasMap; - if ((aliasArray = (UnicodeString*)arrays.get(*alias)) != NULL) { - UnicodeString *path = (UnicodeString*)aliasPathPairs[i + 1]; - if (arrays.get(*path) == NULL) { - // Clone the array - int32_t aliasArraySize = arraySizes.geti(*alias); - LocalArray aliasArrayCopy(new UnicodeString[aliasArraySize], errorCode); - if (U_FAILURE(errorCode)) { return; } - uprv_arrayCopy(aliasArray, aliasArrayCopy.getAlias(), aliasArraySize); - // Put the array on the 'arrays' map - arrays.put(*path, aliasArrayCopy.orphan(), errorCode); - arraySizes.puti(*path, aliasArraySize, errorCode); - } - if (U_FAILURE(errorCode)) { return; } - mod = true; - } else if ((aliasMap = (Hashtable*)maps.get(*alias)) != NULL) { - UnicodeString *path = (UnicodeString*)aliasPathPairs[i + 1]; - if (maps.get(*path) == NULL) { - maps.put(*path, aliasMap, errorCode); - } - if (U_FAILURE(errorCode)) { return; } - mod = true; - } - if (mod) { - aliasPathPairs.removeElementAt(i + 1); - aliasPathPairs.removeElementAt(i); - modified = true; - } else { - i += 2; - } - } - } while (modified && !aliasPathPairs.isEmpty()); - - // Set the resources to visit on the next calendar - if (!resourcesToVisitNext.isNull()) { - resourcesToVisit = std::move(resourcesToVisitNext); - } - } - - // Process the nested resource bundle tables - void processResource(UnicodeString &path, const char *key, ResourceValue &value, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) return; - - ResourceTable table = value.getTable(errorCode); - if (U_FAILURE(errorCode)) return; - Hashtable* stringMap = NULL; - - // Iterate over all the elements of the table and add them to the map - for (int i = 0; table.getKeyAndValue(i, key, value); i++) { - UnicodeString keyUString(key, -1, US_INV); - - // Ignore '%variant' keys - if (keyUString.endsWith(kVariantTagUChar, UPRV_LENGTHOF(kVariantTagUChar))) { - continue; - } - - // == Handle String elements == - if (value.getType() == URES_STRING) { - // We are on a leaf, store the map elements into the stringMap - if (i == 0) { - // mapRefs will keep ownership of 'stringMap': - stringMap = mapRefs.create(false, errorCode); - if (stringMap == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - maps.put(path, stringMap, errorCode); - if (U_FAILURE(errorCode)) { return; } - stringMap->setValueDeleter(uprv_deleteUObject); - } - U_ASSERT(stringMap != NULL); - int32_t valueStringSize; - const UChar *valueString = value.getString(valueStringSize, errorCode); - if (U_FAILURE(errorCode)) { return; } - LocalPointer valueUString(new UnicodeString(true, valueString, valueStringSize), errorCode); - stringMap->put(keyUString, valueUString.orphan(), errorCode); - if (U_FAILURE(errorCode)) { return; } - continue; - } - U_ASSERT(stringMap == NULL); - - // Store the current path's length and append the current key to the path. - int32_t pathLength = path.length(); - path.append(SOLIDUS).append(keyUString); - - // In cyclicNameSets ignore everything but years/format/abbreviated - // and zodiacs/format/abbreviated - if (path.startsWith(kCyclicNameSetsTagUChar, UPRV_LENGTHOF(kCyclicNameSetsTagUChar))) { - UBool skip = true; - int32_t startIndex = UPRV_LENGTHOF(kCyclicNameSetsTagUChar); - int32_t length = 0; - if (startIndex == path.length() - || path.compare(startIndex, (length = UPRV_LENGTHOF(kZodiacsUChar)), kZodiacsUChar, 0, UPRV_LENGTHOF(kZodiacsUChar)) == 0 - || path.compare(startIndex, (length = UPRV_LENGTHOF(kYearsTagUChar)), kYearsTagUChar, 0, UPRV_LENGTHOF(kYearsTagUChar)) == 0 - || path.compare(startIndex, (length = UPRV_LENGTHOF(kDayPartsTagUChar)), kDayPartsTagUChar, 0, UPRV_LENGTHOF(kDayPartsTagUChar)) == 0) { - startIndex += length; - length = 0; - if (startIndex == path.length() - || path.compare(startIndex, (length = UPRV_LENGTHOF(kFormatTagUChar)), kFormatTagUChar, 0, UPRV_LENGTHOF(kFormatTagUChar)) == 0) { - startIndex += length; - length = 0; - if (startIndex == path.length() - || path.compare(startIndex, (length = UPRV_LENGTHOF(kAbbrTagUChar)), kAbbrTagUChar, 0, UPRV_LENGTHOF(kAbbrTagUChar)) == 0) { - skip = false; - } - } - } - if (skip) { - // Drop the latest key on the path and continue - path.retainBetween(0, pathLength); - continue; - } - } - - // == Handle aliases == - if (arrays.get(path) != NULL || maps.get(path) != NULL) { - // Drop the latest key on the path and continue - path.retainBetween(0, pathLength); - continue; - } - - AliasType aliasType = processAliasFromValue(path, value, errorCode); - if (U_FAILURE(errorCode)) { return; } - if (aliasType == SAME_CALENDAR) { - // Store the alias path and the current path on aliasPathPairs - LocalPointer aliasRelativePathCopy(aliasRelativePath.clone(), errorCode); - aliasPathPairs.adoptElement(aliasRelativePathCopy.orphan(), errorCode); - if (U_FAILURE(errorCode)) { return; } - LocalPointer pathCopy(path.clone(), errorCode); - aliasPathPairs.adoptElement(pathCopy.orphan(), errorCode); - if (U_FAILURE(errorCode)) { return; } - - // Drop the latest key on the path and continue - path.retainBetween(0, pathLength); - continue; - } - U_ASSERT(aliasType == NONE); - - // == Handle data == - if (value.getType() == URES_ARRAY) { - // We are on a leaf, store the array - ResourceArray rDataArray = value.getArray(errorCode); - int32_t dataArraySize = rDataArray.getSize(); - LocalArray dataArray(new UnicodeString[dataArraySize], errorCode); - value.getStringArray(dataArray.getAlias(), dataArraySize, errorCode); - arrays.put(path, dataArray.orphan(), errorCode); - arraySizes.puti(path, dataArraySize, errorCode); - if (U_FAILURE(errorCode)) { return; } - } else if (value.getType() == URES_TABLE) { - // We are not on a leaf, recursively process the subtable. - processResource(path, key, value, errorCode); - if (U_FAILURE(errorCode)) { return; } - } - - // Drop the latest key on the path - path.retainBetween(0, pathLength); - } - } - - // Populates an AliasIdentifier with the alias information contained on the UResource.Value. - AliasType processAliasFromValue(UnicodeString ¤tRelativePath, ResourceValue &value, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return NONE; } - - if (value.getType() == URES_ALIAS) { - int32_t aliasPathSize; - const UChar* aliasPathUChar = value.getAliasString(aliasPathSize, errorCode); - if (U_FAILURE(errorCode)) { return NONE; } - UnicodeString aliasPath(aliasPathUChar, aliasPathSize); - const int32_t aliasPrefixLength = UPRV_LENGTHOF(kCalendarAliasPrefixUChar); - if (aliasPath.startsWith(kCalendarAliasPrefixUChar, aliasPrefixLength) - && aliasPath.length() > aliasPrefixLength) { - int32_t typeLimit = aliasPath.indexOf(SOLIDUS, aliasPrefixLength); - if (typeLimit > aliasPrefixLength) { - const UnicodeString aliasCalendarType = - aliasPath.tempSubStringBetween(aliasPrefixLength, typeLimit); - aliasRelativePath.setTo(aliasPath, typeLimit + 1, aliasPath.length()); - - if (currentCalendarType == aliasCalendarType - && currentRelativePath != aliasRelativePath) { - // If we have an alias to the same calendar, the path to the resource must be different - return SAME_CALENDAR; - - } else if (currentCalendarType != aliasCalendarType - && currentRelativePath == aliasRelativePath) { - // If we have an alias to a different calendar, the path to the resource must be the same - if (aliasCalendarType.compare(kGregorianTagUChar, UPRV_LENGTHOF(kGregorianTagUChar)) == 0) { - return GREGORIAN; - } else if (nextCalendarType.isBogus()) { - nextCalendarType = aliasCalendarType; - return DIFFERENT_CALENDAR; - } else if (nextCalendarType == aliasCalendarType) { - return DIFFERENT_CALENDAR; - } - } - } - } - errorCode = U_INTERNAL_PROGRAM_ERROR; - return NONE; - } - return NONE; - } - - // Deleter function to be used by 'arrays' - static void U_CALLCONV deleteUnicodeStringArray(void *uArray) { - delete[] static_cast(uArray); - } -}; -// Virtual destructors have to be defined out of line -CalendarDataSink::~CalendarDataSink() { - arrays.setValueDeleter(deleteUnicodeStringArray); -} -} - -//------------------------------------------------------ - -static void -initField(UnicodeString **field, int32_t& length, const UChar *data, LastResortSize numStr, LastResortSize strLen, UErrorCode &status) { - if (U_SUCCESS(status)) { - length = numStr; - *field = newUnicodeStringArray((size_t)numStr); - if (*field) { - for(int32_t i = 0; isetTo(true, data+(i*((int32_t)strLen)), -1); - } - } - else { - length = 0; - status = U_MEMORY_ALLOCATION_ERROR; - } - } -} - -static void -initField(UnicodeString **field, int32_t& length, CalendarDataSink &sink, CharString &key, UErrorCode &status) { - if (U_SUCCESS(status)) { - UnicodeString keyUString(key.data(), -1, US_INV); - UnicodeString* array = static_cast(sink.arrays.get(keyUString)); - - if (array != NULL) { - length = sink.arraySizes.geti(keyUString); - *field = array; - // DateFormatSymbols takes ownership of the array: - sink.arrays.remove(keyUString); - } else { - length = 0; - status = U_MISSING_RESOURCE_ERROR; - } - } -} - -static void -initField(UnicodeString **field, int32_t& length, CalendarDataSink &sink, CharString &key, int32_t arrayOffset, UErrorCode &status) { - if (U_SUCCESS(status)) { - UnicodeString keyUString(key.data(), -1, US_INV); - UnicodeString* array = static_cast(sink.arrays.get(keyUString)); - - if (array != NULL) { - int32_t arrayLength = sink.arraySizes.geti(keyUString); - length = arrayLength + arrayOffset; - *field = new UnicodeString[length]; - if (*field == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_arrayCopy(array, 0, *field, arrayOffset, arrayLength); - } else { - length = 0; - status = U_MISSING_RESOURCE_ERROR; - } - } -} - -static void -initLeapMonthPattern(UnicodeString *field, int32_t index, CalendarDataSink &sink, CharString &path, UErrorCode &status) { - field[index].remove(); - if (U_SUCCESS(status)) { - UnicodeString pathUString(path.data(), -1, US_INV); - Hashtable *leapMonthTable = static_cast(sink.maps.get(pathUString)); - if (leapMonthTable != NULL) { - UnicodeString leapLabel(false, kLeapTagUChar, UPRV_LENGTHOF(kLeapTagUChar)); - UnicodeString *leapMonthPattern = static_cast(leapMonthTable->get(leapLabel)); - if (leapMonthPattern != NULL) { - field[index].fastCopyFrom(*leapMonthPattern); - } else { - field[index].setToBogus(); - } - return; - } - status = U_MISSING_RESOURCE_ERROR; - } -} - -static CharString -&buildResourcePath(CharString &path, const char* segment1, UErrorCode &errorCode) { - return path.clear().append(segment1, -1, errorCode); -} - -static CharString -&buildResourcePath(CharString &path, const char* segment1, const char* segment2, - UErrorCode &errorCode) { - return buildResourcePath(path, segment1, errorCode).append('/', errorCode) - .append(segment2, -1, errorCode); -} - -static CharString -&buildResourcePath(CharString &path, const char* segment1, const char* segment2, - const char* segment3, UErrorCode &errorCode) { - return buildResourcePath(path, segment1, segment2, errorCode).append('/', errorCode) - .append(segment3, -1, errorCode); -} - -static CharString -&buildResourcePath(CharString &path, const char* segment1, const char* segment2, - const char* segment3, const char* segment4, UErrorCode &errorCode) { - return buildResourcePath(path, segment1, segment2, segment3, errorCode).append('/', errorCode) - .append(segment4, -1, errorCode); -} - -typedef struct { - const char * usageTypeName; - DateFormatSymbols::ECapitalizationContextUsageType usageTypeEnumValue; -} ContextUsageTypeNameToEnumValue; - -static const ContextUsageTypeNameToEnumValue contextUsageTypeMap[] = { - // Entries must be sorted by usageTypeName; entry with NULL name terminates list. - { "day-format-except-narrow", DateFormatSymbols::kCapContextUsageDayFormat }, - { "day-narrow", DateFormatSymbols::kCapContextUsageDayNarrow }, - { "day-standalone-except-narrow", DateFormatSymbols::kCapContextUsageDayStandalone }, - { "era-abbr", DateFormatSymbols::kCapContextUsageEraAbbrev }, - { "era-name", DateFormatSymbols::kCapContextUsageEraWide }, - { "era-narrow", DateFormatSymbols::kCapContextUsageEraNarrow }, - { "metazone-long", DateFormatSymbols::kCapContextUsageMetazoneLong }, - { "metazone-short", DateFormatSymbols::kCapContextUsageMetazoneShort }, - { "month-format-except-narrow", DateFormatSymbols::kCapContextUsageMonthFormat }, - { "month-narrow", DateFormatSymbols::kCapContextUsageMonthNarrow }, - { "month-standalone-except-narrow", DateFormatSymbols::kCapContextUsageMonthStandalone }, - { "zone-long", DateFormatSymbols::kCapContextUsageZoneLong }, - { "zone-short", DateFormatSymbols::kCapContextUsageZoneShort }, - { NULL, (DateFormatSymbols::ECapitalizationContextUsageType)0 }, -}; - -// Resource keys to look up localized strings for day periods. -// The first one must be midnight and the second must be noon, so that their indices coincide -// with the am/pm field. Formatting and parsing code for day periods relies on this coincidence. -static const char *dayPeriodKeys[] = {"midnight", "noon", - "morning1", "afternoon1", "evening1", "night1", - "morning2", "afternoon2", "evening2", "night2"}; - -UnicodeString* loadDayPeriodStrings(CalendarDataSink &sink, CharString &path, - int32_t &stringCount, UErrorCode &status) { - if (U_FAILURE(status)) { return NULL; } - - UnicodeString pathUString(path.data(), -1, US_INV); - Hashtable* map = static_cast(sink.maps.get(pathUString)); - - stringCount = UPRV_LENGTHOF(dayPeriodKeys); - UnicodeString *strings = new UnicodeString[stringCount]; - if (strings == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - if (map != NULL) { - for (int32_t i = 0; i < stringCount; ++i) { - UnicodeString dayPeriodKey(dayPeriodKeys[i], -1, US_INV); - UnicodeString *dayPeriod = static_cast(map->get(dayPeriodKey)); - if (dayPeriod != NULL) { - strings[i].fastCopyFrom(*dayPeriod); - } else { - strings[i].setToBogus(); - } - } - } else { - for (int32_t i = 0; i < stringCount; i++) { - strings[i].setToBogus(); - } - } - return strings; -} - - -void -DateFormatSymbols::initializeData(const Locale& locale, const char *type, UErrorCode& status, UBool useLastResortData) -{ - int32_t len = 0; - /* In case something goes wrong, initialize all of the data to NULL. */ - fEras = NULL; - fErasCount = 0; - fEraNames = NULL; - fEraNamesCount = 0; - fNarrowEras = NULL; - fNarrowErasCount = 0; - fMonths = NULL; - fMonthsCount=0; - fShortMonths = NULL; - fShortMonthsCount=0; - fNarrowMonths = NULL; - fNarrowMonthsCount=0; - fStandaloneMonths = NULL; - fStandaloneMonthsCount=0; - fStandaloneShortMonths = NULL; - fStandaloneShortMonthsCount=0; - fStandaloneNarrowMonths = NULL; - fStandaloneNarrowMonthsCount=0; - fWeekdays = NULL; - fWeekdaysCount=0; - fShortWeekdays = NULL; - fShortWeekdaysCount=0; - fShorterWeekdays = NULL; - fShorterWeekdaysCount=0; - fNarrowWeekdays = NULL; - fNarrowWeekdaysCount=0; - fStandaloneWeekdays = NULL; - fStandaloneWeekdaysCount=0; - fStandaloneShortWeekdays = NULL; - fStandaloneShortWeekdaysCount=0; - fStandaloneShorterWeekdays = NULL; - fStandaloneShorterWeekdaysCount=0; - fStandaloneNarrowWeekdays = NULL; - fStandaloneNarrowWeekdaysCount=0; - fAmPms = NULL; - fAmPmsCount=0; - fNarrowAmPms = NULL; - fNarrowAmPmsCount=0; - fTimeSeparator.setToBogus(); - fQuarters = NULL; - fQuartersCount = 0; - fShortQuarters = NULL; - fShortQuartersCount = 0; - fNarrowQuarters = NULL; - fNarrowQuartersCount = 0; - fStandaloneQuarters = NULL; - fStandaloneQuartersCount = 0; - fStandaloneShortQuarters = NULL; - fStandaloneShortQuartersCount = 0; - fStandaloneNarrowQuarters = NULL; - fStandaloneNarrowQuartersCount = 0; - fLeapMonthPatterns = NULL; - fLeapMonthPatternsCount = 0; - fShortYearNames = NULL; - fShortYearNamesCount = 0; - fShortZodiacNames = NULL; - fShortZodiacNamesCount = 0; - fZoneStringsRowCount = 0; - fZoneStringsColCount = 0; - fZoneStrings = NULL; - fLocaleZoneStrings = NULL; - fAbbreviatedDayPeriods = NULL; - fAbbreviatedDayPeriodsCount = 0; - fWideDayPeriods = NULL; - fWideDayPeriodsCount = 0; - fNarrowDayPeriods = NULL; - fNarrowDayPeriodsCount = 0; - fStandaloneAbbreviatedDayPeriods = NULL; - fStandaloneAbbreviatedDayPeriodsCount = 0; - fStandaloneWideDayPeriods = NULL; - fStandaloneWideDayPeriodsCount = 0; - fStandaloneNarrowDayPeriods = NULL; - fStandaloneNarrowDayPeriodsCount = 0; - uprv_memset(fCapitalization, 0, sizeof(fCapitalization)); - - // We need to preserve the requested locale for - // lazy ZoneStringFormat instantiation. ZoneStringFormat - // is region sensitive, thus, bundle locale bundle's locale - // is not sufficient. - fZSFLocale = locale; - - if (U_FAILURE(status)) return; - - // Create a CalendarDataSink to process this data and the resource bundles - CalendarDataSink calendarSink(status); - UResourceBundle *rb = ures_open(NULL, locale.getBaseName(), &status); - UResourceBundle *cb = ures_getByKey(rb, gCalendarTag, NULL, &status); - - if (U_FAILURE(status)) return; - - // Iterate over the resource bundle data following the fallbacks through different calendar types - UnicodeString calendarType((type != NULL && *type != '\0')? type : gGregorianTag, -1, US_INV); - while (!calendarType.isBogus()) { - CharString calendarTypeBuffer; - calendarTypeBuffer.appendInvariantChars(calendarType, status); - if (U_FAILURE(status)) { return; } - const char *calendarTypeCArray = calendarTypeBuffer.data(); - - // Enumerate this calendar type. If the calendar is not found fallback to gregorian - UErrorCode oldStatus = status; - UResourceBundle *ctb = ures_getByKeyWithFallback(cb, calendarTypeCArray, NULL, &status); - if (status == U_MISSING_RESOURCE_ERROR) { - ures_close(ctb); - if (uprv_strcmp(calendarTypeCArray, gGregorianTag) != 0) { - calendarType.setTo(false, kGregorianTagUChar, UPRV_LENGTHOF(kGregorianTagUChar)); - calendarSink.visitAllResources(); - status = oldStatus; - continue; - } - return; - } - - calendarSink.preEnumerate(calendarType); - ures_getAllItemsWithFallback(ctb, "", calendarSink, status); - ures_close(ctb); - if (U_FAILURE(status)) break; - - // Stop loading when gregorian was loaded - if (uprv_strcmp(calendarTypeCArray, gGregorianTag) == 0) { - break; - } - - // Get the next calendar type to process from the sink - calendarType = calendarSink.nextCalendarType; - - // Gregorian is always the last fallback - if (calendarType.isBogus()) { - calendarType.setTo(false, kGregorianTagUChar, UPRV_LENGTHOF(kGregorianTagUChar)); - calendarSink.visitAllResources(); - } - } - - // CharString object to build paths - CharString path; - - // Load Leap Month Patterns - UErrorCode tempStatus = status; - fLeapMonthPatterns = newUnicodeStringArray(kMonthPatternsCount); - if (fLeapMonthPatterns) { - initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternFormatWide, calendarSink, - buildResourcePath(path, gMonthPatternsTag, gNamesFormatTag, gNamesWideTag, tempStatus), tempStatus); - initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternFormatAbbrev, calendarSink, - buildResourcePath(path, gMonthPatternsTag, gNamesFormatTag, gNamesAbbrTag, tempStatus), tempStatus); - initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternFormatNarrow, calendarSink, - buildResourcePath(path, gMonthPatternsTag, gNamesFormatTag, gNamesNarrowTag, tempStatus), tempStatus); - initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternStandaloneWide, calendarSink, - buildResourcePath(path, gMonthPatternsTag, gNamesStandaloneTag, gNamesWideTag, tempStatus), tempStatus); - initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternStandaloneAbbrev, calendarSink, - buildResourcePath(path, gMonthPatternsTag, gNamesStandaloneTag, gNamesAbbrTag, tempStatus), tempStatus); - initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternStandaloneNarrow, calendarSink, - buildResourcePath(path, gMonthPatternsTag, gNamesStandaloneTag, gNamesNarrowTag, tempStatus), tempStatus); - initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternNumeric, calendarSink, - buildResourcePath(path, gMonthPatternsTag, gNamesNumericTag, gNamesAllTag, tempStatus), tempStatus); - if (U_SUCCESS(tempStatus)) { - // Hack to fix bad C inheritance for dangi monthPatterns (OK in J); this should be handled by aliases in root, but isn't. - // The ordering of the following statements is important. - if (fLeapMonthPatterns[kLeapMonthPatternFormatAbbrev].isEmpty()) { - fLeapMonthPatterns[kLeapMonthPatternFormatAbbrev].setTo(fLeapMonthPatterns[kLeapMonthPatternFormatWide]); - } - if (fLeapMonthPatterns[kLeapMonthPatternFormatNarrow].isEmpty()) { - fLeapMonthPatterns[kLeapMonthPatternFormatNarrow].setTo(fLeapMonthPatterns[kLeapMonthPatternStandaloneNarrow]); - } - if (fLeapMonthPatterns[kLeapMonthPatternStandaloneWide].isEmpty()) { - fLeapMonthPatterns[kLeapMonthPatternStandaloneWide].setTo(fLeapMonthPatterns[kLeapMonthPatternFormatWide]); - } - if (fLeapMonthPatterns[kLeapMonthPatternStandaloneAbbrev].isEmpty()) { - fLeapMonthPatterns[kLeapMonthPatternStandaloneAbbrev].setTo(fLeapMonthPatterns[kLeapMonthPatternFormatAbbrev]); - } - // end of hack - fLeapMonthPatternsCount = kMonthPatternsCount; - } else { - delete[] fLeapMonthPatterns; - fLeapMonthPatterns = NULL; - } - } - - // Load cyclic names sets - tempStatus = status; - initField(&fShortYearNames, fShortYearNamesCount, calendarSink, - buildResourcePath(path, gCyclicNameSetsTag, gNameSetYearsTag, gNamesFormatTag, gNamesAbbrTag, tempStatus), tempStatus); - initField(&fShortZodiacNames, fShortZodiacNamesCount, calendarSink, - buildResourcePath(path, gCyclicNameSetsTag, gNameSetZodiacsTag, gNamesFormatTag, gNamesAbbrTag, tempStatus), tempStatus); - - // Load context transforms and capitalization - tempStatus = U_ZERO_ERROR; - UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &tempStatus); - if (U_SUCCESS(tempStatus)) { - UResourceBundle *contextTransforms = ures_getByKeyWithFallback(localeBundle, gContextTransformsTag, NULL, &tempStatus); - if (U_SUCCESS(tempStatus)) { - UResourceBundle *contextTransformUsage; - while ( (contextTransformUsage = ures_getNextResource(contextTransforms, NULL, &tempStatus)) != NULL ) { - const int32_t * intVector = ures_getIntVector(contextTransformUsage, &len, &status); - if (U_SUCCESS(tempStatus) && intVector != NULL && len >= 2) { - const char* usageType = ures_getKey(contextTransformUsage); - if (usageType != NULL) { - const ContextUsageTypeNameToEnumValue * typeMapPtr = contextUsageTypeMap; - int32_t compResult = 0; - // linear search; list is short and we cannot be sure that bsearch is available - while ( typeMapPtr->usageTypeName != NULL && (compResult = uprv_strcmp(usageType, typeMapPtr->usageTypeName)) > 0 ) { - ++typeMapPtr; - } - if (typeMapPtr->usageTypeName != NULL && compResult == 0) { - fCapitalization[typeMapPtr->usageTypeEnumValue][0] = static_cast(intVector[0]); - fCapitalization[typeMapPtr->usageTypeEnumValue][1] = static_cast(intVector[1]); - } - } - } - tempStatus = U_ZERO_ERROR; - ures_close(contextTransformUsage); - } - ures_close(contextTransforms); - } - - tempStatus = U_ZERO_ERROR; - const LocalPointer numberingSystem( - NumberingSystem::createInstance(locale, tempStatus), tempStatus); - if (U_SUCCESS(tempStatus)) { - // These functions all fail gracefully if passed NULL pointers and - // do nothing unless U_SUCCESS(tempStatus), so it's only necessary - // to check for errors once after all calls are made. - const LocalUResourceBundlePointer numberElementsData(ures_getByKeyWithFallback( - localeBundle, gNumberElementsTag, NULL, &tempStatus)); - const LocalUResourceBundlePointer nsNameData(ures_getByKeyWithFallback( - numberElementsData.getAlias(), numberingSystem->getName(), NULL, &tempStatus)); - const LocalUResourceBundlePointer symbolsData(ures_getByKeyWithFallback( - nsNameData.getAlias(), gSymbolsTag, NULL, &tempStatus)); - fTimeSeparator = ures_getUnicodeStringByKey( - symbolsData.getAlias(), gTimeSeparatorTag, &tempStatus); - if (U_FAILURE(tempStatus)) { - fTimeSeparator.setToBogus(); - } - } - - ures_close(localeBundle); - } - - if (fTimeSeparator.isBogus()) { - fTimeSeparator.setTo(DateFormatSymbols::DEFAULT_TIME_SEPARATOR); - } - - // Load day periods - fWideDayPeriods = loadDayPeriodStrings(calendarSink, - buildResourcePath(path, gDayPeriodTag, gNamesFormatTag, gNamesWideTag, status), - fWideDayPeriodsCount, status); - fNarrowDayPeriods = loadDayPeriodStrings(calendarSink, - buildResourcePath(path, gDayPeriodTag, gNamesFormatTag, gNamesNarrowTag, status), - fNarrowDayPeriodsCount, status); - fAbbreviatedDayPeriods = loadDayPeriodStrings(calendarSink, - buildResourcePath(path, gDayPeriodTag, gNamesFormatTag, gNamesAbbrTag, status), - fAbbreviatedDayPeriodsCount, status); - fStandaloneWideDayPeriods = loadDayPeriodStrings(calendarSink, - buildResourcePath(path, gDayPeriodTag, gNamesStandaloneTag, gNamesWideTag, status), - fStandaloneWideDayPeriodsCount, status); - fStandaloneNarrowDayPeriods = loadDayPeriodStrings(calendarSink, - buildResourcePath(path, gDayPeriodTag, gNamesStandaloneTag, gNamesNarrowTag, status), - fStandaloneNarrowDayPeriodsCount, status); - fStandaloneAbbreviatedDayPeriods = loadDayPeriodStrings(calendarSink, - buildResourcePath(path, gDayPeriodTag, gNamesStandaloneTag, gNamesAbbrTag, status), - fStandaloneAbbreviatedDayPeriodsCount, status); - - U_LOCALE_BASED(locBased, *this); - // if we make it to here, the resource data is cool, and we can get everything out - // of it that we need except for the time-zone and localized-pattern data, which - // are stored in a separate file - locBased.setLocaleIDs(ures_getLocaleByType(cb, ULOC_VALID_LOCALE, &status), - ures_getLocaleByType(cb, ULOC_ACTUAL_LOCALE, &status)); - - // Load eras - initField(&fEras, fErasCount, calendarSink, buildResourcePath(path, gErasTag, gNamesAbbrTag, status), status); - UErrorCode oldStatus = status; - initField(&fEraNames, fEraNamesCount, calendarSink, buildResourcePath(path, gErasTag, gNamesWideTag, status), status); - if (status == U_MISSING_RESOURCE_ERROR) { // Workaround because eras/wide was omitted from CLDR 1.3 - status = oldStatus; - assignArray(fEraNames, fEraNamesCount, fEras, fErasCount); - } - // current ICU4J falls back to abbreviated if narrow eras are missing, so we will too - oldStatus = status; - initField(&fNarrowEras, fNarrowErasCount, calendarSink, buildResourcePath(path, gErasTag, gNamesNarrowTag, status), status); - if (status == U_MISSING_RESOURCE_ERROR) { // Workaround because eras/wide was omitted from CLDR 1.3 - status = oldStatus; - assignArray(fNarrowEras, fNarrowErasCount, fEras, fErasCount); - } - - // Load month names - initField(&fMonths, fMonthsCount, calendarSink, - buildResourcePath(path, gMonthNamesTag, gNamesFormatTag, gNamesWideTag, status), status); - initField(&fShortMonths, fShortMonthsCount, calendarSink, - buildResourcePath(path, gMonthNamesTag, gNamesFormatTag, gNamesAbbrTag, status), status); - initField(&fStandaloneMonths, fStandaloneMonthsCount, calendarSink, - buildResourcePath(path, gMonthNamesTag, gNamesStandaloneTag, gNamesWideTag, status), status); - if (status == U_MISSING_RESOURCE_ERROR) { /* If standalone/wide not available, use format/wide */ - status = U_ZERO_ERROR; - assignArray(fStandaloneMonths, fStandaloneMonthsCount, fMonths, fMonthsCount); - } - initField(&fStandaloneShortMonths, fStandaloneShortMonthsCount, calendarSink, - buildResourcePath(path, gMonthNamesTag, gNamesStandaloneTag, gNamesAbbrTag, status), status); - if (status == U_MISSING_RESOURCE_ERROR) { /* If standalone/abbreviated not available, use format/abbreviated */ - status = U_ZERO_ERROR; - assignArray(fStandaloneShortMonths, fStandaloneShortMonthsCount, fShortMonths, fShortMonthsCount); - } - - UErrorCode narrowMonthsEC = status; - UErrorCode standaloneNarrowMonthsEC = status; - initField(&fNarrowMonths, fNarrowMonthsCount, calendarSink, - buildResourcePath(path, gMonthNamesTag, gNamesFormatTag, gNamesNarrowTag, narrowMonthsEC), narrowMonthsEC); - initField(&fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount, calendarSink, - buildResourcePath(path, gMonthNamesTag, gNamesStandaloneTag, gNamesNarrowTag, narrowMonthsEC), standaloneNarrowMonthsEC); - if (narrowMonthsEC == U_MISSING_RESOURCE_ERROR && standaloneNarrowMonthsEC != U_MISSING_RESOURCE_ERROR) { - // If format/narrow not available, use standalone/narrow - assignArray(fNarrowMonths, fNarrowMonthsCount, fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount); - } else if (narrowMonthsEC != U_MISSING_RESOURCE_ERROR && standaloneNarrowMonthsEC == U_MISSING_RESOURCE_ERROR) { - // If standalone/narrow not available, use format/narrow - assignArray(fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount, fNarrowMonths, fNarrowMonthsCount); - } else if (narrowMonthsEC == U_MISSING_RESOURCE_ERROR && standaloneNarrowMonthsEC == U_MISSING_RESOURCE_ERROR) { - // If neither is available, use format/abbreviated - assignArray(fNarrowMonths, fNarrowMonthsCount, fShortMonths, fShortMonthsCount); - assignArray(fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount, fShortMonths, fShortMonthsCount); - } - - // Load AM/PM markers; if wide or narrow not available, use short - UErrorCode ampmStatus = U_ZERO_ERROR; - initField(&fAmPms, fAmPmsCount, calendarSink, - buildResourcePath(path, gAmPmMarkersTag, ampmStatus), ampmStatus); - if (U_FAILURE(ampmStatus)) { - initField(&fAmPms, fAmPmsCount, calendarSink, - buildResourcePath(path, gAmPmMarkersAbbrTag, status), status); - } - ampmStatus = U_ZERO_ERROR; - initField(&fNarrowAmPms, fNarrowAmPmsCount, calendarSink, - buildResourcePath(path, gAmPmMarkersNarrowTag, ampmStatus), ampmStatus); - if (U_FAILURE(ampmStatus)) { - initField(&fNarrowAmPms, fNarrowAmPmsCount, calendarSink, - buildResourcePath(path, gAmPmMarkersAbbrTag, status), status); - } - - // Load quarters - initField(&fQuarters, fQuartersCount, calendarSink, - buildResourcePath(path, gQuartersTag, gNamesFormatTag, gNamesWideTag, status), status); - initField(&fShortQuarters, fShortQuartersCount, calendarSink, - buildResourcePath(path, gQuartersTag, gNamesFormatTag, gNamesAbbrTag, status), status); - - initField(&fStandaloneQuarters, fStandaloneQuartersCount, calendarSink, - buildResourcePath(path, gQuartersTag, gNamesStandaloneTag, gNamesWideTag, status), status); - if(status == U_MISSING_RESOURCE_ERROR) { - status = U_ZERO_ERROR; - assignArray(fStandaloneQuarters, fStandaloneQuartersCount, fQuarters, fQuartersCount); - } - initField(&fStandaloneShortQuarters, fStandaloneShortQuartersCount, calendarSink, - buildResourcePath(path, gQuartersTag, gNamesStandaloneTag, gNamesAbbrTag, status), status); - if(status == U_MISSING_RESOURCE_ERROR) { - status = U_ZERO_ERROR; - assignArray(fStandaloneShortQuarters, fStandaloneShortQuartersCount, fShortQuarters, fShortQuartersCount); - } - - // unlike the fields above, narrow format quarters fall back on narrow standalone quarters - initField(&fStandaloneNarrowQuarters, fStandaloneNarrowQuartersCount, calendarSink, - buildResourcePath(path, gQuartersTag, gNamesStandaloneTag, gNamesNarrowTag, status), status); - initField(&fNarrowQuarters, fNarrowQuartersCount, calendarSink, - buildResourcePath(path, gQuartersTag, gNamesFormatTag, gNamesNarrowTag, status), status); - if(status == U_MISSING_RESOURCE_ERROR) { - status = U_ZERO_ERROR; - assignArray(fNarrowQuarters, fNarrowQuartersCount, fStandaloneNarrowQuarters, fStandaloneNarrowQuartersCount); - } - - // ICU 3.8 or later version no longer uses localized date-time pattern characters by default (ticket#5597) - /* - // fastCopyFrom()/setTo() - see assignArray comments - resStr = ures_getStringByKey(fResourceBundle, gLocalPatternCharsTag, &len, &status); - fLocalPatternChars.setTo(true, resStr, len); - // If the locale data does not include new pattern chars, use the defaults - // TODO: Consider making this an error, since this may add conflicting characters. - if (len < PATTERN_CHARS_LEN) { - fLocalPatternChars.append(UnicodeString(true, &gPatternChars[len], PATTERN_CHARS_LEN-len)); - } - */ - fLocalPatternChars.setTo(true, gPatternChars, PATTERN_CHARS_LEN); - - // Format wide weekdays -> fWeekdays - // {sfb} fixed to handle 1-based weekdays - initField(&fWeekdays, fWeekdaysCount, calendarSink, - buildResourcePath(path, gDayNamesTag, gNamesFormatTag, gNamesWideTag, status), 1, status); - - // Format abbreviated weekdays -> fShortWeekdays - initField(&fShortWeekdays, fShortWeekdaysCount, calendarSink, - buildResourcePath(path, gDayNamesTag, gNamesFormatTag, gNamesAbbrTag, status), 1, status); - - // Format short weekdays -> fShorterWeekdays (fall back to abbreviated) - initField(&fShorterWeekdays, fShorterWeekdaysCount, calendarSink, - buildResourcePath(path, gDayNamesTag, gNamesFormatTag, gNamesShortTag, status), 1, status); - if (status == U_MISSING_RESOURCE_ERROR) { - status = U_ZERO_ERROR; - assignArray(fShorterWeekdays, fShorterWeekdaysCount, fShortWeekdays, fShortWeekdaysCount); - } - - // Stand-alone wide weekdays -> fStandaloneWeekdays - initField(&fStandaloneWeekdays, fStandaloneWeekdaysCount, calendarSink, - buildResourcePath(path, gDayNamesTag, gNamesStandaloneTag, gNamesWideTag, status), 1, status); - if (status == U_MISSING_RESOURCE_ERROR) { /* If standalone/wide is not available, use format/wide */ - status = U_ZERO_ERROR; - assignArray(fStandaloneWeekdays, fStandaloneWeekdaysCount, fWeekdays, fWeekdaysCount); - } - - // Stand-alone abbreviated weekdays -> fStandaloneShortWeekdays - initField(&fStandaloneShortWeekdays, fStandaloneShortWeekdaysCount, calendarSink, - buildResourcePath(path, gDayNamesTag, gNamesStandaloneTag, gNamesAbbrTag, status), 1, status); - if (status == U_MISSING_RESOURCE_ERROR) { /* If standalone/abbreviated is not available, use format/abbreviated */ - status = U_ZERO_ERROR; - assignArray(fStandaloneShortWeekdays, fStandaloneShortWeekdaysCount, fShortWeekdays, fShortWeekdaysCount); - } - - // Stand-alone short weekdays -> fStandaloneShorterWeekdays (fall back to format abbreviated) - initField(&fStandaloneShorterWeekdays, fStandaloneShorterWeekdaysCount, calendarSink, - buildResourcePath(path, gDayNamesTag, gNamesStandaloneTag, gNamesShortTag, status), 1, status); - if (status == U_MISSING_RESOURCE_ERROR) { /* If standalone/short is not available, use format/short */ - status = U_ZERO_ERROR; - assignArray(fStandaloneShorterWeekdays, fStandaloneShorterWeekdaysCount, fShorterWeekdays, fShorterWeekdaysCount); - } - - // Format narrow weekdays -> fNarrowWeekdays - UErrorCode narrowWeeksEC = status; - initField(&fNarrowWeekdays, fNarrowWeekdaysCount, calendarSink, - buildResourcePath(path, gDayNamesTag, gNamesFormatTag, gNamesNarrowTag, status), 1, narrowWeeksEC); - // Stand-alone narrow weekdays -> fStandaloneNarrowWeekdays - UErrorCode standaloneNarrowWeeksEC = status; - initField(&fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount, calendarSink, - buildResourcePath(path, gDayNamesTag, gNamesStandaloneTag, gNamesNarrowTag, status), 1, standaloneNarrowWeeksEC); - - if (narrowWeeksEC == U_MISSING_RESOURCE_ERROR && standaloneNarrowWeeksEC != U_MISSING_RESOURCE_ERROR) { - // If format/narrow not available, use standalone/narrow - assignArray(fNarrowWeekdays, fNarrowWeekdaysCount, fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount); - } else if (narrowWeeksEC != U_MISSING_RESOURCE_ERROR && standaloneNarrowWeeksEC == U_MISSING_RESOURCE_ERROR) { - // If standalone/narrow not available, use format/narrow - assignArray(fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount, fNarrowWeekdays, fNarrowWeekdaysCount); - } else if (narrowWeeksEC == U_MISSING_RESOURCE_ERROR && standaloneNarrowWeeksEC == U_MISSING_RESOURCE_ERROR ) { - // If neither is available, use format/abbreviated - assignArray(fNarrowWeekdays, fNarrowWeekdaysCount, fShortWeekdays, fShortWeekdaysCount); - assignArray(fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount, fShortWeekdays, fShortWeekdaysCount); - } - - // Last resort fallback in case previous data wasn't loaded - if (U_FAILURE(status)) - { - if (useLastResortData) - { - // Handle the case in which there is no resource data present. - // We don't have to generate usable patterns in this situation; - // we just need to produce something that will be semi-intelligible - // in most locales. - - status = U_USING_FALLBACK_WARNING; - //TODO(fabalbon): make sure we are storing las resort data for all fields in here. - initField(&fEras, fErasCount, (const UChar *)gLastResortEras, kEraNum, kEraLen, status); - initField(&fEraNames, fEraNamesCount, (const UChar *)gLastResortEras, kEraNum, kEraLen, status); - initField(&fNarrowEras, fNarrowErasCount, (const UChar *)gLastResortEras, kEraNum, kEraLen, status); - initField(&fMonths, fMonthsCount, (const UChar *)gLastResortMonthNames, kMonthNum, kMonthLen, status); - initField(&fShortMonths, fShortMonthsCount, (const UChar *)gLastResortMonthNames, kMonthNum, kMonthLen, status); - initField(&fNarrowMonths, fNarrowMonthsCount, (const UChar *)gLastResortMonthNames, kMonthNum, kMonthLen, status); - initField(&fStandaloneMonths, fStandaloneMonthsCount, (const UChar *)gLastResortMonthNames, kMonthNum, kMonthLen, status); - initField(&fStandaloneShortMonths, fStandaloneShortMonthsCount, (const UChar *)gLastResortMonthNames, kMonthNum, kMonthLen, status); - initField(&fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount, (const UChar *)gLastResortMonthNames, kMonthNum, kMonthLen, status); - initField(&fWeekdays, fWeekdaysCount, (const UChar *)gLastResortDayNames, kDayNum, kDayLen, status); - initField(&fShortWeekdays, fShortWeekdaysCount, (const UChar *)gLastResortDayNames, kDayNum, kDayLen, status); - initField(&fShorterWeekdays, fShorterWeekdaysCount, (const UChar *)gLastResortDayNames, kDayNum, kDayLen, status); - initField(&fNarrowWeekdays, fNarrowWeekdaysCount, (const UChar *)gLastResortDayNames, kDayNum, kDayLen, status); - initField(&fStandaloneWeekdays, fStandaloneWeekdaysCount, (const UChar *)gLastResortDayNames, kDayNum, kDayLen, status); - initField(&fStandaloneShortWeekdays, fStandaloneShortWeekdaysCount, (const UChar *)gLastResortDayNames, kDayNum, kDayLen, status); - initField(&fStandaloneShorterWeekdays, fStandaloneShorterWeekdaysCount, (const UChar *)gLastResortDayNames, kDayNum, kDayLen, status); - initField(&fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount, (const UChar *)gLastResortDayNames, kDayNum, kDayLen, status); - initField(&fAmPms, fAmPmsCount, (const UChar *)gLastResortAmPmMarkers, kAmPmNum, kAmPmLen, status); - initField(&fNarrowAmPms, fNarrowAmPmsCount, (const UChar *)gLastResortAmPmMarkers, kAmPmNum, kAmPmLen, status); - initField(&fQuarters, fQuartersCount, (const UChar *)gLastResortQuarters, kQuarterNum, kQuarterLen, status); - initField(&fShortQuarters, fShortQuartersCount, (const UChar *)gLastResortQuarters, kQuarterNum, kQuarterLen, status); - initField(&fNarrowQuarters, fNarrowQuartersCount, (const UChar *)gLastResortQuarters, kQuarterNum, kQuarterLen, status); - initField(&fStandaloneQuarters, fStandaloneQuartersCount, (const UChar *)gLastResortQuarters, kQuarterNum, kQuarterLen, status); - initField(&fStandaloneShortQuarters, fStandaloneShortQuartersCount, (const UChar *)gLastResortQuarters, kQuarterNum, kQuarterLen, status); - initField(&fStandaloneNarrowQuarters, fStandaloneNarrowQuartersCount, (const UChar *)gLastResortQuarters, kQuarterNum, kQuarterLen, status); - fLocalPatternChars.setTo(true, gPatternChars, PATTERN_CHARS_LEN); - } - } - - // Close resources - ures_close(cb); - ures_close(rb); -} - -Locale -DateFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const { - U_LOCALE_BASED(locBased, *this); - return locBased.getLocale(type, status); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1997-2016, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* File DTFMTSYM.CPP +* +* Modification History: +* +* Date Name Description +* 02/19/97 aliu Converted from java. +* 07/21/98 stephen Added getZoneIndex +* Changed weekdays/short weekdays to be one-based +* 06/14/99 stephen Removed SimpleDateFormat::fgTimeZoneDataSuffix +* 11/16/99 weiv Added 'Y' and 'e' to fgPatternChars +* 03/27/00 weiv Keeping resource bundle around! +* 06/30/05 emmons Added eraNames, narrow month/day, standalone context +* 10/12/05 emmons Added setters for eraNames, month/day by width/context +******************************************************************************* +*/ + +#include + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#include "unicode/ustring.h" +#include "unicode/localpointer.h" +#include "unicode/dtfmtsym.h" +#include "unicode/smpdtfmt.h" +#include "unicode/msgfmt.h" +#include "unicode/numsys.h" +#include "unicode/tznames.h" +#include "cpputils.h" +#include "umutex.h" +#include "cmemory.h" +#include "cstring.h" +#include "charstr.h" +#include "dt_impl.h" +#include "locbased.h" +#include "gregoimp.h" +#include "hash.h" +#include "uassert.h" +#include "uresimp.h" +#include "ureslocs.h" +#include "uvector.h" +#include "shareddateformatsymbols.h" +#include "unicode/calendar.h" +#include "unifiedcache.h" + +// ***************************************************************************** +// class DateFormatSymbols +// ***************************************************************************** + +/** + * These are static arrays we use only in the case where we have no + * resource data. + */ + +#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR +#define PATTERN_CHARS_LEN 38 +#else +#define PATTERN_CHARS_LEN 37 +#endif + +/** + * Unlocalized date-time pattern characters. For example: 'y', 'd', etc. All + * locales use the same these unlocalized pattern characters. + */ +static const char16_t gPatternChars[] = { + // if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR: + // GyMdkHmsSEDFwWahKzYeugAZvcLQqVUOXxrbB: + // else: + // GyMdkHmsSEDFwWahKzYeugAZvcLQqVUOXxrbB + + 0x47, 0x79, 0x4D, 0x64, 0x6B, 0x48, 0x6D, 0x73, 0x53, 0x45, + 0x44, 0x46, 0x77, 0x57, 0x61, 0x68, 0x4B, 0x7A, 0x59, 0x65, + 0x75, 0x67, 0x41, 0x5A, 0x76, 0x63, 0x4c, 0x51, 0x71, 0x56, + 0x55, 0x4F, 0x58, 0x78, 0x72, 0x62, 0x42, +#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR + 0x3a, +#endif + 0 +}; + +//------------------------------------------------------ +// Strings of last resort. These are only used if we have no resource +// files. They aren't designed for actual use, just for backup. + +// These are the month names and abbreviations of last resort. +static const char16_t gLastResortMonthNames[13][3] = +{ + {0x0030, 0x0031, 0x0000}, /* "01" */ + {0x0030, 0x0032, 0x0000}, /* "02" */ + {0x0030, 0x0033, 0x0000}, /* "03" */ + {0x0030, 0x0034, 0x0000}, /* "04" */ + {0x0030, 0x0035, 0x0000}, /* "05" */ + {0x0030, 0x0036, 0x0000}, /* "06" */ + {0x0030, 0x0037, 0x0000}, /* "07" */ + {0x0030, 0x0038, 0x0000}, /* "08" */ + {0x0030, 0x0039, 0x0000}, /* "09" */ + {0x0031, 0x0030, 0x0000}, /* "10" */ + {0x0031, 0x0031, 0x0000}, /* "11" */ + {0x0031, 0x0032, 0x0000}, /* "12" */ + {0x0031, 0x0033, 0x0000} /* "13" */ +}; + +// These are the weekday names and abbreviations of last resort. +static const char16_t gLastResortDayNames[8][2] = +{ + {0x0030, 0x0000}, /* "0" */ + {0x0031, 0x0000}, /* "1" */ + {0x0032, 0x0000}, /* "2" */ + {0x0033, 0x0000}, /* "3" */ + {0x0034, 0x0000}, /* "4" */ + {0x0035, 0x0000}, /* "5" */ + {0x0036, 0x0000}, /* "6" */ + {0x0037, 0x0000} /* "7" */ +}; + +// These are the quarter names and abbreviations of last resort. +static const char16_t gLastResortQuarters[4][2] = +{ + {0x0031, 0x0000}, /* "1" */ + {0x0032, 0x0000}, /* "2" */ + {0x0033, 0x0000}, /* "3" */ + {0x0034, 0x0000}, /* "4" */ +}; + +// These are the am/pm and BC/AD markers of last resort. +static const char16_t gLastResortAmPmMarkers[2][3] = +{ + {0x0041, 0x004D, 0x0000}, /* "AM" */ + {0x0050, 0x004D, 0x0000} /* "PM" */ +}; + +static const char16_t gLastResortEras[2][3] = +{ + {0x0042, 0x0043, 0x0000}, /* "BC" */ + {0x0041, 0x0044, 0x0000} /* "AD" */ +}; + +/* Sizes for the last resort string arrays */ +typedef enum LastResortSize { + kMonthNum = 13, + kMonthLen = 3, + + kDayNum = 8, + kDayLen = 2, + + kAmPmNum = 2, + kAmPmLen = 3, + + kQuarterNum = 4, + kQuarterLen = 2, + + kEraNum = 2, + kEraLen = 3, + + kZoneNum = 5, + kZoneLen = 4, + + kGmtHourNum = 4, + kGmtHourLen = 10 +} LastResortSize; + +U_NAMESPACE_BEGIN + +SharedDateFormatSymbols::~SharedDateFormatSymbols() { +} + +template<> U_I18N_API +const SharedDateFormatSymbols * + LocaleCacheKey::createObject( + const void * /*unusedContext*/, UErrorCode &status) const { + char type[256]; + Calendar::getCalendarTypeFromLocale(fLoc, type, UPRV_LENGTHOF(type), status); + if (U_FAILURE(status)) { + return nullptr; + } + SharedDateFormatSymbols *shared + = new SharedDateFormatSymbols(fLoc, type, status); + if (shared == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + if (U_FAILURE(status)) { + delete shared; + return nullptr; + } + shared->addRef(); + return shared; +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateFormatSymbols) + +#define kSUPPLEMENTAL "supplementalData" + +/** + * These are the tags we expect to see in normal resource bundle files associated + * with a locale and calendar + */ +static const char gCalendarTag[]="calendar"; +static const char gGregorianTag[]="gregorian"; +static const char gErasTag[]="eras"; +static const char gCyclicNameSetsTag[]="cyclicNameSets"; +static const char gNameSetYearsTag[]="years"; +static const char gNameSetZodiacsTag[]="zodiacs"; +static const char gMonthNamesTag[]="monthNames"; +static const char gMonthPatternsTag[]="monthPatterns"; +static const char gDayNamesTag[]="dayNames"; +static const char gNamesWideTag[]="wide"; +static const char gNamesAbbrTag[]="abbreviated"; +static const char gNamesShortTag[]="short"; +static const char gNamesNarrowTag[]="narrow"; +static const char gNamesAllTag[]="all"; +static const char gNamesFormatTag[]="format"; +static const char gNamesStandaloneTag[]="stand-alone"; +static const char gNamesNumericTag[]="numeric"; +static const char gAmPmMarkersTag[]="AmPmMarkers"; +static const char gAmPmMarkersAbbrTag[]="AmPmMarkersAbbr"; +static const char gAmPmMarkersNarrowTag[]="AmPmMarkersNarrow"; +static const char gQuartersTag[]="quarters"; +static const char gNumberElementsTag[]="NumberElements"; +static const char gSymbolsTag[]="symbols"; +static const char gTimeSeparatorTag[]="timeSeparator"; +static const char gDayPeriodTag[]="dayPeriod"; + +// static const char gZoneStringsTag[]="zoneStrings"; + +// static const char gLocalPatternCharsTag[]="localPatternChars"; + +static const char gContextTransformsTag[]="contextTransforms"; + +/** + * Jitterbug 2974: MSVC has a bug whereby new X[0] behaves badly. + * Work around this. + */ +static inline UnicodeString* newUnicodeStringArray(size_t count) { + return new UnicodeString[count ? count : 1]; +} + +//------------------------------------------------------ + +DateFormatSymbols * U_EXPORT2 +DateFormatSymbols::createForLocale( + const Locale& locale, UErrorCode &status) { + const SharedDateFormatSymbols *shared = nullptr; + UnifiedCache::getByLocale(locale, shared, status); + if (U_FAILURE(status)) { + return nullptr; + } + DateFormatSymbols *result = new DateFormatSymbols(shared->get()); + shared->removeRef(); + if (result == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + return result; +} + +DateFormatSymbols::DateFormatSymbols(const Locale& locale, + UErrorCode& status) + : UObject() +{ + initializeData(locale, nullptr, status); +} + +DateFormatSymbols::DateFormatSymbols(UErrorCode& status) + : UObject() +{ + initializeData(Locale::getDefault(), nullptr, status, true); +} + + +DateFormatSymbols::DateFormatSymbols(const Locale& locale, + const char *type, + UErrorCode& status) + : UObject() +{ + initializeData(locale, type, status); +} + +DateFormatSymbols::DateFormatSymbols(const char *type, UErrorCode& status) + : UObject() +{ + initializeData(Locale::getDefault(), type, status, true); +} + +DateFormatSymbols::DateFormatSymbols(const DateFormatSymbols& other) + : UObject(other) +{ + copyData(other); +} + +void +DateFormatSymbols::assignArray(UnicodeString*& dstArray, + int32_t& dstCount, + const UnicodeString* srcArray, + int32_t srcCount) +{ + // assignArray() is only called by copyData() and initializeData(), which in turn + // implements the copy constructor and the assignment operator. + // All strings in a DateFormatSymbols object are created in one of the following + // three ways that all allow to safely use UnicodeString::fastCopyFrom(): + // - readonly-aliases from resource bundles + // - readonly-aliases or allocated strings from constants + // - safely cloned strings (with owned buffers) from setXYZ() functions + // + // Note that this is true for as long as DateFormatSymbols can be constructed + // only from a locale bundle or set via the cloning API, + // *and* for as long as all the strings are in *private* fields, preventing + // a subclass from creating these strings in an "unsafe" way (with respect to fastCopyFrom()). + if(srcArray == nullptr) { + // Do not attempt to copy bogus input (which will crash). + // Note that this assignArray method already had the potential to return a null dstArray; + // see handling below for "if(dstArray != nullptr)". + dstCount = 0; + dstArray = nullptr; + return; + } + dstCount = srcCount; + dstArray = newUnicodeStringArray(srcCount); + if(dstArray != nullptr) { + int32_t i; + for(i=0; i= 0; i--) { + delete[] fZoneStrings[i]; + } + uprv_free(fZoneStrings); + fZoneStrings = nullptr; + } +} + +/** + * Copy all of the other's data to this. + */ +void +DateFormatSymbols::copyData(const DateFormatSymbols& other) { + UErrorCode status = U_ZERO_ERROR; + U_LOCALE_BASED(locBased, *this); + locBased.setLocaleIDs( + other.getLocale(ULOC_VALID_LOCALE, status), + other.getLocale(ULOC_ACTUAL_LOCALE, status)); + assignArray(fEras, fErasCount, other.fEras, other.fErasCount); + assignArray(fEraNames, fEraNamesCount, other.fEraNames, other.fEraNamesCount); + assignArray(fNarrowEras, fNarrowErasCount, other.fNarrowEras, other.fNarrowErasCount); + assignArray(fMonths, fMonthsCount, other.fMonths, other.fMonthsCount); + assignArray(fShortMonths, fShortMonthsCount, other.fShortMonths, other.fShortMonthsCount); + assignArray(fNarrowMonths, fNarrowMonthsCount, other.fNarrowMonths, other.fNarrowMonthsCount); + assignArray(fStandaloneMonths, fStandaloneMonthsCount, other.fStandaloneMonths, other.fStandaloneMonthsCount); + assignArray(fStandaloneShortMonths, fStandaloneShortMonthsCount, other.fStandaloneShortMonths, other.fStandaloneShortMonthsCount); + assignArray(fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount, other.fStandaloneNarrowMonths, other.fStandaloneNarrowMonthsCount); + assignArray(fWeekdays, fWeekdaysCount, other.fWeekdays, other.fWeekdaysCount); + assignArray(fShortWeekdays, fShortWeekdaysCount, other.fShortWeekdays, other.fShortWeekdaysCount); + assignArray(fShorterWeekdays, fShorterWeekdaysCount, other.fShorterWeekdays, other.fShorterWeekdaysCount); + assignArray(fNarrowWeekdays, fNarrowWeekdaysCount, other.fNarrowWeekdays, other.fNarrowWeekdaysCount); + assignArray(fStandaloneWeekdays, fStandaloneWeekdaysCount, other.fStandaloneWeekdays, other.fStandaloneWeekdaysCount); + assignArray(fStandaloneShortWeekdays, fStandaloneShortWeekdaysCount, other.fStandaloneShortWeekdays, other.fStandaloneShortWeekdaysCount); + assignArray(fStandaloneShorterWeekdays, fStandaloneShorterWeekdaysCount, other.fStandaloneShorterWeekdays, other.fStandaloneShorterWeekdaysCount); + assignArray(fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount, other.fStandaloneNarrowWeekdays, other.fStandaloneNarrowWeekdaysCount); + assignArray(fAmPms, fAmPmsCount, other.fAmPms, other.fAmPmsCount); + assignArray(fNarrowAmPms, fNarrowAmPmsCount, other.fNarrowAmPms, other.fNarrowAmPmsCount ); + fTimeSeparator.fastCopyFrom(other.fTimeSeparator); // fastCopyFrom() - see assignArray comments + assignArray(fQuarters, fQuartersCount, other.fQuarters, other.fQuartersCount); + assignArray(fShortQuarters, fShortQuartersCount, other.fShortQuarters, other.fShortQuartersCount); + assignArray(fNarrowQuarters, fNarrowQuartersCount, other.fNarrowQuarters, other.fNarrowQuartersCount); + assignArray(fStandaloneQuarters, fStandaloneQuartersCount, other.fStandaloneQuarters, other.fStandaloneQuartersCount); + assignArray(fStandaloneShortQuarters, fStandaloneShortQuartersCount, other.fStandaloneShortQuarters, other.fStandaloneShortQuartersCount); + assignArray(fStandaloneNarrowQuarters, fStandaloneNarrowQuartersCount, other.fStandaloneNarrowQuarters, other.fStandaloneNarrowQuartersCount); + assignArray(fWideDayPeriods, fWideDayPeriodsCount, + other.fWideDayPeriods, other.fWideDayPeriodsCount); + assignArray(fNarrowDayPeriods, fNarrowDayPeriodsCount, + other.fNarrowDayPeriods, other.fNarrowDayPeriodsCount); + assignArray(fAbbreviatedDayPeriods, fAbbreviatedDayPeriodsCount, + other.fAbbreviatedDayPeriods, other.fAbbreviatedDayPeriodsCount); + assignArray(fStandaloneWideDayPeriods, fStandaloneWideDayPeriodsCount, + other.fStandaloneWideDayPeriods, other.fStandaloneWideDayPeriodsCount); + assignArray(fStandaloneNarrowDayPeriods, fStandaloneNarrowDayPeriodsCount, + other.fStandaloneNarrowDayPeriods, other.fStandaloneNarrowDayPeriodsCount); + assignArray(fStandaloneAbbreviatedDayPeriods, fStandaloneAbbreviatedDayPeriodsCount, + other.fStandaloneAbbreviatedDayPeriods, other.fStandaloneAbbreviatedDayPeriodsCount); + if (other.fLeapMonthPatterns != nullptr) { + assignArray(fLeapMonthPatterns, fLeapMonthPatternsCount, other.fLeapMonthPatterns, other.fLeapMonthPatternsCount); + } else { + fLeapMonthPatterns = nullptr; + fLeapMonthPatternsCount = 0; + } + if (other.fShortYearNames != nullptr) { + assignArray(fShortYearNames, fShortYearNamesCount, other.fShortYearNames, other.fShortYearNamesCount); + } else { + fShortYearNames = nullptr; + fShortYearNamesCount = 0; + } + if (other.fShortZodiacNames != nullptr) { + assignArray(fShortZodiacNames, fShortZodiacNamesCount, other.fShortZodiacNames, other.fShortZodiacNamesCount); + } else { + fShortZodiacNames = nullptr; + fShortZodiacNamesCount = 0; + } + + if (other.fZoneStrings != nullptr) { + fZoneStringsColCount = other.fZoneStringsColCount; + fZoneStringsRowCount = other.fZoneStringsRowCount; + createZoneStrings((const UnicodeString**)other.fZoneStrings); + + } else { + fZoneStrings = nullptr; + fZoneStringsColCount = 0; + fZoneStringsRowCount = 0; + } + fZSFLocale = other.fZSFLocale; + // Other zone strings data is created on demand + fLocaleZoneStrings = nullptr; + + // fastCopyFrom() - see assignArray comments + fLocalPatternChars.fastCopyFrom(other.fLocalPatternChars); + + uprv_memcpy(fCapitalization, other.fCapitalization, sizeof(fCapitalization)); +} + +/** + * Assignment operator. + */ +DateFormatSymbols& DateFormatSymbols::operator=(const DateFormatSymbols& other) +{ + if (this == &other) { return *this; } // self-assignment: no-op + dispose(); + copyData(other); + + return *this; +} + +DateFormatSymbols::~DateFormatSymbols() +{ + dispose(); +} + +void DateFormatSymbols::dispose() +{ + delete[] fEras; + delete[] fEraNames; + delete[] fNarrowEras; + delete[] fMonths; + delete[] fShortMonths; + delete[] fNarrowMonths; + delete[] fStandaloneMonths; + delete[] fStandaloneShortMonths; + delete[] fStandaloneNarrowMonths; + delete[] fWeekdays; + delete[] fShortWeekdays; + delete[] fShorterWeekdays; + delete[] fNarrowWeekdays; + delete[] fStandaloneWeekdays; + delete[] fStandaloneShortWeekdays; + delete[] fStandaloneShorterWeekdays; + delete[] fStandaloneNarrowWeekdays; + delete[] fAmPms; + delete[] fNarrowAmPms; + delete[] fQuarters; + delete[] fShortQuarters; + delete[] fNarrowQuarters; + delete[] fStandaloneQuarters; + delete[] fStandaloneShortQuarters; + delete[] fStandaloneNarrowQuarters; + delete[] fLeapMonthPatterns; + delete[] fShortYearNames; + delete[] fShortZodiacNames; + delete[] fAbbreviatedDayPeriods; + delete[] fWideDayPeriods; + delete[] fNarrowDayPeriods; + delete[] fStandaloneAbbreviatedDayPeriods; + delete[] fStandaloneWideDayPeriods; + delete[] fStandaloneNarrowDayPeriods; + + disposeZoneStrings(); +} + +void DateFormatSymbols::disposeZoneStrings() +{ + if (fZoneStrings) { + for (int32_t row = 0; row < fZoneStringsRowCount; ++row) { + delete[] fZoneStrings[row]; + } + uprv_free(fZoneStrings); + } + if (fLocaleZoneStrings) { + for (int32_t row = 0; row < fZoneStringsRowCount; ++row) { + delete[] fLocaleZoneStrings[row]; + } + uprv_free(fLocaleZoneStrings); + } + + fZoneStrings = nullptr; + fLocaleZoneStrings = nullptr; + fZoneStringsRowCount = 0; + fZoneStringsColCount = 0; +} + +UBool +DateFormatSymbols::arrayCompare(const UnicodeString* array1, + const UnicodeString* array2, + int32_t count) +{ + if (array1 == array2) return true; + while (count>0) + { + --count; + if (array1[count] != array2[count]) return false; + } + return true; +} + +bool +DateFormatSymbols::operator==(const DateFormatSymbols& other) const +{ + // First do cheap comparisons + if (this == &other) { + return true; + } + if (fErasCount == other.fErasCount && + fEraNamesCount == other.fEraNamesCount && + fNarrowErasCount == other.fNarrowErasCount && + fMonthsCount == other.fMonthsCount && + fShortMonthsCount == other.fShortMonthsCount && + fNarrowMonthsCount == other.fNarrowMonthsCount && + fStandaloneMonthsCount == other.fStandaloneMonthsCount && + fStandaloneShortMonthsCount == other.fStandaloneShortMonthsCount && + fStandaloneNarrowMonthsCount == other.fStandaloneNarrowMonthsCount && + fWeekdaysCount == other.fWeekdaysCount && + fShortWeekdaysCount == other.fShortWeekdaysCount && + fShorterWeekdaysCount == other.fShorterWeekdaysCount && + fNarrowWeekdaysCount == other.fNarrowWeekdaysCount && + fStandaloneWeekdaysCount == other.fStandaloneWeekdaysCount && + fStandaloneShortWeekdaysCount == other.fStandaloneShortWeekdaysCount && + fStandaloneShorterWeekdaysCount == other.fStandaloneShorterWeekdaysCount && + fStandaloneNarrowWeekdaysCount == other.fStandaloneNarrowWeekdaysCount && + fAmPmsCount == other.fAmPmsCount && + fNarrowAmPmsCount == other.fNarrowAmPmsCount && + fQuartersCount == other.fQuartersCount && + fShortQuartersCount == other.fShortQuartersCount && + fNarrowQuartersCount == other.fNarrowQuartersCount && + fStandaloneQuartersCount == other.fStandaloneQuartersCount && + fStandaloneShortQuartersCount == other.fStandaloneShortQuartersCount && + fStandaloneNarrowQuartersCount == other.fStandaloneNarrowQuartersCount && + fLeapMonthPatternsCount == other.fLeapMonthPatternsCount && + fShortYearNamesCount == other.fShortYearNamesCount && + fShortZodiacNamesCount == other.fShortZodiacNamesCount && + fAbbreviatedDayPeriodsCount == other.fAbbreviatedDayPeriodsCount && + fWideDayPeriodsCount == other.fWideDayPeriodsCount && + fNarrowDayPeriodsCount == other.fNarrowDayPeriodsCount && + fStandaloneAbbreviatedDayPeriodsCount == other.fStandaloneAbbreviatedDayPeriodsCount && + fStandaloneWideDayPeriodsCount == other.fStandaloneWideDayPeriodsCount && + fStandaloneNarrowDayPeriodsCount == other.fStandaloneNarrowDayPeriodsCount && + (uprv_memcmp(fCapitalization, other.fCapitalization, sizeof(fCapitalization))==0)) + { + // Now compare the arrays themselves + if (arrayCompare(fEras, other.fEras, fErasCount) && + arrayCompare(fEraNames, other.fEraNames, fEraNamesCount) && + arrayCompare(fNarrowEras, other.fNarrowEras, fNarrowErasCount) && + arrayCompare(fMonths, other.fMonths, fMonthsCount) && + arrayCompare(fShortMonths, other.fShortMonths, fShortMonthsCount) && + arrayCompare(fNarrowMonths, other.fNarrowMonths, fNarrowMonthsCount) && + arrayCompare(fStandaloneMonths, other.fStandaloneMonths, fStandaloneMonthsCount) && + arrayCompare(fStandaloneShortMonths, other.fStandaloneShortMonths, fStandaloneShortMonthsCount) && + arrayCompare(fStandaloneNarrowMonths, other.fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount) && + arrayCompare(fWeekdays, other.fWeekdays, fWeekdaysCount) && + arrayCompare(fShortWeekdays, other.fShortWeekdays, fShortWeekdaysCount) && + arrayCompare(fShorterWeekdays, other.fShorterWeekdays, fShorterWeekdaysCount) && + arrayCompare(fNarrowWeekdays, other.fNarrowWeekdays, fNarrowWeekdaysCount) && + arrayCompare(fStandaloneWeekdays, other.fStandaloneWeekdays, fStandaloneWeekdaysCount) && + arrayCompare(fStandaloneShortWeekdays, other.fStandaloneShortWeekdays, fStandaloneShortWeekdaysCount) && + arrayCompare(fStandaloneShorterWeekdays, other.fStandaloneShorterWeekdays, fStandaloneShorterWeekdaysCount) && + arrayCompare(fStandaloneNarrowWeekdays, other.fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount) && + arrayCompare(fAmPms, other.fAmPms, fAmPmsCount) && + arrayCompare(fNarrowAmPms, other.fNarrowAmPms, fNarrowAmPmsCount) && + fTimeSeparator == other.fTimeSeparator && + arrayCompare(fQuarters, other.fQuarters, fQuartersCount) && + arrayCompare(fShortQuarters, other.fShortQuarters, fShortQuartersCount) && + arrayCompare(fNarrowQuarters, other.fNarrowQuarters, fNarrowQuartersCount) && + arrayCompare(fStandaloneQuarters, other.fStandaloneQuarters, fStandaloneQuartersCount) && + arrayCompare(fStandaloneShortQuarters, other.fStandaloneShortQuarters, fStandaloneShortQuartersCount) && + arrayCompare(fStandaloneNarrowQuarters, other.fStandaloneNarrowQuarters, fStandaloneNarrowQuartersCount) && + arrayCompare(fLeapMonthPatterns, other.fLeapMonthPatterns, fLeapMonthPatternsCount) && + arrayCompare(fShortYearNames, other.fShortYearNames, fShortYearNamesCount) && + arrayCompare(fShortZodiacNames, other.fShortZodiacNames, fShortZodiacNamesCount) && + arrayCompare(fAbbreviatedDayPeriods, other.fAbbreviatedDayPeriods, fAbbreviatedDayPeriodsCount) && + arrayCompare(fWideDayPeriods, other.fWideDayPeriods, fWideDayPeriodsCount) && + arrayCompare(fNarrowDayPeriods, other.fNarrowDayPeriods, fNarrowDayPeriodsCount) && + arrayCompare(fStandaloneAbbreviatedDayPeriods, other.fStandaloneAbbreviatedDayPeriods, + fStandaloneAbbreviatedDayPeriodsCount) && + arrayCompare(fStandaloneWideDayPeriods, other.fStandaloneWideDayPeriods, + fStandaloneWideDayPeriodsCount) && + arrayCompare(fStandaloneNarrowDayPeriods, other.fStandaloneNarrowDayPeriods, + fStandaloneWideDayPeriodsCount)) + { + // Compare the contents of fZoneStrings + if (fZoneStrings == nullptr && other.fZoneStrings == nullptr) { + if (fZSFLocale == other.fZSFLocale) { + return true; + } + } else if (fZoneStrings != nullptr && other.fZoneStrings != nullptr) { + if (fZoneStringsRowCount == other.fZoneStringsRowCount + && fZoneStringsColCount == other.fZoneStringsColCount) { + bool cmpres = true; + for (int32_t i = 0; (i < fZoneStringsRowCount) && cmpres; i++) { + cmpres = arrayCompare(fZoneStrings[i], other.fZoneStrings[i], fZoneStringsColCount); + } + return cmpres; + } + } + return false; + } + } + return false; +} + +//------------------------------------------------------ + +const UnicodeString* +DateFormatSymbols::getEras(int32_t &count) const +{ + count = fErasCount; + return fEras; +} + +const UnicodeString* +DateFormatSymbols::getEraNames(int32_t &count) const +{ + count = fEraNamesCount; + return fEraNames; +} + +const UnicodeString* +DateFormatSymbols::getNarrowEras(int32_t &count) const +{ + count = fNarrowErasCount; + return fNarrowEras; +} + +const UnicodeString* +DateFormatSymbols::getMonths(int32_t &count) const +{ + count = fMonthsCount; + return fMonths; +} + +const UnicodeString* +DateFormatSymbols::getShortMonths(int32_t &count) const +{ + count = fShortMonthsCount; + return fShortMonths; +} + +const UnicodeString* +DateFormatSymbols::getMonths(int32_t &count, DtContextType context, DtWidthType width ) const +{ + UnicodeString *returnValue = nullptr; + + switch (context) { + case FORMAT : + switch(width) { + case WIDE : + count = fMonthsCount; + returnValue = fMonths; + break; + case ABBREVIATED : + case SHORT : // no month data for this, defaults to ABBREVIATED + count = fShortMonthsCount; + returnValue = fShortMonths; + break; + case NARROW : + count = fNarrowMonthsCount; + returnValue = fNarrowMonths; + break; + case DT_WIDTH_COUNT : + break; + } + break; + case STANDALONE : + switch(width) { + case WIDE : + count = fStandaloneMonthsCount; + returnValue = fStandaloneMonths; + break; + case ABBREVIATED : + case SHORT : // no month data for this, defaults to ABBREVIATED + count = fStandaloneShortMonthsCount; + returnValue = fStandaloneShortMonths; + break; + case NARROW : + count = fStandaloneNarrowMonthsCount; + returnValue = fStandaloneNarrowMonths; + break; + case DT_WIDTH_COUNT : + break; + } + break; + case DT_CONTEXT_COUNT : + break; + } + return returnValue; +} + +const UnicodeString* +DateFormatSymbols::getWeekdays(int32_t &count) const +{ + count = fWeekdaysCount; + return fWeekdays; +} + +const UnicodeString* +DateFormatSymbols::getShortWeekdays(int32_t &count) const +{ + count = fShortWeekdaysCount; + return fShortWeekdays; +} + +const UnicodeString* +DateFormatSymbols::getWeekdays(int32_t &count, DtContextType context, DtWidthType width) const +{ + UnicodeString *returnValue = nullptr; + switch (context) { + case FORMAT : + switch(width) { + case WIDE : + count = fWeekdaysCount; + returnValue = fWeekdays; + break; + case ABBREVIATED : + count = fShortWeekdaysCount; + returnValue = fShortWeekdays; + break; + case SHORT : + count = fShorterWeekdaysCount; + returnValue = fShorterWeekdays; + break; + case NARROW : + count = fNarrowWeekdaysCount; + returnValue = fNarrowWeekdays; + break; + case DT_WIDTH_COUNT : + break; + } + break; + case STANDALONE : + switch(width) { + case WIDE : + count = fStandaloneWeekdaysCount; + returnValue = fStandaloneWeekdays; + break; + case ABBREVIATED : + count = fStandaloneShortWeekdaysCount; + returnValue = fStandaloneShortWeekdays; + break; + case SHORT : + count = fStandaloneShorterWeekdaysCount; + returnValue = fStandaloneShorterWeekdays; + break; + case NARROW : + count = fStandaloneNarrowWeekdaysCount; + returnValue = fStandaloneNarrowWeekdays; + break; + case DT_WIDTH_COUNT : + break; + } + break; + case DT_CONTEXT_COUNT : + break; + } + return returnValue; +} + +const UnicodeString* +DateFormatSymbols::getQuarters(int32_t &count, DtContextType context, DtWidthType width ) const +{ + UnicodeString *returnValue = nullptr; + + switch (context) { + case FORMAT : + switch(width) { + case WIDE : + count = fQuartersCount; + returnValue = fQuarters; + break; + case ABBREVIATED : + case SHORT : // no quarter data for this, defaults to ABBREVIATED + count = fShortQuartersCount; + returnValue = fShortQuarters; + break; + case NARROW : + count = fNarrowQuartersCount; + returnValue = fNarrowQuarters; + break; + case DT_WIDTH_COUNT : + break; + } + break; + case STANDALONE : + switch(width) { + case WIDE : + count = fStandaloneQuartersCount; + returnValue = fStandaloneQuarters; + break; + case ABBREVIATED : + case SHORT : // no quarter data for this, defaults to ABBREVIATED + count = fStandaloneShortQuartersCount; + returnValue = fStandaloneShortQuarters; + break; + case NARROW : + count = fStandaloneNarrowQuartersCount; + returnValue = fStandaloneNarrowQuarters; + break; + case DT_WIDTH_COUNT : + break; + } + break; + case DT_CONTEXT_COUNT : + break; + } + return returnValue; +} + +UnicodeString& +DateFormatSymbols::getTimeSeparatorString(UnicodeString& result) const +{ + // fastCopyFrom() - see assignArray comments + return result.fastCopyFrom(fTimeSeparator); +} + +const UnicodeString* +DateFormatSymbols::getAmPmStrings(int32_t &count) const +{ + count = fAmPmsCount; + return fAmPms; +} + +const UnicodeString* +DateFormatSymbols::getLeapMonthPatterns(int32_t &count) const +{ + count = fLeapMonthPatternsCount; + return fLeapMonthPatterns; +} + +const UnicodeString* +DateFormatSymbols::getYearNames(int32_t& count, + DtContextType /*ignored*/, DtWidthType /*ignored*/) const +{ + count = fShortYearNamesCount; + return fShortYearNames; +} + +void +DateFormatSymbols::setYearNames(const UnicodeString* yearNames, int32_t count, + DtContextType context, DtWidthType width) +{ + if (context == FORMAT && width == ABBREVIATED) { + if (fShortYearNames) { + delete[] fShortYearNames; + } + fShortYearNames = newUnicodeStringArray(count); + uprv_arrayCopy(yearNames, fShortYearNames, count); + fShortYearNamesCount = count; + } +} + +const UnicodeString* +DateFormatSymbols::getZodiacNames(int32_t& count, + DtContextType /*ignored*/, DtWidthType /*ignored*/) const +{ + count = fShortZodiacNamesCount; + return fShortZodiacNames; +} + +void +DateFormatSymbols::setZodiacNames(const UnicodeString* zodiacNames, int32_t count, + DtContextType context, DtWidthType width) +{ + if (context == FORMAT && width == ABBREVIATED) { + if (fShortZodiacNames) { + delete[] fShortZodiacNames; + } + fShortZodiacNames = newUnicodeStringArray(count); + uprv_arrayCopy(zodiacNames, fShortZodiacNames, count); + fShortZodiacNamesCount = count; + } +} + +//------------------------------------------------------ + +void +DateFormatSymbols::setEras(const UnicodeString* erasArray, int32_t count) +{ + // delete the old list if we own it + if (fEras) + delete[] fEras; + + // we always own the new list, which we create here (we duplicate rather + // than adopting the list passed in) + fEras = newUnicodeStringArray(count); + uprv_arrayCopy(erasArray,fEras, count); + fErasCount = count; +} + +void +DateFormatSymbols::setEraNames(const UnicodeString* eraNamesArray, int32_t count) +{ + // delete the old list if we own it + if (fEraNames) + delete[] fEraNames; + + // we always own the new list, which we create here (we duplicate rather + // than adopting the list passed in) + fEraNames = newUnicodeStringArray(count); + uprv_arrayCopy(eraNamesArray,fEraNames, count); + fEraNamesCount = count; +} + +void +DateFormatSymbols::setNarrowEras(const UnicodeString* narrowErasArray, int32_t count) +{ + // delete the old list if we own it + if (fNarrowEras) + delete[] fNarrowEras; + + // we always own the new list, which we create here (we duplicate rather + // than adopting the list passed in) + fNarrowEras = newUnicodeStringArray(count); + uprv_arrayCopy(narrowErasArray,fNarrowEras, count); + fNarrowErasCount = count; +} + +void +DateFormatSymbols::setMonths(const UnicodeString* monthsArray, int32_t count) +{ + // delete the old list if we own it + if (fMonths) + delete[] fMonths; + + // we always own the new list, which we create here (we duplicate rather + // than adopting the list passed in) + fMonths = newUnicodeStringArray(count); + uprv_arrayCopy( monthsArray,fMonths,count); + fMonthsCount = count; +} + +void +DateFormatSymbols::setShortMonths(const UnicodeString* shortMonthsArray, int32_t count) +{ + // delete the old list if we own it + if (fShortMonths) + delete[] fShortMonths; + + // we always own the new list, which we create here (we duplicate rather + // than adopting the list passed in) + fShortMonths = newUnicodeStringArray(count); + uprv_arrayCopy(shortMonthsArray,fShortMonths, count); + fShortMonthsCount = count; +} + +void +DateFormatSymbols::setMonths(const UnicodeString* monthsArray, int32_t count, DtContextType context, DtWidthType width) +{ + // delete the old list if we own it + // we always own the new list, which we create here (we duplicate rather + // than adopting the list passed in) + + switch (context) { + case FORMAT : + switch (width) { + case WIDE : + if (fMonths) + delete[] fMonths; + fMonths = newUnicodeStringArray(count); + uprv_arrayCopy( monthsArray,fMonths,count); + fMonthsCount = count; + break; + case ABBREVIATED : + if (fShortMonths) + delete[] fShortMonths; + fShortMonths = newUnicodeStringArray(count); + uprv_arrayCopy( monthsArray,fShortMonths,count); + fShortMonthsCount = count; + break; + case NARROW : + if (fNarrowMonths) + delete[] fNarrowMonths; + fNarrowMonths = newUnicodeStringArray(count); + uprv_arrayCopy( monthsArray,fNarrowMonths,count); + fNarrowMonthsCount = count; + break; + default : + break; + } + break; + case STANDALONE : + switch (width) { + case WIDE : + if (fStandaloneMonths) + delete[] fStandaloneMonths; + fStandaloneMonths = newUnicodeStringArray(count); + uprv_arrayCopy( monthsArray,fStandaloneMonths,count); + fStandaloneMonthsCount = count; + break; + case ABBREVIATED : + if (fStandaloneShortMonths) + delete[] fStandaloneShortMonths; + fStandaloneShortMonths = newUnicodeStringArray(count); + uprv_arrayCopy( monthsArray,fStandaloneShortMonths,count); + fStandaloneShortMonthsCount = count; + break; + case NARROW : + if (fStandaloneNarrowMonths) + delete[] fStandaloneNarrowMonths; + fStandaloneNarrowMonths = newUnicodeStringArray(count); + uprv_arrayCopy( monthsArray,fStandaloneNarrowMonths,count); + fStandaloneNarrowMonthsCount = count; + break; + default : + break; + } + break; + case DT_CONTEXT_COUNT : + break; + } +} + +void DateFormatSymbols::setWeekdays(const UnicodeString* weekdaysArray, int32_t count) +{ + // delete the old list if we own it + if (fWeekdays) + delete[] fWeekdays; + + // we always own the new list, which we create here (we duplicate rather + // than adopting the list passed in) + fWeekdays = newUnicodeStringArray(count); + uprv_arrayCopy(weekdaysArray,fWeekdays,count); + fWeekdaysCount = count; +} + +void +DateFormatSymbols::setShortWeekdays(const UnicodeString* shortWeekdaysArray, int32_t count) +{ + // delete the old list if we own it + if (fShortWeekdays) + delete[] fShortWeekdays; + + // we always own the new list, which we create here (we duplicate rather + // than adopting the list passed in) + fShortWeekdays = newUnicodeStringArray(count); + uprv_arrayCopy(shortWeekdaysArray, fShortWeekdays, count); + fShortWeekdaysCount = count; +} + +void +DateFormatSymbols::setWeekdays(const UnicodeString* weekdaysArray, int32_t count, DtContextType context, DtWidthType width) +{ + // delete the old list if we own it + // we always own the new list, which we create here (we duplicate rather + // than adopting the list passed in) + + switch (context) { + case FORMAT : + switch (width) { + case WIDE : + if (fWeekdays) + delete[] fWeekdays; + fWeekdays = newUnicodeStringArray(count); + uprv_arrayCopy(weekdaysArray, fWeekdays, count); + fWeekdaysCount = count; + break; + case ABBREVIATED : + if (fShortWeekdays) + delete[] fShortWeekdays; + fShortWeekdays = newUnicodeStringArray(count); + uprv_arrayCopy(weekdaysArray, fShortWeekdays, count); + fShortWeekdaysCount = count; + break; + case SHORT : + if (fShorterWeekdays) + delete[] fShorterWeekdays; + fShorterWeekdays = newUnicodeStringArray(count); + uprv_arrayCopy(weekdaysArray, fShorterWeekdays, count); + fShorterWeekdaysCount = count; + break; + case NARROW : + if (fNarrowWeekdays) + delete[] fNarrowWeekdays; + fNarrowWeekdays = newUnicodeStringArray(count); + uprv_arrayCopy(weekdaysArray, fNarrowWeekdays, count); + fNarrowWeekdaysCount = count; + break; + case DT_WIDTH_COUNT : + break; + } + break; + case STANDALONE : + switch (width) { + case WIDE : + if (fStandaloneWeekdays) + delete[] fStandaloneWeekdays; + fStandaloneWeekdays = newUnicodeStringArray(count); + uprv_arrayCopy(weekdaysArray, fStandaloneWeekdays, count); + fStandaloneWeekdaysCount = count; + break; + case ABBREVIATED : + if (fStandaloneShortWeekdays) + delete[] fStandaloneShortWeekdays; + fStandaloneShortWeekdays = newUnicodeStringArray(count); + uprv_arrayCopy(weekdaysArray, fStandaloneShortWeekdays, count); + fStandaloneShortWeekdaysCount = count; + break; + case SHORT : + if (fStandaloneShorterWeekdays) + delete[] fStandaloneShorterWeekdays; + fStandaloneShorterWeekdays = newUnicodeStringArray(count); + uprv_arrayCopy(weekdaysArray, fStandaloneShorterWeekdays, count); + fStandaloneShorterWeekdaysCount = count; + break; + case NARROW : + if (fStandaloneNarrowWeekdays) + delete[] fStandaloneNarrowWeekdays; + fStandaloneNarrowWeekdays = newUnicodeStringArray(count); + uprv_arrayCopy(weekdaysArray, fStandaloneNarrowWeekdays, count); + fStandaloneNarrowWeekdaysCount = count; + break; + case DT_WIDTH_COUNT : + break; + } + break; + case DT_CONTEXT_COUNT : + break; + } +} + +void +DateFormatSymbols::setQuarters(const UnicodeString* quartersArray, int32_t count, DtContextType context, DtWidthType width) +{ + // delete the old list if we own it + // we always own the new list, which we create here (we duplicate rather + // than adopting the list passed in) + + switch (context) { + case FORMAT : + switch (width) { + case WIDE : + if (fQuarters) + delete[] fQuarters; + fQuarters = newUnicodeStringArray(count); + uprv_arrayCopy( quartersArray,fQuarters,count); + fQuartersCount = count; + break; + case ABBREVIATED : + if (fShortQuarters) + delete[] fShortQuarters; + fShortQuarters = newUnicodeStringArray(count); + uprv_arrayCopy( quartersArray,fShortQuarters,count); + fShortQuartersCount = count; + break; + case NARROW : + if (fNarrowQuarters) + delete[] fNarrowQuarters; + fNarrowQuarters = newUnicodeStringArray(count); + uprv_arrayCopy( quartersArray,fNarrowQuarters,count); + fNarrowQuartersCount = count; + break; + default : + break; + } + break; + case STANDALONE : + switch (width) { + case WIDE : + if (fStandaloneQuarters) + delete[] fStandaloneQuarters; + fStandaloneQuarters = newUnicodeStringArray(count); + uprv_arrayCopy( quartersArray,fStandaloneQuarters,count); + fStandaloneQuartersCount = count; + break; + case ABBREVIATED : + if (fStandaloneShortQuarters) + delete[] fStandaloneShortQuarters; + fStandaloneShortQuarters = newUnicodeStringArray(count); + uprv_arrayCopy( quartersArray,fStandaloneShortQuarters,count); + fStandaloneShortQuartersCount = count; + break; + case NARROW : + if (fStandaloneNarrowQuarters) + delete[] fStandaloneNarrowQuarters; + fStandaloneNarrowQuarters = newUnicodeStringArray(count); + uprv_arrayCopy( quartersArray,fStandaloneNarrowQuarters,count); + fStandaloneNarrowQuartersCount = count; + break; + default : + break; + } + break; + case DT_CONTEXT_COUNT : + break; + } +} + +void +DateFormatSymbols::setAmPmStrings(const UnicodeString* amPmsArray, int32_t count) +{ + // delete the old list if we own it + if (fAmPms) delete[] fAmPms; + + // we always own the new list, which we create here (we duplicate rather + // than adopting the list passed in) + fAmPms = newUnicodeStringArray(count); + uprv_arrayCopy(amPmsArray,fAmPms,count); + fAmPmsCount = count; +} + +void +DateFormatSymbols::setTimeSeparatorString(const UnicodeString& newTimeSeparator) +{ + fTimeSeparator = newTimeSeparator; +} + +const UnicodeString** +DateFormatSymbols::getZoneStrings(int32_t& rowCount, int32_t& columnCount) const +{ + const UnicodeString **result = nullptr; + static UMutex LOCK; + + umtx_lock(&LOCK); + if (fZoneStrings == nullptr) { + if (fLocaleZoneStrings == nullptr) { + ((DateFormatSymbols*)this)->initZoneStringsArray(); + } + result = (const UnicodeString**)fLocaleZoneStrings; + } else { + result = (const UnicodeString**)fZoneStrings; + } + rowCount = fZoneStringsRowCount; + columnCount = fZoneStringsColCount; + umtx_unlock(&LOCK); + + return result; +} + +// For now, we include all zones +#define ZONE_SET UCAL_ZONE_TYPE_ANY + +// This code must be called within a synchronized block +void +DateFormatSymbols::initZoneStringsArray() { + if (fZoneStrings != nullptr || fLocaleZoneStrings != nullptr) { + return; + } + + UErrorCode status = U_ZERO_ERROR; + + StringEnumeration *tzids = nullptr; + UnicodeString ** zarray = nullptr; + TimeZoneNames *tzNames = nullptr; + int32_t rows = 0; + + static const UTimeZoneNameType TYPES[] = { + UTZNM_LONG_STANDARD, UTZNM_SHORT_STANDARD, + UTZNM_LONG_DAYLIGHT, UTZNM_SHORT_DAYLIGHT + }; + static const int32_t NUM_TYPES = 4; + + do { // dummy do-while + + tzids = TimeZone::createTimeZoneIDEnumeration(ZONE_SET, nullptr, nullptr, status); + rows = tzids->count(status); + if (U_FAILURE(status)) { + break; + } + + // Allocate array + int32_t size = rows * sizeof(UnicodeString*); + zarray = (UnicodeString**)uprv_malloc(size); + if (zarray == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + break; + } + uprv_memset(zarray, 0, size); + + tzNames = TimeZoneNames::createInstance(fZSFLocale, status); + tzNames->loadAllDisplayNames(status); + if (U_FAILURE(status)) { break; } + + const UnicodeString *tzid; + int32_t i = 0; + UDate now = Calendar::getNow(); + UnicodeString tzDispName; + + while ((tzid = tzids->snext(status)) != 0) { + if (U_FAILURE(status)) { + break; + } + + zarray[i] = new UnicodeString[5]; + if (zarray[i] == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + break; + } + + zarray[i][0].setTo(*tzid); + tzNames->getDisplayNames(*tzid, TYPES, NUM_TYPES, now, zarray[i]+1, status); + i++; + } + + } while (false); + + if (U_FAILURE(status)) { + if (zarray) { + for (int32_t i = 0; i < rows; i++) { + if (zarray[i]) { + delete[] zarray[i]; + } + } + uprv_free(zarray); + zarray = nullptr; + } + } + + if (tzNames) { + delete tzNames; + } + if (tzids) { + delete tzids; + } + + fLocaleZoneStrings = zarray; + fZoneStringsRowCount = rows; + fZoneStringsColCount = 1 + NUM_TYPES; +} + +void +DateFormatSymbols::setZoneStrings(const UnicodeString* const *strings, int32_t rowCount, int32_t columnCount) +{ + // since deleting a 2-d array is a pain in the butt, we offload that task to + // a separate function + disposeZoneStrings(); + // we always own the new list, which we create here (we duplicate rather + // than adopting the list passed in) + fZoneStringsRowCount = rowCount; + fZoneStringsColCount = columnCount; + createZoneStrings((const UnicodeString**)strings); +} + +//------------------------------------------------------ + +const char16_t * U_EXPORT2 +DateFormatSymbols::getPatternUChars() +{ + return gPatternChars; +} + +UDateFormatField U_EXPORT2 +DateFormatSymbols::getPatternCharIndex(char16_t c) { + const char16_t *p = u_strchr(gPatternChars, c); + if (p == nullptr) { + return UDAT_FIELD_COUNT; + } else { + return static_cast(p - gPatternChars); + } +} + +static const uint64_t kNumericFieldsAlways = + ((uint64_t)1 << UDAT_YEAR_FIELD) | // y + ((uint64_t)1 << UDAT_DATE_FIELD) | // d + ((uint64_t)1 << UDAT_HOUR_OF_DAY1_FIELD) | // k + ((uint64_t)1 << UDAT_HOUR_OF_DAY0_FIELD) | // H + ((uint64_t)1 << UDAT_MINUTE_FIELD) | // m + ((uint64_t)1 << UDAT_SECOND_FIELD) | // s + ((uint64_t)1 << UDAT_FRACTIONAL_SECOND_FIELD) | // S + ((uint64_t)1 << UDAT_DAY_OF_YEAR_FIELD) | // D + ((uint64_t)1 << UDAT_DAY_OF_WEEK_IN_MONTH_FIELD) | // F + ((uint64_t)1 << UDAT_WEEK_OF_YEAR_FIELD) | // w + ((uint64_t)1 << UDAT_WEEK_OF_MONTH_FIELD) | // W + ((uint64_t)1 << UDAT_HOUR1_FIELD) | // h + ((uint64_t)1 << UDAT_HOUR0_FIELD) | // K + ((uint64_t)1 << UDAT_YEAR_WOY_FIELD) | // Y + ((uint64_t)1 << UDAT_EXTENDED_YEAR_FIELD) | // u + ((uint64_t)1 << UDAT_JULIAN_DAY_FIELD) | // g + ((uint64_t)1 << UDAT_MILLISECONDS_IN_DAY_FIELD) | // A + ((uint64_t)1 << UDAT_RELATED_YEAR_FIELD); // r + +static const uint64_t kNumericFieldsForCount12 = + ((uint64_t)1 << UDAT_MONTH_FIELD) | // M or MM + ((uint64_t)1 << UDAT_DOW_LOCAL_FIELD) | // e or ee + ((uint64_t)1 << UDAT_STANDALONE_DAY_FIELD) | // c or cc + ((uint64_t)1 << UDAT_STANDALONE_MONTH_FIELD) | // L or LL + ((uint64_t)1 << UDAT_QUARTER_FIELD) | // Q or QQ + ((uint64_t)1 << UDAT_STANDALONE_QUARTER_FIELD); // q or qq + +UBool U_EXPORT2 +DateFormatSymbols::isNumericField(UDateFormatField f, int32_t count) { + if (f == UDAT_FIELD_COUNT) { + return false; + } + uint64_t flag = ((uint64_t)1 << f); + return ((kNumericFieldsAlways & flag) != 0 || ((kNumericFieldsForCount12 & flag) != 0 && count < 3)); +} + +UBool U_EXPORT2 +DateFormatSymbols::isNumericPatternChar(char16_t c, int32_t count) { + return isNumericField(getPatternCharIndex(c), count); +} + +//------------------------------------------------------ + +UnicodeString& +DateFormatSymbols::getLocalPatternChars(UnicodeString& result) const +{ + // fastCopyFrom() - see assignArray comments + return result.fastCopyFrom(fLocalPatternChars); +} + +//------------------------------------------------------ + +void +DateFormatSymbols::setLocalPatternChars(const UnicodeString& newLocalPatternChars) +{ + fLocalPatternChars = newLocalPatternChars; +} + +//------------------------------------------------------ + +namespace { + +// Constants declarations +static const char16_t kCalendarAliasPrefixUChar[] = { + SOLIDUS, CAP_L, CAP_O, CAP_C, CAP_A, CAP_L, CAP_E, SOLIDUS, + LOW_C, LOW_A, LOW_L, LOW_E, LOW_N, LOW_D, LOW_A, LOW_R, SOLIDUS +}; +static const char16_t kGregorianTagUChar[] = { + LOW_G, LOW_R, LOW_E, LOW_G, LOW_O, LOW_R, LOW_I, LOW_A, LOW_N +}; +static const char16_t kVariantTagUChar[] = { + PERCENT, LOW_V, LOW_A, LOW_R, LOW_I, LOW_A, LOW_N, LOW_T +}; +static const char16_t kLeapTagUChar[] = { + LOW_L, LOW_E, LOW_A, LOW_P +}; +static const char16_t kCyclicNameSetsTagUChar[] = { + LOW_C, LOW_Y, LOW_C, LOW_L, LOW_I, LOW_C, CAP_N, LOW_A, LOW_M, LOW_E, CAP_S, LOW_E, LOW_T, LOW_S +}; +static const char16_t kYearsTagUChar[] = { + SOLIDUS, LOW_Y, LOW_E, LOW_A, LOW_R, LOW_S +}; +static const char16_t kZodiacsUChar[] = { + SOLIDUS, LOW_Z, LOW_O, LOW_D, LOW_I, LOW_A, LOW_C, LOW_S +}; +static const char16_t kDayPartsTagUChar[] = { + SOLIDUS, LOW_D, LOW_A, LOW_Y, CAP_P, LOW_A, LOW_R, LOW_T, LOW_S +}; +static const char16_t kFormatTagUChar[] = { + SOLIDUS, LOW_F, LOW_O, LOW_R, LOW_M, LOW_A, LOW_T +}; +static const char16_t kAbbrTagUChar[] = { + SOLIDUS, LOW_A, LOW_B, LOW_B, LOW_R, LOW_E, LOW_V, LOW_I, LOW_A, LOW_T, LOW_E, LOW_D +}; + +// ResourceSink to enumerate all calendar resources +struct CalendarDataSink : public ResourceSink { + + // Enum which specifies the type of alias received, or no alias + enum AliasType { + SAME_CALENDAR, + DIFFERENT_CALENDAR, + GREGORIAN, + NONE + }; + + // Data structures to store resources from the current resource bundle + Hashtable arrays; + Hashtable arraySizes; + Hashtable maps; + /** + * Whenever there are aliases, the same object will be added twice to 'map'. + * To avoid double deletion, 'maps' won't take ownership of the objects. Instead, + * 'mapRefs' will own them and will delete them when CalendarDataSink is deleted. + */ + MemoryPool mapRefs; + + // Paths and the aliases they point to + UVector aliasPathPairs; + + // Current and next calendar resource table which should be loaded + UnicodeString currentCalendarType; + UnicodeString nextCalendarType; + + // Resources to visit when enumerating fallback calendars + LocalPointer resourcesToVisit; + + // Alias' relative path populated whenever an alias is read + UnicodeString aliasRelativePath; + + // Initializes CalendarDataSink with default values + CalendarDataSink(UErrorCode& status) + : arrays(false, status), arraySizes(false, status), maps(false, status), + mapRefs(), + aliasPathPairs(uprv_deleteUObject, uhash_compareUnicodeString, status), + currentCalendarType(), nextCalendarType(), + resourcesToVisit(nullptr), aliasRelativePath() { + if (U_FAILURE(status)) { return; } + } + virtual ~CalendarDataSink(); + + // Configure the CalendarSink to visit all the resources + void visitAllResources() { + resourcesToVisit.adoptInstead(nullptr); + } + + // Actions to be done before enumerating + void preEnumerate(const UnicodeString &calendarType) { + currentCalendarType = calendarType; + nextCalendarType.setToBogus(); + aliasPathPairs.removeAllElements(); + } + + virtual void put(const char *key, ResourceValue &value, UBool, UErrorCode &errorCode) override { + if (U_FAILURE(errorCode)) { return; } + U_ASSERT(!currentCalendarType.isEmpty()); + + // Stores the resources to visit on the next calendar. + LocalPointer resourcesToVisitNext(nullptr); + ResourceTable calendarData = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + + // Enumerate all resources for this calendar + for (int i = 0; calendarData.getKeyAndValue(i, key, value); i++) { + UnicodeString keyUString(key, -1, US_INV); + + // == Handle aliases == + AliasType aliasType = processAliasFromValue(keyUString, value, errorCode); + if (U_FAILURE(errorCode)) { return; } + if (aliasType == GREGORIAN) { + // Ignore aliases to the gregorian calendar, all of its resources will be loaded anyway. + continue; + + } else if (aliasType == DIFFERENT_CALENDAR) { + // Whenever an alias to the next calendar (except gregorian) is encountered, register the + // calendar type it's pointing to + if (resourcesToVisitNext.isNull()) { + resourcesToVisitNext + .adoptInsteadAndCheckErrorCode(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, errorCode), + errorCode); + if (U_FAILURE(errorCode)) { return; } + } + LocalPointer aliasRelativePathCopy(aliasRelativePath.clone(), errorCode); + resourcesToVisitNext->adoptElement(aliasRelativePathCopy.orphan(), errorCode); + if (U_FAILURE(errorCode)) { return; } + continue; + + } else if (aliasType == SAME_CALENDAR) { + // Register same-calendar alias + if (arrays.get(aliasRelativePath) == nullptr && maps.get(aliasRelativePath) == nullptr) { + LocalPointer aliasRelativePathCopy(aliasRelativePath.clone(), errorCode); + aliasPathPairs.adoptElement(aliasRelativePathCopy.orphan(), errorCode); + if (U_FAILURE(errorCode)) { return; } + LocalPointer keyUStringCopy(keyUString.clone(), errorCode); + aliasPathPairs.adoptElement(keyUStringCopy.orphan(), errorCode); + if (U_FAILURE(errorCode)) { return; } + } + continue; + } + + // Only visit the resources that were referenced by an alias on the previous calendar + // (AmPmMarkersAbbr is an exception). + if (!resourcesToVisit.isNull() && !resourcesToVisit->isEmpty() && !resourcesToVisit->contains(&keyUString) + && uprv_strcmp(key, gAmPmMarkersAbbrTag) != 0) { continue; } + + // == Handle data == + if (uprv_strcmp(key, gAmPmMarkersTag) == 0 + || uprv_strcmp(key, gAmPmMarkersAbbrTag) == 0 + || uprv_strcmp(key, gAmPmMarkersNarrowTag) == 0) { + if (arrays.get(keyUString) == nullptr) { + ResourceArray resourceArray = value.getArray(errorCode); + int32_t arraySize = resourceArray.getSize(); + LocalArray stringArray(new UnicodeString[arraySize], errorCode); + value.getStringArray(stringArray.getAlias(), arraySize, errorCode); + arrays.put(keyUString, stringArray.orphan(), errorCode); + arraySizes.puti(keyUString, arraySize, errorCode); + if (U_FAILURE(errorCode)) { return; } + } + } else if (uprv_strcmp(key, gErasTag) == 0 + || uprv_strcmp(key, gDayNamesTag) == 0 + || uprv_strcmp(key, gMonthNamesTag) == 0 + || uprv_strcmp(key, gQuartersTag) == 0 + || uprv_strcmp(key, gDayPeriodTag) == 0 + || uprv_strcmp(key, gMonthPatternsTag) == 0 + || uprv_strcmp(key, gCyclicNameSetsTag) == 0) { + processResource(keyUString, key, value, errorCode); + } + } + + // Apply same-calendar aliases + UBool modified; + do { + modified = false; + for (int32_t i = 0; i < aliasPathPairs.size();) { + UBool mod = false; + UnicodeString *alias = (UnicodeString*)aliasPathPairs[i]; + UnicodeString *aliasArray; + Hashtable *aliasMap; + if ((aliasArray = (UnicodeString*)arrays.get(*alias)) != nullptr) { + UnicodeString *path = (UnicodeString*)aliasPathPairs[i + 1]; + if (arrays.get(*path) == nullptr) { + // Clone the array + int32_t aliasArraySize = arraySizes.geti(*alias); + LocalArray aliasArrayCopy(new UnicodeString[aliasArraySize], errorCode); + if (U_FAILURE(errorCode)) { return; } + uprv_arrayCopy(aliasArray, aliasArrayCopy.getAlias(), aliasArraySize); + // Put the array on the 'arrays' map + arrays.put(*path, aliasArrayCopy.orphan(), errorCode); + arraySizes.puti(*path, aliasArraySize, errorCode); + } + if (U_FAILURE(errorCode)) { return; } + mod = true; + } else if ((aliasMap = (Hashtable*)maps.get(*alias)) != nullptr) { + UnicodeString *path = (UnicodeString*)aliasPathPairs[i + 1]; + if (maps.get(*path) == nullptr) { + maps.put(*path, aliasMap, errorCode); + } + if (U_FAILURE(errorCode)) { return; } + mod = true; + } + if (mod) { + aliasPathPairs.removeElementAt(i + 1); + aliasPathPairs.removeElementAt(i); + modified = true; + } else { + i += 2; + } + } + } while (modified && !aliasPathPairs.isEmpty()); + + // Set the resources to visit on the next calendar + if (!resourcesToVisitNext.isNull()) { + resourcesToVisit = std::move(resourcesToVisitNext); + } + } + + // Process the nested resource bundle tables + void processResource(UnicodeString &path, const char *key, ResourceValue &value, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) return; + + ResourceTable table = value.getTable(errorCode); + if (U_FAILURE(errorCode)) return; + Hashtable* stringMap = nullptr; + + // Iterate over all the elements of the table and add them to the map + for (int i = 0; table.getKeyAndValue(i, key, value); i++) { + UnicodeString keyUString(key, -1, US_INV); + + // Ignore '%variant' keys + if (keyUString.endsWith(kVariantTagUChar, UPRV_LENGTHOF(kVariantTagUChar))) { + continue; + } + + // == Handle String elements == + if (value.getType() == URES_STRING) { + // We are on a leaf, store the map elements into the stringMap + if (i == 0) { + // mapRefs will keep ownership of 'stringMap': + stringMap = mapRefs.create(false, errorCode); + if (stringMap == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + maps.put(path, stringMap, errorCode); + if (U_FAILURE(errorCode)) { return; } + stringMap->setValueDeleter(uprv_deleteUObject); + } + U_ASSERT(stringMap != nullptr); + int32_t valueStringSize; + const char16_t *valueString = value.getString(valueStringSize, errorCode); + if (U_FAILURE(errorCode)) { return; } + LocalPointer valueUString(new UnicodeString(true, valueString, valueStringSize), errorCode); + stringMap->put(keyUString, valueUString.orphan(), errorCode); + if (U_FAILURE(errorCode)) { return; } + continue; + } + U_ASSERT(stringMap == nullptr); + + // Store the current path's length and append the current key to the path. + int32_t pathLength = path.length(); + path.append(SOLIDUS).append(keyUString); + + // In cyclicNameSets ignore everything but years/format/abbreviated + // and zodiacs/format/abbreviated + if (path.startsWith(kCyclicNameSetsTagUChar, UPRV_LENGTHOF(kCyclicNameSetsTagUChar))) { + UBool skip = true; + int32_t startIndex = UPRV_LENGTHOF(kCyclicNameSetsTagUChar); + int32_t length = 0; + if (startIndex == path.length() + || path.compare(startIndex, (length = UPRV_LENGTHOF(kZodiacsUChar)), kZodiacsUChar, 0, UPRV_LENGTHOF(kZodiacsUChar)) == 0 + || path.compare(startIndex, (length = UPRV_LENGTHOF(kYearsTagUChar)), kYearsTagUChar, 0, UPRV_LENGTHOF(kYearsTagUChar)) == 0 + || path.compare(startIndex, (length = UPRV_LENGTHOF(kDayPartsTagUChar)), kDayPartsTagUChar, 0, UPRV_LENGTHOF(kDayPartsTagUChar)) == 0) { + startIndex += length; + length = 0; + if (startIndex == path.length() + || path.compare(startIndex, (length = UPRV_LENGTHOF(kFormatTagUChar)), kFormatTagUChar, 0, UPRV_LENGTHOF(kFormatTagUChar)) == 0) { + startIndex += length; + length = 0; + if (startIndex == path.length() + || path.compare(startIndex, (length = UPRV_LENGTHOF(kAbbrTagUChar)), kAbbrTagUChar, 0, UPRV_LENGTHOF(kAbbrTagUChar)) == 0) { + skip = false; + } + } + } + if (skip) { + // Drop the latest key on the path and continue + path.retainBetween(0, pathLength); + continue; + } + } + + // == Handle aliases == + if (arrays.get(path) != nullptr || maps.get(path) != nullptr) { + // Drop the latest key on the path and continue + path.retainBetween(0, pathLength); + continue; + } + + AliasType aliasType = processAliasFromValue(path, value, errorCode); + if (U_FAILURE(errorCode)) { return; } + if (aliasType == SAME_CALENDAR) { + // Store the alias path and the current path on aliasPathPairs + LocalPointer aliasRelativePathCopy(aliasRelativePath.clone(), errorCode); + aliasPathPairs.adoptElement(aliasRelativePathCopy.orphan(), errorCode); + if (U_FAILURE(errorCode)) { return; } + LocalPointer pathCopy(path.clone(), errorCode); + aliasPathPairs.adoptElement(pathCopy.orphan(), errorCode); + if (U_FAILURE(errorCode)) { return; } + + // Drop the latest key on the path and continue + path.retainBetween(0, pathLength); + continue; + } + U_ASSERT(aliasType == NONE); + + // == Handle data == + if (value.getType() == URES_ARRAY) { + // We are on a leaf, store the array + ResourceArray rDataArray = value.getArray(errorCode); + int32_t dataArraySize = rDataArray.getSize(); + LocalArray dataArray(new UnicodeString[dataArraySize], errorCode); + value.getStringArray(dataArray.getAlias(), dataArraySize, errorCode); + arrays.put(path, dataArray.orphan(), errorCode); + arraySizes.puti(path, dataArraySize, errorCode); + if (U_FAILURE(errorCode)) { return; } + } else if (value.getType() == URES_TABLE) { + // We are not on a leaf, recursively process the subtable. + processResource(path, key, value, errorCode); + if (U_FAILURE(errorCode)) { return; } + } + + // Drop the latest key on the path + path.retainBetween(0, pathLength); + } + } + + // Populates an AliasIdentifier with the alias information contained on the UResource.Value. + AliasType processAliasFromValue(UnicodeString ¤tRelativePath, ResourceValue &value, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return NONE; } + + if (value.getType() == URES_ALIAS) { + int32_t aliasPathSize; + const char16_t* aliasPathUChar = value.getAliasString(aliasPathSize, errorCode); + if (U_FAILURE(errorCode)) { return NONE; } + UnicodeString aliasPath(aliasPathUChar, aliasPathSize); + const int32_t aliasPrefixLength = UPRV_LENGTHOF(kCalendarAliasPrefixUChar); + if (aliasPath.startsWith(kCalendarAliasPrefixUChar, aliasPrefixLength) + && aliasPath.length() > aliasPrefixLength) { + int32_t typeLimit = aliasPath.indexOf(SOLIDUS, aliasPrefixLength); + if (typeLimit > aliasPrefixLength) { + const UnicodeString aliasCalendarType = + aliasPath.tempSubStringBetween(aliasPrefixLength, typeLimit); + aliasRelativePath.setTo(aliasPath, typeLimit + 1, aliasPath.length()); + + if (currentCalendarType == aliasCalendarType + && currentRelativePath != aliasRelativePath) { + // If we have an alias to the same calendar, the path to the resource must be different + return SAME_CALENDAR; + + } else if (currentCalendarType != aliasCalendarType + && currentRelativePath == aliasRelativePath) { + // If we have an alias to a different calendar, the path to the resource must be the same + if (aliasCalendarType.compare(kGregorianTagUChar, UPRV_LENGTHOF(kGregorianTagUChar)) == 0) { + return GREGORIAN; + } else if (nextCalendarType.isBogus()) { + nextCalendarType = aliasCalendarType; + return DIFFERENT_CALENDAR; + } else if (nextCalendarType == aliasCalendarType) { + return DIFFERENT_CALENDAR; + } + } + } + } + errorCode = U_INTERNAL_PROGRAM_ERROR; + return NONE; + } + return NONE; + } + + // Deleter function to be used by 'arrays' + static void U_CALLCONV deleteUnicodeStringArray(void *uArray) { + delete[] static_cast(uArray); + } +}; +// Virtual destructors have to be defined out of line +CalendarDataSink::~CalendarDataSink() { + arrays.setValueDeleter(deleteUnicodeStringArray); +} +} + +//------------------------------------------------------ + +static void +initField(UnicodeString **field, int32_t& length, const char16_t *data, LastResortSize numStr, LastResortSize strLen, UErrorCode &status) { + if (U_SUCCESS(status)) { + length = numStr; + *field = newUnicodeStringArray((size_t)numStr); + if (*field) { + for(int32_t i = 0; isetTo(true, data+(i*((int32_t)strLen)), -1); + } + } + else { + length = 0; + status = U_MEMORY_ALLOCATION_ERROR; + } + } +} + +static void +initField(UnicodeString **field, int32_t& length, CalendarDataSink &sink, CharString &key, UErrorCode &status) { + if (U_SUCCESS(status)) { + UnicodeString keyUString(key.data(), -1, US_INV); + UnicodeString* array = static_cast(sink.arrays.get(keyUString)); + + if (array != nullptr) { + length = sink.arraySizes.geti(keyUString); + *field = array; + // DateFormatSymbols takes ownership of the array: + sink.arrays.remove(keyUString); + } else { + length = 0; + status = U_MISSING_RESOURCE_ERROR; + } + } +} + +static void +initField(UnicodeString **field, int32_t& length, CalendarDataSink &sink, CharString &key, int32_t arrayOffset, UErrorCode &status) { + if (U_SUCCESS(status)) { + UnicodeString keyUString(key.data(), -1, US_INV); + UnicodeString* array = static_cast(sink.arrays.get(keyUString)); + + if (array != nullptr) { + int32_t arrayLength = sink.arraySizes.geti(keyUString); + length = arrayLength + arrayOffset; + *field = new UnicodeString[length]; + if (*field == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_arrayCopy(array, 0, *field, arrayOffset, arrayLength); + } else { + length = 0; + status = U_MISSING_RESOURCE_ERROR; + } + } +} + +static void +initLeapMonthPattern(UnicodeString *field, int32_t index, CalendarDataSink &sink, CharString &path, UErrorCode &status) { + field[index].remove(); + if (U_SUCCESS(status)) { + UnicodeString pathUString(path.data(), -1, US_INV); + Hashtable *leapMonthTable = static_cast(sink.maps.get(pathUString)); + if (leapMonthTable != nullptr) { + UnicodeString leapLabel(false, kLeapTagUChar, UPRV_LENGTHOF(kLeapTagUChar)); + UnicodeString *leapMonthPattern = static_cast(leapMonthTable->get(leapLabel)); + if (leapMonthPattern != nullptr) { + field[index].fastCopyFrom(*leapMonthPattern); + } else { + field[index].setToBogus(); + } + return; + } + status = U_MISSING_RESOURCE_ERROR; + } +} + +static CharString +&buildResourcePath(CharString &path, const char* segment1, UErrorCode &errorCode) { + return path.clear().append(segment1, -1, errorCode); +} + +static CharString +&buildResourcePath(CharString &path, const char* segment1, const char* segment2, + UErrorCode &errorCode) { + return buildResourcePath(path, segment1, errorCode).append('/', errorCode) + .append(segment2, -1, errorCode); +} + +static CharString +&buildResourcePath(CharString &path, const char* segment1, const char* segment2, + const char* segment3, UErrorCode &errorCode) { + return buildResourcePath(path, segment1, segment2, errorCode).append('/', errorCode) + .append(segment3, -1, errorCode); +} + +static CharString +&buildResourcePath(CharString &path, const char* segment1, const char* segment2, + const char* segment3, const char* segment4, UErrorCode &errorCode) { + return buildResourcePath(path, segment1, segment2, segment3, errorCode).append('/', errorCode) + .append(segment4, -1, errorCode); +} + +typedef struct { + const char * usageTypeName; + DateFormatSymbols::ECapitalizationContextUsageType usageTypeEnumValue; +} ContextUsageTypeNameToEnumValue; + +static const ContextUsageTypeNameToEnumValue contextUsageTypeMap[] = { + // Entries must be sorted by usageTypeName; entry with nullptr name terminates list. + { "day-format-except-narrow", DateFormatSymbols::kCapContextUsageDayFormat }, + { "day-narrow", DateFormatSymbols::kCapContextUsageDayNarrow }, + { "day-standalone-except-narrow", DateFormatSymbols::kCapContextUsageDayStandalone }, + { "era-abbr", DateFormatSymbols::kCapContextUsageEraAbbrev }, + { "era-name", DateFormatSymbols::kCapContextUsageEraWide }, + { "era-narrow", DateFormatSymbols::kCapContextUsageEraNarrow }, + { "metazone-long", DateFormatSymbols::kCapContextUsageMetazoneLong }, + { "metazone-short", DateFormatSymbols::kCapContextUsageMetazoneShort }, + { "month-format-except-narrow", DateFormatSymbols::kCapContextUsageMonthFormat }, + { "month-narrow", DateFormatSymbols::kCapContextUsageMonthNarrow }, + { "month-standalone-except-narrow", DateFormatSymbols::kCapContextUsageMonthStandalone }, + { "zone-long", DateFormatSymbols::kCapContextUsageZoneLong }, + { "zone-short", DateFormatSymbols::kCapContextUsageZoneShort }, + { nullptr, (DateFormatSymbols::ECapitalizationContextUsageType)0 }, +}; + +// Resource keys to look up localized strings for day periods. +// The first one must be midnight and the second must be noon, so that their indices coincide +// with the am/pm field. Formatting and parsing code for day periods relies on this coincidence. +static const char *dayPeriodKeys[] = {"midnight", "noon", + "morning1", "afternoon1", "evening1", "night1", + "morning2", "afternoon2", "evening2", "night2"}; + +UnicodeString* loadDayPeriodStrings(CalendarDataSink &sink, CharString &path, + int32_t &stringCount, UErrorCode &status) { + if (U_FAILURE(status)) { return nullptr; } + + UnicodeString pathUString(path.data(), -1, US_INV); + Hashtable* map = static_cast(sink.maps.get(pathUString)); + + stringCount = UPRV_LENGTHOF(dayPeriodKeys); + UnicodeString *strings = new UnicodeString[stringCount]; + if (strings == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + if (map != nullptr) { + for (int32_t i = 0; i < stringCount; ++i) { + UnicodeString dayPeriodKey(dayPeriodKeys[i], -1, US_INV); + UnicodeString *dayPeriod = static_cast(map->get(dayPeriodKey)); + if (dayPeriod != nullptr) { + strings[i].fastCopyFrom(*dayPeriod); + } else { + strings[i].setToBogus(); + } + } + } else { + for (int32_t i = 0; i < stringCount; i++) { + strings[i].setToBogus(); + } + } + return strings; +} + + +void +DateFormatSymbols::initializeData(const Locale& locale, const char *type, UErrorCode& status, UBool useLastResortData) +{ + int32_t len = 0; + /* In case something goes wrong, initialize all of the data to nullptr. */ + fEras = nullptr; + fErasCount = 0; + fEraNames = nullptr; + fEraNamesCount = 0; + fNarrowEras = nullptr; + fNarrowErasCount = 0; + fMonths = nullptr; + fMonthsCount=0; + fShortMonths = nullptr; + fShortMonthsCount=0; + fNarrowMonths = nullptr; + fNarrowMonthsCount=0; + fStandaloneMonths = nullptr; + fStandaloneMonthsCount=0; + fStandaloneShortMonths = nullptr; + fStandaloneShortMonthsCount=0; + fStandaloneNarrowMonths = nullptr; + fStandaloneNarrowMonthsCount=0; + fWeekdays = nullptr; + fWeekdaysCount=0; + fShortWeekdays = nullptr; + fShortWeekdaysCount=0; + fShorterWeekdays = nullptr; + fShorterWeekdaysCount=0; + fNarrowWeekdays = nullptr; + fNarrowWeekdaysCount=0; + fStandaloneWeekdays = nullptr; + fStandaloneWeekdaysCount=0; + fStandaloneShortWeekdays = nullptr; + fStandaloneShortWeekdaysCount=0; + fStandaloneShorterWeekdays = nullptr; + fStandaloneShorterWeekdaysCount=0; + fStandaloneNarrowWeekdays = nullptr; + fStandaloneNarrowWeekdaysCount=0; + fAmPms = nullptr; + fAmPmsCount=0; + fNarrowAmPms = nullptr; + fNarrowAmPmsCount=0; + fTimeSeparator.setToBogus(); + fQuarters = nullptr; + fQuartersCount = 0; + fShortQuarters = nullptr; + fShortQuartersCount = 0; + fNarrowQuarters = nullptr; + fNarrowQuartersCount = 0; + fStandaloneQuarters = nullptr; + fStandaloneQuartersCount = 0; + fStandaloneShortQuarters = nullptr; + fStandaloneShortQuartersCount = 0; + fStandaloneNarrowQuarters = nullptr; + fStandaloneNarrowQuartersCount = 0; + fLeapMonthPatterns = nullptr; + fLeapMonthPatternsCount = 0; + fShortYearNames = nullptr; + fShortYearNamesCount = 0; + fShortZodiacNames = nullptr; + fShortZodiacNamesCount = 0; + fZoneStringsRowCount = 0; + fZoneStringsColCount = 0; + fZoneStrings = nullptr; + fLocaleZoneStrings = nullptr; + fAbbreviatedDayPeriods = nullptr; + fAbbreviatedDayPeriodsCount = 0; + fWideDayPeriods = nullptr; + fWideDayPeriodsCount = 0; + fNarrowDayPeriods = nullptr; + fNarrowDayPeriodsCount = 0; + fStandaloneAbbreviatedDayPeriods = nullptr; + fStandaloneAbbreviatedDayPeriodsCount = 0; + fStandaloneWideDayPeriods = nullptr; + fStandaloneWideDayPeriodsCount = 0; + fStandaloneNarrowDayPeriods = nullptr; + fStandaloneNarrowDayPeriodsCount = 0; + uprv_memset(fCapitalization, 0, sizeof(fCapitalization)); + + // We need to preserve the requested locale for + // lazy ZoneStringFormat instantiation. ZoneStringFormat + // is region sensitive, thus, bundle locale bundle's locale + // is not sufficient. + fZSFLocale = locale; + + if (U_FAILURE(status)) return; + + // Create a CalendarDataSink to process this data and the resource bundles + CalendarDataSink calendarSink(status); + UResourceBundle *rb = ures_open(nullptr, locale.getBaseName(), &status); + UResourceBundle *cb = ures_getByKey(rb, gCalendarTag, nullptr, &status); + + if (U_FAILURE(status)) return; + + // Iterate over the resource bundle data following the fallbacks through different calendar types + UnicodeString calendarType((type != nullptr && *type != '\0')? type : gGregorianTag, -1, US_INV); + while (!calendarType.isBogus()) { + CharString calendarTypeBuffer; + calendarTypeBuffer.appendInvariantChars(calendarType, status); + if (U_FAILURE(status)) { return; } + const char *calendarTypeCArray = calendarTypeBuffer.data(); + + // Enumerate this calendar type. If the calendar is not found fallback to gregorian + UErrorCode oldStatus = status; + UResourceBundle *ctb = ures_getByKeyWithFallback(cb, calendarTypeCArray, nullptr, &status); + if (status == U_MISSING_RESOURCE_ERROR) { + ures_close(ctb); + if (uprv_strcmp(calendarTypeCArray, gGregorianTag) != 0) { + calendarType.setTo(false, kGregorianTagUChar, UPRV_LENGTHOF(kGregorianTagUChar)); + calendarSink.visitAllResources(); + status = oldStatus; + continue; + } + return; + } + + calendarSink.preEnumerate(calendarType); + ures_getAllItemsWithFallback(ctb, "", calendarSink, status); + ures_close(ctb); + if (U_FAILURE(status)) break; + + // Stop loading when gregorian was loaded + if (uprv_strcmp(calendarTypeCArray, gGregorianTag) == 0) { + break; + } + + // Get the next calendar type to process from the sink + calendarType = calendarSink.nextCalendarType; + + // Gregorian is always the last fallback + if (calendarType.isBogus()) { + calendarType.setTo(false, kGregorianTagUChar, UPRV_LENGTHOF(kGregorianTagUChar)); + calendarSink.visitAllResources(); + } + } + + // CharString object to build paths + CharString path; + + // Load Leap Month Patterns + UErrorCode tempStatus = status; + fLeapMonthPatterns = newUnicodeStringArray(kMonthPatternsCount); + if (fLeapMonthPatterns) { + initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternFormatWide, calendarSink, + buildResourcePath(path, gMonthPatternsTag, gNamesFormatTag, gNamesWideTag, tempStatus), tempStatus); + initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternFormatAbbrev, calendarSink, + buildResourcePath(path, gMonthPatternsTag, gNamesFormatTag, gNamesAbbrTag, tempStatus), tempStatus); + initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternFormatNarrow, calendarSink, + buildResourcePath(path, gMonthPatternsTag, gNamesFormatTag, gNamesNarrowTag, tempStatus), tempStatus); + initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternStandaloneWide, calendarSink, + buildResourcePath(path, gMonthPatternsTag, gNamesStandaloneTag, gNamesWideTag, tempStatus), tempStatus); + initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternStandaloneAbbrev, calendarSink, + buildResourcePath(path, gMonthPatternsTag, gNamesStandaloneTag, gNamesAbbrTag, tempStatus), tempStatus); + initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternStandaloneNarrow, calendarSink, + buildResourcePath(path, gMonthPatternsTag, gNamesStandaloneTag, gNamesNarrowTag, tempStatus), tempStatus); + initLeapMonthPattern(fLeapMonthPatterns, kLeapMonthPatternNumeric, calendarSink, + buildResourcePath(path, gMonthPatternsTag, gNamesNumericTag, gNamesAllTag, tempStatus), tempStatus); + if (U_SUCCESS(tempStatus)) { + // Hack to fix bad C inheritance for dangi monthPatterns (OK in J); this should be handled by aliases in root, but isn't. + // The ordering of the following statements is important. + if (fLeapMonthPatterns[kLeapMonthPatternFormatAbbrev].isEmpty()) { + fLeapMonthPatterns[kLeapMonthPatternFormatAbbrev].setTo(fLeapMonthPatterns[kLeapMonthPatternFormatWide]); + } + if (fLeapMonthPatterns[kLeapMonthPatternFormatNarrow].isEmpty()) { + fLeapMonthPatterns[kLeapMonthPatternFormatNarrow].setTo(fLeapMonthPatterns[kLeapMonthPatternStandaloneNarrow]); + } + if (fLeapMonthPatterns[kLeapMonthPatternStandaloneWide].isEmpty()) { + fLeapMonthPatterns[kLeapMonthPatternStandaloneWide].setTo(fLeapMonthPatterns[kLeapMonthPatternFormatWide]); + } + if (fLeapMonthPatterns[kLeapMonthPatternStandaloneAbbrev].isEmpty()) { + fLeapMonthPatterns[kLeapMonthPatternStandaloneAbbrev].setTo(fLeapMonthPatterns[kLeapMonthPatternFormatAbbrev]); + } + // end of hack + fLeapMonthPatternsCount = kMonthPatternsCount; + } else { + delete[] fLeapMonthPatterns; + fLeapMonthPatterns = nullptr; + } + } + + // Load cyclic names sets + tempStatus = status; + initField(&fShortYearNames, fShortYearNamesCount, calendarSink, + buildResourcePath(path, gCyclicNameSetsTag, gNameSetYearsTag, gNamesFormatTag, gNamesAbbrTag, tempStatus), tempStatus); + initField(&fShortZodiacNames, fShortZodiacNamesCount, calendarSink, + buildResourcePath(path, gCyclicNameSetsTag, gNameSetZodiacsTag, gNamesFormatTag, gNamesAbbrTag, tempStatus), tempStatus); + + // Load context transforms and capitalization + tempStatus = U_ZERO_ERROR; + UResourceBundle *localeBundle = ures_open(nullptr, locale.getName(), &tempStatus); + if (U_SUCCESS(tempStatus)) { + UResourceBundle *contextTransforms = ures_getByKeyWithFallback(localeBundle, gContextTransformsTag, nullptr, &tempStatus); + if (U_SUCCESS(tempStatus)) { + UResourceBundle *contextTransformUsage; + while ( (contextTransformUsage = ures_getNextResource(contextTransforms, nullptr, &tempStatus)) != nullptr ) { + const int32_t * intVector = ures_getIntVector(contextTransformUsage, &len, &status); + if (U_SUCCESS(tempStatus) && intVector != nullptr && len >= 2) { + const char* usageType = ures_getKey(contextTransformUsage); + if (usageType != nullptr) { + const ContextUsageTypeNameToEnumValue * typeMapPtr = contextUsageTypeMap; + int32_t compResult = 0; + // linear search; list is short and we cannot be sure that bsearch is available + while ( typeMapPtr->usageTypeName != nullptr && (compResult = uprv_strcmp(usageType, typeMapPtr->usageTypeName)) > 0 ) { + ++typeMapPtr; + } + if (typeMapPtr->usageTypeName != nullptr && compResult == 0) { + fCapitalization[typeMapPtr->usageTypeEnumValue][0] = static_cast(intVector[0]); + fCapitalization[typeMapPtr->usageTypeEnumValue][1] = static_cast(intVector[1]); + } + } + } + tempStatus = U_ZERO_ERROR; + ures_close(contextTransformUsage); + } + ures_close(contextTransforms); + } + + tempStatus = U_ZERO_ERROR; + const LocalPointer numberingSystem( + NumberingSystem::createInstance(locale, tempStatus), tempStatus); + if (U_SUCCESS(tempStatus)) { + // These functions all fail gracefully if passed nullptr pointers and + // do nothing unless U_SUCCESS(tempStatus), so it's only necessary + // to check for errors once after all calls are made. + const LocalUResourceBundlePointer numberElementsData(ures_getByKeyWithFallback( + localeBundle, gNumberElementsTag, nullptr, &tempStatus)); + const LocalUResourceBundlePointer nsNameData(ures_getByKeyWithFallback( + numberElementsData.getAlias(), numberingSystem->getName(), nullptr, &tempStatus)); + const LocalUResourceBundlePointer symbolsData(ures_getByKeyWithFallback( + nsNameData.getAlias(), gSymbolsTag, nullptr, &tempStatus)); + fTimeSeparator = ures_getUnicodeStringByKey( + symbolsData.getAlias(), gTimeSeparatorTag, &tempStatus); + if (U_FAILURE(tempStatus)) { + fTimeSeparator.setToBogus(); + } + } + + ures_close(localeBundle); + } + + if (fTimeSeparator.isBogus()) { + fTimeSeparator.setTo(DateFormatSymbols::DEFAULT_TIME_SEPARATOR); + } + + // Load day periods + fAbbreviatedDayPeriods = loadDayPeriodStrings(calendarSink, + buildResourcePath(path, gDayPeriodTag, gNamesFormatTag, gNamesAbbrTag, status), + fAbbreviatedDayPeriodsCount, status); + + fWideDayPeriods = loadDayPeriodStrings(calendarSink, + buildResourcePath(path, gDayPeriodTag, gNamesFormatTag, gNamesWideTag, status), + fWideDayPeriodsCount, status); + fNarrowDayPeriods = loadDayPeriodStrings(calendarSink, + buildResourcePath(path, gDayPeriodTag, gNamesFormatTag, gNamesNarrowTag, status), + fNarrowDayPeriodsCount, status); + + fStandaloneAbbreviatedDayPeriods = loadDayPeriodStrings(calendarSink, + buildResourcePath(path, gDayPeriodTag, gNamesStandaloneTag, gNamesAbbrTag, status), + fStandaloneAbbreviatedDayPeriodsCount, status); + + fStandaloneWideDayPeriods = loadDayPeriodStrings(calendarSink, + buildResourcePath(path, gDayPeriodTag, gNamesStandaloneTag, gNamesWideTag, status), + fStandaloneWideDayPeriodsCount, status); + fStandaloneNarrowDayPeriods = loadDayPeriodStrings(calendarSink, + buildResourcePath(path, gDayPeriodTag, gNamesStandaloneTag, gNamesNarrowTag, status), + fStandaloneNarrowDayPeriodsCount, status); + + // Fill in for missing/bogus items (dayPeriods are a map so single items might be missing) + if (U_SUCCESS(status)) { + for (int32_t dpidx = 0; dpidx < fAbbreviatedDayPeriodsCount; ++dpidx) { + if (dpidx < fWideDayPeriodsCount && fWideDayPeriods != nullptr && fWideDayPeriods[dpidx].isBogus()) { + fWideDayPeriods[dpidx].fastCopyFrom(fAbbreviatedDayPeriods[dpidx]); + } + if (dpidx < fNarrowDayPeriodsCount && fNarrowDayPeriods != nullptr && fNarrowDayPeriods[dpidx].isBogus()) { + fNarrowDayPeriods[dpidx].fastCopyFrom(fAbbreviatedDayPeriods[dpidx]); + } + if (dpidx < fStandaloneAbbreviatedDayPeriodsCount && fStandaloneAbbreviatedDayPeriods != nullptr && fStandaloneAbbreviatedDayPeriods[dpidx].isBogus()) { + fStandaloneAbbreviatedDayPeriods[dpidx].fastCopyFrom(fAbbreviatedDayPeriods[dpidx]); + } + if (dpidx < fStandaloneWideDayPeriodsCount && fStandaloneWideDayPeriods != nullptr && fStandaloneWideDayPeriods[dpidx].isBogus()) { + fStandaloneWideDayPeriods[dpidx].fastCopyFrom(fStandaloneAbbreviatedDayPeriods[dpidx]); + } + if (dpidx < fStandaloneNarrowDayPeriodsCount && fStandaloneNarrowDayPeriods != nullptr && fStandaloneNarrowDayPeriods[dpidx].isBogus()) { + fStandaloneNarrowDayPeriods[dpidx].fastCopyFrom(fStandaloneAbbreviatedDayPeriods[dpidx]); + } + } + } + + U_LOCALE_BASED(locBased, *this); + // if we make it to here, the resource data is cool, and we can get everything out + // of it that we need except for the time-zone and localized-pattern data, which + // are stored in a separate file + locBased.setLocaleIDs(ures_getLocaleByType(cb, ULOC_VALID_LOCALE, &status), + ures_getLocaleByType(cb, ULOC_ACTUAL_LOCALE, &status)); + + // Load eras + initField(&fEras, fErasCount, calendarSink, buildResourcePath(path, gErasTag, gNamesAbbrTag, status), status); + UErrorCode oldStatus = status; + initField(&fEraNames, fEraNamesCount, calendarSink, buildResourcePath(path, gErasTag, gNamesWideTag, status), status); + if (status == U_MISSING_RESOURCE_ERROR) { // Workaround because eras/wide was omitted from CLDR 1.3 + status = oldStatus; + assignArray(fEraNames, fEraNamesCount, fEras, fErasCount); + } + // current ICU4J falls back to abbreviated if narrow eras are missing, so we will too + oldStatus = status; + initField(&fNarrowEras, fNarrowErasCount, calendarSink, buildResourcePath(path, gErasTag, gNamesNarrowTag, status), status); + if (status == U_MISSING_RESOURCE_ERROR) { // Workaround because eras/wide was omitted from CLDR 1.3 + status = oldStatus; + assignArray(fNarrowEras, fNarrowErasCount, fEras, fErasCount); + } + + // Load month names + initField(&fMonths, fMonthsCount, calendarSink, + buildResourcePath(path, gMonthNamesTag, gNamesFormatTag, gNamesWideTag, status), status); + initField(&fShortMonths, fShortMonthsCount, calendarSink, + buildResourcePath(path, gMonthNamesTag, gNamesFormatTag, gNamesAbbrTag, status), status); + initField(&fStandaloneMonths, fStandaloneMonthsCount, calendarSink, + buildResourcePath(path, gMonthNamesTag, gNamesStandaloneTag, gNamesWideTag, status), status); + if (status == U_MISSING_RESOURCE_ERROR) { /* If standalone/wide not available, use format/wide */ + status = U_ZERO_ERROR; + assignArray(fStandaloneMonths, fStandaloneMonthsCount, fMonths, fMonthsCount); + } + initField(&fStandaloneShortMonths, fStandaloneShortMonthsCount, calendarSink, + buildResourcePath(path, gMonthNamesTag, gNamesStandaloneTag, gNamesAbbrTag, status), status); + if (status == U_MISSING_RESOURCE_ERROR) { /* If standalone/abbreviated not available, use format/abbreviated */ + status = U_ZERO_ERROR; + assignArray(fStandaloneShortMonths, fStandaloneShortMonthsCount, fShortMonths, fShortMonthsCount); + } + + UErrorCode narrowMonthsEC = status; + UErrorCode standaloneNarrowMonthsEC = status; + initField(&fNarrowMonths, fNarrowMonthsCount, calendarSink, + buildResourcePath(path, gMonthNamesTag, gNamesFormatTag, gNamesNarrowTag, narrowMonthsEC), narrowMonthsEC); + initField(&fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount, calendarSink, + buildResourcePath(path, gMonthNamesTag, gNamesStandaloneTag, gNamesNarrowTag, narrowMonthsEC), standaloneNarrowMonthsEC); + if (narrowMonthsEC == U_MISSING_RESOURCE_ERROR && standaloneNarrowMonthsEC != U_MISSING_RESOURCE_ERROR) { + // If format/narrow not available, use standalone/narrow + assignArray(fNarrowMonths, fNarrowMonthsCount, fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount); + } else if (narrowMonthsEC != U_MISSING_RESOURCE_ERROR && standaloneNarrowMonthsEC == U_MISSING_RESOURCE_ERROR) { + // If standalone/narrow not available, use format/narrow + assignArray(fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount, fNarrowMonths, fNarrowMonthsCount); + } else if (narrowMonthsEC == U_MISSING_RESOURCE_ERROR && standaloneNarrowMonthsEC == U_MISSING_RESOURCE_ERROR) { + // If neither is available, use format/abbreviated + assignArray(fNarrowMonths, fNarrowMonthsCount, fShortMonths, fShortMonthsCount); + assignArray(fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount, fShortMonths, fShortMonthsCount); + } + + // Load AM/PM markers; if wide or narrow not available, use short + UErrorCode ampmStatus = U_ZERO_ERROR; + initField(&fAmPms, fAmPmsCount, calendarSink, + buildResourcePath(path, gAmPmMarkersTag, ampmStatus), ampmStatus); + if (U_FAILURE(ampmStatus)) { + initField(&fAmPms, fAmPmsCount, calendarSink, + buildResourcePath(path, gAmPmMarkersAbbrTag, status), status); + } + ampmStatus = U_ZERO_ERROR; + initField(&fNarrowAmPms, fNarrowAmPmsCount, calendarSink, + buildResourcePath(path, gAmPmMarkersNarrowTag, ampmStatus), ampmStatus); + if (U_FAILURE(ampmStatus)) { + initField(&fNarrowAmPms, fNarrowAmPmsCount, calendarSink, + buildResourcePath(path, gAmPmMarkersAbbrTag, status), status); + } + if(status == U_MISSING_RESOURCE_ERROR) { + status = U_ZERO_ERROR; + assignArray(fNarrowAmPms, fNarrowAmPmsCount, fAmPms, fAmPmsCount); + } + + // Load quarters + initField(&fQuarters, fQuartersCount, calendarSink, + buildResourcePath(path, gQuartersTag, gNamesFormatTag, gNamesWideTag, status), status); + initField(&fShortQuarters, fShortQuartersCount, calendarSink, + buildResourcePath(path, gQuartersTag, gNamesFormatTag, gNamesAbbrTag, status), status); + if(status == U_MISSING_RESOURCE_ERROR) { + status = U_ZERO_ERROR; + assignArray(fShortQuarters, fShortQuartersCount, fQuarters, fQuartersCount); + } + + initField(&fStandaloneQuarters, fStandaloneQuartersCount, calendarSink, + buildResourcePath(path, gQuartersTag, gNamesStandaloneTag, gNamesWideTag, status), status); + if(status == U_MISSING_RESOURCE_ERROR) { + status = U_ZERO_ERROR; + assignArray(fStandaloneQuarters, fStandaloneQuartersCount, fQuarters, fQuartersCount); + } + initField(&fStandaloneShortQuarters, fStandaloneShortQuartersCount, calendarSink, + buildResourcePath(path, gQuartersTag, gNamesStandaloneTag, gNamesAbbrTag, status), status); + if(status == U_MISSING_RESOURCE_ERROR) { + status = U_ZERO_ERROR; + assignArray(fStandaloneShortQuarters, fStandaloneShortQuartersCount, fShortQuarters, fShortQuartersCount); + } + + // unlike the fields above, narrow format quarters fall back on narrow standalone quarters + initField(&fStandaloneNarrowQuarters, fStandaloneNarrowQuartersCount, calendarSink, + buildResourcePath(path, gQuartersTag, gNamesStandaloneTag, gNamesNarrowTag, status), status); + initField(&fNarrowQuarters, fNarrowQuartersCount, calendarSink, + buildResourcePath(path, gQuartersTag, gNamesFormatTag, gNamesNarrowTag, status), status); + if(status == U_MISSING_RESOURCE_ERROR) { + status = U_ZERO_ERROR; + assignArray(fNarrowQuarters, fNarrowQuartersCount, fStandaloneNarrowQuarters, fStandaloneNarrowQuartersCount); + } + + // ICU 3.8 or later version no longer uses localized date-time pattern characters by default (ticket#5597) + /* + // fastCopyFrom()/setTo() - see assignArray comments + resStr = ures_getStringByKey(fResourceBundle, gLocalPatternCharsTag, &len, &status); + fLocalPatternChars.setTo(true, resStr, len); + // If the locale data does not include new pattern chars, use the defaults + // TODO: Consider making this an error, since this may add conflicting characters. + if (len < PATTERN_CHARS_LEN) { + fLocalPatternChars.append(UnicodeString(true, &gPatternChars[len], PATTERN_CHARS_LEN-len)); + } + */ + fLocalPatternChars.setTo(true, gPatternChars, PATTERN_CHARS_LEN); + + // Format wide weekdays -> fWeekdays + // {sfb} fixed to handle 1-based weekdays + initField(&fWeekdays, fWeekdaysCount, calendarSink, + buildResourcePath(path, gDayNamesTag, gNamesFormatTag, gNamesWideTag, status), 1, status); + + // Format abbreviated weekdays -> fShortWeekdays + initField(&fShortWeekdays, fShortWeekdaysCount, calendarSink, + buildResourcePath(path, gDayNamesTag, gNamesFormatTag, gNamesAbbrTag, status), 1, status); + + // Format short weekdays -> fShorterWeekdays (fall back to abbreviated) + initField(&fShorterWeekdays, fShorterWeekdaysCount, calendarSink, + buildResourcePath(path, gDayNamesTag, gNamesFormatTag, gNamesShortTag, status), 1, status); + if (status == U_MISSING_RESOURCE_ERROR) { + status = U_ZERO_ERROR; + assignArray(fShorterWeekdays, fShorterWeekdaysCount, fShortWeekdays, fShortWeekdaysCount); + } + + // Stand-alone wide weekdays -> fStandaloneWeekdays + initField(&fStandaloneWeekdays, fStandaloneWeekdaysCount, calendarSink, + buildResourcePath(path, gDayNamesTag, gNamesStandaloneTag, gNamesWideTag, status), 1, status); + if (status == U_MISSING_RESOURCE_ERROR) { /* If standalone/wide is not available, use format/wide */ + status = U_ZERO_ERROR; + assignArray(fStandaloneWeekdays, fStandaloneWeekdaysCount, fWeekdays, fWeekdaysCount); + } + + // Stand-alone abbreviated weekdays -> fStandaloneShortWeekdays + initField(&fStandaloneShortWeekdays, fStandaloneShortWeekdaysCount, calendarSink, + buildResourcePath(path, gDayNamesTag, gNamesStandaloneTag, gNamesAbbrTag, status), 1, status); + if (status == U_MISSING_RESOURCE_ERROR) { /* If standalone/abbreviated is not available, use format/abbreviated */ + status = U_ZERO_ERROR; + assignArray(fStandaloneShortWeekdays, fStandaloneShortWeekdaysCount, fShortWeekdays, fShortWeekdaysCount); + } + + // Stand-alone short weekdays -> fStandaloneShorterWeekdays (fall back to format abbreviated) + initField(&fStandaloneShorterWeekdays, fStandaloneShorterWeekdaysCount, calendarSink, + buildResourcePath(path, gDayNamesTag, gNamesStandaloneTag, gNamesShortTag, status), 1, status); + if (status == U_MISSING_RESOURCE_ERROR) { /* If standalone/short is not available, use format/short */ + status = U_ZERO_ERROR; + assignArray(fStandaloneShorterWeekdays, fStandaloneShorterWeekdaysCount, fShorterWeekdays, fShorterWeekdaysCount); + } + + // Format narrow weekdays -> fNarrowWeekdays + UErrorCode narrowWeeksEC = status; + initField(&fNarrowWeekdays, fNarrowWeekdaysCount, calendarSink, + buildResourcePath(path, gDayNamesTag, gNamesFormatTag, gNamesNarrowTag, status), 1, narrowWeeksEC); + // Stand-alone narrow weekdays -> fStandaloneNarrowWeekdays + UErrorCode standaloneNarrowWeeksEC = status; + initField(&fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount, calendarSink, + buildResourcePath(path, gDayNamesTag, gNamesStandaloneTag, gNamesNarrowTag, status), 1, standaloneNarrowWeeksEC); + + if (narrowWeeksEC == U_MISSING_RESOURCE_ERROR && standaloneNarrowWeeksEC != U_MISSING_RESOURCE_ERROR) { + // If format/narrow not available, use standalone/narrow + assignArray(fNarrowWeekdays, fNarrowWeekdaysCount, fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount); + } else if (narrowWeeksEC != U_MISSING_RESOURCE_ERROR && standaloneNarrowWeeksEC == U_MISSING_RESOURCE_ERROR) { + // If standalone/narrow not available, use format/narrow + assignArray(fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount, fNarrowWeekdays, fNarrowWeekdaysCount); + } else if (narrowWeeksEC == U_MISSING_RESOURCE_ERROR && standaloneNarrowWeeksEC == U_MISSING_RESOURCE_ERROR ) { + // If neither is available, use format/abbreviated + assignArray(fNarrowWeekdays, fNarrowWeekdaysCount, fShortWeekdays, fShortWeekdaysCount); + assignArray(fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount, fShortWeekdays, fShortWeekdaysCount); + } + + // Last resort fallback in case previous data wasn't loaded + if (U_FAILURE(status)) + { + if (useLastResortData) + { + // Handle the case in which there is no resource data present. + // We don't have to generate usable patterns in this situation; + // we just need to produce something that will be semi-intelligible + // in most locales. + + status = U_USING_FALLBACK_WARNING; + //TODO(fabalbon): make sure we are storing las resort data for all fields in here. + initField(&fEras, fErasCount, (const char16_t *)gLastResortEras, kEraNum, kEraLen, status); + initField(&fEraNames, fEraNamesCount, (const char16_t *)gLastResortEras, kEraNum, kEraLen, status); + initField(&fNarrowEras, fNarrowErasCount, (const char16_t *)gLastResortEras, kEraNum, kEraLen, status); + initField(&fMonths, fMonthsCount, (const char16_t *)gLastResortMonthNames, kMonthNum, kMonthLen, status); + initField(&fShortMonths, fShortMonthsCount, (const char16_t *)gLastResortMonthNames, kMonthNum, kMonthLen, status); + initField(&fNarrowMonths, fNarrowMonthsCount, (const char16_t *)gLastResortMonthNames, kMonthNum, kMonthLen, status); + initField(&fStandaloneMonths, fStandaloneMonthsCount, (const char16_t *)gLastResortMonthNames, kMonthNum, kMonthLen, status); + initField(&fStandaloneShortMonths, fStandaloneShortMonthsCount, (const char16_t *)gLastResortMonthNames, kMonthNum, kMonthLen, status); + initField(&fStandaloneNarrowMonths, fStandaloneNarrowMonthsCount, (const char16_t *)gLastResortMonthNames, kMonthNum, kMonthLen, status); + initField(&fWeekdays, fWeekdaysCount, (const char16_t *)gLastResortDayNames, kDayNum, kDayLen, status); + initField(&fShortWeekdays, fShortWeekdaysCount, (const char16_t *)gLastResortDayNames, kDayNum, kDayLen, status); + initField(&fShorterWeekdays, fShorterWeekdaysCount, (const char16_t *)gLastResortDayNames, kDayNum, kDayLen, status); + initField(&fNarrowWeekdays, fNarrowWeekdaysCount, (const char16_t *)gLastResortDayNames, kDayNum, kDayLen, status); + initField(&fStandaloneWeekdays, fStandaloneWeekdaysCount, (const char16_t *)gLastResortDayNames, kDayNum, kDayLen, status); + initField(&fStandaloneShortWeekdays, fStandaloneShortWeekdaysCount, (const char16_t *)gLastResortDayNames, kDayNum, kDayLen, status); + initField(&fStandaloneShorterWeekdays, fStandaloneShorterWeekdaysCount, (const char16_t *)gLastResortDayNames, kDayNum, kDayLen, status); + initField(&fStandaloneNarrowWeekdays, fStandaloneNarrowWeekdaysCount, (const char16_t *)gLastResortDayNames, kDayNum, kDayLen, status); + initField(&fAmPms, fAmPmsCount, (const char16_t *)gLastResortAmPmMarkers, kAmPmNum, kAmPmLen, status); + initField(&fNarrowAmPms, fNarrowAmPmsCount, (const char16_t *)gLastResortAmPmMarkers, kAmPmNum, kAmPmLen, status); + initField(&fQuarters, fQuartersCount, (const char16_t *)gLastResortQuarters, kQuarterNum, kQuarterLen, status); + initField(&fShortQuarters, fShortQuartersCount, (const char16_t *)gLastResortQuarters, kQuarterNum, kQuarterLen, status); + initField(&fNarrowQuarters, fNarrowQuartersCount, (const char16_t *)gLastResortQuarters, kQuarterNum, kQuarterLen, status); + initField(&fStandaloneQuarters, fStandaloneQuartersCount, (const char16_t *)gLastResortQuarters, kQuarterNum, kQuarterLen, status); + initField(&fStandaloneShortQuarters, fStandaloneShortQuartersCount, (const char16_t *)gLastResortQuarters, kQuarterNum, kQuarterLen, status); + initField(&fStandaloneNarrowQuarters, fStandaloneNarrowQuartersCount, (const char16_t *)gLastResortQuarters, kQuarterNum, kQuarterLen, status); + fLocalPatternChars.setTo(true, gPatternChars, PATTERN_CHARS_LEN); + } + } + + // Close resources + ures_close(cb); + ures_close(rb); +} + +Locale +DateFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const { + U_LOCALE_BASED(locBased, *this); + return locBased.getLocale(type, status); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/dtitv_impl.h b/deps/icu-small/source/i18n/dtitv_impl.h index 6fc16bb3e08d3c..fa7535b589746a 100644 --- a/deps/icu-small/source/i18n/dtitv_impl.h +++ b/deps/icu-small/source/i18n/dtitv_impl.h @@ -1,99 +1,99 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2007-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File DTITV_IMPL.H -* -******************************************************************************* -*/ - - -#ifndef DTITV_IMPL_H__ -#define DTITV_IMPL_H__ - -/** - * \file - * \brief C++ API: Defines macros for interval format implementation - */ - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/unistr.h" - - -#define QUOTE ((UChar)0x0027) -#define LOW_LINE ((UChar)0x005F) -#define COLON ((UChar)0x003A) -#define LEFT_CURLY_BRACKET ((UChar)0x007B) -#define RIGHT_CURLY_BRACKET ((UChar)0x007D) -#define SPACE ((UChar)0x0020) -#define EN_DASH ((UChar)0x2013) -#define SOLIDUS ((UChar)0x002F) - -#define DIGIT_ZERO ((UChar)0x0030) -#define DIGIT_ONE ((UChar)0x0031) - -#define LOW_A ((UChar)0x0061) -#define LOW_B ((UChar)0x0062) -#define LOW_C ((UChar)0x0063) -#define LOW_D ((UChar)0x0064) -#define LOW_E ((UChar)0x0065) -#define LOW_F ((UChar)0x0066) -#define LOW_G ((UChar)0x0067) -#define LOW_H ((UChar)0x0068) -#define LOW_I ((UChar)0x0069) -#define LOW_J ((UChar)0x006a) -#define LOW_K ((UChar)0x006B) -#define LOW_L ((UChar)0x006C) -#define LOW_M ((UChar)0x006D) -#define LOW_N ((UChar)0x006E) -#define LOW_O ((UChar)0x006F) -#define LOW_P ((UChar)0x0070) -#define LOW_Q ((UChar)0x0071) -#define LOW_R ((UChar)0x0072) -#define LOW_S ((UChar)0x0073) -#define LOW_T ((UChar)0x0074) -#define LOW_U ((UChar)0x0075) -#define LOW_V ((UChar)0x0076) -#define LOW_W ((UChar)0x0077) -#define LOW_Y ((UChar)0x0079) -#define LOW_Z ((UChar)0x007A) - -#define CAP_A ((UChar)0x0041) -#define CAP_B ((UChar)0x0042) -#define CAP_C ((UChar)0x0043) -#define CAP_D ((UChar)0x0044) -#define CAP_E ((UChar)0x0045) -#define CAP_F ((UChar)0x0046) -#define CAP_G ((UChar)0x0047) -#define CAP_J ((UChar)0x004A) -#define CAP_H ((UChar)0x0048) -#define CAP_K ((UChar)0x004B) -#define CAP_L ((UChar)0x004C) -#define CAP_M ((UChar)0x004D) -#define CAP_O ((UChar)0x004F) -#define CAP_Q ((UChar)0x0051) -#define CAP_S ((UChar)0x0053) -#define CAP_T ((UChar)0x0054) -#define CAP_U ((UChar)0x0055) -#define CAP_V ((UChar)0x0056) -#define CAP_W ((UChar)0x0057) -#define CAP_Y ((UChar)0x0059) -#define CAP_Z ((UChar)0x005A) - -//#define MINIMUM_SUPPORTED_CALENDAR_FIELD UCAL_MINUTE - -#define MAX_E_COUNT 5 -#define MAX_M_COUNT 5 -//#define MAX_INTERVAL_INDEX 4 -#define MAX_POSITIVE_INT 56632 - - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -#endif -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2007-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTITV_IMPL.H +* +******************************************************************************* +*/ + + +#ifndef DTITV_IMPL_H__ +#define DTITV_IMPL_H__ + +/** + * \file + * \brief C++ API: Defines macros for interval format implementation + */ + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/unistr.h" + + +#define QUOTE ((char16_t)0x0027) +#define LOW_LINE ((char16_t)0x005F) +#define COLON ((char16_t)0x003A) +#define LEFT_CURLY_BRACKET ((char16_t)0x007B) +#define RIGHT_CURLY_BRACKET ((char16_t)0x007D) +#define SPACE ((char16_t)0x0020) +#define EN_DASH ((char16_t)0x2013) +#define SOLIDUS ((char16_t)0x002F) + +#define DIGIT_ZERO ((char16_t)0x0030) +#define DIGIT_ONE ((char16_t)0x0031) + +#define LOW_A ((char16_t)0x0061) +#define LOW_B ((char16_t)0x0062) +#define LOW_C ((char16_t)0x0063) +#define LOW_D ((char16_t)0x0064) +#define LOW_E ((char16_t)0x0065) +#define LOW_F ((char16_t)0x0066) +#define LOW_G ((char16_t)0x0067) +#define LOW_H ((char16_t)0x0068) +#define LOW_I ((char16_t)0x0069) +#define LOW_J ((char16_t)0x006a) +#define LOW_K ((char16_t)0x006B) +#define LOW_L ((char16_t)0x006C) +#define LOW_M ((char16_t)0x006D) +#define LOW_N ((char16_t)0x006E) +#define LOW_O ((char16_t)0x006F) +#define LOW_P ((char16_t)0x0070) +#define LOW_Q ((char16_t)0x0071) +#define LOW_R ((char16_t)0x0072) +#define LOW_S ((char16_t)0x0073) +#define LOW_T ((char16_t)0x0074) +#define LOW_U ((char16_t)0x0075) +#define LOW_V ((char16_t)0x0076) +#define LOW_W ((char16_t)0x0077) +#define LOW_Y ((char16_t)0x0079) +#define LOW_Z ((char16_t)0x007A) + +#define CAP_A ((char16_t)0x0041) +#define CAP_B ((char16_t)0x0042) +#define CAP_C ((char16_t)0x0043) +#define CAP_D ((char16_t)0x0044) +#define CAP_E ((char16_t)0x0045) +#define CAP_F ((char16_t)0x0046) +#define CAP_G ((char16_t)0x0047) +#define CAP_J ((char16_t)0x004A) +#define CAP_H ((char16_t)0x0048) +#define CAP_K ((char16_t)0x004B) +#define CAP_L ((char16_t)0x004C) +#define CAP_M ((char16_t)0x004D) +#define CAP_O ((char16_t)0x004F) +#define CAP_Q ((char16_t)0x0051) +#define CAP_S ((char16_t)0x0053) +#define CAP_T ((char16_t)0x0054) +#define CAP_U ((char16_t)0x0055) +#define CAP_V ((char16_t)0x0056) +#define CAP_W ((char16_t)0x0057) +#define CAP_Y ((char16_t)0x0059) +#define CAP_Z ((char16_t)0x005A) + +//#define MINIMUM_SUPPORTED_CALENDAR_FIELD UCAL_MINUTE + +#define MAX_E_COUNT 5 +#define MAX_M_COUNT 5 +//#define MAX_INTERVAL_INDEX 4 +#define MAX_POSITIVE_INT 56632 + + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif +//eof diff --git a/deps/icu-small/source/i18n/dtitvfmt.cpp b/deps/icu-small/source/i18n/dtitvfmt.cpp index eb5be7846e2d7a..2ed86eaa67fb68 100644 --- a/deps/icu-small/source/i18n/dtitvfmt.cpp +++ b/deps/icu-small/source/i18n/dtitvfmt.cpp @@ -1,1909 +1,1909 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/******************************************************************************* -* Copyright (C) 2008-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File DTITVFMT.CPP -* -******************************************************************************* -*/ - -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/dtitvfmt.h" - -#if !UCONFIG_NO_FORMATTING - -//TODO: put in compilation -//#define DTITVFMT_DEBUG 1 - -#include "unicode/calendar.h" -#include "unicode/dtptngen.h" -#include "unicode/dtitvinf.h" -#include "unicode/simpleformatter.h" -#include "unicode/udisplaycontext.h" -#include "cmemory.h" -#include "cstring.h" -#include "dtitv_impl.h" -#include "mutex.h" -#include "uresimp.h" -#include "formattedval_impl.h" - -#ifdef DTITVFMT_DEBUG -#include -#endif - -U_NAMESPACE_BEGIN - - - -#ifdef DTITVFMT_DEBUG -#define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; } -#endif - - -static const UChar gDateFormatSkeleton[][11] = { -//yMMMMEEEEd -{LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, CAP_E, CAP_E, CAP_E, CAP_E, LOW_D, 0}, -//yMMMMd -{LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, LOW_D, 0}, -//yMMMd -{LOW_Y, CAP_M, CAP_M, CAP_M, LOW_D, 0}, -//yMd -{LOW_Y, CAP_M, LOW_D, 0} }; - - -static const char gCalendarTag[] = "calendar"; -static const char gGregorianTag[] = "gregorian"; -static const char gDateTimePatternsTag[] = "DateTimePatterns"; - - -// latestFirst: -static const UChar gLaterFirstPrefix[] = {LOW_L, LOW_A, LOW_T, LOW_E, LOW_S,LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON}; - -// earliestFirst: -static const UChar gEarlierFirstPrefix[] = {LOW_E, LOW_A, LOW_R, LOW_L, LOW_I, LOW_E, LOW_S, LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON}; - - -class FormattedDateIntervalData : public FormattedValueFieldPositionIteratorImpl { -public: - FormattedDateIntervalData(UErrorCode& status) : FormattedValueFieldPositionIteratorImpl(5, status) {} - virtual ~FormattedDateIntervalData(); -}; - -FormattedDateIntervalData::~FormattedDateIntervalData() = default; - -UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedDateInterval) - - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat) - -// Mutex, protects access to fDateFormat, fFromCalendar and fToCalendar. -// Needed because these data members are modified by const methods of DateIntervalFormat. - -static UMutex gFormatterMutex; - -DateIntervalFormat* U_EXPORT2 -DateIntervalFormat::createInstance(const UnicodeString& skeleton, - UErrorCode& status) { - return createInstance(skeleton, Locale::getDefault(), status); -} - - -DateIntervalFormat* U_EXPORT2 -DateIntervalFormat::createInstance(const UnicodeString& skeleton, - const Locale& locale, - UErrorCode& status) { -#ifdef DTITVFMT_DEBUG - char result[1000]; - char result_1[1000]; - char mesg[2000]; - skeleton.extract(0, skeleton.length(), result, "UTF-8"); - UnicodeString pat; - ((SimpleDateFormat*)dtfmt)->toPattern(pat); - pat.extract(0, pat.length(), result_1, "UTF-8"); - sprintf(mesg, "skeleton: %s; pattern: %s\n", result, result_1); - PRINTMESG(mesg) -#endif - - DateIntervalInfo* dtitvinf = new DateIntervalInfo(locale, status); - if (dtitvinf == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - return create(locale, dtitvinf, &skeleton, status); -} - - - -DateIntervalFormat* U_EXPORT2 -DateIntervalFormat::createInstance(const UnicodeString& skeleton, - const DateIntervalInfo& dtitvinf, - UErrorCode& status) { - return createInstance(skeleton, Locale::getDefault(), dtitvinf, status); -} - - -DateIntervalFormat* U_EXPORT2 -DateIntervalFormat::createInstance(const UnicodeString& skeleton, - const Locale& locale, - const DateIntervalInfo& dtitvinf, - UErrorCode& status) { - DateIntervalInfo* ptn = dtitvinf.clone(); - return create(locale, ptn, &skeleton, status); -} - - -DateIntervalFormat::DateIntervalFormat() -: fInfo(nullptr), - fDateFormat(nullptr), - fFromCalendar(nullptr), - fToCalendar(nullptr), - fLocale(Locale::getRoot()), - fDatePattern(nullptr), - fTimePattern(nullptr), - fDateTimeFormat(nullptr), - fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) -{} - - -DateIntervalFormat::DateIntervalFormat(const DateIntervalFormat& itvfmt) -: Format(itvfmt), - fInfo(nullptr), - fDateFormat(nullptr), - fFromCalendar(nullptr), - fToCalendar(nullptr), - fLocale(itvfmt.fLocale), - fDatePattern(nullptr), - fTimePattern(nullptr), - fDateTimeFormat(nullptr), - fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) { - *this = itvfmt; -} - - -DateIntervalFormat& -DateIntervalFormat::operator=(const DateIntervalFormat& itvfmt) { - if ( this != &itvfmt ) { - delete fDateFormat; - delete fInfo; - delete fFromCalendar; - delete fToCalendar; - delete fDatePattern; - delete fTimePattern; - delete fDateTimeFormat; - { - Mutex lock(&gFormatterMutex); - if ( itvfmt.fDateFormat ) { - fDateFormat = itvfmt.fDateFormat->clone(); - } else { - fDateFormat = nullptr; - } - if ( itvfmt.fFromCalendar ) { - fFromCalendar = itvfmt.fFromCalendar->clone(); - } else { - fFromCalendar = nullptr; - } - if ( itvfmt.fToCalendar ) { - fToCalendar = itvfmt.fToCalendar->clone(); - } else { - fToCalendar = nullptr; - } - } - if ( itvfmt.fInfo ) { - fInfo = itvfmt.fInfo->clone(); - } else { - fInfo = nullptr; - } - fSkeleton = itvfmt.fSkeleton; - int8_t i; - for ( i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { - fIntervalPatterns[i] = itvfmt.fIntervalPatterns[i]; - } - fLocale = itvfmt.fLocale; - fDatePattern = (itvfmt.fDatePattern)? itvfmt.fDatePattern->clone(): nullptr; - fTimePattern = (itvfmt.fTimePattern)? itvfmt.fTimePattern->clone(): nullptr; - fDateTimeFormat = (itvfmt.fDateTimeFormat)? itvfmt.fDateTimeFormat->clone(): nullptr; - fCapitalizationContext = itvfmt.fCapitalizationContext; - } - return *this; -} - - -DateIntervalFormat::~DateIntervalFormat() { - delete fInfo; - delete fDateFormat; - delete fFromCalendar; - delete fToCalendar; - delete fDatePattern; - delete fTimePattern; - delete fDateTimeFormat; -} - - -DateIntervalFormat* -DateIntervalFormat::clone() const { - return new DateIntervalFormat(*this); -} - - -bool -DateIntervalFormat::operator==(const Format& other) const { - if (typeid(*this) != typeid(other)) {return false;} - const DateIntervalFormat* fmt = (DateIntervalFormat*)&other; - if (this == fmt) {return true;} - if (!Format::operator==(other)) {return false;} - if ((fInfo != fmt->fInfo) && (fInfo == nullptr || fmt->fInfo == nullptr)) {return false;} - if (fInfo && fmt->fInfo && (*fInfo != *fmt->fInfo )) {return false;} - { - Mutex lock(&gFormatterMutex); - if (fDateFormat != fmt->fDateFormat && (fDateFormat == nullptr || fmt->fDateFormat == nullptr)) {return false;} - if (fDateFormat && fmt->fDateFormat && (*fDateFormat != *fmt->fDateFormat)) {return false;} - } - // note: fFromCalendar and fToCalendar hold no persistent state, and therefore do not participate in operator ==. - // fDateFormat has the primary calendar for the DateIntervalFormat. - if (fSkeleton != fmt->fSkeleton) {return false;} - if (fDatePattern != fmt->fDatePattern && (fDatePattern == nullptr || fmt->fDatePattern == nullptr)) {return false;} - if (fDatePattern && fmt->fDatePattern && (*fDatePattern != *fmt->fDatePattern)) {return false;} - if (fTimePattern != fmt->fTimePattern && (fTimePattern == nullptr || fmt->fTimePattern == nullptr)) {return false;} - if (fTimePattern && fmt->fTimePattern && (*fTimePattern != *fmt->fTimePattern)) {return false;} - if (fDateTimeFormat != fmt->fDateTimeFormat && (fDateTimeFormat == nullptr || fmt->fDateTimeFormat == nullptr)) {return false;} - if (fDateTimeFormat && fmt->fDateTimeFormat && (*fDateTimeFormat != *fmt->fDateTimeFormat)) {return false;} - if (fLocale != fmt->fLocale) {return false;} - - for (int32_t i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { - if (fIntervalPatterns[i].firstPart != fmt->fIntervalPatterns[i].firstPart) {return false;} - if (fIntervalPatterns[i].secondPart != fmt->fIntervalPatterns[i].secondPart ) {return false;} - if (fIntervalPatterns[i].laterDateFirst != fmt->fIntervalPatterns[i].laterDateFirst) {return false;} - } - if (fCapitalizationContext != fmt->fCapitalizationContext) {return false;} - return true; -} - - -UnicodeString& -DateIntervalFormat::format(const Formattable& obj, - UnicodeString& appendTo, - FieldPosition& fieldPosition, - UErrorCode& status) const { - if ( U_FAILURE(status) ) { - return appendTo; - } - - if ( obj.getType() == Formattable::kObject ) { - const UObject* formatObj = obj.getObject(); - const DateInterval* interval = dynamic_cast(formatObj); - if (interval != nullptr) { - return format(interval, appendTo, fieldPosition, status); - } - } - status = U_ILLEGAL_ARGUMENT_ERROR; - return appendTo; -} - - -UnicodeString& -DateIntervalFormat::format(const DateInterval* dtInterval, - UnicodeString& appendTo, - FieldPosition& fieldPosition, - UErrorCode& status) const { - if ( U_FAILURE(status) ) { - return appendTo; - } - if (fDateFormat == nullptr || fInfo == nullptr) { - status = U_INVALID_STATE_ERROR; - return appendTo; - } - - FieldPositionOnlyHandler handler(fieldPosition); - handler.setAcceptFirstOnly(true); - int8_t ignore; - - Mutex lock(&gFormatterMutex); - return formatIntervalImpl(*dtInterval, appendTo, ignore, handler, status); -} - - -FormattedDateInterval DateIntervalFormat::formatToValue( - const DateInterval& dtInterval, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return FormattedDateInterval(status); - } - // LocalPointer only sets OOM status if U_SUCCESS is true. - LocalPointer result(new FormattedDateIntervalData(status), status); - if (U_FAILURE(status)) { - return FormattedDateInterval(status); - } - UnicodeString string; - int8_t firstIndex; - auto handler = result->getHandler(status); - handler.setCategory(UFIELD_CATEGORY_DATE); - { - Mutex lock(&gFormatterMutex); - formatIntervalImpl(dtInterval, string, firstIndex, handler, status); - } - handler.getError(status); - result->appendString(string, status); - if (U_FAILURE(status)) { - return FormattedDateInterval(status); - } - - // Compute the span fields and sort them into place: - if (firstIndex != -1) { - result->addOverlapSpans(UFIELD_CATEGORY_DATE_INTERVAL_SPAN, firstIndex, status); - if (U_FAILURE(status)) { - return FormattedDateInterval(status); - } - result->sort(); - } - - return FormattedDateInterval(result.orphan()); -} - - -UnicodeString& -DateIntervalFormat::format(Calendar& fromCalendar, - Calendar& toCalendar, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode& status) const { - FieldPositionOnlyHandler handler(pos); - handler.setAcceptFirstOnly(true); - int8_t ignore; - - Mutex lock(&gFormatterMutex); - return formatImpl(fromCalendar, toCalendar, appendTo, ignore, handler, status); -} - - -FormattedDateInterval DateIntervalFormat::formatToValue( - Calendar& fromCalendar, - Calendar& toCalendar, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return FormattedDateInterval(status); - } - // LocalPointer only sets OOM status if U_SUCCESS is true. - LocalPointer result(new FormattedDateIntervalData(status), status); - if (U_FAILURE(status)) { - return FormattedDateInterval(status); - } - UnicodeString string; - int8_t firstIndex; - auto handler = result->getHandler(status); - handler.setCategory(UFIELD_CATEGORY_DATE); - { - Mutex lock(&gFormatterMutex); - formatImpl(fromCalendar, toCalendar, string, firstIndex, handler, status); - } - handler.getError(status); - result->appendString(string, status); - if (U_FAILURE(status)) { - return FormattedDateInterval(status); - } - - // Compute the span fields and sort them into place: - if (firstIndex != -1) { - result->addOverlapSpans(UFIELD_CATEGORY_DATE_INTERVAL_SPAN, firstIndex, status); - result->sort(); - } - - return FormattedDateInterval(result.orphan()); -} - - -UnicodeString& DateIntervalFormat::formatIntervalImpl( - const DateInterval& dtInterval, - UnicodeString& appendTo, - int8_t& firstIndex, - FieldPositionHandler& fphandler, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return appendTo; - } - if (fFromCalendar == nullptr || fToCalendar == nullptr) { - status = U_INVALID_STATE_ERROR; - return appendTo; - } - fFromCalendar->setTime(dtInterval.getFromDate(), status); - fToCalendar->setTime(dtInterval.getToDate(), status); - return formatImpl(*fFromCalendar, *fToCalendar, appendTo, firstIndex, fphandler, status); -} - - -// The following is only called from within the gFormatterMutex lock -UnicodeString& -DateIntervalFormat::formatImpl(Calendar& fromCalendar, - Calendar& toCalendar, - UnicodeString& appendTo, - int8_t& firstIndex, - FieldPositionHandler& fphandler, - UErrorCode& status) const { - if ( U_FAILURE(status) ) { - return appendTo; - } - - // Initialize firstIndex to -1 (single date, no range) - firstIndex = -1; - - // not support different calendar types and time zones - //if ( fromCalendar.getType() != toCalendar.getType() ) { - if ( !fromCalendar.isEquivalentTo(toCalendar) ) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return appendTo; - } - - // First, find the largest different calendar field. - UCalendarDateFields field = UCAL_FIELD_COUNT; - - if ( fromCalendar.get(UCAL_ERA,status) != toCalendar.get(UCAL_ERA,status)) { - field = UCAL_ERA; - } else if ( fromCalendar.get(UCAL_YEAR, status) != - toCalendar.get(UCAL_YEAR, status) ) { - field = UCAL_YEAR; - } else if ( fromCalendar.get(UCAL_MONTH, status) != - toCalendar.get(UCAL_MONTH, status) ) { - field = UCAL_MONTH; - } else if ( fromCalendar.get(UCAL_DATE, status) != - toCalendar.get(UCAL_DATE, status) ) { - field = UCAL_DATE; - } else if ( fromCalendar.get(UCAL_AM_PM, status) != - toCalendar.get(UCAL_AM_PM, status) ) { - field = UCAL_AM_PM; - } else if ( fromCalendar.get(UCAL_HOUR, status) != - toCalendar.get(UCAL_HOUR, status) ) { - field = UCAL_HOUR; - } else if ( fromCalendar.get(UCAL_MINUTE, status) != - toCalendar.get(UCAL_MINUTE, status) ) { - field = UCAL_MINUTE; - } else if ( fromCalendar.get(UCAL_SECOND, status) != - toCalendar.get(UCAL_SECOND, status) ) { - field = UCAL_SECOND; - } else if ( fromCalendar.get(UCAL_MILLISECOND, status) != - toCalendar.get(UCAL_MILLISECOND, status) ) { - field = UCAL_MILLISECOND; - } - - if ( U_FAILURE(status) ) { - return appendTo; - } - UErrorCode tempStatus = U_ZERO_ERROR; // for setContext, ignored - // Set up fDateFormat to handle the first or only part of the interval - // (override later for any second part). Inside lock, OK to modify fDateFormat. - fDateFormat->setContext(fCapitalizationContext, tempStatus); - - if ( field == UCAL_FIELD_COUNT ) { - /* ignore the millisecond etc. small fields' difference. - * use single date when all the above are the same. - */ - return fDateFormat->_format(fromCalendar, appendTo, fphandler, status); - } - UBool fromToOnSameDay = (field==UCAL_AM_PM || field==UCAL_HOUR || field==UCAL_MINUTE || field==UCAL_SECOND || field==UCAL_MILLISECOND); - - // following call should not set wrong status, - // all the pass-in fields are valid till here - int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, - status); - const PatternInfo& intervalPattern = fIntervalPatterns[itvPtnIndex]; - - if ( intervalPattern.firstPart.isEmpty() && - intervalPattern.secondPart.isEmpty() ) { - if ( fDateFormat->isFieldUnitIgnored(field) ) { - /* the largest different calendar field is small than - * the smallest calendar field in pattern, - * return single date format. - */ - return fDateFormat->_format(fromCalendar, appendTo, fphandler, status); - } - return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, firstIndex, fphandler, status); - } - // If the first part in interval pattern is empty, - // the 2nd part of it saves the full-pattern used in fall-back. - // For a 'real' interval pattern, the first part will never be empty. - if ( intervalPattern.firstPart.isEmpty() ) { - // fall back - UnicodeString originalPattern; - fDateFormat->toPattern(originalPattern); - fDateFormat->applyPattern(intervalPattern.secondPart); - appendTo = fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, firstIndex, fphandler, status); - fDateFormat->applyPattern(originalPattern); - return appendTo; - } - Calendar* firstCal; - Calendar* secondCal; - if ( intervalPattern.laterDateFirst ) { - firstCal = &toCalendar; - secondCal = &fromCalendar; - firstIndex = 1; - } else { - firstCal = &fromCalendar; - secondCal = &toCalendar; - firstIndex = 0; - } - // break the interval pattern into 2 parts, - // first part should not be empty, - UnicodeString originalPattern; - fDateFormat->toPattern(originalPattern); - fDateFormat->applyPattern(intervalPattern.firstPart); - fDateFormat->_format(*firstCal, appendTo, fphandler, status); - - if ( !intervalPattern.secondPart.isEmpty() ) { - fDateFormat->applyPattern(intervalPattern.secondPart); - // No capitalization for second part of interval - tempStatus = U_ZERO_ERROR; - fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus); - fDateFormat->_format(*secondCal, appendTo, fphandler, status); - } - fDateFormat->applyPattern(originalPattern); - return appendTo; -} - - - -void -DateIntervalFormat::parseObject(const UnicodeString& /* source */, - Formattable& /* result */, - ParsePosition& /* parse_pos */) const { - // parseObject(const UnicodeString&, Formattable&, UErrorCode&) const - // will set status as U_INVALID_FORMAT_ERROR if - // parse_pos is still 0 -} - - - - -const DateIntervalInfo* -DateIntervalFormat::getDateIntervalInfo() const { - return fInfo; -} - - -void -DateIntervalFormat::setDateIntervalInfo(const DateIntervalInfo& newItvPattern, - UErrorCode& status) { - delete fInfo; - fInfo = new DateIntervalInfo(newItvPattern); - if (fInfo == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - - // Delete patterns that get reset by initializePattern - delete fDatePattern; - fDatePattern = nullptr; - delete fTimePattern; - fTimePattern = nullptr; - delete fDateTimeFormat; - fDateTimeFormat = nullptr; - - if (fDateFormat) { - initializePattern(status); - } -} - - - -const DateFormat* -DateIntervalFormat::getDateFormat() const { - return fDateFormat; -} - - -void -DateIntervalFormat::adoptTimeZone(TimeZone* zone) -{ - if (fDateFormat != nullptr) { - fDateFormat->adoptTimeZone(zone); - } - // The fDateFormat has the primary calendar for the DateIntervalFormat and has - // ownership of any adopted TimeZone; fFromCalendar and fToCalendar are internal - // work clones of that calendar (and should not also be given ownership of the - // adopted TimeZone). - if (fFromCalendar) { - fFromCalendar->setTimeZone(*zone); - } - if (fToCalendar) { - fToCalendar->setTimeZone(*zone); - } -} - -void -DateIntervalFormat::setTimeZone(const TimeZone& zone) -{ - if (fDateFormat != nullptr) { - fDateFormat->setTimeZone(zone); - } - // The fDateFormat has the primary calendar for the DateIntervalFormat; - // fFromCalendar and fToCalendar are internal work clones of that calendar. - if (fFromCalendar) { - fFromCalendar->setTimeZone(zone); - } - if (fToCalendar) { - fToCalendar->setTimeZone(zone); - } -} - -const TimeZone& -DateIntervalFormat::getTimeZone() const -{ - if (fDateFormat != nullptr) { - Mutex lock(&gFormatterMutex); - return fDateFormat->getTimeZone(); - } - // If fDateFormat is nullptr (unexpected), create default timezone. - return *(TimeZone::createDefault()); -} - -void -DateIntervalFormat::setContext(UDisplayContext value, UErrorCode& status) -{ - if (U_FAILURE(status)) - return; - if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) { - fCapitalizationContext = value; - } else { - status = U_ILLEGAL_ARGUMENT_ERROR; - } -} - -UDisplayContext -DateIntervalFormat::getContext(UDisplayContextType type, UErrorCode& status) const -{ - if (U_FAILURE(status)) - return (UDisplayContext)0; - if (type != UDISPCTX_TYPE_CAPITALIZATION) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return (UDisplayContext)0; - } - return fCapitalizationContext; -} - -DateIntervalFormat::DateIntervalFormat(const Locale& locale, - DateIntervalInfo* dtItvInfo, - const UnicodeString* skeleton, - UErrorCode& status) -: fInfo(nullptr), - fDateFormat(nullptr), - fFromCalendar(nullptr), - fToCalendar(nullptr), - fLocale(locale), - fDatePattern(nullptr), - fTimePattern(nullptr), - fDateTimeFormat(nullptr), - fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) -{ - LocalPointer info(dtItvInfo, status); - LocalPointer dtfmt(static_cast( - DateFormat::createInstanceForSkeleton(*skeleton, locale, status)), status); - if (U_FAILURE(status)) { - return; - } - - if ( skeleton ) { - fSkeleton = *skeleton; - } - fInfo = info.orphan(); - fDateFormat = dtfmt.orphan(); - if ( fDateFormat->getCalendar() ) { - fFromCalendar = fDateFormat->getCalendar()->clone(); - fToCalendar = fDateFormat->getCalendar()->clone(); - } - initializePattern(status); -} - -DateIntervalFormat* U_EXPORT2 -DateIntervalFormat::create(const Locale& locale, - DateIntervalInfo* dtitvinf, - const UnicodeString* skeleton, - UErrorCode& status) { - DateIntervalFormat* f = new DateIntervalFormat(locale, dtitvinf, - skeleton, status); - if ( f == nullptr ) { - status = U_MEMORY_ALLOCATION_ERROR; - delete dtitvinf; - } else if ( U_FAILURE(status) ) { - // safe to delete f, although nothing actually is saved - delete f; - f = 0; - } - return f; -} - - - -/** - * Initialize interval patterns locale to this formatter - * - * This code is a bit complicated since - * 1. the interval patterns saved in resource bundle files are interval - * patterns based on date or time only. - * It does not have interval patterns based on both date and time. - * Interval patterns on both date and time are algorithm generated. - * - * For example, it has interval patterns on skeleton "dMy" and "hm", - * but it does not have interval patterns on skeleton "dMyhm". - * - * The rule to genearte interval patterns for both date and time skeleton are - * 1) when the year, month, or day differs, concatenate the two original - * expressions with a separator between, - * For example, interval pattern from "Jan 10, 2007 10:10 am" - * to "Jan 11, 2007 10:10am" is - * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am" - * - * 2) otherwise, present the date followed by the range expression - * for the time. - * For example, interval pattern from "Jan 10, 2007 10:10 am" - * to "Jan 10, 2007 11:10am" is - * "Jan 10, 2007 10:10 am - 11:10am" - * - * 2. even a pattern does not request a certion calendar field, - * the interval pattern needs to include such field if such fields are - * different between 2 dates. - * For example, a pattern/skeleton is "hm", but the interval pattern - * includes year, month, and date when year, month, and date differs. - * - * @param status output param set to success/failure code on exit - * @stable ICU 4.0 - */ -void -DateIntervalFormat::initializePattern(UErrorCode& status) { - if ( U_FAILURE(status) ) { - return; - } - const Locale& locale = fDateFormat->getSmpFmtLocale(); - if ( fSkeleton.isEmpty() ) { - UnicodeString fullPattern; - fDateFormat->toPattern(fullPattern); -#ifdef DTITVFMT_DEBUG - char result[1000]; - char result_1[1000]; - char mesg[2000]; - fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); - sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result); - PRINTMESG(mesg) -#endif - // fSkeleton is already set by createDateIntervalInstance() - // or by createInstance(UnicodeString skeleton, .... ) - fSkeleton = DateTimePatternGenerator::staticGetSkeleton( - fullPattern, status); - if ( U_FAILURE(status) ) { - return; - } - } - - // initialize the fIntervalPattern ordering - int8_t i; - for ( i = 0; i < DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { - fIntervalPatterns[i].laterDateFirst = fInfo->getDefaultOrder(); - } - - /* Check whether the skeleton is a combination of date and time. - * For the complication reason 1 explained above. - */ - UnicodeString dateSkeleton; - UnicodeString timeSkeleton; - UnicodeString normalizedTimeSkeleton; - UnicodeString normalizedDateSkeleton; - - - /* the difference between time skeleton and normalizedTimeSkeleton are: - * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true) - * 2. (Formerly, 'a' was omitted in normalized time skeleton; this is now handled elsewhere) - * 3. there is only one appearance for 'h' or 'H', 'm','v', 'z' in normalized - * time skeleton - * - * The difference between date skeleton and normalizedDateSkeleton are: - * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton - * 2. 'E' and 'EE' are normalized into 'EEE' - * 3. 'MM' is normalized into 'M' - */ - UnicodeString convertedSkeleton = normalizeHourMetacharacters(fSkeleton); - getDateTimeSkeleton(convertedSkeleton, dateSkeleton, normalizedDateSkeleton, - timeSkeleton, normalizedTimeSkeleton); - -#ifdef DTITVFMT_DEBUG - char result[1000]; - char result_1[1000]; - char mesg[2000]; - fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); - sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result); - PRINTMESG(mesg) -#endif - - // move this up here since we need it for fallbacks - if ( timeSkeleton.length() > 0 && dateSkeleton.length() > 0 ) { - // Need the Date/Time pattern for concatenation of the date - // with the time interval. - // The date/time pattern ( such as {0} {1} ) is saved in - // calendar, that is why need to get the CalendarData here. - LocalUResourceBundlePointer dateTimePatternsRes(ures_open(nullptr, locale.getBaseName(), &status)); - ures_getByKey(dateTimePatternsRes.getAlias(), gCalendarTag, - dateTimePatternsRes.getAlias(), &status); - ures_getByKeyWithFallback(dateTimePatternsRes.getAlias(), gGregorianTag, - dateTimePatternsRes.getAlias(), &status); - ures_getByKeyWithFallback(dateTimePatternsRes.getAlias(), gDateTimePatternsTag, - dateTimePatternsRes.getAlias(), &status); - - int32_t dateTimeFormatLength; - const UChar* dateTimeFormat = ures_getStringByIndex( - dateTimePatternsRes.getAlias(), - (int32_t)DateFormat::kDateTime, - &dateTimeFormatLength, &status); - if ( U_SUCCESS(status) && dateTimeFormatLength >= 3 ) { - fDateTimeFormat = new UnicodeString(dateTimeFormat, dateTimeFormatLength); - if (fDateTimeFormat == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - } - - UBool found = setSeparateDateTimePtn(normalizedDateSkeleton, - normalizedTimeSkeleton); - - // for skeletons with seconds, found is false and we enter this block - if ( found == false ) { - // use fallback - // TODO: if user asks "m"(minute), but "d"(day) differ - if ( timeSkeleton.length() != 0 ) { - if ( dateSkeleton.length() == 0 ) { - // prefix with yMd - timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1); - UnicodeString pattern = DateFormat::getBestPattern( - locale, timeSkeleton, status); - if ( U_FAILURE(status) ) { - return; - } - // for fall back interval patterns, - // the first part of the pattern is empty, - // the second part of the pattern is the full-pattern - // should be used in fall-back. - setPatternInfo(UCAL_DATE, nullptr, &pattern, fInfo->getDefaultOrder()); - setPatternInfo(UCAL_MONTH, nullptr, &pattern, fInfo->getDefaultOrder()); - setPatternInfo(UCAL_YEAR, nullptr, &pattern, fInfo->getDefaultOrder()); - - timeSkeleton.insert(0, CAP_G); - pattern = DateFormat::getBestPattern( - locale, timeSkeleton, status); - if ( U_FAILURE(status) ) { - return; - } - setPatternInfo(UCAL_ERA, nullptr, &pattern, fInfo->getDefaultOrder()); - } else { - // TODO: fall back - } - } else { - // TODO: fall back - } - return; - } // end of skeleton not found - // interval patterns for skeleton are found in resource - if ( timeSkeleton.length() == 0 ) { - // done - } else if ( dateSkeleton.length() == 0 ) { - // prefix with yMd - timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1); - UnicodeString pattern = DateFormat::getBestPattern( - locale, timeSkeleton, status); - if ( U_FAILURE(status) ) { - return; - } - // for fall back interval patterns, - // the first part of the pattern is empty, - // the second part of the pattern is the full-pattern - // should be used in fall-back. - setPatternInfo(UCAL_DATE, nullptr, &pattern, fInfo->getDefaultOrder()); - setPatternInfo(UCAL_MONTH, nullptr, &pattern, fInfo->getDefaultOrder()); - setPatternInfo(UCAL_YEAR, nullptr, &pattern, fInfo->getDefaultOrder()); - - timeSkeleton.insert(0, CAP_G); - pattern = DateFormat::getBestPattern( - locale, timeSkeleton, status); - if ( U_FAILURE(status) ) { - return; - } - setPatternInfo(UCAL_ERA, nullptr, &pattern, fInfo->getDefaultOrder()); - } else { - /* if both present, - * 1) when the era, year, month, or day differs, - * concatenate the two original expressions with a separator between, - * 2) otherwise, present the date followed by the - * range expression for the time. - */ - /* - * 1) when the era, year, month, or day differs, - * concatenate the two original expressions with a separator between, - */ - // if field exists, use fall back - UnicodeString skeleton = fSkeleton; - if ( !fieldExistsInSkeleton(UCAL_DATE, dateSkeleton) ) { - // prefix skeleton with 'd' - skeleton.insert(0, LOW_D); - setFallbackPattern(UCAL_DATE, skeleton, status); - } - if ( !fieldExistsInSkeleton(UCAL_MONTH, dateSkeleton) ) { - // then prefix skeleton with 'M' - skeleton.insert(0, CAP_M); - setFallbackPattern(UCAL_MONTH, skeleton, status); - } - if ( !fieldExistsInSkeleton(UCAL_YEAR, dateSkeleton) ) { - // then prefix skeleton with 'y' - skeleton.insert(0, LOW_Y); - setFallbackPattern(UCAL_YEAR, skeleton, status); - } - if ( !fieldExistsInSkeleton(UCAL_ERA, dateSkeleton) ) { - // then prefix skeleton with 'G' - skeleton.insert(0, CAP_G); - setFallbackPattern(UCAL_ERA, skeleton, status); - } - - /* - * 2) otherwise, present the date followed by the - * range expression for the time. - */ - - if ( fDateTimeFormat == nullptr ) { - // earlier failure getting dateTimeFormat - return; - } - - UnicodeString datePattern = DateFormat::getBestPattern( - locale, dateSkeleton, status); - - concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_AM_PM, status); - concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_HOUR, status); - concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_MINUTE, status); - } -} - - - -UnicodeString -DateIntervalFormat::normalizeHourMetacharacters(const UnicodeString& skeleton) const { - UnicodeString result = skeleton; - - UChar hourMetachar = u'\0'; - UChar dayPeriodChar = u'\0'; - int32_t hourFieldStart = 0; - int32_t hourFieldLength = 0; - int32_t dayPeriodStart = 0; - int32_t dayPeriodLength = 0; - for (int32_t i = 0; i < result.length(); i++) { - UChar c = result[i]; - if (c == LOW_J || c == CAP_J || c == CAP_C || c == LOW_H || c == CAP_H || c == LOW_K || c == CAP_K) { - if (hourMetachar == u'\0') { - hourMetachar = c; - hourFieldStart = i; - } - ++hourFieldLength; - } else if (c == LOW_A || c == LOW_B || c == CAP_B) { - if (dayPeriodChar == u'\0') { - dayPeriodChar = c; - dayPeriodStart = i; - } - ++dayPeriodLength; - } else { - if (hourMetachar != u'\0' && dayPeriodChar != u'\0') { - break; - } - } - } - - if (hourMetachar != u'\0') { - UErrorCode err = U_ZERO_ERROR; - UChar hourChar = CAP_H; - UnicodeString convertedPattern = DateFormat::getBestPattern(fLocale, UnicodeString(hourMetachar), err); - - if (U_SUCCESS(err)) { - // strip literal text from the pattern (so literal characters don't get mistaken for pattern - // characters-- such as the 'h' in 'Uhr' in Germam) - int32_t firstQuotePos; - while ((firstQuotePos = convertedPattern.indexOf(u'\'')) != -1) { - int32_t secondQuotePos = convertedPattern.indexOf(u'\'', firstQuotePos + 1); - if (secondQuotePos == -1) { - secondQuotePos = firstQuotePos; - } - convertedPattern.replace(firstQuotePos, (secondQuotePos - firstQuotePos) + 1, UnicodeString()); - } - - if (convertedPattern.indexOf(LOW_H) != -1) { - hourChar = LOW_H; - } else if (convertedPattern.indexOf(CAP_K) != -1) { - hourChar = CAP_K; - } else if (convertedPattern.indexOf(LOW_K) != -1) { - hourChar = LOW_K; - } - - if (convertedPattern.indexOf(LOW_B) != -1) { - dayPeriodChar = LOW_B; - } else if (convertedPattern.indexOf(CAP_B) != -1) { - dayPeriodChar = CAP_B; - } else if (dayPeriodChar == u'\0') { - dayPeriodChar = LOW_A; - } - } - - UnicodeString hourAndDayPeriod(hourChar); - if (hourChar != CAP_H && hourChar != LOW_K) { - int32_t newDayPeriodLength = 0; - if (dayPeriodLength >= 5 || hourFieldLength >= 5) { - newDayPeriodLength = 5; - } else if (dayPeriodLength >= 3 || hourFieldLength >= 3) { - newDayPeriodLength = 3; - } else { - newDayPeriodLength = 1; - } - for (int32_t i = 0; i < newDayPeriodLength; i++) { - hourAndDayPeriod.append(dayPeriodChar); - } - } - result.replace(hourFieldStart, hourFieldLength, hourAndDayPeriod); - if (dayPeriodStart > hourFieldStart) { - // before deleting the original day period field, adjust its position in case - // we just changed the size of the hour field (and new day period field) - dayPeriodStart += hourAndDayPeriod.length() - hourFieldLength; - } - result.remove(dayPeriodStart, dayPeriodLength); - } - return result; -} - - -void U_EXPORT2 -DateIntervalFormat::getDateTimeSkeleton(const UnicodeString& skeleton, - UnicodeString& dateSkeleton, - UnicodeString& normalizedDateSkeleton, - UnicodeString& timeSkeleton, - UnicodeString& normalizedTimeSkeleton) { - // dateSkeleton follows the sequence of y*M*E*d* - // timeSkeleton follows the sequence of hm*[v|z]? - int32_t ECount = 0; - int32_t dCount = 0; - int32_t MCount = 0; - int32_t yCount = 0; - int32_t mCount = 0; - int32_t vCount = 0; - int32_t zCount = 0; - UChar hourChar = u'\0'; - int32_t i; - - for (i = 0; i < skeleton.length(); ++i) { - UChar ch = skeleton[i]; - switch ( ch ) { - case CAP_E: - dateSkeleton.append(ch); - ++ECount; - break; - case LOW_D: - dateSkeleton.append(ch); - ++dCount; - break; - case CAP_M: - dateSkeleton.append(ch); - ++MCount; - break; - case LOW_Y: - dateSkeleton.append(ch); - ++yCount; - break; - case CAP_G: - case CAP_Y: - case LOW_U: - case CAP_Q: - case LOW_Q: - case CAP_L: - case LOW_L: - case CAP_W: - case LOW_W: - case CAP_D: - case CAP_F: - case LOW_G: - case LOW_E: - case LOW_C: - case CAP_U: - case LOW_R: - normalizedDateSkeleton.append(ch); - dateSkeleton.append(ch); - break; - case LOW_H: - case CAP_H: - case LOW_K: - case CAP_K: - timeSkeleton.append(ch); - if (hourChar == u'\0') { - hourChar = ch; - } - break; - case LOW_M: - timeSkeleton.append(ch); - ++mCount; - break; - case LOW_Z: - ++zCount; - timeSkeleton.append(ch); - break; - case LOW_V: - ++vCount; - timeSkeleton.append(ch); - break; - case LOW_A: - case CAP_V: - case CAP_Z: - case LOW_J: - case LOW_S: - case CAP_S: - case CAP_A: - case LOW_B: - case CAP_B: - timeSkeleton.append(ch); - normalizedTimeSkeleton.append(ch); - break; - } - } - - /* generate normalized form for date*/ - if ( yCount != 0 ) { - for (i = 0; i < yCount; ++i) { - normalizedDateSkeleton.append(LOW_Y); - } - } - if ( MCount != 0 ) { - if ( MCount < 3 ) { - normalizedDateSkeleton.append(CAP_M); - } else { - for ( int32_t j = 0; j < MCount && j < MAX_M_COUNT; ++j) { - normalizedDateSkeleton.append(CAP_M); - } - } - } - if ( ECount != 0 ) { - if ( ECount <= 3 ) { - normalizedDateSkeleton.append(CAP_E); - } else { - for ( int32_t j = 0; j < ECount && j < MAX_E_COUNT; ++j ) { - normalizedDateSkeleton.append(CAP_E); - } - } - } - if ( dCount != 0 ) { - normalizedDateSkeleton.append(LOW_D); - } - - /* generate normalized form for time */ - if ( hourChar != u'\0' ) { - normalizedTimeSkeleton.append(hourChar); - } - if ( mCount != 0 ) { - normalizedTimeSkeleton.append(LOW_M); - } - if ( zCount != 0 ) { - normalizedTimeSkeleton.append(LOW_Z); - } - if ( vCount != 0 ) { - normalizedTimeSkeleton.append(LOW_V); - } -} - - -/** - * Generate date or time interval pattern from resource, - * and set them into the interval pattern locale to this formatter. - * - * It needs to handle the following: - * 1. need to adjust field width. - * For example, the interval patterns saved in DateIntervalInfo - * includes "dMMMy", but not "dMMMMy". - * Need to get interval patterns for dMMMMy from dMMMy. - * Another example, the interval patterns saved in DateIntervalInfo - * includes "hmv", but not "hmz". - * Need to get interval patterns for "hmz' from 'hmv' - * - * 2. there might be no pattern for 'y' differ for skeleton "Md", - * in order to get interval patterns for 'y' differ, - * need to look for it from skeleton 'yMd' - * - * @param dateSkeleton normalized date skeleton - * @param timeSkeleton normalized time skeleton - * @return whether the resource is found for the skeleton. - * true if interval pattern found for the skeleton, - * false otherwise. - * @stable ICU 4.0 - */ -UBool -DateIntervalFormat::setSeparateDateTimePtn( - const UnicodeString& dateSkeleton, - const UnicodeString& timeSkeleton) { - const UnicodeString* skeleton; - // if both date and time skeleton present, - // the final interval pattern might include time interval patterns - // ( when, am_pm, hour, minute differ ), - // but not date interval patterns ( when year, month, day differ ). - // For year/month/day differ, it falls back to fall-back pattern. - if ( timeSkeleton.length() != 0 ) { - skeleton = &timeSkeleton; - } else { - skeleton = &dateSkeleton; - } - - /* interval patterns for skeleton "dMMMy" (but not "dMMMMy") - * are defined in resource, - * interval patterns for skeleton "dMMMMy" are calculated by - * 1. get the best match skeleton for "dMMMMy", which is "dMMMy" - * 2. get the interval patterns for "dMMMy", - * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy" - * getBestSkeleton() is step 1. - */ - // best skeleton, and the difference information - int8_t differenceInfo = 0; - const UnicodeString* bestSkeleton = fInfo->getBestSkeleton(*skeleton, - differenceInfo); - /* best skeleton could be nullptr. - For example: in "ca" resource file, - interval format is defined as following - intervalFormats{ - fallback{"{0} - {1}"} - } - there is no skeletons/interval patterns defined, - and the best skeleton match could be nullptr - */ - if ( bestSkeleton == nullptr ) { - return false; - } - - // Set patterns for fallback use, need to do this - // before returning if differenceInfo == -1 - UErrorCode status; - if ( dateSkeleton.length() != 0) { - status = U_ZERO_ERROR; - fDatePattern = new UnicodeString(DateFormat::getBestPattern( - fLocale, dateSkeleton, status)); - // no way to report OOM. :( - } - if ( timeSkeleton.length() != 0) { - status = U_ZERO_ERROR; - fTimePattern = new UnicodeString(DateFormat::getBestPattern( - fLocale, timeSkeleton, status)); - // no way to report OOM. :( - } - - // difference: - // 0 means the best matched skeleton is the same as input skeleton - // 1 means the fields are the same, but field width are different - // 2 means the only difference between fields are v/z, - // -1 means there are other fields difference - // (this will happen, for instance, if the supplied skeleton has seconds, - // but no skeletons in the intervalFormats data do) - if ( differenceInfo == -1 ) { - // skeleton has different fields, not only v/z difference - return false; - } - - if ( timeSkeleton.length() == 0 ) { - UnicodeString extendedSkeleton; - UnicodeString extendedBestSkeleton; - // only has date skeleton - setIntervalPattern(UCAL_DATE, skeleton, bestSkeleton, differenceInfo, - &extendedSkeleton, &extendedBestSkeleton); - - UBool extended = setIntervalPattern(UCAL_MONTH, skeleton, bestSkeleton, - differenceInfo, - &extendedSkeleton, &extendedBestSkeleton); - - if ( extended ) { - bestSkeleton = &extendedBestSkeleton; - skeleton = &extendedSkeleton; - } - setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo, - &extendedSkeleton, &extendedBestSkeleton); - setIntervalPattern(UCAL_ERA, skeleton, bestSkeleton, differenceInfo, - &extendedSkeleton, &extendedBestSkeleton); - } else { - setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo); - setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo); - setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo); - } - return true; -} - - - -void -DateIntervalFormat::setFallbackPattern(UCalendarDateFields field, - const UnicodeString& skeleton, - UErrorCode& status) { - if ( U_FAILURE(status) ) { - return; - } - UnicodeString pattern = DateFormat::getBestPattern( - fLocale, skeleton, status); - if ( U_FAILURE(status) ) { - return; - } - setPatternInfo(field, nullptr, &pattern, fInfo->getDefaultOrder()); -} - - - - -void -DateIntervalFormat::setPatternInfo(UCalendarDateFields field, - const UnicodeString* firstPart, - const UnicodeString* secondPart, - UBool laterDateFirst) { - // for fall back interval patterns, - // the first part of the pattern is empty, - // the second part of the pattern is the full-pattern - // should be used in fall-back. - UErrorCode status = U_ZERO_ERROR; - // following should not set any wrong status. - int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, - status); - if ( U_FAILURE(status) ) { - return; - } - PatternInfo& ptn = fIntervalPatterns[itvPtnIndex]; - if ( firstPart ) { - ptn.firstPart = *firstPart; - } - if ( secondPart ) { - ptn.secondPart = *secondPart; - } - ptn.laterDateFirst = laterDateFirst; -} - -void -DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, - const UnicodeString& intervalPattern) { - UBool order = fInfo->getDefaultOrder(); - setIntervalPattern(field, intervalPattern, order); -} - - -void -DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, - const UnicodeString& intervalPattern, - UBool laterDateFirst) { - const UnicodeString* pattern = &intervalPattern; - UBool order = laterDateFirst; - // check for "latestFirst:" or "earliestFirst:" prefix - int8_t prefixLength = UPRV_LENGTHOF(gLaterFirstPrefix); - int8_t earliestFirstLength = UPRV_LENGTHOF(gEarlierFirstPrefix); - UnicodeString realPattern; - if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) { - order = true; - intervalPattern.extract(prefixLength, - intervalPattern.length() - prefixLength, - realPattern); - pattern = &realPattern; - } else if ( intervalPattern.startsWith(gEarlierFirstPrefix, - earliestFirstLength) ) { - order = false; - intervalPattern.extract(earliestFirstLength, - intervalPattern.length() - earliestFirstLength, - realPattern); - pattern = &realPattern; - } - - int32_t splitPoint = splitPatternInto2Part(*pattern); - - UnicodeString firstPart; - UnicodeString secondPart; - pattern->extract(0, splitPoint, firstPart); - if ( splitPoint < pattern->length() ) { - pattern->extract(splitPoint, pattern->length()-splitPoint, secondPart); - } - setPatternInfo(field, &firstPart, &secondPart, order); -} - - - - -/** - * Generate interval pattern from existing resource - * - * It not only save the interval patterns, - * but also return the extended skeleton and its best match skeleton. - * - * @param field largest different calendar field - * @param skeleton skeleton - * @param bestSkeleton the best match skeleton which has interval pattern - * defined in resource - * @param differenceInfo the difference between skeleton and best skeleton - * 0 means the best matched skeleton is the same as input skeleton - * 1 means the fields are the same, but field width are different - * 2 means the only difference between fields are v/z, - * -1 means there are other fields difference - * - * @param extendedSkeleton extended skeleton - * @param extendedBestSkeleton extended best match skeleton - * @return whether the interval pattern is found - * through extending skeleton or not. - * true if interval pattern is found by - * extending skeleton, false otherwise. - * @stable ICU 4.0 - */ -UBool -DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, - const UnicodeString* skeleton, - const UnicodeString* bestSkeleton, - int8_t differenceInfo, - UnicodeString* extendedSkeleton, - UnicodeString* extendedBestSkeleton) { - UErrorCode status = U_ZERO_ERROR; - // following getIntervalPattern() should not generate error status - UnicodeString pattern; - fInfo->getIntervalPattern(*bestSkeleton, field, pattern, status); - if ( pattern.isEmpty() ) { - // single date - if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton, field) ) { - // do nothing, format will handle it - return false; - } - - // for 24 hour system, interval patterns in resource file - // might not include pattern when am_pm differ, - // which should be the same as hour differ. - // add it here for simplicity - if ( field == UCAL_AM_PM ) { - fInfo->getIntervalPattern(*bestSkeleton, UCAL_HOUR, pattern,status); - if ( !pattern.isEmpty() ) { - UBool suppressDayPeriodField = fSkeleton.indexOf(CAP_J) != -1; - UnicodeString adjustIntervalPattern; - adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo, - suppressDayPeriodField, adjustIntervalPattern); - setIntervalPattern(field, adjustIntervalPattern); - } - return false; - } - // else, looking for pattern when 'y' differ for 'dMMMM' skeleton, - // first, get best match pattern "MMMd", - // since there is no pattern for 'y' differs for skeleton 'MMMd', - // need to look for it from skeleton 'yMMMd', - // if found, adjust field width in interval pattern from - // "MMM" to "MMMM". - UChar fieldLetter = fgCalendarFieldToPatternLetter[field]; - if ( extendedSkeleton ) { - *extendedSkeleton = *skeleton; - *extendedBestSkeleton = *bestSkeleton; - extendedSkeleton->insert(0, fieldLetter); - extendedBestSkeleton->insert(0, fieldLetter); - // for example, looking for patterns when 'y' differ for - // skeleton "MMMM". - fInfo->getIntervalPattern(*extendedBestSkeleton,field,pattern,status); - if ( pattern.isEmpty() && differenceInfo == 0 ) { - // if there is no skeleton "yMMMM" defined, - // look for the best match skeleton, for example: "yMMM" - const UnicodeString* tmpBest = fInfo->getBestSkeleton( - *extendedBestSkeleton, differenceInfo); - if ( tmpBest != 0 && differenceInfo != -1 ) { - fInfo->getIntervalPattern(*tmpBest, field, pattern, status); - bestSkeleton = tmpBest; - } - } - } - } - if ( !pattern.isEmpty() ) { - UBool suppressDayPeriodField = fSkeleton.indexOf(CAP_J) != -1; - if ( differenceInfo != 0 || suppressDayPeriodField) { - UnicodeString adjustIntervalPattern; - adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo, - suppressDayPeriodField, adjustIntervalPattern); - setIntervalPattern(field, adjustIntervalPattern); - } else { - setIntervalPattern(field, pattern); - } - if ( extendedSkeleton && !extendedSkeleton->isEmpty() ) { - return true; - } - } - return false; -} - - - -int32_t U_EXPORT2 -DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern) { - UBool inQuote = false; - UChar prevCh = 0; - int32_t count = 0; - - /* repeatedPattern used to record whether a pattern has already seen. - It is a pattern applies to first calendar if it is first time seen, - otherwise, it is a pattern applies to the second calendar - */ - UBool patternRepeated[] = - { - // A B C D E F G H I J K L M N O - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // P Q R S T U V W X Y Z - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // a b c d e f g h i j k l m n o - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // p q r s t u v w x y z - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - int8_t PATTERN_CHAR_BASE = 0x41; - - /* loop through the pattern string character by character looking for - * the first repeated pattern letter, which breaks the interval pattern - * into 2 parts. - */ - int32_t i; - UBool foundRepetition = false; - for (i = 0; i < intervalPattern.length(); ++i) { - UChar ch = intervalPattern.charAt(i); - - if (ch != prevCh && count > 0) { - // check the repeativeness of pattern letter - UBool repeated = patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)]; - if ( repeated == false ) { - patternRepeated[prevCh - PATTERN_CHAR_BASE] = true; - } else { - foundRepetition = true; - break; - } - count = 0; - } - if (ch == 0x0027 /*'*/) { - // Consecutive single quotes are a single quote literal, - // either outside of quotes or between quotes - if ((i+1) < intervalPattern.length() && - intervalPattern.charAt(i+1) == 0x0027 /*'*/) { - ++i; - } else { - inQuote = ! inQuote; - } - } - else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) - || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { - // ch is a date-time pattern character - prevCh = ch; - ++count; - } - } - // check last pattern char, distinguish - // "dd MM" ( no repetition ), - // "d-d"(last char repeated ), and - // "d-d MM" ( repetition found ) - if ( count > 0 && foundRepetition == false ) { - if ( patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)] == false ) { - count = 0; - } - } - return (i - count); -} - -// The following is only called from fallbackFormat, i.e. within the gFormatterMutex lock -void DateIntervalFormat::fallbackFormatRange( - Calendar& fromCalendar, - Calendar& toCalendar, - UnicodeString& appendTo, - int8_t& firstIndex, - FieldPositionHandler& fphandler, - UErrorCode& status) const { - UnicodeString fallbackPattern; - fInfo->getFallbackIntervalPattern(fallbackPattern); - SimpleFormatter sf(fallbackPattern, 2, 2, status); - if (U_FAILURE(status)) { - return; - } - int32_t offsets[2]; - UnicodeString patternBody = sf.getTextWithNoArguments(offsets, 2); - - UErrorCode tempStatus = U_ZERO_ERROR; // for setContext, ignored - // TODO(ICU-20406): Use SimpleFormatter Iterator interface when available. - if (offsets[0] < offsets[1]) { - firstIndex = 0; - appendTo.append(patternBody.tempSubStringBetween(0, offsets[0])); - fDateFormat->_format(fromCalendar, appendTo, fphandler, status); - appendTo.append(patternBody.tempSubStringBetween(offsets[0], offsets[1])); - // No capitalization for second part of interval - fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus); - fDateFormat->_format(toCalendar, appendTo, fphandler, status); - appendTo.append(patternBody.tempSubStringBetween(offsets[1])); - } else { - firstIndex = 1; - appendTo.append(patternBody.tempSubStringBetween(0, offsets[1])); - fDateFormat->_format(toCalendar, appendTo, fphandler, status); - appendTo.append(patternBody.tempSubStringBetween(offsets[1], offsets[0])); - // No capitalization for second part of interval - fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus); - fDateFormat->_format(fromCalendar, appendTo, fphandler, status); - appendTo.append(patternBody.tempSubStringBetween(offsets[0])); - } -} - -// The following is only called from formatImpl, i.e. within the gFormatterMutex lock -UnicodeString& -DateIntervalFormat::fallbackFormat(Calendar& fromCalendar, - Calendar& toCalendar, - UBool fromToOnSameDay, // new - UnicodeString& appendTo, - int8_t& firstIndex, - FieldPositionHandler& fphandler, - UErrorCode& status) const { - if ( U_FAILURE(status) ) { - return appendTo; - } - - UBool formatDatePlusTimeRange = (fromToOnSameDay && fDatePattern && fTimePattern); - if (formatDatePlusTimeRange) { - SimpleFormatter sf(*fDateTimeFormat, 2, 2, status); - if (U_FAILURE(status)) { - return appendTo; - } - int32_t offsets[2]; - UnicodeString patternBody = sf.getTextWithNoArguments(offsets, 2); - - UnicodeString fullPattern; // for saving the pattern in fDateFormat - fDateFormat->toPattern(fullPattern); // save current pattern, restore later - - UErrorCode tempStatus = U_ZERO_ERROR; // for setContext, ignored - // {0} is time range - // {1} is single date portion - // TODO(ICU-20406): Use SimpleFormatter Iterator interface when available. - if (offsets[0] < offsets[1]) { - appendTo.append(patternBody.tempSubStringBetween(0, offsets[0])); - fDateFormat->applyPattern(*fTimePattern); - fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status); - appendTo.append(patternBody.tempSubStringBetween(offsets[0], offsets[1])); - fDateFormat->applyPattern(*fDatePattern); - // No capitalization for second portion - fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus); - fDateFormat->_format(fromCalendar, appendTo, fphandler, status); - appendTo.append(patternBody.tempSubStringBetween(offsets[1])); - } else { - appendTo.append(patternBody.tempSubStringBetween(0, offsets[1])); - fDateFormat->applyPattern(*fDatePattern); - fDateFormat->_format(fromCalendar, appendTo, fphandler, status); - appendTo.append(patternBody.tempSubStringBetween(offsets[1], offsets[0])); - fDateFormat->applyPattern(*fTimePattern); - // No capitalization for second portion - fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus); - fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status); - appendTo.append(patternBody.tempSubStringBetween(offsets[0])); - } - - // restore full pattern - fDateFormat->applyPattern(fullPattern); - } else { - fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status); - } - return appendTo; -} - - - - -UBool U_EXPORT2 -DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field, - const UnicodeString& skeleton) -{ - const UChar fieldChar = fgCalendarFieldToPatternLetter[field]; - return ( (skeleton.indexOf(fieldChar) == -1)?false:true ) ; -} - - - -void U_EXPORT2 -DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton, - const UnicodeString& bestMatchSkeleton, - const UnicodeString& bestIntervalPattern, - int8_t differenceInfo, - UBool suppressDayPeriodField, - UnicodeString& adjustedPtn) { - adjustedPtn = bestIntervalPattern; - int32_t inputSkeletonFieldWidth[] = - { - // A B C D E F G H I J K L M N O - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // P Q R S T U V W X Y Z - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // a b c d e f g h i j k l m n o - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // p q r s t u v w x y z - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - int32_t bestMatchSkeletonFieldWidth[] = - { - // A B C D E F G H I J K L M N O - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // P Q R S T U V W X Y Z - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // a b c d e f g h i j k l m n o - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // p q r s t u v w x y z - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - const int8_t PATTERN_CHAR_BASE = 0x41; - - DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth); - DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth); - if (suppressDayPeriodField) { - // remove the 'a' and any NBSP/NNBSP on one side of it - findReplaceInPattern(adjustedPtn, UnicodeString(u"\u00A0a",-1), UnicodeString()); - findReplaceInPattern(adjustedPtn, UnicodeString(u"\u202Fa",-1), UnicodeString()); - findReplaceInPattern(adjustedPtn, UnicodeString(u"a\u00A0",-1), UnicodeString()); - findReplaceInPattern(adjustedPtn, UnicodeString(u"a\u202F",-1), UnicodeString()); - findReplaceInPattern(adjustedPtn, UnicodeString(LOW_A), UnicodeString()); - // adjust interior double spaces, remove exterior whitespace - findReplaceInPattern(adjustedPtn, UnicodeString(" "), UnicodeString(" ")); - adjustedPtn.trim(); - } - if ( differenceInfo == 2 ) { - if (inputSkeleton.indexOf(LOW_Z) != -1) { - findReplaceInPattern(adjustedPtn, UnicodeString(LOW_V), UnicodeString(LOW_Z)); - } - if (inputSkeleton.indexOf(CAP_K) != -1) { - findReplaceInPattern(adjustedPtn, UnicodeString(LOW_H), UnicodeString(CAP_K)); - } - if (inputSkeleton.indexOf(LOW_K) != -1) { - findReplaceInPattern(adjustedPtn, UnicodeString(CAP_H), UnicodeString(LOW_K)); - } - if (inputSkeleton.indexOf(LOW_B) != -1) { - findReplaceInPattern(adjustedPtn, UnicodeString(LOW_A), UnicodeString(LOW_B)); - } - } - if (adjustedPtn.indexOf(LOW_A) != -1 && bestMatchSkeletonFieldWidth[LOW_A - PATTERN_CHAR_BASE] == 0) { - bestMatchSkeletonFieldWidth[LOW_A - PATTERN_CHAR_BASE] = 1; - } - if (adjustedPtn.indexOf(LOW_B) != -1 && bestMatchSkeletonFieldWidth[LOW_B - PATTERN_CHAR_BASE] == 0) { - bestMatchSkeletonFieldWidth[LOW_B - PATTERN_CHAR_BASE] = 1; - } - - UBool inQuote = false; - UChar prevCh = 0; - int32_t count = 0; - - // loop through the pattern string character by character - int32_t adjustedPtnLength = adjustedPtn.length(); - int32_t i; - for (i = 0; i < adjustedPtnLength; ++i) { - UChar ch = adjustedPtn.charAt(i); - if (ch != prevCh && count > 0) { - // check the repeativeness of pattern letter - UChar skeletonChar = prevCh; - if ( skeletonChar == CAP_L ) { - // there is no "L" (always be "M") in skeleton, - // but there is "L" in pattern. - // for skeleton "M+", the pattern might be "...L..." - skeletonChar = CAP_M; - } - int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; - int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; - if ( fieldCount == count && inputFieldCount > fieldCount ) { - count = inputFieldCount - fieldCount; - int32_t j; - for ( j = 0; j < count; ++j ) { - adjustedPtn.insert(i, prevCh); - } - i += count; - adjustedPtnLength += count; - } - count = 0; - } - if (ch == 0x0027 /*'*/) { - // Consecutive single quotes are a single quote literal, - // either outside of quotes or between quotes - if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == 0x0027 /* ' */) { - ++i; - } else { - inQuote = ! inQuote; - } - } - else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) - || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { - // ch is a date-time pattern character - prevCh = ch; - ++count; - } - } - if ( count > 0 ) { - // last item - // check the repeativeness of pattern letter - UChar skeletonChar = prevCh; - if ( skeletonChar == CAP_L ) { - // there is no "L" (always be "M") in skeleton, - // but there is "L" in pattern. - // for skeleton "M+", the pattern might be "...L..." - skeletonChar = CAP_M; - } - int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; - int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; - if ( fieldCount == count && inputFieldCount > fieldCount ) { - count = inputFieldCount - fieldCount; - int32_t j; - for ( j = 0; j < count; ++j ) { - adjustedPtn.append(prevCh); - } - } - } -} - -void -DateIntervalFormat::findReplaceInPattern(UnicodeString& targetString, - const UnicodeString& strToReplace, - const UnicodeString& strToReplaceWith) { - int32_t firstQuoteIndex = targetString.indexOf(u'\''); - if (firstQuoteIndex == -1) { - targetString.findAndReplace(strToReplace, strToReplaceWith); - } else { - UnicodeString result; - UnicodeString source = targetString; - - while (firstQuoteIndex >= 0) { - int32_t secondQuoteIndex = source.indexOf(u'\'', firstQuoteIndex + 1); - if (secondQuoteIndex == -1) { - secondQuoteIndex = source.length() - 1; - } - - UnicodeString unquotedText(source, 0, firstQuoteIndex); - UnicodeString quotedText(source, firstQuoteIndex, secondQuoteIndex - firstQuoteIndex + 1); - - unquotedText.findAndReplace(strToReplace, strToReplaceWith); - result += unquotedText; - result += quotedText; - - source.remove(0, secondQuoteIndex + 1); - firstQuoteIndex = source.indexOf(u'\''); - } - source.findAndReplace(strToReplace, strToReplaceWith); - result += source; - targetString = result; - } -} - - - -void -DateIntervalFormat::concatSingleDate2TimeInterval(UnicodeString& format, - const UnicodeString& datePattern, - UCalendarDateFields field, - UErrorCode& status) { - // following should not set wrong status - int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, - status); - if ( U_FAILURE(status) ) { - return; - } - PatternInfo& timeItvPtnInfo = fIntervalPatterns[itvPtnIndex]; - if ( !timeItvPtnInfo.firstPart.isEmpty() ) { - UnicodeString timeIntervalPattern(timeItvPtnInfo.firstPart); - timeIntervalPattern.append(timeItvPtnInfo.secondPart); - UnicodeString combinedPattern; - SimpleFormatter(format, 2, 2, status). - format(timeIntervalPattern, datePattern, combinedPattern, status); - if ( U_FAILURE(status) ) { - return; - } - setIntervalPattern(field, combinedPattern, timeItvPtnInfo.laterDateFirst); - } - // else: fall back - // it should not happen if the interval format defined is valid -} - - - -const UChar -DateIntervalFormat::fgCalendarFieldToPatternLetter[] = -{ - /*GyM*/ CAP_G, LOW_Y, CAP_M, - /*wWd*/ LOW_W, CAP_W, LOW_D, - /*DEF*/ CAP_D, CAP_E, CAP_F, - /*ahH*/ LOW_A, LOW_H, CAP_H, - /*msS*/ LOW_M, LOW_S, CAP_S, // MINUTE, SECOND, MILLISECOND - /*z.Y*/ LOW_Z, SPACE, CAP_Y, // ZONE_OFFSET, DST_OFFSET, YEAR_WOY, - /*eug*/ LOW_E, LOW_U, LOW_G, // DOW_LOCAL, EXTENDED_YEAR, JULIAN_DAY, - /*A..*/ CAP_A, SPACE, SPACE, // MILLISECONDS_IN_DAY, IS_LEAP_MONTH, FIELD_COUNT -}; - - - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************************* +* Copyright (C) 2008-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTITVFMT.CPP +* +******************************************************************************* +*/ + +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/dtitvfmt.h" + +#if !UCONFIG_NO_FORMATTING + +//TODO: put in compilation +//#define DTITVFMT_DEBUG 1 + +#include "unicode/calendar.h" +#include "unicode/dtptngen.h" +#include "unicode/dtitvinf.h" +#include "unicode/simpleformatter.h" +#include "unicode/udisplaycontext.h" +#include "cmemory.h" +#include "cstring.h" +#include "dtitv_impl.h" +#include "mutex.h" +#include "uresimp.h" +#include "formattedval_impl.h" + +#ifdef DTITVFMT_DEBUG +#include +#endif + +U_NAMESPACE_BEGIN + + + +#ifdef DTITVFMT_DEBUG +#define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; } +#endif + + +static const char16_t gDateFormatSkeleton[][11] = { +//yMMMMEEEEd +{LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, CAP_E, CAP_E, CAP_E, CAP_E, LOW_D, 0}, +//yMMMMd +{LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, LOW_D, 0}, +//yMMMd +{LOW_Y, CAP_M, CAP_M, CAP_M, LOW_D, 0}, +//yMd +{LOW_Y, CAP_M, LOW_D, 0} }; + + +static const char gCalendarTag[] = "calendar"; +static const char gGregorianTag[] = "gregorian"; +static const char gDateTimePatternsTag[] = "DateTimePatterns"; + + +// latestFirst: +static const char16_t gLaterFirstPrefix[] = {LOW_L, LOW_A, LOW_T, LOW_E, LOW_S,LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON}; + +// earliestFirst: +static const char16_t gEarlierFirstPrefix[] = {LOW_E, LOW_A, LOW_R, LOW_L, LOW_I, LOW_E, LOW_S, LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON}; + + +class FormattedDateIntervalData : public FormattedValueFieldPositionIteratorImpl { +public: + FormattedDateIntervalData(UErrorCode& status) : FormattedValueFieldPositionIteratorImpl(5, status) {} + virtual ~FormattedDateIntervalData(); +}; + +FormattedDateIntervalData::~FormattedDateIntervalData() = default; + +UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedDateInterval) + + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat) + +// Mutex, protects access to fDateFormat, fFromCalendar and fToCalendar. +// Needed because these data members are modified by const methods of DateIntervalFormat. + +static UMutex gFormatterMutex; + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createInstance(const UnicodeString& skeleton, + UErrorCode& status) { + return createInstance(skeleton, Locale::getDefault(), status); +} + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createInstance(const UnicodeString& skeleton, + const Locale& locale, + UErrorCode& status) { +#ifdef DTITVFMT_DEBUG + char result[1000]; + char result_1[1000]; + char mesg[2000]; + skeleton.extract(0, skeleton.length(), result, "UTF-8"); + UnicodeString pat; + ((SimpleDateFormat*)dtfmt)->toPattern(pat); + pat.extract(0, pat.length(), result_1, "UTF-8"); + snprintf(mesg, sizeof(mesg), "skeleton: %s; pattern: %s\n", result, result_1); + PRINTMESG(mesg) +#endif + + DateIntervalInfo* dtitvinf = new DateIntervalInfo(locale, status); + if (dtitvinf == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + return create(locale, dtitvinf, &skeleton, status); +} + + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createInstance(const UnicodeString& skeleton, + const DateIntervalInfo& dtitvinf, + UErrorCode& status) { + return createInstance(skeleton, Locale::getDefault(), dtitvinf, status); +} + + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::createInstance(const UnicodeString& skeleton, + const Locale& locale, + const DateIntervalInfo& dtitvinf, + UErrorCode& status) { + DateIntervalInfo* ptn = dtitvinf.clone(); + return create(locale, ptn, &skeleton, status); +} + + +DateIntervalFormat::DateIntervalFormat() +: fInfo(nullptr), + fDateFormat(nullptr), + fFromCalendar(nullptr), + fToCalendar(nullptr), + fLocale(Locale::getRoot()), + fDatePattern(nullptr), + fTimePattern(nullptr), + fDateTimeFormat(nullptr), + fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) +{} + + +DateIntervalFormat::DateIntervalFormat(const DateIntervalFormat& itvfmt) +: Format(itvfmt), + fInfo(nullptr), + fDateFormat(nullptr), + fFromCalendar(nullptr), + fToCalendar(nullptr), + fLocale(itvfmt.fLocale), + fDatePattern(nullptr), + fTimePattern(nullptr), + fDateTimeFormat(nullptr), + fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) { + *this = itvfmt; +} + + +DateIntervalFormat& +DateIntervalFormat::operator=(const DateIntervalFormat& itvfmt) { + if ( this != &itvfmt ) { + delete fDateFormat; + delete fInfo; + delete fFromCalendar; + delete fToCalendar; + delete fDatePattern; + delete fTimePattern; + delete fDateTimeFormat; + { + Mutex lock(&gFormatterMutex); + if ( itvfmt.fDateFormat ) { + fDateFormat = itvfmt.fDateFormat->clone(); + } else { + fDateFormat = nullptr; + } + if ( itvfmt.fFromCalendar ) { + fFromCalendar = itvfmt.fFromCalendar->clone(); + } else { + fFromCalendar = nullptr; + } + if ( itvfmt.fToCalendar ) { + fToCalendar = itvfmt.fToCalendar->clone(); + } else { + fToCalendar = nullptr; + } + } + if ( itvfmt.fInfo ) { + fInfo = itvfmt.fInfo->clone(); + } else { + fInfo = nullptr; + } + fSkeleton = itvfmt.fSkeleton; + int8_t i; + for ( i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { + fIntervalPatterns[i] = itvfmt.fIntervalPatterns[i]; + } + fLocale = itvfmt.fLocale; + fDatePattern = (itvfmt.fDatePattern)? itvfmt.fDatePattern->clone(): nullptr; + fTimePattern = (itvfmt.fTimePattern)? itvfmt.fTimePattern->clone(): nullptr; + fDateTimeFormat = (itvfmt.fDateTimeFormat)? itvfmt.fDateTimeFormat->clone(): nullptr; + fCapitalizationContext = itvfmt.fCapitalizationContext; + } + return *this; +} + + +DateIntervalFormat::~DateIntervalFormat() { + delete fInfo; + delete fDateFormat; + delete fFromCalendar; + delete fToCalendar; + delete fDatePattern; + delete fTimePattern; + delete fDateTimeFormat; +} + + +DateIntervalFormat* +DateIntervalFormat::clone() const { + return new DateIntervalFormat(*this); +} + + +bool +DateIntervalFormat::operator==(const Format& other) const { + if (typeid(*this) != typeid(other)) {return false;} + const DateIntervalFormat* fmt = (DateIntervalFormat*)&other; + if (this == fmt) {return true;} + if (!Format::operator==(other)) {return false;} + if ((fInfo != fmt->fInfo) && (fInfo == nullptr || fmt->fInfo == nullptr)) {return false;} + if (fInfo && fmt->fInfo && (*fInfo != *fmt->fInfo )) {return false;} + { + Mutex lock(&gFormatterMutex); + if (fDateFormat != fmt->fDateFormat && (fDateFormat == nullptr || fmt->fDateFormat == nullptr)) {return false;} + if (fDateFormat && fmt->fDateFormat && (*fDateFormat != *fmt->fDateFormat)) {return false;} + } + // note: fFromCalendar and fToCalendar hold no persistent state, and therefore do not participate in operator ==. + // fDateFormat has the primary calendar for the DateIntervalFormat. + if (fSkeleton != fmt->fSkeleton) {return false;} + if (fDatePattern != fmt->fDatePattern && (fDatePattern == nullptr || fmt->fDatePattern == nullptr)) {return false;} + if (fDatePattern && fmt->fDatePattern && (*fDatePattern != *fmt->fDatePattern)) {return false;} + if (fTimePattern != fmt->fTimePattern && (fTimePattern == nullptr || fmt->fTimePattern == nullptr)) {return false;} + if (fTimePattern && fmt->fTimePattern && (*fTimePattern != *fmt->fTimePattern)) {return false;} + if (fDateTimeFormat != fmt->fDateTimeFormat && (fDateTimeFormat == nullptr || fmt->fDateTimeFormat == nullptr)) {return false;} + if (fDateTimeFormat && fmt->fDateTimeFormat && (*fDateTimeFormat != *fmt->fDateTimeFormat)) {return false;} + if (fLocale != fmt->fLocale) {return false;} + + for (int32_t i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { + if (fIntervalPatterns[i].firstPart != fmt->fIntervalPatterns[i].firstPart) {return false;} + if (fIntervalPatterns[i].secondPart != fmt->fIntervalPatterns[i].secondPart ) {return false;} + if (fIntervalPatterns[i].laterDateFirst != fmt->fIntervalPatterns[i].laterDateFirst) {return false;} + } + if (fCapitalizationContext != fmt->fCapitalizationContext) {return false;} + return true; +} + + +UnicodeString& +DateIntervalFormat::format(const Formattable& obj, + UnicodeString& appendTo, + FieldPosition& fieldPosition, + UErrorCode& status) const { + if ( U_FAILURE(status) ) { + return appendTo; + } + + if ( obj.getType() == Formattable::kObject ) { + const UObject* formatObj = obj.getObject(); + const DateInterval* interval = dynamic_cast(formatObj); + if (interval != nullptr) { + return format(interval, appendTo, fieldPosition, status); + } + } + status = U_ILLEGAL_ARGUMENT_ERROR; + return appendTo; +} + + +UnicodeString& +DateIntervalFormat::format(const DateInterval* dtInterval, + UnicodeString& appendTo, + FieldPosition& fieldPosition, + UErrorCode& status) const { + if ( U_FAILURE(status) ) { + return appendTo; + } + if (fDateFormat == nullptr || fInfo == nullptr) { + status = U_INVALID_STATE_ERROR; + return appendTo; + } + + FieldPositionOnlyHandler handler(fieldPosition); + handler.setAcceptFirstOnly(true); + int8_t ignore; + + Mutex lock(&gFormatterMutex); + return formatIntervalImpl(*dtInterval, appendTo, ignore, handler, status); +} + + +FormattedDateInterval DateIntervalFormat::formatToValue( + const DateInterval& dtInterval, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return FormattedDateInterval(status); + } + // LocalPointer only sets OOM status if U_SUCCESS is true. + LocalPointer result(new FormattedDateIntervalData(status), status); + if (U_FAILURE(status)) { + return FormattedDateInterval(status); + } + UnicodeString string; + int8_t firstIndex; + auto handler = result->getHandler(status); + handler.setCategory(UFIELD_CATEGORY_DATE); + { + Mutex lock(&gFormatterMutex); + formatIntervalImpl(dtInterval, string, firstIndex, handler, status); + } + handler.getError(status); + result->appendString(string, status); + if (U_FAILURE(status)) { + return FormattedDateInterval(status); + } + + // Compute the span fields and sort them into place: + if (firstIndex != -1) { + result->addOverlapSpans(UFIELD_CATEGORY_DATE_INTERVAL_SPAN, firstIndex, status); + if (U_FAILURE(status)) { + return FormattedDateInterval(status); + } + result->sort(); + } + + return FormattedDateInterval(result.orphan()); +} + + +UnicodeString& +DateIntervalFormat::format(Calendar& fromCalendar, + Calendar& toCalendar, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& status) const { + FieldPositionOnlyHandler handler(pos); + handler.setAcceptFirstOnly(true); + int8_t ignore; + + Mutex lock(&gFormatterMutex); + return formatImpl(fromCalendar, toCalendar, appendTo, ignore, handler, status); +} + + +FormattedDateInterval DateIntervalFormat::formatToValue( + Calendar& fromCalendar, + Calendar& toCalendar, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return FormattedDateInterval(status); + } + // LocalPointer only sets OOM status if U_SUCCESS is true. + LocalPointer result(new FormattedDateIntervalData(status), status); + if (U_FAILURE(status)) { + return FormattedDateInterval(status); + } + UnicodeString string; + int8_t firstIndex; + auto handler = result->getHandler(status); + handler.setCategory(UFIELD_CATEGORY_DATE); + { + Mutex lock(&gFormatterMutex); + formatImpl(fromCalendar, toCalendar, string, firstIndex, handler, status); + } + handler.getError(status); + result->appendString(string, status); + if (U_FAILURE(status)) { + return FormattedDateInterval(status); + } + + // Compute the span fields and sort them into place: + if (firstIndex != -1) { + result->addOverlapSpans(UFIELD_CATEGORY_DATE_INTERVAL_SPAN, firstIndex, status); + result->sort(); + } + + return FormattedDateInterval(result.orphan()); +} + + +UnicodeString& DateIntervalFormat::formatIntervalImpl( + const DateInterval& dtInterval, + UnicodeString& appendTo, + int8_t& firstIndex, + FieldPositionHandler& fphandler, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return appendTo; + } + if (fFromCalendar == nullptr || fToCalendar == nullptr) { + status = U_INVALID_STATE_ERROR; + return appendTo; + } + fFromCalendar->setTime(dtInterval.getFromDate(), status); + fToCalendar->setTime(dtInterval.getToDate(), status); + return formatImpl(*fFromCalendar, *fToCalendar, appendTo, firstIndex, fphandler, status); +} + + +// The following is only called from within the gFormatterMutex lock +UnicodeString& +DateIntervalFormat::formatImpl(Calendar& fromCalendar, + Calendar& toCalendar, + UnicodeString& appendTo, + int8_t& firstIndex, + FieldPositionHandler& fphandler, + UErrorCode& status) const { + if ( U_FAILURE(status) ) { + return appendTo; + } + + // Initialize firstIndex to -1 (single date, no range) + firstIndex = -1; + + // not support different calendar types and time zones + //if ( fromCalendar.getType() != toCalendar.getType() ) { + if ( !fromCalendar.isEquivalentTo(toCalendar) ) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return appendTo; + } + + // First, find the largest different calendar field. + UCalendarDateFields field = UCAL_FIELD_COUNT; + + if ( fromCalendar.get(UCAL_ERA,status) != toCalendar.get(UCAL_ERA,status)) { + field = UCAL_ERA; + } else if ( fromCalendar.get(UCAL_YEAR, status) != + toCalendar.get(UCAL_YEAR, status) ) { + field = UCAL_YEAR; + } else if ( fromCalendar.get(UCAL_MONTH, status) != + toCalendar.get(UCAL_MONTH, status) ) { + field = UCAL_MONTH; + } else if ( fromCalendar.get(UCAL_DATE, status) != + toCalendar.get(UCAL_DATE, status) ) { + field = UCAL_DATE; + } else if ( fromCalendar.get(UCAL_AM_PM, status) != + toCalendar.get(UCAL_AM_PM, status) ) { + field = UCAL_AM_PM; + } else if ( fromCalendar.get(UCAL_HOUR, status) != + toCalendar.get(UCAL_HOUR, status) ) { + field = UCAL_HOUR; + } else if ( fromCalendar.get(UCAL_MINUTE, status) != + toCalendar.get(UCAL_MINUTE, status) ) { + field = UCAL_MINUTE; + } else if ( fromCalendar.get(UCAL_SECOND, status) != + toCalendar.get(UCAL_SECOND, status) ) { + field = UCAL_SECOND; + } else if ( fromCalendar.get(UCAL_MILLISECOND, status) != + toCalendar.get(UCAL_MILLISECOND, status) ) { + field = UCAL_MILLISECOND; + } + + if ( U_FAILURE(status) ) { + return appendTo; + } + UErrorCode tempStatus = U_ZERO_ERROR; // for setContext, ignored + // Set up fDateFormat to handle the first or only part of the interval + // (override later for any second part). Inside lock, OK to modify fDateFormat. + fDateFormat->setContext(fCapitalizationContext, tempStatus); + + if ( field == UCAL_FIELD_COUNT ) { + /* ignore the millisecond etc. small fields' difference. + * use single date when all the above are the same. + */ + return fDateFormat->_format(fromCalendar, appendTo, fphandler, status); + } + UBool fromToOnSameDay = (field==UCAL_AM_PM || field==UCAL_HOUR || field==UCAL_MINUTE || field==UCAL_SECOND || field==UCAL_MILLISECOND); + + // following call should not set wrong status, + // all the pass-in fields are valid till here + int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, + status); + const PatternInfo& intervalPattern = fIntervalPatterns[itvPtnIndex]; + + if ( intervalPattern.firstPart.isEmpty() && + intervalPattern.secondPart.isEmpty() ) { + if ( fDateFormat->isFieldUnitIgnored(field) ) { + /* the largest different calendar field is small than + * the smallest calendar field in pattern, + * return single date format. + */ + return fDateFormat->_format(fromCalendar, appendTo, fphandler, status); + } + return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, firstIndex, fphandler, status); + } + // If the first part in interval pattern is empty, + // the 2nd part of it saves the full-pattern used in fall-back. + // For a 'real' interval pattern, the first part will never be empty. + if ( intervalPattern.firstPart.isEmpty() ) { + // fall back + UnicodeString originalPattern; + fDateFormat->toPattern(originalPattern); + fDateFormat->applyPattern(intervalPattern.secondPart); + appendTo = fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, firstIndex, fphandler, status); + fDateFormat->applyPattern(originalPattern); + return appendTo; + } + Calendar* firstCal; + Calendar* secondCal; + if ( intervalPattern.laterDateFirst ) { + firstCal = &toCalendar; + secondCal = &fromCalendar; + firstIndex = 1; + } else { + firstCal = &fromCalendar; + secondCal = &toCalendar; + firstIndex = 0; + } + // break the interval pattern into 2 parts, + // first part should not be empty, + UnicodeString originalPattern; + fDateFormat->toPattern(originalPattern); + fDateFormat->applyPattern(intervalPattern.firstPart); + fDateFormat->_format(*firstCal, appendTo, fphandler, status); + + if ( !intervalPattern.secondPart.isEmpty() ) { + fDateFormat->applyPattern(intervalPattern.secondPart); + // No capitalization for second part of interval + tempStatus = U_ZERO_ERROR; + fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus); + fDateFormat->_format(*secondCal, appendTo, fphandler, status); + } + fDateFormat->applyPattern(originalPattern); + return appendTo; +} + + + +void +DateIntervalFormat::parseObject(const UnicodeString& /* source */, + Formattable& /* result */, + ParsePosition& /* parse_pos */) const { + // parseObject(const UnicodeString&, Formattable&, UErrorCode&) const + // will set status as U_INVALID_FORMAT_ERROR if + // parse_pos is still 0 +} + + + + +const DateIntervalInfo* +DateIntervalFormat::getDateIntervalInfo() const { + return fInfo; +} + + +void +DateIntervalFormat::setDateIntervalInfo(const DateIntervalInfo& newItvPattern, + UErrorCode& status) { + delete fInfo; + fInfo = new DateIntervalInfo(newItvPattern); + if (fInfo == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + + // Delete patterns that get reset by initializePattern + delete fDatePattern; + fDatePattern = nullptr; + delete fTimePattern; + fTimePattern = nullptr; + delete fDateTimeFormat; + fDateTimeFormat = nullptr; + + if (fDateFormat) { + initializePattern(status); + } +} + + + +const DateFormat* +DateIntervalFormat::getDateFormat() const { + return fDateFormat; +} + + +void +DateIntervalFormat::adoptTimeZone(TimeZone* zone) +{ + if (fDateFormat != nullptr) { + fDateFormat->adoptTimeZone(zone); + } + // The fDateFormat has the primary calendar for the DateIntervalFormat and has + // ownership of any adopted TimeZone; fFromCalendar and fToCalendar are internal + // work clones of that calendar (and should not also be given ownership of the + // adopted TimeZone). + if (fFromCalendar) { + fFromCalendar->setTimeZone(*zone); + } + if (fToCalendar) { + fToCalendar->setTimeZone(*zone); + } +} + +void +DateIntervalFormat::setTimeZone(const TimeZone& zone) +{ + if (fDateFormat != nullptr) { + fDateFormat->setTimeZone(zone); + } + // The fDateFormat has the primary calendar for the DateIntervalFormat; + // fFromCalendar and fToCalendar are internal work clones of that calendar. + if (fFromCalendar) { + fFromCalendar->setTimeZone(zone); + } + if (fToCalendar) { + fToCalendar->setTimeZone(zone); + } +} + +const TimeZone& +DateIntervalFormat::getTimeZone() const +{ + if (fDateFormat != nullptr) { + Mutex lock(&gFormatterMutex); + return fDateFormat->getTimeZone(); + } + // If fDateFormat is nullptr (unexpected), create default timezone. + return *(TimeZone::createDefault()); +} + +void +DateIntervalFormat::setContext(UDisplayContext value, UErrorCode& status) +{ + if (U_FAILURE(status)) + return; + if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) { + fCapitalizationContext = value; + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + } +} + +UDisplayContext +DateIntervalFormat::getContext(UDisplayContextType type, UErrorCode& status) const +{ + if (U_FAILURE(status)) + return (UDisplayContext)0; + if (type != UDISPCTX_TYPE_CAPITALIZATION) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return (UDisplayContext)0; + } + return fCapitalizationContext; +} + +DateIntervalFormat::DateIntervalFormat(const Locale& locale, + DateIntervalInfo* dtItvInfo, + const UnicodeString* skeleton, + UErrorCode& status) +: fInfo(nullptr), + fDateFormat(nullptr), + fFromCalendar(nullptr), + fToCalendar(nullptr), + fLocale(locale), + fDatePattern(nullptr), + fTimePattern(nullptr), + fDateTimeFormat(nullptr), + fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) +{ + LocalPointer info(dtItvInfo, status); + LocalPointer dtfmt(static_cast( + DateFormat::createInstanceForSkeleton(*skeleton, locale, status)), status); + if (U_FAILURE(status)) { + return; + } + + if ( skeleton ) { + fSkeleton = *skeleton; + } + fInfo = info.orphan(); + fDateFormat = dtfmt.orphan(); + if ( fDateFormat->getCalendar() ) { + fFromCalendar = fDateFormat->getCalendar()->clone(); + fToCalendar = fDateFormat->getCalendar()->clone(); + } + initializePattern(status); +} + +DateIntervalFormat* U_EXPORT2 +DateIntervalFormat::create(const Locale& locale, + DateIntervalInfo* dtitvinf, + const UnicodeString* skeleton, + UErrorCode& status) { + DateIntervalFormat* f = new DateIntervalFormat(locale, dtitvinf, + skeleton, status); + if ( f == nullptr ) { + status = U_MEMORY_ALLOCATION_ERROR; + delete dtitvinf; + } else if ( U_FAILURE(status) ) { + // safe to delete f, although nothing actually is saved + delete f; + f = 0; + } + return f; +} + + + +/** + * Initialize interval patterns locale to this formatter + * + * This code is a bit complicated since + * 1. the interval patterns saved in resource bundle files are interval + * patterns based on date or time only. + * It does not have interval patterns based on both date and time. + * Interval patterns on both date and time are algorithm generated. + * + * For example, it has interval patterns on skeleton "dMy" and "hm", + * but it does not have interval patterns on skeleton "dMyhm". + * + * The rule to genearte interval patterns for both date and time skeleton are + * 1) when the year, month, or day differs, concatenate the two original + * expressions with a separator between, + * For example, interval pattern from "Jan 10, 2007 10:10 am" + * to "Jan 11, 2007 10:10am" is + * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am" + * + * 2) otherwise, present the date followed by the range expression + * for the time. + * For example, interval pattern from "Jan 10, 2007 10:10 am" + * to "Jan 10, 2007 11:10am" is + * "Jan 10, 2007 10:10 am - 11:10am" + * + * 2. even a pattern does not request a certion calendar field, + * the interval pattern needs to include such field if such fields are + * different between 2 dates. + * For example, a pattern/skeleton is "hm", but the interval pattern + * includes year, month, and date when year, month, and date differs. + * + * @param status output param set to success/failure code on exit + * @stable ICU 4.0 + */ +void +DateIntervalFormat::initializePattern(UErrorCode& status) { + if ( U_FAILURE(status) ) { + return; + } + const Locale& locale = fDateFormat->getSmpFmtLocale(); + if ( fSkeleton.isEmpty() ) { + UnicodeString fullPattern; + fDateFormat->toPattern(fullPattern); +#ifdef DTITVFMT_DEBUG + char result[1000]; + char result_1[1000]; + char mesg[2000]; + fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); + snprintf(mesg, sizeof(mesg), "in getBestSkeleton: fSkeleton: %s; \n", result); + PRINTMESG(mesg) +#endif + // fSkeleton is already set by createDateIntervalInstance() + // or by createInstance(UnicodeString skeleton, .... ) + fSkeleton = DateTimePatternGenerator::staticGetSkeleton( + fullPattern, status); + if ( U_FAILURE(status) ) { + return; + } + } + + // initialize the fIntervalPattern ordering + int8_t i; + for ( i = 0; i < DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { + fIntervalPatterns[i].laterDateFirst = fInfo->getDefaultOrder(); + } + + /* Check whether the skeleton is a combination of date and time. + * For the complication reason 1 explained above. + */ + UnicodeString dateSkeleton; + UnicodeString timeSkeleton; + UnicodeString normalizedTimeSkeleton; + UnicodeString normalizedDateSkeleton; + + + /* the difference between time skeleton and normalizedTimeSkeleton are: + * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true) + * 2. (Formerly, 'a' was omitted in normalized time skeleton; this is now handled elsewhere) + * 3. there is only one appearance for 'h' or 'H', 'm','v', 'z' in normalized + * time skeleton + * + * The difference between date skeleton and normalizedDateSkeleton are: + * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton + * 2. 'E' and 'EE' are normalized into 'EEE' + * 3. 'MM' is normalized into 'M' + */ + UnicodeString convertedSkeleton = normalizeHourMetacharacters(fSkeleton); + getDateTimeSkeleton(convertedSkeleton, dateSkeleton, normalizedDateSkeleton, + timeSkeleton, normalizedTimeSkeleton); + +#ifdef DTITVFMT_DEBUG + char result[1000]; + char result_1[1000]; + char mesg[2000]; + fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); + snprintf(mesg, sizeof(mesg), "in getBestSkeleton: fSkeleton: %s; \n", result); + PRINTMESG(mesg) +#endif + + // move this up here since we need it for fallbacks + if ( timeSkeleton.length() > 0 && dateSkeleton.length() > 0 ) { + // Need the Date/Time pattern for concatenation of the date + // with the time interval. + // The date/time pattern ( such as {0} {1} ) is saved in + // calendar, that is why need to get the CalendarData here. + LocalUResourceBundlePointer dateTimePatternsRes(ures_open(nullptr, locale.getBaseName(), &status)); + ures_getByKey(dateTimePatternsRes.getAlias(), gCalendarTag, + dateTimePatternsRes.getAlias(), &status); + ures_getByKeyWithFallback(dateTimePatternsRes.getAlias(), gGregorianTag, + dateTimePatternsRes.getAlias(), &status); + ures_getByKeyWithFallback(dateTimePatternsRes.getAlias(), gDateTimePatternsTag, + dateTimePatternsRes.getAlias(), &status); + + int32_t dateTimeFormatLength; + const char16_t* dateTimeFormat = ures_getStringByIndex( + dateTimePatternsRes.getAlias(), + (int32_t)DateFormat::kDateTime, + &dateTimeFormatLength, &status); + if ( U_SUCCESS(status) && dateTimeFormatLength >= 3 ) { + fDateTimeFormat = new UnicodeString(dateTimeFormat, dateTimeFormatLength); + if (fDateTimeFormat == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + } + + UBool found = setSeparateDateTimePtn(normalizedDateSkeleton, + normalizedTimeSkeleton); + + // for skeletons with seconds, found is false and we enter this block + if ( found == false ) { + // use fallback + // TODO: if user asks "m"(minute), but "d"(day) differ + if ( timeSkeleton.length() != 0 ) { + if ( dateSkeleton.length() == 0 ) { + // prefix with yMd + timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1); + UnicodeString pattern = DateFormat::getBestPattern( + locale, timeSkeleton, status); + if ( U_FAILURE(status) ) { + return; + } + // for fall back interval patterns, + // the first part of the pattern is empty, + // the second part of the pattern is the full-pattern + // should be used in fall-back. + setPatternInfo(UCAL_DATE, nullptr, &pattern, fInfo->getDefaultOrder()); + setPatternInfo(UCAL_MONTH, nullptr, &pattern, fInfo->getDefaultOrder()); + setPatternInfo(UCAL_YEAR, nullptr, &pattern, fInfo->getDefaultOrder()); + + timeSkeleton.insert(0, CAP_G); + pattern = DateFormat::getBestPattern( + locale, timeSkeleton, status); + if ( U_FAILURE(status) ) { + return; + } + setPatternInfo(UCAL_ERA, nullptr, &pattern, fInfo->getDefaultOrder()); + } else { + // TODO: fall back + } + } else { + // TODO: fall back + } + return; + } // end of skeleton not found + // interval patterns for skeleton are found in resource + if ( timeSkeleton.length() == 0 ) { + // done + } else if ( dateSkeleton.length() == 0 ) { + // prefix with yMd + timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1); + UnicodeString pattern = DateFormat::getBestPattern( + locale, timeSkeleton, status); + if ( U_FAILURE(status) ) { + return; + } + // for fall back interval patterns, + // the first part of the pattern is empty, + // the second part of the pattern is the full-pattern + // should be used in fall-back. + setPatternInfo(UCAL_DATE, nullptr, &pattern, fInfo->getDefaultOrder()); + setPatternInfo(UCAL_MONTH, nullptr, &pattern, fInfo->getDefaultOrder()); + setPatternInfo(UCAL_YEAR, nullptr, &pattern, fInfo->getDefaultOrder()); + + timeSkeleton.insert(0, CAP_G); + pattern = DateFormat::getBestPattern( + locale, timeSkeleton, status); + if ( U_FAILURE(status) ) { + return; + } + setPatternInfo(UCAL_ERA, nullptr, &pattern, fInfo->getDefaultOrder()); + } else { + /* if both present, + * 1) when the era, year, month, or day differs, + * concatenate the two original expressions with a separator between, + * 2) otherwise, present the date followed by the + * range expression for the time. + */ + /* + * 1) when the era, year, month, or day differs, + * concatenate the two original expressions with a separator between, + */ + // if field exists, use fall back + UnicodeString skeleton = fSkeleton; + if ( !fieldExistsInSkeleton(UCAL_DATE, dateSkeleton) ) { + // prefix skeleton with 'd' + skeleton.insert(0, LOW_D); + setFallbackPattern(UCAL_DATE, skeleton, status); + } + if ( !fieldExistsInSkeleton(UCAL_MONTH, dateSkeleton) ) { + // then prefix skeleton with 'M' + skeleton.insert(0, CAP_M); + setFallbackPattern(UCAL_MONTH, skeleton, status); + } + if ( !fieldExistsInSkeleton(UCAL_YEAR, dateSkeleton) ) { + // then prefix skeleton with 'y' + skeleton.insert(0, LOW_Y); + setFallbackPattern(UCAL_YEAR, skeleton, status); + } + if ( !fieldExistsInSkeleton(UCAL_ERA, dateSkeleton) ) { + // then prefix skeleton with 'G' + skeleton.insert(0, CAP_G); + setFallbackPattern(UCAL_ERA, skeleton, status); + } + + /* + * 2) otherwise, present the date followed by the + * range expression for the time. + */ + + if ( fDateTimeFormat == nullptr ) { + // earlier failure getting dateTimeFormat + return; + } + + UnicodeString datePattern = DateFormat::getBestPattern( + locale, dateSkeleton, status); + + concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_AM_PM, status); + concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_HOUR, status); + concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_MINUTE, status); + } +} + + + +UnicodeString +DateIntervalFormat::normalizeHourMetacharacters(const UnicodeString& skeleton) const { + UnicodeString result = skeleton; + + char16_t hourMetachar = u'\0'; + char16_t dayPeriodChar = u'\0'; + int32_t hourFieldStart = 0; + int32_t hourFieldLength = 0; + int32_t dayPeriodStart = 0; + int32_t dayPeriodLength = 0; + for (int32_t i = 0; i < result.length(); i++) { + char16_t c = result[i]; + if (c == LOW_J || c == CAP_J || c == CAP_C || c == LOW_H || c == CAP_H || c == LOW_K || c == CAP_K) { + if (hourMetachar == u'\0') { + hourMetachar = c; + hourFieldStart = i; + } + ++hourFieldLength; + } else if (c == LOW_A || c == LOW_B || c == CAP_B) { + if (dayPeriodChar == u'\0') { + dayPeriodChar = c; + dayPeriodStart = i; + } + ++dayPeriodLength; + } else { + if (hourMetachar != u'\0' && dayPeriodChar != u'\0') { + break; + } + } + } + + if (hourMetachar != u'\0') { + UErrorCode err = U_ZERO_ERROR; + char16_t hourChar = CAP_H; + UnicodeString convertedPattern = DateFormat::getBestPattern(fLocale, UnicodeString(hourMetachar), err); + + if (U_SUCCESS(err)) { + // strip literal text from the pattern (so literal characters don't get mistaken for pattern + // characters-- such as the 'h' in 'Uhr' in Germam) + int32_t firstQuotePos; + while ((firstQuotePos = convertedPattern.indexOf(u'\'')) != -1) { + int32_t secondQuotePos = convertedPattern.indexOf(u'\'', firstQuotePos + 1); + if (secondQuotePos == -1) { + secondQuotePos = firstQuotePos; + } + convertedPattern.replace(firstQuotePos, (secondQuotePos - firstQuotePos) + 1, UnicodeString()); + } + + if (convertedPattern.indexOf(LOW_H) != -1) { + hourChar = LOW_H; + } else if (convertedPattern.indexOf(CAP_K) != -1) { + hourChar = CAP_K; + } else if (convertedPattern.indexOf(LOW_K) != -1) { + hourChar = LOW_K; + } + + if (convertedPattern.indexOf(LOW_B) != -1) { + dayPeriodChar = LOW_B; + } else if (convertedPattern.indexOf(CAP_B) != -1) { + dayPeriodChar = CAP_B; + } else if (dayPeriodChar == u'\0') { + dayPeriodChar = LOW_A; + } + } + + UnicodeString hourAndDayPeriod(hourChar); + if (hourChar != CAP_H && hourChar != LOW_K) { + int32_t newDayPeriodLength = 0; + if (dayPeriodLength >= 5 || hourFieldLength >= 5) { + newDayPeriodLength = 5; + } else if (dayPeriodLength >= 3 || hourFieldLength >= 3) { + newDayPeriodLength = 3; + } else { + newDayPeriodLength = 1; + } + for (int32_t i = 0; i < newDayPeriodLength; i++) { + hourAndDayPeriod.append(dayPeriodChar); + } + } + result.replace(hourFieldStart, hourFieldLength, hourAndDayPeriod); + if (dayPeriodStart > hourFieldStart) { + // before deleting the original day period field, adjust its position in case + // we just changed the size of the hour field (and new day period field) + dayPeriodStart += hourAndDayPeriod.length() - hourFieldLength; + } + result.remove(dayPeriodStart, dayPeriodLength); + } + return result; +} + + +void U_EXPORT2 +DateIntervalFormat::getDateTimeSkeleton(const UnicodeString& skeleton, + UnicodeString& dateSkeleton, + UnicodeString& normalizedDateSkeleton, + UnicodeString& timeSkeleton, + UnicodeString& normalizedTimeSkeleton) { + // dateSkeleton follows the sequence of y*M*E*d* + // timeSkeleton follows the sequence of hm*[v|z]? + int32_t ECount = 0; + int32_t dCount = 0; + int32_t MCount = 0; + int32_t yCount = 0; + int32_t mCount = 0; + int32_t vCount = 0; + int32_t zCount = 0; + char16_t hourChar = u'\0'; + int32_t i; + + for (i = 0; i < skeleton.length(); ++i) { + char16_t ch = skeleton[i]; + switch ( ch ) { + case CAP_E: + dateSkeleton.append(ch); + ++ECount; + break; + case LOW_D: + dateSkeleton.append(ch); + ++dCount; + break; + case CAP_M: + dateSkeleton.append(ch); + ++MCount; + break; + case LOW_Y: + dateSkeleton.append(ch); + ++yCount; + break; + case CAP_G: + case CAP_Y: + case LOW_U: + case CAP_Q: + case LOW_Q: + case CAP_L: + case LOW_L: + case CAP_W: + case LOW_W: + case CAP_D: + case CAP_F: + case LOW_G: + case LOW_E: + case LOW_C: + case CAP_U: + case LOW_R: + normalizedDateSkeleton.append(ch); + dateSkeleton.append(ch); + break; + case LOW_H: + case CAP_H: + case LOW_K: + case CAP_K: + timeSkeleton.append(ch); + if (hourChar == u'\0') { + hourChar = ch; + } + break; + case LOW_M: + timeSkeleton.append(ch); + ++mCount; + break; + case LOW_Z: + ++zCount; + timeSkeleton.append(ch); + break; + case LOW_V: + ++vCount; + timeSkeleton.append(ch); + break; + case LOW_A: + case CAP_V: + case CAP_Z: + case LOW_J: + case LOW_S: + case CAP_S: + case CAP_A: + case LOW_B: + case CAP_B: + timeSkeleton.append(ch); + normalizedTimeSkeleton.append(ch); + break; + } + } + + /* generate normalized form for date*/ + if ( yCount != 0 ) { + for (i = 0; i < yCount; ++i) { + normalizedDateSkeleton.append(LOW_Y); + } + } + if ( MCount != 0 ) { + if ( MCount < 3 ) { + normalizedDateSkeleton.append(CAP_M); + } else { + for ( int32_t j = 0; j < MCount && j < MAX_M_COUNT; ++j) { + normalizedDateSkeleton.append(CAP_M); + } + } + } + if ( ECount != 0 ) { + if ( ECount <= 3 ) { + normalizedDateSkeleton.append(CAP_E); + } else { + for ( int32_t j = 0; j < ECount && j < MAX_E_COUNT; ++j ) { + normalizedDateSkeleton.append(CAP_E); + } + } + } + if ( dCount != 0 ) { + normalizedDateSkeleton.append(LOW_D); + } + + /* generate normalized form for time */ + if ( hourChar != u'\0' ) { + normalizedTimeSkeleton.append(hourChar); + } + if ( mCount != 0 ) { + normalizedTimeSkeleton.append(LOW_M); + } + if ( zCount != 0 ) { + normalizedTimeSkeleton.append(LOW_Z); + } + if ( vCount != 0 ) { + normalizedTimeSkeleton.append(LOW_V); + } +} + + +/** + * Generate date or time interval pattern from resource, + * and set them into the interval pattern locale to this formatter. + * + * It needs to handle the following: + * 1. need to adjust field width. + * For example, the interval patterns saved in DateIntervalInfo + * includes "dMMMy", but not "dMMMMy". + * Need to get interval patterns for dMMMMy from dMMMy. + * Another example, the interval patterns saved in DateIntervalInfo + * includes "hmv", but not "hmz". + * Need to get interval patterns for "hmz' from 'hmv' + * + * 2. there might be no pattern for 'y' differ for skeleton "Md", + * in order to get interval patterns for 'y' differ, + * need to look for it from skeleton 'yMd' + * + * @param dateSkeleton normalized date skeleton + * @param timeSkeleton normalized time skeleton + * @return whether the resource is found for the skeleton. + * true if interval pattern found for the skeleton, + * false otherwise. + * @stable ICU 4.0 + */ +UBool +DateIntervalFormat::setSeparateDateTimePtn( + const UnicodeString& dateSkeleton, + const UnicodeString& timeSkeleton) { + const UnicodeString* skeleton; + // if both date and time skeleton present, + // the final interval pattern might include time interval patterns + // ( when, am_pm, hour, minute differ ), + // but not date interval patterns ( when year, month, day differ ). + // For year/month/day differ, it falls back to fall-back pattern. + if ( timeSkeleton.length() != 0 ) { + skeleton = &timeSkeleton; + } else { + skeleton = &dateSkeleton; + } + + /* interval patterns for skeleton "dMMMy" (but not "dMMMMy") + * are defined in resource, + * interval patterns for skeleton "dMMMMy" are calculated by + * 1. get the best match skeleton for "dMMMMy", which is "dMMMy" + * 2. get the interval patterns for "dMMMy", + * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy" + * getBestSkeleton() is step 1. + */ + // best skeleton, and the difference information + int8_t differenceInfo = 0; + const UnicodeString* bestSkeleton = fInfo->getBestSkeleton(*skeleton, + differenceInfo); + /* best skeleton could be nullptr. + For example: in "ca" resource file, + interval format is defined as following + intervalFormats{ + fallback{"{0} - {1}"} + } + there is no skeletons/interval patterns defined, + and the best skeleton match could be nullptr + */ + if ( bestSkeleton == nullptr ) { + return false; + } + + // Set patterns for fallback use, need to do this + // before returning if differenceInfo == -1 + UErrorCode status; + if ( dateSkeleton.length() != 0) { + status = U_ZERO_ERROR; + fDatePattern = new UnicodeString(DateFormat::getBestPattern( + fLocale, dateSkeleton, status)); + // no way to report OOM. :( + } + if ( timeSkeleton.length() != 0) { + status = U_ZERO_ERROR; + fTimePattern = new UnicodeString(DateFormat::getBestPattern( + fLocale, timeSkeleton, status)); + // no way to report OOM. :( + } + + // difference: + // 0 means the best matched skeleton is the same as input skeleton + // 1 means the fields are the same, but field width are different + // 2 means the only difference between fields are v/z, + // -1 means there are other fields difference + // (this will happen, for instance, if the supplied skeleton has seconds, + // but no skeletons in the intervalFormats data do) + if ( differenceInfo == -1 ) { + // skeleton has different fields, not only v/z difference + return false; + } + + if ( timeSkeleton.length() == 0 ) { + UnicodeString extendedSkeleton; + UnicodeString extendedBestSkeleton; + // only has date skeleton + setIntervalPattern(UCAL_DATE, skeleton, bestSkeleton, differenceInfo, + &extendedSkeleton, &extendedBestSkeleton); + + UBool extended = setIntervalPattern(UCAL_MONTH, skeleton, bestSkeleton, + differenceInfo, + &extendedSkeleton, &extendedBestSkeleton); + + if ( extended ) { + bestSkeleton = &extendedBestSkeleton; + skeleton = &extendedSkeleton; + } + setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo, + &extendedSkeleton, &extendedBestSkeleton); + setIntervalPattern(UCAL_ERA, skeleton, bestSkeleton, differenceInfo, + &extendedSkeleton, &extendedBestSkeleton); + } else { + setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo); + setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo); + setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo); + } + return true; +} + + + +void +DateIntervalFormat::setFallbackPattern(UCalendarDateFields field, + const UnicodeString& skeleton, + UErrorCode& status) { + if ( U_FAILURE(status) ) { + return; + } + UnicodeString pattern = DateFormat::getBestPattern( + fLocale, skeleton, status); + if ( U_FAILURE(status) ) { + return; + } + setPatternInfo(field, nullptr, &pattern, fInfo->getDefaultOrder()); +} + + + + +void +DateIntervalFormat::setPatternInfo(UCalendarDateFields field, + const UnicodeString* firstPart, + const UnicodeString* secondPart, + UBool laterDateFirst) { + // for fall back interval patterns, + // the first part of the pattern is empty, + // the second part of the pattern is the full-pattern + // should be used in fall-back. + UErrorCode status = U_ZERO_ERROR; + // following should not set any wrong status. + int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, + status); + if ( U_FAILURE(status) ) { + return; + } + PatternInfo& ptn = fIntervalPatterns[itvPtnIndex]; + if ( firstPart ) { + ptn.firstPart = *firstPart; + } + if ( secondPart ) { + ptn.secondPart = *secondPart; + } + ptn.laterDateFirst = laterDateFirst; +} + +void +DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, + const UnicodeString& intervalPattern) { + UBool order = fInfo->getDefaultOrder(); + setIntervalPattern(field, intervalPattern, order); +} + + +void +DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, + const UnicodeString& intervalPattern, + UBool laterDateFirst) { + const UnicodeString* pattern = &intervalPattern; + UBool order = laterDateFirst; + // check for "latestFirst:" or "earliestFirst:" prefix + int8_t prefixLength = UPRV_LENGTHOF(gLaterFirstPrefix); + int8_t earliestFirstLength = UPRV_LENGTHOF(gEarlierFirstPrefix); + UnicodeString realPattern; + if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) { + order = true; + intervalPattern.extract(prefixLength, + intervalPattern.length() - prefixLength, + realPattern); + pattern = &realPattern; + } else if ( intervalPattern.startsWith(gEarlierFirstPrefix, + earliestFirstLength) ) { + order = false; + intervalPattern.extract(earliestFirstLength, + intervalPattern.length() - earliestFirstLength, + realPattern); + pattern = &realPattern; + } + + int32_t splitPoint = splitPatternInto2Part(*pattern); + + UnicodeString firstPart; + UnicodeString secondPart; + pattern->extract(0, splitPoint, firstPart); + if ( splitPoint < pattern->length() ) { + pattern->extract(splitPoint, pattern->length()-splitPoint, secondPart); + } + setPatternInfo(field, &firstPart, &secondPart, order); +} + + + + +/** + * Generate interval pattern from existing resource + * + * It not only save the interval patterns, + * but also return the extended skeleton and its best match skeleton. + * + * @param field largest different calendar field + * @param skeleton skeleton + * @param bestSkeleton the best match skeleton which has interval pattern + * defined in resource + * @param differenceInfo the difference between skeleton and best skeleton + * 0 means the best matched skeleton is the same as input skeleton + * 1 means the fields are the same, but field width are different + * 2 means the only difference between fields are v/z, + * -1 means there are other fields difference + * + * @param extendedSkeleton extended skeleton + * @param extendedBestSkeleton extended best match skeleton + * @return whether the interval pattern is found + * through extending skeleton or not. + * true if interval pattern is found by + * extending skeleton, false otherwise. + * @stable ICU 4.0 + */ +UBool +DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, + const UnicodeString* skeleton, + const UnicodeString* bestSkeleton, + int8_t differenceInfo, + UnicodeString* extendedSkeleton, + UnicodeString* extendedBestSkeleton) { + UErrorCode status = U_ZERO_ERROR; + // following getIntervalPattern() should not generate error status + UnicodeString pattern; + fInfo->getIntervalPattern(*bestSkeleton, field, pattern, status); + if ( pattern.isEmpty() ) { + // single date + if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton, field) ) { + // do nothing, format will handle it + return false; + } + + // for 24 hour system, interval patterns in resource file + // might not include pattern when am_pm differ, + // which should be the same as hour differ. + // add it here for simplicity + if ( field == UCAL_AM_PM ) { + fInfo->getIntervalPattern(*bestSkeleton, UCAL_HOUR, pattern,status); + if ( !pattern.isEmpty() ) { + UBool suppressDayPeriodField = fSkeleton.indexOf(CAP_J) != -1; + UnicodeString adjustIntervalPattern; + adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo, + suppressDayPeriodField, adjustIntervalPattern); + setIntervalPattern(field, adjustIntervalPattern); + } + return false; + } + // else, looking for pattern when 'y' differ for 'dMMMM' skeleton, + // first, get best match pattern "MMMd", + // since there is no pattern for 'y' differs for skeleton 'MMMd', + // need to look for it from skeleton 'yMMMd', + // if found, adjust field width in interval pattern from + // "MMM" to "MMMM". + char16_t fieldLetter = fgCalendarFieldToPatternLetter[field]; + if ( extendedSkeleton ) { + *extendedSkeleton = *skeleton; + *extendedBestSkeleton = *bestSkeleton; + extendedSkeleton->insert(0, fieldLetter); + extendedBestSkeleton->insert(0, fieldLetter); + // for example, looking for patterns when 'y' differ for + // skeleton "MMMM". + fInfo->getIntervalPattern(*extendedBestSkeleton,field,pattern,status); + if ( pattern.isEmpty() && differenceInfo == 0 ) { + // if there is no skeleton "yMMMM" defined, + // look for the best match skeleton, for example: "yMMM" + const UnicodeString* tmpBest = fInfo->getBestSkeleton( + *extendedBestSkeleton, differenceInfo); + if ( tmpBest != 0 && differenceInfo != -1 ) { + fInfo->getIntervalPattern(*tmpBest, field, pattern, status); + bestSkeleton = tmpBest; + } + } + } + } + if ( !pattern.isEmpty() ) { + UBool suppressDayPeriodField = fSkeleton.indexOf(CAP_J) != -1; + if ( differenceInfo != 0 || suppressDayPeriodField) { + UnicodeString adjustIntervalPattern; + adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo, + suppressDayPeriodField, adjustIntervalPattern); + setIntervalPattern(field, adjustIntervalPattern); + } else { + setIntervalPattern(field, pattern); + } + if ( extendedSkeleton && !extendedSkeleton->isEmpty() ) { + return true; + } + } + return false; +} + + + +int32_t U_EXPORT2 +DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern) { + UBool inQuote = false; + char16_t prevCh = 0; + int32_t count = 0; + + /* repeatedPattern used to record whether a pattern has already seen. + It is a pattern applies to first calendar if it is first time seen, + otherwise, it is a pattern applies to the second calendar + */ + UBool patternRepeated[] = + { + // A B C D E F G H I J K L M N O + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // P Q R S T U V W X Y Z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // a b c d e f g h i j k l m n o + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // p q r s t u v w x y z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + int8_t PATTERN_CHAR_BASE = 0x41; + + /* loop through the pattern string character by character looking for + * the first repeated pattern letter, which breaks the interval pattern + * into 2 parts. + */ + int32_t i; + UBool foundRepetition = false; + for (i = 0; i < intervalPattern.length(); ++i) { + char16_t ch = intervalPattern.charAt(i); + + if (ch != prevCh && count > 0) { + // check the repeativeness of pattern letter + UBool repeated = patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)]; + if ( repeated == false ) { + patternRepeated[prevCh - PATTERN_CHAR_BASE] = true; + } else { + foundRepetition = true; + break; + } + count = 0; + } + if (ch == 0x0027 /*'*/) { + // Consecutive single quotes are a single quote literal, + // either outside of quotes or between quotes + if ((i+1) < intervalPattern.length() && + intervalPattern.charAt(i+1) == 0x0027 /*'*/) { + ++i; + } else { + inQuote = ! inQuote; + } + } + else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) + || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { + // ch is a date-time pattern character + prevCh = ch; + ++count; + } + } + // check last pattern char, distinguish + // "dd MM" ( no repetition ), + // "d-d"(last char repeated ), and + // "d-d MM" ( repetition found ) + if ( count > 0 && foundRepetition == false ) { + if ( patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)] == false ) { + count = 0; + } + } + return (i - count); +} + +// The following is only called from fallbackFormat, i.e. within the gFormatterMutex lock +void DateIntervalFormat::fallbackFormatRange( + Calendar& fromCalendar, + Calendar& toCalendar, + UnicodeString& appendTo, + int8_t& firstIndex, + FieldPositionHandler& fphandler, + UErrorCode& status) const { + UnicodeString fallbackPattern; + fInfo->getFallbackIntervalPattern(fallbackPattern); + SimpleFormatter sf(fallbackPattern, 2, 2, status); + if (U_FAILURE(status)) { + return; + } + int32_t offsets[2]; + UnicodeString patternBody = sf.getTextWithNoArguments(offsets, 2); + + UErrorCode tempStatus = U_ZERO_ERROR; // for setContext, ignored + // TODO(ICU-20406): Use SimpleFormatter Iterator interface when available. + if (offsets[0] < offsets[1]) { + firstIndex = 0; + appendTo.append(patternBody.tempSubStringBetween(0, offsets[0])); + fDateFormat->_format(fromCalendar, appendTo, fphandler, status); + appendTo.append(patternBody.tempSubStringBetween(offsets[0], offsets[1])); + // No capitalization for second part of interval + fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus); + fDateFormat->_format(toCalendar, appendTo, fphandler, status); + appendTo.append(patternBody.tempSubStringBetween(offsets[1])); + } else { + firstIndex = 1; + appendTo.append(patternBody.tempSubStringBetween(0, offsets[1])); + fDateFormat->_format(toCalendar, appendTo, fphandler, status); + appendTo.append(patternBody.tempSubStringBetween(offsets[1], offsets[0])); + // No capitalization for second part of interval + fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus); + fDateFormat->_format(fromCalendar, appendTo, fphandler, status); + appendTo.append(patternBody.tempSubStringBetween(offsets[0])); + } +} + +// The following is only called from formatImpl, i.e. within the gFormatterMutex lock +UnicodeString& +DateIntervalFormat::fallbackFormat(Calendar& fromCalendar, + Calendar& toCalendar, + UBool fromToOnSameDay, // new + UnicodeString& appendTo, + int8_t& firstIndex, + FieldPositionHandler& fphandler, + UErrorCode& status) const { + if ( U_FAILURE(status) ) { + return appendTo; + } + + UBool formatDatePlusTimeRange = (fromToOnSameDay && fDatePattern && fTimePattern); + if (formatDatePlusTimeRange) { + SimpleFormatter sf(*fDateTimeFormat, 2, 2, status); + if (U_FAILURE(status)) { + return appendTo; + } + int32_t offsets[2]; + UnicodeString patternBody = sf.getTextWithNoArguments(offsets, 2); + + UnicodeString fullPattern; // for saving the pattern in fDateFormat + fDateFormat->toPattern(fullPattern); // save current pattern, restore later + + UErrorCode tempStatus = U_ZERO_ERROR; // for setContext, ignored + // {0} is time range + // {1} is single date portion + // TODO(ICU-20406): Use SimpleFormatter Iterator interface when available. + if (offsets[0] < offsets[1]) { + appendTo.append(patternBody.tempSubStringBetween(0, offsets[0])); + fDateFormat->applyPattern(*fTimePattern); + fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status); + appendTo.append(patternBody.tempSubStringBetween(offsets[0], offsets[1])); + fDateFormat->applyPattern(*fDatePattern); + // No capitalization for second portion + fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus); + fDateFormat->_format(fromCalendar, appendTo, fphandler, status); + appendTo.append(patternBody.tempSubStringBetween(offsets[1])); + } else { + appendTo.append(patternBody.tempSubStringBetween(0, offsets[1])); + fDateFormat->applyPattern(*fDatePattern); + fDateFormat->_format(fromCalendar, appendTo, fphandler, status); + appendTo.append(patternBody.tempSubStringBetween(offsets[1], offsets[0])); + fDateFormat->applyPattern(*fTimePattern); + // No capitalization for second portion + fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus); + fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status); + appendTo.append(patternBody.tempSubStringBetween(offsets[0])); + } + + // restore full pattern + fDateFormat->applyPattern(fullPattern); + } else { + fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status); + } + return appendTo; +} + + + + +UBool U_EXPORT2 +DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field, + const UnicodeString& skeleton) +{ + const char16_t fieldChar = fgCalendarFieldToPatternLetter[field]; + return ( (skeleton.indexOf(fieldChar) == -1)?false:true ) ; +} + + + +void U_EXPORT2 +DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton, + const UnicodeString& bestMatchSkeleton, + const UnicodeString& bestIntervalPattern, + int8_t differenceInfo, + UBool suppressDayPeriodField, + UnicodeString& adjustedPtn) { + adjustedPtn = bestIntervalPattern; + int32_t inputSkeletonFieldWidth[] = + { + // A B C D E F G H I J K L M N O + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // P Q R S T U V W X Y Z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // a b c d e f g h i j k l m n o + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // p q r s t u v w x y z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + int32_t bestMatchSkeletonFieldWidth[] = + { + // A B C D E F G H I J K L M N O + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // P Q R S T U V W X Y Z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // a b c d e f g h i j k l m n o + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // p q r s t u v w x y z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + const int8_t PATTERN_CHAR_BASE = 0x41; + + DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth); + DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth); + if (suppressDayPeriodField) { + // remove the 'a' and any NBSP/NNBSP on one side of it + findReplaceInPattern(adjustedPtn, UnicodeString(u"\u00A0a",-1), UnicodeString()); + findReplaceInPattern(adjustedPtn, UnicodeString(u"\u202Fa",-1), UnicodeString()); + findReplaceInPattern(adjustedPtn, UnicodeString(u"a\u00A0",-1), UnicodeString()); + findReplaceInPattern(adjustedPtn, UnicodeString(u"a\u202F",-1), UnicodeString()); + findReplaceInPattern(adjustedPtn, UnicodeString(LOW_A), UnicodeString()); + // adjust interior double spaces, remove exterior whitespace + findReplaceInPattern(adjustedPtn, UnicodeString(" "), UnicodeString(" ")); + adjustedPtn.trim(); + } + if ( differenceInfo == 2 ) { + if (inputSkeleton.indexOf(LOW_Z) != -1) { + findReplaceInPattern(adjustedPtn, UnicodeString(LOW_V), UnicodeString(LOW_Z)); + } + if (inputSkeleton.indexOf(CAP_K) != -1) { + findReplaceInPattern(adjustedPtn, UnicodeString(LOW_H), UnicodeString(CAP_K)); + } + if (inputSkeleton.indexOf(LOW_K) != -1) { + findReplaceInPattern(adjustedPtn, UnicodeString(CAP_H), UnicodeString(LOW_K)); + } + if (inputSkeleton.indexOf(LOW_B) != -1) { + findReplaceInPattern(adjustedPtn, UnicodeString(LOW_A), UnicodeString(LOW_B)); + } + } + if (adjustedPtn.indexOf(LOW_A) != -1 && bestMatchSkeletonFieldWidth[LOW_A - PATTERN_CHAR_BASE] == 0) { + bestMatchSkeletonFieldWidth[LOW_A - PATTERN_CHAR_BASE] = 1; + } + if (adjustedPtn.indexOf(LOW_B) != -1 && bestMatchSkeletonFieldWidth[LOW_B - PATTERN_CHAR_BASE] == 0) { + bestMatchSkeletonFieldWidth[LOW_B - PATTERN_CHAR_BASE] = 1; + } + + UBool inQuote = false; + char16_t prevCh = 0; + int32_t count = 0; + + // loop through the pattern string character by character + int32_t adjustedPtnLength = adjustedPtn.length(); + int32_t i; + for (i = 0; i < adjustedPtnLength; ++i) { + char16_t ch = adjustedPtn.charAt(i); + if (ch != prevCh && count > 0) { + // check the repeativeness of pattern letter + char16_t skeletonChar = prevCh; + if ( skeletonChar == CAP_L ) { + // there is no "L" (always be "M") in skeleton, + // but there is "L" in pattern. + // for skeleton "M+", the pattern might be "...L..." + skeletonChar = CAP_M; + } + int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; + int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; + if ( fieldCount == count && inputFieldCount > fieldCount ) { + count = inputFieldCount - fieldCount; + int32_t j; + for ( j = 0; j < count; ++j ) { + adjustedPtn.insert(i, prevCh); + } + i += count; + adjustedPtnLength += count; + } + count = 0; + } + if (ch == 0x0027 /*'*/) { + // Consecutive single quotes are a single quote literal, + // either outside of quotes or between quotes + if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == 0x0027 /* ' */) { + ++i; + } else { + inQuote = ! inQuote; + } + } + else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) + || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { + // ch is a date-time pattern character + prevCh = ch; + ++count; + } + } + if ( count > 0 ) { + // last item + // check the repeativeness of pattern letter + char16_t skeletonChar = prevCh; + if ( skeletonChar == CAP_L ) { + // there is no "L" (always be "M") in skeleton, + // but there is "L" in pattern. + // for skeleton "M+", the pattern might be "...L..." + skeletonChar = CAP_M; + } + int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; + int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; + if ( fieldCount == count && inputFieldCount > fieldCount ) { + count = inputFieldCount - fieldCount; + int32_t j; + for ( j = 0; j < count; ++j ) { + adjustedPtn.append(prevCh); + } + } + } +} + +void +DateIntervalFormat::findReplaceInPattern(UnicodeString& targetString, + const UnicodeString& strToReplace, + const UnicodeString& strToReplaceWith) { + int32_t firstQuoteIndex = targetString.indexOf(u'\''); + if (firstQuoteIndex == -1) { + targetString.findAndReplace(strToReplace, strToReplaceWith); + } else { + UnicodeString result; + UnicodeString source = targetString; + + while (firstQuoteIndex >= 0) { + int32_t secondQuoteIndex = source.indexOf(u'\'', firstQuoteIndex + 1); + if (secondQuoteIndex == -1) { + secondQuoteIndex = source.length() - 1; + } + + UnicodeString unquotedText(source, 0, firstQuoteIndex); + UnicodeString quotedText(source, firstQuoteIndex, secondQuoteIndex - firstQuoteIndex + 1); + + unquotedText.findAndReplace(strToReplace, strToReplaceWith); + result += unquotedText; + result += quotedText; + + source.remove(0, secondQuoteIndex + 1); + firstQuoteIndex = source.indexOf(u'\''); + } + source.findAndReplace(strToReplace, strToReplaceWith); + result += source; + targetString = result; + } +} + + + +void +DateIntervalFormat::concatSingleDate2TimeInterval(UnicodeString& format, + const UnicodeString& datePattern, + UCalendarDateFields field, + UErrorCode& status) { + // following should not set wrong status + int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, + status); + if ( U_FAILURE(status) ) { + return; + } + PatternInfo& timeItvPtnInfo = fIntervalPatterns[itvPtnIndex]; + if ( !timeItvPtnInfo.firstPart.isEmpty() ) { + UnicodeString timeIntervalPattern(timeItvPtnInfo.firstPart); + timeIntervalPattern.append(timeItvPtnInfo.secondPart); + UnicodeString combinedPattern; + SimpleFormatter(format, 2, 2, status). + format(timeIntervalPattern, datePattern, combinedPattern, status); + if ( U_FAILURE(status) ) { + return; + } + setIntervalPattern(field, combinedPattern, timeItvPtnInfo.laterDateFirst); + } + // else: fall back + // it should not happen if the interval format defined is valid +} + + + +const char16_t +DateIntervalFormat::fgCalendarFieldToPatternLetter[] = +{ + /*GyM*/ CAP_G, LOW_Y, CAP_M, + /*wWd*/ LOW_W, CAP_W, LOW_D, + /*DEF*/ CAP_D, CAP_E, CAP_F, + /*ahH*/ LOW_A, LOW_H, CAP_H, + /*msS*/ LOW_M, LOW_S, CAP_S, // MINUTE, SECOND, MILLISECOND + /*z.Y*/ LOW_Z, SPACE, CAP_Y, // ZONE_OFFSET, DST_OFFSET, YEAR_WOY, + /*eug*/ LOW_E, LOW_U, LOW_G, // DOW_LOCAL, EXTENDED_YEAR, JULIAN_DAY, + /*A..*/ CAP_A, SPACE, SPACE, // MILLISECONDS_IN_DAY, IS_LEAP_MONTH, FIELD_COUNT +}; + + + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/i18n/dtitvinf.cpp b/deps/icu-small/source/i18n/dtitvinf.cpp index f5fb86ce581206..12e16963083eae 100644 --- a/deps/icu-small/source/i18n/dtitvinf.cpp +++ b/deps/icu-small/source/i18n/dtitvinf.cpp @@ -1,814 +1,814 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/******************************************************************************* -* Copyright (C) 2008-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File DTITVINF.CPP -* -******************************************************************************* -*/ - -#include "unicode/dtitvinf.h" - - -#if !UCONFIG_NO_FORMATTING - -//TODO: define it in compiler time -//#define DTITVINF_DEBUG 1 - - -#ifdef DTITVINF_DEBUG -#include -#endif - -#include "cmemory.h" -#include "cstring.h" -#include "unicode/msgfmt.h" -#include "unicode/uloc.h" -#include "unicode/ures.h" -#include "dtitv_impl.h" -#include "charstr.h" -#include "hash.h" -#include "gregoimp.h" -#include "uresimp.h" -#include "hash.h" -#include "gregoimp.h" -#include "uresimp.h" - - -U_NAMESPACE_BEGIN - - -#ifdef DTITVINF_DEBUG -#define PRINTMESG(msg) UPRV_BLOCK_MACRO_BEGIN { \ - std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; \ -} UPRV_BLOCK_MACRO_END -#endif - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo) - -static const char gCalendarTag[]="calendar"; -static const char gGregorianTag[]="gregorian"; -static const char gIntervalDateTimePatternTag[]="intervalFormats"; -static const char gFallbackPatternTag[]="fallback"; - -// {0} -static const UChar gFirstPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET}; -// {1} -static const UChar gSecondPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET}; - -// default fall-back -static const UChar gDefaultFallbackPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, EN_DASH, SPACE, LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET, 0}; - -DateIntervalInfo::DateIntervalInfo(UErrorCode& status) -: fFallbackIntervalPattern(gDefaultFallbackPattern), - fFirstDateInPtnIsLaterDate(false), - fIntervalPatterns(nullptr) -{ - fIntervalPatterns = initHash(status); -} - - - -DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status) -: fFallbackIntervalPattern(gDefaultFallbackPattern), - fFirstDateInPtnIsLaterDate(false), - fIntervalPatterns(nullptr) -{ - initializeData(locale, status); -} - - - -void -DateIntervalInfo::setIntervalPattern(const UnicodeString& skeleton, - UCalendarDateFields lrgDiffCalUnit, - const UnicodeString& intervalPattern, - UErrorCode& status) { - - if ( lrgDiffCalUnit == UCAL_HOUR_OF_DAY ) { - setIntervalPatternInternally(skeleton, UCAL_AM_PM, intervalPattern, status); - setIntervalPatternInternally(skeleton, UCAL_HOUR, intervalPattern, status); - } else if ( lrgDiffCalUnit == UCAL_DAY_OF_MONTH || - lrgDiffCalUnit == UCAL_DAY_OF_WEEK ) { - setIntervalPatternInternally(skeleton, UCAL_DATE, intervalPattern, status); - } else { - setIntervalPatternInternally(skeleton, lrgDiffCalUnit, intervalPattern, status); - } -} - - -void -DateIntervalInfo::setFallbackIntervalPattern( - const UnicodeString& fallbackPattern, - UErrorCode& status) { - if ( U_FAILURE(status) ) { - return; - } - int32_t firstPatternIndex = fallbackPattern.indexOf(gFirstPattern, - UPRV_LENGTHOF(gFirstPattern), 0); - int32_t secondPatternIndex = fallbackPattern.indexOf(gSecondPattern, - UPRV_LENGTHOF(gSecondPattern), 0); - if ( firstPatternIndex == -1 || secondPatternIndex == -1 ) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if ( firstPatternIndex > secondPatternIndex ) { - fFirstDateInPtnIsLaterDate = true; - } - fFallbackIntervalPattern = fallbackPattern; -} - - - -DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf) -: UObject(dtitvinf), - fIntervalPatterns(nullptr) -{ - *this = dtitvinf; -} - - - -DateIntervalInfo& -DateIntervalInfo::operator=(const DateIntervalInfo& dtitvinf) { - if ( this == &dtitvinf ) { - return *this; - } - - UErrorCode status = U_ZERO_ERROR; - deleteHash(fIntervalPatterns); - fIntervalPatterns = initHash(status); - copyHash(dtitvinf.fIntervalPatterns, fIntervalPatterns, status); - if ( U_FAILURE(status) ) { - return *this; - } - - fFallbackIntervalPattern = dtitvinf.fFallbackIntervalPattern; - fFirstDateInPtnIsLaterDate = dtitvinf.fFirstDateInPtnIsLaterDate; - return *this; -} - - -DateIntervalInfo* -DateIntervalInfo::clone() const { - return new DateIntervalInfo(*this); -} - - -DateIntervalInfo::~DateIntervalInfo() { - deleteHash(fIntervalPatterns); - fIntervalPatterns = nullptr; -} - - -bool -DateIntervalInfo::operator==(const DateIntervalInfo& other) const { - bool equal = ( - fFallbackIntervalPattern == other.fFallbackIntervalPattern && - fFirstDateInPtnIsLaterDate == other.fFirstDateInPtnIsLaterDate ); - - if ( equal ) { - equal = fIntervalPatterns->equals(*(other.fIntervalPatterns)); - } - - return equal; -} - - -UnicodeString& -DateIntervalInfo::getIntervalPattern(const UnicodeString& skeleton, - UCalendarDateFields field, - UnicodeString& result, - UErrorCode& status) const { - if ( U_FAILURE(status) ) { - return result; - } - - const UnicodeString* patternsOfOneSkeleton = (UnicodeString*) fIntervalPatterns->get(skeleton); - if ( patternsOfOneSkeleton != nullptr ) { - IntervalPatternIndex index = calendarFieldToIntervalIndex(field, status); - if ( U_FAILURE(status) ) { - return result; - } - const UnicodeString& intervalPattern = patternsOfOneSkeleton[index]; - if ( !intervalPattern.isEmpty() ) { - result = intervalPattern; - } - } - return result; -} - - -UBool -DateIntervalInfo::getDefaultOrder() const { - return fFirstDateInPtnIsLaterDate; -} - - -UnicodeString& -DateIntervalInfo::getFallbackIntervalPattern(UnicodeString& result) const { - result = fFallbackIntervalPattern; - return result; -} - -#define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY) - - -static const int32_t PATH_PREFIX_LENGTH = 17; -static const UChar PATH_PREFIX[] = {SOLIDUS, CAP_L, CAP_O, CAP_C, CAP_A, CAP_L, CAP_E, SOLIDUS, - LOW_C, LOW_A, LOW_L, LOW_E, LOW_N, LOW_D, LOW_A, LOW_R, SOLIDUS}; -static const int32_t PATH_SUFFIX_LENGTH = 16; -static const UChar PATH_SUFFIX[] = {SOLIDUS, LOW_I, LOW_N, LOW_T, LOW_E, LOW_R, LOW_V, LOW_A, - LOW_L, CAP_F, LOW_O, LOW_R, LOW_M, LOW_A, LOW_T, LOW_S}; - -/** - * Sink for enumerating all of the date interval skeletons. - */ -struct DateIntervalInfo::DateIntervalSink : public ResourceSink { - - // Output data - DateIntervalInfo &dateIntervalInfo; - - // Next calendar type - UnicodeString nextCalendarType; - - DateIntervalSink(DateIntervalInfo &diInfo, const char *currentCalendarType) - : dateIntervalInfo(diInfo), nextCalendarType(currentCalendarType, -1, US_INV) { } - virtual ~DateIntervalSink(); - - virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &errorCode) override { - if (U_FAILURE(errorCode)) { return; } - - // Iterate over all the calendar entries and only pick the 'intervalFormats' table. - ResourceTable dateIntervalData = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int32_t i = 0; dateIntervalData.getKeyAndValue(i, key, value); i++) { - if (uprv_strcmp(key, gIntervalDateTimePatternTag) != 0) { - continue; - } - - // Handle aliases and tables. Ignore the rest. - if (value.getType() == URES_ALIAS) { - // Get the calendar type for the alias path. - const UnicodeString &aliasPath = value.getAliasUnicodeString(errorCode); - if (U_FAILURE(errorCode)) { return; } - - nextCalendarType.remove(); - getCalendarTypeFromPath(aliasPath, nextCalendarType, errorCode); - - if (U_FAILURE(errorCode)) { - resetNextCalendarType(); - } - break; - - } else if (value.getType() == URES_TABLE) { - // Iterate over all the skeletons in the 'intervalFormat' table. - ResourceTable skeletonData = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int32_t j = 0; skeletonData.getKeyAndValue(j, key, value); j++) { - if (value.getType() == URES_TABLE) { - // Process the skeleton - processSkeletonTable(key, value, errorCode); - if (U_FAILURE(errorCode)) { return; } - } - } - break; - } - } - } - - /** - * Processes the patterns for a skeleton table - */ - void processSkeletonTable(const char *key, ResourceValue &value, UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return; } - - // Iterate over all the patterns in the current skeleton table - const char *currentSkeleton = key; - ResourceTable patternData = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int32_t k = 0; patternData.getKeyAndValue(k, key, value); k++) { - if (value.getType() == URES_STRING) { - // Process the key - UCalendarDateFields calendarField = validateAndProcessPatternLetter(key); - - // If the calendar field has a valid value - if (calendarField < UCAL_FIELD_COUNT) { - // Set the interval pattern - setIntervalPatternIfAbsent(currentSkeleton, calendarField, value, errorCode); - if (U_FAILURE(errorCode)) { return; } - } - } - } - } - - /** - * Extracts the calendar type from the path. - */ - static void getCalendarTypeFromPath(const UnicodeString &path, UnicodeString &calendarType, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { return; } - - if (!path.startsWith(PATH_PREFIX, PATH_PREFIX_LENGTH) || !path.endsWith(PATH_SUFFIX, PATH_SUFFIX_LENGTH)) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - - path.extractBetween(PATH_PREFIX_LENGTH, path.length() - PATH_SUFFIX_LENGTH, calendarType); - } - - /** - * Validates and processes the pattern letter - */ - UCalendarDateFields validateAndProcessPatternLetter(const char *patternLetter) { - // Check that patternLetter is just one letter - char c0; - if ((c0 = patternLetter[0]) != 0 && patternLetter[1] == 0) { - // Check that the pattern letter is accepted - if (c0 == 'G') { - return UCAL_ERA; - } else if (c0 == 'y') { - return UCAL_YEAR; - } else if (c0 == 'M') { - return UCAL_MONTH; - } else if (c0 == 'd') { - return UCAL_DATE; - } else if (c0 == 'a') { - return UCAL_AM_PM; - } else if (c0 == 'B') { - // TODO: Using AM/PM as a proxy for flexible day period isn't really correct, but it's close - return UCAL_AM_PM; - } else if (c0 == 'h' || c0 == 'H') { - return UCAL_HOUR; - } else if (c0 == 'm') { - return UCAL_MINUTE; - }// TODO(ticket:12190): Why icu4c doesn't accept the calendar field "s" but icu4j does? - } - return UCAL_FIELD_COUNT; - } - - /** - * Stores the interval pattern for the current skeleton in the internal data structure - * if it's not present. - */ - void setIntervalPatternIfAbsent(const char *currentSkeleton, UCalendarDateFields lrgDiffCalUnit, - const ResourceValue &value, UErrorCode &errorCode) { - // Check if the pattern has already been stored on the data structure - IntervalPatternIndex index = - dateIntervalInfo.calendarFieldToIntervalIndex(lrgDiffCalUnit, errorCode); - if (U_FAILURE(errorCode)) { return; } - - UnicodeString skeleton(currentSkeleton, -1, US_INV); - UnicodeString* patternsOfOneSkeleton = - (UnicodeString*)(dateIntervalInfo.fIntervalPatterns->get(skeleton)); - - if (patternsOfOneSkeleton == nullptr || patternsOfOneSkeleton[index].isEmpty()) { - UnicodeString pattern = value.getUnicodeString(errorCode); - dateIntervalInfo.setIntervalPatternInternally(skeleton, lrgDiffCalUnit, - pattern, errorCode); - } - } - - const UnicodeString &getNextCalendarType() { - return nextCalendarType; - } - - void resetNextCalendarType() { - nextCalendarType.setToBogus(); - } -}; - -// Virtual destructors must be defined out of line. -DateIntervalInfo::DateIntervalSink::~DateIntervalSink() {} - - - -void -DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& status) -{ - fIntervalPatterns = initHash(status); - if (U_FAILURE(status)) { - return; - } - const char *locName = locale.getName(); - - // Get the correct calendar type - const char * calendarTypeToUse = gGregorianTag; // initial default - char calendarType[ULOC_KEYWORDS_CAPACITY]; // to be filled in with the type to use, if all goes well - char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY]; - // obtain a locale that always has the calendar key value that should be used - (void)ures_getFunctionalEquivalent(localeWithCalendarKey, ULOC_LOCALE_IDENTIFIER_CAPACITY, nullptr, - "calendar", "calendar", locName, nullptr, false, &status); - localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination - // now get the calendar key value from that locale - int32_t calendarTypeLen = uloc_getKeywordValue(localeWithCalendarKey, "calendar", calendarType, - ULOC_KEYWORDS_CAPACITY, &status); - if (U_SUCCESS(status) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) { - calendarTypeToUse = calendarType; - } - status = U_ZERO_ERROR; - - // Instantiate the resource bundles - UResourceBundle *rb, *calBundle; - rb = ures_open(nullptr, locName, &status); - if (U_FAILURE(status)) { - return; - } - calBundle = ures_getByKeyWithFallback(rb, gCalendarTag, nullptr, &status); - - - if (U_SUCCESS(status)) { - UResourceBundle *calTypeBundle, *itvDtPtnResource; - - // Get the fallback pattern - const UChar* resStr = nullptr; - int32_t resStrLen = 0; - calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, nullptr, &status); - itvDtPtnResource = ures_getByKeyWithFallback(calTypeBundle, - gIntervalDateTimePatternTag, nullptr, &status); - // TODO(ICU-20400): After the fixing, we should find the "fallback" from - // the rb directly by the path "calendar/${calendar}/intervalFormats/fallback". - if ( U_SUCCESS(status) ) { - resStr = ures_getStringByKeyWithFallback(itvDtPtnResource, gFallbackPatternTag, - &resStrLen, &status); - } - - if ( U_SUCCESS(status) && (resStr != nullptr)) { - UnicodeString pattern = UnicodeString(true, resStr, resStrLen); - setFallbackIntervalPattern(pattern, status); - } - ures_close(itvDtPtnResource); - ures_close(calTypeBundle); - - - // Instantiate the sink - DateIntervalSink sink(*this, calendarTypeToUse); - const UnicodeString &calendarTypeToUseUString = sink.getNextCalendarType(); - - // Already loaded calendar types - Hashtable loadedCalendarTypes(false, status); - - if (U_SUCCESS(status)) { - while (!calendarTypeToUseUString.isBogus()) { - // Set an error when a loop is detected - if (loadedCalendarTypes.geti(calendarTypeToUseUString) == 1) { - status = U_INVALID_FORMAT_ERROR; - break; - } - - // Register the calendar type to avoid loops - loadedCalendarTypes.puti(calendarTypeToUseUString, 1, status); - if (U_FAILURE(status)) { break; } - - // Get the calendar string - CharString calTypeBuffer; - calTypeBuffer.appendInvariantChars(calendarTypeToUseUString, status); - if (U_FAILURE(status)) { break; } - const char *calType = calTypeBuffer.data(); - - // Reset the next calendar type to load. - sink.resetNextCalendarType(); - - // Get all resources for this calendar type - ures_getAllItemsWithFallback(calBundle, calType, sink, status); - } - } - } - - // Close the opened resource bundles - ures_close(calBundle); - ures_close(rb); -} - -void -DateIntervalInfo::setIntervalPatternInternally(const UnicodeString& skeleton, - UCalendarDateFields lrgDiffCalUnit, - const UnicodeString& intervalPattern, - UErrorCode& status) { - IntervalPatternIndex index = calendarFieldToIntervalIndex(lrgDiffCalUnit,status); - if ( U_FAILURE(status) ) { - return; - } - UnicodeString* patternsOfOneSkeleton = (UnicodeString*)(fIntervalPatterns->get(skeleton)); - UBool emptyHash = false; - if ( patternsOfOneSkeleton == nullptr ) { - patternsOfOneSkeleton = new UnicodeString[kIPI_MAX_INDEX]; - if (patternsOfOneSkeleton == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - emptyHash = true; - } - - patternsOfOneSkeleton[index] = intervalPattern; - if ( emptyHash == true ) { - fIntervalPatterns->put(skeleton, patternsOfOneSkeleton, status); - } -} - - - -void -DateIntervalInfo::parseSkeleton(const UnicodeString& skeleton, - int32_t* skeletonFieldWidth) { - const int8_t PATTERN_CHAR_BASE = 0x41; - int32_t i; - for ( i = 0; i < skeleton.length(); ++i ) { - // it is an ASCII char in skeleton - int8_t ch = (int8_t)skeleton.charAt(i); - ++skeletonFieldWidth[ch - PATTERN_CHAR_BASE]; - } -} - - - -UBool -DateIntervalInfo::stringNumeric(int32_t fieldWidth, int32_t anotherFieldWidth, - char patternLetter) { - if ( patternLetter == 'M' ) { - if ( (fieldWidth <= 2 && anotherFieldWidth > 2) || - (fieldWidth > 2 && anotherFieldWidth <= 2 )) { - return true; - } - } - return false; -} - - - -const UnicodeString* -DateIntervalInfo::getBestSkeleton(const UnicodeString& skeleton, - int8_t& bestMatchDistanceInfo) const { -#ifdef DTITVINF_DEBUG - char result[1000]; - char result_1[1000]; - char mesg[2000]; - skeleton.extract(0, skeleton.length(), result, "UTF-8"); - sprintf(mesg, "in getBestSkeleton: skeleton: %s; \n", result); - PRINTMESG(mesg) -#endif - - - int32_t inputSkeletonFieldWidth[] = - { - // A B C D E F G H I J K L M N O - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // P Q R S T U V W X Y Z - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // a b c d e f g h i j k l m n o - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // p q r s t u v w x y z - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - int32_t skeletonFieldWidth[] = - { - // A B C D E F G H I J K L M N O - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // P Q R S T U V W X Y Z - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // a b c d e f g h i j k l m n o - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // p q r s t u v w x y z - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - const int32_t DIFFERENT_FIELD = 0x1000; - const int32_t STRING_NUMERIC_DIFFERENCE = 0x100; - const int32_t BASE = 0x41; - - // hack for certain alternate characters - // resource bundles only have time skeletons containing 'v', 'h', and 'H' - // but not time skeletons containing 'z', 'K', or 'k' - // the skeleton may also include 'a' or 'b', which never occur in the resource bundles, so strip them out too - UBool replacedAlternateChars = false; - const UnicodeString* inputSkeleton = &skeleton; - UnicodeString copySkeleton; - if ( skeleton.indexOf(LOW_Z) != -1 || skeleton.indexOf(LOW_K) != -1 || skeleton.indexOf(CAP_K) != -1 || skeleton.indexOf(LOW_A) != -1 || skeleton.indexOf(LOW_B) != -1 ) { - copySkeleton = skeleton; - copySkeleton.findAndReplace(UnicodeString(LOW_Z), UnicodeString(LOW_V)); - copySkeleton.findAndReplace(UnicodeString(LOW_K), UnicodeString(CAP_H)); - copySkeleton.findAndReplace(UnicodeString(CAP_K), UnicodeString(LOW_H)); - copySkeleton.findAndReplace(UnicodeString(LOW_A), UnicodeString()); - copySkeleton.findAndReplace(UnicodeString(LOW_B), UnicodeString()); - inputSkeleton = ©Skeleton; - replacedAlternateChars = true; - } - - parseSkeleton(*inputSkeleton, inputSkeletonFieldWidth); - int32_t bestDistance = MAX_POSITIVE_INT; - const UnicodeString* bestSkeleton = nullptr; - - // 0 means exact the same skeletons; - // 1 means having the same field, but with different length, - // 2 means only z/v, h/K, or H/k differs - // -1 means having different field. - bestMatchDistanceInfo = 0; - int8_t fieldLength = UPRV_LENGTHOF(skeletonFieldWidth); - - int32_t pos = UHASH_FIRST; - const UHashElement* elem = nullptr; - while ( (elem = fIntervalPatterns->nextElement(pos)) != nullptr ) { - const UHashTok keyTok = elem->key; - UnicodeString* newSkeleton = (UnicodeString*)keyTok.pointer; -#ifdef DTITVINF_DEBUG - skeleton->extract(0, skeleton->length(), result, "UTF-8"); - sprintf(mesg, "available skeletons: skeleton: %s; \n", result); - PRINTMESG(mesg) -#endif - - // clear skeleton field width - int8_t i; - for ( i = 0; i < fieldLength; ++i ) { - skeletonFieldWidth[i] = 0; - } - parseSkeleton(*newSkeleton, skeletonFieldWidth); - // calculate distance - int32_t distance = 0; - int8_t fieldDifference = 1; - for ( i = 0; i < fieldLength; ++i ) { - int32_t inputFieldWidth = inputSkeletonFieldWidth[i]; - int32_t fieldWidth = skeletonFieldWidth[i]; - if ( inputFieldWidth == fieldWidth ) { - continue; - } - if ( inputFieldWidth == 0 ) { - fieldDifference = -1; - distance += DIFFERENT_FIELD; - } else if ( fieldWidth == 0 ) { - fieldDifference = -1; - distance += DIFFERENT_FIELD; - } else if (stringNumeric(inputFieldWidth, fieldWidth, - (char)(i+BASE) ) ) { - distance += STRING_NUMERIC_DIFFERENCE; - } else { - distance += (inputFieldWidth > fieldWidth) ? - (inputFieldWidth - fieldWidth) : - (fieldWidth - inputFieldWidth); - } - } - if ( distance < bestDistance ) { - bestSkeleton = newSkeleton; - bestDistance = distance; - bestMatchDistanceInfo = fieldDifference; - } - if ( distance == 0 ) { - bestMatchDistanceInfo = 0; - break; - } - } - if ( replacedAlternateChars && bestMatchDistanceInfo != -1 ) { - bestMatchDistanceInfo = 2; - } - return bestSkeleton; -} - - - -DateIntervalInfo::IntervalPatternIndex -DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field, - UErrorCode& status) { - if ( U_FAILURE(status) ) { - return kIPI_MAX_INDEX; - } - IntervalPatternIndex index = kIPI_MAX_INDEX; - switch ( field ) { - case UCAL_ERA: - index = kIPI_ERA; - break; - case UCAL_YEAR: - index = kIPI_YEAR; - break; - case UCAL_MONTH: - index = kIPI_MONTH; - break; - case UCAL_DATE: - case UCAL_DAY_OF_WEEK: - //case UCAL_DAY_OF_MONTH: - index = kIPI_DATE; - break; - case UCAL_AM_PM: - index = kIPI_AM_PM; - break; - case UCAL_HOUR: - case UCAL_HOUR_OF_DAY: - index = kIPI_HOUR; - break; - case UCAL_MINUTE: - index = kIPI_MINUTE; - break; - case UCAL_SECOND: - index = kIPI_SECOND; - break; - case UCAL_MILLISECOND: - index = kIPI_MILLISECOND; - break; - default: - status = U_ILLEGAL_ARGUMENT_ERROR; - } - return index; -} - - - -void -DateIntervalInfo::deleteHash(Hashtable* hTable) -{ - if ( hTable == nullptr ) { - return; - } - int32_t pos = UHASH_FIRST; - const UHashElement* element = nullptr; - while ( (element = hTable->nextElement(pos)) != nullptr ) { - const UHashTok valueTok = element->value; - const UnicodeString* value = (UnicodeString*)valueTok.pointer; - delete[] value; - } - delete fIntervalPatterns; -} - - -U_CDECL_BEGIN - -/** - * set hash table value comparator - * - * @param val1 one value in comparison - * @param val2 the other value in comparison - * @return true if 2 values are the same, false otherwise - */ -static UBool U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2); - -static UBool -U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2) { - const UnicodeString* pattern1 = (UnicodeString*)val1.pointer; - const UnicodeString* pattern2 = (UnicodeString*)val2.pointer; - UBool ret = true; - int8_t i; - for ( i = 0; i < DateIntervalInfo::kMaxIntervalPatternIndex && ret == true; ++i ) { - ret = (pattern1[i] == pattern2[i]); - } - return ret; -} - -U_CDECL_END - - -Hashtable* -DateIntervalInfo::initHash(UErrorCode& status) { - if ( U_FAILURE(status) ) { - return nullptr; - } - Hashtable* hTable; - if ( (hTable = new Hashtable(false, status)) == nullptr ) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - if ( U_FAILURE(status) ) { - delete hTable; - return nullptr; - } - hTable->setValueComparator(dtitvinfHashTableValueComparator); - return hTable; -} - - -void -DateIntervalInfo::copyHash(const Hashtable* source, - Hashtable* target, - UErrorCode& status) { - if ( U_FAILURE(status) ) { - return; - } - int32_t pos = UHASH_FIRST; - const UHashElement* element = nullptr; - if ( source ) { - while ( (element = source->nextElement(pos)) != nullptr ) { - const UHashTok keyTok = element->key; - const UnicodeString* key = (UnicodeString*)keyTok.pointer; - const UHashTok valueTok = element->value; - const UnicodeString* value = (UnicodeString*)valueTok.pointer; - UnicodeString* copy = new UnicodeString[kIPI_MAX_INDEX]; - if (copy == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - int8_t i; - for ( i = 0; i < kIPI_MAX_INDEX; ++i ) { - copy[i] = value[i]; - } - target->put(UnicodeString(*key), copy, status); - if ( U_FAILURE(status) ) { - return; - } - } - } -} - - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************************* +* Copyright (C) 2008-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTITVINF.CPP +* +******************************************************************************* +*/ + +#include "unicode/dtitvinf.h" + + +#if !UCONFIG_NO_FORMATTING + +//TODO: define it in compiler time +//#define DTITVINF_DEBUG 1 + + +#ifdef DTITVINF_DEBUG +#include +#endif + +#include "cmemory.h" +#include "cstring.h" +#include "unicode/msgfmt.h" +#include "unicode/uloc.h" +#include "unicode/ures.h" +#include "dtitv_impl.h" +#include "charstr.h" +#include "hash.h" +#include "gregoimp.h" +#include "uresimp.h" +#include "hash.h" +#include "gregoimp.h" +#include "uresimp.h" + + +U_NAMESPACE_BEGIN + + +#ifdef DTITVINF_DEBUG +#define PRINTMESG(msg) UPRV_BLOCK_MACRO_BEGIN { \ + std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; \ +} UPRV_BLOCK_MACRO_END +#endif + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo) + +static const char gCalendarTag[]="calendar"; +static const char gGregorianTag[]="gregorian"; +static const char gIntervalDateTimePatternTag[]="intervalFormats"; +static const char gFallbackPatternTag[]="fallback"; + +// {0} +static const char16_t gFirstPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET}; +// {1} +static const char16_t gSecondPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET}; + +// default fall-back +static const char16_t gDefaultFallbackPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, EN_DASH, SPACE, LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET, 0}; + +DateIntervalInfo::DateIntervalInfo(UErrorCode& status) +: fFallbackIntervalPattern(gDefaultFallbackPattern), + fFirstDateInPtnIsLaterDate(false), + fIntervalPatterns(nullptr) +{ + fIntervalPatterns = initHash(status); +} + + + +DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status) +: fFallbackIntervalPattern(gDefaultFallbackPattern), + fFirstDateInPtnIsLaterDate(false), + fIntervalPatterns(nullptr) +{ + initializeData(locale, status); +} + + + +void +DateIntervalInfo::setIntervalPattern(const UnicodeString& skeleton, + UCalendarDateFields lrgDiffCalUnit, + const UnicodeString& intervalPattern, + UErrorCode& status) { + + if ( lrgDiffCalUnit == UCAL_HOUR_OF_DAY ) { + setIntervalPatternInternally(skeleton, UCAL_AM_PM, intervalPattern, status); + setIntervalPatternInternally(skeleton, UCAL_HOUR, intervalPattern, status); + } else if ( lrgDiffCalUnit == UCAL_DAY_OF_MONTH || + lrgDiffCalUnit == UCAL_DAY_OF_WEEK ) { + setIntervalPatternInternally(skeleton, UCAL_DATE, intervalPattern, status); + } else { + setIntervalPatternInternally(skeleton, lrgDiffCalUnit, intervalPattern, status); + } +} + + +void +DateIntervalInfo::setFallbackIntervalPattern( + const UnicodeString& fallbackPattern, + UErrorCode& status) { + if ( U_FAILURE(status) ) { + return; + } + int32_t firstPatternIndex = fallbackPattern.indexOf(gFirstPattern, + UPRV_LENGTHOF(gFirstPattern), 0); + int32_t secondPatternIndex = fallbackPattern.indexOf(gSecondPattern, + UPRV_LENGTHOF(gSecondPattern), 0); + if ( firstPatternIndex == -1 || secondPatternIndex == -1 ) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if ( firstPatternIndex > secondPatternIndex ) { + fFirstDateInPtnIsLaterDate = true; + } + fFallbackIntervalPattern = fallbackPattern; +} + + + +DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf) +: UObject(dtitvinf), + fIntervalPatterns(nullptr) +{ + *this = dtitvinf; +} + + + +DateIntervalInfo& +DateIntervalInfo::operator=(const DateIntervalInfo& dtitvinf) { + if ( this == &dtitvinf ) { + return *this; + } + + UErrorCode status = U_ZERO_ERROR; + deleteHash(fIntervalPatterns); + fIntervalPatterns = initHash(status); + copyHash(dtitvinf.fIntervalPatterns, fIntervalPatterns, status); + if ( U_FAILURE(status) ) { + return *this; + } + + fFallbackIntervalPattern = dtitvinf.fFallbackIntervalPattern; + fFirstDateInPtnIsLaterDate = dtitvinf.fFirstDateInPtnIsLaterDate; + return *this; +} + + +DateIntervalInfo* +DateIntervalInfo::clone() const { + return new DateIntervalInfo(*this); +} + + +DateIntervalInfo::~DateIntervalInfo() { + deleteHash(fIntervalPatterns); + fIntervalPatterns = nullptr; +} + + +bool +DateIntervalInfo::operator==(const DateIntervalInfo& other) const { + bool equal = ( + fFallbackIntervalPattern == other.fFallbackIntervalPattern && + fFirstDateInPtnIsLaterDate == other.fFirstDateInPtnIsLaterDate ); + + if ( equal ) { + equal = fIntervalPatterns->equals(*(other.fIntervalPatterns)); + } + + return equal; +} + + +UnicodeString& +DateIntervalInfo::getIntervalPattern(const UnicodeString& skeleton, + UCalendarDateFields field, + UnicodeString& result, + UErrorCode& status) const { + if ( U_FAILURE(status) ) { + return result; + } + + const UnicodeString* patternsOfOneSkeleton = (UnicodeString*) fIntervalPatterns->get(skeleton); + if ( patternsOfOneSkeleton != nullptr ) { + IntervalPatternIndex index = calendarFieldToIntervalIndex(field, status); + if ( U_FAILURE(status) ) { + return result; + } + const UnicodeString& intervalPattern = patternsOfOneSkeleton[index]; + if ( !intervalPattern.isEmpty() ) { + result = intervalPattern; + } + } + return result; +} + + +UBool +DateIntervalInfo::getDefaultOrder() const { + return fFirstDateInPtnIsLaterDate; +} + + +UnicodeString& +DateIntervalInfo::getFallbackIntervalPattern(UnicodeString& result) const { + result = fFallbackIntervalPattern; + return result; +} + +#define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY) + + +static const int32_t PATH_PREFIX_LENGTH = 17; +static const char16_t PATH_PREFIX[] = {SOLIDUS, CAP_L, CAP_O, CAP_C, CAP_A, CAP_L, CAP_E, SOLIDUS, + LOW_C, LOW_A, LOW_L, LOW_E, LOW_N, LOW_D, LOW_A, LOW_R, SOLIDUS}; +static const int32_t PATH_SUFFIX_LENGTH = 16; +static const char16_t PATH_SUFFIX[] = {SOLIDUS, LOW_I, LOW_N, LOW_T, LOW_E, LOW_R, LOW_V, LOW_A, + LOW_L, CAP_F, LOW_O, LOW_R, LOW_M, LOW_A, LOW_T, LOW_S}; + +/** + * Sink for enumerating all of the date interval skeletons. + */ +struct DateIntervalInfo::DateIntervalSink : public ResourceSink { + + // Output data + DateIntervalInfo &dateIntervalInfo; + + // Next calendar type + UnicodeString nextCalendarType; + + DateIntervalSink(DateIntervalInfo &diInfo, const char *currentCalendarType) + : dateIntervalInfo(diInfo), nextCalendarType(currentCalendarType, -1, US_INV) { } + virtual ~DateIntervalSink(); + + virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &errorCode) override { + if (U_FAILURE(errorCode)) { return; } + + // Iterate over all the calendar entries and only pick the 'intervalFormats' table. + ResourceTable dateIntervalData = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + for (int32_t i = 0; dateIntervalData.getKeyAndValue(i, key, value); i++) { + if (uprv_strcmp(key, gIntervalDateTimePatternTag) != 0) { + continue; + } + + // Handle aliases and tables. Ignore the rest. + if (value.getType() == URES_ALIAS) { + // Get the calendar type for the alias path. + const UnicodeString &aliasPath = value.getAliasUnicodeString(errorCode); + if (U_FAILURE(errorCode)) { return; } + + nextCalendarType.remove(); + getCalendarTypeFromPath(aliasPath, nextCalendarType, errorCode); + + if (U_FAILURE(errorCode)) { + resetNextCalendarType(); + } + break; + + } else if (value.getType() == URES_TABLE) { + // Iterate over all the skeletons in the 'intervalFormat' table. + ResourceTable skeletonData = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + for (int32_t j = 0; skeletonData.getKeyAndValue(j, key, value); j++) { + if (value.getType() == URES_TABLE) { + // Process the skeleton + processSkeletonTable(key, value, errorCode); + if (U_FAILURE(errorCode)) { return; } + } + } + break; + } + } + } + + /** + * Processes the patterns for a skeleton table + */ + void processSkeletonTable(const char *key, ResourceValue &value, UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return; } + + // Iterate over all the patterns in the current skeleton table + const char *currentSkeleton = key; + ResourceTable patternData = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + for (int32_t k = 0; patternData.getKeyAndValue(k, key, value); k++) { + if (value.getType() == URES_STRING) { + // Process the key + UCalendarDateFields calendarField = validateAndProcessPatternLetter(key); + + // If the calendar field has a valid value + if (calendarField < UCAL_FIELD_COUNT) { + // Set the interval pattern + setIntervalPatternIfAbsent(currentSkeleton, calendarField, value, errorCode); + if (U_FAILURE(errorCode)) { return; } + } + } + } + } + + /** + * Extracts the calendar type from the path. + */ + static void getCalendarTypeFromPath(const UnicodeString &path, UnicodeString &calendarType, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { return; } + + if (!path.startsWith(PATH_PREFIX, PATH_PREFIX_LENGTH) || !path.endsWith(PATH_SUFFIX, PATH_SUFFIX_LENGTH)) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + + path.extractBetween(PATH_PREFIX_LENGTH, path.length() - PATH_SUFFIX_LENGTH, calendarType); + } + + /** + * Validates and processes the pattern letter + */ + UCalendarDateFields validateAndProcessPatternLetter(const char *patternLetter) { + // Check that patternLetter is just one letter + char c0; + if ((c0 = patternLetter[0]) != 0 && patternLetter[1] == 0) { + // Check that the pattern letter is accepted + if (c0 == 'G') { + return UCAL_ERA; + } else if (c0 == 'y') { + return UCAL_YEAR; + } else if (c0 == 'M') { + return UCAL_MONTH; + } else if (c0 == 'd') { + return UCAL_DATE; + } else if (c0 == 'a') { + return UCAL_AM_PM; + } else if (c0 == 'B') { + // TODO: Using AM/PM as a proxy for flexible day period isn't really correct, but it's close + return UCAL_AM_PM; + } else if (c0 == 'h' || c0 == 'H') { + return UCAL_HOUR; + } else if (c0 == 'm') { + return UCAL_MINUTE; + }// TODO(ticket:12190): Why icu4c doesn't accept the calendar field "s" but icu4j does? + } + return UCAL_FIELD_COUNT; + } + + /** + * Stores the interval pattern for the current skeleton in the internal data structure + * if it's not present. + */ + void setIntervalPatternIfAbsent(const char *currentSkeleton, UCalendarDateFields lrgDiffCalUnit, + const ResourceValue &value, UErrorCode &errorCode) { + // Check if the pattern has already been stored on the data structure + IntervalPatternIndex index = + dateIntervalInfo.calendarFieldToIntervalIndex(lrgDiffCalUnit, errorCode); + if (U_FAILURE(errorCode)) { return; } + + UnicodeString skeleton(currentSkeleton, -1, US_INV); + UnicodeString* patternsOfOneSkeleton = + (UnicodeString*)(dateIntervalInfo.fIntervalPatterns->get(skeleton)); + + if (patternsOfOneSkeleton == nullptr || patternsOfOneSkeleton[index].isEmpty()) { + UnicodeString pattern = value.getUnicodeString(errorCode); + dateIntervalInfo.setIntervalPatternInternally(skeleton, lrgDiffCalUnit, + pattern, errorCode); + } + } + + const UnicodeString &getNextCalendarType() { + return nextCalendarType; + } + + void resetNextCalendarType() { + nextCalendarType.setToBogus(); + } +}; + +// Virtual destructors must be defined out of line. +DateIntervalInfo::DateIntervalSink::~DateIntervalSink() {} + + + +void +DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& status) +{ + fIntervalPatterns = initHash(status); + if (U_FAILURE(status)) { + return; + } + const char *locName = locale.getName(); + + // Get the correct calendar type + const char * calendarTypeToUse = gGregorianTag; // initial default + char calendarType[ULOC_KEYWORDS_CAPACITY]; // to be filled in with the type to use, if all goes well + char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY]; + // obtain a locale that always has the calendar key value that should be used + (void)ures_getFunctionalEquivalent(localeWithCalendarKey, ULOC_LOCALE_IDENTIFIER_CAPACITY, nullptr, + "calendar", "calendar", locName, nullptr, false, &status); + localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination + // now get the calendar key value from that locale + int32_t calendarTypeLen = uloc_getKeywordValue(localeWithCalendarKey, "calendar", calendarType, + ULOC_KEYWORDS_CAPACITY, &status); + if (U_SUCCESS(status) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) { + calendarTypeToUse = calendarType; + } + status = U_ZERO_ERROR; + + // Instantiate the resource bundles + UResourceBundle *rb, *calBundle; + rb = ures_open(nullptr, locName, &status); + if (U_FAILURE(status)) { + return; + } + calBundle = ures_getByKeyWithFallback(rb, gCalendarTag, nullptr, &status); + + + if (U_SUCCESS(status)) { + UResourceBundle *calTypeBundle, *itvDtPtnResource; + + // Get the fallback pattern + const char16_t* resStr = nullptr; + int32_t resStrLen = 0; + calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, nullptr, &status); + itvDtPtnResource = ures_getByKeyWithFallback(calTypeBundle, + gIntervalDateTimePatternTag, nullptr, &status); + // TODO(ICU-20400): After the fixing, we should find the "fallback" from + // the rb directly by the path "calendar/${calendar}/intervalFormats/fallback". + if ( U_SUCCESS(status) ) { + resStr = ures_getStringByKeyWithFallback(itvDtPtnResource, gFallbackPatternTag, + &resStrLen, &status); + } + + if ( U_SUCCESS(status) && (resStr != nullptr)) { + UnicodeString pattern = UnicodeString(true, resStr, resStrLen); + setFallbackIntervalPattern(pattern, status); + } + ures_close(itvDtPtnResource); + ures_close(calTypeBundle); + + + // Instantiate the sink + DateIntervalSink sink(*this, calendarTypeToUse); + const UnicodeString &calendarTypeToUseUString = sink.getNextCalendarType(); + + // Already loaded calendar types + Hashtable loadedCalendarTypes(false, status); + + if (U_SUCCESS(status)) { + while (!calendarTypeToUseUString.isBogus()) { + // Set an error when a loop is detected + if (loadedCalendarTypes.geti(calendarTypeToUseUString) == 1) { + status = U_INVALID_FORMAT_ERROR; + break; + } + + // Register the calendar type to avoid loops + loadedCalendarTypes.puti(calendarTypeToUseUString, 1, status); + if (U_FAILURE(status)) { break; } + + // Get the calendar string + CharString calTypeBuffer; + calTypeBuffer.appendInvariantChars(calendarTypeToUseUString, status); + if (U_FAILURE(status)) { break; } + const char *calType = calTypeBuffer.data(); + + // Reset the next calendar type to load. + sink.resetNextCalendarType(); + + // Get all resources for this calendar type + ures_getAllItemsWithFallback(calBundle, calType, sink, status); + } + } + } + + // Close the opened resource bundles + ures_close(calBundle); + ures_close(rb); +} + +void +DateIntervalInfo::setIntervalPatternInternally(const UnicodeString& skeleton, + UCalendarDateFields lrgDiffCalUnit, + const UnicodeString& intervalPattern, + UErrorCode& status) { + IntervalPatternIndex index = calendarFieldToIntervalIndex(lrgDiffCalUnit,status); + if ( U_FAILURE(status) ) { + return; + } + UnicodeString* patternsOfOneSkeleton = (UnicodeString*)(fIntervalPatterns->get(skeleton)); + UBool emptyHash = false; + if ( patternsOfOneSkeleton == nullptr ) { + patternsOfOneSkeleton = new UnicodeString[kIPI_MAX_INDEX]; + if (patternsOfOneSkeleton == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + emptyHash = true; + } + + patternsOfOneSkeleton[index] = intervalPattern; + if ( emptyHash ) { + fIntervalPatterns->put(skeleton, patternsOfOneSkeleton, status); + } +} + + + +void +DateIntervalInfo::parseSkeleton(const UnicodeString& skeleton, + int32_t* skeletonFieldWidth) { + const int8_t PATTERN_CHAR_BASE = 0x41; + int32_t i; + for ( i = 0; i < skeleton.length(); ++i ) { + // it is an ASCII char in skeleton + int8_t ch = (int8_t)skeleton.charAt(i); + ++skeletonFieldWidth[ch - PATTERN_CHAR_BASE]; + } +} + + + +UBool +DateIntervalInfo::stringNumeric(int32_t fieldWidth, int32_t anotherFieldWidth, + char patternLetter) { + if ( patternLetter == 'M' ) { + if ( (fieldWidth <= 2 && anotherFieldWidth > 2) || + (fieldWidth > 2 && anotherFieldWidth <= 2 )) { + return true; + } + } + return false; +} + + + +const UnicodeString* +DateIntervalInfo::getBestSkeleton(const UnicodeString& skeleton, + int8_t& bestMatchDistanceInfo) const { +#ifdef DTITVINF_DEBUG + char result[1000]; + char result_1[1000]; + char mesg[2000]; + skeleton.extract(0, skeleton.length(), result, "UTF-8"); + snprintf(mesg, sizeof(mesg), "in getBestSkeleton: skeleton: %s; \n", result); + PRINTMESG(mesg) +#endif + + + int32_t inputSkeletonFieldWidth[] = + { + // A B C D E F G H I J K L M N O + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // P Q R S T U V W X Y Z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // a b c d e f g h i j k l m n o + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // p q r s t u v w x y z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + int32_t skeletonFieldWidth[] = + { + // A B C D E F G H I J K L M N O + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // P Q R S T U V W X Y Z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // a b c d e f g h i j k l m n o + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // p q r s t u v w x y z + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + const int32_t DIFFERENT_FIELD = 0x1000; + const int32_t STRING_NUMERIC_DIFFERENCE = 0x100; + const int32_t BASE = 0x41; + + // hack for certain alternate characters + // resource bundles only have time skeletons containing 'v', 'h', and 'H' + // but not time skeletons containing 'z', 'K', or 'k' + // the skeleton may also include 'a' or 'b', which never occur in the resource bundles, so strip them out too + UBool replacedAlternateChars = false; + const UnicodeString* inputSkeleton = &skeleton; + UnicodeString copySkeleton; + if ( skeleton.indexOf(LOW_Z) != -1 || skeleton.indexOf(LOW_K) != -1 || skeleton.indexOf(CAP_K) != -1 || skeleton.indexOf(LOW_A) != -1 || skeleton.indexOf(LOW_B) != -1 ) { + copySkeleton = skeleton; + copySkeleton.findAndReplace(UnicodeString(LOW_Z), UnicodeString(LOW_V)); + copySkeleton.findAndReplace(UnicodeString(LOW_K), UnicodeString(CAP_H)); + copySkeleton.findAndReplace(UnicodeString(CAP_K), UnicodeString(LOW_H)); + copySkeleton.findAndReplace(UnicodeString(LOW_A), UnicodeString()); + copySkeleton.findAndReplace(UnicodeString(LOW_B), UnicodeString()); + inputSkeleton = ©Skeleton; + replacedAlternateChars = true; + } + + parseSkeleton(*inputSkeleton, inputSkeletonFieldWidth); + int32_t bestDistance = MAX_POSITIVE_INT; + const UnicodeString* bestSkeleton = nullptr; + + // 0 means exact the same skeletons; + // 1 means having the same field, but with different length, + // 2 means only z/v, h/K, or H/k differs + // -1 means having different field. + bestMatchDistanceInfo = 0; + int8_t fieldLength = UPRV_LENGTHOF(skeletonFieldWidth); + + int32_t pos = UHASH_FIRST; + const UHashElement* elem = nullptr; + while ( (elem = fIntervalPatterns->nextElement(pos)) != nullptr ) { + const UHashTok keyTok = elem->key; + UnicodeString* newSkeleton = (UnicodeString*)keyTok.pointer; +#ifdef DTITVINF_DEBUG + skeleton->extract(0, skeleton->length(), result, "UTF-8"); + snprintf(mesg, sizeof(mesg), "available skeletons: skeleton: %s; \n", result); + PRINTMESG(mesg) +#endif + + // clear skeleton field width + int8_t i; + for ( i = 0; i < fieldLength; ++i ) { + skeletonFieldWidth[i] = 0; + } + parseSkeleton(*newSkeleton, skeletonFieldWidth); + // calculate distance + int32_t distance = 0; + int8_t fieldDifference = 1; + for ( i = 0; i < fieldLength; ++i ) { + int32_t inputFieldWidth = inputSkeletonFieldWidth[i]; + int32_t fieldWidth = skeletonFieldWidth[i]; + if ( inputFieldWidth == fieldWidth ) { + continue; + } + if ( inputFieldWidth == 0 ) { + fieldDifference = -1; + distance += DIFFERENT_FIELD; + } else if ( fieldWidth == 0 ) { + fieldDifference = -1; + distance += DIFFERENT_FIELD; + } else if (stringNumeric(inputFieldWidth, fieldWidth, + (char)(i+BASE) ) ) { + distance += STRING_NUMERIC_DIFFERENCE; + } else { + distance += (inputFieldWidth > fieldWidth) ? + (inputFieldWidth - fieldWidth) : + (fieldWidth - inputFieldWidth); + } + } + if ( distance < bestDistance ) { + bestSkeleton = newSkeleton; + bestDistance = distance; + bestMatchDistanceInfo = fieldDifference; + } + if ( distance == 0 ) { + bestMatchDistanceInfo = 0; + break; + } + } + if ( replacedAlternateChars && bestMatchDistanceInfo != -1 ) { + bestMatchDistanceInfo = 2; + } + return bestSkeleton; +} + + + +DateIntervalInfo::IntervalPatternIndex +DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field, + UErrorCode& status) { + if ( U_FAILURE(status) ) { + return kIPI_MAX_INDEX; + } + IntervalPatternIndex index = kIPI_MAX_INDEX; + switch ( field ) { + case UCAL_ERA: + index = kIPI_ERA; + break; + case UCAL_YEAR: + index = kIPI_YEAR; + break; + case UCAL_MONTH: + index = kIPI_MONTH; + break; + case UCAL_DATE: + case UCAL_DAY_OF_WEEK: + //case UCAL_DAY_OF_MONTH: + index = kIPI_DATE; + break; + case UCAL_AM_PM: + index = kIPI_AM_PM; + break; + case UCAL_HOUR: + case UCAL_HOUR_OF_DAY: + index = kIPI_HOUR; + break; + case UCAL_MINUTE: + index = kIPI_MINUTE; + break; + case UCAL_SECOND: + index = kIPI_SECOND; + break; + case UCAL_MILLISECOND: + index = kIPI_MILLISECOND; + break; + default: + status = U_ILLEGAL_ARGUMENT_ERROR; + } + return index; +} + + + +void +DateIntervalInfo::deleteHash(Hashtable* hTable) +{ + if ( hTable == nullptr ) { + return; + } + int32_t pos = UHASH_FIRST; + const UHashElement* element = nullptr; + while ( (element = hTable->nextElement(pos)) != nullptr ) { + const UHashTok valueTok = element->value; + const UnicodeString* value = (UnicodeString*)valueTok.pointer; + delete[] value; + } + delete fIntervalPatterns; +} + + +U_CDECL_BEGIN + +/** + * set hash table value comparator + * + * @param val1 one value in comparison + * @param val2 the other value in comparison + * @return true if 2 values are the same, false otherwise + */ +static UBool U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2); + +static UBool +U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2) { + const UnicodeString* pattern1 = (UnicodeString*)val1.pointer; + const UnicodeString* pattern2 = (UnicodeString*)val2.pointer; + UBool ret = true; + int8_t i; + for ( i = 0; i < DateIntervalInfo::kMaxIntervalPatternIndex && ret ; ++i ) { + ret = (pattern1[i] == pattern2[i]); + } + return ret; +} + +U_CDECL_END + + +Hashtable* +DateIntervalInfo::initHash(UErrorCode& status) { + if ( U_FAILURE(status) ) { + return nullptr; + } + Hashtable* hTable; + if ( (hTable = new Hashtable(false, status)) == nullptr ) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + if ( U_FAILURE(status) ) { + delete hTable; + return nullptr; + } + hTable->setValueComparator(dtitvinfHashTableValueComparator); + return hTable; +} + + +void +DateIntervalInfo::copyHash(const Hashtable* source, + Hashtable* target, + UErrorCode& status) { + if ( U_FAILURE(status) ) { + return; + } + int32_t pos = UHASH_FIRST; + const UHashElement* element = nullptr; + if ( source ) { + while ( (element = source->nextElement(pos)) != nullptr ) { + const UHashTok keyTok = element->key; + const UnicodeString* key = (UnicodeString*)keyTok.pointer; + const UHashTok valueTok = element->value; + const UnicodeString* value = (UnicodeString*)valueTok.pointer; + UnicodeString* copy = new UnicodeString[kIPI_MAX_INDEX]; + if (copy == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + int8_t i; + for ( i = 0; i < kIPI_MAX_INDEX; ++i ) { + copy[i] = value[i]; + } + target->put(UnicodeString(*key), copy, status); + if ( U_FAILURE(status) ) { + return; + } + } + } +} + + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/i18n/dtptngen.cpp b/deps/icu-small/source/i18n/dtptngen.cpp index f4e28de91bce3f..02e9a6a2d64c98 100644 --- a/deps/icu-small/source/i18n/dtptngen.cpp +++ b/deps/icu-small/source/i18n/dtptngen.cpp @@ -1,3020 +1,3020 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2007-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File DTPTNGEN.CPP -* -******************************************************************************* -*/ - -#include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING - -#include "unicode/datefmt.h" -#include "unicode/decimfmt.h" -#include "unicode/dtfmtsym.h" -#include "unicode/dtptngen.h" -#include "unicode/localpointer.h" -#include "unicode/simpleformatter.h" -#include "unicode/smpdtfmt.h" -#include "unicode/udat.h" -#include "unicode/udatpg.h" -#include "unicode/uniset.h" -#include "unicode/uloc.h" -#include "unicode/ures.h" -#include "unicode/ustring.h" -#include "unicode/rep.h" -#include "unicode/region.h" -#include "cpputils.h" -#include "mutex.h" -#include "umutex.h" -#include "cmemory.h" -#include "cstring.h" -#include "locbased.h" -#include "hash.h" -#include "uhash.h" -#include "uresimp.h" -#include "dtptngen_impl.h" -#include "ucln_in.h" -#include "charstr.h" -#include "uassert.h" - -#if U_CHARSET_FAMILY==U_EBCDIC_FAMILY -/** - * If we are on EBCDIC, use an iterator which will - * traverse the bundles in ASCII order. - */ -#define U_USE_ASCII_BUNDLE_ITERATOR -#define U_SORT_ASCII_BUNDLE_ITERATOR -#endif - -#if defined(U_USE_ASCII_BUNDLE_ITERATOR) - -#include "unicode/ustring.h" -#include "uarrsort.h" - -struct UResAEntry { - UChar *key; - UResourceBundle *item; -}; - -struct UResourceBundleAIterator { - UResourceBundle *bund; - UResAEntry *entries; - int32_t num; - int32_t cursor; -}; - -/* Must be C linkage to pass function pointer to the sort function */ - -U_CDECL_BEGIN - -static int32_t U_CALLCONV -ures_a_codepointSort(const void *context, const void *left, const void *right) { - //CompareContext *cmp=(CompareContext *)context; - return u_strcmp(((const UResAEntry *)left)->key, - ((const UResAEntry *)right)->key); -} - -U_CDECL_END - -static void ures_a_open(UResourceBundleAIterator *aiter, UResourceBundle *bund, UErrorCode *status) { - if(U_FAILURE(*status)) { - return; - } - aiter->bund = bund; - aiter->num = ures_getSize(aiter->bund); - aiter->cursor = 0; -#if !defined(U_SORT_ASCII_BUNDLE_ITERATOR) - aiter->entries = nullptr; -#else - aiter->entries = (UResAEntry*)uprv_malloc(sizeof(UResAEntry)*aiter->num); - for(int i=0;inum;i++) { - aiter->entries[i].item = ures_getByIndex(aiter->bund, i, nullptr, status); - const char *akey = ures_getKey(aiter->entries[i].item); - int32_t len = uprv_strlen(akey)+1; - aiter->entries[i].key = (UChar*)uprv_malloc(len*sizeof(UChar)); - u_charsToUChars(akey, aiter->entries[i].key, len); - } - uprv_sortArray(aiter->entries, aiter->num, sizeof(UResAEntry), ures_a_codepointSort, nullptr, true, status); -#endif -} - -static void ures_a_close(UResourceBundleAIterator *aiter) { -#if defined(U_SORT_ASCII_BUNDLE_ITERATOR) - for(int i=0;inum;i++) { - uprv_free(aiter->entries[i].key); - ures_close(aiter->entries[i].item); - } -#endif -} - -static const UChar *ures_a_getNextString(UResourceBundleAIterator *aiter, int32_t *len, const char **key, UErrorCode *err) { -#if !defined(U_SORT_ASCII_BUNDLE_ITERATOR) - return ures_getNextString(aiter->bund, len, key, err); -#else - if(U_FAILURE(*err)) return nullptr; - UResourceBundle *item = aiter->entries[aiter->cursor].item; - const UChar* ret = ures_getString(item, len, err); - *key = ures_getKey(item); - aiter->cursor++; - return ret; -#endif -} - - -#endif - - -U_NAMESPACE_BEGIN - -// ***************************************************************************** -// class DateTimePatternGenerator -// ***************************************************************************** -static const UChar Canonical_Items[] = { - // GyQMwWEDFdaHmsSv - CAP_G, LOW_Y, CAP_Q, CAP_M, LOW_W, CAP_W, CAP_E, - CAP_D, CAP_F, LOW_D, LOW_A, // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J - CAP_H, LOW_M, LOW_S, CAP_S, LOW_V, 0 -}; - -static const dtTypeElem dtTypes[] = { - // patternChar, field, type, minLen, weight - {CAP_G, UDATPG_ERA_FIELD, DT_SHORT, 1, 3,}, - {CAP_G, UDATPG_ERA_FIELD, DT_LONG, 4, 0}, - {CAP_G, UDATPG_ERA_FIELD, DT_NARROW, 5, 0}, - - {LOW_Y, UDATPG_YEAR_FIELD, DT_NUMERIC, 1, 20}, - {CAP_Y, UDATPG_YEAR_FIELD, DT_NUMERIC + DT_DELTA, 1, 20}, - {LOW_U, UDATPG_YEAR_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 20}, - {LOW_R, UDATPG_YEAR_FIELD, DT_NUMERIC + 3*DT_DELTA, 1, 20}, - {CAP_U, UDATPG_YEAR_FIELD, DT_SHORT, 1, 3}, - {CAP_U, UDATPG_YEAR_FIELD, DT_LONG, 4, 0}, - {CAP_U, UDATPG_YEAR_FIELD, DT_NARROW, 5, 0}, - - {CAP_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC, 1, 2}, - {CAP_Q, UDATPG_QUARTER_FIELD, DT_SHORT, 3, 0}, - {CAP_Q, UDATPG_QUARTER_FIELD, DT_LONG, 4, 0}, - {CAP_Q, UDATPG_QUARTER_FIELD, DT_NARROW, 5, 0}, - {LOW_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, - {LOW_Q, UDATPG_QUARTER_FIELD, DT_SHORT - DT_DELTA, 3, 0}, - {LOW_Q, UDATPG_QUARTER_FIELD, DT_LONG - DT_DELTA, 4, 0}, - {LOW_Q, UDATPG_QUARTER_FIELD, DT_NARROW - DT_DELTA, 5, 0}, - - {CAP_M, UDATPG_MONTH_FIELD, DT_NUMERIC, 1, 2}, - {CAP_M, UDATPG_MONTH_FIELD, DT_SHORT, 3, 0}, - {CAP_M, UDATPG_MONTH_FIELD, DT_LONG, 4, 0}, - {CAP_M, UDATPG_MONTH_FIELD, DT_NARROW, 5, 0}, - {CAP_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, - {CAP_L, UDATPG_MONTH_FIELD, DT_SHORT - DT_DELTA, 3, 0}, - {CAP_L, UDATPG_MONTH_FIELD, DT_LONG - DT_DELTA, 4, 0}, - {CAP_L, UDATPG_MONTH_FIELD, DT_NARROW - DT_DELTA, 5, 0}, - {LOW_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 1}, - - {LOW_W, UDATPG_WEEK_OF_YEAR_FIELD, DT_NUMERIC, 1, 2}, - - {CAP_W, UDATPG_WEEK_OF_MONTH_FIELD, DT_NUMERIC, 1, 0}, - - {CAP_E, UDATPG_WEEKDAY_FIELD, DT_SHORT, 1, 3}, - {CAP_E, UDATPG_WEEKDAY_FIELD, DT_LONG, 4, 0}, - {CAP_E, UDATPG_WEEKDAY_FIELD, DT_NARROW, 5, 0}, - {CAP_E, UDATPG_WEEKDAY_FIELD, DT_SHORTER, 6, 0}, - {LOW_C, UDATPG_WEEKDAY_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 2}, - {LOW_C, UDATPG_WEEKDAY_FIELD, DT_SHORT - 2*DT_DELTA, 3, 0}, - {LOW_C, UDATPG_WEEKDAY_FIELD, DT_LONG - 2*DT_DELTA, 4, 0}, - {LOW_C, UDATPG_WEEKDAY_FIELD, DT_NARROW - 2*DT_DELTA, 5, 0}, - {LOW_C, UDATPG_WEEKDAY_FIELD, DT_SHORTER - 2*DT_DELTA, 6, 0}, - {LOW_E, UDATPG_WEEKDAY_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, // LOW_E is currently not used in CLDR data, should not be canonical - {LOW_E, UDATPG_WEEKDAY_FIELD, DT_SHORT - DT_DELTA, 3, 0}, - {LOW_E, UDATPG_WEEKDAY_FIELD, DT_LONG - DT_DELTA, 4, 0}, - {LOW_E, UDATPG_WEEKDAY_FIELD, DT_NARROW - DT_DELTA, 5, 0}, - {LOW_E, UDATPG_WEEKDAY_FIELD, DT_SHORTER - DT_DELTA, 6, 0}, - - {LOW_D, UDATPG_DAY_FIELD, DT_NUMERIC, 1, 2}, - {LOW_G, UDATPG_DAY_FIELD, DT_NUMERIC + DT_DELTA, 1, 20}, // really internal use, so we don't care - - {CAP_D, UDATPG_DAY_OF_YEAR_FIELD, DT_NUMERIC, 1, 3}, - - {CAP_F, UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, DT_NUMERIC, 1, 0}, - - {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_SHORT, 1, 3}, - {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_LONG, 4, 0}, - {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_NARROW, 5, 0}, - {LOW_B, UDATPG_DAYPERIOD_FIELD, DT_SHORT - DT_DELTA, 1, 3}, - {LOW_B, UDATPG_DAYPERIOD_FIELD, DT_LONG - DT_DELTA, 4, 0}, - {LOW_B, UDATPG_DAYPERIOD_FIELD, DT_NARROW - DT_DELTA, 5, 0}, - // b needs to be closer to a than to B, so we make this 3*DT_DELTA - {CAP_B, UDATPG_DAYPERIOD_FIELD, DT_SHORT - 3*DT_DELTA, 1, 3}, - {CAP_B, UDATPG_DAYPERIOD_FIELD, DT_LONG - 3*DT_DELTA, 4, 0}, - {CAP_B, UDATPG_DAYPERIOD_FIELD, DT_NARROW - 3*DT_DELTA, 5, 0}, - - {CAP_H, UDATPG_HOUR_FIELD, DT_NUMERIC + 10*DT_DELTA, 1, 2}, // 24 hour - {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + 11*DT_DELTA, 1, 2}, // 24 hour - {LOW_H, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12 hour - {CAP_K, UDATPG_HOUR_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, // 12 hour - // The C code has had versions of the following 3, keep & update. Should not need these, but... - // Without these, certain tests using e.g. staticGetSkeleton fail because j/J in patterns - // get skipped instead of mapped to the right hour chars, for example in - // DateFormatTest::TestPatternFromSkeleton - // IntlTestDateTimePatternGeneratorAPI:: testStaticGetSkeleton - // DateIntervalFormatTest::testTicket11985 - // Need to investigate better handling of jJC replacement e.g. in staticGetSkeleton. - {CAP_J, UDATPG_HOUR_FIELD, DT_NUMERIC + 5*DT_DELTA, 1, 2}, // 12/24 hour no AM/PM - {LOW_J, UDATPG_HOUR_FIELD, DT_NUMERIC + 6*DT_DELTA, 1, 6}, // 12/24 hour - {CAP_C, UDATPG_HOUR_FIELD, DT_NUMERIC + 7*DT_DELTA, 1, 6}, // 12/24 hour with preferred dayPeriods for 12 - - {LOW_M, UDATPG_MINUTE_FIELD, DT_NUMERIC, 1, 2}, - - {LOW_S, UDATPG_SECOND_FIELD, DT_NUMERIC, 1, 2}, - {CAP_A, UDATPG_SECOND_FIELD, DT_NUMERIC + DT_DELTA, 1, 1000}, - - {CAP_S, UDATPG_FRACTIONAL_SECOND_FIELD, DT_NUMERIC, 1, 1000}, - - {LOW_V, UDATPG_ZONE_FIELD, DT_SHORT - 2*DT_DELTA, 1, 0}, - {LOW_V, UDATPG_ZONE_FIELD, DT_LONG - 2*DT_DELTA, 4, 0}, - {LOW_Z, UDATPG_ZONE_FIELD, DT_SHORT, 1, 3}, - {LOW_Z, UDATPG_ZONE_FIELD, DT_LONG, 4, 0}, - {CAP_Z, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 3}, - {CAP_Z, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, - {CAP_Z, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 5, 0}, - {CAP_O, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0}, - {CAP_O, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, - {CAP_V, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0}, - {CAP_V, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 2, 0}, - {CAP_V, UDATPG_ZONE_FIELD, DT_LONG-1 - DT_DELTA, 3, 0}, - {CAP_V, UDATPG_ZONE_FIELD, DT_LONG-2 - DT_DELTA, 4, 0}, - {CAP_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0}, - {CAP_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0}, - {CAP_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, - {LOW_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0}, - {LOW_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0}, - {LOW_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, - - {0, UDATPG_FIELD_COUNT, 0, 0, 0} , // last row of dtTypes[] - }; - -static const char* const CLDR_FIELD_APPEND[] = { - "Era", "Year", "Quarter", "Month", "Week", "*", "Day-Of-Week", - "*", "*", "Day", "*", // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J - "Hour", "Minute", "Second", "*", "Timezone" -}; - -static const char* const CLDR_FIELD_NAME[UDATPG_FIELD_COUNT] = { - "era", "year", "quarter", "month", "week", "weekOfMonth", "weekday", - "dayOfYear", "weekdayOfMonth", "day", "dayperiod", // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J - "hour", "minute", "second", "*", "zone" -}; - -static const char* const CLDR_FIELD_WIDTH[] = { // [UDATPG_WIDTH_COUNT] - "", "-short", "-narrow" -}; - -static constexpr UDateTimePGDisplayWidth UDATPG_WIDTH_APPENDITEM = UDATPG_WIDE; -static constexpr int32_t UDATPG_FIELD_KEY_MAX = 24; // max length of CLDR field tag (type + width) - -// For appendItems -static const UChar UDATPG_ItemFormat[]= {0x7B, 0x30, 0x7D, 0x20, 0x251C, 0x7B, 0x32, 0x7D, 0x3A, - 0x20, 0x7B, 0x31, 0x7D, 0x2524, 0}; // {0} \u251C{2}: {1}\u2524 - -//static const UChar repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ" - -static const char DT_DateTimePatternsTag[]="DateTimePatterns"; -static const char DT_DateAtTimePatternsTag[]="DateTimePatterns%atTime"; -static const char DT_DateTimeCalendarTag[]="calendar"; -static const char DT_DateTimeGregorianTag[]="gregorian"; -static const char DT_DateTimeAppendItemsTag[]="appendItems"; -static const char DT_DateTimeFieldsTag[]="fields"; -static const char DT_DateTimeAvailableFormatsTag[]="availableFormats"; -//static const UnicodeString repeatedPattern=UnicodeString(repeatedPatterns); - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimePatternGenerator) -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTSkeletonEnumeration) -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTRedundantEnumeration) - -DateTimePatternGenerator* U_EXPORT2 -DateTimePatternGenerator::createInstance(UErrorCode& status) { - return createInstance(Locale::getDefault(), status); -} - -DateTimePatternGenerator* U_EXPORT2 -DateTimePatternGenerator::createInstance(const Locale& locale, UErrorCode& status) { - if (U_FAILURE(status)) { - return nullptr; - } - LocalPointer result( - new DateTimePatternGenerator(locale, status), status); - return U_SUCCESS(status) ? result.orphan() : nullptr; -} - -DateTimePatternGenerator* U_EXPORT2 -DateTimePatternGenerator::createInstanceNoStdPat(const Locale& locale, UErrorCode& status) { - if (U_FAILURE(status)) { - return nullptr; - } - LocalPointer result( - new DateTimePatternGenerator(locale, status, true), status); - return U_SUCCESS(status) ? result.orphan() : nullptr; -} - -DateTimePatternGenerator* U_EXPORT2 -DateTimePatternGenerator::createEmptyInstance(UErrorCode& status) { - if (U_FAILURE(status)) { - return nullptr; - } - LocalPointer result( - new DateTimePatternGenerator(status), status); - return U_SUCCESS(status) ? result.orphan() : nullptr; -} - -DateTimePatternGenerator::DateTimePatternGenerator(UErrorCode &status) : - skipMatcher(nullptr), - fAvailableFormatKeyHash(nullptr), - fDefaultHourFormatChar(0), - internalErrorCode(U_ZERO_ERROR) -{ - fp = new FormatParser(); - dtMatcher = new DateTimeMatcher(); - distanceInfo = new DistanceInfo(); - patternMap = new PatternMap(); - if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) { - internalErrorCode = status = U_MEMORY_ALLOCATION_ERROR; - } -} - -DateTimePatternGenerator::DateTimePatternGenerator(const Locale& locale, UErrorCode &status, UBool skipStdPatterns) : - skipMatcher(nullptr), - fAvailableFormatKeyHash(nullptr), - fDefaultHourFormatChar(0), - internalErrorCode(U_ZERO_ERROR) -{ - fp = new FormatParser(); - dtMatcher = new DateTimeMatcher(); - distanceInfo = new DistanceInfo(); - patternMap = new PatternMap(); - if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) { - internalErrorCode = status = U_MEMORY_ALLOCATION_ERROR; - } - else { - initData(locale, status, skipStdPatterns); - } -} - -DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerator& other) : - UObject(), - skipMatcher(nullptr), - fAvailableFormatKeyHash(nullptr), - fDefaultHourFormatChar(0), - internalErrorCode(U_ZERO_ERROR) -{ - fp = new FormatParser(); - dtMatcher = new DateTimeMatcher(); - distanceInfo = new DistanceInfo(); - patternMap = new PatternMap(); - if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) { - internalErrorCode = U_MEMORY_ALLOCATION_ERROR; - } - *this=other; -} - -DateTimePatternGenerator& -DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) { - // reflexive case - if (&other == this) { - return *this; - } - internalErrorCode = other.internalErrorCode; - pLocale = other.pLocale; - fDefaultHourFormatChar = other.fDefaultHourFormatChar; - *fp = *(other.fp); - dtMatcher->copyFrom(other.dtMatcher->skeleton); - *distanceInfo = *(other.distanceInfo); - for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { - dateTimeFormat[style] = other.dateTimeFormat[style]; - } - decimal = other.decimal; - for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { - dateTimeFormat[style].getTerminatedBuffer(); // NUL-terminate for the C API. - } - decimal.getTerminatedBuffer(); - delete skipMatcher; - if ( other.skipMatcher == nullptr ) { - skipMatcher = nullptr; - } - else { - skipMatcher = new DateTimeMatcher(*other.skipMatcher); - if (skipMatcher == nullptr) - { - internalErrorCode = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - } - for (int32_t i=0; i< UDATPG_FIELD_COUNT; ++i ) { - appendItemFormats[i] = other.appendItemFormats[i]; - appendItemFormats[i].getTerminatedBuffer(); // NUL-terminate for the C API. - for (int32_t j=0; j< UDATPG_WIDTH_COUNT; ++j ) { - fieldDisplayNames[i][j] = other.fieldDisplayNames[i][j]; - fieldDisplayNames[i][j].getTerminatedBuffer(); // NUL-terminate for the C API. - } - } - patternMap->copyFrom(*other.patternMap, internalErrorCode); - copyHashtable(other.fAvailableFormatKeyHash, internalErrorCode); - return *this; -} - - -bool -DateTimePatternGenerator::operator==(const DateTimePatternGenerator& other) const { - if (this == &other) { - return true; - } - if ((pLocale==other.pLocale) && (patternMap->equals(*other.patternMap)) && - (decimal==other.decimal)) { - for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { - if (dateTimeFormat[style] != other.dateTimeFormat[style]) { - return false; - } - } - for ( int32_t i=0 ; i list; - int32_t length = 0; - int32_t preferredFormat = ALLOWED_HOUR_FORMAT_UNKNOWN; - for (int32_t j = 0; formatList.getKeyAndValue(j, key, value); ++j) { - if (uprv_strcmp(key, "allowed") == 0) { - if (value.getType() == URES_STRING) { - length = 2; // 1 preferred to add later, 1 allowed to add now - if (list.allocateInsteadAndReset(length + 1) == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - list[1] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode)); - } - else { - ResourceArray allowedFormats = value.getArray(errorCode); - length = allowedFormats.getSize() + 1; // 1 preferred, getSize allowed - if (list.allocateInsteadAndReset(length + 1) == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - for (int32_t k = 1; k < length; ++k) { - allowedFormats.getValue(k-1, value); - list[k] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode)); - } - } - } else if (uprv_strcmp(key, "preferred") == 0) { - preferredFormat = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode)); - } - } - if (length > 1) { - list[0] = (preferredFormat!=ALLOWED_HOUR_FORMAT_UNKNOWN)? preferredFormat: list[1]; - } else { - // fallback handling for missing data - length = 2; // 1 preferred, 1 allowed - if (list.allocateInsteadAndReset(length + 1) == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - list[0] = (preferredFormat!=ALLOWED_HOUR_FORMAT_UNKNOWN)? preferredFormat: ALLOWED_HOUR_FORMAT_H; - list[1] = list[0]; - } - list[length] = ALLOWED_HOUR_FORMAT_UNKNOWN; - // At this point list[] will have at least two non-ALLOWED_HOUR_FORMAT_UNKNOWN entries, - // followed by ALLOWED_HOUR_FORMAT_UNKNOWN. - uhash_put(localeToAllowedHourFormatsMap, const_cast(regionOrLocale), list.orphan(), &errorCode); - if (U_FAILURE(errorCode)) { return; } - } - } - - AllowedHourFormat getHourFormatFromUnicodeString(const UnicodeString &s) { - if (s.length() == 1) { - if (s[0] == LOW_H) { return ALLOWED_HOUR_FORMAT_h; } - if (s[0] == CAP_H) { return ALLOWED_HOUR_FORMAT_H; } - if (s[0] == CAP_K) { return ALLOWED_HOUR_FORMAT_K; } - if (s[0] == LOW_K) { return ALLOWED_HOUR_FORMAT_k; } - } else if (s.length() == 2) { - if (s[0] == LOW_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_hb; } - if (s[0] == LOW_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_hB; } - if (s[0] == CAP_K && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_Kb; } - if (s[0] == CAP_K && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_KB; } - if (s[0] == CAP_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_Hb; } - if (s[0] == CAP_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_HB; } - } - - return ALLOWED_HOUR_FORMAT_UNKNOWN; - } -}; - -} // namespace - -AllowedHourFormatsSink::~AllowedHourFormatsSink() {} - -U_CFUNC void U_CALLCONV DateTimePatternGenerator::loadAllowedHourFormatsData(UErrorCode &status) { - if (U_FAILURE(status)) { return; } - localeToAllowedHourFormatsMap = uhash_open( - uhash_hashChars, uhash_compareChars, nullptr, &status); - if (U_FAILURE(status)) { return; } - - uhash_setValueDeleter(localeToAllowedHourFormatsMap, deleteAllowedHourFormats); - ucln_i18n_registerCleanup(UCLN_I18N_ALLOWED_HOUR_FORMATS, allowedHourFormatsCleanup); - - LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "supplementalData", &status)); - if (U_FAILURE(status)) { return; } - - AllowedHourFormatsSink sink; - // TODO: Currently in the enumeration each table allocates a new array. - // Try to reduce the number of memory allocations. Consider storing a - // UVector32 with the concatenation of all of the sub-arrays, put the start index - // into the hashmap, store 6 single-value sub-arrays right at the beginning of the - // vector (at index enum*2) for easy data sharing, copy sub-arrays into runtime - // object. Remember to clean up the vector, too. - ures_getAllItemsWithFallback(rb.getAlias(), "timeData", sink, status); -} - -static int32_t* getAllowedHourFormatsLangCountry(const char* language, const char* country, UErrorCode& status) { - CharString langCountry; - langCountry.append(language, status); - langCountry.append('_', status); - langCountry.append(country, status); - - int32_t* allowedFormats; - allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, langCountry.data()); - if (allowedFormats == nullptr) { - allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, const_cast(country)); - } - - return allowedFormats; -} - -void DateTimePatternGenerator::getAllowedHourFormats(const Locale &locale, UErrorCode &status) { - if (U_FAILURE(status)) { return; } - - const char *language = locale.getLanguage(); - const char *country = locale.getCountry(); - - char regionOverride[8]; - int32_t regionOverrideLength = locale.getKeywordValue("rg", regionOverride, sizeof(regionOverride), status); - if (U_SUCCESS(status) && regionOverrideLength > 0) { - country = regionOverride; - if (regionOverrideLength > 2) { - // chop off any subdivision codes that may have been included - regionOverride[2] = '\0'; - } - } - - Locale maxLocale; // must be here for correct lifetime - if (*language == '\0' || *country == '\0') { - maxLocale = locale; - UErrorCode localStatus = U_ZERO_ERROR; - maxLocale.addLikelySubtags(localStatus); - if (U_SUCCESS(localStatus)) { - language = maxLocale.getLanguage(); - country = maxLocale.getCountry(); - } - } - if (*language == '\0') { - // Unexpected, but fail gracefully - language = "und"; - } - if (*country == '\0') { - country = "001"; - } - - int32_t* allowedFormats = getAllowedHourFormatsLangCountry(language, country, status); - - // We need to check if there is an hour cycle on locale - char buffer[8]; - int32_t count = locale.getKeywordValue("hours", buffer, sizeof(buffer), status); - - fDefaultHourFormatChar = 0; - if (U_SUCCESS(status) && count > 0) { - if(uprv_strcmp(buffer, "h24") == 0) { - fDefaultHourFormatChar = LOW_K; - } else if(uprv_strcmp(buffer, "h23") == 0) { - fDefaultHourFormatChar = CAP_H; - } else if(uprv_strcmp(buffer, "h12") == 0) { - fDefaultHourFormatChar = LOW_H; - } else if(uprv_strcmp(buffer, "h11") == 0) { - fDefaultHourFormatChar = CAP_K; - } - } - - // Check if the region has an alias - if (allowedFormats == nullptr) { - UErrorCode localStatus = U_ZERO_ERROR; - const Region* region = Region::getInstance(country, localStatus); - if (U_SUCCESS(localStatus)) { - country = region->getRegionCode(); // the real region code - allowedFormats = getAllowedHourFormatsLangCountry(language, country, status); - } - } - - if (allowedFormats != nullptr) { // Lookup is successful - // Here allowedFormats points to a list consisting of key for preferredFormat, - // followed by one or more keys for allowedFormats, then followed by ALLOWED_HOUR_FORMAT_UNKNOWN. - if (!fDefaultHourFormatChar) { - switch (allowedFormats[0]) { - case ALLOWED_HOUR_FORMAT_h: fDefaultHourFormatChar = LOW_H; break; - case ALLOWED_HOUR_FORMAT_H: fDefaultHourFormatChar = CAP_H; break; - case ALLOWED_HOUR_FORMAT_K: fDefaultHourFormatChar = CAP_K; break; - case ALLOWED_HOUR_FORMAT_k: fDefaultHourFormatChar = LOW_K; break; - default: fDefaultHourFormatChar = CAP_H; break; - } - } - - for (int32_t i = 0; i < UPRV_LENGTHOF(fAllowedHourFormats); ++i) { - fAllowedHourFormats[i] = allowedFormats[i + 1]; - if (fAllowedHourFormats[i] == ALLOWED_HOUR_FORMAT_UNKNOWN) { - break; - } - } - } else { // Lookup failed, twice - if (!fDefaultHourFormatChar) { - fDefaultHourFormatChar = CAP_H; - } - fAllowedHourFormats[0] = ALLOWED_HOUR_FORMAT_H; - fAllowedHourFormats[1] = ALLOWED_HOUR_FORMAT_UNKNOWN; - } -} - -UDateFormatHourCycle -DateTimePatternGenerator::getDefaultHourCycle(UErrorCode& status) const { - if (U_FAILURE(status)) { - return UDAT_HOUR_CYCLE_23; - } - if (fDefaultHourFormatChar == 0) { - // We need to return something, but the caller should ignore it - // anyways since the returned status is a failure. - status = U_UNSUPPORTED_ERROR; - return UDAT_HOUR_CYCLE_23; - } - switch (fDefaultHourFormatChar) { - case CAP_K: - return UDAT_HOUR_CYCLE_11; - case LOW_H: - return UDAT_HOUR_CYCLE_12; - case CAP_H: - return UDAT_HOUR_CYCLE_23; - case LOW_K: - return UDAT_HOUR_CYCLE_24; - default: - UPRV_UNREACHABLE_EXIT; - } -} - -UnicodeString -DateTimePatternGenerator::getSkeleton(const UnicodeString& pattern, UErrorCode& -/*status*/) { - FormatParser fp2; - DateTimeMatcher matcher; - PtnSkeleton localSkeleton; - matcher.set(pattern, &fp2, localSkeleton); - return localSkeleton.getSkeleton(); -} - -UnicodeString -DateTimePatternGenerator::staticGetSkeleton( - const UnicodeString& pattern, UErrorCode& /*status*/) { - FormatParser fp; - DateTimeMatcher matcher; - PtnSkeleton localSkeleton; - matcher.set(pattern, &fp, localSkeleton); - return localSkeleton.getSkeleton(); -} - -UnicodeString -DateTimePatternGenerator::getBaseSkeleton(const UnicodeString& pattern, UErrorCode& /*status*/) { - FormatParser fp2; - DateTimeMatcher matcher; - PtnSkeleton localSkeleton; - matcher.set(pattern, &fp2, localSkeleton); - return localSkeleton.getBaseSkeleton(); -} - -UnicodeString -DateTimePatternGenerator::staticGetBaseSkeleton( - const UnicodeString& pattern, UErrorCode& /*status*/) { - FormatParser fp; - DateTimeMatcher matcher; - PtnSkeleton localSkeleton; - matcher.set(pattern, &fp, localSkeleton); - return localSkeleton.getBaseSkeleton(); -} - -void -DateTimePatternGenerator::addICUPatterns(const Locale& locale, UErrorCode& status) { - if (U_FAILURE(status)) { return; } - UnicodeString dfPattern; - UnicodeString conflictingString; - DateFormat* df; - - // Load with ICU patterns - for (int32_t i=DateFormat::kFull; i<=DateFormat::kShort; i++) { - DateFormat::EStyle style = (DateFormat::EStyle)i; - df = DateFormat::createDateInstance(style, locale); - SimpleDateFormat* sdf; - if (df != nullptr && (sdf = dynamic_cast(df)) != nullptr) { - sdf->toPattern(dfPattern); - addPattern(dfPattern, false, conflictingString, status); - } - // TODO Maybe we should return an error when the date format isn't simple. - delete df; - if (U_FAILURE(status)) { return; } - - df = DateFormat::createTimeInstance(style, locale); - if (df != nullptr && (sdf = dynamic_cast(df)) != nullptr) { - sdf->toPattern(dfPattern); - addPattern(dfPattern, false, conflictingString, status); - - // TODO: C++ and Java are inconsistent (see #12568). - // C++ uses MEDIUM, but Java uses SHORT. - if ( i==DateFormat::kShort && !dfPattern.isEmpty() ) { - consumeShortTimePattern(dfPattern, status); - } - } - // TODO Maybe we should return an error when the date format isn't simple. - delete df; - if (U_FAILURE(status)) { return; } - } -} - -void -DateTimePatternGenerator::hackTimes(const UnicodeString& hackPattern, UErrorCode& status) { - UnicodeString conflictingString; - - fp->set(hackPattern); - UnicodeString mmss; - UBool gotMm=false; - for (int32_t i=0; iitemNumber; ++i) { - UnicodeString field = fp->items[i]; - if ( fp->isQuoteLiteral(field) ) { - if ( gotMm ) { - UnicodeString quoteLiteral; - fp->getQuoteLiteral(quoteLiteral, &i); - mmss += quoteLiteral; - } - } - else { - if (fp->isPatternSeparator(field) && gotMm) { - mmss+=field; - } - else { - UChar ch=field.charAt(0); - if (ch==LOW_M) { - gotMm=true; - mmss+=field; - } - else { - if (ch==LOW_S) { - if (!gotMm) { - break; - } - mmss+= field; - addPattern(mmss, false, conflictingString, status); - break; - } - else { - if (gotMm || ch==LOW_Z || ch==CAP_Z || ch==LOW_V || ch==CAP_V) { - break; - } - } - } - } - } - } -} - -#define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY) - -void -DateTimePatternGenerator::getCalendarTypeToUse(const Locale& locale, CharString& destination, UErrorCode& err) { - destination.clear().append(DT_DateTimeGregorianTag, -1, err); // initial default - if ( U_SUCCESS(err) ) { - UErrorCode localStatus = U_ZERO_ERROR; - char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY]; - // obtain a locale that always has the calendar key value that should be used - ures_getFunctionalEquivalent( - localeWithCalendarKey, - ULOC_LOCALE_IDENTIFIER_CAPACITY, - nullptr, - "calendar", - "calendar", - locale.getName(), - nullptr, - false, - &localStatus); - localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination - // now get the calendar key value from that locale - char calendarType[ULOC_KEYWORDS_CAPACITY]; - int32_t calendarTypeLen = uloc_getKeywordValue( - localeWithCalendarKey, - "calendar", - calendarType, - ULOC_KEYWORDS_CAPACITY, - &localStatus); - // If the input locale was invalid, don't fail with missing resource error, instead - // continue with default of Gregorian. - if (U_FAILURE(localStatus) && localStatus != U_MISSING_RESOURCE_ERROR) { - err = localStatus; - return; - } - if (calendarTypeLen > 0 && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) { - destination.clear().append(calendarType, -1, err); - if (U_FAILURE(err)) { return; } - } - } -} - -void -DateTimePatternGenerator::consumeShortTimePattern(const UnicodeString& shortTimePattern, - UErrorCode& status) { - if (U_FAILURE(status)) { return; } - // ICU-20383 No longer set fDefaultHourFormatChar to the hour format character from - // this pattern; instead it is set from localeToAllowedHourFormatsMap which now - // includes entries for both preferred and allowed formats. - - // HACK for hh:ss - hackTimes(shortTimePattern, status); -} - -struct DateTimePatternGenerator::AppendItemFormatsSink : public ResourceSink { - - // Destination for data, modified via setters. - DateTimePatternGenerator& dtpg; - - AppendItemFormatsSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {} - virtual ~AppendItemFormatsSink(); - - virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, - UErrorCode &errorCode) override { - UDateTimePatternField field = dtpg.getAppendFormatNumber(key); - if (field == UDATPG_FIELD_COUNT) { return; } - const UnicodeString& valueStr = value.getUnicodeString(errorCode); - if (dtpg.getAppendItemFormat(field).isEmpty() && !valueStr.isEmpty()) { - dtpg.setAppendItemFormat(field, valueStr); - } - } - - void fillInMissing() { - UnicodeString defaultItemFormat(true, UDATPG_ItemFormat, UPRV_LENGTHOF(UDATPG_ItemFormat)-1); // Read-only alias. - for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) { - UDateTimePatternField field = (UDateTimePatternField)i; - if (dtpg.getAppendItemFormat(field).isEmpty()) { - dtpg.setAppendItemFormat(field, defaultItemFormat); - } - } - } -}; - -struct DateTimePatternGenerator::AppendItemNamesSink : public ResourceSink { - - // Destination for data, modified via setters. - DateTimePatternGenerator& dtpg; - - AppendItemNamesSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {} - virtual ~AppendItemNamesSink(); - - virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, - UErrorCode &errorCode) override { - UDateTimePGDisplayWidth width; - UDateTimePatternField field = dtpg.getFieldAndWidthIndices(key, &width); - if (field == UDATPG_FIELD_COUNT) { return; } - ResourceTable detailsTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - if (!detailsTable.findValue("dn", value)) { return; } - const UnicodeString& valueStr = value.getUnicodeString(errorCode); - if (U_SUCCESS(errorCode) && dtpg.getFieldDisplayName(field,width).isEmpty() && !valueStr.isEmpty()) { - dtpg.setFieldDisplayName(field,width,valueStr); - } - } - - void fillInMissing() { - for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) { - UnicodeString& valueStr = dtpg.getMutableFieldDisplayName((UDateTimePatternField)i, UDATPG_WIDE); - if (valueStr.isEmpty()) { - valueStr = CAP_F; - U_ASSERT(i < 20); - if (i < 10) { - // F0, F1, ..., F9 - valueStr += (UChar)(i+0x30); - } else { - // F10, F11, ... - valueStr += (UChar)0x31; - valueStr += (UChar)(i-10 + 0x30); - } - // NUL-terminate for the C API. - valueStr.getTerminatedBuffer(); - } - for (int32_t j = 1; j < UDATPG_WIDTH_COUNT; j++) { - UnicodeString& valueStr2 = dtpg.getMutableFieldDisplayName((UDateTimePatternField)i, (UDateTimePGDisplayWidth)j); - if (valueStr2.isEmpty()) { - valueStr2 = dtpg.getFieldDisplayName((UDateTimePatternField)i, (UDateTimePGDisplayWidth)(j-1)); - } - } - } - } -}; - -struct DateTimePatternGenerator::AvailableFormatsSink : public ResourceSink { - - // Destination for data, modified via setters. - DateTimePatternGenerator& dtpg; - - // Temporary variable, required for calling addPatternWithSkeleton. - UnicodeString conflictingPattern; - - AvailableFormatsSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {} - virtual ~AvailableFormatsSink(); - - virtual void put(const char *key, ResourceValue &value, UBool isRoot, - UErrorCode &errorCode) override { - const UnicodeString formatKey(key, -1, US_INV); - if (!dtpg.isAvailableFormatSet(formatKey) ) { - dtpg.setAvailableFormat(formatKey, errorCode); - // Add pattern with its associated skeleton. Override any duplicate - // derived from std patterns, but not a previous availableFormats entry: - const UnicodeString& formatValue = value.getUnicodeString(errorCode); - conflictingPattern.remove(); - dtpg.addPatternWithSkeleton(formatValue, &formatKey, !isRoot, conflictingPattern, errorCode); - } - } -}; - -// Virtual destructors must be defined out of line. -DateTimePatternGenerator::AppendItemFormatsSink::~AppendItemFormatsSink() {} -DateTimePatternGenerator::AppendItemNamesSink::~AppendItemNamesSink() {} -DateTimePatternGenerator::AvailableFormatsSink::~AvailableFormatsSink() {} - -void -DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& errorCode) { - if (U_FAILURE(errorCode)) { return; } - UnicodeString rbPattern, value, field; - CharString path; - - LocalUResourceBundlePointer rb(ures_open(nullptr, locale.getName(), &errorCode)); - if (U_FAILURE(errorCode)) { return; } - - CharString calendarTypeToUse; // to be filled in with the type to use, if all goes well - getCalendarTypeToUse(locale, calendarTypeToUse, errorCode); - if (U_FAILURE(errorCode)) { return; } - - // Local err to ignore resource not found exceptions - UErrorCode err = U_ZERO_ERROR; - - // Load append item formats. - AppendItemFormatsSink appendItemFormatsSink(*this); - path.clear() - .append(DT_DateTimeCalendarTag, errorCode) - .append('/', errorCode) - .append(calendarTypeToUse, errorCode) - .append('/', errorCode) - .append(DT_DateTimeAppendItemsTag, errorCode); // i.e., calendar/xxx/appendItems - if (U_FAILURE(errorCode)) { return; } - ures_getAllChildrenWithFallback(rb.getAlias(), path.data(), appendItemFormatsSink, err); - appendItemFormatsSink.fillInMissing(); - - // Load CLDR item names. - err = U_ZERO_ERROR; - AppendItemNamesSink appendItemNamesSink(*this); - ures_getAllChildrenWithFallback(rb.getAlias(), DT_DateTimeFieldsTag, appendItemNamesSink, err); - appendItemNamesSink.fillInMissing(); - - // Load the available formats from CLDR. - err = U_ZERO_ERROR; - initHashtable(errorCode); - if (U_FAILURE(errorCode)) { return; } - AvailableFormatsSink availableFormatsSink(*this); - path.clear() - .append(DT_DateTimeCalendarTag, errorCode) - .append('/', errorCode) - .append(calendarTypeToUse, errorCode) - .append('/', errorCode) - .append(DT_DateTimeAvailableFormatsTag, errorCode); // i.e., calendar/xxx/availableFormats - if (U_FAILURE(errorCode)) { return; } - ures_getAllChildrenWithFallback(rb.getAlias(), path.data(), availableFormatsSink, err); -} - -void -DateTimePatternGenerator::initHashtable(UErrorCode& err) { - if (U_FAILURE(err)) { return; } - if (fAvailableFormatKeyHash!=nullptr) { - return; - } - LocalPointer hash(new Hashtable(false, err), err); - if (U_SUCCESS(err)) { - fAvailableFormatKeyHash = hash.orphan(); - } -} - -void -DateTimePatternGenerator::setAppendItemFormat(UDateTimePatternField field, const UnicodeString& value) { - appendItemFormats[field] = value; - // NUL-terminate for the C API. - appendItemFormats[field].getTerminatedBuffer(); -} - -const UnicodeString& -DateTimePatternGenerator::getAppendItemFormat(UDateTimePatternField field) const { - return appendItemFormats[field]; -} - -void -DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field, const UnicodeString& value) { - setFieldDisplayName(field, UDATPG_WIDTH_APPENDITEM, value); -} - -const UnicodeString& -DateTimePatternGenerator::getAppendItemName(UDateTimePatternField field) const { - return fieldDisplayNames[field][UDATPG_WIDTH_APPENDITEM]; -} - -void -DateTimePatternGenerator::setFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width, const UnicodeString& value) { - fieldDisplayNames[field][width] = value; - // NUL-terminate for the C API. - fieldDisplayNames[field][width].getTerminatedBuffer(); -} - -UnicodeString -DateTimePatternGenerator::getFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width) const { - return fieldDisplayNames[field][width]; -} - -UnicodeString& -DateTimePatternGenerator::getMutableFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width) { - return fieldDisplayNames[field][width]; -} - -void -DateTimePatternGenerator::getAppendName(UDateTimePatternField field, UnicodeString& value) { - value = SINGLE_QUOTE; - value += fieldDisplayNames[field][UDATPG_WIDTH_APPENDITEM]; - value += SINGLE_QUOTE; -} - -UnicodeString -DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UErrorCode& status) { - return getBestPattern(patternForm, UDATPG_MATCH_NO_OPTIONS, status); -} - -UnicodeString -DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDateTimePatternMatchOptions options, UErrorCode& status) { - if (U_FAILURE(status)) { - return UnicodeString(); - } - if (U_FAILURE(internalErrorCode)) { - status = internalErrorCode; - return UnicodeString(); - } - const UnicodeString *bestPattern = nullptr; - UnicodeString dtFormat; - UnicodeString resultPattern; - int32_t flags = kDTPGNoFlags; - - int32_t dateMask=(1<set(patternFormMapped, fp); - const PtnSkeleton* specifiedSkeleton = nullptr; - bestPattern=getBestRaw(*dtMatcher, -1, distanceInfo, status, &specifiedSkeleton); - if (U_FAILURE(status)) { - return UnicodeString(); - } - - if ( distanceInfo->missingFieldMask==0 && distanceInfo->extraFieldMask==0 ) { - resultPattern = adjustFieldTypes(*bestPattern, specifiedSkeleton, flags, options); - - return resultPattern; - } - int32_t neededFields = dtMatcher->getFieldMask(); - UnicodeString datePattern=getBestAppending(neededFields & dateMask, flags, status, options); - UnicodeString timePattern=getBestAppending(neededFields & timeMask, flags, status, options); - if (U_FAILURE(status)) { - return UnicodeString(); - } - if (datePattern.length()==0) { - if (timePattern.length()==0) { - resultPattern.remove(); - } - else { - return timePattern; - } - } - if (timePattern.length()==0) { - return datePattern; - } - resultPattern.remove(); - status = U_ZERO_ERROR; - // determine which dateTimeFormat to use - PtnSkeleton* reqSkeleton = dtMatcher->getSkeletonPtr(); - UDateFormatStyle style = UDAT_SHORT; - int32_t monthFieldLen = reqSkeleton->baseOriginal.getFieldLength(UDATPG_MONTH_FIELD); - if (monthFieldLen == 4) { - if (reqSkeleton->baseOriginal.getFieldLength(UDATPG_WEEKDAY_FIELD) > 0) { - style = UDAT_FULL; - } else { - style = UDAT_LONG; - } - } else if (monthFieldLen == 3) { - style = UDAT_MEDIUM; - } - // and now use it to compose date and time - dtFormat=getDateTimeFormat(style, status); - SimpleFormatter(dtFormat, 2, 2, status).format(timePattern, datePattern, resultPattern, status); - return resultPattern; -} - -/* - * Map a skeleton that may have metacharacters jJC to one without, by replacing - * the metacharacters with locale-appropriate fields of h/H/k/K and of a/b/B - * (depends on fDefaultHourFormatChar and fAllowedHourFormats being set, which in - * turn depends on initData having been run). This method also updates the flags - * as necessary. Returns the updated skeleton. - */ -UnicodeString -DateTimePatternGenerator::mapSkeletonMetacharacters(const UnicodeString& patternForm, int32_t* flags, UErrorCode& status) { - UnicodeString patternFormMapped; - patternFormMapped.remove(); - UBool inQuoted = false; - int32_t patPos, patLen = patternForm.length(); - for (patPos = 0; patPos < patLen; patPos++) { - UChar patChr = patternForm.charAt(patPos); - if (patChr == SINGLE_QUOTE) { - inQuoted = !inQuoted; - } else if (!inQuoted) { - // Handle special mappings for 'j' and 'C' in which fields lengths - // 1,3,5 => hour field length 1 - // 2,4,6 => hour field length 2 - // 1,2 => abbreviated dayPeriod (field length 1..3) - // 3,4 => long dayPeriod (field length 4) - // 5,6 => narrow dayPeriod (field length 5) - if (patChr == LOW_J || patChr == CAP_C) { - int32_t extraLen = 0; // 1 less than total field length - while (patPos+1 < patLen && patternForm.charAt(patPos+1)==patChr) { - extraLen++; - patPos++; - } - int32_t hourLen = 1 + (extraLen & 1); - int32_t dayPeriodLen = (extraLen < 2)? 1: 3 + (extraLen >> 1); - UChar hourChar = LOW_H; - UChar dayPeriodChar = LOW_A; - if (patChr == LOW_J) { - hourChar = fDefaultHourFormatChar; - } else { - AllowedHourFormat bestAllowed; - if (fAllowedHourFormats[0] != ALLOWED_HOUR_FORMAT_UNKNOWN) { - bestAllowed = (AllowedHourFormat)fAllowedHourFormats[0]; - } else { - status = U_INVALID_FORMAT_ERROR; - return UnicodeString(); - } - if (bestAllowed == ALLOWED_HOUR_FORMAT_H || bestAllowed == ALLOWED_HOUR_FORMAT_HB || bestAllowed == ALLOWED_HOUR_FORMAT_Hb) { - hourChar = CAP_H; - } else if (bestAllowed == ALLOWED_HOUR_FORMAT_K || bestAllowed == ALLOWED_HOUR_FORMAT_KB || bestAllowed == ALLOWED_HOUR_FORMAT_Kb) { - hourChar = CAP_K; - } else if (bestAllowed == ALLOWED_HOUR_FORMAT_k) { - hourChar = LOW_K; - } - // in #13183 just add b/B to skeleton, no longer need to set special flags - if (bestAllowed == ALLOWED_HOUR_FORMAT_HB || bestAllowed == ALLOWED_HOUR_FORMAT_hB || bestAllowed == ALLOWED_HOUR_FORMAT_KB) { - dayPeriodChar = CAP_B; - } else if (bestAllowed == ALLOWED_HOUR_FORMAT_Hb || bestAllowed == ALLOWED_HOUR_FORMAT_hb || bestAllowed == ALLOWED_HOUR_FORMAT_Kb) { - dayPeriodChar = LOW_B; - } - } - if (hourChar==CAP_H || hourChar==LOW_K) { - dayPeriodLen = 0; - } - while (dayPeriodLen-- > 0) { - patternFormMapped.append(dayPeriodChar); - } - while (hourLen-- > 0) { - patternFormMapped.append(hourChar); - } - } else if (patChr == CAP_J) { - // Get pattern for skeleton with H, then replace H or k - // with fDefaultHourFormatChar (if different) - patternFormMapped.append(CAP_H); - *flags |= kDTPGSkeletonUsesCapJ; - } else { - patternFormMapped.append(patChr); - } - } - } - return patternFormMapped; -} - -UnicodeString -DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern, - const UnicodeString& skeleton, - UErrorCode& status) { - return replaceFieldTypes(pattern, skeleton, UDATPG_MATCH_NO_OPTIONS, status); -} - -UnicodeString -DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern, - const UnicodeString& skeleton, - UDateTimePatternMatchOptions options, - UErrorCode& status) { - if (U_FAILURE(status)) { - return UnicodeString(); - } - if (U_FAILURE(internalErrorCode)) { - status = internalErrorCode; - return UnicodeString(); - } - dtMatcher->set(skeleton, fp); - UnicodeString result = adjustFieldTypes(pattern, nullptr, kDTPGNoFlags, options); - return result; -} - -void -DateTimePatternGenerator::setDecimal(const UnicodeString& newDecimal) { - this->decimal = newDecimal; - // NUL-terminate for the C API. - this->decimal.getTerminatedBuffer(); -} - -const UnicodeString& -DateTimePatternGenerator::getDecimal() const { - return decimal; -} - -void -DateTimePatternGenerator::addCanonicalItems(UErrorCode& status) { - if (U_FAILURE(status)) { return; } - UnicodeString conflictingPattern; - - for (int32_t i=0; i 0) { - addPattern(UnicodeString(Canonical_Items[i]), false, conflictingPattern, status); - } - if (U_FAILURE(status)) { return; } - } -} - -void -DateTimePatternGenerator::setDateTimeFormat(const UnicodeString& dtFormat) { - UErrorCode status = U_ZERO_ERROR; - for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { - setDateTimeFormat((UDateFormatStyle)style, dtFormat, status); - } -} - -const UnicodeString& -DateTimePatternGenerator::getDateTimeFormat() const { - UErrorCode status = U_ZERO_ERROR; - return getDateTimeFormat(UDAT_MEDIUM, status); -} - -void -DateTimePatternGenerator::setDateTimeFormat(UDateFormatStyle style, const UnicodeString& dtFormat, UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - if (style < UDAT_FULL || style > UDAT_SHORT) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - dateTimeFormat[style] = dtFormat; - // Note for the following: getTerminatedBuffer() can re-allocate the UnicodeString - // buffer so we do this here before clients request a const ref to the UnicodeString - // or its buffer. - dateTimeFormat[style].getTerminatedBuffer(); // NUL-terminate for the C API. -} - -const UnicodeString& -DateTimePatternGenerator::getDateTimeFormat(UDateFormatStyle style, UErrorCode& status) const { - static const UnicodeString emptyString = UNICODE_STRING_SIMPLE(""); - if (U_FAILURE(status)) { - return emptyString; - } - if (style < UDAT_FULL || style > UDAT_SHORT) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return emptyString; - } - return dateTimeFormat[style]; -} - -static const int32_t cTypeBufMax = 32; - -void -DateTimePatternGenerator::setDateTimeFromCalendar(const Locale& locale, UErrorCode& status) { - if (U_FAILURE(status)) { return; } - - const UChar *resStr; - int32_t resStrLen = 0; - - LocalUResourceBundlePointer calData(ures_open(nullptr, locale.getBaseName(), &status)); - if (U_FAILURE(status)) { return; } - ures_getByKey(calData.getAlias(), DT_DateTimeCalendarTag, calData.getAlias(), &status); - if (U_FAILURE(status)) { return; } - - char cType[cTypeBufMax + 1]; - Calendar::getCalendarTypeFromLocale(locale, cType, cTypeBufMax, status); - cType[cTypeBufMax] = 0; - if (U_FAILURE(status) || cType[0] == 0) { - status = U_ZERO_ERROR; - uprv_strcpy(cType, DT_DateTimeGregorianTag); - } - UBool cTypeIsGregorian = (uprv_strcmp(cType, DT_DateTimeGregorianTag) == 0); - - // Currently, for compatibility with pre-CLDR-42 data, we default to the "atTime" - // combining patterns. Depending on guidance in CLDR 42 spec and on DisplayOptions, - // we may change this. - LocalUResourceBundlePointer specificCalBundle; - LocalUResourceBundlePointer dateTimePatterns; - int32_t dateTimeOffset = 0; // initially for DateTimePatterns%atTime - if (!cTypeIsGregorian) { - specificCalBundle.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), cType, - nullptr, &status)); - dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(specificCalBundle.getAlias(), DT_DateAtTimePatternsTag, // the %atTime variant, 4 entries - nullptr, &status)); - } - if (dateTimePatterns.isNull() || status == U_MISSING_RESOURCE_ERROR) { - status = U_ZERO_ERROR; - specificCalBundle.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), DT_DateTimeGregorianTag, - nullptr, &status)); - dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(specificCalBundle.getAlias(), DT_DateAtTimePatternsTag, // the %atTime variant, 4 entries - nullptr, &status)); - } - if (U_SUCCESS(status) && (ures_getSize(dateTimePatterns.getAlias()) < 4)) { - status = U_INVALID_FORMAT_ERROR; - } - if (status == U_MISSING_RESOURCE_ERROR) { - // Try again with standard variant - status = U_ZERO_ERROR; - dateTimePatterns.orphan(); - dateTimeOffset = (int32_t)DateFormat::kDateTimeOffset; - if (!cTypeIsGregorian) { - specificCalBundle.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), cType, - nullptr, &status)); - dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(specificCalBundle.getAlias(), DT_DateTimePatternsTag, // the standard variant, 13 entries - nullptr, &status)); - } - if (dateTimePatterns.isNull() || status == U_MISSING_RESOURCE_ERROR) { - status = U_ZERO_ERROR; - specificCalBundle.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), DT_DateTimeGregorianTag, - nullptr, &status)); - dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(specificCalBundle.getAlias(), DT_DateTimePatternsTag, // the standard variant, 13 entries - nullptr, &status)); - } - if (U_SUCCESS(status) && (ures_getSize(dateTimePatterns.getAlias()) <= DateFormat::kDateTimeOffset + DateFormat::kShort)) { - status = U_INVALID_FORMAT_ERROR; - } - } - if (U_FAILURE(status)) { return; } - for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { - resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), dateTimeOffset + style, &resStrLen, &status); - setDateTimeFormat((UDateFormatStyle)style, UnicodeString(true, resStr, resStrLen), status); - } -} - -void -DateTimePatternGenerator::setDecimalSymbols(const Locale& locale, UErrorCode& status) { - DecimalFormatSymbols dfs = DecimalFormatSymbols(locale, status); - if(U_SUCCESS(status)) { - decimal = dfs.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); - // NUL-terminate for the C API. - decimal.getTerminatedBuffer(); - } -} - -UDateTimePatternConflict -DateTimePatternGenerator::addPattern( - const UnicodeString& pattern, - UBool override, - UnicodeString &conflictingPattern, - UErrorCode& status) -{ - if (U_FAILURE(internalErrorCode)) { - status = internalErrorCode; - return UDATPG_NO_CONFLICT; - } - - return addPatternWithSkeleton(pattern, nullptr, override, conflictingPattern, status); -} - -// For DateTimePatternGenerator::addPatternWithSkeleton - -// If skeletonToUse is specified, then an availableFormats entry is being added. In this case: -// 1. We pass that skeleton to matcher.set instead of having it derive a skeleton from the pattern. -// 2. If the new entry's skeleton or basePattern does match an existing entry but that entry also had a skeleton specified -// (i.e. it was also from availableFormats), then the new entry does not override it regardless of the value of the override -// parameter. This prevents later availableFormats entries from a parent locale overriding earlier ones from the actual -// specified locale. However, availableFormats entries *should* override entries with matching skeleton whose skeleton was -// derived (i.e. entries derived from the standard date/time patters for the specified locale). -// 3. When adding the pattern (patternMap->add), we set a new boolean to indicate that the added entry had a -// specified skeleton (which sets a new field in the PtnElem in the PatternMap). -UDateTimePatternConflict -DateTimePatternGenerator::addPatternWithSkeleton( - const UnicodeString& pattern, - const UnicodeString* skeletonToUse, - UBool override, - UnicodeString& conflictingPattern, - UErrorCode& status) -{ - if (U_FAILURE(internalErrorCode)) { - status = internalErrorCode; - return UDATPG_NO_CONFLICT; - } - - UnicodeString basePattern; - PtnSkeleton skeleton; - UDateTimePatternConflict conflictingStatus = UDATPG_NO_CONFLICT; - - DateTimeMatcher matcher; - if ( skeletonToUse == nullptr ) { - matcher.set(pattern, fp, skeleton); - matcher.getBasePattern(basePattern); - } else { - matcher.set(*skeletonToUse, fp, skeleton); // no longer trims skeleton fields to max len 3, per #7930 - matcher.getBasePattern(basePattern); // or perhaps instead: basePattern = *skeletonToUse; - } - // We only care about base conflicts - and replacing the pattern associated with a base - if: - // 1. the conflicting previous base pattern did *not* have an explicit skeleton; in that case the previous - // base + pattern combination was derived from either (a) a canonical item, (b) a standard format, or - // (c) a pattern specified programmatically with a previous call to addPattern (which would only happen - // if we are getting here from a subsequent call to addPattern). - // 2. a skeleton is specified for the current pattern, but override=false; in that case we are checking - // availableFormats items from root, which should not override any previous entry with the same base. - UBool entryHadSpecifiedSkeleton; - const UnicodeString *duplicatePattern = patternMap->getPatternFromBasePattern(basePattern, entryHadSpecifiedSkeleton); - if (duplicatePattern != nullptr && (!entryHadSpecifiedSkeleton || (skeletonToUse != nullptr && !override))) { - conflictingStatus = UDATPG_BASE_CONFLICT; - conflictingPattern = *duplicatePattern; - if (!override) { - return conflictingStatus; - } - } - // The only time we get here with override=true and skeletonToUse!=null is when adding availableFormats - // items from CLDR data. In that case, we don't want an item from a parent locale to replace an item with - // same skeleton from the specified locale, so skip the current item if skeletonWasSpecified is true for - // the previously-specified conflicting item. - const PtnSkeleton* entrySpecifiedSkeleton = nullptr; - duplicatePattern = patternMap->getPatternFromSkeleton(skeleton, &entrySpecifiedSkeleton); - if (duplicatePattern != nullptr ) { - conflictingStatus = UDATPG_CONFLICT; - conflictingPattern = *duplicatePattern; - if (!override || (skeletonToUse != nullptr && entrySpecifiedSkeleton != nullptr)) { - return conflictingStatus; - } - } - patternMap->add(basePattern, skeleton, pattern, skeletonToUse != nullptr, status); - if(U_FAILURE(status)) { - return conflictingStatus; - } - - return UDATPG_NO_CONFLICT; -} - - -UDateTimePatternField -DateTimePatternGenerator::getAppendFormatNumber(const char* field) const { - for (int32_t i=0; i0; --i) { - if (uprv_strcmp(CLDR_FIELD_WIDTH[i], hyphenPtr)==0) { - *widthP=(UDateTimePGDisplayWidth)i; - break; - } - } - *hyphenPtr = 0; // now delete width portion of key - } - for (int32_t i=0; igetPatternFromSkeleton(*trial.getSkeletonPtr(), &specifiedSkeleton); - missingFields->setTo(tempInfo); - if (distance==0) { - break; - } - } - } - - // If the best raw match had a specified skeleton and that skeleton was requested by the caller, - // then return it too. This generally happens when the caller needs to pass that skeleton - // through to adjustFieldTypes so the latter can do a better job. - if (bestPattern && specifiedSkeletonPtr) { - *specifiedSkeletonPtr = specifiedSkeleton; - } - return bestPattern; -} - -UnicodeString -DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern, - const PtnSkeleton* specifiedSkeleton, - int32_t flags, - UDateTimePatternMatchOptions options) { - UnicodeString newPattern; - fp->set(pattern); - for (int32_t i=0; i < fp->itemNumber; i++) { - UnicodeString field = fp->items[i]; - if ( fp->isQuoteLiteral(field) ) { - - UnicodeString quoteLiteral; - fp->getQuoteLiteral(quoteLiteral, &i); - newPattern += quoteLiteral; - } - else { - if (fp->isPatternSeparator(field)) { - newPattern+=field; - continue; - } - int32_t canonicalIndex = fp->getCanonicalIndex(field); - if (canonicalIndex < 0) { - newPattern+=field; - continue; // don't adjust - } - const dtTypeElem *row = &dtTypes[canonicalIndex]; - int32_t typeValue = row->field; - - // handle day periods - with #13183, no longer need special handling here, integrated with normal types - - if ((flags & kDTPGFixFractionalSeconds) != 0 && typeValue == UDATPG_SECOND_FIELD) { - field += decimal; - dtMatcher->skeleton.original.appendFieldTo(UDATPG_FRACTIONAL_SECOND_FIELD, field); - } else if (dtMatcher->skeleton.type[typeValue]!=0) { - // Here: - // - "reqField" is the field from the originally requested skeleton after replacement - // of metacharacters 'j', 'C' and 'J', with length "reqFieldLen". - // - "field" is the field from the found pattern. - // - // The adjusted field should consist of characters from the originally requested - // skeleton, except in the case of UDATPG_MONTH_FIELD or - // UDATPG_WEEKDAY_FIELD or UDATPG_YEAR_FIELD, in which case it should consist - // of characters from the found pattern. In some cases of UDATPG_HOUR_FIELD, - // there is adjustment following the "defaultHourFormatChar". There is explanation - // how it is done below. - // - // The length of the adjusted field (adjFieldLen) should match that in the originally - // requested skeleton, except that in the following cases the length of the adjusted field - // should match that in the found pattern (i.e. the length of this pattern field should - // not be adjusted): - // 1. typeValue is UDATPG_HOUR_FIELD/MINUTE/SECOND and the corresponding bit in options is - // not set (ticket #7180). Note, we may want to implement a similar change for other - // numeric fields (MM, dd, etc.) so the default behavior is to get locale preference for - // field length, but options bits can be used to override this. - // 2. There is a specified skeleton for the found pattern and one of the following is true: - // a) The length of the field in the skeleton (skelFieldLen) is equal to reqFieldLen. - // b) The pattern field is numeric and the skeleton field is not, or vice versa. - - UChar reqFieldChar = dtMatcher->skeleton.original.getFieldChar(typeValue); - int32_t reqFieldLen = dtMatcher->skeleton.original.getFieldLength(typeValue); - if (reqFieldChar == CAP_E && reqFieldLen < 3) - reqFieldLen = 3; // 1-3 for E are equivalent to 3 for c,e - int32_t adjFieldLen = reqFieldLen; - if ( (typeValue==UDATPG_HOUR_FIELD && (options & UDATPG_MATCH_HOUR_FIELD_LENGTH)==0) || - (typeValue==UDATPG_MINUTE_FIELD && (options & UDATPG_MATCH_MINUTE_FIELD_LENGTH)==0) || - (typeValue==UDATPG_SECOND_FIELD && (options & UDATPG_MATCH_SECOND_FIELD_LENGTH)==0) ) { - adjFieldLen = field.length(); - } else if (specifiedSkeleton && reqFieldChar != LOW_C && reqFieldChar != LOW_E) { - // (we skip this section for 'c' and 'e' because unlike the other characters considered in this function, - // they have no minimum field length-- 'E' and 'EE' are equivalent to 'EEE', but 'e' and 'ee' are not - // equivalent to 'eee' -- see the entries for "week day" in - // https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table for more info) - int32_t skelFieldLen = specifiedSkeleton->original.getFieldLength(typeValue); - UBool patFieldIsNumeric = (row->type > 0); - UBool skelFieldIsNumeric = (specifiedSkeleton->type[typeValue] > 0); - if (skelFieldLen == reqFieldLen || (patFieldIsNumeric && !skelFieldIsNumeric) || (skelFieldIsNumeric && !patFieldIsNumeric)) { - // don't adjust the field length in the found pattern - adjFieldLen = field.length(); - } - } - UChar c = (typeValue!= UDATPG_HOUR_FIELD - && typeValue!= UDATPG_MONTH_FIELD - && typeValue!= UDATPG_WEEKDAY_FIELD - && (typeValue!= UDATPG_YEAR_FIELD || reqFieldChar==CAP_Y)) - ? reqFieldChar - : field.charAt(0); - if (c == CAP_E && adjFieldLen < 3) { - c = LOW_E; - } - if (typeValue == UDATPG_HOUR_FIELD && fDefaultHourFormatChar != 0) { - // The adjustment here is required to match spec (https://www.unicode.org/reports/tr35/tr35-dates.html#dfst-hour). - // It is necessary to match the hour-cycle preferred by the Locale. - // Given that, we need to do the following adjustments: - // 1. When hour-cycle is h11 it should replace 'h' by 'K'. - // 2. When hour-cycle is h23 it should replace 'H' by 'k'. - // 3. When hour-cycle is h24 it should replace 'k' by 'H'. - // 4. When hour-cycle is h12 it should replace 'K' by 'h'. - - if ((flags & kDTPGSkeletonUsesCapJ) != 0 || reqFieldChar == fDefaultHourFormatChar) { - c = fDefaultHourFormatChar; - } else if (reqFieldChar == LOW_H && fDefaultHourFormatChar == CAP_K) { - c = CAP_K; - } else if (reqFieldChar == CAP_H && fDefaultHourFormatChar == LOW_K) { - c = LOW_K; - } else if (reqFieldChar == LOW_K && fDefaultHourFormatChar == CAP_H) { - c = CAP_H; - } else if (reqFieldChar == CAP_K && fDefaultHourFormatChar == LOW_H) { - c = LOW_H; - } - } - - field.remove(); - for (int32_t j=adjFieldLen; j>0; --j) { - field += c; - } - } - newPattern+=field; - } - } - return newPattern; -} - -UnicodeString -DateTimePatternGenerator::getBestAppending(int32_t missingFields, int32_t flags, UErrorCode &status, UDateTimePatternMatchOptions options) { - if (U_FAILURE(status)) { - return UnicodeString(); - } - UnicodeString resultPattern, tempPattern; - const UnicodeString* tempPatternPtr; - int32_t lastMissingFieldMask=0; - if (missingFields!=0) { - resultPattern=UnicodeString(); - const PtnSkeleton* specifiedSkeleton=nullptr; - tempPatternPtr = getBestRaw(*dtMatcher, missingFields, distanceInfo, status, &specifiedSkeleton); - if (U_FAILURE(status)) { - return UnicodeString(); - } - tempPattern = *tempPatternPtr; - resultPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options); - if ( distanceInfo->missingFieldMask==0 ) { - return resultPattern; - } - while (distanceInfo->missingFieldMask!=0) { // precondition: EVERY single field must work! - if ( lastMissingFieldMask == distanceInfo->missingFieldMask ) { - break; // cannot find the proper missing field - } - if (((distanceInfo->missingFieldMask & UDATPG_SECOND_AND_FRACTIONAL_MASK)==UDATPG_FRACTIONAL_MASK) && - ((missingFields & UDATPG_SECOND_AND_FRACTIONAL_MASK) == UDATPG_SECOND_AND_FRACTIONAL_MASK)) { - resultPattern = adjustFieldTypes(resultPattern, specifiedSkeleton, flags | kDTPGFixFractionalSeconds, options); - distanceInfo->missingFieldMask &= ~UDATPG_FRACTIONAL_MASK; - continue; - } - int32_t startingMask = distanceInfo->missingFieldMask; - tempPatternPtr = getBestRaw(*dtMatcher, distanceInfo->missingFieldMask, distanceInfo, status, &specifiedSkeleton); - if (U_FAILURE(status)) { - return UnicodeString(); - } - tempPattern = *tempPatternPtr; - tempPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options); - int32_t foundMask=startingMask& ~distanceInfo->missingFieldMask; - int32_t topField=getTopBitNumber(foundMask); - - if (appendItemFormats[topField].length() != 0) { - UnicodeString appendName; - getAppendName((UDateTimePatternField)topField, appendName); - const UnicodeString *values[3] = { - &resultPattern, - &tempPattern, - &appendName - }; - SimpleFormatter(appendItemFormats[topField], 2, 3, status). - formatAndReplace(values, 3, resultPattern, nullptr, 0, status); - } - lastMissingFieldMask = distanceInfo->missingFieldMask; - } - } - return resultPattern; -} - -int32_t -DateTimePatternGenerator::getTopBitNumber(int32_t foundMask) const { - if ( foundMask==0 ) { - return 0; - } - int32_t i=0; - while (foundMask!=0) { - foundMask >>=1; - ++i; - } - if (i-1 >UDATPG_ZONE_FIELD) { - return UDATPG_ZONE_FIELD; - } - else - return i-1; -} - -void -DateTimePatternGenerator::setAvailableFormat(const UnicodeString &key, UErrorCode& err) -{ - fAvailableFormatKeyHash->puti(key, 1, err); -} - -UBool -DateTimePatternGenerator::isAvailableFormatSet(const UnicodeString &key) const { - return (UBool)(fAvailableFormatKeyHash->geti(key) == 1); -} - -void -DateTimePatternGenerator::copyHashtable(Hashtable *other, UErrorCode &status) { - if (other == nullptr || U_FAILURE(status)) { - return; - } - if (fAvailableFormatKeyHash != nullptr) { - delete fAvailableFormatKeyHash; - fAvailableFormatKeyHash = nullptr; - } - initHashtable(status); - if(U_FAILURE(status)){ - return; - } - int32_t pos = UHASH_FIRST; - const UHashElement* elem = nullptr; - // walk through the hash table and create a deep clone - while((elem = other->nextElement(pos))!= nullptr){ - const UHashTok otherKeyTok = elem->key; - UnicodeString* otherKey = (UnicodeString*)otherKeyTok.pointer; - fAvailableFormatKeyHash->puti(*otherKey, 1, status); - if(U_FAILURE(status)){ - return; - } - } -} - -StringEnumeration* -DateTimePatternGenerator::getSkeletons(UErrorCode& status) const { - if (U_FAILURE(status)) { - return nullptr; - } - if (U_FAILURE(internalErrorCode)) { - status = internalErrorCode; - return nullptr; - } - LocalPointer skeletonEnumerator( - new DTSkeletonEnumeration(*patternMap, DT_SKELETON, status), status); - - return U_SUCCESS(status) ? skeletonEnumerator.orphan() : nullptr; -} - -const UnicodeString& -DateTimePatternGenerator::getPatternForSkeleton(const UnicodeString& skeleton) const { - PtnElem *curElem; - - if (skeleton.length() ==0) { - return emptyString; - } - curElem = patternMap->getHeader(skeleton.charAt(0)); - while ( curElem != nullptr ) { - if ( curElem->skeleton->getSkeleton()==skeleton ) { - return curElem->pattern; - } - curElem = curElem->next.getAlias(); - } - return emptyString; -} - -StringEnumeration* -DateTimePatternGenerator::getBaseSkeletons(UErrorCode& status) const { - if (U_FAILURE(status)) { - return nullptr; - } - if (U_FAILURE(internalErrorCode)) { - status = internalErrorCode; - return nullptr; - } - LocalPointer baseSkeletonEnumerator( - new DTSkeletonEnumeration(*patternMap, DT_BASESKELETON, status), status); - - return U_SUCCESS(status) ? baseSkeletonEnumerator.orphan() : nullptr; -} - -StringEnumeration* -DateTimePatternGenerator::getRedundants(UErrorCode& status) { - if (U_FAILURE(status)) { return nullptr; } - if (U_FAILURE(internalErrorCode)) { - status = internalErrorCode; - return nullptr; - } - LocalPointer output(new DTRedundantEnumeration(), status); - if (U_FAILURE(status)) { return nullptr; } - const UnicodeString *pattern; - PatternMapIterator it(status); - if (U_FAILURE(status)) { return nullptr; } - - for (it.set(*patternMap); it.hasNext(); ) { - DateTimeMatcher current = it.next(); - pattern = patternMap->getPatternFromSkeleton(*(it.getSkeleton())); - if ( isCanonicalItem(*pattern) ) { - continue; - } - if ( skipMatcher == nullptr ) { - skipMatcher = new DateTimeMatcher(current); - if (skipMatcher == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - } - else { - *skipMatcher = current; - } - UnicodeString trial = getBestPattern(current.getPattern(), status); - if (U_FAILURE(status)) { return nullptr; } - if (trial == *pattern) { - ((DTRedundantEnumeration *)output.getAlias())->add(*pattern, status); - if (U_FAILURE(status)) { return nullptr; } - } - if (current.equals(skipMatcher)) { - continue; - } - } - return output.orphan(); -} - -UBool -DateTimePatternGenerator::isCanonicalItem(const UnicodeString& item) const { - if ( item.length() != 1 ) { - return false; - } - for (int32_t i=0; iisDupAllowed = other.isDupAllowed; - for (int32_t bootIndex = 0; bootIndex < MAX_PATTERN_ENTRIES; ++bootIndex) { - PtnElem *curElem, *otherElem, *prevElem=nullptr; - otherElem = other.boot[bootIndex]; - while (otherElem != nullptr) { - LocalPointer newElem(new PtnElem(otherElem->basePattern, otherElem->pattern), status); - if (U_FAILURE(status)) { - return; // out of memory - } - newElem->skeleton.adoptInsteadAndCheckErrorCode(new PtnSkeleton(*(otherElem->skeleton)), status); - if (U_FAILURE(status)) { - return; // out of memory - } - newElem->skeletonWasSpecified = otherElem->skeletonWasSpecified; - - // Release ownership from the LocalPointer of the PtnElem object. - // The PtnElem will now be owned by either the boot (for the first entry in the linked-list) - // or owned by the previous PtnElem object in the linked-list. - curElem = newElem.orphan(); - - if (this->boot[bootIndex] == nullptr) { - this->boot[bootIndex] = curElem; - } else { - if (prevElem != nullptr) { - prevElem->next.adoptInstead(curElem); - } else { - UPRV_UNREACHABLE_EXIT; - } - } - prevElem = curElem; - otherElem = otherElem->next.getAlias(); - } - - } -} - -PtnElem* -PatternMap::getHeader(UChar baseChar) const { - PtnElem* curElem; - - if ( (baseChar >= CAP_A) && (baseChar <= CAP_Z) ) { - curElem = boot[baseChar-CAP_A]; - } - else { - if ( (baseChar >=LOW_A) && (baseChar <= LOW_Z) ) { - curElem = boot[26+baseChar-LOW_A]; - } - else { - return nullptr; - } - } - return curElem; -} - -PatternMap::~PatternMap() { - for (int32_t i=0; i < MAX_PATTERN_ENTRIES; ++i ) { - if (boot[i] != nullptr ) { - delete boot[i]; - boot[i] = nullptr; - } - } -} // PatternMap destructor - -void -PatternMap::add(const UnicodeString& basePattern, - const PtnSkeleton& skeleton, - const UnicodeString& value,// mapped pattern value - UBool skeletonWasSpecified, - UErrorCode &status) { - UChar baseChar = basePattern.charAt(0); - PtnElem *curElem, *baseElem; - status = U_ZERO_ERROR; - - // the baseChar must be A-Z or a-z - if ((baseChar >= CAP_A) && (baseChar <= CAP_Z)) { - baseElem = boot[baseChar-CAP_A]; - } - else { - if ((baseChar >=LOW_A) && (baseChar <= LOW_Z)) { - baseElem = boot[26+baseChar-LOW_A]; - } - else { - status = U_ILLEGAL_CHARACTER; - return; - } - } - - if (baseElem == nullptr) { - LocalPointer newElem(new PtnElem(basePattern, value), status); - if (U_FAILURE(status)) { - return; // out of memory - } - newElem->skeleton.adoptInsteadAndCheckErrorCode(new PtnSkeleton(skeleton), status); - if (U_FAILURE(status)) { - return; // out of memory - } - newElem->skeletonWasSpecified = skeletonWasSpecified; - if (baseChar >= LOW_A) { - boot[26 + (baseChar - LOW_A)] = newElem.orphan(); // the boot array now owns the PtnElem. - } - else { - boot[baseChar - CAP_A] = newElem.orphan(); // the boot array now owns the PtnElem. - } - } - if ( baseElem != nullptr ) { - curElem = getDuplicateElem(basePattern, skeleton, baseElem); - - if (curElem == nullptr) { - // add new element to the list. - curElem = baseElem; - while( curElem -> next != nullptr ) - { - curElem = curElem->next.getAlias(); - } - - LocalPointer newElem(new PtnElem(basePattern, value), status); - if (U_FAILURE(status)) { - return; // out of memory - } - newElem->skeleton.adoptInsteadAndCheckErrorCode(new PtnSkeleton(skeleton), status); - if (U_FAILURE(status)) { - return; // out of memory - } - newElem->skeletonWasSpecified = skeletonWasSpecified; - curElem->next.adoptInstead(newElem.orphan()); - curElem = curElem->next.getAlias(); - } - else { - // Pattern exists in the list already. - if ( !isDupAllowed ) { - return; - } - // Overwrite the value. - curElem->pattern = value; - // It was a bug that we were not doing the following previously, - // though that bug hid other problems by making things partly work. - curElem->skeletonWasSpecified = skeletonWasSpecified; - } - } -} // PatternMap::add - -// Find the pattern from the given basePattern string. -const UnicodeString * -PatternMap::getPatternFromBasePattern(const UnicodeString& basePattern, UBool& skeletonWasSpecified) const { // key to search for - PtnElem *curElem; - - if ((curElem=getHeader(basePattern.charAt(0)))==nullptr) { - return nullptr; // no match - } - - do { - if ( basePattern.compare(curElem->basePattern)==0 ) { - skeletonWasSpecified = curElem->skeletonWasSpecified; - return &(curElem->pattern); - } - curElem = curElem->next.getAlias(); - } while (curElem != nullptr); - - return nullptr; -} // PatternMap::getFromBasePattern - - -// Find the pattern from the given skeleton. -// At least when this is called from getBestRaw & addPattern (in which case specifiedSkeletonPtr is non-NULL), -// the comparison should be based on skeleton.original (which is unique and tied to the distance measurement in bestRaw) -// and not skeleton.baseOriginal (which is not unique); otherwise we may pick a different skeleton than the one with the -// optimum distance value in getBestRaw. When this is called from public getRedundants (specifiedSkeletonPtr is NULL), -// for now it will continue to compare based on baseOriginal so as not to change the behavior unnecessarily. -const UnicodeString * -PatternMap::getPatternFromSkeleton(const PtnSkeleton& skeleton, const PtnSkeleton** specifiedSkeletonPtr) const { // key to search for - PtnElem *curElem; - - if (specifiedSkeletonPtr) { - *specifiedSkeletonPtr = nullptr; - } - - // find boot entry - UChar baseChar = skeleton.getFirstChar(); - if ((curElem=getHeader(baseChar))==nullptr) { - return nullptr; // no match - } - - do { - UBool equal; - if (specifiedSkeletonPtr != nullptr) { // called from DateTimePatternGenerator::getBestRaw or addPattern, use original - equal = curElem->skeleton->original == skeleton.original; - } else { // called from DateTimePatternGenerator::getRedundants, use baseOriginal - equal = curElem->skeleton->baseOriginal == skeleton.baseOriginal; - } - if (equal) { - if (specifiedSkeletonPtr && curElem->skeletonWasSpecified) { - *specifiedSkeletonPtr = curElem->skeleton.getAlias(); - } - return &(curElem->pattern); - } - curElem = curElem->next.getAlias(); - } while (curElem != nullptr); - - return nullptr; -} - -UBool -PatternMap::equals(const PatternMap& other) const { - if ( this==&other ) { - return true; - } - for (int32_t bootIndex = 0; bootIndex < MAX_PATTERN_ENTRIES; ++bootIndex) { - if (boot[bootIndex] == other.boot[bootIndex]) { - continue; - } - if ((boot[bootIndex] == nullptr) || (other.boot[bootIndex] == nullptr)) { - return false; - } - PtnElem *otherElem = other.boot[bootIndex]; - PtnElem *myElem = boot[bootIndex]; - while ((otherElem != nullptr) || (myElem != nullptr)) { - if ( myElem == otherElem ) { - break; - } - if ((otherElem == nullptr) || (myElem == nullptr)) { - return false; - } - if ( (myElem->basePattern != otherElem->basePattern) || - (myElem->pattern != otherElem->pattern) ) { - return false; - } - if ((myElem->skeleton.getAlias() != otherElem->skeleton.getAlias()) && - !myElem->skeleton->equals(*(otherElem->skeleton))) { - return false; - } - myElem = myElem->next.getAlias(); - otherElem = otherElem->next.getAlias(); - } - } - return true; -} - -// find any key existing in the mapping table already. -// return true if there is an existing key, otherwise return false. -PtnElem* -PatternMap::getDuplicateElem( - const UnicodeString &basePattern, - const PtnSkeleton &skeleton, - PtnElem *baseElem) { - PtnElem *curElem; - - if ( baseElem == nullptr ) { - return nullptr; - } - else { - curElem = baseElem; - } - do { - if ( basePattern.compare(curElem->basePattern)==0 ) { - UBool isEqual = true; - for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) { - if (curElem->skeleton->type[i] != skeleton.type[i] ) { - isEqual = false; - break; - } - } - if (isEqual) { - return curElem; - } - } - curElem = curElem->next.getAlias(); - } while( curElem != nullptr ); - - // end of the list - return nullptr; - -} // PatternMap::getDuplicateElem - -DateTimeMatcher::DateTimeMatcher(void) { -} - -DateTimeMatcher::~DateTimeMatcher() {} - -DateTimeMatcher::DateTimeMatcher(const DateTimeMatcher& other) { - copyFrom(other.skeleton); -} - -DateTimeMatcher& DateTimeMatcher::operator=(const DateTimeMatcher& other) { - copyFrom(other.skeleton); - return *this; -} - - -void -DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp) { - PtnSkeleton localSkeleton; - return set(pattern, fp, localSkeleton); -} - -void -DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp, PtnSkeleton& skeletonResult) { - int32_t i; - for (i=0; iset(pattern); - for (i=0; i < fp->itemNumber; i++) { - const UnicodeString& value = fp->items[i]; - // don't skip 'a' anymore, dayPeriod handled specially below - - if ( fp->isQuoteLiteral(value) ) { - UnicodeString quoteLiteral; - fp->getQuoteLiteral(quoteLiteral, &i); - continue; - } - int32_t canonicalIndex = fp->getCanonicalIndex(value); - if (canonicalIndex < 0) { - continue; - } - const dtTypeElem *row = &dtTypes[canonicalIndex]; - int32_t field = row->field; - skeletonResult.original.populate(field, value); - UChar repeatChar = row->patternChar; - int32_t repeatCount = row->minLen; - skeletonResult.baseOriginal.populate(field, repeatChar, repeatCount); - int16_t subField = row->type; - if (row->type > 0) { - U_ASSERT(value.length() < INT16_MAX); - subField += static_cast(value.length()); - } - skeletonResult.type[field] = subField; - } - - // #20739, we have a skeleton with minutes and milliseconds, but no seconds - // - // Theoretically we would need to check and fix all fields with "gaps": - // for example year-day (no month), month-hour (no day), and so on, All the possible field combinations. - // Plus some smartness: year + hour => should we add month, or add day-of-year? - // What about month + day-of-week, or month + am/pm indicator. - // I think beyond a certain point we should not try to fix bad developer input and try guessing what they mean. - // Garbage in, garbage out. - if (!skeletonResult.original.isFieldEmpty(UDATPG_MINUTE_FIELD) - && !skeletonResult.original.isFieldEmpty(UDATPG_FRACTIONAL_SECOND_FIELD) - && skeletonResult.original.isFieldEmpty(UDATPG_SECOND_FIELD)) { - // Force the use of seconds - for (i = 0; dtTypes[i].patternChar != 0; i++) { - if (dtTypes[i].field == UDATPG_SECOND_FIELD) { - // first entry for UDATPG_SECOND_FIELD - skeletonResult.original.populate(UDATPG_SECOND_FIELD, dtTypes[i].patternChar, dtTypes[i].minLen); - skeletonResult.baseOriginal.populate(UDATPG_SECOND_FIELD, dtTypes[i].patternChar, dtTypes[i].minLen); - // We add value.length, same as above, when type is first initialized. - // The value we want to "fake" here is "s", and 1 means "s".length() - int16_t subField = dtTypes[i].type; - skeletonResult.type[UDATPG_SECOND_FIELD] = (subField > 0) ? subField + 1 : subField; - break; - } - } - } - - // #13183, handle special behavior for day period characters (a, b, B) - if (!skeletonResult.original.isFieldEmpty(UDATPG_HOUR_FIELD)) { - if (skeletonResult.original.getFieldChar(UDATPG_HOUR_FIELD)==LOW_H || skeletonResult.original.getFieldChar(UDATPG_HOUR_FIELD)==CAP_K) { - // We have a skeleton with 12-hour-cycle format - if (skeletonResult.original.isFieldEmpty(UDATPG_DAYPERIOD_FIELD)) { - // But we do not have a day period in the skeleton; add the default DAYPERIOD (currently "a") - for (i = 0; dtTypes[i].patternChar != 0; i++) { - if ( dtTypes[i].field == UDATPG_DAYPERIOD_FIELD ) { - // first entry for UDATPG_DAYPERIOD_FIELD - skeletonResult.original.populate(UDATPG_DAYPERIOD_FIELD, dtTypes[i].patternChar, dtTypes[i].minLen); - skeletonResult.baseOriginal.populate(UDATPG_DAYPERIOD_FIELD, dtTypes[i].patternChar, dtTypes[i].minLen); - skeletonResult.type[UDATPG_DAYPERIOD_FIELD] = dtTypes[i].type; - skeletonResult.addedDefaultDayPeriod = true; - break; - } - } - } - } else { - // Skeleton has 24-hour-cycle hour format and has dayPeriod, delete dayPeriod (i.e. ignore it) - skeletonResult.original.clearField(UDATPG_DAYPERIOD_FIELD); - skeletonResult.baseOriginal.clearField(UDATPG_DAYPERIOD_FIELD); - skeletonResult.type[UDATPG_DAYPERIOD_FIELD] = NONE; - } - } - copyFrom(skeletonResult); -} - -void -DateTimeMatcher::getBasePattern(UnicodeString &result ) { - result.remove(); // Reset the result first. - skeleton.baseOriginal.appendTo(result); -} - -UnicodeString -DateTimeMatcher::getPattern() { - UnicodeString result; - return skeleton.original.appendTo(result); -} - -int32_t -DateTimeMatcher::getDistance(const DateTimeMatcher& other, int32_t includeMask, DistanceInfo& distanceInfo) const { - int32_t result = 0; - distanceInfo.clear(); - for (int32_t i=0; iskeleton.original; -} - -int32_t -DateTimeMatcher::getFieldMask() const { - int32_t result = 0; - - for (int32_t i=0; i= pattern.length()) { - return DONE; - } - // check the current char is between A-Z or a-z - do { - UChar c=pattern.charAt(curLoc); - if ( (c>=CAP_A && c<=CAP_Z) || (c>=LOW_A && c<=LOW_Z) ) { - curLoc++; - } - else { - startPos = curLoc; - *len=1; - return ADD_TOKEN; - } - - if ( pattern.charAt(curLoc)!= pattern.charAt(startPos) ) { - break; // not the same token - } - } while(curLoc <= pattern.length()); - *len = curLoc-startPos; - return ADD_TOKEN; -} - -void -FormatParser::set(const UnicodeString& pattern) { - int32_t startPos = 0; - TokenStatus result = START; - int32_t len = 0; - itemNumber = 0; - - do { - result = setTokens( pattern, startPos, &len ); - if ( result == ADD_TOKEN ) - { - items[itemNumber++] = UnicodeString(pattern, startPos, len ); - startPos += len; - } - else { - break; - } - } while (result==ADD_TOKEN && itemNumber < MAX_DT_TOKEN); -} - -int32_t -FormatParser::getCanonicalIndex(const UnicodeString& s, UBool strict) { - int32_t len = s.length(); - if (len == 0) { - return -1; - } - UChar ch = s.charAt(0); - - // Verify that all are the same character. - for (int32_t l = 1; l < len; l++) { - if (ch != s.charAt(l)) { - return -1; - } - } - int32_t i = 0; - int32_t bestRow = -1; - while (dtTypes[i].patternChar != 0x0000) { - if ( dtTypes[i].patternChar != ch ) { - ++i; - continue; - } - bestRow = i; - if (dtTypes[i].patternChar != dtTypes[i+1].patternChar) { - return i; - } - if (dtTypes[i+1].minLen <= len) { - ++i; - continue; - } - return i; - } - return strict ? -1 : bestRow; -} - -UBool -FormatParser::isQuoteLiteral(const UnicodeString& s) { - return (UBool)(s.charAt(0) == SINGLE_QUOTE); -} - -// This function assumes the current itemIndex points to the quote literal. -// Please call isQuoteLiteral prior to this function. -void -FormatParser::getQuoteLiteral(UnicodeString& quote, int32_t *itemIndex) { - int32_t i = *itemIndex; - - quote.remove(); - if (items[i].charAt(0)==SINGLE_QUOTE) { - quote += items[i]; - ++i; - } - while ( i < itemNumber ) { - if ( items[i].charAt(0)==SINGLE_QUOTE ) { - if ( (i+1patternMap=&newPatternMap; -} - -PtnSkeleton* -PatternMapIterator::getSkeleton() const { - if ( nodePtr == nullptr ) { - return nullptr; - } - else { - return nodePtr->skeleton.getAlias(); - } -} - -UBool -PatternMapIterator::hasNext() const { - int32_t headIndex = bootIndex; - PtnElem *curPtr = nodePtr; - - if (patternMap==nullptr) { - return false; - } - while ( headIndex < MAX_PATTERN_ENTRIES ) { - if ( curPtr != nullptr ) { - if ( curPtr->next != nullptr ) { - return true; - } - else { - headIndex++; - curPtr=nullptr; - continue; - } - } - else { - if ( patternMap->boot[headIndex] != nullptr ) { - return true; - } - else { - headIndex++; - continue; - } - } - } - return false; -} - -DateTimeMatcher& -PatternMapIterator::next() { - while ( bootIndex < MAX_PATTERN_ENTRIES ) { - if ( nodePtr != nullptr ) { - if ( nodePtr->next != nullptr ) { - nodePtr = nodePtr->next.getAlias(); - break; - } - else { - bootIndex++; - nodePtr=nullptr; - continue; - } - } - else { - if ( patternMap->boot[bootIndex] != nullptr ) { - nodePtr = patternMap->boot[bootIndex]; - break; - } - else { - bootIndex++; - continue; - } - } - } - if (nodePtr!=nullptr) { - matcher->copyFrom(*nodePtr->skeleton); - } - else { - matcher->copyFrom(); - } - return *matcher; -} - - -SkeletonFields::SkeletonFields() { - // Set initial values to zero - clear(); -} - -void SkeletonFields::clear() { - uprv_memset(chars, 0, sizeof(chars)); - uprv_memset(lengths, 0, sizeof(lengths)); -} - -void SkeletonFields::copyFrom(const SkeletonFields& other) { - uprv_memcpy(chars, other.chars, sizeof(chars)); - uprv_memcpy(lengths, other.lengths, sizeof(lengths)); -} - -void SkeletonFields::clearField(int32_t field) { - chars[field] = 0; - lengths[field] = 0; -} - -UChar SkeletonFields::getFieldChar(int32_t field) const { - return chars[field]; -} - -int32_t SkeletonFields::getFieldLength(int32_t field) const { - return lengths[field]; -} - -void SkeletonFields::populate(int32_t field, const UnicodeString& value) { - populate(field, value.charAt(0), value.length()); -} - -void SkeletonFields::populate(int32_t field, UChar ch, int32_t length) { - chars[field] = (int8_t) ch; - lengths[field] = (int8_t) length; -} - -UBool SkeletonFields::isFieldEmpty(int32_t field) const { - return lengths[field] == 0; -} - -UnicodeString& SkeletonFields::appendTo(UnicodeString& string) const { - for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) { - appendFieldTo(i, string); - } - return string; -} - -UnicodeString& SkeletonFields::appendFieldTo(int32_t field, UnicodeString& string) const { - UChar ch(chars[field]); - int32_t length = (int32_t) lengths[field]; - - for (int32_t i=0; i= 0) { - // for backward compatibility: if DateTimeMatcher.set added a single 'a' that - // was not in the provided skeleton, remove it here before returning skeleton. - result.remove(pos, 1); - } - return result; -} - -UnicodeString -PtnSkeleton::getBaseSkeleton() const { - UnicodeString result; - result = baseOriginal.appendTo(result); - int32_t pos; - if (addedDefaultDayPeriod && (pos = result.indexOf(LOW_A)) >= 0) { - // for backward compatibility: if DateTimeMatcher.set added a single 'a' that - // was not in the provided skeleton, remove it here before returning skeleton. - result.remove(pos, 1); - } - return result; -} - -UChar -PtnSkeleton::getFirstChar() const { - return baseOriginal.getFirstChar(); -} - -PtnSkeleton::~PtnSkeleton() { -} - -PtnElem::PtnElem(const UnicodeString &basePat, const UnicodeString &pat) : - basePattern(basePat), skeleton(nullptr), pattern(pat), next(nullptr) -{ -} - -PtnElem::~PtnElem() { -} - -DTSkeletonEnumeration::DTSkeletonEnumeration(PatternMap& patternMap, dtStrEnum type, UErrorCode& status) : fSkeletons(nullptr) { - PtnElem *curElem; - PtnSkeleton *curSkeleton; - UnicodeString s; - int32_t bootIndex; - - pos=0; - fSkeletons.adoptInsteadAndCheckErrorCode(new UVector(status), status); - if (U_FAILURE(status)) { - return; - } - - for (bootIndex=0; bootIndexbasePattern; - break; - case DT_PATTERN: - s=curElem->pattern; - break; - case DT_SKELETON: - curSkeleton=curElem->skeleton.getAlias(); - s=curSkeleton->getSkeleton(); - break; - } - if ( !isCanonicalItem(s) ) { - LocalPointer newElem(s.clone(), status); - if (U_FAILURE(status)) { - return; - } - fSkeletons->addElement(newElem.getAlias(), status); - if (U_FAILURE(status)) { - fSkeletons.adoptInstead(nullptr); - return; - } - newElem.orphan(); // fSkeletons vector now owns the UnicodeString (although it - // does not use a deleter function to manage the ownership). - } - curElem = curElem->next.getAlias(); - } - } - if ((bootIndex==MAX_PATTERN_ENTRIES) && (curElem!=nullptr) ) { - status = U_BUFFER_OVERFLOW_ERROR; - } -} - -const UnicodeString* -DTSkeletonEnumeration::snext(UErrorCode& status) { - if (U_SUCCESS(status) && fSkeletons.isValid() && pos < fSkeletons->size()) { - return (const UnicodeString*)fSkeletons->elementAt(pos++); - } - return nullptr; -} - -void -DTSkeletonEnumeration::reset(UErrorCode& /*status*/) { - pos=0; -} - -int32_t -DTSkeletonEnumeration::count(UErrorCode& /*status*/) const { - return (fSkeletons.isNull()) ? 0 : fSkeletons->size(); -} - -UBool -DTSkeletonEnumeration::isCanonicalItem(const UnicodeString& item) { - if ( item.length() != 1 ) { - return false; - } - for (int32_t i=0; isize(); ++i) { - if ((s = (UnicodeString *)fSkeletons->elementAt(i)) != nullptr) { - delete s; - } - } - } -} - -DTRedundantEnumeration::DTRedundantEnumeration() : pos(0), fPatterns(nullptr) { -} - -void -DTRedundantEnumeration::add(const UnicodeString& pattern, UErrorCode& status) { - if (U_FAILURE(status)) { return; } - if (fPatterns.isNull()) { - fPatterns.adoptInsteadAndCheckErrorCode(new UVector(status), status); - if (U_FAILURE(status)) { - return; - } - } - LocalPointer newElem(new UnicodeString(pattern), status); - if (U_FAILURE(status)) { - return; - } - fPatterns->addElement(newElem.getAlias(), status); - if (U_FAILURE(status)) { - fPatterns.adoptInstead(nullptr); - return; - } - newElem.orphan(); // fPatterns now owns the string, although a UVector - // deleter function is not used to manage that ownership. -} - -const UnicodeString* -DTRedundantEnumeration::snext(UErrorCode& status) { - if (U_SUCCESS(status) && fPatterns.isValid() && pos < fPatterns->size()) { - return (const UnicodeString*)fPatterns->elementAt(pos++); - } - return nullptr; -} - -void -DTRedundantEnumeration::reset(UErrorCode& /*status*/) { - pos=0; -} - -int32_t -DTRedundantEnumeration::count(UErrorCode& /*status*/) const { - return (fPatterns.isNull()) ? 0 : fPatterns->size(); -} - -UBool -DTRedundantEnumeration::isCanonicalItem(const UnicodeString& item) const { - if ( item.length() != 1 ) { - return false; - } - for (int32_t i=0; isize(); ++i) { - if ((s = (UnicodeString *)fPatterns->elementAt(i)) != nullptr) { - delete s; - } - } - } -} - -U_NAMESPACE_END - - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2007-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTPTNGEN.CPP +* +******************************************************************************* +*/ + +#include "unicode/utypes.h" +#if !UCONFIG_NO_FORMATTING + +#include "unicode/datefmt.h" +#include "unicode/decimfmt.h" +#include "unicode/dtfmtsym.h" +#include "unicode/dtptngen.h" +#include "unicode/localpointer.h" +#include "unicode/simpleformatter.h" +#include "unicode/smpdtfmt.h" +#include "unicode/udat.h" +#include "unicode/udatpg.h" +#include "unicode/uniset.h" +#include "unicode/uloc.h" +#include "unicode/ures.h" +#include "unicode/ustring.h" +#include "unicode/rep.h" +#include "unicode/region.h" +#include "cpputils.h" +#include "mutex.h" +#include "umutex.h" +#include "cmemory.h" +#include "cstring.h" +#include "locbased.h" +#include "hash.h" +#include "uhash.h" +#include "uresimp.h" +#include "dtptngen_impl.h" +#include "ucln_in.h" +#include "charstr.h" +#include "uassert.h" + +#if U_CHARSET_FAMILY==U_EBCDIC_FAMILY +/** + * If we are on EBCDIC, use an iterator which will + * traverse the bundles in ASCII order. + */ +#define U_USE_ASCII_BUNDLE_ITERATOR +#define U_SORT_ASCII_BUNDLE_ITERATOR +#endif + +#if defined(U_USE_ASCII_BUNDLE_ITERATOR) + +#include "unicode/ustring.h" +#include "uarrsort.h" + +struct UResAEntry { + char16_t *key; + UResourceBundle *item; +}; + +struct UResourceBundleAIterator { + UResourceBundle *bund; + UResAEntry *entries; + int32_t num; + int32_t cursor; +}; + +/* Must be C linkage to pass function pointer to the sort function */ + +U_CDECL_BEGIN + +static int32_t U_CALLCONV +ures_a_codepointSort(const void *context, const void *left, const void *right) { + //CompareContext *cmp=(CompareContext *)context; + return u_strcmp(((const UResAEntry *)left)->key, + ((const UResAEntry *)right)->key); +} + +U_CDECL_END + +static void ures_a_open(UResourceBundleAIterator *aiter, UResourceBundle *bund, UErrorCode *status) { + if(U_FAILURE(*status)) { + return; + } + aiter->bund = bund; + aiter->num = ures_getSize(aiter->bund); + aiter->cursor = 0; +#if !defined(U_SORT_ASCII_BUNDLE_ITERATOR) + aiter->entries = nullptr; +#else + aiter->entries = (UResAEntry*)uprv_malloc(sizeof(UResAEntry)*aiter->num); + for(int i=0;inum;i++) { + aiter->entries[i].item = ures_getByIndex(aiter->bund, i, nullptr, status); + const char *akey = ures_getKey(aiter->entries[i].item); + int32_t len = uprv_strlen(akey)+1; + aiter->entries[i].key = (char16_t*)uprv_malloc(len*sizeof(char16_t)); + u_charsToUChars(akey, aiter->entries[i].key, len); + } + uprv_sortArray(aiter->entries, aiter->num, sizeof(UResAEntry), ures_a_codepointSort, nullptr, true, status); +#endif +} + +static void ures_a_close(UResourceBundleAIterator *aiter) { +#if defined(U_SORT_ASCII_BUNDLE_ITERATOR) + for(int i=0;inum;i++) { + uprv_free(aiter->entries[i].key); + ures_close(aiter->entries[i].item); + } +#endif +} + +static const char16_t *ures_a_getNextString(UResourceBundleAIterator *aiter, int32_t *len, const char **key, UErrorCode *err) { +#if !defined(U_SORT_ASCII_BUNDLE_ITERATOR) + return ures_getNextString(aiter->bund, len, key, err); +#else + if(U_FAILURE(*err)) return nullptr; + UResourceBundle *item = aiter->entries[aiter->cursor].item; + const char16_t* ret = ures_getString(item, len, err); + *key = ures_getKey(item); + aiter->cursor++; + return ret; +#endif +} + + +#endif + + +U_NAMESPACE_BEGIN + +// ***************************************************************************** +// class DateTimePatternGenerator +// ***************************************************************************** +static const char16_t Canonical_Items[] = { + // GyQMwWEDFdaHmsSv + CAP_G, LOW_Y, CAP_Q, CAP_M, LOW_W, CAP_W, CAP_E, + CAP_D, CAP_F, LOW_D, LOW_A, // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J + CAP_H, LOW_M, LOW_S, CAP_S, LOW_V, 0 +}; + +static const dtTypeElem dtTypes[] = { + // patternChar, field, type, minLen, weight + {CAP_G, UDATPG_ERA_FIELD, DT_SHORT, 1, 3,}, + {CAP_G, UDATPG_ERA_FIELD, DT_LONG, 4, 0}, + {CAP_G, UDATPG_ERA_FIELD, DT_NARROW, 5, 0}, + + {LOW_Y, UDATPG_YEAR_FIELD, DT_NUMERIC, 1, 20}, + {CAP_Y, UDATPG_YEAR_FIELD, DT_NUMERIC + DT_DELTA, 1, 20}, + {LOW_U, UDATPG_YEAR_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 20}, + {LOW_R, UDATPG_YEAR_FIELD, DT_NUMERIC + 3*DT_DELTA, 1, 20}, + {CAP_U, UDATPG_YEAR_FIELD, DT_SHORT, 1, 3}, + {CAP_U, UDATPG_YEAR_FIELD, DT_LONG, 4, 0}, + {CAP_U, UDATPG_YEAR_FIELD, DT_NARROW, 5, 0}, + + {CAP_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC, 1, 2}, + {CAP_Q, UDATPG_QUARTER_FIELD, DT_SHORT, 3, 0}, + {CAP_Q, UDATPG_QUARTER_FIELD, DT_LONG, 4, 0}, + {CAP_Q, UDATPG_QUARTER_FIELD, DT_NARROW, 5, 0}, + {LOW_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, + {LOW_Q, UDATPG_QUARTER_FIELD, DT_SHORT - DT_DELTA, 3, 0}, + {LOW_Q, UDATPG_QUARTER_FIELD, DT_LONG - DT_DELTA, 4, 0}, + {LOW_Q, UDATPG_QUARTER_FIELD, DT_NARROW - DT_DELTA, 5, 0}, + + {CAP_M, UDATPG_MONTH_FIELD, DT_NUMERIC, 1, 2}, + {CAP_M, UDATPG_MONTH_FIELD, DT_SHORT, 3, 0}, + {CAP_M, UDATPG_MONTH_FIELD, DT_LONG, 4, 0}, + {CAP_M, UDATPG_MONTH_FIELD, DT_NARROW, 5, 0}, + {CAP_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, + {CAP_L, UDATPG_MONTH_FIELD, DT_SHORT - DT_DELTA, 3, 0}, + {CAP_L, UDATPG_MONTH_FIELD, DT_LONG - DT_DELTA, 4, 0}, + {CAP_L, UDATPG_MONTH_FIELD, DT_NARROW - DT_DELTA, 5, 0}, + {LOW_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 1}, + + {LOW_W, UDATPG_WEEK_OF_YEAR_FIELD, DT_NUMERIC, 1, 2}, + + {CAP_W, UDATPG_WEEK_OF_MONTH_FIELD, DT_NUMERIC, 1, 0}, + + {CAP_E, UDATPG_WEEKDAY_FIELD, DT_SHORT, 1, 3}, + {CAP_E, UDATPG_WEEKDAY_FIELD, DT_LONG, 4, 0}, + {CAP_E, UDATPG_WEEKDAY_FIELD, DT_NARROW, 5, 0}, + {CAP_E, UDATPG_WEEKDAY_FIELD, DT_SHORTER, 6, 0}, + {LOW_C, UDATPG_WEEKDAY_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 2}, + {LOW_C, UDATPG_WEEKDAY_FIELD, DT_SHORT - 2*DT_DELTA, 3, 0}, + {LOW_C, UDATPG_WEEKDAY_FIELD, DT_LONG - 2*DT_DELTA, 4, 0}, + {LOW_C, UDATPG_WEEKDAY_FIELD, DT_NARROW - 2*DT_DELTA, 5, 0}, + {LOW_C, UDATPG_WEEKDAY_FIELD, DT_SHORTER - 2*DT_DELTA, 6, 0}, + {LOW_E, UDATPG_WEEKDAY_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, // LOW_E is currently not used in CLDR data, should not be canonical + {LOW_E, UDATPG_WEEKDAY_FIELD, DT_SHORT - DT_DELTA, 3, 0}, + {LOW_E, UDATPG_WEEKDAY_FIELD, DT_LONG - DT_DELTA, 4, 0}, + {LOW_E, UDATPG_WEEKDAY_FIELD, DT_NARROW - DT_DELTA, 5, 0}, + {LOW_E, UDATPG_WEEKDAY_FIELD, DT_SHORTER - DT_DELTA, 6, 0}, + + {LOW_D, UDATPG_DAY_FIELD, DT_NUMERIC, 1, 2}, + {LOW_G, UDATPG_DAY_FIELD, DT_NUMERIC + DT_DELTA, 1, 20}, // really internal use, so we don't care + + {CAP_D, UDATPG_DAY_OF_YEAR_FIELD, DT_NUMERIC, 1, 3}, + + {CAP_F, UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, DT_NUMERIC, 1, 0}, + + {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_SHORT, 1, 3}, + {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_LONG, 4, 0}, + {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_NARROW, 5, 0}, + {LOW_B, UDATPG_DAYPERIOD_FIELD, DT_SHORT - DT_DELTA, 1, 3}, + {LOW_B, UDATPG_DAYPERIOD_FIELD, DT_LONG - DT_DELTA, 4, 0}, + {LOW_B, UDATPG_DAYPERIOD_FIELD, DT_NARROW - DT_DELTA, 5, 0}, + // b needs to be closer to a than to B, so we make this 3*DT_DELTA + {CAP_B, UDATPG_DAYPERIOD_FIELD, DT_SHORT - 3*DT_DELTA, 1, 3}, + {CAP_B, UDATPG_DAYPERIOD_FIELD, DT_LONG - 3*DT_DELTA, 4, 0}, + {CAP_B, UDATPG_DAYPERIOD_FIELD, DT_NARROW - 3*DT_DELTA, 5, 0}, + + {CAP_H, UDATPG_HOUR_FIELD, DT_NUMERIC + 10*DT_DELTA, 1, 2}, // 24 hour + {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + 11*DT_DELTA, 1, 2}, // 24 hour + {LOW_H, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12 hour + {CAP_K, UDATPG_HOUR_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, // 12 hour + // The C code has had versions of the following 3, keep & update. Should not need these, but... + // Without these, certain tests using e.g. staticGetSkeleton fail because j/J in patterns + // get skipped instead of mapped to the right hour chars, for example in + // DateFormatTest::TestPatternFromSkeleton + // IntlTestDateTimePatternGeneratorAPI:: testStaticGetSkeleton + // DateIntervalFormatTest::testTicket11985 + // Need to investigate better handling of jJC replacement e.g. in staticGetSkeleton. + {CAP_J, UDATPG_HOUR_FIELD, DT_NUMERIC + 5*DT_DELTA, 1, 2}, // 12/24 hour no AM/PM + {LOW_J, UDATPG_HOUR_FIELD, DT_NUMERIC + 6*DT_DELTA, 1, 6}, // 12/24 hour + {CAP_C, UDATPG_HOUR_FIELD, DT_NUMERIC + 7*DT_DELTA, 1, 6}, // 12/24 hour with preferred dayPeriods for 12 + + {LOW_M, UDATPG_MINUTE_FIELD, DT_NUMERIC, 1, 2}, + + {LOW_S, UDATPG_SECOND_FIELD, DT_NUMERIC, 1, 2}, + {CAP_A, UDATPG_SECOND_FIELD, DT_NUMERIC + DT_DELTA, 1, 1000}, + + {CAP_S, UDATPG_FRACTIONAL_SECOND_FIELD, DT_NUMERIC, 1, 1000}, + + {LOW_V, UDATPG_ZONE_FIELD, DT_SHORT - 2*DT_DELTA, 1, 0}, + {LOW_V, UDATPG_ZONE_FIELD, DT_LONG - 2*DT_DELTA, 4, 0}, + {LOW_Z, UDATPG_ZONE_FIELD, DT_SHORT, 1, 3}, + {LOW_Z, UDATPG_ZONE_FIELD, DT_LONG, 4, 0}, + {CAP_Z, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 3}, + {CAP_Z, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, + {CAP_Z, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 5, 0}, + {CAP_O, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0}, + {CAP_O, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, + {CAP_V, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0}, + {CAP_V, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 2, 0}, + {CAP_V, UDATPG_ZONE_FIELD, DT_LONG-1 - DT_DELTA, 3, 0}, + {CAP_V, UDATPG_ZONE_FIELD, DT_LONG-2 - DT_DELTA, 4, 0}, + {CAP_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0}, + {CAP_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0}, + {CAP_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, + {LOW_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0}, + {LOW_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0}, + {LOW_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, + + {0, UDATPG_FIELD_COUNT, 0, 0, 0} , // last row of dtTypes[] + }; + +static const char* const CLDR_FIELD_APPEND[] = { + "Era", "Year", "Quarter", "Month", "Week", "*", "Day-Of-Week", + "*", "*", "Day", "*", // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J + "Hour", "Minute", "Second", "*", "Timezone" +}; + +static const char* const CLDR_FIELD_NAME[UDATPG_FIELD_COUNT] = { + "era", "year", "quarter", "month", "week", "weekOfMonth", "weekday", + "dayOfYear", "weekdayOfMonth", "day", "dayperiod", // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J + "hour", "minute", "second", "*", "zone" +}; + +static const char* const CLDR_FIELD_WIDTH[] = { // [UDATPG_WIDTH_COUNT] + "", "-short", "-narrow" +}; + +static constexpr UDateTimePGDisplayWidth UDATPG_WIDTH_APPENDITEM = UDATPG_WIDE; +static constexpr int32_t UDATPG_FIELD_KEY_MAX = 24; // max length of CLDR field tag (type + width) + +// For appendItems +static const char16_t UDATPG_ItemFormat[]= {0x7B, 0x30, 0x7D, 0x20, 0x251C, 0x7B, 0x32, 0x7D, 0x3A, + 0x20, 0x7B, 0x31, 0x7D, 0x2524, 0}; // {0} \u251C{2}: {1}\u2524 + +//static const char16_t repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ" + +static const char DT_DateTimePatternsTag[]="DateTimePatterns"; +static const char DT_DateAtTimePatternsTag[]="DateTimePatterns%atTime"; +static const char DT_DateTimeCalendarTag[]="calendar"; +static const char DT_DateTimeGregorianTag[]="gregorian"; +static const char DT_DateTimeAppendItemsTag[]="appendItems"; +static const char DT_DateTimeFieldsTag[]="fields"; +static const char DT_DateTimeAvailableFormatsTag[]="availableFormats"; +//static const UnicodeString repeatedPattern=UnicodeString(repeatedPatterns); + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimePatternGenerator) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTSkeletonEnumeration) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTRedundantEnumeration) + +DateTimePatternGenerator* U_EXPORT2 +DateTimePatternGenerator::createInstance(UErrorCode& status) { + return createInstance(Locale::getDefault(), status); +} + +DateTimePatternGenerator* U_EXPORT2 +DateTimePatternGenerator::createInstance(const Locale& locale, UErrorCode& status) { + if (U_FAILURE(status)) { + return nullptr; + } + LocalPointer result( + new DateTimePatternGenerator(locale, status), status); + return U_SUCCESS(status) ? result.orphan() : nullptr; +} + +DateTimePatternGenerator* U_EXPORT2 +DateTimePatternGenerator::createInstanceNoStdPat(const Locale& locale, UErrorCode& status) { + if (U_FAILURE(status)) { + return nullptr; + } + LocalPointer result( + new DateTimePatternGenerator(locale, status, true), status); + return U_SUCCESS(status) ? result.orphan() : nullptr; +} + +DateTimePatternGenerator* U_EXPORT2 +DateTimePatternGenerator::createEmptyInstance(UErrorCode& status) { + if (U_FAILURE(status)) { + return nullptr; + } + LocalPointer result( + new DateTimePatternGenerator(status), status); + return U_SUCCESS(status) ? result.orphan() : nullptr; +} + +DateTimePatternGenerator::DateTimePatternGenerator(UErrorCode &status) : + skipMatcher(nullptr), + fAvailableFormatKeyHash(nullptr), + fDefaultHourFormatChar(0), + internalErrorCode(U_ZERO_ERROR) +{ + fp = new FormatParser(); + dtMatcher = new DateTimeMatcher(); + distanceInfo = new DistanceInfo(); + patternMap = new PatternMap(); + if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) { + internalErrorCode = status = U_MEMORY_ALLOCATION_ERROR; + } +} + +DateTimePatternGenerator::DateTimePatternGenerator(const Locale& locale, UErrorCode &status, UBool skipStdPatterns) : + skipMatcher(nullptr), + fAvailableFormatKeyHash(nullptr), + fDefaultHourFormatChar(0), + internalErrorCode(U_ZERO_ERROR) +{ + fp = new FormatParser(); + dtMatcher = new DateTimeMatcher(); + distanceInfo = new DistanceInfo(); + patternMap = new PatternMap(); + if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) { + internalErrorCode = status = U_MEMORY_ALLOCATION_ERROR; + } + else { + initData(locale, status, skipStdPatterns); + } +} + +DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerator& other) : + UObject(), + skipMatcher(nullptr), + fAvailableFormatKeyHash(nullptr), + fDefaultHourFormatChar(0), + internalErrorCode(U_ZERO_ERROR) +{ + fp = new FormatParser(); + dtMatcher = new DateTimeMatcher(); + distanceInfo = new DistanceInfo(); + patternMap = new PatternMap(); + if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) { + internalErrorCode = U_MEMORY_ALLOCATION_ERROR; + } + *this=other; +} + +DateTimePatternGenerator& +DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) { + // reflexive case + if (&other == this) { + return *this; + } + internalErrorCode = other.internalErrorCode; + pLocale = other.pLocale; + fDefaultHourFormatChar = other.fDefaultHourFormatChar; + *fp = *(other.fp); + dtMatcher->copyFrom(other.dtMatcher->skeleton); + *distanceInfo = *(other.distanceInfo); + for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { + dateTimeFormat[style] = other.dateTimeFormat[style]; + } + decimal = other.decimal; + for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { + dateTimeFormat[style].getTerminatedBuffer(); // NUL-terminate for the C API. + } + decimal.getTerminatedBuffer(); + delete skipMatcher; + if ( other.skipMatcher == nullptr ) { + skipMatcher = nullptr; + } + else { + skipMatcher = new DateTimeMatcher(*other.skipMatcher); + if (skipMatcher == nullptr) + { + internalErrorCode = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + } + for (int32_t i=0; i< UDATPG_FIELD_COUNT; ++i ) { + appendItemFormats[i] = other.appendItemFormats[i]; + appendItemFormats[i].getTerminatedBuffer(); // NUL-terminate for the C API. + for (int32_t j=0; j< UDATPG_WIDTH_COUNT; ++j ) { + fieldDisplayNames[i][j] = other.fieldDisplayNames[i][j]; + fieldDisplayNames[i][j].getTerminatedBuffer(); // NUL-terminate for the C API. + } + } + patternMap->copyFrom(*other.patternMap, internalErrorCode); + copyHashtable(other.fAvailableFormatKeyHash, internalErrorCode); + return *this; +} + + +bool +DateTimePatternGenerator::operator==(const DateTimePatternGenerator& other) const { + if (this == &other) { + return true; + } + if ((pLocale==other.pLocale) && (patternMap->equals(*other.patternMap)) && + (decimal==other.decimal)) { + for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { + if (dateTimeFormat[style] != other.dateTimeFormat[style]) { + return false; + } + } + for ( int32_t i=0 ; i list; + int32_t length = 0; + int32_t preferredFormat = ALLOWED_HOUR_FORMAT_UNKNOWN; + for (int32_t j = 0; formatList.getKeyAndValue(j, key, value); ++j) { + if (uprv_strcmp(key, "allowed") == 0) { + if (value.getType() == URES_STRING) { + length = 2; // 1 preferred to add later, 1 allowed to add now + if (list.allocateInsteadAndReset(length + 1) == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + list[1] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode)); + } + else { + ResourceArray allowedFormats = value.getArray(errorCode); + length = allowedFormats.getSize() + 1; // 1 preferred, getSize allowed + if (list.allocateInsteadAndReset(length + 1) == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + for (int32_t k = 1; k < length; ++k) { + allowedFormats.getValue(k-1, value); + list[k] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode)); + } + } + } else if (uprv_strcmp(key, "preferred") == 0) { + preferredFormat = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode)); + } + } + if (length > 1) { + list[0] = (preferredFormat!=ALLOWED_HOUR_FORMAT_UNKNOWN)? preferredFormat: list[1]; + } else { + // fallback handling for missing data + length = 2; // 1 preferred, 1 allowed + if (list.allocateInsteadAndReset(length + 1) == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + list[0] = (preferredFormat!=ALLOWED_HOUR_FORMAT_UNKNOWN)? preferredFormat: ALLOWED_HOUR_FORMAT_H; + list[1] = list[0]; + } + list[length] = ALLOWED_HOUR_FORMAT_UNKNOWN; + // At this point list[] will have at least two non-ALLOWED_HOUR_FORMAT_UNKNOWN entries, + // followed by ALLOWED_HOUR_FORMAT_UNKNOWN. + uhash_put(localeToAllowedHourFormatsMap, const_cast(regionOrLocale), list.orphan(), &errorCode); + if (U_FAILURE(errorCode)) { return; } + } + } + + AllowedHourFormat getHourFormatFromUnicodeString(const UnicodeString &s) { + if (s.length() == 1) { + if (s[0] == LOW_H) { return ALLOWED_HOUR_FORMAT_h; } + if (s[0] == CAP_H) { return ALLOWED_HOUR_FORMAT_H; } + if (s[0] == CAP_K) { return ALLOWED_HOUR_FORMAT_K; } + if (s[0] == LOW_K) { return ALLOWED_HOUR_FORMAT_k; } + } else if (s.length() == 2) { + if (s[0] == LOW_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_hb; } + if (s[0] == LOW_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_hB; } + if (s[0] == CAP_K && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_Kb; } + if (s[0] == CAP_K && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_KB; } + if (s[0] == CAP_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_Hb; } + if (s[0] == CAP_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_HB; } + } + + return ALLOWED_HOUR_FORMAT_UNKNOWN; + } +}; + +} // namespace + +AllowedHourFormatsSink::~AllowedHourFormatsSink() {} + +U_CFUNC void U_CALLCONV DateTimePatternGenerator::loadAllowedHourFormatsData(UErrorCode &status) { + if (U_FAILURE(status)) { return; } + localeToAllowedHourFormatsMap = uhash_open( + uhash_hashChars, uhash_compareChars, nullptr, &status); + if (U_FAILURE(status)) { return; } + + uhash_setValueDeleter(localeToAllowedHourFormatsMap, deleteAllowedHourFormats); + ucln_i18n_registerCleanup(UCLN_I18N_ALLOWED_HOUR_FORMATS, allowedHourFormatsCleanup); + + LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "supplementalData", &status)); + if (U_FAILURE(status)) { return; } + + AllowedHourFormatsSink sink; + // TODO: Currently in the enumeration each table allocates a new array. + // Try to reduce the number of memory allocations. Consider storing a + // UVector32 with the concatenation of all of the sub-arrays, put the start index + // into the hashmap, store 6 single-value sub-arrays right at the beginning of the + // vector (at index enum*2) for easy data sharing, copy sub-arrays into runtime + // object. Remember to clean up the vector, too. + ures_getAllItemsWithFallback(rb.getAlias(), "timeData", sink, status); +} + +static int32_t* getAllowedHourFormatsLangCountry(const char* language, const char* country, UErrorCode& status) { + CharString langCountry; + langCountry.append(language, status); + langCountry.append('_', status); + langCountry.append(country, status); + + int32_t* allowedFormats; + allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, langCountry.data()); + if (allowedFormats == nullptr) { + allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, const_cast(country)); + } + + return allowedFormats; +} + +void DateTimePatternGenerator::getAllowedHourFormats(const Locale &locale, UErrorCode &status) { + if (U_FAILURE(status)) { return; } + + const char *language = locale.getLanguage(); + const char *country = locale.getCountry(); + + char regionOverride[8]; + int32_t regionOverrideLength = locale.getKeywordValue("rg", regionOverride, sizeof(regionOverride), status); + if (U_SUCCESS(status) && regionOverrideLength > 0) { + country = regionOverride; + if (regionOverrideLength > 2) { + // chop off any subdivision codes that may have been included + regionOverride[2] = '\0'; + } + } + + Locale maxLocale; // must be here for correct lifetime + if (*language == '\0' || *country == '\0') { + maxLocale = locale; + UErrorCode localStatus = U_ZERO_ERROR; + maxLocale.addLikelySubtags(localStatus); + if (U_SUCCESS(localStatus)) { + language = maxLocale.getLanguage(); + country = maxLocale.getCountry(); + } + } + if (*language == '\0') { + // Unexpected, but fail gracefully + language = "und"; + } + if (*country == '\0') { + country = "001"; + } + + int32_t* allowedFormats = getAllowedHourFormatsLangCountry(language, country, status); + + // We need to check if there is an hour cycle on locale + char buffer[8]; + int32_t count = locale.getKeywordValue("hours", buffer, sizeof(buffer), status); + + fDefaultHourFormatChar = 0; + if (U_SUCCESS(status) && count > 0) { + if(uprv_strcmp(buffer, "h24") == 0) { + fDefaultHourFormatChar = LOW_K; + } else if(uprv_strcmp(buffer, "h23") == 0) { + fDefaultHourFormatChar = CAP_H; + } else if(uprv_strcmp(buffer, "h12") == 0) { + fDefaultHourFormatChar = LOW_H; + } else if(uprv_strcmp(buffer, "h11") == 0) { + fDefaultHourFormatChar = CAP_K; + } + } + + // Check if the region has an alias + if (allowedFormats == nullptr) { + UErrorCode localStatus = U_ZERO_ERROR; + const Region* region = Region::getInstance(country, localStatus); + if (U_SUCCESS(localStatus)) { + country = region->getRegionCode(); // the real region code + allowedFormats = getAllowedHourFormatsLangCountry(language, country, status); + } + } + + if (allowedFormats != nullptr) { // Lookup is successful + // Here allowedFormats points to a list consisting of key for preferredFormat, + // followed by one or more keys for allowedFormats, then followed by ALLOWED_HOUR_FORMAT_UNKNOWN. + if (!fDefaultHourFormatChar) { + switch (allowedFormats[0]) { + case ALLOWED_HOUR_FORMAT_h: fDefaultHourFormatChar = LOW_H; break; + case ALLOWED_HOUR_FORMAT_H: fDefaultHourFormatChar = CAP_H; break; + case ALLOWED_HOUR_FORMAT_K: fDefaultHourFormatChar = CAP_K; break; + case ALLOWED_HOUR_FORMAT_k: fDefaultHourFormatChar = LOW_K; break; + default: fDefaultHourFormatChar = CAP_H; break; + } + } + + for (int32_t i = 0; i < UPRV_LENGTHOF(fAllowedHourFormats); ++i) { + fAllowedHourFormats[i] = allowedFormats[i + 1]; + if (fAllowedHourFormats[i] == ALLOWED_HOUR_FORMAT_UNKNOWN) { + break; + } + } + } else { // Lookup failed, twice + if (!fDefaultHourFormatChar) { + fDefaultHourFormatChar = CAP_H; + } + fAllowedHourFormats[0] = ALLOWED_HOUR_FORMAT_H; + fAllowedHourFormats[1] = ALLOWED_HOUR_FORMAT_UNKNOWN; + } +} + +UDateFormatHourCycle +DateTimePatternGenerator::getDefaultHourCycle(UErrorCode& status) const { + if (U_FAILURE(status)) { + return UDAT_HOUR_CYCLE_23; + } + if (fDefaultHourFormatChar == 0) { + // We need to return something, but the caller should ignore it + // anyways since the returned status is a failure. + status = U_UNSUPPORTED_ERROR; + return UDAT_HOUR_CYCLE_23; + } + switch (fDefaultHourFormatChar) { + case CAP_K: + return UDAT_HOUR_CYCLE_11; + case LOW_H: + return UDAT_HOUR_CYCLE_12; + case CAP_H: + return UDAT_HOUR_CYCLE_23; + case LOW_K: + return UDAT_HOUR_CYCLE_24; + default: + UPRV_UNREACHABLE_EXIT; + } +} + +UnicodeString +DateTimePatternGenerator::getSkeleton(const UnicodeString& pattern, UErrorCode& +/*status*/) { + FormatParser fp2; + DateTimeMatcher matcher; + PtnSkeleton localSkeleton; + matcher.set(pattern, &fp2, localSkeleton); + return localSkeleton.getSkeleton(); +} + +UnicodeString +DateTimePatternGenerator::staticGetSkeleton( + const UnicodeString& pattern, UErrorCode& /*status*/) { + FormatParser fp; + DateTimeMatcher matcher; + PtnSkeleton localSkeleton; + matcher.set(pattern, &fp, localSkeleton); + return localSkeleton.getSkeleton(); +} + +UnicodeString +DateTimePatternGenerator::getBaseSkeleton(const UnicodeString& pattern, UErrorCode& /*status*/) { + FormatParser fp2; + DateTimeMatcher matcher; + PtnSkeleton localSkeleton; + matcher.set(pattern, &fp2, localSkeleton); + return localSkeleton.getBaseSkeleton(); +} + +UnicodeString +DateTimePatternGenerator::staticGetBaseSkeleton( + const UnicodeString& pattern, UErrorCode& /*status*/) { + FormatParser fp; + DateTimeMatcher matcher; + PtnSkeleton localSkeleton; + matcher.set(pattern, &fp, localSkeleton); + return localSkeleton.getBaseSkeleton(); +} + +void +DateTimePatternGenerator::addICUPatterns(const Locale& locale, UErrorCode& status) { + if (U_FAILURE(status)) { return; } + UnicodeString dfPattern; + UnicodeString conflictingString; + DateFormat* df; + + // Load with ICU patterns + for (int32_t i=DateFormat::kFull; i<=DateFormat::kShort; i++) { + DateFormat::EStyle style = (DateFormat::EStyle)i; + df = DateFormat::createDateInstance(style, locale); + SimpleDateFormat* sdf; + if (df != nullptr && (sdf = dynamic_cast(df)) != nullptr) { + sdf->toPattern(dfPattern); + addPattern(dfPattern, false, conflictingString, status); + } + // TODO Maybe we should return an error when the date format isn't simple. + delete df; + if (U_FAILURE(status)) { return; } + + df = DateFormat::createTimeInstance(style, locale); + if (df != nullptr && (sdf = dynamic_cast(df)) != nullptr) { + sdf->toPattern(dfPattern); + addPattern(dfPattern, false, conflictingString, status); + + // TODO: C++ and Java are inconsistent (see #12568). + // C++ uses MEDIUM, but Java uses SHORT. + if ( i==DateFormat::kShort && !dfPattern.isEmpty() ) { + consumeShortTimePattern(dfPattern, status); + } + } + // TODO Maybe we should return an error when the date format isn't simple. + delete df; + if (U_FAILURE(status)) { return; } + } +} + +void +DateTimePatternGenerator::hackTimes(const UnicodeString& hackPattern, UErrorCode& status) { + UnicodeString conflictingString; + + fp->set(hackPattern); + UnicodeString mmss; + UBool gotMm=false; + for (int32_t i=0; iitemNumber; ++i) { + UnicodeString field = fp->items[i]; + if ( fp->isQuoteLiteral(field) ) { + if ( gotMm ) { + UnicodeString quoteLiteral; + fp->getQuoteLiteral(quoteLiteral, &i); + mmss += quoteLiteral; + } + } + else { + if (fp->isPatternSeparator(field) && gotMm) { + mmss+=field; + } + else { + char16_t ch=field.charAt(0); + if (ch==LOW_M) { + gotMm=true; + mmss+=field; + } + else { + if (ch==LOW_S) { + if (!gotMm) { + break; + } + mmss+= field; + addPattern(mmss, false, conflictingString, status); + break; + } + else { + if (gotMm || ch==LOW_Z || ch==CAP_Z || ch==LOW_V || ch==CAP_V) { + break; + } + } + } + } + } + } +} + +#define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY) + +void +DateTimePatternGenerator::getCalendarTypeToUse(const Locale& locale, CharString& destination, UErrorCode& err) { + destination.clear().append(DT_DateTimeGregorianTag, -1, err); // initial default + if ( U_SUCCESS(err) ) { + UErrorCode localStatus = U_ZERO_ERROR; + char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY]; + // obtain a locale that always has the calendar key value that should be used + ures_getFunctionalEquivalent( + localeWithCalendarKey, + ULOC_LOCALE_IDENTIFIER_CAPACITY, + nullptr, + "calendar", + "calendar", + locale.getName(), + nullptr, + false, + &localStatus); + localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination + // now get the calendar key value from that locale + char calendarType[ULOC_KEYWORDS_CAPACITY]; + int32_t calendarTypeLen = uloc_getKeywordValue( + localeWithCalendarKey, + "calendar", + calendarType, + ULOC_KEYWORDS_CAPACITY, + &localStatus); + // If the input locale was invalid, don't fail with missing resource error, instead + // continue with default of Gregorian. + if (U_FAILURE(localStatus) && localStatus != U_MISSING_RESOURCE_ERROR) { + err = localStatus; + return; + } + if (calendarTypeLen > 0 && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) { + destination.clear().append(calendarType, -1, err); + if (U_FAILURE(err)) { return; } + } + } +} + +void +DateTimePatternGenerator::consumeShortTimePattern(const UnicodeString& shortTimePattern, + UErrorCode& status) { + if (U_FAILURE(status)) { return; } + // ICU-20383 No longer set fDefaultHourFormatChar to the hour format character from + // this pattern; instead it is set from localeToAllowedHourFormatsMap which now + // includes entries for both preferred and allowed formats. + + // HACK for hh:ss + hackTimes(shortTimePattern, status); +} + +struct DateTimePatternGenerator::AppendItemFormatsSink : public ResourceSink { + + // Destination for data, modified via setters. + DateTimePatternGenerator& dtpg; + + AppendItemFormatsSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {} + virtual ~AppendItemFormatsSink(); + + virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, + UErrorCode &errorCode) override { + UDateTimePatternField field = dtpg.getAppendFormatNumber(key); + if (field == UDATPG_FIELD_COUNT) { return; } + const UnicodeString& valueStr = value.getUnicodeString(errorCode); + if (dtpg.getAppendItemFormat(field).isEmpty() && !valueStr.isEmpty()) { + dtpg.setAppendItemFormat(field, valueStr); + } + } + + void fillInMissing() { + UnicodeString defaultItemFormat(true, UDATPG_ItemFormat, UPRV_LENGTHOF(UDATPG_ItemFormat)-1); // Read-only alias. + for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) { + UDateTimePatternField field = (UDateTimePatternField)i; + if (dtpg.getAppendItemFormat(field).isEmpty()) { + dtpg.setAppendItemFormat(field, defaultItemFormat); + } + } + } +}; + +struct DateTimePatternGenerator::AppendItemNamesSink : public ResourceSink { + + // Destination for data, modified via setters. + DateTimePatternGenerator& dtpg; + + AppendItemNamesSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {} + virtual ~AppendItemNamesSink(); + + virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, + UErrorCode &errorCode) override { + UDateTimePGDisplayWidth width; + UDateTimePatternField field = dtpg.getFieldAndWidthIndices(key, &width); + if (field == UDATPG_FIELD_COUNT) { return; } + ResourceTable detailsTable = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + if (!detailsTable.findValue("dn", value)) { return; } + const UnicodeString& valueStr = value.getUnicodeString(errorCode); + if (U_SUCCESS(errorCode) && dtpg.getFieldDisplayName(field,width).isEmpty() && !valueStr.isEmpty()) { + dtpg.setFieldDisplayName(field,width,valueStr); + } + } + + void fillInMissing() { + for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) { + UnicodeString& valueStr = dtpg.getMutableFieldDisplayName((UDateTimePatternField)i, UDATPG_WIDE); + if (valueStr.isEmpty()) { + valueStr = CAP_F; + U_ASSERT(i < 20); + if (i < 10) { + // F0, F1, ..., F9 + valueStr += (char16_t)(i+0x30); + } else { + // F10, F11, ... + valueStr += (char16_t)0x31; + valueStr += (char16_t)(i-10 + 0x30); + } + // NUL-terminate for the C API. + valueStr.getTerminatedBuffer(); + } + for (int32_t j = 1; j < UDATPG_WIDTH_COUNT; j++) { + UnicodeString& valueStr2 = dtpg.getMutableFieldDisplayName((UDateTimePatternField)i, (UDateTimePGDisplayWidth)j); + if (valueStr2.isEmpty()) { + valueStr2 = dtpg.getFieldDisplayName((UDateTimePatternField)i, (UDateTimePGDisplayWidth)(j-1)); + } + } + } + } +}; + +struct DateTimePatternGenerator::AvailableFormatsSink : public ResourceSink { + + // Destination for data, modified via setters. + DateTimePatternGenerator& dtpg; + + // Temporary variable, required for calling addPatternWithSkeleton. + UnicodeString conflictingPattern; + + AvailableFormatsSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {} + virtual ~AvailableFormatsSink(); + + virtual void put(const char *key, ResourceValue &value, UBool isRoot, + UErrorCode &errorCode) override { + const UnicodeString formatKey(key, -1, US_INV); + if (!dtpg.isAvailableFormatSet(formatKey) ) { + dtpg.setAvailableFormat(formatKey, errorCode); + // Add pattern with its associated skeleton. Override any duplicate + // derived from std patterns, but not a previous availableFormats entry: + const UnicodeString& formatValue = value.getUnicodeString(errorCode); + conflictingPattern.remove(); + dtpg.addPatternWithSkeleton(formatValue, &formatKey, !isRoot, conflictingPattern, errorCode); + } + } +}; + +// Virtual destructors must be defined out of line. +DateTimePatternGenerator::AppendItemFormatsSink::~AppendItemFormatsSink() {} +DateTimePatternGenerator::AppendItemNamesSink::~AppendItemNamesSink() {} +DateTimePatternGenerator::AvailableFormatsSink::~AvailableFormatsSink() {} + +void +DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& errorCode) { + if (U_FAILURE(errorCode)) { return; } + UnicodeString rbPattern, value, field; + CharString path; + + LocalUResourceBundlePointer rb(ures_open(nullptr, locale.getName(), &errorCode)); + if (U_FAILURE(errorCode)) { return; } + + CharString calendarTypeToUse; // to be filled in with the type to use, if all goes well + getCalendarTypeToUse(locale, calendarTypeToUse, errorCode); + if (U_FAILURE(errorCode)) { return; } + + // Local err to ignore resource not found exceptions + UErrorCode err = U_ZERO_ERROR; + + // Load append item formats. + AppendItemFormatsSink appendItemFormatsSink(*this); + path.clear() + .append(DT_DateTimeCalendarTag, errorCode) + .append('/', errorCode) + .append(calendarTypeToUse, errorCode) + .append('/', errorCode) + .append(DT_DateTimeAppendItemsTag, errorCode); // i.e., calendar/xxx/appendItems + if (U_FAILURE(errorCode)) { return; } + ures_getAllChildrenWithFallback(rb.getAlias(), path.data(), appendItemFormatsSink, err); + appendItemFormatsSink.fillInMissing(); + + // Load CLDR item names. + err = U_ZERO_ERROR; + AppendItemNamesSink appendItemNamesSink(*this); + ures_getAllChildrenWithFallback(rb.getAlias(), DT_DateTimeFieldsTag, appendItemNamesSink, err); + appendItemNamesSink.fillInMissing(); + + // Load the available formats from CLDR. + err = U_ZERO_ERROR; + initHashtable(errorCode); + if (U_FAILURE(errorCode)) { return; } + AvailableFormatsSink availableFormatsSink(*this); + path.clear() + .append(DT_DateTimeCalendarTag, errorCode) + .append('/', errorCode) + .append(calendarTypeToUse, errorCode) + .append('/', errorCode) + .append(DT_DateTimeAvailableFormatsTag, errorCode); // i.e., calendar/xxx/availableFormats + if (U_FAILURE(errorCode)) { return; } + ures_getAllChildrenWithFallback(rb.getAlias(), path.data(), availableFormatsSink, err); +} + +void +DateTimePatternGenerator::initHashtable(UErrorCode& err) { + if (U_FAILURE(err)) { return; } + if (fAvailableFormatKeyHash!=nullptr) { + return; + } + LocalPointer hash(new Hashtable(false, err), err); + if (U_SUCCESS(err)) { + fAvailableFormatKeyHash = hash.orphan(); + } +} + +void +DateTimePatternGenerator::setAppendItemFormat(UDateTimePatternField field, const UnicodeString& value) { + appendItemFormats[field] = value; + // NUL-terminate for the C API. + appendItemFormats[field].getTerminatedBuffer(); +} + +const UnicodeString& +DateTimePatternGenerator::getAppendItemFormat(UDateTimePatternField field) const { + return appendItemFormats[field]; +} + +void +DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field, const UnicodeString& value) { + setFieldDisplayName(field, UDATPG_WIDTH_APPENDITEM, value); +} + +const UnicodeString& +DateTimePatternGenerator::getAppendItemName(UDateTimePatternField field) const { + return fieldDisplayNames[field][UDATPG_WIDTH_APPENDITEM]; +} + +void +DateTimePatternGenerator::setFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width, const UnicodeString& value) { + fieldDisplayNames[field][width] = value; + // NUL-terminate for the C API. + fieldDisplayNames[field][width].getTerminatedBuffer(); +} + +UnicodeString +DateTimePatternGenerator::getFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width) const { + return fieldDisplayNames[field][width]; +} + +UnicodeString& +DateTimePatternGenerator::getMutableFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width) { + return fieldDisplayNames[field][width]; +} + +void +DateTimePatternGenerator::getAppendName(UDateTimePatternField field, UnicodeString& value) { + value = SINGLE_QUOTE; + value += fieldDisplayNames[field][UDATPG_WIDTH_APPENDITEM]; + value += SINGLE_QUOTE; +} + +UnicodeString +DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UErrorCode& status) { + return getBestPattern(patternForm, UDATPG_MATCH_NO_OPTIONS, status); +} + +UnicodeString +DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDateTimePatternMatchOptions options, UErrorCode& status) { + if (U_FAILURE(status)) { + return UnicodeString(); + } + if (U_FAILURE(internalErrorCode)) { + status = internalErrorCode; + return UnicodeString(); + } + const UnicodeString *bestPattern = nullptr; + UnicodeString dtFormat; + UnicodeString resultPattern; + int32_t flags = kDTPGNoFlags; + + int32_t dateMask=(1<set(patternFormMapped, fp); + const PtnSkeleton* specifiedSkeleton = nullptr; + bestPattern=getBestRaw(*dtMatcher, -1, distanceInfo, status, &specifiedSkeleton); + if (U_FAILURE(status)) { + return UnicodeString(); + } + + if ( distanceInfo->missingFieldMask==0 && distanceInfo->extraFieldMask==0 ) { + resultPattern = adjustFieldTypes(*bestPattern, specifiedSkeleton, flags, options); + + return resultPattern; + } + int32_t neededFields = dtMatcher->getFieldMask(); + UnicodeString datePattern=getBestAppending(neededFields & dateMask, flags, status, options); + UnicodeString timePattern=getBestAppending(neededFields & timeMask, flags, status, options); + if (U_FAILURE(status)) { + return UnicodeString(); + } + if (datePattern.length()==0) { + if (timePattern.length()==0) { + resultPattern.remove(); + } + else { + return timePattern; + } + } + if (timePattern.length()==0) { + return datePattern; + } + resultPattern.remove(); + status = U_ZERO_ERROR; + // determine which dateTimeFormat to use + PtnSkeleton* reqSkeleton = dtMatcher->getSkeletonPtr(); + UDateFormatStyle style = UDAT_SHORT; + int32_t monthFieldLen = reqSkeleton->baseOriginal.getFieldLength(UDATPG_MONTH_FIELD); + if (monthFieldLen == 4) { + if (reqSkeleton->baseOriginal.getFieldLength(UDATPG_WEEKDAY_FIELD) > 0) { + style = UDAT_FULL; + } else { + style = UDAT_LONG; + } + } else if (monthFieldLen == 3) { + style = UDAT_MEDIUM; + } + // and now use it to compose date and time + dtFormat=getDateTimeFormat(style, status); + SimpleFormatter(dtFormat, 2, 2, status).format(timePattern, datePattern, resultPattern, status); + return resultPattern; +} + +/* + * Map a skeleton that may have metacharacters jJC to one without, by replacing + * the metacharacters with locale-appropriate fields of h/H/k/K and of a/b/B + * (depends on fDefaultHourFormatChar and fAllowedHourFormats being set, which in + * turn depends on initData having been run). This method also updates the flags + * as necessary. Returns the updated skeleton. + */ +UnicodeString +DateTimePatternGenerator::mapSkeletonMetacharacters(const UnicodeString& patternForm, int32_t* flags, UErrorCode& status) { + UnicodeString patternFormMapped; + patternFormMapped.remove(); + UBool inQuoted = false; + int32_t patPos, patLen = patternForm.length(); + for (patPos = 0; patPos < patLen; patPos++) { + char16_t patChr = patternForm.charAt(patPos); + if (patChr == SINGLE_QUOTE) { + inQuoted = !inQuoted; + } else if (!inQuoted) { + // Handle special mappings for 'j' and 'C' in which fields lengths + // 1,3,5 => hour field length 1 + // 2,4,6 => hour field length 2 + // 1,2 => abbreviated dayPeriod (field length 1..3) + // 3,4 => long dayPeriod (field length 4) + // 5,6 => narrow dayPeriod (field length 5) + if (patChr == LOW_J || patChr == CAP_C) { + int32_t extraLen = 0; // 1 less than total field length + while (patPos+1 < patLen && patternForm.charAt(patPos+1)==patChr) { + extraLen++; + patPos++; + } + int32_t hourLen = 1 + (extraLen & 1); + int32_t dayPeriodLen = (extraLen < 2)? 1: 3 + (extraLen >> 1); + char16_t hourChar = LOW_H; + char16_t dayPeriodChar = LOW_A; + if (patChr == LOW_J) { + hourChar = fDefaultHourFormatChar; + } else { + AllowedHourFormat bestAllowed; + if (fAllowedHourFormats[0] != ALLOWED_HOUR_FORMAT_UNKNOWN) { + bestAllowed = (AllowedHourFormat)fAllowedHourFormats[0]; + } else { + status = U_INVALID_FORMAT_ERROR; + return UnicodeString(); + } + if (bestAllowed == ALLOWED_HOUR_FORMAT_H || bestAllowed == ALLOWED_HOUR_FORMAT_HB || bestAllowed == ALLOWED_HOUR_FORMAT_Hb) { + hourChar = CAP_H; + } else if (bestAllowed == ALLOWED_HOUR_FORMAT_K || bestAllowed == ALLOWED_HOUR_FORMAT_KB || bestAllowed == ALLOWED_HOUR_FORMAT_Kb) { + hourChar = CAP_K; + } else if (bestAllowed == ALLOWED_HOUR_FORMAT_k) { + hourChar = LOW_K; + } + // in #13183 just add b/B to skeleton, no longer need to set special flags + if (bestAllowed == ALLOWED_HOUR_FORMAT_HB || bestAllowed == ALLOWED_HOUR_FORMAT_hB || bestAllowed == ALLOWED_HOUR_FORMAT_KB) { + dayPeriodChar = CAP_B; + } else if (bestAllowed == ALLOWED_HOUR_FORMAT_Hb || bestAllowed == ALLOWED_HOUR_FORMAT_hb || bestAllowed == ALLOWED_HOUR_FORMAT_Kb) { + dayPeriodChar = LOW_B; + } + } + if (hourChar==CAP_H || hourChar==LOW_K) { + dayPeriodLen = 0; + } + while (dayPeriodLen-- > 0) { + patternFormMapped.append(dayPeriodChar); + } + while (hourLen-- > 0) { + patternFormMapped.append(hourChar); + } + } else if (patChr == CAP_J) { + // Get pattern for skeleton with H, then replace H or k + // with fDefaultHourFormatChar (if different) + patternFormMapped.append(CAP_H); + *flags |= kDTPGSkeletonUsesCapJ; + } else { + patternFormMapped.append(patChr); + } + } + } + return patternFormMapped; +} + +UnicodeString +DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern, + const UnicodeString& skeleton, + UErrorCode& status) { + return replaceFieldTypes(pattern, skeleton, UDATPG_MATCH_NO_OPTIONS, status); +} + +UnicodeString +DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern, + const UnicodeString& skeleton, + UDateTimePatternMatchOptions options, + UErrorCode& status) { + if (U_FAILURE(status)) { + return UnicodeString(); + } + if (U_FAILURE(internalErrorCode)) { + status = internalErrorCode; + return UnicodeString(); + } + dtMatcher->set(skeleton, fp); + UnicodeString result = adjustFieldTypes(pattern, nullptr, kDTPGNoFlags, options); + return result; +} + +void +DateTimePatternGenerator::setDecimal(const UnicodeString& newDecimal) { + this->decimal = newDecimal; + // NUL-terminate for the C API. + this->decimal.getTerminatedBuffer(); +} + +const UnicodeString& +DateTimePatternGenerator::getDecimal() const { + return decimal; +} + +void +DateTimePatternGenerator::addCanonicalItems(UErrorCode& status) { + if (U_FAILURE(status)) { return; } + UnicodeString conflictingPattern; + + for (int32_t i=0; i 0) { + addPattern(UnicodeString(Canonical_Items[i]), false, conflictingPattern, status); + } + if (U_FAILURE(status)) { return; } + } +} + +void +DateTimePatternGenerator::setDateTimeFormat(const UnicodeString& dtFormat) { + UErrorCode status = U_ZERO_ERROR; + for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { + setDateTimeFormat((UDateFormatStyle)style, dtFormat, status); + } +} + +const UnicodeString& +DateTimePatternGenerator::getDateTimeFormat() const { + UErrorCode status = U_ZERO_ERROR; + return getDateTimeFormat(UDAT_MEDIUM, status); +} + +void +DateTimePatternGenerator::setDateTimeFormat(UDateFormatStyle style, const UnicodeString& dtFormat, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (style < UDAT_FULL || style > UDAT_SHORT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + dateTimeFormat[style] = dtFormat; + // Note for the following: getTerminatedBuffer() can re-allocate the UnicodeString + // buffer so we do this here before clients request a const ref to the UnicodeString + // or its buffer. + dateTimeFormat[style].getTerminatedBuffer(); // NUL-terminate for the C API. +} + +const UnicodeString& +DateTimePatternGenerator::getDateTimeFormat(UDateFormatStyle style, UErrorCode& status) const { + static const UnicodeString emptyString = UNICODE_STRING_SIMPLE(""); + if (U_FAILURE(status)) { + return emptyString; + } + if (style < UDAT_FULL || style > UDAT_SHORT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return emptyString; + } + return dateTimeFormat[style]; +} + +static const int32_t cTypeBufMax = 32; + +void +DateTimePatternGenerator::setDateTimeFromCalendar(const Locale& locale, UErrorCode& status) { + if (U_FAILURE(status)) { return; } + + const char16_t *resStr; + int32_t resStrLen = 0; + + LocalUResourceBundlePointer calData(ures_open(nullptr, locale.getBaseName(), &status)); + if (U_FAILURE(status)) { return; } + ures_getByKey(calData.getAlias(), DT_DateTimeCalendarTag, calData.getAlias(), &status); + if (U_FAILURE(status)) { return; } + + char cType[cTypeBufMax + 1]; + Calendar::getCalendarTypeFromLocale(locale, cType, cTypeBufMax, status); + cType[cTypeBufMax] = 0; + if (U_FAILURE(status) || cType[0] == 0) { + status = U_ZERO_ERROR; + uprv_strcpy(cType, DT_DateTimeGregorianTag); + } + UBool cTypeIsGregorian = (uprv_strcmp(cType, DT_DateTimeGregorianTag) == 0); + + // Currently, for compatibility with pre-CLDR-42 data, we default to the "atTime" + // combining patterns. Depending on guidance in CLDR 42 spec and on DisplayOptions, + // we may change this. + LocalUResourceBundlePointer specificCalBundle; + LocalUResourceBundlePointer dateTimePatterns; + int32_t dateTimeOffset = 0; // initially for DateTimePatterns%atTime + if (!cTypeIsGregorian) { + specificCalBundle.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), cType, + nullptr, &status)); + dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(specificCalBundle.getAlias(), DT_DateAtTimePatternsTag, // the %atTime variant, 4 entries + nullptr, &status)); + } + if (dateTimePatterns.isNull() || status == U_MISSING_RESOURCE_ERROR) { + status = U_ZERO_ERROR; + specificCalBundle.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), DT_DateTimeGregorianTag, + nullptr, &status)); + dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(specificCalBundle.getAlias(), DT_DateAtTimePatternsTag, // the %atTime variant, 4 entries + nullptr, &status)); + } + if (U_SUCCESS(status) && (ures_getSize(dateTimePatterns.getAlias()) < 4)) { + status = U_INVALID_FORMAT_ERROR; + } + if (status == U_MISSING_RESOURCE_ERROR) { + // Try again with standard variant + status = U_ZERO_ERROR; + dateTimePatterns.orphan(); + dateTimeOffset = (int32_t)DateFormat::kDateTimeOffset; + if (!cTypeIsGregorian) { + specificCalBundle.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), cType, + nullptr, &status)); + dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(specificCalBundle.getAlias(), DT_DateTimePatternsTag, // the standard variant, 13 entries + nullptr, &status)); + } + if (dateTimePatterns.isNull() || status == U_MISSING_RESOURCE_ERROR) { + status = U_ZERO_ERROR; + specificCalBundle.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), DT_DateTimeGregorianTag, + nullptr, &status)); + dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(specificCalBundle.getAlias(), DT_DateTimePatternsTag, // the standard variant, 13 entries + nullptr, &status)); + } + if (U_SUCCESS(status) && (ures_getSize(dateTimePatterns.getAlias()) <= DateFormat::kDateTimeOffset + DateFormat::kShort)) { + status = U_INVALID_FORMAT_ERROR; + } + } + if (U_FAILURE(status)) { return; } + for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) { + resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), dateTimeOffset + style, &resStrLen, &status); + setDateTimeFormat((UDateFormatStyle)style, UnicodeString(true, resStr, resStrLen), status); + } +} + +void +DateTimePatternGenerator::setDecimalSymbols(const Locale& locale, UErrorCode& status) { + DecimalFormatSymbols dfs = DecimalFormatSymbols(locale, status); + if(U_SUCCESS(status)) { + decimal = dfs.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); + // NUL-terminate for the C API. + decimal.getTerminatedBuffer(); + } +} + +UDateTimePatternConflict +DateTimePatternGenerator::addPattern( + const UnicodeString& pattern, + UBool override, + UnicodeString &conflictingPattern, + UErrorCode& status) +{ + if (U_FAILURE(internalErrorCode)) { + status = internalErrorCode; + return UDATPG_NO_CONFLICT; + } + + return addPatternWithSkeleton(pattern, nullptr, override, conflictingPattern, status); +} + +// For DateTimePatternGenerator::addPatternWithSkeleton - +// If skeletonToUse is specified, then an availableFormats entry is being added. In this case: +// 1. We pass that skeleton to matcher.set instead of having it derive a skeleton from the pattern. +// 2. If the new entry's skeleton or basePattern does match an existing entry but that entry also had a skeleton specified +// (i.e. it was also from availableFormats), then the new entry does not override it regardless of the value of the override +// parameter. This prevents later availableFormats entries from a parent locale overriding earlier ones from the actual +// specified locale. However, availableFormats entries *should* override entries with matching skeleton whose skeleton was +// derived (i.e. entries derived from the standard date/time patters for the specified locale). +// 3. When adding the pattern (patternMap->add), we set a new boolean to indicate that the added entry had a +// specified skeleton (which sets a new field in the PtnElem in the PatternMap). +UDateTimePatternConflict +DateTimePatternGenerator::addPatternWithSkeleton( + const UnicodeString& pattern, + const UnicodeString* skeletonToUse, + UBool override, + UnicodeString& conflictingPattern, + UErrorCode& status) +{ + if (U_FAILURE(internalErrorCode)) { + status = internalErrorCode; + return UDATPG_NO_CONFLICT; + } + + UnicodeString basePattern; + PtnSkeleton skeleton; + UDateTimePatternConflict conflictingStatus = UDATPG_NO_CONFLICT; + + DateTimeMatcher matcher; + if ( skeletonToUse == nullptr ) { + matcher.set(pattern, fp, skeleton); + matcher.getBasePattern(basePattern); + } else { + matcher.set(*skeletonToUse, fp, skeleton); // no longer trims skeleton fields to max len 3, per #7930 + matcher.getBasePattern(basePattern); // or perhaps instead: basePattern = *skeletonToUse; + } + // We only care about base conflicts - and replacing the pattern associated with a base - if: + // 1. the conflicting previous base pattern did *not* have an explicit skeleton; in that case the previous + // base + pattern combination was derived from either (a) a canonical item, (b) a standard format, or + // (c) a pattern specified programmatically with a previous call to addPattern (which would only happen + // if we are getting here from a subsequent call to addPattern). + // 2. a skeleton is specified for the current pattern, but override=false; in that case we are checking + // availableFormats items from root, which should not override any previous entry with the same base. + UBool entryHadSpecifiedSkeleton; + const UnicodeString *duplicatePattern = patternMap->getPatternFromBasePattern(basePattern, entryHadSpecifiedSkeleton); + if (duplicatePattern != nullptr && (!entryHadSpecifiedSkeleton || (skeletonToUse != nullptr && !override))) { + conflictingStatus = UDATPG_BASE_CONFLICT; + conflictingPattern = *duplicatePattern; + if (!override) { + return conflictingStatus; + } + } + // The only time we get here with override=true and skeletonToUse!=null is when adding availableFormats + // items from CLDR data. In that case, we don't want an item from a parent locale to replace an item with + // same skeleton from the specified locale, so skip the current item if skeletonWasSpecified is true for + // the previously-specified conflicting item. + const PtnSkeleton* entrySpecifiedSkeleton = nullptr; + duplicatePattern = patternMap->getPatternFromSkeleton(skeleton, &entrySpecifiedSkeleton); + if (duplicatePattern != nullptr ) { + conflictingStatus = UDATPG_CONFLICT; + conflictingPattern = *duplicatePattern; + if (!override || (skeletonToUse != nullptr && entrySpecifiedSkeleton != nullptr)) { + return conflictingStatus; + } + } + patternMap->add(basePattern, skeleton, pattern, skeletonToUse != nullptr, status); + if(U_FAILURE(status)) { + return conflictingStatus; + } + + return UDATPG_NO_CONFLICT; +} + + +UDateTimePatternField +DateTimePatternGenerator::getAppendFormatNumber(const char* field) const { + for (int32_t i=0; i0; --i) { + if (uprv_strcmp(CLDR_FIELD_WIDTH[i], hyphenPtr)==0) { + *widthP=(UDateTimePGDisplayWidth)i; + break; + } + } + *hyphenPtr = 0; // now delete width portion of key + } + for (int32_t i=0; igetPatternFromSkeleton(*trial.getSkeletonPtr(), &specifiedSkeleton); + missingFields->setTo(tempInfo); + if (distance==0) { + break; + } + } + } + + // If the best raw match had a specified skeleton and that skeleton was requested by the caller, + // then return it too. This generally happens when the caller needs to pass that skeleton + // through to adjustFieldTypes so the latter can do a better job. + if (bestPattern && specifiedSkeletonPtr) { + *specifiedSkeletonPtr = specifiedSkeleton; + } + return bestPattern; +} + +UnicodeString +DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern, + const PtnSkeleton* specifiedSkeleton, + int32_t flags, + UDateTimePatternMatchOptions options) { + UnicodeString newPattern; + fp->set(pattern); + for (int32_t i=0; i < fp->itemNumber; i++) { + UnicodeString field = fp->items[i]; + if ( fp->isQuoteLiteral(field) ) { + + UnicodeString quoteLiteral; + fp->getQuoteLiteral(quoteLiteral, &i); + newPattern += quoteLiteral; + } + else { + if (fp->isPatternSeparator(field)) { + newPattern+=field; + continue; + } + int32_t canonicalIndex = fp->getCanonicalIndex(field); + if (canonicalIndex < 0) { + newPattern+=field; + continue; // don't adjust + } + const dtTypeElem *row = &dtTypes[canonicalIndex]; + int32_t typeValue = row->field; + + // handle day periods - with #13183, no longer need special handling here, integrated with normal types + + if ((flags & kDTPGFixFractionalSeconds) != 0 && typeValue == UDATPG_SECOND_FIELD) { + field += decimal; + dtMatcher->skeleton.original.appendFieldTo(UDATPG_FRACTIONAL_SECOND_FIELD, field); + } else if (dtMatcher->skeleton.type[typeValue]!=0) { + // Here: + // - "reqField" is the field from the originally requested skeleton after replacement + // of metacharacters 'j', 'C' and 'J', with length "reqFieldLen". + // - "field" is the field from the found pattern. + // + // The adjusted field should consist of characters from the originally requested + // skeleton, except in the case of UDATPG_MONTH_FIELD or + // UDATPG_WEEKDAY_FIELD or UDATPG_YEAR_FIELD, in which case it should consist + // of characters from the found pattern. In some cases of UDATPG_HOUR_FIELD, + // there is adjustment following the "defaultHourFormatChar". There is explanation + // how it is done below. + // + // The length of the adjusted field (adjFieldLen) should match that in the originally + // requested skeleton, except that in the following cases the length of the adjusted field + // should match that in the found pattern (i.e. the length of this pattern field should + // not be adjusted): + // 1. typeValue is UDATPG_HOUR_FIELD/MINUTE/SECOND and the corresponding bit in options is + // not set (ticket #7180). Note, we may want to implement a similar change for other + // numeric fields (MM, dd, etc.) so the default behavior is to get locale preference for + // field length, but options bits can be used to override this. + // 2. There is a specified skeleton for the found pattern and one of the following is true: + // a) The length of the field in the skeleton (skelFieldLen) is equal to reqFieldLen. + // b) The pattern field is numeric and the skeleton field is not, or vice versa. + + char16_t reqFieldChar = dtMatcher->skeleton.original.getFieldChar(typeValue); + int32_t reqFieldLen = dtMatcher->skeleton.original.getFieldLength(typeValue); + if (reqFieldChar == CAP_E && reqFieldLen < 3) + reqFieldLen = 3; // 1-3 for E are equivalent to 3 for c,e + int32_t adjFieldLen = reqFieldLen; + if ( (typeValue==UDATPG_HOUR_FIELD && (options & UDATPG_MATCH_HOUR_FIELD_LENGTH)==0) || + (typeValue==UDATPG_MINUTE_FIELD && (options & UDATPG_MATCH_MINUTE_FIELD_LENGTH)==0) || + (typeValue==UDATPG_SECOND_FIELD && (options & UDATPG_MATCH_SECOND_FIELD_LENGTH)==0) ) { + adjFieldLen = field.length(); + } else if (specifiedSkeleton && reqFieldChar != LOW_C && reqFieldChar != LOW_E) { + // (we skip this section for 'c' and 'e' because unlike the other characters considered in this function, + // they have no minimum field length-- 'E' and 'EE' are equivalent to 'EEE', but 'e' and 'ee' are not + // equivalent to 'eee' -- see the entries for "week day" in + // https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table for more info) + int32_t skelFieldLen = specifiedSkeleton->original.getFieldLength(typeValue); + UBool patFieldIsNumeric = (row->type > 0); + UBool skelFieldIsNumeric = (specifiedSkeleton->type[typeValue] > 0); + if (skelFieldLen == reqFieldLen || (patFieldIsNumeric && !skelFieldIsNumeric) || (skelFieldIsNumeric && !patFieldIsNumeric)) { + // don't adjust the field length in the found pattern + adjFieldLen = field.length(); + } + } + char16_t c = (typeValue!= UDATPG_HOUR_FIELD + && typeValue!= UDATPG_MONTH_FIELD + && typeValue!= UDATPG_WEEKDAY_FIELD + && (typeValue!= UDATPG_YEAR_FIELD || reqFieldChar==CAP_Y)) + ? reqFieldChar + : field.charAt(0); + if (c == CAP_E && adjFieldLen < 3) { + c = LOW_E; + } + if (typeValue == UDATPG_HOUR_FIELD && fDefaultHourFormatChar != 0) { + // The adjustment here is required to match spec (https://www.unicode.org/reports/tr35/tr35-dates.html#dfst-hour). + // It is necessary to match the hour-cycle preferred by the Locale. + // Given that, we need to do the following adjustments: + // 1. When hour-cycle is h11 it should replace 'h' by 'K'. + // 2. When hour-cycle is h23 it should replace 'H' by 'k'. + // 3. When hour-cycle is h24 it should replace 'k' by 'H'. + // 4. When hour-cycle is h12 it should replace 'K' by 'h'. + + if ((flags & kDTPGSkeletonUsesCapJ) != 0 || reqFieldChar == fDefaultHourFormatChar) { + c = fDefaultHourFormatChar; + } else if (reqFieldChar == LOW_H && fDefaultHourFormatChar == CAP_K) { + c = CAP_K; + } else if (reqFieldChar == CAP_H && fDefaultHourFormatChar == LOW_K) { + c = LOW_K; + } else if (reqFieldChar == LOW_K && fDefaultHourFormatChar == CAP_H) { + c = CAP_H; + } else if (reqFieldChar == CAP_K && fDefaultHourFormatChar == LOW_H) { + c = LOW_H; + } + } + + field.remove(); + for (int32_t j=adjFieldLen; j>0; --j) { + field += c; + } + } + newPattern+=field; + } + } + return newPattern; +} + +UnicodeString +DateTimePatternGenerator::getBestAppending(int32_t missingFields, int32_t flags, UErrorCode &status, UDateTimePatternMatchOptions options) { + if (U_FAILURE(status)) { + return UnicodeString(); + } + UnicodeString resultPattern, tempPattern; + const UnicodeString* tempPatternPtr; + int32_t lastMissingFieldMask=0; + if (missingFields!=0) { + resultPattern=UnicodeString(); + const PtnSkeleton* specifiedSkeleton=nullptr; + tempPatternPtr = getBestRaw(*dtMatcher, missingFields, distanceInfo, status, &specifiedSkeleton); + if (U_FAILURE(status)) { + return UnicodeString(); + } + tempPattern = *tempPatternPtr; + resultPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options); + if ( distanceInfo->missingFieldMask==0 ) { + return resultPattern; + } + while (distanceInfo->missingFieldMask!=0) { // precondition: EVERY single field must work! + if ( lastMissingFieldMask == distanceInfo->missingFieldMask ) { + break; // cannot find the proper missing field + } + if (((distanceInfo->missingFieldMask & UDATPG_SECOND_AND_FRACTIONAL_MASK)==UDATPG_FRACTIONAL_MASK) && + ((missingFields & UDATPG_SECOND_AND_FRACTIONAL_MASK) == UDATPG_SECOND_AND_FRACTIONAL_MASK)) { + resultPattern = adjustFieldTypes(resultPattern, specifiedSkeleton, flags | kDTPGFixFractionalSeconds, options); + distanceInfo->missingFieldMask &= ~UDATPG_FRACTIONAL_MASK; + continue; + } + int32_t startingMask = distanceInfo->missingFieldMask; + tempPatternPtr = getBestRaw(*dtMatcher, distanceInfo->missingFieldMask, distanceInfo, status, &specifiedSkeleton); + if (U_FAILURE(status)) { + return UnicodeString(); + } + tempPattern = *tempPatternPtr; + tempPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options); + int32_t foundMask=startingMask& ~distanceInfo->missingFieldMask; + int32_t topField=getTopBitNumber(foundMask); + + if (appendItemFormats[topField].length() != 0) { + UnicodeString appendName; + getAppendName((UDateTimePatternField)topField, appendName); + const UnicodeString *values[3] = { + &resultPattern, + &tempPattern, + &appendName + }; + SimpleFormatter(appendItemFormats[topField], 2, 3, status). + formatAndReplace(values, 3, resultPattern, nullptr, 0, status); + } + lastMissingFieldMask = distanceInfo->missingFieldMask; + } + } + return resultPattern; +} + +int32_t +DateTimePatternGenerator::getTopBitNumber(int32_t foundMask) const { + if ( foundMask==0 ) { + return 0; + } + int32_t i=0; + while (foundMask!=0) { + foundMask >>=1; + ++i; + } + if (i-1 >UDATPG_ZONE_FIELD) { + return UDATPG_ZONE_FIELD; + } + else + return i-1; +} + +void +DateTimePatternGenerator::setAvailableFormat(const UnicodeString &key, UErrorCode& err) +{ + fAvailableFormatKeyHash->puti(key, 1, err); +} + +UBool +DateTimePatternGenerator::isAvailableFormatSet(const UnicodeString &key) const { + return (UBool)(fAvailableFormatKeyHash->geti(key) == 1); +} + +void +DateTimePatternGenerator::copyHashtable(Hashtable *other, UErrorCode &status) { + if (other == nullptr || U_FAILURE(status)) { + return; + } + if (fAvailableFormatKeyHash != nullptr) { + delete fAvailableFormatKeyHash; + fAvailableFormatKeyHash = nullptr; + } + initHashtable(status); + if(U_FAILURE(status)){ + return; + } + int32_t pos = UHASH_FIRST; + const UHashElement* elem = nullptr; + // walk through the hash table and create a deep clone + while((elem = other->nextElement(pos))!= nullptr){ + const UHashTok otherKeyTok = elem->key; + UnicodeString* otherKey = (UnicodeString*)otherKeyTok.pointer; + fAvailableFormatKeyHash->puti(*otherKey, 1, status); + if(U_FAILURE(status)){ + return; + } + } +} + +StringEnumeration* +DateTimePatternGenerator::getSkeletons(UErrorCode& status) const { + if (U_FAILURE(status)) { + return nullptr; + } + if (U_FAILURE(internalErrorCode)) { + status = internalErrorCode; + return nullptr; + } + LocalPointer skeletonEnumerator( + new DTSkeletonEnumeration(*patternMap, DT_SKELETON, status), status); + + return U_SUCCESS(status) ? skeletonEnumerator.orphan() : nullptr; +} + +const UnicodeString& +DateTimePatternGenerator::getPatternForSkeleton(const UnicodeString& skeleton) const { + PtnElem *curElem; + + if (skeleton.length() ==0) { + return emptyString; + } + curElem = patternMap->getHeader(skeleton.charAt(0)); + while ( curElem != nullptr ) { + if ( curElem->skeleton->getSkeleton()==skeleton ) { + return curElem->pattern; + } + curElem = curElem->next.getAlias(); + } + return emptyString; +} + +StringEnumeration* +DateTimePatternGenerator::getBaseSkeletons(UErrorCode& status) const { + if (U_FAILURE(status)) { + return nullptr; + } + if (U_FAILURE(internalErrorCode)) { + status = internalErrorCode; + return nullptr; + } + LocalPointer baseSkeletonEnumerator( + new DTSkeletonEnumeration(*patternMap, DT_BASESKELETON, status), status); + + return U_SUCCESS(status) ? baseSkeletonEnumerator.orphan() : nullptr; +} + +StringEnumeration* +DateTimePatternGenerator::getRedundants(UErrorCode& status) { + if (U_FAILURE(status)) { return nullptr; } + if (U_FAILURE(internalErrorCode)) { + status = internalErrorCode; + return nullptr; + } + LocalPointer output(new DTRedundantEnumeration(), status); + if (U_FAILURE(status)) { return nullptr; } + const UnicodeString *pattern; + PatternMapIterator it(status); + if (U_FAILURE(status)) { return nullptr; } + + for (it.set(*patternMap); it.hasNext(); ) { + DateTimeMatcher current = it.next(); + pattern = patternMap->getPatternFromSkeleton(*(it.getSkeleton())); + if ( isCanonicalItem(*pattern) ) { + continue; + } + if ( skipMatcher == nullptr ) { + skipMatcher = new DateTimeMatcher(current); + if (skipMatcher == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + } + else { + *skipMatcher = current; + } + UnicodeString trial = getBestPattern(current.getPattern(), status); + if (U_FAILURE(status)) { return nullptr; } + if (trial == *pattern) { + ((DTRedundantEnumeration *)output.getAlias())->add(*pattern, status); + if (U_FAILURE(status)) { return nullptr; } + } + if (current.equals(skipMatcher)) { + continue; + } + } + return output.orphan(); +} + +UBool +DateTimePatternGenerator::isCanonicalItem(const UnicodeString& item) const { + if ( item.length() != 1 ) { + return false; + } + for (int32_t i=0; iisDupAllowed = other.isDupAllowed; + for (int32_t bootIndex = 0; bootIndex < MAX_PATTERN_ENTRIES; ++bootIndex) { + PtnElem *curElem, *otherElem, *prevElem=nullptr; + otherElem = other.boot[bootIndex]; + while (otherElem != nullptr) { + LocalPointer newElem(new PtnElem(otherElem->basePattern, otherElem->pattern), status); + if (U_FAILURE(status)) { + return; // out of memory + } + newElem->skeleton.adoptInsteadAndCheckErrorCode(new PtnSkeleton(*(otherElem->skeleton)), status); + if (U_FAILURE(status)) { + return; // out of memory + } + newElem->skeletonWasSpecified = otherElem->skeletonWasSpecified; + + // Release ownership from the LocalPointer of the PtnElem object. + // The PtnElem will now be owned by either the boot (for the first entry in the linked-list) + // or owned by the previous PtnElem object in the linked-list. + curElem = newElem.orphan(); + + if (this->boot[bootIndex] == nullptr) { + this->boot[bootIndex] = curElem; + } else { + if (prevElem != nullptr) { + prevElem->next.adoptInstead(curElem); + } else { + UPRV_UNREACHABLE_EXIT; + } + } + prevElem = curElem; + otherElem = otherElem->next.getAlias(); + } + + } +} + +PtnElem* +PatternMap::getHeader(char16_t baseChar) const { + PtnElem* curElem; + + if ( (baseChar >= CAP_A) && (baseChar <= CAP_Z) ) { + curElem = boot[baseChar-CAP_A]; + } + else { + if ( (baseChar >=LOW_A) && (baseChar <= LOW_Z) ) { + curElem = boot[26+baseChar-LOW_A]; + } + else { + return nullptr; + } + } + return curElem; +} + +PatternMap::~PatternMap() { + for (int32_t i=0; i < MAX_PATTERN_ENTRIES; ++i ) { + if (boot[i] != nullptr ) { + delete boot[i]; + boot[i] = nullptr; + } + } +} // PatternMap destructor + +void +PatternMap::add(const UnicodeString& basePattern, + const PtnSkeleton& skeleton, + const UnicodeString& value,// mapped pattern value + UBool skeletonWasSpecified, + UErrorCode &status) { + char16_t baseChar = basePattern.charAt(0); + PtnElem *curElem, *baseElem; + status = U_ZERO_ERROR; + + // the baseChar must be A-Z or a-z + if ((baseChar >= CAP_A) && (baseChar <= CAP_Z)) { + baseElem = boot[baseChar-CAP_A]; + } + else { + if ((baseChar >=LOW_A) && (baseChar <= LOW_Z)) { + baseElem = boot[26+baseChar-LOW_A]; + } + else { + status = U_ILLEGAL_CHARACTER; + return; + } + } + + if (baseElem == nullptr) { + LocalPointer newElem(new PtnElem(basePattern, value), status); + if (U_FAILURE(status)) { + return; // out of memory + } + newElem->skeleton.adoptInsteadAndCheckErrorCode(new PtnSkeleton(skeleton), status); + if (U_FAILURE(status)) { + return; // out of memory + } + newElem->skeletonWasSpecified = skeletonWasSpecified; + if (baseChar >= LOW_A) { + boot[26 + (baseChar - LOW_A)] = newElem.orphan(); // the boot array now owns the PtnElem. + } + else { + boot[baseChar - CAP_A] = newElem.orphan(); // the boot array now owns the PtnElem. + } + } + if ( baseElem != nullptr ) { + curElem = getDuplicateElem(basePattern, skeleton, baseElem); + + if (curElem == nullptr) { + // add new element to the list. + curElem = baseElem; + while( curElem -> next != nullptr ) + { + curElem = curElem->next.getAlias(); + } + + LocalPointer newElem(new PtnElem(basePattern, value), status); + if (U_FAILURE(status)) { + return; // out of memory + } + newElem->skeleton.adoptInsteadAndCheckErrorCode(new PtnSkeleton(skeleton), status); + if (U_FAILURE(status)) { + return; // out of memory + } + newElem->skeletonWasSpecified = skeletonWasSpecified; + curElem->next.adoptInstead(newElem.orphan()); + curElem = curElem->next.getAlias(); + } + else { + // Pattern exists in the list already. + if ( !isDupAllowed ) { + return; + } + // Overwrite the value. + curElem->pattern = value; + // It was a bug that we were not doing the following previously, + // though that bug hid other problems by making things partly work. + curElem->skeletonWasSpecified = skeletonWasSpecified; + } + } +} // PatternMap::add + +// Find the pattern from the given basePattern string. +const UnicodeString * +PatternMap::getPatternFromBasePattern(const UnicodeString& basePattern, UBool& skeletonWasSpecified) const { // key to search for + PtnElem *curElem; + + if ((curElem=getHeader(basePattern.charAt(0)))==nullptr) { + return nullptr; // no match + } + + do { + if ( basePattern.compare(curElem->basePattern)==0 ) { + skeletonWasSpecified = curElem->skeletonWasSpecified; + return &(curElem->pattern); + } + curElem = curElem->next.getAlias(); + } while (curElem != nullptr); + + return nullptr; +} // PatternMap::getFromBasePattern + + +// Find the pattern from the given skeleton. +// At least when this is called from getBestRaw & addPattern (in which case specifiedSkeletonPtr is non-nullptr), +// the comparison should be based on skeleton.original (which is unique and tied to the distance measurement in bestRaw) +// and not skeleton.baseOriginal (which is not unique); otherwise we may pick a different skeleton than the one with the +// optimum distance value in getBestRaw. When this is called from public getRedundants (specifiedSkeletonPtr is nullptr), +// for now it will continue to compare based on baseOriginal so as not to change the behavior unnecessarily. +const UnicodeString * +PatternMap::getPatternFromSkeleton(const PtnSkeleton& skeleton, const PtnSkeleton** specifiedSkeletonPtr) const { // key to search for + PtnElem *curElem; + + if (specifiedSkeletonPtr) { + *specifiedSkeletonPtr = nullptr; + } + + // find boot entry + char16_t baseChar = skeleton.getFirstChar(); + if ((curElem=getHeader(baseChar))==nullptr) { + return nullptr; // no match + } + + do { + UBool equal; + if (specifiedSkeletonPtr != nullptr) { // called from DateTimePatternGenerator::getBestRaw or addPattern, use original + equal = curElem->skeleton->original == skeleton.original; + } else { // called from DateTimePatternGenerator::getRedundants, use baseOriginal + equal = curElem->skeleton->baseOriginal == skeleton.baseOriginal; + } + if (equal) { + if (specifiedSkeletonPtr && curElem->skeletonWasSpecified) { + *specifiedSkeletonPtr = curElem->skeleton.getAlias(); + } + return &(curElem->pattern); + } + curElem = curElem->next.getAlias(); + } while (curElem != nullptr); + + return nullptr; +} + +UBool +PatternMap::equals(const PatternMap& other) const { + if ( this==&other ) { + return true; + } + for (int32_t bootIndex = 0; bootIndex < MAX_PATTERN_ENTRIES; ++bootIndex) { + if (boot[bootIndex] == other.boot[bootIndex]) { + continue; + } + if ((boot[bootIndex] == nullptr) || (other.boot[bootIndex] == nullptr)) { + return false; + } + PtnElem *otherElem = other.boot[bootIndex]; + PtnElem *myElem = boot[bootIndex]; + while ((otherElem != nullptr) || (myElem != nullptr)) { + if ( myElem == otherElem ) { + break; + } + if ((otherElem == nullptr) || (myElem == nullptr)) { + return false; + } + if ( (myElem->basePattern != otherElem->basePattern) || + (myElem->pattern != otherElem->pattern) ) { + return false; + } + if ((myElem->skeleton.getAlias() != otherElem->skeleton.getAlias()) && + !myElem->skeleton->equals(*(otherElem->skeleton))) { + return false; + } + myElem = myElem->next.getAlias(); + otherElem = otherElem->next.getAlias(); + } + } + return true; +} + +// find any key existing in the mapping table already. +// return true if there is an existing key, otherwise return false. +PtnElem* +PatternMap::getDuplicateElem( + const UnicodeString &basePattern, + const PtnSkeleton &skeleton, + PtnElem *baseElem) { + PtnElem *curElem; + + if ( baseElem == nullptr ) { + return nullptr; + } + else { + curElem = baseElem; + } + do { + if ( basePattern.compare(curElem->basePattern)==0 ) { + UBool isEqual = true; + for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) { + if (curElem->skeleton->type[i] != skeleton.type[i] ) { + isEqual = false; + break; + } + } + if (isEqual) { + return curElem; + } + } + curElem = curElem->next.getAlias(); + } while( curElem != nullptr ); + + // end of the list + return nullptr; + +} // PatternMap::getDuplicateElem + +DateTimeMatcher::DateTimeMatcher() { +} + +DateTimeMatcher::~DateTimeMatcher() {} + +DateTimeMatcher::DateTimeMatcher(const DateTimeMatcher& other) { + copyFrom(other.skeleton); +} + +DateTimeMatcher& DateTimeMatcher::operator=(const DateTimeMatcher& other) { + copyFrom(other.skeleton); + return *this; +} + + +void +DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp) { + PtnSkeleton localSkeleton; + return set(pattern, fp, localSkeleton); +} + +void +DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp, PtnSkeleton& skeletonResult) { + int32_t i; + for (i=0; iset(pattern); + for (i=0; i < fp->itemNumber; i++) { + const UnicodeString& value = fp->items[i]; + // don't skip 'a' anymore, dayPeriod handled specially below + + if ( fp->isQuoteLiteral(value) ) { + UnicodeString quoteLiteral; + fp->getQuoteLiteral(quoteLiteral, &i); + continue; + } + int32_t canonicalIndex = fp->getCanonicalIndex(value); + if (canonicalIndex < 0) { + continue; + } + const dtTypeElem *row = &dtTypes[canonicalIndex]; + int32_t field = row->field; + skeletonResult.original.populate(field, value); + char16_t repeatChar = row->patternChar; + int32_t repeatCount = row->minLen; + skeletonResult.baseOriginal.populate(field, repeatChar, repeatCount); + int16_t subField = row->type; + if (row->type > 0) { + U_ASSERT(value.length() < INT16_MAX); + subField += static_cast(value.length()); + } + skeletonResult.type[field] = subField; + } + + // #20739, we have a skeleton with minutes and milliseconds, but no seconds + // + // Theoretically we would need to check and fix all fields with "gaps": + // for example year-day (no month), month-hour (no day), and so on, All the possible field combinations. + // Plus some smartness: year + hour => should we add month, or add day-of-year? + // What about month + day-of-week, or month + am/pm indicator. + // I think beyond a certain point we should not try to fix bad developer input and try guessing what they mean. + // Garbage in, garbage out. + if (!skeletonResult.original.isFieldEmpty(UDATPG_MINUTE_FIELD) + && !skeletonResult.original.isFieldEmpty(UDATPG_FRACTIONAL_SECOND_FIELD) + && skeletonResult.original.isFieldEmpty(UDATPG_SECOND_FIELD)) { + // Force the use of seconds + for (i = 0; dtTypes[i].patternChar != 0; i++) { + if (dtTypes[i].field == UDATPG_SECOND_FIELD) { + // first entry for UDATPG_SECOND_FIELD + skeletonResult.original.populate(UDATPG_SECOND_FIELD, dtTypes[i].patternChar, dtTypes[i].minLen); + skeletonResult.baseOriginal.populate(UDATPG_SECOND_FIELD, dtTypes[i].patternChar, dtTypes[i].minLen); + // We add value.length, same as above, when type is first initialized. + // The value we want to "fake" here is "s", and 1 means "s".length() + int16_t subField = dtTypes[i].type; + skeletonResult.type[UDATPG_SECOND_FIELD] = (subField > 0) ? subField + 1 : subField; + break; + } + } + } + + // #13183, handle special behavior for day period characters (a, b, B) + if (!skeletonResult.original.isFieldEmpty(UDATPG_HOUR_FIELD)) { + if (skeletonResult.original.getFieldChar(UDATPG_HOUR_FIELD)==LOW_H || skeletonResult.original.getFieldChar(UDATPG_HOUR_FIELD)==CAP_K) { + // We have a skeleton with 12-hour-cycle format + if (skeletonResult.original.isFieldEmpty(UDATPG_DAYPERIOD_FIELD)) { + // But we do not have a day period in the skeleton; add the default DAYPERIOD (currently "a") + for (i = 0; dtTypes[i].patternChar != 0; i++) { + if ( dtTypes[i].field == UDATPG_DAYPERIOD_FIELD ) { + // first entry for UDATPG_DAYPERIOD_FIELD + skeletonResult.original.populate(UDATPG_DAYPERIOD_FIELD, dtTypes[i].patternChar, dtTypes[i].minLen); + skeletonResult.baseOriginal.populate(UDATPG_DAYPERIOD_FIELD, dtTypes[i].patternChar, dtTypes[i].minLen); + skeletonResult.type[UDATPG_DAYPERIOD_FIELD] = dtTypes[i].type; + skeletonResult.addedDefaultDayPeriod = true; + break; + } + } + } + } else { + // Skeleton has 24-hour-cycle hour format and has dayPeriod, delete dayPeriod (i.e. ignore it) + skeletonResult.original.clearField(UDATPG_DAYPERIOD_FIELD); + skeletonResult.baseOriginal.clearField(UDATPG_DAYPERIOD_FIELD); + skeletonResult.type[UDATPG_DAYPERIOD_FIELD] = NONE; + } + } + copyFrom(skeletonResult); +} + +void +DateTimeMatcher::getBasePattern(UnicodeString &result ) { + result.remove(); // Reset the result first. + skeleton.baseOriginal.appendTo(result); +} + +UnicodeString +DateTimeMatcher::getPattern() { + UnicodeString result; + return skeleton.original.appendTo(result); +} + +int32_t +DateTimeMatcher::getDistance(const DateTimeMatcher& other, int32_t includeMask, DistanceInfo& distanceInfo) const { + int32_t result = 0; + distanceInfo.clear(); + for (int32_t i=0; iskeleton.original; +} + +int32_t +DateTimeMatcher::getFieldMask() const { + int32_t result = 0; + + for (int32_t i=0; i= pattern.length()) { + return DONE; + } + // check the current char is between A-Z or a-z + do { + char16_t c=pattern.charAt(curLoc); + if ( (c>=CAP_A && c<=CAP_Z) || (c>=LOW_A && c<=LOW_Z) ) { + curLoc++; + } + else { + startPos = curLoc; + *len=1; + return ADD_TOKEN; + } + + if ( pattern.charAt(curLoc)!= pattern.charAt(startPos) ) { + break; // not the same token + } + } while(curLoc <= pattern.length()); + *len = curLoc-startPos; + return ADD_TOKEN; +} + +void +FormatParser::set(const UnicodeString& pattern) { + int32_t startPos = 0; + TokenStatus result = START; + int32_t len = 0; + itemNumber = 0; + + do { + result = setTokens( pattern, startPos, &len ); + if ( result == ADD_TOKEN ) + { + items[itemNumber++] = UnicodeString(pattern, startPos, len ); + startPos += len; + } + else { + break; + } + } while (result==ADD_TOKEN && itemNumber < MAX_DT_TOKEN); +} + +int32_t +FormatParser::getCanonicalIndex(const UnicodeString& s, UBool strict) { + int32_t len = s.length(); + if (len == 0) { + return -1; + } + char16_t ch = s.charAt(0); + + // Verify that all are the same character. + for (int32_t l = 1; l < len; l++) { + if (ch != s.charAt(l)) { + return -1; + } + } + int32_t i = 0; + int32_t bestRow = -1; + while (dtTypes[i].patternChar != 0x0000) { + if ( dtTypes[i].patternChar != ch ) { + ++i; + continue; + } + bestRow = i; + if (dtTypes[i].patternChar != dtTypes[i+1].patternChar) { + return i; + } + if (dtTypes[i+1].minLen <= len) { + ++i; + continue; + } + return i; + } + return strict ? -1 : bestRow; +} + +UBool +FormatParser::isQuoteLiteral(const UnicodeString& s) { + return (UBool)(s.charAt(0) == SINGLE_QUOTE); +} + +// This function assumes the current itemIndex points to the quote literal. +// Please call isQuoteLiteral prior to this function. +void +FormatParser::getQuoteLiteral(UnicodeString& quote, int32_t *itemIndex) { + int32_t i = *itemIndex; + + quote.remove(); + if (items[i].charAt(0)==SINGLE_QUOTE) { + quote += items[i]; + ++i; + } + while ( i < itemNumber ) { + if ( items[i].charAt(0)==SINGLE_QUOTE ) { + if ( (i+1patternMap=&newPatternMap; +} + +PtnSkeleton* +PatternMapIterator::getSkeleton() const { + if ( nodePtr == nullptr ) { + return nullptr; + } + else { + return nodePtr->skeleton.getAlias(); + } +} + +UBool +PatternMapIterator::hasNext() const { + int32_t headIndex = bootIndex; + PtnElem *curPtr = nodePtr; + + if (patternMap==nullptr) { + return false; + } + while ( headIndex < MAX_PATTERN_ENTRIES ) { + if ( curPtr != nullptr ) { + if ( curPtr->next != nullptr ) { + return true; + } + else { + headIndex++; + curPtr=nullptr; + continue; + } + } + else { + if ( patternMap->boot[headIndex] != nullptr ) { + return true; + } + else { + headIndex++; + continue; + } + } + } + return false; +} + +DateTimeMatcher& +PatternMapIterator::next() { + while ( bootIndex < MAX_PATTERN_ENTRIES ) { + if ( nodePtr != nullptr ) { + if ( nodePtr->next != nullptr ) { + nodePtr = nodePtr->next.getAlias(); + break; + } + else { + bootIndex++; + nodePtr=nullptr; + continue; + } + } + else { + if ( patternMap->boot[bootIndex] != nullptr ) { + nodePtr = patternMap->boot[bootIndex]; + break; + } + else { + bootIndex++; + continue; + } + } + } + if (nodePtr!=nullptr) { + matcher->copyFrom(*nodePtr->skeleton); + } + else { + matcher->copyFrom(); + } + return *matcher; +} + + +SkeletonFields::SkeletonFields() { + // Set initial values to zero + clear(); +} + +void SkeletonFields::clear() { + uprv_memset(chars, 0, sizeof(chars)); + uprv_memset(lengths, 0, sizeof(lengths)); +} + +void SkeletonFields::copyFrom(const SkeletonFields& other) { + uprv_memcpy(chars, other.chars, sizeof(chars)); + uprv_memcpy(lengths, other.lengths, sizeof(lengths)); +} + +void SkeletonFields::clearField(int32_t field) { + chars[field] = 0; + lengths[field] = 0; +} + +char16_t SkeletonFields::getFieldChar(int32_t field) const { + return chars[field]; +} + +int32_t SkeletonFields::getFieldLength(int32_t field) const { + return lengths[field]; +} + +void SkeletonFields::populate(int32_t field, const UnicodeString& value) { + populate(field, value.charAt(0), value.length()); +} + +void SkeletonFields::populate(int32_t field, char16_t ch, int32_t length) { + chars[field] = (int8_t) ch; + lengths[field] = (int8_t) length; +} + +UBool SkeletonFields::isFieldEmpty(int32_t field) const { + return lengths[field] == 0; +} + +UnicodeString& SkeletonFields::appendTo(UnicodeString& string) const { + for (int32_t i = 0; i < UDATPG_FIELD_COUNT; ++i) { + appendFieldTo(i, string); + } + return string; +} + +UnicodeString& SkeletonFields::appendFieldTo(int32_t field, UnicodeString& string) const { + char16_t ch(chars[field]); + int32_t length = (int32_t) lengths[field]; + + for (int32_t i=0; i= 0) { + // for backward compatibility: if DateTimeMatcher.set added a single 'a' that + // was not in the provided skeleton, remove it here before returning skeleton. + result.remove(pos, 1); + } + return result; +} + +UnicodeString +PtnSkeleton::getBaseSkeleton() const { + UnicodeString result; + result = baseOriginal.appendTo(result); + int32_t pos; + if (addedDefaultDayPeriod && (pos = result.indexOf(LOW_A)) >= 0) { + // for backward compatibility: if DateTimeMatcher.set added a single 'a' that + // was not in the provided skeleton, remove it here before returning skeleton. + result.remove(pos, 1); + } + return result; +} + +char16_t +PtnSkeleton::getFirstChar() const { + return baseOriginal.getFirstChar(); +} + +PtnSkeleton::~PtnSkeleton() { +} + +PtnElem::PtnElem(const UnicodeString &basePat, const UnicodeString &pat) : + basePattern(basePat), skeleton(nullptr), pattern(pat), next(nullptr) +{ +} + +PtnElem::~PtnElem() { +} + +DTSkeletonEnumeration::DTSkeletonEnumeration(PatternMap& patternMap, dtStrEnum type, UErrorCode& status) : fSkeletons(nullptr) { + PtnElem *curElem; + PtnSkeleton *curSkeleton; + UnicodeString s; + int32_t bootIndex; + + pos=0; + fSkeletons.adoptInsteadAndCheckErrorCode(new UVector(status), status); + if (U_FAILURE(status)) { + return; + } + + for (bootIndex=0; bootIndexbasePattern; + break; + case DT_PATTERN: + s=curElem->pattern; + break; + case DT_SKELETON: + curSkeleton=curElem->skeleton.getAlias(); + s=curSkeleton->getSkeleton(); + break; + } + if ( !isCanonicalItem(s) ) { + LocalPointer newElem(s.clone(), status); + if (U_FAILURE(status)) { + return; + } + fSkeletons->addElement(newElem.getAlias(), status); + if (U_FAILURE(status)) { + fSkeletons.adoptInstead(nullptr); + return; + } + newElem.orphan(); // fSkeletons vector now owns the UnicodeString (although it + // does not use a deleter function to manage the ownership). + } + curElem = curElem->next.getAlias(); + } + } + if ((bootIndex==MAX_PATTERN_ENTRIES) && (curElem!=nullptr) ) { + status = U_BUFFER_OVERFLOW_ERROR; + } +} + +const UnicodeString* +DTSkeletonEnumeration::snext(UErrorCode& status) { + if (U_SUCCESS(status) && fSkeletons.isValid() && pos < fSkeletons->size()) { + return (const UnicodeString*)fSkeletons->elementAt(pos++); + } + return nullptr; +} + +void +DTSkeletonEnumeration::reset(UErrorCode& /*status*/) { + pos=0; +} + +int32_t +DTSkeletonEnumeration::count(UErrorCode& /*status*/) const { + return (fSkeletons.isNull()) ? 0 : fSkeletons->size(); +} + +UBool +DTSkeletonEnumeration::isCanonicalItem(const UnicodeString& item) { + if ( item.length() != 1 ) { + return false; + } + for (int32_t i=0; isize(); ++i) { + if ((s = (UnicodeString *)fSkeletons->elementAt(i)) != nullptr) { + delete s; + } + } + } +} + +DTRedundantEnumeration::DTRedundantEnumeration() : pos(0), fPatterns(nullptr) { +} + +void +DTRedundantEnumeration::add(const UnicodeString& pattern, UErrorCode& status) { + if (U_FAILURE(status)) { return; } + if (fPatterns.isNull()) { + fPatterns.adoptInsteadAndCheckErrorCode(new UVector(status), status); + if (U_FAILURE(status)) { + return; + } + } + LocalPointer newElem(new UnicodeString(pattern), status); + if (U_FAILURE(status)) { + return; + } + fPatterns->addElement(newElem.getAlias(), status); + if (U_FAILURE(status)) { + fPatterns.adoptInstead(nullptr); + return; + } + newElem.orphan(); // fPatterns now owns the string, although a UVector + // deleter function is not used to manage that ownership. +} + +const UnicodeString* +DTRedundantEnumeration::snext(UErrorCode& status) { + if (U_SUCCESS(status) && fPatterns.isValid() && pos < fPatterns->size()) { + return (const UnicodeString*)fPatterns->elementAt(pos++); + } + return nullptr; +} + +void +DTRedundantEnumeration::reset(UErrorCode& /*status*/) { + pos=0; +} + +int32_t +DTRedundantEnumeration::count(UErrorCode& /*status*/) const { + return (fPatterns.isNull()) ? 0 : fPatterns->size(); +} + +UBool +DTRedundantEnumeration::isCanonicalItem(const UnicodeString& item) const { + if ( item.length() != 1 ) { + return false; + } + for (int32_t i=0; isize(); ++i) { + if ((s = (UnicodeString *)fPatterns->elementAt(i)) != nullptr) { + delete s; + } + } + } +} + +U_NAMESPACE_END + + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/dtptngen_impl.h b/deps/icu-small/source/i18n/dtptngen_impl.h index 5caae11654bb19..22b89b3b622236 100644 --- a/deps/icu-small/source/i18n/dtptngen_impl.h +++ b/deps/icu-small/source/i18n/dtptngen_impl.h @@ -1,310 +1,310 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2007-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File DTPTNGEN.H -* -******************************************************************************* -*/ - -#ifndef __DTPTNGEN_IMPL_H__ -#define __DTPTNGEN_IMPL_H__ - -#include "unicode/udatpg.h" - -#include "unicode/strenum.h" -#include "unicode/unistr.h" -#include "uvector.h" - -// TODO(claireho): Split off Builder class. -// TODO(claireho): If splitting off Builder class: As subclass or independent? - -#define MAX_PATTERN_ENTRIES 52 -#define MAX_CLDR_FIELD_LEN 60 -#define MAX_DT_TOKEN 50 -#define MAX_RESOURCE_FIELD 12 -#define MAX_AVAILABLE_FORMATS 12 -#define NONE 0 -#define EXTRA_FIELD 0x10000 -#define MISSING_FIELD 0x1000 -#define MAX_STRING_ENUMERATION 200 -#define SINGLE_QUOTE ((UChar)0x0027) -#define FORWARDSLASH ((UChar)0x002F) -#define BACKSLASH ((UChar)0x005C) -#define SPACE ((UChar)0x0020) -#define QUOTATION_MARK ((UChar)0x0022) -#define ASTERISK ((UChar)0x002A) -#define PLUSSITN ((UChar)0x002B) -#define COMMA ((UChar)0x002C) -#define HYPHEN ((UChar)0x002D) -#define DOT ((UChar)0x002E) -#define COLON ((UChar)0x003A) -#define CAP_A ((UChar)0x0041) -#define CAP_B ((UChar)0x0042) -#define CAP_C ((UChar)0x0043) -#define CAP_D ((UChar)0x0044) -#define CAP_E ((UChar)0x0045) -#define CAP_F ((UChar)0x0046) -#define CAP_G ((UChar)0x0047) -#define CAP_H ((UChar)0x0048) -#define CAP_J ((UChar)0x004A) -#define CAP_K ((UChar)0x004B) -#define CAP_L ((UChar)0x004C) -#define CAP_M ((UChar)0x004D) -#define CAP_O ((UChar)0x004F) -#define CAP_Q ((UChar)0x0051) -#define CAP_S ((UChar)0x0053) -#define CAP_T ((UChar)0x0054) -#define CAP_U ((UChar)0x0055) -#define CAP_V ((UChar)0x0056) -#define CAP_W ((UChar)0x0057) -#define CAP_X ((UChar)0x0058) -#define CAP_Y ((UChar)0x0059) -#define CAP_Z ((UChar)0x005A) -#define LOWLINE ((UChar)0x005F) -#define LOW_A ((UChar)0x0061) -#define LOW_B ((UChar)0x0062) -#define LOW_C ((UChar)0x0063) -#define LOW_D ((UChar)0x0064) -#define LOW_E ((UChar)0x0065) -#define LOW_F ((UChar)0x0066) -#define LOW_G ((UChar)0x0067) -#define LOW_H ((UChar)0x0068) -#define LOW_I ((UChar)0x0069) -#define LOW_J ((UChar)0x006A) -#define LOW_K ((UChar)0x006B) -#define LOW_L ((UChar)0x006C) -#define LOW_M ((UChar)0x006D) -#define LOW_N ((UChar)0x006E) -#define LOW_O ((UChar)0x006F) -#define LOW_P ((UChar)0x0070) -#define LOW_Q ((UChar)0x0071) -#define LOW_R ((UChar)0x0072) -#define LOW_S ((UChar)0x0073) -#define LOW_T ((UChar)0x0074) -#define LOW_U ((UChar)0x0075) -#define LOW_V ((UChar)0x0076) -#define LOW_W ((UChar)0x0077) -#define LOW_X ((UChar)0x0078) -#define LOW_Y ((UChar)0x0079) -#define LOW_Z ((UChar)0x007A) -#define DT_NARROW -0x101 -#define DT_SHORTER -0x102 -#define DT_SHORT -0x103 -#define DT_LONG -0x104 -#define DT_NUMERIC 0x100 -#define DT_DELTA 0x10 - -U_NAMESPACE_BEGIN - -const int32_t UDATPG_FRACTIONAL_MASK = 1< skeleton; - UnicodeString pattern; - UBool skeletonWasSpecified; // if specified in availableFormats, not derived - LocalPointer next; - - PtnElem(const UnicodeString &basePattern, const UnicodeString &pattern); - virtual ~PtnElem(); -}; - -class FormatParser : public UMemory { -public: - UnicodeString items[MAX_DT_TOKEN]; - int32_t itemNumber; - - FormatParser(); - virtual ~FormatParser(); - void set(const UnicodeString& patternString); - void getQuoteLiteral(UnicodeString& quote, int32_t *itemIndex); - UBool isPatternSeparator(const UnicodeString& field) const; - static UBool isQuoteLiteral(const UnicodeString& s); - static int32_t getCanonicalIndex(const UnicodeString& s) { return getCanonicalIndex(s, true); } - static int32_t getCanonicalIndex(const UnicodeString& s, UBool strict); - -private: - typedef enum TokenStatus { - START, - ADD_TOKEN, - SYNTAX_ERROR, - DONE - } TokenStatus; - - TokenStatus status; - virtual TokenStatus setTokens(const UnicodeString& pattern, int32_t startPos, int32_t *len); -}; - -class DistanceInfo : public UMemory { -public: - int32_t missingFieldMask; - int32_t extraFieldMask; - - DistanceInfo() {} - virtual ~DistanceInfo(); - void clear() { missingFieldMask = extraFieldMask = 0; } - void setTo(const DistanceInfo& other); - void addMissing(int32_t field) { missingFieldMask |= (1< matcher; - PatternMap *patternMap; -}; - -class DTSkeletonEnumeration : public StringEnumeration { -public: - DTSkeletonEnumeration(PatternMap& patternMap, dtStrEnum type, UErrorCode& status); - virtual ~DTSkeletonEnumeration(); - static UClassID U_EXPORT2 getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; - virtual const UnicodeString* snext(UErrorCode& status) override; - virtual void reset(UErrorCode& status) override; - virtual int32_t count(UErrorCode& status) const override; -private: - int32_t pos; - UBool isCanonicalItem(const UnicodeString& item); - LocalPointer fSkeletons; -}; - -class DTRedundantEnumeration : public StringEnumeration { -public: - DTRedundantEnumeration(); - virtual ~DTRedundantEnumeration(); - static UClassID U_EXPORT2 getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; - virtual const UnicodeString* snext(UErrorCode& status) override; - virtual void reset(UErrorCode& status) override; - virtual int32_t count(UErrorCode& status) const override; - void add(const UnicodeString &pattern, UErrorCode& status); -private: - int32_t pos; - UBool isCanonicalItem(const UnicodeString& item) const; - LocalPointer fPatterns; -}; - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2007-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTPTNGEN.H +* +******************************************************************************* +*/ + +#ifndef __DTPTNGEN_IMPL_H__ +#define __DTPTNGEN_IMPL_H__ + +#include "unicode/udatpg.h" + +#include "unicode/strenum.h" +#include "unicode/unistr.h" +#include "uvector.h" + +// TODO(claireho): Split off Builder class. +// TODO(claireho): If splitting off Builder class: As subclass or independent? + +#define MAX_PATTERN_ENTRIES 52 +#define MAX_CLDR_FIELD_LEN 60 +#define MAX_DT_TOKEN 50 +#define MAX_RESOURCE_FIELD 12 +#define MAX_AVAILABLE_FORMATS 12 +#define NONE 0 +#define EXTRA_FIELD 0x10000 +#define MISSING_FIELD 0x1000 +#define MAX_STRING_ENUMERATION 200 +#define SINGLE_QUOTE ((char16_t)0x0027) +#define FORWARDSLASH ((char16_t)0x002F) +#define BACKSLASH ((char16_t)0x005C) +#define SPACE ((char16_t)0x0020) +#define QUOTATION_MARK ((char16_t)0x0022) +#define ASTERISK ((char16_t)0x002A) +#define PLUSSITN ((char16_t)0x002B) +#define COMMA ((char16_t)0x002C) +#define HYPHEN ((char16_t)0x002D) +#define DOT ((char16_t)0x002E) +#define COLON ((char16_t)0x003A) +#define CAP_A ((char16_t)0x0041) +#define CAP_B ((char16_t)0x0042) +#define CAP_C ((char16_t)0x0043) +#define CAP_D ((char16_t)0x0044) +#define CAP_E ((char16_t)0x0045) +#define CAP_F ((char16_t)0x0046) +#define CAP_G ((char16_t)0x0047) +#define CAP_H ((char16_t)0x0048) +#define CAP_J ((char16_t)0x004A) +#define CAP_K ((char16_t)0x004B) +#define CAP_L ((char16_t)0x004C) +#define CAP_M ((char16_t)0x004D) +#define CAP_O ((char16_t)0x004F) +#define CAP_Q ((char16_t)0x0051) +#define CAP_S ((char16_t)0x0053) +#define CAP_T ((char16_t)0x0054) +#define CAP_U ((char16_t)0x0055) +#define CAP_V ((char16_t)0x0056) +#define CAP_W ((char16_t)0x0057) +#define CAP_X ((char16_t)0x0058) +#define CAP_Y ((char16_t)0x0059) +#define CAP_Z ((char16_t)0x005A) +#define LOWLINE ((char16_t)0x005F) +#define LOW_A ((char16_t)0x0061) +#define LOW_B ((char16_t)0x0062) +#define LOW_C ((char16_t)0x0063) +#define LOW_D ((char16_t)0x0064) +#define LOW_E ((char16_t)0x0065) +#define LOW_F ((char16_t)0x0066) +#define LOW_G ((char16_t)0x0067) +#define LOW_H ((char16_t)0x0068) +#define LOW_I ((char16_t)0x0069) +#define LOW_J ((char16_t)0x006A) +#define LOW_K ((char16_t)0x006B) +#define LOW_L ((char16_t)0x006C) +#define LOW_M ((char16_t)0x006D) +#define LOW_N ((char16_t)0x006E) +#define LOW_O ((char16_t)0x006F) +#define LOW_P ((char16_t)0x0070) +#define LOW_Q ((char16_t)0x0071) +#define LOW_R ((char16_t)0x0072) +#define LOW_S ((char16_t)0x0073) +#define LOW_T ((char16_t)0x0074) +#define LOW_U ((char16_t)0x0075) +#define LOW_V ((char16_t)0x0076) +#define LOW_W ((char16_t)0x0077) +#define LOW_X ((char16_t)0x0078) +#define LOW_Y ((char16_t)0x0079) +#define LOW_Z ((char16_t)0x007A) +#define DT_NARROW -0x101 +#define DT_SHORTER -0x102 +#define DT_SHORT -0x103 +#define DT_LONG -0x104 +#define DT_NUMERIC 0x100 +#define DT_DELTA 0x10 + +U_NAMESPACE_BEGIN + +const int32_t UDATPG_FRACTIONAL_MASK = 1< skeleton; + UnicodeString pattern; + UBool skeletonWasSpecified; // if specified in availableFormats, not derived + LocalPointer next; + + PtnElem(const UnicodeString &basePattern, const UnicodeString &pattern); + virtual ~PtnElem(); +}; + +class FormatParser : public UMemory { +public: + UnicodeString items[MAX_DT_TOKEN]; + int32_t itemNumber; + + FormatParser(); + virtual ~FormatParser(); + void set(const UnicodeString& patternString); + void getQuoteLiteral(UnicodeString& quote, int32_t *itemIndex); + UBool isPatternSeparator(const UnicodeString& field) const; + static UBool isQuoteLiteral(const UnicodeString& s); + static int32_t getCanonicalIndex(const UnicodeString& s) { return getCanonicalIndex(s, true); } + static int32_t getCanonicalIndex(const UnicodeString& s, UBool strict); + +private: + typedef enum TokenStatus { + START, + ADD_TOKEN, + SYNTAX_ERROR, + DONE + } TokenStatus; + + TokenStatus status; + virtual TokenStatus setTokens(const UnicodeString& pattern, int32_t startPos, int32_t *len); +}; + +class DistanceInfo : public UMemory { +public: + int32_t missingFieldMask; + int32_t extraFieldMask; + + DistanceInfo() {} + virtual ~DistanceInfo(); + void clear() { missingFieldMask = extraFieldMask = 0; } + void setTo(const DistanceInfo& other); + void addMissing(int32_t field) { missingFieldMask |= (1< matcher; + PatternMap *patternMap; +}; + +class DTSkeletonEnumeration : public StringEnumeration { +public: + DTSkeletonEnumeration(PatternMap& patternMap, dtStrEnum type, UErrorCode& status); + virtual ~DTSkeletonEnumeration(); + static UClassID U_EXPORT2 getStaticClassID(); + virtual UClassID getDynamicClassID() const override; + virtual const UnicodeString* snext(UErrorCode& status) override; + virtual void reset(UErrorCode& status) override; + virtual int32_t count(UErrorCode& status) const override; +private: + int32_t pos; + UBool isCanonicalItem(const UnicodeString& item); + LocalPointer fSkeletons; +}; + +class DTRedundantEnumeration : public StringEnumeration { +public: + DTRedundantEnumeration(); + virtual ~DTRedundantEnumeration(); + static UClassID U_EXPORT2 getStaticClassID(); + virtual UClassID getDynamicClassID() const override; + virtual const UnicodeString* snext(UErrorCode& status) override; + virtual void reset(UErrorCode& status) override; + virtual int32_t count(UErrorCode& status) const override; + void add(const UnicodeString &pattern, UErrorCode& status); +private: + int32_t pos; + UBool isCanonicalItem(const UnicodeString& item) const; + LocalPointer fPatterns; +}; + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/i18n/dtrule.cpp b/deps/icu-small/source/i18n/dtrule.cpp index 63949b63aa9d77..f8ffc2d1a0200c 100644 --- a/deps/icu-small/source/i18n/dtrule.cpp +++ b/deps/icu-small/source/i18n/dtrule.cpp @@ -1,141 +1,141 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2007-2012, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/dtrule.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimeRule) - -DateTimeRule::DateTimeRule(int32_t month, - int32_t dayOfMonth, - int32_t millisInDay, - TimeRuleType timeType) -: fMonth(month), fDayOfMonth(dayOfMonth), fDayOfWeek(0), fWeekInMonth(0), fMillisInDay(millisInDay), - fDateRuleType(DateTimeRule::DOM), fTimeRuleType(timeType) { -} - -DateTimeRule::DateTimeRule(int32_t month, - int32_t weekInMonth, - int32_t dayOfWeek, - int32_t millisInDay, - TimeRuleType timeType) -: fMonth(month), fDayOfMonth(0), fDayOfWeek(dayOfWeek), fWeekInMonth(weekInMonth), fMillisInDay(millisInDay), - fDateRuleType(DateTimeRule::DOW), fTimeRuleType(timeType) { -} - -DateTimeRule::DateTimeRule(int32_t month, - int32_t dayOfMonth, - int32_t dayOfWeek, - UBool after, - int32_t millisInDay, - TimeRuleType timeType) -: UObject(), - fMonth(month), fDayOfMonth(dayOfMonth), fDayOfWeek(dayOfWeek), fWeekInMonth(0), fMillisInDay(millisInDay), - fTimeRuleType(timeType) { - if (after) { - fDateRuleType = DateTimeRule::DOW_GEQ_DOM; - } else { - fDateRuleType = DateTimeRule::DOW_LEQ_DOM; - } -} - -DateTimeRule::DateTimeRule(const DateTimeRule& source) -: UObject(source), - fMonth(source.fMonth), fDayOfMonth(source.fDayOfMonth), fDayOfWeek(source.fDayOfWeek), - fWeekInMonth(source.fWeekInMonth), fMillisInDay(source.fMillisInDay), - fDateRuleType(source.fDateRuleType), fTimeRuleType(source.fTimeRuleType) { -} - -DateTimeRule::~DateTimeRule() { -} - -DateTimeRule* -DateTimeRule::clone() const { - return new DateTimeRule(*this); -} - -DateTimeRule& -DateTimeRule::operator=(const DateTimeRule& right) { - if (this != &right) { - fMonth = right.fMonth; - fDayOfMonth = right.fDayOfMonth; - fDayOfWeek = right.fDayOfWeek; - fWeekInMonth = right.fWeekInMonth; - fMillisInDay = right.fMillisInDay; - fDateRuleType = right.fDateRuleType; - fTimeRuleType = right.fTimeRuleType; - } - return *this; -} - -bool -DateTimeRule::operator==(const DateTimeRule& that) const { - return ((this == &that) || - (typeid(*this) == typeid(that) && - fMonth == that.fMonth && - fDayOfMonth == that.fDayOfMonth && - fDayOfWeek == that.fDayOfWeek && - fWeekInMonth == that.fWeekInMonth && - fMillisInDay == that.fMillisInDay && - fDateRuleType == that.fDateRuleType && - fTimeRuleType == that.fTimeRuleType)); -} - -bool -DateTimeRule::operator!=(const DateTimeRule& that) const { - return !operator==(that); -} - -DateTimeRule::DateRuleType -DateTimeRule::getDateRuleType(void) const { - return fDateRuleType; -} - -DateTimeRule::TimeRuleType -DateTimeRule::getTimeRuleType(void) const { - return fTimeRuleType; -} - -int32_t -DateTimeRule::getRuleMonth(void) const { - return fMonth; -} - -int32_t -DateTimeRule::getRuleDayOfMonth(void) const { - return fDayOfMonth; -} - -int32_t -DateTimeRule::getRuleDayOfWeek(void) const { - return fDayOfWeek; -} - -int32_t -DateTimeRule::getRuleWeekInMonth(void) const { - return fWeekInMonth; -} - -int32_t -DateTimeRule::getRuleMillisInDay(void) const { - return fMillisInDay; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2007-2012, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +*/ + +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/dtrule.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimeRule) + +DateTimeRule::DateTimeRule(int32_t month, + int32_t dayOfMonth, + int32_t millisInDay, + TimeRuleType timeType) +: fMonth(month), fDayOfMonth(dayOfMonth), fDayOfWeek(0), fWeekInMonth(0), fMillisInDay(millisInDay), + fDateRuleType(DateTimeRule::DOM), fTimeRuleType(timeType) { +} + +DateTimeRule::DateTimeRule(int32_t month, + int32_t weekInMonth, + int32_t dayOfWeek, + int32_t millisInDay, + TimeRuleType timeType) +: fMonth(month), fDayOfMonth(0), fDayOfWeek(dayOfWeek), fWeekInMonth(weekInMonth), fMillisInDay(millisInDay), + fDateRuleType(DateTimeRule::DOW), fTimeRuleType(timeType) { +} + +DateTimeRule::DateTimeRule(int32_t month, + int32_t dayOfMonth, + int32_t dayOfWeek, + UBool after, + int32_t millisInDay, + TimeRuleType timeType) +: UObject(), + fMonth(month), fDayOfMonth(dayOfMonth), fDayOfWeek(dayOfWeek), fWeekInMonth(0), fMillisInDay(millisInDay), + fTimeRuleType(timeType) { + if (after) { + fDateRuleType = DateTimeRule::DOW_GEQ_DOM; + } else { + fDateRuleType = DateTimeRule::DOW_LEQ_DOM; + } +} + +DateTimeRule::DateTimeRule(const DateTimeRule& source) +: UObject(source), + fMonth(source.fMonth), fDayOfMonth(source.fDayOfMonth), fDayOfWeek(source.fDayOfWeek), + fWeekInMonth(source.fWeekInMonth), fMillisInDay(source.fMillisInDay), + fDateRuleType(source.fDateRuleType), fTimeRuleType(source.fTimeRuleType) { +} + +DateTimeRule::~DateTimeRule() { +} + +DateTimeRule* +DateTimeRule::clone() const { + return new DateTimeRule(*this); +} + +DateTimeRule& +DateTimeRule::operator=(const DateTimeRule& right) { + if (this != &right) { + fMonth = right.fMonth; + fDayOfMonth = right.fDayOfMonth; + fDayOfWeek = right.fDayOfWeek; + fWeekInMonth = right.fWeekInMonth; + fMillisInDay = right.fMillisInDay; + fDateRuleType = right.fDateRuleType; + fTimeRuleType = right.fTimeRuleType; + } + return *this; +} + +bool +DateTimeRule::operator==(const DateTimeRule& that) const { + return ((this == &that) || + (typeid(*this) == typeid(that) && + fMonth == that.fMonth && + fDayOfMonth == that.fDayOfMonth && + fDayOfWeek == that.fDayOfWeek && + fWeekInMonth == that.fWeekInMonth && + fMillisInDay == that.fMillisInDay && + fDateRuleType == that.fDateRuleType && + fTimeRuleType == that.fTimeRuleType)); +} + +bool +DateTimeRule::operator!=(const DateTimeRule& that) const { + return !operator==(that); +} + +DateTimeRule::DateRuleType +DateTimeRule::getDateRuleType() const { + return fDateRuleType; +} + +DateTimeRule::TimeRuleType +DateTimeRule::getTimeRuleType() const { + return fTimeRuleType; +} + +int32_t +DateTimeRule::getRuleMonth() const { + return fMonth; +} + +int32_t +DateTimeRule::getRuleDayOfMonth() const { + return fDayOfMonth; +} + +int32_t +DateTimeRule::getRuleDayOfWeek() const { + return fDayOfWeek; +} + +int32_t +DateTimeRule::getRuleWeekInMonth() const { + return fWeekInMonth; +} + +int32_t +DateTimeRule::getRuleMillisInDay() const { + return fMillisInDay; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/erarules.cpp b/deps/icu-small/source/i18n/erarules.cpp index ffc7c993a22069..d3f2121cf6b656 100644 --- a/deps/icu-small/source/i18n/erarules.cpp +++ b/deps/icu-small/source/i18n/erarules.cpp @@ -1,326 +1,326 @@ -// © 2018 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#include - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include -#include "unicode/ucal.h" -#include "unicode/ures.h" -#include "unicode/ustring.h" -#include "unicode/timezone.h" -#include "cmemory.h" -#include "cstring.h" -#include "erarules.h" -#include "gregoimp.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - -static const int32_t MAX_ENCODED_START_YEAR = 32767; -static const int32_t MIN_ENCODED_START_YEAR = -32768; -static const int32_t MIN_ENCODED_START = -2147483391; // encodeDate(MIN_ENCODED_START_YEAR, 1, 1, ...); - -static const int32_t YEAR_MASK = 0xFFFF0000; -static const int32_t MONTH_MASK = 0x0000FF00; -static const int32_t DAY_MASK = 0x000000FF; - -static const int32_t MAX_INT32 = 0x7FFFFFFF; -static const int32_t MIN_INT32 = 0xFFFFFFFF; - -static const UChar VAL_FALSE[] = {0x66, 0x61, 0x6c, 0x73, 0x65}; // "false" -static const UChar VAL_FALSE_LEN = 5; - -static UBool isSet(int startDate) { - return startDate != 0; -} - -static UBool isValidRuleStartDate(int32_t year, int32_t month, int32_t day) { - return year >= MIN_ENCODED_START_YEAR && year <= MAX_ENCODED_START_YEAR - && month >= 1 && month <= 12 && day >=1 && day <= 31; -} - -/** - * Encode year/month/date to a single integer. - * year is high 16 bits (-32768 to 32767), month is - * next 8 bits and day of month is last 8 bits. - * - * @param year year - * @param month month (1-base) - * @param day day of month - * @return an encoded date. - */ -static int32_t encodeDate(int32_t year, int32_t month, int32_t day) { - return year << 16 | month << 8 | day; -} - -static void decodeDate(int32_t encodedDate, int32_t (&fields)[3]) { - if (encodedDate == MIN_ENCODED_START) { - fields[0] = MIN_INT32; - fields[1] = 1; - fields[2] = 1; - } else { - fields[0] = (encodedDate & YEAR_MASK) >> 16; - fields[1] = (encodedDate & MONTH_MASK) >> 8; - fields[2] = encodedDate & DAY_MASK; - } -} - -/** - * Compare an encoded date with another date specified by year/month/day. - * @param encoded An encoded date - * @param year Year of another date - * @param month Month of another date - * @param day Day of another date - * @return -1 when encoded date is earlier, 0 when two dates are same, - * and 1 when encoded date is later. - */ -static int32_t compareEncodedDateWithYMD(int encoded, int year, int month, int day) { - if (year < MIN_ENCODED_START_YEAR) { - if (encoded == MIN_ENCODED_START) { - if (year > MIN_INT32 || month > 1 || day > 1) { - return -1; - } - return 0; - } else { - return 1; - } - } else if (year > MAX_ENCODED_START_YEAR) { - return -1; - } else { - int tmp = encodeDate(year, month, day); - if (encoded < tmp) { - return -1; - } else if (encoded == tmp) { - return 0; - } else { - return 1; - } - } -} - -EraRules::EraRules(LocalMemory& eraStartDates, int32_t numEras) - : numEras(numEras) { - startDates = std::move(eraStartDates); - initCurrentEra(); -} - -EraRules::~EraRules() { -} - -EraRules* EraRules::createInstance(const char *calType, UBool includeTentativeEra, UErrorCode& status) { - if(U_FAILURE(status)) { - return nullptr; - } - LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "supplementalData", &status)); - ures_getByKey(rb.getAlias(), "calendarData", rb.getAlias(), &status); - ures_getByKey(rb.getAlias(), calType, rb.getAlias(), &status); - ures_getByKey(rb.getAlias(), "eras", rb.getAlias(), &status); - - if (U_FAILURE(status)) { - return nullptr; - } - - int32_t numEras = ures_getSize(rb.getAlias()); - int32_t firstTentativeIdx = MAX_INT32; - - LocalMemory startDates(static_cast(uprv_malloc(numEras * sizeof(int32_t)))); - if (startDates.isNull()) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - uprv_memset(startDates.getAlias(), 0 , numEras * sizeof(int32_t)); - - while (ures_hasNext(rb.getAlias())) { - LocalUResourceBundlePointer eraRuleRes(ures_getNextResource(rb.getAlias(), nullptr, &status)); - if (U_FAILURE(status)) { - return nullptr; - } - const char *eraIdxStr = ures_getKey(eraRuleRes.getAlias()); - char *endp; - int32_t eraIdx = (int32_t)strtol(eraIdxStr, &endp, 10); - if ((size_t)(endp - eraIdxStr) != uprv_strlen(eraIdxStr)) { - status = U_INVALID_FORMAT_ERROR; - return nullptr; - } - if (eraIdx < 0 || eraIdx >= numEras) { - status = U_INVALID_FORMAT_ERROR; - return nullptr; - } - if (isSet(startDates[eraIdx])) { - // start date of the index was already set - status = U_INVALID_FORMAT_ERROR; - return nullptr; - } - - UBool hasName = true; - UBool hasEnd = true; - int32_t len; - while (ures_hasNext(eraRuleRes.getAlias())) { - LocalUResourceBundlePointer res(ures_getNextResource(eraRuleRes.getAlias(), nullptr, &status)); - if (U_FAILURE(status)) { - return nullptr; - } - const char *key = ures_getKey(res.getAlias()); - if (uprv_strcmp(key, "start") == 0) { - const int32_t *fields = ures_getIntVector(res.getAlias(), &len, &status); - if (U_FAILURE(status)) { - return nullptr; - } - if (len != 3 || !isValidRuleStartDate(fields[0], fields[1], fields[2])) { - status = U_INVALID_FORMAT_ERROR; - return nullptr; - } - startDates[eraIdx] = encodeDate(fields[0], fields[1], fields[2]); - } else if (uprv_strcmp(key, "named") == 0) { - const UChar *val = ures_getString(res.getAlias(), &len, &status); - if (u_strncmp(val, VAL_FALSE, VAL_FALSE_LEN) == 0) { - hasName = false; - } - } else if (uprv_strcmp(key, "end") == 0) { - hasEnd = true; - } - } - - if (isSet(startDates[eraIdx])) { - if (hasEnd) { - // This implementation assumes either start or end is available, not both. - // For now, just ignore the end rule. - } - } else { - if (hasEnd) { - if (eraIdx != 0) { - // This implementation does not support end only rule for eras other than - // the first one. - status = U_INVALID_FORMAT_ERROR; - return nullptr; - } - U_ASSERT(eraIdx == 0); - startDates[eraIdx] = MIN_ENCODED_START; - } else { - status = U_INVALID_FORMAT_ERROR; - return nullptr; - } - } - - if (hasName) { - if (eraIdx >= firstTentativeIdx) { - status = U_INVALID_FORMAT_ERROR; - return nullptr; - } - } else { - if (eraIdx < firstTentativeIdx) { - firstTentativeIdx = eraIdx; - } - } - } - - EraRules *result; - if (firstTentativeIdx < MAX_INT32 && !includeTentativeEra) { - result = new EraRules(startDates, firstTentativeIdx); - } else { - result = new EraRules(startDates, numEras); - } - - if (result == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return result; -} - -void EraRules::getStartDate(int32_t eraIdx, int32_t (&fields)[3], UErrorCode& status) const { - if(U_FAILURE(status)) { - return; - } - if (eraIdx < 0 || eraIdx >= numEras) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - decodeDate(startDates[eraIdx], fields); -} - -int32_t EraRules::getStartYear(int32_t eraIdx, UErrorCode& status) const { - int year = MAX_INT32; // bogus value - if(U_FAILURE(status)) { - return year; - } - if (eraIdx < 0 || eraIdx >= numEras) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return year; - } - int fields[3]; - decodeDate(startDates[eraIdx], fields); - year = fields[0]; - - return year; -} - -int32_t EraRules::getEraIndex(int32_t year, int32_t month, int32_t day, UErrorCode& status) const { - if(U_FAILURE(status)) { - return -1; - } - - if (month < 1 || month > 12 || day < 1 || day > 31) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return -1; - } - int32_t high = numEras; // last index + 1 - int32_t low; - - // Short circuit for recent years. Most modern computations will - // occur in the last few eras. - if (compareEncodedDateWithYMD(startDates[getCurrentEraIndex()], year, month, day) <= 0) { - low = getCurrentEraIndex(); - } else { - low = 0; - } - - // Do binary search - while (low < high - 1) { - int i = (low + high) / 2; - if (compareEncodedDateWithYMD(startDates[i], year, month, day) <= 0) { - low = i; - } else { - high = i; - } - } - return low; -} - -void EraRules::initCurrentEra() { - // Compute local wall time in millis using ICU's default time zone. - UErrorCode ec = U_ZERO_ERROR; - UDate localMillis = ucal_getNow(); - - int32_t rawOffset, dstOffset; - TimeZone* zone = TimeZone::createDefault(); - // If we failed to create the default time zone, we are in a bad state and don't - // really have many options. Carry on using UTC millis as a fallback. - if (zone != nullptr) { - zone->getOffset(localMillis, false, rawOffset, dstOffset, ec); - delete zone; - localMillis += (rawOffset + dstOffset); - } - - int year, month0, dom, dow, doy, mid; - Grego::timeToFields(localMillis, year, month0, dom, dow, doy, mid); - int currentEncodedDate = encodeDate(year, month0 + 1 /* changes to 1-base */, dom); - int eraIdx = numEras - 1; - while (eraIdx > 0) { - if (currentEncodedDate >= startDates[eraIdx]) { - break; - } - eraIdx--; - } - // Note: current era could be before the first era. - // In this case, this implementation returns the first era index (0). - currentEra = eraIdx; -} - -U_NAMESPACE_END -#endif /* #if !UCONFIG_NO_FORMATTING */ - - +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include +#include "unicode/ucal.h" +#include "unicode/ures.h" +#include "unicode/ustring.h" +#include "unicode/timezone.h" +#include "cmemory.h" +#include "cstring.h" +#include "erarules.h" +#include "gregoimp.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +static const int32_t MAX_ENCODED_START_YEAR = 32767; +static const int32_t MIN_ENCODED_START_YEAR = -32768; +static const int32_t MIN_ENCODED_START = -2147483391; // encodeDate(MIN_ENCODED_START_YEAR, 1, 1, ...); + +static const int32_t YEAR_MASK = 0xFFFF0000; +static const int32_t MONTH_MASK = 0x0000FF00; +static const int32_t DAY_MASK = 0x000000FF; + +static const int32_t MAX_INT32 = 0x7FFFFFFF; +static const int32_t MIN_INT32 = 0xFFFFFFFF; + +static const char16_t VAL_FALSE[] = {0x66, 0x61, 0x6c, 0x73, 0x65}; // "false" +static const char16_t VAL_FALSE_LEN = 5; + +static UBool isSet(int startDate) { + return startDate != 0; +} + +static UBool isValidRuleStartDate(int32_t year, int32_t month, int32_t day) { + return year >= MIN_ENCODED_START_YEAR && year <= MAX_ENCODED_START_YEAR + && month >= 1 && month <= 12 && day >=1 && day <= 31; +} + +/** + * Encode year/month/date to a single integer. + * year is high 16 bits (-32768 to 32767), month is + * next 8 bits and day of month is last 8 bits. + * + * @param year year + * @param month month (1-base) + * @param day day of month + * @return an encoded date. + */ +static int32_t encodeDate(int32_t year, int32_t month, int32_t day) { + return (int32_t)((uint32_t)year << 16) | month << 8 | day; +} + +static void decodeDate(int32_t encodedDate, int32_t (&fields)[3]) { + if (encodedDate == MIN_ENCODED_START) { + fields[0] = MIN_INT32; + fields[1] = 1; + fields[2] = 1; + } else { + fields[0] = (encodedDate & YEAR_MASK) >> 16; + fields[1] = (encodedDate & MONTH_MASK) >> 8; + fields[2] = encodedDate & DAY_MASK; + } +} + +/** + * Compare an encoded date with another date specified by year/month/day. + * @param encoded An encoded date + * @param year Year of another date + * @param month Month of another date + * @param day Day of another date + * @return -1 when encoded date is earlier, 0 when two dates are same, + * and 1 when encoded date is later. + */ +static int32_t compareEncodedDateWithYMD(int encoded, int year, int month, int day) { + if (year < MIN_ENCODED_START_YEAR) { + if (encoded == MIN_ENCODED_START) { + if (year > MIN_INT32 || month > 1 || day > 1) { + return -1; + } + return 0; + } else { + return 1; + } + } else if (year > MAX_ENCODED_START_YEAR) { + return -1; + } else { + int tmp = encodeDate(year, month, day); + if (encoded < tmp) { + return -1; + } else if (encoded == tmp) { + return 0; + } else { + return 1; + } + } +} + +EraRules::EraRules(LocalMemory& eraStartDates, int32_t numEras) + : numEras(numEras) { + startDates = std::move(eraStartDates); + initCurrentEra(); +} + +EraRules::~EraRules() { +} + +EraRules* EraRules::createInstance(const char *calType, UBool includeTentativeEra, UErrorCode& status) { + if(U_FAILURE(status)) { + return nullptr; + } + LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "supplementalData", &status)); + ures_getByKey(rb.getAlias(), "calendarData", rb.getAlias(), &status); + ures_getByKey(rb.getAlias(), calType, rb.getAlias(), &status); + ures_getByKey(rb.getAlias(), "eras", rb.getAlias(), &status); + + if (U_FAILURE(status)) { + return nullptr; + } + + int32_t numEras = ures_getSize(rb.getAlias()); + int32_t firstTentativeIdx = MAX_INT32; + + LocalMemory startDates(static_cast(uprv_malloc(numEras * sizeof(int32_t)))); + if (startDates.isNull()) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + uprv_memset(startDates.getAlias(), 0 , numEras * sizeof(int32_t)); + + while (ures_hasNext(rb.getAlias())) { + LocalUResourceBundlePointer eraRuleRes(ures_getNextResource(rb.getAlias(), nullptr, &status)); + if (U_FAILURE(status)) { + return nullptr; + } + const char *eraIdxStr = ures_getKey(eraRuleRes.getAlias()); + char *endp; + int32_t eraIdx = (int32_t)strtol(eraIdxStr, &endp, 10); + if ((size_t)(endp - eraIdxStr) != uprv_strlen(eraIdxStr)) { + status = U_INVALID_FORMAT_ERROR; + return nullptr; + } + if (eraIdx < 0 || eraIdx >= numEras) { + status = U_INVALID_FORMAT_ERROR; + return nullptr; + } + if (isSet(startDates[eraIdx])) { + // start date of the index was already set + status = U_INVALID_FORMAT_ERROR; + return nullptr; + } + + UBool hasName = true; + UBool hasEnd = true; + int32_t len; + while (ures_hasNext(eraRuleRes.getAlias())) { + LocalUResourceBundlePointer res(ures_getNextResource(eraRuleRes.getAlias(), nullptr, &status)); + if (U_FAILURE(status)) { + return nullptr; + } + const char *key = ures_getKey(res.getAlias()); + if (uprv_strcmp(key, "start") == 0) { + const int32_t *fields = ures_getIntVector(res.getAlias(), &len, &status); + if (U_FAILURE(status)) { + return nullptr; + } + if (len != 3 || !isValidRuleStartDate(fields[0], fields[1], fields[2])) { + status = U_INVALID_FORMAT_ERROR; + return nullptr; + } + startDates[eraIdx] = encodeDate(fields[0], fields[1], fields[2]); + } else if (uprv_strcmp(key, "named") == 0) { + const char16_t *val = ures_getString(res.getAlias(), &len, &status); + if (u_strncmp(val, VAL_FALSE, VAL_FALSE_LEN) == 0) { + hasName = false; + } + } else if (uprv_strcmp(key, "end") == 0) { + hasEnd = true; + } + } + + if (isSet(startDates[eraIdx])) { + if (hasEnd) { + // This implementation assumes either start or end is available, not both. + // For now, just ignore the end rule. + } + } else { + if (hasEnd) { + if (eraIdx != 0) { + // This implementation does not support end only rule for eras other than + // the first one. + status = U_INVALID_FORMAT_ERROR; + return nullptr; + } + U_ASSERT(eraIdx == 0); + startDates[eraIdx] = MIN_ENCODED_START; + } else { + status = U_INVALID_FORMAT_ERROR; + return nullptr; + } + } + + if (hasName) { + if (eraIdx >= firstTentativeIdx) { + status = U_INVALID_FORMAT_ERROR; + return nullptr; + } + } else { + if (eraIdx < firstTentativeIdx) { + firstTentativeIdx = eraIdx; + } + } + } + + EraRules *result; + if (firstTentativeIdx < MAX_INT32 && !includeTentativeEra) { + result = new EraRules(startDates, firstTentativeIdx); + } else { + result = new EraRules(startDates, numEras); + } + + if (result == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return result; +} + +void EraRules::getStartDate(int32_t eraIdx, int32_t (&fields)[3], UErrorCode& status) const { + if(U_FAILURE(status)) { + return; + } + if (eraIdx < 0 || eraIdx >= numEras) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + decodeDate(startDates[eraIdx], fields); +} + +int32_t EraRules::getStartYear(int32_t eraIdx, UErrorCode& status) const { + int year = MAX_INT32; // bogus value + if(U_FAILURE(status)) { + return year; + } + if (eraIdx < 0 || eraIdx >= numEras) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return year; + } + int fields[3]; + decodeDate(startDates[eraIdx], fields); + year = fields[0]; + + return year; +} + +int32_t EraRules::getEraIndex(int32_t year, int32_t month, int32_t day, UErrorCode& status) const { + if(U_FAILURE(status)) { + return -1; + } + + if (month < 1 || month > 12 || day < 1 || day > 31) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return -1; + } + int32_t high = numEras; // last index + 1 + int32_t low; + + // Short circuit for recent years. Most modern computations will + // occur in the last few eras. + if (compareEncodedDateWithYMD(startDates[getCurrentEraIndex()], year, month, day) <= 0) { + low = getCurrentEraIndex(); + } else { + low = 0; + } + + // Do binary search + while (low < high - 1) { + int i = (low + high) / 2; + if (compareEncodedDateWithYMD(startDates[i], year, month, day) <= 0) { + low = i; + } else { + high = i; + } + } + return low; +} + +void EraRules::initCurrentEra() { + // Compute local wall time in millis using ICU's default time zone. + UErrorCode ec = U_ZERO_ERROR; + UDate localMillis = ucal_getNow(); + + int32_t rawOffset, dstOffset; + TimeZone* zone = TimeZone::createDefault(); + // If we failed to create the default time zone, we are in a bad state and don't + // really have many options. Carry on using UTC millis as a fallback. + if (zone != nullptr) { + zone->getOffset(localMillis, false, rawOffset, dstOffset, ec); + delete zone; + localMillis += (rawOffset + dstOffset); + } + + int year, month0, dom, dow, doy, mid; + Grego::timeToFields(localMillis, year, month0, dom, dow, doy, mid); + int currentEncodedDate = encodeDate(year, month0 + 1 /* changes to 1-base */, dom); + int eraIdx = numEras - 1; + while (eraIdx > 0) { + if (currentEncodedDate >= startDates[eraIdx]) { + break; + } + eraIdx--; + } + // Note: current era could be before the first era. + // In this case, this implementation returns the first era index (0). + currentEra = eraIdx; +} + +U_NAMESPACE_END +#endif /* #if !UCONFIG_NO_FORMATTING */ + + diff --git a/deps/icu-small/source/i18n/erarules.h b/deps/icu-small/source/i18n/erarules.h index 74b7862da4c18c..e538a0ba377dc4 100644 --- a/deps/icu-small/source/i18n/erarules.h +++ b/deps/icu-small/source/i18n/erarules.h @@ -1,99 +1,99 @@ -// © 2018 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#ifndef ERARULES_H_ -#define ERARULES_H_ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/localpointer.h" -#include "unicode/uobject.h" -#include "cmemory.h" - -U_NAMESPACE_BEGIN - -// Export an explicit template instantiation of LocalMemory used as a data member of EraRules. -// 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. -#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN -#if defined(_MSC_VER) -// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!= -#pragma warning(push) -#pragma warning(disable: 4661) -#endif -template class U_I18N_API LocalPointerBase; -template class U_I18N_API LocalMemory; -#if defined(_MSC_VER) -#pragma warning(pop) -#endif -#endif - -class U_I18N_API EraRules : public UMemory { -public: - ~EraRules(); - - static EraRules* createInstance(const char *calType, UBool includeTentativeEra, UErrorCode& status); - - /** - * Gets number of effective eras - * @return number of effective eras - */ - inline int32_t getNumberOfEras() const { - return numEras; - } - - /** - * Gets start date of an era - * @param eraIdx Era index - * @param fields Receives date fields. The result includes values of year, month, - * day of month in this order. When an era has no start date, the result - * will be January 1st in year whose value is minimum integer. - * @param status Receives status. - */ - void getStartDate(int32_t eraIdx, int32_t (&fields)[3], UErrorCode& status) const; - - /** - * Gets start year of an era - * @param eraIdx Era index - * @param status Receives status. - * @return The first year of an era. When a era has no start date, minimum int32 - * value is returned. - */ - int32_t getStartYear(int32_t eraIdx, UErrorCode& status) const; - - /** - * Returns era index for the specified year/month/day. - * @param year Year - * @param month Month (1-base) - * @param day Day of month - * @param status Receives status - * @return era index (or 0, when the specified date is before the first era) - */ - int32_t getEraIndex(int32_t year, int32_t month, int32_t day, UErrorCode& status) const; - - /** - * Gets the current era index. This is calculated only once for an instance of - * EraRules. The current era calculation is based on the default time zone at - * the time of instantiation. - * - * @return era index of current era (or 0, when current date is before the first era) - */ - inline int32_t getCurrentEraIndex() const { - return currentEra; - } - -private: - EraRules(LocalMemory& eraStartDates, int32_t numEra); - - void initCurrentEra(); - - LocalMemory startDates; - int32_t numEras; - int32_t currentEra; -}; - -U_NAMESPACE_END -#endif /* #if !UCONFIG_NO_FORMATTING */ -#endif /* ERARULES_H_ */ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#ifndef ERARULES_H_ +#define ERARULES_H_ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/localpointer.h" +#include "unicode/uobject.h" +#include "cmemory.h" + +U_NAMESPACE_BEGIN + +// Export an explicit template instantiation of LocalMemory used as a data member of EraRules. +// 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. +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +#if defined(_MSC_VER) +// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!= +#pragma warning(push) +#pragma warning(disable: 4661) +#endif +template class U_I18N_API LocalPointerBase; +template class U_I18N_API LocalMemory; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#endif + +class U_I18N_API EraRules : public UMemory { +public: + ~EraRules(); + + static EraRules* createInstance(const char *calType, UBool includeTentativeEra, UErrorCode& status); + + /** + * Gets number of effective eras + * @return number of effective eras + */ + inline int32_t getNumberOfEras() const { + return numEras; + } + + /** + * Gets start date of an era + * @param eraIdx Era index + * @param fields Receives date fields. The result includes values of year, month, + * day of month in this order. When an era has no start date, the result + * will be January 1st in year whose value is minimum integer. + * @param status Receives status. + */ + void getStartDate(int32_t eraIdx, int32_t (&fields)[3], UErrorCode& status) const; + + /** + * Gets start year of an era + * @param eraIdx Era index + * @param status Receives status. + * @return The first year of an era. When a era has no start date, minimum int32 + * value is returned. + */ + int32_t getStartYear(int32_t eraIdx, UErrorCode& status) const; + + /** + * Returns era index for the specified year/month/day. + * @param year Year + * @param month Month (1-base) + * @param day Day of month + * @param status Receives status + * @return era index (or 0, when the specified date is before the first era) + */ + int32_t getEraIndex(int32_t year, int32_t month, int32_t day, UErrorCode& status) const; + + /** + * Gets the current era index. This is calculated only once for an instance of + * EraRules. The current era calculation is based on the default time zone at + * the time of instantiation. + * + * @return era index of current era (or 0, when current date is before the first era) + */ + inline int32_t getCurrentEraIndex() const { + return currentEra; + } + +private: + EraRules(LocalMemory& eraStartDates, int32_t numEra); + + void initCurrentEra(); + + LocalMemory startDates; + int32_t numEras; + int32_t currentEra; +}; + +U_NAMESPACE_END +#endif /* #if !UCONFIG_NO_FORMATTING */ +#endif /* ERARULES_H_ */ diff --git a/deps/icu-small/source/i18n/esctrn.cpp b/deps/icu-small/source/i18n/esctrn.cpp index 00c1304d2455d1..f0a474f769b50b 100644 --- a/deps/icu-small/source/i18n/esctrn.cpp +++ b/deps/icu-small/source/i18n/esctrn.cpp @@ -1,181 +1,181 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2001-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/19/2001 aliu Creation. -********************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/utf16.h" -#include "esctrn.h" -#include "util.h" - -U_NAMESPACE_BEGIN - -static const UChar UNIPRE[] = {85,43,0}; // "U+" -static const UChar BS_u[] = {92,117,0}; // "\\u" -static const UChar BS_U[] = {92,85,0}; // "\\U" -static const UChar XMLPRE[] = {38,35,120,0}; // "&#x" -static const UChar XML10PRE[] = {38,35,0}; // "&#" -static const UChar PERLPRE[] = {92,120,123,0}; // "\\x{" -static const UChar SEMI[] = {59,0}; // ";" -static const UChar RBRACE[] = {125,0}; // "}" - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(EscapeTransliterator) - -/** - * Factory methods - */ -static Transliterator* _createEscUnicode(const UnicodeString& ID, Transliterator::Token /*context*/) { - // Unicode: "U+10FFFF" hex, min=4, max=6 - return new EscapeTransliterator(ID, UnicodeString(true, UNIPRE, 2), UnicodeString(), 16, 4, true, NULL); -} -static Transliterator* _createEscJava(const UnicodeString& ID, Transliterator::Token /*context*/) { - // Java: "\\uFFFF" hex, min=4, max=4 - return new EscapeTransliterator(ID, UnicodeString(true, BS_u, 2), UnicodeString(), 16, 4, false, NULL); -} -static Transliterator* _createEscC(const UnicodeString& ID, Transliterator::Token /*context*/) { - // C: "\\uFFFF" hex, min=4, max=4; \\U0010FFFF hex, min=8, max=8 - return new EscapeTransliterator(ID, UnicodeString(true, BS_u, 2), UnicodeString(), 16, 4, true, - new EscapeTransliterator(UnicodeString(), UnicodeString(true, BS_U, 2), UnicodeString(), 16, 8, true, NULL)); -} -static Transliterator* _createEscXML(const UnicodeString& ID, Transliterator::Token /*context*/) { - // XML: "􏿿" hex, min=1, max=6 - return new EscapeTransliterator(ID, UnicodeString(true, XMLPRE, 3), UnicodeString(SEMI[0]), 16, 1, true, NULL); -} -static Transliterator* _createEscXML10(const UnicodeString& ID, Transliterator::Token /*context*/) { - // XML10: "&1114111;" dec, min=1, max=7 (not really "Any-Hex") - return new EscapeTransliterator(ID, UnicodeString(true, XML10PRE, 2), UnicodeString(SEMI[0]), 10, 1, true, NULL); -} -static Transliterator* _createEscPerl(const UnicodeString& ID, Transliterator::Token /*context*/) { - // Perl: "\\x{263A}" hex, min=1, max=6 - return new EscapeTransliterator(ID, UnicodeString(true, PERLPRE, 3), UnicodeString(RBRACE[0]), 16, 1, true, NULL); -} - -/** - * Registers standard variants with the system. Called by - * Transliterator during initialization. - */ -void EscapeTransliterator::registerIDs() { - Token t = integerToken(0); - - Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex/Unicode"), _createEscUnicode, t); - - Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex/Java"), _createEscJava, t); - - Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex/C"), _createEscC, t); - - Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex/XML"), _createEscXML, t); - - Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex/XML10"), _createEscXML10, t); - - Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex/Perl"), _createEscPerl, t); - - Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex"), _createEscJava, t); -} - -/** - * Constructs an escape transliterator with the given ID and - * parameters. See the class member documentation for details. - */ -EscapeTransliterator::EscapeTransliterator(const UnicodeString& newID, - const UnicodeString& _prefix, const UnicodeString& _suffix, - int32_t _radix, int32_t _minDigits, - UBool _grokSupplementals, - EscapeTransliterator* adoptedSupplementalHandler) : - Transliterator(newID, NULL) -{ - this->prefix = _prefix; - this->suffix = _suffix; - this->radix = _radix; - this->minDigits = _minDigits; - this->grokSupplementals = _grokSupplementals; - this->supplementalHandler = adoptedSupplementalHandler; -} - -/** - * Copy constructor. - */ -EscapeTransliterator::EscapeTransliterator(const EscapeTransliterator& o) : - Transliterator(o), - prefix(o.prefix), - suffix(o.suffix), - radix(o.radix), - minDigits(o.minDigits), - grokSupplementals(o.grokSupplementals) { - supplementalHandler = (o.supplementalHandler != 0) ? - new EscapeTransliterator(*o.supplementalHandler) : NULL; -} - -EscapeTransliterator::~EscapeTransliterator() { - delete supplementalHandler; -} - -/** - * Transliterator API. - */ -EscapeTransliterator* EscapeTransliterator::clone() const { - return new EscapeTransliterator(*this); -} - -/** - * Implements {@link Transliterator#handleTransliterate}. - */ -void EscapeTransliterator::handleTransliterate(Replaceable& text, - UTransPosition& pos, - UBool /*isIncremental*/) const -{ - /* TODO: Verify that isIncremental can be ignored */ - int32_t start = pos.start; - int32_t limit = pos.limit; - - UnicodeString buf(prefix); - int32_t prefixLen = prefix.length(); - UBool redoPrefix = false; - - while (start < limit) { - int32_t c = grokSupplementals ? text.char32At(start) : text.charAt(start); - int32_t charLen = grokSupplementals ? U16_LENGTH(c) : 1; - - if ((c & 0xFFFF0000) != 0 && supplementalHandler != NULL) { - buf.truncate(0); - buf.append(supplementalHandler->prefix); - ICU_Utility::appendNumber(buf, c, supplementalHandler->radix, - supplementalHandler->minDigits); - buf.append(supplementalHandler->suffix); - redoPrefix = true; - } else { - if (redoPrefix) { - buf.truncate(0); - buf.append(prefix); - redoPrefix = false; - } else { - buf.truncate(prefixLen); - } - ICU_Utility::appendNumber(buf, c, radix, minDigits); - buf.append(suffix); - } - - text.handleReplaceBetween(start, start + charLen, buf); - start += buf.length(); - limit += buf.length() - charLen; - } - - pos.contextLimit += limit - pos.limit; - pos.limit = limit; - pos.start = start; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2001-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/19/2001 aliu Creation. +********************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/utf16.h" +#include "esctrn.h" +#include "util.h" + +U_NAMESPACE_BEGIN + +static const char16_t UNIPRE[] = {85,43,0}; // "U+" +static const char16_t BS_u[] = {92,117,0}; // "\\u" +static const char16_t BS_U[] = {92,85,0}; // "\\U" +static const char16_t XMLPRE[] = {38,35,120,0}; // "&#x" +static const char16_t XML10PRE[] = {38,35,0}; // "&#" +static const char16_t PERLPRE[] = {92,120,123,0}; // "\\x{" +static const char16_t SEMI[] = {59,0}; // ";" +static const char16_t RBRACE[] = {125,0}; // "}" + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(EscapeTransliterator) + +/** + * Factory methods + */ +static Transliterator* _createEscUnicode(const UnicodeString& ID, Transliterator::Token /*context*/) { + // Unicode: "U+10FFFF" hex, min=4, max=6 + return new EscapeTransliterator(ID, UnicodeString(true, UNIPRE, 2), UnicodeString(), 16, 4, true, nullptr); +} +static Transliterator* _createEscJava(const UnicodeString& ID, Transliterator::Token /*context*/) { + // Java: "\\uFFFF" hex, min=4, max=4 + return new EscapeTransliterator(ID, UnicodeString(true, BS_u, 2), UnicodeString(), 16, 4, false, nullptr); +} +static Transliterator* _createEscC(const UnicodeString& ID, Transliterator::Token /*context*/) { + // C: "\\uFFFF" hex, min=4, max=4; \\U0010FFFF hex, min=8, max=8 + return new EscapeTransliterator(ID, UnicodeString(true, BS_u, 2), UnicodeString(), 16, 4, true, + new EscapeTransliterator(UnicodeString(), UnicodeString(true, BS_U, 2), UnicodeString(), 16, 8, true, nullptr)); +} +static Transliterator* _createEscXML(const UnicodeString& ID, Transliterator::Token /*context*/) { + // XML: "􏿿" hex, min=1, max=6 + return new EscapeTransliterator(ID, UnicodeString(true, XMLPRE, 3), UnicodeString(SEMI[0]), 16, 1, true, nullptr); +} +static Transliterator* _createEscXML10(const UnicodeString& ID, Transliterator::Token /*context*/) { + // XML10: "&1114111;" dec, min=1, max=7 (not really "Any-Hex") + return new EscapeTransliterator(ID, UnicodeString(true, XML10PRE, 2), UnicodeString(SEMI[0]), 10, 1, true, nullptr); +} +static Transliterator* _createEscPerl(const UnicodeString& ID, Transliterator::Token /*context*/) { + // Perl: "\\x{263A}" hex, min=1, max=6 + return new EscapeTransliterator(ID, UnicodeString(true, PERLPRE, 3), UnicodeString(RBRACE[0]), 16, 1, true, nullptr); +} + +/** + * Registers standard variants with the system. Called by + * Transliterator during initialization. + */ +void EscapeTransliterator::registerIDs() { + Token t = integerToken(0); + + Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex/Unicode"), _createEscUnicode, t); + + Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex/Java"), _createEscJava, t); + + Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex/C"), _createEscC, t); + + Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex/XML"), _createEscXML, t); + + Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex/XML10"), _createEscXML10, t); + + Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex/Perl"), _createEscPerl, t); + + Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-Hex"), _createEscJava, t); +} + +/** + * Constructs an escape transliterator with the given ID and + * parameters. See the class member documentation for details. + */ +EscapeTransliterator::EscapeTransliterator(const UnicodeString& newID, + const UnicodeString& _prefix, const UnicodeString& _suffix, + int32_t _radix, int32_t _minDigits, + UBool _grokSupplementals, + EscapeTransliterator* adoptedSupplementalHandler) : + Transliterator(newID, nullptr) +{ + this->prefix = _prefix; + this->suffix = _suffix; + this->radix = _radix; + this->minDigits = _minDigits; + this->grokSupplementals = _grokSupplementals; + this->supplementalHandler = adoptedSupplementalHandler; +} + +/** + * Copy constructor. + */ +EscapeTransliterator::EscapeTransliterator(const EscapeTransliterator& o) : + Transliterator(o), + prefix(o.prefix), + suffix(o.suffix), + radix(o.radix), + minDigits(o.minDigits), + grokSupplementals(o.grokSupplementals) { + supplementalHandler = (o.supplementalHandler != 0) ? + new EscapeTransliterator(*o.supplementalHandler) : nullptr; +} + +EscapeTransliterator::~EscapeTransliterator() { + delete supplementalHandler; +} + +/** + * Transliterator API. + */ +EscapeTransliterator* EscapeTransliterator::clone() const { + return new EscapeTransliterator(*this); +} + +/** + * Implements {@link Transliterator#handleTransliterate}. + */ +void EscapeTransliterator::handleTransliterate(Replaceable& text, + UTransPosition& pos, + UBool /*isIncremental*/) const +{ + /* TODO: Verify that isIncremental can be ignored */ + int32_t start = pos.start; + int32_t limit = pos.limit; + + UnicodeString buf(prefix); + int32_t prefixLen = prefix.length(); + UBool redoPrefix = false; + + while (start < limit) { + int32_t c = grokSupplementals ? text.char32At(start) : text.charAt(start); + int32_t charLen = grokSupplementals ? U16_LENGTH(c) : 1; + + if ((c & 0xFFFF0000) != 0 && supplementalHandler != nullptr) { + buf.truncate(0); + buf.append(supplementalHandler->prefix); + ICU_Utility::appendNumber(buf, c, supplementalHandler->radix, + supplementalHandler->minDigits); + buf.append(supplementalHandler->suffix); + redoPrefix = true; + } else { + if (redoPrefix) { + buf.truncate(0); + buf.append(prefix); + redoPrefix = false; + } else { + buf.truncate(prefixLen); + } + ICU_Utility::appendNumber(buf, c, radix, minDigits); + buf.append(suffix); + } + + text.handleReplaceBetween(start, start + charLen, buf); + start += buf.length(); + limit += buf.length() - charLen; + } + + pos.contextLimit += limit - pos.limit; + pos.limit = limit; + pos.start = start; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +//eof diff --git a/deps/icu-small/source/i18n/esctrn.h b/deps/icu-small/source/i18n/esctrn.h index a4282ea86a2c51..eddd732946e6ec 100644 --- a/deps/icu-small/source/i18n/esctrn.h +++ b/deps/icu-small/source/i18n/esctrn.h @@ -1,144 +1,144 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2001-2007, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/20/2001 aliu Creation. -********************************************************************** -*/ -#ifndef ESCTRN_H -#define ESCTRN_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/translit.h" - -U_NAMESPACE_BEGIN - -/** - * A transliterator that converts Unicode characters to an escape - * form. Examples of escape forms are "U+4E01" and "􏿿". - * Escape forms have a prefix and suffix, either of which may be - * empty, a radix, typically 16 or 10, a minimum digit count, - * typically 1, 4, or 8, and a boolean that specifies whether - * supplemental characters are handled as 32-bit code points or as two - * 16-bit code units. Most escape forms handle 32-bit code points, - * but some, such as the Java form, intentionally break them into two - * surrogate pairs, for backward compatibility. - * - *

Some escape forms actually have two different patterns, one for - * BMP characters (0..FFFF) and one for supplements (>FFFF). To - * handle this, a second EscapeTransliterator may be defined that - * specifies the pattern to be produced for supplementals. An example - * of a form that requires this is the C form, which uses "\\uFFFF" - * for BMP characters and "\\U0010FFFF" for supplementals. - * - *

This class is package private. It registers several standard - * variants with the system which are then accessed via their IDs. - * - * @author Alan Liu - */ -class EscapeTransliterator : public Transliterator { - - private: - - /** - * The prefix of the escape form; may be empty, but usually isn't. - */ - UnicodeString prefix; - - /** - * The prefix of the escape form; often empty. - */ - UnicodeString suffix; - - /** - * The radix to display the number in. Typically 16 or 10. Must - * be in the range 2 to 36. - */ - int32_t radix; - - /** - * The minimum number of digits. Typically 1, 4, or 8. Values - * less than 1 are equivalent to 1. - */ - int32_t minDigits; - - /** - * If true, supplementals are handled as 32-bit code points. If - * false, they are handled as two 16-bit code units. - */ - UBool grokSupplementals; - - /** - * The form to be used for supplementals. If this is null then - * the same form is used for BMP characters and supplementals. If - * this is not null and if grokSupplementals is true then the - * prefix, suffix, radix, and minDigits of this object are used - * for supplementals. This pointer is owned. - */ - EscapeTransliterator* supplementalHandler; - - public: - - /** - * Registers standard variants with the system. Called by - * Transliterator during initialization. - */ - static void registerIDs(); - - /** - * Constructs an escape transliterator with the given ID and - * parameters. See the class member documentation for details. - */ - EscapeTransliterator(const UnicodeString& ID, - const UnicodeString& prefix, const UnicodeString& suffix, - int32_t radix, int32_t minDigits, - UBool grokSupplementals, - EscapeTransliterator* adoptedSupplementalHandler); - - /** - * Copy constructor. - */ - EscapeTransliterator(const EscapeTransliterator&); - - /** - * Destructor. - */ - virtual ~EscapeTransliterator(); - - /** - * Transliterator API. - */ - virtual EscapeTransliterator* clone() const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - virtual UClassID getDynamicClassID() const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); - - protected: - - /** - * Implements {@link Transliterator#handleTransliterate}. - */ - virtual void handleTransliterate(Replaceable& text, UTransPosition& offset, - UBool isIncremental) const override; - -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2001-2007, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/20/2001 aliu Creation. +********************************************************************** +*/ +#ifndef ESCTRN_H +#define ESCTRN_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/translit.h" + +U_NAMESPACE_BEGIN + +/** + * A transliterator that converts Unicode characters to an escape + * form. Examples of escape forms are "U+4E01" and "􏿿". + * Escape forms have a prefix and suffix, either of which may be + * empty, a radix, typically 16 or 10, a minimum digit count, + * typically 1, 4, or 8, and a boolean that specifies whether + * supplemental characters are handled as 32-bit code points or as two + * 16-bit code units. Most escape forms handle 32-bit code points, + * but some, such as the Java form, intentionally break them into two + * surrogate pairs, for backward compatibility. + * + *

Some escape forms actually have two different patterns, one for + * BMP characters (0..FFFF) and one for supplements (>FFFF). To + * handle this, a second EscapeTransliterator may be defined that + * specifies the pattern to be produced for supplementals. An example + * of a form that requires this is the C form, which uses "\\uFFFF" + * for BMP characters and "\\U0010FFFF" for supplementals. + * + *

This class is package private. It registers several standard + * variants with the system which are then accessed via their IDs. + * + * @author Alan Liu + */ +class EscapeTransliterator : public Transliterator { + + private: + + /** + * The prefix of the escape form; may be empty, but usually isn't. + */ + UnicodeString prefix; + + /** + * The prefix of the escape form; often empty. + */ + UnicodeString suffix; + + /** + * The radix to display the number in. Typically 16 or 10. Must + * be in the range 2 to 36. + */ + int32_t radix; + + /** + * The minimum number of digits. Typically 1, 4, or 8. Values + * less than 1 are equivalent to 1. + */ + int32_t minDigits; + + /** + * If true, supplementals are handled as 32-bit code points. If + * false, they are handled as two 16-bit code units. + */ + UBool grokSupplementals; + + /** + * The form to be used for supplementals. If this is null then + * the same form is used for BMP characters and supplementals. If + * this is not null and if grokSupplementals is true then the + * prefix, suffix, radix, and minDigits of this object are used + * for supplementals. This pointer is owned. + */ + EscapeTransliterator* supplementalHandler; + + public: + + /** + * Registers standard variants with the system. Called by + * Transliterator during initialization. + */ + static void registerIDs(); + + /** + * Constructs an escape transliterator with the given ID and + * parameters. See the class member documentation for details. + */ + EscapeTransliterator(const UnicodeString& ID, + const UnicodeString& prefix, const UnicodeString& suffix, + int32_t radix, int32_t minDigits, + UBool grokSupplementals, + EscapeTransliterator* adoptedSupplementalHandler); + + /** + * Copy constructor. + */ + EscapeTransliterator(const EscapeTransliterator&); + + /** + * Destructor. + */ + virtual ~EscapeTransliterator(); + + /** + * Transliterator API. + */ + virtual EscapeTransliterator* clone() const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + virtual UClassID getDynamicClassID() const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + + protected: + + /** + * Implements {@link Transliterator#handleTransliterate}. + */ + virtual void handleTransliterate(Replaceable& text, UTransPosition& offset, + UBool isIncremental) const override; + +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/ethpccal.cpp b/deps/icu-small/source/i18n/ethpccal.cpp index 07937872242c1a..bc9b19b21f9dc4 100644 --- a/deps/icu-small/source/i18n/ethpccal.cpp +++ b/deps/icu-small/source/i18n/ethpccal.cpp @@ -1,207 +1,259 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2003 - 2013, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "umutex.h" -#include "ethpccal.h" -#include "cecal.h" -#include - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(EthiopicCalendar) - -//static const int32_t JD_EPOCH_OFFSET_AMETE_ALEM = -285019; -static const int32_t JD_EPOCH_OFFSET_AMETE_MIHRET = 1723856; -static const int32_t AMETE_MIHRET_DELTA = 5500; // 5501 - 1 (Amete Alem 5501 = Amete Mihret 1) - -//------------------------------------------------------------------------- -// Constructors... -//------------------------------------------------------------------------- - -EthiopicCalendar::EthiopicCalendar(const Locale& aLocale, - UErrorCode& success, - EEraType type /*= AMETE_MIHRET_ERA*/) -: CECalendar(aLocale, success), - eraType(type) -{ -} - -EthiopicCalendar::EthiopicCalendar(const EthiopicCalendar& other) -: CECalendar(other), - eraType(other.eraType) -{ -} - -EthiopicCalendar::~EthiopicCalendar() -{ -} - -EthiopicCalendar* -EthiopicCalendar::clone() const -{ - return new EthiopicCalendar(*this); -} - -const char * -EthiopicCalendar::getType() const -{ - if (isAmeteAlemEra()) { - return "ethiopic-amete-alem"; - } - return "ethiopic"; -} - -void -EthiopicCalendar::setAmeteAlemEra(UBool onOff) -{ - eraType = onOff ? AMETE_ALEM_ERA : AMETE_MIHRET_ERA; -} - -UBool -EthiopicCalendar::isAmeteAlemEra() const -{ - return (eraType == AMETE_ALEM_ERA); -} - -//------------------------------------------------------------------------- -// Calendar framework -//------------------------------------------------------------------------- - -int32_t -EthiopicCalendar::handleGetExtendedYear() -{ - // Ethiopic calendar uses EXTENDED_YEAR aligned to - // Amelete Hihret year always. - int32_t eyear; - if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { - eyear = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 - } else if (isAmeteAlemEra()) { - eyear = internalGet(UCAL_YEAR, 1 + AMETE_MIHRET_DELTA) - - AMETE_MIHRET_DELTA; // Default to year 1 of Amelete Mihret - } else { - // The year defaults to the epoch start, the era to AMETE_MIHRET - int32_t era = internalGet(UCAL_ERA, AMETE_MIHRET); - if (era == AMETE_MIHRET) { - eyear = internalGet(UCAL_YEAR, 1); // Default to year 1 - } else { - eyear = internalGet(UCAL_YEAR, 1) - AMETE_MIHRET_DELTA; - } - } - return eyear; -} - -void -EthiopicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) -{ - int32_t eyear, month, day, era, year; - jdToCE(julianDay, getJDEpochOffset(), eyear, month, day); - - if (isAmeteAlemEra()) { - era = AMETE_ALEM; - year = eyear + AMETE_MIHRET_DELTA; - } else { - if (eyear > 0) { - era = AMETE_MIHRET; - year = eyear; - } else { - era = AMETE_ALEM; - year = eyear + AMETE_MIHRET_DELTA; - } - } - - internalSet(UCAL_EXTENDED_YEAR, eyear); - internalSet(UCAL_ERA, era); - internalSet(UCAL_YEAR, year); - internalSet(UCAL_MONTH, month); - internalSet(UCAL_DATE, day); - internalSet(UCAL_DAY_OF_YEAR, (30 * month) + day); -} - -int32_t -EthiopicCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const -{ - if (isAmeteAlemEra() && field == UCAL_ERA) { - return 0; // Only one era in this mode, era is always 0 - } - return CECalendar::handleGetLimit(field, limitType); -} - -/** - * The system maintains a static default century start date and Year. They are - * initialized the first time they are used. Once the system default century date - * and year are set, they do not change. - */ -static UDate gSystemDefaultCenturyStart = DBL_MIN; -static int32_t gSystemDefaultCenturyStartYear = -1; -static icu::UInitOnce gSystemDefaultCenturyInit {}; - -static void U_CALLCONV initializeSystemDefaultCentury() -{ - UErrorCode status = U_ZERO_ERROR; - EthiopicCalendar calendar(Locale("@calendar=ethiopic"), status); - if (U_SUCCESS(status)) { - calendar.setTime(Calendar::getNow(), status); - calendar.add(UCAL_YEAR, -80, status); - - gSystemDefaultCenturyStart = calendar.getTime(status); - gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); - } - // We have no recourse upon failure unless we want to propagate the failure - // out. -} - -UDate -EthiopicCalendar::defaultCenturyStart() const -{ - // lazy-evaluate systemDefaultCenturyStart - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStart; -} - -int32_t -EthiopicCalendar::defaultCenturyStartYear() const -{ - // lazy-evaluate systemDefaultCenturyStartYear - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - if (isAmeteAlemEra()) { - return gSystemDefaultCenturyStartYear + AMETE_MIHRET_DELTA; - } - return gSystemDefaultCenturyStartYear; -} - - -int32_t -EthiopicCalendar::getJDEpochOffset() const -{ - return JD_EPOCH_OFFSET_AMETE_MIHRET; -} - - -#if 0 -// We do not want to introduce this API in ICU4C. -// It was accidentally introduced in ICU4J as a public API. - -//------------------------------------------------------------------------- -// Calendar system Conversion methods... -//------------------------------------------------------------------------- - -int32_t -EthiopicCalendar::ethiopicToJD(int32_t year, int32_t month, int32_t date) -{ - return ceToJD(year, month, date, JD_EPOCH_OFFSET_AMETE_MIHRET); -} -#endif - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2003 - 2013, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "umutex.h" +#include "ethpccal.h" +#include "cecal.h" +#include + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(EthiopicCalendar) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(EthiopicAmeteAlemCalendar) + +//static const int32_t JD_EPOCH_OFFSET_AMETE_ALEM = -285019; +static const int32_t JD_EPOCH_OFFSET_AMETE_MIHRET = 1723856; +static const int32_t AMETE_MIHRET_DELTA = 5500; // 5501 - 1 (Amete Alem 5501 = Amete Mihret 1) + +//------------------------------------------------------------------------- +// Constructors... +//------------------------------------------------------------------------- + +EthiopicCalendar::EthiopicCalendar(const Locale& aLocale, + UErrorCode& success) +: CECalendar(aLocale, success) +{ +} + +EthiopicCalendar::~EthiopicCalendar() +{ +} + +EthiopicCalendar* +EthiopicCalendar::clone() const +{ + return new EthiopicCalendar(*this); +} + +const char * +EthiopicCalendar::getType() const +{ + return "ethiopic"; +} + +//------------------------------------------------------------------------- +// Calendar framework +//------------------------------------------------------------------------- + +int32_t +EthiopicCalendar::handleGetExtendedYear() +{ + // Ethiopic calendar uses EXTENDED_YEAR aligned to + // Amelete Hihret year always. + if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { + return internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 + } + // The year defaults to the epoch start, the era to AMETE_MIHRET + if (internalGet(UCAL_ERA, AMETE_MIHRET) == AMETE_MIHRET) { + return internalGet(UCAL_YEAR, 1); // Default to year 1 + } + return internalGet(UCAL_YEAR, 1) - AMETE_MIHRET_DELTA; +} + +void +EthiopicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) +{ + int32_t eyear, month, day; + jdToCE(julianDay, getJDEpochOffset(), eyear, month, day); + + internalSet(UCAL_EXTENDED_YEAR, eyear); + internalSet(UCAL_ERA, (eyear > 0) ? AMETE_MIHRET : AMETE_ALEM); + internalSet(UCAL_YEAR, (eyear > 0) ? eyear : (eyear + AMETE_MIHRET_DELTA)); + internalSet(UCAL_MONTH, month); + internalSet(UCAL_ORDINAL_MONTH, month); + internalSet(UCAL_DATE, day); + internalSet(UCAL_DAY_OF_YEAR, (30 * month) + day); +} + +constexpr uint32_t kEthiopicRelatedYearDiff = 8; + +int32_t EthiopicCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kEthiopicRelatedYearDiff; +} + +void EthiopicCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kEthiopicRelatedYearDiff); +} + +/** + * The system maintains a static default century start date and Year. They are + * initialized the first time they are used. Once the system default century date + * and year are set, they do not change. + */ +static UDate gSystemDefaultCenturyStart = DBL_MIN; +static int32_t gSystemDefaultCenturyStartYear = -1; +static icu::UInitOnce gSystemDefaultCenturyInit {}; + +static void U_CALLCONV initializeSystemDefaultCentury() +{ + UErrorCode status = U_ZERO_ERROR; + EthiopicCalendar calendar(Locale("@calendar=ethiopic"), status); + if (U_SUCCESS(status)) { + calendar.setTime(Calendar::getNow(), status); + calendar.add(UCAL_YEAR, -80, status); + + gSystemDefaultCenturyStart = calendar.getTime(status); + gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); + } + // We have no recourse upon failure unless we want to propagate the failure + // out. +} + +UDate +EthiopicCalendar::defaultCenturyStart() const +{ + // lazy-evaluate systemDefaultCenturyStart + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStart; +} + +int32_t +EthiopicCalendar::defaultCenturyStartYear() const +{ + // lazy-evaluate systemDefaultCenturyStartYear + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStartYear; +} + + +int32_t +EthiopicCalendar::getJDEpochOffset() const +{ + return JD_EPOCH_OFFSET_AMETE_MIHRET; +} + + +#if 0 +// We do not want to introduce this API in ICU4C. +// It was accidentally introduced in ICU4J as a public API. + +//------------------------------------------------------------------------- +// Calendar system Conversion methods... +//------------------------------------------------------------------------- + +int32_t +EthiopicCalendar::ethiopicToJD(int32_t year, int32_t month, int32_t date) +{ + return ceToJD(year, month, date, JD_EPOCH_OFFSET_AMETE_MIHRET); +} +#endif + +//------------------------------------------------------------------------- +// Constructors... +//------------------------------------------------------------------------- + +EthiopicAmeteAlemCalendar::EthiopicAmeteAlemCalendar(const Locale& aLocale, + UErrorCode& success) +: EthiopicCalendar(aLocale, success) +{ +} + +EthiopicAmeteAlemCalendar::~EthiopicAmeteAlemCalendar() +{ +} + +EthiopicAmeteAlemCalendar* +EthiopicAmeteAlemCalendar::clone() const +{ + return new EthiopicAmeteAlemCalendar(*this); +} + +//------------------------------------------------------------------------- +// Calendar framework +//------------------------------------------------------------------------- + +const char * +EthiopicAmeteAlemCalendar::getType() const +{ + return "ethiopic-amete-alem"; +} + +int32_t +EthiopicAmeteAlemCalendar::handleGetExtendedYear() +{ + // Ethiopic calendar uses EXTENDED_YEAR aligned to + // Amelete Hihret year always. + if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { + return internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 + } + return internalGet(UCAL_YEAR, 1 + AMETE_MIHRET_DELTA) + - AMETE_MIHRET_DELTA; // Default to year 1 of Amelete Mihret +} + +void +EthiopicAmeteAlemCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) +{ + int32_t eyear, month, day; + jdToCE(julianDay, getJDEpochOffset(), eyear, month, day); + + internalSet(UCAL_EXTENDED_YEAR, eyear); + internalSet(UCAL_ERA, AMETE_ALEM); + internalSet(UCAL_YEAR, eyear + AMETE_MIHRET_DELTA); + internalSet(UCAL_MONTH, month); + internalSet(UCAL_ORDINAL_MONTH, month); + internalSet(UCAL_DATE, day); + internalSet(UCAL_DAY_OF_YEAR, (30 * month) + day); +} + +int32_t +EthiopicAmeteAlemCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const +{ + if (field == UCAL_ERA) { + return 0; // Only one era in this mode, era is always 0 + } + return EthiopicCalendar::handleGetLimit(field, limitType); +} + +constexpr uint32_t kEthiopicAmeteAlemRelatedYearDiff = -5492; + +int32_t EthiopicAmeteAlemCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kEthiopicAmeteAlemRelatedYearDiff; +} + +void EthiopicAmeteAlemCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kEthiopicAmeteAlemRelatedYearDiff); +} + +int32_t +EthiopicAmeteAlemCalendar::defaultCenturyStartYear() const +{ + return EthiopicCalendar::defaultCenturyStartYear() + AMETE_MIHRET_DELTA; +} + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/i18n/ethpccal.h b/deps/icu-small/source/i18n/ethpccal.h index 0cc5b6c535ec3c..9ccb8be6b33e72 100644 --- a/deps/icu-small/source/i18n/ethpccal.h +++ b/deps/icu-small/source/i18n/ethpccal.h @@ -1,272 +1,367 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2003 - 2013, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - -#ifndef ETHPCCAL_H -#define ETHPCCAL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/calendar.h" -#include "cecal.h" - -U_NAMESPACE_BEGIN - -/** - * Implement the Ethiopic calendar system. - * @internal - */ -class EthiopicCalendar : public CECalendar { - -public: - /** - * Calendar type - use Amete Alem era for all the time or not - * @internal - */ - enum EEraType { - AMETE_MIHRET_ERA, - AMETE_ALEM_ERA - }; - - /** - * Useful constants for EthiopicCalendar. - * @internal - */ - enum EMonths { - /** - * Constant for መስከረም, the 1st month of the Ethiopic year. - */ - MESKEREM, - - /** - * Constant for ጥቅምት, the 2nd month of the Ethiopic year. - */ - TEKEMT, - - /** - * Constant for ኅዳር, the 3rd month of the Ethiopic year. - */ - HEDAR, - - /** - * Constant for ታኅሣሥ, the 4th month of the Ethiopic year. - */ - TAHSAS, - - /** - * Constant for ጥር, the 5th month of the Ethiopic year. - */ - TER, - - /** - * Constant for የካቲት, the 6th month of the Ethiopic year. - */ - YEKATIT, - - /** - * Constant for መጋቢት, the 7th month of the Ethiopic year. - */ - MEGABIT, - - /** - * Constant for ሚያዝያ, the 8th month of the Ethiopic year. - */ - MIAZIA, - - /** - * Constant for ግንቦት, the 9th month of the Ethiopic year. - */ - GENBOT, - - /** - * Constant for ሰኔ, the 10th month of the Ethiopic year. - */ - SENE, - - /** - * Constant for ሐምሌ, the 11th month of the Ethiopic year. - */ - HAMLE, - - /** - * Constant for ነሐሴ, the 12th month of the Ethiopic year. - */ - NEHASSA, - - /** - * Constant for ጳጉሜን, the 13th month of the Ethiopic year. - */ - PAGUMEN - }; - - enum EEras { - AMETE_ALEM, // Before the epoch - AMETE_MIHRET // After the epoch - }; - - /** - * Constructs a EthiopicCalendar based on the current time in the default time zone - * with the given locale. - * - * @param aLocale The given locale. - * @param success Indicates the status of EthiopicCalendar object construction. - * Returns U_ZERO_ERROR if constructed successfully. - * @param type Whether this Ethiopic calendar use Amete Mihrret (default) or - * only use Amete Alem for all the time. - * @internal - */ - EthiopicCalendar(const Locale& aLocale, UErrorCode& success, EEraType type = AMETE_MIHRET_ERA); - - /** - * Copy Constructor - * @internal - */ - EthiopicCalendar(const EthiopicCalendar& other); - - /** - * Destructor. - * @internal - */ - virtual ~EthiopicCalendar(); - - /** - * Create and return a polymorphic copy of this calendar. - * @return return a polymorphic copy of this calendar. - * @internal - */ - virtual EthiopicCalendar* clone() const override; - - /** - * return the calendar type, "ethiopic" - * @return calendar type - * @internal - */ - virtual const char * getType() const override; - - /** - * Set Alem or Mihret era. - * @param onOff Set Amete Alem era if true, otherwise set Amete Mihret era. - * @internal - */ - void setAmeteAlemEra (UBool onOff); - - /** - * Return true if this calendar is set to the Amete Alem era. - * @return true if set to the Amete Alem era. - * @internal - */ - UBool isAmeteAlemEra() const; - -protected: - //------------------------------------------------------------------------- - // Calendar framework - //------------------------------------------------------------------------- - - /** - * Return the extended year defined by the current fields. - * @internal - */ - virtual int32_t handleGetExtendedYear() override; - - /** - * Compute fields from the JD - * @internal - */ - virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; - - /** - * Calculate the limit for a specified type of limit and field - * @internal - */ - virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; - - /** - * Returns the date of the start of the default century - * @return start of century - in milliseconds since epoch, 1970 - * @internal - */ - virtual UDate defaultCenturyStart() const override; - - /** - * Returns the year in which the default century begins - * @internal - */ - virtual int32_t defaultCenturyStartYear() const override; - - /** - * Return the date offset from Julian - * @internal - */ - virtual int32_t getJDEpochOffset() const override; - -private: - /** - * When eraType is AMETE_ALEM_ERA, then this calendar use only AMETE_ALEM - * for the era. Otherwise (default), this calendar uses both AMETE_ALEM - * and AMETE_MIHRET. - * - * EXTENDED_YEAR AMETE_ALEM_ERA AMETE_MIHRET_ERA - * 0 Amete Alem 5500 Amete Alem 5500 - * 1 Amete Mihret 1 Amete Alem 5501 - */ - EEraType eraType; - -public: - /** - * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual - * override. This method is to implement a simple version of RTTI, since not all C++ - * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call - * this method. - * - * @return The class ID for this object. All objects of a given class have the - * same class ID. Objects of other classes have different class IDs. - * @internal - */ - virtual UClassID getDynamicClassID(void) const override; - - /** - * Return the class ID for this class. This is useful only for comparing to a return - * value from getDynamicClassID(). For example: - * - * Base* polymorphic_pointer = createPolymorphicObject(); - * if (polymorphic_pointer->getDynamicClassID() == - * Derived::getStaticClassID()) ... - * - * @return The class ID for all objects of this class. - * @internal - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(void); - -#if 0 -// We do not want to introduce this API in ICU4C. -// It was accidentally introduced in ICU4J as a public API. - -public: - //------------------------------------------------------------------------- - // Calendar system Conversion methods... - //------------------------------------------------------------------------- - - /** - * Convert an Ethiopic year, month, and day to a Julian day. - * - * @param year the extended year - * @param month the month - * @param day the day - * @return Julian day - * @internal - */ - int32_t ethiopicToJD(int32_t year, int32_t month, int32_t day); -#endif -}; - -U_NAMESPACE_END -#endif /* #if !UCONFIG_NO_FORMATTING */ -#endif /* ETHPCCAL_H */ -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2003 - 2013, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +*/ + +#ifndef ETHPCCAL_H +#define ETHPCCAL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" +#include "cecal.h" + +U_NAMESPACE_BEGIN + +/** + * Implement the Ethiopic calendar system. + * @internal + */ +class EthiopicCalendar : public CECalendar { + +public: + /** + * Useful constants for EthiopicCalendar. + * @internal + */ + enum EMonths { + /** + * Constant for መስከረም, the 1st month of the Ethiopic year. + */ + MESKEREM, + + /** + * Constant for ጥቅምት, the 2nd month of the Ethiopic year. + */ + TEKEMT, + + /** + * Constant for ኅዳር, the 3rd month of the Ethiopic year. + */ + HEDAR, + + /** + * Constant for ታኅሣሥ, the 4th month of the Ethiopic year. + */ + TAHSAS, + + /** + * Constant for ጥር, the 5th month of the Ethiopic year. + */ + TER, + + /** + * Constant for የካቲት, the 6th month of the Ethiopic year. + */ + YEKATIT, + + /** + * Constant for መጋቢት, the 7th month of the Ethiopic year. + */ + MEGABIT, + + /** + * Constant for ሚያዝያ, the 8th month of the Ethiopic year. + */ + MIAZIA, + + /** + * Constant for ግንቦት, the 9th month of the Ethiopic year. + */ + GENBOT, + + /** + * Constant for ሰኔ, the 10th month of the Ethiopic year. + */ + SENE, + + /** + * Constant for ሐምሌ, the 11th month of the Ethiopic year. + */ + HAMLE, + + /** + * Constant for ነሐሴ, the 12th month of the Ethiopic year. + */ + NEHASSA, + + /** + * Constant for ጳጉሜን, the 13th month of the Ethiopic year. + */ + PAGUMEN + }; + + enum EEras { + AMETE_ALEM, // Before the epoch + AMETE_MIHRET // After the epoch + }; + + /** + * Constructs a EthiopicCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of EthiopicCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @param type Whether this Ethiopic calendar use Amete Mihrret (default) or + * only use Amete Alem for all the time. + * @internal + */ + EthiopicCalendar(const Locale& aLocale, UErrorCode& success); + + /** + * Copy Constructor + * @internal + */ + EthiopicCalendar(const EthiopicCalendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~EthiopicCalendar(); + + /** + * Create and return a polymorphic copy of this calendar. + * @return return a polymorphic copy of this calendar. + * @internal + */ + virtual EthiopicCalendar* clone() const override; + + /** + * Return the calendar type, "ethiopic" + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + /** + * @return The related Gregorian year; will be obtained by modifying the value + * obtained by get from UCAL_EXTENDED_YEAR field + * @internal + */ + virtual int32_t getRelatedYear(UErrorCode &status) const override; + + /** + * @param year The related Gregorian year to set; will be modified as necessary then + * set in UCAL_EXTENDED_YEAR field + * @internal + */ + virtual void setRelatedYear(int32_t year) override; + +protected: + //------------------------------------------------------------------------- + // Calendar framework + //------------------------------------------------------------------------- + + /** + * Return the extended year defined by the current fields. + * This calendar uses both AMETE_ALEM and AMETE_MIHRET. + * + * EXTENDED_YEAR ERA YEAR + * 0 AMETE_ALEM 5500 + * 1 AMETE_MIHRET 1 + * @internal + */ + virtual int32_t handleGetExtendedYear() override; + + /** + * Compute fields from the JD + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; + + /** + * Returns the date of the start of the default century + * @return start of century - in milliseconds since epoch, 1970 + * @internal + */ + virtual UDate defaultCenturyStart() const override; + + /** + * Returns the year in which the default century begins + * @internal + */ + virtual int32_t defaultCenturyStartYear() const override; + + /** + * Return the date offset from Julian + * @internal + */ + virtual int32_t getJDEpochOffset() const override; + +public: + /** + * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual + * override. This method is to implement a simple version of RTTI, since not all C++ + * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call + * this method. + * + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + +#if 0 +// We do not want to introduce this API in ICU4C. +// It was accidentally introduced in ICU4J as a public API. + +public: + //------------------------------------------------------------------------- + // Calendar system Conversion methods... + //------------------------------------------------------------------------- + + /** + * Convert an Ethiopic year, month, and day to a Julian day. + * + * @param year the extended year + * @param month the month + * @param day the day + * @return Julian day + * @internal + */ + int32_t ethiopicToJD(int32_t year, int32_t month, int32_t day); +#endif +}; + +/** + * Implement the Ethiopic Amete Alem calendar system. + * @internal + */ +class EthiopicAmeteAlemCalendar : public EthiopicCalendar { + +public: + /** + * Constructs a EthiopicAmeteAlemCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of EthiopicCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + EthiopicAmeteAlemCalendar(const Locale& aLocale, UErrorCode& success); + + /** + * Copy Constructor + * @internal + */ + EthiopicAmeteAlemCalendar(const EthiopicAmeteAlemCalendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~EthiopicAmeteAlemCalendar(); + + /** + * Create and return a polymorphic copy of this calendar. + * @return return a polymorphic copy of this calendar. + * @internal + */ + virtual EthiopicAmeteAlemCalendar* clone() const override; + + /** + * Return the calendar type, "ethiopic-amete-alem" + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + /** + * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual + * override. This method is to implement a simple version of RTTI, since not all C++ + * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call + * this method. + * + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + + /** + * @return The related Gregorian year; will be obtained by modifying the value + * obtained by get from UCAL_EXTENDED_YEAR field + * @internal + */ + virtual int32_t getRelatedYear(UErrorCode &status) const override; + + /** + * @param year The related Gregorian year to set; will be modified as necessary then + * set in UCAL_EXTENDED_YEAR field + * @internal + */ + virtual void setRelatedYear(int32_t year) override; + +protected: + //------------------------------------------------------------------------- + // Calendar framework + //------------------------------------------------------------------------- + + /** + * Return the extended year defined by the current fields. + * This calendar use only AMETE_ALEM for the era. + * + * EXTENDED_YEAR ERA YEAR + * 0 AMETE_ALEM 5500 + * 1 AMETE_ALEM 5501 + * @internal + */ + virtual int32_t handleGetExtendedYear() override; + + /** + * Compute fields from the JD + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; + + /** + * Calculate the limit for a specified type of limit and field + * @internal + */ + virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; + /** + * Returns the year in which the default century begins + * @internal + */ + virtual int32_t defaultCenturyStartYear() const override; +}; + +U_NAMESPACE_END +#endif /* #if !UCONFIG_NO_FORMATTING */ +#endif /* ETHPCCAL_H */ +//eof diff --git a/deps/icu-small/source/i18n/fmtable.cpp b/deps/icu-small/source/i18n/fmtable.cpp index c3ede98328e200..de13f9cc9cbf75 100644 --- a/deps/icu-small/source/i18n/fmtable.cpp +++ b/deps/icu-small/source/i18n/fmtable.cpp @@ -1,1042 +1,1042 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File FMTABLE.CPP -* -* Modification History: -* -* Date Name Description -* 03/25/97 clhuang Initial Implementation. -******************************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include -#include -#include "unicode/fmtable.h" -#include "unicode/ustring.h" -#include "unicode/measure.h" -#include "unicode/curramt.h" -#include "unicode/uformattable.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "fmtableimp.h" -#include "number_decimalquantity.h" - -// ***************************************************************************** -// class Formattable -// ***************************************************************************** - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Formattable) - -using number::impl::DecimalQuantity; - - -//-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. - -// NOTE: As of 3.0, there are limitations to the UObject API. It does -// not (yet) support cloning, operator=, nor operator==. To -// work around this, I implement some simple inlines here. Later -// these can be modified or removed. [alan] - -// NOTE: These inlines assume that all fObjects are in fact instances -// of the Measure class, which is true as of 3.0. [alan] - -// Return true if *a == *b. -static inline UBool objectEquals(const UObject* a, const UObject* b) { - // LATER: return *a == *b; - return *((const Measure*) a) == *((const Measure*) b); -} - -// Return a clone of *a. -static inline UObject* objectClone(const UObject* a) { - // LATER: return a->clone(); - return ((const Measure*) a)->clone(); -} - -// Return true if *a is an instance of Measure. -static inline UBool instanceOfMeasure(const UObject* a) { - return dynamic_cast(a) != NULL; -} - -/** - * Creates a new Formattable array and copies the values from the specified - * original. - * @param array the original array - * @param count the original array count - * @return the new Formattable array. - */ -static Formattable* createArrayCopy(const Formattable* array, int32_t count) { - Formattable *result = new Formattable[count]; - if (result != NULL) { - for (int32_t i=0; i INT32_MAX) { - status = U_INVALID_FORMAT_ERROR; - return INT32_MAX; - } else if (fValue.fInt64 < INT32_MIN) { - status = U_INVALID_FORMAT_ERROR; - return INT32_MIN; - } else { - return (int32_t)fValue.fInt64; - } - case Formattable::kDouble: - if (fValue.fDouble > INT32_MAX) { - status = U_INVALID_FORMAT_ERROR; - return INT32_MAX; - } else if (fValue.fDouble < INT32_MIN) { - status = U_INVALID_FORMAT_ERROR; - return INT32_MIN; - } else { - return (int32_t)fValue.fDouble; // loses fraction - } - case Formattable::kObject: - if (fValue.fObject == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - // TODO Later replace this with instanceof call - if (instanceOfMeasure(fValue.fObject)) { - return ((const Measure*) fValue.fObject)-> - getNumber().getLong(status); - } - U_FALLTHROUGH; - default: - status = U_INVALID_FORMAT_ERROR; - return 0; - } -} - -// ------------------------------------- -// Maximum int that can be represented exactly in a double. (53 bits) -// Larger ints may be rounded to a near-by value as not all are representable. -// TODO: move this constant elsewhere, possibly configure it for different -// floating point formats, if any non-standard ones are still in use. -static const int64_t U_DOUBLE_MAX_EXACT_INT = 9007199254740992LL; - -int64_t -Formattable::getInt64(UErrorCode& status) const -{ - if (U_FAILURE(status)) { - return 0; - } - - switch (fType) { - case Formattable::kLong: - case Formattable::kInt64: - return fValue.fInt64; - case Formattable::kDouble: - if (fValue.fDouble > (double)U_INT64_MAX) { - status = U_INVALID_FORMAT_ERROR; - return U_INT64_MAX; - } else if (fValue.fDouble < (double)U_INT64_MIN) { - status = U_INVALID_FORMAT_ERROR; - return U_INT64_MIN; - } else if (fabs(fValue.fDouble) > U_DOUBLE_MAX_EXACT_INT && fDecimalQuantity != NULL) { - if (fDecimalQuantity->fitsInLong(true)) { - return fDecimalQuantity->toLong(); - } else { - // Unexpected - status = U_INVALID_FORMAT_ERROR; - return fDecimalQuantity->isNegative() ? U_INT64_MIN : U_INT64_MAX; - } - } else { - return (int64_t)fValue.fDouble; - } - case Formattable::kObject: - if (fValue.fObject == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - if (instanceOfMeasure(fValue.fObject)) { - return ((const Measure*) fValue.fObject)-> - getNumber().getInt64(status); - } - U_FALLTHROUGH; - default: - status = U_INVALID_FORMAT_ERROR; - return 0; - } -} - -// ------------------------------------- -double -Formattable::getDouble(UErrorCode& status) const -{ - if (U_FAILURE(status)) { - return 0; - } - - switch (fType) { - case Formattable::kLong: - case Formattable::kInt64: // loses precision - return (double)fValue.fInt64; - case Formattable::kDouble: - return fValue.fDouble; - case Formattable::kObject: - if (fValue.fObject == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - // TODO Later replace this with instanceof call - if (instanceOfMeasure(fValue.fObject)) { - return ((const Measure*) fValue.fObject)-> - getNumber().getDouble(status); - } - U_FALLTHROUGH; - default: - status = U_INVALID_FORMAT_ERROR; - return 0; - } -} - -const UObject* -Formattable::getObject() const { - return (fType == kObject) ? fValue.fObject : NULL; -} - -// ------------------------------------- -// Sets the value to a double value d. - -void -Formattable::setDouble(double d) -{ - dispose(); - fType = kDouble; - fValue.fDouble = d; -} - -// ------------------------------------- -// Sets the value to a long value l. - -void -Formattable::setLong(int32_t l) -{ - dispose(); - fType = kLong; - fValue.fInt64 = l; -} - -// ------------------------------------- -// Sets the value to an int64 value ll. - -void -Formattable::setInt64(int64_t ll) -{ - dispose(); - fType = kInt64; - fValue.fInt64 = ll; -} - -// ------------------------------------- -// Sets the value to a Date instance d. - -void -Formattable::setDate(UDate d) -{ - dispose(); - fType = kDate; - fValue.fDate = d; -} - -// ------------------------------------- -// Sets the value to a string value stringToCopy. - -void -Formattable::setString(const UnicodeString& stringToCopy) -{ - dispose(); - fType = kString; - fValue.fString = new UnicodeString(stringToCopy); -} - -// ------------------------------------- -// Sets the value to an array of Formattable objects. - -void -Formattable::setArray(const Formattable* array, int32_t count) -{ - dispose(); - fType = kArray; - fValue.fArrayAndCount.fArray = createArrayCopy(array, count); - fValue.fArrayAndCount.fCount = count; -} - -// ------------------------------------- -// Adopts the stringToAdopt value. - -void -Formattable::adoptString(UnicodeString* stringToAdopt) -{ - dispose(); - fType = kString; - fValue.fString = stringToAdopt; -} - -// ------------------------------------- -// Adopts the array value and its count. - -void -Formattable::adoptArray(Formattable* array, int32_t count) -{ - dispose(); - fType = kArray; - fValue.fArrayAndCount.fArray = array; - fValue.fArrayAndCount.fCount = count; -} - -void -Formattable::adoptObject(UObject* objectToAdopt) { - dispose(); - fType = kObject; - fValue.fObject = objectToAdopt; -} - -// ------------------------------------- -UnicodeString& -Formattable::getString(UnicodeString& result, UErrorCode& status) const -{ - if (fType != kString) { - setError(status, U_INVALID_FORMAT_ERROR); - result.setToBogus(); - } else { - if (fValue.fString == NULL) { - setError(status, U_MEMORY_ALLOCATION_ERROR); - } else { - result = *fValue.fString; - } - } - return result; -} - -// ------------------------------------- -const UnicodeString& -Formattable::getString(UErrorCode& status) const -{ - if (fType != kString) { - setError(status, U_INVALID_FORMAT_ERROR); - return *getBogus(); - } - if (fValue.fString == NULL) { - setError(status, U_MEMORY_ALLOCATION_ERROR); - return *getBogus(); - } - return *fValue.fString; -} - -// ------------------------------------- -UnicodeString& -Formattable::getString(UErrorCode& status) -{ - if (fType != kString) { - setError(status, U_INVALID_FORMAT_ERROR); - return *getBogus(); - } - if (fValue.fString == NULL) { - setError(status, U_MEMORY_ALLOCATION_ERROR); - return *getBogus(); - } - return *fValue.fString; -} - -// ------------------------------------- -const Formattable* -Formattable::getArray(int32_t& count, UErrorCode& status) const -{ - if (fType != kArray) { - setError(status, U_INVALID_FORMAT_ERROR); - count = 0; - return NULL; - } - count = fValue.fArrayAndCount.fCount; - return fValue.fArrayAndCount.fArray; -} - -// ------------------------------------- -// Gets the bogus string, ensures mondo bogosity. - -UnicodeString* -Formattable::getBogus() const -{ - return (UnicodeString*)&fBogus; /* cast away const :-( */ -} - - -// -------------------------------------- -StringPiece Formattable::getDecimalNumber(UErrorCode &status) { - if (U_FAILURE(status)) { - return ""; - } - if (fDecimalStr != NULL) { - return fDecimalStr->toStringPiece(); - } - - CharString *decimalStr = internalGetCharString(status); - if(decimalStr == NULL) { - return ""; // getDecimalNumber returns "" for error cases - } else { - return decimalStr->toStringPiece(); - } -} - -CharString *Formattable::internalGetCharString(UErrorCode &status) { - if(fDecimalStr == NULL) { - if (fDecimalQuantity == NULL) { - // No decimal number for the formattable yet. Which means the value was - // set directly by the user as an int, int64 or double. If the value came - // from parsing, or from the user setting a decimal number, fDecimalNum - // would already be set. - // - LocalPointer dq(new DecimalQuantity(), status); - if (U_FAILURE(status)) { return nullptr; } - populateDecimalQuantity(*dq, status); - if (U_FAILURE(status)) { return nullptr; } - fDecimalQuantity = dq.orphan(); - } - - fDecimalStr = new CharString(); - if (fDecimalStr == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - // Older ICUs called uprv_decNumberToString here, which is not exactly the same as - // DecimalQuantity::toScientificString(). The biggest difference is that uprv_decNumberToString does - // not print scientific notation for magnitudes greater than -5 and smaller than some amount (+5?). - if (fDecimalQuantity->isInfinite()) { - fDecimalStr->append("Infinity", status); - } else if (fDecimalQuantity->isNaN()) { - fDecimalStr->append("NaN", status); - } else if (fDecimalQuantity->isZeroish()) { - fDecimalStr->append("0", -1, status); - } else if (fType==kLong || fType==kInt64 || // use toPlainString for integer types - (fDecimalQuantity->getMagnitude() != INT32_MIN && std::abs(fDecimalQuantity->getMagnitude()) < 5)) { - fDecimalStr->appendInvariantChars(fDecimalQuantity->toPlainString(), status); - } else { - fDecimalStr->appendInvariantChars(fDecimalQuantity->toScientificString(), status); - } - } - return fDecimalStr; -} - -void -Formattable::populateDecimalQuantity(number::impl::DecimalQuantity& output, UErrorCode& status) const { - if (fDecimalQuantity != nullptr) { - output = *fDecimalQuantity; - return; - } - - switch (fType) { - case kDouble: - output.setToDouble(this->getDouble()); - output.roundToInfinity(); - break; - case kLong: - output.setToInt(this->getLong()); - break; - case kInt64: - output.setToLong(this->getInt64()); - break; - default: - // The formattable's value is not a numeric type. - status = U_INVALID_STATE_ERROR; - } -} - -// --------------------------------------- -void -Formattable::adoptDecimalQuantity(DecimalQuantity *dq) { - if (fDecimalQuantity != NULL) { - delete fDecimalQuantity; - } - fDecimalQuantity = dq; - if (dq == NULL) { // allow adoptDigitList(NULL) to clear - return; - } - - // Set the value into the Union of simple type values. - // Cannot use the set() functions because they would delete the fDecimalNum value. - if (fDecimalQuantity->fitsInLong()) { - fValue.fInt64 = fDecimalQuantity->toLong(); - if (fValue.fInt64 <= INT32_MAX && fValue.fInt64 >= INT32_MIN) { - fType = kLong; - } else { - fType = kInt64; - } - } else { - fType = kDouble; - fValue.fDouble = fDecimalQuantity->toDouble(); - } -} - - -// --------------------------------------- -void -Formattable::setDecimalNumber(StringPiece numberString, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - dispose(); - - auto* dq = new DecimalQuantity(); - dq->setToDecNumber(numberString, status); - adoptDecimalQuantity(dq); - - // Note that we do not hang on to the caller's input string. - // If we are asked for the string, we will regenerate one from fDecimalQuantity. -} - -#if 0 -//---------------------------------------------------- -// console I/O -//---------------------------------------------------- -#ifdef _DEBUG - -#include -using namespace std; - -#include "unicode/datefmt.h" -#include "unistrm.h" - -class FormattableStreamer /* not : public UObject because all methods are static */ { -public: - static void streamOut(ostream& stream, const Formattable& obj); - -private: - FormattableStreamer() {} // private - forbid instantiation -}; - -// This is for debugging purposes only. This will send a displayable -// form of the Formattable object to the output stream. - -void -FormattableStreamer::streamOut(ostream& stream, const Formattable& obj) -{ - static DateFormat *defDateFormat = 0; - - UnicodeString buffer; - switch(obj.getType()) { - case Formattable::kDate : - // Creates a DateFormat instance for formatting the - // Date instance. - if (defDateFormat == 0) { - defDateFormat = DateFormat::createInstance(); - } - defDateFormat->format(obj.getDate(), buffer); - stream << buffer; - break; - case Formattable::kDouble : - // Output the double as is. - stream << obj.getDouble() << 'D'; - break; - case Formattable::kLong : - // Output the double as is. - stream << obj.getLong() << 'L'; - break; - case Formattable::kString: - // Output the double as is. Please see UnicodeString console - // I/O routine for more details. - stream << '"' << obj.getString(buffer) << '"'; - break; - case Formattable::kArray: - int32_t i, count; - const Formattable* array; - array = obj.getArray(count); - stream << '['; - // Recursively calling the console I/O routine for each element in the array. - for (i=0; itoUFormattable(); - - if( fmt == NULL ) { - *status = U_MEMORY_ALLOCATION_ERROR; - } - return fmt; -} - -U_CAPI void U_EXPORT2 -ufmt_close(UFormattable *fmt) { - Formattable *obj = Formattable::fromUFormattable(fmt); - - delete obj; -} - -U_CAPI UFormattableType U_EXPORT2 -ufmt_getType(const UFormattable *fmt, UErrorCode *status) { - if(U_FAILURE(*status)) { - return (UFormattableType)UFMT_COUNT; - } - const Formattable *obj = Formattable::fromUFormattable(fmt); - return (UFormattableType)obj->getType(); -} - - -U_CAPI UBool U_EXPORT2 -ufmt_isNumeric(const UFormattable *fmt) { - const Formattable *obj = Formattable::fromUFormattable(fmt); - return obj->isNumeric(); -} - -U_CAPI UDate U_EXPORT2 -ufmt_getDate(const UFormattable *fmt, UErrorCode *status) { - const Formattable *obj = Formattable::fromUFormattable(fmt); - - return obj->getDate(*status); -} - -U_CAPI double U_EXPORT2 -ufmt_getDouble(UFormattable *fmt, UErrorCode *status) { - Formattable *obj = Formattable::fromUFormattable(fmt); - - return obj->getDouble(*status); -} - -U_CAPI int32_t U_EXPORT2 -ufmt_getLong(UFormattable *fmt, UErrorCode *status) { - Formattable *obj = Formattable::fromUFormattable(fmt); - - return obj->getLong(*status); -} - - -U_CAPI const void *U_EXPORT2 -ufmt_getObject(const UFormattable *fmt, UErrorCode *status) { - const Formattable *obj = Formattable::fromUFormattable(fmt); - - const void *ret = obj->getObject(); - if( ret==NULL && - (obj->getType() != Formattable::kObject) && - U_SUCCESS( *status )) { - *status = U_INVALID_FORMAT_ERROR; - } - return ret; -} - -U_CAPI const UChar* U_EXPORT2 -ufmt_getUChars(UFormattable *fmt, int32_t *len, UErrorCode *status) { - Formattable *obj = Formattable::fromUFormattable(fmt); - - // avoid bogosity by checking the type first. - if( obj->getType() != Formattable::kString ) { - if( U_SUCCESS(*status) ){ - *status = U_INVALID_FORMAT_ERROR; - } - return NULL; - } - - // This should return a valid string - UnicodeString &str = obj->getString(*status); - if( U_SUCCESS(*status) && len != NULL ) { - *len = str.length(); - } - return str.getTerminatedBuffer(); -} - -U_CAPI int32_t U_EXPORT2 -ufmt_getArrayLength(const UFormattable* fmt, UErrorCode *status) { - const Formattable *obj = Formattable::fromUFormattable(fmt); - - int32_t count; - (void)obj->getArray(count, *status); - return count; -} - -U_CAPI UFormattable * U_EXPORT2 -ufmt_getArrayItemByIndex(UFormattable* fmt, int32_t n, UErrorCode *status) { - Formattable *obj = Formattable::fromUFormattable(fmt); - int32_t count; - (void)obj->getArray(count, *status); - if(U_FAILURE(*status)) { - return NULL; - } else if(n<0 || n>=count) { - setError(*status, U_INDEX_OUTOFBOUNDS_ERROR); - return NULL; - } else { - return (*obj)[n].toUFormattable(); // returns non-const Formattable - } -} - -U_CAPI const char * U_EXPORT2 -ufmt_getDecNumChars(UFormattable *fmt, int32_t *len, UErrorCode *status) { - if(U_FAILURE(*status)) { - return ""; - } - Formattable *obj = Formattable::fromUFormattable(fmt); - CharString *charString = obj->internalGetCharString(*status); - if(U_FAILURE(*status)) { - return ""; - } - if(charString == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return ""; - } else { - if(len!=NULL) { - *len = charString->length(); - } - return charString->data(); - } -} - -U_CAPI int64_t U_EXPORT2 -ufmt_getInt64(UFormattable *fmt, UErrorCode *status) { - Formattable *obj = Formattable::fromUFormattable(fmt); - return obj->getInt64(*status); -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1997-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File FMTABLE.CPP +* +* Modification History: +* +* Date Name Description +* 03/25/97 clhuang Initial Implementation. +******************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include +#include +#include "unicode/fmtable.h" +#include "unicode/ustring.h" +#include "unicode/measure.h" +#include "unicode/curramt.h" +#include "unicode/uformattable.h" +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "fmtableimp.h" +#include "number_decimalquantity.h" + +// ***************************************************************************** +// class Formattable +// ***************************************************************************** + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Formattable) + +using number::impl::DecimalQuantity; + + +//-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. + +// NOTE: As of 3.0, there are limitations to the UObject API. It does +// not (yet) support cloning, operator=, nor operator==. To +// work around this, I implement some simple inlines here. Later +// these can be modified or removed. [alan] + +// NOTE: These inlines assume that all fObjects are in fact instances +// of the Measure class, which is true as of 3.0. [alan] + +// Return true if *a == *b. +static inline UBool objectEquals(const UObject* a, const UObject* b) { + // LATER: return *a == *b; + return *((const Measure*) a) == *((const Measure*) b); +} + +// Return a clone of *a. +static inline UObject* objectClone(const UObject* a) { + // LATER: return a->clone(); + return ((const Measure*) a)->clone(); +} + +// Return true if *a is an instance of Measure. +static inline UBool instanceOfMeasure(const UObject* a) { + return dynamic_cast(a) != nullptr; +} + +/** + * Creates a new Formattable array and copies the values from the specified + * original. + * @param array the original array + * @param count the original array count + * @return the new Formattable array. + */ +static Formattable* createArrayCopy(const Formattable* array, int32_t count) { + Formattable *result = new Formattable[count]; + if (result != nullptr) { + for (int32_t i=0; i INT32_MAX) { + status = U_INVALID_FORMAT_ERROR; + return INT32_MAX; + } else if (fValue.fInt64 < INT32_MIN) { + status = U_INVALID_FORMAT_ERROR; + return INT32_MIN; + } else { + return (int32_t)fValue.fInt64; + } + case Formattable::kDouble: + if (fValue.fDouble > INT32_MAX) { + status = U_INVALID_FORMAT_ERROR; + return INT32_MAX; + } else if (fValue.fDouble < INT32_MIN) { + status = U_INVALID_FORMAT_ERROR; + return INT32_MIN; + } else { + return (int32_t)fValue.fDouble; // loses fraction + } + case Formattable::kObject: + if (fValue.fObject == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + // TODO Later replace this with instanceof call + if (instanceOfMeasure(fValue.fObject)) { + return ((const Measure*) fValue.fObject)-> + getNumber().getLong(status); + } + U_FALLTHROUGH; + default: + status = U_INVALID_FORMAT_ERROR; + return 0; + } +} + +// ------------------------------------- +// Maximum int that can be represented exactly in a double. (53 bits) +// Larger ints may be rounded to a near-by value as not all are representable. +// TODO: move this constant elsewhere, possibly configure it for different +// floating point formats, if any non-standard ones are still in use. +static const int64_t U_DOUBLE_MAX_EXACT_INT = 9007199254740992LL; + +int64_t +Formattable::getInt64(UErrorCode& status) const +{ + if (U_FAILURE(status)) { + return 0; + } + + switch (fType) { + case Formattable::kLong: + case Formattable::kInt64: + return fValue.fInt64; + case Formattable::kDouble: + if (fValue.fDouble > (double)U_INT64_MAX) { + status = U_INVALID_FORMAT_ERROR; + return U_INT64_MAX; + } else if (fValue.fDouble < (double)U_INT64_MIN) { + status = U_INVALID_FORMAT_ERROR; + return U_INT64_MIN; + } else if (fabs(fValue.fDouble) > U_DOUBLE_MAX_EXACT_INT && fDecimalQuantity != nullptr) { + if (fDecimalQuantity->fitsInLong(true)) { + return fDecimalQuantity->toLong(); + } else { + // Unexpected + status = U_INVALID_FORMAT_ERROR; + return fDecimalQuantity->isNegative() ? U_INT64_MIN : U_INT64_MAX; + } + } else { + return (int64_t)fValue.fDouble; + } + case Formattable::kObject: + if (fValue.fObject == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + if (instanceOfMeasure(fValue.fObject)) { + return ((const Measure*) fValue.fObject)-> + getNumber().getInt64(status); + } + U_FALLTHROUGH; + default: + status = U_INVALID_FORMAT_ERROR; + return 0; + } +} + +// ------------------------------------- +double +Formattable::getDouble(UErrorCode& status) const +{ + if (U_FAILURE(status)) { + return 0; + } + + switch (fType) { + case Formattable::kLong: + case Formattable::kInt64: // loses precision + return (double)fValue.fInt64; + case Formattable::kDouble: + return fValue.fDouble; + case Formattable::kObject: + if (fValue.fObject == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + // TODO Later replace this with instanceof call + if (instanceOfMeasure(fValue.fObject)) { + return ((const Measure*) fValue.fObject)-> + getNumber().getDouble(status); + } + U_FALLTHROUGH; + default: + status = U_INVALID_FORMAT_ERROR; + return 0; + } +} + +const UObject* +Formattable::getObject() const { + return (fType == kObject) ? fValue.fObject : nullptr; +} + +// ------------------------------------- +// Sets the value to a double value d. + +void +Formattable::setDouble(double d) +{ + dispose(); + fType = kDouble; + fValue.fDouble = d; +} + +// ------------------------------------- +// Sets the value to a long value l. + +void +Formattable::setLong(int32_t l) +{ + dispose(); + fType = kLong; + fValue.fInt64 = l; +} + +// ------------------------------------- +// Sets the value to an int64 value ll. + +void +Formattable::setInt64(int64_t ll) +{ + dispose(); + fType = kInt64; + fValue.fInt64 = ll; +} + +// ------------------------------------- +// Sets the value to a Date instance d. + +void +Formattable::setDate(UDate d) +{ + dispose(); + fType = kDate; + fValue.fDate = d; +} + +// ------------------------------------- +// Sets the value to a string value stringToCopy. + +void +Formattable::setString(const UnicodeString& stringToCopy) +{ + dispose(); + fType = kString; + fValue.fString = new UnicodeString(stringToCopy); +} + +// ------------------------------------- +// Sets the value to an array of Formattable objects. + +void +Formattable::setArray(const Formattable* array, int32_t count) +{ + dispose(); + fType = kArray; + fValue.fArrayAndCount.fArray = createArrayCopy(array, count); + fValue.fArrayAndCount.fCount = count; +} + +// ------------------------------------- +// Adopts the stringToAdopt value. + +void +Formattable::adoptString(UnicodeString* stringToAdopt) +{ + dispose(); + fType = kString; + fValue.fString = stringToAdopt; +} + +// ------------------------------------- +// Adopts the array value and its count. + +void +Formattable::adoptArray(Formattable* array, int32_t count) +{ + dispose(); + fType = kArray; + fValue.fArrayAndCount.fArray = array; + fValue.fArrayAndCount.fCount = count; +} + +void +Formattable::adoptObject(UObject* objectToAdopt) { + dispose(); + fType = kObject; + fValue.fObject = objectToAdopt; +} + +// ------------------------------------- +UnicodeString& +Formattable::getString(UnicodeString& result, UErrorCode& status) const +{ + if (fType != kString) { + setError(status, U_INVALID_FORMAT_ERROR); + result.setToBogus(); + } else { + if (fValue.fString == nullptr) { + setError(status, U_MEMORY_ALLOCATION_ERROR); + } else { + result = *fValue.fString; + } + } + return result; +} + +// ------------------------------------- +const UnicodeString& +Formattable::getString(UErrorCode& status) const +{ + if (fType != kString) { + setError(status, U_INVALID_FORMAT_ERROR); + return *getBogus(); + } + if (fValue.fString == nullptr) { + setError(status, U_MEMORY_ALLOCATION_ERROR); + return *getBogus(); + } + return *fValue.fString; +} + +// ------------------------------------- +UnicodeString& +Formattable::getString(UErrorCode& status) +{ + if (fType != kString) { + setError(status, U_INVALID_FORMAT_ERROR); + return *getBogus(); + } + if (fValue.fString == nullptr) { + setError(status, U_MEMORY_ALLOCATION_ERROR); + return *getBogus(); + } + return *fValue.fString; +} + +// ------------------------------------- +const Formattable* +Formattable::getArray(int32_t& count, UErrorCode& status) const +{ + if (fType != kArray) { + setError(status, U_INVALID_FORMAT_ERROR); + count = 0; + return nullptr; + } + count = fValue.fArrayAndCount.fCount; + return fValue.fArrayAndCount.fArray; +} + +// ------------------------------------- +// Gets the bogus string, ensures mondo bogosity. + +UnicodeString* +Formattable::getBogus() const +{ + return (UnicodeString*)&fBogus; /* cast away const :-( */ +} + + +// -------------------------------------- +StringPiece Formattable::getDecimalNumber(UErrorCode &status) { + if (U_FAILURE(status)) { + return ""; + } + if (fDecimalStr != nullptr) { + return fDecimalStr->toStringPiece(); + } + + CharString *decimalStr = internalGetCharString(status); + if(decimalStr == nullptr) { + return ""; // getDecimalNumber returns "" for error cases + } else { + return decimalStr->toStringPiece(); + } +} + +CharString *Formattable::internalGetCharString(UErrorCode &status) { + if(fDecimalStr == nullptr) { + if (fDecimalQuantity == nullptr) { + // No decimal number for the formattable yet. Which means the value was + // set directly by the user as an int, int64 or double. If the value came + // from parsing, or from the user setting a decimal number, fDecimalNum + // would already be set. + // + LocalPointer dq(new DecimalQuantity(), status); + if (U_FAILURE(status)) { return nullptr; } + populateDecimalQuantity(*dq, status); + if (U_FAILURE(status)) { return nullptr; } + fDecimalQuantity = dq.orphan(); + } + + fDecimalStr = new CharString(); + if (fDecimalStr == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + // Older ICUs called uprv_decNumberToString here, which is not exactly the same as + // DecimalQuantity::toScientificString(). The biggest difference is that uprv_decNumberToString does + // not print scientific notation for magnitudes greater than -5 and smaller than some amount (+5?). + if (fDecimalQuantity->isInfinite()) { + fDecimalStr->append("Infinity", status); + } else if (fDecimalQuantity->isNaN()) { + fDecimalStr->append("NaN", status); + } else if (fDecimalQuantity->isZeroish()) { + fDecimalStr->append("0", -1, status); + } else if (fType==kLong || fType==kInt64 || // use toPlainString for integer types + (fDecimalQuantity->getMagnitude() != INT32_MIN && std::abs(fDecimalQuantity->getMagnitude()) < 5)) { + fDecimalStr->appendInvariantChars(fDecimalQuantity->toPlainString(), status); + } else { + fDecimalStr->appendInvariantChars(fDecimalQuantity->toScientificString(), status); + } + } + return fDecimalStr; +} + +void +Formattable::populateDecimalQuantity(number::impl::DecimalQuantity& output, UErrorCode& status) const { + if (fDecimalQuantity != nullptr) { + output = *fDecimalQuantity; + return; + } + + switch (fType) { + case kDouble: + output.setToDouble(this->getDouble()); + output.roundToInfinity(); + break; + case kLong: + output.setToInt(this->getLong()); + break; + case kInt64: + output.setToLong(this->getInt64()); + break; + default: + // The formattable's value is not a numeric type. + status = U_INVALID_STATE_ERROR; + } +} + +// --------------------------------------- +void +Formattable::adoptDecimalQuantity(DecimalQuantity *dq) { + if (fDecimalQuantity != nullptr) { + delete fDecimalQuantity; + } + fDecimalQuantity = dq; + if (dq == nullptr) { // allow adoptDigitList(nullptr) to clear + return; + } + + // Set the value into the Union of simple type values. + // Cannot use the set() functions because they would delete the fDecimalNum value. + if (fDecimalQuantity->fitsInLong()) { + fValue.fInt64 = fDecimalQuantity->toLong(); + if (fValue.fInt64 <= INT32_MAX && fValue.fInt64 >= INT32_MIN) { + fType = kLong; + } else { + fType = kInt64; + } + } else { + fType = kDouble; + fValue.fDouble = fDecimalQuantity->toDouble(); + } +} + + +// --------------------------------------- +void +Formattable::setDecimalNumber(StringPiece numberString, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + dispose(); + + auto* dq = new DecimalQuantity(); + dq->setToDecNumber(numberString, status); + adoptDecimalQuantity(dq); + + // Note that we do not hang on to the caller's input string. + // If we are asked for the string, we will regenerate one from fDecimalQuantity. +} + +#if 0 +//---------------------------------------------------- +// console I/O +//---------------------------------------------------- +#ifdef _DEBUG + +#include +using namespace std; + +#include "unicode/datefmt.h" +#include "unistrm.h" + +class FormattableStreamer /* not : public UObject because all methods are static */ { +public: + static void streamOut(ostream& stream, const Formattable& obj); + +private: + FormattableStreamer() {} // private - forbid instantiation +}; + +// This is for debugging purposes only. This will send a displayable +// form of the Formattable object to the output stream. + +void +FormattableStreamer::streamOut(ostream& stream, const Formattable& obj) +{ + static DateFormat *defDateFormat = 0; + + UnicodeString buffer; + switch(obj.getType()) { + case Formattable::kDate : + // Creates a DateFormat instance for formatting the + // Date instance. + if (defDateFormat == 0) { + defDateFormat = DateFormat::createInstance(); + } + defDateFormat->format(obj.getDate(), buffer); + stream << buffer; + break; + case Formattable::kDouble : + // Output the double as is. + stream << obj.getDouble() << 'D'; + break; + case Formattable::kLong : + // Output the double as is. + stream << obj.getLong() << 'L'; + break; + case Formattable::kString: + // Output the double as is. Please see UnicodeString console + // I/O routine for more details. + stream << '"' << obj.getString(buffer) << '"'; + break; + case Formattable::kArray: + int32_t i, count; + const Formattable* array; + array = obj.getArray(count); + stream << '['; + // Recursively calling the console I/O routine for each element in the array. + for (i=0; itoUFormattable(); + + if( fmt == nullptr ) { + *status = U_MEMORY_ALLOCATION_ERROR; + } + return fmt; +} + +U_CAPI void U_EXPORT2 +ufmt_close(UFormattable *fmt) { + Formattable *obj = Formattable::fromUFormattable(fmt); + + delete obj; +} + +U_CAPI UFormattableType U_EXPORT2 +ufmt_getType(const UFormattable *fmt, UErrorCode *status) { + if(U_FAILURE(*status)) { + return (UFormattableType)UFMT_COUNT; + } + const Formattable *obj = Formattable::fromUFormattable(fmt); + return (UFormattableType)obj->getType(); +} + + +U_CAPI UBool U_EXPORT2 +ufmt_isNumeric(const UFormattable *fmt) { + const Formattable *obj = Formattable::fromUFormattable(fmt); + return obj->isNumeric(); +} + +U_CAPI UDate U_EXPORT2 +ufmt_getDate(const UFormattable *fmt, UErrorCode *status) { + const Formattable *obj = Formattable::fromUFormattable(fmt); + + return obj->getDate(*status); +} + +U_CAPI double U_EXPORT2 +ufmt_getDouble(UFormattable *fmt, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + + return obj->getDouble(*status); +} + +U_CAPI int32_t U_EXPORT2 +ufmt_getLong(UFormattable *fmt, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + + return obj->getLong(*status); +} + + +U_CAPI const void *U_EXPORT2 +ufmt_getObject(const UFormattable *fmt, UErrorCode *status) { + const Formattable *obj = Formattable::fromUFormattable(fmt); + + const void *ret = obj->getObject(); + if( ret==nullptr && + (obj->getType() != Formattable::kObject) && + U_SUCCESS( *status )) { + *status = U_INVALID_FORMAT_ERROR; + } + return ret; +} + +U_CAPI const char16_t* U_EXPORT2 +ufmt_getUChars(UFormattable *fmt, int32_t *len, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + + // avoid bogosity by checking the type first. + if( obj->getType() != Formattable::kString ) { + if( U_SUCCESS(*status) ){ + *status = U_INVALID_FORMAT_ERROR; + } + return nullptr; + } + + // This should return a valid string + UnicodeString &str = obj->getString(*status); + if( U_SUCCESS(*status) && len != nullptr ) { + *len = str.length(); + } + return str.getTerminatedBuffer(); +} + +U_CAPI int32_t U_EXPORT2 +ufmt_getArrayLength(const UFormattable* fmt, UErrorCode *status) { + const Formattable *obj = Formattable::fromUFormattable(fmt); + + int32_t count; + (void)obj->getArray(count, *status); + return count; +} + +U_CAPI UFormattable * U_EXPORT2 +ufmt_getArrayItemByIndex(UFormattable* fmt, int32_t n, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + int32_t count; + (void)obj->getArray(count, *status); + if(U_FAILURE(*status)) { + return nullptr; + } else if(n<0 || n>=count) { + setError(*status, U_INDEX_OUTOFBOUNDS_ERROR); + return nullptr; + } else { + return (*obj)[n].toUFormattable(); // returns non-const Formattable + } +} + +U_CAPI const char * U_EXPORT2 +ufmt_getDecNumChars(UFormattable *fmt, int32_t *len, UErrorCode *status) { + if(U_FAILURE(*status)) { + return ""; + } + Formattable *obj = Formattable::fromUFormattable(fmt); + CharString *charString = obj->internalGetCharString(*status); + if(U_FAILURE(*status)) { + return ""; + } + if(charString == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return ""; + } else { + if(len!=nullptr) { + *len = charString->length(); + } + return charString->data(); + } +} + +U_CAPI int64_t U_EXPORT2 +ufmt_getInt64(UFormattable *fmt, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + return obj->getInt64(*status); +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/fmtable_cnv.cpp b/deps/icu-small/source/i18n/fmtable_cnv.cpp index bc3847b6963e70..d0c2ddf4ca640c 100644 --- a/deps/icu-small/source/i18n/fmtable_cnv.cpp +++ b/deps/icu-small/source/i18n/fmtable_cnv.cpp @@ -1,44 +1,44 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2010, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -* -* File FMTABLE.CPP -* -* Modification History: -* -* Date Name Description -* 03/25/97 clhuang Initial Implementation. -******************************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_CONVERSION - -#include "unicode/fmtable.h" - -// ***************************************************************************** -// class Formattable -// ***************************************************************************** - -U_NAMESPACE_BEGIN - -// ------------------------------------- -// Creates a formattable object with a char* string. -// This API is useless. The API that takes a UnicodeString is actually just as good. -Formattable::Formattable(const char* stringToCopy) -{ - init(); - fType = kString; - fValue.fString = new UnicodeString(stringToCopy); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING || !UCONFIG_NO_CONVERSION */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1997-2010, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* File FMTABLE.CPP +* +* Modification History: +* +* Date Name Description +* 03/25/97 clhuang Initial Implementation. +******************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_CONVERSION + +#include "unicode/fmtable.h" + +// ***************************************************************************** +// class Formattable +// ***************************************************************************** + +U_NAMESPACE_BEGIN + +// ------------------------------------- +// Creates a formattable object with a char* string. +// This API is useless. The API that takes a UnicodeString is actually just as good. +Formattable::Formattable(const char* stringToCopy) +{ + init(); + fType = kString; + fValue.fString = new UnicodeString(stringToCopy); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING || !UCONFIG_NO_CONVERSION */ + +//eof diff --git a/deps/icu-small/source/i18n/fmtableimp.h b/deps/icu-small/source/i18n/fmtableimp.h index 2707d6ece2e2eb..2361bcdf1d75b0 100644 --- a/deps/icu-small/source/i18n/fmtableimp.h +++ b/deps/icu-small/source/i18n/fmtableimp.h @@ -1,31 +1,31 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2014, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - -#ifndef FMTABLEIMP_H -#define FMTABLEIMP_H - -#include "number_decimalquantity.h" - -#if !UCONFIG_NO_FORMATTING - -U_NAMESPACE_BEGIN - -/** - * 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. - * IBM Mainframes have 56 bits of mantissa, 7 bits of base 16 exponent, 1 bit sign. - * Define this constant to the smallest value from those for supported platforms. - * @internal - */ -static const int64_t MAX_INT64_IN_DOUBLE = 0x001FFFFFFFFFFFFFLL; - -U_NAMESPACE_END - -#endif // #if !UCONFIG_NO_FORMATTING -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2014, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +*/ + +#ifndef FMTABLEIMP_H +#define FMTABLEIMP_H + +#include "number_decimalquantity.h" + +#if !UCONFIG_NO_FORMATTING + +U_NAMESPACE_BEGIN + +/** + * 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. + * IBM Mainframes have 56 bits of mantissa, 7 bits of base 16 exponent, 1 bit sign. + * Define this constant to the smallest value from those for supported platforms. + * @internal + */ +static const int64_t MAX_INT64_IN_DOUBLE = 0x001FFFFFFFFFFFFFLL; + +U_NAMESPACE_END + +#endif // #if !UCONFIG_NO_FORMATTING +#endif diff --git a/deps/icu-small/source/i18n/format.cpp b/deps/icu-small/source/i18n/format.cpp index 10856a4acba286..693ccb917024d2 100644 --- a/deps/icu-small/source/i18n/format.cpp +++ b/deps/icu-small/source/i18n/format.cpp @@ -1,219 +1,219 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2012, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -* -* File FORMAT.CPP -* -* Modification History: -* -* Date Name Description -* 02/19/97 aliu Converted from java. -* 03/17/97 clhuang Implemented with new APIs. -* 03/27/97 helena Updated to pass the simple test after code review. -* 07/20/98 stephen Added explicit init values for Field/ParsePosition -******************************************************************************** -*/ -// ***************************************************************************** -// This file was generated from the java source file Format.java -// ***************************************************************************** - -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/utypes.h" - -#ifndef U_I18N_IMPLEMENTATION -#error U_I18N_IMPLEMENTATION not set - must be set for all ICU source files in i18n/ - see https://unicode-org.github.io/icu/userguide/howtouseicu -#endif - -/* - * Dummy code: - * If all modules in the I18N library are switched off, then there are no - * library exports and MSVC 6 writes a .dll but not a .lib file. - * Unless we export _something_ in that case... - */ -#if UCONFIG_NO_COLLATION && UCONFIG_NO_FORMATTING && UCONFIG_NO_TRANSLITERATION -U_CAPI int32_t U_EXPORT2 -uprv_icuin_lib_dummy(int32_t i) { - return -i; -} -#endif - -/* Format class implementation ---------------------------------------------- */ - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/format.h" -#include "unicode/ures.h" -#include "cstring.h" -#include "locbased.h" - -// ***************************************************************************** -// class Format -// ***************************************************************************** - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FieldPosition) - -FieldPosition::~FieldPosition() {} - -FieldPosition * -FieldPosition::clone() const { - return new FieldPosition(*this); -} - -// ------------------------------------- -// default constructor - -Format::Format() - : UObject() -{ - *validLocale = *actualLocale = 0; -} - -// ------------------------------------- - -Format::~Format() -{ -} - -// ------------------------------------- -// copy constructor - -Format::Format(const Format &that) - : UObject(that) -{ - *this = that; -} - -// ------------------------------------- -// assignment operator - -Format& -Format::operator=(const Format& that) -{ - if (this != &that) { - uprv_strcpy(validLocale, that.validLocale); - uprv_strcpy(actualLocale, that.actualLocale); - } - return *this; -} - -// ------------------------------------- -// Formats the obj and append the result in the buffer, toAppendTo. -// This calls the actual implementation in the concrete subclasses. - -UnicodeString& -Format::format(const Formattable& obj, - UnicodeString& toAppendTo, - UErrorCode& status) const -{ - if (U_FAILURE(status)) return toAppendTo; - - FieldPosition pos(FieldPosition::DONT_CARE); - - return format(obj, toAppendTo, pos, status); -} - -// ------------------------------------- -// Default implementation sets unsupported error; subclasses should -// override. - -UnicodeString& -Format::format(const Formattable& /* unused obj */, - UnicodeString& toAppendTo, - FieldPositionIterator* /* unused posIter */, - UErrorCode& status) const -{ - if (!U_FAILURE(status)) { - status = U_UNSUPPORTED_ERROR; - } - return toAppendTo; -} - -// ------------------------------------- -// Parses the source string and create the corresponding -// result object. Checks the parse position for errors. - -void -Format::parseObject(const UnicodeString& source, - Formattable& result, - UErrorCode& status) const -{ - if (U_FAILURE(status)) return; - - ParsePosition parsePosition(0); - parseObject(source, result, parsePosition); - if (parsePosition.getIndex() == 0) { - status = U_INVALID_FORMAT_ERROR; - } -} - -// ------------------------------------- - -bool -Format::operator==(const Format& that) const -{ - // Subclasses: Call this method and then add more specific checks. - return typeid(*this) == typeid(that); -} -//--------------------------------------- - -/** - * Simple function for initializing a UParseError from a UnicodeString. - * - * @param pattern The pattern to copy into the parseError - * @param pos The position in pattern where the error occurred - * @param parseError The UParseError object to fill in - * @draft ICU 2.4 - */ -void Format::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 = pos+1; - stop = ((pos+U_PARSE_CONTEXT_LEN)<=pattern.length()) ? (pos+(U_PARSE_CONTEXT_LEN-1)) : - pattern.length(); - pattern.extract(start,stop-start,parseError.postContext,0); - //null terminate the buffer - parseError.postContext[stop-start]= 0; -} - -Locale -Format::getLocale(ULocDataLocaleType type, UErrorCode& status) const { - U_LOCALE_BASED(locBased, *this); - return locBased.getLocale(type, status); -} - -const char * -Format::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const { - U_LOCALE_BASED(locBased, *this); - return locBased.getLocaleID(type, status); -} - -void -Format::setLocaleIDs(const char* valid, const char* actual) { - U_LOCALE_BASED(locBased, *this); - locBased.setLocaleIDs(valid, actual); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1997-2012, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* File FORMAT.CPP +* +* Modification History: +* +* Date Name Description +* 02/19/97 aliu Converted from java. +* 03/17/97 clhuang Implemented with new APIs. +* 03/27/97 helena Updated to pass the simple test after code review. +* 07/20/98 stephen Added explicit init values for Field/ParsePosition +******************************************************************************** +*/ +// ***************************************************************************** +// This file was generated from the java source file Format.java +// ***************************************************************************** + +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/utypes.h" + +#ifndef U_I18N_IMPLEMENTATION +#error U_I18N_IMPLEMENTATION not set - must be set for all ICU source files in i18n/ - see https://unicode-org.github.io/icu/userguide/howtouseicu +#endif + +/* + * Dummy code: + * If all modules in the I18N library are switched off, then there are no + * library exports and MSVC 6 writes a .dll but not a .lib file. + * Unless we export _something_ in that case... + */ +#if UCONFIG_NO_COLLATION && UCONFIG_NO_FORMATTING && UCONFIG_NO_TRANSLITERATION +U_CAPI int32_t U_EXPORT2 +uprv_icuin_lib_dummy(int32_t i) { + return -i; +} +#endif + +/* Format class implementation ---------------------------------------------- */ + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/format.h" +#include "unicode/ures.h" +#include "cstring.h" +#include "locbased.h" + +// ***************************************************************************** +// class Format +// ***************************************************************************** + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FieldPosition) + +FieldPosition::~FieldPosition() {} + +FieldPosition * +FieldPosition::clone() const { + return new FieldPosition(*this); +} + +// ------------------------------------- +// default constructor + +Format::Format() + : UObject() +{ + *validLocale = *actualLocale = 0; +} + +// ------------------------------------- + +Format::~Format() +{ +} + +// ------------------------------------- +// copy constructor + +Format::Format(const Format &that) + : UObject(that) +{ + *this = that; +} + +// ------------------------------------- +// assignment operator + +Format& +Format::operator=(const Format& that) +{ + if (this != &that) { + uprv_strcpy(validLocale, that.validLocale); + uprv_strcpy(actualLocale, that.actualLocale); + } + return *this; +} + +// ------------------------------------- +// Formats the obj and append the result in the buffer, toAppendTo. +// This calls the actual implementation in the concrete subclasses. + +UnicodeString& +Format::format(const Formattable& obj, + UnicodeString& toAppendTo, + UErrorCode& status) const +{ + if (U_FAILURE(status)) return toAppendTo; + + FieldPosition pos(FieldPosition::DONT_CARE); + + return format(obj, toAppendTo, pos, status); +} + +// ------------------------------------- +// Default implementation sets unsupported error; subclasses should +// override. + +UnicodeString& +Format::format(const Formattable& /* unused obj */, + UnicodeString& toAppendTo, + FieldPositionIterator* /* unused posIter */, + UErrorCode& status) const +{ + if (!U_FAILURE(status)) { + status = U_UNSUPPORTED_ERROR; + } + return toAppendTo; +} + +// ------------------------------------- +// Parses the source string and create the corresponding +// result object. Checks the parse position for errors. + +void +Format::parseObject(const UnicodeString& source, + Formattable& result, + UErrorCode& status) const +{ + if (U_FAILURE(status)) return; + + ParsePosition parsePosition(0); + parseObject(source, result, parsePosition); + if (parsePosition.getIndex() == 0) { + status = U_INVALID_FORMAT_ERROR; + } +} + +// ------------------------------------- + +bool +Format::operator==(const Format& that) const +{ + // Subclasses: Call this method and then add more specific checks. + return typeid(*this) == typeid(that); +} +//--------------------------------------- + +/** + * Simple function for initializing a UParseError from a UnicodeString. + * + * @param pattern The pattern to copy into the parseError + * @param pos The position in pattern where the error occurred + * @param parseError The UParseError object to fill in + * @draft ICU 2.4 + */ +void Format::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 = pos+1; + stop = ((pos+U_PARSE_CONTEXT_LEN)<=pattern.length()) ? (pos+(U_PARSE_CONTEXT_LEN-1)) : + pattern.length(); + pattern.extract(start,stop-start,parseError.postContext,0); + //null terminate the buffer + parseError.postContext[stop-start]= 0; +} + +Locale +Format::getLocale(ULocDataLocaleType type, UErrorCode& status) const { + U_LOCALE_BASED(locBased, *this); + return locBased.getLocale(type, status); +} + +const char * +Format::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const { + U_LOCALE_BASED(locBased, *this); + return locBased.getLocaleID(type, status); +} + +void +Format::setLocaleIDs(const char* valid, const char* actual) { + U_LOCALE_BASED(locBased, *this); + locBased.setLocaleIDs(valid, actual); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/formatted_string_builder.cpp b/deps/icu-small/source/i18n/formatted_string_builder.cpp index 8dbf954af9ffa8..aaeea37a5cb88d 100644 --- a/deps/icu-small/source/i18n/formatted_string_builder.cpp +++ b/deps/icu-small/source/i18n/formatted_string_builder.cpp @@ -1,470 +1,470 @@ -// © 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 - -#include "formatted_string_builder.h" -#include "putilimp.h" -#include "unicode/ustring.h" -#include "unicode/utf16.h" -#include "unicode/unum.h" // for UNumberFormatFields literals - -namespace { - -// A version of uprv_memcpy that checks for length 0. -// By default, uprv_memcpy requires a length of at least 1. -inline void uprv_memcpy2(void* dest, const void* src, size_t len) { - if (len > 0) { - uprv_memcpy(dest, src, len); - } -} - -// A version of uprv_memmove that checks for length 0. -// By default, uprv_memmove requires a length of at least 1. -inline void uprv_memmove2(void* dest, const void* src, size_t len) { - if (len > 0) { - uprv_memmove(dest, src, len); - } -} - -} // namespace - - -U_NAMESPACE_BEGIN - -FormattedStringBuilder::FormattedStringBuilder() { -#if U_DEBUG - // Initializing the memory to non-zero helps catch some bugs that involve - // reading from an improperly terminated string. - for (int32_t i=0; i DEFAULT_CAPACITY) { - // FIXME: uprv_malloc - // C++ note: malloc appears in two places: here and in prepareForInsertHelper. - auto newChars = static_cast (uprv_malloc(sizeof(char16_t) * capacity)); - auto newFields = static_cast(uprv_malloc(sizeof(Field) * capacity)); - if (newChars == nullptr || newFields == nullptr) { - // UErrorCode is not available; fail silently. - uprv_free(newChars); - uprv_free(newFields); - *this = FormattedStringBuilder(); // can't fail - return *this; - } - - fUsingHeap = true; - fChars.heap.capacity = capacity; - fChars.heap.ptr = newChars; - fFields.heap.capacity = capacity; - fFields.heap.ptr = newFields; - } - - uprv_memcpy2(getCharPtr(), other.getCharPtr(), sizeof(char16_t) * capacity); - uprv_memcpy2(getFieldPtr(), other.getFieldPtr(), sizeof(Field) * capacity); - - fZero = other.fZero; - fLength = other.fLength; - return *this; -} - -int32_t FormattedStringBuilder::length() const { - return fLength; -} - -int32_t FormattedStringBuilder::codePointCount() const { - return u_countChar32(getCharPtr() + fZero, fLength); -} - -UChar32 FormattedStringBuilder::getFirstCodePoint() const { - if (fLength == 0) { - return -1; - } - UChar32 cp; - U16_GET(getCharPtr() + fZero, 0, 0, fLength, cp); - return cp; -} - -UChar32 FormattedStringBuilder::getLastCodePoint() const { - if (fLength == 0) { - return -1; - } - int32_t offset = fLength; - U16_BACK_1(getCharPtr() + fZero, 0, offset); - UChar32 cp; - U16_GET(getCharPtr() + fZero, 0, offset, fLength, cp); - return cp; -} - -UChar32 FormattedStringBuilder::codePointAt(int32_t index) const { - UChar32 cp; - U16_GET(getCharPtr() + fZero, 0, index, fLength, cp); - return cp; -} - -UChar32 FormattedStringBuilder::codePointBefore(int32_t index) const { - int32_t offset = index; - U16_BACK_1(getCharPtr() + fZero, 0, offset); - UChar32 cp; - U16_GET(getCharPtr() + fZero, 0, offset, fLength, cp); - return cp; -} - -FormattedStringBuilder &FormattedStringBuilder::clear() { - // TODO: Reset the heap here? - fZero = getCapacity() / 2; - fLength = 0; - return *this; -} - -int32_t -FormattedStringBuilder::insertCodePoint(int32_t index, UChar32 codePoint, Field field, UErrorCode &status) { - int32_t count = U16_LENGTH(codePoint); - int32_t position = prepareForInsert(index, count, status); - if (U_FAILURE(status)) { - return count; - } - if (count == 1) { - getCharPtr()[position] = (char16_t) codePoint; - getFieldPtr()[position] = field; - } else { - getCharPtr()[position] = U16_LEAD(codePoint); - getCharPtr()[position + 1] = U16_TRAIL(codePoint); - getFieldPtr()[position] = getFieldPtr()[position + 1] = field; - } - return count; -} - -int32_t FormattedStringBuilder::insert(int32_t index, const UnicodeString &unistr, Field field, - UErrorCode &status) { - if (unistr.length() == 0) { - // Nothing to insert. - return 0; - } else if (unistr.length() == 1) { - // Fast path: insert using insertCodePoint. - return insertCodePoint(index, unistr.charAt(0), field, status); - } else { - return insert(index, unistr, 0, unistr.length(), field, status); - } -} - -int32_t -FormattedStringBuilder::insert(int32_t index, const UnicodeString &unistr, int32_t start, int32_t end, - Field field, UErrorCode &status) { - int32_t count = end - start; - int32_t position = prepareForInsert(index, count, status); - if (U_FAILURE(status)) { - return count; - } - for (int32_t i = 0; i < count; i++) { - getCharPtr()[position + i] = unistr.charAt(start + i); - getFieldPtr()[position + i] = field; - } - return count; -} - -int32_t -FormattedStringBuilder::splice(int32_t startThis, int32_t endThis, const UnicodeString &unistr, - int32_t startOther, int32_t endOther, Field field, UErrorCode& status) { - int32_t thisLength = endThis - startThis; - int32_t otherLength = endOther - startOther; - int32_t count = otherLength - thisLength; - if (U_FAILURE(status)) { - return count; - } - int32_t position; - if (count > 0) { - // Overall, chars need to be added. - position = prepareForInsert(startThis, count, status); - } else { - // Overall, chars need to be removed or kept the same. - position = remove(startThis, -count); - } - if (U_FAILURE(status)) { - return count; - } - for (int32_t i = 0; i < otherLength; i++) { - getCharPtr()[position + i] = unistr.charAt(startOther + i); - getFieldPtr()[position + i] = field; - } - return count; -} - -int32_t FormattedStringBuilder::append(const FormattedStringBuilder &other, UErrorCode &status) { - return insert(fLength, other, status); -} - -int32_t -FormattedStringBuilder::insert(int32_t index, const FormattedStringBuilder &other, UErrorCode &status) { - if (U_FAILURE(status)) { - return 0; - } - if (this == &other) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - int32_t count = other.fLength; - if (count == 0) { - // Nothing to insert. - return 0; - } - int32_t position = prepareForInsert(index, count, status); - if (U_FAILURE(status)) { - return count; - } - for (int32_t i = 0; i < count; i++) { - getCharPtr()[position + i] = other.charAt(i); - getFieldPtr()[position + i] = other.fieldAt(i); - } - return count; -} - -void FormattedStringBuilder::writeTerminator(UErrorCode& status) { - int32_t position = prepareForInsert(fLength, 1, status); - if (U_FAILURE(status)) { - return; - } - getCharPtr()[position] = 0; - getFieldPtr()[position] = kUndefinedField; - fLength--; -} - -int32_t FormattedStringBuilder::prepareForInsert(int32_t index, int32_t count, UErrorCode &status) { - U_ASSERT(index >= 0); - U_ASSERT(index <= fLength); - U_ASSERT(count >= 0); - U_ASSERT(fZero >= 0); - U_ASSERT(fLength >= 0); - U_ASSERT(getCapacity() - fZero >= fLength); - if (U_FAILURE(status)) { - return count; - } - if (index == 0 && fZero - count >= 0) { - // Append to start - fZero -= count; - fLength += count; - return fZero; - } else if (index == fLength && count <= getCapacity() - fZero - fLength) { - // Append to end - fLength += count; - return fZero + fLength - count; - } else { - // Move chars around and/or allocate more space - return prepareForInsertHelper(index, count, status); - } -} - -int32_t FormattedStringBuilder::prepareForInsertHelper(int32_t index, int32_t count, UErrorCode &status) { - int32_t oldCapacity = getCapacity(); - int32_t oldZero = fZero; - char16_t *oldChars = getCharPtr(); - Field *oldFields = getFieldPtr(); - int32_t newLength; - if (uprv_add32_overflow(fLength, count, &newLength)) { - status = U_INPUT_TOO_LONG_ERROR; - return -1; - } - int32_t newZero; - if (newLength > oldCapacity) { - if (newLength > INT32_MAX / 2) { - // We do not support more than 1G char16_t in this code because - // dealing with >2G *bytes* can cause subtle bugs. - status = U_INPUT_TOO_LONG_ERROR; - return -1; - } - // Keep newCapacity also to at most 1G char16_t. - int32_t newCapacity = newLength * 2; - newZero = (newCapacity - newLength) / 2; - - // C++ note: malloc appears in two places: here and in the assignment operator. - auto newChars = static_cast (uprv_malloc(sizeof(char16_t) * static_cast(newCapacity))); - auto newFields = static_cast(uprv_malloc(sizeof(Field) * static_cast(newCapacity))); - if (newChars == nullptr || newFields == nullptr) { - uprv_free(newChars); - uprv_free(newFields); - status = U_MEMORY_ALLOCATION_ERROR; - return -1; - } - - // First copy the prefix and then the suffix, leaving room for the new chars that the - // caller wants to insert. - // C++ note: memcpy is OK because the src and dest do not overlap. - uprv_memcpy2(newChars + newZero, oldChars + oldZero, sizeof(char16_t) * index); - uprv_memcpy2(newChars + newZero + index + count, - oldChars + oldZero + index, - sizeof(char16_t) * (fLength - index)); - uprv_memcpy2(newFields + newZero, oldFields + oldZero, sizeof(Field) * index); - uprv_memcpy2(newFields + newZero + index + count, - oldFields + oldZero + index, - sizeof(Field) * (fLength - index)); - - if (fUsingHeap) { - uprv_free(oldChars); - uprv_free(oldFields); - } - fUsingHeap = true; - fChars.heap.ptr = newChars; - fChars.heap.capacity = newCapacity; - fFields.heap.ptr = newFields; - fFields.heap.capacity = newCapacity; - } else { - newZero = (oldCapacity - newLength) / 2; - - // C++ note: memmove is required because src and dest may overlap. - // First copy the entire string to the location of the prefix, and then move the suffix - // to make room for the new chars that the caller wants to insert. - uprv_memmove2(oldChars + newZero, oldChars + oldZero, sizeof(char16_t) * fLength); - uprv_memmove2(oldChars + newZero + index + count, - oldChars + newZero + index, - sizeof(char16_t) * (fLength - index)); - uprv_memmove2(oldFields + newZero, oldFields + oldZero, sizeof(Field) * fLength); - uprv_memmove2(oldFields + newZero + index + count, - oldFields + newZero + index, - sizeof(Field) * (fLength - index)); - } - fZero = newZero; - fLength = newLength; - return fZero + index; -} - -int32_t FormattedStringBuilder::remove(int32_t index, int32_t count) { - U_ASSERT(0 <= index); - U_ASSERT(index <= fLength); - U_ASSERT(count <= (fLength - index)); - U_ASSERT(index <= getCapacity() - fZero); - - int32_t position = index + fZero; - // TODO: Reset the heap here? (If the string after removal can fit on stack?) - uprv_memmove2(getCharPtr() + position, - getCharPtr() + position + count, - sizeof(char16_t) * (fLength - index - count)); - uprv_memmove2(getFieldPtr() + position, - getFieldPtr() + position + count, - sizeof(Field) * (fLength - index - count)); - fLength -= count; - return position; -} - -UnicodeString FormattedStringBuilder::toUnicodeString() const { - return UnicodeString(getCharPtr() + fZero, fLength); -} - -const UnicodeString FormattedStringBuilder::toTempUnicodeString() const { - // Readonly-alias constructor: - return UnicodeString(false, getCharPtr() + fZero, fLength); -} - -UnicodeString FormattedStringBuilder::toDebugString() const { - UnicodeString sb; - sb.append(u"", -1); - return sb; -} - -const char16_t *FormattedStringBuilder::chars() const { - return getCharPtr() + fZero; -} - -bool FormattedStringBuilder::contentEquals(const FormattedStringBuilder &other) const { - if (fLength != other.fLength) { - return false; - } - for (int32_t i = 0; i < fLength; i++) { - if (charAt(i) != other.charAt(i) || fieldAt(i) != other.fieldAt(i)) { - return false; - } - } - return true; -} - -bool FormattedStringBuilder::containsField(Field field) const { - for (int32_t i = 0; i < fLength; i++) { - if (field == fieldAt(i)) { - return true; - } - } - return false; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include "formatted_string_builder.h" +#include "putilimp.h" +#include "unicode/ustring.h" +#include "unicode/utf16.h" +#include "unicode/unum.h" // for UNumberFormatFields literals + +namespace { + +// A version of uprv_memcpy that checks for length 0. +// By default, uprv_memcpy requires a length of at least 1. +inline void uprv_memcpy2(void* dest, const void* src, size_t len) { + if (len > 0) { + uprv_memcpy(dest, src, len); + } +} + +// A version of uprv_memmove that checks for length 0. +// By default, uprv_memmove requires a length of at least 1. +inline void uprv_memmove2(void* dest, const void* src, size_t len) { + if (len > 0) { + uprv_memmove(dest, src, len); + } +} + +} // namespace + + +U_NAMESPACE_BEGIN + +FormattedStringBuilder::FormattedStringBuilder() { +#if U_DEBUG + // Initializing the memory to non-zero helps catch some bugs that involve + // reading from an improperly terminated string. + for (int32_t i=0; i DEFAULT_CAPACITY) { + // FIXME: uprv_malloc + // C++ note: malloc appears in two places: here and in prepareForInsertHelper. + auto newChars = static_cast (uprv_malloc(sizeof(char16_t) * capacity)); + auto newFields = static_cast(uprv_malloc(sizeof(Field) * capacity)); + if (newChars == nullptr || newFields == nullptr) { + // UErrorCode is not available; fail silently. + uprv_free(newChars); + uprv_free(newFields); + *this = FormattedStringBuilder(); // can't fail + return *this; + } + + fUsingHeap = true; + fChars.heap.capacity = capacity; + fChars.heap.ptr = newChars; + fFields.heap.capacity = capacity; + fFields.heap.ptr = newFields; + } + + uprv_memcpy2(getCharPtr(), other.getCharPtr(), sizeof(char16_t) * capacity); + uprv_memcpy2(getFieldPtr(), other.getFieldPtr(), sizeof(Field) * capacity); + + fZero = other.fZero; + fLength = other.fLength; + return *this; +} + +int32_t FormattedStringBuilder::length() const { + return fLength; +} + +int32_t FormattedStringBuilder::codePointCount() const { + return u_countChar32(getCharPtr() + fZero, fLength); +} + +UChar32 FormattedStringBuilder::getFirstCodePoint() const { + if (fLength == 0) { + return -1; + } + UChar32 cp; + U16_GET(getCharPtr() + fZero, 0, 0, fLength, cp); + return cp; +} + +UChar32 FormattedStringBuilder::getLastCodePoint() const { + if (fLength == 0) { + return -1; + } + int32_t offset = fLength; + U16_BACK_1(getCharPtr() + fZero, 0, offset); + UChar32 cp; + U16_GET(getCharPtr() + fZero, 0, offset, fLength, cp); + return cp; +} + +UChar32 FormattedStringBuilder::codePointAt(int32_t index) const { + UChar32 cp; + U16_GET(getCharPtr() + fZero, 0, index, fLength, cp); + return cp; +} + +UChar32 FormattedStringBuilder::codePointBefore(int32_t index) const { + int32_t offset = index; + U16_BACK_1(getCharPtr() + fZero, 0, offset); + UChar32 cp; + U16_GET(getCharPtr() + fZero, 0, offset, fLength, cp); + return cp; +} + +FormattedStringBuilder &FormattedStringBuilder::clear() { + // TODO: Reset the heap here? + fZero = getCapacity() / 2; + fLength = 0; + return *this; +} + +int32_t +FormattedStringBuilder::insertCodePoint(int32_t index, UChar32 codePoint, Field field, UErrorCode &status) { + int32_t count = U16_LENGTH(codePoint); + int32_t position = prepareForInsert(index, count, status); + if (U_FAILURE(status)) { + return count; + } + if (count == 1) { + getCharPtr()[position] = (char16_t) codePoint; + getFieldPtr()[position] = field; + } else { + getCharPtr()[position] = U16_LEAD(codePoint); + getCharPtr()[position + 1] = U16_TRAIL(codePoint); + getFieldPtr()[position] = getFieldPtr()[position + 1] = field; + } + return count; +} + +int32_t FormattedStringBuilder::insert(int32_t index, const UnicodeString &unistr, Field field, + UErrorCode &status) { + if (unistr.length() == 0) { + // Nothing to insert. + return 0; + } else if (unistr.length() == 1) { + // Fast path: insert using insertCodePoint. + return insertCodePoint(index, unistr.charAt(0), field, status); + } else { + return insert(index, unistr, 0, unistr.length(), field, status); + } +} + +int32_t +FormattedStringBuilder::insert(int32_t index, const UnicodeString &unistr, int32_t start, int32_t end, + Field field, UErrorCode &status) { + int32_t count = end - start; + int32_t position = prepareForInsert(index, count, status); + if (U_FAILURE(status)) { + return count; + } + for (int32_t i = 0; i < count; i++) { + getCharPtr()[position + i] = unistr.charAt(start + i); + getFieldPtr()[position + i] = field; + } + return count; +} + +int32_t +FormattedStringBuilder::splice(int32_t startThis, int32_t endThis, const UnicodeString &unistr, + int32_t startOther, int32_t endOther, Field field, UErrorCode& status) { + int32_t thisLength = endThis - startThis; + int32_t otherLength = endOther - startOther; + int32_t count = otherLength - thisLength; + if (U_FAILURE(status)) { + return count; + } + int32_t position; + if (count > 0) { + // Overall, chars need to be added. + position = prepareForInsert(startThis, count, status); + } else { + // Overall, chars need to be removed or kept the same. + position = remove(startThis, -count); + } + if (U_FAILURE(status)) { + return count; + } + for (int32_t i = 0; i < otherLength; i++) { + getCharPtr()[position + i] = unistr.charAt(startOther + i); + getFieldPtr()[position + i] = field; + } + return count; +} + +int32_t FormattedStringBuilder::append(const FormattedStringBuilder &other, UErrorCode &status) { + return insert(fLength, other, status); +} + +int32_t +FormattedStringBuilder::insert(int32_t index, const FormattedStringBuilder &other, UErrorCode &status) { + if (U_FAILURE(status)) { + return 0; + } + if (this == &other) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + int32_t count = other.fLength; + if (count == 0) { + // Nothing to insert. + return 0; + } + int32_t position = prepareForInsert(index, count, status); + if (U_FAILURE(status)) { + return count; + } + for (int32_t i = 0; i < count; i++) { + getCharPtr()[position + i] = other.charAt(i); + getFieldPtr()[position + i] = other.fieldAt(i); + } + return count; +} + +void FormattedStringBuilder::writeTerminator(UErrorCode& status) { + int32_t position = prepareForInsert(fLength, 1, status); + if (U_FAILURE(status)) { + return; + } + getCharPtr()[position] = 0; + getFieldPtr()[position] = kUndefinedField; + fLength--; +} + +int32_t FormattedStringBuilder::prepareForInsert(int32_t index, int32_t count, UErrorCode &status) { + U_ASSERT(index >= 0); + U_ASSERT(index <= fLength); + U_ASSERT(count >= 0); + U_ASSERT(fZero >= 0); + U_ASSERT(fLength >= 0); + U_ASSERT(getCapacity() - fZero >= fLength); + if (U_FAILURE(status)) { + return count; + } + if (index == 0 && fZero - count >= 0) { + // Append to start + fZero -= count; + fLength += count; + return fZero; + } else if (index == fLength && count <= getCapacity() - fZero - fLength) { + // Append to end + fLength += count; + return fZero + fLength - count; + } else { + // Move chars around and/or allocate more space + return prepareForInsertHelper(index, count, status); + } +} + +int32_t FormattedStringBuilder::prepareForInsertHelper(int32_t index, int32_t count, UErrorCode &status) { + int32_t oldCapacity = getCapacity(); + int32_t oldZero = fZero; + char16_t *oldChars = getCharPtr(); + Field *oldFields = getFieldPtr(); + int32_t newLength; + if (uprv_add32_overflow(fLength, count, &newLength)) { + status = U_INPUT_TOO_LONG_ERROR; + return -1; + } + int32_t newZero; + if (newLength > oldCapacity) { + if (newLength > INT32_MAX / 2) { + // We do not support more than 1G char16_t in this code because + // dealing with >2G *bytes* can cause subtle bugs. + status = U_INPUT_TOO_LONG_ERROR; + return -1; + } + // Keep newCapacity also to at most 1G char16_t. + int32_t newCapacity = newLength * 2; + newZero = (newCapacity - newLength) / 2; + + // C++ note: malloc appears in two places: here and in the assignment operator. + auto newChars = static_cast (uprv_malloc(sizeof(char16_t) * static_cast(newCapacity))); + auto newFields = static_cast(uprv_malloc(sizeof(Field) * static_cast(newCapacity))); + if (newChars == nullptr || newFields == nullptr) { + uprv_free(newChars); + uprv_free(newFields); + status = U_MEMORY_ALLOCATION_ERROR; + return -1; + } + + // First copy the prefix and then the suffix, leaving room for the new chars that the + // caller wants to insert. + // C++ note: memcpy is OK because the src and dest do not overlap. + uprv_memcpy2(newChars + newZero, oldChars + oldZero, sizeof(char16_t) * index); + uprv_memcpy2(newChars + newZero + index + count, + oldChars + oldZero + index, + sizeof(char16_t) * (fLength - index)); + uprv_memcpy2(newFields + newZero, oldFields + oldZero, sizeof(Field) * index); + uprv_memcpy2(newFields + newZero + index + count, + oldFields + oldZero + index, + sizeof(Field) * (fLength - index)); + + if (fUsingHeap) { + uprv_free(oldChars); + uprv_free(oldFields); + } + fUsingHeap = true; + fChars.heap.ptr = newChars; + fChars.heap.capacity = newCapacity; + fFields.heap.ptr = newFields; + fFields.heap.capacity = newCapacity; + } else { + newZero = (oldCapacity - newLength) / 2; + + // C++ note: memmove is required because src and dest may overlap. + // First copy the entire string to the location of the prefix, and then move the suffix + // to make room for the new chars that the caller wants to insert. + uprv_memmove2(oldChars + newZero, oldChars + oldZero, sizeof(char16_t) * fLength); + uprv_memmove2(oldChars + newZero + index + count, + oldChars + newZero + index, + sizeof(char16_t) * (fLength - index)); + uprv_memmove2(oldFields + newZero, oldFields + oldZero, sizeof(Field) * fLength); + uprv_memmove2(oldFields + newZero + index + count, + oldFields + newZero + index, + sizeof(Field) * (fLength - index)); + } + fZero = newZero; + fLength = newLength; + return fZero + index; +} + +int32_t FormattedStringBuilder::remove(int32_t index, int32_t count) { + U_ASSERT(0 <= index); + U_ASSERT(index <= fLength); + U_ASSERT(count <= (fLength - index)); + U_ASSERT(index <= getCapacity() - fZero); + + int32_t position = index + fZero; + // TODO: Reset the heap here? (If the string after removal can fit on stack?) + uprv_memmove2(getCharPtr() + position, + getCharPtr() + position + count, + sizeof(char16_t) * (fLength - index - count)); + uprv_memmove2(getFieldPtr() + position, + getFieldPtr() + position + count, + sizeof(Field) * (fLength - index - count)); + fLength -= count; + return position; +} + +UnicodeString FormattedStringBuilder::toUnicodeString() const { + return UnicodeString(getCharPtr() + fZero, fLength); +} + +const UnicodeString FormattedStringBuilder::toTempUnicodeString() const { + // Readonly-alias constructor: + return UnicodeString(false, getCharPtr() + fZero, fLength); +} + +UnicodeString FormattedStringBuilder::toDebugString() const { + UnicodeString sb; + sb.append(u"", -1); + return sb; +} + +const char16_t *FormattedStringBuilder::chars() const { + return getCharPtr() + fZero; +} + +bool FormattedStringBuilder::contentEquals(const FormattedStringBuilder &other) const { + if (fLength != other.fLength) { + return false; + } + for (int32_t i = 0; i < fLength; i++) { + if (charAt(i) != other.charAt(i) || fieldAt(i) != other.fieldAt(i)) { + return false; + } + } + return true; +} + +bool FormattedStringBuilder::containsField(Field field) const { + for (int32_t i = 0; i < fLength; i++) { + if (field == fieldAt(i)) { + return true; + } + } + return false; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/formatted_string_builder.h b/deps/icu-small/source/i18n/formatted_string_builder.h index 32e0900ae23ac8..97c8ccf9b5621c 100644 --- a/deps/icu-small/source/i18n/formatted_string_builder.h +++ b/deps/icu-small/source/i18n/formatted_string_builder.h @@ -1,272 +1,272 @@ -// © 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_STRINGBUILDER_H__ -#define __NUMBER_STRINGBUILDER_H__ - - -#include -#include - -#include "cstring.h" -#include "uassert.h" -#include "fphdlimp.h" - -U_NAMESPACE_BEGIN - -class FormattedValueStringBuilderImpl; - -/** - * A StringBuilder optimized for formatting. It implements the following key - * features beyond a UnicodeString: - * - *

    - *
  1. Efficient prepend as well as append. - *
  2. Keeps track of Fields in an efficient manner. - *
- * - * See also FormattedValueStringBuilderImpl. - * - * @author sffc (Shane Carr) - */ -class U_I18N_API FormattedStringBuilder : public UMemory { - private: - static const int32_t DEFAULT_CAPACITY = 40; - - template - union ValueOrHeapArray { - T value[DEFAULT_CAPACITY]; - struct { - T *ptr; - int32_t capacity; - } heap; - }; - - public: - FormattedStringBuilder(); - - ~FormattedStringBuilder(); - - FormattedStringBuilder(const FormattedStringBuilder &other); - - // Convention: bottom 4 bits for field, top 4 bits for field category. - // Field category 0 implies the number category so that the number field - // literals can be directly passed as a Field type. - // Exported as U_I18N_API so it can be used by other exports on Windows. - struct U_I18N_API Field { - uint8_t bits; - - Field() = default; - constexpr Field(uint8_t category, uint8_t field); - - inline UFieldCategory getCategory() const; - inline int32_t getField() const; - inline bool isNumeric() const; - inline bool isUndefined() const; - inline bool operator==(const Field& other) const; - inline bool operator!=(const Field& other) const; - }; - - FormattedStringBuilder &operator=(const FormattedStringBuilder &other); - - int32_t length() const; - - int32_t codePointCount() const; - - inline char16_t charAt(int32_t index) const { - U_ASSERT(index >= 0); - U_ASSERT(index < fLength); - return getCharPtr()[fZero + index]; - } - - inline Field fieldAt(int32_t index) const { - U_ASSERT(index >= 0); - U_ASSERT(index < fLength); - return getFieldPtr()[fZero + index]; - } - - UChar32 getFirstCodePoint() const; - - UChar32 getLastCodePoint() const; - - UChar32 codePointAt(int32_t index) const; - - UChar32 codePointBefore(int32_t index) const; - - FormattedStringBuilder &clear(); - - /** Appends a UTF-16 code unit. */ - inline int32_t appendChar16(char16_t codeUnit, Field field, UErrorCode& status) { - // appendCodePoint handles both code units and code points. - return insertCodePoint(fLength, codeUnit, field, status); - } - - /** Inserts a UTF-16 code unit. Note: insert at index 0 is very efficient. */ - inline int32_t insertChar16(int32_t index, char16_t codeUnit, Field field, UErrorCode& status) { - // insertCodePoint handles both code units and code points. - return insertCodePoint(index, codeUnit, field, status); - } - - /** Appends a Unicode code point. */ - inline int32_t appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status) { - return insertCodePoint(fLength, codePoint, field, status); - } - - /** Inserts a Unicode code point. Note: insert at index 0 is very efficient. */ - int32_t insertCodePoint(int32_t index, UChar32 codePoint, Field field, UErrorCode &status); - - /** Appends a string. */ - inline int32_t append(const UnicodeString &unistr, Field field, UErrorCode &status) { - return insert(fLength, unistr, field, status); - } - - /** Inserts a string. Note: insert at index 0 is very efficient. */ - int32_t insert(int32_t index, const UnicodeString &unistr, Field field, UErrorCode &status); - - /** Inserts a substring. Note: insert at index 0 is very efficient. - * - * @param start Start index of the substring of unistr to be inserted. - * @param end End index of the substring of unistr to be inserted (exclusive). - */ - int32_t insert(int32_t index, const UnicodeString &unistr, int32_t start, int32_t end, Field field, - UErrorCode &status); - - /** Deletes a substring and then inserts a string at that same position. - * Similar to JavaScript Array.prototype.splice(). - * - * @param startThis Start of the span to delete. - * @param endThis End of the span to delete (exclusive). - * @param unistr The string to insert at the deletion position. - * @param startOther Start index of the substring of unistr to be inserted. - * @param endOther End index of the substring of unistr to be inserted (exclusive). - */ - int32_t splice(int32_t startThis, int32_t endThis, const UnicodeString &unistr, - int32_t startOther, int32_t endOther, Field field, UErrorCode& status); - - /** Appends a formatted string. */ - int32_t append(const FormattedStringBuilder &other, UErrorCode &status); - - /** Inserts a formatted string. Note: insert at index 0 is very efficient. */ - int32_t insert(int32_t index, const FormattedStringBuilder &other, UErrorCode &status); - - /** - * Ensures that the string buffer contains a NUL terminator. The NUL terminator does - * not count toward the string length. Any further changes to the string (insert or - * append) may invalidate the NUL terminator. - * - * You should call this method after the formatted string is completely built if you - * plan to return a pointer to the string from a C API. - */ - void writeTerminator(UErrorCode& status); - - /** - * Gets a "safe" UnicodeString that can be used even after the FormattedStringBuilder is destructed. - */ - UnicodeString toUnicodeString() const; - - /** - * Gets an "unsafe" UnicodeString that is valid only as long as the FormattedStringBuilder is alive and - * unchanged. Slightly faster than toUnicodeString(). - */ - const UnicodeString toTempUnicodeString() const; - - UnicodeString toDebugString() const; - - const char16_t *chars() const; - - bool contentEquals(const FormattedStringBuilder &other) const; - - bool containsField(Field field) const; - - private: - bool fUsingHeap = false; - ValueOrHeapArray fChars; - ValueOrHeapArray fFields; - int32_t fZero = DEFAULT_CAPACITY / 2; - int32_t fLength = 0; - - inline char16_t *getCharPtr() { - return fUsingHeap ? fChars.heap.ptr : fChars.value; - } - - inline const char16_t *getCharPtr() const { - return fUsingHeap ? fChars.heap.ptr : fChars.value; - } - - inline Field *getFieldPtr() { - return fUsingHeap ? fFields.heap.ptr : fFields.value; - } - - inline const Field *getFieldPtr() const { - return fUsingHeap ? fFields.heap.ptr : fFields.value; - } - - inline int32_t getCapacity() const { - return fUsingHeap ? fChars.heap.capacity : DEFAULT_CAPACITY; - } - - int32_t prepareForInsert(int32_t index, int32_t count, UErrorCode &status); - - int32_t prepareForInsertHelper(int32_t index, int32_t count, UErrorCode &status); - - int32_t remove(int32_t index, int32_t count); - - friend class FormattedValueStringBuilderImpl; -}; - -static_assert( - // std::is_pod<> is deprecated. - std::is_standard_layout::value && - std::is_trivial::value, - "Field should be a POD type for efficient initialization"); - -constexpr FormattedStringBuilder::Field::Field(uint8_t category, uint8_t field) - : bits(( - U_ASSERT(category <= 0xf), - U_ASSERT(field <= 0xf), - static_cast((category << 4) | field) - )) {} - -/** - * Internal constant for the undefined field for use in FormattedStringBuilder. - */ -constexpr FormattedStringBuilder::Field kUndefinedField = {UFIELD_CATEGORY_UNDEFINED, 0}; - -/** - * Internal field to signal "numeric" when fields are not supported in NumberFormat. - */ -constexpr FormattedStringBuilder::Field kGeneralNumericField = {UFIELD_CATEGORY_UNDEFINED, 1}; - -inline UFieldCategory FormattedStringBuilder::Field::getCategory() const { - return static_cast(bits >> 4); -} - -inline int32_t FormattedStringBuilder::Field::getField() const { - return bits & 0xf; -} - -inline bool FormattedStringBuilder::Field::isNumeric() const { - return getCategory() == UFIELD_CATEGORY_NUMBER || *this == kGeneralNumericField; -} - -inline bool FormattedStringBuilder::Field::isUndefined() const { - return getCategory() == UFIELD_CATEGORY_UNDEFINED; -} - -inline bool FormattedStringBuilder::Field::operator==(const Field& other) const { - return bits == other.bits; -} - -inline bool FormattedStringBuilder::Field::operator!=(const Field& other) const { - return bits != other.bits; -} - -U_NAMESPACE_END - - -#endif //__NUMBER_STRINGBUILDER_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_STRINGBUILDER_H__ +#define __NUMBER_STRINGBUILDER_H__ + + +#include +#include + +#include "cstring.h" +#include "uassert.h" +#include "fphdlimp.h" + +U_NAMESPACE_BEGIN + +class FormattedValueStringBuilderImpl; + +/** + * A StringBuilder optimized for formatting. It implements the following key + * features beyond a UnicodeString: + * + *
    + *
  1. Efficient prepend as well as append. + *
  2. Keeps track of Fields in an efficient manner. + *
+ * + * See also FormattedValueStringBuilderImpl. + * + * @author sffc (Shane Carr) + */ +class U_I18N_API FormattedStringBuilder : public UMemory { + private: + static const int32_t DEFAULT_CAPACITY = 40; + + template + union ValueOrHeapArray { + T value[DEFAULT_CAPACITY]; + struct { + T *ptr; + int32_t capacity; + } heap; + }; + + public: + FormattedStringBuilder(); + + ~FormattedStringBuilder(); + + FormattedStringBuilder(const FormattedStringBuilder &other); + + // Convention: bottom 4 bits for field, top 4 bits for field category. + // Field category 0 implies the number category so that the number field + // literals can be directly passed as a Field type. + // Exported as U_I18N_API so it can be used by other exports on Windows. + struct U_I18N_API Field { + uint8_t bits; + + Field() = default; + constexpr Field(uint8_t category, uint8_t field); + + inline UFieldCategory getCategory() const; + inline int32_t getField() const; + inline bool isNumeric() const; + inline bool isUndefined() const; + inline bool operator==(const Field& other) const; + inline bool operator!=(const Field& other) const; + }; + + FormattedStringBuilder &operator=(const FormattedStringBuilder &other); + + int32_t length() const; + + int32_t codePointCount() const; + + inline char16_t charAt(int32_t index) const { + U_ASSERT(index >= 0); + U_ASSERT(index < fLength); + return getCharPtr()[fZero + index]; + } + + inline Field fieldAt(int32_t index) const { + U_ASSERT(index >= 0); + U_ASSERT(index < fLength); + return getFieldPtr()[fZero + index]; + } + + UChar32 getFirstCodePoint() const; + + UChar32 getLastCodePoint() const; + + UChar32 codePointAt(int32_t index) const; + + UChar32 codePointBefore(int32_t index) const; + + FormattedStringBuilder &clear(); + + /** Appends a UTF-16 code unit. */ + inline int32_t appendChar16(char16_t codeUnit, Field field, UErrorCode& status) { + // appendCodePoint handles both code units and code points. + return insertCodePoint(fLength, codeUnit, field, status); + } + + /** Inserts a UTF-16 code unit. Note: insert at index 0 is very efficient. */ + inline int32_t insertChar16(int32_t index, char16_t codeUnit, Field field, UErrorCode& status) { + // insertCodePoint handles both code units and code points. + return insertCodePoint(index, codeUnit, field, status); + } + + /** Appends a Unicode code point. */ + inline int32_t appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status) { + return insertCodePoint(fLength, codePoint, field, status); + } + + /** Inserts a Unicode code point. Note: insert at index 0 is very efficient. */ + int32_t insertCodePoint(int32_t index, UChar32 codePoint, Field field, UErrorCode &status); + + /** Appends a string. */ + inline int32_t append(const UnicodeString &unistr, Field field, UErrorCode &status) { + return insert(fLength, unistr, field, status); + } + + /** Inserts a string. Note: insert at index 0 is very efficient. */ + int32_t insert(int32_t index, const UnicodeString &unistr, Field field, UErrorCode &status); + + /** Inserts a substring. Note: insert at index 0 is very efficient. + * + * @param start Start index of the substring of unistr to be inserted. + * @param end End index of the substring of unistr to be inserted (exclusive). + */ + int32_t insert(int32_t index, const UnicodeString &unistr, int32_t start, int32_t end, Field field, + UErrorCode &status); + + /** Deletes a substring and then inserts a string at that same position. + * Similar to JavaScript Array.prototype.splice(). + * + * @param startThis Start of the span to delete. + * @param endThis End of the span to delete (exclusive). + * @param unistr The string to insert at the deletion position. + * @param startOther Start index of the substring of unistr to be inserted. + * @param endOther End index of the substring of unistr to be inserted (exclusive). + */ + int32_t splice(int32_t startThis, int32_t endThis, const UnicodeString &unistr, + int32_t startOther, int32_t endOther, Field field, UErrorCode& status); + + /** Appends a formatted string. */ + int32_t append(const FormattedStringBuilder &other, UErrorCode &status); + + /** Inserts a formatted string. Note: insert at index 0 is very efficient. */ + int32_t insert(int32_t index, const FormattedStringBuilder &other, UErrorCode &status); + + /** + * Ensures that the string buffer contains a NUL terminator. The NUL terminator does + * not count toward the string length. Any further changes to the string (insert or + * append) may invalidate the NUL terminator. + * + * You should call this method after the formatted string is completely built if you + * plan to return a pointer to the string from a C API. + */ + void writeTerminator(UErrorCode& status); + + /** + * Gets a "safe" UnicodeString that can be used even after the FormattedStringBuilder is destructed. + */ + UnicodeString toUnicodeString() const; + + /** + * Gets an "unsafe" UnicodeString that is valid only as long as the FormattedStringBuilder is alive and + * unchanged. Slightly faster than toUnicodeString(). + */ + const UnicodeString toTempUnicodeString() const; + + UnicodeString toDebugString() const; + + const char16_t *chars() const; + + bool contentEquals(const FormattedStringBuilder &other) const; + + bool containsField(Field field) const; + + private: + bool fUsingHeap = false; + ValueOrHeapArray fChars; + ValueOrHeapArray fFields; + int32_t fZero = DEFAULT_CAPACITY / 2; + int32_t fLength = 0; + + inline char16_t *getCharPtr() { + return fUsingHeap ? fChars.heap.ptr : fChars.value; + } + + inline const char16_t *getCharPtr() const { + return fUsingHeap ? fChars.heap.ptr : fChars.value; + } + + inline Field *getFieldPtr() { + return fUsingHeap ? fFields.heap.ptr : fFields.value; + } + + inline const Field *getFieldPtr() const { + return fUsingHeap ? fFields.heap.ptr : fFields.value; + } + + inline int32_t getCapacity() const { + return fUsingHeap ? fChars.heap.capacity : DEFAULT_CAPACITY; + } + + int32_t prepareForInsert(int32_t index, int32_t count, UErrorCode &status); + + int32_t prepareForInsertHelper(int32_t index, int32_t count, UErrorCode &status); + + int32_t remove(int32_t index, int32_t count); + + friend class FormattedValueStringBuilderImpl; +}; + +static_assert( + // std::is_pod<> is deprecated. + std::is_standard_layout::value && + std::is_trivial::value, + "Field should be a POD type for efficient initialization"); + +constexpr FormattedStringBuilder::Field::Field(uint8_t category, uint8_t field) + : bits(( + U_ASSERT(category <= 0xf), + U_ASSERT(field <= 0xf), + static_cast((category << 4) | field) + )) {} + +/** + * Internal constant for the undefined field for use in FormattedStringBuilder. + */ +constexpr FormattedStringBuilder::Field kUndefinedField = {UFIELD_CATEGORY_UNDEFINED, 0}; + +/** + * Internal field to signal "numeric" when fields are not supported in NumberFormat. + */ +constexpr FormattedStringBuilder::Field kGeneralNumericField = {UFIELD_CATEGORY_UNDEFINED, 1}; + +inline UFieldCategory FormattedStringBuilder::Field::getCategory() const { + return static_cast(bits >> 4); +} + +inline int32_t FormattedStringBuilder::Field::getField() const { + return bits & 0xf; +} + +inline bool FormattedStringBuilder::Field::isNumeric() const { + return getCategory() == UFIELD_CATEGORY_NUMBER || *this == kGeneralNumericField; +} + +inline bool FormattedStringBuilder::Field::isUndefined() const { + return getCategory() == UFIELD_CATEGORY_UNDEFINED; +} + +inline bool FormattedStringBuilder::Field::operator==(const Field& other) const { + return bits == other.bits; +} + +inline bool FormattedStringBuilder::Field::operator!=(const Field& other) const { + return bits != other.bits; +} + +U_NAMESPACE_END + + +#endif //__NUMBER_STRINGBUILDER_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/formattedval_impl.h b/deps/icu-small/source/i18n/formattedval_impl.h index 2b9a3970d2e4eb..fe4766210b1690 100644 --- a/deps/icu-small/source/i18n/formattedval_impl.h +++ b/deps/icu-small/source/i18n/formattedval_impl.h @@ -1,315 +1,318 @@ -// © 2018 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#ifndef __FORMVAL_IMPL_H__ -#define __FORMVAL_IMPL_H__ - -#include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING - -// This file contains compliant implementations of FormattedValue which can be -// leveraged by ICU formatters. -// -// Each implementation is defined in its own cpp file in order to split -// dependencies more modularly. - -#include "unicode/formattedvalue.h" -#include "capi_helper.h" -#include "fphdlimp.h" -#include "util.h" -#include "uvectr32.h" -#include "formatted_string_builder.h" - - -/** - * Represents the type of constraint for ConstrainedFieldPosition. - * - * Constraints are used to control the behavior of iteration in FormattedValue. - * - * @internal - */ -typedef enum UCFPosConstraintType { - /** - * Represents the lack of a constraint. - * - * This is the value of fConstraint if no "constrain" methods were called. - * - * @internal - */ - UCFPOS_CONSTRAINT_NONE = 0, - - /** - * Represents that the field category is constrained. - * - * This is the value of fConstraint if constraintCategory was called. - * - * FormattedValue implementations should not change the field category - * while this constraint is active. - * - * @internal - */ - UCFPOS_CONSTRAINT_CATEGORY, - - /** - * Represents that the field and field category are constrained. - * - * This is the value of fConstraint if constraintField was called. - * - * FormattedValue implementations should not change the field or field category - * while this constraint is active. - * - * @internal - */ - UCFPOS_CONSTRAINT_FIELD -} UCFPosConstraintType; - - -U_NAMESPACE_BEGIN - - -/** - * Implementation of FormattedValue using FieldPositionHandler to accept fields. - * - * TODO(ICU-20897): This class is unused. If it is not needed when fixing ICU-20897, - * it should be deleted. - */ -class FormattedValueFieldPositionIteratorImpl : public UMemory, public FormattedValue { -public: - - /** @param initialFieldCapacity Initially allocate space for this many fields. */ - FormattedValueFieldPositionIteratorImpl(int32_t initialFieldCapacity, UErrorCode& status); - - virtual ~FormattedValueFieldPositionIteratorImpl(); - - // Implementation of FormattedValue (const): - - UnicodeString toString(UErrorCode& status) const U_OVERRIDE; - UnicodeString toTempString(UErrorCode& status) const U_OVERRIDE; - Appendable& appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE; - UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE; - - // Additional methods used during construction phase only (non-const): - - FieldPositionIteratorHandler getHandler(UErrorCode& status); - void appendString(UnicodeString string, UErrorCode& status); - - /** - * Computes the spans for duplicated values. - * For example, if the string has fields: - * - * ...aa..[b.cc]..d.[bb.e.c]..a.. - * - * then the spans will be the bracketed regions. - * - * Assumes that the currently known fields are sorted - * and all in the same category. - */ - void addOverlapSpans(UFieldCategory spanCategory, int8_t firstIndex, UErrorCode& status); - - /** - * Sorts the fields: start index first, length second. - */ - void sort(); - -private: - UnicodeString fString; - UVector32 fFields; -}; - - -// Internal struct that must be exported for MSVC -struct U_I18N_API SpanInfo { - UFieldCategory category; - int32_t spanValue; - int32_t start; - int32_t length; -}; - -// Export an explicit template instantiation of the MaybeStackArray that -// is used as a data member of CEBuffer. -// -// 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 - -/** - * Implementation of FormattedValue based on FormattedStringBuilder. - * - * The implementation currently revolves around numbers and number fields. - * However, it can be generalized in the future when there is a need. - * - * @author sffc (Shane Carr) - */ -// Exported as U_I18N_API for tests -class U_I18N_API FormattedValueStringBuilderImpl : public UMemory, public FormattedValue { -public: - - FormattedValueStringBuilderImpl(FormattedStringBuilder::Field numericField); - - virtual ~FormattedValueStringBuilderImpl(); - - // Implementation of FormattedValue (const): - - UnicodeString toString(UErrorCode& status) const U_OVERRIDE; - UnicodeString toTempString(UErrorCode& status) const U_OVERRIDE; - Appendable& appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE; - UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE; - - // Additional helper functions: - UBool nextFieldPosition(FieldPosition& fp, UErrorCode& status) const; - void getAllFieldPositions(FieldPositionIteratorHandler& fpih, UErrorCode& status) const; - inline FormattedStringBuilder& getStringRef() { - return fString; - } - inline const FormattedStringBuilder& getStringRef() const { - return fString; - } - void resetString(); - - /** - * Adds additional metadata used for span fields. - * - * category: the category to use for the span field. - * spanValue: the value of the span field: index of the list item, for example. - * start: the start position within the string of the span. -1 if unknown. - * length: the length of the span, used to split adjacent fields. - */ - void appendSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status); - void prependSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status); - -private: - FormattedStringBuilder fString; - FormattedStringBuilder::Field fNumericField; - MaybeStackArray spanIndices; - int32_t spanIndicesCount = 0; - - bool nextPositionImpl(ConstrainedFieldPosition& cfpos, FormattedStringBuilder::Field numericField, UErrorCode& status) const; - static bool isIntOrGroup(FormattedStringBuilder::Field field); - static bool isTrimmable(FormattedStringBuilder::Field field); - int32_t trimBack(int32_t limit) const; - int32_t trimFront(int32_t start) const; -}; - - -// C API Helpers for FormattedValue -// Magic number as ASCII == "UFV" -struct UFormattedValueImpl; -typedef IcuCApiHelper UFormattedValueApiHelper; -struct UFormattedValueImpl : public UMemory, public UFormattedValueApiHelper { - // This pointer should be set by the child class. - FormattedValue* fFormattedValue = nullptr; -}; - - -/** Boilerplate to check for valid status before dereferencing the fData pointer. */ -#define UPRV_FORMATTED_VALUE_METHOD_GUARD(returnExpression) \ - if (U_FAILURE(status)) { \ - return returnExpression; \ - } \ - if (fData == nullptr) { \ - status = fErrorCode; \ - return returnExpression; \ - } \ - - -/** Implementation of the methods from U_FORMATTED_VALUE_SUBCLASS_AUTO. */ -#define UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(Name) \ - Name::Name(Name&& src) U_NOEXCEPT \ - : fData(src.fData), fErrorCode(src.fErrorCode) { \ - src.fData = nullptr; \ - src.fErrorCode = U_INVALID_STATE_ERROR; \ - } \ - Name::~Name() { \ - delete fData; \ - fData = nullptr; \ - } \ - Name& Name::operator=(Name&& src) U_NOEXCEPT { \ - delete fData; \ - fData = src.fData; \ - src.fData = nullptr; \ - fErrorCode = src.fErrorCode; \ - src.fErrorCode = U_INVALID_STATE_ERROR; \ - return *this; \ - } \ - UnicodeString Name::toString(UErrorCode& status) const { \ - UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \ - return fData->toString(status); \ - } \ - UnicodeString Name::toTempString(UErrorCode& status) const { \ - UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \ - return fData->toTempString(status); \ - } \ - Appendable& Name::appendTo(Appendable& appendable, UErrorCode& status) const { \ - UPRV_FORMATTED_VALUE_METHOD_GUARD(appendable) \ - return fData->appendTo(appendable, status); \ - } \ - UBool Name::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const { \ - UPRV_FORMATTED_VALUE_METHOD_GUARD(false) \ - return fData->nextPosition(cfpos, status); \ - } - - -/** Like UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL but without impl type declarations. */ -#define UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix) \ - U_CAPI CType* U_EXPORT2 \ - Prefix ## _openResult (UErrorCode* ec) { \ - if (U_FAILURE(*ec)) { \ - return nullptr; \ - } \ - ImplType* impl = new ImplType(); \ - if (impl == nullptr) { \ - *ec = U_MEMORY_ALLOCATION_ERROR; \ - return nullptr; \ - } \ - return static_cast(impl)->exportForC(); \ - } \ - U_CAPI const UFormattedValue* U_EXPORT2 \ - Prefix ## _resultAsValue (const CType* uresult, UErrorCode* ec) { \ - const ImplType* result = HelperType::validate(uresult, *ec); \ - if (U_FAILURE(*ec)) { return nullptr; } \ - return static_cast(result)->exportConstForC(); \ - } \ - U_CAPI void U_EXPORT2 \ - Prefix ## _closeResult (CType* uresult) { \ - UErrorCode localStatus = U_ZERO_ERROR; \ - const ImplType* impl = HelperType::validate(uresult, localStatus); \ - delete impl; \ - } - - -/** - * Implementation of the standard methods for a UFormattedValue "subclass" C API. - * @param CPPType The public C++ type, like FormattedList - * @param CType The public C type, like UFormattedList - * @param ImplType A name to use for the implementation class - * @param HelperType A name to use for the "mixin" typedef for C API conversion - * @param Prefix The C API prefix, like ulistfmt - * @param MagicNumber A unique 32-bit number to use to identify this type - */ -#define UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(CPPType, CType, ImplType, HelperType, Prefix, MagicNumber) \ - U_NAMESPACE_BEGIN \ - class ImplType; \ - typedef IcuCApiHelper HelperType; \ - class ImplType : public UFormattedValueImpl, public HelperType { \ - public: \ - ImplType(); \ - ~ImplType(); \ - CPPType fImpl; \ - }; \ - ImplType::ImplType() { \ - fFormattedValue = &fImpl; \ - } \ - ImplType::~ImplType() {} \ - U_NAMESPACE_END \ - UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix) - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ -#endif // __FORMVAL_IMPL_H__ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#ifndef __FORMVAL_IMPL_H__ +#define __FORMVAL_IMPL_H__ + +#include "unicode/utypes.h" +#if !UCONFIG_NO_FORMATTING + +// This file contains compliant implementations of FormattedValue which can be +// leveraged by ICU formatters. +// +// Each implementation is defined in its own cpp file in order to split +// dependencies more modularly. + +#include "unicode/formattedvalue.h" +#include "capi_helper.h" +#include "fphdlimp.h" +#include "util.h" +#include "uvectr32.h" +#include "formatted_string_builder.h" + + +/** + * Represents the type of constraint for ConstrainedFieldPosition. + * + * Constraints are used to control the behavior of iteration in FormattedValue. + * + * @internal + */ +typedef enum UCFPosConstraintType { + /** + * Represents the lack of a constraint. + * + * This is the value of fConstraint if no "constrain" methods were called. + * + * @internal + */ + UCFPOS_CONSTRAINT_NONE = 0, + + /** + * Represents that the field category is constrained. + * + * This is the value of fConstraint if constraintCategory was called. + * + * FormattedValue implementations should not change the field category + * while this constraint is active. + * + * @internal + */ + UCFPOS_CONSTRAINT_CATEGORY, + + /** + * Represents that the field and field category are constrained. + * + * This is the value of fConstraint if constraintField was called. + * + * FormattedValue implementations should not change the field or field category + * while this constraint is active. + * + * @internal + */ + UCFPOS_CONSTRAINT_FIELD +} UCFPosConstraintType; + + +U_NAMESPACE_BEGIN + + +/** + * Implementation of FormattedValue using FieldPositionHandler to accept fields. + * + * TODO(ICU-20897): This class is unused. If it is not needed when fixing ICU-20897, + * it should be deleted. + */ +class FormattedValueFieldPositionIteratorImpl : public UMemory, public FormattedValue { +public: + + /** @param initialFieldCapacity Initially allocate space for this many fields. */ + FormattedValueFieldPositionIteratorImpl(int32_t initialFieldCapacity, UErrorCode& status); + + virtual ~FormattedValueFieldPositionIteratorImpl(); + + // Implementation of FormattedValue (const): + + UnicodeString toString(UErrorCode& status) const override; + UnicodeString toTempString(UErrorCode& status) const override; + Appendable& appendTo(Appendable& appendable, UErrorCode& status) const override; + UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const override; + + // Additional methods used during construction phase only (non-const): + + FieldPositionIteratorHandler getHandler(UErrorCode& status); + void appendString(UnicodeString string, UErrorCode& status); + + /** + * Computes the spans for duplicated values. + * For example, if the string has fields: + * + * ...aa..[b.cc]..d.[bb.e.c]..a.. + * + * then the spans will be the bracketed regions. + * + * Assumes that the currently known fields are sorted + * and all in the same category. + */ + void addOverlapSpans(UFieldCategory spanCategory, int8_t firstIndex, UErrorCode& status); + + /** + * Sorts the fields: start index first, length second. + */ + void sort(); + +private: + UnicodeString fString; + UVector32 fFields; +}; + + +// Internal struct that must be exported for MSVC +struct U_I18N_API SpanInfo { + UFieldCategory category; + int32_t spanValue; + int32_t start; + int32_t length; +}; + +// Export an explicit template instantiation of the MaybeStackArray that +// is used as a data member of CEBuffer. +// +// 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 + +/** + * Implementation of FormattedValue based on FormattedStringBuilder. + * + * The implementation currently revolves around numbers and number fields. + * However, it can be generalized in the future when there is a need. + * + * @author sffc (Shane Carr) + */ +// Exported as U_I18N_API for tests +class U_I18N_API FormattedValueStringBuilderImpl : public UMemory, public FormattedValue { +public: + + FormattedValueStringBuilderImpl(FormattedStringBuilder::Field numericField); + + virtual ~FormattedValueStringBuilderImpl(); + + FormattedValueStringBuilderImpl(FormattedValueStringBuilderImpl&&) = default; + FormattedValueStringBuilderImpl& operator=(FormattedValueStringBuilderImpl&&) = default; + + // Implementation of FormattedValue (const): + + UnicodeString toString(UErrorCode& status) const override; + UnicodeString toTempString(UErrorCode& status) const override; + Appendable& appendTo(Appendable& appendable, UErrorCode& status) const override; + UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const override; + + // Additional helper functions: + UBool nextFieldPosition(FieldPosition& fp, UErrorCode& status) const; + void getAllFieldPositions(FieldPositionIteratorHandler& fpih, UErrorCode& status) const; + inline FormattedStringBuilder& getStringRef() { + return fString; + } + inline const FormattedStringBuilder& getStringRef() const { + return fString; + } + void resetString(); + + /** + * Adds additional metadata used for span fields. + * + * category: the category to use for the span field. + * spanValue: the value of the span field: index of the list item, for example. + * start: the start position within the string of the span. -1 if unknown. + * length: the length of the span, used to split adjacent fields. + */ + void appendSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status); + void prependSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status); + +private: + FormattedStringBuilder fString; + FormattedStringBuilder::Field fNumericField; + MaybeStackArray spanIndices; + int32_t spanIndicesCount = 0; + + bool nextPositionImpl(ConstrainedFieldPosition& cfpos, FormattedStringBuilder::Field numericField, UErrorCode& status) const; + static bool isIntOrGroup(FormattedStringBuilder::Field field); + static bool isTrimmable(FormattedStringBuilder::Field field); + int32_t trimBack(int32_t limit) const; + int32_t trimFront(int32_t start) const; +}; + + +// C API Helpers for FormattedValue +// Magic number as ASCII == "UFV" +struct UFormattedValueImpl; +typedef IcuCApiHelper UFormattedValueApiHelper; +struct UFormattedValueImpl : public UMemory, public UFormattedValueApiHelper { + // This pointer should be set by the child class. + FormattedValue* fFormattedValue = nullptr; +}; + + +/** Boilerplate to check for valid status before dereferencing the fData pointer. */ +#define UPRV_FORMATTED_VALUE_METHOD_GUARD(returnExpression) \ + if (U_FAILURE(status)) { \ + return returnExpression; \ + } \ + if (fData == nullptr) { \ + status = fErrorCode; \ + return returnExpression; \ + } \ + + +/** Implementation of the methods from U_FORMATTED_VALUE_SUBCLASS_AUTO. */ +#define UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(Name) \ + Name::Name(Name&& src) noexcept \ + : fData(src.fData), fErrorCode(src.fErrorCode) { \ + src.fData = nullptr; \ + src.fErrorCode = U_INVALID_STATE_ERROR; \ + } \ + Name::~Name() { \ + delete fData; \ + fData = nullptr; \ + } \ + Name& Name::operator=(Name&& src) noexcept { \ + delete fData; \ + fData = src.fData; \ + src.fData = nullptr; \ + fErrorCode = src.fErrorCode; \ + src.fErrorCode = U_INVALID_STATE_ERROR; \ + return *this; \ + } \ + UnicodeString Name::toString(UErrorCode& status) const { \ + UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \ + return fData->toString(status); \ + } \ + UnicodeString Name::toTempString(UErrorCode& status) const { \ + UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \ + return fData->toTempString(status); \ + } \ + Appendable& Name::appendTo(Appendable& appendable, UErrorCode& status) const { \ + UPRV_FORMATTED_VALUE_METHOD_GUARD(appendable) \ + return fData->appendTo(appendable, status); \ + } \ + UBool Name::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const { \ + UPRV_FORMATTED_VALUE_METHOD_GUARD(false) \ + return fData->nextPosition(cfpos, status); \ + } + + +/** Like UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL but without impl type declarations. */ +#define UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix) \ + U_CAPI CType* U_EXPORT2 \ + Prefix ## _openResult (UErrorCode* ec) { \ + if (U_FAILURE(*ec)) { \ + return nullptr; \ + } \ + ImplType* impl = new ImplType(); \ + if (impl == nullptr) { \ + *ec = U_MEMORY_ALLOCATION_ERROR; \ + return nullptr; \ + } \ + return static_cast(impl)->exportForC(); \ + } \ + U_CAPI const UFormattedValue* U_EXPORT2 \ + Prefix ## _resultAsValue (const CType* uresult, UErrorCode* ec) { \ + const ImplType* result = HelperType::validate(uresult, *ec); \ + if (U_FAILURE(*ec)) { return nullptr; } \ + return static_cast(result)->exportConstForC(); \ + } \ + U_CAPI void U_EXPORT2 \ + Prefix ## _closeResult (CType* uresult) { \ + UErrorCode localStatus = U_ZERO_ERROR; \ + const ImplType* impl = HelperType::validate(uresult, localStatus); \ + delete impl; \ + } + + +/** + * Implementation of the standard methods for a UFormattedValue "subclass" C API. + * @param CPPType The public C++ type, like FormattedList + * @param CType The public C type, like UFormattedList + * @param ImplType A name to use for the implementation class + * @param HelperType A name to use for the "mixin" typedef for C API conversion + * @param Prefix The C API prefix, like ulistfmt + * @param MagicNumber A unique 32-bit number to use to identify this type + */ +#define UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(CPPType, CType, ImplType, HelperType, Prefix, MagicNumber) \ + U_NAMESPACE_BEGIN \ + class ImplType; \ + typedef IcuCApiHelper HelperType; \ + class ImplType : public UFormattedValueImpl, public HelperType { \ + public: \ + ImplType(); \ + ~ImplType(); \ + CPPType fImpl; \ + }; \ + ImplType::ImplType() { \ + fFormattedValue = &fImpl; \ + } \ + ImplType::~ImplType() {} \ + U_NAMESPACE_END \ + UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix) + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ +#endif // __FORMVAL_IMPL_H__ diff --git a/deps/icu-small/source/i18n/formattedval_iterimpl.cpp b/deps/icu-small/source/i18n/formattedval_iterimpl.cpp index ec770e2191d615..832b04912e7f0c 100644 --- a/deps/icu-small/source/i18n/formattedval_iterimpl.cpp +++ b/deps/icu-small/source/i18n/formattedval_iterimpl.cpp @@ -1,176 +1,176 @@ -// © 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 - -// This file contains one implementation of FormattedValue. -// Other independent implementations should go into their own cpp file for -// better dependency modularization. - -#include "formattedval_impl.h" -#include "putilimp.h" - -U_NAMESPACE_BEGIN - - -FormattedValueFieldPositionIteratorImpl::FormattedValueFieldPositionIteratorImpl( - int32_t initialFieldCapacity, - UErrorCode& status) - : fFields(initialFieldCapacity * 4, status) { -} - -FormattedValueFieldPositionIteratorImpl::~FormattedValueFieldPositionIteratorImpl() = default; - -UnicodeString FormattedValueFieldPositionIteratorImpl::toString( - UErrorCode&) const { - return fString; -} - -UnicodeString FormattedValueFieldPositionIteratorImpl::toTempString( - UErrorCode&) const { - // The alias must point to memory owned by this object; - // fastCopyFrom doesn't do this when using a stack buffer. - return UnicodeString(true, fString.getBuffer(), fString.length()); -} - -Appendable& FormattedValueFieldPositionIteratorImpl::appendTo( - Appendable& appendable, - UErrorCode&) const { - appendable.appendString(fString.getBuffer(), fString.length()); - return appendable; -} - -UBool FormattedValueFieldPositionIteratorImpl::nextPosition( - ConstrainedFieldPosition& cfpos, - UErrorCode&) const { - U_ASSERT(fFields.size() % 4 == 0); - int32_t numFields = fFields.size() / 4; - int32_t i = static_cast(cfpos.getInt64IterationContext()); - for (; i < numFields; i++) { - UFieldCategory category = static_cast(fFields.elementAti(i * 4)); - int32_t field = fFields.elementAti(i * 4 + 1); - if (cfpos.matchesField(category, field)) { - int32_t start = fFields.elementAti(i * 4 + 2); - int32_t limit = fFields.elementAti(i * 4 + 3); - cfpos.setState(category, field, start, limit); - break; - } - } - cfpos.setInt64IterationContext(i == numFields ? i : i + 1); - return i < numFields; -} - - -FieldPositionIteratorHandler FormattedValueFieldPositionIteratorImpl::getHandler( - UErrorCode& status) { - return FieldPositionIteratorHandler(&fFields, status); -} - -void FormattedValueFieldPositionIteratorImpl::appendString( - UnicodeString string, - UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - fString.append(string); - // Make the string NUL-terminated - if (fString.getTerminatedBuffer() == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } -} - - -void FormattedValueFieldPositionIteratorImpl::addOverlapSpans( - UFieldCategory spanCategory, - int8_t firstIndex, - UErrorCode& status) { - // In order to avoid fancy data structures, this is an O(N^2) algorithm, - // which should be fine for all real-life applications of this function. - int32_t s1a = INT32_MAX; - int32_t s1b = 0; - int32_t s2a = INT32_MAX; - int32_t s2b = 0; - int32_t numFields = fFields.size() / 4; - for (int32_t i = 0; i higher rank - comparison = start2 - start1; - } else if (limit1 != limit2) { - // Higher length (end index) -> lower rank - comparison = limit1 - limit2; - } else if (categ1 != categ2) { - // Higher field category -> lower rank - comparison = categ1 - categ2; - } else if (field1 != field2) { - // Higher field -> higher rank - comparison = field2 - field1; - } - if (comparison < 0) { - // Perform a swap - isSorted = false; - fFields.setElementAt(categ2, i*4 + 0); - fFields.setElementAt(field2, i*4 + 1); - fFields.setElementAt(start2, i*4 + 2); - fFields.setElementAt(limit2, i*4 + 3); - fFields.setElementAt(categ1, i*4 + 4); - fFields.setElementAt(field1, i*4 + 5); - fFields.setElementAt(start1, i*4 + 6); - fFields.setElementAt(limit1, i*4 + 7); - } - } - if (isSorted) { - break; - } - } -} - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +// This file contains one implementation of FormattedValue. +// Other independent implementations should go into their own cpp file for +// better dependency modularization. + +#include "formattedval_impl.h" +#include "putilimp.h" + +U_NAMESPACE_BEGIN + + +FormattedValueFieldPositionIteratorImpl::FormattedValueFieldPositionIteratorImpl( + int32_t initialFieldCapacity, + UErrorCode& status) + : fFields(initialFieldCapacity * 4, status) { +} + +FormattedValueFieldPositionIteratorImpl::~FormattedValueFieldPositionIteratorImpl() = default; + +UnicodeString FormattedValueFieldPositionIteratorImpl::toString( + UErrorCode&) const { + return fString; +} + +UnicodeString FormattedValueFieldPositionIteratorImpl::toTempString( + UErrorCode&) const { + // The alias must point to memory owned by this object; + // fastCopyFrom doesn't do this when using a stack buffer. + return UnicodeString(true, fString.getBuffer(), fString.length()); +} + +Appendable& FormattedValueFieldPositionIteratorImpl::appendTo( + Appendable& appendable, + UErrorCode&) const { + appendable.appendString(fString.getBuffer(), fString.length()); + return appendable; +} + +UBool FormattedValueFieldPositionIteratorImpl::nextPosition( + ConstrainedFieldPosition& cfpos, + UErrorCode&) const { + U_ASSERT(fFields.size() % 4 == 0); + int32_t numFields = fFields.size() / 4; + int32_t i = static_cast(cfpos.getInt64IterationContext()); + for (; i < numFields; i++) { + UFieldCategory category = static_cast(fFields.elementAti(i * 4)); + int32_t field = fFields.elementAti(i * 4 + 1); + if (cfpos.matchesField(category, field)) { + int32_t start = fFields.elementAti(i * 4 + 2); + int32_t limit = fFields.elementAti(i * 4 + 3); + cfpos.setState(category, field, start, limit); + break; + } + } + cfpos.setInt64IterationContext(i == numFields ? i : i + 1); + return i < numFields; +} + + +FieldPositionIteratorHandler FormattedValueFieldPositionIteratorImpl::getHandler( + UErrorCode& status) { + return FieldPositionIteratorHandler(&fFields, status); +} + +void FormattedValueFieldPositionIteratorImpl::appendString( + UnicodeString string, + UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + fString.append(string); + // Make the string NUL-terminated + if (fString.getTerminatedBuffer() == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } +} + + +void FormattedValueFieldPositionIteratorImpl::addOverlapSpans( + UFieldCategory spanCategory, + int8_t firstIndex, + UErrorCode& status) { + // In order to avoid fancy data structures, this is an O(N^2) algorithm, + // which should be fine for all real-life applications of this function. + int32_t s1a = INT32_MAX; + int32_t s1b = 0; + int32_t s2a = INT32_MAX; + int32_t s2b = 0; + int32_t numFields = fFields.size() / 4; + for (int32_t i = 0; i higher rank + comparison = start2 - start1; + } else if (limit1 != limit2) { + // Higher length (end index) -> lower rank + comparison = limit1 - limit2; + } else if (categ1 != categ2) { + // Higher field category -> lower rank + comparison = categ1 - categ2; + } else if (field1 != field2) { + // Higher field -> higher rank + comparison = field2 - field1; + } + if (comparison < 0) { + // Perform a swap + isSorted = false; + fFields.setElementAt(categ2, i*4 + 0); + fFields.setElementAt(field2, i*4 + 1); + fFields.setElementAt(start2, i*4 + 2); + fFields.setElementAt(limit2, i*4 + 3); + fFields.setElementAt(categ1, i*4 + 4); + fFields.setElementAt(field1, i*4 + 5); + fFields.setElementAt(start1, i*4 + 6); + fFields.setElementAt(limit1, i*4 + 7); + } + } + if (isSorted) { + break; + } + } +} + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/formattedval_sbimpl.cpp b/deps/icu-small/source/i18n/formattedval_sbimpl.cpp index 72197cdd8c7abf..04770aa6d528bb 100644 --- a/deps/icu-small/source/i18n/formattedval_sbimpl.cpp +++ b/deps/icu-small/source/i18n/formattedval_sbimpl.cpp @@ -1,350 +1,350 @@ -// © 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 - -// This file contains one implementation of FormattedValue. -// Other independent implementations should go into their own cpp file for -// better dependency modularization. - -#include "unicode/ustring.h" -#include "formattedval_impl.h" -#include "number_types.h" -#include "formatted_string_builder.h" -#include "number_utils.h" -#include "static_unicode_sets.h" -#include "unicode/listformatter.h" - -U_NAMESPACE_BEGIN - - -typedef FormattedStringBuilder::Field Field; - - -FormattedValueStringBuilderImpl::FormattedValueStringBuilderImpl(Field numericField) - : fNumericField(numericField) { -} - -FormattedValueStringBuilderImpl::~FormattedValueStringBuilderImpl() { -} - - -UnicodeString FormattedValueStringBuilderImpl::toString(UErrorCode&) const { - return fString.toUnicodeString(); -} - -UnicodeString FormattedValueStringBuilderImpl::toTempString(UErrorCode&) const { - return fString.toTempUnicodeString(); -} - -Appendable& FormattedValueStringBuilderImpl::appendTo(Appendable& appendable, UErrorCode&) const { - appendable.appendString(fString.chars(), fString.length()); - return appendable; -} - -UBool FormattedValueStringBuilderImpl::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const { - // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool - return nextPositionImpl(cfpos, fNumericField, status) ? true : false; -} - -UBool FormattedValueStringBuilderImpl::nextFieldPosition(FieldPosition& fp, UErrorCode& status) const { - int32_t rawField = fp.getField(); - - if (rawField == FieldPosition::DONT_CARE) { - return false; - } - - if (rawField < 0 || rawField >= UNUM_FIELD_COUNT) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } - - ConstrainedFieldPosition cfpos; - cfpos.constrainField(UFIELD_CATEGORY_NUMBER, rawField); - cfpos.setState(UFIELD_CATEGORY_NUMBER, rawField, fp.getBeginIndex(), fp.getEndIndex()); - if (nextPositionImpl(cfpos, kUndefinedField, status)) { - fp.setBeginIndex(cfpos.getStart()); - fp.setEndIndex(cfpos.getLimit()); - return true; - } - - // Special case: fraction should start after integer if fraction is not present - if (rawField == UNUM_FRACTION_FIELD && fp.getEndIndex() == 0) { - bool inside = false; - int32_t i = fString.fZero; - for (; i < fString.fZero + fString.fLength; i++) { - if (isIntOrGroup(fString.getFieldPtr()[i]) || fString.getFieldPtr()[i] == Field(UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD)) { - inside = true; - } else if (inside) { - break; - } - } - fp.setBeginIndex(i - fString.fZero); - fp.setEndIndex(i - fString.fZero); - } - - return false; -} - -void FormattedValueStringBuilderImpl::getAllFieldPositions(FieldPositionIteratorHandler& fpih, - UErrorCode& status) const { - ConstrainedFieldPosition cfpos; - while (nextPositionImpl(cfpos, kUndefinedField, status)) { - fpih.addAttribute(cfpos.getField(), cfpos.getStart(), cfpos.getLimit()); - } -} - -void FormattedValueStringBuilderImpl::resetString() { - fString.clear(); - spanIndicesCount = 0; -} - -// Signal the end of the string using a field that doesn't exist and that is -// different from kUndefinedField, which is used for "null field". -static constexpr Field kEndField = Field(0xf, 0xf); - -bool FormattedValueStringBuilderImpl::nextPositionImpl(ConstrainedFieldPosition& cfpos, Field numericField, UErrorCode& /*status*/) const { - int32_t fieldStart = -1; - Field currField = kUndefinedField; - bool prevIsSpan = false; - int32_t nextSpanStart = -1; - if (spanIndicesCount > 0) { - int64_t si = cfpos.getInt64IterationContext(); - U_ASSERT(si <= spanIndicesCount); - if (si < spanIndicesCount) { - nextSpanStart = spanIndices[si].start; - } - if (si > 0) { - prevIsSpan = cfpos.getCategory() == spanIndices[si-1].category - && cfpos.getField() == spanIndices[si-1].spanValue; - } - } - bool prevIsNumeric = false; - if (numericField != kUndefinedField) { - prevIsNumeric = cfpos.getCategory() == numericField.getCategory() - && cfpos.getField() == numericField.getField(); - } - bool prevIsInteger = cfpos.getCategory() == UFIELD_CATEGORY_NUMBER - && cfpos.getField() == UNUM_INTEGER_FIELD; - - for (int32_t i = fString.fZero + cfpos.getLimit(); i <= fString.fZero + fString.fLength; i++) { - Field _field = (i < fString.fZero + fString.fLength) ? fString.getFieldPtr()[i] : kEndField; - // Case 1: currently scanning a field. - if (currField != kUndefinedField) { - if (currField != _field) { - int32_t end = i - fString.fZero; - // Grouping separators can be whitespace; don't throw them out! - if (isTrimmable(currField)) { - end = trimBack(i - fString.fZero); - } - if (end <= fieldStart) { - // Entire field position is ignorable; skip. - fieldStart = -1; - currField = kUndefinedField; - i--; // look at this index again - continue; - } - int32_t start = fieldStart; - if (isTrimmable(currField)) { - start = trimFront(start); - } - cfpos.setState(currField.getCategory(), currField.getField(), start, end); - return true; - } - continue; - } - // Special case: emit normalField if we are pointing at the end of spanField. - if (i > fString.fZero && prevIsSpan) { - int64_t si = cfpos.getInt64IterationContext() - 1; - U_ASSERT(si >= 0); - int32_t previ = i - spanIndices[si].length; - U_ASSERT(previ >= fString.fZero); - Field prevField = fString.getFieldPtr()[previ]; - if (prevField == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) { - // Special handling for ULISTFMT_ELEMENT_FIELD - if (cfpos.matchesField(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) { - fieldStart = i - fString.fZero - spanIndices[si].length; - int32_t end = fieldStart + spanIndices[si].length; - cfpos.setState( - UFIELD_CATEGORY_LIST, - ULISTFMT_ELEMENT_FIELD, - fieldStart, - end); - return true; - } else { - prevIsSpan = false; - } - } else { - // Re-wind, since there may be multiple fields in the span. - i = previ; - _field = prevField; - } - } - // Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER. - if (cfpos.matchesField(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD) - && i > fString.fZero - && !prevIsInteger - && !prevIsNumeric - && isIntOrGroup(fString.getFieldPtr()[i - 1]) - && !isIntOrGroup(_field)) { - int j = i - 1; - for (; j >= fString.fZero && isIntOrGroup(fString.getFieldPtr()[j]); j--) {} - cfpos.setState( - UFIELD_CATEGORY_NUMBER, - UNUM_INTEGER_FIELD, - j - fString.fZero + 1, - i - fString.fZero); - return true; - } - // Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC. - if (numericField != kUndefinedField - && cfpos.matchesField(numericField.getCategory(), numericField.getField()) - && i > fString.fZero - && !prevIsNumeric - && fString.getFieldPtr()[i - 1].isNumeric() - && !_field.isNumeric()) { - // Re-wind to the beginning of the field and then emit it - int32_t j = i - 1; - for (; j >= fString.fZero && fString.getFieldPtr()[j].isNumeric(); j--) {} - cfpos.setState( - numericField.getCategory(), - numericField.getField(), - j - fString.fZero + 1, - i - fString.fZero); - return true; - } - // Check for span field - if (!prevIsSpan && ( - _field == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD) || - i - fString.fZero == nextSpanStart)) { - int64_t si = cfpos.getInt64IterationContext(); - if (si >= spanIndicesCount) { - break; - } - UFieldCategory spanCategory = spanIndices[si].category; - int32_t spanValue = spanIndices[si].spanValue; - int32_t length = spanIndices[si].length; - cfpos.setInt64IterationContext(si + 1); - if (si + 1 < spanIndicesCount) { - nextSpanStart = spanIndices[si + 1].start; - } - if (length == 0) { - // ICU-21871: Don't return fields on empty spans - i--; - continue; - } - if (cfpos.matchesField(spanCategory, spanValue)) { - fieldStart = i - fString.fZero; - int32_t end = fieldStart + length; - cfpos.setState( - spanCategory, - spanValue, - fieldStart, - end); - return true; - } else if (_field == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) { - // Special handling for ULISTFMT_ELEMENT_FIELD - if (cfpos.matchesField(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) { - fieldStart = i - fString.fZero; - int32_t end = fieldStart + length; - cfpos.setState( - UFIELD_CATEGORY_LIST, - ULISTFMT_ELEMENT_FIELD, - fieldStart, - end); - return true; - } else { - // Failed to match; jump ahead - i += length - 1; - // goto loopend - } - } - } - // Special case: skip over INTEGER; will be coalesced later. - else if (_field == Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)) { - _field = kUndefinedField; - } - // No field starting at this position. - else if (_field.isUndefined() || _field == kEndField) { - // goto loopend - } - // No SpanField - else if (cfpos.matchesField(_field.getCategory(), _field.getField())) { - fieldStart = i - fString.fZero; - currField = _field; - } - // loopend: - prevIsSpan = false; - prevIsNumeric = false; - prevIsInteger = false; - } - - U_ASSERT(currField == kUndefinedField); - // Always set the position to the end so that we don't revisit previous sections - cfpos.setState( - cfpos.getCategory(), - cfpos.getField(), - fString.fLength, - fString.fLength); - return false; -} - -void FormattedValueStringBuilderImpl::appendSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status) { - if (U_FAILURE(status)) { return; } - U_ASSERT(spanIndices.getCapacity() >= spanIndicesCount); - if (spanIndices.getCapacity() == spanIndicesCount) { - if (!spanIndices.resize(spanIndicesCount * 2, spanIndicesCount)) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - spanIndices[spanIndicesCount] = {category, spanValue, start, length}; - spanIndicesCount++; -} - -void FormattedValueStringBuilderImpl::prependSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status) { - if (U_FAILURE(status)) { return; } - U_ASSERT(spanIndices.getCapacity() >= spanIndicesCount); - if (spanIndices.getCapacity() == spanIndicesCount) { - if (!spanIndices.resize(spanIndicesCount * 2, spanIndicesCount)) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - for (int32_t i = spanIndicesCount - 1; i >= 0; i--) { - spanIndices[i+1] = spanIndices[i]; - } - spanIndices[0] = {category, spanValue, start, length}; - spanIndicesCount++; -} - -bool FormattedValueStringBuilderImpl::isIntOrGroup(Field field) { - return field == Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD) - || field == Field(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD); -} - -bool FormattedValueStringBuilderImpl::isTrimmable(Field field) { - return field != Field(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD) - && field.getCategory() != UFIELD_CATEGORY_LIST; -} - -int32_t FormattedValueStringBuilderImpl::trimBack(int32_t limit) const { - return unisets::get(unisets::DEFAULT_IGNORABLES)->spanBack( - fString.getCharPtr() + fString.fZero, - limit, - USET_SPAN_CONTAINED); -} - -int32_t FormattedValueStringBuilderImpl::trimFront(int32_t start) const { - return start + unisets::get(unisets::DEFAULT_IGNORABLES)->span( - fString.getCharPtr() + fString.fZero + start, - fString.fLength - start, - USET_SPAN_CONTAINED); -} - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +// This file contains one implementation of FormattedValue. +// Other independent implementations should go into their own cpp file for +// better dependency modularization. + +#include "unicode/ustring.h" +#include "formattedval_impl.h" +#include "number_types.h" +#include "formatted_string_builder.h" +#include "number_utils.h" +#include "static_unicode_sets.h" +#include "unicode/listformatter.h" + +U_NAMESPACE_BEGIN + + +typedef FormattedStringBuilder::Field Field; + + +FormattedValueStringBuilderImpl::FormattedValueStringBuilderImpl(Field numericField) + : fNumericField(numericField) { +} + +FormattedValueStringBuilderImpl::~FormattedValueStringBuilderImpl() { +} + + +UnicodeString FormattedValueStringBuilderImpl::toString(UErrorCode&) const { + return fString.toUnicodeString(); +} + +UnicodeString FormattedValueStringBuilderImpl::toTempString(UErrorCode&) const { + return fString.toTempUnicodeString(); +} + +Appendable& FormattedValueStringBuilderImpl::appendTo(Appendable& appendable, UErrorCode&) const { + appendable.appendString(fString.chars(), fString.length()); + return appendable; +} + +UBool FormattedValueStringBuilderImpl::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const { + // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool + return nextPositionImpl(cfpos, fNumericField, status) ? true : false; +} + +UBool FormattedValueStringBuilderImpl::nextFieldPosition(FieldPosition& fp, UErrorCode& status) const { + int32_t rawField = fp.getField(); + + if (rawField == FieldPosition::DONT_CARE) { + return false; + } + + if (rawField < 0 || rawField >= UNUM_FIELD_COUNT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } + + ConstrainedFieldPosition cfpos; + cfpos.constrainField(UFIELD_CATEGORY_NUMBER, rawField); + cfpos.setState(UFIELD_CATEGORY_NUMBER, rawField, fp.getBeginIndex(), fp.getEndIndex()); + if (nextPositionImpl(cfpos, kUndefinedField, status)) { + fp.setBeginIndex(cfpos.getStart()); + fp.setEndIndex(cfpos.getLimit()); + return true; + } + + // Special case: fraction should start after integer if fraction is not present + if (rawField == UNUM_FRACTION_FIELD && fp.getEndIndex() == 0) { + bool inside = false; + int32_t i = fString.fZero; + for (; i < fString.fZero + fString.fLength; i++) { + if (isIntOrGroup(fString.getFieldPtr()[i]) || fString.getFieldPtr()[i] == Field(UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD)) { + inside = true; + } else if (inside) { + break; + } + } + fp.setBeginIndex(i - fString.fZero); + fp.setEndIndex(i - fString.fZero); + } + + return false; +} + +void FormattedValueStringBuilderImpl::getAllFieldPositions(FieldPositionIteratorHandler& fpih, + UErrorCode& status) const { + ConstrainedFieldPosition cfpos; + while (nextPositionImpl(cfpos, kUndefinedField, status)) { + fpih.addAttribute(cfpos.getField(), cfpos.getStart(), cfpos.getLimit()); + } +} + +void FormattedValueStringBuilderImpl::resetString() { + fString.clear(); + spanIndicesCount = 0; +} + +// Signal the end of the string using a field that doesn't exist and that is +// different from kUndefinedField, which is used for "null field". +static constexpr Field kEndField = Field(0xf, 0xf); + +bool FormattedValueStringBuilderImpl::nextPositionImpl(ConstrainedFieldPosition& cfpos, Field numericField, UErrorCode& /*status*/) const { + int32_t fieldStart = -1; + Field currField = kUndefinedField; + bool prevIsSpan = false; + int32_t nextSpanStart = -1; + if (spanIndicesCount > 0) { + int64_t si = cfpos.getInt64IterationContext(); + U_ASSERT(si <= spanIndicesCount); + if (si < spanIndicesCount) { + nextSpanStart = spanIndices[si].start; + } + if (si > 0) { + prevIsSpan = cfpos.getCategory() == spanIndices[si-1].category + && cfpos.getField() == spanIndices[si-1].spanValue; + } + } + bool prevIsNumeric = false; + if (numericField != kUndefinedField) { + prevIsNumeric = cfpos.getCategory() == numericField.getCategory() + && cfpos.getField() == numericField.getField(); + } + bool prevIsInteger = cfpos.getCategory() == UFIELD_CATEGORY_NUMBER + && cfpos.getField() == UNUM_INTEGER_FIELD; + + for (int32_t i = fString.fZero + cfpos.getLimit(); i <= fString.fZero + fString.fLength; i++) { + Field _field = (i < fString.fZero + fString.fLength) ? fString.getFieldPtr()[i] : kEndField; + // Case 1: currently scanning a field. + if (currField != kUndefinedField) { + if (currField != _field) { + int32_t end = i - fString.fZero; + // Grouping separators can be whitespace; don't throw them out! + if (isTrimmable(currField)) { + end = trimBack(i - fString.fZero); + } + if (end <= fieldStart) { + // Entire field position is ignorable; skip. + fieldStart = -1; + currField = kUndefinedField; + i--; // look at this index again + continue; + } + int32_t start = fieldStart; + if (isTrimmable(currField)) { + start = trimFront(start); + } + cfpos.setState(currField.getCategory(), currField.getField(), start, end); + return true; + } + continue; + } + // Special case: emit normalField if we are pointing at the end of spanField. + if (i > fString.fZero && prevIsSpan) { + int64_t si = cfpos.getInt64IterationContext() - 1; + U_ASSERT(si >= 0); + int32_t previ = i - spanIndices[si].length; + U_ASSERT(previ >= fString.fZero); + Field prevField = fString.getFieldPtr()[previ]; + if (prevField == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) { + // Special handling for ULISTFMT_ELEMENT_FIELD + if (cfpos.matchesField(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) { + fieldStart = i - fString.fZero - spanIndices[si].length; + int32_t end = fieldStart + spanIndices[si].length; + cfpos.setState( + UFIELD_CATEGORY_LIST, + ULISTFMT_ELEMENT_FIELD, + fieldStart, + end); + return true; + } else { + prevIsSpan = false; + } + } else { + // Re-wind, since there may be multiple fields in the span. + i = previ; + _field = prevField; + } + } + // Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER. + if (cfpos.matchesField(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD) + && i > fString.fZero + && !prevIsInteger + && !prevIsNumeric + && isIntOrGroup(fString.getFieldPtr()[i - 1]) + && !isIntOrGroup(_field)) { + int j = i - 1; + for (; j >= fString.fZero && isIntOrGroup(fString.getFieldPtr()[j]); j--) {} + cfpos.setState( + UFIELD_CATEGORY_NUMBER, + UNUM_INTEGER_FIELD, + j - fString.fZero + 1, + i - fString.fZero); + return true; + } + // Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC. + if (numericField != kUndefinedField + && cfpos.matchesField(numericField.getCategory(), numericField.getField()) + && i > fString.fZero + && !prevIsNumeric + && fString.getFieldPtr()[i - 1].isNumeric() + && !_field.isNumeric()) { + // Re-wind to the beginning of the field and then emit it + int32_t j = i - 1; + for (; j >= fString.fZero && fString.getFieldPtr()[j].isNumeric(); j--) {} + cfpos.setState( + numericField.getCategory(), + numericField.getField(), + j - fString.fZero + 1, + i - fString.fZero); + return true; + } + // Check for span field + if (!prevIsSpan && ( + _field == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD) || + i - fString.fZero == nextSpanStart)) { + int64_t si = cfpos.getInt64IterationContext(); + if (si >= spanIndicesCount) { + break; + } + UFieldCategory spanCategory = spanIndices[si].category; + int32_t spanValue = spanIndices[si].spanValue; + int32_t length = spanIndices[si].length; + cfpos.setInt64IterationContext(si + 1); + if (si + 1 < spanIndicesCount) { + nextSpanStart = spanIndices[si + 1].start; + } + if (length == 0) { + // ICU-21871: Don't return fields on empty spans + i--; + continue; + } + if (cfpos.matchesField(spanCategory, spanValue)) { + fieldStart = i - fString.fZero; + int32_t end = fieldStart + length; + cfpos.setState( + spanCategory, + spanValue, + fieldStart, + end); + return true; + } else if (_field == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) { + // Special handling for ULISTFMT_ELEMENT_FIELD + if (cfpos.matchesField(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) { + fieldStart = i - fString.fZero; + int32_t end = fieldStart + length; + cfpos.setState( + UFIELD_CATEGORY_LIST, + ULISTFMT_ELEMENT_FIELD, + fieldStart, + end); + return true; + } else { + // Failed to match; jump ahead + i += length - 1; + // goto loopend + } + } + } + // Special case: skip over INTEGER; will be coalesced later. + else if (_field == Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)) { + _field = kUndefinedField; + } + // No field starting at this position. + else if (_field.isUndefined() || _field == kEndField) { + // goto loopend + } + // No SpanField + else if (cfpos.matchesField(_field.getCategory(), _field.getField())) { + fieldStart = i - fString.fZero; + currField = _field; + } + // loopend: + prevIsSpan = false; + prevIsNumeric = false; + prevIsInteger = false; + } + + U_ASSERT(currField == kUndefinedField); + // Always set the position to the end so that we don't revisit previous sections + cfpos.setState( + cfpos.getCategory(), + cfpos.getField(), + fString.fLength, + fString.fLength); + return false; +} + +void FormattedValueStringBuilderImpl::appendSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status) { + if (U_FAILURE(status)) { return; } + U_ASSERT(spanIndices.getCapacity() >= spanIndicesCount); + if (spanIndices.getCapacity() == spanIndicesCount) { + if (!spanIndices.resize(spanIndicesCount * 2, spanIndicesCount)) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + spanIndices[spanIndicesCount] = {category, spanValue, start, length}; + spanIndicesCount++; +} + +void FormattedValueStringBuilderImpl::prependSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status) { + if (U_FAILURE(status)) { return; } + U_ASSERT(spanIndices.getCapacity() >= spanIndicesCount); + if (spanIndices.getCapacity() == spanIndicesCount) { + if (!spanIndices.resize(spanIndicesCount * 2, spanIndicesCount)) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + for (int32_t i = spanIndicesCount - 1; i >= 0; i--) { + spanIndices[i+1] = spanIndices[i]; + } + spanIndices[0] = {category, spanValue, start, length}; + spanIndicesCount++; +} + +bool FormattedValueStringBuilderImpl::isIntOrGroup(Field field) { + return field == Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD) + || field == Field(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD); +} + +bool FormattedValueStringBuilderImpl::isTrimmable(Field field) { + return field != Field(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD) + && field.getCategory() != UFIELD_CATEGORY_LIST; +} + +int32_t FormattedValueStringBuilderImpl::trimBack(int32_t limit) const { + return unisets::get(unisets::DEFAULT_IGNORABLES)->spanBack( + fString.getCharPtr() + fString.fZero, + limit, + USET_SPAN_CONTAINED); +} + +int32_t FormattedValueStringBuilderImpl::trimFront(int32_t start) const { + return start + unisets::get(unisets::DEFAULT_IGNORABLES)->span( + fString.getCharPtr() + fString.fZero + start, + fString.fLength - start, + USET_SPAN_CONTAINED); +} + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/formattedvalue.cpp b/deps/icu-small/source/i18n/formattedvalue.cpp index f103c015b8e7cf..e511ffb52c7b66 100644 --- a/deps/icu-small/source/i18n/formattedvalue.cpp +++ b/deps/icu-small/source/i18n/formattedvalue.cpp @@ -1,234 +1,234 @@ -// © 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 - -#include "unicode/formattedvalue.h" -#include "formattedval_impl.h" -#include "capi_helper.h" - -U_NAMESPACE_BEGIN - - -ConstrainedFieldPosition::ConstrainedFieldPosition() {} - -ConstrainedFieldPosition::~ConstrainedFieldPosition() {} - -void ConstrainedFieldPosition::reset() { - fContext = 0LL; - fField = 0; - fStart = 0; - fLimit = 0; - fConstraint = UCFPOS_CONSTRAINT_NONE; - fCategory = UFIELD_CATEGORY_UNDEFINED; -} - -void ConstrainedFieldPosition::constrainCategory(int32_t category) { - fConstraint = UCFPOS_CONSTRAINT_CATEGORY; - fCategory = category; -} - -void ConstrainedFieldPosition::constrainField(int32_t category, int32_t field) { - fConstraint = UCFPOS_CONSTRAINT_FIELD; - fCategory = category; - fField = field; -} - -void ConstrainedFieldPosition::setInt64IterationContext(int64_t context) { - fContext = context; -} - -UBool ConstrainedFieldPosition::matchesField(int32_t category, int32_t field) const { - switch (fConstraint) { - case UCFPOS_CONSTRAINT_NONE: - return true; - case UCFPOS_CONSTRAINT_CATEGORY: - return fCategory == category; - case UCFPOS_CONSTRAINT_FIELD: - return fCategory == category && fField == field; - default: - UPRV_UNREACHABLE_EXIT; - } -} - -void ConstrainedFieldPosition::setState( - int32_t category, - int32_t field, - int32_t start, - int32_t limit) { - fCategory = category; - fField = field; - fStart = start; - fLimit = limit; -} - - -FormattedValue::~FormattedValue() = default; - - -/////////////////////// -/// C API FUNCTIONS /// -/////////////////////// - -struct UConstrainedFieldPositionImpl : public UMemory, - // Magic number as ASCII == "UCF" - public IcuCApiHelper { - ConstrainedFieldPosition fImpl; -}; - -U_CAPI UConstrainedFieldPosition* U_EXPORT2 -ucfpos_open(UErrorCode* ec) { - auto* impl = new UConstrainedFieldPositionImpl(); - if (impl == nullptr) { - *ec = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - return impl->exportForC(); -} - -U_CAPI void U_EXPORT2 -ucfpos_reset(UConstrainedFieldPosition* ptr, UErrorCode* ec) { - auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); - if (U_FAILURE(*ec)) { - return; - } - impl->fImpl.reset(); -} - -U_CAPI void U_EXPORT2 -ucfpos_constrainCategory(UConstrainedFieldPosition* ptr, int32_t category, UErrorCode* ec) { - auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); - if (U_FAILURE(*ec)) { - return; - } - impl->fImpl.constrainCategory(category); -} - -U_CAPI void U_EXPORT2 -ucfpos_constrainField(UConstrainedFieldPosition* ptr, int32_t category, int32_t field, UErrorCode* ec) { - auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); - if (U_FAILURE(*ec)) { - return; - } - impl->fImpl.constrainField(category, field); -} - -U_CAPI int32_t U_EXPORT2 -ucfpos_getCategory(const UConstrainedFieldPosition* ptr, UErrorCode* ec) { - const auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); - if (U_FAILURE(*ec)) { - return UFIELD_CATEGORY_UNDEFINED; - } - return impl->fImpl.getCategory(); -} - -U_CAPI int32_t U_EXPORT2 -ucfpos_getField(const UConstrainedFieldPosition* ptr, UErrorCode* ec) { - const auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); - if (U_FAILURE(*ec)) { - return 0; - } - return impl->fImpl.getField(); -} - -U_CAPI void U_EXPORT2 -ucfpos_getIndexes(const UConstrainedFieldPosition* ptr, int32_t* pStart, int32_t* pLimit, UErrorCode* ec) { - const auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); - if (U_FAILURE(*ec)) { - return; - } - *pStart = impl->fImpl.getStart(); - *pLimit = impl->fImpl.getLimit(); -} - -U_CAPI int64_t U_EXPORT2 -ucfpos_getInt64IterationContext(const UConstrainedFieldPosition* ptr, UErrorCode* ec) { - const auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); - if (U_FAILURE(*ec)) { - return 0; - } - return impl->fImpl.getInt64IterationContext(); -} - -U_CAPI void U_EXPORT2 -ucfpos_setInt64IterationContext(UConstrainedFieldPosition* ptr, int64_t context, UErrorCode* ec) { - auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); - if (U_FAILURE(*ec)) { - return; - } - impl->fImpl.setInt64IterationContext(context); -} - -U_CAPI UBool U_EXPORT2 -ucfpos_matchesField(const UConstrainedFieldPosition* ptr, int32_t category, int32_t field, UErrorCode* ec) { - const auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); - if (U_FAILURE(*ec)) { - return 0; - } - return impl->fImpl.matchesField(category, field); -} - -U_CAPI void U_EXPORT2 -ucfpos_setState( - UConstrainedFieldPosition* ptr, - int32_t category, - int32_t field, - int32_t start, - int32_t limit, - UErrorCode* ec) { - auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); - if (U_FAILURE(*ec)) { - return; - } - impl->fImpl.setState(category, field, start, limit); -} - -U_CAPI void U_EXPORT2 -ucfpos_close(UConstrainedFieldPosition* ptr) { - UErrorCode localStatus = U_ZERO_ERROR; - auto* impl = UConstrainedFieldPositionImpl::validate(ptr, localStatus); - delete impl; -} - - -U_CAPI const UChar* U_EXPORT2 -ufmtval_getString( - const UFormattedValue* ufmtval, - int32_t* pLength, - UErrorCode* ec) { - const auto* impl = UFormattedValueApiHelper::validate(ufmtval, *ec); - if (U_FAILURE(*ec)) { - return nullptr; - } - UnicodeString readOnlyAlias = impl->fFormattedValue->toTempString(*ec); - if (U_FAILURE(*ec)) { - return nullptr; - } - if (pLength != nullptr) { - *pLength = readOnlyAlias.length(); - } - // Note: this line triggers -Wreturn-local-addr, but it is safe because toTempString is - // defined to return memory owned by the ufmtval argument. - return readOnlyAlias.getBuffer(); -} - - -U_CAPI UBool U_EXPORT2 -ufmtval_nextPosition( - const UFormattedValue* ufmtval, - UConstrainedFieldPosition* ucfpos, - UErrorCode* ec) { - const auto* fmtval = UFormattedValueApiHelper::validate(ufmtval, *ec); - auto* cfpos = UConstrainedFieldPositionImpl::validate(ucfpos, *ec); - if (U_FAILURE(*ec)) { - return false; - } - return fmtval->fFormattedValue->nextPosition(cfpos->fImpl, *ec); -} - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include "unicode/formattedvalue.h" +#include "formattedval_impl.h" +#include "capi_helper.h" + +U_NAMESPACE_BEGIN + + +ConstrainedFieldPosition::ConstrainedFieldPosition() {} + +ConstrainedFieldPosition::~ConstrainedFieldPosition() {} + +void ConstrainedFieldPosition::reset() { + fContext = 0LL; + fField = 0; + fStart = 0; + fLimit = 0; + fConstraint = UCFPOS_CONSTRAINT_NONE; + fCategory = UFIELD_CATEGORY_UNDEFINED; +} + +void ConstrainedFieldPosition::constrainCategory(int32_t category) { + fConstraint = UCFPOS_CONSTRAINT_CATEGORY; + fCategory = category; +} + +void ConstrainedFieldPosition::constrainField(int32_t category, int32_t field) { + fConstraint = UCFPOS_CONSTRAINT_FIELD; + fCategory = category; + fField = field; +} + +void ConstrainedFieldPosition::setInt64IterationContext(int64_t context) { + fContext = context; +} + +UBool ConstrainedFieldPosition::matchesField(int32_t category, int32_t field) const { + switch (fConstraint) { + case UCFPOS_CONSTRAINT_NONE: + return true; + case UCFPOS_CONSTRAINT_CATEGORY: + return fCategory == category; + case UCFPOS_CONSTRAINT_FIELD: + return fCategory == category && fField == field; + default: + UPRV_UNREACHABLE_EXIT; + } +} + +void ConstrainedFieldPosition::setState( + int32_t category, + int32_t field, + int32_t start, + int32_t limit) { + fCategory = category; + fField = field; + fStart = start; + fLimit = limit; +} + + +FormattedValue::~FormattedValue() = default; + + +/////////////////////// +/// C API FUNCTIONS /// +/////////////////////// + +struct UConstrainedFieldPositionImpl : public UMemory, + // Magic number as ASCII == "UCF" + public IcuCApiHelper { + ConstrainedFieldPosition fImpl; +}; + +U_CAPI UConstrainedFieldPosition* U_EXPORT2 +ucfpos_open(UErrorCode* ec) { + auto* impl = new UConstrainedFieldPositionImpl(); + if (impl == nullptr) { + *ec = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + return impl->exportForC(); +} + +U_CAPI void U_EXPORT2 +ucfpos_reset(UConstrainedFieldPosition* ptr, UErrorCode* ec) { + auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); + if (U_FAILURE(*ec)) { + return; + } + impl->fImpl.reset(); +} + +U_CAPI void U_EXPORT2 +ucfpos_constrainCategory(UConstrainedFieldPosition* ptr, int32_t category, UErrorCode* ec) { + auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); + if (U_FAILURE(*ec)) { + return; + } + impl->fImpl.constrainCategory(category); +} + +U_CAPI void U_EXPORT2 +ucfpos_constrainField(UConstrainedFieldPosition* ptr, int32_t category, int32_t field, UErrorCode* ec) { + auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); + if (U_FAILURE(*ec)) { + return; + } + impl->fImpl.constrainField(category, field); +} + +U_CAPI int32_t U_EXPORT2 +ucfpos_getCategory(const UConstrainedFieldPosition* ptr, UErrorCode* ec) { + const auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); + if (U_FAILURE(*ec)) { + return UFIELD_CATEGORY_UNDEFINED; + } + return impl->fImpl.getCategory(); +} + +U_CAPI int32_t U_EXPORT2 +ucfpos_getField(const UConstrainedFieldPosition* ptr, UErrorCode* ec) { + const auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); + if (U_FAILURE(*ec)) { + return 0; + } + return impl->fImpl.getField(); +} + +U_CAPI void U_EXPORT2 +ucfpos_getIndexes(const UConstrainedFieldPosition* ptr, int32_t* pStart, int32_t* pLimit, UErrorCode* ec) { + const auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); + if (U_FAILURE(*ec)) { + return; + } + *pStart = impl->fImpl.getStart(); + *pLimit = impl->fImpl.getLimit(); +} + +U_CAPI int64_t U_EXPORT2 +ucfpos_getInt64IterationContext(const UConstrainedFieldPosition* ptr, UErrorCode* ec) { + const auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); + if (U_FAILURE(*ec)) { + return 0; + } + return impl->fImpl.getInt64IterationContext(); +} + +U_CAPI void U_EXPORT2 +ucfpos_setInt64IterationContext(UConstrainedFieldPosition* ptr, int64_t context, UErrorCode* ec) { + auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); + if (U_FAILURE(*ec)) { + return; + } + impl->fImpl.setInt64IterationContext(context); +} + +U_CAPI UBool U_EXPORT2 +ucfpos_matchesField(const UConstrainedFieldPosition* ptr, int32_t category, int32_t field, UErrorCode* ec) { + const auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); + if (U_FAILURE(*ec)) { + return 0; + } + return impl->fImpl.matchesField(category, field); +} + +U_CAPI void U_EXPORT2 +ucfpos_setState( + UConstrainedFieldPosition* ptr, + int32_t category, + int32_t field, + int32_t start, + int32_t limit, + UErrorCode* ec) { + auto* impl = UConstrainedFieldPositionImpl::validate(ptr, *ec); + if (U_FAILURE(*ec)) { + return; + } + impl->fImpl.setState(category, field, start, limit); +} + +U_CAPI void U_EXPORT2 +ucfpos_close(UConstrainedFieldPosition* ptr) { + UErrorCode localStatus = U_ZERO_ERROR; + auto* impl = UConstrainedFieldPositionImpl::validate(ptr, localStatus); + delete impl; +} + + +U_CAPI const char16_t* U_EXPORT2 +ufmtval_getString( + const UFormattedValue* ufmtval, + int32_t* pLength, + UErrorCode* ec) { + const auto* impl = UFormattedValueApiHelper::validate(ufmtval, *ec); + if (U_FAILURE(*ec)) { + return nullptr; + } + UnicodeString readOnlyAlias = impl->fFormattedValue->toTempString(*ec); + if (U_FAILURE(*ec)) { + return nullptr; + } + if (pLength != nullptr) { + *pLength = readOnlyAlias.length(); + } + // Note: this line triggers -Wreturn-local-addr, but it is safe because toTempString is + // defined to return memory owned by the ufmtval argument. + return readOnlyAlias.getBuffer(); +} + + +U_CAPI UBool U_EXPORT2 +ufmtval_nextPosition( + const UFormattedValue* ufmtval, + UConstrainedFieldPosition* ucfpos, + UErrorCode* ec) { + const auto* fmtval = UFormattedValueApiHelper::validate(ufmtval, *ec); + auto* cfpos = UConstrainedFieldPositionImpl::validate(ucfpos, *ec); + if (U_FAILURE(*ec)) { + return false; + } + return fmtval->fFormattedValue->nextPosition(cfpos->fImpl, *ec); +} + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/fphdlimp.cpp b/deps/icu-small/source/i18n/fphdlimp.cpp index 4f6a98c2120560..0bd2e1d7fdcbdd 100644 --- a/deps/icu-small/source/i18n/fphdlimp.cpp +++ b/deps/icu-small/source/i18n/fphdlimp.cpp @@ -1,125 +1,125 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2009-2015, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "fphdlimp.h" -#include "uvectr32.h" - -U_NAMESPACE_BEGIN - -// utility FieldPositionHandler -// base class, null implementation - -FieldPositionHandler::~FieldPositionHandler() { -} - -void FieldPositionHandler::setShift(int32_t delta) { - fShift = delta; -} - - -// utility subclass FieldPositionOnlyHandler - -FieldPositionOnlyHandler::FieldPositionOnlyHandler(FieldPosition& _pos) - : pos(_pos) { -} - -FieldPositionOnlyHandler::~FieldPositionOnlyHandler() { -} - -void -FieldPositionOnlyHandler::addAttribute(int32_t id, int32_t start, int32_t limit) { - if (pos.getField() == id && (!acceptFirstOnly || !seenFirst)) { - seenFirst = true; - pos.setBeginIndex(start + fShift); - pos.setEndIndex(limit + fShift); - } -} - -void -FieldPositionOnlyHandler::shiftLast(int32_t delta) { - if (delta != 0 && pos.getField() != FieldPosition::DONT_CARE && pos.getBeginIndex() != -1) { - pos.setBeginIndex(delta + pos.getBeginIndex()); - pos.setEndIndex(delta + pos.getEndIndex()); - } -} - -UBool -FieldPositionOnlyHandler::isRecording(void) const { - return pos.getField() != FieldPosition::DONT_CARE; -} - -void FieldPositionOnlyHandler::setAcceptFirstOnly(UBool acceptFirstOnly) { - this->acceptFirstOnly = acceptFirstOnly; -} - - -// utility subclass FieldPositionIteratorHandler - -FieldPositionIteratorHandler::FieldPositionIteratorHandler(FieldPositionIterator* posIter, - UErrorCode& _status) - : iter(posIter), vec(NULL), status(_status), fCategory(UFIELD_CATEGORY_UNDEFINED) { - if (iter && U_SUCCESS(status)) { - vec = new UVector32(status); - } -} - -FieldPositionIteratorHandler::FieldPositionIteratorHandler( - UVector32* vec, - UErrorCode& status) - : iter(nullptr), vec(vec), status(status), fCategory(UFIELD_CATEGORY_UNDEFINED) { -} - -FieldPositionIteratorHandler::~FieldPositionIteratorHandler() { - // setData adopts the vec regardless of status, so it's safe to null it - if (iter) { - iter->setData(vec, status); - } - // if iter is null, we never allocated vec, so no need to free it - vec = NULL; -} - -void -FieldPositionIteratorHandler::addAttribute(int32_t id, int32_t start, int32_t limit) { - if (vec && U_SUCCESS(status) && start < limit) { - int32_t size = vec->size(); - vec->addElement(fCategory, status); - vec->addElement(id, status); - vec->addElement(start + fShift, status); - vec->addElement(limit + fShift, status); - if (!U_SUCCESS(status)) { - vec->setSize(size); - } - } -} - -void -FieldPositionIteratorHandler::shiftLast(int32_t delta) { - if (U_SUCCESS(status) && delta != 0) { - int32_t i = vec->size(); - if (i > 0) { - --i; - vec->setElementAt(delta + vec->elementAti(i), i); - --i; - vec->setElementAt(delta + vec->elementAti(i), i); - } - } -} - -UBool -FieldPositionIteratorHandler::isRecording(void) const { - return U_SUCCESS(status); -} - -U_NAMESPACE_END - -#endif /* !UCONFIG_NO_FORMATTING */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2009-2015, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "fphdlimp.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +// utility FieldPositionHandler +// base class, null implementation + +FieldPositionHandler::~FieldPositionHandler() { +} + +void FieldPositionHandler::setShift(int32_t delta) { + fShift = delta; +} + + +// utility subclass FieldPositionOnlyHandler + +FieldPositionOnlyHandler::FieldPositionOnlyHandler(FieldPosition& _pos) + : pos(_pos) { +} + +FieldPositionOnlyHandler::~FieldPositionOnlyHandler() { +} + +void +FieldPositionOnlyHandler::addAttribute(int32_t id, int32_t start, int32_t limit) { + if (pos.getField() == id && (!acceptFirstOnly || !seenFirst)) { + seenFirst = true; + pos.setBeginIndex(start + fShift); + pos.setEndIndex(limit + fShift); + } +} + +void +FieldPositionOnlyHandler::shiftLast(int32_t delta) { + if (delta != 0 && pos.getField() != FieldPosition::DONT_CARE && pos.getBeginIndex() != -1) { + pos.setBeginIndex(delta + pos.getBeginIndex()); + pos.setEndIndex(delta + pos.getEndIndex()); + } +} + +UBool +FieldPositionOnlyHandler::isRecording() const { + return pos.getField() != FieldPosition::DONT_CARE; +} + +void FieldPositionOnlyHandler::setAcceptFirstOnly(UBool acceptFirstOnly) { + this->acceptFirstOnly = acceptFirstOnly; +} + + +// utility subclass FieldPositionIteratorHandler + +FieldPositionIteratorHandler::FieldPositionIteratorHandler(FieldPositionIterator* posIter, + UErrorCode& _status) + : iter(posIter), vec(nullptr), status(_status), fCategory(UFIELD_CATEGORY_UNDEFINED) { + if (iter && U_SUCCESS(status)) { + vec = new UVector32(status); + } +} + +FieldPositionIteratorHandler::FieldPositionIteratorHandler( + UVector32* vec, + UErrorCode& status) + : iter(nullptr), vec(vec), status(status), fCategory(UFIELD_CATEGORY_UNDEFINED) { +} + +FieldPositionIteratorHandler::~FieldPositionIteratorHandler() { + // setData adopts the vec regardless of status, so it's safe to null it + if (iter) { + iter->setData(vec, status); + } + // if iter is null, we never allocated vec, so no need to free it + vec = nullptr; +} + +void +FieldPositionIteratorHandler::addAttribute(int32_t id, int32_t start, int32_t limit) { + if (vec && U_SUCCESS(status) && start < limit) { + int32_t size = vec->size(); + vec->addElement(fCategory, status); + vec->addElement(id, status); + vec->addElement(start + fShift, status); + vec->addElement(limit + fShift, status); + if (!U_SUCCESS(status)) { + vec->setSize(size); + } + } +} + +void +FieldPositionIteratorHandler::shiftLast(int32_t delta) { + if (U_SUCCESS(status) && delta != 0) { + int32_t i = vec->size(); + if (i > 0) { + --i; + vec->setElementAt(delta + vec->elementAti(i), i); + --i; + vec->setElementAt(delta + vec->elementAti(i), i); + } + } +} + +UBool +FieldPositionIteratorHandler::isRecording() const { + return U_SUCCESS(status); +} + +U_NAMESPACE_END + +#endif /* !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/fphdlimp.h b/deps/icu-small/source/i18n/fphdlimp.h index 4fb0c7b6fe686e..d5501d5aabe9b1 100644 --- a/deps/icu-small/source/i18n/fphdlimp.h +++ b/deps/icu-small/source/i18n/fphdlimp.h @@ -1,109 +1,109 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2009-2015, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - -#ifndef FPHDLIMP_H -#define FPHDLIMP_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/fieldpos.h" -#include "unicode/fpositer.h" -#include "unicode/formattedvalue.h" - -U_NAMESPACE_BEGIN - -// utility FieldPositionHandler -// 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) = 0; - virtual void shiftLast(int32_t delta) = 0; - virtual UBool isRecording(void) const = 0; - - void setShift(int32_t delta); -}; - - -// utility subclass FieldPositionOnlyHandler - -class FieldPositionOnlyHandler : public FieldPositionHandler { - FieldPosition& pos; - UBool acceptFirstOnly = false; - UBool seenFirst = false; - - public: - FieldPositionOnlyHandler(FieldPosition& pos); - virtual ~FieldPositionOnlyHandler(); - - 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; - - /** - * Enable this option to lock in the FieldPosition value after seeing the - * first occurrence of the field. The default behavior is to take the last - * occurrence. - */ - void setAcceptFirstOnly(UBool acceptFirstOnly); -}; - - -// utility subclass FieldPositionIteratorHandler -// exported as U_I18N_API for tests - -class U_I18N_API FieldPositionIteratorHandler : public FieldPositionHandler { - FieldPositionIterator* iter; // can be NULL - UVector32* vec; - UErrorCode status; - UFieldCategory fCategory; - - // Note, we keep a reference to status, so if status is on the stack, we have - // to be destroyed before status goes out of scope. Easiest thing is to - // allocate us on the stack in the same (or narrower) scope as status has. - // This attempts to encourage that by blocking heap allocation. - static void* U_EXPORT2 operator new(size_t) U_NOEXCEPT = delete; - static void* U_EXPORT2 operator new[](size_t) U_NOEXCEPT = delete; -#if U_HAVE_PLACEMENT_NEW - static void* U_EXPORT2 operator new(size_t, void*) U_NOEXCEPT = delete; -#endif - - public: - FieldPositionIteratorHandler(FieldPositionIterator* posIter, UErrorCode& status); - /** If using this constructor, you must call getError() when done formatting! */ - FieldPositionIteratorHandler(UVector32* vec, UErrorCode& status); - ~FieldPositionIteratorHandler(); - - 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; - - /** Copies a failed error code into _status. */ - inline void getError(UErrorCode& _status) { - if (U_SUCCESS(_status) && U_FAILURE(status)) { - _status = status; - } - } - - inline void setCategory(UFieldCategory category) { - fCategory = category; - } -}; - -U_NAMESPACE_END - -#endif /* !UCONFIG_NO_FORMATTING */ - -#endif /* FPHDLIMP_H */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2009-2015, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +*/ + +#ifndef FPHDLIMP_H +#define FPHDLIMP_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/fieldpos.h" +#include "unicode/fpositer.h" +#include "unicode/formattedvalue.h" + +U_NAMESPACE_BEGIN + +// utility FieldPositionHandler +// 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) = 0; + virtual void shiftLast(int32_t delta) = 0; + virtual UBool isRecording() const = 0; + + void setShift(int32_t delta); +}; + + +// utility subclass FieldPositionOnlyHandler + +class FieldPositionOnlyHandler : public FieldPositionHandler { + FieldPosition& pos; + UBool acceptFirstOnly = false; + UBool seenFirst = false; + + public: + FieldPositionOnlyHandler(FieldPosition& pos); + virtual ~FieldPositionOnlyHandler(); + + void addAttribute(int32_t id, int32_t start, int32_t limit) override; + void shiftLast(int32_t delta) override; + UBool isRecording() const override; + + /** + * Enable this option to lock in the FieldPosition value after seeing the + * first occurrence of the field. The default behavior is to take the last + * occurrence. + */ + void setAcceptFirstOnly(UBool acceptFirstOnly); +}; + + +// utility subclass FieldPositionIteratorHandler +// exported as U_I18N_API for tests + +class U_I18N_API FieldPositionIteratorHandler : public FieldPositionHandler { + FieldPositionIterator* iter; // can be nullptr + UVector32* vec; + UErrorCode status; + UFieldCategory fCategory; + + // Note, we keep a reference to status, so if status is on the stack, we have + // to be destroyed before status goes out of scope. Easiest thing is to + // allocate us on the stack in the same (or narrower) scope as status has. + // This attempts to encourage that by blocking heap allocation. + static void* U_EXPORT2 operator new(size_t) noexcept = delete; + static void* U_EXPORT2 operator new[](size_t) noexcept = delete; +#if U_HAVE_PLACEMENT_NEW + static void* U_EXPORT2 operator new(size_t, void*) noexcept = delete; +#endif + + public: + FieldPositionIteratorHandler(FieldPositionIterator* posIter, UErrorCode& status); + /** If using this constructor, you must call getError() when done formatting! */ + FieldPositionIteratorHandler(UVector32* vec, UErrorCode& status); + ~FieldPositionIteratorHandler(); + + void addAttribute(int32_t id, int32_t start, int32_t limit) override; + void shiftLast(int32_t delta) override; + UBool isRecording() const override; + + /** Copies a failed error code into _status. */ + inline void getError(UErrorCode& _status) { + if (U_SUCCESS(_status) && U_FAILURE(status)) { + _status = status; + } + } + + inline void setCategory(UFieldCategory category) { + fCategory = category; + } +}; + +U_NAMESPACE_END + +#endif /* !UCONFIG_NO_FORMATTING */ + +#endif /* FPHDLIMP_H */ diff --git a/deps/icu-small/source/i18n/fpositer.cpp b/deps/icu-small/source/i18n/fpositer.cpp index 64bec990e3a9a7..afa78cd9e0c9f7 100644 --- a/deps/icu-small/source/i18n/fpositer.cpp +++ b/deps/icu-small/source/i18n/fpositer.cpp @@ -1,114 +1,114 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 2009-2012, International Business Machines Corporation and -* others. All Rights Reserved. -****************************************************************************** -* Date Name Description -* 12/14/09 doug Creation. -****************************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/fpositer.h" -#include "cmemory.h" -#include "uvectr32.h" - -U_NAMESPACE_BEGIN - -FieldPositionIterator::~FieldPositionIterator() { - delete data; - data = NULL; - pos = -1; -} - -FieldPositionIterator::FieldPositionIterator() - : data(NULL), pos(-1) { -} - -FieldPositionIterator::FieldPositionIterator(const FieldPositionIterator &rhs) - : UObject(rhs), data(NULL), pos(rhs.pos) { - - if (rhs.data) { - UErrorCode status = U_ZERO_ERROR; - data = new UVector32(status); - data->assign(*rhs.data, status); - if (status != U_ZERO_ERROR) { - delete data; - data = NULL; - pos = -1; - } - } -} - -bool FieldPositionIterator::operator==(const FieldPositionIterator &rhs) const { - if (&rhs == this) { - return true; - } - if (pos != rhs.pos) { - return false; - } - if (!data) { - return rhs.data == NULL; - } - return rhs.data ? data->operator==(*rhs.data) : false; -} - -void FieldPositionIterator::setData(UVector32 *adopt, UErrorCode& status) { - // Verify that adopt has valid data, and update status if it doesn't. - if (U_SUCCESS(status)) { - if (adopt) { - if (adopt->size() == 0) { - delete adopt; - adopt = NULL; - } else if ((adopt->size() % 4) != 0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } else { - for (int i = 2; i < adopt->size(); i += 4) { - if (adopt->elementAti(i) >= adopt->elementAti(i+1)) { - status = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - } - } - } - } - - // We own the data, even if status is in error, so we need to delete it now - // if we're not keeping track of it. - if (!U_SUCCESS(status)) { - delete adopt; - return; - } - - delete data; - data = adopt; - pos = adopt == NULL ? -1 : 0; -} - -UBool FieldPositionIterator::next(FieldPosition& fp) { - if (pos == -1) { - return false; - } - - // Ignore the first element of the tetrad: used for field category - pos++; - fp.setField(data->elementAti(pos++)); - fp.setBeginIndex(data->elementAti(pos++)); - fp.setEndIndex(data->elementAti(pos++)); - - if (pos == data->size()) { - pos = -1; - } - - return true; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 2009-2012, International Business Machines Corporation and +* others. All Rights Reserved. +****************************************************************************** +* Date Name Description +* 12/14/09 doug Creation. +****************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/fpositer.h" +#include "cmemory.h" +#include "uvectr32.h" + +U_NAMESPACE_BEGIN + +FieldPositionIterator::~FieldPositionIterator() { + delete data; + data = nullptr; + pos = -1; +} + +FieldPositionIterator::FieldPositionIterator() + : data(nullptr), pos(-1) { +} + +FieldPositionIterator::FieldPositionIterator(const FieldPositionIterator &rhs) + : UObject(rhs), data(nullptr), pos(rhs.pos) { + + if (rhs.data) { + UErrorCode status = U_ZERO_ERROR; + data = new UVector32(status); + data->assign(*rhs.data, status); + if (status != U_ZERO_ERROR) { + delete data; + data = nullptr; + pos = -1; + } + } +} + +bool FieldPositionIterator::operator==(const FieldPositionIterator &rhs) const { + if (&rhs == this) { + return true; + } + if (pos != rhs.pos) { + return false; + } + if (!data) { + return rhs.data == nullptr; + } + return rhs.data ? data->operator==(*rhs.data) : false; +} + +void FieldPositionIterator::setData(UVector32 *adopt, UErrorCode& status) { + // Verify that adopt has valid data, and update status if it doesn't. + if (U_SUCCESS(status)) { + if (adopt) { + if (adopt->size() == 0) { + delete adopt; + adopt = nullptr; + } else if ((adopt->size() % 4) != 0) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } else { + for (int i = 2; i < adopt->size(); i += 4) { + if (adopt->elementAti(i) >= adopt->elementAti(i+1)) { + status = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + } + } + } + } + + // We own the data, even if status is in error, so we need to delete it now + // if we're not keeping track of it. + if (!U_SUCCESS(status)) { + delete adopt; + return; + } + + delete data; + data = adopt; + pos = adopt == nullptr ? -1 : 0; +} + +UBool FieldPositionIterator::next(FieldPosition& fp) { + if (pos == -1) { + return false; + } + + // Ignore the first element of the tetrad: used for field category + pos++; + fp.setField(data->elementAti(pos++)); + fp.setBeginIndex(data->elementAti(pos++)); + fp.setEndIndex(data->elementAti(pos++)); + + if (pos == data->size()) { + pos = -1; + } + + return true; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + diff --git a/deps/icu-small/source/i18n/funcrepl.cpp b/deps/icu-small/source/i18n/funcrepl.cpp index 7dd54ed8d2b6a7..d96320595239d8 100644 --- a/deps/icu-small/source/i18n/funcrepl.cpp +++ b/deps/icu-small/source/i18n/funcrepl.cpp @@ -1,130 +1,130 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2012, International Business Machines Corporation -* and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 02/04/2002 aliu Creation. -********************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/translit.h" -#include "unicode/uniset.h" -#include "funcrepl.h" - -static const UChar AMPERSAND = 38; // '&' -static const UChar OPEN[] = {40,32,0}; // "( " -static const UChar CLOSE[] = {32,41,0}; // " )" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FunctionReplacer) - -/** - * Construct a replacer that takes the output of the given - * replacer, passes it through the given transliterator, and emits - * the result as output. - */ -FunctionReplacer::FunctionReplacer(Transliterator* adoptedTranslit, - UnicodeFunctor* adoptedReplacer) { - translit = adoptedTranslit; - replacer = adoptedReplacer; -} - -/** - * Copy constructor. - */ -FunctionReplacer::FunctionReplacer(const FunctionReplacer& other) : - UnicodeFunctor(other), - UnicodeReplacer(other) -{ - translit = other.translit->clone(); - replacer = other.replacer->clone(); -} - -/** - * Destructor - */ -FunctionReplacer::~FunctionReplacer() { - delete translit; - delete replacer; -} - -/** - * Implement UnicodeFunctor - */ -FunctionReplacer* FunctionReplacer::clone() const { - return new FunctionReplacer(*this); -} - -/** - * UnicodeFunctor API. Cast 'this' to a UnicodeReplacer* pointer - * and return the pointer. - */ -UnicodeReplacer* FunctionReplacer::toReplacer() const { - FunctionReplacer *nonconst_this = const_cast(this); - UnicodeReplacer *nonconst_base = static_cast(nonconst_this); - - return nonconst_base; -} - -/** - * UnicodeReplacer API - */ -int32_t FunctionReplacer::replace(Replaceable& text, - int32_t start, - int32_t limit, - int32_t& cursor) -{ - - // First delegate to subordinate replacer - int32_t len = replacer->toReplacer()->replace(text, start, limit, cursor); - limit = start + len; - - // Now transliterate - limit = translit->transliterate(text, start, limit); - - return limit - start; -} - -/** - * UnicodeReplacer API - */ -UnicodeString& FunctionReplacer::toReplacerPattern(UnicodeString& rule, - UBool escapeUnprintable) const { - UnicodeString str; - rule.truncate(0); - rule.append(AMPERSAND); - rule.append(translit->getID()); - rule.append(OPEN, 2); - rule.append(replacer->toReplacer()->toReplacerPattern(str, escapeUnprintable)); - rule.append(CLOSE, 2); - return rule; -} - -/** - * Implement UnicodeReplacer - */ -void FunctionReplacer::addReplacementSetTo(UnicodeSet& toUnionTo) const { - UnicodeSet set; - toUnionTo.addAll(translit->getTargetSet(set)); -} - -/** - * UnicodeFunctor API - */ -void FunctionReplacer::setData(const TransliterationRuleData* d) { - replacer->setData(d); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2012, International Business Machines Corporation +* and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 02/04/2002 aliu Creation. +********************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/translit.h" +#include "unicode/uniset.h" +#include "funcrepl.h" + +static const char16_t AMPERSAND = 38; // '&' +static const char16_t OPEN[] = {40,32,0}; // "( " +static const char16_t CLOSE[] = {32,41,0}; // " )" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FunctionReplacer) + +/** + * Construct a replacer that takes the output of the given + * replacer, passes it through the given transliterator, and emits + * the result as output. + */ +FunctionReplacer::FunctionReplacer(Transliterator* adoptedTranslit, + UnicodeFunctor* adoptedReplacer) { + translit = adoptedTranslit; + replacer = adoptedReplacer; +} + +/** + * Copy constructor. + */ +FunctionReplacer::FunctionReplacer(const FunctionReplacer& other) : + UnicodeFunctor(other), + UnicodeReplacer(other) +{ + translit = other.translit->clone(); + replacer = other.replacer->clone(); +} + +/** + * Destructor + */ +FunctionReplacer::~FunctionReplacer() { + delete translit; + delete replacer; +} + +/** + * Implement UnicodeFunctor + */ +FunctionReplacer* FunctionReplacer::clone() const { + return new FunctionReplacer(*this); +} + +/** + * UnicodeFunctor API. Cast 'this' to a UnicodeReplacer* pointer + * and return the pointer. + */ +UnicodeReplacer* FunctionReplacer::toReplacer() const { + FunctionReplacer *nonconst_this = const_cast(this); + UnicodeReplacer *nonconst_base = static_cast(nonconst_this); + + return nonconst_base; +} + +/** + * UnicodeReplacer API + */ +int32_t FunctionReplacer::replace(Replaceable& text, + int32_t start, + int32_t limit, + int32_t& cursor) +{ + + // First delegate to subordinate replacer + int32_t len = replacer->toReplacer()->replace(text, start, limit, cursor); + limit = start + len; + + // Now transliterate + limit = translit->transliterate(text, start, limit); + + return limit - start; +} + +/** + * UnicodeReplacer API + */ +UnicodeString& FunctionReplacer::toReplacerPattern(UnicodeString& rule, + UBool escapeUnprintable) const { + UnicodeString str; + rule.truncate(0); + rule.append(AMPERSAND); + rule.append(translit->getID()); + rule.append(OPEN, 2); + rule.append(replacer->toReplacer()->toReplacerPattern(str, escapeUnprintable)); + rule.append(CLOSE, 2); + return rule; +} + +/** + * Implement UnicodeReplacer + */ +void FunctionReplacer::addReplacementSetTo(UnicodeSet& toUnionTo) const { + UnicodeSet set; + toUnionTo.addAll(translit->getTargetSet(set)); +} + +/** + * UnicodeFunctor API + */ +void FunctionReplacer::setData(const TransliterationRuleData* d) { + replacer->setData(d); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +//eof diff --git a/deps/icu-small/source/i18n/funcrepl.h b/deps/icu-small/source/i18n/funcrepl.h index 529a10ebbf5377..5edc4779cd82cf 100644 --- a/deps/icu-small/source/i18n/funcrepl.h +++ b/deps/icu-small/source/i18n/funcrepl.h @@ -1,121 +1,121 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2002-2011, International Business Machines Corporation -* and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 02/04/2002 aliu Creation. -********************************************************************** -*/ - -#ifndef FUNCREPL_H -#define FUNCREPL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/unifunct.h" -#include "unicode/unirepl.h" - -U_NAMESPACE_BEGIN - -class Transliterator; - -/** - * A replacer that calls a transliterator to generate its output text. - * The input text to the transliterator is the output of another - * UnicodeReplacer object. That is, this replacer wraps another - * replacer with a transliterator. - * - * @author Alan Liu - */ -class FunctionReplacer : public UnicodeFunctor, public UnicodeReplacer { - - private: - - /** - * The transliterator. Must not be null. OWNED. - */ - Transliterator* translit; - - /** - * The replacer object. This generates text that is then - * processed by 'translit'. Must not be null. OWNED. - */ - UnicodeFunctor* replacer; - - public: - - /** - * Construct a replacer that takes the output of the given - * replacer, passes it through the given transliterator, and emits - * the result as output. - */ - FunctionReplacer(Transliterator* adoptedTranslit, - UnicodeFunctor* adoptedReplacer); - - /** - * Copy constructor. - */ - FunctionReplacer(const FunctionReplacer& other); - - /** - * Destructor - */ - virtual ~FunctionReplacer(); - - /** - * Implement UnicodeFunctor - */ - virtual FunctionReplacer* clone() const override; - - /** - * UnicodeFunctor API. Cast 'this' to a UnicodeReplacer* pointer - * and return the pointer. - */ - virtual UnicodeReplacer* toReplacer() const override; - - /** - * UnicodeReplacer API - */ - virtual int32_t replace(Replaceable& text, - int32_t start, - int32_t limit, - int32_t& cursor) override; - - /** - * UnicodeReplacer API - */ - virtual UnicodeString& toReplacerPattern(UnicodeString& rule, - UBool escapeUnprintable) const override; - - /** - * Implement UnicodeReplacer - */ - virtual void addReplacementSetTo(UnicodeSet& toUnionTo) const override; - - /** - * UnicodeFunctor API - */ - virtual void setData(const TransliterationRuleData*) override; - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - virtual UClassID getDynamicClassID() const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - static UClassID U_EXPORT2 getStaticClassID(); -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ -#endif - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2002-2011, International Business Machines Corporation +* and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 02/04/2002 aliu Creation. +********************************************************************** +*/ + +#ifndef FUNCREPL_H +#define FUNCREPL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/unifunct.h" +#include "unicode/unirepl.h" + +U_NAMESPACE_BEGIN + +class Transliterator; + +/** + * A replacer that calls a transliterator to generate its output text. + * The input text to the transliterator is the output of another + * UnicodeReplacer object. That is, this replacer wraps another + * replacer with a transliterator. + * + * @author Alan Liu + */ +class FunctionReplacer : public UnicodeFunctor, public UnicodeReplacer { + + private: + + /** + * The transliterator. Must not be null. OWNED. + */ + Transliterator* translit; + + /** + * The replacer object. This generates text that is then + * processed by 'translit'. Must not be null. OWNED. + */ + UnicodeFunctor* replacer; + + public: + + /** + * Construct a replacer that takes the output of the given + * replacer, passes it through the given transliterator, and emits + * the result as output. + */ + FunctionReplacer(Transliterator* adoptedTranslit, + UnicodeFunctor* adoptedReplacer); + + /** + * Copy constructor. + */ + FunctionReplacer(const FunctionReplacer& other); + + /** + * Destructor + */ + virtual ~FunctionReplacer(); + + /** + * Implement UnicodeFunctor + */ + virtual FunctionReplacer* clone() const override; + + /** + * UnicodeFunctor API. Cast 'this' to a UnicodeReplacer* pointer + * and return the pointer. + */ + virtual UnicodeReplacer* toReplacer() const override; + + /** + * UnicodeReplacer API + */ + virtual int32_t replace(Replaceable& text, + int32_t start, + int32_t limit, + int32_t& cursor) override; + + /** + * UnicodeReplacer API + */ + virtual UnicodeString& toReplacerPattern(UnicodeString& rule, + UBool escapeUnprintable) const override; + + /** + * Implement UnicodeReplacer + */ + virtual void addReplacementSetTo(UnicodeSet& toUnionTo) const override; + + /** + * UnicodeFunctor API + */ + virtual void setData(const TransliterationRuleData*) override; + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + virtual UClassID getDynamicClassID() const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + static UClassID U_EXPORT2 getStaticClassID(); +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ +#endif + +//eof diff --git a/deps/icu-small/source/i18n/gender.cpp b/deps/icu-small/source/i18n/gender.cpp index ef64d178467864..4f4755498a2095 100644 --- a/deps/icu-small/source/i18n/gender.cpp +++ b/deps/icu-small/source/i18n/gender.cpp @@ -1,254 +1,254 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2008-2013, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* -* File GENDER.CPP -* -* Modification History:* -* Date Name Description -* -******************************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/gender.h" -#include "unicode/ugender.h" -#include "unicode/ures.h" - -#include "cmemory.h" -#include "cstring.h" -#include "mutex.h" -#include "uassert.h" -#include "ucln_in.h" -#include "umutex.h" -#include "uhash.h" - -static UHashtable* gGenderInfoCache = NULL; - -static const char* gNeutralStr = "neutral"; -static const char* gMailTaintsStr = "maleTaints"; -static const char* gMixedNeutralStr = "mixedNeutral"; -static icu::GenderInfo* gObjs = NULL; -static icu::UInitOnce gGenderInitOnce {}; - -enum GenderStyle { - NEUTRAL, - MIXED_NEUTRAL, - MALE_TAINTS, - GENDER_STYLE_LENGTH -}; - -U_CDECL_BEGIN - -static UBool U_CALLCONV gender_cleanup(void) { - if (gGenderInfoCache != NULL) { - uhash_close(gGenderInfoCache); - gGenderInfoCache = NULL; - delete [] gObjs; - } - gGenderInitOnce.reset(); - return true; -} - -U_CDECL_END - -U_NAMESPACE_BEGIN - -void U_CALLCONV GenderInfo_initCache(UErrorCode &status) { - ucln_i18n_registerCleanup(UCLN_I18N_GENDERINFO, gender_cleanup); - U_ASSERT(gGenderInfoCache == NULL); - if (U_FAILURE(status)) { - return; - } - gObjs = new GenderInfo[GENDER_STYLE_LENGTH]; - if (gObjs == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - for (int i = 0; i < GENDER_STYLE_LENGTH; i++) { - gObjs[i]._style = i; - } - gGenderInfoCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); - if (U_FAILURE(status)) { - delete [] gObjs; - return; - } - uhash_setKeyDeleter(gGenderInfoCache, uprv_free); -} - - -GenderInfo::GenderInfo() { -} - -GenderInfo::~GenderInfo() { -} - -const GenderInfo* GenderInfo::getInstance(const Locale& locale, UErrorCode& status) { - // Make sure our cache exists. - umtx_initOnce(gGenderInitOnce, &GenderInfo_initCache, status); - if (U_FAILURE(status)) { - return NULL; - } - - static UMutex gGenderMetaLock; - const GenderInfo* result = NULL; - const char* key = locale.getName(); - { - Mutex lock(&gGenderMetaLock); - result = (const GenderInfo*) uhash_get(gGenderInfoCache, key); - } - if (result) { - return result; - } - - // On cache miss, try to create GenderInfo from CLDR data - result = loadInstance(locale, status); - if (U_FAILURE(status)) { - return NULL; - } - - // Try to put our GenderInfo object in cache. If there is a race condition, - // favor the GenderInfo object that is already in the cache. - { - Mutex lock(&gGenderMetaLock); - GenderInfo* temp = (GenderInfo*) uhash_get(gGenderInfoCache, key); - if (temp) { - result = temp; - } else { - uhash_put(gGenderInfoCache, uprv_strdup(key), (void*) result, &status); - if (U_FAILURE(status)) { - return NULL; - } - } - } - return result; -} - -const GenderInfo* GenderInfo::loadInstance(const Locale& locale, UErrorCode& status) { - LocalUResourceBundlePointer rb( - ures_openDirect(NULL, "genderList", &status)); - if (U_FAILURE(status)) { - return NULL; - } - LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), "genderList", NULL, &status)); - if (U_FAILURE(status)) { - return NULL; - } - int32_t resLen = 0; - const char* curLocaleName = locale.getName(); - UErrorCode key_status = U_ZERO_ERROR; - const UChar* s = ures_getStringByKey(locRes.getAlias(), curLocaleName, &resLen, &key_status); - if (s == NULL) { - key_status = U_ZERO_ERROR; - char parentLocaleName[ULOC_FULLNAME_CAPACITY]; - uprv_strcpy(parentLocaleName, curLocaleName); - while (s == NULL && uloc_getParent(parentLocaleName, parentLocaleName, ULOC_FULLNAME_CAPACITY, &key_status) > 0) { - key_status = U_ZERO_ERROR; - resLen = 0; - s = ures_getStringByKey(locRes.getAlias(), parentLocaleName, &resLen, &key_status); - key_status = U_ZERO_ERROR; - } - } - if (s == NULL) { - return &gObjs[NEUTRAL]; - } - char type_str[256] = ""; - u_UCharsToChars(s, type_str, resLen + 1); - if (uprv_strcmp(type_str, gNeutralStr) == 0) { - return &gObjs[NEUTRAL]; - } - if (uprv_strcmp(type_str, gMixedNeutralStr) == 0) { - return &gObjs[MIXED_NEUTRAL]; - } - if (uprv_strcmp(type_str, gMailTaintsStr) == 0) { - return &gObjs[MALE_TAINTS]; - } - return &gObjs[NEUTRAL]; -} - -UGender GenderInfo::getListGender(const UGender* genders, int32_t length, UErrorCode& status) const { - if (U_FAILURE(status)) { - return UGENDER_OTHER; - } - if (length == 0) { - return UGENDER_OTHER; - } - if (length == 1) { - return genders[0]; - } - UBool has_female = false; - UBool has_male = false; - switch (_style) { - case NEUTRAL: - return UGENDER_OTHER; - case MIXED_NEUTRAL: - for (int32_t i = 0; i < length; ++i) { - switch (genders[i]) { - case UGENDER_OTHER: - return UGENDER_OTHER; - break; - case UGENDER_FEMALE: - if (has_male) { - return UGENDER_OTHER; - } - has_female = true; - break; - case UGENDER_MALE: - if (has_female) { - return UGENDER_OTHER; - } - has_male = true; - break; - default: - break; - } - } - return has_male ? UGENDER_MALE : UGENDER_FEMALE; - break; - case MALE_TAINTS: - for (int32_t i = 0; i < length; ++i) { - if (genders[i] != UGENDER_FEMALE) { - return UGENDER_MALE; - } - } - return UGENDER_FEMALE; - break; - default: - return UGENDER_OTHER; - break; - } -} - -const GenderInfo* GenderInfo::getNeutralInstance() { - return &gObjs[NEUTRAL]; -} - -const GenderInfo* GenderInfo::getMixedNeutralInstance() { - return &gObjs[MIXED_NEUTRAL]; -} - -const GenderInfo* GenderInfo::getMaleTaintsInstance() { - return &gObjs[MALE_TAINTS]; -} - -U_NAMESPACE_END - -U_CAPI const UGenderInfo* U_EXPORT2 -ugender_getInstance(const char* locale, UErrorCode* status) { - return (const UGenderInfo*) icu::GenderInfo::getInstance(locale, *status); -} - -U_CAPI UGender U_EXPORT2 -ugender_getListGender(const UGenderInfo* genderInfo, const UGender* genders, int32_t size, UErrorCode* status) { - return ((const icu::GenderInfo *)genderInfo)->getListGender(genders, size, *status); -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2008-2013, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* +* File GENDER.CPP +* +* Modification History:* +* Date Name Description +* +******************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/gender.h" +#include "unicode/ugender.h" +#include "unicode/ures.h" + +#include "cmemory.h" +#include "cstring.h" +#include "mutex.h" +#include "uassert.h" +#include "ucln_in.h" +#include "umutex.h" +#include "uhash.h" + +static UHashtable* gGenderInfoCache = nullptr; + +static const char* gNeutralStr = "neutral"; +static const char* gMailTaintsStr = "maleTaints"; +static const char* gMixedNeutralStr = "mixedNeutral"; +static icu::GenderInfo* gObjs = nullptr; +static icu::UInitOnce gGenderInitOnce {}; + +enum GenderStyle { + NEUTRAL, + MIXED_NEUTRAL, + MALE_TAINTS, + GENDER_STYLE_LENGTH +}; + +U_CDECL_BEGIN + +static UBool U_CALLCONV gender_cleanup() { + if (gGenderInfoCache != nullptr) { + uhash_close(gGenderInfoCache); + gGenderInfoCache = nullptr; + delete [] gObjs; + } + gGenderInitOnce.reset(); + return true; +} + +U_CDECL_END + +U_NAMESPACE_BEGIN + +void U_CALLCONV GenderInfo_initCache(UErrorCode &status) { + ucln_i18n_registerCleanup(UCLN_I18N_GENDERINFO, gender_cleanup); + U_ASSERT(gGenderInfoCache == nullptr); + if (U_FAILURE(status)) { + return; + } + gObjs = new GenderInfo[GENDER_STYLE_LENGTH]; + if (gObjs == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + for (int i = 0; i < GENDER_STYLE_LENGTH; i++) { + gObjs[i]._style = i; + } + gGenderInfoCache = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status); + if (U_FAILURE(status)) { + delete [] gObjs; + return; + } + uhash_setKeyDeleter(gGenderInfoCache, uprv_free); +} + + +GenderInfo::GenderInfo() { +} + +GenderInfo::~GenderInfo() { +} + +const GenderInfo* GenderInfo::getInstance(const Locale& locale, UErrorCode& status) { + // Make sure our cache exists. + umtx_initOnce(gGenderInitOnce, &GenderInfo_initCache, status); + if (U_FAILURE(status)) { + return nullptr; + } + + static UMutex gGenderMetaLock; + const GenderInfo* result = nullptr; + const char* key = locale.getName(); + { + Mutex lock(&gGenderMetaLock); + result = (const GenderInfo*) uhash_get(gGenderInfoCache, key); + } + if (result) { + return result; + } + + // On cache miss, try to create GenderInfo from CLDR data + result = loadInstance(locale, status); + if (U_FAILURE(status)) { + return nullptr; + } + + // Try to put our GenderInfo object in cache. If there is a race condition, + // favor the GenderInfo object that is already in the cache. + { + Mutex lock(&gGenderMetaLock); + GenderInfo* temp = (GenderInfo*) uhash_get(gGenderInfoCache, key); + if (temp) { + result = temp; + } else { + uhash_put(gGenderInfoCache, uprv_strdup(key), (void*) result, &status); + if (U_FAILURE(status)) { + return nullptr; + } + } + } + return result; +} + +const GenderInfo* GenderInfo::loadInstance(const Locale& locale, UErrorCode& status) { + LocalUResourceBundlePointer rb( + ures_openDirect(nullptr, "genderList", &status)); + if (U_FAILURE(status)) { + return nullptr; + } + LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), "genderList", nullptr, &status)); + if (U_FAILURE(status)) { + return nullptr; + } + int32_t resLen = 0; + const char* curLocaleName = locale.getName(); + UErrorCode key_status = U_ZERO_ERROR; + const char16_t* s = ures_getStringByKey(locRes.getAlias(), curLocaleName, &resLen, &key_status); + if (s == nullptr) { + key_status = U_ZERO_ERROR; + char parentLocaleName[ULOC_FULLNAME_CAPACITY]; + uprv_strcpy(parentLocaleName, curLocaleName); + while (s == nullptr && uloc_getParent(parentLocaleName, parentLocaleName, ULOC_FULLNAME_CAPACITY, &key_status) > 0) { + key_status = U_ZERO_ERROR; + resLen = 0; + s = ures_getStringByKey(locRes.getAlias(), parentLocaleName, &resLen, &key_status); + key_status = U_ZERO_ERROR; + } + } + if (s == nullptr) { + return &gObjs[NEUTRAL]; + } + char type_str[256] = ""; + u_UCharsToChars(s, type_str, resLen + 1); + if (uprv_strcmp(type_str, gNeutralStr) == 0) { + return &gObjs[NEUTRAL]; + } + if (uprv_strcmp(type_str, gMixedNeutralStr) == 0) { + return &gObjs[MIXED_NEUTRAL]; + } + if (uprv_strcmp(type_str, gMailTaintsStr) == 0) { + return &gObjs[MALE_TAINTS]; + } + return &gObjs[NEUTRAL]; +} + +UGender GenderInfo::getListGender(const UGender* genders, int32_t length, UErrorCode& status) const { + if (U_FAILURE(status)) { + return UGENDER_OTHER; + } + if (length == 0) { + return UGENDER_OTHER; + } + if (length == 1) { + return genders[0]; + } + UBool has_female = false; + UBool has_male = false; + switch (_style) { + case NEUTRAL: + return UGENDER_OTHER; + case MIXED_NEUTRAL: + for (int32_t i = 0; i < length; ++i) { + switch (genders[i]) { + case UGENDER_OTHER: + return UGENDER_OTHER; + break; + case UGENDER_FEMALE: + if (has_male) { + return UGENDER_OTHER; + } + has_female = true; + break; + case UGENDER_MALE: + if (has_female) { + return UGENDER_OTHER; + } + has_male = true; + break; + default: + break; + } + } + return has_male ? UGENDER_MALE : UGENDER_FEMALE; + break; + case MALE_TAINTS: + for (int32_t i = 0; i < length; ++i) { + if (genders[i] != UGENDER_FEMALE) { + return UGENDER_MALE; + } + } + return UGENDER_FEMALE; + break; + default: + return UGENDER_OTHER; + break; + } +} + +const GenderInfo* GenderInfo::getNeutralInstance() { + return &gObjs[NEUTRAL]; +} + +const GenderInfo* GenderInfo::getMixedNeutralInstance() { + return &gObjs[MIXED_NEUTRAL]; +} + +const GenderInfo* GenderInfo::getMaleTaintsInstance() { + return &gObjs[MALE_TAINTS]; +} + +U_NAMESPACE_END + +U_CAPI const UGenderInfo* U_EXPORT2 +ugender_getInstance(const char* locale, UErrorCode* status) { + return (const UGenderInfo*) icu::GenderInfo::getInstance(locale, *status); +} + +U_CAPI UGender U_EXPORT2 +ugender_getListGender(const UGenderInfo* genderInfo, const UGender* genders, int32_t size, UErrorCode* status) { + return ((const icu::GenderInfo *)genderInfo)->getListGender(genders, size, *status); +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/gregocal.cpp b/deps/icu-small/source/i18n/gregocal.cpp index 63a6c2d452978b..efed43302d6600 100644 --- a/deps/icu-small/source/i18n/gregocal.cpp +++ b/deps/icu-small/source/i18n/gregocal.cpp @@ -1,1320 +1,1307 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File GREGOCAL.CPP -* -* Modification History: -* -* Date Name Description -* 02/05/97 clhuang Creation. -* 03/28/97 aliu Made highly questionable fix to computeFields to -* handle DST correctly. -* 04/22/97 aliu Cleaned up code drastically. Added monthLength(). -* Finished unimplemented parts of computeTime() for -* week-based date determination. Removed quetionable -* fix and wrote correct fix for computeFields() and -* daylight time handling. Rewrote inDaylightTime() -* and computeFields() to handle sensitive Daylight to -* Standard time transitions correctly. -* 05/08/97 aliu Added code review changes. Fixed isLeapYear() to -* not cutover. -* 08/12/97 aliu Added equivalentTo. Misc other fixes. Updated -* add() from Java source. -* 07/28/98 stephen Sync up with JDK 1.2 -* 09/14/98 stephen Changed type of kOneDay, kOneWeek to double. -* Fixed bug in roll() -* 10/15/99 aliu Fixed j31, incorrect WEEK_OF_YEAR computation. -* 10/15/99 aliu Fixed j32, cannot set date to Feb 29 2000 AD. -* {JDK bug 4210209 4209272} -* 11/15/99 weiv Added YEAR_WOY and DOW_LOCAL computation -* to timeToFields method, updated kMinValues, kMaxValues & kLeastMaxValues -* 12/09/99 aliu Fixed j81, calculation errors and roll bugs -* in year of cutover. -* 01/24/2000 aliu Revised computeJulianDay for YEAR YEAR_WOY WOY. -******************************************************************************** -*/ - -#include "unicode/utypes.h" -#include - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/gregocal.h" -#include "gregoimp.h" -#include "umutex.h" -#include "uassert.h" - -// ***************************************************************************** -// class GregorianCalendar -// ***************************************************************************** - -/** -* Note that the Julian date used here is not a true Julian date, since -* it is measured from midnight, not noon. This value is the Julian -* day number of January 1, 1970 (Gregorian calendar) at noon UTC. [LIU] -*/ - -static const int16_t kNumDays[] -= {0,31,59,90,120,151,181,212,243,273,304,334}; // 0-based, for day-in-year -static const int16_t kLeapNumDays[] -= {0,31,60,91,121,152,182,213,244,274,305,335}; // 0-based, for day-in-year -static const int8_t kMonthLength[] -= {31,28,31,30,31,30,31,31,30,31,30,31}; // 0-based -static const int8_t kLeapMonthLength[] -= {31,29,31,30,31,30,31,31,30,31,30,31}; // 0-based - -// setTimeInMillis() limits the Julian day range to +/-7F000000. -// This would seem to limit the year range to: -// ms=+183882168921600000 jd=7f000000 December 20, 5828963 AD -// ms=-184303902528000000 jd=81000000 September 20, 5838270 BC -// HOWEVER, CalendarRegressionTest/Test4167060 shows that the actual -// range limit on the year field is smaller (~ +/-140000). [alan 3.0] - -static const int32_t kGregorianCalendarLimits[UCAL_FIELD_COUNT][4] = { - // Minimum Greatest Least Maximum - // Minimum Maximum - { 0, 0, 1, 1}, // ERA - { 1, 1, 140742, 144683}, // YEAR - { 0, 0, 11, 11}, // MONTH - { 1, 1, 52, 53}, // WEEK_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH - { 1, 1, 28, 31}, // DAY_OF_MONTH - { 1, 1, 365, 366}, // DAY_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK - { -1, -1, 4, 5}, // DAY_OF_WEEK_IN_MONTH - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET - { -140742, -140742, 140742, 144683}, // YEAR_WOY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL - { -140742, -140742, 140742, 144683}, // EXTENDED_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH -}; - -/* -*
-*                            Greatest       Least 
-* Field name        Minimum   Minimum     Maximum     Maximum
-* ----------        -------   -------     -------     -------
-* ERA                     0         0           1           1
-* YEAR                    1         1      140742      144683
-* MONTH                   0         0          11          11
-* WEEK_OF_YEAR            1         1          52          53
-* WEEK_OF_MONTH           0         0           4           6
-* DAY_OF_MONTH            1         1          28          31
-* DAY_OF_YEAR             1         1         365         366
-* DAY_OF_WEEK             1         1           7           7
-* DAY_OF_WEEK_IN_MONTH   -1        -1           4           5
-* AM_PM                   0         0           1           1
-* HOUR                    0         0          11          11
-* HOUR_OF_DAY             0         0          23          23
-* MINUTE                  0         0          59          59
-* SECOND                  0         0          59          59
-* MILLISECOND             0         0         999         999
-* ZONE_OFFSET           -12*      -12*         12*         12*
-* DST_OFFSET              0         0           1*          1*
-* YEAR_WOY                1         1      140742      144683
-* DOW_LOCAL               1         1           7           7
-* 
-* (*) In units of one-hour -*/ - -#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) -#include -#endif - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(GregorianCalendar) - -// 00:00:00 UTC, October 15, 1582, expressed in ms from the epoch. -// Note that only Italy and other Catholic countries actually -// observed this cutover. Most other countries followed in -// the next few centuries, some as late as 1928. [LIU] -// in Java, -12219292800000L -//const UDate GregorianCalendar::kPapalCutover = -12219292800000L; -static const uint32_t kCutoverJulianDay = 2299161; -static const UDate kPapalCutover = (2299161.0 - kEpochStartAsJulianDay) * U_MILLIS_PER_DAY; -//static const UDate kPapalCutoverJulian = (2299161.0 - kEpochStartAsJulianDay); - -// ------------------------------------- - -GregorianCalendar::GregorianCalendar(UErrorCode& status) -: Calendar(status), -fGregorianCutover(kPapalCutover), -fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), -fIsGregorian(true), fInvertGregorian(false) -{ - setTimeInMillis(getNow(), status); -} - -// ------------------------------------- - -GregorianCalendar::GregorianCalendar(TimeZone* zone, UErrorCode& status) -: Calendar(zone, Locale::getDefault(), status), -fGregorianCutover(kPapalCutover), -fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), -fIsGregorian(true), fInvertGregorian(false) -{ - setTimeInMillis(getNow(), status); -} - -// ------------------------------------- - -GregorianCalendar::GregorianCalendar(const TimeZone& zone, UErrorCode& status) -: Calendar(zone, Locale::getDefault(), status), -fGregorianCutover(kPapalCutover), -fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), -fIsGregorian(true), fInvertGregorian(false) -{ - setTimeInMillis(getNow(), status); -} - -// ------------------------------------- - -GregorianCalendar::GregorianCalendar(const Locale& aLocale, UErrorCode& status) -: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, status), -fGregorianCutover(kPapalCutover), -fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), -fIsGregorian(true), fInvertGregorian(false) -{ - setTimeInMillis(getNow(), status); -} - -// ------------------------------------- - -GregorianCalendar::GregorianCalendar(TimeZone* zone, const Locale& aLocale, - UErrorCode& status) - : Calendar(zone, aLocale, status), - fGregorianCutover(kPapalCutover), - fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), - fIsGregorian(true), fInvertGregorian(false) -{ - setTimeInMillis(getNow(), status); -} - -// ------------------------------------- - -GregorianCalendar::GregorianCalendar(const TimeZone& zone, const Locale& aLocale, - UErrorCode& status) - : Calendar(zone, aLocale, status), - fGregorianCutover(kPapalCutover), - fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), - fIsGregorian(true), fInvertGregorian(false) -{ - setTimeInMillis(getNow(), status); -} - -// ------------------------------------- - -GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date, - UErrorCode& status) - : Calendar(TimeZone::createDefault(), Locale::getDefault(), status), - fGregorianCutover(kPapalCutover), - fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), - fIsGregorian(true), fInvertGregorian(false) -{ - set(UCAL_ERA, AD); - set(UCAL_YEAR, year); - set(UCAL_MONTH, month); - set(UCAL_DATE, date); -} - -// ------------------------------------- - -GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date, - int32_t hour, int32_t minute, UErrorCode& status) - : Calendar(TimeZone::createDefault(), Locale::getDefault(), status), - fGregorianCutover(kPapalCutover), - fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), - fIsGregorian(true), fInvertGregorian(false) -{ - set(UCAL_ERA, AD); - set(UCAL_YEAR, year); - set(UCAL_MONTH, month); - set(UCAL_DATE, date); - set(UCAL_HOUR_OF_DAY, hour); - set(UCAL_MINUTE, minute); -} - -// ------------------------------------- - -GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date, - int32_t hour, int32_t minute, int32_t second, - UErrorCode& status) - : Calendar(TimeZone::createDefault(), Locale::getDefault(), status), - fGregorianCutover(kPapalCutover), - fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), - fIsGregorian(true), fInvertGregorian(false) -{ - set(UCAL_ERA, AD); - set(UCAL_YEAR, year); - set(UCAL_MONTH, month); - set(UCAL_DATE, date); - set(UCAL_HOUR_OF_DAY, hour); - set(UCAL_MINUTE, minute); - set(UCAL_SECOND, second); -} - -// ------------------------------------- - -GregorianCalendar::~GregorianCalendar() -{ -} - -// ------------------------------------- - -GregorianCalendar::GregorianCalendar(const GregorianCalendar &source) -: Calendar(source), -fGregorianCutover(source.fGregorianCutover), -fCutoverJulianDay(source.fCutoverJulianDay), fNormalizedGregorianCutover(source.fNormalizedGregorianCutover), fGregorianCutoverYear(source.fGregorianCutoverYear), -fIsGregorian(source.fIsGregorian), fInvertGregorian(source.fInvertGregorian) -{ -} - -// ------------------------------------- - -GregorianCalendar* GregorianCalendar::clone() const -{ - return new GregorianCalendar(*this); -} - -// ------------------------------------- - -GregorianCalendar & -GregorianCalendar::operator=(const GregorianCalendar &right) -{ - if (this != &right) - { - Calendar::operator=(right); - fGregorianCutover = right.fGregorianCutover; - fNormalizedGregorianCutover = right.fNormalizedGregorianCutover; - fGregorianCutoverYear = right.fGregorianCutoverYear; - fCutoverJulianDay = right.fCutoverJulianDay; - } - return *this; -} - -// ------------------------------------- - -UBool GregorianCalendar::isEquivalentTo(const Calendar& other) const -{ - // Calendar override. - return Calendar::isEquivalentTo(other) && - fGregorianCutover == ((GregorianCalendar*)&other)->fGregorianCutover; -} - -// ------------------------------------- - -void -GregorianCalendar::setGregorianChange(UDate date, UErrorCode& status) -{ - if (U_FAILURE(status)) - return; - - // Precompute two internal variables which we use to do the actual - // cutover computations. These are the normalized cutover, which is the - // midnight at or before the cutover, and the cutover year. The - // normalized cutover is in pure date milliseconds; it contains no time - // of day or timezone component, and it used to compare against other - // pure date values. - double cutoverDay = ClockMath::floorDivide(date, (double)kOneDay); - - // Handle the rare case of numeric overflow where the user specifies a time - // outside of INT32_MIN .. INT32_MAX number of days. - - if (cutoverDay <= INT32_MIN) { - cutoverDay = INT32_MIN; - fGregorianCutover = fNormalizedGregorianCutover = cutoverDay * kOneDay; - } else if (cutoverDay >= INT32_MAX) { - cutoverDay = INT32_MAX; - fGregorianCutover = fNormalizedGregorianCutover = cutoverDay * kOneDay; - } else { - fNormalizedGregorianCutover = cutoverDay * kOneDay; - fGregorianCutover = date; - } - - // Normalize the year so BC values are represented as 0 and negative - // values. - GregorianCalendar *cal = new GregorianCalendar(getTimeZone(), status); - /* test for NULL */ - if (cal == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if(U_FAILURE(status)) - return; - cal->setTime(date, status); - fGregorianCutoverYear = cal->get(UCAL_YEAR, status); - if (cal->get(UCAL_ERA, status) == BC) - fGregorianCutoverYear = 1 - fGregorianCutoverYear; - fCutoverJulianDay = (int32_t)cutoverDay; - delete cal; -} - - -void GregorianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) { - int32_t eyear, month, dayOfMonth, dayOfYear, unusedRemainder; - - - if(U_FAILURE(status)) { - return; - } - -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: jd%d- (greg's %d)- [cut=%d]\n", - __FILE__, __LINE__, julianDay, getGregorianDayOfYear(), fCutoverJulianDay); -#endif - - - if (julianDay >= fCutoverJulianDay) { - month = getGregorianMonth(); - dayOfMonth = getGregorianDayOfMonth(); - dayOfYear = getGregorianDayOfYear(); - eyear = getGregorianYear(); - } else { - // The Julian epoch day (not the same as Julian Day) - // is zero on Saturday December 30, 0 (Gregorian). - int32_t julianEpochDay = julianDay - (kJan1_1JulianDay - 2); - eyear = (int32_t) ClockMath::floorDivide((4.0*julianEpochDay) + 1464.0, (int32_t) 1461, &unusedRemainder); - - // Compute the Julian calendar day number for January 1, eyear - int32_t january1 = 365*(eyear-1) + ClockMath::floorDivide(eyear-1, (int32_t)4); - dayOfYear = (julianEpochDay - january1); // 0-based - - // Julian leap years occurred historically every 4 years starting - // with 8 AD. Before 8 AD the spacing is irregular; every 3 years - // from 45 BC to 9 BC, and then none until 8 AD. However, we don't - // implement this historical detail; instead, we implement the - // computationally cleaner proleptic calendar, which assumes - // consistent 4-year cycles throughout time. - UBool isLeap = ((eyear&0x3) == 0); // equiv. to (eyear%4 == 0) - - // Common Julian/Gregorian calculation - int32_t correction = 0; - int32_t march1 = isLeap ? 60 : 59; // zero-based DOY for March 1 - if (dayOfYear >= march1) { - correction = isLeap ? 1 : 2; - } - month = (12 * (dayOfYear + correction) + 6) / 367; // zero-based month - dayOfMonth = dayOfYear - (isLeap?kLeapNumDays[month]:kNumDays[month]) + 1; // one-based DOM - ++dayOfYear; -#if defined (U_DEBUG_CAL) - // fprintf(stderr, "%d - %d[%d] + 1\n", dayOfYear, isLeap?kLeapNumDays[month]:kNumDays[month], month ); - // fprintf(stderr, "%s:%d: greg's HCF %d -> %d/%d/%d not %d/%d/%d\n", - // __FILE__, __LINE__,julianDay, - // eyear,month,dayOfMonth, - // getGregorianYear(), getGregorianMonth(), getGregorianDayOfMonth() ); - fprintf(stderr, "%s:%d: doy %d (greg's %d)- [cut=%d]\n", - __FILE__, __LINE__, dayOfYear, getGregorianDayOfYear(), fCutoverJulianDay); -#endif - - } - - // [j81] if we are after the cutover in its year, shift the day of the year - if((eyear == fGregorianCutoverYear) && (julianDay >= fCutoverJulianDay)) { - //from handleComputeMonthStart - int32_t gregShift = Grego::gregorianShift(eyear); -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: gregorian shift %d ::: doy%d => %d [cut=%d]\n", - __FILE__, __LINE__,gregShift, dayOfYear, dayOfYear+gregShift, fCutoverJulianDay); -#endif - dayOfYear += gregShift; - } - - internalSet(UCAL_MONTH, month); - internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); - internalSet(UCAL_DAY_OF_YEAR, dayOfYear); - internalSet(UCAL_EXTENDED_YEAR, eyear); - int32_t era = AD; - if (eyear < 1) { - era = BC; - eyear = 1 - eyear; - } - internalSet(UCAL_ERA, era); - internalSet(UCAL_YEAR, eyear); -} - - -// ------------------------------------- - -UDate -GregorianCalendar::getGregorianChange() const -{ - return fGregorianCutover; -} - -// ------------------------------------- - -UBool -GregorianCalendar::isLeapYear(int32_t year) const -{ - // MSVC complains bitterly if we try to use Grego::isLeapYear here - // NOTE: year&0x3 == year%4 - return (year >= fGregorianCutoverYear ? - (((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0))) : // Gregorian - ((year&0x3) == 0)); // Julian -} - -// ------------------------------------- - -int32_t GregorianCalendar::handleComputeJulianDay(UCalendarDateFields bestField) -{ - fInvertGregorian = false; - - int32_t jd = Calendar::handleComputeJulianDay(bestField); - - if((bestField == UCAL_WEEK_OF_YEAR) && // if we are doing WOY calculations, we are counting relative to Jan 1 *julian* - (internalGet(UCAL_EXTENDED_YEAR)==fGregorianCutoverYear) && - jd >= fCutoverJulianDay) { - fInvertGregorian = true; // So that the Julian Jan 1 will be used in handleComputeMonthStart - return Calendar::handleComputeJulianDay(bestField); - } - - - // The following check handles portions of the cutover year BEFORE the - // cutover itself happens. - //if ((fIsGregorian==true) != (jd >= fCutoverJulianDay)) { /* cutoverJulianDay)) { */ - if ((fIsGregorian==true) != (jd >= fCutoverJulianDay)) { /* cutoverJulianDay)) { */ -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: jd [invert] %d\n", - __FILE__, __LINE__, jd); -#endif - fInvertGregorian = true; - jd = Calendar::handleComputeJulianDay(bestField); -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: fIsGregorian %s, fInvertGregorian %s - ", - __FILE__, __LINE__,fIsGregorian?"T":"F", fInvertGregorian?"T":"F"); - fprintf(stderr, " jd NOW %d\n", - jd); -#endif - } else { -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: jd [==] %d - %sfIsGregorian %sfInvertGregorian, %d\n", - __FILE__, __LINE__, jd, fIsGregorian?"T":"F", fInvertGregorian?"T":"F", bestField); -#endif - } - - if(fIsGregorian && (internalGet(UCAL_EXTENDED_YEAR) == fGregorianCutoverYear)) { - int32_t gregShift = Grego::gregorianShift(internalGet(UCAL_EXTENDED_YEAR)); - if (bestField == UCAL_DAY_OF_YEAR) { -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: [DOY%d] gregorian shift of JD %d += %d\n", - __FILE__, __LINE__, fFields[bestField],jd, gregShift); -#endif - jd -= gregShift; - } else if ( bestField == UCAL_WEEK_OF_MONTH ) { - int32_t weekShift = 14; -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: [WOY/WOM] gregorian week shift of %d += %d\n", - __FILE__, __LINE__, jd, weekShift); -#endif - jd += weekShift; // shift by weeks for week based fields. - } - } - - return jd; -} - -int32_t GregorianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, - - UBool /* useMonth */) const -{ - GregorianCalendar *nonConstThis = (GregorianCalendar*)this; // cast away const - - // If the month is out of range, adjust it into range, and - // modify the extended year value accordingly. - if (month < 0 || month > 11) { - eyear += ClockMath::floorDivide(month, 12, &month); - } - - UBool isLeap = eyear%4 == 0; - int64_t y = (int64_t)eyear-1; - int64_t julianDay = 365*y + ClockMath::floorDivide(y, (int64_t)4) + (kJan1_1JulianDay - 3); - - nonConstThis->fIsGregorian = (eyear >= fGregorianCutoverYear); -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: (hcms%d/%d) fIsGregorian %s, fInvertGregorian %s\n", - __FILE__, __LINE__, eyear,month, fIsGregorian?"T":"F", fInvertGregorian?"T":"F"); -#endif - if (fInvertGregorian) { - nonConstThis->fIsGregorian = !fIsGregorian; - } - if (fIsGregorian) { - isLeap = isLeap && ((eyear%100 != 0) || (eyear%400 == 0)); - // Add 2 because Gregorian calendar starts 2 days after - // Julian calendar - int32_t gregShift = Grego::gregorianShift(eyear); -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: (hcms%d/%d) gregorian shift of %d += %d\n", - __FILE__, __LINE__, eyear, month, julianDay, gregShift); -#endif - julianDay += gregShift; - } - - // At this point julianDay indicates the day BEFORE the first - // day of January 1, of either the Julian or Gregorian - // calendar. - - if (month != 0) { - julianDay += isLeap?kLeapNumDays[month]:kNumDays[month]; - } - - return static_cast(julianDay); -} - -int32_t GregorianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const -{ - // If the month is out of range, adjust it into range, and - // modify the extended year value accordingly. - if (month < 0 || month > 11) { - extendedYear += ClockMath::floorDivide(month, 12, &month); - } - - return isLeapYear(extendedYear) ? kLeapMonthLength[month] : kMonthLength[month]; -} - -int32_t GregorianCalendar::handleGetYearLength(int32_t eyear) const { - return isLeapYear(eyear) ? 366 : 365; -} - - -int32_t -GregorianCalendar::monthLength(int32_t month) const -{ - int32_t year = internalGet(UCAL_EXTENDED_YEAR); - return handleGetMonthLength(year, month); -} - -// ------------------------------------- - -int32_t -GregorianCalendar::monthLength(int32_t month, int32_t year) const -{ - return isLeapYear(year) ? kLeapMonthLength[month] : kMonthLength[month]; -} - -// ------------------------------------- - -int32_t -GregorianCalendar::yearLength(int32_t year) const -{ - return isLeapYear(year) ? 366 : 365; -} - -// ------------------------------------- - -int32_t -GregorianCalendar::yearLength() const -{ - return isLeapYear(internalGet(UCAL_YEAR)) ? 366 : 365; -} - -// ------------------------------------- - -/** -* After adjustments such as add(MONTH), add(YEAR), we don't want the -* month to jump around. E.g., we don't want Jan 31 + 1 month to go to Mar -* 3, we want it to go to Feb 28. Adjustments which might run into this -* problem call this method to retain the proper month. -*/ -void -GregorianCalendar::pinDayOfMonth() -{ - int32_t monthLen = monthLength(internalGet(UCAL_MONTH)); - int32_t dom = internalGet(UCAL_DATE); - if(dom > monthLen) - set(UCAL_DATE, monthLen); -} - -// ------------------------------------- - - -UBool -GregorianCalendar::validateFields() const -{ - for (int32_t field = 0; field < UCAL_FIELD_COUNT; field++) { - // Ignore DATE and DAY_OF_YEAR which are handled below - if (field != UCAL_DATE && - field != UCAL_DAY_OF_YEAR && - isSet((UCalendarDateFields)field) && - ! boundsCheck(internalGet((UCalendarDateFields)field), (UCalendarDateFields)field)) - return false; - } - - // Values differ in Least-Maximum and Maximum should be handled - // specially. - if (isSet(UCAL_DATE)) { - int32_t date = internalGet(UCAL_DATE); - if (date < getMinimum(UCAL_DATE) || - date > monthLength(internalGet(UCAL_MONTH))) { - return false; - } - } - - if (isSet(UCAL_DAY_OF_YEAR)) { - int32_t days = internalGet(UCAL_DAY_OF_YEAR); - if (days < 1 || days > yearLength()) { - return false; - } - } - - // Handle DAY_OF_WEEK_IN_MONTH, which must not have the value zero. - // We've checked against minimum and maximum above already. - if (isSet(UCAL_DAY_OF_WEEK_IN_MONTH) && - 0 == internalGet(UCAL_DAY_OF_WEEK_IN_MONTH)) { - return false; - } - - return true; -} - -// ------------------------------------- - -UBool -GregorianCalendar::boundsCheck(int32_t value, UCalendarDateFields field) const -{ - return value >= getMinimum(field) && value <= getMaximum(field); -} - -// ------------------------------------- - -UDate -GregorianCalendar::getEpochDay(UErrorCode& status) -{ - complete(status); - // Divide by 1000 (convert to seconds) in order to prevent overflow when - // dealing with UDate(Long.MIN_VALUE) and UDate(Long.MAX_VALUE). - double wallSec = internalGetTime()/1000 + (internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET))/1000; - - return ClockMath::floorDivide(wallSec, kOneDay/1000.0); -} - -// ------------------------------------- - - -// ------------------------------------- - -/** -* Compute the julian day number of the day BEFORE the first day of -* January 1, year 1 of the given calendar. If julianDay == 0, it -* specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian -* or Gregorian). -*/ -double GregorianCalendar::computeJulianDayOfYear(UBool isGregorian, - int32_t year, UBool& isLeap) -{ - isLeap = year%4 == 0; - int32_t y = year - 1; - double julianDay = 365.0*y + ClockMath::floorDivide(y, 4) + (kJan1_1JulianDay - 3); - - if (isGregorian) { - isLeap = isLeap && ((year%100 != 0) || (year%400 == 0)); - // Add 2 because Gregorian calendar starts 2 days after Julian calendar - julianDay += Grego::gregorianShift(year); - } - - return julianDay; -} - -// /** -// * Compute the day of week, relative to the first day of week, from -// * 0..6, of the current DOW_LOCAL or DAY_OF_WEEK fields. This is -// * equivalent to get(DOW_LOCAL) - 1. -// */ -// int32_t GregorianCalendar::computeRelativeDOW() const { -// int32_t relDow = 0; -// if (fStamp[UCAL_DOW_LOCAL] > fStamp[UCAL_DAY_OF_WEEK]) { -// relDow = internalGet(UCAL_DOW_LOCAL) - 1; // 1-based -// } else if (fStamp[UCAL_DAY_OF_WEEK] != kUnset) { -// relDow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); -// if (relDow < 0) relDow += 7; -// } -// return relDow; -// } - -// /** -// * Compute the day of week, relative to the first day of week, -// * from 0..6 of the given julian day. -// */ -// int32_t GregorianCalendar::computeRelativeDOW(double julianDay) const { -// int32_t relDow = julianDayToDayOfWeek(julianDay) - getFirstDayOfWeek(); -// if (relDow < 0) { -// relDow += 7; -// } -// return relDow; -// } - -// /** -// * Compute the DOY using the WEEK_OF_YEAR field and the julian day -// * of the day BEFORE January 1 of a year (a return value from -// * computeJulianDayOfYear). -// */ -// int32_t GregorianCalendar::computeDOYfromWOY(double julianDayOfYear) const { -// // Compute DOY from day of week plus week of year - -// // Find the day of the week for the first of this year. This -// // is zero-based, with 0 being the locale-specific first day of -// // the week. Add 1 to get first day of year. -// int32_t fdy = computeRelativeDOW(julianDayOfYear + 1); - -// return -// // Compute doy of first (relative) DOW of WOY 1 -// (((7 - fdy) < getMinimalDaysInFirstWeek()) -// ? (8 - fdy) : (1 - fdy)) - -// // Adjust for the week number. -// + (7 * (internalGet(UCAL_WEEK_OF_YEAR) - 1)) - -// // Adjust for the DOW -// + computeRelativeDOW(); -// } - -// ------------------------------------- - -double -GregorianCalendar::millisToJulianDay(UDate millis) -{ - return (double)kEpochStartAsJulianDay + ClockMath::floorDivide(millis, (double)kOneDay); -} - -// ------------------------------------- - -UDate -GregorianCalendar::julianDayToMillis(double julian) -{ - return (UDate) ((julian - kEpochStartAsJulianDay) * (double) kOneDay); -} - -// ------------------------------------- - -int32_t -GregorianCalendar::aggregateStamp(int32_t stamp_a, int32_t stamp_b) -{ - return (((stamp_a != kUnset && stamp_b != kUnset) - ? uprv_max(stamp_a, stamp_b) - : (int32_t)kUnset)); -} - -// ------------------------------------- - -/** -* Roll a field by a signed amount. -* Note: This will be made public later. [LIU] -*/ - -void -GregorianCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { - roll((UCalendarDateFields) field, amount, status); -} - -void -GregorianCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) -{ - if((amount == 0) || U_FAILURE(status)) { - return; - } - - // J81 processing. (gregorian cutover) - UBool inCutoverMonth = false; - int32_t cMonthLen=0; // 'c' for cutover; in days - int32_t cDayOfMonth=0; // no discontinuity: [0, cMonthLen) - double cMonthStart=0.0; // in ms - - // Common code - see if we're in the cutover month of the cutover year - if(get(UCAL_EXTENDED_YEAR, status) == fGregorianCutoverYear) { - switch (field) { - case UCAL_DAY_OF_MONTH: - case UCAL_WEEK_OF_MONTH: - { - int32_t max = monthLength(internalGet(UCAL_MONTH)); - UDate t = internalGetTime(); - // We subtract 1 from the DAY_OF_MONTH to make it zero-based, and an - // additional 10 if we are after the cutover. Thus the monthStart - // value will be correct iff we actually are in the cutover month. - cDayOfMonth = internalGet(UCAL_DAY_OF_MONTH) - ((t >= fGregorianCutover) ? 10 : 0); - cMonthStart = t - ((cDayOfMonth - 1) * kOneDay); - // A month containing the cutover is 10 days shorter. - if ((cMonthStart < fGregorianCutover) && - (cMonthStart + (cMonthLen=(max-10))*kOneDay >= fGregorianCutover)) { - inCutoverMonth = true; - } - } - break; - default: - ; - } - } - - switch (field) { - case UCAL_WEEK_OF_YEAR: { - // Unlike WEEK_OF_MONTH, WEEK_OF_YEAR never shifts the day of the - // week. Also, rolling the week of the year can have seemingly - // strange effects simply because the year of the week of year - // may be different from the calendar year. For example, the - // date Dec 28, 1997 is the first day of week 1 of 1998 (if - // weeks start on Sunday and the minimal days in first week is - // <= 3). - int32_t woy = get(UCAL_WEEK_OF_YEAR, status); - // Get the ISO year, which matches the week of year. This - // may be one year before or after the calendar year. - int32_t isoYear = get(UCAL_YEAR_WOY, status); - int32_t isoDoy = internalGet(UCAL_DAY_OF_YEAR); - if (internalGet(UCAL_MONTH) == UCAL_JANUARY) { - if (woy >= 52) { - isoDoy += handleGetYearLength(isoYear); - } - } else { - if (woy == 1) { - isoDoy -= handleGetYearLength(isoYear - 1); - } - } - woy += amount; - // Do fast checks to avoid unnecessary computation: - if (woy < 1 || woy > 52) { - // Determine the last week of the ISO year. - // We do this using the standard formula we use - // everywhere in this file. If we can see that the - // days at the end of the year are going to fall into - // week 1 of the next year, we drop the last week by - // subtracting 7 from the last day of the year. - int32_t lastDoy = handleGetYearLength(isoYear); - int32_t lastRelDow = (lastDoy - isoDoy + internalGet(UCAL_DAY_OF_WEEK) - - getFirstDayOfWeek()) % 7; - if (lastRelDow < 0) lastRelDow += 7; - if ((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) lastDoy -= 7; - int32_t lastWoy = weekNumber(lastDoy, lastRelDow + 1); - woy = ((woy + lastWoy - 1) % lastWoy) + 1; - } - set(UCAL_WEEK_OF_YEAR, woy); - set(UCAL_YEAR_WOY,isoYear); - return; - } - - case UCAL_DAY_OF_MONTH: - if( !inCutoverMonth ) { - Calendar::roll(field, amount, status); - return; - } else { - // [j81] 1582 special case for DOM - // The default computation works except when the current month - // contains the Gregorian cutover. We handle this special case - // here. [j81 - aliu] - double monthLen = cMonthLen * kOneDay; - double msIntoMonth = uprv_fmod(internalGetTime() - cMonthStart + - amount * kOneDay, monthLen); - if (msIntoMonth < 0) { - msIntoMonth += monthLen; - } -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: roll DOM %d -> %.0lf ms \n", - __FILE__, __LINE__,amount, cMonthLen, cMonthStart+msIntoMonth); -#endif - setTimeInMillis(cMonthStart + msIntoMonth, status); - return; - } - - case UCAL_WEEK_OF_MONTH: - if( !inCutoverMonth ) { - Calendar::roll(field, amount, status); - return; - } else { -#if defined (U_DEBUG_CAL) - fprintf(stderr, "%s:%d: roll WOM %d ??????????????????? \n", - __FILE__, __LINE__,amount); -#endif - // NOTE: following copied from the old - // GregorianCalendar::roll( WEEK_OF_MONTH ) code - - // This is tricky, because during the roll we may have to shift - // to a different day of the week. For example: - - // s m t w r f s - // 1 2 3 4 5 - // 6 7 8 9 10 11 12 - - // When rolling from the 6th or 7th back one week, we go to the - // 1st (assuming that the first partial week counts). The same - // thing happens at the end of the month. - - // The other tricky thing is that we have to figure out whether - // the first partial week actually counts or not, based on the - // minimal first days in the week. And we have to use the - // correct first day of the week to delineate the week - // boundaries. - - // Here's our algorithm. First, we find the real boundaries of - // the month. Then we discard the first partial week if it - // doesn't count in this locale. Then we fill in the ends with - // phantom days, so that the first partial week and the last - // partial week are full weeks. We then have a nice square - // block of weeks. We do the usual rolling within this block, - // as is done elsewhere in this method. If we wind up on one of - // the phantom days that we added, we recognize this and pin to - // the first or the last day of the month. Easy, eh? - - // Another wrinkle: To fix jitterbug 81, we have to make all this - // work in the oddball month containing the Gregorian cutover. - // This month is 10 days shorter than usual, and also contains - // a discontinuity in the days; e.g., the default cutover month - // is Oct 1582, and goes from day of month 4 to day of month 15. - - // Normalize the DAY_OF_WEEK so that 0 is the first day of the week - // in this locale. We have dow in 0..6. - int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); - if (dow < 0) - dow += 7; - - // Find the day of month, compensating for cutover discontinuity. - int32_t dom = cDayOfMonth; - - // Find the day of the week (normalized for locale) for the first - // of the month. - int32_t fdm = (dow - dom + 1) % 7; - if (fdm < 0) - fdm += 7; - - // Get the first day of the first full week of the month, - // including phantom days, if any. Figure out if the first week - // counts or not; if it counts, then fill in phantom days. If - // not, advance to the first real full week (skip the partial week). - int32_t start; - if ((7 - fdm) < getMinimalDaysInFirstWeek()) - start = 8 - fdm; // Skip the first partial week - else - start = 1 - fdm; // This may be zero or negative - - // Get the day of the week (normalized for locale) for the last - // day of the month. - int32_t monthLen = cMonthLen; - int32_t ldm = (monthLen - dom + dow) % 7; - // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here. - - // Get the limit day for the blocked-off rectangular month; that - // is, the day which is one past the last day of the month, - // after the month has already been filled in with phantom days - // to fill out the last week. This day has a normalized DOW of 0. - int32_t limit = monthLen + 7 - ldm; - - // Now roll between start and (limit - 1). - int32_t gap = limit - start; - int32_t newDom = (dom + amount*7 - start) % gap; - if (newDom < 0) - newDom += gap; - newDom += start; - - // Finally, pin to the real start and end of the month. - if (newDom < 1) - newDom = 1; - if (newDom > monthLen) - newDom = monthLen; - - // Set the DAY_OF_MONTH. We rely on the fact that this field - // takes precedence over everything else (since all other fields - // are also set at this point). If this fact changes (if the - // disambiguation algorithm changes) then we will have to unset - // the appropriate fields here so that DAY_OF_MONTH is attended - // to. - - // If we are in the cutover month, manipulate ms directly. Don't do - // this in general because it doesn't work across DST boundaries - // (details, details). This takes care of the discontinuity. - setTimeInMillis(cMonthStart + (newDom-1)*kOneDay, status); - return; - } - - default: - Calendar::roll(field, amount, status); - return; - } -} - -// ------------------------------------- - - -/** -* Return the minimum value that this field could have, given the current date. -* For the Gregorian calendar, this is the same as getMinimum() and getGreatestMinimum(). -* @param field the time field. -* @return the minimum value that this field could have, given the current date. -* @deprecated ICU 2.6. Use getActualMinimum(UCalendarDateFields field) instead. -*/ -int32_t GregorianCalendar::getActualMinimum(EDateFields field) const -{ - return getMinimum((UCalendarDateFields)field); -} - -int32_t GregorianCalendar::getActualMinimum(EDateFields field, UErrorCode& /* status */) const -{ - return getMinimum((UCalendarDateFields)field); -} - -/** -* Return the minimum value that this field could have, given the current date. -* For the Gregorian calendar, this is the same as getMinimum() and getGreatestMinimum(). -* @param field the time field. -* @return the minimum value that this field could have, given the current date. -* @draft ICU 2.6. -*/ -int32_t GregorianCalendar::getActualMinimum(UCalendarDateFields field, UErrorCode& /* status */) const -{ - return getMinimum(field); -} - - -// ------------------------------------ - -/** -* Old year limits were least max 292269054, max 292278994. -*/ - -/** -* @stable ICU 2.0 -*/ -int32_t GregorianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { - return kGregorianCalendarLimits[field][limitType]; -} - -/** -* Return the maximum value that this field could have, given the current date. -* For example, with the date "Feb 3, 1997" and the DAY_OF_MONTH field, the actual -* maximum would be 28; for "Feb 3, 1996" it s 29. Similarly for a Hebrew calendar, -* for some years the actual maximum for MONTH is 12, and for others 13. -* @stable ICU 2.0 -*/ -int32_t GregorianCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const -{ - /* It is a known limitation that the code here (and in getActualMinimum) - * won't behave properly at the extreme limits of GregorianCalendar's - * representable range (except for the code that handles the YEAR - * field). That's because the ends of the representable range are at - * odd spots in the year. For calendars with the default Gregorian - * cutover, these limits are Sun Dec 02 16:47:04 GMT 292269055 BC to Sun - * Aug 17 07:12:55 GMT 292278994 AD, somewhat different for non-GMT - * zones. As a result, if the calendar is set to Aug 1 292278994 AD, - * the actual maximum of DAY_OF_MONTH is 17, not 30. If the date is Mar - * 31 in that year, the actual maximum month might be Jul, whereas is - * the date is Mar 15, the actual maximum might be Aug -- depending on - * the precise semantics that are desired. Similar considerations - * affect all fields. Nonetheless, this effect is sufficiently arcane - * that we permit it, rather than complicating the code to handle such - * intricacies. - liu 8/20/98 - - * UPDATE: No longer true, since we have pulled in the limit values on - * the year. - Liu 11/6/00 */ - - switch (field) { - - case UCAL_YEAR: - /* The year computation is no different, in principle, from the - * others, however, the range of possible maxima is large. In - * addition, the way we know we've exceeded the range is different. - * For these reasons, we use the special case code below to handle - * this field. - * - * The actual maxima for YEAR depend on the type of calendar: - * - * Gregorian = May 17, 292275056 BC - Aug 17, 292278994 AD - * Julian = Dec 2, 292269055 BC - Jan 3, 292272993 AD - * Hybrid = Dec 2, 292269055 BC - Aug 17, 292278994 AD - * - * We know we've exceeded the maximum when either the month, date, - * time, or era changes in response to setting the year. We don't - * check for month, date, and time here because the year and era are - * sufficient to detect an invalid year setting. NOTE: If code is - * added to check the month and date in the future for some reason, - * Feb 29 must be allowed to shift to Mar 1 when setting the year. - */ - { - if(U_FAILURE(status)) return 0; - Calendar *cal = clone(); - if(!cal) { - status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - - cal->setLenient(true); - - int32_t era = cal->get(UCAL_ERA, status); - UDate d = cal->getTime(status); - - /* Perform a binary search, with the invariant that lowGood is a - * valid year, and highBad is an out of range year. - */ - int32_t lowGood = kGregorianCalendarLimits[UCAL_YEAR][1]; - int32_t highBad = kGregorianCalendarLimits[UCAL_YEAR][2]+1; - while ((lowGood + 1) < highBad) { - int32_t y = (lowGood + highBad) / 2; - cal->set(UCAL_YEAR, y); - if (cal->get(UCAL_YEAR, status) == y && cal->get(UCAL_ERA, status) == era) { - lowGood = y; - } else { - highBad = y; - cal->setTime(d, status); // Restore original fields - } - } - - delete cal; - return lowGood; - } - - default: - return Calendar::getActualMaximum(field,status); - } -} - - -int32_t GregorianCalendar::handleGetExtendedYear() { - // the year to return - int32_t year = kEpochYear; - - // year field to use - int32_t yearField = UCAL_EXTENDED_YEAR; - - // There are three separate fields which could be used to - // derive the proper year. Use the one most recently set. - if (fStamp[yearField] < fStamp[UCAL_YEAR]) - yearField = UCAL_YEAR; - if (fStamp[yearField] < fStamp[UCAL_YEAR_WOY]) - yearField = UCAL_YEAR_WOY; - - // based on the "best" year field, get the year - switch(yearField) { - case UCAL_EXTENDED_YEAR: - year = internalGet(UCAL_EXTENDED_YEAR, kEpochYear); - break; - - case UCAL_YEAR: - { - // The year defaults to the epoch start, the era to AD - int32_t era = internalGet(UCAL_ERA, AD); - if (era == BC) { - year = 1 - internalGet(UCAL_YEAR, 1); // Convert to extended year - } else { - year = internalGet(UCAL_YEAR, kEpochYear); - } - } - break; - - case UCAL_YEAR_WOY: - year = handleGetExtendedYearFromWeekFields(internalGet(UCAL_YEAR_WOY), internalGet(UCAL_WEEK_OF_YEAR)); -#if defined (U_DEBUG_CAL) - // if(internalGet(UCAL_YEAR_WOY) != year) { - fprintf(stderr, "%s:%d: hGEYFWF[%d,%d] -> %d\n", - __FILE__, __LINE__,internalGet(UCAL_YEAR_WOY),internalGet(UCAL_WEEK_OF_YEAR),year); - //} -#endif - break; - - default: - year = kEpochYear; - } - return year; -} - -int32_t GregorianCalendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy) -{ - // convert year to extended form - int32_t era = internalGet(UCAL_ERA, AD); - if(era == BC) { - yearWoy = 1 - yearWoy; - } - return Calendar::handleGetExtendedYearFromWeekFields(yearWoy, woy); -} - - -// ------------------------------------- - -UBool -GregorianCalendar::inDaylightTime(UErrorCode& status) const -{ - if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) - return false; - - // Force an update of the state of the Calendar. - ((GregorianCalendar*)this)->complete(status); // cast away const - - return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : false); -} - -// ------------------------------------- - -/** -* Return the ERA. We need a special method for this because the -* default ERA is AD, but a zero (unset) ERA is BC. -*/ -int32_t -GregorianCalendar::internalGetEra() const { - return isSet(UCAL_ERA) ? internalGet(UCAL_ERA) : (int32_t)AD; -} - -const char * -GregorianCalendar::getType() const { - //static const char kGregorianType = "gregorian"; - - return "gregorian"; -} - -/** - * The system maintains a static default century start date and Year. They are - * initialized the first time they are used. Once the system default century date - * and year are set, they do not change. - */ -static UDate gSystemDefaultCenturyStart = DBL_MIN; -static int32_t gSystemDefaultCenturyStartYear = -1; -static icu::UInitOnce gSystemDefaultCenturyInit {}; - - -UBool GregorianCalendar::haveDefaultCentury() const -{ - return true; -} - -static void U_CALLCONV -initializeSystemDefaultCentury() -{ - // initialize systemDefaultCentury and systemDefaultCenturyYear based - // on the current time. They'll be set to 80 years before - // the current time. - UErrorCode status = U_ZERO_ERROR; - GregorianCalendar calendar(status); - if (U_SUCCESS(status)) { - calendar.setTime(Calendar::getNow(), status); - calendar.add(UCAL_YEAR, -80, status); - - gSystemDefaultCenturyStart = calendar.getTime(status); - gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); - } - // We have no recourse upon failure unless we want to propagate the failure - // out. -} - -UDate GregorianCalendar::defaultCenturyStart() const { - // lazy-evaluate systemDefaultCenturyStart - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStart; -} - -int32_t GregorianCalendar::defaultCenturyStartYear() const { - // lazy-evaluate systemDefaultCenturyStartYear - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStartYear; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1997-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File GREGOCAL.CPP +* +* Modification History: +* +* Date Name Description +* 02/05/97 clhuang Creation. +* 03/28/97 aliu Made highly questionable fix to computeFields to +* handle DST correctly. +* 04/22/97 aliu Cleaned up code drastically. Added monthLength(). +* Finished unimplemented parts of computeTime() for +* week-based date determination. Removed quetionable +* fix and wrote correct fix for computeFields() and +* daylight time handling. Rewrote inDaylightTime() +* and computeFields() to handle sensitive Daylight to +* Standard time transitions correctly. +* 05/08/97 aliu Added code review changes. Fixed isLeapYear() to +* not cutover. +* 08/12/97 aliu Added equivalentTo. Misc other fixes. Updated +* add() from Java source. +* 07/28/98 stephen Sync up with JDK 1.2 +* 09/14/98 stephen Changed type of kOneDay, kOneWeek to double. +* Fixed bug in roll() +* 10/15/99 aliu Fixed j31, incorrect WEEK_OF_YEAR computation. +* 10/15/99 aliu Fixed j32, cannot set date to Feb 29 2000 AD. +* {JDK bug 4210209 4209272} +* 11/15/99 weiv Added YEAR_WOY and DOW_LOCAL computation +* to timeToFields method, updated kMinValues, kMaxValues & kLeastMaxValues +* 12/09/99 aliu Fixed j81, calculation errors and roll bugs +* in year of cutover. +* 01/24/2000 aliu Revised computeJulianDay for YEAR YEAR_WOY WOY. +******************************************************************************** +*/ + +#include "unicode/utypes.h" +#include + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/gregocal.h" +#include "gregoimp.h" +#include "umutex.h" +#include "uassert.h" + +// ***************************************************************************** +// class GregorianCalendar +// ***************************************************************************** + +/** +* Note that the Julian date used here is not a true Julian date, since +* it is measured from midnight, not noon. This value is the Julian +* day number of January 1, 1970 (Gregorian calendar) at noon UTC. [LIU] +*/ + +static const int16_t kNumDays[] += {0,31,59,90,120,151,181,212,243,273,304,334}; // 0-based, for day-in-year +static const int16_t kLeapNumDays[] += {0,31,60,91,121,152,182,213,244,274,305,335}; // 0-based, for day-in-year +static const int8_t kMonthLength[] += {31,28,31,30,31,30,31,31,30,31,30,31}; // 0-based +static const int8_t kLeapMonthLength[] += {31,29,31,30,31,30,31,31,30,31,30,31}; // 0-based + +// setTimeInMillis() limits the Julian day range to +/-7F000000. +// This would seem to limit the year range to: +// ms=+183882168921600000 jd=7f000000 December 20, 5828963 AD +// ms=-184303902528000000 jd=81000000 September 20, 5838270 BC +// HOWEVER, CalendarRegressionTest/Test4167060 shows that the actual +// range limit on the year field is smaller (~ +/-140000). [alan 3.0] + +static const int32_t kGregorianCalendarLimits[UCAL_FIELD_COUNT][4] = { + // Minimum Greatest Least Maximum + // Minimum Maximum + { 0, 0, 1, 1}, // ERA + { 1, 1, 140742, 144683}, // YEAR + { 0, 0, 11, 11}, // MONTH + { 1, 1, 52, 53}, // WEEK_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH + { 1, 1, 28, 31}, // DAY_OF_MONTH + { 1, 1, 365, 366}, // DAY_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK + { -1, -1, 4, 5}, // DAY_OF_WEEK_IN_MONTH + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET + { -140742, -140742, 140742, 144683}, // YEAR_WOY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL + { -140742, -140742, 140742, 144683}, // EXTENDED_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH + { 0, 0, 11, 11}, // ORDINAL_MONTH +}; + +/* +*
+*                            Greatest       Least 
+* Field name        Minimum   Minimum     Maximum     Maximum
+* ----------        -------   -------     -------     -------
+* ERA                     0         0           1           1
+* YEAR                    1         1      140742      144683
+* MONTH                   0         0          11          11
+* WEEK_OF_YEAR            1         1          52          53
+* WEEK_OF_MONTH           0         0           4           6
+* DAY_OF_MONTH            1         1          28          31
+* DAY_OF_YEAR             1         1         365         366
+* DAY_OF_WEEK             1         1           7           7
+* DAY_OF_WEEK_IN_MONTH   -1        -1           4           5
+* AM_PM                   0         0           1           1
+* HOUR                    0         0          11          11
+* HOUR_OF_DAY             0         0          23          23
+* MINUTE                  0         0          59          59
+* SECOND                  0         0          59          59
+* MILLISECOND             0         0         999         999
+* ZONE_OFFSET           -12*      -12*         12*         12*
+* DST_OFFSET              0         0           1*          1*
+* YEAR_WOY                1         1      140742      144683
+* DOW_LOCAL               1         1           7           7
+* 
+* (*) In units of one-hour +*/ + +#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) +#include +#endif + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(GregorianCalendar) + +// 00:00:00 UTC, October 15, 1582, expressed in ms from the epoch. +// Note that only Italy and other Catholic countries actually +// observed this cutover. Most other countries followed in +// the next few centuries, some as late as 1928. [LIU] +// in Java, -12219292800000L +//const UDate GregorianCalendar::kPapalCutover = -12219292800000L; +static const uint32_t kCutoverJulianDay = 2299161; +static const UDate kPapalCutover = (2299161.0 - kEpochStartAsJulianDay) * U_MILLIS_PER_DAY; +//static const UDate kPapalCutoverJulian = (2299161.0 - kEpochStartAsJulianDay); + +// ------------------------------------- + +GregorianCalendar::GregorianCalendar(UErrorCode& status) +: Calendar(status), +fGregorianCutover(kPapalCutover), +fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), +fIsGregorian(true), fInvertGregorian(false) +{ + setTimeInMillis(getNow(), status); +} + +// ------------------------------------- + +GregorianCalendar::GregorianCalendar(TimeZone* zone, UErrorCode& status) +: Calendar(zone, Locale::getDefault(), status), +fGregorianCutover(kPapalCutover), +fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), +fIsGregorian(true), fInvertGregorian(false) +{ + setTimeInMillis(getNow(), status); +} + +// ------------------------------------- + +GregorianCalendar::GregorianCalendar(const TimeZone& zone, UErrorCode& status) +: Calendar(zone, Locale::getDefault(), status), +fGregorianCutover(kPapalCutover), +fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), +fIsGregorian(true), fInvertGregorian(false) +{ + setTimeInMillis(getNow(), status); +} + +// ------------------------------------- + +GregorianCalendar::GregorianCalendar(const Locale& aLocale, UErrorCode& status) +: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, status), +fGregorianCutover(kPapalCutover), +fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), +fIsGregorian(true), fInvertGregorian(false) +{ + setTimeInMillis(getNow(), status); +} + +// ------------------------------------- + +GregorianCalendar::GregorianCalendar(TimeZone* zone, const Locale& aLocale, + UErrorCode& status) + : Calendar(zone, aLocale, status), + fGregorianCutover(kPapalCutover), + fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), + fIsGregorian(true), fInvertGregorian(false) +{ + setTimeInMillis(getNow(), status); +} + +// ------------------------------------- + +GregorianCalendar::GregorianCalendar(const TimeZone& zone, const Locale& aLocale, + UErrorCode& status) + : Calendar(zone, aLocale, status), + fGregorianCutover(kPapalCutover), + fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), + fIsGregorian(true), fInvertGregorian(false) +{ + setTimeInMillis(getNow(), status); +} + +// ------------------------------------- + +GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date, + UErrorCode& status) + : Calendar(TimeZone::createDefault(), Locale::getDefault(), status), + fGregorianCutover(kPapalCutover), + fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), + fIsGregorian(true), fInvertGregorian(false) +{ + set(UCAL_ERA, AD); + set(UCAL_YEAR, year); + set(UCAL_MONTH, month); + set(UCAL_DATE, date); +} + +// ------------------------------------- + +GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date, + int32_t hour, int32_t minute, UErrorCode& status) + : Calendar(TimeZone::createDefault(), Locale::getDefault(), status), + fGregorianCutover(kPapalCutover), + fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), + fIsGregorian(true), fInvertGregorian(false) +{ + set(UCAL_ERA, AD); + set(UCAL_YEAR, year); + set(UCAL_MONTH, month); + set(UCAL_DATE, date); + set(UCAL_HOUR_OF_DAY, hour); + set(UCAL_MINUTE, minute); +} + +// ------------------------------------- + +GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date, + int32_t hour, int32_t minute, int32_t second, + UErrorCode& status) + : Calendar(TimeZone::createDefault(), Locale::getDefault(), status), + fGregorianCutover(kPapalCutover), + fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), + fIsGregorian(true), fInvertGregorian(false) +{ + set(UCAL_ERA, AD); + set(UCAL_YEAR, year); + set(UCAL_MONTH, month); + set(UCAL_DATE, date); + set(UCAL_HOUR_OF_DAY, hour); + set(UCAL_MINUTE, minute); + set(UCAL_SECOND, second); +} + +// ------------------------------------- + +GregorianCalendar::~GregorianCalendar() +{ +} + +// ------------------------------------- + +GregorianCalendar::GregorianCalendar(const GregorianCalendar &source) +: Calendar(source), +fGregorianCutover(source.fGregorianCutover), +fCutoverJulianDay(source.fCutoverJulianDay), fNormalizedGregorianCutover(source.fNormalizedGregorianCutover), fGregorianCutoverYear(source.fGregorianCutoverYear), +fIsGregorian(source.fIsGregorian), fInvertGregorian(source.fInvertGregorian) +{ +} + +// ------------------------------------- + +GregorianCalendar* GregorianCalendar::clone() const +{ + return new GregorianCalendar(*this); +} + +// ------------------------------------- + +GregorianCalendar & +GregorianCalendar::operator=(const GregorianCalendar &right) +{ + if (this != &right) + { + Calendar::operator=(right); + fGregorianCutover = right.fGregorianCutover; + fNormalizedGregorianCutover = right.fNormalizedGregorianCutover; + fGregorianCutoverYear = right.fGregorianCutoverYear; + fCutoverJulianDay = right.fCutoverJulianDay; + } + return *this; +} + +// ------------------------------------- + +UBool GregorianCalendar::isEquivalentTo(const Calendar& other) const +{ + // Calendar override. + return Calendar::isEquivalentTo(other) && + fGregorianCutover == ((GregorianCalendar*)&other)->fGregorianCutover; +} + +// ------------------------------------- + +void +GregorianCalendar::setGregorianChange(UDate date, UErrorCode& status) +{ + if (U_FAILURE(status)) + return; + + // Precompute two internal variables which we use to do the actual + // cutover computations. These are the normalized cutover, which is the + // midnight at or before the cutover, and the cutover year. The + // normalized cutover is in pure date milliseconds; it contains no time + // of day or timezone component, and it used to compare against other + // pure date values. + double cutoverDay = ClockMath::floorDivide(date, (double)kOneDay); + + // Handle the rare case of numeric overflow where the user specifies a time + // outside of INT32_MIN .. INT32_MAX number of days. + + if (cutoverDay <= INT32_MIN) { + cutoverDay = INT32_MIN; + fGregorianCutover = fNormalizedGregorianCutover = cutoverDay * kOneDay; + } else if (cutoverDay >= INT32_MAX) { + cutoverDay = INT32_MAX; + fGregorianCutover = fNormalizedGregorianCutover = cutoverDay * kOneDay; + } else { + fNormalizedGregorianCutover = cutoverDay * kOneDay; + fGregorianCutover = date; + } + + // Normalize the year so BC values are represented as 0 and negative + // values. + GregorianCalendar *cal = new GregorianCalendar(getTimeZone(), status); + /* test for nullptr */ + if (cal == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if(U_FAILURE(status)) + return; + cal->setTime(date, status); + fGregorianCutoverYear = cal->get(UCAL_YEAR, status); + if (cal->get(UCAL_ERA, status) == BC) + fGregorianCutoverYear = 1 - fGregorianCutoverYear; + fCutoverJulianDay = (int32_t)cutoverDay; + delete cal; +} + + +void GregorianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) { + int32_t eyear, month, dayOfMonth, dayOfYear, unusedRemainder; + + + if(U_FAILURE(status)) { + return; + } + +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: jd%d- (greg's %d)- [cut=%d]\n", + __FILE__, __LINE__, julianDay, getGregorianDayOfYear(), fCutoverJulianDay); +#endif + + + if (julianDay >= fCutoverJulianDay) { + month = getGregorianMonth(); + dayOfMonth = getGregorianDayOfMonth(); + dayOfYear = getGregorianDayOfYear(); + eyear = getGregorianYear(); + } else { + // The Julian epoch day (not the same as Julian Day) + // is zero on Saturday December 30, 0 (Gregorian). + int32_t julianEpochDay = julianDay - (kJan1_1JulianDay - 2); + eyear = (int32_t) ClockMath::floorDivide((4.0*julianEpochDay) + 1464.0, (int32_t) 1461, &unusedRemainder); + + // Compute the Julian calendar day number for January 1, eyear + int32_t january1 = 365*(eyear-1) + ClockMath::floorDivide(eyear-1, (int32_t)4); + dayOfYear = (julianEpochDay - january1); // 0-based + + // Julian leap years occurred historically every 4 years starting + // with 8 AD. Before 8 AD the spacing is irregular; every 3 years + // from 45 BC to 9 BC, and then none until 8 AD. However, we don't + // implement this historical detail; instead, we implement the + // computationally cleaner proleptic calendar, which assumes + // consistent 4-year cycles throughout time. + UBool isLeap = ((eyear&0x3) == 0); // equiv. to (eyear%4 == 0) + + // Common Julian/Gregorian calculation + int32_t correction = 0; + int32_t march1 = isLeap ? 60 : 59; // zero-based DOY for March 1 + if (dayOfYear >= march1) { + correction = isLeap ? 1 : 2; + } + month = (12 * (dayOfYear + correction) + 6) / 367; // zero-based month + dayOfMonth = dayOfYear - (isLeap?kLeapNumDays[month]:kNumDays[month]) + 1; // one-based DOM + ++dayOfYear; +#if defined (U_DEBUG_CAL) + // fprintf(stderr, "%d - %d[%d] + 1\n", dayOfYear, isLeap?kLeapNumDays[month]:kNumDays[month], month ); + // fprintf(stderr, "%s:%d: greg's HCF %d -> %d/%d/%d not %d/%d/%d\n", + // __FILE__, __LINE__,julianDay, + // eyear,month,dayOfMonth, + // getGregorianYear(), getGregorianMonth(), getGregorianDayOfMonth() ); + fprintf(stderr, "%s:%d: doy %d (greg's %d)- [cut=%d]\n", + __FILE__, __LINE__, dayOfYear, getGregorianDayOfYear(), fCutoverJulianDay); +#endif + + } + + // [j81] if we are after the cutover in its year, shift the day of the year + if((eyear == fGregorianCutoverYear) && (julianDay >= fCutoverJulianDay)) { + //from handleComputeMonthStart + int32_t gregShift = Grego::gregorianShift(eyear); +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: gregorian shift %d ::: doy%d => %d [cut=%d]\n", + __FILE__, __LINE__,gregShift, dayOfYear, dayOfYear+gregShift, fCutoverJulianDay); +#endif + dayOfYear += gregShift; + } + + internalSet(UCAL_MONTH, month); + internalSet(UCAL_ORDINAL_MONTH, month); + internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); + internalSet(UCAL_DAY_OF_YEAR, dayOfYear); + internalSet(UCAL_EXTENDED_YEAR, eyear); + int32_t era = AD; + if (eyear < 1) { + era = BC; + eyear = 1 - eyear; + } + internalSet(UCAL_ERA, era); + internalSet(UCAL_YEAR, eyear); +} + + +// ------------------------------------- + +UDate +GregorianCalendar::getGregorianChange() const +{ + return fGregorianCutover; +} + +// ------------------------------------- + +UBool +GregorianCalendar::isLeapYear(int32_t year) const +{ + // MSVC complains bitterly if we try to use Grego::isLeapYear here + // NOTE: year&0x3 == year%4 + return (year >= fGregorianCutoverYear ? + (((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0))) : // Gregorian + ((year&0x3) == 0)); // Julian +} + +// ------------------------------------- + +int32_t GregorianCalendar::handleComputeJulianDay(UCalendarDateFields bestField) +{ + fInvertGregorian = false; + + int32_t jd = Calendar::handleComputeJulianDay(bestField); + + if((bestField == UCAL_WEEK_OF_YEAR) && // if we are doing WOY calculations, we are counting relative to Jan 1 *julian* + (internalGet(UCAL_EXTENDED_YEAR)==fGregorianCutoverYear) && + jd >= fCutoverJulianDay) { + fInvertGregorian = true; // So that the Julian Jan 1 will be used in handleComputeMonthStart + return Calendar::handleComputeJulianDay(bestField); + } + + + // The following check handles portions of the cutover year BEFORE the + // cutover itself happens. + //if ((fIsGregorian==true) != (jd >= fCutoverJulianDay)) { /* cutoverJulianDay)) { */ + if ((fIsGregorian) != (jd >= fCutoverJulianDay)) { /* cutoverJulianDay)) { */ +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: jd [invert] %d\n", + __FILE__, __LINE__, jd); +#endif + fInvertGregorian = true; + jd = Calendar::handleComputeJulianDay(bestField); +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: fIsGregorian %s, fInvertGregorian %s - ", + __FILE__, __LINE__,fIsGregorian?"T":"F", fInvertGregorian?"T":"F"); + fprintf(stderr, " jd NOW %d\n", + jd); +#endif + } else { +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: jd [==] %d - %sfIsGregorian %sfInvertGregorian, %d\n", + __FILE__, __LINE__, jd, fIsGregorian?"T":"F", fInvertGregorian?"T":"F", bestField); +#endif + } + + if(fIsGregorian && (internalGet(UCAL_EXTENDED_YEAR) == fGregorianCutoverYear)) { + int32_t gregShift = Grego::gregorianShift(internalGet(UCAL_EXTENDED_YEAR)); + if (bestField == UCAL_DAY_OF_YEAR) { +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: [DOY%d] gregorian shift of JD %d += %d\n", + __FILE__, __LINE__, fFields[bestField],jd, gregShift); +#endif + jd -= gregShift; + } else if ( bestField == UCAL_WEEK_OF_MONTH ) { + int32_t weekShift = 14; +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: [WOY/WOM] gregorian week shift of %d += %d\n", + __FILE__, __LINE__, jd, weekShift); +#endif + jd += weekShift; // shift by weeks for week based fields. + } + } + + return jd; +} + +int32_t GregorianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, + + UBool /* useMonth */) const +{ + GregorianCalendar *nonConstThis = (GregorianCalendar*)this; // cast away const + + // If the month is out of range, adjust it into range, and + // modify the extended year value accordingly. + if (month < 0 || month > 11) { + eyear += ClockMath::floorDivide(month, 12, &month); + } + + UBool isLeap = eyear%4 == 0; + int64_t y = (int64_t)eyear-1; + int64_t julianDay = 365*y + ClockMath::floorDivide(y, (int64_t)4) + (kJan1_1JulianDay - 3); + + nonConstThis->fIsGregorian = (eyear >= fGregorianCutoverYear); +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: (hcms%d/%d) fIsGregorian %s, fInvertGregorian %s\n", + __FILE__, __LINE__, eyear,month, fIsGregorian?"T":"F", fInvertGregorian?"T":"F"); +#endif + if (fInvertGregorian) { + nonConstThis->fIsGregorian = !fIsGregorian; + } + if (fIsGregorian) { + isLeap = isLeap && ((eyear%100 != 0) || (eyear%400 == 0)); + // Add 2 because Gregorian calendar starts 2 days after + // Julian calendar + int32_t gregShift = Grego::gregorianShift(eyear); +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: (hcms%d/%d) gregorian shift of %d += %d\n", + __FILE__, __LINE__, eyear, month, julianDay, gregShift); +#endif + julianDay += gregShift; + } + + // At this point julianDay indicates the day BEFORE the first + // day of January 1, of either the Julian or Gregorian + // calendar. + + if (month != 0) { + julianDay += isLeap?kLeapNumDays[month]:kNumDays[month]; + } + + return static_cast(julianDay); +} + +int32_t GregorianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const +{ + // If the month is out of range, adjust it into range, and + // modify the extended year value accordingly. + if (month < 0 || month > 11) { + extendedYear += ClockMath::floorDivide(month, 12, &month); + } + + return isLeapYear(extendedYear) ? kLeapMonthLength[month] : kMonthLength[month]; +} + +int32_t GregorianCalendar::handleGetYearLength(int32_t eyear) const { + return isLeapYear(eyear) ? 366 : 365; +} + + +int32_t +GregorianCalendar::monthLength(int32_t month) const +{ + int32_t year = internalGet(UCAL_EXTENDED_YEAR); + return handleGetMonthLength(year, month); +} + +// ------------------------------------- + +int32_t +GregorianCalendar::monthLength(int32_t month, int32_t year) const +{ + return isLeapYear(year) ? kLeapMonthLength[month] : kMonthLength[month]; +} + +// ------------------------------------- + +int32_t +GregorianCalendar::yearLength(int32_t year) const +{ + return isLeapYear(year) ? 366 : 365; +} + +// ------------------------------------- + +int32_t +GregorianCalendar::yearLength() const +{ + return isLeapYear(internalGet(UCAL_YEAR)) ? 366 : 365; +} + +// ------------------------------------- + +/** +* After adjustments such as add(MONTH), add(YEAR), we don't want the +* month to jump around. E.g., we don't want Jan 31 + 1 month to go to Mar +* 3, we want it to go to Feb 28. Adjustments which might run into this +* problem call this method to retain the proper month. +*/ +void +GregorianCalendar::pinDayOfMonth() +{ + int32_t monthLen = monthLength(internalGetMonth()); + int32_t dom = internalGet(UCAL_DATE); + if(dom > monthLen) + set(UCAL_DATE, monthLen); +} + +// ------------------------------------- + + +UBool +GregorianCalendar::validateFields() const +{ + for (int32_t field = 0; field < UCAL_FIELD_COUNT; field++) { + // Ignore DATE and DAY_OF_YEAR which are handled below + if (field != UCAL_DATE && + field != UCAL_DAY_OF_YEAR && + isSet((UCalendarDateFields)field) && + ! boundsCheck(internalGet((UCalendarDateFields)field), (UCalendarDateFields)field)) + return false; + } + + // Values differ in Least-Maximum and Maximum should be handled + // specially. + if (isSet(UCAL_DATE)) { + int32_t date = internalGet(UCAL_DATE); + if (date < getMinimum(UCAL_DATE) || + date > monthLength(internalGetMonth())) { + return false; + } + } + + if (isSet(UCAL_DAY_OF_YEAR)) { + int32_t days = internalGet(UCAL_DAY_OF_YEAR); + if (days < 1 || days > yearLength()) { + return false; + } + } + + // Handle DAY_OF_WEEK_IN_MONTH, which must not have the value zero. + // We've checked against minimum and maximum above already. + if (isSet(UCAL_DAY_OF_WEEK_IN_MONTH) && + 0 == internalGet(UCAL_DAY_OF_WEEK_IN_MONTH)) { + return false; + } + + return true; +} + +// ------------------------------------- + +UBool +GregorianCalendar::boundsCheck(int32_t value, UCalendarDateFields field) const +{ + return value >= getMinimum(field) && value <= getMaximum(field); +} + +// ------------------------------------- + +UDate +GregorianCalendar::getEpochDay(UErrorCode& status) +{ + complete(status); + // Divide by 1000 (convert to seconds) in order to prevent overflow when + // dealing with UDate(Long.MIN_VALUE) and UDate(Long.MAX_VALUE). + double wallSec = internalGetTime()/1000 + (internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET))/1000; + + return ClockMath::floorDivide(wallSec, kOneDay/1000.0); +} + +// ------------------------------------- + + +// ------------------------------------- + +/** +* Compute the julian day number of the day BEFORE the first day of +* January 1, year 1 of the given calendar. If julianDay == 0, it +* specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian +* or Gregorian). +*/ +double GregorianCalendar::computeJulianDayOfYear(UBool isGregorian, + int32_t year, UBool& isLeap) +{ + isLeap = year%4 == 0; + int32_t y = year - 1; + double julianDay = 365.0*y + ClockMath::floorDivide(y, 4) + (kJan1_1JulianDay - 3); + + if (isGregorian) { + isLeap = isLeap && ((year%100 != 0) || (year%400 == 0)); + // Add 2 because Gregorian calendar starts 2 days after Julian calendar + julianDay += Grego::gregorianShift(year); + } + + return julianDay; +} + +// /** +// * Compute the day of week, relative to the first day of week, from +// * 0..6, of the current DOW_LOCAL or DAY_OF_WEEK fields. This is +// * equivalent to get(DOW_LOCAL) - 1. +// */ +// int32_t GregorianCalendar::computeRelativeDOW() const { +// int32_t relDow = 0; +// if (fStamp[UCAL_DOW_LOCAL] > fStamp[UCAL_DAY_OF_WEEK]) { +// relDow = internalGet(UCAL_DOW_LOCAL) - 1; // 1-based +// } else if (fStamp[UCAL_DAY_OF_WEEK] != kUnset) { +// relDow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); +// if (relDow < 0) relDow += 7; +// } +// return relDow; +// } + +// /** +// * Compute the day of week, relative to the first day of week, +// * from 0..6 of the given julian day. +// */ +// int32_t GregorianCalendar::computeRelativeDOW(double julianDay) const { +// int32_t relDow = julianDayToDayOfWeek(julianDay) - getFirstDayOfWeek(); +// if (relDow < 0) { +// relDow += 7; +// } +// return relDow; +// } + +// /** +// * Compute the DOY using the WEEK_OF_YEAR field and the julian day +// * of the day BEFORE January 1 of a year (a return value from +// * computeJulianDayOfYear). +// */ +// int32_t GregorianCalendar::computeDOYfromWOY(double julianDayOfYear) const { +// // Compute DOY from day of week plus week of year + +// // Find the day of the week for the first of this year. This +// // is zero-based, with 0 being the locale-specific first day of +// // the week. Add 1 to get first day of year. +// int32_t fdy = computeRelativeDOW(julianDayOfYear + 1); + +// return +// // Compute doy of first (relative) DOW of WOY 1 +// (((7 - fdy) < getMinimalDaysInFirstWeek()) +// ? (8 - fdy) : (1 - fdy)) + +// // Adjust for the week number. +// + (7 * (internalGet(UCAL_WEEK_OF_YEAR) - 1)) + +// // Adjust for the DOW +// + computeRelativeDOW(); +// } + +// ------------------------------------- + +double +GregorianCalendar::millisToJulianDay(UDate millis) +{ + return (double)kEpochStartAsJulianDay + ClockMath::floorDivide(millis, (double)kOneDay); +} + +// ------------------------------------- + +UDate +GregorianCalendar::julianDayToMillis(double julian) +{ + return (UDate) ((julian - kEpochStartAsJulianDay) * (double) kOneDay); +} + +// ------------------------------------- + +int32_t +GregorianCalendar::aggregateStamp(int32_t stamp_a, int32_t stamp_b) +{ + return (((stamp_a != kUnset && stamp_b != kUnset) + ? uprv_max(stamp_a, stamp_b) + : (int32_t)kUnset)); +} + +// ------------------------------------- + +/** +* Roll a field by a signed amount. +* Note: This will be made public later. [LIU] +*/ + +void +GregorianCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { + roll((UCalendarDateFields) field, amount, status); +} + +void +GregorianCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) UPRV_NO_SANITIZE_UNDEFINED { + if((amount == 0) || U_FAILURE(status)) { + return; + } + + // J81 processing. (gregorian cutover) + UBool inCutoverMonth = false; + int32_t cMonthLen=0; // 'c' for cutover; in days + int32_t cDayOfMonth=0; // no discontinuity: [0, cMonthLen) + double cMonthStart=0.0; // in ms + + // Common code - see if we're in the cutover month of the cutover year + if(get(UCAL_EXTENDED_YEAR, status) == fGregorianCutoverYear) { + switch (field) { + case UCAL_DAY_OF_MONTH: + case UCAL_WEEK_OF_MONTH: + { + int32_t max = monthLength(internalGetMonth()); + UDate t = internalGetTime(); + // We subtract 1 from the DAY_OF_MONTH to make it zero-based, and an + // additional 10 if we are after the cutover. Thus the monthStart + // value will be correct iff we actually are in the cutover month. + cDayOfMonth = internalGet(UCAL_DAY_OF_MONTH) - ((t >= fGregorianCutover) ? 10 : 0); + cMonthStart = t - ((cDayOfMonth - 1) * kOneDay); + // A month containing the cutover is 10 days shorter. + if ((cMonthStart < fGregorianCutover) && + (cMonthStart + (cMonthLen=(max-10))*kOneDay >= fGregorianCutover)) { + inCutoverMonth = true; + } + } + break; + default: + ; + } + } + + switch (field) { + case UCAL_WEEK_OF_YEAR: { + // Unlike WEEK_OF_MONTH, WEEK_OF_YEAR never shifts the day of the + // week. Also, rolling the week of the year can have seemingly + // strange effects simply because the year of the week of year + // may be different from the calendar year. For example, the + // date Dec 28, 1997 is the first day of week 1 of 1998 (if + // weeks start on Sunday and the minimal days in first week is + // <= 3). + int32_t woy = get(UCAL_WEEK_OF_YEAR, status); + // Get the ISO year, which matches the week of year. This + // may be one year before or after the calendar year. + int32_t isoYear = get(UCAL_YEAR_WOY, status); + int32_t isoDoy = internalGet(UCAL_DAY_OF_YEAR); + if (internalGetMonth() == UCAL_JANUARY) { + if (woy >= 52) { + isoDoy += handleGetYearLength(isoYear); + } + } else { + if (woy == 1) { + isoDoy -= handleGetYearLength(isoYear - 1); + } + } + woy += amount; + // Do fast checks to avoid unnecessary computation: + if (woy < 1 || woy > 52) { + // Determine the last week of the ISO year. + // We do this using the standard formula we use + // everywhere in this file. If we can see that the + // days at the end of the year are going to fall into + // week 1 of the next year, we drop the last week by + // subtracting 7 from the last day of the year. + int32_t lastDoy = handleGetYearLength(isoYear); + int32_t lastRelDow = (lastDoy - isoDoy + internalGet(UCAL_DAY_OF_WEEK) - + getFirstDayOfWeek()) % 7; + if (lastRelDow < 0) lastRelDow += 7; + if ((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) lastDoy -= 7; + int32_t lastWoy = weekNumber(lastDoy, lastRelDow + 1); + woy = ((woy + lastWoy - 1) % lastWoy) + 1; + } + set(UCAL_WEEK_OF_YEAR, woy); + set(UCAL_YEAR_WOY,isoYear); + return; + } + + case UCAL_DAY_OF_MONTH: + if( !inCutoverMonth ) { + Calendar::roll(field, amount, status); + return; + } else { + // [j81] 1582 special case for DOM + // The default computation works except when the current month + // contains the Gregorian cutover. We handle this special case + // here. [j81 - aliu] + double monthLen = cMonthLen * kOneDay; + double msIntoMonth = uprv_fmod(internalGetTime() - cMonthStart + + amount * kOneDay, monthLen); + if (msIntoMonth < 0) { + msIntoMonth += monthLen; + } +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: roll DOM %d -> %.0lf ms \n", + __FILE__, __LINE__,amount, cMonthLen, cMonthStart+msIntoMonth); +#endif + setTimeInMillis(cMonthStart + msIntoMonth, status); + return; + } + + case UCAL_WEEK_OF_MONTH: + if( !inCutoverMonth ) { + Calendar::roll(field, amount, status); + return; + } else { +#if defined (U_DEBUG_CAL) + fprintf(stderr, "%s:%d: roll WOM %d ??????????????????? \n", + __FILE__, __LINE__,amount); +#endif + // NOTE: following copied from the old + // GregorianCalendar::roll( WEEK_OF_MONTH ) code + + // This is tricky, because during the roll we may have to shift + // to a different day of the week. For example: + + // s m t w r f s + // 1 2 3 4 5 + // 6 7 8 9 10 11 12 + + // When rolling from the 6th or 7th back one week, we go to the + // 1st (assuming that the first partial week counts). The same + // thing happens at the end of the month. + + // The other tricky thing is that we have to figure out whether + // the first partial week actually counts or not, based on the + // minimal first days in the week. And we have to use the + // correct first day of the week to delineate the week + // boundaries. + + // Here's our algorithm. First, we find the real boundaries of + // the month. Then we discard the first partial week if it + // doesn't count in this locale. Then we fill in the ends with + // phantom days, so that the first partial week and the last + // partial week are full weeks. We then have a nice square + // block of weeks. We do the usual rolling within this block, + // as is done elsewhere in this method. If we wind up on one of + // the phantom days that we added, we recognize this and pin to + // the first or the last day of the month. Easy, eh? + + // Another wrinkle: To fix jitterbug 81, we have to make all this + // work in the oddball month containing the Gregorian cutover. + // This month is 10 days shorter than usual, and also contains + // a discontinuity in the days; e.g., the default cutover month + // is Oct 1582, and goes from day of month 4 to day of month 15. + + // Normalize the DAY_OF_WEEK so that 0 is the first day of the week + // in this locale. We have dow in 0..6. + int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); + if (dow < 0) + dow += 7; + + // Find the day of month, compensating for cutover discontinuity. + int32_t dom = cDayOfMonth; + + // Find the day of the week (normalized for locale) for the first + // of the month. + int32_t fdm = (dow - dom + 1) % 7; + if (fdm < 0) + fdm += 7; + + // Get the first day of the first full week of the month, + // including phantom days, if any. Figure out if the first week + // counts or not; if it counts, then fill in phantom days. If + // not, advance to the first real full week (skip the partial week). + int32_t start; + if ((7 - fdm) < getMinimalDaysInFirstWeek()) + start = 8 - fdm; // Skip the first partial week + else + start = 1 - fdm; // This may be zero or negative + + // Get the day of the week (normalized for locale) for the last + // day of the month. + int32_t monthLen = cMonthLen; + int32_t ldm = (monthLen - dom + dow) % 7; + // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here. + + // Get the limit day for the blocked-off rectangular month; that + // is, the day which is one past the last day of the month, + // after the month has already been filled in with phantom days + // to fill out the last week. This day has a normalized DOW of 0. + int32_t limit = monthLen + 7 - ldm; + + // Now roll between start and (limit - 1). + int32_t gap = limit - start; + int32_t newDom = (dom + amount*7 - start) % gap; + if (newDom < 0) + newDom += gap; + newDom += start; + + // Finally, pin to the real start and end of the month. + if (newDom < 1) + newDom = 1; + if (newDom > monthLen) + newDom = monthLen; + + // Set the DAY_OF_MONTH. We rely on the fact that this field + // takes precedence over everything else (since all other fields + // are also set at this point). If this fact changes (if the + // disambiguation algorithm changes) then we will have to unset + // the appropriate fields here so that DAY_OF_MONTH is attended + // to. + + // If we are in the cutover month, manipulate ms directly. Don't do + // this in general because it doesn't work across DST boundaries + // (details, details). This takes care of the discontinuity. + setTimeInMillis(cMonthStart + (newDom-1)*kOneDay, status); + return; + } + + default: + Calendar::roll(field, amount, status); + return; + } +} + +// ------------------------------------- + + +/** +* Return the minimum value that this field could have, given the current date. +* For the Gregorian calendar, this is the same as getMinimum() and getGreatestMinimum(). +* @param field the time field. +* @return the minimum value that this field could have, given the current date. +* @deprecated ICU 2.6. Use getActualMinimum(UCalendarDateFields field) instead. +*/ +int32_t GregorianCalendar::getActualMinimum(EDateFields field) const +{ + return getMinimum((UCalendarDateFields)field); +} + +int32_t GregorianCalendar::getActualMinimum(EDateFields field, UErrorCode& /* status */) const +{ + return getMinimum((UCalendarDateFields)field); +} + +/** +* Return the minimum value that this field could have, given the current date. +* For the Gregorian calendar, this is the same as getMinimum() and getGreatestMinimum(). +* @param field the time field. +* @return the minimum value that this field could have, given the current date. +* @draft ICU 2.6. +*/ +int32_t GregorianCalendar::getActualMinimum(UCalendarDateFields field, UErrorCode& /* status */) const +{ + return getMinimum(field); +} + + +// ------------------------------------ + +/** +* Old year limits were least max 292269054, max 292278994. +*/ + +/** +* @stable ICU 2.0 +*/ +int32_t GregorianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { + return kGregorianCalendarLimits[field][limitType]; +} + +/** +* Return the maximum value that this field could have, given the current date. +* For example, with the date "Feb 3, 1997" and the DAY_OF_MONTH field, the actual +* maximum would be 28; for "Feb 3, 1996" it s 29. Similarly for a Hebrew calendar, +* for some years the actual maximum for MONTH is 12, and for others 13. +* @stable ICU 2.0 +*/ +int32_t GregorianCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const +{ + /* It is a known limitation that the code here (and in getActualMinimum) + * won't behave properly at the extreme limits of GregorianCalendar's + * representable range (except for the code that handles the YEAR + * field). That's because the ends of the representable range are at + * odd spots in the year. For calendars with the default Gregorian + * cutover, these limits are Sun Dec 02 16:47:04 GMT 292269055 BC to Sun + * Aug 17 07:12:55 GMT 292278994 AD, somewhat different for non-GMT + * zones. As a result, if the calendar is set to Aug 1 292278994 AD, + * the actual maximum of DAY_OF_MONTH is 17, not 30. If the date is Mar + * 31 in that year, the actual maximum month might be Jul, whereas is + * the date is Mar 15, the actual maximum might be Aug -- depending on + * the precise semantics that are desired. Similar considerations + * affect all fields. Nonetheless, this effect is sufficiently arcane + * that we permit it, rather than complicating the code to handle such + * intricacies. - liu 8/20/98 + + * UPDATE: No longer true, since we have pulled in the limit values on + * the year. - Liu 11/6/00 */ + + switch (field) { + + case UCAL_YEAR: + /* The year computation is no different, in principle, from the + * others, however, the range of possible maxima is large. In + * addition, the way we know we've exceeded the range is different. + * For these reasons, we use the special case code below to handle + * this field. + * + * The actual maxima for YEAR depend on the type of calendar: + * + * Gregorian = May 17, 292275056 BC - Aug 17, 292278994 AD + * Julian = Dec 2, 292269055 BC - Jan 3, 292272993 AD + * Hybrid = Dec 2, 292269055 BC - Aug 17, 292278994 AD + * + * We know we've exceeded the maximum when either the month, date, + * time, or era changes in response to setting the year. We don't + * check for month, date, and time here because the year and era are + * sufficient to detect an invalid year setting. NOTE: If code is + * added to check the month and date in the future for some reason, + * Feb 29 must be allowed to shift to Mar 1 when setting the year. + */ + { + if(U_FAILURE(status)) return 0; + Calendar *cal = clone(); + if(!cal) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + + cal->setLenient(true); + + int32_t era = cal->get(UCAL_ERA, status); + UDate d = cal->getTime(status); + + /* Perform a binary search, with the invariant that lowGood is a + * valid year, and highBad is an out of range year. + */ + int32_t lowGood = kGregorianCalendarLimits[UCAL_YEAR][1]; + int32_t highBad = kGregorianCalendarLimits[UCAL_YEAR][2]+1; + while ((lowGood + 1) < highBad) { + int32_t y = (lowGood + highBad) / 2; + cal->set(UCAL_YEAR, y); + if (cal->get(UCAL_YEAR, status) == y && cal->get(UCAL_ERA, status) == era) { + lowGood = y; + } else { + highBad = y; + cal->setTime(d, status); // Restore original fields + } + } + + delete cal; + return lowGood; + } + + default: + return Calendar::getActualMaximum(field,status); + } +} + + +int32_t GregorianCalendar::handleGetExtendedYear() { + // the year to return + int32_t year = kEpochYear; + + // year field to use + int32_t yearField = UCAL_EXTENDED_YEAR; + + // There are three separate fields which could be used to + // derive the proper year. Use the one most recently set. + if (fStamp[yearField] < fStamp[UCAL_YEAR]) + yearField = UCAL_YEAR; + if (fStamp[yearField] < fStamp[UCAL_YEAR_WOY]) + yearField = UCAL_YEAR_WOY; + + // based on the "best" year field, get the year + switch(yearField) { + case UCAL_EXTENDED_YEAR: + year = internalGet(UCAL_EXTENDED_YEAR, kEpochYear); + break; + + case UCAL_YEAR: + { + // The year defaults to the epoch start, the era to AD + int32_t era = internalGet(UCAL_ERA, AD); + if (era == BC) { + year = 1 - internalGet(UCAL_YEAR, 1); // Convert to extended year + } else { + year = internalGet(UCAL_YEAR, kEpochYear); + } + } + break; + + case UCAL_YEAR_WOY: + year = handleGetExtendedYearFromWeekFields(internalGet(UCAL_YEAR_WOY), internalGet(UCAL_WEEK_OF_YEAR)); +#if defined (U_DEBUG_CAL) + // if(internalGet(UCAL_YEAR_WOY) != year) { + fprintf(stderr, "%s:%d: hGEYFWF[%d,%d] -> %d\n", + __FILE__, __LINE__,internalGet(UCAL_YEAR_WOY),internalGet(UCAL_WEEK_OF_YEAR),year); + //} +#endif + break; + + default: + year = kEpochYear; + } + return year; +} + +int32_t GregorianCalendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy) +{ + // convert year to extended form + int32_t era = internalGet(UCAL_ERA, AD); + if(era == BC) { + yearWoy = 1 - yearWoy; + } + return Calendar::handleGetExtendedYearFromWeekFields(yearWoy, woy); +} + + +// ------------------------------------- + +/** +* Return the ERA. We need a special method for this because the +* default ERA is AD, but a zero (unset) ERA is BC. +*/ +int32_t +GregorianCalendar::internalGetEra() const { + return isSet(UCAL_ERA) ? internalGet(UCAL_ERA) : (int32_t)AD; +} + +const char * +GregorianCalendar::getType() const { + //static const char kGregorianType = "gregorian"; + + return "gregorian"; +} + +/** + * The system maintains a static default century start date and Year. They are + * initialized the first time they are used. Once the system default century date + * and year are set, they do not change. + */ +static UDate gSystemDefaultCenturyStart = DBL_MIN; +static int32_t gSystemDefaultCenturyStartYear = -1; +static icu::UInitOnce gSystemDefaultCenturyInit {}; + + +UBool GregorianCalendar::haveDefaultCentury() const +{ + return true; +} + +static void U_CALLCONV +initializeSystemDefaultCentury() +{ + // initialize systemDefaultCentury and systemDefaultCenturyYear based + // on the current time. They'll be set to 80 years before + // the current time. + UErrorCode status = U_ZERO_ERROR; + GregorianCalendar calendar(status); + if (U_SUCCESS(status)) { + calendar.setTime(Calendar::getNow(), status); + calendar.add(UCAL_YEAR, -80, status); + + gSystemDefaultCenturyStart = calendar.getTime(status); + gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); + } + // We have no recourse upon failure unless we want to propagate the failure + // out. +} + +UDate GregorianCalendar::defaultCenturyStart() const { + // lazy-evaluate systemDefaultCenturyStart + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStart; +} + +int32_t GregorianCalendar::defaultCenturyStartYear() const { + // lazy-evaluate systemDefaultCenturyStartYear + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStartYear; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/gregoimp.cpp b/deps/icu-small/source/i18n/gregoimp.cpp index f862cd1d831424..54d07ea36c9319 100644 --- a/deps/icu-small/source/i18n/gregoimp.cpp +++ b/deps/icu-small/source/i18n/gregoimp.cpp @@ -1,171 +1,176 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (c) 2003-2008, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - * Author: Alan Liu - * Created: September 2 2003 - * Since: ICU 2.8 - ********************************************************************** - */ - -#include "gregoimp.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/ucal.h" -#include "uresimp.h" -#include "cstring.h" -#include "uassert.h" - -U_NAMESPACE_BEGIN - -int32_t ClockMath::floorDivide(int32_t numerator, int32_t denominator) { - return (numerator >= 0) ? - numerator / denominator : ((numerator + 1) / denominator) - 1; -} - -int64_t ClockMath::floorDivide(int64_t numerator, int64_t denominator) { - return (numerator >= 0) ? - numerator / denominator : ((numerator + 1) / denominator) - 1; -} - -int32_t ClockMath::floorDivide(double numerator, int32_t denominator, - int32_t* remainder) { - // For an integer n and representable ⌊x/n⌋, ⌊RN(x/n)⌋=⌊x/n⌋, where RN is - // rounding to nearest. - double quotient = uprv_floor(numerator / denominator); - // For doubles x and n, where n is an integer and ⌊x+n⌋ < 2³¹, the - // expression `(int32_t) (x + n)` evaluated with rounding to nearest - // differs from ⌊x+n⌋ if 0 < ⌈x⌉−x ≪ x+n, as `x + n` is rounded up to - // n+⌈x⌉ = ⌊x+n⌋ + 1. Rewriting it as ⌊x⌋+n makes the addition exact. - *remainder = (int32_t) (uprv_floor(numerator) - (quotient * denominator)); - return (int32_t) quotient; -} - -double ClockMath::floorDivide(double dividend, double divisor, - double* remainder) { - // Only designed to work for positive divisors - U_ASSERT(divisor > 0); - double quotient = floorDivide(dividend, divisor); - *remainder = dividend - (quotient * divisor); - // N.B. For certain large dividends, on certain platforms, there - // is a bug such that the quotient is off by one. If you doubt - // this to be true, set a breakpoint below and run cintltst. - if (*remainder < 0 || *remainder >= divisor) { - // E.g. 6.7317038241449352e+022 / 86400000.0 is wrong on my - // machine (too high by one). 4.1792057231752762e+024 / - // 86400000.0 is wrong the other way (too low). - double q = quotient; - quotient += (*remainder < 0) ? -1 : +1; - if (q == quotient) { - // For quotients > ~2^53, we won't be able to add or - // subtract one, since the LSB of the mantissa will be > - // 2^0; that is, the exponent (base 2) will be larger than - // the length, in bits, of the mantissa. In that case, we - // can't give a correct answer, so we set the remainder to - // zero. This has the desired effect of making extreme - // values give back an approximate answer rather than - // crashing. For example, UDate values above a ~10^25 - // might all have a time of midnight. - *remainder = 0; - } else { - *remainder = dividend - (quotient * divisor); - } - } - U_ASSERT(0 <= *remainder && *remainder < divisor); - return quotient; -} - -const int32_t JULIAN_1_CE = 1721426; // January 1, 1 CE Gregorian -const int32_t JULIAN_1970_CE = 2440588; // January 1, 1970 CE Gregorian - -const int16_t Grego::DAYS_BEFORE[24] = - {0,31,59,90,120,151,181,212,243,273,304,334, - 0,31,60,91,121,152,182,213,244,274,305,335}; - -const int8_t Grego::MONTH_LENGTH[24] = - {31,28,31,30,31,30,31,31,30,31,30,31, - 31,29,31,30,31,30,31,31,30,31,30,31}; - -double Grego::fieldsToDay(int32_t year, int32_t month, int32_t dom) { - - int32_t y = year - 1; - - double julian = 365 * y + ClockMath::floorDivide(y, 4) + (JULIAN_1_CE - 3) + // Julian cal - ClockMath::floorDivide(y, 400) - ClockMath::floorDivide(y, 100) + 2 + // => Gregorian cal - DAYS_BEFORE[month + (isLeapYear(year) ? 12 : 0)] + dom; // => month/dom - - return julian - JULIAN_1970_CE; // JD => epoch day -} - -void Grego::dayToFields(double day, int32_t& year, int32_t& month, - int32_t& dom, int32_t& dow, int32_t& doy) { - - // Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar) - day += JULIAN_1970_CE - JULIAN_1_CE; - - // Convert from the day number to the multiple radix - // representation. We use 400-year, 100-year, and 4-year cycles. - // For example, the 4-year cycle has 4 years + 1 leap day; giving - // 1461 == 365*4 + 1 days. - int32_t n400 = ClockMath::floorDivide(day, 146097, &doy); // 400-year cycle length - int32_t n100 = ClockMath::floorDivide(doy, 36524, &doy); // 100-year cycle length - int32_t n4 = ClockMath::floorDivide(doy, 1461, &doy); // 4-year cycle length - int32_t n1 = ClockMath::floorDivide(doy, 365, &doy); - year = 400*n400 + 100*n100 + 4*n4 + n1; - if (n100 == 4 || n1 == 4) { - doy = 365; // Dec 31 at end of 4- or 400-year cycle - } else { - ++year; - } - - UBool isLeap = isLeapYear(year); - - // Gregorian day zero is a Monday. - dow = (int32_t) uprv_fmod(day + 1, 7); - dow += (dow < 0) ? (UCAL_SUNDAY + 7) : UCAL_SUNDAY; - - // Common Julian/Gregorian calculation - int32_t correction = 0; - int32_t march1 = isLeap ? 60 : 59; // zero-based DOY for March 1 - if (doy >= march1) { - correction = isLeap ? 1 : 2; - } - month = (12 * (doy + correction) + 6) / 367; // zero-based month - dom = doy - DAYS_BEFORE[month + (isLeap ? 12 : 0)] + 1; // one-based DOM - doy++; // one-based doy -} - -void Grego::timeToFields(UDate time, int32_t& year, int32_t& month, - int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid) { - double millisInDay; - double day = ClockMath::floorDivide((double)time, (double)U_MILLIS_PER_DAY, &millisInDay); - mid = (int32_t)millisInDay; - dayToFields(day, year, month, dom, dow, doy); -} - -int32_t Grego::dayOfWeek(double day) { - int32_t dow; - ClockMath::floorDivide(day + int{UCAL_THURSDAY}, 7, &dow); - return (dow == 0) ? UCAL_SATURDAY : dow; -} - -int32_t Grego::dayOfWeekInMonth(int32_t year, int32_t month, int32_t dom) { - int32_t weekInMonth = (dom + 6)/7; - if (weekInMonth == 4) { - if (dom + 7 > monthLength(year, month)) { - weekInMonth = -1; - } - } else if (weekInMonth == 5) { - weekInMonth = -1; - } - return weekInMonth; -} - -U_NAMESPACE_END - -#endif -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (c) 2003-2008, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + * Author: Alan Liu + * Created: September 2 2003 + * Since: ICU 2.8 + ********************************************************************** + */ + +#include "gregoimp.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/ucal.h" +#include "uresimp.h" +#include "cstring.h" +#include "uassert.h" + +U_NAMESPACE_BEGIN + +int32_t ClockMath::floorDivide(int32_t numerator, int32_t denominator) { + return (numerator >= 0) ? + numerator / denominator : ((numerator + 1) / denominator) - 1; +} + +int64_t ClockMath::floorDivide(int64_t numerator, int64_t denominator) { + return (numerator >= 0) ? + numerator / denominator : ((numerator + 1) / denominator) - 1; +} + +int32_t ClockMath::floorDivide(double numerator, int32_t denominator, + int32_t* remainder) { + // For an integer n and representable ⌊x/n⌋, ⌊RN(x/n)⌋=⌊x/n⌋, where RN is + // rounding to nearest. + double quotient = uprv_floor(numerator / denominator); + if (remainder != nullptr) { + // For doubles x and n, where n is an integer and ⌊x+n⌋ < 2³¹, the + // expression `(int32_t) (x + n)` evaluated with rounding to nearest + // differs from ⌊x+n⌋ if 0 < ⌈x⌉−x ≪ x+n, as `x + n` is rounded up to + // n+⌈x⌉ = ⌊x+n⌋ + 1. Rewriting it as ⌊x⌋+n makes the addition exact. + *remainder = (int32_t) (uprv_floor(numerator) - (quotient * denominator)); + } + return (int32_t) quotient; +} + +double ClockMath::floorDivide(double dividend, double divisor, + double* remainder) { + // Only designed to work for positive divisors + U_ASSERT(divisor > 0); + double quotient = floorDivide(dividend, divisor); + double r = dividend - (quotient * divisor); + // N.B. For certain large dividends, on certain platforms, there + // is a bug such that the quotient is off by one. If you doubt + // this to be true, set a breakpoint below and run cintltst. + if (r < 0 || r >= divisor) { + // E.g. 6.7317038241449352e+022 / 86400000.0 is wrong on my + // machine (too high by one). 4.1792057231752762e+024 / + // 86400000.0 is wrong the other way (too low). + double q = quotient; + quotient += (r < 0) ? -1 : +1; + if (q == quotient) { + // For quotients > ~2^53, we won't be able to add or + // subtract one, since the LSB of the mantissa will be > + // 2^0; that is, the exponent (base 2) will be larger than + // the length, in bits, of the mantissa. In that case, we + // can't give a correct answer, so we set the remainder to + // zero. This has the desired effect of making extreme + // values give back an approximate answer rather than + // crashing. For example, UDate values above a ~10^25 + // might all have a time of midnight. + r = 0; + } else { + r = dividend - (quotient * divisor); + } + } + U_ASSERT(0 <= r && r < divisor); + if (remainder != nullptr) { + *remainder = r; + } + return quotient; +} + +const int32_t JULIAN_1_CE = 1721426; // January 1, 1 CE Gregorian +const int32_t JULIAN_1970_CE = 2440588; // January 1, 1970 CE Gregorian + +const int16_t Grego::DAYS_BEFORE[24] = + {0,31,59,90,120,151,181,212,243,273,304,334, + 0,31,60,91,121,152,182,213,244,274,305,335}; + +const int8_t Grego::MONTH_LENGTH[24] = + {31,28,31,30,31,30,31,31,30,31,30,31, + 31,29,31,30,31,30,31,31,30,31,30,31}; + +double Grego::fieldsToDay(int32_t year, int32_t month, int32_t dom) { + + int32_t y = year - 1; + + double julian = 365 * y + ClockMath::floorDivide(y, 4) + (JULIAN_1_CE - 3) + // Julian cal + ClockMath::floorDivide(y, 400) - ClockMath::floorDivide(y, 100) + 2 + // => Gregorian cal + DAYS_BEFORE[month + (isLeapYear(year) ? 12 : 0)] + dom; // => month/dom + + return julian - JULIAN_1970_CE; // JD => epoch day +} + +void Grego::dayToFields(double day, int32_t& year, int32_t& month, + int32_t& dom, int32_t& dow, int32_t& doy) { + + // Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar) + day += JULIAN_1970_CE - JULIAN_1_CE; + + // Convert from the day number to the multiple radix + // representation. We use 400-year, 100-year, and 4-year cycles. + // For example, the 4-year cycle has 4 years + 1 leap day; giving + // 1461 == 365*4 + 1 days. + int32_t n400 = ClockMath::floorDivide(day, 146097, &doy); // 400-year cycle length + int32_t n100 = ClockMath::floorDivide(doy, 36524, &doy); // 100-year cycle length + int32_t n4 = ClockMath::floorDivide(doy, 1461, &doy); // 4-year cycle length + int32_t n1 = ClockMath::floorDivide(doy, 365, &doy); + year = 400*n400 + 100*n100 + 4*n4 + n1; + if (n100 == 4 || n1 == 4) { + doy = 365; // Dec 31 at end of 4- or 400-year cycle + } else { + ++year; + } + + UBool isLeap = isLeapYear(year); + + // Gregorian day zero is a Monday. + dow = (int32_t) uprv_fmod(day + 1, 7); + dow += (dow < 0) ? (UCAL_SUNDAY + 7) : UCAL_SUNDAY; + + // Common Julian/Gregorian calculation + int32_t correction = 0; + int32_t march1 = isLeap ? 60 : 59; // zero-based DOY for March 1 + if (doy >= march1) { + correction = isLeap ? 1 : 2; + } + month = (12 * (doy + correction) + 6) / 367; // zero-based month + dom = doy - DAYS_BEFORE[month + (isLeap ? 12 : 0)] + 1; // one-based DOM + doy++; // one-based doy +} + +void Grego::timeToFields(UDate time, int32_t& year, int32_t& month, + int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid) { + double millisInDay; + double day = ClockMath::floorDivide((double)time, (double)U_MILLIS_PER_DAY, &millisInDay); + mid = (int32_t)millisInDay; + dayToFields(day, year, month, dom, dow, doy); +} + +int32_t Grego::dayOfWeek(double day) { + int32_t dow; + ClockMath::floorDivide(day + int{UCAL_THURSDAY}, 7, &dow); + return (dow == 0) ? UCAL_SATURDAY : dow; +} + +int32_t Grego::dayOfWeekInMonth(int32_t year, int32_t month, int32_t dom) { + int32_t weekInMonth = (dom + 6)/7; + if (weekInMonth == 4) { + if (dom + 7 > monthLength(year, month)) { + weekInMonth = -1; + } + } else if (weekInMonth == 5) { + weekInMonth = -1; + } + return weekInMonth; +} + +U_NAMESPACE_END + +#endif +//eof diff --git a/deps/icu-small/source/i18n/gregoimp.h b/deps/icu-small/source/i18n/gregoimp.h index d65d6a4f88e928..fc0d89c92456ac 100644 --- a/deps/icu-small/source/i18n/gregoimp.h +++ b/deps/icu-small/source/i18n/gregoimp.h @@ -1,312 +1,312 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (c) 2003-2008, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - * Author: Alan Liu - * Created: September 2 2003 - * Since: ICU 2.8 - ********************************************************************** - */ - -#ifndef GREGOIMP_H -#define GREGOIMP_H -#include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING - -#include "unicode/ures.h" -#include "unicode/locid.h" -#include "putilimp.h" - -U_NAMESPACE_BEGIN - -/** - * A utility class providing mathematical functions used by time zone - * and calendar code. Do not instantiate. Formerly just named 'Math'. - * @internal - */ -class ClockMath { - public: - /** - * Divide two integers, returning the floor of the quotient. - * Unlike the built-in division, this is mathematically - * well-behaved. E.g., -1/4 => 0 but - * floorDivide(-1,4) => -1. - * @param numerator the numerator - * @param denominator a divisor which must be != 0 - * @return the floor of the quotient - */ - static int32_t floorDivide(int32_t numerator, int32_t denominator); - - /** - * Divide two integers, returning the floor of the quotient. - * Unlike the built-in division, this is mathematically - * well-behaved. E.g., -1/4 => 0 but - * floorDivide(-1,4) => -1. - * @param numerator the numerator - * @param denominator a divisor which must be != 0 - * @return the floor of the quotient - */ - static int64_t floorDivide(int64_t numerator, int64_t denominator); - - /** - * Divide two numbers, returning the floor of the quotient. - * Unlike the built-in division, this is mathematically - * well-behaved. E.g., -1/4 => 0 but - * floorDivide(-1,4) => -1. - * @param numerator the numerator - * @param denominator a divisor which must be != 0 - * @return the floor of the quotient - */ - static inline double floorDivide(double numerator, double denominator); - - /** - * Divide two numbers, returning the floor of the quotient and - * the modulus remainder. Unlike the built-in division, this is - * mathematically well-behaved. E.g., -1/4 => 0 and - * -1%4 => -1, but floorDivide(-1,4) => - * -1 with remainder => 3. NOTE: If numerator is - * too large, the returned quotient may overflow. - * @param numerator the numerator - * @param denominator a divisor which must be != 0 - * @param remainder output parameter to receive the - * remainder. Unlike numerator % denominator, this - * will always be non-negative, in the half-open range [0, - * |denominator|). - * @return the floor of the quotient - */ - static int32_t floorDivide(double numerator, int32_t denominator, - int32_t* remainder); - - /** - * For a positive divisor, return the quotient and remainder - * such that dividend = quotient*divisor + remainder and - * 0 <= remainder < divisor. - * - * Works around edge-case bugs. Handles pathological input - * (dividend >> divisor) reasonably. - * - * Calling with a divisor <= 0 is disallowed. - */ - static double floorDivide(double dividend, double divisor, - double* remainder); -}; - -// Useful millisecond constants -#define kOneDay (1.0 * U_MILLIS_PER_DAY) // 86,400,000 -#define kOneHour (60*60*1000) -#define kOneMinute 60000 -#define kOneSecond 1000 -#define kOneMillisecond 1 -#define kOneWeek (7.0 * kOneDay) // 604,800,000 - -// Epoch constants -#define kJan1_1JulianDay 1721426 // January 1, year 1 (Gregorian) - -#define kEpochStartAsJulianDay 2440588 // January 1, 1970 (Gregorian) - -#define kEpochYear 1970 - - -#define kEarliestViableMillis -185331720384000000.0 // minimum representable by julian day -1e17 - -#define kLatestViableMillis 185753453990400000.0 // max representable by julian day +1e17 - -/** - * The minimum supported Julian day. This value is equivalent to - * MIN_MILLIS. - */ -#define MIN_JULIAN (-0x7F000000) - -/** - * The minimum supported epoch milliseconds. This value is equivalent - * to MIN_JULIAN. - */ -#define MIN_MILLIS ((MIN_JULIAN - kEpochStartAsJulianDay) * kOneDay) - -/** - * The maximum supported Julian day. This value is equivalent to - * MAX_MILLIS. - */ -#define MAX_JULIAN (+0x7F000000) - -/** - * The maximum supported epoch milliseconds. This value is equivalent - * to MAX_JULIAN. - */ -#define MAX_MILLIS ((MAX_JULIAN - kEpochStartAsJulianDay) * kOneDay) - -/** - * A utility class providing proleptic Gregorian calendar functions - * used by time zone and calendar code. Do not instantiate. - * - * Note: Unlike GregorianCalendar, all computations performed by this - * class occur in the pure proleptic GregorianCalendar. - */ -class Grego { - public: - /** - * Return true if the given year is a leap year. - * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. - * @return true if the year is a leap year - */ - static inline UBool isLeapYear(int32_t year); - - /** - * Return the number of days in the given month. - * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. - * @param month 0-based month, with 0==Jan - * @return the number of days in the given month - */ - static inline int8_t monthLength(int32_t year, int32_t month); - - /** - * Return the length of a previous month of the Gregorian calendar. - * @param y the extended year - * @param m the 0-based month number - * @return the number of days in the month previous to the given month - */ - static inline int8_t previousMonthLength(int y, int m); - - /** - * Convert a year, month, and day-of-month, given in the proleptic - * Gregorian calendar, to 1970 epoch days. - * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. - * @param month 0-based month, with 0==Jan - * @param dom 1-based day of month - * @return the day number, with day 0 == Jan 1 1970 - */ - static double fieldsToDay(int32_t year, int32_t month, int32_t dom); - - /** - * Convert a 1970-epoch day number to proleptic Gregorian year, - * month, day-of-month, and day-of-week. - * @param day 1970-epoch day (integral value) - * @param year output parameter to receive year - * @param month output parameter to receive month (0-based, 0==Jan) - * @param dom output parameter to receive day-of-month (1-based) - * @param dow output parameter to receive day-of-week (1-based, 1==Sun) - * @param doy output parameter to receive day-of-year (1-based) - */ - static void dayToFields(double day, int32_t& year, int32_t& month, - int32_t& dom, int32_t& dow, int32_t& doy); - - /** - * Convert a 1970-epoch day number to proleptic Gregorian year, - * month, day-of-month, and day-of-week. - * @param day 1970-epoch day (integral value) - * @param year output parameter to receive year - * @param month output parameter to receive month (0-based, 0==Jan) - * @param dom output parameter to receive day-of-month (1-based) - * @param dow output parameter to receive day-of-week (1-based, 1==Sun) - */ - static inline void dayToFields(double day, int32_t& year, int32_t& month, - int32_t& dom, int32_t& dow); - - /** - * Convert a 1970-epoch milliseconds to proleptic Gregorian year, - * month, day-of-month, and day-of-week, day of year and millis-in-day. - * @param time 1970-epoch milliseconds - * @param year output parameter to receive year - * @param month output parameter to receive month (0-based, 0==Jan) - * @param dom output parameter to receive day-of-month (1-based) - * @param dow output parameter to receive day-of-week (1-based, 1==Sun) - * @param doy output parameter to receive day-of-year (1-based) - * @param mid output parameter to receive millis-in-day - */ - static void timeToFields(UDate time, int32_t& year, int32_t& month, - int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid); - - /** - * Return the day of week on the 1970-epoch day - * @param day the 1970-epoch day (integral value) - * @return the day of week - */ - static int32_t dayOfWeek(double day); - - /** - * Returns the ordinal number for the specified day of week within the month. - * The valid return value is 1, 2, 3, 4 or -1. - * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. - * @param month 0-based month, with 0==Jan - * @param dom 1-based day of month - * @return The ordinal number for the specified day of week within the month - */ - static int32_t dayOfWeekInMonth(int32_t year, int32_t month, int32_t dom); - - /** - * Converts Julian day to time as milliseconds. - * @param julian the given Julian day number. - * @return time as milliseconds. - * @internal - */ - static inline double julianDayToMillis(int32_t julian); - - /** - * Converts time as milliseconds to Julian day. - * @param millis the given milliseconds. - * @return the Julian day number. - * @internal - */ - static inline int32_t millisToJulianDay(double millis); - - /** - * Calculates the Gregorian day shift value for an extended year. - * @param eyear Extended year - * @returns number of days to ADD to Julian in order to convert from J->G - */ - static inline int32_t gregorianShift(int32_t eyear); - - private: - static const int16_t DAYS_BEFORE[24]; - static const int8_t MONTH_LENGTH[24]; -}; - -inline double ClockMath::floorDivide(double numerator, double denominator) { - return uprv_floor(numerator / denominator); -} - -inline UBool Grego::isLeapYear(int32_t year) { - // year&0x3 == year%4 - return ((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0)); -} - -inline int8_t -Grego::monthLength(int32_t year, int32_t month) { - return MONTH_LENGTH[month + (isLeapYear(year) ? 12 : 0)]; -} - -inline int8_t -Grego::previousMonthLength(int y, int m) { - return (m > 0) ? monthLength(y, m-1) : 31; -} - -inline void Grego::dayToFields(double day, int32_t& year, int32_t& month, - int32_t& dom, int32_t& dow) { - int32_t doy_unused; - dayToFields(day,year,month,dom,dow,doy_unused); -} - -inline double Grego::julianDayToMillis(int32_t julian) -{ - return (julian - kEpochStartAsJulianDay) * kOneDay; -} - -inline int32_t Grego::millisToJulianDay(double millis) { - return (int32_t) (kEpochStartAsJulianDay + ClockMath::floorDivide(millis, (double)kOneDay)); -} - -inline int32_t Grego::gregorianShift(int32_t eyear) { - int64_t y = (int64_t)eyear-1; - int32_t gregShift = static_cast(ClockMath::floorDivide(y, (int64_t)400) - ClockMath::floorDivide(y, (int64_t)100) + 2); - return gregShift; -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_FORMATTING -#endif // GREGOIMP_H - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (c) 2003-2008, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + * Author: Alan Liu + * Created: September 2 2003 + * Since: ICU 2.8 + ********************************************************************** + */ + +#ifndef GREGOIMP_H +#define GREGOIMP_H +#include "unicode/utypes.h" +#if !UCONFIG_NO_FORMATTING + +#include "unicode/ures.h" +#include "unicode/locid.h" +#include "putilimp.h" + +U_NAMESPACE_BEGIN + +/** + * A utility class providing mathematical functions used by time zone + * and calendar code. Do not instantiate. Formerly just named 'Math'. + * @internal + */ +class ClockMath { + public: + /** + * Divide two integers, returning the floor of the quotient. + * Unlike the built-in division, this is mathematically + * well-behaved. E.g., -1/4 => 0 but + * floorDivide(-1,4) => -1. + * @param numerator the numerator + * @param denominator a divisor which must be != 0 + * @return the floor of the quotient + */ + static int32_t floorDivide(int32_t numerator, int32_t denominator); + + /** + * Divide two integers, returning the floor of the quotient. + * Unlike the built-in division, this is mathematically + * well-behaved. E.g., -1/4 => 0 but + * floorDivide(-1,4) => -1. + * @param numerator the numerator + * @param denominator a divisor which must be != 0 + * @return the floor of the quotient + */ + static int64_t floorDivide(int64_t numerator, int64_t denominator); + + /** + * Divide two numbers, returning the floor of the quotient. + * Unlike the built-in division, this is mathematically + * well-behaved. E.g., -1/4 => 0 but + * floorDivide(-1,4) => -1. + * @param numerator the numerator + * @param denominator a divisor which must be != 0 + * @return the floor of the quotient + */ + static inline double floorDivide(double numerator, double denominator); + + /** + * Divide two numbers, returning the floor of the quotient and + * the modulus remainder. Unlike the built-in division, this is + * mathematically well-behaved. E.g., -1/4 => 0 and + * -1%4 => -1, but floorDivide(-1,4) => + * -1 with remainder => 3. NOTE: If numerator is + * too large, the returned quotient may overflow. + * @param numerator the numerator + * @param denominator a divisor which must be != 0 + * @param remainder output parameter to receive the + * remainder. Unlike numerator % denominator, this + * will always be non-negative, in the half-open range [0, + * |denominator|). + * @return the floor of the quotient + */ + static int32_t floorDivide(double numerator, int32_t denominator, + int32_t* remainder); + + /** + * For a positive divisor, return the quotient and remainder + * such that dividend = quotient*divisor + remainder and + * 0 <= remainder < divisor. + * + * Works around edge-case bugs. Handles pathological input + * (dividend >> divisor) reasonably. + * + * Calling with a divisor <= 0 is disallowed. + */ + static double floorDivide(double dividend, double divisor, + double* remainder); +}; + +// Useful millisecond constants +#define kOneDay (1.0 * U_MILLIS_PER_DAY) // 86,400,000 +#define kOneHour (60*60*1000) +#define kOneMinute 60000 +#define kOneSecond 1000 +#define kOneMillisecond 1 +#define kOneWeek (7.0 * kOneDay) // 604,800,000 + +// Epoch constants +#define kJan1_1JulianDay 1721426 // January 1, year 1 (Gregorian) + +#define kEpochStartAsJulianDay 2440588 // January 1, 1970 (Gregorian) + +#define kEpochYear 1970 + + +#define kEarliestViableMillis -185331720384000000.0 // minimum representable by julian day -1e17 + +#define kLatestViableMillis 185753453990400000.0 // max representable by julian day +1e17 + +/** + * The minimum supported Julian day. This value is equivalent to + * MIN_MILLIS. + */ +#define MIN_JULIAN (-0x7F000000) + +/** + * The minimum supported epoch milliseconds. This value is equivalent + * to MIN_JULIAN. + */ +#define MIN_MILLIS ((MIN_JULIAN - kEpochStartAsJulianDay) * kOneDay) + +/** + * The maximum supported Julian day. This value is equivalent to + * MAX_MILLIS. + */ +#define MAX_JULIAN (+0x7F000000) + +/** + * The maximum supported epoch milliseconds. This value is equivalent + * to MAX_JULIAN. + */ +#define MAX_MILLIS ((MAX_JULIAN - kEpochStartAsJulianDay) * kOneDay) + +/** + * A utility class providing proleptic Gregorian calendar functions + * used by time zone and calendar code. Do not instantiate. + * + * Note: Unlike GregorianCalendar, all computations performed by this + * class occur in the pure proleptic GregorianCalendar. + */ +class Grego { + public: + /** + * Return true if the given year is a leap year. + * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. + * @return true if the year is a leap year + */ + static inline UBool isLeapYear(int32_t year); + + /** + * Return the number of days in the given month. + * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. + * @param month 0-based month, with 0==Jan + * @return the number of days in the given month + */ + static inline int8_t monthLength(int32_t year, int32_t month); + + /** + * Return the length of a previous month of the Gregorian calendar. + * @param y the extended year + * @param m the 0-based month number + * @return the number of days in the month previous to the given month + */ + static inline int8_t previousMonthLength(int y, int m); + + /** + * Convert a year, month, and day-of-month, given in the proleptic + * Gregorian calendar, to 1970 epoch days. + * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. + * @param month 0-based month, with 0==Jan + * @param dom 1-based day of month + * @return the day number, with day 0 == Jan 1 1970 + */ + static double fieldsToDay(int32_t year, int32_t month, int32_t dom); + + /** + * Convert a 1970-epoch day number to proleptic Gregorian year, + * month, day-of-month, and day-of-week. + * @param day 1970-epoch day (integral value) + * @param year output parameter to receive year + * @param month output parameter to receive month (0-based, 0==Jan) + * @param dom output parameter to receive day-of-month (1-based) + * @param dow output parameter to receive day-of-week (1-based, 1==Sun) + * @param doy output parameter to receive day-of-year (1-based) + */ + static void dayToFields(double day, int32_t& year, int32_t& month, + int32_t& dom, int32_t& dow, int32_t& doy); + + /** + * Convert a 1970-epoch day number to proleptic Gregorian year, + * month, day-of-month, and day-of-week. + * @param day 1970-epoch day (integral value) + * @param year output parameter to receive year + * @param month output parameter to receive month (0-based, 0==Jan) + * @param dom output parameter to receive day-of-month (1-based) + * @param dow output parameter to receive day-of-week (1-based, 1==Sun) + */ + static inline void dayToFields(double day, int32_t& year, int32_t& month, + int32_t& dom, int32_t& dow); + + /** + * Convert a 1970-epoch milliseconds to proleptic Gregorian year, + * month, day-of-month, and day-of-week, day of year and millis-in-day. + * @param time 1970-epoch milliseconds + * @param year output parameter to receive year + * @param month output parameter to receive month (0-based, 0==Jan) + * @param dom output parameter to receive day-of-month (1-based) + * @param dow output parameter to receive day-of-week (1-based, 1==Sun) + * @param doy output parameter to receive day-of-year (1-based) + * @param mid output parameter to receive millis-in-day + */ + static void timeToFields(UDate time, int32_t& year, int32_t& month, + int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid); + + /** + * Return the day of week on the 1970-epoch day + * @param day the 1970-epoch day (integral value) + * @return the day of week + */ + static int32_t dayOfWeek(double day); + + /** + * Returns the ordinal number for the specified day of week within the month. + * The valid return value is 1, 2, 3, 4 or -1. + * @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc. + * @param month 0-based month, with 0==Jan + * @param dom 1-based day of month + * @return The ordinal number for the specified day of week within the month + */ + static int32_t dayOfWeekInMonth(int32_t year, int32_t month, int32_t dom); + + /** + * Converts Julian day to time as milliseconds. + * @param julian the given Julian day number. + * @return time as milliseconds. + * @internal + */ + static inline double julianDayToMillis(int32_t julian); + + /** + * Converts time as milliseconds to Julian day. + * @param millis the given milliseconds. + * @return the Julian day number. + * @internal + */ + static inline int32_t millisToJulianDay(double millis); + + /** + * Calculates the Gregorian day shift value for an extended year. + * @param eyear Extended year + * @returns number of days to ADD to Julian in order to convert from J->G + */ + static inline int32_t gregorianShift(int32_t eyear); + + private: + static const int16_t DAYS_BEFORE[24]; + static const int8_t MONTH_LENGTH[24]; +}; + +inline double ClockMath::floorDivide(double numerator, double denominator) { + return uprv_floor(numerator / denominator); +} + +inline UBool Grego::isLeapYear(int32_t year) { + // year&0x3 == year%4 + return ((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0)); +} + +inline int8_t +Grego::monthLength(int32_t year, int32_t month) { + return MONTH_LENGTH[month + (isLeapYear(year) ? 12 : 0)]; +} + +inline int8_t +Grego::previousMonthLength(int y, int m) { + return (m > 0) ? monthLength(y, m-1) : 31; +} + +inline void Grego::dayToFields(double day, int32_t& year, int32_t& month, + int32_t& dom, int32_t& dow) { + int32_t doy_unused; + dayToFields(day,year,month,dom,dow,doy_unused); +} + +inline double Grego::julianDayToMillis(int32_t julian) +{ + return (julian - kEpochStartAsJulianDay) * kOneDay; +} + +inline int32_t Grego::millisToJulianDay(double millis) { + return (int32_t) (kEpochStartAsJulianDay + ClockMath::floorDivide(millis, (double)kOneDay)); +} + +inline int32_t Grego::gregorianShift(int32_t eyear) { + int64_t y = (int64_t)eyear-1; + int32_t gregShift = static_cast(ClockMath::floorDivide(y, (int64_t)400) - ClockMath::floorDivide(y, (int64_t)100) + 2); + return gregShift; +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING +#endif // GREGOIMP_H + +//eof diff --git a/deps/icu-small/source/i18n/hebrwcal.cpp b/deps/icu-small/source/i18n/hebrwcal.cpp index b3e6bcb65c3342..0c9ec9f62e4110 100644 --- a/deps/icu-small/source/i18n/hebrwcal.cpp +++ b/deps/icu-small/source/i18n/hebrwcal.cpp @@ -1,733 +1,790 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 2003-2016, International Business Machines Corporation -* and others. All Rights Reserved. -****************************************************************************** -* -* File HEBRWCAL.CPP -* -* Modification History: -* -* Date Name Description -* 12/03/2003 srl ported from java HebrewCalendar -***************************************************************************** -*/ - -#include "hebrwcal.h" - -#if !UCONFIG_NO_FORMATTING - -#include "cmemory.h" -#include "umutex.h" -#include -#include "gregoimp.h" // Math -#include "astro.h" // CalendarAstronomer -#include "uhash.h" -#include "ucln_in.h" - -// Hebrew Calendar implementation - -/** -* The absolute date, in milliseconds since 1/1/1970 AD, Gregorian, -* of the start of the Hebrew calendar. In order to keep this calendar's -* time of day in sync with that of the Gregorian calendar, we use -* midnight, rather than sunset the day before. -*/ -//static const double EPOCH_MILLIS = -180799862400000.; // 1/1/1 HY - -static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { - // Minimum Greatest Least Maximum - // Minimum Maximum - { 0, 0, 0, 0}, // ERA - { -5000000, -5000000, 5000000, 5000000}, // YEAR - { 0, 0, 12, 12}, // MONTH - { 1, 1, 51, 56}, // WEEK_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH - { 1, 1, 29, 30}, // DAY_OF_MONTH - { 1, 1, 353, 385}, // DAY_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK - { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET - { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL - { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH -}; - -/** -* The lengths of the Hebrew months. This is complicated, because there -* are three different types of years, or six if you count leap years. -* Due to the rules for postponing the start of the year to avoid having -* certain holidays fall on the sabbath, the year can end up being three -* different lengths, called "deficient", "normal", and "complete". -*/ -static const int8_t MONTH_LENGTH[][3] = { - // Deficient Normal Complete - { 30, 30, 30 }, //Tishri - { 29, 29, 30 }, //Heshvan - { 29, 30, 30 }, //Kislev - { 29, 29, 29 }, //Tevet - { 30, 30, 30 }, //Shevat - { 30, 30, 30 }, //Adar I (leap years only) - { 29, 29, 29 }, //Adar - { 30, 30, 30 }, //Nisan - { 29, 29, 29 }, //Iyar - { 30, 30, 30 }, //Sivan - { 29, 29, 29 }, //Tammuz - { 30, 30, 30 }, //Av - { 29, 29, 29 }, //Elul -}; - -/** -* The cumulative # of days to the end of each month in a non-leap year -* Although this can be calculated from the MONTH_LENGTH table, -* keeping it around separately makes some calculations a lot faster -*/ - -static const int16_t MONTH_START[][3] = { - // Deficient Normal Complete - { 0, 0, 0 }, // (placeholder) - { 30, 30, 30 }, // Tishri - { 59, 59, 60 }, // Heshvan - { 88, 89, 90 }, // Kislev - { 117, 118, 119 }, // Tevet - { 147, 148, 149 }, // Shevat - { 147, 148, 149 }, // (Adar I placeholder) - { 176, 177, 178 }, // Adar - { 206, 207, 208 }, // Nisan - { 235, 236, 237 }, // Iyar - { 265, 266, 267 }, // Sivan - { 294, 295, 296 }, // Tammuz - { 324, 325, 326 }, // Av - { 353, 354, 355 }, // Elul -}; - -/** -* The cumulative # of days to the end of each month in a leap year -*/ -static const int16_t LEAP_MONTH_START[][3] = { - // Deficient Normal Complete - { 0, 0, 0 }, // (placeholder) - { 30, 30, 30 }, // Tishri - { 59, 59, 60 }, // Heshvan - { 88, 89, 90 }, // Kislev - { 117, 118, 119 }, // Tevet - { 147, 148, 149 }, // Shevat - { 177, 178, 179 }, // Adar I - { 206, 207, 208 }, // Adar II - { 236, 237, 238 }, // Nisan - { 265, 266, 267 }, // Iyar - { 295, 296, 297 }, // Sivan - { 324, 325, 326 }, // Tammuz - { 354, 355, 356 }, // Av - { 383, 384, 385 }, // Elul -}; - -static icu::CalendarCache *gCache = NULL; - -U_CDECL_BEGIN -static UBool calendar_hebrew_cleanup(void) { - delete gCache; - gCache = NULL; - return true; -} -U_CDECL_END - -U_NAMESPACE_BEGIN -//------------------------------------------------------------------------- -// Constructors... -//------------------------------------------------------------------------- - -/** -* Constructs a default HebrewCalendar using the current time -* in the default time zone with the default locale. -* @internal -*/ -HebrewCalendar::HebrewCalendar(const Locale& aLocale, UErrorCode& success) -: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success) - -{ - setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. -} - - -HebrewCalendar::~HebrewCalendar() { -} - -const char *HebrewCalendar::getType() const { - return "hebrew"; -} - -HebrewCalendar* HebrewCalendar::clone() const { - return new HebrewCalendar(*this); -} - -HebrewCalendar::HebrewCalendar(const HebrewCalendar& other) : Calendar(other) { -} - - -//------------------------------------------------------------------------- -// Rolling and adding functions overridden from Calendar -// -// These methods call through to the default implementation in IBMCalendar -// for most of the fields and only handle the unusual ones themselves. -//------------------------------------------------------------------------- - -/** -* Add a signed amount to a specified field, using this calendar's rules. -* For example, to add three days to the current date, you can call -* add(Calendar.DATE, 3). -*

-* When adding to certain fields, the values of other fields may conflict and -* need to be changed. For example, when adding one to the {@link #MONTH MONTH} field -* for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field -* must be adjusted so that the result is "29 Elul 5758" rather than the invalid -* "30 Elul 5758". -*

-* This method is able to add to -* all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET}, -* and {@link #ZONE_OFFSET ZONE_OFFSET}. -*

-* Note: You should always use {@link #roll roll} and add rather -* than attempting to perform arithmetic operations directly on the fields -* of a HebrewCalendar. Since the {@link #MONTH MONTH} field behaves -* discontinuously in non-leap years, simple arithmetic can give invalid results. -*

-* @param field the time field. -* @param amount the amount to add to the field. -* -* @exception IllegalArgumentException if the field is invalid or refers -* to a field that cannot be handled by this method. -* @internal -*/ -void HebrewCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) -{ - if(U_FAILURE(status)) { - return; - } - switch (field) { - case UCAL_MONTH: - { - // We can't just do a set(MONTH, get(MONTH) + amount). The - // reason is ADAR_1. Suppose amount is +2 and we land in - // ADAR_1 -- then we have to bump to ADAR_2 aka ADAR. But - // if amount is -2 and we land in ADAR_1, then we have to - // bump the other way -- down to SHEVAT. - Alan 11/00 - int32_t month = get(UCAL_MONTH, status); - int32_t year = get(UCAL_YEAR, status); - UBool acrossAdar1; - if (amount > 0) { - acrossAdar1 = (month < ADAR_1); // started before ADAR_1? - month += amount; - for (;;) { - if (acrossAdar1 && month>=ADAR_1 && !isLeapYear(year)) { - ++month; - } - if (month <= ELUL) { - break; - } - month -= ELUL+1; - ++year; - acrossAdar1 = true; - } - } else { - acrossAdar1 = (month > ADAR_1); // started after ADAR_1? - month += amount; - for (;;) { - if (acrossAdar1 && month<=ADAR_1 && !isLeapYear(year)) { - --month; - } - if (month >= 0) { - break; - } - month += ELUL+1; - --year; - acrossAdar1 = true; - } - } - set(UCAL_MONTH, month); - set(UCAL_YEAR, year); - pinField(UCAL_DAY_OF_MONTH, status); - break; - } - - default: - Calendar::add(field, amount, status); - break; - } -} - -/** -* @deprecated ICU 2.6 use UCalendarDateFields instead of EDateFields -*/ -void HebrewCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) -{ - add((UCalendarDateFields)field, amount, status); -} - -/** -* Rolls (up/down) a specified amount time on the given field. For -* example, to roll the current date up by three days, you can call -* roll(Calendar.DATE, 3). If the -* field is rolled past its maximum allowable value, it will "wrap" back -* to its minimum and continue rolling. -* For example, calling roll(Calendar.DATE, 10) -* on a Hebrew calendar set to "25 Av 5758" will result in the date "5 Av 5758". -*

-* When rolling certain fields, the values of other fields may conflict and -* need to be changed. For example, when rolling the {@link #MONTH MONTH} field -* upward by one for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field -* must be adjusted so that the result is "29 Elul 5758" rather than the invalid -* "30 Elul". -*

-* This method is able to roll -* all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET}, -* and {@link #ZONE_OFFSET ZONE_OFFSET}. Subclasses may, of course, add support for -* additional fields in their overrides of roll. -*

-* Note: You should always use roll and {@link #add add} rather -* than attempting to perform arithmetic operations directly on the fields -* of a HebrewCalendar. Since the {@link #MONTH MONTH} field behaves -* discontinuously in non-leap years, simple arithmetic can give invalid results. -*

-* @param field the time field. -* @param amount the amount by which the field should be rolled. -* -* @exception IllegalArgumentException if the field is invalid or refers -* to a field that cannot be handled by this method. -* @internal -*/ -void HebrewCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) -{ - if(U_FAILURE(status)) { - return; - } - switch (field) { - case UCAL_MONTH: - { - int32_t month = get(UCAL_MONTH, status); - int32_t year = get(UCAL_YEAR, status); - - UBool leapYear = isLeapYear(year); - int32_t yearLength = monthsInYear(year); - int32_t newMonth = month + (amount % yearLength); - // - // If it's not a leap year and we're rolling past the missing month - // of ADAR_1, we need to roll an extra month to make up for it. - // - if (!leapYear) { - if (amount > 0 && month < ADAR_1 && newMonth >= ADAR_1) { - newMonth++; - } else if (amount < 0 && month > ADAR_1 && newMonth <= ADAR_1) { - newMonth--; - } - } - set(UCAL_MONTH, (newMonth + 13) % 13); - pinField(UCAL_DAY_OF_MONTH, status); - return; - } - default: - Calendar::roll(field, amount, status); - } -} - -void HebrewCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { - roll((UCalendarDateFields)field, amount, status); -} - -//------------------------------------------------------------------------- -// Support methods -//------------------------------------------------------------------------- - -// Hebrew date calculations are performed in terms of days, hours, and -// "parts" (or halakim), which are 1/1080 of an hour, or 3 1/3 seconds. -static const int32_t HOUR_PARTS = 1080; -static const int32_t DAY_PARTS = 24*HOUR_PARTS; - -// An approximate value for the length of a lunar month. -// It is used to calculate the approximate year and month of a given -// absolute date. -static const int32_t MONTH_DAYS = 29; -static const int32_t MONTH_FRACT = 12*HOUR_PARTS + 793; -static const int32_t MONTH_PARTS = MONTH_DAYS*DAY_PARTS + MONTH_FRACT; - -// The time of the new moon (in parts) on 1 Tishri, year 1 (the epoch) -// counting from noon on the day before. BAHARAD is an abbreviation of -// Bet (Monday), Hey (5 hours from sunset), Resh-Daled (204). -static const int32_t BAHARAD = 11*HOUR_PARTS + 204; - -/** -* Finds the day # of the first day in the given Hebrew year. -* To do this, we want to calculate the time of the Tishri 1 new moon -* in that year. -*

-* The algorithm here is similar to ones described in a number of -* references, including: -*

-*/ -int32_t HebrewCalendar::startOfYear(int32_t year, UErrorCode &status) -{ - ucln_i18n_registerCleanup(UCLN_I18N_HEBREW_CALENDAR, calendar_hebrew_cleanup); - int32_t day = CalendarCache::get(&gCache, year, status); - - if (day == 0) { - // # of months before year - int32_t months = (int32_t)ClockMath::floorDivide((235 * (int64_t)year - 234), (int64_t)19); - - int64_t frac = (int64_t)months * MONTH_FRACT + BAHARAD; // Fractional part of day # - day = months * 29 + (int32_t)(frac / DAY_PARTS); // Whole # part of calculation - frac = frac % DAY_PARTS; // Time of day - - int32_t wd = (day % 7); // Day of week (0 == Monday) - - if (wd == 2 || wd == 4 || wd == 6) { - // If the 1st is on Sun, Wed, or Fri, postpone to the next day - day += 1; - wd = (day % 7); - } - if (wd == 1 && frac > 15*HOUR_PARTS+204 && !isLeapYear(year) ) { - // If the new moon falls after 3:11:20am (15h204p from the previous noon) - // on a Tuesday and it is not a leap year, postpone by 2 days. - // This prevents 356-day years. - day += 2; - } - else if (wd == 0 && frac > 21*HOUR_PARTS+589 && isLeapYear(year-1) ) { - // If the new moon falls after 9:32:43 1/3am (21h589p from yesterday noon) - // on a Monday and *last* year was a leap year, postpone by 1 day. - // Prevents 382-day years. - day += 1; - } - CalendarCache::put(&gCache, year, day, status); - } - return day; -} - -/** -* Find the day of the week for a given day -* -* @param day The # of days since the start of the Hebrew calendar, -* 1-based (i.e. 1/1/1 AM is day 1). -*/ -int32_t HebrewCalendar::absoluteDayToDayOfWeek(int32_t day) -{ - // We know that 1/1/1 AM is a Monday, which makes the math easy... - return (day % 7) + 1; -} - -/** -* Returns the the type of a given year. -* 0 "Deficient" year with 353 or 383 days -* 1 "Normal" year with 354 or 384 days -* 2 "Complete" year with 355 or 385 days -*/ -int32_t HebrewCalendar::yearType(int32_t year) const -{ - int32_t yearLength = handleGetYearLength(year); - - if (yearLength > 380) { - yearLength -= 30; // Subtract length of leap month. - } - - int type = 0; - - switch (yearLength) { - case 353: - type = 0; break; - case 354: - type = 1; break; - case 355: - type = 2; break; - default: - //throw new RuntimeException("Illegal year length " + yearLength + " in year " + year); - type = 1; - } - return type; -} - -/** -* Determine whether a given Hebrew year is a leap year -* -* The rule here is that if (year % 19) == 0, 3, 6, 8, 11, 14, or 17. -* The formula below performs the same test, believe it or not. -*/ -UBool HebrewCalendar::isLeapYear(int32_t year) { - //return (year * 12 + 17) % 19 >= 12; - int32_t x = (year*12 + 17) % 19; - return x >= ((x < 0) ? -7 : 12); -} - -int32_t HebrewCalendar::monthsInYear(int32_t year) { - return isLeapYear(year) ? 13 : 12; -} - -//------------------------------------------------------------------------- -// Calendar framework -//------------------------------------------------------------------------- - -/** -* @internal -*/ -int32_t HebrewCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { - return LIMITS[field][limitType]; -} - -/** -* Returns the length of the given month in the given year -* @internal -*/ -int32_t HebrewCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { - // Resolve out-of-range months. This is necessary in order to - // obtain the correct year. We correct to - // a 12- or 13-month year (add/subtract 12 or 13, depending - // on the year) but since we _always_ number from 0..12, and - // the leap year determines whether or not month 5 (Adar 1) - // is present, we allow 0..12 in any given year. - while (month < 0) { - month += monthsInYear(--extendedYear); - } - // Careful: allow 0..12 in all years - while (month > 12) { - month -= monthsInYear(extendedYear++); - } - - switch (month) { - case HESHVAN: - case KISLEV: - // These two month lengths can vary - return MONTH_LENGTH[month][yearType(extendedYear)]; - - default: - // The rest are a fixed length - return MONTH_LENGTH[month][0]; - } -} - -/** -* Returns the number of days in the given Hebrew year -* @internal -*/ -int32_t HebrewCalendar::handleGetYearLength(int32_t eyear) const { - UErrorCode status = U_ZERO_ERROR; - return startOfYear(eyear+1, status) - startOfYear(eyear, status); -} - -void HebrewCalendar::validateField(UCalendarDateFields field, UErrorCode &status) { - if (field == UCAL_MONTH && !isLeapYear(handleGetExtendedYear()) && internalGet(UCAL_MONTH) == ADAR_1) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - Calendar::validateField(field, status); -} -//------------------------------------------------------------------------- -// Functions for converting from milliseconds to field values -//------------------------------------------------------------------------- - -/** -* Subclasses may override this method to compute several fields -* specific to each calendar system. These are: -* -*
  • ERA -*
  • YEAR -*
  • MONTH -*
  • DAY_OF_MONTH -*
  • DAY_OF_YEAR -*
  • EXTENDED_YEAR
-* -* Subclasses can refer to the DAY_OF_WEEK and DOW_LOCAL fields, -* which will be set when this method is called. Subclasses can -* also call the getGregorianXxx() methods to obtain Gregorian -* calendar equivalents for the given Julian day. -* -*

In addition, subclasses should compute any subclass-specific -* fields, that is, fields from BASE_FIELD_COUNT to -* getFieldCount() - 1. -* @internal -*/ -void HebrewCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { - int32_t d = julianDay - 347997; - double m = ClockMath::floorDivide((d * (double)DAY_PARTS), (double) MONTH_PARTS); // Months (approx) - int32_t year = (int32_t)(ClockMath::floorDivide((19. * m + 234.), 235.) + 1.); // Years (approx) - int32_t ys = startOfYear(year, status); // 1st day of year - int32_t dayOfYear = (d - ys); - - // Because of the postponement rules, it's possible to guess wrong. Fix it. - while (dayOfYear < 1) { - year--; - ys = startOfYear(year, status); - dayOfYear = (d - ys); - } - - // Now figure out which month we're in, and the date within that month - int32_t type = yearType(year); - UBool isLeap = isLeapYear(year); - - int32_t month = 0; - int32_t momax = UPRV_LENGTHOF(MONTH_START); - while (month < momax && dayOfYear > ( isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type] ) ) { - month++; - } - if (month >= momax || month<=0) { - // TODO: I found dayOfYear could be out of range when - // a large value is set to julianDay. I patched startOfYear - // to reduce the chace, but it could be still reproduced either - // by startOfYear or other places. For now, we check - // the month is in valid range to avoid out of array index - // access problem here. However, we need to carefully review - // the calendar implementation to check the extreme limit of - // each calendar field and the code works well for any values - // in the valid value range. -yoshito - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - month--; - int dayOfMonth = dayOfYear - (isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type]); - - internalSet(UCAL_ERA, 0); - internalSet(UCAL_YEAR, year); - internalSet(UCAL_EXTENDED_YEAR, year); - internalSet(UCAL_MONTH, month); - internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); - internalSet(UCAL_DAY_OF_YEAR, dayOfYear); -} - -//------------------------------------------------------------------------- -// Functions for converting from field values to milliseconds -//------------------------------------------------------------------------- - -/** -* @internal -*/ -int32_t HebrewCalendar::handleGetExtendedYear() { - int32_t year; - if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { - year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 - } else { - year = internalGet(UCAL_YEAR, 1); // Default to year 1 - } - return year; -} - -/** -* Return JD of start of given month/year. -* @internal -*/ -int32_t HebrewCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/) const { - UErrorCode status = U_ZERO_ERROR; - // Resolve out-of-range months. This is necessary in order to - // obtain the correct year. We correct to - // a 12- or 13-month year (add/subtract 12 or 13, depending - // on the year) but since we _always_ number from 0..12, and - // the leap year determines whether or not month 5 (Adar 1) - // is present, we allow 0..12 in any given year. - while (month < 0) { - month += monthsInYear(--eyear); - } - // Careful: allow 0..12 in all years - while (month > 12) { - month -= monthsInYear(eyear++); - } - - int32_t day = startOfYear(eyear, status); - - if(U_FAILURE(status)) { - return 0; - } - - if (month != 0) { - if (isLeapYear(eyear)) { - day += LEAP_MONTH_START[month][yearType(eyear)]; - } else { - day += MONTH_START[month][yearType(eyear)]; - } - } - - return (int) (day + 347997); -} - -UBool -HebrewCalendar::inDaylightTime(UErrorCode& status) const -{ - // copied from GregorianCalendar - if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) - return false; - - // Force an update of the state of the Calendar. - ((HebrewCalendar*)this)->complete(status); // cast away const - - return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : false); -} - -/** - * The system maintains a static default century start date and Year. They are - * initialized the first time they are used. Once the system default century date - * and year are set, they do not change. - */ -static UDate gSystemDefaultCenturyStart = DBL_MIN; -static int32_t gSystemDefaultCenturyStartYear = -1; -static icu::UInitOnce gSystemDefaultCenturyInit {}; - -UBool HebrewCalendar::haveDefaultCentury() const -{ - return true; -} - -static void U_CALLCONV initializeSystemDefaultCentury() -{ - // initialize systemDefaultCentury and systemDefaultCenturyYear based - // on the current time. They'll be set to 80 years before - // the current time. - UErrorCode status = U_ZERO_ERROR; - HebrewCalendar calendar(Locale("@calendar=hebrew"),status); - if (U_SUCCESS(status)) { - calendar.setTime(Calendar::getNow(), status); - calendar.add(UCAL_YEAR, -80, status); - - gSystemDefaultCenturyStart = calendar.getTime(status); - gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); - } - // We have no recourse upon failure unless we want to propagate the failure - // out. -} - - -UDate HebrewCalendar::defaultCenturyStart() const { - // lazy-evaluate systemDefaultCenturyStart - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStart; -} - -int32_t HebrewCalendar::defaultCenturyStartYear() const { - // lazy-evaluate systemDefaultCenturyStartYear - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStartYear; -} - - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(HebrewCalendar) - -U_NAMESPACE_END - -#endif // UCONFIG_NO_FORMATTING - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 2003-2016, International Business Machines Corporation +* and others. All Rights Reserved. +****************************************************************************** +* +* File HEBRWCAL.CPP +* +* Modification History: +* +* Date Name Description +* 12/03/2003 srl ported from java HebrewCalendar +***************************************************************************** +*/ + +#include "hebrwcal.h" + +#if !UCONFIG_NO_FORMATTING + +#include "cmemory.h" +#include "cstring.h" +#include "umutex.h" +#include +#include "gregoimp.h" // ClockMath +#include "astro.h" // CalendarCache +#include "uhash.h" +#include "ucln_in.h" + +// Hebrew Calendar implementation + +/** +* The absolute date, in milliseconds since 1/1/1970 AD, Gregorian, +* of the start of the Hebrew calendar. In order to keep this calendar's +* time of day in sync with that of the Gregorian calendar, we use +* midnight, rather than sunset the day before. +*/ +//static const double EPOCH_MILLIS = -180799862400000.; // 1/1/1 HY + +static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { + // Minimum Greatest Least Maximum + // Minimum Maximum + { 0, 0, 0, 0}, // ERA + { -5000000, -5000000, 5000000, 5000000}, // YEAR + { 0, 0, 12, 12}, // MONTH + { 1, 1, 51, 56}, // WEEK_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH + { 1, 1, 29, 30}, // DAY_OF_MONTH + { 1, 1, 353, 385}, // DAY_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK + { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET + { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL + { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH + { 0, 0, 11, 12}, // ORDINAL_MONTH +}; + +/** +* The lengths of the Hebrew months. This is complicated, because there +* are three different types of years, or six if you count leap years. +* Due to the rules for postponing the start of the year to avoid having +* certain holidays fall on the sabbath, the year can end up being three +* different lengths, called "deficient", "normal", and "complete". +*/ +static const int8_t MONTH_LENGTH[][3] = { + // Deficient Normal Complete + { 30, 30, 30 }, //Tishri + { 29, 29, 30 }, //Heshvan + { 29, 30, 30 }, //Kislev + { 29, 29, 29 }, //Tevet + { 30, 30, 30 }, //Shevat + { 30, 30, 30 }, //Adar I (leap years only) + { 29, 29, 29 }, //Adar + { 30, 30, 30 }, //Nisan + { 29, 29, 29 }, //Iyar + { 30, 30, 30 }, //Sivan + { 29, 29, 29 }, //Tammuz + { 30, 30, 30 }, //Av + { 29, 29, 29 }, //Elul +}; + +/** +* The cumulative # of days to the end of each month in a non-leap year +* Although this can be calculated from the MONTH_LENGTH table, +* keeping it around separately makes some calculations a lot faster +*/ + +static const int16_t MONTH_START[][3] = { + // Deficient Normal Complete + { 0, 0, 0 }, // (placeholder) + { 30, 30, 30 }, // Tishri + { 59, 59, 60 }, // Heshvan + { 88, 89, 90 }, // Kislev + { 117, 118, 119 }, // Tevet + { 147, 148, 149 }, // Shevat + { 147, 148, 149 }, // (Adar I placeholder) + { 176, 177, 178 }, // Adar + { 206, 207, 208 }, // Nisan + { 235, 236, 237 }, // Iyar + { 265, 266, 267 }, // Sivan + { 294, 295, 296 }, // Tammuz + { 324, 325, 326 }, // Av + { 353, 354, 355 }, // Elul +}; + +/** +* The cumulative # of days to the end of each month in a leap year +*/ +static const int16_t LEAP_MONTH_START[][3] = { + // Deficient Normal Complete + { 0, 0, 0 }, // (placeholder) + { 30, 30, 30 }, // Tishri + { 59, 59, 60 }, // Heshvan + { 88, 89, 90 }, // Kislev + { 117, 118, 119 }, // Tevet + { 147, 148, 149 }, // Shevat + { 177, 178, 179 }, // Adar I + { 206, 207, 208 }, // Adar II + { 236, 237, 238 }, // Nisan + { 265, 266, 267 }, // Iyar + { 295, 296, 297 }, // Sivan + { 324, 325, 326 }, // Tammuz + { 354, 355, 356 }, // Av + { 383, 384, 385 }, // Elul +}; + +static icu::CalendarCache *gCache = nullptr; + +U_CDECL_BEGIN +static UBool calendar_hebrew_cleanup() { + delete gCache; + gCache = nullptr; + return true; +} +U_CDECL_END + +U_NAMESPACE_BEGIN +//------------------------------------------------------------------------- +// Constructors... +//------------------------------------------------------------------------- + +/** +* Constructs a default HebrewCalendar using the current time +* in the default time zone with the default locale. +* @internal +*/ +HebrewCalendar::HebrewCalendar(const Locale& aLocale, UErrorCode& success) +: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success) + +{ + setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. +} + + +HebrewCalendar::~HebrewCalendar() { +} + +const char *HebrewCalendar::getType() const { + return "hebrew"; +} + +HebrewCalendar* HebrewCalendar::clone() const { + return new HebrewCalendar(*this); +} + +HebrewCalendar::HebrewCalendar(const HebrewCalendar& other) : Calendar(other) { +} + + +//------------------------------------------------------------------------- +// Rolling and adding functions overridden from Calendar +// +// These methods call through to the default implementation in IBMCalendar +// for most of the fields and only handle the unusual ones themselves. +//------------------------------------------------------------------------- + +/** +* Add a signed amount to a specified field, using this calendar's rules. +* For example, to add three days to the current date, you can call +* add(Calendar.DATE, 3). +*

+* When adding to certain fields, the values of other fields may conflict and +* need to be changed. For example, when adding one to the {@link #MONTH MONTH} field +* for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field +* must be adjusted so that the result is "29 Elul 5758" rather than the invalid +* "30 Elul 5758". +*

+* This method is able to add to +* all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET}, +* and {@link #ZONE_OFFSET ZONE_OFFSET}. +*

+* Note: You should always use {@link #roll roll} and add rather +* than attempting to perform arithmetic operations directly on the fields +* of a HebrewCalendar. Since the {@link #MONTH MONTH} field behaves +* discontinuously in non-leap years, simple arithmetic can give invalid results. +*

+* @param field the time field. +* @param amount the amount to add to the field. +* +* @exception IllegalArgumentException if the field is invalid or refers +* to a field that cannot be handled by this method. +* @internal +*/ +void HebrewCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) +{ + if(U_FAILURE(status)) { + return; + } + switch (field) { + case UCAL_MONTH: + case UCAL_ORDINAL_MONTH: + { + // We can't just do a set(MONTH, get(MONTH) + amount). The + // reason is ADAR_1. Suppose amount is +2 and we land in + // ADAR_1 -- then we have to bump to ADAR_2 aka ADAR. But + // if amount is -2 and we land in ADAR_1, then we have to + // bump the other way -- down to SHEVAT. - Alan 11/00 + int32_t month = get(UCAL_MONTH, status); + int32_t year = get(UCAL_YEAR, status); + UBool acrossAdar1; + if (amount > 0) { + acrossAdar1 = (month < ADAR_1); // started before ADAR_1? + month += amount; + for (;;) { + if (acrossAdar1 && month>=ADAR_1 && !isLeapYear(year)) { + ++month; + } + if (month <= ELUL) { + break; + } + month -= ELUL+1; + ++year; + acrossAdar1 = true; + } + } else { + acrossAdar1 = (month > ADAR_1); // started after ADAR_1? + month += amount; + for (;;) { + if (acrossAdar1 && month<=ADAR_1 && !isLeapYear(year)) { + --month; + } + if (month >= 0) { + break; + } + month += ELUL+1; + --year; + acrossAdar1 = true; + } + } + set(UCAL_MONTH, month); + set(UCAL_YEAR, year); + pinField(UCAL_DAY_OF_MONTH, status); + break; + } + + default: + Calendar::add(field, amount, status); + break; + } +} + +/** +* @deprecated ICU 2.6 use UCalendarDateFields instead of EDateFields +*/ +void HebrewCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) +{ + add((UCalendarDateFields)field, amount, status); +} + +/** +* Rolls (up/down) a specified amount time on the given field. For +* example, to roll the current date up by three days, you can call +* roll(Calendar.DATE, 3). If the +* field is rolled past its maximum allowable value, it will "wrap" back +* to its minimum and continue rolling. +* For example, calling roll(Calendar.DATE, 10) +* on a Hebrew calendar set to "25 Av 5758" will result in the date "5 Av 5758". +*

+* When rolling certain fields, the values of other fields may conflict and +* need to be changed. For example, when rolling the {@link #MONTH MONTH} field +* upward by one for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field +* must be adjusted so that the result is "29 Elul 5758" rather than the invalid +* "30 Elul". +*

+* This method is able to roll +* all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET}, +* and {@link #ZONE_OFFSET ZONE_OFFSET}. Subclasses may, of course, add support for +* additional fields in their overrides of roll. +*

+* Note: You should always use roll and {@link #add add} rather +* than attempting to perform arithmetic operations directly on the fields +* of a HebrewCalendar. Since the {@link #MONTH MONTH} field behaves +* discontinuously in non-leap years, simple arithmetic can give invalid results. +*

+* @param field the time field. +* @param amount the amount by which the field should be rolled. +* +* @exception IllegalArgumentException if the field is invalid or refers +* to a field that cannot be handled by this method. +* @internal +*/ +void HebrewCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) +{ + if(U_FAILURE(status)) { + return; + } + switch (field) { + case UCAL_MONTH: + case UCAL_ORDINAL_MONTH: + { + int32_t month = get(UCAL_MONTH, status); + int32_t year = get(UCAL_YEAR, status); + + UBool leapYear = isLeapYear(year); + int32_t yearLength = monthsInYear(year); + int32_t newMonth = month + (amount % yearLength); + // + // If it's not a leap year and we're rolling past the missing month + // of ADAR_1, we need to roll an extra month to make up for it. + // + if (!leapYear) { + if (amount > 0 && month < ADAR_1 && newMonth >= ADAR_1) { + newMonth++; + } else if (amount < 0 && month > ADAR_1 && newMonth <= ADAR_1) { + newMonth--; + } + } + set(UCAL_MONTH, (newMonth + 13) % 13); + pinField(UCAL_DAY_OF_MONTH, status); + return; + } + default: + Calendar::roll(field, amount, status); + } +} + +void HebrewCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { + roll((UCalendarDateFields)field, amount, status); +} + +//------------------------------------------------------------------------- +// Support methods +//------------------------------------------------------------------------- + +// Hebrew date calculations are performed in terms of days, hours, and +// "parts" (or halakim), which are 1/1080 of an hour, or 3 1/3 seconds. +static const int32_t HOUR_PARTS = 1080; +static const int32_t DAY_PARTS = 24*HOUR_PARTS; + +// An approximate value for the length of a lunar month. +// It is used to calculate the approximate year and month of a given +// absolute date. +static const int32_t MONTH_DAYS = 29; +static const int32_t MONTH_FRACT = 12*HOUR_PARTS + 793; +static const int32_t MONTH_PARTS = MONTH_DAYS*DAY_PARTS + MONTH_FRACT; + +// The time of the new moon (in parts) on 1 Tishri, year 1 (the epoch) +// counting from noon on the day before. BAHARAD is an abbreviation of +// Bet (Monday), Hey (5 hours from sunset), Resh-Daled (204). +static const int32_t BAHARAD = 11*HOUR_PARTS + 204; + +/** +* Finds the day # of the first day in the given Hebrew year. +* To do this, we want to calculate the time of the Tishri 1 new moon +* in that year. +*

+* The algorithm here is similar to ones described in a number of +* references, including: +*

+*/ +int32_t HebrewCalendar::startOfYear(int32_t year, UErrorCode &status) +{ + ucln_i18n_registerCleanup(UCLN_I18N_HEBREW_CALENDAR, calendar_hebrew_cleanup); + int32_t day = CalendarCache::get(&gCache, year, status); + + if (day == 0) { + // # of months before year + int32_t months = (int32_t)ClockMath::floorDivide((235 * (int64_t)year - 234), (int64_t)19); + + int64_t frac = (int64_t)months * MONTH_FRACT + BAHARAD; // Fractional part of day # + day = months * 29 + (int32_t)(frac / DAY_PARTS); // Whole # part of calculation + frac = frac % DAY_PARTS; // Time of day + + int32_t wd = (day % 7); // Day of week (0 == Monday) + + if (wd == 2 || wd == 4 || wd == 6) { + // If the 1st is on Sun, Wed, or Fri, postpone to the next day + day += 1; + wd = (day % 7); + } + if (wd == 1 && frac > 15*HOUR_PARTS+204 && !isLeapYear(year) ) { + // If the new moon falls after 3:11:20am (15h204p from the previous noon) + // on a Tuesday and it is not a leap year, postpone by 2 days. + // This prevents 356-day years. + day += 2; + } + else if (wd == 0 && frac > 21*HOUR_PARTS+589 && isLeapYear(year-1) ) { + // If the new moon falls after 9:32:43 1/3am (21h589p from yesterday noon) + // on a Monday and *last* year was a leap year, postpone by 1 day. + // Prevents 382-day years. + day += 1; + } + CalendarCache::put(&gCache, year, day, status); + } + return day; +} + +/** +* Find the day of the week for a given day +* +* @param day The # of days since the start of the Hebrew calendar, +* 1-based (i.e. 1/1/1 AM is day 1). +*/ +int32_t HebrewCalendar::absoluteDayToDayOfWeek(int32_t day) +{ + // We know that 1/1/1 AM is a Monday, which makes the math easy... + return (day % 7) + 1; +} + +/** +* Returns the the type of a given year. +* 0 "Deficient" year with 353 or 383 days +* 1 "Normal" year with 354 or 384 days +* 2 "Complete" year with 355 or 385 days +*/ +int32_t HebrewCalendar::yearType(int32_t year) const +{ + int32_t yearLength = handleGetYearLength(year); + + if (yearLength > 380) { + yearLength -= 30; // Subtract length of leap month. + } + + int type = 0; + + switch (yearLength) { + case 353: + type = 0; break; + case 354: + type = 1; break; + case 355: + type = 2; break; + default: + //throw new RuntimeException("Illegal year length " + yearLength + " in year " + year); + type = 1; + } + return type; +} + +/** +* Determine whether a given Hebrew year is a leap year +* +* The rule here is that if (year % 19) == 0, 3, 6, 8, 11, 14, or 17. +* The formula below performs the same test, believe it or not. +*/ +UBool HebrewCalendar::isLeapYear(int32_t year) { + //return (year * 12 + 17) % 19 >= 12; + int32_t x = (year*12 + 17) % 19; + return x >= ((x < 0) ? -7 : 12); +} + +int32_t HebrewCalendar::monthsInYear(int32_t year) { + return isLeapYear(year) ? 13 : 12; +} + +//------------------------------------------------------------------------- +// Calendar framework +//------------------------------------------------------------------------- + +/** +* @internal +*/ +int32_t HebrewCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { + return LIMITS[field][limitType]; +} + +/** +* Returns the length of the given month in the given year +* @internal +*/ +int32_t HebrewCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { + // Resolve out-of-range months. This is necessary in order to + // obtain the correct year. We correct to + // a 12- or 13-month year (add/subtract 12 or 13, depending + // on the year) but since we _always_ number from 0..12, and + // the leap year determines whether or not month 5 (Adar 1) + // is present, we allow 0..12 in any given year. + while (month < 0) { + month += monthsInYear(--extendedYear); + } + // Careful: allow 0..12 in all years + while (month > 12) { + month -= monthsInYear(extendedYear++); + } + + switch (month) { + case HESHVAN: + case KISLEV: + // These two month lengths can vary + return MONTH_LENGTH[month][yearType(extendedYear)]; + + default: + // The rest are a fixed length + return MONTH_LENGTH[month][0]; + } +} + +/** +* Returns the number of days in the given Hebrew year +* @internal +*/ +int32_t HebrewCalendar::handleGetYearLength(int32_t eyear) const { + UErrorCode status = U_ZERO_ERROR; + return startOfYear(eyear+1, status) - startOfYear(eyear, status); +} + +void HebrewCalendar::validateField(UCalendarDateFields field, UErrorCode &status) { + if ((field == UCAL_MONTH || field == UCAL_ORDINAL_MONTH) + && !isLeapYear(handleGetExtendedYear()) && internalGetMonth() == ADAR_1) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + Calendar::validateField(field, status); +} +//------------------------------------------------------------------------- +// Functions for converting from milliseconds to field values +//------------------------------------------------------------------------- + +/** +* Subclasses may override this method to compute several fields +* specific to each calendar system. These are: +* +*
  • ERA +*
  • YEAR +*
  • MONTH +*
  • DAY_OF_MONTH +*
  • DAY_OF_YEAR +*
  • EXTENDED_YEAR
+* +* Subclasses can refer to the DAY_OF_WEEK and DOW_LOCAL fields, +* which will be set when this method is called. Subclasses can +* also call the getGregorianXxx() methods to obtain Gregorian +* calendar equivalents for the given Julian day. +* +*

In addition, subclasses should compute any subclass-specific +* fields, that is, fields from BASE_FIELD_COUNT to +* getFieldCount() - 1. +* @internal +*/ +void HebrewCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { + int32_t d = julianDay - 347997; + double m = ClockMath::floorDivide((d * (double)DAY_PARTS), (double) MONTH_PARTS); // Months (approx) + int32_t year = (int32_t)(ClockMath::floorDivide((19. * m + 234.), 235.) + 1.); // Years (approx) + int32_t ys = startOfYear(year, status); // 1st day of year + int32_t dayOfYear = (d - ys); + + // Because of the postponement rules, it's possible to guess wrong. Fix it. + while (dayOfYear < 1) { + year--; + ys = startOfYear(year, status); + dayOfYear = (d - ys); + } + + // Now figure out which month we're in, and the date within that month + int32_t type = yearType(year); + UBool isLeap = isLeapYear(year); + + int32_t month = 0; + int32_t momax = UPRV_LENGTHOF(MONTH_START); + while (month < momax && dayOfYear > ( isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type] ) ) { + month++; + } + if (month >= momax || month<=0) { + // TODO: I found dayOfYear could be out of range when + // a large value is set to julianDay. I patched startOfYear + // to reduce the chace, but it could be still reproduced either + // by startOfYear or other places. For now, we check + // the month is in valid range to avoid out of array index + // access problem here. However, we need to carefully review + // the calendar implementation to check the extreme limit of + // each calendar field and the code works well for any values + // in the valid value range. -yoshito + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + month--; + int dayOfMonth = dayOfYear - (isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type]); + + internalSet(UCAL_ERA, 0); + internalSet(UCAL_YEAR, year); + internalSet(UCAL_EXTENDED_YEAR, year); + int32_t ordinal_month = month; + if (!isLeap && ordinal_month > ADAR_1) { + ordinal_month--; + } + internalSet(UCAL_ORDINAL_MONTH, ordinal_month); + internalSet(UCAL_MONTH, month); + internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); + internalSet(UCAL_DAY_OF_YEAR, dayOfYear); +} + +//------------------------------------------------------------------------- +// Functions for converting from field values to milliseconds +//------------------------------------------------------------------------- + +/** +* @internal +*/ +int32_t HebrewCalendar::handleGetExtendedYear() { + int32_t year; + if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { + year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 + } else { + year = internalGet(UCAL_YEAR, 1); // Default to year 1 + } + return year; +} + +/** +* Return JD of start of given month/year. +* @internal +*/ +int32_t HebrewCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/) const { + UErrorCode status = U_ZERO_ERROR; + // Resolve out-of-range months. This is necessary in order to + // obtain the correct year. We correct to + // a 12- or 13-month year (add/subtract 12 or 13, depending + // on the year) but since we _always_ number from 0..12, and + // the leap year determines whether or not month 5 (Adar 1) + // is present, we allow 0..12 in any given year. + while (month < 0) { + month += monthsInYear(--eyear); + } + // Careful: allow 0..12 in all years + while (month > 12) { + month -= monthsInYear(eyear++); + } + + int32_t day = startOfYear(eyear, status); + + if(U_FAILURE(status)) { + return 0; + } + + if (month != 0) { + if (isLeapYear(eyear)) { + day += LEAP_MONTH_START[month][yearType(eyear)]; + } else { + day += MONTH_START[month][yearType(eyear)]; + } + } + + return (int) (day + 347997); +} + +constexpr uint32_t kHebrewRelatedYearDiff = -3760; + +int32_t HebrewCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kHebrewRelatedYearDiff; +} + +void HebrewCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kHebrewRelatedYearDiff); +} + +/** + * The system maintains a static default century start date and Year. They are + * initialized the first time they are used. Once the system default century date + * and year are set, they do not change. + */ +static UDate gSystemDefaultCenturyStart = DBL_MIN; +static int32_t gSystemDefaultCenturyStartYear = -1; +static icu::UInitOnce gSystemDefaultCenturyInit {}; + +UBool HebrewCalendar::haveDefaultCentury() const +{ + return true; +} + +static void U_CALLCONV initializeSystemDefaultCentury() +{ + // initialize systemDefaultCentury and systemDefaultCenturyYear based + // on the current time. They'll be set to 80 years before + // the current time. + UErrorCode status = U_ZERO_ERROR; + HebrewCalendar calendar(Locale("@calendar=hebrew"),status); + if (U_SUCCESS(status)) { + calendar.setTime(Calendar::getNow(), status); + calendar.add(UCAL_YEAR, -80, status); + + gSystemDefaultCenturyStart = calendar.getTime(status); + gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); + } + // We have no recourse upon failure unless we want to propagate the failure + // out. +} + + +UDate HebrewCalendar::defaultCenturyStart() const { + // lazy-evaluate systemDefaultCenturyStart + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStart; +} + +int32_t HebrewCalendar::defaultCenturyStartYear() const { + // lazy-evaluate systemDefaultCenturyStartYear + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStartYear; +} + +bool HebrewCalendar::inTemporalLeapYear(UErrorCode& status) const { + if (U_FAILURE(status)) return false; + int32_t eyear = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) return false; + return isLeapYear(eyear); +} + +static const char * const gTemporalMonthCodesForHebrew[] = { + "M01", "M02", "M03", "M04", "M05", "M05L", "M06", + "M07", "M08", "M09", "M10", "M11", "M12", nullptr +}; + +const char* HebrewCalendar::getTemporalMonthCode(UErrorCode& status) const { + int32_t month = get(UCAL_MONTH, status); + if (U_FAILURE(status)) return nullptr; + return gTemporalMonthCodesForHebrew[month]; +} + +void HebrewCalendar::setTemporalMonthCode(const char* code, UErrorCode& status ) +{ + if (U_FAILURE(status)) return; + int32_t len = static_cast(uprv_strlen(code)); + if (len == 3 || len == 4) { + for (int m = 0; gTemporalMonthCodesForHebrew[m] != nullptr; m++) { + if (uprv_strcmp(code, gTemporalMonthCodesForHebrew[m]) == 0) { + set(UCAL_MONTH, m); + return; + } + } + } + status = U_ILLEGAL_ARGUMENT_ERROR; +} + +int32_t HebrewCalendar::internalGetMonth() const { + if (resolveFields(kMonthPrecedence) == UCAL_ORDINAL_MONTH) { + int32_t ordinalMonth = internalGet(UCAL_ORDINAL_MONTH); + HebrewCalendar *nonConstThis = (HebrewCalendar*)this; // cast away const + + int32_t year = nonConstThis->handleGetExtendedYear(); + return ordinalMonth + ((isLeapYear(year) && (ordinalMonth > ADAR_1)) ? 1: 0); + } + return Calendar::internalGetMonth(); +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(HebrewCalendar) + +U_NAMESPACE_END + +#endif // UCONFIG_NO_FORMATTING + diff --git a/deps/icu-small/source/i18n/hebrwcal.h b/deps/icu-small/source/i18n/hebrwcal.h index d75651d47a4fde..55899e4a0eb373 100644 --- a/deps/icu-small/source/i18n/hebrwcal.h +++ b/deps/icu-small/source/i18n/hebrwcal.h @@ -1,445 +1,492 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 2003-2013, International Business Machines Corporation -* and others. All Rights Reserved. -****************************************************************************** -* -* File HEBRWCAL.H -* -* Modification History: -* -* Date Name Description -* 05/13/2003 srl copied from gregocal.h -* 11/26/2003 srl copied from buddhcal.h -****************************************************************************** -*/ - -#ifndef HEBRWCAL_H -#define HEBRWCAL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/calendar.h" -#include "unicode/gregocal.h" - -U_NAMESPACE_BEGIN - -/** - * HebrewCalendar is a subclass of Calendar - * that that implements the traditional Hebrew calendar. - * This is the civil calendar in Israel and the liturgical calendar - * of the Jewish faith worldwide. - *

- * The Hebrew calendar is lunisolar and thus has a number of interesting - * properties that distinguish it from the Gregorian. Months start - * on the day of (an arithmetic approximation of) each new moon. Since the - * solar year (approximately 365.24 days) is not an even multiple of - * the lunar month (approximately 29.53 days) an extra "leap month" is - * inserted in 7 out of every 19 years. To make matters even more - * interesting, the start of a year can be delayed by up to three days - * in order to prevent certain holidays from falling on the Sabbath and - * to prevent certain illegal year lengths. Finally, the lengths of certain - * months can vary depending on the number of days in the year. - *

- * The leap month is known as "Adar 1" and is inserted between the - * months of Shevat and Adar in leap years. Since the leap month does - * not come at the end of the year, calculations involving - * month numbers are particularly complex. Users of this class should - * make sure to use the {@link #roll roll} and {@link #add add} methods - * rather than attempting to perform date arithmetic by manipulating - * the fields directly. - *

- * Note: In the traditional Hebrew calendar, days start at sunset. - * However, in order to keep the time fields in this class - * synchronized with those of the other calendars and with local clock time, - * we treat days and months as beginning at midnight, - * roughly 6 hours after the corresponding sunset. - *

- * If you are interested in more information on the rules behind the Hebrew - * calendar, see one of the following references: - *

- *

- * @see com.ibm.icu.util.GregorianCalendar - * - * @author Laura Werner - * @author Alan Liu - * @author Steven R. Loomis - *

- * @internal - */ -class U_I18N_API HebrewCalendar : public Calendar { -public: - /** - * Useful constants for HebrewCalendar. - * @internal - */ - enum Month { - /** - * Constant for Tishri, the 1st month of the Hebrew year. - */ - TISHRI, - /** - * Constant for Heshvan, the 2nd month of the Hebrew year. - */ - HESHVAN, - /** - * Constant for Kislev, the 3rd month of the Hebrew year. - */ - KISLEV, - - /** - * Constant for Tevet, the 4th month of the Hebrew year. - */ - TEVET, - - /** - * Constant for Shevat, the 5th month of the Hebrew year. - */ - SHEVAT, - - /** - * Constant for Adar I, the 6th month of the Hebrew year - * (present in leap years only). In non-leap years, the calendar - * jumps from Shevat (5th month) to Adar (7th month). - */ - ADAR_1, - - /** - * Constant for the Adar, the 7th month of the Hebrew year. - */ - ADAR, - - /** - * Constant for Nisan, the 8th month of the Hebrew year. - */ - NISAN, - - /** - * Constant for Iyar, the 9th month of the Hebrew year. - */ - IYAR, - - /** - * Constant for Sivan, the 10th month of the Hebrew year. - */ - SIVAN, - - /** - * Constant for Tammuz, the 11th month of the Hebrew year. - */ - TAMUZ, - - /** - * Constant for Av, the 12th month of the Hebrew year. - */ - AV, - - /** - * Constant for Elul, the 13th month of the Hebrew year. - */ - ELUL - }; - - /** - * Constructs a HebrewCalendar based on the current time in the default time zone - * with the given locale. - * - * @param aLocale The given locale. - * @param success Indicates the status of HebrewCalendar object construction. - * Returns U_ZERO_ERROR if constructed successfully. - * @internal - */ - HebrewCalendar(const Locale& aLocale, UErrorCode& success); - - - /** - * Destructor - * @internal - */ - virtual ~HebrewCalendar(); - - /** - * Copy constructor - * @param source the object to be copied. - * @internal - */ - HebrewCalendar(const HebrewCalendar& source); - - /** - * Create and return a polymorphic copy of this calendar. - * @return return a polymorphic copy of this calendar. - * @internal - */ - virtual HebrewCalendar* clone() const override; - -public: - /** - * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual - * override. This method is to implement a simple version of RTTI, since not all C++ - * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call - * this method. - * - * @return The class ID for this object. All objects of a given class have the - * same class ID. Objects of other classes have different class IDs. - * @internal - */ - virtual UClassID getDynamicClassID(void) const override; - - /** - * Return the class ID for this class. This is useful only for comparing to a return - * value from getDynamicClassID(). For example: - * - * Base* polymorphic_pointer = createPolymorphicObject(); - * if (polymorphic_pointer->getDynamicClassID() == - * Derived::getStaticClassID()) ... - * - * @return The class ID for all objects of this class. - * @internal - */ - static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * return the calendar type, "hebrew". - * - * @return calendar type - * @internal - */ - virtual const char * getType() const override; - - - // Calendar API - public: - /** - * (Overrides Calendar) UDate Arithmetic function. Adds the specified (signed) amount - * of time to the given time field, based on the calendar's rules. For more - * information, see the documentation for Calendar::add(). - * - * @param field The time field. - * @param amount The amount of date or time to be added to the field. - * @param status Output param set to success/failure code on exit. If any value - * previously set in the time field is invalid, this will be set to - * an error status. - */ - virtual void add(UCalendarDateFields field, int32_t amount, UErrorCode& status) override; - /** - * @deprecated ICU 2.6 use UCalendarDateFields instead of EDateFields - */ - virtual void add(EDateFields field, int32_t amount, UErrorCode& status) override; - - - /** - * (Overrides Calendar) Rolls up or down by the given amount in the specified field. - * For more information, see the documentation for Calendar::roll(). - * - * @param field The time field. - * @param amount Indicates amount to roll. - * @param status Output param set to success/failure code on exit. If any value - * previously set in the time field is invalid, this will be set to - * an error status. - * @internal - */ - virtual void roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) override; - - /** - * (Overrides Calendar) Rolls up or down by the given amount in the specified field. - * For more information, see the documentation for Calendar::roll(). - * - * @param field The time field. - * @param amount Indicates amount to roll. - * @param status Output param set to success/failure code on exit. If any value - * previously set in the time field is invalid, this will be set to - * an error status. - * @deprecated ICU 2.6. Use roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) instead. -` */ - virtual void roll(EDateFields field, int32_t amount, UErrorCode& status) override; - - /** - * @internal - */ - static UBool isLeapYear(int32_t year) ; - - protected: - - /** - * Subclass API for defining limits of different types. - * Subclasses must implement this method to return limits for the - * following fields: - * - *

UCAL_ERA
-     * UCAL_YEAR
-     * UCAL_MONTH
-     * UCAL_WEEK_OF_YEAR
-     * UCAL_WEEK_OF_MONTH
-     * UCAL_DATE (DAY_OF_MONTH on Java)
-     * UCAL_DAY_OF_YEAR
-     * UCAL_DAY_OF_WEEK_IN_MONTH
-     * UCAL_YEAR_WOY
-     * UCAL_EXTENDED_YEAR
- * - * @param field one of the above field numbers - * @param limitType one of MINIMUM, GREATEST_MINIMUM, - * LEAST_MAXIMUM, or MAXIMUM - * @internal - */ - virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; - - /** - * Return the number of days in the given month of the given extended - * year of this calendar system. Subclasses should override this - * method if they can provide a more correct or more efficient - * implementation than the default implementation in Calendar. - * @internal - */ - virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; - - /** - * Return the number of days in the given extended year of this - * calendar system. Subclasses should override this method if they can - * provide a more correct or more efficient implementation than the - * default implementation in Calendar. - * @stable ICU 2.0 - */ - virtual int32_t handleGetYearLength(int32_t eyear) const override; - /** - * Subclasses may override this method to compute several fields - * specific to each calendar system. These are: - * - *
  • ERA - *
  • YEAR - *
  • MONTH - *
  • DAY_OF_MONTH - *
  • DAY_OF_YEAR - *
  • EXTENDED_YEAR
- * - *

The GregorianCalendar implementation implements - * a calendar with the specified Julian/Gregorian cutover date. - * @internal - */ - virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; - /** - * Return the extended year defined by the current fields. This will - * use the UCAL_EXTENDED_YEAR field or the UCAL_YEAR and supra-year fields (such - * as UCAL_ERA) specific to the calendar system, depending on which set of - * fields is newer. - * @return the extended year - * @internal - */ - virtual int32_t handleGetExtendedYear() override; - /** - * Return the Julian day number of day before the first day of the - * given month in the given extended year. Subclasses should override - * this method to implement their calendar system. - * @param eyear the extended year - * @param month the zero-based month, or 0 if useMonth is false - * @param useMonth if false, compute the day before the first day of - * the given year, otherwise, compute the day before the first day of - * the given month - * @param return the Julian day number of the day before the first - * day of the given month and year - * @internal - */ - virtual int32_t handleComputeMonthStart(int32_t eyear, int32_t month, - UBool useMonth) const override; - - - /** - * Validate a single field of this calendar. - * Overrides Calendar::validateField(int) to provide - * special handling for month validation for Hebrew calendar. - * @internal - */ - virtual void validateField(UCalendarDateFields field, UErrorCode &status) override; - - protected: - - /** - * (Overrides Calendar) Return true if the current date for this Calendar is in - * Daylight Savings Time. Recognizes DST_OFFSET, if it is set. - * - * @param status Fill-in parameter which receives the status of this operation. - * @return True if the current date for this Calendar is in Daylight Savings Time, - * false, otherwise. - * @internal - */ - virtual UBool inDaylightTime(UErrorCode& status) const override; - - /** - * Returns true because the Hebrew Calendar does have a default century - * @internal - */ - virtual UBool haveDefaultCentury() const override; - - /** - * Returns the date of the start of the default century - * @return start of century - in milliseconds since epoch, 1970 - * @internal - */ - virtual UDate defaultCenturyStart() const override; - - /** - * Returns the year in which the default century begins - * @internal - */ - virtual int32_t defaultCenturyStartYear() const override; - - private: // Calendar-specific implementation - /** - * Finds the day # of the first day in the given Hebrew year. - * To do this, we want to calculate the time of the Tishri 1 new moon - * in that year. - *

- * The algorithm here is similar to ones described in a number of - * references, including: - *

- * @param year extended year - * @return day number (JD) - * @internal - */ - static int32_t startOfYear(int32_t year, UErrorCode& status); - - static int32_t absoluteDayToDayOfWeek(int32_t day) ; - - /** - * @internal - */ - int32_t yearType(int32_t year) const; - - /** - * @internal - */ - static int32_t monthsInYear(int32_t year) ; -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -#endif -//eof - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 2003-2013, International Business Machines Corporation +* and others. All Rights Reserved. +****************************************************************************** +* +* File HEBRWCAL.H +* +* Modification History: +* +* Date Name Description +* 05/13/2003 srl copied from gregocal.h +* 11/26/2003 srl copied from buddhcal.h +****************************************************************************** +*/ + +#ifndef HEBRWCAL_H +#define HEBRWCAL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" +#include "unicode/gregocal.h" + +U_NAMESPACE_BEGIN + +/** + * HebrewCalendar is a subclass of Calendar + * that that implements the traditional Hebrew calendar. + * This is the civil calendar in Israel and the liturgical calendar + * of the Jewish faith worldwide. + *

+ * The Hebrew calendar is lunisolar and thus has a number of interesting + * properties that distinguish it from the Gregorian. Months start + * on the day of (an arithmetic approximation of) each new moon. Since the + * solar year (approximately 365.24 days) is not an even multiple of + * the lunar month (approximately 29.53 days) an extra "leap month" is + * inserted in 7 out of every 19 years. To make matters even more + * interesting, the start of a year can be delayed by up to three days + * in order to prevent certain holidays from falling on the Sabbath and + * to prevent certain illegal year lengths. Finally, the lengths of certain + * months can vary depending on the number of days in the year. + *

+ * The leap month is known as "Adar 1" and is inserted between the + * months of Shevat and Adar in leap years. Since the leap month does + * not come at the end of the year, calculations involving + * month numbers are particularly complex. Users of this class should + * make sure to use the {@link #roll roll} and {@link #add add} methods + * rather than attempting to perform date arithmetic by manipulating + * the fields directly. + *

+ * Note: In the traditional Hebrew calendar, days start at sunset. + * However, in order to keep the time fields in this class + * synchronized with those of the other calendars and with local clock time, + * we treat days and months as beginning at midnight, + * roughly 6 hours after the corresponding sunset. + *

+ * If you are interested in more information on the rules behind the Hebrew + * calendar, see one of the following references: + *

+ *

+ * @see com.ibm.icu.util.GregorianCalendar + * + * @author Laura Werner + * @author Alan Liu + * @author Steven R. Loomis + *

+ * @internal + */ +class U_I18N_API HebrewCalendar : public Calendar { +public: + /** + * Useful constants for HebrewCalendar. + * @internal + */ + enum Month { + /** + * Constant for Tishri, the 1st month of the Hebrew year. + */ + TISHRI, + /** + * Constant for Heshvan, the 2nd month of the Hebrew year. + */ + HESHVAN, + /** + * Constant for Kislev, the 3rd month of the Hebrew year. + */ + KISLEV, + + /** + * Constant for Tevet, the 4th month of the Hebrew year. + */ + TEVET, + + /** + * Constant for Shevat, the 5th month of the Hebrew year. + */ + SHEVAT, + + /** + * Constant for Adar I, the 6th month of the Hebrew year + * (present in leap years only). In non-leap years, the calendar + * jumps from Shevat (5th month) to Adar (7th month). + */ + ADAR_1, + + /** + * Constant for the Adar, the 7th month of the Hebrew year. + */ + ADAR, + + /** + * Constant for Nisan, the 8th month of the Hebrew year. + */ + NISAN, + + /** + * Constant for Iyar, the 9th month of the Hebrew year. + */ + IYAR, + + /** + * Constant for Sivan, the 10th month of the Hebrew year. + */ + SIVAN, + + /** + * Constant for Tammuz, the 11th month of the Hebrew year. + */ + TAMUZ, + + /** + * Constant for Av, the 12th month of the Hebrew year. + */ + AV, + + /** + * Constant for Elul, the 13th month of the Hebrew year. + */ + ELUL + }; + + /** + * Constructs a HebrewCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of HebrewCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + HebrewCalendar(const Locale& aLocale, UErrorCode& success); + + + /** + * Destructor + * @internal + */ + virtual ~HebrewCalendar(); + + /** + * Copy constructor + * @param source the object to be copied. + * @internal + */ + HebrewCalendar(const HebrewCalendar& source); + + /** + * Create and return a polymorphic copy of this calendar. + * @return return a polymorphic copy of this calendar. + * @internal + */ + virtual HebrewCalendar* clone() const override; + +public: + /** + * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual + * override. This method is to implement a simple version of RTTI, since not all C++ + * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call + * this method. + * + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * return the calendar type, "hebrew". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + + // Calendar API + public: + /** + * (Overrides Calendar) UDate Arithmetic function. Adds the specified (signed) amount + * of time to the given time field, based on the calendar's rules. For more + * information, see the documentation for Calendar::add(). + * + * @param field The time field. + * @param amount The amount of date or time to be added to the field. + * @param status Output param set to success/failure code on exit. If any value + * previously set in the time field is invalid, this will be set to + * an error status. + */ + virtual void add(UCalendarDateFields field, int32_t amount, UErrorCode& status) override; + /** + * @deprecated ICU 2.6 use UCalendarDateFields instead of EDateFields + */ + virtual void add(EDateFields field, int32_t amount, UErrorCode& status) override; + + + /** + * (Overrides Calendar) Rolls up or down by the given amount in the specified field. + * For more information, see the documentation for Calendar::roll(). + * + * @param field The time field. + * @param amount Indicates amount to roll. + * @param status Output param set to success/failure code on exit. If any value + * previously set in the time field is invalid, this will be set to + * an error status. + * @internal + */ + virtual void roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) override; + + /** + * (Overrides Calendar) Rolls up or down by the given amount in the specified field. + * For more information, see the documentation for Calendar::roll(). + * + * @param field The time field. + * @param amount Indicates amount to roll. + * @param status Output param set to success/failure code on exit. If any value + * previously set in the time field is invalid, this will be set to + * an error status. + * @deprecated ICU 2.6. Use roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) instead. +` */ + virtual void roll(EDateFields field, int32_t amount, UErrorCode& status) override; + + /** + * @internal + */ + static UBool isLeapYear(int32_t year) ; + + /** + * @return The related Gregorian year; will be obtained by modifying the value + * obtained by get from UCAL_EXTENDED_YEAR field + * @internal + */ + virtual int32_t getRelatedYear(UErrorCode &status) const override; + + /** + * @param year The related Gregorian year to set; will be modified as necessary then + * set in UCAL_EXTENDED_YEAR field + * @internal + */ + virtual void setRelatedYear(int32_t year) override; + + protected: + + /** + * Subclass API for defining limits of different types. + * Subclasses must implement this method to return limits for the + * following fields: + * + *

UCAL_ERA
+     * UCAL_YEAR
+     * UCAL_MONTH
+     * UCAL_WEEK_OF_YEAR
+     * UCAL_WEEK_OF_MONTH
+     * UCAL_DATE (DAY_OF_MONTH on Java)
+     * UCAL_DAY_OF_YEAR
+     * UCAL_DAY_OF_WEEK_IN_MONTH
+     * UCAL_YEAR_WOY
+     * UCAL_EXTENDED_YEAR
+ * + * @param field one of the above field numbers + * @param limitType one of MINIMUM, GREATEST_MINIMUM, + * LEAST_MAXIMUM, or MAXIMUM + * @internal + */ + virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; + + /** + * Return the number of days in the given month of the given extended + * year of this calendar system. Subclasses should override this + * method if they can provide a more correct or more efficient + * implementation than the default implementation in Calendar. + * @internal + */ + virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; + + /** + * Return the number of days in the given extended year of this + * calendar system. Subclasses should override this method if they can + * provide a more correct or more efficient implementation than the + * default implementation in Calendar. + * @stable ICU 2.0 + */ + virtual int32_t handleGetYearLength(int32_t eyear) const override; + /** + * Subclasses may override this method to compute several fields + * specific to each calendar system. These are: + * + *
  • ERA + *
  • YEAR + *
  • MONTH + *
  • DAY_OF_MONTH + *
  • DAY_OF_YEAR + *
  • EXTENDED_YEAR
+ * + *

The GregorianCalendar implementation implements + * a calendar with the specified Julian/Gregorian cutover date. + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; + /** + * Return the extended year defined by the current fields. This will + * use the UCAL_EXTENDED_YEAR field or the UCAL_YEAR and supra-year fields (such + * as UCAL_ERA) specific to the calendar system, depending on which set of + * fields is newer. + * @return the extended year + * @internal + */ + virtual int32_t handleGetExtendedYear() override; + /** + * Return the Julian day number of day before the first day of the + * given month in the given extended year. Subclasses should override + * this method to implement their calendar system. + * @param eyear the extended year + * @param month the zero-based month, or 0 if useMonth is false + * @param useMonth if false, compute the day before the first day of + * the given year, otherwise, compute the day before the first day of + * the given month + * @param return the Julian day number of the day before the first + * day of the given month and year + * @internal + */ + virtual int32_t handleComputeMonthStart(int32_t eyear, int32_t month, + UBool useMonth) const override; + + + /** + * Validate a single field of this calendar. + * Overrides Calendar::validateField(int) to provide + * special handling for month validation for Hebrew calendar. + * @internal + */ + virtual void validateField(UCalendarDateFields field, UErrorCode &status) override; + + protected: + /** + * Returns true because the Hebrew Calendar does have a default century + * @internal + */ + virtual UBool haveDefaultCentury() const override; + + /** + * Returns the date of the start of the default century + * @return start of century - in milliseconds since epoch, 1970 + * @internal + */ + virtual UDate defaultCenturyStart() const override; + + /** + * Returns the year in which the default century begins + * @internal + */ + virtual int32_t defaultCenturyStartYear() const override; + + public: + /** + * Returns true if the date is in a leap year. + * + * @param status ICU Error Code + * @return True if the date in the fields is in a Temporal proposal + * defined leap year. False otherwise. + */ + virtual bool inTemporalLeapYear(UErrorCode& status) const override; + + /** + * Gets The Temporal monthCode value corresponding to the month for the date. + * The value is a string identifier that starts with the literal grapheme + * "M" followed by two graphemes representing the zero-padded month number + * of the current month in a normal (non-leap) year and suffixed by an + * optional literal grapheme "L" if this is a leap month in a lunisolar + * calendar. For the Hebrew calendar, the values are "M01" .. "M12" for + * non-leap year, and "M01" .. "M05", "M05L", "M06" .. "M12" for leap year. + * + * @param status ICU Error Code + * @return One of 13 possible strings in {"M01".. "M05", "M05L", + * "M06" .. "M12"}. + * @draft ICU 73 + */ + virtual const char* getTemporalMonthCode(UErrorCode& status) const override; + + /** + * Sets The Temporal monthCode which is a string identifier that starts + * with the literal grapheme "M" followed by two graphemes representing + * the zero-padded month number of the current month in a normal + * (non-leap) year and suffixed by an optional literal grapheme "L" if this + * is a leap month in a lunisolar calendar. For Hebrew calendar, the values + * are "M01" .. "M12" for non-leap years, and "M01" .. "M05", "M05L", "M06" + * .. "M12" for leap year. + * + * @param temporalMonth The value to be set for temporal monthCode. + * @param status ICU Error Code + * + * @draft ICU 73 + */ + virtual void setTemporalMonthCode(const char* code, UErrorCode& status ) override; + + protected: + virtual int32_t internalGetMonth() const override; + + private: // Calendar-specific implementation + /** + * Finds the day # of the first day in the given Hebrew year. + * To do this, we want to calculate the time of the Tishri 1 new moon + * in that year. + *

+ * The algorithm here is similar to ones described in a number of + * references, including: + *

+ * @param year extended year + * @return day number (JD) + * @internal + */ + static int32_t startOfYear(int32_t year, UErrorCode& status); + + static int32_t absoluteDayToDayOfWeek(int32_t day) ; + + /** + * @internal + */ + int32_t yearType(int32_t year) const; + + /** + * @internal + */ + static int32_t monthsInYear(int32_t year) ; +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif +//eof + diff --git a/deps/icu-small/source/i18n/i18n.rc b/deps/icu-small/source/i18n/i18n.rc index c31ef3ad5f78cd..0ee1b9774fa71b 100644 --- a/deps/icu-small/source/i18n/i18n.rc +++ b/deps/icu-small/source/i18n/i18n.rc @@ -1,110 +1,110 @@ -// Do not edit with Microsoft Developer Studio Resource Editor. -// It will permanently substitute version numbers that are intended to be -// picked up by the pre-processor during each build. -// Copyright (C) 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// Copyright (c) 2001-2010 International Business Machines -// Corporation and others. All Rights Reserved. -// -#include "../common/msvcres.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// - -LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "../common/msvcres.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include \0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// -#define STR(s) #s -#define CommaVersionString(a, b, c, d) STR(a) ", " STR(b) ", " STR(c) ", " STR(d) "\0" - -VS_VERSION_INFO VERSIONINFO - FILEVERSION U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM - PRODUCTVERSION U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_DLL - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "00000000" - BEGIN - VALUE "Comments", ICU_WEBSITE "\0" - VALUE "CompanyName", ICU_COMPANY "\0" - VALUE "FileDescription", ICU_PRODUCT_PREFIX " I18N DLL\0" - VALUE "FileVersion", CommaVersionString(U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM) - VALUE "LegalCopyright", U_COPYRIGHT_STRING "\0" -#ifdef _DEBUG - VALUE "OriginalFilename", "icuin" U_ICU_VERSION_SHORT "d.dll\0" -#else - VALUE "OriginalFilename", "icuin" U_ICU_VERSION_SHORT ".dll\0" -#endif - VALUE "PrivateBuild", "\0" - VALUE "ProductName", ICU_PRODUCT "\0" - VALUE "ProductVersion", CommaVersionString(U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM) - VALUE "SpecialBuild", "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x000, 0000 - END -END - -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// Do not edit with Microsoft Developer Studio Resource Editor. +// It will permanently substitute version numbers that are intended to be +// picked up by the pre-processor during each build. +// Copyright (C) 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// Copyright (c) 2001-2010 International Business Machines +// Corporation and others. All Rights Reserved. +// +#include "../common/msvcres.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "../common/msvcres.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include \0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#define STR(s) #s +#define CommaVersionString(a, b, c, d) STR(a) ", " STR(b) ", " STR(c) ", " STR(d) "\0" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM + PRODUCTVERSION U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "00000000" + BEGIN + VALUE "Comments", ICU_WEBSITE "\0" + VALUE "CompanyName", ICU_COMPANY "\0" + VALUE "FileDescription", ICU_PRODUCT_PREFIX " I18N DLL\0" + VALUE "FileVersion", CommaVersionString(U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM) + VALUE "LegalCopyright", U_COPYRIGHT_STRING "\0" +#ifdef _DEBUG + VALUE "OriginalFilename", "icuin" U_ICU_VERSION_SHORT "d.dll\0" +#else + VALUE "OriginalFilename", "icuin" U_ICU_VERSION_SHORT ".dll\0" +#endif + VALUE "PrivateBuild", "\0" + VALUE "ProductName", ICU_PRODUCT "\0" + VALUE "ProductVersion", CommaVersionString(U_ICU_VERSION_MAJOR_NUM, U_ICU_VERSION_MINOR_NUM, U_ICU_VERSION_PATCHLEVEL_NUM, U_ICU_VERSION_BUILDLEVEL_NUM) + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x000, 0000 + END +END + +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/deps/icu-small/source/i18n/indiancal.cpp b/deps/icu-small/source/i18n/indiancal.cpp index 935290a5751dc3..626de848a6a512 100644 --- a/deps/icu-small/source/i18n/indiancal.cpp +++ b/deps/icu-small/source/i18n/indiancal.cpp @@ -1,376 +1,379 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - * Copyright (C) 2003-2014, International Business Machines Corporation - * and others. All Rights Reserved. - ****************************************************************************** - * - * File INDIANCAL.CPP - ***************************************************************************** - */ - -#include "indiancal.h" -#include -#if !UCONFIG_NO_FORMATTING - -#include "mutex.h" -#include -#include "gregoimp.h" // Math -#include "astro.h" // CalendarAstronomer -#include "uhash.h" - -// Debugging -#ifdef U_DEBUG_INDIANCAL -#include -#include - -#endif - -U_NAMESPACE_BEGIN - -// Implementation of the IndianCalendar class - -//------------------------------------------------------------------------- -// Constructors... -//------------------------------------------------------------------------- - - -IndianCalendar* IndianCalendar::clone() const { - return new IndianCalendar(*this); -} - -IndianCalendar::IndianCalendar(const Locale& aLocale, UErrorCode& success) - : Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success) -{ - setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. -} - -IndianCalendar::IndianCalendar(const IndianCalendar& other) : Calendar(other) { -} - -IndianCalendar::~IndianCalendar() -{ -} -const char *IndianCalendar::getType() const { - return "indian"; -} - -static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { - // Minimum Greatest Least Maximum - // Minimum Maximum - { 0, 0, 0, 0}, // ERA - { -5000000, -5000000, 5000000, 5000000}, // YEAR - { 0, 0, 11, 11}, // MONTH - { 1, 1, 52, 53}, // WEEK_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH - { 1, 1, 30, 31}, // DAY_OF_MONTH - { 1, 1, 365, 366}, // DAY_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK - { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET - { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL - { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH -}; - -static const int32_t INDIAN_ERA_START = 78; -static const int32_t INDIAN_YEAR_START = 80; - -int32_t IndianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { - return LIMITS[field][limitType]; -} - -/* - * Determine whether the given gregorian year is a Leap year - */ -static UBool isGregorianLeap(int32_t year) -{ - return Grego::isLeapYear(year); -} - -//---------------------------------------------------------------------- -// Calendar framework -//---------------------------------------------------------------------- - -/* - * Return the length (in days) of the given month. - * - * @param eyear The year in Saka Era - * @param month The month(0-based) in Indian calendar - */ -int32_t IndianCalendar::handleGetMonthLength(int32_t eyear, int32_t month) const { - if (month < 0 || month > 11) { - eyear += ClockMath::floorDivide(month, 12, &month); - } - - if (isGregorianLeap(eyear + INDIAN_ERA_START) && month == 0) { - return 31; - } - - if (month >= 1 && month <= 5) { - return 31; - } - - return 30; -} - -/* - * Return the number of days in the given Indian year - * - * @param eyear The year in Saka Era. - */ -int32_t IndianCalendar::handleGetYearLength(int32_t eyear) const { - return isGregorianLeap(eyear + INDIAN_ERA_START) ? 366 : 365; -} -/* - * Returns the Julian Day corresponding to gregorian date - * - * @param year The Gregorian year - * @param month The month in Gregorian Year, 0 based. - * @param date The date in Gregorian day in month - */ -static double gregorianToJD(int32_t year, int32_t month, int32_t date) { - return Grego::fieldsToDay(year, month, date) + kEpochStartAsJulianDay - 0.5; -} - -/* - * Returns the Gregorian Date corresponding to a given Julian Day - * Month is 0 based. - * @param jd The Julian Day - */ -static int32_t* jdToGregorian(double jd, int32_t gregorianDate[3]) { - int32_t gdow; - Grego::dayToFields(jd - kEpochStartAsJulianDay, - gregorianDate[0], gregorianDate[1], gregorianDate[2], gdow); - return gregorianDate; -} - - -//------------------------------------------------------------------------- -// Functions for converting from field values to milliseconds.... -//------------------------------------------------------------------------- -static double IndianToJD(int32_t year, int32_t month, int32_t date) { - int32_t leapMonth, gyear, m; - double start, jd; - - gyear = year + INDIAN_ERA_START; - - - if(isGregorianLeap(gyear)) { - leapMonth = 31; - start = gregorianToJD(gyear, 2 /* The third month in 0 based month */, 21); - } - else { - leapMonth = 30; - start = gregorianToJD(gyear, 2 /* The third month in 0 based month */, 22); - } - - if (month == 1) { - jd = start + (date - 1); - } else { - jd = start + leapMonth; - m = month - 2; - - //m = Math.min(m, 5); - if (m > 5) { - m = 5; - } - - jd += m * 31; - - if (month >= 8) { - m = month - 7; - jd += m * 30; - } - jd += date - 1; - } - - return jd; -} - -/* - * Return JD of start of given month/year of Indian Calendar - * @param eyear The year in Indian Calendar measured from Saka Era (78 AD). - * @param month The month in Indian calendar - */ -int32_t IndianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */ ) const { - - //month is 0 based; converting it to 1-based - int32_t imonth; - - // If the month is out of range, adjust it into range, and adjust the extended year accordingly - if (month < 0 || month > 11) { - eyear += (int32_t)ClockMath::floorDivide(month, 12, &month); - } - - if(month == 12){ - imonth = 1; - } else { - imonth = month + 1; - } - - double jd = IndianToJD(eyear ,imonth, 1); - - return (int32_t)jd; -} - -//------------------------------------------------------------------------- -// Functions for converting from milliseconds to field values -//------------------------------------------------------------------------- - -int32_t IndianCalendar::handleGetExtendedYear() { - int32_t year; - - if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { - year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 - } else { - year = internalGet(UCAL_YEAR, 1); // Default to year 1 - } - - return year; -} - -/* - * Override Calendar to compute several fields specific to the Indian - * calendar system. These are: - * - *
  • ERA - *
  • YEAR - *
  • MONTH - *
  • DAY_OF_MONTH - *
  • EXTENDED_YEAR
- * - * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this - * method is called. The getGregorianXxx() methods return Gregorian - * calendar equivalents for the given Julian day. - */ -void IndianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& /* status */) { - double jdAtStartOfGregYear; - int32_t leapMonth, IndianYear, yday, IndianMonth, IndianDayOfMonth, mday; - int32_t gregorianYear; // Stores gregorian date corresponding to Julian day; - int32_t gd[3]; - - gregorianYear = jdToGregorian(julianDay, gd)[0]; // Gregorian date for Julian day - IndianYear = gregorianYear - INDIAN_ERA_START; // Year in Saka era - jdAtStartOfGregYear = gregorianToJD(gregorianYear, 0, 1); // JD at start of Gregorian year - yday = (int32_t)(julianDay - jdAtStartOfGregYear); // Day number in Gregorian year (starting from 0) - - if (yday < INDIAN_YEAR_START) { - // Day is at the end of the preceding Saka year - IndianYear -= 1; - leapMonth = isGregorianLeap(gregorianYear - 1) ? 31 : 30; // Days in leapMonth this year, previous Gregorian year - yday += leapMonth + (31 * 5) + (30 * 3) + 10; - } else { - leapMonth = isGregorianLeap(gregorianYear) ? 31 : 30; // Days in leapMonth this year - yday -= INDIAN_YEAR_START; - } - - if (yday < leapMonth) { - IndianMonth = 0; - IndianDayOfMonth = yday + 1; - } else { - mday = yday - leapMonth; - if (mday < (31 * 5)) { - IndianMonth = (int32_t)uprv_floor(mday / 31) + 1; - IndianDayOfMonth = (mday % 31) + 1; - } else { - mday -= 31 * 5; - IndianMonth = (int32_t)uprv_floor(mday / 30) + 6; - IndianDayOfMonth = (mday % 30) + 1; - } - } - - internalSet(UCAL_ERA, 0); - internalSet(UCAL_EXTENDED_YEAR, IndianYear); - internalSet(UCAL_YEAR, IndianYear); - internalSet(UCAL_MONTH, IndianMonth); - internalSet(UCAL_DAY_OF_MONTH, IndianDayOfMonth); - internalSet(UCAL_DAY_OF_YEAR, yday + 1); // yday is 0-based -} - -UBool -IndianCalendar::inDaylightTime(UErrorCode& status) const -{ - // copied from GregorianCalendar - if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) { - return false; - } - - // Force an update of the state of the Calendar. - ((IndianCalendar*)this)->complete(status); // cast away const - - return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : false); -} - - -/** - * The system maintains a static default century start date and Year. They are - * initialized the first time they are used. Once the system default century date - * and year are set, they do not change. - */ -static UDate gSystemDefaultCenturyStart = DBL_MIN; -static int32_t gSystemDefaultCenturyStartYear = -1; -static icu::UInitOnce gSystemDefaultCenturyInit {}; - - -UBool IndianCalendar::haveDefaultCentury() const -{ - return true; -} - -static void U_CALLCONV -initializeSystemDefaultCentury() -{ - // initialize systemDefaultCentury and systemDefaultCenturyYear based - // on the current time. They'll be set to 80 years before - // the current time. - UErrorCode status = U_ZERO_ERROR; - - IndianCalendar calendar ( Locale ( "@calendar=Indian" ), status); - if ( U_SUCCESS ( status ) ) { - calendar.setTime ( Calendar::getNow(), status ); - calendar.add ( UCAL_YEAR, -80, status ); - - UDate newStart = calendar.getTime ( status ); - int32_t newYear = calendar.get ( UCAL_YEAR, status ); - - gSystemDefaultCenturyStart = newStart; - gSystemDefaultCenturyStartYear = newYear; - } - // We have no recourse upon failure. -} - - -UDate -IndianCalendar::defaultCenturyStart() const -{ - // lazy-evaluate systemDefaultCenturyStart - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStart; -} - -int32_t -IndianCalendar::defaultCenturyStartYear() const -{ - // lazy-evaluate systemDefaultCenturyStartYear - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStartYear; -} - - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IndianCalendar) - -U_NAMESPACE_END - -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + * Copyright (C) 2003-2014, International Business Machines Corporation + * and others. All Rights Reserved. + ****************************************************************************** + * + * File INDIANCAL.CPP + ***************************************************************************** + */ + +#include "indiancal.h" +#include +#if !UCONFIG_NO_FORMATTING + +#include "mutex.h" +#include +#include "gregoimp.h" // Math +#include "uhash.h" + +// Debugging +#ifdef U_DEBUG_INDIANCAL +#include +#include + +#endif + +U_NAMESPACE_BEGIN + +// Implementation of the IndianCalendar class + +//------------------------------------------------------------------------- +// Constructors... +//------------------------------------------------------------------------- + + +IndianCalendar* IndianCalendar::clone() const { + return new IndianCalendar(*this); +} + +IndianCalendar::IndianCalendar(const Locale& aLocale, UErrorCode& success) + : Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success) +{ + setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. +} + +IndianCalendar::IndianCalendar(const IndianCalendar& other) : Calendar(other) { +} + +IndianCalendar::~IndianCalendar() +{ +} +const char *IndianCalendar::getType() const { + return "indian"; +} + +static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { + // Minimum Greatest Least Maximum + // Minimum Maximum + { 0, 0, 0, 0}, // ERA + { -5000000, -5000000, 5000000, 5000000}, // YEAR + { 0, 0, 11, 11}, // MONTH + { 1, 1, 52, 53}, // WEEK_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH + { 1, 1, 30, 31}, // DAY_OF_MONTH + { 1, 1, 365, 366}, // DAY_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK + { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET + { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL + { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH + { 0, 0, 11, 11}, // ORDINAL_MONTH +}; + +static const int32_t INDIAN_ERA_START = 78; +static const int32_t INDIAN_YEAR_START = 80; + +int32_t IndianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { + return LIMITS[field][limitType]; +} + +/* + * Determine whether the given gregorian year is a Leap year + */ +static UBool isGregorianLeap(int32_t year) +{ + return Grego::isLeapYear(year); +} + +//---------------------------------------------------------------------- +// Calendar framework +//---------------------------------------------------------------------- + +/* + * Return the length (in days) of the given month. + * + * @param eyear The year in Saka Era + * @param month The month(0-based) in Indian calendar + */ +int32_t IndianCalendar::handleGetMonthLength(int32_t eyear, int32_t month) const { + if (month < 0 || month > 11) { + eyear += ClockMath::floorDivide(month, 12, &month); + } + + if (isGregorianLeap(eyear + INDIAN_ERA_START) && month == 0) { + return 31; + } + + if (month >= 1 && month <= 5) { + return 31; + } + + return 30; +} + +/* + * Return the number of days in the given Indian year + * + * @param eyear The year in Saka Era. + */ +int32_t IndianCalendar::handleGetYearLength(int32_t eyear) const { + return isGregorianLeap(eyear + INDIAN_ERA_START) ? 366 : 365; +} +/* + * Returns the Julian Day corresponding to gregorian date + * + * @param year The Gregorian year + * @param month The month in Gregorian Year, 0 based. + * @param date The date in Gregorian day in month + */ +static double gregorianToJD(int32_t year, int32_t month, int32_t date) { + return Grego::fieldsToDay(year, month, date) + kEpochStartAsJulianDay - 0.5; +} + +/* + * Returns the Gregorian Date corresponding to a given Julian Day + * Month is 0 based. + * @param jd The Julian Day + */ +static int32_t* jdToGregorian(double jd, int32_t gregorianDate[3]) { + int32_t gdow; + Grego::dayToFields(jd - kEpochStartAsJulianDay, + gregorianDate[0], gregorianDate[1], gregorianDate[2], gdow); + return gregorianDate; +} + + +//------------------------------------------------------------------------- +// Functions for converting from field values to milliseconds.... +//------------------------------------------------------------------------- +static double IndianToJD(int32_t year, int32_t month, int32_t date) { + int32_t leapMonth, gyear, m; + double start, jd; + + gyear = year + INDIAN_ERA_START; + + + if(isGregorianLeap(gyear)) { + leapMonth = 31; + start = gregorianToJD(gyear, 2 /* The third month in 0 based month */, 21); + } + else { + leapMonth = 30; + start = gregorianToJD(gyear, 2 /* The third month in 0 based month */, 22); + } + + if (month == 1) { + jd = start + (date - 1); + } else { + jd = start + leapMonth; + m = month - 2; + + //m = Math.min(m, 5); + if (m > 5) { + m = 5; + } + + jd += m * 31; + + if (month >= 8) { + m = month - 7; + jd += m * 30; + } + jd += date - 1; + } + + return jd; +} + +/* + * Return JD of start of given month/year of Indian Calendar + * @param eyear The year in Indian Calendar measured from Saka Era (78 AD). + * @param month The month in Indian calendar + */ +int32_t IndianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */ ) const { + + //month is 0 based; converting it to 1-based + int32_t imonth; + + // If the month is out of range, adjust it into range, and adjust the extended year accordingly + if (month < 0 || month > 11) { + eyear += (int32_t)ClockMath::floorDivide(month, 12, &month); + } + + if(month == 12){ + imonth = 1; + } else { + imonth = month + 1; + } + + double jd = IndianToJD(eyear ,imonth, 1); + + return (int32_t)jd; +} + +//------------------------------------------------------------------------- +// Functions for converting from milliseconds to field values +//------------------------------------------------------------------------- + +int32_t IndianCalendar::handleGetExtendedYear() { + int32_t year; + + if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { + year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 + } else { + year = internalGet(UCAL_YEAR, 1); // Default to year 1 + } + + return year; +} + +/* + * Override Calendar to compute several fields specific to the Indian + * calendar system. These are: + * + *
  • ERA + *
  • YEAR + *
  • MONTH + *
  • DAY_OF_MONTH + *
  • EXTENDED_YEAR
+ * + * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this + * method is called. The getGregorianXxx() methods return Gregorian + * calendar equivalents for the given Julian day. + */ +void IndianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& /* status */) { + double jdAtStartOfGregYear; + int32_t leapMonth, IndianYear, yday, IndianMonth, IndianDayOfMonth, mday; + int32_t gregorianYear; // Stores gregorian date corresponding to Julian day; + int32_t gd[3]; + + gregorianYear = jdToGregorian(julianDay, gd)[0]; // Gregorian date for Julian day + IndianYear = gregorianYear - INDIAN_ERA_START; // Year in Saka era + jdAtStartOfGregYear = gregorianToJD(gregorianYear, 0, 1); // JD at start of Gregorian year + yday = (int32_t)(julianDay - jdAtStartOfGregYear); // Day number in Gregorian year (starting from 0) + + if (yday < INDIAN_YEAR_START) { + // Day is at the end of the preceding Saka year + IndianYear -= 1; + leapMonth = isGregorianLeap(gregorianYear - 1) ? 31 : 30; // Days in leapMonth this year, previous Gregorian year + yday += leapMonth + (31 * 5) + (30 * 3) + 10; + } else { + leapMonth = isGregorianLeap(gregorianYear) ? 31 : 30; // Days in leapMonth this year + yday -= INDIAN_YEAR_START; + } + + if (yday < leapMonth) { + IndianMonth = 0; + IndianDayOfMonth = yday + 1; + } else { + mday = yday - leapMonth; + if (mday < (31 * 5)) { + IndianMonth = (int32_t)uprv_floor(mday / 31) + 1; + IndianDayOfMonth = (mday % 31) + 1; + } else { + mday -= 31 * 5; + IndianMonth = (int32_t)uprv_floor(mday / 30) + 6; + IndianDayOfMonth = (mday % 30) + 1; + } + } + + internalSet(UCAL_ERA, 0); + internalSet(UCAL_EXTENDED_YEAR, IndianYear); + internalSet(UCAL_YEAR, IndianYear); + internalSet(UCAL_MONTH, IndianMonth); + internalSet(UCAL_ORDINAL_MONTH, IndianMonth); + internalSet(UCAL_DAY_OF_MONTH, IndianDayOfMonth); + internalSet(UCAL_DAY_OF_YEAR, yday + 1); // yday is 0-based +} + +constexpr uint32_t kIndianRelatedYearDiff = 79; + +int32_t IndianCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kIndianRelatedYearDiff; +} + +void IndianCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kIndianRelatedYearDiff); +} + +/** + * The system maintains a static default century start date and Year. They are + * initialized the first time they are used. Once the system default century date + * and year are set, they do not change. + */ +static UDate gSystemDefaultCenturyStart = DBL_MIN; +static int32_t gSystemDefaultCenturyStartYear = -1; +static icu::UInitOnce gSystemDefaultCenturyInit {}; + + +UBool IndianCalendar::haveDefaultCentury() const +{ + return true; +} + +static void U_CALLCONV +initializeSystemDefaultCentury() +{ + // initialize systemDefaultCentury and systemDefaultCenturyYear based + // on the current time. They'll be set to 80 years before + // the current time. + UErrorCode status = U_ZERO_ERROR; + + IndianCalendar calendar ( Locale ( "@calendar=Indian" ), status); + if ( U_SUCCESS ( status ) ) { + calendar.setTime ( Calendar::getNow(), status ); + calendar.add ( UCAL_YEAR, -80, status ); + + UDate newStart = calendar.getTime ( status ); + int32_t newYear = calendar.get ( UCAL_YEAR, status ); + + gSystemDefaultCenturyStart = newStart; + gSystemDefaultCenturyStartYear = newYear; + } + // We have no recourse upon failure. +} + + +UDate +IndianCalendar::defaultCenturyStart() const +{ + // lazy-evaluate systemDefaultCenturyStart + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStart; +} + +int32_t +IndianCalendar::defaultCenturyStartYear() const +{ + // lazy-evaluate systemDefaultCenturyStartYear + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStartYear; +} + + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IndianCalendar) + +U_NAMESPACE_END + +#endif + diff --git a/deps/icu-small/source/i18n/indiancal.h b/deps/icu-small/source/i18n/indiancal.h index bfbea0032954cd..f94c34c876e37a 100644 --- a/deps/icu-small/source/i18n/indiancal.h +++ b/deps/icu-small/source/i18n/indiancal.h @@ -1,331 +1,333 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ***************************************************************************** - * Copyright (C) 2003-2008, International Business Machines Corporation - * and others. All Rights Reserved. - ***************************************************************************** - * - * File INDIANCAL.H - ***************************************************************************** - */ - -#ifndef INDIANCAL_H -#define INDIANCAL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/calendar.h" - -U_NAMESPACE_BEGIN - -/** - * Concrete class which provides the Indian calendar. - *

- * IndianCalendar is a subclass of Calendar - * that numbers years since the beginning of SAKA ERA. This is the civil calendar - * which is accepted by government of India as Indian National Calendar. - * The two calendars most widely used in India today are the Vikrama calendar - * followed in North India and the Shalivahana or Saka calendar which is followed - * in South India and Maharashtra. - - * A variant of the Shalivahana Calendar was reformed and standardized as the - * Indian National calendar in 1957. - *

- * Some details of Indian National Calendar (to be implemented) : - * The Months - * Month Length Start date (Gregorian) - * ================================================= - * 1 Chaitra 30/31 March 22* - * 2 Vaisakha 31 April 21 - * 3 Jyaistha 31 May 22 - * 4 Asadha 31 June 22 - * 5 Sravana 31 July 23 - * 6 Bhadra 31 August 23 - * 7 Asvina 30 September 23 - * 8 Kartika 30 October 23 - * 9 Agrahayana 30 November 22 - * 10 Pausa 30 December 22 - * 11 Magha 30 January 21 - * 12 Phalguna 30 February 20 - - * In leap years, Chaitra has 31 days and starts on March 21 instead. - * The leap years of Gregorian calendar and Indian National Calendar are in synchornization. - * So When its a leap year in Gregorian calendar then Chaitra has 31 days. - * - * The Years - * Years are counted in the Saka Era, which starts its year 0 in 78AD (by gregorian calendar). - * So for eg. 9th June 2006 by Gregorian Calendar, is same as 19th of Jyaistha in 1928 of Saka - * era by Indian National Calendar. - *

- * The Indian Calendar has only one allowable era: Saka Era. If the - * calendar is not in lenient mode (see setLenient), dates before - * 1/1/1 Saka Era are rejected with an IllegalArgumentException. - *

- * @internal - */ - - -class U_I18N_API IndianCalendar : public Calendar { -public: - /** - * Useful constants for IndianCalendar. - * @internal - */ - enum EEras { - /** - * Constant for Chaitra, the 1st month of the Indian year. - */ - CHAITRA, - - /** - * Constant for Vaisakha, the 2nd month of the Indian year. - */ - VAISAKHA, - - /** - * Constant for Jyaistha, the 3rd month of the Indian year. - */ - JYAISTHA, - - /** - * Constant for Asadha, the 4th month of the Indian year. - */ - ASADHA, - - /** - * Constant for Sravana, the 5th month of the Indian year. - */ - SRAVANA, - - /** - * Constant for Bhadra the 6th month of the Indian year - */ - BHADRA, - - /** - * Constant for the Asvina, the 7th month of the Indian year. - */ - ASVINA, - - /** - * Constant for Kartika, the 8th month of the Indian year. - */ - KARTIKA, - - /** - * Constant for Agrahayana, the 9th month of the Indian year. - */ - AGRAHAYANA, - - /** - * Constant for Pausa, the 10th month of the Indian year. - */ - PAUSA, - - /** - * Constant for Magha, the 11th month of the Indian year. - */ - MAGHA, - - /** - * Constant for Phalguna, the 12th month of the Indian year. - */ - PHALGUNA - }; - - //------------------------------------------------------------------------- - // Constructors... - //------------------------------------------------------------------------- - - /** - * Constructs an IndianCalendar based on the current time in the default time zone - * with the given locale. - * - * @param aLocale The given locale. - * @param success Indicates the status of IndianCalendar object construction. - * Returns U_ZERO_ERROR if constructed successfully. - * @param beCivil Whether the calendar should be civil (default-true) or religious (false) - * @internal - */ - IndianCalendar(const Locale& aLocale, UErrorCode &success); - - /** - * Copy Constructor - * @internal - */ - IndianCalendar(const IndianCalendar& other); - - /** - * Destructor. - * @internal - */ - virtual ~IndianCalendar(); - - /** - * Determines whether this object uses the fixed-cycle Indian civil calendar - * or an approximation of the religious, astronomical calendar. - * - * @param beCivil CIVIL to use the civil calendar, - * ASTRONOMICAL to use the astronomical calendar. - * @internal - */ - //void setCivil(ECivil beCivil, UErrorCode &status); - - /** - * Returns true if this object is using the fixed-cycle civil - * calendar, or false if using the religious, astronomical - * calendar. - * @internal - */ - //UBool isCivil(); - - - // TODO: copy c'tor, etc - - // clone - virtual IndianCalendar* clone() const override; - - private: - /** - * Determine whether a year is the gregorian year a leap year - */ - //static UBool isGregorianLeap(int32_t year); - //---------------------------------------------------------------------- - // Calendar framework - //---------------------------------------------------------------------- - protected: - /** - * @internal - */ - virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; - - /** - * Return the length (in days) of the given month. - * - * @param year The year in Saka era - * @param year The month(0-based) in Indian year - * @internal - */ - virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; - - /** - * Return the number of days in the given Indian year - * @internal - */ - virtual int32_t handleGetYearLength(int32_t extendedYear) const override; - - //------------------------------------------------------------------------- - // Functions for converting from field values to milliseconds.... - //------------------------------------------------------------------------- - - // Return JD of start of given month/year - /** - * @internal - */ - virtual int32_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override; - - //------------------------------------------------------------------------- - // Functions for converting from milliseconds to field values - //------------------------------------------------------------------------- - - /** - * @internal - */ - virtual int32_t handleGetExtendedYear() override; - - /** - * Override Calendar to compute several fields specific to the Indian - * calendar system. These are: - * - *

  • ERA - *
  • YEAR - *
  • MONTH - *
  • DAY_OF_MONTH - *
  • DAY_OF_YEAR - *
  • EXTENDED_YEAR
- * - * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this - * method is called. The getGregorianXxx() methods return Gregorian - * calendar equivalents for the given Julian day. - * @internal - */ - virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; - - // UObject stuff - public: - /** - * @return The class ID for this object. All objects of a given class have the - * same class ID. Objects of other classes have different class IDs. - * @internal - */ - virtual UClassID getDynamicClassID(void) const override; - - /** - * Return the class ID for this class. This is useful only for comparing to a return - * value from getDynamicClassID(). For example: - * - * Base* polymorphic_pointer = createPolymorphicObject(); - * if (polymorphic_pointer->getDynamicClassID() == - * Derived::getStaticClassID()) ... - * - * @return The class ID for all objects of this class. - * @internal - */ - static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * return the calendar type, "indian". - * - * @return calendar type - * @internal - */ - virtual const char * getType() const override; - -private: - IndianCalendar() = delete; // default constructor not implemented - - // Default century. -protected: - - /** - * (Overrides Calendar) Return true if the current date for this Calendar is in - * Daylight Savings Time. Recognizes DST_OFFSET, if it is set. - * - * @param status Fill-in parameter which receives the status of this operation. - * @return True if the current date for this Calendar is in Daylight Savings Time, - * false, otherwise. - * @internal - */ - virtual UBool inDaylightTime(UErrorCode& status) const override; - - - /** - * Returns true because the Indian Calendar does have a default century - * @internal - */ - virtual UBool haveDefaultCentury() const override; - - /** - * Returns the date of the start of the default century - * @return start of century - in milliseconds since epoch, 1970 - * @internal - */ - virtual UDate defaultCenturyStart() const override; - - /** - * Returns the year in which the default century begins - * @internal - */ - virtual int32_t defaultCenturyStartYear() const override; -}; - -U_NAMESPACE_END - -#endif -#endif - - - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ***************************************************************************** + * Copyright (C) 2003-2008, International Business Machines Corporation + * and others. All Rights Reserved. + ***************************************************************************** + * + * File INDIANCAL.H + ***************************************************************************** + */ + +#ifndef INDIANCAL_H +#define INDIANCAL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" + +U_NAMESPACE_BEGIN + +/** + * Concrete class which provides the Indian calendar. + *

+ * IndianCalendar is a subclass of Calendar + * that numbers years since the beginning of SAKA ERA. This is the civil calendar + * which is accepted by government of India as Indian National Calendar. + * The two calendars most widely used in India today are the Vikrama calendar + * followed in North India and the Shalivahana or Saka calendar which is followed + * in South India and Maharashtra. + + * A variant of the Shalivahana Calendar was reformed and standardized as the + * Indian National calendar in 1957. + *

+ * Some details of Indian National Calendar (to be implemented) : + * The Months + * Month Length Start date (Gregorian) + * ================================================= + * 1 Chaitra 30/31 March 22* + * 2 Vaisakha 31 April 21 + * 3 Jyaistha 31 May 22 + * 4 Asadha 31 June 22 + * 5 Sravana 31 July 23 + * 6 Bhadra 31 August 23 + * 7 Asvina 30 September 23 + * 8 Kartika 30 October 23 + * 9 Agrahayana 30 November 22 + * 10 Pausa 30 December 22 + * 11 Magha 30 January 21 + * 12 Phalguna 30 February 20 + + * In leap years, Chaitra has 31 days and starts on March 21 instead. + * The leap years of Gregorian calendar and Indian National Calendar are in synchornization. + * So When its a leap year in Gregorian calendar then Chaitra has 31 days. + * + * The Years + * Years are counted in the Saka Era, which starts its year 0 in 78AD (by gregorian calendar). + * So for eg. 9th June 2006 by Gregorian Calendar, is same as 19th of Jyaistha in 1928 of Saka + * era by Indian National Calendar. + *

+ * The Indian Calendar has only one allowable era: Saka Era. If the + * calendar is not in lenient mode (see setLenient), dates before + * 1/1/1 Saka Era are rejected with an IllegalArgumentException. + *

+ * @internal + */ + + +class U_I18N_API IndianCalendar : public Calendar { +public: + /** + * Useful constants for IndianCalendar. + * @internal + */ + enum EEras { + /** + * Constant for Chaitra, the 1st month of the Indian year. + */ + CHAITRA, + + /** + * Constant for Vaisakha, the 2nd month of the Indian year. + */ + VAISAKHA, + + /** + * Constant for Jyaistha, the 3rd month of the Indian year. + */ + JYAISTHA, + + /** + * Constant for Asadha, the 4th month of the Indian year. + */ + ASADHA, + + /** + * Constant for Sravana, the 5th month of the Indian year. + */ + SRAVANA, + + /** + * Constant for Bhadra the 6th month of the Indian year + */ + BHADRA, + + /** + * Constant for the Asvina, the 7th month of the Indian year. + */ + ASVINA, + + /** + * Constant for Kartika, the 8th month of the Indian year. + */ + KARTIKA, + + /** + * Constant for Agrahayana, the 9th month of the Indian year. + */ + AGRAHAYANA, + + /** + * Constant for Pausa, the 10th month of the Indian year. + */ + PAUSA, + + /** + * Constant for Magha, the 11th month of the Indian year. + */ + MAGHA, + + /** + * Constant for Phalguna, the 12th month of the Indian year. + */ + PHALGUNA + }; + + //------------------------------------------------------------------------- + // Constructors... + //------------------------------------------------------------------------- + + /** + * Constructs an IndianCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of IndianCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @param beCivil Whether the calendar should be civil (default-true) or religious (false) + * @internal + */ + IndianCalendar(const Locale& aLocale, UErrorCode &success); + + /** + * Copy Constructor + * @internal + */ + IndianCalendar(const IndianCalendar& other); + + /** + * Destructor. + * @internal + */ + virtual ~IndianCalendar(); + + /** + * Determines whether this object uses the fixed-cycle Indian civil calendar + * or an approximation of the religious, astronomical calendar. + * + * @param beCivil CIVIL to use the civil calendar, + * ASTRONOMICAL to use the astronomical calendar. + * @internal + */ + //void setCivil(ECivil beCivil, UErrorCode &status); + + /** + * Returns true if this object is using the fixed-cycle civil + * calendar, or false if using the religious, astronomical + * calendar. + * @internal + */ + //UBool isCivil(); + + + // TODO: copy c'tor, etc + + // clone + virtual IndianCalendar* clone() const override; + + private: + /** + * Determine whether a year is the gregorian year a leap year + */ + //static UBool isGregorianLeap(int32_t year); + //---------------------------------------------------------------------- + // Calendar framework + //---------------------------------------------------------------------- + protected: + /** + * @internal + */ + virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; + + /** + * Return the length (in days) of the given month. + * + * @param year The year in Saka era + * @param year The month(0-based) in Indian year + * @internal + */ + virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; + + /** + * Return the number of days in the given Indian year + * @internal + */ + virtual int32_t handleGetYearLength(int32_t extendedYear) const override; + + //------------------------------------------------------------------------- + // Functions for converting from field values to milliseconds.... + //------------------------------------------------------------------------- + + // Return JD of start of given month/year + /** + * @internal + */ + virtual int32_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override; + + //------------------------------------------------------------------------- + // Functions for converting from milliseconds to field values + //------------------------------------------------------------------------- + + /** + * @internal + */ + virtual int32_t handleGetExtendedYear() override; + + /** + * Override Calendar to compute several fields specific to the Indian + * calendar system. These are: + * + *

  • ERA + *
  • YEAR + *
  • MONTH + *
  • DAY_OF_MONTH + *
  • DAY_OF_YEAR + *
  • EXTENDED_YEAR
+ * + * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this + * method is called. The getGregorianXxx() methods return Gregorian + * calendar equivalents for the given Julian day. + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; + + // UObject stuff + public: + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * return the calendar type, "indian". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + /** + * @return The related Gregorian year; will be obtained by modifying the value + * obtained by get from UCAL_EXTENDED_YEAR field + * @internal + */ + virtual int32_t getRelatedYear(UErrorCode &status) const override; + + /** + * @param year The related Gregorian year to set; will be modified as necessary then + * set in UCAL_EXTENDED_YEAR field + * @internal + */ + virtual void setRelatedYear(int32_t year) override; + + +private: + IndianCalendar() = delete; // default constructor not implemented + + // Default century. +protected: + /** + * Returns true because the Indian Calendar does have a default century + * @internal + */ + virtual UBool haveDefaultCentury() const override; + + /** + * Returns the date of the start of the default century + * @return start of century - in milliseconds since epoch, 1970 + * @internal + */ + virtual UDate defaultCenturyStart() const override; + + /** + * Returns the year in which the default century begins + * @internal + */ + virtual int32_t defaultCenturyStartYear() const override; +}; + +U_NAMESPACE_END + +#endif +#endif + + + diff --git a/deps/icu-small/source/i18n/inputext.cpp b/deps/icu-small/source/i18n/inputext.cpp index 7c78ad249a4ba6..c76662d23344cb 100644 --- a/deps/icu-small/source/i18n/inputext.cpp +++ b/deps/icu-small/source/i18n/inputext.cpp @@ -1,164 +1,164 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2005-2016, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_CONVERSION - -#include "inputext.h" - -#include "cmemory.h" -#include "cstring.h" - -#include - -U_NAMESPACE_BEGIN - -#define BUFFER_SIZE 8192 - -#define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type)) -#define DELETE_ARRAY(array) uprv_free((void *) (array)) - -InputText::InputText(UErrorCode &status) - : fInputBytes(NEW_ARRAY(uint8_t, BUFFER_SIZE)), // The text to be checked. Markup will have been - // removed if appropriate. - fByteStats(NEW_ARRAY(int16_t, 256)), // byte frequency statistics for the input text. - // Value is percent, not absolute. - fDeclaredEncoding(0), - fRawInput(0), - fRawLength(0) -{ - if (fInputBytes == NULL || fByteStats == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } -} - -InputText::~InputText() -{ - DELETE_ARRAY(fDeclaredEncoding); - DELETE_ARRAY(fByteStats); - DELETE_ARRAY(fInputBytes); -} - -void InputText::setText(const char *in, int32_t len) -{ - fInputLen = 0; - fC1Bytes = false; - fRawInput = (const uint8_t *) in; - fRawLength = len == -1? (int32_t)uprv_strlen(in) : len; -} - -void InputText::setDeclaredEncoding(const char* encoding, int32_t len) -{ - if(encoding) { - if (len == -1) { - len = (int32_t)uprv_strlen(encoding); - } - - len += 1; // to make place for the \0 at the end. - uprv_free(fDeclaredEncoding); - fDeclaredEncoding = NEW_ARRAY(char, len); - uprv_strncpy(fDeclaredEncoding, encoding, len); - } -} - -UBool InputText::isSet() const -{ - return fRawInput != NULL; -} - -/** -* MungeInput - after getting a set of raw input data to be analyzed, preprocess -* it by removing what appears to be html markup. -* -* @internal -*/ -void InputText::MungeInput(UBool fStripTags) { - int srci = 0; - int dsti = 0; - uint8_t b; - bool inMarkup = false; - int32_t openTags = 0; - int32_t badTags = 0; - - // - // html / xml markup stripping. - // quick and dirty, not 100% accurate, but hopefully good enough, statistically. - // discard everything within < brackets > - // Count how many total '<' and illegal (nested) '<' occur, so we can make some - // guess as to whether the input was actually marked up at all. - // TODO: Think about how this interacts with EBCDIC charsets that are detected. - if (fStripTags) { - for (srci = 0; srci < fRawLength && dsti < BUFFER_SIZE; srci += 1) { - b = fRawInput[srci]; - - if (b == (uint8_t)0x3C) { /* Check for the ASCII '<' */ - if (inMarkup) { - badTags += 1; - } - - inMarkup = true; - openTags += 1; - } - - if (! inMarkup) { - fInputBytes[dsti++] = b; - } - - if (b == (uint8_t)0x3E) { /* Check for the ASCII '>' */ - inMarkup = false; - } - } - - fInputLen = dsti; - } - - // - // If it looks like this input wasn't marked up, or if it looks like it's - // essentially nothing but markup abandon the markup stripping. - // Detection will have to work on the unstripped input. - // - if (openTags<5 || openTags/5 < badTags || - (fInputLen < 100 && fRawLength>600)) - { - int32_t limit = fRawLength; - - if (limit > BUFFER_SIZE) { - limit = BUFFER_SIZE; - } - - for (srci=0; srci + +U_NAMESPACE_BEGIN + +#define BUFFER_SIZE 8192 + +#define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type)) +#define DELETE_ARRAY(array) uprv_free((void *) (array)) + +InputText::InputText(UErrorCode &status) + : fInputBytes(NEW_ARRAY(uint8_t, BUFFER_SIZE)), // The text to be checked. Markup will have been + // removed if appropriate. + fByteStats(NEW_ARRAY(int16_t, 256)), // byte frequency statistics for the input text. + // Value is percent, not absolute. + fDeclaredEncoding(0), + fRawInput(0), + fRawLength(0) +{ + if (fInputBytes == nullptr || fByteStats == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } +} + +InputText::~InputText() +{ + DELETE_ARRAY(fDeclaredEncoding); + DELETE_ARRAY(fByteStats); + DELETE_ARRAY(fInputBytes); +} + +void InputText::setText(const char *in, int32_t len) +{ + fInputLen = 0; + fC1Bytes = false; + fRawInput = (const uint8_t *) in; + fRawLength = len == -1? (int32_t)uprv_strlen(in) : len; +} + +void InputText::setDeclaredEncoding(const char* encoding, int32_t len) +{ + if(encoding) { + if (len == -1) { + len = (int32_t)uprv_strlen(encoding); + } + + len += 1; // to make place for the \0 at the end. + uprv_free(fDeclaredEncoding); + fDeclaredEncoding = NEW_ARRAY(char, len); + uprv_strncpy(fDeclaredEncoding, encoding, len); + } +} + +UBool InputText::isSet() const +{ + return fRawInput != nullptr; +} + +/** +* MungeInput - after getting a set of raw input data to be analyzed, preprocess +* it by removing what appears to be html markup. +* +* @internal +*/ +void InputText::MungeInput(UBool fStripTags) { + int srci = 0; + int dsti = 0; + uint8_t b; + bool inMarkup = false; + int32_t openTags = 0; + int32_t badTags = 0; + + // + // html / xml markup stripping. + // quick and dirty, not 100% accurate, but hopefully good enough, statistically. + // discard everything within < brackets > + // Count how many total '<' and illegal (nested) '<' occur, so we can make some + // guess as to whether the input was actually marked up at all. + // TODO: Think about how this interacts with EBCDIC charsets that are detected. + if (fStripTags) { + for (srci = 0; srci < fRawLength && dsti < BUFFER_SIZE; srci += 1) { + b = fRawInput[srci]; + + if (b == (uint8_t)0x3C) { /* Check for the ASCII '<' */ + if (inMarkup) { + badTags += 1; + } + + inMarkup = true; + openTags += 1; + } + + if (! inMarkup) { + fInputBytes[dsti++] = b; + } + + if (b == (uint8_t)0x3E) { /* Check for the ASCII '>' */ + inMarkup = false; + } + } + + fInputLen = dsti; + } + + // + // If it looks like this input wasn't marked up, or if it looks like it's + // essentially nothing but markup abandon the markup stripping. + // Detection will have to work on the unstripped input. + // + if (openTags<5 || openTags/5 < badTags || + (fInputLen < 100 && fRawLength>600)) + { + int32_t limit = fRawLength; + + if (limit > BUFFER_SIZE) { + limit = BUFFER_SIZE; + } + + for (srci=0; srci -#include "gregoimp.h" // Math -#include "astro.h" // CalendarAstronomer -#include "uhash.h" -#include "ucln_in.h" -#include "uassert.h" - -static const UDate HIJRA_MILLIS = -42521587200000.0; // 7/16/622 AD 00:00 - -// Debugging -#ifdef U_DEBUG_ISLAMCAL -# include -# include -static void debug_islamcal_loc(const char *f, int32_t l) -{ - fprintf(stderr, "%s:%d: ", f, l); -} - -static void debug_islamcal_msg(const char *pat, ...) -{ - va_list ap; - va_start(ap, pat); - vfprintf(stderr, pat, ap); - fflush(stderr); -} -// must use double parens, i.e.: U_DEBUG_ISLAMCAL_MSG(("four is: %d",4)); -#define U_DEBUG_ISLAMCAL_MSG(x) {debug_islamcal_loc(__FILE__,__LINE__);debug_islamcal_msg x;} -#else -#define U_DEBUG_ISLAMCAL_MSG(x) -#endif - - -// --- The cache -- -// cache of months -static icu::CalendarCache *gMonthCache = NULL; -static icu::CalendarAstronomer *gIslamicCalendarAstro = NULL; - -U_CDECL_BEGIN -static UBool calendar_islamic_cleanup(void) { - if (gMonthCache) { - delete gMonthCache; - gMonthCache = NULL; - } - if (gIslamicCalendarAstro) { - delete gIslamicCalendarAstro; - gIslamicCalendarAstro = NULL; - } - return true; -} -U_CDECL_END - -U_NAMESPACE_BEGIN - -// Implementation of the IslamicCalendar class - -/** - * Friday EPOC - */ -static const int32_t CIVIL_EPOC = 1948440; // CE 622 July 16 Friday (Julian calendar) / CE 622 July 19 (Gregorian calendar) - -/** - * Thursday EPOC - */ -static const int32_t ASTRONOMICAL_EPOC = 1948439; // CE 622 July 15 Thursday (Julian calendar) - - -static const int32_t UMALQURA_YEAR_START = 1300; -static const int32_t UMALQURA_YEAR_END = 1600; - -static const int UMALQURA_MONTHLENGTH[] = { - //* 1300 -1302 */ "1010 1010 1010", "1101 0101 0100", "1110 1100 1001", - 0x0AAA, 0x0D54, 0x0EC9, - //* 1303 -1307 */ "0110 1101 0100", "0110 1110 1010", "0011 0110 1100", "1010 1010 1101", "0101 0101 0101", - 0x06D4, 0x06EA, 0x036C, 0x0AAD, 0x0555, - //* 1308 -1312 */ "0110 1010 1001", "0111 1001 0010", "1011 1010 1001", "0101 1101 0100", "1010 1101 1010", - 0x06A9, 0x0792, 0x0BA9, 0x05D4, 0x0ADA, - //* 1313 -1317 */ "0101 0101 1100", "1101 0010 1101", "0110 1001 0101", "0111 0100 1010", "1011 0101 0100", - 0x055C, 0x0D2D, 0x0695, 0x074A, 0x0B54, - //* 1318 -1322 */ "1011 0110 1010", "0101 1010 1101", "0100 1010 1110", "1010 0100 1111", "0101 0001 0111", - 0x0B6A, 0x05AD, 0x04AE, 0x0A4F, 0x0517, - //* 1323 -1327 */ "0110 1000 1011", "0110 1010 0101", "1010 1101 0101", "0010 1101 0110", "1001 0101 1011", - 0x068B, 0x06A5, 0x0AD5, 0x02D6, 0x095B, - //* 1328 -1332 */ "0100 1001 1101", "1010 0100 1101", "1101 0010 0110", "1101 1001 0101", "0101 1010 1100", - 0x049D, 0x0A4D, 0x0D26, 0x0D95, 0x05AC, - //* 1333 -1337 */ "1001 1011 0110", "0010 1011 1010", "1010 0101 1011", "0101 0010 1011", "1010 1001 0101", - 0x09B6, 0x02BA, 0x0A5B, 0x052B, 0x0A95, - //* 1338 -1342 */ "0110 1100 1010", "1010 1110 1001", "0010 1111 0100", "1001 0111 0110", "0010 1011 0110", - 0x06CA, 0x0AE9, 0x02F4, 0x0976, 0x02B6, - //* 1343 -1347 */ "1001 0101 0110", "1010 1100 1010", "1011 1010 0100", "1011 1101 0010", "0101 1101 1001", - 0x0956, 0x0ACA, 0x0BA4, 0x0BD2, 0x05D9, - //* 1348 -1352 */ "0010 1101 1100", "1001 0110 1101", "0101 0100 1101", "1010 1010 0101", "1011 0101 0010", - 0x02DC, 0x096D, 0x054D, 0x0AA5, 0x0B52, - //* 1353 -1357 */ "1011 1010 0101", "0101 1011 0100", "1001 1011 0110", "0101 0101 0111", "0010 1001 0111", - 0x0BA5, 0x05B4, 0x09B6, 0x0557, 0x0297, - //* 1358 -1362 */ "0101 0100 1011", "0110 1010 0011", "0111 0101 0010", "1011 0110 0101", "0101 0110 1010", - 0x054B, 0x06A3, 0x0752, 0x0B65, 0x056A, - //* 1363 -1367 */ "1010 1010 1011", "0101 0010 1011", "1100 1001 0101", "1101 0100 1010", "1101 1010 0101", - 0x0AAB, 0x052B, 0x0C95, 0x0D4A, 0x0DA5, - //* 1368 -1372 */ "0101 1100 1010", "1010 1101 0110", "1001 0101 0111", "0100 1010 1011", "1001 0100 1011", - 0x05CA, 0x0AD6, 0x0957, 0x04AB, 0x094B, - //* 1373 -1377 */ "1010 1010 0101", "1011 0101 0010", "1011 0110 1010", "0101 0111 0101", "0010 0111 0110", - 0x0AA5, 0x0B52, 0x0B6A, 0x0575, 0x0276, - //* 1378 -1382 */ "1000 1011 0111", "0100 0101 1011", "0101 0101 0101", "0101 1010 1001", "0101 1011 0100", - 0x08B7, 0x045B, 0x0555, 0x05A9, 0x05B4, - //* 1383 -1387 */ "1001 1101 1010", "0100 1101 1101", "0010 0110 1110", "1001 0011 0110", "1010 1010 1010", - 0x09DA, 0x04DD, 0x026E, 0x0936, 0x0AAA, - //* 1388 -1392 */ "1101 0101 0100", "1101 1011 0010", "0101 1101 0101", "0010 1101 1010", "1001 0101 1011", - 0x0D54, 0x0DB2, 0x05D5, 0x02DA, 0x095B, - //* 1393 -1397 */ "0100 1010 1011", "1010 0101 0101", "1011 0100 1001", "1011 0110 0100", "1011 0111 0001", - 0x04AB, 0x0A55, 0x0B49, 0x0B64, 0x0B71, - //* 1398 -1402 */ "0101 1011 0100", "1010 1011 0101", "1010 0101 0101", "1101 0010 0101", "1110 1001 0010", - 0x05B4, 0x0AB5, 0x0A55, 0x0D25, 0x0E92, - //* 1403 -1407 */ "1110 1100 1001", "0110 1101 0100", "1010 1110 1001", "1001 0110 1011", "0100 1010 1011", - 0x0EC9, 0x06D4, 0x0AE9, 0x096B, 0x04AB, - //* 1408 -1412 */ "1010 1001 0011", "1101 0100 1001", "1101 1010 0100", "1101 1011 0010", "1010 1011 1001", - 0x0A93, 0x0D49, 0x0DA4, 0x0DB2, 0x0AB9, - //* 1413 -1417 */ "0100 1011 1010", "1010 0101 1011", "0101 0010 1011", "1010 1001 0101", "1011 0010 1010", - 0x04BA, 0x0A5B, 0x052B, 0x0A95, 0x0B2A, - //* 1418 -1422 */ "1011 0101 0101", "0101 0101 1100", "0100 1011 1101", "0010 0011 1101", "1001 0001 1101", - 0x0B55, 0x055C, 0x04BD, 0x023D, 0x091D, - //* 1423 -1427 */ "1010 1001 0101", "1011 0100 1010", "1011 0101 1010", "0101 0110 1101", "0010 1011 0110", - 0x0A95, 0x0B4A, 0x0B5A, 0x056D, 0x02B6, - //* 1428 -1432 */ "1001 0011 1011", "0100 1001 1011", "0110 0101 0101", "0110 1010 1001", "0111 0101 0100", - 0x093B, 0x049B, 0x0655, 0x06A9, 0x0754, - //* 1433 -1437 */ "1011 0110 1010", "0101 0110 1100", "1010 1010 1101", "0101 0101 0101", "1011 0010 1001", - 0x0B6A, 0x056C, 0x0AAD, 0x0555, 0x0B29, - //* 1438 -1442 */ "1011 1001 0010", "1011 1010 1001", "0101 1101 0100", "1010 1101 1010", "0101 0101 1010", - 0x0B92, 0x0BA9, 0x05D4, 0x0ADA, 0x055A, - //* 1443 -1447 */ "1010 1010 1011", "0101 1001 0101", "0111 0100 1001", "0111 0110 0100", "1011 1010 1010", - 0x0AAB, 0x0595, 0x0749, 0x0764, 0x0BAA, - //* 1448 -1452 */ "0101 1011 0101", "0010 1011 0110", "1010 0101 0110", "1110 0100 1101", "1011 0010 0101", - 0x05B5, 0x02B6, 0x0A56, 0x0E4D, 0x0B25, - //* 1453 -1457 */ "1011 0101 0010", "1011 0110 1010", "0101 1010 1101", "0010 1010 1110", "1001 0010 1111", - 0x0B52, 0x0B6A, 0x05AD, 0x02AE, 0x092F, - //* 1458 -1462 */ "0100 1001 0111", "0110 0100 1011", "0110 1010 0101", "0110 1010 1100", "1010 1101 0110", - 0x0497, 0x064B, 0x06A5, 0x06AC, 0x0AD6, - //* 1463 -1467 */ "0101 0101 1101", "0100 1001 1101", "1010 0100 1101", "1101 0001 0110", "1101 1001 0101", - 0x055D, 0x049D, 0x0A4D, 0x0D16, 0x0D95, - //* 1468 -1472 */ "0101 1010 1010", "0101 1011 0101", "0010 1101 1010", "1001 0101 1011", "0100 1010 1101", - 0x05AA, 0x05B5, 0x02DA, 0x095B, 0x04AD, - //* 1473 -1477 */ "0101 1001 0101", "0110 1100 1010", "0110 1110 0100", "1010 1110 1010", "0100 1111 0101", - 0x0595, 0x06CA, 0x06E4, 0x0AEA, 0x04F5, - //* 1478 -1482 */ "0010 1011 0110", "1001 0101 0110", "1010 1010 1010", "1011 0101 0100", "1011 1101 0010", - 0x02B6, 0x0956, 0x0AAA, 0x0B54, 0x0BD2, - //* 1483 -1487 */ "0101 1101 1001", "0010 1110 1010", "1001 0110 1101", "0100 1010 1101", "1010 1001 0101", - 0x05D9, 0x02EA, 0x096D, 0x04AD, 0x0A95, - //* 1488 -1492 */ "1011 0100 1010", "1011 1010 0101", "0101 1011 0010", "1001 1011 0101", "0100 1101 0110", - 0x0B4A, 0x0BA5, 0x05B2, 0x09B5, 0x04D6, - //* 1493 -1497 */ "1010 1001 0111", "0101 0100 0111", "0110 1001 0011", "0111 0100 1001", "1011 0101 0101", - 0x0A97, 0x0547, 0x0693, 0x0749, 0x0B55, - //* 1498 -1508 */ "0101 0110 1010", "1010 0110 1011", "0101 0010 1011", "1010 1000 1011", "1101 0100 0110", "1101 1010 0011", "0101 1100 1010", "1010 1101 0110", "0100 1101 1011", "0010 0110 1011", "1001 0100 1011", - 0x056A, 0x0A6B, 0x052B, 0x0A8B, 0x0D46, 0x0DA3, 0x05CA, 0x0AD6, 0x04DB, 0x026B, 0x094B, - //* 1509 -1519 */ "1010 1010 0101", "1011 0101 0010", "1011 0110 1001", "0101 0111 0101", "0001 0111 0110", "1000 1011 0111", "0010 0101 1011", "0101 0010 1011", "0101 0110 0101", "0101 1011 0100", "1001 1101 1010", - 0x0AA5, 0x0B52, 0x0B69, 0x0575, 0x0176, 0x08B7, 0x025B, 0x052B, 0x0565, 0x05B4, 0x09DA, - //* 1520 -1530 */ "0100 1110 1101", "0001 0110 1101", "1000 1011 0110", "1010 1010 0110", "1101 0101 0010", "1101 1010 1001", "0101 1101 0100", "1010 1101 1010", "1001 0101 1011", "0100 1010 1011", "0110 0101 0011", - 0x04ED, 0x016D, 0x08B6, 0x0AA6, 0x0D52, 0x0DA9, 0x05D4, 0x0ADA, 0x095B, 0x04AB, 0x0653, - //* 1531 -1541 */ "0111 0010 1001", "0111 0110 0010", "1011 1010 1001", "0101 1011 0010", "1010 1011 0101", "0101 0101 0101", "1011 0010 0101", "1101 1001 0010", "1110 1100 1001", "0110 1101 0010", "1010 1110 1001", - 0x0729, 0x0762, 0x0BA9, 0x05B2, 0x0AB5, 0x0555, 0x0B25, 0x0D92, 0x0EC9, 0x06D2, 0x0AE9, - //* 1542 -1552 */ "0101 0110 1011", "0100 1010 1011", "1010 0101 0101", "1101 0010 1001", "1101 0101 0100", "1101 1010 1010", "1001 1011 0101", "0100 1011 1010", "1010 0011 1011", "0100 1001 1011", "1010 0100 1101", - 0x056B, 0x04AB, 0x0A55, 0x0D29, 0x0D54, 0x0DAA, 0x09B5, 0x04BA, 0x0A3B, 0x049B, 0x0A4D, - //* 1553 -1563 */ "1010 1010 1010", "1010 1101 0101", "0010 1101 1010", "1001 0101 1101", "0100 0101 1110", "1010 0010 1110", "1100 1001 1010", "1101 0101 0101", "0110 1011 0010", "0110 1011 1001", "0100 1011 1010", - 0x0AAA, 0x0AD5, 0x02DA, 0x095D, 0x045E, 0x0A2E, 0x0C9A, 0x0D55, 0x06B2, 0x06B9, 0x04BA, - //* 1564 -1574 */ "1010 0101 1101", "0101 0010 1101", "1010 1001 0101", "1011 0101 0010", "1011 1010 1000", "1011 1011 0100", "0101 1011 1001", "0010 1101 1010", "1001 0101 1010", "1011 0100 1010", "1101 1010 0100", - 0x0A5D, 0x052D, 0x0A95, 0x0B52, 0x0BA8, 0x0BB4, 0x05B9, 0x02DA, 0x095A, 0x0B4A, 0x0DA4, - //* 1575 -1585 */ "1110 1101 0001", "0110 1110 1000", "1011 0110 1010", "0101 0110 1101", "0101 0011 0101", "0110 1001 0101", "1101 0100 1010", "1101 1010 1000", "1101 1101 0100", "0110 1101 1010", "0101 0101 1011", - 0x0ED1, 0x06E8, 0x0B6A, 0x056D, 0x0535, 0x0695, 0x0D4A, 0x0DA8, 0x0DD4, 0x06DA, 0x055B, - //* 1586 -1596 */ "0010 1001 1101", "0110 0010 1011", "1011 0001 0101", "1011 0100 1010", "1011 1001 0101", "0101 1010 1010", "1010 1010 1110", "1001 0010 1110", "1100 1000 1111", "0101 0010 0111", "0110 1001 0101", - 0x029D, 0x062B, 0x0B15, 0x0B4A, 0x0B95, 0x05AA, 0x0AAE, 0x092E, 0x0C8F, 0x0527, 0x0695, - //* 1597 -1600 */ "0110 1010 1010", "1010 1101 0110", "0101 0101 1101", "0010 1001 1101", }; - 0x06AA, 0x0AD6, 0x055D, 0x029D -}; - -int32_t getUmalqura_MonthLength(int32_t y, int32_t m) { - int32_t mask = (int32_t) (0x01 << (11 - m)); // set mask for bit corresponding to month - if((UMALQURA_MONTHLENGTH[y] & mask) == 0 ) - return 29; - else - return 30; - -} - -//------------------------------------------------------------------------- -// Constructors... -//------------------------------------------------------------------------- - -const char *IslamicCalendar::getType() const { - const char *sType = NULL; - - switch (cType) { - case CIVIL: - sType = "islamic-civil"; - break; - case ASTRONOMICAL: - sType = "islamic"; - break; - case TBLA: - sType = "islamic-tbla"; - break; - case UMALQURA: - sType = "islamic-umalqura"; - break; - default: - UPRV_UNREACHABLE_EXIT; // out of range - } - return sType; -} - -IslamicCalendar* IslamicCalendar::clone() const { - return new IslamicCalendar(*this); -} - -IslamicCalendar::IslamicCalendar(const Locale& aLocale, UErrorCode& success, ECalculationType type) -: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success), -cType(type) -{ - setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. -} - -IslamicCalendar::IslamicCalendar(const IslamicCalendar& other) : Calendar(other), cType(other.cType) { -} - -IslamicCalendar::~IslamicCalendar() -{ -} - -void IslamicCalendar::setCalculationType(ECalculationType type, UErrorCode &status) -{ - if (cType != type) { - // The fields of the calendar will become invalid, because the calendar - // rules are different - UDate m = getTimeInMillis(status); - cType = type; - clear(); - setTimeInMillis(m, status); - } -} - -/** -* Returns true if this object is using the fixed-cycle civil -* calendar, or false if using the religious, astronomical -* calendar. -* @draft ICU 2.4 -*/ -UBool IslamicCalendar::isCivil() { - return (cType == CIVIL); -} - -//------------------------------------------------------------------------- -// Minimum / Maximum access functions -//------------------------------------------------------------------------- - -// Note: Current IslamicCalendar implementation does not work -// well with negative years. - -// TODO: In some cases the current ICU Islamic calendar implementation shows -// a month as having 31 days. Since date parsing now uses range checks based -// on the table below, we need to change the range for last day of month to -// include 31 as a workaround until the implementation is fixed. -static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { - // Minimum Greatest Least Maximum - // Minimum Maximum - { 0, 0, 0, 0}, // ERA - { 1, 1, 5000000, 5000000}, // YEAR - { 0, 0, 11, 11}, // MONTH - { 1, 1, 50, 51}, // WEEK_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH - { 1, 1, 29, 31}, // DAY_OF_MONTH - 31 to workaround for cal implementation bug, should be 30 - { 1, 1, 354, 355}, // DAY_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK - { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET - { 1, 1, 5000000, 5000000}, // YEAR_WOY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL - { 1, 1, 5000000, 5000000}, // EXTENDED_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH -}; - -/** -* @draft ICU 2.4 -*/ -int32_t IslamicCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { - return LIMITS[field][limitType]; -} - -//------------------------------------------------------------------------- -// Assorted calculation utilities -// - -// we could compress this down more if we need to -static const int8_t umAlQuraYrStartEstimateFix[] = { - 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, // 1300.. - -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, // 1310.. - 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, // 1320.. - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // 1330.. - 0, 0, 1, 0, 0, -1, -1, 0, 0, 0, // 1340.. - 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, // 1350.. - 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, // 1360.. - 0, 1, 1, 0, 0, -1, 0, 1, 0, 1, // 1370.. - 1, 0, 0, -1, 0, 1, 0, 0, 0, -1, // 1380.. - 0, 1, 0, 1, 0, 0, 0, -1, 0, 0, // 1390.. - 0, 0, -1, -1, 0, -1, 0, 1, 0, 0, // 1400.. - 0, -1, 0, 0, 0, 1, 0, 0, 0, 0, // 1410.. - 0, 1, 0, 0, -1, -1, 0, 0, 0, 1, // 1420.. - 0, 0, -1, -1, 0, -1, 0, 0, -1, -1, // 1430.. - 0, -1, 0, -1, 0, 0, -1, -1, 0, 0, // 1440.. - 0, 0, 0, 0, -1, 0, 1, 0, 1, 1, // 1450.. - 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, // 1460.. - 1, 0, 1, 0, 0, 0, -1, 0, 1, 0, // 1470.. - 0, -1, -1, 0, 0, 0, 1, 0, 0, 0, // 1480.. - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // 1490.. - 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, // 1500.. - 0, -1, 0, 1, 0, 1, 1, 0, 0, 0, // 1510.. - 0, 1, 0, 0, 0, -1, 0, 0, 0, 1, // 1520.. - 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, // 1530.. - 0, -1, 0, 1, 0, 0, 0, -1, 0, 1, // 1540.. - 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, // 1550.. - -1, 0, 0, 0, 0, 1, 0, 0, 0, -1, // 1560.. - 0, 0, 0, 0, -1, -1, 0, -1, 0, 1, // 1570.. - 0, 0, -1, -1, 0, 0, 1, 1, 0, 0, // 1580.. - -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, // 1590.. - 1 // 1600 -}; - -/** -* Determine whether a year is a leap year in the Islamic civil calendar -*/ -UBool IslamicCalendar::civilLeapYear(int32_t year) -{ - return (14 + 11 * year) % 30 < 11; -} - -/** -* Return the day # on which the given year starts. Days are counted -* from the Hijri epoch, origin 0. -*/ -int32_t IslamicCalendar::yearStart(int32_t year) const{ - if (cType == CIVIL || cType == TBLA || - (cType == UMALQURA && (year < UMALQURA_YEAR_START || year > UMALQURA_YEAR_END))) - { - return (year-1)*354 + ClockMath::floorDivide((3+11*(int64_t)year),(int64_t)30); - } else if(cType==ASTRONOMICAL){ - return trueMonthStart(12*(year-1)); - } else { - year -= UMALQURA_YEAR_START; - // rounded least-squares fit of the dates previously calculated from UMALQURA_MONTHLENGTH iteration - int32_t yrStartLinearEstimate = (int32_t)((354.36720 * (double)year) + 460322.05 + 0.5); - // need a slight correction to some - return yrStartLinearEstimate + umAlQuraYrStartEstimateFix[year]; - } -} - -/** -* Return the day # on which the given month starts. Days are counted -* from the Hijri epoch, origin 0. -* -* @param year The hijri year -* @param month The hijri month, 0-based (assumed to be in range 0..11) -*/ -int32_t IslamicCalendar::monthStart(int32_t year, int32_t month) const { - if (cType == CIVIL || cType == TBLA) { - // This does not handle months out of the range 0..11 - return (int32_t)uprv_ceil(29.5*month) - + (year-1)*354 + (int32_t)ClockMath::floorDivide((3+11*(int64_t)year),(int64_t)30); - } else if(cType==ASTRONOMICAL){ - return trueMonthStart(12*(year-1) + month); - } else { - int32_t ms = yearStart(year); - for(int i=0; i< month; i++){ - ms+= handleGetMonthLength(year, i); - } - return ms; - } -} - -/** -* Find the day number on which a particular month of the true/lunar -* Islamic calendar starts. -* -* @param month The month in question, origin 0 from the Hijri epoch -* -* @return The day number on which the given month starts. -*/ -int32_t IslamicCalendar::trueMonthStart(int32_t month) const -{ - UErrorCode status = U_ZERO_ERROR; - int32_t start = CalendarCache::get(&gMonthCache, month, status); - - if (start==0) { - // Make a guess at when the month started, using the average length - UDate origin = HIJRA_MILLIS - + uprv_floor(month * CalendarAstronomer::SYNODIC_MONTH) * kOneDay; - - // moonAge will fail due to memory allocation error - double age = moonAge(origin, status); - if (U_FAILURE(status)) { - goto trueMonthStartEnd; - } - - if (age >= 0) { - // The month has already started - do { - origin -= kOneDay; - age = moonAge(origin, status); - if (U_FAILURE(status)) { - goto trueMonthStartEnd; - } - } while (age >= 0); - } - else { - // Preceding month has not ended yet. - do { - origin += kOneDay; - age = moonAge(origin, status); - if (U_FAILURE(status)) { - goto trueMonthStartEnd; - } - } while (age < 0); - } - start = (int32_t)(ClockMath::floorDivide( - (int64_t)((int64_t)origin - HIJRA_MILLIS), (int64_t)kOneDay) + 1); - CalendarCache::put(&gMonthCache, month, start, status); - } -trueMonthStartEnd : - if(U_FAILURE(status)) { - start = 0; - } - return start; -} - -/** -* Return the "age" of the moon at the given time; this is the difference -* in ecliptic latitude between the moon and the sun. This method simply -* calls CalendarAstronomer.moonAge, converts to degrees, -* and adjusts the result to be in the range [-180, 180]. -* -* @param time The time at which the moon's age is desired, -* in millis since 1/1/1970. -*/ -double IslamicCalendar::moonAge(UDate time, UErrorCode &status) -{ - double age = 0; - - static UMutex astroLock; // pod bay door lock - umtx_lock(&astroLock); - if(gIslamicCalendarAstro == NULL) { - gIslamicCalendarAstro = new CalendarAstronomer(); - if (gIslamicCalendarAstro == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return age; - } - ucln_i18n_registerCleanup(UCLN_I18N_ISLAMIC_CALENDAR, calendar_islamic_cleanup); - } - gIslamicCalendarAstro->setTime(time); - age = gIslamicCalendarAstro->getMoonAge(); - umtx_unlock(&astroLock); - - // Convert to degrees and normalize... - age = age * 180 / CalendarAstronomer::PI; - if (age > 180) { - age = age - 360; - } - - return age; -} - -//---------------------------------------------------------------------- -// Calendar framework -//---------------------------------------------------------------------- - -/** -* Return the length (in days) of the given month. -* -* @param year The hijri year -* @param year The hijri month, 0-based -* @draft ICU 2.4 -*/ -int32_t IslamicCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { - - int32_t length = 0; - - if (cType == CIVIL || cType == TBLA || - (cType == UMALQURA && (extendedYearUMALQURA_YEAR_END)) ) { - length = 29 + (month+1) % 2; - if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) { - length++; - } - } else if(cType == ASTRONOMICAL){ - month = 12*(extendedYear-1) + month; - length = trueMonthStart(month+1) - trueMonthStart(month) ; - } else { - length = getUmalqura_MonthLength(extendedYear - UMALQURA_YEAR_START, month); - } - return length; -} - -/** -* Return the number of days in the given Islamic year -* @draft ICU 2.4 -*/ -int32_t IslamicCalendar::handleGetYearLength(int32_t extendedYear) const { - if (cType == CIVIL || cType == TBLA || - (cType == UMALQURA && (extendedYearUMALQURA_YEAR_END)) ) { - return 354 + (civilLeapYear(extendedYear) ? 1 : 0); - } else if(cType == ASTRONOMICAL){ - int32_t month = 12*(extendedYear-1); - return (trueMonthStart(month + 12) - trueMonthStart(month)); - } else { - int len = 0; - for(int i=0; i<12; i++) { - len += handleGetMonthLength(extendedYear, i); - } - return len; - } -} - -//------------------------------------------------------------------------- -// Functions for converting from field values to milliseconds.... -//------------------------------------------------------------------------- - -// Return JD of start of given month/year -// Calendar says: -// Get the Julian day of the day BEFORE the start of this year. -// If useMonth is true, get the day before the start of the month. -// Hence the -1 -/** -* @draft ICU 2.4 -*/ -int32_t IslamicCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */) const { - // This may be called by Calendar::handleComputeJulianDay with months out of the range - // 0..11. Need to handle that here since monthStart requires months in the range 0.11. - if (month > 11) { - eyear += (month / 12); - month %= 12; - } else if (month < 0) { - month++; - eyear += (month / 12) - 1; - month = (month % 12) + 11; - } - return monthStart(eyear, month) + ((cType == TBLA)? ASTRONOMICAL_EPOC: CIVIL_EPOC) - 1; -} - -//------------------------------------------------------------------------- -// Functions for converting from milliseconds to field values -//------------------------------------------------------------------------- - -/** -* @draft ICU 2.4 -*/ -int32_t IslamicCalendar::handleGetExtendedYear() { - int32_t year; - if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { - year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 - } else { - year = internalGet(UCAL_YEAR, 1); // Default to year 1 - } - return year; -} - -/** -* Override Calendar to compute several fields specific to the Islamic -* calendar system. These are: -* -*
  • ERA -*
  • YEAR -*
  • MONTH -*
  • DAY_OF_MONTH -*
  • DAY_OF_YEAR -*
  • EXTENDED_YEAR
-* -* The DAY_OF_WEEK and DOW_LOCAL fields are already set when this -* method is called. The getGregorianXxx() methods return Gregorian -* calendar equivalents for the given Julian day. -* @draft ICU 2.4 -*/ -void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { - int32_t year, month, dayOfMonth, dayOfYear; - int32_t startDate; - int32_t days = julianDay - CIVIL_EPOC; - - if (cType == CIVIL || cType == TBLA) { - if(cType == TBLA) { - days = julianDay - ASTRONOMICAL_EPOC; - } - // Use the civil calendar approximation, which is just arithmetic - year = (int32_t)ClockMath::floorDivide(30 * (int64_t)days + 10646, (int64_t)10631); - month = (int32_t)uprv_ceil((days - 29 - yearStart(year)) / 29.5 ); - month = month<11?month:11; - startDate = monthStart(year, month); - } else if(cType == ASTRONOMICAL){ - // Guess at the number of elapsed full months since the epoch - int32_t months = (int32_t)uprv_floor((double)days / CalendarAstronomer::SYNODIC_MONTH); - - startDate = (int32_t)uprv_floor(months * CalendarAstronomer::SYNODIC_MONTH); - - double age = moonAge(internalGetTime(), status); - if (U_FAILURE(status)) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if ( days - startDate >= 25 && age > 0) { - // If we're near the end of the month, assume next month and search backwards - months++; - } - - // Find out the last time that the new moon was actually visible at this longitude - // This returns midnight the night that the moon was visible at sunset. - while ((startDate = trueMonthStart(months)) > days) { - // If it was after the date in question, back up a month and try again - months--; - } - - year = months >= 0 ? ((months / 12) + 1) : ((months + 1 ) / 12); - month = ((months % 12) + 12 ) % 12; - } else if(cType == UMALQURA) { - int32_t umalquraStartdays = yearStart(UMALQURA_YEAR_START) ; - if( days < umalquraStartdays){ - //Use Civil calculation - year = (int32_t)ClockMath::floorDivide( - (30 * (int64_t)days + 10646) , (int64_t)10631.0 ); - month = (int32_t)uprv_ceil((days - 29 - yearStart(year)) / 29.5 ); - month = month<11?month:11; - startDate = monthStart(year, month); - }else{ - int y =UMALQURA_YEAR_START-1, m =0; - long d = 1; - while(d > 0){ - y++; - d = days - yearStart(y) +1; - if(d == handleGetYearLength(y)){ - m=11; - break; - }else if(d < handleGetYearLength(y) ){ - int monthLen = handleGetMonthLength(y, m); - m=0; - while(d > monthLen){ - d -= monthLen; - m++; - monthLen = handleGetMonthLength(y, m); - } - break; - } - } - year = y; - month = m; - } - } else { // invalid 'civil' - UPRV_UNREACHABLE_EXIT; // should not get here, out of range - } - - dayOfMonth = (days - monthStart(year, month)) + 1; - - // Now figure out the day of the year. - dayOfYear = (days - monthStart(year, 0)) + 1; - - - internalSet(UCAL_ERA, 0); - internalSet(UCAL_YEAR, year); - internalSet(UCAL_EXTENDED_YEAR, year); - internalSet(UCAL_MONTH, month); - internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); - internalSet(UCAL_DAY_OF_YEAR, dayOfYear); -} - -UBool -IslamicCalendar::inDaylightTime(UErrorCode& status) const -{ - // copied from GregorianCalendar - if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) - return false; - - // Force an update of the state of the Calendar. - ((IslamicCalendar*)this)->complete(status); // cast away const - - return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : false); -} - -/** - * The system maintains a static default century start date and Year. They are - * initialized the first time they are used. Once the system default century date - * and year are set, they do not change. - */ -static UDate gSystemDefaultCenturyStart = DBL_MIN; -static int32_t gSystemDefaultCenturyStartYear = -1; -static icu::UInitOnce gSystemDefaultCenturyInit {}; - - -UBool IslamicCalendar::haveDefaultCentury() const -{ - return true; -} - -UDate IslamicCalendar::defaultCenturyStart() const -{ - // lazy-evaluate systemDefaultCenturyStart - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStart; -} - -int32_t IslamicCalendar::defaultCenturyStartYear() const -{ - // lazy-evaluate systemDefaultCenturyStartYear - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStartYear; -} - - -U_CFUNC void U_CALLCONV -IslamicCalendar::initializeSystemDefaultCentury() -{ - // initialize systemDefaultCentury and systemDefaultCenturyYear based - // on the current time. They'll be set to 80 years before - // the current time. - UErrorCode status = U_ZERO_ERROR; - IslamicCalendar calendar(Locale("@calendar=islamic-civil"),status); - if (U_SUCCESS(status)) { - calendar.setTime(Calendar::getNow(), status); - calendar.add(UCAL_YEAR, -80, status); - - gSystemDefaultCenturyStart = calendar.getTime(status); - gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); - } - // We have no recourse upon failure unless we want to propagate the failure - // out. -} - - - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicCalendar) - -U_NAMESPACE_END - -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 2003-2015, International Business Machines Corporation +* and others. All Rights Reserved. +****************************************************************************** +* +* File ISLAMCAL.H +* +* Modification History: +* +* Date Name Description +* 10/14/2003 srl ported from java IslamicCalendar +***************************************************************************** +*/ + +#include "islamcal.h" + +#if !UCONFIG_NO_FORMATTING + +#include "umutex.h" +#include +#include "gregoimp.h" // Math +#include "astro.h" // CalendarAstronomer +#include "uhash.h" +#include "ucln_in.h" +#include "uassert.h" + +static const UDate HIJRA_MILLIS = -42521587200000.0; // 7/16/622 AD 00:00 + +// Debugging +#ifdef U_DEBUG_ISLAMCAL +# include +# include +static void debug_islamcal_loc(const char *f, int32_t l) +{ + fprintf(stderr, "%s:%d: ", f, l); +} + +static void debug_islamcal_msg(const char *pat, ...) +{ + va_list ap; + va_start(ap, pat); + vfprintf(stderr, pat, ap); + fflush(stderr); +} +// must use double parens, i.e.: U_DEBUG_ISLAMCAL_MSG(("four is: %d",4)); +#define U_DEBUG_ISLAMCAL_MSG(x) {debug_islamcal_loc(__FILE__,__LINE__);debug_islamcal_msg x;} +#else +#define U_DEBUG_ISLAMCAL_MSG(x) +#endif + + +// --- The cache -- +// cache of months +static icu::CalendarCache *gMonthCache = nullptr; +static icu::CalendarAstronomer *gIslamicCalendarAstro = nullptr; + +U_CDECL_BEGIN +static UBool calendar_islamic_cleanup() { + if (gMonthCache) { + delete gMonthCache; + gMonthCache = nullptr; + } + if (gIslamicCalendarAstro) { + delete gIslamicCalendarAstro; + gIslamicCalendarAstro = nullptr; + } + return true; +} +U_CDECL_END + +U_NAMESPACE_BEGIN + +// Implementation of the IslamicCalendar class + +/** + * Friday EPOC + */ +static const int32_t CIVIL_EPOC = 1948440; // CE 622 July 16 Friday (Julian calendar) / CE 622 July 19 (Gregorian calendar) + +/** + * Thursday EPOC + */ +static const int32_t ASTRONOMICAL_EPOC = 1948439; // CE 622 July 15 Thursday (Julian calendar) + + +static const int32_t UMALQURA_YEAR_START = 1300; +static const int32_t UMALQURA_YEAR_END = 1600; + +static const int UMALQURA_MONTHLENGTH[] = { + //* 1300 -1302 */ "1010 1010 1010", "1101 0101 0100", "1110 1100 1001", + 0x0AAA, 0x0D54, 0x0EC9, + //* 1303 -1307 */ "0110 1101 0100", "0110 1110 1010", "0011 0110 1100", "1010 1010 1101", "0101 0101 0101", + 0x06D4, 0x06EA, 0x036C, 0x0AAD, 0x0555, + //* 1308 -1312 */ "0110 1010 1001", "0111 1001 0010", "1011 1010 1001", "0101 1101 0100", "1010 1101 1010", + 0x06A9, 0x0792, 0x0BA9, 0x05D4, 0x0ADA, + //* 1313 -1317 */ "0101 0101 1100", "1101 0010 1101", "0110 1001 0101", "0111 0100 1010", "1011 0101 0100", + 0x055C, 0x0D2D, 0x0695, 0x074A, 0x0B54, + //* 1318 -1322 */ "1011 0110 1010", "0101 1010 1101", "0100 1010 1110", "1010 0100 1111", "0101 0001 0111", + 0x0B6A, 0x05AD, 0x04AE, 0x0A4F, 0x0517, + //* 1323 -1327 */ "0110 1000 1011", "0110 1010 0101", "1010 1101 0101", "0010 1101 0110", "1001 0101 1011", + 0x068B, 0x06A5, 0x0AD5, 0x02D6, 0x095B, + //* 1328 -1332 */ "0100 1001 1101", "1010 0100 1101", "1101 0010 0110", "1101 1001 0101", "0101 1010 1100", + 0x049D, 0x0A4D, 0x0D26, 0x0D95, 0x05AC, + //* 1333 -1337 */ "1001 1011 0110", "0010 1011 1010", "1010 0101 1011", "0101 0010 1011", "1010 1001 0101", + 0x09B6, 0x02BA, 0x0A5B, 0x052B, 0x0A95, + //* 1338 -1342 */ "0110 1100 1010", "1010 1110 1001", "0010 1111 0100", "1001 0111 0110", "0010 1011 0110", + 0x06CA, 0x0AE9, 0x02F4, 0x0976, 0x02B6, + //* 1343 -1347 */ "1001 0101 0110", "1010 1100 1010", "1011 1010 0100", "1011 1101 0010", "0101 1101 1001", + 0x0956, 0x0ACA, 0x0BA4, 0x0BD2, 0x05D9, + //* 1348 -1352 */ "0010 1101 1100", "1001 0110 1101", "0101 0100 1101", "1010 1010 0101", "1011 0101 0010", + 0x02DC, 0x096D, 0x054D, 0x0AA5, 0x0B52, + //* 1353 -1357 */ "1011 1010 0101", "0101 1011 0100", "1001 1011 0110", "0101 0101 0111", "0010 1001 0111", + 0x0BA5, 0x05B4, 0x09B6, 0x0557, 0x0297, + //* 1358 -1362 */ "0101 0100 1011", "0110 1010 0011", "0111 0101 0010", "1011 0110 0101", "0101 0110 1010", + 0x054B, 0x06A3, 0x0752, 0x0B65, 0x056A, + //* 1363 -1367 */ "1010 1010 1011", "0101 0010 1011", "1100 1001 0101", "1101 0100 1010", "1101 1010 0101", + 0x0AAB, 0x052B, 0x0C95, 0x0D4A, 0x0DA5, + //* 1368 -1372 */ "0101 1100 1010", "1010 1101 0110", "1001 0101 0111", "0100 1010 1011", "1001 0100 1011", + 0x05CA, 0x0AD6, 0x0957, 0x04AB, 0x094B, + //* 1373 -1377 */ "1010 1010 0101", "1011 0101 0010", "1011 0110 1010", "0101 0111 0101", "0010 0111 0110", + 0x0AA5, 0x0B52, 0x0B6A, 0x0575, 0x0276, + //* 1378 -1382 */ "1000 1011 0111", "0100 0101 1011", "0101 0101 0101", "0101 1010 1001", "0101 1011 0100", + 0x08B7, 0x045B, 0x0555, 0x05A9, 0x05B4, + //* 1383 -1387 */ "1001 1101 1010", "0100 1101 1101", "0010 0110 1110", "1001 0011 0110", "1010 1010 1010", + 0x09DA, 0x04DD, 0x026E, 0x0936, 0x0AAA, + //* 1388 -1392 */ "1101 0101 0100", "1101 1011 0010", "0101 1101 0101", "0010 1101 1010", "1001 0101 1011", + 0x0D54, 0x0DB2, 0x05D5, 0x02DA, 0x095B, + //* 1393 -1397 */ "0100 1010 1011", "1010 0101 0101", "1011 0100 1001", "1011 0110 0100", "1011 0111 0001", + 0x04AB, 0x0A55, 0x0B49, 0x0B64, 0x0B71, + //* 1398 -1402 */ "0101 1011 0100", "1010 1011 0101", "1010 0101 0101", "1101 0010 0101", "1110 1001 0010", + 0x05B4, 0x0AB5, 0x0A55, 0x0D25, 0x0E92, + //* 1403 -1407 */ "1110 1100 1001", "0110 1101 0100", "1010 1110 1001", "1001 0110 1011", "0100 1010 1011", + 0x0EC9, 0x06D4, 0x0AE9, 0x096B, 0x04AB, + //* 1408 -1412 */ "1010 1001 0011", "1101 0100 1001", "1101 1010 0100", "1101 1011 0010", "1010 1011 1001", + 0x0A93, 0x0D49, 0x0DA4, 0x0DB2, 0x0AB9, + //* 1413 -1417 */ "0100 1011 1010", "1010 0101 1011", "0101 0010 1011", "1010 1001 0101", "1011 0010 1010", + 0x04BA, 0x0A5B, 0x052B, 0x0A95, 0x0B2A, + //* 1418 -1422 */ "1011 0101 0101", "0101 0101 1100", "0100 1011 1101", "0010 0011 1101", "1001 0001 1101", + 0x0B55, 0x055C, 0x04BD, 0x023D, 0x091D, + //* 1423 -1427 */ "1010 1001 0101", "1011 0100 1010", "1011 0101 1010", "0101 0110 1101", "0010 1011 0110", + 0x0A95, 0x0B4A, 0x0B5A, 0x056D, 0x02B6, + //* 1428 -1432 */ "1001 0011 1011", "0100 1001 1011", "0110 0101 0101", "0110 1010 1001", "0111 0101 0100", + 0x093B, 0x049B, 0x0655, 0x06A9, 0x0754, + //* 1433 -1437 */ "1011 0110 1010", "0101 0110 1100", "1010 1010 1101", "0101 0101 0101", "1011 0010 1001", + 0x0B6A, 0x056C, 0x0AAD, 0x0555, 0x0B29, + //* 1438 -1442 */ "1011 1001 0010", "1011 1010 1001", "0101 1101 0100", "1010 1101 1010", "0101 0101 1010", + 0x0B92, 0x0BA9, 0x05D4, 0x0ADA, 0x055A, + //* 1443 -1447 */ "1010 1010 1011", "0101 1001 0101", "0111 0100 1001", "0111 0110 0100", "1011 1010 1010", + 0x0AAB, 0x0595, 0x0749, 0x0764, 0x0BAA, + //* 1448 -1452 */ "0101 1011 0101", "0010 1011 0110", "1010 0101 0110", "1110 0100 1101", "1011 0010 0101", + 0x05B5, 0x02B6, 0x0A56, 0x0E4D, 0x0B25, + //* 1453 -1457 */ "1011 0101 0010", "1011 0110 1010", "0101 1010 1101", "0010 1010 1110", "1001 0010 1111", + 0x0B52, 0x0B6A, 0x05AD, 0x02AE, 0x092F, + //* 1458 -1462 */ "0100 1001 0111", "0110 0100 1011", "0110 1010 0101", "0110 1010 1100", "1010 1101 0110", + 0x0497, 0x064B, 0x06A5, 0x06AC, 0x0AD6, + //* 1463 -1467 */ "0101 0101 1101", "0100 1001 1101", "1010 0100 1101", "1101 0001 0110", "1101 1001 0101", + 0x055D, 0x049D, 0x0A4D, 0x0D16, 0x0D95, + //* 1468 -1472 */ "0101 1010 1010", "0101 1011 0101", "0010 1101 1010", "1001 0101 1011", "0100 1010 1101", + 0x05AA, 0x05B5, 0x02DA, 0x095B, 0x04AD, + //* 1473 -1477 */ "0101 1001 0101", "0110 1100 1010", "0110 1110 0100", "1010 1110 1010", "0100 1111 0101", + 0x0595, 0x06CA, 0x06E4, 0x0AEA, 0x04F5, + //* 1478 -1482 */ "0010 1011 0110", "1001 0101 0110", "1010 1010 1010", "1011 0101 0100", "1011 1101 0010", + 0x02B6, 0x0956, 0x0AAA, 0x0B54, 0x0BD2, + //* 1483 -1487 */ "0101 1101 1001", "0010 1110 1010", "1001 0110 1101", "0100 1010 1101", "1010 1001 0101", + 0x05D9, 0x02EA, 0x096D, 0x04AD, 0x0A95, + //* 1488 -1492 */ "1011 0100 1010", "1011 1010 0101", "0101 1011 0010", "1001 1011 0101", "0100 1101 0110", + 0x0B4A, 0x0BA5, 0x05B2, 0x09B5, 0x04D6, + //* 1493 -1497 */ "1010 1001 0111", "0101 0100 0111", "0110 1001 0011", "0111 0100 1001", "1011 0101 0101", + 0x0A97, 0x0547, 0x0693, 0x0749, 0x0B55, + //* 1498 -1508 */ "0101 0110 1010", "1010 0110 1011", "0101 0010 1011", "1010 1000 1011", "1101 0100 0110", "1101 1010 0011", "0101 1100 1010", "1010 1101 0110", "0100 1101 1011", "0010 0110 1011", "1001 0100 1011", + 0x056A, 0x0A6B, 0x052B, 0x0A8B, 0x0D46, 0x0DA3, 0x05CA, 0x0AD6, 0x04DB, 0x026B, 0x094B, + //* 1509 -1519 */ "1010 1010 0101", "1011 0101 0010", "1011 0110 1001", "0101 0111 0101", "0001 0111 0110", "1000 1011 0111", "0010 0101 1011", "0101 0010 1011", "0101 0110 0101", "0101 1011 0100", "1001 1101 1010", + 0x0AA5, 0x0B52, 0x0B69, 0x0575, 0x0176, 0x08B7, 0x025B, 0x052B, 0x0565, 0x05B4, 0x09DA, + //* 1520 -1530 */ "0100 1110 1101", "0001 0110 1101", "1000 1011 0110", "1010 1010 0110", "1101 0101 0010", "1101 1010 1001", "0101 1101 0100", "1010 1101 1010", "1001 0101 1011", "0100 1010 1011", "0110 0101 0011", + 0x04ED, 0x016D, 0x08B6, 0x0AA6, 0x0D52, 0x0DA9, 0x05D4, 0x0ADA, 0x095B, 0x04AB, 0x0653, + //* 1531 -1541 */ "0111 0010 1001", "0111 0110 0010", "1011 1010 1001", "0101 1011 0010", "1010 1011 0101", "0101 0101 0101", "1011 0010 0101", "1101 1001 0010", "1110 1100 1001", "0110 1101 0010", "1010 1110 1001", + 0x0729, 0x0762, 0x0BA9, 0x05B2, 0x0AB5, 0x0555, 0x0B25, 0x0D92, 0x0EC9, 0x06D2, 0x0AE9, + //* 1542 -1552 */ "0101 0110 1011", "0100 1010 1011", "1010 0101 0101", "1101 0010 1001", "1101 0101 0100", "1101 1010 1010", "1001 1011 0101", "0100 1011 1010", "1010 0011 1011", "0100 1001 1011", "1010 0100 1101", + 0x056B, 0x04AB, 0x0A55, 0x0D29, 0x0D54, 0x0DAA, 0x09B5, 0x04BA, 0x0A3B, 0x049B, 0x0A4D, + //* 1553 -1563 */ "1010 1010 1010", "1010 1101 0101", "0010 1101 1010", "1001 0101 1101", "0100 0101 1110", "1010 0010 1110", "1100 1001 1010", "1101 0101 0101", "0110 1011 0010", "0110 1011 1001", "0100 1011 1010", + 0x0AAA, 0x0AD5, 0x02DA, 0x095D, 0x045E, 0x0A2E, 0x0C9A, 0x0D55, 0x06B2, 0x06B9, 0x04BA, + //* 1564 -1574 */ "1010 0101 1101", "0101 0010 1101", "1010 1001 0101", "1011 0101 0010", "1011 1010 1000", "1011 1011 0100", "0101 1011 1001", "0010 1101 1010", "1001 0101 1010", "1011 0100 1010", "1101 1010 0100", + 0x0A5D, 0x052D, 0x0A95, 0x0B52, 0x0BA8, 0x0BB4, 0x05B9, 0x02DA, 0x095A, 0x0B4A, 0x0DA4, + //* 1575 -1585 */ "1110 1101 0001", "0110 1110 1000", "1011 0110 1010", "0101 0110 1101", "0101 0011 0101", "0110 1001 0101", "1101 0100 1010", "1101 1010 1000", "1101 1101 0100", "0110 1101 1010", "0101 0101 1011", + 0x0ED1, 0x06E8, 0x0B6A, 0x056D, 0x0535, 0x0695, 0x0D4A, 0x0DA8, 0x0DD4, 0x06DA, 0x055B, + //* 1586 -1596 */ "0010 1001 1101", "0110 0010 1011", "1011 0001 0101", "1011 0100 1010", "1011 1001 0101", "0101 1010 1010", "1010 1010 1110", "1001 0010 1110", "1100 1000 1111", "0101 0010 0111", "0110 1001 0101", + 0x029D, 0x062B, 0x0B15, 0x0B4A, 0x0B95, 0x05AA, 0x0AAE, 0x092E, 0x0C8F, 0x0527, 0x0695, + //* 1597 -1600 */ "0110 1010 1010", "1010 1101 0110", "0101 0101 1101", "0010 1001 1101", }; + 0x06AA, 0x0AD6, 0x055D, 0x029D +}; + +int32_t getUmalqura_MonthLength(int32_t y, int32_t m) { + int32_t mask = (int32_t) (0x01 << (11 - m)); // set mask for bit corresponding to month + if((UMALQURA_MONTHLENGTH[y] & mask) == 0 ) + return 29; + else + return 30; + +} + +//------------------------------------------------------------------------- +// Constructors... +//------------------------------------------------------------------------- + +const char *IslamicCalendar::getType() const { + return "islamic"; +} + +IslamicCalendar* IslamicCalendar::clone() const { + return new IslamicCalendar(*this); +} + +IslamicCalendar::IslamicCalendar(const Locale& aLocale, UErrorCode& success) +: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success) +{ + setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. +} + +IslamicCalendar::~IslamicCalendar() +{ +} +//------------------------------------------------------------------------- +// Minimum / Maximum access functions +//------------------------------------------------------------------------- + +// Note: Current IslamicCalendar implementation does not work +// well with negative years. + +// TODO: In some cases the current ICU Islamic calendar implementation shows +// a month as having 31 days. Since date parsing now uses range checks based +// on the table below, we need to change the range for last day of month to +// include 31 as a workaround until the implementation is fixed. +static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { + // Minimum Greatest Least Maximum + // Minimum Maximum + { 0, 0, 0, 0}, // ERA + { 1, 1, 5000000, 5000000}, // YEAR + { 0, 0, 11, 11}, // MONTH + { 1, 1, 50, 51}, // WEEK_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH + { 1, 1, 29, 31}, // DAY_OF_MONTH - 31 to workaround for cal implementation bug, should be 30 + { 1, 1, 354, 355}, // DAY_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK + { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET + { 1, 1, 5000000, 5000000}, // YEAR_WOY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL + { 1, 1, 5000000, 5000000}, // EXTENDED_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH + { 0, 0, 11, 11}, // ORDINAL_MONTH +}; + +/** +* @draft ICU 2.4 +*/ +int32_t IslamicCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { + return LIMITS[field][limitType]; +} + +//------------------------------------------------------------------------- +// Assorted calculation utilities +// + +// we could compress this down more if we need to +static const int8_t umAlQuraYrStartEstimateFix[] = { + 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, // 1300.. + -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, // 1310.. + 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, // 1320.. + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // 1330.. + 0, 0, 1, 0, 0, -1, -1, 0, 0, 0, // 1340.. + 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, // 1350.. + 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, // 1360.. + 0, 1, 1, 0, 0, -1, 0, 1, 0, 1, // 1370.. + 1, 0, 0, -1, 0, 1, 0, 0, 0, -1, // 1380.. + 0, 1, 0, 1, 0, 0, 0, -1, 0, 0, // 1390.. + 0, 0, -1, -1, 0, -1, 0, 1, 0, 0, // 1400.. + 0, -1, 0, 0, 0, 1, 0, 0, 0, 0, // 1410.. + 0, 1, 0, 0, -1, -1, 0, 0, 0, 1, // 1420.. + 0, 0, -1, -1, 0, -1, 0, 0, -1, -1, // 1430.. + 0, -1, 0, -1, 0, 0, -1, -1, 0, 0, // 1440.. + 0, 0, 0, 0, -1, 0, 1, 0, 1, 1, // 1450.. + 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, // 1460.. + 1, 0, 1, 0, 0, 0, -1, 0, 1, 0, // 1470.. + 0, -1, -1, 0, 0, 0, 1, 0, 0, 0, // 1480.. + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // 1490.. + 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, // 1500.. + 0, -1, 0, 1, 0, 1, 1, 0, 0, 0, // 1510.. + 0, 1, 0, 0, 0, -1, 0, 0, 0, 1, // 1520.. + 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, // 1530.. + 0, -1, 0, 1, 0, 0, 0, -1, 0, 1, // 1540.. + 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, // 1550.. + -1, 0, 0, 0, 0, 1, 0, 0, 0, -1, // 1560.. + 0, 0, 0, 0, -1, -1, 0, -1, 0, 1, // 1570.. + 0, 0, -1, -1, 0, 0, 1, 1, 0, 0, // 1580.. + -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, // 1590.. + 1 // 1600 +}; + +/** +* Determine whether a year is a leap year in the Islamic civil calendar +*/ +UBool IslamicCalendar::civilLeapYear(int32_t year) +{ + return (14 + 11 * year) % 30 < 11; +} + +/** +* Return the day # on which the given year starts. Days are counted +* from the Hijri epoch, origin 0. +*/ +int32_t IslamicCalendar::yearStart(int32_t year) const{ + return trueMonthStart(12*(year-1)); +} + +/** +* Return the day # on which the given month starts. Days are counted +* from the Hijri epoch, origin 0. +* +* @param year The hijri year +* @param month The hijri month, 0-based (assumed to be in range 0..11) +*/ +int32_t IslamicCalendar::monthStart(int32_t year, int32_t month) const { + return trueMonthStart(12*(year-1) + month); +} + +/** +* Find the day number on which a particular month of the true/lunar +* Islamic calendar starts. +* +* @param month The month in question, origin 0 from the Hijri epoch +* +* @return The day number on which the given month starts. +*/ +int32_t IslamicCalendar::trueMonthStart(int32_t month) const +{ + UErrorCode status = U_ZERO_ERROR; + int32_t start = CalendarCache::get(&gMonthCache, month, status); + + if (start==0) { + // Make a guess at when the month started, using the average length + UDate origin = HIJRA_MILLIS + + uprv_floor(month * CalendarAstronomer::SYNODIC_MONTH) * kOneDay; + + // moonAge will fail due to memory allocation error + double age = moonAge(origin, status); + if (U_FAILURE(status)) { + goto trueMonthStartEnd; + } + + if (age >= 0) { + // The month has already started + do { + origin -= kOneDay; + age = moonAge(origin, status); + if (U_FAILURE(status)) { + goto trueMonthStartEnd; + } + } while (age >= 0); + } + else { + // Preceding month has not ended yet. + do { + origin += kOneDay; + age = moonAge(origin, status); + if (U_FAILURE(status)) { + goto trueMonthStartEnd; + } + } while (age < 0); + } + start = (int32_t)(ClockMath::floorDivide( + (int64_t)((int64_t)origin - HIJRA_MILLIS), (int64_t)kOneDay) + 1); + CalendarCache::put(&gMonthCache, month, start, status); + } +trueMonthStartEnd : + if(U_FAILURE(status)) { + start = 0; + } + return start; +} + +/** +* Return the "age" of the moon at the given time; this is the difference +* in ecliptic latitude between the moon and the sun. This method simply +* calls CalendarAstronomer.moonAge, converts to degrees, +* and adjusts the result to be in the range [-180, 180]. +* +* @param time The time at which the moon's age is desired, +* in millis since 1/1/1970. +*/ +double IslamicCalendar::moonAge(UDate time, UErrorCode &status) +{ + double age = 0; + + static UMutex astroLock; // pod bay door lock + umtx_lock(&astroLock); + if(gIslamicCalendarAstro == nullptr) { + gIslamicCalendarAstro = new CalendarAstronomer(); + if (gIslamicCalendarAstro == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return age; + } + ucln_i18n_registerCleanup(UCLN_I18N_ISLAMIC_CALENDAR, calendar_islamic_cleanup); + } + gIslamicCalendarAstro->setTime(time); + age = gIslamicCalendarAstro->getMoonAge(); + umtx_unlock(&astroLock); + + // Convert to degrees and normalize... + age = age * 180 / CalendarAstronomer::PI; + if (age > 180) { + age = age - 360; + } + + return age; +} + +//---------------------------------------------------------------------- +// Calendar framework +//---------------------------------------------------------------------- + +/** +* Return the length (in days) of the given month. +* +* @param year The hijri year +* @param year The hijri month, 0-based +* @draft ICU 2.4 +*/ +int32_t IslamicCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { + month = 12*(extendedYear-1) + month; + return trueMonthStart(month+1) - trueMonthStart(month) ; +} + +/** +* Return the number of days in the given Islamic year +* @draft ICU 2.4 +*/ +int32_t IslamicCalendar::handleGetYearLength(int32_t extendedYear) const { + int32_t month = 12*(extendedYear-1); + return (trueMonthStart(month + 12) - trueMonthStart(month)); +} + +//------------------------------------------------------------------------- +// Functions for converting from field values to milliseconds.... +//------------------------------------------------------------------------- + +// Return JD of start of given month/year +// Calendar says: +// Get the Julian day of the day BEFORE the start of this year. +// If useMonth is true, get the day before the start of the month. +// Hence the -1 +/** +* @draft ICU 2.4 +*/ +int32_t IslamicCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */) const { + // This may be called by Calendar::handleComputeJulianDay with months out of the range + // 0..11. Need to handle that here since monthStart requires months in the range 0.11. + if (month > 11) { + eyear += (month / 12); + month %= 12; + } else if (month < 0) { + month++; + eyear += (month / 12) - 1; + month = (month % 12) + 11; + } + return monthStart(eyear, month) + getEpoc() - 1; +} + +//------------------------------------------------------------------------- +// Functions for converting from milliseconds to field values +//------------------------------------------------------------------------- + +/** +* @draft ICU 2.4 +*/ +int32_t IslamicCalendar::handleGetExtendedYear() { + int32_t year; + if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { + year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 + } else { + year = internalGet(UCAL_YEAR, 1); // Default to year 1 + } + return year; +} + +/** +* Override Calendar to compute several fields specific to the Islamic +* calendar system. These are: +* +*
  • ERA +*
  • YEAR +*
  • MONTH +*
  • DAY_OF_MONTH +*
  • DAY_OF_YEAR +*
  • EXTENDED_YEAR
+* +* The DAY_OF_WEEK and DOW_LOCAL fields are already set when this +* method is called. The getGregorianXxx() methods return Gregorian +* calendar equivalents for the given Julian day. +* @draft ICU 2.4 +*/ +void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { + if (U_FAILURE(status)) return; + int32_t days = julianDay - getEpoc(); + + // Guess at the number of elapsed full months since the epoch + int32_t month = (int32_t)uprv_floor((double)days / CalendarAstronomer::SYNODIC_MONTH); + + int32_t startDate = (int32_t)uprv_floor(month * CalendarAstronomer::SYNODIC_MONTH); + + double age = moonAge(internalGetTime(), status); + if (U_FAILURE(status)) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if ( days - startDate >= 25 && age > 0) { + // If we're near the end of the month, assume next month and search backwards + month++; + } + + // Find out the last time that the new moon was actually visible at this longitude + // This returns midnight the night that the moon was visible at sunset. + while ((startDate = trueMonthStart(month)) > days) { + // If it was after the date in question, back up a month and try again + month--; + } + + int32_t year = month >= 0 ? ((month / 12) + 1) : ((month + 1 ) / 12); + month = ((month % 12) + 12 ) % 12; + int32_t dayOfMonth = (days - monthStart(year, month)) + 1; + + // Now figure out the day of the year. + int32_t dayOfYear = (days - monthStart(year, 0)) + 1; + + internalSet(UCAL_ERA, 0); + internalSet(UCAL_YEAR, year); + internalSet(UCAL_EXTENDED_YEAR, year); + internalSet(UCAL_MONTH, month); + internalSet(UCAL_ORDINAL_MONTH, month); + internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); + internalSet(UCAL_DAY_OF_YEAR, dayOfYear); +} + +int32_t IslamicCalendar::getEpoc() const { + return CIVIL_EPOC; +} + +static int32_t gregoYearFromIslamicStart(int32_t year) { + // ad hoc conversion, improve under #10752 + // rough est for now, ok for grego 1846-2138, + // otherwise occasionally wrong (for 3% of years) + int cycle, offset, shift = 0; + if (year >= 1397) { + cycle = (year - 1397) / 67; + offset = (year - 1397) % 67; + shift = 2*cycle + ((offset >= 33)? 1: 0); + } else { + cycle = (year - 1396) / 67 - 1; + offset = -(year - 1396) % 67; + shift = 2*cycle + ((offset <= 33)? 1: 0); + } + return year + 579 - shift; +} + +int32_t IslamicCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return gregoYearFromIslamicStart(year); +} + +static int32_t firstIslamicStartYearFromGrego(int32_t year) { + // ad hoc conversion, improve under #10752 + // rough est for now, ok for grego 1846-2138, + // otherwise occasionally wrong (for 3% of years) + int cycle, offset, shift = 0; + if (year >= 1977) { + cycle = (year - 1977) / 65; + offset = (year - 1977) % 65; + shift = 2*cycle + ((offset >= 32)? 1: 0); + } else { + cycle = (year - 1976) / 65 - 1; + offset = -(year - 1976) % 65; + shift = 2*cycle + ((offset <= 32)? 1: 0); + } + return year - 579 + shift; +} + +void IslamicCalendar::setRelatedYear(int32_t year) +{ + set(UCAL_EXTENDED_YEAR, firstIslamicStartYearFromGrego(year)); +} + +/** + * The system maintains a static default century start date and Year. They are + * initialized the first time they are used. Once the system default century date + * and year are set, they do not change. + */ +static UDate gSystemDefaultCenturyStart = DBL_MIN; +static int32_t gSystemDefaultCenturyStartYear = -1; +static icu::UInitOnce gSystemDefaultCenturyInit {}; + + +UBool IslamicCalendar::haveDefaultCentury() const +{ + return true; +} + +UDate IslamicCalendar::defaultCenturyStart() const +{ + // lazy-evaluate systemDefaultCenturyStart + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStart; +} + +int32_t IslamicCalendar::defaultCenturyStartYear() const +{ + // lazy-evaluate systemDefaultCenturyStartYear + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStartYear; +} + +bool +IslamicCalendar::inTemporalLeapYear(UErrorCode &status) const +{ + int32_t days = getActualMaximum(UCAL_DAY_OF_YEAR, status); + if (U_FAILURE(status)) return false; + return days == 355; +} + + +U_CFUNC void U_CALLCONV +IslamicCalendar::initializeSystemDefaultCentury() +{ + // initialize systemDefaultCentury and systemDefaultCenturyYear based + // on the current time. They'll be set to 80 years before + // the current time. + UErrorCode status = U_ZERO_ERROR; + IslamicCalendar calendar(Locale("@calendar=islamic-civil"),status); + if (U_SUCCESS(status)) { + calendar.setTime(Calendar::getNow(), status); + calendar.add(UCAL_YEAR, -80, status); + + gSystemDefaultCenturyStart = calendar.getTime(status); + gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); + } + // We have no recourse upon failure unless we want to propagate the failure + // out. +} + +/***************************************************************************** + * IslamicCivilCalendar + *****************************************************************************/ +IslamicCivilCalendar::IslamicCivilCalendar(const Locale& aLocale, UErrorCode& success) + : IslamicCalendar(aLocale, success) +{ +} + +IslamicCivilCalendar::~IslamicCivilCalendar() +{ +} + +const char *IslamicCivilCalendar::getType() const { + return "islamic-civil"; +} + +IslamicCivilCalendar* IslamicCivilCalendar::clone() const { + return new IslamicCivilCalendar(*this); +} + +/** +* Return the day # on which the given year starts. Days are counted +* from the Hijri epoch, origin 0. +*/ +int32_t IslamicCivilCalendar::yearStart(int32_t year) const{ + return static_cast( + (year-1)*354 + ClockMath::floorDivide((3+11*static_cast(year)), + static_cast(30))); +} + +/** +* Return the day # on which the given month starts. Days are counted +* from the Hijri epoch, origin 0. +* +* @param year The hijri year +* @param month The hijri month, 0-based (assumed to be in range 0..11) +*/ +int32_t IslamicCivilCalendar::monthStart(int32_t year, int32_t month) const { + // This does not handle months out of the range 0..11 + return static_cast( + uprv_ceil(29.5*month) + (year-1)*354 + + static_cast(ClockMath::floorDivide( + 3+11*static_cast(year), + static_cast(30)))); +} + +/** +* Return the length (in days) of the given month. +* +* @param year The hijri year +* @param year The hijri month, 0-based +* @draft ICU 2.4 +*/ +int32_t IslamicCivilCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { + int32_t length = 29 + (month+1) % 2; + if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) { + length++; + } + return length; +} + +/** +* Return the number of days in the given Islamic year +* @draft ICU 2.4 +*/ +int32_t IslamicCivilCalendar::handleGetYearLength(int32_t extendedYear) const { + return 354 + (civilLeapYear(extendedYear) ? 1 : 0); +} + +/** +* Override Calendar to compute several fields specific to the Islamic +* calendar system. These are: +* +*
  • ERA +*
  • YEAR +*
  • MONTH +*
  • DAY_OF_MONTH +*
  • DAY_OF_YEAR +*
  • EXTENDED_YEAR
+* +* The DAY_OF_WEEK and DOW_LOCAL fields are already set when this +* method is called. The getGregorianXxx() methods return Gregorian +* calendar equivalents for the given Julian day. +* @draft ICU 2.4 +*/ +void IslamicCivilCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { + if (U_FAILURE(status)) return; + int32_t days = julianDay - getEpoc(); + + // Use the civil calendar approximation, which is just arithmetic + int32_t year = static_cast( + ClockMath::floorDivide(30 * static_cast(days) + 10646, + static_cast(10631))); + int32_t month = static_cast( + uprv_ceil((days - 29 - yearStart(year)) / 29.5 )); + month = month<11?month:11; + + int32_t dayOfMonth = (days - monthStart(year, month)) + 1; + + // Now figure out the day of the year. + int32_t dayOfYear = (days - monthStart(year, 0)) + 1; + + internalSet(UCAL_ERA, 0); + internalSet(UCAL_YEAR, year); + internalSet(UCAL_EXTENDED_YEAR, year); + internalSet(UCAL_MONTH, month); + internalSet(UCAL_ORDINAL_MONTH, month); + internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); + internalSet(UCAL_DAY_OF_YEAR, dayOfYear); +} +/***************************************************************************** + * IslamicTBLACalendar + *****************************************************************************/ +IslamicTBLACalendar::IslamicTBLACalendar(const Locale& aLocale, UErrorCode& success) + : IslamicCivilCalendar(aLocale, success) +{ +} + +IslamicTBLACalendar::~IslamicTBLACalendar() +{ +} + +const char *IslamicTBLACalendar::getType() const { + return "islamic-tbla"; +} + +IslamicTBLACalendar* IslamicTBLACalendar::clone() const { + return new IslamicTBLACalendar(*this); +} + +int32_t IslamicTBLACalendar::getEpoc() const { + return ASTRONOMICAL_EPOC; +} + +/***************************************************************************** + * IslamicUmalquraCalendar + *****************************************************************************/ +IslamicUmalquraCalendar::IslamicUmalquraCalendar(const Locale& aLocale, UErrorCode& success) + : IslamicCalendar(aLocale, success) +{ +} + +IslamicUmalquraCalendar::~IslamicUmalquraCalendar() +{ +} + +const char *IslamicUmalquraCalendar::getType() const { + return "islamic-umalqura"; +} + +IslamicUmalquraCalendar* IslamicUmalquraCalendar::clone() const { + return new IslamicUmalquraCalendar(*this); +} + +/** +* Return the day # on which the given year starts. Days are counted +* from the Hijri epoch, origin 0. +*/ +int32_t IslamicUmalquraCalendar::yearStart(int32_t year) const { + if (year < UMALQURA_YEAR_START || year > UMALQURA_YEAR_END) { + return static_cast( + (year-1)*354 + ClockMath::floorDivide((3+11*static_cast(year)), + static_cast(30))); + } + year -= UMALQURA_YEAR_START; + // rounded least-squares fit of the dates previously calculated from UMALQURA_MONTHLENGTH iteration + int32_t yrStartLinearEstimate = static_cast( + (354.36720 * (double)year) + 460322.05 + 0.5); + // need a slight correction to some + return yrStartLinearEstimate + umAlQuraYrStartEstimateFix[year]; +} + +/** +* Return the day # on which the given month starts. Days are counted +* from the Hijri epoch, origin 0. +* +* @param year The hijri year +* @param month The hijri month, 0-based (assumed to be in range 0..11) +*/ +int32_t IslamicUmalquraCalendar::monthStart(int32_t year, int32_t month) const { + int32_t ms = yearStart(year); + for(int i=0; i< month; i++){ + ms+= handleGetMonthLength(year, i); + } + return ms; +} + +/** +* Return the length (in days) of the given month. +* +* @param year The hijri year +* @param year The hijri month, 0-based +*/ +int32_t IslamicUmalquraCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { + int32_t length = 0; + if (extendedYearUMALQURA_YEAR_END) { + length = 29 + (month+1) % 2; + if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) { + length++; + } + return length; + } + return getUmalqura_MonthLength(extendedYear - UMALQURA_YEAR_START, month); +} + +/** +* Return the number of days in the given Islamic year +* @draft ICU 2.4 +*/ +int32_t IslamicUmalquraCalendar::handleGetYearLength(int32_t extendedYear) const { + if (extendedYearUMALQURA_YEAR_END) { + return 354 + (civilLeapYear(extendedYear) ? 1 : 0); + } + int len = 0; + for(int i=0; i<12; i++) { + len += handleGetMonthLength(extendedYear, i); + } + return len; +} + +/** +* Override Calendar to compute several fields specific to the Islamic +* calendar system. These are: +* +*
  • ERA +*
  • YEAR +*
  • MONTH +*
  • DAY_OF_MONTH +*
  • DAY_OF_YEAR +*
  • EXTENDED_YEAR
+* +* The DAY_OF_WEEK and DOW_LOCAL fields are already set when this +* method is called. The getGregorianXxx() methods return Gregorian +* calendar equivalents for the given Julian day. +* @draft ICU 2.4 +*/ +void IslamicUmalquraCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { + if (U_FAILURE(status)) return; + int32_t year, month, dayOfMonth, dayOfYear; + int32_t days = julianDay - getEpoc(); + + int32_t umalquraStartdays = yearStart(UMALQURA_YEAR_START) ; + if (days < umalquraStartdays) { + //Use Civil calculation + year = (int32_t)ClockMath::floorDivide( + (30 * (int64_t)days + 10646) , (int64_t)10631.0 ); + month = (int32_t)uprv_ceil((days - 29 - yearStart(year)) / 29.5 ); + month = month < 11 ? month : 11; + } else { + int y =UMALQURA_YEAR_START-1, m =0; + long d = 1; + while (d > 0) { + y++; + d = days - yearStart(y) +1; + if (d == handleGetYearLength(y)) { + m=11; + break; + } + if (d < handleGetYearLength(y)){ + int monthLen = handleGetMonthLength(y, m); + m=0; + while(d > monthLen){ + d -= monthLen; + m++; + monthLen = handleGetMonthLength(y, m); + } + break; + } + } + year = y; + month = m; + } + + dayOfMonth = (days - monthStart(year, month)) + 1; + + // Now figure out the day of the year. + dayOfYear = (days - monthStart(year, 0)) + 1; + + internalSet(UCAL_ERA, 0); + internalSet(UCAL_YEAR, year); + internalSet(UCAL_EXTENDED_YEAR, year); + internalSet(UCAL_MONTH, month); + internalSet(UCAL_ORDINAL_MONTH, month); + internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); + internalSet(UCAL_DAY_OF_YEAR, dayOfYear); +} +/***************************************************************************** + * IslamicRGSACalendar + *****************************************************************************/ +IslamicRGSACalendar::IslamicRGSACalendar(const Locale& aLocale, UErrorCode& success) + : IslamicCalendar(aLocale, success) +{ +} + +IslamicRGSACalendar::~IslamicRGSACalendar() +{ +} + +const char *IslamicRGSACalendar::getType() const { + return "islamic-rgsa"; +} + +IslamicRGSACalendar* IslamicRGSACalendar::clone() const { + return new IslamicRGSACalendar(*this); +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicCalendar) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicCivilCalendar) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicUmalquraCalendar) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicTBLACalendar) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicRGSACalendar) + +U_NAMESPACE_END + +#endif + diff --git a/deps/icu-small/source/i18n/islamcal.h b/deps/icu-small/source/i18n/islamcal.h index 7d9941d4709638..834780591e2f30 100644 --- a/deps/icu-small/source/i18n/islamcal.h +++ b/deps/icu-small/source/i18n/islamcal.h @@ -1,431 +1,763 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ******************************************************************************** - * Copyright (C) 2003-2013, International Business Machines Corporation - * and others. All Rights Reserved. - ****************************************************************************** - * - * File ISLAMCAL.H - * - * Modification History: - * - * Date Name Description - * 10/14/2003 srl ported from java IslamicCalendar - ***************************************************************************** - */ - -#ifndef ISLAMCAL_H -#define ISLAMCAL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/calendar.h" - -U_NAMESPACE_BEGIN - -/** - * IslamicCalendar is a subclass of Calendar - * that implements the Islamic civil and religious calendars. It - * is used as the civil calendar in most of the Arab world and the - * liturgical calendar of the Islamic faith worldwide. This calendar - * is also known as the "Hijri" calendar, since it starts at the time - * of Mohammed's emigration (or "hijra") to Medinah on Thursday, - * July 15, 622 AD (Julian). - *

- * The Islamic calendar is strictly lunar, and thus an Islamic year of twelve - * lunar months does not correspond to the solar year used by most other - * calendar systems, including the Gregorian. An Islamic year is, on average, - * about 354 days long, so each successive Islamic year starts about 11 days - * earlier in the corresponding Gregorian year. - *

- * Each month of the calendar starts when the new moon's crescent is visible - * at sunset. However, in order to keep the time fields in this class - * synchronized with those of the other calendars and with local clock time, - * we treat days and months as beginning at midnight, - * roughly 6 hours after the corresponding sunset. - *

- * There are two main variants of the Islamic calendar in existence. The first - * is the civil calendar, which uses a fixed cycle of alternating 29- - * and 30-day months, with a leap day added to the last month of 11 out of - * every 30 years. This calendar is easily calculated and thus predictable in - * advance, so it is used as the civil calendar in a number of Arab countries. - * This is the default behavior of a newly-created IslamicCalendar - * object. - *

- * The Islamic religious calendar, however, is based on the observation - * of the crescent moon. It is thus affected by the position at which the - * observations are made, seasonal variations in the time of sunset, the - * eccentricities of the moon's orbit, and even the weather at the observation - * site. This makes it impossible to calculate in advance, and it causes the - * start of a month in the religious calendar to differ from the civil calendar - * by up to three days. - *

- * Using astronomical calculations for the position of the sun and moon, the - * moon's illumination, and other factors, it is possible to determine the start - * of a lunar month with a fairly high degree of certainty. However, these - * calculations are extremely complicated and thus slow, so most algorithms, - * including the one used here, are only approximations of the true astronomical - * calculations. At present, the approximations used in this class are fairly - * simplistic; they will be improved in later versions of the code. - *

- * The {@link #setCivil setCivil} method determines - * which approach is used to determine the start of a month. By default, the - * fixed-cycle civil calendar is used. However, if setCivil(false) - * is called, an approximation of the true lunar calendar will be used. - * - * @see GregorianCalendar - * - * @author Laura Werner - * @author Alan Liu - * @author Steven R. Loomis - * @internal - */ -class U_I18N_API IslamicCalendar : public Calendar { - public: - //------------------------------------------------------------------------- - // Constants... - //------------------------------------------------------------------------- - - /** - * Calendar type - civil or religious or um alqura - * @internal - */ - enum ECalculationType { - ASTRONOMICAL, - CIVIL, - UMALQURA, - TBLA - }; - - /** - * Constants for the months - * @internal - */ - enum EMonths { - /** - * Constant for Muharram, the 1st month of the Islamic year. - * @internal - */ - MUHARRAM = 0, - - /** - * Constant for Safar, the 2nd month of the Islamic year. - * @internal - */ - SAFAR = 1, - - /** - * Constant for Rabi' al-awwal (or Rabi' I), the 3rd month of the Islamic year. - * @internal - */ - RABI_1 = 2, - - /** - * Constant for Rabi' al-thani or (Rabi' II), the 4th month of the Islamic year. - * @internal - */ - RABI_2 = 3, - - /** - * Constant for Jumada al-awwal or (Jumada I), the 5th month of the Islamic year. - * @internal - */ - JUMADA_1 = 4, - - /** - * Constant for Jumada al-thani or (Jumada II), the 6th month of the Islamic year. - * @internal - */ - JUMADA_2 = 5, - - /** - * Constant for Rajab, the 7th month of the Islamic year. - * @internal - */ - RAJAB = 6, - - /** - * Constant for Sha'ban, the 8th month of the Islamic year. - * @internal - */ - SHABAN = 7, - - /** - * Constant for Ramadan, the 9th month of the Islamic year. - * @internal - */ - RAMADAN = 8, - - /** - * Constant for Shawwal, the 10th month of the Islamic year. - * @internal - */ - SHAWWAL = 9, - - /** - * Constant for Dhu al-Qi'dah, the 11th month of the Islamic year. - * @internal - */ - DHU_AL_QIDAH = 10, - - /** - * Constant for Dhu al-Hijjah, the 12th month of the Islamic year. - * @internal - */ - DHU_AL_HIJJAH = 11, - - ISLAMIC_MONTH_MAX - }; - - - //------------------------------------------------------------------------- - // Constructors... - //------------------------------------------------------------------------- - - /** - * Constructs an IslamicCalendar based on the current time in the default time zone - * with the given locale. - * - * @param aLocale The given locale. - * @param success Indicates the status of IslamicCalendar object construction. - * Returns U_ZERO_ERROR if constructed successfully. - * @param type The Islamic calendar calculation type. The default value is CIVIL. - * @internal - */ - IslamicCalendar(const Locale& aLocale, UErrorCode &success, ECalculationType type = CIVIL); - - /** - * Copy Constructor - * @internal - */ - IslamicCalendar(const IslamicCalendar& other); - - /** - * Destructor. - * @internal - */ - virtual ~IslamicCalendar(); - - /** - * Sets Islamic calendar calculation type used by this instance. - * - * @param type The calendar calculation type, CIVIL to use the civil - * calendar, ASTRONOMICAL to use the astronomical calendar. - * @internal - */ - void setCalculationType(ECalculationType type, UErrorCode &status); - - /** - * Returns true if this object is using the fixed-cycle civil - * calendar, or false if using the religious, astronomical - * calendar. - * @internal - */ - UBool isCivil(); - - - // TODO: copy c'tor, etc - - // clone - virtual IslamicCalendar* clone() const override; - - private: - /** - * Determine whether a year is a leap year in the Islamic civil calendar - */ - static UBool civilLeapYear(int32_t year); - - /** - * Return the day # on which the given year starts. Days are counted - * from the Hijri epoch, origin 0. - */ - int32_t yearStart(int32_t year) const; - - /** - * Return the day # on which the given month starts. Days are counted - * from the Hijri epoch, origin 0. - * - * @param year The hijri year - * @param year The hijri month, 0-based - */ - int32_t monthStart(int32_t year, int32_t month) const; - - /** - * Find the day number on which a particular month of the true/lunar - * Islamic calendar starts. - * - * @param month The month in question, origin 0 from the Hijri epoch - * - * @return The day number on which the given month starts. - */ - int32_t trueMonthStart(int32_t month) const; - - /** - * Return the "age" of the moon at the given time; this is the difference - * in ecliptic latitude between the moon and the sun. This method simply - * calls CalendarAstronomer.moonAge, converts to degrees, - * and adjusts the resultto be in the range [-180, 180]. - * - * @param time The time at which the moon's age is desired, - * in millis since 1/1/1970. - */ - static double moonAge(UDate time, UErrorCode &status); - - //------------------------------------------------------------------------- - // Internal data.... - // - - /** - * CIVIL if this object uses the fixed-cycle Islamic civil calendar, - * and ASTRONOMICAL if it approximates the true religious calendar using - * astronomical calculations for the time of the new moon. - */ - ECalculationType cType; - - //---------------------------------------------------------------------- - // Calendar framework - //---------------------------------------------------------------------- - protected: - /** - * @internal - */ - virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; - - /** - * Return the length (in days) of the given month. - * - * @param year The hijri year - * @param year The hijri month, 0-based - * @internal - */ - virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; - - /** - * Return the number of days in the given Islamic year - * @internal - */ - virtual int32_t handleGetYearLength(int32_t extendedYear) const override; - - //------------------------------------------------------------------------- - // Functions for converting from field values to milliseconds.... - //------------------------------------------------------------------------- - - // Return JD of start of given month/year - /** - * @internal - */ - virtual int32_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override; - - //------------------------------------------------------------------------- - // Functions for converting from milliseconds to field values - //------------------------------------------------------------------------- - - /** - * @internal - */ - virtual int32_t handleGetExtendedYear() override; - - /** - * Override Calendar to compute several fields specific to the Islamic - * calendar system. These are: - * - *

  • ERA - *
  • YEAR - *
  • MONTH - *
  • DAY_OF_MONTH - *
  • DAY_OF_YEAR - *
  • EXTENDED_YEAR
- * - * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this - * method is called. The getGregorianXxx() methods return Gregorian - * calendar equivalents for the given Julian day. - * @internal - */ - virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; - - // UObject stuff - public: - /** - * @return The class ID for this object. All objects of a given class have the - * same class ID. Objects of other classes have different class IDs. - * @internal - */ - virtual UClassID getDynamicClassID(void) const override; - - /** - * Return the class ID for this class. This is useful only for comparing to a return - * value from getDynamicClassID(). For example: - * - * Base* polymorphic_pointer = createPolymorphicObject(); - * if (polymorphic_pointer->getDynamicClassID() == - * Derived::getStaticClassID()) ... - * - * @return The class ID for all objects of this class. - * @internal - */ - /*U_I18N_API*/ static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * return the calendar type, "buddhist". - * - * @return calendar type - * @internal - */ - virtual const char * getType() const override; - - private: - IslamicCalendar() = delete; // default constructor not implemented - - // Default century. - protected: - - /** - * (Overrides Calendar) Return true if the current date for this Calendar is in - * Daylight Savings Time. Recognizes DST_OFFSET, if it is set. - * - * @param status Fill-in parameter which receives the status of this operation. - * @return True if the current date for this Calendar is in Daylight Savings Time, - * false, otherwise. - * @internal - */ - virtual UBool inDaylightTime(UErrorCode& status) const override; - - - /** - * Returns true because the Islamic Calendar does have a default century - * @internal - */ - virtual UBool haveDefaultCentury() const override; - - /** - * Returns the date of the start of the default century - * @return start of century - in milliseconds since epoch, 1970 - * @internal - */ - virtual UDate defaultCenturyStart() const override; - - /** - * Returns the year in which the default century begins - * @internal - */ - virtual int32_t defaultCenturyStartYear() const override; - - private: - /** - * Initializes the 100-year window that dates with 2-digit years - * are considered to fall within so that its start date is 80 years - * before the current time. - */ - static void U_CALLCONV initializeSystemDefaultCentury(void); -}; - -U_NAMESPACE_END - -#endif -#endif - - - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************************** + * Copyright (C) 2003-2013, International Business Machines Corporation + * and others. All Rights Reserved. + ****************************************************************************** + * + * File ISLAMCAL.H + * + * Modification History: + * + * Date Name Description + * 10/14/2003 srl ported from java IslamicCalendar + ***************************************************************************** + */ + +#ifndef ISLAMCAL_H +#define ISLAMCAL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" + +U_NAMESPACE_BEGIN + +/** + * IslamicCalendar is a subclass of Calendar + * that implements the Islamic civil and religious calendars. It + * is used as the civil calendar in most of the Arab world and the + * liturgical calendar of the Islamic faith worldwide. This calendar + * is also known as the "Hijri" calendar, since it starts at the time + * of Mohammed's emigration (or "hijra") to Medinah on Thursday, + * July 15, 622 AD (Julian). + *

+ * The Islamic calendar is strictly lunar, and thus an Islamic year of twelve + * lunar months does not correspond to the solar year used by most other + * calendar systems, including the Gregorian. An Islamic year is, on average, + * about 354 days long, so each successive Islamic year starts about 11 days + * earlier in the corresponding Gregorian year. + *

+ * Each month of the calendar starts when the new moon's crescent is visible + * at sunset. However, in order to keep the time fields in this class + * synchronized with those of the other calendars and with local clock time, + * we treat days and months as beginning at midnight, + * roughly 6 hours after the corresponding sunset. + *

+ * There are two main variants of the Islamic calendar in existence. The first + * is the civil calendar, which uses a fixed cycle of alternating 29- + * and 30-day months, with a leap day added to the last month of 11 out of + * every 30 years. This calendar is easily calculated and thus predictable in + * advance, so it is used as the civil calendar in a number of Arab countries. + * This is the default behavior of a newly-created IslamicCalendar + * object. This calendar variant is implemented in the IslamicCivilCalendar + * class. + *

+ * The Islamic religious calendar, however, is based on the observation + * of the crescent moon. It is thus affected by the position at which the + * observations are made, seasonal variations in the time of sunset, the + * eccentricities of the moon's orbit, and even the weather at the observation + * site. This makes it impossible to calculate in advance, and it causes the + * start of a month in the religious calendar to differ from the civil calendar + * by up to three days. + *

+ * Using astronomical calculations for the position of the sun and moon, the + * moon's illumination, and other factors, it is possible to determine the start + * of a lunar month with a fairly high degree of certainty. However, these + * calculations are extremely complicated and thus slow, so most algorithms, + * including the one used here, are only approximations of the true astronomical + * calculations. At present, the approximations used in this class are fairly + * simplistic; they will be improved in later versions of the code. + *

+ * + * @see GregorianCalendar + * + * @author Laura Werner + * @author Alan Liu + * @author Steven R. Loomis + * @internal + */ +class U_I18N_API IslamicCalendar : public Calendar { + public: + //------------------------------------------------------------------------- + // Constants... + //------------------------------------------------------------------------- + /** + * Constants for the months + * @internal + */ + enum EMonths { + /** + * Constant for Muharram, the 1st month of the Islamic year. + * @internal + */ + MUHARRAM = 0, + + /** + * Constant for Safar, the 2nd month of the Islamic year. + * @internal + */ + SAFAR = 1, + + /** + * Constant for Rabi' al-awwal (or Rabi' I), the 3rd month of the Islamic year. + * @internal + */ + RABI_1 = 2, + + /** + * Constant for Rabi' al-thani or (Rabi' II), the 4th month of the Islamic year. + * @internal + */ + RABI_2 = 3, + + /** + * Constant for Jumada al-awwal or (Jumada I), the 5th month of the Islamic year. + * @internal + */ + JUMADA_1 = 4, + + /** + * Constant for Jumada al-thani or (Jumada II), the 6th month of the Islamic year. + * @internal + */ + JUMADA_2 = 5, + + /** + * Constant for Rajab, the 7th month of the Islamic year. + * @internal + */ + RAJAB = 6, + + /** + * Constant for Sha'ban, the 8th month of the Islamic year. + * @internal + */ + SHABAN = 7, + + /** + * Constant for Ramadan, the 9th month of the Islamic year. + * @internal + */ + RAMADAN = 8, + + /** + * Constant for Shawwal, the 10th month of the Islamic year. + * @internal + */ + SHAWWAL = 9, + + /** + * Constant for Dhu al-Qi'dah, the 11th month of the Islamic year. + * @internal + */ + DHU_AL_QIDAH = 10, + + /** + * Constant for Dhu al-Hijjah, the 12th month of the Islamic year. + * @internal + */ + DHU_AL_HIJJAH = 11, + + ISLAMIC_MONTH_MAX + }; + + + //------------------------------------------------------------------------- + // Constructors... + //------------------------------------------------------------------------- + + /** + * Constructs an IslamicCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of IslamicCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + IslamicCalendar(const Locale& aLocale, UErrorCode &success); + + /** + * Copy Constructor + * @internal + */ + IslamicCalendar(const IslamicCalendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~IslamicCalendar(); + + // clone + virtual IslamicCalendar* clone() const override; + + protected: + /** + * Determine whether a year is a leap year in the Islamic civil calendar + */ + static UBool civilLeapYear(int32_t year); + + /** + * Return the day # on which the given year starts. Days are counted + * from the Hijri epoch, origin 0. + */ + virtual int32_t yearStart(int32_t year) const; + + /** + * Return the day # on which the given month starts. Days are counted + * from the Hijri epoch, origin 0. + * + * @param year The hijri year + * @param year The hijri month, 0-based + */ + virtual int32_t monthStart(int32_t year, int32_t month) const; + + /** + * Find the day number on which a particular month of the true/lunar + * Islamic calendar starts. + * + * @param month The month in question, origin 0 from the Hijri epoch + * + * @return The day number on which the given month starts. + */ + int32_t trueMonthStart(int32_t month) const; + + private: + /** + * Return the "age" of the moon at the given time; this is the difference + * in ecliptic latitude between the moon and the sun. This method simply + * calls CalendarAstronomer.moonAge, converts to degrees, + * and adjusts the resultto be in the range [-180, 180]. + * + * @param time The time at which the moon's age is desired, + * in millis since 1/1/1970. + */ + static double moonAge(UDate time, UErrorCode &status); + + //---------------------------------------------------------------------- + // Calendar framework + //---------------------------------------------------------------------- + protected: + /** + * @internal + */ + virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; + + /** + * Return the length (in days) of the given month. + * + * @param year The hijri year + * @param year The hijri month, 0-based + * @internal + */ + virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; + + /** + * Return the number of days in the given Islamic year + * @internal + */ + virtual int32_t handleGetYearLength(int32_t extendedYear) const override; + + //------------------------------------------------------------------------- + // Functions for converting from field values to milliseconds.... + //------------------------------------------------------------------------- + + // Return JD of start of given month/year + /** + * @internal + */ + virtual int32_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override; + + //------------------------------------------------------------------------- + // Functions for converting from milliseconds to field values + //------------------------------------------------------------------------- + + /** + * @internal + */ + virtual int32_t handleGetExtendedYear() override; + + /** + * Override Calendar to compute several fields specific to the Islamic + * calendar system. These are: + * + *

  • ERA + *
  • YEAR + *
  • MONTH + *
  • DAY_OF_MONTH + *
  • DAY_OF_YEAR + *
  • EXTENDED_YEAR
+ * + * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this + * method is called. The getGregorianXxx() methods return Gregorian + * calendar equivalents for the given Julian day. + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; + + /** + * Return the epoc. + * @internal + */ + virtual int32_t getEpoc() const; + + // UObject stuff + public: + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + /*U_I18N_API*/ static UClassID U_EXPORT2 getStaticClassID(); + + /** + * return the calendar type, "islamic". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + /** + * @return The related Gregorian year; will be obtained by modifying the value + * obtained by get from UCAL_EXTENDED_YEAR field + * @internal + */ + virtual int32_t getRelatedYear(UErrorCode &status) const override; + + /** + * @param year The related Gregorian year to set; will be modified as necessary then + * set in UCAL_EXTENDED_YEAR field + * @internal + */ + virtual void setRelatedYear(int32_t year) override; + + /** + * Returns true if the date is in a leap year. + * + * @param status ICU Error Code + * @return True if the date in the fields is in a Temporal proposal + * defined leap year. False otherwise. + */ + virtual bool inTemporalLeapYear(UErrorCode &status) const override; + + private: + IslamicCalendar() = delete; // default constructor not implemented + + // Default century. + protected: + /** + * Returns true because the Islamic Calendar does have a default century + * @internal + */ + virtual UBool haveDefaultCentury() const override; + + /** + * Returns the date of the start of the default century + * @return start of century - in milliseconds since epoch, 1970 + * @internal + */ + virtual UDate defaultCenturyStart() const override; + + /** + * Returns the year in which the default century begins + * @internal + */ + virtual int32_t defaultCenturyStartYear() const override; + + private: + /** + * Initializes the 100-year window that dates with 2-digit years + * are considered to fall within so that its start date is 80 years + * before the current time. + */ + static void U_CALLCONV initializeSystemDefaultCentury(); +}; + +/* + * IslamicCivilCalendar is one of the two main variants of the Islamic calendar. + * The civil calendar, which uses a fixed cycle of alternating 29- + * and 30-day months, with a leap day added to the last month of 11 out of + * every 30 years. This calendar is easily calculated and thus predictable in + * advance, so it is used as the civil calendar in a number of Arab countries. + * This calendar is referring as "Islamic calendar, tabular (intercalary years + * [2,5,7,10,13,16,18,21,24,26,29]- civil epoch" in CLDR. + */ +class U_I18N_API IslamicCivilCalendar : public IslamicCalendar { + public: + /** + * Constructs an IslamicCivilCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of IslamicCivilCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + IslamicCivilCalendar(const Locale& aLocale, UErrorCode &success); + + /** + * Copy Constructor + * @internal + */ + IslamicCivilCalendar(const IslamicCivilCalendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~IslamicCivilCalendar(); + + // clone + virtual IslamicCivilCalendar* clone() const override; + + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * return the calendar type, "islamic-civil". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + protected: + /** + * Return the day # on which the given year starts. Days are counted + * from the Hijri epoch, origin 0. + * @internal + */ + virtual int32_t yearStart(int32_t year) const override; + + /** + * Return the day # on which the given month starts. Days are counted + * from the Hijri epoch, origin 0. + * + * @param year The hijri year + * @param year The hijri month, 0-based + * @internal + */ + virtual int32_t monthStart(int32_t year, int32_t month) const override; + + /** + * Return the length (in days) of the given month. + * + * @param year The hijri year + * @param year The hijri month, 0-based + * @internal + */ + virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; + + /** + * Return the number of days in the given Islamic year + * @internal + */ + virtual int32_t handleGetYearLength(int32_t extendedYear) const override; + + /** + * Override Calendar to compute several fields specific to the Islamic + * calendar system. These are: + * + *
  • ERA + *
  • YEAR + *
  • MONTH + *
  • DAY_OF_MONTH + *
  • DAY_OF_YEAR + *
  • EXTENDED_YEAR
+ * + * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this + * method is called. The getGregorianXxx() methods return Gregorian + * calendar equivalents for the given Julian day. + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; +}; + +/* + * IslamicTBLACalendar calendar. + * This is a subclass of IslamicCivilCalendar. The only differences in the + * calendar math is it uses different epoch. + * This calendar is referring as "Islamic calendar, tabular (intercalary years + * [2,5,7,10,13,16,18,21,24,26,29] - astronomical epoch" in CLDR. + */ +class U_I18N_API IslamicTBLACalendar : public IslamicCivilCalendar { + public: + /** + * Constructs an IslamicTBLACalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of IslamicTBLACalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + IslamicTBLACalendar(const Locale& aLocale, UErrorCode &success); + + /** + * Copy Constructor + * @internal + */ + IslamicTBLACalendar(const IslamicTBLACalendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~IslamicTBLACalendar(); + + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * return the calendar type, "islamic-tbla". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + // clone + virtual IslamicTBLACalendar* clone() const override; + + protected: + /** + * Return the epoc. + * @internal + */ + virtual int32_t getEpoc() const override; +}; + +/* + * IslamicUmalquraCalendar + * This calendar is referred as "Islamic calendar, Umm al-Qura" in CLDR. + */ +class U_I18N_API IslamicUmalquraCalendar : public IslamicCalendar { + public: + /** + * Constructs an IslamicUmalquraCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of IslamicUmalquraCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + IslamicUmalquraCalendar(const Locale& aLocale, UErrorCode &success); + + /** + * Copy Constructor + * @internal + */ + IslamicUmalquraCalendar(const IslamicUmalquraCalendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~IslamicUmalquraCalendar(); + + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * return the calendar type, "islamic-umalqura". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + // clone + virtual IslamicUmalquraCalendar* clone() const override; + + protected: + /** + * Return the day # on which the given year starts. Days are counted + * from the Hijri epoch, origin 0. + * @internal + */ + virtual int32_t yearStart(int32_t year) const override; + + /** + * Return the day # on which the given month starts. Days are counted + * from the Hijri epoch, origin 0. + * + * @param year The hijri year + * @param year The hijri month, 0-based + * @internal + */ + virtual int32_t monthStart(int32_t year, int32_t month) const override; + + /** + * Return the length (in days) of the given month. + * + * @param year The hijri year + * @param year The hijri month, 0-based + * @internal + */ + virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; + + /** + * Return the number of days in the given Islamic year + * @internal + */ + virtual int32_t handleGetYearLength(int32_t extendedYear) const override; + + /** + * Override Calendar to compute several fields specific to the Islamic + * calendar system. These are: + * + *
  • ERA + *
  • YEAR + *
  • MONTH + *
  • DAY_OF_MONTH + *
  • DAY_OF_YEAR + *
  • EXTENDED_YEAR
+ * + * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this + * method is called. The getGregorianXxx() methods return Gregorian + * calendar equivalents for the given Julian day. + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; +}; + + +/* + * IslamicRGSACalendar + * Islamic calendar, Saudi Arabia sighting. Since the calendar depends on the + * sighting, it is impossible to implement by algorithm ahead of time. It is + * currently identical to IslamicCalendar except the getType will return + * "islamic-rgsa". + */ +class U_I18N_API IslamicRGSACalendar : public IslamicCalendar { + public: + /** + * Constructs an IslamicRGSACalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of IslamicRGSACalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + IslamicRGSACalendar(const Locale& aLocale, UErrorCode &success); + + /** + * Copy Constructor + * @internal + */ + IslamicRGSACalendar(const IslamicRGSACalendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~IslamicRGSACalendar(); + + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * return the calendar type, "islamic-rgsa". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + // clone + virtual IslamicRGSACalendar* clone() const override; +}; + +U_NAMESPACE_END + +#endif +#endif diff --git a/deps/icu-small/source/i18n/iso8601cal.cpp b/deps/icu-small/source/i18n/iso8601cal.cpp new file mode 100644 index 00000000000000..62664ea42723f5 --- /dev/null +++ b/deps/icu-small/source/i18n/iso8601cal.cpp @@ -0,0 +1,37 @@ +// © 2022 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "iso8601cal.h" +#include "unicode/gregocal.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ISO8601Calendar) + +ISO8601Calendar::ISO8601Calendar(const Locale& aLocale, UErrorCode& success) +: GregorianCalendar(aLocale, success) +{ + setFirstDayOfWeek(UCAL_MONDAY); + setMinimalDaysInFirstWeek(4); +} + +ISO8601Calendar::~ISO8601Calendar() +{ +} + +ISO8601Calendar* ISO8601Calendar::clone() const +{ + return new ISO8601Calendar(*this); +} + +const char *ISO8601Calendar::getType() const +{ + return "iso8601"; +} + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/i18n/iso8601cal.h b/deps/icu-small/source/i18n/iso8601cal.h new file mode 100644 index 00000000000000..3e407384144dcd --- /dev/null +++ b/deps/icu-small/source/i18n/iso8601cal.h @@ -0,0 +1,102 @@ +// © 2022 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +#ifndef ISO8601CAL_H +#define ISO8601CAL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" +#include "unicode/gregocal.h" +#include "unicode/timezone.h" + +U_NAMESPACE_BEGIN + +/** + * Concrete class which provides the ISO8601 calendar. + *

+ * ISO8601Calendar is a subclass of GregorianCalendar + * that the first day of a week is Monday and the minimal days in the first + * week of a year or month is four days. + *

+ * The ISO8601 calendar is identical to the Gregorian calendar in all respects + * except for the first day of week and the minimal days in the first week + * of a year. + * @internal + */ +class ISO8601Calendar : public GregorianCalendar { + public: + //------------------------------------------------------------------------- + // Constructors... + //------------------------------------------------------------------------- + + /** + * Constructs a DangiCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of ISO8601Calendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + ISO8601Calendar(const Locale& aLocale, UErrorCode &success); + + /** + * Copy Constructor + * @internal + */ + ISO8601Calendar(const ISO8601Calendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~ISO8601Calendar(); + + /** + * Clone. + * @internal + */ + virtual ISO8601Calendar* clone() const override; + + // UObject stuff + public: + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + + /** + * return the calendar type, "iso8601". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + + private: + + ISO8601Calendar(); // default constructor not implemented +}; + +U_NAMESPACE_END + +#endif +#endif diff --git a/deps/icu-small/source/i18n/japancal.cpp b/deps/icu-small/source/i18n/japancal.cpp index ca9b0704a00dfc..e8e6c5ca00c317 100644 --- a/deps/icu-small/source/i18n/japancal.cpp +++ b/deps/icu-small/source/i18n/japancal.cpp @@ -1,309 +1,309 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2003-2009,2012,2016 International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File JAPANCAL.CPP -* -* Modification History: -* 05/16/2003 srl copied from buddhcal.cpp -* -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING -#if U_PLATFORM_HAS_WINUWP_API == 0 -#include // getenv() is not available in UWP env -#else -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -# define VC_EXTRALEAN -# define NOUSER -# define NOSERVICE -# define NOIME -# define NOMCX -#include -#endif -#include "cmemory.h" -#include "erarules.h" -#include "japancal.h" -#include "unicode/gregocal.h" -#include "umutex.h" -#include "uassert.h" -#include "ucln_in.h" -#include "cstring.h" - -static icu::EraRules * gJapaneseEraRules = nullptr; -static icu::UInitOnce gJapaneseEraRulesInitOnce {}; -static int32_t gCurrentEra = 0; - -U_CDECL_BEGIN -static UBool japanese_calendar_cleanup(void) { - if (gJapaneseEraRules) { - delete gJapaneseEraRules; - gJapaneseEraRules = nullptr; - } - gCurrentEra = 0; - gJapaneseEraRulesInitOnce.reset(); - return true; -} -U_CDECL_END - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(JapaneseCalendar) - -static const int32_t kGregorianEpoch = 1970; // used as the default value of EXTENDED_YEAR -static const char* TENTATIVE_ERA_VAR_NAME = "ICU_ENABLE_TENTATIVE_ERA"; - - -// Export the following for use by test code. -UBool JapaneseCalendar::enableTentativeEra() { - // Although start date of next Japanese era is planned ahead, a name of - // new era might not be available. This implementation allows tester to - // check a new era without era names by settings below (in priority order). - // By default, such tentative era is disabled. - - // 1. Environment variable ICU_ENABLE_TENTATIVE_ERA=true or false - - UBool includeTentativeEra = false; - -#if U_PLATFORM_HAS_WINUWP_API == 1 - // UWP doesn't allow access to getenv(), but we can call GetEnvironmentVariableW to do the same thing. - UChar varName[26] = {}; - u_charsToUChars(TENTATIVE_ERA_VAR_NAME, varName, static_cast(uprv_strlen(TENTATIVE_ERA_VAR_NAME))); - WCHAR varValue[5] = {}; - DWORD ret = GetEnvironmentVariableW(reinterpret_cast(varName), varValue, UPRV_LENGTHOF(varValue)); - if ((ret == 4) && (_wcsicmp(varValue, L"true") == 0)) { - includeTentativeEra = true; - } -#else - char *envVarVal = getenv(TENTATIVE_ERA_VAR_NAME); - if (envVarVal != NULL && uprv_stricmp(envVarVal, "true") == 0) { - includeTentativeEra = true; - } -#endif - return includeTentativeEra; -} - - -// Initialize global Japanese era data -static void U_CALLCONV initializeEras(UErrorCode &status) { - gJapaneseEraRules = EraRules::createInstance("japanese", JapaneseCalendar::enableTentativeEra(), status); - if (U_FAILURE(status)) { - return; - } - gCurrentEra = gJapaneseEraRules->getCurrentEraIndex(); -} - -static void init(UErrorCode &status) { - umtx_initOnce(gJapaneseEraRulesInitOnce, &initializeEras, status); - ucln_i18n_registerCleanup(UCLN_I18N_JAPANESE_CALENDAR, japanese_calendar_cleanup); -} - -/* Some platforms don't like to export constants, like old Palm OS and some z/OS configurations. */ -uint32_t JapaneseCalendar::getCurrentEra() { - return gCurrentEra; -} - -JapaneseCalendar::JapaneseCalendar(const Locale& aLocale, UErrorCode& success) -: GregorianCalendar(aLocale, success) -{ - init(success); - setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. -} - -JapaneseCalendar::~JapaneseCalendar() -{ -} - -JapaneseCalendar::JapaneseCalendar(const JapaneseCalendar& source) -: GregorianCalendar(source) -{ - UErrorCode status = U_ZERO_ERROR; - init(status); - U_ASSERT(U_SUCCESS(status)); -} - -JapaneseCalendar& JapaneseCalendar::operator= ( const JapaneseCalendar& right) -{ - GregorianCalendar::operator=(right); - return *this; -} - -JapaneseCalendar* JapaneseCalendar::clone() const -{ - return new JapaneseCalendar(*this); -} - -const char *JapaneseCalendar::getType() const -{ - return "japanese"; -} - -int32_t JapaneseCalendar::getDefaultMonthInYear(int32_t eyear) -{ - int32_t era = internalGetEra(); - // TODO do we assume we can trust 'era'? What if it is denormalized? - - int32_t month = 0; - - // Find out if we are at the edge of an era - int32_t eraStart[3] = { 0,0,0 }; - UErrorCode status = U_ZERO_ERROR; - gJapaneseEraRules->getStartDate(era, eraStart, status); - U_ASSERT(U_SUCCESS(status)); - if(eyear == eraStart[0]) { - // Yes, we're in the first year of this era. - return eraStart[1] // month - -1; // return 0-based month - } - - return month; -} - -int32_t JapaneseCalendar::getDefaultDayInMonth(int32_t eyear, int32_t month) -{ - int32_t era = internalGetEra(); - int32_t day = 1; - - int32_t eraStart[3] = { 0,0,0 }; - UErrorCode status = U_ZERO_ERROR; - gJapaneseEraRules->getStartDate(era, eraStart, status); - U_ASSERT(U_SUCCESS(status)); - if(eyear == eraStart[0]) { - if(month == eraStart[1] - 1) { - return eraStart[2]; - } - } - - return day; -} - - -int32_t JapaneseCalendar::internalGetEra() const -{ - return internalGet(UCAL_ERA, gCurrentEra); -} - -int32_t JapaneseCalendar::handleGetExtendedYear() -{ - // EXTENDED_YEAR in JapaneseCalendar is a Gregorian year - // The default value of EXTENDED_YEAR is 1970 (Showa 45) - int32_t year; - - if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR && - newerField(UCAL_EXTENDED_YEAR, UCAL_ERA) == UCAL_EXTENDED_YEAR) { - year = internalGet(UCAL_EXTENDED_YEAR, kGregorianEpoch); - } else { - UErrorCode status = U_ZERO_ERROR; - int32_t eraStartYear = gJapaneseEraRules->getStartYear(internalGet(UCAL_ERA, gCurrentEra), status); - U_ASSERT(U_SUCCESS(status)); - - // extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc - year = internalGet(UCAL_YEAR, 1) // pin to minimum of year 1 (first year) - + eraStartYear // add gregorian starting year - - 1; // Subtract one because year starts at 1 - } - return year; -} - - -void JapaneseCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) -{ - //Calendar::timeToFields(theTime, quick, status); - GregorianCalendar::handleComputeFields(julianDay, status); - int32_t year = internalGet(UCAL_EXTENDED_YEAR); // Gregorian year - int32_t eraIdx = gJapaneseEraRules->getEraIndex(year, internalGet(UCAL_MONTH) + 1, internalGet(UCAL_DAY_OF_MONTH), status); - - internalSet(UCAL_ERA, eraIdx); - internalSet(UCAL_YEAR, year - gJapaneseEraRules->getStartYear(eraIdx, status) + 1); -} - -/* -Disable pivoting -*/ -UBool JapaneseCalendar::haveDefaultCentury() const -{ - return false; -} - -UDate JapaneseCalendar::defaultCenturyStart() const -{ - return 0;// WRONG -} - -int32_t JapaneseCalendar::defaultCenturyStartYear() const -{ - return 0; -} - -int32_t JapaneseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const -{ - switch(field) { - case UCAL_ERA: - if (limitType == UCAL_LIMIT_MINIMUM || limitType == UCAL_LIMIT_GREATEST_MINIMUM) { - return 0; - } - return gJapaneseEraRules->getNumberOfEras() - 1; // max known era, not gCurrentEra - case UCAL_YEAR: - { - switch (limitType) { - case UCAL_LIMIT_MINIMUM: - case UCAL_LIMIT_GREATEST_MINIMUM: - return 1; - case UCAL_LIMIT_LEAST_MAXIMUM: - return 1; - case UCAL_LIMIT_COUNT: //added to avoid warning - case UCAL_LIMIT_MAXIMUM: - { - UErrorCode status = U_ZERO_ERROR; - int32_t eraStartYear = gJapaneseEraRules->getStartYear(gCurrentEra, status); - U_ASSERT(U_SUCCESS(status)); - return GregorianCalendar::handleGetLimit(UCAL_YEAR, UCAL_LIMIT_MAXIMUM) - eraStartYear; - } - default: - return 1; // Error condition, invalid limitType - } - } - default: - return GregorianCalendar::handleGetLimit(field,limitType); - } -} - -int32_t JapaneseCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const { - if (field == UCAL_YEAR) { - int32_t era = get(UCAL_ERA, status); - if (U_FAILURE(status)) { - return 0; // error case... any value - } - if (era == gJapaneseEraRules->getNumberOfEras() - 1) { // max known era, not gCurrentEra - // TODO: Investigate what value should be used here - revisit after 4.0. - return handleGetLimit(UCAL_YEAR, UCAL_LIMIT_MAXIMUM); - } else { - int32_t nextEraStart[3] = { 0,0,0 }; - gJapaneseEraRules->getStartDate(era + 1, nextEraStart, status); - int32_t nextEraYear = nextEraStart[0]; - int32_t nextEraMonth = nextEraStart[1]; // 1-base - int32_t nextEraDate = nextEraStart[2]; - - int32_t eraStartYear = gJapaneseEraRules->getStartYear(era, status); - int32_t maxYear = nextEraYear - eraStartYear + 1; // 1-base - if (nextEraMonth == 1 && nextEraDate == 1) { - // Subtract 1, because the next era starts at Jan 1 - maxYear--; - } - return maxYear; - } - } - return GregorianCalendar::getActualMaximum(field, status); -} - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2003-2009,2012,2016 International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File JAPANCAL.CPP +* +* Modification History: +* 05/16/2003 srl copied from buddhcal.cpp +* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#if U_PLATFORM_HAS_WINUWP_API == 0 +#include // getenv() is not available in UWP env +#else +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +# define VC_EXTRALEAN +# define NOUSER +# define NOSERVICE +# define NOIME +# define NOMCX +#include +#endif +#include "cmemory.h" +#include "erarules.h" +#include "japancal.h" +#include "unicode/gregocal.h" +#include "umutex.h" +#include "uassert.h" +#include "ucln_in.h" +#include "cstring.h" + +static icu::EraRules * gJapaneseEraRules = nullptr; +static icu::UInitOnce gJapaneseEraRulesInitOnce {}; +static int32_t gCurrentEra = 0; + +U_CDECL_BEGIN +static UBool japanese_calendar_cleanup() { + if (gJapaneseEraRules) { + delete gJapaneseEraRules; + gJapaneseEraRules = nullptr; + } + gCurrentEra = 0; + gJapaneseEraRulesInitOnce.reset(); + return true; +} +U_CDECL_END + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(JapaneseCalendar) + +static const int32_t kGregorianEpoch = 1970; // used as the default value of EXTENDED_YEAR +static const char* TENTATIVE_ERA_VAR_NAME = "ICU_ENABLE_TENTATIVE_ERA"; + + +// Export the following for use by test code. +UBool JapaneseCalendar::enableTentativeEra() { + // Although start date of next Japanese era is planned ahead, a name of + // new era might not be available. This implementation allows tester to + // check a new era without era names by settings below (in priority order). + // By default, such tentative era is disabled. + + // 1. Environment variable ICU_ENABLE_TENTATIVE_ERA=true or false + + UBool includeTentativeEra = false; + +#if U_PLATFORM_HAS_WINUWP_API == 1 + // UWP doesn't allow access to getenv(), but we can call GetEnvironmentVariableW to do the same thing. + char16_t varName[26] = {}; + u_charsToUChars(TENTATIVE_ERA_VAR_NAME, varName, static_cast(uprv_strlen(TENTATIVE_ERA_VAR_NAME))); + WCHAR varValue[5] = {}; + DWORD ret = GetEnvironmentVariableW(reinterpret_cast(varName), varValue, UPRV_LENGTHOF(varValue)); + if ((ret == 4) && (_wcsicmp(varValue, L"true") == 0)) { + includeTentativeEra = true; + } +#else + char *envVarVal = getenv(TENTATIVE_ERA_VAR_NAME); + if (envVarVal != nullptr && uprv_stricmp(envVarVal, "true") == 0) { + includeTentativeEra = true; + } +#endif + return includeTentativeEra; +} + + +// Initialize global Japanese era data +static void U_CALLCONV initializeEras(UErrorCode &status) { + gJapaneseEraRules = EraRules::createInstance("japanese", JapaneseCalendar::enableTentativeEra(), status); + if (U_FAILURE(status)) { + return; + } + gCurrentEra = gJapaneseEraRules->getCurrentEraIndex(); +} + +static void init(UErrorCode &status) { + umtx_initOnce(gJapaneseEraRulesInitOnce, &initializeEras, status); + ucln_i18n_registerCleanup(UCLN_I18N_JAPANESE_CALENDAR, japanese_calendar_cleanup); +} + +/* Some platforms don't like to export constants, like old Palm OS and some z/OS configurations. */ +uint32_t JapaneseCalendar::getCurrentEra() { + return gCurrentEra; +} + +JapaneseCalendar::JapaneseCalendar(const Locale& aLocale, UErrorCode& success) +: GregorianCalendar(aLocale, success) +{ + init(success); + setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. +} + +JapaneseCalendar::~JapaneseCalendar() +{ +} + +JapaneseCalendar::JapaneseCalendar(const JapaneseCalendar& source) +: GregorianCalendar(source) +{ + UErrorCode status = U_ZERO_ERROR; + init(status); + U_ASSERT(U_SUCCESS(status)); +} + +JapaneseCalendar& JapaneseCalendar::operator= ( const JapaneseCalendar& right) +{ + GregorianCalendar::operator=(right); + return *this; +} + +JapaneseCalendar* JapaneseCalendar::clone() const +{ + return new JapaneseCalendar(*this); +} + +const char *JapaneseCalendar::getType() const +{ + return "japanese"; +} + +int32_t JapaneseCalendar::getDefaultMonthInYear(int32_t eyear) +{ + int32_t era = internalGetEra(); + // TODO do we assume we can trust 'era'? What if it is denormalized? + + int32_t month = 0; + + // Find out if we are at the edge of an era + int32_t eraStart[3] = { 0,0,0 }; + UErrorCode status = U_ZERO_ERROR; + gJapaneseEraRules->getStartDate(era, eraStart, status); + U_ASSERT(U_SUCCESS(status)); + if(eyear == eraStart[0]) { + // Yes, we're in the first year of this era. + return eraStart[1] // month + -1; // return 0-based month + } + + return month; +} + +int32_t JapaneseCalendar::getDefaultDayInMonth(int32_t eyear, int32_t month) +{ + int32_t era = internalGetEra(); + int32_t day = 1; + + int32_t eraStart[3] = { 0,0,0 }; + UErrorCode status = U_ZERO_ERROR; + gJapaneseEraRules->getStartDate(era, eraStart, status); + U_ASSERT(U_SUCCESS(status)); + if(eyear == eraStart[0]) { + if(month == eraStart[1] - 1) { + return eraStart[2]; + } + } + + return day; +} + + +int32_t JapaneseCalendar::internalGetEra() const +{ + return internalGet(UCAL_ERA, gCurrentEra); +} + +int32_t JapaneseCalendar::handleGetExtendedYear() +{ + // EXTENDED_YEAR in JapaneseCalendar is a Gregorian year + // The default value of EXTENDED_YEAR is 1970 (Showa 45) + int32_t year; + + if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR && + newerField(UCAL_EXTENDED_YEAR, UCAL_ERA) == UCAL_EXTENDED_YEAR) { + year = internalGet(UCAL_EXTENDED_YEAR, kGregorianEpoch); + } else { + UErrorCode status = U_ZERO_ERROR; + int32_t eraStartYear = gJapaneseEraRules->getStartYear(internalGet(UCAL_ERA, gCurrentEra), status); + U_ASSERT(U_SUCCESS(status)); + + // extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc + year = internalGet(UCAL_YEAR, 1) // pin to minimum of year 1 (first year) + + eraStartYear // add gregorian starting year + - 1; // Subtract one because year starts at 1 + } + return year; +} + + +void JapaneseCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) +{ + //Calendar::timeToFields(theTime, quick, status); + GregorianCalendar::handleComputeFields(julianDay, status); + int32_t year = internalGet(UCAL_EXTENDED_YEAR); // Gregorian year + int32_t eraIdx = gJapaneseEraRules->getEraIndex(year, internalGetMonth() + 1, internalGet(UCAL_DAY_OF_MONTH), status); + + internalSet(UCAL_ERA, eraIdx); + internalSet(UCAL_YEAR, year - gJapaneseEraRules->getStartYear(eraIdx, status) + 1); +} + +/* +Disable pivoting +*/ +UBool JapaneseCalendar::haveDefaultCentury() const +{ + return false; +} + +UDate JapaneseCalendar::defaultCenturyStart() const +{ + return 0;// WRONG +} + +int32_t JapaneseCalendar::defaultCenturyStartYear() const +{ + return 0; +} + +int32_t JapaneseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const +{ + switch(field) { + case UCAL_ERA: + if (limitType == UCAL_LIMIT_MINIMUM || limitType == UCAL_LIMIT_GREATEST_MINIMUM) { + return 0; + } + return gJapaneseEraRules->getNumberOfEras() - 1; // max known era, not gCurrentEra + case UCAL_YEAR: + { + switch (limitType) { + case UCAL_LIMIT_MINIMUM: + case UCAL_LIMIT_GREATEST_MINIMUM: + return 1; + case UCAL_LIMIT_LEAST_MAXIMUM: + return 1; + case UCAL_LIMIT_COUNT: //added to avoid warning + case UCAL_LIMIT_MAXIMUM: + { + UErrorCode status = U_ZERO_ERROR; + int32_t eraStartYear = gJapaneseEraRules->getStartYear(gCurrentEra, status); + U_ASSERT(U_SUCCESS(status)); + return GregorianCalendar::handleGetLimit(UCAL_YEAR, UCAL_LIMIT_MAXIMUM) - eraStartYear; + } + default: + return 1; // Error condition, invalid limitType + } + } + default: + return GregorianCalendar::handleGetLimit(field,limitType); + } +} + +int32_t JapaneseCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const { + if (field == UCAL_YEAR) { + int32_t era = get(UCAL_ERA, status); + if (U_FAILURE(status)) { + return 0; // error case... any value + } + if (era == gJapaneseEraRules->getNumberOfEras() - 1) { // max known era, not gCurrentEra + // TODO: Investigate what value should be used here - revisit after 4.0. + return handleGetLimit(UCAL_YEAR, UCAL_LIMIT_MAXIMUM); + } else { + int32_t nextEraStart[3] = { 0,0,0 }; + gJapaneseEraRules->getStartDate(era + 1, nextEraStart, status); + int32_t nextEraYear = nextEraStart[0]; + int32_t nextEraMonth = nextEraStart[1]; // 1-base + int32_t nextEraDate = nextEraStart[2]; + + int32_t eraStartYear = gJapaneseEraRules->getStartYear(era, status); + int32_t maxYear = nextEraYear - eraStartYear + 1; // 1-base + if (nextEraMonth == 1 && nextEraDate == 1) { + // Subtract 1, because the next era starts at Jan 1 + maxYear--; + } + return maxYear; + } + } + return GregorianCalendar::getActualMaximum(field, status); +} + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/i18n/japancal.h b/deps/icu-small/source/i18n/japancal.h index 88513440528b04..5b1fea05145aae 100644 --- a/deps/icu-small/source/i18n/japancal.h +++ b/deps/icu-small/source/i18n/japancal.h @@ -1,234 +1,234 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ******************************************************************************** - * Copyright (C) 2003-2008, International Business Machines Corporation - * and others. All Rights Reserved. - ******************************************************************************** - * - * File JAPANCAL.H - * - * Modification History: - * - * Date Name Description - * 05/13/2003 srl copied from gregocal.h - ******************************************************************************** - */ - -#ifndef JAPANCAL_H -#define JAPANCAL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/calendar.h" -#include "unicode/gregocal.h" - -U_NAMESPACE_BEGIN - -/** - * Concrete class which provides the Japanese calendar. - *

- * JapaneseCalendar is a subclass of GregorianCalendar - * that numbers years and eras based on the reigns of the Japanese emperors. - * The Japanese calendar is identical to the Gregorian calendar in all respects - * except for the year and era. The ascension of each emperor to the throne - * begins a new era, and the years of that era are numbered starting with the - * year of ascension as year 1. - *

- * Note that in the year of an imperial ascension, there are two possible sets - * of year and era values: that for the old era and for the new. For example, a - * new era began on January 7, 1989 AD. Strictly speaking, the first six days - * of that year were in the Showa era, e.g. "January 6, 64 Showa", while the rest - * of the year was in the Heisei era, e.g. "January 7, 1 Heisei". This class - * handles this distinction correctly when computing dates. However, in lenient - * mode either form of date is acceptable as input. - *

- * In modern times, eras have started on January 8, 1868 AD, Gregorian (Meiji), - * July 30, 1912 (Taisho), December 25, 1926 (Showa), and January 7, 1989 (Heisei). Constants - * for these eras, suitable for use in the UCAL_ERA field, are provided - * in this class. Note that the number used for each era is more or - * less arbitrary. Currently, the era starting in 645 AD is era #0; however this - * may change in the future. Use the predefined constants rather than using actual, - * absolute numbers. - *

- * Since ICU4C 63, start date of each era is imported from CLDR. CLDR era data - * may contain tentative era in near future with placeholder names. By default, - * such era data is not enabled. ICU4C users who want to test the behavior of - * the future era can enable this one of following settings (in the priority - * order): - *

    - *
  1. Environment variable ICU_ENABLE_TENTATIVE_ERA=true.
  2. - * - * @internal - */ -class JapaneseCalendar : public GregorianCalendar { -public: - - /** - * Check environment variable. - * @internal - */ - U_I18N_API static UBool U_EXPORT2 enableTentativeEra(void); - - /** - * Useful constants for JapaneseCalendar. - * Exported for use by test code. - * @internal - */ - U_I18N_API static uint32_t U_EXPORT2 getCurrentEra(void); // the current era - - /** - * Constructs a JapaneseCalendar based on the current time in the default time zone - * with the given locale. - * - * @param aLocale The given locale. - * @param success Indicates the status of JapaneseCalendar object construction. - * Returns U_ZERO_ERROR if constructed successfully. - * @stable ICU 2.0 - */ - JapaneseCalendar(const Locale& aLocale, UErrorCode& success); - - - /** - * Destructor - * @internal - */ - virtual ~JapaneseCalendar(); - - /** - * Copy constructor - * @param source the object to be copied. - * @internal - */ - JapaneseCalendar(const JapaneseCalendar& source); - - /** - * Default assignment operator - * @param right the object to be copied. - * @internal - */ - JapaneseCalendar& operator=(const JapaneseCalendar& right); - - /** - * Create and return a polymorphic copy of this calendar. - * @return return a polymorphic copy of this calendar. - * @internal - */ - virtual JapaneseCalendar* clone() const override; - - /** - * Return the extended year defined by the current fields. In the - * Japanese calendar case, this is equal to the equivalent extended Gregorian year. - * @internal - */ - virtual int32_t handleGetExtendedYear() override; - - /** - * Return the maximum value that this field could have, given the current date. - * @internal - */ - virtual int32_t getActualMaximum(UCalendarDateFields field, UErrorCode& status) const override; - - -public: - /** - * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual - * override. This method is to implement a simple version of RTTI, since not all C++ - * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call - * this method. - * - * @return The class ID for this object. All objects of a given class have the - * same class ID. Objects of other classes have different class IDs. - * @internal - */ - virtual UClassID getDynamicClassID(void) const override; - - /** - * Return the class ID for this class. This is useful only for comparing to a return - * value from getDynamicClassID(). For example: - * - * Base* polymorphic_pointer = createPolymorphicObject(); - * if (polymorphic_pointer->getDynamicClassID() == - * Derived::getStaticClassID()) ... - * - * @return The class ID for all objects of this class. - * @internal - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * return the calendar type, "japanese". - * - * @return calendar type - * @internal - */ - virtual const char * getType() const override; - - /** - * @return false - no default century in Japanese - * @internal - */ - virtual UBool haveDefaultCentury() const override; - - /** - * Not used - no default century. - * @internal - */ - virtual UDate defaultCenturyStart() const override; - /** - * Not used - no default century. - * @internal - */ - virtual int32_t defaultCenturyStartYear() const override; - -private: - JapaneseCalendar(); // default constructor not implemented - -protected: - /** - * Calculate the era for internal computation - * @internal - */ - virtual int32_t internalGetEra() const override; - - /** - * Compute fields from the JD - * @internal - */ - virtual void handleComputeFields(int32_t julianDay, UErrorCode& status) override; - - /** - * Calculate the limit for a specified type of limit and field - * @internal - */ - virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; - - /*** - * Called by computeJulianDay. Returns the default month (0-based) for the year, - * taking year and era into account. Will return the first month of the given era, if - * the current year is an ascension year. - * @param eyear the extended year - * @internal - */ - virtual int32_t getDefaultMonthInYear(int32_t eyear) override; - - /*** - * Called by computeJulianDay. Returns the default day (1-based) for the month, - * taking currently-set year and era into account. Will return the first day of the given - * era, if the current month is an ascension year and month. - * @param eyear the extended year - * @param mon the month in the year - * @internal - */ - virtual int32_t getDefaultDayInMonth(int32_t eyear, int32_t month) override; -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -#endif -//eof - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ******************************************************************************** + * Copyright (C) 2003-2008, International Business Machines Corporation + * and others. All Rights Reserved. + ******************************************************************************** + * + * File JAPANCAL.H + * + * Modification History: + * + * Date Name Description + * 05/13/2003 srl copied from gregocal.h + ******************************************************************************** + */ + +#ifndef JAPANCAL_H +#define JAPANCAL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" +#include "unicode/gregocal.h" + +U_NAMESPACE_BEGIN + +/** + * Concrete class which provides the Japanese calendar. + *

    + * JapaneseCalendar is a subclass of GregorianCalendar + * that numbers years and eras based on the reigns of the Japanese emperors. + * The Japanese calendar is identical to the Gregorian calendar in all respects + * except for the year and era. The ascension of each emperor to the throne + * begins a new era, and the years of that era are numbered starting with the + * year of ascension as year 1. + *

    + * Note that in the year of an imperial ascension, there are two possible sets + * of year and era values: that for the old era and for the new. For example, a + * new era began on January 7, 1989 AD. Strictly speaking, the first six days + * of that year were in the Showa era, e.g. "January 6, 64 Showa", while the rest + * of the year was in the Heisei era, e.g. "January 7, 1 Heisei". This class + * handles this distinction correctly when computing dates. However, in lenient + * mode either form of date is acceptable as input. + *

    + * In modern times, eras have started on January 8, 1868 AD, Gregorian (Meiji), + * July 30, 1912 (Taisho), December 25, 1926 (Showa), and January 7, 1989 (Heisei). Constants + * for these eras, suitable for use in the UCAL_ERA field, are provided + * in this class. Note that the number used for each era is more or + * less arbitrary. Currently, the era starting in 645 AD is era #0; however this + * may change in the future. Use the predefined constants rather than using actual, + * absolute numbers. + *

    + * Since ICU4C 63, start date of each era is imported from CLDR. CLDR era data + * may contain tentative era in near future with placeholder names. By default, + * such era data is not enabled. ICU4C users who want to test the behavior of + * the future era can enable this one of following settings (in the priority + * order): + *

      + *
    1. Environment variable ICU_ENABLE_TENTATIVE_ERA=true.
    2. + * + * @internal + */ +class JapaneseCalendar : public GregorianCalendar { +public: + + /** + * Check environment variable. + * @internal + */ + U_I18N_API static UBool U_EXPORT2 enableTentativeEra(); + + /** + * Useful constants for JapaneseCalendar. + * Exported for use by test code. + * @internal + */ + U_I18N_API static uint32_t U_EXPORT2 getCurrentEra(); // the current era + + /** + * Constructs a JapaneseCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of JapaneseCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @stable ICU 2.0 + */ + JapaneseCalendar(const Locale& aLocale, UErrorCode& success); + + + /** + * Destructor + * @internal + */ + virtual ~JapaneseCalendar(); + + /** + * Copy constructor + * @param source the object to be copied. + * @internal + */ + JapaneseCalendar(const JapaneseCalendar& source); + + /** + * Default assignment operator + * @param right the object to be copied. + * @internal + */ + JapaneseCalendar& operator=(const JapaneseCalendar& right); + + /** + * Create and return a polymorphic copy of this calendar. + * @return return a polymorphic copy of this calendar. + * @internal + */ + virtual JapaneseCalendar* clone() const override; + + /** + * Return the extended year defined by the current fields. In the + * Japanese calendar case, this is equal to the equivalent extended Gregorian year. + * @internal + */ + virtual int32_t handleGetExtendedYear() override; + + /** + * Return the maximum value that this field could have, given the current date. + * @internal + */ + virtual int32_t getActualMaximum(UCalendarDateFields field, UErrorCode& status) const override; + + +public: + /** + * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual + * override. This method is to implement a simple version of RTTI, since not all C++ + * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call + * this method. + * + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + + /** + * return the calendar type, "japanese". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + /** + * @return false - no default century in Japanese + * @internal + */ + virtual UBool haveDefaultCentury() const override; + + /** + * Not used - no default century. + * @internal + */ + virtual UDate defaultCenturyStart() const override; + /** + * Not used - no default century. + * @internal + */ + virtual int32_t defaultCenturyStartYear() const override; + +private: + JapaneseCalendar(); // default constructor not implemented + +protected: + /** + * Calculate the era for internal computation + * @internal + */ + virtual int32_t internalGetEra() const override; + + /** + * Compute fields from the JD + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode& status) override; + + /** + * Calculate the limit for a specified type of limit and field + * @internal + */ + virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; + + /*** + * Called by computeJulianDay. Returns the default month (0-based) for the year, + * taking year and era into account. Will return the first month of the given era, if + * the current year is an ascension year. + * @param eyear the extended year + * @internal + */ + virtual int32_t getDefaultMonthInYear(int32_t eyear) override; + + /*** + * Called by computeJulianDay. Returns the default day (1-based) for the month, + * taking currently-set year and era into account. Will return the first day of the given + * era, if the current month is an ascension year and month. + * @param eyear the extended year + * @param mon the month in the year + * @internal + */ + virtual int32_t getDefaultDayInMonth(int32_t eyear, int32_t month) override; +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif +//eof + diff --git a/deps/icu-small/source/i18n/listformatter.cpp b/deps/icu-small/source/i18n/listformatter.cpp index 4142fa461dada8..19e0928862da13 100644 --- a/deps/icu-small/source/i18n/listformatter.cpp +++ b/deps/icu-small/source/i18n/listformatter.cpp @@ -1,739 +1,732 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* -* Copyright (C) 2013-2016, International Business Machines -* Corporation and others. All Rights Reserved. -* -******************************************************************************* -* file name: listformatter.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* created on: 2012aug27 -* created by: Umesh P. Nair -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "cmemory.h" -#include "unicode/fpositer.h" // FieldPositionIterator -#include "unicode/listformatter.h" -#include "unicode/simpleformatter.h" -#include "unicode/ulistformatter.h" -#include "unicode/uscript.h" -#include "fphdlimp.h" -#include "mutex.h" -#include "hash.h" -#include "cstring.h" -#include "uarrsort.h" -#include "ulocimp.h" -#include "charstr.h" -#include "ucln_in.h" -#include "uresimp.h" -#include "resource.h" -#include "formattedval_impl.h" - -U_NAMESPACE_BEGIN - -namespace { - -class PatternHandler : public UObject { -public: - PatternHandler(const UnicodeString& two, const UnicodeString& end, UErrorCode& errorCode) : - twoPattern(two, 2, 2, errorCode), - endPattern(end, 2, 2, errorCode) { } - - PatternHandler(const SimpleFormatter& two, const SimpleFormatter& end) : - twoPattern(two), - endPattern(end) { } - - virtual ~PatternHandler(); - - virtual PatternHandler* clone() const { return new PatternHandler(twoPattern, endPattern); } - - /** Argument: final string in the list. */ - virtual const SimpleFormatter& getTwoPattern(const UnicodeString&) const { - return twoPattern; - } - - /** Argument: final string in the list. */ - virtual const SimpleFormatter& getEndPattern(const UnicodeString&) const { - return endPattern; - } - -protected: - SimpleFormatter twoPattern; - SimpleFormatter endPattern; -}; - -PatternHandler::~PatternHandler() { -} - -class ContextualHandler : public PatternHandler { -public: - ContextualHandler(bool (*testFunc)(const UnicodeString& text), - const UnicodeString& thenTwo, - const UnicodeString& elseTwo, - const UnicodeString& thenEnd, - const UnicodeString& elseEnd, - UErrorCode& errorCode) : - PatternHandler(elseTwo, elseEnd, errorCode), - test(testFunc), - thenTwoPattern(thenTwo, 2, 2, errorCode), - thenEndPattern(thenEnd, 2, 2, errorCode) { } - - ContextualHandler(bool (*testFunc)(const UnicodeString& text), - const SimpleFormatter& thenTwo, SimpleFormatter elseTwo, - const SimpleFormatter& thenEnd, SimpleFormatter elseEnd) : - PatternHandler(elseTwo, elseEnd), - test(testFunc), - thenTwoPattern(thenTwo), - thenEndPattern(thenEnd) { } - - ~ContextualHandler() override; - - PatternHandler* clone() const override { - return new ContextualHandler( - test, thenTwoPattern, twoPattern, thenEndPattern, endPattern); - } - - const SimpleFormatter& getTwoPattern( - const UnicodeString& text) const override { - return (test)(text) ? thenTwoPattern : twoPattern; - } - - const SimpleFormatter& getEndPattern( - const UnicodeString& text) const override { - return (test)(text) ? thenEndPattern : endPattern; - } - -private: - bool (*test)(const UnicodeString&); - SimpleFormatter thenTwoPattern; - SimpleFormatter thenEndPattern; -}; - -ContextualHandler::~ContextualHandler() { -} - -static const char16_t *spanishY = u"{0} y {1}"; -static const char16_t *spanishE = u"{0} e {1}"; -static const char16_t *spanishO = u"{0} o {1}"; -static const char16_t *spanishU = u"{0} u {1}"; -static const char16_t *hebrewVav = u"{0} \u05D5{1}"; -static const char16_t *hebrewVavDash = u"{0} \u05D5-{1}"; - -// Condiction to change to e. -// Starts with "hi" or "i" but not with "hie" nor "hia" -static bool shouldChangeToE(const UnicodeString& text) { - int32_t len = text.length(); - if (len == 0) { return false; } - // Case insensitive match hi but not hie nor hia. - if ((text[0] == u'h' || text[0] == u'H') && - ((len > 1) && (text[1] == u'i' || text[1] == u'I')) && - ((len == 2) || !(text[2] == u'a' || text[2] == u'A' || text[2] == u'e' || text[2] == u'E'))) { - return true; - } - // Case insensitive for "start with i" - if (text[0] == u'i' || text[0] == u'I') { return true; } - return false; -} - -// Condiction to change to u. -// Starts with "o", "ho", and "8". Also "11" by itself. -// re: ^((o|ho|8).*|11)$ -static bool shouldChangeToU(const UnicodeString& text) { - int32_t len = text.length(); - if (len == 0) { return false; } - // Case insensitive match o.* and 8.* - if (text[0] == u'o' || text[0] == u'O' || text[0] == u'8') { return true; } - // Case insensitive match ho.* - if ((text[0] == u'h' || text[0] == u'H') && - ((len > 1) && (text[1] == 'o' || text[1] == u'O'))) { - return true; - } - // match "^11$" and "^11 .*" - if ((len >= 2) && text[0] == u'1' && text[1] == u'1' && (len == 2 || text[2] == u' ')) { return true; } - return false; -} - -// Condiction to change to VAV follow by a dash. -// Starts with non Hebrew letter. -static bool shouldChangeToVavDash(const UnicodeString& text) { - if (text.isEmpty()) { return false; } - UErrorCode status = U_ZERO_ERROR; - return uscript_getScript(text.char32At(0), &status) != USCRIPT_HEBREW; -} - -PatternHandler* createPatternHandler( - const char* lang, const UnicodeString& two, const UnicodeString& end, - UErrorCode& status) { - if (uprv_strcmp(lang, "es") == 0) { - // Spanish - UnicodeString spanishYStr(true, spanishY, -1); - bool twoIsY = two == spanishYStr; - bool endIsY = end == spanishYStr; - if (twoIsY || endIsY) { - UnicodeString replacement(true, spanishE, -1); - return new ContextualHandler( - shouldChangeToE, - twoIsY ? replacement : two, two, - endIsY ? replacement : end, end, status); - } - UnicodeString spanishOStr(true, spanishO, -1); - bool twoIsO = two == spanishOStr; - bool endIsO = end == spanishOStr; - if (twoIsO || endIsO) { - UnicodeString replacement(true, spanishU, -1); - return new ContextualHandler( - shouldChangeToU, - twoIsO ? replacement : two, two, - endIsO ? replacement : end, end, status); - } - } else if (uprv_strcmp(lang, "he") == 0 || uprv_strcmp(lang, "iw") == 0) { - // Hebrew - UnicodeString hebrewVavStr(true, hebrewVav, -1); - bool twoIsVav = two == hebrewVavStr; - bool endIsVav = end == hebrewVavStr; - if (twoIsVav || endIsVav) { - UnicodeString replacement(true, hebrewVavDash, -1); - return new ContextualHandler( - shouldChangeToVavDash, - twoIsVav ? replacement : two, two, - endIsVav ? replacement : end, end, status); - } - } - return new PatternHandler(two, end, status); -} - -} // namespace - -struct ListFormatInternal : public UMemory { - SimpleFormatter startPattern; - SimpleFormatter middlePattern; - LocalPointer patternHandler; - -ListFormatInternal( - const UnicodeString& two, - const UnicodeString& start, - const UnicodeString& middle, - const UnicodeString& end, - const Locale& locale, - UErrorCode &errorCode) : - startPattern(start, 2, 2, errorCode), - middlePattern(middle, 2, 2, errorCode), - patternHandler(createPatternHandler(locale.getLanguage(), two, end, errorCode), errorCode) { } - -ListFormatInternal(const ListFormatData &data, UErrorCode &errorCode) : - startPattern(data.startPattern, errorCode), - middlePattern(data.middlePattern, errorCode), - patternHandler(createPatternHandler( - data.locale.getLanguage(), data.twoPattern, data.endPattern, errorCode), errorCode) { } - -ListFormatInternal(const ListFormatInternal &other) : - startPattern(other.startPattern), - middlePattern(other.middlePattern), - patternHandler(other.patternHandler->clone()) { } -}; - - -class FormattedListData : public FormattedValueStringBuilderImpl { -public: - FormattedListData(UErrorCode&) : FormattedValueStringBuilderImpl(kUndefinedField) {} - virtual ~FormattedListData(); -}; - -FormattedListData::~FormattedListData() = default; - -UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedList) - - -static Hashtable* listPatternHash = nullptr; - -U_CDECL_BEGIN -static UBool U_CALLCONV uprv_listformatter_cleanup() { - delete listPatternHash; - listPatternHash = nullptr; - return true; -} - -static void U_CALLCONV -uprv_deleteListFormatInternal(void *obj) { - delete static_cast(obj); -} - -U_CDECL_END - -ListFormatter::ListFormatter(const ListFormatter& other) : - owned(other.owned), data(other.data) { - if (other.owned != nullptr) { - owned = new ListFormatInternal(*other.owned); - data = owned; - } -} - -ListFormatter& ListFormatter::operator=(const ListFormatter& other) { - if (this == &other) { - return *this; - } - delete owned; - if (other.owned) { - owned = new ListFormatInternal(*other.owned); - data = owned; - } else { - owned = nullptr; - data = other.data; - } - return *this; -} - -void ListFormatter::initializeHash(UErrorCode& errorCode) { - if (U_FAILURE(errorCode)) { - return; - } - - listPatternHash = new Hashtable(); - if (listPatternHash == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - - listPatternHash->setValueDeleter(uprv_deleteListFormatInternal); - ucln_i18n_registerCleanup(UCLN_I18N_LIST_FORMATTER, uprv_listformatter_cleanup); - -} - -const ListFormatInternal* ListFormatter::getListFormatInternal( - const Locale& locale, const char *style, UErrorCode& errorCode) { - if (U_FAILURE(errorCode)) { - return nullptr; - } - CharString keyBuffer(locale.getName(), errorCode); - keyBuffer.append(':', errorCode).append(style, errorCode); - UnicodeString key(keyBuffer.data(), -1, US_INV); - ListFormatInternal* result = nullptr; - static UMutex listFormatterMutex; - { - Mutex m(&listFormatterMutex); - if (listPatternHash == nullptr) { - initializeHash(errorCode); - if (U_FAILURE(errorCode)) { - return nullptr; - } - } - result = static_cast(listPatternHash->get(key)); - } - if (result != nullptr) { - return result; - } - result = loadListFormatInternal(locale, style, errorCode); - if (U_FAILURE(errorCode)) { - return nullptr; - } - - { - Mutex m(&listFormatterMutex); - ListFormatInternal* temp = static_cast(listPatternHash->get(key)); - if (temp != nullptr) { - delete result; - result = temp; - } else { - listPatternHash->put(key, result, errorCode); - if (U_FAILURE(errorCode)) { - return nullptr; - } - } - } - return result; -} - -static const char* typeWidthToStyleString(UListFormatterType type, UListFormatterWidth width) { - switch (type) { - case ULISTFMT_TYPE_AND: - switch (width) { - case ULISTFMT_WIDTH_WIDE: - return "standard"; - case ULISTFMT_WIDTH_SHORT: - return "standard-short"; - case ULISTFMT_WIDTH_NARROW: - return "standard-narrow"; - default: - return nullptr; - } - break; - - case ULISTFMT_TYPE_OR: - switch (width) { - case ULISTFMT_WIDTH_WIDE: - return "or"; - case ULISTFMT_WIDTH_SHORT: - return "or-short"; - case ULISTFMT_WIDTH_NARROW: - return "or-narrow"; - default: - return nullptr; - } - break; - - case ULISTFMT_TYPE_UNITS: - switch (width) { - case ULISTFMT_WIDTH_WIDE: - return "unit"; - case ULISTFMT_WIDTH_SHORT: - return "unit-short"; - case ULISTFMT_WIDTH_NARROW: - return "unit-narrow"; - default: - return nullptr; - } - } - - return nullptr; -} - -static const UChar solidus = 0x2F; -static const UChar aliasPrefix[] = { 0x6C,0x69,0x73,0x74,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2F }; // "listPattern/" -enum { - kAliasPrefixLen = UPRV_LENGTHOF(aliasPrefix), - kStyleLenMax = 24 // longest currently is 14 -}; - -struct ListFormatter::ListPatternsSink : public ResourceSink { - UnicodeString two, start, middle, end; -#if ((U_PLATFORM == U_PF_AIX) || (U_PLATFORM == U_PF_OS390)) && (U_CPLUSPLUS_VERSION < 11) - char aliasedStyle[kStyleLenMax+1]; - ListPatternsSink() { - uprv_memset(aliasedStyle, 0, kStyleLenMax+1); - } -#else - char aliasedStyle[kStyleLenMax+1] = {0}; - - ListPatternsSink() {} -#endif - virtual ~ListPatternsSink(); - - void setAliasedStyle(UnicodeString alias) { - int32_t startIndex = alias.indexOf(aliasPrefix, kAliasPrefixLen, 0); - if (startIndex < 0) { - return; - } - startIndex += kAliasPrefixLen; - int32_t endIndex = alias.indexOf(solidus, startIndex); - if (endIndex < 0) { - endIndex = alias.length(); - } - alias.extract(startIndex, endIndex-startIndex, aliasedStyle, kStyleLenMax+1, US_INV); - aliasedStyle[kStyleLenMax] = 0; - } - - void handleValueForPattern(ResourceValue &value, UnicodeString &pattern, UErrorCode &errorCode) { - if (pattern.isEmpty()) { - if (value.getType() == URES_ALIAS) { - if (aliasedStyle[0] == 0) { - setAliasedStyle(value.getAliasUnicodeString(errorCode)); - } - } else { - pattern = value.getUnicodeString(errorCode); - } - } - } - - virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, - UErrorCode &errorCode) override { - aliasedStyle[0] = 0; - if (value.getType() == URES_ALIAS) { - setAliasedStyle(value.getAliasUnicodeString(errorCode)); - return; - } - ResourceTable listPatterns = value.getTable(errorCode); - for (int i = 0; U_SUCCESS(errorCode) && listPatterns.getKeyAndValue(i, key, value); ++i) { - if (uprv_strcmp(key, "2") == 0) { - handleValueForPattern(value, two, errorCode); - } else if (uprv_strcmp(key, "end") == 0) { - handleValueForPattern(value, end, errorCode); - } else if (uprv_strcmp(key, "middle") == 0) { - handleValueForPattern(value, middle, errorCode); - } else if (uprv_strcmp(key, "start") == 0) { - handleValueForPattern(value, start, errorCode); - } - } - } -}; - -// Virtual destructors must be defined out of line. -ListFormatter::ListPatternsSink::~ListPatternsSink() {} - -ListFormatInternal* ListFormatter::loadListFormatInternal( - const Locale& locale, const char * style, UErrorCode& errorCode) { - UResourceBundle* rb = ures_open(nullptr, locale.getName(), &errorCode); - rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode); - if (U_FAILURE(errorCode)) { - ures_close(rb); - return nullptr; - } - ListFormatter::ListPatternsSink sink; - char currentStyle[kStyleLenMax+1]; - uprv_strncpy(currentStyle, style, kStyleLenMax); - currentStyle[kStyleLenMax] = 0; - - for (;;) { - ures_getAllItemsWithFallback(rb, currentStyle, sink, errorCode); - if (U_FAILURE(errorCode) || sink.aliasedStyle[0] == 0 || uprv_strcmp(currentStyle, sink.aliasedStyle) == 0) { - break; - } - uprv_strcpy(currentStyle, sink.aliasedStyle); - } - ures_close(rb); - if (U_FAILURE(errorCode)) { - return nullptr; - } - if (sink.two.isEmpty() || sink.start.isEmpty() || sink.middle.isEmpty() || sink.end.isEmpty()) { - errorCode = U_MISSING_RESOURCE_ERROR; - return nullptr; - } - - ListFormatInternal* result = new ListFormatInternal(sink.two, sink.start, sink.middle, sink.end, locale, errorCode); - if (result == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - if (U_FAILURE(errorCode)) { - delete result; - return nullptr; - } - return result; -} - -ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) { - Locale locale; // The default locale. - return createInstance(locale, errorCode); -} - -ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) { - return createInstance(locale, ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_WIDE, errorCode); -} - -ListFormatter* ListFormatter::createInstance( - const Locale& locale, UListFormatterType type, UListFormatterWidth width, UErrorCode& errorCode) { - const char* style = typeWidthToStyleString(type, width); - if (style == nullptr) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - return createInstance(locale, style, errorCode); -} - -ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) { - const ListFormatInternal* listFormatInternal = getListFormatInternal(locale, style, errorCode); - if (U_FAILURE(errorCode)) { - return nullptr; - } - ListFormatter* p = new ListFormatter(listFormatInternal); - if (p == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - return p; -} - -ListFormatter::ListFormatter(const ListFormatData& listFormatData, UErrorCode &errorCode) { - owned = new ListFormatInternal(listFormatData, errorCode); - data = owned; -} - -ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(nullptr), data(listFormatterInternal) { -} - -ListFormatter::~ListFormatter() { - delete owned; -} - -namespace { - -class FormattedListBuilder { -public: - LocalPointer data; - - /** For lists of length 1+ */ - FormattedListBuilder(const UnicodeString& start, UErrorCode& status) - : data(new FormattedListData(status), status) { - if (U_SUCCESS(status)) { - data->getStringRef().append( - start, - {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD}, - status); - data->appendSpanInfo(UFIELD_CATEGORY_LIST_SPAN, 0, -1, start.length(), status); - } - } - - /** For lists of length 0 */ - FormattedListBuilder(UErrorCode& status) - : data(new FormattedListData(status), status) { - } - - void append(const SimpleFormatter& pattern, const UnicodeString& next, int32_t position, UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - if (pattern.getArgumentLimit() != 2) { - status = U_INTERNAL_PROGRAM_ERROR; - return; - } - // In the pattern, {0} are the pre-existing elements and {1} is the new element. - int32_t offsets[] = {0, 0}; - UnicodeString temp = pattern.getTextWithNoArguments(offsets, 2); - if (offsets[0] <= offsets[1]) { - // prefix{0}infix{1}suffix - // Prepend prefix, then append infix, element, and suffix - data->getStringRef().insert( - 0, - temp.tempSubStringBetween(0, offsets[0]), - {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD}, - status); - data->getStringRef().append( - temp.tempSubStringBetween(offsets[0], offsets[1]), - {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD}, - status); - data->getStringRef().append( - next, - {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD}, - status); - data->appendSpanInfo(UFIELD_CATEGORY_LIST_SPAN, position, -1, next.length(), status); - data->getStringRef().append( - temp.tempSubString(offsets[1]), - {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD}, - status); - } else { - // prefix{1}infix{0}suffix - // Prepend infix, element, and prefix, then append suffix. - // (We prepend in reverse order because prepending at index 0 is fast.) - data->getStringRef().insert( - 0, - temp.tempSubStringBetween(offsets[1], offsets[0]), - {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD}, - status); - data->getStringRef().insert( - 0, - next, - {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD}, - status); - data->prependSpanInfo(UFIELD_CATEGORY_LIST_SPAN, position, -1, next.length(), status); - data->getStringRef().insert( - 0, - temp.tempSubStringBetween(0, offsets[1]), - {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD}, - status); - data->getStringRef().append( - temp.tempSubString(offsets[0]), - {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD}, - status); - } - } -}; - -} - -UnicodeString& ListFormatter::format( - const UnicodeString items[], - int32_t nItems, - UnicodeString& appendTo, - UErrorCode& errorCode) const { - int32_t offset; - return format(items, nItems, appendTo, -1, offset, errorCode); -} - -UnicodeString& ListFormatter::format( - const UnicodeString items[], - int32_t nItems, - UnicodeString& appendTo, - int32_t index, - int32_t &offset, - UErrorCode& errorCode) const { - int32_t initialOffset = appendTo.length(); - auto result = formatStringsToValue(items, nItems, errorCode); - UnicodeStringAppendable appendable(appendTo); - result.appendTo(appendable, errorCode); - if (index >= 0) { - ConstrainedFieldPosition cfpos; - cfpos.constrainField(UFIELD_CATEGORY_LIST_SPAN, index); - result.nextPosition(cfpos, errorCode); - offset = initialOffset + cfpos.getStart(); - } - return appendTo; -} - -FormattedList ListFormatter::formatStringsToValue( - const UnicodeString items[], - int32_t nItems, - UErrorCode& errorCode) const { - if (nItems == 0) { - FormattedListBuilder result(errorCode); - if (U_FAILURE(errorCode)) { - return FormattedList(errorCode); - } else { - return FormattedList(result.data.orphan()); - } - } else if (nItems == 1) { - FormattedListBuilder result(items[0], errorCode); - result.data->getStringRef().writeTerminator(errorCode); - if (U_FAILURE(errorCode)) { - return FormattedList(errorCode); - } else { - return FormattedList(result.data.orphan()); - } - } else if (nItems == 2) { - FormattedListBuilder result(items[0], errorCode); - if (U_FAILURE(errorCode)) { - return FormattedList(errorCode); - } - result.append( - data->patternHandler->getTwoPattern(items[1]), - items[1], - 1, - errorCode); - result.data->getStringRef().writeTerminator(errorCode); - if (U_FAILURE(errorCode)) { - return FormattedList(errorCode); - } else { - return FormattedList(result.data.orphan()); - } - } - - FormattedListBuilder result(items[0], errorCode); - if (U_FAILURE(errorCode)) { - return FormattedList(errorCode); - } - result.append( - data->startPattern, - items[1], - 1, - errorCode); - for (int32_t i = 2; i < nItems - 1; i++) { - result.append( - data->middlePattern, - items[i], - i, - errorCode); - } - result.append( - data->patternHandler->getEndPattern(items[nItems-1]), - items[nItems-1], - nItems-1, - errorCode); - result.data->getStringRef().writeTerminator(errorCode); - if (U_FAILURE(errorCode)) { - return FormattedList(errorCode); - } else { - return FormattedList(result.data.orphan()); - } -} - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* +* Copyright (C) 2013-2016, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: listformatter.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2012aug27 +* created by: Umesh P. Nair +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "cmemory.h" +#include "unicode/fpositer.h" // FieldPositionIterator +#include "unicode/listformatter.h" +#include "unicode/simpleformatter.h" +#include "unicode/ulistformatter.h" +#include "unicode/uscript.h" +#include "fphdlimp.h" +#include "mutex.h" +#include "hash.h" +#include "cstring.h" +#include "uarrsort.h" +#include "ulocimp.h" +#include "charstr.h" +#include "ucln_in.h" +#include "uresimp.h" +#include "resource.h" +#include "formattedval_impl.h" + +U_NAMESPACE_BEGIN + +namespace { + +class PatternHandler : public UObject { +public: + PatternHandler(const UnicodeString& two, const UnicodeString& end, UErrorCode& errorCode) : + twoPattern(two, 2, 2, errorCode), + endPattern(end, 2, 2, errorCode) { } + + PatternHandler(const SimpleFormatter& two, const SimpleFormatter& end) : + twoPattern(two), + endPattern(end) { } + + virtual ~PatternHandler(); + + virtual PatternHandler* clone() const { return new PatternHandler(twoPattern, endPattern); } + + /** Argument: final string in the list. */ + virtual const SimpleFormatter& getTwoPattern(const UnicodeString&) const { + return twoPattern; + } + + /** Argument: final string in the list. */ + virtual const SimpleFormatter& getEndPattern(const UnicodeString&) const { + return endPattern; + } + +protected: + SimpleFormatter twoPattern; + SimpleFormatter endPattern; +}; + +PatternHandler::~PatternHandler() { +} + +class ContextualHandler : public PatternHandler { +public: + ContextualHandler(bool (*testFunc)(const UnicodeString& text), + const UnicodeString& thenTwo, + const UnicodeString& elseTwo, + const UnicodeString& thenEnd, + const UnicodeString& elseEnd, + UErrorCode& errorCode) : + PatternHandler(elseTwo, elseEnd, errorCode), + test(testFunc), + thenTwoPattern(thenTwo, 2, 2, errorCode), + thenEndPattern(thenEnd, 2, 2, errorCode) { } + + ContextualHandler(bool (*testFunc)(const UnicodeString& text), + const SimpleFormatter& thenTwo, SimpleFormatter elseTwo, + const SimpleFormatter& thenEnd, SimpleFormatter elseEnd) : + PatternHandler(elseTwo, elseEnd), + test(testFunc), + thenTwoPattern(thenTwo), + thenEndPattern(thenEnd) { } + + ~ContextualHandler() override; + + PatternHandler* clone() const override { + return new ContextualHandler( + test, thenTwoPattern, twoPattern, thenEndPattern, endPattern); + } + + const SimpleFormatter& getTwoPattern( + const UnicodeString& text) const override { + return (test)(text) ? thenTwoPattern : twoPattern; + } + + const SimpleFormatter& getEndPattern( + const UnicodeString& text) const override { + return (test)(text) ? thenEndPattern : endPattern; + } + +private: + bool (*test)(const UnicodeString&); + SimpleFormatter thenTwoPattern; + SimpleFormatter thenEndPattern; +}; + +ContextualHandler::~ContextualHandler() { +} + +static const char16_t *spanishY = u"{0} y {1}"; +static const char16_t *spanishE = u"{0} e {1}"; +static const char16_t *spanishO = u"{0} o {1}"; +static const char16_t *spanishU = u"{0} u {1}"; +static const char16_t *hebrewVav = u"{0} \u05D5{1}"; +static const char16_t *hebrewVavDash = u"{0} \u05D5-{1}"; + +// Condiction to change to e. +// Starts with "hi" or "i" but not with "hie" nor "hia" +static bool shouldChangeToE(const UnicodeString& text) { + int32_t len = text.length(); + if (len == 0) { return false; } + // Case insensitive match hi but not hie nor hia. + if ((text[0] == u'h' || text[0] == u'H') && + ((len > 1) && (text[1] == u'i' || text[1] == u'I')) && + ((len == 2) || !(text[2] == u'a' || text[2] == u'A' || text[2] == u'e' || text[2] == u'E'))) { + return true; + } + // Case insensitive for "start with i" + if (text[0] == u'i' || text[0] == u'I') { return true; } + return false; +} + +// Condiction to change to u. +// Starts with "o", "ho", and "8". Also "11" by itself. +// re: ^((o|ho|8).*|11)$ +static bool shouldChangeToU(const UnicodeString& text) { + int32_t len = text.length(); + if (len == 0) { return false; } + // Case insensitive match o.* and 8.* + if (text[0] == u'o' || text[0] == u'O' || text[0] == u'8') { return true; } + // Case insensitive match ho.* + if ((text[0] == u'h' || text[0] == u'H') && + ((len > 1) && (text[1] == 'o' || text[1] == u'O'))) { + return true; + } + // match "^11$" and "^11 .*" + if ((len >= 2) && text[0] == u'1' && text[1] == u'1' && (len == 2 || text[2] == u' ')) { return true; } + return false; +} + +// Condiction to change to VAV follow by a dash. +// Starts with non Hebrew letter. +static bool shouldChangeToVavDash(const UnicodeString& text) { + if (text.isEmpty()) { return false; } + UErrorCode status = U_ZERO_ERROR; + return uscript_getScript(text.char32At(0), &status) != USCRIPT_HEBREW; +} + +PatternHandler* createPatternHandler( + const char* lang, const UnicodeString& two, const UnicodeString& end, + UErrorCode& status) { + if (uprv_strcmp(lang, "es") == 0) { + // Spanish + UnicodeString spanishYStr(true, spanishY, -1); + bool twoIsY = two == spanishYStr; + bool endIsY = end == spanishYStr; + if (twoIsY || endIsY) { + UnicodeString replacement(true, spanishE, -1); + return new ContextualHandler( + shouldChangeToE, + twoIsY ? replacement : two, two, + endIsY ? replacement : end, end, status); + } + UnicodeString spanishOStr(true, spanishO, -1); + bool twoIsO = two == spanishOStr; + bool endIsO = end == spanishOStr; + if (twoIsO || endIsO) { + UnicodeString replacement(true, spanishU, -1); + return new ContextualHandler( + shouldChangeToU, + twoIsO ? replacement : two, two, + endIsO ? replacement : end, end, status); + } + } else if (uprv_strcmp(lang, "he") == 0 || uprv_strcmp(lang, "iw") == 0) { + // Hebrew + UnicodeString hebrewVavStr(true, hebrewVav, -1); + bool twoIsVav = two == hebrewVavStr; + bool endIsVav = end == hebrewVavStr; + if (twoIsVav || endIsVav) { + UnicodeString replacement(true, hebrewVavDash, -1); + return new ContextualHandler( + shouldChangeToVavDash, + twoIsVav ? replacement : two, two, + endIsVav ? replacement : end, end, status); + } + } + return new PatternHandler(two, end, status); +} + +} // namespace + +struct ListFormatInternal : public UMemory { + SimpleFormatter startPattern; + SimpleFormatter middlePattern; + LocalPointer patternHandler; + +ListFormatInternal( + const UnicodeString& two, + const UnicodeString& start, + const UnicodeString& middle, + const UnicodeString& end, + const Locale& locale, + UErrorCode &errorCode) : + startPattern(start, 2, 2, errorCode), + middlePattern(middle, 2, 2, errorCode), + patternHandler(createPatternHandler(locale.getLanguage(), two, end, errorCode), errorCode) { } + +ListFormatInternal(const ListFormatData &data, UErrorCode &errorCode) : + startPattern(data.startPattern, errorCode), + middlePattern(data.middlePattern, errorCode), + patternHandler(createPatternHandler( + data.locale.getLanguage(), data.twoPattern, data.endPattern, errorCode), errorCode) { } + +ListFormatInternal(const ListFormatInternal &other) : + startPattern(other.startPattern), + middlePattern(other.middlePattern), + patternHandler(other.patternHandler->clone()) { } +}; + + +class FormattedListData : public FormattedValueStringBuilderImpl { +public: + FormattedListData(UErrorCode&) : FormattedValueStringBuilderImpl(kUndefinedField) {} + virtual ~FormattedListData(); +}; + +FormattedListData::~FormattedListData() = default; + +UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedList) + + +static Hashtable* listPatternHash = nullptr; + +U_CDECL_BEGIN +static UBool U_CALLCONV uprv_listformatter_cleanup() { + delete listPatternHash; + listPatternHash = nullptr; + return true; +} + +static void U_CALLCONV +uprv_deleteListFormatInternal(void *obj) { + delete static_cast(obj); +} + +U_CDECL_END + +ListFormatter::ListFormatter(const ListFormatter& other) : + owned(other.owned), data(other.data) { + if (other.owned != nullptr) { + owned = new ListFormatInternal(*other.owned); + data = owned; + } +} + +ListFormatter& ListFormatter::operator=(const ListFormatter& other) { + if (this == &other) { + return *this; + } + delete owned; + if (other.owned) { + owned = new ListFormatInternal(*other.owned); + data = owned; + } else { + owned = nullptr; + data = other.data; + } + return *this; +} + +void ListFormatter::initializeHash(UErrorCode& errorCode) { + if (U_FAILURE(errorCode)) { + return; + } + + listPatternHash = new Hashtable(); + if (listPatternHash == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + + listPatternHash->setValueDeleter(uprv_deleteListFormatInternal); + ucln_i18n_registerCleanup(UCLN_I18N_LIST_FORMATTER, uprv_listformatter_cleanup); + +} + +const ListFormatInternal* ListFormatter::getListFormatInternal( + const Locale& locale, const char *style, UErrorCode& errorCode) { + if (U_FAILURE(errorCode)) { + return nullptr; + } + CharString keyBuffer(locale.getName(), errorCode); + keyBuffer.append(':', errorCode).append(style, errorCode); + UnicodeString key(keyBuffer.data(), -1, US_INV); + ListFormatInternal* result = nullptr; + static UMutex listFormatterMutex; + { + Mutex m(&listFormatterMutex); + if (listPatternHash == nullptr) { + initializeHash(errorCode); + if (U_FAILURE(errorCode)) { + return nullptr; + } + } + result = static_cast(listPatternHash->get(key)); + } + if (result != nullptr) { + return result; + } + result = loadListFormatInternal(locale, style, errorCode); + if (U_FAILURE(errorCode)) { + return nullptr; + } + + { + Mutex m(&listFormatterMutex); + ListFormatInternal* temp = static_cast(listPatternHash->get(key)); + if (temp != nullptr) { + delete result; + result = temp; + } else { + listPatternHash->put(key, result, errorCode); + if (U_FAILURE(errorCode)) { + return nullptr; + } + } + } + return result; +} + +static const char* typeWidthToStyleString(UListFormatterType type, UListFormatterWidth width) { + switch (type) { + case ULISTFMT_TYPE_AND: + switch (width) { + case ULISTFMT_WIDTH_WIDE: + return "standard"; + case ULISTFMT_WIDTH_SHORT: + return "standard-short"; + case ULISTFMT_WIDTH_NARROW: + return "standard-narrow"; + default: + return nullptr; + } + break; + + case ULISTFMT_TYPE_OR: + switch (width) { + case ULISTFMT_WIDTH_WIDE: + return "or"; + case ULISTFMT_WIDTH_SHORT: + return "or-short"; + case ULISTFMT_WIDTH_NARROW: + return "or-narrow"; + default: + return nullptr; + } + break; + + case ULISTFMT_TYPE_UNITS: + switch (width) { + case ULISTFMT_WIDTH_WIDE: + return "unit"; + case ULISTFMT_WIDTH_SHORT: + return "unit-short"; + case ULISTFMT_WIDTH_NARROW: + return "unit-narrow"; + default: + return nullptr; + } + } + + return nullptr; +} + +static const char16_t solidus = 0x2F; +static const char16_t aliasPrefix[] = { 0x6C,0x69,0x73,0x74,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2F }; // "listPattern/" +enum { + kAliasPrefixLen = UPRV_LENGTHOF(aliasPrefix), + kStyleLenMax = 24 // longest currently is 14 +}; + +struct ListFormatter::ListPatternsSink : public ResourceSink { + UnicodeString two, start, middle, end; + char aliasedStyle[kStyleLenMax+1] = {0}; + + ListPatternsSink() {} + virtual ~ListPatternsSink(); + + void setAliasedStyle(UnicodeString alias) { + int32_t startIndex = alias.indexOf(aliasPrefix, kAliasPrefixLen, 0); + if (startIndex < 0) { + return; + } + startIndex += kAliasPrefixLen; + int32_t endIndex = alias.indexOf(solidus, startIndex); + if (endIndex < 0) { + endIndex = alias.length(); + } + alias.extract(startIndex, endIndex-startIndex, aliasedStyle, kStyleLenMax+1, US_INV); + aliasedStyle[kStyleLenMax] = 0; + } + + void handleValueForPattern(ResourceValue &value, UnicodeString &pattern, UErrorCode &errorCode) { + if (pattern.isEmpty()) { + if (value.getType() == URES_ALIAS) { + if (aliasedStyle[0] == 0) { + setAliasedStyle(value.getAliasUnicodeString(errorCode)); + } + } else { + pattern = value.getUnicodeString(errorCode); + } + } + } + + virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, + UErrorCode &errorCode) override { + aliasedStyle[0] = 0; + if (value.getType() == URES_ALIAS) { + setAliasedStyle(value.getAliasUnicodeString(errorCode)); + return; + } + ResourceTable listPatterns = value.getTable(errorCode); + for (int i = 0; U_SUCCESS(errorCode) && listPatterns.getKeyAndValue(i, key, value); ++i) { + if (uprv_strcmp(key, "2") == 0) { + handleValueForPattern(value, two, errorCode); + } else if (uprv_strcmp(key, "end") == 0) { + handleValueForPattern(value, end, errorCode); + } else if (uprv_strcmp(key, "middle") == 0) { + handleValueForPattern(value, middle, errorCode); + } else if (uprv_strcmp(key, "start") == 0) { + handleValueForPattern(value, start, errorCode); + } + } + } +}; + +// Virtual destructors must be defined out of line. +ListFormatter::ListPatternsSink::~ListPatternsSink() {} + +ListFormatInternal* ListFormatter::loadListFormatInternal( + const Locale& locale, const char * style, UErrorCode& errorCode) { + UResourceBundle* rb = ures_open(nullptr, locale.getName(), &errorCode); + rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode); + if (U_FAILURE(errorCode)) { + ures_close(rb); + return nullptr; + } + ListFormatter::ListPatternsSink sink; + char currentStyle[kStyleLenMax+1]; + uprv_strncpy(currentStyle, style, kStyleLenMax); + currentStyle[kStyleLenMax] = 0; + + for (;;) { + ures_getAllItemsWithFallback(rb, currentStyle, sink, errorCode); + if (U_FAILURE(errorCode) || sink.aliasedStyle[0] == 0 || uprv_strcmp(currentStyle, sink.aliasedStyle) == 0) { + break; + } + uprv_strcpy(currentStyle, sink.aliasedStyle); + } + ures_close(rb); + if (U_FAILURE(errorCode)) { + return nullptr; + } + if (sink.two.isEmpty() || sink.start.isEmpty() || sink.middle.isEmpty() || sink.end.isEmpty()) { + errorCode = U_MISSING_RESOURCE_ERROR; + return nullptr; + } + + ListFormatInternal* result = new ListFormatInternal(sink.two, sink.start, sink.middle, sink.end, locale, errorCode); + if (result == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + if (U_FAILURE(errorCode)) { + delete result; + return nullptr; + } + return result; +} + +ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) { + Locale locale; // The default locale. + return createInstance(locale, errorCode); +} + +ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) { + return createInstance(locale, ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_WIDE, errorCode); +} + +ListFormatter* ListFormatter::createInstance( + const Locale& locale, UListFormatterType type, UListFormatterWidth width, UErrorCode& errorCode) { + const char* style = typeWidthToStyleString(type, width); + if (style == nullptr) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + return createInstance(locale, style, errorCode); +} + +ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) { + const ListFormatInternal* listFormatInternal = getListFormatInternal(locale, style, errorCode); + if (U_FAILURE(errorCode)) { + return nullptr; + } + ListFormatter* p = new ListFormatter(listFormatInternal); + if (p == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + return p; +} + +ListFormatter::ListFormatter(const ListFormatData& listFormatData, UErrorCode &errorCode) { + owned = new ListFormatInternal(listFormatData, errorCode); + data = owned; +} + +ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(nullptr), data(listFormatterInternal) { +} + +ListFormatter::~ListFormatter() { + delete owned; +} + +namespace { + +class FormattedListBuilder { +public: + LocalPointer data; + + /** For lists of length 1+ */ + FormattedListBuilder(const UnicodeString& start, UErrorCode& status) + : data(new FormattedListData(status), status) { + if (U_SUCCESS(status)) { + data->getStringRef().append( + start, + {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD}, + status); + data->appendSpanInfo(UFIELD_CATEGORY_LIST_SPAN, 0, -1, start.length(), status); + } + } + + /** For lists of length 0 */ + FormattedListBuilder(UErrorCode& status) + : data(new FormattedListData(status), status) { + } + + void append(const SimpleFormatter& pattern, const UnicodeString& next, int32_t position, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (pattern.getArgumentLimit() != 2) { + status = U_INTERNAL_PROGRAM_ERROR; + return; + } + // In the pattern, {0} are the pre-existing elements and {1} is the new element. + int32_t offsets[] = {0, 0}; + UnicodeString temp = pattern.getTextWithNoArguments(offsets, 2); + if (offsets[0] <= offsets[1]) { + // prefix{0}infix{1}suffix + // Prepend prefix, then append infix, element, and suffix + data->getStringRef().insert( + 0, + temp.tempSubStringBetween(0, offsets[0]), + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD}, + status); + data->getStringRef().append( + temp.tempSubStringBetween(offsets[0], offsets[1]), + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD}, + status); + data->getStringRef().append( + next, + {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD}, + status); + data->appendSpanInfo(UFIELD_CATEGORY_LIST_SPAN, position, -1, next.length(), status); + data->getStringRef().append( + temp.tempSubString(offsets[1]), + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD}, + status); + } else { + // prefix{1}infix{0}suffix + // Prepend infix, element, and prefix, then append suffix. + // (We prepend in reverse order because prepending at index 0 is fast.) + data->getStringRef().insert( + 0, + temp.tempSubStringBetween(offsets[1], offsets[0]), + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD}, + status); + data->getStringRef().insert( + 0, + next, + {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD}, + status); + data->prependSpanInfo(UFIELD_CATEGORY_LIST_SPAN, position, -1, next.length(), status); + data->getStringRef().insert( + 0, + temp.tempSubStringBetween(0, offsets[1]), + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD}, + status); + data->getStringRef().append( + temp.tempSubString(offsets[0]), + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD}, + status); + } + } +}; + +} + +UnicodeString& ListFormatter::format( + const UnicodeString items[], + int32_t nItems, + UnicodeString& appendTo, + UErrorCode& errorCode) const { + int32_t offset; + return format(items, nItems, appendTo, -1, offset, errorCode); +} + +UnicodeString& ListFormatter::format( + const UnicodeString items[], + int32_t nItems, + UnicodeString& appendTo, + int32_t index, + int32_t &offset, + UErrorCode& errorCode) const { + int32_t initialOffset = appendTo.length(); + auto result = formatStringsToValue(items, nItems, errorCode); + UnicodeStringAppendable appendable(appendTo); + result.appendTo(appendable, errorCode); + if (index >= 0) { + ConstrainedFieldPosition cfpos; + cfpos.constrainField(UFIELD_CATEGORY_LIST_SPAN, index); + result.nextPosition(cfpos, errorCode); + offset = initialOffset + cfpos.getStart(); + } + return appendTo; +} + +FormattedList ListFormatter::formatStringsToValue( + const UnicodeString items[], + int32_t nItems, + UErrorCode& errorCode) const { + if (nItems == 0) { + FormattedListBuilder result(errorCode); + if (U_FAILURE(errorCode)) { + return FormattedList(errorCode); + } else { + return FormattedList(result.data.orphan()); + } + } else if (nItems == 1) { + FormattedListBuilder result(items[0], errorCode); + result.data->getStringRef().writeTerminator(errorCode); + if (U_FAILURE(errorCode)) { + return FormattedList(errorCode); + } else { + return FormattedList(result.data.orphan()); + } + } else if (nItems == 2) { + FormattedListBuilder result(items[0], errorCode); + if (U_FAILURE(errorCode)) { + return FormattedList(errorCode); + } + result.append( + data->patternHandler->getTwoPattern(items[1]), + items[1], + 1, + errorCode); + result.data->getStringRef().writeTerminator(errorCode); + if (U_FAILURE(errorCode)) { + return FormattedList(errorCode); + } else { + return FormattedList(result.data.orphan()); + } + } + + FormattedListBuilder result(items[0], errorCode); + if (U_FAILURE(errorCode)) { + return FormattedList(errorCode); + } + result.append( + data->startPattern, + items[1], + 1, + errorCode); + for (int32_t i = 2; i < nItems - 1; i++) { + result.append( + data->middlePattern, + items[i], + i, + errorCode); + } + result.append( + data->patternHandler->getEndPattern(items[nItems-1]), + items[nItems-1], + nItems-1, + errorCode); + result.data->getStringRef().writeTerminator(errorCode); + if (U_FAILURE(errorCode)) { + return FormattedList(errorCode); + } else { + return FormattedList(result.data.orphan()); + } +} + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/measfmt.cpp b/deps/icu-small/source/i18n/measfmt.cpp index d2b4e7018dce2a..14e2113f636b2c 100644 --- a/deps/icu-small/source/i18n/measfmt.cpp +++ b/deps/icu-small/source/i18n/measfmt.cpp @@ -1,893 +1,893 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2004-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: April 20, 2004 -* Since: ICU 3.0 -********************************************************************** -*/ -#include "utypeinfo.h" // for 'typeid' to work -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/measfmt.h" -#include "unicode/numfmt.h" -#include "currfmt.h" -#include "unicode/localpointer.h" -#include "resource.h" -#include "unicode/simpleformatter.h" -#include "quantityformatter.h" -#include "unicode/plurrule.h" -#include "unicode/decimfmt.h" -#include "uresimp.h" -#include "unicode/ures.h" -#include "unicode/ustring.h" -#include "ureslocs.h" -#include "cstring.h" -#include "mutex.h" -#include "ucln_in.h" -#include "unicode/listformatter.h" -#include "charstr.h" -#include "unicode/putil.h" -#include "unicode/smpdtfmt.h" -#include "uassert.h" -#include "unicode/numberformatter.h" -#include "number_longnames.h" -#include "number_utypes.h" - -#include "sharednumberformat.h" -#include "sharedpluralrules.h" -#include "standardplural.h" -#include "unifiedcache.h" - - -U_NAMESPACE_BEGIN - -using number::impl::UFormattedNumberData; - -static constexpr int32_t WIDTH_INDEX_COUNT = UMEASFMT_WIDTH_NARROW + 1; - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat) - -// Used to format durations like 5:47 or 21:35:42. -class NumericDateFormatters : public UMemory { -public: - // Formats like H:mm - UnicodeString hourMinute; - - // formats like M:ss - UnicodeString minuteSecond; - - // formats like H:mm:ss - UnicodeString hourMinuteSecond; - - // Constructor that takes the actual patterns for hour-minute, - // minute-second, and hour-minute-second respectively. - NumericDateFormatters( - const UnicodeString &hm, - const UnicodeString &ms, - const UnicodeString &hms) : - hourMinute(hm), - minuteSecond(ms), - hourMinuteSecond(hms) { - } -private: - NumericDateFormatters(const NumericDateFormatters &other); - NumericDateFormatters &operator=(const NumericDateFormatters &other); -}; - -static UMeasureFormatWidth getRegularWidth(UMeasureFormatWidth width) { - if (width >= WIDTH_INDEX_COUNT) { - return UMEASFMT_WIDTH_NARROW; - } - return width; -} - -static UNumberUnitWidth getUnitWidth(UMeasureFormatWidth width) { - switch (width) { - case UMEASFMT_WIDTH_WIDE: - return UNUM_UNIT_WIDTH_FULL_NAME; - case UMEASFMT_WIDTH_NARROW: - case UMEASFMT_WIDTH_NUMERIC: - return UNUM_UNIT_WIDTH_NARROW; - case UMEASFMT_WIDTH_SHORT: - default: - return UNUM_UNIT_WIDTH_SHORT; - } -} - -/** - * Instances contain all MeasureFormat specific data for a particular locale. - * This data is cached. It is never copied, but is shared via shared pointers. - * - * Note: We might change the cache data to have an array[WIDTH_INDEX_COUNT] of - * complete sets of unit & per patterns, - * to correspond to the resource data and its aliases. - * - * TODO: Maybe store more sparsely in general, with pointers rather than potentially-empty objects. - */ -class MeasureFormatCacheData : public SharedObject { -public: - - /** - * Redirection data from root-bundle, top-level sideways aliases. - * - UMEASFMT_WIDTH_COUNT: initial value, just fall back to root - * - UMEASFMT_WIDTH_WIDE/SHORT/NARROW: sideways alias for missing data - */ - UMeasureFormatWidth widthFallback[WIDTH_INDEX_COUNT]; - - MeasureFormatCacheData(); - virtual ~MeasureFormatCacheData(); - - void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) { - delete currencyFormats[widthIndex]; - currencyFormats[widthIndex] = nfToAdopt; - } - const NumberFormat *getCurrencyFormat(UMeasureFormatWidth width) const { - return currencyFormats[getRegularWidth(width)]; - } - void adoptIntegerFormat(NumberFormat *nfToAdopt) { - delete integerFormat; - integerFormat = nfToAdopt; - } - const NumberFormat *getIntegerFormat() const { - return integerFormat; - } - void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) { - delete numericDateFormatters; - numericDateFormatters = formattersToAdopt; - } - const NumericDateFormatters *getNumericDateFormatters() const { - return numericDateFormatters; - } - -private: - NumberFormat* currencyFormats[WIDTH_INDEX_COUNT]; - NumberFormat* integerFormat; - NumericDateFormatters* numericDateFormatters; - - MeasureFormatCacheData(const MeasureFormatCacheData &other); - MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other); -}; - -MeasureFormatCacheData::MeasureFormatCacheData() - : integerFormat(nullptr), numericDateFormatters(nullptr) { - for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { - widthFallback[i] = UMEASFMT_WIDTH_COUNT; - } - memset(currencyFormats, 0, sizeof(currencyFormats)); -} - -MeasureFormatCacheData::~MeasureFormatCacheData() { - for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) { - delete currencyFormats[i]; - } - // Note: the contents of 'dnams' are pointers into the resource bundle - delete integerFormat; - delete numericDateFormatters; -} - -static UBool isCurrency(const MeasureUnit &unit) { - return (uprv_strcmp(unit.getType(), "currency") == 0); -} - -static UBool getString( - const UResourceBundle *resource, - UnicodeString &result, - UErrorCode &status) { - int32_t len = 0; - const UChar *resStr = ures_getString(resource, &len, &status); - if (U_FAILURE(status)) { - return false; - } - result.setTo(true, resStr, len); - return true; -} - -static UnicodeString loadNumericDateFormatterPattern( - const UResourceBundle *resource, - const char *pattern, - UErrorCode &status) { - UnicodeString result; - if (U_FAILURE(status)) { - return result; - } - CharString chs; - chs.append("durationUnits", status) - .append("/", status).append(pattern, status); - LocalUResourceBundlePointer patternBundle( - ures_getByKeyWithFallback( - resource, - chs.data(), - NULL, - &status)); - if (U_FAILURE(status)) { - return result; - } - getString(patternBundle.getAlias(), result, status); - // Replace 'h' with 'H' - int32_t len = result.length(); - UChar *buffer = result.getBuffer(len); - for (int32_t i = 0; i < len; ++i) { - if (buffer[i] == 0x68) { // 'h' - buffer[i] = 0x48; // 'H' - } - } - result.releaseBuffer(len); - return result; -} - -static NumericDateFormatters *loadNumericDateFormatters( - const UResourceBundle *resource, - UErrorCode &status) { - if (U_FAILURE(status)) { - return NULL; - } - NumericDateFormatters *result = new NumericDateFormatters( - loadNumericDateFormatterPattern(resource, "hm", status), - loadNumericDateFormatterPattern(resource, "ms", status), - loadNumericDateFormatterPattern(resource, "hms", status)); - if (U_FAILURE(status)) { - delete result; - return NULL; - } - return result; -} - -template<> -const MeasureFormatCacheData *LocaleCacheKey::createObject( - const void * /*unused*/, UErrorCode &status) const { - const char *localeId = fLoc.getName(); - LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, localeId, &status)); - static UNumberFormatStyle currencyStyles[] = { - UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY}; - LocalPointer result(new MeasureFormatCacheData(), status); - if (U_FAILURE(status)) { - return NULL; - } - result->adoptNumericDateFormatters(loadNumericDateFormatters( - unitsBundle.getAlias(), status)); - if (U_FAILURE(status)) { - return NULL; - } - - for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { - // NumberFormat::createInstance can erase warning codes from status, so pass it - // a separate status instance - UErrorCode localStatus = U_ZERO_ERROR; - result->adoptCurrencyFormat(i, NumberFormat::createInstance( - localeId, currencyStyles[i], localStatus)); - if (localStatus != U_ZERO_ERROR) { - status = localStatus; - } - if (U_FAILURE(status)) { - return NULL; - } - } - NumberFormat *inf = NumberFormat::createInstance( - localeId, UNUM_DECIMAL, status); - if (U_FAILURE(status)) { - return NULL; - } - inf->setMaximumFractionDigits(0); - DecimalFormat *decfmt = dynamic_cast(inf); - if (decfmt != NULL) { - decfmt->setRoundingMode(DecimalFormat::kRoundDown); - } - result->adoptIntegerFormat(inf); - result->addRef(); - return result.orphan(); -} - -static UBool isTimeUnit(const MeasureUnit &mu, const char *tu) { - return uprv_strcmp(mu.getType(), "duration") == 0 && - uprv_strcmp(mu.getSubtype(), tu) == 0; -} - -// Converts a composite measure into hours-minutes-seconds and stores at hms -// array. [0] is hours; [1] is minutes; [2] is seconds. Returns a bit map of -// units found: 1=hours, 2=minutes, 4=seconds. For example, if measures -// contains hours-minutes, this function would return 3. -// -// If measures cannot be converted into hours, minutes, seconds or if amounts -// are negative, or if hours, minutes, seconds are out of order, returns 0. -static int32_t toHMS( - const Measure *measures, - int32_t measureCount, - Formattable *hms, - UErrorCode &status) { - if (U_FAILURE(status)) { - return 0; - } - int32_t result = 0; - if (U_FAILURE(status)) { - return 0; - } - // We use copy constructor to ensure that both sides of equality operator - // are instances of MeasureUnit base class and not a subclass. Otherwise, - // operator== will immediately return false. - for (int32_t i = 0; i < measureCount; ++i) { - if (isTimeUnit(measures[i].getUnit(), "hour")) { - // hour must come first - if (result >= 1) { - return 0; - } - hms[0] = measures[i].getNumber(); - if (hms[0].getDouble() < 0.0) { - return 0; - } - result |= 1; - } else if (isTimeUnit(measures[i].getUnit(), "minute")) { - // minute must come after hour - if (result >= 2) { - return 0; - } - hms[1] = measures[i].getNumber(); - if (hms[1].getDouble() < 0.0) { - return 0; - } - result |= 2; - } else if (isTimeUnit(measures[i].getUnit(), "second")) { - // second must come after hour and minute - if (result >= 4) { - return 0; - } - hms[2] = measures[i].getNumber(); - if (hms[2].getDouble() < 0.0) { - return 0; - } - result |= 4; - } else { - return 0; - } - } - return result; -} - - -MeasureFormat::MeasureFormat( - const Locale &locale, UMeasureFormatWidth w, UErrorCode &status) - : cache(NULL), - numberFormat(NULL), - pluralRules(NULL), - fWidth(w), - listFormatter(NULL) { - initMeasureFormat(locale, w, NULL, status); -} - -MeasureFormat::MeasureFormat( - const Locale &locale, - UMeasureFormatWidth w, - NumberFormat *nfToAdopt, - UErrorCode &status) - : cache(NULL), - numberFormat(NULL), - pluralRules(NULL), - fWidth(w), - listFormatter(NULL) { - initMeasureFormat(locale, w, nfToAdopt, status); -} - -MeasureFormat::MeasureFormat(const MeasureFormat &other) : - Format(other), - cache(other.cache), - numberFormat(other.numberFormat), - pluralRules(other.pluralRules), - fWidth(other.fWidth), - listFormatter(NULL) { - cache->addRef(); - numberFormat->addRef(); - pluralRules->addRef(); - if (other.listFormatter != NULL) { - listFormatter = new ListFormatter(*other.listFormatter); - } -} - -MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) { - if (this == &other) { - return *this; - } - Format::operator=(other); - SharedObject::copyPtr(other.cache, cache); - SharedObject::copyPtr(other.numberFormat, numberFormat); - SharedObject::copyPtr(other.pluralRules, pluralRules); - fWidth = other.fWidth; - delete listFormatter; - if (other.listFormatter != NULL) { - listFormatter = new ListFormatter(*other.listFormatter); - } else { - listFormatter = NULL; - } - return *this; -} - -MeasureFormat::MeasureFormat() : - cache(NULL), - numberFormat(NULL), - pluralRules(NULL), - fWidth(UMEASFMT_WIDTH_SHORT), - listFormatter(NULL) { -} - -MeasureFormat::~MeasureFormat() { - if (cache != NULL) { - cache->removeRef(); - } - if (numberFormat != NULL) { - numberFormat->removeRef(); - } - if (pluralRules != NULL) { - pluralRules->removeRef(); - } - delete listFormatter; -} - -bool MeasureFormat::operator==(const Format &other) const { - if (this == &other) { // Same object, equal - return true; - } - if (!Format::operator==(other)) { - return false; - } - const MeasureFormat &rhs = static_cast(other); - - // Note: Since the ListFormatter depends only on Locale and width, we - // don't have to check it here. - - // differing widths aren't equivalent - if (fWidth != rhs.fWidth) { - return false; - } - // Width the same check locales. - // We don't need to check locales if both objects have same cache. - if (cache != rhs.cache) { - UErrorCode status = U_ZERO_ERROR; - const char *localeId = getLocaleID(status); - const char *rhsLocaleId = rhs.getLocaleID(status); - if (U_FAILURE(status)) { - // On failure, assume not equal - return false; - } - if (uprv_strcmp(localeId, rhsLocaleId) != 0) { - return false; - } - } - // Locales same, check NumberFormat if shared data differs. - return ( - numberFormat == rhs.numberFormat || - **numberFormat == **rhs.numberFormat); -} - -MeasureFormat *MeasureFormat::clone() const { - return new MeasureFormat(*this); -} - -UnicodeString &MeasureFormat::format( - const Formattable &obj, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const { - if (U_FAILURE(status)) return appendTo; - if (obj.getType() == Formattable::kObject) { - const UObject* formatObj = obj.getObject(); - const Measure* amount = dynamic_cast(formatObj); - if (amount != NULL) { - return formatMeasure( - *amount, **numberFormat, appendTo, pos, status); - } - } - status = U_ILLEGAL_ARGUMENT_ERROR; - return appendTo; -} - -void MeasureFormat::parseObject( - const UnicodeString & /*source*/, - Formattable & /*result*/, - ParsePosition& /*pos*/) const { - return; -} - -UnicodeString &MeasureFormat::formatMeasurePerUnit( - const Measure &measure, - const MeasureUnit &perUnit, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return appendTo; - } - auto* df = dynamic_cast(&getNumberFormatInternal()); - if (df == nullptr) { - // Don't know how to handle other types of NumberFormat - status = U_UNSUPPORTED_ERROR; - return appendTo; - } - UFormattedNumberData result; - if (auto* lnf = df->toNumberFormatter(status)) { - result.quantity.setToDouble(measure.getNumber().getDouble(status)); - lnf->unit(measure.getUnit()) - .perUnit(perUnit) - .unitWidth(getUnitWidth(fWidth)) - .formatImpl(&result, status); - } - DecimalFormat::fieldPositionHelper(result, pos, appendTo.length(), status); - appendTo.append(result.toTempString(status)); - return appendTo; -} - -UnicodeString &MeasureFormat::formatMeasures( - const Measure *measures, - int32_t measureCount, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return appendTo; - } - if (measureCount == 0) { - return appendTo; - } - if (measureCount == 1) { - return formatMeasure(measures[0], **numberFormat, appendTo, pos, status); - } - if (fWidth == UMEASFMT_WIDTH_NUMERIC) { - Formattable hms[3]; - int32_t bitMap = toHMS(measures, measureCount, hms, status); - if (bitMap > 0) { - return formatNumeric(hms, bitMap, appendTo, status); - } - } - if (pos.getField() != FieldPosition::DONT_CARE) { - return formatMeasuresSlowTrack( - measures, measureCount, appendTo, pos, status); - } - UnicodeString *results = new UnicodeString[measureCount]; - if (results == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return appendTo; - } - for (int32_t i = 0; i < measureCount; ++i) { - const NumberFormat *nf = cache->getIntegerFormat(); - if (i == measureCount - 1) { - nf = numberFormat->get(); - } - formatMeasure( - measures[i], - *nf, - results[i], - pos, - status); - } - listFormatter->format(results, measureCount, appendTo, status); - delete [] results; - return appendTo; -} - -UnicodeString MeasureFormat::getUnitDisplayName(const MeasureUnit& unit, UErrorCode& status) const { - return number::impl::LongNameHandler::getUnitDisplayName( - getLocale(status), - unit, - getUnitWidth(fWidth), - status); -} - -void MeasureFormat::initMeasureFormat( - const Locale &locale, - UMeasureFormatWidth w, - NumberFormat *nfToAdopt, - UErrorCode &status) { - static const UListFormatterWidth listWidths[] = { - ULISTFMT_WIDTH_WIDE, - ULISTFMT_WIDTH_SHORT, - ULISTFMT_WIDTH_NARROW}; - LocalPointer nf(nfToAdopt); - if (U_FAILURE(status)) { - return; - } - const char *name = locale.getName(); - setLocaleIDs(name, name); - - UnifiedCache::getByLocale(locale, cache, status); - if (U_FAILURE(status)) { - return; - } - - const SharedPluralRules *pr = PluralRules::createSharedInstance( - locale, UPLURAL_TYPE_CARDINAL, status); - if (U_FAILURE(status)) { - return; - } - SharedObject::copyPtr(pr, pluralRules); - pr->removeRef(); - if (nf.isNull()) { - // TODO: Clean this up - const SharedNumberFormat *shared = NumberFormat::createSharedInstance( - locale, UNUM_DECIMAL, status); - if (U_FAILURE(status)) { - return; - } - SharedObject::copyPtr(shared, numberFormat); - shared->removeRef(); - } else { - adoptNumberFormat(nf.orphan(), status); - if (U_FAILURE(status)) { - return; - } - } - fWidth = w; - delete listFormatter; - listFormatter = ListFormatter::createInstance( - locale, - ULISTFMT_TYPE_UNITS, - listWidths[getRegularWidth(fWidth)], - status); -} - -void MeasureFormat::adoptNumberFormat( - NumberFormat *nfToAdopt, UErrorCode &status) { - LocalPointer nf(nfToAdopt); - if (U_FAILURE(status)) { - return; - } - SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias()); - if (shared == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - nf.orphan(); - SharedObject::copyPtr(shared, numberFormat); -} - -UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) { - if (U_FAILURE(status) || locale == getLocale(status)) { - return false; - } - initMeasureFormat(locale, fWidth, NULL, status); - return U_SUCCESS(status); -} - -const NumberFormat &MeasureFormat::getNumberFormatInternal() const { - return **numberFormat; -} - -const NumberFormat &MeasureFormat::getCurrencyFormatInternal() const { - return *cache->getCurrencyFormat(UMEASFMT_WIDTH_NARROW); -} - -const PluralRules &MeasureFormat::getPluralRules() const { - return **pluralRules; -} - -Locale MeasureFormat::getLocale(UErrorCode &status) const { - return Format::getLocale(ULOC_VALID_LOCALE, status); -} - -const char *MeasureFormat::getLocaleID(UErrorCode &status) const { - return Format::getLocaleID(ULOC_VALID_LOCALE, status); -} - -UnicodeString &MeasureFormat::formatMeasure( - const Measure &measure, - const NumberFormat &nf, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return appendTo; - } - const Formattable& amtNumber = measure.getNumber(); - const MeasureUnit& amtUnit = measure.getUnit(); - if (isCurrency(amtUnit)) { - UChar isoCode[4]; - u_charsToUChars(amtUnit.getSubtype(), isoCode, 4); - return cache->getCurrencyFormat(fWidth)->format( - new CurrencyAmount(amtNumber, isoCode, status), - appendTo, - pos, - status); - } - auto* df = dynamic_cast(&nf); - if (df == nullptr) { - // Handle other types of NumberFormat using the ICU 63 code, modified to - // get the unitPattern from LongNameHandler and handle fallback to OTHER. - UnicodeString formattedNumber; - StandardPlural::Form pluralForm = QuantityFormatter::selectPlural( - amtNumber, nf, **pluralRules, formattedNumber, pos, status); - UnicodeString pattern = number::impl::LongNameHandler::getUnitPattern(getLocale(status), - amtUnit, getUnitWidth(fWidth), pluralForm, status); - // The above handles fallback from other widths to short, and from other plural forms to OTHER - if (U_FAILURE(status)) { - return appendTo; - } - SimpleFormatter formatter(pattern, 0, 1, status); - return QuantityFormatter::format(formatter, formattedNumber, appendTo, pos, status); - } - UFormattedNumberData result; - if (auto* lnf = df->toNumberFormatter(status)) { - result.quantity.setToDouble(amtNumber.getDouble(status)); - lnf->unit(amtUnit) - .unitWidth(getUnitWidth(fWidth)) - .formatImpl(&result, status); - } - DecimalFormat::fieldPositionHelper(result, pos, appendTo.length(), status); - appendTo.append(result.toTempString(status)); - return appendTo; -} - - -// Formats numeric time duration as 5:00:47 or 3:54. -UnicodeString &MeasureFormat::formatNumeric( - const Formattable *hms, // always length 3 - int32_t bitMap, // 1=hour set, 2=minute set, 4=second set - UnicodeString &appendTo, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return appendTo; - } - - UnicodeString pattern; - - double hours = hms[0].getDouble(status); - double minutes = hms[1].getDouble(status); - double seconds = hms[2].getDouble(status); - if (U_FAILURE(status)) { - return appendTo; - } - - // All possible combinations: "h", "m", "s", "hm", "hs", "ms", "hms" - if (bitMap == 5 || bitMap == 7) { // "hms" & "hs" (we add minutes if "hs") - pattern = cache->getNumericDateFormatters()->hourMinuteSecond; - hours = uprv_trunc(hours); - minutes = uprv_trunc(minutes); - } else if (bitMap == 3) { // "hm" - pattern = cache->getNumericDateFormatters()->hourMinute; - hours = uprv_trunc(hours); - } else if (bitMap == 6) { // "ms" - pattern = cache->getNumericDateFormatters()->minuteSecond; - minutes = uprv_trunc(minutes); - } else { // h m s, handled outside formatNumeric. No value is also an error. - status = U_INTERNAL_PROGRAM_ERROR; - return appendTo; - } - - const DecimalFormat *numberFormatter = dynamic_cast(numberFormat->get()); - if (!numberFormatter) { - status = U_INTERNAL_PROGRAM_ERROR; - return appendTo; - } - number::LocalizedNumberFormatter numberFormatter2; - if (auto* lnf = numberFormatter->toNumberFormatter(status)) { - numberFormatter2 = lnf->integerWidth(number::IntegerWidth::zeroFillTo(2)); - } else { - return appendTo; - } - - FormattedStringBuilder fsb; - - UBool protect = false; - const int32_t patternLength = pattern.length(); - for (int32_t i = 0; i < patternLength; i++) { - char16_t c = pattern[i]; - - // Also set the proper field in this switch - // We don't use DateFormat.Field because this is not a date / time, is a duration. - double value = 0; - switch (c) { - case u'H': value = hours; break; - case u'm': value = minutes; break; - case u's': value = seconds; break; - } - - // There is not enough info to add Field(s) for the unit because all we have are plain - // text patterns. For example in "21:51" there is no text for something like "hour", - // while in something like "21h51" there is ("h"). But we can't really tell... - switch (c) { - case u'H': - case u'm': - case u's': - if (protect) { - fsb.appendChar16(c, kUndefinedField, status); - } else { - UnicodeString tmp; - if ((i + 1 < patternLength) && pattern[i + 1] == c) { // doubled - tmp = numberFormatter2.formatDouble(value, status).toString(status); - i++; - } else { - numberFormatter->format(value, tmp, status); - } - // TODO: Use proper Field - fsb.append(tmp, kUndefinedField, status); - } - break; - case u'\'': - // '' is escaped apostrophe - if ((i + 1 < patternLength) && pattern[i + 1] == c) { - fsb.appendChar16(c, kUndefinedField, status); - i++; - } else { - protect = !protect; - } - break; - default: - fsb.appendChar16(c, kUndefinedField, status); - } - } - - appendTo.append(fsb.toTempUnicodeString()); - - return appendTo; -} - -UnicodeString &MeasureFormat::formatMeasuresSlowTrack( - const Measure *measures, - int32_t measureCount, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return appendTo; - } - FieldPosition dontCare(FieldPosition::DONT_CARE); - FieldPosition fpos(pos.getField()); - LocalArray results(new UnicodeString[measureCount], status); - int32_t fieldPositionFoundIndex = -1; - for (int32_t i = 0; i < measureCount; ++i) { - const NumberFormat *nf = cache->getIntegerFormat(); - if (i == measureCount - 1) { - nf = numberFormat->get(); - } - if (fieldPositionFoundIndex == -1) { - formatMeasure(measures[i], *nf, results[i], fpos, status); - if (U_FAILURE(status)) { - return appendTo; - } - if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) { - fieldPositionFoundIndex = i; - } - } else { - formatMeasure(measures[i], *nf, results[i], dontCare, status); - } - } - int32_t offset; - listFormatter->format( - results.getAlias(), - measureCount, - appendTo, - fieldPositionFoundIndex, - offset, - status); - if (U_FAILURE(status)) { - return appendTo; - } - // Fix up FieldPosition indexes if our field is found. - if (fieldPositionFoundIndex != -1 && offset != -1) { - pos.setBeginIndex(fpos.getBeginIndex() + offset); - pos.setEndIndex(fpos.getEndIndex() + offset); - } - return appendTo; -} - -MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale, - UErrorCode& ec) { - if (U_FAILURE(ec)) { - return nullptr; - } - LocalPointer fmt(new CurrencyFormat(locale, ec), ec); - return fmt.orphan(); -} - -MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) { - if (U_FAILURE(ec)) { - return nullptr; - } - return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2004-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: April 20, 2004 +* Since: ICU 3.0 +********************************************************************** +*/ +#include "utypeinfo.h" // for 'typeid' to work +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/measfmt.h" +#include "unicode/numfmt.h" +#include "currfmt.h" +#include "unicode/localpointer.h" +#include "resource.h" +#include "unicode/simpleformatter.h" +#include "quantityformatter.h" +#include "unicode/plurrule.h" +#include "unicode/decimfmt.h" +#include "uresimp.h" +#include "unicode/ures.h" +#include "unicode/ustring.h" +#include "ureslocs.h" +#include "cstring.h" +#include "mutex.h" +#include "ucln_in.h" +#include "unicode/listformatter.h" +#include "charstr.h" +#include "unicode/putil.h" +#include "unicode/smpdtfmt.h" +#include "uassert.h" +#include "unicode/numberformatter.h" +#include "number_longnames.h" +#include "number_utypes.h" + +#include "sharednumberformat.h" +#include "sharedpluralrules.h" +#include "standardplural.h" +#include "unifiedcache.h" + + +U_NAMESPACE_BEGIN + +using number::impl::UFormattedNumberData; + +static constexpr int32_t WIDTH_INDEX_COUNT = UMEASFMT_WIDTH_NARROW + 1; + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat) + +// Used to format durations like 5:47 or 21:35:42. +class NumericDateFormatters : public UMemory { +public: + // Formats like H:mm + UnicodeString hourMinute; + + // formats like M:ss + UnicodeString minuteSecond; + + // formats like H:mm:ss + UnicodeString hourMinuteSecond; + + // Constructor that takes the actual patterns for hour-minute, + // minute-second, and hour-minute-second respectively. + NumericDateFormatters( + const UnicodeString &hm, + const UnicodeString &ms, + const UnicodeString &hms) : + hourMinute(hm), + minuteSecond(ms), + hourMinuteSecond(hms) { + } +private: + NumericDateFormatters(const NumericDateFormatters &other); + NumericDateFormatters &operator=(const NumericDateFormatters &other); +}; + +static UMeasureFormatWidth getRegularWidth(UMeasureFormatWidth width) { + if (width >= WIDTH_INDEX_COUNT) { + return UMEASFMT_WIDTH_NARROW; + } + return width; +} + +static UNumberUnitWidth getUnitWidth(UMeasureFormatWidth width) { + switch (width) { + case UMEASFMT_WIDTH_WIDE: + return UNUM_UNIT_WIDTH_FULL_NAME; + case UMEASFMT_WIDTH_NARROW: + case UMEASFMT_WIDTH_NUMERIC: + return UNUM_UNIT_WIDTH_NARROW; + case UMEASFMT_WIDTH_SHORT: + default: + return UNUM_UNIT_WIDTH_SHORT; + } +} + +/** + * Instances contain all MeasureFormat specific data for a particular locale. + * This data is cached. It is never copied, but is shared via shared pointers. + * + * Note: We might change the cache data to have an array[WIDTH_INDEX_COUNT] of + * complete sets of unit & per patterns, + * to correspond to the resource data and its aliases. + * + * TODO: Maybe store more sparsely in general, with pointers rather than potentially-empty objects. + */ +class MeasureFormatCacheData : public SharedObject { +public: + + /** + * Redirection data from root-bundle, top-level sideways aliases. + * - UMEASFMT_WIDTH_COUNT: initial value, just fall back to root + * - UMEASFMT_WIDTH_WIDE/SHORT/NARROW: sideways alias for missing data + */ + UMeasureFormatWidth widthFallback[WIDTH_INDEX_COUNT]; + + MeasureFormatCacheData(); + virtual ~MeasureFormatCacheData(); + + void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) { + delete currencyFormats[widthIndex]; + currencyFormats[widthIndex] = nfToAdopt; + } + const NumberFormat *getCurrencyFormat(UMeasureFormatWidth width) const { + return currencyFormats[getRegularWidth(width)]; + } + void adoptIntegerFormat(NumberFormat *nfToAdopt) { + delete integerFormat; + integerFormat = nfToAdopt; + } + const NumberFormat *getIntegerFormat() const { + return integerFormat; + } + void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) { + delete numericDateFormatters; + numericDateFormatters = formattersToAdopt; + } + const NumericDateFormatters *getNumericDateFormatters() const { + return numericDateFormatters; + } + +private: + NumberFormat* currencyFormats[WIDTH_INDEX_COUNT]; + NumberFormat* integerFormat; + NumericDateFormatters* numericDateFormatters; + + MeasureFormatCacheData(const MeasureFormatCacheData &other); + MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other); +}; + +MeasureFormatCacheData::MeasureFormatCacheData() + : integerFormat(nullptr), numericDateFormatters(nullptr) { + for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { + widthFallback[i] = UMEASFMT_WIDTH_COUNT; + } + memset(currencyFormats, 0, sizeof(currencyFormats)); +} + +MeasureFormatCacheData::~MeasureFormatCacheData() { + for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) { + delete currencyFormats[i]; + } + // Note: the contents of 'dnams' are pointers into the resource bundle + delete integerFormat; + delete numericDateFormatters; +} + +static UBool isCurrency(const MeasureUnit &unit) { + return (uprv_strcmp(unit.getType(), "currency") == 0); +} + +static UBool getString( + const UResourceBundle *resource, + UnicodeString &result, + UErrorCode &status) { + int32_t len = 0; + const char16_t *resStr = ures_getString(resource, &len, &status); + if (U_FAILURE(status)) { + return false; + } + result.setTo(true, resStr, len); + return true; +} + +static UnicodeString loadNumericDateFormatterPattern( + const UResourceBundle *resource, + const char *pattern, + UErrorCode &status) { + UnicodeString result; + if (U_FAILURE(status)) { + return result; + } + CharString chs; + chs.append("durationUnits", status) + .append("/", status).append(pattern, status); + LocalUResourceBundlePointer patternBundle( + ures_getByKeyWithFallback( + resource, + chs.data(), + nullptr, + &status)); + if (U_FAILURE(status)) { + return result; + } + getString(patternBundle.getAlias(), result, status); + // Replace 'h' with 'H' + int32_t len = result.length(); + char16_t *buffer = result.getBuffer(len); + for (int32_t i = 0; i < len; ++i) { + if (buffer[i] == 0x68) { // 'h' + buffer[i] = 0x48; // 'H' + } + } + result.releaseBuffer(len); + return result; +} + +static NumericDateFormatters *loadNumericDateFormatters( + const UResourceBundle *resource, + UErrorCode &status) { + if (U_FAILURE(status)) { + return nullptr; + } + NumericDateFormatters *result = new NumericDateFormatters( + loadNumericDateFormatterPattern(resource, "hm", status), + loadNumericDateFormatterPattern(resource, "ms", status), + loadNumericDateFormatterPattern(resource, "hms", status)); + if (U_FAILURE(status)) { + delete result; + return nullptr; + } + return result; +} + +template<> +const MeasureFormatCacheData *LocaleCacheKey::createObject( + const void * /*unused*/, UErrorCode &status) const { + const char *localeId = fLoc.getName(); + LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, localeId, &status)); + static UNumberFormatStyle currencyStyles[] = { + UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY}; + LocalPointer result(new MeasureFormatCacheData(), status); + if (U_FAILURE(status)) { + return nullptr; + } + result->adoptNumericDateFormatters(loadNumericDateFormatters( + unitsBundle.getAlias(), status)); + if (U_FAILURE(status)) { + return nullptr; + } + + for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { + // NumberFormat::createInstance can erase warning codes from status, so pass it + // a separate status instance + UErrorCode localStatus = U_ZERO_ERROR; + result->adoptCurrencyFormat(i, NumberFormat::createInstance( + localeId, currencyStyles[i], localStatus)); + if (localStatus != U_ZERO_ERROR) { + status = localStatus; + } + if (U_FAILURE(status)) { + return nullptr; + } + } + NumberFormat *inf = NumberFormat::createInstance( + localeId, UNUM_DECIMAL, status); + if (U_FAILURE(status)) { + return nullptr; + } + inf->setMaximumFractionDigits(0); + DecimalFormat *decfmt = dynamic_cast(inf); + if (decfmt != nullptr) { + decfmt->setRoundingMode(DecimalFormat::kRoundDown); + } + result->adoptIntegerFormat(inf); + result->addRef(); + return result.orphan(); +} + +static UBool isTimeUnit(const MeasureUnit &mu, const char *tu) { + return uprv_strcmp(mu.getType(), "duration") == 0 && + uprv_strcmp(mu.getSubtype(), tu) == 0; +} + +// Converts a composite measure into hours-minutes-seconds and stores at hms +// array. [0] is hours; [1] is minutes; [2] is seconds. Returns a bit map of +// units found: 1=hours, 2=minutes, 4=seconds. For example, if measures +// contains hours-minutes, this function would return 3. +// +// If measures cannot be converted into hours, minutes, seconds or if amounts +// are negative, or if hours, minutes, seconds are out of order, returns 0. +static int32_t toHMS( + const Measure *measures, + int32_t measureCount, + Formattable *hms, + UErrorCode &status) { + if (U_FAILURE(status)) { + return 0; + } + int32_t result = 0; + if (U_FAILURE(status)) { + return 0; + } + // We use copy constructor to ensure that both sides of equality operator + // are instances of MeasureUnit base class and not a subclass. Otherwise, + // operator== will immediately return false. + for (int32_t i = 0; i < measureCount; ++i) { + if (isTimeUnit(measures[i].getUnit(), "hour")) { + // hour must come first + if (result >= 1) { + return 0; + } + hms[0] = measures[i].getNumber(); + if (hms[0].getDouble() < 0.0) { + return 0; + } + result |= 1; + } else if (isTimeUnit(measures[i].getUnit(), "minute")) { + // minute must come after hour + if (result >= 2) { + return 0; + } + hms[1] = measures[i].getNumber(); + if (hms[1].getDouble() < 0.0) { + return 0; + } + result |= 2; + } else if (isTimeUnit(measures[i].getUnit(), "second")) { + // second must come after hour and minute + if (result >= 4) { + return 0; + } + hms[2] = measures[i].getNumber(); + if (hms[2].getDouble() < 0.0) { + return 0; + } + result |= 4; + } else { + return 0; + } + } + return result; +} + + +MeasureFormat::MeasureFormat( + const Locale &locale, UMeasureFormatWidth w, UErrorCode &status) + : cache(nullptr), + numberFormat(nullptr), + pluralRules(nullptr), + fWidth(w), + listFormatter(nullptr) { + initMeasureFormat(locale, w, nullptr, status); +} + +MeasureFormat::MeasureFormat( + const Locale &locale, + UMeasureFormatWidth w, + NumberFormat *nfToAdopt, + UErrorCode &status) + : cache(nullptr), + numberFormat(nullptr), + pluralRules(nullptr), + fWidth(w), + listFormatter(nullptr) { + initMeasureFormat(locale, w, nfToAdopt, status); +} + +MeasureFormat::MeasureFormat(const MeasureFormat &other) : + Format(other), + cache(other.cache), + numberFormat(other.numberFormat), + pluralRules(other.pluralRules), + fWidth(other.fWidth), + listFormatter(nullptr) { + cache->addRef(); + numberFormat->addRef(); + pluralRules->addRef(); + if (other.listFormatter != nullptr) { + listFormatter = new ListFormatter(*other.listFormatter); + } +} + +MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) { + if (this == &other) { + return *this; + } + Format::operator=(other); + SharedObject::copyPtr(other.cache, cache); + SharedObject::copyPtr(other.numberFormat, numberFormat); + SharedObject::copyPtr(other.pluralRules, pluralRules); + fWidth = other.fWidth; + delete listFormatter; + if (other.listFormatter != nullptr) { + listFormatter = new ListFormatter(*other.listFormatter); + } else { + listFormatter = nullptr; + } + return *this; +} + +MeasureFormat::MeasureFormat() : + cache(nullptr), + numberFormat(nullptr), + pluralRules(nullptr), + fWidth(UMEASFMT_WIDTH_SHORT), + listFormatter(nullptr) { +} + +MeasureFormat::~MeasureFormat() { + if (cache != nullptr) { + cache->removeRef(); + } + if (numberFormat != nullptr) { + numberFormat->removeRef(); + } + if (pluralRules != nullptr) { + pluralRules->removeRef(); + } + delete listFormatter; +} + +bool MeasureFormat::operator==(const Format &other) const { + if (this == &other) { // Same object, equal + return true; + } + if (!Format::operator==(other)) { + return false; + } + const MeasureFormat &rhs = static_cast(other); + + // Note: Since the ListFormatter depends only on Locale and width, we + // don't have to check it here. + + // differing widths aren't equivalent + if (fWidth != rhs.fWidth) { + return false; + } + // Width the same check locales. + // We don't need to check locales if both objects have same cache. + if (cache != rhs.cache) { + UErrorCode status = U_ZERO_ERROR; + const char *localeId = getLocaleID(status); + const char *rhsLocaleId = rhs.getLocaleID(status); + if (U_FAILURE(status)) { + // On failure, assume not equal + return false; + } + if (uprv_strcmp(localeId, rhsLocaleId) != 0) { + return false; + } + } + // Locales same, check NumberFormat if shared data differs. + return ( + numberFormat == rhs.numberFormat || + **numberFormat == **rhs.numberFormat); +} + +MeasureFormat *MeasureFormat::clone() const { + return new MeasureFormat(*this); +} + +UnicodeString &MeasureFormat::format( + const Formattable &obj, + UnicodeString &appendTo, + FieldPosition &pos, + UErrorCode &status) const { + if (U_FAILURE(status)) return appendTo; + if (obj.getType() == Formattable::kObject) { + const UObject* formatObj = obj.getObject(); + const Measure* amount = dynamic_cast(formatObj); + if (amount != nullptr) { + return formatMeasure( + *amount, **numberFormat, appendTo, pos, status); + } + } + status = U_ILLEGAL_ARGUMENT_ERROR; + return appendTo; +} + +void MeasureFormat::parseObject( + const UnicodeString & /*source*/, + Formattable & /*result*/, + ParsePosition& /*pos*/) const { + return; +} + +UnicodeString &MeasureFormat::formatMeasurePerUnit( + const Measure &measure, + const MeasureUnit &perUnit, + UnicodeString &appendTo, + FieldPosition &pos, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return appendTo; + } + auto* df = dynamic_cast(&getNumberFormatInternal()); + if (df == nullptr) { + // Don't know how to handle other types of NumberFormat + status = U_UNSUPPORTED_ERROR; + return appendTo; + } + UFormattedNumberData result; + if (auto* lnf = df->toNumberFormatter(status)) { + result.quantity.setToDouble(measure.getNumber().getDouble(status)); + lnf->unit(measure.getUnit()) + .perUnit(perUnit) + .unitWidth(getUnitWidth(fWidth)) + .formatImpl(&result, status); + } + DecimalFormat::fieldPositionHelper(result, pos, appendTo.length(), status); + appendTo.append(result.toTempString(status)); + return appendTo; +} + +UnicodeString &MeasureFormat::formatMeasures( + const Measure *measures, + int32_t measureCount, + UnicodeString &appendTo, + FieldPosition &pos, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return appendTo; + } + if (measureCount == 0) { + return appendTo; + } + if (measureCount == 1) { + return formatMeasure(measures[0], **numberFormat, appendTo, pos, status); + } + if (fWidth == UMEASFMT_WIDTH_NUMERIC) { + Formattable hms[3]; + int32_t bitMap = toHMS(measures, measureCount, hms, status); + if (bitMap > 0) { + return formatNumeric(hms, bitMap, appendTo, status); + } + } + if (pos.getField() != FieldPosition::DONT_CARE) { + return formatMeasuresSlowTrack( + measures, measureCount, appendTo, pos, status); + } + UnicodeString *results = new UnicodeString[measureCount]; + if (results == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return appendTo; + } + for (int32_t i = 0; i < measureCount; ++i) { + const NumberFormat *nf = cache->getIntegerFormat(); + if (i == measureCount - 1) { + nf = numberFormat->get(); + } + formatMeasure( + measures[i], + *nf, + results[i], + pos, + status); + } + listFormatter->format(results, measureCount, appendTo, status); + delete [] results; + return appendTo; +} + +UnicodeString MeasureFormat::getUnitDisplayName(const MeasureUnit& unit, UErrorCode& status) const { + return number::impl::LongNameHandler::getUnitDisplayName( + getLocale(status), + unit, + getUnitWidth(fWidth), + status); +} + +void MeasureFormat::initMeasureFormat( + const Locale &locale, + UMeasureFormatWidth w, + NumberFormat *nfToAdopt, + UErrorCode &status) { + static const UListFormatterWidth listWidths[] = { + ULISTFMT_WIDTH_WIDE, + ULISTFMT_WIDTH_SHORT, + ULISTFMT_WIDTH_NARROW}; + LocalPointer nf(nfToAdopt); + if (U_FAILURE(status)) { + return; + } + const char *name = locale.getName(); + setLocaleIDs(name, name); + + UnifiedCache::getByLocale(locale, cache, status); + if (U_FAILURE(status)) { + return; + } + + const SharedPluralRules *pr = PluralRules::createSharedInstance( + locale, UPLURAL_TYPE_CARDINAL, status); + if (U_FAILURE(status)) { + return; + } + SharedObject::copyPtr(pr, pluralRules); + pr->removeRef(); + if (nf.isNull()) { + // TODO: Clean this up + const SharedNumberFormat *shared = NumberFormat::createSharedInstance( + locale, UNUM_DECIMAL, status); + if (U_FAILURE(status)) { + return; + } + SharedObject::copyPtr(shared, numberFormat); + shared->removeRef(); + } else { + adoptNumberFormat(nf.orphan(), status); + if (U_FAILURE(status)) { + return; + } + } + fWidth = w; + delete listFormatter; + listFormatter = ListFormatter::createInstance( + locale, + ULISTFMT_TYPE_UNITS, + listWidths[getRegularWidth(fWidth)], + status); +} + +void MeasureFormat::adoptNumberFormat( + NumberFormat *nfToAdopt, UErrorCode &status) { + LocalPointer nf(nfToAdopt); + if (U_FAILURE(status)) { + return; + } + SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias()); + if (shared == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + nf.orphan(); + SharedObject::copyPtr(shared, numberFormat); +} + +UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) { + if (U_FAILURE(status) || locale == getLocale(status)) { + return false; + } + initMeasureFormat(locale, fWidth, nullptr, status); + return U_SUCCESS(status); +} + +const NumberFormat &MeasureFormat::getNumberFormatInternal() const { + return **numberFormat; +} + +const NumberFormat &MeasureFormat::getCurrencyFormatInternal() const { + return *cache->getCurrencyFormat(UMEASFMT_WIDTH_NARROW); +} + +const PluralRules &MeasureFormat::getPluralRules() const { + return **pluralRules; +} + +Locale MeasureFormat::getLocale(UErrorCode &status) const { + return Format::getLocale(ULOC_VALID_LOCALE, status); +} + +const char *MeasureFormat::getLocaleID(UErrorCode &status) const { + return Format::getLocaleID(ULOC_VALID_LOCALE, status); +} + +UnicodeString &MeasureFormat::formatMeasure( + const Measure &measure, + const NumberFormat &nf, + UnicodeString &appendTo, + FieldPosition &pos, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return appendTo; + } + const Formattable& amtNumber = measure.getNumber(); + const MeasureUnit& amtUnit = measure.getUnit(); + if (isCurrency(amtUnit)) { + char16_t isoCode[4]; + u_charsToUChars(amtUnit.getSubtype(), isoCode, 4); + return cache->getCurrencyFormat(fWidth)->format( + new CurrencyAmount(amtNumber, isoCode, status), + appendTo, + pos, + status); + } + auto* df = dynamic_cast(&nf); + if (df == nullptr) { + // Handle other types of NumberFormat using the ICU 63 code, modified to + // get the unitPattern from LongNameHandler and handle fallback to OTHER. + UnicodeString formattedNumber; + StandardPlural::Form pluralForm = QuantityFormatter::selectPlural( + amtNumber, nf, **pluralRules, formattedNumber, pos, status); + UnicodeString pattern = number::impl::LongNameHandler::getUnitPattern(getLocale(status), + amtUnit, getUnitWidth(fWidth), pluralForm, status); + // The above handles fallback from other widths to short, and from other plural forms to OTHER + if (U_FAILURE(status)) { + return appendTo; + } + SimpleFormatter formatter(pattern, 0, 1, status); + return QuantityFormatter::format(formatter, formattedNumber, appendTo, pos, status); + } + UFormattedNumberData result; + if (auto* lnf = df->toNumberFormatter(status)) { + result.quantity.setToDouble(amtNumber.getDouble(status)); + lnf->unit(amtUnit) + .unitWidth(getUnitWidth(fWidth)) + .formatImpl(&result, status); + } + DecimalFormat::fieldPositionHelper(result, pos, appendTo.length(), status); + appendTo.append(result.toTempString(status)); + return appendTo; +} + + +// Formats numeric time duration as 5:00:47 or 3:54. +UnicodeString &MeasureFormat::formatNumeric( + const Formattable *hms, // always length 3 + int32_t bitMap, // 1=hour set, 2=minute set, 4=second set + UnicodeString &appendTo, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return appendTo; + } + + UnicodeString pattern; + + double hours = hms[0].getDouble(status); + double minutes = hms[1].getDouble(status); + double seconds = hms[2].getDouble(status); + if (U_FAILURE(status)) { + return appendTo; + } + + // All possible combinations: "h", "m", "s", "hm", "hs", "ms", "hms" + if (bitMap == 5 || bitMap == 7) { // "hms" & "hs" (we add minutes if "hs") + pattern = cache->getNumericDateFormatters()->hourMinuteSecond; + hours = uprv_trunc(hours); + minutes = uprv_trunc(minutes); + } else if (bitMap == 3) { // "hm" + pattern = cache->getNumericDateFormatters()->hourMinute; + hours = uprv_trunc(hours); + } else if (bitMap == 6) { // "ms" + pattern = cache->getNumericDateFormatters()->minuteSecond; + minutes = uprv_trunc(minutes); + } else { // h m s, handled outside formatNumeric. No value is also an error. + status = U_INTERNAL_PROGRAM_ERROR; + return appendTo; + } + + const DecimalFormat *numberFormatter = dynamic_cast(numberFormat->get()); + if (!numberFormatter) { + status = U_INTERNAL_PROGRAM_ERROR; + return appendTo; + } + number::LocalizedNumberFormatter numberFormatter2; + if (auto* lnf = numberFormatter->toNumberFormatter(status)) { + numberFormatter2 = lnf->integerWidth(number::IntegerWidth::zeroFillTo(2)); + } else { + return appendTo; + } + + FormattedStringBuilder fsb; + + UBool protect = false; + const int32_t patternLength = pattern.length(); + for (int32_t i = 0; i < patternLength; i++) { + char16_t c = pattern[i]; + + // Also set the proper field in this switch + // We don't use DateFormat.Field because this is not a date / time, is a duration. + double value = 0; + switch (c) { + case u'H': value = hours; break; + case u'm': value = minutes; break; + case u's': value = seconds; break; + } + + // There is not enough info to add Field(s) for the unit because all we have are plain + // text patterns. For example in "21:51" there is no text for something like "hour", + // while in something like "21h51" there is ("h"). But we can't really tell... + switch (c) { + case u'H': + case u'm': + case u's': + if (protect) { + fsb.appendChar16(c, kUndefinedField, status); + } else { + UnicodeString tmp; + if ((i + 1 < patternLength) && pattern[i + 1] == c) { // doubled + tmp = numberFormatter2.formatDouble(value, status).toString(status); + i++; + } else { + numberFormatter->format(value, tmp, status); + } + // TODO: Use proper Field + fsb.append(tmp, kUndefinedField, status); + } + break; + case u'\'': + // '' is escaped apostrophe + if ((i + 1 < patternLength) && pattern[i + 1] == c) { + fsb.appendChar16(c, kUndefinedField, status); + i++; + } else { + protect = !protect; + } + break; + default: + fsb.appendChar16(c, kUndefinedField, status); + } + } + + appendTo.append(fsb.toTempUnicodeString()); + + return appendTo; +} + +UnicodeString &MeasureFormat::formatMeasuresSlowTrack( + const Measure *measures, + int32_t measureCount, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return appendTo; + } + FieldPosition dontCare(FieldPosition::DONT_CARE); + FieldPosition fpos(pos.getField()); + LocalArray results(new UnicodeString[measureCount], status); + int32_t fieldPositionFoundIndex = -1; + for (int32_t i = 0; i < measureCount; ++i) { + const NumberFormat *nf = cache->getIntegerFormat(); + if (i == measureCount - 1) { + nf = numberFormat->get(); + } + if (fieldPositionFoundIndex == -1) { + formatMeasure(measures[i], *nf, results[i], fpos, status); + if (U_FAILURE(status)) { + return appendTo; + } + if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) { + fieldPositionFoundIndex = i; + } + } else { + formatMeasure(measures[i], *nf, results[i], dontCare, status); + } + } + int32_t offset; + listFormatter->format( + results.getAlias(), + measureCount, + appendTo, + fieldPositionFoundIndex, + offset, + status); + if (U_FAILURE(status)) { + return appendTo; + } + // Fix up FieldPosition indexes if our field is found. + if (fieldPositionFoundIndex != -1 && offset != -1) { + pos.setBeginIndex(fpos.getBeginIndex() + offset); + pos.setEndIndex(fpos.getEndIndex() + offset); + } + return appendTo; +} + +MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale, + UErrorCode& ec) { + if (U_FAILURE(ec)) { + return nullptr; + } + LocalPointer fmt(new CurrencyFormat(locale, ec), ec); + return fmt.orphan(); +} + +MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) { + if (U_FAILURE(ec)) { + return nullptr; + } + return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/measunit.cpp b/deps/icu-small/source/i18n/measunit.cpp index f53137c48cd301..5440d08ba16a47 100644 --- a/deps/icu-small/source/i18n/measunit.cpp +++ b/deps/icu-small/source/i18n/measunit.cpp @@ -1,2382 +1,2391 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2004-2016, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: April 26, 2004 -* Since: ICU 3.0 -********************************************************************** -*/ -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/measunit.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/uenum.h" -#include "unicode/errorcode.h" -#include "ustrenum.h" -#include "cstring.h" -#include "uassert.h" -#include "measunit_impl.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureUnit) - -// All code between the "Start generated code" comment and -// the "End generated code" comment is auto generated code -// and must not be edited manually. For instructions on how to correctly -// update this code, refer to: -// https://icu.unicode.org/design/formatting/measureformat/updating-measure-unit -// -// Start generated code for measunit.cpp - -// Maps from Type ID to offset in gSubTypes. -static const int32_t gOffsets[] = { - 0, - 2, - 7, - 17, - 27, - 31, - 332, - 343, - 360, - 364, - 373, - 376, - 380, - 388, - 410, - 414, - 429, - 430, - 436, - 446, - 450, - 454, - 456, - 490 -}; - -static const int32_t kCurrencyOffset = 5; - -// Must be sorted alphabetically. -static const char * const gTypes[] = { - "acceleration", - "angle", - "area", - "concentr", - "consumption", - "currency", - "digital", - "duration", - "electric", - "energy", - "force", - "frequency", - "graphics", - "length", - "light", - "mass", - "none", - "power", - "pressure", - "speed", - "temperature", - "torque", - "volume" -}; - -// Must be grouped by type and sorted alphabetically within each type. -static const char * const gSubTypes[] = { - "g-force", - "meter-per-square-second", - "arc-minute", - "arc-second", - "degree", - "radian", - "revolution", - "acre", - "dunam", - "hectare", - "square-centimeter", - "square-foot", - "square-inch", - "square-kilometer", - "square-meter", - "square-mile", - "square-yard", - "item", - "karat", - "milligram-ofglucose-per-deciliter", - "milligram-per-deciliter", - "millimole-per-liter", - "mole", - "percent", - "permille", - "permillion", - "permyriad", - "liter-per-100-kilometer", - "liter-per-kilometer", - "mile-per-gallon", - "mile-per-gallon-imperial", - "ADP", - "AED", - "AFA", - "AFN", - "ALK", - "ALL", - "AMD", - "ANG", - "AOA", - "AOK", - "AON", - "AOR", - "ARA", - "ARP", - "ARS", - "ARY", - "ATS", - "AUD", - "AWG", - "AYM", - "AZM", - "AZN", - "BAD", - "BAM", - "BBD", - "BDT", - "BEC", - "BEF", - "BEL", - "BGJ", - "BGK", - "BGL", - "BGN", - "BHD", - "BIF", - "BMD", - "BND", - "BOB", - "BOP", - "BOV", - "BRB", - "BRC", - "BRE", - "BRL", - "BRN", - "BRR", - "BSD", - "BTN", - "BUK", - "BWP", - "BYB", - "BYN", - "BYR", - "BZD", - "CAD", - "CDF", - "CHC", - "CHE", - "CHF", - "CHW", - "CLF", - "CLP", - "CNY", - "COP", - "COU", - "CRC", - "CSD", - "CSJ", - "CSK", - "CUC", - "CUP", - "CVE", - "CYP", - "CZK", - "DDM", - "DEM", - "DJF", - "DKK", - "DOP", - "DZD", - "ECS", - "ECV", - "EEK", - "EGP", - "ERN", - "ESA", - "ESB", - "ESP", - "ETB", - "EUR", - "FIM", - "FJD", - "FKP", - "FRF", - "GBP", - "GEK", - "GEL", - "GHC", - "GHP", - "GHS", - "GIP", - "GMD", - "GNE", - "GNF", - "GNS", - "GQE", - "GRD", - "GTQ", - "GWE", - "GWP", - "GYD", - "HKD", - "HNL", - "HRD", - "HRK", - "HTG", - "HUF", - "IDR", - "IEP", - "ILP", - "ILR", - "ILS", - "INR", - "IQD", - "IRR", - "ISJ", - "ISK", - "ITL", - "JMD", - "JOD", - "JPY", - "KES", - "KGS", - "KHR", - "KMF", - "KPW", - "KRW", - "KWD", - "KYD", - "KZT", - "LAJ", - "LAK", - "LBP", - "LKR", - "LRD", - "LSL", - "LSM", - "LTL", - "LTT", - "LUC", - "LUF", - "LUL", - "LVL", - "LVR", - "LYD", - "MAD", - "MDL", - "MGA", - "MGF", - "MKD", - "MLF", - "MMK", - "MNT", - "MOP", - "MRO", - "MRU", - "MTL", - "MTP", - "MUR", - "MVQ", - "MVR", - "MWK", - "MXN", - "MXP", - "MXV", - "MYR", - "MZE", - "MZM", - "MZN", - "NAD", - "NGN", - "NIC", - "NIO", - "NLG", - "NOK", - "NPR", - "NZD", - "OMR", - "PAB", - "PEH", - "PEI", - "PEN", - "PES", - "PGK", - "PHP", - "PKR", - "PLN", - "PLZ", - "PTE", - "PYG", - "QAR", - "RHD", - "ROK", - "ROL", - "RON", - "RSD", - "RUB", - "RUR", - "RWF", - "SAR", - "SBD", - "SCR", - "SDD", - "SDG", - "SDP", - "SEK", - "SGD", - "SHP", - "SIT", - "SKK", - "SLE", - "SLL", - "SOS", - "SRD", - "SRG", - "SSP", - "STD", - "STN", - "SUR", - "SVC", - "SYP", - "SZL", - "THB", - "TJR", - "TJS", - "TMM", - "TMT", - "TND", - "TOP", - "TPE", - "TRL", - "TRY", - "TTD", - "TWD", - "TZS", - "UAH", - "UAK", - "UGS", - "UGW", - "UGX", - "USD", - "USN", - "USS", - "UYI", - "UYN", - "UYP", - "UYU", - "UYW", - "UZS", - "VEB", - "VED", - "VEF", - "VES", - "VNC", - "VND", - "VUV", - "WST", - "XAF", - "XAG", - "XAU", - "XBA", - "XBB", - "XBC", - "XBD", - "XCD", - "XDR", - "XEU", - "XOF", - "XPD", - "XPF", - "XPT", - "XSU", - "XTS", - "XUA", - "XXX", - "YDD", - "YER", - "YUD", - "YUM", - "YUN", - "ZAL", - "ZAR", - "ZMK", - "ZMW", - "ZRN", - "ZRZ", - "ZWC", - "ZWD", - "ZWL", - "ZWN", - "ZWR", - "bit", - "byte", - "gigabit", - "gigabyte", - "kilobit", - "kilobyte", - "megabit", - "megabyte", - "petabyte", - "terabit", - "terabyte", - "century", - "day", - "day-person", - "decade", - "hour", - "microsecond", - "millisecond", - "minute", - "month", - "month-person", - "nanosecond", - "quarter", - "second", - "week", - "week-person", - "year", - "year-person", - "ampere", - "milliampere", - "ohm", - "volt", - "british-thermal-unit", - "calorie", - "electronvolt", - "foodcalorie", - "joule", - "kilocalorie", - "kilojoule", - "kilowatt-hour", - "therm-us", - "kilowatt-hour-per-100-kilometer", - "newton", - "pound-force", - "gigahertz", - "hertz", - "kilohertz", - "megahertz", - "dot", - "dot-per-centimeter", - "dot-per-inch", - "em", - "megapixel", - "pixel", - "pixel-per-centimeter", - "pixel-per-inch", - "astronomical-unit", - "centimeter", - "decimeter", - "earth-radius", - "fathom", - "foot", - "furlong", - "inch", - "kilometer", - "light-year", - "meter", - "micrometer", - "mile", - "mile-scandinavian", - "millimeter", - "nanometer", - "nautical-mile", - "parsec", - "picometer", - "point", - "solar-radius", - "yard", - "candela", - "lumen", - "lux", - "solar-luminosity", - "carat", - "dalton", - "earth-mass", - "grain", - "gram", - "kilogram", - "microgram", - "milligram", - "ounce", - "ounce-troy", - "pound", - "solar-mass", - "stone", - "ton", - "tonne", - "", - "gigawatt", - "horsepower", - "kilowatt", - "megawatt", - "milliwatt", - "watt", - "atmosphere", - "bar", - "hectopascal", - "inch-ofhg", - "kilopascal", - "megapascal", - "millibar", - "millimeter-ofhg", - "pascal", - "pound-force-per-square-inch", - "kilometer-per-hour", - "knot", - "meter-per-second", - "mile-per-hour", - "celsius", - "fahrenheit", - "generic", - "kelvin", - "newton-meter", - "pound-force-foot", - "acre-foot", - "barrel", - "bushel", - "centiliter", - "cubic-centimeter", - "cubic-foot", - "cubic-inch", - "cubic-kilometer", - "cubic-meter", - "cubic-mile", - "cubic-yard", - "cup", - "cup-metric", - "deciliter", - "dessert-spoon", - "dessert-spoon-imperial", - "dram", - "drop", - "fluid-ounce", - "fluid-ounce-imperial", - "gallon", - "gallon-imperial", - "hectoliter", - "jigger", - "liter", - "megaliter", - "milliliter", - "pinch", - "pint", - "pint-metric", - "quart", - "quart-imperial", - "tablespoon", - "teaspoon" -}; - -// Shortcuts to the base unit in order to make the default constructor fast -static const int32_t kBaseTypeIdx = 16; -static const int32_t kBaseSubTypeIdx = 0; - -MeasureUnit *MeasureUnit::createGForce(UErrorCode &status) { - return MeasureUnit::create(0, 0, status); -} - -MeasureUnit MeasureUnit::getGForce() { - return MeasureUnit(0, 0); -} - -MeasureUnit *MeasureUnit::createMeterPerSecondSquared(UErrorCode &status) { - return MeasureUnit::create(0, 1, status); -} - -MeasureUnit MeasureUnit::getMeterPerSecondSquared() { - return MeasureUnit(0, 1); -} - -MeasureUnit *MeasureUnit::createArcMinute(UErrorCode &status) { - return MeasureUnit::create(1, 0, status); -} - -MeasureUnit MeasureUnit::getArcMinute() { - return MeasureUnit(1, 0); -} - -MeasureUnit *MeasureUnit::createArcSecond(UErrorCode &status) { - return MeasureUnit::create(1, 1, status); -} - -MeasureUnit MeasureUnit::getArcSecond() { - return MeasureUnit(1, 1); -} - -MeasureUnit *MeasureUnit::createDegree(UErrorCode &status) { - return MeasureUnit::create(1, 2, status); -} - -MeasureUnit MeasureUnit::getDegree() { - return MeasureUnit(1, 2); -} - -MeasureUnit *MeasureUnit::createRadian(UErrorCode &status) { - return MeasureUnit::create(1, 3, status); -} - -MeasureUnit MeasureUnit::getRadian() { - return MeasureUnit(1, 3); -} - -MeasureUnit *MeasureUnit::createRevolutionAngle(UErrorCode &status) { - return MeasureUnit::create(1, 4, status); -} - -MeasureUnit MeasureUnit::getRevolutionAngle() { - return MeasureUnit(1, 4); -} - -MeasureUnit *MeasureUnit::createAcre(UErrorCode &status) { - return MeasureUnit::create(2, 0, status); -} - -MeasureUnit MeasureUnit::getAcre() { - return MeasureUnit(2, 0); -} - -MeasureUnit *MeasureUnit::createDunam(UErrorCode &status) { - return MeasureUnit::create(2, 1, status); -} - -MeasureUnit MeasureUnit::getDunam() { - return MeasureUnit(2, 1); -} - -MeasureUnit *MeasureUnit::createHectare(UErrorCode &status) { - return MeasureUnit::create(2, 2, status); -} - -MeasureUnit MeasureUnit::getHectare() { - return MeasureUnit(2, 2); -} - -MeasureUnit *MeasureUnit::createSquareCentimeter(UErrorCode &status) { - return MeasureUnit::create(2, 3, status); -} - -MeasureUnit MeasureUnit::getSquareCentimeter() { - return MeasureUnit(2, 3); -} - -MeasureUnit *MeasureUnit::createSquareFoot(UErrorCode &status) { - return MeasureUnit::create(2, 4, status); -} - -MeasureUnit MeasureUnit::getSquareFoot() { - return MeasureUnit(2, 4); -} - -MeasureUnit *MeasureUnit::createSquareInch(UErrorCode &status) { - return MeasureUnit::create(2, 5, status); -} - -MeasureUnit MeasureUnit::getSquareInch() { - return MeasureUnit(2, 5); -} - -MeasureUnit *MeasureUnit::createSquareKilometer(UErrorCode &status) { - return MeasureUnit::create(2, 6, status); -} - -MeasureUnit MeasureUnit::getSquareKilometer() { - return MeasureUnit(2, 6); -} - -MeasureUnit *MeasureUnit::createSquareMeter(UErrorCode &status) { - return MeasureUnit::create(2, 7, status); -} - -MeasureUnit MeasureUnit::getSquareMeter() { - return MeasureUnit(2, 7); -} - -MeasureUnit *MeasureUnit::createSquareMile(UErrorCode &status) { - return MeasureUnit::create(2, 8, status); -} - -MeasureUnit MeasureUnit::getSquareMile() { - return MeasureUnit(2, 8); -} - -MeasureUnit *MeasureUnit::createSquareYard(UErrorCode &status) { - return MeasureUnit::create(2, 9, status); -} - -MeasureUnit MeasureUnit::getSquareYard() { - return MeasureUnit(2, 9); -} - -MeasureUnit *MeasureUnit::createItem(UErrorCode &status) { - return MeasureUnit::create(3, 0, status); -} - -MeasureUnit MeasureUnit::getItem() { - return MeasureUnit(3, 0); -} - -MeasureUnit *MeasureUnit::createKarat(UErrorCode &status) { - return MeasureUnit::create(3, 1, status); -} - -MeasureUnit MeasureUnit::getKarat() { - return MeasureUnit(3, 1); -} - -MeasureUnit *MeasureUnit::createMilligramOfglucosePerDeciliter(UErrorCode &status) { - return MeasureUnit::create(3, 2, status); -} - -MeasureUnit MeasureUnit::getMilligramOfglucosePerDeciliter() { - return MeasureUnit(3, 2); -} - -MeasureUnit *MeasureUnit::createMilligramPerDeciliter(UErrorCode &status) { - return MeasureUnit::create(3, 3, status); -} - -MeasureUnit MeasureUnit::getMilligramPerDeciliter() { - return MeasureUnit(3, 3); -} - -MeasureUnit *MeasureUnit::createMillimolePerLiter(UErrorCode &status) { - return MeasureUnit::create(3, 4, status); -} - -MeasureUnit MeasureUnit::getMillimolePerLiter() { - return MeasureUnit(3, 4); -} - -MeasureUnit *MeasureUnit::createMole(UErrorCode &status) { - return MeasureUnit::create(3, 5, status); -} - -MeasureUnit MeasureUnit::getMole() { - return MeasureUnit(3, 5); -} - -MeasureUnit *MeasureUnit::createPercent(UErrorCode &status) { - return MeasureUnit::create(3, 6, status); -} - -MeasureUnit MeasureUnit::getPercent() { - return MeasureUnit(3, 6); -} - -MeasureUnit *MeasureUnit::createPermille(UErrorCode &status) { - return MeasureUnit::create(3, 7, status); -} - -MeasureUnit MeasureUnit::getPermille() { - return MeasureUnit(3, 7); -} - -MeasureUnit *MeasureUnit::createPartPerMillion(UErrorCode &status) { - return MeasureUnit::create(3, 8, status); -} - -MeasureUnit MeasureUnit::getPartPerMillion() { - return MeasureUnit(3, 8); -} - -MeasureUnit *MeasureUnit::createPermyriad(UErrorCode &status) { - return MeasureUnit::create(3, 9, status); -} - -MeasureUnit MeasureUnit::getPermyriad() { - return MeasureUnit(3, 9); -} - -MeasureUnit *MeasureUnit::createLiterPer100Kilometers(UErrorCode &status) { - return MeasureUnit::create(4, 0, status); -} - -MeasureUnit MeasureUnit::getLiterPer100Kilometers() { - return MeasureUnit(4, 0); -} - -MeasureUnit *MeasureUnit::createLiterPerKilometer(UErrorCode &status) { - return MeasureUnit::create(4, 1, status); -} - -MeasureUnit MeasureUnit::getLiterPerKilometer() { - return MeasureUnit(4, 1); -} - -MeasureUnit *MeasureUnit::createMilePerGallon(UErrorCode &status) { - return MeasureUnit::create(4, 2, status); -} - -MeasureUnit MeasureUnit::getMilePerGallon() { - return MeasureUnit(4, 2); -} - -MeasureUnit *MeasureUnit::createMilePerGallonImperial(UErrorCode &status) { - return MeasureUnit::create(4, 3, status); -} - -MeasureUnit MeasureUnit::getMilePerGallonImperial() { - return MeasureUnit(4, 3); -} - -MeasureUnit *MeasureUnit::createBit(UErrorCode &status) { - return MeasureUnit::create(6, 0, status); -} - -MeasureUnit MeasureUnit::getBit() { - return MeasureUnit(6, 0); -} - -MeasureUnit *MeasureUnit::createByte(UErrorCode &status) { - return MeasureUnit::create(6, 1, status); -} - -MeasureUnit MeasureUnit::getByte() { - return MeasureUnit(6, 1); -} - -MeasureUnit *MeasureUnit::createGigabit(UErrorCode &status) { - return MeasureUnit::create(6, 2, status); -} - -MeasureUnit MeasureUnit::getGigabit() { - return MeasureUnit(6, 2); -} - -MeasureUnit *MeasureUnit::createGigabyte(UErrorCode &status) { - return MeasureUnit::create(6, 3, status); -} - -MeasureUnit MeasureUnit::getGigabyte() { - return MeasureUnit(6, 3); -} - -MeasureUnit *MeasureUnit::createKilobit(UErrorCode &status) { - return MeasureUnit::create(6, 4, status); -} - -MeasureUnit MeasureUnit::getKilobit() { - return MeasureUnit(6, 4); -} - -MeasureUnit *MeasureUnit::createKilobyte(UErrorCode &status) { - return MeasureUnit::create(6, 5, status); -} - -MeasureUnit MeasureUnit::getKilobyte() { - return MeasureUnit(6, 5); -} - -MeasureUnit *MeasureUnit::createMegabit(UErrorCode &status) { - return MeasureUnit::create(6, 6, status); -} - -MeasureUnit MeasureUnit::getMegabit() { - return MeasureUnit(6, 6); -} - -MeasureUnit *MeasureUnit::createMegabyte(UErrorCode &status) { - return MeasureUnit::create(6, 7, status); -} - -MeasureUnit MeasureUnit::getMegabyte() { - return MeasureUnit(6, 7); -} - -MeasureUnit *MeasureUnit::createPetabyte(UErrorCode &status) { - return MeasureUnit::create(6, 8, status); -} - -MeasureUnit MeasureUnit::getPetabyte() { - return MeasureUnit(6, 8); -} - -MeasureUnit *MeasureUnit::createTerabit(UErrorCode &status) { - return MeasureUnit::create(6, 9, status); -} - -MeasureUnit MeasureUnit::getTerabit() { - return MeasureUnit(6, 9); -} - -MeasureUnit *MeasureUnit::createTerabyte(UErrorCode &status) { - return MeasureUnit::create(6, 10, status); -} - -MeasureUnit MeasureUnit::getTerabyte() { - return MeasureUnit(6, 10); -} - -MeasureUnit *MeasureUnit::createCentury(UErrorCode &status) { - return MeasureUnit::create(7, 0, status); -} - -MeasureUnit MeasureUnit::getCentury() { - return MeasureUnit(7, 0); -} - -MeasureUnit *MeasureUnit::createDay(UErrorCode &status) { - return MeasureUnit::create(7, 1, status); -} - -MeasureUnit MeasureUnit::getDay() { - return MeasureUnit(7, 1); -} - -MeasureUnit *MeasureUnit::createDayPerson(UErrorCode &status) { - return MeasureUnit::create(7, 2, status); -} - -MeasureUnit MeasureUnit::getDayPerson() { - return MeasureUnit(7, 2); -} - -MeasureUnit *MeasureUnit::createDecade(UErrorCode &status) { - return MeasureUnit::create(7, 3, status); -} - -MeasureUnit MeasureUnit::getDecade() { - return MeasureUnit(7, 3); -} - -MeasureUnit *MeasureUnit::createHour(UErrorCode &status) { - return MeasureUnit::create(7, 4, status); -} - -MeasureUnit MeasureUnit::getHour() { - return MeasureUnit(7, 4); -} - -MeasureUnit *MeasureUnit::createMicrosecond(UErrorCode &status) { - return MeasureUnit::create(7, 5, status); -} - -MeasureUnit MeasureUnit::getMicrosecond() { - return MeasureUnit(7, 5); -} - -MeasureUnit *MeasureUnit::createMillisecond(UErrorCode &status) { - return MeasureUnit::create(7, 6, status); -} - -MeasureUnit MeasureUnit::getMillisecond() { - return MeasureUnit(7, 6); -} - -MeasureUnit *MeasureUnit::createMinute(UErrorCode &status) { - return MeasureUnit::create(7, 7, status); -} - -MeasureUnit MeasureUnit::getMinute() { - return MeasureUnit(7, 7); -} - -MeasureUnit *MeasureUnit::createMonth(UErrorCode &status) { - return MeasureUnit::create(7, 8, status); -} - -MeasureUnit MeasureUnit::getMonth() { - return MeasureUnit(7, 8); -} - -MeasureUnit *MeasureUnit::createMonthPerson(UErrorCode &status) { - return MeasureUnit::create(7, 9, status); -} - -MeasureUnit MeasureUnit::getMonthPerson() { - return MeasureUnit(7, 9); -} - -MeasureUnit *MeasureUnit::createNanosecond(UErrorCode &status) { - return MeasureUnit::create(7, 10, status); -} - -MeasureUnit MeasureUnit::getNanosecond() { - return MeasureUnit(7, 10); -} - -MeasureUnit *MeasureUnit::createQuarter(UErrorCode &status) { - return MeasureUnit::create(7, 11, status); -} - -MeasureUnit MeasureUnit::getQuarter() { - return MeasureUnit(7, 11); -} - -MeasureUnit *MeasureUnit::createSecond(UErrorCode &status) { - return MeasureUnit::create(7, 12, status); -} - -MeasureUnit MeasureUnit::getSecond() { - return MeasureUnit(7, 12); -} - -MeasureUnit *MeasureUnit::createWeek(UErrorCode &status) { - return MeasureUnit::create(7, 13, status); -} - -MeasureUnit MeasureUnit::getWeek() { - return MeasureUnit(7, 13); -} - -MeasureUnit *MeasureUnit::createWeekPerson(UErrorCode &status) { - return MeasureUnit::create(7, 14, status); -} - -MeasureUnit MeasureUnit::getWeekPerson() { - return MeasureUnit(7, 14); -} - -MeasureUnit *MeasureUnit::createYear(UErrorCode &status) { - return MeasureUnit::create(7, 15, status); -} - -MeasureUnit MeasureUnit::getYear() { - return MeasureUnit(7, 15); -} - -MeasureUnit *MeasureUnit::createYearPerson(UErrorCode &status) { - return MeasureUnit::create(7, 16, status); -} - -MeasureUnit MeasureUnit::getYearPerson() { - return MeasureUnit(7, 16); -} - -MeasureUnit *MeasureUnit::createAmpere(UErrorCode &status) { - return MeasureUnit::create(8, 0, status); -} - -MeasureUnit MeasureUnit::getAmpere() { - return MeasureUnit(8, 0); -} - -MeasureUnit *MeasureUnit::createMilliampere(UErrorCode &status) { - return MeasureUnit::create(8, 1, status); -} - -MeasureUnit MeasureUnit::getMilliampere() { - return MeasureUnit(8, 1); -} - -MeasureUnit *MeasureUnit::createOhm(UErrorCode &status) { - return MeasureUnit::create(8, 2, status); -} - -MeasureUnit MeasureUnit::getOhm() { - return MeasureUnit(8, 2); -} - -MeasureUnit *MeasureUnit::createVolt(UErrorCode &status) { - return MeasureUnit::create(8, 3, status); -} - -MeasureUnit MeasureUnit::getVolt() { - return MeasureUnit(8, 3); -} - -MeasureUnit *MeasureUnit::createBritishThermalUnit(UErrorCode &status) { - return MeasureUnit::create(9, 0, status); -} - -MeasureUnit MeasureUnit::getBritishThermalUnit() { - return MeasureUnit(9, 0); -} - -MeasureUnit *MeasureUnit::createCalorie(UErrorCode &status) { - return MeasureUnit::create(9, 1, status); -} - -MeasureUnit MeasureUnit::getCalorie() { - return MeasureUnit(9, 1); -} - -MeasureUnit *MeasureUnit::createElectronvolt(UErrorCode &status) { - return MeasureUnit::create(9, 2, status); -} - -MeasureUnit MeasureUnit::getElectronvolt() { - return MeasureUnit(9, 2); -} - -MeasureUnit *MeasureUnit::createFoodcalorie(UErrorCode &status) { - return MeasureUnit::create(9, 3, status); -} - -MeasureUnit MeasureUnit::getFoodcalorie() { - return MeasureUnit(9, 3); -} - -MeasureUnit *MeasureUnit::createJoule(UErrorCode &status) { - return MeasureUnit::create(9, 4, status); -} - -MeasureUnit MeasureUnit::getJoule() { - return MeasureUnit(9, 4); -} - -MeasureUnit *MeasureUnit::createKilocalorie(UErrorCode &status) { - return MeasureUnit::create(9, 5, status); -} - -MeasureUnit MeasureUnit::getKilocalorie() { - return MeasureUnit(9, 5); -} - -MeasureUnit *MeasureUnit::createKilojoule(UErrorCode &status) { - return MeasureUnit::create(9, 6, status); -} - -MeasureUnit MeasureUnit::getKilojoule() { - return MeasureUnit(9, 6); -} - -MeasureUnit *MeasureUnit::createKilowattHour(UErrorCode &status) { - return MeasureUnit::create(9, 7, status); -} - -MeasureUnit MeasureUnit::getKilowattHour() { - return MeasureUnit(9, 7); -} - -MeasureUnit *MeasureUnit::createThermUs(UErrorCode &status) { - return MeasureUnit::create(9, 8, status); -} - -MeasureUnit MeasureUnit::getThermUs() { - return MeasureUnit(9, 8); -} - -MeasureUnit *MeasureUnit::createKilowattHourPer100Kilometer(UErrorCode &status) { - return MeasureUnit::create(10, 0, status); -} - -MeasureUnit MeasureUnit::getKilowattHourPer100Kilometer() { - return MeasureUnit(10, 0); -} - -MeasureUnit *MeasureUnit::createNewton(UErrorCode &status) { - return MeasureUnit::create(10, 1, status); -} - -MeasureUnit MeasureUnit::getNewton() { - return MeasureUnit(10, 1); -} - -MeasureUnit *MeasureUnit::createPoundForce(UErrorCode &status) { - return MeasureUnit::create(10, 2, status); -} - -MeasureUnit MeasureUnit::getPoundForce() { - return MeasureUnit(10, 2); -} - -MeasureUnit *MeasureUnit::createGigahertz(UErrorCode &status) { - return MeasureUnit::create(11, 0, status); -} - -MeasureUnit MeasureUnit::getGigahertz() { - return MeasureUnit(11, 0); -} - -MeasureUnit *MeasureUnit::createHertz(UErrorCode &status) { - return MeasureUnit::create(11, 1, status); -} - -MeasureUnit MeasureUnit::getHertz() { - return MeasureUnit(11, 1); -} - -MeasureUnit *MeasureUnit::createKilohertz(UErrorCode &status) { - return MeasureUnit::create(11, 2, status); -} - -MeasureUnit MeasureUnit::getKilohertz() { - return MeasureUnit(11, 2); -} - -MeasureUnit *MeasureUnit::createMegahertz(UErrorCode &status) { - return MeasureUnit::create(11, 3, status); -} - -MeasureUnit MeasureUnit::getMegahertz() { - return MeasureUnit(11, 3); -} - -MeasureUnit *MeasureUnit::createDot(UErrorCode &status) { - return MeasureUnit::create(12, 0, status); -} - -MeasureUnit MeasureUnit::getDot() { - return MeasureUnit(12, 0); -} - -MeasureUnit *MeasureUnit::createDotPerCentimeter(UErrorCode &status) { - return MeasureUnit::create(12, 1, status); -} - -MeasureUnit MeasureUnit::getDotPerCentimeter() { - return MeasureUnit(12, 1); -} - -MeasureUnit *MeasureUnit::createDotPerInch(UErrorCode &status) { - return MeasureUnit::create(12, 2, status); -} - -MeasureUnit MeasureUnit::getDotPerInch() { - return MeasureUnit(12, 2); -} - -MeasureUnit *MeasureUnit::createEm(UErrorCode &status) { - return MeasureUnit::create(12, 3, status); -} - -MeasureUnit MeasureUnit::getEm() { - return MeasureUnit(12, 3); -} - -MeasureUnit *MeasureUnit::createMegapixel(UErrorCode &status) { - return MeasureUnit::create(12, 4, status); -} - -MeasureUnit MeasureUnit::getMegapixel() { - return MeasureUnit(12, 4); -} - -MeasureUnit *MeasureUnit::createPixel(UErrorCode &status) { - return MeasureUnit::create(12, 5, status); -} - -MeasureUnit MeasureUnit::getPixel() { - return MeasureUnit(12, 5); -} - -MeasureUnit *MeasureUnit::createPixelPerCentimeter(UErrorCode &status) { - return MeasureUnit::create(12, 6, status); -} - -MeasureUnit MeasureUnit::getPixelPerCentimeter() { - return MeasureUnit(12, 6); -} - -MeasureUnit *MeasureUnit::createPixelPerInch(UErrorCode &status) { - return MeasureUnit::create(12, 7, status); -} - -MeasureUnit MeasureUnit::getPixelPerInch() { - return MeasureUnit(12, 7); -} - -MeasureUnit *MeasureUnit::createAstronomicalUnit(UErrorCode &status) { - return MeasureUnit::create(13, 0, status); -} - -MeasureUnit MeasureUnit::getAstronomicalUnit() { - return MeasureUnit(13, 0); -} - -MeasureUnit *MeasureUnit::createCentimeter(UErrorCode &status) { - return MeasureUnit::create(13, 1, status); -} - -MeasureUnit MeasureUnit::getCentimeter() { - return MeasureUnit(13, 1); -} - -MeasureUnit *MeasureUnit::createDecimeter(UErrorCode &status) { - return MeasureUnit::create(13, 2, status); -} - -MeasureUnit MeasureUnit::getDecimeter() { - return MeasureUnit(13, 2); -} - -MeasureUnit *MeasureUnit::createEarthRadius(UErrorCode &status) { - return MeasureUnit::create(13, 3, status); -} - -MeasureUnit MeasureUnit::getEarthRadius() { - return MeasureUnit(13, 3); -} - -MeasureUnit *MeasureUnit::createFathom(UErrorCode &status) { - return MeasureUnit::create(13, 4, status); -} - -MeasureUnit MeasureUnit::getFathom() { - return MeasureUnit(13, 4); -} - -MeasureUnit *MeasureUnit::createFoot(UErrorCode &status) { - return MeasureUnit::create(13, 5, status); -} - -MeasureUnit MeasureUnit::getFoot() { - return MeasureUnit(13, 5); -} - -MeasureUnit *MeasureUnit::createFurlong(UErrorCode &status) { - return MeasureUnit::create(13, 6, status); -} - -MeasureUnit MeasureUnit::getFurlong() { - return MeasureUnit(13, 6); -} - -MeasureUnit *MeasureUnit::createInch(UErrorCode &status) { - return MeasureUnit::create(13, 7, status); -} - -MeasureUnit MeasureUnit::getInch() { - return MeasureUnit(13, 7); -} - -MeasureUnit *MeasureUnit::createKilometer(UErrorCode &status) { - return MeasureUnit::create(13, 8, status); -} - -MeasureUnit MeasureUnit::getKilometer() { - return MeasureUnit(13, 8); -} - -MeasureUnit *MeasureUnit::createLightYear(UErrorCode &status) { - return MeasureUnit::create(13, 9, status); -} - -MeasureUnit MeasureUnit::getLightYear() { - return MeasureUnit(13, 9); -} - -MeasureUnit *MeasureUnit::createMeter(UErrorCode &status) { - return MeasureUnit::create(13, 10, status); -} - -MeasureUnit MeasureUnit::getMeter() { - return MeasureUnit(13, 10); -} - -MeasureUnit *MeasureUnit::createMicrometer(UErrorCode &status) { - return MeasureUnit::create(13, 11, status); -} - -MeasureUnit MeasureUnit::getMicrometer() { - return MeasureUnit(13, 11); -} - -MeasureUnit *MeasureUnit::createMile(UErrorCode &status) { - return MeasureUnit::create(13, 12, status); -} - -MeasureUnit MeasureUnit::getMile() { - return MeasureUnit(13, 12); -} - -MeasureUnit *MeasureUnit::createMileScandinavian(UErrorCode &status) { - return MeasureUnit::create(13, 13, status); -} - -MeasureUnit MeasureUnit::getMileScandinavian() { - return MeasureUnit(13, 13); -} - -MeasureUnit *MeasureUnit::createMillimeter(UErrorCode &status) { - return MeasureUnit::create(13, 14, status); -} - -MeasureUnit MeasureUnit::getMillimeter() { - return MeasureUnit(13, 14); -} - -MeasureUnit *MeasureUnit::createNanometer(UErrorCode &status) { - return MeasureUnit::create(13, 15, status); -} - -MeasureUnit MeasureUnit::getNanometer() { - return MeasureUnit(13, 15); -} - -MeasureUnit *MeasureUnit::createNauticalMile(UErrorCode &status) { - return MeasureUnit::create(13, 16, status); -} - -MeasureUnit MeasureUnit::getNauticalMile() { - return MeasureUnit(13, 16); -} - -MeasureUnit *MeasureUnit::createParsec(UErrorCode &status) { - return MeasureUnit::create(13, 17, status); -} - -MeasureUnit MeasureUnit::getParsec() { - return MeasureUnit(13, 17); -} - -MeasureUnit *MeasureUnit::createPicometer(UErrorCode &status) { - return MeasureUnit::create(13, 18, status); -} - -MeasureUnit MeasureUnit::getPicometer() { - return MeasureUnit(13, 18); -} - -MeasureUnit *MeasureUnit::createPoint(UErrorCode &status) { - return MeasureUnit::create(13, 19, status); -} - -MeasureUnit MeasureUnit::getPoint() { - return MeasureUnit(13, 19); -} - -MeasureUnit *MeasureUnit::createSolarRadius(UErrorCode &status) { - return MeasureUnit::create(13, 20, status); -} - -MeasureUnit MeasureUnit::getSolarRadius() { - return MeasureUnit(13, 20); -} - -MeasureUnit *MeasureUnit::createYard(UErrorCode &status) { - return MeasureUnit::create(13, 21, status); -} - -MeasureUnit MeasureUnit::getYard() { - return MeasureUnit(13, 21); -} - -MeasureUnit *MeasureUnit::createCandela(UErrorCode &status) { - return MeasureUnit::create(14, 0, status); -} - -MeasureUnit MeasureUnit::getCandela() { - return MeasureUnit(14, 0); -} - -MeasureUnit *MeasureUnit::createLumen(UErrorCode &status) { - return MeasureUnit::create(14, 1, status); -} - -MeasureUnit MeasureUnit::getLumen() { - return MeasureUnit(14, 1); -} - -MeasureUnit *MeasureUnit::createLux(UErrorCode &status) { - return MeasureUnit::create(14, 2, status); -} - -MeasureUnit MeasureUnit::getLux() { - return MeasureUnit(14, 2); -} - -MeasureUnit *MeasureUnit::createSolarLuminosity(UErrorCode &status) { - return MeasureUnit::create(14, 3, status); -} - -MeasureUnit MeasureUnit::getSolarLuminosity() { - return MeasureUnit(14, 3); -} - -MeasureUnit *MeasureUnit::createCarat(UErrorCode &status) { - return MeasureUnit::create(15, 0, status); -} - -MeasureUnit MeasureUnit::getCarat() { - return MeasureUnit(15, 0); -} - -MeasureUnit *MeasureUnit::createDalton(UErrorCode &status) { - return MeasureUnit::create(15, 1, status); -} - -MeasureUnit MeasureUnit::getDalton() { - return MeasureUnit(15, 1); -} - -MeasureUnit *MeasureUnit::createEarthMass(UErrorCode &status) { - return MeasureUnit::create(15, 2, status); -} - -MeasureUnit MeasureUnit::getEarthMass() { - return MeasureUnit(15, 2); -} - -MeasureUnit *MeasureUnit::createGrain(UErrorCode &status) { - return MeasureUnit::create(15, 3, status); -} - -MeasureUnit MeasureUnit::getGrain() { - return MeasureUnit(15, 3); -} - -MeasureUnit *MeasureUnit::createGram(UErrorCode &status) { - return MeasureUnit::create(15, 4, status); -} - -MeasureUnit MeasureUnit::getGram() { - return MeasureUnit(15, 4); -} - -MeasureUnit *MeasureUnit::createKilogram(UErrorCode &status) { - return MeasureUnit::create(15, 5, status); -} - -MeasureUnit MeasureUnit::getKilogram() { - return MeasureUnit(15, 5); -} - -MeasureUnit *MeasureUnit::createMetricTon(UErrorCode &status) { - return MeasureUnit::create(15, 14, status); -} - -MeasureUnit MeasureUnit::getMetricTon() { - return MeasureUnit(15, 14); -} - -MeasureUnit *MeasureUnit::createMicrogram(UErrorCode &status) { - return MeasureUnit::create(15, 6, status); -} - -MeasureUnit MeasureUnit::getMicrogram() { - return MeasureUnit(15, 6); -} - -MeasureUnit *MeasureUnit::createMilligram(UErrorCode &status) { - return MeasureUnit::create(15, 7, status); -} - -MeasureUnit MeasureUnit::getMilligram() { - return MeasureUnit(15, 7); -} - -MeasureUnit *MeasureUnit::createOunce(UErrorCode &status) { - return MeasureUnit::create(15, 8, status); -} - -MeasureUnit MeasureUnit::getOunce() { - return MeasureUnit(15, 8); -} - -MeasureUnit *MeasureUnit::createOunceTroy(UErrorCode &status) { - return MeasureUnit::create(15, 9, status); -} - -MeasureUnit MeasureUnit::getOunceTroy() { - return MeasureUnit(15, 9); -} - -MeasureUnit *MeasureUnit::createPound(UErrorCode &status) { - return MeasureUnit::create(15, 10, status); -} - -MeasureUnit MeasureUnit::getPound() { - return MeasureUnit(15, 10); -} - -MeasureUnit *MeasureUnit::createSolarMass(UErrorCode &status) { - return MeasureUnit::create(15, 11, status); -} - -MeasureUnit MeasureUnit::getSolarMass() { - return MeasureUnit(15, 11); -} - -MeasureUnit *MeasureUnit::createStone(UErrorCode &status) { - return MeasureUnit::create(15, 12, status); -} - -MeasureUnit MeasureUnit::getStone() { - return MeasureUnit(15, 12); -} - -MeasureUnit *MeasureUnit::createTon(UErrorCode &status) { - return MeasureUnit::create(15, 13, status); -} - -MeasureUnit MeasureUnit::getTon() { - return MeasureUnit(15, 13); -} - -MeasureUnit *MeasureUnit::createTonne(UErrorCode &status) { - return MeasureUnit::create(15, 14, status); -} - -MeasureUnit MeasureUnit::getTonne() { - return MeasureUnit(15, 14); -} - -MeasureUnit *MeasureUnit::createGigawatt(UErrorCode &status) { - return MeasureUnit::create(17, 0, status); -} - -MeasureUnit MeasureUnit::getGigawatt() { - return MeasureUnit(17, 0); -} - -MeasureUnit *MeasureUnit::createHorsepower(UErrorCode &status) { - return MeasureUnit::create(17, 1, status); -} - -MeasureUnit MeasureUnit::getHorsepower() { - return MeasureUnit(17, 1); -} - -MeasureUnit *MeasureUnit::createKilowatt(UErrorCode &status) { - return MeasureUnit::create(17, 2, status); -} - -MeasureUnit MeasureUnit::getKilowatt() { - return MeasureUnit(17, 2); -} - -MeasureUnit *MeasureUnit::createMegawatt(UErrorCode &status) { - return MeasureUnit::create(17, 3, status); -} - -MeasureUnit MeasureUnit::getMegawatt() { - return MeasureUnit(17, 3); -} - -MeasureUnit *MeasureUnit::createMilliwatt(UErrorCode &status) { - return MeasureUnit::create(17, 4, status); -} - -MeasureUnit MeasureUnit::getMilliwatt() { - return MeasureUnit(17, 4); -} - -MeasureUnit *MeasureUnit::createWatt(UErrorCode &status) { - return MeasureUnit::create(17, 5, status); -} - -MeasureUnit MeasureUnit::getWatt() { - return MeasureUnit(17, 5); -} - -MeasureUnit *MeasureUnit::createAtmosphere(UErrorCode &status) { - return MeasureUnit::create(18, 0, status); -} - -MeasureUnit MeasureUnit::getAtmosphere() { - return MeasureUnit(18, 0); -} - -MeasureUnit *MeasureUnit::createBar(UErrorCode &status) { - return MeasureUnit::create(18, 1, status); -} - -MeasureUnit MeasureUnit::getBar() { - return MeasureUnit(18, 1); -} - -MeasureUnit *MeasureUnit::createHectopascal(UErrorCode &status) { - return MeasureUnit::create(18, 2, status); -} - -MeasureUnit MeasureUnit::getHectopascal() { - return MeasureUnit(18, 2); -} - -MeasureUnit *MeasureUnit::createInchHg(UErrorCode &status) { - return MeasureUnit::create(18, 3, status); -} - -MeasureUnit MeasureUnit::getInchHg() { - return MeasureUnit(18, 3); -} - -MeasureUnit *MeasureUnit::createKilopascal(UErrorCode &status) { - return MeasureUnit::create(18, 4, status); -} - -MeasureUnit MeasureUnit::getKilopascal() { - return MeasureUnit(18, 4); -} - -MeasureUnit *MeasureUnit::createMegapascal(UErrorCode &status) { - return MeasureUnit::create(18, 5, status); -} - -MeasureUnit MeasureUnit::getMegapascal() { - return MeasureUnit(18, 5); -} - -MeasureUnit *MeasureUnit::createMillibar(UErrorCode &status) { - return MeasureUnit::create(18, 6, status); -} - -MeasureUnit MeasureUnit::getMillibar() { - return MeasureUnit(18, 6); -} - -MeasureUnit *MeasureUnit::createMillimeterOfMercury(UErrorCode &status) { - return MeasureUnit::create(18, 7, status); -} - -MeasureUnit MeasureUnit::getMillimeterOfMercury() { - return MeasureUnit(18, 7); -} - -MeasureUnit *MeasureUnit::createPascal(UErrorCode &status) { - return MeasureUnit::create(18, 8, status); -} - -MeasureUnit MeasureUnit::getPascal() { - return MeasureUnit(18, 8); -} - -MeasureUnit *MeasureUnit::createPoundPerSquareInch(UErrorCode &status) { - return MeasureUnit::create(18, 9, status); -} - -MeasureUnit MeasureUnit::getPoundPerSquareInch() { - return MeasureUnit(18, 9); -} - -MeasureUnit *MeasureUnit::createKilometerPerHour(UErrorCode &status) { - return MeasureUnit::create(19, 0, status); -} - -MeasureUnit MeasureUnit::getKilometerPerHour() { - return MeasureUnit(19, 0); -} - -MeasureUnit *MeasureUnit::createKnot(UErrorCode &status) { - return MeasureUnit::create(19, 1, status); -} - -MeasureUnit MeasureUnit::getKnot() { - return MeasureUnit(19, 1); -} - -MeasureUnit *MeasureUnit::createMeterPerSecond(UErrorCode &status) { - return MeasureUnit::create(19, 2, status); -} - -MeasureUnit MeasureUnit::getMeterPerSecond() { - return MeasureUnit(19, 2); -} - -MeasureUnit *MeasureUnit::createMilePerHour(UErrorCode &status) { - return MeasureUnit::create(19, 3, status); -} - -MeasureUnit MeasureUnit::getMilePerHour() { - return MeasureUnit(19, 3); -} - -MeasureUnit *MeasureUnit::createCelsius(UErrorCode &status) { - return MeasureUnit::create(20, 0, status); -} - -MeasureUnit MeasureUnit::getCelsius() { - return MeasureUnit(20, 0); -} - -MeasureUnit *MeasureUnit::createFahrenheit(UErrorCode &status) { - return MeasureUnit::create(20, 1, status); -} - -MeasureUnit MeasureUnit::getFahrenheit() { - return MeasureUnit(20, 1); -} - -MeasureUnit *MeasureUnit::createGenericTemperature(UErrorCode &status) { - return MeasureUnit::create(20, 2, status); -} - -MeasureUnit MeasureUnit::getGenericTemperature() { - return MeasureUnit(20, 2); -} - -MeasureUnit *MeasureUnit::createKelvin(UErrorCode &status) { - return MeasureUnit::create(20, 3, status); -} - -MeasureUnit MeasureUnit::getKelvin() { - return MeasureUnit(20, 3); -} - -MeasureUnit *MeasureUnit::createNewtonMeter(UErrorCode &status) { - return MeasureUnit::create(21, 0, status); -} - -MeasureUnit MeasureUnit::getNewtonMeter() { - return MeasureUnit(21, 0); -} - -MeasureUnit *MeasureUnit::createPoundFoot(UErrorCode &status) { - return MeasureUnit::create(21, 1, status); -} - -MeasureUnit MeasureUnit::getPoundFoot() { - return MeasureUnit(21, 1); -} - -MeasureUnit *MeasureUnit::createAcreFoot(UErrorCode &status) { - return MeasureUnit::create(22, 0, status); -} - -MeasureUnit MeasureUnit::getAcreFoot() { - return MeasureUnit(22, 0); -} - -MeasureUnit *MeasureUnit::createBarrel(UErrorCode &status) { - return MeasureUnit::create(22, 1, status); -} - -MeasureUnit MeasureUnit::getBarrel() { - return MeasureUnit(22, 1); -} - -MeasureUnit *MeasureUnit::createBushel(UErrorCode &status) { - return MeasureUnit::create(22, 2, status); -} - -MeasureUnit MeasureUnit::getBushel() { - return MeasureUnit(22, 2); -} - -MeasureUnit *MeasureUnit::createCentiliter(UErrorCode &status) { - return MeasureUnit::create(22, 3, status); -} - -MeasureUnit MeasureUnit::getCentiliter() { - return MeasureUnit(22, 3); -} - -MeasureUnit *MeasureUnit::createCubicCentimeter(UErrorCode &status) { - return MeasureUnit::create(22, 4, status); -} - -MeasureUnit MeasureUnit::getCubicCentimeter() { - return MeasureUnit(22, 4); -} - -MeasureUnit *MeasureUnit::createCubicFoot(UErrorCode &status) { - return MeasureUnit::create(22, 5, status); -} - -MeasureUnit MeasureUnit::getCubicFoot() { - return MeasureUnit(22, 5); -} - -MeasureUnit *MeasureUnit::createCubicInch(UErrorCode &status) { - return MeasureUnit::create(22, 6, status); -} - -MeasureUnit MeasureUnit::getCubicInch() { - return MeasureUnit(22, 6); -} - -MeasureUnit *MeasureUnit::createCubicKilometer(UErrorCode &status) { - return MeasureUnit::create(22, 7, status); -} - -MeasureUnit MeasureUnit::getCubicKilometer() { - return MeasureUnit(22, 7); -} - -MeasureUnit *MeasureUnit::createCubicMeter(UErrorCode &status) { - return MeasureUnit::create(22, 8, status); -} - -MeasureUnit MeasureUnit::getCubicMeter() { - return MeasureUnit(22, 8); -} - -MeasureUnit *MeasureUnit::createCubicMile(UErrorCode &status) { - return MeasureUnit::create(22, 9, status); -} - -MeasureUnit MeasureUnit::getCubicMile() { - return MeasureUnit(22, 9); -} - -MeasureUnit *MeasureUnit::createCubicYard(UErrorCode &status) { - return MeasureUnit::create(22, 10, status); -} - -MeasureUnit MeasureUnit::getCubicYard() { - return MeasureUnit(22, 10); -} - -MeasureUnit *MeasureUnit::createCup(UErrorCode &status) { - return MeasureUnit::create(22, 11, status); -} - -MeasureUnit MeasureUnit::getCup() { - return MeasureUnit(22, 11); -} - -MeasureUnit *MeasureUnit::createCupMetric(UErrorCode &status) { - return MeasureUnit::create(22, 12, status); -} - -MeasureUnit MeasureUnit::getCupMetric() { - return MeasureUnit(22, 12); -} - -MeasureUnit *MeasureUnit::createDeciliter(UErrorCode &status) { - return MeasureUnit::create(22, 13, status); -} - -MeasureUnit MeasureUnit::getDeciliter() { - return MeasureUnit(22, 13); -} - -MeasureUnit *MeasureUnit::createDessertSpoon(UErrorCode &status) { - return MeasureUnit::create(22, 14, status); -} - -MeasureUnit MeasureUnit::getDessertSpoon() { - return MeasureUnit(22, 14); -} - -MeasureUnit *MeasureUnit::createDessertSpoonImperial(UErrorCode &status) { - return MeasureUnit::create(22, 15, status); -} - -MeasureUnit MeasureUnit::getDessertSpoonImperial() { - return MeasureUnit(22, 15); -} - -MeasureUnit *MeasureUnit::createDram(UErrorCode &status) { - return MeasureUnit::create(22, 16, status); -} - -MeasureUnit MeasureUnit::getDram() { - return MeasureUnit(22, 16); -} - -MeasureUnit *MeasureUnit::createDrop(UErrorCode &status) { - return MeasureUnit::create(22, 17, status); -} - -MeasureUnit MeasureUnit::getDrop() { - return MeasureUnit(22, 17); -} - -MeasureUnit *MeasureUnit::createFluidOunce(UErrorCode &status) { - return MeasureUnit::create(22, 18, status); -} - -MeasureUnit MeasureUnit::getFluidOunce() { - return MeasureUnit(22, 18); -} - -MeasureUnit *MeasureUnit::createFluidOunceImperial(UErrorCode &status) { - return MeasureUnit::create(22, 19, status); -} - -MeasureUnit MeasureUnit::getFluidOunceImperial() { - return MeasureUnit(22, 19); -} - -MeasureUnit *MeasureUnit::createGallon(UErrorCode &status) { - return MeasureUnit::create(22, 20, status); -} - -MeasureUnit MeasureUnit::getGallon() { - return MeasureUnit(22, 20); -} - -MeasureUnit *MeasureUnit::createGallonImperial(UErrorCode &status) { - return MeasureUnit::create(22, 21, status); -} - -MeasureUnit MeasureUnit::getGallonImperial() { - return MeasureUnit(22, 21); -} - -MeasureUnit *MeasureUnit::createHectoliter(UErrorCode &status) { - return MeasureUnit::create(22, 22, status); -} - -MeasureUnit MeasureUnit::getHectoliter() { - return MeasureUnit(22, 22); -} - -MeasureUnit *MeasureUnit::createJigger(UErrorCode &status) { - return MeasureUnit::create(22, 23, status); -} - -MeasureUnit MeasureUnit::getJigger() { - return MeasureUnit(22, 23); -} - -MeasureUnit *MeasureUnit::createLiter(UErrorCode &status) { - return MeasureUnit::create(22, 24, status); -} - -MeasureUnit MeasureUnit::getLiter() { - return MeasureUnit(22, 24); -} - -MeasureUnit *MeasureUnit::createMegaliter(UErrorCode &status) { - return MeasureUnit::create(22, 25, status); -} - -MeasureUnit MeasureUnit::getMegaliter() { - return MeasureUnit(22, 25); -} - -MeasureUnit *MeasureUnit::createMilliliter(UErrorCode &status) { - return MeasureUnit::create(22, 26, status); -} - -MeasureUnit MeasureUnit::getMilliliter() { - return MeasureUnit(22, 26); -} - -MeasureUnit *MeasureUnit::createPinch(UErrorCode &status) { - return MeasureUnit::create(22, 27, status); -} - -MeasureUnit MeasureUnit::getPinch() { - return MeasureUnit(22, 27); -} - -MeasureUnit *MeasureUnit::createPint(UErrorCode &status) { - return MeasureUnit::create(22, 28, status); -} - -MeasureUnit MeasureUnit::getPint() { - return MeasureUnit(22, 28); -} - -MeasureUnit *MeasureUnit::createPintMetric(UErrorCode &status) { - return MeasureUnit::create(22, 29, status); -} - -MeasureUnit MeasureUnit::getPintMetric() { - return MeasureUnit(22, 29); -} - -MeasureUnit *MeasureUnit::createQuart(UErrorCode &status) { - return MeasureUnit::create(22, 30, status); -} - -MeasureUnit MeasureUnit::getQuart() { - return MeasureUnit(22, 30); -} - -MeasureUnit *MeasureUnit::createQuartImperial(UErrorCode &status) { - return MeasureUnit::create(22, 31, status); -} - -MeasureUnit MeasureUnit::getQuartImperial() { - return MeasureUnit(22, 31); -} - -MeasureUnit *MeasureUnit::createTablespoon(UErrorCode &status) { - return MeasureUnit::create(22, 32, status); -} - -MeasureUnit MeasureUnit::getTablespoon() { - return MeasureUnit(22, 32); -} - -MeasureUnit *MeasureUnit::createTeaspoon(UErrorCode &status) { - return MeasureUnit::create(22, 33, status); -} - -MeasureUnit MeasureUnit::getTeaspoon() { - return MeasureUnit(22, 33); -} - -// End generated code for measunit.cpp - -static int32_t binarySearch( - const char * const * array, int32_t start, int32_t end, StringPiece key) { - while (start < end) { - int32_t mid = (start + end) / 2; - int32_t cmp = StringPiece(array[mid]).compare(key); - if (cmp < 0) { - start = mid + 1; - continue; - } - if (cmp == 0) { - return mid; - } - end = mid; - } - return -1; -} - -MeasureUnit::MeasureUnit() : MeasureUnit(kBaseTypeIdx, kBaseSubTypeIdx) { -} - -MeasureUnit::MeasureUnit(int32_t typeId, int32_t subTypeId) - : fImpl(nullptr), fSubTypeId(subTypeId), fTypeId(typeId) { -} - -MeasureUnit::MeasureUnit(const MeasureUnit &other) - : fImpl(nullptr) { - *this = other; -} - -MeasureUnit::MeasureUnit(MeasureUnit &&other) noexcept - : fImpl(other.fImpl), - fSubTypeId(other.fSubTypeId), - fTypeId(other.fTypeId) { - other.fImpl = nullptr; -} - -MeasureUnit::MeasureUnit(MeasureUnitImpl&& impl) - : fImpl(nullptr), fSubTypeId(-1), fTypeId(-1) { - if (!findBySubType(impl.identifier.toStringPiece(), this)) { - fImpl = new MeasureUnitImpl(std::move(impl)); - } -} - -MeasureUnit &MeasureUnit::operator=(const MeasureUnit &other) { - if (this == &other) { - return *this; - } - if (fImpl != nullptr) { - delete fImpl; - } - if (other.fImpl) { - ErrorCode localStatus; - fImpl = new MeasureUnitImpl(other.fImpl->copy(localStatus)); - if (!fImpl || localStatus.isFailure()) { - // Unrecoverable allocation error; set to the default unit - *this = MeasureUnit(); - return *this; - } - } else { - fImpl = nullptr; - } - fTypeId = other.fTypeId; - fSubTypeId = other.fSubTypeId; - return *this; -} - -MeasureUnit &MeasureUnit::operator=(MeasureUnit &&other) noexcept { - if (this == &other) { - return *this; - } - if (fImpl != nullptr) { - delete fImpl; - } - fImpl = other.fImpl; - other.fImpl = nullptr; - fTypeId = other.fTypeId; - fSubTypeId = other.fSubTypeId; - return *this; -} - -MeasureUnit *MeasureUnit::clone() const { - return new MeasureUnit(*this); -} - -MeasureUnit::~MeasureUnit() { - if (fImpl != nullptr) { - delete fImpl; - fImpl = nullptr; - } -} - -const char *MeasureUnit::getType() const { - // We have a type & subtype only if fTypeId is present. - if (fTypeId == -1) { - return ""; - } - return gTypes[fTypeId]; -} - -const char *MeasureUnit::getSubtype() const { - // We have a type & subtype only if fTypeId is present. - if (fTypeId == -1) { - return ""; - } - return getIdentifier(); -} - -const char *MeasureUnit::getIdentifier() const { - return fImpl ? fImpl->identifier.data() : gSubTypes[getOffset()]; -} - -bool MeasureUnit::operator==(const UObject& other) const { - if (this == &other) { // Same object, equal - return true; - } - if (typeid(*this) != typeid(other)) { // Different types, not equal - return false; - } - const MeasureUnit &rhs = static_cast(other); - return uprv_strcmp(getIdentifier(), rhs.getIdentifier()) == 0; -} - -int32_t MeasureUnit::getAvailable( - MeasureUnit *dest, - int32_t destCapacity, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { - return 0; - } - if (destCapacity < UPRV_LENGTHOF(gSubTypes)) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return UPRV_LENGTHOF(gSubTypes); - } - int32_t idx = 0; - for (int32_t typeIdx = 0; typeIdx < UPRV_LENGTHOF(gTypes); ++typeIdx) { - int32_t len = gOffsets[typeIdx + 1] - gOffsets[typeIdx]; - for (int32_t subTypeIdx = 0; subTypeIdx < len; ++subTypeIdx) { - dest[idx].setTo(typeIdx, subTypeIdx); - ++idx; - } - } - U_ASSERT(idx == UPRV_LENGTHOF(gSubTypes)); - return UPRV_LENGTHOF(gSubTypes); -} - -int32_t MeasureUnit::getAvailable( - const char *type, - MeasureUnit *dest, - int32_t destCapacity, - UErrorCode &errorCode) { - if (U_FAILURE(errorCode)) { - return 0; - } - int32_t typeIdx = binarySearch(gTypes, 0, UPRV_LENGTHOF(gTypes), type); - if (typeIdx == -1) { - return 0; - } - int32_t len = gOffsets[typeIdx + 1] - gOffsets[typeIdx]; - if (destCapacity < len) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return len; - } - for (int subTypeIdx = 0; subTypeIdx < len; ++subTypeIdx) { - dest[subTypeIdx].setTo(typeIdx, subTypeIdx); - } - return len; -} - -StringEnumeration* MeasureUnit::getAvailableTypes(UErrorCode &errorCode) { - UEnumeration *uenum = uenum_openCharStringsEnumeration( - gTypes, UPRV_LENGTHOF(gTypes), &errorCode); - if (U_FAILURE(errorCode)) { - uenum_close(uenum); - return NULL; - } - StringEnumeration *result = new UStringEnumeration(uenum); - if (result == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - uenum_close(uenum); - return NULL; - } - return result; -} - -bool MeasureUnit::findBySubType(StringPiece subType, MeasureUnit* output) { - // Sanity checking kCurrencyOffset and final entry in gOffsets - U_ASSERT(uprv_strcmp(gTypes[kCurrencyOffset], "currency") == 0); - U_ASSERT(gOffsets[UPRV_LENGTHOF(gOffsets) - 1] == UPRV_LENGTHOF(gSubTypes)); - - for (int32_t t = 0; t < UPRV_LENGTHOF(gOffsets) - 1; t++) { - // Skip currency units - if (t == kCurrencyOffset) { - continue; - } - int32_t st = binarySearch(gSubTypes, gOffsets[t], gOffsets[t + 1], subType); - if (st >= 0) { - output->setTo(t, st - gOffsets[t]); - return true; - } - } - return false; -} - -MeasureUnit *MeasureUnit::create(int typeId, int subTypeId, UErrorCode &status) { - if (U_FAILURE(status)) { - return NULL; - } - MeasureUnit *result = new MeasureUnit(typeId, subTypeId); - if (result == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return result; -} - -void MeasureUnit::initTime(const char *timeId) { - int32_t result = binarySearch(gTypes, 0, UPRV_LENGTHOF(gTypes), "duration"); - U_ASSERT(result != -1); - fTypeId = result; - result = binarySearch(gSubTypes, gOffsets[fTypeId], gOffsets[fTypeId + 1], timeId); - U_ASSERT(result != -1); - fSubTypeId = result - gOffsets[fTypeId]; -} - -void MeasureUnit::initCurrency(StringPiece isoCurrency) { - int32_t result = binarySearch(gTypes, 0, UPRV_LENGTHOF(gTypes), "currency"); - U_ASSERT(result != -1); - fTypeId = result; - result = binarySearch( - gSubTypes, gOffsets[fTypeId], gOffsets[fTypeId + 1], isoCurrency); - if (result == -1) { - fImpl = new MeasureUnitImpl(MeasureUnitImpl::forCurrencyCode(isoCurrency)); - if (fImpl) { - fSubTypeId = -1; - return; - } - // malloc error: fall back to the undefined currency - result = binarySearch( - gSubTypes, gOffsets[fTypeId], gOffsets[fTypeId + 1], kDefaultCurrency8); - U_ASSERT(result != -1); - } - fSubTypeId = result - gOffsets[fTypeId]; -} - -void MeasureUnit::setTo(int32_t typeId, int32_t subTypeId) { - fTypeId = typeId; - fSubTypeId = subTypeId; - if (fImpl != nullptr) { - delete fImpl; - fImpl = nullptr; - } -} - -int32_t MeasureUnit::getOffset() const { - if (fTypeId < 0 || fSubTypeId < 0) { - return -1; - } - return gOffsets[fTypeId] + fSubTypeId; -} - -MeasureUnitImpl MeasureUnitImpl::copy(UErrorCode &status) const { - MeasureUnitImpl result; - result.complexity = complexity; - result.identifier.append(identifier, status); - for (int32_t i = 0; i < singleUnits.length(); i++) { - SingleUnitImpl *item = result.singleUnits.emplaceBack(*singleUnits[i]); - if (!item) { - status = U_MEMORY_ALLOCATION_ERROR; - return result; - } - } - return result; -} - -U_NAMESPACE_END - -#endif /* !UNCONFIG_NO_FORMATTING */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2004-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: April 26, 2004 +* Since: ICU 3.0 +********************************************************************** +*/ +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/measunit.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/uenum.h" +#include "unicode/errorcode.h" +#include "ustrenum.h" +#include "cstring.h" +#include "uassert.h" +#include "measunit_impl.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureUnit) + +// All code between the "Start generated code" comment and +// the "End generated code" comment is auto generated code +// and must not be edited manually. For instructions on how to correctly +// update this code, refer to: +// https://icu.unicode.org/design/formatting/measureformat/updating-measure-unit +// +// Start generated code for measunit.cpp + +// Maps from Type ID to offset in gSubTypes. +static const int32_t gOffsets[] = { + 0, + 2, + 7, + 17, + 27, + 31, + 332, + 343, + 360, + 364, + 373, + 376, + 380, + 388, + 410, + 414, + 429, + 430, + 436, + 446, + 451, + 455, + 457, + 491 +}; + +static const int32_t kCurrencyOffset = 5; + +// Must be sorted alphabetically. +static const char * const gTypes[] = { + "acceleration", + "angle", + "area", + "concentr", + "consumption", + "currency", + "digital", + "duration", + "electric", + "energy", + "force", + "frequency", + "graphics", + "length", + "light", + "mass", + "none", + "power", + "pressure", + "speed", + "temperature", + "torque", + "volume" +}; + +// Must be grouped by type and sorted alphabetically within each type. +static const char * const gSubTypes[] = { + "g-force", + "meter-per-square-second", + "arc-minute", + "arc-second", + "degree", + "radian", + "revolution", + "acre", + "dunam", + "hectare", + "square-centimeter", + "square-foot", + "square-inch", + "square-kilometer", + "square-meter", + "square-mile", + "square-yard", + "item", + "karat", + "milligram-ofglucose-per-deciliter", + "milligram-per-deciliter", + "millimole-per-liter", + "mole", + "percent", + "permille", + "permillion", + "permyriad", + "liter-per-100-kilometer", + "liter-per-kilometer", + "mile-per-gallon", + "mile-per-gallon-imperial", + "ADP", + "AED", + "AFA", + "AFN", + "ALK", + "ALL", + "AMD", + "ANG", + "AOA", + "AOK", + "AON", + "AOR", + "ARA", + "ARP", + "ARS", + "ARY", + "ATS", + "AUD", + "AWG", + "AYM", + "AZM", + "AZN", + "BAD", + "BAM", + "BBD", + "BDT", + "BEC", + "BEF", + "BEL", + "BGJ", + "BGK", + "BGL", + "BGN", + "BHD", + "BIF", + "BMD", + "BND", + "BOB", + "BOP", + "BOV", + "BRB", + "BRC", + "BRE", + "BRL", + "BRN", + "BRR", + "BSD", + "BTN", + "BUK", + "BWP", + "BYB", + "BYN", + "BYR", + "BZD", + "CAD", + "CDF", + "CHC", + "CHE", + "CHF", + "CHW", + "CLF", + "CLP", + "CNY", + "COP", + "COU", + "CRC", + "CSD", + "CSJ", + "CSK", + "CUC", + "CUP", + "CVE", + "CYP", + "CZK", + "DDM", + "DEM", + "DJF", + "DKK", + "DOP", + "DZD", + "ECS", + "ECV", + "EEK", + "EGP", + "ERN", + "ESA", + "ESB", + "ESP", + "ETB", + "EUR", + "FIM", + "FJD", + "FKP", + "FRF", + "GBP", + "GEK", + "GEL", + "GHC", + "GHP", + "GHS", + "GIP", + "GMD", + "GNE", + "GNF", + "GNS", + "GQE", + "GRD", + "GTQ", + "GWE", + "GWP", + "GYD", + "HKD", + "HNL", + "HRD", + "HRK", + "HTG", + "HUF", + "IDR", + "IEP", + "ILP", + "ILR", + "ILS", + "INR", + "IQD", + "IRR", + "ISJ", + "ISK", + "ITL", + "JMD", + "JOD", + "JPY", + "KES", + "KGS", + "KHR", + "KMF", + "KPW", + "KRW", + "KWD", + "KYD", + "KZT", + "LAJ", + "LAK", + "LBP", + "LKR", + "LRD", + "LSL", + "LSM", + "LTL", + "LTT", + "LUC", + "LUF", + "LUL", + "LVL", + "LVR", + "LYD", + "MAD", + "MDL", + "MGA", + "MGF", + "MKD", + "MLF", + "MMK", + "MNT", + "MOP", + "MRO", + "MRU", + "MTL", + "MTP", + "MUR", + "MVQ", + "MVR", + "MWK", + "MXN", + "MXP", + "MXV", + "MYR", + "MZE", + "MZM", + "MZN", + "NAD", + "NGN", + "NIC", + "NIO", + "NLG", + "NOK", + "NPR", + "NZD", + "OMR", + "PAB", + "PEH", + "PEI", + "PEN", + "PES", + "PGK", + "PHP", + "PKR", + "PLN", + "PLZ", + "PTE", + "PYG", + "QAR", + "RHD", + "ROK", + "ROL", + "RON", + "RSD", + "RUB", + "RUR", + "RWF", + "SAR", + "SBD", + "SCR", + "SDD", + "SDG", + "SDP", + "SEK", + "SGD", + "SHP", + "SIT", + "SKK", + "SLE", + "SLL", + "SOS", + "SRD", + "SRG", + "SSP", + "STD", + "STN", + "SUR", + "SVC", + "SYP", + "SZL", + "THB", + "TJR", + "TJS", + "TMM", + "TMT", + "TND", + "TOP", + "TPE", + "TRL", + "TRY", + "TTD", + "TWD", + "TZS", + "UAH", + "UAK", + "UGS", + "UGW", + "UGX", + "USD", + "USN", + "USS", + "UYI", + "UYN", + "UYP", + "UYU", + "UYW", + "UZS", + "VEB", + "VED", + "VEF", + "VES", + "VNC", + "VND", + "VUV", + "WST", + "XAF", + "XAG", + "XAU", + "XBA", + "XBB", + "XBC", + "XBD", + "XCD", + "XDR", + "XEU", + "XOF", + "XPD", + "XPF", + "XPT", + "XSU", + "XTS", + "XUA", + "XXX", + "YDD", + "YER", + "YUD", + "YUM", + "YUN", + "ZAL", + "ZAR", + "ZMK", + "ZMW", + "ZRN", + "ZRZ", + "ZWC", + "ZWD", + "ZWL", + "ZWN", + "ZWR", + "bit", + "byte", + "gigabit", + "gigabyte", + "kilobit", + "kilobyte", + "megabit", + "megabyte", + "petabyte", + "terabit", + "terabyte", + "century", + "day", + "day-person", + "decade", + "hour", + "microsecond", + "millisecond", + "minute", + "month", + "month-person", + "nanosecond", + "quarter", + "second", + "week", + "week-person", + "year", + "year-person", + "ampere", + "milliampere", + "ohm", + "volt", + "british-thermal-unit", + "calorie", + "electronvolt", + "foodcalorie", + "joule", + "kilocalorie", + "kilojoule", + "kilowatt-hour", + "therm-us", + "kilowatt-hour-per-100-kilometer", + "newton", + "pound-force", + "gigahertz", + "hertz", + "kilohertz", + "megahertz", + "dot", + "dot-per-centimeter", + "dot-per-inch", + "em", + "megapixel", + "pixel", + "pixel-per-centimeter", + "pixel-per-inch", + "astronomical-unit", + "centimeter", + "decimeter", + "earth-radius", + "fathom", + "foot", + "furlong", + "inch", + "kilometer", + "light-year", + "meter", + "micrometer", + "mile", + "mile-scandinavian", + "millimeter", + "nanometer", + "nautical-mile", + "parsec", + "picometer", + "point", + "solar-radius", + "yard", + "candela", + "lumen", + "lux", + "solar-luminosity", + "carat", + "dalton", + "earth-mass", + "grain", + "gram", + "kilogram", + "microgram", + "milligram", + "ounce", + "ounce-troy", + "pound", + "solar-mass", + "stone", + "ton", + "tonne", + "", + "gigawatt", + "horsepower", + "kilowatt", + "megawatt", + "milliwatt", + "watt", + "atmosphere", + "bar", + "hectopascal", + "inch-ofhg", + "kilopascal", + "megapascal", + "millibar", + "millimeter-ofhg", + "pascal", + "pound-force-per-square-inch", + "beaufort", + "kilometer-per-hour", + "knot", + "meter-per-second", + "mile-per-hour", + "celsius", + "fahrenheit", + "generic", + "kelvin", + "newton-meter", + "pound-force-foot", + "acre-foot", + "barrel", + "bushel", + "centiliter", + "cubic-centimeter", + "cubic-foot", + "cubic-inch", + "cubic-kilometer", + "cubic-meter", + "cubic-mile", + "cubic-yard", + "cup", + "cup-metric", + "deciliter", + "dessert-spoon", + "dessert-spoon-imperial", + "dram", + "drop", + "fluid-ounce", + "fluid-ounce-imperial", + "gallon", + "gallon-imperial", + "hectoliter", + "jigger", + "liter", + "megaliter", + "milliliter", + "pinch", + "pint", + "pint-metric", + "quart", + "quart-imperial", + "tablespoon", + "teaspoon" +}; + +// Shortcuts to the base unit in order to make the default constructor fast +static const int32_t kBaseTypeIdx = 16; +static const int32_t kBaseSubTypeIdx = 0; + +MeasureUnit *MeasureUnit::createGForce(UErrorCode &status) { + return MeasureUnit::create(0, 0, status); +} + +MeasureUnit MeasureUnit::getGForce() { + return MeasureUnit(0, 0); +} + +MeasureUnit *MeasureUnit::createMeterPerSecondSquared(UErrorCode &status) { + return MeasureUnit::create(0, 1, status); +} + +MeasureUnit MeasureUnit::getMeterPerSecondSquared() { + return MeasureUnit(0, 1); +} + +MeasureUnit *MeasureUnit::createArcMinute(UErrorCode &status) { + return MeasureUnit::create(1, 0, status); +} + +MeasureUnit MeasureUnit::getArcMinute() { + return MeasureUnit(1, 0); +} + +MeasureUnit *MeasureUnit::createArcSecond(UErrorCode &status) { + return MeasureUnit::create(1, 1, status); +} + +MeasureUnit MeasureUnit::getArcSecond() { + return MeasureUnit(1, 1); +} + +MeasureUnit *MeasureUnit::createDegree(UErrorCode &status) { + return MeasureUnit::create(1, 2, status); +} + +MeasureUnit MeasureUnit::getDegree() { + return MeasureUnit(1, 2); +} + +MeasureUnit *MeasureUnit::createRadian(UErrorCode &status) { + return MeasureUnit::create(1, 3, status); +} + +MeasureUnit MeasureUnit::getRadian() { + return MeasureUnit(1, 3); +} + +MeasureUnit *MeasureUnit::createRevolutionAngle(UErrorCode &status) { + return MeasureUnit::create(1, 4, status); +} + +MeasureUnit MeasureUnit::getRevolutionAngle() { + return MeasureUnit(1, 4); +} + +MeasureUnit *MeasureUnit::createAcre(UErrorCode &status) { + return MeasureUnit::create(2, 0, status); +} + +MeasureUnit MeasureUnit::getAcre() { + return MeasureUnit(2, 0); +} + +MeasureUnit *MeasureUnit::createDunam(UErrorCode &status) { + return MeasureUnit::create(2, 1, status); +} + +MeasureUnit MeasureUnit::getDunam() { + return MeasureUnit(2, 1); +} + +MeasureUnit *MeasureUnit::createHectare(UErrorCode &status) { + return MeasureUnit::create(2, 2, status); +} + +MeasureUnit MeasureUnit::getHectare() { + return MeasureUnit(2, 2); +} + +MeasureUnit *MeasureUnit::createSquareCentimeter(UErrorCode &status) { + return MeasureUnit::create(2, 3, status); +} + +MeasureUnit MeasureUnit::getSquareCentimeter() { + return MeasureUnit(2, 3); +} + +MeasureUnit *MeasureUnit::createSquareFoot(UErrorCode &status) { + return MeasureUnit::create(2, 4, status); +} + +MeasureUnit MeasureUnit::getSquareFoot() { + return MeasureUnit(2, 4); +} + +MeasureUnit *MeasureUnit::createSquareInch(UErrorCode &status) { + return MeasureUnit::create(2, 5, status); +} + +MeasureUnit MeasureUnit::getSquareInch() { + return MeasureUnit(2, 5); +} + +MeasureUnit *MeasureUnit::createSquareKilometer(UErrorCode &status) { + return MeasureUnit::create(2, 6, status); +} + +MeasureUnit MeasureUnit::getSquareKilometer() { + return MeasureUnit(2, 6); +} + +MeasureUnit *MeasureUnit::createSquareMeter(UErrorCode &status) { + return MeasureUnit::create(2, 7, status); +} + +MeasureUnit MeasureUnit::getSquareMeter() { + return MeasureUnit(2, 7); +} + +MeasureUnit *MeasureUnit::createSquareMile(UErrorCode &status) { + return MeasureUnit::create(2, 8, status); +} + +MeasureUnit MeasureUnit::getSquareMile() { + return MeasureUnit(2, 8); +} + +MeasureUnit *MeasureUnit::createSquareYard(UErrorCode &status) { + return MeasureUnit::create(2, 9, status); +} + +MeasureUnit MeasureUnit::getSquareYard() { + return MeasureUnit(2, 9); +} + +MeasureUnit *MeasureUnit::createItem(UErrorCode &status) { + return MeasureUnit::create(3, 0, status); +} + +MeasureUnit MeasureUnit::getItem() { + return MeasureUnit(3, 0); +} + +MeasureUnit *MeasureUnit::createKarat(UErrorCode &status) { + return MeasureUnit::create(3, 1, status); +} + +MeasureUnit MeasureUnit::getKarat() { + return MeasureUnit(3, 1); +} + +MeasureUnit *MeasureUnit::createMilligramOfglucosePerDeciliter(UErrorCode &status) { + return MeasureUnit::create(3, 2, status); +} + +MeasureUnit MeasureUnit::getMilligramOfglucosePerDeciliter() { + return MeasureUnit(3, 2); +} + +MeasureUnit *MeasureUnit::createMilligramPerDeciliter(UErrorCode &status) { + return MeasureUnit::create(3, 3, status); +} + +MeasureUnit MeasureUnit::getMilligramPerDeciliter() { + return MeasureUnit(3, 3); +} + +MeasureUnit *MeasureUnit::createMillimolePerLiter(UErrorCode &status) { + return MeasureUnit::create(3, 4, status); +} + +MeasureUnit MeasureUnit::getMillimolePerLiter() { + return MeasureUnit(3, 4); +} + +MeasureUnit *MeasureUnit::createMole(UErrorCode &status) { + return MeasureUnit::create(3, 5, status); +} + +MeasureUnit MeasureUnit::getMole() { + return MeasureUnit(3, 5); +} + +MeasureUnit *MeasureUnit::createPercent(UErrorCode &status) { + return MeasureUnit::create(3, 6, status); +} + +MeasureUnit MeasureUnit::getPercent() { + return MeasureUnit(3, 6); +} + +MeasureUnit *MeasureUnit::createPermille(UErrorCode &status) { + return MeasureUnit::create(3, 7, status); +} + +MeasureUnit MeasureUnit::getPermille() { + return MeasureUnit(3, 7); +} + +MeasureUnit *MeasureUnit::createPartPerMillion(UErrorCode &status) { + return MeasureUnit::create(3, 8, status); +} + +MeasureUnit MeasureUnit::getPartPerMillion() { + return MeasureUnit(3, 8); +} + +MeasureUnit *MeasureUnit::createPermyriad(UErrorCode &status) { + return MeasureUnit::create(3, 9, status); +} + +MeasureUnit MeasureUnit::getPermyriad() { + return MeasureUnit(3, 9); +} + +MeasureUnit *MeasureUnit::createLiterPer100Kilometers(UErrorCode &status) { + return MeasureUnit::create(4, 0, status); +} + +MeasureUnit MeasureUnit::getLiterPer100Kilometers() { + return MeasureUnit(4, 0); +} + +MeasureUnit *MeasureUnit::createLiterPerKilometer(UErrorCode &status) { + return MeasureUnit::create(4, 1, status); +} + +MeasureUnit MeasureUnit::getLiterPerKilometer() { + return MeasureUnit(4, 1); +} + +MeasureUnit *MeasureUnit::createMilePerGallon(UErrorCode &status) { + return MeasureUnit::create(4, 2, status); +} + +MeasureUnit MeasureUnit::getMilePerGallon() { + return MeasureUnit(4, 2); +} + +MeasureUnit *MeasureUnit::createMilePerGallonImperial(UErrorCode &status) { + return MeasureUnit::create(4, 3, status); +} + +MeasureUnit MeasureUnit::getMilePerGallonImperial() { + return MeasureUnit(4, 3); +} + +MeasureUnit *MeasureUnit::createBit(UErrorCode &status) { + return MeasureUnit::create(6, 0, status); +} + +MeasureUnit MeasureUnit::getBit() { + return MeasureUnit(6, 0); +} + +MeasureUnit *MeasureUnit::createByte(UErrorCode &status) { + return MeasureUnit::create(6, 1, status); +} + +MeasureUnit MeasureUnit::getByte() { + return MeasureUnit(6, 1); +} + +MeasureUnit *MeasureUnit::createGigabit(UErrorCode &status) { + return MeasureUnit::create(6, 2, status); +} + +MeasureUnit MeasureUnit::getGigabit() { + return MeasureUnit(6, 2); +} + +MeasureUnit *MeasureUnit::createGigabyte(UErrorCode &status) { + return MeasureUnit::create(6, 3, status); +} + +MeasureUnit MeasureUnit::getGigabyte() { + return MeasureUnit(6, 3); +} + +MeasureUnit *MeasureUnit::createKilobit(UErrorCode &status) { + return MeasureUnit::create(6, 4, status); +} + +MeasureUnit MeasureUnit::getKilobit() { + return MeasureUnit(6, 4); +} + +MeasureUnit *MeasureUnit::createKilobyte(UErrorCode &status) { + return MeasureUnit::create(6, 5, status); +} + +MeasureUnit MeasureUnit::getKilobyte() { + return MeasureUnit(6, 5); +} + +MeasureUnit *MeasureUnit::createMegabit(UErrorCode &status) { + return MeasureUnit::create(6, 6, status); +} + +MeasureUnit MeasureUnit::getMegabit() { + return MeasureUnit(6, 6); +} + +MeasureUnit *MeasureUnit::createMegabyte(UErrorCode &status) { + return MeasureUnit::create(6, 7, status); +} + +MeasureUnit MeasureUnit::getMegabyte() { + return MeasureUnit(6, 7); +} + +MeasureUnit *MeasureUnit::createPetabyte(UErrorCode &status) { + return MeasureUnit::create(6, 8, status); +} + +MeasureUnit MeasureUnit::getPetabyte() { + return MeasureUnit(6, 8); +} + +MeasureUnit *MeasureUnit::createTerabit(UErrorCode &status) { + return MeasureUnit::create(6, 9, status); +} + +MeasureUnit MeasureUnit::getTerabit() { + return MeasureUnit(6, 9); +} + +MeasureUnit *MeasureUnit::createTerabyte(UErrorCode &status) { + return MeasureUnit::create(6, 10, status); +} + +MeasureUnit MeasureUnit::getTerabyte() { + return MeasureUnit(6, 10); +} + +MeasureUnit *MeasureUnit::createCentury(UErrorCode &status) { + return MeasureUnit::create(7, 0, status); +} + +MeasureUnit MeasureUnit::getCentury() { + return MeasureUnit(7, 0); +} + +MeasureUnit *MeasureUnit::createDay(UErrorCode &status) { + return MeasureUnit::create(7, 1, status); +} + +MeasureUnit MeasureUnit::getDay() { + return MeasureUnit(7, 1); +} + +MeasureUnit *MeasureUnit::createDayPerson(UErrorCode &status) { + return MeasureUnit::create(7, 2, status); +} + +MeasureUnit MeasureUnit::getDayPerson() { + return MeasureUnit(7, 2); +} + +MeasureUnit *MeasureUnit::createDecade(UErrorCode &status) { + return MeasureUnit::create(7, 3, status); +} + +MeasureUnit MeasureUnit::getDecade() { + return MeasureUnit(7, 3); +} + +MeasureUnit *MeasureUnit::createHour(UErrorCode &status) { + return MeasureUnit::create(7, 4, status); +} + +MeasureUnit MeasureUnit::getHour() { + return MeasureUnit(7, 4); +} + +MeasureUnit *MeasureUnit::createMicrosecond(UErrorCode &status) { + return MeasureUnit::create(7, 5, status); +} + +MeasureUnit MeasureUnit::getMicrosecond() { + return MeasureUnit(7, 5); +} + +MeasureUnit *MeasureUnit::createMillisecond(UErrorCode &status) { + return MeasureUnit::create(7, 6, status); +} + +MeasureUnit MeasureUnit::getMillisecond() { + return MeasureUnit(7, 6); +} + +MeasureUnit *MeasureUnit::createMinute(UErrorCode &status) { + return MeasureUnit::create(7, 7, status); +} + +MeasureUnit MeasureUnit::getMinute() { + return MeasureUnit(7, 7); +} + +MeasureUnit *MeasureUnit::createMonth(UErrorCode &status) { + return MeasureUnit::create(7, 8, status); +} + +MeasureUnit MeasureUnit::getMonth() { + return MeasureUnit(7, 8); +} + +MeasureUnit *MeasureUnit::createMonthPerson(UErrorCode &status) { + return MeasureUnit::create(7, 9, status); +} + +MeasureUnit MeasureUnit::getMonthPerson() { + return MeasureUnit(7, 9); +} + +MeasureUnit *MeasureUnit::createNanosecond(UErrorCode &status) { + return MeasureUnit::create(7, 10, status); +} + +MeasureUnit MeasureUnit::getNanosecond() { + return MeasureUnit(7, 10); +} + +MeasureUnit *MeasureUnit::createQuarter(UErrorCode &status) { + return MeasureUnit::create(7, 11, status); +} + +MeasureUnit MeasureUnit::getQuarter() { + return MeasureUnit(7, 11); +} + +MeasureUnit *MeasureUnit::createSecond(UErrorCode &status) { + return MeasureUnit::create(7, 12, status); +} + +MeasureUnit MeasureUnit::getSecond() { + return MeasureUnit(7, 12); +} + +MeasureUnit *MeasureUnit::createWeek(UErrorCode &status) { + return MeasureUnit::create(7, 13, status); +} + +MeasureUnit MeasureUnit::getWeek() { + return MeasureUnit(7, 13); +} + +MeasureUnit *MeasureUnit::createWeekPerson(UErrorCode &status) { + return MeasureUnit::create(7, 14, status); +} + +MeasureUnit MeasureUnit::getWeekPerson() { + return MeasureUnit(7, 14); +} + +MeasureUnit *MeasureUnit::createYear(UErrorCode &status) { + return MeasureUnit::create(7, 15, status); +} + +MeasureUnit MeasureUnit::getYear() { + return MeasureUnit(7, 15); +} + +MeasureUnit *MeasureUnit::createYearPerson(UErrorCode &status) { + return MeasureUnit::create(7, 16, status); +} + +MeasureUnit MeasureUnit::getYearPerson() { + return MeasureUnit(7, 16); +} + +MeasureUnit *MeasureUnit::createAmpere(UErrorCode &status) { + return MeasureUnit::create(8, 0, status); +} + +MeasureUnit MeasureUnit::getAmpere() { + return MeasureUnit(8, 0); +} + +MeasureUnit *MeasureUnit::createMilliampere(UErrorCode &status) { + return MeasureUnit::create(8, 1, status); +} + +MeasureUnit MeasureUnit::getMilliampere() { + return MeasureUnit(8, 1); +} + +MeasureUnit *MeasureUnit::createOhm(UErrorCode &status) { + return MeasureUnit::create(8, 2, status); +} + +MeasureUnit MeasureUnit::getOhm() { + return MeasureUnit(8, 2); +} + +MeasureUnit *MeasureUnit::createVolt(UErrorCode &status) { + return MeasureUnit::create(8, 3, status); +} + +MeasureUnit MeasureUnit::getVolt() { + return MeasureUnit(8, 3); +} + +MeasureUnit *MeasureUnit::createBritishThermalUnit(UErrorCode &status) { + return MeasureUnit::create(9, 0, status); +} + +MeasureUnit MeasureUnit::getBritishThermalUnit() { + return MeasureUnit(9, 0); +} + +MeasureUnit *MeasureUnit::createCalorie(UErrorCode &status) { + return MeasureUnit::create(9, 1, status); +} + +MeasureUnit MeasureUnit::getCalorie() { + return MeasureUnit(9, 1); +} + +MeasureUnit *MeasureUnit::createElectronvolt(UErrorCode &status) { + return MeasureUnit::create(9, 2, status); +} + +MeasureUnit MeasureUnit::getElectronvolt() { + return MeasureUnit(9, 2); +} + +MeasureUnit *MeasureUnit::createFoodcalorie(UErrorCode &status) { + return MeasureUnit::create(9, 3, status); +} + +MeasureUnit MeasureUnit::getFoodcalorie() { + return MeasureUnit(9, 3); +} + +MeasureUnit *MeasureUnit::createJoule(UErrorCode &status) { + return MeasureUnit::create(9, 4, status); +} + +MeasureUnit MeasureUnit::getJoule() { + return MeasureUnit(9, 4); +} + +MeasureUnit *MeasureUnit::createKilocalorie(UErrorCode &status) { + return MeasureUnit::create(9, 5, status); +} + +MeasureUnit MeasureUnit::getKilocalorie() { + return MeasureUnit(9, 5); +} + +MeasureUnit *MeasureUnit::createKilojoule(UErrorCode &status) { + return MeasureUnit::create(9, 6, status); +} + +MeasureUnit MeasureUnit::getKilojoule() { + return MeasureUnit(9, 6); +} + +MeasureUnit *MeasureUnit::createKilowattHour(UErrorCode &status) { + return MeasureUnit::create(9, 7, status); +} + +MeasureUnit MeasureUnit::getKilowattHour() { + return MeasureUnit(9, 7); +} + +MeasureUnit *MeasureUnit::createThermUs(UErrorCode &status) { + return MeasureUnit::create(9, 8, status); +} + +MeasureUnit MeasureUnit::getThermUs() { + return MeasureUnit(9, 8); +} + +MeasureUnit *MeasureUnit::createKilowattHourPer100Kilometer(UErrorCode &status) { + return MeasureUnit::create(10, 0, status); +} + +MeasureUnit MeasureUnit::getKilowattHourPer100Kilometer() { + return MeasureUnit(10, 0); +} + +MeasureUnit *MeasureUnit::createNewton(UErrorCode &status) { + return MeasureUnit::create(10, 1, status); +} + +MeasureUnit MeasureUnit::getNewton() { + return MeasureUnit(10, 1); +} + +MeasureUnit *MeasureUnit::createPoundForce(UErrorCode &status) { + return MeasureUnit::create(10, 2, status); +} + +MeasureUnit MeasureUnit::getPoundForce() { + return MeasureUnit(10, 2); +} + +MeasureUnit *MeasureUnit::createGigahertz(UErrorCode &status) { + return MeasureUnit::create(11, 0, status); +} + +MeasureUnit MeasureUnit::getGigahertz() { + return MeasureUnit(11, 0); +} + +MeasureUnit *MeasureUnit::createHertz(UErrorCode &status) { + return MeasureUnit::create(11, 1, status); +} + +MeasureUnit MeasureUnit::getHertz() { + return MeasureUnit(11, 1); +} + +MeasureUnit *MeasureUnit::createKilohertz(UErrorCode &status) { + return MeasureUnit::create(11, 2, status); +} + +MeasureUnit MeasureUnit::getKilohertz() { + return MeasureUnit(11, 2); +} + +MeasureUnit *MeasureUnit::createMegahertz(UErrorCode &status) { + return MeasureUnit::create(11, 3, status); +} + +MeasureUnit MeasureUnit::getMegahertz() { + return MeasureUnit(11, 3); +} + +MeasureUnit *MeasureUnit::createDot(UErrorCode &status) { + return MeasureUnit::create(12, 0, status); +} + +MeasureUnit MeasureUnit::getDot() { + return MeasureUnit(12, 0); +} + +MeasureUnit *MeasureUnit::createDotPerCentimeter(UErrorCode &status) { + return MeasureUnit::create(12, 1, status); +} + +MeasureUnit MeasureUnit::getDotPerCentimeter() { + return MeasureUnit(12, 1); +} + +MeasureUnit *MeasureUnit::createDotPerInch(UErrorCode &status) { + return MeasureUnit::create(12, 2, status); +} + +MeasureUnit MeasureUnit::getDotPerInch() { + return MeasureUnit(12, 2); +} + +MeasureUnit *MeasureUnit::createEm(UErrorCode &status) { + return MeasureUnit::create(12, 3, status); +} + +MeasureUnit MeasureUnit::getEm() { + return MeasureUnit(12, 3); +} + +MeasureUnit *MeasureUnit::createMegapixel(UErrorCode &status) { + return MeasureUnit::create(12, 4, status); +} + +MeasureUnit MeasureUnit::getMegapixel() { + return MeasureUnit(12, 4); +} + +MeasureUnit *MeasureUnit::createPixel(UErrorCode &status) { + return MeasureUnit::create(12, 5, status); +} + +MeasureUnit MeasureUnit::getPixel() { + return MeasureUnit(12, 5); +} + +MeasureUnit *MeasureUnit::createPixelPerCentimeter(UErrorCode &status) { + return MeasureUnit::create(12, 6, status); +} + +MeasureUnit MeasureUnit::getPixelPerCentimeter() { + return MeasureUnit(12, 6); +} + +MeasureUnit *MeasureUnit::createPixelPerInch(UErrorCode &status) { + return MeasureUnit::create(12, 7, status); +} + +MeasureUnit MeasureUnit::getPixelPerInch() { + return MeasureUnit(12, 7); +} + +MeasureUnit *MeasureUnit::createAstronomicalUnit(UErrorCode &status) { + return MeasureUnit::create(13, 0, status); +} + +MeasureUnit MeasureUnit::getAstronomicalUnit() { + return MeasureUnit(13, 0); +} + +MeasureUnit *MeasureUnit::createCentimeter(UErrorCode &status) { + return MeasureUnit::create(13, 1, status); +} + +MeasureUnit MeasureUnit::getCentimeter() { + return MeasureUnit(13, 1); +} + +MeasureUnit *MeasureUnit::createDecimeter(UErrorCode &status) { + return MeasureUnit::create(13, 2, status); +} + +MeasureUnit MeasureUnit::getDecimeter() { + return MeasureUnit(13, 2); +} + +MeasureUnit *MeasureUnit::createEarthRadius(UErrorCode &status) { + return MeasureUnit::create(13, 3, status); +} + +MeasureUnit MeasureUnit::getEarthRadius() { + return MeasureUnit(13, 3); +} + +MeasureUnit *MeasureUnit::createFathom(UErrorCode &status) { + return MeasureUnit::create(13, 4, status); +} + +MeasureUnit MeasureUnit::getFathom() { + return MeasureUnit(13, 4); +} + +MeasureUnit *MeasureUnit::createFoot(UErrorCode &status) { + return MeasureUnit::create(13, 5, status); +} + +MeasureUnit MeasureUnit::getFoot() { + return MeasureUnit(13, 5); +} + +MeasureUnit *MeasureUnit::createFurlong(UErrorCode &status) { + return MeasureUnit::create(13, 6, status); +} + +MeasureUnit MeasureUnit::getFurlong() { + return MeasureUnit(13, 6); +} + +MeasureUnit *MeasureUnit::createInch(UErrorCode &status) { + return MeasureUnit::create(13, 7, status); +} + +MeasureUnit MeasureUnit::getInch() { + return MeasureUnit(13, 7); +} + +MeasureUnit *MeasureUnit::createKilometer(UErrorCode &status) { + return MeasureUnit::create(13, 8, status); +} + +MeasureUnit MeasureUnit::getKilometer() { + return MeasureUnit(13, 8); +} + +MeasureUnit *MeasureUnit::createLightYear(UErrorCode &status) { + return MeasureUnit::create(13, 9, status); +} + +MeasureUnit MeasureUnit::getLightYear() { + return MeasureUnit(13, 9); +} + +MeasureUnit *MeasureUnit::createMeter(UErrorCode &status) { + return MeasureUnit::create(13, 10, status); +} + +MeasureUnit MeasureUnit::getMeter() { + return MeasureUnit(13, 10); +} + +MeasureUnit *MeasureUnit::createMicrometer(UErrorCode &status) { + return MeasureUnit::create(13, 11, status); +} + +MeasureUnit MeasureUnit::getMicrometer() { + return MeasureUnit(13, 11); +} + +MeasureUnit *MeasureUnit::createMile(UErrorCode &status) { + return MeasureUnit::create(13, 12, status); +} + +MeasureUnit MeasureUnit::getMile() { + return MeasureUnit(13, 12); +} + +MeasureUnit *MeasureUnit::createMileScandinavian(UErrorCode &status) { + return MeasureUnit::create(13, 13, status); +} + +MeasureUnit MeasureUnit::getMileScandinavian() { + return MeasureUnit(13, 13); +} + +MeasureUnit *MeasureUnit::createMillimeter(UErrorCode &status) { + return MeasureUnit::create(13, 14, status); +} + +MeasureUnit MeasureUnit::getMillimeter() { + return MeasureUnit(13, 14); +} + +MeasureUnit *MeasureUnit::createNanometer(UErrorCode &status) { + return MeasureUnit::create(13, 15, status); +} + +MeasureUnit MeasureUnit::getNanometer() { + return MeasureUnit(13, 15); +} + +MeasureUnit *MeasureUnit::createNauticalMile(UErrorCode &status) { + return MeasureUnit::create(13, 16, status); +} + +MeasureUnit MeasureUnit::getNauticalMile() { + return MeasureUnit(13, 16); +} + +MeasureUnit *MeasureUnit::createParsec(UErrorCode &status) { + return MeasureUnit::create(13, 17, status); +} + +MeasureUnit MeasureUnit::getParsec() { + return MeasureUnit(13, 17); +} + +MeasureUnit *MeasureUnit::createPicometer(UErrorCode &status) { + return MeasureUnit::create(13, 18, status); +} + +MeasureUnit MeasureUnit::getPicometer() { + return MeasureUnit(13, 18); +} + +MeasureUnit *MeasureUnit::createPoint(UErrorCode &status) { + return MeasureUnit::create(13, 19, status); +} + +MeasureUnit MeasureUnit::getPoint() { + return MeasureUnit(13, 19); +} + +MeasureUnit *MeasureUnit::createSolarRadius(UErrorCode &status) { + return MeasureUnit::create(13, 20, status); +} + +MeasureUnit MeasureUnit::getSolarRadius() { + return MeasureUnit(13, 20); +} + +MeasureUnit *MeasureUnit::createYard(UErrorCode &status) { + return MeasureUnit::create(13, 21, status); +} + +MeasureUnit MeasureUnit::getYard() { + return MeasureUnit(13, 21); +} + +MeasureUnit *MeasureUnit::createCandela(UErrorCode &status) { + return MeasureUnit::create(14, 0, status); +} + +MeasureUnit MeasureUnit::getCandela() { + return MeasureUnit(14, 0); +} + +MeasureUnit *MeasureUnit::createLumen(UErrorCode &status) { + return MeasureUnit::create(14, 1, status); +} + +MeasureUnit MeasureUnit::getLumen() { + return MeasureUnit(14, 1); +} + +MeasureUnit *MeasureUnit::createLux(UErrorCode &status) { + return MeasureUnit::create(14, 2, status); +} + +MeasureUnit MeasureUnit::getLux() { + return MeasureUnit(14, 2); +} + +MeasureUnit *MeasureUnit::createSolarLuminosity(UErrorCode &status) { + return MeasureUnit::create(14, 3, status); +} + +MeasureUnit MeasureUnit::getSolarLuminosity() { + return MeasureUnit(14, 3); +} + +MeasureUnit *MeasureUnit::createCarat(UErrorCode &status) { + return MeasureUnit::create(15, 0, status); +} + +MeasureUnit MeasureUnit::getCarat() { + return MeasureUnit(15, 0); +} + +MeasureUnit *MeasureUnit::createDalton(UErrorCode &status) { + return MeasureUnit::create(15, 1, status); +} + +MeasureUnit MeasureUnit::getDalton() { + return MeasureUnit(15, 1); +} + +MeasureUnit *MeasureUnit::createEarthMass(UErrorCode &status) { + return MeasureUnit::create(15, 2, status); +} + +MeasureUnit MeasureUnit::getEarthMass() { + return MeasureUnit(15, 2); +} + +MeasureUnit *MeasureUnit::createGrain(UErrorCode &status) { + return MeasureUnit::create(15, 3, status); +} + +MeasureUnit MeasureUnit::getGrain() { + return MeasureUnit(15, 3); +} + +MeasureUnit *MeasureUnit::createGram(UErrorCode &status) { + return MeasureUnit::create(15, 4, status); +} + +MeasureUnit MeasureUnit::getGram() { + return MeasureUnit(15, 4); +} + +MeasureUnit *MeasureUnit::createKilogram(UErrorCode &status) { + return MeasureUnit::create(15, 5, status); +} + +MeasureUnit MeasureUnit::getKilogram() { + return MeasureUnit(15, 5); +} + +MeasureUnit *MeasureUnit::createMetricTon(UErrorCode &status) { + return MeasureUnit::create(15, 14, status); +} + +MeasureUnit MeasureUnit::getMetricTon() { + return MeasureUnit(15, 14); +} + +MeasureUnit *MeasureUnit::createMicrogram(UErrorCode &status) { + return MeasureUnit::create(15, 6, status); +} + +MeasureUnit MeasureUnit::getMicrogram() { + return MeasureUnit(15, 6); +} + +MeasureUnit *MeasureUnit::createMilligram(UErrorCode &status) { + return MeasureUnit::create(15, 7, status); +} + +MeasureUnit MeasureUnit::getMilligram() { + return MeasureUnit(15, 7); +} + +MeasureUnit *MeasureUnit::createOunce(UErrorCode &status) { + return MeasureUnit::create(15, 8, status); +} + +MeasureUnit MeasureUnit::getOunce() { + return MeasureUnit(15, 8); +} + +MeasureUnit *MeasureUnit::createOunceTroy(UErrorCode &status) { + return MeasureUnit::create(15, 9, status); +} + +MeasureUnit MeasureUnit::getOunceTroy() { + return MeasureUnit(15, 9); +} + +MeasureUnit *MeasureUnit::createPound(UErrorCode &status) { + return MeasureUnit::create(15, 10, status); +} + +MeasureUnit MeasureUnit::getPound() { + return MeasureUnit(15, 10); +} + +MeasureUnit *MeasureUnit::createSolarMass(UErrorCode &status) { + return MeasureUnit::create(15, 11, status); +} + +MeasureUnit MeasureUnit::getSolarMass() { + return MeasureUnit(15, 11); +} + +MeasureUnit *MeasureUnit::createStone(UErrorCode &status) { + return MeasureUnit::create(15, 12, status); +} + +MeasureUnit MeasureUnit::getStone() { + return MeasureUnit(15, 12); +} + +MeasureUnit *MeasureUnit::createTon(UErrorCode &status) { + return MeasureUnit::create(15, 13, status); +} + +MeasureUnit MeasureUnit::getTon() { + return MeasureUnit(15, 13); +} + +MeasureUnit *MeasureUnit::createTonne(UErrorCode &status) { + return MeasureUnit::create(15, 14, status); +} + +MeasureUnit MeasureUnit::getTonne() { + return MeasureUnit(15, 14); +} + +MeasureUnit *MeasureUnit::createGigawatt(UErrorCode &status) { + return MeasureUnit::create(17, 0, status); +} + +MeasureUnit MeasureUnit::getGigawatt() { + return MeasureUnit(17, 0); +} + +MeasureUnit *MeasureUnit::createHorsepower(UErrorCode &status) { + return MeasureUnit::create(17, 1, status); +} + +MeasureUnit MeasureUnit::getHorsepower() { + return MeasureUnit(17, 1); +} + +MeasureUnit *MeasureUnit::createKilowatt(UErrorCode &status) { + return MeasureUnit::create(17, 2, status); +} + +MeasureUnit MeasureUnit::getKilowatt() { + return MeasureUnit(17, 2); +} + +MeasureUnit *MeasureUnit::createMegawatt(UErrorCode &status) { + return MeasureUnit::create(17, 3, status); +} + +MeasureUnit MeasureUnit::getMegawatt() { + return MeasureUnit(17, 3); +} + +MeasureUnit *MeasureUnit::createMilliwatt(UErrorCode &status) { + return MeasureUnit::create(17, 4, status); +} + +MeasureUnit MeasureUnit::getMilliwatt() { + return MeasureUnit(17, 4); +} + +MeasureUnit *MeasureUnit::createWatt(UErrorCode &status) { + return MeasureUnit::create(17, 5, status); +} + +MeasureUnit MeasureUnit::getWatt() { + return MeasureUnit(17, 5); +} + +MeasureUnit *MeasureUnit::createAtmosphere(UErrorCode &status) { + return MeasureUnit::create(18, 0, status); +} + +MeasureUnit MeasureUnit::getAtmosphere() { + return MeasureUnit(18, 0); +} + +MeasureUnit *MeasureUnit::createBar(UErrorCode &status) { + return MeasureUnit::create(18, 1, status); +} + +MeasureUnit MeasureUnit::getBar() { + return MeasureUnit(18, 1); +} + +MeasureUnit *MeasureUnit::createHectopascal(UErrorCode &status) { + return MeasureUnit::create(18, 2, status); +} + +MeasureUnit MeasureUnit::getHectopascal() { + return MeasureUnit(18, 2); +} + +MeasureUnit *MeasureUnit::createInchHg(UErrorCode &status) { + return MeasureUnit::create(18, 3, status); +} + +MeasureUnit MeasureUnit::getInchHg() { + return MeasureUnit(18, 3); +} + +MeasureUnit *MeasureUnit::createKilopascal(UErrorCode &status) { + return MeasureUnit::create(18, 4, status); +} + +MeasureUnit MeasureUnit::getKilopascal() { + return MeasureUnit(18, 4); +} + +MeasureUnit *MeasureUnit::createMegapascal(UErrorCode &status) { + return MeasureUnit::create(18, 5, status); +} + +MeasureUnit MeasureUnit::getMegapascal() { + return MeasureUnit(18, 5); +} + +MeasureUnit *MeasureUnit::createMillibar(UErrorCode &status) { + return MeasureUnit::create(18, 6, status); +} + +MeasureUnit MeasureUnit::getMillibar() { + return MeasureUnit(18, 6); +} + +MeasureUnit *MeasureUnit::createMillimeterOfMercury(UErrorCode &status) { + return MeasureUnit::create(18, 7, status); +} + +MeasureUnit MeasureUnit::getMillimeterOfMercury() { + return MeasureUnit(18, 7); +} + +MeasureUnit *MeasureUnit::createPascal(UErrorCode &status) { + return MeasureUnit::create(18, 8, status); +} + +MeasureUnit MeasureUnit::getPascal() { + return MeasureUnit(18, 8); +} + +MeasureUnit *MeasureUnit::createPoundPerSquareInch(UErrorCode &status) { + return MeasureUnit::create(18, 9, status); +} + +MeasureUnit MeasureUnit::getPoundPerSquareInch() { + return MeasureUnit(18, 9); +} + +MeasureUnit *MeasureUnit::createBeaufort(UErrorCode &status) { + return MeasureUnit::create(19, 0, status); +} + +MeasureUnit MeasureUnit::getBeaufort() { + return MeasureUnit(19, 0); +} + +MeasureUnit *MeasureUnit::createKilometerPerHour(UErrorCode &status) { + return MeasureUnit::create(19, 1, status); +} + +MeasureUnit MeasureUnit::getKilometerPerHour() { + return MeasureUnit(19, 1); +} + +MeasureUnit *MeasureUnit::createKnot(UErrorCode &status) { + return MeasureUnit::create(19, 2, status); +} + +MeasureUnit MeasureUnit::getKnot() { + return MeasureUnit(19, 2); +} + +MeasureUnit *MeasureUnit::createMeterPerSecond(UErrorCode &status) { + return MeasureUnit::create(19, 3, status); +} + +MeasureUnit MeasureUnit::getMeterPerSecond() { + return MeasureUnit(19, 3); +} + +MeasureUnit *MeasureUnit::createMilePerHour(UErrorCode &status) { + return MeasureUnit::create(19, 4, status); +} + +MeasureUnit MeasureUnit::getMilePerHour() { + return MeasureUnit(19, 4); +} + +MeasureUnit *MeasureUnit::createCelsius(UErrorCode &status) { + return MeasureUnit::create(20, 0, status); +} + +MeasureUnit MeasureUnit::getCelsius() { + return MeasureUnit(20, 0); +} + +MeasureUnit *MeasureUnit::createFahrenheit(UErrorCode &status) { + return MeasureUnit::create(20, 1, status); +} + +MeasureUnit MeasureUnit::getFahrenheit() { + return MeasureUnit(20, 1); +} + +MeasureUnit *MeasureUnit::createGenericTemperature(UErrorCode &status) { + return MeasureUnit::create(20, 2, status); +} + +MeasureUnit MeasureUnit::getGenericTemperature() { + return MeasureUnit(20, 2); +} + +MeasureUnit *MeasureUnit::createKelvin(UErrorCode &status) { + return MeasureUnit::create(20, 3, status); +} + +MeasureUnit MeasureUnit::getKelvin() { + return MeasureUnit(20, 3); +} + +MeasureUnit *MeasureUnit::createNewtonMeter(UErrorCode &status) { + return MeasureUnit::create(21, 0, status); +} + +MeasureUnit MeasureUnit::getNewtonMeter() { + return MeasureUnit(21, 0); +} + +MeasureUnit *MeasureUnit::createPoundFoot(UErrorCode &status) { + return MeasureUnit::create(21, 1, status); +} + +MeasureUnit MeasureUnit::getPoundFoot() { + return MeasureUnit(21, 1); +} + +MeasureUnit *MeasureUnit::createAcreFoot(UErrorCode &status) { + return MeasureUnit::create(22, 0, status); +} + +MeasureUnit MeasureUnit::getAcreFoot() { + return MeasureUnit(22, 0); +} + +MeasureUnit *MeasureUnit::createBarrel(UErrorCode &status) { + return MeasureUnit::create(22, 1, status); +} + +MeasureUnit MeasureUnit::getBarrel() { + return MeasureUnit(22, 1); +} + +MeasureUnit *MeasureUnit::createBushel(UErrorCode &status) { + return MeasureUnit::create(22, 2, status); +} + +MeasureUnit MeasureUnit::getBushel() { + return MeasureUnit(22, 2); +} + +MeasureUnit *MeasureUnit::createCentiliter(UErrorCode &status) { + return MeasureUnit::create(22, 3, status); +} + +MeasureUnit MeasureUnit::getCentiliter() { + return MeasureUnit(22, 3); +} + +MeasureUnit *MeasureUnit::createCubicCentimeter(UErrorCode &status) { + return MeasureUnit::create(22, 4, status); +} + +MeasureUnit MeasureUnit::getCubicCentimeter() { + return MeasureUnit(22, 4); +} + +MeasureUnit *MeasureUnit::createCubicFoot(UErrorCode &status) { + return MeasureUnit::create(22, 5, status); +} + +MeasureUnit MeasureUnit::getCubicFoot() { + return MeasureUnit(22, 5); +} + +MeasureUnit *MeasureUnit::createCubicInch(UErrorCode &status) { + return MeasureUnit::create(22, 6, status); +} + +MeasureUnit MeasureUnit::getCubicInch() { + return MeasureUnit(22, 6); +} + +MeasureUnit *MeasureUnit::createCubicKilometer(UErrorCode &status) { + return MeasureUnit::create(22, 7, status); +} + +MeasureUnit MeasureUnit::getCubicKilometer() { + return MeasureUnit(22, 7); +} + +MeasureUnit *MeasureUnit::createCubicMeter(UErrorCode &status) { + return MeasureUnit::create(22, 8, status); +} + +MeasureUnit MeasureUnit::getCubicMeter() { + return MeasureUnit(22, 8); +} + +MeasureUnit *MeasureUnit::createCubicMile(UErrorCode &status) { + return MeasureUnit::create(22, 9, status); +} + +MeasureUnit MeasureUnit::getCubicMile() { + return MeasureUnit(22, 9); +} + +MeasureUnit *MeasureUnit::createCubicYard(UErrorCode &status) { + return MeasureUnit::create(22, 10, status); +} + +MeasureUnit MeasureUnit::getCubicYard() { + return MeasureUnit(22, 10); +} + +MeasureUnit *MeasureUnit::createCup(UErrorCode &status) { + return MeasureUnit::create(22, 11, status); +} + +MeasureUnit MeasureUnit::getCup() { + return MeasureUnit(22, 11); +} + +MeasureUnit *MeasureUnit::createCupMetric(UErrorCode &status) { + return MeasureUnit::create(22, 12, status); +} + +MeasureUnit MeasureUnit::getCupMetric() { + return MeasureUnit(22, 12); +} + +MeasureUnit *MeasureUnit::createDeciliter(UErrorCode &status) { + return MeasureUnit::create(22, 13, status); +} + +MeasureUnit MeasureUnit::getDeciliter() { + return MeasureUnit(22, 13); +} + +MeasureUnit *MeasureUnit::createDessertSpoon(UErrorCode &status) { + return MeasureUnit::create(22, 14, status); +} + +MeasureUnit MeasureUnit::getDessertSpoon() { + return MeasureUnit(22, 14); +} + +MeasureUnit *MeasureUnit::createDessertSpoonImperial(UErrorCode &status) { + return MeasureUnit::create(22, 15, status); +} + +MeasureUnit MeasureUnit::getDessertSpoonImperial() { + return MeasureUnit(22, 15); +} + +MeasureUnit *MeasureUnit::createDram(UErrorCode &status) { + return MeasureUnit::create(22, 16, status); +} + +MeasureUnit MeasureUnit::getDram() { + return MeasureUnit(22, 16); +} + +MeasureUnit *MeasureUnit::createDrop(UErrorCode &status) { + return MeasureUnit::create(22, 17, status); +} + +MeasureUnit MeasureUnit::getDrop() { + return MeasureUnit(22, 17); +} + +MeasureUnit *MeasureUnit::createFluidOunce(UErrorCode &status) { + return MeasureUnit::create(22, 18, status); +} + +MeasureUnit MeasureUnit::getFluidOunce() { + return MeasureUnit(22, 18); +} + +MeasureUnit *MeasureUnit::createFluidOunceImperial(UErrorCode &status) { + return MeasureUnit::create(22, 19, status); +} + +MeasureUnit MeasureUnit::getFluidOunceImperial() { + return MeasureUnit(22, 19); +} + +MeasureUnit *MeasureUnit::createGallon(UErrorCode &status) { + return MeasureUnit::create(22, 20, status); +} + +MeasureUnit MeasureUnit::getGallon() { + return MeasureUnit(22, 20); +} + +MeasureUnit *MeasureUnit::createGallonImperial(UErrorCode &status) { + return MeasureUnit::create(22, 21, status); +} + +MeasureUnit MeasureUnit::getGallonImperial() { + return MeasureUnit(22, 21); +} + +MeasureUnit *MeasureUnit::createHectoliter(UErrorCode &status) { + return MeasureUnit::create(22, 22, status); +} + +MeasureUnit MeasureUnit::getHectoliter() { + return MeasureUnit(22, 22); +} + +MeasureUnit *MeasureUnit::createJigger(UErrorCode &status) { + return MeasureUnit::create(22, 23, status); +} + +MeasureUnit MeasureUnit::getJigger() { + return MeasureUnit(22, 23); +} + +MeasureUnit *MeasureUnit::createLiter(UErrorCode &status) { + return MeasureUnit::create(22, 24, status); +} + +MeasureUnit MeasureUnit::getLiter() { + return MeasureUnit(22, 24); +} + +MeasureUnit *MeasureUnit::createMegaliter(UErrorCode &status) { + return MeasureUnit::create(22, 25, status); +} + +MeasureUnit MeasureUnit::getMegaliter() { + return MeasureUnit(22, 25); +} + +MeasureUnit *MeasureUnit::createMilliliter(UErrorCode &status) { + return MeasureUnit::create(22, 26, status); +} + +MeasureUnit MeasureUnit::getMilliliter() { + return MeasureUnit(22, 26); +} + +MeasureUnit *MeasureUnit::createPinch(UErrorCode &status) { + return MeasureUnit::create(22, 27, status); +} + +MeasureUnit MeasureUnit::getPinch() { + return MeasureUnit(22, 27); +} + +MeasureUnit *MeasureUnit::createPint(UErrorCode &status) { + return MeasureUnit::create(22, 28, status); +} + +MeasureUnit MeasureUnit::getPint() { + return MeasureUnit(22, 28); +} + +MeasureUnit *MeasureUnit::createPintMetric(UErrorCode &status) { + return MeasureUnit::create(22, 29, status); +} + +MeasureUnit MeasureUnit::getPintMetric() { + return MeasureUnit(22, 29); +} + +MeasureUnit *MeasureUnit::createQuart(UErrorCode &status) { + return MeasureUnit::create(22, 30, status); +} + +MeasureUnit MeasureUnit::getQuart() { + return MeasureUnit(22, 30); +} + +MeasureUnit *MeasureUnit::createQuartImperial(UErrorCode &status) { + return MeasureUnit::create(22, 31, status); +} + +MeasureUnit MeasureUnit::getQuartImperial() { + return MeasureUnit(22, 31); +} + +MeasureUnit *MeasureUnit::createTablespoon(UErrorCode &status) { + return MeasureUnit::create(22, 32, status); +} + +MeasureUnit MeasureUnit::getTablespoon() { + return MeasureUnit(22, 32); +} + +MeasureUnit *MeasureUnit::createTeaspoon(UErrorCode &status) { + return MeasureUnit::create(22, 33, status); +} + +MeasureUnit MeasureUnit::getTeaspoon() { + return MeasureUnit(22, 33); +} + +// End generated code for measunit.cpp + +static int32_t binarySearch( + const char * const * array, int32_t start, int32_t end, StringPiece key) { + while (start < end) { + int32_t mid = (start + end) / 2; + int32_t cmp = StringPiece(array[mid]).compare(key); + if (cmp < 0) { + start = mid + 1; + continue; + } + if (cmp == 0) { + return mid; + } + end = mid; + } + return -1; +} + +MeasureUnit::MeasureUnit() : MeasureUnit(kBaseTypeIdx, kBaseSubTypeIdx) { +} + +MeasureUnit::MeasureUnit(int32_t typeId, int32_t subTypeId) + : fImpl(nullptr), fSubTypeId(subTypeId), fTypeId(typeId) { +} + +MeasureUnit::MeasureUnit(const MeasureUnit &other) + : fImpl(nullptr) { + *this = other; +} + +MeasureUnit::MeasureUnit(MeasureUnit &&other) noexcept + : fImpl(other.fImpl), + fSubTypeId(other.fSubTypeId), + fTypeId(other.fTypeId) { + other.fImpl = nullptr; +} + +MeasureUnit::MeasureUnit(MeasureUnitImpl&& impl) + : fImpl(nullptr), fSubTypeId(-1), fTypeId(-1) { + if (!findBySubType(impl.identifier.toStringPiece(), this)) { + fImpl = new MeasureUnitImpl(std::move(impl)); + } +} + +MeasureUnit &MeasureUnit::operator=(const MeasureUnit &other) { + if (this == &other) { + return *this; + } + if (fImpl != nullptr) { + delete fImpl; + } + if (other.fImpl) { + ErrorCode localStatus; + fImpl = new MeasureUnitImpl(other.fImpl->copy(localStatus)); + if (!fImpl || localStatus.isFailure()) { + // Unrecoverable allocation error; set to the default unit + *this = MeasureUnit(); + return *this; + } + } else { + fImpl = nullptr; + } + fTypeId = other.fTypeId; + fSubTypeId = other.fSubTypeId; + return *this; +} + +MeasureUnit &MeasureUnit::operator=(MeasureUnit &&other) noexcept { + if (this == &other) { + return *this; + } + if (fImpl != nullptr) { + delete fImpl; + } + fImpl = other.fImpl; + other.fImpl = nullptr; + fTypeId = other.fTypeId; + fSubTypeId = other.fSubTypeId; + return *this; +} + +MeasureUnit *MeasureUnit::clone() const { + return new MeasureUnit(*this); +} + +MeasureUnit::~MeasureUnit() { + if (fImpl != nullptr) { + delete fImpl; + fImpl = nullptr; + } +} + +const char *MeasureUnit::getType() const { + // We have a type & subtype only if fTypeId is present. + if (fTypeId == -1) { + return ""; + } + return gTypes[fTypeId]; +} + +const char *MeasureUnit::getSubtype() const { + // We have a type & subtype only if fTypeId is present. + if (fTypeId == -1) { + return ""; + } + return getIdentifier(); +} + +const char *MeasureUnit::getIdentifier() const { + return fImpl ? fImpl->identifier.data() : gSubTypes[getOffset()]; +} + +bool MeasureUnit::operator==(const UObject& other) const { + if (this == &other) { // Same object, equal + return true; + } + if (typeid(*this) != typeid(other)) { // Different types, not equal + return false; + } + const MeasureUnit &rhs = static_cast(other); + return uprv_strcmp(getIdentifier(), rhs.getIdentifier()) == 0; +} + +int32_t MeasureUnit::getAvailable( + MeasureUnit *dest, + int32_t destCapacity, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { + return 0; + } + if (destCapacity < UPRV_LENGTHOF(gSubTypes)) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return UPRV_LENGTHOF(gSubTypes); + } + int32_t idx = 0; + for (int32_t typeIdx = 0; typeIdx < UPRV_LENGTHOF(gTypes); ++typeIdx) { + int32_t len = gOffsets[typeIdx + 1] - gOffsets[typeIdx]; + for (int32_t subTypeIdx = 0; subTypeIdx < len; ++subTypeIdx) { + dest[idx].setTo(typeIdx, subTypeIdx); + ++idx; + } + } + U_ASSERT(idx == UPRV_LENGTHOF(gSubTypes)); + return UPRV_LENGTHOF(gSubTypes); +} + +int32_t MeasureUnit::getAvailable( + const char *type, + MeasureUnit *dest, + int32_t destCapacity, + UErrorCode &errorCode) { + if (U_FAILURE(errorCode)) { + return 0; + } + int32_t typeIdx = binarySearch(gTypes, 0, UPRV_LENGTHOF(gTypes), type); + if (typeIdx == -1) { + return 0; + } + int32_t len = gOffsets[typeIdx + 1] - gOffsets[typeIdx]; + if (destCapacity < len) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return len; + } + for (int subTypeIdx = 0; subTypeIdx < len; ++subTypeIdx) { + dest[subTypeIdx].setTo(typeIdx, subTypeIdx); + } + return len; +} + +StringEnumeration* MeasureUnit::getAvailableTypes(UErrorCode &errorCode) { + UEnumeration *uenum = uenum_openCharStringsEnumeration( + gTypes, UPRV_LENGTHOF(gTypes), &errorCode); + if (U_FAILURE(errorCode)) { + uenum_close(uenum); + return nullptr; + } + StringEnumeration *result = new UStringEnumeration(uenum); + if (result == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + uenum_close(uenum); + return nullptr; + } + return result; +} + +bool MeasureUnit::findBySubType(StringPiece subType, MeasureUnit* output) { + // Sanity checking kCurrencyOffset and final entry in gOffsets + U_ASSERT(uprv_strcmp(gTypes[kCurrencyOffset], "currency") == 0); + U_ASSERT(gOffsets[UPRV_LENGTHOF(gOffsets) - 1] == UPRV_LENGTHOF(gSubTypes)); + + for (int32_t t = 0; t < UPRV_LENGTHOF(gOffsets) - 1; t++) { + // Skip currency units + if (t == kCurrencyOffset) { + continue; + } + int32_t st = binarySearch(gSubTypes, gOffsets[t], gOffsets[t + 1], subType); + if (st >= 0) { + output->setTo(t, st - gOffsets[t]); + return true; + } + } + return false; +} + +MeasureUnit *MeasureUnit::create(int typeId, int subTypeId, UErrorCode &status) { + if (U_FAILURE(status)) { + return nullptr; + } + MeasureUnit *result = new MeasureUnit(typeId, subTypeId); + if (result == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return result; +} + +void MeasureUnit::initTime(const char *timeId) { + int32_t result = binarySearch(gTypes, 0, UPRV_LENGTHOF(gTypes), "duration"); + U_ASSERT(result != -1); + fTypeId = result; + result = binarySearch(gSubTypes, gOffsets[fTypeId], gOffsets[fTypeId + 1], timeId); + U_ASSERT(result != -1); + fSubTypeId = result - gOffsets[fTypeId]; +} + +void MeasureUnit::initCurrency(StringPiece isoCurrency) { + int32_t result = binarySearch(gTypes, 0, UPRV_LENGTHOF(gTypes), "currency"); + U_ASSERT(result != -1); + fTypeId = result; + result = binarySearch( + gSubTypes, gOffsets[fTypeId], gOffsets[fTypeId + 1], isoCurrency); + if (result == -1) { + fImpl = new MeasureUnitImpl(MeasureUnitImpl::forCurrencyCode(isoCurrency)); + if (fImpl) { + fSubTypeId = -1; + return; + } + // malloc error: fall back to the undefined currency + result = binarySearch( + gSubTypes, gOffsets[fTypeId], gOffsets[fTypeId + 1], kDefaultCurrency8); + U_ASSERT(result != -1); + } + fSubTypeId = result - gOffsets[fTypeId]; +} + +void MeasureUnit::setTo(int32_t typeId, int32_t subTypeId) { + fTypeId = typeId; + fSubTypeId = subTypeId; + if (fImpl != nullptr) { + delete fImpl; + fImpl = nullptr; + } +} + +int32_t MeasureUnit::getOffset() const { + if (fTypeId < 0 || fSubTypeId < 0) { + return -1; + } + return gOffsets[fTypeId] + fSubTypeId; +} + +MeasureUnitImpl MeasureUnitImpl::copy(UErrorCode &status) const { + MeasureUnitImpl result; + result.complexity = complexity; + result.identifier.append(identifier, status); + for (int32_t i = 0; i < singleUnits.length(); i++) { + SingleUnitImpl *item = result.singleUnits.emplaceBack(*singleUnits[i]); + if (!item) { + status = U_MEMORY_ALLOCATION_ERROR; + return result; + } + } + return result; +} + +U_NAMESPACE_END + +#endif /* !UNCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/measunit_extra.cpp b/deps/icu-small/source/i18n/measunit_extra.cpp index 3d49d1d610a2bc..ecb18dd6154789 100644 --- a/deps/icu-small/source/i18n/measunit_extra.cpp +++ b/deps/icu-small/source/i18n/measunit_extra.cpp @@ -1,1260 +1,1260 @@ -// © 2020 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -// Extra functions for MeasureUnit not needed for all clients. -// Separate .o file so that it can be removed for modularity. - -#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 "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "measunit_impl.h" -#include "resource.h" -#include "uarrsort.h" -#include "uassert.h" -#include "ucln_in.h" -#include "umutex.h" -#include "unicode/bytestrie.h" -#include "unicode/bytestriebuilder.h" -#include "unicode/localpointer.h" -#include "unicode/stringpiece.h" -#include "unicode/stringtriebuilder.h" -#include "unicode/ures.h" -#include "unicode/ustringtrie.h" -#include "uresimp.h" -#include "util.h" -#include - -U_NAMESPACE_BEGIN - - -namespace { - -// TODO: Propose a new error code for this? -constexpr UErrorCode kUnitIdentifierSyntaxError = U_ILLEGAL_ARGUMENT_ERROR; - -// Trie value offset for SI or binary prefixes. This is big enough to ensure we only -// insert positive integers into the trie. -constexpr int32_t kPrefixOffset = 64; -static_assert(kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MIN_BIN > 0, - "kPrefixOffset is too small for minimum UMeasurePrefix value"); -static_assert(kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MIN_SI > 0, - "kPrefixOffset is too small for minimum UMeasurePrefix value"); - -// Trie value offset for compound parts, e.g. "-per-", "-", "-and-". -constexpr int32_t kCompoundPartOffset = 128; -static_assert(kCompoundPartOffset > kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MAX_BIN, - "Ambiguous token values: prefix tokens are overlapping with CompoundPart tokens"); -static_assert(kCompoundPartOffset > kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MAX_SI, - "Ambiguous token values: prefix tokens are overlapping with CompoundPart tokens"); - -enum CompoundPart { - // Represents "-per-" - COMPOUND_PART_PER = kCompoundPartOffset, - // Represents "-" - COMPOUND_PART_TIMES, - // Represents "-and-" - COMPOUND_PART_AND, -}; - -// Trie value offset for "per-". -constexpr int32_t kInitialCompoundPartOffset = 192; - -enum InitialCompoundPart { - // Represents "per-", the only compound part that can appear at the start of - // an identifier. - INITIAL_COMPOUND_PART_PER = kInitialCompoundPartOffset, -}; - -// Trie value offset for powers like "square-", "cubic-", "pow2-" etc. -constexpr int32_t kPowerPartOffset = 256; - -enum PowerPart { - POWER_PART_P2 = kPowerPartOffset + 2, - POWER_PART_P3, - POWER_PART_P4, - POWER_PART_P5, - POWER_PART_P6, - POWER_PART_P7, - POWER_PART_P8, - POWER_PART_P9, - POWER_PART_P10, - POWER_PART_P11, - POWER_PART_P12, - POWER_PART_P13, - POWER_PART_P14, - POWER_PART_P15, -}; - -// Trie value offset for simple units, e.g. "gram", "nautical-mile", -// "fluid-ounce-imperial". -constexpr int32_t kSimpleUnitOffset = 512; - -const struct UnitPrefixStrings { - const char* const string; - UMeasurePrefix value; -} gUnitPrefixStrings[] = { - // SI prefixes - { "yotta", UMEASURE_PREFIX_YOTTA }, - { "zetta", UMEASURE_PREFIX_ZETTA }, - { "exa", UMEASURE_PREFIX_EXA }, - { "peta", UMEASURE_PREFIX_PETA }, - { "tera", UMEASURE_PREFIX_TERA }, - { "giga", UMEASURE_PREFIX_GIGA }, - { "mega", UMEASURE_PREFIX_MEGA }, - { "kilo", UMEASURE_PREFIX_KILO }, - { "hecto", UMEASURE_PREFIX_HECTO }, - { "deka", UMEASURE_PREFIX_DEKA }, - { "deci", UMEASURE_PREFIX_DECI }, - { "centi", UMEASURE_PREFIX_CENTI }, - { "milli", UMEASURE_PREFIX_MILLI }, - { "micro", UMEASURE_PREFIX_MICRO }, - { "nano", UMEASURE_PREFIX_NANO }, - { "pico", UMEASURE_PREFIX_PICO }, - { "femto", UMEASURE_PREFIX_FEMTO }, - { "atto", UMEASURE_PREFIX_ATTO }, - { "zepto", UMEASURE_PREFIX_ZEPTO }, - { "yocto", UMEASURE_PREFIX_YOCTO }, - // Binary prefixes - { "yobi", UMEASURE_PREFIX_YOBI }, - { "zebi", UMEASURE_PREFIX_ZEBI }, - { "exbi", UMEASURE_PREFIX_EXBI }, - { "pebi", UMEASURE_PREFIX_PEBI }, - { "tebi", UMEASURE_PREFIX_TEBI }, - { "gibi", UMEASURE_PREFIX_GIBI }, - { "mebi", UMEASURE_PREFIX_MEBI }, - { "kibi", UMEASURE_PREFIX_KIBI }, -}; - -/** - * A ResourceSink that collects simple unit identifiers from the keys of the - * convertUnits table into an array, and adds these values to a TrieBuilder, - * with associated values being their index into this array plus a specified - * offset. - * - * Example code: - * - * UErrorCode status = U_ZERO_ERROR; - * BytesTrieBuilder b(status); - * int32_t ARR_SIZE = 200; - * const char *unitIdentifiers[ARR_SIZE]; - * int32_t *unitCategories[ARR_SIZE]; - * SimpleUnitIdentifiersSink identifierSink(gSerializedUnitCategoriesTrie, unitIdentifiers, - * unitCategories, ARR_SIZE, b, kTrieValueOffset); - * LocalUResourceBundlePointer unitsBundle(ures_openDirect(NULL, "units", &status)); - * ures_getAllItemsWithFallback(unitsBundle.getAlias(), "convertUnits", identifierSink, status); - */ -class SimpleUnitIdentifiersSink : public icu::ResourceSink { - public: - /** - * Constructor. - * @param quantitiesTrieData The data for constructing a quantitiesTrie, - * which maps from a simple unit identifier to an index into the - * gCategories array. - * @param out Array of char* to which pointers to the simple unit - * identifiers will be saved. (Does not take ownership.) - * @param outCategories Array of int32_t to which category indexes will be - * saved: this corresponds to simple unit IDs saved to `out`, mapping - * from the ID to the value produced by the quantitiesTrie (which is an - * index into the gCategories array). - * @param outSize The size of `out` and `outCategories`. - * @param trieBuilder The trie builder to which the simple unit identifier - * should be added. The trie builder must outlive this resource sink. - * @param trieValueOffset This is added to the index of the identifier in - * the `out` array, before adding to `trieBuilder` as the value - * associated with the identifier. - */ - explicit SimpleUnitIdentifiersSink(StringPiece quantitiesTrieData, const char **out, - int32_t *outCategories, int32_t outSize, - BytesTrieBuilder &trieBuilder, int32_t trieValueOffset) - : outArray(out), outCategories(outCategories), outSize(outSize), trieBuilder(trieBuilder), - trieValueOffset(trieValueOffset), quantitiesTrieData(quantitiesTrieData), outIndex(0) {} - - /** - * Adds the table keys found in value to the output vector. - * @param key The key of the resource passed to `value`: the second - * parameter of the ures_getAllItemsWithFallback() call. - * @param value Should be a ResourceTable value, if - * ures_getAllItemsWithFallback() was called correctly for this sink. - * @param noFallback Ignored. - * @param status The standard ICU error code output parameter. - */ - void put(const char * /*key*/, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override { - ResourceTable table = value.getTable(status); - if (U_FAILURE(status)) return; - - if (outIndex + table.getSize() > outSize) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - return; - } - - BytesTrie quantitiesTrie(quantitiesTrieData.data()); - - // Collect keys from the table resource. - const char *simpleUnitID; - for (int32_t i = 0; table.getKeyAndValue(i, simpleUnitID, value); ++i) { - U_ASSERT(i < table.getSize()); - U_ASSERT(outIndex < outSize); - if (uprv_strcmp(simpleUnitID, "kilogram") == 0) { - // For parsing, we use "gram", the prefixless metric mass unit. We - // thus ignore the SI Base Unit of Mass: it exists due to being the - // mass conversion target unit, but not needed for MeasureUnit - // parsing. - continue; - } - outArray[outIndex] = simpleUnitID; - trieBuilder.add(simpleUnitID, trieValueOffset + outIndex, status); - - // Find the base target unit for this simple unit - ResourceTable table = value.getTable(status); - if (U_FAILURE(status)) { return; } - if (!table.findValue("target", value)) { - status = U_INVALID_FORMAT_ERROR; - break; - } - int32_t len; - const UChar* uTarget = value.getString(len, status); - CharString target; - target.appendInvariantChars(uTarget, len, status); - if (U_FAILURE(status)) { return; } - quantitiesTrie.reset(); - UStringTrieResult result = quantitiesTrie.next(target.data(), target.length()); - if (!USTRINGTRIE_HAS_VALUE(result)) { - status = U_INVALID_FORMAT_ERROR; - break; - } - outCategories[outIndex] = quantitiesTrie.getValue(); - - outIndex++; - } - } - - private: - const char **outArray; - int32_t *outCategories; - int32_t outSize; - BytesTrieBuilder &trieBuilder; - int32_t trieValueOffset; - - StringPiece quantitiesTrieData; - - int32_t outIndex; -}; - -/** - * A ResourceSink that collects information from `unitQuantities` in the `units` - * resource to provide key->value lookups from base unit to category, as well as - * preserving ordering information for these categories. See `units.txt`. - * - * For example: "kilogram" -> "mass", "meter-per-second" -> "speed". - * - * In C++ unitQuantity values are collected in order into a UChar* array, while - * unitQuantity keys are added added to a TrieBuilder, with associated values - * being the index into the aforementioned UChar* array. - */ -class CategoriesSink : public icu::ResourceSink { - public: - /** - * Constructor. - * @param out Array of UChar* to which unitQuantity values will be saved. - * The pointers returned not owned: they point directly at the resource - * strings in static memory. - * @param outSize The size of the `out` array. - * @param trieBuilder The trie builder to which the keys (base units) of - * each unitQuantity will be added, each with value being the offset - * into `out`. - */ - explicit CategoriesSink(const UChar **out, int32_t &outSize, BytesTrieBuilder &trieBuilder) - : outQuantitiesArray(out), outSize(outSize), trieBuilder(trieBuilder), outIndex(0) {} - - void put(const char * /*key*/, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override { - ResourceArray array = value.getArray(status); - if (U_FAILURE(status)) { - return; - } - - if (outIndex + array.getSize() > outSize) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - return; - } - - for (int32_t i = 0; array.getValue(i, value); ++i) { - U_ASSERT(outIndex < outSize); - ResourceTable table = value.getTable(status); - if (U_FAILURE(status)) { - return; - } - if (table.getSize() != 1) { - status = U_INVALID_FORMAT_ERROR; - return; - } - const char *key; - table.getKeyAndValue(0, key, value); - int32_t uTmpLen; - outQuantitiesArray[outIndex] = value.getString(uTmpLen, status); - trieBuilder.add(key, outIndex, status); - outIndex++; - } - } - - private: - const UChar **outQuantitiesArray; - int32_t &outSize; - BytesTrieBuilder &trieBuilder; - - int32_t outIndex; -}; - -icu::UInitOnce gUnitExtrasInitOnce {}; - -// Array of simple unit IDs. -// -// The array memory itself is owned by this pointer, but the individual char* in -// that array point at static memory. (Note that these char* are also returned -// by SingleUnitImpl::getSimpleUnitID().) -const char **gSimpleUnits = nullptr; - -// Maps from the value associated with each simple unit ID to an index into the -// gCategories array. -int32_t *gSimpleUnitCategories = nullptr; - -char *gSerializedUnitExtrasStemTrie = nullptr; - -// Array of UChar* pointing at the unit categories (aka "quantities", aka -// "types"), as found in the `unitQuantities` resource. The array memory itself -// is owned by this pointer, but the individual UChar* in that array point at -// static memory. -const UChar **gCategories = nullptr; -// Number of items in `gCategories`. -int32_t gCategoriesCount = 0; -// Serialized BytesTrie for mapping from base units to indices into gCategories. -char *gSerializedUnitCategoriesTrie = nullptr; - -UBool U_CALLCONV cleanupUnitExtras() { - uprv_free(gSerializedUnitCategoriesTrie); - gSerializedUnitCategoriesTrie = nullptr; - uprv_free(gCategories); - gCategories = nullptr; - uprv_free(gSerializedUnitExtrasStemTrie); - gSerializedUnitExtrasStemTrie = nullptr; - uprv_free(gSimpleUnitCategories); - gSimpleUnitCategories = nullptr; - uprv_free(gSimpleUnits); - gSimpleUnits = nullptr; - gUnitExtrasInitOnce.reset(); - return true; -} - -void U_CALLCONV initUnitExtras(UErrorCode& status) { - ucln_i18n_registerCleanup(UCLN_I18N_UNIT_EXTRAS, cleanupUnitExtras); - LocalUResourceBundlePointer unitsBundle(ures_openDirect(nullptr, "units", &status)); - - // Collect unitQuantities information into gSerializedUnitCategoriesTrie and gCategories. - const char *CATEGORY_TABLE_NAME = "unitQuantities"; - LocalUResourceBundlePointer unitQuantities( - ures_getByKey(unitsBundle.getAlias(), CATEGORY_TABLE_NAME, nullptr, &status)); - if (U_FAILURE(status)) { return; } - gCategoriesCount = unitQuantities.getAlias()->fSize; - size_t quantitiesMallocSize = sizeof(UChar *) * gCategoriesCount; - gCategories = static_cast(uprv_malloc(quantitiesMallocSize)); - if (gCategories == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_memset(gCategories, 0, quantitiesMallocSize); - BytesTrieBuilder quantitiesBuilder(status); - CategoriesSink categoriesSink(gCategories, gCategoriesCount, quantitiesBuilder); - ures_getAllItemsWithFallback(unitsBundle.getAlias(), CATEGORY_TABLE_NAME, categoriesSink, status); - StringPiece resultQuantities = quantitiesBuilder.buildStringPiece(USTRINGTRIE_BUILD_FAST, status); - if (U_FAILURE(status)) { return; } - // Copy the result into the global constant pointer - size_t numBytesQuantities = resultQuantities.length(); - gSerializedUnitCategoriesTrie = static_cast(uprv_malloc(numBytesQuantities)); - if (gSerializedUnitCategoriesTrie == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_memcpy(gSerializedUnitCategoriesTrie, resultQuantities.data(), numBytesQuantities); - - // Build the BytesTrie that Parser needs for parsing unit identifiers. - - BytesTrieBuilder b(status); - if (U_FAILURE(status)) { return; } - - // Add SI and binary prefixes - for (const auto& unitPrefixInfo : gUnitPrefixStrings) { - b.add(unitPrefixInfo.string, unitPrefixInfo.value + kPrefixOffset, status); - } - if (U_FAILURE(status)) { return; } - - // Add syntax parts (compound, power prefixes) - b.add("-per-", COMPOUND_PART_PER, status); - b.add("-", COMPOUND_PART_TIMES, status); - b.add("-and-", COMPOUND_PART_AND, status); - b.add("per-", INITIAL_COMPOUND_PART_PER, status); - b.add("square-", POWER_PART_P2, status); - b.add("cubic-", POWER_PART_P3, status); - b.add("pow2-", POWER_PART_P2, status); - b.add("pow3-", POWER_PART_P3, status); - b.add("pow4-", POWER_PART_P4, status); - b.add("pow5-", POWER_PART_P5, status); - b.add("pow6-", POWER_PART_P6, status); - b.add("pow7-", POWER_PART_P7, status); - b.add("pow8-", POWER_PART_P8, status); - b.add("pow9-", POWER_PART_P9, status); - b.add("pow10-", POWER_PART_P10, status); - b.add("pow11-", POWER_PART_P11, status); - b.add("pow12-", POWER_PART_P12, status); - b.add("pow13-", POWER_PART_P13, status); - b.add("pow14-", POWER_PART_P14, status); - b.add("pow15-", POWER_PART_P15, status); - if (U_FAILURE(status)) { return; } - - // Add sanctioned simple units by offset: simple units all have entries in - // units/convertUnits resources. - LocalUResourceBundlePointer convertUnits( - ures_getByKey(unitsBundle.getAlias(), "convertUnits", nullptr, &status)); - if (U_FAILURE(status)) { return; } - - // Allocate enough space: with identifierSink below skipping kilogram, we're - // probably allocating one more than needed. - int32_t simpleUnitsCount = convertUnits.getAlias()->fSize; - int32_t arrayMallocSize = sizeof(char *) * simpleUnitsCount; - gSimpleUnits = static_cast(uprv_malloc(arrayMallocSize)); - if (gSimpleUnits == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_memset(gSimpleUnits, 0, arrayMallocSize); - arrayMallocSize = sizeof(int32_t) * simpleUnitsCount; - gSimpleUnitCategories = static_cast(uprv_malloc(arrayMallocSize)); - if (gSimpleUnitCategories == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_memset(gSimpleUnitCategories, 0, arrayMallocSize); - - // Populate gSimpleUnits and build the associated trie. - SimpleUnitIdentifiersSink identifierSink(resultQuantities, gSimpleUnits, gSimpleUnitCategories, - simpleUnitsCount, b, kSimpleUnitOffset); - ures_getAllItemsWithFallback(unitsBundle.getAlias(), "convertUnits", identifierSink, status); - - // Build the CharsTrie - // TODO: Use SLOW or FAST here? - StringPiece result = b.buildStringPiece(USTRINGTRIE_BUILD_FAST, status); - if (U_FAILURE(status)) { return; } - - // Copy the result into the global constant pointer - size_t numBytes = result.length(); - gSerializedUnitExtrasStemTrie = static_cast(uprv_malloc(numBytes)); - if (gSerializedUnitExtrasStemTrie == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_memcpy(gSerializedUnitExtrasStemTrie, result.data(), numBytes); -} - -class Token { -public: - Token(int32_t match) : fMatch(match) {} - - enum Type { - TYPE_UNDEFINED, - TYPE_PREFIX, - // Token type for "-per-", "-", and "-and-". - TYPE_COMPOUND_PART, - // Token type for "per-". - TYPE_INITIAL_COMPOUND_PART, - TYPE_POWER_PART, - TYPE_SIMPLE_UNIT, - }; - - // Calling getType() is invalid, resulting in an assertion failure, if Token - // value isn't positive. - Type getType() const { - U_ASSERT(fMatch > 0); - if (fMatch < kCompoundPartOffset) { - return TYPE_PREFIX; - } - if (fMatch < kInitialCompoundPartOffset) { - return TYPE_COMPOUND_PART; - } - if (fMatch < kPowerPartOffset) { - return TYPE_INITIAL_COMPOUND_PART; - } - if (fMatch < kSimpleUnitOffset) { - return TYPE_POWER_PART; - } - return TYPE_SIMPLE_UNIT; - } - - UMeasurePrefix getUnitPrefix() const { - U_ASSERT(getType() == TYPE_PREFIX); - return static_cast(fMatch - kPrefixOffset); - } - - // Valid only for tokens with type TYPE_COMPOUND_PART. - int32_t getMatch() const { - U_ASSERT(getType() == TYPE_COMPOUND_PART); - return fMatch; - } - - int32_t getInitialCompoundPart() const { - // Even if there is only one InitialCompoundPart value, we have this - // function for the simplicity of code consistency. - U_ASSERT(getType() == TYPE_INITIAL_COMPOUND_PART); - // Defensive: if this assert fails, code using this function also needs - // to change. - U_ASSERT(fMatch == INITIAL_COMPOUND_PART_PER); - return fMatch; - } - - int8_t getPower() const { - U_ASSERT(getType() == TYPE_POWER_PART); - return static_cast(fMatch - kPowerPartOffset); - } - - int32_t getSimpleUnitIndex() const { - U_ASSERT(getType() == TYPE_SIMPLE_UNIT); - return fMatch - kSimpleUnitOffset; - } - -private: - int32_t fMatch; -}; - -class Parser { -public: - /** - * Factory function for parsing the given identifier. - * - * @param source The identifier to parse. This function does not make a copy - * of source: the underlying string that source points at, must outlive the - * parser. - * @param status ICU error code. - */ - static Parser from(StringPiece source, UErrorCode& status) { - if (U_FAILURE(status)) { - return Parser(); - } - umtx_initOnce(gUnitExtrasInitOnce, &initUnitExtras, status); - if (U_FAILURE(status)) { - return Parser(); - } - return Parser(source); - } - - MeasureUnitImpl parse(UErrorCode& status) { - MeasureUnitImpl result; - - if (U_FAILURE(status)) { - return result; - } - if (fSource.empty()) { - // The dimenionless unit: nothing to parse. leave result as is. - return result; - } - - while (hasNext()) { - bool sawAnd = false; - - SingleUnitImpl singleUnit = nextSingleUnit(sawAnd, status); - if (U_FAILURE(status)) { - return result; - } - - bool added = result.appendSingleUnit(singleUnit, status); - if (U_FAILURE(status)) { - return result; - } - - if (sawAnd && !added) { - // Two similar units are not allowed in a mixed unit. - status = kUnitIdentifierSyntaxError; - return result; - } - - if (result.singleUnits.length() >= 2) { - // nextSingleUnit fails appropriately for "per" and "and" in the - // same identifier. It doesn't fail for other compound units - // (COMPOUND_PART_TIMES). Consequently we take care of that - // here. - UMeasureUnitComplexity complexity = - sawAnd ? UMEASURE_UNIT_MIXED : UMEASURE_UNIT_COMPOUND; - if (result.singleUnits.length() == 2) { - // After appending two singleUnits, the complexity will be `UMEASURE_UNIT_COMPOUND` - U_ASSERT(result.complexity == UMEASURE_UNIT_COMPOUND); - result.complexity = complexity; - } else if (result.complexity != complexity) { - // Can't have mixed compound units - status = kUnitIdentifierSyntaxError; - return result; - } - } - } - - return result; - } - -private: - // Tracks parser progress: the offset into fSource. - int32_t fIndex = 0; - - // Since we're not owning this memory, whatever is passed to the constructor - // should live longer than this Parser - and the parser shouldn't return any - // references to that string. - StringPiece fSource; - BytesTrie fTrie; - - // Set to true when we've seen a "-per-" or a "per-", after which all units - // are in the denominator. Until we find an "-and-", at which point the - // identifier is invalid pending TODO(CLDR-13701). - bool fAfterPer = false; - - Parser() : fSource(""), fTrie(u"") {} - - Parser(StringPiece source) - : fSource(source), fTrie(gSerializedUnitExtrasStemTrie) {} - - inline bool hasNext() const { - return fIndex < fSource.length(); - } - - // Returns the next Token parsed from fSource, advancing fIndex to the end - // of that token in fSource. In case of U_FAILURE(status), the token - // returned will cause an abort if getType() is called on it. - Token nextToken(UErrorCode& status) { - fTrie.reset(); - int32_t match = -1; - // Saves the position in the fSource string for the end of the most - // recent matching token. - int32_t previ = -1; - // Find the longest token that matches a value in the trie: - while (fIndex < fSource.length()) { - auto result = fTrie.next(fSource.data()[fIndex++]); - if (result == USTRINGTRIE_NO_MATCH) { - break; - } else if (result == USTRINGTRIE_NO_VALUE) { - continue; - } - U_ASSERT(USTRINGTRIE_HAS_VALUE(result)); - match = fTrie.getValue(); - previ = fIndex; - if (result == USTRINGTRIE_FINAL_VALUE) { - break; - } - U_ASSERT(result == USTRINGTRIE_INTERMEDIATE_VALUE); - // continue; - } - - if (match < 0) { - status = kUnitIdentifierSyntaxError; - } else { - fIndex = previ; - } - return Token(match); - } - - /** - * Returns the next "single unit" via result. - * - * If a "-per-" was parsed, the result will have appropriate negative - * dimensionality. - * - * Returns an error if we parse both compound units and "-and-", since mixed - * compound units are not yet supported - TODO(CLDR-13701). - * - * @param result Will be overwritten by the result, if status shows success. - * @param sawAnd If an "-and-" was parsed prior to finding the "single - * unit", sawAnd is set to true. If not, it is left as is. - * @param status ICU error code. - */ - SingleUnitImpl nextSingleUnit(bool &sawAnd, UErrorCode &status) { - SingleUnitImpl result; - if (U_FAILURE(status)) { - return result; - } - - // state: - // 0 = no tokens seen yet (will accept power, SI or binary prefix, or simple unit) - // 1 = power token seen (will not accept another power token) - // 2 = SI or binary prefix token seen (will not accept a power, or SI or binary prefix token) - int32_t state = 0; - - bool atStart = fIndex == 0; - Token token = nextToken(status); - if (U_FAILURE(status)) { - return result; - } - - if (atStart) { - // Identifiers optionally start with "per-". - if (token.getType() == Token::TYPE_INITIAL_COMPOUND_PART) { - U_ASSERT(token.getInitialCompoundPart() == INITIAL_COMPOUND_PART_PER); - fAfterPer = true; - result.dimensionality = -1; - - token = nextToken(status); - if (U_FAILURE(status)) { - return result; - } - } - } else { - // All other SingleUnit's are separated from previous SingleUnit's - // via a compound part: - if (token.getType() != Token::TYPE_COMPOUND_PART) { - status = kUnitIdentifierSyntaxError; - return result; - } - - switch (token.getMatch()) { - case COMPOUND_PART_PER: - if (sawAnd) { - // Mixed compound units not yet supported, - // TODO(CLDR-13701). - status = kUnitIdentifierSyntaxError; - return result; - } - fAfterPer = true; - result.dimensionality = -1; - break; - - case COMPOUND_PART_TIMES: - if (fAfterPer) { - result.dimensionality = -1; - } - break; - - case COMPOUND_PART_AND: - if (fAfterPer) { - // Can't start with "-and-", and mixed compound units - // not yet supported, TODO(CLDR-13701). - status = kUnitIdentifierSyntaxError; - return result; - } - sawAnd = true; - break; - } - - token = nextToken(status); - if (U_FAILURE(status)) { - return result; - } - } - - // Read tokens until we have a complete SingleUnit or we reach the end. - while (true) { - switch (token.getType()) { - case Token::TYPE_POWER_PART: - if (state > 0) { - status = kUnitIdentifierSyntaxError; - return result; - } - result.dimensionality *= token.getPower(); - state = 1; - break; - - case Token::TYPE_PREFIX: - if (state > 1) { - status = kUnitIdentifierSyntaxError; - return result; - } - result.unitPrefix = token.getUnitPrefix(); - state = 2; - break; - - case Token::TYPE_SIMPLE_UNIT: - result.index = token.getSimpleUnitIndex(); - return result; - - default: - status = kUnitIdentifierSyntaxError; - return result; - } - - if (!hasNext()) { - // We ran out of tokens before finding a complete single unit. - status = kUnitIdentifierSyntaxError; - return result; - } - token = nextToken(status); - if (U_FAILURE(status)) { - return result; - } - } - - return result; - } -}; - -// Sorting function wrapping SingleUnitImpl::compareTo for use with uprv_sortArray. -int32_t U_CALLCONV -compareSingleUnits(const void* /*context*/, const void* left, const void* right) { - auto realLeft = static_cast(left); - auto realRight = static_cast(right); - return (*realLeft)->compareTo(**realRight); -} - -// Returns an index into the gCategories array, for the "unitQuantity" (aka -// "type" or "category") associated with the given base unit identifier. Returns -// -1 on failure, together with U_UNSUPPORTED_ERROR. -int32_t getUnitCategoryIndex(BytesTrie &trie, StringPiece baseUnitIdentifier, UErrorCode &status) { - UStringTrieResult result = trie.reset().next(baseUnitIdentifier.data(), baseUnitIdentifier.length()); - if (!USTRINGTRIE_HAS_VALUE(result)) { - status = U_UNSUPPORTED_ERROR; - return -1; - } - - return trie.getValue(); -} - -} // namespace - -U_CAPI int32_t U_EXPORT2 -umeas_getPrefixPower(UMeasurePrefix unitPrefix) { - if (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_BIN && - unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_BIN) { - return unitPrefix - UMEASURE_PREFIX_INTERNAL_ONE_BIN; - } - U_ASSERT(unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI && - unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI); - return unitPrefix - UMEASURE_PREFIX_ONE; -} - -U_CAPI int32_t U_EXPORT2 -umeas_getPrefixBase(UMeasurePrefix unitPrefix) { - if (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_BIN && - unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_BIN) { - return 1024; - } - U_ASSERT(unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI && - unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI); - return 10; -} - -CharString U_I18N_API getUnitQuantity(const MeasureUnitImpl &baseMeasureUnitImpl, UErrorCode &status) { - CharString result; - MeasureUnitImpl baseUnitImpl = baseMeasureUnitImpl.copy(status); - UErrorCode localStatus = U_ZERO_ERROR; - umtx_initOnce(gUnitExtrasInitOnce, &initUnitExtras, status); - if (U_FAILURE(status)) { - return result; - } - BytesTrie trie(gSerializedUnitCategoriesTrie); - - baseUnitImpl.serialize(status); - StringPiece identifier = baseUnitImpl.identifier.data(); - int32_t idx = getUnitCategoryIndex(trie, identifier, localStatus); - if (U_FAILURE(status)) { - return result; - } - - // In case the base unit identifier did not match any entry. - if (U_FAILURE(localStatus)) { - localStatus = U_ZERO_ERROR; - baseUnitImpl.takeReciprocal(status); - baseUnitImpl.serialize(status); - identifier.set(baseUnitImpl.identifier.data()); - idx = getUnitCategoryIndex(trie, identifier, localStatus); - - if (U_FAILURE(status)) { - return result; - } - } - - // In case the reciprocal of the base unit identifier did not match any entry. - MeasureUnitImpl simplifiedUnit = baseMeasureUnitImpl.copyAndSimplify(status); - if (U_FAILURE(status)) { - return result; - } - if (U_FAILURE(localStatus)) { - localStatus = U_ZERO_ERROR; - simplifiedUnit.serialize(status); - identifier.set(simplifiedUnit.identifier.data()); - idx = getUnitCategoryIndex(trie, identifier, localStatus); - - if (U_FAILURE(status)) { - return result; - } - } - - // In case the simplified base unit identifier did not match any entry. - if (U_FAILURE(localStatus)) { - localStatus = U_ZERO_ERROR; - simplifiedUnit.takeReciprocal(status); - simplifiedUnit.serialize(status); - identifier.set(simplifiedUnit.identifier.data()); - idx = getUnitCategoryIndex(trie, identifier, localStatus); - - if (U_FAILURE(status)) { - return result; - } - } - - // If there is no match at all, throw an exception. - if (U_FAILURE(localStatus)) { - status = U_INVALID_FORMAT_ERROR; - return result; - } - - if (idx < 0 || idx >= gCategoriesCount) { - status = U_INVALID_FORMAT_ERROR; - return result; - } - - result.appendInvariantChars(gCategories[idx], u_strlen(gCategories[idx]), status); - return result; -} - -// In ICU4J, this is MeasureUnit.getSingleUnitImpl(). -SingleUnitImpl SingleUnitImpl::forMeasureUnit(const MeasureUnit& measureUnit, UErrorCode& status) { - MeasureUnitImpl temp; - const MeasureUnitImpl& impl = MeasureUnitImpl::forMeasureUnit(measureUnit, temp, status); - if (U_FAILURE(status)) { - return {}; - } - if (impl.singleUnits.length() == 0) { - return {}; - } - if (impl.singleUnits.length() == 1) { - return *impl.singleUnits[0]; - } - status = U_ILLEGAL_ARGUMENT_ERROR; - return {}; -} - -MeasureUnit SingleUnitImpl::build(UErrorCode& status) const { - MeasureUnitImpl temp; - temp.appendSingleUnit(*this, status); - // TODO(icu-units#28): the MeasureUnitImpl::build() method uses - // findBySubtype, which is relatively slow. - // - At the time of loading the simple unit IDs, we could also save a - // mapping to the builtin MeasureUnit type and subtype they correspond to. - // - This method could then check dimensionality and index, and if both are - // 1, directly return MeasureUnit instances very quickly. - return std::move(temp).build(status); -} - -const char *SingleUnitImpl::getSimpleUnitID() const { - return gSimpleUnits[index]; -} - -void SingleUnitImpl::appendNeutralIdentifier(CharString &result, UErrorCode &status) const { - int32_t absPower = std::abs(this->dimensionality); - - U_ASSERT(absPower > 0); // "this function does not support the dimensionless single units"; - - if (absPower == 1) { - // no-op - } else if (absPower == 2) { - result.append(StringPiece("square-"), status); - } else if (absPower == 3) { - result.append(StringPiece("cubic-"), status); - } else if (absPower <= 15) { - result.append(StringPiece("pow"), status); - result.appendNumber(absPower, status); - result.append(StringPiece("-"), status); - } else { - status = U_ILLEGAL_ARGUMENT_ERROR; // Unit Identifier Syntax Error - return; - } - - if (U_FAILURE(status)) { - return; - } - - if (this->unitPrefix != UMEASURE_PREFIX_ONE) { - bool found = false; - for (const auto &unitPrefixInfo : gUnitPrefixStrings) { - // TODO: consider using binary search? If we do this, add a unit - // test to ensure gUnitPrefixStrings is sorted? - if (unitPrefixInfo.value == this->unitPrefix) { - result.append(unitPrefixInfo.string, status); - found = true; - break; - } - } - if (!found) { - status = U_UNSUPPORTED_ERROR; - return; - } - } - - result.append(StringPiece(this->getSimpleUnitID()), status); -} - -int32_t SingleUnitImpl::getUnitCategoryIndex() const { - return gSimpleUnitCategories[index]; -} - -MeasureUnitImpl::MeasureUnitImpl(const SingleUnitImpl &singleUnit, UErrorCode &status) { - this->appendSingleUnit(singleUnit, status); -} - -MeasureUnitImpl MeasureUnitImpl::forIdentifier(StringPiece identifier, UErrorCode& status) { - return Parser::from(identifier, status).parse(status); -} - -const MeasureUnitImpl& MeasureUnitImpl::forMeasureUnit( - const MeasureUnit& measureUnit, MeasureUnitImpl& memory, UErrorCode& status) { - if (measureUnit.fImpl) { - return *measureUnit.fImpl; - } else { - memory = Parser::from(measureUnit.getIdentifier(), status).parse(status); - return memory; - } -} - -MeasureUnitImpl MeasureUnitImpl::forMeasureUnitMaybeCopy( - const MeasureUnit& measureUnit, UErrorCode& status) { - if (measureUnit.fImpl) { - return measureUnit.fImpl->copy(status); - } else { - return Parser::from(measureUnit.getIdentifier(), status).parse(status); - } -} - -void MeasureUnitImpl::takeReciprocal(UErrorCode& /*status*/) { - identifier.clear(); - for (int32_t i = 0; i < singleUnits.length(); i++) { - singleUnits[i]->dimensionality *= -1; - } -} - -MeasureUnitImpl MeasureUnitImpl::copyAndSimplify(UErrorCode &status) const { - MeasureUnitImpl result; - for (int32_t i = 0; i < singleUnits.length(); i++) { - const SingleUnitImpl &singleUnit = *this->singleUnits[i]; - - // The following `for` loop will cause time complexity to be O(n^2). - // However, n is very small (number of units, generally, at maximum equal to 10) - bool unitExist = false; - for (int32_t j = 0; j < result.singleUnits.length(); j++) { - if (uprv_strcmp(result.singleUnits[j]->getSimpleUnitID(), singleUnit.getSimpleUnitID()) == - 0 && - result.singleUnits[j]->unitPrefix == singleUnit.unitPrefix) { - unitExist = true; - result.singleUnits[j]->dimensionality = - result.singleUnits[j]->dimensionality + singleUnit.dimensionality; - break; - } - } - - if (!unitExist) { - result.appendSingleUnit(singleUnit, status); - } - } - - return result; -} - -bool MeasureUnitImpl::appendSingleUnit(const SingleUnitImpl &singleUnit, UErrorCode &status) { - identifier.clear(); - - if (singleUnit.isDimensionless()) { - // Do not append dimensionless units. - return false; - } - - // Find a similar unit that already exists, to attempt to coalesce - SingleUnitImpl *oldUnit = nullptr; - for (int32_t i = 0; i < this->singleUnits.length(); i++) { - auto *candidate = this->singleUnits[i]; - if (candidate->isCompatibleWith(singleUnit)) { - oldUnit = candidate; - } - } - - if (oldUnit) { - // Both dimensionalities will be positive, or both will be negative, by - // virtue of isCompatibleWith(). - oldUnit->dimensionality += singleUnit.dimensionality; - - return false; - } - - // Add a copy of singleUnit - // NOTE: MaybeStackVector::emplaceBackAndCheckErrorCode creates new copy of singleUnit. - this->singleUnits.emplaceBackAndCheckErrorCode(status, singleUnit); - if (U_FAILURE(status)) { - return false; - } - - // If the MeasureUnitImpl is `UMEASURE_UNIT_SINGLE` and after the appending a unit, the `singleUnits` - // contains more than one. thus means the complexity should be `UMEASURE_UNIT_COMPOUND` - if (this->singleUnits.length() > 1 && - this->complexity == UMeasureUnitComplexity::UMEASURE_UNIT_SINGLE) { - this->complexity = UMeasureUnitComplexity::UMEASURE_UNIT_COMPOUND; - } - - return true; -} - -MaybeStackVector -MeasureUnitImpl::extractIndividualUnitsWithIndices(UErrorCode &status) const { - MaybeStackVector result; - - if (this->complexity != UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) { - result.emplaceBackAndCheckErrorCode(status, 0, *this, status); - return result; - } - - for (int32_t i = 0; i < singleUnits.length(); ++i) { - result.emplaceBackAndCheckErrorCode(status, i, *singleUnits[i], status); - if (U_FAILURE(status)) { - return result; - } - } - - return result; -} - -/** - * Normalize a MeasureUnitImpl and generate the identifier string in place. - */ -void MeasureUnitImpl::serialize(UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - - if (this->singleUnits.length() == 0) { - // Dimensionless, constructed by the default constructor. - return; - } - - if (this->complexity == UMEASURE_UNIT_COMPOUND) { - // Note: don't sort a MIXED unit - uprv_sortArray(this->singleUnits.getAlias(), this->singleUnits.length(), - sizeof(this->singleUnits[0]), compareSingleUnits, nullptr, false, &status); - if (U_FAILURE(status)) { - return; - } - } - - CharString result; - bool beforePer = true; - bool firstTimeNegativeDimension = false; - for (int32_t i = 0; i < this->singleUnits.length(); i++) { - if (beforePer && (*this->singleUnits[i]).dimensionality < 0) { - beforePer = false; - firstTimeNegativeDimension = true; - } else if ((*this->singleUnits[i]).dimensionality < 0) { - firstTimeNegativeDimension = false; - } - - if (U_FAILURE(status)) { - return; - } - - if (this->complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) { - if (result.length() != 0) { - result.append(StringPiece("-and-"), status); - } - } else { - if (firstTimeNegativeDimension) { - if (result.length() == 0) { - result.append(StringPiece("per-"), status); - } else { - result.append(StringPiece("-per-"), status); - } - } else { - if (result.length() != 0) { - result.append(StringPiece("-"), status); - } - } - } - - this->singleUnits[i]->appendNeutralIdentifier(result, status); - } - - this->identifier = CharString(result, status); -} - -MeasureUnit MeasureUnitImpl::build(UErrorCode& status) && { - this->serialize(status); - return MeasureUnit(std::move(*this)); -} - -MeasureUnit MeasureUnit::forIdentifier(StringPiece identifier, UErrorCode& status) { - return Parser::from(identifier, status).parse(status).build(status); -} - -UMeasureUnitComplexity MeasureUnit::getComplexity(UErrorCode& status) const { - MeasureUnitImpl temp; - return MeasureUnitImpl::forMeasureUnit(*this, temp, status).complexity; -} - -UMeasurePrefix MeasureUnit::getPrefix(UErrorCode& status) const { - return SingleUnitImpl::forMeasureUnit(*this, status).unitPrefix; -} - -MeasureUnit MeasureUnit::withPrefix(UMeasurePrefix prefix, UErrorCode& status) const { - SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status); - singleUnit.unitPrefix = prefix; - return singleUnit.build(status); -} - -int32_t MeasureUnit::getDimensionality(UErrorCode& status) const { - SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status); - if (U_FAILURE(status)) { return 0; } - if (singleUnit.isDimensionless()) { - return 0; - } - return singleUnit.dimensionality; -} - -MeasureUnit MeasureUnit::withDimensionality(int32_t dimensionality, UErrorCode& status) const { - SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status); - singleUnit.dimensionality = dimensionality; - return singleUnit.build(status); -} - -MeasureUnit MeasureUnit::reciprocal(UErrorCode& status) const { - MeasureUnitImpl impl = MeasureUnitImpl::forMeasureUnitMaybeCopy(*this, status); - impl.takeReciprocal(status); - return std::move(impl).build(status); -} - -MeasureUnit MeasureUnit::product(const MeasureUnit& other, UErrorCode& status) const { - MeasureUnitImpl impl = MeasureUnitImpl::forMeasureUnitMaybeCopy(*this, status); - MeasureUnitImpl temp; - const MeasureUnitImpl& otherImpl = MeasureUnitImpl::forMeasureUnit(other, temp, status); - if (impl.complexity == UMEASURE_UNIT_MIXED || otherImpl.complexity == UMEASURE_UNIT_MIXED) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return {}; - } - for (int32_t i = 0; i < otherImpl.singleUnits.length(); i++) { - impl.appendSingleUnit(*otherImpl.singleUnits[i], status); - } - if (impl.singleUnits.length() > 1) { - impl.complexity = UMEASURE_UNIT_COMPOUND; - } - return std::move(impl).build(status); -} - -LocalArray MeasureUnit::splitToSingleUnitsImpl(int32_t& outCount, UErrorCode& status) const { - MeasureUnitImpl temp; - const MeasureUnitImpl& impl = MeasureUnitImpl::forMeasureUnit(*this, temp, status); - outCount = impl.singleUnits.length(); - MeasureUnit* arr = new MeasureUnit[outCount]; - if (arr == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return LocalArray(); - } - for (int32_t i = 0; i < outCount; i++) { - arr[i] = impl.singleUnits[i]->build(status); - } - return LocalArray(arr, status); -} - - -U_NAMESPACE_END - -#endif /* !UNCONFIG_NO_FORMATTING */ +// © 2020 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// Extra functions for MeasureUnit not needed for all clients. +// Separate .o file so that it can be removed for modularity. + +#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 "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "measunit_impl.h" +#include "resource.h" +#include "uarrsort.h" +#include "uassert.h" +#include "ucln_in.h" +#include "umutex.h" +#include "unicode/bytestrie.h" +#include "unicode/bytestriebuilder.h" +#include "unicode/localpointer.h" +#include "unicode/stringpiece.h" +#include "unicode/stringtriebuilder.h" +#include "unicode/ures.h" +#include "unicode/ustringtrie.h" +#include "uresimp.h" +#include "util.h" +#include + +U_NAMESPACE_BEGIN + + +namespace { + +// TODO: Propose a new error code for this? +constexpr UErrorCode kUnitIdentifierSyntaxError = U_ILLEGAL_ARGUMENT_ERROR; + +// Trie value offset for SI or binary prefixes. This is big enough to ensure we only +// insert positive integers into the trie. +constexpr int32_t kPrefixOffset = 64; +static_assert(kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MIN_BIN > 0, + "kPrefixOffset is too small for minimum UMeasurePrefix value"); +static_assert(kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MIN_SI > 0, + "kPrefixOffset is too small for minimum UMeasurePrefix value"); + +// Trie value offset for compound parts, e.g. "-per-", "-", "-and-". +constexpr int32_t kCompoundPartOffset = 128; +static_assert(kCompoundPartOffset > kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MAX_BIN, + "Ambiguous token values: prefix tokens are overlapping with CompoundPart tokens"); +static_assert(kCompoundPartOffset > kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MAX_SI, + "Ambiguous token values: prefix tokens are overlapping with CompoundPart tokens"); + +enum CompoundPart { + // Represents "-per-" + COMPOUND_PART_PER = kCompoundPartOffset, + // Represents "-" + COMPOUND_PART_TIMES, + // Represents "-and-" + COMPOUND_PART_AND, +}; + +// Trie value offset for "per-". +constexpr int32_t kInitialCompoundPartOffset = 192; + +enum InitialCompoundPart { + // Represents "per-", the only compound part that can appear at the start of + // an identifier. + INITIAL_COMPOUND_PART_PER = kInitialCompoundPartOffset, +}; + +// Trie value offset for powers like "square-", "cubic-", "pow2-" etc. +constexpr int32_t kPowerPartOffset = 256; + +enum PowerPart { + POWER_PART_P2 = kPowerPartOffset + 2, + POWER_PART_P3, + POWER_PART_P4, + POWER_PART_P5, + POWER_PART_P6, + POWER_PART_P7, + POWER_PART_P8, + POWER_PART_P9, + POWER_PART_P10, + POWER_PART_P11, + POWER_PART_P12, + POWER_PART_P13, + POWER_PART_P14, + POWER_PART_P15, +}; + +// Trie value offset for simple units, e.g. "gram", "nautical-mile", +// "fluid-ounce-imperial". +constexpr int32_t kSimpleUnitOffset = 512; + +const struct UnitPrefixStrings { + const char* const string; + UMeasurePrefix value; +} gUnitPrefixStrings[] = { + // SI prefixes + { "yotta", UMEASURE_PREFIX_YOTTA }, + { "zetta", UMEASURE_PREFIX_ZETTA }, + { "exa", UMEASURE_PREFIX_EXA }, + { "peta", UMEASURE_PREFIX_PETA }, + { "tera", UMEASURE_PREFIX_TERA }, + { "giga", UMEASURE_PREFIX_GIGA }, + { "mega", UMEASURE_PREFIX_MEGA }, + { "kilo", UMEASURE_PREFIX_KILO }, + { "hecto", UMEASURE_PREFIX_HECTO }, + { "deka", UMEASURE_PREFIX_DEKA }, + { "deci", UMEASURE_PREFIX_DECI }, + { "centi", UMEASURE_PREFIX_CENTI }, + { "milli", UMEASURE_PREFIX_MILLI }, + { "micro", UMEASURE_PREFIX_MICRO }, + { "nano", UMEASURE_PREFIX_NANO }, + { "pico", UMEASURE_PREFIX_PICO }, + { "femto", UMEASURE_PREFIX_FEMTO }, + { "atto", UMEASURE_PREFIX_ATTO }, + { "zepto", UMEASURE_PREFIX_ZEPTO }, + { "yocto", UMEASURE_PREFIX_YOCTO }, + // Binary prefixes + { "yobi", UMEASURE_PREFIX_YOBI }, + { "zebi", UMEASURE_PREFIX_ZEBI }, + { "exbi", UMEASURE_PREFIX_EXBI }, + { "pebi", UMEASURE_PREFIX_PEBI }, + { "tebi", UMEASURE_PREFIX_TEBI }, + { "gibi", UMEASURE_PREFIX_GIBI }, + { "mebi", UMEASURE_PREFIX_MEBI }, + { "kibi", UMEASURE_PREFIX_KIBI }, +}; + +/** + * A ResourceSink that collects simple unit identifiers from the keys of the + * convertUnits table into an array, and adds these values to a TrieBuilder, + * with associated values being their index into this array plus a specified + * offset. + * + * Example code: + * + * UErrorCode status = U_ZERO_ERROR; + * BytesTrieBuilder b(status); + * int32_t ARR_SIZE = 200; + * const char *unitIdentifiers[ARR_SIZE]; + * int32_t *unitCategories[ARR_SIZE]; + * SimpleUnitIdentifiersSink identifierSink(gSerializedUnitCategoriesTrie, unitIdentifiers, + * unitCategories, ARR_SIZE, b, kTrieValueOffset); + * LocalUResourceBundlePointer unitsBundle(ures_openDirect(nullptr, "units", &status)); + * ures_getAllItemsWithFallback(unitsBundle.getAlias(), "convertUnits", identifierSink, status); + */ +class SimpleUnitIdentifiersSink : public icu::ResourceSink { + public: + /** + * Constructor. + * @param quantitiesTrieData The data for constructing a quantitiesTrie, + * which maps from a simple unit identifier to an index into the + * gCategories array. + * @param out Array of char* to which pointers to the simple unit + * identifiers will be saved. (Does not take ownership.) + * @param outCategories Array of int32_t to which category indexes will be + * saved: this corresponds to simple unit IDs saved to `out`, mapping + * from the ID to the value produced by the quantitiesTrie (which is an + * index into the gCategories array). + * @param outSize The size of `out` and `outCategories`. + * @param trieBuilder The trie builder to which the simple unit identifier + * should be added. The trie builder must outlive this resource sink. + * @param trieValueOffset This is added to the index of the identifier in + * the `out` array, before adding to `trieBuilder` as the value + * associated with the identifier. + */ + explicit SimpleUnitIdentifiersSink(StringPiece quantitiesTrieData, const char **out, + int32_t *outCategories, int32_t outSize, + BytesTrieBuilder &trieBuilder, int32_t trieValueOffset) + : outArray(out), outCategories(outCategories), outSize(outSize), trieBuilder(trieBuilder), + trieValueOffset(trieValueOffset), quantitiesTrieData(quantitiesTrieData), outIndex(0) {} + + /** + * Adds the table keys found in value to the output vector. + * @param key The key of the resource passed to `value`: the second + * parameter of the ures_getAllItemsWithFallback() call. + * @param value Should be a ResourceTable value, if + * ures_getAllItemsWithFallback() was called correctly for this sink. + * @param noFallback Ignored. + * @param status The standard ICU error code output parameter. + */ + void put(const char * /*key*/, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override { + ResourceTable table = value.getTable(status); + if (U_FAILURE(status)) return; + + if (outIndex + table.getSize() > outSize) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + return; + } + + BytesTrie quantitiesTrie(quantitiesTrieData.data()); + + // Collect keys from the table resource. + const char *simpleUnitID; + for (int32_t i = 0; table.getKeyAndValue(i, simpleUnitID, value); ++i) { + U_ASSERT(i < table.getSize()); + U_ASSERT(outIndex < outSize); + if (uprv_strcmp(simpleUnitID, "kilogram") == 0) { + // For parsing, we use "gram", the prefixless metric mass unit. We + // thus ignore the SI Base Unit of Mass: it exists due to being the + // mass conversion target unit, but not needed for MeasureUnit + // parsing. + continue; + } + outArray[outIndex] = simpleUnitID; + trieBuilder.add(simpleUnitID, trieValueOffset + outIndex, status); + + // Find the base target unit for this simple unit + ResourceTable table = value.getTable(status); + if (U_FAILURE(status)) { return; } + if (!table.findValue("target", value)) { + status = U_INVALID_FORMAT_ERROR; + break; + } + int32_t len; + const char16_t* uTarget = value.getString(len, status); + CharString target; + target.appendInvariantChars(uTarget, len, status); + if (U_FAILURE(status)) { return; } + quantitiesTrie.reset(); + UStringTrieResult result = quantitiesTrie.next(target.data(), target.length()); + if (!USTRINGTRIE_HAS_VALUE(result)) { + status = U_INVALID_FORMAT_ERROR; + break; + } + outCategories[outIndex] = quantitiesTrie.getValue(); + + outIndex++; + } + } + + private: + const char **outArray; + int32_t *outCategories; + int32_t outSize; + BytesTrieBuilder &trieBuilder; + int32_t trieValueOffset; + + StringPiece quantitiesTrieData; + + int32_t outIndex; +}; + +/** + * A ResourceSink that collects information from `unitQuantities` in the `units` + * resource to provide key->value lookups from base unit to category, as well as + * preserving ordering information for these categories. See `units.txt`. + * + * For example: "kilogram" -> "mass", "meter-per-second" -> "speed". + * + * In C++ unitQuantity values are collected in order into a char16_t* array, while + * unitQuantity keys are added added to a TrieBuilder, with associated values + * being the index into the aforementioned char16_t* array. + */ +class CategoriesSink : public icu::ResourceSink { + public: + /** + * Constructor. + * @param out Array of char16_t* to which unitQuantity values will be saved. + * The pointers returned not owned: they point directly at the resource + * strings in static memory. + * @param outSize The size of the `out` array. + * @param trieBuilder The trie builder to which the keys (base units) of + * each unitQuantity will be added, each with value being the offset + * into `out`. + */ + explicit CategoriesSink(const char16_t **out, int32_t &outSize, BytesTrieBuilder &trieBuilder) + : outQuantitiesArray(out), outSize(outSize), trieBuilder(trieBuilder), outIndex(0) {} + + void put(const char * /*key*/, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override { + ResourceArray array = value.getArray(status); + if (U_FAILURE(status)) { + return; + } + + if (outIndex + array.getSize() > outSize) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + return; + } + + for (int32_t i = 0; array.getValue(i, value); ++i) { + U_ASSERT(outIndex < outSize); + ResourceTable table = value.getTable(status); + if (U_FAILURE(status)) { + return; + } + if (table.getSize() != 1) { + status = U_INVALID_FORMAT_ERROR; + return; + } + const char *key; + table.getKeyAndValue(0, key, value); + int32_t uTmpLen; + outQuantitiesArray[outIndex] = value.getString(uTmpLen, status); + trieBuilder.add(key, outIndex, status); + outIndex++; + } + } + + private: + const char16_t **outQuantitiesArray; + int32_t &outSize; + BytesTrieBuilder &trieBuilder; + + int32_t outIndex; +}; + +icu::UInitOnce gUnitExtrasInitOnce {}; + +// Array of simple unit IDs. +// +// The array memory itself is owned by this pointer, but the individual char* in +// that array point at static memory. (Note that these char* are also returned +// by SingleUnitImpl::getSimpleUnitID().) +const char **gSimpleUnits = nullptr; + +// Maps from the value associated with each simple unit ID to an index into the +// gCategories array. +int32_t *gSimpleUnitCategories = nullptr; + +char *gSerializedUnitExtrasStemTrie = nullptr; + +// Array of char16_t* pointing at the unit categories (aka "quantities", aka +// "types"), as found in the `unitQuantities` resource. The array memory itself +// is owned by this pointer, but the individual char16_t* in that array point at +// static memory. +const char16_t **gCategories = nullptr; +// Number of items in `gCategories`. +int32_t gCategoriesCount = 0; +// Serialized BytesTrie for mapping from base units to indices into gCategories. +char *gSerializedUnitCategoriesTrie = nullptr; + +UBool U_CALLCONV cleanupUnitExtras() { + uprv_free(gSerializedUnitCategoriesTrie); + gSerializedUnitCategoriesTrie = nullptr; + uprv_free(gCategories); + gCategories = nullptr; + uprv_free(gSerializedUnitExtrasStemTrie); + gSerializedUnitExtrasStemTrie = nullptr; + uprv_free(gSimpleUnitCategories); + gSimpleUnitCategories = nullptr; + uprv_free(gSimpleUnits); + gSimpleUnits = nullptr; + gUnitExtrasInitOnce.reset(); + return true; +} + +void U_CALLCONV initUnitExtras(UErrorCode& status) { + ucln_i18n_registerCleanup(UCLN_I18N_UNIT_EXTRAS, cleanupUnitExtras); + LocalUResourceBundlePointer unitsBundle(ures_openDirect(nullptr, "units", &status)); + + // Collect unitQuantities information into gSerializedUnitCategoriesTrie and gCategories. + const char *CATEGORY_TABLE_NAME = "unitQuantities"; + LocalUResourceBundlePointer unitQuantities( + ures_getByKey(unitsBundle.getAlias(), CATEGORY_TABLE_NAME, nullptr, &status)); + if (U_FAILURE(status)) { return; } + gCategoriesCount = unitQuantities.getAlias()->fSize; + size_t quantitiesMallocSize = sizeof(char16_t *) * gCategoriesCount; + gCategories = static_cast(uprv_malloc(quantitiesMallocSize)); + if (gCategories == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_memset(gCategories, 0, quantitiesMallocSize); + BytesTrieBuilder quantitiesBuilder(status); + CategoriesSink categoriesSink(gCategories, gCategoriesCount, quantitiesBuilder); + ures_getAllItemsWithFallback(unitsBundle.getAlias(), CATEGORY_TABLE_NAME, categoriesSink, status); + StringPiece resultQuantities = quantitiesBuilder.buildStringPiece(USTRINGTRIE_BUILD_FAST, status); + if (U_FAILURE(status)) { return; } + // Copy the result into the global constant pointer + size_t numBytesQuantities = resultQuantities.length(); + gSerializedUnitCategoriesTrie = static_cast(uprv_malloc(numBytesQuantities)); + if (gSerializedUnitCategoriesTrie == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_memcpy(gSerializedUnitCategoriesTrie, resultQuantities.data(), numBytesQuantities); + + // Build the BytesTrie that Parser needs for parsing unit identifiers. + + BytesTrieBuilder b(status); + if (U_FAILURE(status)) { return; } + + // Add SI and binary prefixes + for (const auto& unitPrefixInfo : gUnitPrefixStrings) { + b.add(unitPrefixInfo.string, unitPrefixInfo.value + kPrefixOffset, status); + } + if (U_FAILURE(status)) { return; } + + // Add syntax parts (compound, power prefixes) + b.add("-per-", COMPOUND_PART_PER, status); + b.add("-", COMPOUND_PART_TIMES, status); + b.add("-and-", COMPOUND_PART_AND, status); + b.add("per-", INITIAL_COMPOUND_PART_PER, status); + b.add("square-", POWER_PART_P2, status); + b.add("cubic-", POWER_PART_P3, status); + b.add("pow2-", POWER_PART_P2, status); + b.add("pow3-", POWER_PART_P3, status); + b.add("pow4-", POWER_PART_P4, status); + b.add("pow5-", POWER_PART_P5, status); + b.add("pow6-", POWER_PART_P6, status); + b.add("pow7-", POWER_PART_P7, status); + b.add("pow8-", POWER_PART_P8, status); + b.add("pow9-", POWER_PART_P9, status); + b.add("pow10-", POWER_PART_P10, status); + b.add("pow11-", POWER_PART_P11, status); + b.add("pow12-", POWER_PART_P12, status); + b.add("pow13-", POWER_PART_P13, status); + b.add("pow14-", POWER_PART_P14, status); + b.add("pow15-", POWER_PART_P15, status); + if (U_FAILURE(status)) { return; } + + // Add sanctioned simple units by offset: simple units all have entries in + // units/convertUnits resources. + LocalUResourceBundlePointer convertUnits( + ures_getByKey(unitsBundle.getAlias(), "convertUnits", nullptr, &status)); + if (U_FAILURE(status)) { return; } + + // Allocate enough space: with identifierSink below skipping kilogram, we're + // probably allocating one more than needed. + int32_t simpleUnitsCount = convertUnits.getAlias()->fSize; + int32_t arrayMallocSize = sizeof(char *) * simpleUnitsCount; + gSimpleUnits = static_cast(uprv_malloc(arrayMallocSize)); + if (gSimpleUnits == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_memset(gSimpleUnits, 0, arrayMallocSize); + arrayMallocSize = sizeof(int32_t) * simpleUnitsCount; + gSimpleUnitCategories = static_cast(uprv_malloc(arrayMallocSize)); + if (gSimpleUnitCategories == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_memset(gSimpleUnitCategories, 0, arrayMallocSize); + + // Populate gSimpleUnits and build the associated trie. + SimpleUnitIdentifiersSink identifierSink(resultQuantities, gSimpleUnits, gSimpleUnitCategories, + simpleUnitsCount, b, kSimpleUnitOffset); + ures_getAllItemsWithFallback(unitsBundle.getAlias(), "convertUnits", identifierSink, status); + + // Build the CharsTrie + // TODO: Use SLOW or FAST here? + StringPiece result = b.buildStringPiece(USTRINGTRIE_BUILD_FAST, status); + if (U_FAILURE(status)) { return; } + + // Copy the result into the global constant pointer + size_t numBytes = result.length(); + gSerializedUnitExtrasStemTrie = static_cast(uprv_malloc(numBytes)); + if (gSerializedUnitExtrasStemTrie == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_memcpy(gSerializedUnitExtrasStemTrie, result.data(), numBytes); +} + +class Token { +public: + Token(int32_t match) : fMatch(match) {} + + enum Type { + TYPE_UNDEFINED, + TYPE_PREFIX, + // Token type for "-per-", "-", and "-and-". + TYPE_COMPOUND_PART, + // Token type for "per-". + TYPE_INITIAL_COMPOUND_PART, + TYPE_POWER_PART, + TYPE_SIMPLE_UNIT, + }; + + // Calling getType() is invalid, resulting in an assertion failure, if Token + // value isn't positive. + Type getType() const { + U_ASSERT(fMatch > 0); + if (fMatch < kCompoundPartOffset) { + return TYPE_PREFIX; + } + if (fMatch < kInitialCompoundPartOffset) { + return TYPE_COMPOUND_PART; + } + if (fMatch < kPowerPartOffset) { + return TYPE_INITIAL_COMPOUND_PART; + } + if (fMatch < kSimpleUnitOffset) { + return TYPE_POWER_PART; + } + return TYPE_SIMPLE_UNIT; + } + + UMeasurePrefix getUnitPrefix() const { + U_ASSERT(getType() == TYPE_PREFIX); + return static_cast(fMatch - kPrefixOffset); + } + + // Valid only for tokens with type TYPE_COMPOUND_PART. + int32_t getMatch() const { + U_ASSERT(getType() == TYPE_COMPOUND_PART); + return fMatch; + } + + int32_t getInitialCompoundPart() const { + // Even if there is only one InitialCompoundPart value, we have this + // function for the simplicity of code consistency. + U_ASSERT(getType() == TYPE_INITIAL_COMPOUND_PART); + // Defensive: if this assert fails, code using this function also needs + // to change. + U_ASSERT(fMatch == INITIAL_COMPOUND_PART_PER); + return fMatch; + } + + int8_t getPower() const { + U_ASSERT(getType() == TYPE_POWER_PART); + return static_cast(fMatch - kPowerPartOffset); + } + + int32_t getSimpleUnitIndex() const { + U_ASSERT(getType() == TYPE_SIMPLE_UNIT); + return fMatch - kSimpleUnitOffset; + } + +private: + int32_t fMatch; +}; + +class Parser { +public: + /** + * Factory function for parsing the given identifier. + * + * @param source The identifier to parse. This function does not make a copy + * of source: the underlying string that source points at, must outlive the + * parser. + * @param status ICU error code. + */ + static Parser from(StringPiece source, UErrorCode& status) { + if (U_FAILURE(status)) { + return Parser(); + } + umtx_initOnce(gUnitExtrasInitOnce, &initUnitExtras, status); + if (U_FAILURE(status)) { + return Parser(); + } + return Parser(source); + } + + MeasureUnitImpl parse(UErrorCode& status) { + MeasureUnitImpl result; + + if (U_FAILURE(status)) { + return result; + } + if (fSource.empty()) { + // The dimenionless unit: nothing to parse. leave result as is. + return result; + } + + while (hasNext()) { + bool sawAnd = false; + + SingleUnitImpl singleUnit = nextSingleUnit(sawAnd, status); + if (U_FAILURE(status)) { + return result; + } + + bool added = result.appendSingleUnit(singleUnit, status); + if (U_FAILURE(status)) { + return result; + } + + if (sawAnd && !added) { + // Two similar units are not allowed in a mixed unit. + status = kUnitIdentifierSyntaxError; + return result; + } + + if (result.singleUnits.length() >= 2) { + // nextSingleUnit fails appropriately for "per" and "and" in the + // same identifier. It doesn't fail for other compound units + // (COMPOUND_PART_TIMES). Consequently we take care of that + // here. + UMeasureUnitComplexity complexity = + sawAnd ? UMEASURE_UNIT_MIXED : UMEASURE_UNIT_COMPOUND; + if (result.singleUnits.length() == 2) { + // After appending two singleUnits, the complexity will be `UMEASURE_UNIT_COMPOUND` + U_ASSERT(result.complexity == UMEASURE_UNIT_COMPOUND); + result.complexity = complexity; + } else if (result.complexity != complexity) { + // Can't have mixed compound units + status = kUnitIdentifierSyntaxError; + return result; + } + } + } + + return result; + } + +private: + // Tracks parser progress: the offset into fSource. + int32_t fIndex = 0; + + // Since we're not owning this memory, whatever is passed to the constructor + // should live longer than this Parser - and the parser shouldn't return any + // references to that string. + StringPiece fSource; + BytesTrie fTrie; + + // Set to true when we've seen a "-per-" or a "per-", after which all units + // are in the denominator. Until we find an "-and-", at which point the + // identifier is invalid pending TODO(CLDR-13701). + bool fAfterPer = false; + + Parser() : fSource(""), fTrie(u"") {} + + Parser(StringPiece source) + : fSource(source), fTrie(gSerializedUnitExtrasStemTrie) {} + + inline bool hasNext() const { + return fIndex < fSource.length(); + } + + // Returns the next Token parsed from fSource, advancing fIndex to the end + // of that token in fSource. In case of U_FAILURE(status), the token + // returned will cause an abort if getType() is called on it. + Token nextToken(UErrorCode& status) { + fTrie.reset(); + int32_t match = -1; + // Saves the position in the fSource string for the end of the most + // recent matching token. + int32_t previ = -1; + // Find the longest token that matches a value in the trie: + while (fIndex < fSource.length()) { + auto result = fTrie.next(fSource.data()[fIndex++]); + if (result == USTRINGTRIE_NO_MATCH) { + break; + } else if (result == USTRINGTRIE_NO_VALUE) { + continue; + } + U_ASSERT(USTRINGTRIE_HAS_VALUE(result)); + match = fTrie.getValue(); + previ = fIndex; + if (result == USTRINGTRIE_FINAL_VALUE) { + break; + } + U_ASSERT(result == USTRINGTRIE_INTERMEDIATE_VALUE); + // continue; + } + + if (match < 0) { + status = kUnitIdentifierSyntaxError; + } else { + fIndex = previ; + } + return Token(match); + } + + /** + * Returns the next "single unit" via result. + * + * If a "-per-" was parsed, the result will have appropriate negative + * dimensionality. + * + * Returns an error if we parse both compound units and "-and-", since mixed + * compound units are not yet supported - TODO(CLDR-13701). + * + * @param result Will be overwritten by the result, if status shows success. + * @param sawAnd If an "-and-" was parsed prior to finding the "single + * unit", sawAnd is set to true. If not, it is left as is. + * @param status ICU error code. + */ + SingleUnitImpl nextSingleUnit(bool &sawAnd, UErrorCode &status) { + SingleUnitImpl result; + if (U_FAILURE(status)) { + return result; + } + + // state: + // 0 = no tokens seen yet (will accept power, SI or binary prefix, or simple unit) + // 1 = power token seen (will not accept another power token) + // 2 = SI or binary prefix token seen (will not accept a power, or SI or binary prefix token) + int32_t state = 0; + + bool atStart = fIndex == 0; + Token token = nextToken(status); + if (U_FAILURE(status)) { + return result; + } + + if (atStart) { + // Identifiers optionally start with "per-". + if (token.getType() == Token::TYPE_INITIAL_COMPOUND_PART) { + U_ASSERT(token.getInitialCompoundPart() == INITIAL_COMPOUND_PART_PER); + fAfterPer = true; + result.dimensionality = -1; + + token = nextToken(status); + if (U_FAILURE(status)) { + return result; + } + } + } else { + // All other SingleUnit's are separated from previous SingleUnit's + // via a compound part: + if (token.getType() != Token::TYPE_COMPOUND_PART) { + status = kUnitIdentifierSyntaxError; + return result; + } + + switch (token.getMatch()) { + case COMPOUND_PART_PER: + if (sawAnd) { + // Mixed compound units not yet supported, + // TODO(CLDR-13701). + status = kUnitIdentifierSyntaxError; + return result; + } + fAfterPer = true; + result.dimensionality = -1; + break; + + case COMPOUND_PART_TIMES: + if (fAfterPer) { + result.dimensionality = -1; + } + break; + + case COMPOUND_PART_AND: + if (fAfterPer) { + // Can't start with "-and-", and mixed compound units + // not yet supported, TODO(CLDR-13701). + status = kUnitIdentifierSyntaxError; + return result; + } + sawAnd = true; + break; + } + + token = nextToken(status); + if (U_FAILURE(status)) { + return result; + } + } + + // Read tokens until we have a complete SingleUnit or we reach the end. + while (true) { + switch (token.getType()) { + case Token::TYPE_POWER_PART: + if (state > 0) { + status = kUnitIdentifierSyntaxError; + return result; + } + result.dimensionality *= token.getPower(); + state = 1; + break; + + case Token::TYPE_PREFIX: + if (state > 1) { + status = kUnitIdentifierSyntaxError; + return result; + } + result.unitPrefix = token.getUnitPrefix(); + state = 2; + break; + + case Token::TYPE_SIMPLE_UNIT: + result.index = token.getSimpleUnitIndex(); + return result; + + default: + status = kUnitIdentifierSyntaxError; + return result; + } + + if (!hasNext()) { + // We ran out of tokens before finding a complete single unit. + status = kUnitIdentifierSyntaxError; + return result; + } + token = nextToken(status); + if (U_FAILURE(status)) { + return result; + } + } + + return result; + } +}; + +// Sorting function wrapping SingleUnitImpl::compareTo for use with uprv_sortArray. +int32_t U_CALLCONV +compareSingleUnits(const void* /*context*/, const void* left, const void* right) { + auto realLeft = static_cast(left); + auto realRight = static_cast(right); + return (*realLeft)->compareTo(**realRight); +} + +// Returns an index into the gCategories array, for the "unitQuantity" (aka +// "type" or "category") associated with the given base unit identifier. Returns +// -1 on failure, together with U_UNSUPPORTED_ERROR. +int32_t getUnitCategoryIndex(BytesTrie &trie, StringPiece baseUnitIdentifier, UErrorCode &status) { + UStringTrieResult result = trie.reset().next(baseUnitIdentifier.data(), baseUnitIdentifier.length()); + if (!USTRINGTRIE_HAS_VALUE(result)) { + status = U_UNSUPPORTED_ERROR; + return -1; + } + + return trie.getValue(); +} + +} // namespace + +U_CAPI int32_t U_EXPORT2 +umeas_getPrefixPower(UMeasurePrefix unitPrefix) { + if (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_BIN && + unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_BIN) { + return unitPrefix - UMEASURE_PREFIX_INTERNAL_ONE_BIN; + } + U_ASSERT(unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI && + unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI); + return unitPrefix - UMEASURE_PREFIX_ONE; +} + +U_CAPI int32_t U_EXPORT2 +umeas_getPrefixBase(UMeasurePrefix unitPrefix) { + if (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_BIN && + unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_BIN) { + return 1024; + } + U_ASSERT(unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI && + unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI); + return 10; +} + +CharString U_I18N_API getUnitQuantity(const MeasureUnitImpl &baseMeasureUnitImpl, UErrorCode &status) { + CharString result; + MeasureUnitImpl baseUnitImpl = baseMeasureUnitImpl.copy(status); + UErrorCode localStatus = U_ZERO_ERROR; + umtx_initOnce(gUnitExtrasInitOnce, &initUnitExtras, status); + if (U_FAILURE(status)) { + return result; + } + BytesTrie trie(gSerializedUnitCategoriesTrie); + + baseUnitImpl.serialize(status); + StringPiece identifier = baseUnitImpl.identifier.data(); + int32_t idx = getUnitCategoryIndex(trie, identifier, localStatus); + if (U_FAILURE(status)) { + return result; + } + + // In case the base unit identifier did not match any entry. + if (U_FAILURE(localStatus)) { + localStatus = U_ZERO_ERROR; + baseUnitImpl.takeReciprocal(status); + baseUnitImpl.serialize(status); + identifier.set(baseUnitImpl.identifier.data()); + idx = getUnitCategoryIndex(trie, identifier, localStatus); + + if (U_FAILURE(status)) { + return result; + } + } + + // In case the reciprocal of the base unit identifier did not match any entry. + MeasureUnitImpl simplifiedUnit = baseMeasureUnitImpl.copyAndSimplify(status); + if (U_FAILURE(status)) { + return result; + } + if (U_FAILURE(localStatus)) { + localStatus = U_ZERO_ERROR; + simplifiedUnit.serialize(status); + identifier.set(simplifiedUnit.identifier.data()); + idx = getUnitCategoryIndex(trie, identifier, localStatus); + + if (U_FAILURE(status)) { + return result; + } + } + + // In case the simplified base unit identifier did not match any entry. + if (U_FAILURE(localStatus)) { + localStatus = U_ZERO_ERROR; + simplifiedUnit.takeReciprocal(status); + simplifiedUnit.serialize(status); + identifier.set(simplifiedUnit.identifier.data()); + idx = getUnitCategoryIndex(trie, identifier, localStatus); + + if (U_FAILURE(status)) { + return result; + } + } + + // If there is no match at all, throw an exception. + if (U_FAILURE(localStatus)) { + status = U_INVALID_FORMAT_ERROR; + return result; + } + + if (idx < 0 || idx >= gCategoriesCount) { + status = U_INVALID_FORMAT_ERROR; + return result; + } + + result.appendInvariantChars(gCategories[idx], u_strlen(gCategories[idx]), status); + return result; +} + +// In ICU4J, this is MeasureUnit.getSingleUnitImpl(). +SingleUnitImpl SingleUnitImpl::forMeasureUnit(const MeasureUnit& measureUnit, UErrorCode& status) { + MeasureUnitImpl temp; + const MeasureUnitImpl& impl = MeasureUnitImpl::forMeasureUnit(measureUnit, temp, status); + if (U_FAILURE(status)) { + return {}; + } + if (impl.singleUnits.length() == 0) { + return {}; + } + if (impl.singleUnits.length() == 1) { + return *impl.singleUnits[0]; + } + status = U_ILLEGAL_ARGUMENT_ERROR; + return {}; +} + +MeasureUnit SingleUnitImpl::build(UErrorCode& status) const { + MeasureUnitImpl temp; + temp.appendSingleUnit(*this, status); + // TODO(icu-units#28): the MeasureUnitImpl::build() method uses + // findBySubtype, which is relatively slow. + // - At the time of loading the simple unit IDs, we could also save a + // mapping to the builtin MeasureUnit type and subtype they correspond to. + // - This method could then check dimensionality and index, and if both are + // 1, directly return MeasureUnit instances very quickly. + return std::move(temp).build(status); +} + +const char *SingleUnitImpl::getSimpleUnitID() const { + return gSimpleUnits[index]; +} + +void SingleUnitImpl::appendNeutralIdentifier(CharString &result, UErrorCode &status) const UPRV_NO_SANITIZE_UNDEFINED { + int32_t absPower = std::abs(this->dimensionality); + + U_ASSERT(absPower > 0); // "this function does not support the dimensionless single units"; + + if (absPower == 1) { + // no-op + } else if (absPower == 2) { + result.append(StringPiece("square-"), status); + } else if (absPower == 3) { + result.append(StringPiece("cubic-"), status); + } else if (absPower <= 15) { + result.append(StringPiece("pow"), status); + result.appendNumber(absPower, status); + result.append(StringPiece("-"), status); + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; // Unit Identifier Syntax Error + return; + } + + if (U_FAILURE(status)) { + return; + } + + if (this->unitPrefix != UMEASURE_PREFIX_ONE) { + bool found = false; + for (const auto &unitPrefixInfo : gUnitPrefixStrings) { + // TODO: consider using binary search? If we do this, add a unit + // test to ensure gUnitPrefixStrings is sorted? + if (unitPrefixInfo.value == this->unitPrefix) { + result.append(unitPrefixInfo.string, status); + found = true; + break; + } + } + if (!found) { + status = U_UNSUPPORTED_ERROR; + return; + } + } + + result.append(StringPiece(this->getSimpleUnitID()), status); +} + +int32_t SingleUnitImpl::getUnitCategoryIndex() const { + return gSimpleUnitCategories[index]; +} + +MeasureUnitImpl::MeasureUnitImpl(const SingleUnitImpl &singleUnit, UErrorCode &status) { + this->appendSingleUnit(singleUnit, status); +} + +MeasureUnitImpl MeasureUnitImpl::forIdentifier(StringPiece identifier, UErrorCode& status) { + return Parser::from(identifier, status).parse(status); +} + +const MeasureUnitImpl& MeasureUnitImpl::forMeasureUnit( + const MeasureUnit& measureUnit, MeasureUnitImpl& memory, UErrorCode& status) { + if (measureUnit.fImpl) { + return *measureUnit.fImpl; + } else { + memory = Parser::from(measureUnit.getIdentifier(), status).parse(status); + return memory; + } +} + +MeasureUnitImpl MeasureUnitImpl::forMeasureUnitMaybeCopy( + const MeasureUnit& measureUnit, UErrorCode& status) { + if (measureUnit.fImpl) { + return measureUnit.fImpl->copy(status); + } else { + return Parser::from(measureUnit.getIdentifier(), status).parse(status); + } +} + +void MeasureUnitImpl::takeReciprocal(UErrorCode& /*status*/) { + identifier.clear(); + for (int32_t i = 0; i < singleUnits.length(); i++) { + singleUnits[i]->dimensionality *= -1; + } +} + +MeasureUnitImpl MeasureUnitImpl::copyAndSimplify(UErrorCode &status) const { + MeasureUnitImpl result; + for (int32_t i = 0; i < singleUnits.length(); i++) { + const SingleUnitImpl &singleUnit = *this->singleUnits[i]; + + // The following `for` loop will cause time complexity to be O(n^2). + // However, n is very small (number of units, generally, at maximum equal to 10) + bool unitExist = false; + for (int32_t j = 0; j < result.singleUnits.length(); j++) { + if (uprv_strcmp(result.singleUnits[j]->getSimpleUnitID(), singleUnit.getSimpleUnitID()) == + 0 && + result.singleUnits[j]->unitPrefix == singleUnit.unitPrefix) { + unitExist = true; + result.singleUnits[j]->dimensionality = + result.singleUnits[j]->dimensionality + singleUnit.dimensionality; + break; + } + } + + if (!unitExist) { + result.appendSingleUnit(singleUnit, status); + } + } + + return result; +} + +bool MeasureUnitImpl::appendSingleUnit(const SingleUnitImpl &singleUnit, UErrorCode &status) { + identifier.clear(); + + if (singleUnit.isDimensionless()) { + // Do not append dimensionless units. + return false; + } + + // Find a similar unit that already exists, to attempt to coalesce + SingleUnitImpl *oldUnit = nullptr; + for (int32_t i = 0; i < this->singleUnits.length(); i++) { + auto *candidate = this->singleUnits[i]; + if (candidate->isCompatibleWith(singleUnit)) { + oldUnit = candidate; + } + } + + if (oldUnit) { + // Both dimensionalities will be positive, or both will be negative, by + // virtue of isCompatibleWith(). + oldUnit->dimensionality += singleUnit.dimensionality; + + return false; + } + + // Add a copy of singleUnit + // NOTE: MaybeStackVector::emplaceBackAndCheckErrorCode creates new copy of singleUnit. + this->singleUnits.emplaceBackAndCheckErrorCode(status, singleUnit); + if (U_FAILURE(status)) { + return false; + } + + // If the MeasureUnitImpl is `UMEASURE_UNIT_SINGLE` and after the appending a unit, the `singleUnits` + // contains more than one. thus means the complexity should be `UMEASURE_UNIT_COMPOUND` + if (this->singleUnits.length() > 1 && + this->complexity == UMeasureUnitComplexity::UMEASURE_UNIT_SINGLE) { + this->complexity = UMeasureUnitComplexity::UMEASURE_UNIT_COMPOUND; + } + + return true; +} + +MaybeStackVector +MeasureUnitImpl::extractIndividualUnitsWithIndices(UErrorCode &status) const { + MaybeStackVector result; + + if (this->complexity != UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) { + result.emplaceBackAndCheckErrorCode(status, 0, *this, status); + return result; + } + + for (int32_t i = 0; i < singleUnits.length(); ++i) { + result.emplaceBackAndCheckErrorCode(status, i, *singleUnits[i], status); + if (U_FAILURE(status)) { + return result; + } + } + + return result; +} + +/** + * Normalize a MeasureUnitImpl and generate the identifier string in place. + */ +void MeasureUnitImpl::serialize(UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + + if (this->singleUnits.length() == 0) { + // Dimensionless, constructed by the default constructor. + return; + } + + if (this->complexity == UMEASURE_UNIT_COMPOUND) { + // Note: don't sort a MIXED unit + uprv_sortArray(this->singleUnits.getAlias(), this->singleUnits.length(), + sizeof(this->singleUnits[0]), compareSingleUnits, nullptr, false, &status); + if (U_FAILURE(status)) { + return; + } + } + + CharString result; + bool beforePer = true; + bool firstTimeNegativeDimension = false; + for (int32_t i = 0; i < this->singleUnits.length(); i++) { + if (beforePer && (*this->singleUnits[i]).dimensionality < 0) { + beforePer = false; + firstTimeNegativeDimension = true; + } else if ((*this->singleUnits[i]).dimensionality < 0) { + firstTimeNegativeDimension = false; + } + + if (U_FAILURE(status)) { + return; + } + + if (this->complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) { + if (result.length() != 0) { + result.append(StringPiece("-and-"), status); + } + } else { + if (firstTimeNegativeDimension) { + if (result.length() == 0) { + result.append(StringPiece("per-"), status); + } else { + result.append(StringPiece("-per-"), status); + } + } else { + if (result.length() != 0) { + result.append(StringPiece("-"), status); + } + } + } + + this->singleUnits[i]->appendNeutralIdentifier(result, status); + } + + this->identifier = CharString(result, status); +} + +MeasureUnit MeasureUnitImpl::build(UErrorCode& status) && { + this->serialize(status); + return MeasureUnit(std::move(*this)); +} + +MeasureUnit MeasureUnit::forIdentifier(StringPiece identifier, UErrorCode& status) { + return Parser::from(identifier, status).parse(status).build(status); +} + +UMeasureUnitComplexity MeasureUnit::getComplexity(UErrorCode& status) const { + MeasureUnitImpl temp; + return MeasureUnitImpl::forMeasureUnit(*this, temp, status).complexity; +} + +UMeasurePrefix MeasureUnit::getPrefix(UErrorCode& status) const { + return SingleUnitImpl::forMeasureUnit(*this, status).unitPrefix; +} + +MeasureUnit MeasureUnit::withPrefix(UMeasurePrefix prefix, UErrorCode& status) const UPRV_NO_SANITIZE_UNDEFINED { + SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status); + singleUnit.unitPrefix = prefix; + return singleUnit.build(status); +} + +int32_t MeasureUnit::getDimensionality(UErrorCode& status) const { + SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status); + if (U_FAILURE(status)) { return 0; } + if (singleUnit.isDimensionless()) { + return 0; + } + return singleUnit.dimensionality; +} + +MeasureUnit MeasureUnit::withDimensionality(int32_t dimensionality, UErrorCode& status) const { + SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status); + singleUnit.dimensionality = dimensionality; + return singleUnit.build(status); +} + +MeasureUnit MeasureUnit::reciprocal(UErrorCode& status) const { + MeasureUnitImpl impl = MeasureUnitImpl::forMeasureUnitMaybeCopy(*this, status); + impl.takeReciprocal(status); + return std::move(impl).build(status); +} + +MeasureUnit MeasureUnit::product(const MeasureUnit& other, UErrorCode& status) const { + MeasureUnitImpl impl = MeasureUnitImpl::forMeasureUnitMaybeCopy(*this, status); + MeasureUnitImpl temp; + const MeasureUnitImpl& otherImpl = MeasureUnitImpl::forMeasureUnit(other, temp, status); + if (impl.complexity == UMEASURE_UNIT_MIXED || otherImpl.complexity == UMEASURE_UNIT_MIXED) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return {}; + } + for (int32_t i = 0; i < otherImpl.singleUnits.length(); i++) { + impl.appendSingleUnit(*otherImpl.singleUnits[i], status); + } + if (impl.singleUnits.length() > 1) { + impl.complexity = UMEASURE_UNIT_COMPOUND; + } + return std::move(impl).build(status); +} + +LocalArray MeasureUnit::splitToSingleUnitsImpl(int32_t& outCount, UErrorCode& status) const { + MeasureUnitImpl temp; + const MeasureUnitImpl& impl = MeasureUnitImpl::forMeasureUnit(*this, temp, status); + outCount = impl.singleUnits.length(); + MeasureUnit* arr = new MeasureUnit[outCount]; + if (arr == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return LocalArray(); + } + for (int32_t i = 0; i < outCount; i++) { + arr[i] = impl.singleUnits[i]->build(status); + } + return LocalArray(arr, status); +} + + +U_NAMESPACE_END + +#endif /* !UNCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/measunit_impl.h b/deps/icu-small/source/i18n/measunit_impl.h index c60ff2fc33bdc9..212e4deb86f331 100644 --- a/deps/icu-small/source/i18n/measunit_impl.h +++ b/deps/icu-small/source/i18n/measunit_impl.h @@ -1,376 +1,376 @@ -// © 2020 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#ifndef __MEASUNIT_IMPL_H__ -#define __MEASUNIT_IMPL_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/measunit.h" -#include "cmemory.h" -#include "charstr.h" - -U_NAMESPACE_BEGIN - -namespace number { -namespace impl { -class LongNameHandler; -} -} // namespace number - -static const char16_t kDefaultCurrency[] = u"XXX"; -static const char kDefaultCurrency8[] = "XXX"; - -/** - * Looks up the "unitQuantity" (aka "type" or "category") of a base unit - * identifier. The category is returned via `result`, which must initially be - * empty. - * - * This only supports base units: other units must be resolved to base units - * before passing to this function, otherwise U_UNSUPPORTED_ERROR status may be - * returned. - * - * Categories are found in `unitQuantities` in the `units` resource (see - * `units.txt`). - */ -// TODO: make this function accepts any `MeasureUnit` as Java and move it to the `UnitsData` class. -CharString U_I18N_API getUnitQuantity(const MeasureUnitImpl &baseMeasureUnitImpl, UErrorCode &status); - -/** - * A struct representing a single unit (optional SI or binary prefix, and dimensionality). - */ -struct U_I18N_API SingleUnitImpl : public UMemory { - /** - * Gets a single unit from the MeasureUnit. If there are multiple single units, sets an error - * code and returns the base dimensionless unit. Parses if necessary. - */ - static SingleUnitImpl forMeasureUnit(const MeasureUnit& measureUnit, UErrorCode& status); - - /** Transform this SingleUnitImpl into a MeasureUnit, simplifying if possible. */ - MeasureUnit build(UErrorCode& status) const; - - /** - * Returns the "simple unit ID", without SI or dimensionality prefix: this - * instance may represent a square-kilometer, but only "meter" will be - * returned. - * - * The returned pointer points at memory that exists for the duration of the - * program's running. - */ - const char *getSimpleUnitID() const; - - /** - * Generates and append a neutral identifier string for a single unit which means we do not include - * the dimension signal. - */ - void appendNeutralIdentifier(CharString &result, UErrorCode &status) const; - - /** - * Returns the index of this unit's "quantity" in unitQuantities (in - * measunit_extra.cpp). The value of this index determines sort order for - * normalization of unit identifiers. - */ - int32_t getUnitCategoryIndex() const; - - /** - * Compare this SingleUnitImpl to another SingleUnitImpl for the sake of - * sorting and coalescing. - * - * Sort order of units is specified by UTS #35 - * (https://unicode.org/reports/tr35/tr35-info.html#Unit_Identifier_Normalization). - * - * Takes the sign of dimensionality into account, but not the absolute - * value: per-meter is not considered the same as meter, but meter is - * considered the same as square-meter. - * - * The dimensionless unit generally does not get compared, but if it did, it - * would sort before other units by virtue of index being < 0 and - * dimensionality not being negative. - */ - int32_t compareTo(const SingleUnitImpl& other) const { - if (dimensionality < 0 && other.dimensionality > 0) { - // Positive dimensions first - return 1; - } - if (dimensionality > 0 && other.dimensionality < 0) { - return -1; - } - - // Sort by official quantity order - int32_t thisQuantity = this->getUnitCategoryIndex(); - int32_t otherQuantity = other.getUnitCategoryIndex(); - if (thisQuantity < otherQuantity) { - return -1; - } - if (thisQuantity > otherQuantity) { - return 1; - } - - // If quantity order didn't help, then we go by index. - if (index < other.index) { - return -1; - } - if (index > other.index) { - return 1; - } - - // When comparing binary prefixes vs SI prefixes, instead of comparing the actual values, we can - // multiply the binary prefix power by 3 and compare the powers. if they are equal, we can can - // compare the bases. - // NOTE: this methodology will fail if the binary prefix more than or equal 98. - int32_t unitBase = umeas_getPrefixBase(unitPrefix); - int32_t otherUnitBase = umeas_getPrefixBase(other.unitPrefix); - - // Values for comparison purposes only. - int32_t unitPower = unitBase == 1024 /* Binary Prefix */ ? umeas_getPrefixPower(unitPrefix) * 3 - : umeas_getPrefixPower(unitPrefix); - int32_t otherUnitPower = - otherUnitBase == 1024 /* Binary Prefix */ ? umeas_getPrefixPower(other.unitPrefix) * 3 - : umeas_getPrefixPower(other.unitPrefix); - - // NOTE: if the unitPower is less than the other, - // we return 1 not -1. Thus because we want th sorting order - // for the bigger prefix to be before the smaller. - // Example: megabyte should come before kilobyte. - if (unitPower < otherUnitPower) { - return 1; - } - if (unitPower > otherUnitPower) { - return -1; - } - - if (unitBase < otherUnitBase) { - return 1; - } - if (unitBase > otherUnitBase) { - return -1; - } - - return 0; - } - - /** - * Return whether this SingleUnitImpl is compatible with another for the purpose of coalescing. - * - * Units with the same base unit and SI or binary prefix should match, except that they must also - * have the same dimensionality sign, such that we don't merge numerator and denominator. - */ - bool isCompatibleWith(const SingleUnitImpl& other) const { - return (compareTo(other) == 0); - } - - /** - * Returns true if this unit is the "dimensionless base unit", as produced - * by the MeasureUnit() default constructor. (This does not include the - * likes of concentrations or angles.) - */ - bool isDimensionless() const { - return index == -1; - } - - /** - * Simple unit index, unique for every simple unit, -1 for the dimensionless - * unit. This is an index into a string list in measunit_extra.cpp, as - * loaded by SimpleUnitIdentifiersSink. - * - * The default value is -1, meaning the dimensionless unit: - * isDimensionless() will return true, until index is changed. - */ - int32_t index = -1; - - /** - * SI or binary prefix. - * - * This is ignored for the dimensionless unit. - */ - UMeasurePrefix unitPrefix = UMEASURE_PREFIX_ONE; - - /** - * Dimensionality. - * - * This is meaningless for the dimensionless unit. - */ - int32_t dimensionality = 1; -}; - -// Forward declaration -struct MeasureUnitImplWithIndex; - -// Export explicit template instantiations of MaybeStackArray, MemoryPool and -// MaybeStackVector. This is required when building DLLs for Windows. (See -// datefmt.h, collationiterator.h, erarules.h and others for similar examples.) -#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN -template class U_I18N_API MaybeStackArray; -template class U_I18N_API MemoryPool; -template class U_I18N_API MaybeStackVector; -#endif - -/** - * Internal representation of measurement units. Capable of representing all complexities of units, - * including mixed and compound units. - */ -class U_I18N_API MeasureUnitImpl : public UMemory { - public: - MeasureUnitImpl() = default; - MeasureUnitImpl(MeasureUnitImpl &&other) = default; - // No copy constructor, use MeasureUnitImpl::copy() to make it explicit. - MeasureUnitImpl(const MeasureUnitImpl &other, UErrorCode &status) = delete; - MeasureUnitImpl(const SingleUnitImpl &singleUnit, UErrorCode &status); - - MeasureUnitImpl &operator=(MeasureUnitImpl &&other) noexcept = default; - - /** Extract the MeasureUnitImpl from a MeasureUnit. */ - static inline const MeasureUnitImpl *get(const MeasureUnit &measureUnit) { - return measureUnit.fImpl; - } - - /** - * Parse a unit identifier into a MeasureUnitImpl. - * - * @param identifier The unit identifier string. - * @param status Set if the identifier string is not valid. - * @return A newly parsed value object. Behaviour of this unit is - * unspecified if an error is returned via status. - */ - static MeasureUnitImpl forIdentifier(StringPiece identifier, UErrorCode& status); - - /** - * Extract the MeasureUnitImpl from a MeasureUnit, or parse if it is not present. - * - * @param measureUnit The source MeasureUnit. - * @param memory A place to write the new MeasureUnitImpl if parsing is required. - * @param status Set if an error occurs. - * @return A reference to either measureUnit.fImpl or memory. - */ - static const MeasureUnitImpl& forMeasureUnit( - const MeasureUnit& measureUnit, MeasureUnitImpl& memory, UErrorCode& status); - - /** - * Extract the MeasureUnitImpl from a MeasureUnit, or parse if it is not present. - * - * @param measureUnit The source MeasureUnit. - * @param status Set if an error occurs. - * @return A value object, either newly parsed or copied from measureUnit. - */ - static MeasureUnitImpl forMeasureUnitMaybeCopy( - const MeasureUnit& measureUnit, UErrorCode& status); - - /** - * Used for currency units. - */ - static inline MeasureUnitImpl forCurrencyCode(StringPiece currencyCode) { - MeasureUnitImpl result; - UErrorCode localStatus = U_ZERO_ERROR; - result.identifier.append(currencyCode, localStatus); - // localStatus is not expected to fail since currencyCode should be 3 chars long - return result; - } - - /** Transform this MeasureUnitImpl into a MeasureUnit, simplifying if possible. */ - MeasureUnit build(UErrorCode& status) &&; - - /** - * Create a copy of this MeasureUnitImpl. Don't use copy constructor to make this explicit. - */ - MeasureUnitImpl copy(UErrorCode& status) const; - - /** - * Extracts the list of all the individual units inside the `MeasureUnitImpl` with their indices. - * For example: - * - if the `MeasureUnitImpl` is `foot-per-hour` - * it will return a list of 1 {(0, `foot-per-hour`)} - * - if the `MeasureUnitImpl` is `foot-and-inch` - * it will return a list of 2 {(0, `foot`), (1, `inch`)} - */ - MaybeStackVector - extractIndividualUnitsWithIndices(UErrorCode &status) const; - - /** Mutates this MeasureUnitImpl to take the reciprocal. */ - void takeReciprocal(UErrorCode& status); - - /** - * Returns a simplified version of the unit. - * NOTE: the simplification happen when there are two units equals in their base unit and their - * prefixes. - * - * Example 1: "square-meter-per-meter" --> "meter" - * Example 2: "square-millimeter-per-meter" --> "square-millimeter-per-meter" - */ - MeasureUnitImpl copyAndSimplify(UErrorCode &status) const; - - /** - * Mutates this MeasureUnitImpl to append a single unit. - * - * @return true if a new item was added. If unit is the dimensionless unit, - * it is never added: the return value will always be false. - */ - bool appendSingleUnit(const SingleUnitImpl& singleUnit, UErrorCode& status); - - /** - * Normalizes a MeasureUnitImpl and generate the identifier string in place. - */ - void serialize(UErrorCode &status); - - /** The complexity, either SINGLE, COMPOUND, or MIXED. */ - UMeasureUnitComplexity complexity = UMEASURE_UNIT_SINGLE; - - /** - * The list of single units. These may be summed or multiplied, based on the - * value of the complexity field. - * - * The "dimensionless" unit (SingleUnitImpl default constructor) must not be - * added to this list. - */ - MaybeStackVector singleUnits; - - /** - * The full unit identifier. Owned by the MeasureUnitImpl. Empty if not computed. - */ - CharString identifier; - - // For calling serialize - // TODO(icu-units#147): revisit serialization - friend class number::impl::LongNameHandler; -}; - -struct U_I18N_API MeasureUnitImplWithIndex : public UMemory { - const int32_t index; - MeasureUnitImpl unitImpl; - // Makes a copy of unitImpl. - MeasureUnitImplWithIndex(int32_t index, const MeasureUnitImpl &unitImpl, UErrorCode &status) - : index(index), unitImpl(unitImpl.copy(status)) { - } - MeasureUnitImplWithIndex(int32_t index, const SingleUnitImpl &singleUnitImpl, UErrorCode &status) - : index(index), unitImpl(MeasureUnitImpl(singleUnitImpl, status)) { - } -}; - -// Export explicit template instantiations of MaybeStackArray, MemoryPool and -// MaybeStackVector. This is required when building DLLs for Windows. (See -// datefmt.h, collationiterator.h, erarules.h and others for similar examples.) -#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN -template class U_I18N_API MaybeStackArray; -template class U_I18N_API MemoryPool; -template class U_I18N_API MaybeStackVector; - -// Export an explicit template instantiation of the LocalPointer that is used as a -// data member of MeasureUnitImpl. -// (When building DLLs for Windows this is required.) -#if defined(_MSC_VER) -// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!= -#pragma warning(push) -#pragma warning(disable : 4661) -#endif -template class U_I18N_API LocalPointerBase; -template class U_I18N_API LocalPointer; -#if defined(_MSC_VER) -#pragma warning(pop) -#endif -#endif - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ -#endif //__MEASUNIT_IMPL_H__ +// © 2020 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#ifndef __MEASUNIT_IMPL_H__ +#define __MEASUNIT_IMPL_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/measunit.h" +#include "cmemory.h" +#include "charstr.h" + +U_NAMESPACE_BEGIN + +namespace number { +namespace impl { +class LongNameHandler; +} +} // namespace number + +static const char16_t kDefaultCurrency[] = u"XXX"; +static const char kDefaultCurrency8[] = "XXX"; + +/** + * Looks up the "unitQuantity" (aka "type" or "category") of a base unit + * identifier. The category is returned via `result`, which must initially be + * empty. + * + * This only supports base units: other units must be resolved to base units + * before passing to this function, otherwise U_UNSUPPORTED_ERROR status may be + * returned. + * + * Categories are found in `unitQuantities` in the `units` resource (see + * `units.txt`). + */ +// TODO: make this function accepts any `MeasureUnit` as Java and move it to the `UnitsData` class. +CharString U_I18N_API getUnitQuantity(const MeasureUnitImpl &baseMeasureUnitImpl, UErrorCode &status); + +/** + * A struct representing a single unit (optional SI or binary prefix, and dimensionality). + */ +struct U_I18N_API SingleUnitImpl : public UMemory { + /** + * Gets a single unit from the MeasureUnit. If there are multiple single units, sets an error + * code and returns the base dimensionless unit. Parses if necessary. + */ + static SingleUnitImpl forMeasureUnit(const MeasureUnit& measureUnit, UErrorCode& status); + + /** Transform this SingleUnitImpl into a MeasureUnit, simplifying if possible. */ + MeasureUnit build(UErrorCode& status) const; + + /** + * Returns the "simple unit ID", without SI or dimensionality prefix: this + * instance may represent a square-kilometer, but only "meter" will be + * returned. + * + * The returned pointer points at memory that exists for the duration of the + * program's running. + */ + const char *getSimpleUnitID() const; + + /** + * Generates and append a neutral identifier string for a single unit which means we do not include + * the dimension signal. + */ + void appendNeutralIdentifier(CharString &result, UErrorCode &status) const; + + /** + * Returns the index of this unit's "quantity" in unitQuantities (in + * measunit_extra.cpp). The value of this index determines sort order for + * normalization of unit identifiers. + */ + int32_t getUnitCategoryIndex() const; + + /** + * Compare this SingleUnitImpl to another SingleUnitImpl for the sake of + * sorting and coalescing. + * + * Sort order of units is specified by UTS #35 + * (https://unicode.org/reports/tr35/tr35-info.html#Unit_Identifier_Normalization). + * + * Takes the sign of dimensionality into account, but not the absolute + * value: per-meter is not considered the same as meter, but meter is + * considered the same as square-meter. + * + * The dimensionless unit generally does not get compared, but if it did, it + * would sort before other units by virtue of index being < 0 and + * dimensionality not being negative. + */ + int32_t compareTo(const SingleUnitImpl& other) const { + if (dimensionality < 0 && other.dimensionality > 0) { + // Positive dimensions first + return 1; + } + if (dimensionality > 0 && other.dimensionality < 0) { + return -1; + } + + // Sort by official quantity order + int32_t thisQuantity = this->getUnitCategoryIndex(); + int32_t otherQuantity = other.getUnitCategoryIndex(); + if (thisQuantity < otherQuantity) { + return -1; + } + if (thisQuantity > otherQuantity) { + return 1; + } + + // If quantity order didn't help, then we go by index. + if (index < other.index) { + return -1; + } + if (index > other.index) { + return 1; + } + + // When comparing binary prefixes vs SI prefixes, instead of comparing the actual values, we can + // multiply the binary prefix power by 3 and compare the powers. if they are equal, we can can + // compare the bases. + // NOTE: this methodology will fail if the binary prefix more than or equal 98. + int32_t unitBase = umeas_getPrefixBase(unitPrefix); + int32_t otherUnitBase = umeas_getPrefixBase(other.unitPrefix); + + // Values for comparison purposes only. + int32_t unitPower = unitBase == 1024 /* Binary Prefix */ ? umeas_getPrefixPower(unitPrefix) * 3 + : umeas_getPrefixPower(unitPrefix); + int32_t otherUnitPower = + otherUnitBase == 1024 /* Binary Prefix */ ? umeas_getPrefixPower(other.unitPrefix) * 3 + : umeas_getPrefixPower(other.unitPrefix); + + // NOTE: if the unitPower is less than the other, + // we return 1 not -1. Thus because we want th sorting order + // for the bigger prefix to be before the smaller. + // Example: megabyte should come before kilobyte. + if (unitPower < otherUnitPower) { + return 1; + } + if (unitPower > otherUnitPower) { + return -1; + } + + if (unitBase < otherUnitBase) { + return 1; + } + if (unitBase > otherUnitBase) { + return -1; + } + + return 0; + } + + /** + * Return whether this SingleUnitImpl is compatible with another for the purpose of coalescing. + * + * Units with the same base unit and SI or binary prefix should match, except that they must also + * have the same dimensionality sign, such that we don't merge numerator and denominator. + */ + bool isCompatibleWith(const SingleUnitImpl& other) const { + return (compareTo(other) == 0); + } + + /** + * Returns true if this unit is the "dimensionless base unit", as produced + * by the MeasureUnit() default constructor. (This does not include the + * likes of concentrations or angles.) + */ + bool isDimensionless() const { + return index == -1; + } + + /** + * Simple unit index, unique for every simple unit, -1 for the dimensionless + * unit. This is an index into a string list in measunit_extra.cpp, as + * loaded by SimpleUnitIdentifiersSink. + * + * The default value is -1, meaning the dimensionless unit: + * isDimensionless() will return true, until index is changed. + */ + int32_t index = -1; + + /** + * SI or binary prefix. + * + * This is ignored for the dimensionless unit. + */ + UMeasurePrefix unitPrefix = UMEASURE_PREFIX_ONE; + + /** + * Dimensionality. + * + * This is meaningless for the dimensionless unit. + */ + int32_t dimensionality = 1; +}; + +// Forward declaration +struct MeasureUnitImplWithIndex; + +// Export explicit template instantiations of MaybeStackArray, MemoryPool and +// MaybeStackVector. This is required when building DLLs for Windows. (See +// datefmt.h, collationiterator.h, erarules.h and others for similar examples.) +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +template class U_I18N_API MaybeStackArray; +template class U_I18N_API MemoryPool; +template class U_I18N_API MaybeStackVector; +#endif + +/** + * Internal representation of measurement units. Capable of representing all complexities of units, + * including mixed and compound units. + */ +class U_I18N_API MeasureUnitImpl : public UMemory { + public: + MeasureUnitImpl() = default; + MeasureUnitImpl(MeasureUnitImpl &&other) = default; + // No copy constructor, use MeasureUnitImpl::copy() to make it explicit. + MeasureUnitImpl(const MeasureUnitImpl &other, UErrorCode &status) = delete; + MeasureUnitImpl(const SingleUnitImpl &singleUnit, UErrorCode &status); + + MeasureUnitImpl &operator=(MeasureUnitImpl &&other) noexcept = default; + + /** Extract the MeasureUnitImpl from a MeasureUnit. */ + static inline const MeasureUnitImpl *get(const MeasureUnit &measureUnit) { + return measureUnit.fImpl; + } + + /** + * Parse a unit identifier into a MeasureUnitImpl. + * + * @param identifier The unit identifier string. + * @param status Set if the identifier string is not valid. + * @return A newly parsed value object. Behaviour of this unit is + * unspecified if an error is returned via status. + */ + static MeasureUnitImpl forIdentifier(StringPiece identifier, UErrorCode& status); + + /** + * Extract the MeasureUnitImpl from a MeasureUnit, or parse if it is not present. + * + * @param measureUnit The source MeasureUnit. + * @param memory A place to write the new MeasureUnitImpl if parsing is required. + * @param status Set if an error occurs. + * @return A reference to either measureUnit.fImpl or memory. + */ + static const MeasureUnitImpl& forMeasureUnit( + const MeasureUnit& measureUnit, MeasureUnitImpl& memory, UErrorCode& status); + + /** + * Extract the MeasureUnitImpl from a MeasureUnit, or parse if it is not present. + * + * @param measureUnit The source MeasureUnit. + * @param status Set if an error occurs. + * @return A value object, either newly parsed or copied from measureUnit. + */ + static MeasureUnitImpl forMeasureUnitMaybeCopy( + const MeasureUnit& measureUnit, UErrorCode& status); + + /** + * Used for currency units. + */ + static inline MeasureUnitImpl forCurrencyCode(StringPiece currencyCode) { + MeasureUnitImpl result; + UErrorCode localStatus = U_ZERO_ERROR; + result.identifier.append(currencyCode, localStatus); + // localStatus is not expected to fail since currencyCode should be 3 chars long + return result; + } + + /** Transform this MeasureUnitImpl into a MeasureUnit, simplifying if possible. */ + MeasureUnit build(UErrorCode& status) &&; + + /** + * Create a copy of this MeasureUnitImpl. Don't use copy constructor to make this explicit. + */ + MeasureUnitImpl copy(UErrorCode& status) const; + + /** + * Extracts the list of all the individual units inside the `MeasureUnitImpl` with their indices. + * For example: + * - if the `MeasureUnitImpl` is `foot-per-hour` + * it will return a list of 1 {(0, `foot-per-hour`)} + * - if the `MeasureUnitImpl` is `foot-and-inch` + * it will return a list of 2 {(0, `foot`), (1, `inch`)} + */ + MaybeStackVector + extractIndividualUnitsWithIndices(UErrorCode &status) const; + + /** Mutates this MeasureUnitImpl to take the reciprocal. */ + void takeReciprocal(UErrorCode& status); + + /** + * Returns a simplified version of the unit. + * NOTE: the simplification happen when there are two units equals in their base unit and their + * prefixes. + * + * Example 1: "square-meter-per-meter" --> "meter" + * Example 2: "square-millimeter-per-meter" --> "square-millimeter-per-meter" + */ + MeasureUnitImpl copyAndSimplify(UErrorCode &status) const; + + /** + * Mutates this MeasureUnitImpl to append a single unit. + * + * @return true if a new item was added. If unit is the dimensionless unit, + * it is never added: the return value will always be false. + */ + bool appendSingleUnit(const SingleUnitImpl& singleUnit, UErrorCode& status); + + /** + * Normalizes a MeasureUnitImpl and generate the identifier string in place. + */ + void serialize(UErrorCode &status); + + /** The complexity, either SINGLE, COMPOUND, or MIXED. */ + UMeasureUnitComplexity complexity = UMEASURE_UNIT_SINGLE; + + /** + * The list of single units. These may be summed or multiplied, based on the + * value of the complexity field. + * + * The "dimensionless" unit (SingleUnitImpl default constructor) must not be + * added to this list. + */ + MaybeStackVector singleUnits; + + /** + * The full unit identifier. Owned by the MeasureUnitImpl. Empty if not computed. + */ + CharString identifier; + + // For calling serialize + // TODO(icu-units#147): revisit serialization + friend class number::impl::LongNameHandler; +}; + +struct U_I18N_API MeasureUnitImplWithIndex : public UMemory { + const int32_t index; + MeasureUnitImpl unitImpl; + // Makes a copy of unitImpl. + MeasureUnitImplWithIndex(int32_t index, const MeasureUnitImpl &unitImpl, UErrorCode &status) + : index(index), unitImpl(unitImpl.copy(status)) { + } + MeasureUnitImplWithIndex(int32_t index, const SingleUnitImpl &singleUnitImpl, UErrorCode &status) + : index(index), unitImpl(MeasureUnitImpl(singleUnitImpl, status)) { + } +}; + +// Export explicit template instantiations of MaybeStackArray, MemoryPool and +// MaybeStackVector. This is required when building DLLs for Windows. (See +// datefmt.h, collationiterator.h, erarules.h and others for similar examples.) +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +template class U_I18N_API MaybeStackArray; +template class U_I18N_API MemoryPool; +template class U_I18N_API MaybeStackVector; + +// Export an explicit template instantiation of the LocalPointer that is used as a +// data member of MeasureUnitImpl. +// (When building DLLs for Windows this is required.) +#if defined(_MSC_VER) +// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!= +#pragma warning(push) +#pragma warning(disable : 4661) +#endif +template class U_I18N_API LocalPointerBase; +template class U_I18N_API LocalPointer; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#endif + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ +#endif //__MEASUNIT_IMPL_H__ diff --git a/deps/icu-small/source/i18n/measure.cpp b/deps/icu-small/source/i18n/measure.cpp index b9c47fd401596d..1f892d0a1331c3 100644 --- a/deps/icu-small/source/i18n/measure.cpp +++ b/deps/icu-small/source/i18n/measure.cpp @@ -1,78 +1,78 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2004-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: April 26, 2004 -* Since: ICU 3.0 -********************************************************************** -*/ -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/measure.h" -#include "unicode/measunit.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Measure) - -Measure::Measure() : unit(nullptr) {} - -Measure::Measure(const Formattable& _number, MeasureUnit* adoptedUnit, - UErrorCode& ec) : - number(_number), unit(adoptedUnit) { - if (U_SUCCESS(ec) && - (!number.isNumeric() || adoptedUnit == 0)) { - ec = U_ILLEGAL_ARGUMENT_ERROR; - } -} - -Measure::Measure(const Measure& other) : - UObject(other), unit(nullptr) { - *this = other; -} - -Measure& Measure::operator=(const Measure& other) { - if (this != &other) { - delete unit; - number = other.number; - if (other.unit != nullptr) { - unit = other.unit->clone(); - } else { - unit = nullptr; - } - } - return *this; -} - -Measure *Measure::clone() const { - return new Measure(*this); -} - -Measure::~Measure() { - delete unit; -} - -bool Measure::operator==(const UObject& other) const { - if (this == &other) { // Same object, equal - return true; - } - if (typeid(*this) != typeid(other)) { // Different types, not equal - return false; - } - const Measure &m = static_cast(other); - return number == m.number && - ((unit == NULL) == (m.unit == NULL)) && - (unit == NULL || *unit == *m.unit); -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_FORMATTING +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2004-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: April 26, 2004 +* Since: ICU 3.0 +********************************************************************** +*/ +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/measure.h" +#include "unicode/measunit.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Measure) + +Measure::Measure() : unit(nullptr) {} + +Measure::Measure(const Formattable& _number, MeasureUnit* adoptedUnit, + UErrorCode& ec) : + number(_number), unit(adoptedUnit) { + if (U_SUCCESS(ec) && + (!number.isNumeric() || adoptedUnit == 0)) { + ec = U_ILLEGAL_ARGUMENT_ERROR; + } +} + +Measure::Measure(const Measure& other) : + UObject(other), unit(nullptr) { + *this = other; +} + +Measure& Measure::operator=(const Measure& other) { + if (this != &other) { + delete unit; + number = other.number; + if (other.unit != nullptr) { + unit = other.unit->clone(); + } else { + unit = nullptr; + } + } + return *this; +} + +Measure *Measure::clone() const { + return new Measure(*this); +} + +Measure::~Measure() { + delete unit; +} + +bool Measure::operator==(const UObject& other) const { + if (this == &other) { // Same object, equal + return true; + } + if (typeid(*this) != typeid(other)) { // Different types, not equal + return false; + } + const Measure &m = static_cast(other); + return number == m.number && + ((unit == nullptr) == (m.unit == nullptr)) && + (unit == nullptr || *unit == *m.unit); +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/msgfmt.cpp b/deps/icu-small/source/i18n/msgfmt.cpp index 29476f328f8077..bf31f23358c99b 100644 --- a/deps/icu-small/source/i18n/msgfmt.cpp +++ b/deps/icu-small/source/i18n/msgfmt.cpp @@ -1,2008 +1,2009 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/******************************************************************** - * COPYRIGHT: - * Copyright (c) 1997-2015, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************** - * - * File MSGFMT.CPP - * - * Modification History: - * - * Date Name Description - * 02/19/97 aliu Converted from java. - * 03/20/97 helena Finished first cut of implementation. - * 04/10/97 aliu Made to work on AIX. Added stoi to replace wtoi. - * 06/11/97 helena Fixed addPattern to take the pattern correctly. - * 06/17/97 helena Fixed the getPattern to return the correct pattern. - * 07/09/97 helena Made ParsePosition into a class. - * 02/22/99 stephen Removed character literals for EBCDIC safety - * 11/01/09 kirtig Added SelectFormat - ********************************************************************/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/appendable.h" -#include "unicode/choicfmt.h" -#include "unicode/datefmt.h" -#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" -#include "unicode/smpdtfmt.h" -#include "unicode/umsg.h" -#include "unicode/ustring.h" -#include "cmemory.h" -#include "patternprops.h" -#include "messageimpl.h" -#include "msgfmt_impl.h" -#include "plurrule_impl.h" -#include "uassert.h" -#include "uelement.h" -#include "uhash.h" -#include "ustrfmt.h" -#include "util.h" -#include "uvector.h" -#include "number_decimalquantity.h" - -// ***************************************************************************** -// class MessageFormat -// ***************************************************************************** - -#define SINGLE_QUOTE ((UChar)0x0027) -#define COMMA ((UChar)0x002C) -#define LEFT_CURLY_BRACE ((UChar)0x007B) -#define RIGHT_CURLY_BRACE ((UChar)0x007D) - -//--------------------------------------- -// static data - -static const UChar ID_NUMBER[] = { - 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0 /* "number" */ -}; -static const UChar ID_DATE[] = { - 0x64, 0x61, 0x74, 0x65, 0 /* "date" */ -}; -static const UChar ID_TIME[] = { - 0x74, 0x69, 0x6D, 0x65, 0 /* "time" */ -}; -static const UChar ID_SPELLOUT[] = { - 0x73, 0x70, 0x65, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0 /* "spellout" */ -}; -static const UChar ID_ORDINAL[] = { - 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0 /* "ordinal" */ -}; -static const UChar ID_DURATION[] = { - 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0 /* "duration" */ -}; - -// MessageFormat Type List Number, Date, Time or Choice -static const UChar * const TYPE_IDS[] = { - ID_NUMBER, - ID_DATE, - ID_TIME, - ID_SPELLOUT, - ID_ORDINAL, - ID_DURATION, - NULL, -}; - -static const UChar ID_EMPTY[] = { - 0 /* empty string, used for default so that null can mark end of list */ -}; -static const UChar ID_CURRENCY[] = { - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x63, 0x79, 0 /* "currency" */ -}; -static const UChar ID_PERCENT[] = { - 0x70, 0x65, 0x72, 0x63, 0x65, 0x6E, 0x74, 0 /* "percent" */ -}; -static const UChar ID_INTEGER[] = { - 0x69, 0x6E, 0x74, 0x65, 0x67, 0x65, 0x72, 0 /* "integer" */ -}; - -// NumberFormat modifier list, default, currency, percent or integer -static const UChar * const NUMBER_STYLE_IDS[] = { - ID_EMPTY, - ID_CURRENCY, - ID_PERCENT, - ID_INTEGER, - NULL, -}; - -static const UChar ID_SHORT[] = { - 0x73, 0x68, 0x6F, 0x72, 0x74, 0 /* "short" */ -}; -static const UChar ID_MEDIUM[] = { - 0x6D, 0x65, 0x64, 0x69, 0x75, 0x6D, 0 /* "medium" */ -}; -static const UChar ID_LONG[] = { - 0x6C, 0x6F, 0x6E, 0x67, 0 /* "long" */ -}; -static const UChar ID_FULL[] = { - 0x66, 0x75, 0x6C, 0x6C, 0 /* "full" */ -}; - -// DateFormat modifier list, default, short, medium, long or full -static const UChar * const DATE_STYLE_IDS[] = { - ID_EMPTY, - ID_SHORT, - ID_MEDIUM, - ID_LONG, - ID_FULL, - NULL, -}; - -static const icu::DateFormat::EStyle DATE_STYLES[] = { - icu::DateFormat::kDefault, - icu::DateFormat::kShort, - icu::DateFormat::kMedium, - icu::DateFormat::kLong, - icu::DateFormat::kFull, -}; - -static const int32_t DEFAULT_INITIAL_CAPACITY = 10; - -static const UChar NULL_STRING[] = { - 0x6E, 0x75, 0x6C, 0x6C, 0 // "null" -}; - -static const UChar OTHER_STRING[] = { - 0x6F, 0x74, 0x68, 0x65, 0x72, 0 // "other" -}; - -U_CDECL_BEGIN -static UBool U_CALLCONV equalFormatsForHash(const UHashTok key1, - const UHashTok key2) { - return icu::MessageFormat::equalFormats(key1.pointer, key2.pointer); -} - -U_CDECL_END - -U_NAMESPACE_BEGIN - -// ------------------------------------- -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MessageFormat) -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FormatNameEnumeration) - -//-------------------------------------------------------------------- - -/** - * Convert an integer value to a string and append the result to - * the given UnicodeString. - */ -static UnicodeString& itos(int32_t i, UnicodeString& appendTo) { - UChar temp[16]; - uprv_itou(temp,16,i,10,0); // 10 == radix - appendTo.append(temp, -1); - return appendTo; -} - - -// AppendableWrapper: encapsulates the result of formatting, keeping track -// of the string and its length. -class AppendableWrapper : public UMemory { -public: - AppendableWrapper(Appendable& appendable) : app(appendable), len(0) { - } - void append(const UnicodeString& s) { - app.appendString(s.getBuffer(), s.length()); - len += s.length(); - } - void append(const UChar* s, const int32_t sLength) { - app.appendString(s, sLength); - len += sLength; - } - void append(const UnicodeString& s, int32_t start, int32_t length) { - append(s.tempSubString(start, length)); - } - void formatAndAppend(const Format* formatter, const Formattable& arg, UErrorCode& ec) { - UnicodeString s; - formatter->format(arg, s, ec); - if (U_SUCCESS(ec)) { - append(s); - } - } - void formatAndAppend(const Format* formatter, const Formattable& arg, - const UnicodeString &argString, UErrorCode& ec) { - if (!argString.isEmpty()) { - if (U_SUCCESS(ec)) { - append(argString); - } - } else { - formatAndAppend(formatter, arg, ec); - } - } - int32_t length() { - return len; - } -private: - Appendable& app; - int32_t len; -}; - - -// ------------------------------------- -// Creates a MessageFormat instance based on the pattern. - -MessageFormat::MessageFormat(const UnicodeString& pattern, - UErrorCode& success) -: fLocale(Locale::getDefault()), // Uses the default locale - msgPattern(success), - formatAliases(NULL), - formatAliasesCapacity(0), - argTypes(NULL), - argTypeCount(0), - argTypeCapacity(0), - hasArgTypeConflicts(false), - defaultNumberFormat(NULL), - defaultDateFormat(NULL), - cachedFormatters(NULL), - customFormatArgStarts(NULL), - pluralProvider(*this, UPLURAL_TYPE_CARDINAL), - ordinalProvider(*this, UPLURAL_TYPE_ORDINAL) -{ - setLocaleIDs(fLocale.getName(), fLocale.getName()); - applyPattern(pattern, success); -} - -MessageFormat::MessageFormat(const UnicodeString& pattern, - const Locale& newLocale, - UErrorCode& success) -: fLocale(newLocale), - msgPattern(success), - formatAliases(NULL), - formatAliasesCapacity(0), - argTypes(NULL), - argTypeCount(0), - argTypeCapacity(0), - hasArgTypeConflicts(false), - defaultNumberFormat(NULL), - defaultDateFormat(NULL), - cachedFormatters(NULL), - customFormatArgStarts(NULL), - pluralProvider(*this, UPLURAL_TYPE_CARDINAL), - ordinalProvider(*this, UPLURAL_TYPE_ORDINAL) -{ - setLocaleIDs(fLocale.getName(), fLocale.getName()); - applyPattern(pattern, success); -} - -MessageFormat::MessageFormat(const UnicodeString& pattern, - const Locale& newLocale, - UParseError& parseError, - UErrorCode& success) -: fLocale(newLocale), - msgPattern(success), - formatAliases(NULL), - formatAliasesCapacity(0), - argTypes(NULL), - argTypeCount(0), - argTypeCapacity(0), - hasArgTypeConflicts(false), - defaultNumberFormat(NULL), - defaultDateFormat(NULL), - cachedFormatters(NULL), - customFormatArgStarts(NULL), - pluralProvider(*this, UPLURAL_TYPE_CARDINAL), - ordinalProvider(*this, UPLURAL_TYPE_ORDINAL) -{ - setLocaleIDs(fLocale.getName(), fLocale.getName()); - applyPattern(pattern, parseError, success); -} - -MessageFormat::MessageFormat(const MessageFormat& that) -: - Format(that), - fLocale(that.fLocale), - msgPattern(that.msgPattern), - formatAliases(NULL), - formatAliasesCapacity(0), - argTypes(NULL), - argTypeCount(0), - argTypeCapacity(0), - hasArgTypeConflicts(that.hasArgTypeConflicts), - defaultNumberFormat(NULL), - defaultDateFormat(NULL), - cachedFormatters(NULL), - customFormatArgStarts(NULL), - pluralProvider(*this, UPLURAL_TYPE_CARDINAL), - ordinalProvider(*this, UPLURAL_TYPE_ORDINAL) -{ - // This will take care of creating the hash tables (since they are NULL). - UErrorCode ec = U_ZERO_ERROR; - copyObjects(that, ec); - if (U_FAILURE(ec)) { - resetPattern(); - } -} - -MessageFormat::~MessageFormat() -{ - uhash_close(cachedFormatters); - uhash_close(customFormatArgStarts); - - uprv_free(argTypes); - uprv_free(formatAliases); - delete defaultNumberFormat; - delete defaultDateFormat; -} - -//-------------------------------------------------------------------- -// Variable-size array management - -/** - * Allocate argTypes[] to at least the given capacity and return - * true if successful. If not, leave argTypes[] unchanged. - * - * If argTypes is NULL, allocate it. If it is not NULL, enlarge it - * if necessary to be at least as large as specified. - */ -UBool MessageFormat::allocateArgTypes(int32_t capacity, UErrorCode& status) { - if (U_FAILURE(status)) { - return false; - } - if (argTypeCapacity >= capacity) { - return true; - } - if (capacity < DEFAULT_INITIAL_CAPACITY) { - capacity = DEFAULT_INITIAL_CAPACITY; - } else if (capacity < 2*argTypeCapacity) { - capacity = 2*argTypeCapacity; - } - Formattable::Type* a = (Formattable::Type*) - uprv_realloc(argTypes, sizeof(*argTypes) * capacity); - if (a == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return false; - } - argTypes = a; - argTypeCapacity = capacity; - return true; -} - -// ------------------------------------- -// assignment operator - -const MessageFormat& -MessageFormat::operator=(const MessageFormat& that) -{ - if (this != &that) { - // Calls the super class for assignment first. - Format::operator=(that); - - setLocale(that.fLocale); - msgPattern = that.msgPattern; - hasArgTypeConflicts = that.hasArgTypeConflicts; - - UErrorCode ec = U_ZERO_ERROR; - copyObjects(that, ec); - if (U_FAILURE(ec)) { - resetPattern(); - } - } - return *this; -} - -bool -MessageFormat::operator==(const Format& rhs) const -{ - if (this == &rhs) return true; - - MessageFormat& that = (MessageFormat&)rhs; - - // Check class ID before checking MessageFormat members - if (!Format::operator==(rhs) || - msgPattern != that.msgPattern || - fLocale != that.fLocale) { - return false; - } - - // Compare hashtables. - if ((customFormatArgStarts == NULL) != (that.customFormatArgStarts == NULL)) { - return false; - } - if (customFormatArgStarts == NULL) { - return true; - } - - UErrorCode ec = U_ZERO_ERROR; - const int32_t count = uhash_count(customFormatArgStarts); - const int32_t rhs_count = uhash_count(that.customFormatArgStarts); - if (count != rhs_count) { - return false; - } - int32_t idx = 0, rhs_idx = 0, pos = UHASH_FIRST, rhs_pos = UHASH_FIRST; - for (; idx < count && rhs_idx < rhs_count && U_SUCCESS(ec); ++idx, ++rhs_idx) { - const UHashElement* cur = uhash_nextElement(customFormatArgStarts, &pos); - const UHashElement* rhs_cur = uhash_nextElement(that.customFormatArgStarts, &rhs_pos); - if (cur->key.integer != rhs_cur->key.integer) { - return false; - } - const Format* format = (const Format*)uhash_iget(cachedFormatters, cur->key.integer); - const Format* rhs_format = (const Format*)uhash_iget(that.cachedFormatters, rhs_cur->key.integer); - if (*format != *rhs_format) { - return false; - } - } - return true; -} - -// ------------------------------------- -// Creates a copy of this MessageFormat, the caller owns the copy. - -MessageFormat* -MessageFormat::clone() const -{ - return new MessageFormat(*this); -} - -// ------------------------------------- -// Sets the locale of this MessageFormat object to theLocale. - -void -MessageFormat::setLocale(const Locale& theLocale) -{ - if (fLocale != theLocale) { - delete defaultNumberFormat; - defaultNumberFormat = NULL; - delete defaultDateFormat; - defaultDateFormat = NULL; - fLocale = theLocale; - setLocaleIDs(fLocale.getName(), fLocale.getName()); - pluralProvider.reset(); - ordinalProvider.reset(); - } -} - -// ------------------------------------- -// Gets the locale of this MessageFormat object. - -const Locale& -MessageFormat::getLocale() const -{ - return fLocale; -} - -void -MessageFormat::applyPattern(const UnicodeString& newPattern, - UErrorCode& status) -{ - UParseError parseError; - applyPattern(newPattern,parseError,status); -} - - -// ------------------------------------- -// Applies the new pattern and returns an error if the pattern -// is not correct. -void -MessageFormat::applyPattern(const UnicodeString& pattern, - UParseError& parseError, - UErrorCode& ec) -{ - if(U_FAILURE(ec)) { - return; - } - msgPattern.parse(pattern, &parseError, ec); - cacheExplicitFormats(ec); - - if (U_FAILURE(ec)) { - resetPattern(); - } -} - -void MessageFormat::resetPattern() { - msgPattern.clear(); - uhash_close(cachedFormatters); - cachedFormatters = NULL; - uhash_close(customFormatArgStarts); - customFormatArgStarts = NULL; - argTypeCount = 0; - hasArgTypeConflicts = false; -} - -void -MessageFormat::applyPattern(const UnicodeString& pattern, - UMessagePatternApostropheMode aposMode, - UParseError* parseError, - UErrorCode& status) { - if (aposMode != msgPattern.getApostropheMode()) { - msgPattern.clearPatternAndSetApostropheMode(aposMode); - } - applyPattern(pattern, *parseError, status); -} - -// ------------------------------------- -// Converts this MessageFormat instance to a pattern. - -UnicodeString& -MessageFormat::toPattern(UnicodeString& appendTo) const { - if ((customFormatArgStarts != NULL && 0 != uhash_count(customFormatArgStarts)) || - 0 == msgPattern.countParts() - ) { - appendTo.setToBogus(); - return appendTo; - } - return appendTo.append(msgPattern.getPatternString()); -} - -int32_t MessageFormat::nextTopLevelArgStart(int32_t partIndex) const { - if (partIndex != 0) { - partIndex = msgPattern.getLimitPartIndex(partIndex); - } - for (;;) { - UMessagePatternPartType type = msgPattern.getPartType(++partIndex); - if (type == UMSGPAT_PART_TYPE_ARG_START) { - return partIndex; - } - if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { - return -1; - } - } -} - -void MessageFormat::setArgStartFormat(int32_t argStart, - Format* formatter, - UErrorCode& status) { - if (U_FAILURE(status)) { - delete formatter; - return; - } - if (cachedFormatters == NULL) { - cachedFormatters=uhash_open(uhash_hashLong, uhash_compareLong, - equalFormatsForHash, &status); - if (U_FAILURE(status)) { - delete formatter; - return; - } - uhash_setValueDeleter(cachedFormatters, uprv_deleteUObject); - } - if (formatter == NULL) { - formatter = new DummyFormat(); - } - uhash_iput(cachedFormatters, argStart, formatter, &status); -} - - -UBool MessageFormat::argNameMatches(int32_t partIndex, const UnicodeString& argName, int32_t argNumber) { - const MessagePattern::Part& part = msgPattern.getPart(partIndex); - return part.getType() == UMSGPAT_PART_TYPE_ARG_NAME ? - msgPattern.partSubstringMatches(part, argName) : - part.getValue() == argNumber; // ARG_NUMBER -} - -// Sets a custom formatter for a MessagePattern ARG_START part index. -// "Custom" formatters are provided by the user via setFormat() or similar APIs. -void MessageFormat::setCustomArgStartFormat(int32_t argStart, - Format* formatter, - UErrorCode& status) { - setArgStartFormat(argStart, formatter, status); - if (customFormatArgStarts == NULL) { - customFormatArgStarts=uhash_open(uhash_hashLong, uhash_compareLong, - NULL, &status); - } - uhash_iputi(customFormatArgStarts, argStart, 1, &status); -} - -Format* MessageFormat::getCachedFormatter(int32_t argumentNumber) const { - if (cachedFormatters == NULL) { - return NULL; - } - void* ptr = uhash_iget(cachedFormatters, argumentNumber); - if (ptr != NULL && dynamic_cast((Format*)ptr) == NULL) { - return (Format*) ptr; - } else { - // Not cached, or a DummyFormat representing setFormat(NULL). - return NULL; - } -} - -// ------------------------------------- -// Adopts the new formats array and updates the array count. -// This MessageFormat instance owns the new formats. -void -MessageFormat::adoptFormats(Format** newFormats, - int32_t count) { - if (newFormats == NULL || count < 0) { - return; - } - // Throw away any cached formatters. - if (cachedFormatters != NULL) { - uhash_removeAll(cachedFormatters); - } - if (customFormatArgStarts != NULL) { - uhash_removeAll(customFormatArgStarts); - } - - int32_t formatNumber = 0; - UErrorCode status = U_ZERO_ERROR; - for (int32_t partIndex = 0; - formatNumber < count && U_SUCCESS(status) && - (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { - setCustomArgStartFormat(partIndex, newFormats[formatNumber], status); - ++formatNumber; - } - // Delete those that didn't get used (if any). - for (; formatNumber < count; ++formatNumber) { - delete newFormats[formatNumber]; - } - -} - -// ------------------------------------- -// Sets the new formats array and updates the array count. -// This MessageFormat instance makes a copy of the new formats. - -void -MessageFormat::setFormats(const Format** newFormats, - int32_t count) { - if (newFormats == NULL || count < 0) { - return; - } - // Throw away any cached formatters. - if (cachedFormatters != NULL) { - uhash_removeAll(cachedFormatters); - } - if (customFormatArgStarts != NULL) { - uhash_removeAll(customFormatArgStarts); - } - - UErrorCode status = U_ZERO_ERROR; - int32_t formatNumber = 0; - for (int32_t partIndex = 0; - formatNumber < count && U_SUCCESS(status) && (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { - Format* newFormat = NULL; - if (newFormats[formatNumber] != NULL) { - newFormat = newFormats[formatNumber]->clone(); - if (newFormat == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } - } - setCustomArgStartFormat(partIndex, newFormat, status); - ++formatNumber; - } - if (U_FAILURE(status)) { - resetPattern(); - } -} - -// ------------------------------------- -// Adopt a single format by format number. -// Do nothing if the format number is not less than the array count. - -void -MessageFormat::adoptFormat(int32_t n, Format *newFormat) { - LocalPointer p(newFormat); - if (n >= 0) { - int32_t formatNumber = 0; - for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { - if (n == formatNumber) { - UErrorCode status = U_ZERO_ERROR; - setCustomArgStartFormat(partIndex, p.orphan(), status); - return; - } - ++formatNumber; - } - } -} - -// ------------------------------------- -// Adopt a single format by format name. -// Do nothing if there is no match of formatName. -void -MessageFormat::adoptFormat(const UnicodeString& formatName, - Format* formatToAdopt, - UErrorCode& status) { - LocalPointer p(formatToAdopt); - if (U_FAILURE(status)) { - return; - } - int32_t argNumber = MessagePattern::validateArgumentName(formatName); - if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - for (int32_t partIndex = 0; - (partIndex = nextTopLevelArgStart(partIndex)) >= 0 && U_SUCCESS(status); - ) { - if (argNameMatches(partIndex + 1, formatName, argNumber)) { - Format* f; - if (p.isValid()) { - f = p.orphan(); - } else if (formatToAdopt == NULL) { - f = NULL; - } else { - f = formatToAdopt->clone(); - if (f == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - setCustomArgStartFormat(partIndex, f, status); - } - } -} - -// ------------------------------------- -// Set a single format. -// Do nothing if the variable is not less than the array count. -void -MessageFormat::setFormat(int32_t n, const Format& newFormat) { - - if (n >= 0) { - int32_t formatNumber = 0; - for (int32_t partIndex = 0; - (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { - if (n == formatNumber) { - Format* new_format = newFormat.clone(); - if (new_format) { - UErrorCode status = U_ZERO_ERROR; - setCustomArgStartFormat(partIndex, new_format, status); - } - return; - } - ++formatNumber; - } - } -} - -// ------------------------------------- -// Get a single format by format name. -// Do nothing if the variable is not less than the array count. -Format * -MessageFormat::getFormat(const UnicodeString& formatName, UErrorCode& status) { - if (U_FAILURE(status) || cachedFormatters == NULL) return NULL; - - int32_t argNumber = MessagePattern::validateArgumentName(formatName); - if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { - if (argNameMatches(partIndex + 1, formatName, argNumber)) { - return getCachedFormatter(partIndex); - } - } - return NULL; -} - -// ------------------------------------- -// Set a single format by format name -// Do nothing if the variable is not less than the array count. -void -MessageFormat::setFormat(const UnicodeString& formatName, - const Format& newFormat, - UErrorCode& status) { - if (U_FAILURE(status)) return; - - int32_t argNumber = MessagePattern::validateArgumentName(formatName); - if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - for (int32_t partIndex = 0; - (partIndex = nextTopLevelArgStart(partIndex)) >= 0 && U_SUCCESS(status); - ) { - if (argNameMatches(partIndex + 1, formatName, argNumber)) { - Format* new_format = newFormat.clone(); - if (new_format == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - setCustomArgStartFormat(partIndex, new_format, status); - } - } -} - -// ------------------------------------- -// Gets the format array. -const Format** -MessageFormat::getFormats(int32_t& cnt) const -{ - // This old API returns an array (which we hold) of Format* - // pointers. The array is valid up to the next call to any - // method on this object. We construct and resize an array - // on demand that contains aliases to the subformats[i].format - // pointers. - - // Get total required capacity first (it's refreshed on each call). - int32_t totalCapacity = 0; - for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0; ++totalCapacity) {} - - MessageFormat* t = const_cast (this); - cnt = 0; - if (formatAliases == nullptr) { - t->formatAliasesCapacity = totalCapacity; - Format** a = (Format**) - uprv_malloc(sizeof(Format*) * formatAliasesCapacity); - if (a == nullptr) { - t->formatAliasesCapacity = 0; - return nullptr; - } - t->formatAliases = a; - } else if (totalCapacity > formatAliasesCapacity) { - Format** a = (Format**) - uprv_realloc(formatAliases, sizeof(Format*) * totalCapacity); - if (a == nullptr) { - t->formatAliasesCapacity = 0; - return nullptr; - } - t->formatAliases = a; - t->formatAliasesCapacity = totalCapacity; - } - - for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { - t->formatAliases[cnt++] = getCachedFormatter(partIndex); - } - - return (const Format**)formatAliases; -} - - -UnicodeString MessageFormat::getArgName(int32_t partIndex) { - const MessagePattern::Part& part = msgPattern.getPart(partIndex); - return msgPattern.getSubstring(part); -} - -StringEnumeration* -MessageFormat::getFormatNames(UErrorCode& status) { - if (U_FAILURE(status)) return NULL; - - LocalPointer formatNames(new UVector(status), status); - if (U_FAILURE(status)) { - return nullptr; - } - formatNames->setDeleter(uprv_deleteUObject); - - for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { - LocalPointer name(getArgName(partIndex + 1).clone(), status); - formatNames->adoptElement(name.orphan(), status); - if (U_FAILURE(status)) return nullptr; - } - - LocalPointer nameEnumerator( - new FormatNameEnumeration(std::move(formatNames), status), status); - return U_SUCCESS(status) ? nameEnumerator.orphan() : nullptr; -} - -// ------------------------------------- -// Formats the source Formattable array and copy into the result buffer. -// Ignore the FieldPosition result for error checking. - -UnicodeString& -MessageFormat::format(const Formattable* source, - int32_t cnt, - UnicodeString& appendTo, - FieldPosition& ignore, - UErrorCode& success) const -{ - return format(source, NULL, cnt, appendTo, &ignore, success); -} - -// ------------------------------------- -// Internally creates a MessageFormat instance based on the -// pattern and formats the arguments Formattable array and -// copy into the appendTo buffer. - -UnicodeString& -MessageFormat::format( const UnicodeString& pattern, - const Formattable* arguments, - int32_t cnt, - UnicodeString& appendTo, - UErrorCode& success) -{ - MessageFormat temp(pattern, success); - return temp.format(arguments, NULL, cnt, appendTo, NULL, success); -} - -// ------------------------------------- -// Formats the source Formattable object and copy into the -// appendTo buffer. The Formattable object must be an array -// of Formattable instances, returns error otherwise. - -UnicodeString& -MessageFormat::format(const Formattable& source, - UnicodeString& appendTo, - FieldPosition& ignore, - UErrorCode& success) const -{ - if (U_FAILURE(success)) - return appendTo; - if (source.getType() != Formattable::kArray) { - success = U_ILLEGAL_ARGUMENT_ERROR; - return appendTo; - } - int32_t cnt; - const Formattable* tmpPtr = source.getArray(cnt); - return format(tmpPtr, NULL, cnt, appendTo, &ignore, success); -} - -UnicodeString& -MessageFormat::format(const UnicodeString* argumentNames, - const Formattable* arguments, - int32_t count, - UnicodeString& appendTo, - UErrorCode& success) const { - return format(arguments, argumentNames, count, appendTo, NULL, success); -} - -// Does linear search to find the match for an ArgName. -const Formattable* MessageFormat::getArgFromListByName(const Formattable* arguments, - const UnicodeString *argumentNames, - int32_t cnt, UnicodeString& name) const { - for (int32_t i = 0; i < cnt; ++i) { - if (0 == argumentNames[i].compare(name)) { - return arguments + i; - } - } - return NULL; -} - - -UnicodeString& -MessageFormat::format(const Formattable* arguments, - const UnicodeString *argumentNames, - int32_t cnt, - UnicodeString& appendTo, - FieldPosition* pos, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return appendTo; - } - - UnicodeStringAppendable usapp(appendTo); - AppendableWrapper app(usapp); - format(0, NULL, arguments, argumentNames, cnt, app, pos, status); - return appendTo; -} - -namespace { - -/** - * Mutable input/output values for the PluralSelectorProvider. - * Separate so that it is possible to make MessageFormat Freezable. - */ -class PluralSelectorContext { -public: - PluralSelectorContext(int32_t start, const UnicodeString &name, - const Formattable &num, double off, UErrorCode &errorCode) - : startIndex(start), argName(name), offset(off), - numberArgIndex(-1), formatter(NULL), forReplaceNumber(false) { - // number needs to be set even when select() is not called. - // Keep it as a Number/Formattable: - // For format() methods, and to preserve information (e.g., BigDecimal). - if(off == 0) { - number = num; - } else { - number = num.getDouble(errorCode) - off; - } - } - - // Input values for plural selection with decimals. - int32_t startIndex; - const UnicodeString &argName; - /** argument number - plural offset */ - Formattable number; - double offset; - // Output values for plural selection with decimals. - /** -1 if REPLACE_NUMBER, 0 arg not found, >0 ARG_START index */ - int32_t numberArgIndex; - const Format *formatter; - /** formatted argument number - plural offset */ - UnicodeString numberString; - /** true if number-offset was formatted with the stock number formatter */ - UBool forReplaceNumber; -}; - -} // namespace - -// if argumentNames is NULL, this means arguments is a numeric array. -// arguments can not be NULL. -// We use const void *plNumber rather than const PluralSelectorContext *pluralNumber -// so that we need not declare the PluralSelectorContext in the public header file. -void MessageFormat::format(int32_t msgStart, const void *plNumber, - const Formattable* arguments, - const UnicodeString *argumentNames, - int32_t cnt, - AppendableWrapper& appendTo, - FieldPosition* ignore, - UErrorCode& success) const { - if (U_FAILURE(success)) { - return; - } - - const UnicodeString& msgString = msgPattern.getPatternString(); - int32_t prevIndex = msgPattern.getPart(msgStart).getLimit(); - for (int32_t i = msgStart + 1; U_SUCCESS(success) ; ++i) { - const MessagePattern::Part* part = &msgPattern.getPart(i); - const UMessagePatternPartType type = part->getType(); - int32_t index = part->getIndex(); - appendTo.append(msgString, prevIndex, index - prevIndex); - if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { - return; - } - prevIndex = part->getLimit(); - if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) { - const PluralSelectorContext &pluralNumber = - *static_cast(plNumber); - if(pluralNumber.forReplaceNumber) { - // number-offset was already formatted. - appendTo.formatAndAppend(pluralNumber.formatter, - pluralNumber.number, pluralNumber.numberString, success); - } else { - const NumberFormat* nf = getDefaultNumberFormat(success); - appendTo.formatAndAppend(nf, pluralNumber.number, success); - } - continue; - } - if (type != UMSGPAT_PART_TYPE_ARG_START) { - continue; - } - int32_t argLimit = msgPattern.getLimitPartIndex(i); - UMessagePatternArgType argType = part->getArgType(); - part = &msgPattern.getPart(++i); - const Formattable* arg; - UBool noArg = false; - UnicodeString argName = msgPattern.getSubstring(*part); - if (argumentNames == NULL) { - int32_t argNumber = part->getValue(); // ARG_NUMBER - if (0 <= argNumber && argNumber < cnt) { - arg = arguments + argNumber; - } else { - arg = NULL; - noArg = true; - } - } else { - arg = getArgFromListByName(arguments, argumentNames, cnt, argName); - if (arg == NULL) { - noArg = true; - } - } - ++i; - int32_t prevDestLength = appendTo.length(); - const Format* formatter = NULL; - if (noArg) { - appendTo.append( - UnicodeString(LEFT_CURLY_BRACE).append(argName).append(RIGHT_CURLY_BRACE)); - } else if (arg == NULL) { - appendTo.append(NULL_STRING, 4); - } else if(plNumber!=NULL && - static_cast(plNumber)->numberArgIndex==(i-2)) { - const PluralSelectorContext &pluralNumber = - *static_cast(plNumber); - if(pluralNumber.offset == 0) { - // The number was already formatted with this formatter. - appendTo.formatAndAppend(pluralNumber.formatter, pluralNumber.number, - pluralNumber.numberString, success); - } else { - // Do not use the formatted (number-offset) string for a named argument - // that formats the number without subtracting the offset. - appendTo.formatAndAppend(pluralNumber.formatter, *arg, success); - } - } else if ((formatter = getCachedFormatter(i -2)) != 0) { - // Handles all ArgType.SIMPLE, and formatters from setFormat() and its siblings. - if (dynamic_cast(formatter) || - dynamic_cast(formatter) || - dynamic_cast(formatter)) { - // We only handle nested formats here if they were provided via - // setFormat() or its siblings. Otherwise they are not cached and instead - // handled below according to argType. - UnicodeString subMsgString; - formatter->format(*arg, subMsgString, success); - if (subMsgString.indexOf(LEFT_CURLY_BRACE) >= 0 || - (subMsgString.indexOf(SINGLE_QUOTE) >= 0 && !MessageImpl::jdkAposMode(msgPattern)) - ) { - MessageFormat subMsgFormat(subMsgString, fLocale, success); - subMsgFormat.format(0, NULL, arguments, argumentNames, cnt, appendTo, ignore, success); - } else { - appendTo.append(subMsgString); - } - } else { - appendTo.formatAndAppend(formatter, *arg, success); - } - } else if (argType == UMSGPAT_ARG_TYPE_NONE || (cachedFormatters && uhash_iget(cachedFormatters, i - 2))) { - // We arrive here if getCachedFormatter returned NULL, but there was actually an element in the hash table. - // This can only happen if the hash table contained a DummyFormat, so the if statement above is a check - // for the hash table containing DummyFormat. - if (arg->isNumeric()) { - const NumberFormat* nf = getDefaultNumberFormat(success); - appendTo.formatAndAppend(nf, *arg, success); - } else if (arg->getType() == Formattable::kDate) { - const DateFormat* df = getDefaultDateFormat(success); - appendTo.formatAndAppend(df, *arg, success); - } else { - appendTo.append(arg->getString(success)); - } - } else if (argType == UMSGPAT_ARG_TYPE_CHOICE) { - if (!arg->isNumeric()) { - success = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - // We must use the Formattable::getDouble() variant with the UErrorCode parameter - // because only this one converts non-double numeric types to double. - const double number = arg->getDouble(success); - int32_t subMsgStart = ChoiceFormat::findSubMessage(msgPattern, i, number); - formatComplexSubMessage(subMsgStart, NULL, arguments, argumentNames, - cnt, appendTo, success); - } else if (UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType)) { - if (!arg->isNumeric()) { - success = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - const PluralSelectorProvider &selector = - argType == UMSGPAT_ARG_TYPE_PLURAL ? pluralProvider : ordinalProvider; - // We must use the Formattable::getDouble() variant with the UErrorCode parameter - // because only this one converts non-double numeric types to double. - double offset = msgPattern.getPluralOffset(i); - PluralSelectorContext context(i, argName, *arg, offset, success); - int32_t subMsgStart = PluralFormat::findSubMessage( - msgPattern, i, selector, &context, arg->getDouble(success), success); - formatComplexSubMessage(subMsgStart, &context, arguments, argumentNames, - cnt, appendTo, success); - } else if (argType == UMSGPAT_ARG_TYPE_SELECT) { - int32_t subMsgStart = SelectFormat::findSubMessage(msgPattern, i, arg->getString(success), success); - formatComplexSubMessage(subMsgStart, NULL, arguments, argumentNames, - cnt, appendTo, success); - } else { - // This should never happen. - success = U_INTERNAL_PROGRAM_ERROR; - return; - } - ignore = updateMetaData(appendTo, prevDestLength, ignore, arg); - prevIndex = msgPattern.getPart(argLimit).getLimit(); - i = argLimit; - } -} - - -void MessageFormat::formatComplexSubMessage(int32_t msgStart, - const void *plNumber, - const Formattable* arguments, - const UnicodeString *argumentNames, - int32_t cnt, - AppendableWrapper& appendTo, - UErrorCode& success) const { - if (U_FAILURE(success)) { - return; - } - - if (!MessageImpl::jdkAposMode(msgPattern)) { - format(msgStart, plNumber, arguments, argumentNames, cnt, appendTo, NULL, success); - return; - } - - // JDK compatibility mode: (see JDK MessageFormat.format() API docs) - // - remove SKIP_SYNTAX; that is, remove half of the apostrophes - // - if the result string contains an open curly brace '{' then - // instantiate a temporary MessageFormat object and format again; - // otherwise just append the result string - const UnicodeString& msgString = msgPattern.getPatternString(); - UnicodeString sb; - int32_t prevIndex = msgPattern.getPart(msgStart).getLimit(); - for (int32_t i = msgStart;;) { - const MessagePattern::Part& part = msgPattern.getPart(++i); - const UMessagePatternPartType type = part.getType(); - int32_t index = part.getIndex(); - if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { - sb.append(msgString, prevIndex, index - prevIndex); - break; - } else if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER || type == UMSGPAT_PART_TYPE_SKIP_SYNTAX) { - sb.append(msgString, prevIndex, index - prevIndex); - if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) { - const PluralSelectorContext &pluralNumber = - *static_cast(plNumber); - if(pluralNumber.forReplaceNumber) { - // number-offset was already formatted. - sb.append(pluralNumber.numberString); - } else { - const NumberFormat* nf = getDefaultNumberFormat(success); - sb.append(nf->format(pluralNumber.number, sb, success)); - } - } - prevIndex = part.getLimit(); - } else if (type == UMSGPAT_PART_TYPE_ARG_START) { - sb.append(msgString, prevIndex, index - prevIndex); - prevIndex = index; - i = msgPattern.getLimitPartIndex(i); - index = msgPattern.getPart(i).getLimit(); - MessageImpl::appendReducedApostrophes(msgString, prevIndex, index, sb); - prevIndex = index; - } - } - if (sb.indexOf(LEFT_CURLY_BRACE) >= 0) { - UnicodeString emptyPattern; // gcc 3.3.3 fails with "UnicodeString()" as the first parameter. - MessageFormat subMsgFormat(emptyPattern, fLocale, success); - subMsgFormat.applyPattern(sb, UMSGPAT_APOS_DOUBLE_REQUIRED, NULL, success); - subMsgFormat.format(0, NULL, arguments, argumentNames, cnt, appendTo, NULL, success); - } else { - appendTo.append(sb); - } -} - - -UnicodeString MessageFormat::getLiteralStringUntilNextArgument(int32_t from) const { - const UnicodeString& msgString=msgPattern.getPatternString(); - int32_t prevIndex=msgPattern.getPart(from).getLimit(); - UnicodeString b; - for (int32_t i = from + 1; ; ++i) { - const MessagePattern::Part& part = msgPattern.getPart(i); - const UMessagePatternPartType type=part.getType(); - int32_t index=part.getIndex(); - b.append(msgString, prevIndex, index - prevIndex); - if(type==UMSGPAT_PART_TYPE_ARG_START || type==UMSGPAT_PART_TYPE_MSG_LIMIT) { - return b; - } - // Unexpected Part "part" in parsed message. - U_ASSERT(type==UMSGPAT_PART_TYPE_SKIP_SYNTAX || type==UMSGPAT_PART_TYPE_INSERT_CHAR); - prevIndex=part.getLimit(); - } -} - - -FieldPosition* MessageFormat::updateMetaData(AppendableWrapper& /*dest*/, int32_t /*prevLength*/, - FieldPosition* /*fp*/, const Formattable* /*argId*/) const { - // Unlike in Java, there are no field attributes defined for MessageFormat. Do nothing. - return NULL; - /* - if (fp != NULL && Field.ARGUMENT.equals(fp.getFieldAttribute())) { - fp->setBeginIndex(prevLength); - fp->setEndIndex(dest.get_length()); - return NULL; - } - return fp; - */ -} - -int32_t -MessageFormat::findOtherSubMessage(int32_t partIndex) const { - int32_t count=msgPattern.countParts(); - const MessagePattern::Part *part = &msgPattern.getPart(partIndex); - if(MessagePattern::Part::hasNumericValue(part->getType())) { - ++partIndex; - } - // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples - // until ARG_LIMIT or end of plural-only pattern. - UnicodeString other(false, OTHER_STRING, 5); - do { - part=&msgPattern.getPart(partIndex++); - UMessagePatternPartType type=part->getType(); - if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) { - break; - } - U_ASSERT(type==UMSGPAT_PART_TYPE_ARG_SELECTOR); - // part is an ARG_SELECTOR followed by an optional explicit value, and then a message - if(msgPattern.partSubstringMatches(*part, other)) { - return partIndex; - } - if(MessagePattern::Part::hasNumericValue(msgPattern.getPartType(partIndex))) { - ++partIndex; // skip the numeric-value part of "=1" etc. - } - partIndex=msgPattern.getLimitPartIndex(partIndex); - } while(++partIndex 0) { - if (!allocateArgTypes(argTypeCount, ec)) { - return; - } - uprv_memcpy(argTypes, that.argTypes, argTypeCount * sizeof(argTypes[0])); - } - if (cachedFormatters != NULL) { - uhash_removeAll(cachedFormatters); - } - if (customFormatArgStarts != NULL) { - uhash_removeAll(customFormatArgStarts); - } - if (that.cachedFormatters) { - if (cachedFormatters == NULL) { - cachedFormatters=uhash_open(uhash_hashLong, uhash_compareLong, - equalFormatsForHash, &ec); - if (U_FAILURE(ec)) { - return; - } - uhash_setValueDeleter(cachedFormatters, uprv_deleteUObject); - } - - const int32_t count = uhash_count(that.cachedFormatters); - int32_t pos, idx; - for (idx = 0, pos = UHASH_FIRST; idx < count && U_SUCCESS(ec); ++idx) { - const UHashElement* cur = uhash_nextElement(that.cachedFormatters, &pos); - Format* newFormat = ((Format*)(cur->value.pointer))->clone(); - if (newFormat) { - uhash_iput(cachedFormatters, cur->key.integer, newFormat, &ec); - } else { - ec = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - } - if (that.customFormatArgStarts) { - if (customFormatArgStarts == NULL) { - customFormatArgStarts=uhash_open(uhash_hashLong, uhash_compareLong, - NULL, &ec); - } - const int32_t count = uhash_count(that.customFormatArgStarts); - int32_t pos, idx; - for (idx = 0, pos = UHASH_FIRST; idx < count && U_SUCCESS(ec); ++idx) { - const UHashElement* cur = uhash_nextElement(that.customFormatArgStarts, &pos); - uhash_iputi(customFormatArgStarts, cur->key.integer, cur->value.integer, &ec); - } - } -} - - -Formattable* -MessageFormat::parse(int32_t msgStart, - const UnicodeString& source, - ParsePosition& pos, - int32_t& count, - UErrorCode& ec) const { - count = 0; - if (U_FAILURE(ec)) { - pos.setErrorIndex(pos.getIndex()); - return NULL; - } - // parse() does not work with named arguments. - if (msgPattern.hasNamedArguments()) { - ec = U_ARGUMENT_TYPE_MISMATCH; - pos.setErrorIndex(pos.getIndex()); - return NULL; - } - LocalArray resultArray(new Formattable[argTypeCount ? argTypeCount : 1]); - const UnicodeString& msgString=msgPattern.getPatternString(); - int32_t prevIndex=msgPattern.getPart(msgStart).getLimit(); - int32_t sourceOffset = pos.getIndex(); - ParsePosition tempStatus(0); - - for(int32_t i=msgStart+1; ; ++i) { - UBool haveArgResult = false; - const MessagePattern::Part* part=&msgPattern.getPart(i); - const UMessagePatternPartType type=part->getType(); - int32_t index=part->getIndex(); - // Make sure the literal string matches. - int32_t len = index - prevIndex; - if (len == 0 || (0 == msgString.compare(prevIndex, len, source, sourceOffset, len))) { - sourceOffset += len; - prevIndex += len; - } else { - pos.setErrorIndex(sourceOffset); - return NULL; // leave index as is to signal error - } - if(type==UMSGPAT_PART_TYPE_MSG_LIMIT) { - // Things went well! Done. - pos.setIndex(sourceOffset); - return resultArray.orphan(); - } - if(type==UMSGPAT_PART_TYPE_SKIP_SYNTAX || type==UMSGPAT_PART_TYPE_INSERT_CHAR) { - prevIndex=part->getLimit(); - continue; - } - // We do not support parsing Plural formats. (No REPLACE_NUMBER here.) - // Unexpected Part "part" in parsed message. - U_ASSERT(type==UMSGPAT_PART_TYPE_ARG_START); - int32_t argLimit=msgPattern.getLimitPartIndex(i); - - UMessagePatternArgType argType=part->getArgType(); - part=&msgPattern.getPart(++i); - int32_t argNumber = part->getValue(); // ARG_NUMBER - UnicodeString key; - ++i; - const Format* formatter = NULL; - Formattable& argResult = resultArray[argNumber]; - - if(cachedFormatters!=NULL && (formatter = getCachedFormatter(i - 2))!=NULL) { - // Just parse using the formatter. - tempStatus.setIndex(sourceOffset); - formatter->parseObject(source, argResult, tempStatus); - if (tempStatus.getIndex() == sourceOffset) { - pos.setErrorIndex(sourceOffset); - return NULL; // leave index as is to signal error - } - sourceOffset = tempStatus.getIndex(); - haveArgResult = true; - } else if( - argType==UMSGPAT_ARG_TYPE_NONE || (cachedFormatters && uhash_iget(cachedFormatters, i -2))) { - // We arrive here if getCachedFormatter returned NULL, but there was actually an element in the hash table. - // This can only happen if the hash table contained a DummyFormat, so the if statement above is a check - // for the hash table containing DummyFormat. - - // Match as a string. - // if at end, use longest possible match - // otherwise uses first match to intervening string - // does NOT recursively try all possibilities - UnicodeString stringAfterArgument = getLiteralStringUntilNextArgument(argLimit); - int32_t next; - if (!stringAfterArgument.isEmpty()) { - next = source.indexOf(stringAfterArgument, sourceOffset); - } else { - next = source.length(); - } - if (next < 0) { - pos.setErrorIndex(sourceOffset); - return NULL; // leave index as is to signal error - } else { - UnicodeString strValue(source.tempSubString(sourceOffset, next - sourceOffset)); - UnicodeString compValue; - compValue.append(LEFT_CURLY_BRACE); - itos(argNumber, compValue); - compValue.append(RIGHT_CURLY_BRACE); - if (0 != strValue.compare(compValue)) { - argResult.setString(strValue); - haveArgResult = true; - } - sourceOffset = next; - } - } else if(argType==UMSGPAT_ARG_TYPE_CHOICE) { - tempStatus.setIndex(sourceOffset); - double choiceResult = ChoiceFormat::parseArgument(msgPattern, i, source, tempStatus); - if (tempStatus.getIndex() == sourceOffset) { - pos.setErrorIndex(sourceOffset); - return NULL; // leave index as is to signal error - } - argResult.setDouble(choiceResult); - haveArgResult = true; - sourceOffset = tempStatus.getIndex(); - } else if(UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType) || argType==UMSGPAT_ARG_TYPE_SELECT) { - // Parsing not supported. - ec = U_UNSUPPORTED_ERROR; - return NULL; - } else { - // This should never happen. - ec = U_INTERNAL_PROGRAM_ERROR; - return NULL; - } - if (haveArgResult && count <= argNumber) { - count = argNumber + 1; - } - prevIndex=msgPattern.getPart(argLimit).getLimit(); - i=argLimit; - } -} -// ------------------------------------- -// Parses the source pattern and returns the Formattable objects array, -// the array count and the ending parse position. The caller of this method -// owns the array. - -Formattable* -MessageFormat::parse(const UnicodeString& source, - ParsePosition& pos, - int32_t& count) const { - UErrorCode ec = U_ZERO_ERROR; - return parse(0, source, pos, count, ec); -} - -// ------------------------------------- -// Parses the source string and returns the array of -// Formattable objects and the array count. The caller -// owns the returned array. - -Formattable* -MessageFormat::parse(const UnicodeString& source, - int32_t& cnt, - UErrorCode& success) const -{ - if (msgPattern.hasNamedArguments()) { - success = U_ARGUMENT_TYPE_MISMATCH; - return NULL; - } - ParsePosition status(0); - // Calls the actual implementation method and starts - // from zero offset of the source text. - Formattable* result = parse(source, status, cnt); - if (status.getIndex() == 0) { - success = U_MESSAGE_PARSE_ERROR; - delete[] result; - return NULL; - } - return result; -} - -// ------------------------------------- -// Parses the source text and copy into the result buffer. - -void -MessageFormat::parseObject( const UnicodeString& source, - Formattable& result, - ParsePosition& status) const -{ - int32_t cnt = 0; - Formattable* tmpResult = parse(source, status, cnt); - if (tmpResult != NULL) - result.adoptArray(tmpResult, cnt); -} - -UnicodeString -MessageFormat::autoQuoteApostrophe(const UnicodeString& pattern, UErrorCode& status) { - UnicodeString result; - if (U_SUCCESS(status)) { - int32_t plen = pattern.length(); - const UChar* pat = pattern.getBuffer(); - int32_t blen = plen * 2 + 1; // space for null termination, convenience - UChar* buf = result.getBuffer(blen); - if (buf == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - int32_t len = umsg_autoQuoteApostrophe(pat, plen, buf, blen, &status); - result.releaseBuffer(U_SUCCESS(status) ? len : 0); - } - } - if (U_FAILURE(status)) { - result.setToBogus(); - } - return result; -} - -// ------------------------------------- - -static Format* makeRBNF(URBNFRuleSetTag tag, const Locale& locale, const UnicodeString& defaultRuleSet, UErrorCode& ec) { - RuleBasedNumberFormat* fmt = new RuleBasedNumberFormat(tag, locale, ec); - if (fmt == NULL) { - ec = U_MEMORY_ALLOCATION_ERROR; - } else if (U_SUCCESS(ec) && defaultRuleSet.length() > 0) { - UErrorCode localStatus = U_ZERO_ERROR; // ignore unrecognized default rule set - fmt->setDefaultRuleSet(defaultRuleSet, localStatus); - } - return fmt; -} - -void MessageFormat::cacheExplicitFormats(UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - - if (cachedFormatters != NULL) { - uhash_removeAll(cachedFormatters); - } - if (customFormatArgStarts != NULL) { - uhash_removeAll(customFormatArgStarts); - } - - // The last two "parts" can at most be ARG_LIMIT and MSG_LIMIT - // which we need not examine. - int32_t limit = msgPattern.countParts() - 2; - argTypeCount = 0; - // We also need not look at the first two "parts" - // (at most MSG_START and ARG_START) in this loop. - // We determine the argTypeCount first so that we can allocateArgTypes - // so that the next loop can set argTypes[argNumber]. - // (This is for the C API which needs the argTypes to read its va_arg list.) - for (int32_t i = 2; i < limit && U_SUCCESS(status); ++i) { - const MessagePattern::Part& part = msgPattern.getPart(i); - if (part.getType() == UMSGPAT_PART_TYPE_ARG_NUMBER) { - const int argNumber = part.getValue(); - if (argNumber >= argTypeCount) { - argTypeCount = argNumber + 1; - } - } - } - if (!allocateArgTypes(argTypeCount, status)) { - return; - } - // Set all argTypes to kObject, as a "none" value, for lack of any better value. - // We never use kObject for real arguments. - // We use it as "no argument yet" for the check for hasArgTypeConflicts. - for (int32_t i = 0; i < argTypeCount; ++i) { - argTypes[i] = Formattable::kObject; - } - hasArgTypeConflicts = false; - - // This loop starts at part index 1 because we do need to examine - // ARG_START parts. (But we can ignore the MSG_START.) - for (int32_t i = 1; i < limit && U_SUCCESS(status); ++i) { - const MessagePattern::Part* part = &msgPattern.getPart(i); - if (part->getType() != UMSGPAT_PART_TYPE_ARG_START) { - continue; - } - UMessagePatternArgType argType = part->getArgType(); - - int32_t argNumber = -1; - part = &msgPattern.getPart(i + 1); - if (part->getType() == UMSGPAT_PART_TYPE_ARG_NUMBER) { - argNumber = part->getValue(); - } - Formattable::Type formattableType; - - switch (argType) { - case UMSGPAT_ARG_TYPE_NONE: - formattableType = Formattable::kString; - break; - case UMSGPAT_ARG_TYPE_SIMPLE: { - int32_t index = i; - i += 2; - UnicodeString explicitType = msgPattern.getSubstring(msgPattern.getPart(i++)); - UnicodeString style; - if ((part = &msgPattern.getPart(i))->getType() == UMSGPAT_PART_TYPE_ARG_STYLE) { - style = msgPattern.getSubstring(*part); - ++i; - } - UParseError parseError; - Format* formatter = createAppropriateFormat(explicitType, style, formattableType, parseError, status); - setArgStartFormat(index, formatter, status); - break; - } - case UMSGPAT_ARG_TYPE_CHOICE: - case UMSGPAT_ARG_TYPE_PLURAL: - case UMSGPAT_ARG_TYPE_SELECTORDINAL: - formattableType = Formattable::kDouble; - break; - case UMSGPAT_ARG_TYPE_SELECT: - formattableType = Formattable::kString; - break; - default: - status = U_INTERNAL_PROGRAM_ERROR; // Should be unreachable. - formattableType = Formattable::kString; - break; - } - if (argNumber != -1) { - if (argTypes[argNumber] != Formattable::kObject && argTypes[argNumber] != formattableType) { - hasArgTypeConflicts = true; - } - argTypes[argNumber] = formattableType; - } - } -} - -Format* MessageFormat::createAppropriateFormat(UnicodeString& type, UnicodeString& style, - Formattable::Type& formattableType, UParseError& parseError, - UErrorCode& ec) { - if (U_FAILURE(ec)) { - return NULL; - } - Format* fmt = NULL; - int32_t typeID, styleID; - DateFormat::EStyle date_style; - int32_t firstNonSpace; - - switch (typeID = findKeyword(type, TYPE_IDS)) { - case 0: // number - formattableType = Formattable::kDouble; - switch (findKeyword(style, NUMBER_STYLE_IDS)) { - case 0: // default - fmt = NumberFormat::createInstance(fLocale, ec); - break; - case 1: // currency - fmt = NumberFormat::createCurrencyInstance(fLocale, ec); - break; - case 2: // percent - fmt = NumberFormat::createPercentInstance(fLocale, ec); - break; - case 3: // integer - formattableType = Formattable::kLong; - fmt = createIntegerFormat(fLocale, ec); - break; - default: // pattern or skeleton - firstNonSpace = PatternProps::skipWhiteSpace(style, 0); - if (style.compare(firstNonSpace, 2, u"::", 0, 2) == 0) { - // Skeleton - UnicodeString skeleton = style.tempSubString(firstNonSpace + 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; - } - break; - - case 1: // date - case 2: // time - formattableType = Formattable::kDate; - firstNonSpace = PatternProps::skipWhiteSpace(style, 0); - if (style.compare(firstNonSpace, 2, u"::", 0, 2) == 0) { - // Skeleton - UnicodeString skeleton = style.tempSubString(firstNonSpace + 2); - fmt = DateFormat::createInstanceForSkeleton(skeleton, fLocale, ec); - } else { - // Pattern - styleID = findKeyword(style, DATE_STYLE_IDS); - date_style = (styleID >= 0) ? DATE_STYLES[styleID] : DateFormat::kDefault; - - if (typeID == 1) { - fmt = DateFormat::createDateInstance(date_style, fLocale); - } else { - fmt = DateFormat::createTimeInstance(date_style, fLocale); - } - - if (styleID < 0 && fmt != NULL) { - SimpleDateFormat* sdtfmt = dynamic_cast(fmt); - if (sdtfmt != NULL) { - sdtfmt->applyPattern(style); - } - } - } - break; - - case 3: // spellout - formattableType = Formattable::kDouble; - fmt = makeRBNF(URBNF_SPELLOUT, fLocale, style, ec); - break; - case 4: // ordinal - formattableType = Formattable::kDouble; - fmt = makeRBNF(URBNF_ORDINAL, fLocale, style, ec); - break; - case 5: // duration - formattableType = Formattable::kDouble; - fmt = makeRBNF(URBNF_DURATION, fLocale, style, ec); - break; - default: - formattableType = Formattable::kString; - ec = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - - return fmt; -} - - -//------------------------------------- -// Finds the string, s, in the string array, list. -int32_t MessageFormat::findKeyword(const UnicodeString& s, - const UChar * const *list) -{ - if (s.isEmpty()) { - return 0; // default - } - - int32_t length = s.length(); - const UChar *ps = PatternProps::trimWhiteSpace(s.getBuffer(), length); - UnicodeString buffer(false, ps, length); - // Trims the space characters and turns all characters - // in s to lower case. - buffer.toLower(""); - for (int32_t i = 0; list[i]; ++i) { - if (!buffer.compare(list[i], u_strlen(list[i]))) { - return i; - } - } - return -1; -} - -/** - * Convenience method that ought to be in NumberFormat - */ -NumberFormat* -MessageFormat::createIntegerFormat(const Locale& locale, UErrorCode& status) const { - NumberFormat *temp = NumberFormat::createInstance(locale, status); - DecimalFormat *temp2; - if (temp != NULL && (temp2 = dynamic_cast(temp)) != NULL) { - temp2->setMaximumFractionDigits(0); - temp2->setDecimalSeparatorAlwaysShown(false); - temp2->setParseIntegerOnly(true); - } - - return temp; -} - -/** - * Return the default number format. Used to format a numeric - * argument when subformats[i].format is NULL. Returns NULL - * on failure. - * - * Semantically const but may modify *this. - */ -const NumberFormat* MessageFormat::getDefaultNumberFormat(UErrorCode& ec) const { - if (defaultNumberFormat == NULL) { - MessageFormat* t = (MessageFormat*) this; - t->defaultNumberFormat = NumberFormat::createInstance(fLocale, ec); - if (U_FAILURE(ec)) { - delete t->defaultNumberFormat; - t->defaultNumberFormat = NULL; - } else if (t->defaultNumberFormat == NULL) { - ec = U_MEMORY_ALLOCATION_ERROR; - } - } - return defaultNumberFormat; -} - -/** - * Return the default date format. Used to format a date - * argument when subformats[i].format is NULL. Returns NULL - * on failure. - * - * Semantically const but may modify *this. - */ -const DateFormat* MessageFormat::getDefaultDateFormat(UErrorCode& ec) const { - if (defaultDateFormat == NULL) { - MessageFormat* t = (MessageFormat*) this; - t->defaultDateFormat = DateFormat::createDateTimeInstance(DateFormat::kShort, DateFormat::kShort, fLocale); - if (t->defaultDateFormat == NULL) { - ec = U_MEMORY_ALLOCATION_ERROR; - } - } - return defaultDateFormat; -} - -UBool -MessageFormat::usesNamedArguments() const { - return msgPattern.hasNamedArguments(); -} - -int32_t -MessageFormat::getArgTypeCount() const { - return argTypeCount; -} - -UBool MessageFormat::equalFormats(const void* left, const void* right) { - return *(const Format*)left==*(const Format*)right; -} - - -bool MessageFormat::DummyFormat::operator==(const Format&) const { - return true; -} - -MessageFormat::DummyFormat* MessageFormat::DummyFormat::clone() const { - return new DummyFormat(); -} - -UnicodeString& MessageFormat::DummyFormat::format(const Formattable&, - UnicodeString& appendTo, - UErrorCode& status) const { - if (U_SUCCESS(status)) { - status = U_UNSUPPORTED_ERROR; - } - return appendTo; -} - -UnicodeString& MessageFormat::DummyFormat::format(const Formattable&, - UnicodeString& appendTo, - FieldPosition&, - UErrorCode& status) const { - if (U_SUCCESS(status)) { - status = U_UNSUPPORTED_ERROR; - } - return appendTo; -} - -UnicodeString& MessageFormat::DummyFormat::format(const Formattable&, - UnicodeString& appendTo, - FieldPositionIterator*, - UErrorCode& status) const { - if (U_SUCCESS(status)) { - status = U_UNSUPPORTED_ERROR; - } - return appendTo; -} - -void MessageFormat::DummyFormat::parseObject(const UnicodeString&, - Formattable&, - ParsePosition& ) const { -} - - -FormatNameEnumeration::FormatNameEnumeration(LocalPointer nameList, UErrorCode& /*status*/) { - pos=0; - fFormatNames = std::move(nameList); -} - -const UnicodeString* -FormatNameEnumeration::snext(UErrorCode& status) { - if (U_SUCCESS(status) && pos < fFormatNames->size()) { - return (const UnicodeString*)fFormatNames->elementAt(pos++); - } - return NULL; -} - -void -FormatNameEnumeration::reset(UErrorCode& /*status*/) { - pos=0; -} - -int32_t -FormatNameEnumeration::count(UErrorCode& /*status*/) const { - return (fFormatNames==NULL) ? 0 : fFormatNames->size(); -} - -FormatNameEnumeration::~FormatNameEnumeration() { -} - -MessageFormat::PluralSelectorProvider::PluralSelectorProvider(const MessageFormat &mf, UPluralType t) - : msgFormat(mf), rules(NULL), type(t) { -} - -MessageFormat::PluralSelectorProvider::~PluralSelectorProvider() { - delete rules; -} - -UnicodeString MessageFormat::PluralSelectorProvider::select(void *ctx, double number, - UErrorCode& ec) const { - if (U_FAILURE(ec)) { - return UnicodeString(false, OTHER_STRING, 5); - } - MessageFormat::PluralSelectorProvider* t = const_cast(this); - if(rules == NULL) { - t->rules = PluralRules::forLocale(msgFormat.fLocale, type, ec); - if (U_FAILURE(ec)) { - return UnicodeString(false, OTHER_STRING, 5); - } - } - // Select a sub-message according to how the number is formatted, - // which is specified in the selected sub-message. - // We avoid this circle by looking at how - // the number is formatted in the "other" sub-message - // which must always be present and usually contains the number. - // Message authors should be consistent across sub-messages. - PluralSelectorContext &context = *static_cast(ctx); - int32_t otherIndex = msgFormat.findOtherSubMessage(context.startIndex); - context.numberArgIndex = msgFormat.findFirstPluralNumberArg(otherIndex, context.argName); - if(context.numberArgIndex > 0 && msgFormat.cachedFormatters != NULL) { - context.formatter = - (const Format*)uhash_iget(msgFormat.cachedFormatters, context.numberArgIndex); - } - if(context.formatter == NULL) { - context.formatter = msgFormat.getDefaultNumberFormat(ec); - context.forReplaceNumber = true; - } - if (context.number.getDouble(ec) != number) { - ec = U_INTERNAL_PROGRAM_ERROR; - return UnicodeString(false, OTHER_STRING, 5); - } - context.formatter->format(context.number, context.numberString, ec); - auto* decFmt = dynamic_cast(context.formatter); - if(decFmt != NULL) { - number::impl::DecimalQuantity dq; - decFmt->formatToDecimalQuantity(context.number, dq, ec); - if (U_FAILURE(ec)) { - return UnicodeString(false, OTHER_STRING, 5); - } - return rules->select(dq); - } else { - return rules->select(number); - } -} - -void MessageFormat::PluralSelectorProvider::reset() { - delete rules; - rules = NULL; -} - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 1997-2015, International Business Machines Corporation and + * others. All Rights Reserved. + ******************************************************************** + * + * File MSGFMT.CPP + * + * Modification History: + * + * Date Name Description + * 02/19/97 aliu Converted from java. + * 03/20/97 helena Finished first cut of implementation. + * 04/10/97 aliu Made to work on AIX. Added stoi to replace wtoi. + * 06/11/97 helena Fixed addPattern to take the pattern correctly. + * 06/17/97 helena Fixed the getPattern to return the correct pattern. + * 07/09/97 helena Made ParsePosition into a class. + * 02/22/99 stephen Removed character literals for EBCDIC safety + * 11/01/09 kirtig Added SelectFormat + ********************************************************************/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/appendable.h" +#include "unicode/choicfmt.h" +#include "unicode/datefmt.h" +#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" +#include "unicode/smpdtfmt.h" +#include "unicode/umsg.h" +#include "unicode/ustring.h" +#include "cmemory.h" +#include "patternprops.h" +#include "messageimpl.h" +#include "msgfmt_impl.h" +#include "plurrule_impl.h" +#include "uassert.h" +#include "uelement.h" +#include "uhash.h" +#include "ustrfmt.h" +#include "util.h" +#include "uvector.h" +#include "number_decimalquantity.h" + +// ***************************************************************************** +// class MessageFormat +// ***************************************************************************** + +#define SINGLE_QUOTE ((char16_t)0x0027) +#define COMMA ((char16_t)0x002C) +#define LEFT_CURLY_BRACE ((char16_t)0x007B) +#define RIGHT_CURLY_BRACE ((char16_t)0x007D) + +//--------------------------------------- +// static data + +static const char16_t ID_NUMBER[] = { + 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0 /* "number" */ +}; +static const char16_t ID_DATE[] = { + 0x64, 0x61, 0x74, 0x65, 0 /* "date" */ +}; +static const char16_t ID_TIME[] = { + 0x74, 0x69, 0x6D, 0x65, 0 /* "time" */ +}; +static const char16_t ID_SPELLOUT[] = { + 0x73, 0x70, 0x65, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0 /* "spellout" */ +}; +static const char16_t ID_ORDINAL[] = { + 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0 /* "ordinal" */ +}; +static const char16_t ID_DURATION[] = { + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0 /* "duration" */ +}; + +// MessageFormat Type List Number, Date, Time or Choice +static const char16_t * const TYPE_IDS[] = { + ID_NUMBER, + ID_DATE, + ID_TIME, + ID_SPELLOUT, + ID_ORDINAL, + ID_DURATION, + nullptr, +}; + +static const char16_t ID_EMPTY[] = { + 0 /* empty string, used for default so that null can mark end of list */ +}; +static const char16_t ID_CURRENCY[] = { + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x63, 0x79, 0 /* "currency" */ +}; +static const char16_t ID_PERCENT[] = { + 0x70, 0x65, 0x72, 0x63, 0x65, 0x6E, 0x74, 0 /* "percent" */ +}; +static const char16_t ID_INTEGER[] = { + 0x69, 0x6E, 0x74, 0x65, 0x67, 0x65, 0x72, 0 /* "integer" */ +}; + +// NumberFormat modifier list, default, currency, percent or integer +static const char16_t * const NUMBER_STYLE_IDS[] = { + ID_EMPTY, + ID_CURRENCY, + ID_PERCENT, + ID_INTEGER, + nullptr, +}; + +static const char16_t ID_SHORT[] = { + 0x73, 0x68, 0x6F, 0x72, 0x74, 0 /* "short" */ +}; +static const char16_t ID_MEDIUM[] = { + 0x6D, 0x65, 0x64, 0x69, 0x75, 0x6D, 0 /* "medium" */ +}; +static const char16_t ID_LONG[] = { + 0x6C, 0x6F, 0x6E, 0x67, 0 /* "long" */ +}; +static const char16_t ID_FULL[] = { + 0x66, 0x75, 0x6C, 0x6C, 0 /* "full" */ +}; + +// DateFormat modifier list, default, short, medium, long or full +static const char16_t * const DATE_STYLE_IDS[] = { + ID_EMPTY, + ID_SHORT, + ID_MEDIUM, + ID_LONG, + ID_FULL, + nullptr, +}; + +static const icu::DateFormat::EStyle DATE_STYLES[] = { + icu::DateFormat::kDefault, + icu::DateFormat::kShort, + icu::DateFormat::kMedium, + icu::DateFormat::kLong, + icu::DateFormat::kFull, +}; + +static const int32_t DEFAULT_INITIAL_CAPACITY = 10; + +static const char16_t NULL_STRING[] = { + 0x6E, 0x75, 0x6C, 0x6C, 0 // "null" +}; + +static const char16_t OTHER_STRING[] = { + 0x6F, 0x74, 0x68, 0x65, 0x72, 0 // "other" +}; + +U_CDECL_BEGIN +static UBool U_CALLCONV equalFormatsForHash(const UHashTok key1, + const UHashTok key2) { + return icu::MessageFormat::equalFormats(key1.pointer, key2.pointer); +} + +U_CDECL_END + +U_NAMESPACE_BEGIN + +// ------------------------------------- +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MessageFormat) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FormatNameEnumeration) + +//-------------------------------------------------------------------- + +/** + * Convert an integer value to a string and append the result to + * the given UnicodeString. + */ +static UnicodeString& itos(int32_t i, UnicodeString& appendTo) { + char16_t temp[16]; + uprv_itou(temp,16,i,10,0); // 10 == radix + appendTo.append(temp, -1); + return appendTo; +} + + +// AppendableWrapper: encapsulates the result of formatting, keeping track +// of the string and its length. +class AppendableWrapper : public UMemory { +public: + AppendableWrapper(Appendable& appendable) : app(appendable), len(0) { + } + void append(const UnicodeString& s) { + app.appendString(s.getBuffer(), s.length()); + len += s.length(); + } + void append(const char16_t* s, const int32_t sLength) { + app.appendString(s, sLength); + len += sLength; + } + void append(const UnicodeString& s, int32_t start, int32_t length) { + append(s.tempSubString(start, length)); + } + void formatAndAppend(const Format* formatter, const Formattable& arg, UErrorCode& ec) { + UnicodeString s; + formatter->format(arg, s, ec); + if (U_SUCCESS(ec)) { + append(s); + } + } + void formatAndAppend(const Format* formatter, const Formattable& arg, + const UnicodeString &argString, UErrorCode& ec) { + if (!argString.isEmpty()) { + if (U_SUCCESS(ec)) { + append(argString); + } + } else { + formatAndAppend(formatter, arg, ec); + } + } + int32_t length() { + return len; + } +private: + Appendable& app; + int32_t len; +}; + + +// ------------------------------------- +// Creates a MessageFormat instance based on the pattern. + +MessageFormat::MessageFormat(const UnicodeString& pattern, + UErrorCode& success) +: fLocale(Locale::getDefault()), // Uses the default locale + msgPattern(success), + formatAliases(nullptr), + formatAliasesCapacity(0), + argTypes(nullptr), + argTypeCount(0), + argTypeCapacity(0), + hasArgTypeConflicts(false), + defaultNumberFormat(nullptr), + defaultDateFormat(nullptr), + cachedFormatters(nullptr), + customFormatArgStarts(nullptr), + pluralProvider(*this, UPLURAL_TYPE_CARDINAL), + ordinalProvider(*this, UPLURAL_TYPE_ORDINAL) +{ + setLocaleIDs(fLocale.getName(), fLocale.getName()); + applyPattern(pattern, success); +} + +MessageFormat::MessageFormat(const UnicodeString& pattern, + const Locale& newLocale, + UErrorCode& success) +: fLocale(newLocale), + msgPattern(success), + formatAliases(nullptr), + formatAliasesCapacity(0), + argTypes(nullptr), + argTypeCount(0), + argTypeCapacity(0), + hasArgTypeConflicts(false), + defaultNumberFormat(nullptr), + defaultDateFormat(nullptr), + cachedFormatters(nullptr), + customFormatArgStarts(nullptr), + pluralProvider(*this, UPLURAL_TYPE_CARDINAL), + ordinalProvider(*this, UPLURAL_TYPE_ORDINAL) +{ + setLocaleIDs(fLocale.getName(), fLocale.getName()); + applyPattern(pattern, success); +} + +MessageFormat::MessageFormat(const UnicodeString& pattern, + const Locale& newLocale, + UParseError& parseError, + UErrorCode& success) +: fLocale(newLocale), + msgPattern(success), + formatAliases(nullptr), + formatAliasesCapacity(0), + argTypes(nullptr), + argTypeCount(0), + argTypeCapacity(0), + hasArgTypeConflicts(false), + defaultNumberFormat(nullptr), + defaultDateFormat(nullptr), + cachedFormatters(nullptr), + customFormatArgStarts(nullptr), + pluralProvider(*this, UPLURAL_TYPE_CARDINAL), + ordinalProvider(*this, UPLURAL_TYPE_ORDINAL) +{ + setLocaleIDs(fLocale.getName(), fLocale.getName()); + applyPattern(pattern, parseError, success); +} + +MessageFormat::MessageFormat(const MessageFormat& that) +: + Format(that), + fLocale(that.fLocale), + msgPattern(that.msgPattern), + formatAliases(nullptr), + formatAliasesCapacity(0), + argTypes(nullptr), + argTypeCount(0), + argTypeCapacity(0), + hasArgTypeConflicts(that.hasArgTypeConflicts), + defaultNumberFormat(nullptr), + defaultDateFormat(nullptr), + cachedFormatters(nullptr), + customFormatArgStarts(nullptr), + pluralProvider(*this, UPLURAL_TYPE_CARDINAL), + ordinalProvider(*this, UPLURAL_TYPE_ORDINAL) +{ + // This will take care of creating the hash tables (since they are nullptr). + UErrorCode ec = U_ZERO_ERROR; + copyObjects(that, ec); + if (U_FAILURE(ec)) { + resetPattern(); + } +} + +MessageFormat::~MessageFormat() +{ + uhash_close(cachedFormatters); + uhash_close(customFormatArgStarts); + + uprv_free(argTypes); + uprv_free(formatAliases); + delete defaultNumberFormat; + delete defaultDateFormat; +} + +//-------------------------------------------------------------------- +// Variable-size array management + +/** + * Allocate argTypes[] to at least the given capacity and return + * true if successful. If not, leave argTypes[] unchanged. + * + * If argTypes is nullptr, allocate it. If it is not nullptr, enlarge it + * if necessary to be at least as large as specified. + */ +UBool MessageFormat::allocateArgTypes(int32_t capacity, UErrorCode& status) { + if (U_FAILURE(status)) { + return false; + } + if (argTypeCapacity >= capacity) { + return true; + } + if (capacity < DEFAULT_INITIAL_CAPACITY) { + capacity = DEFAULT_INITIAL_CAPACITY; + } else if (capacity < 2*argTypeCapacity) { + capacity = 2*argTypeCapacity; + } + Formattable::Type* a = (Formattable::Type*) + uprv_realloc(argTypes, sizeof(*argTypes) * capacity); + if (a == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return false; + } + argTypes = a; + argTypeCapacity = capacity; + return true; +} + +// ------------------------------------- +// assignment operator + +const MessageFormat& +MessageFormat::operator=(const MessageFormat& that) +{ + if (this != &that) { + // Calls the super class for assignment first. + Format::operator=(that); + + setLocale(that.fLocale); + msgPattern = that.msgPattern; + hasArgTypeConflicts = that.hasArgTypeConflicts; + + UErrorCode ec = U_ZERO_ERROR; + copyObjects(that, ec); + if (U_FAILURE(ec)) { + resetPattern(); + } + } + return *this; +} + +bool +MessageFormat::operator==(const Format& rhs) const +{ + if (this == &rhs) return true; + + // Check class ID before checking MessageFormat members + if (!Format::operator==(rhs)) return false; + + const MessageFormat& that = static_cast(rhs); + if (msgPattern != that.msgPattern || + fLocale != that.fLocale) { + return false; + } + + // Compare hashtables. + if ((customFormatArgStarts == nullptr) != (that.customFormatArgStarts == nullptr)) { + return false; + } + if (customFormatArgStarts == nullptr) { + return true; + } + + UErrorCode ec = U_ZERO_ERROR; + const int32_t count = uhash_count(customFormatArgStarts); + const int32_t rhs_count = uhash_count(that.customFormatArgStarts); + if (count != rhs_count) { + return false; + } + int32_t idx = 0, rhs_idx = 0, pos = UHASH_FIRST, rhs_pos = UHASH_FIRST; + for (; idx < count && rhs_idx < rhs_count && U_SUCCESS(ec); ++idx, ++rhs_idx) { + const UHashElement* cur = uhash_nextElement(customFormatArgStarts, &pos); + const UHashElement* rhs_cur = uhash_nextElement(that.customFormatArgStarts, &rhs_pos); + if (cur->key.integer != rhs_cur->key.integer) { + return false; + } + const Format* format = (const Format*)uhash_iget(cachedFormatters, cur->key.integer); + const Format* rhs_format = (const Format*)uhash_iget(that.cachedFormatters, rhs_cur->key.integer); + if (*format != *rhs_format) { + return false; + } + } + return true; +} + +// ------------------------------------- +// Creates a copy of this MessageFormat, the caller owns the copy. + +MessageFormat* +MessageFormat::clone() const +{ + return new MessageFormat(*this); +} + +// ------------------------------------- +// Sets the locale of this MessageFormat object to theLocale. + +void +MessageFormat::setLocale(const Locale& theLocale) +{ + if (fLocale != theLocale) { + delete defaultNumberFormat; + defaultNumberFormat = nullptr; + delete defaultDateFormat; + defaultDateFormat = nullptr; + fLocale = theLocale; + setLocaleIDs(fLocale.getName(), fLocale.getName()); + pluralProvider.reset(); + ordinalProvider.reset(); + } +} + +// ------------------------------------- +// Gets the locale of this MessageFormat object. + +const Locale& +MessageFormat::getLocale() const +{ + return fLocale; +} + +void +MessageFormat::applyPattern(const UnicodeString& newPattern, + UErrorCode& status) +{ + UParseError parseError; + applyPattern(newPattern,parseError,status); +} + + +// ------------------------------------- +// Applies the new pattern and returns an error if the pattern +// is not correct. +void +MessageFormat::applyPattern(const UnicodeString& pattern, + UParseError& parseError, + UErrorCode& ec) +{ + if(U_FAILURE(ec)) { + return; + } + msgPattern.parse(pattern, &parseError, ec); + cacheExplicitFormats(ec); + + if (U_FAILURE(ec)) { + resetPattern(); + } +} + +void MessageFormat::resetPattern() { + msgPattern.clear(); + uhash_close(cachedFormatters); + cachedFormatters = nullptr; + uhash_close(customFormatArgStarts); + customFormatArgStarts = nullptr; + argTypeCount = 0; + hasArgTypeConflicts = false; +} + +void +MessageFormat::applyPattern(const UnicodeString& pattern, + UMessagePatternApostropheMode aposMode, + UParseError* parseError, + UErrorCode& status) { + if (aposMode != msgPattern.getApostropheMode()) { + msgPattern.clearPatternAndSetApostropheMode(aposMode); + } + UParseError tempParseError; + applyPattern(pattern, (parseError == nullptr) ? tempParseError : *parseError, status); +} + +// ------------------------------------- +// Converts this MessageFormat instance to a pattern. + +UnicodeString& +MessageFormat::toPattern(UnicodeString& appendTo) const { + if ((customFormatArgStarts != nullptr && 0 != uhash_count(customFormatArgStarts)) || + 0 == msgPattern.countParts() + ) { + appendTo.setToBogus(); + return appendTo; + } + return appendTo.append(msgPattern.getPatternString()); +} + +int32_t MessageFormat::nextTopLevelArgStart(int32_t partIndex) const { + if (partIndex != 0) { + partIndex = msgPattern.getLimitPartIndex(partIndex); + } + for (;;) { + UMessagePatternPartType type = msgPattern.getPartType(++partIndex); + if (type == UMSGPAT_PART_TYPE_ARG_START) { + return partIndex; + } + if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { + return -1; + } + } +} + +void MessageFormat::setArgStartFormat(int32_t argStart, + Format* formatter, + UErrorCode& status) { + if (U_FAILURE(status)) { + delete formatter; + return; + } + if (cachedFormatters == nullptr) { + cachedFormatters=uhash_open(uhash_hashLong, uhash_compareLong, + equalFormatsForHash, &status); + if (U_FAILURE(status)) { + delete formatter; + return; + } + uhash_setValueDeleter(cachedFormatters, uprv_deleteUObject); + } + if (formatter == nullptr) { + formatter = new DummyFormat(); + } + uhash_iput(cachedFormatters, argStart, formatter, &status); +} + + +UBool MessageFormat::argNameMatches(int32_t partIndex, const UnicodeString& argName, int32_t argNumber) { + const MessagePattern::Part& part = msgPattern.getPart(partIndex); + return part.getType() == UMSGPAT_PART_TYPE_ARG_NAME ? + msgPattern.partSubstringMatches(part, argName) : + part.getValue() == argNumber; // ARG_NUMBER +} + +// Sets a custom formatter for a MessagePattern ARG_START part index. +// "Custom" formatters are provided by the user via setFormat() or similar APIs. +void MessageFormat::setCustomArgStartFormat(int32_t argStart, + Format* formatter, + UErrorCode& status) { + setArgStartFormat(argStart, formatter, status); + if (customFormatArgStarts == nullptr) { + customFormatArgStarts=uhash_open(uhash_hashLong, uhash_compareLong, + nullptr, &status); + } + uhash_iputi(customFormatArgStarts, argStart, 1, &status); +} + +Format* MessageFormat::getCachedFormatter(int32_t argumentNumber) const { + if (cachedFormatters == nullptr) { + return nullptr; + } + void* ptr = uhash_iget(cachedFormatters, argumentNumber); + if (ptr != nullptr && dynamic_cast((Format*)ptr) == nullptr) { + return (Format*) ptr; + } else { + // Not cached, or a DummyFormat representing setFormat(nullptr). + return nullptr; + } +} + +// ------------------------------------- +// Adopts the new formats array and updates the array count. +// This MessageFormat instance owns the new formats. +void +MessageFormat::adoptFormats(Format** newFormats, + int32_t count) { + if (newFormats == nullptr || count < 0) { + return; + } + // Throw away any cached formatters. + if (cachedFormatters != nullptr) { + uhash_removeAll(cachedFormatters); + } + if (customFormatArgStarts != nullptr) { + uhash_removeAll(customFormatArgStarts); + } + + int32_t formatNumber = 0; + UErrorCode status = U_ZERO_ERROR; + for (int32_t partIndex = 0; + formatNumber < count && U_SUCCESS(status) && + (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { + setCustomArgStartFormat(partIndex, newFormats[formatNumber], status); + ++formatNumber; + } + // Delete those that didn't get used (if any). + for (; formatNumber < count; ++formatNumber) { + delete newFormats[formatNumber]; + } + +} + +// ------------------------------------- +// Sets the new formats array and updates the array count. +// This MessageFormat instance makes a copy of the new formats. + +void +MessageFormat::setFormats(const Format** newFormats, + int32_t count) { + if (newFormats == nullptr || count < 0) { + return; + } + // Throw away any cached formatters. + if (cachedFormatters != nullptr) { + uhash_removeAll(cachedFormatters); + } + if (customFormatArgStarts != nullptr) { + uhash_removeAll(customFormatArgStarts); + } + + UErrorCode status = U_ZERO_ERROR; + int32_t formatNumber = 0; + for (int32_t partIndex = 0; + formatNumber < count && U_SUCCESS(status) && (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { + Format* newFormat = nullptr; + if (newFormats[formatNumber] != nullptr) { + newFormat = newFormats[formatNumber]->clone(); + if (newFormat == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + } + setCustomArgStartFormat(partIndex, newFormat, status); + ++formatNumber; + } + if (U_FAILURE(status)) { + resetPattern(); + } +} + +// ------------------------------------- +// Adopt a single format by format number. +// Do nothing if the format number is not less than the array count. + +void +MessageFormat::adoptFormat(int32_t n, Format *newFormat) { + LocalPointer p(newFormat); + if (n >= 0) { + int32_t formatNumber = 0; + for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { + if (n == formatNumber) { + UErrorCode status = U_ZERO_ERROR; + setCustomArgStartFormat(partIndex, p.orphan(), status); + return; + } + ++formatNumber; + } + } +} + +// ------------------------------------- +// Adopt a single format by format name. +// Do nothing if there is no match of formatName. +void +MessageFormat::adoptFormat(const UnicodeString& formatName, + Format* formatToAdopt, + UErrorCode& status) { + LocalPointer p(formatToAdopt); + if (U_FAILURE(status)) { + return; + } + int32_t argNumber = MessagePattern::validateArgumentName(formatName); + if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + for (int32_t partIndex = 0; + (partIndex = nextTopLevelArgStart(partIndex)) >= 0 && U_SUCCESS(status); + ) { + if (argNameMatches(partIndex + 1, formatName, argNumber)) { + Format* f; + if (p.isValid()) { + f = p.orphan(); + } else if (formatToAdopt == nullptr) { + f = nullptr; + } else { + f = formatToAdopt->clone(); + if (f == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + setCustomArgStartFormat(partIndex, f, status); + } + } +} + +// ------------------------------------- +// Set a single format. +// Do nothing if the variable is not less than the array count. +void +MessageFormat::setFormat(int32_t n, const Format& newFormat) { + + if (n >= 0) { + int32_t formatNumber = 0; + for (int32_t partIndex = 0; + (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { + if (n == formatNumber) { + Format* new_format = newFormat.clone(); + if (new_format) { + UErrorCode status = U_ZERO_ERROR; + setCustomArgStartFormat(partIndex, new_format, status); + } + return; + } + ++formatNumber; + } + } +} + +// ------------------------------------- +// Get a single format by format name. +// Do nothing if the variable is not less than the array count. +Format * +MessageFormat::getFormat(const UnicodeString& formatName, UErrorCode& status) { + if (U_FAILURE(status) || cachedFormatters == nullptr) return nullptr; + + int32_t argNumber = MessagePattern::validateArgumentName(formatName); + if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { + if (argNameMatches(partIndex + 1, formatName, argNumber)) { + return getCachedFormatter(partIndex); + } + } + return nullptr; +} + +// ------------------------------------- +// Set a single format by format name +// Do nothing if the variable is not less than the array count. +void +MessageFormat::setFormat(const UnicodeString& formatName, + const Format& newFormat, + UErrorCode& status) { + if (U_FAILURE(status)) return; + + int32_t argNumber = MessagePattern::validateArgumentName(formatName); + if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + for (int32_t partIndex = 0; + (partIndex = nextTopLevelArgStart(partIndex)) >= 0 && U_SUCCESS(status); + ) { + if (argNameMatches(partIndex + 1, formatName, argNumber)) { + Format* new_format = newFormat.clone(); + if (new_format == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + setCustomArgStartFormat(partIndex, new_format, status); + } + } +} + +// ------------------------------------- +// Gets the format array. +const Format** +MessageFormat::getFormats(int32_t& cnt) const +{ + // This old API returns an array (which we hold) of Format* + // pointers. The array is valid up to the next call to any + // method on this object. We construct and resize an array + // on demand that contains aliases to the subformats[i].format + // pointers. + + // Get total required capacity first (it's refreshed on each call). + int32_t totalCapacity = 0; + for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0; ++totalCapacity) {} + + MessageFormat* t = const_cast (this); + cnt = 0; + if (formatAliases == nullptr) { + t->formatAliasesCapacity = totalCapacity; + Format** a = (Format**) + uprv_malloc(sizeof(Format*) * formatAliasesCapacity); + if (a == nullptr) { + t->formatAliasesCapacity = 0; + return nullptr; + } + t->formatAliases = a; + } else if (totalCapacity > formatAliasesCapacity) { + Format** a = (Format**) + uprv_realloc(formatAliases, sizeof(Format*) * totalCapacity); + if (a == nullptr) { + t->formatAliasesCapacity = 0; + return nullptr; + } + t->formatAliases = a; + t->formatAliasesCapacity = totalCapacity; + } + + for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { + t->formatAliases[cnt++] = getCachedFormatter(partIndex); + } + + return (const Format**)formatAliases; +} + + +UnicodeString MessageFormat::getArgName(int32_t partIndex) { + const MessagePattern::Part& part = msgPattern.getPart(partIndex); + return msgPattern.getSubstring(part); +} + +StringEnumeration* +MessageFormat::getFormatNames(UErrorCode& status) { + if (U_FAILURE(status)) return nullptr; + + LocalPointer formatNames(new UVector(status), status); + if (U_FAILURE(status)) { + return nullptr; + } + formatNames->setDeleter(uprv_deleteUObject); + + for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { + LocalPointer name(getArgName(partIndex + 1).clone(), status); + formatNames->adoptElement(name.orphan(), status); + if (U_FAILURE(status)) return nullptr; + } + + LocalPointer nameEnumerator( + new FormatNameEnumeration(std::move(formatNames), status), status); + return U_SUCCESS(status) ? nameEnumerator.orphan() : nullptr; +} + +// ------------------------------------- +// Formats the source Formattable array and copy into the result buffer. +// Ignore the FieldPosition result for error checking. + +UnicodeString& +MessageFormat::format(const Formattable* source, + int32_t cnt, + UnicodeString& appendTo, + FieldPosition& ignore, + UErrorCode& success) const +{ + return format(source, nullptr, cnt, appendTo, &ignore, success); +} + +// ------------------------------------- +// Internally creates a MessageFormat instance based on the +// pattern and formats the arguments Formattable array and +// copy into the appendTo buffer. + +UnicodeString& +MessageFormat::format( const UnicodeString& pattern, + const Formattable* arguments, + int32_t cnt, + UnicodeString& appendTo, + UErrorCode& success) +{ + MessageFormat temp(pattern, success); + return temp.format(arguments, nullptr, cnt, appendTo, nullptr, success); +} + +// ------------------------------------- +// Formats the source Formattable object and copy into the +// appendTo buffer. The Formattable object must be an array +// of Formattable instances, returns error otherwise. + +UnicodeString& +MessageFormat::format(const Formattable& source, + UnicodeString& appendTo, + FieldPosition& ignore, + UErrorCode& success) const +{ + if (U_FAILURE(success)) + return appendTo; + if (source.getType() != Formattable::kArray) { + success = U_ILLEGAL_ARGUMENT_ERROR; + return appendTo; + } + int32_t cnt; + const Formattable* tmpPtr = source.getArray(cnt); + return format(tmpPtr, nullptr, cnt, appendTo, &ignore, success); +} + +UnicodeString& +MessageFormat::format(const UnicodeString* argumentNames, + const Formattable* arguments, + int32_t count, + UnicodeString& appendTo, + UErrorCode& success) const { + return format(arguments, argumentNames, count, appendTo, nullptr, success); +} + +// Does linear search to find the match for an ArgName. +const Formattable* MessageFormat::getArgFromListByName(const Formattable* arguments, + const UnicodeString *argumentNames, + int32_t cnt, UnicodeString& name) const { + for (int32_t i = 0; i < cnt; ++i) { + if (0 == argumentNames[i].compare(name)) { + return arguments + i; + } + } + return nullptr; +} + + +UnicodeString& +MessageFormat::format(const Formattable* arguments, + const UnicodeString *argumentNames, + int32_t cnt, + UnicodeString& appendTo, + FieldPosition* pos, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return appendTo; + } + + UnicodeStringAppendable usapp(appendTo); + AppendableWrapper app(usapp); + format(0, nullptr, arguments, argumentNames, cnt, app, pos, status); + return appendTo; +} + +namespace { + +/** + * Mutable input/output values for the PluralSelectorProvider. + * Separate so that it is possible to make MessageFormat Freezable. + */ +class PluralSelectorContext { +public: + PluralSelectorContext(int32_t start, const UnicodeString &name, + const Formattable &num, double off, UErrorCode &errorCode) + : startIndex(start), argName(name), offset(off), + numberArgIndex(-1), formatter(nullptr), forReplaceNumber(false) { + // number needs to be set even when select() is not called. + // Keep it as a Number/Formattable: + // For format() methods, and to preserve information (e.g., BigDecimal). + if(off == 0) { + number = num; + } else { + number = num.getDouble(errorCode) - off; + } + } + + // Input values for plural selection with decimals. + int32_t startIndex; + const UnicodeString &argName; + /** argument number - plural offset */ + Formattable number; + double offset; + // Output values for plural selection with decimals. + /** -1 if REPLACE_NUMBER, 0 arg not found, >0 ARG_START index */ + int32_t numberArgIndex; + const Format *formatter; + /** formatted argument number - plural offset */ + UnicodeString numberString; + /** true if number-offset was formatted with the stock number formatter */ + UBool forReplaceNumber; +}; + +} // namespace + +// if argumentNames is nullptr, this means arguments is a numeric array. +// arguments can not be nullptr. +// We use const void *plNumber rather than const PluralSelectorContext *pluralNumber +// so that we need not declare the PluralSelectorContext in the public header file. +void MessageFormat::format(int32_t msgStart, const void *plNumber, + const Formattable* arguments, + const UnicodeString *argumentNames, + int32_t cnt, + AppendableWrapper& appendTo, + FieldPosition* ignore, + UErrorCode& success) const { + if (U_FAILURE(success)) { + return; + } + + const UnicodeString& msgString = msgPattern.getPatternString(); + int32_t prevIndex = msgPattern.getPart(msgStart).getLimit(); + for (int32_t i = msgStart + 1; U_SUCCESS(success) ; ++i) { + const MessagePattern::Part* part = &msgPattern.getPart(i); + const UMessagePatternPartType type = part->getType(); + int32_t index = part->getIndex(); + appendTo.append(msgString, prevIndex, index - prevIndex); + if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { + return; + } + prevIndex = part->getLimit(); + if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) { + const PluralSelectorContext &pluralNumber = + *static_cast(plNumber); + if(pluralNumber.forReplaceNumber) { + // number-offset was already formatted. + appendTo.formatAndAppend(pluralNumber.formatter, + pluralNumber.number, pluralNumber.numberString, success); + } else { + const NumberFormat* nf = getDefaultNumberFormat(success); + appendTo.formatAndAppend(nf, pluralNumber.number, success); + } + continue; + } + if (type != UMSGPAT_PART_TYPE_ARG_START) { + continue; + } + int32_t argLimit = msgPattern.getLimitPartIndex(i); + UMessagePatternArgType argType = part->getArgType(); + part = &msgPattern.getPart(++i); + const Formattable* arg; + UBool noArg = false; + UnicodeString argName = msgPattern.getSubstring(*part); + if (argumentNames == nullptr) { + int32_t argNumber = part->getValue(); // ARG_NUMBER + if (0 <= argNumber && argNumber < cnt) { + arg = arguments + argNumber; + } else { + arg = nullptr; + noArg = true; + } + } else { + arg = getArgFromListByName(arguments, argumentNames, cnt, argName); + if (arg == nullptr) { + noArg = true; + } + } + ++i; + int32_t prevDestLength = appendTo.length(); + const Format* formatter = nullptr; + if (noArg) { + appendTo.append( + UnicodeString(LEFT_CURLY_BRACE).append(argName).append(RIGHT_CURLY_BRACE)); + } else if (arg == nullptr) { + appendTo.append(NULL_STRING, 4); + } else if(plNumber!=nullptr && + static_cast(plNumber)->numberArgIndex==(i-2)) { + const PluralSelectorContext &pluralNumber = + *static_cast(plNumber); + if(pluralNumber.offset == 0) { + // The number was already formatted with this formatter. + appendTo.formatAndAppend(pluralNumber.formatter, pluralNumber.number, + pluralNumber.numberString, success); + } else { + // Do not use the formatted (number-offset) string for a named argument + // that formats the number without subtracting the offset. + appendTo.formatAndAppend(pluralNumber.formatter, *arg, success); + } + } else if ((formatter = getCachedFormatter(i -2)) != 0) { + // Handles all ArgType.SIMPLE, and formatters from setFormat() and its siblings. + if (dynamic_cast(formatter) || + dynamic_cast(formatter) || + dynamic_cast(formatter)) { + // We only handle nested formats here if they were provided via + // setFormat() or its siblings. Otherwise they are not cached and instead + // handled below according to argType. + UnicodeString subMsgString; + formatter->format(*arg, subMsgString, success); + if (subMsgString.indexOf(LEFT_CURLY_BRACE) >= 0 || + (subMsgString.indexOf(SINGLE_QUOTE) >= 0 && !MessageImpl::jdkAposMode(msgPattern)) + ) { + MessageFormat subMsgFormat(subMsgString, fLocale, success); + subMsgFormat.format(0, nullptr, arguments, argumentNames, cnt, appendTo, ignore, success); + } else { + appendTo.append(subMsgString); + } + } else { + appendTo.formatAndAppend(formatter, *arg, success); + } + } else if (argType == UMSGPAT_ARG_TYPE_NONE || (cachedFormatters && uhash_iget(cachedFormatters, i - 2))) { + // We arrive here if getCachedFormatter returned nullptr, but there was actually an element in the hash table. + // This can only happen if the hash table contained a DummyFormat, so the if statement above is a check + // for the hash table containing DummyFormat. + if (arg->isNumeric()) { + const NumberFormat* nf = getDefaultNumberFormat(success); + appendTo.formatAndAppend(nf, *arg, success); + } else if (arg->getType() == Formattable::kDate) { + const DateFormat* df = getDefaultDateFormat(success); + appendTo.formatAndAppend(df, *arg, success); + } else { + appendTo.append(arg->getString(success)); + } + } else if (argType == UMSGPAT_ARG_TYPE_CHOICE) { + if (!arg->isNumeric()) { + success = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + // We must use the Formattable::getDouble() variant with the UErrorCode parameter + // because only this one converts non-double numeric types to double. + const double number = arg->getDouble(success); + int32_t subMsgStart = ChoiceFormat::findSubMessage(msgPattern, i, number); + formatComplexSubMessage(subMsgStart, nullptr, arguments, argumentNames, + cnt, appendTo, success); + } else if (UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType)) { + if (!arg->isNumeric()) { + success = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + const PluralSelectorProvider &selector = + argType == UMSGPAT_ARG_TYPE_PLURAL ? pluralProvider : ordinalProvider; + // We must use the Formattable::getDouble() variant with the UErrorCode parameter + // because only this one converts non-double numeric types to double. + double offset = msgPattern.getPluralOffset(i); + PluralSelectorContext context(i, argName, *arg, offset, success); + int32_t subMsgStart = PluralFormat::findSubMessage( + msgPattern, i, selector, &context, arg->getDouble(success), success); + formatComplexSubMessage(subMsgStart, &context, arguments, argumentNames, + cnt, appendTo, success); + } else if (argType == UMSGPAT_ARG_TYPE_SELECT) { + int32_t subMsgStart = SelectFormat::findSubMessage(msgPattern, i, arg->getString(success), success); + formatComplexSubMessage(subMsgStart, nullptr, arguments, argumentNames, + cnt, appendTo, success); + } else { + // This should never happen. + success = U_INTERNAL_PROGRAM_ERROR; + return; + } + ignore = updateMetaData(appendTo, prevDestLength, ignore, arg); + prevIndex = msgPattern.getPart(argLimit).getLimit(); + i = argLimit; + } +} + + +void MessageFormat::formatComplexSubMessage(int32_t msgStart, + const void *plNumber, + const Formattable* arguments, + const UnicodeString *argumentNames, + int32_t cnt, + AppendableWrapper& appendTo, + UErrorCode& success) const { + if (U_FAILURE(success)) { + return; + } + + if (!MessageImpl::jdkAposMode(msgPattern)) { + format(msgStart, plNumber, arguments, argumentNames, cnt, appendTo, nullptr, success); + return; + } + + // JDK compatibility mode: (see JDK MessageFormat.format() API docs) + // - remove SKIP_SYNTAX; that is, remove half of the apostrophes + // - if the result string contains an open curly brace '{' then + // instantiate a temporary MessageFormat object and format again; + // otherwise just append the result string + const UnicodeString& msgString = msgPattern.getPatternString(); + UnicodeString sb; + int32_t prevIndex = msgPattern.getPart(msgStart).getLimit(); + for (int32_t i = msgStart;;) { + const MessagePattern::Part& part = msgPattern.getPart(++i); + const UMessagePatternPartType type = part.getType(); + int32_t index = part.getIndex(); + if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { + sb.append(msgString, prevIndex, index - prevIndex); + break; + } else if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER || type == UMSGPAT_PART_TYPE_SKIP_SYNTAX) { + sb.append(msgString, prevIndex, index - prevIndex); + if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) { + const PluralSelectorContext &pluralNumber = + *static_cast(plNumber); + if(pluralNumber.forReplaceNumber) { + // number-offset was already formatted. + sb.append(pluralNumber.numberString); + } else { + const NumberFormat* nf = getDefaultNumberFormat(success); + sb.append(nf->format(pluralNumber.number, sb, success)); + } + } + prevIndex = part.getLimit(); + } else if (type == UMSGPAT_PART_TYPE_ARG_START) { + sb.append(msgString, prevIndex, index - prevIndex); + prevIndex = index; + i = msgPattern.getLimitPartIndex(i); + index = msgPattern.getPart(i).getLimit(); + MessageImpl::appendReducedApostrophes(msgString, prevIndex, index, sb); + prevIndex = index; + } + } + if (sb.indexOf(LEFT_CURLY_BRACE) >= 0) { + UnicodeString emptyPattern; // gcc 3.3.3 fails with "UnicodeString()" as the first parameter. + MessageFormat subMsgFormat(emptyPattern, fLocale, success); + subMsgFormat.applyPattern(sb, UMSGPAT_APOS_DOUBLE_REQUIRED, nullptr, success); + subMsgFormat.format(0, nullptr, arguments, argumentNames, cnt, appendTo, nullptr, success); + } else { + appendTo.append(sb); + } +} + + +UnicodeString MessageFormat::getLiteralStringUntilNextArgument(int32_t from) const { + const UnicodeString& msgString=msgPattern.getPatternString(); + int32_t prevIndex=msgPattern.getPart(from).getLimit(); + UnicodeString b; + for (int32_t i = from + 1; ; ++i) { + const MessagePattern::Part& part = msgPattern.getPart(i); + const UMessagePatternPartType type=part.getType(); + int32_t index=part.getIndex(); + b.append(msgString, prevIndex, index - prevIndex); + if(type==UMSGPAT_PART_TYPE_ARG_START || type==UMSGPAT_PART_TYPE_MSG_LIMIT) { + return b; + } + // Unexpected Part "part" in parsed message. + U_ASSERT(type==UMSGPAT_PART_TYPE_SKIP_SYNTAX || type==UMSGPAT_PART_TYPE_INSERT_CHAR); + prevIndex=part.getLimit(); + } +} + + +FieldPosition* MessageFormat::updateMetaData(AppendableWrapper& /*dest*/, int32_t /*prevLength*/, + FieldPosition* /*fp*/, const Formattable* /*argId*/) const { + // Unlike in Java, there are no field attributes defined for MessageFormat. Do nothing. + return nullptr; + /* + if (fp != nullptr && Field.ARGUMENT.equals(fp.getFieldAttribute())) { + fp->setBeginIndex(prevLength); + fp->setEndIndex(dest.get_length()); + return nullptr; + } + return fp; + */ +} + +int32_t +MessageFormat::findOtherSubMessage(int32_t partIndex) const { + int32_t count=msgPattern.countParts(); + const MessagePattern::Part *part = &msgPattern.getPart(partIndex); + if(MessagePattern::Part::hasNumericValue(part->getType())) { + ++partIndex; + } + // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples + // until ARG_LIMIT or end of plural-only pattern. + UnicodeString other(false, OTHER_STRING, 5); + do { + part=&msgPattern.getPart(partIndex++); + UMessagePatternPartType type=part->getType(); + if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) { + break; + } + U_ASSERT(type==UMSGPAT_PART_TYPE_ARG_SELECTOR); + // part is an ARG_SELECTOR followed by an optional explicit value, and then a message + if(msgPattern.partSubstringMatches(*part, other)) { + return partIndex; + } + if(MessagePattern::Part::hasNumericValue(msgPattern.getPartType(partIndex))) { + ++partIndex; // skip the numeric-value part of "=1" etc. + } + partIndex=msgPattern.getLimitPartIndex(partIndex); + } while(++partIndex 0) { + if (!allocateArgTypes(argTypeCount, ec)) { + return; + } + uprv_memcpy(argTypes, that.argTypes, argTypeCount * sizeof(argTypes[0])); + } + if (cachedFormatters != nullptr) { + uhash_removeAll(cachedFormatters); + } + if (customFormatArgStarts != nullptr) { + uhash_removeAll(customFormatArgStarts); + } + if (that.cachedFormatters) { + if (cachedFormatters == nullptr) { + cachedFormatters=uhash_open(uhash_hashLong, uhash_compareLong, + equalFormatsForHash, &ec); + if (U_FAILURE(ec)) { + return; + } + uhash_setValueDeleter(cachedFormatters, uprv_deleteUObject); + } + + const int32_t count = uhash_count(that.cachedFormatters); + int32_t pos, idx; + for (idx = 0, pos = UHASH_FIRST; idx < count && U_SUCCESS(ec); ++idx) { + const UHashElement* cur = uhash_nextElement(that.cachedFormatters, &pos); + Format* newFormat = ((Format*)(cur->value.pointer))->clone(); + if (newFormat) { + uhash_iput(cachedFormatters, cur->key.integer, newFormat, &ec); + } else { + ec = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + } + if (that.customFormatArgStarts) { + if (customFormatArgStarts == nullptr) { + customFormatArgStarts=uhash_open(uhash_hashLong, uhash_compareLong, + nullptr, &ec); + } + const int32_t count = uhash_count(that.customFormatArgStarts); + int32_t pos, idx; + for (idx = 0, pos = UHASH_FIRST; idx < count && U_SUCCESS(ec); ++idx) { + const UHashElement* cur = uhash_nextElement(that.customFormatArgStarts, &pos); + uhash_iputi(customFormatArgStarts, cur->key.integer, cur->value.integer, &ec); + } + } +} + + +Formattable* +MessageFormat::parse(int32_t msgStart, + const UnicodeString& source, + ParsePosition& pos, + int32_t& count, + UErrorCode& ec) const { + count = 0; + if (U_FAILURE(ec)) { + pos.setErrorIndex(pos.getIndex()); + return nullptr; + } + // parse() does not work with named arguments. + if (msgPattern.hasNamedArguments()) { + ec = U_ARGUMENT_TYPE_MISMATCH; + pos.setErrorIndex(pos.getIndex()); + return nullptr; + } + LocalArray resultArray(new Formattable[argTypeCount ? argTypeCount : 1]); + const UnicodeString& msgString=msgPattern.getPatternString(); + int32_t prevIndex=msgPattern.getPart(msgStart).getLimit(); + int32_t sourceOffset = pos.getIndex(); + ParsePosition tempStatus(0); + + for(int32_t i=msgStart+1; ; ++i) { + UBool haveArgResult = false; + const MessagePattern::Part* part=&msgPattern.getPart(i); + const UMessagePatternPartType type=part->getType(); + int32_t index=part->getIndex(); + // Make sure the literal string matches. + int32_t len = index - prevIndex; + if (len == 0 || (0 == msgString.compare(prevIndex, len, source, sourceOffset, len))) { + sourceOffset += len; + prevIndex += len; + } else { + pos.setErrorIndex(sourceOffset); + return nullptr; // leave index as is to signal error + } + if(type==UMSGPAT_PART_TYPE_MSG_LIMIT) { + // Things went well! Done. + pos.setIndex(sourceOffset); + return resultArray.orphan(); + } + if(type==UMSGPAT_PART_TYPE_SKIP_SYNTAX || type==UMSGPAT_PART_TYPE_INSERT_CHAR) { + prevIndex=part->getLimit(); + continue; + } + // We do not support parsing Plural formats. (No REPLACE_NUMBER here.) + // Unexpected Part "part" in parsed message. + U_ASSERT(type==UMSGPAT_PART_TYPE_ARG_START); + int32_t argLimit=msgPattern.getLimitPartIndex(i); + + UMessagePatternArgType argType=part->getArgType(); + part=&msgPattern.getPart(++i); + int32_t argNumber = part->getValue(); // ARG_NUMBER + UnicodeString key; + ++i; + const Format* formatter = nullptr; + Formattable& argResult = resultArray[argNumber]; + + if(cachedFormatters!=nullptr && (formatter = getCachedFormatter(i - 2))!=nullptr) { + // Just parse using the formatter. + tempStatus.setIndex(sourceOffset); + formatter->parseObject(source, argResult, tempStatus); + if (tempStatus.getIndex() == sourceOffset) { + pos.setErrorIndex(sourceOffset); + return nullptr; // leave index as is to signal error + } + sourceOffset = tempStatus.getIndex(); + haveArgResult = true; + } else if( + argType==UMSGPAT_ARG_TYPE_NONE || (cachedFormatters && uhash_iget(cachedFormatters, i -2))) { + // We arrive here if getCachedFormatter returned nullptr, but there was actually an element in the hash table. + // This can only happen if the hash table contained a DummyFormat, so the if statement above is a check + // for the hash table containing DummyFormat. + + // Match as a string. + // if at end, use longest possible match + // otherwise uses first match to intervening string + // does NOT recursively try all possibilities + UnicodeString stringAfterArgument = getLiteralStringUntilNextArgument(argLimit); + int32_t next; + if (!stringAfterArgument.isEmpty()) { + next = source.indexOf(stringAfterArgument, sourceOffset); + } else { + next = source.length(); + } + if (next < 0) { + pos.setErrorIndex(sourceOffset); + return nullptr; // leave index as is to signal error + } else { + UnicodeString strValue(source.tempSubString(sourceOffset, next - sourceOffset)); + UnicodeString compValue; + compValue.append(LEFT_CURLY_BRACE); + itos(argNumber, compValue); + compValue.append(RIGHT_CURLY_BRACE); + if (0 != strValue.compare(compValue)) { + argResult.setString(strValue); + haveArgResult = true; + } + sourceOffset = next; + } + } else if(argType==UMSGPAT_ARG_TYPE_CHOICE) { + tempStatus.setIndex(sourceOffset); + double choiceResult = ChoiceFormat::parseArgument(msgPattern, i, source, tempStatus); + if (tempStatus.getIndex() == sourceOffset) { + pos.setErrorIndex(sourceOffset); + return nullptr; // leave index as is to signal error + } + argResult.setDouble(choiceResult); + haveArgResult = true; + sourceOffset = tempStatus.getIndex(); + } else if(UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType) || argType==UMSGPAT_ARG_TYPE_SELECT) { + // Parsing not supported. + ec = U_UNSUPPORTED_ERROR; + return nullptr; + } else { + // This should never happen. + ec = U_INTERNAL_PROGRAM_ERROR; + return nullptr; + } + if (haveArgResult && count <= argNumber) { + count = argNumber + 1; + } + prevIndex=msgPattern.getPart(argLimit).getLimit(); + i=argLimit; + } +} +// ------------------------------------- +// Parses the source pattern and returns the Formattable objects array, +// the array count and the ending parse position. The caller of this method +// owns the array. + +Formattable* +MessageFormat::parse(const UnicodeString& source, + ParsePosition& pos, + int32_t& count) const { + UErrorCode ec = U_ZERO_ERROR; + return parse(0, source, pos, count, ec); +} + +// ------------------------------------- +// Parses the source string and returns the array of +// Formattable objects and the array count. The caller +// owns the returned array. + +Formattable* +MessageFormat::parse(const UnicodeString& source, + int32_t& cnt, + UErrorCode& success) const +{ + if (msgPattern.hasNamedArguments()) { + success = U_ARGUMENT_TYPE_MISMATCH; + return nullptr; + } + ParsePosition status(0); + // Calls the actual implementation method and starts + // from zero offset of the source text. + Formattable* result = parse(source, status, cnt); + if (status.getIndex() == 0) { + success = U_MESSAGE_PARSE_ERROR; + delete[] result; + return nullptr; + } + return result; +} + +// ------------------------------------- +// Parses the source text and copy into the result buffer. + +void +MessageFormat::parseObject( const UnicodeString& source, + Formattable& result, + ParsePosition& status) const +{ + int32_t cnt = 0; + Formattable* tmpResult = parse(source, status, cnt); + if (tmpResult != nullptr) + result.adoptArray(tmpResult, cnt); +} + +UnicodeString +MessageFormat::autoQuoteApostrophe(const UnicodeString& pattern, UErrorCode& status) { + UnicodeString result; + if (U_SUCCESS(status)) { + int32_t plen = pattern.length(); + const char16_t* pat = pattern.getBuffer(); + int32_t blen = plen * 2 + 1; // space for null termination, convenience + char16_t* buf = result.getBuffer(blen); + if (buf == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + int32_t len = umsg_autoQuoteApostrophe(pat, plen, buf, blen, &status); + result.releaseBuffer(U_SUCCESS(status) ? len : 0); + } + } + if (U_FAILURE(status)) { + result.setToBogus(); + } + return result; +} + +// ------------------------------------- + +static Format* makeRBNF(URBNFRuleSetTag tag, const Locale& locale, const UnicodeString& defaultRuleSet, UErrorCode& ec) { + RuleBasedNumberFormat* fmt = new RuleBasedNumberFormat(tag, locale, ec); + if (fmt == nullptr) { + ec = U_MEMORY_ALLOCATION_ERROR; + } else if (U_SUCCESS(ec) && defaultRuleSet.length() > 0) { + UErrorCode localStatus = U_ZERO_ERROR; // ignore unrecognized default rule set + fmt->setDefaultRuleSet(defaultRuleSet, localStatus); + } + return fmt; +} + +void MessageFormat::cacheExplicitFormats(UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + + if (cachedFormatters != nullptr) { + uhash_removeAll(cachedFormatters); + } + if (customFormatArgStarts != nullptr) { + uhash_removeAll(customFormatArgStarts); + } + + // The last two "parts" can at most be ARG_LIMIT and MSG_LIMIT + // which we need not examine. + int32_t limit = msgPattern.countParts() - 2; + argTypeCount = 0; + // We also need not look at the first two "parts" + // (at most MSG_START and ARG_START) in this loop. + // We determine the argTypeCount first so that we can allocateArgTypes + // so that the next loop can set argTypes[argNumber]. + // (This is for the C API which needs the argTypes to read its va_arg list.) + for (int32_t i = 2; i < limit && U_SUCCESS(status); ++i) { + const MessagePattern::Part& part = msgPattern.getPart(i); + if (part.getType() == UMSGPAT_PART_TYPE_ARG_NUMBER) { + const int argNumber = part.getValue(); + if (argNumber >= argTypeCount) { + argTypeCount = argNumber + 1; + } + } + } + if (!allocateArgTypes(argTypeCount, status)) { + return; + } + // Set all argTypes to kObject, as a "none" value, for lack of any better value. + // We never use kObject for real arguments. + // We use it as "no argument yet" for the check for hasArgTypeConflicts. + for (int32_t i = 0; i < argTypeCount; ++i) { + argTypes[i] = Formattable::kObject; + } + hasArgTypeConflicts = false; + + // This loop starts at part index 1 because we do need to examine + // ARG_START parts. (But we can ignore the MSG_START.) + for (int32_t i = 1; i < limit && U_SUCCESS(status); ++i) { + const MessagePattern::Part* part = &msgPattern.getPart(i); + if (part->getType() != UMSGPAT_PART_TYPE_ARG_START) { + continue; + } + UMessagePatternArgType argType = part->getArgType(); + + int32_t argNumber = -1; + part = &msgPattern.getPart(i + 1); + if (part->getType() == UMSGPAT_PART_TYPE_ARG_NUMBER) { + argNumber = part->getValue(); + } + Formattable::Type formattableType; + + switch (argType) { + case UMSGPAT_ARG_TYPE_NONE: + formattableType = Formattable::kString; + break; + case UMSGPAT_ARG_TYPE_SIMPLE: { + int32_t index = i; + i += 2; + UnicodeString explicitType = msgPattern.getSubstring(msgPattern.getPart(i++)); + UnicodeString style; + if ((part = &msgPattern.getPart(i))->getType() == UMSGPAT_PART_TYPE_ARG_STYLE) { + style = msgPattern.getSubstring(*part); + ++i; + } + UParseError parseError; + Format* formatter = createAppropriateFormat(explicitType, style, formattableType, parseError, status); + setArgStartFormat(index, formatter, status); + break; + } + case UMSGPAT_ARG_TYPE_CHOICE: + case UMSGPAT_ARG_TYPE_PLURAL: + case UMSGPAT_ARG_TYPE_SELECTORDINAL: + formattableType = Formattable::kDouble; + break; + case UMSGPAT_ARG_TYPE_SELECT: + formattableType = Formattable::kString; + break; + default: + status = U_INTERNAL_PROGRAM_ERROR; // Should be unreachable. + formattableType = Formattable::kString; + break; + } + if (argNumber != -1) { + if (argTypes[argNumber] != Formattable::kObject && argTypes[argNumber] != formattableType) { + hasArgTypeConflicts = true; + } + argTypes[argNumber] = formattableType; + } + } +} + +Format* MessageFormat::createAppropriateFormat(UnicodeString& type, UnicodeString& style, + Formattable::Type& formattableType, UParseError& parseError, + UErrorCode& ec) { + if (U_FAILURE(ec)) { + return nullptr; + } + Format* fmt = nullptr; + int32_t typeID, styleID; + DateFormat::EStyle date_style; + int32_t firstNonSpace; + + switch (typeID = findKeyword(type, TYPE_IDS)) { + case 0: // number + formattableType = Formattable::kDouble; + switch (findKeyword(style, NUMBER_STYLE_IDS)) { + case 0: // default + fmt = NumberFormat::createInstance(fLocale, ec); + break; + case 1: // currency + fmt = NumberFormat::createCurrencyInstance(fLocale, ec); + break; + case 2: // percent + fmt = NumberFormat::createPercentInstance(fLocale, ec); + break; + case 3: // integer + formattableType = Formattable::kLong; + fmt = createIntegerFormat(fLocale, ec); + break; + default: // pattern or skeleton + firstNonSpace = PatternProps::skipWhiteSpace(style, 0); + if (style.compare(firstNonSpace, 2, u"::", 0, 2) == 0) { + // Skeleton + UnicodeString skeleton = style.tempSubString(firstNonSpace + 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; + } + break; + + case 1: // date + case 2: // time + formattableType = Formattable::kDate; + firstNonSpace = PatternProps::skipWhiteSpace(style, 0); + if (style.compare(firstNonSpace, 2, u"::", 0, 2) == 0) { + // Skeleton + UnicodeString skeleton = style.tempSubString(firstNonSpace + 2); + fmt = DateFormat::createInstanceForSkeleton(skeleton, fLocale, ec); + } else { + // Pattern + styleID = findKeyword(style, DATE_STYLE_IDS); + date_style = (styleID >= 0) ? DATE_STYLES[styleID] : DateFormat::kDefault; + + if (typeID == 1) { + fmt = DateFormat::createDateInstance(date_style, fLocale); + } else { + fmt = DateFormat::createTimeInstance(date_style, fLocale); + } + + if (styleID < 0 && fmt != nullptr) { + SimpleDateFormat* sdtfmt = dynamic_cast(fmt); + if (sdtfmt != nullptr) { + sdtfmt->applyPattern(style); + } + } + } + break; + + case 3: // spellout + formattableType = Formattable::kDouble; + fmt = makeRBNF(URBNF_SPELLOUT, fLocale, style, ec); + break; + case 4: // ordinal + formattableType = Formattable::kDouble; + fmt = makeRBNF(URBNF_ORDINAL, fLocale, style, ec); + break; + case 5: // duration + formattableType = Formattable::kDouble; + fmt = makeRBNF(URBNF_DURATION, fLocale, style, ec); + break; + default: + formattableType = Formattable::kString; + ec = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + + return fmt; +} + + +//------------------------------------- +// Finds the string, s, in the string array, list. +int32_t MessageFormat::findKeyword(const UnicodeString& s, + const char16_t * const *list) +{ + if (s.isEmpty()) { + return 0; // default + } + + int32_t length = s.length(); + const char16_t *ps = PatternProps::trimWhiteSpace(s.getBuffer(), length); + UnicodeString buffer(false, ps, length); + // Trims the space characters and turns all characters + // in s to lower case. + buffer.toLower(""); + for (int32_t i = 0; list[i]; ++i) { + if (!buffer.compare(list[i], u_strlen(list[i]))) { + return i; + } + } + return -1; +} + +/** + * Convenience method that ought to be in NumberFormat + */ +NumberFormat* +MessageFormat::createIntegerFormat(const Locale& locale, UErrorCode& status) const { + NumberFormat *temp = NumberFormat::createInstance(locale, status); + DecimalFormat *temp2; + if (temp != nullptr && (temp2 = dynamic_cast(temp)) != nullptr) { + temp2->setMaximumFractionDigits(0); + temp2->setDecimalSeparatorAlwaysShown(false); + temp2->setParseIntegerOnly(true); + } + + return temp; +} + +/** + * Return the default number format. Used to format a numeric + * argument when subformats[i].format is nullptr. Returns nullptr + * on failure. + * + * Semantically const but may modify *this. + */ +const NumberFormat* MessageFormat::getDefaultNumberFormat(UErrorCode& ec) const { + if (defaultNumberFormat == nullptr) { + MessageFormat* t = (MessageFormat*) this; + t->defaultNumberFormat = NumberFormat::createInstance(fLocale, ec); + if (U_FAILURE(ec)) { + delete t->defaultNumberFormat; + t->defaultNumberFormat = nullptr; + } else if (t->defaultNumberFormat == nullptr) { + ec = U_MEMORY_ALLOCATION_ERROR; + } + } + return defaultNumberFormat; +} + +/** + * Return the default date format. Used to format a date + * argument when subformats[i].format is nullptr. Returns nullptr + * on failure. + * + * Semantically const but may modify *this. + */ +const DateFormat* MessageFormat::getDefaultDateFormat(UErrorCode& ec) const { + if (defaultDateFormat == nullptr) { + MessageFormat* t = (MessageFormat*) this; + t->defaultDateFormat = DateFormat::createDateTimeInstance(DateFormat::kShort, DateFormat::kShort, fLocale); + if (t->defaultDateFormat == nullptr) { + ec = U_MEMORY_ALLOCATION_ERROR; + } + } + return defaultDateFormat; +} + +UBool +MessageFormat::usesNamedArguments() const { + return msgPattern.hasNamedArguments(); +} + +int32_t +MessageFormat::getArgTypeCount() const { + return argTypeCount; +} + +UBool MessageFormat::equalFormats(const void* left, const void* right) { + return *(const Format*)left==*(const Format*)right; +} + + +bool MessageFormat::DummyFormat::operator==(const Format&) const { + return true; +} + +MessageFormat::DummyFormat* MessageFormat::DummyFormat::clone() const { + return new DummyFormat(); +} + +UnicodeString& MessageFormat::DummyFormat::format(const Formattable&, + UnicodeString& appendTo, + UErrorCode& status) const { + if (U_SUCCESS(status)) { + status = U_UNSUPPORTED_ERROR; + } + return appendTo; +} + +UnicodeString& MessageFormat::DummyFormat::format(const Formattable&, + UnicodeString& appendTo, + FieldPosition&, + UErrorCode& status) const { + if (U_SUCCESS(status)) { + status = U_UNSUPPORTED_ERROR; + } + return appendTo; +} + +UnicodeString& MessageFormat::DummyFormat::format(const Formattable&, + UnicodeString& appendTo, + FieldPositionIterator*, + UErrorCode& status) const { + if (U_SUCCESS(status)) { + status = U_UNSUPPORTED_ERROR; + } + return appendTo; +} + +void MessageFormat::DummyFormat::parseObject(const UnicodeString&, + Formattable&, + ParsePosition& ) const { +} + + +FormatNameEnumeration::FormatNameEnumeration(LocalPointer nameList, UErrorCode& /*status*/) { + pos=0; + fFormatNames = std::move(nameList); +} + +const UnicodeString* +FormatNameEnumeration::snext(UErrorCode& status) { + if (U_SUCCESS(status) && pos < fFormatNames->size()) { + return (const UnicodeString*)fFormatNames->elementAt(pos++); + } + return nullptr; +} + +void +FormatNameEnumeration::reset(UErrorCode& /*status*/) { + pos=0; +} + +int32_t +FormatNameEnumeration::count(UErrorCode& /*status*/) const { + return (fFormatNames==nullptr) ? 0 : fFormatNames->size(); +} + +FormatNameEnumeration::~FormatNameEnumeration() { +} + +MessageFormat::PluralSelectorProvider::PluralSelectorProvider(const MessageFormat &mf, UPluralType t) + : msgFormat(mf), rules(nullptr), type(t) { +} + +MessageFormat::PluralSelectorProvider::~PluralSelectorProvider() { + delete rules; +} + +UnicodeString MessageFormat::PluralSelectorProvider::select(void *ctx, double number, + UErrorCode& ec) const { + if (U_FAILURE(ec)) { + return UnicodeString(false, OTHER_STRING, 5); + } + MessageFormat::PluralSelectorProvider* t = const_cast(this); + if(rules == nullptr) { + t->rules = PluralRules::forLocale(msgFormat.fLocale, type, ec); + if (U_FAILURE(ec)) { + return UnicodeString(false, OTHER_STRING, 5); + } + } + // Select a sub-message according to how the number is formatted, + // which is specified in the selected sub-message. + // We avoid this circle by looking at how + // the number is formatted in the "other" sub-message + // which must always be present and usually contains the number. + // Message authors should be consistent across sub-messages. + PluralSelectorContext &context = *static_cast(ctx); + int32_t otherIndex = msgFormat.findOtherSubMessage(context.startIndex); + context.numberArgIndex = msgFormat.findFirstPluralNumberArg(otherIndex, context.argName); + if(context.numberArgIndex > 0 && msgFormat.cachedFormatters != nullptr) { + context.formatter = + (const Format*)uhash_iget(msgFormat.cachedFormatters, context.numberArgIndex); + } + if(context.formatter == nullptr) { + context.formatter = msgFormat.getDefaultNumberFormat(ec); + context.forReplaceNumber = true; + } + if (context.number.getDouble(ec) != number) { + ec = U_INTERNAL_PROGRAM_ERROR; + return UnicodeString(false, OTHER_STRING, 5); + } + context.formatter->format(context.number, context.numberString, ec); + auto* decFmt = dynamic_cast(context.formatter); + if(decFmt != nullptr) { + number::impl::DecimalQuantity dq; + decFmt->formatToDecimalQuantity(context.number, dq, ec); + if (U_FAILURE(ec)) { + return UnicodeString(false, OTHER_STRING, 5); + } + return rules->select(dq); + } else { + return rules->select(number); + } +} + +void MessageFormat::PluralSelectorProvider::reset() { + delete rules; + rules = nullptr; +} + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/msgfmt_impl.h b/deps/icu-small/source/i18n/msgfmt_impl.h index 80cb8a691eb1dd..2a119ef8d97db1 100644 --- a/deps/icu-small/source/i18n/msgfmt_impl.h +++ b/deps/icu-small/source/i18n/msgfmt_impl.h @@ -1,45 +1,45 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2007-2008, International Business Machines Corporation and -* others. All Rights Reserved. * -******************************************************************************* -* -* File MSGFMT.H -* -******************************************************************************* -*/ - -#ifndef __MSGFMT_IMPL_H__ -#define __MSGFMT_IMPL_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/msgfmt.h" -#include "uvector.h" -#include "unicode/strenum.h" - -U_NAMESPACE_BEGIN - -class FormatNameEnumeration : public StringEnumeration { -public: - FormatNameEnumeration(LocalPointer fFormatNames, UErrorCode& status); - virtual ~FormatNameEnumeration(); - static UClassID U_EXPORT2 getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; - virtual const UnicodeString* snext(UErrorCode& status) override; - virtual void reset(UErrorCode& status) override; - virtual int32_t count(UErrorCode& status) const override; -private: - int32_t pos; - LocalPointer fFormatNames; -}; - -U_NAMESPACE_END - -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2007-2008, International Business Machines Corporation and +* others. All Rights Reserved. * +******************************************************************************* +* +* File MSGFMT.H +* +******************************************************************************* +*/ + +#ifndef __MSGFMT_IMPL_H__ +#define __MSGFMT_IMPL_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/msgfmt.h" +#include "uvector.h" +#include "unicode/strenum.h" + +U_NAMESPACE_BEGIN + +class FormatNameEnumeration : public StringEnumeration { +public: + FormatNameEnumeration(LocalPointer fFormatNames, UErrorCode& status); + virtual ~FormatNameEnumeration(); + static UClassID U_EXPORT2 getStaticClassID(); + virtual UClassID getDynamicClassID() const override; + virtual const UnicodeString* snext(UErrorCode& status) override; + virtual void reset(UErrorCode& status) override; + virtual int32_t count(UErrorCode& status) const override; +private: + int32_t pos; + LocalPointer fFormatNames; +}; + +U_NAMESPACE_END + +#endif + +#endif diff --git a/deps/icu-small/source/i18n/name2uni.cpp b/deps/icu-small/source/i18n/name2uni.cpp index b22c68b022af0a..d7c8efae71723a 100644 --- a/deps/icu-small/source/i18n/name2uni.cpp +++ b/deps/icu-small/source/i18n/name2uni.cpp @@ -1,259 +1,259 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2001-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 06/07/01 aliu Creation. -********************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/unifilt.h" -#include "unicode/uchar.h" -#include "unicode/uniset.h" -#include "unicode/utf16.h" -#include "cmemory.h" -#include "name2uni.h" -#include "patternprops.h" -#include "uprops.h" -#include "uinvchar.h" -#include "util.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NameUnicodeTransliterator) - -static const UChar OPEN[] = {92,78,126,123,126,0}; // "\N~{~" -static const UChar OPEN_DELIM = 92; // '\\' first char of OPEN -static const UChar CLOSE_DELIM = 125; // '}' -static const UChar SPACE = 32; // ' ' - -U_CDECL_BEGIN - -// USetAdder implementation -// Does not use uset.h to reduce code dependencies -static void U_CALLCONV -_set_add(USet *set, UChar32 c) { - uset_add(set, c); -} - -// These functions aren't used. -/*static void U_CALLCONV -_set_addRange(USet *set, UChar32 start, UChar32 end) { - ((UnicodeSet *)set)->add(start, end); -} - -static void U_CALLCONV -_set_addString(USet *set, const UChar *str, int32_t length) { - ((UnicodeSet *)set)->add(UnicodeString((UBool)(length<0), str, length)); -}*/ - -U_CDECL_END - -/** - * Constructs a transliterator with the default delimiters '{' and - * '}'. - */ -NameUnicodeTransliterator::NameUnicodeTransliterator(UnicodeFilter* adoptedFilter) : - Transliterator(UNICODE_STRING("Name-Any", 8), adoptedFilter) { - - UnicodeSet *legalPtr = &legal; - // Get the legal character set - USetAdder sa = { - (USet *)legalPtr, // USet* == UnicodeSet* - _set_add, - NULL, // Don't need _set_addRange - NULL, // Don't need _set_addString - NULL, // Don't need remove() - NULL - }; - uprv_getCharNameCharacters(&sa); -} - -/** - * Destructor. - */ -NameUnicodeTransliterator::~NameUnicodeTransliterator() {} - -/** - * Copy constructor. - */ -NameUnicodeTransliterator::NameUnicodeTransliterator(const NameUnicodeTransliterator& o) : - Transliterator(o), legal(o.legal) {} - -/** - * Assignment operator. - */ -/*NameUnicodeTransliterator& NameUnicodeTransliterator::operator=( - const NameUnicodeTransliterator& o) { - Transliterator::operator=(o); - // not necessary: the legal sets should all be the same -- legal=o.legal; - return *this; -}*/ - -/** - * Transliterator API. - */ -NameUnicodeTransliterator* NameUnicodeTransliterator::clone() const { - return new NameUnicodeTransliterator(*this); -} - -/** - * Implements {@link Transliterator#handleTransliterate}. - */ -void NameUnicodeTransliterator::handleTransliterate(Replaceable& text, UTransPosition& offsets, - UBool isIncremental) const { - // The failure mode, here and below, is to behave like Any-Null, - // if either there is no name data (max len == 0) or there is no - // memory (malloc() => NULL). - - int32_t maxLen = uprv_getMaxCharNameLength(); - if (maxLen == 0) { - offsets.start = offsets.limit; - return; - } - - // Accommodate the longest possible name - ++maxLen; // allow for temporary trailing space - char* cbuf = (char*) uprv_malloc(maxLen); - if (cbuf == NULL) { - offsets.start = offsets.limit; - return; - } - - UnicodeString openPat(true, OPEN, -1); - UnicodeString str, name; - - int32_t cursor = offsets.start; - int32_t limit = offsets.limit; - - // Modes: - // 0 - looking for open delimiter - // 1 - after open delimiter - int32_t mode = 0; - int32_t openPos = -1; // open delim candidate pos - - UChar32 c; - while (cursor < limit) { - c = text.char32At(cursor); - - switch (mode) { - case 0: // looking for open delimiter - if (c == OPEN_DELIM) { // quick check first - openPos = cursor; - int32_t i = - ICU_Utility::parsePattern(openPat, text, cursor, limit); - if (i >= 0 && i < limit) { - mode = 1; - name.truncate(0); - cursor = i; - continue; // *** reprocess char32At(cursor) - } - } - break; - - case 1: // after open delimiter - // Look for legal chars. If \s+ is found, convert it - // to a single space. If closeDelimiter is found, exit - // the loop. If any other character is found, exit the - // loop. If the limit is reached, exit the loop. - - // Convert \s+ => SPACE. This assumes there are no - // runs of >1 space characters in names. - if (PatternProps::isWhiteSpace(c)) { - // Ignore leading whitespace - if (name.length() > 0 && - name.charAt(name.length()-1) != SPACE) { - name.append(SPACE); - // If we are too long then abort. maxLen includes - // temporary trailing space, so use '>'. - if (name.length() > maxLen) { - mode = 0; - } - } - break; - } - - if (c == CLOSE_DELIM) { - int32_t len = name.length(); - - // Delete trailing space, if any - if (len > 0 && - name.charAt(len-1) == SPACE) { - --len; - } - - if (uprv_isInvariantUString(name.getBuffer(), len)) { - cbuf[0] = 0; - name.extract(0, len, cbuf, maxLen, US_INV); - - UErrorCode status = U_ZERO_ERROR; - c = u_charFromName(U_EXTENDED_CHAR_NAME, cbuf, &status); - if (U_SUCCESS(status)) { - // Lookup succeeded - - // assert(U16_LENGTH(CLOSE_DELIM) == 1); - cursor++; // advance over CLOSE_DELIM - - str.truncate(0); - str.append(c); - text.handleReplaceBetween(openPos, cursor, str); - - // Adjust indices for the change in the length of - // the string. Do not assume that str.length() == - // 1, in case of surrogates. - int32_t delta = cursor - openPos - str.length(); - cursor -= delta; - limit -= delta; - // assert(cursor == openPos + str.length()); - } - } - // If the lookup failed, we leave things as-is and - // still switch to mode 0 and continue. - mode = 0; - openPos = -1; // close off candidate - continue; // *** reprocess char32At(cursor) - } - - // Check if c is a legal char. We assume here that - // legal.contains(OPEN_DELIM) is false, so when we abort a - // name, we don't have to go back to openPos+1. - if (legal.contains(c)) { - name.append(c); - // If we go past the longest possible name then abort. - // maxLen includes temporary trailing space, so use '>='. - if (name.length() >= maxLen) { - mode = 0; - } - } - - // Invalid character - else { - --cursor; // Backup and reprocess this character - mode = 0; - } - - break; - } - - cursor += U16_LENGTH(c); - } - - offsets.contextLimit += limit - offsets.limit; - offsets.limit = limit; - // In incremental mode, only advance the cursor up to the last - // open delimiter candidate. - offsets.start = (isIncremental && openPos >= 0) ? openPos : cursor; - - uprv_free(cbuf); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2001-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 06/07/01 aliu Creation. +********************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/unifilt.h" +#include "unicode/uchar.h" +#include "unicode/uniset.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "name2uni.h" +#include "patternprops.h" +#include "uprops.h" +#include "uinvchar.h" +#include "util.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NameUnicodeTransliterator) + +static const char16_t OPEN[] = {92,78,126,123,126,0}; // "\N~{~" +static const char16_t OPEN_DELIM = 92; // '\\' first char of OPEN +static const char16_t CLOSE_DELIM = 125; // '}' +static const char16_t SPACE = 32; // ' ' + +U_CDECL_BEGIN + +// USetAdder implementation +// Does not use uset.h to reduce code dependencies +static void U_CALLCONV +_set_add(USet *set, UChar32 c) { + uset_add(set, c); +} + +// These functions aren't used. +/*static void U_CALLCONV +_set_addRange(USet *set, UChar32 start, UChar32 end) { + ((UnicodeSet *)set)->add(start, end); +} + +static void U_CALLCONV +_set_addString(USet *set, const char16_t *str, int32_t length) { + ((UnicodeSet *)set)->add(UnicodeString((UBool)(length<0), str, length)); +}*/ + +U_CDECL_END + +/** + * Constructs a transliterator with the default delimiters '{' and + * '}'. + */ +NameUnicodeTransliterator::NameUnicodeTransliterator(UnicodeFilter* adoptedFilter) : + Transliterator(UNICODE_STRING("Name-Any", 8), adoptedFilter) { + + UnicodeSet *legalPtr = &legal; + // Get the legal character set + USetAdder sa = { + (USet *)legalPtr, // USet* == UnicodeSet* + _set_add, + nullptr, // Don't need _set_addRange + nullptr, // Don't need _set_addString + nullptr, // Don't need remove() + nullptr + }; + uprv_getCharNameCharacters(&sa); +} + +/** + * Destructor. + */ +NameUnicodeTransliterator::~NameUnicodeTransliterator() {} + +/** + * Copy constructor. + */ +NameUnicodeTransliterator::NameUnicodeTransliterator(const NameUnicodeTransliterator& o) : + Transliterator(o), legal(o.legal) {} + +/** + * Assignment operator. + */ +/*NameUnicodeTransliterator& NameUnicodeTransliterator::operator=( + const NameUnicodeTransliterator& o) { + Transliterator::operator=(o); + // not necessary: the legal sets should all be the same -- legal=o.legal; + return *this; +}*/ + +/** + * Transliterator API. + */ +NameUnicodeTransliterator* NameUnicodeTransliterator::clone() const { + return new NameUnicodeTransliterator(*this); +} + +/** + * Implements {@link Transliterator#handleTransliterate}. + */ +void NameUnicodeTransliterator::handleTransliterate(Replaceable& text, UTransPosition& offsets, + UBool isIncremental) const { + // The failure mode, here and below, is to behave like Any-Null, + // if either there is no name data (max len == 0) or there is no + // memory (malloc() => nullptr). + + int32_t maxLen = uprv_getMaxCharNameLength(); + if (maxLen == 0) { + offsets.start = offsets.limit; + return; + } + + // Accommodate the longest possible name + ++maxLen; // allow for temporary trailing space + char* cbuf = (char*) uprv_malloc(maxLen); + if (cbuf == nullptr) { + offsets.start = offsets.limit; + return; + } + + UnicodeString openPat(true, OPEN, -1); + UnicodeString str, name; + + int32_t cursor = offsets.start; + int32_t limit = offsets.limit; + + // Modes: + // 0 - looking for open delimiter + // 1 - after open delimiter + int32_t mode = 0; + int32_t openPos = -1; // open delim candidate pos + + UChar32 c; + while (cursor < limit) { + c = text.char32At(cursor); + + switch (mode) { + case 0: // looking for open delimiter + if (c == OPEN_DELIM) { // quick check first + openPos = cursor; + int32_t i = + ICU_Utility::parsePattern(openPat, text, cursor, limit); + if (i >= 0 && i < limit) { + mode = 1; + name.truncate(0); + cursor = i; + continue; // *** reprocess char32At(cursor) + } + } + break; + + case 1: // after open delimiter + // Look for legal chars. If \s+ is found, convert it + // to a single space. If closeDelimiter is found, exit + // the loop. If any other character is found, exit the + // loop. If the limit is reached, exit the loop. + + // Convert \s+ => SPACE. This assumes there are no + // runs of >1 space characters in names. + if (PatternProps::isWhiteSpace(c)) { + // Ignore leading whitespace + if (name.length() > 0 && + name.charAt(name.length()-1) != SPACE) { + name.append(SPACE); + // If we are too long then abort. maxLen includes + // temporary trailing space, so use '>'. + if (name.length() > maxLen) { + mode = 0; + } + } + break; + } + + if (c == CLOSE_DELIM) { + int32_t len = name.length(); + + // Delete trailing space, if any + if (len > 0 && + name.charAt(len-1) == SPACE) { + --len; + } + + if (uprv_isInvariantUString(name.getBuffer(), len)) { + cbuf[0] = 0; + name.extract(0, len, cbuf, maxLen, US_INV); + + UErrorCode status = U_ZERO_ERROR; + c = u_charFromName(U_EXTENDED_CHAR_NAME, cbuf, &status); + if (U_SUCCESS(status)) { + // Lookup succeeded + + // assert(U16_LENGTH(CLOSE_DELIM) == 1); + cursor++; // advance over CLOSE_DELIM + + str.truncate(0); + str.append(c); + text.handleReplaceBetween(openPos, cursor, str); + + // Adjust indices for the change in the length of + // the string. Do not assume that str.length() == + // 1, in case of surrogates. + int32_t delta = cursor - openPos - str.length(); + cursor -= delta; + limit -= delta; + // assert(cursor == openPos + str.length()); + } + } + // If the lookup failed, we leave things as-is and + // still switch to mode 0 and continue. + mode = 0; + openPos = -1; // close off candidate + continue; // *** reprocess char32At(cursor) + } + + // Check if c is a legal char. We assume here that + // legal.contains(OPEN_DELIM) is false, so when we abort a + // name, we don't have to go back to openPos+1. + if (legal.contains(c)) { + name.append(c); + // If we go past the longest possible name then abort. + // maxLen includes temporary trailing space, so use '>='. + if (name.length() >= maxLen) { + mode = 0; + } + } + + // Invalid character + else { + --cursor; // Backup and reprocess this character + mode = 0; + } + + break; + } + + cursor += U16_LENGTH(c); + } + + offsets.contextLimit += limit - offsets.limit; + offsets.limit = limit; + // In incremental mode, only advance the cursor up to the last + // open delimiter candidate. + offsets.start = (isIncremental && openPos >= 0) ? openPos : cursor; + + uprv_free(cbuf); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ diff --git a/deps/icu-small/source/i18n/name2uni.h b/deps/icu-small/source/i18n/name2uni.h index 6881c6bc85745c..aae99c33895d49 100644 --- a/deps/icu-small/source/i18n/name2uni.h +++ b/deps/icu-small/source/i18n/name2uni.h @@ -1,93 +1,93 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2001-2007, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 06/07/01 aliu Creation. -********************************************************************** -*/ -#ifndef NAME2UNI_H -#define NAME2UNI_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/translit.h" -#include "unicode/uniset.h" - -U_NAMESPACE_BEGIN - -/** - * A transliterator that performs name to character mapping. - * It recognizes the Perl syntax \N{name}. - * @author Alan Liu - */ -class NameUnicodeTransliterator : public Transliterator { -public: - - /** - * Constructs a transliterator. - * @param adoptedFilter the filter for this transliterator. - */ - NameUnicodeTransliterator(UnicodeFilter* adoptedFilter = 0); - - /** - * Destructor. - */ - virtual ~NameUnicodeTransliterator(); - - /** - * Copy constructor. - */ - NameUnicodeTransliterator(const NameUnicodeTransliterator&); - - /** - * Transliterator API. - * @return A copy of the object. - */ - virtual NameUnicodeTransliterator* clone() const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - virtual UClassID getDynamicClassID() const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); - - protected: - - /** - * Implements {@link Transliterator#handleTransliterate}. - * @param text the buffer holding transliterated and - * untransliterated text - * @param offset the start and limit of the text, the position - * of the cursor, and the start and limit of transliteration. - * @param incremental if true, assume more text may be coming after - * pos.contextLimit. Otherwise, assume the text is complete. - */ - virtual void handleTransliterate(Replaceable& text, UTransPosition& offset, - UBool isIncremental) const override; - - /** - * Set of characters which occur in Unicode character names. - */ - UnicodeSet legal; -private: - /** - * Assignment operator. - */ - NameUnicodeTransliterator& operator=(const NameUnicodeTransliterator&); -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2001-2007, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 06/07/01 aliu Creation. +********************************************************************** +*/ +#ifndef NAME2UNI_H +#define NAME2UNI_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/translit.h" +#include "unicode/uniset.h" + +U_NAMESPACE_BEGIN + +/** + * A transliterator that performs name to character mapping. + * It recognizes the Perl syntax \N{name}. + * @author Alan Liu + */ +class NameUnicodeTransliterator : public Transliterator { +public: + + /** + * Constructs a transliterator. + * @param adoptedFilter the filter for this transliterator. + */ + NameUnicodeTransliterator(UnicodeFilter* adoptedFilter = 0); + + /** + * Destructor. + */ + virtual ~NameUnicodeTransliterator(); + + /** + * Copy constructor. + */ + NameUnicodeTransliterator(const NameUnicodeTransliterator&); + + /** + * Transliterator API. + * @return A copy of the object. + */ + virtual NameUnicodeTransliterator* clone() const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + virtual UClassID getDynamicClassID() const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + + protected: + + /** + * Implements {@link Transliterator#handleTransliterate}. + * @param text the buffer holding transliterated and + * untransliterated text + * @param offset the start and limit of the text, the position + * of the cursor, and the start and limit of transliteration. + * @param incremental if true, assume more text may be coming after + * pos.contextLimit. Otherwise, assume the text is complete. + */ + virtual void handleTransliterate(Replaceable& text, UTransPosition& offset, + UBool isIncremental) const override; + + /** + * Set of characters which occur in Unicode character names. + */ + UnicodeSet legal; +private: + /** + * Assignment operator. + */ + NameUnicodeTransliterator& operator=(const NameUnicodeTransliterator&); +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/nfrlist.h b/deps/icu-small/source/i18n/nfrlist.h index 3eb1882b2f957e..bb1b6516ecb284 100644 --- a/deps/icu-small/source/i18n/nfrlist.h +++ b/deps/icu-small/source/i18n/nfrlist.h @@ -1,112 +1,112 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 1997-2012, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* file name: nfrlist.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* Modification history -* Date Name Comments -* 10/11/2001 Doug Ported from ICU4J -*/ - -#ifndef NFRLIST_H -#define NFRLIST_H - -#include "unicode/rbnf.h" - -#if U_HAVE_RBNF - -#include "unicode/uobject.h" -#include "nfrule.h" - -#include "cmemory.h" - -U_NAMESPACE_BEGIN - -// unsafe class for internal use only. assume memory allocations succeed, indexes are valid. -// should be a template, but we can't use them - -class NFRuleList : public UMemory { -protected: - NFRule** fStuff; - uint32_t fCount; - uint32_t fCapacity; -public: - NFRuleList(uint32_t capacity = 10) - : fStuff(capacity ? (NFRule**)uprv_malloc(capacity * sizeof(NFRule*)) : NULL) - , fCount(0) - , fCapacity(capacity) {} - ~NFRuleList() { - if (fStuff) { - for(uint32_t i = 0; i < fCount; ++i) { - delete fStuff[i]; - } - uprv_free(fStuff); - } - } - NFRule* operator[](uint32_t index) const { return fStuff != NULL ? fStuff[index] : NULL; } - NFRule* remove(uint32_t index) { - if (fStuff == NULL) { - return NULL; - } - NFRule* result = fStuff[index]; - fCount -= 1; - for (uint32_t i = index; i < fCount; ++i) { // assumes small arrays - fStuff[i] = fStuff[i+1]; - } - return result; - } - void add(NFRule* thing) { - if (fCount == fCapacity) { - fCapacity += 10; - fStuff = (NFRule**)uprv_realloc(fStuff, fCapacity * sizeof(NFRule*)); // assume success - } - if (fStuff != NULL) { - fStuff[fCount++] = thing; - } else { - fCapacity = 0; - fCount = 0; - } - } - uint32_t size() const { return fCount; } - NFRule* last() const { return (fCount > 0 && fStuff != NULL) ? fStuff[fCount-1] : NULL; } - NFRule** release() { - add(NULL); // ensure null termination - NFRule** result = fStuff; - fStuff = NULL; - fCount = 0; - fCapacity = 0; - return result; - } - void deleteAll() { - NFRule** tmp = NULL; - int32_t size = fCount; - if (size > 0) { - tmp = release(); - for (int32_t i = 0; i < size; i++) { - delete tmp[i]; - } - if (tmp) { - uprv_free(tmp); - } - } - } - -private: - NFRuleList(const NFRuleList &other); // forbid copying of this class - NFRuleList &operator=(const NFRuleList &other); // forbid copying of this class -}; - -U_NAMESPACE_END - -/* U_HAVE_RBNF */ -#endif - -// NFRLIST_H -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 1997-2012, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* file name: nfrlist.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* Modification history +* Date Name Comments +* 10/11/2001 Doug Ported from ICU4J +*/ + +#ifndef NFRLIST_H +#define NFRLIST_H + +#include "unicode/rbnf.h" + +#if U_HAVE_RBNF + +#include "unicode/uobject.h" +#include "nfrule.h" + +#include "cmemory.h" + +U_NAMESPACE_BEGIN + +// unsafe class for internal use only. assume memory allocations succeed, indexes are valid. +// should be a template, but we can't use them + +class NFRuleList : public UMemory { +protected: + NFRule** fStuff; + uint32_t fCount; + uint32_t fCapacity; +public: + NFRuleList(uint32_t capacity = 10) + : fStuff(capacity ? (NFRule**)uprv_malloc(capacity * sizeof(NFRule*)) : nullptr) + , fCount(0) + , fCapacity(capacity) {} + ~NFRuleList() { + if (fStuff) { + for(uint32_t i = 0; i < fCount; ++i) { + delete fStuff[i]; + } + uprv_free(fStuff); + } + } + NFRule* operator[](uint32_t index) const { return fStuff != nullptr ? fStuff[index] : nullptr; } + NFRule* remove(uint32_t index) { + if (fStuff == nullptr) { + return nullptr; + } + NFRule* result = fStuff[index]; + fCount -= 1; + for (uint32_t i = index; i < fCount; ++i) { // assumes small arrays + fStuff[i] = fStuff[i+1]; + } + return result; + } + void add(NFRule* thing) { + if (fCount == fCapacity) { + fCapacity += 10; + fStuff = (NFRule**)uprv_realloc(fStuff, fCapacity * sizeof(NFRule*)); // assume success + } + if (fStuff != nullptr) { + fStuff[fCount++] = thing; + } else { + fCapacity = 0; + fCount = 0; + } + } + uint32_t size() const { return fCount; } + NFRule* last() const { return (fCount > 0 && fStuff != nullptr) ? fStuff[fCount-1] : nullptr; } + NFRule** release() { + add(nullptr); // ensure null termination + NFRule** result = fStuff; + fStuff = nullptr; + fCount = 0; + fCapacity = 0; + return result; + } + void deleteAll() { + NFRule** tmp = nullptr; + int32_t size = fCount; + if (size > 0) { + tmp = release(); + for (int32_t i = 0; i < size; i++) { + delete tmp[i]; + } + if (tmp) { + uprv_free(tmp); + } + } + } + +private: + NFRuleList(const NFRuleList &other); // forbid copying of this class + NFRuleList &operator=(const NFRuleList &other); // forbid copying of this class +}; + +U_NAMESPACE_END + +/* U_HAVE_RBNF */ +#endif + +// NFRLIST_H +#endif diff --git a/deps/icu-small/source/i18n/nfrs.cpp b/deps/icu-small/source/i18n/nfrs.cpp index 17fab139113907..3549d90239a565 100644 --- a/deps/icu-small/source/i18n/nfrs.cpp +++ b/deps/icu-small/source/i18n/nfrs.cpp @@ -1,1035 +1,1035 @@ -// © 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 name: nfrs.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* Modification history -* Date Name Comments -* 10/11/2001 Doug Ported from ICU4J -*/ - -#include "nfrs.h" - -#if U_HAVE_RBNF - -#include "unicode/uchar.h" -#include "nfrule.h" -#include "nfrlist.h" -#include "patternprops.h" -#include "putilimp.h" - -#ifdef RBNF_DEBUG -#include "cmemory.h" -#endif - -enum { - /** -x */ - NEGATIVE_RULE_INDEX = 0, - /** x.x */ - IMPROPER_FRACTION_RULE_INDEX = 1, - /** 0.x */ - PROPER_FRACTION_RULE_INDEX = 2, - /** x.0 */ - DEFAULT_RULE_INDEX = 3, - /** Inf */ - INFINITY_RULE_INDEX = 4, - /** NaN */ - NAN_RULE_INDEX = 5, - NON_NUMERICAL_RULE_LENGTH = 6 -}; - -U_NAMESPACE_BEGIN - -#if 0 -// euclid's algorithm works with doubles -// note, doubles only get us up to one quadrillion or so, which -// isn't as much range as we get with longs. We probably still -// want either 64-bit math, or BigInteger. - -static int64_t -util_lcm(int64_t x, int64_t y) -{ - x.abs(); - y.abs(); - - if (x == 0 || y == 0) { - return 0; - } else { - do { - if (x < y) { - int64_t t = x; x = y; y = t; - } - x -= y * (x/y); - } while (x != 0); - - return y; - } -} - -#else -/** - * Calculates the least common multiple of x and y. - */ -static int64_t -util_lcm(int64_t x, int64_t y) -{ - // binary gcd algorithm from Knuth, "The Art of Computer Programming," - // vol. 2, 1st ed., pp. 298-299 - int64_t x1 = x; - int64_t y1 = y; - - int p2 = 0; - while ((x1 & 1) == 0 && (y1 & 1) == 0) { - ++p2; - x1 >>= 1; - y1 >>= 1; - } - - int64_t t; - if ((x1 & 1) == 1) { - t = -y1; - } else { - t = x1; - } - - while (t != 0) { - while ((t & 1) == 0) { - t = t >> 1; - } - if (t > 0) { - x1 = t; - } else { - y1 = -t; - } - t = x1 - y1; - } - - int64_t gcd = x1 << p2; - - // x * y == gcd(x, y) * lcm(x, y) - return x / gcd * y; -} -#endif - -static const UChar gPercent = 0x0025; -static const UChar gColon = 0x003a; -static const UChar gSemicolon = 0x003b; -static const UChar gLineFeed = 0x000a; - -static const UChar gPercentPercent[] = -{ - 0x25, 0x25, 0 -}; /* "%%" */ - -static const UChar gNoparse[] = -{ - 0x40, 0x6E, 0x6F, 0x70, 0x61, 0x72, 0x73, 0x65, 0 -}; /* "@noparse" */ - -NFRuleSet::NFRuleSet(RuleBasedNumberFormat *_owner, UnicodeString* descriptions, int32_t index, UErrorCode& status) - : name() - , rules(0) - , owner(_owner) - , fractionRules() - , fIsFractionRuleSet(false) - , fIsPublic(false) - , fIsParseable(true) -{ - for (int32_t i = 0; i < NON_NUMERICAL_RULE_LENGTH; ++i) { - nonNumericalRules[i] = NULL; - } - - if (U_FAILURE(status)) { - return; - } - - UnicodeString& description = descriptions[index]; // !!! make sure index is valid - - if (description.length() == 0) { - // throw new IllegalArgumentException("Empty rule set description"); - status = U_PARSE_ERROR; - return; - } - - // if the description begins with a rule set name (the rule set - // name can be omitted in formatter descriptions that consist - // of only one rule set), copy it out into our "name" member - // and delete it from the description - if (description.charAt(0) == gPercent) { - int32_t pos = description.indexOf(gColon); - if (pos == -1) { - // throw new IllegalArgumentException("Rule set name doesn't end in colon"); - status = U_PARSE_ERROR; - } else { - name.setTo(description, 0, pos); - while (pos < description.length() && PatternProps::isWhiteSpace(description.charAt(++pos))) { - } - description.remove(0, pos); - } - } else { - name.setTo(UNICODE_STRING_SIMPLE("%default")); - } - - if (description.length() == 0) { - // throw new IllegalArgumentException("Empty rule set description"); - status = U_PARSE_ERROR; - } - - fIsPublic = name.indexOf(gPercentPercent, 2, 0) != 0; - - if ( name.endsWith(gNoparse,8) ) { - fIsParseable = false; - name.truncate(name.length()-8); // remove the @noparse from the name - } - - // all of the other members of NFRuleSet are initialized - // by parseRules() -} - -void -NFRuleSet::parseRules(UnicodeString& description, UErrorCode& status) -{ - // start by creating a Vector whose elements are Strings containing - // the descriptions of the rules (one rule per element). The rules - // are separated by semicolons (there's no escape facility: ALL - // semicolons are rule delimiters) - - if (U_FAILURE(status)) { - return; - } - - // ensure we are starting with an empty rule list - rules.deleteAll(); - - // dlf - the original code kept a separate description array for no reason, - // so I got rid of it. The loop was too complex so I simplified it. - - UnicodeString currentDescription; - int32_t oldP = 0; - while (oldP < description.length()) { - int32_t p = description.indexOf(gSemicolon, oldP); - if (p == -1) { - p = description.length(); - } - currentDescription.setTo(description, oldP, p - oldP); - NFRule::makeRules(currentDescription, this, rules.last(), owner, rules, status); - oldP = p + 1; - } - - // for rules that didn't specify a base value, their base values - // were initialized to 0. Make another pass through the list and - // set all those rules' base values. We also remove any special - // rules from the list and put them into their own member variables - int64_t defaultBaseValue = 0; - - // (this isn't a for loop because we might be deleting items from - // the vector-- we want to make sure we only increment i when - // we _didn't_ delete anything from the vector) - int32_t rulesSize = rules.size(); - for (int32_t i = 0; i < rulesSize; i++) { - NFRule* rule = rules[i]; - int64_t baseValue = rule->getBaseValue(); - - if (baseValue == 0) { - // if the rule's base value is 0, fill in a default - // base value (this will be 1 plus the preceding - // rule's base value for regular rule sets, and the - // same as the preceding rule's base value in fraction - // rule sets) - rule->setBaseValue(defaultBaseValue, status); - } - else { - // if it's a regular rule that already knows its base value, - // check to make sure the rules are in order, and update - // the default base value for the next rule - if (baseValue < defaultBaseValue) { - // throw new IllegalArgumentException("Rules are not in order"); - status = U_PARSE_ERROR; - return; - } - defaultBaseValue = baseValue; - } - if (!fIsFractionRuleSet) { - ++defaultBaseValue; - } - } -} - -/** - * Set one of the non-numerical rules. - * @param rule The rule to set. - */ -void NFRuleSet::setNonNumericalRule(NFRule *rule) { - int64_t baseValue = rule->getBaseValue(); - if (baseValue == NFRule::kNegativeNumberRule) { - delete nonNumericalRules[NEGATIVE_RULE_INDEX]; - nonNumericalRules[NEGATIVE_RULE_INDEX] = rule; - } - else if (baseValue == NFRule::kImproperFractionRule) { - setBestFractionRule(IMPROPER_FRACTION_RULE_INDEX, rule, true); - } - else if (baseValue == NFRule::kProperFractionRule) { - setBestFractionRule(PROPER_FRACTION_RULE_INDEX, rule, true); - } - else if (baseValue == NFRule::kDefaultRule) { - setBestFractionRule(DEFAULT_RULE_INDEX, rule, true); - } - else if (baseValue == NFRule::kInfinityRule) { - delete nonNumericalRules[INFINITY_RULE_INDEX]; - nonNumericalRules[INFINITY_RULE_INDEX] = rule; - } - else if (baseValue == NFRule::kNaNRule) { - delete nonNumericalRules[NAN_RULE_INDEX]; - nonNumericalRules[NAN_RULE_INDEX] = rule; - } -} - -/** - * Determine the best fraction rule to use. Rules matching the decimal point from - * DecimalFormatSymbols become the main set of rules to use. - * @param originalIndex The index into nonNumericalRules - * @param newRule The new rule to consider - * @param rememberRule Should the new rule be added to fractionRules. - */ -void NFRuleSet::setBestFractionRule(int32_t originalIndex, NFRule *newRule, UBool rememberRule) { - if (rememberRule) { - fractionRules.add(newRule); - } - NFRule *bestResult = nonNumericalRules[originalIndex]; - if (bestResult == NULL) { - nonNumericalRules[originalIndex] = newRule; - } - else { - // We have more than one. Which one is better? - const DecimalFormatSymbols *decimalFormatSymbols = owner->getDecimalFormatSymbols(); - if (decimalFormatSymbols->getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol).charAt(0) - == newRule->getDecimalPoint()) - { - nonNumericalRules[originalIndex] = newRule; - } - // else leave it alone - } -} - -NFRuleSet::~NFRuleSet() -{ - for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) { - if (i != IMPROPER_FRACTION_RULE_INDEX - && i != PROPER_FRACTION_RULE_INDEX - && i != DEFAULT_RULE_INDEX) - { - delete nonNumericalRules[i]; - } - // else it will be deleted via NFRuleList fractionRules - } -} - -static UBool -util_equalRules(const NFRule* rule1, const NFRule* rule2) -{ - if (rule1) { - if (rule2) { - return *rule1 == *rule2; - } - } else if (!rule2) { - return true; - } - return false; -} - -bool -NFRuleSet::operator==(const NFRuleSet& rhs) const -{ - if (rules.size() == rhs.rules.size() && - fIsFractionRuleSet == rhs.fIsFractionRuleSet && - name == rhs.name) { - - // ...then compare the non-numerical rule lists... - for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) { - if (!util_equalRules(nonNumericalRules[i], rhs.nonNumericalRules[i])) { - return false; - } - } - - // ...then compare the rule lists... - for (uint32_t i = 0; i < rules.size(); ++i) { - if (*rules[i] != *rhs.rules[i]) { - return false; - } - } - return true; - } - return false; -} - -void -NFRuleSet::setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& status) { - for (uint32_t i = 0; i < rules.size(); ++i) { - rules[i]->setDecimalFormatSymbols(newSymbols, status); - } - // Switch the fraction rules to mirror the DecimalFormatSymbols. - for (int32_t nonNumericalIdx = IMPROPER_FRACTION_RULE_INDEX; nonNumericalIdx <= DEFAULT_RULE_INDEX; nonNumericalIdx++) { - if (nonNumericalRules[nonNumericalIdx]) { - for (uint32_t fIdx = 0; fIdx < fractionRules.size(); fIdx++) { - NFRule *fractionRule = fractionRules[fIdx]; - if (nonNumericalRules[nonNumericalIdx]->getBaseValue() == fractionRule->getBaseValue()) { - setBestFractionRule(nonNumericalIdx, fractionRule, false); - } - } - } - } - - for (uint32_t nnrIdx = 0; nnrIdx < NON_NUMERICAL_RULE_LENGTH; nnrIdx++) { - NFRule *rule = nonNumericalRules[nnrIdx]; - if (rule) { - rule->setDecimalFormatSymbols(newSymbols, status); - } - } -} - -#define RECURSION_LIMIT 64 - -void -NFRuleSet::format(int64_t number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const -{ - if (recursionCount >= RECURSION_LIMIT) { - // stop recursion - status = U_INVALID_STATE_ERROR; - return; - } - const NFRule *rule = findNormalRule(number); - if (rule) { // else error, but can't report it - rule->doFormat(number, toAppendTo, pos, ++recursionCount, status); - } -} - -void -NFRuleSet::format(double number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const -{ - if (recursionCount >= RECURSION_LIMIT) { - // stop recursion - status = U_INVALID_STATE_ERROR; - return; - } - const NFRule *rule = findDoubleRule(number); - if (rule) { // else error, but can't report it - rule->doFormat(number, toAppendTo, pos, ++recursionCount, status); - } -} - -const NFRule* -NFRuleSet::findDoubleRule(double number) const -{ - // if this is a fraction rule set, use findFractionRuleSetRule() - if (isFractionRuleSet()) { - return findFractionRuleSetRule(number); - } - - if (uprv_isNaN(number)) { - const NFRule *rule = nonNumericalRules[NAN_RULE_INDEX]; - if (!rule) { - rule = owner->getDefaultNaNRule(); - } - return rule; - } - - // if the number is negative, return the negative number rule - // (if there isn't a negative-number rule, we pretend it's a - // positive number) - if (number < 0) { - if (nonNumericalRules[NEGATIVE_RULE_INDEX]) { - return nonNumericalRules[NEGATIVE_RULE_INDEX]; - } else { - number = -number; - } - } - - if (uprv_isInfinite(number)) { - const NFRule *rule = nonNumericalRules[INFINITY_RULE_INDEX]; - if (!rule) { - rule = owner->getDefaultInfinityRule(); - } - return rule; - } - - // if the number isn't an integer, we use one of the fraction rules... - if (number != uprv_floor(number)) { - // if the number is between 0 and 1, return the proper - // fraction rule - if (number < 1 && nonNumericalRules[PROPER_FRACTION_RULE_INDEX]) { - return nonNumericalRules[PROPER_FRACTION_RULE_INDEX]; - } - // otherwise, return the improper fraction rule - else if (nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX]) { - return nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX]; - } - } - - // if there's a default rule, use it to format the number - if (nonNumericalRules[DEFAULT_RULE_INDEX]) { - return nonNumericalRules[DEFAULT_RULE_INDEX]; - } - - // and if we haven't yet returned a rule, use findNormalRule() - // to find the applicable rule - int64_t r = util64_fromDouble(number + 0.5); - return findNormalRule(r); -} - -const NFRule * -NFRuleSet::findNormalRule(int64_t number) const -{ - // if this is a fraction rule set, use findFractionRuleSetRule() - // to find the rule (we should only go into this clause if the - // value is 0) - if (fIsFractionRuleSet) { - return findFractionRuleSetRule((double)number); - } - - // if the number is negative, return the negative-number rule - // (if there isn't one, pretend the number is positive) - if (number < 0) { - if (nonNumericalRules[NEGATIVE_RULE_INDEX]) { - return nonNumericalRules[NEGATIVE_RULE_INDEX]; - } else { - number = -number; - } - } - - // we have to repeat the preceding two checks, even though we - // do them in findRule(), because the version of format() that - // takes a long bypasses findRule() and goes straight to this - // function. This function does skip the fraction rules since - // we know the value is an integer (it also skips the default - // rule, since it's considered a fraction rule. Skipping the - // default rule in this function is also how we avoid infinite - // recursion) - - // {dlf} unfortunately this fails if there are no rules except - // special rules. If there are no rules, use the default rule. - - // binary-search the rule list for the applicable rule - // (a rule is used for all values from its base value to - // the next rule's base value) - int32_t hi = rules.size(); - if (hi > 0) { - int32_t lo = 0; - - while (lo < hi) { - int32_t mid = (lo + hi) / 2; - if (rules[mid]->getBaseValue() == number) { - return rules[mid]; - } - else if (rules[mid]->getBaseValue() > number) { - hi = mid; - } - else { - lo = mid + 1; - } - } - if (hi == 0) { // bad rule set, minimum base > 0 - return NULL; // want to throw exception here - } - - NFRule *result = rules[hi - 1]; - - // use shouldRollBack() to see whether we need to invoke the - // rollback rule (see shouldRollBack()'s documentation for - // an explanation of the rollback rule). If we do, roll back - // one rule and return that one instead of the one we'd normally - // return - if (result->shouldRollBack(number)) { - if (hi == 1) { // bad rule set, no prior rule to rollback to from this base - return NULL; - } - result = rules[hi - 2]; - } - return result; - } - // else use the default rule - return nonNumericalRules[DEFAULT_RULE_INDEX]; -} - -/** - * If this rule is a fraction rule set, this function is used by - * findRule() to select the most appropriate rule for formatting - * the number. Basically, the base value of each rule in the rule - * set is treated as the denominator of a fraction. Whichever - * denominator can produce the fraction closest in value to the - * number passed in is the result. If there's a tie, the earlier - * one in the list wins. (If there are two rules in a row with the - * same base value, the first one is used when the numerator of the - * fraction would be 1, and the second rule is used the rest of the - * time. - * @param number The number being formatted (which will always be - * a number between 0 and 1) - * @return The rule to use to format this number - */ -const NFRule* -NFRuleSet::findFractionRuleSetRule(double number) const -{ - // the obvious way to do this (multiply the value being formatted - // by each rule's base value until you get an integral result) - // doesn't work because of rounding error. This method is more - // accurate - - // find the least common multiple of the rules' base values - // and multiply this by the number being formatted. This is - // all the precision we need, and we can do all of the rest - // of the math using integer arithmetic - int64_t leastCommonMultiple = rules[0]->getBaseValue(); - int64_t numerator; - { - for (uint32_t i = 1; i < rules.size(); ++i) { - leastCommonMultiple = util_lcm(leastCommonMultiple, rules[i]->getBaseValue()); - } - numerator = util64_fromDouble(number * (double)leastCommonMultiple + 0.5); - } - // for each rule, do the following... - int64_t tempDifference; - int64_t difference = util64_fromDouble(uprv_maxMantissa()); - int32_t winner = 0; - for (uint32_t i = 0; i < rules.size(); ++i) { - // "numerator" is the numerator of the fraction if the - // denominator is the LCD. The numerator if the rule's - // base value is the denominator is "numerator" times the - // base value divided bythe LCD. Here we check to see if - // that's an integer, and if not, how close it is to being - // an integer. - tempDifference = numerator * rules[i]->getBaseValue() % leastCommonMultiple; - - - // normalize the result of the above calculation: we want - // the numerator's distance from the CLOSEST multiple - // of the LCD - if (leastCommonMultiple - tempDifference < tempDifference) { - tempDifference = leastCommonMultiple - tempDifference; - } - - // if this is as close as we've come, keep track of how close - // that is, and the line number of the rule that did it. If - // we've scored a direct hit, we don't have to look at any more - // rules - if (tempDifference < difference) { - difference = tempDifference; - winner = i; - if (difference == 0) { - break; - } - } - } - - // if we have two successive rules that both have the winning base - // value, then the first one (the one we found above) is used if - // the numerator of the fraction is 1 and the second one is used if - // the numerator of the fraction is anything else (this lets us - // do things like "one third"/"two thirds" without having to define - // a whole bunch of extra rule sets) - if ((unsigned)(winner + 1) < rules.size() && - rules[winner + 1]->getBaseValue() == rules[winner]->getBaseValue()) { - double n = ((double)rules[winner]->getBaseValue()) * number; - if (n < 0.5 || n >= 2) { - ++winner; - } - } - - // finally, return the winning rule - return rules[winner]; -} - -/** - * Parses a string. Matches the string to be parsed against each - * of its rules (with a base value less than upperBound) and returns - * the value produced by the rule that matched the most characters - * in the source string. - * @param text The string to parse - * @param parsePosition The initial position is ignored and assumed - * to be 0. On exit, this object has been updated to point to the - * first character position this rule set didn't consume. - * @param upperBound Limits the rules that can be allowed to match. - * Only rules whose base values are strictly less than upperBound - * are considered. - * @return The numerical result of parsing this string. This will - * be the matching rule's base value, composed appropriately with - * the results of matching any of its substitutions. The object - * will be an instance of Long if it's an integral value; otherwise, - * it will be an instance of Double. This function always returns - * a valid object: If nothing matched the input string at all, - * this function returns new Long(0), and the parse position is - * left unchanged. - */ -#ifdef RBNF_DEBUG -#include - -static void dumpUS(FILE* f, const UnicodeString& us) { - int len = us.length(); - char* buf = (char *)uprv_malloc((len+1)*sizeof(char)); //new char[len+1]; - if (buf != NULL) { - us.extract(0, len, buf); - buf[len] = 0; - fprintf(f, "%s", buf); - uprv_free(buf); //delete[] buf; - } -} -#endif - -UBool -NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBound, uint32_t nonNumericalExecutedRuleMask, Formattable& result) const -{ - // try matching each rule in the rule set against the text being - // parsed. Whichever one matches the most characters is the one - // that determines the value we return. - - result.setLong(0); - - // dump out if there's no text to parse - if (text.length() == 0) { - return 0; - } - - ParsePosition highWaterMark; - ParsePosition workingPos = pos; - -#ifdef RBNF_DEBUG - fprintf(stderr, " %x '", this); - dumpUS(stderr, name); - fprintf(stderr, "' text '"); - dumpUS(stderr, text); - fprintf(stderr, "'\n"); - fprintf(stderr, " parse negative: %d\n", this, negativeNumberRule != 0); -#endif - // Try each of the negative rules, fraction rules, infinity rules and NaN rules - for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) { - if (nonNumericalRules[i] && ((nonNumericalExecutedRuleMask >> i) & 1) == 0) { - // Mark this rule as being executed so that we don't try to execute it again. - nonNumericalExecutedRuleMask |= 1 << i; - - Formattable tempResult; - UBool success = nonNumericalRules[i]->doParse(text, workingPos, 0, upperBound, nonNumericalExecutedRuleMask, tempResult); - if (success && (workingPos.getIndex() > highWaterMark.getIndex())) { - result = tempResult; - highWaterMark = workingPos; - } - workingPos = pos; - } - } -#ifdef RBNF_DEBUG - fprintf(stderr, " continue other with text '"); - dumpUS(stderr, text); - fprintf(stderr, "' hwm: %d\n", highWaterMark.getIndex()); -#endif - - // finally, go through the regular rules one at a time. We start - // at the end of the list because we want to try matching the most - // sigificant rule first (this helps ensure that we parse - // "five thousand three hundred six" as - // "(five thousand) (three hundred) (six)" rather than - // "((five thousand three) hundred) (six)"). Skip rules whose - // base values are higher than the upper bound (again, this helps - // limit ambiguity by making sure the rules that match a rule's - // are less significant than the rule containing the substitutions)/ - { - int64_t ub = util64_fromDouble(upperBound); -#ifdef RBNF_DEBUG - { - char ubstr[64]; - util64_toa(ub, ubstr, 64); - char ubstrhex[64]; - util64_toa(ub, ubstrhex, 64, 16); - fprintf(stderr, "ub: %g, i64: %s (%s)\n", upperBound, ubstr, ubstrhex); - } -#endif - for (int32_t i = rules.size(); --i >= 0 && highWaterMark.getIndex() < text.length();) { - if ((!fIsFractionRuleSet) && (rules[i]->getBaseValue() >= ub)) { - continue; - } - Formattable tempResult; - UBool success = rules[i]->doParse(text, workingPos, fIsFractionRuleSet, upperBound, nonNumericalExecutedRuleMask, tempResult); - if (success && workingPos.getIndex() > highWaterMark.getIndex()) { - result = tempResult; - highWaterMark = workingPos; - } - workingPos = pos; - } - } -#ifdef RBNF_DEBUG - fprintf(stderr, " exit\n"); -#endif - // finally, update the parse position we were passed to point to the - // first character we didn't use, and return the result that - // corresponds to that string of characters - pos = highWaterMark; - - return 1; -} - -void -NFRuleSet::appendRules(UnicodeString& result) const -{ - uint32_t i; - - // the rule set name goes first... - result.append(name); - result.append(gColon); - result.append(gLineFeed); - - // followed by the regular rules... - for (i = 0; i < rules.size(); i++) { - rules[i]->_appendRuleText(result); - result.append(gLineFeed); - } - - // followed by the special rules (if they exist) - for (i = 0; i < NON_NUMERICAL_RULE_LENGTH; ++i) { - NFRule *rule = nonNumericalRules[i]; - if (nonNumericalRules[i]) { - if (rule->getBaseValue() == NFRule::kImproperFractionRule - || rule->getBaseValue() == NFRule::kProperFractionRule - || rule->getBaseValue() == NFRule::kDefaultRule) - { - for (uint32_t fIdx = 0; fIdx < fractionRules.size(); fIdx++) { - NFRule *fractionRule = fractionRules[fIdx]; - if (fractionRule->getBaseValue() == rule->getBaseValue()) { - fractionRule->_appendRuleText(result); - result.append(gLineFeed); - } - } - } - else { - rule->_appendRuleText(result); - result.append(gLineFeed); - } - } - } -} - -// utility functions - -int64_t util64_fromDouble(double d) { - int64_t result = 0; - if (!uprv_isNaN(d)) { - double mant = uprv_maxMantissa(); - if (d < -mant) { - d = -mant; - } else if (d > mant) { - d = mant; - } - UBool neg = d < 0; - if (neg) { - d = -d; - } - result = (int64_t)uprv_floor(d); - if (neg) { - result = -result; - } - } - return result; -} - -uint64_t util64_pow(uint32_t base, uint16_t exponent) { - if (base == 0) { - return 0; - } - uint64_t result = 1; - uint64_t pow = base; - while (true) { - if ((exponent & 1) == 1) { - result *= pow; - } - exponent >>= 1; - if (exponent == 0) { - break; - } - pow *= pow; - } - return result; -} - -static const uint8_t asciiDigits[] = { - 0x30u, 0x31u, 0x32u, 0x33u, 0x34u, 0x35u, 0x36u, 0x37u, - 0x38u, 0x39u, 0x61u, 0x62u, 0x63u, 0x64u, 0x65u, 0x66u, - 0x67u, 0x68u, 0x69u, 0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, - 0x6fu, 0x70u, 0x71u, 0x72u, 0x73u, 0x74u, 0x75u, 0x76u, - 0x77u, 0x78u, 0x79u, 0x7au, -}; - -static const UChar kUMinus = (UChar)0x002d; - -#ifdef RBNF_DEBUG -static const char kMinus = '-'; - -static const uint8_t digitInfo[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0x80u, 0x81u, 0x82u, 0x83u, 0x84u, 0x85u, 0x86u, 0x87u, - 0x88u, 0x89u, 0, 0, 0, 0, 0, 0, - 0, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, 0x90u, - 0x91u, 0x92u, 0x93u, 0x94u, 0x95u, 0x96u, 0x97u, 0x98u, - 0x99u, 0x9au, 0x9bu, 0x9cu, 0x9du, 0x9eu, 0x9fu, 0xa0u, - 0xa1u, 0xa2u, 0xa3u, 0, 0, 0, 0, 0, - 0, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, 0x90u, - 0x91u, 0x92u, 0x93u, 0x94u, 0x95u, 0x96u, 0x97u, 0x98u, - 0x99u, 0x9au, 0x9bu, 0x9cu, 0x9du, 0x9eu, 0x9fu, 0xa0u, - 0xa1u, 0xa2u, 0xa3u, 0, 0, 0, 0, 0, -}; - -int64_t util64_atoi(const char* str, uint32_t radix) -{ - if (radix > 36) { - radix = 36; - } else if (radix < 2) { - radix = 2; - } - int64_t lradix = radix; - - int neg = 0; - if (*str == kMinus) { - ++str; - neg = 1; - } - int64_t result = 0; - uint8_t b; - while ((b = digitInfo[*str++]) && ((b &= 0x7f) < radix)) { - result *= lradix; - result += (int32_t)b; - } - if (neg) { - result = -result; - } - return result; -} - -int64_t util64_utoi(const UChar* str, uint32_t radix) -{ - if (radix > 36) { - radix = 36; - } else if (radix < 2) { - radix = 2; - } - int64_t lradix = radix; - - int neg = 0; - if (*str == kUMinus) { - ++str; - neg = 1; - } - int64_t result = 0; - UChar c; - uint8_t b; - while (((c = *str++) < 0x0080) && (b = digitInfo[c]) && ((b &= 0x7f) < radix)) { - result *= lradix; - result += (int32_t)b; - } - if (neg) { - result = -result; - } - return result; -} - -uint32_t util64_toa(int64_t w, char* buf, uint32_t len, uint32_t radix, UBool raw) -{ - if (radix > 36) { - radix = 36; - } else if (radix < 2) { - radix = 2; - } - int64_t base = radix; - - char* p = buf; - if (len && (w < 0) && (radix == 10) && !raw) { - w = -w; - *p++ = kMinus; - --len; - } else if (len && (w == 0)) { - *p++ = (char)raw ? 0 : asciiDigits[0]; - --len; - } - - while (len && w != 0) { - int64_t n = w / base; - int64_t m = n * base; - int32_t d = (int32_t)(w-m); - *p++ = raw ? (char)d : asciiDigits[d]; - w = n; - --len; - } - if (len) { - *p = 0; // null terminate if room for caller convenience - } - - len = p - buf; - if (*buf == kMinus) { - ++buf; - } - while (--p > buf) { - char c = *p; - *p = *buf; - *buf = c; - ++buf; - } - - return len; -} -#endif - -uint32_t util64_tou(int64_t w, UChar* buf, uint32_t len, uint32_t radix, UBool raw) -{ - if (radix > 36) { - radix = 36; - } else if (radix < 2) { - radix = 2; - } - int64_t base = radix; - - UChar* p = buf; - if (len && (w < 0) && (radix == 10) && !raw) { - w = -w; - *p++ = kUMinus; - --len; - } else if (len && (w == 0)) { - *p++ = (UChar)raw ? 0 : asciiDigits[0]; - --len; - } - - while (len && (w != 0)) { - int64_t n = w / base; - int64_t m = n * base; - int32_t d = (int32_t)(w-m); - *p++ = (UChar)(raw ? d : asciiDigits[d]); - w = n; - --len; - } - if (len) { - *p = 0; // null terminate if room for caller convenience - } - - len = (uint32_t)(p - buf); - if (*buf == kUMinus) { - ++buf; - } - while (--p > buf) { - UChar c = *p; - *p = *buf; - *buf = c; - ++buf; - } - - return len; -} - - -U_NAMESPACE_END - -/* U_HAVE_RBNF */ -#endif +// © 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 name: nfrs.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* Modification history +* Date Name Comments +* 10/11/2001 Doug Ported from ICU4J +*/ + +#include "nfrs.h" + +#if U_HAVE_RBNF + +#include "unicode/uchar.h" +#include "nfrule.h" +#include "nfrlist.h" +#include "patternprops.h" +#include "putilimp.h" + +#ifdef RBNF_DEBUG +#include "cmemory.h" +#endif + +enum { + /** -x */ + NEGATIVE_RULE_INDEX = 0, + /** x.x */ + IMPROPER_FRACTION_RULE_INDEX = 1, + /** 0.x */ + PROPER_FRACTION_RULE_INDEX = 2, + /** x.0 */ + DEFAULT_RULE_INDEX = 3, + /** Inf */ + INFINITY_RULE_INDEX = 4, + /** NaN */ + NAN_RULE_INDEX = 5, + NON_NUMERICAL_RULE_LENGTH = 6 +}; + +U_NAMESPACE_BEGIN + +#if 0 +// euclid's algorithm works with doubles +// note, doubles only get us up to one quadrillion or so, which +// isn't as much range as we get with longs. We probably still +// want either 64-bit math, or BigInteger. + +static int64_t +util_lcm(int64_t x, int64_t y) +{ + x.abs(); + y.abs(); + + if (x == 0 || y == 0) { + return 0; + } else { + do { + if (x < y) { + int64_t t = x; x = y; y = t; + } + x -= y * (x/y); + } while (x != 0); + + return y; + } +} + +#else +/** + * Calculates the least common multiple of x and y. + */ +static int64_t +util_lcm(int64_t x, int64_t y) +{ + // binary gcd algorithm from Knuth, "The Art of Computer Programming," + // vol. 2, 1st ed., pp. 298-299 + int64_t x1 = x; + int64_t y1 = y; + + int p2 = 0; + while ((x1 & 1) == 0 && (y1 & 1) == 0) { + ++p2; + x1 >>= 1; + y1 >>= 1; + } + + int64_t t; + if ((x1 & 1) == 1) { + t = -y1; + } else { + t = x1; + } + + while (t != 0) { + while ((t & 1) == 0) { + t = t >> 1; + } + if (t > 0) { + x1 = t; + } else { + y1 = -t; + } + t = x1 - y1; + } + + int64_t gcd = x1 << p2; + + // x * y == gcd(x, y) * lcm(x, y) + return x / gcd * y; +} +#endif + +static const char16_t gPercent = 0x0025; +static const char16_t gColon = 0x003a; +static const char16_t gSemicolon = 0x003b; +static const char16_t gLineFeed = 0x000a; + +static const char16_t gPercentPercent[] = +{ + 0x25, 0x25, 0 +}; /* "%%" */ + +static const char16_t gNoparse[] = +{ + 0x40, 0x6E, 0x6F, 0x70, 0x61, 0x72, 0x73, 0x65, 0 +}; /* "@noparse" */ + +NFRuleSet::NFRuleSet(RuleBasedNumberFormat *_owner, UnicodeString* descriptions, int32_t index, UErrorCode& status) + : name() + , rules(0) + , owner(_owner) + , fractionRules() + , fIsFractionRuleSet(false) + , fIsPublic(false) + , fIsParseable(true) +{ + for (int32_t i = 0; i < NON_NUMERICAL_RULE_LENGTH; ++i) { + nonNumericalRules[i] = nullptr; + } + + if (U_FAILURE(status)) { + return; + } + + UnicodeString& description = descriptions[index]; // !!! make sure index is valid + + if (description.length() == 0) { + // throw new IllegalArgumentException("Empty rule set description"); + status = U_PARSE_ERROR; + return; + } + + // if the description begins with a rule set name (the rule set + // name can be omitted in formatter descriptions that consist + // of only one rule set), copy it out into our "name" member + // and delete it from the description + if (description.charAt(0) == gPercent) { + int32_t pos = description.indexOf(gColon); + if (pos == -1) { + // throw new IllegalArgumentException("Rule set name doesn't end in colon"); + status = U_PARSE_ERROR; + } else { + name.setTo(description, 0, pos); + while (pos < description.length() && PatternProps::isWhiteSpace(description.charAt(++pos))) { + } + description.remove(0, pos); + } + } else { + name.setTo(UNICODE_STRING_SIMPLE("%default")); + } + + if (description.length() == 0) { + // throw new IllegalArgumentException("Empty rule set description"); + status = U_PARSE_ERROR; + } + + fIsPublic = name.indexOf(gPercentPercent, 2, 0) != 0; + + if ( name.endsWith(gNoparse,8) ) { + fIsParseable = false; + name.truncate(name.length()-8); // remove the @noparse from the name + } + + // all of the other members of NFRuleSet are initialized + // by parseRules() +} + +void +NFRuleSet::parseRules(UnicodeString& description, UErrorCode& status) +{ + // start by creating a Vector whose elements are Strings containing + // the descriptions of the rules (one rule per element). The rules + // are separated by semicolons (there's no escape facility: ALL + // semicolons are rule delimiters) + + if (U_FAILURE(status)) { + return; + } + + // ensure we are starting with an empty rule list + rules.deleteAll(); + + // dlf - the original code kept a separate description array for no reason, + // so I got rid of it. The loop was too complex so I simplified it. + + UnicodeString currentDescription; + int32_t oldP = 0; + while (oldP < description.length()) { + int32_t p = description.indexOf(gSemicolon, oldP); + if (p == -1) { + p = description.length(); + } + currentDescription.setTo(description, oldP, p - oldP); + NFRule::makeRules(currentDescription, this, rules.last(), owner, rules, status); + oldP = p + 1; + } + + // for rules that didn't specify a base value, their base values + // were initialized to 0. Make another pass through the list and + // set all those rules' base values. We also remove any special + // rules from the list and put them into their own member variables + int64_t defaultBaseValue = 0; + + // (this isn't a for loop because we might be deleting items from + // the vector-- we want to make sure we only increment i when + // we _didn't_ delete anything from the vector) + int32_t rulesSize = rules.size(); + for (int32_t i = 0; i < rulesSize; i++) { + NFRule* rule = rules[i]; + int64_t baseValue = rule->getBaseValue(); + + if (baseValue == 0) { + // if the rule's base value is 0, fill in a default + // base value (this will be 1 plus the preceding + // rule's base value for regular rule sets, and the + // same as the preceding rule's base value in fraction + // rule sets) + rule->setBaseValue(defaultBaseValue, status); + } + else { + // if it's a regular rule that already knows its base value, + // check to make sure the rules are in order, and update + // the default base value for the next rule + if (baseValue < defaultBaseValue) { + // throw new IllegalArgumentException("Rules are not in order"); + status = U_PARSE_ERROR; + return; + } + defaultBaseValue = baseValue; + } + if (!fIsFractionRuleSet) { + ++defaultBaseValue; + } + } +} + +/** + * Set one of the non-numerical rules. + * @param rule The rule to set. + */ +void NFRuleSet::setNonNumericalRule(NFRule *rule) { + int64_t baseValue = rule->getBaseValue(); + if (baseValue == NFRule::kNegativeNumberRule) { + delete nonNumericalRules[NEGATIVE_RULE_INDEX]; + nonNumericalRules[NEGATIVE_RULE_INDEX] = rule; + } + else if (baseValue == NFRule::kImproperFractionRule) { + setBestFractionRule(IMPROPER_FRACTION_RULE_INDEX, rule, true); + } + else if (baseValue == NFRule::kProperFractionRule) { + setBestFractionRule(PROPER_FRACTION_RULE_INDEX, rule, true); + } + else if (baseValue == NFRule::kDefaultRule) { + setBestFractionRule(DEFAULT_RULE_INDEX, rule, true); + } + else if (baseValue == NFRule::kInfinityRule) { + delete nonNumericalRules[INFINITY_RULE_INDEX]; + nonNumericalRules[INFINITY_RULE_INDEX] = rule; + } + else if (baseValue == NFRule::kNaNRule) { + delete nonNumericalRules[NAN_RULE_INDEX]; + nonNumericalRules[NAN_RULE_INDEX] = rule; + } +} + +/** + * Determine the best fraction rule to use. Rules matching the decimal point from + * DecimalFormatSymbols become the main set of rules to use. + * @param originalIndex The index into nonNumericalRules + * @param newRule The new rule to consider + * @param rememberRule Should the new rule be added to fractionRules. + */ +void NFRuleSet::setBestFractionRule(int32_t originalIndex, NFRule *newRule, UBool rememberRule) { + if (rememberRule) { + fractionRules.add(newRule); + } + NFRule *bestResult = nonNumericalRules[originalIndex]; + if (bestResult == nullptr) { + nonNumericalRules[originalIndex] = newRule; + } + else { + // We have more than one. Which one is better? + const DecimalFormatSymbols *decimalFormatSymbols = owner->getDecimalFormatSymbols(); + if (decimalFormatSymbols->getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol).charAt(0) + == newRule->getDecimalPoint()) + { + nonNumericalRules[originalIndex] = newRule; + } + // else leave it alone + } +} + +NFRuleSet::~NFRuleSet() +{ + for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) { + if (i != IMPROPER_FRACTION_RULE_INDEX + && i != PROPER_FRACTION_RULE_INDEX + && i != DEFAULT_RULE_INDEX) + { + delete nonNumericalRules[i]; + } + // else it will be deleted via NFRuleList fractionRules + } +} + +static UBool +util_equalRules(const NFRule* rule1, const NFRule* rule2) +{ + if (rule1) { + if (rule2) { + return *rule1 == *rule2; + } + } else if (!rule2) { + return true; + } + return false; +} + +bool +NFRuleSet::operator==(const NFRuleSet& rhs) const +{ + if (rules.size() == rhs.rules.size() && + fIsFractionRuleSet == rhs.fIsFractionRuleSet && + name == rhs.name) { + + // ...then compare the non-numerical rule lists... + for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) { + if (!util_equalRules(nonNumericalRules[i], rhs.nonNumericalRules[i])) { + return false; + } + } + + // ...then compare the rule lists... + for (uint32_t i = 0; i < rules.size(); ++i) { + if (*rules[i] != *rhs.rules[i]) { + return false; + } + } + return true; + } + return false; +} + +void +NFRuleSet::setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& status) { + for (uint32_t i = 0; i < rules.size(); ++i) { + rules[i]->setDecimalFormatSymbols(newSymbols, status); + } + // Switch the fraction rules to mirror the DecimalFormatSymbols. + for (int32_t nonNumericalIdx = IMPROPER_FRACTION_RULE_INDEX; nonNumericalIdx <= DEFAULT_RULE_INDEX; nonNumericalIdx++) { + if (nonNumericalRules[nonNumericalIdx]) { + for (uint32_t fIdx = 0; fIdx < fractionRules.size(); fIdx++) { + NFRule *fractionRule = fractionRules[fIdx]; + if (nonNumericalRules[nonNumericalIdx]->getBaseValue() == fractionRule->getBaseValue()) { + setBestFractionRule(nonNumericalIdx, fractionRule, false); + } + } + } + } + + for (uint32_t nnrIdx = 0; nnrIdx < NON_NUMERICAL_RULE_LENGTH; nnrIdx++) { + NFRule *rule = nonNumericalRules[nnrIdx]; + if (rule) { + rule->setDecimalFormatSymbols(newSymbols, status); + } + } +} + +#define RECURSION_LIMIT 64 + +void +NFRuleSet::format(int64_t number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const +{ + if (recursionCount >= RECURSION_LIMIT) { + // stop recursion + status = U_INVALID_STATE_ERROR; + return; + } + const NFRule *rule = findNormalRule(number); + if (rule) { // else error, but can't report it + rule->doFormat(number, toAppendTo, pos, ++recursionCount, status); + } +} + +void +NFRuleSet::format(double number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const +{ + if (recursionCount >= RECURSION_LIMIT) { + // stop recursion + status = U_INVALID_STATE_ERROR; + return; + } + const NFRule *rule = findDoubleRule(number); + if (rule) { // else error, but can't report it + rule->doFormat(number, toAppendTo, pos, ++recursionCount, status); + } +} + +const NFRule* +NFRuleSet::findDoubleRule(double number) const +{ + // if this is a fraction rule set, use findFractionRuleSetRule() + if (isFractionRuleSet()) { + return findFractionRuleSetRule(number); + } + + if (uprv_isNaN(number)) { + const NFRule *rule = nonNumericalRules[NAN_RULE_INDEX]; + if (!rule) { + rule = owner->getDefaultNaNRule(); + } + return rule; + } + + // if the number is negative, return the negative number rule + // (if there isn't a negative-number rule, we pretend it's a + // positive number) + if (number < 0) { + if (nonNumericalRules[NEGATIVE_RULE_INDEX]) { + return nonNumericalRules[NEGATIVE_RULE_INDEX]; + } else { + number = -number; + } + } + + if (uprv_isInfinite(number)) { + const NFRule *rule = nonNumericalRules[INFINITY_RULE_INDEX]; + if (!rule) { + rule = owner->getDefaultInfinityRule(); + } + return rule; + } + + // if the number isn't an integer, we use one of the fraction rules... + if (number != uprv_floor(number)) { + // if the number is between 0 and 1, return the proper + // fraction rule + if (number < 1 && nonNumericalRules[PROPER_FRACTION_RULE_INDEX]) { + return nonNumericalRules[PROPER_FRACTION_RULE_INDEX]; + } + // otherwise, return the improper fraction rule + else if (nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX]) { + return nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX]; + } + } + + // if there's a default rule, use it to format the number + if (nonNumericalRules[DEFAULT_RULE_INDEX]) { + return nonNumericalRules[DEFAULT_RULE_INDEX]; + } + + // and if we haven't yet returned a rule, use findNormalRule() + // to find the applicable rule + int64_t r = util64_fromDouble(number + 0.5); + return findNormalRule(r); +} + +const NFRule * +NFRuleSet::findNormalRule(int64_t number) const +{ + // if this is a fraction rule set, use findFractionRuleSetRule() + // to find the rule (we should only go into this clause if the + // value is 0) + if (fIsFractionRuleSet) { + return findFractionRuleSetRule((double)number); + } + + // if the number is negative, return the negative-number rule + // (if there isn't one, pretend the number is positive) + if (number < 0) { + if (nonNumericalRules[NEGATIVE_RULE_INDEX]) { + return nonNumericalRules[NEGATIVE_RULE_INDEX]; + } else { + number = -number; + } + } + + // we have to repeat the preceding two checks, even though we + // do them in findRule(), because the version of format() that + // takes a long bypasses findRule() and goes straight to this + // function. This function does skip the fraction rules since + // we know the value is an integer (it also skips the default + // rule, since it's considered a fraction rule. Skipping the + // default rule in this function is also how we avoid infinite + // recursion) + + // {dlf} unfortunately this fails if there are no rules except + // special rules. If there are no rules, use the default rule. + + // binary-search the rule list for the applicable rule + // (a rule is used for all values from its base value to + // the next rule's base value) + int32_t hi = rules.size(); + if (hi > 0) { + int32_t lo = 0; + + while (lo < hi) { + int32_t mid = (lo + hi) / 2; + if (rules[mid]->getBaseValue() == number) { + return rules[mid]; + } + else if (rules[mid]->getBaseValue() > number) { + hi = mid; + } + else { + lo = mid + 1; + } + } + if (hi == 0) { // bad rule set, minimum base > 0 + return nullptr; // want to throw exception here + } + + NFRule *result = rules[hi - 1]; + + // use shouldRollBack() to see whether we need to invoke the + // rollback rule (see shouldRollBack()'s documentation for + // an explanation of the rollback rule). If we do, roll back + // one rule and return that one instead of the one we'd normally + // return + if (result->shouldRollBack(number)) { + if (hi == 1) { // bad rule set, no prior rule to rollback to from this base + return nullptr; + } + result = rules[hi - 2]; + } + return result; + } + // else use the default rule + return nonNumericalRules[DEFAULT_RULE_INDEX]; +} + +/** + * If this rule is a fraction rule set, this function is used by + * findRule() to select the most appropriate rule for formatting + * the number. Basically, the base value of each rule in the rule + * set is treated as the denominator of a fraction. Whichever + * denominator can produce the fraction closest in value to the + * number passed in is the result. If there's a tie, the earlier + * one in the list wins. (If there are two rules in a row with the + * same base value, the first one is used when the numerator of the + * fraction would be 1, and the second rule is used the rest of the + * time. + * @param number The number being formatted (which will always be + * a number between 0 and 1) + * @return The rule to use to format this number + */ +const NFRule* +NFRuleSet::findFractionRuleSetRule(double number) const +{ + // the obvious way to do this (multiply the value being formatted + // by each rule's base value until you get an integral result) + // doesn't work because of rounding error. This method is more + // accurate + + // find the least common multiple of the rules' base values + // and multiply this by the number being formatted. This is + // all the precision we need, and we can do all of the rest + // of the math using integer arithmetic + int64_t leastCommonMultiple = rules[0]->getBaseValue(); + int64_t numerator; + { + for (uint32_t i = 1; i < rules.size(); ++i) { + leastCommonMultiple = util_lcm(leastCommonMultiple, rules[i]->getBaseValue()); + } + numerator = util64_fromDouble(number * (double)leastCommonMultiple + 0.5); + } + // for each rule, do the following... + int64_t tempDifference; + int64_t difference = util64_fromDouble(uprv_maxMantissa()); + int32_t winner = 0; + for (uint32_t i = 0; i < rules.size(); ++i) { + // "numerator" is the numerator of the fraction if the + // denominator is the LCD. The numerator if the rule's + // base value is the denominator is "numerator" times the + // base value divided bythe LCD. Here we check to see if + // that's an integer, and if not, how close it is to being + // an integer. + tempDifference = numerator * rules[i]->getBaseValue() % leastCommonMultiple; + + + // normalize the result of the above calculation: we want + // the numerator's distance from the CLOSEST multiple + // of the LCD + if (leastCommonMultiple - tempDifference < tempDifference) { + tempDifference = leastCommonMultiple - tempDifference; + } + + // if this is as close as we've come, keep track of how close + // that is, and the line number of the rule that did it. If + // we've scored a direct hit, we don't have to look at any more + // rules + if (tempDifference < difference) { + difference = tempDifference; + winner = i; + if (difference == 0) { + break; + } + } + } + + // if we have two successive rules that both have the winning base + // value, then the first one (the one we found above) is used if + // the numerator of the fraction is 1 and the second one is used if + // the numerator of the fraction is anything else (this lets us + // do things like "one third"/"two thirds" without having to define + // a whole bunch of extra rule sets) + if ((unsigned)(winner + 1) < rules.size() && + rules[winner + 1]->getBaseValue() == rules[winner]->getBaseValue()) { + double n = ((double)rules[winner]->getBaseValue()) * number; + if (n < 0.5 || n >= 2) { + ++winner; + } + } + + // finally, return the winning rule + return rules[winner]; +} + +/** + * Parses a string. Matches the string to be parsed against each + * of its rules (with a base value less than upperBound) and returns + * the value produced by the rule that matched the most characters + * in the source string. + * @param text The string to parse + * @param parsePosition The initial position is ignored and assumed + * to be 0. On exit, this object has been updated to point to the + * first character position this rule set didn't consume. + * @param upperBound Limits the rules that can be allowed to match. + * Only rules whose base values are strictly less than upperBound + * are considered. + * @return The numerical result of parsing this string. This will + * be the matching rule's base value, composed appropriately with + * the results of matching any of its substitutions. The object + * will be an instance of Long if it's an integral value; otherwise, + * it will be an instance of Double. This function always returns + * a valid object: If nothing matched the input string at all, + * this function returns new Long(0), and the parse position is + * left unchanged. + */ +#ifdef RBNF_DEBUG +#include + +static void dumpUS(FILE* f, const UnicodeString& us) { + int len = us.length(); + char* buf = (char *)uprv_malloc((len+1)*sizeof(char)); //new char[len+1]; + if (buf != nullptr) { + us.extract(0, len, buf); + buf[len] = 0; + fprintf(f, "%s", buf); + uprv_free(buf); //delete[] buf; + } +} +#endif + +UBool +NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBound, uint32_t nonNumericalExecutedRuleMask, Formattable& result) const +{ + // try matching each rule in the rule set against the text being + // parsed. Whichever one matches the most characters is the one + // that determines the value we return. + + result.setLong(0); + + // dump out if there's no text to parse + if (text.length() == 0) { + return 0; + } + + ParsePosition highWaterMark; + ParsePosition workingPos = pos; + +#ifdef RBNF_DEBUG + fprintf(stderr, " %x '", this); + dumpUS(stderr, name); + fprintf(stderr, "' text '"); + dumpUS(stderr, text); + fprintf(stderr, "'\n"); + fprintf(stderr, " parse negative: %d\n", this, negativeNumberRule != 0); +#endif + // Try each of the negative rules, fraction rules, infinity rules and NaN rules + for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) { + if (nonNumericalRules[i] && ((nonNumericalExecutedRuleMask >> i) & 1) == 0) { + // Mark this rule as being executed so that we don't try to execute it again. + nonNumericalExecutedRuleMask |= 1 << i; + + Formattable tempResult; + UBool success = nonNumericalRules[i]->doParse(text, workingPos, 0, upperBound, nonNumericalExecutedRuleMask, tempResult); + if (success && (workingPos.getIndex() > highWaterMark.getIndex())) { + result = tempResult; + highWaterMark = workingPos; + } + workingPos = pos; + } + } +#ifdef RBNF_DEBUG + fprintf(stderr, " continue other with text '"); + dumpUS(stderr, text); + fprintf(stderr, "' hwm: %d\n", highWaterMark.getIndex()); +#endif + + // finally, go through the regular rules one at a time. We start + // at the end of the list because we want to try matching the most + // sigificant rule first (this helps ensure that we parse + // "five thousand three hundred six" as + // "(five thousand) (three hundred) (six)" rather than + // "((five thousand three) hundred) (six)"). Skip rules whose + // base values are higher than the upper bound (again, this helps + // limit ambiguity by making sure the rules that match a rule's + // are less significant than the rule containing the substitutions)/ + { + int64_t ub = util64_fromDouble(upperBound); +#ifdef RBNF_DEBUG + { + char ubstr[64]; + util64_toa(ub, ubstr, 64); + char ubstrhex[64]; + util64_toa(ub, ubstrhex, 64, 16); + fprintf(stderr, "ub: %g, i64: %s (%s)\n", upperBound, ubstr, ubstrhex); + } +#endif + for (int32_t i = rules.size(); --i >= 0 && highWaterMark.getIndex() < text.length();) { + if ((!fIsFractionRuleSet) && (rules[i]->getBaseValue() >= ub)) { + continue; + } + Formattable tempResult; + UBool success = rules[i]->doParse(text, workingPos, fIsFractionRuleSet, upperBound, nonNumericalExecutedRuleMask, tempResult); + if (success && workingPos.getIndex() > highWaterMark.getIndex()) { + result = tempResult; + highWaterMark = workingPos; + } + workingPos = pos; + } + } +#ifdef RBNF_DEBUG + fprintf(stderr, " exit\n"); +#endif + // finally, update the parse position we were passed to point to the + // first character we didn't use, and return the result that + // corresponds to that string of characters + pos = highWaterMark; + + return 1; +} + +void +NFRuleSet::appendRules(UnicodeString& result) const +{ + uint32_t i; + + // the rule set name goes first... + result.append(name); + result.append(gColon); + result.append(gLineFeed); + + // followed by the regular rules... + for (i = 0; i < rules.size(); i++) { + rules[i]->_appendRuleText(result); + result.append(gLineFeed); + } + + // followed by the special rules (if they exist) + for (i = 0; i < NON_NUMERICAL_RULE_LENGTH; ++i) { + NFRule *rule = nonNumericalRules[i]; + if (nonNumericalRules[i]) { + if (rule->getBaseValue() == NFRule::kImproperFractionRule + || rule->getBaseValue() == NFRule::kProperFractionRule + || rule->getBaseValue() == NFRule::kDefaultRule) + { + for (uint32_t fIdx = 0; fIdx < fractionRules.size(); fIdx++) { + NFRule *fractionRule = fractionRules[fIdx]; + if (fractionRule->getBaseValue() == rule->getBaseValue()) { + fractionRule->_appendRuleText(result); + result.append(gLineFeed); + } + } + } + else { + rule->_appendRuleText(result); + result.append(gLineFeed); + } + } + } +} + +// utility functions + +int64_t util64_fromDouble(double d) { + int64_t result = 0; + if (!uprv_isNaN(d)) { + double mant = uprv_maxMantissa(); + if (d < -mant) { + d = -mant; + } else if (d > mant) { + d = mant; + } + UBool neg = d < 0; + if (neg) { + d = -d; + } + result = (int64_t)uprv_floor(d); + if (neg) { + result = -result; + } + } + return result; +} + +uint64_t util64_pow(uint32_t base, uint16_t exponent) { + if (base == 0) { + return 0; + } + uint64_t result = 1; + uint64_t pow = base; + while (true) { + if ((exponent & 1) == 1) { + result *= pow; + } + exponent >>= 1; + if (exponent == 0) { + break; + } + pow *= pow; + } + return result; +} + +static const uint8_t asciiDigits[] = { + 0x30u, 0x31u, 0x32u, 0x33u, 0x34u, 0x35u, 0x36u, 0x37u, + 0x38u, 0x39u, 0x61u, 0x62u, 0x63u, 0x64u, 0x65u, 0x66u, + 0x67u, 0x68u, 0x69u, 0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, + 0x6fu, 0x70u, 0x71u, 0x72u, 0x73u, 0x74u, 0x75u, 0x76u, + 0x77u, 0x78u, 0x79u, 0x7au, +}; + +static const char16_t kUMinus = (char16_t)0x002d; + +#ifdef RBNF_DEBUG +static const char kMinus = '-'; + +static const uint8_t digitInfo[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0x80u, 0x81u, 0x82u, 0x83u, 0x84u, 0x85u, 0x86u, 0x87u, + 0x88u, 0x89u, 0, 0, 0, 0, 0, 0, + 0, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, 0x90u, + 0x91u, 0x92u, 0x93u, 0x94u, 0x95u, 0x96u, 0x97u, 0x98u, + 0x99u, 0x9au, 0x9bu, 0x9cu, 0x9du, 0x9eu, 0x9fu, 0xa0u, + 0xa1u, 0xa2u, 0xa3u, 0, 0, 0, 0, 0, + 0, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, 0x90u, + 0x91u, 0x92u, 0x93u, 0x94u, 0x95u, 0x96u, 0x97u, 0x98u, + 0x99u, 0x9au, 0x9bu, 0x9cu, 0x9du, 0x9eu, 0x9fu, 0xa0u, + 0xa1u, 0xa2u, 0xa3u, 0, 0, 0, 0, 0, +}; + +int64_t util64_atoi(const char* str, uint32_t radix) +{ + if (radix > 36) { + radix = 36; + } else if (radix < 2) { + radix = 2; + } + int64_t lradix = radix; + + int neg = 0; + if (*str == kMinus) { + ++str; + neg = 1; + } + int64_t result = 0; + uint8_t b; + while ((b = digitInfo[*str++]) && ((b &= 0x7f) < radix)) { + result *= lradix; + result += (int32_t)b; + } + if (neg) { + result = -result; + } + return result; +} + +int64_t util64_utoi(const char16_t* str, uint32_t radix) +{ + if (radix > 36) { + radix = 36; + } else if (radix < 2) { + radix = 2; + } + int64_t lradix = radix; + + int neg = 0; + if (*str == kUMinus) { + ++str; + neg = 1; + } + int64_t result = 0; + char16_t c; + uint8_t b; + while (((c = *str++) < 0x0080) && (b = digitInfo[c]) && ((b &= 0x7f) < radix)) { + result *= lradix; + result += (int32_t)b; + } + if (neg) { + result = -result; + } + return result; +} + +uint32_t util64_toa(int64_t w, char* buf, uint32_t len, uint32_t radix, UBool raw) +{ + if (radix > 36) { + radix = 36; + } else if (radix < 2) { + radix = 2; + } + int64_t base = radix; + + char* p = buf; + if (len && (w < 0) && (radix == 10) && !raw) { + w = -w; + *p++ = kMinus; + --len; + } else if (len && (w == 0)) { + *p++ = (char)raw ? 0 : asciiDigits[0]; + --len; + } + + while (len && w != 0) { + int64_t n = w / base; + int64_t m = n * base; + int32_t d = (int32_t)(w-m); + *p++ = raw ? (char)d : asciiDigits[d]; + w = n; + --len; + } + if (len) { + *p = 0; // null terminate if room for caller convenience + } + + len = p - buf; + if (*buf == kMinus) { + ++buf; + } + while (--p > buf) { + char c = *p; + *p = *buf; + *buf = c; + ++buf; + } + + return len; +} +#endif + +uint32_t util64_tou(int64_t w, char16_t* buf, uint32_t len, uint32_t radix, UBool raw) +{ + if (radix > 36) { + radix = 36; + } else if (radix < 2) { + radix = 2; + } + int64_t base = radix; + + char16_t* p = buf; + if (len && (w < 0) && (radix == 10) && !raw) { + w = -w; + *p++ = kUMinus; + --len; + } else if (len && (w == 0)) { + *p++ = (char16_t)raw ? 0 : asciiDigits[0]; + --len; + } + + while (len && (w != 0)) { + int64_t n = w / base; + int64_t m = n * base; + int32_t d = (int32_t)(w-m); + *p++ = (char16_t)(raw ? d : asciiDigits[d]); + w = n; + --len; + } + if (len) { + *p = 0; // null terminate if room for caller convenience + } + + len = (uint32_t)(p - buf); + if (*buf == kUMinus) { + ++buf; + } + while (--p > buf) { + char16_t c = *p; + *p = *buf; + *buf = c; + ++buf; + } + + return len; +} + + +U_NAMESPACE_END + +/* U_HAVE_RBNF */ +#endif diff --git a/deps/icu-small/source/i18n/nfrs.h b/deps/icu-small/source/i18n/nfrs.h index a6ad3a3bb7f05d..ac35581197f421 100644 --- a/deps/icu-small/source/i18n/nfrs.h +++ b/deps/icu-small/source/i18n/nfrs.h @@ -1,111 +1,111 @@ -// © 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 name: nfrs.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* Modification history -* Date Name Comments -* 10/11/2001 Doug Ported from ICU4J -*/ - -#ifndef NFRS_H -#define NFRS_H - -#include "unicode/uobject.h" -#include "unicode/rbnf.h" - -#if U_HAVE_RBNF - -#include "unicode/utypes.h" -#include "unicode/umisc.h" - -#include "nfrlist.h" - -U_NAMESPACE_BEGIN - -class NFRuleSet : public UMemory { -public: - NFRuleSet(RuleBasedNumberFormat *owner, UnicodeString* descriptions, int32_t index, UErrorCode& status); - void parseRules(UnicodeString& rules, UErrorCode& status); - void setNonNumericalRule(NFRule *rule); - void setBestFractionRule(int32_t originalIndex, NFRule *newRule, UBool rememberRule); - void makeIntoFractionRuleSet() { fIsFractionRuleSet = true; } - - ~NFRuleSet(); - - bool operator==(const NFRuleSet& rhs) const; - bool operator!=(const NFRuleSet& rhs) const { return !operator==(rhs); } - - UBool isPublic() const { return fIsPublic; } - - UBool isParseable() const { return fIsParseable; } - - UBool isFractionRuleSet() const { return fIsFractionRuleSet; } - - void getName(UnicodeString& result) const { result.setTo(name); } - UBool isNamed(const UnicodeString& _name) const { return this->name == _name; } - - void format(int64_t number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const; - void format(double number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const; - - UBool parse(const UnicodeString& text, ParsePosition& pos, double upperBound, uint32_t nonNumericalExecutedRuleMask, Formattable& result) const; - - void appendRules(UnicodeString& result) const; // toString - - void setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& status); - - const RuleBasedNumberFormat *getOwner() const { return owner; } -private: - const NFRule * findNormalRule(int64_t number) const; - const NFRule * findDoubleRule(double number) const; - const NFRule * findFractionRuleSetRule(double number) const; - - friend class NFSubstitution; - -private: - UnicodeString name; - NFRuleList rules; - NFRule *nonNumericalRules[6]; - RuleBasedNumberFormat *owner; - NFRuleList fractionRules; - UBool fIsFractionRuleSet; - UBool fIsPublic; - UBool fIsParseable; - - NFRuleSet(const NFRuleSet &other); // forbid copying of this class - NFRuleSet &operator=(const NFRuleSet &other); // forbid copying of this class -}; - -// utilities from old llong.h -// convert mantissa portion of double to int64 -int64_t util64_fromDouble(double d); - -// raise radix to the power exponent, only non-negative exponents -// Arithmetic is performed in unsigned space since overflow in -// signed space is undefined behavior. -uint64_t util64_pow(uint32_t radix, uint16_t exponent); - -// convert n to digit string in buffer, return length of string -uint32_t util64_tou(int64_t n, UChar* buffer, uint32_t buflen, uint32_t radix = 10, UBool raw = false); - -#ifdef RBNF_DEBUG -int64_t util64_utoi(const UChar* str, uint32_t radix = 10); -uint32_t util64_toa(int64_t n, char* buffer, uint32_t buflen, uint32_t radix = 10, UBool raw = false); -int64_t util64_atoi(const char* str, uint32_t radix); -#endif - - -U_NAMESPACE_END - -/* U_HAVE_RBNF */ -#endif - -// NFRS_H -#endif +// © 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 name: nfrs.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* Modification history +* Date Name Comments +* 10/11/2001 Doug Ported from ICU4J +*/ + +#ifndef NFRS_H +#define NFRS_H + +#include "unicode/uobject.h" +#include "unicode/rbnf.h" + +#if U_HAVE_RBNF + +#include "unicode/utypes.h" +#include "unicode/umisc.h" + +#include "nfrlist.h" + +U_NAMESPACE_BEGIN + +class NFRuleSet : public UMemory { +public: + NFRuleSet(RuleBasedNumberFormat *owner, UnicodeString* descriptions, int32_t index, UErrorCode& status); + void parseRules(UnicodeString& rules, UErrorCode& status); + void setNonNumericalRule(NFRule *rule); + void setBestFractionRule(int32_t originalIndex, NFRule *newRule, UBool rememberRule); + void makeIntoFractionRuleSet() { fIsFractionRuleSet = true; } + + ~NFRuleSet(); + + bool operator==(const NFRuleSet& rhs) const; + bool operator!=(const NFRuleSet& rhs) const { return !operator==(rhs); } + + UBool isPublic() const { return fIsPublic; } + + UBool isParseable() const { return fIsParseable; } + + UBool isFractionRuleSet() const { return fIsFractionRuleSet; } + + void getName(UnicodeString& result) const { result.setTo(name); } + UBool isNamed(const UnicodeString& _name) const { return this->name == _name; } + + void format(int64_t number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const; + void format(double number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const; + + UBool parse(const UnicodeString& text, ParsePosition& pos, double upperBound, uint32_t nonNumericalExecutedRuleMask, Formattable& result) const; + + void appendRules(UnicodeString& result) const; // toString + + void setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& status); + + const RuleBasedNumberFormat *getOwner() const { return owner; } +private: + const NFRule * findNormalRule(int64_t number) const; + const NFRule * findDoubleRule(double number) const; + const NFRule * findFractionRuleSetRule(double number) const; + + friend class NFSubstitution; + +private: + UnicodeString name; + NFRuleList rules; + NFRule *nonNumericalRules[6]; + RuleBasedNumberFormat *owner; + NFRuleList fractionRules; + UBool fIsFractionRuleSet; + UBool fIsPublic; + UBool fIsParseable; + + NFRuleSet(const NFRuleSet &other); // forbid copying of this class + NFRuleSet &operator=(const NFRuleSet &other); // forbid copying of this class +}; + +// utilities from old llong.h +// convert mantissa portion of double to int64 +int64_t util64_fromDouble(double d); + +// raise radix to the power exponent, only non-negative exponents +// Arithmetic is performed in unsigned space since overflow in +// signed space is undefined behavior. +uint64_t util64_pow(uint32_t radix, uint16_t exponent); + +// convert n to digit string in buffer, return length of string +uint32_t util64_tou(int64_t n, char16_t* buffer, uint32_t buflen, uint32_t radix = 10, UBool raw = false); + +#ifdef RBNF_DEBUG +int64_t util64_utoi(const char16_t* str, uint32_t radix = 10); +uint32_t util64_toa(int64_t n, char* buffer, uint32_t buflen, uint32_t radix = 10, UBool raw = false); +int64_t util64_atoi(const char* str, uint32_t radix); +#endif + + +U_NAMESPACE_END + +/* U_HAVE_RBNF */ +#endif + +// NFRS_H +#endif diff --git a/deps/icu-small/source/i18n/nfrule.cpp b/deps/icu-small/source/i18n/nfrule.cpp index 2f8383c764c03d..ed06351c8e5ef6 100644 --- a/deps/icu-small/source/i18n/nfrule.cpp +++ b/deps/icu-small/source/i18n/nfrule.cpp @@ -1,1632 +1,1632 @@ -// © 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 name: nfrule.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* Modification history -* Date Name Comments -* 10/11/2001 Doug Ported from ICU4J -*/ - -#include "nfrule.h" - -#if U_HAVE_RBNF - -#include "unicode/localpointer.h" -#include "unicode/rbnf.h" -#include "unicode/tblcoll.h" -#include "unicode/plurfmt.h" -#include "unicode/upluralrules.h" -#include "unicode/coleitr.h" -#include "unicode/uchar.h" -#include "nfrs.h" -#include "nfrlist.h" -#include "nfsubs.h" -#include "patternprops.h" -#include "putilimp.h" - -U_NAMESPACE_BEGIN - -NFRule::NFRule(const RuleBasedNumberFormat* _rbnf, const UnicodeString &_ruleText, UErrorCode &status) - : baseValue((int32_t)0) - , radix(10) - , exponent(0) - , decimalPoint(0) - , fRuleText(_ruleText) - , sub1(NULL) - , sub2(NULL) - , formatter(_rbnf) - , rulePatternFormat(NULL) -{ - if (!fRuleText.isEmpty()) { - parseRuleDescriptor(fRuleText, status); - } -} - -NFRule::~NFRule() -{ - if (sub1 != sub2) { - delete sub2; - sub2 = NULL; - } - delete sub1; - sub1 = NULL; - delete rulePatternFormat; - rulePatternFormat = NULL; -} - -static const UChar gLeftBracket = 0x005b; -static const UChar gRightBracket = 0x005d; -static const UChar gColon = 0x003a; -static const UChar gZero = 0x0030; -static const UChar gNine = 0x0039; -static const UChar gSpace = 0x0020; -static const UChar gSlash = 0x002f; -static const UChar gGreaterThan = 0x003e; -static const UChar gLessThan = 0x003c; -static const UChar gComma = 0x002c; -static const UChar gDot = 0x002e; -static const UChar gTick = 0x0027; -//static const UChar gMinus = 0x002d; -static const UChar gSemicolon = 0x003b; -static const UChar gX = 0x0078; - -static const UChar gMinusX[] = {0x2D, 0x78, 0}; /* "-x" */ -static const UChar gInf[] = {0x49, 0x6E, 0x66, 0}; /* "Inf" */ -static const UChar gNaN[] = {0x4E, 0x61, 0x4E, 0}; /* "NaN" */ - -static const UChar gDollarOpenParenthesis[] = {0x24, 0x28, 0}; /* "$(" */ -static const UChar gClosedParenthesisDollar[] = {0x29, 0x24, 0}; /* ")$" */ - -static const UChar gLessLess[] = {0x3C, 0x3C, 0}; /* "<<" */ -static const UChar gLessPercent[] = {0x3C, 0x25, 0}; /* "<%" */ -static const UChar gLessHash[] = {0x3C, 0x23, 0}; /* "<#" */ -static const UChar gLessZero[] = {0x3C, 0x30, 0}; /* "<0" */ -static const UChar gGreaterGreater[] = {0x3E, 0x3E, 0}; /* ">>" */ -static const UChar gGreaterPercent[] = {0x3E, 0x25, 0}; /* ">%" */ -static const UChar gGreaterHash[] = {0x3E, 0x23, 0}; /* ">#" */ -static const UChar gGreaterZero[] = {0x3E, 0x30, 0}; /* ">0" */ -static const UChar gEqualPercent[] = {0x3D, 0x25, 0}; /* "=%" */ -static const UChar gEqualHash[] = {0x3D, 0x23, 0}; /* "=#" */ -static const UChar gEqualZero[] = {0x3D, 0x30, 0}; /* "=0" */ -static const UChar gGreaterGreaterGreater[] = {0x3E, 0x3E, 0x3E, 0}; /* ">>>" */ - -static const UChar * const RULE_PREFIXES[] = { - gLessLess, gLessPercent, gLessHash, gLessZero, - gGreaterGreater, gGreaterPercent,gGreaterHash, gGreaterZero, - gEqualPercent, gEqualHash, gEqualZero, NULL -}; - -void -NFRule::makeRules(UnicodeString& description, - NFRuleSet *owner, - const NFRule *predecessor, - const RuleBasedNumberFormat *rbnf, - NFRuleList& rules, - UErrorCode& status) -{ - // we know we're making at least one rule, so go ahead and - // new it up and initialize its basevalue and divisor - // (this also strips the rule descriptor, if any, off the - // description string) - NFRule* rule1 = new NFRule(rbnf, description, status); - /* test for NULL */ - if (rule1 == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - description = rule1->fRuleText; - - // check the description to see whether there's text enclosed - // in brackets - int32_t brack1 = description.indexOf(gLeftBracket); - int32_t brack2 = brack1 < 0 ? -1 : description.indexOf(gRightBracket); - - // if the description doesn't contain a matched pair of brackets, - // or if it's of a type that doesn't recognize bracketed text, - // then leave the description alone, initialize the rule's - // rule text and substitutions, and return that rule - if (brack2 < 0 || brack1 > brack2 - || rule1->getType() == kProperFractionRule - || rule1->getType() == kNegativeNumberRule - || rule1->getType() == kInfinityRule - || rule1->getType() == kNaNRule) - { - rule1->extractSubstitutions(owner, description, predecessor, status); - } - else { - // if the description does contain a matched pair of brackets, - // then it's really shorthand for two rules (with one exception) - NFRule* rule2 = NULL; - UnicodeString sbuf; - - // we'll actually only split the rule into two rules if its - // base value is an even multiple of its divisor (or it's one - // of the special rules) - if ((rule1->baseValue > 0 - && (rule1->baseValue % util64_pow(rule1->radix, rule1->exponent)) == 0) - || rule1->getType() == kImproperFractionRule - || rule1->getType() == kDefaultRule) { - - // if it passes that test, new up the second rule. If the - // rule set both rules will belong to is a fraction rule - // set, they both have the same base value; otherwise, - // increment the original rule's base value ("rule1" actually - // goes SECOND in the rule set's rule list) - rule2 = new NFRule(rbnf, UnicodeString(), status); - /* test for NULL */ - if (rule2 == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (rule1->baseValue >= 0) { - rule2->baseValue = rule1->baseValue; - if (!owner->isFractionRuleSet()) { - ++rule1->baseValue; - } - } - - // if the description began with "x.x" and contains bracketed - // text, it describes both the improper fraction rule and - // the proper fraction rule - else if (rule1->getType() == kImproperFractionRule) { - rule2->setType(kProperFractionRule); - } - - // if the description began with "x.0" and contains bracketed - // text, it describes both the default rule and the - // improper fraction rule - else if (rule1->getType() == kDefaultRule) { - rule2->baseValue = rule1->baseValue; - rule1->setType(kImproperFractionRule); - } - - // both rules have the same radix and exponent (i.e., the - // same divisor) - rule2->radix = rule1->radix; - rule2->exponent = rule1->exponent; - - // rule2's rule text omits the stuff in brackets: initialize - // its rule text and substitutions accordingly - sbuf.append(description, 0, brack1); - if (brack2 + 1 < description.length()) { - sbuf.append(description, brack2 + 1, description.length() - brack2 - 1); - } - rule2->extractSubstitutions(owner, sbuf, predecessor, status); - } - - // rule1's text includes the text in the brackets but omits - // the brackets themselves: initialize _its_ rule text and - // substitutions accordingly - sbuf.setTo(description, 0, brack1); - sbuf.append(description, brack1 + 1, brack2 - brack1 - 1); - if (brack2 + 1 < description.length()) { - sbuf.append(description, brack2 + 1, description.length() - brack2 - 1); - } - rule1->extractSubstitutions(owner, sbuf, predecessor, status); - - // if we only have one rule, return it; if we have two, return - // a two-element array containing them (notice that rule2 goes - // BEFORE rule1 in the list: in all cases, rule2 OMITS the - // material in the brackets and rule1 INCLUDES the material - // in the brackets) - if (rule2 != NULL) { - if (rule2->baseValue >= kNoBase) { - rules.add(rule2); - } - else { - owner->setNonNumericalRule(rule2); - } - } - } - if (rule1->baseValue >= kNoBase) { - rules.add(rule1); - } - else { - owner->setNonNumericalRule(rule1); - } -} - -/** - * This function parses the rule's rule descriptor (i.e., the base - * value and/or other tokens that precede the rule's rule text - * in the description) and sets the rule's base value, radix, and - * exponent according to the descriptor. (If the description doesn't - * include a rule descriptor, then this function sets everything to - * default values and the rule set sets the rule's real base value). - * @param description The rule's description - * @return If "description" included a rule descriptor, this is - * "description" with the descriptor and any trailing whitespace - * stripped off. Otherwise; it's "descriptor" unchangd. - */ -void -NFRule::parseRuleDescriptor(UnicodeString& description, UErrorCode& status) -{ - // the description consists of a rule descriptor and a rule body, - // separated by a colon. The rule descriptor is optional. If - // it's omitted, just set the base value to 0. - int32_t p = description.indexOf(gColon); - if (p != -1) { - // copy the descriptor out into its own string and strip it, - // along with any trailing whitespace, out of the original - // description - UnicodeString descriptor; - descriptor.setTo(description, 0, p); - - ++p; - while (p < description.length() && PatternProps::isWhiteSpace(description.charAt(p))) { - ++p; - } - description.removeBetween(0, p); - - // check first to see if the rule descriptor matches the token - // for one of the special rules. If it does, set the base - // value to the correct identifier value - int descriptorLength = descriptor.length(); - UChar firstChar = descriptor.charAt(0); - UChar lastChar = descriptor.charAt(descriptorLength - 1); - if (firstChar >= gZero && firstChar <= gNine && lastChar != gX) { - // if the rule descriptor begins with a digit, it's a descriptor - // for a normal rule - // since we don't have Long.parseLong, and this isn't much work anyway, - // just build up the value as we encounter the digits. - int64_t val = 0; - p = 0; - UChar c = gSpace; - - // begin parsing the descriptor: copy digits - // into "tempValue", skip periods, commas, and spaces, - // stop on a slash or > sign (or at the end of the string), - // and throw an exception on any other character - int64_t ll_10 = 10; - while (p < descriptorLength) { - c = descriptor.charAt(p); - if (c >= gZero && c <= gNine) { - val = val * ll_10 + (int32_t)(c - gZero); - } - else if (c == gSlash || c == gGreaterThan) { - break; - } - else if (PatternProps::isWhiteSpace(c) || c == gComma || c == gDot) { - } - else { - // throw new IllegalArgumentException("Illegal character in rule descriptor"); - status = U_PARSE_ERROR; - return; - } - ++p; - } - - // we have the base value, so set it - setBaseValue(val, status); - - // if we stopped the previous loop on a slash, we're - // now parsing the rule's radix. Again, accumulate digits - // in tempValue, skip punctuation, stop on a > mark, and - // throw an exception on anything else - if (c == gSlash) { - val = 0; - ++p; - ll_10 = 10; - while (p < descriptorLength) { - c = descriptor.charAt(p); - if (c >= gZero && c <= gNine) { - val = val * ll_10 + (int32_t)(c - gZero); - } - else if (c == gGreaterThan) { - break; - } - else if (PatternProps::isWhiteSpace(c) || c == gComma || c == gDot) { - } - else { - // throw new IllegalArgumentException("Illegal character is rule descriptor"); - status = U_PARSE_ERROR; - return; - } - ++p; - } - - // tempValue now contain's the rule's radix. Set it - // accordingly, and recalculate the rule's exponent - radix = (int32_t)val; - if (radix == 0) { - // throw new IllegalArgumentException("Rule can't have radix of 0"); - status = U_PARSE_ERROR; - } - - exponent = expectedExponent(); - } - - // if we stopped the previous loop on a > sign, then continue - // for as long as we still see > signs. For each one, - // decrement the exponent (unless the exponent is already 0). - // If we see another character before reaching the end of - // the descriptor, that's also a syntax error. - if (c == gGreaterThan) { - while (p < descriptor.length()) { - c = descriptor.charAt(p); - if (c == gGreaterThan && exponent > 0) { - --exponent; - } else { - // throw new IllegalArgumentException("Illegal character in rule descriptor"); - status = U_PARSE_ERROR; - return; - } - ++p; - } - } - } - else if (0 == descriptor.compare(gMinusX, 2)) { - setType(kNegativeNumberRule); - } - else if (descriptorLength == 3) { - if (firstChar == gZero && lastChar == gX) { - setBaseValue(kProperFractionRule, status); - decimalPoint = descriptor.charAt(1); - } - else if (firstChar == gX && lastChar == gX) { - setBaseValue(kImproperFractionRule, status); - decimalPoint = descriptor.charAt(1); - } - else if (firstChar == gX && lastChar == gZero) { - setBaseValue(kDefaultRule, status); - decimalPoint = descriptor.charAt(1); - } - else if (descriptor.compare(gNaN, 3) == 0) { - setBaseValue(kNaNRule, status); - } - else if (descriptor.compare(gInf, 3) == 0) { - setBaseValue(kInfinityRule, status); - } - } - } - // else use the default base value for now. - - // finally, if the rule body begins with an apostrophe, strip it off - // (this is generally used to put whitespace at the beginning of - // a rule's rule text) - if (description.length() > 0 && description.charAt(0) == gTick) { - description.removeBetween(0, 1); - } - - // return the description with all the stuff we've just waded through - // stripped off the front. It now contains just the rule body. - // return description; -} - -/** -* Searches the rule's rule text for the substitution tokens, -* creates the substitutions, and removes the substitution tokens -* from the rule's rule text. -* @param owner The rule set containing this rule -* @param predecessor The rule preseding this one in "owners" rule list -* @param ownersOwner The RuleBasedFormat that owns this rule -*/ -void -NFRule::extractSubstitutions(const NFRuleSet* ruleSet, - const UnicodeString &ruleText, - const NFRule* predecessor, - UErrorCode& status) -{ - if (U_FAILURE(status)) { - return; - } - fRuleText = ruleText; - sub1 = extractSubstitution(ruleSet, predecessor, status); - if (sub1 == NULL) { - // Small optimization. There is no need to create a redundant NullSubstitution. - sub2 = NULL; - } - else { - sub2 = extractSubstitution(ruleSet, predecessor, status); - } - int32_t pluralRuleStart = fRuleText.indexOf(gDollarOpenParenthesis, -1, 0); - int32_t pluralRuleEnd = (pluralRuleStart >= 0 ? fRuleText.indexOf(gClosedParenthesisDollar, -1, pluralRuleStart) : -1); - if (pluralRuleEnd >= 0) { - int32_t endType = fRuleText.indexOf(gComma, pluralRuleStart); - if (endType < 0) { - status = U_PARSE_ERROR; - return; - } - UnicodeString type(fRuleText.tempSubString(pluralRuleStart + 2, endType - pluralRuleStart - 2)); - UPluralType pluralType; - if (type.startsWith(UNICODE_STRING_SIMPLE("cardinal"))) { - pluralType = UPLURAL_TYPE_CARDINAL; - } - else if (type.startsWith(UNICODE_STRING_SIMPLE("ordinal"))) { - pluralType = UPLURAL_TYPE_ORDINAL; - } - else { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - rulePatternFormat = formatter->createPluralFormat(pluralType, - fRuleText.tempSubString(endType + 1, pluralRuleEnd - endType - 1), status); - } -} - -/** -* Searches the rule's rule text for the first substitution token, -* creates a substitution based on it, and removes the token from -* the rule's rule text. -* @param owner The rule set containing this rule -* @param predecessor The rule preceding this one in the rule set's -* rule list -* @param ownersOwner The RuleBasedNumberFormat that owns this rule -* @return The newly-created substitution. This is never null; if -* the rule text doesn't contain any substitution tokens, this will -* be a NullSubstitution. -*/ -NFSubstitution * -NFRule::extractSubstitution(const NFRuleSet* ruleSet, - const NFRule* predecessor, - UErrorCode& status) -{ - NFSubstitution* result = NULL; - - // search the rule's rule text for the first two characters of - // a substitution token - int32_t subStart = indexOfAnyRulePrefix(); - int32_t subEnd = subStart; - - // if we didn't find one, create a null substitution positioned - // at the end of the rule text - if (subStart == -1) { - return NULL; - } - - // special-case the ">>>" token, since searching for the > at the - // end will actually find the > in the middle - if (fRuleText.indexOf(gGreaterGreaterGreater, 3, 0) == subStart) { - subEnd = subStart + 2; - - // otherwise the substitution token ends with the same character - // it began with - } else { - UChar c = fRuleText.charAt(subStart); - subEnd = fRuleText.indexOf(c, subStart + 1); - // special case for '<%foo<<' - if (c == gLessThan && subEnd != -1 && subEnd < fRuleText.length() - 1 && fRuleText.charAt(subEnd+1) == c) { - // ordinals use "=#,##0==%abbrev=" as their rule. Notice that the '==' in the middle - // occurs because of the juxtaposition of two different rules. The check for '<' is a hack - // to get around this. Having the duplicate at the front would cause problems with - // rules like "<<%" to format, say, percents... - ++subEnd; - } - } - - // if we don't find the end of the token (i.e., if we're on a single, - // unmatched token character), create a null substitution positioned - // at the end of the rule - if (subEnd == -1) { - return NULL; - } - - // if we get here, we have a real substitution token (or at least - // some text bounded by substitution token characters). Use - // makeSubstitution() to create the right kind of substitution - UnicodeString subToken; - subToken.setTo(fRuleText, subStart, subEnd + 1 - subStart); - result = NFSubstitution::makeSubstitution(subStart, this, predecessor, ruleSet, - this->formatter, subToken, status); - - // remove the substitution from the rule text - fRuleText.removeBetween(subStart, subEnd+1); - - return result; -} - -/** - * Sets the rule's base value, and causes the radix and exponent - * to be recalculated. This is used during construction when we - * don't know the rule's base value until after it's been - * constructed. It should be used at any other time. - * @param The new base value for the rule. - */ -void -NFRule::setBaseValue(int64_t newBaseValue, UErrorCode& status) -{ - // set the base value - baseValue = newBaseValue; - radix = 10; - - // if this isn't a special rule, recalculate the radix and exponent - // (the radix always defaults to 10; if it's supposed to be something - // else, it's cleaned up by the caller and the exponent is - // recalculated again-- the only function that does this is - // NFRule.parseRuleDescriptor() ) - if (baseValue >= 1) { - exponent = expectedExponent(); - - // this function gets called on a fully-constructed rule whose - // description didn't specify a base value. This means it - // has substitutions, and some substitutions hold on to copies - // of the rule's divisor. Fix their copies of the divisor. - if (sub1 != NULL) { - sub1->setDivisor(radix, exponent, status); - } - if (sub2 != NULL) { - sub2->setDivisor(radix, exponent, status); - } - - // if this is a special rule, its radix and exponent are basically - // ignored. Set them to "safe" default values - } else { - exponent = 0; - } -} - -/** -* This calculates the rule's exponent based on its radix and base -* value. This will be the highest power the radix can be raised to -* and still produce a result less than or equal to the base value. -*/ -int16_t -NFRule::expectedExponent() const -{ - // since the log of 0, or the log base 0 of something, causes an - // error, declare the exponent in these cases to be 0 (we also - // deal with the special-rule identifiers here) - if (radix == 0 || baseValue < 1) { - return 0; - } - - // we get rounding error in some cases-- for example, log 1000 / log 10 - // gives us 1.9999999996 instead of 2. The extra logic here is to take - // that into account - int16_t tempResult = (int16_t)(uprv_log((double)baseValue) / uprv_log((double)radix)); - int64_t temp = util64_pow(radix, tempResult + 1); - if (temp <= baseValue) { - tempResult += 1; - } - return tempResult; -} - -/** - * Searches the rule's rule text for any of the specified strings. - * @return The index of the first match in the rule's rule text - * (i.e., the first substring in the rule's rule text that matches - * _any_ of the strings in "strings"). If none of the strings in - * "strings" is found in the rule's rule text, returns -1. - */ -int32_t -NFRule::indexOfAnyRulePrefix() const -{ - int result = -1; - for (int i = 0; RULE_PREFIXES[i]; i++) { - int32_t pos = fRuleText.indexOf(*RULE_PREFIXES[i]); - if (pos != -1 && (result == -1 || pos < result)) { - result = pos; - } - } - return result; -} - -//----------------------------------------------------------------------- -// boilerplate -//----------------------------------------------------------------------- - -static UBool -util_equalSubstitutions(const NFSubstitution* sub1, const NFSubstitution* sub2) -{ - if (sub1) { - if (sub2) { - return *sub1 == *sub2; - } - } else if (!sub2) { - return true; - } - return false; -} - -/** -* Tests two rules for equality. -* @param that The rule to compare this one against -* @return True is the two rules are functionally equivalent -*/ -bool -NFRule::operator==(const NFRule& rhs) const -{ - return baseValue == rhs.baseValue - && radix == rhs.radix - && exponent == rhs.exponent - && fRuleText == rhs.fRuleText - && util_equalSubstitutions(sub1, rhs.sub1) - && util_equalSubstitutions(sub2, rhs.sub2); -} - -/** -* Returns a textual representation of the rule. This won't -* necessarily be the same as the description that this rule -* was created with, but it will produce the same result. -* @return A textual description of the rule -*/ -static void util_append64(UnicodeString& result, int64_t n) -{ - UChar buffer[256]; - int32_t len = util64_tou(n, buffer, sizeof(buffer)); - UnicodeString temp(buffer, len); - result.append(temp); -} - -void -NFRule::_appendRuleText(UnicodeString& result) const -{ - switch (getType()) { - case kNegativeNumberRule: result.append(gMinusX, 2); break; - case kImproperFractionRule: result.append(gX).append(decimalPoint == 0 ? gDot : decimalPoint).append(gX); break; - case kProperFractionRule: result.append(gZero).append(decimalPoint == 0 ? gDot : decimalPoint).append(gX); break; - case kDefaultRule: result.append(gX).append(decimalPoint == 0 ? gDot : decimalPoint).append(gZero); break; - case kInfinityRule: result.append(gInf, 3); break; - case kNaNRule: result.append(gNaN, 3); break; - default: - // for a normal rule, write out its base value, and if the radix is - // something other than 10, write out the radix (with the preceding - // slash, of course). Then calculate the expected exponent and if - // if isn't the same as the actual exponent, write an appropriate - // number of > signs. Finally, terminate the whole thing with - // a colon. - util_append64(result, baseValue); - if (radix != 10) { - result.append(gSlash); - util_append64(result, radix); - } - int numCarets = expectedExponent() - exponent; - for (int i = 0; i < numCarets; i++) { - result.append(gGreaterThan); - } - break; - } - result.append(gColon); - result.append(gSpace); - - // if the rule text begins with a space, write an apostrophe - // (whitespace after the rule descriptor is ignored; the - // apostrophe is used to make the whitespace significant) - if (fRuleText.charAt(0) == gSpace && (sub1 == NULL || sub1->getPos() != 0)) { - result.append(gTick); - } - - // now, write the rule's rule text, inserting appropriate - // substitution tokens in the appropriate places - UnicodeString ruleTextCopy; - ruleTextCopy.setTo(fRuleText); - - UnicodeString temp; - if (sub2 != NULL) { - sub2->toString(temp); - ruleTextCopy.insert(sub2->getPos(), temp); - } - if (sub1 != NULL) { - sub1->toString(temp); - ruleTextCopy.insert(sub1->getPos(), temp); - } - - result.append(ruleTextCopy); - - // and finally, top the whole thing off with a semicolon and - // return the result - result.append(gSemicolon); -} - -int64_t NFRule::getDivisor() const -{ - return util64_pow(radix, exponent); -} - - -//----------------------------------------------------------------------- -// formatting -//----------------------------------------------------------------------- - -/** -* Formats the number, and inserts the resulting text into -* toInsertInto. -* @param number The number being formatted -* @param toInsertInto The string where the resultant text should -* be inserted -* @param pos The position in toInsertInto where the resultant text -* should be inserted -*/ -void -NFRule::doFormat(int64_t number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const -{ - // first, insert the rule's rule text into toInsertInto at the - // specified position, then insert the results of the substitutions - // into the right places in toInsertInto (notice we do the - // substitutions in reverse order so that the offsets don't get - // messed up) - int32_t pluralRuleStart = fRuleText.length(); - int32_t lengthOffset = 0; - if (!rulePatternFormat) { - toInsertInto.insert(pos, fRuleText); - } - else { - pluralRuleStart = fRuleText.indexOf(gDollarOpenParenthesis, -1, 0); - int pluralRuleEnd = fRuleText.indexOf(gClosedParenthesisDollar, -1, pluralRuleStart); - int initialLength = toInsertInto.length(); - if (pluralRuleEnd < fRuleText.length() - 1) { - toInsertInto.insert(pos, fRuleText.tempSubString(pluralRuleEnd + 2)); - } - toInsertInto.insert(pos, - rulePatternFormat->format((int32_t)(number/util64_pow(radix, exponent)), status)); - if (pluralRuleStart > 0) { - toInsertInto.insert(pos, fRuleText.tempSubString(0, pluralRuleStart)); - } - lengthOffset = fRuleText.length() - (toInsertInto.length() - initialLength); - } - - if (sub2 != NULL) { - sub2->doSubstitution(number, toInsertInto, pos - (sub2->getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount, status); - } - if (sub1 != NULL) { - sub1->doSubstitution(number, toInsertInto, pos - (sub1->getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount, status); - } -} - -/** -* Formats the number, and inserts the resulting text into -* toInsertInto. -* @param number The number being formatted -* @param toInsertInto The string where the resultant text should -* be inserted -* @param pos The position in toInsertInto where the resultant text -* should be inserted -*/ -void -NFRule::doFormat(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const -{ - // first, insert the rule's rule text into toInsertInto at the - // specified position, then insert the results of the substitutions - // into the right places in toInsertInto - // [again, we have two copies of this routine that do the same thing - // so that we don't sacrifice precision in a long by casting it - // to a double] - int32_t pluralRuleStart = fRuleText.length(); - int32_t lengthOffset = 0; - if (!rulePatternFormat) { - toInsertInto.insert(pos, fRuleText); - } - else { - pluralRuleStart = fRuleText.indexOf(gDollarOpenParenthesis, -1, 0); - int pluralRuleEnd = fRuleText.indexOf(gClosedParenthesisDollar, -1, pluralRuleStart); - int initialLength = toInsertInto.length(); - if (pluralRuleEnd < fRuleText.length() - 1) { - toInsertInto.insert(pos, fRuleText.tempSubString(pluralRuleEnd + 2)); - } - double pluralVal = number; - if (0 <= pluralVal && pluralVal < 1) { - // We're in a fractional rule, and we have to match the NumeratorSubstitution behavior. - // 2.3 can become 0.2999999999999998 for the fraction due to rounding errors. - pluralVal = uprv_round(pluralVal * util64_pow(radix, exponent)); - } - else { - pluralVal = pluralVal / util64_pow(radix, exponent); - } - toInsertInto.insert(pos, rulePatternFormat->format((int32_t)(pluralVal), status)); - if (pluralRuleStart > 0) { - toInsertInto.insert(pos, fRuleText.tempSubString(0, pluralRuleStart)); - } - lengthOffset = fRuleText.length() - (toInsertInto.length() - initialLength); - } - - if (sub2 != NULL) { - sub2->doSubstitution(number, toInsertInto, pos - (sub2->getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount, status); - } - if (sub1 != NULL) { - sub1->doSubstitution(number, toInsertInto, pos - (sub1->getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount, status); - } -} - -/** -* Used by the owning rule set to determine whether to invoke the -* rollback rule (i.e., whether this rule or the one that precedes -* it in the rule set's list should be used to format the number) -* @param The number being formatted -* @return True if the rule set should use the rule that precedes -* this one in its list; false if it should use this rule -*/ -UBool -NFRule::shouldRollBack(int64_t number) const -{ - // we roll back if the rule contains a modulus substitution, - // the number being formatted is an even multiple of the rule's - // divisor, and the rule's base value is NOT an even multiple - // of its divisor - // In other words, if the original description had - // 100: << hundred[ >>]; - // that expands into - // 100: << hundred; - // 101: << hundred >>; - // internally. But when we're formatting 200, if we use the rule - // at 101, which would normally apply, we get "two hundred zero". - // To prevent this, we roll back and use the rule at 100 instead. - // This is the logic that makes this happen: the rule at 101 has - // a modulus substitution, its base value isn't an even multiple - // of 100, and the value we're trying to format _is_ an even - // multiple of 100. This is called the "rollback rule." - if ((sub1 != NULL && sub1->isModulusSubstitution()) || (sub2 != NULL && sub2->isModulusSubstitution())) { - int64_t re = util64_pow(radix, exponent); - return (number % re) == 0 && (baseValue % re) != 0; - } - return false; -} - -//----------------------------------------------------------------------- -// parsing -//----------------------------------------------------------------------- - -/** -* Attempts to parse the string with this rule. -* @param text The string being parsed -* @param parsePosition On entry, the value is ignored and assumed to -* be 0. On exit, this has been updated with the position of the first -* character not consumed by matching the text against this rule -* (if this rule doesn't match the text at all, the parse position -* if left unchanged (presumably at 0) and the function returns -* new Long(0)). -* @param isFractionRule True if this rule is contained within a -* fraction rule set. This is only used if the rule has no -* substitutions. -* @return If this rule matched the text, this is the rule's base value -* combined appropriately with the results of parsing the substitutions. -* If nothing matched, this is new Long(0) and the parse position is -* left unchanged. The result will be an instance of Long if the -* result is an integer and Double otherwise. The result is never null. -*/ -#ifdef RBNF_DEBUG -#include - -static void dumpUS(FILE* f, const UnicodeString& us) { - int len = us.length(); - char* buf = (char *)uprv_malloc((len+1)*sizeof(char)); //new char[len+1]; - if (buf != NULL) { - us.extract(0, len, buf); - buf[len] = 0; - fprintf(f, "%s", buf); - uprv_free(buf); //delete[] buf; - } -} -#endif -UBool -NFRule::doParse(const UnicodeString& text, - ParsePosition& parsePosition, - UBool isFractionRule, - double upperBound, - uint32_t nonNumericalExecutedRuleMask, - Formattable& resVal) const -{ - // internally we operate on a copy of the string being parsed - // (because we're going to change it) and use our own ParsePosition - ParsePosition pp; - UnicodeString workText(text); - - int32_t sub1Pos = sub1 != NULL ? sub1->getPos() : fRuleText.length(); - int32_t sub2Pos = sub2 != NULL ? sub2->getPos() : fRuleText.length(); - - // check to see whether the text before the first substitution - // matches the text at the beginning of the string being - // parsed. If it does, strip that off the front of workText; - // otherwise, dump out with a mismatch - UnicodeString prefix; - prefix.setTo(fRuleText, 0, sub1Pos); - -#ifdef RBNF_DEBUG - fprintf(stderr, "doParse %p ", this); - { - UnicodeString rt; - _appendRuleText(rt); - dumpUS(stderr, rt); - } - - fprintf(stderr, " text: '"); - dumpUS(stderr, text); - fprintf(stderr, "' prefix: '"); - dumpUS(stderr, prefix); -#endif - stripPrefix(workText, prefix, pp); - int32_t prefixLength = text.length() - workText.length(); - -#ifdef RBNF_DEBUG - fprintf(stderr, "' pl: %d ppi: %d s1p: %d\n", prefixLength, pp.getIndex(), sub1Pos); -#endif - - if (pp.getIndex() == 0 && sub1Pos != 0) { - // commented out because ParsePosition doesn't have error index in 1.1.x - // restored for ICU4C port - parsePosition.setErrorIndex(pp.getErrorIndex()); - resVal.setLong(0); - return true; - } - if (baseValue == kInfinityRule) { - // If you match this, don't try to perform any calculations on it. - parsePosition.setIndex(pp.getIndex()); - resVal.setDouble(uprv_getInfinity()); - return true; - } - if (baseValue == kNaNRule) { - // If you match this, don't try to perform any calculations on it. - parsePosition.setIndex(pp.getIndex()); - resVal.setDouble(uprv_getNaN()); - return true; - } - - // this is the fun part. The basic guts of the rule-matching - // logic is matchToDelimiter(), which is called twice. The first - // time it searches the input string for the rule text BETWEEN - // the substitutions and tries to match the intervening text - // in the input string with the first substitution. If that - // succeeds, it then calls it again, this time to look for the - // rule text after the second substitution and to match the - // intervening input text against the second substitution. - // - // For example, say we have a rule that looks like this: - // first << middle >> last; - // and input text that looks like this: - // first one middle two last - // First we use stripPrefix() to match "first " in both places and - // strip it off the front, leaving - // one middle two last - // Then we use matchToDelimiter() to match " middle " and try to - // match "one" against a substitution. If it's successful, we now - // have - // two last - // We use matchToDelimiter() a second time to match " last" and - // try to match "two" against a substitution. If "two" matches - // the substitution, we have a successful parse. - // - // Since it's possible in many cases to find multiple instances - // of each of these pieces of rule text in the input string, - // we need to try all the possible combinations of these - // locations. This prevents us from prematurely declaring a mismatch, - // and makes sure we match as much input text as we can. - int highWaterMark = 0; - double result = 0; - int start = 0; - double tempBaseValue = (double)(baseValue <= 0 ? 0 : baseValue); - - UnicodeString temp; - do { - // our partial parse result starts out as this rule's base - // value. If it finds a successful match, matchToDelimiter() - // will compose this in some way with what it gets back from - // the substitution, giving us a new partial parse result - pp.setIndex(0); - - temp.setTo(fRuleText, sub1Pos, sub2Pos - sub1Pos); - double partialResult = matchToDelimiter(workText, start, tempBaseValue, - temp, pp, sub1, - nonNumericalExecutedRuleMask, - upperBound); - - // if we got a successful match (or were trying to match a - // null substitution), pp is now pointing at the first unmatched - // character. Take note of that, and try matchToDelimiter() - // on the input text again - if (pp.getIndex() != 0 || sub1 == NULL) { - start = pp.getIndex(); - - UnicodeString workText2; - workText2.setTo(workText, pp.getIndex(), workText.length() - pp.getIndex()); - ParsePosition pp2; - - // the second matchToDelimiter() will compose our previous - // partial result with whatever it gets back from its - // substitution if there's a successful match, giving us - // a real result - temp.setTo(fRuleText, sub2Pos, fRuleText.length() - sub2Pos); - partialResult = matchToDelimiter(workText2, 0, partialResult, - temp, pp2, sub2, - nonNumericalExecutedRuleMask, - upperBound); - - // if we got a successful match on this second - // matchToDelimiter() call, update the high-water mark - // and result (if necessary) - if (pp2.getIndex() != 0 || sub2 == NULL) { - if (prefixLength + pp.getIndex() + pp2.getIndex() > highWaterMark) { - highWaterMark = prefixLength + pp.getIndex() + pp2.getIndex(); - result = partialResult; - } - } - else { - // commented out because ParsePosition doesn't have error index in 1.1.x - // restored for ICU4C port - int32_t i_temp = pp2.getErrorIndex() + sub1Pos + pp.getIndex(); - if (i_temp> parsePosition.getErrorIndex()) { - parsePosition.setErrorIndex(i_temp); - } - } - } - else { - // commented out because ParsePosition doesn't have error index in 1.1.x - // restored for ICU4C port - int32_t i_temp = sub1Pos + pp.getErrorIndex(); - if (i_temp > parsePosition.getErrorIndex()) { - parsePosition.setErrorIndex(i_temp); - } - } - // keep trying to match things until the outer matchToDelimiter() - // call fails to make a match (each time, it picks up where it - // left off the previous time) - } while (sub1Pos != sub2Pos - && pp.getIndex() > 0 - && pp.getIndex() < workText.length() - && pp.getIndex() != start); - - // update the caller's ParsePosition with our high-water mark - // (i.e., it now points at the first character this function - // didn't match-- the ParsePosition is therefore unchanged if - // we didn't match anything) - parsePosition.setIndex(highWaterMark); - // commented out because ParsePosition doesn't have error index in 1.1.x - // restored for ICU4C port - if (highWaterMark > 0) { - parsePosition.setErrorIndex(0); - } - - // this is a hack for one unusual condition: Normally, whether this - // rule belong to a fraction rule set or not is handled by its - // substitutions. But if that rule HAS NO substitutions, then - // we have to account for it here. By definition, if the matching - // rule in a fraction rule set has no substitutions, its numerator - // is 1, and so the result is the reciprocal of its base value. - if (isFractionRule && highWaterMark > 0 && sub1 == NULL) { - result = 1 / result; - } - - resVal.setDouble(result); - return true; // ??? do we need to worry if it is a long or a double? -} - -/** -* This function is used by parse() to match the text being parsed -* against a possible prefix string. This function -* matches characters from the beginning of the string being parsed -* to characters from the prospective prefix. If they match, pp is -* updated to the first character not matched, and the result is -* the unparsed part of the string. If they don't match, the whole -* string is returned, and pp is left unchanged. -* @param text The string being parsed -* @param prefix The text to match against -* @param pp On entry, ignored and assumed to be 0. On exit, points -* to the first unmatched character (assuming the whole prefix matched), -* or is unchanged (if the whole prefix didn't match). -* @return If things match, this is the unparsed part of "text"; -* if they didn't match, this is "text". -*/ -void -NFRule::stripPrefix(UnicodeString& text, const UnicodeString& prefix, ParsePosition& pp) const -{ - // if the prefix text is empty, dump out without doing anything - if (prefix.length() != 0) { - UErrorCode status = U_ZERO_ERROR; - // use prefixLength() to match the beginning of - // "text" against "prefix". This function returns the - // number of characters from "text" that matched (or 0 if - // we didn't match the whole prefix) - int32_t pfl = prefixLength(text, prefix, status); - if (U_FAILURE(status)) { // Memory allocation error. - return; - } - if (pfl != 0) { - // if we got a successful match, update the parse position - // and strip the prefix off of "text" - pp.setIndex(pp.getIndex() + pfl); - text.remove(0, pfl); - } - } -} - -/** -* Used by parse() to match a substitution and any following text. -* "text" is searched for instances of "delimiter". For each instance -* of delimiter, the intervening text is tested to see whether it -* matches the substitution. The longest match wins. -* @param text The string being parsed -* @param startPos The position in "text" where we should start looking -* for "delimiter". -* @param baseValue A partial parse result (often the rule's base value), -* which is combined with the result from matching the substitution -* @param delimiter The string to search "text" for. -* @param pp Ignored and presumed to be 0 on entry. If there's a match, -* on exit this will point to the first unmatched character. -* @param sub If we find "delimiter" in "text", this substitution is used -* to match the text between the beginning of the string and the -* position of "delimiter." (If "delimiter" is the empty string, then -* this function just matches against this substitution and updates -* everything accordingly.) -* @param upperBound When matching the substitution, it will only -* consider rules with base values lower than this value. -* @return If there's a match, this is the result of composing -* baseValue with the result of matching the substitution. Otherwise, -* this is new Long(0). It's never null. If the result is an integer, -* this will be an instance of Long; otherwise, it's an instance of -* Double. -* -* !!! note {dlf} in point of fact, in the java code the caller always converts -* the result to a double, so we might as well return one. -*/ -double -NFRule::matchToDelimiter(const UnicodeString& text, - int32_t startPos, - double _baseValue, - const UnicodeString& delimiter, - ParsePosition& pp, - const NFSubstitution* sub, - uint32_t nonNumericalExecutedRuleMask, - double upperBound) const -{ - UErrorCode status = U_ZERO_ERROR; - // if "delimiter" contains real (i.e., non-ignorable) text, search - // it for "delimiter" beginning at "start". If that succeeds, then - // use "sub"'s doParse() method to match the text before the - // instance of "delimiter" we just found. - if (!allIgnorable(delimiter, status)) { - if (U_FAILURE(status)) { //Memory allocation error. - return 0; - } - ParsePosition tempPP; - Formattable result; - - // use findText() to search for "delimiter". It returns a two- - // element array: element 0 is the position of the match, and - // element 1 is the number of characters that matched - // "delimiter". - int32_t dLen; - int32_t dPos = findText(text, delimiter, startPos, &dLen); - - // if findText() succeeded, isolate the text preceding the - // match, and use "sub" to match that text - while (dPos >= 0) { - UnicodeString subText; - subText.setTo(text, 0, dPos); - if (subText.length() > 0) { - UBool success = sub->doParse(subText, tempPP, _baseValue, upperBound, -#if UCONFIG_NO_COLLATION - false, -#else - formatter->isLenient(), -#endif - nonNumericalExecutedRuleMask, - result); - - // if the substitution could match all the text up to - // where we found "delimiter", then this function has - // a successful match. Bump the caller's parse position - // to point to the first character after the text - // that matches "delimiter", and return the result - // we got from parsing the substitution. - if (success && tempPP.getIndex() == dPos) { - pp.setIndex(dPos + dLen); - return result.getDouble(); - } - else { - // commented out because ParsePosition doesn't have error index in 1.1.x - // restored for ICU4C port - if (tempPP.getErrorIndex() > 0) { - pp.setErrorIndex(tempPP.getErrorIndex()); - } else { - pp.setErrorIndex(tempPP.getIndex()); - } - } - } - - // if we didn't match the substitution, search for another - // copy of "delimiter" in "text" and repeat the loop if - // we find it - tempPP.setIndex(0); - dPos = findText(text, delimiter, dPos + dLen, &dLen); - } - // if we make it here, this was an unsuccessful match, and we - // leave pp unchanged and return 0 - pp.setIndex(0); - return 0; - - // if "delimiter" is empty, or consists only of ignorable characters - // (i.e., is semantically empty), thwe we obviously can't search - // for "delimiter". Instead, just use "sub" to parse as much of - // "text" as possible. - } - else if (sub == NULL) { - return _baseValue; - } - else { - ParsePosition tempPP; - Formattable result; - - // try to match the whole string against the substitution - UBool success = sub->doParse(text, tempPP, _baseValue, upperBound, -#if UCONFIG_NO_COLLATION - false, -#else - formatter->isLenient(), -#endif - nonNumericalExecutedRuleMask, - result); - if (success && (tempPP.getIndex() != 0)) { - // if there's a successful match (or it's a null - // substitution), update pp to point to the first - // character we didn't match, and pass the result from - // sub.doParse() on through to the caller - pp.setIndex(tempPP.getIndex()); - return result.getDouble(); - } - else { - // commented out because ParsePosition doesn't have error index in 1.1.x - // restored for ICU4C port - pp.setErrorIndex(tempPP.getErrorIndex()); - } - - // and if we get to here, then nothing matched, so we return - // 0 and leave pp alone - return 0; - } -} - -/** -* Used by stripPrefix() to match characters. If lenient parse mode -* is off, this just calls startsWith(). If lenient parse mode is on, -* this function uses CollationElementIterators to match characters in -* the strings (only primary-order differences are significant in -* determining whether there's a match). -* @param str The string being tested -* @param prefix The text we're hoping to see at the beginning -* of "str" -* @return If "prefix" is found at the beginning of "str", this -* is the number of characters in "str" that were matched (this -* isn't necessarily the same as the length of "prefix" when matching -* text with a collator). If there's no match, this is 0. -*/ -int32_t -NFRule::prefixLength(const UnicodeString& str, const UnicodeString& prefix, UErrorCode& status) const -{ - // if we're looking for an empty prefix, it obviously matches - // zero characters. Just go ahead and return 0. - if (prefix.length() == 0) { - return 0; - } - -#if !UCONFIG_NO_COLLATION - // go through all this grief if we're in lenient-parse mode - if (formatter->isLenient()) { - // Check if non-lenient rule finds the text before call lenient parsing - if (str.startsWith(prefix)) { - return prefix.length(); - } - // get the formatter's collator and use it to create two - // collation element iterators, one over the target string - // and another over the prefix (right now, we'll throw an - // exception if the collator we get back from the formatter - // isn't a RuleBasedCollator, because RuleBasedCollator defines - // the CollationElementIterator protocol. Hopefully, this - // will change someday.) - const RuleBasedCollator* collator = formatter->getCollator(); - if (collator == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - LocalPointer strIter(collator->createCollationElementIterator(str)); - LocalPointer prefixIter(collator->createCollationElementIterator(prefix)); - // Check for memory allocation error. - if (strIter.isNull() || prefixIter.isNull()) { - status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - - UErrorCode err = U_ZERO_ERROR; - - // The original code was problematic. Consider this match: - // prefix = "fifty-" - // string = " fifty-7" - // The intent is to match string up to the '7', by matching 'fifty-' at position 1 - // in the string. Unfortunately, we were getting a match, and then computing where - // the match terminated by rematching the string. The rematch code was using as an - // initial guess the substring of string between 0 and prefix.length. Because of - // the leading space and trailing hyphen (both ignorable) this was succeeding, leaving - // the position before the hyphen in the string. Recursing down, we then parsed the - // remaining string '-7' as numeric. The resulting number turned out as 43 (50 - 7). - // This was not pretty, especially since the string "fifty-7" parsed just fine. - // - // We have newer APIs now, so we can use calls on the iterator to determine what we - // matched up to. If we terminate because we hit the last element in the string, - // our match terminates at this length. If we terminate because we hit the last element - // in the target, our match terminates at one before the element iterator position. - - // match collation elements between the strings - int32_t oStr = strIter->next(err); - int32_t oPrefix = prefixIter->next(err); - - while (oPrefix != CollationElementIterator::NULLORDER) { - // skip over ignorable characters in the target string - while (CollationElementIterator::primaryOrder(oStr) == 0 - && oStr != CollationElementIterator::NULLORDER) { - oStr = strIter->next(err); - } - - // skip over ignorable characters in the prefix - while (CollationElementIterator::primaryOrder(oPrefix) == 0 - && oPrefix != CollationElementIterator::NULLORDER) { - oPrefix = prefixIter->next(err); - } - - // dlf: move this above following test, if we consume the - // entire target, aren't we ok even if the source was also - // entirely consumed? - - // if skipping over ignorables brought to the end of - // the prefix, we DID match: drop out of the loop - if (oPrefix == CollationElementIterator::NULLORDER) { - break; - } - - // if skipping over ignorables brought us to the end - // of the target string, we didn't match and return 0 - if (oStr == CollationElementIterator::NULLORDER) { - return 0; - } - - // match collation elements from the two strings - // (considering only primary differences). If we - // get a mismatch, dump out and return 0 - if (CollationElementIterator::primaryOrder(oStr) - != CollationElementIterator::primaryOrder(oPrefix)) { - return 0; - - // otherwise, advance to the next character in each string - // and loop (we drop out of the loop when we exhaust - // collation elements in the prefix) - } else { - oStr = strIter->next(err); - oPrefix = prefixIter->next(err); - } - } - - int32_t result = strIter->getOffset(); - if (oStr != CollationElementIterator::NULLORDER) { - --result; // back over character that we don't want to consume; - } - -#ifdef RBNF_DEBUG - fprintf(stderr, "prefix length: %d\n", result); -#endif - return result; -#if 0 - //---------------------------------------------------------------- - // JDK 1.2-specific API call - // return strIter.getOffset(); - //---------------------------------------------------------------- - // JDK 1.1 HACK (take out for 1.2-specific code) - - // if we make it to here, we have a successful match. Now we - // have to find out HOW MANY characters from the target string - // matched the prefix (there isn't necessarily a one-to-one - // mapping between collation elements and characters). - // In JDK 1.2, there's a simple getOffset() call we can use. - // In JDK 1.1, on the other hand, we have to go through some - // ugly contortions. First, use the collator to compare the - // same number of characters from the prefix and target string. - // If they're equal, we're done. - collator->setStrength(Collator::PRIMARY); - if (str.length() >= prefix.length()) { - UnicodeString temp; - temp.setTo(str, 0, prefix.length()); - if (collator->equals(temp, prefix)) { -#ifdef RBNF_DEBUG - fprintf(stderr, "returning: %d\n", prefix.length()); -#endif - return prefix.length(); - } - } - - // if they're not equal, then we have to compare successively - // larger and larger substrings of the target string until we - // get to one that matches the prefix. At that point, we know - // how many characters matched the prefix, and we can return. - int32_t p = 1; - while (p <= str.length()) { - UnicodeString temp; - temp.setTo(str, 0, p); - if (collator->equals(temp, prefix)) { - return p; - } else { - ++p; - } - } - - // SHOULD NEVER GET HERE!!! - return 0; - //---------------------------------------------------------------- -#endif - - // If lenient parsing is turned off, forget all that crap above. - // Just use String.startsWith() and be done with it. - } else -#endif - { - if (str.startsWith(prefix)) { - return prefix.length(); - } else { - return 0; - } - } -} - -/** -* Searches a string for another string. If lenient parsing is off, -* this just calls indexOf(). If lenient parsing is on, this function -* uses CollationElementIterator to match characters, and only -* primary-order differences are significant in determining whether -* there's a match. -* @param str The string to search -* @param key The string to search "str" for -* @param startingAt The index into "str" where the search is to -* begin -* @return A two-element array of ints. Element 0 is the position -* of the match, or -1 if there was no match. Element 1 is the -* number of characters in "str" that matched (which isn't necessarily -* the same as the length of "key") -*/ -int32_t -NFRule::findText(const UnicodeString& str, - const UnicodeString& key, - int32_t startingAt, - int32_t* length) const -{ - if (rulePatternFormat) { - Formattable result; - FieldPosition position(UNUM_INTEGER_FIELD); - position.setBeginIndex(startingAt); - rulePatternFormat->parseType(str, this, result, position); - int start = position.getBeginIndex(); - if (start >= 0) { - int32_t pluralRuleStart = fRuleText.indexOf(gDollarOpenParenthesis, -1, 0); - int32_t pluralRuleSuffix = fRuleText.indexOf(gClosedParenthesisDollar, -1, pluralRuleStart) + 2; - int32_t matchLen = position.getEndIndex() - start; - UnicodeString prefix(fRuleText.tempSubString(0, pluralRuleStart)); - UnicodeString suffix(fRuleText.tempSubString(pluralRuleSuffix)); - if (str.compare(start - prefix.length(), prefix.length(), prefix, 0, prefix.length()) == 0 - && str.compare(start + matchLen, suffix.length(), suffix, 0, suffix.length()) == 0) - { - *length = matchLen + prefix.length() + suffix.length(); - return start - prefix.length(); - } - } - *length = 0; - return -1; - } - if (!formatter->isLenient()) { - // if lenient parsing is turned off, this is easy: just call - // String.indexOf() and we're done - *length = key.length(); - return str.indexOf(key, startingAt); - } - else { - // Check if non-lenient rule finds the text before call lenient parsing - *length = key.length(); - int32_t pos = str.indexOf(key, startingAt); - if(pos >= 0) { - return pos; - } else { - // but if lenient parsing is turned ON, we've got some work ahead of us - return findTextLenient(str, key, startingAt, length); - } - } -} - -int32_t -NFRule::findTextLenient(const UnicodeString& str, - const UnicodeString& key, - int32_t startingAt, - int32_t* length) const -{ - //---------------------------------------------------------------- - // JDK 1.1 HACK (take out of 1.2-specific code) - - // in JDK 1.2, CollationElementIterator provides us with an - // API to map between character offsets and collation elements - // and we can do this by marching through the string comparing - // collation elements. We can't do that in JDK 1.1. Instead, - // we have to go through this horrible slow mess: - int32_t p = startingAt; - int32_t keyLen = 0; - - // basically just isolate smaller and smaller substrings of - // the target string (each running to the end of the string, - // and with the first one running from startingAt to the end) - // and then use prefixLength() to see if the search key is at - // the beginning of each substring. This is excruciatingly - // slow, but it will locate the key and tell use how long the - // matching text was. - UnicodeString temp; - UErrorCode status = U_ZERO_ERROR; - while (p < str.length() && keyLen == 0) { - temp.setTo(str, p, str.length() - p); - keyLen = prefixLength(temp, key, status); - if (U_FAILURE(status)) { - break; - } - if (keyLen != 0) { - *length = keyLen; - return p; - } - ++p; - } - // if we make it to here, we didn't find it. Return -1 for the - // location. The length should be ignored, but set it to 0, - // which should be "safe" - *length = 0; - return -1; -} - -/** -* Checks to see whether a string consists entirely of ignorable -* characters. -* @param str The string to test. -* @return true if the string is empty of consists entirely of -* characters that the number formatter's collator says are -* ignorable at the primary-order level. false otherwise. -*/ -UBool -NFRule::allIgnorable(const UnicodeString& str, UErrorCode& status) const -{ - // if the string is empty, we can just return true - if (str.length() == 0) { - return true; - } - -#if !UCONFIG_NO_COLLATION - // if lenient parsing is turned on, walk through the string with - // a collation element iterator and make sure each collation - // element is 0 (ignorable) at the primary level - if (formatter->isLenient()) { - const RuleBasedCollator* collator = formatter->getCollator(); - if (collator == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return false; - } - LocalPointer iter(collator->createCollationElementIterator(str)); - - // Memory allocation error check. - if (iter.isNull()) { - status = U_MEMORY_ALLOCATION_ERROR; - return false; - } - - UErrorCode err = U_ZERO_ERROR; - int32_t o = iter->next(err); - while (o != CollationElementIterator::NULLORDER - && CollationElementIterator::primaryOrder(o) == 0) { - o = iter->next(err); - } - - return o == CollationElementIterator::NULLORDER; - } -#endif - - // if lenient parsing is turned off, there is no such thing as - // an ignorable character: return true only if the string is empty - return false; -} - -void -NFRule::setDecimalFormatSymbols(const DecimalFormatSymbols& newSymbols, UErrorCode& status) { - if (sub1 != NULL) { - sub1->setDecimalFormatSymbols(newSymbols, status); - } - if (sub2 != NULL) { - sub2->setDecimalFormatSymbols(newSymbols, status); - } -} - -U_NAMESPACE_END - -/* U_HAVE_RBNF */ -#endif +// © 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 name: nfrule.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* Modification history +* Date Name Comments +* 10/11/2001 Doug Ported from ICU4J +*/ + +#include "nfrule.h" + +#if U_HAVE_RBNF + +#include "unicode/localpointer.h" +#include "unicode/rbnf.h" +#include "unicode/tblcoll.h" +#include "unicode/plurfmt.h" +#include "unicode/upluralrules.h" +#include "unicode/coleitr.h" +#include "unicode/uchar.h" +#include "nfrs.h" +#include "nfrlist.h" +#include "nfsubs.h" +#include "patternprops.h" +#include "putilimp.h" + +U_NAMESPACE_BEGIN + +NFRule::NFRule(const RuleBasedNumberFormat* _rbnf, const UnicodeString &_ruleText, UErrorCode &status) + : baseValue((int32_t)0) + , radix(10) + , exponent(0) + , decimalPoint(0) + , fRuleText(_ruleText) + , sub1(nullptr) + , sub2(nullptr) + , formatter(_rbnf) + , rulePatternFormat(nullptr) +{ + if (!fRuleText.isEmpty()) { + parseRuleDescriptor(fRuleText, status); + } +} + +NFRule::~NFRule() +{ + if (sub1 != sub2) { + delete sub2; + sub2 = nullptr; + } + delete sub1; + sub1 = nullptr; + delete rulePatternFormat; + rulePatternFormat = nullptr; +} + +static const char16_t gLeftBracket = 0x005b; +static const char16_t gRightBracket = 0x005d; +static const char16_t gColon = 0x003a; +static const char16_t gZero = 0x0030; +static const char16_t gNine = 0x0039; +static const char16_t gSpace = 0x0020; +static const char16_t gSlash = 0x002f; +static const char16_t gGreaterThan = 0x003e; +static const char16_t gLessThan = 0x003c; +static const char16_t gComma = 0x002c; +static const char16_t gDot = 0x002e; +static const char16_t gTick = 0x0027; +//static const char16_t gMinus = 0x002d; +static const char16_t gSemicolon = 0x003b; +static const char16_t gX = 0x0078; + +static const char16_t gMinusX[] = {0x2D, 0x78, 0}; /* "-x" */ +static const char16_t gInf[] = {0x49, 0x6E, 0x66, 0}; /* "Inf" */ +static const char16_t gNaN[] = {0x4E, 0x61, 0x4E, 0}; /* "NaN" */ + +static const char16_t gDollarOpenParenthesis[] = {0x24, 0x28, 0}; /* "$(" */ +static const char16_t gClosedParenthesisDollar[] = {0x29, 0x24, 0}; /* ")$" */ + +static const char16_t gLessLess[] = {0x3C, 0x3C, 0}; /* "<<" */ +static const char16_t gLessPercent[] = {0x3C, 0x25, 0}; /* "<%" */ +static const char16_t gLessHash[] = {0x3C, 0x23, 0}; /* "<#" */ +static const char16_t gLessZero[] = {0x3C, 0x30, 0}; /* "<0" */ +static const char16_t gGreaterGreater[] = {0x3E, 0x3E, 0}; /* ">>" */ +static const char16_t gGreaterPercent[] = {0x3E, 0x25, 0}; /* ">%" */ +static const char16_t gGreaterHash[] = {0x3E, 0x23, 0}; /* ">#" */ +static const char16_t gGreaterZero[] = {0x3E, 0x30, 0}; /* ">0" */ +static const char16_t gEqualPercent[] = {0x3D, 0x25, 0}; /* "=%" */ +static const char16_t gEqualHash[] = {0x3D, 0x23, 0}; /* "=#" */ +static const char16_t gEqualZero[] = {0x3D, 0x30, 0}; /* "=0" */ +static const char16_t gGreaterGreaterGreater[] = {0x3E, 0x3E, 0x3E, 0}; /* ">>>" */ + +static const char16_t * const RULE_PREFIXES[] = { + gLessLess, gLessPercent, gLessHash, gLessZero, + gGreaterGreater, gGreaterPercent,gGreaterHash, gGreaterZero, + gEqualPercent, gEqualHash, gEqualZero, nullptr +}; + +void +NFRule::makeRules(UnicodeString& description, + NFRuleSet *owner, + const NFRule *predecessor, + const RuleBasedNumberFormat *rbnf, + NFRuleList& rules, + UErrorCode& status) +{ + // we know we're making at least one rule, so go ahead and + // new it up and initialize its basevalue and divisor + // (this also strips the rule descriptor, if any, off the + // description string) + NFRule* rule1 = new NFRule(rbnf, description, status); + /* test for nullptr */ + if (rule1 == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + description = rule1->fRuleText; + + // check the description to see whether there's text enclosed + // in brackets + int32_t brack1 = description.indexOf(gLeftBracket); + int32_t brack2 = brack1 < 0 ? -1 : description.indexOf(gRightBracket); + + // if the description doesn't contain a matched pair of brackets, + // or if it's of a type that doesn't recognize bracketed text, + // then leave the description alone, initialize the rule's + // rule text and substitutions, and return that rule + if (brack2 < 0 || brack1 > brack2 + || rule1->getType() == kProperFractionRule + || rule1->getType() == kNegativeNumberRule + || rule1->getType() == kInfinityRule + || rule1->getType() == kNaNRule) + { + rule1->extractSubstitutions(owner, description, predecessor, status); + } + else { + // if the description does contain a matched pair of brackets, + // then it's really shorthand for two rules (with one exception) + NFRule* rule2 = nullptr; + UnicodeString sbuf; + + // we'll actually only split the rule into two rules if its + // base value is an even multiple of its divisor (or it's one + // of the special rules) + if ((rule1->baseValue > 0 + && (rule1->baseValue % util64_pow(rule1->radix, rule1->exponent)) == 0) + || rule1->getType() == kImproperFractionRule + || rule1->getType() == kDefaultRule) { + + // if it passes that test, new up the second rule. If the + // rule set both rules will belong to is a fraction rule + // set, they both have the same base value; otherwise, + // increment the original rule's base value ("rule1" actually + // goes SECOND in the rule set's rule list) + rule2 = new NFRule(rbnf, UnicodeString(), status); + /* test for nullptr */ + if (rule2 == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (rule1->baseValue >= 0) { + rule2->baseValue = rule1->baseValue; + if (!owner->isFractionRuleSet()) { + ++rule1->baseValue; + } + } + + // if the description began with "x.x" and contains bracketed + // text, it describes both the improper fraction rule and + // the proper fraction rule + else if (rule1->getType() == kImproperFractionRule) { + rule2->setType(kProperFractionRule); + } + + // if the description began with "x.0" and contains bracketed + // text, it describes both the default rule and the + // improper fraction rule + else if (rule1->getType() == kDefaultRule) { + rule2->baseValue = rule1->baseValue; + rule1->setType(kImproperFractionRule); + } + + // both rules have the same radix and exponent (i.e., the + // same divisor) + rule2->radix = rule1->radix; + rule2->exponent = rule1->exponent; + + // rule2's rule text omits the stuff in brackets: initialize + // its rule text and substitutions accordingly + sbuf.append(description, 0, brack1); + if (brack2 + 1 < description.length()) { + sbuf.append(description, brack2 + 1, description.length() - brack2 - 1); + } + rule2->extractSubstitutions(owner, sbuf, predecessor, status); + } + + // rule1's text includes the text in the brackets but omits + // the brackets themselves: initialize _its_ rule text and + // substitutions accordingly + sbuf.setTo(description, 0, brack1); + sbuf.append(description, brack1 + 1, brack2 - brack1 - 1); + if (brack2 + 1 < description.length()) { + sbuf.append(description, brack2 + 1, description.length() - brack2 - 1); + } + rule1->extractSubstitutions(owner, sbuf, predecessor, status); + + // if we only have one rule, return it; if we have two, return + // a two-element array containing them (notice that rule2 goes + // BEFORE rule1 in the list: in all cases, rule2 OMITS the + // material in the brackets and rule1 INCLUDES the material + // in the brackets) + if (rule2 != nullptr) { + if (rule2->baseValue >= kNoBase) { + rules.add(rule2); + } + else { + owner->setNonNumericalRule(rule2); + } + } + } + if (rule1->baseValue >= kNoBase) { + rules.add(rule1); + } + else { + owner->setNonNumericalRule(rule1); + } +} + +/** + * This function parses the rule's rule descriptor (i.e., the base + * value and/or other tokens that precede the rule's rule text + * in the description) and sets the rule's base value, radix, and + * exponent according to the descriptor. (If the description doesn't + * include a rule descriptor, then this function sets everything to + * default values and the rule set sets the rule's real base value). + * @param description The rule's description + * @return If "description" included a rule descriptor, this is + * "description" with the descriptor and any trailing whitespace + * stripped off. Otherwise; it's "descriptor" unchangd. + */ +void +NFRule::parseRuleDescriptor(UnicodeString& description, UErrorCode& status) +{ + // the description consists of a rule descriptor and a rule body, + // separated by a colon. The rule descriptor is optional. If + // it's omitted, just set the base value to 0. + int32_t p = description.indexOf(gColon); + if (p != -1) { + // copy the descriptor out into its own string and strip it, + // along with any trailing whitespace, out of the original + // description + UnicodeString descriptor; + descriptor.setTo(description, 0, p); + + ++p; + while (p < description.length() && PatternProps::isWhiteSpace(description.charAt(p))) { + ++p; + } + description.removeBetween(0, p); + + // check first to see if the rule descriptor matches the token + // for one of the special rules. If it does, set the base + // value to the correct identifier value + int descriptorLength = descriptor.length(); + char16_t firstChar = descriptor.charAt(0); + char16_t lastChar = descriptor.charAt(descriptorLength - 1); + if (firstChar >= gZero && firstChar <= gNine && lastChar != gX) { + // if the rule descriptor begins with a digit, it's a descriptor + // for a normal rule + // since we don't have Long.parseLong, and this isn't much work anyway, + // just build up the value as we encounter the digits. + int64_t val = 0; + p = 0; + char16_t c = gSpace; + + // begin parsing the descriptor: copy digits + // into "tempValue", skip periods, commas, and spaces, + // stop on a slash or > sign (or at the end of the string), + // and throw an exception on any other character + int64_t ll_10 = 10; + while (p < descriptorLength) { + c = descriptor.charAt(p); + if (c >= gZero && c <= gNine) { + val = val * ll_10 + (int32_t)(c - gZero); + } + else if (c == gSlash || c == gGreaterThan) { + break; + } + else if (PatternProps::isWhiteSpace(c) || c == gComma || c == gDot) { + } + else { + // throw new IllegalArgumentException("Illegal character in rule descriptor"); + status = U_PARSE_ERROR; + return; + } + ++p; + } + + // we have the base value, so set it + setBaseValue(val, status); + + // if we stopped the previous loop on a slash, we're + // now parsing the rule's radix. Again, accumulate digits + // in tempValue, skip punctuation, stop on a > mark, and + // throw an exception on anything else + if (c == gSlash) { + val = 0; + ++p; + ll_10 = 10; + while (p < descriptorLength) { + c = descriptor.charAt(p); + if (c >= gZero && c <= gNine) { + val = val * ll_10 + (int32_t)(c - gZero); + } + else if (c == gGreaterThan) { + break; + } + else if (PatternProps::isWhiteSpace(c) || c == gComma || c == gDot) { + } + else { + // throw new IllegalArgumentException("Illegal character is rule descriptor"); + status = U_PARSE_ERROR; + return; + } + ++p; + } + + // tempValue now contain's the rule's radix. Set it + // accordingly, and recalculate the rule's exponent + radix = (int32_t)val; + if (radix == 0) { + // throw new IllegalArgumentException("Rule can't have radix of 0"); + status = U_PARSE_ERROR; + } + + exponent = expectedExponent(); + } + + // if we stopped the previous loop on a > sign, then continue + // for as long as we still see > signs. For each one, + // decrement the exponent (unless the exponent is already 0). + // If we see another character before reaching the end of + // the descriptor, that's also a syntax error. + if (c == gGreaterThan) { + while (p < descriptor.length()) { + c = descriptor.charAt(p); + if (c == gGreaterThan && exponent > 0) { + --exponent; + } else { + // throw new IllegalArgumentException("Illegal character in rule descriptor"); + status = U_PARSE_ERROR; + return; + } + ++p; + } + } + } + else if (0 == descriptor.compare(gMinusX, 2)) { + setType(kNegativeNumberRule); + } + else if (descriptorLength == 3) { + if (firstChar == gZero && lastChar == gX) { + setBaseValue(kProperFractionRule, status); + decimalPoint = descriptor.charAt(1); + } + else if (firstChar == gX && lastChar == gX) { + setBaseValue(kImproperFractionRule, status); + decimalPoint = descriptor.charAt(1); + } + else if (firstChar == gX && lastChar == gZero) { + setBaseValue(kDefaultRule, status); + decimalPoint = descriptor.charAt(1); + } + else if (descriptor.compare(gNaN, 3) == 0) { + setBaseValue(kNaNRule, status); + } + else if (descriptor.compare(gInf, 3) == 0) { + setBaseValue(kInfinityRule, status); + } + } + } + // else use the default base value for now. + + // finally, if the rule body begins with an apostrophe, strip it off + // (this is generally used to put whitespace at the beginning of + // a rule's rule text) + if (description.length() > 0 && description.charAt(0) == gTick) { + description.removeBetween(0, 1); + } + + // return the description with all the stuff we've just waded through + // stripped off the front. It now contains just the rule body. + // return description; +} + +/** +* Searches the rule's rule text for the substitution tokens, +* creates the substitutions, and removes the substitution tokens +* from the rule's rule text. +* @param owner The rule set containing this rule +* @param predecessor The rule preseding this one in "owners" rule list +* @param ownersOwner The RuleBasedFormat that owns this rule +*/ +void +NFRule::extractSubstitutions(const NFRuleSet* ruleSet, + const UnicodeString &ruleText, + const NFRule* predecessor, + UErrorCode& status) +{ + if (U_FAILURE(status)) { + return; + } + fRuleText = ruleText; + sub1 = extractSubstitution(ruleSet, predecessor, status); + if (sub1 == nullptr) { + // Small optimization. There is no need to create a redundant NullSubstitution. + sub2 = nullptr; + } + else { + sub2 = extractSubstitution(ruleSet, predecessor, status); + } + int32_t pluralRuleStart = fRuleText.indexOf(gDollarOpenParenthesis, -1, 0); + int32_t pluralRuleEnd = (pluralRuleStart >= 0 ? fRuleText.indexOf(gClosedParenthesisDollar, -1, pluralRuleStart) : -1); + if (pluralRuleEnd >= 0) { + int32_t endType = fRuleText.indexOf(gComma, pluralRuleStart); + if (endType < 0) { + status = U_PARSE_ERROR; + return; + } + UnicodeString type(fRuleText.tempSubString(pluralRuleStart + 2, endType - pluralRuleStart - 2)); + UPluralType pluralType; + if (type.startsWith(UNICODE_STRING_SIMPLE("cardinal"))) { + pluralType = UPLURAL_TYPE_CARDINAL; + } + else if (type.startsWith(UNICODE_STRING_SIMPLE("ordinal"))) { + pluralType = UPLURAL_TYPE_ORDINAL; + } + else { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + rulePatternFormat = formatter->createPluralFormat(pluralType, + fRuleText.tempSubString(endType + 1, pluralRuleEnd - endType - 1), status); + } +} + +/** +* Searches the rule's rule text for the first substitution token, +* creates a substitution based on it, and removes the token from +* the rule's rule text. +* @param owner The rule set containing this rule +* @param predecessor The rule preceding this one in the rule set's +* rule list +* @param ownersOwner The RuleBasedNumberFormat that owns this rule +* @return The newly-created substitution. This is never null; if +* the rule text doesn't contain any substitution tokens, this will +* be a NullSubstitution. +*/ +NFSubstitution * +NFRule::extractSubstitution(const NFRuleSet* ruleSet, + const NFRule* predecessor, + UErrorCode& status) +{ + NFSubstitution* result = nullptr; + + // search the rule's rule text for the first two characters of + // a substitution token + int32_t subStart = indexOfAnyRulePrefix(); + int32_t subEnd = subStart; + + // if we didn't find one, create a null substitution positioned + // at the end of the rule text + if (subStart == -1) { + return nullptr; + } + + // special-case the ">>>" token, since searching for the > at the + // end will actually find the > in the middle + if (fRuleText.indexOf(gGreaterGreaterGreater, 3, 0) == subStart) { + subEnd = subStart + 2; + + // otherwise the substitution token ends with the same character + // it began with + } else { + char16_t c = fRuleText.charAt(subStart); + subEnd = fRuleText.indexOf(c, subStart + 1); + // special case for '<%foo<<' + if (c == gLessThan && subEnd != -1 && subEnd < fRuleText.length() - 1 && fRuleText.charAt(subEnd+1) == c) { + // ordinals use "=#,##0==%abbrev=" as their rule. Notice that the '==' in the middle + // occurs because of the juxtaposition of two different rules. The check for '<' is a hack + // to get around this. Having the duplicate at the front would cause problems with + // rules like "<<%" to format, say, percents... + ++subEnd; + } + } + + // if we don't find the end of the token (i.e., if we're on a single, + // unmatched token character), create a null substitution positioned + // at the end of the rule + if (subEnd == -1) { + return nullptr; + } + + // if we get here, we have a real substitution token (or at least + // some text bounded by substitution token characters). Use + // makeSubstitution() to create the right kind of substitution + UnicodeString subToken; + subToken.setTo(fRuleText, subStart, subEnd + 1 - subStart); + result = NFSubstitution::makeSubstitution(subStart, this, predecessor, ruleSet, + this->formatter, subToken, status); + + // remove the substitution from the rule text + fRuleText.removeBetween(subStart, subEnd+1); + + return result; +} + +/** + * Sets the rule's base value, and causes the radix and exponent + * to be recalculated. This is used during construction when we + * don't know the rule's base value until after it's been + * constructed. It should be used at any other time. + * @param The new base value for the rule. + */ +void +NFRule::setBaseValue(int64_t newBaseValue, UErrorCode& status) +{ + // set the base value + baseValue = newBaseValue; + radix = 10; + + // if this isn't a special rule, recalculate the radix and exponent + // (the radix always defaults to 10; if it's supposed to be something + // else, it's cleaned up by the caller and the exponent is + // recalculated again-- the only function that does this is + // NFRule.parseRuleDescriptor() ) + if (baseValue >= 1) { + exponent = expectedExponent(); + + // this function gets called on a fully-constructed rule whose + // description didn't specify a base value. This means it + // has substitutions, and some substitutions hold on to copies + // of the rule's divisor. Fix their copies of the divisor. + if (sub1 != nullptr) { + sub1->setDivisor(radix, exponent, status); + } + if (sub2 != nullptr) { + sub2->setDivisor(radix, exponent, status); + } + + // if this is a special rule, its radix and exponent are basically + // ignored. Set them to "safe" default values + } else { + exponent = 0; + } +} + +/** +* This calculates the rule's exponent based on its radix and base +* value. This will be the highest power the radix can be raised to +* and still produce a result less than or equal to the base value. +*/ +int16_t +NFRule::expectedExponent() const +{ + // since the log of 0, or the log base 0 of something, causes an + // error, declare the exponent in these cases to be 0 (we also + // deal with the special-rule identifiers here) + if (radix == 0 || baseValue < 1) { + return 0; + } + + // we get rounding error in some cases-- for example, log 1000 / log 10 + // gives us 1.9999999996 instead of 2. The extra logic here is to take + // that into account + int16_t tempResult = (int16_t)(uprv_log((double)baseValue) / uprv_log((double)radix)); + int64_t temp = util64_pow(radix, tempResult + 1); + if (temp <= baseValue) { + tempResult += 1; + } + return tempResult; +} + +/** + * Searches the rule's rule text for any of the specified strings. + * @return The index of the first match in the rule's rule text + * (i.e., the first substring in the rule's rule text that matches + * _any_ of the strings in "strings"). If none of the strings in + * "strings" is found in the rule's rule text, returns -1. + */ +int32_t +NFRule::indexOfAnyRulePrefix() const +{ + int result = -1; + for (int i = 0; RULE_PREFIXES[i]; i++) { + int32_t pos = fRuleText.indexOf(*RULE_PREFIXES[i]); + if (pos != -1 && (result == -1 || pos < result)) { + result = pos; + } + } + return result; +} + +//----------------------------------------------------------------------- +// boilerplate +//----------------------------------------------------------------------- + +static UBool +util_equalSubstitutions(const NFSubstitution* sub1, const NFSubstitution* sub2) +{ + if (sub1) { + if (sub2) { + return *sub1 == *sub2; + } + } else if (!sub2) { + return true; + } + return false; +} + +/** +* Tests two rules for equality. +* @param that The rule to compare this one against +* @return True is the two rules are functionally equivalent +*/ +bool +NFRule::operator==(const NFRule& rhs) const +{ + return baseValue == rhs.baseValue + && radix == rhs.radix + && exponent == rhs.exponent + && fRuleText == rhs.fRuleText + && util_equalSubstitutions(sub1, rhs.sub1) + && util_equalSubstitutions(sub2, rhs.sub2); +} + +/** +* Returns a textual representation of the rule. This won't +* necessarily be the same as the description that this rule +* was created with, but it will produce the same result. +* @return A textual description of the rule +*/ +static void util_append64(UnicodeString& result, int64_t n) +{ + char16_t buffer[256]; + int32_t len = util64_tou(n, buffer, sizeof(buffer)); + UnicodeString temp(buffer, len); + result.append(temp); +} + +void +NFRule::_appendRuleText(UnicodeString& result) const +{ + switch (getType()) { + case kNegativeNumberRule: result.append(gMinusX, 2); break; + case kImproperFractionRule: result.append(gX).append(decimalPoint == 0 ? gDot : decimalPoint).append(gX); break; + case kProperFractionRule: result.append(gZero).append(decimalPoint == 0 ? gDot : decimalPoint).append(gX); break; + case kDefaultRule: result.append(gX).append(decimalPoint == 0 ? gDot : decimalPoint).append(gZero); break; + case kInfinityRule: result.append(gInf, 3); break; + case kNaNRule: result.append(gNaN, 3); break; + default: + // for a normal rule, write out its base value, and if the radix is + // something other than 10, write out the radix (with the preceding + // slash, of course). Then calculate the expected exponent and if + // if isn't the same as the actual exponent, write an appropriate + // number of > signs. Finally, terminate the whole thing with + // a colon. + util_append64(result, baseValue); + if (radix != 10) { + result.append(gSlash); + util_append64(result, radix); + } + int numCarets = expectedExponent() - exponent; + for (int i = 0; i < numCarets; i++) { + result.append(gGreaterThan); + } + break; + } + result.append(gColon); + result.append(gSpace); + + // if the rule text begins with a space, write an apostrophe + // (whitespace after the rule descriptor is ignored; the + // apostrophe is used to make the whitespace significant) + if (fRuleText.charAt(0) == gSpace && (sub1 == nullptr || sub1->getPos() != 0)) { + result.append(gTick); + } + + // now, write the rule's rule text, inserting appropriate + // substitution tokens in the appropriate places + UnicodeString ruleTextCopy; + ruleTextCopy.setTo(fRuleText); + + UnicodeString temp; + if (sub2 != nullptr) { + sub2->toString(temp); + ruleTextCopy.insert(sub2->getPos(), temp); + } + if (sub1 != nullptr) { + sub1->toString(temp); + ruleTextCopy.insert(sub1->getPos(), temp); + } + + result.append(ruleTextCopy); + + // and finally, top the whole thing off with a semicolon and + // return the result + result.append(gSemicolon); +} + +int64_t NFRule::getDivisor() const +{ + return util64_pow(radix, exponent); +} + + +//----------------------------------------------------------------------- +// formatting +//----------------------------------------------------------------------- + +/** +* Formats the number, and inserts the resulting text into +* toInsertInto. +* @param number The number being formatted +* @param toInsertInto The string where the resultant text should +* be inserted +* @param pos The position in toInsertInto where the resultant text +* should be inserted +*/ +void +NFRule::doFormat(int64_t number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const +{ + // first, insert the rule's rule text into toInsertInto at the + // specified position, then insert the results of the substitutions + // into the right places in toInsertInto (notice we do the + // substitutions in reverse order so that the offsets don't get + // messed up) + int32_t pluralRuleStart = fRuleText.length(); + int32_t lengthOffset = 0; + if (!rulePatternFormat) { + toInsertInto.insert(pos, fRuleText); + } + else { + pluralRuleStart = fRuleText.indexOf(gDollarOpenParenthesis, -1, 0); + int pluralRuleEnd = fRuleText.indexOf(gClosedParenthesisDollar, -1, pluralRuleStart); + int initialLength = toInsertInto.length(); + if (pluralRuleEnd < fRuleText.length() - 1) { + toInsertInto.insert(pos, fRuleText.tempSubString(pluralRuleEnd + 2)); + } + toInsertInto.insert(pos, + rulePatternFormat->format((int32_t)(number/util64_pow(radix, exponent)), status)); + if (pluralRuleStart > 0) { + toInsertInto.insert(pos, fRuleText.tempSubString(0, pluralRuleStart)); + } + lengthOffset = fRuleText.length() - (toInsertInto.length() - initialLength); + } + + if (sub2 != nullptr) { + sub2->doSubstitution(number, toInsertInto, pos - (sub2->getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount, status); + } + if (sub1 != nullptr) { + sub1->doSubstitution(number, toInsertInto, pos - (sub1->getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount, status); + } +} + +/** +* Formats the number, and inserts the resulting text into +* toInsertInto. +* @param number The number being formatted +* @param toInsertInto The string where the resultant text should +* be inserted +* @param pos The position in toInsertInto where the resultant text +* should be inserted +*/ +void +NFRule::doFormat(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const +{ + // first, insert the rule's rule text into toInsertInto at the + // specified position, then insert the results of the substitutions + // into the right places in toInsertInto + // [again, we have two copies of this routine that do the same thing + // so that we don't sacrifice precision in a long by casting it + // to a double] + int32_t pluralRuleStart = fRuleText.length(); + int32_t lengthOffset = 0; + if (!rulePatternFormat) { + toInsertInto.insert(pos, fRuleText); + } + else { + pluralRuleStart = fRuleText.indexOf(gDollarOpenParenthesis, -1, 0); + int pluralRuleEnd = fRuleText.indexOf(gClosedParenthesisDollar, -1, pluralRuleStart); + int initialLength = toInsertInto.length(); + if (pluralRuleEnd < fRuleText.length() - 1) { + toInsertInto.insert(pos, fRuleText.tempSubString(pluralRuleEnd + 2)); + } + double pluralVal = number; + if (0 <= pluralVal && pluralVal < 1) { + // We're in a fractional rule, and we have to match the NumeratorSubstitution behavior. + // 2.3 can become 0.2999999999999998 for the fraction due to rounding errors. + pluralVal = uprv_round(pluralVal * util64_pow(radix, exponent)); + } + else { + pluralVal = pluralVal / util64_pow(radix, exponent); + } + toInsertInto.insert(pos, rulePatternFormat->format((int32_t)(pluralVal), status)); + if (pluralRuleStart > 0) { + toInsertInto.insert(pos, fRuleText.tempSubString(0, pluralRuleStart)); + } + lengthOffset = fRuleText.length() - (toInsertInto.length() - initialLength); + } + + if (sub2 != nullptr) { + sub2->doSubstitution(number, toInsertInto, pos - (sub2->getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount, status); + } + if (sub1 != nullptr) { + sub1->doSubstitution(number, toInsertInto, pos - (sub1->getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount, status); + } +} + +/** +* Used by the owning rule set to determine whether to invoke the +* rollback rule (i.e., whether this rule or the one that precedes +* it in the rule set's list should be used to format the number) +* @param The number being formatted +* @return True if the rule set should use the rule that precedes +* this one in its list; false if it should use this rule +*/ +UBool +NFRule::shouldRollBack(int64_t number) const +{ + // we roll back if the rule contains a modulus substitution, + // the number being formatted is an even multiple of the rule's + // divisor, and the rule's base value is NOT an even multiple + // of its divisor + // In other words, if the original description had + // 100: << hundred[ >>]; + // that expands into + // 100: << hundred; + // 101: << hundred >>; + // internally. But when we're formatting 200, if we use the rule + // at 101, which would normally apply, we get "two hundred zero". + // To prevent this, we roll back and use the rule at 100 instead. + // This is the logic that makes this happen: the rule at 101 has + // a modulus substitution, its base value isn't an even multiple + // of 100, and the value we're trying to format _is_ an even + // multiple of 100. This is called the "rollback rule." + if ((sub1 != nullptr && sub1->isModulusSubstitution()) || (sub2 != nullptr && sub2->isModulusSubstitution())) { + int64_t re = util64_pow(radix, exponent); + return (number % re) == 0 && (baseValue % re) != 0; + } + return false; +} + +//----------------------------------------------------------------------- +// parsing +//----------------------------------------------------------------------- + +/** +* Attempts to parse the string with this rule. +* @param text The string being parsed +* @param parsePosition On entry, the value is ignored and assumed to +* be 0. On exit, this has been updated with the position of the first +* character not consumed by matching the text against this rule +* (if this rule doesn't match the text at all, the parse position +* if left unchanged (presumably at 0) and the function returns +* new Long(0)). +* @param isFractionRule True if this rule is contained within a +* fraction rule set. This is only used if the rule has no +* substitutions. +* @return If this rule matched the text, this is the rule's base value +* combined appropriately with the results of parsing the substitutions. +* If nothing matched, this is new Long(0) and the parse position is +* left unchanged. The result will be an instance of Long if the +* result is an integer and Double otherwise. The result is never null. +*/ +#ifdef RBNF_DEBUG +#include + +static void dumpUS(FILE* f, const UnicodeString& us) { + int len = us.length(); + char* buf = (char *)uprv_malloc((len+1)*sizeof(char)); //new char[len+1]; + if (buf != nullptr) { + us.extract(0, len, buf); + buf[len] = 0; + fprintf(f, "%s", buf); + uprv_free(buf); //delete[] buf; + } +} +#endif +UBool +NFRule::doParse(const UnicodeString& text, + ParsePosition& parsePosition, + UBool isFractionRule, + double upperBound, + uint32_t nonNumericalExecutedRuleMask, + Formattable& resVal) const +{ + // internally we operate on a copy of the string being parsed + // (because we're going to change it) and use our own ParsePosition + ParsePosition pp; + UnicodeString workText(text); + + int32_t sub1Pos = sub1 != nullptr ? sub1->getPos() : fRuleText.length(); + int32_t sub2Pos = sub2 != nullptr ? sub2->getPos() : fRuleText.length(); + + // check to see whether the text before the first substitution + // matches the text at the beginning of the string being + // parsed. If it does, strip that off the front of workText; + // otherwise, dump out with a mismatch + UnicodeString prefix; + prefix.setTo(fRuleText, 0, sub1Pos); + +#ifdef RBNF_DEBUG + fprintf(stderr, "doParse %p ", this); + { + UnicodeString rt; + _appendRuleText(rt); + dumpUS(stderr, rt); + } + + fprintf(stderr, " text: '"); + dumpUS(stderr, text); + fprintf(stderr, "' prefix: '"); + dumpUS(stderr, prefix); +#endif + stripPrefix(workText, prefix, pp); + int32_t prefixLength = text.length() - workText.length(); + +#ifdef RBNF_DEBUG + fprintf(stderr, "' pl: %d ppi: %d s1p: %d\n", prefixLength, pp.getIndex(), sub1Pos); +#endif + + if (pp.getIndex() == 0 && sub1Pos != 0) { + // commented out because ParsePosition doesn't have error index in 1.1.x + // restored for ICU4C port + parsePosition.setErrorIndex(pp.getErrorIndex()); + resVal.setLong(0); + return true; + } + if (baseValue == kInfinityRule) { + // If you match this, don't try to perform any calculations on it. + parsePosition.setIndex(pp.getIndex()); + resVal.setDouble(uprv_getInfinity()); + return true; + } + if (baseValue == kNaNRule) { + // If you match this, don't try to perform any calculations on it. + parsePosition.setIndex(pp.getIndex()); + resVal.setDouble(uprv_getNaN()); + return true; + } + + // this is the fun part. The basic guts of the rule-matching + // logic is matchToDelimiter(), which is called twice. The first + // time it searches the input string for the rule text BETWEEN + // the substitutions and tries to match the intervening text + // in the input string with the first substitution. If that + // succeeds, it then calls it again, this time to look for the + // rule text after the second substitution and to match the + // intervening input text against the second substitution. + // + // For example, say we have a rule that looks like this: + // first << middle >> last; + // and input text that looks like this: + // first one middle two last + // First we use stripPrefix() to match "first " in both places and + // strip it off the front, leaving + // one middle two last + // Then we use matchToDelimiter() to match " middle " and try to + // match "one" against a substitution. If it's successful, we now + // have + // two last + // We use matchToDelimiter() a second time to match " last" and + // try to match "two" against a substitution. If "two" matches + // the substitution, we have a successful parse. + // + // Since it's possible in many cases to find multiple instances + // of each of these pieces of rule text in the input string, + // we need to try all the possible combinations of these + // locations. This prevents us from prematurely declaring a mismatch, + // and makes sure we match as much input text as we can. + int highWaterMark = 0; + double result = 0; + int start = 0; + double tempBaseValue = (double)(baseValue <= 0 ? 0 : baseValue); + + UnicodeString temp; + do { + // our partial parse result starts out as this rule's base + // value. If it finds a successful match, matchToDelimiter() + // will compose this in some way with what it gets back from + // the substitution, giving us a new partial parse result + pp.setIndex(0); + + temp.setTo(fRuleText, sub1Pos, sub2Pos - sub1Pos); + double partialResult = matchToDelimiter(workText, start, tempBaseValue, + temp, pp, sub1, + nonNumericalExecutedRuleMask, + upperBound); + + // if we got a successful match (or were trying to match a + // null substitution), pp is now pointing at the first unmatched + // character. Take note of that, and try matchToDelimiter() + // on the input text again + if (pp.getIndex() != 0 || sub1 == nullptr) { + start = pp.getIndex(); + + UnicodeString workText2; + workText2.setTo(workText, pp.getIndex(), workText.length() - pp.getIndex()); + ParsePosition pp2; + + // the second matchToDelimiter() will compose our previous + // partial result with whatever it gets back from its + // substitution if there's a successful match, giving us + // a real result + temp.setTo(fRuleText, sub2Pos, fRuleText.length() - sub2Pos); + partialResult = matchToDelimiter(workText2, 0, partialResult, + temp, pp2, sub2, + nonNumericalExecutedRuleMask, + upperBound); + + // if we got a successful match on this second + // matchToDelimiter() call, update the high-water mark + // and result (if necessary) + if (pp2.getIndex() != 0 || sub2 == nullptr) { + if (prefixLength + pp.getIndex() + pp2.getIndex() > highWaterMark) { + highWaterMark = prefixLength + pp.getIndex() + pp2.getIndex(); + result = partialResult; + } + } + else { + // commented out because ParsePosition doesn't have error index in 1.1.x + // restored for ICU4C port + int32_t i_temp = pp2.getErrorIndex() + sub1Pos + pp.getIndex(); + if (i_temp> parsePosition.getErrorIndex()) { + parsePosition.setErrorIndex(i_temp); + } + } + } + else { + // commented out because ParsePosition doesn't have error index in 1.1.x + // restored for ICU4C port + int32_t i_temp = sub1Pos + pp.getErrorIndex(); + if (i_temp > parsePosition.getErrorIndex()) { + parsePosition.setErrorIndex(i_temp); + } + } + // keep trying to match things until the outer matchToDelimiter() + // call fails to make a match (each time, it picks up where it + // left off the previous time) + } while (sub1Pos != sub2Pos + && pp.getIndex() > 0 + && pp.getIndex() < workText.length() + && pp.getIndex() != start); + + // update the caller's ParsePosition with our high-water mark + // (i.e., it now points at the first character this function + // didn't match-- the ParsePosition is therefore unchanged if + // we didn't match anything) + parsePosition.setIndex(highWaterMark); + // commented out because ParsePosition doesn't have error index in 1.1.x + // restored for ICU4C port + if (highWaterMark > 0) { + parsePosition.setErrorIndex(0); + } + + // this is a hack for one unusual condition: Normally, whether this + // rule belong to a fraction rule set or not is handled by its + // substitutions. But if that rule HAS NO substitutions, then + // we have to account for it here. By definition, if the matching + // rule in a fraction rule set has no substitutions, its numerator + // is 1, and so the result is the reciprocal of its base value. + if (isFractionRule && highWaterMark > 0 && sub1 == nullptr) { + result = 1 / result; + } + + resVal.setDouble(result); + return true; // ??? do we need to worry if it is a long or a double? +} + +/** +* This function is used by parse() to match the text being parsed +* against a possible prefix string. This function +* matches characters from the beginning of the string being parsed +* to characters from the prospective prefix. If they match, pp is +* updated to the first character not matched, and the result is +* the unparsed part of the string. If they don't match, the whole +* string is returned, and pp is left unchanged. +* @param text The string being parsed +* @param prefix The text to match against +* @param pp On entry, ignored and assumed to be 0. On exit, points +* to the first unmatched character (assuming the whole prefix matched), +* or is unchanged (if the whole prefix didn't match). +* @return If things match, this is the unparsed part of "text"; +* if they didn't match, this is "text". +*/ +void +NFRule::stripPrefix(UnicodeString& text, const UnicodeString& prefix, ParsePosition& pp) const +{ + // if the prefix text is empty, dump out without doing anything + if (prefix.length() != 0) { + UErrorCode status = U_ZERO_ERROR; + // use prefixLength() to match the beginning of + // "text" against "prefix". This function returns the + // number of characters from "text" that matched (or 0 if + // we didn't match the whole prefix) + int32_t pfl = prefixLength(text, prefix, status); + if (U_FAILURE(status)) { // Memory allocation error. + return; + } + if (pfl != 0) { + // if we got a successful match, update the parse position + // and strip the prefix off of "text" + pp.setIndex(pp.getIndex() + pfl); + text.remove(0, pfl); + } + } +} + +/** +* Used by parse() to match a substitution and any following text. +* "text" is searched for instances of "delimiter". For each instance +* of delimiter, the intervening text is tested to see whether it +* matches the substitution. The longest match wins. +* @param text The string being parsed +* @param startPos The position in "text" where we should start looking +* for "delimiter". +* @param baseValue A partial parse result (often the rule's base value), +* which is combined with the result from matching the substitution +* @param delimiter The string to search "text" for. +* @param pp Ignored and presumed to be 0 on entry. If there's a match, +* on exit this will point to the first unmatched character. +* @param sub If we find "delimiter" in "text", this substitution is used +* to match the text between the beginning of the string and the +* position of "delimiter." (If "delimiter" is the empty string, then +* this function just matches against this substitution and updates +* everything accordingly.) +* @param upperBound When matching the substitution, it will only +* consider rules with base values lower than this value. +* @return If there's a match, this is the result of composing +* baseValue with the result of matching the substitution. Otherwise, +* this is new Long(0). It's never null. If the result is an integer, +* this will be an instance of Long; otherwise, it's an instance of +* Double. +* +* !!! note {dlf} in point of fact, in the java code the caller always converts +* the result to a double, so we might as well return one. +*/ +double +NFRule::matchToDelimiter(const UnicodeString& text, + int32_t startPos, + double _baseValue, + const UnicodeString& delimiter, + ParsePosition& pp, + const NFSubstitution* sub, + uint32_t nonNumericalExecutedRuleMask, + double upperBound) const +{ + UErrorCode status = U_ZERO_ERROR; + // if "delimiter" contains real (i.e., non-ignorable) text, search + // it for "delimiter" beginning at "start". If that succeeds, then + // use "sub"'s doParse() method to match the text before the + // instance of "delimiter" we just found. + if (!allIgnorable(delimiter, status)) { + if (U_FAILURE(status)) { //Memory allocation error. + return 0; + } + ParsePosition tempPP; + Formattable result; + + // use findText() to search for "delimiter". It returns a two- + // element array: element 0 is the position of the match, and + // element 1 is the number of characters that matched + // "delimiter". + int32_t dLen; + int32_t dPos = findText(text, delimiter, startPos, &dLen); + + // if findText() succeeded, isolate the text preceding the + // match, and use "sub" to match that text + while (dPos >= 0) { + UnicodeString subText; + subText.setTo(text, 0, dPos); + if (subText.length() > 0) { + UBool success = sub->doParse(subText, tempPP, _baseValue, upperBound, +#if UCONFIG_NO_COLLATION + false, +#else + formatter->isLenient(), +#endif + nonNumericalExecutedRuleMask, + result); + + // if the substitution could match all the text up to + // where we found "delimiter", then this function has + // a successful match. Bump the caller's parse position + // to point to the first character after the text + // that matches "delimiter", and return the result + // we got from parsing the substitution. + if (success && tempPP.getIndex() == dPos) { + pp.setIndex(dPos + dLen); + return result.getDouble(); + } + else { + // commented out because ParsePosition doesn't have error index in 1.1.x + // restored for ICU4C port + if (tempPP.getErrorIndex() > 0) { + pp.setErrorIndex(tempPP.getErrorIndex()); + } else { + pp.setErrorIndex(tempPP.getIndex()); + } + } + } + + // if we didn't match the substitution, search for another + // copy of "delimiter" in "text" and repeat the loop if + // we find it + tempPP.setIndex(0); + dPos = findText(text, delimiter, dPos + dLen, &dLen); + } + // if we make it here, this was an unsuccessful match, and we + // leave pp unchanged and return 0 + pp.setIndex(0); + return 0; + + // if "delimiter" is empty, or consists only of ignorable characters + // (i.e., is semantically empty), thwe we obviously can't search + // for "delimiter". Instead, just use "sub" to parse as much of + // "text" as possible. + } + else if (sub == nullptr) { + return _baseValue; + } + else { + ParsePosition tempPP; + Formattable result; + + // try to match the whole string against the substitution + UBool success = sub->doParse(text, tempPP, _baseValue, upperBound, +#if UCONFIG_NO_COLLATION + false, +#else + formatter->isLenient(), +#endif + nonNumericalExecutedRuleMask, + result); + if (success && (tempPP.getIndex() != 0)) { + // if there's a successful match (or it's a null + // substitution), update pp to point to the first + // character we didn't match, and pass the result from + // sub.doParse() on through to the caller + pp.setIndex(tempPP.getIndex()); + return result.getDouble(); + } + else { + // commented out because ParsePosition doesn't have error index in 1.1.x + // restored for ICU4C port + pp.setErrorIndex(tempPP.getErrorIndex()); + } + + // and if we get to here, then nothing matched, so we return + // 0 and leave pp alone + return 0; + } +} + +/** +* Used by stripPrefix() to match characters. If lenient parse mode +* is off, this just calls startsWith(). If lenient parse mode is on, +* this function uses CollationElementIterators to match characters in +* the strings (only primary-order differences are significant in +* determining whether there's a match). +* @param str The string being tested +* @param prefix The text we're hoping to see at the beginning +* of "str" +* @return If "prefix" is found at the beginning of "str", this +* is the number of characters in "str" that were matched (this +* isn't necessarily the same as the length of "prefix" when matching +* text with a collator). If there's no match, this is 0. +*/ +int32_t +NFRule::prefixLength(const UnicodeString& str, const UnicodeString& prefix, UErrorCode& status) const +{ + // if we're looking for an empty prefix, it obviously matches + // zero characters. Just go ahead and return 0. + if (prefix.length() == 0) { + return 0; + } + +#if !UCONFIG_NO_COLLATION + // go through all this grief if we're in lenient-parse mode + if (formatter->isLenient()) { + // Check if non-lenient rule finds the text before call lenient parsing + if (str.startsWith(prefix)) { + return prefix.length(); + } + // get the formatter's collator and use it to create two + // collation element iterators, one over the target string + // and another over the prefix (right now, we'll throw an + // exception if the collator we get back from the formatter + // isn't a RuleBasedCollator, because RuleBasedCollator defines + // the CollationElementIterator protocol. Hopefully, this + // will change someday.) + const RuleBasedCollator* collator = formatter->getCollator(); + if (collator == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + LocalPointer strIter(collator->createCollationElementIterator(str)); + LocalPointer prefixIter(collator->createCollationElementIterator(prefix)); + // Check for memory allocation error. + if (strIter.isNull() || prefixIter.isNull()) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + + UErrorCode err = U_ZERO_ERROR; + + // The original code was problematic. Consider this match: + // prefix = "fifty-" + // string = " fifty-7" + // The intent is to match string up to the '7', by matching 'fifty-' at position 1 + // in the string. Unfortunately, we were getting a match, and then computing where + // the match terminated by rematching the string. The rematch code was using as an + // initial guess the substring of string between 0 and prefix.length. Because of + // the leading space and trailing hyphen (both ignorable) this was succeeding, leaving + // the position before the hyphen in the string. Recursing down, we then parsed the + // remaining string '-7' as numeric. The resulting number turned out as 43 (50 - 7). + // This was not pretty, especially since the string "fifty-7" parsed just fine. + // + // We have newer APIs now, so we can use calls on the iterator to determine what we + // matched up to. If we terminate because we hit the last element in the string, + // our match terminates at this length. If we terminate because we hit the last element + // in the target, our match terminates at one before the element iterator position. + + // match collation elements between the strings + int32_t oStr = strIter->next(err); + int32_t oPrefix = prefixIter->next(err); + + while (oPrefix != CollationElementIterator::NULLORDER) { + // skip over ignorable characters in the target string + while (CollationElementIterator::primaryOrder(oStr) == 0 + && oStr != CollationElementIterator::NULLORDER) { + oStr = strIter->next(err); + } + + // skip over ignorable characters in the prefix + while (CollationElementIterator::primaryOrder(oPrefix) == 0 + && oPrefix != CollationElementIterator::NULLORDER) { + oPrefix = prefixIter->next(err); + } + + // dlf: move this above following test, if we consume the + // entire target, aren't we ok even if the source was also + // entirely consumed? + + // if skipping over ignorables brought to the end of + // the prefix, we DID match: drop out of the loop + if (oPrefix == CollationElementIterator::NULLORDER) { + break; + } + + // if skipping over ignorables brought us to the end + // of the target string, we didn't match and return 0 + if (oStr == CollationElementIterator::NULLORDER) { + return 0; + } + + // match collation elements from the two strings + // (considering only primary differences). If we + // get a mismatch, dump out and return 0 + if (CollationElementIterator::primaryOrder(oStr) + != CollationElementIterator::primaryOrder(oPrefix)) { + return 0; + + // otherwise, advance to the next character in each string + // and loop (we drop out of the loop when we exhaust + // collation elements in the prefix) + } else { + oStr = strIter->next(err); + oPrefix = prefixIter->next(err); + } + } + + int32_t result = strIter->getOffset(); + if (oStr != CollationElementIterator::NULLORDER) { + --result; // back over character that we don't want to consume; + } + +#ifdef RBNF_DEBUG + fprintf(stderr, "prefix length: %d\n", result); +#endif + return result; +#if 0 + //---------------------------------------------------------------- + // JDK 1.2-specific API call + // return strIter.getOffset(); + //---------------------------------------------------------------- + // JDK 1.1 HACK (take out for 1.2-specific code) + + // if we make it to here, we have a successful match. Now we + // have to find out HOW MANY characters from the target string + // matched the prefix (there isn't necessarily a one-to-one + // mapping between collation elements and characters). + // In JDK 1.2, there's a simple getOffset() call we can use. + // In JDK 1.1, on the other hand, we have to go through some + // ugly contortions. First, use the collator to compare the + // same number of characters from the prefix and target string. + // If they're equal, we're done. + collator->setStrength(Collator::PRIMARY); + if (str.length() >= prefix.length()) { + UnicodeString temp; + temp.setTo(str, 0, prefix.length()); + if (collator->equals(temp, prefix)) { +#ifdef RBNF_DEBUG + fprintf(stderr, "returning: %d\n", prefix.length()); +#endif + return prefix.length(); + } + } + + // if they're not equal, then we have to compare successively + // larger and larger substrings of the target string until we + // get to one that matches the prefix. At that point, we know + // how many characters matched the prefix, and we can return. + int32_t p = 1; + while (p <= str.length()) { + UnicodeString temp; + temp.setTo(str, 0, p); + if (collator->equals(temp, prefix)) { + return p; + } else { + ++p; + } + } + + // SHOULD NEVER GET HERE!!! + return 0; + //---------------------------------------------------------------- +#endif + + // If lenient parsing is turned off, forget all that crap above. + // Just use String.startsWith() and be done with it. + } else +#endif + { + if (str.startsWith(prefix)) { + return prefix.length(); + } else { + return 0; + } + } +} + +/** +* Searches a string for another string. If lenient parsing is off, +* this just calls indexOf(). If lenient parsing is on, this function +* uses CollationElementIterator to match characters, and only +* primary-order differences are significant in determining whether +* there's a match. +* @param str The string to search +* @param key The string to search "str" for +* @param startingAt The index into "str" where the search is to +* begin +* @return A two-element array of ints. Element 0 is the position +* of the match, or -1 if there was no match. Element 1 is the +* number of characters in "str" that matched (which isn't necessarily +* the same as the length of "key") +*/ +int32_t +NFRule::findText(const UnicodeString& str, + const UnicodeString& key, + int32_t startingAt, + int32_t* length) const +{ + if (rulePatternFormat) { + Formattable result; + FieldPosition position(UNUM_INTEGER_FIELD); + position.setBeginIndex(startingAt); + rulePatternFormat->parseType(str, this, result, position); + int start = position.getBeginIndex(); + if (start >= 0) { + int32_t pluralRuleStart = fRuleText.indexOf(gDollarOpenParenthesis, -1, 0); + int32_t pluralRuleSuffix = fRuleText.indexOf(gClosedParenthesisDollar, -1, pluralRuleStart) + 2; + int32_t matchLen = position.getEndIndex() - start; + UnicodeString prefix(fRuleText.tempSubString(0, pluralRuleStart)); + UnicodeString suffix(fRuleText.tempSubString(pluralRuleSuffix)); + if (str.compare(start - prefix.length(), prefix.length(), prefix, 0, prefix.length()) == 0 + && str.compare(start + matchLen, suffix.length(), suffix, 0, suffix.length()) == 0) + { + *length = matchLen + prefix.length() + suffix.length(); + return start - prefix.length(); + } + } + *length = 0; + return -1; + } + if (!formatter->isLenient()) { + // if lenient parsing is turned off, this is easy: just call + // String.indexOf() and we're done + *length = key.length(); + return str.indexOf(key, startingAt); + } + else { + // Check if non-lenient rule finds the text before call lenient parsing + *length = key.length(); + int32_t pos = str.indexOf(key, startingAt); + if(pos >= 0) { + return pos; + } else { + // but if lenient parsing is turned ON, we've got some work ahead of us + return findTextLenient(str, key, startingAt, length); + } + } +} + +int32_t +NFRule::findTextLenient(const UnicodeString& str, + const UnicodeString& key, + int32_t startingAt, + int32_t* length) const +{ + //---------------------------------------------------------------- + // JDK 1.1 HACK (take out of 1.2-specific code) + + // in JDK 1.2, CollationElementIterator provides us with an + // API to map between character offsets and collation elements + // and we can do this by marching through the string comparing + // collation elements. We can't do that in JDK 1.1. Instead, + // we have to go through this horrible slow mess: + int32_t p = startingAt; + int32_t keyLen = 0; + + // basically just isolate smaller and smaller substrings of + // the target string (each running to the end of the string, + // and with the first one running from startingAt to the end) + // and then use prefixLength() to see if the search key is at + // the beginning of each substring. This is excruciatingly + // slow, but it will locate the key and tell use how long the + // matching text was. + UnicodeString temp; + UErrorCode status = U_ZERO_ERROR; + while (p < str.length() && keyLen == 0) { + temp.setTo(str, p, str.length() - p); + keyLen = prefixLength(temp, key, status); + if (U_FAILURE(status)) { + break; + } + if (keyLen != 0) { + *length = keyLen; + return p; + } + ++p; + } + // if we make it to here, we didn't find it. Return -1 for the + // location. The length should be ignored, but set it to 0, + // which should be "safe" + *length = 0; + return -1; +} + +/** +* Checks to see whether a string consists entirely of ignorable +* characters. +* @param str The string to test. +* @return true if the string is empty of consists entirely of +* characters that the number formatter's collator says are +* ignorable at the primary-order level. false otherwise. +*/ +UBool +NFRule::allIgnorable(const UnicodeString& str, UErrorCode& status) const +{ + // if the string is empty, we can just return true + if (str.length() == 0) { + return true; + } + +#if !UCONFIG_NO_COLLATION + // if lenient parsing is turned on, walk through the string with + // a collation element iterator and make sure each collation + // element is 0 (ignorable) at the primary level + if (formatter->isLenient()) { + const RuleBasedCollator* collator = formatter->getCollator(); + if (collator == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return false; + } + LocalPointer iter(collator->createCollationElementIterator(str)); + + // Memory allocation error check. + if (iter.isNull()) { + status = U_MEMORY_ALLOCATION_ERROR; + return false; + } + + UErrorCode err = U_ZERO_ERROR; + int32_t o = iter->next(err); + while (o != CollationElementIterator::NULLORDER + && CollationElementIterator::primaryOrder(o) == 0) { + o = iter->next(err); + } + + return o == CollationElementIterator::NULLORDER; + } +#endif + + // if lenient parsing is turned off, there is no such thing as + // an ignorable character: return true only if the string is empty + return false; +} + +void +NFRule::setDecimalFormatSymbols(const DecimalFormatSymbols& newSymbols, UErrorCode& status) { + if (sub1 != nullptr) { + sub1->setDecimalFormatSymbols(newSymbols, status); + } + if (sub2 != nullptr) { + sub2->setDecimalFormatSymbols(newSymbols, status); + } +} + +U_NAMESPACE_END + +/* U_HAVE_RBNF */ +#endif diff --git a/deps/icu-small/source/i18n/nfrule.h b/deps/icu-small/source/i18n/nfrule.h index 5e615e485c8077..b2ecdf68c9c73b 100644 --- a/deps/icu-small/source/i18n/nfrule.h +++ b/deps/icu-small/source/i18n/nfrule.h @@ -1,129 +1,129 @@ -// © 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 NFRULE_H -#define NFRULE_H - -#include "unicode/rbnf.h" - -#if U_HAVE_RBNF - -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "unicode/unistr.h" - -U_NAMESPACE_BEGIN - -class FieldPosition; -class Formattable; -class NFRuleList; -class NFRuleSet; -class NFSubstitution; -class ParsePosition; -class PluralFormat; -class RuleBasedNumberFormat; -class UnicodeString; - -class NFRule : public UMemory { -public: - - enum ERuleType { - kNoBase = 0, - kNegativeNumberRule = -1, - kImproperFractionRule = -2, - kProperFractionRule = -3, - kDefaultRule = -4, - kInfinityRule = -5, - kNaNRule = -6, - kOtherRule = -7 - }; - - static void makeRules(UnicodeString& definition, - NFRuleSet* ruleSet, - const NFRule* predecessor, - const RuleBasedNumberFormat* rbnf, - NFRuleList& ruleList, - UErrorCode& status); - - NFRule(const RuleBasedNumberFormat* rbnf, const UnicodeString &ruleText, UErrorCode &status); - ~NFRule(); - - bool operator==(const NFRule& rhs) const; - bool operator!=(const NFRule& rhs) const { return !operator==(rhs); } - - ERuleType getType() const { return (ERuleType)(baseValue <= kNoBase ? (ERuleType)baseValue : kOtherRule); } - void setType(ERuleType ruleType) { baseValue = (int32_t)ruleType; } - - int64_t getBaseValue() const { return baseValue; } - void setBaseValue(int64_t value, UErrorCode& status); - - UChar getDecimalPoint() const { return decimalPoint; } - - int64_t getDivisor() const; - - void doFormat(int64_t number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const; - void doFormat(double number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const; - - UBool doParse(const UnicodeString& text, - ParsePosition& pos, - UBool isFractional, - double upperBound, - uint32_t nonNumericalExecutedRuleMask, - Formattable& result) const; - - UBool shouldRollBack(int64_t number) const; - - void _appendRuleText(UnicodeString& result) const; - - int32_t findTextLenient(const UnicodeString& str, const UnicodeString& key, - int32_t startingAt, int32_t* resultCount) const; - - void setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& status); - -private: - void parseRuleDescriptor(UnicodeString& descriptor, UErrorCode& status); - void extractSubstitutions(const NFRuleSet* ruleSet, const UnicodeString &ruleText, const NFRule* predecessor, UErrorCode& status); - NFSubstitution* extractSubstitution(const NFRuleSet* ruleSet, const NFRule* predecessor, UErrorCode& status); - - int16_t expectedExponent() const; - int32_t indexOfAnyRulePrefix() const; - double matchToDelimiter(const UnicodeString& text, int32_t startPos, double baseValue, - const UnicodeString& delimiter, ParsePosition& pp, const NFSubstitution* sub, - uint32_t nonNumericalExecutedRuleMask, - double upperBound) const; - void stripPrefix(UnicodeString& text, const UnicodeString& prefix, ParsePosition& pp) const; - - int32_t prefixLength(const UnicodeString& str, const UnicodeString& prefix, UErrorCode& status) const; - UBool allIgnorable(const UnicodeString& str, UErrorCode& status) const; - int32_t findText(const UnicodeString& str, const UnicodeString& key, - int32_t startingAt, int32_t* resultCount) const; - -private: - int64_t baseValue; - int32_t radix; - int16_t exponent; - UChar decimalPoint; - UnicodeString fRuleText; - NFSubstitution* sub1; - NFSubstitution* sub2; - const RuleBasedNumberFormat* formatter; - const PluralFormat* rulePatternFormat; - - NFRule(const NFRule &other); // forbid copying of this class - NFRule &operator=(const NFRule &other); // forbid copying of this class -}; - -U_NAMESPACE_END - -/* U_HAVE_RBNF */ -#endif - -// NFRULE_H -#endif - +// © 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 NFRULE_H +#define NFRULE_H + +#include "unicode/rbnf.h" + +#if U_HAVE_RBNF + +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "unicode/unistr.h" + +U_NAMESPACE_BEGIN + +class FieldPosition; +class Formattable; +class NFRuleList; +class NFRuleSet; +class NFSubstitution; +class ParsePosition; +class PluralFormat; +class RuleBasedNumberFormat; +class UnicodeString; + +class NFRule : public UMemory { +public: + + enum ERuleType { + kNoBase = 0, + kNegativeNumberRule = -1, + kImproperFractionRule = -2, + kProperFractionRule = -3, + kDefaultRule = -4, + kInfinityRule = -5, + kNaNRule = -6, + kOtherRule = -7 + }; + + static void makeRules(UnicodeString& definition, + NFRuleSet* ruleSet, + const NFRule* predecessor, + const RuleBasedNumberFormat* rbnf, + NFRuleList& ruleList, + UErrorCode& status); + + NFRule(const RuleBasedNumberFormat* rbnf, const UnicodeString &ruleText, UErrorCode &status); + ~NFRule(); + + bool operator==(const NFRule& rhs) const; + bool operator!=(const NFRule& rhs) const { return !operator==(rhs); } + + ERuleType getType() const { return (ERuleType)(baseValue <= kNoBase ? (ERuleType)baseValue : kOtherRule); } + void setType(ERuleType ruleType) { baseValue = (int32_t)ruleType; } + + int64_t getBaseValue() const { return baseValue; } + void setBaseValue(int64_t value, UErrorCode& status); + + char16_t getDecimalPoint() const { return decimalPoint; } + + int64_t getDivisor() const; + + void doFormat(int64_t number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const; + void doFormat(double number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const; + + UBool doParse(const UnicodeString& text, + ParsePosition& pos, + UBool isFractional, + double upperBound, + uint32_t nonNumericalExecutedRuleMask, + Formattable& result) const; + + UBool shouldRollBack(int64_t number) const; + + void _appendRuleText(UnicodeString& result) const; + + int32_t findTextLenient(const UnicodeString& str, const UnicodeString& key, + int32_t startingAt, int32_t* resultCount) const; + + void setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& status); + +private: + void parseRuleDescriptor(UnicodeString& descriptor, UErrorCode& status); + void extractSubstitutions(const NFRuleSet* ruleSet, const UnicodeString &ruleText, const NFRule* predecessor, UErrorCode& status); + NFSubstitution* extractSubstitution(const NFRuleSet* ruleSet, const NFRule* predecessor, UErrorCode& status); + + int16_t expectedExponent() const; + int32_t indexOfAnyRulePrefix() const; + double matchToDelimiter(const UnicodeString& text, int32_t startPos, double baseValue, + const UnicodeString& delimiter, ParsePosition& pp, const NFSubstitution* sub, + uint32_t nonNumericalExecutedRuleMask, + double upperBound) const; + void stripPrefix(UnicodeString& text, const UnicodeString& prefix, ParsePosition& pp) const; + + int32_t prefixLength(const UnicodeString& str, const UnicodeString& prefix, UErrorCode& status) const; + UBool allIgnorable(const UnicodeString& str, UErrorCode& status) const; + int32_t findText(const UnicodeString& str, const UnicodeString& key, + int32_t startingAt, int32_t* resultCount) const; + +private: + int64_t baseValue; + int32_t radix; + int16_t exponent; + char16_t decimalPoint; + UnicodeString fRuleText; + NFSubstitution* sub1; + NFSubstitution* sub2; + const RuleBasedNumberFormat* formatter; + const PluralFormat* rulePatternFormat; + + NFRule(const NFRule &other); // forbid copying of this class + NFRule &operator=(const NFRule &other); // forbid copying of this class +}; + +U_NAMESPACE_END + +/* U_HAVE_RBNF */ +#endif + +// NFRULE_H +#endif + diff --git a/deps/icu-small/source/i18n/nfsubs.cpp b/deps/icu-small/source/i18n/nfsubs.cpp index 9dba77b1e30823..fdbbe3285797cd 100644 --- a/deps/icu-small/source/i18n/nfsubs.cpp +++ b/deps/icu-small/source/i18n/nfsubs.cpp @@ -1,1343 +1,1343 @@ -// © 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 name: nfsubs.cpp -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* Modification history -* Date Name Comments -* 10/11/2001 Doug Ported from ICU4J -*/ - -#include -#include "utypeinfo.h" // for 'typeid' to work - -#include "nfsubs.h" -#include "fmtableimp.h" -#include "putilimp.h" -#include "number_decimalquantity.h" - -#if U_HAVE_RBNF - -static const UChar gLessThan = 0x003c; -static const UChar gEquals = 0x003d; -static const UChar gGreaterThan = 0x003e; -static const UChar gPercent = 0x0025; -static const UChar gPound = 0x0023; -static const UChar gZero = 0x0030; -static const UChar gSpace = 0x0020; - -static const UChar gEqualsEquals[] = -{ - 0x3D, 0x3D, 0 -}; /* "==" */ -static const UChar gGreaterGreaterGreaterThan[] = -{ - 0x3E, 0x3E, 0x3E, 0 -}; /* ">>>" */ -static const UChar gGreaterGreaterThan[] = -{ - 0x3E, 0x3E, 0 -}; /* ">>" */ - -U_NAMESPACE_BEGIN - -using number::impl::DecimalQuantity; - -class SameValueSubstitution : public NFSubstitution { -public: - SameValueSubstitution(int32_t pos, - const NFRuleSet* ruleset, - const UnicodeString& description, - UErrorCode& status); - virtual ~SameValueSubstitution(); - - virtual int64_t transformNumber(int64_t number) const override { return number; } - virtual double transformNumber(double number) const override { return number; } - virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const override { return newRuleValue; } - virtual double calcUpperBound(double oldUpperBound) const override { return oldUpperBound; } - virtual UChar tokenChar() const override { return (UChar)0x003d; } // '=' - -public: - static UClassID getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; -}; - -SameValueSubstitution::~SameValueSubstitution() {} - -class MultiplierSubstitution : public NFSubstitution { - int64_t divisor; - -public: - MultiplierSubstitution(int32_t _pos, - const NFRule *rule, - const NFRuleSet* _ruleSet, - const UnicodeString& description, - UErrorCode& status) - : NFSubstitution(_pos, _ruleSet, description, status), divisor(rule->getDivisor()) - { - if (divisor == 0) { - status = U_PARSE_ERROR; - } - } - virtual ~MultiplierSubstitution(); - - virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status) override { - divisor = util64_pow(radix, exponent); - - if(divisor == 0) { - status = U_PARSE_ERROR; - } - } - - virtual bool operator==(const NFSubstitution& rhs) const override; - - virtual int64_t transformNumber(int64_t number) const override { - return number / divisor; - } - - virtual double transformNumber(double number) const override { - if (getRuleSet()) { - return uprv_floor(number / divisor); - } else { - return number / divisor; - } - } - - virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const override { - return newRuleValue * divisor; - } - - virtual double calcUpperBound(double /*oldUpperBound*/) const override { return static_cast(divisor); } - - virtual UChar tokenChar() const override { return (UChar)0x003c; } // '<' - -public: - static UClassID getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; -}; - -MultiplierSubstitution::~MultiplierSubstitution() {} - -class ModulusSubstitution : public NFSubstitution { - int64_t divisor; - const NFRule* ruleToUse; -public: - ModulusSubstitution(int32_t pos, - const NFRule* rule, - const NFRule* rulePredecessor, - const NFRuleSet* ruleSet, - const UnicodeString& description, - UErrorCode& status); - virtual ~ModulusSubstitution(); - - virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status) override { - divisor = util64_pow(radix, exponent); - - if (divisor == 0) { - status = U_PARSE_ERROR; - } - } - - virtual bool operator==(const NFSubstitution& rhs) const override; - - virtual void doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override; - virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override; - - virtual int64_t transformNumber(int64_t number) const override { return number % divisor; } - virtual double transformNumber(double number) const override { return uprv_fmod(number, static_cast(divisor)); } - - virtual UBool doParse(const UnicodeString& text, - ParsePosition& parsePosition, - double baseValue, - double upperBound, - UBool lenientParse, - uint32_t nonNumericalExecutedRuleMask, - Formattable& result) const override; - - virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { - return oldRuleValue - uprv_fmod(oldRuleValue, static_cast(divisor)) + newRuleValue; - } - - virtual double calcUpperBound(double /*oldUpperBound*/) const override { return static_cast(divisor); } - - virtual UBool isModulusSubstitution() const override { return true; } - - virtual UChar tokenChar() const override { return (UChar)0x003e; } // '>' - - virtual void toString(UnicodeString& result) const override; - -public: - static UClassID getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; -}; - -ModulusSubstitution::~ModulusSubstitution() {} - -class IntegralPartSubstitution : public NFSubstitution { -public: - IntegralPartSubstitution(int32_t _pos, - const NFRuleSet* _ruleSet, - const UnicodeString& description, - UErrorCode& status) - : NFSubstitution(_pos, _ruleSet, description, status) {} - virtual ~IntegralPartSubstitution(); - - virtual int64_t transformNumber(int64_t number) const override { return number; } - virtual double transformNumber(double number) const override { return uprv_floor(number); } - virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue + oldRuleValue; } - virtual double calcUpperBound(double /*oldUpperBound*/) const override { return DBL_MAX; } - virtual UChar tokenChar() const override { return (UChar)0x003c; } // '<' - -public: - static UClassID getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; -}; - -IntegralPartSubstitution::~IntegralPartSubstitution() {} - -class FractionalPartSubstitution : public NFSubstitution { - UBool byDigits; - UBool useSpaces; - enum { kMaxDecimalDigits = 8 }; -public: - FractionalPartSubstitution(int32_t pos, - const NFRuleSet* ruleSet, - const UnicodeString& description, - UErrorCode& status); - virtual ~FractionalPartSubstitution(); - - virtual bool operator==(const NFSubstitution& rhs) const override; - - virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override; - virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const override {} - virtual int64_t transformNumber(int64_t /*number*/) const override { return 0; } - virtual double transformNumber(double number) const override { return number - uprv_floor(number); } - - virtual UBool doParse(const UnicodeString& text, - ParsePosition& parsePosition, - double baseValue, - double upperBound, - UBool lenientParse, - uint32_t nonNumericalExecutedRuleMask, - Formattable& result) const override; - - virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue + oldRuleValue; } - virtual double calcUpperBound(double /*oldUpperBound*/) const override { return 0.0; } - virtual UChar tokenChar() const override { return (UChar)0x003e; } // '>' - -public: - static UClassID getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; -}; - -FractionalPartSubstitution::~FractionalPartSubstitution() {} - -class AbsoluteValueSubstitution : public NFSubstitution { -public: - AbsoluteValueSubstitution(int32_t _pos, - const NFRuleSet* _ruleSet, - const UnicodeString& description, - UErrorCode& status) - : NFSubstitution(_pos, _ruleSet, description, status) {} - virtual ~AbsoluteValueSubstitution(); - - virtual int64_t transformNumber(int64_t number) const override { return number >= 0 ? number : -number; } - virtual double transformNumber(double number) const override { return uprv_fabs(number); } - virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const override { return -newRuleValue; } - virtual double calcUpperBound(double /*oldUpperBound*/) const override { return DBL_MAX; } - virtual UChar tokenChar() const override { return (UChar)0x003e; } // '>' - -public: - static UClassID getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; -}; - -AbsoluteValueSubstitution::~AbsoluteValueSubstitution() {} - -class NumeratorSubstitution : public NFSubstitution { - double denominator; - int64_t ldenominator; - UBool withZeros; -public: - static inline UnicodeString fixdesc(const UnicodeString& desc) { - if (desc.endsWith(LTLT, 2)) { - UnicodeString result(desc, 0, desc.length()-1); - return result; - } - return desc; - } - NumeratorSubstitution(int32_t _pos, - double _denominator, - NFRuleSet* _ruleSet, - const UnicodeString& description, - UErrorCode& status) - : NFSubstitution(_pos, _ruleSet, fixdesc(description), status), denominator(_denominator) - { - ldenominator = util64_fromDouble(denominator); - withZeros = description.endsWith(LTLT, 2); - } - virtual ~NumeratorSubstitution(); - - virtual bool operator==(const NFSubstitution& rhs) const override; - - virtual int64_t transformNumber(int64_t number) const override { return number * ldenominator; } - virtual double transformNumber(double number) const override { return uprv_round(number * denominator); } - - virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const override {} - virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override; - virtual UBool doParse(const UnicodeString& text, - ParsePosition& parsePosition, - double baseValue, - double upperBound, - UBool /*lenientParse*/, - uint32_t nonNumericalExecutedRuleMask, - Formattable& result) const override; - - virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue / oldRuleValue; } - virtual double calcUpperBound(double /*oldUpperBound*/) const override { return denominator; } - virtual UChar tokenChar() const override { return (UChar)0x003c; } // '<' -private: - static const UChar LTLT[2]; - -public: - static UClassID getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; -}; - -NumeratorSubstitution::~NumeratorSubstitution() {} - -NFSubstitution* -NFSubstitution::makeSubstitution(int32_t pos, - const NFRule* rule, - const NFRule* predecessor, - const NFRuleSet* ruleSet, - const RuleBasedNumberFormat* formatter, - const UnicodeString& description, - UErrorCode& status) -{ - // if the description is empty, return a NullSubstitution - if (description.length() == 0) { - return NULL; - } - - switch (description.charAt(0)) { - // if the description begins with '<'... - case gLessThan: - // throw an exception if the rule is a negative number - // rule - if (rule->getBaseValue() == NFRule::kNegativeNumberRule) { - // throw new IllegalArgumentException("<< not allowed in negative-number rule"); - status = U_PARSE_ERROR; - return NULL; - } - - // if the rule is a fraction rule, return an - // IntegralPartSubstitution - else if (rule->getBaseValue() == NFRule::kImproperFractionRule - || rule->getBaseValue() == NFRule::kProperFractionRule - || rule->getBaseValue() == NFRule::kDefaultRule) { - return new IntegralPartSubstitution(pos, ruleSet, description, status); - } - - // if the rule set containing the rule is a fraction - // rule set, return a NumeratorSubstitution - else if (ruleSet->isFractionRuleSet()) { - return new NumeratorSubstitution(pos, (double)rule->getBaseValue(), - formatter->getDefaultRuleSet(), description, status); - } - - // otherwise, return a MultiplierSubstitution - else { - return new MultiplierSubstitution(pos, rule, ruleSet, - description, status); - } - - // if the description begins with '>'... - case gGreaterThan: - // if the rule is a negative-number rule, return - // an AbsoluteValueSubstitution - if (rule->getBaseValue() == NFRule::kNegativeNumberRule) { - return new AbsoluteValueSubstitution(pos, ruleSet, description, status); - } - - // if the rule is a fraction rule, return a - // FractionalPartSubstitution - else if (rule->getBaseValue() == NFRule::kImproperFractionRule - || rule->getBaseValue() == NFRule::kProperFractionRule - || rule->getBaseValue() == NFRule::kDefaultRule) { - return new FractionalPartSubstitution(pos, ruleSet, description, status); - } - - // if the rule set owning the rule is a fraction rule set, - // throw an exception - else if (ruleSet->isFractionRuleSet()) { - // throw new IllegalArgumentException(">> not allowed in fraction rule set"); - status = U_PARSE_ERROR; - return NULL; - } - - // otherwise, return a ModulusSubstitution - else { - return new ModulusSubstitution(pos, rule, predecessor, - ruleSet, description, status); - } - - // if the description begins with '=', always return a - // SameValueSubstitution - case gEquals: - return new SameValueSubstitution(pos, ruleSet, description, status); - - // and if it's anything else, throw an exception - default: - // throw new IllegalArgumentException("Illegal substitution character"); - status = U_PARSE_ERROR; - } - return NULL; -} - -NFSubstitution::NFSubstitution(int32_t _pos, - const NFRuleSet* _ruleSet, - const UnicodeString& description, - UErrorCode& status) - : pos(_pos), ruleSet(NULL), numberFormat(NULL) -{ - // the description should begin and end with the same character. - // If it doesn't that's a syntax error. Otherwise, - // makeSubstitution() was the only thing that needed to know - // about these characters, so strip them off - UnicodeString workingDescription(description); - if (description.length() >= 2 - && description.charAt(0) == description.charAt(description.length() - 1)) - { - workingDescription.remove(description.length() - 1, 1); - workingDescription.remove(0, 1); - } - else if (description.length() != 0) { - // throw new IllegalArgumentException("Illegal substitution syntax"); - status = U_PARSE_ERROR; - return; - } - - if (workingDescription.length() == 0) { - // if the description was just two paired token characters - // (i.e., "<<" or ">>"), it uses the rule set it belongs to to - // format its result - this->ruleSet = _ruleSet; - } - else if (workingDescription.charAt(0) == gPercent) { - // if the description contains a rule set name, that's the rule - // set we use to format the result: get a reference to the - // names rule set - this->ruleSet = _ruleSet->getOwner()->findRuleSet(workingDescription, status); - } - else if (workingDescription.charAt(0) == gPound || workingDescription.charAt(0) ==gZero) { - // if the description begins with 0 or #, treat it as a - // DecimalFormat pattern, and initialize a DecimalFormat with - // that pattern (then set it to use the DecimalFormatSymbols - // belonging to our formatter) - const DecimalFormatSymbols* sym = _ruleSet->getOwner()->getDecimalFormatSymbols(); - if (!sym) { - status = U_MISSING_RESOURCE_ERROR; - return; - } - DecimalFormat *tempNumberFormat = new DecimalFormat(workingDescription, *sym, status); - /* test for NULL */ - if (!tempNumberFormat) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (U_FAILURE(status)) { - delete tempNumberFormat; - return; - } - this->numberFormat = tempNumberFormat; - } - else if (workingDescription.charAt(0) == gGreaterThan) { - // if the description is ">>>", this substitution bypasses the - // usual rule-search process and always uses the rule that precedes - // it in its own rule set's rule list (this is used for place-value - // notations: formats where you want to see a particular part of - // a number even when it's 0) - - // this causes problems when >>> is used in a frationalPartSubstitution - // this->ruleSet = NULL; - this->ruleSet = _ruleSet; - this->numberFormat = NULL; - } - else { - // and of the description is none of these things, it's a syntax error - - // throw new IllegalArgumentException("Illegal substitution syntax"); - status = U_PARSE_ERROR; - } -} - -NFSubstitution::~NFSubstitution() -{ - delete numberFormat; - numberFormat = NULL; -} - -/** - * Set's the substitution's divisor. Used by NFRule.setBaseValue(). - * A no-op for all substitutions except multiplier and modulus - * substitutions. - * @param radix The radix of the divisor - * @param exponent The exponent of the divisor - */ -void -NFSubstitution::setDivisor(int32_t /*radix*/, int16_t /*exponent*/, UErrorCode& /*status*/) { - // a no-op for all substitutions except multiplier and modulus substitutions -} - -void -NFSubstitution::setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& /*status*/) { - if (numberFormat != NULL) { - numberFormat->setDecimalFormatSymbols(newSymbols); - } -} - -//----------------------------------------------------------------------- -// boilerplate -//----------------------------------------------------------------------- - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution) - -/** - * Compares two substitutions for equality - * @param The substitution to compare this one to - * @return true if the two substitutions are functionally equivalent - */ -bool -NFSubstitution::operator==(const NFSubstitution& rhs) const -{ - // compare class and all of the fields all substitutions have - // in common - // this should be called by subclasses before their own equality tests - return typeid(*this) == typeid(rhs) - && pos == rhs.pos - && (ruleSet == NULL) == (rhs.ruleSet == NULL) - // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead? - && (numberFormat == NULL - ? (rhs.numberFormat == NULL) - : (*numberFormat == *rhs.numberFormat)); -} - -/** - * Returns a textual description of the substitution - * @return A textual description of the substitution. This might - * not be identical to the description it was created from, but - * it'll produce the same result. - */ -void -NFSubstitution::toString(UnicodeString& text) const -{ - // use tokenChar() to get the character at the beginning and - // end of the substitutin token. In between them will go - // either the name of the rule set it uses, or the pattern of - // the DecimalFormat it uses - text.remove(); - text.append(tokenChar()); - - UnicodeString temp; - if (ruleSet != NULL) { - ruleSet->getName(temp); - } else if (numberFormat != NULL) { - numberFormat->toPattern(temp); - } - text.append(temp); - text.append(tokenChar()); -} - -//----------------------------------------------------------------------- -// formatting -//----------------------------------------------------------------------- - -/** - * Performs a mathematical operation on the number, formats it using - * either ruleSet or decimalFormat, and inserts the result into - * toInsertInto. - * @param number The number being formatted. - * @param toInsertInto The string we insert the result into - * @param pos The position in toInsertInto where the owning rule's - * rule text begins (this value is added to this substitution's - * position to determine exactly where to insert the new text) - */ -void -NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const -{ - if (ruleSet != NULL) { - // Perform a transformation on the number that is dependent - // on the type of substitution this is, then just call its - // rule set's format() method to format the result - ruleSet->format(transformNumber(number), toInsertInto, _pos + this->pos, recursionCount, status); - } else if (numberFormat != NULL) { - if (number <= MAX_INT64_IN_DOUBLE) { - // or perform the transformation on the number (preserving - // the result's fractional part if the formatter it set - // to show it), then use that formatter's format() method - // to format the result - double numberToFormat = transformNumber((double)number); - if (numberFormat->getMaximumFractionDigits() == 0) { - numberToFormat = uprv_floor(numberToFormat); - } - - UnicodeString temp; - numberFormat->format(numberToFormat, temp, status); - toInsertInto.insert(_pos + this->pos, temp); - } - else { - // We have gone beyond double precision. Something has to give. - // We're favoring accuracy of the large number over potential rules - // that round like a CompactDecimalFormat, which is not a common use case. - // - // Perform a transformation on the number that is dependent - // on the type of substitution this is, then just call its - // rule set's format() method to format the result - int64_t numberToFormat = transformNumber(number); - UnicodeString temp; - numberFormat->format(numberToFormat, temp, status); - toInsertInto.insert(_pos + this->pos, temp); - } - } -} - -/** - * Performs a mathematical operation on the number, formats it using - * either ruleSet or decimalFormat, and inserts the result into - * toInsertInto. - * @param number The number being formatted. - * @param toInsertInto The string we insert the result into - * @param pos The position in toInsertInto where the owning rule's - * rule text begins (this value is added to this substitution's - * position to determine exactly where to insert the new text) - */ -void -NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const { - // perform a transformation on the number being formatted that - // is dependent on the type of substitution this is - double numberToFormat = transformNumber(number); - - if (uprv_isInfinite(numberToFormat)) { - // This is probably a minus rule. Combine it with an infinite rule. - const NFRule *infiniteRule = ruleSet->findDoubleRule(uprv_getInfinity()); - infiniteRule->doFormat(numberToFormat, toInsertInto, _pos + this->pos, recursionCount, status); - return; - } - - // if the result is an integer, from here on out we work in integer - // space (saving time and memory and preserving accuracy) - if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != NULL) { - ruleSet->format(util64_fromDouble(numberToFormat), toInsertInto, _pos + this->pos, recursionCount, status); - - // if the result isn't an integer, then call either our rule set's - // format() method or our DecimalFormat's format() method to - // format the result - } else { - if (ruleSet != NULL) { - ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos, recursionCount, status); - } else if (numberFormat != NULL) { - UnicodeString temp; - numberFormat->format(numberToFormat, temp); - toInsertInto.insert(_pos + this->pos, temp); - } - } -} - - - //----------------------------------------------------------------------- - // parsing - //----------------------------------------------------------------------- - -#ifdef RBNF_DEBUG -#include -#endif - -/** - * Parses a string using the rule set or DecimalFormat belonging - * to this substitution. If there's a match, a mathematical - * operation (the inverse of the one used in formatting) is - * performed on the result of the parse and the value passed in - * and returned as the result. The parse position is updated to - * point to the first unmatched character in the string. - * @param text The string to parse - * @param parsePosition On entry, ignored, but assumed to be 0. - * On exit, this is updated to point to the first unmatched - * character (or 0 if the substitution didn't match) - * @param baseValue A partial parse result that should be - * combined with the result of this parse - * @param upperBound When searching the rule set for a rule - * matching the string passed in, only rules with base values - * lower than this are considered - * @param lenientParse If true and matching against rules fails, - * the substitution will also try matching the text against - * numerals using a default-costructed NumberFormat. If false, - * no extra work is done. (This value is false whenever the - * formatter isn't in lenient-parse mode, but is also false - * under some conditions even when the formatter _is_ in - * lenient-parse mode.) - * @return If there's a match, this is the result of composing - * baseValue with whatever was returned from matching the - * characters. This will be either a Long or a Double. If there's - * no match this is new Long(0) (not null), and parsePosition - * is left unchanged. - */ -UBool -NFSubstitution::doParse(const UnicodeString& text, - ParsePosition& parsePosition, - double baseValue, - double upperBound, - UBool lenientParse, - uint32_t nonNumericalExecutedRuleMask, - Formattable& result) const -{ -#ifdef RBNF_DEBUG - fprintf(stderr, " %x bv: %g ub: %g\n", this, baseValue, upperBound); -#endif - // figure out the highest base value a rule can have and match - // the text being parsed (this varies according to the type of - // substitutions: multiplier, modulus, and numerator substitutions - // restrict the search to rules with base values lower than their - // own; same-value substitutions leave the upper bound wherever - // it was, and the others allow any rule to match - upperBound = calcUpperBound(upperBound); - - // use our rule set to parse the text. If that fails and - // lenient parsing is enabled (this is always false if the - // formatter's lenient-parsing mode is off, but it may also - // be false even when the formatter's lenient-parse mode is - // on), then also try parsing the text using a default- - // constructed NumberFormat - if (ruleSet != NULL) { - ruleSet->parse(text, parsePosition, upperBound, nonNumericalExecutedRuleMask, result); - if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) { - UErrorCode status = U_ZERO_ERROR; - NumberFormat* fmt = NumberFormat::createInstance(status); - if (U_SUCCESS(status)) { - fmt->parse(text, result, parsePosition); - } - delete fmt; - } - - // ...or use our DecimalFormat to parse the text - } else if (numberFormat != NULL) { - numberFormat->parse(text, result, parsePosition); - } - - // if the parse was successful, we've already advanced the caller's - // parse position (this is the one function that doesn't have one - // of its own). Derive a parse result and return it as a Long, - // if possible, or a Double - if (parsePosition.getIndex() != 0) { - UErrorCode status = U_ZERO_ERROR; - double tempResult = result.getDouble(status); - - // composeRuleValue() produces a full parse result from - // the partial parse result passed to this function from - // the caller (this is either the owning rule's base value - // or the partial result obtained from composing the - // owning rule's base value with its other substitution's - // parse result) and the partial parse result obtained by - // matching the substitution (which will be the same value - // the caller would get by parsing just this part of the - // text with RuleBasedNumberFormat.parse() ). How the two - // values are used to derive the full parse result depends - // on the types of substitutions: For a regular rule, the - // ultimate result is its multiplier substitution's result - // times the rule's divisor (or the rule's base value) plus - // the modulus substitution's result (which will actually - // supersede part of the rule's base value). For a negative- - // number rule, the result is the negative of its substitution's - // result. For a fraction rule, it's the sum of its two - // substitution results. For a rule in a fraction rule set, - // it's the numerator substitution's result divided by - // the rule's base value. Results from same-value substitutions - // propagate back upard, and null substitutions don't affect - // the result. - tempResult = composeRuleValue(tempResult, baseValue); - result.setDouble(tempResult); - return true; - // if the parse was UNsuccessful, return 0 - } else { - result.setLong(0); - return false; - } -} - - /** - * Returns true if this is a modulus substitution. (We didn't do this - * with instanceof partially because it causes source files to - * proliferate and partially because we have to port this to C++.) - * @return true if this object is an instance of ModulusSubstitution - */ -UBool -NFSubstitution::isModulusSubstitution() const { - return false; -} - -//=================================================================== -// SameValueSubstitution -//=================================================================== - -/** - * A substitution that passes the value passed to it through unchanged. - * Represented by == in rule descriptions. - */ -SameValueSubstitution::SameValueSubstitution(int32_t _pos, - const NFRuleSet* _ruleSet, - const UnicodeString& description, - UErrorCode& status) -: NFSubstitution(_pos, _ruleSet, description, status) -{ - if (0 == description.compare(gEqualsEquals, 2)) { - // throw new IllegalArgumentException("== is not a legal token"); - status = U_PARSE_ERROR; - } -} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution) - -//=================================================================== -// MultiplierSubstitution -//=================================================================== - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution) - -bool MultiplierSubstitution::operator==(const NFSubstitution& rhs) const -{ - return NFSubstitution::operator==(rhs) && - divisor == ((const MultiplierSubstitution*)&rhs)->divisor; -} - - -//=================================================================== -// ModulusSubstitution -//=================================================================== - -/** - * A substitution that divides the number being formatted by the its rule's - * divisor and formats the remainder. Represented by ">>" in a - * regular rule. - */ -ModulusSubstitution::ModulusSubstitution(int32_t _pos, - const NFRule* rule, - const NFRule* predecessor, - const NFRuleSet* _ruleSet, - const UnicodeString& description, - UErrorCode& status) - : NFSubstitution(_pos, _ruleSet, description, status) - , divisor(rule->getDivisor()) - , ruleToUse(NULL) -{ - // the owning rule's divisor controls the behavior of this - // substitution: rather than keeping a backpointer to the rule, - // we keep a copy of the divisor - - if (divisor == 0) { - status = U_PARSE_ERROR; - } - - if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) { - // the >>> token doesn't alter how this substitution calculates the - // values it uses for formatting and parsing, but it changes - // what's done with that value after it's obtained: >>> short- - // circuits the rule-search process and goes straight to the - // specified rule to format the substitution value - ruleToUse = predecessor; - } -} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution) - -bool ModulusSubstitution::operator==(const NFSubstitution& rhs) const -{ - return NFSubstitution::operator==(rhs) && - divisor == ((const ModulusSubstitution*)&rhs)->divisor && - ruleToUse == ((const ModulusSubstitution*)&rhs)->ruleToUse; -} - -//----------------------------------------------------------------------- -// formatting -//----------------------------------------------------------------------- - - -/** - * If this is a >>> substitution, use ruleToUse to fill in - * the substitution. Otherwise, just use the superclass function. - * @param number The number being formatted - * @toInsertInto The string to insert the result of this substitution - * into - * @param pos The position of the rule text in toInsertInto - */ -void -ModulusSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const -{ - // if this isn't a >>> substitution, just use the inherited version - // of this function (which uses either a rule set or a DecimalFormat - // to format its substitution value) - if (ruleToUse == NULL) { - NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status); - - // a >>> substitution goes straight to a particular rule to - // format the substitution value - } else { - int64_t numberToFormat = transformNumber(number); - ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos(), recursionCount, status); - } -} - -/** -* If this is a >>> substitution, use ruleToUse to fill in -* the substitution. Otherwise, just use the superclass function. -* @param number The number being formatted -* @toInsertInto The string to insert the result of this substitution -* into -* @param pos The position of the rule text in toInsertInto -*/ -void -ModulusSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const -{ - // if this isn't a >>> substitution, just use the inherited version - // of this function (which uses either a rule set or a DecimalFormat - // to format its substitution value) - if (ruleToUse == NULL) { - NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status); - - // a >>> substitution goes straight to a particular rule to - // format the substitution value - } else { - double numberToFormat = transformNumber(number); - - ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos(), recursionCount, status); - } -} - -//----------------------------------------------------------------------- -// parsing -//----------------------------------------------------------------------- - -/** - * If this is a >>> substitution, match only against ruleToUse. - * Otherwise, use the superclass function. - * @param text The string to parse - * @param parsePosition Ignored on entry, updated on exit to point to - * the first unmatched character. - * @param baseValue The partial parse result prior to calling this - * routine. - */ -UBool -ModulusSubstitution::doParse(const UnicodeString& text, - ParsePosition& parsePosition, - double baseValue, - double upperBound, - UBool lenientParse, - uint32_t nonNumericalExecutedRuleMask, - Formattable& result) const -{ - // if this isn't a >>> substitution, we can just use the - // inherited parse() routine to do the parsing - if (ruleToUse == NULL) { - return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, nonNumericalExecutedRuleMask, result); - - // but if it IS a >>> substitution, we have to do it here: we - // use the specific rule's doParse() method, and then we have to - // do some of the other work of NFRuleSet.parse() - } else { - ruleToUse->doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask, result); - - if (parsePosition.getIndex() != 0) { - UErrorCode status = U_ZERO_ERROR; - double tempResult = result.getDouble(status); - tempResult = composeRuleValue(tempResult, baseValue); - result.setDouble(tempResult); - } - - return true; - } -} -/** - * Returns a textual description of the substitution - * @return A textual description of the substitution. This might - * not be identical to the description it was created from, but - * it'll produce the same result. - */ -void -ModulusSubstitution::toString(UnicodeString& text) const -{ - // use tokenChar() to get the character at the beginning and - // end of the substitutin token. In between them will go - // either the name of the rule set it uses, or the pattern of - // the DecimalFormat it uses - - if ( ruleToUse != NULL ) { // Must have been a >>> substitution. - text.remove(); - text.append(tokenChar()); - text.append(tokenChar()); - text.append(tokenChar()); - } else { // Otherwise just use the super-class function. - NFSubstitution::toString(text); - } -} -//=================================================================== -// IntegralPartSubstitution -//=================================================================== - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution) - - -//=================================================================== -// FractionalPartSubstitution -//=================================================================== - - - /** - * Constructs a FractionalPartSubstitution. This object keeps a flag - * telling whether it should format by digits or not. In addition, - * it marks the rule set it calls (if any) as a fraction rule set. - */ -FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos, - const NFRuleSet* _ruleSet, - const UnicodeString& description, - UErrorCode& status) - : NFSubstitution(_pos, _ruleSet, description, status) - , byDigits(false) - , useSpaces(true) - -{ - // akk, ruleSet can change in superclass constructor - if (0 == description.compare(gGreaterGreaterThan, 2) || - 0 == description.compare(gGreaterGreaterGreaterThan, 3) || - _ruleSet == getRuleSet()) { - byDigits = true; - if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) { - useSpaces = false; - } - } else { - // cast away const - ((NFRuleSet*)getRuleSet())->makeIntoFractionRuleSet(); - } -} - -//----------------------------------------------------------------------- -// formatting -//----------------------------------------------------------------------- - -/** - * If in "by digits" mode, fills in the substitution one decimal digit - * at a time using the rule set containing this substitution. - * Otherwise, uses the superclass function. - * @param number The number being formatted - * @param toInsertInto The string to insert the result of formatting - * the substitution into - * @param pos The position of the owning rule's rule text in - * toInsertInto - */ -void -FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, - int32_t _pos, int32_t recursionCount, UErrorCode& status) const -{ - // if we're not in "byDigits" mode, just use the inherited - // doSubstitution() routine - if (!byDigits) { - NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status); - - // if we're in "byDigits" mode, transform the value into an integer - // by moving the decimal point eight places to the right and - // pulling digits off the right one at a time, formatting each digit - // as an integer using this substitution's owning rule set - // (this is slower, but more accurate, than doing it from the - // other end) - } else { - // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits)); - // // this flag keeps us from formatting trailing zeros. It starts - // // out false because we're pulling from the right, and switches - // // to true the first time we encounter a non-zero digit - // UBool doZeros = false; - // for (int32_t i = 0; i < kMaxDecimalDigits; i++) { - // int64_t digit = numberToFormat % 10; - // if (digit != 0 || doZeros) { - // if (doZeros && useSpaces) { - // toInsertInto.insert(_pos + getPos(), gSpace); - // } - // doZeros = true; - // getRuleSet()->format(digit, toInsertInto, _pos + getPos()); - // } - // numberToFormat /= 10; - // } - - 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.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. - if (pad && useSpaces) { - toInsertInto.insert(_pos + getPos(), gSpace); - } else { - pad = true; - } - int64_t digit = dl.getDigit(didx); - getRuleSet()->format(digit, toInsertInto, _pos + getPos(), recursionCount, status); - } - - if (!pad) { - // hack around lack of precision in digitlist. if we would end up with - // "foo point" make sure we add a " zero" to the end. - getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos(), recursionCount, status); - } - } -} - -//----------------------------------------------------------------------- -// parsing -//----------------------------------------------------------------------- - -/** - * If in "by digits" mode, parses the string as if it were a string - * of individual digits; otherwise, uses the superclass function. - * @param text The string to parse - * @param parsePosition Ignored on entry, but updated on exit to point - * to the first unmatched character - * @param baseValue The partial parse result prior to entering this - * function - * @param upperBound Only consider rules with base values lower than - * this when filling in the substitution - * @param lenientParse If true, try matching the text as numerals if - * matching as words doesn't work - * @return If the match was successful, the current partial parse - * result; otherwise new Long(0). The result is either a Long or - * a Double. - */ - -UBool -FractionalPartSubstitution::doParse(const UnicodeString& text, - ParsePosition& parsePosition, - double baseValue, - double /*upperBound*/, - UBool lenientParse, - uint32_t nonNumericalExecutedRuleMask, - Formattable& resVal) const -{ - // if we're not in byDigits mode, we can just use the inherited - // doParse() - if (!byDigits) { - return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, nonNumericalExecutedRuleMask, resVal); - - // if we ARE in byDigits mode, parse the text one digit at a time - // using this substitution's owning rule set (we do this by setting - // upperBound to 10 when calling doParse() ) until we reach - // nonmatching text - } else { - UnicodeString workText(text); - ParsePosition workPos(1); - double result = 0; - int32_t digit; -// double p10 = 0.1; - - DecimalQuantity dl; - int32_t totalDigits = 0; - NumberFormat* fmt = NULL; - while (workText.length() > 0 && workPos.getIndex() != 0) { - workPos.setIndex(0); - Formattable temp; - getRuleSet()->parse(workText, workPos, 10, nonNumericalExecutedRuleMask, temp); - UErrorCode status = U_ZERO_ERROR; - digit = temp.getLong(status); -// digit = temp.getType() == Formattable::kLong ? -// temp.getLong() : -// (int32_t)temp.getDouble(); - - if (lenientParse && workPos.getIndex() == 0) { - if (!fmt) { - status = U_ZERO_ERROR; - fmt = NumberFormat::createInstance(status); - if (U_FAILURE(status)) { - delete fmt; - fmt = NULL; - } - } - if (fmt) { - fmt->parse(workText, temp, workPos); - digit = temp.getLong(status); - } - } - - if (workPos.getIndex() != 0) { - dl.appendDigit(static_cast(digit), 0, true); - totalDigits++; -// result += digit * p10; -// p10 /= 10; - parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); - workText.removeBetween(0, workPos.getIndex()); - while (workText.length() > 0 && workText.charAt(0) == gSpace) { - workText.removeBetween(0, 1); - parsePosition.setIndex(parsePosition.getIndex() + 1); - } - } - } - delete fmt; - - dl.adjustMagnitude(-totalDigits); - result = dl.toDouble(); - result = composeRuleValue(result, baseValue); - resVal.setDouble(result); - return true; - } -} - -bool -FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const -{ - return NFSubstitution::operator==(rhs) && - ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits; -} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution) - - -//=================================================================== -// AbsoluteValueSubstitution -//=================================================================== - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution) - -//=================================================================== -// NumeratorSubstitution -//=================================================================== - -void -NumeratorSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t apos, int32_t recursionCount, UErrorCode& status) const { - // perform a transformation on the number being formatted that - // is dependent on the type of substitution this is - - double numberToFormat = transformNumber(number); - int64_t longNF = util64_fromDouble(numberToFormat); - - const NFRuleSet* aruleSet = getRuleSet(); - if (withZeros && aruleSet != NULL) { - // if there are leading zeros in the decimal expansion then emit them - int64_t nf =longNF; - int32_t len = toInsertInto.length(); - while ((nf *= 10) < denominator) { - toInsertInto.insert(apos + getPos(), gSpace); - aruleSet->format((int64_t)0, toInsertInto, apos + getPos(), recursionCount, status); - } - apos += toInsertInto.length() - len; - } - - // if the result is an integer, from here on out we work in integer - // space (saving time and memory and preserving accuracy) - if (numberToFormat == longNF && aruleSet != NULL) { - aruleSet->format(longNF, toInsertInto, apos + getPos(), recursionCount, status); - - // if the result isn't an integer, then call either our rule set's - // format() method or our DecimalFormat's format() method to - // format the result - } else { - if (aruleSet != NULL) { - aruleSet->format(numberToFormat, toInsertInto, apos + getPos(), recursionCount, status); - } else { - UnicodeString temp; - getNumberFormat()->format(numberToFormat, temp, status); - toInsertInto.insert(apos + getPos(), temp); - } - } -} - -UBool -NumeratorSubstitution::doParse(const UnicodeString& text, - ParsePosition& parsePosition, - double baseValue, - double upperBound, - UBool /*lenientParse*/, - uint32_t nonNumericalExecutedRuleMask, - Formattable& result) const -{ - // we don't have to do anything special to do the parsing here, - // but we have to turn lenient parsing off-- if we leave it on, - // it SERIOUSLY messes up the algorithm - - // if withZeros is true, we need to count the zeros - // and use that to adjust the parse result - UErrorCode status = U_ZERO_ERROR; - int32_t zeroCount = 0; - UnicodeString workText(text); - - if (withZeros) { - ParsePosition workPos(1); - Formattable temp; - - while (workText.length() > 0 && workPos.getIndex() != 0) { - workPos.setIndex(0); - getRuleSet()->parse(workText, workPos, 1, nonNumericalExecutedRuleMask, temp); // parse zero or nothing at all - if (workPos.getIndex() == 0) { - // we failed, either there were no more zeros, or the number was formatted with digits - // either way, we're done - break; - } - - ++zeroCount; - parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); - workText.remove(0, workPos.getIndex()); - while (workText.length() > 0 && workText.charAt(0) == gSpace) { - workText.remove(0, 1); - parsePosition.setIndex(parsePosition.getIndex() + 1); - } - } - - workText = text; - workText.remove(0, (int32_t)parsePosition.getIndex()); - parsePosition.setIndex(0); - } - - // we've parsed off the zeros, now let's parse the rest from our current position - NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, false, nonNumericalExecutedRuleMask, result); - - if (withZeros) { - // any base value will do in this case. is there a way to - // force this to not bother trying all the base values? - - // compute the 'effective' base and prescale the value down - int64_t n = result.getLong(status); // force conversion! - int64_t d = 1; - while (d <= n) { - d *= 10; - } - // now add the zeros - while (zeroCount > 0) { - d *= 10; - --zeroCount; - } - // d is now our true denominator - result.setDouble((double)n/(double)d); - } - - return true; -} - -bool -NumeratorSubstitution::operator==(const NFSubstitution& rhs) const -{ - return NFSubstitution::operator==(rhs) && - denominator == ((const NumeratorSubstitution*)&rhs)->denominator; -} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution) - -const UChar NumeratorSubstitution::LTLT[] = { 0x003c, 0x003c }; - -U_NAMESPACE_END - -/* U_HAVE_RBNF */ -#endif - +// © 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 name: nfsubs.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* Modification history +* Date Name Comments +* 10/11/2001 Doug Ported from ICU4J +*/ + +#include +#include "utypeinfo.h" // for 'typeid' to work + +#include "nfsubs.h" +#include "fmtableimp.h" +#include "putilimp.h" +#include "number_decimalquantity.h" + +#if U_HAVE_RBNF + +static const char16_t gLessThan = 0x003c; +static const char16_t gEquals = 0x003d; +static const char16_t gGreaterThan = 0x003e; +static const char16_t gPercent = 0x0025; +static const char16_t gPound = 0x0023; +static const char16_t gZero = 0x0030; +static const char16_t gSpace = 0x0020; + +static const char16_t gEqualsEquals[] = +{ + 0x3D, 0x3D, 0 +}; /* "==" */ +static const char16_t gGreaterGreaterGreaterThan[] = +{ + 0x3E, 0x3E, 0x3E, 0 +}; /* ">>>" */ +static const char16_t gGreaterGreaterThan[] = +{ + 0x3E, 0x3E, 0 +}; /* ">>" */ + +U_NAMESPACE_BEGIN + +using number::impl::DecimalQuantity; + +class SameValueSubstitution : public NFSubstitution { +public: + SameValueSubstitution(int32_t pos, + const NFRuleSet* ruleset, + const UnicodeString& description, + UErrorCode& status); + virtual ~SameValueSubstitution(); + + virtual int64_t transformNumber(int64_t number) const override { return number; } + virtual double transformNumber(double number) const override { return number; } + virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const override { return newRuleValue; } + virtual double calcUpperBound(double oldUpperBound) const override { return oldUpperBound; } + virtual char16_t tokenChar() const override { return (char16_t)0x003d; } // '=' + +public: + static UClassID getStaticClassID(); + virtual UClassID getDynamicClassID() const override; +}; + +SameValueSubstitution::~SameValueSubstitution() {} + +class MultiplierSubstitution : public NFSubstitution { + int64_t divisor; + +public: + MultiplierSubstitution(int32_t _pos, + const NFRule *rule, + const NFRuleSet* _ruleSet, + const UnicodeString& description, + UErrorCode& status) + : NFSubstitution(_pos, _ruleSet, description, status), divisor(rule->getDivisor()) + { + if (divisor == 0) { + status = U_PARSE_ERROR; + } + } + virtual ~MultiplierSubstitution(); + + virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status) override { + divisor = util64_pow(radix, exponent); + + if(divisor == 0) { + status = U_PARSE_ERROR; + } + } + + virtual bool operator==(const NFSubstitution& rhs) const override; + + virtual int64_t transformNumber(int64_t number) const override { + return number / divisor; + } + + virtual double transformNumber(double number) const override { + if (getRuleSet()) { + return uprv_floor(number / divisor); + } else { + return number / divisor; + } + } + + virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const override { + return newRuleValue * divisor; + } + + virtual double calcUpperBound(double /*oldUpperBound*/) const override { return static_cast(divisor); } + + virtual char16_t tokenChar() const override { return (char16_t)0x003c; } // '<' + +public: + static UClassID getStaticClassID(); + virtual UClassID getDynamicClassID() const override; +}; + +MultiplierSubstitution::~MultiplierSubstitution() {} + +class ModulusSubstitution : public NFSubstitution { + int64_t divisor; + const NFRule* ruleToUse; +public: + ModulusSubstitution(int32_t pos, + const NFRule* rule, + const NFRule* rulePredecessor, + const NFRuleSet* ruleSet, + const UnicodeString& description, + UErrorCode& status); + virtual ~ModulusSubstitution(); + + virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status) override { + divisor = util64_pow(radix, exponent); + + if (divisor == 0) { + status = U_PARSE_ERROR; + } + } + + virtual bool operator==(const NFSubstitution& rhs) const override; + + virtual void doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override; + virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override; + + virtual int64_t transformNumber(int64_t number) const override { return number % divisor; } + virtual double transformNumber(double number) const override { return uprv_fmod(number, static_cast(divisor)); } + + virtual UBool doParse(const UnicodeString& text, + ParsePosition& parsePosition, + double baseValue, + double upperBound, + UBool lenientParse, + uint32_t nonNumericalExecutedRuleMask, + Formattable& result) const override; + + virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { + return oldRuleValue - uprv_fmod(oldRuleValue, static_cast(divisor)) + newRuleValue; + } + + virtual double calcUpperBound(double /*oldUpperBound*/) const override { return static_cast(divisor); } + + virtual UBool isModulusSubstitution() const override { return true; } + + virtual char16_t tokenChar() const override { return (char16_t)0x003e; } // '>' + + virtual void toString(UnicodeString& result) const override; + +public: + static UClassID getStaticClassID(); + virtual UClassID getDynamicClassID() const override; +}; + +ModulusSubstitution::~ModulusSubstitution() {} + +class IntegralPartSubstitution : public NFSubstitution { +public: + IntegralPartSubstitution(int32_t _pos, + const NFRuleSet* _ruleSet, + const UnicodeString& description, + UErrorCode& status) + : NFSubstitution(_pos, _ruleSet, description, status) {} + virtual ~IntegralPartSubstitution(); + + virtual int64_t transformNumber(int64_t number) const override { return number; } + virtual double transformNumber(double number) const override { return uprv_floor(number); } + virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue + oldRuleValue; } + virtual double calcUpperBound(double /*oldUpperBound*/) const override { return DBL_MAX; } + virtual char16_t tokenChar() const override { return (char16_t)0x003c; } // '<' + +public: + static UClassID getStaticClassID(); + virtual UClassID getDynamicClassID() const override; +}; + +IntegralPartSubstitution::~IntegralPartSubstitution() {} + +class FractionalPartSubstitution : public NFSubstitution { + UBool byDigits; + UBool useSpaces; + enum { kMaxDecimalDigits = 8 }; +public: + FractionalPartSubstitution(int32_t pos, + const NFRuleSet* ruleSet, + const UnicodeString& description, + UErrorCode& status); + virtual ~FractionalPartSubstitution(); + + virtual bool operator==(const NFSubstitution& rhs) const override; + + virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override; + virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const override {} + virtual int64_t transformNumber(int64_t /*number*/) const override { return 0; } + virtual double transformNumber(double number) const override { return number - uprv_floor(number); } + + virtual UBool doParse(const UnicodeString& text, + ParsePosition& parsePosition, + double baseValue, + double upperBound, + UBool lenientParse, + uint32_t nonNumericalExecutedRuleMask, + Formattable& result) const override; + + virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue + oldRuleValue; } + virtual double calcUpperBound(double /*oldUpperBound*/) const override { return 0.0; } + virtual char16_t tokenChar() const override { return (char16_t)0x003e; } // '>' + +public: + static UClassID getStaticClassID(); + virtual UClassID getDynamicClassID() const override; +}; + +FractionalPartSubstitution::~FractionalPartSubstitution() {} + +class AbsoluteValueSubstitution : public NFSubstitution { +public: + AbsoluteValueSubstitution(int32_t _pos, + const NFRuleSet* _ruleSet, + const UnicodeString& description, + UErrorCode& status) + : NFSubstitution(_pos, _ruleSet, description, status) {} + virtual ~AbsoluteValueSubstitution(); + + virtual int64_t transformNumber(int64_t number) const override { return number >= 0 ? number : -number; } + virtual double transformNumber(double number) const override { return uprv_fabs(number); } + virtual double composeRuleValue(double newRuleValue, double /*oldRuleValue*/) const override { return -newRuleValue; } + virtual double calcUpperBound(double /*oldUpperBound*/) const override { return DBL_MAX; } + virtual char16_t tokenChar() const override { return (char16_t)0x003e; } // '>' + +public: + static UClassID getStaticClassID(); + virtual UClassID getDynamicClassID() const override; +}; + +AbsoluteValueSubstitution::~AbsoluteValueSubstitution() {} + +class NumeratorSubstitution : public NFSubstitution { + double denominator; + int64_t ldenominator; + UBool withZeros; +public: + static inline UnicodeString fixdesc(const UnicodeString& desc) { + if (desc.endsWith(LTLT, 2)) { + UnicodeString result(desc, 0, desc.length()-1); + return result; + } + return desc; + } + NumeratorSubstitution(int32_t _pos, + double _denominator, + NFRuleSet* _ruleSet, + const UnicodeString& description, + UErrorCode& status) + : NFSubstitution(_pos, _ruleSet, fixdesc(description), status), denominator(_denominator) + { + ldenominator = util64_fromDouble(denominator); + withZeros = description.endsWith(LTLT, 2); + } + virtual ~NumeratorSubstitution(); + + virtual bool operator==(const NFSubstitution& rhs) const override; + + virtual int64_t transformNumber(int64_t number) const override { return number * ldenominator; } + virtual double transformNumber(double number) const override { return uprv_round(number * denominator); } + + virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const override {} + virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const override; + virtual UBool doParse(const UnicodeString& text, + ParsePosition& parsePosition, + double baseValue, + double upperBound, + UBool /*lenientParse*/, + uint32_t nonNumericalExecutedRuleMask, + Formattable& result) const override; + + virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const override { return newRuleValue / oldRuleValue; } + virtual double calcUpperBound(double /*oldUpperBound*/) const override { return denominator; } + virtual char16_t tokenChar() const override { return (char16_t)0x003c; } // '<' +private: + static const char16_t LTLT[2]; + +public: + static UClassID getStaticClassID(); + virtual UClassID getDynamicClassID() const override; +}; + +NumeratorSubstitution::~NumeratorSubstitution() {} + +NFSubstitution* +NFSubstitution::makeSubstitution(int32_t pos, + const NFRule* rule, + const NFRule* predecessor, + const NFRuleSet* ruleSet, + const RuleBasedNumberFormat* formatter, + const UnicodeString& description, + UErrorCode& status) +{ + // if the description is empty, return a NullSubstitution + if (description.length() == 0) { + return nullptr; + } + + switch (description.charAt(0)) { + // if the description begins with '<'... + case gLessThan: + // throw an exception if the rule is a negative number + // rule + if (rule->getBaseValue() == NFRule::kNegativeNumberRule) { + // throw new IllegalArgumentException("<< not allowed in negative-number rule"); + status = U_PARSE_ERROR; + return nullptr; + } + + // if the rule is a fraction rule, return an + // IntegralPartSubstitution + else if (rule->getBaseValue() == NFRule::kImproperFractionRule + || rule->getBaseValue() == NFRule::kProperFractionRule + || rule->getBaseValue() == NFRule::kDefaultRule) { + return new IntegralPartSubstitution(pos, ruleSet, description, status); + } + + // if the rule set containing the rule is a fraction + // rule set, return a NumeratorSubstitution + else if (ruleSet->isFractionRuleSet()) { + return new NumeratorSubstitution(pos, (double)rule->getBaseValue(), + formatter->getDefaultRuleSet(), description, status); + } + + // otherwise, return a MultiplierSubstitution + else { + return new MultiplierSubstitution(pos, rule, ruleSet, + description, status); + } + + // if the description begins with '>'... + case gGreaterThan: + // if the rule is a negative-number rule, return + // an AbsoluteValueSubstitution + if (rule->getBaseValue() == NFRule::kNegativeNumberRule) { + return new AbsoluteValueSubstitution(pos, ruleSet, description, status); + } + + // if the rule is a fraction rule, return a + // FractionalPartSubstitution + else if (rule->getBaseValue() == NFRule::kImproperFractionRule + || rule->getBaseValue() == NFRule::kProperFractionRule + || rule->getBaseValue() == NFRule::kDefaultRule) { + return new FractionalPartSubstitution(pos, ruleSet, description, status); + } + + // if the rule set owning the rule is a fraction rule set, + // throw an exception + else if (ruleSet->isFractionRuleSet()) { + // throw new IllegalArgumentException(">> not allowed in fraction rule set"); + status = U_PARSE_ERROR; + return nullptr; + } + + // otherwise, return a ModulusSubstitution + else { + return new ModulusSubstitution(pos, rule, predecessor, + ruleSet, description, status); + } + + // if the description begins with '=', always return a + // SameValueSubstitution + case gEquals: + return new SameValueSubstitution(pos, ruleSet, description, status); + + // and if it's anything else, throw an exception + default: + // throw new IllegalArgumentException("Illegal substitution character"); + status = U_PARSE_ERROR; + } + return nullptr; +} + +NFSubstitution::NFSubstitution(int32_t _pos, + const NFRuleSet* _ruleSet, + const UnicodeString& description, + UErrorCode& status) + : pos(_pos), ruleSet(nullptr), numberFormat(nullptr) +{ + // the description should begin and end with the same character. + // If it doesn't that's a syntax error. Otherwise, + // makeSubstitution() was the only thing that needed to know + // about these characters, so strip them off + UnicodeString workingDescription(description); + if (description.length() >= 2 + && description.charAt(0) == description.charAt(description.length() - 1)) + { + workingDescription.remove(description.length() - 1, 1); + workingDescription.remove(0, 1); + } + else if (description.length() != 0) { + // throw new IllegalArgumentException("Illegal substitution syntax"); + status = U_PARSE_ERROR; + return; + } + + if (workingDescription.length() == 0) { + // if the description was just two paired token characters + // (i.e., "<<" or ">>"), it uses the rule set it belongs to to + // format its result + this->ruleSet = _ruleSet; + } + else if (workingDescription.charAt(0) == gPercent) { + // if the description contains a rule set name, that's the rule + // set we use to format the result: get a reference to the + // names rule set + this->ruleSet = _ruleSet->getOwner()->findRuleSet(workingDescription, status); + } + else if (workingDescription.charAt(0) == gPound || workingDescription.charAt(0) ==gZero) { + // if the description begins with 0 or #, treat it as a + // DecimalFormat pattern, and initialize a DecimalFormat with + // that pattern (then set it to use the DecimalFormatSymbols + // belonging to our formatter) + const DecimalFormatSymbols* sym = _ruleSet->getOwner()->getDecimalFormatSymbols(); + if (!sym) { + status = U_MISSING_RESOURCE_ERROR; + return; + } + DecimalFormat *tempNumberFormat = new DecimalFormat(workingDescription, *sym, status); + /* test for nullptr */ + if (!tempNumberFormat) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (U_FAILURE(status)) { + delete tempNumberFormat; + return; + } + this->numberFormat = tempNumberFormat; + } + else if (workingDescription.charAt(0) == gGreaterThan) { + // if the description is ">>>", this substitution bypasses the + // usual rule-search process and always uses the rule that precedes + // it in its own rule set's rule list (this is used for place-value + // notations: formats where you want to see a particular part of + // a number even when it's 0) + + // this causes problems when >>> is used in a frationalPartSubstitution + // this->ruleSet = nullptr; + this->ruleSet = _ruleSet; + this->numberFormat = nullptr; + } + else { + // and of the description is none of these things, it's a syntax error + + // throw new IllegalArgumentException("Illegal substitution syntax"); + status = U_PARSE_ERROR; + } +} + +NFSubstitution::~NFSubstitution() +{ + delete numberFormat; + numberFormat = nullptr; +} + +/** + * Set's the substitution's divisor. Used by NFRule.setBaseValue(). + * A no-op for all substitutions except multiplier and modulus + * substitutions. + * @param radix The radix of the divisor + * @param exponent The exponent of the divisor + */ +void +NFSubstitution::setDivisor(int32_t /*radix*/, int16_t /*exponent*/, UErrorCode& /*status*/) { + // a no-op for all substitutions except multiplier and modulus substitutions +} + +void +NFSubstitution::setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& /*status*/) { + if (numberFormat != nullptr) { + numberFormat->setDecimalFormatSymbols(newSymbols); + } +} + +//----------------------------------------------------------------------- +// boilerplate +//----------------------------------------------------------------------- + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NFSubstitution) + +/** + * Compares two substitutions for equality + * @param The substitution to compare this one to + * @return true if the two substitutions are functionally equivalent + */ +bool +NFSubstitution::operator==(const NFSubstitution& rhs) const +{ + // compare class and all of the fields all substitutions have + // in common + // this should be called by subclasses before their own equality tests + return typeid(*this) == typeid(rhs) + && pos == rhs.pos + && (ruleSet == nullptr) == (rhs.ruleSet == nullptr) + // && ruleSet == rhs.ruleSet causes circularity, other checks to make instead? + && (numberFormat == nullptr + ? (rhs.numberFormat == nullptr) + : (*numberFormat == *rhs.numberFormat)); +} + +/** + * Returns a textual description of the substitution + * @return A textual description of the substitution. This might + * not be identical to the description it was created from, but + * it'll produce the same result. + */ +void +NFSubstitution::toString(UnicodeString& text) const +{ + // use tokenChar() to get the character at the beginning and + // end of the substitutin token. In between them will go + // either the name of the rule set it uses, or the pattern of + // the DecimalFormat it uses + text.remove(); + text.append(tokenChar()); + + UnicodeString temp; + if (ruleSet != nullptr) { + ruleSet->getName(temp); + } else if (numberFormat != nullptr) { + numberFormat->toPattern(temp); + } + text.append(temp); + text.append(tokenChar()); +} + +//----------------------------------------------------------------------- +// formatting +//----------------------------------------------------------------------- + +/** + * Performs a mathematical operation on the number, formats it using + * either ruleSet or decimalFormat, and inserts the result into + * toInsertInto. + * @param number The number being formatted. + * @param toInsertInto The string we insert the result into + * @param pos The position in toInsertInto where the owning rule's + * rule text begins (this value is added to this substitution's + * position to determine exactly where to insert the new text) + */ +void +NFSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const +{ + if (ruleSet != nullptr) { + // Perform a transformation on the number that is dependent + // on the type of substitution this is, then just call its + // rule set's format() method to format the result + ruleSet->format(transformNumber(number), toInsertInto, _pos + this->pos, recursionCount, status); + } else if (numberFormat != nullptr) { + if (number <= MAX_INT64_IN_DOUBLE) { + // or perform the transformation on the number (preserving + // the result's fractional part if the formatter it set + // to show it), then use that formatter's format() method + // to format the result + double numberToFormat = transformNumber((double)number); + if (numberFormat->getMaximumFractionDigits() == 0) { + numberToFormat = uprv_floor(numberToFormat); + } + + UnicodeString temp; + numberFormat->format(numberToFormat, temp, status); + toInsertInto.insert(_pos + this->pos, temp); + } + else { + // We have gone beyond double precision. Something has to give. + // We're favoring accuracy of the large number over potential rules + // that round like a CompactDecimalFormat, which is not a common use case. + // + // Perform a transformation on the number that is dependent + // on the type of substitution this is, then just call its + // rule set's format() method to format the result + int64_t numberToFormat = transformNumber(number); + UnicodeString temp; + numberFormat->format(numberToFormat, temp, status); + toInsertInto.insert(_pos + this->pos, temp); + } + } +} + +/** + * Performs a mathematical operation on the number, formats it using + * either ruleSet or decimalFormat, and inserts the result into + * toInsertInto. + * @param number The number being formatted. + * @param toInsertInto The string we insert the result into + * @param pos The position in toInsertInto where the owning rule's + * rule text begins (this value is added to this substitution's + * position to determine exactly where to insert the new text) + */ +void +NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const { + // perform a transformation on the number being formatted that + // is dependent on the type of substitution this is + double numberToFormat = transformNumber(number); + + if (uprv_isInfinite(numberToFormat)) { + // This is probably a minus rule. Combine it with an infinite rule. + const NFRule *infiniteRule = ruleSet->findDoubleRule(uprv_getInfinity()); + infiniteRule->doFormat(numberToFormat, toInsertInto, _pos + this->pos, recursionCount, status); + return; + } + + // if the result is an integer, from here on out we work in integer + // space (saving time and memory and preserving accuracy) + if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != nullptr) { + ruleSet->format(util64_fromDouble(numberToFormat), toInsertInto, _pos + this->pos, recursionCount, status); + + // if the result isn't an integer, then call either our rule set's + // format() method or our DecimalFormat's format() method to + // format the result + } else { + if (ruleSet != nullptr) { + ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos, recursionCount, status); + } else if (numberFormat != nullptr) { + UnicodeString temp; + numberFormat->format(numberToFormat, temp); + toInsertInto.insert(_pos + this->pos, temp); + } + } +} + + + //----------------------------------------------------------------------- + // parsing + //----------------------------------------------------------------------- + +#ifdef RBNF_DEBUG +#include +#endif + +/** + * Parses a string using the rule set or DecimalFormat belonging + * to this substitution. If there's a match, a mathematical + * operation (the inverse of the one used in formatting) is + * performed on the result of the parse and the value passed in + * and returned as the result. The parse position is updated to + * point to the first unmatched character in the string. + * @param text The string to parse + * @param parsePosition On entry, ignored, but assumed to be 0. + * On exit, this is updated to point to the first unmatched + * character (or 0 if the substitution didn't match) + * @param baseValue A partial parse result that should be + * combined with the result of this parse + * @param upperBound When searching the rule set for a rule + * matching the string passed in, only rules with base values + * lower than this are considered + * @param lenientParse If true and matching against rules fails, + * the substitution will also try matching the text against + * numerals using a default-costructed NumberFormat. If false, + * no extra work is done. (This value is false whenever the + * formatter isn't in lenient-parse mode, but is also false + * under some conditions even when the formatter _is_ in + * lenient-parse mode.) + * @return If there's a match, this is the result of composing + * baseValue with whatever was returned from matching the + * characters. This will be either a Long or a Double. If there's + * no match this is new Long(0) (not null), and parsePosition + * is left unchanged. + */ +UBool +NFSubstitution::doParse(const UnicodeString& text, + ParsePosition& parsePosition, + double baseValue, + double upperBound, + UBool lenientParse, + uint32_t nonNumericalExecutedRuleMask, + Formattable& result) const +{ +#ifdef RBNF_DEBUG + fprintf(stderr, " %x bv: %g ub: %g\n", this, baseValue, upperBound); +#endif + // figure out the highest base value a rule can have and match + // the text being parsed (this varies according to the type of + // substitutions: multiplier, modulus, and numerator substitutions + // restrict the search to rules with base values lower than their + // own; same-value substitutions leave the upper bound wherever + // it was, and the others allow any rule to match + upperBound = calcUpperBound(upperBound); + + // use our rule set to parse the text. If that fails and + // lenient parsing is enabled (this is always false if the + // formatter's lenient-parsing mode is off, but it may also + // be false even when the formatter's lenient-parse mode is + // on), then also try parsing the text using a default- + // constructed NumberFormat + if (ruleSet != nullptr) { + ruleSet->parse(text, parsePosition, upperBound, nonNumericalExecutedRuleMask, result); + if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) { + UErrorCode status = U_ZERO_ERROR; + NumberFormat* fmt = NumberFormat::createInstance(status); + if (U_SUCCESS(status)) { + fmt->parse(text, result, parsePosition); + } + delete fmt; + } + + // ...or use our DecimalFormat to parse the text + } else if (numberFormat != nullptr) { + numberFormat->parse(text, result, parsePosition); + } + + // if the parse was successful, we've already advanced the caller's + // parse position (this is the one function that doesn't have one + // of its own). Derive a parse result and return it as a Long, + // if possible, or a Double + if (parsePosition.getIndex() != 0) { + UErrorCode status = U_ZERO_ERROR; + double tempResult = result.getDouble(status); + + // composeRuleValue() produces a full parse result from + // the partial parse result passed to this function from + // the caller (this is either the owning rule's base value + // or the partial result obtained from composing the + // owning rule's base value with its other substitution's + // parse result) and the partial parse result obtained by + // matching the substitution (which will be the same value + // the caller would get by parsing just this part of the + // text with RuleBasedNumberFormat.parse() ). How the two + // values are used to derive the full parse result depends + // on the types of substitutions: For a regular rule, the + // ultimate result is its multiplier substitution's result + // times the rule's divisor (or the rule's base value) plus + // the modulus substitution's result (which will actually + // supersede part of the rule's base value). For a negative- + // number rule, the result is the negative of its substitution's + // result. For a fraction rule, it's the sum of its two + // substitution results. For a rule in a fraction rule set, + // it's the numerator substitution's result divided by + // the rule's base value. Results from same-value substitutions + // propagate back upard, and null substitutions don't affect + // the result. + tempResult = composeRuleValue(tempResult, baseValue); + result.setDouble(tempResult); + return true; + // if the parse was UNsuccessful, return 0 + } else { + result.setLong(0); + return false; + } +} + + /** + * Returns true if this is a modulus substitution. (We didn't do this + * with instanceof partially because it causes source files to + * proliferate and partially because we have to port this to C++.) + * @return true if this object is an instance of ModulusSubstitution + */ +UBool +NFSubstitution::isModulusSubstitution() const { + return false; +} + +//=================================================================== +// SameValueSubstitution +//=================================================================== + +/** + * A substitution that passes the value passed to it through unchanged. + * Represented by == in rule descriptions. + */ +SameValueSubstitution::SameValueSubstitution(int32_t _pos, + const NFRuleSet* _ruleSet, + const UnicodeString& description, + UErrorCode& status) +: NFSubstitution(_pos, _ruleSet, description, status) +{ + if (0 == description.compare(gEqualsEquals, 2)) { + // throw new IllegalArgumentException("== is not a legal token"); + status = U_PARSE_ERROR; + } +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SameValueSubstitution) + +//=================================================================== +// MultiplierSubstitution +//=================================================================== + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MultiplierSubstitution) + +bool MultiplierSubstitution::operator==(const NFSubstitution& rhs) const +{ + return NFSubstitution::operator==(rhs) && + divisor == ((const MultiplierSubstitution*)&rhs)->divisor; +} + + +//=================================================================== +// ModulusSubstitution +//=================================================================== + +/** + * A substitution that divides the number being formatted by the its rule's + * divisor and formats the remainder. Represented by ">>" in a + * regular rule. + */ +ModulusSubstitution::ModulusSubstitution(int32_t _pos, + const NFRule* rule, + const NFRule* predecessor, + const NFRuleSet* _ruleSet, + const UnicodeString& description, + UErrorCode& status) + : NFSubstitution(_pos, _ruleSet, description, status) + , divisor(rule->getDivisor()) + , ruleToUse(nullptr) +{ + // the owning rule's divisor controls the behavior of this + // substitution: rather than keeping a backpointer to the rule, + // we keep a copy of the divisor + + if (divisor == 0) { + status = U_PARSE_ERROR; + } + + if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) { + // the >>> token doesn't alter how this substitution calculates the + // values it uses for formatting and parsing, but it changes + // what's done with that value after it's obtained: >>> short- + // circuits the rule-search process and goes straight to the + // specified rule to format the substitution value + ruleToUse = predecessor; + } +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ModulusSubstitution) + +bool ModulusSubstitution::operator==(const NFSubstitution& rhs) const +{ + return NFSubstitution::operator==(rhs) && + divisor == ((const ModulusSubstitution*)&rhs)->divisor && + ruleToUse == ((const ModulusSubstitution*)&rhs)->ruleToUse; +} + +//----------------------------------------------------------------------- +// formatting +//----------------------------------------------------------------------- + + +/** + * If this is a >>> substitution, use ruleToUse to fill in + * the substitution. Otherwise, just use the superclass function. + * @param number The number being formatted + * @toInsertInto The string to insert the result of this substitution + * into + * @param pos The position of the rule text in toInsertInto + */ +void +ModulusSubstitution::doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const +{ + // if this isn't a >>> substitution, just use the inherited version + // of this function (which uses either a rule set or a DecimalFormat + // to format its substitution value) + if (ruleToUse == nullptr) { + NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status); + + // a >>> substitution goes straight to a particular rule to + // format the substitution value + } else { + int64_t numberToFormat = transformNumber(number); + ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos(), recursionCount, status); + } +} + +/** +* If this is a >>> substitution, use ruleToUse to fill in +* the substitution. Otherwise, just use the superclass function. +* @param number The number being formatted +* @toInsertInto The string to insert the result of this substitution +* into +* @param pos The position of the rule text in toInsertInto +*/ +void +ModulusSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos, int32_t recursionCount, UErrorCode& status) const +{ + // if this isn't a >>> substitution, just use the inherited version + // of this function (which uses either a rule set or a DecimalFormat + // to format its substitution value) + if (ruleToUse == nullptr) { + NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status); + + // a >>> substitution goes straight to a particular rule to + // format the substitution value + } else { + double numberToFormat = transformNumber(number); + + ruleToUse->doFormat(numberToFormat, toInsertInto, _pos + getPos(), recursionCount, status); + } +} + +//----------------------------------------------------------------------- +// parsing +//----------------------------------------------------------------------- + +/** + * If this is a >>> substitution, match only against ruleToUse. + * Otherwise, use the superclass function. + * @param text The string to parse + * @param parsePosition Ignored on entry, updated on exit to point to + * the first unmatched character. + * @param baseValue The partial parse result prior to calling this + * routine. + */ +UBool +ModulusSubstitution::doParse(const UnicodeString& text, + ParsePosition& parsePosition, + double baseValue, + double upperBound, + UBool lenientParse, + uint32_t nonNumericalExecutedRuleMask, + Formattable& result) const +{ + // if this isn't a >>> substitution, we can just use the + // inherited parse() routine to do the parsing + if (ruleToUse == nullptr) { + return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, nonNumericalExecutedRuleMask, result); + + // but if it IS a >>> substitution, we have to do it here: we + // use the specific rule's doParse() method, and then we have to + // do some of the other work of NFRuleSet.parse() + } else { + ruleToUse->doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask, result); + + if (parsePosition.getIndex() != 0) { + UErrorCode status = U_ZERO_ERROR; + double tempResult = result.getDouble(status); + tempResult = composeRuleValue(tempResult, baseValue); + result.setDouble(tempResult); + } + + return true; + } +} +/** + * Returns a textual description of the substitution + * @return A textual description of the substitution. This might + * not be identical to the description it was created from, but + * it'll produce the same result. + */ +void +ModulusSubstitution::toString(UnicodeString& text) const +{ + // use tokenChar() to get the character at the beginning and + // end of the substitutin token. In between them will go + // either the name of the rule set it uses, or the pattern of + // the DecimalFormat it uses + + if ( ruleToUse != nullptr ) { // Must have been a >>> substitution. + text.remove(); + text.append(tokenChar()); + text.append(tokenChar()); + text.append(tokenChar()); + } else { // Otherwise just use the super-class function. + NFSubstitution::toString(text); + } +} +//=================================================================== +// IntegralPartSubstitution +//=================================================================== + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution) + + +//=================================================================== +// FractionalPartSubstitution +//=================================================================== + + + /** + * Constructs a FractionalPartSubstitution. This object keeps a flag + * telling whether it should format by digits or not. In addition, + * it marks the rule set it calls (if any) as a fraction rule set. + */ +FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos, + const NFRuleSet* _ruleSet, + const UnicodeString& description, + UErrorCode& status) + : NFSubstitution(_pos, _ruleSet, description, status) + , byDigits(false) + , useSpaces(true) + +{ + // akk, ruleSet can change in superclass constructor + if (0 == description.compare(gGreaterGreaterThan, 2) || + 0 == description.compare(gGreaterGreaterGreaterThan, 3) || + _ruleSet == getRuleSet()) { + byDigits = true; + if (0 == description.compare(gGreaterGreaterGreaterThan, 3)) { + useSpaces = false; + } + } else { + // cast away const + ((NFRuleSet*)getRuleSet())->makeIntoFractionRuleSet(); + } +} + +//----------------------------------------------------------------------- +// formatting +//----------------------------------------------------------------------- + +/** + * If in "by digits" mode, fills in the substitution one decimal digit + * at a time using the rule set containing this substitution. + * Otherwise, uses the superclass function. + * @param number The number being formatted + * @param toInsertInto The string to insert the result of formatting + * the substitution into + * @param pos The position of the owning rule's rule text in + * toInsertInto + */ +void +FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, + int32_t _pos, int32_t recursionCount, UErrorCode& status) const +{ + // if we're not in "byDigits" mode, just use the inherited + // doSubstitution() routine + if (!byDigits) { + NFSubstitution::doSubstitution(number, toInsertInto, _pos, recursionCount, status); + + // if we're in "byDigits" mode, transform the value into an integer + // by moving the decimal point eight places to the right and + // pulling digits off the right one at a time, formatting each digit + // as an integer using this substitution's owning rule set + // (this is slower, but more accurate, than doing it from the + // other end) + } else { + // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits)); + // // this flag keeps us from formatting trailing zeros. It starts + // // out false because we're pulling from the right, and switches + // // to true the first time we encounter a non-zero digit + // UBool doZeros = false; + // for (int32_t i = 0; i < kMaxDecimalDigits; i++) { + // int64_t digit = numberToFormat % 10; + // if (digit != 0 || doZeros) { + // if (doZeros && useSpaces) { + // toInsertInto.insert(_pos + getPos(), gSpace); + // } + // doZeros = true; + // getRuleSet()->format(digit, toInsertInto, _pos + getPos()); + // } + // numberToFormat /= 10; + // } + + 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.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. + if (pad && useSpaces) { + toInsertInto.insert(_pos + getPos(), gSpace); + } else { + pad = true; + } + int64_t digit = dl.getDigit(didx); + getRuleSet()->format(digit, toInsertInto, _pos + getPos(), recursionCount, status); + } + + if (!pad) { + // hack around lack of precision in digitlist. if we would end up with + // "foo point" make sure we add a " zero" to the end. + getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos(), recursionCount, status); + } + } +} + +//----------------------------------------------------------------------- +// parsing +//----------------------------------------------------------------------- + +/** + * If in "by digits" mode, parses the string as if it were a string + * of individual digits; otherwise, uses the superclass function. + * @param text The string to parse + * @param parsePosition Ignored on entry, but updated on exit to point + * to the first unmatched character + * @param baseValue The partial parse result prior to entering this + * function + * @param upperBound Only consider rules with base values lower than + * this when filling in the substitution + * @param lenientParse If true, try matching the text as numerals if + * matching as words doesn't work + * @return If the match was successful, the current partial parse + * result; otherwise new Long(0). The result is either a Long or + * a Double. + */ + +UBool +FractionalPartSubstitution::doParse(const UnicodeString& text, + ParsePosition& parsePosition, + double baseValue, + double /*upperBound*/, + UBool lenientParse, + uint32_t nonNumericalExecutedRuleMask, + Formattable& resVal) const +{ + // if we're not in byDigits mode, we can just use the inherited + // doParse() + if (!byDigits) { + return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, nonNumericalExecutedRuleMask, resVal); + + // if we ARE in byDigits mode, parse the text one digit at a time + // using this substitution's owning rule set (we do this by setting + // upperBound to 10 when calling doParse() ) until we reach + // nonmatching text + } else { + UnicodeString workText(text); + ParsePosition workPos(1); + double result = 0; + int32_t digit; +// double p10 = 0.1; + + DecimalQuantity dl; + int32_t totalDigits = 0; + NumberFormat* fmt = nullptr; + while (workText.length() > 0 && workPos.getIndex() != 0) { + workPos.setIndex(0); + Formattable temp; + getRuleSet()->parse(workText, workPos, 10, nonNumericalExecutedRuleMask, temp); + UErrorCode status = U_ZERO_ERROR; + digit = temp.getLong(status); +// digit = temp.getType() == Formattable::kLong ? +// temp.getLong() : +// (int32_t)temp.getDouble(); + + if (lenientParse && workPos.getIndex() == 0) { + if (!fmt) { + status = U_ZERO_ERROR; + fmt = NumberFormat::createInstance(status); + if (U_FAILURE(status)) { + delete fmt; + fmt = nullptr; + } + } + if (fmt) { + fmt->parse(workText, temp, workPos); + digit = temp.getLong(status); + } + } + + if (workPos.getIndex() != 0) { + dl.appendDigit(static_cast(digit), 0, true); + totalDigits++; +// result += digit * p10; +// p10 /= 10; + parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); + workText.removeBetween(0, workPos.getIndex()); + while (workText.length() > 0 && workText.charAt(0) == gSpace) { + workText.removeBetween(0, 1); + parsePosition.setIndex(parsePosition.getIndex() + 1); + } + } + } + delete fmt; + + dl.adjustMagnitude(-totalDigits); + result = dl.toDouble(); + result = composeRuleValue(result, baseValue); + resVal.setDouble(result); + return true; + } +} + +bool +FractionalPartSubstitution::operator==(const NFSubstitution& rhs) const +{ + return NFSubstitution::operator==(rhs) && + ((const FractionalPartSubstitution*)&rhs)->byDigits == byDigits; +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FractionalPartSubstitution) + + +//=================================================================== +// AbsoluteValueSubstitution +//=================================================================== + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AbsoluteValueSubstitution) + +//=================================================================== +// NumeratorSubstitution +//=================================================================== + +void +NumeratorSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t apos, int32_t recursionCount, UErrorCode& status) const { + // perform a transformation on the number being formatted that + // is dependent on the type of substitution this is + + double numberToFormat = transformNumber(number); + int64_t longNF = util64_fromDouble(numberToFormat); + + const NFRuleSet* aruleSet = getRuleSet(); + if (withZeros && aruleSet != nullptr) { + // if there are leading zeros in the decimal expansion then emit them + int64_t nf =longNF; + int32_t len = toInsertInto.length(); + while ((nf *= 10) < denominator) { + toInsertInto.insert(apos + getPos(), gSpace); + aruleSet->format((int64_t)0, toInsertInto, apos + getPos(), recursionCount, status); + } + apos += toInsertInto.length() - len; + } + + // if the result is an integer, from here on out we work in integer + // space (saving time and memory and preserving accuracy) + if (numberToFormat == longNF && aruleSet != nullptr) { + aruleSet->format(longNF, toInsertInto, apos + getPos(), recursionCount, status); + + // if the result isn't an integer, then call either our rule set's + // format() method or our DecimalFormat's format() method to + // format the result + } else { + if (aruleSet != nullptr) { + aruleSet->format(numberToFormat, toInsertInto, apos + getPos(), recursionCount, status); + } else { + UnicodeString temp; + getNumberFormat()->format(numberToFormat, temp, status); + toInsertInto.insert(apos + getPos(), temp); + } + } +} + +UBool +NumeratorSubstitution::doParse(const UnicodeString& text, + ParsePosition& parsePosition, + double baseValue, + double upperBound, + UBool /*lenientParse*/, + uint32_t nonNumericalExecutedRuleMask, + Formattable& result) const +{ + // we don't have to do anything special to do the parsing here, + // but we have to turn lenient parsing off-- if we leave it on, + // it SERIOUSLY messes up the algorithm + + // if withZeros is true, we need to count the zeros + // and use that to adjust the parse result + UErrorCode status = U_ZERO_ERROR; + int32_t zeroCount = 0; + UnicodeString workText(text); + + if (withZeros) { + ParsePosition workPos(1); + Formattable temp; + + while (workText.length() > 0 && workPos.getIndex() != 0) { + workPos.setIndex(0); + getRuleSet()->parse(workText, workPos, 1, nonNumericalExecutedRuleMask, temp); // parse zero or nothing at all + if (workPos.getIndex() == 0) { + // we failed, either there were no more zeros, or the number was formatted with digits + // either way, we're done + break; + } + + ++zeroCount; + parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); + workText.remove(0, workPos.getIndex()); + while (workText.length() > 0 && workText.charAt(0) == gSpace) { + workText.remove(0, 1); + parsePosition.setIndex(parsePosition.getIndex() + 1); + } + } + + workText = text; + workText.remove(0, (int32_t)parsePosition.getIndex()); + parsePosition.setIndex(0); + } + + // we've parsed off the zeros, now let's parse the rest from our current position + NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, false, nonNumericalExecutedRuleMask, result); + + if (withZeros) { + // any base value will do in this case. is there a way to + // force this to not bother trying all the base values? + + // compute the 'effective' base and prescale the value down + int64_t n = result.getLong(status); // force conversion! + int64_t d = 1; + while (d <= n) { + d *= 10; + } + // now add the zeros + while (zeroCount > 0) { + d *= 10; + --zeroCount; + } + // d is now our true denominator + result.setDouble((double)n/(double)d); + } + + return true; +} + +bool +NumeratorSubstitution::operator==(const NFSubstitution& rhs) const +{ + return NFSubstitution::operator==(rhs) && + denominator == ((const NumeratorSubstitution*)&rhs)->denominator; +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution) + +const char16_t NumeratorSubstitution::LTLT[] = { 0x003c, 0x003c }; + +U_NAMESPACE_END + +/* U_HAVE_RBNF */ +#endif + diff --git a/deps/icu-small/source/i18n/nfsubs.h b/deps/icu-small/source/i18n/nfsubs.h index a38a3722a59d24..6dd029ff4ef1ec 100644 --- a/deps/icu-small/source/i18n/nfsubs.h +++ b/deps/icu-small/source/i18n/nfsubs.h @@ -1,262 +1,262 @@ -// © 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 name: nfsubs.h -* encoding: UTF-8 -* tab size: 8 (not used) -* indentation:4 -* -* Modification history -* Date Name Comments -* 10/11/2001 Doug Ported from ICU4J -*/ - -#ifndef NFSUBS_H -#define NFSUBS_H - -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "nfrule.h" - -#if U_HAVE_RBNF - -#include "unicode/utypes.h" -#include "unicode/decimfmt.h" -#include "nfrs.h" -#include - -U_NAMESPACE_BEGIN - -class NFSubstitution : public UObject { - int32_t pos; - const NFRuleSet* ruleSet; - DecimalFormat* numberFormat; - -protected: - NFSubstitution(int32_t pos, - const NFRuleSet* ruleSet, - const UnicodeString& description, - UErrorCode& status); - - /** - * Get the Ruleset of the object. - * @return the Ruleset of the object. - */ - const NFRuleSet* getRuleSet() const { return ruleSet; } - - /** - * get the NumberFormat of this object. - * @return the numberformat of this object. - */ - const DecimalFormat* getNumberFormat() const { return numberFormat; } - -public: - static NFSubstitution* makeSubstitution(int32_t pos, - const NFRule* rule, - const NFRule* predecessor, - const NFRuleSet* ruleSet, - const RuleBasedNumberFormat* rbnf, - const UnicodeString& description, - UErrorCode& status); - - /** - * Destructor. - */ - virtual ~NFSubstitution(); - - /** - * Return true if the given Format objects are semantically equal. - * Objects of different subclasses are considered unequal. - * @param rhs the object to be compared with. - * @return true if the given Format objects are semantically equal. - */ - virtual bool operator==(const NFSubstitution& rhs) const; - - /** - * Return true if the given Format objects are semantically unequal. - * Objects of different subclasses are considered unequal. - * @param rhs the object to be compared with. - * @return true if the given Format objects are semantically unequal. - */ - bool operator!=(const NFSubstitution& rhs) const { return !operator==(rhs); } - - /** - * Sets the substitution's divisor. Used by NFRule.setBaseValue(). - * A no-op for all substitutions except multiplier and modulus - * substitutions. - * @param radix The radix of the divisor - * @param exponent The exponent of the divisor - */ - virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status); - - /** - * Replaces result with the string describing the substitution. - * @param result Output param which will receive the string. - */ - virtual void toString(UnicodeString& result) const; - - void setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& status); - - //----------------------------------------------------------------------- - // formatting - //----------------------------------------------------------------------- - - /** - * Performs a mathematical operation on the number, formats it using - * either ruleSet or decimalFormat, and inserts the result into - * toInsertInto. - * @param number The number being formatted. - * @param toInsertInto The string we insert the result into - * @param pos The position in toInsertInto where the owning rule's - * rule text begins (this value is added to this substitution's - * position to determine exactly where to insert the new text) - */ - virtual void doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const; - - /** - * Performs a mathematical operation on the number, formats it using - * either ruleSet or decimalFormat, and inserts the result into - * toInsertInto. - * @param number The number being formatted. - * @param toInsertInto The string we insert the result into - * @param pos The position in toInsertInto where the owning rule's - * rule text begins (this value is added to this substitution's - * position to determine exactly where to insert the new text) - */ - virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const; - -protected: - /** - * Subclasses override this function to perform some kind of - * mathematical operation on the number. The result of this operation - * is formatted using the rule set or DecimalFormat that this - * substitution refers to, and the result is inserted into the result - * string. - * @param The number being formatted - * @return The result of performing the opreration on the number - */ - virtual int64_t transformNumber(int64_t number) const = 0; - - /** - * Subclasses override this function to perform some kind of - * mathematical operation on the number. The result of this operation - * is formatted using the rule set or DecimalFormat that this - * substitution refers to, and the result is inserted into the result - * string. - * @param The number being formatted - * @return The result of performing the opreration on the number - */ - virtual double transformNumber(double number) const = 0; - -public: - //----------------------------------------------------------------------- - // parsing - //----------------------------------------------------------------------- - - /** - * Parses a string using the rule set or DecimalFormat belonging - * to this substitution. If there's a match, a mathematical - * operation (the inverse of the one used in formatting) is - * performed on the result of the parse and the value passed in - * and returned as the result. The parse position is updated to - * point to the first unmatched character in the string. - * @param text The string to parse - * @param parsePosition On entry, ignored, but assumed to be 0. - * On exit, this is updated to point to the first unmatched - * character (or 0 if the substitution didn't match) - * @param baseValue A partial parse result that should be - * combined with the result of this parse - * @param upperBound When searching the rule set for a rule - * matching the string passed in, only rules with base values - * lower than this are considered - * @param lenientParse If true and matching against rules fails, - * the substitution will also try matching the text against - * numerals using a default-costructed NumberFormat. If false, - * no extra work is done. (This value is false whenever the - * formatter isn't in lenient-parse mode, but is also false - * under some conditions even when the formatter _is_ in - * lenient-parse mode.) - * @return If there's a match, this is the result of composing - * baseValue with whatever was returned from matching the - * characters. This will be either a Long or a Double. If there's - * no match this is new Long(0) (not null), and parsePosition - * is left unchanged. - */ - virtual UBool doParse(const UnicodeString& text, - ParsePosition& parsePosition, - double baseValue, - double upperBound, - UBool lenientParse, - uint32_t nonNumericalExecutedRuleMask, - Formattable& result) const; - - /** - * Derives a new value from the two values passed in. The two values - * are typically either the base values of two rules (the one containing - * the substitution and the one matching the substitution) or partial - * parse results derived in some other way. The operation is generally - * the inverse of the operation performed by transformNumber(). - * @param newRuleValue The value produced by matching this substitution - * @param oldRuleValue The value that was passed to the substitution - * by the rule that owns it - * @return A third value derived from the other two, representing a - * partial parse result - */ - virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const = 0; - - /** - * Calculates an upper bound when searching for a rule that matches - * this substitution. Rules with base values greater than or equal - * to upperBound are not considered. - * @param oldUpperBound The current upper-bound setting. The new - * upper bound can't be any higher. - * @return the upper bound when searching for a rule that matches - * this substitution. - */ - virtual double calcUpperBound(double oldUpperBound) const = 0; - - //----------------------------------------------------------------------- - // simple accessors - //----------------------------------------------------------------------- - - /** - * Returns the substitution's position in the rule that owns it. - * @return The substitution's position in the rule that owns it. - */ - int32_t getPos() const { return pos; } - - /** - * Returns the character used in the textual representation of - * substitutions of this type. Used by toString(). - * @return This substitution's token character. - */ - virtual UChar tokenChar() const = 0; - - /** - * Returns true if this is a modulus substitution. (We didn't do this - * with instanceof partially because it causes source files to - * proliferate and partially because we have to port this to C++.) - * @return true if this object is an instance of ModulusSubstitution - */ - virtual UBool isModulusSubstitution() const; - -private: - NFSubstitution(const NFSubstitution &other) = delete; // forbid copying of this class - NFSubstitution &operator=(const NFSubstitution &other) = delete; // forbid copying of this class - -public: - static UClassID getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; -}; - -U_NAMESPACE_END - -/* U_HAVE_RBNF */ -#endif - -// NFSUBS_H -#endif +// © 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 name: nfsubs.h +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* Modification history +* Date Name Comments +* 10/11/2001 Doug Ported from ICU4J +*/ + +#ifndef NFSUBS_H +#define NFSUBS_H + +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "nfrule.h" + +#if U_HAVE_RBNF + +#include "unicode/utypes.h" +#include "unicode/decimfmt.h" +#include "nfrs.h" +#include + +U_NAMESPACE_BEGIN + +class NFSubstitution : public UObject { + int32_t pos; + const NFRuleSet* ruleSet; + DecimalFormat* numberFormat; + +protected: + NFSubstitution(int32_t pos, + const NFRuleSet* ruleSet, + const UnicodeString& description, + UErrorCode& status); + + /** + * Get the Ruleset of the object. + * @return the Ruleset of the object. + */ + const NFRuleSet* getRuleSet() const { return ruleSet; } + + /** + * get the NumberFormat of this object. + * @return the numberformat of this object. + */ + const DecimalFormat* getNumberFormat() const { return numberFormat; } + +public: + static NFSubstitution* makeSubstitution(int32_t pos, + const NFRule* rule, + const NFRule* predecessor, + const NFRuleSet* ruleSet, + const RuleBasedNumberFormat* rbnf, + const UnicodeString& description, + UErrorCode& status); + + /** + * Destructor. + */ + virtual ~NFSubstitution(); + + /** + * Return true if the given Format objects are semantically equal. + * Objects of different subclasses are considered unequal. + * @param rhs the object to be compared with. + * @return true if the given Format objects are semantically equal. + */ + virtual bool operator==(const NFSubstitution& rhs) const; + + /** + * Return true if the given Format objects are semantically unequal. + * Objects of different subclasses are considered unequal. + * @param rhs the object to be compared with. + * @return true if the given Format objects are semantically unequal. + */ + bool operator!=(const NFSubstitution& rhs) const { return !operator==(rhs); } + + /** + * Sets the substitution's divisor. Used by NFRule.setBaseValue(). + * A no-op for all substitutions except multiplier and modulus + * substitutions. + * @param radix The radix of the divisor + * @param exponent The exponent of the divisor + */ + virtual void setDivisor(int32_t radix, int16_t exponent, UErrorCode& status); + + /** + * Replaces result with the string describing the substitution. + * @param result Output param which will receive the string. + */ + virtual void toString(UnicodeString& result) const; + + void setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& status); + + //----------------------------------------------------------------------- + // formatting + //----------------------------------------------------------------------- + + /** + * Performs a mathematical operation on the number, formats it using + * either ruleSet or decimalFormat, and inserts the result into + * toInsertInto. + * @param number The number being formatted. + * @param toInsertInto The string we insert the result into + * @param pos The position in toInsertInto where the owning rule's + * rule text begins (this value is added to this substitution's + * position to determine exactly where to insert the new text) + */ + virtual void doSubstitution(int64_t number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const; + + /** + * Performs a mathematical operation on the number, formats it using + * either ruleSet or decimalFormat, and inserts the result into + * toInsertInto. + * @param number The number being formatted. + * @param toInsertInto The string we insert the result into + * @param pos The position in toInsertInto where the owning rule's + * rule text begins (this value is added to this substitution's + * position to determine exactly where to insert the new text) + */ + virtual void doSubstitution(double number, UnicodeString& toInsertInto, int32_t pos, int32_t recursionCount, UErrorCode& status) const; + +protected: + /** + * Subclasses override this function to perform some kind of + * mathematical operation on the number. The result of this operation + * is formatted using the rule set or DecimalFormat that this + * substitution refers to, and the result is inserted into the result + * string. + * @param The number being formatted + * @return The result of performing the opreration on the number + */ + virtual int64_t transformNumber(int64_t number) const = 0; + + /** + * Subclasses override this function to perform some kind of + * mathematical operation on the number. The result of this operation + * is formatted using the rule set or DecimalFormat that this + * substitution refers to, and the result is inserted into the result + * string. + * @param The number being formatted + * @return The result of performing the opreration on the number + */ + virtual double transformNumber(double number) const = 0; + +public: + //----------------------------------------------------------------------- + // parsing + //----------------------------------------------------------------------- + + /** + * Parses a string using the rule set or DecimalFormat belonging + * to this substitution. If there's a match, a mathematical + * operation (the inverse of the one used in formatting) is + * performed on the result of the parse and the value passed in + * and returned as the result. The parse position is updated to + * point to the first unmatched character in the string. + * @param text The string to parse + * @param parsePosition On entry, ignored, but assumed to be 0. + * On exit, this is updated to point to the first unmatched + * character (or 0 if the substitution didn't match) + * @param baseValue A partial parse result that should be + * combined with the result of this parse + * @param upperBound When searching the rule set for a rule + * matching the string passed in, only rules with base values + * lower than this are considered + * @param lenientParse If true and matching against rules fails, + * the substitution will also try matching the text against + * numerals using a default-costructed NumberFormat. If false, + * no extra work is done. (This value is false whenever the + * formatter isn't in lenient-parse mode, but is also false + * under some conditions even when the formatter _is_ in + * lenient-parse mode.) + * @return If there's a match, this is the result of composing + * baseValue with whatever was returned from matching the + * characters. This will be either a Long or a Double. If there's + * no match this is new Long(0) (not null), and parsePosition + * is left unchanged. + */ + virtual UBool doParse(const UnicodeString& text, + ParsePosition& parsePosition, + double baseValue, + double upperBound, + UBool lenientParse, + uint32_t nonNumericalExecutedRuleMask, + Formattable& result) const; + + /** + * Derives a new value from the two values passed in. The two values + * are typically either the base values of two rules (the one containing + * the substitution and the one matching the substitution) or partial + * parse results derived in some other way. The operation is generally + * the inverse of the operation performed by transformNumber(). + * @param newRuleValue The value produced by matching this substitution + * @param oldRuleValue The value that was passed to the substitution + * by the rule that owns it + * @return A third value derived from the other two, representing a + * partial parse result + */ + virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const = 0; + + /** + * Calculates an upper bound when searching for a rule that matches + * this substitution. Rules with base values greater than or equal + * to upperBound are not considered. + * @param oldUpperBound The current upper-bound setting. The new + * upper bound can't be any higher. + * @return the upper bound when searching for a rule that matches + * this substitution. + */ + virtual double calcUpperBound(double oldUpperBound) const = 0; + + //----------------------------------------------------------------------- + // simple accessors + //----------------------------------------------------------------------- + + /** + * Returns the substitution's position in the rule that owns it. + * @return The substitution's position in the rule that owns it. + */ + int32_t getPos() const { return pos; } + + /** + * Returns the character used in the textual representation of + * substitutions of this type. Used by toString(). + * @return This substitution's token character. + */ + virtual char16_t tokenChar() const = 0; + + /** + * Returns true if this is a modulus substitution. (We didn't do this + * with instanceof partially because it causes source files to + * proliferate and partially because we have to port this to C++.) + * @return true if this object is an instance of ModulusSubstitution + */ + virtual UBool isModulusSubstitution() const; + +private: + NFSubstitution(const NFSubstitution &other) = delete; // forbid copying of this class + NFSubstitution &operator=(const NFSubstitution &other) = delete; // forbid copying of this class + +public: + static UClassID getStaticClassID(); + virtual UClassID getDynamicClassID() const override; +}; + +U_NAMESPACE_END + +/* U_HAVE_RBNF */ +#endif + +// NFSUBS_H +#endif diff --git a/deps/icu-small/source/i18n/nortrans.cpp b/deps/icu-small/source/i18n/nortrans.cpp index b1809daebd058a..620600013ff2a6 100644 --- a/deps/icu-small/source/i18n/nortrans.cpp +++ b/deps/icu-small/source/i18n/nortrans.cpp @@ -1,178 +1,178 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2001-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 07/03/01 aliu Creation. -********************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/normalizer2.h" -#include "unicode/utf16.h" -#include "cstring.h" -#include "nortrans.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NormalizationTransliterator) - -static inline Transliterator::Token cstrToken(const char *s) { - return Transliterator::pointerToken((void *)s); -} - -/** - * System registration hook. - */ -void NormalizationTransliterator::registerIDs() { - // In the Token, the byte after the NUL is the UNormalization2Mode. - Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-NFC"), - _create, cstrToken("nfc\0\0")); - Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-NFKC"), - _create, cstrToken("nfkc\0\0")); - Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-NFD"), - _create, cstrToken("nfc\0\1")); - Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-NFKD"), - _create, cstrToken("nfkc\0\1")); - Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-FCD"), - _create, cstrToken("nfc\0\2")); - Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-FCC"), - _create, cstrToken("nfc\0\3")); - Transliterator::_registerSpecialInverse(UNICODE_STRING_SIMPLE("NFC"), - UNICODE_STRING_SIMPLE("NFD"), true); - Transliterator::_registerSpecialInverse(UNICODE_STRING_SIMPLE("NFKC"), - UNICODE_STRING_SIMPLE("NFKD"), true); - Transliterator::_registerSpecialInverse(UNICODE_STRING_SIMPLE("FCC"), - UNICODE_STRING_SIMPLE("NFD"), false); - Transliterator::_registerSpecialInverse(UNICODE_STRING_SIMPLE("FCD"), - UNICODE_STRING_SIMPLE("FCD"), false); -} - -/** - * Factory methods - */ -Transliterator* NormalizationTransliterator::_create(const UnicodeString& ID, - Token context) { - const char *name = (const char *)context.pointer; - UNormalization2Mode mode = (UNormalization2Mode)uprv_strchr(name, 0)[1]; - UErrorCode errorCode = U_ZERO_ERROR; - const Normalizer2 *norm2 = Normalizer2::getInstance(NULL, name, mode, errorCode); - if(U_SUCCESS(errorCode)) { - return new NormalizationTransliterator(ID, *norm2); - } else { - return NULL; - } -} - -/** - * Constructs a transliterator. - */ -NormalizationTransliterator::NormalizationTransliterator(const UnicodeString& id, - const Normalizer2 &norm2) : - Transliterator(id, 0), fNorm2(norm2) {} - -/** - * Destructor. - */ -NormalizationTransliterator::~NormalizationTransliterator() { -} - -/** - * Copy constructor. - */ -NormalizationTransliterator::NormalizationTransliterator(const NormalizationTransliterator& o) : - Transliterator(o), fNorm2(o.fNorm2) {} - -/** - * Transliterator API. - */ -NormalizationTransliterator* NormalizationTransliterator::clone() const { - return new NormalizationTransliterator(*this); -} - -/** - * Implements {@link Transliterator#handleTransliterate}. - */ -void NormalizationTransliterator::handleTransliterate(Replaceable& text, UTransPosition& offsets, - UBool isIncremental) const { - // start and limit of the input range - int32_t start = offsets.start; - int32_t limit = offsets.limit; - if(start >= limit) { - return; - } - - /* - * Normalize as short chunks at a time as possible even in - * bulk mode, so that styled text is minimally disrupted. - * In incremental mode, a chunk that ends with offsets.limit - * must not be normalized. - * - * If it was known that the input text is not styled, then - * a bulk mode normalization could look like this: - - UnicodeString input, normalized; - int32_t length = limit - start; - _Replaceable_extractBetween(text, start, limit, input.getBuffer(length)); - input.releaseBuffer(length); - - UErrorCode status = U_ZERO_ERROR; - fNorm2.normalize(input, normalized, status); - - text.handleReplaceBetween(start, limit, normalized); - - int32_t delta = normalized.length() - length; - offsets.contextLimit += delta; - offsets.limit += delta; - offsets.start = limit + delta; - - */ - UErrorCode errorCode = U_ZERO_ERROR; - UnicodeString segment; - UnicodeString normalized; - UChar32 c = text.char32At(start); - do { - int32_t prev = start; - // Skip at least one character so we make progress. - // c holds the character at start. - segment.remove(); - do { - segment.append(c); - start += U16_LENGTH(c); - } while(start < limit && !fNorm2.hasBoundaryBefore(c = text.char32At(start))); - if(start == limit && isIncremental && !fNorm2.hasBoundaryAfter(c)) { - // stop in incremental mode when we reach the input limit - // in case there are additional characters that could change the - // normalization result - start=prev; - break; - } - fNorm2.normalize(segment, normalized, errorCode); - if(U_FAILURE(errorCode)) { - break; - } - if(segment != normalized) { - // replace the input chunk with its normalized form - text.handleReplaceBetween(prev, start, normalized); - - // update all necessary indexes accordingly - int32_t delta = normalized.length() - (start - prev); - start += delta; - limit += delta; - } - } while(start < limit); - - offsets.start = start; - offsets.contextLimit += limit - offsets.limit; - offsets.limit = limit; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2001-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 07/03/01 aliu Creation. +********************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/normalizer2.h" +#include "unicode/utf16.h" +#include "cstring.h" +#include "nortrans.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NormalizationTransliterator) + +static inline Transliterator::Token cstrToken(const char *s) { + return Transliterator::pointerToken((void *)s); +} + +/** + * System registration hook. + */ +void NormalizationTransliterator::registerIDs() { + // In the Token, the byte after the NUL is the UNormalization2Mode. + Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-NFC"), + _create, cstrToken("nfc\0\0")); + Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-NFKC"), + _create, cstrToken("nfkc\0\0")); + Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-NFD"), + _create, cstrToken("nfc\0\1")); + Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-NFKD"), + _create, cstrToken("nfkc\0\1")); + Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-FCD"), + _create, cstrToken("nfc\0\2")); + Transliterator::_registerFactory(UNICODE_STRING_SIMPLE("Any-FCC"), + _create, cstrToken("nfc\0\3")); + Transliterator::_registerSpecialInverse(UNICODE_STRING_SIMPLE("NFC"), + UNICODE_STRING_SIMPLE("NFD"), true); + Transliterator::_registerSpecialInverse(UNICODE_STRING_SIMPLE("NFKC"), + UNICODE_STRING_SIMPLE("NFKD"), true); + Transliterator::_registerSpecialInverse(UNICODE_STRING_SIMPLE("FCC"), + UNICODE_STRING_SIMPLE("NFD"), false); + Transliterator::_registerSpecialInverse(UNICODE_STRING_SIMPLE("FCD"), + UNICODE_STRING_SIMPLE("FCD"), false); +} + +/** + * Factory methods + */ +Transliterator* NormalizationTransliterator::_create(const UnicodeString& ID, + Token context) { + const char *name = (const char *)context.pointer; + UNormalization2Mode mode = (UNormalization2Mode)uprv_strchr(name, 0)[1]; + UErrorCode errorCode = U_ZERO_ERROR; + const Normalizer2 *norm2 = Normalizer2::getInstance(nullptr, name, mode, errorCode); + if(U_SUCCESS(errorCode)) { + return new NormalizationTransliterator(ID, *norm2); + } else { + return nullptr; + } +} + +/** + * Constructs a transliterator. + */ +NormalizationTransliterator::NormalizationTransliterator(const UnicodeString& id, + const Normalizer2 &norm2) : + Transliterator(id, 0), fNorm2(norm2) {} + +/** + * Destructor. + */ +NormalizationTransliterator::~NormalizationTransliterator() { +} + +/** + * Copy constructor. + */ +NormalizationTransliterator::NormalizationTransliterator(const NormalizationTransliterator& o) : + Transliterator(o), fNorm2(o.fNorm2) {} + +/** + * Transliterator API. + */ +NormalizationTransliterator* NormalizationTransliterator::clone() const { + return new NormalizationTransliterator(*this); +} + +/** + * Implements {@link Transliterator#handleTransliterate}. + */ +void NormalizationTransliterator::handleTransliterate(Replaceable& text, UTransPosition& offsets, + UBool isIncremental) const { + // start and limit of the input range + int32_t start = offsets.start; + int32_t limit = offsets.limit; + if(start >= limit) { + return; + } + + /* + * Normalize as short chunks at a time as possible even in + * bulk mode, so that styled text is minimally disrupted. + * In incremental mode, a chunk that ends with offsets.limit + * must not be normalized. + * + * If it was known that the input text is not styled, then + * a bulk mode normalization could look like this: + + UnicodeString input, normalized; + int32_t length = limit - start; + _Replaceable_extractBetween(text, start, limit, input.getBuffer(length)); + input.releaseBuffer(length); + + UErrorCode status = U_ZERO_ERROR; + fNorm2.normalize(input, normalized, status); + + text.handleReplaceBetween(start, limit, normalized); + + int32_t delta = normalized.length() - length; + offsets.contextLimit += delta; + offsets.limit += delta; + offsets.start = limit + delta; + + */ + UErrorCode errorCode = U_ZERO_ERROR; + UnicodeString segment; + UnicodeString normalized; + UChar32 c = text.char32At(start); + do { + int32_t prev = start; + // Skip at least one character so we make progress. + // c holds the character at start. + segment.remove(); + do { + segment.append(c); + start += U16_LENGTH(c); + } while(start < limit && !fNorm2.hasBoundaryBefore(c = text.char32At(start))); + if(start == limit && isIncremental && !fNorm2.hasBoundaryAfter(c)) { + // stop in incremental mode when we reach the input limit + // in case there are additional characters that could change the + // normalization result + start=prev; + break; + } + fNorm2.normalize(segment, normalized, errorCode); + if(U_FAILURE(errorCode)) { + break; + } + if(segment != normalized) { + // replace the input chunk with its normalized form + text.handleReplaceBetween(prev, start, normalized); + + // update all necessary indexes accordingly + int32_t delta = normalized.length() - (start - prev); + start += delta; + limit += delta; + } + } while(start < limit); + + offsets.start = start; + offsets.contextLimit += limit - offsets.limit; + offsets.limit = limit; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ diff --git a/deps/icu-small/source/i18n/nortrans.h b/deps/icu-small/source/i18n/nortrans.h index 01cb97ab38b4a1..99922c933d956e 100644 --- a/deps/icu-small/source/i18n/nortrans.h +++ b/deps/icu-small/source/i18n/nortrans.h @@ -1,102 +1,102 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2001-2010, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 07/03/01 aliu Creation. -********************************************************************** -*/ -#ifndef NORTRANS_H -#define NORTRANS_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/translit.h" -#include "unicode/normalizer2.h" - -U_NAMESPACE_BEGIN - -/** - * A transliterator that performs normalization. - * @author Alan Liu - */ -class NormalizationTransliterator : public Transliterator { - const Normalizer2 &fNorm2; - - public: - - /** - * Destructor. - */ - virtual ~NormalizationTransliterator(); - - /** - * Copy constructor. - */ - NormalizationTransliterator(const NormalizationTransliterator&); - - /** - * Transliterator API. - * @return A copy of the object. - */ - virtual NormalizationTransliterator* clone() const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - virtual UClassID getDynamicClassID() const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); - - protected: - - /** - * Implements {@link Transliterator#handleTransliterate}. - * @param text the buffer holding transliterated and - * untransliterated text - * @param offset the start and limit of the text, the position - * of the cursor, and the start and limit of transliteration. - * @param incremental if true, assume more text may be coming after - * pos.contextLimit. Otherwise, assume the text is complete. - */ - virtual void handleTransliterate(Replaceable& text, UTransPosition& offset, - UBool isIncremental) const override; - public: - - /** - * System registration hook. Public to Transliterator only. - */ - static void registerIDs(); - - private: - - // Transliterator::Factory methods - static Transliterator* _create(const UnicodeString& ID, - Token context); - - /** - * Constructs a transliterator. This method is private. - * Public users must use the factory method createInstance(). - */ - NormalizationTransliterator(const UnicodeString& id, const Normalizer2 &norm2); - -private: - /** - * Assignment operator. - */ - NormalizationTransliterator& operator=(const NormalizationTransliterator&); -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2001-2010, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 07/03/01 aliu Creation. +********************************************************************** +*/ +#ifndef NORTRANS_H +#define NORTRANS_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/translit.h" +#include "unicode/normalizer2.h" + +U_NAMESPACE_BEGIN + +/** + * A transliterator that performs normalization. + * @author Alan Liu + */ +class NormalizationTransliterator : public Transliterator { + const Normalizer2 &fNorm2; + + public: + + /** + * Destructor. + */ + virtual ~NormalizationTransliterator(); + + /** + * Copy constructor. + */ + NormalizationTransliterator(const NormalizationTransliterator&); + + /** + * Transliterator API. + * @return A copy of the object. + */ + virtual NormalizationTransliterator* clone() const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + virtual UClassID getDynamicClassID() const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + + protected: + + /** + * Implements {@link Transliterator#handleTransliterate}. + * @param text the buffer holding transliterated and + * untransliterated text + * @param offset the start and limit of the text, the position + * of the cursor, and the start and limit of transliteration. + * @param incremental if true, assume more text may be coming after + * pos.contextLimit. Otherwise, assume the text is complete. + */ + virtual void handleTransliterate(Replaceable& text, UTransPosition& offset, + UBool isIncremental) const override; + public: + + /** + * System registration hook. Public to Transliterator only. + */ + static void registerIDs(); + + private: + + // Transliterator::Factory methods + static Transliterator* _create(const UnicodeString& ID, + Token context); + + /** + * Constructs a transliterator. This method is private. + * Public users must use the factory method createInstance(). + */ + NormalizationTransliterator(const UnicodeString& id, const Normalizer2 &norm2); + +private: + /** + * Assignment operator. + */ + NormalizationTransliterator& operator=(const NormalizationTransliterator&); +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/nultrans.cpp b/deps/icu-small/source/i18n/nultrans.cpp index 439cc55d381a2a..b2853fe856961b 100644 --- a/deps/icu-small/source/i18n/nultrans.cpp +++ b/deps/icu-small/source/i18n/nultrans.cpp @@ -1,38 +1,38 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2000-2005, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 01/11/2000 aliu Creation. -********************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "nultrans.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NullTransliterator) - -NullTransliterator::NullTransliterator() : Transliterator(UNICODE_STRING_SIMPLE("Any-Null"), 0) {} - -NullTransliterator::~NullTransliterator() {} - -NullTransliterator* NullTransliterator::clone() const { - return new NullTransliterator(); -} - -void NullTransliterator::handleTransliterate(Replaceable& /*text*/, UTransPosition& offsets, - UBool /*isIncremental*/) const { - offsets.start = offsets.limit; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2000-2005, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 01/11/2000 aliu Creation. +********************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "nultrans.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NullTransliterator) + +NullTransliterator::NullTransliterator() : Transliterator(UNICODE_STRING_SIMPLE("Any-Null"), 0) {} + +NullTransliterator::~NullTransliterator() {} + +NullTransliterator* NullTransliterator::clone() const { + return new NullTransliterator(); +} + +void NullTransliterator::handleTransliterate(Replaceable& /*text*/, UTransPosition& offsets, + UBool /*isIncremental*/) const { + offsets.start = offsets.limit; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ diff --git a/deps/icu-small/source/i18n/nultrans.h b/deps/icu-small/source/i18n/nultrans.h index f5f2fbc9117419..bd6965169aa6eb 100644 --- a/deps/icu-small/source/i18n/nultrans.h +++ b/deps/icu-small/source/i18n/nultrans.h @@ -1,73 +1,73 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2000-2007, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 01/11/2000 aliu Creation. -********************************************************************** -*/ -#ifndef NULTRANS_H -#define NULTRANS_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/translit.h" - -U_NAMESPACE_BEGIN - -/** - * A transliterator that leaves text unchanged. - * @author Alan Liu - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ -class NullTransliterator : public Transliterator { - -public: - - /** - * Constructs a transliterator. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - NullTransliterator(); - - /** - * Destructor. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - virtual ~NullTransliterator(); - - /** - * Transliterator API. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - virtual NullTransliterator* clone() const override; - - /** - * Implements {@link Transliterator#handleTransliterate}. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - virtual void handleTransliterate(Replaceable& text, UTransPosition& offset, - UBool isIncremental) const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - virtual UClassID getDynamicClassID() const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); - -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2000-2007, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 01/11/2000 aliu Creation. +********************************************************************** +*/ +#ifndef NULTRANS_H +#define NULTRANS_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/translit.h" + +U_NAMESPACE_BEGIN + +/** + * A transliterator that leaves text unchanged. + * @author Alan Liu + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ +class NullTransliterator : public Transliterator { + +public: + + /** + * Constructs a transliterator. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + NullTransliterator(); + + /** + * Destructor. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + virtual ~NullTransliterator(); + + /** + * Transliterator API. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + virtual NullTransliterator* clone() const override; + + /** + * Implements {@link Transliterator#handleTransliterate}. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + virtual void handleTransliterate(Replaceable& text, UTransPosition& offset, + UBool isIncremental) const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + virtual UClassID getDynamicClassID() const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/number_affixutils.cpp b/deps/icu-small/source/i18n/number_affixutils.cpp index 5f5ff4c3a63422..72ca36451e6987 100644 --- a/deps/icu-small/source/i18n/number_affixutils.cpp +++ b/deps/icu-small/source/i18n/number_affixutils.cpp @@ -1,444 +1,444 @@ -// © 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 - -#include "number_affixutils.h" -#include "unicode/utf16.h" -#include "unicode/uniset.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - -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.char32At(offset); - - switch (state) { - case STATE_BASE: - if (cp == u'\'') { - // First quote - state = STATE_FIRST_QUOTE; - } else { - // Unquoted symbol - length++; - } - break; - case STATE_FIRST_QUOTE: - if (cp == u'\'') { - // Repeated quote - length++; - state = STATE_BASE; - } else { - // Quoted code point - length++; - state = STATE_INSIDE_QUOTE; - } - break; - case STATE_INSIDE_QUOTE: - if (cp == u'\'') { - // End of quoted sequence - state = STATE_AFTER_QUOTE; - } else { - // Quoted code point - length++; - } - break; - case STATE_AFTER_QUOTE: - if (cp == u'\'') { - // Double quote inside of quoted sequence - length++; - state = STATE_INSIDE_QUOTE; - } else { - // Unquoted symbol - length++; - } - break; - default: - UPRV_UNREACHABLE_EXIT; - } - - offset += U16_LENGTH(cp); - } - - switch (state) { - case STATE_FIRST_QUOTE: - case STATE_INSIDE_QUOTE: - status = U_ILLEGAL_ARGUMENT_ERROR; - break; - default: - break; - } - - return length; -} - -UnicodeString AffixUtils::escape(const UnicodeString &input) { - AffixPatternState state = STATE_BASE; - int32_t offset = 0; - UnicodeString output; - for (; offset < input.length();) { - UChar32 cp = input.char32At(offset); - - switch (cp) { - case u'\'': - output.append(u"''", -1); - break; - - case u'-': - case u'+': - case u'%': - case u'‰': - case u'¤': - if (state == STATE_BASE) { - output.append(u'\''); - output.append(cp); - state = STATE_INSIDE_QUOTE; - } else { - output.append(cp); - } - break; - - default: - if (state == STATE_INSIDE_QUOTE) { - output.append(u'\''); - output.append(cp); - state = STATE_BASE; - } else { - output.append(cp); - } - break; - } - offset += U16_LENGTH(cp); - } - - if (state == STATE_INSIDE_QUOTE) { - output.append(u'\''); - } - - return output; -} - -Field AffixUtils::getFieldForType(AffixPatternType type) { - switch (type) { - case TYPE_MINUS_SIGN: - return {UFIELD_CATEGORY_NUMBER, UNUM_SIGN_FIELD}; - case TYPE_PLUS_SIGN: - return {UFIELD_CATEGORY_NUMBER, UNUM_SIGN_FIELD}; - case TYPE_APPROXIMATELY_SIGN: - return {UFIELD_CATEGORY_NUMBER, UNUM_APPROXIMATELY_SIGN_FIELD}; - case TYPE_PERCENT: - return {UFIELD_CATEGORY_NUMBER, UNUM_PERCENT_FIELD}; - case TYPE_PERMILLE: - return {UFIELD_CATEGORY_NUMBER, UNUM_PERMILL_FIELD}; - case TYPE_CURRENCY_SINGLE: - return {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}; - case TYPE_CURRENCY_DOUBLE: - return {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}; - case TYPE_CURRENCY_TRIPLE: - return {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}; - case TYPE_CURRENCY_QUAD: - return {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}; - case TYPE_CURRENCY_QUINT: - return {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}; - case TYPE_CURRENCY_OVERFLOW: - return {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}; - default: - UPRV_UNREACHABLE_EXIT; - } -} - -int32_t -AffixUtils::unescape(const UnicodeString &affixPattern, FormattedStringBuilder &output, int32_t position, - const SymbolProvider &provider, Field field, UErrorCode &status) { - int32_t length = 0; - AffixTag tag; - while (hasNext(tag, affixPattern)) { - tag = nextToken(tag, affixPattern, status); - if (U_FAILURE(status)) { return length; } - if (tag.type == TYPE_CURRENCY_OVERFLOW) { - // Don't go to the provider for this special case - length += output.insertCodePoint( - position + length, - 0xFFFD, - {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}, - status); - } else if (tag.type < 0) { - length += output.insert( - position + length, provider.getSymbol(tag.type), getFieldForType(tag.type), status); - } else { - length += output.insertCodePoint(position + length, tag.codePoint, field, status); - } - } - return length; -} - -int32_t AffixUtils::unescapedCodePointCount(const UnicodeString &affixPattern, - const SymbolProvider &provider, UErrorCode &status) { - int32_t length = 0; - AffixTag tag; - while (hasNext(tag, affixPattern)) { - tag = nextToken(tag, affixPattern, status); - if (U_FAILURE(status)) { return length; } - if (tag.type == TYPE_CURRENCY_OVERFLOW) { - length += 1; - } else if (tag.type < 0) { - length += provider.getSymbol(tag.type).length(); - } else { - length += U16_LENGTH(tag.codePoint); - } - } - return length; -} - -bool -AffixUtils::containsType(const UnicodeString &affixPattern, AffixPatternType type, UErrorCode &status) { - if (affixPattern.length() == 0) { - return false; - } - AffixTag tag; - while (hasNext(tag, affixPattern)) { - tag = nextToken(tag, affixPattern, status); - if (U_FAILURE(status)) { return false; } - if (tag.type == type) { - return true; - } - } - return false; -} - -bool AffixUtils::hasCurrencySymbols(const UnicodeString &affixPattern, UErrorCode &status) { - if (affixPattern.length() == 0) { - return false; - } - AffixTag tag; - while (hasNext(tag, affixPattern)) { - tag = nextToken(tag, affixPattern, status); - if (U_FAILURE(status)) { return false; } - if (tag.type < 0 && getFieldForType(tag.type) == Field(UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD)) { - return true; - } - } - return false; -} - -UnicodeString AffixUtils::replaceType(const UnicodeString &affixPattern, AffixPatternType type, - char16_t replacementChar, UErrorCode &status) { - UnicodeString output(affixPattern); // copy - if (affixPattern.length() == 0) { - return output; - } - AffixTag tag; - while (hasNext(tag, affixPattern)) { - tag = nextToken(tag, affixPattern, status); - if (U_FAILURE(status)) { return output; } - if (tag.type == type) { - output.replace(tag.offset - 1, 1, replacementChar); - } - } - return output; -} - -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.char32At(offset); - int32_t count = U16_LENGTH(cp); - - switch (state) { - case STATE_BASE: - switch (cp) { - case u'\'': - state = STATE_FIRST_QUOTE; - offset += count; - // continue to the next code point - break; - case u'-': - return makeTag(offset + count, TYPE_MINUS_SIGN, STATE_BASE, 0); - case u'+': - return makeTag(offset + count, TYPE_PLUS_SIGN, STATE_BASE, 0); - case u'~': - return makeTag(offset + count, TYPE_APPROXIMATELY_SIGN, STATE_BASE, 0); - case u'%': - return makeTag(offset + count, TYPE_PERCENT, STATE_BASE, 0); - case u'‰': - return makeTag(offset + count, TYPE_PERMILLE, STATE_BASE, 0); - case u'¤': - state = STATE_FIRST_CURR; - offset += count; - // continue to the next code point - break; - default: - return makeTag(offset + count, TYPE_CODEPOINT, STATE_BASE, cp); - } - break; - case STATE_FIRST_QUOTE: - if (cp == u'\'') { - return makeTag(offset + count, TYPE_CODEPOINT, STATE_BASE, cp); - } else { - return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp); - } - case STATE_INSIDE_QUOTE: - if (cp == u'\'') { - state = STATE_AFTER_QUOTE; - offset += count; - // continue to the next code point - break; - } else { - return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp); - } - case STATE_AFTER_QUOTE: - if (cp == u'\'') { - return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp); - } else { - state = STATE_BASE; - // re-evaluate this code point - break; - } - case STATE_FIRST_CURR: - if (cp == u'¤') { - state = STATE_SECOND_CURR; - offset += count; - // continue to the next code point - break; - } else { - return makeTag(offset, TYPE_CURRENCY_SINGLE, STATE_BASE, 0); - } - case STATE_SECOND_CURR: - if (cp == u'¤') { - state = STATE_THIRD_CURR; - offset += count; - // continue to the next code point - break; - } else { - return makeTag(offset, TYPE_CURRENCY_DOUBLE, STATE_BASE, 0); - } - case STATE_THIRD_CURR: - if (cp == u'¤') { - state = STATE_FOURTH_CURR; - offset += count; - // continue to the next code point - break; - } else { - return makeTag(offset, TYPE_CURRENCY_TRIPLE, STATE_BASE, 0); - } - case STATE_FOURTH_CURR: - if (cp == u'¤') { - state = STATE_FIFTH_CURR; - offset += count; - // continue to the next code point - break; - } else { - return makeTag(offset, TYPE_CURRENCY_QUAD, STATE_BASE, 0); - } - case STATE_FIFTH_CURR: - if (cp == u'¤') { - state = STATE_OVERFLOW_CURR; - offset += count; - // continue to the next code point - break; - } else { - return makeTag(offset, TYPE_CURRENCY_QUINT, STATE_BASE, 0); - } - case STATE_OVERFLOW_CURR: - if (cp == u'¤') { - offset += count; - // continue to the next code point and loop back to this state - break; - } else { - return makeTag(offset, TYPE_CURRENCY_OVERFLOW, STATE_BASE, 0); - } - default: - UPRV_UNREACHABLE_EXIT; - } - } - // End of string - switch (state) { - case STATE_BASE: - // No more tokens in string. - return {-1}; - case STATE_FIRST_QUOTE: - case STATE_INSIDE_QUOTE: - // For consistent behavior with the JDK and ICU 58, set an error here. - status = U_ILLEGAL_ARGUMENT_ERROR; - return {-1}; - case STATE_AFTER_QUOTE: - // No more tokens in string. - return {-1}; - case STATE_FIRST_CURR: - return makeTag(offset, TYPE_CURRENCY_SINGLE, STATE_BASE, 0); - case STATE_SECOND_CURR: - return makeTag(offset, TYPE_CURRENCY_DOUBLE, STATE_BASE, 0); - case STATE_THIRD_CURR: - return makeTag(offset, TYPE_CURRENCY_TRIPLE, STATE_BASE, 0); - case STATE_FOURTH_CURR: - return makeTag(offset, TYPE_CURRENCY_QUAD, STATE_BASE, 0); - case STATE_FIFTH_CURR: - return makeTag(offset, TYPE_CURRENCY_QUINT, STATE_BASE, 0); - case STATE_OVERFLOW_CURR: - return makeTag(offset, TYPE_CURRENCY_OVERFLOW, STATE_BASE, 0); - default: - UPRV_UNREACHABLE_EXIT; - } -} - -bool AffixUtils::hasNext(const AffixTag &tag, const UnicodeString &string) { - // First check for the {-1} and default initializer syntax. - if (tag.offset < 0) { - return false; - } else if (tag.offset == 0) { - return string.length() > 0; - } - // The rest of the fields are safe to use now. - // Special case: the last character in string is an end quote. - if (tag.state == STATE_INSIDE_QUOTE && tag.offset == string.length() - 1 && - string.charAt(tag.offset) == u'\'') { - return false; - } else if (tag.state != STATE_BASE) { - return true; - } else { - return tag.offset < string.length(); - } -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include "number_affixutils.h" +#include "unicode/utf16.h" +#include "unicode/uniset.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +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.char32At(offset); + + switch (state) { + case STATE_BASE: + if (cp == u'\'') { + // First quote + state = STATE_FIRST_QUOTE; + } else { + // Unquoted symbol + length++; + } + break; + case STATE_FIRST_QUOTE: + if (cp == u'\'') { + // Repeated quote + length++; + state = STATE_BASE; + } else { + // Quoted code point + length++; + state = STATE_INSIDE_QUOTE; + } + break; + case STATE_INSIDE_QUOTE: + if (cp == u'\'') { + // End of quoted sequence + state = STATE_AFTER_QUOTE; + } else { + // Quoted code point + length++; + } + break; + case STATE_AFTER_QUOTE: + if (cp == u'\'') { + // Double quote inside of quoted sequence + length++; + state = STATE_INSIDE_QUOTE; + } else { + // Unquoted symbol + length++; + } + break; + default: + UPRV_UNREACHABLE_EXIT; + } + + offset += U16_LENGTH(cp); + } + + switch (state) { + case STATE_FIRST_QUOTE: + case STATE_INSIDE_QUOTE: + status = U_ILLEGAL_ARGUMENT_ERROR; + break; + default: + break; + } + + return length; +} + +UnicodeString AffixUtils::escape(const UnicodeString &input) { + AffixPatternState state = STATE_BASE; + int32_t offset = 0; + UnicodeString output; + for (; offset < input.length();) { + UChar32 cp = input.char32At(offset); + + switch (cp) { + case u'\'': + output.append(u"''", -1); + break; + + case u'-': + case u'+': + case u'%': + case u'‰': + case u'¤': + if (state == STATE_BASE) { + output.append(u'\''); + output.append(cp); + state = STATE_INSIDE_QUOTE; + } else { + output.append(cp); + } + break; + + default: + if (state == STATE_INSIDE_QUOTE) { + output.append(u'\''); + output.append(cp); + state = STATE_BASE; + } else { + output.append(cp); + } + break; + } + offset += U16_LENGTH(cp); + } + + if (state == STATE_INSIDE_QUOTE) { + output.append(u'\''); + } + + return output; +} + +Field AffixUtils::getFieldForType(AffixPatternType type) { + switch (type) { + case TYPE_MINUS_SIGN: + return {UFIELD_CATEGORY_NUMBER, UNUM_SIGN_FIELD}; + case TYPE_PLUS_SIGN: + return {UFIELD_CATEGORY_NUMBER, UNUM_SIGN_FIELD}; + case TYPE_APPROXIMATELY_SIGN: + return {UFIELD_CATEGORY_NUMBER, UNUM_APPROXIMATELY_SIGN_FIELD}; + case TYPE_PERCENT: + return {UFIELD_CATEGORY_NUMBER, UNUM_PERCENT_FIELD}; + case TYPE_PERMILLE: + return {UFIELD_CATEGORY_NUMBER, UNUM_PERMILL_FIELD}; + case TYPE_CURRENCY_SINGLE: + return {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}; + case TYPE_CURRENCY_DOUBLE: + return {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}; + case TYPE_CURRENCY_TRIPLE: + return {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}; + case TYPE_CURRENCY_QUAD: + return {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}; + case TYPE_CURRENCY_QUINT: + return {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}; + case TYPE_CURRENCY_OVERFLOW: + return {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}; + default: + UPRV_UNREACHABLE_EXIT; + } +} + +int32_t +AffixUtils::unescape(const UnicodeString &affixPattern, FormattedStringBuilder &output, int32_t position, + const SymbolProvider &provider, Field field, UErrorCode &status) { + int32_t length = 0; + AffixTag tag; + while (hasNext(tag, affixPattern)) { + tag = nextToken(tag, affixPattern, status); + if (U_FAILURE(status)) { return length; } + if (tag.type == TYPE_CURRENCY_OVERFLOW) { + // Don't go to the provider for this special case + length += output.insertCodePoint( + position + length, + 0xFFFD, + {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}, + status); + } else if (tag.type < 0) { + length += output.insert( + position + length, provider.getSymbol(tag.type), getFieldForType(tag.type), status); + } else { + length += output.insertCodePoint(position + length, tag.codePoint, field, status); + } + } + return length; +} + +int32_t AffixUtils::unescapedCodePointCount(const UnicodeString &affixPattern, + const SymbolProvider &provider, UErrorCode &status) { + int32_t length = 0; + AffixTag tag; + while (hasNext(tag, affixPattern)) { + tag = nextToken(tag, affixPattern, status); + if (U_FAILURE(status)) { return length; } + if (tag.type == TYPE_CURRENCY_OVERFLOW) { + length += 1; + } else if (tag.type < 0) { + length += provider.getSymbol(tag.type).length(); + } else { + length += U16_LENGTH(tag.codePoint); + } + } + return length; +} + +bool +AffixUtils::containsType(const UnicodeString &affixPattern, AffixPatternType type, UErrorCode &status) { + if (affixPattern.length() == 0) { + return false; + } + AffixTag tag; + while (hasNext(tag, affixPattern)) { + tag = nextToken(tag, affixPattern, status); + if (U_FAILURE(status)) { return false; } + if (tag.type == type) { + return true; + } + } + return false; +} + +bool AffixUtils::hasCurrencySymbols(const UnicodeString &affixPattern, UErrorCode &status) { + if (affixPattern.length() == 0) { + return false; + } + AffixTag tag; + while (hasNext(tag, affixPattern)) { + tag = nextToken(tag, affixPattern, status); + if (U_FAILURE(status)) { return false; } + if (tag.type < 0 && getFieldForType(tag.type) == Field(UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD)) { + return true; + } + } + return false; +} + +UnicodeString AffixUtils::replaceType(const UnicodeString &affixPattern, AffixPatternType type, + char16_t replacementChar, UErrorCode &status) { + UnicodeString output(affixPattern); // copy + if (affixPattern.length() == 0) { + return output; + } + AffixTag tag; + while (hasNext(tag, affixPattern)) { + tag = nextToken(tag, affixPattern, status); + if (U_FAILURE(status)) { return output; } + if (tag.type == type) { + output.replace(tag.offset - 1, 1, replacementChar); + } + } + return output; +} + +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.char32At(offset); + int32_t count = U16_LENGTH(cp); + + switch (state) { + case STATE_BASE: + switch (cp) { + case u'\'': + state = STATE_FIRST_QUOTE; + offset += count; + // continue to the next code point + break; + case u'-': + return makeTag(offset + count, TYPE_MINUS_SIGN, STATE_BASE, 0); + case u'+': + return makeTag(offset + count, TYPE_PLUS_SIGN, STATE_BASE, 0); + case u'~': + return makeTag(offset + count, TYPE_APPROXIMATELY_SIGN, STATE_BASE, 0); + case u'%': + return makeTag(offset + count, TYPE_PERCENT, STATE_BASE, 0); + case u'‰': + return makeTag(offset + count, TYPE_PERMILLE, STATE_BASE, 0); + case u'¤': + state = STATE_FIRST_CURR; + offset += count; + // continue to the next code point + break; + default: + return makeTag(offset + count, TYPE_CODEPOINT, STATE_BASE, cp); + } + break; + case STATE_FIRST_QUOTE: + if (cp == u'\'') { + return makeTag(offset + count, TYPE_CODEPOINT, STATE_BASE, cp); + } else { + return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp); + } + case STATE_INSIDE_QUOTE: + if (cp == u'\'') { + state = STATE_AFTER_QUOTE; + offset += count; + // continue to the next code point + break; + } else { + return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp); + } + case STATE_AFTER_QUOTE: + if (cp == u'\'') { + return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp); + } else { + state = STATE_BASE; + // re-evaluate this code point + break; + } + case STATE_FIRST_CURR: + if (cp == u'¤') { + state = STATE_SECOND_CURR; + offset += count; + // continue to the next code point + break; + } else { + return makeTag(offset, TYPE_CURRENCY_SINGLE, STATE_BASE, 0); + } + case STATE_SECOND_CURR: + if (cp == u'¤') { + state = STATE_THIRD_CURR; + offset += count; + // continue to the next code point + break; + } else { + return makeTag(offset, TYPE_CURRENCY_DOUBLE, STATE_BASE, 0); + } + case STATE_THIRD_CURR: + if (cp == u'¤') { + state = STATE_FOURTH_CURR; + offset += count; + // continue to the next code point + break; + } else { + return makeTag(offset, TYPE_CURRENCY_TRIPLE, STATE_BASE, 0); + } + case STATE_FOURTH_CURR: + if (cp == u'¤') { + state = STATE_FIFTH_CURR; + offset += count; + // continue to the next code point + break; + } else { + return makeTag(offset, TYPE_CURRENCY_QUAD, STATE_BASE, 0); + } + case STATE_FIFTH_CURR: + if (cp == u'¤') { + state = STATE_OVERFLOW_CURR; + offset += count; + // continue to the next code point + break; + } else { + return makeTag(offset, TYPE_CURRENCY_QUINT, STATE_BASE, 0); + } + case STATE_OVERFLOW_CURR: + if (cp == u'¤') { + offset += count; + // continue to the next code point and loop back to this state + break; + } else { + return makeTag(offset, TYPE_CURRENCY_OVERFLOW, STATE_BASE, 0); + } + default: + UPRV_UNREACHABLE_EXIT; + } + } + // End of string + switch (state) { + case STATE_BASE: + // No more tokens in string. + return {-1}; + case STATE_FIRST_QUOTE: + case STATE_INSIDE_QUOTE: + // For consistent behavior with the JDK and ICU 58, set an error here. + status = U_ILLEGAL_ARGUMENT_ERROR; + return {-1}; + case STATE_AFTER_QUOTE: + // No more tokens in string. + return {-1}; + case STATE_FIRST_CURR: + return makeTag(offset, TYPE_CURRENCY_SINGLE, STATE_BASE, 0); + case STATE_SECOND_CURR: + return makeTag(offset, TYPE_CURRENCY_DOUBLE, STATE_BASE, 0); + case STATE_THIRD_CURR: + return makeTag(offset, TYPE_CURRENCY_TRIPLE, STATE_BASE, 0); + case STATE_FOURTH_CURR: + return makeTag(offset, TYPE_CURRENCY_QUAD, STATE_BASE, 0); + case STATE_FIFTH_CURR: + return makeTag(offset, TYPE_CURRENCY_QUINT, STATE_BASE, 0); + case STATE_OVERFLOW_CURR: + return makeTag(offset, TYPE_CURRENCY_OVERFLOW, STATE_BASE, 0); + default: + UPRV_UNREACHABLE_EXIT; + } +} + +bool AffixUtils::hasNext(const AffixTag &tag, const UnicodeString &string) { + // First check for the {-1} and default initializer syntax. + if (tag.offset < 0) { + return false; + } else if (tag.offset == 0) { + return string.length() > 0; + } + // The rest of the fields are safe to use now. + // Special case: the last character in string is an end quote. + if (tag.state == STATE_INSIDE_QUOTE && tag.offset == string.length() - 1 && + string.charAt(tag.offset) == u'\'') { + return false; + } else if (tag.state != STATE_BASE) { + return true; + } else { + return tag.offset < string.length(); + } +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_affixutils.h b/deps/icu-small/source/i18n/number_affixutils.h index 5cfde61ffd0ca2..990798e85fccd2 100644 --- a/deps/icu-small/source/i18n/number_affixutils.h +++ b/deps/icu-small/source/i18n/number_affixutils.h @@ -1,244 +1,244 @@ -// © 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_AFFIXUTILS_H__ -#define __NUMBER_AFFIXUTILS_H__ - -#include -#include "number_types.h" -#include "unicode/stringpiece.h" -#include "unicode/unistr.h" -#include "formatted_string_builder.h" -#include "unicode/uniset.h" - -U_NAMESPACE_BEGIN namespace number { -namespace impl { - -enum AffixPatternState { - STATE_BASE = 0, - STATE_FIRST_QUOTE = 1, - STATE_INSIDE_QUOTE = 2, - STATE_AFTER_QUOTE = 3, - STATE_FIRST_CURR = 4, - STATE_SECOND_CURR = 5, - STATE_THIRD_CURR = 6, - STATE_FOURTH_CURR = 7, - STATE_FIFTH_CURR = 8, - STATE_OVERFLOW_CURR = 9 -}; - -// enum AffixPatternType defined in internals.h - -struct AffixTag { - int32_t offset; - UChar32 codePoint; - AffixPatternState state; - AffixPatternType type; - - AffixTag() - : offset(0), state(STATE_BASE) {} - - AffixTag(int32_t offset) - : offset(offset) {} - - AffixTag(int32_t offset, UChar32 codePoint, AffixPatternState state, AffixPatternType 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(); - - // TODO: Could this be more efficient if it returned by reference? - virtual UnicodeString getSymbol(AffixPatternType type) const = 0; -}; - -/** - * Performs manipulations on affix patterns: the prefix and suffix strings associated with a decimal - * format pattern. For example: - * - * - * - * - * - * - * - *
      Affix PatternExample Unescaped (Formatted) String
      abcabc
      ab-ab−
      ab'-'ab-
      ab''ab'
      - * - * To manually iterate over tokens in a literal string, use the following pattern, which is designed - * to be efficient. - * - *
      - * long tag = 0L;
      - * while (AffixPatternUtils.hasNext(tag, patternString)) {
      - *   tag = AffixPatternUtils.nextToken(tag, patternString);
      - *   int typeOrCp = AffixPatternUtils.getTypeOrCp(tag);
      - *   switch (typeOrCp) {
      - *     case AffixPatternUtils.TYPE_MINUS_SIGN:
      - *       // Current token is a minus sign.
      - *       break;
      - *     case AffixPatternUtils.TYPE_PLUS_SIGN:
      - *       // Current token is a plus sign.
      - *       break;
      - *     case AffixPatternUtils.TYPE_PERCENT:
      - *       // Current token is a percent sign.
      - *       break;
      - *     // ... other types ...
      - *     default:
      - *       // Current token is an arbitrary code point.
      - *       // The variable typeOrCp is the code point.
      - *       break;
      - *   }
      - * }
      - * 
      - */ -class U_I18N_API AffixUtils { - - public: - - /** - * Estimates the number of code points present in an unescaped version of the affix pattern string - * (one that would be returned by {@link #unescape}), assuming that all interpolated symbols - * consume one code point and that currencies consume as many code points as their symbol width. - * Used for computing padding width. - * - * @param patternString The original string whose width will be estimated. - * @return The length of the unescaped string. - */ - static int32_t estimateLength(const UnicodeString& patternString, UErrorCode& status); - - /** - * Takes a string and escapes (quotes) characters that have special meaning in the affix pattern - * syntax. This function does not reverse-lookup symbols. - * - *

      Example input: "-$x"; example output: "'-'$x" - * - * @param input The string to be escaped. - * @return The resulting UnicodeString. - */ - static UnicodeString escape(const UnicodeString& input); - - static Field getFieldForType(AffixPatternType type); - - /** - * Executes the unescape state machine. Replaces the unquoted characters "-", "+", "%", "‰", and - * "¤" with the corresponding symbols provided by the {@link SymbolProvider}, and inserts the - * result into the FormattedStringBuilder at the requested location. - * - *

      Example input: "'-'¤x"; example output: "-$x" - * - * @param affixPattern The original string to be unescaped. - * @param output The FormattedStringBuilder to mutate with the result. - * @param position The index into the FormattedStringBuilder to insert the string. - * @param provider An object to generate locale symbols. - */ - static int32_t unescape(const UnicodeString& affixPattern, FormattedStringBuilder& output, - int32_t position, const SymbolProvider& provider, Field field, - UErrorCode& status); - - /** - * Sames as {@link #unescape}, but only calculates the code point count. More efficient than {@link #unescape} - * if you only need the length but not the string itself. - * - * @param affixPattern The original string to be unescaped. - * @param provider An object to generate locale symbols. - * @return The same return value as if you called {@link #unescape}. - */ - 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 - * one of the constants "TYPE_" in {@link AffixPatternUtils}. - * - * @param affixPattern The affix pattern to check. - * @param type The token type. - * @return true if the affix pattern contains the given token type; false otherwise. - */ - static bool containsType(const UnicodeString& affixPattern, AffixPatternType type, UErrorCode& status); - - /** - * Checks whether the specified affix pattern has any unquoted currency symbols ("¤"). - * - * @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 UnicodeString& affixPattern, UErrorCode& status); - - /** - * Replaces all occurrences of tokens with the given type with the given replacement char. - * - * @param affixPattern The source affix pattern (does not get modified). - * @param type The token type. - * @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 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. - * - * @param tag A bitmask used for keeping track of state from token to token. The initial value - * should be 0L. - * @param patternString The affix pattern. - * @return The bitmask tag to pass to the next call of this method to retrieve the following token - * (never negative), or -1 if there were no more tokens in the affix pattern. - * @see #hasNext - */ - 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 - * {@link #nextToken}. - * - * @param tag The bitmask tag of the previous token, as returned by {@link #nextToken}. - * @param string The affix pattern. - * @return true if there are more tokens to consume; false otherwise. - */ - static bool hasNext(const AffixTag& tag, const UnicodeString& string); - - private: - /** - * Encodes the given values into a tag struct. - * 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) { - return {offset, cp, state, type}; - } -}; - -} // namespace impl -} // namespace number -U_NAMESPACE_END - - -#endif //__NUMBER_AFFIXUTILS_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_AFFIXUTILS_H__ +#define __NUMBER_AFFIXUTILS_H__ + +#include +#include "number_types.h" +#include "unicode/stringpiece.h" +#include "unicode/unistr.h" +#include "formatted_string_builder.h" +#include "unicode/uniset.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +enum AffixPatternState { + STATE_BASE = 0, + STATE_FIRST_QUOTE = 1, + STATE_INSIDE_QUOTE = 2, + STATE_AFTER_QUOTE = 3, + STATE_FIRST_CURR = 4, + STATE_SECOND_CURR = 5, + STATE_THIRD_CURR = 6, + STATE_FOURTH_CURR = 7, + STATE_FIFTH_CURR = 8, + STATE_OVERFLOW_CURR = 9 +}; + +// enum AffixPatternType defined in internals.h + +struct AffixTag { + int32_t offset; + UChar32 codePoint; + AffixPatternState state; + AffixPatternType type; + + AffixTag() + : offset(0), state(STATE_BASE) {} + + AffixTag(int32_t offset) + : offset(offset) {} + + AffixTag(int32_t offset, UChar32 codePoint, AffixPatternState state, AffixPatternType 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(); + + // TODO: Could this be more efficient if it returned by reference? + virtual UnicodeString getSymbol(AffixPatternType type) const = 0; +}; + +/** + * Performs manipulations on affix patterns: the prefix and suffix strings associated with a decimal + * format pattern. For example: + * + * + * + * + * + * + * + *
      Affix PatternExample Unescaped (Formatted) String
      abcabc
      ab-ab−
      ab'-'ab-
      ab''ab'
      + * + * To manually iterate over tokens in a literal string, use the following pattern, which is designed + * to be efficient. + * + *

      + * long tag = 0L;
      + * while (AffixPatternUtils.hasNext(tag, patternString)) {
      + *   tag = AffixPatternUtils.nextToken(tag, patternString);
      + *   int typeOrCp = AffixPatternUtils.getTypeOrCp(tag);
      + *   switch (typeOrCp) {
      + *     case AffixPatternUtils.TYPE_MINUS_SIGN:
      + *       // Current token is a minus sign.
      + *       break;
      + *     case AffixPatternUtils.TYPE_PLUS_SIGN:
      + *       // Current token is a plus sign.
      + *       break;
      + *     case AffixPatternUtils.TYPE_PERCENT:
      + *       // Current token is a percent sign.
      + *       break;
      + *     // ... other types ...
      + *     default:
      + *       // Current token is an arbitrary code point.
      + *       // The variable typeOrCp is the code point.
      + *       break;
      + *   }
      + * }
      + * 
      + */ +class U_I18N_API AffixUtils { + + public: + + /** + * Estimates the number of code points present in an unescaped version of the affix pattern string + * (one that would be returned by {@link #unescape}), assuming that all interpolated symbols + * consume one code point and that currencies consume as many code points as their symbol width. + * Used for computing padding width. + * + * @param patternString The original string whose width will be estimated. + * @return The length of the unescaped string. + */ + static int32_t estimateLength(const UnicodeString& patternString, UErrorCode& status); + + /** + * Takes a string and escapes (quotes) characters that have special meaning in the affix pattern + * syntax. This function does not reverse-lookup symbols. + * + *

      Example input: "-$x"; example output: "'-'$x" + * + * @param input The string to be escaped. + * @return The resulting UnicodeString. + */ + static UnicodeString escape(const UnicodeString& input); + + static Field getFieldForType(AffixPatternType type); + + /** + * Executes the unescape state machine. Replaces the unquoted characters "-", "+", "%", "‰", and + * "¤" with the corresponding symbols provided by the {@link SymbolProvider}, and inserts the + * result into the FormattedStringBuilder at the requested location. + * + *

      Example input: "'-'¤x"; example output: "-$x" + * + * @param affixPattern The original string to be unescaped. + * @param output The FormattedStringBuilder to mutate with the result. + * @param position The index into the FormattedStringBuilder to insert the string. + * @param provider An object to generate locale symbols. + */ + static int32_t unescape(const UnicodeString& affixPattern, FormattedStringBuilder& output, + int32_t position, const SymbolProvider& provider, Field field, + UErrorCode& status); + + /** + * Sames as {@link #unescape}, but only calculates the code point count. More efficient than {@link #unescape} + * if you only need the length but not the string itself. + * + * @param affixPattern The original string to be unescaped. + * @param provider An object to generate locale symbols. + * @return The same return value as if you called {@link #unescape}. + */ + 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 + * one of the constants "TYPE_" in {@link AffixPatternUtils}. + * + * @param affixPattern The affix pattern to check. + * @param type The token type. + * @return true if the affix pattern contains the given token type; false otherwise. + */ + static bool containsType(const UnicodeString& affixPattern, AffixPatternType type, UErrorCode& status); + + /** + * Checks whether the specified affix pattern has any unquoted currency symbols ("¤"). + * + * @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 UnicodeString& affixPattern, UErrorCode& status); + + /** + * Replaces all occurrences of tokens with the given type with the given replacement char. + * + * @param affixPattern The source affix pattern (does not get modified). + * @param type The token type. + * @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 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. + * + * @param tag A bitmask used for keeping track of state from token to token. The initial value + * should be 0L. + * @param patternString The affix pattern. + * @return The bitmask tag to pass to the next call of this method to retrieve the following token + * (never negative), or -1 if there were no more tokens in the affix pattern. + * @see #hasNext + */ + 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 + * {@link #nextToken}. + * + * @param tag The bitmask tag of the previous token, as returned by {@link #nextToken}. + * @param string The affix pattern. + * @return true if there are more tokens to consume; false otherwise. + */ + static bool hasNext(const AffixTag& tag, const UnicodeString& string); + + private: + /** + * Encodes the given values into a tag struct. + * 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) { + return {offset, cp, state, type}; + } +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + + +#endif //__NUMBER_AFFIXUTILS_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_asformat.cpp b/deps/icu-small/source/i18n/number_asformat.cpp index 8f2314d6898e0d..f9662898807744 100644 --- a/deps/icu-small/source/i18n/number_asformat.cpp +++ b/deps/icu-small/source/i18n/number_asformat.cpp @@ -1,117 +1,117 @@ -// © 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; - -bool 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); -} - -LocalizedNumberFormatterAsFormat* 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.nextFieldPosition(pos, status); - if (found && appendTo.length() != 0) { - pos.setBeginIndex(pos.getBeginIndex() + appendTo.length()); - pos.setEndIndex(pos.getEndIndex() + appendTo.length()); - } - appendTo.append(data.toTempString(status)); - 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.toTempString(status)); - if (posIter != nullptr) { - FieldPositionIteratorHandler fpih(posIter, status); - data.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; -} - - -// Definitions of public API methods (put here for dependency disentanglement) - -Format* LocalizedNumberFormatter::toFormat(UErrorCode& status) const { - if (U_FAILURE(status)) { - return nullptr; - } - LocalPointer retval( - new LocalizedNumberFormatterAsFormat(*this, fMacros.locale), status); - return retval.orphan(); -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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; + +bool 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); +} + +LocalizedNumberFormatterAsFormat* 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.nextFieldPosition(pos, status); + if (found && appendTo.length() != 0) { + pos.setBeginIndex(pos.getBeginIndex() + appendTo.length()); + pos.setEndIndex(pos.getEndIndex() + appendTo.length()); + } + appendTo.append(data.toTempString(status)); + 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.toTempString(status)); + if (posIter != nullptr) { + FieldPositionIteratorHandler fpih(posIter, status); + data.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; +} + + +// Definitions of public API methods (put here for dependency disentanglement) + +Format* LocalizedNumberFormatter::toFormat(UErrorCode& status) const { + if (U_FAILURE(status)) { + return nullptr; + } + LocalPointer retval( + new LocalizedNumberFormatterAsFormat(*this, fMacros.locale), status); + return retval.orphan(); +} + +#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 index 394b9a811fd7d6..3203d7c87ae3d8 100644 --- a/deps/icu-small/source/i18n/number_asformat.h +++ b/deps/icu-small/source/i18n/number_asformat.h @@ -1,106 +1,106 @@ -// © 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. - * - * @see NumberFormatter - */ -class U_I18N_API LocalizedNumberFormatterAsFormat : public Format { - public: - LocalizedNumberFormatterAsFormat(const LocalizedNumberFormatter& formatter, const Locale& locale); - - /** - * Destructor. - */ - ~LocalizedNumberFormatterAsFormat() U_OVERRIDE; - - /** - * Equals operator. - */ - bool operator==(const Format& other) const U_OVERRIDE; - - /** - * Creates a copy of this object. - */ - LocalizedNumberFormatterAsFormat* 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 */ +// © 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. + * + * @see NumberFormatter + */ +class U_I18N_API LocalizedNumberFormatterAsFormat : public Format { + public: + LocalizedNumberFormatterAsFormat(const LocalizedNumberFormatter& formatter, const Locale& locale); + + /** + * Destructor. + */ + ~LocalizedNumberFormatterAsFormat() override; + + /** + * Equals operator. + */ + bool operator==(const Format& other) const override; + + /** + * Creates a copy of this object. + */ + LocalizedNumberFormatterAsFormat* clone() const 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 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 override; + + /** + * Not supported: sets an error index and returns. + */ + void parseObject(const UnicodeString& source, Formattable& result, + ParsePosition& parse_pos) const 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 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 index 42bb05c06613d2..db27411d7f9380 100644 --- a/deps/icu-small/source/i18n/number_capi.cpp +++ b/deps/icu-small/source/i18n/number_capi.cpp @@ -1,255 +1,430 @@ -// © 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 "formattedval_impl.h" -#include "number_decnum.h" -#include "unicode/numberformatter.h" -#include "unicode/unumberformatter.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - - -U_NAMESPACE_BEGIN -namespace number { -namespace impl { - -/** - * Implementation class for UNumberFormatter. Wraps a LocalizedNumberFormatter. - */ -struct UNumberFormatterData : public UMemory, - // Magic number as ASCII == "NFR" (NumberFormatteR) - public IcuCApiHelper { - LocalizedNumberFormatter fFormatter; -}; - -struct UFormattedNumberImpl; - -// Magic number as ASCII == "FDN" (FormatteDNumber) -typedef IcuCApiHelper UFormattedNumberApiHelper; - -struct UFormattedNumberImpl : public UFormattedValueImpl, public UFormattedNumberApiHelper { - UFormattedNumberImpl(); - ~UFormattedNumberImpl(); - - FormattedNumber fImpl; - UFormattedNumberData fData; -}; - -UFormattedNumberImpl::UFormattedNumberImpl() - : fImpl(&fData) { - fFormattedValue = &fImpl; -} - -UFormattedNumberImpl::~UFormattedNumberImpl() { - // Disown the data from fImpl so it doesn't get deleted twice - fImpl.fData = nullptr; -} - -} -} -U_NAMESPACE_END - - -UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL( - UFormattedNumber, - UFormattedNumberImpl, - UFormattedNumberApiHelper, - unumf) - - -const DecimalQuantity* icu::number::impl::validateUFormattedNumberToDecimalQuantity( - const UFormattedNumber* uresult, UErrorCode& status) { - auto* result = UFormattedNumberApiHelper::validate(uresult, status); - if (U_FAILURE(status)) { - return nullptr; - } - return &result->fData.quantity; -} - - - -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 UNumberFormatter* U_EXPORT2 -unumf_openForSkeletonAndLocaleWithError(const UChar* skeleton, int32_t skeletonLen, const char* locale, - UParseError* perror, 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, *perror, *ec).locale(locale); - 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); - auto* result = UFormattedNumberApiHelper::validate(uresult, *ec); - if (U_FAILURE(*ec)) { return; } - - result->fData.resetString(); - result->fData.quantity.clear(); - result->fData.quantity.setToLong(value); - formatter->fFormatter.formatImpl(&result->fData, *ec); -} - -U_CAPI void U_EXPORT2 -unumf_formatDouble(const UNumberFormatter* uformatter, double value, UFormattedNumber* uresult, - UErrorCode* ec) { - const UNumberFormatterData* formatter = UNumberFormatterData::validate(uformatter, *ec); - auto* result = UFormattedNumberApiHelper::validate(uresult, *ec); - if (U_FAILURE(*ec)) { return; } - - result->fData.resetString(); - result->fData.quantity.clear(); - result->fData.quantity.setToDouble(value); - formatter->fFormatter.formatImpl(&result->fData, *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); - auto* result = UFormattedNumberApiHelper::validate(uresult, *ec); - if (U_FAILURE(*ec)) { return; } - - result->fData.resetString(); - result->fData.quantity.clear(); - result->fData.quantity.setToDecNumber({value, valueLen}, *ec); - if (U_FAILURE(*ec)) { return; } - formatter->fFormatter.formatImpl(&result->fData, *ec); -} - -U_CAPI int32_t U_EXPORT2 -unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t bufferCapacity, - UErrorCode* ec) { - const auto* result = UFormattedNumberApiHelper::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->fData.toTempString(*ec).extract(buffer, bufferCapacity, *ec); -} - -U_CAPI UBool U_EXPORT2 -unumf_resultNextFieldPosition(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec) { - const auto* result = UFormattedNumberApiHelper::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->fData.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 auto* result = UFormattedNumberApiHelper::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->fData.getAllFieldPositions(fpih, *ec); -} - -U_CAPI int32_t U_EXPORT2 -unumf_resultToDecimalNumber( - const UFormattedNumber* uresult, - char* dest, - int32_t destCapacity, - UErrorCode* ec) { - const auto* result = UFormattedNumberApiHelper::validate(uresult, *ec); - if (U_FAILURE(*ec)) { - return 0; - } - DecNum decnum; - return result->fData.quantity - .toDecNum(decnum, *ec) - .toCharString(*ec) - .extract(dest, destCapacity, *ec); -} - -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 */ - - - - - - - - - - - - - - - - - - - - - - - - - - - +// © 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 "formattedval_impl.h" +#include "number_decnum.h" +#include "unicode/numberformatter.h" +#include "unicode/unumberformatter.h" +#include "unicode/simplenumberformatter.h" +#include "unicode/usimplenumberformatter.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +U_NAMESPACE_BEGIN +namespace number { +namespace impl { + +/** + * Implementation class for UNumberFormatter. Wraps a LocalizedNumberFormatter. + */ +struct UNumberFormatterData : public UMemory, + // Magic number as ASCII == "NFR" (NumberFormatteR) + public IcuCApiHelper { + LocalizedNumberFormatter fFormatter; +}; + +/** + * Implementation class for USimpleNumber. Wraps a SimpleNumberFormatter. + */ +struct USimpleNumberData : public UMemory, + // Magic number as ASCII == "SNM" (SimpleNuMber) + public IcuCApiHelper { + SimpleNumber fNumber; +}; + +/** + * Implementation class for USimpleNumberFormatter. Wraps a SimpleNumberFormatter. + */ +struct USimpleNumberFormatterData : public UMemory, + // Magic number as ASCII == "SNF" (SimpleNumberFormatter) + public IcuCApiHelper { + SimpleNumberFormatter fFormatter; +}; + +struct UFormattedNumberImpl; + +// Magic number as ASCII == "FDN" (FormatteDNumber) +typedef IcuCApiHelper UFormattedNumberApiHelper; + +struct UFormattedNumberImpl : public UFormattedValueImpl, public UFormattedNumberApiHelper { + UFormattedNumberImpl(); + ~UFormattedNumberImpl(); + + FormattedNumber fImpl; + UFormattedNumberData fData; + + void setTo(FormattedNumber value); +}; + +UFormattedNumberImpl::UFormattedNumberImpl() + : fImpl(&fData) { + fFormattedValue = &fImpl; +} + +UFormattedNumberImpl::~UFormattedNumberImpl() { + // Disown the data from fImpl so it doesn't get deleted twice + fImpl.fData = nullptr; +} + +void UFormattedNumberImpl::setTo(FormattedNumber value) { + fData = std::move(*value.fData); +} + +} +} +U_NAMESPACE_END + + +UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL( + UFormattedNumber, + UFormattedNumberImpl, + UFormattedNumberApiHelper, + unumf) + + +const DecimalQuantity* icu::number::impl::validateUFormattedNumberToDecimalQuantity( + const UFormattedNumber* uresult, UErrorCode& status) { + auto* result = UFormattedNumberApiHelper::validate(uresult, status); + if (U_FAILURE(status)) { + return nullptr; + } + return &result->fData.quantity; +} + + + +U_CAPI UNumberFormatter* U_EXPORT2 +unumf_openForSkeletonAndLocale(const char16_t* 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 UNumberFormatter* U_EXPORT2 +unumf_openForSkeletonAndLocaleWithError(const char16_t* skeleton, int32_t skeletonLen, const char* locale, + UParseError* perror, 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); + UParseError tempParseError; + impl->fFormatter = NumberFormatter::forSkeleton(skeletonString, (perror == nullptr) ? tempParseError : *perror, *ec).locale(locale); + 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); + auto* result = UFormattedNumberApiHelper::validate(uresult, *ec); + if (U_FAILURE(*ec)) { return; } + + result->fData.resetString(); + result->fData.quantity.clear(); + result->fData.quantity.setToLong(value); + formatter->fFormatter.formatImpl(&result->fData, *ec); +} + +U_CAPI void U_EXPORT2 +unumf_formatDouble(const UNumberFormatter* uformatter, double value, UFormattedNumber* uresult, + UErrorCode* ec) { + const UNumberFormatterData* formatter = UNumberFormatterData::validate(uformatter, *ec); + auto* result = UFormattedNumberApiHelper::validate(uresult, *ec); + if (U_FAILURE(*ec)) { return; } + + result->fData.resetString(); + result->fData.quantity.clear(); + result->fData.quantity.setToDouble(value); + formatter->fFormatter.formatImpl(&result->fData, *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); + auto* result = UFormattedNumberApiHelper::validate(uresult, *ec); + if (U_FAILURE(*ec)) { return; } + + result->fData.resetString(); + result->fData.quantity.clear(); + result->fData.quantity.setToDecNumber({value, valueLen}, *ec); + if (U_FAILURE(*ec)) { return; } + formatter->fFormatter.formatImpl(&result->fData, *ec); +} + +U_CAPI int32_t U_EXPORT2 +unumf_resultToString(const UFormattedNumber* uresult, char16_t* buffer, int32_t bufferCapacity, + UErrorCode* ec) { + const auto* result = UFormattedNumberApiHelper::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->fData.toTempString(*ec).extract(buffer, bufferCapacity, *ec); +} + +U_CAPI UBool U_EXPORT2 +unumf_resultNextFieldPosition(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec) { + const auto* result = UFormattedNumberApiHelper::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->fData.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 auto* result = UFormattedNumberApiHelper::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->fData.getAllFieldPositions(fpih, *ec); +} + +U_CAPI int32_t U_EXPORT2 +unumf_resultToDecimalNumber( + const UFormattedNumber* uresult, + char* dest, + int32_t destCapacity, + UErrorCode* ec) { + const auto* result = UFormattedNumberApiHelper::validate(uresult, *ec); + if (U_FAILURE(*ec)) { + return 0; + } + DecNum decnum; + return result->fData.quantity + .toDecNum(decnum, *ec) + .toCharString(*ec) + .extract(dest, destCapacity, *ec); +} + +U_CAPI void U_EXPORT2 +unumf_close(UNumberFormatter* f) { + UErrorCode localStatus = U_ZERO_ERROR; + const UNumberFormatterData* impl = UNumberFormatterData::validate(f, localStatus); + delete impl; +} + + +///// SIMPLE NUMBER FORMATTER ///// + +U_CAPI USimpleNumber* U_EXPORT2 +usnum_openForInt64(int64_t value, UErrorCode* ec) { + auto* number = new USimpleNumberData(); + if (number == nullptr) { + *ec = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + number->fNumber = SimpleNumber::forInt64(value, *ec); + return number->exportForC(); +} + +U_CAPI void U_EXPORT2 +usnum_setToInt64(USimpleNumber* unumber, int64_t value, UErrorCode* ec) { + auto* number = USimpleNumberData::validate(unumber, *ec); + if (U_FAILURE(*ec)) { + return; + } + number->fNumber = SimpleNumber::forInt64(value, *ec); +} + +U_CAPI void U_EXPORT2 +usnum_multiplyByPowerOfTen(USimpleNumber* unumber, int32_t power, UErrorCode* ec) { + auto* number = USimpleNumberData::validate(unumber, *ec); + if (U_FAILURE(*ec)) { + return; + } + number->fNumber.multiplyByPowerOfTen(power, *ec); +} + +U_CAPI void U_EXPORT2 +usnum_roundTo(USimpleNumber* unumber, int32_t position, UNumberFormatRoundingMode roundingMode, UErrorCode* ec) { + auto* number = USimpleNumberData::validate(unumber, *ec); + if (U_FAILURE(*ec)) { + return; + } + number->fNumber.roundTo(position, roundingMode, *ec); +} + +U_CAPI void U_EXPORT2 +usnum_setMinimumIntegerDigits(USimpleNumber* unumber, int32_t minimumIntegerDigits, UErrorCode* ec) { + auto* number = USimpleNumberData::validate(unumber, *ec); + if (U_FAILURE(*ec)) { + return; + } + number->fNumber.setMinimumIntegerDigits(minimumIntegerDigits, *ec); +} + +U_CAPI void U_EXPORT2 +usnum_setMinimumFractionDigits(USimpleNumber* unumber, int32_t minimumFractionDigits, UErrorCode* ec) { + auto* number = USimpleNumberData::validate(unumber, *ec); + if (U_FAILURE(*ec)) { + return; + } + number->fNumber.setMinimumFractionDigits(minimumFractionDigits, *ec); +} + +U_CAPI void U_EXPORT2 +usnum_truncateStart(USimpleNumber* unumber, int32_t maximumIntegerDigits, UErrorCode* ec) { + auto* number = USimpleNumberData::validate(unumber, *ec); + if (U_FAILURE(*ec)) { + return; + } + number->fNumber.truncateStart(maximumIntegerDigits, *ec); +} + +U_CAPI void U_EXPORT2 +usnum_setSign(USimpleNumber* unumber, USimpleNumberSign sign, UErrorCode* ec) { + auto* number = USimpleNumberData::validate(unumber, *ec); + if (U_FAILURE(*ec)) { + return; + } + number->fNumber.setSign(sign, *ec); +} + +U_CAPI USimpleNumberFormatter* U_EXPORT2 +usnumf_openForLocale(const char* locale, UErrorCode* ec) { + auto* impl = new USimpleNumberFormatterData(); + if (impl == nullptr) { + *ec = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + impl->fFormatter = SimpleNumberFormatter::forLocale(locale, *ec); + return impl->exportForC(); +} + +U_CAPI USimpleNumberFormatter* U_EXPORT2 +usnumf_openForLocaleAndGroupingStrategy( + const char* locale, UNumberGroupingStrategy groupingStrategy, UErrorCode* ec) { + auto* impl = new USimpleNumberFormatterData(); + if (impl == nullptr) { + *ec = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + impl->fFormatter = SimpleNumberFormatter::forLocaleAndGroupingStrategy(locale, groupingStrategy, *ec); + return impl->exportForC(); +} + +U_CAPI void U_EXPORT2 +usnumf_format( + const USimpleNumberFormatter* uformatter, + USimpleNumber* unumber, + UFormattedNumber* uresult, + UErrorCode* ec) { + auto* formatter = USimpleNumberFormatterData::validate(uformatter, *ec); + auto* number = USimpleNumberData::validate(unumber, *ec); + auto* result = UFormattedNumberApiHelper::validate(uresult, *ec); + if (U_FAILURE(*ec)) { + return; + } + auto localResult = formatter->fFormatter.format(std::move(number->fNumber), *ec); + if (U_FAILURE(*ec)) { + return; + } + result->setTo(std::move(localResult)); +} + +U_CAPI void U_EXPORT2 +usnumf_formatInt64( + const USimpleNumberFormatter* uformatter, + int64_t value, + UFormattedNumber* uresult, + UErrorCode* ec) { + auto* formatter = USimpleNumberFormatterData::validate(uformatter, *ec); + auto* result = UFormattedNumberApiHelper::validate(uresult, *ec); + if (U_FAILURE(*ec)) { + return; + } + auto localResult = formatter->fFormatter.formatInt64(value, *ec); + result->setTo(std::move(localResult)); +} + +U_CAPI void U_EXPORT2 +usnum_close(USimpleNumber* unumber) { + UErrorCode localStatus = U_ZERO_ERROR; + const USimpleNumberData* impl = USimpleNumberData::validate(unumber, localStatus); + delete impl; +} + +U_CAPI void U_EXPORT2 +usnumf_close(USimpleNumberFormatter* uformatter) { + UErrorCode localStatus = U_ZERO_ERROR; + const USimpleNumberFormatterData* impl = USimpleNumberFormatterData::validate(uformatter, 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 4dc96e7ea1b91e..dd728cee722e1a 100644 --- a/deps/icu-small/source/i18n/number_compact.cpp +++ b/deps/icu-small/source/i18n/number_compact.cpp @@ -1,353 +1,353 @@ -// © 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 - -#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; -using namespace icu::number; -using namespace icu::number::impl; - -namespace { - -// A dummy object used when a "0" compact decimal entry is encountered. This is necessary -// in order to prevent falling back to root. Object equality ("==") is intended. -const UChar *USE_FALLBACK = u""; - -/** Produces a string like "NumberElements/latn/patternsShort/decimalFormat". */ -void getResourceBundleKey(const char *nsName, CompactStyle compactStyle, CompactType compactType, - CharString &sb, UErrorCode &status) { - sb.clear(); - sb.append("NumberElements/", status); - sb.append(nsName, status); - sb.append(compactStyle == CompactStyle::UNUM_SHORT ? "/patternsShort" : "/patternsLong", status); - sb.append(compactType == CompactType::TYPE_DECIMAL ? "/decimalFormat" : "/currencyFormat", status); -} - -int32_t getIndex(int32_t magnitude, StandardPlural::Form plural) { - return magnitude * StandardPlural::COUNT + plural; -} - -int32_t countZeros(const UChar *patternString, int32_t patternLength) { - // NOTE: This strategy for computing the number of zeros is a hack for efficiency. - // It could break if there are any 0s that aren't part of the main pattern. - int32_t numZeros = 0; - for (int32_t i = 0; i < patternLength; i++) { - if (patternString[i] == u'0') { - numZeros++; - } else if (numZeros > 0) { - break; // zeros should always be contiguous - } - } - return numZeros; -} - -} // namespace - -// NOTE: patterns and multipliers both get zero-initialized. -CompactData::CompactData() : patterns(), multipliers(), largestMagnitude(0), isEmpty(true) { -} - -void CompactData::populate(const Locale &locale, const char *nsName, CompactStyle compactStyle, - CompactType compactType, UErrorCode &status) { - CompactDataSink sink(*this); - LocalUResourceBundlePointer rb(ures_open(nullptr, locale.getName(), &status)); - if (U_FAILURE(status)) { return; } - - bool nsIsLatn = strcmp(nsName, "latn") == 0; - bool compactIsShort = compactStyle == CompactStyle::UNUM_SHORT; - - // Fall back to latn numbering system and/or short compact style. - CharString resourceKey; - getResourceBundleKey(nsName, compactStyle, compactType, resourceKey, status); - UErrorCode localStatus = U_ZERO_ERROR; - ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus); - if (isEmpty && !nsIsLatn) { - getResourceBundleKey("latn", compactStyle, compactType, resourceKey, status); - localStatus = U_ZERO_ERROR; - ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus); - } - if (isEmpty && !compactIsShort) { - getResourceBundleKey(nsName, CompactStyle::UNUM_SHORT, compactType, resourceKey, status); - localStatus = U_ZERO_ERROR; - ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus); - } - if (isEmpty && !nsIsLatn && !compactIsShort) { - getResourceBundleKey("latn", CompactStyle::UNUM_SHORT, compactType, resourceKey, status); - localStatus = U_ZERO_ERROR; - ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus); - } - - // The last fallback should be guaranteed to return data. - if (isEmpty) { - status = U_INTERNAL_PROGRAM_ERROR; - } -} - -int32_t CompactData::getMultiplier(int32_t magnitude) const { - if (magnitude < 0) { - return 0; - } - if (magnitude > largestMagnitude) { - magnitude = largestMagnitude; - } - return multipliers[magnitude]; -} - -const UChar *CompactData::getPattern( - int32_t magnitude, - const PluralRules *rules, - const DecimalQuantity &dq) const { - if (magnitude < 0) { - return nullptr; - } - if (magnitude > largestMagnitude) { - magnitude = largestMagnitude; - } - const UChar *patternString = nullptr; - if (dq.hasIntegerValue()) { - int64_t i = dq.toLong(true); - if (i == 0) { - patternString = patterns[getIndex(magnitude, StandardPlural::Form::EQ_0)]; - } else if (i == 1) { - patternString = patterns[getIndex(magnitude, StandardPlural::Form::EQ_1)]; - } - if (patternString != nullptr) { - return patternString; - } - } - StandardPlural::Form plural = utils::getStandardPlural(rules, dq); - patternString = patterns[getIndex(magnitude, plural)]; - if (patternString == nullptr && plural != StandardPlural::OTHER) { - // Fall back to "other" plural variant - patternString = patterns[getIndex(magnitude, StandardPlural::OTHER)]; - } - if (patternString == USE_FALLBACK) { // == is intended - // Return null if USE_FALLBACK is present - patternString = nullptr; - } - return patternString; -} - -void CompactData::getUniquePatterns(UVector &output, UErrorCode &status) const { - U_ASSERT(output.isEmpty()); - // NOTE: In C++, this is done more manually with a UVector. - // In Java, we can take advantage of JDK HashSet. - for (auto pattern : patterns) { - if (pattern == nullptr || pattern == USE_FALLBACK) { - continue; - } - - // Insert pattern into the UVector if the UVector does not already contain the pattern. - // Search the UVector from the end since identical patterns are likely to be adjacent. - for (int32_t i = output.size() - 1; i >= 0; i--) { - if (u_strcmp(pattern, static_cast(output[i])) == 0) { - goto continue_outer; - } - } - - // The string was not found; add it to the UVector. - // Note: must cast off const from pattern to store it in a UVector, which expects (void *) - output.addElement(const_cast(pattern), status); - - continue_outer: - continue; - } -} - -void CompactData::CompactDataSink::put(const char *key, ResourceValue &value, UBool /*noFallback*/, - UErrorCode &status) { - // traverse into the table of powers of ten - ResourceTable powersOfTenTable = value.getTable(status); - if (U_FAILURE(status)) { return; } - for (int i3 = 0; powersOfTenTable.getKeyAndValue(i3, key, value); ++i3) { - - // Assumes that the keys are always of the form "10000" where the magnitude is the - // length of the key minus one. We only support magnitudes less than COMPACT_MAX_DIGITS; - // ignore entries that have greater magnitude. - auto magnitude = static_cast (strlen(key) - 1); - U_ASSERT(magnitude < COMPACT_MAX_DIGITS); // debug assert - if (magnitude >= COMPACT_MAX_DIGITS) { // skip in production - continue; - } - int8_t multiplier = data.multipliers[magnitude]; - - // Iterate over the plural variants ("one", "other", etc) - ResourceTable pluralVariantsTable = value.getTable(status); - if (U_FAILURE(status)) { return; } - for (int i4 = 0; pluralVariantsTable.getKeyAndValue(i4, key, value); ++i4) { - // Skip this magnitude/plural if we already have it from a child locale. - // Note: This also skips USE_FALLBACK entries. - StandardPlural::Form plural = StandardPlural::fromString(key, status); - if (U_FAILURE(status)) { return; } - if (data.patterns[getIndex(magnitude, plural)] != nullptr) { - continue; - } - - // The value "0" means that we need to use the default pattern and not fall back - // to parent locales. Example locale where this is relevant: 'it'. - int32_t patternLength; - const UChar *patternString = value.getString(patternLength, status); - if (U_FAILURE(status)) { return; } - if (u_strcmp(patternString, u"0") == 0) { - patternString = USE_FALLBACK; - patternLength = 0; - } - - // Save the pattern string. We will parse it lazily. - data.patterns[getIndex(magnitude, plural)] = patternString; - - // If necessary, compute the multiplier: the difference between the magnitude - // and the number of zeros in the pattern. - if (multiplier == 0) { - int32_t numZeros = countZeros(patternString, patternLength); - if (numZeros > 0) { // numZeros==0 in certain cases, like Somali "Kun" - multiplier = static_cast (numZeros - magnitude - 1); - } - } - } - - // Save the multiplier. - if (data.multipliers[magnitude] == 0) { - data.multipliers[magnitude] = multiplier; - if (magnitude > data.largestMagnitude) { - data.largestMagnitude = magnitude; - } - data.isEmpty = false; - } else { - U_ASSERT(data.multipliers[magnitude] == multiplier); - } - } -} - -/////////////////////////////////////////////////////////// -/// END OF CompactData.java; BEGIN CompactNotation.java /// -/////////////////////////////////////////////////////////// - -CompactHandler::CompactHandler( - CompactStyle compactStyle, - const Locale &locale, - const char *nsName, - CompactType compactType, - const PluralRules *rules, - MutablePatternModifier *buildReference, - bool safe, - const MicroPropsGenerator *parent, - UErrorCode &status) - : rules(rules), parent(parent), safe(safe) { - data.populate(locale, nsName, compactStyle, compactType, status); - if (safe) { - // Safe code path - precomputeAllModifiers(*buildReference, status); - } else { - // Unsafe code path - // Store the MutablePatternModifier reference. - unsafePatternModifier = buildReference; - } -} - -CompactHandler::~CompactHandler() { - for (int32_t i = 0; i < precomputedModsLength; i++) { - delete precomputedMods[i].mod; - } -} - -void CompactHandler::precomputeAllModifiers(MutablePatternModifier &buildReference, UErrorCode &status) { - if (U_FAILURE(status)) { return; } - - // Initial capacity of 12 for 0K, 00K, 000K, ...M, ...B, and ...T - UVector allPatterns(12, status); - if (U_FAILURE(status)) { return; } - data.getUniquePatterns(allPatterns, status); - if (U_FAILURE(status)) { return; } - - // C++ only: ensure that precomputedMods has room. - precomputedModsLength = allPatterns.size(); - if (precomputedMods.getCapacity() < precomputedModsLength) { - precomputedMods.resize(allPatterns.size(), status); - if (U_FAILURE(status)) { return; } - } - - for (int32_t i = 0; i < precomputedModsLength; i++) { - auto patternString = static_cast(allPatterns[i]); - UnicodeString hello(patternString); - CompactModInfo &info = precomputedMods[i]; - ParsedPatternInfo patternInfo; - PatternParser::parseToPatternInfo(UnicodeString(patternString), patternInfo, status); - if (U_FAILURE(status)) { return; } - buildReference.setPatternInfo(&patternInfo, {UFIELD_CATEGORY_NUMBER, UNUM_COMPACT_FIELD}); - info.mod = buildReference.createImmutable(status); - if (U_FAILURE(status)) { return; } - info.patternString = patternString; - } -} - -void CompactHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const { - parent->processQuantity(quantity, micros, status); - if (U_FAILURE(status)) { return; } - - // Treat zero, NaN, and infinity as if they had magnitude 0 - int32_t magnitude; - int32_t multiplier = 0; - if (quantity.isZeroish()) { - magnitude = 0; - micros.rounder.apply(quantity, status); - } else { - // TODO: Revisit chooseMultiplierAndApply - multiplier = micros.rounder.chooseMultiplierAndApply(quantity, data, status); - magnitude = quantity.isZeroish() ? 0 : quantity.getMagnitude(); - magnitude -= multiplier; - } - - const UChar *patternString = data.getPattern(magnitude, rules, quantity); - if (patternString == nullptr) { - // Use the default (non-compact) modifier. - // No need to take any action. - } else if (safe) { - // Safe code path. - // Java uses a hash set here for O(1) lookup. C++ uses a linear search. - // TODO: Benchmark this and maybe change to a binary search or hash table. - int32_t i = 0; - for (; i < precomputedModsLength; i++) { - const CompactModInfo &info = precomputedMods[i]; - if (u_strcmp(patternString, info.patternString) == 0) { - info.mod->applyToMicros(micros, quantity, status); - break; - } - } - // It should be guaranteed that we found the entry. - U_ASSERT(i < precomputedModsLength); - } else { - // Unsafe code path. - // Overwrite the PatternInfo in the existing modMiddle. - // C++ Note: Use unsafePatternInfo for proper lifecycle. - ParsedPatternInfo &patternInfo = const_cast(this)->unsafePatternInfo; - PatternParser::parseToPatternInfo(UnicodeString(patternString), patternInfo, status); - unsafePatternModifier->setPatternInfo( - &unsafePatternInfo, - {UFIELD_CATEGORY_NUMBER, UNUM_COMPACT_FIELD}); - unsafePatternModifier->setNumberProperties(quantity.signum(), StandardPlural::Form::COUNT); - micros.modMiddle = unsafePatternModifier; - } - - // Change the exponent only after we select appropriate plural form - // for formatting purposes so that we preserve expected formatted - // string behavior. - quantity.adjustExponent(-1 * multiplier); - - // We already performed rounding. Do not perform it again. - micros.rounder = RoundingImpl::passThrough(); -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#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; +using namespace icu::number; +using namespace icu::number::impl; + +namespace { + +// A dummy object used when a "0" compact decimal entry is encountered. This is necessary +// in order to prevent falling back to root. Object equality ("==") is intended. +const char16_t *USE_FALLBACK = u""; + +/** Produces a string like "NumberElements/latn/patternsShort/decimalFormat". */ +void getResourceBundleKey(const char *nsName, CompactStyle compactStyle, CompactType compactType, + CharString &sb, UErrorCode &status) { + sb.clear(); + sb.append("NumberElements/", status); + sb.append(nsName, status); + sb.append(compactStyle == CompactStyle::UNUM_SHORT ? "/patternsShort" : "/patternsLong", status); + sb.append(compactType == CompactType::TYPE_DECIMAL ? "/decimalFormat" : "/currencyFormat", status); +} + +int32_t getIndex(int32_t magnitude, StandardPlural::Form plural) { + return magnitude * StandardPlural::COUNT + plural; +} + +int32_t countZeros(const char16_t *patternString, int32_t patternLength) { + // NOTE: This strategy for computing the number of zeros is a hack for efficiency. + // It could break if there are any 0s that aren't part of the main pattern. + int32_t numZeros = 0; + for (int32_t i = 0; i < patternLength; i++) { + if (patternString[i] == u'0') { + numZeros++; + } else if (numZeros > 0) { + break; // zeros should always be contiguous + } + } + return numZeros; +} + +} // namespace + +// NOTE: patterns and multipliers both get zero-initialized. +CompactData::CompactData() : patterns(), multipliers(), largestMagnitude(0), isEmpty(true) { +} + +void CompactData::populate(const Locale &locale, const char *nsName, CompactStyle compactStyle, + CompactType compactType, UErrorCode &status) { + CompactDataSink sink(*this); + LocalUResourceBundlePointer rb(ures_open(nullptr, locale.getName(), &status)); + if (U_FAILURE(status)) { return; } + + bool nsIsLatn = strcmp(nsName, "latn") == 0; + bool compactIsShort = compactStyle == CompactStyle::UNUM_SHORT; + + // Fall back to latn numbering system and/or short compact style. + CharString resourceKey; + getResourceBundleKey(nsName, compactStyle, compactType, resourceKey, status); + UErrorCode localStatus = U_ZERO_ERROR; + ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus); + if (isEmpty && !nsIsLatn) { + getResourceBundleKey("latn", compactStyle, compactType, resourceKey, status); + localStatus = U_ZERO_ERROR; + ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus); + } + if (isEmpty && !compactIsShort) { + getResourceBundleKey(nsName, CompactStyle::UNUM_SHORT, compactType, resourceKey, status); + localStatus = U_ZERO_ERROR; + ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus); + } + if (isEmpty && !nsIsLatn && !compactIsShort) { + getResourceBundleKey("latn", CompactStyle::UNUM_SHORT, compactType, resourceKey, status); + localStatus = U_ZERO_ERROR; + ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus); + } + + // The last fallback should be guaranteed to return data. + if (isEmpty) { + status = U_INTERNAL_PROGRAM_ERROR; + } +} + +int32_t CompactData::getMultiplier(int32_t magnitude) const { + if (magnitude < 0) { + return 0; + } + if (magnitude > largestMagnitude) { + magnitude = largestMagnitude; + } + return multipliers[magnitude]; +} + +const char16_t *CompactData::getPattern( + int32_t magnitude, + const PluralRules *rules, + const DecimalQuantity &dq) const { + if (magnitude < 0) { + return nullptr; + } + if (magnitude > largestMagnitude) { + magnitude = largestMagnitude; + } + const char16_t *patternString = nullptr; + if (dq.hasIntegerValue()) { + int64_t i = dq.toLong(true); + if (i == 0) { + patternString = patterns[getIndex(magnitude, StandardPlural::Form::EQ_0)]; + } else if (i == 1) { + patternString = patterns[getIndex(magnitude, StandardPlural::Form::EQ_1)]; + } + if (patternString != nullptr) { + return patternString; + } + } + StandardPlural::Form plural = utils::getStandardPlural(rules, dq); + patternString = patterns[getIndex(magnitude, plural)]; + if (patternString == nullptr && plural != StandardPlural::OTHER) { + // Fall back to "other" plural variant + patternString = patterns[getIndex(magnitude, StandardPlural::OTHER)]; + } + if (patternString == USE_FALLBACK) { // == is intended + // Return null if USE_FALLBACK is present + patternString = nullptr; + } + return patternString; +} + +void CompactData::getUniquePatterns(UVector &output, UErrorCode &status) const { + U_ASSERT(output.isEmpty()); + // NOTE: In C++, this is done more manually with a UVector. + // In Java, we can take advantage of JDK HashSet. + for (auto pattern : patterns) { + if (pattern == nullptr || pattern == USE_FALLBACK) { + continue; + } + + // Insert pattern into the UVector if the UVector does not already contain the pattern. + // Search the UVector from the end since identical patterns are likely to be adjacent. + for (int32_t i = output.size() - 1; i >= 0; i--) { + if (u_strcmp(pattern, static_cast(output[i])) == 0) { + goto continue_outer; + } + } + + // The string was not found; add it to the UVector. + // Note: must cast off const from pattern to store it in a UVector, which expects (void *) + output.addElement(const_cast(pattern), status); + + continue_outer: + continue; + } +} + +void CompactData::CompactDataSink::put(const char *key, ResourceValue &value, UBool /*noFallback*/, + UErrorCode &status) { + // traverse into the table of powers of ten + ResourceTable powersOfTenTable = value.getTable(status); + if (U_FAILURE(status)) { return; } + for (int i3 = 0; powersOfTenTable.getKeyAndValue(i3, key, value); ++i3) { + + // Assumes that the keys are always of the form "10000" where the magnitude is the + // length of the key minus one. We only support magnitudes less than COMPACT_MAX_DIGITS; + // ignore entries that have greater magnitude. + auto magnitude = static_cast (strlen(key) - 1); + U_ASSERT(magnitude < COMPACT_MAX_DIGITS); // debug assert + if (magnitude >= COMPACT_MAX_DIGITS) { // skip in production + continue; + } + int8_t multiplier = data.multipliers[magnitude]; + + // Iterate over the plural variants ("one", "other", etc) + ResourceTable pluralVariantsTable = value.getTable(status); + if (U_FAILURE(status)) { return; } + for (int i4 = 0; pluralVariantsTable.getKeyAndValue(i4, key, value); ++i4) { + // Skip this magnitude/plural if we already have it from a child locale. + // Note: This also skips USE_FALLBACK entries. + StandardPlural::Form plural = StandardPlural::fromString(key, status); + if (U_FAILURE(status)) { return; } + if (data.patterns[getIndex(magnitude, plural)] != nullptr) { + continue; + } + + // The value "0" means that we need to use the default pattern and not fall back + // to parent locales. Example locale where this is relevant: 'it'. + int32_t patternLength; + const char16_t *patternString = value.getString(patternLength, status); + if (U_FAILURE(status)) { return; } + if (u_strcmp(patternString, u"0") == 0) { + patternString = USE_FALLBACK; + patternLength = 0; + } + + // Save the pattern string. We will parse it lazily. + data.patterns[getIndex(magnitude, plural)] = patternString; + + // If necessary, compute the multiplier: the difference between the magnitude + // and the number of zeros in the pattern. + if (multiplier == 0) { + int32_t numZeros = countZeros(patternString, patternLength); + if (numZeros > 0) { // numZeros==0 in certain cases, like Somali "Kun" + multiplier = static_cast (numZeros - magnitude - 1); + } + } + } + + // Save the multiplier. + if (data.multipliers[magnitude] == 0) { + data.multipliers[magnitude] = multiplier; + if (magnitude > data.largestMagnitude) { + data.largestMagnitude = magnitude; + } + data.isEmpty = false; + } else { + U_ASSERT(data.multipliers[magnitude] == multiplier); + } + } +} + +/////////////////////////////////////////////////////////// +/// END OF CompactData.java; BEGIN CompactNotation.java /// +/////////////////////////////////////////////////////////// + +CompactHandler::CompactHandler( + CompactStyle compactStyle, + const Locale &locale, + const char *nsName, + CompactType compactType, + const PluralRules *rules, + MutablePatternModifier *buildReference, + bool safe, + const MicroPropsGenerator *parent, + UErrorCode &status) + : rules(rules), parent(parent), safe(safe) { + data.populate(locale, nsName, compactStyle, compactType, status); + if (safe) { + // Safe code path + precomputeAllModifiers(*buildReference, status); + } else { + // Unsafe code path + // Store the MutablePatternModifier reference. + unsafePatternModifier = buildReference; + } +} + +CompactHandler::~CompactHandler() { + for (int32_t i = 0; i < precomputedModsLength; i++) { + delete precomputedMods[i].mod; + } +} + +void CompactHandler::precomputeAllModifiers(MutablePatternModifier &buildReference, UErrorCode &status) { + if (U_FAILURE(status)) { return; } + + // Initial capacity of 12 for 0K, 00K, 000K, ...M, ...B, and ...T + UVector allPatterns(12, status); + if (U_FAILURE(status)) { return; } + data.getUniquePatterns(allPatterns, status); + if (U_FAILURE(status)) { return; } + + // C++ only: ensure that precomputedMods has room. + precomputedModsLength = allPatterns.size(); + if (precomputedMods.getCapacity() < precomputedModsLength) { + precomputedMods.resize(allPatterns.size(), status); + if (U_FAILURE(status)) { return; } + } + + for (int32_t i = 0; i < precomputedModsLength; i++) { + auto patternString = static_cast(allPatterns[i]); + UnicodeString hello(patternString); + CompactModInfo &info = precomputedMods[i]; + ParsedPatternInfo patternInfo; + PatternParser::parseToPatternInfo(UnicodeString(patternString), patternInfo, status); + if (U_FAILURE(status)) { return; } + buildReference.setPatternInfo(&patternInfo, {UFIELD_CATEGORY_NUMBER, UNUM_COMPACT_FIELD}); + info.mod = buildReference.createImmutable(status); + if (U_FAILURE(status)) { return; } + info.patternString = patternString; + } +} + +void CompactHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, + UErrorCode &status) const { + parent->processQuantity(quantity, micros, status); + if (U_FAILURE(status)) { return; } + + // Treat zero, NaN, and infinity as if they had magnitude 0 + int32_t magnitude; + int32_t multiplier = 0; + if (quantity.isZeroish()) { + magnitude = 0; + micros.rounder.apply(quantity, status); + } else { + // TODO: Revisit chooseMultiplierAndApply + multiplier = micros.rounder.chooseMultiplierAndApply(quantity, data, status); + magnitude = quantity.isZeroish() ? 0 : quantity.getMagnitude(); + magnitude -= multiplier; + } + + const char16_t *patternString = data.getPattern(magnitude, rules, quantity); + if (patternString == nullptr) { + // Use the default (non-compact) modifier. + // No need to take any action. + } else if (safe) { + // Safe code path. + // Java uses a hash set here for O(1) lookup. C++ uses a linear search. + // TODO: Benchmark this and maybe change to a binary search or hash table. + int32_t i = 0; + for (; i < precomputedModsLength; i++) { + const CompactModInfo &info = precomputedMods[i]; + if (u_strcmp(patternString, info.patternString) == 0) { + info.mod->applyToMicros(micros, quantity, status); + break; + } + } + // It should be guaranteed that we found the entry. + U_ASSERT(i < precomputedModsLength); + } else { + // Unsafe code path. + // Overwrite the PatternInfo in the existing modMiddle. + // C++ Note: Use unsafePatternInfo for proper lifecycle. + ParsedPatternInfo &patternInfo = const_cast(this)->unsafePatternInfo; + PatternParser::parseToPatternInfo(UnicodeString(patternString), patternInfo, status); + unsafePatternModifier->setPatternInfo( + &unsafePatternInfo, + {UFIELD_CATEGORY_NUMBER, UNUM_COMPACT_FIELD}); + unsafePatternModifier->setNumberProperties(quantity.signum(), StandardPlural::Form::COUNT); + micros.modMiddle = unsafePatternModifier; + } + + // Change the exponent only after we select appropriate plural form + // for formatting purposes so that we preserve expected formatted + // string behavior. + quantity.adjustExponent(-1 * multiplier); + + // We already performed rounding. Do not perform it again. + 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 fa29744c103a38..bd323df7ab8ba4 100644 --- a/deps/icu-small/source/i18n/number_compact.h +++ b/deps/icu-small/source/i18n/number_compact.h @@ -1,100 +1,100 @@ -// © 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_COMPACT_H__ -#define __NUMBER_COMPACT_H__ - -#include "standardplural.h" -#include "number_types.h" -#include "unicode/unum.h" -#include "uvector.h" -#include "resource.h" -#include "number_patternmodifier.h" - -U_NAMESPACE_BEGIN namespace number { -namespace impl { - -static const int32_t COMPACT_MAX_DIGITS = 20; - -class CompactData : public MultiplierProducer { - public: - CompactData(); - - void populate(const Locale &locale, const char *nsName, CompactStyle compactStyle, - CompactType compactType, UErrorCode &status); - - int32_t getMultiplier(int32_t magnitude) const U_OVERRIDE; - - const UChar *getPattern( - int32_t magnitude, - const PluralRules *rules, - const DecimalQuantity &dq) const; - - void getUniquePatterns(UVector &output, UErrorCode &status) const; - - private: - const UChar *patterns[(COMPACT_MAX_DIGITS + 1) * StandardPlural::COUNT]; - int8_t multipliers[COMPACT_MAX_DIGITS + 1]; - int8_t largestMagnitude; - UBool isEmpty; - - class CompactDataSink : public ResourceSink { - public: - explicit CompactDataSink(CompactData &data) : data(data) {} - - void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) U_OVERRIDE; - - private: - CompactData &data; - }; -}; - -struct CompactModInfo { - const ImmutablePatternModifier *mod; - const UChar* patternString; -}; - -class CompactHandler : public MicroPropsGenerator, public UMemory { - public: - CompactHandler( - CompactStyle compactStyle, - const Locale &locale, - const char *nsName, - CompactType compactType, - const PluralRules *rules, - MutablePatternModifier *buildReference, - bool safe, - const MicroPropsGenerator *parent, - UErrorCode &status); - - ~CompactHandler() U_OVERRIDE; - - void - processQuantity(DecimalQuantity &quantity, MicroProps µs, UErrorCode &status) const U_OVERRIDE; - - private: - const PluralRules *rules; - const MicroPropsGenerator *parent; - // Initial capacity of 12 for 0K, 00K, 000K, ...M, ...B, and ...T - MaybeStackArray precomputedMods; - int32_t precomputedModsLength = 0; - CompactData data; - ParsedPatternInfo unsafePatternInfo; - MutablePatternModifier* unsafePatternModifier; - UBool safe; - - /** Used by the safe code path */ - void precomputeAllModifiers(MutablePatternModifier &buildReference, UErrorCode &status); -}; - - -} // namespace impl -} // namespace number -U_NAMESPACE_END - -#endif //__NUMBER_COMPACT_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_COMPACT_H__ +#define __NUMBER_COMPACT_H__ + +#include "standardplural.h" +#include "number_types.h" +#include "unicode/unum.h" +#include "uvector.h" +#include "resource.h" +#include "number_patternmodifier.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +static const int32_t COMPACT_MAX_DIGITS = 20; + +class CompactData : public MultiplierProducer { + public: + CompactData(); + + void populate(const Locale &locale, const char *nsName, CompactStyle compactStyle, + CompactType compactType, UErrorCode &status); + + int32_t getMultiplier(int32_t magnitude) const override; + + const char16_t *getPattern( + int32_t magnitude, + const PluralRules *rules, + const DecimalQuantity &dq) const; + + void getUniquePatterns(UVector &output, UErrorCode &status) const; + + private: + const char16_t *patterns[(COMPACT_MAX_DIGITS + 1) * StandardPlural::COUNT]; + int8_t multipliers[COMPACT_MAX_DIGITS + 1]; + int8_t largestMagnitude; + UBool isEmpty; + + class CompactDataSink : public ResourceSink { + public: + explicit CompactDataSink(CompactData &data) : data(data) {} + + void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override; + + private: + CompactData &data; + }; +}; + +struct CompactModInfo { + const ImmutablePatternModifier *mod; + const char16_t* patternString; +}; + +class CompactHandler : public MicroPropsGenerator, public UMemory { + public: + CompactHandler( + CompactStyle compactStyle, + const Locale &locale, + const char *nsName, + CompactType compactType, + const PluralRules *rules, + MutablePatternModifier *buildReference, + bool safe, + const MicroPropsGenerator *parent, + UErrorCode &status); + + ~CompactHandler() override; + + void + processQuantity(DecimalQuantity &quantity, MicroProps µs, UErrorCode &status) const override; + + private: + const PluralRules *rules; + const MicroPropsGenerator *parent; + // Initial capacity of 12 for 0K, 00K, 000K, ...M, ...B, and ...T + MaybeStackArray precomputedMods; + int32_t precomputedModsLength = 0; + CompactData data; + ParsedPatternInfo unsafePatternInfo; + MutablePatternModifier* unsafePatternModifier; + UBool safe; + + /** Used by the safe code path */ + void precomputeAllModifiers(MutablePatternModifier &buildReference, UErrorCode &status); +}; + + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif //__NUMBER_COMPACT_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_currencysymbols.cpp b/deps/icu-small/source/i18n/number_currencysymbols.cpp index 8d5127556be91a..6a07b7cc464351 100644 --- a/deps/icu-small/source/i18n/number_currencysymbols.cpp +++ b/deps/icu-small/source/i18n/number_currencysymbols.cpp @@ -1,135 +1,135 @@ -// © 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::getFormalCurrencySymbol(UErrorCode& status) const { - // Note: currently no override is available for formal currency symbol - return loadSymbol(UCURR_FORMAL_SYMBOL_NAME, status); -} - -UnicodeString CurrencySymbols::getVariantCurrencySymbol(UErrorCode& status) const { - // Note: currently no override is available for variant currency symbol - return loadSymbol(UCURR_VARIANT_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(); - int32_t symbolLen = 0; - const char16_t* symbol = ucurr_getName( - isoCode, - fLocaleName.data(), - selector, - nullptr /* isChoiceFormat */, - &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(); - int32_t symbolLen = 0; - const char16_t* symbol = ucurr_getPluralName( - isoCode, - fLocaleName.data(), - nullptr /* 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); - } -} - -bool CurrencySymbols::hasEmptyCurrencySymbol() const { - return !fCurrencySymbol.isBogus() && fCurrencySymbol.isEmpty(); -} - - -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 */ +// © 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::getFormalCurrencySymbol(UErrorCode& status) const { + // Note: currently no override is available for formal currency symbol + return loadSymbol(UCURR_FORMAL_SYMBOL_NAME, status); +} + +UnicodeString CurrencySymbols::getVariantCurrencySymbol(UErrorCode& status) const { + // Note: currently no override is available for variant currency symbol + return loadSymbol(UCURR_VARIANT_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(); + int32_t symbolLen = 0; + const char16_t* symbol = ucurr_getName( + isoCode, + fLocaleName.data(), + selector, + nullptr /* isChoiceFormat */, + &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(); + int32_t symbolLen = 0; + const char16_t* symbol = ucurr_getPluralName( + isoCode, + fLocaleName.data(), + nullptr /* 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); + } +} + +bool CurrencySymbols::hasEmptyCurrencySymbol() const { + return !fCurrencySymbol.isBogus() && fCurrencySymbol.isEmpty(); +} + + +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 index c2223bd0f0be5c..adb89be018de00 100644 --- a/deps/icu-small/source/i18n/number_currencysymbols.h +++ b/deps/icu-small/source/i18n/number_currencysymbols.h @@ -1,71 +1,71 @@ -// © 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 getFormalCurrencySymbol(UErrorCode& status) const; - - UnicodeString getVariantCurrencySymbol(UErrorCode& status) const; - - UnicodeString getCurrencySymbol(UErrorCode& status) const; - - UnicodeString getIntlCurrencySymbol(UErrorCode& status) const; - - UnicodeString getPluralName(StandardPlural::Form plural, UErrorCode& status) const; - - bool hasEmptyCurrencySymbol() 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 */ +// © 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 getFormalCurrencySymbol(UErrorCode& status) const; + + UnicodeString getVariantCurrencySymbol(UErrorCode& status) const; + + UnicodeString getCurrencySymbol(UErrorCode& status) const; + + UnicodeString getIntlCurrencySymbol(UErrorCode& status) const; + + UnicodeString getPluralName(StandardPlural::Form plural, UErrorCode& status) const; + + bool hasEmptyCurrencySymbol() 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 b40e1276c350fe..be1cc0bcd0ce44 100644 --- a/deps/icu-small/source/i18n/number_decimalquantity.cpp +++ b/deps/icu-small/source/i18n/number_decimalquantity.cpp @@ -1,1474 +1,1474 @@ -// © 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 - -#include -#include -#include -#include - -#include "unicode/plurrule.h" -#include "cmemory.h" -#include "number_decnum.h" -#include "putilimp.h" -#include "number_decimalquantity.h" -#include "number_roundingutils.h" -#include "double-conversion.h" -#include "charstr.h" -#include "number_utils.h" -#include "uassert.h" -#include "util.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - -using icu::double_conversion::DoubleToStringConverter; -using icu::double_conversion::StringToDoubleConverter; - -namespace { - -int8_t NEGATIVE_FLAG = 1; -int8_t INFINITY_FLAG = 2; -int8_t NAN_FLAG = 4; - -/** 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. - int32_t diff = static_cast(static_cast(a) - static_cast(b)); - if (b < 0 && diff < a) { return INT32_MAX; } - if (b > 0 && diff > a) { return INT32_MIN; } - return diff; -} - -static double DOUBLE_MULTIPLIERS[] = { - 1e0, - 1e1, - 1e2, - 1e3, - 1e4, - 1e5, - 1e6, - 1e7, - 1e8, - 1e9, - 1e10, - 1e11, - 1e12, - 1e13, - 1e14, - 1e15, - 1e16, - 1e17, - 1e18, - 1e19, - 1e20, - 1e21}; - -} // namespace - -icu::IFixedDecimal::~IFixedDecimal() = default; - -DecimalQuantity::DecimalQuantity() { - setBcdToZero(); - flags = 0; -} - -DecimalQuantity::~DecimalQuantity() { - if (usingBytes) { - uprv_free(fBCD.bcdBytes.ptr); - fBCD.bcdBytes.ptr = nullptr; - usingBytes = false; - } -} - -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; - lReqPos = other.lReqPos; - rReqPos = other.rReqPos; - scale = other.scale; - precision = other.precision; - flags = other.flags; - origDouble = other.origDouble; - origDelta = other.origDelta; - isApproximate = other.isApproximate; - exponent = other.exponent; -} - -void DecimalQuantity::clear() { - lReqPos = 0; - rReqPos = 0; - flags = 0; - setBcdToZero(); // sets scale, precision, hasDouble, origDouble, origDelta, and BCD data -} - -void DecimalQuantity::setMinInteger(int32_t minInt) { - // Validation should happen outside of DecimalQuantity, e.g., in the Precision class. - U_ASSERT(minInt >= 0); - - // 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 - lReqPos = minInt; -} - -void DecimalQuantity::setMinFraction(int32_t minFrac) { - // Validation should happen outside of DecimalQuantity, e.g., in the Precision class. - U_ASSERT(minFrac >= 0); - - // Save values into internal state - // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE - rReqPos = -minFrac; -} - -void DecimalQuantity::applyMaxInteger(int32_t maxInt) { - // Validation should happen outside of DecimalQuantity, e.g., in the Precision class. - U_ASSERT(maxInt >= 0); - - if (precision == 0) { - return; - } - - if (maxInt <= scale) { - setBcdToZero(); - return; - } - - int32_t magnitude = getMagnitude(); - if (maxInt <= magnitude) { - popFromLeft(magnitude - maxInt + 1); - compact(); - } -} - -uint64_t DecimalQuantity::getPositionFingerprint() const { - uint64_t fingerprint = 0; - fingerprint ^= (lReqPos << 16); - fingerprint ^= (static_cast(rReqPos) << 32); - return fingerprint; -} - -void DecimalQuantity::roundToIncrement( - uint64_t increment, - digits_t magnitude, - RoundingMode roundingMode, - UErrorCode& status) { - // Do not call this method with an increment having only a 1 or a 5 digit! - // Use a more efficient call to either roundToMagnitude() or roundToNickel(). - // Check a few popular rounding increments; a more thorough check is in Java. - U_ASSERT(increment != 1); - U_ASSERT(increment != 5); - - DecimalQuantity incrementDQ; - incrementDQ.setToLong(increment); - incrementDQ.adjustMagnitude(magnitude); - DecNum incrementDN; - incrementDQ.toDecNum(incrementDN, status); - if (U_FAILURE(status)) { return; } - - // Divide this DecimalQuantity by the increment, round, then multiply back. - divideBy(incrementDN, status); - if (U_FAILURE(status)) { return; } - roundToMagnitude(0, roundingMode, status); - if (U_FAILURE(status)) { return; } - multiplyBy(incrementDN, status); - if (U_FAILURE(status)) { return; } -} - -void DecimalQuantity::multiplyBy(const DecNum& multiplicand, UErrorCode& status) { - if (isZeroish()) { - return; - } - // 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 (isZeroish()) { - 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 { - U_ASSERT(precision != 0); - return scale + precision - 1; -} - -bool DecimalQuantity::adjustMagnitude(int32_t delta) { - if (precision != 0) { - // 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; -} - -int32_t DecimalQuantity::adjustToZeroScale() { - int32_t retval = scale; - scale = 0; - return retval; -} - -double DecimalQuantity::getPluralOperand(PluralOperand operand) const { - // If this assertion fails, you need to call roundToInfinity() or some other rounding method. - // See the comment at the top of this file explaining the "isApproximate" field. - U_ASSERT(!isApproximate); - - switch (operand) { - case PLURAL_OPERAND_I: - // 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: - return static_cast(toFractionLong(false)); - case PLURAL_OPERAND_V: - return fractionCount(); - case PLURAL_OPERAND_W: - return fractionCountWithoutTrailingZeros(); - case PLURAL_OPERAND_E: - return static_cast(getExponent()); - case PLURAL_OPERAND_C: - // Plural operand `c` is currently an alias for `e`. - return static_cast(getExponent()); - default: - return std::abs(toDouble()); - } -} - -int32_t DecimalQuantity::getExponent() const { - return exponent; -} - -void DecimalQuantity::adjustExponent(int delta) { - exponent = exponent + delta; -} - -void DecimalQuantity::resetExponent() { - adjustMagnitude(exponent); - exponent = 0; -} - -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. - U_ASSERT(!isApproximate); - - int32_t magnitude = scale + precision; - int32_t result = (lReqPos > magnitude) ? lReqPos : magnitude; - return result - 1; -} - -int32_t DecimalQuantity::getLowerDisplayMagnitude() 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); - - int32_t magnitude = scale; - int32_t result = (rReqPos < magnitude) ? rReqPos : magnitude; - return result; -} - -int8_t DecimalQuantity::getDigit(int32_t magnitude) const { - // If this assertion fails, you need to call roundToInfinity() or some other rounding method. - // See the comment at the top of this file explaining the "isApproximate" field. - U_ASSERT(!isApproximate); - - return getDigitPos(magnitude - scale); -} - -int32_t DecimalQuantity::fractionCount() const { - int32_t fractionCountWithExponent = -getLowerDisplayMagnitude() - exponent; - return fractionCountWithExponent > 0 ? fractionCountWithExponent : 0; -} - -int32_t DecimalQuantity::fractionCountWithoutTrailingZeros() const { - int32_t fractionCountWithExponent = -scale - exponent; - return fractionCountWithExponent > 0 ? fractionCountWithExponent : 0; // max(-fractionCountWithExponent, 0) -} - -bool DecimalQuantity::isNegative() const { - return (flags & NEGATIVE_FLAG) != 0; -} - -Signum DecimalQuantity::signum() const { - bool isZero = (isZeroish() && !isInfinite()); - bool isNeg = isNegative(); - if (isZero && isNeg) { - return SIGNUM_NEG_ZERO; - } else if (isZero) { - return SIGNUM_POS_ZERO; - } else if (isNeg) { - return SIGNUM_NEG; - } else { - return SIGNUM_POS; - } -} - -bool DecimalQuantity::isInfinite() const { - return (flags & INFINITY_FLAG) != 0; -} - -bool DecimalQuantity::isNaN() const { - return (flags & NAN_FLAG) != 0; -} - -bool DecimalQuantity::isZeroish() const { - return precision == 0; -} - -DecimalQuantity &DecimalQuantity::setToInt(int32_t n) { - setBcdToZero(); - flags = 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; - } - if (n != 0) { - _setToInt(n); - compact(); - } - return *this; -} - -void DecimalQuantity::_setToInt(int32_t n) { - if (n == INT32_MIN) { - readLongToBcd(-static_cast(n)); - } else { - readIntToBcd(n); - } -} - -DecimalQuantity &DecimalQuantity::setToLong(int64_t n) { - setBcdToZero(); - flags = 0; - if (n < 0 && n > INT64_MIN) { - flags |= NEGATIVE_FLAG; - n = -n; - } - if (n != 0) { - _setToLong(n); - compact(); - } - return *this; -} - -void DecimalQuantity::_setToLong(int64_t n) { - if (n == INT64_MIN) { - 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 { - readLongToBcd(n); - } -} - -DecimalQuantity &DecimalQuantity::setToDouble(double n) { - setBcdToZero(); - flags = 0; - // signbit() from handles +0.0 vs -0.0 - if (std::signbit(n)) { - flags |= NEGATIVE_FLAG; - n = -n; - } - if (std::isnan(n) != 0) { - flags |= NAN_FLAG; - } else if (std::isfinite(n) == 0) { - flags |= INFINITY_FLAG; - } else if (n != 0) { - _setToDoubleFast(n); - compact(); - } - return *this; -} - -void DecimalQuantity::_setToDoubleFast(double n) { - isApproximate = true; - origDouble = n; - origDelta = 0; - - // Make sure the double is an IEEE 754 double. If not, fall back to the slow path right now. - // TODO: Make a fast path for other types of doubles. - if (!std::numeric_limits::is_iec559) { - convertToAccurateDouble(); - return; - } - - // To get the bits from the double, use memcpy, which takes care of endianness. - uint64_t ieeeBits; - uprv_memcpy(&ieeeBits, &n, sizeof(n)); - int32_t exponent = static_cast((ieeeBits & 0x7ff0000000000000L) >> 52) - 0x3ff; - - // Not all integers can be represented exactly for exponent > 52 - if (exponent <= 52 && static_cast(n) == n) { - _setToLong(static_cast(n)); - return; - } - - if (exponent == -1023 || exponent == 1024) { - // The extreme values of exponent are special; use slow path. - convertToAccurateDouble(); - return; - } - - // 3.3219... is log2(10) - auto fracLength = static_cast ((52 - exponent) / 3.32192809488736234787031942948939017586); - if (fracLength >= 0) { - int32_t i = fracLength; - // 1e22 is the largest exact double. - for (; i >= 22; i -= 22) n *= 1e22; - n *= DOUBLE_MULTIPLIERS[i]; - } else { - int32_t i = fracLength; - // 1e22 is the largest exact double. - for (; i <= -22; i += 22) n /= 1e22; - n /= DOUBLE_MULTIPLIERS[-i]; - } - auto result = static_cast(uprv_round(n)); - if (result != 0) { - _setToLong(result); - scale -= fracLength; - } -} - -void DecimalQuantity::convertToAccurateDouble() { - U_ASSERT(origDouble != 0); - int32_t delta = origDelta; - - // Call the slow oracle function (Double.toString in Java, DoubleToAscii in C++). - char buffer[DoubleToStringConverter::kBase10MaximalLength + 1]; - bool sign; // unused; always positive - int32_t length; - int32_t point; - DoubleToStringConverter::DoubleToAscii( - origDouble, - DoubleToStringConverter::DtoaMode::SHORTEST, - 0, - buffer, - sizeof(buffer), - &sign, - &length, - &point - ); - - setBcdToZero(); - readDoubleConversionToBcd(buffer, length, point); - scale += delta; - explicitExactDouble = true; -} - -DecimalQuantity &DecimalQuantity::setToDecNumber(StringPiece n, UErrorCode& status) { - setBcdToZero(); - flags = 0; - - // Compute the decNumber representation - DecNum decnum; - decnum.setTo(n, status); - - _setToDecNum(decnum, status); - return *this; -} - -DecimalQuantity& DecimalQuantity::setToDecNum(const DecNum& decnum, UErrorCode& status) { - setBcdToZero(); - flags = 0; - - _setToDecNum(decnum, status); - return *this; -} - -void DecimalQuantity::_setToDecNum(const DecNum& decnum, UErrorCode& status) { - if (U_FAILURE(status)) { return; } - if (decnum.isNegative()) { - flags |= NEGATIVE_FLAG; - } - if (decnum.isNaN()) { - flags |= NAN_FLAG; - } else if (decnum.isInfinity()) { - flags |= INFINITY_FLAG; - } else if (!decnum.isZero()) { - readDecNumberToBcd(decnum); - compact(); - } -} - -DecimalQuantity DecimalQuantity::fromExponentString(UnicodeString num, UErrorCode& status) { - if (num.indexOf(u'e') >= 0 || num.indexOf(u'c') >= 0 - || num.indexOf(u'E') >= 0 || num.indexOf(u'C') >= 0) { - int32_t ePos = num.lastIndexOf('e'); - if (ePos < 0) { - ePos = num.lastIndexOf('c'); - } - if (ePos < 0) { - ePos = num.lastIndexOf('E'); - } - if (ePos < 0) { - ePos = num.lastIndexOf('C'); - } - int32_t expNumPos = ePos + 1; - UnicodeString exponentStr = num.tempSubString(expNumPos, num.length() - expNumPos); - - // parse exponentStr into exponent, but note that parseAsciiInteger doesn't handle the minus sign - bool isExpStrNeg = num[expNumPos] == u'-'; - int32_t exponentParsePos = isExpStrNeg ? 1 : 0; - int32_t exponent = ICU_Utility::parseAsciiInteger(exponentStr, exponentParsePos); - exponent = isExpStrNeg ? -exponent : exponent; - - // Compute the decNumber representation - UnicodeString fractionStr = num.tempSubString(0, ePos); - CharString fracCharStr = CharString(); - fracCharStr.appendInvariantChars(fractionStr, status); - DecNum decnum; - decnum.setTo(fracCharStr.toStringPiece(), status); - - // Clear and set this DecimalQuantity instance - DecimalQuantity dq; - dq.setToDecNum(decnum, status); - int32_t numFracDigit = getVisibleFractionCount(fractionStr); - dq.setMinFraction(numFracDigit); - dq.adjustExponent(exponent); - - return dq; - } else { - DecimalQuantity dq; - int numFracDigit = getVisibleFractionCount(num); - - CharString numCharStr = CharString(); - numCharStr.appendInvariantChars(num, status); - dq.setToDecNumber(numCharStr.toStringPiece(), status); - - dq.setMinFraction(numFracDigit); - return dq; - } -} - -int32_t DecimalQuantity::getVisibleFractionCount(UnicodeString value) { - int decimalPos = value.indexOf('.') + 1; - if (decimalPos == 0) { - return 0; - } else { - return value.length() - decimalPos; - } -} - -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 = exponent + scale + precision - 1; - if (truncateIfOverflow) { - upperMagnitude = std::min(upperMagnitude, 17); - } - for (int32_t magnitude = upperMagnitude; magnitude >= 0; magnitude--) { - result = result * 10 + getDigitPos(magnitude - scale - exponent); - } - if (isNegative()) { - return static_cast(0LL - result); // i.e., -result - } - return static_cast(result); -} - -uint64_t DecimalQuantity::toFractionLong(bool includeTrailingZeros) const { - uint64_t result = 0L; - int32_t magnitude = -1 - exponent; - int32_t lowerMagnitude = scale; - 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; -} - -bool DecimalQuantity::fitsInLong(bool ignoreFraction) const { - if (isInfinite() || isNaN()) { - return false; - } - if (isZeroish()) { - return true; - } - if (exponent + 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; - } else if (isInfinite()) { - return isNegative() ? -INFINITY : INFINITY; - } - - // 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); -} - -DecNum& DecimalQuantity::toDecNum(DecNum& output, UErrorCode& status) const { - // Special handling for zero - if (precision == 0) { - output.setTo("0", status); - return output; - } - - // 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, status); - if (U_FAILURE(status)) { - return output; - } - for (int32_t m = 0; m < precision; m++) { - ubcd[precision - m - 1] = static_cast(getDigitPos(m)); - } - output.setTo(ubcd.getAlias(), precision, scale, isNegative(), status); - return output; -} - -void DecimalQuantity::truncate() { - if (scale < 0) { - shiftRight(-scale); - scale = 0; - compact(); - } -} - -void DecimalQuantity::roundToNickel(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status) { - roundToMagnitude(magnitude, roundingMode, true, status); -} - -void DecimalQuantity::roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status) { - roundToMagnitude(magnitude, roundingMode, false, status); -} - -void DecimalQuantity::roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, bool nickel, UErrorCode& status) { - // The position in the BCD at which rounding will be performed; digits to the right of position - // will be rounded away. - int position = safeSubtract(magnitude, scale); - - // "trailing" = least significant digit to the left of rounding - int8_t trailingDigit = getDigitPos(position); - - if (position <= 0 && !isApproximate && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { - // All digits are to the left of the rounding magnitude. - } else if (precision == 0) { - // No rounding for zero. - } else { - // Perform rounding logic. - // "leading" = most significant digit to the right of rounding - int8_t leadingDigit = getDigitPos(safeSubtract(position, 1)); - - // Compute which section of the number we are in. - // EDGE means we are at the bottom or top edge, like 1.000 or 1.999 (used by doubles) - // LOWER means we are between the bottom edge and the midpoint, like 1.391 - // MIDPOINT means we are exactly in the middle, like 1.500 - // UPPER means we are between the midpoint and the top edge, like 1.916 - roundingutils::Section section; - if (!isApproximate) { - if (nickel && trailingDigit != 2 && trailingDigit != 7) { - // Nickel rounding, and not at .02x or .07x - if (trailingDigit < 2) { - // .00, .01 => down to .00 - section = roundingutils::SECTION_LOWER; - } else if (trailingDigit < 5) { - // .03, .04 => up to .05 - section = roundingutils::SECTION_UPPER; - } else if (trailingDigit < 7) { - // .05, .06 => down to .05 - section = roundingutils::SECTION_LOWER; - } else { - // .08, .09 => up to .10 - section = roundingutils::SECTION_UPPER; - } - } else if (leadingDigit < 5) { - // Includes nickel rounding .020-.024 and .070-.074 - section = roundingutils::SECTION_LOWER; - } else if (leadingDigit > 5) { - // Includes nickel rounding .026-.029 and .076-.079 - section = roundingutils::SECTION_UPPER; - } else { - // Includes nickel rounding .025 and .075 - section = roundingutils::SECTION_MIDPOINT; - for (int p = safeSubtract(position, 2); p >= 0; p--) { - if (getDigitPos(p) != 0) { - section = roundingutils::SECTION_UPPER; - break; - } - } - } - } else { - int32_t p = safeSubtract(position, 2); - int32_t minP = uprv_max(0, precision - 14); - if (leadingDigit == 0 && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { - section = roundingutils::SECTION_LOWER_EDGE; - for (; p >= minP; p--) { - if (getDigitPos(p) != 0) { - section = roundingutils::SECTION_LOWER; - break; - } - } - } else if (leadingDigit == 4 && (!nickel || trailingDigit == 2 || trailingDigit == 7)) { - section = roundingutils::SECTION_MIDPOINT; - for (; p >= minP; p--) { - if (getDigitPos(p) != 9) { - section = roundingutils::SECTION_LOWER; - break; - } - } - } else if (leadingDigit == 5 && (!nickel || trailingDigit == 2 || trailingDigit == 7)) { - section = roundingutils::SECTION_MIDPOINT; - for (; p >= minP; p--) { - if (getDigitPos(p) != 0) { - section = roundingutils::SECTION_UPPER; - break; - } - } - } else if (leadingDigit == 9 && (!nickel || trailingDigit == 4 || trailingDigit == 9)) { - section = roundingutils::SECTION_UPPER_EDGE; - for (; p >= minP; p--) { - if (getDigitPos(p) != 9) { - section = roundingutils::SECTION_UPPER; - break; - } - } - } else if (nickel && trailingDigit != 2 && trailingDigit != 7) { - // Nickel rounding, and not at .02x or .07x - if (trailingDigit < 2) { - // .00, .01 => down to .00 - section = roundingutils::SECTION_LOWER; - } else if (trailingDigit < 5) { - // .03, .04 => up to .05 - section = roundingutils::SECTION_UPPER; - } else if (trailingDigit < 7) { - // .05, .06 => down to .05 - section = roundingutils::SECTION_LOWER; - } else { - // .08, .09 => up to .10 - section = roundingutils::SECTION_UPPER; - } - } else if (leadingDigit < 5) { - // Includes nickel rounding .020-.024 and .070-.074 - section = roundingutils::SECTION_LOWER; - } else { - // Includes nickel rounding .026-.029 and .076-.079 - section = roundingutils::SECTION_UPPER; - } - - bool roundsAtMidpoint = roundingutils::roundsAtMidpoint(roundingMode); - if (safeSubtract(position, 1) < precision - 14 || - (roundsAtMidpoint && section == roundingutils::SECTION_MIDPOINT) || - (!roundsAtMidpoint && section < 0 /* i.e. at upper or lower edge */)) { - // Oops! This means that we have to get the exact representation of the double, - // because the zone of uncertainty is along the rounding boundary. - convertToAccurateDouble(); - roundToMagnitude(magnitude, roundingMode, nickel, status); // start over - return; - } - - // Turn off the approximate double flag, since the value is now confirmed to be exact. - isApproximate = false; - origDouble = 0.0; - origDelta = 0; - - if (position <= 0 && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { - // All digits are to the left of the rounding magnitude. - return; - } - - // Good to continue rounding. - if (section == -1) { section = roundingutils::SECTION_LOWER; } - if (section == -2) { section = roundingutils::SECTION_UPPER; } - } - - // Nickel rounding "half even" goes to the nearest whole (away from the 5). - bool isEven = nickel - ? (trailingDigit < 2 || trailingDigit > 7 - || (trailingDigit == 2 && section != roundingutils::SECTION_UPPER) - || (trailingDigit == 7 && section == roundingutils::SECTION_UPPER)) - : (trailingDigit % 2) == 0; - - bool roundDown = roundingutils::getRoundingDirection(isEven, - isNegative(), - section, - roundingMode, - status); - if (U_FAILURE(status)) { - return; - } - - // Perform truncation - if (position >= precision) { - U_ASSERT(trailingDigit == 0); - setBcdToZero(); - scale = magnitude; - } else { - shiftRight(position); - } - - if (nickel) { - if (trailingDigit < 5 && roundDown) { - setDigitPos(0, 0); - compact(); - return; - } else if (trailingDigit >= 5 && !roundDown) { - setDigitPos(0, 9); - trailingDigit = 9; - // do not return: use the bubbling logic below - } else { - setDigitPos(0, 5); - // If the quantity was set to 0, we may need to restore a digit. - if (precision == 0) { - precision = 1; - } - // compact not necessary: digit at position 0 is nonzero - return; - } - } - - // Bubble the result to the higher digits - if (!roundDown) { - if (trailingDigit == 9) { - int bubblePos = 0; - // Note: in the long implementation, the most digits BCD can have at this point is - // 15, so bubblePos <= 15 and getDigitPos(bubblePos) is safe. - for (; getDigitPos(bubblePos) == 9; bubblePos++) {} - shiftRight(bubblePos); // shift off the trailing 9s - } - int8_t digit0 = getDigitPos(0); - U_ASSERT(digit0 != 9); - setDigitPos(0, static_cast(digit0 + 1)); - precision += 1; // in case an extra digit got added - } - - compact(); - } -} - -void DecimalQuantity::roundToInfinity() { - if (isApproximate) { - convertToAccurateDouble(); - } -} - -void DecimalQuantity::appendDigit(int8_t value, int32_t leadingZeros, bool appendAsInteger) { - U_ASSERT(leadingZeros >= 0); - - // Zero requires special handling to maintain the invariant that the least-significant digit - // in the BCD is nonzero. - if (value == 0) { - if (appendAsInteger && precision != 0) { - scale += leadingZeros + 1; - } - return; - } - - // Deal with trailing zeros - if (scale > 0) { - leadingZeros += scale; - if (appendAsInteger) { - scale = 0; - } - } - - // Append digit - shiftLeft(leadingZeros + 1); - setDigitPos(0, value); - - // Fix scale if in integer mode - if (appendAsInteger) { - scale += leadingZeros + 1; - } -} - -UnicodeString DecimalQuantity::toPlainString() const { - U_ASSERT(!isApproximate); - UnicodeString sb; - if (isNegative()) { - sb.append(u'-'); - } - if (precision == 0) { - sb.append(u'0'); - return sb; - } - int32_t upper = scale + precision + exponent - 1; - int32_t lower = scale + exponent; - if (upper < lReqPos - 1) { - upper = lReqPos - 1; - } - if (lower > rReqPos) { - lower = rReqPos; - } - int32_t p = upper; - if (p < 0) { - sb.append(u'0'); - } - for (; p >= 0; p--) { - sb.append(u'0' + getDigitPos(p - scale - exponent)); - } - if (lower < 0) { - sb.append(u'.'); - } - for(; p >= lower; p--) { - sb.append(u'0' + getDigitPos(p - scale - exponent)); - } - return sb; -} - - -UnicodeString DecimalQuantity::toExponentString() const { - U_ASSERT(!isApproximate); - UnicodeString sb; - if (isNegative()) { - sb.append(u'-'); - } - - int32_t upper = scale + precision - 1; - int32_t lower = scale; - if (upper < lReqPos - 1) { - upper = lReqPos - 1; - } - if (lower > rReqPos) { - lower = rReqPos; - } - int32_t p = upper; - if (p < 0) { - sb.append(u'0'); - } - for (; p >= 0; p--) { - sb.append(u'0' + getDigitPos(p - scale)); - } - if (lower < 0) { - sb.append(u'.'); - } - for(; p >= lower; p--) { - sb.append(u'0' + getDigitPos(p - scale)); - } - - if (exponent != 0) { - sb.append(u'c'); - ICU_Utility::appendNumber(sb, exponent); - } - - 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; - } - int32_t upperPos = precision - 1; - int32_t lowerPos = 0; - 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 + exponent; - if (_scale == INT32_MIN) { - result.append({u"-2147483648", -1}); - return result; - } else 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 /// -//////////////////////////////////////////////////// - -int8_t DecimalQuantity::getDigitPos(int32_t position) const { - if (usingBytes) { - if (position < 0 || position >= precision) { return 0; } - return fBCD.bcdBytes.ptr[position]; - } else { - if (position < 0 || position >= 16) { return 0; } - return (int8_t) ((fBCD.bcdLong >> (position * 4)) & 0xf); - } -} - -void DecimalQuantity::setDigitPos(int32_t position, int8_t value) { - U_ASSERT(position >= 0); - if (usingBytes) { - ensureCapacity(position + 1); - fBCD.bcdBytes.ptr[position] = value; - } else if (position >= 16) { - switchStorage(); - ensureCapacity(position + 1); - fBCD.bcdBytes.ptr[position] = value; - } else { - int shift = position * 4; - fBCD.bcdLong = (fBCD.bcdLong & ~(0xfL << shift)) | ((long) value << shift); - } -} - -void DecimalQuantity::shiftLeft(int32_t numDigits) { - if (!usingBytes && precision + numDigits > 16) { - switchStorage(); - } - if (usingBytes) { - ensureCapacity(precision + numDigits); - uprv_memmove(fBCD.bcdBytes.ptr + numDigits, fBCD.bcdBytes.ptr, precision); - uprv_memset(fBCD.bcdBytes.ptr, 0, numDigits); - } else { - fBCD.bcdLong <<= (numDigits * 4); - } - scale -= numDigits; - precision += numDigits; -} - -void DecimalQuantity::shiftRight(int32_t numDigits) { - if (usingBytes) { - int i = 0; - for (; i < precision - numDigits; i++) { - fBCD.bcdBytes.ptr[i] = fBCD.bcdBytes.ptr[i + numDigits]; - } - for (; i < precision; i++) { - fBCD.bcdBytes.ptr[i] = 0; - } - } else { - fBCD.bcdLong >>= (numDigits * 4); - } - scale += numDigits; - precision -= numDigits; -} - -void DecimalQuantity::popFromLeft(int32_t numDigits) { - U_ASSERT(numDigits <= precision); - if (usingBytes) { - int i = precision - 1; - for (; i >= precision - numDigits; i--) { - fBCD.bcdBytes.ptr[i] = 0; - } - } else { - fBCD.bcdLong &= (static_cast(1) << ((precision - numDigits) * 4)) - 1; - } - precision -= numDigits; -} - -void DecimalQuantity::setBcdToZero() { - if (usingBytes) { - uprv_free(fBCD.bcdBytes.ptr); - fBCD.bcdBytes.ptr = nullptr; - usingBytes = false; - } - fBCD.bcdLong = 0L; - scale = 0; - precision = 0; - isApproximate = false; - origDouble = 0; - origDelta = 0; - exponent = 0; -} - -void DecimalQuantity::readIntToBcd(int32_t n) { - U_ASSERT(n != 0); - // ints always fit inside the long implementation. - uint64_t result = 0L; - int i = 16; - for (; n != 0; n /= 10, i--) { - result = (result >> 4) + ((static_cast(n) % 10) << 60); - } - U_ASSERT(!usingBytes); - fBCD.bcdLong = result >> (i * 4); - scale = 0; - precision = 16 - i; -} - -void DecimalQuantity::readLongToBcd(int64_t n) { - U_ASSERT(n != 0); - if (n >= 10000000000000000L) { - ensureCapacity(); - int i = 0; - for (; n != 0L; n /= 10L, i++) { - fBCD.bcdBytes.ptr[i] = static_cast(n % 10); - } - U_ASSERT(usingBytes); - scale = 0; - precision = i; - } else { - uint64_t result = 0L; - int i = 16; - for (; n != 0L; n /= 10L, i--) { - result = (result >> 4) + ((n % 10) << 60); - } - U_ASSERT(i >= 0); - U_ASSERT(!usingBytes); - fBCD.bcdLong = result >> (i * 4); - scale = 0; - precision = 16 - i; - } -} - -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++) { - fBCD.bcdBytes.ptr[i] = dn->lsu[i]; - } - } else { - uint64_t result = 0L; - for (int32_t i = 0; i < dn->digits; i++) { - result |= static_cast(dn->lsu[i]) << (4 * i); - } - fBCD.bcdLong = result; - } - scale = dn->exponent; - precision = dn->digits; -} - -void DecimalQuantity::readDoubleConversionToBcd( - const char* buffer, int32_t length, int32_t point) { - // NOTE: Despite the fact that double-conversion's API is called - // "DoubleToAscii", they actually use '0' (as opposed to u8'0'). - if (length > 16) { - ensureCapacity(length); - for (int32_t i = 0; i < length; i++) { - fBCD.bcdBytes.ptr[i] = buffer[length-i-1] - '0'; - } - } else { - uint64_t result = 0L; - for (int32_t i = 0; i < length; i++) { - result |= static_cast(buffer[length-i-1] - '0') << (4 * i); - } - fBCD.bcdLong = result; - } - scale = point - length; - precision = length; -} - -void DecimalQuantity::compact() { - if (usingBytes) { - int32_t delta = 0; - for (; delta < precision && fBCD.bcdBytes.ptr[delta] == 0; delta++); - if (delta == precision) { - // Number is zero - setBcdToZero(); - return; - } else { - // Remove trailing zeros - shiftRight(delta); - } - - // Compute precision - int32_t leading = precision - 1; - for (; leading >= 0 && fBCD.bcdBytes.ptr[leading] == 0; leading--); - precision = leading + 1; - - // Switch storage mechanism if possible - if (precision <= 16) { - switchStorage(); - } - - } else { - if (fBCD.bcdLong == 0L) { - // Number is zero - setBcdToZero(); - return; - } - - // Compact the number (remove trailing zeros) - // TODO: Use a more efficient algorithm here and below. There is a logarithmic one. - int32_t delta = 0; - for (; delta < precision && getDigitPos(delta) == 0; delta++); - fBCD.bcdLong >>= delta * 4; - scale += delta; - - // Compute precision - int32_t leading = precision - 1; - for (; leading >= 0 && getDigitPos(leading) == 0; leading--); - precision = leading + 1; - } -} - -void DecimalQuantity::ensureCapacity() { - ensureCapacity(40); -} - -void DecimalQuantity::ensureCapacity(int32_t capacity) { - if (capacity == 0) { return; } - int32_t oldCapacity = usingBytes ? fBCD.bcdBytes.len : 0; - if (!usingBytes) { - // TODO: There is nothing being done to check for memory allocation failures. - // TODO: Consider indexing by nybbles instead of bytes in C++, so that we can - // make these arrays half the size. - fBCD.bcdBytes.ptr = static_cast(uprv_malloc(capacity * sizeof(int8_t))); - fBCD.bcdBytes.len = capacity; - // Initialize the byte array to zeros (this is done automatically in Java) - uprv_memset(fBCD.bcdBytes.ptr, 0, capacity * sizeof(int8_t)); - } else if (oldCapacity < 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(bcd1 + oldCapacity, 0, (capacity - oldCapacity) * sizeof(int8_t)); - uprv_free(fBCD.bcdBytes.ptr); - fBCD.bcdBytes.ptr = bcd1; - fBCD.bcdBytes.len = capacity * 2; - } - usingBytes = true; -} - -void DecimalQuantity::switchStorage() { - if (usingBytes) { - // Change from bytes to long - uint64_t bcdLong = 0L; - for (int i = precision - 1; i >= 0; i--) { - bcdLong <<= 4; - bcdLong |= fBCD.bcdBytes.ptr[i]; - } - uprv_free(fBCD.bcdBytes.ptr); - fBCD.bcdBytes.ptr = nullptr; - fBCD.bcdLong = bcdLong; - usingBytes = false; - } else { - // Change from long to bytes - // Copy the long into a local variable since it will get munged when we allocate the bytes - uint64_t bcdLong = fBCD.bcdLong; - ensureCapacity(); - for (int i = 0; i < precision; i++) { - fBCD.bcdBytes.ptr[i] = static_cast(bcdLong & 0xf); - bcdLong >>= 4; - } - U_ASSERT(usingBytes); - } -} - -void DecimalQuantity::copyBcdFrom(const DecimalQuantity &other) { - setBcdToZero(); - if (other.usingBytes) { - ensureCapacity(other.precision); - uprv_memcpy(fBCD.bcdBytes.ptr, other.fBCD.bcdBytes.ptr, other.precision * sizeof(int8_t)); - } else { - fBCD.bcdLong = other.fBCD.bcdLong; - } -} - -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"; } - int32_t capacity = fBCD.bcdBytes.len; - if (precision > capacity) { return u"Precision exceeds length of byte array"; } - if (getDigitPos(precision - 1) == 0) { return u"Most significant digit is zero in byte mode"; } - if (getDigitPos(0) == 0) { return u"Least significant digit is zero in long mode"; } - for (int i = 0; i < precision; i++) { - if (getDigitPos(i) >= 10) { return u"Digit exceeding 10 in byte array"; } - if (getDigitPos(i) < 0) { return u"Digit below 0 in byte array"; } - } - for (int i = precision; i < capacity; i++) { - if (getDigitPos(i) != 0) { return u"Nonzero digits outside of range in byte array"; } - } - } else { - if (precision == 0 && fBCD.bcdLong != 0) { - return u"Value in bcdLong even though precision is zero"; - } - if (precision > 16) { return u"Precision exceeds length of long"; } - if (precision != 0 && getDigitPos(precision - 1) == 0) { - return u"Most significant digit is zero in long mode"; - } - if (precision != 0 && getDigitPos(0) == 0) { - return u"Least significant digit is zero in long mode"; - } - for (int i = 0; i < precision; i++) { - if (getDigitPos(i) >= 10) { return u"Digit exceeding 10 in long"; } - if (getDigitPos(i) < 0) { return u"Digit below 0 in long (?!)"; } - } - for (int i = precision; i < 16; i++) { - if (getDigitPos(i) != 0) { return u"Nonzero digits outside of range in long"; } - } - } - - // No error - return nullptr; -} - -bool DecimalQuantity::operator==(const DecimalQuantity& other) const { - bool basicEquals = - scale == other.scale - && precision == other.precision - && flags == other.flags - && lReqPos == other.lReqPos - && rReqPos == other.rReqPos - && isApproximate == other.isApproximate; - if (!basicEquals) { - return false; - } - - if (precision == 0) { - return true; - } else if (isApproximate) { - return origDouble == other.origDouble && origDelta == other.origDelta; - } else { - for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) { - if (getDigit(m) != other.getDigit(m)) { - return false; - } - } - return true; - } -} - -UnicodeString DecimalQuantity::toString() const { - UErrorCode localStatus = U_ZERO_ERROR; - MaybeStackArray digits(precision + 1, localStatus); - if (U_FAILURE(localStatus)) { - return ICU_Utility::makeBogusString(); - } - for (int32_t i = 0; i < precision; i++) { - digits[i] = getDigitPos(precision - i - 1) + '0'; - } - digits[precision] = 0; // terminate buffer - char buffer8[100]; - snprintf( - buffer8, - sizeof(buffer8), - "", - lReqPos, - rReqPos, - (usingBytes ? "bytes" : "long"), - (isNegative() ? "-" : ""), - (precision == 0 ? "0" : digits.getAlias()), - "E", - scale); - return UnicodeString(buffer8, -1, US_INV); -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include +#include +#include +#include + +#include "unicode/plurrule.h" +#include "cmemory.h" +#include "number_decnum.h" +#include "putilimp.h" +#include "number_decimalquantity.h" +#include "number_roundingutils.h" +#include "double-conversion.h" +#include "charstr.h" +#include "number_utils.h" +#include "uassert.h" +#include "util.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +using icu::double_conversion::DoubleToStringConverter; +using icu::double_conversion::StringToDoubleConverter; + +namespace { + +int8_t NEGATIVE_FLAG = 1; +int8_t INFINITY_FLAG = 2; +int8_t NAN_FLAG = 4; + +/** 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. + int32_t diff = static_cast(static_cast(a) - static_cast(b)); + if (b < 0 && diff < a) { return INT32_MAX; } + if (b > 0 && diff > a) { return INT32_MIN; } + return diff; +} + +static double DOUBLE_MULTIPLIERS[] = { + 1e0, + 1e1, + 1e2, + 1e3, + 1e4, + 1e5, + 1e6, + 1e7, + 1e8, + 1e9, + 1e10, + 1e11, + 1e12, + 1e13, + 1e14, + 1e15, + 1e16, + 1e17, + 1e18, + 1e19, + 1e20, + 1e21}; + +} // namespace + +icu::IFixedDecimal::~IFixedDecimal() = default; + +DecimalQuantity::DecimalQuantity() { + setBcdToZero(); + flags = 0; +} + +DecimalQuantity::~DecimalQuantity() { + if (usingBytes) { + uprv_free(fBCD.bcdBytes.ptr); + fBCD.bcdBytes.ptr = nullptr; + usingBytes = false; + } +} + +DecimalQuantity::DecimalQuantity(const DecimalQuantity &other) { + *this = other; +} + +DecimalQuantity::DecimalQuantity(DecimalQuantity&& src) 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) noexcept { + if (this == &src) { + return *this; + } + moveBcdFrom(src); + copyFieldsFrom(src); + return *this; +} + +void DecimalQuantity::copyFieldsFrom(const DecimalQuantity& other) { + bogus = other.bogus; + lReqPos = other.lReqPos; + rReqPos = other.rReqPos; + scale = other.scale; + precision = other.precision; + flags = other.flags; + origDouble = other.origDouble; + origDelta = other.origDelta; + isApproximate = other.isApproximate; + exponent = other.exponent; +} + +void DecimalQuantity::clear() { + lReqPos = 0; + rReqPos = 0; + flags = 0; + setBcdToZero(); // sets scale, precision, hasDouble, origDouble, origDelta, and BCD data +} + +void DecimalQuantity::setMinInteger(int32_t minInt) { + // Validation should happen outside of DecimalQuantity, e.g., in the Precision class. + U_ASSERT(minInt >= 0); + + // 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 + lReqPos = minInt; +} + +void DecimalQuantity::setMinFraction(int32_t minFrac) { + // Validation should happen outside of DecimalQuantity, e.g., in the Precision class. + U_ASSERT(minFrac >= 0); + + // Save values into internal state + // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE + rReqPos = -minFrac; +} + +void DecimalQuantity::applyMaxInteger(int32_t maxInt) { + // Validation should happen outside of DecimalQuantity, e.g., in the Precision class. + U_ASSERT(maxInt >= 0); + + if (precision == 0) { + return; + } + + if (maxInt <= scale) { + setBcdToZero(); + return; + } + + int32_t magnitude = getMagnitude(); + if (maxInt <= magnitude) { + popFromLeft(magnitude - maxInt + 1); + compact(); + } +} + +uint64_t DecimalQuantity::getPositionFingerprint() const { + uint64_t fingerprint = 0; + fingerprint ^= (lReqPos << 16); + fingerprint ^= (static_cast(rReqPos) << 32); + return fingerprint; +} + +void DecimalQuantity::roundToIncrement( + uint64_t increment, + digits_t magnitude, + RoundingMode roundingMode, + UErrorCode& status) { + // Do not call this method with an increment having only a 1 or a 5 digit! + // Use a more efficient call to either roundToMagnitude() or roundToNickel(). + // Check a few popular rounding increments; a more thorough check is in Java. + U_ASSERT(increment != 1); + U_ASSERT(increment != 5); + + DecimalQuantity incrementDQ; + incrementDQ.setToLong(increment); + incrementDQ.adjustMagnitude(magnitude); + DecNum incrementDN; + incrementDQ.toDecNum(incrementDN, status); + if (U_FAILURE(status)) { return; } + + // Divide this DecimalQuantity by the increment, round, then multiply back. + divideBy(incrementDN, status); + if (U_FAILURE(status)) { return; } + roundToMagnitude(0, roundingMode, status); + if (U_FAILURE(status)) { return; } + multiplyBy(incrementDN, status); + if (U_FAILURE(status)) { return; } +} + +void DecimalQuantity::multiplyBy(const DecNum& multiplicand, UErrorCode& status) { + if (isZeroish()) { + return; + } + // 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 (isZeroish()) { + 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 { + U_ASSERT(precision != 0); + return scale + precision - 1; +} + +bool DecimalQuantity::adjustMagnitude(int32_t delta) { + if (precision != 0) { + // 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; +} + +int32_t DecimalQuantity::adjustToZeroScale() { + int32_t retval = scale; + scale = 0; + return retval; +} + +double DecimalQuantity::getPluralOperand(PluralOperand operand) const { + // If this assertion fails, you need to call roundToInfinity() or some other rounding method. + // See the comment at the top of this file explaining the "isApproximate" field. + U_ASSERT(!isApproximate); + + switch (operand) { + case PLURAL_OPERAND_I: + // 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: + return static_cast(toFractionLong(false)); + case PLURAL_OPERAND_V: + return fractionCount(); + case PLURAL_OPERAND_W: + return fractionCountWithoutTrailingZeros(); + case PLURAL_OPERAND_E: + return static_cast(getExponent()); + case PLURAL_OPERAND_C: + // Plural operand `c` is currently an alias for `e`. + return static_cast(getExponent()); + default: + return std::abs(toDouble()); + } +} + +int32_t DecimalQuantity::getExponent() const { + return exponent; +} + +void DecimalQuantity::adjustExponent(int delta) { + exponent = exponent + delta; +} + +void DecimalQuantity::resetExponent() { + adjustMagnitude(exponent); + exponent = 0; +} + +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. + U_ASSERT(!isApproximate); + + int32_t magnitude = scale + precision; + int32_t result = (lReqPos > magnitude) ? lReqPos : magnitude; + return result - 1; +} + +int32_t DecimalQuantity::getLowerDisplayMagnitude() 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); + + int32_t magnitude = scale; + int32_t result = (rReqPos < magnitude) ? rReqPos : magnitude; + return result; +} + +int8_t DecimalQuantity::getDigit(int32_t magnitude) const { + // If this assertion fails, you need to call roundToInfinity() or some other rounding method. + // See the comment at the top of this file explaining the "isApproximate" field. + U_ASSERT(!isApproximate); + + return getDigitPos(magnitude - scale); +} + +int32_t DecimalQuantity::fractionCount() const { + int32_t fractionCountWithExponent = -getLowerDisplayMagnitude() - exponent; + return fractionCountWithExponent > 0 ? fractionCountWithExponent : 0; +} + +int32_t DecimalQuantity::fractionCountWithoutTrailingZeros() const { + int32_t fractionCountWithExponent = -scale - exponent; + return fractionCountWithExponent > 0 ? fractionCountWithExponent : 0; // max(-fractionCountWithExponent, 0) +} + +bool DecimalQuantity::isNegative() const { + return (flags & NEGATIVE_FLAG) != 0; +} + +Signum DecimalQuantity::signum() const { + bool isZero = (isZeroish() && !isInfinite()); + bool isNeg = isNegative(); + if (isZero && isNeg) { + return SIGNUM_NEG_ZERO; + } else if (isZero) { + return SIGNUM_POS_ZERO; + } else if (isNeg) { + return SIGNUM_NEG; + } else { + return SIGNUM_POS; + } +} + +bool DecimalQuantity::isInfinite() const { + return (flags & INFINITY_FLAG) != 0; +} + +bool DecimalQuantity::isNaN() const { + return (flags & NAN_FLAG) != 0; +} + +bool DecimalQuantity::isZeroish() const { + return precision == 0; +} + +DecimalQuantity &DecimalQuantity::setToInt(int32_t n) { + setBcdToZero(); + flags = 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; + } + if (n != 0) { + _setToInt(n); + compact(); + } + return *this; +} + +void DecimalQuantity::_setToInt(int32_t n) { + if (n == INT32_MIN) { + readLongToBcd(-static_cast(n)); + } else { + readIntToBcd(n); + } +} + +DecimalQuantity &DecimalQuantity::setToLong(int64_t n) { + setBcdToZero(); + flags = 0; + if (n < 0 && n > INT64_MIN) { + flags |= NEGATIVE_FLAG; + n = -n; + } + if (n != 0) { + _setToLong(n); + compact(); + } + return *this; +} + +void DecimalQuantity::_setToLong(int64_t n) { + if (n == INT64_MIN) { + 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 { + readLongToBcd(n); + } +} + +DecimalQuantity &DecimalQuantity::setToDouble(double n) { + setBcdToZero(); + flags = 0; + // signbit() from handles +0.0 vs -0.0 + if (std::signbit(n)) { + flags |= NEGATIVE_FLAG; + n = -n; + } + if (std::isnan(n) != 0) { + flags |= NAN_FLAG; + } else if (std::isfinite(n) == 0) { + flags |= INFINITY_FLAG; + } else if (n != 0) { + _setToDoubleFast(n); + compact(); + } + return *this; +} + +void DecimalQuantity::_setToDoubleFast(double n) { + isApproximate = true; + origDouble = n; + origDelta = 0; + + // Make sure the double is an IEEE 754 double. If not, fall back to the slow path right now. + // TODO: Make a fast path for other types of doubles. + if (!std::numeric_limits::is_iec559) { + convertToAccurateDouble(); + return; + } + + // To get the bits from the double, use memcpy, which takes care of endianness. + uint64_t ieeeBits; + uprv_memcpy(&ieeeBits, &n, sizeof(n)); + int32_t exponent = static_cast((ieeeBits & 0x7ff0000000000000L) >> 52) - 0x3ff; + + // Not all integers can be represented exactly for exponent > 52 + if (exponent <= 52 && static_cast(n) == n) { + _setToLong(static_cast(n)); + return; + } + + if (exponent == -1023 || exponent == 1024) { + // The extreme values of exponent are special; use slow path. + convertToAccurateDouble(); + return; + } + + // 3.3219... is log2(10) + auto fracLength = static_cast ((52 - exponent) / 3.32192809488736234787031942948939017586); + if (fracLength >= 0) { + int32_t i = fracLength; + // 1e22 is the largest exact double. + for (; i >= 22; i -= 22) n *= 1e22; + n *= DOUBLE_MULTIPLIERS[i]; + } else { + int32_t i = fracLength; + // 1e22 is the largest exact double. + for (; i <= -22; i += 22) n /= 1e22; + n /= DOUBLE_MULTIPLIERS[-i]; + } + auto result = static_cast(uprv_round(n)); + if (result != 0) { + _setToLong(result); + scale -= fracLength; + } +} + +void DecimalQuantity::convertToAccurateDouble() { + U_ASSERT(origDouble != 0); + int32_t delta = origDelta; + + // Call the slow oracle function (Double.toString in Java, DoubleToAscii in C++). + char buffer[DoubleToStringConverter::kBase10MaximalLength + 1]; + bool sign; // unused; always positive + int32_t length; + int32_t point; + DoubleToStringConverter::DoubleToAscii( + origDouble, + DoubleToStringConverter::DtoaMode::SHORTEST, + 0, + buffer, + sizeof(buffer), + &sign, + &length, + &point + ); + + setBcdToZero(); + readDoubleConversionToBcd(buffer, length, point); + scale += delta; + explicitExactDouble = true; +} + +DecimalQuantity &DecimalQuantity::setToDecNumber(StringPiece n, UErrorCode& status) { + setBcdToZero(); + flags = 0; + + // Compute the decNumber representation + DecNum decnum; + decnum.setTo(n, status); + + _setToDecNum(decnum, status); + return *this; +} + +DecimalQuantity& DecimalQuantity::setToDecNum(const DecNum& decnum, UErrorCode& status) { + setBcdToZero(); + flags = 0; + + _setToDecNum(decnum, status); + return *this; +} + +void DecimalQuantity::_setToDecNum(const DecNum& decnum, UErrorCode& status) { + if (U_FAILURE(status)) { return; } + if (decnum.isNegative()) { + flags |= NEGATIVE_FLAG; + } + if (decnum.isNaN()) { + flags |= NAN_FLAG; + } else if (decnum.isInfinity()) { + flags |= INFINITY_FLAG; + } else if (!decnum.isZero()) { + readDecNumberToBcd(decnum); + compact(); + } +} + +DecimalQuantity DecimalQuantity::fromExponentString(UnicodeString num, UErrorCode& status) { + if (num.indexOf(u'e') >= 0 || num.indexOf(u'c') >= 0 + || num.indexOf(u'E') >= 0 || num.indexOf(u'C') >= 0) { + int32_t ePos = num.lastIndexOf('e'); + if (ePos < 0) { + ePos = num.lastIndexOf('c'); + } + if (ePos < 0) { + ePos = num.lastIndexOf('E'); + } + if (ePos < 0) { + ePos = num.lastIndexOf('C'); + } + int32_t expNumPos = ePos + 1; + UnicodeString exponentStr = num.tempSubString(expNumPos, num.length() - expNumPos); + + // parse exponentStr into exponent, but note that parseAsciiInteger doesn't handle the minus sign + bool isExpStrNeg = num[expNumPos] == u'-'; + int32_t exponentParsePos = isExpStrNeg ? 1 : 0; + int32_t exponent = ICU_Utility::parseAsciiInteger(exponentStr, exponentParsePos); + exponent = isExpStrNeg ? -exponent : exponent; + + // Compute the decNumber representation + UnicodeString fractionStr = num.tempSubString(0, ePos); + CharString fracCharStr = CharString(); + fracCharStr.appendInvariantChars(fractionStr, status); + DecNum decnum; + decnum.setTo(fracCharStr.toStringPiece(), status); + + // Clear and set this DecimalQuantity instance + DecimalQuantity dq; + dq.setToDecNum(decnum, status); + int32_t numFracDigit = getVisibleFractionCount(fractionStr); + dq.setMinFraction(numFracDigit); + dq.adjustExponent(exponent); + + return dq; + } else { + DecimalQuantity dq; + int numFracDigit = getVisibleFractionCount(num); + + CharString numCharStr = CharString(); + numCharStr.appendInvariantChars(num, status); + dq.setToDecNumber(numCharStr.toStringPiece(), status); + + dq.setMinFraction(numFracDigit); + return dq; + } +} + +int32_t DecimalQuantity::getVisibleFractionCount(UnicodeString value) { + int decimalPos = value.indexOf('.') + 1; + if (decimalPos == 0) { + return 0; + } else { + return value.length() - decimalPos; + } +} + +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 = exponent + scale + precision - 1; + if (truncateIfOverflow) { + upperMagnitude = std::min(upperMagnitude, 17); + } + for (int32_t magnitude = upperMagnitude; magnitude >= 0; magnitude--) { + result = result * 10 + getDigitPos(magnitude - scale - exponent); + } + if (isNegative()) { + return static_cast(0LL - result); // i.e., -result + } + return static_cast(result); +} + +uint64_t DecimalQuantity::toFractionLong(bool includeTrailingZeros) const { + uint64_t result = 0L; + int32_t magnitude = -1 - exponent; + int32_t lowerMagnitude = scale; + 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; +} + +bool DecimalQuantity::fitsInLong(bool ignoreFraction) const { + if (isInfinite() || isNaN()) { + return false; + } + if (isZeroish()) { + return true; + } + if (exponent + 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; + } else if (isInfinite()) { + return isNegative() ? -INFINITY : INFINITY; + } + + // 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); +} + +DecNum& DecimalQuantity::toDecNum(DecNum& output, UErrorCode& status) const { + // Special handling for zero + if (precision == 0) { + output.setTo("0", status); + return output; + } + + // 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, status); + if (U_FAILURE(status)) { + return output; + } + for (int32_t m = 0; m < precision; m++) { + ubcd[precision - m - 1] = static_cast(getDigitPos(m)); + } + output.setTo(ubcd.getAlias(), precision, scale, isNegative(), status); + return output; +} + +void DecimalQuantity::truncate() { + if (scale < 0) { + shiftRight(-scale); + scale = 0; + compact(); + } +} + +void DecimalQuantity::roundToNickel(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status) { + roundToMagnitude(magnitude, roundingMode, true, status); +} + +void DecimalQuantity::roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status) { + roundToMagnitude(magnitude, roundingMode, false, status); +} + +void DecimalQuantity::roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, bool nickel, UErrorCode& status) { + // The position in the BCD at which rounding will be performed; digits to the right of position + // will be rounded away. + int position = safeSubtract(magnitude, scale); + + // "trailing" = least significant digit to the left of rounding + int8_t trailingDigit = getDigitPos(position); + + if (position <= 0 && !isApproximate && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { + // All digits are to the left of the rounding magnitude. + } else if (precision == 0) { + // No rounding for zero. + } else { + // Perform rounding logic. + // "leading" = most significant digit to the right of rounding + int8_t leadingDigit = getDigitPos(safeSubtract(position, 1)); + + // Compute which section of the number we are in. + // EDGE means we are at the bottom or top edge, like 1.000 or 1.999 (used by doubles) + // LOWER means we are between the bottom edge and the midpoint, like 1.391 + // MIDPOINT means we are exactly in the middle, like 1.500 + // UPPER means we are between the midpoint and the top edge, like 1.916 + roundingutils::Section section; + if (!isApproximate) { + if (nickel && trailingDigit != 2 && trailingDigit != 7) { + // Nickel rounding, and not at .02x or .07x + if (trailingDigit < 2) { + // .00, .01 => down to .00 + section = roundingutils::SECTION_LOWER; + } else if (trailingDigit < 5) { + // .03, .04 => up to .05 + section = roundingutils::SECTION_UPPER; + } else if (trailingDigit < 7) { + // .05, .06 => down to .05 + section = roundingutils::SECTION_LOWER; + } else { + // .08, .09 => up to .10 + section = roundingutils::SECTION_UPPER; + } + } else if (leadingDigit < 5) { + // Includes nickel rounding .020-.024 and .070-.074 + section = roundingutils::SECTION_LOWER; + } else if (leadingDigit > 5) { + // Includes nickel rounding .026-.029 and .076-.079 + section = roundingutils::SECTION_UPPER; + } else { + // Includes nickel rounding .025 and .075 + section = roundingutils::SECTION_MIDPOINT; + for (int p = safeSubtract(position, 2); p >= 0; p--) { + if (getDigitPos(p) != 0) { + section = roundingutils::SECTION_UPPER; + break; + } + } + } + } else { + int32_t p = safeSubtract(position, 2); + int32_t minP = uprv_max(0, precision - 14); + if (leadingDigit == 0 && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { + section = roundingutils::SECTION_LOWER_EDGE; + for (; p >= minP; p--) { + if (getDigitPos(p) != 0) { + section = roundingutils::SECTION_LOWER; + break; + } + } + } else if (leadingDigit == 4 && (!nickel || trailingDigit == 2 || trailingDigit == 7)) { + section = roundingutils::SECTION_MIDPOINT; + for (; p >= minP; p--) { + if (getDigitPos(p) != 9) { + section = roundingutils::SECTION_LOWER; + break; + } + } + } else if (leadingDigit == 5 && (!nickel || trailingDigit == 2 || trailingDigit == 7)) { + section = roundingutils::SECTION_MIDPOINT; + for (; p >= minP; p--) { + if (getDigitPos(p) != 0) { + section = roundingutils::SECTION_UPPER; + break; + } + } + } else if (leadingDigit == 9 && (!nickel || trailingDigit == 4 || trailingDigit == 9)) { + section = roundingutils::SECTION_UPPER_EDGE; + for (; p >= minP; p--) { + if (getDigitPos(p) != 9) { + section = roundingutils::SECTION_UPPER; + break; + } + } + } else if (nickel && trailingDigit != 2 && trailingDigit != 7) { + // Nickel rounding, and not at .02x or .07x + if (trailingDigit < 2) { + // .00, .01 => down to .00 + section = roundingutils::SECTION_LOWER; + } else if (trailingDigit < 5) { + // .03, .04 => up to .05 + section = roundingutils::SECTION_UPPER; + } else if (trailingDigit < 7) { + // .05, .06 => down to .05 + section = roundingutils::SECTION_LOWER; + } else { + // .08, .09 => up to .10 + section = roundingutils::SECTION_UPPER; + } + } else if (leadingDigit < 5) { + // Includes nickel rounding .020-.024 and .070-.074 + section = roundingutils::SECTION_LOWER; + } else { + // Includes nickel rounding .026-.029 and .076-.079 + section = roundingutils::SECTION_UPPER; + } + + bool roundsAtMidpoint = roundingutils::roundsAtMidpoint(roundingMode); + if (safeSubtract(position, 1) < precision - 14 || + (roundsAtMidpoint && section == roundingutils::SECTION_MIDPOINT) || + (!roundsAtMidpoint && section < 0 /* i.e. at upper or lower edge */)) { + // Oops! This means that we have to get the exact representation of the double, + // because the zone of uncertainty is along the rounding boundary. + convertToAccurateDouble(); + roundToMagnitude(magnitude, roundingMode, nickel, status); // start over + return; + } + + // Turn off the approximate double flag, since the value is now confirmed to be exact. + isApproximate = false; + origDouble = 0.0; + origDelta = 0; + + if (position <= 0 && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { + // All digits are to the left of the rounding magnitude. + return; + } + + // Good to continue rounding. + if (section == -1) { section = roundingutils::SECTION_LOWER; } + if (section == -2) { section = roundingutils::SECTION_UPPER; } + } + + // Nickel rounding "half even" goes to the nearest whole (away from the 5). + bool isEven = nickel + ? (trailingDigit < 2 || trailingDigit > 7 + || (trailingDigit == 2 && section != roundingutils::SECTION_UPPER) + || (trailingDigit == 7 && section == roundingutils::SECTION_UPPER)) + : (trailingDigit % 2) == 0; + + bool roundDown = roundingutils::getRoundingDirection(isEven, + isNegative(), + section, + roundingMode, + status); + if (U_FAILURE(status)) { + return; + } + + // Perform truncation + if (position >= precision) { + U_ASSERT(trailingDigit == 0); + setBcdToZero(); + scale = magnitude; + } else { + shiftRight(position); + } + + if (nickel) { + if (trailingDigit < 5 && roundDown) { + setDigitPos(0, 0); + compact(); + return; + } else if (trailingDigit >= 5 && !roundDown) { + setDigitPos(0, 9); + trailingDigit = 9; + // do not return: use the bubbling logic below + } else { + setDigitPos(0, 5); + // If the quantity was set to 0, we may need to restore a digit. + if (precision == 0) { + precision = 1; + } + // compact not necessary: digit at position 0 is nonzero + return; + } + } + + // Bubble the result to the higher digits + if (!roundDown) { + if (trailingDigit == 9) { + int bubblePos = 0; + // Note: in the long implementation, the most digits BCD can have at this point is + // 15, so bubblePos <= 15 and getDigitPos(bubblePos) is safe. + for (; getDigitPos(bubblePos) == 9; bubblePos++) {} + shiftRight(bubblePos); // shift off the trailing 9s + } + int8_t digit0 = getDigitPos(0); + U_ASSERT(digit0 != 9); + setDigitPos(0, static_cast(digit0 + 1)); + precision += 1; // in case an extra digit got added + } + + compact(); + } +} + +void DecimalQuantity::roundToInfinity() { + if (isApproximate) { + convertToAccurateDouble(); + } +} + +void DecimalQuantity::appendDigit(int8_t value, int32_t leadingZeros, bool appendAsInteger) { + U_ASSERT(leadingZeros >= 0); + + // Zero requires special handling to maintain the invariant that the least-significant digit + // in the BCD is nonzero. + if (value == 0) { + if (appendAsInteger && precision != 0) { + scale += leadingZeros + 1; + } + return; + } + + // Deal with trailing zeros + if (scale > 0) { + leadingZeros += scale; + if (appendAsInteger) { + scale = 0; + } + } + + // Append digit + shiftLeft(leadingZeros + 1); + setDigitPos(0, value); + + // Fix scale if in integer mode + if (appendAsInteger) { + scale += leadingZeros + 1; + } +} + +UnicodeString DecimalQuantity::toPlainString() const { + U_ASSERT(!isApproximate); + UnicodeString sb; + if (isNegative()) { + sb.append(u'-'); + } + if (precision == 0) { + sb.append(u'0'); + return sb; + } + int32_t upper = scale + precision + exponent - 1; + int32_t lower = scale + exponent; + if (upper < lReqPos - 1) { + upper = lReqPos - 1; + } + if (lower > rReqPos) { + lower = rReqPos; + } + int32_t p = upper; + if (p < 0) { + sb.append(u'0'); + } + for (; p >= 0; p--) { + sb.append(u'0' + getDigitPos(p - scale - exponent)); + } + if (lower < 0) { + sb.append(u'.'); + } + for(; p >= lower; p--) { + sb.append(u'0' + getDigitPos(p - scale - exponent)); + } + return sb; +} + + +UnicodeString DecimalQuantity::toExponentString() const { + U_ASSERT(!isApproximate); + UnicodeString sb; + if (isNegative()) { + sb.append(u'-'); + } + + int32_t upper = scale + precision - 1; + int32_t lower = scale; + if (upper < lReqPos - 1) { + upper = lReqPos - 1; + } + if (lower > rReqPos) { + lower = rReqPos; + } + int32_t p = upper; + if (p < 0) { + sb.append(u'0'); + } + for (; p >= 0; p--) { + sb.append(u'0' + getDigitPos(p - scale)); + } + if (lower < 0) { + sb.append(u'.'); + } + for(; p >= lower; p--) { + sb.append(u'0' + getDigitPos(p - scale)); + } + + if (exponent != 0) { + sb.append(u'c'); + ICU_Utility::appendNumber(sb, exponent); + } + + 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; + } + int32_t upperPos = precision - 1; + int32_t lowerPos = 0; + 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 + exponent; + if (_scale == INT32_MIN) { + result.append({u"-2147483648", -1}); + return result; + } else 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 /// +//////////////////////////////////////////////////// + +int8_t DecimalQuantity::getDigitPos(int32_t position) const { + if (usingBytes) { + if (position < 0 || position >= precision) { return 0; } + return fBCD.bcdBytes.ptr[position]; + } else { + if (position < 0 || position >= 16) { return 0; } + return (int8_t) ((fBCD.bcdLong >> (position * 4)) & 0xf); + } +} + +void DecimalQuantity::setDigitPos(int32_t position, int8_t value) { + U_ASSERT(position >= 0); + if (usingBytes) { + ensureCapacity(position + 1); + fBCD.bcdBytes.ptr[position] = value; + } else if (position >= 16) { + switchStorage(); + ensureCapacity(position + 1); + fBCD.bcdBytes.ptr[position] = value; + } else { + int shift = position * 4; + fBCD.bcdLong = (fBCD.bcdLong & ~(0xfL << shift)) | ((long) value << shift); + } +} + +void DecimalQuantity::shiftLeft(int32_t numDigits) { + if (!usingBytes && precision + numDigits > 16) { + switchStorage(); + } + if (usingBytes) { + ensureCapacity(precision + numDigits); + uprv_memmove(fBCD.bcdBytes.ptr + numDigits, fBCD.bcdBytes.ptr, precision); + uprv_memset(fBCD.bcdBytes.ptr, 0, numDigits); + } else { + fBCD.bcdLong <<= (numDigits * 4); + } + scale -= numDigits; + precision += numDigits; +} + +void DecimalQuantity::shiftRight(int32_t numDigits) { + if (usingBytes) { + int i = 0; + for (; i < precision - numDigits; i++) { + fBCD.bcdBytes.ptr[i] = fBCD.bcdBytes.ptr[i + numDigits]; + } + for (; i < precision; i++) { + fBCD.bcdBytes.ptr[i] = 0; + } + } else { + fBCD.bcdLong >>= (numDigits * 4); + } + scale += numDigits; + precision -= numDigits; +} + +void DecimalQuantity::popFromLeft(int32_t numDigits) { + U_ASSERT(numDigits <= precision); + if (usingBytes) { + int i = precision - 1; + for (; i >= precision - numDigits; i--) { + fBCD.bcdBytes.ptr[i] = 0; + } + } else { + fBCD.bcdLong &= (static_cast(1) << ((precision - numDigits) * 4)) - 1; + } + precision -= numDigits; +} + +void DecimalQuantity::setBcdToZero() { + if (usingBytes) { + uprv_free(fBCD.bcdBytes.ptr); + fBCD.bcdBytes.ptr = nullptr; + usingBytes = false; + } + fBCD.bcdLong = 0L; + scale = 0; + precision = 0; + isApproximate = false; + origDouble = 0; + origDelta = 0; + exponent = 0; +} + +void DecimalQuantity::readIntToBcd(int32_t n) { + U_ASSERT(n != 0); + // ints always fit inside the long implementation. + uint64_t result = 0L; + int i = 16; + for (; n != 0; n /= 10, i--) { + result = (result >> 4) + ((static_cast(n) % 10) << 60); + } + U_ASSERT(!usingBytes); + fBCD.bcdLong = result >> (i * 4); + scale = 0; + precision = 16 - i; +} + +void DecimalQuantity::readLongToBcd(int64_t n) { + U_ASSERT(n != 0); + if (n >= 10000000000000000L) { + ensureCapacity(); + int i = 0; + for (; n != 0L; n /= 10L, i++) { + fBCD.bcdBytes.ptr[i] = static_cast(n % 10); + } + U_ASSERT(usingBytes); + scale = 0; + precision = i; + } else { + uint64_t result = 0L; + int i = 16; + for (; n != 0L; n /= 10L, i--) { + result = (result >> 4) + ((n % 10) << 60); + } + U_ASSERT(i >= 0); + U_ASSERT(!usingBytes); + fBCD.bcdLong = result >> (i * 4); + scale = 0; + precision = 16 - i; + } +} + +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++) { + fBCD.bcdBytes.ptr[i] = dn->lsu[i]; + } + } else { + uint64_t result = 0L; + for (int32_t i = 0; i < dn->digits; i++) { + result |= static_cast(dn->lsu[i]) << (4 * i); + } + fBCD.bcdLong = result; + } + scale = dn->exponent; + precision = dn->digits; +} + +void DecimalQuantity::readDoubleConversionToBcd( + const char* buffer, int32_t length, int32_t point) { + // NOTE: Despite the fact that double-conversion's API is called + // "DoubleToAscii", they actually use '0' (as opposed to u8'0'). + if (length > 16) { + ensureCapacity(length); + for (int32_t i = 0; i < length; i++) { + fBCD.bcdBytes.ptr[i] = buffer[length-i-1] - '0'; + } + } else { + uint64_t result = 0L; + for (int32_t i = 0; i < length; i++) { + result |= static_cast(buffer[length-i-1] - '0') << (4 * i); + } + fBCD.bcdLong = result; + } + scale = point - length; + precision = length; +} + +void DecimalQuantity::compact() { + if (usingBytes) { + int32_t delta = 0; + for (; delta < precision && fBCD.bcdBytes.ptr[delta] == 0; delta++); + if (delta == precision) { + // Number is zero + setBcdToZero(); + return; + } else { + // Remove trailing zeros + shiftRight(delta); + } + + // Compute precision + int32_t leading = precision - 1; + for (; leading >= 0 && fBCD.bcdBytes.ptr[leading] == 0; leading--); + precision = leading + 1; + + // Switch storage mechanism if possible + if (precision <= 16) { + switchStorage(); + } + + } else { + if (fBCD.bcdLong == 0L) { + // Number is zero + setBcdToZero(); + return; + } + + // Compact the number (remove trailing zeros) + // TODO: Use a more efficient algorithm here and below. There is a logarithmic one. + int32_t delta = 0; + for (; delta < precision && getDigitPos(delta) == 0; delta++); + fBCD.bcdLong >>= delta * 4; + scale += delta; + + // Compute precision + int32_t leading = precision - 1; + for (; leading >= 0 && getDigitPos(leading) == 0; leading--); + precision = leading + 1; + } +} + +void DecimalQuantity::ensureCapacity() { + ensureCapacity(40); +} + +void DecimalQuantity::ensureCapacity(int32_t capacity) { + if (capacity == 0) { return; } + int32_t oldCapacity = usingBytes ? fBCD.bcdBytes.len : 0; + if (!usingBytes) { + // TODO: There is nothing being done to check for memory allocation failures. + // TODO: Consider indexing by nybbles instead of bytes in C++, so that we can + // make these arrays half the size. + fBCD.bcdBytes.ptr = static_cast(uprv_malloc(capacity * sizeof(int8_t))); + fBCD.bcdBytes.len = capacity; + // Initialize the byte array to zeros (this is done automatically in Java) + uprv_memset(fBCD.bcdBytes.ptr, 0, capacity * sizeof(int8_t)); + } else if (oldCapacity < 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(bcd1 + oldCapacity, 0, (capacity - oldCapacity) * sizeof(int8_t)); + uprv_free(fBCD.bcdBytes.ptr); + fBCD.bcdBytes.ptr = bcd1; + fBCD.bcdBytes.len = capacity * 2; + } + usingBytes = true; +} + +void DecimalQuantity::switchStorage() { + if (usingBytes) { + // Change from bytes to long + uint64_t bcdLong = 0L; + for (int i = precision - 1; i >= 0; i--) { + bcdLong <<= 4; + bcdLong |= fBCD.bcdBytes.ptr[i]; + } + uprv_free(fBCD.bcdBytes.ptr); + fBCD.bcdBytes.ptr = nullptr; + fBCD.bcdLong = bcdLong; + usingBytes = false; + } else { + // Change from long to bytes + // Copy the long into a local variable since it will get munged when we allocate the bytes + uint64_t bcdLong = fBCD.bcdLong; + ensureCapacity(); + for (int i = 0; i < precision; i++) { + fBCD.bcdBytes.ptr[i] = static_cast(bcdLong & 0xf); + bcdLong >>= 4; + } + U_ASSERT(usingBytes); + } +} + +void DecimalQuantity::copyBcdFrom(const DecimalQuantity &other) { + setBcdToZero(); + if (other.usingBytes) { + ensureCapacity(other.precision); + uprv_memcpy(fBCD.bcdBytes.ptr, other.fBCD.bcdBytes.ptr, other.precision * sizeof(int8_t)); + } else { + fBCD.bcdLong = other.fBCD.bcdLong; + } +} + +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"; } + int32_t capacity = fBCD.bcdBytes.len; + if (precision > capacity) { return u"Precision exceeds length of byte array"; } + if (getDigitPos(precision - 1) == 0) { return u"Most significant digit is zero in byte mode"; } + if (getDigitPos(0) == 0) { return u"Least significant digit is zero in long mode"; } + for (int i = 0; i < precision; i++) { + if (getDigitPos(i) >= 10) { return u"Digit exceeding 10 in byte array"; } + if (getDigitPos(i) < 0) { return u"Digit below 0 in byte array"; } + } + for (int i = precision; i < capacity; i++) { + if (getDigitPos(i) != 0) { return u"Nonzero digits outside of range in byte array"; } + } + } else { + if (precision == 0 && fBCD.bcdLong != 0) { + return u"Value in bcdLong even though precision is zero"; + } + if (precision > 16) { return u"Precision exceeds length of long"; } + if (precision != 0 && getDigitPos(precision - 1) == 0) { + return u"Most significant digit is zero in long mode"; + } + if (precision != 0 && getDigitPos(0) == 0) { + return u"Least significant digit is zero in long mode"; + } + for (int i = 0; i < precision; i++) { + if (getDigitPos(i) >= 10) { return u"Digit exceeding 10 in long"; } + if (getDigitPos(i) < 0) { return u"Digit below 0 in long (?!)"; } + } + for (int i = precision; i < 16; i++) { + if (getDigitPos(i) != 0) { return u"Nonzero digits outside of range in long"; } + } + } + + // No error + return nullptr; +} + +bool DecimalQuantity::operator==(const DecimalQuantity& other) const { + bool basicEquals = + scale == other.scale + && precision == other.precision + && flags == other.flags + && lReqPos == other.lReqPos + && rReqPos == other.rReqPos + && isApproximate == other.isApproximate; + if (!basicEquals) { + return false; + } + + if (precision == 0) { + return true; + } else if (isApproximate) { + return origDouble == other.origDouble && origDelta == other.origDelta; + } else { + for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) { + if (getDigit(m) != other.getDigit(m)) { + return false; + } + } + return true; + } +} + +UnicodeString DecimalQuantity::toString() const { + UErrorCode localStatus = U_ZERO_ERROR; + MaybeStackArray digits(precision + 1, localStatus); + if (U_FAILURE(localStatus)) { + return ICU_Utility::makeBogusString(); + } + for (int32_t i = 0; i < precision; i++) { + digits[i] = getDigitPos(precision - i - 1) + '0'; + } + digits[precision] = 0; // terminate buffer + char buffer8[100]; + snprintf( + buffer8, + sizeof(buffer8), + "", + lReqPos, + rReqPos, + (usingBytes ? "bytes" : "long"), + (isNegative() ? "-" : ""), + (precision == 0 ? "0" : digits.getAlias()), + "E", + scale); + return UnicodeString(buffer8, -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 862addf5d6cd90..65e65a2ac62a17 100644 --- a/deps/icu-small/source/i18n/number_decimalquantity.h +++ b/deps/icu-small/source/i18n/number_decimalquantity.h @@ -1,559 +1,559 @@ -// © 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_DECIMALQUANTITY_H__ -#define __NUMBER_DECIMALQUANTITY_H__ - -#include -#include "unicode/umachine.h" -#include "standardplural.h" -#include "plurrule_impl.h" -#include "number_types.h" - -U_NAMESPACE_BEGIN namespace number { -namespace impl { - -// Forward-declare (maybe don't want number_utils.h included here): -class DecNum; - -/** - * A class for representing a number to be processed by the decimal formatting pipeline. Includes - * methods for rounding, plural rules, and decimal digit extraction. - * - *

      By design, this is NOT IMMUTABLE and NOT THREAD SAFE. It is intended to be an intermediate - * object holding state during a pass through the decimal formatting pipeline. - * - *

      Represents numbers and digit display properties using Binary Coded Decimal (BCD). - * - *

      Java has multiple implementations for testing, but C++ has only one implementation. - */ -class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { - public: - /** Copy constructor. */ - DecimalQuantity(const DecimalQuantity &other); - - /** Move constructor. */ - DecimalQuantity(DecimalQuantity &&src) U_NOEXCEPT; - - DecimalQuantity(); - - ~DecimalQuantity() override; - - /** - * Sets this instance to be equal to another instance. - * - * @param other The instance to copy from. - */ - DecimalQuantity &operator=(const DecimalQuantity &other); - - /** Move assignment */ - DecimalQuantity &operator=(DecimalQuantity&& src) U_NOEXCEPT; - - /** - * Sets the minimum integer digits that this {@link DecimalQuantity} should generate. - * This method does not perform rounding. - * - * @param minInt The minimum number of integer digits. - */ - void setMinInteger(int32_t minInt); - - /** - * Sets the minimum fraction digits that this {@link DecimalQuantity} should generate. - * This method does not perform rounding. - * - * @param minFrac The minimum number of fraction digits. - */ - void setMinFraction(int32_t minFrac); - - /** - * Truncates digits from the upper magnitude of the number in order to satisfy the - * specified maximum number of integer digits. - * - * @param maxInt The maximum number of integer digits. - */ - void applyMaxInteger(int32_t maxInt); - - /** - * Rounds the number to a specified interval, such as 0.05. - * - *

      If rounding to a power of ten, use the more efficient {@link #roundToMagnitude} instead. - * - * @param increment The increment to which to round. - * @param magnitude The power of 10 to which to round. - * @param roundingMode The {@link RoundingMode} to use if rounding is necessary. - */ - void roundToIncrement( - uint64_t increment, - digits_t magnitude, - RoundingMode roundingMode, - UErrorCode& status); - - /** Removes all fraction digits. */ - void truncate(); - - /** - * Rounds the number to the nearest multiple of 5 at the specified magnitude. - * For example, when magnitude == -2, this performs rounding to the nearest 0.05. - * - * @param magnitude The magnitude at which the digit should become either 0 or 5. - * @param roundingMode Rounding strategy. - */ - void roundToNickel(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status); - - /** - * Rounds the number to a specified magnitude (power of ten). - * - * @param roundingMagnitude The power of ten to which to round. For example, a value of -2 will - * round to 2 decimal places. - * @param roundingMode The {@link RoundingMode} to use if rounding is necessary. - */ - void roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status); - - /** - * Rounds the number to an infinite number of decimal points. This has no effect except for - * forcing the double in {@link DecimalQuantity_AbstractBCD} to adopt its exact representation. - */ - void roundToInfinity(); - - /** - * 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 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 occurred; false otherwise. - */ - bool adjustMagnitude(int32_t delta); - - /** - * Scales the number such that the least significant nonzero digit is at magnitude 0. - * - * @return The previous magnitude of the least significant digit. - */ - int32_t adjustToZeroScale(); - - /** - * @return The power of ten corresponding to the most significant nonzero digit. - * The number must not be zero. - */ - int32_t getMagnitude() const; - - /** - * @return The value of the (suppressed) exponent after the number has been - * put into a notation with exponents (ex: compact, scientific). Ex: given - * the number 1000 as "1K" / "1E3", the return value will be 3 (positive). - */ - int32_t getExponent() const; - - /** - * Adjusts the value for the (suppressed) exponent stored when using - * notation with exponents (ex: compact, scientific). - * - *

      Adjusting the exponent is decoupled from {@link #adjustMagnitude} in - * order to allow flexibility for {@link StandardPlural} to be selected in - * formatting (ex: for compact notation) either with or without the exponent - * applied in the value of the number. - * @param delta - * The value to adjust the exponent by. - */ - void adjustExponent(int32_t delta); - - /** - * Resets the DecimalQuantity to the value before adjustMagnitude and adjustExponent. - */ - void resetExponent(); - - /** - * @return Whether the value represented by this {@link DecimalQuantity} is - * zero, infinity, or NaN. - */ - bool isZeroish() const; - - /** @return Whether the value represented by this {@link DecimalQuantity} is less than zero. */ - bool isNegative() const; - - /** @return The appropriate value from the Signum enum. */ - Signum signum() const; - - /** @return Whether the value represented by this {@link DecimalQuantity} is infinite. */ - bool isInfinite() const U_OVERRIDE; - - /** @return Whether the value represented by this {@link DecimalQuantity} is not a number. */ - bool isNaN() const U_OVERRIDE; - - /** - * Note: this method incorporates the value of {@code exponent} - * (for cases such as compact notation) to return the proper long value - * represented by the result. - * @param truncateIfOverflow if false and the number does NOT fit, fails with an assertion error. - */ - int64_t toLong(bool truncateIfOverflow = false) const; - - /** - * Note: this method incorporates the value of {@code exponent} - * (for cases such as compact notation) to return the proper long value - * represented by the result. - */ - 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. */ - DecNum& toDecNum(DecNum& output, UErrorCode& status) const; - - DecimalQuantity &setToInt(int32_t n); - - DecimalQuantity &setToLong(int64_t n); - - DecimalQuantity &setToDouble(double n); - - /** - * Produces a DecimalQuantity that was parsed from a string by the decNumber - * C Library. - * - * decNumber is similar to BigDecimal in Java, and supports parsing strings - * such as "123.456621E+40". - */ - DecimalQuantity &setToDecNumber(StringPiece n, UErrorCode& status); - - /** Internal method if the caller already has a DecNum. */ - DecimalQuantity &setToDecNum(const DecNum& n, UErrorCode& status); - - /** Returns a DecimalQuantity after parsing the input string. */ - static DecimalQuantity fromExponentString(UnicodeString n, UErrorCode& status); - - /** - * Appends a digit, optionally with one or more leading zeros, to the end of the value represented - * by this DecimalQuantity. - * - *

      The primary use of this method is to construct numbers during a parsing loop. It allows - * parsing to take advantage of the digit list infrastructure primarily designed for formatting. - * - * @param value The digit to append. - * @param leadingZeros The number of zeros to append before the digit. For example, if the value - * in this instance starts as 12.3, and you append a 4 with 1 leading zero, the value becomes - * 12.304. - * @param appendAsInteger If true, increase the magnitude of existing digits to make room for the - * new digit. If false, append to the end like a fraction digit. If true, there must not be - * any fraction digits already in the number. - * @internal - * @deprecated This API is ICU internal only. - */ - void appendDigit(int8_t value, int32_t leadingZeros, bool appendAsInteger); - - 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. - * - * @param magnitude The magnitude of the digit. - * @return The digit at the specified magnitude. - */ - int8_t getDigit(int32_t magnitude) const; - - /** - * Gets the largest power of ten that needs to be displayed. The value returned by this function - * will be bounded between minInt and maxInt. - * - * @return The highest-magnitude digit to be displayed. - */ - int32_t getUpperDisplayMagnitude() const; - - /** - * Gets the smallest power of ten that needs to be displayed. The value returned by this function - * will be bounded between -minFrac and -maxFrac. - * - * @return The lowest-magnitude digit to be displayed. - */ - int32_t getLowerDisplayMagnitude() const; - - int32_t fractionCount() const; - - int32_t fractionCountWithoutTrailingZeros() const; - - void clear(); - - /** This method is for internal testing only. */ - uint64_t getPositionFingerprint() const; - -// /** -// * If the given {@link FieldPosition} is a {@link UFieldPosition}, populates it with the fraction -// * length and fraction long value. If the argument is not a {@link UFieldPosition}, nothing -// * happens. -// * -// * @param fp The {@link UFieldPosition} to populate. -// */ -// void populateUFieldPosition(FieldPosition fp); - - /** - * Checks whether the bytes stored in this instance are all valid. For internal unit testing only. - * - * @return An error message if this instance is invalid, or null if this instance is healthy. - */ - const char16_t* checkHealth() const; - - UnicodeString toString() const; - - /** Returns the string in standard exponential notation. */ - UnicodeString toScientificString() const; - - /** Returns the string without exponential notation. Slightly slower than toScientificString(). */ - UnicodeString toPlainString() const; - - /** Returns the string using ASCII digits and using exponential notation for non-zero - exponents, following the UTS 35 specification for plural rule samples. */ - UnicodeString toExponentString() const; - - /** Visible for testing */ - inline bool isUsingBytes() { return usingBytes; } - - /** 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 - * object represents the number "3.14", the BCD will be "0x314" and the scale will be -2. - * - *

      Note that in {@link java.math.BigDecimal}, the scale is defined differently: the number of - * digits after the decimal place, which is the negative of our definition of scale. - */ - int32_t scale; - - /** - * The number of digits in the BCD. For example, "1007" has BCD "0x1007" and precision 4. The - * maximum precision is 16 since a long can hold only 16 digits. - * - *

      This value must be re-calculated whenever the value in bcd changes by using {@link - * #computePrecisionAndCompact()}. - */ - int32_t precision; - - /** - * A bitmask of properties relating to the number represented by this object. - * - * @see #NEGATIVE_FLAG - * @see #INFINITY_FLAG - * @see #NAN_FLAG - */ - int8_t flags; - - // The following three fields relate to the double-to-ascii fast path algorithm. - // When a double is given to DecimalQuantityBCD, it is converted to using a fast algorithm. The - // fast algorithm guarantees correctness to only the first ~12 digits of the double. The process - // of rounding the number ensures that the converted digits are correct, falling back to a slow- - // path algorithm if required. Therefore, if a DecimalQuantity is constructed from a double, it - // is *required* that roundToMagnitude(), roundToIncrement(), or roundToInfinity() is called. If - // you don't round, assertions will fail in certain other methods if you try calling them. - - /** - * Whether the value in the BCD comes from the double fast path without having been rounded to - * ensure correctness - */ - UBool isApproximate; - - /** - * The original number provided by the user and which is represented in BCD. Used when we need to - * re-compute the BCD for an exact double representation. - */ - double origDouble; - - /** - * The change in magnitude relative to the original double. Used when we need to re-compute the - * BCD for an exact double representation. - */ - int32_t origDelta; - - // Positions to keep track of leading and trailing zeros. - // lReqPos is the magnitude of the first required leading zero. - // rReqPos is the magnitude of the last required trailing zero. - int32_t lReqPos = 0; - int32_t rReqPos = 0; - - // The value of the (suppressed) exponent after the number has been put into - // a notation with exponents (ex: compact, scientific). - int32_t exponent = 0; - - /** - * The BCD of the 16 digits of the number represented by this object. Every 4 bits of the long map - * to one digit. For example, the number "12345" in BCD is "0x12345". - * - *

      Whenever bcd changes internally, {@link #compact()} must be called, except in special cases - * like setting the digit to zero. - */ - union { - struct { - int8_t *ptr; - int32_t len; - } bcdBytes; - uint64_t bcdLong; - } fBCD; - - bool usingBytes = false; - - /** - * Whether this {@link DecimalQuantity} has been explicitly converted to an exact double. true if - * backed by a double that was explicitly converted via convertToAccurateDouble; false otherwise. - * Used for testing. - */ - bool explicitExactDouble = false; - - void roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, bool nickel, UErrorCode& status); - - /** - * Returns a single digit from the BCD list. No internal state is changed by calling this method. - * - * @param position The position of the digit to pop, counted in BCD units from the least - * significant digit. If outside the range supported by the implementation, zero is returned. - * @return The digit at the specified location. - */ - int8_t getDigitPos(int32_t position) const; - - /** - * Sets the digit in the BCD list. This method only sets the digit; it is the caller's - * responsibility to call {@link #compact} after setting the digit, and to ensure - * that the precision field is updated to reflect the correct number of digits if a - * nonzero digit is added to the decimal. - * - * @param position The position of the digit to pop, counted in BCD units from the least - * significant digit. If outside the range supported by the implementation, an AssertionError - * is thrown. - * @param value The digit to set at the specified location. - */ - void setDigitPos(int32_t position, int8_t value); - - /** - * Adds zeros to the end of the BCD list. This will result in an invalid BCD representation; it is - * the caller's responsibility to do further manipulation and then call {@link #compact}. - * - * @param numDigits The number of zeros to add. - */ - void shiftLeft(int32_t numDigits); - - /** - * Directly removes digits from the end of the BCD list. - * Updates the scale and precision. - * - * CAUTION: it is the caller's responsibility to call {@link #compact} after this method. - */ - void shiftRight(int32_t numDigits); - - /** - * Directly removes digits from the front of the BCD list. - * Updates precision. - * - * CAUTION: it is the caller's responsibility to call {@link #compact} after this method. - */ - void popFromLeft(int32_t numDigits); - - /** - * Sets the internal representation to zero. Clears any values stored in scale, precision, - * hasDouble, origDouble, origDelta, exponent, and BCD data. - */ - void setBcdToZero(); - - /** - * Sets the internal BCD state to represent the value in the given int. The int is guaranteed to - * be either positive. The internal state is guaranteed to be empty when this method is called. - * - * @param n The value to consume. - */ - void readIntToBcd(int32_t n); - - /** - * Sets the internal BCD state to represent the value in the given long. The long is guaranteed to - * be either positive. The internal state is guaranteed to be empty when this method is called. - * - * @param n The value to consume. - */ - void readLongToBcd(int64_t n); - - 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 - * digit. - * - *

      This method must always be called when bcd changes in order for assumptions to be correct in - * methods like {@link #fractionCount()}. - */ - void compact(); - - void _setToInt(int32_t n); - - void _setToLong(int64_t n); - - void _setToDoubleFast(double n); - - void _setToDecNum(const DecNum& dn, UErrorCode& status); - - static int32_t getVisibleFractionCount(UnicodeString value); - - void convertToAccurateDouble(); - - /** Ensure that a byte array of at least 40 digits is allocated. */ - void ensureCapacity(); - - void ensureCapacity(int32_t capacity); - - /** Switches the internal storage mechanism between the 64-bit long and the byte array. */ - void switchStorage(); -}; - -} // namespace impl -} // namespace number -U_NAMESPACE_END - - -#endif //__NUMBER_DECIMALQUANTITY_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_DECIMALQUANTITY_H__ +#define __NUMBER_DECIMALQUANTITY_H__ + +#include +#include "unicode/umachine.h" +#include "standardplural.h" +#include "plurrule_impl.h" +#include "number_types.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +// Forward-declare (maybe don't want number_utils.h included here): +class DecNum; + +/** + * A class for representing a number to be processed by the decimal formatting pipeline. Includes + * methods for rounding, plural rules, and decimal digit extraction. + * + *

      By design, this is NOT IMMUTABLE and NOT THREAD SAFE. It is intended to be an intermediate + * object holding state during a pass through the decimal formatting pipeline. + * + *

      Represents numbers and digit display properties using Binary Coded Decimal (BCD). + * + *

      Java has multiple implementations for testing, but C++ has only one implementation. + */ +class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { + public: + /** Copy constructor. */ + DecimalQuantity(const DecimalQuantity &other); + + /** Move constructor. */ + DecimalQuantity(DecimalQuantity &&src) noexcept; + + DecimalQuantity(); + + ~DecimalQuantity() override; + + /** + * Sets this instance to be equal to another instance. + * + * @param other The instance to copy from. + */ + DecimalQuantity &operator=(const DecimalQuantity &other); + + /** Move assignment */ + DecimalQuantity &operator=(DecimalQuantity&& src) noexcept; + + /** + * Sets the minimum integer digits that this {@link DecimalQuantity} should generate. + * This method does not perform rounding. + * + * @param minInt The minimum number of integer digits. + */ + void setMinInteger(int32_t minInt); + + /** + * Sets the minimum fraction digits that this {@link DecimalQuantity} should generate. + * This method does not perform rounding. + * + * @param minFrac The minimum number of fraction digits. + */ + void setMinFraction(int32_t minFrac); + + /** + * Truncates digits from the upper magnitude of the number in order to satisfy the + * specified maximum number of integer digits. + * + * @param maxInt The maximum number of integer digits. + */ + void applyMaxInteger(int32_t maxInt); + + /** + * Rounds the number to a specified interval, such as 0.05. + * + *

      If rounding to a power of ten, use the more efficient {@link #roundToMagnitude} instead. + * + * @param increment The increment to which to round. + * @param magnitude The power of 10 to which to round. + * @param roundingMode The {@link RoundingMode} to use if rounding is necessary. + */ + void roundToIncrement( + uint64_t increment, + digits_t magnitude, + RoundingMode roundingMode, + UErrorCode& status); + + /** Removes all fraction digits. */ + void truncate(); + + /** + * Rounds the number to the nearest multiple of 5 at the specified magnitude. + * For example, when magnitude == -2, this performs rounding to the nearest 0.05. + * + * @param magnitude The magnitude at which the digit should become either 0 or 5. + * @param roundingMode Rounding strategy. + */ + void roundToNickel(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status); + + /** + * Rounds the number to a specified magnitude (power of ten). + * + * @param roundingMagnitude The power of ten to which to round. For example, a value of -2 will + * round to 2 decimal places. + * @param roundingMode The {@link RoundingMode} to use if rounding is necessary. + */ + void roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status); + + /** + * Rounds the number to an infinite number of decimal points. This has no effect except for + * forcing the double in {@link DecimalQuantity_AbstractBCD} to adopt its exact representation. + */ + void roundToInfinity(); + + /** + * 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 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 occurred; false otherwise. + */ + bool adjustMagnitude(int32_t delta); + + /** + * Scales the number such that the least significant nonzero digit is at magnitude 0. + * + * @return The previous magnitude of the least significant digit. + */ + int32_t adjustToZeroScale(); + + /** + * @return The power of ten corresponding to the most significant nonzero digit. + * The number must not be zero. + */ + int32_t getMagnitude() const; + + /** + * @return The value of the (suppressed) exponent after the number has been + * put into a notation with exponents (ex: compact, scientific). Ex: given + * the number 1000 as "1K" / "1E3", the return value will be 3 (positive). + */ + int32_t getExponent() const; + + /** + * Adjusts the value for the (suppressed) exponent stored when using + * notation with exponents (ex: compact, scientific). + * + *

      Adjusting the exponent is decoupled from {@link #adjustMagnitude} in + * order to allow flexibility for {@link StandardPlural} to be selected in + * formatting (ex: for compact notation) either with or without the exponent + * applied in the value of the number. + * @param delta + * The value to adjust the exponent by. + */ + void adjustExponent(int32_t delta); + + /** + * Resets the DecimalQuantity to the value before adjustMagnitude and adjustExponent. + */ + void resetExponent(); + + /** + * @return Whether the value represented by this {@link DecimalQuantity} is + * zero, infinity, or NaN. + */ + bool isZeroish() const; + + /** @return Whether the value represented by this {@link DecimalQuantity} is less than zero. */ + bool isNegative() const; + + /** @return The appropriate value from the Signum enum. */ + Signum signum() const; + + /** @return Whether the value represented by this {@link DecimalQuantity} is infinite. */ + bool isInfinite() const override; + + /** @return Whether the value represented by this {@link DecimalQuantity} is not a number. */ + bool isNaN() const override; + + /** + * Note: this method incorporates the value of {@code exponent} + * (for cases such as compact notation) to return the proper long value + * represented by the result. + * @param truncateIfOverflow if false and the number does NOT fit, fails with an assertion error. + */ + int64_t toLong(bool truncateIfOverflow = false) const; + + /** + * Note: this method incorporates the value of {@code exponent} + * (for cases such as compact notation) to return the proper long value + * represented by the result. + */ + 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. */ + DecNum& toDecNum(DecNum& output, UErrorCode& status) const; + + DecimalQuantity &setToInt(int32_t n); + + DecimalQuantity &setToLong(int64_t n); + + DecimalQuantity &setToDouble(double n); + + /** + * Produces a DecimalQuantity that was parsed from a string by the decNumber + * C Library. + * + * decNumber is similar to BigDecimal in Java, and supports parsing strings + * such as "123.456621E+40". + */ + DecimalQuantity &setToDecNumber(StringPiece n, UErrorCode& status); + + /** Internal method if the caller already has a DecNum. */ + DecimalQuantity &setToDecNum(const DecNum& n, UErrorCode& status); + + /** Returns a DecimalQuantity after parsing the input string. */ + static DecimalQuantity fromExponentString(UnicodeString n, UErrorCode& status); + + /** + * Appends a digit, optionally with one or more leading zeros, to the end of the value represented + * by this DecimalQuantity. + * + *

      The primary use of this method is to construct numbers during a parsing loop. It allows + * parsing to take advantage of the digit list infrastructure primarily designed for formatting. + * + * @param value The digit to append. + * @param leadingZeros The number of zeros to append before the digit. For example, if the value + * in this instance starts as 12.3, and you append a 4 with 1 leading zero, the value becomes + * 12.304. + * @param appendAsInteger If true, increase the magnitude of existing digits to make room for the + * new digit. If false, append to the end like a fraction digit. If true, there must not be + * any fraction digits already in the number. + * @internal + * @deprecated This API is ICU internal only. + */ + void appendDigit(int8_t value, int32_t leadingZeros, bool appendAsInteger); + + double getPluralOperand(PluralOperand operand) const override; + + bool hasIntegerValue() const 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. + * + * @param magnitude The magnitude of the digit. + * @return The digit at the specified magnitude. + */ + int8_t getDigit(int32_t magnitude) const; + + /** + * Gets the largest power of ten that needs to be displayed. The value returned by this function + * will be bounded between minInt and maxInt. + * + * @return The highest-magnitude digit to be displayed. + */ + int32_t getUpperDisplayMagnitude() const; + + /** + * Gets the smallest power of ten that needs to be displayed. The value returned by this function + * will be bounded between -minFrac and -maxFrac. + * + * @return The lowest-magnitude digit to be displayed. + */ + int32_t getLowerDisplayMagnitude() const; + + int32_t fractionCount() const; + + int32_t fractionCountWithoutTrailingZeros() const; + + void clear(); + + /** This method is for internal testing only. */ + uint64_t getPositionFingerprint() const; + +// /** +// * If the given {@link FieldPosition} is a {@link UFieldPosition}, populates it with the fraction +// * length and fraction long value. If the argument is not a {@link UFieldPosition}, nothing +// * happens. +// * +// * @param fp The {@link UFieldPosition} to populate. +// */ +// void populateUFieldPosition(FieldPosition fp); + + /** + * Checks whether the bytes stored in this instance are all valid. For internal unit testing only. + * + * @return An error message if this instance is invalid, or null if this instance is healthy. + */ + const char16_t* checkHealth() const; + + UnicodeString toString() const; + + /** Returns the string in standard exponential notation. */ + UnicodeString toScientificString() const; + + /** Returns the string without exponential notation. Slightly slower than toScientificString(). */ + UnicodeString toPlainString() const; + + /** Returns the string using ASCII digits and using exponential notation for non-zero + exponents, following the UTS 35 specification for plural rule samples. */ + UnicodeString toExponentString() const; + + /** Visible for testing */ + inline bool isUsingBytes() { return usingBytes; } + + /** 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 + * object represents the number "3.14", the BCD will be "0x314" and the scale will be -2. + * + *

      Note that in {@link java.math.BigDecimal}, the scale is defined differently: the number of + * digits after the decimal place, which is the negative of our definition of scale. + */ + int32_t scale; + + /** + * The number of digits in the BCD. For example, "1007" has BCD "0x1007" and precision 4. The + * maximum precision is 16 since a long can hold only 16 digits. + * + *

      This value must be re-calculated whenever the value in bcd changes by using {@link + * #computePrecisionAndCompact()}. + */ + int32_t precision; + + /** + * A bitmask of properties relating to the number represented by this object. + * + * @see #NEGATIVE_FLAG + * @see #INFINITY_FLAG + * @see #NAN_FLAG + */ + int8_t flags; + + // The following three fields relate to the double-to-ascii fast path algorithm. + // When a double is given to DecimalQuantityBCD, it is converted to using a fast algorithm. The + // fast algorithm guarantees correctness to only the first ~12 digits of the double. The process + // of rounding the number ensures that the converted digits are correct, falling back to a slow- + // path algorithm if required. Therefore, if a DecimalQuantity is constructed from a double, it + // is *required* that roundToMagnitude(), roundToIncrement(), or roundToInfinity() is called. If + // you don't round, assertions will fail in certain other methods if you try calling them. + + /** + * Whether the value in the BCD comes from the double fast path without having been rounded to + * ensure correctness + */ + UBool isApproximate; + + /** + * The original number provided by the user and which is represented in BCD. Used when we need to + * re-compute the BCD for an exact double representation. + */ + double origDouble; + + /** + * The change in magnitude relative to the original double. Used when we need to re-compute the + * BCD for an exact double representation. + */ + int32_t origDelta; + + // Positions to keep track of leading and trailing zeros. + // lReqPos is the magnitude of the first required leading zero. + // rReqPos is the magnitude of the last required trailing zero. + int32_t lReqPos = 0; + int32_t rReqPos = 0; + + // The value of the (suppressed) exponent after the number has been put into + // a notation with exponents (ex: compact, scientific). + int32_t exponent = 0; + + /** + * The BCD of the 16 digits of the number represented by this object. Every 4 bits of the long map + * to one digit. For example, the number "12345" in BCD is "0x12345". + * + *

      Whenever bcd changes internally, {@link #compact()} must be called, except in special cases + * like setting the digit to zero. + */ + union { + struct { + int8_t *ptr; + int32_t len; + } bcdBytes; + uint64_t bcdLong; + } fBCD; + + bool usingBytes = false; + + /** + * Whether this {@link DecimalQuantity} has been explicitly converted to an exact double. true if + * backed by a double that was explicitly converted via convertToAccurateDouble; false otherwise. + * Used for testing. + */ + bool explicitExactDouble = false; + + void roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, bool nickel, UErrorCode& status); + + /** + * Returns a single digit from the BCD list. No internal state is changed by calling this method. + * + * @param position The position of the digit to pop, counted in BCD units from the least + * significant digit. If outside the range supported by the implementation, zero is returned. + * @return The digit at the specified location. + */ + int8_t getDigitPos(int32_t position) const; + + /** + * Sets the digit in the BCD list. This method only sets the digit; it is the caller's + * responsibility to call {@link #compact} after setting the digit, and to ensure + * that the precision field is updated to reflect the correct number of digits if a + * nonzero digit is added to the decimal. + * + * @param position The position of the digit to pop, counted in BCD units from the least + * significant digit. If outside the range supported by the implementation, an AssertionError + * is thrown. + * @param value The digit to set at the specified location. + */ + void setDigitPos(int32_t position, int8_t value); + + /** + * Adds zeros to the end of the BCD list. This will result in an invalid BCD representation; it is + * the caller's responsibility to do further manipulation and then call {@link #compact}. + * + * @param numDigits The number of zeros to add. + */ + void shiftLeft(int32_t numDigits); + + /** + * Directly removes digits from the end of the BCD list. + * Updates the scale and precision. + * + * CAUTION: it is the caller's responsibility to call {@link #compact} after this method. + */ + void shiftRight(int32_t numDigits); + + /** + * Directly removes digits from the front of the BCD list. + * Updates precision. + * + * CAUTION: it is the caller's responsibility to call {@link #compact} after this method. + */ + void popFromLeft(int32_t numDigits); + + /** + * Sets the internal representation to zero. Clears any values stored in scale, precision, + * hasDouble, origDouble, origDelta, exponent, and BCD data. + */ + void setBcdToZero(); + + /** + * Sets the internal BCD state to represent the value in the given int. The int is guaranteed to + * be either positive. The internal state is guaranteed to be empty when this method is called. + * + * @param n The value to consume. + */ + void readIntToBcd(int32_t n); + + /** + * Sets the internal BCD state to represent the value in the given long. The long is guaranteed to + * be either positive. The internal state is guaranteed to be empty when this method is called. + * + * @param n The value to consume. + */ + void readLongToBcd(int64_t n); + + 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 + * digit. + * + *

      This method must always be called when bcd changes in order for assumptions to be correct in + * methods like {@link #fractionCount()}. + */ + void compact(); + + void _setToInt(int32_t n); + + void _setToLong(int64_t n); + + void _setToDoubleFast(double n); + + void _setToDecNum(const DecNum& dn, UErrorCode& status); + + static int32_t getVisibleFractionCount(UnicodeString value); + + void convertToAccurateDouble(); + + /** Ensure that a byte array of at least 40 digits is allocated. */ + void ensureCapacity(); + + void ensureCapacity(int32_t capacity); + + /** Switches the internal storage mechanism between the 64-bit long and the byte array. */ + void switchStorage(); +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + + +#endif //__NUMBER_DECIMALQUANTITY_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_decimfmtprops.cpp b/deps/icu-small/source/i18n/number_decimfmtprops.cpp index 6dbfc69ec83cd0..297f63b60599f7 100644 --- a/deps/icu-small/source/i18n/number_decimfmtprops.cpp +++ b/deps/icu-small/source/i18n/number_decimfmtprops.cpp @@ -1,154 +1,154 @@ -// © 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 - -#include "number_decimfmtprops.h" -#include "umutex.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - - -namespace { - -alignas(DecimalFormatProperties) -char kRawDefaultProperties[sizeof(DecimalFormatProperties)]; - -icu::UInitOnce gDefaultPropertiesInitOnce {}; - -void U_CALLCONV initDefaultProperties(UErrorCode&) { - // can't fail, uses placement new into statically allocated space. - new(kRawDefaultProperties) DecimalFormatProperties(); // set to the default instance -} - -} - - -DecimalFormatProperties::DecimalFormatProperties() { - clear(); -} - -void DecimalFormatProperties::clear() { - compactStyle.nullify(); - currency.nullify(); - currencyPluralInfo.fPtr.adoptInstead(nullptr); - currencyUsage.nullify(); - decimalPatternMatchRequired = false; - decimalSeparatorAlwaysShown = false; - exponentSignAlwaysShown = false; - currencyAsDecimal = false; - formatFailIfMoreThanMaxDigits = false; - formatWidth = -1; - groupingSize = -1; - groupingUsed = true; - magnitudeMultiplier = 0; - maximumFractionDigits = -1; - maximumIntegerDigits = -1; - maximumSignificantDigits = -1; - minimumExponentDigits = -1; - minimumFractionDigits = -1; - minimumGroupingDigits = -1; - minimumIntegerDigits = -1; - minimumSignificantDigits = -1; - multiplier = 1; - multiplierScale = 0; - negativePrefix.setToBogus(); - negativePrefixPattern.setToBogus(); - negativeSuffix.setToBogus(); - negativeSuffixPattern.setToBogus(); - padPosition.nullify(); - padString.setToBogus(); - parseCaseSensitive = false; - parseIntegerOnly = false; - parseMode.nullify(); - parseNoExponent = false; - parseToBigDecimal = false; - parseAllInput = UNUM_MAYBE; - positivePrefix.setToBogus(); - positivePrefixPattern.setToBogus(); - positiveSuffix.setToBogus(); - positiveSuffixPattern.setToBogus(); - roundingIncrement = 0.0; - roundingMode.nullify(); - secondaryGroupingSize = -1; - signAlwaysShown = false; -} - -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 && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown; - eq = eq && exponentSignAlwaysShown == other.exponentSignAlwaysShown; - eq = eq && currencyAsDecimal == other.currencyAsDecimal; - eq = eq && formatFailIfMoreThanMaxDigits == other.formatFailIfMoreThanMaxDigits; - eq = eq && formatWidth == other.formatWidth; - eq = eq && magnitudeMultiplier == other.magnitudeMultiplier; - eq = eq && maximumSignificantDigits == other.maximumSignificantDigits; - eq = eq && minimumExponentDigits == other.minimumExponentDigits; - eq = eq && minimumGroupingDigits == other.minimumGroupingDigits; - eq = eq && minimumSignificantDigits == other.minimumSignificantDigits; - eq = eq && multiplier == other.multiplier; - eq = eq && multiplierScale == other.multiplierScale; - eq = eq && negativePrefix == other.negativePrefix; - eq = eq && negativeSuffix == other.negativeSuffix; - eq = eq && padPosition == other.padPosition; - eq = eq && padString == other.padString; - eq = eq && positivePrefix == other.positivePrefix; - eq = eq && positiveSuffix == other.positiveSuffix; - 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); -} - -const DecimalFormatProperties& DecimalFormatProperties::getDefault() { - UErrorCode localStatus = U_ZERO_ERROR; - umtx_initOnce(gDefaultPropertiesInitOnce, &initDefaultProperties, localStatus); - return *reinterpret_cast(kRawDefaultProperties); -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include "number_decimfmtprops.h" +#include "umutex.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +namespace { + +alignas(DecimalFormatProperties) +char kRawDefaultProperties[sizeof(DecimalFormatProperties)]; + +icu::UInitOnce gDefaultPropertiesInitOnce {}; + +void U_CALLCONV initDefaultProperties(UErrorCode&) { + // can't fail, uses placement new into statically allocated space. + new(kRawDefaultProperties) DecimalFormatProperties(); // set to the default instance +} + +} + + +DecimalFormatProperties::DecimalFormatProperties() { + clear(); +} + +void DecimalFormatProperties::clear() { + compactStyle.nullify(); + currency.nullify(); + currencyPluralInfo.fPtr.adoptInstead(nullptr); + currencyUsage.nullify(); + decimalPatternMatchRequired = false; + decimalSeparatorAlwaysShown = false; + exponentSignAlwaysShown = false; + currencyAsDecimal = false; + formatFailIfMoreThanMaxDigits = false; + formatWidth = -1; + groupingSize = -1; + groupingUsed = true; + magnitudeMultiplier = 0; + maximumFractionDigits = -1; + maximumIntegerDigits = -1; + maximumSignificantDigits = -1; + minimumExponentDigits = -1; + minimumFractionDigits = -1; + minimumGroupingDigits = -1; + minimumIntegerDigits = -1; + minimumSignificantDigits = -1; + multiplier = 1; + multiplierScale = 0; + negativePrefix.setToBogus(); + negativePrefixPattern.setToBogus(); + negativeSuffix.setToBogus(); + negativeSuffixPattern.setToBogus(); + padPosition.nullify(); + padString.setToBogus(); + parseCaseSensitive = false; + parseIntegerOnly = false; + parseMode.nullify(); + parseNoExponent = false; + parseToBigDecimal = false; + parseAllInput = UNUM_MAYBE; + positivePrefix.setToBogus(); + positivePrefixPattern.setToBogus(); + positiveSuffix.setToBogus(); + positiveSuffixPattern.setToBogus(); + roundingIncrement = 0.0; + roundingMode.nullify(); + secondaryGroupingSize = -1; + signAlwaysShown = false; +} + +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 && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown; + eq = eq && exponentSignAlwaysShown == other.exponentSignAlwaysShown; + eq = eq && currencyAsDecimal == other.currencyAsDecimal; + eq = eq && formatFailIfMoreThanMaxDigits == other.formatFailIfMoreThanMaxDigits; + eq = eq && formatWidth == other.formatWidth; + eq = eq && magnitudeMultiplier == other.magnitudeMultiplier; + eq = eq && maximumSignificantDigits == other.maximumSignificantDigits; + eq = eq && minimumExponentDigits == other.minimumExponentDigits; + eq = eq && minimumGroupingDigits == other.minimumGroupingDigits; + eq = eq && minimumSignificantDigits == other.minimumSignificantDigits; + eq = eq && multiplier == other.multiplier; + eq = eq && multiplierScale == other.multiplierScale; + eq = eq && negativePrefix == other.negativePrefix; + eq = eq && negativeSuffix == other.negativeSuffix; + eq = eq && padPosition == other.padPosition; + eq = eq && padString == other.padString; + eq = eq && positivePrefix == other.positivePrefix; + eq = eq && positiveSuffix == other.positiveSuffix; + 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); +} + +const DecimalFormatProperties& DecimalFormatProperties::getDefault() { + UErrorCode localStatus = U_ZERO_ERROR; + umtx_initOnce(gDefaultPropertiesInitOnce, &initDefaultProperties, localStatus); + return *reinterpret_cast(kRawDefaultProperties); +} + +#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 5f72f649842e66..aa1797824a4e20 100644 --- a/deps/icu-small/source/i18n/number_decimfmtprops.h +++ b/deps/icu-small/source/i18n/number_decimfmtprops.h @@ -1,176 +1,176 @@ -// © 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_DECIMFMTPROPS_H__ -#define __NUMBER_DECIMFMTPROPS_H__ - -#include "unicode/unistr.h" -#include -#include "unicode/plurrule.h" -#include "unicode/currpinf.h" -#include "unicode/unum.h" -#include "unicode/localpointer.h" -#include "number_types.h" - -U_NAMESPACE_BEGIN - -// Export an explicit template instantiation of the LocalPointer that is used as a -// data member of CurrencyPluralInfoWrapper. -// (When building DLLs for Windows this is required.) -#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN -#if defined(_MSC_VER) -// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!= -#pragma warning(push) -#pragma warning(disable: 4661) -#endif -template class U_I18N_API LocalPointerBase; -template class U_I18N_API LocalPointer; -#if defined(_MSC_VER) -#pragma warning(pop) -#endif -#endif - -namespace number { -namespace impl { - -// Exported as U_I18N_API because it is a public member field of exported DecimalFormatProperties -// Using this wrapper is rather unfortunate, but is needed on Windows platforms in order to allow -// for DLL-exporting a fully specified template instantiation. -class U_I18N_API CurrencyPluralInfoWrapper { -public: - LocalPointer fPtr; - - CurrencyPluralInfoWrapper() = default; - - CurrencyPluralInfoWrapper(const CurrencyPluralInfoWrapper& other) { - if (!other.fPtr.isNull()) { - fPtr.adoptInstead(new CurrencyPluralInfo(*other.fPtr)); - } - } - - CurrencyPluralInfoWrapper& operator=(const CurrencyPluralInfoWrapper& other) { - if (this != &other && // self-assignment: no-op - !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 : public UMemory { - - public: - NullableValue compactStyle; - NullableValue currency; - CurrencyPluralInfoWrapper currencyPluralInfo; - NullableValue currencyUsage; - bool decimalPatternMatchRequired; - bool decimalSeparatorAlwaysShown; - bool exponentSignAlwaysShown; - bool currencyAsDecimal; - bool formatFailIfMoreThanMaxDigits; // ICU4C-only - int32_t formatWidth; - int32_t groupingSize; - bool groupingUsed; - int32_t magnitudeMultiplier; // internal field like multiplierScale but separate to avoid conflict - int32_t maximumFractionDigits; - int32_t maximumIntegerDigits; - int32_t maximumSignificantDigits; - int32_t minimumExponentDigits; - int32_t minimumFractionDigits; - int32_t minimumGroupingDigits; - int32_t minimumIntegerDigits; - int32_t minimumSignificantDigits; - int32_t multiplier; - int32_t multiplierScale; // ICU4C-only - UnicodeString negativePrefix; - UnicodeString negativePrefixPattern; - UnicodeString negativeSuffix; - UnicodeString negativeSuffixPattern; - NullableValue padPosition; - UnicodeString padString; - bool parseCaseSensitive; - bool parseIntegerOnly; - NullableValue parseMode; - bool parseNoExponent; - bool parseToBigDecimal; // TODO: Not needed in ICU4C? - UNumberFormatAttributeValue parseAllInput; // ICU4C-only - //PluralRules pluralRules; - UnicodeString positivePrefix; - UnicodeString positivePrefixPattern; - UnicodeString positiveSuffix; - UnicodeString positiveSuffixPattern; - double roundingIncrement; - NullableValue roundingMode; - int32_t secondaryGroupingSize; - bool signAlwaysShown; - - DecimalFormatProperties(); - - inline bool operator==(const DecimalFormatProperties& other) const { - return _equals(other, false); - } - - void clear(); - - /** - * Checks for equality to the default DecimalFormatProperties, but ignores the prescribed set of - * options for fast-path formatting. - */ - bool equalsDefaultExceptFastFormat() const; - - /** - * Returns the default DecimalFormatProperties instance. - */ - static const DecimalFormatProperties& getDefault(); - - private: - bool _equals(const DecimalFormatProperties& other, bool ignoreForFastFormat) const; -}; - -} // namespace impl -} // namespace number -U_NAMESPACE_END - - -#endif //__NUMBER_DECIMFMTPROPS_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_DECIMFMTPROPS_H__ +#define __NUMBER_DECIMFMTPROPS_H__ + +#include "unicode/unistr.h" +#include +#include "unicode/plurrule.h" +#include "unicode/currpinf.h" +#include "unicode/unum.h" +#include "unicode/localpointer.h" +#include "number_types.h" + +U_NAMESPACE_BEGIN + +// Export an explicit template instantiation of the LocalPointer that is used as a +// data member of CurrencyPluralInfoWrapper. +// (When building DLLs for Windows this is required.) +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +#if defined(_MSC_VER) +// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!= +#pragma warning(push) +#pragma warning(disable: 4661) +#endif +template class U_I18N_API LocalPointerBase; +template class U_I18N_API LocalPointer; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#endif + +namespace number { +namespace impl { + +// Exported as U_I18N_API because it is a public member field of exported DecimalFormatProperties +// Using this wrapper is rather unfortunate, but is needed on Windows platforms in order to allow +// for DLL-exporting a fully specified template instantiation. +class U_I18N_API CurrencyPluralInfoWrapper { +public: + LocalPointer fPtr; + + CurrencyPluralInfoWrapper() = default; + + CurrencyPluralInfoWrapper(const CurrencyPluralInfoWrapper& other) { + if (!other.fPtr.isNull()) { + fPtr.adoptInstead(new CurrencyPluralInfo(*other.fPtr)); + } + } + + CurrencyPluralInfoWrapper& operator=(const CurrencyPluralInfoWrapper& other) { + if (this != &other && // self-assignment: no-op + !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 : public UMemory { + + public: + NullableValue compactStyle; + NullableValue currency; + CurrencyPluralInfoWrapper currencyPluralInfo; + NullableValue currencyUsage; + bool decimalPatternMatchRequired; + bool decimalSeparatorAlwaysShown; + bool exponentSignAlwaysShown; + bool currencyAsDecimal; + bool formatFailIfMoreThanMaxDigits; // ICU4C-only + int32_t formatWidth; + int32_t groupingSize; + bool groupingUsed; + int32_t magnitudeMultiplier; // internal field like multiplierScale but separate to avoid conflict + int32_t maximumFractionDigits; + int32_t maximumIntegerDigits; + int32_t maximumSignificantDigits; + int32_t minimumExponentDigits; + int32_t minimumFractionDigits; + int32_t minimumGroupingDigits; + int32_t minimumIntegerDigits; + int32_t minimumSignificantDigits; + int32_t multiplier; + int32_t multiplierScale; // ICU4C-only + UnicodeString negativePrefix; + UnicodeString negativePrefixPattern; + UnicodeString negativeSuffix; + UnicodeString negativeSuffixPattern; + NullableValue padPosition; + UnicodeString padString; + bool parseCaseSensitive; + bool parseIntegerOnly; + NullableValue parseMode; + bool parseNoExponent; + bool parseToBigDecimal; // TODO: Not needed in ICU4C? + UNumberFormatAttributeValue parseAllInput; // ICU4C-only + //PluralRules pluralRules; + UnicodeString positivePrefix; + UnicodeString positivePrefixPattern; + UnicodeString positiveSuffix; + UnicodeString positiveSuffixPattern; + double roundingIncrement; + NullableValue roundingMode; + int32_t secondaryGroupingSize; + bool signAlwaysShown; + + DecimalFormatProperties(); + + inline bool operator==(const DecimalFormatProperties& other) const { + return _equals(other, false); + } + + void clear(); + + /** + * Checks for equality to the default DecimalFormatProperties, but ignores the prescribed set of + * options for fast-path formatting. + */ + bool equalsDefaultExceptFastFormat() const; + + /** + * Returns the default DecimalFormatProperties instance. + */ + static const DecimalFormatProperties& getDefault(); + + private: + bool _equals(const DecimalFormatProperties& other, bool ignoreForFastFormat) const; +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + + +#endif //__NUMBER_DECIMFMTPROPS_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_decnum.h b/deps/icu-small/source/i18n/number_decnum.h index 94a0b31bcb5dd7..e9b859b3e8056b 100644 --- a/deps/icu-small/source/i18n/number_decnum.h +++ b/deps/icu-small/source/i18n/number_decnum.h @@ -1,94 +1,94 @@ -// © 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" -#include "bytesinkutil.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; - - /** Is infinity or NaN */ - bool isSpecial() const; - - bool isInfinity() const; - - bool isNaN() const; - - void toString(ByteSink& output, UErrorCode& status) const; - - inline CharString toCharString(UErrorCode& status) const { - CharString cstr; - CharStringByteSink sink(&cstr); - toString(sink, status); - return cstr; - } - - 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 */ +// © 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" +#include "bytesinkutil.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; + + /** Is infinity or NaN */ + bool isSpecial() const; + + bool isInfinity() const; + + bool isNaN() const; + + void toString(ByteSink& output, UErrorCode& status) const; + + inline CharString toCharString(UErrorCode& status) const { + CharString cstr; + CharStringByteSink sink(&cstr); + toString(sink, status); + return cstr; + } + + 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 8a968d922c2912..f3f6ebd9079264 100644 --- a/deps/icu-small/source/i18n/number_fluent.cpp +++ b/deps/icu-small/source/i18n/number_fluent.cpp @@ -1,738 +1,738 @@ -// © 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 - -#include "uassert.h" -#include "unicode/numberformatter.h" -#include "number_decimalquantity.h" -#include "number_formatimpl.h" -#include "umutex.h" -#include "number_asformat.h" -#include "number_utils.h" -#include "number_utypes.h" -#include "number_mapper.h" -#include "util.h" -#include "fphdlimp.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - -#if (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN) && defined(_MSC_VER) -// Ignore MSVC warning 4661. This is generated for NumberFormatterSettings<>::toSkeleton() as this method -// is defined elsewhere (in number_skeletons.cpp). The compiler is warning that the explicit template instantiation -// inside this single translation unit (CPP file) is incomplete, and thus it isn't sure if the template class is -// fully defined. However, since each translation unit explicitly instantiates all the necessary template classes, -// they will all be passed to the linker, and the linker will still find and export all the class members. -#pragma warning(push) -#pragma warning(disable: 4661) -#endif - -template -Derived NumberFormatterSettings::notation(const Notation& notation) const& { - Derived copy(*this); - // NOTE: Slicing is OK. - copy.fMacros.notation = notation; - return copy; -} - -template -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. - copy.fMacros.unit = unit; - return copy; -} - -template -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 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 = std::move(*unit); - delete unit; - } - return copy; -} - -template -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; - return copy; -} - -template -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 = std::move(*perUnit); - delete perUnit; - } - return copy; -} - -template -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.precision = precision; - return copy; -} - -template -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(UNumberGroupingStrategy 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. - copy.fMacros.grouper = Grouper::forStrategy(strategy); - return copy; -} - -template -Derived NumberFormatterSettings::grouping(UNumberGroupingStrategy 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::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::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::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::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::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::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::usage(const StringPiece usage) const& { - Derived copy(*this); - copy.fMacros.usage.set(usage); - return copy; -} - -template -Derived NumberFormatterSettings::usage(const StringPiece usage)&& { - Derived move(std::move(*this)); - move.fMacros.usage.set(usage); - return move; -} - -template -Derived NumberFormatterSettings::displayOptions(const DisplayOptions &displayOptions) const & { - Derived copy(*this); - // `displayCase` does not recognise the `undefined` - if (displayOptions.getGrammaticalCase() == UDISPOPT_GRAMMATICAL_CASE_UNDEFINED) { - copy.fMacros.unitDisplayCase.set(nullptr); - return copy; - } - - copy.fMacros.unitDisplayCase.set( - udispopt_getGrammaticalCaseIdentifier(displayOptions.getGrammaticalCase())); - return copy; -} - -template -Derived NumberFormatterSettings::displayOptions(const DisplayOptions &displayOptions) && { - Derived move(std::move(*this)); - // `displayCase` does not recognise the `undefined` - if (displayOptions.getGrammaticalCase() == UDISPOPT_GRAMMATICAL_CASE_UNDEFINED) { - move.fMacros.unitDisplayCase.set(nullptr); - return move; - } - - move.fMacros.unitDisplayCase.set( - udispopt_getGrammaticalCaseIdentifier(displayOptions.getGrammaticalCase())); - return move; -} - -template -Derived NumberFormatterSettings::unitDisplayCase(const StringPiece unitDisplayCase) const& { - Derived copy(*this); - copy.fMacros.unitDisplayCase.set(unitDisplayCase); - return copy; -} - -template -Derived NumberFormatterSettings::unitDisplayCase(const StringPiece unitDisplayCase)&& { - Derived move(std::move(*this)); - move.fMacros.unitDisplayCase.set(unitDisplayCase); - return move; -} - -template -Derived NumberFormatterSettings::padding(const Padder& padder) const& { - Derived copy(*this); - copy.fMacros.padder = padder; - return copy; -} - -template -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; -} - -// Note: toSkeleton defined in number_skeletons.cpp - -template -LocalPointer NumberFormatterSettings::clone() const & { - return LocalPointer(new Derived(*this)); -} - -template -LocalPointer NumberFormatterSettings::clone() && { - return LocalPointer(new Derived(std::move(*this))); -} - -// Declare all classes that implement NumberFormatterSettings -// See https://stackoverflow.com/a/495056/1407170 -template -class icu::number::NumberFormatterSettings; -template -class icu::number::NumberFormatterSettings; - - -UnlocalizedNumberFormatter NumberFormatter::with() { - UnlocalizedNumberFormatter result; - return result; -} - -LocalizedNumberFormatter NumberFormatter::withLocale(const Locale& locale) { - return with().locale(locale); -} - -// Note: forSkeleton defined in number_skeletons.cpp - - -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 -} - -// Make default copy constructor call the NumberFormatterSettings copy constructor. -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; -} - -// Make default copy constructor call the NumberFormatterSettings copy constructor. -LocalizedNumberFormatter::LocalizedNumberFormatter(const LNF& other) - : LNF(static_cast&>(other)) {} - -LocalizedNumberFormatter::LocalizedNumberFormatter(const NFS& other) - : NFS(other) { - UErrorCode localStatus = U_ZERO_ERROR; // Can't bubble up the error - lnfCopyHelper(static_cast(other), localStatus); -} - -LocalizedNumberFormatter::LocalizedNumberFormatter(LocalizedNumberFormatter&& src) U_NOEXCEPT - : LNF(static_cast&&>(src)) {} - -LocalizedNumberFormatter::LocalizedNumberFormatter(NFS&& src) U_NOEXCEPT - : NFS(std::move(src)) { - lnfMoveHelper(std::move(static_cast(src))); -} - -LocalizedNumberFormatter& LocalizedNumberFormatter::operator=(const LNF& other) { - if (this == &other) { return *this; } // self-assignment: no-op - NFS::operator=(static_cast&>(other)); - UErrorCode localStatus = U_ZERO_ERROR; // Can't bubble up the error - lnfCopyHelper(other, localStatus); - return *this; -} - -LocalizedNumberFormatter& LocalizedNumberFormatter::operator=(LNF&& src) U_NOEXCEPT { - NFS::operator=(static_cast&&>(src)); - lnfMoveHelper(std::move(src)); - return *this; -} - -void LocalizedNumberFormatter::resetCompiled() { - auto* callCount = reinterpret_cast(fUnsafeCallCount); - umtx_storeRelease(*callCount, 0); - fCompiled = nullptr; -} - -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. - delete fCompiled; - if (src.fCompiled != nullptr) { - auto* callCount = reinterpret_cast(fUnsafeCallCount); - umtx_storeRelease(*callCount, INT32_MIN); - fCompiled = src.fCompiled; - // Reset the source object to leave it in a safe state. - src.resetCompiled(); - } else { - resetCompiled(); - } - - // Unconditionally move the warehouse - delete fWarehouse; - fWarehouse = src.fWarehouse; - src.fWarehouse = nullptr; -} - -void LocalizedNumberFormatter::lnfCopyHelper(const LNF&, UErrorCode& status) { - // When copying, always reset the compiled formatter. - delete fCompiled; - resetCompiled(); - - // If MacroProps has a reference to AffixPatternProvider, we need to copy it. - // If MacroProps has a reference to PluralRules, copy that one, too. - delete fWarehouse; - if (fMacros.affixProvider || fMacros.rules) { - LocalPointer warehouse(new DecimalFormatWarehouse(), status); - if (U_FAILURE(status)) { - fWarehouse = nullptr; - return; - } - if (fMacros.affixProvider) { - warehouse->affixProvider.setTo(fMacros.affixProvider, status); - fMacros.affixProvider = &warehouse->affixProvider.get(); - } - if (fMacros.rules) { - warehouse->rules.adoptInsteadAndCheckErrorCode( - new PluralRules(*fMacros.rules), status); - fMacros.rules = warehouse->rules.getAlias(); - } - fWarehouse = warehouse.orphan(); - } else { - fWarehouse = nullptr; - } -} - - -LocalizedNumberFormatter::~LocalizedNumberFormatter() { - delete fCompiled; - delete fWarehouse; -} - -LocalizedNumberFormatter::LocalizedNumberFormatter(const MacroProps& macros, const Locale& locale) { - fMacros = macros; - fMacros.locale = locale; -} - -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); -} - -LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale& locale)&& { - return LocalizedNumberFormatter(std::move(fMacros), locale); -} - -FormattedNumber LocalizedNumberFormatter::formatInt(int64_t value, 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.setToLong(value); - 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 { - 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.setToDouble(value); - 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 { - 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.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::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->format(results, status); - } else { - NumberFormatterImpl::formatStatic(fMacros, results, status); - } - if (U_FAILURE(status)) { - return; - } - results->getStringRef().writeTerminator(status); -} - -void LocalizedNumberFormatter::getAffixImpl(bool isPrefix, bool isNegative, UnicodeString& result, - UErrorCode& status) const { - FormattedStringBuilder string; - auto signum = static_cast(isNegative ? SIGNUM_NEG : SIGNUM_POS); - // 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"); - 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 - // is exactly threshold, then it is the current thread's job to build the data structure. - // Note: We set the callCount to INT32_MIN so that if another thread proceeds to increment - // the atomic int, the value remains below zero. - int32_t currentCount = umtx_loadAcquire(*callCount); - if (0 <= currentCount && currentCount <= fMacros.threshold && fMacros.threshold > 0) { - currentCount = umtx_atomic_inc(callCount); - } - - if (currentCount == fMacros.threshold && fMacros.threshold > 0) { - // Build the data structure and then use it (slow to fast path). - const NumberFormatterImpl* compiled = new NumberFormatterImpl(fMacros, status); - if (compiled == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return false; - } - U_ASSERT(fCompiled == nullptr); - const_cast(this)->fCompiled = compiled; - umtx_storeRelease(*callCount, INT32_MIN); - return true; - } else if (currentCount < 0) { - // The data structure is already built; use it (fast path). - U_ASSERT(fCompiled != nullptr); - return true; - } else { - // Format the number without building the data structure (slow path). - return false; - } -} - -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); -} - -// Note: toFormat defined in number_asformat.cpp - -const DecimalFormatSymbols* LocalizedNumberFormatter::getDecimalFormatSymbols() const { - return fMacros.symbols.getDecimalFormatSymbols(); -} - -#if (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN) && defined(_MSC_VER) -// Warning 4661. -#pragma warning(pop) -#endif - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include "uassert.h" +#include "unicode/numberformatter.h" +#include "number_decimalquantity.h" +#include "number_formatimpl.h" +#include "umutex.h" +#include "number_asformat.h" +#include "number_utils.h" +#include "number_utypes.h" +#include "number_mapper.h" +#include "util.h" +#include "fphdlimp.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +#if (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN) && defined(_MSC_VER) +// Ignore MSVC warning 4661. This is generated for NumberFormatterSettings<>::toSkeleton() as this method +// is defined elsewhere (in number_skeletons.cpp). The compiler is warning that the explicit template instantiation +// inside this single translation unit (CPP file) is incomplete, and thus it isn't sure if the template class is +// fully defined. However, since each translation unit explicitly instantiates all the necessary template classes, +// they will all be passed to the linker, and the linker will still find and export all the class members. +#pragma warning(push) +#pragma warning(disable: 4661) +#endif + +template +Derived NumberFormatterSettings::notation(const Notation& notation) const& { + Derived copy(*this); + // NOTE: Slicing is OK. + copy.fMacros.notation = notation; + return copy; +} + +template +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. + copy.fMacros.unit = unit; + return copy; +} + +template +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 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 = std::move(*unit); + delete unit; + } + return copy; +} + +template +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; + return copy; +} + +template +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 = std::move(*perUnit); + delete perUnit; + } + return copy; +} + +template +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.precision = precision; + return copy; +} + +template +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(UNumberGroupingStrategy 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. + copy.fMacros.grouper = Grouper::forStrategy(strategy); + return copy; +} + +template +Derived NumberFormatterSettings::grouping(UNumberGroupingStrategy 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::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::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::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::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::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::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::usage(const StringPiece usage) const& { + Derived copy(*this); + copy.fMacros.usage.set(usage); + return copy; +} + +template +Derived NumberFormatterSettings::usage(const StringPiece usage)&& { + Derived move(std::move(*this)); + move.fMacros.usage.set(usage); + return move; +} + +template +Derived NumberFormatterSettings::displayOptions(const DisplayOptions &displayOptions) const & { + Derived copy(*this); + // `displayCase` does not recognise the `undefined` + if (displayOptions.getGrammaticalCase() == UDISPOPT_GRAMMATICAL_CASE_UNDEFINED) { + copy.fMacros.unitDisplayCase.set(nullptr); + return copy; + } + + copy.fMacros.unitDisplayCase.set( + udispopt_getGrammaticalCaseIdentifier(displayOptions.getGrammaticalCase())); + return copy; +} + +template +Derived NumberFormatterSettings::displayOptions(const DisplayOptions &displayOptions) && { + Derived move(std::move(*this)); + // `displayCase` does not recognise the `undefined` + if (displayOptions.getGrammaticalCase() == UDISPOPT_GRAMMATICAL_CASE_UNDEFINED) { + move.fMacros.unitDisplayCase.set(nullptr); + return move; + } + + move.fMacros.unitDisplayCase.set( + udispopt_getGrammaticalCaseIdentifier(displayOptions.getGrammaticalCase())); + return move; +} + +template +Derived NumberFormatterSettings::unitDisplayCase(const StringPiece unitDisplayCase) const& { + Derived copy(*this); + copy.fMacros.unitDisplayCase.set(unitDisplayCase); + return copy; +} + +template +Derived NumberFormatterSettings::unitDisplayCase(const StringPiece unitDisplayCase)&& { + Derived move(std::move(*this)); + move.fMacros.unitDisplayCase.set(unitDisplayCase); + return move; +} + +template +Derived NumberFormatterSettings::padding(const Padder& padder) const& { + Derived copy(*this); + copy.fMacros.padder = padder; + return copy; +} + +template +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; +} + +// Note: toSkeleton defined in number_skeletons.cpp + +template +LocalPointer NumberFormatterSettings::clone() const & { + return LocalPointer(new Derived(*this)); +} + +template +LocalPointer NumberFormatterSettings::clone() && { + return LocalPointer(new Derived(std::move(*this))); +} + +// Declare all classes that implement NumberFormatterSettings +// See https://stackoverflow.com/a/495056/1407170 +template +class icu::number::NumberFormatterSettings; +template +class icu::number::NumberFormatterSettings; + + +UnlocalizedNumberFormatter NumberFormatter::with() { + UnlocalizedNumberFormatter result; + return result; +} + +LocalizedNumberFormatter NumberFormatter::withLocale(const Locale& locale) { + return with().locale(locale); +} + +// Note: forSkeleton defined in number_skeletons.cpp + + +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 +} + +// Make default copy constructor call the NumberFormatterSettings copy constructor. +UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(UNF&& src) noexcept + : UNF(static_cast&&>(src)) {} + +UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(NFS&& src) 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) noexcept { + NFS::operator=(static_cast&&>(src)); + // No additional fields to assign + return *this; +} + +// Make default copy constructor call the NumberFormatterSettings copy constructor. +LocalizedNumberFormatter::LocalizedNumberFormatter(const LNF& other) + : LNF(static_cast&>(other)) {} + +LocalizedNumberFormatter::LocalizedNumberFormatter(const NFS& other) + : NFS(other) { + UErrorCode localStatus = U_ZERO_ERROR; // Can't bubble up the error + lnfCopyHelper(static_cast(other), localStatus); +} + +LocalizedNumberFormatter::LocalizedNumberFormatter(LocalizedNumberFormatter&& src) noexcept + : LNF(static_cast&&>(src)) {} + +LocalizedNumberFormatter::LocalizedNumberFormatter(NFS&& src) noexcept + : NFS(std::move(src)) { + lnfMoveHelper(std::move(static_cast(src))); +} + +LocalizedNumberFormatter& LocalizedNumberFormatter::operator=(const LNF& other) { + if (this == &other) { return *this; } // self-assignment: no-op + NFS::operator=(static_cast&>(other)); + UErrorCode localStatus = U_ZERO_ERROR; // Can't bubble up the error + lnfCopyHelper(other, localStatus); + return *this; +} + +LocalizedNumberFormatter& LocalizedNumberFormatter::operator=(LNF&& src) noexcept { + NFS::operator=(static_cast&&>(src)); + lnfMoveHelper(std::move(src)); + return *this; +} + +void LocalizedNumberFormatter::resetCompiled() { + auto* callCount = reinterpret_cast(fUnsafeCallCount); + umtx_storeRelease(*callCount, 0); + fCompiled = nullptr; +} + +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. + delete fCompiled; + if (src.fCompiled != nullptr) { + auto* callCount = reinterpret_cast(fUnsafeCallCount); + umtx_storeRelease(*callCount, INT32_MIN); + fCompiled = src.fCompiled; + // Reset the source object to leave it in a safe state. + src.resetCompiled(); + } else { + resetCompiled(); + } + + // Unconditionally move the warehouse + delete fWarehouse; + fWarehouse = src.fWarehouse; + src.fWarehouse = nullptr; +} + +void LocalizedNumberFormatter::lnfCopyHelper(const LNF&, UErrorCode& status) { + // When copying, always reset the compiled formatter. + delete fCompiled; + resetCompiled(); + + // If MacroProps has a reference to AffixPatternProvider, we need to copy it. + // If MacroProps has a reference to PluralRules, copy that one, too. + delete fWarehouse; + if (fMacros.affixProvider || fMacros.rules) { + LocalPointer warehouse(new DecimalFormatWarehouse(), status); + if (U_FAILURE(status)) { + fWarehouse = nullptr; + return; + } + if (fMacros.affixProvider) { + warehouse->affixProvider.setTo(fMacros.affixProvider, status); + fMacros.affixProvider = &warehouse->affixProvider.get(); + } + if (fMacros.rules) { + warehouse->rules.adoptInsteadAndCheckErrorCode( + new PluralRules(*fMacros.rules), status); + fMacros.rules = warehouse->rules.getAlias(); + } + fWarehouse = warehouse.orphan(); + } else { + fWarehouse = nullptr; + } +} + + +LocalizedNumberFormatter::~LocalizedNumberFormatter() { + delete fCompiled; + delete fWarehouse; +} + +LocalizedNumberFormatter::LocalizedNumberFormatter(const MacroProps& macros, const Locale& locale) { + fMacros = macros; + fMacros.locale = locale; +} + +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); +} + +LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale& locale)&& { + return LocalizedNumberFormatter(std::move(fMacros), locale); +} + +FormattedNumber LocalizedNumberFormatter::formatInt(int64_t value, 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.setToLong(value); + 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 { + 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.setToDouble(value); + 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 { + 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.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::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->format(results, status); + } else { + NumberFormatterImpl::formatStatic(fMacros, results, status); + } + if (U_FAILURE(status)) { + return; + } + results->getStringRef().writeTerminator(status); +} + +void LocalizedNumberFormatter::getAffixImpl(bool isPrefix, bool isNegative, UnicodeString& result, + UErrorCode& status) const { + FormattedStringBuilder string; + auto signum = static_cast(isNegative ? SIGNUM_NEG : SIGNUM_POS); + // 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"); + 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 + // is exactly threshold, then it is the current thread's job to build the data structure. + // Note: We set the callCount to INT32_MIN so that if another thread proceeds to increment + // the atomic int, the value remains below zero. + int32_t currentCount = umtx_loadAcquire(*callCount); + if (0 <= currentCount && currentCount <= fMacros.threshold && fMacros.threshold > 0) { + currentCount = umtx_atomic_inc(callCount); + } + + if (currentCount == fMacros.threshold && fMacros.threshold > 0) { + // Build the data structure and then use it (slow to fast path). + const NumberFormatterImpl* compiled = new NumberFormatterImpl(fMacros, status); + if (compiled == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return false; + } + U_ASSERT(fCompiled == nullptr); + const_cast(this)->fCompiled = compiled; + umtx_storeRelease(*callCount, INT32_MIN); + return true; + } else if (currentCount < 0) { + // The data structure is already built; use it (fast path). + U_ASSERT(fCompiled != nullptr); + return true; + } else { + // Format the number without building the data structure (slow path). + return false; + } +} + +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); +} + +// Note: toFormat defined in number_asformat.cpp + +const DecimalFormatSymbols* LocalizedNumberFormatter::getDecimalFormatSymbols() const { + return fMacros.symbols.getDecimalFormatSymbols(); +} + +#if (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN) && defined(_MSC_VER) +// Warning 4661. +#pragma warning(pop) +#endif + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_formatimpl.cpp b/deps/icu-small/source/i18n/number_formatimpl.cpp index 4fb190b744b7c1..fcf3ef0afadfa7 100644 --- a/deps/icu-small/source/i18n/number_formatimpl.cpp +++ b/deps/icu-small/source/i18n/number_formatimpl.cpp @@ -1,627 +1,647 @@ -// © 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 - -#include "cstring.h" -#include "unicode/ures.h" -#include "uresimp.h" -#include "charstr.h" -#include "number_formatimpl.h" -#include "unicode/numfmt.h" -#include "number_patternstring.h" -#include "number_utils.h" -#include "unicode/numberformatter.h" -#include "unicode/dcfmtsym.h" -#include "number_scientific.h" -#include "number_compact.h" -#include "uresimp.h" -#include "ureslocs.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - - -NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& status) - : NumberFormatterImpl(macros, true, status) { -} - -int32_t NumberFormatterImpl::formatStatic(const MacroProps ¯os, UFormattedNumberData *results, - UErrorCode &status) { - DecimalQuantity &inValue = results->quantity; - FormattedStringBuilder &outString = results->getStringRef(); - NumberFormatterImpl impl(macros, false, status); - MicroProps& micros = impl.preProcessUnsafe(inValue, status); - if (U_FAILURE(status)) { return 0; } - int32_t length = writeNumber(micros, inValue, outString, 0, status); - length += writeAffixes(micros, outString, 0, length, status); - results->outputUnit = std::move(micros.outputUnit); - results->gender = micros.gender; - return length; -} - -int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, Signum signum, - StandardPlural::Form plural, - FormattedStringBuilder& 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. - -int32_t NumberFormatterImpl::format(UFormattedNumberData *results, UErrorCode &status) const { - DecimalQuantity &inValue = results->quantity; - FormattedStringBuilder &outString = results->getStringRef(); - MicroProps micros; - preProcess(inValue, micros, status); - if (U_FAILURE(status)) { return 0; } - int32_t length = writeNumber(micros, inValue, outString, 0, status); - length += writeAffixes(micros, outString, 0, length, status); - results->outputUnit = std::move(micros.outputUnit); - results->gender = micros.gender; - return length; -} - -void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut, - UErrorCode& status) const { - if (U_FAILURE(status)) { return; } - if (fMicroPropsGenerator == nullptr) { - status = U_INTERNAL_PROGRAM_ERROR; - return; - } - fMicroPropsGenerator->processQuantity(inValue, microsOut, status); - microsOut.integerWidth.apply(inValue, status); -} - -MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErrorCode& status) { - if (U_FAILURE(status)) { - return fMicros; // must always return a value - } - if (fMicroPropsGenerator == nullptr) { - status = U_INTERNAL_PROGRAM_ERROR; - return fMicros; // must always return a value - } - fMicroPropsGenerator->processQuantity(inValue, fMicros, status); - fMicros.integerWidth.apply(inValue, status); - return fMicros; -} - -int32_t NumberFormatterImpl::getPrefixSuffix(Signum signum, StandardPlural::Form plural, - FormattedStringBuilder& 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(); -} - -int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(Signum signum, StandardPlural::Form plural, - FormattedStringBuilder& 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(); -} - -NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) { - fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status); -} - -////////// - -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)) { - return nullptr; - } - - // TODO: Accept currency symbols from DecimalFormatSymbols? - - // Pre-compute a few values for efficiency. - bool isCurrency = utils::unitIsCurrency(macros.unit); - bool isBaseUnit = utils::unitIsBaseUnit(macros.unit); - bool isPercent = utils::unitIsPercent(macros.unit); - bool isPermille = utils::unitIsPermille(macros.unit); - bool isCompactNotation = macros.notation.fType == Notation::NTN_COMPACT; - bool isAccounting = - macros.sign == UNUM_SIGN_ACCOUNTING || - macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS || - macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO || - macros.sign == UNUM_SIGN_ACCOUNTING_NEGATIVE; - CurrencyUnit currency(u"", status); - if (isCurrency) { - currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit - } - UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT; - if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) { - unitWidth = macros.unitWidth; - } - // Use CLDR unit data for all MeasureUnits (not currency and not - // no-unit), except use the dedicated percent pattern for percent and - // permille. However, use the CLDR unit data for percent/permille if a - // long name was requested OR if compact notation is being used, since - // compact notation overrides the middle modifier (micros.modMiddle) - // normally used for the percent pattern. - bool isCldrUnit = !isCurrency - && !isBaseUnit - && (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME - || !(isPercent || isPermille) - || isCompactNotation - ); - bool isMixedUnit = isCldrUnit && (uprv_strcmp(macros.unit.getType(), "") == 0) && - macros.unit.getComplexity(status) == UMEASURE_UNIT_MIXED; - - // Select the numbering system. - LocalPointer nsLocal; - const NumberingSystem* ns; - if (macros.symbols.isNumberingSystem()) { - ns = macros.symbols.getNumberingSystem(); - } else { - // TODO: Is there a way to avoid creating the NumberingSystem object? - ns = NumberingSystem::createInstance(macros.locale, status); - // Give ownership to the function scope. - nsLocal.adoptInstead(ns); - } - const char* nsName = U_SUCCESS(status) ? ns->getName() : "latn"; - uprv_strncpy(fMicros.nsName, nsName, 8); - fMicros.nsName[8] = 0; // guarantee NUL-terminated - - // Default gender: none. - fMicros.gender = ""; - - // Resolve the symbols. Do this here because currency may need to customize them. - if (macros.symbols.isDecimalFormatSymbols()) { - fMicros.symbols = macros.symbols.getDecimalFormatSymbols(); - } else { - LocalPointer newSymbols( - new DecimalFormatSymbols(macros.locale, *ns, status), status); - if (U_FAILURE(status)) { - return nullptr; - } - if (isCurrency) { - newSymbols->setCurrency(currency.getISOCurrency(), status); - if (U_FAILURE(status)) { - return nullptr; - } - } - fMicros.symbols = newSymbols.getAlias(); - fSymbols.adoptInstead(newSymbols.orphan()); - } - - // Load and parse the pattern string. It is used for grouping sizes and affixes only. - // If we are formatting currency, check for a currency-specific pattern. - const char16_t* pattern = nullptr; - if (isCurrency && fMicros.symbols->getCurrencyPattern() != nullptr) { - pattern = fMicros.symbols->getCurrencyPattern(); - } - if (pattern == nullptr) { - CldrPatternStyle patternStyle; - if (isCldrUnit) { - patternStyle = CLDR_PATTERN_STYLE_DECIMAL; - } else if (isPercent || isPermille) { - patternStyle = CLDR_PATTERN_STYLE_PERCENT; - } else if (!isCurrency || unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { - patternStyle = CLDR_PATTERN_STYLE_DECIMAL; - } else if (isAccounting) { - // NOTE: Although ACCOUNTING and ACCOUNTING_ALWAYS are only supported in currencies right now, - // the API contract allows us to add support to other units in the future. - patternStyle = CLDR_PATTERN_STYLE_ACCOUNTING; - } else { - patternStyle = CLDR_PATTERN_STYLE_CURRENCY; - } - pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status); - if (U_FAILURE(status)) { - return nullptr; - } - } - auto patternInfo = new ParsedPatternInfo(); - if (patternInfo == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - fPatternInfo.adoptInstead(patternInfo); - PatternParser::parseToPatternInfo(UnicodeString(pattern), *patternInfo, status); - if (U_FAILURE(status)) { - return nullptr; - } - - ///////////////////////////////////////////////////////////////////////////////////// - /// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR /// - ///////////////////////////////////////////////////////////////////////////////////// - - // Unit Preferences and Conversions as our first step - if (macros.usage.isSet()) { - if (!isCldrUnit) { - // We only support "usage" when the input unit is specified, and is - // a CLDR Unit. - status = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - auto usagePrefsHandler = - new UsagePrefsHandler(macros.locale, macros.unit, macros.usage.fValue, chain, status); - fUsagePrefsHandler.adoptInsteadAndCheckErrorCode(usagePrefsHandler, status); - chain = fUsagePrefsHandler.getAlias(); - } else if (isMixedUnit) { - auto unitConversionHandler = new UnitConversionHandler(macros.unit, chain, status); - fUnitConversionHandler.adoptInsteadAndCheckErrorCode(unitConversionHandler, status); - chain = fUnitConversionHandler.getAlias(); - } - - // Multiplier - if (macros.scale.isValid()) { - fMicros.helpers.multiplier.setAndChain(macros.scale, chain); - chain = &fMicros.helpers.multiplier; - } - - // Rounding strategy - Precision precision; - if (!macros.precision.isBogus()) { - precision = macros.precision; - } else if (isCompactNotation) { - precision = Precision::integer().withMinDigits(2); - } else if (isCurrency) { - precision = Precision::currency(UCURR_USAGE_STANDARD); - } else if (macros.usage.isSet()) { - // Bogus Precision - it will get set in the UsagePrefsHandler instead - precision = Precision(); - } else { - precision = Precision::maxFraction(6); - } - UNumberFormatRoundingMode roundingMode; - roundingMode = macros.roundingMode; - fMicros.rounder = {precision, roundingMode, currency, status}; - if (U_FAILURE(status)) { - return nullptr; - } - - // Grouping strategy - if (!macros.grouper.isBogus()) { - fMicros.grouping = macros.grouper; - } else if (isCompactNotation) { - // Compact notation uses minGrouping by default since ICU 59 - fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2); - } else { - fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO); - } - fMicros.grouping.setLocaleData(*fPatternInfo, macros.locale); - - // Padding strategy - if (!macros.padder.isBogus()) { - fMicros.padding = macros.padder; - } else { - fMicros.padding = Padder::none(); - } - - // Integer width - if (!macros.integerWidth.isBogus()) { - fMicros.integerWidth = macros.integerWidth; - } else { - fMicros.integerWidth = IntegerWidth::standard(); - } - - // Sign display - if (macros.sign != UNUM_SIGN_COUNT) { - fMicros.sign = macros.sign; - } else { - fMicros.sign = UNUM_SIGN_AUTO; - } - - // Decimal mark display - if (macros.decimal != UNUM_DECIMAL_SEPARATOR_COUNT) { - fMicros.decimal = macros.decimal; - } else { - fMicros.decimal = UNUM_DECIMAL_SEPARATOR_AUTO; - } - - // Use monetary separator symbols - fMicros.useCurrency = isCurrency; - - // Inner modifier (scientific notation) - if (macros.notation.fType == Notation::NTN_SCIENTIFIC) { - auto newScientificHandler = new ScientificHandler(¯os.notation, fMicros.symbols, chain); - if (newScientificHandler == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - fScientificHandler.adoptInstead(newScientificHandler); - chain = fScientificHandler.getAlias(); - } else { - // No inner modifier required - fMicros.modInner = &fMicros.helpers.emptyStrongModifier; - } - - // Middle modifier (patterns, positive/negative, currency symbols, percent) - auto patternModifier = new MutablePatternModifier(false); - if (patternModifier == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - fPatternModifier.adoptInstead(patternModifier); - const AffixPatternProvider* affixProvider = - macros.affixProvider != nullptr && ( - // For more information on this condition, see ICU-22073 - !isCompactNotation || isCurrency == macros.affixProvider->hasCurrencySign()) - ? macros.affixProvider - : static_cast(fPatternInfo.getAlias()); - patternModifier->setPatternInfo(affixProvider, kUndefinedField); - patternModifier->setPatternAttributes(fMicros.sign, isPermille, macros.approximately); - if (patternModifier->needsPlurals()) { - patternModifier->setSymbols( - fMicros.symbols, - currency, - unitWidth, - resolvePluralRules(macros.rules, macros.locale, status), - status); - } else { - patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr, status); - } - if (safe) { - fImmutablePatternModifier.adoptInsteadAndCheckErrorCode(patternModifier->createImmutable(status), - status); - } - if (U_FAILURE(status)) { - return nullptr; - } - - // currencyAsDecimal - if (affixProvider->currencyAsDecimal()) { - fMicros.currencyAsDecimal = patternModifier->getCurrencySymbolForUnitWidth(status); - } - - // Outer modifier (CLDR units and currency long names) - if (isCldrUnit) { - const char *unitDisplayCase = ""; - if (macros.unitDisplayCase.isSet()) { - unitDisplayCase = macros.unitDisplayCase.fValue; - } - if (macros.usage.isSet()) { - fLongNameMultiplexer.adoptInsteadAndCheckErrorCode( - LongNameMultiplexer::forMeasureUnits( - macros.locale, *fUsagePrefsHandler->getOutputUnits(), unitWidth, unitDisplayCase, - resolvePluralRules(macros.rules, macros.locale, status), chain, status), - status); - chain = fLongNameMultiplexer.getAlias(); - } else if (isMixedUnit) { - fMixedUnitLongNameHandler.adoptInsteadAndCheckErrorCode(new MixedUnitLongNameHandler(), - status); - MixedUnitLongNameHandler::forMeasureUnit( - macros.locale, macros.unit, unitWidth, unitDisplayCase, - resolvePluralRules(macros.rules, macros.locale, status), chain, - fMixedUnitLongNameHandler.getAlias(), status); - chain = fMixedUnitLongNameHandler.getAlias(); - } else { - MeasureUnit unit = macros.unit; - if (!utils::unitIsBaseUnit(macros.perUnit)) { - unit = unit.product(macros.perUnit.reciprocal(status), status); - // This isn't strictly necessary, but was what we specced out - // when perUnit became a backward-compatibility thing: - // unit/perUnit use case is only valid if both units are - // built-ins, or the product is a built-in. - if (uprv_strcmp(unit.getType(), "") == 0 && - (uprv_strcmp(macros.unit.getType(), "") == 0 || - uprv_strcmp(macros.perUnit.getType(), "") == 0)) { - status = U_UNSUPPORTED_ERROR; - return nullptr; - } - } - fLongNameHandler.adoptInsteadAndCheckErrorCode(new LongNameHandler(), status); - LongNameHandler::forMeasureUnit(macros.locale, unit, unitWidth, unitDisplayCase, - resolvePluralRules(macros.rules, macros.locale, status), - chain, fLongNameHandler.getAlias(), status); - chain = fLongNameHandler.getAlias(); - } - } else if (isCurrency && unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { - fLongNameHandler.adoptInsteadAndCheckErrorCode( - LongNameHandler::forCurrencyLongNames( - macros.locale, currency, resolvePluralRules(macros.rules, macros.locale, status), chain, - status), - status); - chain = fLongNameHandler.getAlias(); - } else { - // No outer modifier required - fMicros.modOuter = &fMicros.helpers.emptyWeakModifier; - } - if (U_FAILURE(status)) { - return nullptr; - } - - // Compact notation - if (isCompactNotation) { - CompactType compactType = (isCurrency && unitWidth != UNUM_UNIT_WIDTH_FULL_NAME) - ? CompactType::TYPE_CURRENCY : CompactType::TYPE_DECIMAL; - auto newCompactHandler = new CompactHandler( - macros.notation.fUnion.compactStyle, - macros.locale, - nsName, - compactType, - resolvePluralRules(macros.rules, macros.locale, status), - patternModifier, - safe, - chain, - status); - if (U_FAILURE(status)) { - return nullptr; - } - if (newCompactHandler == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - fCompactHandler.adoptInstead(newCompactHandler); - chain = fCompactHandler.getAlias(); - } - if (U_FAILURE(status)) { - return nullptr; - } - - // Always add the pattern modifier as the last element of the chain. - if (safe) { - fImmutablePatternModifier->addToChain(chain); - chain = fImmutablePatternModifier.getAlias(); - } else { - patternModifier->addToChain(chain); - chain = patternModifier; - } - - return chain; -} - -const PluralRules* -NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale, - UErrorCode& status) { - if (rulesPtr != nullptr) { - return rulesPtr; - } - // Lazily create PluralRules - if (fRules.isNull()) { - fRules.adoptInstead(PluralRules::forLocale(locale, status)); - } - return fRules.getAlias(); -} - -int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, FormattedStringBuilder& string, - int32_t start, int32_t end, UErrorCode& status) { - U_ASSERT(micros.modOuter != nullptr); - // Always apply the inner modifier (which is "strong"). - int32_t length = micros.modInner->apply(string, start, end, status); - if (micros.padding.isValid()) { - length += micros.padding - .padAndApply(*micros.modMiddle, *micros.modOuter, string, start, length + end, status); - } else { - length += micros.modMiddle->apply(string, start, length + end, status); - length += micros.modOuter->apply(string, start, length + end, status); - } - return length; -} - -int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity, - FormattedStringBuilder& string, int32_t index, - UErrorCode& status) { - int32_t length = 0; - if (quantity.isInfinite()) { - length += string.insert( - length + index, - micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kInfinitySymbol), - {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD}, - status); - - } else if (quantity.isNaN()) { - length += string.insert( - length + index, - micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kNaNSymbol), - {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD}, - status); - - } else { - // Add the integer digits - length += writeIntegerDigits(micros, quantity, string, length + index, status); - - // Add the decimal point - if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) { - if (!micros.currencyAsDecimal.isBogus()) { - length += string.insert( - length + index, - micros.currencyAsDecimal, - {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}, - status); - } else if (micros.useCurrency) { - length += string.insert( - length + index, - micros.symbols->getSymbol( - DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol), - {UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD}, - status); - } else { - length += string.insert( - length + index, - micros.symbols->getSymbol( - DecimalFormatSymbols::ENumberFormatSymbol::kDecimalSeparatorSymbol), - {UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD}, - status); - } - } - - // Add the fraction digits - length += writeFractionDigits(micros, quantity, string, length + index, status); - - if (length == 0) { - // Force output of the digit for value 0 - length += utils::insertDigitFromSymbols( - string, - index, - 0, - *micros.symbols, - {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD}, - status); - } - } - - return length; -} - -int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity, - FormattedStringBuilder& string, int32_t index, - UErrorCode& status) { - int length = 0; - int integerCount = quantity.getUpperDisplayMagnitude() + 1; - for (int i = 0; i < integerCount; i++) { - // Add grouping separator - if (micros.grouping.groupAtPosition(i, quantity)) { - length += string.insert( - index, - micros.useCurrency ? micros.symbols->getSymbol( - DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol) - : micros.symbols->getSymbol( - DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol), - {UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD}, - status); - } - - // Get and append the next digit value - int8_t nextDigit = quantity.getDigit(i); - length += utils::insertDigitFromSymbols( - string, - index, - nextDigit, - *micros.symbols, - {UFIELD_CATEGORY_NUMBER, - UNUM_INTEGER_FIELD}, - status); - } - return length; -} - -int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity, - FormattedStringBuilder& string, int32_t index, - 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 += utils::insertDigitFromSymbols( - string, - length + index, - nextDigit, - *micros.symbols, - {UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD}, - status); - } - return length; -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include "cstring.h" +#include "unicode/ures.h" +#include "uresimp.h" +#include "charstr.h" +#include "number_formatimpl.h" +#include "unicode/numfmt.h" +#include "number_patternstring.h" +#include "number_utils.h" +#include "unicode/numberformatter.h" +#include "unicode/dcfmtsym.h" +#include "number_scientific.h" +#include "number_compact.h" +#include "uresimp.h" +#include "ureslocs.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& status) + : NumberFormatterImpl(macros, true, status) { +} + +int32_t NumberFormatterImpl::formatStatic(const MacroProps ¯os, UFormattedNumberData *results, + UErrorCode &status) { + DecimalQuantity &inValue = results->quantity; + FormattedStringBuilder &outString = results->getStringRef(); + NumberFormatterImpl impl(macros, false, status); + MicroProps& micros = impl.preProcessUnsafe(inValue, status); + if (U_FAILURE(status)) { return 0; } + int32_t length = writeNumber(micros.simple, inValue, outString, 0, status); + length += writeAffixes(micros, outString, 0, length, status); + results->outputUnit = std::move(micros.outputUnit); + results->gender = micros.gender; + return length; +} + +int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, Signum signum, + StandardPlural::Form plural, + FormattedStringBuilder& 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. + +int32_t NumberFormatterImpl::format(UFormattedNumberData *results, UErrorCode &status) const { + DecimalQuantity &inValue = results->quantity; + FormattedStringBuilder &outString = results->getStringRef(); + MicroProps micros; + preProcess(inValue, micros, status); + if (U_FAILURE(status)) { return 0; } + int32_t length = writeNumber(micros.simple, inValue, outString, 0, status); + length += writeAffixes(micros, outString, 0, length, status); + results->outputUnit = std::move(micros.outputUnit); + results->gender = micros.gender; + return length; +} + +void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut, + UErrorCode& status) const { + if (U_FAILURE(status)) { return; } + if (fMicroPropsGenerator == nullptr) { + status = U_INTERNAL_PROGRAM_ERROR; + return; + } + fMicroPropsGenerator->processQuantity(inValue, microsOut, status); + microsOut.integerWidth.apply(inValue, status); +} + +MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErrorCode& status) { + if (U_FAILURE(status)) { + return fMicros; // must always return a value + } + if (fMicroPropsGenerator == nullptr) { + status = U_INTERNAL_PROGRAM_ERROR; + return fMicros; // must always return a value + } + fMicroPropsGenerator->processQuantity(inValue, fMicros, status); + fMicros.integerWidth.apply(inValue, status); + return fMicros; +} + +int32_t NumberFormatterImpl::getPrefixSuffix(Signum signum, StandardPlural::Form plural, + FormattedStringBuilder& 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(); +} + +int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(Signum signum, StandardPlural::Form plural, + FormattedStringBuilder& 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(); +} + +NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) { + fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status); +} + +////////// + +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)) { + return nullptr; + } + + // TODO: Accept currency symbols from DecimalFormatSymbols? + + // Pre-compute a few values for efficiency. + bool isCurrency = utils::unitIsCurrency(macros.unit); + bool isBaseUnit = utils::unitIsBaseUnit(macros.unit); + bool isPercent = utils::unitIsPercent(macros.unit); + bool isPermille = utils::unitIsPermille(macros.unit); + bool isCompactNotation = macros.notation.fType == Notation::NTN_COMPACT; + bool isAccounting = + macros.sign == UNUM_SIGN_ACCOUNTING || + macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS || + macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO || + macros.sign == UNUM_SIGN_ACCOUNTING_NEGATIVE; + CurrencyUnit currency(u"", status); + if (isCurrency) { + currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit + } + UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT; + if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) { + unitWidth = macros.unitWidth; + } + // Use CLDR unit data for all MeasureUnits (not currency and not + // no-unit), except use the dedicated percent pattern for percent and + // permille. However, use the CLDR unit data for percent/permille if a + // long name was requested OR if compact notation is being used, since + // compact notation overrides the middle modifier (micros.modMiddle) + // normally used for the percent pattern. + bool isCldrUnit = !isCurrency + && !isBaseUnit + && (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME + || !(isPercent || isPermille) + || isCompactNotation + ); + bool isMixedUnit = isCldrUnit && (uprv_strcmp(macros.unit.getType(), "") == 0) && + macros.unit.getComplexity(status) == UMEASURE_UNIT_MIXED; + + // Select the numbering system. + LocalPointer nsLocal; + const NumberingSystem* ns; + if (macros.symbols.isNumberingSystem()) { + ns = macros.symbols.getNumberingSystem(); + } else { + // TODO: Is there a way to avoid creating the NumberingSystem object? + ns = NumberingSystem::createInstance(macros.locale, status); + // Give ownership to the function scope. + nsLocal.adoptInstead(ns); + } + const char* nsName = U_SUCCESS(status) ? ns->getName() : "latn"; + uprv_strncpy(fMicros.nsName, nsName, 8); + fMicros.nsName[8] = 0; // guarantee NUL-terminated + + // Default gender: none. + fMicros.gender = ""; + + // Resolve the symbols. Do this here because currency may need to customize them. + if (macros.symbols.isDecimalFormatSymbols()) { + fMicros.simple.symbols = macros.symbols.getDecimalFormatSymbols(); + } else { + LocalPointer newSymbols( + new DecimalFormatSymbols(macros.locale, *ns, status), status); + if (U_FAILURE(status)) { + return nullptr; + } + if (isCurrency) { + newSymbols->setCurrency(currency.getISOCurrency(), status); + if (U_FAILURE(status)) { + return nullptr; + } + } + fMicros.simple.symbols = newSymbols.getAlias(); + fSymbols.adoptInstead(newSymbols.orphan()); + } + + // Load and parse the pattern string. It is used for grouping sizes and affixes only. + // If we are formatting currency, check for a currency-specific pattern. + const char16_t* pattern = nullptr; + if (isCurrency && fMicros.simple.symbols->getCurrencyPattern() != nullptr) { + pattern = fMicros.simple.symbols->getCurrencyPattern(); + } + if (pattern == nullptr) { + CldrPatternStyle patternStyle; + if (isCldrUnit) { + patternStyle = CLDR_PATTERN_STYLE_DECIMAL; + } else if (isPercent || isPermille) { + patternStyle = CLDR_PATTERN_STYLE_PERCENT; + } else if (!isCurrency || unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { + patternStyle = CLDR_PATTERN_STYLE_DECIMAL; + } else if (isAccounting) { + // NOTE: Although ACCOUNTING and ACCOUNTING_ALWAYS are only supported in currencies right now, + // the API contract allows us to add support to other units in the future. + patternStyle = CLDR_PATTERN_STYLE_ACCOUNTING; + } else { + patternStyle = CLDR_PATTERN_STYLE_CURRENCY; + } + pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status); + if (U_FAILURE(status)) { + return nullptr; + } + } + auto patternInfo = new ParsedPatternInfo(); + if (patternInfo == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + fPatternInfo.adoptInstead(patternInfo); + PatternParser::parseToPatternInfo(UnicodeString(pattern), *patternInfo, status); + if (U_FAILURE(status)) { + return nullptr; + } + + ///////////////////////////////////////////////////////////////////////////////////// + /// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR /// + ///////////////////////////////////////////////////////////////////////////////////// + + // Unit Preferences and Conversions as our first step + if (macros.usage.isSet()) { + if (!isCldrUnit) { + // We only support "usage" when the input unit is specified, and is + // a CLDR Unit. + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + auto usagePrefsHandler = + new UsagePrefsHandler(macros.locale, macros.unit, macros.usage.fValue, chain, status); + fUsagePrefsHandler.adoptInsteadAndCheckErrorCode(usagePrefsHandler, status); + chain = fUsagePrefsHandler.getAlias(); + } else if (isMixedUnit) { + auto unitConversionHandler = new UnitConversionHandler(macros.unit, chain, status); + fUnitConversionHandler.adoptInsteadAndCheckErrorCode(unitConversionHandler, status); + chain = fUnitConversionHandler.getAlias(); + } + + // Multiplier + if (macros.scale.isValid()) { + fMicros.helpers.multiplier.setAndChain(macros.scale, chain); + chain = &fMicros.helpers.multiplier; + } + + // Rounding strategy + Precision precision; + if (!macros.precision.isBogus()) { + precision = macros.precision; + } else if (isCompactNotation) { + precision = Precision::integer().withMinDigits(2); + } else if (isCurrency) { + precision = Precision::currency(UCURR_USAGE_STANDARD); + } else if (macros.usage.isSet()) { + // Bogus Precision - it will get set in the UsagePrefsHandler instead + precision = Precision(); + } else { + precision = Precision::maxFraction(6); + } + UNumberFormatRoundingMode roundingMode; + roundingMode = macros.roundingMode; + fMicros.rounder = {precision, roundingMode, currency, status}; + if (U_FAILURE(status)) { + return nullptr; + } + + // Grouping strategy + if (!macros.grouper.isBogus()) { + fMicros.simple.grouping = macros.grouper; + } else if (isCompactNotation) { + // Compact notation uses minGrouping by default since ICU 59 + fMicros.simple.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2); + } else { + fMicros.simple.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO); + } + fMicros.simple.grouping.setLocaleData(*fPatternInfo, macros.locale); + + // Padding strategy + if (!macros.padder.isBogus()) { + fMicros.padding = macros.padder; + } else { + fMicros.padding = Padder::none(); + } + + // Integer width + if (!macros.integerWidth.isBogus()) { + fMicros.integerWidth = macros.integerWidth; + } else { + fMicros.integerWidth = IntegerWidth::standard(); + } + + // Sign display + if (macros.sign != UNUM_SIGN_COUNT) { + fMicros.sign = macros.sign; + } else { + fMicros.sign = UNUM_SIGN_AUTO; + } + + // Decimal mark display + if (macros.decimal != UNUM_DECIMAL_SEPARATOR_COUNT) { + fMicros.simple.decimal = macros.decimal; + } else { + fMicros.simple.decimal = UNUM_DECIMAL_SEPARATOR_AUTO; + } + + // Use monetary separator symbols + fMicros.simple.useCurrency = isCurrency; + + // Inner modifier (scientific notation) + if (macros.notation.fType == Notation::NTN_SCIENTIFIC) { + auto newScientificHandler = new ScientificHandler(¯os.notation, fMicros.simple.symbols, chain); + if (newScientificHandler == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + fScientificHandler.adoptInstead(newScientificHandler); + chain = fScientificHandler.getAlias(); + } else { + // No inner modifier required + fMicros.modInner = &fMicros.helpers.emptyStrongModifier; + } + + // Middle modifier (patterns, positive/negative, currency symbols, percent) + auto patternModifier = new MutablePatternModifier(false); + if (patternModifier == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + fPatternModifier.adoptInstead(patternModifier); + const AffixPatternProvider* affixProvider = + macros.affixProvider != nullptr && ( + // For more information on this condition, see ICU-22073 + !isCompactNotation || isCurrency == macros.affixProvider->hasCurrencySign()) + ? macros.affixProvider + : static_cast(fPatternInfo.getAlias()); + patternModifier->setPatternInfo(affixProvider, kUndefinedField); + patternModifier->setPatternAttributes(fMicros.sign, isPermille, macros.approximately); + if (patternModifier->needsPlurals()) { + patternModifier->setSymbols( + fMicros.simple.symbols, + currency, + unitWidth, + resolvePluralRules(macros.rules, macros.locale, status), + status); + } else { + patternModifier->setSymbols(fMicros.simple.symbols, currency, unitWidth, nullptr, status); + } + if (safe) { + fImmutablePatternModifier.adoptInsteadAndCheckErrorCode(patternModifier->createImmutable(status), + status); + } + if (U_FAILURE(status)) { + return nullptr; + } + + // currencyAsDecimal + if (affixProvider->currencyAsDecimal()) { + fMicros.simple.currencyAsDecimal = patternModifier->getCurrencySymbolForUnitWidth(status); + } + + // Outer modifier (CLDR units and currency long names) + if (isCldrUnit) { + const char *unitDisplayCase = ""; + if (macros.unitDisplayCase.isSet()) { + unitDisplayCase = macros.unitDisplayCase.fValue; + } + if (macros.usage.isSet()) { + fLongNameMultiplexer.adoptInsteadAndCheckErrorCode( + LongNameMultiplexer::forMeasureUnits( + macros.locale, *fUsagePrefsHandler->getOutputUnits(), unitWidth, unitDisplayCase, + resolvePluralRules(macros.rules, macros.locale, status), chain, status), + status); + chain = fLongNameMultiplexer.getAlias(); + } else if (isMixedUnit) { + fMixedUnitLongNameHandler.adoptInsteadAndCheckErrorCode(new MixedUnitLongNameHandler(), + status); + MixedUnitLongNameHandler::forMeasureUnit( + macros.locale, macros.unit, unitWidth, unitDisplayCase, + resolvePluralRules(macros.rules, macros.locale, status), chain, + fMixedUnitLongNameHandler.getAlias(), status); + chain = fMixedUnitLongNameHandler.getAlias(); + } else { + MeasureUnit unit = macros.unit; + if (!utils::unitIsBaseUnit(macros.perUnit)) { + unit = unit.product(macros.perUnit.reciprocal(status), status); + // This isn't strictly necessary, but was what we specced out + // when perUnit became a backward-compatibility thing: + // unit/perUnit use case is only valid if both units are + // built-ins, or the product is a built-in. + if (uprv_strcmp(unit.getType(), "") == 0 && + (uprv_strcmp(macros.unit.getType(), "") == 0 || + uprv_strcmp(macros.perUnit.getType(), "") == 0)) { + status = U_UNSUPPORTED_ERROR; + return nullptr; + } + } + fLongNameHandler.adoptInsteadAndCheckErrorCode(new LongNameHandler(), status); + LongNameHandler::forMeasureUnit(macros.locale, unit, unitWidth, unitDisplayCase, + resolvePluralRules(macros.rules, macros.locale, status), + chain, fLongNameHandler.getAlias(), status); + chain = fLongNameHandler.getAlias(); + } + } else if (isCurrency && unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) { + fLongNameHandler.adoptInsteadAndCheckErrorCode( + LongNameHandler::forCurrencyLongNames( + macros.locale, currency, resolvePluralRules(macros.rules, macros.locale, status), chain, + status), + status); + chain = fLongNameHandler.getAlias(); + } else { + // No outer modifier required + fMicros.modOuter = &fMicros.helpers.emptyWeakModifier; + } + if (U_FAILURE(status)) { + return nullptr; + } + + // Compact notation + if (isCompactNotation) { + CompactType compactType = (isCurrency && unitWidth != UNUM_UNIT_WIDTH_FULL_NAME) + ? CompactType::TYPE_CURRENCY : CompactType::TYPE_DECIMAL; + auto newCompactHandler = new CompactHandler( + macros.notation.fUnion.compactStyle, + macros.locale, + nsName, + compactType, + resolvePluralRules(macros.rules, macros.locale, status), + patternModifier, + safe, + chain, + status); + if (U_FAILURE(status)) { + return nullptr; + } + if (newCompactHandler == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + fCompactHandler.adoptInstead(newCompactHandler); + chain = fCompactHandler.getAlias(); + } + if (U_FAILURE(status)) { + return nullptr; + } + + // Always add the pattern modifier as the last element of the chain. + if (safe) { + fImmutablePatternModifier->addToChain(chain); + chain = fImmutablePatternModifier.getAlias(); + } else { + patternModifier->addToChain(chain); + chain = patternModifier; + } + + return chain; +} + +const PluralRules* +NumberFormatterImpl::resolvePluralRules( + const PluralRules* rulesPtr, + const Locale& locale, + UErrorCode& status) { + if (rulesPtr != nullptr) { + return rulesPtr; + } + // Lazily create PluralRules + if (fRules.isNull()) { + fRules.adoptInstead(PluralRules::forLocale(locale, status)); + } + return fRules.getAlias(); +} + +int32_t NumberFormatterImpl::writeAffixes( + const MicroProps& micros, + FormattedStringBuilder& string, + int32_t start, + int32_t end, + UErrorCode& status) { + U_ASSERT(micros.modOuter != nullptr); + // Always apply the inner modifier (which is "strong"). + int32_t length = micros.modInner->apply(string, start, end, status); + if (micros.padding.isValid()) { + length += micros.padding + .padAndApply(*micros.modMiddle, *micros.modOuter, string, start, length + end, status); + } else { + length += micros.modMiddle->apply(string, start, length + end, status); + length += micros.modOuter->apply(string, start, length + end, status); + } + return length; +} + +int32_t NumberFormatterImpl::writeNumber( + const SimpleMicroProps& micros, + DecimalQuantity& quantity, + FormattedStringBuilder& string, + int32_t index, + UErrorCode& status) { + int32_t length = 0; + if (quantity.isInfinite()) { + length += string.insert( + length + index, + micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kInfinitySymbol), + {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD}, + status); + + } else if (quantity.isNaN()) { + length += string.insert( + length + index, + micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kNaNSymbol), + {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD}, + status); + + } else { + // Add the integer digits + length += writeIntegerDigits( + micros, + quantity, + string, + length + index, + status); + + // Add the decimal point + if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) { + if (!micros.currencyAsDecimal.isBogus()) { + length += string.insert( + length + index, + micros.currencyAsDecimal, + {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}, + status); + } else if (micros.useCurrency) { + length += string.insert( + length + index, + micros.symbols->getSymbol( + DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol), + {UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD}, + status); + } else { + length += string.insert( + length + index, + micros.symbols->getSymbol( + DecimalFormatSymbols::ENumberFormatSymbol::kDecimalSeparatorSymbol), + {UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD}, + status); + } + } + + // Add the fraction digits + length += writeFractionDigits(micros, quantity, string, length + index, status); + + if (length == 0) { + // Force output of the digit for value 0 + length += utils::insertDigitFromSymbols( + string, + index, + 0, + *micros.symbols, + {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD}, + status); + } + } + + return length; +} + +int32_t NumberFormatterImpl::writeIntegerDigits( + const SimpleMicroProps& micros, + DecimalQuantity& quantity, + FormattedStringBuilder& string, + int32_t index, + UErrorCode& status) { + int length = 0; + int integerCount = quantity.getUpperDisplayMagnitude() + 1; + for (int i = 0; i < integerCount; i++) { + // Add grouping separator + if (micros.grouping.groupAtPosition(i, quantity)) { + length += string.insert( + index, + micros.useCurrency ? micros.symbols->getSymbol( + DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol) + : micros.symbols->getSymbol( + DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol), + {UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD}, + status); + } + + // Get and append the next digit value + int8_t nextDigit = quantity.getDigit(i); + length += utils::insertDigitFromSymbols( + string, + index, + nextDigit, + *micros.symbols, + {UFIELD_CATEGORY_NUMBER, + UNUM_INTEGER_FIELD}, + status); + } + return length; +} + +int32_t NumberFormatterImpl::writeFractionDigits( + const SimpleMicroProps& micros, + DecimalQuantity& quantity, + FormattedStringBuilder& string, + int32_t index, + 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 += utils::insertDigitFromSymbols( + string, + length + index, + nextDigit, + *micros.symbols, + {UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD}, + status); + } + return length; +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_formatimpl.h b/deps/icu-small/source/i18n/number_formatimpl.h index d7be1468b6d7af..ee76ebe75eb9bb 100644 --- a/deps/icu-small/source/i18n/number_formatimpl.h +++ b/deps/icu-small/source/i18n/number_formatimpl.h @@ -1,164 +1,180 @@ -// © 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_FORMATIMPL_H__ -#define __NUMBER_FORMATIMPL_H__ - -#include "number_types.h" -#include "formatted_string_builder.h" -#include "number_patternstring.h" -#include "number_usageprefs.h" -#include "number_utils.h" -#include "number_patternmodifier.h" -#include "number_longnames.h" -#include "number_compact.h" -#include "number_microprops.h" -#include "number_utypes.h" - -U_NAMESPACE_BEGIN namespace number { -namespace impl { - -/** - * This is the "brain" of the number formatting pipeline. It ties all the pieces together, taking in a MacroProps and a - * DecimalQuantity and outputting a properly formatted number string. - */ -class NumberFormatterImpl : public UMemory { - public: - /** - * Builds a "safe" MicroPropsGenerator, which is thread-safe and can be used repeatedly. - * The caller owns the returned NumberFormatterImpl. - */ - NumberFormatterImpl(const MacroProps ¯os, UErrorCode &status); - - /** - * Default constructor; leaves the NumberFormatterImpl in an undefined state. - * Takes an error code to prevent the method from being called accidentally. - */ - NumberFormatterImpl(UErrorCode &) {} - - /** - * Builds and evaluates an "unsafe" MicroPropsGenerator, which is cheaper but can be used only once. - */ - static int32_t formatStatic(const MacroProps ¯os, UFormattedNumberData *results, - 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, Signum signum, - StandardPlural::Form plural, FormattedStringBuilder& outString, - UErrorCode& status); - - /** - * Evaluates the "safe" MicroPropsGenerator created by "fromMacros". - */ - int32_t format(UFormattedNumberData *results, UErrorCode &status) const; - - /** - * Like format(), but saves the result into an output MicroProps without additional processing. - */ - void preProcess(DecimalQuantity& inValue, MicroProps& microsOut, UErrorCode& status) const; - - /** - * Like getPrefixSuffixStatic() but uses the safe compiled object. - */ - int32_t getPrefixSuffix(Signum signum, StandardPlural::Form plural, FormattedStringBuilder& outString, - UErrorCode& status) const; - - const MicroProps& getRawMicroProps() const { - return fMicros; - } - - /** - * Synthesizes the output string from a MicroProps and DecimalQuantity. - * This method formats only the main number, not affixes. - */ - static int32_t writeNumber(const MicroProps& micros, DecimalQuantity& quantity, - FormattedStringBuilder& string, int32_t index, UErrorCode& status); - - /** - * Adds the affixes. Intended to be called immediately after formatNumber. - */ - static int32_t writeAffixes(const MicroProps& micros, FormattedStringBuilder& string, int32_t start, - int32_t end, UErrorCode& status); - - private: - // Head of the MicroPropsGenerator linked list. Subclasses' processQuantity - // methods process this list in a parent-first order, such that the last - // item added, which this points to, typically has its logic executed last. - const MicroPropsGenerator *fMicroPropsGenerator = nullptr; - - // Tail of the list: - MicroProps fMicros; - - // Other fields possibly used by the number formatting pipeline: - // TODO: Convert more of these LocalPointers to value objects to reduce the number of news? - LocalPointer fUsagePrefsHandler; - LocalPointer fUnitConversionHandler; - LocalPointer fSymbols; - LocalPointer fRules; - LocalPointer fPatternInfo; - LocalPointer fScientificHandler; - LocalPointer fPatternModifier; - LocalPointer fImmutablePatternModifier; - LocalPointer fLongNameHandler; - // TODO: use a common base class that enables fLongNameHandler, - // fLongNameMultiplexer, and fMixedUnitLongNameHandler to be merged into one - // member? - LocalPointer fMixedUnitLongNameHandler; - LocalPointer fLongNameMultiplexer; - LocalPointer fCompactHandler; - - NumberFormatterImpl(const MacroProps ¯os, bool safe, UErrorCode &status); - - MicroProps& preProcessUnsafe(DecimalQuantity &inValue, UErrorCode &status); - - int32_t getPrefixSuffixUnsafe(Signum signum, StandardPlural::Form plural, - FormattedStringBuilder& 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. - */ - const PluralRules * - resolvePluralRules(const PluralRules *rulesPtr, const Locale &locale, UErrorCode &status); - - /** - * Synthesizes the MacroProps into a MicroPropsGenerator. All information, including the locale, is encoded into the - * MicroPropsGenerator, except for the quantity itself, which is left abstract and must be provided to the returned - * MicroPropsGenerator instance. - * - * @see MicroPropsGenerator - * @param macros - * The {@link MacroProps} to consume. This method does not mutate the MacroProps instance. - * @param safe - * If true, the returned MicroPropsGenerator will be thread-safe. If false, the returned value will - * not be thread-safe, intended for a single "one-shot" use only. Building the thread-safe - * object is more expensive. - */ - const MicroPropsGenerator * - macrosToMicroGenerator(const MacroProps ¯os, bool safe, UErrorCode &status); - - static int32_t - writeIntegerDigits(const MicroProps µs, DecimalQuantity &quantity, FormattedStringBuilder &string, - int32_t index, UErrorCode &status); - - static int32_t - writeFractionDigits(const MicroProps µs, DecimalQuantity &quantity, FormattedStringBuilder &string, - int32_t index, UErrorCode &status); -}; - -} // namespace impl -} // namespace number -U_NAMESPACE_END - - -#endif //__NUMBER_FORMATIMPL_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_FORMATIMPL_H__ +#define __NUMBER_FORMATIMPL_H__ + +#include "number_types.h" +#include "formatted_string_builder.h" +#include "number_patternstring.h" +#include "number_usageprefs.h" +#include "number_utils.h" +#include "number_patternmodifier.h" +#include "number_longnames.h" +#include "number_compact.h" +#include "number_microprops.h" +#include "number_utypes.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +/** + * This is the "brain" of the number formatting pipeline. It ties all the pieces together, taking in a MacroProps and a + * DecimalQuantity and outputting a properly formatted number string. + */ +class NumberFormatterImpl : public UMemory { + public: + /** + * Builds a "safe" MicroPropsGenerator, which is thread-safe and can be used repeatedly. + * The caller owns the returned NumberFormatterImpl. + */ + NumberFormatterImpl(const MacroProps ¯os, UErrorCode &status); + + /** + * Default constructor; leaves the NumberFormatterImpl in an undefined state. + * Takes an error code to prevent the method from being called accidentally. + */ + NumberFormatterImpl(UErrorCode &) {} + + /** + * Builds and evaluates an "unsafe" MicroPropsGenerator, which is cheaper but can be used only once. + */ + static int32_t formatStatic(const MacroProps ¯os, UFormattedNumberData *results, + 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, Signum signum, + StandardPlural::Form plural, FormattedStringBuilder& outString, + UErrorCode& status); + + /** + * Evaluates the "safe" MicroPropsGenerator created by "fromMacros". + */ + int32_t format(UFormattedNumberData *results, UErrorCode &status) const; + + /** + * Like format(), but saves the result into an output MicroProps without additional processing. + */ + void preProcess(DecimalQuantity& inValue, MicroProps& microsOut, UErrorCode& status) const; + + /** + * Like getPrefixSuffixStatic() but uses the safe compiled object. + */ + int32_t getPrefixSuffix(Signum signum, StandardPlural::Form plural, FormattedStringBuilder& outString, + UErrorCode& status) const; + + const MicroProps& getRawMicroProps() const { + return fMicros; + } + + /** + * Synthesizes the output string from a MicroProps and DecimalQuantity. + * This method formats only the main number, not affixes. + */ + static int32_t writeNumber( + const SimpleMicroProps& micros, + DecimalQuantity& quantity, + FormattedStringBuilder& string, + int32_t index, + UErrorCode& status); + + /** + * Adds the affixes. Intended to be called immediately after formatNumber. + */ + static int32_t writeAffixes( + const MicroProps& micros, + FormattedStringBuilder& string, + int32_t start, + int32_t end, + UErrorCode& status); + + private: + // Head of the MicroPropsGenerator linked list. Subclasses' processQuantity + // methods process this list in a parent-first order, such that the last + // item added, which this points to, typically has its logic executed last. + const MicroPropsGenerator *fMicroPropsGenerator = nullptr; + + // Tail of the list: + MicroProps fMicros; + + // Other fields possibly used by the number formatting pipeline: + // TODO: Convert more of these LocalPointers to value objects to reduce the number of news? + LocalPointer fUsagePrefsHandler; + LocalPointer fUnitConversionHandler; + LocalPointer fSymbols; + LocalPointer fRules; + LocalPointer fPatternInfo; + LocalPointer fScientificHandler; + LocalPointer fPatternModifier; + LocalPointer fImmutablePatternModifier; + LocalPointer fLongNameHandler; + // TODO: use a common base class that enables fLongNameHandler, + // fLongNameMultiplexer, and fMixedUnitLongNameHandler to be merged into one + // member? + LocalPointer fMixedUnitLongNameHandler; + LocalPointer fLongNameMultiplexer; + LocalPointer fCompactHandler; + + NumberFormatterImpl(const MacroProps ¯os, bool safe, UErrorCode &status); + + MicroProps& preProcessUnsafe(DecimalQuantity &inValue, UErrorCode &status); + + int32_t getPrefixSuffixUnsafe(Signum signum, StandardPlural::Form plural, + FormattedStringBuilder& 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. + */ + const PluralRules * + resolvePluralRules(const PluralRules *rulesPtr, const Locale &locale, UErrorCode &status); + + /** + * Synthesizes the MacroProps into a MicroPropsGenerator. All information, including the locale, is encoded into the + * MicroPropsGenerator, except for the quantity itself, which is left abstract and must be provided to the returned + * MicroPropsGenerator instance. + * + * @see MicroPropsGenerator + * @param macros + * The {@link MacroProps} to consume. This method does not mutate the MacroProps instance. + * @param safe + * If true, the returned MicroPropsGenerator will be thread-safe. If false, the returned value will + * not be thread-safe, intended for a single "one-shot" use only. Building the thread-safe + * object is more expensive. + */ + const MicroPropsGenerator * + macrosToMicroGenerator(const MacroProps ¯os, bool safe, UErrorCode &status); + + static int32_t + writeIntegerDigits( + const SimpleMicroProps& micros, + DecimalQuantity &quantity, + FormattedStringBuilder &string, + int32_t index, + UErrorCode &status); + + static int32_t + writeFractionDigits( + const SimpleMicroProps& micros, + DecimalQuantity &quantity, + FormattedStringBuilder &string, + int32_t index, + UErrorCode &status); +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + + +#endif //__NUMBER_FORMATIMPL_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_grouping.cpp b/deps/icu-small/source/i18n/number_grouping.cpp index 9ba639e67e2257..df953d3083727d 100644 --- a/deps/icu-small/source/i18n/number_grouping.cpp +++ b/deps/icu-small/source/i18n/number_grouping.cpp @@ -1,109 +1,109 @@ -// © 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 - -#include "unicode/numberformatter.h" -#include "number_patternstring.h" -#include "uresimp.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - -namespace { - -int16_t getMinGroupingForLocale(const Locale& locale) { - // TODO: Cache this? - UErrorCode localStatus = U_ZERO_ERROR; - LocalUResourceBundlePointer bundle(ures_open(NULL, locale.getName(), &localStatus)); - int32_t resultLen = 0; - const char16_t* result = ures_getStringByKeyWithFallback( - bundle.getAlias(), - "NumberElements/minimumGroupingDigits", - &resultLen, - &localStatus); - // TODO: Is it safe to assume resultLen == 1? Would locales set minGrouping >= 10? - if (U_FAILURE(localStatus) || resultLen != 1) { - return 1; - } - return result[0] - u'0'; -} - -} - -Grouper Grouper::forStrategy(UNumberGroupingStrategy grouping) { - switch (grouping) { - case UNUM_GROUPING_OFF: - return {-1, -1, -2, grouping}; - case UNUM_GROUPING_AUTO: - return {-2, -2, -2, grouping}; - case UNUM_GROUPING_MIN2: - return {-2, -2, -3, grouping}; - case UNUM_GROUPING_ON_ALIGNED: - return {-4, -4, 1, grouping}; - case UNUM_GROUPING_THOUSANDS: - return {3, 3, 1, grouping}; - default: - UPRV_UNREACHABLE_EXIT; - } -} - -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 (fMinGrouping == -2) { - fMinGrouping = getMinGroupingForLocale(locale); - } else if (fMinGrouping == -3) { - fMinGrouping = static_cast(uprv_max(2, getMinGroupingForLocale(locale))); - } else { - // leave fMinGrouping alone - } - if (fGrouping1 != -2 && fGrouping2 != -4) { - return; - } - auto grouping1 = static_cast (patternInfo.positive.groupingSizes & 0xffff); - auto grouping2 = static_cast ((patternInfo.positive.groupingSizes >> 16) & 0xffff); - auto grouping3 = static_cast ((patternInfo.positive.groupingSizes >> 32) & 0xffff); - if (grouping2 == -1) { - grouping1 = fGrouping1 == -4 ? (short) 3 : (short) -1; - } - if (grouping3 == -1) { - grouping2 = grouping1; - } - fGrouping1 = grouping1; - fGrouping2 = grouping2; -} - -bool Grouper::groupAtPosition(int32_t position, const impl::DecimalQuantity &value) const { - U_ASSERT(fGrouping1 > -2); - if (fGrouping1 == -1 || fGrouping1 == 0) { - // Either -1 or 0 means "no grouping" - return false; - } - position -= fGrouping1; - return position >= 0 && (position % fGrouping2) == 0 - && value.getUpperDisplayMagnitude() - fGrouping1 + 1 >= fMinGrouping; -} - -int16_t Grouper::getPrimary() const { - return fGrouping1; -} - -int16_t Grouper::getSecondary() const { - return fGrouping2; -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include "unicode/numberformatter.h" +#include "number_patternstring.h" +#include "uresimp.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +namespace { + +int16_t getMinGroupingForLocale(const Locale& locale) { + // TODO: Cache this? + UErrorCode localStatus = U_ZERO_ERROR; + LocalUResourceBundlePointer bundle(ures_open(nullptr, locale.getName(), &localStatus)); + int32_t resultLen = 0; + const char16_t* result = ures_getStringByKeyWithFallback( + bundle.getAlias(), + "NumberElements/minimumGroupingDigits", + &resultLen, + &localStatus); + // TODO: Is it safe to assume resultLen == 1? Would locales set minGrouping >= 10? + if (U_FAILURE(localStatus) || resultLen != 1) { + return 1; + } + return result[0] - u'0'; +} + +} + +Grouper Grouper::forStrategy(UNumberGroupingStrategy grouping) { + switch (grouping) { + case UNUM_GROUPING_OFF: + return {-1, -1, -2, grouping}; + case UNUM_GROUPING_AUTO: + return {-2, -2, -2, grouping}; + case UNUM_GROUPING_MIN2: + return {-2, -2, -3, grouping}; + case UNUM_GROUPING_ON_ALIGNED: + return {-4, -4, 1, grouping}; + case UNUM_GROUPING_THOUSANDS: + return {3, 3, 1, grouping}; + default: + UPRV_UNREACHABLE_EXIT; + } +} + +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 (fMinGrouping == -2) { + fMinGrouping = getMinGroupingForLocale(locale); + } else if (fMinGrouping == -3) { + fMinGrouping = static_cast(uprv_max(2, getMinGroupingForLocale(locale))); + } else { + // leave fMinGrouping alone + } + if (fGrouping1 != -2 && fGrouping2 != -4) { + return; + } + auto grouping1 = static_cast (patternInfo.positive.groupingSizes & 0xffff); + auto grouping2 = static_cast ((patternInfo.positive.groupingSizes >> 16) & 0xffff); + auto grouping3 = static_cast ((patternInfo.positive.groupingSizes >> 32) & 0xffff); + if (grouping2 == -1) { + grouping1 = fGrouping1 == -4 ? (short) 3 : (short) -1; + } + if (grouping3 == -1) { + grouping2 = grouping1; + } + fGrouping1 = grouping1; + fGrouping2 = grouping2; +} + +bool Grouper::groupAtPosition(int32_t position, const impl::DecimalQuantity &value) const { + U_ASSERT(fGrouping1 > -2); + if (fGrouping1 == -1 || fGrouping1 == 0) { + // Either -1 or 0 means "no grouping" + return false; + } + position -= fGrouping1; + return position >= 0 && (position % fGrouping2) == 0 + && 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 10b853423c847a..2d31b27371afec 100644 --- a/deps/icu-small/source/i18n/number_integerwidth.cpp +++ b/deps/icu-small/source/i18n/number_integerwidth.cpp @@ -1,71 +1,71 @@ -// © 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 - -#include "unicode/numberformatter.h" -#include "number_types.h" -#include "number_decimalquantity.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - -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, false}; - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -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), false}; - } else if (maxInt == -1) { - return {minInt, -1, false}; - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -void IntegerWidth::apply(impl::DecimalQuantity& quantity, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - if (fHasError) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } else if (fUnion.minMaxInt.fMaxInt == -1) { - quantity.setMinInteger(fUnion.minMaxInt.fMinInt); - } else { - // Enforce the backwards-compatibility feature "FormatFailIfMoreThanMaxDigits" - if (fUnion.minMaxInt.fFormatFailIfMoreThanMaxDigits && - fUnion.minMaxInt.fMaxInt < quantity.getMagnitude()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } - quantity.setMinInteger(fUnion.minMaxInt.fMinInt); - quantity.applyMaxInteger(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 */ +// © 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 + +#include "unicode/numberformatter.h" +#include "number_types.h" +#include "number_decimalquantity.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +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, false}; + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +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), false}; + } else if (maxInt == -1) { + return {minInt, -1, false}; + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +void IntegerWidth::apply(impl::DecimalQuantity& quantity, UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + if (fHasError) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } else if (fUnion.minMaxInt.fMaxInt == -1) { + quantity.setMinInteger(fUnion.minMaxInt.fMinInt); + } else { + // Enforce the backwards-compatibility feature "FormatFailIfMoreThanMaxDigits" + if (fUnion.minMaxInt.fFormatFailIfMoreThanMaxDigits && + fUnion.minMaxInt.fMaxInt < quantity.getMagnitude()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } + quantity.setMinInteger(fUnion.minMaxInt.fMinInt); + quantity.applyMaxInteger(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 be0320cecf236a..efb6ea09cee239 100644 --- a/deps/icu-small/source/i18n/number_longnames.cpp +++ b/deps/icu-small/source/i18n/number_longnames.cpp @@ -1,1766 +1,1766 @@ -// © 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 - -#include - -#include "unicode/simpleformatter.h" -#include "unicode/ures.h" -#include "ureslocs.h" -#include "charstr.h" -#include "uresimp.h" -#include "measunit_impl.h" -#include "number_longnames.h" -#include "number_microprops.h" -#include -#include "cstring.h" -#include "util.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - -namespace { - -/** - * Display Name (this format has no placeholder). - * - * Used as an index into the LongNameHandler::simpleFormats array. Units - * resources cover the normal set of PluralRules keys, as well as `dnam` and - * `per` forms. - */ -constexpr int32_t DNAM_INDEX = StandardPlural::Form::COUNT; -/** - * "per" form (e.g. "{0} per day" is day's "per" form). - * - * Used as an index into the LongNameHandler::simpleFormats array. Units - * resources cover the normal set of PluralRules keys, as well as `dnam` and - * `per` forms. - */ -constexpr int32_t PER_INDEX = StandardPlural::Form::COUNT + 1; -/** - * Gender of the word, in languages with grammatical gender. - */ -constexpr int32_t GENDER_INDEX = StandardPlural::Form::COUNT + 2; -// Number of keys in the array populated by PluralTableSink. -constexpr int32_t ARRAY_LENGTH = StandardPlural::Form::COUNT + 3; - -// TODO(icu-units#28): load this list from resources, after creating a "&set" -// function for use in ldml2icu rules. -const int32_t GENDER_COUNT = 7; -const char *gGenders[GENDER_COUNT] = {"animate", "common", "feminine", "inanimate", - "masculine", "neuter", "personal"}; - -// Converts a UnicodeString to a const char*, either pointing to a string in -// gGenders, or pointing to an empty string if an appropriate string was not -// found. -const char *getGenderString(UnicodeString uGender, UErrorCode status) { - if (uGender.length() == 0) { - return ""; - } - CharString gender; - gender.appendInvariantChars(uGender, status); - if (U_FAILURE(status)) { - return ""; - } - int32_t first = 0; - int32_t last = GENDER_COUNT; - while (first < last) { - int32_t mid = (first + last) / 2; - int32_t cmp = uprv_strcmp(gender.data(), gGenders[mid]); - if (cmp == 0) { - return gGenders[mid]; - } else if (cmp > 0) { - first = mid + 1; - } else if (cmp < 0) { - last = mid; - } - } - // We don't return an error in case our gGenders list is incomplete in - // production. - // - // TODO(icu-units#28): a unit test checking all locales' genders are covered - // by gGenders? Else load a complete list of genders found in - // grammaticalFeatures in an initOnce. - return ""; -} - -// Returns the array index that corresponds to the given pluralKeyword. -static int32_t getIndex(const char* pluralKeyword, UErrorCode& status) { - // pluralKeyword can also be "dnam", "per", or "gender" - switch (*pluralKeyword) { - case 'd': - if (uprv_strcmp(pluralKeyword + 1, "nam") == 0) { - return DNAM_INDEX; - } - break; - case 'g': - if (uprv_strcmp(pluralKeyword + 1, "ender") == 0) { - return GENDER_INDEX; - } - break; - case 'p': - if (uprv_strcmp(pluralKeyword + 1, "er") == 0) { - return PER_INDEX; - } - break; - default: - break; - } - StandardPlural::Form plural = StandardPlural::fromString(pluralKeyword, status); - return plural; -} - -// Selects a string out of the `strings` array which corresponds to the -// specified plural form, with fallback to the OTHER form. -// -// The `strings` array must have ARRAY_LENGTH items: one corresponding to each -// of the plural forms, plus a display name ("dnam") and a "per" form. -static UnicodeString getWithPlural( - const UnicodeString* strings, - StandardPlural::Form plural, - UErrorCode& status) { - UnicodeString result = strings[plural]; - if (result.isBogus()) { - result = strings[StandardPlural::Form::OTHER]; - } - if (result.isBogus()) { - // There should always be data in the "other" plural variant. - status = U_INTERNAL_PROGRAM_ERROR; - } - return result; -} - -enum PlaceholderPosition { PH_EMPTY, PH_NONE, PH_BEGINNING, PH_MIDDLE, PH_END }; - -/** - * Returns three outputs extracted from pattern. - * - * @param coreUnit is extracted as per Extract(...) in the spec: - * https://unicode.org/reports/tr35/tr35-general.html#compound-units - * @param PlaceholderPosition indicates where in the string the placeholder was - * found. - * @param joinerChar Iff the placeholder was at the beginning or end, joinerChar - * contains the space character (if any) that separated the placeholder from - * the rest of the pattern. Otherwise, joinerChar is set to NUL. Only one - * space character is considered. - */ -void extractCorePattern(const UnicodeString &pattern, - UnicodeString &coreUnit, - PlaceholderPosition &placeholderPosition, - UChar &joinerChar) { - joinerChar = 0; - int32_t len = pattern.length(); - if (pattern.startsWith(u"{0}", 3)) { - placeholderPosition = PH_BEGINNING; - if (u_isJavaSpaceChar(pattern[3])) { - joinerChar = pattern[3]; - coreUnit.setTo(pattern, 4, len - 4); - } else { - coreUnit.setTo(pattern, 3, len - 3); - } - } else if (pattern.endsWith(u"{0}", 3)) { - placeholderPosition = PH_END; - if (u_isJavaSpaceChar(pattern[len - 4])) { - coreUnit.setTo(pattern, 0, len - 4); - joinerChar = pattern[len - 4]; - } else { - coreUnit.setTo(pattern, 0, len - 3); - } - } else if (pattern.indexOf(u"{0}", 3, 1, len - 2) == -1) { - placeholderPosition = PH_NONE; - coreUnit = pattern; - } else { - placeholderPosition = PH_MIDDLE; - coreUnit = pattern; - } -} - -////////////////////////// -/// BEGIN DATA LOADING /// -////////////////////////// - -// Gets the gender of a built-in unit: unit must be a built-in. Returns an empty -// string both in case of unknown gender and in case of unknown unit. -UnicodeString -getGenderForBuiltin(const Locale &locale, const MeasureUnit &builtinUnit, UErrorCode &status) { - LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, locale.getName(), &status)); - if (U_FAILURE(status)) { return {}; } - - // Map duration-year-person, duration-week-person, etc. to duration-year, duration-week, ... - // TODO(ICU-20400): Get duration-*-person data properly with aliases. - StringPiece subtypeForResource; - int32_t subtypeLen = static_cast(uprv_strlen(builtinUnit.getSubtype())); - if (subtypeLen > 7 && uprv_strcmp(builtinUnit.getSubtype() + subtypeLen - 7, "-person") == 0) { - subtypeForResource = {builtinUnit.getSubtype(), subtypeLen - 7}; - } else { - subtypeForResource = builtinUnit.getSubtype(); - } - - CharString key; - key.append("units/", status); - key.append(builtinUnit.getType(), status); - key.append("/", status); - key.append(subtypeForResource, status); - key.append("/gender", status); - - UErrorCode localStatus = status; - int32_t resultLen = 0; - const UChar *result = - ures_getStringByKeyWithFallback(unitsBundle.getAlias(), key.data(), &resultLen, &localStatus); - if (U_SUCCESS(localStatus)) { - status = localStatus; - return UnicodeString(true, result, resultLen); - } else { - // TODO(icu-units#28): "$unitRes/gender" does not exist. Do we want to - // check whether the parent "$unitRes" exists? Then we could return - // U_MISSING_RESOURCE_ERROR for incorrect usage (e.g. builtinUnit not - // being a builtin). - return {}; - } -} - -// Loads data from a resource tree with paths matching -// $key/$pluralForm/$gender/$case, with lateral inheritance for missing cases -// and genders. -// -// An InflectedPluralSink is configured to load data for a specific gender and -// case. It loads all plural forms, because selection between plural forms is -// dependent upon the value being formatted. -// -// See data/unit/de.txt and data/unit/fr.txt for examples - take a look at -// units/compound/power2: German has case, French has differences for gender, -// but no case. -// -// TODO(icu-units#138): Conceptually similar to PluralTableSink, however the -// tree structures are different. After homogenizing the structures, we may be -// able to unify the two classes. -// -// TODO: Spec violation: expects presence of "count" - does not fallback to an -// absent "count"! If this fallback were added, getCompoundValue could be -// superseded? -class InflectedPluralSink : public ResourceSink { - public: - // Accepts `char*` rather than StringPiece because - // ResourceTable::findValue(...) requires a null-terminated `char*`. - // - // NOTE: outArray MUST have a length of at least ARRAY_LENGTH. No bounds - // checking is performed. - explicit InflectedPluralSink(const char *gender, const char *caseVariant, UnicodeString *outArray) - : gender(gender), caseVariant(caseVariant), outArray(outArray) { - // Initialize the array to bogus strings. - for (int32_t i = 0; i < ARRAY_LENGTH; i++) { - outArray[i].setToBogus(); - } - } - - // See ResourceSink::put(). - void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) U_OVERRIDE { - int32_t pluralIndex = getIndex(key, status); - if (U_FAILURE(status)) { return; } - if (!outArray[pluralIndex].isBogus()) { - // We already have a pattern - return; - } - ResourceTable genderTable = value.getTable(status); - ResourceTable caseTable; // This instance has to outlive `value` - if (loadForPluralForm(genderTable, caseTable, value, status)) { - outArray[pluralIndex] = value.getUnicodeString(status); - } - } - - private: - // Tries to load data for the configured gender from `genderTable`. Returns - // true if found, returning the data in `value`. The returned data will be - // for the configured gender if found, falling back to "neuter" and - // no-gender if not. The caseTable parameter holds the intermediate - // ResourceTable for the sake of lifetime management. - bool loadForPluralForm(const ResourceTable &genderTable, - ResourceTable &caseTable, - ResourceValue &value, - UErrorCode &status) { - if (uprv_strcmp(gender, "") != 0) { - if (loadForGender(genderTable, gender, caseTable, value, status)) { - return true; - } - if (uprv_strcmp(gender, "neuter") != 0 && - loadForGender(genderTable, "neuter", caseTable, value, status)) { - return true; - } - } - if (loadForGender(genderTable, "_", caseTable, value, status)) { - return true; - } - return false; - } - - // Tries to load data for the given gender from `genderTable`. Returns true - // if found, returning the data in `value`. The returned data will be for - // the configured case if found, falling back to "nominative" and no-case if - // not. - bool loadForGender(const ResourceTable &genderTable, - const char *genderVal, - ResourceTable &caseTable, - ResourceValue &value, - UErrorCode &status) { - if (!genderTable.findValue(genderVal, value)) { - return false; - } - caseTable = value.getTable(status); - if (uprv_strcmp(caseVariant, "") != 0) { - if (loadForCase(caseTable, caseVariant, value)) { - return true; - } - if (uprv_strcmp(caseVariant, "nominative") != 0 && - loadForCase(caseTable, "nominative", value)) { - return true; - } - } - if (loadForCase(caseTable, "_", value)) { - return true; - } - return false; - } - - // Tries to load data for the given case from `caseTable`. Returns true if - // found, returning the data in `value`. - bool loadForCase(const ResourceTable &caseTable, const char *caseValue, ResourceValue &value) { - if (!caseTable.findValue(caseValue, value)) { - return false; - } - return true; - } - - const char *gender; - const char *caseVariant; - UnicodeString *outArray; -}; - -// Fetches localised formatting patterns for the given subKey. See documentation -// for InflectedPluralSink for details. -// -// Data is loaded for the appropriate unit width, with missing data filled in -// from unitsShort. -void getInflectedMeasureData(StringPiece subKey, - const Locale &locale, - const UNumberUnitWidth &width, - const char *gender, - const char *caseVariant, - UnicodeString *outArray, - UErrorCode &status) { - InflectedPluralSink sink(gender, caseVariant, outArray); - LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, locale.getName(), &status)); - if (U_FAILURE(status)) { return; } - - CharString key; - key.append("units", status); - if (width == UNUM_UNIT_WIDTH_NARROW) { - key.append("Narrow", status); - } else if (width == UNUM_UNIT_WIDTH_SHORT) { - key.append("Short", status); - } - key.append("/", status); - key.append(subKey, status); - - UErrorCode localStatus = status; - ures_getAllChildrenWithFallback(unitsBundle.getAlias(), key.data(), sink, localStatus); - if (width == UNUM_UNIT_WIDTH_SHORT) { - status = localStatus; - return; - } -} - -class PluralTableSink : public ResourceSink { - public: - // NOTE: outArray MUST have a length of at least ARRAY_LENGTH. No bounds - // checking is performed. - explicit PluralTableSink(UnicodeString *outArray) : outArray(outArray) { - // Initialize the array to bogus strings. - for (int32_t i = 0; i < ARRAY_LENGTH; i++) { - outArray[i].setToBogus(); - } - } - - void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) U_OVERRIDE { - if (uprv_strcmp(key, "case") == 0) { - return; - } - int32_t index = getIndex(key, status); - if (U_FAILURE(status)) { return; } - if (!outArray[index].isBogus()) { - return; - } - outArray[index] = value.getUnicodeString(status); - if (U_FAILURE(status)) { return; } - } - - private: - UnicodeString *outArray; -}; - -/** - * Populates outArray with `locale`-specific values for `unit` through use of - * PluralTableSink. Only the set of basic units are supported! - * - * Reading from resources *unitsNarrow* and *unitsShort* (for width - * UNUM_UNIT_WIDTH_NARROW), or just *unitsShort* (for width - * UNUM_UNIT_WIDTH_SHORT). For other widths, it reads just "units". - * - * @param unit must be a built-in unit, i.e. must have a type and subtype, - * listed in gTypes and gSubTypes in measunit.cpp. - * @param unitDisplayCase the empty string and "nominative" are treated the - * same. For other cases, strings for the requested case are used if found. - * (For any missing case-specific data, we fall back to nominative.) - * @param outArray must be of fixed length ARRAY_LENGTH. - */ -void getMeasureData(const Locale &locale, - const MeasureUnit &unit, - const UNumberUnitWidth &width, - const char *unitDisplayCase, - UnicodeString *outArray, - UErrorCode &status) { - PluralTableSink sink(outArray); - LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, locale.getName(), &status)); - if (U_FAILURE(status)) { return; } - - CharString subKey; - subKey.append("/", status); - subKey.append(unit.getType(), status); - subKey.append("/", status); - - // Check if unitSubType is an alias or not. - LocalUResourceBundlePointer aliasBundle(ures_open(U_ICUDATA_ALIAS, "metadata", &status)); - - UErrorCode aliasStatus = status; - StackUResourceBundle aliasFillIn; - CharString aliasKey; - aliasKey.append("alias/unit/", aliasStatus); - aliasKey.append(unit.getSubtype(), aliasStatus); - aliasKey.append("/replacement", aliasStatus); - ures_getByKeyWithFallback(aliasBundle.getAlias(), aliasKey.data(), aliasFillIn.getAlias(), - &aliasStatus); - CharString unitSubType; - if (!U_FAILURE(aliasStatus)) { - // This means the subType is an alias. Then, replace unitSubType with the replacement. - auto replacement = ures_getUnicodeString(aliasFillIn.getAlias(), &status); - unitSubType.appendInvariantChars(replacement, status); - } else { - unitSubType.append(unit.getSubtype(), status); - } - - // Map duration-year-person, duration-week-person, etc. to duration-year, duration-week, ... - // TODO(ICU-20400): Get duration-*-person data properly with aliases. - int32_t subtypeLen = static_cast(uprv_strlen(unitSubType.data())); - if (subtypeLen > 7 && uprv_strcmp(unitSubType.data() + subtypeLen - 7, "-person") == 0) { - subKey.append({unitSubType.data(), subtypeLen - 7}, status); - } else { - subKey.append({unitSubType.data(), subtypeLen}, status); - } - - if (width != UNUM_UNIT_WIDTH_FULL_NAME) { - UErrorCode localStatus = status; - CharString genderKey; - genderKey.append("units", localStatus); - genderKey.append(subKey, localStatus); - genderKey.append("/gender", localStatus); - StackUResourceBundle fillIn; - ures_getByKeyWithFallback(unitsBundle.getAlias(), genderKey.data(), fillIn.getAlias(), - &localStatus); - outArray[GENDER_INDEX] = ures_getUnicodeString(fillIn.getAlias(), &localStatus); - } - - CharString key; - key.append("units", status); - if (width == UNUM_UNIT_WIDTH_NARROW) { - key.append("Narrow", status); - } else if (width == UNUM_UNIT_WIDTH_SHORT) { - key.append("Short", status); - } - key.append(subKey, status); - - // Grab desired case first, if available. Then grab no-case data to fill in - // the gaps. - if (width == UNUM_UNIT_WIDTH_FULL_NAME && unitDisplayCase[0] != 0) { - CharString caseKey; - caseKey.append(key, status); - caseKey.append("/case/", status); - caseKey.append(unitDisplayCase, status); - - UErrorCode localStatus = U_ZERO_ERROR; - // TODO(icu-units#138): our fallback logic is not spec-compliant: - // lateral fallback should happen before locale fallback. Switch to - // getInflectedMeasureData after homogenizing data format? Find a unit - // test case that demonstrates the incorrect fallback logic (via - // regional variant of an inflected language?) - ures_getAllChildrenWithFallback(unitsBundle.getAlias(), caseKey.data(), sink, localStatus); - } - - // TODO(icu-units#138): our fallback logic is not spec-compliant: we - // check the given case, then go straight to the no-case data. The spec - // states we should first look for case="nominative". As part of #138, - // either get the spec changed, or add unit tests that warn us if - // case="nominative" data differs from no-case data? - UErrorCode localStatus = U_ZERO_ERROR; - ures_getAllChildrenWithFallback(unitsBundle.getAlias(), key.data(), sink, localStatus); - if (width == UNUM_UNIT_WIDTH_SHORT) { - if (U_FAILURE(localStatus)) { - status = localStatus; - } - return; - } -} - -// NOTE: outArray MUST have a length of at least ARRAY_LENGTH. -void getCurrencyLongNameData(const Locale &locale, const CurrencyUnit ¤cy, UnicodeString *outArray, - UErrorCode &status) { - // In ICU4J, this method gets a CurrencyData from CurrencyData.provider. - // TODO(ICU4J): Implement this without going through CurrencyData, like in ICU4C? - PluralTableSink sink(outArray); - LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_CURR, locale.getName(), &status)); - if (U_FAILURE(status)) { return; } - ures_getAllChildrenWithFallback(unitsBundle.getAlias(), "CurrencyUnitPatterns", sink, status); - if (U_FAILURE(status)) { return; } - for (int32_t i = 0; i < StandardPlural::Form::COUNT; i++) { - UnicodeString &pattern = outArray[i]; - if (pattern.isBogus()) { - continue; - } - int32_t longNameLen = 0; - const char16_t *longName = ucurr_getPluralName( - currency.getISOCurrency(), - locale.getName(), - nullptr /* isChoiceFormat */, - StandardPlural::getKeyword(static_cast(i)), - &longNameLen, - &status); - // Example pattern from data: "{0} {1}" - // Example output after find-and-replace: "{0} US dollars" - pattern.findAndReplace(UnicodeString(u"{1}"), UnicodeString(longName, longNameLen)); - } -} - -UnicodeString getCompoundValue(StringPiece compoundKey, - const Locale &locale, - const UNumberUnitWidth &width, - UErrorCode &status) { - LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, locale.getName(), &status)); - if (U_FAILURE(status)) { return {}; } - CharString key; - key.append("units", status); - if (width == UNUM_UNIT_WIDTH_NARROW) { - key.append("Narrow", status); - } else if (width == UNUM_UNIT_WIDTH_SHORT) { - key.append("Short", status); - } - key.append("/compound/", status); - key.append(compoundKey, status); - - UErrorCode localStatus = status; - int32_t len = 0; - const UChar *ptr = - ures_getStringByKeyWithFallback(unitsBundle.getAlias(), key.data(), &len, &localStatus); - if (U_FAILURE(localStatus) && width != UNUM_UNIT_WIDTH_SHORT) { - // Fall back to short, which contains more compound data - key.clear(); - key.append("unitsShort/compound/", status); - key.append(compoundKey, status); - ptr = ures_getStringByKeyWithFallback(unitsBundle.getAlias(), key.data(), &len, &status); - } else { - status = localStatus; - } - if (U_FAILURE(status)) { - return {}; - } - return UnicodeString(ptr, len); -} - -/** - * Loads and applies deriveComponent rules from CLDR's grammaticalFeatures.xml. - * - * Consider a deriveComponent rule that looks like this: - * - * - * - * Instantiating an instance as follows: - * - * DerivedComponents d(loc, "case", "per"); - * - * Applying the rule in the XML element above, `d.value0("foo")` will be "foo", - * and `d.value1("foo")` will be "nominative". - * - * The values returned by value0(...) and value1(...) are valid only while the - * instance exists. In case of any kind of failure, value0(...) and value1(...) - * will return "". - */ -class DerivedComponents { - public: - /** - * Constructor. - * - * The feature and structure parameters must be null-terminated. The string - * referenced by compoundValue must exist for longer than the - * DerivedComponents instance. - */ - DerivedComponents(const Locale &locale, const char *feature, const char *structure) { - StackUResourceBundle derivationsBundle, stackBundle; - ures_openDirectFillIn(derivationsBundle.getAlias(), NULL, "grammaticalFeatures", &status); - ures_getByKey(derivationsBundle.getAlias(), "grammaticalData", derivationsBundle.getAlias(), - &status); - ures_getByKey(derivationsBundle.getAlias(), "derivations", derivationsBundle.getAlias(), - &status); - if (U_FAILURE(status)) { - return; - } - UErrorCode localStatus = U_ZERO_ERROR; - // TODO(icu-units#28): use standard normal locale resolution algorithms - // rather than just grabbing language: - ures_getByKey(derivationsBundle.getAlias(), locale.getLanguage(), stackBundle.getAlias(), - &localStatus); - // TODO(icu-units#28): - // - code currently assumes if the locale exists, the rules are there - - // instead of falling back to root when the requested rule is missing. - // - investigate ures.h functions, see if one that uses res_findResource() - // might be better (or use res_findResource directly), or maybe help - // improve ures documentation to guide function selection? - if (localStatus == U_MISSING_RESOURCE_ERROR) { - ures_getByKey(derivationsBundle.getAlias(), "root", stackBundle.getAlias(), &status); - } else { - status = localStatus; - } - ures_getByKey(stackBundle.getAlias(), "component", stackBundle.getAlias(), &status); - ures_getByKey(stackBundle.getAlias(), feature, stackBundle.getAlias(), &status); - ures_getByKey(stackBundle.getAlias(), structure, stackBundle.getAlias(), &status); - UnicodeString val0 = ures_getUnicodeStringByIndex(stackBundle.getAlias(), 0, &status); - UnicodeString val1 = ures_getUnicodeStringByIndex(stackBundle.getAlias(), 1, &status); - if (U_SUCCESS(status)) { - if (val0.compare(UnicodeString(u"compound")) == 0) { - compound0_ = true; - } else { - compound0_ = false; - value0_.appendInvariantChars(val0, status); - } - if (val1.compare(UnicodeString(u"compound")) == 0) { - compound1_ = true; - } else { - compound1_ = false; - value1_.appendInvariantChars(val1, status); - } - } - } - - // Returns a StringPiece that is only valid as long as the instance exists. - StringPiece value0(const StringPiece compoundValue) const { - return compound0_ ? compoundValue : value0_.toStringPiece(); - } - - // Returns a StringPiece that is only valid as long as the instance exists. - StringPiece value1(const StringPiece compoundValue) const { - return compound1_ ? compoundValue : value1_.toStringPiece(); - } - - // Returns a char* that is only valid as long as the instance exists. - const char *value0(const char *compoundValue) const { - return compound0_ ? compoundValue : value0_.data(); - } - - // Returns a char* that is only valid as long as the instance exists. - const char *value1(const char *compoundValue) const { - return compound1_ ? compoundValue : value1_.data(); - } - - private: - UErrorCode status = U_ZERO_ERROR; - - // Holds strings referred to by value0 and value1; - bool compound0_ = false, compound1_ = false; - CharString value0_, value1_; -}; - -// TODO(icu-units#28): test somehow? Associate with an ICU ticket for adding -// testsuite support for testing with synthetic data? -/** - * Loads and returns the value in rules that look like these: - * - * - * - * - * Currently a fake example, but spec compliant: - * - * - * NOTE: If U_FAILURE(status), returns an empty string. - */ -UnicodeString -getDeriveCompoundRule(Locale locale, const char *feature, const char *structure, UErrorCode &status) { - StackUResourceBundle derivationsBundle, stackBundle; - ures_openDirectFillIn(derivationsBundle.getAlias(), NULL, "grammaticalFeatures", &status); - ures_getByKey(derivationsBundle.getAlias(), "grammaticalData", derivationsBundle.getAlias(), - &status); - ures_getByKey(derivationsBundle.getAlias(), "derivations", derivationsBundle.getAlias(), &status); - // TODO: use standard normal locale resolution algorithms rather than just grabbing language: - ures_getByKey(derivationsBundle.getAlias(), locale.getLanguage(), stackBundle.getAlias(), &status); - // TODO: - // - code currently assumes if the locale exists, the rules are there - - // instead of falling back to root when the requested rule is missing. - // - investigate ures.h functions, see if one that uses res_findResource() - // might be better (or use res_findResource directly), or maybe help - // improve ures documentation to guide function selection? - if (status == U_MISSING_RESOURCE_ERROR) { - status = U_ZERO_ERROR; - ures_getByKey(derivationsBundle.getAlias(), "root", stackBundle.getAlias(), &status); - } - ures_getByKey(stackBundle.getAlias(), "compound", stackBundle.getAlias(), &status); - ures_getByKey(stackBundle.getAlias(), feature, stackBundle.getAlias(), &status); - UnicodeString uVal = ures_getUnicodeStringByKey(stackBundle.getAlias(), structure, &status); - if (U_FAILURE(status)) { - return {}; - } - U_ASSERT(!uVal.isBogus()); - return uVal; -} - -// Returns the gender string for structures following these rules: -// -// -// -// -// Fake example: -// -// -// data0 and data1 should be pattern arrays (UnicodeString[ARRAY_SIZE]) that -// correspond to value="0" and value="1". -// -// Pass a nullptr to data1 if the structure has no concept of value="1" (e.g. -// "prefix" doesn't). -UnicodeString getDerivedGender(Locale locale, - const char *structure, - UnicodeString *data0, - UnicodeString *data1, - UErrorCode &status) { - UnicodeString val = getDeriveCompoundRule(locale, "gender", structure, status); - if (val.length() == 1) { - switch (val[0]) { - case u'0': - return data0[GENDER_INDEX]; - case u'1': - if (data1 == nullptr) { - return {}; - } - return data1[GENDER_INDEX]; - } - } - return val; -} - -//////////////////////// -/// END DATA LOADING /// -//////////////////////// - -// TODO: promote this somewhere? It's based on patternprops.cpp' trimWhitespace -const UChar *trimSpaceChars(const UChar *s, int32_t &length) { - if (length <= 0 || (!u_isJavaSpaceChar(s[0]) && !u_isJavaSpaceChar(s[length - 1]))) { - return s; - } - int32_t start = 0; - int32_t limit = length; - while (start < limit && u_isJavaSpaceChar(s[start])) { - ++start; - } - if (start < limit) { - // There is non-white space at start; we will not move limit below that, - // so we need not test start 0); // Else it would not be COMPOUND - if (mui.singleUnits[endSlice]->dimensionality < 0) { - // We have a -per- construct - UnicodeString perRule = getDeriveCompoundRule(locale, "gender", "per", status); - if (perRule.length() != 1) { - // Fixed gender for -per- units - return perRule; - } - if (perRule[0] == u'1') { - // Find the start of the denominator. We already know there is one. - while (mui.singleUnits[startSlice]->dimensionality >= 0) { - startSlice++; - } - } else { - // Find the end of the numerator - while (endSlice >= 0 && mui.singleUnits[endSlice]->dimensionality < 0) { - endSlice--; - } - if (endSlice < 0) { - // We have only a denominator, e.g. "per-second". - // TODO(icu-units#28): find out what gender to use in the - // absence of a first value - mentioned in CLDR-14253. - return {}; - } - } - } - if (endSlice > startSlice) { - // We have a -times- construct - UnicodeString timesRule = getDeriveCompoundRule(locale, "gender", "times", status); - if (timesRule.length() != 1) { - // Fixed gender for -times- units - return timesRule; - } - if (timesRule[0] == u'0') { - endSlice = startSlice; - } else { - // We assume timesRule[0] == u'1' - startSlice = endSlice; - } - } - U_ASSERT(startSlice == endSlice); - singleUnitIndex = startSlice; - } else if (mui.complexity == UMEASURE_UNIT_MIXED) { - status = U_INTERNAL_PROGRAM_ERROR; - return {}; - } else { - U_ASSERT(mui.complexity == UMEASURE_UNIT_SINGLE); - U_ASSERT(mui.singleUnits.length() == 1); - } - - // Now we know which singleUnit's gender we want - const SingleUnitImpl *singleUnit = mui.singleUnits[singleUnitIndex]; - // Check for any power-prefix gender override: - if (std::abs(singleUnit->dimensionality) != 1) { - UnicodeString powerRule = getDeriveCompoundRule(locale, "gender", "power", status); - if (powerRule.length() != 1) { - // Fixed gender for -powN- units - return powerRule; - } - // powerRule[0] == u'0'; u'1' not currently in spec. - } - // Check for any SI and binary prefix gender override: - if (std::abs(singleUnit->dimensionality) != 1) { - UnicodeString prefixRule = getDeriveCompoundRule(locale, "gender", "prefix", status); - if (prefixRule.length() != 1) { - // Fixed gender for -powN- units - return prefixRule; - } - // prefixRule[0] == u'0'; u'1' not currently in spec. - } - // Now we've boiled it down to the gender of one simple unit identifier: - return getGenderForBuiltin(locale, MeasureUnit::forIdentifier(singleUnit->getSimpleUnitID(), status), - status); -} - -void maybeCalculateGender(const Locale &locale, - const MeasureUnit &unitRef, - UnicodeString *outArray, - UErrorCode &status) { - if (outArray[GENDER_INDEX].isBogus()) { - UnicodeString meterGender = getGenderForBuiltin(locale, MeasureUnit::getMeter(), status); - if (meterGender.isEmpty()) { - // No gender for meter: assume ungendered language - return; - } - // We have a gendered language, but are lacking gender for unitRef. - outArray[GENDER_INDEX] = calculateGenderForUnit(locale, unitRef, status); - } -} - -} // namespace - -void LongNameHandler::forMeasureUnit(const Locale &loc, - const MeasureUnit &unitRef, - const UNumberUnitWidth &width, - const char *unitDisplayCase, - const PluralRules *rules, - const MicroPropsGenerator *parent, - LongNameHandler *fillIn, - UErrorCode &status) { - // From https://unicode.org/reports/tr35/tr35-general.html#compound-units - - // Points 1 and 2 are mostly handled by MeasureUnit: - // - // 1. If the unitId is empty or invalid, fail - // 2. Put the unitId into normalized order - U_ASSERT(fillIn != nullptr); - - if (uprv_strcmp(unitRef.getType(), "") != 0) { - // Handling built-in units: - // - // 3. Set result to be getValue(unitId with length, pluralCategory, caseVariant) - // - If result is not empty, return it - UnicodeString simpleFormats[ARRAY_LENGTH]; - getMeasureData(loc, unitRef, width, unitDisplayCase, simpleFormats, status); - maybeCalculateGender(loc, unitRef, simpleFormats, status); - if (U_FAILURE(status)) { - return; - } - fillIn->rules = rules; - fillIn->parent = parent; - fillIn->simpleFormatsToModifiers(simpleFormats, - {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD}, status); - if (!simpleFormats[GENDER_INDEX].isBogus()) { - fillIn->gender = getGenderString(simpleFormats[GENDER_INDEX], status); - } - return; - - // TODO(icu-units#145): figure out why this causes a failure in - // format/MeasureFormatTest/TestIndividualPluralFallback and other - // tests, when it should have been an alternative for the lines above: - - // forArbitraryUnit(loc, unitRef, width, unitDisplayCase, fillIn, status); - // fillIn->rules = rules; - // fillIn->parent = parent; - // return; - } else { - // Check if it is a MeasureUnit this constructor handles: this - // constructor does not handle mixed units - U_ASSERT(unitRef.getComplexity(status) != UMEASURE_UNIT_MIXED); - forArbitraryUnit(loc, unitRef, width, unitDisplayCase, fillIn, status); - fillIn->rules = rules; - fillIn->parent = parent; - return; - } -} - -void LongNameHandler::forArbitraryUnit(const Locale &loc, - const MeasureUnit &unitRef, - const UNumberUnitWidth &width, - const char *unitDisplayCase, - LongNameHandler *fillIn, - UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if (fillIn == nullptr) { - status = U_INTERNAL_PROGRAM_ERROR; - return; - } - - // Numbered list items are from the algorithms at - // https://unicode.org/reports/tr35/tr35-general.html#compound-units: - // - // 4. Divide the unitId into numerator (the part before the "-per-") and - // denominator (the part after the "-per-). If both are empty, fail - MeasureUnitImpl unit; - MeasureUnitImpl perUnit; - { - MeasureUnitImpl fullUnit = MeasureUnitImpl::forMeasureUnitMaybeCopy(unitRef, status); - if (U_FAILURE(status)) { - return; - } - for (int32_t i = 0; i < fullUnit.singleUnits.length(); i++) { - SingleUnitImpl *subUnit = fullUnit.singleUnits[i]; - if (subUnit->dimensionality > 0) { - unit.appendSingleUnit(*subUnit, status); - } else { - subUnit->dimensionality *= -1; - perUnit.appendSingleUnit(*subUnit, status); - } - } - } - - // TODO(icu-units#28): check placeholder logic, see if it needs to be - // present here instead of only in processPatternTimes: - // - // 5. Set both globalPlaceholder and globalPlaceholderPosition to be empty - - DerivedComponents derivedPerCases(loc, "case", "per"); - - // 6. numeratorUnitString - UnicodeString numeratorUnitData[ARRAY_LENGTH]; - processPatternTimes(std::move(unit), loc, width, derivedPerCases.value0(unitDisplayCase), - numeratorUnitData, status); - - // 7. denominatorUnitString - UnicodeString denominatorUnitData[ARRAY_LENGTH]; - processPatternTimes(std::move(perUnit), loc, width, derivedPerCases.value1(unitDisplayCase), - denominatorUnitData, status); - - // TODO(icu-units#139): - // - implement DerivedComponents for "plural/times" and "plural/power": - // French has different rules, we'll be producing the wrong results - // currently. (Prove via tests!) - // - implement DerivedComponents for "plural/per", "plural/prefix", - // "case/times", "case/power", and "case/prefix" - although they're - // currently hardcoded. Languages with different rules are surely on the - // way. - // - // Currently we only use "case/per", "plural/times", "case/times", and - // "case/power". - // - // This may have impact on multiSimpleFormatsToModifiers(...) below too? - // These rules are currently (ICU 69) all the same and hard-coded below. - UnicodeString perUnitPattern; - if (!denominatorUnitData[PER_INDEX].isBogus()) { - // If we have no denominator, we obtain the empty string: - perUnitPattern = denominatorUnitData[PER_INDEX]; - } else { - // 8. Set perPattern to be getValue([per], locale, length) - UnicodeString rawPerUnitFormat = getCompoundValue("per", loc, width, status); - // rawPerUnitFormat is something like "{0} per {1}"; we need to substitute in the secondary unit. - SimpleFormatter perPatternFormatter(rawPerUnitFormat, 2, 2, status); - if (U_FAILURE(status)) { - return; - } - // Plural and placeholder handling for 7. denominatorUnitString: - // TODO(icu-units#139): hardcoded: - // - UnicodeString denominatorFormat = - getWithPlural(denominatorUnitData, StandardPlural::Form::ONE, status); - // Some "one" pattern may not contain "{0}". For example in "ar" or "ne" locale. - SimpleFormatter denominatorFormatter(denominatorFormat, 0, 1, status); - if (U_FAILURE(status)) { - return; - } - UnicodeString denominatorPattern = denominatorFormatter.getTextWithNoArguments(); - int32_t trimmedLen = denominatorPattern.length(); - const UChar *trimmed = trimSpaceChars(denominatorPattern.getBuffer(), trimmedLen); - UnicodeString denominatorString(false, trimmed, trimmedLen); - // 9. If the denominatorString is empty, set result to - // [numeratorString], otherwise set result to format(perPattern, - // numeratorString, denominatorString) - // - // TODO(icu-units#28): Why does UnicodeString need to be explicit in the - // following line? - perPatternFormatter.format(UnicodeString(u"{0}"), denominatorString, perUnitPattern, status); - if (U_FAILURE(status)) { - return; - } - } - if (perUnitPattern.length() == 0) { - fillIn->simpleFormatsToModifiers(numeratorUnitData, - {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD}, status); - } else { - fillIn->multiSimpleFormatsToModifiers(numeratorUnitData, perUnitPattern, - {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD}, status); - } - - // Gender - // - // TODO(icu-units#28): find out what gender to use in the absence of a first - // value - e.g. what's the gender of "per-second"? Mentioned in CLDR-14253. - // - // gender/per deriveCompound rules don't say: - // - fillIn->gender = getGenderString( - getDerivedGender(loc, "per", numeratorUnitData, denominatorUnitData, status), status); -} - -void LongNameHandler::processPatternTimes(MeasureUnitImpl &&productUnit, - Locale loc, - const UNumberUnitWidth &width, - const char *caseVariant, - UnicodeString *outArray, - UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if (productUnit.complexity == UMEASURE_UNIT_MIXED) { - // These are handled by MixedUnitLongNameHandler - status = U_UNSUPPORTED_ERROR; - return; - } - -#if U_DEBUG - for (int32_t pluralIndex = 0; pluralIndex < ARRAY_LENGTH; pluralIndex++) { - U_ASSERT(outArray[pluralIndex].length() == 0); - U_ASSERT(!outArray[pluralIndex].isBogus()); - } -#endif - - if (productUnit.identifier.isEmpty()) { - // TODO(icu-units#28): consider when serialize should be called. - // identifier might also be empty for MeasureUnit(). - productUnit.serialize(status); - } - if (U_FAILURE(status)) { - return; - } - if (productUnit.identifier.length() == 0) { - // MeasureUnit(): no units: return empty strings. - return; - } - - MeasureUnit builtinUnit; - if (MeasureUnit::findBySubType(productUnit.identifier.toStringPiece(), &builtinUnit)) { - // TODO(icu-units#145): spec doesn't cover builtin-per-builtin, it - // breaks them all down. Do we want to drop this? - // - findBySubType isn't super efficient, if we skip it and go to basic - // singles, we don't have to construct MeasureUnit's anymore. - // - Check all the existing unit tests that fail without this: is it due - // to incorrect fallback via getMeasureData? - // - Do those unit tests cover this code path representatively? - if (builtinUnit != MeasureUnit()) { - getMeasureData(loc, builtinUnit, width, caseVariant, outArray, status); - maybeCalculateGender(loc, builtinUnit, outArray, status); - } - return; - } - - // 2. Set timesPattern to be getValue(times, locale, length) - UnicodeString timesPattern = getCompoundValue("times", loc, width, status); - SimpleFormatter timesPatternFormatter(timesPattern, 2, 2, status); - if (U_FAILURE(status)) { - return; - } - - PlaceholderPosition globalPlaceholder[ARRAY_LENGTH]; - UChar globalJoinerChar = 0; - // Numbered list items are from the algorithms at - // https://unicode.org/reports/tr35/tr35-general.html#compound-units: - // - // pattern(...) point 5: - // - Set both globalPlaceholder and globalPlaceholderPosition to be empty - // - // 3. Set result to be empty - for (int32_t pluralIndex = 0; pluralIndex < ARRAY_LENGTH; pluralIndex++) { - // Initial state: empty string pattern, via all falling back to OTHER: - if (pluralIndex == StandardPlural::Form::OTHER) { - outArray[pluralIndex].remove(); - } else { - outArray[pluralIndex].setToBogus(); - } - globalPlaceholder[pluralIndex] = PH_EMPTY; - } - - // Empty string represents "compound" (propagate the plural form). - const char *pluralCategory = ""; - DerivedComponents derivedTimesPlurals(loc, "plural", "times"); - DerivedComponents derivedTimesCases(loc, "case", "times"); - DerivedComponents derivedPowerCases(loc, "case", "power"); - - // 4. For each single_unit in product_unit - for (int32_t singleUnitIndex = 0; singleUnitIndex < productUnit.singleUnits.length(); - singleUnitIndex++) { - SingleUnitImpl *singleUnit = productUnit.singleUnits[singleUnitIndex]; - const char *singlePluralCategory; - const char *singleCaseVariant; - // TODO(icu-units#28): ensure we have unit tests that change/fail if we - // assign incorrect case variants here: - if (singleUnitIndex < productUnit.singleUnits.length() - 1) { - // 4.1. If hasMultiple - singlePluralCategory = derivedTimesPlurals.value0(pluralCategory); - singleCaseVariant = derivedTimesCases.value0(caseVariant); - pluralCategory = derivedTimesPlurals.value1(pluralCategory); - caseVariant = derivedTimesCases.value1(caseVariant); - } else { - singlePluralCategory = derivedTimesPlurals.value1(pluralCategory); - singleCaseVariant = derivedTimesCases.value1(caseVariant); - } - - // 4.2. Get the gender of that single_unit - MeasureUnit simpleUnit; - if (!MeasureUnit::findBySubType(singleUnit->getSimpleUnitID(), &simpleUnit)) { - // Ideally all simple units should be known, but they're not: - // 100-kilometer is internally treated as a simple unit, but it is - // not a built-in unit and does not have formatting data in CLDR 39. - // - // TODO(icu-units#28): test (desirable) invariants in unit tests. - status = U_UNSUPPORTED_ERROR; - return; - } - const char *gender = getGenderString(getGenderForBuiltin(loc, simpleUnit, status), status); - - // 4.3. If singleUnit starts with a dimensionality_prefix, such as 'square-' - U_ASSERT(singleUnit->dimensionality > 0); - int32_t dimensionality = singleUnit->dimensionality; - UnicodeString dimensionalityPrefixPatterns[ARRAY_LENGTH]; - if (dimensionality != 1) { - // 4.3.1. set dimensionalityPrefixPattern to be - // getValue(that dimensionality_prefix, locale, length, singlePluralCategory, singleCaseVariant, gender), - // such as "{0} kwadratowym" - CharString dimensionalityKey("compound/power", status); - dimensionalityKey.appendNumber(dimensionality, status); - getInflectedMeasureData(dimensionalityKey.toStringPiece(), loc, width, gender, - singleCaseVariant, dimensionalityPrefixPatterns, status); - if (U_FAILURE(status)) { - // At the time of writing, only pow2 and pow3 are supported. - // Attempting to format other powers results in a - // U_RESOURCE_TYPE_MISMATCH. We convert the error if we - // understand it: - if (status == U_RESOURCE_TYPE_MISMATCH && dimensionality > 3) { - status = U_UNSUPPORTED_ERROR; - } - return; - } - - // TODO(icu-units#139): - // 4.3.2. set singlePluralCategory to be power0(singlePluralCategory) - - // 4.3.3. set singleCaseVariant to be power0(singleCaseVariant) - singleCaseVariant = derivedPowerCases.value0(singleCaseVariant); - // 4.3.4. remove the dimensionality_prefix from singleUnit - singleUnit->dimensionality = 1; - } - - // 4.4. if singleUnit starts with an si_prefix, such as 'centi' - UMeasurePrefix prefix = singleUnit->unitPrefix; - UnicodeString prefixPattern; - if (prefix != UMEASURE_PREFIX_ONE) { - // 4.4.1. set siPrefixPattern to be getValue(that si_prefix, locale, - // length), such as "centy{0}" - CharString prefixKey; - // prefixKey looks like "1024p3" or "10p-2": - prefixKey.appendNumber(umeas_getPrefixBase(prefix), status); - prefixKey.append('p', status); - prefixKey.appendNumber(umeas_getPrefixPower(prefix), status); - // Contains a pattern like "centy{0}". - prefixPattern = getCompoundValue(prefixKey.toStringPiece(), loc, width, status); - - // 4.4.2. set singlePluralCategory to be prefix0(singlePluralCategory) - // - // TODO(icu-units#139): that refers to these rules: - // - // though I'm not sure what other value they might end up having. - // - // 4.4.3. set singleCaseVariant to be prefix0(singleCaseVariant) - // - // TODO(icu-units#139): that refers to: - // but the prefix (value0) doesn't have case, the rest simply - // propagates. - - // 4.4.4. remove the si_prefix from singleUnit - singleUnit->unitPrefix = UMEASURE_PREFIX_ONE; - } - - // 4.5. Set corePattern to be the getValue(singleUnit, locale, length, - // singlePluralCategory, singleCaseVariant), such as "{0} metrem" - UnicodeString singleUnitArray[ARRAY_LENGTH]; - // At this point we are left with a Simple Unit: - U_ASSERT(uprv_strcmp(singleUnit->build(status).getIdentifier(), singleUnit->getSimpleUnitID()) == - 0); - getMeasureData(loc, singleUnit->build(status), width, singleCaseVariant, singleUnitArray, - status); - if (U_FAILURE(status)) { - // Shouldn't happen if we have data for all single units - return; - } - - // Calculate output gender - if (!singleUnitArray[GENDER_INDEX].isBogus()) { - U_ASSERT(!singleUnitArray[GENDER_INDEX].isEmpty()); - UnicodeString uVal; - - if (prefix != UMEASURE_PREFIX_ONE) { - singleUnitArray[GENDER_INDEX] = - getDerivedGender(loc, "prefix", singleUnitArray, nullptr, status); - } - - if (dimensionality != 1) { - singleUnitArray[GENDER_INDEX] = - getDerivedGender(loc, "power", singleUnitArray, nullptr, status); - } - - UnicodeString timesGenderRule = getDeriveCompoundRule(loc, "gender", "times", status); - if (timesGenderRule.length() == 1) { - switch (timesGenderRule[0]) { - case u'0': - if (singleUnitIndex == 0) { - U_ASSERT(outArray[GENDER_INDEX].isBogus()); - outArray[GENDER_INDEX] = singleUnitArray[GENDER_INDEX]; - } - break; - case u'1': - if (singleUnitIndex == productUnit.singleUnits.length() - 1) { - U_ASSERT(outArray[GENDER_INDEX].isBogus()); - outArray[GENDER_INDEX] = singleUnitArray[GENDER_INDEX]; - } - } - } else { - if (outArray[GENDER_INDEX].isBogus()) { - outArray[GENDER_INDEX] = timesGenderRule; - } - } - } - - // Calculate resulting patterns for each plural form - for (int32_t pluralIndex = 0; pluralIndex < StandardPlural::Form::COUNT; pluralIndex++) { - StandardPlural::Form plural = static_cast(pluralIndex); - - // singleUnitArray[pluralIndex] looks something like "{0} Meter" - if (outArray[pluralIndex].isBogus()) { - if (singleUnitArray[pluralIndex].isBogus()) { - // Let the usual plural fallback mechanism take care of this - // plural form - continue; - } else { - // Since our singleUnit can have a plural form that outArray - // doesn't yet have (relying on fallback to OTHER), we start - // by grabbing it with the normal plural fallback mechanism - outArray[pluralIndex] = getWithPlural(outArray, plural, status); - if (U_FAILURE(status)) { - return; - } - } - } - - if (uprv_strcmp(singlePluralCategory, "") != 0) { - plural = static_cast(getIndex(singlePluralCategory, status)); - } - - // 4.6. Extract(corePattern, coreUnit, placeholder, placeholderPosition) from that pattern. - UnicodeString coreUnit; - PlaceholderPosition placeholderPosition; - UChar joinerChar; - extractCorePattern(getWithPlural(singleUnitArray, plural, status), coreUnit, - placeholderPosition, joinerChar); - - // 4.7 If the position is middle, then fail - if (placeholderPosition == PH_MIDDLE) { - status = U_UNSUPPORTED_ERROR; - return; - } - - // 4.8. If globalPlaceholder is empty - if (globalPlaceholder[pluralIndex] == PH_EMPTY) { - globalPlaceholder[pluralIndex] = placeholderPosition; - globalJoinerChar = joinerChar; - } else { - // Expect all units involved to have the same placeholder position - U_ASSERT(globalPlaceholder[pluralIndex] == placeholderPosition); - // TODO(icu-units#28): Do we want to add a unit test that checks - // for consistent joiner chars? Probably not, given how - // inconsistent they are. File a CLDR ticket with examples? - } - // Now coreUnit would be just "Meter" - - // 4.9. If siPrefixPattern is not empty - if (prefix != UMEASURE_PREFIX_ONE) { - SimpleFormatter prefixCompiled(prefixPattern, 1, 1, status); - if (U_FAILURE(status)) { - return; - } - - // 4.9.1. Set coreUnit to be the combineLowercasing(locale, length, siPrefixPattern, - // coreUnit) - UnicodeString tmp; - // combineLowercasing(locale, length, prefixPattern, coreUnit) - // - // TODO(icu-units#28): run this only if prefixPattern does not - // contain space characters - do languages "as", "bn", "hi", - // "kk", etc have concepts of upper and lower case?: - if (width == UNUM_UNIT_WIDTH_FULL_NAME) { - coreUnit.toLower(loc); - } - prefixCompiled.format(coreUnit, tmp, status); - if (U_FAILURE(status)) { - return; - } - coreUnit = tmp; - } - - // 4.10. If dimensionalityPrefixPattern is not empty - if (dimensionality != 1) { - SimpleFormatter dimensionalityCompiled( - getWithPlural(dimensionalityPrefixPatterns, plural, status), 1, 1, status); - if (U_FAILURE(status)) { - return; - } - - // 4.10.1. Set coreUnit to be the combineLowercasing(locale, length, - // dimensionalityPrefixPattern, coreUnit) - UnicodeString tmp; - // combineLowercasing(locale, length, prefixPattern, coreUnit) - // - // TODO(icu-units#28): run this only if prefixPattern does not - // contain space characters - do languages "as", "bn", "hi", - // "kk", etc have concepts of upper and lower case?: - if (width == UNUM_UNIT_WIDTH_FULL_NAME) { - coreUnit.toLower(loc); - } - dimensionalityCompiled.format(coreUnit, tmp, status); - if (U_FAILURE(status)) { - return; - } - coreUnit = tmp; - } - - if (outArray[pluralIndex].length() == 0) { - // 4.11. If the result is empty, set result to be coreUnit - outArray[pluralIndex] = coreUnit; - } else { - // 4.12. Otherwise set result to be format(timesPattern, result, coreUnit) - UnicodeString tmp; - timesPatternFormatter.format(outArray[pluralIndex], coreUnit, tmp, status); - outArray[pluralIndex] = tmp; - } - } - } - for (int32_t pluralIndex = 0; pluralIndex < StandardPlural::Form::COUNT; pluralIndex++) { - if (globalPlaceholder[pluralIndex] == PH_BEGINNING) { - UnicodeString tmp; - tmp.append(u"{0}", 3); - if (globalJoinerChar != 0) { - tmp.append(globalJoinerChar); - } - tmp.append(outArray[pluralIndex]); - outArray[pluralIndex] = tmp; - } else if (globalPlaceholder[pluralIndex] == PH_END) { - if (globalJoinerChar != 0) { - outArray[pluralIndex].append(globalJoinerChar); - } - outArray[pluralIndex].append(u"{0}", 3); - } - } -} - -UnicodeString LongNameHandler::getUnitDisplayName( - const Locale& loc, - const MeasureUnit& unit, - UNumberUnitWidth width, - UErrorCode& status) { - if (U_FAILURE(status)) { - return ICU_Utility::makeBogusString(); - } - UnicodeString simpleFormats[ARRAY_LENGTH]; - getMeasureData(loc, unit, width, "", simpleFormats, status); - return simpleFormats[DNAM_INDEX]; -} - -UnicodeString LongNameHandler::getUnitPattern( - const Locale& loc, - const MeasureUnit& unit, - UNumberUnitWidth width, - StandardPlural::Form pluralForm, - UErrorCode& status) { - if (U_FAILURE(status)) { - return ICU_Utility::makeBogusString(); - } - UnicodeString simpleFormats[ARRAY_LENGTH]; - getMeasureData(loc, unit, width, "", simpleFormats, status); - // The above already handles fallback from other widths to short - if (U_FAILURE(status)) { - return ICU_Utility::makeBogusString(); - } - // Now handle fallback from other plural forms to OTHER - return (!(simpleFormats[pluralForm]).isBogus())? simpleFormats[pluralForm]: - simpleFormats[StandardPlural::Form::OTHER]; -} - -LongNameHandler* LongNameHandler::forCurrencyLongNames(const Locale &loc, const CurrencyUnit ¤cy, - const PluralRules *rules, - const MicroPropsGenerator *parent, - UErrorCode &status) { - auto* result = new LongNameHandler(rules, parent); - if (result == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - UnicodeString simpleFormats[ARRAY_LENGTH]; - getCurrencyLongNameData(loc, currency, simpleFormats, status); - if (U_FAILURE(status)) { return nullptr; } - result->simpleFormatsToModifiers(simpleFormats, {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}, status); - // TODO(icu-units#28): currency gender? - return result; -} - -void LongNameHandler::simpleFormatsToModifiers(const UnicodeString *simpleFormats, Field field, - UErrorCode &status) { - for (int32_t i = 0; i < StandardPlural::Form::COUNT; i++) { - StandardPlural::Form plural = static_cast(i); - UnicodeString simpleFormat = getWithPlural(simpleFormats, plural, status); - if (U_FAILURE(status)) { return; } - SimpleFormatter compiledFormatter(simpleFormat, 0, 1, status); - if (U_FAILURE(status)) { return; } - fModifiers[i] = SimpleModifier(compiledFormatter, field, false, {this, SIGNUM_POS_ZERO, plural}); - } -} - -void LongNameHandler::multiSimpleFormatsToModifiers(const UnicodeString *leadFormats, UnicodeString trailFormat, - Field field, UErrorCode &status) { - SimpleFormatter trailCompiled(trailFormat, 1, 1, status); - if (U_FAILURE(status)) { return; } - for (int32_t i = 0; i < StandardPlural::Form::COUNT; i++) { - StandardPlural::Form plural = static_cast(i); - UnicodeString leadFormat = getWithPlural(leadFormats, plural, status); - if (U_FAILURE(status)) { return; } - UnicodeString compoundFormat; - if (leadFormat.length() == 0) { - compoundFormat = trailFormat; - } else { - trailCompiled.format(leadFormat, compoundFormat, status); - if (U_FAILURE(status)) { return; } - } - SimpleFormatter compoundCompiled(compoundFormat, 0, 1, status); - if (U_FAILURE(status)) { return; } - fModifiers[i] = SimpleModifier(compoundCompiled, field, false, {this, SIGNUM_POS_ZERO, plural}); - } -} - -void LongNameHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const { - if (parent != NULL) { - parent->processQuantity(quantity, micros, status); - } - StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, rules, quantity, status); - micros.modOuter = &fModifiers[pluralForm]; - micros.gender = gender; -} - -const Modifier* LongNameHandler::getModifier(Signum /*signum*/, StandardPlural::Form plural) const { - return &fModifiers[plural]; -} - -void MixedUnitLongNameHandler::forMeasureUnit(const Locale &loc, - const MeasureUnit &mixedUnit, - const UNumberUnitWidth &width, - const char *unitDisplayCase, - const PluralRules *rules, - const MicroPropsGenerator *parent, - MixedUnitLongNameHandler *fillIn, - UErrorCode &status) { - U_ASSERT(mixedUnit.getComplexity(status) == UMEASURE_UNIT_MIXED); - U_ASSERT(fillIn != nullptr); - if (U_FAILURE(status)) { - return; - } - - MeasureUnitImpl temp; - const MeasureUnitImpl &impl = MeasureUnitImpl::forMeasureUnit(mixedUnit, temp, status); - // Defensive, for production code: - if (impl.complexity != UMEASURE_UNIT_MIXED) { - // Should be using the normal LongNameHandler - status = U_UNSUPPORTED_ERROR; - return; - } - - fillIn->fMixedUnitCount = impl.singleUnits.length(); - fillIn->fMixedUnitData.adoptInstead(new UnicodeString[fillIn->fMixedUnitCount * ARRAY_LENGTH]); - for (int32_t i = 0; i < fillIn->fMixedUnitCount; i++) { - // Grab data for each of the components. - UnicodeString *unitData = &fillIn->fMixedUnitData[i * ARRAY_LENGTH]; - // TODO(CLDR-14582): check from the CLDR-14582 ticket whether this - // propagation of unitDisplayCase is correct: - getMeasureData(loc, impl.singleUnits[i]->build(status), width, unitDisplayCase, unitData, - status); - // TODO(ICU-21494): if we add support for gender for mixed units, we may - // need maybeCalculateGender() here. - } - - // TODO(icu-units#120): Make sure ICU doesn't output zero-valued - // high-magnitude fields - // * for mixed units count N, produce N listFormatters, one for each subset - // that might be formatted. - UListFormatterWidth listWidth = ULISTFMT_WIDTH_SHORT; - if (width == UNUM_UNIT_WIDTH_NARROW) { - listWidth = ULISTFMT_WIDTH_NARROW; - } else if (width == UNUM_UNIT_WIDTH_FULL_NAME) { - // This might be the same as SHORT in most languages: - listWidth = ULISTFMT_WIDTH_WIDE; - } - fillIn->fListFormatter.adoptInsteadAndCheckErrorCode( - ListFormatter::createInstance(loc, ULISTFMT_TYPE_UNITS, listWidth, status), status); - // TODO(ICU-21494): grab gender of each unit, calculate the gender - // associated with this list formatter, save it for later. - fillIn->rules = rules; - fillIn->parent = parent; - - // We need a localised NumberFormatter for the numbers of the bigger units - // (providing Arabic numerals, for example). - fillIn->fNumberFormatter = NumberFormatter::withLocale(loc); -} - -void MixedUnitLongNameHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const { - U_ASSERT(fMixedUnitCount > 1); - if (parent != nullptr) { - parent->processQuantity(quantity, micros, status); - } - micros.modOuter = getMixedUnitModifier(quantity, micros, status); -} - -const Modifier *MixedUnitLongNameHandler::getMixedUnitModifier(DecimalQuantity &quantity, - MicroProps µs, - UErrorCode &status) const { - if (micros.mixedMeasuresCount == 0) { - U_ASSERT(micros.mixedMeasuresCount > 0); // Mixed unit: we must have more than one unit value - status = U_UNSUPPORTED_ERROR; - return µs.helpers.emptyWeakModifier; - } - - // Algorithm: - // - // For the mixed-units measurement of: "3 yard, 1 foot, 2.6 inch", we should - // find "3 yard" and "1 foot" in micros.mixedMeasures. - // - // Obtain long-names with plural forms corresponding to measure values: - // * {0} yards, {0} foot, {0} inches - // - // Format the integer values appropriately and modify with the format - // strings: - // - 3 yards, 1 foot - // - // Use ListFormatter to combine, with one placeholder: - // - 3 yards, 1 foot and {0} inches - // - // Return a SimpleModifier for this pattern, letting the rest of the - // pipeline take care of the remaining inches. - - LocalArray outputMeasuresList(new UnicodeString[fMixedUnitCount], status); - if (U_FAILURE(status)) { - return µs.helpers.emptyWeakModifier; - } - - StandardPlural::Form quantityPlural = StandardPlural::Form::OTHER; - for (int32_t i = 0; i < micros.mixedMeasuresCount; i++) { - DecimalQuantity fdec; - - // If numbers are negative, only the first number needs to have its - // negative sign formatted. - int64_t number = i > 0 ? std::abs(micros.mixedMeasures[i]) : micros.mixedMeasures[i]; - - if (micros.indexOfQuantity == i) { // Insert placeholder for `quantity` - // If quantity is not the first value and quantity is negative - if (micros.indexOfQuantity > 0 && quantity.isNegative()) { - quantity.negate(); - } - - StandardPlural::Form quantityPlural = - utils::getPluralSafe(micros.rounder, rules, quantity, status); - UnicodeString quantityFormatWithPlural = - getWithPlural(&fMixedUnitData[i * ARRAY_LENGTH], quantityPlural, status); - SimpleFormatter quantityFormatter(quantityFormatWithPlural, 0, 1, status); - quantityFormatter.format(UnicodeString(u"{0}"), outputMeasuresList[i], status); - } else { - fdec.setToLong(number); - StandardPlural::Form pluralForm = utils::getStandardPlural(rules, fdec); - UnicodeString simpleFormat = - getWithPlural(&fMixedUnitData[i * ARRAY_LENGTH], pluralForm, status); - SimpleFormatter compiledFormatter(simpleFormat, 0, 1, status); - UnicodeString num; - auto appendable = UnicodeStringAppendable(num); - - fNumberFormatter.formatDecimalQuantity(fdec, status).appendTo(appendable, status); - compiledFormatter.format(num, outputMeasuresList[i], status); - } - } - - // TODO(ICU-21494): implement gender for lists of mixed units. Presumably we - // can set micros.gender to the gender associated with the list formatter in - // use below (once we have correct support for that). And then document this - // appropriately? "getMixedUnitModifier" doesn't sound like it would do - // something like this. - - // Combine list into a "premixed" pattern - UnicodeString premixedFormatPattern; - fListFormatter->format(outputMeasuresList.getAlias(), fMixedUnitCount, premixedFormatPattern, - status); - SimpleFormatter premixedCompiled(premixedFormatPattern, 0, 1, status); - if (U_FAILURE(status)) { - return µs.helpers.emptyWeakModifier; - } - - micros.helpers.mixedUnitModifier = - SimpleModifier(premixedCompiled, kUndefinedField, false, {this, SIGNUM_POS_ZERO, quantityPlural}); - return µs.helpers.mixedUnitModifier; -} - -const Modifier *MixedUnitLongNameHandler::getModifier(Signum /*signum*/, - StandardPlural::Form /*plural*/) const { - // TODO(icu-units#28): investigate this method when investigating where - // ModifierStore::getModifier() gets used. To be sure it remains - // unreachable: - UPRV_UNREACHABLE_EXIT; - return nullptr; -} - -LongNameMultiplexer *LongNameMultiplexer::forMeasureUnits(const Locale &loc, - const MaybeStackVector &units, - const UNumberUnitWidth &width, - const char *unitDisplayCase, - const PluralRules *rules, - const MicroPropsGenerator *parent, - UErrorCode &status) { - LocalPointer result(new LongNameMultiplexer(parent), status); - if (U_FAILURE(status)) { - return nullptr; - } - U_ASSERT(units.length() > 0); - if (result->fHandlers.resize(units.length()) == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - result->fMeasureUnits.adoptInstead(new MeasureUnit[units.length()]); - for (int32_t i = 0, length = units.length(); i < length; i++) { - const MeasureUnit &unit = *units[i]; - result->fMeasureUnits[i] = unit; - if (unit.getComplexity(status) == UMEASURE_UNIT_MIXED) { - MixedUnitLongNameHandler *mlnh = result->fMixedUnitHandlers.createAndCheckErrorCode(status); - MixedUnitLongNameHandler::forMeasureUnit(loc, unit, width, unitDisplayCase, rules, NULL, - mlnh, status); - result->fHandlers[i] = mlnh; - } else { - LongNameHandler *lnh = result->fLongNameHandlers.createAndCheckErrorCode(status); - LongNameHandler::forMeasureUnit(loc, unit, width, unitDisplayCase, rules, NULL, lnh, status); - result->fHandlers[i] = lnh; - } - if (U_FAILURE(status)) { - return nullptr; - } - } - return result.orphan(); -} - -void LongNameMultiplexer::processQuantity(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const { - // We call parent->processQuantity() from the Multiplexer, instead of - // letting LongNameHandler handle it: we don't know which LongNameHandler to - // call until we've called the parent! - fParent->processQuantity(quantity, micros, status); - - // Call the correct LongNameHandler based on outputUnit - for (int i = 0; i < fHandlers.getCapacity(); i++) { - if (fMeasureUnits[i] == micros.outputUnit) { - fHandlers[i]->processQuantity(quantity, micros, status); - return; - } - } - if (U_FAILURE(status)) { - return; - } - // We shouldn't receive any outputUnit for which we haven't already got a - // LongNameHandler: - status = U_INTERNAL_PROGRAM_ERROR; -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include + +#include "unicode/simpleformatter.h" +#include "unicode/ures.h" +#include "ureslocs.h" +#include "charstr.h" +#include "uresimp.h" +#include "measunit_impl.h" +#include "number_longnames.h" +#include "number_microprops.h" +#include +#include "cstring.h" +#include "util.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +namespace { + +/** + * Display Name (this format has no placeholder). + * + * Used as an index into the LongNameHandler::simpleFormats array. Units + * resources cover the normal set of PluralRules keys, as well as `dnam` and + * `per` forms. + */ +constexpr int32_t DNAM_INDEX = StandardPlural::Form::COUNT; +/** + * "per" form (e.g. "{0} per day" is day's "per" form). + * + * Used as an index into the LongNameHandler::simpleFormats array. Units + * resources cover the normal set of PluralRules keys, as well as `dnam` and + * `per` forms. + */ +constexpr int32_t PER_INDEX = StandardPlural::Form::COUNT + 1; +/** + * Gender of the word, in languages with grammatical gender. + */ +constexpr int32_t GENDER_INDEX = StandardPlural::Form::COUNT + 2; +// Number of keys in the array populated by PluralTableSink. +constexpr int32_t ARRAY_LENGTH = StandardPlural::Form::COUNT + 3; + +// TODO(icu-units#28): load this list from resources, after creating a "&set" +// function for use in ldml2icu rules. +const int32_t GENDER_COUNT = 7; +const char *gGenders[GENDER_COUNT] = {"animate", "common", "feminine", "inanimate", + "masculine", "neuter", "personal"}; + +// Converts a UnicodeString to a const char*, either pointing to a string in +// gGenders, or pointing to an empty string if an appropriate string was not +// found. +const char *getGenderString(UnicodeString uGender, UErrorCode status) { + if (uGender.length() == 0) { + return ""; + } + CharString gender; + gender.appendInvariantChars(uGender, status); + if (U_FAILURE(status)) { + return ""; + } + int32_t first = 0; + int32_t last = GENDER_COUNT; + while (first < last) { + int32_t mid = (first + last) / 2; + int32_t cmp = uprv_strcmp(gender.data(), gGenders[mid]); + if (cmp == 0) { + return gGenders[mid]; + } else if (cmp > 0) { + first = mid + 1; + } else if (cmp < 0) { + last = mid; + } + } + // We don't return an error in case our gGenders list is incomplete in + // production. + // + // TODO(icu-units#28): a unit test checking all locales' genders are covered + // by gGenders? Else load a complete list of genders found in + // grammaticalFeatures in an initOnce. + return ""; +} + +// Returns the array index that corresponds to the given pluralKeyword. +static int32_t getIndex(const char* pluralKeyword, UErrorCode& status) { + // pluralKeyword can also be "dnam", "per", or "gender" + switch (*pluralKeyword) { + case 'd': + if (uprv_strcmp(pluralKeyword + 1, "nam") == 0) { + return DNAM_INDEX; + } + break; + case 'g': + if (uprv_strcmp(pluralKeyword + 1, "ender") == 0) { + return GENDER_INDEX; + } + break; + case 'p': + if (uprv_strcmp(pluralKeyword + 1, "er") == 0) { + return PER_INDEX; + } + break; + default: + break; + } + StandardPlural::Form plural = StandardPlural::fromString(pluralKeyword, status); + return plural; +} + +// Selects a string out of the `strings` array which corresponds to the +// specified plural form, with fallback to the OTHER form. +// +// The `strings` array must have ARRAY_LENGTH items: one corresponding to each +// of the plural forms, plus a display name ("dnam") and a "per" form. +static UnicodeString getWithPlural( + const UnicodeString* strings, + StandardPlural::Form plural, + UErrorCode& status) { + UnicodeString result = strings[plural]; + if (result.isBogus()) { + result = strings[StandardPlural::Form::OTHER]; + } + if (result.isBogus()) { + // There should always be data in the "other" plural variant. + status = U_INTERNAL_PROGRAM_ERROR; + } + return result; +} + +enum PlaceholderPosition { PH_EMPTY, PH_NONE, PH_BEGINNING, PH_MIDDLE, PH_END }; + +/** + * Returns three outputs extracted from pattern. + * + * @param coreUnit is extracted as per Extract(...) in the spec: + * https://unicode.org/reports/tr35/tr35-general.html#compound-units + * @param PlaceholderPosition indicates where in the string the placeholder was + * found. + * @param joinerChar Iff the placeholder was at the beginning or end, joinerChar + * contains the space character (if any) that separated the placeholder from + * the rest of the pattern. Otherwise, joinerChar is set to NUL. Only one + * space character is considered. + */ +void extractCorePattern(const UnicodeString &pattern, + UnicodeString &coreUnit, + PlaceholderPosition &placeholderPosition, + char16_t &joinerChar) { + joinerChar = 0; + int32_t len = pattern.length(); + if (pattern.startsWith(u"{0}", 3)) { + placeholderPosition = PH_BEGINNING; + if (u_isJavaSpaceChar(pattern[3])) { + joinerChar = pattern[3]; + coreUnit.setTo(pattern, 4, len - 4); + } else { + coreUnit.setTo(pattern, 3, len - 3); + } + } else if (pattern.endsWith(u"{0}", 3)) { + placeholderPosition = PH_END; + if (u_isJavaSpaceChar(pattern[len - 4])) { + coreUnit.setTo(pattern, 0, len - 4); + joinerChar = pattern[len - 4]; + } else { + coreUnit.setTo(pattern, 0, len - 3); + } + } else if (pattern.indexOf(u"{0}", 3, 1, len - 2) == -1) { + placeholderPosition = PH_NONE; + coreUnit = pattern; + } else { + placeholderPosition = PH_MIDDLE; + coreUnit = pattern; + } +} + +////////////////////////// +/// BEGIN DATA LOADING /// +////////////////////////// + +// Gets the gender of a built-in unit: unit must be a built-in. Returns an empty +// string both in case of unknown gender and in case of unknown unit. +UnicodeString +getGenderForBuiltin(const Locale &locale, const MeasureUnit &builtinUnit, UErrorCode &status) { + LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, locale.getName(), &status)); + if (U_FAILURE(status)) { return {}; } + + // Map duration-year-person, duration-week-person, etc. to duration-year, duration-week, ... + // TODO(ICU-20400): Get duration-*-person data properly with aliases. + StringPiece subtypeForResource; + int32_t subtypeLen = static_cast(uprv_strlen(builtinUnit.getSubtype())); + if (subtypeLen > 7 && uprv_strcmp(builtinUnit.getSubtype() + subtypeLen - 7, "-person") == 0) { + subtypeForResource = {builtinUnit.getSubtype(), subtypeLen - 7}; + } else { + subtypeForResource = builtinUnit.getSubtype(); + } + + CharString key; + key.append("units/", status); + key.append(builtinUnit.getType(), status); + key.append("/", status); + key.append(subtypeForResource, status); + key.append("/gender", status); + + UErrorCode localStatus = status; + int32_t resultLen = 0; + const char16_t *result = + ures_getStringByKeyWithFallback(unitsBundle.getAlias(), key.data(), &resultLen, &localStatus); + if (U_SUCCESS(localStatus)) { + status = localStatus; + return UnicodeString(true, result, resultLen); + } else { + // TODO(icu-units#28): "$unitRes/gender" does not exist. Do we want to + // check whether the parent "$unitRes" exists? Then we could return + // U_MISSING_RESOURCE_ERROR for incorrect usage (e.g. builtinUnit not + // being a builtin). + return {}; + } +} + +// Loads data from a resource tree with paths matching +// $key/$pluralForm/$gender/$case, with lateral inheritance for missing cases +// and genders. +// +// An InflectedPluralSink is configured to load data for a specific gender and +// case. It loads all plural forms, because selection between plural forms is +// dependent upon the value being formatted. +// +// See data/unit/de.txt and data/unit/fr.txt for examples - take a look at +// units/compound/power2: German has case, French has differences for gender, +// but no case. +// +// TODO(icu-units#138): Conceptually similar to PluralTableSink, however the +// tree structures are different. After homogenizing the structures, we may be +// able to unify the two classes. +// +// TODO: Spec violation: expects presence of "count" - does not fallback to an +// absent "count"! If this fallback were added, getCompoundValue could be +// superseded? +class InflectedPluralSink : public ResourceSink { + public: + // Accepts `char*` rather than StringPiece because + // ResourceTable::findValue(...) requires a null-terminated `char*`. + // + // NOTE: outArray MUST have a length of at least ARRAY_LENGTH. No bounds + // checking is performed. + explicit InflectedPluralSink(const char *gender, const char *caseVariant, UnicodeString *outArray) + : gender(gender), caseVariant(caseVariant), outArray(outArray) { + // Initialize the array to bogus strings. + for (int32_t i = 0; i < ARRAY_LENGTH; i++) { + outArray[i].setToBogus(); + } + } + + // See ResourceSink::put(). + void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override { + int32_t pluralIndex = getIndex(key, status); + if (U_FAILURE(status)) { return; } + if (!outArray[pluralIndex].isBogus()) { + // We already have a pattern + return; + } + ResourceTable genderTable = value.getTable(status); + ResourceTable caseTable; // This instance has to outlive `value` + if (loadForPluralForm(genderTable, caseTable, value, status)) { + outArray[pluralIndex] = value.getUnicodeString(status); + } + } + + private: + // Tries to load data for the configured gender from `genderTable`. Returns + // true if found, returning the data in `value`. The returned data will be + // for the configured gender if found, falling back to "neuter" and + // no-gender if not. The caseTable parameter holds the intermediate + // ResourceTable for the sake of lifetime management. + bool loadForPluralForm(const ResourceTable &genderTable, + ResourceTable &caseTable, + ResourceValue &value, + UErrorCode &status) { + if (uprv_strcmp(gender, "") != 0) { + if (loadForGender(genderTable, gender, caseTable, value, status)) { + return true; + } + if (uprv_strcmp(gender, "neuter") != 0 && + loadForGender(genderTable, "neuter", caseTable, value, status)) { + return true; + } + } + if (loadForGender(genderTable, "_", caseTable, value, status)) { + return true; + } + return false; + } + + // Tries to load data for the given gender from `genderTable`. Returns true + // if found, returning the data in `value`. The returned data will be for + // the configured case if found, falling back to "nominative" and no-case if + // not. + bool loadForGender(const ResourceTable &genderTable, + const char *genderVal, + ResourceTable &caseTable, + ResourceValue &value, + UErrorCode &status) { + if (!genderTable.findValue(genderVal, value)) { + return false; + } + caseTable = value.getTable(status); + if (uprv_strcmp(caseVariant, "") != 0) { + if (loadForCase(caseTable, caseVariant, value)) { + return true; + } + if (uprv_strcmp(caseVariant, "nominative") != 0 && + loadForCase(caseTable, "nominative", value)) { + return true; + } + } + if (loadForCase(caseTable, "_", value)) { + return true; + } + return false; + } + + // Tries to load data for the given case from `caseTable`. Returns true if + // found, returning the data in `value`. + bool loadForCase(const ResourceTable &caseTable, const char *caseValue, ResourceValue &value) { + if (!caseTable.findValue(caseValue, value)) { + return false; + } + return true; + } + + const char *gender; + const char *caseVariant; + UnicodeString *outArray; +}; + +// Fetches localised formatting patterns for the given subKey. See documentation +// for InflectedPluralSink for details. +// +// Data is loaded for the appropriate unit width, with missing data filled in +// from unitsShort. +void getInflectedMeasureData(StringPiece subKey, + const Locale &locale, + const UNumberUnitWidth &width, + const char *gender, + const char *caseVariant, + UnicodeString *outArray, + UErrorCode &status) { + InflectedPluralSink sink(gender, caseVariant, outArray); + LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, locale.getName(), &status)); + if (U_FAILURE(status)) { return; } + + CharString key; + key.append("units", status); + if (width == UNUM_UNIT_WIDTH_NARROW) { + key.append("Narrow", status); + } else if (width == UNUM_UNIT_WIDTH_SHORT) { + key.append("Short", status); + } + key.append("/", status); + key.append(subKey, status); + + UErrorCode localStatus = status; + ures_getAllChildrenWithFallback(unitsBundle.getAlias(), key.data(), sink, localStatus); + if (width == UNUM_UNIT_WIDTH_SHORT) { + status = localStatus; + return; + } +} + +class PluralTableSink : public ResourceSink { + public: + // NOTE: outArray MUST have a length of at least ARRAY_LENGTH. No bounds + // checking is performed. + explicit PluralTableSink(UnicodeString *outArray) : outArray(outArray) { + // Initialize the array to bogus strings. + for (int32_t i = 0; i < ARRAY_LENGTH; i++) { + outArray[i].setToBogus(); + } + } + + void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override { + if (uprv_strcmp(key, "case") == 0) { + return; + } + int32_t index = getIndex(key, status); + if (U_FAILURE(status)) { return; } + if (!outArray[index].isBogus()) { + return; + } + outArray[index] = value.getUnicodeString(status); + if (U_FAILURE(status)) { return; } + } + + private: + UnicodeString *outArray; +}; + +/** + * Populates outArray with `locale`-specific values for `unit` through use of + * PluralTableSink. Only the set of basic units are supported! + * + * Reading from resources *unitsNarrow* and *unitsShort* (for width + * UNUM_UNIT_WIDTH_NARROW), or just *unitsShort* (for width + * UNUM_UNIT_WIDTH_SHORT). For other widths, it reads just "units". + * + * @param unit must be a built-in unit, i.e. must have a type and subtype, + * listed in gTypes and gSubTypes in measunit.cpp. + * @param unitDisplayCase the empty string and "nominative" are treated the + * same. For other cases, strings for the requested case are used if found. + * (For any missing case-specific data, we fall back to nominative.) + * @param outArray must be of fixed length ARRAY_LENGTH. + */ +void getMeasureData(const Locale &locale, + const MeasureUnit &unit, + const UNumberUnitWidth &width, + const char *unitDisplayCase, + UnicodeString *outArray, + UErrorCode &status) { + PluralTableSink sink(outArray); + LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, locale.getName(), &status)); + if (U_FAILURE(status)) { return; } + + CharString subKey; + subKey.append("/", status); + subKey.append(unit.getType(), status); + subKey.append("/", status); + + // Check if unitSubType is an alias or not. + LocalUResourceBundlePointer aliasBundle(ures_open(U_ICUDATA_ALIAS, "metadata", &status)); + + UErrorCode aliasStatus = status; + StackUResourceBundle aliasFillIn; + CharString aliasKey; + aliasKey.append("alias/unit/", aliasStatus); + aliasKey.append(unit.getSubtype(), aliasStatus); + aliasKey.append("/replacement", aliasStatus); + ures_getByKeyWithFallback(aliasBundle.getAlias(), aliasKey.data(), aliasFillIn.getAlias(), + &aliasStatus); + CharString unitSubType; + if (!U_FAILURE(aliasStatus)) { + // This means the subType is an alias. Then, replace unitSubType with the replacement. + auto replacement = ures_getUnicodeString(aliasFillIn.getAlias(), &status); + unitSubType.appendInvariantChars(replacement, status); + } else { + unitSubType.append(unit.getSubtype(), status); + } + + // Map duration-year-person, duration-week-person, etc. to duration-year, duration-week, ... + // TODO(ICU-20400): Get duration-*-person data properly with aliases. + int32_t subtypeLen = static_cast(uprv_strlen(unitSubType.data())); + if (subtypeLen > 7 && uprv_strcmp(unitSubType.data() + subtypeLen - 7, "-person") == 0) { + subKey.append({unitSubType.data(), subtypeLen - 7}, status); + } else { + subKey.append({unitSubType.data(), subtypeLen}, status); + } + + if (width != UNUM_UNIT_WIDTH_FULL_NAME) { + UErrorCode localStatus = status; + CharString genderKey; + genderKey.append("units", localStatus); + genderKey.append(subKey, localStatus); + genderKey.append("/gender", localStatus); + StackUResourceBundle fillIn; + ures_getByKeyWithFallback(unitsBundle.getAlias(), genderKey.data(), fillIn.getAlias(), + &localStatus); + outArray[GENDER_INDEX] = ures_getUnicodeString(fillIn.getAlias(), &localStatus); + } + + CharString key; + key.append("units", status); + if (width == UNUM_UNIT_WIDTH_NARROW) { + key.append("Narrow", status); + } else if (width == UNUM_UNIT_WIDTH_SHORT) { + key.append("Short", status); + } + key.append(subKey, status); + + // Grab desired case first, if available. Then grab no-case data to fill in + // the gaps. + if (width == UNUM_UNIT_WIDTH_FULL_NAME && unitDisplayCase[0] != 0) { + CharString caseKey; + caseKey.append(key, status); + caseKey.append("/case/", status); + caseKey.append(unitDisplayCase, status); + + UErrorCode localStatus = U_ZERO_ERROR; + // TODO(icu-units#138): our fallback logic is not spec-compliant: + // lateral fallback should happen before locale fallback. Switch to + // getInflectedMeasureData after homogenizing data format? Find a unit + // test case that demonstrates the incorrect fallback logic (via + // regional variant of an inflected language?) + ures_getAllChildrenWithFallback(unitsBundle.getAlias(), caseKey.data(), sink, localStatus); + } + + // TODO(icu-units#138): our fallback logic is not spec-compliant: we + // check the given case, then go straight to the no-case data. The spec + // states we should first look for case="nominative". As part of #138, + // either get the spec changed, or add unit tests that warn us if + // case="nominative" data differs from no-case data? + UErrorCode localStatus = U_ZERO_ERROR; + ures_getAllChildrenWithFallback(unitsBundle.getAlias(), key.data(), sink, localStatus); + if (width == UNUM_UNIT_WIDTH_SHORT) { + if (U_FAILURE(localStatus)) { + status = localStatus; + } + return; + } +} + +// NOTE: outArray MUST have a length of at least ARRAY_LENGTH. +void getCurrencyLongNameData(const Locale &locale, const CurrencyUnit ¤cy, UnicodeString *outArray, + UErrorCode &status) { + // In ICU4J, this method gets a CurrencyData from CurrencyData.provider. + // TODO(ICU4J): Implement this without going through CurrencyData, like in ICU4C? + PluralTableSink sink(outArray); + LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_CURR, locale.getName(), &status)); + if (U_FAILURE(status)) { return; } + ures_getAllChildrenWithFallback(unitsBundle.getAlias(), "CurrencyUnitPatterns", sink, status); + if (U_FAILURE(status)) { return; } + for (int32_t i = 0; i < StandardPlural::Form::COUNT; i++) { + UnicodeString &pattern = outArray[i]; + if (pattern.isBogus()) { + continue; + } + int32_t longNameLen = 0; + const char16_t *longName = ucurr_getPluralName( + currency.getISOCurrency(), + locale.getName(), + nullptr /* isChoiceFormat */, + StandardPlural::getKeyword(static_cast(i)), + &longNameLen, + &status); + // Example pattern from data: "{0} {1}" + // Example output after find-and-replace: "{0} US dollars" + pattern.findAndReplace(UnicodeString(u"{1}"), UnicodeString(longName, longNameLen)); + } +} + +UnicodeString getCompoundValue(StringPiece compoundKey, + const Locale &locale, + const UNumberUnitWidth &width, + UErrorCode &status) { + LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, locale.getName(), &status)); + if (U_FAILURE(status)) { return {}; } + CharString key; + key.append("units", status); + if (width == UNUM_UNIT_WIDTH_NARROW) { + key.append("Narrow", status); + } else if (width == UNUM_UNIT_WIDTH_SHORT) { + key.append("Short", status); + } + key.append("/compound/", status); + key.append(compoundKey, status); + + UErrorCode localStatus = status; + int32_t len = 0; + const char16_t *ptr = + ures_getStringByKeyWithFallback(unitsBundle.getAlias(), key.data(), &len, &localStatus); + if (U_FAILURE(localStatus) && width != UNUM_UNIT_WIDTH_SHORT) { + // Fall back to short, which contains more compound data + key.clear(); + key.append("unitsShort/compound/", status); + key.append(compoundKey, status); + ptr = ures_getStringByKeyWithFallback(unitsBundle.getAlias(), key.data(), &len, &status); + } else { + status = localStatus; + } + if (U_FAILURE(status)) { + return {}; + } + return UnicodeString(ptr, len); +} + +/** + * Loads and applies deriveComponent rules from CLDR's grammaticalFeatures.xml. + * + * Consider a deriveComponent rule that looks like this: + * + * + * + * Instantiating an instance as follows: + * + * DerivedComponents d(loc, "case", "per"); + * + * Applying the rule in the XML element above, `d.value0("foo")` will be "foo", + * and `d.value1("foo")` will be "nominative". + * + * The values returned by value0(...) and value1(...) are valid only while the + * instance exists. In case of any kind of failure, value0(...) and value1(...) + * will return "". + */ +class DerivedComponents { + public: + /** + * Constructor. + * + * The feature and structure parameters must be null-terminated. The string + * referenced by compoundValue must exist for longer than the + * DerivedComponents instance. + */ + DerivedComponents(const Locale &locale, const char *feature, const char *structure) { + StackUResourceBundle derivationsBundle, stackBundle; + ures_openDirectFillIn(derivationsBundle.getAlias(), nullptr, "grammaticalFeatures", &status); + ures_getByKey(derivationsBundle.getAlias(), "grammaticalData", derivationsBundle.getAlias(), + &status); + ures_getByKey(derivationsBundle.getAlias(), "derivations", derivationsBundle.getAlias(), + &status); + if (U_FAILURE(status)) { + return; + } + UErrorCode localStatus = U_ZERO_ERROR; + // TODO(icu-units#28): use standard normal locale resolution algorithms + // rather than just grabbing language: + ures_getByKey(derivationsBundle.getAlias(), locale.getLanguage(), stackBundle.getAlias(), + &localStatus); + // TODO(icu-units#28): + // - code currently assumes if the locale exists, the rules are there - + // instead of falling back to root when the requested rule is missing. + // - investigate ures.h functions, see if one that uses res_findResource() + // might be better (or use res_findResource directly), or maybe help + // improve ures documentation to guide function selection? + if (localStatus == U_MISSING_RESOURCE_ERROR) { + ures_getByKey(derivationsBundle.getAlias(), "root", stackBundle.getAlias(), &status); + } else { + status = localStatus; + } + ures_getByKey(stackBundle.getAlias(), "component", stackBundle.getAlias(), &status); + ures_getByKey(stackBundle.getAlias(), feature, stackBundle.getAlias(), &status); + ures_getByKey(stackBundle.getAlias(), structure, stackBundle.getAlias(), &status); + UnicodeString val0 = ures_getUnicodeStringByIndex(stackBundle.getAlias(), 0, &status); + UnicodeString val1 = ures_getUnicodeStringByIndex(stackBundle.getAlias(), 1, &status); + if (U_SUCCESS(status)) { + if (val0.compare(UnicodeString(u"compound")) == 0) { + compound0_ = true; + } else { + compound0_ = false; + value0_.appendInvariantChars(val0, status); + } + if (val1.compare(UnicodeString(u"compound")) == 0) { + compound1_ = true; + } else { + compound1_ = false; + value1_.appendInvariantChars(val1, status); + } + } + } + + // Returns a StringPiece that is only valid as long as the instance exists. + StringPiece value0(const StringPiece compoundValue) const { + return compound0_ ? compoundValue : value0_.toStringPiece(); + } + + // Returns a StringPiece that is only valid as long as the instance exists. + StringPiece value1(const StringPiece compoundValue) const { + return compound1_ ? compoundValue : value1_.toStringPiece(); + } + + // Returns a char* that is only valid as long as the instance exists. + const char *value0(const char *compoundValue) const { + return compound0_ ? compoundValue : value0_.data(); + } + + // Returns a char* that is only valid as long as the instance exists. + const char *value1(const char *compoundValue) const { + return compound1_ ? compoundValue : value1_.data(); + } + + private: + UErrorCode status = U_ZERO_ERROR; + + // Holds strings referred to by value0 and value1; + bool compound0_ = false, compound1_ = false; + CharString value0_, value1_; +}; + +// TODO(icu-units#28): test somehow? Associate with an ICU ticket for adding +// testsuite support for testing with synthetic data? +/** + * Loads and returns the value in rules that look like these: + * + * + * + * + * Currently a fake example, but spec compliant: + * + * + * NOTE: If U_FAILURE(status), returns an empty string. + */ +UnicodeString +getDeriveCompoundRule(Locale locale, const char *feature, const char *structure, UErrorCode &status) { + StackUResourceBundle derivationsBundle, stackBundle; + ures_openDirectFillIn(derivationsBundle.getAlias(), nullptr, "grammaticalFeatures", &status); + ures_getByKey(derivationsBundle.getAlias(), "grammaticalData", derivationsBundle.getAlias(), + &status); + ures_getByKey(derivationsBundle.getAlias(), "derivations", derivationsBundle.getAlias(), &status); + // TODO: use standard normal locale resolution algorithms rather than just grabbing language: + ures_getByKey(derivationsBundle.getAlias(), locale.getLanguage(), stackBundle.getAlias(), &status); + // TODO: + // - code currently assumes if the locale exists, the rules are there - + // instead of falling back to root when the requested rule is missing. + // - investigate ures.h functions, see if one that uses res_findResource() + // might be better (or use res_findResource directly), or maybe help + // improve ures documentation to guide function selection? + if (status == U_MISSING_RESOURCE_ERROR) { + status = U_ZERO_ERROR; + ures_getByKey(derivationsBundle.getAlias(), "root", stackBundle.getAlias(), &status); + } + ures_getByKey(stackBundle.getAlias(), "compound", stackBundle.getAlias(), &status); + ures_getByKey(stackBundle.getAlias(), feature, stackBundle.getAlias(), &status); + UnicodeString uVal = ures_getUnicodeStringByKey(stackBundle.getAlias(), structure, &status); + if (U_FAILURE(status)) { + return {}; + } + U_ASSERT(!uVal.isBogus()); + return uVal; +} + +// Returns the gender string for structures following these rules: +// +// +// +// +// Fake example: +// +// +// data0 and data1 should be pattern arrays (UnicodeString[ARRAY_SIZE]) that +// correspond to value="0" and value="1". +// +// Pass a nullptr to data1 if the structure has no concept of value="1" (e.g. +// "prefix" doesn't). +UnicodeString getDerivedGender(Locale locale, + const char *structure, + UnicodeString *data0, + UnicodeString *data1, + UErrorCode &status) { + UnicodeString val = getDeriveCompoundRule(locale, "gender", structure, status); + if (val.length() == 1) { + switch (val[0]) { + case u'0': + return data0[GENDER_INDEX]; + case u'1': + if (data1 == nullptr) { + return {}; + } + return data1[GENDER_INDEX]; + } + } + return val; +} + +//////////////////////// +/// END DATA LOADING /// +//////////////////////// + +// TODO: promote this somewhere? It's based on patternprops.cpp' trimWhitespace +const char16_t *trimSpaceChars(const char16_t *s, int32_t &length) { + if (length <= 0 || (!u_isJavaSpaceChar(s[0]) && !u_isJavaSpaceChar(s[length - 1]))) { + return s; + } + int32_t start = 0; + int32_t limit = length; + while (start < limit && u_isJavaSpaceChar(s[start])) { + ++start; + } + if (start < limit) { + // There is non-white space at start; we will not move limit below that, + // so we need not test start 0); // Else it would not be COMPOUND + if (mui.singleUnits[endSlice]->dimensionality < 0) { + // We have a -per- construct + UnicodeString perRule = getDeriveCompoundRule(locale, "gender", "per", status); + if (perRule.length() != 1) { + // Fixed gender for -per- units + return perRule; + } + if (perRule[0] == u'1') { + // Find the start of the denominator. We already know there is one. + while (mui.singleUnits[startSlice]->dimensionality >= 0) { + startSlice++; + } + } else { + // Find the end of the numerator + while (endSlice >= 0 && mui.singleUnits[endSlice]->dimensionality < 0) { + endSlice--; + } + if (endSlice < 0) { + // We have only a denominator, e.g. "per-second". + // TODO(icu-units#28): find out what gender to use in the + // absence of a first value - mentioned in CLDR-14253. + return {}; + } + } + } + if (endSlice > startSlice) { + // We have a -times- construct + UnicodeString timesRule = getDeriveCompoundRule(locale, "gender", "times", status); + if (timesRule.length() != 1) { + // Fixed gender for -times- units + return timesRule; + } + if (timesRule[0] == u'0') { + endSlice = startSlice; + } else { + // We assume timesRule[0] == u'1' + startSlice = endSlice; + } + } + U_ASSERT(startSlice == endSlice); + singleUnitIndex = startSlice; + } else if (mui.complexity == UMEASURE_UNIT_MIXED) { + status = U_INTERNAL_PROGRAM_ERROR; + return {}; + } else { + U_ASSERT(mui.complexity == UMEASURE_UNIT_SINGLE); + U_ASSERT(mui.singleUnits.length() == 1); + } + + // Now we know which singleUnit's gender we want + const SingleUnitImpl *singleUnit = mui.singleUnits[singleUnitIndex]; + // Check for any power-prefix gender override: + if (std::abs(singleUnit->dimensionality) != 1) { + UnicodeString powerRule = getDeriveCompoundRule(locale, "gender", "power", status); + if (powerRule.length() != 1) { + // Fixed gender for -powN- units + return powerRule; + } + // powerRule[0] == u'0'; u'1' not currently in spec. + } + // Check for any SI and binary prefix gender override: + if (std::abs(singleUnit->dimensionality) != 1) { + UnicodeString prefixRule = getDeriveCompoundRule(locale, "gender", "prefix", status); + if (prefixRule.length() != 1) { + // Fixed gender for -powN- units + return prefixRule; + } + // prefixRule[0] == u'0'; u'1' not currently in spec. + } + // Now we've boiled it down to the gender of one simple unit identifier: + return getGenderForBuiltin(locale, MeasureUnit::forIdentifier(singleUnit->getSimpleUnitID(), status), + status); +} + +void maybeCalculateGender(const Locale &locale, + const MeasureUnit &unitRef, + UnicodeString *outArray, + UErrorCode &status) { + if (outArray[GENDER_INDEX].isBogus()) { + UnicodeString meterGender = getGenderForBuiltin(locale, MeasureUnit::getMeter(), status); + if (meterGender.isEmpty()) { + // No gender for meter: assume ungendered language + return; + } + // We have a gendered language, but are lacking gender for unitRef. + outArray[GENDER_INDEX] = calculateGenderForUnit(locale, unitRef, status); + } +} + +} // namespace + +void LongNameHandler::forMeasureUnit(const Locale &loc, + const MeasureUnit &unitRef, + const UNumberUnitWidth &width, + const char *unitDisplayCase, + const PluralRules *rules, + const MicroPropsGenerator *parent, + LongNameHandler *fillIn, + UErrorCode &status) { + // From https://unicode.org/reports/tr35/tr35-general.html#compound-units - + // Points 1 and 2 are mostly handled by MeasureUnit: + // + // 1. If the unitId is empty or invalid, fail + // 2. Put the unitId into normalized order + U_ASSERT(fillIn != nullptr); + + if (uprv_strcmp(unitRef.getType(), "") != 0) { + // Handling built-in units: + // + // 3. Set result to be getValue(unitId with length, pluralCategory, caseVariant) + // - If result is not empty, return it + UnicodeString simpleFormats[ARRAY_LENGTH]; + getMeasureData(loc, unitRef, width, unitDisplayCase, simpleFormats, status); + maybeCalculateGender(loc, unitRef, simpleFormats, status); + if (U_FAILURE(status)) { + return; + } + fillIn->rules = rules; + fillIn->parent = parent; + fillIn->simpleFormatsToModifiers(simpleFormats, + {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD}, status); + if (!simpleFormats[GENDER_INDEX].isBogus()) { + fillIn->gender = getGenderString(simpleFormats[GENDER_INDEX], status); + } + return; + + // TODO(icu-units#145): figure out why this causes a failure in + // format/MeasureFormatTest/TestIndividualPluralFallback and other + // tests, when it should have been an alternative for the lines above: + + // forArbitraryUnit(loc, unitRef, width, unitDisplayCase, fillIn, status); + // fillIn->rules = rules; + // fillIn->parent = parent; + // return; + } else { + // Check if it is a MeasureUnit this constructor handles: this + // constructor does not handle mixed units + U_ASSERT(unitRef.getComplexity(status) != UMEASURE_UNIT_MIXED); + forArbitraryUnit(loc, unitRef, width, unitDisplayCase, fillIn, status); + fillIn->rules = rules; + fillIn->parent = parent; + return; + } +} + +void LongNameHandler::forArbitraryUnit(const Locale &loc, + const MeasureUnit &unitRef, + const UNumberUnitWidth &width, + const char *unitDisplayCase, + LongNameHandler *fillIn, + UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + if (fillIn == nullptr) { + status = U_INTERNAL_PROGRAM_ERROR; + return; + } + + // Numbered list items are from the algorithms at + // https://unicode.org/reports/tr35/tr35-general.html#compound-units: + // + // 4. Divide the unitId into numerator (the part before the "-per-") and + // denominator (the part after the "-per-). If both are empty, fail + MeasureUnitImpl unit; + MeasureUnitImpl perUnit; + { + MeasureUnitImpl fullUnit = MeasureUnitImpl::forMeasureUnitMaybeCopy(unitRef, status); + if (U_FAILURE(status)) { + return; + } + for (int32_t i = 0; i < fullUnit.singleUnits.length(); i++) { + SingleUnitImpl *subUnit = fullUnit.singleUnits[i]; + if (subUnit->dimensionality > 0) { + unit.appendSingleUnit(*subUnit, status); + } else { + subUnit->dimensionality *= -1; + perUnit.appendSingleUnit(*subUnit, status); + } + } + } + + // TODO(icu-units#28): check placeholder logic, see if it needs to be + // present here instead of only in processPatternTimes: + // + // 5. Set both globalPlaceholder and globalPlaceholderPosition to be empty + + DerivedComponents derivedPerCases(loc, "case", "per"); + + // 6. numeratorUnitString + UnicodeString numeratorUnitData[ARRAY_LENGTH]; + processPatternTimes(std::move(unit), loc, width, derivedPerCases.value0(unitDisplayCase), + numeratorUnitData, status); + + // 7. denominatorUnitString + UnicodeString denominatorUnitData[ARRAY_LENGTH]; + processPatternTimes(std::move(perUnit), loc, width, derivedPerCases.value1(unitDisplayCase), + denominatorUnitData, status); + + // TODO(icu-units#139): + // - implement DerivedComponents for "plural/times" and "plural/power": + // French has different rules, we'll be producing the wrong results + // currently. (Prove via tests!) + // - implement DerivedComponents for "plural/per", "plural/prefix", + // "case/times", "case/power", and "case/prefix" - although they're + // currently hardcoded. Languages with different rules are surely on the + // way. + // + // Currently we only use "case/per", "plural/times", "case/times", and + // "case/power". + // + // This may have impact on multiSimpleFormatsToModifiers(...) below too? + // These rules are currently (ICU 69) all the same and hard-coded below. + UnicodeString perUnitPattern; + if (!denominatorUnitData[PER_INDEX].isBogus()) { + // If we have no denominator, we obtain the empty string: + perUnitPattern = denominatorUnitData[PER_INDEX]; + } else { + // 8. Set perPattern to be getValue([per], locale, length) + UnicodeString rawPerUnitFormat = getCompoundValue("per", loc, width, status); + // rawPerUnitFormat is something like "{0} per {1}"; we need to substitute in the secondary unit. + SimpleFormatter perPatternFormatter(rawPerUnitFormat, 2, 2, status); + if (U_FAILURE(status)) { + return; + } + // Plural and placeholder handling for 7. denominatorUnitString: + // TODO(icu-units#139): hardcoded: + // + UnicodeString denominatorFormat = + getWithPlural(denominatorUnitData, StandardPlural::Form::ONE, status); + // Some "one" pattern may not contain "{0}". For example in "ar" or "ne" locale. + SimpleFormatter denominatorFormatter(denominatorFormat, 0, 1, status); + if (U_FAILURE(status)) { + return; + } + UnicodeString denominatorPattern = denominatorFormatter.getTextWithNoArguments(); + int32_t trimmedLen = denominatorPattern.length(); + const char16_t *trimmed = trimSpaceChars(denominatorPattern.getBuffer(), trimmedLen); + UnicodeString denominatorString(false, trimmed, trimmedLen); + // 9. If the denominatorString is empty, set result to + // [numeratorString], otherwise set result to format(perPattern, + // numeratorString, denominatorString) + // + // TODO(icu-units#28): Why does UnicodeString need to be explicit in the + // following line? + perPatternFormatter.format(UnicodeString(u"{0}"), denominatorString, perUnitPattern, status); + if (U_FAILURE(status)) { + return; + } + } + if (perUnitPattern.length() == 0) { + fillIn->simpleFormatsToModifiers(numeratorUnitData, + {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD}, status); + } else { + fillIn->multiSimpleFormatsToModifiers(numeratorUnitData, perUnitPattern, + {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD}, status); + } + + // Gender + // + // TODO(icu-units#28): find out what gender to use in the absence of a first + // value - e.g. what's the gender of "per-second"? Mentioned in CLDR-14253. + // + // gender/per deriveCompound rules don't say: + // + fillIn->gender = getGenderString( + getDerivedGender(loc, "per", numeratorUnitData, denominatorUnitData, status), status); +} + +void LongNameHandler::processPatternTimes(MeasureUnitImpl &&productUnit, + Locale loc, + const UNumberUnitWidth &width, + const char *caseVariant, + UnicodeString *outArray, + UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + if (productUnit.complexity == UMEASURE_UNIT_MIXED) { + // These are handled by MixedUnitLongNameHandler + status = U_UNSUPPORTED_ERROR; + return; + } + +#if U_DEBUG + for (int32_t pluralIndex = 0; pluralIndex < ARRAY_LENGTH; pluralIndex++) { + U_ASSERT(outArray[pluralIndex].length() == 0); + U_ASSERT(!outArray[pluralIndex].isBogus()); + } +#endif + + if (productUnit.identifier.isEmpty()) { + // TODO(icu-units#28): consider when serialize should be called. + // identifier might also be empty for MeasureUnit(). + productUnit.serialize(status); + } + if (U_FAILURE(status)) { + return; + } + if (productUnit.identifier.length() == 0) { + // MeasureUnit(): no units: return empty strings. + return; + } + + MeasureUnit builtinUnit; + if (MeasureUnit::findBySubType(productUnit.identifier.toStringPiece(), &builtinUnit)) { + // TODO(icu-units#145): spec doesn't cover builtin-per-builtin, it + // breaks them all down. Do we want to drop this? + // - findBySubType isn't super efficient, if we skip it and go to basic + // singles, we don't have to construct MeasureUnit's anymore. + // - Check all the existing unit tests that fail without this: is it due + // to incorrect fallback via getMeasureData? + // - Do those unit tests cover this code path representatively? + if (builtinUnit != MeasureUnit()) { + getMeasureData(loc, builtinUnit, width, caseVariant, outArray, status); + maybeCalculateGender(loc, builtinUnit, outArray, status); + } + return; + } + + // 2. Set timesPattern to be getValue(times, locale, length) + UnicodeString timesPattern = getCompoundValue("times", loc, width, status); + SimpleFormatter timesPatternFormatter(timesPattern, 2, 2, status); + if (U_FAILURE(status)) { + return; + } + + PlaceholderPosition globalPlaceholder[ARRAY_LENGTH]; + char16_t globalJoinerChar = 0; + // Numbered list items are from the algorithms at + // https://unicode.org/reports/tr35/tr35-general.html#compound-units: + // + // pattern(...) point 5: + // - Set both globalPlaceholder and globalPlaceholderPosition to be empty + // + // 3. Set result to be empty + for (int32_t pluralIndex = 0; pluralIndex < ARRAY_LENGTH; pluralIndex++) { + // Initial state: empty string pattern, via all falling back to OTHER: + if (pluralIndex == StandardPlural::Form::OTHER) { + outArray[pluralIndex].remove(); + } else { + outArray[pluralIndex].setToBogus(); + } + globalPlaceholder[pluralIndex] = PH_EMPTY; + } + + // Empty string represents "compound" (propagate the plural form). + const char *pluralCategory = ""; + DerivedComponents derivedTimesPlurals(loc, "plural", "times"); + DerivedComponents derivedTimesCases(loc, "case", "times"); + DerivedComponents derivedPowerCases(loc, "case", "power"); + + // 4. For each single_unit in product_unit + for (int32_t singleUnitIndex = 0; singleUnitIndex < productUnit.singleUnits.length(); + singleUnitIndex++) { + SingleUnitImpl *singleUnit = productUnit.singleUnits[singleUnitIndex]; + const char *singlePluralCategory; + const char *singleCaseVariant; + // TODO(icu-units#28): ensure we have unit tests that change/fail if we + // assign incorrect case variants here: + if (singleUnitIndex < productUnit.singleUnits.length() - 1) { + // 4.1. If hasMultiple + singlePluralCategory = derivedTimesPlurals.value0(pluralCategory); + singleCaseVariant = derivedTimesCases.value0(caseVariant); + pluralCategory = derivedTimesPlurals.value1(pluralCategory); + caseVariant = derivedTimesCases.value1(caseVariant); + } else { + singlePluralCategory = derivedTimesPlurals.value1(pluralCategory); + singleCaseVariant = derivedTimesCases.value1(caseVariant); + } + + // 4.2. Get the gender of that single_unit + MeasureUnit simpleUnit; + if (!MeasureUnit::findBySubType(singleUnit->getSimpleUnitID(), &simpleUnit)) { + // Ideally all simple units should be known, but they're not: + // 100-kilometer is internally treated as a simple unit, but it is + // not a built-in unit and does not have formatting data in CLDR 39. + // + // TODO(icu-units#28): test (desirable) invariants in unit tests. + status = U_UNSUPPORTED_ERROR; + return; + } + const char *gender = getGenderString(getGenderForBuiltin(loc, simpleUnit, status), status); + + // 4.3. If singleUnit starts with a dimensionality_prefix, such as 'square-' + U_ASSERT(singleUnit->dimensionality > 0); + int32_t dimensionality = singleUnit->dimensionality; + UnicodeString dimensionalityPrefixPatterns[ARRAY_LENGTH]; + if (dimensionality != 1) { + // 4.3.1. set dimensionalityPrefixPattern to be + // getValue(that dimensionality_prefix, locale, length, singlePluralCategory, singleCaseVariant, gender), + // such as "{0} kwadratowym" + CharString dimensionalityKey("compound/power", status); + dimensionalityKey.appendNumber(dimensionality, status); + getInflectedMeasureData(dimensionalityKey.toStringPiece(), loc, width, gender, + singleCaseVariant, dimensionalityPrefixPatterns, status); + if (U_FAILURE(status)) { + // At the time of writing, only pow2 and pow3 are supported. + // Attempting to format other powers results in a + // U_RESOURCE_TYPE_MISMATCH. We convert the error if we + // understand it: + if (status == U_RESOURCE_TYPE_MISMATCH && dimensionality > 3) { + status = U_UNSUPPORTED_ERROR; + } + return; + } + + // TODO(icu-units#139): + // 4.3.2. set singlePluralCategory to be power0(singlePluralCategory) + + // 4.3.3. set singleCaseVariant to be power0(singleCaseVariant) + singleCaseVariant = derivedPowerCases.value0(singleCaseVariant); + // 4.3.4. remove the dimensionality_prefix from singleUnit + singleUnit->dimensionality = 1; + } + + // 4.4. if singleUnit starts with an si_prefix, such as 'centi' + UMeasurePrefix prefix = singleUnit->unitPrefix; + UnicodeString prefixPattern; + if (prefix != UMEASURE_PREFIX_ONE) { + // 4.4.1. set siPrefixPattern to be getValue(that si_prefix, locale, + // length), such as "centy{0}" + CharString prefixKey; + // prefixKey looks like "1024p3" or "10p-2": + prefixKey.appendNumber(umeas_getPrefixBase(prefix), status); + prefixKey.append('p', status); + prefixKey.appendNumber(umeas_getPrefixPower(prefix), status); + // Contains a pattern like "centy{0}". + prefixPattern = getCompoundValue(prefixKey.toStringPiece(), loc, width, status); + + // 4.4.2. set singlePluralCategory to be prefix0(singlePluralCategory) + // + // TODO(icu-units#139): that refers to these rules: + // + // though I'm not sure what other value they might end up having. + // + // 4.4.3. set singleCaseVariant to be prefix0(singleCaseVariant) + // + // TODO(icu-units#139): that refers to: + // but the prefix (value0) doesn't have case, the rest simply + // propagates. + + // 4.4.4. remove the si_prefix from singleUnit + singleUnit->unitPrefix = UMEASURE_PREFIX_ONE; + } + + // 4.5. Set corePattern to be the getValue(singleUnit, locale, length, + // singlePluralCategory, singleCaseVariant), such as "{0} metrem" + UnicodeString singleUnitArray[ARRAY_LENGTH]; + // At this point we are left with a Simple Unit: + U_ASSERT(uprv_strcmp(singleUnit->build(status).getIdentifier(), singleUnit->getSimpleUnitID()) == + 0); + getMeasureData(loc, singleUnit->build(status), width, singleCaseVariant, singleUnitArray, + status); + if (U_FAILURE(status)) { + // Shouldn't happen if we have data for all single units + return; + } + + // Calculate output gender + if (!singleUnitArray[GENDER_INDEX].isBogus()) { + U_ASSERT(!singleUnitArray[GENDER_INDEX].isEmpty()); + UnicodeString uVal; + + if (prefix != UMEASURE_PREFIX_ONE) { + singleUnitArray[GENDER_INDEX] = + getDerivedGender(loc, "prefix", singleUnitArray, nullptr, status); + } + + if (dimensionality != 1) { + singleUnitArray[GENDER_INDEX] = + getDerivedGender(loc, "power", singleUnitArray, nullptr, status); + } + + UnicodeString timesGenderRule = getDeriveCompoundRule(loc, "gender", "times", status); + if (timesGenderRule.length() == 1) { + switch (timesGenderRule[0]) { + case u'0': + if (singleUnitIndex == 0) { + U_ASSERT(outArray[GENDER_INDEX].isBogus()); + outArray[GENDER_INDEX] = singleUnitArray[GENDER_INDEX]; + } + break; + case u'1': + if (singleUnitIndex == productUnit.singleUnits.length() - 1) { + U_ASSERT(outArray[GENDER_INDEX].isBogus()); + outArray[GENDER_INDEX] = singleUnitArray[GENDER_INDEX]; + } + } + } else { + if (outArray[GENDER_INDEX].isBogus()) { + outArray[GENDER_INDEX] = timesGenderRule; + } + } + } + + // Calculate resulting patterns for each plural form + for (int32_t pluralIndex = 0; pluralIndex < StandardPlural::Form::COUNT; pluralIndex++) { + StandardPlural::Form plural = static_cast(pluralIndex); + + // singleUnitArray[pluralIndex] looks something like "{0} Meter" + if (outArray[pluralIndex].isBogus()) { + if (singleUnitArray[pluralIndex].isBogus()) { + // Let the usual plural fallback mechanism take care of this + // plural form + continue; + } else { + // Since our singleUnit can have a plural form that outArray + // doesn't yet have (relying on fallback to OTHER), we start + // by grabbing it with the normal plural fallback mechanism + outArray[pluralIndex] = getWithPlural(outArray, plural, status); + if (U_FAILURE(status)) { + return; + } + } + } + + if (uprv_strcmp(singlePluralCategory, "") != 0) { + plural = static_cast(getIndex(singlePluralCategory, status)); + } + + // 4.6. Extract(corePattern, coreUnit, placeholder, placeholderPosition) from that pattern. + UnicodeString coreUnit; + PlaceholderPosition placeholderPosition; + char16_t joinerChar; + extractCorePattern(getWithPlural(singleUnitArray, plural, status), coreUnit, + placeholderPosition, joinerChar); + + // 4.7 If the position is middle, then fail + if (placeholderPosition == PH_MIDDLE) { + status = U_UNSUPPORTED_ERROR; + return; + } + + // 4.8. If globalPlaceholder is empty + if (globalPlaceholder[pluralIndex] == PH_EMPTY) { + globalPlaceholder[pluralIndex] = placeholderPosition; + globalJoinerChar = joinerChar; + } else { + // Expect all units involved to have the same placeholder position + U_ASSERT(globalPlaceholder[pluralIndex] == placeholderPosition); + // TODO(icu-units#28): Do we want to add a unit test that checks + // for consistent joiner chars? Probably not, given how + // inconsistent they are. File a CLDR ticket with examples? + } + // Now coreUnit would be just "Meter" + + // 4.9. If siPrefixPattern is not empty + if (prefix != UMEASURE_PREFIX_ONE) { + SimpleFormatter prefixCompiled(prefixPattern, 1, 1, status); + if (U_FAILURE(status)) { + return; + } + + // 4.9.1. Set coreUnit to be the combineLowercasing(locale, length, siPrefixPattern, + // coreUnit) + UnicodeString tmp; + // combineLowercasing(locale, length, prefixPattern, coreUnit) + // + // TODO(icu-units#28): run this only if prefixPattern does not + // contain space characters - do languages "as", "bn", "hi", + // "kk", etc have concepts of upper and lower case?: + if (width == UNUM_UNIT_WIDTH_FULL_NAME) { + coreUnit.toLower(loc); + } + prefixCompiled.format(coreUnit, tmp, status); + if (U_FAILURE(status)) { + return; + } + coreUnit = tmp; + } + + // 4.10. If dimensionalityPrefixPattern is not empty + if (dimensionality != 1) { + SimpleFormatter dimensionalityCompiled( + getWithPlural(dimensionalityPrefixPatterns, plural, status), 1, 1, status); + if (U_FAILURE(status)) { + return; + } + + // 4.10.1. Set coreUnit to be the combineLowercasing(locale, length, + // dimensionalityPrefixPattern, coreUnit) + UnicodeString tmp; + // combineLowercasing(locale, length, prefixPattern, coreUnit) + // + // TODO(icu-units#28): run this only if prefixPattern does not + // contain space characters - do languages "as", "bn", "hi", + // "kk", etc have concepts of upper and lower case?: + if (width == UNUM_UNIT_WIDTH_FULL_NAME) { + coreUnit.toLower(loc); + } + dimensionalityCompiled.format(coreUnit, tmp, status); + if (U_FAILURE(status)) { + return; + } + coreUnit = tmp; + } + + if (outArray[pluralIndex].length() == 0) { + // 4.11. If the result is empty, set result to be coreUnit + outArray[pluralIndex] = coreUnit; + } else { + // 4.12. Otherwise set result to be format(timesPattern, result, coreUnit) + UnicodeString tmp; + timesPatternFormatter.format(outArray[pluralIndex], coreUnit, tmp, status); + outArray[pluralIndex] = tmp; + } + } + } + for (int32_t pluralIndex = 0; pluralIndex < StandardPlural::Form::COUNT; pluralIndex++) { + if (globalPlaceholder[pluralIndex] == PH_BEGINNING) { + UnicodeString tmp; + tmp.append(u"{0}", 3); + if (globalJoinerChar != 0) { + tmp.append(globalJoinerChar); + } + tmp.append(outArray[pluralIndex]); + outArray[pluralIndex] = tmp; + } else if (globalPlaceholder[pluralIndex] == PH_END) { + if (globalJoinerChar != 0) { + outArray[pluralIndex].append(globalJoinerChar); + } + outArray[pluralIndex].append(u"{0}", 3); + } + } +} + +UnicodeString LongNameHandler::getUnitDisplayName( + const Locale& loc, + const MeasureUnit& unit, + UNumberUnitWidth width, + UErrorCode& status) { + if (U_FAILURE(status)) { + return ICU_Utility::makeBogusString(); + } + UnicodeString simpleFormats[ARRAY_LENGTH]; + getMeasureData(loc, unit, width, "", simpleFormats, status); + return simpleFormats[DNAM_INDEX]; +} + +UnicodeString LongNameHandler::getUnitPattern( + const Locale& loc, + const MeasureUnit& unit, + UNumberUnitWidth width, + StandardPlural::Form pluralForm, + UErrorCode& status) { + if (U_FAILURE(status)) { + return ICU_Utility::makeBogusString(); + } + UnicodeString simpleFormats[ARRAY_LENGTH]; + getMeasureData(loc, unit, width, "", simpleFormats, status); + // The above already handles fallback from other widths to short + if (U_FAILURE(status)) { + return ICU_Utility::makeBogusString(); + } + // Now handle fallback from other plural forms to OTHER + return (!(simpleFormats[pluralForm]).isBogus())? simpleFormats[pluralForm]: + simpleFormats[StandardPlural::Form::OTHER]; +} + +LongNameHandler* LongNameHandler::forCurrencyLongNames(const Locale &loc, const CurrencyUnit ¤cy, + const PluralRules *rules, + const MicroPropsGenerator *parent, + UErrorCode &status) { + auto* result = new LongNameHandler(rules, parent); + if (result == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + UnicodeString simpleFormats[ARRAY_LENGTH]; + getCurrencyLongNameData(loc, currency, simpleFormats, status); + if (U_FAILURE(status)) { return nullptr; } + result->simpleFormatsToModifiers(simpleFormats, {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}, status); + // TODO(icu-units#28): currency gender? + return result; +} + +void LongNameHandler::simpleFormatsToModifiers(const UnicodeString *simpleFormats, Field field, + UErrorCode &status) { + for (int32_t i = 0; i < StandardPlural::Form::COUNT; i++) { + StandardPlural::Form plural = static_cast(i); + UnicodeString simpleFormat = getWithPlural(simpleFormats, plural, status); + if (U_FAILURE(status)) { return; } + SimpleFormatter compiledFormatter(simpleFormat, 0, 1, status); + if (U_FAILURE(status)) { return; } + fModifiers[i] = SimpleModifier(compiledFormatter, field, false, {this, SIGNUM_POS_ZERO, plural}); + } +} + +void LongNameHandler::multiSimpleFormatsToModifiers(const UnicodeString *leadFormats, UnicodeString trailFormat, + Field field, UErrorCode &status) { + SimpleFormatter trailCompiled(trailFormat, 1, 1, status); + if (U_FAILURE(status)) { return; } + for (int32_t i = 0; i < StandardPlural::Form::COUNT; i++) { + StandardPlural::Form plural = static_cast(i); + UnicodeString leadFormat = getWithPlural(leadFormats, plural, status); + if (U_FAILURE(status)) { return; } + UnicodeString compoundFormat; + if (leadFormat.length() == 0) { + compoundFormat = trailFormat; + } else { + trailCompiled.format(leadFormat, compoundFormat, status); + if (U_FAILURE(status)) { return; } + } + SimpleFormatter compoundCompiled(compoundFormat, 0, 1, status); + if (U_FAILURE(status)) { return; } + fModifiers[i] = SimpleModifier(compoundCompiled, field, false, {this, SIGNUM_POS_ZERO, plural}); + } +} + +void LongNameHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, + UErrorCode &status) const { + if (parent != nullptr) { + parent->processQuantity(quantity, micros, status); + } + StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, rules, quantity, status); + micros.modOuter = &fModifiers[pluralForm]; + micros.gender = gender; +} + +const Modifier* LongNameHandler::getModifier(Signum /*signum*/, StandardPlural::Form plural) const { + return &fModifiers[plural]; +} + +void MixedUnitLongNameHandler::forMeasureUnit(const Locale &loc, + const MeasureUnit &mixedUnit, + const UNumberUnitWidth &width, + const char *unitDisplayCase, + const PluralRules *rules, + const MicroPropsGenerator *parent, + MixedUnitLongNameHandler *fillIn, + UErrorCode &status) { + U_ASSERT(mixedUnit.getComplexity(status) == UMEASURE_UNIT_MIXED); + U_ASSERT(fillIn != nullptr); + if (U_FAILURE(status)) { + return; + } + + MeasureUnitImpl temp; + const MeasureUnitImpl &impl = MeasureUnitImpl::forMeasureUnit(mixedUnit, temp, status); + // Defensive, for production code: + if (impl.complexity != UMEASURE_UNIT_MIXED) { + // Should be using the normal LongNameHandler + status = U_UNSUPPORTED_ERROR; + return; + } + + fillIn->fMixedUnitCount = impl.singleUnits.length(); + fillIn->fMixedUnitData.adoptInstead(new UnicodeString[fillIn->fMixedUnitCount * ARRAY_LENGTH]); + for (int32_t i = 0; i < fillIn->fMixedUnitCount; i++) { + // Grab data for each of the components. + UnicodeString *unitData = &fillIn->fMixedUnitData[i * ARRAY_LENGTH]; + // TODO(CLDR-14582): check from the CLDR-14582 ticket whether this + // propagation of unitDisplayCase is correct: + getMeasureData(loc, impl.singleUnits[i]->build(status), width, unitDisplayCase, unitData, + status); + // TODO(ICU-21494): if we add support for gender for mixed units, we may + // need maybeCalculateGender() here. + } + + // TODO(icu-units#120): Make sure ICU doesn't output zero-valued + // high-magnitude fields + // * for mixed units count N, produce N listFormatters, one for each subset + // that might be formatted. + UListFormatterWidth listWidth = ULISTFMT_WIDTH_SHORT; + if (width == UNUM_UNIT_WIDTH_NARROW) { + listWidth = ULISTFMT_WIDTH_NARROW; + } else if (width == UNUM_UNIT_WIDTH_FULL_NAME) { + // This might be the same as SHORT in most languages: + listWidth = ULISTFMT_WIDTH_WIDE; + } + fillIn->fListFormatter.adoptInsteadAndCheckErrorCode( + ListFormatter::createInstance(loc, ULISTFMT_TYPE_UNITS, listWidth, status), status); + // TODO(ICU-21494): grab gender of each unit, calculate the gender + // associated with this list formatter, save it for later. + fillIn->rules = rules; + fillIn->parent = parent; + + // We need a localised NumberFormatter for the numbers of the bigger units + // (providing Arabic numerals, for example). + fillIn->fNumberFormatter = NumberFormatter::withLocale(loc); +} + +void MixedUnitLongNameHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, + UErrorCode &status) const { + U_ASSERT(fMixedUnitCount > 1); + if (parent != nullptr) { + parent->processQuantity(quantity, micros, status); + } + micros.modOuter = getMixedUnitModifier(quantity, micros, status); +} + +const Modifier *MixedUnitLongNameHandler::getMixedUnitModifier(DecimalQuantity &quantity, + MicroProps µs, + UErrorCode &status) const { + if (micros.mixedMeasuresCount == 0) { + U_ASSERT(micros.mixedMeasuresCount > 0); // Mixed unit: we must have more than one unit value + status = U_UNSUPPORTED_ERROR; + return µs.helpers.emptyWeakModifier; + } + + // Algorithm: + // + // For the mixed-units measurement of: "3 yard, 1 foot, 2.6 inch", we should + // find "3 yard" and "1 foot" in micros.mixedMeasures. + // + // Obtain long-names with plural forms corresponding to measure values: + // * {0} yards, {0} foot, {0} inches + // + // Format the integer values appropriately and modify with the format + // strings: + // - 3 yards, 1 foot + // + // Use ListFormatter to combine, with one placeholder: + // - 3 yards, 1 foot and {0} inches + // + // Return a SimpleModifier for this pattern, letting the rest of the + // pipeline take care of the remaining inches. + + LocalArray outputMeasuresList(new UnicodeString[fMixedUnitCount], status); + if (U_FAILURE(status)) { + return µs.helpers.emptyWeakModifier; + } + + StandardPlural::Form quantityPlural = StandardPlural::Form::OTHER; + for (int32_t i = 0; i < micros.mixedMeasuresCount; i++) { + DecimalQuantity fdec; + + // If numbers are negative, only the first number needs to have its + // negative sign formatted. + int64_t number = i > 0 ? std::abs(micros.mixedMeasures[i]) : micros.mixedMeasures[i]; + + if (micros.indexOfQuantity == i) { // Insert placeholder for `quantity` + // If quantity is not the first value and quantity is negative + if (micros.indexOfQuantity > 0 && quantity.isNegative()) { + quantity.negate(); + } + + StandardPlural::Form quantityPlural = + utils::getPluralSafe(micros.rounder, rules, quantity, status); + UnicodeString quantityFormatWithPlural = + getWithPlural(&fMixedUnitData[i * ARRAY_LENGTH], quantityPlural, status); + SimpleFormatter quantityFormatter(quantityFormatWithPlural, 0, 1, status); + quantityFormatter.format(UnicodeString(u"{0}"), outputMeasuresList[i], status); + } else { + fdec.setToLong(number); + StandardPlural::Form pluralForm = utils::getStandardPlural(rules, fdec); + UnicodeString simpleFormat = + getWithPlural(&fMixedUnitData[i * ARRAY_LENGTH], pluralForm, status); + SimpleFormatter compiledFormatter(simpleFormat, 0, 1, status); + UnicodeString num; + auto appendable = UnicodeStringAppendable(num); + + fNumberFormatter.formatDecimalQuantity(fdec, status).appendTo(appendable, status); + compiledFormatter.format(num, outputMeasuresList[i], status); + } + } + + // TODO(ICU-21494): implement gender for lists of mixed units. Presumably we + // can set micros.gender to the gender associated with the list formatter in + // use below (once we have correct support for that). And then document this + // appropriately? "getMixedUnitModifier" doesn't sound like it would do + // something like this. + + // Combine list into a "premixed" pattern + UnicodeString premixedFormatPattern; + fListFormatter->format(outputMeasuresList.getAlias(), fMixedUnitCount, premixedFormatPattern, + status); + SimpleFormatter premixedCompiled(premixedFormatPattern, 0, 1, status); + if (U_FAILURE(status)) { + return µs.helpers.emptyWeakModifier; + } + + micros.helpers.mixedUnitModifier = + SimpleModifier(premixedCompiled, kUndefinedField, false, {this, SIGNUM_POS_ZERO, quantityPlural}); + return µs.helpers.mixedUnitModifier; +} + +const Modifier *MixedUnitLongNameHandler::getModifier(Signum /*signum*/, + StandardPlural::Form /*plural*/) const { + // TODO(icu-units#28): investigate this method when investigating where + // ModifierStore::getModifier() gets used. To be sure it remains + // unreachable: + UPRV_UNREACHABLE_EXIT; + return nullptr; +} + +LongNameMultiplexer *LongNameMultiplexer::forMeasureUnits(const Locale &loc, + const MaybeStackVector &units, + const UNumberUnitWidth &width, + const char *unitDisplayCase, + const PluralRules *rules, + const MicroPropsGenerator *parent, + UErrorCode &status) { + LocalPointer result(new LongNameMultiplexer(parent), status); + if (U_FAILURE(status)) { + return nullptr; + } + U_ASSERT(units.length() > 0); + if (result->fHandlers.resize(units.length()) == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + result->fMeasureUnits.adoptInstead(new MeasureUnit[units.length()]); + for (int32_t i = 0, length = units.length(); i < length; i++) { + const MeasureUnit &unit = *units[i]; + result->fMeasureUnits[i] = unit; + if (unit.getComplexity(status) == UMEASURE_UNIT_MIXED) { + MixedUnitLongNameHandler *mlnh = result->fMixedUnitHandlers.createAndCheckErrorCode(status); + MixedUnitLongNameHandler::forMeasureUnit(loc, unit, width, unitDisplayCase, rules, nullptr, + mlnh, status); + result->fHandlers[i] = mlnh; + } else { + LongNameHandler *lnh = result->fLongNameHandlers.createAndCheckErrorCode(status); + LongNameHandler::forMeasureUnit(loc, unit, width, unitDisplayCase, rules, nullptr, lnh, status); + result->fHandlers[i] = lnh; + } + if (U_FAILURE(status)) { + return nullptr; + } + } + return result.orphan(); +} + +void LongNameMultiplexer::processQuantity(DecimalQuantity &quantity, MicroProps µs, + UErrorCode &status) const { + // We call parent->processQuantity() from the Multiplexer, instead of + // letting LongNameHandler handle it: we don't know which LongNameHandler to + // call until we've called the parent! + fParent->processQuantity(quantity, micros, status); + + // Call the correct LongNameHandler based on outputUnit + for (int i = 0; i < fHandlers.getCapacity(); i++) { + if (fMeasureUnits[i] == micros.outputUnit) { + fHandlers[i]->processQuantity(quantity, micros, status); + return; + } + } + if (U_FAILURE(status)) { + return; + } + // We shouldn't receive any outputUnit for which we haven't already got a + // LongNameHandler: + status = U_INTERNAL_PROGRAM_ERROR; +} + +#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 06949989d7fbcd..bf6fe1f5849379 100644 --- a/deps/icu-small/source/i18n/number_longnames.h +++ b/deps/icu-small/source/i18n/number_longnames.h @@ -1,272 +1,272 @@ -// © 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_LONGNAMES_H__ -#define __NUMBER_LONGNAMES_H__ - -#include "cmemory.h" -#include "unicode/listformatter.h" -#include "unicode/uversion.h" -#include "number_utils.h" -#include "number_modifiers.h" - -U_NAMESPACE_BEGIN namespace number { -namespace impl { - -// LongNameHandler takes care of formatting currency and measurement unit names, -// as well as populating the gender of measure units. -class LongNameHandler : public MicroPropsGenerator, public ModifierStore, public UMemory { - public: - static UnicodeString getUnitDisplayName( - const Locale& loc, - const MeasureUnit& unit, - UNumberUnitWidth width, - UErrorCode& status); - - // This function does not support inflections or other newer NumberFormatter - // features: it exists to support the older not-recommended MeasureFormat. - static UnicodeString getUnitPattern( - const Locale& loc, - const MeasureUnit& unit, - UNumberUnitWidth width, - StandardPlural::Form pluralForm, - UErrorCode& status); - - static LongNameHandler* - forCurrencyLongNames(const Locale &loc, const CurrencyUnit ¤cy, const PluralRules *rules, - const MicroPropsGenerator *parent, UErrorCode &status); - - /** - * Construct a localized LongNameHandler for the specified MeasureUnit. - * - * Mixed units are not supported, use MixedUnitLongNameHandler::forMeasureUnit. - * - * This function uses a fillIn instead of returning a pointer, because we - * want to fill in instances in a MemoryPool (which cannot adopt pointers it - * didn't create itself). - * - * @param loc The desired locale. - * @param unitRef The measure unit to construct a LongNameHandler for. - * @param width Specifies the desired unit rendering. - * @param unitDisplayCase Specifies the desired grammatical case. If the - * specified case is not found, we fall back to nominative or no-case. - * @param rules Does not take ownership. - * @param parent Does not take ownership. - * @param fillIn Required. - */ - static void forMeasureUnit(const Locale &loc, - const MeasureUnit &unitRef, - const UNumberUnitWidth &width, - const char *unitDisplayCase, - const PluralRules *rules, - const MicroPropsGenerator *parent, - LongNameHandler *fillIn, - UErrorCode &status); - - /** - * Selects the plural-appropriate Modifier from the set of fModifiers based - * on the plural form. - */ - void - processQuantity(DecimalQuantity &quantity, MicroProps µs, UErrorCode &status) const U_OVERRIDE; - - const Modifier* getModifier(Signum signum, StandardPlural::Form plural) const U_OVERRIDE; - - private: - // A set of pre-computed modifiers, one for each plural form. - SimpleModifier fModifiers[StandardPlural::Form::COUNT]; - // Not owned - const PluralRules *rules; - // Not owned - const MicroPropsGenerator *parent; - // Grammatical gender of the formatted result. Not owned: must point at - // static or global strings. - const char *gender = ""; - - LongNameHandler(const PluralRules *rules, const MicroPropsGenerator *parent) - : rules(rules), parent(parent) { - } - - LongNameHandler() : rules(nullptr), parent(nullptr) { - } - - // Enables MemoryPool::emplaceBack(): requires access to - // the private constructors. - friend class MemoryPool; - - // Allow macrosToMicroGenerator to call the private default constructor. - friend class NumberFormatterImpl; - - // Fills in LongNameHandler fields for formatting units identified `unit`. - static void forArbitraryUnit(const Locale &loc, - const MeasureUnit &unit, - const UNumberUnitWidth &width, - const char *unitDisplayCase, - LongNameHandler *fillIn, - UErrorCode &status); - - // Roughly corresponds to patternTimes(...) in the spec: - // https://unicode.org/reports/tr35/tr35-general.html#compound-units - // - // productUnit is an rvalue reference to indicate this function consumes it, - // leaving it in a not-useful / undefined state. - static void processPatternTimes(MeasureUnitImpl &&productUnit, - Locale loc, - const UNumberUnitWidth &width, - const char *caseVariant, - UnicodeString *outArray, - UErrorCode &status); - - // Sets fModifiers to use the patterns from `simpleFormats`. - void simpleFormatsToModifiers(const UnicodeString *simpleFormats, Field field, UErrorCode &status); - - // Sets fModifiers to a combination of `leadFormats` (one per plural form) - // and `trailFormat` appended to each. - // - // With a leadFormat of "{0}m" and a trailFormat of "{0}/s", it produces a - // pattern of "{0}m/s" by inserting each leadFormat pattern into trailFormat. - void multiSimpleFormatsToModifiers(const UnicodeString *leadFormats, UnicodeString trailFormat, - Field field, UErrorCode &status); -}; - -// Similar to LongNameHandler, but only for MIXED units. -class MixedUnitLongNameHandler : public MicroPropsGenerator, public ModifierStore, public UMemory { - public: - /** - * Construct a localized MixedUnitLongNameHandler for the specified - * MeasureUnit. It must be a MIXED unit. - * - * This function uses a fillIn instead of returning a pointer, because we - * want to fill in instances in a MemoryPool (which cannot adopt pointers it - * didn't create itself). - * - * @param loc The desired locale. - * @param mixedUnit The mixed measure unit to construct a - * MixedUnitLongNameHandler for. - * @param width Specifies the desired unit rendering. - * @param unitDisplayCase Specifies the desired grammatical case. If the - * specified case is not found, we fall back to nominative or no-case. - * @param rules Does not take ownership. - * @param parent Does not take ownership. - * @param fillIn Required. - */ - static void forMeasureUnit(const Locale &loc, - const MeasureUnit &mixedUnit, - const UNumberUnitWidth &width, - const char *unitDisplayCase, - const PluralRules *rules, - const MicroPropsGenerator *parent, - MixedUnitLongNameHandler *fillIn, - UErrorCode &status); - - /** - * Produces a plural-appropriate Modifier for a mixed unit: `quantity` is - * taken as the final smallest unit, while the larger unit values must be - * provided via `micros.mixedMeasures`. - */ - void processQuantity(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const U_OVERRIDE; - - // Required for ModifierStore. And ModifierStore is required by - // SimpleModifier constructor's last parameter. We assert his will never get - // called though. - const Modifier *getModifier(Signum signum, StandardPlural::Form plural) const U_OVERRIDE; - - private: - // Not owned - const PluralRules *rules; - - // Not owned - const MicroPropsGenerator *parent; - - // Total number of units in the MeasureUnit this handler was configured for: - // for "foot-and-inch", this will be 2. - int32_t fMixedUnitCount = 1; - - // Stores unit data for each of the individual units. For each unit, it - // stores ARRAY_LENGTH strings, as returned by getMeasureData. (Each unit - // with index `i` has ARRAY_LENGTH strings starting at index - // `i*ARRAY_LENGTH` in this array.) - LocalArray fMixedUnitData; - - // Formats the larger units of Mixed Unit measurements. - LocalizedNumberFormatter fNumberFormatter; - - // Joins mixed units together. - LocalPointer fListFormatter; - - MixedUnitLongNameHandler(const PluralRules *rules, const MicroPropsGenerator *parent) - : rules(rules), parent(parent) { - } - - MixedUnitLongNameHandler() : rules(nullptr), parent(nullptr) { - } - - // Allow macrosToMicroGenerator to call the private default constructor. - friend class NumberFormatterImpl; - - // Enables MemoryPool::emplaceBack(): requires access to - // the private constructors. - friend class MemoryPool; - - // For a mixed unit, returns a Modifier that takes only one parameter: the - // smallest and final unit of the set. The bigger units' values and labels - // get baked into this Modifier, together with the unit label of the final - // unit. - const Modifier *getMixedUnitModifier(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const; -}; - -/** - * A MicroPropsGenerator that multiplexes between different LongNameHandlers, - * depending on the outputUnit. - * - * See processQuantity() for the input requirements. - */ -class LongNameMultiplexer : public MicroPropsGenerator, public UMemory { - public: - // Produces a multiplexer for LongNameHandlers, one for each unit in - // `units`. An individual unit might be a mixed unit. - static LongNameMultiplexer *forMeasureUnits(const Locale &loc, - const MaybeStackVector &units, - const UNumberUnitWidth &width, - const char *unitDisplayCase, - const PluralRules *rules, - const MicroPropsGenerator *parent, - UErrorCode &status); - - // The output unit must be provided via `micros.outputUnit`, it must match - // one of the units provided to the factory function. - void processQuantity(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const U_OVERRIDE; - - private: - /** - * Because we only know which LongNameHandler we wish to call after calling - * earlier MicroPropsGenerators in the chain, LongNameMultiplexer keeps the - * parent link, while the LongNameHandlers are given no parents. - */ - MemoryPool fLongNameHandlers; - MemoryPool fMixedUnitHandlers; - // Unowned pointers to instances owned by MaybeStackVectors. - MaybeStackArray fHandlers; - // Each MeasureUnit corresponds to the same-index MicroPropsGenerator - // pointed to in fHandlers. - LocalArray fMeasureUnits; - - const MicroPropsGenerator *fParent; - - LongNameMultiplexer(const MicroPropsGenerator *parent) : fParent(parent) { - } -}; - -} // namespace impl -} // namespace number -U_NAMESPACE_END - -#endif //__NUMBER_LONGNAMES_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_LONGNAMES_H__ +#define __NUMBER_LONGNAMES_H__ + +#include "cmemory.h" +#include "unicode/listformatter.h" +#include "unicode/uversion.h" +#include "number_utils.h" +#include "number_modifiers.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +// LongNameHandler takes care of formatting currency and measurement unit names, +// as well as populating the gender of measure units. +class LongNameHandler : public MicroPropsGenerator, public ModifierStore, public UMemory { + public: + static UnicodeString getUnitDisplayName( + const Locale& loc, + const MeasureUnit& unit, + UNumberUnitWidth width, + UErrorCode& status); + + // This function does not support inflections or other newer NumberFormatter + // features: it exists to support the older not-recommended MeasureFormat. + static UnicodeString getUnitPattern( + const Locale& loc, + const MeasureUnit& unit, + UNumberUnitWidth width, + StandardPlural::Form pluralForm, + UErrorCode& status); + + static LongNameHandler* + forCurrencyLongNames(const Locale &loc, const CurrencyUnit ¤cy, const PluralRules *rules, + const MicroPropsGenerator *parent, UErrorCode &status); + + /** + * Construct a localized LongNameHandler for the specified MeasureUnit. + * + * Mixed units are not supported, use MixedUnitLongNameHandler::forMeasureUnit. + * + * This function uses a fillIn instead of returning a pointer, because we + * want to fill in instances in a MemoryPool (which cannot adopt pointers it + * didn't create itself). + * + * @param loc The desired locale. + * @param unitRef The measure unit to construct a LongNameHandler for. + * @param width Specifies the desired unit rendering. + * @param unitDisplayCase Specifies the desired grammatical case. If the + * specified case is not found, we fall back to nominative or no-case. + * @param rules Does not take ownership. + * @param parent Does not take ownership. + * @param fillIn Required. + */ + static void forMeasureUnit(const Locale &loc, + const MeasureUnit &unitRef, + const UNumberUnitWidth &width, + const char *unitDisplayCase, + const PluralRules *rules, + const MicroPropsGenerator *parent, + LongNameHandler *fillIn, + UErrorCode &status); + + /** + * Selects the plural-appropriate Modifier from the set of fModifiers based + * on the plural form. + */ + void + processQuantity(DecimalQuantity &quantity, MicroProps µs, UErrorCode &status) const override; + + const Modifier* getModifier(Signum signum, StandardPlural::Form plural) const override; + + private: + // A set of pre-computed modifiers, one for each plural form. + SimpleModifier fModifiers[StandardPlural::Form::COUNT]; + // Not owned + const PluralRules *rules; + // Not owned + const MicroPropsGenerator *parent; + // Grammatical gender of the formatted result. Not owned: must point at + // static or global strings. + const char *gender = ""; + + LongNameHandler(const PluralRules *rules, const MicroPropsGenerator *parent) + : rules(rules), parent(parent) { + } + + LongNameHandler() : rules(nullptr), parent(nullptr) { + } + + // Enables MemoryPool::emplaceBack(): requires access to + // the private constructors. + friend class MemoryPool; + + // Allow macrosToMicroGenerator to call the private default constructor. + friend class NumberFormatterImpl; + + // Fills in LongNameHandler fields for formatting units identified `unit`. + static void forArbitraryUnit(const Locale &loc, + const MeasureUnit &unit, + const UNumberUnitWidth &width, + const char *unitDisplayCase, + LongNameHandler *fillIn, + UErrorCode &status); + + // Roughly corresponds to patternTimes(...) in the spec: + // https://unicode.org/reports/tr35/tr35-general.html#compound-units + // + // productUnit is an rvalue reference to indicate this function consumes it, + // leaving it in a not-useful / undefined state. + static void processPatternTimes(MeasureUnitImpl &&productUnit, + Locale loc, + const UNumberUnitWidth &width, + const char *caseVariant, + UnicodeString *outArray, + UErrorCode &status); + + // Sets fModifiers to use the patterns from `simpleFormats`. + void simpleFormatsToModifiers(const UnicodeString *simpleFormats, Field field, UErrorCode &status); + + // Sets fModifiers to a combination of `leadFormats` (one per plural form) + // and `trailFormat` appended to each. + // + // With a leadFormat of "{0}m" and a trailFormat of "{0}/s", it produces a + // pattern of "{0}m/s" by inserting each leadFormat pattern into trailFormat. + void multiSimpleFormatsToModifiers(const UnicodeString *leadFormats, UnicodeString trailFormat, + Field field, UErrorCode &status); +}; + +// Similar to LongNameHandler, but only for MIXED units. +class MixedUnitLongNameHandler : public MicroPropsGenerator, public ModifierStore, public UMemory { + public: + /** + * Construct a localized MixedUnitLongNameHandler for the specified + * MeasureUnit. It must be a MIXED unit. + * + * This function uses a fillIn instead of returning a pointer, because we + * want to fill in instances in a MemoryPool (which cannot adopt pointers it + * didn't create itself). + * + * @param loc The desired locale. + * @param mixedUnit The mixed measure unit to construct a + * MixedUnitLongNameHandler for. + * @param width Specifies the desired unit rendering. + * @param unitDisplayCase Specifies the desired grammatical case. If the + * specified case is not found, we fall back to nominative or no-case. + * @param rules Does not take ownership. + * @param parent Does not take ownership. + * @param fillIn Required. + */ + static void forMeasureUnit(const Locale &loc, + const MeasureUnit &mixedUnit, + const UNumberUnitWidth &width, + const char *unitDisplayCase, + const PluralRules *rules, + const MicroPropsGenerator *parent, + MixedUnitLongNameHandler *fillIn, + UErrorCode &status); + + /** + * Produces a plural-appropriate Modifier for a mixed unit: `quantity` is + * taken as the final smallest unit, while the larger unit values must be + * provided via `micros.mixedMeasures`. + */ + void processQuantity(DecimalQuantity &quantity, MicroProps µs, + UErrorCode &status) const override; + + // Required for ModifierStore. And ModifierStore is required by + // SimpleModifier constructor's last parameter. We assert his will never get + // called though. + const Modifier *getModifier(Signum signum, StandardPlural::Form plural) const override; + + private: + // Not owned + const PluralRules *rules; + + // Not owned + const MicroPropsGenerator *parent; + + // Total number of units in the MeasureUnit this handler was configured for: + // for "foot-and-inch", this will be 2. + int32_t fMixedUnitCount = 1; + + // Stores unit data for each of the individual units. For each unit, it + // stores ARRAY_LENGTH strings, as returned by getMeasureData. (Each unit + // with index `i` has ARRAY_LENGTH strings starting at index + // `i*ARRAY_LENGTH` in this array.) + LocalArray fMixedUnitData; + + // Formats the larger units of Mixed Unit measurements. + LocalizedNumberFormatter fNumberFormatter; + + // Joins mixed units together. + LocalPointer fListFormatter; + + MixedUnitLongNameHandler(const PluralRules *rules, const MicroPropsGenerator *parent) + : rules(rules), parent(parent) { + } + + MixedUnitLongNameHandler() : rules(nullptr), parent(nullptr) { + } + + // Allow macrosToMicroGenerator to call the private default constructor. + friend class NumberFormatterImpl; + + // Enables MemoryPool::emplaceBack(): requires access to + // the private constructors. + friend class MemoryPool; + + // For a mixed unit, returns a Modifier that takes only one parameter: the + // smallest and final unit of the set. The bigger units' values and labels + // get baked into this Modifier, together with the unit label of the final + // unit. + const Modifier *getMixedUnitModifier(DecimalQuantity &quantity, MicroProps µs, + UErrorCode &status) const; +}; + +/** + * A MicroPropsGenerator that multiplexes between different LongNameHandlers, + * depending on the outputUnit. + * + * See processQuantity() for the input requirements. + */ +class LongNameMultiplexer : public MicroPropsGenerator, public UMemory { + public: + // Produces a multiplexer for LongNameHandlers, one for each unit in + // `units`. An individual unit might be a mixed unit. + static LongNameMultiplexer *forMeasureUnits(const Locale &loc, + const MaybeStackVector &units, + const UNumberUnitWidth &width, + const char *unitDisplayCase, + const PluralRules *rules, + const MicroPropsGenerator *parent, + UErrorCode &status); + + // The output unit must be provided via `micros.outputUnit`, it must match + // one of the units provided to the factory function. + void processQuantity(DecimalQuantity &quantity, MicroProps µs, + UErrorCode &status) const override; + + private: + /** + * Because we only know which LongNameHandler we wish to call after calling + * earlier MicroPropsGenerators in the chain, LongNameMultiplexer keeps the + * parent link, while the LongNameHandlers are given no parents. + */ + MemoryPool fLongNameHandlers; + MemoryPool fMixedUnitHandlers; + // Unowned pointers to instances owned by MaybeStackVectors. + MaybeStackArray fHandlers; + // Each MeasureUnit corresponds to the same-index MicroPropsGenerator + // pointed to in fHandlers. + LocalArray fMeasureUnits; + + const MicroPropsGenerator *fParent; + + LongNameMultiplexer(const MicroPropsGenerator *parent) : fParent(parent) { + } +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif //__NUMBER_LONGNAMES_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_mapper.cpp b/deps/icu-small/source/i18n/number_mapper.cpp index 2f398d4a9392fb..4188e12a7c776d 100644 --- a/deps/icu-small/source/i18n/number_mapper.cpp +++ b/deps/icu-small/source/i18n/number_mapper.cpp @@ -1,525 +1,525 @@ -// © 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" - -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 // - ///////////// - - warehouse.affixProvider.setTo(properties, status); - macros.affixProvider = &warehouse.affixProvider.get(); - - /////////// - // UNITS // - /////////// - - bool useCurrency = ( - !properties.currency.isNull() || - !properties.currencyPluralInfo.fPtr.isNull() || - !properties.currencyUsage.isNull() || - warehouse.affixProvider.get().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 - } - - /////////////////////// - // 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; - // Not assigning directly to macros.roundingMode here: we change - // roundingMode if and when we also change macros.precision. - 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. - if (minInt == 0 && maxFrac != 0) { - minFrac = (minFrac < 0 || (minFrac == 0 && maxInt == 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) { - if (PatternStringUtils::ignoreRoundingIncrement(roundingIncrement, maxFrac)) { - precision = Precision::constructFraction(minFrac, maxFrac); - } else { - // Convert the double increment to an integer increment - precision = Precision::increment(roundingIncrement).withMinFraction(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()) { - macros.roundingMode = 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 > 0) { - 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 maxInt_ = properties.maximumIntegerDigits; - 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(); - } 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); - } else { - int maxSig_ = minInt_ + maxFrac_; - // Bug #20058: if maxInt_ > minInt_ > 1, then minInt_ should be 1. - if (maxInt_ > minInt_ && minInt_ > 1) { - minInt_ = 1; - } - int minSig_ = minInt_ + minFrac_; - // To avoid regression, maxSig is not reset when minInt_ set to 1. - // TODO: Reset maxSig_ = 1 + minFrac_ to follow the spec. - macros.precision = Precision::constructSignificant(minSig_, maxSig_); - } - macros.roundingMode = roundingMode; - } - } - - ////////////////////// - // COMPACT NOTATION // - ////////////////////// - - if (!properties.compactStyle.isNull()) { - if (properties.compactStyle.getNoError() == UNumberCompactStyle::UNUM_LONG) { - macros.notation = Notation::compactLong(); - } else { - macros.notation = Notation::compactShort(); - } - } - - ///////////////// - // 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 - || rounding_.fType == Precision::PrecisionType::RND_INCREMENT_ONE - || rounding_.fType == Precision::PrecisionType::RND_INCREMENT_FIVE) { - minFrac_ = rounding_.fUnion.increment.fMinFrac; - // If incrementRounding is used, maxFrac is set equal to minFrac - maxFrac_ = rounding_.fUnion.increment.fMinFrac; - // Convert the integer increment to a double - DecimalQuantity dq; - dq.setToLong(rounding_.fUnion.increment.fIncrement); - dq.adjustMagnitude(rounding_.fUnion.increment.fIncrementMagnitude); - increment_ = dq.toDouble(); - } 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& status) { - 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. - - // 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; - } - - // For declaring if this is a currency pattern, we need to look at the - // original pattern, not at any user-specified overrides. - isCurrencyPattern = ( - AffixUtils::hasCurrencySymbols(ppp, status) || - AffixUtils::hasCurrencySymbols(psp, status) || - AffixUtils::hasCurrencySymbols(npp, status) || - AffixUtils::hasCurrencySymbols(nsp, status) || - properties.currencyAsDecimal); - - fCurrencyAsDecimal = properties.currencyAsDecimal; -} - -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 { - return ( - (negSuffix != posSuffix) || - negPrefix.tempSubString(1) != posPrefix || - negPrefix.charAt(0) != u'-' - ); -} - -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 { - return isCurrencyPattern; -} - -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; -} - -bool PropertiesAffixPatternProvider::currencyAsDecimal() const { - return fCurrencyAsDecimal; -} - - -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(); -} - -bool CurrencyPluralInfoAffixProvider::currencyAsDecimal() const { - return affixesByPlural[StandardPlural::OTHER].currencyAsDecimal(); -} - - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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" + +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 // + ///////////// + + warehouse.affixProvider.setTo(properties, status); + macros.affixProvider = &warehouse.affixProvider.get(); + + /////////// + // UNITS // + /////////// + + bool useCurrency = ( + !properties.currency.isNull() || + !properties.currencyPluralInfo.fPtr.isNull() || + !properties.currencyUsage.isNull() || + warehouse.affixProvider.get().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 + } + + /////////////////////// + // 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; + // Not assigning directly to macros.roundingMode here: we change + // roundingMode if and when we also change macros.precision. + 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. + if (minInt == 0 && maxFrac != 0) { + minFrac = (minFrac < 0 || (minFrac == 0 && maxInt == 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) { + if (PatternStringUtils::ignoreRoundingIncrement(roundingIncrement, maxFrac)) { + precision = Precision::constructFraction(minFrac, maxFrac); + } else { + // Convert the double increment to an integer increment + precision = Precision::increment(roundingIncrement).withMinFraction(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()) { + macros.roundingMode = 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 > 0) { + 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 maxInt_ = properties.maximumIntegerDigits; + 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(); + } 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); + } else { + int maxSig_ = minInt_ + maxFrac_; + // Bug #20058: if maxInt_ > minInt_ > 1, then minInt_ should be 1. + if (maxInt_ > minInt_ && minInt_ > 1) { + minInt_ = 1; + } + int minSig_ = minInt_ + minFrac_; + // To avoid regression, maxSig is not reset when minInt_ set to 1. + // TODO: Reset maxSig_ = 1 + minFrac_ to follow the spec. + macros.precision = Precision::constructSignificant(minSig_, maxSig_); + } + macros.roundingMode = roundingMode; + } + } + + ////////////////////// + // COMPACT NOTATION // + ////////////////////// + + if (!properties.compactStyle.isNull()) { + if (properties.compactStyle.getNoError() == UNumberCompactStyle::UNUM_LONG) { + macros.notation = Notation::compactLong(); + } else { + macros.notation = Notation::compactShort(); + } + } + + ///////////////// + // 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 + || rounding_.fType == Precision::PrecisionType::RND_INCREMENT_ONE + || rounding_.fType == Precision::PrecisionType::RND_INCREMENT_FIVE) { + minFrac_ = rounding_.fUnion.increment.fMinFrac; + // If incrementRounding is used, maxFrac is set equal to minFrac + maxFrac_ = rounding_.fUnion.increment.fMinFrac; + // Convert the integer increment to a double + DecimalQuantity dq; + dq.setToLong(rounding_.fUnion.increment.fIncrement); + dq.adjustMagnitude(rounding_.fUnion.increment.fIncrementMagnitude); + increment_ = dq.toDouble(); + } 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& status) { + 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. + + // 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; + } + + // For declaring if this is a currency pattern, we need to look at the + // original pattern, not at any user-specified overrides. + isCurrencyPattern = ( + AffixUtils::hasCurrencySymbols(ppp, status) || + AffixUtils::hasCurrencySymbols(psp, status) || + AffixUtils::hasCurrencySymbols(npp, status) || + AffixUtils::hasCurrencySymbols(nsp, status) || + properties.currencyAsDecimal); + + fCurrencyAsDecimal = properties.currencyAsDecimal; +} + +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 { + return ( + (negSuffix != posSuffix) || + negPrefix.tempSubString(1) != posPrefix || + negPrefix.charAt(0) != u'-' + ); +} + +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 { + return isCurrencyPattern; +} + +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; +} + +bool PropertiesAffixPatternProvider::currencyAsDecimal() const { + return fCurrencyAsDecimal; +} + + +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(); +} + +bool CurrencyPluralInfoAffixProvider::currencyAsDecimal() const { + return affixesByPlural[StandardPlural::OTHER].currencyAsDecimal(); +} + + +#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 index 8879b7a94ea372..fd49903337ecef 100644 --- a/deps/icu-small/source/i18n/number_mapper.h +++ b/deps/icu-small/source/i18n/number_mapper.h @@ -1,266 +1,266 @@ -// © 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 AutoAffixPatternProvider; -class CurrencyPluralInfoAffixProvider; - - -class PropertiesAffixPatternProvider : public AffixPatternProvider, public UMemory { - public: - bool isBogus() const { - return fBogus; - } - - void setToBogus() { - fBogus = true; - } - - void setTo(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; - - bool currencyAsDecimal() const U_OVERRIDE; - - private: - UnicodeString posPrefix; - UnicodeString posSuffix; - UnicodeString negPrefix; - UnicodeString negSuffix; - bool isCurrencyPattern; - bool fCurrencyAsDecimal; - - PropertiesAffixPatternProvider() = default; // puts instance in valid but undefined state - - const UnicodeString& getStringInternal(int32_t flags) const; - - bool fBogus{true}; - - friend class AutoAffixPatternProvider; - friend class CurrencyPluralInfoAffixProvider; -}; - - -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; - - bool currencyAsDecimal() const U_OVERRIDE; - - private: - PropertiesAffixPatternProvider affixesByPlural[StandardPlural::COUNT]; - - CurrencyPluralInfoAffixProvider() = default; - - bool fBogus{true}; - - friend class AutoAffixPatternProvider; -}; - - -class AutoAffixPatternProvider { - public: - inline AutoAffixPatternProvider() = default; - - inline AutoAffixPatternProvider(const DecimalFormatProperties& properties, UErrorCode& status) { - setTo(properties, status); - } - - inline void setTo(const DecimalFormatProperties& properties, UErrorCode& status) { - if (properties.currencyPluralInfo.fPtr.isNull()) { - propertiesAPP.setTo(properties, status); - currencyPluralInfoAPP.setToBogus(); - } else { - propertiesAPP.setToBogus(); - currencyPluralInfoAPP.setTo(*properties.currencyPluralInfo.fPtr, properties, status); - } - } - - inline void setTo(const AffixPatternProvider* provider, UErrorCode& status) { - if (auto ptr = dynamic_cast(provider)) { - propertiesAPP = *ptr; - } else if (auto ptr = dynamic_cast(provider)) { - currencyPluralInfoAPP = *ptr; - } else { - status = U_INTERNAL_PROGRAM_ERROR; - } - } - - inline const AffixPatternProvider& get() const { - if (!currencyPluralInfoAPP.isBogus()) { - return currencyPluralInfoAPP; - } else { - return propertiesAPP; - } - } - - private: - PropertiesAffixPatternProvider propertiesAPP; - CurrencyPluralInfoAffixProvider currencyPluralInfoAPP; -}; - - -/** - * A struct for ownership of a few objects needed for formatting. - */ -struct DecimalFormatWarehouse : public UMemory { - AutoAffixPatternProvider affixProvider; - LocalPointer rules; -}; - - -/** -* Internal fields for DecimalFormat. -* TODO: Make some of these fields by value instead of by LocalPointer? -*/ -struct DecimalFormatFields : public UMemory { - - DecimalFormatFields() {} - - DecimalFormatFields(const DecimalFormatProperties& propsToCopy) - : properties(propsToCopy) {} - - /** The property bag corresponding to user-specified settings and settings from the pattern string. */ - DecimalFormatProperties 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. - */ - LocalizedNumberFormatter 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. */ - DecimalFormatProperties 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 */ +// © 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 AutoAffixPatternProvider; +class CurrencyPluralInfoAffixProvider; + + +class PropertiesAffixPatternProvider : public AffixPatternProvider, public UMemory { + public: + bool isBogus() const { + return fBogus; + } + + void setToBogus() { + fBogus = true; + } + + void setTo(const DecimalFormatProperties& properties, UErrorCode& status); + + // AffixPatternProvider Methods: + + char16_t charAt(int32_t flags, int32_t i) const override; + + int32_t length(int32_t flags) const override; + + UnicodeString getString(int32_t flags) const override; + + bool hasCurrencySign() const override; + + bool positiveHasPlusSign() const override; + + bool hasNegativeSubpattern() const override; + + bool negativeHasMinusSign() const override; + + bool containsSymbolType(AffixPatternType, UErrorCode&) const override; + + bool hasBody() const override; + + bool currencyAsDecimal() const override; + + private: + UnicodeString posPrefix; + UnicodeString posSuffix; + UnicodeString negPrefix; + UnicodeString negSuffix; + bool isCurrencyPattern; + bool fCurrencyAsDecimal; + + PropertiesAffixPatternProvider() = default; // puts instance in valid but undefined state + + const UnicodeString& getStringInternal(int32_t flags) const; + + bool fBogus{true}; + + friend class AutoAffixPatternProvider; + friend class CurrencyPluralInfoAffixProvider; +}; + + +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 override; + + int32_t length(int32_t flags) const override; + + UnicodeString getString(int32_t flags) const override; + + bool hasCurrencySign() const override; + + bool positiveHasPlusSign() const override; + + bool hasNegativeSubpattern() const override; + + bool negativeHasMinusSign() const override; + + bool containsSymbolType(AffixPatternType, UErrorCode&) const override; + + bool hasBody() const override; + + bool currencyAsDecimal() const override; + + private: + PropertiesAffixPatternProvider affixesByPlural[StandardPlural::COUNT]; + + CurrencyPluralInfoAffixProvider() = default; + + bool fBogus{true}; + + friend class AutoAffixPatternProvider; +}; + + +class AutoAffixPatternProvider { + public: + inline AutoAffixPatternProvider() = default; + + inline AutoAffixPatternProvider(const DecimalFormatProperties& properties, UErrorCode& status) { + setTo(properties, status); + } + + inline void setTo(const DecimalFormatProperties& properties, UErrorCode& status) { + if (properties.currencyPluralInfo.fPtr.isNull()) { + propertiesAPP.setTo(properties, status); + currencyPluralInfoAPP.setToBogus(); + } else { + propertiesAPP.setToBogus(); + currencyPluralInfoAPP.setTo(*properties.currencyPluralInfo.fPtr, properties, status); + } + } + + inline void setTo(const AffixPatternProvider* provider, UErrorCode& status) { + if (auto ptr = dynamic_cast(provider)) { + propertiesAPP = *ptr; + } else if (auto ptr = dynamic_cast(provider)) { + currencyPluralInfoAPP = *ptr; + } else { + status = U_INTERNAL_PROGRAM_ERROR; + } + } + + inline const AffixPatternProvider& get() const { + if (!currencyPluralInfoAPP.isBogus()) { + return currencyPluralInfoAPP; + } else { + return propertiesAPP; + } + } + + private: + PropertiesAffixPatternProvider propertiesAPP; + CurrencyPluralInfoAffixProvider currencyPluralInfoAPP; +}; + + +/** + * A struct for ownership of a few objects needed for formatting. + */ +struct DecimalFormatWarehouse : public UMemory { + AutoAffixPatternProvider affixProvider; + LocalPointer rules; +}; + + +/** +* Internal fields for DecimalFormat. +* TODO: Make some of these fields by value instead of by LocalPointer? +*/ +struct DecimalFormatFields : public UMemory { + + DecimalFormatFields() {} + + DecimalFormatFields(const DecimalFormatProperties& propsToCopy) + : properties(propsToCopy) {} + + /** The property bag corresponding to user-specified settings and settings from the pattern string. */ + DecimalFormatProperties 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. + */ + LocalizedNumberFormatter 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. */ + DecimalFormatProperties 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 index c34e7c17e97aa5..838789fa2b0c7d 100644 --- a/deps/icu-small/source/i18n/number_microprops.h +++ b/deps/icu-small/source/i18n/number_microprops.h @@ -1,191 +1,197 @@ -// © 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" -#include "util.h" - -U_NAMESPACE_BEGIN namespace number { -namespace impl { - -/** - * A copyable container for the integer values of mixed unit measurements. - * - * If memory allocation fails during copying, no values are copied and status is - * set to U_MEMORY_ALLOCATION_ERROR. - */ -class IntMeasures : public MaybeStackArray { - public: - /** - * Default constructor initializes with internal T[stackCapacity] buffer. - * - * Stack Capacity: most mixed units are expected to consist of two or three - * subunits, so one or two integer measures should be enough. - */ - IntMeasures() : MaybeStackArray() {} - - /** - * Copy constructor. - * - * If memory allocation fails during copying, no values are copied and - * status is set to U_MEMORY_ALLOCATION_ERROR. - */ - IntMeasures(const IntMeasures &other) : MaybeStackArray() { - this->operator=(other); - } - - // Assignment operator - IntMeasures &operator=(const IntMeasures &rhs) { - if (this == &rhs) { - return *this; - } - copyFrom(rhs, status); - return *this; - } - - /** Move constructor */ - IntMeasures(IntMeasures &&src) = default; - - /** Move assignment */ - IntMeasures &operator=(IntMeasures &&src) = default; - - UErrorCode status = U_ZERO_ERROR; -}; - -/** - * MicroProps is the first MicroPropsGenerator that should be should be called, - * producing an initialized MicroProps instance that will be passed on and - * modified throughout the rest of the chain of MicroPropsGenerator instances. - */ -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; - char nsName[9]; - - // Currency symbol to be used as the decimal separator - UnicodeString currencyAsDecimal = ICU_Utility::makeBogusString(); - - // No ownership: must point at a string which will outlive MicroProps - // instances, e.g. a string with static storage duration, or just a string - // that will never be deallocated or modified. - const char *gender; - - // Note: This struct has no direct ownership of the following pointers. - const DecimalFormatSymbols* symbols; - - // Pointers to Modifiers provided by the number formatting pipeline (when - // the value is known): - - // A Modifier provided by LongNameHandler, used for currency long names and - // units. If there is no LongNameHandler needed, this should be an - // EmptyModifier. (This is typically the third modifier applied.) - const Modifier* modOuter; - // A Modifier for short currencies and compact notation. (This is typically - // the second modifier applied.) - const Modifier* modMiddle = nullptr; - // A Modifier provided by ScientificHandler, used for scientific notation. - // This is typically the first modifier applied. - const Modifier* modInner; - - // The following "helper" fields may optionally be used during the MicroPropsGenerator. - // They live here to retain memory. - struct { - // The ScientificModifier for which ScientificHandler is responsible. - // ScientificHandler::processQuantity() modifies this Modifier. - ScientificModifier scientificModifier; - // EmptyModifier used for modOuter - EmptyModifier emptyWeakModifier{false}; - // EmptyModifier used for modInner - EmptyModifier emptyStrongModifier{true}; - MultiplierFormatHandler multiplier; - // A Modifier used for Mixed Units. When formatting mixed units, - // LongNameHandler assigns this Modifier. - SimpleModifier mixedUnitModifier; - } helpers; - - // The MeasureUnit with which the output is represented. May also have - // UMEASURE_UNIT_MIXED complexity, in which case mixedMeasures comes into - // play. - MeasureUnit outputUnit; - - // Contains all the values of each unit in mixed units. For quantity (which is the floating value of - // the smallest unit in the mixed unit), the value stores in `quantity`. - // NOTE: the value of quantity in `mixedMeasures` will be left unset. - IntMeasures mixedMeasures; - - // Points to quantity position, -1 if the position is not set yet. - int32_t indexOfQuantity = -1; - - // Number of mixedMeasures that have been populated - int32_t mixedMeasuresCount = 0; - - MicroProps() = default; - - MicroProps(const MicroProps& other) = default; - - MicroProps& operator=(const MicroProps& other) = default; - - /** - * As MicroProps is the "base instance", this implementation of - * MicroPropsGenerator::processQuantity() just ensures that the output - * `micros` is correctly initialized. - * - * For the "safe" invocation of this function, micros must not be *this, - * such that a copy of the base instance is made. For the "unsafe" path, - * this function can be used only once, because the base MicroProps instance - * will be modified and thus not be available for re-use. - * - * @param quantity The quantity for consideration and optional mutation. - * @param micros The MicroProps instance to populate. If this parameter is - * not already `*this`, it will be overwritten with a copy of `*this`. - */ - void processQuantity(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const U_OVERRIDE { - (void) quantity; - (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. - U_ASSERT(!exhausted); - micros = *this; - } - } - - private: - // Internal fields: - bool exhausted = false; -}; - -} // namespace impl -} // namespace number -U_NAMESPACE_END - -#endif // __NUMBER_MICROPROPS_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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" +#include "util.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +/** + * A copyable container for the integer values of mixed unit measurements. + * + * If memory allocation fails during copying, no values are copied and status is + * set to U_MEMORY_ALLOCATION_ERROR. + */ +class IntMeasures : public MaybeStackArray { + public: + /** + * Default constructor initializes with internal T[stackCapacity] buffer. + * + * Stack Capacity: most mixed units are expected to consist of two or three + * subunits, so one or two integer measures should be enough. + */ + IntMeasures() : MaybeStackArray() {} + + /** + * Copy constructor. + * + * If memory allocation fails during copying, no values are copied and + * status is set to U_MEMORY_ALLOCATION_ERROR. + */ + IntMeasures(const IntMeasures &other) : MaybeStackArray() { + this->operator=(other); + } + + // Assignment operator + IntMeasures &operator=(const IntMeasures &rhs) { + if (this == &rhs) { + return *this; + } + copyFrom(rhs, status); + return *this; + } + + /** Move constructor */ + IntMeasures(IntMeasures &&src) = default; + + /** Move assignment */ + IntMeasures &operator=(IntMeasures &&src) = default; + + UErrorCode status = U_ZERO_ERROR; +}; + +struct SimpleMicroProps : public UMemory { + Grouper grouping; + bool useCurrency = false; + UNumberDecimalSeparatorDisplay decimal = UNUM_DECIMAL_SEPARATOR_AUTO; + + // Currency symbol to be used as the decimal separator + UnicodeString currencyAsDecimal = ICU_Utility::makeBogusString(); + + // Note: This struct has no direct ownership of the following pointer. + const DecimalFormatSymbols* symbols = nullptr; +}; + +/** + * MicroProps is the first MicroPropsGenerator that should be should be called, + * producing an initialized MicroProps instance that will be passed on and + * modified throughout the rest of the chain of MicroPropsGenerator instances. + */ +struct MicroProps : public MicroPropsGenerator { + SimpleMicroProps simple; + + // NOTE: All of these fields are properly initialized in NumberFormatterImpl. + RoundingImpl rounder; + Padder padding; + IntegerWidth integerWidth; + UNumberSignDisplay sign; + char nsName[9]; + + // No ownership: must point at a string which will outlive MicroProps + // instances, e.g. a string with static storage duration, or just a string + // that will never be deallocated or modified. + const char *gender; + + // Note: This struct has no direct ownership of the following pointers. + + // Pointers to Modifiers provided by the number formatting pipeline (when + // the value is known): + + // A Modifier provided by LongNameHandler, used for currency long names and + // units. If there is no LongNameHandler needed, this should be an + // EmptyModifier. (This is typically the third modifier applied.) + const Modifier* modOuter; + // A Modifier for short currencies and compact notation. (This is typically + // the second modifier applied.) + const Modifier* modMiddle = nullptr; + // A Modifier provided by ScientificHandler, used for scientific notation. + // This is typically the first modifier applied. + const Modifier* modInner; + + // The following "helper" fields may optionally be used during the MicroPropsGenerator. + // They live here to retain memory. + struct { + // The ScientificModifier for which ScientificHandler is responsible. + // ScientificHandler::processQuantity() modifies this Modifier. + ScientificModifier scientificModifier; + // EmptyModifier used for modOuter + EmptyModifier emptyWeakModifier{false}; + // EmptyModifier used for modInner + EmptyModifier emptyStrongModifier{true}; + MultiplierFormatHandler multiplier; + // A Modifier used for Mixed Units. When formatting mixed units, + // LongNameHandler assigns this Modifier. + SimpleModifier mixedUnitModifier; + } helpers; + + // The MeasureUnit with which the output is represented. May also have + // UMEASURE_UNIT_MIXED complexity, in which case mixedMeasures comes into + // play. + MeasureUnit outputUnit; + + // Contains all the values of each unit in mixed units. For quantity (which is the floating value of + // the smallest unit in the mixed unit), the value stores in `quantity`. + // NOTE: the value of quantity in `mixedMeasures` will be left unset. + IntMeasures mixedMeasures; + + // Points to quantity position, -1 if the position is not set yet. + int32_t indexOfQuantity = -1; + + // Number of mixedMeasures that have been populated + int32_t mixedMeasuresCount = 0; + + MicroProps() = default; + + MicroProps(const MicroProps& other) = default; + + MicroProps& operator=(const MicroProps& other) = default; + + /** + * As MicroProps is the "base instance", this implementation of + * MicroPropsGenerator::processQuantity() just ensures that the output + * `micros` is correctly initialized. + * + * For the "safe" invocation of this function, micros must not be *this, + * such that a copy of the base instance is made. For the "unsafe" path, + * this function can be used only once, because the base MicroProps instance + * will be modified and thus not be available for re-use. + * + * @param quantity The quantity for consideration and optional mutation. + * @param micros The MicroProps instance to populate. If this parameter is + * not already `*this`, it will be overwritten with a copy of `*this`. + */ + void processQuantity(DecimalQuantity &quantity, MicroProps µs, + UErrorCode &status) const override { + (void) quantity; + (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. + U_ASSERT(!exhausted); + 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 092b66ff579363..96c3201de27a82 100644 --- a/deps/icu-small/source/i18n/number_modifiers.cpp +++ b/deps/icu-small/source/i18n/number_modifiers.cpp @@ -1,485 +1,494 @@ -// © 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 - -#include "umutex.h" -#include "ucln_cmn.h" -#include "ucln_in.h" -#include "number_modifiers.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - -namespace { - -// TODO: This is copied from simpleformatter.cpp -const int32_t ARG_NUM_LIMIT = 0x100; - -// These are the default currency spacing UnicodeSets in CLDR. -// Pre-compute them for performance. -// The Java unit test testCurrencySpacingPatternStability() will start failing if these change in CLDR. -icu::UInitOnce gDefaultCurrencySpacingInitOnce {}; - -UnicodeSet *UNISET_DIGIT = nullptr; -UnicodeSet *UNISET_NOTSZ = nullptr; - -UBool U_CALLCONV cleanupDefaultCurrencySpacing() { - delete UNISET_DIGIT; - UNISET_DIGIT = nullptr; - delete UNISET_NOTSZ; - UNISET_NOTSZ = nullptr; - gDefaultCurrencySpacingInitOnce.reset(); - return true; -} - -void U_CALLCONV initDefaultCurrencySpacing(UErrorCode &status) { - ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY_SPACING, cleanupDefaultCurrencySpacing); - UNISET_DIGIT = new UnicodeSet(UnicodeString(u"[:digit:]"), status); - UNISET_NOTSZ = new UnicodeSet(UnicodeString(u"[[:^S:]&[:^Z:]]"), status); - if (UNISET_DIGIT == nullptr || UNISET_NOTSZ == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - UNISET_DIGIT->freeze(); - UNISET_NOTSZ->freeze(); -} - -} // namespace - - -Modifier::~Modifier() = default; - -Modifier::Parameters::Parameters() - : obj(nullptr) {} - -Modifier::Parameters::Parameters( - const ModifierStore* _obj, Signum _signum, StandardPlural::Form _plural) - : obj(_obj), signum(_signum), plural(_plural) {} - -ModifierStore::~ModifierStore() = default; - -AdoptingModifierStore::~AdoptingModifierStore() { - for (const Modifier *mod : mods) { - delete mod; - } -} - - -int32_t ConstantAffixModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex, - UErrorCode &status) const { - // Insert the suffix first since inserting the prefix will change the rightIndex - int length = output.insert(rightIndex, fSuffix, fField, status); - length += output.insert(leftIndex, fPrefix, fField, status); - return length; -} - -int32_t ConstantAffixModifier::getPrefixLength() const { - return fPrefix.length(); -} - -int32_t ConstantAffixModifier::getCodePointCount() const { - return fPrefix.countChar32() + fSuffix.countChar32(); -} - -bool ConstantAffixModifier::isStrong() const { - return fStrong; -} - -bool ConstantAffixModifier::containsField(Field field) const { - (void)field; - // This method is not currently used. - UPRV_UNREACHABLE_EXIT; -} - -void ConstantAffixModifier::getParameters(Parameters& output) const { - (void)output; - // This method is not currently used. - UPRV_UNREACHABLE_EXIT; -} - -bool ConstantAffixModifier::semanticallyEquivalent(const Modifier& other) const { - auto* _other = dynamic_cast(&other); - if (_other == nullptr) { - return false; - } - return fPrefix == _other->fPrefix - && fSuffix == _other->fSuffix - && fField == _other->fField - && fStrong == _other->fStrong; -} - - -SimpleModifier::SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong) - : SimpleModifier(simpleFormatter, field, strong, {}) {} - -SimpleModifier::SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong, - const Modifier::Parameters parameters) - : fCompiledPattern(simpleFormatter.compiledPattern), fField(field), fStrong(strong), - fParameters(parameters) { - int32_t argLimit = SimpleFormatter::getArgumentLimit( - fCompiledPattern.getBuffer(), fCompiledPattern.length()); - if (argLimit == 0) { - // No arguments in compiled pattern - fPrefixLength = fCompiledPattern.charAt(1) - ARG_NUM_LIMIT; - U_ASSERT(2 + fPrefixLength == fCompiledPattern.length()); - // Set suffixOffset = -1 to indicate no arguments in compiled pattern. - fSuffixOffset = -1; - fSuffixLength = 0; - } else { - U_ASSERT(argLimit == 1); - if (fCompiledPattern.charAt(1) != 0) { - // Found prefix - fPrefixLength = fCompiledPattern.charAt(1) - ARG_NUM_LIMIT; - fSuffixOffset = 3 + fPrefixLength; - } else { - // No prefix - fPrefixLength = 0; - fSuffixOffset = 2; - } - if (3 + fPrefixLength < fCompiledPattern.length()) { - // Found suffix - fSuffixLength = fCompiledPattern.charAt(fSuffixOffset) - ARG_NUM_LIMIT; - } else { - // No suffix - fSuffixLength = 0; - } - } -} - -SimpleModifier::SimpleModifier() - : fField(kUndefinedField), fStrong(false), fPrefixLength(0), fSuffixLength(0) { -} - -int32_t SimpleModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex, - UErrorCode &status) const { - return formatAsPrefixSuffix(output, leftIndex, rightIndex, status); -} - -int32_t SimpleModifier::getPrefixLength() const { - return fPrefixLength; -} - -int32_t SimpleModifier::getCodePointCount() const { - int32_t count = 0; - if (fPrefixLength > 0) { - count += fCompiledPattern.countChar32(2, fPrefixLength); - } - if (fSuffixLength > 0) { - count += fCompiledPattern.countChar32(1 + fSuffixOffset, fSuffixLength); - } - return count; -} - -bool SimpleModifier::isStrong() const { - return fStrong; -} - -bool SimpleModifier::containsField(Field field) const { - (void)field; - // This method is not currently used. - UPRV_UNREACHABLE_EXIT; -} - -void SimpleModifier::getParameters(Parameters& output) const { - output = fParameters; -} - -bool SimpleModifier::semanticallyEquivalent(const Modifier& other) const { - auto* _other = dynamic_cast(&other); - if (_other == nullptr) { - return false; - } - if (fParameters.obj != nullptr) { - return fParameters.obj == _other->fParameters.obj; - } - return fCompiledPattern == _other->fCompiledPattern - && fField == _other->fField - && fStrong == _other->fStrong; -} - - -int32_t -SimpleModifier::formatAsPrefixSuffix(FormattedStringBuilder &result, int32_t startIndex, int32_t endIndex, - UErrorCode &status) const { - if (fSuffixOffset == -1 && fPrefixLength + fSuffixLength > 0) { - // There is no argument for the inner number; overwrite the entire segment with our string. - return result.splice(startIndex, endIndex, fCompiledPattern, 2, 2 + fPrefixLength, fField, status); - } else { - if (fPrefixLength > 0) { - result.insert(startIndex, fCompiledPattern, 2, 2 + fPrefixLength, fField, status); - } - if (fSuffixLength > 0) { - result.insert( - endIndex + fPrefixLength, - fCompiledPattern, - 1 + fSuffixOffset, - 1 + fSuffixOffset + fSuffixLength, - fField, - status); - } - return fPrefixLength + fSuffixLength; - } -} - - -int32_t -SimpleModifier::formatTwoArgPattern(const SimpleFormatter& compiled, FormattedStringBuilder& result, - int32_t index, int32_t* outPrefixLength, int32_t* outSuffixLength, - Field field, UErrorCode& status) { - const UnicodeString& compiledPattern = compiled.compiledPattern; - int32_t argLimit = SimpleFormatter::getArgumentLimit( - compiledPattern.getBuffer(), compiledPattern.length()); - if (argLimit != 2) { - status = U_INTERNAL_PROGRAM_ERROR; - return 0; - } - int32_t offset = 1; // offset into compiledPattern - int32_t length = 0; // chars added to result - - int32_t prefixLength = compiledPattern.charAt(offset); - offset++; - if (prefixLength < ARG_NUM_LIMIT) { - // No prefix - prefixLength = 0; - } else { - prefixLength -= ARG_NUM_LIMIT; - result.insert(index + length, compiledPattern, offset, offset + prefixLength, field, status); - offset += prefixLength; - length += prefixLength; - offset++; - } - - int32_t infixLength = compiledPattern.charAt(offset); - offset++; - if (infixLength < ARG_NUM_LIMIT) { - // No infix - infixLength = 0; - } else { - infixLength -= ARG_NUM_LIMIT; - result.insert(index + length, compiledPattern, offset, offset + infixLength, field, status); - offset += infixLength; - length += infixLength; - offset++; - } - - int32_t suffixLength; - if (offset == compiledPattern.length()) { - // No suffix - suffixLength = 0; - } else { - suffixLength = compiledPattern.charAt(offset) - ARG_NUM_LIMIT; - offset++; - result.insert(index + length, compiledPattern, offset, offset + suffixLength, field, status); - length += suffixLength; - } - - *outPrefixLength = prefixLength; - *outSuffixLength = suffixLength; - - return length; -} - - -int32_t ConstantMultiFieldModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex, - UErrorCode &status) const { - int32_t length = output.insert(leftIndex, fPrefix, status); - if (fOverwrite) { - length += output.splice( - leftIndex + length, - rightIndex + length, - UnicodeString(), 0, 0, - kUndefinedField, status); - } - length += output.insert(rightIndex + length, fSuffix, status); - return length; -} - -int32_t ConstantMultiFieldModifier::getPrefixLength() const { - return fPrefix.length(); -} - -int32_t ConstantMultiFieldModifier::getCodePointCount() const { - return fPrefix.codePointCount() + fSuffix.codePointCount(); -} - -bool ConstantMultiFieldModifier::isStrong() const { - return fStrong; -} - -bool ConstantMultiFieldModifier::containsField(Field field) const { - return fPrefix.containsField(field) || fSuffix.containsField(field); -} - -void ConstantMultiFieldModifier::getParameters(Parameters& output) const { - output = fParameters; -} - -bool ConstantMultiFieldModifier::semanticallyEquivalent(const Modifier& other) const { - auto* _other = dynamic_cast(&other); - if (_other == nullptr) { - return false; - } - if (fParameters.obj != nullptr) { - return fParameters.obj == _other->fParameters.obj; - } - return fPrefix.contentEquals(_other->fPrefix) - && fSuffix.contentEquals(_other->fSuffix) - && fOverwrite == _other->fOverwrite - && fStrong == _other->fStrong; -} - - -CurrencySpacingEnabledModifier::CurrencySpacingEnabledModifier(const FormattedStringBuilder &prefix, - const FormattedStringBuilder &suffix, - bool overwrite, - bool strong, - const DecimalFormatSymbols &symbols, - UErrorCode &status) - : ConstantMultiFieldModifier(prefix, suffix, overwrite, strong) { - // Check for currency spacing. Do not build the UnicodeSets unless there is - // a currency code point at a boundary. - if (prefix.length() > 0 && prefix.fieldAt(prefix.length() - 1) == Field(UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD)) { - int prefixCp = prefix.getLastCodePoint(); - UnicodeSet prefixUnicodeSet = getUnicodeSet(symbols, IN_CURRENCY, PREFIX, status); - if (prefixUnicodeSet.contains(prefixCp)) { - fAfterPrefixUnicodeSet = getUnicodeSet(symbols, IN_NUMBER, PREFIX, status); - fAfterPrefixUnicodeSet.freeze(); - fAfterPrefixInsert = getInsertString(symbols, PREFIX, status); - } else { - fAfterPrefixUnicodeSet.setToBogus(); - fAfterPrefixInsert.setToBogus(); - } - } else { - fAfterPrefixUnicodeSet.setToBogus(); - fAfterPrefixInsert.setToBogus(); - } - if (suffix.length() > 0 && suffix.fieldAt(0) == Field(UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD)) { - int suffixCp = suffix.getFirstCodePoint(); - UnicodeSet suffixUnicodeSet = getUnicodeSet(symbols, IN_CURRENCY, SUFFIX, status); - if (suffixUnicodeSet.contains(suffixCp)) { - fBeforeSuffixUnicodeSet = getUnicodeSet(symbols, IN_NUMBER, SUFFIX, status); - fBeforeSuffixUnicodeSet.freeze(); - fBeforeSuffixInsert = getInsertString(symbols, SUFFIX, status); - } else { - fBeforeSuffixUnicodeSet.setToBogus(); - fBeforeSuffixInsert.setToBogus(); - } - } else { - fBeforeSuffixUnicodeSet.setToBogus(); - fBeforeSuffixInsert.setToBogus(); - } -} - -int32_t CurrencySpacingEnabledModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex, - UErrorCode &status) const { - // Currency spacing logic - int length = 0; - if (rightIndex - leftIndex > 0 && !fAfterPrefixUnicodeSet.isBogus() && - fAfterPrefixUnicodeSet.contains(output.codePointAt(leftIndex))) { - // TODO: Should we use the CURRENCY field here? - length += output.insert( - leftIndex, - fAfterPrefixInsert, - kUndefinedField, - status); - } - if (rightIndex - leftIndex > 0 && !fBeforeSuffixUnicodeSet.isBogus() && - fBeforeSuffixUnicodeSet.contains(output.codePointBefore(rightIndex))) { - // TODO: Should we use the CURRENCY field here? - length += output.insert( - rightIndex + length, - fBeforeSuffixInsert, - kUndefinedField, - status); - } - - // Call super for the remaining logic - length += ConstantMultiFieldModifier::apply(output, leftIndex, rightIndex + length, status); - return length; -} - -int32_t -CurrencySpacingEnabledModifier::applyCurrencySpacing(FormattedStringBuilder &output, int32_t prefixStart, - int32_t prefixLen, int32_t suffixStart, - int32_t suffixLen, - const DecimalFormatSymbols &symbols, - UErrorCode &status) { - int length = 0; - bool hasPrefix = (prefixLen > 0); - bool hasSuffix = (suffixLen > 0); - bool hasNumber = (suffixStart - prefixStart - prefixLen > 0); // could be empty string - if (hasPrefix && hasNumber) { - length += applyCurrencySpacingAffix(output, prefixStart + prefixLen, PREFIX, symbols, status); - } - if (hasSuffix && hasNumber) { - length += applyCurrencySpacingAffix(output, suffixStart + length, SUFFIX, symbols, status); - } - return length; -} - -int32_t -CurrencySpacingEnabledModifier::applyCurrencySpacingAffix(FormattedStringBuilder &output, int32_t index, - EAffix affix, - const DecimalFormatSymbols &symbols, - UErrorCode &status) { - // NOTE: For prefix, output.fieldAt(index-1) gets the last field type in the prefix. - // This works even if the last code point in the prefix is 2 code units because the - // field value gets populated to both indices in the field array. - Field affixField = (affix == PREFIX) ? output.fieldAt(index - 1) : output.fieldAt(index); - if (affixField != Field(UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD)) { - return 0; - } - int affixCp = (affix == PREFIX) ? output.codePointBefore(index) : output.codePointAt(index); - UnicodeSet affixUniset = getUnicodeSet(symbols, IN_CURRENCY, affix, status); - if (!affixUniset.contains(affixCp)) { - return 0; - } - int numberCp = (affix == PREFIX) ? output.codePointAt(index) : output.codePointBefore(index); - UnicodeSet numberUniset = getUnicodeSet(symbols, IN_NUMBER, affix, status); - if (!numberUniset.contains(numberCp)) { - return 0; - } - UnicodeString spacingString = getInsertString(symbols, affix, status); - - // NOTE: This next line *inserts* the spacing string, triggering an arraycopy. - // It would be more efficient if this could be done before affixes were attached, - // so that it could be prepended/appended instead of inserted. - // However, the build code path is more efficient, and this is the most natural - // place to put currency spacing in the non-build code path. - // TODO: Should we use the CURRENCY field here? - return output.insert(index, spacingString, kUndefinedField, status); -} - -UnicodeSet -CurrencySpacingEnabledModifier::getUnicodeSet(const DecimalFormatSymbols &symbols, EPosition position, - EAffix affix, UErrorCode &status) { - // Ensure the static defaults are initialized: - umtx_initOnce(gDefaultCurrencySpacingInitOnce, &initDefaultCurrencySpacing, status); - if (U_FAILURE(status)) { - return UnicodeSet(); - } - - const UnicodeString& pattern = symbols.getPatternForCurrencySpacing( - position == IN_CURRENCY ? UNUM_CURRENCY_MATCH : UNUM_CURRENCY_SURROUNDING_MATCH, - affix == SUFFIX, - status); - if (pattern.compare(u"[:digit:]", -1) == 0) { - return *UNISET_DIGIT; - } else if (pattern.compare(u"[[:^S:]&[:^Z:]]", -1) == 0) { - return *UNISET_NOTSZ; - } else { - return UnicodeSet(pattern, status); - } -} - -UnicodeString -CurrencySpacingEnabledModifier::getInsertString(const DecimalFormatSymbols &symbols, EAffix affix, - UErrorCode &status) { - return symbols.getPatternForCurrencySpacing(UNUM_CURRENCY_INSERT, affix == SUFFIX, status); -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include "umutex.h" +#include "ucln_cmn.h" +#include "ucln_in.h" +#include "number_modifiers.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +namespace { + +// TODO: This is copied from simpleformatter.cpp +const int32_t ARG_NUM_LIMIT = 0x100; + +// These are the default currency spacing UnicodeSets in CLDR. +// Pre-compute them for performance. +// The Java unit test testCurrencySpacingPatternStability() will start failing if these change in CLDR. +icu::UInitOnce gDefaultCurrencySpacingInitOnce {}; + +UnicodeSet *UNISET_DIGIT = nullptr; +UnicodeSet *UNISET_NOTSZ = nullptr; + +UBool U_CALLCONV cleanupDefaultCurrencySpacing() { + delete UNISET_DIGIT; + UNISET_DIGIT = nullptr; + delete UNISET_NOTSZ; + UNISET_NOTSZ = nullptr; + gDefaultCurrencySpacingInitOnce.reset(); + return true; +} + +void U_CALLCONV initDefaultCurrencySpacing(UErrorCode &status) { + ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY_SPACING, cleanupDefaultCurrencySpacing); + UNISET_DIGIT = new UnicodeSet(UnicodeString(u"[:digit:]"), status); + UNISET_NOTSZ = new UnicodeSet(UnicodeString(u"[[:^S:]&[:^Z:]]"), status); + if (UNISET_DIGIT == nullptr || UNISET_NOTSZ == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + UNISET_DIGIT->freeze(); + UNISET_NOTSZ->freeze(); +} + +} // namespace + + +Modifier::~Modifier() = default; + +Modifier::Parameters::Parameters() + : obj(nullptr) {} + +Modifier::Parameters::Parameters( + const ModifierStore* _obj, Signum _signum, StandardPlural::Form _plural) + : obj(_obj), signum(_signum), plural(_plural) {} + +ModifierStore::~ModifierStore() = default; + +AdoptingSignumModifierStore::~AdoptingSignumModifierStore() { + for (const Modifier *mod : mods) { + delete mod; + } +} + +AdoptingSignumModifierStore& +AdoptingSignumModifierStore::operator=(AdoptingSignumModifierStore&& other) noexcept { + for (size_t i=0; imods[i] = other.mods[i]; + other.mods[i] = nullptr; + } + return *this; +} + + +int32_t ConstantAffixModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex, + UErrorCode &status) const { + // Insert the suffix first since inserting the prefix will change the rightIndex + int length = output.insert(rightIndex, fSuffix, fField, status); + length += output.insert(leftIndex, fPrefix, fField, status); + return length; +} + +int32_t ConstantAffixModifier::getPrefixLength() const { + return fPrefix.length(); +} + +int32_t ConstantAffixModifier::getCodePointCount() const { + return fPrefix.countChar32() + fSuffix.countChar32(); +} + +bool ConstantAffixModifier::isStrong() const { + return fStrong; +} + +bool ConstantAffixModifier::containsField(Field field) const { + (void)field; + // This method is not currently used. + UPRV_UNREACHABLE_EXIT; +} + +void ConstantAffixModifier::getParameters(Parameters& output) const { + (void)output; + // This method is not currently used. + UPRV_UNREACHABLE_EXIT; +} + +bool ConstantAffixModifier::semanticallyEquivalent(const Modifier& other) const { + auto* _other = dynamic_cast(&other); + if (_other == nullptr) { + return false; + } + return fPrefix == _other->fPrefix + && fSuffix == _other->fSuffix + && fField == _other->fField + && fStrong == _other->fStrong; +} + + +SimpleModifier::SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong) + : SimpleModifier(simpleFormatter, field, strong, {}) {} + +SimpleModifier::SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong, + const Modifier::Parameters parameters) + : fCompiledPattern(simpleFormatter.compiledPattern), fField(field), fStrong(strong), + fParameters(parameters) { + int32_t argLimit = SimpleFormatter::getArgumentLimit( + fCompiledPattern.getBuffer(), fCompiledPattern.length()); + if (argLimit == 0) { + // No arguments in compiled pattern + fPrefixLength = fCompiledPattern.charAt(1) - ARG_NUM_LIMIT; + U_ASSERT(2 + fPrefixLength == fCompiledPattern.length()); + // Set suffixOffset = -1 to indicate no arguments in compiled pattern. + fSuffixOffset = -1; + fSuffixLength = 0; + } else { + U_ASSERT(argLimit == 1); + if (fCompiledPattern.charAt(1) != 0) { + // Found prefix + fPrefixLength = fCompiledPattern.charAt(1) - ARG_NUM_LIMIT; + fSuffixOffset = 3 + fPrefixLength; + } else { + // No prefix + fPrefixLength = 0; + fSuffixOffset = 2; + } + if (3 + fPrefixLength < fCompiledPattern.length()) { + // Found suffix + fSuffixLength = fCompiledPattern.charAt(fSuffixOffset) - ARG_NUM_LIMIT; + } else { + // No suffix + fSuffixLength = 0; + } + } +} + +SimpleModifier::SimpleModifier() + : fField(kUndefinedField), fStrong(false), fPrefixLength(0), fSuffixLength(0) { +} + +int32_t SimpleModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex, + UErrorCode &status) const { + return formatAsPrefixSuffix(output, leftIndex, rightIndex, status); +} + +int32_t SimpleModifier::getPrefixLength() const { + return fPrefixLength; +} + +int32_t SimpleModifier::getCodePointCount() const { + int32_t count = 0; + if (fPrefixLength > 0) { + count += fCompiledPattern.countChar32(2, fPrefixLength); + } + if (fSuffixLength > 0) { + count += fCompiledPattern.countChar32(1 + fSuffixOffset, fSuffixLength); + } + return count; +} + +bool SimpleModifier::isStrong() const { + return fStrong; +} + +bool SimpleModifier::containsField(Field field) const { + (void)field; + // This method is not currently used. + UPRV_UNREACHABLE_EXIT; +} + +void SimpleModifier::getParameters(Parameters& output) const { + output = fParameters; +} + +bool SimpleModifier::semanticallyEquivalent(const Modifier& other) const { + auto* _other = dynamic_cast(&other); + if (_other == nullptr) { + return false; + } + if (fParameters.obj != nullptr) { + return fParameters.obj == _other->fParameters.obj; + } + return fCompiledPattern == _other->fCompiledPattern + && fField == _other->fField + && fStrong == _other->fStrong; +} + + +int32_t +SimpleModifier::formatAsPrefixSuffix(FormattedStringBuilder &result, int32_t startIndex, int32_t endIndex, + UErrorCode &status) const { + if (fSuffixOffset == -1 && fPrefixLength + fSuffixLength > 0) { + // There is no argument for the inner number; overwrite the entire segment with our string. + return result.splice(startIndex, endIndex, fCompiledPattern, 2, 2 + fPrefixLength, fField, status); + } else { + if (fPrefixLength > 0) { + result.insert(startIndex, fCompiledPattern, 2, 2 + fPrefixLength, fField, status); + } + if (fSuffixLength > 0) { + result.insert( + endIndex + fPrefixLength, + fCompiledPattern, + 1 + fSuffixOffset, + 1 + fSuffixOffset + fSuffixLength, + fField, + status); + } + return fPrefixLength + fSuffixLength; + } +} + + +int32_t +SimpleModifier::formatTwoArgPattern(const SimpleFormatter& compiled, FormattedStringBuilder& result, + int32_t index, int32_t* outPrefixLength, int32_t* outSuffixLength, + Field field, UErrorCode& status) { + const UnicodeString& compiledPattern = compiled.compiledPattern; + int32_t argLimit = SimpleFormatter::getArgumentLimit( + compiledPattern.getBuffer(), compiledPattern.length()); + if (argLimit != 2) { + status = U_INTERNAL_PROGRAM_ERROR; + return 0; + } + int32_t offset = 1; // offset into compiledPattern + int32_t length = 0; // chars added to result + + int32_t prefixLength = compiledPattern.charAt(offset); + offset++; + if (prefixLength < ARG_NUM_LIMIT) { + // No prefix + prefixLength = 0; + } else { + prefixLength -= ARG_NUM_LIMIT; + result.insert(index + length, compiledPattern, offset, offset + prefixLength, field, status); + offset += prefixLength; + length += prefixLength; + offset++; + } + + int32_t infixLength = compiledPattern.charAt(offset); + offset++; + if (infixLength < ARG_NUM_LIMIT) { + // No infix + infixLength = 0; + } else { + infixLength -= ARG_NUM_LIMIT; + result.insert(index + length, compiledPattern, offset, offset + infixLength, field, status); + offset += infixLength; + length += infixLength; + offset++; + } + + int32_t suffixLength; + if (offset == compiledPattern.length()) { + // No suffix + suffixLength = 0; + } else { + suffixLength = compiledPattern.charAt(offset) - ARG_NUM_LIMIT; + offset++; + result.insert(index + length, compiledPattern, offset, offset + suffixLength, field, status); + length += suffixLength; + } + + *outPrefixLength = prefixLength; + *outSuffixLength = suffixLength; + + return length; +} + + +int32_t ConstantMultiFieldModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex, + UErrorCode &status) const { + int32_t length = output.insert(leftIndex, fPrefix, status); + if (fOverwrite) { + length += output.splice( + leftIndex + length, + rightIndex + length, + UnicodeString(), 0, 0, + kUndefinedField, status); + } + length += output.insert(rightIndex + length, fSuffix, status); + return length; +} + +int32_t ConstantMultiFieldModifier::getPrefixLength() const { + return fPrefix.length(); +} + +int32_t ConstantMultiFieldModifier::getCodePointCount() const { + return fPrefix.codePointCount() + fSuffix.codePointCount(); +} + +bool ConstantMultiFieldModifier::isStrong() const { + return fStrong; +} + +bool ConstantMultiFieldModifier::containsField(Field field) const { + return fPrefix.containsField(field) || fSuffix.containsField(field); +} + +void ConstantMultiFieldModifier::getParameters(Parameters& output) const { + output = fParameters; +} + +bool ConstantMultiFieldModifier::semanticallyEquivalent(const Modifier& other) const { + auto* _other = dynamic_cast(&other); + if (_other == nullptr) { + return false; + } + if (fParameters.obj != nullptr) { + return fParameters.obj == _other->fParameters.obj; + } + return fPrefix.contentEquals(_other->fPrefix) + && fSuffix.contentEquals(_other->fSuffix) + && fOverwrite == _other->fOverwrite + && fStrong == _other->fStrong; +} + + +CurrencySpacingEnabledModifier::CurrencySpacingEnabledModifier(const FormattedStringBuilder &prefix, + const FormattedStringBuilder &suffix, + bool overwrite, + bool strong, + const DecimalFormatSymbols &symbols, + UErrorCode &status) + : ConstantMultiFieldModifier(prefix, suffix, overwrite, strong) { + // Check for currency spacing. Do not build the UnicodeSets unless there is + // a currency code point at a boundary. + if (prefix.length() > 0 && prefix.fieldAt(prefix.length() - 1) == Field(UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD)) { + int prefixCp = prefix.getLastCodePoint(); + UnicodeSet prefixUnicodeSet = getUnicodeSet(symbols, IN_CURRENCY, PREFIX, status); + if (prefixUnicodeSet.contains(prefixCp)) { + fAfterPrefixUnicodeSet = getUnicodeSet(symbols, IN_NUMBER, PREFIX, status); + fAfterPrefixUnicodeSet.freeze(); + fAfterPrefixInsert = getInsertString(symbols, PREFIX, status); + } else { + fAfterPrefixUnicodeSet.setToBogus(); + fAfterPrefixInsert.setToBogus(); + } + } else { + fAfterPrefixUnicodeSet.setToBogus(); + fAfterPrefixInsert.setToBogus(); + } + if (suffix.length() > 0 && suffix.fieldAt(0) == Field(UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD)) { + int suffixCp = suffix.getFirstCodePoint(); + UnicodeSet suffixUnicodeSet = getUnicodeSet(symbols, IN_CURRENCY, SUFFIX, status); + if (suffixUnicodeSet.contains(suffixCp)) { + fBeforeSuffixUnicodeSet = getUnicodeSet(symbols, IN_NUMBER, SUFFIX, status); + fBeforeSuffixUnicodeSet.freeze(); + fBeforeSuffixInsert = getInsertString(symbols, SUFFIX, status); + } else { + fBeforeSuffixUnicodeSet.setToBogus(); + fBeforeSuffixInsert.setToBogus(); + } + } else { + fBeforeSuffixUnicodeSet.setToBogus(); + fBeforeSuffixInsert.setToBogus(); + } +} + +int32_t CurrencySpacingEnabledModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex, + UErrorCode &status) const { + // Currency spacing logic + int length = 0; + if (rightIndex - leftIndex > 0 && !fAfterPrefixUnicodeSet.isBogus() && + fAfterPrefixUnicodeSet.contains(output.codePointAt(leftIndex))) { + // TODO: Should we use the CURRENCY field here? + length += output.insert( + leftIndex, + fAfterPrefixInsert, + kUndefinedField, + status); + } + if (rightIndex - leftIndex > 0 && !fBeforeSuffixUnicodeSet.isBogus() && + fBeforeSuffixUnicodeSet.contains(output.codePointBefore(rightIndex))) { + // TODO: Should we use the CURRENCY field here? + length += output.insert( + rightIndex + length, + fBeforeSuffixInsert, + kUndefinedField, + status); + } + + // Call super for the remaining logic + length += ConstantMultiFieldModifier::apply(output, leftIndex, rightIndex + length, status); + return length; +} + +int32_t +CurrencySpacingEnabledModifier::applyCurrencySpacing(FormattedStringBuilder &output, int32_t prefixStart, + int32_t prefixLen, int32_t suffixStart, + int32_t suffixLen, + const DecimalFormatSymbols &symbols, + UErrorCode &status) { + int length = 0; + bool hasPrefix = (prefixLen > 0); + bool hasSuffix = (suffixLen > 0); + bool hasNumber = (suffixStart - prefixStart - prefixLen > 0); // could be empty string + if (hasPrefix && hasNumber) { + length += applyCurrencySpacingAffix(output, prefixStart + prefixLen, PREFIX, symbols, status); + } + if (hasSuffix && hasNumber) { + length += applyCurrencySpacingAffix(output, suffixStart + length, SUFFIX, symbols, status); + } + return length; +} + +int32_t +CurrencySpacingEnabledModifier::applyCurrencySpacingAffix(FormattedStringBuilder &output, int32_t index, + EAffix affix, + const DecimalFormatSymbols &symbols, + UErrorCode &status) { + // NOTE: For prefix, output.fieldAt(index-1) gets the last field type in the prefix. + // This works even if the last code point in the prefix is 2 code units because the + // field value gets populated to both indices in the field array. + Field affixField = (affix == PREFIX) ? output.fieldAt(index - 1) : output.fieldAt(index); + if (affixField != Field(UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD)) { + return 0; + } + int affixCp = (affix == PREFIX) ? output.codePointBefore(index) : output.codePointAt(index); + UnicodeSet affixUniset = getUnicodeSet(symbols, IN_CURRENCY, affix, status); + if (!affixUniset.contains(affixCp)) { + return 0; + } + int numberCp = (affix == PREFIX) ? output.codePointAt(index) : output.codePointBefore(index); + UnicodeSet numberUniset = getUnicodeSet(symbols, IN_NUMBER, affix, status); + if (!numberUniset.contains(numberCp)) { + return 0; + } + UnicodeString spacingString = getInsertString(symbols, affix, status); + + // NOTE: This next line *inserts* the spacing string, triggering an arraycopy. + // It would be more efficient if this could be done before affixes were attached, + // so that it could be prepended/appended instead of inserted. + // However, the build code path is more efficient, and this is the most natural + // place to put currency spacing in the non-build code path. + // TODO: Should we use the CURRENCY field here? + return output.insert(index, spacingString, kUndefinedField, status); +} + +UnicodeSet +CurrencySpacingEnabledModifier::getUnicodeSet(const DecimalFormatSymbols &symbols, EPosition position, + EAffix affix, UErrorCode &status) { + // Ensure the static defaults are initialized: + umtx_initOnce(gDefaultCurrencySpacingInitOnce, &initDefaultCurrencySpacing, status); + if (U_FAILURE(status)) { + return UnicodeSet(); + } + + const UnicodeString& pattern = symbols.getPatternForCurrencySpacing( + position == IN_CURRENCY ? UNUM_CURRENCY_MATCH : UNUM_CURRENCY_SURROUNDING_MATCH, + affix == SUFFIX, + status); + if (pattern.compare(u"[:digit:]", -1) == 0) { + return *UNISET_DIGIT; + } else if (pattern.compare(u"[[:^S:]&[:^Z:]]", -1) == 0) { + return *UNISET_NOTSZ; + } else { + return UnicodeSet(pattern, status); + } +} + +UnicodeString +CurrencySpacingEnabledModifier::getInsertString(const DecimalFormatSymbols &symbols, EAffix affix, + UErrorCode &status) { + return symbols.getPatternForCurrencySpacing(UNUM_CURRENCY_INSERT, affix == SUFFIX, status); +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_modifiers.h b/deps/icu-small/source/i18n/number_modifiers.h index 09af3f48813c88..9ef1dc6e56c98c 100644 --- a/deps/icu-small/source/i18n/number_modifiers.h +++ b/deps/icu-small/source/i18n/number_modifiers.h @@ -1,338 +1,360 @@ -// © 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_MODIFIERS_H__ -#define __NUMBER_MODIFIERS_H__ - -#include -#include -#include "unicode/uniset.h" -#include "unicode/simpleformatter.h" -#include "standardplural.h" -#include "formatted_string_builder.h" -#include "number_types.h" - -U_NAMESPACE_BEGIN namespace number { -namespace impl { - -/** - * The canonical implementation of {@link Modifier}, containing a prefix and suffix string. - * TODO: This is not currently being used by real code and could be removed. - */ -class U_I18N_API ConstantAffixModifier : public Modifier, public UObject { - public: - ConstantAffixModifier(const UnicodeString &prefix, const UnicodeString &suffix, Field field, - bool strong) - : fPrefix(prefix), fSuffix(suffix), fField(field), fStrong(strong) {} - - int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, - UErrorCode &status) const U_OVERRIDE; - - int32_t getPrefixLength() const U_OVERRIDE; - - int32_t getCodePointCount() const U_OVERRIDE; - - bool isStrong() const U_OVERRIDE; - - bool containsField(Field field) const U_OVERRIDE; - - void getParameters(Parameters& output) const U_OVERRIDE; - - bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; - - private: - UnicodeString fPrefix; - UnicodeString fSuffix; - Field fField; - bool fStrong; -}; - -/** - * The second primary implementation of {@link Modifier}, this one consuming a {@link SimpleFormatter} - * pattern. - */ -class U_I18N_API SimpleModifier : public Modifier, public UMemory { - public: - SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong); - - SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong, - const Modifier::Parameters parameters); - - // Default constructor for LongNameHandler.h - SimpleModifier(); - - int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, - UErrorCode &status) const U_OVERRIDE; - - int32_t getPrefixLength() const U_OVERRIDE; - - int32_t getCodePointCount() const U_OVERRIDE; - - bool isStrong() const U_OVERRIDE; - - bool containsField(Field field) const U_OVERRIDE; - - void getParameters(Parameters& output) const U_OVERRIDE; - - bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; - - /** - * TODO: This belongs in SimpleFormatterImpl. The only reason I haven't moved it there yet is because - * FormattedStringBuilder is an internal class and SimpleFormatterImpl feels like it should not depend on it. - * - *

      - * Formats a value that is already stored inside the StringBuilder result between the indices - * startIndex and endIndex by inserting characters before the start index and after the - * end index. - * - *

      - * This is well-defined only for patterns with exactly one argument. - * - * @param result - * The StringBuilder containing the value argument. - * @param startIndex - * The left index of the value within the string builder. - * @param endIndex - * The right index of the value within the string builder. - * @return The number of characters (UTF-16 code points) that were added to the StringBuilder. - */ - int32_t - formatAsPrefixSuffix(FormattedStringBuilder& result, int32_t startIndex, int32_t endIndex, - UErrorCode& status) const; - - /** - * TODO: Like above, this belongs with the rest of the SimpleFormatterImpl code. - * I put it here so that the SimpleFormatter uses in FormattedStringBuilder are near each other. - * - *

      - * Applies the compiled two-argument pattern to the FormattedStringBuilder. - * - *

      - * This method is optimized for the case where the prefix and suffix are often empty, such as - * in the range pattern like "{0}-{1}". - */ - static int32_t - formatTwoArgPattern(const SimpleFormatter& compiled, FormattedStringBuilder& result, - int32_t index, int32_t* outPrefixLength, int32_t* outSuffixLength, - Field field, UErrorCode& status); - - private: - UnicodeString fCompiledPattern; - Field fField; - bool fStrong = false; - int32_t fPrefixLength = 0; - int32_t fSuffixOffset = -1; - int32_t fSuffixLength = 0; - Modifier::Parameters fParameters; -}; - -/** - * An implementation of {@link Modifier} that allows for multiple types of fields in the same modifier. Constructed - * based on the contents of two {@link FormattedStringBuilder} instances (one for the prefix, one for the suffix). - */ -class U_I18N_API ConstantMultiFieldModifier : public Modifier, public UMemory { - public: - ConstantMultiFieldModifier( - const FormattedStringBuilder &prefix, - const FormattedStringBuilder &suffix, - bool overwrite, - bool strong, - const Modifier::Parameters parameters) - : fPrefix(prefix), - fSuffix(suffix), - fOverwrite(overwrite), - fStrong(strong), - fParameters(parameters) {} - - ConstantMultiFieldModifier( - const FormattedStringBuilder &prefix, - const FormattedStringBuilder &suffix, - bool overwrite, - bool strong) - : fPrefix(prefix), - fSuffix(suffix), - fOverwrite(overwrite), - fStrong(strong) {} - - int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, - UErrorCode &status) const U_OVERRIDE; - - int32_t getPrefixLength() const U_OVERRIDE; - - int32_t getCodePointCount() const U_OVERRIDE; - - bool isStrong() const U_OVERRIDE; - - bool containsField(Field field) const U_OVERRIDE; - - void getParameters(Parameters& output) const U_OVERRIDE; - - bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; - - protected: - // NOTE: In Java, these are stored as array pointers. In C++, the FormattedStringBuilder is stored by - // value and is treated internally as immutable. - FormattedStringBuilder fPrefix; - FormattedStringBuilder fSuffix; - bool fOverwrite; - bool fStrong; - Modifier::Parameters fParameters; -}; - -/** Identical to {@link ConstantMultiFieldModifier}, but supports currency spacing. */ -class U_I18N_API CurrencySpacingEnabledModifier : public ConstantMultiFieldModifier { - public: - /** Safe code path */ - CurrencySpacingEnabledModifier( - const FormattedStringBuilder &prefix, - const FormattedStringBuilder &suffix, - bool overwrite, - bool strong, - const DecimalFormatSymbols &symbols, - UErrorCode &status); - - int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, - UErrorCode &status) const U_OVERRIDE; - - /** Unsafe code path */ - static int32_t - applyCurrencySpacing(FormattedStringBuilder &output, int32_t prefixStart, int32_t prefixLen, - int32_t suffixStart, int32_t suffixLen, const DecimalFormatSymbols &symbols, - UErrorCode &status); - - private: - UnicodeSet fAfterPrefixUnicodeSet; - UnicodeString fAfterPrefixInsert; - UnicodeSet fBeforeSuffixUnicodeSet; - UnicodeString fBeforeSuffixInsert; - - enum EAffix { - PREFIX, SUFFIX - }; - - enum EPosition { - IN_CURRENCY, IN_NUMBER - }; - - /** Unsafe code path */ - static int32_t applyCurrencySpacingAffix(FormattedStringBuilder &output, int32_t index, EAffix affix, - const DecimalFormatSymbols &symbols, UErrorCode &status); - - static UnicodeSet - getUnicodeSet(const DecimalFormatSymbols &symbols, EPosition position, EAffix affix, - UErrorCode &status); - - static UnicodeString - getInsertString(const DecimalFormatSymbols &symbols, EAffix affix, UErrorCode &status); -}; - -/** A Modifier that does not do anything. */ -class U_I18N_API EmptyModifier : public Modifier, public UMemory { - public: - explicit EmptyModifier(bool isStrong) : fStrong(isStrong) {} - - int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, - UErrorCode &status) const U_OVERRIDE { - (void)output; - (void)leftIndex; - (void)rightIndex; - (void)status; - return 0; - } - - int32_t getPrefixLength() const U_OVERRIDE { - return 0; - } - - int32_t getCodePointCount() const U_OVERRIDE { - return 0; - } - - bool isStrong() const U_OVERRIDE { - return fStrong; - } - - bool containsField(Field field) const U_OVERRIDE { - (void)field; - return false; - } - - void getParameters(Parameters& output) const U_OVERRIDE { - output.obj = nullptr; - } - - bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE { - return other.getCodePointCount() == 0; - } - - private: - bool fStrong; -}; - -/** - * This implementation of ModifierStore adopts Modifier pointers. - */ -class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory { - public: - virtual ~AdoptingModifierStore(); - - static constexpr StandardPlural::Form DEFAULT_STANDARD_PLURAL = StandardPlural::OTHER; - - AdoptingModifierStore() = default; - - // No copying! - AdoptingModifierStore(const AdoptingModifierStore &other) = delete; - - /** - * Sets the Modifier with the specified signum and plural form. - */ - void adoptModifier(Signum signum, StandardPlural::Form plural, const Modifier *mod) { - U_ASSERT(mods[getModIndex(signum, plural)] == nullptr); - mods[getModIndex(signum, plural)] = mod; - } - - /** - * Sets the Modifier with the specified signum. - * The modifier will apply to all plural forms. - */ - void adoptModifierWithoutPlural(Signum signum, const Modifier *mod) { - U_ASSERT(mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)] == nullptr); - mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)] = mod; - } - - /** Returns a reference to the modifier; no ownership change. */ - const Modifier *getModifier(Signum signum, StandardPlural::Form plural) const U_OVERRIDE { - const Modifier* modifier = mods[getModIndex(signum, plural)]; - if (modifier == nullptr && plural != DEFAULT_STANDARD_PLURAL) { - modifier = mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)]; - } - return modifier; - } - - /** Returns a reference to the modifier; no ownership change. */ - const Modifier *getModifierWithoutPlural(Signum signum) const { - return mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)]; - } - - private: - // NOTE: mods is zero-initialized (to nullptr) - const Modifier *mods[4 * StandardPlural::COUNT] = {}; - - inline static int32_t getModIndex(Signum signum, StandardPlural::Form plural) { - U_ASSERT(signum >= 0 && signum < SIGNUM_COUNT); - U_ASSERT(plural >= 0 && plural < StandardPlural::COUNT); - return static_cast(plural) * SIGNUM_COUNT + signum; - } -}; - -} // namespace impl -} // namespace number -U_NAMESPACE_END - - -#endif //__NUMBER_MODIFIERS_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_MODIFIERS_H__ +#define __NUMBER_MODIFIERS_H__ + +#include +#include +#include "unicode/uniset.h" +#include "unicode/simpleformatter.h" +#include "standardplural.h" +#include "formatted_string_builder.h" +#include "number_types.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +/** + * The canonical implementation of {@link Modifier}, containing a prefix and suffix string. + * TODO: This is not currently being used by real code and could be removed. + */ +class U_I18N_API ConstantAffixModifier : public Modifier, public UObject { + public: + ConstantAffixModifier(const UnicodeString &prefix, const UnicodeString &suffix, Field field, + bool strong) + : fPrefix(prefix), fSuffix(suffix), fField(field), fStrong(strong) {} + + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + UErrorCode &status) const override; + + int32_t getPrefixLength() const override; + + int32_t getCodePointCount() const override; + + bool isStrong() const override; + + bool containsField(Field field) const override; + + void getParameters(Parameters& output) const override; + + bool semanticallyEquivalent(const Modifier& other) const override; + + private: + UnicodeString fPrefix; + UnicodeString fSuffix; + Field fField; + bool fStrong; +}; + +/** + * The second primary implementation of {@link Modifier}, this one consuming a {@link SimpleFormatter} + * pattern. + */ +class U_I18N_API SimpleModifier : public Modifier, public UMemory { + public: + SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong); + + SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong, + const Modifier::Parameters parameters); + + // Default constructor for LongNameHandler.h + SimpleModifier(); + + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + UErrorCode &status) const override; + + int32_t getPrefixLength() const override; + + int32_t getCodePointCount() const override; + + bool isStrong() const override; + + bool containsField(Field field) const override; + + void getParameters(Parameters& output) const override; + + bool semanticallyEquivalent(const Modifier& other) const override; + + /** + * TODO: This belongs in SimpleFormatterImpl. The only reason I haven't moved it there yet is because + * FormattedStringBuilder is an internal class and SimpleFormatterImpl feels like it should not depend on it. + * + *

      + * Formats a value that is already stored inside the StringBuilder result between the indices + * startIndex and endIndex by inserting characters before the start index and after the + * end index. + * + *

      + * This is well-defined only for patterns with exactly one argument. + * + * @param result + * The StringBuilder containing the value argument. + * @param startIndex + * The left index of the value within the string builder. + * @param endIndex + * The right index of the value within the string builder. + * @return The number of characters (UTF-16 code points) that were added to the StringBuilder. + */ + int32_t + formatAsPrefixSuffix(FormattedStringBuilder& result, int32_t startIndex, int32_t endIndex, + UErrorCode& status) const; + + /** + * TODO: Like above, this belongs with the rest of the SimpleFormatterImpl code. + * I put it here so that the SimpleFormatter uses in FormattedStringBuilder are near each other. + * + *

      + * Applies the compiled two-argument pattern to the FormattedStringBuilder. + * + *

      + * This method is optimized for the case where the prefix and suffix are often empty, such as + * in the range pattern like "{0}-{1}". + */ + static int32_t + formatTwoArgPattern(const SimpleFormatter& compiled, FormattedStringBuilder& result, + int32_t index, int32_t* outPrefixLength, int32_t* outSuffixLength, + Field field, UErrorCode& status); + + private: + UnicodeString fCompiledPattern; + Field fField; + bool fStrong = false; + int32_t fPrefixLength = 0; + int32_t fSuffixOffset = -1; + int32_t fSuffixLength = 0; + Modifier::Parameters fParameters; +}; + +/** + * An implementation of {@link Modifier} that allows for multiple types of fields in the same modifier. Constructed + * based on the contents of two {@link FormattedStringBuilder} instances (one for the prefix, one for the suffix). + */ +class U_I18N_API ConstantMultiFieldModifier : public Modifier, public UMemory { + public: + ConstantMultiFieldModifier( + const FormattedStringBuilder &prefix, + const FormattedStringBuilder &suffix, + bool overwrite, + bool strong, + const Modifier::Parameters parameters) + : fPrefix(prefix), + fSuffix(suffix), + fOverwrite(overwrite), + fStrong(strong), + fParameters(parameters) {} + + ConstantMultiFieldModifier( + const FormattedStringBuilder &prefix, + const FormattedStringBuilder &suffix, + bool overwrite, + bool strong) + : fPrefix(prefix), + fSuffix(suffix), + fOverwrite(overwrite), + fStrong(strong) {} + + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + UErrorCode &status) const override; + + int32_t getPrefixLength() const override; + + int32_t getCodePointCount() const override; + + bool isStrong() const override; + + bool containsField(Field field) const override; + + void getParameters(Parameters& output) const override; + + bool semanticallyEquivalent(const Modifier& other) const override; + + protected: + // NOTE: In Java, these are stored as array pointers. In C++, the FormattedStringBuilder is stored by + // value and is treated internally as immutable. + FormattedStringBuilder fPrefix; + FormattedStringBuilder fSuffix; + bool fOverwrite; + bool fStrong; + Modifier::Parameters fParameters; +}; + +/** Identical to {@link ConstantMultiFieldModifier}, but supports currency spacing. */ +class U_I18N_API CurrencySpacingEnabledModifier : public ConstantMultiFieldModifier { + public: + /** Safe code path */ + CurrencySpacingEnabledModifier( + const FormattedStringBuilder &prefix, + const FormattedStringBuilder &suffix, + bool overwrite, + bool strong, + const DecimalFormatSymbols &symbols, + UErrorCode &status); + + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + UErrorCode &status) const override; + + /** Unsafe code path */ + static int32_t + applyCurrencySpacing(FormattedStringBuilder &output, int32_t prefixStart, int32_t prefixLen, + int32_t suffixStart, int32_t suffixLen, const DecimalFormatSymbols &symbols, + UErrorCode &status); + + private: + UnicodeSet fAfterPrefixUnicodeSet; + UnicodeString fAfterPrefixInsert; + UnicodeSet fBeforeSuffixUnicodeSet; + UnicodeString fBeforeSuffixInsert; + + enum EAffix { + PREFIX, SUFFIX + }; + + enum EPosition { + IN_CURRENCY, IN_NUMBER + }; + + /** Unsafe code path */ + static int32_t applyCurrencySpacingAffix(FormattedStringBuilder &output, int32_t index, EAffix affix, + const DecimalFormatSymbols &symbols, UErrorCode &status); + + static UnicodeSet + getUnicodeSet(const DecimalFormatSymbols &symbols, EPosition position, EAffix affix, + UErrorCode &status); + + static UnicodeString + getInsertString(const DecimalFormatSymbols &symbols, EAffix affix, UErrorCode &status); +}; + +/** A Modifier that does not do anything. */ +class U_I18N_API EmptyModifier : public Modifier, public UMemory { + public: + explicit EmptyModifier(bool isStrong) : fStrong(isStrong) {} + + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + UErrorCode &status) const override { + (void)output; + (void)leftIndex; + (void)rightIndex; + (void)status; + return 0; + } + + int32_t getPrefixLength() const override { + return 0; + } + + int32_t getCodePointCount() const override { + return 0; + } + + bool isStrong() const override { + return fStrong; + } + + bool containsField(Field field) const override { + (void)field; + return false; + } + + void getParameters(Parameters& output) const override { + output.obj = nullptr; + } + + bool semanticallyEquivalent(const Modifier& other) const override { + return other.getCodePointCount() == 0; + } + + private: + bool fStrong; +}; + +/** An adopting Modifier store that varies by signum but not plural form. */ +class U_I18N_API AdoptingSignumModifierStore : public UMemory { + public: + virtual ~AdoptingSignumModifierStore(); + + AdoptingSignumModifierStore() = default; + + // No copying! + AdoptingSignumModifierStore(const AdoptingSignumModifierStore &other) = delete; + AdoptingSignumModifierStore& operator=(const AdoptingSignumModifierStore& other) = delete; + + // Moving is OK + AdoptingSignumModifierStore(AdoptingSignumModifierStore &&other) noexcept { + *this = std::move(other); + } + AdoptingSignumModifierStore& operator=(AdoptingSignumModifierStore&& other) noexcept; + + /** Take ownership of the Modifier and slot it in at the given Signum. */ + void adoptModifier(Signum signum, const Modifier* mod) { + U_ASSERT(mods[signum] == nullptr); + mods[signum] = mod; + } + + inline const Modifier*& operator[](Signum signum) { + return mods[signum]; + } + inline Modifier const* operator[](Signum signum) const { + return mods[signum]; + } + + private: + const Modifier* mods[SIGNUM_COUNT] = {}; +}; + +/** + * This implementation of ModifierStore adopts Modifier pointers. + */ +class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory { + public: + static constexpr StandardPlural::Form DEFAULT_STANDARD_PLURAL = StandardPlural::OTHER; + + AdoptingModifierStore() = default; + + // No copying! + AdoptingModifierStore(const AdoptingModifierStore &other) = delete; + + // Moving is OK + AdoptingModifierStore(AdoptingModifierStore &&other) = default; + + /** Sets the modifiers for a specific plural form. */ + void adoptSignumModifierStore(StandardPlural::Form plural, AdoptingSignumModifierStore other) { + mods[plural] = std::move(other); + } + + /** Sets the modifiers for the default plural form. */ + void adoptSignumModifierStoreNoPlural(AdoptingSignumModifierStore other) { + mods[DEFAULT_STANDARD_PLURAL] = std::move(other); + } + + /** Returns a reference to the modifier; no ownership change. */ + const Modifier *getModifier(Signum signum, StandardPlural::Form plural) const override { + const Modifier* modifier = mods[plural][signum]; + if (modifier == nullptr && plural != DEFAULT_STANDARD_PLURAL) { + modifier = mods[DEFAULT_STANDARD_PLURAL][signum]; + } + return modifier; + } + + /** Returns a reference to the modifier; no ownership change. */ + const Modifier *getModifierWithoutPlural(Signum signum) const { + return mods[DEFAULT_STANDARD_PLURAL][signum]; + } + + private: + // NOTE: mods is zero-initialized (to nullptr) + AdoptingSignumModifierStore mods[StandardPlural::COUNT] = {}; +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + + +#endif //__NUMBER_MODIFIERS_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_multiplier.cpp b/deps/icu-small/source/i18n/number_multiplier.cpp index 58e1e441bd28c5..cb5e81bdcbe2cb 100644 --- a/deps/icu-small/source/i18n/number_multiplier.cpp +++ b/deps/icu-small/source/i18n/number_multiplier.cpp @@ -1,160 +1,160 @@ -// © 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) { - if (this == &other) { return *this; } // self-assignment: no-op - 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; - if (fArbitrary != nullptr) { - delete fArbitrary; - } - 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) { - fMultiplier = multiplier; - fParent = parent; -} - -void MultiplierFormatHandler::processQuantity(DecimalQuantity& quantity, MicroProps& micros, - UErrorCode& status) const { - fParent->processQuantity(quantity, micros, status); - fMultiplier.applyTo(quantity); -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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) { + if (this == &other) { return *this; } // self-assignment: no-op + 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) 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) noexcept { + fMagnitude = src.fMagnitude; + if (fArbitrary != nullptr) { + delete fArbitrary; + } + 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) { + fMultiplier = multiplier; + fParent = parent; +} + +void MultiplierFormatHandler::processQuantity(DecimalQuantity& quantity, MicroProps& micros, + UErrorCode& status) const { + fParent->processQuantity(quantity, micros, status); + fMultiplier.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 index d8235dc601b559..430f3f8174ef10 100644 --- a/deps/icu-small/source/i18n/number_multiplier.h +++ b/deps/icu-small/source/i18n/number_multiplier.h @@ -1,57 +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 fMultiplier; - const MicroPropsGenerator *fParent; -}; - - -/** 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 */ +// © 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 override; + + private: + Scale fMultiplier; + const MicroPropsGenerator *fParent; +}; + + +/** 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 b3cabb57a50ea7..610d7de6d1186a 100644 --- a/deps/icu-small/source/i18n/number_notation.cpp +++ b/deps/icu-small/source/i18n/number_notation.cpp @@ -1,88 +1,88 @@ -// © 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 - -#include "unicode/numberformatter.h" -#include "number_types.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - - -ScientificNotation Notation::scientific() { - // NOTE: ISO C++ does not allow C99 designated initializers. - ScientificSettings settings; - settings.fEngineeringInterval = 1; - settings.fRequireMinInt = false; - settings.fMinExponentDigits = 1; - settings.fExponentSignDisplay = UNUM_SIGN_AUTO; - NotationUnion union_; - union_.scientific = settings; - return {NTN_SCIENTIFIC, union_}; -} - -ScientificNotation Notation::engineering() { - ScientificSettings settings; - settings.fEngineeringInterval = 3; - settings.fRequireMinInt = false; - settings.fMinExponentDigits = 1; - settings.fExponentSignDisplay = UNUM_SIGN_AUTO; - NotationUnion union_; - union_.scientific = settings; - 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; - return {NTN_COMPACT, union_}; -} - -Notation Notation::compactLong() { - NotationUnion union_; - union_.compactStyle = CompactStyle::UNUM_LONG; - return {NTN_COMPACT, union_}; -} - -Notation Notation::simple() { - return {}; -} - -ScientificNotation -ScientificNotation::withMinExponentDigits(int32_t minExponentDigits) const { - if (minExponentDigits >= 1 && minExponentDigits <= kMaxIntFracSig) { - ScientificSettings settings = fUnion.scientific; - settings.fMinExponentDigits = static_cast(minExponentDigits); - NotationUnion union_ = {settings}; - return {NTN_SCIENTIFIC, union_}; - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -ScientificNotation -ScientificNotation::withExponentSignDisplay(UNumberSignDisplay exponentSignDisplay) const { - ScientificSettings settings = fUnion.scientific; - settings.fExponentSignDisplay = exponentSignDisplay; - NotationUnion union_ = {settings}; - return {NTN_SCIENTIFIC, union_}; -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include "unicode/numberformatter.h" +#include "number_types.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +ScientificNotation Notation::scientific() { + // NOTE: ISO C++ does not allow C99 designated initializers. + ScientificSettings settings; + settings.fEngineeringInterval = 1; + settings.fRequireMinInt = false; + settings.fMinExponentDigits = 1; + settings.fExponentSignDisplay = UNUM_SIGN_AUTO; + NotationUnion union_; + union_.scientific = settings; + return {NTN_SCIENTIFIC, union_}; +} + +ScientificNotation Notation::engineering() { + ScientificSettings settings; + settings.fEngineeringInterval = 3; + settings.fRequireMinInt = false; + settings.fMinExponentDigits = 1; + settings.fExponentSignDisplay = UNUM_SIGN_AUTO; + NotationUnion union_; + union_.scientific = settings; + 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; + return {NTN_COMPACT, union_}; +} + +Notation Notation::compactLong() { + NotationUnion union_; + union_.compactStyle = CompactStyle::UNUM_LONG; + return {NTN_COMPACT, union_}; +} + +Notation Notation::simple() { + return {}; +} + +ScientificNotation +ScientificNotation::withMinExponentDigits(int32_t minExponentDigits) const { + if (minExponentDigits >= 1 && minExponentDigits <= kMaxIntFracSig) { + ScientificSettings settings = fUnion.scientific; + settings.fMinExponentDigits = static_cast(minExponentDigits); + NotationUnion union_ = {settings}; + return {NTN_SCIENTIFIC, union_}; + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +ScientificNotation +ScientificNotation::withExponentSignDisplay(UNumberSignDisplay exponentSignDisplay) const { + ScientificSettings settings = fUnion.scientific; + settings.fExponentSignDisplay = exponentSignDisplay; + NotationUnion union_ = {settings}; + return {NTN_SCIENTIFIC, union_}; +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_output.cpp b/deps/icu-small/source/i18n/number_output.cpp index 729a2cd5e6449f..a33362d81e40ed 100644 --- a/deps/icu-small/source/i18n/number_output.cpp +++ b/deps/icu-small/source/i18n/number_output.cpp @@ -1,86 +1,86 @@ -// © 2019 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/measunit.h" -#include "unicode/numberformatter.h" -#include "number_utypes.h" -#include "util.h" -#include "number_decimalquantity.h" -#include "number_decnum.h" -#include "numrange_impl.h" - -U_NAMESPACE_BEGIN -namespace number { - - -UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedNumber) - -#define UPRV_NOARG - -void FormattedNumber::toDecimalNumber(ByteSink& sink, UErrorCode& status) const { - UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG) - impl::DecNum decnum; - fData->quantity.toDecNum(decnum, status); - decnum.toString(sink, status); -} - -void FormattedNumber::getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih, - UErrorCode& status) const { - UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG) - fData->getAllFieldPositions(fpih, status); -} - -MeasureUnit FormattedNumber::getOutputUnit(UErrorCode& status) const { - UPRV_FORMATTED_VALUE_METHOD_GUARD(MeasureUnit()) - return fData->outputUnit; -} - -UDisplayOptionsNounClass FormattedNumber::getNounClass(UErrorCode &status) const { - UPRV_FORMATTED_VALUE_METHOD_GUARD(UDISPOPT_NOUN_CLASS_UNDEFINED); - const char *nounClass = fData->gender; - return udispopt_fromNounClassIdentifier(nounClass); -} - -void FormattedNumber::getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const { - UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG) - output = fData->quantity; -} - - -impl::UFormattedNumberData::~UFormattedNumberData() = default; - - -UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedNumberRange) - -#define UPRV_NOARG - -void FormattedNumberRange::getDecimalNumbers(ByteSink& sink1, ByteSink& sink2, UErrorCode& status) const { - UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG) - impl::DecNum decnum1; - impl::DecNum decnum2; - fData->quantity1.toDecNum(decnum1, status).toString(sink1, status); - fData->quantity2.toDecNum(decnum2, status).toString(sink2, status); -} - -UNumberRangeIdentityResult FormattedNumberRange::getIdentityResult(UErrorCode& status) const { - UPRV_FORMATTED_VALUE_METHOD_GUARD(UNUM_IDENTITY_RESULT_NOT_EQUAL) - return fData->identityResult; -} - -const impl::UFormattedNumberRangeData* FormattedNumberRange::getData(UErrorCode& status) const { - UPRV_FORMATTED_VALUE_METHOD_GUARD(nullptr) - return fData; -} - - -impl::UFormattedNumberRangeData::~UFormattedNumberRangeData() = default; - - -} // namespace number -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/measunit.h" +#include "unicode/numberformatter.h" +#include "number_utypes.h" +#include "util.h" +#include "number_decimalquantity.h" +#include "number_decnum.h" +#include "numrange_impl.h" + +U_NAMESPACE_BEGIN +namespace number { + + +UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedNumber) + +#define UPRV_NOARG + +void FormattedNumber::toDecimalNumber(ByteSink& sink, UErrorCode& status) const { + UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG) + impl::DecNum decnum; + fData->quantity.toDecNum(decnum, status); + decnum.toString(sink, status); +} + +void FormattedNumber::getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih, + UErrorCode& status) const { + UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG) + fData->getAllFieldPositions(fpih, status); +} + +MeasureUnit FormattedNumber::getOutputUnit(UErrorCode& status) const { + UPRV_FORMATTED_VALUE_METHOD_GUARD(MeasureUnit()) + return fData->outputUnit; +} + +UDisplayOptionsNounClass FormattedNumber::getNounClass(UErrorCode &status) const { + UPRV_FORMATTED_VALUE_METHOD_GUARD(UDISPOPT_NOUN_CLASS_UNDEFINED); + const char *nounClass = fData->gender; + return udispopt_fromNounClassIdentifier(nounClass); +} + +void FormattedNumber::getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const { + UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG) + output = fData->quantity; +} + + +impl::UFormattedNumberData::~UFormattedNumberData() = default; + + +UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedNumberRange) + +#define UPRV_NOARG + +void FormattedNumberRange::getDecimalNumbers(ByteSink& sink1, ByteSink& sink2, UErrorCode& status) const { + UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG) + impl::DecNum decnum1; + impl::DecNum decnum2; + fData->quantity1.toDecNum(decnum1, status).toString(sink1, status); + fData->quantity2.toDecNum(decnum2, status).toString(sink2, status); +} + +UNumberRangeIdentityResult FormattedNumberRange::getIdentityResult(UErrorCode& status) const { + UPRV_FORMATTED_VALUE_METHOD_GUARD(UNUM_IDENTITY_RESULT_NOT_EQUAL) + return fData->identityResult; +} + +const impl::UFormattedNumberRangeData* FormattedNumberRange::getData(UErrorCode& status) const { + UPRV_FORMATTED_VALUE_METHOD_GUARD(nullptr) + return fData; +} + + +impl::UFormattedNumberRangeData::~UFormattedNumberRangeData() = default; + + +} // namespace number +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_padding.cpp b/deps/icu-small/source/i18n/number_padding.cpp index c320c3ffb6fadc..cbf1bbf8568f74 100644 --- a/deps/icu-small/source/i18n/number_padding.cpp +++ b/deps/icu-small/source/i18n/number_padding.cpp @@ -1,96 +1,96 @@ -// © 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 - -#include "unicode/numberformatter.h" -#include "number_types.h" -#include "formatted_string_builder.h" -#include "number_decimfmtprops.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - -namespace { - -int32_t -addPaddingHelper(UChar32 paddingCp, int32_t requiredPadding, FormattedStringBuilder &string, int32_t index, - UErrorCode &status) { - for (int32_t i = 0; i < requiredPadding; i++) { - // TODO: If appending to the end, this will cause actual insertion operations. Improve. - string.insertCodePoint(index, paddingCp, kUndefinedField, status); - } - return U16_LENGTH(paddingCp) * requiredPadding; -} - -} - -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; -} - -Padder::Padder(int32_t width) : fWidth(width) {} - -Padder Padder::none() { - return {-1}; -} - -Padder Padder::codePoints(UChar32 cp, int32_t targetWidth, UNumberFormatPadPosition position) { - // TODO: Validate the code point? - if (targetWidth >= 0) { - return {cp, targetWidth, position}; - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -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, - FormattedStringBuilder &string, int32_t leftIndex, int32_t rightIndex, - UErrorCode &status) const { - int32_t modLength = mod1.getCodePointCount() + mod2.getCodePointCount(); - int32_t requiredPadding = fWidth - modLength - string.codePointCount(); - U_ASSERT(leftIndex == 0 && - rightIndex == string.length()); // fix the previous line to remove this assertion - - int length = 0; - if (requiredPadding <= 0) { - // Padding is not required. - length += mod1.apply(string, leftIndex, rightIndex, status); - length += mod2.apply(string, leftIndex, rightIndex + length, status); - return length; - } - - PadPosition position = fUnion.padding.fPosition; - UChar32 paddingCp = fUnion.padding.fCp; - if (position == UNUM_PAD_AFTER_PREFIX) { - length += addPaddingHelper(paddingCp, requiredPadding, string, leftIndex, status); - } else if (position == UNUM_PAD_BEFORE_SUFFIX) { - length += addPaddingHelper(paddingCp, requiredPadding, string, rightIndex + length, status); - } - length += mod1.apply(string, leftIndex, rightIndex + length, status); - length += mod2.apply(string, leftIndex, rightIndex + length, status); - if (position == UNUM_PAD_BEFORE_PREFIX) { - length += addPaddingHelper(paddingCp, requiredPadding, string, leftIndex, status); - } else if (position == UNUM_PAD_AFTER_SUFFIX) { - length += addPaddingHelper(paddingCp, requiredPadding, string, rightIndex + length, status); - } - - return length; -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include "unicode/numberformatter.h" +#include "number_types.h" +#include "formatted_string_builder.h" +#include "number_decimfmtprops.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +namespace { + +int32_t +addPaddingHelper(UChar32 paddingCp, int32_t requiredPadding, FormattedStringBuilder &string, int32_t index, + UErrorCode &status) { + for (int32_t i = 0; i < requiredPadding; i++) { + // TODO: If appending to the end, this will cause actual insertion operations. Improve. + string.insertCodePoint(index, paddingCp, kUndefinedField, status); + } + return U16_LENGTH(paddingCp) * requiredPadding; +} + +} + +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; +} + +Padder::Padder(int32_t width) : fWidth(width) {} + +Padder Padder::none() { + return {-1}; +} + +Padder Padder::codePoints(UChar32 cp, int32_t targetWidth, UNumberFormatPadPosition position) { + // TODO: Validate the code point? + if (targetWidth >= 0) { + return {cp, targetWidth, position}; + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +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, + FormattedStringBuilder &string, int32_t leftIndex, int32_t rightIndex, + UErrorCode &status) const { + int32_t modLength = mod1.getCodePointCount() + mod2.getCodePointCount(); + int32_t requiredPadding = fWidth - modLength - string.codePointCount(); + U_ASSERT(leftIndex == 0 && + rightIndex == string.length()); // fix the previous line to remove this assertion + + int length = 0; + if (requiredPadding <= 0) { + // Padding is not required. + length += mod1.apply(string, leftIndex, rightIndex, status); + length += mod2.apply(string, leftIndex, rightIndex + length, status); + return length; + } + + PadPosition position = fUnion.padding.fPosition; + UChar32 paddingCp = fUnion.padding.fCp; + if (position == UNUM_PAD_AFTER_PREFIX) { + length += addPaddingHelper(paddingCp, requiredPadding, string, leftIndex, status); + } else if (position == UNUM_PAD_BEFORE_SUFFIX) { + length += addPaddingHelper(paddingCp, requiredPadding, string, rightIndex + length, status); + } + length += mod1.apply(string, leftIndex, rightIndex + length, status); + length += mod2.apply(string, leftIndex, rightIndex + length, status); + if (position == UNUM_PAD_BEFORE_PREFIX) { + length += addPaddingHelper(paddingCp, requiredPadding, string, leftIndex, status); + } else if (position == UNUM_PAD_AFTER_SUFFIX) { + length += addPaddingHelper(paddingCp, requiredPadding, string, rightIndex + length, status); + } + + return length; +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_patternmodifier.cpp b/deps/icu-small/source/i18n/number_patternmodifier.cpp index 088a30ecd7ff6f..cd8b864f1b94a0 100644 --- a/deps/icu-small/source/i18n/number_patternmodifier.cpp +++ b/deps/icu-small/source/i18n/number_patternmodifier.cpp @@ -1,347 +1,348 @@ -// © 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 - -#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; - - -AffixPatternProvider::~AffixPatternProvider() = default; - - -MutablePatternModifier::MutablePatternModifier(bool isStrong) - : fStrong(isStrong) {} - -void MutablePatternModifier::setPatternInfo(const AffixPatternProvider* patternInfo, Field field) { - fPatternInfo = patternInfo; - fField = field; -} - -void MutablePatternModifier::setPatternAttributes( - UNumberSignDisplay signDisplay, - bool perMille, - bool approximately) { - fSignDisplay = signDisplay; - fPerMilleReplacesPercent = perMille; - fApproximately = approximately; -} - -void MutablePatternModifier::setSymbols(const DecimalFormatSymbols* symbols, - const CurrencyUnit& currency, - const UNumberUnitWidth unitWidth, - const PluralRules* rules, - UErrorCode& status) { - U_ASSERT((rules != nullptr) == needsPlurals()); - fSymbols = symbols; - fCurrencySymbols = {currency, symbols->getLocale(), *symbols, status}; - fUnitWidth = unitWidth; - fRules = rules; -} - -void MutablePatternModifier::setNumberProperties(Signum signum, StandardPlural::Form plural) { - fSignum = signum; - fPlural = plural; -} - -bool MutablePatternModifier::needsPlurals() const { - UErrorCode statusLocal = U_ZERO_ERROR; - return fPatternInfo->containsSymbolType(AffixPatternType::TYPE_CURRENCY_TRIPLE, statusLocal); - // Silently ignore any error codes. -} - -ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) { - // TODO: Move StandardPlural VALUES to standardplural.h - static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = { - StandardPlural::Form::ZERO, - StandardPlural::Form::ONE, - StandardPlural::Form::TWO, - StandardPlural::Form::FEW, - StandardPlural::Form::MANY, - StandardPlural::Form::OTHER}; - - auto pm = new AdoptingModifierStore(); - if (pm == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - - if (needsPlurals()) { - // Slower path when we require the plural keyword. - for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) { - setNumberProperties(SIGNUM_POS, plural); - pm->adoptModifier(SIGNUM_POS, plural, createConstantModifier(status)); - setNumberProperties(SIGNUM_NEG_ZERO, plural); - pm->adoptModifier(SIGNUM_NEG_ZERO, plural, createConstantModifier(status)); - setNumberProperties(SIGNUM_POS_ZERO, plural); - pm->adoptModifier(SIGNUM_POS_ZERO, plural, createConstantModifier(status)); - setNumberProperties(SIGNUM_NEG, plural); - pm->adoptModifier(SIGNUM_NEG, plural, createConstantModifier(status)); - } - if (U_FAILURE(status)) { - delete pm; - return nullptr; - } - return new ImmutablePatternModifier(pm, fRules); // adopts pm - } else { - // Faster path when plural keyword is not needed. - setNumberProperties(SIGNUM_POS, StandardPlural::Form::COUNT); - pm->adoptModifierWithoutPlural(SIGNUM_POS, createConstantModifier(status)); - setNumberProperties(SIGNUM_NEG_ZERO, StandardPlural::Form::COUNT); - pm->adoptModifierWithoutPlural(SIGNUM_NEG_ZERO, createConstantModifier(status)); - setNumberProperties(SIGNUM_POS_ZERO, StandardPlural::Form::COUNT); - pm->adoptModifierWithoutPlural(SIGNUM_POS_ZERO, createConstantModifier(status)); - setNumberProperties(SIGNUM_NEG, StandardPlural::Form::COUNT); - pm->adoptModifierWithoutPlural(SIGNUM_NEG, createConstantModifier(status)); - if (U_FAILURE(status)) { - delete pm; - return nullptr; - } - return new ImmutablePatternModifier(pm, nullptr); // adopts pm - } -} - -ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErrorCode& status) { - FormattedStringBuilder a; - FormattedStringBuilder b; - insertPrefix(a, 0, status); - insertSuffix(b, 0, status); - if (fPatternInfo->hasCurrencySign()) { - return new CurrencySpacingEnabledModifier( - a, b, !fPatternInfo->hasBody(), fStrong, *fSymbols, status); - } else { - return new ConstantMultiFieldModifier(a, b, !fPatternInfo->hasBody(), fStrong); - } -} - -ImmutablePatternModifier::ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules) - : pm(pm), rules(rules), parent(nullptr) {} - -void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroProps& micros, - UErrorCode& status) const { - parent->processQuantity(quantity, micros, status); - micros.rounder.apply(quantity, status); - if (micros.modMiddle != nullptr) { - return; - } - applyToMicros(micros, quantity, status); -} - -void ImmutablePatternModifier::applyToMicros( - MicroProps& micros, const DecimalQuantity& quantity, UErrorCode& status) const { - if (rules == nullptr) { - micros.modMiddle = pm->getModifierWithoutPlural(quantity.signum()); - } else { - StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, rules, quantity, status); - micros.modMiddle = pm->getModifier(quantity.signum(), pluralForm); - } -} - -const Modifier* ImmutablePatternModifier::getModifier(Signum signum, StandardPlural::Form plural) const { - if (rules == nullptr) { - return pm->getModifierWithoutPlural(signum); - } else { - return pm->getModifier(signum, plural); - } -} - -void ImmutablePatternModifier::addToChain(const MicroPropsGenerator* parent) { - this->parent = parent; -} - - -/** Used by the unsafe code path. */ -MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) { - fParent = parent; - return *this; -} - -void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& micros, - UErrorCode& status) const { - fParent->processQuantity(fq, micros, status); - micros.rounder.apply(fq, status); - if (micros.modMiddle != nullptr) { - return; - } - // 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); - if (needsPlurals()) { - StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, fRules, fq, status); - nonConstThis->setNumberProperties(fq.signum(), pluralForm); - } else { - nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT); - } - micros.modMiddle = this; -} - -int32_t MutablePatternModifier::apply(FormattedStringBuilder& 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); - 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 (!fPatternInfo->hasBody()) { - overwriteLen = output.splice( - leftIndex + prefixLen, - rightIndex + prefixLen, - UnicodeString(), - 0, - 0, - kUndefinedField, - status); - } - CurrencySpacingEnabledModifier::applyCurrencySpacing( - output, - leftIndex, - prefixLen, - rightIndex + overwriteLen + prefixLen, - suffixLen, - *fSymbols, - status); - return prefixLen + overwriteLen + suffixLen; -} - -int32_t MutablePatternModifier::getPrefixLength() 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); - - // Enter and exit CharSequence Mode to get the length. - UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception - nonConstThis->prepareAffix(true); - int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // prefix length - return result; -} - -int32_t MutablePatternModifier::getCodePointCount() 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); - - // Render the affixes to get the length - UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception - 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; -} - -bool MutablePatternModifier::isStrong() const { - return fStrong; -} - -bool MutablePatternModifier::containsField(Field field) const { - (void)field; - // This method is not currently used. - UPRV_UNREACHABLE_EXIT; -} - -void MutablePatternModifier::getParameters(Parameters& output) const { - (void)output; - // This method is not currently used. - UPRV_UNREACHABLE_EXIT; -} - -bool MutablePatternModifier::semanticallyEquivalent(const Modifier& other) const { - (void)other; - // This method is not currently used. - UPRV_UNREACHABLE_EXIT; -} - -int32_t MutablePatternModifier::insertPrefix(FormattedStringBuilder& sb, int position, UErrorCode& status) { - prepareAffix(true); - int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status); - return length; -} - -int32_t MutablePatternModifier::insertSuffix(FormattedStringBuilder& sb, int position, UErrorCode& status) { - prepareAffix(false); - int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status); - return length; -} - -/** This method contains the heart of the logic for rendering LDML affix strings. */ -void MutablePatternModifier::prepareAffix(bool isPrefix) { - PatternStringUtils::patternInfoToStringBuilder( - *fPatternInfo, - isPrefix, - PatternStringUtils::resolveSignDisplay(fSignDisplay, fSignum), - fApproximately, - fPlural, - fPerMilleReplacesPercent, - false, // dropCurrencySymbols - currentAffix); -} - -UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const { - UErrorCode localStatus = U_ZERO_ERROR; - switch (type) { - case AffixPatternType::TYPE_MINUS_SIGN: - return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol); - case AffixPatternType::TYPE_PLUS_SIGN: - return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol); - case AffixPatternType::TYPE_APPROXIMATELY_SIGN: - return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kApproximatelySignSymbol); - case AffixPatternType::TYPE_PERCENT: - return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol); - case AffixPatternType::TYPE_PERMILLE: - return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol); - case AffixPatternType::TYPE_CURRENCY_SINGLE: - return getCurrencySymbolForUnitWidth(localStatus); - case AffixPatternType::TYPE_CURRENCY_DOUBLE: - return fCurrencySymbols.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(fPlural != StandardPlural::Form::COUNT); - return fCurrencySymbols.getPluralName(fPlural, localStatus); - case AffixPatternType::TYPE_CURRENCY_QUAD: - return UnicodeString(u"\uFFFD"); - case AffixPatternType::TYPE_CURRENCY_QUINT: - return UnicodeString(u"\uFFFD"); - default: - UPRV_UNREACHABLE_EXIT; - } -} - -UnicodeString MutablePatternModifier::getCurrencySymbolForUnitWidth(UErrorCode& status) const { - switch (fUnitWidth) { - case UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW: - return fCurrencySymbols.getNarrowCurrencySymbol(status); - case UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT: - return fCurrencySymbols.getCurrencySymbol(status); - case UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE: - return fCurrencySymbols.getIntlCurrencySymbol(status); - case UNumberUnitWidth::UNUM_UNIT_WIDTH_FORMAL: - return fCurrencySymbols.getFormalCurrencySymbol(status); - case UNumberUnitWidth::UNUM_UNIT_WIDTH_VARIANT: - return fCurrencySymbols.getVariantCurrencySymbol(status); - case UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN: - return UnicodeString(); - default: - return fCurrencySymbols.getCurrencySymbol(status); - } -} - -UnicodeString MutablePatternModifier::toUnicodeString() const { - // Never called by AffixUtils - UPRV_UNREACHABLE_EXIT; -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#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; + + +AffixPatternProvider::~AffixPatternProvider() = default; + + +MutablePatternModifier::MutablePatternModifier(bool isStrong) + : fStrong(isStrong) {} + +void MutablePatternModifier::setPatternInfo(const AffixPatternProvider* patternInfo, Field field) { + fPatternInfo = patternInfo; + fField = field; +} + +void MutablePatternModifier::setPatternAttributes( + UNumberSignDisplay signDisplay, + bool perMille, + bool approximately) { + fSignDisplay = signDisplay; + fPerMilleReplacesPercent = perMille; + fApproximately = approximately; +} + +void MutablePatternModifier::setSymbols(const DecimalFormatSymbols* symbols, + const CurrencyUnit& currency, + const UNumberUnitWidth unitWidth, + const PluralRules* rules, + UErrorCode& status) { + U_ASSERT((rules != nullptr) == needsPlurals()); + fSymbols = symbols; + fCurrencySymbols = {currency, symbols->getLocale(), *symbols, status}; + fUnitWidth = unitWidth; + fRules = rules; +} + +void MutablePatternModifier::setNumberProperties(Signum signum, StandardPlural::Form plural) { + fSignum = signum; + fPlural = plural; +} + +bool MutablePatternModifier::needsPlurals() const { + UErrorCode statusLocal = U_ZERO_ERROR; + return fPatternInfo->containsSymbolType(AffixPatternType::TYPE_CURRENCY_TRIPLE, statusLocal); + // Silently ignore any error codes. +} + +AdoptingSignumModifierStore MutablePatternModifier::createImmutableForPlural(StandardPlural::Form plural, UErrorCode& status) { + AdoptingSignumModifierStore pm; + + setNumberProperties(SIGNUM_POS, plural); + pm.adoptModifier(SIGNUM_POS, createConstantModifier(status)); + setNumberProperties(SIGNUM_NEG_ZERO, plural); + pm.adoptModifier(SIGNUM_NEG_ZERO, createConstantModifier(status)); + setNumberProperties(SIGNUM_POS_ZERO, plural); + pm.adoptModifier(SIGNUM_POS_ZERO, createConstantModifier(status)); + setNumberProperties(SIGNUM_NEG, plural); + pm.adoptModifier(SIGNUM_NEG, createConstantModifier(status)); + + return pm; +} + +ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) { + // TODO: Move StandardPlural VALUES to standardplural.h + static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = { + StandardPlural::Form::ZERO, + StandardPlural::Form::ONE, + StandardPlural::Form::TWO, + StandardPlural::Form::FEW, + StandardPlural::Form::MANY, + StandardPlural::Form::OTHER}; + + auto pm = new AdoptingModifierStore(); + if (pm == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + if (needsPlurals()) { + // Slower path when we require the plural keyword. + for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) { + pm->adoptSignumModifierStore(plural, createImmutableForPlural(plural, status)); + } + if (U_FAILURE(status)) { + delete pm; + return nullptr; + } + return new ImmutablePatternModifier(pm, fRules); // adopts pm + } else { + // Faster path when plural keyword is not needed. + pm->adoptSignumModifierStoreNoPlural(createImmutableForPlural(StandardPlural::Form::COUNT, status)); + if (U_FAILURE(status)) { + delete pm; + return nullptr; + } + return new ImmutablePatternModifier(pm, nullptr); // adopts pm + } +} + +ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErrorCode& status) { + FormattedStringBuilder a; + FormattedStringBuilder b; + insertPrefix(a, 0, status); + insertSuffix(b, 0, status); + if (fPatternInfo->hasCurrencySign()) { + return new CurrencySpacingEnabledModifier( + a, b, !fPatternInfo->hasBody(), fStrong, *fSymbols, status); + } else { + return new ConstantMultiFieldModifier(a, b, !fPatternInfo->hasBody(), fStrong); + } +} + +ImmutablePatternModifier::ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules) + : pm(pm), rules(rules), parent(nullptr) {} + +void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroProps& micros, + UErrorCode& status) const { + parent->processQuantity(quantity, micros, status); + micros.rounder.apply(quantity, status); + if (micros.modMiddle != nullptr) { + return; + } + applyToMicros(micros, quantity, status); +} + +void ImmutablePatternModifier::applyToMicros( + MicroProps& micros, const DecimalQuantity& quantity, UErrorCode& status) const { + if (rules == nullptr) { + micros.modMiddle = pm->getModifierWithoutPlural(quantity.signum()); + } else { + StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, rules, quantity, status); + micros.modMiddle = pm->getModifier(quantity.signum(), pluralForm); + } +} + +const Modifier* ImmutablePatternModifier::getModifier(Signum signum, StandardPlural::Form plural) const { + if (rules == nullptr) { + return pm->getModifierWithoutPlural(signum); + } else { + return pm->getModifier(signum, plural); + } +} + +void ImmutablePatternModifier::addToChain(const MicroPropsGenerator* parent) { + this->parent = parent; +} + + +/** Used by the unsafe code path. */ +MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) { + fParent = parent; + return *this; +} + +void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& micros, + UErrorCode& status) const { + fParent->processQuantity(fq, micros, status); + micros.rounder.apply(fq, status); + if (micros.modMiddle != nullptr) { + return; + } + // 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); + if (needsPlurals()) { + StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, fRules, fq, status); + nonConstThis->setNumberProperties(fq.signum(), pluralForm); + } else { + nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT); + } + micros.modMiddle = this; +} + +int32_t MutablePatternModifier::apply(FormattedStringBuilder& 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); + 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 (!fPatternInfo->hasBody()) { + overwriteLen = output.splice( + leftIndex + prefixLen, + rightIndex + prefixLen, + UnicodeString(), + 0, + 0, + kUndefinedField, + status); + } + CurrencySpacingEnabledModifier::applyCurrencySpacing( + output, + leftIndex, + prefixLen, + rightIndex + overwriteLen + prefixLen, + suffixLen, + *fSymbols, + status); + return prefixLen + overwriteLen + suffixLen; +} + +int32_t MutablePatternModifier::getPrefixLength() 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); + + // Enter and exit CharSequence Mode to get the length. + UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception + nonConstThis->prepareAffix(true); + int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // prefix length + return result; +} + +int32_t MutablePatternModifier::getCodePointCount() 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); + + // Render the affixes to get the length + UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception + 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; +} + +bool MutablePatternModifier::isStrong() const { + return fStrong; +} + +bool MutablePatternModifier::containsField(Field field) const { + (void)field; + // This method is not currently used. + UPRV_UNREACHABLE_EXIT; +} + +void MutablePatternModifier::getParameters(Parameters& output) const { + (void)output; + // This method is not currently used. + UPRV_UNREACHABLE_EXIT; +} + +bool MutablePatternModifier::semanticallyEquivalent(const Modifier& other) const { + (void)other; + // This method is not currently used. + UPRV_UNREACHABLE_EXIT; +} + +int32_t MutablePatternModifier::insertPrefix(FormattedStringBuilder& sb, int position, UErrorCode& status) { + prepareAffix(true); + int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status); + return length; +} + +int32_t MutablePatternModifier::insertSuffix(FormattedStringBuilder& sb, int position, UErrorCode& status) { + prepareAffix(false); + int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status); + return length; +} + +/** This method contains the heart of the logic for rendering LDML affix strings. */ +void MutablePatternModifier::prepareAffix(bool isPrefix) { + PatternStringUtils::patternInfoToStringBuilder( + *fPatternInfo, + isPrefix, + PatternStringUtils::resolveSignDisplay(fSignDisplay, fSignum), + fApproximately, + fPlural, + fPerMilleReplacesPercent, + false, // dropCurrencySymbols + currentAffix); +} + +UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const { + UErrorCode localStatus = U_ZERO_ERROR; + switch (type) { + case AffixPatternType::TYPE_MINUS_SIGN: + return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol); + case AffixPatternType::TYPE_PLUS_SIGN: + return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol); + case AffixPatternType::TYPE_APPROXIMATELY_SIGN: + return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kApproximatelySignSymbol); + case AffixPatternType::TYPE_PERCENT: + return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol); + case AffixPatternType::TYPE_PERMILLE: + return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol); + case AffixPatternType::TYPE_CURRENCY_SINGLE: + return getCurrencySymbolForUnitWidth(localStatus); + case AffixPatternType::TYPE_CURRENCY_DOUBLE: + return fCurrencySymbols.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(fPlural != StandardPlural::Form::COUNT); + return fCurrencySymbols.getPluralName(fPlural, localStatus); + case AffixPatternType::TYPE_CURRENCY_QUAD: + return UnicodeString(u"\uFFFD"); + case AffixPatternType::TYPE_CURRENCY_QUINT: + return UnicodeString(u"\uFFFD"); + default: + UPRV_UNREACHABLE_EXIT; + } +} + +UnicodeString MutablePatternModifier::getCurrencySymbolForUnitWidth(UErrorCode& status) const { + switch (fUnitWidth) { + case UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW: + return fCurrencySymbols.getNarrowCurrencySymbol(status); + case UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT: + return fCurrencySymbols.getCurrencySymbol(status); + case UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE: + return fCurrencySymbols.getIntlCurrencySymbol(status); + case UNumberUnitWidth::UNUM_UNIT_WIDTH_FORMAL: + return fCurrencySymbols.getFormalCurrencySymbol(status); + case UNumberUnitWidth::UNUM_UNIT_WIDTH_VARIANT: + return fCurrencySymbols.getVariantCurrencySymbol(status); + case UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN: + return UnicodeString(); + default: + return fCurrencySymbols.getCurrencySymbol(status); + } +} + +UnicodeString MutablePatternModifier::toUnicodeString() const { + // Never called by AffixUtils + UPRV_UNREACHABLE_EXIT; +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_patternmodifier.h b/deps/icu-small/source/i18n/number_patternmodifier.h index 4f825e1ed21916..3865757adc96b8 100644 --- a/deps/icu-small/source/i18n/number_patternmodifier.h +++ b/deps/icu-small/source/i18n/number_patternmodifier.h @@ -1,262 +1,265 @@ -// © 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_PATTERNMODIFIER_H__ -#define __NUMBER_PATTERNMODIFIER_H__ - -#include "standardplural.h" -#include "unicode/numberformatter.h" -#include "number_patternstring.h" -#include "number_types.h" -#include "number_modifiers.h" -#include "number_utils.h" -#include "number_currencysymbols.h" - -U_NAMESPACE_BEGIN - -// Export an explicit template instantiation of the LocalPointer that is used as a -// data member of AdoptingModifierStore. -// (When building DLLs for Windows this is required.) -#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN -#if defined(_MSC_VER) -// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!= -#pragma warning(push) -#pragma warning(disable : 4661) -#endif -template class U_I18N_API LocalPointerBase; -template class U_I18N_API LocalPointer; -#if defined(_MSC_VER) -#pragma warning(pop) -#endif -#endif - -namespace number { -namespace impl { - -// Forward declaration -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; - - void processQuantity(DecimalQuantity&, MicroProps& micros, UErrorCode& status) const U_OVERRIDE; - - void applyToMicros(MicroProps& micros, const DecimalQuantity& quantity, UErrorCode& status) const; - - const Modifier* getModifier(Signum signum, StandardPlural::Form plural) const; - - // Non-const method: - void addToChain(const MicroPropsGenerator* parent); - - private: - ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules); - - const LocalPointer pm; - const PluralRules* rules; - const MicroPropsGenerator* parent; - - friend class MutablePatternModifier; -}; - -/** - * This class is a {@link Modifier} that wraps a decimal format pattern. It applies the pattern's affixes in - * {@link Modifier#apply}. - * - *

      - * In addition to being a Modifier, this class contains the business logic for substituting the correct locale symbols - * into the affixes of the decimal format pattern. - * - *

      - * In order to use this class, create a new instance and call the following four setters: {@link #setPatternInfo}, - * {@link #setPatternAttributes}, {@link #setSymbols}, and {@link #setNumberProperties}. After calling these four - * setters, the instance will be ready for use as a Modifier. - * - *

      - * This is a MUTABLE, NON-THREAD-SAFE class designed for performance. Do NOT save references to this or attempt to use - * it from multiple threads! Instead, you can obtain a safe, immutable decimal format pattern modifier by calling - * {@link MutablePatternModifier#createImmutable}, in effect treating this instance as a builder for the immutable - * variant. - */ -class U_I18N_API MutablePatternModifier - : public MicroPropsGenerator, - public Modifier, - public SymbolProvider, - public UMemory { - public: - - ~MutablePatternModifier() U_OVERRIDE = default; - - /** - * @param isStrong - * Whether the modifier should be considered strong. For more information, see - * {@link Modifier#isStrong()}. Most of the time, decimal format pattern modifiers should be considered - * as non-strong. - */ - explicit MutablePatternModifier(bool isStrong); - - /** - * Sets a reference to the parsed decimal format pattern, usually obtained from - * {@link PatternStringParser#parseToPatternInfo(String)}, but any implementation of {@link AffixPatternProvider} is - * accepted. - * - * @param field - * Which field to use for literal characters in the pattern. - */ - void setPatternInfo(const AffixPatternProvider *patternInfo, Field field); - - /** - * Sets attributes that imply changes to the literal interpretation of the pattern string affixes. - * - * @param signDisplay - * Whether to force a plus sign on positive numbers. - * @param perMille - * Whether to substitute the percent sign in the pattern with a permille sign. - * @param approximately - * Whether to prepend approximately to the sign - */ - void setPatternAttributes(UNumberSignDisplay signDisplay, bool perMille, bool approximately); - - /** - * Sets locale-specific details that affect the symbols substituted into the pattern string affixes. - * - * @param symbols - * The desired instance of DecimalFormatSymbols. - * @param currency - * The currency 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()}. - * @param status - * Set if an error occurs while loading currency data. - */ - void setSymbols(const DecimalFormatSymbols* symbols, const CurrencyUnit& currency, - UNumberUnitWidth unitWidth, const PluralRules* rules, UErrorCode& status); - - /** - * Sets attributes of the current number being processed. - * - * @param signum - * -1 if negative; +1 if positive; or 0 if zero. - * @param plural - * The plural form of the number, required only if the pattern contains the triple - * currency sign, "¤¤¤" (and as indicated by {@link #needsPlurals()}). - */ - void setNumberProperties(Signum signum, StandardPlural::Form plural); - - /** - * Returns true if the pattern represented by this MurkyModifier requires a plural keyword in order to localize. - * This is currently true only if there is a currency long name placeholder in the pattern ("¤¤¤"). - */ - bool needsPlurals() const; - - /** - * Creates a new quantity-dependent Modifier that behaves the same as the current instance, but which is immutable - * and can be saved for future use. The number properties in the current instance are mutated; all other properties - * are left untouched. - * - *

      - * The resulting modifier cannot be used in a QuantityChain. - * - *

      - * CREATES A NEW HEAP OBJECT; THE CALLER GETS OWNERSHIP. - * - * @return An immutable that supports both positive and negative numbers. - */ - ImmutablePatternModifier *createImmutable(UErrorCode &status); - - MicroPropsGenerator &addToChain(const MicroPropsGenerator *parent); - - void processQuantity(DecimalQuantity &, MicroProps µs, UErrorCode &status) const U_OVERRIDE; - - int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, - UErrorCode &status) const U_OVERRIDE; - - int32_t getPrefixLength() const U_OVERRIDE; - - int32_t getCodePointCount() const U_OVERRIDE; - - bool isStrong() const U_OVERRIDE; - - bool containsField(Field field) const U_OVERRIDE; - - void getParameters(Parameters& output) const U_OVERRIDE; - - bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; - - /** - * Returns the string that substitutes a given symbol type in a pattern. - */ - UnicodeString getSymbol(AffixPatternType type) const U_OVERRIDE; - - /** - * Returns the currency symbol for the unit width specified in setSymbols() - */ - UnicodeString getCurrencySymbolForUnitWidth(UErrorCode& status) const; - - UnicodeString toUnicodeString() const; - - private: - // Modifier details (initialized in constructor) - const bool fStrong; - - // Pattern details (initialized in setPatternInfo and setPatternAttributes) - const AffixPatternProvider *fPatternInfo; - Field fField; - UNumberSignDisplay fSignDisplay; - bool fPerMilleReplacesPercent; - bool fApproximately; - - // Symbol details (initialized in setSymbols) - const DecimalFormatSymbols *fSymbols; - UNumberUnitWidth fUnitWidth; - CurrencySymbols fCurrencySymbols; - const PluralRules *fRules; - - // Number details (initialized in setNumberProperties) - Signum fSignum; - StandardPlural::Form fPlural; - - // QuantityChain details (initialized in addToChain) - const MicroPropsGenerator *fParent; - - // Transient fields for rendering - UnicodeString currentAffix; - - /** - * Uses the current properties to create a single {@link ConstantMultiFieldModifier} with currency spacing support - * if required. - * - *

      - * CREATES A NEW HEAP OBJECT; THE CALLER GETS OWNERSHIP. - * - * @param a - * A working FormattedStringBuilder object; passed from the outside to prevent the need to create many new - * instances if this method is called in a loop. - * @param b - * Another working FormattedStringBuilder object. - * @return The constant modifier object. - */ - ConstantMultiFieldModifier *createConstantModifier(UErrorCode &status); - - int32_t insertPrefix(FormattedStringBuilder &sb, int position, UErrorCode &status); - - int32_t insertSuffix(FormattedStringBuilder &sb, int position, UErrorCode &status); - - void prepareAffix(bool isPrefix); -}; - - -} // namespace impl -} // namespace number -U_NAMESPACE_END - -#endif //__NUMBER_PATTERNMODIFIER_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_PATTERNMODIFIER_H__ +#define __NUMBER_PATTERNMODIFIER_H__ + +#include "standardplural.h" +#include "unicode/numberformatter.h" +#include "number_patternstring.h" +#include "number_types.h" +#include "number_modifiers.h" +#include "number_utils.h" +#include "number_currencysymbols.h" + +U_NAMESPACE_BEGIN + +// Export an explicit template instantiation of the LocalPointer that is used as a +// data member of AdoptingModifierStore. +// (When building DLLs for Windows this is required.) +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +#if defined(_MSC_VER) +// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!= +#pragma warning(push) +#pragma warning(disable : 4661) +#endif +template class U_I18N_API LocalPointerBase; +template class U_I18N_API LocalPointer; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#endif + +namespace number { +namespace impl { + +// Forward declaration +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() override = default; + + void processQuantity(DecimalQuantity&, MicroProps& micros, UErrorCode& status) const override; + + void applyToMicros(MicroProps& micros, const DecimalQuantity& quantity, UErrorCode& status) const; + + const Modifier* getModifier(Signum signum, StandardPlural::Form plural) const; + + // Non-const method: + void addToChain(const MicroPropsGenerator* parent); + + private: + ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules); + + const LocalPointer pm; + const PluralRules* rules; + const MicroPropsGenerator* parent; + + friend class MutablePatternModifier; +}; + +/** + * This class is a {@link Modifier} that wraps a decimal format pattern. It applies the pattern's affixes in + * {@link Modifier#apply}. + * + *

      + * In addition to being a Modifier, this class contains the business logic for substituting the correct locale symbols + * into the affixes of the decimal format pattern. + * + *

      + * In order to use this class, create a new instance and call the following four setters: {@link #setPatternInfo}, + * {@link #setPatternAttributes}, {@link #setSymbols}, and {@link #setNumberProperties}. After calling these four + * setters, the instance will be ready for use as a Modifier. + * + *

      + * This is a MUTABLE, NON-THREAD-SAFE class designed for performance. Do NOT save references to this or attempt to use + * it from multiple threads! Instead, you can obtain a safe, immutable decimal format pattern modifier by calling + * {@link MutablePatternModifier#createImmutable}, in effect treating this instance as a builder for the immutable + * variant. + */ +class U_I18N_API MutablePatternModifier + : public MicroPropsGenerator, + public Modifier, + public SymbolProvider, + public UMemory { + public: + + ~MutablePatternModifier() override = default; + + /** + * @param isStrong + * Whether the modifier should be considered strong. For more information, see + * {@link Modifier#isStrong()}. Most of the time, decimal format pattern modifiers should be considered + * as non-strong. + */ + explicit MutablePatternModifier(bool isStrong); + + /** + * Sets a reference to the parsed decimal format pattern, usually obtained from + * {@link PatternStringParser#parseToPatternInfo(String)}, but any implementation of {@link AffixPatternProvider} is + * accepted. + * + * @param field + * Which field to use for literal characters in the pattern. + */ + void setPatternInfo(const AffixPatternProvider *patternInfo, Field field); + + /** + * Sets attributes that imply changes to the literal interpretation of the pattern string affixes. + * + * @param signDisplay + * Whether to force a plus sign on positive numbers. + * @param perMille + * Whether to substitute the percent sign in the pattern with a permille sign. + * @param approximately + * Whether to prepend approximately to the sign + */ + void setPatternAttributes(UNumberSignDisplay signDisplay, bool perMille, bool approximately); + + /** + * Sets locale-specific details that affect the symbols substituted into the pattern string affixes. + * + * @param symbols + * The desired instance of DecimalFormatSymbols. + * @param currency + * The currency 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()}. + * @param status + * Set if an error occurs while loading currency data. + */ + void setSymbols(const DecimalFormatSymbols* symbols, const CurrencyUnit& currency, + UNumberUnitWidth unitWidth, const PluralRules* rules, UErrorCode& status); + + /** + * Sets attributes of the current number being processed. + * + * @param signum + * -1 if negative; +1 if positive; or 0 if zero. + * @param plural + * The plural form of the number, required only if the pattern contains the triple + * currency sign, "¤¤¤" (and as indicated by {@link #needsPlurals()}). + */ + void setNumberProperties(Signum signum, StandardPlural::Form plural); + + /** + * Returns true if the pattern represented by this MurkyModifier requires a plural keyword in order to localize. + * This is currently true only if there is a currency long name placeholder in the pattern ("¤¤¤"). + */ + bool needsPlurals() const; + + /** Creates a quantity-dependent Modifier for the specified plural form. */ + AdoptingSignumModifierStore createImmutableForPlural(StandardPlural::Form plural, UErrorCode& status); + + /** + * Creates a new quantity-dependent Modifier that behaves the same as the current instance, but which is immutable + * and can be saved for future use. The number properties in the current instance are mutated; all other properties + * are left untouched. + * + *

      + * The resulting modifier cannot be used in a QuantityChain. + * + *

      + * CREATES A NEW HEAP OBJECT; THE CALLER GETS OWNERSHIP. + * + * @return An immutable that supports both positive and negative numbers. + */ + ImmutablePatternModifier *createImmutable(UErrorCode &status); + + MicroPropsGenerator &addToChain(const MicroPropsGenerator *parent); + + void processQuantity(DecimalQuantity &, MicroProps µs, UErrorCode &status) const override; + + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + UErrorCode &status) const override; + + int32_t getPrefixLength() const override; + + int32_t getCodePointCount() const override; + + bool isStrong() const override; + + bool containsField(Field field) const override; + + void getParameters(Parameters& output) const override; + + bool semanticallyEquivalent(const Modifier& other) const override; + + /** + * Returns the string that substitutes a given symbol type in a pattern. + */ + UnicodeString getSymbol(AffixPatternType type) const override; + + /** + * Returns the currency symbol for the unit width specified in setSymbols() + */ + UnicodeString getCurrencySymbolForUnitWidth(UErrorCode& status) const; + + UnicodeString toUnicodeString() const; + + private: + // Modifier details (initialized in constructor) + const bool fStrong; + + // Pattern details (initialized in setPatternInfo and setPatternAttributes) + const AffixPatternProvider *fPatternInfo; + Field fField; + UNumberSignDisplay fSignDisplay; + bool fPerMilleReplacesPercent; + bool fApproximately; + + // Symbol details (initialized in setSymbols) + const DecimalFormatSymbols *fSymbols; + UNumberUnitWidth fUnitWidth; + CurrencySymbols fCurrencySymbols; + const PluralRules *fRules; + + // Number details (initialized in setNumberProperties) + Signum fSignum; + StandardPlural::Form fPlural; + + // QuantityChain details (initialized in addToChain) + const MicroPropsGenerator *fParent; + + // Transient fields for rendering + UnicodeString currentAffix; + + /** + * Uses the current properties to create a single {@link ConstantMultiFieldModifier} with currency spacing support + * if required. + * + *

      + * CREATES A NEW HEAP OBJECT; THE CALLER GETS OWNERSHIP. + * + * @param a + * A working FormattedStringBuilder object; passed from the outside to prevent the need to create many new + * instances if this method is called in a loop. + * @param b + * Another working FormattedStringBuilder object. + * @return The constant modifier object. + */ + ConstantMultiFieldModifier *createConstantModifier(UErrorCode &status); + + int32_t insertPrefix(FormattedStringBuilder &sb, int position, UErrorCode &status); + + int32_t insertSuffix(FormattedStringBuilder &sb, int position, UErrorCode &status); + + void prepareAffix(bool isPrefix); +}; + + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif //__NUMBER_PATTERNMODIFIER_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_patternstring.cpp b/deps/icu-small/source/i18n/number_patternstring.cpp index 557a7320856562..836d803f30ae2b 100644 --- a/deps/icu-small/source/i18n/number_patternstring.cpp +++ b/deps/icu-small/source/i18n/number_patternstring.cpp @@ -1,1212 +1,1212 @@ -// © 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 - -// 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" -#include "number_mapper.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - - -void PatternParser::parseToPatternInfo(const UnicodeString& patternString, ParsedPatternInfo& patternInfo, - UErrorCode& status) { - patternInfo.consumePattern(patternString, status); -} - -DecimalFormatProperties -PatternParser::parseToProperties(const UnicodeString& pattern, IgnoreRounding ignoreRounding, - UErrorCode& status) { - DecimalFormatProperties properties; - parseToExistingPropertiesImpl(pattern, properties, ignoreRounding, status); - return properties; -} - -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); - if (index < 0 || index >= endpoints.end - endpoints.start) { - UPRV_UNREACHABLE_EXIT; - } - return pattern.charAt(endpoints.start + index); -} - -int32_t ParsedPatternInfo::length(int32_t flags) const { - return getLengthFromEndpoints(getEndpoints(flags)); -} - -int32_t ParsedPatternInfo::getLengthFromEndpoints(const Endpoints& endpoints) { - return endpoints.end - endpoints.start; -} - -UnicodeString ParsedPatternInfo::getString(int32_t flags) const { - const Endpoints& endpoints = getEndpoints(flags); - if (endpoints.start == endpoints.end) { - return UnicodeString(); - } - // Create a new UnicodeString - return UnicodeString(pattern, endpoints.start, endpoints.end - endpoints.start); -} - -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; - if (isNegative && padding) { - return negative.paddingEndpoints; - } else if (padding) { - return positive.paddingEndpoints; - } else if (prefix && isNegative) { - return negative.prefixEndpoints; - } else if (prefix) { - return positive.prefixEndpoints; - } else if (isNegative) { - return negative.suffixEndpoints; - } else { - return positive.suffixEndpoints; - } -} - -bool ParsedPatternInfo::positiveHasPlusSign() const { - return positive.hasPlusSign; -} - -bool ParsedPatternInfo::hasNegativeSubpattern() const { - return fHasNegativeSubpattern; -} - -bool ParsedPatternInfo::negativeHasMinusSign() const { - return negative.hasMinusSign; -} - -bool ParsedPatternInfo::hasCurrencySign() const { - return positive.hasCurrencySign || (fHasNegativeSubpattern && negative.hasCurrencySign); -} - -bool ParsedPatternInfo::containsSymbolType(AffixPatternType type, UErrorCode& status) const { - return AffixUtils::containsType(pattern, type, status); -} - -bool ParsedPatternInfo::hasBody() const { - return positive.integerTotal > 0; -} - -bool ParsedPatternInfo::currencyAsDecimal() const { - return positive.hasCurrencyDecimal; -} - -///////////////////////////////////////////////////// -/// BEGIN RECURSIVE DESCENT PARSER IMPLEMENTATION /// -///////////////////////////////////////////////////// - -UChar32 ParsedPatternInfo::ParserState::peek() { - if (offset == pattern.length()) { - return -1; - } else { - return pattern.char32At(offset); - } -} - -UChar32 ParsedPatternInfo::ParserState::peek2() { - if (offset == pattern.length()) { - return -1; - } - int32_t cp1 = pattern.char32At(offset); - int32_t offset2 = offset + U16_LENGTH(cp1); - if (offset2 == pattern.length()) { - return -1; - } - return pattern.char32At(offset2); -} - -UChar32 ParsedPatternInfo::ParserState::next() { - int32_t codePoint = peek(); - offset += U16_LENGTH(codePoint); - return codePoint; -} - -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); - if (U_FAILURE(status)) { return; } - if (state.peek() == u';') { - state.next(); // consume the ';' - // Don't consume the negative subpattern if it is empty (trailing ';') - if (state.peek() != -1) { - fHasNegativeSubpattern = true; - currentSubpattern = &negative; - consumeSubpattern(status); - if (U_FAILURE(status)) { return; } - } - } - if (state.peek() != -1) { - state.toParseException(u"Found unquoted special character"); - status = U_UNQUOTED_SPECIAL; - } -} - -void ParsedPatternInfo::consumeSubpattern(UErrorCode& status) { - // subpattern := literals? number exponent? literals? - consumePadding(PadPosition::UNUM_PAD_BEFORE_PREFIX, status); - if (U_FAILURE(status)) { return; } - consumeAffix(currentSubpattern->prefixEndpoints, status); - if (U_FAILURE(status)) { return; } - consumePadding(PadPosition::UNUM_PAD_AFTER_PREFIX, status); - if (U_FAILURE(status)) { return; } - consumeFormat(status); - if (U_FAILURE(status)) { return; } - consumeExponent(status); - if (U_FAILURE(status)) { return; } - consumePadding(PadPosition::UNUM_PAD_BEFORE_SUFFIX, status); - if (U_FAILURE(status)) { return; } - consumeAffix(currentSubpattern->suffixEndpoints, status); - if (U_FAILURE(status)) { return; } - consumePadding(PadPosition::UNUM_PAD_AFTER_SUFFIX, status); - if (U_FAILURE(status)) { return; } -} - -void ParsedPatternInfo::consumePadding(PadPosition paddingLocation, UErrorCode& status) { - if (state.peek() != u'*') { - return; - } - 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) { - // literals := { literal } - endpoints.start = state.offset; - while (true) { - switch (state.peek()) { - case u'#': - case u'@': - case u';': - case u'*': - case u'.': - case u',': - case u'0': - case u'1': - case u'2': - case u'3': - case u'4': - case u'5': - case u'6': - case u'7': - case u'8': - case u'9': - case -1: - // Characters that cannot appear unquoted in a literal - // break outer; - goto after_outer; - - case u'%': - currentSubpattern->hasPercentSign = true; - break; - - case u'‰': - currentSubpattern->hasPerMilleSign = true; - break; - - case u'¤': - currentSubpattern->hasCurrencySign = true; - break; - - case u'-': - currentSubpattern->hasMinusSign = true; - break; - - case u'+': - currentSubpattern->hasPlusSign = true; - break; - - default: - break; - } - consumeLiteral(status); - if (U_FAILURE(status)) { return; } - } - after_outer: - endpoints.end = state.offset; -} - -void ParsedPatternInfo::consumeLiteral(UErrorCode& status) { - if (state.peek() == -1) { - state.toParseException(u"Expected unquoted literal but found EOL"); - status = U_PATTERN_SYNTAX_ERROR; - return; - } else if (state.peek() == u'\'') { - state.next(); // consume the starting quote - while (state.peek() != u'\'') { - if (state.peek() == -1) { - state.toParseException(u"Expected quoted literal but found EOL"); - status = U_PATTERN_SYNTAX_ERROR; - return; - } else { - state.next(); // consume a quoted character - } - } - state.next(); // consume the ending quote - } else { - // consume a non-quoted literal character - state.next(); - } -} - -void ParsedPatternInfo::consumeFormat(UErrorCode& status) { - consumeIntegerFormat(status); - if (U_FAILURE(status)) { return; } - if (state.peek() == u'.') { - state.next(); // consume the decimal point - currentSubpattern->hasDecimal = true; - currentSubpattern->widthExceptAffixes += 1; - consumeFractionFormat(status); - if (U_FAILURE(status)) { return; } - } else if (state.peek() == u'¤') { - // Check if currency is a decimal separator - switch (state.peek2()) { - case u'#': - case u'0': - case u'1': - case u'2': - case u'3': - case u'4': - case u'5': - case u'6': - case u'7': - case u'8': - case u'9': - break; - default: - // Currency symbol followed by a non-numeric character; - // treat as a normal affix. - return; - } - // Currency symbol is followed by a numeric character; - // treat as a decimal separator. - currentSubpattern->hasCurrencySign = true; - currentSubpattern->hasCurrencyDecimal = true; - currentSubpattern->hasDecimal = true; - currentSubpattern->widthExceptAffixes += 1; - state.next(); // consume the symbol - consumeFractionFormat(status); - if (U_FAILURE(status)) { return; } - } -} - -void ParsedPatternInfo::consumeIntegerFormat(UErrorCode& status) { - // Convenience reference: - ParsedSubpatternInfo& result = *currentSubpattern; - - while (true) { - switch (state.peek()) { - case u',': - result.widthExceptAffixes += 1; - result.groupingSizes <<= 16; - break; - - case u'#': - if (result.integerNumerals > 0) { - state.toParseException(u"# cannot follow 0 before decimal point"); - status = U_UNEXPECTED_TOKEN; - return; - } - result.widthExceptAffixes += 1; - result.groupingSizes += 1; - if (result.integerAtSigns > 0) { - result.integerTrailingHashSigns += 1; - } else { - result.integerLeadingHashSigns += 1; - } - result.integerTotal += 1; - break; - - case u'@': - if (result.integerNumerals > 0) { - state.toParseException(u"Cannot mix 0 and @"); - status = U_UNEXPECTED_TOKEN; - return; - } - if (result.integerTrailingHashSigns > 0) { - state.toParseException(u"Cannot nest # inside of a run of @"); - status = U_UNEXPECTED_TOKEN; - return; - } - result.widthExceptAffixes += 1; - result.groupingSizes += 1; - result.integerAtSigns += 1; - result.integerTotal += 1; - break; - - case u'0': - case u'1': - case u'2': - case u'3': - case u'4': - case u'5': - case u'6': - case u'7': - case u'8': - case u'9': - if (result.integerAtSigns > 0) { - state.toParseException(u"Cannot mix @ and 0"); - status = U_UNEXPECTED_TOKEN; - return; - } - result.widthExceptAffixes += 1; - result.groupingSizes += 1; - result.integerNumerals += 1; - result.integerTotal += 1; - if (!result.rounding.isZeroish() || state.peek() != u'0') { - result.rounding.appendDigit(static_cast(state.peek() - u'0'), 0, true); - } - break; - - default: - goto after_outer; - } - state.next(); // consume the symbol - } - - after_outer: - // Disallow patterns with a trailing ',' or with two ',' next to each other - auto grouping1 = static_cast (result.groupingSizes & 0xffff); - auto grouping2 = static_cast ((result.groupingSizes >> 16) & 0xffff); - auto grouping3 = static_cast ((result.groupingSizes >> 32) & 0xffff); - if (grouping1 == 0 && grouping2 != -1) { - state.toParseException(u"Trailing grouping separator is invalid"); - status = U_UNEXPECTED_TOKEN; - return; - } - if (grouping2 == 0 && grouping3 != -1) { - state.toParseException(u"Grouping width of zero is invalid"); - status = U_PATTERN_SYNTAX_ERROR; - return; - } -} - -void ParsedPatternInfo::consumeFractionFormat(UErrorCode& status) { - // Convenience reference: - ParsedSubpatternInfo& result = *currentSubpattern; - - int32_t zeroCounter = 0; - while (true) { - switch (state.peek()) { - case u'#': - result.widthExceptAffixes += 1; - result.fractionHashSigns += 1; - result.fractionTotal += 1; - zeroCounter++; - break; - - case u'0': - case u'1': - case u'2': - case u'3': - case u'4': - case u'5': - case u'6': - case u'7': - case u'8': - case u'9': - if (result.fractionHashSigns > 0) { - state.toParseException(u"0 cannot follow # after decimal point"); - status = U_UNEXPECTED_TOKEN; - return; - } - result.widthExceptAffixes += 1; - result.fractionNumerals += 1; - result.fractionTotal += 1; - if (state.peek() == u'0') { - zeroCounter++; - } else { - result.rounding - .appendDigit(static_cast(state.peek() - u'0'), zeroCounter, false); - zeroCounter = 0; - } - break; - - default: - return; - } - state.next(); // consume the symbol - } -} - -void ParsedPatternInfo::consumeExponent(UErrorCode& status) { - // Convenience reference: - ParsedSubpatternInfo& result = *currentSubpattern; - - if (state.peek() != u'E') { - return; - } - if ((result.groupingSizes & 0xffff0000L) != 0xffff0000L) { - state.toParseException(u"Cannot have grouping separator in scientific notation"); - status = U_MALFORMED_EXPONENTIAL_PATTERN; - return; - } - state.next(); // consume the E - result.widthExceptAffixes++; - if (state.peek() == u'+') { - state.next(); // consume the + - result.exponentHasPlusSign = true; - result.widthExceptAffixes++; - } - while (state.peek() == u'0') { - state.next(); // consume the 0 - result.exponentZeros += 1; - result.widthExceptAffixes++; - } -} - -/////////////////////////////////////////////////// -/// END RECURSIVE DESCENT PARSER IMPLEMENTATION /// -/////////////////////////////////////////////////// - -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? - properties.clear(); - return; - } - - ParsedPatternInfo patternInfo; - parseToPatternInfo(pattern, patternInfo, status); - if (U_FAILURE(status)) { return; } - patternInfoToProperties(properties, patternInfo, ignoreRounding, 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; - - bool ignoreRounding; - if (_ignoreRounding == IGNORE_ROUNDING_NEVER) { - ignoreRounding = false; - } else if (_ignoreRounding == IGNORE_ROUNDING_IF_CURRENCY) { - ignoreRounding = positive.hasCurrencySign; - } else { - U_ASSERT(_ignoreRounding == IGNORE_ROUNDING_ALWAYS); - ignoreRounding = true; - } - - // Grouping settings - auto grouping1 = static_cast (positive.groupingSizes & 0xffff); - auto grouping2 = static_cast ((positive.groupingSizes >> 16) & 0xffff); - 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; - } else { - properties.secondaryGroupingSize = -1; - } - - // For backwards compatibility, require that the pattern emit at least one min digit. - int minInt, minFrac; - if (positive.integerTotal == 0 && positive.fractionTotal > 0) { - // patterns like ".##" - minInt = 0; - minFrac = uprv_max(1, positive.fractionNumerals); - } else if (positive.integerNumerals == 0 && positive.fractionNumerals == 0) { - // patterns like "#.##" - minInt = 1; - minFrac = 0; - } else { - minInt = positive.integerNumerals; - minFrac = positive.fractionNumerals; - } - - // Rounding settings - // Don't set basic rounding when there is a currency sign; defer to CurrencyUsage - if (positive.integerAtSigns > 0) { - properties.minimumFractionDigits = -1; - properties.maximumFractionDigits = -1; - properties.roundingIncrement = 0.0; - properties.minimumSignificantDigits = positive.integerAtSigns; - properties.maximumSignificantDigits = positive.integerAtSigns + positive.integerTrailingHashSigns; - } else if (!positive.rounding.isZeroish()) { - if (!ignoreRounding) { - properties.minimumFractionDigits = minFrac; - properties.maximumFractionDigits = positive.fractionTotal; - properties.roundingIncrement = positive.rounding.toDouble(); - } else { - properties.minimumFractionDigits = -1; - properties.maximumFractionDigits = -1; - properties.roundingIncrement = 0.0; - } - properties.minimumSignificantDigits = -1; - properties.maximumSignificantDigits = -1; - } else { - if (!ignoreRounding) { - properties.minimumFractionDigits = minFrac; - properties.maximumFractionDigits = positive.fractionTotal; - properties.roundingIncrement = 0.0; - } else { - properties.minimumFractionDigits = -1; - properties.maximumFractionDigits = -1; - properties.roundingIncrement = 0.0; - } - properties.minimumSignificantDigits = -1; - properties.maximumSignificantDigits = -1; - } - - // If the pattern ends with a '.' then force the decimal point. - if (positive.hasDecimal && positive.fractionTotal == 0) { - properties.decimalSeparatorAlwaysShown = true; - } else { - properties.decimalSeparatorAlwaysShown = false; - } - - // Persist the currency as decimal separator - properties.currencyAsDecimal = positive.hasCurrencyDecimal; - - // Scientific notation settings - if (positive.exponentZeros > 0) { - properties.exponentSignAlwaysShown = positive.exponentHasPlusSign; - properties.minimumExponentDigits = positive.exponentZeros; - if (positive.integerAtSigns == 0) { - // patterns without '@' can define max integer digits, used for engineering notation - properties.minimumIntegerDigits = positive.integerNumerals; - properties.maximumIntegerDigits = positive.integerTotal; - } else { - // patterns with '@' cannot define max integer digits - properties.minimumIntegerDigits = 1; - properties.maximumIntegerDigits = -1; - } - } else { - properties.exponentSignAlwaysShown = false; - properties.minimumExponentDigits = -1; - properties.minimumIntegerDigits = minInt; - properties.maximumIntegerDigits = -1; - } - - // Compute the affix patterns (required for both padding and affixes) - UnicodeString posPrefix = patternInfo.getString(AffixPatternProvider::AFFIX_PREFIX); - UnicodeString posSuffix = patternInfo.getString(0); - - // Padding settings - if (positive.hasPadding) { - // The width of the positive prefix and suffix templates are included in the padding - 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) { - properties.padString = rawPaddingString; - } else if (rawPaddingString.length() == 2) { - if (rawPaddingString.charAt(0) == u'\'') { - properties.padString.setTo(u"'", -1); - } else { - properties.padString = rawPaddingString; - } - } else { - properties.padString = UnicodeString(rawPaddingString, 1, rawPaddingString.length() - 2); - } - properties.padPosition = positive.paddingLocation; - } else { - properties.formatWidth = -1; - properties.padString.setToBogus(); - properties.padPosition.nullify(); - } - - // Set the affixes - // Always call the setter, even if the prefixes are empty, especially in the case of the - // negative prefix pattern, to prevent default values from overriding the pattern. - properties.positivePrefixPattern = posPrefix; - properties.positiveSuffixPattern = posSuffix; - if (patternInfo.fHasNegativeSubpattern) { - properties.negativePrefixPattern = patternInfo.getString( - AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN | AffixPatternProvider::AFFIX_PREFIX); - properties.negativeSuffixPattern = patternInfo.getString( - AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN); - } else { - properties.negativePrefixPattern.setToBogus(); - properties.negativeSuffixPattern.setToBogus(); - } - - // Set the magnitude multiplier - if (positive.hasPercentSign) { - properties.magnitudeMultiplier = 2; - } else if (positive.hasPerMilleSign) { - properties.magnitudeMultiplier = 3; - } else { - properties.magnitudeMultiplier = 0; - } -} - -/////////////////////////////////////////////////////////////////// -/// End PatternStringParser.java; begin PatternStringUtils.java /// -/////////////////////////////////////////////////////////////////// - -// Determine whether a given roundingIncrement should be ignored for formatting -// based on the current maxFrac value (maximum fraction digits). For example a -// roundingIncrement of 0.01 should be ignored if maxFrac is 1, but not if maxFrac -// is 2 or more. Note that roundingIncrements are rounded in significance, so -// a roundingIncrement of 0.006 is treated like 0.01 for this determination, i.e. -// it should not be ignored if maxFrac is 2 or more (but a roundingIncrement of -// 0.005 is treated like 0.001 for significance). This is the reason for the -// initial doubling below. -// roundIncr must be non-zero. -bool PatternStringUtils::ignoreRoundingIncrement(double roundIncr, int32_t maxFrac) { - if (maxFrac < 0) { - return false; - } - int32_t frac = 0; - roundIncr *= 2.0; - for (frac = 0; frac <= maxFrac && roundIncr <= 1.0; frac++, roundIncr *= 10.0); - return (frac > maxFrac); -} - -UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatProperties& properties, - UErrorCode& status) { - UnicodeString sb; - - // Convenience references - // The uprv_min() calls prevent DoS - int32_t dosMax = 100; - int32_t grouping1 = uprv_max(0, uprv_min(properties.groupingSize, dosMax)); - int32_t grouping2 = uprv_max(0, uprv_min(properties.secondaryGroupingSize, dosMax)); - bool useGrouping = properties.groupingUsed; - int32_t paddingWidth = uprv_min(properties.formatWidth, dosMax); - NullableValue paddingLocation = properties.padPosition; - UnicodeString paddingString = properties.padString; - int32_t minInt = uprv_max(0, uprv_min(properties.minimumIntegerDigits, dosMax)); - int32_t maxInt = uprv_min(properties.maximumIntegerDigits, dosMax); - int32_t minFrac = uprv_max(0, uprv_min(properties.minimumFractionDigits, dosMax)); - int32_t maxFrac = uprv_min(properties.maximumFractionDigits, dosMax); - int32_t minSig = uprv_min(properties.minimumSignificantDigits, dosMax); - int32_t maxSig = uprv_min(properties.maximumSignificantDigits, dosMax); - bool alwaysShowDecimal = properties.decimalSeparatorAlwaysShown; - int32_t exponentDigits = uprv_min(properties.minimumExponentDigits, dosMax); - bool exponentShowPlusSign = properties.exponentSignAlwaysShown; - - AutoAffixPatternProvider affixProvider(properties, status); - - // Prefixes - sb.append(affixProvider.get().getString(AffixPatternProvider::AFFIX_POS_PREFIX)); - int32_t afterPrefixPos = sb.length(); - - // Figure out the grouping sizes. - if (!useGrouping) { - grouping1 = 0; - grouping2 = 0; - } else if (grouping1 == grouping2) { - grouping1 = 0; - } - int32_t groupingLength = grouping1 + grouping2 + 1; - - // Figure out the digits we need to put in the pattern. - double increment = properties.roundingIncrement; - UnicodeString digitsString; - int32_t digitsStringScale = 0; - if (maxSig != uprv_min(dosMax, -1)) { - // Significant Digits. - while (digitsString.length() < minSig) { - digitsString.append(u'@'); - } - while (digitsString.length() < maxSig) { - digitsString.append(u'#'); - } - } else if (increment != 0.0 && !ignoreRoundingIncrement(increment,maxFrac)) { - // Rounding Increment. - DecimalQuantity incrementQuantity; - incrementQuantity.setToDouble(increment); - incrementQuantity.roundToInfinity(); - digitsStringScale = incrementQuantity.getLowerDisplayMagnitude(); - incrementQuantity.adjustMagnitude(-digitsStringScale); - incrementQuantity.setMinInteger(minInt - digitsStringScale); - UnicodeString str = incrementQuantity.toPlainString(); - if (str.charAt(0) == u'-') { - // TODO: Unsupported operation exception or fail silently? - digitsString.append(str, 1, str.length() - 1); - } else { - digitsString.append(str); - } - } - while (digitsString.length() + digitsStringScale < minInt) { - digitsString.insert(0, u'0'); - } - while (-digitsStringScale < minFrac) { - digitsString.append(u'0'); - digitsStringScale--; - } - - // Write the digits to the string builder - int32_t m0 = uprv_max(groupingLength, digitsString.length() + digitsStringScale); - m0 = (maxInt != dosMax) ? uprv_max(maxInt, m0) - 1 : m0 - 1; - int32_t mN = (maxFrac != dosMax) ? uprv_min(-maxFrac, digitsStringScale) : digitsStringScale; - for (int32_t magnitude = m0; magnitude >= mN; magnitude--) { - int32_t di = digitsString.length() + digitsStringScale - magnitude - 1; - if (di < 0 || di >= digitsString.length()) { - sb.append(u'#'); - } else { - sb.append(digitsString.charAt(di)); - } - // Decimal separator - if (magnitude == 0 && (alwaysShowDecimal || mN < 0)) { - if (properties.currencyAsDecimal) { - sb.append(u'¤'); - } else { - sb.append(u'.'); - } - } - if (!useGrouping) { - continue; - } - // Least-significant grouping separator - if (magnitude > 0 && magnitude == grouping1) { - sb.append(u','); - } - // All other grouping separators - if (magnitude > grouping1 && grouping2 > 0 && (magnitude - grouping1) % grouping2 == 0) { - sb.append(u','); - } - } - - // Exponential notation - if (exponentDigits != uprv_min(dosMax, -1)) { - sb.append(u'E'); - if (exponentShowPlusSign) { - sb.append(u'+'); - } - for (int32_t i = 0; i < exponentDigits; i++) { - sb.append(u'0'); - } - } - - // Suffixes - int32_t beforeSuffixPos = sb.length(); - sb.append(affixProvider.get().getString(AffixPatternProvider::AFFIX_POS_SUFFIX)); - - // Resolve Padding - if (paddingWidth > 0 && !paddingLocation.isNull()) { - while (paddingWidth - sb.length() > 0) { - sb.insert(afterPrefixPos, u'#'); - beforeSuffixPos++; - } - int32_t addedLength; - switch (paddingLocation.get(status)) { - case PadPosition::UNUM_PAD_BEFORE_PREFIX: - addedLength = escapePaddingString(paddingString, sb, 0, status); - sb.insert(0, u'*'); - afterPrefixPos += addedLength + 1; - beforeSuffixPos += addedLength + 1; - break; - case PadPosition::UNUM_PAD_AFTER_PREFIX: - addedLength = escapePaddingString(paddingString, sb, afterPrefixPos, status); - sb.insert(afterPrefixPos, u'*'); - afterPrefixPos += addedLength + 1; - beforeSuffixPos += addedLength + 1; - break; - case PadPosition::UNUM_PAD_BEFORE_SUFFIX: - escapePaddingString(paddingString, sb, beforeSuffixPos, status); - sb.insert(beforeSuffixPos, u'*'); - break; - case PadPosition::UNUM_PAD_AFTER_SUFFIX: - sb.append(u'*'); - escapePaddingString(paddingString, sb, sb.length(), status); - break; - } - if (U_FAILURE(status)) { return sb; } - } - - // Negative affixes - // Ignore if the negative prefix pattern is "-" and the negative suffix is empty - if (affixProvider.get().hasNegativeSubpattern()) { - sb.append(u';'); - sb.append(affixProvider.get().getString(AffixPatternProvider::AFFIX_NEG_PREFIX)); - // Copy the positive digit format into the negative. - // This is optional; the pattern is the same as if '#' were appended here instead. - // NOTE: It is not safe to append the UnicodeString to itself, so we need to copy. - // See https://unicode-org.atlassian.net/browse/ICU-13707 - UnicodeString copy(sb); - sb.append(copy, afterPrefixPos, beforeSuffixPos - afterPrefixPos); - sb.append(affixProvider.get().getString(AffixPatternProvider::AFFIX_NEG_SUFFIX)); - } - - return sb; -} - -int PatternStringUtils::escapePaddingString(UnicodeString input, UnicodeString& output, int startIndex, - UErrorCode& status) { - (void) status; - if (input.length() == 0) { - input.setTo(kFallbackPaddingString, -1); - } - int startLength = output.length(); - if (input.length() == 1) { - if (input.compare(u"'", -1) == 0) { - output.insert(startIndex, u"''", -1); - } else { - output.insert(startIndex, input); - } - } else { - output.insert(startIndex, u'\''); - int offset = 1; - for (int i = 0; i < input.length(); i++) { - // it's okay to deal in chars here because the quote mark is the only interesting thing. - char16_t ch = input.charAt(i); - if (ch == u'\'') { - output.insert(startIndex + offset, u"''", -1); - offset += 2; - } else { - output.insert(startIndex + offset, ch); - offset += 1; - } - } - output.insert(startIndex + offset, u'\''); - } - 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; - // TODO: Add approximately sign here? - 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, - PatternSignType patternSignType, - bool approximately, - StandardPlural::Form plural, - bool perMilleReplacesPercent, - bool dropCurrencySymbols, - UnicodeString& output) { - - // Should the output render '+' where '-' would normally appear in the pattern? - bool plusReplacesMinusSign = (patternSignType == PATTERN_SIGN_TYPE_POS_SIGN) - && !patternInfo.positiveHasPlusSign(); - - // Should we use the affix from the negative subpattern? - // (If not, we will use the positive subpattern.) - bool useNegativeAffixPattern = patternInfo.hasNegativeSubpattern() - && (patternSignType == PATTERN_SIGN_TYPE_NEG - || (patternInfo.negativeHasMinusSign() && (plusReplacesMinusSign || approximately))); - - // 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 (patternSignType == PATTERN_SIGN_TYPE_NEG) { - prependSign = true; - } else { - prependSign = plusReplacesMinusSign || approximately; - } - - // What symbols should take the place of the sign placeholder? - const char16_t* signSymbols = u"-"; - if (approximately) { - if (plusReplacesMinusSign) { - signSymbols = u"~+"; - } else if (patternSignType == PATTERN_SIGN_TYPE_NEG) { - signSymbols = u"~-"; - } else { - signSymbols = u"~"; - } - } else if (plusReplacesMinusSign) { - signSymbols = u"+"; - } - - // Compute the number of tokens in the affix pattern (signSymbols is considered one token). - 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 (candidate == u'-') { - if (u_strlen(signSymbols) == 1) { - candidate = signSymbols[0]; - } else { - output.append(signSymbols[0]); - candidate = signSymbols[1]; - } - } - if (perMilleReplacesPercent && candidate == u'%') { - candidate = u'‰'; - } - if (dropCurrencySymbols && candidate == u'\u00A4') { - continue; - } - output.append(candidate); - } -} - -PatternSignType PatternStringUtils::resolveSignDisplay(UNumberSignDisplay signDisplay, Signum signum) { - switch (signDisplay) { - case UNUM_SIGN_AUTO: - case UNUM_SIGN_ACCOUNTING: - switch (signum) { - case SIGNUM_NEG: - case SIGNUM_NEG_ZERO: - return PATTERN_SIGN_TYPE_NEG; - case SIGNUM_POS_ZERO: - case SIGNUM_POS: - return PATTERN_SIGN_TYPE_POS; - default: - break; - } - break; - - case UNUM_SIGN_ALWAYS: - case UNUM_SIGN_ACCOUNTING_ALWAYS: - switch (signum) { - case SIGNUM_NEG: - case SIGNUM_NEG_ZERO: - return PATTERN_SIGN_TYPE_NEG; - case SIGNUM_POS_ZERO: - case SIGNUM_POS: - return PATTERN_SIGN_TYPE_POS_SIGN; - default: - break; - } - break; - - case UNUM_SIGN_EXCEPT_ZERO: - case UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO: - switch (signum) { - case SIGNUM_NEG: - return PATTERN_SIGN_TYPE_NEG; - case SIGNUM_NEG_ZERO: - case SIGNUM_POS_ZERO: - return PATTERN_SIGN_TYPE_POS; - case SIGNUM_POS: - return PATTERN_SIGN_TYPE_POS_SIGN; - default: - break; - } - break; - - case UNUM_SIGN_NEGATIVE: - case UNUM_SIGN_ACCOUNTING_NEGATIVE: - switch (signum) { - case SIGNUM_NEG: - return PATTERN_SIGN_TYPE_NEG; - case SIGNUM_NEG_ZERO: - case SIGNUM_POS_ZERO: - case SIGNUM_POS: - return PATTERN_SIGN_TYPE_POS; - default: - break; - } - break; - - case UNUM_SIGN_NEVER: - return PATTERN_SIGN_TYPE_POS; - - default: - break; - } - - UPRV_UNREACHABLE_EXIT; - return PATTERN_SIGN_TYPE_POS; -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +// 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" +#include "number_mapper.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +void PatternParser::parseToPatternInfo(const UnicodeString& patternString, ParsedPatternInfo& patternInfo, + UErrorCode& status) { + patternInfo.consumePattern(patternString, status); +} + +DecimalFormatProperties +PatternParser::parseToProperties(const UnicodeString& pattern, IgnoreRounding ignoreRounding, + UErrorCode& status) { + DecimalFormatProperties properties; + parseToExistingPropertiesImpl(pattern, properties, ignoreRounding, status); + return properties; +} + +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); + if (index < 0 || index >= endpoints.end - endpoints.start) { + UPRV_UNREACHABLE_EXIT; + } + return pattern.charAt(endpoints.start + index); +} + +int32_t ParsedPatternInfo::length(int32_t flags) const { + return getLengthFromEndpoints(getEndpoints(flags)); +} + +int32_t ParsedPatternInfo::getLengthFromEndpoints(const Endpoints& endpoints) { + return endpoints.end - endpoints.start; +} + +UnicodeString ParsedPatternInfo::getString(int32_t flags) const { + const Endpoints& endpoints = getEndpoints(flags); + if (endpoints.start == endpoints.end) { + return UnicodeString(); + } + // Create a new UnicodeString + return UnicodeString(pattern, endpoints.start, endpoints.end - endpoints.start); +} + +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; + if (isNegative && padding) { + return negative.paddingEndpoints; + } else if (padding) { + return positive.paddingEndpoints; + } else if (prefix && isNegative) { + return negative.prefixEndpoints; + } else if (prefix) { + return positive.prefixEndpoints; + } else if (isNegative) { + return negative.suffixEndpoints; + } else { + return positive.suffixEndpoints; + } +} + +bool ParsedPatternInfo::positiveHasPlusSign() const { + return positive.hasPlusSign; +} + +bool ParsedPatternInfo::hasNegativeSubpattern() const { + return fHasNegativeSubpattern; +} + +bool ParsedPatternInfo::negativeHasMinusSign() const { + return negative.hasMinusSign; +} + +bool ParsedPatternInfo::hasCurrencySign() const { + return positive.hasCurrencySign || (fHasNegativeSubpattern && negative.hasCurrencySign); +} + +bool ParsedPatternInfo::containsSymbolType(AffixPatternType type, UErrorCode& status) const { + return AffixUtils::containsType(pattern, type, status); +} + +bool ParsedPatternInfo::hasBody() const { + return positive.integerTotal > 0; +} + +bool ParsedPatternInfo::currencyAsDecimal() const { + return positive.hasCurrencyDecimal; +} + +///////////////////////////////////////////////////// +/// BEGIN RECURSIVE DESCENT PARSER IMPLEMENTATION /// +///////////////////////////////////////////////////// + +UChar32 ParsedPatternInfo::ParserState::peek() { + if (offset == pattern.length()) { + return -1; + } else { + return pattern.char32At(offset); + } +} + +UChar32 ParsedPatternInfo::ParserState::peek2() { + if (offset == pattern.length()) { + return -1; + } + int32_t cp1 = pattern.char32At(offset); + int32_t offset2 = offset + U16_LENGTH(cp1); + if (offset2 == pattern.length()) { + return -1; + } + return pattern.char32At(offset2); +} + +UChar32 ParsedPatternInfo::ParserState::next() { + int32_t codePoint = peek(); + offset += U16_LENGTH(codePoint); + return codePoint; +} + +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); + if (U_FAILURE(status)) { return; } + if (state.peek() == u';') { + state.next(); // consume the ';' + // Don't consume the negative subpattern if it is empty (trailing ';') + if (state.peek() != -1) { + fHasNegativeSubpattern = true; + currentSubpattern = &negative; + consumeSubpattern(status); + if (U_FAILURE(status)) { return; } + } + } + if (state.peek() != -1) { + state.toParseException(u"Found unquoted special character"); + status = U_UNQUOTED_SPECIAL; + } +} + +void ParsedPatternInfo::consumeSubpattern(UErrorCode& status) { + // subpattern := literals? number exponent? literals? + consumePadding(PadPosition::UNUM_PAD_BEFORE_PREFIX, status); + if (U_FAILURE(status)) { return; } + consumeAffix(currentSubpattern->prefixEndpoints, status); + if (U_FAILURE(status)) { return; } + consumePadding(PadPosition::UNUM_PAD_AFTER_PREFIX, status); + if (U_FAILURE(status)) { return; } + consumeFormat(status); + if (U_FAILURE(status)) { return; } + consumeExponent(status); + if (U_FAILURE(status)) { return; } + consumePadding(PadPosition::UNUM_PAD_BEFORE_SUFFIX, status); + if (U_FAILURE(status)) { return; } + consumeAffix(currentSubpattern->suffixEndpoints, status); + if (U_FAILURE(status)) { return; } + consumePadding(PadPosition::UNUM_PAD_AFTER_SUFFIX, status); + if (U_FAILURE(status)) { return; } +} + +void ParsedPatternInfo::consumePadding(PadPosition paddingLocation, UErrorCode& status) { + if (state.peek() != u'*') { + return; + } + 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) { + // literals := { literal } + endpoints.start = state.offset; + while (true) { + switch (state.peek()) { + case u'#': + case u'@': + case u';': + case u'*': + case u'.': + case u',': + case u'0': + case u'1': + case u'2': + case u'3': + case u'4': + case u'5': + case u'6': + case u'7': + case u'8': + case u'9': + case -1: + // Characters that cannot appear unquoted in a literal + // break outer; + goto after_outer; + + case u'%': + currentSubpattern->hasPercentSign = true; + break; + + case u'‰': + currentSubpattern->hasPerMilleSign = true; + break; + + case u'¤': + currentSubpattern->hasCurrencySign = true; + break; + + case u'-': + currentSubpattern->hasMinusSign = true; + break; + + case u'+': + currentSubpattern->hasPlusSign = true; + break; + + default: + break; + } + consumeLiteral(status); + if (U_FAILURE(status)) { return; } + } + after_outer: + endpoints.end = state.offset; +} + +void ParsedPatternInfo::consumeLiteral(UErrorCode& status) { + if (state.peek() == -1) { + state.toParseException(u"Expected unquoted literal but found EOL"); + status = U_PATTERN_SYNTAX_ERROR; + return; + } else if (state.peek() == u'\'') { + state.next(); // consume the starting quote + while (state.peek() != u'\'') { + if (state.peek() == -1) { + state.toParseException(u"Expected quoted literal but found EOL"); + status = U_PATTERN_SYNTAX_ERROR; + return; + } else { + state.next(); // consume a quoted character + } + } + state.next(); // consume the ending quote + } else { + // consume a non-quoted literal character + state.next(); + } +} + +void ParsedPatternInfo::consumeFormat(UErrorCode& status) { + consumeIntegerFormat(status); + if (U_FAILURE(status)) { return; } + if (state.peek() == u'.') { + state.next(); // consume the decimal point + currentSubpattern->hasDecimal = true; + currentSubpattern->widthExceptAffixes += 1; + consumeFractionFormat(status); + if (U_FAILURE(status)) { return; } + } else if (state.peek() == u'¤') { + // Check if currency is a decimal separator + switch (state.peek2()) { + case u'#': + case u'0': + case u'1': + case u'2': + case u'3': + case u'4': + case u'5': + case u'6': + case u'7': + case u'8': + case u'9': + break; + default: + // Currency symbol followed by a non-numeric character; + // treat as a normal affix. + return; + } + // Currency symbol is followed by a numeric character; + // treat as a decimal separator. + currentSubpattern->hasCurrencySign = true; + currentSubpattern->hasCurrencyDecimal = true; + currentSubpattern->hasDecimal = true; + currentSubpattern->widthExceptAffixes += 1; + state.next(); // consume the symbol + consumeFractionFormat(status); + if (U_FAILURE(status)) { return; } + } +} + +void ParsedPatternInfo::consumeIntegerFormat(UErrorCode& status) { + // Convenience reference: + ParsedSubpatternInfo& result = *currentSubpattern; + + while (true) { + switch (state.peek()) { + case u',': + result.widthExceptAffixes += 1; + result.groupingSizes <<= 16; + break; + + case u'#': + if (result.integerNumerals > 0) { + state.toParseException(u"# cannot follow 0 before decimal point"); + status = U_UNEXPECTED_TOKEN; + return; + } + result.widthExceptAffixes += 1; + result.groupingSizes += 1; + if (result.integerAtSigns > 0) { + result.integerTrailingHashSigns += 1; + } else { + result.integerLeadingHashSigns += 1; + } + result.integerTotal += 1; + break; + + case u'@': + if (result.integerNumerals > 0) { + state.toParseException(u"Cannot mix 0 and @"); + status = U_UNEXPECTED_TOKEN; + return; + } + if (result.integerTrailingHashSigns > 0) { + state.toParseException(u"Cannot nest # inside of a run of @"); + status = U_UNEXPECTED_TOKEN; + return; + } + result.widthExceptAffixes += 1; + result.groupingSizes += 1; + result.integerAtSigns += 1; + result.integerTotal += 1; + break; + + case u'0': + case u'1': + case u'2': + case u'3': + case u'4': + case u'5': + case u'6': + case u'7': + case u'8': + case u'9': + if (result.integerAtSigns > 0) { + state.toParseException(u"Cannot mix @ and 0"); + status = U_UNEXPECTED_TOKEN; + return; + } + result.widthExceptAffixes += 1; + result.groupingSizes += 1; + result.integerNumerals += 1; + result.integerTotal += 1; + if (!result.rounding.isZeroish() || state.peek() != u'0') { + result.rounding.appendDigit(static_cast(state.peek() - u'0'), 0, true); + } + break; + + default: + goto after_outer; + } + state.next(); // consume the symbol + } + + after_outer: + // Disallow patterns with a trailing ',' or with two ',' next to each other + auto grouping1 = static_cast (result.groupingSizes & 0xffff); + auto grouping2 = static_cast ((result.groupingSizes >> 16) & 0xffff); + auto grouping3 = static_cast ((result.groupingSizes >> 32) & 0xffff); + if (grouping1 == 0 && grouping2 != -1) { + state.toParseException(u"Trailing grouping separator is invalid"); + status = U_UNEXPECTED_TOKEN; + return; + } + if (grouping2 == 0 && grouping3 != -1) { + state.toParseException(u"Grouping width of zero is invalid"); + status = U_PATTERN_SYNTAX_ERROR; + return; + } +} + +void ParsedPatternInfo::consumeFractionFormat(UErrorCode& status) { + // Convenience reference: + ParsedSubpatternInfo& result = *currentSubpattern; + + int32_t zeroCounter = 0; + while (true) { + switch (state.peek()) { + case u'#': + result.widthExceptAffixes += 1; + result.fractionHashSigns += 1; + result.fractionTotal += 1; + zeroCounter++; + break; + + case u'0': + case u'1': + case u'2': + case u'3': + case u'4': + case u'5': + case u'6': + case u'7': + case u'8': + case u'9': + if (result.fractionHashSigns > 0) { + state.toParseException(u"0 cannot follow # after decimal point"); + status = U_UNEXPECTED_TOKEN; + return; + } + result.widthExceptAffixes += 1; + result.fractionNumerals += 1; + result.fractionTotal += 1; + if (state.peek() == u'0') { + zeroCounter++; + } else { + result.rounding + .appendDigit(static_cast(state.peek() - u'0'), zeroCounter, false); + zeroCounter = 0; + } + break; + + default: + return; + } + state.next(); // consume the symbol + } +} + +void ParsedPatternInfo::consumeExponent(UErrorCode& status) { + // Convenience reference: + ParsedSubpatternInfo& result = *currentSubpattern; + + if (state.peek() != u'E') { + return; + } + if ((result.groupingSizes & 0xffff0000L) != 0xffff0000L) { + state.toParseException(u"Cannot have grouping separator in scientific notation"); + status = U_MALFORMED_EXPONENTIAL_PATTERN; + return; + } + state.next(); // consume the E + result.widthExceptAffixes++; + if (state.peek() == u'+') { + state.next(); // consume the + + result.exponentHasPlusSign = true; + result.widthExceptAffixes++; + } + while (state.peek() == u'0') { + state.next(); // consume the 0 + result.exponentZeros += 1; + result.widthExceptAffixes++; + } +} + +/////////////////////////////////////////////////// +/// END RECURSIVE DESCENT PARSER IMPLEMENTATION /// +/////////////////////////////////////////////////// + +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? + properties.clear(); + return; + } + + ParsedPatternInfo patternInfo; + parseToPatternInfo(pattern, patternInfo, status); + if (U_FAILURE(status)) { return; } + patternInfoToProperties(properties, patternInfo, ignoreRounding, 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; + + bool ignoreRounding; + if (_ignoreRounding == IGNORE_ROUNDING_NEVER) { + ignoreRounding = false; + } else if (_ignoreRounding == IGNORE_ROUNDING_IF_CURRENCY) { + ignoreRounding = positive.hasCurrencySign; + } else { + U_ASSERT(_ignoreRounding == IGNORE_ROUNDING_ALWAYS); + ignoreRounding = true; + } + + // Grouping settings + auto grouping1 = static_cast (positive.groupingSizes & 0xffff); + auto grouping2 = static_cast ((positive.groupingSizes >> 16) & 0xffff); + 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; + } else { + properties.secondaryGroupingSize = -1; + } + + // For backwards compatibility, require that the pattern emit at least one min digit. + int minInt, minFrac; + if (positive.integerTotal == 0 && positive.fractionTotal > 0) { + // patterns like ".##" + minInt = 0; + minFrac = uprv_max(1, positive.fractionNumerals); + } else if (positive.integerNumerals == 0 && positive.fractionNumerals == 0) { + // patterns like "#.##" + minInt = 1; + minFrac = 0; + } else { + minInt = positive.integerNumerals; + minFrac = positive.fractionNumerals; + } + + // Rounding settings + // Don't set basic rounding when there is a currency sign; defer to CurrencyUsage + if (positive.integerAtSigns > 0) { + properties.minimumFractionDigits = -1; + properties.maximumFractionDigits = -1; + properties.roundingIncrement = 0.0; + properties.minimumSignificantDigits = positive.integerAtSigns; + properties.maximumSignificantDigits = positive.integerAtSigns + positive.integerTrailingHashSigns; + } else if (!positive.rounding.isZeroish()) { + if (!ignoreRounding) { + properties.minimumFractionDigits = minFrac; + properties.maximumFractionDigits = positive.fractionTotal; + properties.roundingIncrement = positive.rounding.toDouble(); + } else { + properties.minimumFractionDigits = -1; + properties.maximumFractionDigits = -1; + properties.roundingIncrement = 0.0; + } + properties.minimumSignificantDigits = -1; + properties.maximumSignificantDigits = -1; + } else { + if (!ignoreRounding) { + properties.minimumFractionDigits = minFrac; + properties.maximumFractionDigits = positive.fractionTotal; + properties.roundingIncrement = 0.0; + } else { + properties.minimumFractionDigits = -1; + properties.maximumFractionDigits = -1; + properties.roundingIncrement = 0.0; + } + properties.minimumSignificantDigits = -1; + properties.maximumSignificantDigits = -1; + } + + // If the pattern ends with a '.' then force the decimal point. + if (positive.hasDecimal && positive.fractionTotal == 0) { + properties.decimalSeparatorAlwaysShown = true; + } else { + properties.decimalSeparatorAlwaysShown = false; + } + + // Persist the currency as decimal separator + properties.currencyAsDecimal = positive.hasCurrencyDecimal; + + // Scientific notation settings + if (positive.exponentZeros > 0) { + properties.exponentSignAlwaysShown = positive.exponentHasPlusSign; + properties.minimumExponentDigits = positive.exponentZeros; + if (positive.integerAtSigns == 0) { + // patterns without '@' can define max integer digits, used for engineering notation + properties.minimumIntegerDigits = positive.integerNumerals; + properties.maximumIntegerDigits = positive.integerTotal; + } else { + // patterns with '@' cannot define max integer digits + properties.minimumIntegerDigits = 1; + properties.maximumIntegerDigits = -1; + } + } else { + properties.exponentSignAlwaysShown = false; + properties.minimumExponentDigits = -1; + properties.minimumIntegerDigits = minInt; + properties.maximumIntegerDigits = -1; + } + + // Compute the affix patterns (required for both padding and affixes) + UnicodeString posPrefix = patternInfo.getString(AffixPatternProvider::AFFIX_PREFIX); + UnicodeString posSuffix = patternInfo.getString(0); + + // Padding settings + if (positive.hasPadding) { + // The width of the positive prefix and suffix templates are included in the padding + 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) { + properties.padString = rawPaddingString; + } else if (rawPaddingString.length() == 2) { + if (rawPaddingString.charAt(0) == u'\'') { + properties.padString.setTo(u"'", -1); + } else { + properties.padString = rawPaddingString; + } + } else { + properties.padString = UnicodeString(rawPaddingString, 1, rawPaddingString.length() - 2); + } + properties.padPosition = positive.paddingLocation; + } else { + properties.formatWidth = -1; + properties.padString.setToBogus(); + properties.padPosition.nullify(); + } + + // Set the affixes + // Always call the setter, even if the prefixes are empty, especially in the case of the + // negative prefix pattern, to prevent default values from overriding the pattern. + properties.positivePrefixPattern = posPrefix; + properties.positiveSuffixPattern = posSuffix; + if (patternInfo.fHasNegativeSubpattern) { + properties.negativePrefixPattern = patternInfo.getString( + AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN | AffixPatternProvider::AFFIX_PREFIX); + properties.negativeSuffixPattern = patternInfo.getString( + AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN); + } else { + properties.negativePrefixPattern.setToBogus(); + properties.negativeSuffixPattern.setToBogus(); + } + + // Set the magnitude multiplier + if (positive.hasPercentSign) { + properties.magnitudeMultiplier = 2; + } else if (positive.hasPerMilleSign) { + properties.magnitudeMultiplier = 3; + } else { + properties.magnitudeMultiplier = 0; + } +} + +/////////////////////////////////////////////////////////////////// +/// End PatternStringParser.java; begin PatternStringUtils.java /// +/////////////////////////////////////////////////////////////////// + +// Determine whether a given roundingIncrement should be ignored for formatting +// based on the current maxFrac value (maximum fraction digits). For example a +// roundingIncrement of 0.01 should be ignored if maxFrac is 1, but not if maxFrac +// is 2 or more. Note that roundingIncrements are rounded in significance, so +// a roundingIncrement of 0.006 is treated like 0.01 for this determination, i.e. +// it should not be ignored if maxFrac is 2 or more (but a roundingIncrement of +// 0.005 is treated like 0.001 for significance). This is the reason for the +// initial doubling below. +// roundIncr must be non-zero. +bool PatternStringUtils::ignoreRoundingIncrement(double roundIncr, int32_t maxFrac) { + if (maxFrac < 0) { + return false; + } + int32_t frac = 0; + roundIncr *= 2.0; + for (frac = 0; frac <= maxFrac && roundIncr <= 1.0; frac++, roundIncr *= 10.0); + return (frac > maxFrac); +} + +UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatProperties& properties, + UErrorCode& status) { + UnicodeString sb; + + // Convenience references + // The uprv_min() calls prevent DoS + int32_t dosMax = 100; + int32_t grouping1 = uprv_max(0, uprv_min(properties.groupingSize, dosMax)); + int32_t grouping2 = uprv_max(0, uprv_min(properties.secondaryGroupingSize, dosMax)); + bool useGrouping = properties.groupingUsed; + int32_t paddingWidth = uprv_min(properties.formatWidth, dosMax); + NullableValue paddingLocation = properties.padPosition; + UnicodeString paddingString = properties.padString; + int32_t minInt = uprv_max(0, uprv_min(properties.minimumIntegerDigits, dosMax)); + int32_t maxInt = uprv_min(properties.maximumIntegerDigits, dosMax); + int32_t minFrac = uprv_max(0, uprv_min(properties.minimumFractionDigits, dosMax)); + int32_t maxFrac = uprv_min(properties.maximumFractionDigits, dosMax); + int32_t minSig = uprv_min(properties.minimumSignificantDigits, dosMax); + int32_t maxSig = uprv_min(properties.maximumSignificantDigits, dosMax); + bool alwaysShowDecimal = properties.decimalSeparatorAlwaysShown; + int32_t exponentDigits = uprv_min(properties.minimumExponentDigits, dosMax); + bool exponentShowPlusSign = properties.exponentSignAlwaysShown; + + AutoAffixPatternProvider affixProvider(properties, status); + + // Prefixes + sb.append(affixProvider.get().getString(AffixPatternProvider::AFFIX_POS_PREFIX)); + int32_t afterPrefixPos = sb.length(); + + // Figure out the grouping sizes. + if (!useGrouping) { + grouping1 = 0; + grouping2 = 0; + } else if (grouping1 == grouping2) { + grouping1 = 0; + } + int32_t groupingLength = grouping1 + grouping2 + 1; + + // Figure out the digits we need to put in the pattern. + double increment = properties.roundingIncrement; + UnicodeString digitsString; + int32_t digitsStringScale = 0; + if (maxSig != uprv_min(dosMax, -1)) { + // Significant Digits. + while (digitsString.length() < minSig) { + digitsString.append(u'@'); + } + while (digitsString.length() < maxSig) { + digitsString.append(u'#'); + } + } else if (increment != 0.0 && !ignoreRoundingIncrement(increment,maxFrac)) { + // Rounding Increment. + DecimalQuantity incrementQuantity; + incrementQuantity.setToDouble(increment); + incrementQuantity.roundToInfinity(); + digitsStringScale = incrementQuantity.getLowerDisplayMagnitude(); + incrementQuantity.adjustMagnitude(-digitsStringScale); + incrementQuantity.setMinInteger(minInt - digitsStringScale); + UnicodeString str = incrementQuantity.toPlainString(); + if (str.charAt(0) == u'-') { + // TODO: Unsupported operation exception or fail silently? + digitsString.append(str, 1, str.length() - 1); + } else { + digitsString.append(str); + } + } + while (digitsString.length() + digitsStringScale < minInt) { + digitsString.insert(0, u'0'); + } + while (-digitsStringScale < minFrac) { + digitsString.append(u'0'); + digitsStringScale--; + } + + // Write the digits to the string builder + int32_t m0 = uprv_max(groupingLength, digitsString.length() + digitsStringScale); + m0 = (maxInt != dosMax) ? uprv_max(maxInt, m0) - 1 : m0 - 1; + int32_t mN = (maxFrac != dosMax) ? uprv_min(-maxFrac, digitsStringScale) : digitsStringScale; + for (int32_t magnitude = m0; magnitude >= mN; magnitude--) { + int32_t di = digitsString.length() + digitsStringScale - magnitude - 1; + if (di < 0 || di >= digitsString.length()) { + sb.append(u'#'); + } else { + sb.append(digitsString.charAt(di)); + } + // Decimal separator + if (magnitude == 0 && (alwaysShowDecimal || mN < 0)) { + if (properties.currencyAsDecimal) { + sb.append(u'¤'); + } else { + sb.append(u'.'); + } + } + if (!useGrouping) { + continue; + } + // Least-significant grouping separator + if (magnitude > 0 && magnitude == grouping1) { + sb.append(u','); + } + // All other grouping separators + if (magnitude > grouping1 && grouping2 > 0 && (magnitude - grouping1) % grouping2 == 0) { + sb.append(u','); + } + } + + // Exponential notation + if (exponentDigits != uprv_min(dosMax, -1)) { + sb.append(u'E'); + if (exponentShowPlusSign) { + sb.append(u'+'); + } + for (int32_t i = 0; i < exponentDigits; i++) { + sb.append(u'0'); + } + } + + // Suffixes + int32_t beforeSuffixPos = sb.length(); + sb.append(affixProvider.get().getString(AffixPatternProvider::AFFIX_POS_SUFFIX)); + + // Resolve Padding + if (paddingWidth > 0 && !paddingLocation.isNull()) { + while (paddingWidth - sb.length() > 0) { + sb.insert(afterPrefixPos, u'#'); + beforeSuffixPos++; + } + int32_t addedLength; + switch (paddingLocation.get(status)) { + case PadPosition::UNUM_PAD_BEFORE_PREFIX: + addedLength = escapePaddingString(paddingString, sb, 0, status); + sb.insert(0, u'*'); + afterPrefixPos += addedLength + 1; + beforeSuffixPos += addedLength + 1; + break; + case PadPosition::UNUM_PAD_AFTER_PREFIX: + addedLength = escapePaddingString(paddingString, sb, afterPrefixPos, status); + sb.insert(afterPrefixPos, u'*'); + afterPrefixPos += addedLength + 1; + beforeSuffixPos += addedLength + 1; + break; + case PadPosition::UNUM_PAD_BEFORE_SUFFIX: + escapePaddingString(paddingString, sb, beforeSuffixPos, status); + sb.insert(beforeSuffixPos, u'*'); + break; + case PadPosition::UNUM_PAD_AFTER_SUFFIX: + sb.append(u'*'); + escapePaddingString(paddingString, sb, sb.length(), status); + break; + } + if (U_FAILURE(status)) { return sb; } + } + + // Negative affixes + // Ignore if the negative prefix pattern is "-" and the negative suffix is empty + if (affixProvider.get().hasNegativeSubpattern()) { + sb.append(u';'); + sb.append(affixProvider.get().getString(AffixPatternProvider::AFFIX_NEG_PREFIX)); + // Copy the positive digit format into the negative. + // This is optional; the pattern is the same as if '#' were appended here instead. + // NOTE: It is not safe to append the UnicodeString to itself, so we need to copy. + // See https://unicode-org.atlassian.net/browse/ICU-13707 + UnicodeString copy(sb); + sb.append(copy, afterPrefixPos, beforeSuffixPos - afterPrefixPos); + sb.append(affixProvider.get().getString(AffixPatternProvider::AFFIX_NEG_SUFFIX)); + } + + return sb; +} + +int PatternStringUtils::escapePaddingString(UnicodeString input, UnicodeString& output, int startIndex, + UErrorCode& status) { + (void) status; + if (input.length() == 0) { + input.setTo(kFallbackPaddingString, -1); + } + int startLength = output.length(); + if (input.length() == 1) { + if (input.compare(u"'", -1) == 0) { + output.insert(startIndex, u"''", -1); + } else { + output.insert(startIndex, input); + } + } else { + output.insert(startIndex, u'\''); + int offset = 1; + for (int i = 0; i < input.length(); i++) { + // it's okay to deal in chars here because the quote mark is the only interesting thing. + char16_t ch = input.charAt(i); + if (ch == u'\'') { + output.insert(startIndex + offset, u"''", -1); + offset += 2; + } else { + output.insert(startIndex + offset, ch); + offset += 1; + } + } + output.insert(startIndex + offset, u'\''); + } + 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; + // TODO: Add approximately sign here? + 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++) { + char16_t 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, + PatternSignType patternSignType, + bool approximately, + StandardPlural::Form plural, + bool perMilleReplacesPercent, + bool dropCurrencySymbols, + UnicodeString& output) { + + // Should the output render '+' where '-' would normally appear in the pattern? + bool plusReplacesMinusSign = (patternSignType == PATTERN_SIGN_TYPE_POS_SIGN) + && !patternInfo.positiveHasPlusSign(); + + // Should we use the affix from the negative subpattern? + // (If not, we will use the positive subpattern.) + bool useNegativeAffixPattern = patternInfo.hasNegativeSubpattern() + && (patternSignType == PATTERN_SIGN_TYPE_NEG + || (patternInfo.negativeHasMinusSign() && (plusReplacesMinusSign || approximately))); + + // 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 (patternSignType == PATTERN_SIGN_TYPE_NEG) { + prependSign = true; + } else { + prependSign = plusReplacesMinusSign || approximately; + } + + // What symbols should take the place of the sign placeholder? + const char16_t* signSymbols = u"-"; + if (approximately) { + if (plusReplacesMinusSign) { + signSymbols = u"~+"; + } else if (patternSignType == PATTERN_SIGN_TYPE_NEG) { + signSymbols = u"~-"; + } else { + signSymbols = u"~"; + } + } else if (plusReplacesMinusSign) { + signSymbols = u"+"; + } + + // Compute the number of tokens in the affix pattern (signSymbols is considered one token). + 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 (candidate == u'-') { + if (u_strlen(signSymbols) == 1) { + candidate = signSymbols[0]; + } else { + output.append(signSymbols[0]); + candidate = signSymbols[1]; + } + } + if (perMilleReplacesPercent && candidate == u'%') { + candidate = u'‰'; + } + if (dropCurrencySymbols && candidate == u'\u00A4') { + continue; + } + output.append(candidate); + } +} + +PatternSignType PatternStringUtils::resolveSignDisplay(UNumberSignDisplay signDisplay, Signum signum) { + switch (signDisplay) { + case UNUM_SIGN_AUTO: + case UNUM_SIGN_ACCOUNTING: + switch (signum) { + case SIGNUM_NEG: + case SIGNUM_NEG_ZERO: + return PATTERN_SIGN_TYPE_NEG; + case SIGNUM_POS_ZERO: + case SIGNUM_POS: + return PATTERN_SIGN_TYPE_POS; + default: + break; + } + break; + + case UNUM_SIGN_ALWAYS: + case UNUM_SIGN_ACCOUNTING_ALWAYS: + switch (signum) { + case SIGNUM_NEG: + case SIGNUM_NEG_ZERO: + return PATTERN_SIGN_TYPE_NEG; + case SIGNUM_POS_ZERO: + case SIGNUM_POS: + return PATTERN_SIGN_TYPE_POS_SIGN; + default: + break; + } + break; + + case UNUM_SIGN_EXCEPT_ZERO: + case UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO: + switch (signum) { + case SIGNUM_NEG: + return PATTERN_SIGN_TYPE_NEG; + case SIGNUM_NEG_ZERO: + case SIGNUM_POS_ZERO: + return PATTERN_SIGN_TYPE_POS; + case SIGNUM_POS: + return PATTERN_SIGN_TYPE_POS_SIGN; + default: + break; + } + break; + + case UNUM_SIGN_NEGATIVE: + case UNUM_SIGN_ACCOUNTING_NEGATIVE: + switch (signum) { + case SIGNUM_NEG: + return PATTERN_SIGN_TYPE_NEG; + case SIGNUM_NEG_ZERO: + case SIGNUM_POS_ZERO: + case SIGNUM_POS: + return PATTERN_SIGN_TYPE_POS; + default: + break; + } + break; + + case UNUM_SIGN_NEVER: + return PATTERN_SIGN_TYPE_POS; + + default: + break; + } + + UPRV_UNREACHABLE_EXIT; + return PATTERN_SIGN_TYPE_POS; +} + +#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 08696697847e77..b94f421608c9d6 100644 --- a/deps/icu-small/source/i18n/number_patternstring.h +++ b/deps/icu-small/source/i18n/number_patternstring.h @@ -1,340 +1,340 @@ -// © 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_PATTERNSTRING_H__ -#define __NUMBER_PATTERNSTRING_H__ - - -#include -#include "unicode/unum.h" -#include "unicode/unistr.h" -#include "number_types.h" -#include "number_decimalquantity.h" -#include "number_decimfmtprops.h" -#include "number_affixutils.h" - -U_NAMESPACE_BEGIN namespace number { -namespace impl { - -// Forward declaration -class PatternParser; - -// Note: the order of fields in this enum matters for parsing. -enum PatternSignType { - /** Render using normal positive subpattern rules */ - PATTERN_SIGN_TYPE_POS, - /** Render using rules to force the display of a plus sign */ - PATTERN_SIGN_TYPE_POS_SIGN, - /** Render using negative subpattern rules */ - PATTERN_SIGN_TYPE_NEG, - /** Count for looping over the possibilities */ - PATTERN_SIGN_TYPE_COUNT -}; - -// Exported as U_I18N_API because it is a public member field of exported ParsedSubpatternInfo -struct U_I18N_API Endpoints { - int32_t start = 0; - int32_t end = 0; -}; - -// Exported as U_I18N_API because it is a public member field of exported ParsedPatternInfo -struct U_I18N_API ParsedSubpatternInfo { - uint64_t groupingSizes = 0x0000ffffffff0000L; - int32_t integerLeadingHashSigns = 0; - int32_t integerTrailingHashSigns = 0; - int32_t integerNumerals = 0; - int32_t integerAtSigns = 0; - int32_t integerTotal = 0; // for convenience - int32_t fractionNumerals = 0; - int32_t fractionHashSigns = 0; - int32_t fractionTotal = 0; // for convenience - bool hasDecimal = false; - int32_t widthExceptAffixes = 0; - // 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; - bool hasPercentSign = false; - bool hasPerMilleSign = false; - bool hasCurrencySign = false; - bool hasCurrencyDecimal = false; - bool hasMinusSign = false; - bool hasPlusSign = false; - - Endpoints prefixEndpoints; - Endpoints suffixEndpoints; - Endpoints paddingEndpoints; -}; - -// Exported as U_I18N_API because it is needed for the unit test PatternStringTest -struct U_I18N_API ParsedPatternInfo : public AffixPatternProvider, public UMemory { - UnicodeString pattern; - ParsedSubpatternInfo positive; - ParsedSubpatternInfo negative; - - ParsedPatternInfo() - : state(this->pattern), currentSubpattern(nullptr) {} - - ~ParsedPatternInfo() U_OVERRIDE = default; - - // 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 U_OVERRIDE; - - bool positiveHasPlusSign() const U_OVERRIDE; - - bool hasNegativeSubpattern() const U_OVERRIDE; - - bool negativeHasMinusSign() const U_OVERRIDE; - - bool hasCurrencySign() const U_OVERRIDE; - - bool containsSymbolType(AffixPatternType type, UErrorCode& status) const U_OVERRIDE; - - bool hasBody() const U_OVERRIDE; - - bool currencyAsDecimal() const U_OVERRIDE; - - private: - struct U_I18N_API ParserState { - const UnicodeString& pattern; // reference to the parent - int32_t offset = 0; - - 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; - } - - /** Returns the next code point, or -1 if string is too short. */ - UChar32 peek(); - - /** Returns the code point after the next code point, or -1 if string is too short. */ - UChar32 peek2(); - - /** Returns the next code point and then steps forward. */ - UChar32 next(); - - // 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; - - // 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; - - // 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; - - /** Run the recursive descent parser. */ - void consumePattern(const UnicodeString& patternString, UErrorCode& status); - - void consumeSubpattern(UErrorCode& status); - - void consumePadding(PadPosition paddingLocation, UErrorCode& status); - - void consumeAffix(Endpoints& endpoints, UErrorCode& status); - - void consumeLiteral(UErrorCode& status); - - void consumeFormat(UErrorCode& status); - - void consumeIntegerFormat(UErrorCode& status); - - void consumeFractionFormat(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: - /** - * Runs the recursive descent parser on the given pattern string, returning a data structure with raw information - * about the pattern string. - * - *

      - * To obtain a more useful form of the data, consider using {@link #parseToProperties} instead. - * - * TODO: Change argument type to const char16_t* instead of UnicodeString? - * - * @param patternString - * 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); - - /** - * Parses a pattern string into a new property bag. - * - * @param pattern - * The pattern string, like "#,##0.00" - * @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. - * @return A property bag object. - * @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, UErrorCode& status); - - /** - * Parses a pattern string into an existing property bag. All properties that can be encoded into a pattern string - * will be overwritten with either their default value or with the value coming from the pattern string. Properties - * that cannot be encoded into a pattern string, such as rounding mode, are not modified. - * - * @param pattern - * The pattern string, like "#,##0.00" - * @param properties - * The property bag object to overwrite. - * @param ignoreRounding - * See {@link #parseToProperties(String pattern, int ignoreRounding)}. - * @throws IllegalArgumentException - * If there was a syntax error in the pattern string. - */ - 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); - - /** Finalizes the temporary data stored in the ParsedPatternInfo to the Properties. */ - static void patternInfoToProperties(DecimalFormatProperties& properties, - ParsedPatternInfo& patternInfo, IgnoreRounding _ignoreRounding, - UErrorCode& status); -}; - -class U_I18N_API PatternStringUtils { - public: - /** - * Determine whether a given roundingIncrement should be ignored for formatting - * based on the current maxFrac value (maximum fraction digits). For example a - * roundingIncrement of 0.01 should be ignored if maxFrac is 1, but not if maxFrac - * is 2 or more. Note that roundingIncrements are rounded up in significance, so - * a roundingIncrement of 0.006 is treated like 0.01 for this determination, i.e. - * it should not be ignored if maxFrac is 2 or more (but a roundingIncrement of - * 0.005 is treated like 0.001 for significance). - * - * This test is needed for both NumberPropertyMapper::oldToNew and - * PatternStringUtils::propertiesToPatternString. In Java it cannot be - * exported by NumberPropertyMapper (package private) so it is in - * PatternStringUtils, do the same in C. - * - * @param roundIncr - * The roundingIncrement to be checked. Must be non-zero. - * @param maxFrac - * The current maximum fraction digits value. - * @return true if roundIncr should be ignored for formatting. - */ - static bool ignoreRoundingIncrement(double roundIncr, int32_t maxFrac); - - /** - * Creates a pattern string from a property bag. - * - *

      - * Since pattern strings support only a subset of the functionality available in a property bag, a new property bag - * created from the string returned by this function may not be the same as the original property bag. - * - * @param properties - * The property bag to serialize. - * @return A pattern string approximately serializing the property bag. - */ - static UnicodeString propertiesToPatternString(const DecimalFormatProperties& properties, - UErrorCode& status); - - - /** - * Converts a pattern between standard notation and localized notation. Localized notation means that instead of - * using generic placeholders in the pattern, you use the corresponding locale-specific characters instead. For - * example, in locale fr-FR, the period in the pattern "0.000" means "decimal" in standard notation (as it - * does in every other locale), but it means "grouping" in localized notation. - * - *

      - * A greedy string-substitution strategy is used to substitute locale symbols. If two symbols are ambiguous or have - * the same prefix, the result is not well-defined. - * - *

      - * Locale symbols are not allowed to contain the ASCII quote character. - * - *

      - * This method is provided for backwards compatibility and should not be used in any new code. - * - * TODO(C++): This method is not yet implemented. - * - * @param input - * The pattern to convert. - * @param symbols - * The symbols corresponding to the localized pattern. - * @param toLocalized - * true to convert from standard to localized notation; false to convert from localized to standard - * notation. - * @return The pattern expressed in the other notation. - */ - 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, - PatternSignType patternSignType, - bool approximately, - StandardPlural::Form plural, - bool perMilleReplacesPercent, - bool dropCurrencySymbols, - UnicodeString& output); - - static PatternSignType resolveSignDisplay(UNumberSignDisplay signDisplay, Signum signum); - - private: - /** @return The number of chars inserted. */ - static int escapePaddingString(UnicodeString input, UnicodeString& output, int startIndex, - UErrorCode& status); -}; - -} // namespace impl -} // namespace number -U_NAMESPACE_END - - -#endif //__NUMBER_PATTERNSTRING_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_PATTERNSTRING_H__ +#define __NUMBER_PATTERNSTRING_H__ + + +#include +#include "unicode/unum.h" +#include "unicode/unistr.h" +#include "number_types.h" +#include "number_decimalquantity.h" +#include "number_decimfmtprops.h" +#include "number_affixutils.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +// Forward declaration +class PatternParser; + +// Note: the order of fields in this enum matters for parsing. +enum PatternSignType { + /** Render using normal positive subpattern rules */ + PATTERN_SIGN_TYPE_POS, + /** Render using rules to force the display of a plus sign */ + PATTERN_SIGN_TYPE_POS_SIGN, + /** Render using negative subpattern rules */ + PATTERN_SIGN_TYPE_NEG, + /** Count for looping over the possibilities */ + PATTERN_SIGN_TYPE_COUNT +}; + +// Exported as U_I18N_API because it is a public member field of exported ParsedSubpatternInfo +struct U_I18N_API Endpoints { + int32_t start = 0; + int32_t end = 0; +}; + +// Exported as U_I18N_API because it is a public member field of exported ParsedPatternInfo +struct U_I18N_API ParsedSubpatternInfo { + uint64_t groupingSizes = 0x0000ffffffff0000L; + int32_t integerLeadingHashSigns = 0; + int32_t integerTrailingHashSigns = 0; + int32_t integerNumerals = 0; + int32_t integerAtSigns = 0; + int32_t integerTotal = 0; // for convenience + int32_t fractionNumerals = 0; + int32_t fractionHashSigns = 0; + int32_t fractionTotal = 0; // for convenience + bool hasDecimal = false; + int32_t widthExceptAffixes = 0; + // 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; + bool hasPercentSign = false; + bool hasPerMilleSign = false; + bool hasCurrencySign = false; + bool hasCurrencyDecimal = false; + bool hasMinusSign = false; + bool hasPlusSign = false; + + Endpoints prefixEndpoints; + Endpoints suffixEndpoints; + Endpoints paddingEndpoints; +}; + +// Exported as U_I18N_API because it is needed for the unit test PatternStringTest +struct U_I18N_API ParsedPatternInfo : public AffixPatternProvider, public UMemory { + UnicodeString pattern; + ParsedSubpatternInfo positive; + ParsedSubpatternInfo negative; + + ParsedPatternInfo() + : state(this->pattern), currentSubpattern(nullptr) {} + + ~ParsedPatternInfo() override = default; + + // Need to declare this explicitly because of the destructor + ParsedPatternInfo& operator=(ParsedPatternInfo&& src) noexcept = default; + + static int32_t getLengthFromEndpoints(const Endpoints& endpoints); + + char16_t charAt(int32_t flags, int32_t index) const override; + + int32_t length(int32_t flags) const override; + + UnicodeString getString(int32_t flags) const override; + + bool positiveHasPlusSign() const override; + + bool hasNegativeSubpattern() const override; + + bool negativeHasMinusSign() const override; + + bool hasCurrencySign() const override; + + bool containsSymbolType(AffixPatternType type, UErrorCode& status) const override; + + bool hasBody() const override; + + bool currencyAsDecimal() const override; + + private: + struct U_I18N_API ParserState { + const UnicodeString& pattern; // reference to the parent + int32_t offset = 0; + + explicit ParserState(const UnicodeString& _pattern) + : pattern(_pattern) {} + + ParserState& operator=(ParserState&& src) 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; + } + + /** Returns the next code point, or -1 if string is too short. */ + UChar32 peek(); + + /** Returns the code point after the next code point, or -1 if string is too short. */ + UChar32 peek2(); + + /** Returns the next code point and then steps forward. */ + UChar32 next(); + + // 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; + + // 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; + + // 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; + + /** Run the recursive descent parser. */ + void consumePattern(const UnicodeString& patternString, UErrorCode& status); + + void consumeSubpattern(UErrorCode& status); + + void consumePadding(PadPosition paddingLocation, UErrorCode& status); + + void consumeAffix(Endpoints& endpoints, UErrorCode& status); + + void consumeLiteral(UErrorCode& status); + + void consumeFormat(UErrorCode& status); + + void consumeIntegerFormat(UErrorCode& status); + + void consumeFractionFormat(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: + /** + * Runs the recursive descent parser on the given pattern string, returning a data structure with raw information + * about the pattern string. + * + *

      + * To obtain a more useful form of the data, consider using {@link #parseToProperties} instead. + * + * TODO: Change argument type to const char16_t* instead of UnicodeString? + * + * @param patternString + * 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); + + /** + * Parses a pattern string into a new property bag. + * + * @param pattern + * The pattern string, like "#,##0.00" + * @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. + * @return A property bag object. + * @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, UErrorCode& status); + + /** + * Parses a pattern string into an existing property bag. All properties that can be encoded into a pattern string + * will be overwritten with either their default value or with the value coming from the pattern string. Properties + * that cannot be encoded into a pattern string, such as rounding mode, are not modified. + * + * @param pattern + * The pattern string, like "#,##0.00" + * @param properties + * The property bag object to overwrite. + * @param ignoreRounding + * See {@link #parseToProperties(String pattern, int ignoreRounding)}. + * @throws IllegalArgumentException + * If there was a syntax error in the pattern string. + */ + 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); + + /** Finalizes the temporary data stored in the ParsedPatternInfo to the Properties. */ + static void patternInfoToProperties(DecimalFormatProperties& properties, + ParsedPatternInfo& patternInfo, IgnoreRounding _ignoreRounding, + UErrorCode& status); +}; + +class U_I18N_API PatternStringUtils { + public: + /** + * Determine whether a given roundingIncrement should be ignored for formatting + * based on the current maxFrac value (maximum fraction digits). For example a + * roundingIncrement of 0.01 should be ignored if maxFrac is 1, but not if maxFrac + * is 2 or more. Note that roundingIncrements are rounded up in significance, so + * a roundingIncrement of 0.006 is treated like 0.01 for this determination, i.e. + * it should not be ignored if maxFrac is 2 or more (but a roundingIncrement of + * 0.005 is treated like 0.001 for significance). + * + * This test is needed for both NumberPropertyMapper::oldToNew and + * PatternStringUtils::propertiesToPatternString. In Java it cannot be + * exported by NumberPropertyMapper (package private) so it is in + * PatternStringUtils, do the same in C. + * + * @param roundIncr + * The roundingIncrement to be checked. Must be non-zero. + * @param maxFrac + * The current maximum fraction digits value. + * @return true if roundIncr should be ignored for formatting. + */ + static bool ignoreRoundingIncrement(double roundIncr, int32_t maxFrac); + + /** + * Creates a pattern string from a property bag. + * + *

      + * Since pattern strings support only a subset of the functionality available in a property bag, a new property bag + * created from the string returned by this function may not be the same as the original property bag. + * + * @param properties + * The property bag to serialize. + * @return A pattern string approximately serializing the property bag. + */ + static UnicodeString propertiesToPatternString(const DecimalFormatProperties& properties, + UErrorCode& status); + + + /** + * Converts a pattern between standard notation and localized notation. Localized notation means that instead of + * using generic placeholders in the pattern, you use the corresponding locale-specific characters instead. For + * example, in locale fr-FR, the period in the pattern "0.000" means "decimal" in standard notation (as it + * does in every other locale), but it means "grouping" in localized notation. + * + *

      + * A greedy string-substitution strategy is used to substitute locale symbols. If two symbols are ambiguous or have + * the same prefix, the result is not well-defined. + * + *

      + * Locale symbols are not allowed to contain the ASCII quote character. + * + *

      + * This method is provided for backwards compatibility and should not be used in any new code. + * + * TODO(C++): This method is not yet implemented. + * + * @param input + * The pattern to convert. + * @param symbols + * The symbols corresponding to the localized pattern. + * @param toLocalized + * true to convert from standard to localized notation; false to convert from localized to standard + * notation. + * @return The pattern expressed in the other notation. + */ + 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, + PatternSignType patternSignType, + bool approximately, + StandardPlural::Form plural, + bool perMilleReplacesPercent, + bool dropCurrencySymbols, + UnicodeString& output); + + static PatternSignType resolveSignDisplay(UNumberSignDisplay signDisplay, Signum signum); + + private: + /** @return The number of chars inserted. */ + static int escapePaddingString(UnicodeString input, UnicodeString& output, int startIndex, + UErrorCode& status); +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + + +#endif //__NUMBER_PATTERNSTRING_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_rounding.cpp b/deps/icu-small/source/i18n/number_rounding.cpp index a9b3f16c050d94..42882cd1576996 100644 --- a/deps/icu-small/source/i18n/number_rounding.cpp +++ b/deps/icu-small/source/i18n/number_rounding.cpp @@ -1,552 +1,552 @@ -// © 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 - -#include "charstr.h" -#include "uassert.h" -#include "unicode/numberformatter.h" -#include "number_types.h" -#include "number_decimalquantity.h" -#include "double-conversion.h" -#include "number_roundingutils.h" -#include "number_skeletons.h" -#include "number_decnum.h" -#include "putilimp.h" -#include "string_segment.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - - -using double_conversion::DoubleToStringConverter; -using icu::StringSegment; - -void number::impl::parseIncrementOption(const StringSegment &segment, - Precision &outPrecision, - 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) || dq.isNaN() || dq.isInfinite()) { - // throw new SkeletonSyntaxException("Invalid rounding increment", segment, e); - status = U_NUMBER_SKELETON_SYNTAX_ERROR; - return; - } - // Now we break apart the number into a mantissa and exponent (magnitude). - int32_t magnitude = dq.adjustToZeroScale(); - // setToDecNumber drops trailing zeros, so we search for the '.' manually. - for (int32_t i=0; i= 0 && minMaxFractionPlaces <= kMaxIntFracSig) { - return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces); - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -FractionPrecision Precision::minFraction(int32_t minFractionPlaces) { - if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) { - return constructFraction(minFractionPlaces, -1); - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -FractionPrecision Precision::maxFraction(int32_t maxFractionPlaces) { - if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) { - return constructFraction(0, maxFractionPlaces); - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -FractionPrecision Precision::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) { - if (minFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig && - minFractionPlaces <= maxFractionPlaces) { - return constructFraction(minFractionPlaces, maxFractionPlaces); - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -Precision Precision::fixedSignificantDigits(int32_t minMaxSignificantDigits) { - if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= kMaxIntFracSig) { - return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits); - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -Precision Precision::minSignificantDigits(int32_t minSignificantDigits) { - if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) { - return constructSignificant(minSignificantDigits, -1); - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -Precision Precision::maxSignificantDigits(int32_t maxSignificantDigits) { - if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) { - return constructSignificant(1, maxSignificantDigits); - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -Precision Precision::minMaxSignificantDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) { - if (minSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig && - minSignificantDigits <= maxSignificantDigits) { - return constructSignificant(minSignificantDigits, maxSignificantDigits); - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -Precision Precision::trailingZeroDisplay(UNumberTrailingZeroDisplay trailingZeroDisplay) const { - Precision result(*this); // copy constructor - result.fTrailingZeroDisplay = trailingZeroDisplay; - return result; -} - -IncrementPrecision Precision::increment(double roundingIncrement) { - if (roundingIncrement > 0.0) { - DecimalQuantity dq; - dq.setToDouble(roundingIncrement); - dq.roundToInfinity(); - int32_t magnitude = dq.adjustToZeroScale(); - return constructIncrement(dq.toLong(), magnitude); - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -IncrementPrecision Precision::incrementExact(uint64_t mantissa, int16_t magnitude) { - if (mantissa > 0.0) { - return constructIncrement(mantissa, magnitude); - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -CurrencyPrecision Precision::currency(UCurrencyUsage currencyUsage) { - return constructCurrency(currencyUsage); -} - -Precision FractionPrecision::withSignificantDigits( - int32_t minSignificantDigits, - int32_t maxSignificantDigits, - UNumberRoundingPriority priority) const { - if (fType == RND_ERROR) { return *this; } // no-op in error state - if (minSignificantDigits >= 1 && - maxSignificantDigits >= minSignificantDigits && - maxSignificantDigits <= kMaxIntFracSig) { - return constructFractionSignificant( - *this, - minSignificantDigits, - maxSignificantDigits, - priority, - false); - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -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, - 1, - minSignificantDigits, - UNUM_ROUNDING_PRIORITY_RELAXED, - true); - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -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, - UNUM_ROUNDING_PRIORITY_STRICT, - true); - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -// Private method on base class -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(); - double increment = ucurr_getRoundingIncrementForUsage(isoCode, fUnion.currencyUsage, &status); - int32_t minMaxFrac = ucurr_getDefaultFractionDigitsForUsage( - isoCode, fUnion.currencyUsage, &status); - Precision retval = (increment != 0.0) - ? Precision::increment(increment) - : static_cast(Precision::fixedFraction(minMaxFrac)); - retval.fTrailingZeroDisplay = fTrailingZeroDisplay; - return retval; -} - -// Public method on CurrencyPrecision subclass -Precision CurrencyPrecision::withCurrency(const CurrencyUnit ¤cy) const { - UErrorCode localStatus = U_ZERO_ERROR; - Precision result = Precision::withCurrency(currency, localStatus); - if (U_FAILURE(localStatus)) { - return {localStatus}; - } - return result; -} - -Precision IncrementPrecision::withMinFraction(int32_t minFrac) const { - if (fType == RND_ERROR) { return *this; } // no-op in error state - if (minFrac >= 0 && minFrac <= kMaxIntFracSig) { - IncrementPrecision copy = *this; - copy.fUnion.increment.fMinFrac = minFrac; - return copy; - } else { - return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; - } -} - -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; - PrecisionUnion union_; - union_.fracSig = settings; - return {RND_FRACTION, union_}; -} - -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); - PrecisionUnion union_; - union_.fracSig = settings; - return {RND_SIGNIFICANT, union_}; -} - -Precision -Precision::constructFractionSignificant( - const FractionPrecision &base, - int32_t minSig, - int32_t maxSig, - UNumberRoundingPriority priority, - bool retain) { - FractionSignificantSettings settings = base.fUnion.fracSig; - settings.fMinSig = static_cast(minSig); - settings.fMaxSig = static_cast(maxSig); - settings.fPriority = priority; - settings.fRetain = retain; - PrecisionUnion union_; - union_.fracSig = settings; - return {RND_FRACTION_SIGNIFICANT, union_}; -} - -IncrementPrecision Precision::constructIncrement(uint64_t increment, digits_t magnitude) { - IncrementSettings settings; - // Note: For number formatting, fIncrement is used for RND_INCREMENT but not - // RND_INCREMENT_ONE or RND_INCREMENT_FIVE. However, fIncrement is used in all - // three when constructing a skeleton. - settings.fIncrement = increment; - settings.fIncrementMagnitude = magnitude; - settings.fMinFrac = magnitude > 0 ? 0 : -magnitude; - PrecisionUnion union_; - union_.increment = settings; - if (increment == 1) { - // NOTE: In C++, we must return the correct value type with the correct union. - // It would be invalid to return a RND_FRACTION here because the methods on the - // IncrementPrecision type assume that the union is backed by increment data. - return {RND_INCREMENT_ONE, union_}; - } else if (increment == 5) { - return {RND_INCREMENT_FIVE, union_}; - } else { - return {RND_INCREMENT, union_}; - } -} - -CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) { - PrecisionUnion union_; - union_.currencyUsage = usage; - return {RND_CURRENCY, union_}; -} - - -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() { - return {}; -} - -bool RoundingImpl::isSignificantDigits() const { - return fPrecision.fType == Precision::RND_SIGNIFICANT; -} - -int32_t -RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer, - UErrorCode &status) { - // Do not call this method with zero, NaN, or infinity. - U_ASSERT(!input.isZeroish()); - - // Perform the first attempt at rounding. - int magnitude = input.getMagnitude(); - int multiplier = producer.getMultiplier(magnitude); - input.adjustMagnitude(multiplier); - apply(input, status); - - // If the number rounded to zero, exit. - if (input.isZeroish() || U_FAILURE(status)) { - return multiplier; - } - - // If the new magnitude after rounding is the same as it was before rounding, then we are done. - // This case applies to most numbers. - if (input.getMagnitude() == magnitude + multiplier) { - return multiplier; - } - - // If the above case DIDN'T apply, then we have a case like 99.9 -> 100 or 999.9 -> 1000: - // The number rounded up to the next magnitude. Check if the multiplier changes; if it doesn't, - // we do not need to make any more adjustments. - int _multiplier = producer.getMultiplier(magnitude + 1); - if (multiplier == _multiplier) { - return multiplier; - } - - // We have a case like 999.9 -> 1000, where the correct output is "1K", not "1000". - // Fix the magnitude and re-apply the rounding strategy. - input.adjustMagnitude(_multiplier - multiplier); - apply(input, status); - return _multiplier; -} - -/** This is the method that contains the actual rounding logic. */ -void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - if (fPassThrough) { - return; - } - int32_t resolvedMinFraction = 0; - 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 Precision::RND_NONE: - value.roundToInfinity(); - break; - - case Precision::RND_FRACTION: - value.roundToMagnitude( - getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac), - fRoundingMode, - status); - resolvedMinFraction = - uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac)); - break; - - case Precision::RND_SIGNIFICANT: - value.roundToMagnitude( - getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig), - fRoundingMode, - status); - resolvedMinFraction = - uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig)); - // Make sure that digits are displayed on zero. - if (value.isZeroish() && fPrecision.fUnion.fracSig.fMinSig > 0) { - value.setMinInteger(1); - } - break; - - case Precision::RND_FRACTION_SIGNIFICANT: { - // From ECMA-402: - /* - Let sResult be ToRawPrecision(...). - Let fResult be ToRawFixed(...). - If intlObj.[[RoundingType]] is morePrecision, then - If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then - Let result be sResult. - Else, - Let result be fResult. - Else, - Assert: intlObj.[[RoundingType]] is lessPrecision. - If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then - Let result be fResult. - Else, - Let result be sResult. - */ - - int32_t roundingMag1 = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac); - int32_t roundingMag2 = getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig); - int32_t roundingMag; - if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { - roundingMag = uprv_min(roundingMag1, roundingMag2); - } else { - roundingMag = uprv_max(roundingMag1, roundingMag2); - } - if (!value.isZeroish()) { - int32_t upperMag = value.getMagnitude(); - value.roundToMagnitude(roundingMag, fRoundingMode, status); - if (!value.isZeroish() && value.getMagnitude() != upperMag && roundingMag1 == roundingMag2) { - // roundingMag2 needs to be the magnitude after rounding - roundingMag2 += 1; - } - } - - int32_t displayMag1 = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac); - int32_t displayMag2 = getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig); - int32_t displayMag; - if (fPrecision.fUnion.fracSig.fRetain) { - // withMinDigits + withMaxDigits - displayMag = uprv_min(displayMag1, displayMag2); - } else if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { - if (roundingMag2 <= roundingMag1) { - displayMag = displayMag2; - } else { - displayMag = displayMag1; - } - } else { - U_ASSERT(fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_STRICT); - if (roundingMag2 <= roundingMag1) { - displayMag = displayMag1; - } else { - displayMag = displayMag2; - } - } - resolvedMinFraction = uprv_max(0, -displayMag); - - break; - } - - case Precision::RND_INCREMENT: - value.roundToIncrement( - fPrecision.fUnion.increment.fIncrement, - fPrecision.fUnion.increment.fIncrementMagnitude, - fRoundingMode, - status); - resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac; - break; - - case Precision::RND_INCREMENT_ONE: - value.roundToMagnitude( - fPrecision.fUnion.increment.fIncrementMagnitude, - fRoundingMode, - status); - resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac; - break; - - case Precision::RND_INCREMENT_FIVE: - value.roundToNickel( - fPrecision.fUnion.increment.fIncrementMagnitude, - fRoundingMode, - status); - resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac; - break; - - case Precision::RND_CURRENCY: - // Call .withCurrency() before .apply()! - UPRV_UNREACHABLE_EXIT; - - default: - UPRV_UNREACHABLE_EXIT; - } - - if (fPrecision.fTrailingZeroDisplay == UNUM_TRAILING_ZERO_AUTO || - // PLURAL_OPERAND_T returns fraction digits as an integer - value.getPluralOperand(PLURAL_OPERAND_T) != 0) { - value.setMinFraction(resolvedMinFraction); - } -} - -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". - // Question: Is it useful to look at trailingZeroDisplay here? - U_ASSERT(isSignificantDigits()); - U_ASSERT(value.isZeroish()); - value.setMinFraction(fPrecision.fUnion.fracSig.fMinSig - minInt); -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include "charstr.h" +#include "uassert.h" +#include "unicode/numberformatter.h" +#include "number_types.h" +#include "number_decimalquantity.h" +#include "double-conversion.h" +#include "number_roundingutils.h" +#include "number_skeletons.h" +#include "number_decnum.h" +#include "putilimp.h" +#include "string_segment.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +using double_conversion::DoubleToStringConverter; +using icu::StringSegment; + +void number::impl::parseIncrementOption(const StringSegment &segment, + Precision &outPrecision, + UErrorCode &status) { + // Need to do char <-> char16_t 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) || dq.isNaN() || dq.isInfinite()) { + // throw new SkeletonSyntaxException("Invalid rounding increment", segment, e); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + // Now we break apart the number into a mantissa and exponent (magnitude). + int32_t magnitude = dq.adjustToZeroScale(); + // setToDecNumber drops trailing zeros, so we search for the '.' manually. + for (int32_t i=0; i= 0 && minMaxFractionPlaces <= kMaxIntFracSig) { + return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +FractionPrecision Precision::minFraction(int32_t minFractionPlaces) { + if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) { + return constructFraction(minFractionPlaces, -1); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +FractionPrecision Precision::maxFraction(int32_t maxFractionPlaces) { + if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) { + return constructFraction(0, maxFractionPlaces); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +FractionPrecision Precision::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) { + if (minFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig && + minFractionPlaces <= maxFractionPlaces) { + return constructFraction(minFractionPlaces, maxFractionPlaces); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +Precision Precision::fixedSignificantDigits(int32_t minMaxSignificantDigits) { + if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= kMaxIntFracSig) { + return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +Precision Precision::minSignificantDigits(int32_t minSignificantDigits) { + if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) { + return constructSignificant(minSignificantDigits, -1); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +Precision Precision::maxSignificantDigits(int32_t maxSignificantDigits) { + if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) { + return constructSignificant(1, maxSignificantDigits); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +Precision Precision::minMaxSignificantDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) { + if (minSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig && + minSignificantDigits <= maxSignificantDigits) { + return constructSignificant(minSignificantDigits, maxSignificantDigits); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +Precision Precision::trailingZeroDisplay(UNumberTrailingZeroDisplay trailingZeroDisplay) const { + Precision result(*this); // copy constructor + result.fTrailingZeroDisplay = trailingZeroDisplay; + return result; +} + +IncrementPrecision Precision::increment(double roundingIncrement) { + if (roundingIncrement > 0.0) { + DecimalQuantity dq; + dq.setToDouble(roundingIncrement); + dq.roundToInfinity(); + int32_t magnitude = dq.adjustToZeroScale(); + return constructIncrement(dq.toLong(), magnitude); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +IncrementPrecision Precision::incrementExact(uint64_t mantissa, int16_t magnitude) { + if (mantissa > 0.0) { + return constructIncrement(mantissa, magnitude); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +CurrencyPrecision Precision::currency(UCurrencyUsage currencyUsage) { + return constructCurrency(currencyUsage); +} + +Precision FractionPrecision::withSignificantDigits( + int32_t minSignificantDigits, + int32_t maxSignificantDigits, + UNumberRoundingPriority priority) const { + if (fType == RND_ERROR) { return *this; } // no-op in error state + if (minSignificantDigits >= 1 && + maxSignificantDigits >= minSignificantDigits && + maxSignificantDigits <= kMaxIntFracSig) { + return constructFractionSignificant( + *this, + minSignificantDigits, + maxSignificantDigits, + priority, + false); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +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, + 1, + minSignificantDigits, + UNUM_ROUNDING_PRIORITY_RELAXED, + true); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +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, + UNUM_ROUNDING_PRIORITY_STRICT, + true); + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +// Private method on base class +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(); + double increment = ucurr_getRoundingIncrementForUsage(isoCode, fUnion.currencyUsage, &status); + int32_t minMaxFrac = ucurr_getDefaultFractionDigitsForUsage( + isoCode, fUnion.currencyUsage, &status); + Precision retval = (increment != 0.0) + ? Precision::increment(increment) + : static_cast(Precision::fixedFraction(minMaxFrac)); + retval.fTrailingZeroDisplay = fTrailingZeroDisplay; + return retval; +} + +// Public method on CurrencyPrecision subclass +Precision CurrencyPrecision::withCurrency(const CurrencyUnit ¤cy) const { + UErrorCode localStatus = U_ZERO_ERROR; + Precision result = Precision::withCurrency(currency, localStatus); + if (U_FAILURE(localStatus)) { + return {localStatus}; + } + return result; +} + +Precision IncrementPrecision::withMinFraction(int32_t minFrac) const { + if (fType == RND_ERROR) { return *this; } // no-op in error state + if (minFrac >= 0 && minFrac <= kMaxIntFracSig) { + IncrementPrecision copy = *this; + copy.fUnion.increment.fMinFrac = minFrac; + return copy; + } else { + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; + } +} + +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; + PrecisionUnion union_; + union_.fracSig = settings; + return {RND_FRACTION, union_}; +} + +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); + PrecisionUnion union_; + union_.fracSig = settings; + return {RND_SIGNIFICANT, union_}; +} + +Precision +Precision::constructFractionSignificant( + const FractionPrecision &base, + int32_t minSig, + int32_t maxSig, + UNumberRoundingPriority priority, + bool retain) { + FractionSignificantSettings settings = base.fUnion.fracSig; + settings.fMinSig = static_cast(minSig); + settings.fMaxSig = static_cast(maxSig); + settings.fPriority = priority; + settings.fRetain = retain; + PrecisionUnion union_; + union_.fracSig = settings; + return {RND_FRACTION_SIGNIFICANT, union_}; +} + +IncrementPrecision Precision::constructIncrement(uint64_t increment, digits_t magnitude) { + IncrementSettings settings; + // Note: For number formatting, fIncrement is used for RND_INCREMENT but not + // RND_INCREMENT_ONE or RND_INCREMENT_FIVE. However, fIncrement is used in all + // three when constructing a skeleton. + settings.fIncrement = increment; + settings.fIncrementMagnitude = magnitude; + settings.fMinFrac = magnitude > 0 ? 0 : -magnitude; + PrecisionUnion union_; + union_.increment = settings; + if (increment == 1) { + // NOTE: In C++, we must return the correct value type with the correct union. + // It would be invalid to return a RND_FRACTION here because the methods on the + // IncrementPrecision type assume that the union is backed by increment data. + return {RND_INCREMENT_ONE, union_}; + } else if (increment == 5) { + return {RND_INCREMENT_FIVE, union_}; + } else { + return {RND_INCREMENT, union_}; + } +} + +CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) { + PrecisionUnion union_; + union_.currencyUsage = usage; + return {RND_CURRENCY, union_}; +} + + +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() { + return {}; +} + +bool RoundingImpl::isSignificantDigits() const { + return fPrecision.fType == Precision::RND_SIGNIFICANT; +} + +int32_t +RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer, + UErrorCode &status) { + // Do not call this method with zero, NaN, or infinity. + U_ASSERT(!input.isZeroish()); + + // Perform the first attempt at rounding. + int magnitude = input.getMagnitude(); + int multiplier = producer.getMultiplier(magnitude); + input.adjustMagnitude(multiplier); + apply(input, status); + + // If the number rounded to zero, exit. + if (input.isZeroish() || U_FAILURE(status)) { + return multiplier; + } + + // If the new magnitude after rounding is the same as it was before rounding, then we are done. + // This case applies to most numbers. + if (input.getMagnitude() == magnitude + multiplier) { + return multiplier; + } + + // If the above case DIDN'T apply, then we have a case like 99.9 -> 100 or 999.9 -> 1000: + // The number rounded up to the next magnitude. Check if the multiplier changes; if it doesn't, + // we do not need to make any more adjustments. + int _multiplier = producer.getMultiplier(magnitude + 1); + if (multiplier == _multiplier) { + return multiplier; + } + + // We have a case like 999.9 -> 1000, where the correct output is "1K", not "1000". + // Fix the magnitude and re-apply the rounding strategy. + input.adjustMagnitude(_multiplier - multiplier); + apply(input, status); + return _multiplier; +} + +/** This is the method that contains the actual rounding logic. */ +void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + if (fPassThrough) { + return; + } + int32_t resolvedMinFraction = 0; + 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 Precision::RND_NONE: + value.roundToInfinity(); + break; + + case Precision::RND_FRACTION: + value.roundToMagnitude( + getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac), + fRoundingMode, + status); + resolvedMinFraction = + uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac)); + break; + + case Precision::RND_SIGNIFICANT: + value.roundToMagnitude( + getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig), + fRoundingMode, + status); + resolvedMinFraction = + uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig)); + // Make sure that digits are displayed on zero. + if (value.isZeroish() && fPrecision.fUnion.fracSig.fMinSig > 0) { + value.setMinInteger(1); + } + break; + + case Precision::RND_FRACTION_SIGNIFICANT: { + // From ECMA-402: + /* + Let sResult be ToRawPrecision(...). + Let fResult be ToRawFixed(...). + If intlObj.[[RoundingType]] is morePrecision, then + If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then + Let result be sResult. + Else, + Let result be fResult. + Else, + Assert: intlObj.[[RoundingType]] is lessPrecision. + If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then + Let result be fResult. + Else, + Let result be sResult. + */ + + int32_t roundingMag1 = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac); + int32_t roundingMag2 = getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig); + int32_t roundingMag; + if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { + roundingMag = uprv_min(roundingMag1, roundingMag2); + } else { + roundingMag = uprv_max(roundingMag1, roundingMag2); + } + if (!value.isZeroish()) { + int32_t upperMag = value.getMagnitude(); + value.roundToMagnitude(roundingMag, fRoundingMode, status); + if (!value.isZeroish() && value.getMagnitude() != upperMag && roundingMag1 == roundingMag2) { + // roundingMag2 needs to be the magnitude after rounding + roundingMag2 += 1; + } + } + + int32_t displayMag1 = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac); + int32_t displayMag2 = getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig); + int32_t displayMag; + if (fPrecision.fUnion.fracSig.fRetain) { + // withMinDigits + withMaxDigits + displayMag = uprv_min(displayMag1, displayMag2); + } else if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { + if (roundingMag2 <= roundingMag1) { + displayMag = displayMag2; + } else { + displayMag = displayMag1; + } + } else { + U_ASSERT(fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_STRICT); + if (roundingMag2 <= roundingMag1) { + displayMag = displayMag1; + } else { + displayMag = displayMag2; + } + } + resolvedMinFraction = uprv_max(0, -displayMag); + + break; + } + + case Precision::RND_INCREMENT: + value.roundToIncrement( + fPrecision.fUnion.increment.fIncrement, + fPrecision.fUnion.increment.fIncrementMagnitude, + fRoundingMode, + status); + resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac; + break; + + case Precision::RND_INCREMENT_ONE: + value.roundToMagnitude( + fPrecision.fUnion.increment.fIncrementMagnitude, + fRoundingMode, + status); + resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac; + break; + + case Precision::RND_INCREMENT_FIVE: + value.roundToNickel( + fPrecision.fUnion.increment.fIncrementMagnitude, + fRoundingMode, + status); + resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac; + break; + + case Precision::RND_CURRENCY: + // Call .withCurrency() before .apply()! + UPRV_UNREACHABLE_EXIT; + + default: + UPRV_UNREACHABLE_EXIT; + } + + if (fPrecision.fTrailingZeroDisplay == UNUM_TRAILING_ZERO_AUTO || + // PLURAL_OPERAND_T returns fraction digits as an integer + value.getPluralOperand(PLURAL_OPERAND_T) != 0) { + value.setMinFraction(resolvedMinFraction); + } +} + +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". + // Question: Is it useful to look at trailingZeroDisplay here? + U_ASSERT(isSignificantDigits()); + U_ASSERT(value.isZeroish()); + value.setMinFraction(fPrecision.fUnion.fracSig.fMinSig - minInt); +} + +#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 66571272545854..57d61a61f5d621 100644 --- a/deps/icu-small/source/i18n/number_roundingutils.h +++ b/deps/icu-small/source/i18n/number_roundingutils.h @@ -1,247 +1,247 @@ -// © 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_ROUNDINGUTILS_H__ -#define __NUMBER_ROUNDINGUTILS_H__ - -#include "number_types.h" -#include "string_segment.h" - -U_NAMESPACE_BEGIN -namespace number { -namespace impl { -namespace roundingutils { - -enum Section { - SECTION_LOWER_EDGE = -1, - SECTION_UPPER_EDGE = -2, - SECTION_LOWER = 1, - SECTION_MIDPOINT = 2, - SECTION_UPPER = 3 -}; - -/** - * Converts a rounding mode and metadata about the quantity being rounded to a boolean determining - * whether the value should be rounded toward infinity or toward zero. - * - *

      The parameters are of type int because benchmarks on an x86-64 processor against OpenJDK - * showed that ints were demonstrably faster than enums in switch statements. - * - * @param isEven Whether the digit immediately before the rounding magnitude is even. - * @param isNegative Whether the quantity is negative. - * @param section Whether the part of the quantity to the right of the rounding magnitude is - * exactly halfway between two digits, whether it is in the lower part (closer to zero), or - * whether it is in the upper part (closer to infinity). See {@link #SECTION_LOWER}, {@link - * #SECTION_MIDPOINT}, and {@link #SECTION_UPPER}. - * @param roundingMode The integer version of the {@link RoundingMode}, which you can get via - * {@link RoundingMode#ordinal}. - * @param status Error code, set to U_FORMAT_INEXACT_ERROR if the rounding mode is kRoundUnnecessary. - * @return true if the number should be rounded toward zero; false if it should be rounded toward - * infinity. - */ -inline bool -getRoundingDirection(bool isEven, bool isNegative, Section section, RoundingMode roundingMode, - UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - switch (roundingMode) { - case RoundingMode::UNUM_ROUND_UP: - // round away from zero - return false; - - case RoundingMode::UNUM_ROUND_DOWN: - // round toward zero - return true; - - case RoundingMode::UNUM_ROUND_CEILING: - // round toward positive infinity - return isNegative; - - case RoundingMode::UNUM_ROUND_FLOOR: - // round toward negative infinity - return !isNegative; - - case RoundingMode::UNUM_ROUND_HALFUP: - switch (section) { - case SECTION_MIDPOINT: - return false; - case SECTION_LOWER: - return true; - case SECTION_UPPER: - return false; - default: - break; - } - break; - - case RoundingMode::UNUM_ROUND_HALFDOWN: - switch (section) { - case SECTION_MIDPOINT: - return true; - case SECTION_LOWER: - return true; - case SECTION_UPPER: - return false; - default: - break; - } - break; - - case RoundingMode::UNUM_ROUND_HALFEVEN: - switch (section) { - case SECTION_MIDPOINT: - return isEven; - case SECTION_LOWER: - return true; - case SECTION_UPPER: - return false; - default: - break; - } - break; - - case RoundingMode::UNUM_ROUND_HALF_ODD: - switch (section) { - case SECTION_MIDPOINT: - return !isEven; - case SECTION_LOWER: - return true; - case SECTION_UPPER: - return false; - default: - break; - } - break; - - case RoundingMode::UNUM_ROUND_HALF_CEILING: - switch (section) { - case SECTION_MIDPOINT: - return isNegative; - case SECTION_LOWER: - return true; - case SECTION_UPPER: - return false; - default: - break; - } - break; - - case RoundingMode::UNUM_ROUND_HALF_FLOOR: - switch (section) { - case SECTION_MIDPOINT: - return !isNegative; - case SECTION_LOWER: - return true; - case SECTION_UPPER: - return false; - default: - break; - } - break; - - default: - break; - } - - status = U_FORMAT_INEXACT_ERROR; - return false; -} - -/** - * Gets whether the given rounding mode's rounding boundary is at the midpoint. The rounding - * boundary is the point at which a number switches from being rounded down to being rounded up. - * For example, with rounding mode HALF_EVEN, HALF_UP, or HALF_DOWN, the rounding boundary is at - * the midpoint, and this function would return true. However, for UP, DOWN, CEILING, and FLOOR, - * the rounding boundary is at the "edge", and this function would return false. - * - * @param roundingMode The integer version of the {@link RoundingMode}. - * @return true if rounding mode is HALF_EVEN, HALF_UP, or HALF_DOWN; false otherwise. - */ -inline bool roundsAtMidpoint(int roundingMode) { - switch (roundingMode) { - case RoundingMode::UNUM_ROUND_UP: - case RoundingMode::UNUM_ROUND_DOWN: - case RoundingMode::UNUM_ROUND_CEILING: - case RoundingMode::UNUM_ROUND_FLOOR: - return false; - - default: - return true; - } -} - -} // 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; // defaults to pass-through rounder - - 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 = true; // default value - - // Permits access to fPrecision. - friend class units::UnitsRouter; - - // Permits access to fPrecision. - friend class UnitConversionHandler; -}; - -/** - * Parses Precision-related skeleton strings without knowledge of MacroProps - * - see blueprint_helpers::parseIncrementOption(). - * - * Referencing MacroProps means needing to pull in the .o files that have the - * destructors for the SymbolsWrapper, StringProp, and Scale classes. - */ -void parseIncrementOption(const StringSegment &segment, Precision &outPrecision, UErrorCode &status); - -} // namespace impl -} // namespace number -U_NAMESPACE_END - -#endif //__NUMBER_ROUNDINGUTILS_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_ROUNDINGUTILS_H__ +#define __NUMBER_ROUNDINGUTILS_H__ + +#include "number_types.h" +#include "string_segment.h" + +U_NAMESPACE_BEGIN +namespace number { +namespace impl { +namespace roundingutils { + +enum Section { + SECTION_LOWER_EDGE = -1, + SECTION_UPPER_EDGE = -2, + SECTION_LOWER = 1, + SECTION_MIDPOINT = 2, + SECTION_UPPER = 3 +}; + +/** + * Converts a rounding mode and metadata about the quantity being rounded to a boolean determining + * whether the value should be rounded toward infinity or toward zero. + * + *

      The parameters are of type int because benchmarks on an x86-64 processor against OpenJDK + * showed that ints were demonstrably faster than enums in switch statements. + * + * @param isEven Whether the digit immediately before the rounding magnitude is even. + * @param isNegative Whether the quantity is negative. + * @param section Whether the part of the quantity to the right of the rounding magnitude is + * exactly halfway between two digits, whether it is in the lower part (closer to zero), or + * whether it is in the upper part (closer to infinity). See {@link #SECTION_LOWER}, {@link + * #SECTION_MIDPOINT}, and {@link #SECTION_UPPER}. + * @param roundingMode The integer version of the {@link RoundingMode}, which you can get via + * {@link RoundingMode#ordinal}. + * @param status Error code, set to U_FORMAT_INEXACT_ERROR if the rounding mode is kRoundUnnecessary. + * @return true if the number should be rounded toward zero; false if it should be rounded toward + * infinity. + */ +inline bool +getRoundingDirection(bool isEven, bool isNegative, Section section, RoundingMode roundingMode, + UErrorCode &status) { + if (U_FAILURE(status)) { + return false; + } + switch (roundingMode) { + case RoundingMode::UNUM_ROUND_UP: + // round away from zero + return false; + + case RoundingMode::UNUM_ROUND_DOWN: + // round toward zero + return true; + + case RoundingMode::UNUM_ROUND_CEILING: + // round toward positive infinity + return isNegative; + + case RoundingMode::UNUM_ROUND_FLOOR: + // round toward negative infinity + return !isNegative; + + case RoundingMode::UNUM_ROUND_HALFUP: + switch (section) { + case SECTION_MIDPOINT: + return false; + case SECTION_LOWER: + return true; + case SECTION_UPPER: + return false; + default: + break; + } + break; + + case RoundingMode::UNUM_ROUND_HALFDOWN: + switch (section) { + case SECTION_MIDPOINT: + return true; + case SECTION_LOWER: + return true; + case SECTION_UPPER: + return false; + default: + break; + } + break; + + case RoundingMode::UNUM_ROUND_HALFEVEN: + switch (section) { + case SECTION_MIDPOINT: + return isEven; + case SECTION_LOWER: + return true; + case SECTION_UPPER: + return false; + default: + break; + } + break; + + case RoundingMode::UNUM_ROUND_HALF_ODD: + switch (section) { + case SECTION_MIDPOINT: + return !isEven; + case SECTION_LOWER: + return true; + case SECTION_UPPER: + return false; + default: + break; + } + break; + + case RoundingMode::UNUM_ROUND_HALF_CEILING: + switch (section) { + case SECTION_MIDPOINT: + return isNegative; + case SECTION_LOWER: + return true; + case SECTION_UPPER: + return false; + default: + break; + } + break; + + case RoundingMode::UNUM_ROUND_HALF_FLOOR: + switch (section) { + case SECTION_MIDPOINT: + return !isNegative; + case SECTION_LOWER: + return true; + case SECTION_UPPER: + return false; + default: + break; + } + break; + + default: + break; + } + + status = U_FORMAT_INEXACT_ERROR; + return false; +} + +/** + * Gets whether the given rounding mode's rounding boundary is at the midpoint. The rounding + * boundary is the point at which a number switches from being rounded down to being rounded up. + * For example, with rounding mode HALF_EVEN, HALF_UP, or HALF_DOWN, the rounding boundary is at + * the midpoint, and this function would return true. However, for UP, DOWN, CEILING, and FLOOR, + * the rounding boundary is at the "edge", and this function would return false. + * + * @param roundingMode The integer version of the {@link RoundingMode}. + * @return true if rounding mode is HALF_EVEN, HALF_UP, or HALF_DOWN; false otherwise. + */ +inline bool roundsAtMidpoint(int roundingMode) { + switch (roundingMode) { + case RoundingMode::UNUM_ROUND_UP: + case RoundingMode::UNUM_ROUND_DOWN: + case RoundingMode::UNUM_ROUND_CEILING: + case RoundingMode::UNUM_ROUND_FLOOR: + return false; + + default: + return true; + } +} + +} // 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; // defaults to pass-through rounder + + 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 = true; // default value + + // Permits access to fPrecision. + friend class units::UnitsRouter; + + // Permits access to fPrecision. + friend class UnitConversionHandler; +}; + +/** + * Parses Precision-related skeleton strings without knowledge of MacroProps + * - see blueprint_helpers::parseIncrementOption(). + * + * Referencing MacroProps means needing to pull in the .o files that have the + * destructors for the SymbolsWrapper, StringProp, and Scale classes. + */ +void parseIncrementOption(const StringSegment &segment, Precision &outPrecision, UErrorCode &status); + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif //__NUMBER_ROUNDINGUTILS_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_scientific.cpp b/deps/icu-small/source/i18n/number_scientific.cpp index d365d982d4ce85..704d9f8b128134 100644 --- a/deps/icu-small/source/i18n/number_scientific.cpp +++ b/deps/icu-small/source/i18n/number_scientific.cpp @@ -1,177 +1,177 @@ -// © 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 - -#include -#include "number_scientific.h" -#include "number_utils.h" -#include "formatted_string_builder.h" -#include "unicode/unum.h" -#include "number_microprops.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - -// NOTE: The object lifecycle of ScientificModifier and ScientificHandler differ greatly in Java and C++. -// -// During formatting, we need to provide an object with state (the exponent) as the inner modifier. -// -// In Java, where the priority is put on reducing object creations, the unsafe code path re-uses the -// ScientificHandler as a ScientificModifier, and the safe code path pre-computes 25 ScientificModifier -// instances. This scheme reduces the number of object creations by 1 in both safe and unsafe. -// -// In C++, MicroProps provides a pre-allocated ScientificModifier, and ScientificHandler simply populates -// the state (the exponent) into that ScientificModifier. There is no difference between safe and unsafe. - -ScientificModifier::ScientificModifier() : fExponent(0), fHandler(nullptr) {} - -void ScientificModifier::set(int32_t exponent, const ScientificHandler *handler) { - // ScientificModifier should be set only once. - U_ASSERT(fHandler == nullptr); - fExponent = exponent; - fHandler = handler; -} - -int32_t ScientificModifier::apply(FormattedStringBuilder &output, int32_t /*leftIndex*/, int32_t rightIndex, - UErrorCode &status) const { - // FIXME: Localized exponent separator location. - int i = rightIndex; - // Append the exponent separator and sign - i += output.insert( - i, - fHandler->fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kExponentialSymbol), - {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SYMBOL_FIELD}, - status); - if (fExponent < 0 && fHandler->fSettings.fExponentSignDisplay != UNUM_SIGN_NEVER) { - i += output.insert( - i, - fHandler->fSymbols - ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol), - {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SIGN_FIELD}, - status); - } else if (fExponent >= 0 && fHandler->fSettings.fExponentSignDisplay == UNUM_SIGN_ALWAYS) { - i += output.insert( - i, - fHandler->fSymbols - ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol), - {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SIGN_FIELD}, - status); - } - // Append the exponent digits (using a simple inline algorithm) - 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); - i += utils::insertDigitFromSymbols( - output, - i - j, - d, - *fHandler->fSymbols, - {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_FIELD}, - status); - } - return i - rightIndex; -} - -int32_t ScientificModifier::getPrefixLength() const { - // TODO: Localized exponent separator location. - return 0; -} - -int32_t ScientificModifier::getCodePointCount() const { - // NOTE: This method is only called one place, NumberRangeFormatterImpl. - // The call site only cares about != 0 and != 1. - // Return a very large value so that if this method is used elsewhere, we should notice. - return 999; -} - -bool ScientificModifier::isStrong() const { - // Scientific is always strong - return true; -} - -bool ScientificModifier::containsField(Field field) const { - (void)field; - // This method is not used for inner modifiers. - UPRV_UNREACHABLE_EXIT; -} - -void ScientificModifier::getParameters(Parameters& output) const { - // Not part of any plural sets - output.obj = nullptr; -} - -bool ScientificModifier::semanticallyEquivalent(const Modifier& other) const { - auto* _other = dynamic_cast(&other); - if (_other == nullptr) { - return false; - } - // TODO: Check for locale symbols and settings as well? Could be less efficient. - return fExponent == _other->fExponent; -} - -// Note: Visual Studio does not compile this function without full name space. Why? -icu::number::impl::ScientificHandler::ScientificHandler(const Notation *notation, const DecimalFormatSymbols *symbols, - const MicroPropsGenerator *parent) : - fSettings(notation->fUnion.scientific), fSymbols(symbols), fParent(parent) {} - -void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const { - fParent->processQuantity(quantity, micros, status); - if (U_FAILURE(status)) { return; } - - // Do not apply scientific notation to special doubles - if (quantity.isInfinite() || quantity.isNaN()) { - micros.modInner = µs.helpers.emptyStrongModifier; - return; - } - - // Treat zero as if it had magnitude 0 - int32_t exponent; - if (quantity.isZeroish()) { - if (fSettings.fRequireMinInt && micros.rounder.isSignificantDigits()) { - // Show "00.000E0" on pattern "00.000E0" - micros.rounder.apply(quantity, fSettings.fEngineeringInterval, status); - exponent = 0; - } else { - micros.rounder.apply(quantity, status); - exponent = 0; - } - } else { - 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; - - // Change the exponent only after we select appropriate plural form - // for formatting purposes so that we preserve expected formatted - // string behavior. - quantity.adjustExponent(exponent); - - // We already performed rounding. Do not perform it again. - micros.rounder = RoundingImpl::passThrough(); -} - -int32_t ScientificHandler::getMultiplier(int32_t magnitude) const { - int32_t interval = fSettings.fEngineeringInterval; - int32_t digitsShown; - if (fSettings.fRequireMinInt) { - // For patterns like "000.00E0" and ".00E0" - digitsShown = interval; - } else if (interval <= 1) { - // For patterns like "0.00E0" and "@@@E0" - digitsShown = 1; - } else { - // For patterns like "##0.00" - digitsShown = ((magnitude % interval + interval) % interval) + 1; - } - return digitsShown - magnitude - 1; -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + +#include +#include "number_scientific.h" +#include "number_utils.h" +#include "formatted_string_builder.h" +#include "unicode/unum.h" +#include "number_microprops.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +// NOTE: The object lifecycle of ScientificModifier and ScientificHandler differ greatly in Java and C++. +// +// During formatting, we need to provide an object with state (the exponent) as the inner modifier. +// +// In Java, where the priority is put on reducing object creations, the unsafe code path re-uses the +// ScientificHandler as a ScientificModifier, and the safe code path pre-computes 25 ScientificModifier +// instances. This scheme reduces the number of object creations by 1 in both safe and unsafe. +// +// In C++, MicroProps provides a pre-allocated ScientificModifier, and ScientificHandler simply populates +// the state (the exponent) into that ScientificModifier. There is no difference between safe and unsafe. + +ScientificModifier::ScientificModifier() : fExponent(0), fHandler(nullptr) {} + +void ScientificModifier::set(int32_t exponent, const ScientificHandler *handler) { + // ScientificModifier should be set only once. + U_ASSERT(fHandler == nullptr); + fExponent = exponent; + fHandler = handler; +} + +int32_t ScientificModifier::apply(FormattedStringBuilder &output, int32_t /*leftIndex*/, int32_t rightIndex, + UErrorCode &status) const { + // FIXME: Localized exponent separator location. + int i = rightIndex; + // Append the exponent separator and sign + i += output.insert( + i, + fHandler->fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kExponentialSymbol), + {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SYMBOL_FIELD}, + status); + if (fExponent < 0 && fHandler->fSettings.fExponentSignDisplay != UNUM_SIGN_NEVER) { + i += output.insert( + i, + fHandler->fSymbols + ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol), + {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SIGN_FIELD}, + status); + } else if (fExponent >= 0 && fHandler->fSettings.fExponentSignDisplay == UNUM_SIGN_ALWAYS) { + i += output.insert( + i, + fHandler->fSymbols + ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol), + {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SIGN_FIELD}, + status); + } + // Append the exponent digits (using a simple inline algorithm) + 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); + i += utils::insertDigitFromSymbols( + output, + i - j, + d, + *fHandler->fSymbols, + {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_FIELD}, + status); + } + return i - rightIndex; +} + +int32_t ScientificModifier::getPrefixLength() const { + // TODO: Localized exponent separator location. + return 0; +} + +int32_t ScientificModifier::getCodePointCount() const { + // NOTE: This method is only called one place, NumberRangeFormatterImpl. + // The call site only cares about != 0 and != 1. + // Return a very large value so that if this method is used elsewhere, we should notice. + return 999; +} + +bool ScientificModifier::isStrong() const { + // Scientific is always strong + return true; +} + +bool ScientificModifier::containsField(Field field) const { + (void)field; + // This method is not used for inner modifiers. + UPRV_UNREACHABLE_EXIT; +} + +void ScientificModifier::getParameters(Parameters& output) const { + // Not part of any plural sets + output.obj = nullptr; +} + +bool ScientificModifier::semanticallyEquivalent(const Modifier& other) const { + auto* _other = dynamic_cast(&other); + if (_other == nullptr) { + return false; + } + // TODO: Check for locale symbols and settings as well? Could be less efficient. + return fExponent == _other->fExponent; +} + +// Note: Visual Studio does not compile this function without full name space. Why? +icu::number::impl::ScientificHandler::ScientificHandler(const Notation *notation, const DecimalFormatSymbols *symbols, + const MicroPropsGenerator *parent) : + fSettings(notation->fUnion.scientific), fSymbols(symbols), fParent(parent) {} + +void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, + UErrorCode &status) const { + fParent->processQuantity(quantity, micros, status); + if (U_FAILURE(status)) { return; } + + // Do not apply scientific notation to special doubles + if (quantity.isInfinite() || quantity.isNaN()) { + micros.modInner = µs.helpers.emptyStrongModifier; + return; + } + + // Treat zero as if it had magnitude 0 + int32_t exponent; + if (quantity.isZeroish()) { + if (fSettings.fRequireMinInt && micros.rounder.isSignificantDigits()) { + // Show "00.000E0" on pattern "00.000E0" + micros.rounder.apply(quantity, fSettings.fEngineeringInterval, status); + exponent = 0; + } else { + micros.rounder.apply(quantity, status); + exponent = 0; + } + } else { + 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; + + // Change the exponent only after we select appropriate plural form + // for formatting purposes so that we preserve expected formatted + // string behavior. + quantity.adjustExponent(exponent); + + // We already performed rounding. Do not perform it again. + micros.rounder = RoundingImpl::passThrough(); +} + +int32_t ScientificHandler::getMultiplier(int32_t magnitude) const { + int32_t interval = fSettings.fEngineeringInterval; + int32_t digitsShown; + if (fSettings.fRequireMinInt) { + // For patterns like "000.00E0" and ".00E0" + digitsShown = interval; + } else if (interval <= 1) { + // For patterns like "0.00E0" and "@@@E0" + digitsShown = 1; + } else { + // For patterns like "##0.00" + digitsShown = ((magnitude % interval + interval) % interval) + 1; + } + return digitsShown - magnitude - 1; +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_scientific.h b/deps/icu-small/source/i18n/number_scientific.h index a40a6e416d1929..2df673fd7141f3 100644 --- a/deps/icu-small/source/i18n/number_scientific.h +++ b/deps/icu-small/source/i18n/number_scientific.h @@ -1,68 +1,68 @@ -// © 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_SCIENTIFIC_H__ -#define __NUMBER_SCIENTIFIC_H__ - -#include "number_types.h" - -U_NAMESPACE_BEGIN namespace number { -namespace impl { - -// Forward-declare -class ScientificHandler; - -class U_I18N_API ScientificModifier : public UMemory, public Modifier { - public: - ScientificModifier(); - - void set(int32_t exponent, const ScientificHandler *handler); - - int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, - UErrorCode &status) const U_OVERRIDE; - - int32_t getPrefixLength() const U_OVERRIDE; - - int32_t getCodePointCount() const U_OVERRIDE; - - bool isStrong() const U_OVERRIDE; - - bool containsField(Field field) const U_OVERRIDE; - - void getParameters(Parameters& output) const U_OVERRIDE; - - bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; - - private: - int32_t fExponent; - const ScientificHandler *fHandler; -}; - -class ScientificHandler : public UMemory, public MicroPropsGenerator, public MultiplierProducer { - public: - ScientificHandler(const Notation *notation, const DecimalFormatSymbols *symbols, - const MicroPropsGenerator *parent); - - void - processQuantity(DecimalQuantity &quantity, MicroProps µs, UErrorCode &status) const U_OVERRIDE; - - int32_t getMultiplier(int32_t magnitude) const U_OVERRIDE; - - private: - const Notation::ScientificSettings fSettings; - const DecimalFormatSymbols *fSymbols; - const MicroPropsGenerator *fParent; - - friend class ScientificModifier; -}; - -} // namespace impl -} // namespace number -U_NAMESPACE_END - -#endif //__NUMBER_SCIENTIFIC_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_SCIENTIFIC_H__ +#define __NUMBER_SCIENTIFIC_H__ + +#include "number_types.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +// Forward-declare +class ScientificHandler; + +class U_I18N_API ScientificModifier : public UMemory, public Modifier { + public: + ScientificModifier(); + + void set(int32_t exponent, const ScientificHandler *handler); + + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + UErrorCode &status) const override; + + int32_t getPrefixLength() const override; + + int32_t getCodePointCount() const override; + + bool isStrong() const override; + + bool containsField(Field field) const override; + + void getParameters(Parameters& output) const override; + + bool semanticallyEquivalent(const Modifier& other) const override; + + private: + int32_t fExponent; + const ScientificHandler *fHandler; +}; + +class ScientificHandler : public UMemory, public MicroPropsGenerator, public MultiplierProducer { + public: + ScientificHandler(const Notation *notation, const DecimalFormatSymbols *symbols, + const MicroPropsGenerator *parent); + + void + processQuantity(DecimalQuantity &quantity, MicroProps µs, UErrorCode &status) const override; + + int32_t getMultiplier(int32_t magnitude) const override; + + private: + const Notation::ScientificSettings fSettings; + const DecimalFormatSymbols *fSymbols; + const MicroPropsGenerator *fParent; + + friend class ScientificModifier; +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif //__NUMBER_SCIENTIFIC_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_simple.cpp b/deps/icu-small/source/i18n/number_simple.cpp new file mode 100644 index 00000000000000..9423b4eb39aa9c --- /dev/null +++ b/deps/icu-small/source/i18n/number_simple.cpp @@ -0,0 +1,255 @@ +// © 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 + +#include "unicode/numberformatter.h" +#include "unicode/simplenumberformatter.h" +#include "number_formatimpl.h" +#include "number_utils.h" +#include "number_patternmodifier.h" +#include "number_utypes.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +SimpleNumber +SimpleNumber::forInt64(int64_t value, UErrorCode& status) { + if (U_FAILURE(status)) { + return SimpleNumber(); + } + auto results = new UFormattedNumberData(); + if (results == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return SimpleNumber(); + } + results->quantity.setToLong(value); + return SimpleNumber(results, status); +} + +SimpleNumber::SimpleNumber(UFormattedNumberData* data, UErrorCode& status) : fData(data) { + if (U_FAILURE(status)) { + return; + } + if (fData == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (fData->quantity.isNegative()) { + fSign = UNUM_SIMPLE_NUMBER_MINUS_SIGN; + } else { + fSign = UNUM_SIMPLE_NUMBER_NO_SIGN; + } +} + +void SimpleNumber::cleanup() { + delete fData; + fData = nullptr; +} + +void SimpleNumber::multiplyByPowerOfTen(int32_t power, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (fData == nullptr) { + status = U_INVALID_STATE_ERROR; + return; + } + fData->quantity.adjustMagnitude(power); +} + +void SimpleNumber::roundTo(int32_t position, UNumberFormatRoundingMode roundingMode, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (fData == nullptr) { + status = U_INVALID_STATE_ERROR; + return; + } + fData->quantity.roundToMagnitude(position, roundingMode, status); +} + +void SimpleNumber::setMinimumIntegerDigits(uint32_t position, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (fData == nullptr) { + status = U_INVALID_STATE_ERROR; + return; + } + fData->quantity.setMinInteger(position); +} + +void SimpleNumber::setMinimumFractionDigits(uint32_t position, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (fData == nullptr) { + status = U_INVALID_STATE_ERROR; + return; + } + fData->quantity.setMinFraction(position); +} + +void SimpleNumber::truncateStart(uint32_t position, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (fData == nullptr) { + status = U_INVALID_STATE_ERROR; + return; + } + fData->quantity.applyMaxInteger(position); +} + +void SimpleNumber::setSign(USimpleNumberSign sign, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (fData == nullptr) { + status = U_INVALID_STATE_ERROR; + return; + } + fSign = sign; +} + + +void SimpleNumberFormatter::cleanup() { + delete fOwnedSymbols; + delete fMicros; + delete fPatternModifier; + fOwnedSymbols = nullptr; + fMicros = nullptr; + fPatternModifier = nullptr; +} + +SimpleNumberFormatter SimpleNumberFormatter::forLocale(const icu::Locale &locale, UErrorCode &status) { + return SimpleNumberFormatter::forLocaleAndGroupingStrategy(locale, UNUM_GROUPING_AUTO, status); +} + +SimpleNumberFormatter SimpleNumberFormatter::forLocaleAndGroupingStrategy( + const icu::Locale &locale, + UNumberGroupingStrategy groupingStrategy, + UErrorCode &status) { + SimpleNumberFormatter retval; + retval.fOwnedSymbols = new DecimalFormatSymbols(locale, status); + if (U_FAILURE(status)) { + return retval; + } + if (retval.fOwnedSymbols == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return retval; + } + retval.initialize(locale, *retval.fOwnedSymbols, groupingStrategy, status); + return retval; +} + + +SimpleNumberFormatter SimpleNumberFormatter::forLocaleAndSymbolsAndGroupingStrategy( + const icu::Locale &locale, + const DecimalFormatSymbols &symbols, + UNumberGroupingStrategy groupingStrategy, + UErrorCode &status) { + SimpleNumberFormatter retval; + retval.initialize(locale, symbols, groupingStrategy, status); + return retval; +} + + +void SimpleNumberFormatter::initialize( + const icu::Locale &locale, + const DecimalFormatSymbols &symbols, + UNumberGroupingStrategy groupingStrategy, + UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + + fMicros = new SimpleMicroProps(); + if (fMicros == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + fMicros->symbols = &symbols; + + auto pattern = utils::getPatternForStyle( + locale, + symbols.getNumberingSystemName(), + CLDR_PATTERN_STYLE_DECIMAL, + status); + if (U_FAILURE(status)) { + return; + } + + ParsedPatternInfo patternInfo; + PatternParser::parseToPatternInfo(UnicodeString(pattern), patternInfo, status); + if (U_FAILURE(status)) { + return; + } + + auto grouper = Grouper::forStrategy(groupingStrategy); + grouper.setLocaleData(patternInfo, locale); + fMicros->grouping = grouper; + + MutablePatternModifier patternModifier(false); + patternModifier.setPatternInfo(&patternInfo, kUndefinedField); + patternModifier.setPatternAttributes(UNUM_SIGN_EXCEPT_ZERO, false, false); + patternModifier.setSymbols(fMicros->symbols, {}, UNUM_UNIT_WIDTH_SHORT, nullptr, status); + + fPatternModifier = new AdoptingSignumModifierStore(patternModifier.createImmutableForPlural(StandardPlural::COUNT, status)); + + fGroupingStrategy = groupingStrategy; + return; +} + +FormattedNumber SimpleNumberFormatter::format(SimpleNumber value, UErrorCode &status) const { + formatImpl(value.fData, value.fSign, status); + + // Do not save the results object if we encountered a failure. + if (U_SUCCESS(status)) { + auto temp = value.fData; + value.fData = nullptr; + return FormattedNumber(temp); + } else { + return FormattedNumber(status); + } +} + +void SimpleNumberFormatter::formatImpl(UFormattedNumberData* data, USimpleNumberSign sign, UErrorCode &status) const { + if (U_FAILURE(status)) { + return; + } + if (data == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (fPatternModifier == nullptr || fMicros == nullptr) { + status = U_INVALID_STATE_ERROR; + return; + } + + Signum signum; + if (sign == UNUM_SIMPLE_NUMBER_MINUS_SIGN) { + signum = SIGNUM_NEG; + } else if (sign == UNUM_SIMPLE_NUMBER_PLUS_SIGN) { + signum = SIGNUM_POS; + } else { + signum = SIGNUM_POS_ZERO; + } + + const Modifier* modifier = (*fPatternModifier)[signum]; + auto length = NumberFormatterImpl::writeNumber( + *fMicros, + data->quantity, + data->getStringRef(), + 0, + status); + length += modifier->apply(data->getStringRef(), 0, length, status); + data->getStringRef().writeTerminator(status); +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_skeletons.cpp b/deps/icu-small/source/i18n/number_skeletons.cpp index 3db50369cb667b..e2a11002813acd 100644 --- a/deps/icu-small/source/i18n/number_skeletons.cpp +++ b/deps/icu-small/source/i18n/number_skeletons.cpp @@ -1,1813 +1,1813 @@ -// © 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_roundingutils.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" -#include "string_segment.h" -#include "unicode/errorcode.h" -#include "util.h" -#include "measunit_impl.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; -using namespace icu::number::impl::skeleton; - -namespace { - -icu::UInitOnce gNumberSkeletonsInitOnce {}; - -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-odd", STEM_ROUNDING_MODE_HALF_ODD, status); - b.add(u"rounding-mode-half-ceiling", STEM_ROUNDING_MODE_HALF_CEILING, status); - b.add(u"rounding-mode-half-floor", STEM_ROUNDING_MODE_HALF_FLOOR, 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"integer-width-trunc", STEM_INTEGER_WIDTH_TRUNC, 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-formal", STEM_UNIT_WIDTH_FORMAL, status); - b.add(u"unit-width-variant", STEM_UNIT_WIDTH_VARIANT, 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"sign-negative", STEM_SIGN_NEGATIVE, status); - b.add(u"sign-accounting-negative", STEM_SIGN_ACCOUNTING_NEGATIVE, 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"unit", STEM_UNIT, status); - b.add(u"usage", STEM_UNIT_USAGE, 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; } - - // Section 3 (concise tokens): - b.add(u"K", STEM_COMPACT_SHORT, status); - b.add(u"KK", STEM_COMPACT_LONG, status); - b.add(u"%", STEM_PERCENT, status); - b.add(u"%x100", STEM_PERCENT_100, status); - b.add(u",_", STEM_GROUP_OFF, status); - b.add(u",?", STEM_GROUP_MIN2, status); - b.add(u",!", STEM_GROUP_ON_ALIGNED, status); - b.add(u"+!", STEM_SIGN_ALWAYS, status); - b.add(u"+_", STEM_SIGN_NEVER, status); - b.add(u"()", STEM_SIGN_ACCOUNTING, status); - b.add(u"()!", STEM_SIGN_ACCOUNTING_ALWAYS, status); - b.add(u"+?", STEM_SIGN_EXCEPT_ZERO, status); - b.add(u"()?", STEM_SIGN_ACCOUNTING_EXCEPT_ZERO, status); - b.add(u"+-", STEM_SIGN_NEGATIVE, status); - b.add(u"()-", STEM_SIGN_ACCOUNTING_NEGATIVE, 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 */ \ -UPRV_BLOCK_MACRO_BEGIN { \ - if ((seen).field) { \ - (status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \ - return STATE_NULL; \ - } \ - (seen).field = true; \ -} UPRV_BLOCK_MACRO_END - - -} // 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: - UPRV_UNREACHABLE_EXIT; - } -} - -MeasureUnit stem_to_object::unit(skeleton::StemEnum stem) { - switch (stem) { - case STEM_BASE_UNIT: - return MeasureUnit(); - case STEM_PERCENT: - return MeasureUnit::getPercent(); - case STEM_PERMILLE: - return MeasureUnit::getPermille(); - default: - UPRV_UNREACHABLE_EXIT; - } -} - -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: - UPRV_UNREACHABLE_EXIT; - } -} - -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_ODD: - return UNUM_ROUND_HALF_ODD; - case STEM_ROUNDING_MODE_HALF_CEILING: - return UNUM_ROUND_HALF_CEILING; - case STEM_ROUNDING_MODE_HALF_FLOOR: - return UNUM_ROUND_HALF_FLOOR; - 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: - UPRV_UNREACHABLE_EXIT; - } -} - -UNumberGroupingStrategy 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_FORMAL: - return UNUM_UNIT_WIDTH_FORMAL; - case STEM_UNIT_WIDTH_VARIANT: - return UNUM_UNIT_WIDTH_VARIANT; - 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; - case STEM_SIGN_NEGATIVE: - return UNUM_SIGN_NEGATIVE; - case STEM_SIGN_ACCOUNTING_NEGATIVE: - return UNUM_SIGN_ACCOUNTING_NEGATIVE; - 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_HALF_ODD: - sb.append(u"rounding-mode-half-odd", -1); - break; - case UNUM_ROUND_HALF_CEILING: - sb.append(u"rounding-mode-half-ceiling", -1); - break; - case UNUM_ROUND_HALF_FLOOR: - sb.append(u"rounding-mode-half-floor", -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: - UPRV_UNREACHABLE_EXIT; - } -} - -void enum_to_stem_string::groupingStrategy(UNumberGroupingStrategy 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: - UPRV_UNREACHABLE_EXIT; - } -} - -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_FORMAL: - sb.append(u"unit-width-formal", -1); - break; - case UNUM_UNIT_WIDTH_VARIANT: - sb.append(u"unit-width-variant", -1); - break; - case UNUM_UNIT_WIDTH_HIDDEN: - sb.append(u"unit-width-hidden", -1); - break; - default: - UPRV_UNREACHABLE_EXIT; - } -} - -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; - case UNUM_SIGN_NEGATIVE: - sb.append(u"sign-negative", -1); - break; - case UNUM_SIGN_ACCOUNTING_NEGATIVE: - sb.append(u"sign-accounting-negative", -1); - break; - default: - UPRV_UNREACHABLE_EXIT; - } -} - -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: - UPRV_UNREACHABLE_EXIT; - } -} - - -UnlocalizedNumberFormatter skeleton::create( - const UnicodeString& skeletonString, UParseError* perror, UErrorCode& status) { - - // Initialize perror - if (perror != nullptr) { - perror->line = 0; - perror->offset = -1; - perror->preContext[0] = 0; - perror->postContext[0] = 0; - } - - umtx_initOnce(gNumberSkeletonsInitOnce, &initNumberSkeletons, status); - if (U_FAILURE(status)) { - return {}; - } - - int32_t errOffset; - MacroProps macros = parseSkeleton(skeletonString, errOffset, status); - if (U_SUCCESS(status)) { - return NumberFormatter::with().macros(macros); - } - - if (perror == nullptr) { - return {}; - } - - // Populate the UParseError with the error location - perror->offset = errOffset; - int32_t contextStart = uprv_max(0, errOffset - U_PARSE_CONTEXT_LEN + 1); - int32_t contextEnd = uprv_min(skeletonString.length(), errOffset + U_PARSE_CONTEXT_LEN - 1); - skeletonString.extract(contextStart, errOffset - contextStart, perror->preContext, 0); - perror->preContext[errOffset - contextStart] = 0; - skeletonString.extract(errOffset, contextEnd - errOffset, perror->postContext, 0); - perror->postContext[contextEnd - errOffset] = 0; - return {}; -} - -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, int32_t& errOffset, UErrorCode& status) { - U_ASSERT(U_SUCCESS(status)); - U_ASSERT(kSerializedStemTrie != nullptr); - - // 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)) { - errOffset = segment.getOffset(); - 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; - errOffset = segment.getOffset(); - 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; - errOffset = segment.getOffset(); - 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_IDENTIFIER_UNIT: - case STATE_UNIT_USAGE: - 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; - errOffset = segment.getOffset(); - 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) { - U_ASSERT(U_SUCCESS(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_PRECISION; - case u'E': - CHECK_NULL(seen, notation, status); - blueprint_helpers::parseScientificStem(segment, macros, status); - return STATE_NULL; - case u'0': - CHECK_NULL(seen, integerWidth, status); - blueprint_helpers::parseIntegerStem(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_PERCENT_100: - CHECK_NULL(seen, scale, status); - CHECK_NULL(seen, unit, status); - macros.scale = Scale::powerOfTen(2); - macros.unit = NoUnit::percent(); - 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_PRECISION; - } - - 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_ODD: - case STEM_ROUNDING_MODE_HALF_CEILING: - case STEM_ROUNDING_MODE_HALF_FLOOR: - 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_INTEGER_WIDTH_TRUNC: - CHECK_NULL(seen, integerWidth, status); - macros.integerWidth = IntegerWidth::zeroFillTo(0).truncateAt(0); - 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_FORMAL: - case STEM_UNIT_WIDTH_VARIANT: - 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: - case STEM_SIGN_NEGATIVE: - case STEM_SIGN_ACCOUNTING_NEGATIVE: - 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_UNIT: - CHECK_NULL(seen, unit, status); - CHECK_NULL(seen, perUnit, status); - return STATE_IDENTIFIER_UNIT; - - case STEM_UNIT_USAGE: - CHECK_NULL(seen, usage, status); - return STATE_UNIT_USAGE; - - case STEM_CURRENCY: - CHECK_NULL(seen, unit, status); - CHECK_NULL(seen, perUnit, 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: - UPRV_UNREACHABLE_EXIT; - } -} - -ParseState skeleton::parseOption(ParseState stem, const StringSegment& segment, MacroProps& macros, - UErrorCode& status) { - U_ASSERT(U_SUCCESS(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_IDENTIFIER_UNIT: - blueprint_helpers::parseIdentifierUnitOption(segment, macros, status); - return STATE_NULL; - case STATE_UNIT_USAGE: - blueprint_helpers::parseUnitUsageOption(segment, macros, status); - return STATE_NULL; - case STATE_INCREMENT_PRECISION: - blueprint_helpers::parseIncrementOption(segment, macros, status); - return STATE_PRECISION; - 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_PRECISION; - } - if (U_FAILURE(status)) { - return {}; - } - // If the fracSig option was not found, try normal precision options. - stem = STATE_PRECISION; - break; - default: - break; - } - - // Trailing zeros option - switch (stem) { - case STATE_PRECISION: - if (blueprint_helpers::parseTrailingZeroOption(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::usage(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.unitDisplayCase.isSet()) { - status = U_UNSUPPORTED_ERROR; - return; - } - if (macros.affixProvider != nullptr) { - status = U_UNSUPPORTED_ERROR; - return; - } - if (macros.rules != 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 (!isWildcardChar(segment.charAt(0))) { - 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(kWildcardChar); - 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) { - U_ASSERT(U_SUCCESS(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... - 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 (Aug 2020) is "volume", which has 33 units. - static constexpr int32_t CAPACITY = 40; - 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::parseMeasurePerUnitOption(const StringSegment& segment, MacroProps& macros, - UErrorCode& status) { - // A little bit of a hack: save 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::parseIdentifierUnitOption(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); - - ErrorCode internalStatus; - macros.unit = MeasureUnit::forIdentifier(buffer.toStringPiece(), internalStatus); - if (internalStatus.isFailure()) { - // throw new SkeletonSyntaxException("Invalid core unit identifier", segment, e); - status = U_NUMBER_SKELETON_SYNTAX_ERROR; - return; - } -} - -void blueprint_helpers::parseUnitUsageOption(const StringSegment &segment, MacroProps ¯os, - 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); - macros.usage.set(buffer.toStringPiece()); - // We do not do any validation of the usage string: it depends on the - // unitPreferenceData in the units resources. -} - -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 (isWildcardChar(segment.charAt(offset))) { - 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) { - if (minFrac == 0) { - macros.precision = Precision::unlimited(); - } else { - 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(kWildcardChar); - } else { - appendMultiple(sb, u'#', maxFrac - minFrac); - } -} - -void -blueprint_helpers::parseDigitsStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) { - U_ASSERT(segment.charAt(0) == u'@'); - int32_t offset = 0; - int32_t minSig = 0; - int32_t maxSig; - for (; offset < segment.length(); offset++) { - if (segment.charAt(offset) == u'@') { - minSig++; - } else { - break; - } - } - if (offset < segment.length()) { - if (isWildcardChar(segment.charAt(offset))) { - 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(kWildcardChar); - } else { - appendMultiple(sb, u'#', maxSig - minSig); - } -} - -void blueprint_helpers::parseScientificStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) { - U_ASSERT(segment.charAt(0) == u'E'); - { - int32_t offset = 1; - if (segment.length() == offset) { - goto fail; - } - bool isEngineering = false; - if (segment.charAt(offset) == u'E') { - isEngineering = true; - offset++; - if (segment.length() == offset) { - goto fail; - } - } - UNumberSignDisplay signDisplay = UNUM_SIGN_AUTO; - if (segment.charAt(offset) == u'+') { - offset++; - if (segment.length() == offset) { - goto fail; - } - if (segment.charAt(offset) == u'!') { - signDisplay = UNUM_SIGN_ALWAYS; - } else if (segment.charAt(offset) == u'?') { - signDisplay = UNUM_SIGN_EXCEPT_ZERO; - } else { - // NOTE: Other sign displays are not included because they aren't useful in this context - goto fail; - } - offset++; - if (segment.length() == offset) { - goto fail; - } - } - int32_t minDigits = 0; - for (; offset < segment.length(); offset++) { - if (segment.charAt(offset) != u'0') { - goto fail; - } - minDigits++; - } - macros.notation = (isEngineering ? Notation::engineering() : Notation::scientific()) - .withExponentSignDisplay(signDisplay) - .withMinExponentDigits(minDigits); - return; - } - fail: void(); - // throw new SkeletonSyntaxException("Invalid scientific stem", segment); - status = U_NUMBER_SKELETON_SYNTAX_ERROR; - return; -} - -void blueprint_helpers::parseIntegerStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) { - U_ASSERT(segment.charAt(0) == u'0'); - int32_t offset = 1; - for (; offset < segment.length(); offset++) { - if (segment.charAt(offset) != u'0') { - offset--; - break; - } - } - if (offset < segment.length()) { - // throw new SkeletonSyntaxException("Invalid integer stem", segment); - status = U_NUMBER_SKELETON_SYNTAX_ERROR; - return; - } - macros.integerWidth = IntegerWidth::zeroFillTo(offset); - return; -} - -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; - } - } - if (offset < segment.length()) { - if (isWildcardChar(segment.charAt(offset))) { - // @+, @@+, @@@+ - maxSig = -1; - offset++; - } else { - // @#, @##, @### - // @@#, @@##, @@@# - maxSig = minSig; - for (; offset < segment.length(); offset++) { - if (segment.charAt(offset) == u'#') { - maxSig++; - } else { - break; - } - } - } - } else { - // @, @@, @@@ - maxSig = minSig; - } - auto& oldPrecision = static_cast(macros.precision); - if (offset < segment.length()) { - UNumberRoundingPriority priority; - if (maxSig == -1) { - // The wildcard character is not allowed with the priority annotation - status = U_NUMBER_SKELETON_SYNTAX_ERROR; - return false; - } - if (segment.codePointAt(offset) == u'r') { - priority = UNUM_ROUNDING_PRIORITY_RELAXED; - offset++; - } else if (segment.codePointAt(offset) == u's') { - priority = UNUM_ROUNDING_PRIORITY_STRICT; - offset++; - } else { - // Invalid digits option for fraction rounder - status = U_NUMBER_SKELETON_SYNTAX_ERROR; - return false; - } - if (offset < segment.length()) { - // Invalid digits option for fraction rounder - status = U_NUMBER_SKELETON_SYNTAX_ERROR; - return false; - } - macros.precision = oldPrecision.withSignificantDigits(minSig, maxSig, priority); - } else if (maxSig == -1) { - // withMinDigits - macros.precision = oldPrecision.withMinDigits(minSig); - } else if (minSig == 1) { - // withMaxDigits - macros.precision = oldPrecision.withMaxDigits(maxSig); - } else { - // Digits options with both min and max sig require the priority option - status = U_NUMBER_SKELETON_SYNTAX_ERROR; - return false; - } - - return true; -} - -bool blueprint_helpers::parseTrailingZeroOption(const StringSegment& segment, MacroProps& macros, UErrorCode&) { - if (segment == u"w") { - macros.precision = macros.precision.trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE); - return true; - } - return false; -} - -void blueprint_helpers::parseIncrementOption(const StringSegment &segment, MacroProps ¯os, - UErrorCode &status) { - number::impl::parseIncrementOption(segment, macros.precision, status); -} - -void blueprint_helpers::generateIncrementOption( - uint32_t increment, - digits_t incrementMagnitude, - int32_t minFrac, - UnicodeString& sb, - UErrorCode&) { - // Utilize DecimalQuantity/double_conversion to format this for us. - DecimalQuantity dq; - dq.setToLong(increment); - dq.adjustMagnitude(incrementMagnitude); - dq.setMinFraction(minFrac); - sb.append(dq.toPlainString()); -} - -void blueprint_helpers::parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros, - UErrorCode& status) { - int32_t offset = 0; - int32_t minInt = 0; - int32_t maxInt; - if (isWildcardChar(segment.charAt(0))) { - maxInt = -1; - offset++; - } else { - maxInt = 0; - } - for (; offset < segment.length(); offset++) { - if (maxInt != -1 && 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(kWildcardChar); - } 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) || decnum->isSpecial()) { - // 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) { - MeasureUnit unit = macros.unit; - if (!utils::unitIsBaseUnit(macros.perUnit)) { - if (utils::unitIsCurrency(macros.unit) || utils::unitIsCurrency(macros.perUnit)) { - status = U_UNSUPPORTED_ERROR; - return false; - } - unit = unit.product(macros.perUnit.reciprocal(status), status); - } - - if (utils::unitIsCurrency(unit)) { - sb.append(u"currency/", -1); - CurrencyUnit currency(unit, status); - if (U_FAILURE(status)) { - return false; - } - blueprint_helpers::generateCurrencyOption(currency, sb, status); - return true; - } else if (utils::unitIsBaseUnit(unit)) { - // Default value is not shown in normalized form - return false; - } else if (utils::unitIsPercent(unit)) { - sb.append(u"percent", -1); - return true; - } else if (utils::unitIsPermille(unit)) { - sb.append(u"permille", -1); - return true; - } else { - sb.append(u"unit/", -1); - sb.append(unit.getIdentifier()); - return true; - } -} - -bool GeneratorHelpers::usage(const MacroProps& macros, UnicodeString& sb, UErrorCode& /* status */) { - if (macros.usage.isSet()) { - sb.append(u"usage/", -1); - sb.append(UnicodeString(macros.usage.fValue, -1, US_INV)); - return true; - } - return false; -} - -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.fRetain) { - if (impl.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { - // withMinDigits - blueprint_helpers::generateDigitsStem(impl.fMaxSig, -1, sb, status); - } else { - // withMaxDigits - blueprint_helpers::generateDigitsStem(1, impl.fMaxSig, sb, status); - } - } else { - blueprint_helpers::generateDigitsStem(impl.fMinSig, impl.fMaxSig, sb, status); - if (impl.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { - sb.append(u'r'); - } else { - sb.append(u's'); - } - } - } else if (macros.precision.fType == Precision::RND_INCREMENT - || macros.precision.fType == Precision::RND_INCREMENT_ONE - || macros.precision.fType == Precision::RND_INCREMENT_FIVE) { - const Precision::IncrementSettings& impl = macros.precision.fUnion.increment; - sb.append(u"precision-increment/", -1); - blueprint_helpers::generateIncrementOption( - impl.fIncrement, - impl.fIncrementMagnitude, - impl.fMinFrac, - 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; - } - - if (macros.precision.fTrailingZeroDisplay == UNUM_TRAILING_ZERO_HIDE_IF_WHOLE) { - sb.append(u"/w", -1); - } - - // 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; - } - const auto& minMaxInt = macros.integerWidth.fUnion.minMaxInt; - if (minMaxInt.fMinInt == 0 && minMaxInt.fMaxInt == 0) { - sb.append(u"integer-width-trunc", -1); - return true; - } - sb.append(u"integer-width/", -1); - blueprint_helpers::generateIntegerWidthOption( - minMaxInt.fMinInt, - 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; -} - - -// Definitions of public API methods (put here for dependency disentanglement) - -#if (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN) && defined(_MSC_VER) -// Ignore MSVC warning 4661. This is generated for NumberFormatterSettings<>::toSkeleton() as this method -// is defined elsewhere (in number_skeletons.cpp). The compiler is warning that the explicit template instantiation -// inside this single translation unit (CPP file) is incomplete, and thus it isn't sure if the template class is -// fully defined. However, since each translation unit explicitly instantiates all the necessary template classes, -// they will all be passed to the linker, and the linker will still find and export all the class members. -#pragma warning(push) -#pragma warning(disable: 4661) -#endif - -template -UnicodeString NumberFormatterSettings::toSkeleton(UErrorCode& status) const { - if (U_FAILURE(status)) { - return ICU_Utility::makeBogusString(); - } - 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 -class icu::number::NumberFormatterSettings; -template -class icu::number::NumberFormatterSettings; - -UnlocalizedNumberFormatter -NumberFormatter::forSkeleton(const UnicodeString& skeleton, UErrorCode& status) { - return skeleton::create(skeleton, nullptr, status); -} - -UnlocalizedNumberFormatter -NumberFormatter::forSkeleton(const UnicodeString& skeleton, UParseError& perror, UErrorCode& status) { - return skeleton::create(skeleton, &perror, status); -} - -#if (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN) && defined(_MSC_VER) -// Warning 4661. -#pragma warning(pop) -#endif - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_roundingutils.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" +#include "string_segment.h" +#include "unicode/errorcode.h" +#include "util.h" +#include "measunit_impl.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; +using namespace icu::number::impl::skeleton; + +namespace { + +icu::UInitOnce gNumberSkeletonsInitOnce {}; + +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-odd", STEM_ROUNDING_MODE_HALF_ODD, status); + b.add(u"rounding-mode-half-ceiling", STEM_ROUNDING_MODE_HALF_CEILING, status); + b.add(u"rounding-mode-half-floor", STEM_ROUNDING_MODE_HALF_FLOOR, 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"integer-width-trunc", STEM_INTEGER_WIDTH_TRUNC, 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-formal", STEM_UNIT_WIDTH_FORMAL, status); + b.add(u"unit-width-variant", STEM_UNIT_WIDTH_VARIANT, 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"sign-negative", STEM_SIGN_NEGATIVE, status); + b.add(u"sign-accounting-negative", STEM_SIGN_ACCOUNTING_NEGATIVE, 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"unit", STEM_UNIT, status); + b.add(u"usage", STEM_UNIT_USAGE, 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; } + + // Section 3 (concise tokens): + b.add(u"K", STEM_COMPACT_SHORT, status); + b.add(u"KK", STEM_COMPACT_LONG, status); + b.add(u"%", STEM_PERCENT, status); + b.add(u"%x100", STEM_PERCENT_100, status); + b.add(u",_", STEM_GROUP_OFF, status); + b.add(u",?", STEM_GROUP_MIN2, status); + b.add(u",!", STEM_GROUP_ON_ALIGNED, status); + b.add(u"+!", STEM_SIGN_ALWAYS, status); + b.add(u"+_", STEM_SIGN_NEVER, status); + b.add(u"()", STEM_SIGN_ACCOUNTING, status); + b.add(u"()!", STEM_SIGN_ACCOUNTING_ALWAYS, status); + b.add(u"+?", STEM_SIGN_EXCEPT_ZERO, status); + b.add(u"()?", STEM_SIGN_ACCOUNTING_EXCEPT_ZERO, status); + b.add(u"+-", STEM_SIGN_NEGATIVE, status); + b.add(u"()-", STEM_SIGN_ACCOUNTING_NEGATIVE, 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 */ \ +UPRV_BLOCK_MACRO_BEGIN { \ + if ((seen).field) { \ + (status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \ + return STATE_NULL; \ + } \ + (seen).field = true; \ +} UPRV_BLOCK_MACRO_END + + +} // 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: + UPRV_UNREACHABLE_EXIT; + } +} + +MeasureUnit stem_to_object::unit(skeleton::StemEnum stem) { + switch (stem) { + case STEM_BASE_UNIT: + return MeasureUnit(); + case STEM_PERCENT: + return MeasureUnit::getPercent(); + case STEM_PERMILLE: + return MeasureUnit::getPermille(); + default: + UPRV_UNREACHABLE_EXIT; + } +} + +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: + UPRV_UNREACHABLE_EXIT; + } +} + +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_ODD: + return UNUM_ROUND_HALF_ODD; + case STEM_ROUNDING_MODE_HALF_CEILING: + return UNUM_ROUND_HALF_CEILING; + case STEM_ROUNDING_MODE_HALF_FLOOR: + return UNUM_ROUND_HALF_FLOOR; + 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: + UPRV_UNREACHABLE_EXIT; + } +} + +UNumberGroupingStrategy 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_FORMAL: + return UNUM_UNIT_WIDTH_FORMAL; + case STEM_UNIT_WIDTH_VARIANT: + return UNUM_UNIT_WIDTH_VARIANT; + 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; + case STEM_SIGN_NEGATIVE: + return UNUM_SIGN_NEGATIVE; + case STEM_SIGN_ACCOUNTING_NEGATIVE: + return UNUM_SIGN_ACCOUNTING_NEGATIVE; + 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_HALF_ODD: + sb.append(u"rounding-mode-half-odd", -1); + break; + case UNUM_ROUND_HALF_CEILING: + sb.append(u"rounding-mode-half-ceiling", -1); + break; + case UNUM_ROUND_HALF_FLOOR: + sb.append(u"rounding-mode-half-floor", -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: + UPRV_UNREACHABLE_EXIT; + } +} + +void enum_to_stem_string::groupingStrategy(UNumberGroupingStrategy 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: + UPRV_UNREACHABLE_EXIT; + } +} + +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_FORMAL: + sb.append(u"unit-width-formal", -1); + break; + case UNUM_UNIT_WIDTH_VARIANT: + sb.append(u"unit-width-variant", -1); + break; + case UNUM_UNIT_WIDTH_HIDDEN: + sb.append(u"unit-width-hidden", -1); + break; + default: + UPRV_UNREACHABLE_EXIT; + } +} + +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; + case UNUM_SIGN_NEGATIVE: + sb.append(u"sign-negative", -1); + break; + case UNUM_SIGN_ACCOUNTING_NEGATIVE: + sb.append(u"sign-accounting-negative", -1); + break; + default: + UPRV_UNREACHABLE_EXIT; + } +} + +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: + UPRV_UNREACHABLE_EXIT; + } +} + + +UnlocalizedNumberFormatter skeleton::create( + const UnicodeString& skeletonString, UParseError* perror, UErrorCode& status) { + + // Initialize perror + if (perror != nullptr) { + perror->line = 0; + perror->offset = -1; + perror->preContext[0] = 0; + perror->postContext[0] = 0; + } + + umtx_initOnce(gNumberSkeletonsInitOnce, &initNumberSkeletons, status); + if (U_FAILURE(status)) { + return {}; + } + + int32_t errOffset; + MacroProps macros = parseSkeleton(skeletonString, errOffset, status); + if (U_SUCCESS(status)) { + return NumberFormatter::with().macros(macros); + } + + if (perror == nullptr) { + return {}; + } + + // Populate the UParseError with the error location + perror->offset = errOffset; + int32_t contextStart = uprv_max(0, errOffset - U_PARSE_CONTEXT_LEN + 1); + int32_t contextEnd = uprv_min(skeletonString.length(), errOffset + U_PARSE_CONTEXT_LEN - 1); + skeletonString.extract(contextStart, errOffset - contextStart, perror->preContext, 0); + perror->preContext[errOffset - contextStart] = 0; + skeletonString.extract(errOffset, contextEnd - errOffset, perror->postContext, 0); + perror->postContext[contextEnd - errOffset] = 0; + return {}; +} + +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, int32_t& errOffset, UErrorCode& status) { + U_ASSERT(U_SUCCESS(status)); + U_ASSERT(kSerializedStemTrie != nullptr); + + // 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)) { + errOffset = segment.getOffset(); + 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; + errOffset = segment.getOffset(); + 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; + errOffset = segment.getOffset(); + 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_IDENTIFIER_UNIT: + case STATE_UNIT_USAGE: + 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; + errOffset = segment.getOffset(); + 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) { + U_ASSERT(U_SUCCESS(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_PRECISION; + case u'E': + CHECK_NULL(seen, notation, status); + blueprint_helpers::parseScientificStem(segment, macros, status); + return STATE_NULL; + case u'0': + CHECK_NULL(seen, integerWidth, status); + blueprint_helpers::parseIntegerStem(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_PERCENT_100: + CHECK_NULL(seen, scale, status); + CHECK_NULL(seen, unit, status); + macros.scale = Scale::powerOfTen(2); + macros.unit = NoUnit::percent(); + 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_PRECISION; + } + + 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_ODD: + case STEM_ROUNDING_MODE_HALF_CEILING: + case STEM_ROUNDING_MODE_HALF_FLOOR: + 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_INTEGER_WIDTH_TRUNC: + CHECK_NULL(seen, integerWidth, status); + macros.integerWidth = IntegerWidth::zeroFillTo(0).truncateAt(0); + 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_FORMAL: + case STEM_UNIT_WIDTH_VARIANT: + 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: + case STEM_SIGN_NEGATIVE: + case STEM_SIGN_ACCOUNTING_NEGATIVE: + 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_UNIT: + CHECK_NULL(seen, unit, status); + CHECK_NULL(seen, perUnit, status); + return STATE_IDENTIFIER_UNIT; + + case STEM_UNIT_USAGE: + CHECK_NULL(seen, usage, status); + return STATE_UNIT_USAGE; + + case STEM_CURRENCY: + CHECK_NULL(seen, unit, status); + CHECK_NULL(seen, perUnit, 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: + UPRV_UNREACHABLE_EXIT; + } +} + +ParseState skeleton::parseOption(ParseState stem, const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + U_ASSERT(U_SUCCESS(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_IDENTIFIER_UNIT: + blueprint_helpers::parseIdentifierUnitOption(segment, macros, status); + return STATE_NULL; + case STATE_UNIT_USAGE: + blueprint_helpers::parseUnitUsageOption(segment, macros, status); + return STATE_NULL; + case STATE_INCREMENT_PRECISION: + blueprint_helpers::parseIncrementOption(segment, macros, status); + return STATE_PRECISION; + 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_PRECISION; + } + if (U_FAILURE(status)) { + return {}; + } + // If the fracSig option was not found, try normal precision options. + stem = STATE_PRECISION; + break; + default: + break; + } + + // Trailing zeros option + switch (stem) { + case STATE_PRECISION: + if (blueprint_helpers::parseTrailingZeroOption(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::usage(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.unitDisplayCase.isSet()) { + status = U_UNSUPPORTED_ERROR; + return; + } + if (macros.affixProvider != nullptr) { + status = U_UNSUPPORTED_ERROR; + return; + } + if (macros.rules != 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 (!isWildcardChar(segment.charAt(0))) { + 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(kWildcardChar); + 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 char16_t* 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) { + U_ASSERT(U_SUCCESS(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 <-> char16_t conversion... + 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 (Aug 2020) is "volume", which has 33 units. + static constexpr int32_t CAPACITY = 40; + 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::parseMeasurePerUnitOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + // A little bit of a hack: save 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::parseIdentifierUnitOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + // Need to do char <-> char16_t conversion... + U_ASSERT(U_SUCCESS(status)); + CharString buffer; + SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status); + + ErrorCode internalStatus; + macros.unit = MeasureUnit::forIdentifier(buffer.toStringPiece(), internalStatus); + if (internalStatus.isFailure()) { + // throw new SkeletonSyntaxException("Invalid core unit identifier", segment, e); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } +} + +void blueprint_helpers::parseUnitUsageOption(const StringSegment &segment, MacroProps ¯os, + UErrorCode &status) { + // Need to do char <-> char16_t conversion... + U_ASSERT(U_SUCCESS(status)); + CharString buffer; + SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status); + macros.usage.set(buffer.toStringPiece()); + // We do not do any validation of the usage string: it depends on the + // unitPreferenceData in the units resources. +} + +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 (isWildcardChar(segment.charAt(offset))) { + 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) { + if (minFrac == 0) { + macros.precision = Precision::unlimited(); + } else { + 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(kWildcardChar); + } else { + appendMultiple(sb, u'#', maxFrac - minFrac); + } +} + +void +blueprint_helpers::parseDigitsStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) { + U_ASSERT(segment.charAt(0) == u'@'); + int32_t offset = 0; + int32_t minSig = 0; + int32_t maxSig; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'@') { + minSig++; + } else { + break; + } + } + if (offset < segment.length()) { + if (isWildcardChar(segment.charAt(offset))) { + 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(kWildcardChar); + } else { + appendMultiple(sb, u'#', maxSig - minSig); + } +} + +void blueprint_helpers::parseScientificStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) { + U_ASSERT(segment.charAt(0) == u'E'); + { + int32_t offset = 1; + if (segment.length() == offset) { + goto fail; + } + bool isEngineering = false; + if (segment.charAt(offset) == u'E') { + isEngineering = true; + offset++; + if (segment.length() == offset) { + goto fail; + } + } + UNumberSignDisplay signDisplay = UNUM_SIGN_AUTO; + if (segment.charAt(offset) == u'+') { + offset++; + if (segment.length() == offset) { + goto fail; + } + if (segment.charAt(offset) == u'!') { + signDisplay = UNUM_SIGN_ALWAYS; + } else if (segment.charAt(offset) == u'?') { + signDisplay = UNUM_SIGN_EXCEPT_ZERO; + } else { + // NOTE: Other sign displays are not included because they aren't useful in this context + goto fail; + } + offset++; + if (segment.length() == offset) { + goto fail; + } + } + int32_t minDigits = 0; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) != u'0') { + goto fail; + } + minDigits++; + } + macros.notation = (isEngineering ? Notation::engineering() : Notation::scientific()) + .withExponentSignDisplay(signDisplay) + .withMinExponentDigits(minDigits); + return; + } + fail: void(); + // throw new SkeletonSyntaxException("Invalid scientific stem", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; +} + +void blueprint_helpers::parseIntegerStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) { + U_ASSERT(segment.charAt(0) == u'0'); + int32_t offset = 1; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) != u'0') { + offset--; + break; + } + } + if (offset < segment.length()) { + // throw new SkeletonSyntaxException("Invalid integer stem", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + macros.integerWidth = IntegerWidth::zeroFillTo(offset); + return; +} + +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; + } + } + if (offset < segment.length()) { + if (isWildcardChar(segment.charAt(offset))) { + // @+, @@+, @@@+ + maxSig = -1; + offset++; + } else { + // @#, @##, @### + // @@#, @@##, @@@# + maxSig = minSig; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'#') { + maxSig++; + } else { + break; + } + } + } + } else { + // @, @@, @@@ + maxSig = minSig; + } + auto& oldPrecision = static_cast(macros.precision); + if (offset < segment.length()) { + UNumberRoundingPriority priority; + if (maxSig == -1) { + // The wildcard character is not allowed with the priority annotation + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return false; + } + if (segment.codePointAt(offset) == u'r') { + priority = UNUM_ROUNDING_PRIORITY_RELAXED; + offset++; + } else if (segment.codePointAt(offset) == u's') { + priority = UNUM_ROUNDING_PRIORITY_STRICT; + offset++; + } else { + // Invalid digits option for fraction rounder + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return false; + } + if (offset < segment.length()) { + // Invalid digits option for fraction rounder + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return false; + } + macros.precision = oldPrecision.withSignificantDigits(minSig, maxSig, priority); + } else if (maxSig == -1) { + // withMinDigits + macros.precision = oldPrecision.withMinDigits(minSig); + } else if (minSig == 1) { + // withMaxDigits + macros.precision = oldPrecision.withMaxDigits(maxSig); + } else { + // Digits options with both min and max sig require the priority option + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return false; + } + + return true; +} + +bool blueprint_helpers::parseTrailingZeroOption(const StringSegment& segment, MacroProps& macros, UErrorCode&) { + if (segment == u"w") { + macros.precision = macros.precision.trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE); + return true; + } + return false; +} + +void blueprint_helpers::parseIncrementOption(const StringSegment &segment, MacroProps ¯os, + UErrorCode &status) { + number::impl::parseIncrementOption(segment, macros.precision, status); +} + +void blueprint_helpers::generateIncrementOption( + uint32_t increment, + digits_t incrementMagnitude, + int32_t minFrac, + UnicodeString& sb, + UErrorCode&) { + // Utilize DecimalQuantity/double_conversion to format this for us. + DecimalQuantity dq; + dq.setToLong(increment); + dq.adjustMagnitude(incrementMagnitude); + dq.setMinFraction(minFrac); + sb.append(dq.toPlainString()); +} + +void blueprint_helpers::parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + int32_t offset = 0; + int32_t minInt = 0; + int32_t maxInt; + if (isWildcardChar(segment.charAt(0))) { + maxInt = -1; + offset++; + } else { + maxInt = 0; + } + for (; offset < segment.length(); offset++) { + if (maxInt != -1 && 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(kWildcardChar); + } 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 <-> char16_t 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 <-> char16_t conversion... + sb.append(UnicodeString(ns.getName(), -1, US_INV)); +} + +void blueprint_helpers::parseScaleOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + // Need to do char <-> char16_t 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) || decnum->isSpecial()) { + // 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) { + MeasureUnit unit = macros.unit; + if (!utils::unitIsBaseUnit(macros.perUnit)) { + if (utils::unitIsCurrency(macros.unit) || utils::unitIsCurrency(macros.perUnit)) { + status = U_UNSUPPORTED_ERROR; + return false; + } + unit = unit.product(macros.perUnit.reciprocal(status), status); + } + + if (utils::unitIsCurrency(unit)) { + sb.append(u"currency/", -1); + CurrencyUnit currency(unit, status); + if (U_FAILURE(status)) { + return false; + } + blueprint_helpers::generateCurrencyOption(currency, sb, status); + return true; + } else if (utils::unitIsBaseUnit(unit)) { + // Default value is not shown in normalized form + return false; + } else if (utils::unitIsPercent(unit)) { + sb.append(u"percent", -1); + return true; + } else if (utils::unitIsPermille(unit)) { + sb.append(u"permille", -1); + return true; + } else { + sb.append(u"unit/", -1); + sb.append(unit.getIdentifier()); + return true; + } +} + +bool GeneratorHelpers::usage(const MacroProps& macros, UnicodeString& sb, UErrorCode& /* status */) { + if (macros.usage.isSet()) { + sb.append(u"usage/", -1); + sb.append(UnicodeString(macros.usage.fValue, -1, US_INV)); + return true; + } + return false; +} + +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.fRetain) { + if (impl.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { + // withMinDigits + blueprint_helpers::generateDigitsStem(impl.fMaxSig, -1, sb, status); + } else { + // withMaxDigits + blueprint_helpers::generateDigitsStem(1, impl.fMaxSig, sb, status); + } + } else { + blueprint_helpers::generateDigitsStem(impl.fMinSig, impl.fMaxSig, sb, status); + if (impl.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { + sb.append(u'r'); + } else { + sb.append(u's'); + } + } + } else if (macros.precision.fType == Precision::RND_INCREMENT + || macros.precision.fType == Precision::RND_INCREMENT_ONE + || macros.precision.fType == Precision::RND_INCREMENT_FIVE) { + const Precision::IncrementSettings& impl = macros.precision.fUnion.increment; + sb.append(u"precision-increment/", -1); + blueprint_helpers::generateIncrementOption( + impl.fIncrement, + impl.fIncrementMagnitude, + impl.fMinFrac, + 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; + } + + if (macros.precision.fTrailingZeroDisplay == UNUM_TRAILING_ZERO_HIDE_IF_WHOLE) { + sb.append(u"/w", -1); + } + + // 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; + } + const auto& minMaxInt = macros.integerWidth.fUnion.minMaxInt; + if (minMaxInt.fMinInt == 0 && minMaxInt.fMaxInt == 0) { + sb.append(u"integer-width-trunc", -1); + return true; + } + sb.append(u"integer-width/", -1); + blueprint_helpers::generateIntegerWidthOption( + minMaxInt.fMinInt, + 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; +} + + +// Definitions of public API methods (put here for dependency disentanglement) + +#if (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN) && defined(_MSC_VER) +// Ignore MSVC warning 4661. This is generated for NumberFormatterSettings<>::toSkeleton() as this method +// is defined elsewhere (in number_skeletons.cpp). The compiler is warning that the explicit template instantiation +// inside this single translation unit (CPP file) is incomplete, and thus it isn't sure if the template class is +// fully defined. However, since each translation unit explicitly instantiates all the necessary template classes, +// they will all be passed to the linker, and the linker will still find and export all the class members. +#pragma warning(push) +#pragma warning(disable: 4661) +#endif + +template +UnicodeString NumberFormatterSettings::toSkeleton(UErrorCode& status) const { + if (U_FAILURE(status)) { + return ICU_Utility::makeBogusString(); + } + 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 +class icu::number::NumberFormatterSettings; +template +class icu::number::NumberFormatterSettings; + +UnlocalizedNumberFormatter +NumberFormatter::forSkeleton(const UnicodeString& skeleton, UErrorCode& status) { + return skeleton::create(skeleton, nullptr, status); +} + +UnlocalizedNumberFormatter +NumberFormatter::forSkeleton(const UnicodeString& skeleton, UParseError& perror, UErrorCode& status) { + return skeleton::create(skeleton, &perror, status); +} + +#if (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN) && defined(_MSC_VER) +// Warning 4661. +#pragma warning(pop) +#endif + +#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 index 27f69cd48c39e9..edcc4272980af2 100644 --- a/deps/icu-small/source/i18n/number_skeletons.h +++ b/deps/icu-small/source/i18n/number_skeletons.h @@ -1,393 +1,393 @@ -// © 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" -#include "string_segment.h" - -U_NAMESPACE_BEGIN -namespace number { -namespace impl { - -// Forward-declaration -struct SeenMacroProps; - -// namespace for enums and entrypoint functions -namespace skeleton { - -//////////////////////////////////////////////////////////////////////////////////////// -// NOTE: For examples of how to add a new stem to the number skeleton parser, see: // -// https://github.com/unicode-org/icu/commit/a2a7982216b2348070dc71093775ac7195793d73 // -// and // -// https://github.com/unicode-org/icu/commit/6fe86f3934a8a5701034f648a8f7c5087e84aa28 // -//////////////////////////////////////////////////////////////////////////////////////// - -/** - * 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, - STATE_PRECISION, - - // Section 2: An option is required: - - STATE_INCREMENT_PRECISION, - STATE_MEASURE_UNIT, - STATE_PER_MEASURE_UNIT, - STATE_IDENTIFIER_UNIT, - STATE_UNIT_USAGE, - 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_PERCENT_100, // concise-only - 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_ODD, - STEM_ROUNDING_MODE_HALF_CEILING, - STEM_ROUNDING_MODE_HALF_FLOOR, - STEM_ROUNDING_MODE_HALF_DOWN, - STEM_ROUNDING_MODE_HALF_UP, - STEM_ROUNDING_MODE_UNNECESSARY, - STEM_INTEGER_WIDTH_TRUNC, - 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_FORMAL, - STEM_UNIT_WIDTH_VARIANT, - 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_SIGN_NEGATIVE, - STEM_SIGN_ACCOUNTING_NEGATIVE, - 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_UNIT, - STEM_UNIT_USAGE, - STEM_CURRENCY, - STEM_INTEGER_WIDTH, - STEM_NUMBERING_SYSTEM, - STEM_SCALE, -}; - -/** Default wildcard char, accepted on input and printed in output */ -constexpr char16_t kWildcardChar = u'*'; - -/** Alternative wildcard char, accept on input but not printed in output */ -constexpr char16_t kAltWildcardChar = u'+'; - -/** Checks whether the char is a wildcard on input */ -inline bool isWildcardChar(char16_t c) { - return c == kWildcardChar || c == kAltWildcardChar; -} - -/** - * 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, UParseError* perror, 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, int32_t& errOffset, 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); - -UNumberGroupingStrategy 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(UNumberGroupingStrategy 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); - -// "measure-unit/" is deprecated in favour of "unit/". -void parseMeasureUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); - -// "per-measure-unit/" is deprecated in favour of "unit/". -void parseMeasurePerUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); - -/** - * Parses unit identifiers like "meter-per-second" and "foot-and-inch", as - * specified via a "unit/" concise skeleton. - */ -void parseIdentifierUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); - -void parseUnitUsageOption(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); - -void parseScientificStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status); - -// Note: no generateScientificStem since this syntax was added later in ICU 67 - -void parseIntegerStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status); - -// Note: no generateIntegerStem since this syntax was added later in ICU 67 - -/** @return Whether we successfully found and parsed a frac-sig option. */ -bool parseFracSigOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); - -/** @return Whether we successfully found and parsed a trailing zero option. */ -bool parseTrailingZeroOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); - -void parseIncrementOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); - -void -generateIncrementOption(uint32_t increment, digits_t incrementMagnitude, int32_t minFrac, 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 usage(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 usage = 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 { - -#define SKELETON_UCHAR_TO_CHAR(dest, src, start, end, status) (void)(dest); \ -UPRV_BLOCK_MACRO_BEGIN { \ - 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; \ - } \ -} UPRV_BLOCK_MACRO_END - -} // namespace - -} // namespace impl -} // namespace number -U_NAMESPACE_END - -#endif //__SOURCE_NUMBER_SKELETONS_H__ -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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" +#include "string_segment.h" + +U_NAMESPACE_BEGIN +namespace number { +namespace impl { + +// Forward-declaration +struct SeenMacroProps; + +// namespace for enums and entrypoint functions +namespace skeleton { + +//////////////////////////////////////////////////////////////////////////////////////// +// NOTE: For examples of how to add a new stem to the number skeleton parser, see: // +// https://github.com/unicode-org/icu/commit/a2a7982216b2348070dc71093775ac7195793d73 // +// and // +// https://github.com/unicode-org/icu/commit/6fe86f3934a8a5701034f648a8f7c5087e84aa28 // +//////////////////////////////////////////////////////////////////////////////////////// + +/** + * 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, + STATE_PRECISION, + + // Section 2: An option is required: + + STATE_INCREMENT_PRECISION, + STATE_MEASURE_UNIT, + STATE_PER_MEASURE_UNIT, + STATE_IDENTIFIER_UNIT, + STATE_UNIT_USAGE, + 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_PERCENT_100, // concise-only + 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_ODD, + STEM_ROUNDING_MODE_HALF_CEILING, + STEM_ROUNDING_MODE_HALF_FLOOR, + STEM_ROUNDING_MODE_HALF_DOWN, + STEM_ROUNDING_MODE_HALF_UP, + STEM_ROUNDING_MODE_UNNECESSARY, + STEM_INTEGER_WIDTH_TRUNC, + 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_FORMAL, + STEM_UNIT_WIDTH_VARIANT, + 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_SIGN_NEGATIVE, + STEM_SIGN_ACCOUNTING_NEGATIVE, + 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_UNIT, + STEM_UNIT_USAGE, + STEM_CURRENCY, + STEM_INTEGER_WIDTH, + STEM_NUMBERING_SYSTEM, + STEM_SCALE, +}; + +/** Default wildcard char, accepted on input and printed in output */ +constexpr char16_t kWildcardChar = u'*'; + +/** Alternative wildcard char, accept on input but not printed in output */ +constexpr char16_t kAltWildcardChar = u'+'; + +/** Checks whether the char is a wildcard on input */ +inline bool isWildcardChar(char16_t c) { + return c == kWildcardChar || c == kAltWildcardChar; +} + +/** + * 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, UParseError* perror, 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, int32_t& errOffset, 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); + +UNumberGroupingStrategy 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(UNumberGroupingStrategy 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); + +// "measure-unit/" is deprecated in favour of "unit/". +void parseMeasureUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +// "per-measure-unit/" is deprecated in favour of "unit/". +void parseMeasurePerUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +/** + * Parses unit identifiers like "meter-per-second" and "foot-and-inch", as + * specified via a "unit/" concise skeleton. + */ +void parseIdentifierUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void parseUnitUsageOption(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); + +void parseScientificStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +// Note: no generateScientificStem since this syntax was added later in ICU 67 + +void parseIntegerStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +// Note: no generateIntegerStem since this syntax was added later in ICU 67 + +/** @return Whether we successfully found and parsed a frac-sig option. */ +bool parseFracSigOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +/** @return Whether we successfully found and parsed a trailing zero option. */ +bool parseTrailingZeroOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void parseIncrementOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void +generateIncrementOption(uint32_t increment, digits_t incrementMagnitude, int32_t minFrac, 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 usage(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 usage = 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 { + +#define SKELETON_UCHAR_TO_CHAR(dest, src, start, end, status) (void)(dest); \ +UPRV_BLOCK_MACRO_BEGIN { \ + 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; \ + } \ +} UPRV_BLOCK_MACRO_END + +} // namespace + +} // 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_symbolswrapper.cpp b/deps/icu-small/source/i18n/number_symbolswrapper.cpp index ac3043d1ca11c2..f1737ceaa94a97 100644 --- a/deps/icu-small/source/i18n/number_symbolswrapper.cpp +++ b/deps/icu-small/source/i18n/number_symbolswrapper.cpp @@ -1,131 +1,131 @@ -// © 2020 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "number_microprops.h" -#include "unicode/numberformatter.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - -SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper &other) { - doCopyFrom(other); -} - -SymbolsWrapper::SymbolsWrapper(SymbolsWrapper &&src) U_NOEXCEPT { - doMoveFrom(std::move(src)); -} - -SymbolsWrapper &SymbolsWrapper::operator=(const SymbolsWrapper &other) { - if (this == &other) { - return *this; - } - doCleanup(); - doCopyFrom(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) { - doCleanup(); - fType = SYMPTR_DFS; - fPtr.dfs = new DecimalFormatSymbols(dfs); -} - -void SymbolsWrapper::setTo(const NumberingSystem *ns) { - doCleanup(); - fType = SYMPTR_NS; - fPtr.ns = ns; -} - -void SymbolsWrapper::doCopyFrom(const SymbolsWrapper &other) { - fType = other.fType; - switch (fType) { - case SYMPTR_NONE: - // No action necessary - break; - case SYMPTR_DFS: - // Memory allocation failures are exposed in copyErrorTo() - if (other.fPtr.dfs != nullptr) { - fPtr.dfs = new DecimalFormatSymbols(*other.fPtr.dfs); - } else { - fPtr.dfs = nullptr; - } - break; - case SYMPTR_NS: - // Memory allocation failures are exposed in copyErrorTo() - if (other.fPtr.ns != nullptr) { - fPtr.ns = new NumberingSystem(*other.fPtr.ns); - } else { - fPtr.ns = nullptr; - } - break; - } -} - -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: - // No action necessary - break; - case SYMPTR_DFS: - delete fPtr.dfs; - break; - case SYMPTR_NS: - delete fPtr.ns; - break; - } -} - -bool SymbolsWrapper::isDecimalFormatSymbols() const { - return fType == SYMPTR_DFS; -} - -bool SymbolsWrapper::isNumberingSystem() const { - return fType == SYMPTR_NS; -} - -const DecimalFormatSymbols *SymbolsWrapper::getDecimalFormatSymbols() const { - U_ASSERT(fType == SYMPTR_DFS); - return fPtr.dfs; -} - -const NumberingSystem *SymbolsWrapper::getNumberingSystem() const { - U_ASSERT(fType == SYMPTR_NS); - return fPtr.ns; -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 2020 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "number_microprops.h" +#include "unicode/numberformatter.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper &other) { + doCopyFrom(other); +} + +SymbolsWrapper::SymbolsWrapper(SymbolsWrapper &&src) noexcept { + doMoveFrom(std::move(src)); +} + +SymbolsWrapper &SymbolsWrapper::operator=(const SymbolsWrapper &other) { + if (this == &other) { + return *this; + } + doCleanup(); + doCopyFrom(other); + return *this; +} + +SymbolsWrapper &SymbolsWrapper::operator=(SymbolsWrapper &&src) noexcept { + if (this == &src) { + return *this; + } + doCleanup(); + doMoveFrom(std::move(src)); + return *this; +} + +SymbolsWrapper::~SymbolsWrapper() { + doCleanup(); +} + +void SymbolsWrapper::setTo(const DecimalFormatSymbols &dfs) { + doCleanup(); + fType = SYMPTR_DFS; + fPtr.dfs = new DecimalFormatSymbols(dfs); +} + +void SymbolsWrapper::setTo(const NumberingSystem *ns) { + doCleanup(); + fType = SYMPTR_NS; + fPtr.ns = ns; +} + +void SymbolsWrapper::doCopyFrom(const SymbolsWrapper &other) { + fType = other.fType; + switch (fType) { + case SYMPTR_NONE: + // No action necessary + break; + case SYMPTR_DFS: + // Memory allocation failures are exposed in copyErrorTo() + if (other.fPtr.dfs != nullptr) { + fPtr.dfs = new DecimalFormatSymbols(*other.fPtr.dfs); + } else { + fPtr.dfs = nullptr; + } + break; + case SYMPTR_NS: + // Memory allocation failures are exposed in copyErrorTo() + if (other.fPtr.ns != nullptr) { + fPtr.ns = new NumberingSystem(*other.fPtr.ns); + } else { + fPtr.ns = nullptr; + } + break; + } +} + +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: + // No action necessary + break; + case SYMPTR_DFS: + delete fPtr.dfs; + break; + case SYMPTR_NS: + delete fPtr.ns; + break; + } +} + +bool SymbolsWrapper::isDecimalFormatSymbols() const { + return fType == SYMPTR_DFS; +} + +bool SymbolsWrapper::isNumberingSystem() const { + return fType == SYMPTR_NS; +} + +const DecimalFormatSymbols *SymbolsWrapper::getDecimalFormatSymbols() const { + U_ASSERT(fType == SYMPTR_DFS); + return fPtr.dfs; +} + +const NumberingSystem *SymbolsWrapper::getNumberingSystem() const { + U_ASSERT(fType == SYMPTR_NS); + return fPtr.ns; +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_types.h b/deps/icu-small/source/i18n/number_types.h index 84846efb9242ac..a2984bbeea566e 100644 --- a/deps/icu-small/source/i18n/number_types.h +++ b/deps/icu-small/source/i18n/number_types.h @@ -1,374 +1,374 @@ -// © 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_TYPES_H__ -#define __NUMBER_TYPES_H__ - -#include -#include "unicode/decimfmt.h" -#include "unicode/unum.h" -#include "unicode/numsys.h" -#include "unicode/numberformatter.h" -#include "unicode/utf16.h" -#include "uassert.h" -#include "unicode/platform.h" -#include "unicode/uniset.h" -#include "standardplural.h" -#include "formatted_string_builder.h" - -U_NAMESPACE_BEGIN -namespace number { -namespace impl { - -// For convenience and historical reasons, import the Field typedef to the namespace. -typedef FormattedStringBuilder::Field Field; - -// Typedef several enums for brevity and for easier comparison to Java. - -typedef UNumberFormatRoundingMode RoundingMode; - -typedef UNumberFormatPadPosition PadPosition; - -typedef UNumberCompactStyle CompactStyle; - -// ICU4J Equivalent: RoundingUtils.MAX_INT_FRAC_SIG -static constexpr int32_t kMaxIntFracSig = 999; - -// ICU4J Equivalent: RoundingUtils.DEFAULT_ROUNDING_MODE -static constexpr RoundingMode kDefaultMode = RoundingMode::UNUM_FOUND_HALFEVEN; - -// ICU4J Equivalent: Padder.FALLBACK_PADDING_STRING -static constexpr char16_t kFallbackPaddingString[] = u" "; - -// Forward declarations: - -class Modifier; -class MutablePatternModifier; -class DecimalQuantity; -class ModifierStore; -struct MicroProps; - - -enum AffixPatternType { - // Represents a literal character; the value is stored in the code point field. - TYPE_CODEPOINT = 0, - - // Represents a minus sign symbol '-'. - TYPE_MINUS_SIGN = -1, - - // Represents a plus sign symbol '+'. - TYPE_PLUS_SIGN = -2, - - // Represents an approximately sign symbol '~'. - TYPE_APPROXIMATELY_SIGN = -3, - - // Represents a percent sign symbol '%'. - TYPE_PERCENT = -4, - - // Represents a permille sign symbol '‰'. - TYPE_PERMILLE = -5, - - // Represents a single currency symbol '¤'. - TYPE_CURRENCY_SINGLE = -6, - - // Represents a double currency symbol '¤¤'. - TYPE_CURRENCY_DOUBLE = -7, - - // Represents a triple currency symbol '¤¤¤'. - TYPE_CURRENCY_TRIPLE = -8, - - // Represents a quadruple currency symbol '¤¤¤¤'. - TYPE_CURRENCY_QUAD = -9, - - // Represents a quintuple currency symbol '¤¤¤¤¤'. - TYPE_CURRENCY_QUINT = -10, - - // Represents a sequence of six or more currency symbols. - TYPE_CURRENCY_OVERFLOW = -15 -}; - -enum CompactType { - TYPE_DECIMAL, TYPE_CURRENCY -}; - -enum Signum { - SIGNUM_NEG = 0, - SIGNUM_NEG_ZERO = 1, - SIGNUM_POS_ZERO = 2, - SIGNUM_POS = 3, - SIGNUM_COUNT = 4, -}; - - -class U_I18N_API AffixPatternProvider { - public: - static const int32_t AFFIX_PLURAL_MASK = 0xff; - static const int32_t AFFIX_PREFIX = 0x100; - static const int32_t AFFIX_NEGATIVE_SUBPATTERN = 0x200; - static const int32_t AFFIX_PADDING = 0x400; - - // 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; - - virtual bool hasNegativeSubpattern() const = 0; - - virtual bool negativeHasMinusSign() 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 - * have one. This is used in cases like compact notation, where the pattern replaces the entire - * number instead of rendering the number. - */ - virtual bool hasBody() const = 0; - - /** - * True if the currency symbol should replace the decimal separator. - */ - virtual bool currencyAsDecimal() const = 0; -}; - - -/** - * A Modifier is an object that can be passed through the formatting pipeline until it is finally applied to the string - * builder. A Modifier usually contains a prefix and a suffix that are applied, but it could contain something else, - * like a {@link com.ibm.icu.text.SimpleFormatter} pattern. - * - * A Modifier is usually immutable, except in cases such as {@link MutablePatternModifier}, which are mutable for performance - * reasons. - * - * Exported as U_I18N_API because it is a base class for other exported types - */ -class U_I18N_API Modifier { - public: - virtual ~Modifier(); - - /** - * Apply this Modifier to the string builder. - * - * @param output - * The string builder to which to apply this modifier. - * @param leftIndex - * The left index of the string within the builder. Equal to 0 when only one number is being formatted. - * @param rightIndex - * The right index of the string within the string builder. Equal to length when only one number is being - * formatted. - * @return The number of characters (UTF-16 code units) that were added to the string builder. - */ - virtual int32_t apply(FormattedStringBuilder& 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 - * prefix and suffix strings. - * - * @return The number of characters (UTF-16 code units) in the prefix. - */ - virtual int32_t getPrefixLength() const = 0; - - /** - * Returns the number of code points in the modifier, prefix plus suffix. - */ - virtual int32_t getCodePointCount() const = 0; - - /** - * Whether this modifier is strong. If a modifier is strong, it should always be applied immediately and not allowed - * to bubble up. With regard to padding, strong modifiers are considered to be on the inside of the prefix and - * suffix. - * - * @return Whether the modifier is strong. - */ - virtual bool isStrong() const = 0; - - /** - * Whether the modifier contains at least one occurrence of the given field. - */ - virtual bool containsField(Field field) const = 0; - - /** - * A fill-in for getParameters(). obj will always be set; if non-null, the other - * two fields are also safe to read. - */ - struct U_I18N_API Parameters { - const ModifierStore* obj = nullptr; - Signum signum; - StandardPlural::Form plural; - - Parameters(); - Parameters(const ModifierStore* _obj, Signum _signum, StandardPlural::Form _plural); - }; - - /** - * Gets a set of "parameters" for this Modifier. - * - * TODO: Make this return a `const Parameters*` more like Java? - */ - virtual void getParameters(Parameters& output) const = 0; - - /** - * Returns whether this Modifier is *semantically equivalent* to the other Modifier; - * in many cases, this is the same as equal, but parameters should be ignored. - */ - virtual bool semanticallyEquivalent(const Modifier& other) const = 0; -}; - - -/** - * This is *not* a modifier; rather, it is an object that can return modifiers - * based on given parameters. - * - * Exported as U_I18N_API because it is a base class for other exported types. - */ -class U_I18N_API ModifierStore { - public: - virtual ~ModifierStore(); - - /** - * Returns a Modifier with the given parameters (best-effort). - */ - virtual const Modifier* getModifier(Signum signum, StandardPlural::Form plural) const = 0; -}; - - -/** - * This interface is used when all number formatting settings, including the locale, are known, except for the quantity - * itself. The {@link #processQuantity} method performs the final step in the number processing pipeline: it uses the - * quantity to generate a finalized {@link MicroProps}, which can be used to render the number to output. - * - * In other words, this interface is used for the parts of number processing that are quantity-dependent. - * - * In order to allow for multiple different objects to all mutate the same MicroProps, a "chain" of MicroPropsGenerators - * are linked together, and each one is responsible for manipulating a certain quantity-dependent part of the - * MicroProps. At the tail of the linked list is a base instance of {@link MicroProps} with properties that are not - * quantity-dependent. Each element in the linked list calls {@link #processQuantity} on its "parent", then does its - * work, and then returns the result. - * - * This chain of MicroPropsGenerators is typically constructed by NumberFormatterImpl::macrosToMicroGenerator() when - * constructing a NumberFormatter. - * - * Exported as U_I18N_API because it is a base class for other exported types - * - */ -class U_I18N_API MicroPropsGenerator { - public: - virtual ~MicroPropsGenerator() = default; - - /** - * Considers the given {@link DecimalQuantity}, optionally mutates it, and - * populates a {@link MicroProps} instance. - * - * @param quantity The quantity for consideration and optional mutation. - * @param micros The MicroProps instance to populate. It will be modified as - * needed for the given quantity. - */ - virtual void processQuantity(DecimalQuantity& quantity, MicroProps& micros, - UErrorCode& status) const = 0; -}; - -/** - * An interface used by compact notation and scientific notation to choose a multiplier while rounding. - */ -class MultiplierProducer { - public: - virtual ~MultiplierProducer(); - - /** - * Maps a magnitude to a multiplier in powers of ten. For example, in compact notation in English, a magnitude of 5 - * (e.g., 100,000) should return a multiplier of -3, since the number is displayed in thousands. - * - * @param magnitude - * The power of ten of the input number. - * @return The shift in powers of ten. - */ - virtual int32_t getMultiplier(int32_t magnitude) const = 0; -}; - -// Exported as U_I18N_API because it is a public member field of exported DecimalFormatProperties -template -class U_I18N_API NullableValue { - public: - NullableValue() - : fNull(true) {} - - NullableValue(const NullableValue& other) = default; - - explicit NullableValue(const T& other) { - fValue = other; - fNull = false; - } - - NullableValue& operator=(const NullableValue& other) { - fNull = other.fNull; - if (!fNull) { - fValue = other.fValue; - } - return *this; - } - - NullableValue& operator=(const T& other) { - fValue = other; - fNull = false; - return *this; - } - - 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)); - } - - void nullify() { - // TODO: It might be nice to call the destructor here. - fNull = true; - } - - bool isNull() const { - return fNull; - } - - 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; -}; - - -} // namespace impl -} // namespace number -U_NAMESPACE_END - -#endif //__NUMBER_TYPES_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_TYPES_H__ +#define __NUMBER_TYPES_H__ + +#include +#include "unicode/decimfmt.h" +#include "unicode/unum.h" +#include "unicode/numsys.h" +#include "unicode/numberformatter.h" +#include "unicode/utf16.h" +#include "uassert.h" +#include "unicode/platform.h" +#include "unicode/uniset.h" +#include "standardplural.h" +#include "formatted_string_builder.h" + +U_NAMESPACE_BEGIN +namespace number { +namespace impl { + +// For convenience and historical reasons, import the Field typedef to the namespace. +typedef FormattedStringBuilder::Field Field; + +// Typedef several enums for brevity and for easier comparison to Java. + +typedef UNumberFormatRoundingMode RoundingMode; + +typedef UNumberFormatPadPosition PadPosition; + +typedef UNumberCompactStyle CompactStyle; + +// ICU4J Equivalent: RoundingUtils.MAX_INT_FRAC_SIG +static constexpr int32_t kMaxIntFracSig = 999; + +// ICU4J Equivalent: RoundingUtils.DEFAULT_ROUNDING_MODE +static constexpr RoundingMode kDefaultMode = RoundingMode::UNUM_FOUND_HALFEVEN; + +// ICU4J Equivalent: Padder.FALLBACK_PADDING_STRING +static constexpr char16_t kFallbackPaddingString[] = u" "; + +// Forward declarations: + +class Modifier; +class MutablePatternModifier; +class DecimalQuantity; +class ModifierStore; +struct MicroProps; + + +enum AffixPatternType { + // Represents a literal character; the value is stored in the code point field. + TYPE_CODEPOINT = 0, + + // Represents a minus sign symbol '-'. + TYPE_MINUS_SIGN = -1, + + // Represents a plus sign symbol '+'. + TYPE_PLUS_SIGN = -2, + + // Represents an approximately sign symbol '~'. + TYPE_APPROXIMATELY_SIGN = -3, + + // Represents a percent sign symbol '%'. + TYPE_PERCENT = -4, + + // Represents a permille sign symbol '‰'. + TYPE_PERMILLE = -5, + + // Represents a single currency symbol '¤'. + TYPE_CURRENCY_SINGLE = -6, + + // Represents a double currency symbol '¤¤'. + TYPE_CURRENCY_DOUBLE = -7, + + // Represents a triple currency symbol '¤¤¤'. + TYPE_CURRENCY_TRIPLE = -8, + + // Represents a quadruple currency symbol '¤¤¤¤'. + TYPE_CURRENCY_QUAD = -9, + + // Represents a quintuple currency symbol '¤¤¤¤¤'. + TYPE_CURRENCY_QUINT = -10, + + // Represents a sequence of six or more currency symbols. + TYPE_CURRENCY_OVERFLOW = -15 +}; + +enum CompactType { + TYPE_DECIMAL, TYPE_CURRENCY +}; + +enum Signum { + SIGNUM_NEG = 0, + SIGNUM_NEG_ZERO = 1, + SIGNUM_POS_ZERO = 2, + SIGNUM_POS = 3, + SIGNUM_COUNT = 4, +}; + + +class U_I18N_API AffixPatternProvider { + public: + static const int32_t AFFIX_PLURAL_MASK = 0xff; + static const int32_t AFFIX_PREFIX = 0x100; + static const int32_t AFFIX_NEGATIVE_SUBPATTERN = 0x200; + static const int32_t AFFIX_PADDING = 0x400; + + // 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; + + virtual bool hasNegativeSubpattern() const = 0; + + virtual bool negativeHasMinusSign() 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 + * have one. This is used in cases like compact notation, where the pattern replaces the entire + * number instead of rendering the number. + */ + virtual bool hasBody() const = 0; + + /** + * True if the currency symbol should replace the decimal separator. + */ + virtual bool currencyAsDecimal() const = 0; +}; + + +/** + * A Modifier is an object that can be passed through the formatting pipeline until it is finally applied to the string + * builder. A Modifier usually contains a prefix and a suffix that are applied, but it could contain something else, + * like a {@link com.ibm.icu.text.SimpleFormatter} pattern. + * + * A Modifier is usually immutable, except in cases such as {@link MutablePatternModifier}, which are mutable for performance + * reasons. + * + * Exported as U_I18N_API because it is a base class for other exported types + */ +class U_I18N_API Modifier { + public: + virtual ~Modifier(); + + /** + * Apply this Modifier to the string builder. + * + * @param output + * The string builder to which to apply this modifier. + * @param leftIndex + * The left index of the string within the builder. Equal to 0 when only one number is being formatted. + * @param rightIndex + * The right index of the string within the string builder. Equal to length when only one number is being + * formatted. + * @return The number of characters (UTF-16 code units) that were added to the string builder. + */ + virtual int32_t apply(FormattedStringBuilder& 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 + * prefix and suffix strings. + * + * @return The number of characters (UTF-16 code units) in the prefix. + */ + virtual int32_t getPrefixLength() const = 0; + + /** + * Returns the number of code points in the modifier, prefix plus suffix. + */ + virtual int32_t getCodePointCount() const = 0; + + /** + * Whether this modifier is strong. If a modifier is strong, it should always be applied immediately and not allowed + * to bubble up. With regard to padding, strong modifiers are considered to be on the inside of the prefix and + * suffix. + * + * @return Whether the modifier is strong. + */ + virtual bool isStrong() const = 0; + + /** + * Whether the modifier contains at least one occurrence of the given field. + */ + virtual bool containsField(Field field) const = 0; + + /** + * A fill-in for getParameters(). obj will always be set; if non-null, the other + * two fields are also safe to read. + */ + struct U_I18N_API Parameters { + const ModifierStore* obj = nullptr; + Signum signum; + StandardPlural::Form plural; + + Parameters(); + Parameters(const ModifierStore* _obj, Signum _signum, StandardPlural::Form _plural); + }; + + /** + * Gets a set of "parameters" for this Modifier. + * + * TODO: Make this return a `const Parameters*` more like Java? + */ + virtual void getParameters(Parameters& output) const = 0; + + /** + * Returns whether this Modifier is *semantically equivalent* to the other Modifier; + * in many cases, this is the same as equal, but parameters should be ignored. + */ + virtual bool semanticallyEquivalent(const Modifier& other) const = 0; +}; + + +/** + * This is *not* a modifier; rather, it is an object that can return modifiers + * based on given parameters. + * + * Exported as U_I18N_API because it is a base class for other exported types. + */ +class U_I18N_API ModifierStore { + public: + virtual ~ModifierStore(); + + /** + * Returns a Modifier with the given parameters (best-effort). + */ + virtual const Modifier* getModifier(Signum signum, StandardPlural::Form plural) const = 0; +}; + + +/** + * This interface is used when all number formatting settings, including the locale, are known, except for the quantity + * itself. The {@link #processQuantity} method performs the final step in the number processing pipeline: it uses the + * quantity to generate a finalized {@link MicroProps}, which can be used to render the number to output. + * + * In other words, this interface is used for the parts of number processing that are quantity-dependent. + * + * In order to allow for multiple different objects to all mutate the same MicroProps, a "chain" of MicroPropsGenerators + * are linked together, and each one is responsible for manipulating a certain quantity-dependent part of the + * MicroProps. At the tail of the linked list is a base instance of {@link MicroProps} with properties that are not + * quantity-dependent. Each element in the linked list calls {@link #processQuantity} on its "parent", then does its + * work, and then returns the result. + * + * This chain of MicroPropsGenerators is typically constructed by NumberFormatterImpl::macrosToMicroGenerator() when + * constructing a NumberFormatter. + * + * Exported as U_I18N_API because it is a base class for other exported types + * + */ +class U_I18N_API MicroPropsGenerator { + public: + virtual ~MicroPropsGenerator() = default; + + /** + * Considers the given {@link DecimalQuantity}, optionally mutates it, and + * populates a {@link MicroProps} instance. + * + * @param quantity The quantity for consideration and optional mutation. + * @param micros The MicroProps instance to populate. It will be modified as + * needed for the given quantity. + */ + virtual void processQuantity(DecimalQuantity& quantity, MicroProps& micros, + UErrorCode& status) const = 0; +}; + +/** + * An interface used by compact notation and scientific notation to choose a multiplier while rounding. + */ +class MultiplierProducer { + public: + virtual ~MultiplierProducer(); + + /** + * Maps a magnitude to a multiplier in powers of ten. For example, in compact notation in English, a magnitude of 5 + * (e.g., 100,000) should return a multiplier of -3, since the number is displayed in thousands. + * + * @param magnitude + * The power of ten of the input number. + * @return The shift in powers of ten. + */ + virtual int32_t getMultiplier(int32_t magnitude) const = 0; +}; + +// Exported as U_I18N_API because it is a public member field of exported DecimalFormatProperties +template +class U_I18N_API NullableValue { + public: + NullableValue() + : fNull(true) {} + + NullableValue(const NullableValue& other) = default; + + explicit NullableValue(const T& other) { + fValue = other; + fNull = false; + } + + NullableValue& operator=(const NullableValue& other) { + fNull = other.fNull; + if (!fNull) { + fValue = other.fValue; + } + return *this; + } + + NullableValue& operator=(const T& other) { + fValue = other; + fNull = false; + return *this; + } + + 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)); + } + + void nullify() { + // TODO: It might be nice to call the destructor here. + fNull = true; + } + + bool isNull() const { + return fNull; + } + + 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; +}; + + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif //__NUMBER_TYPES_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_usageprefs.cpp b/deps/icu-small/source/i18n/number_usageprefs.cpp index 26fdfafeea4ec2..aeb4ef8e246c65 100644 --- a/deps/icu-small/source/i18n/number_usageprefs.cpp +++ b/deps/icu-small/source/i18n/number_usageprefs.cpp @@ -1,214 +1,216 @@ -// © 2020 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "number_usageprefs.h" -#include "cstring.h" -#include "number_decimalquantity.h" -#include "number_microprops.h" -#include "number_roundingutils.h" -#include "number_skeletons.h" -#include "unicode/char16ptr.h" -#include "unicode/currunit.h" -#include "unicode/fmtable.h" -#include "unicode/measure.h" -#include "unicode/numberformatter.h" -#include "unicode/platform.h" -#include "unicode/unum.h" -#include "unicode/urename.h" -#include "units_data.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; -using icu::StringSegment; -using icu::units::ConversionRates; - -// Copy constructor -StringProp::StringProp(const StringProp &other) : StringProp() { - this->operator=(other); -} - -// Copy assignment operator -StringProp &StringProp::operator=(const StringProp &other) { - if (this == &other) { return *this; } // self-assignment: no-op - fLength = 0; - fError = other.fError; - if (fValue != nullptr) { - uprv_free(fValue); - fValue = nullptr; - } - if (other.fValue == nullptr) { - return *this; - } - if (U_FAILURE(other.fError)) { - // We don't bother trying to allocating memory if we're in any case busy - // copying an errored StringProp. - return *this; - } - fValue = (char *)uprv_malloc(other.fLength + 1); - if (fValue == nullptr) { - fError = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - fLength = other.fLength; - uprv_strncpy(fValue, other.fValue, fLength + 1); - return *this; -} - -// Move constructor -StringProp::StringProp(StringProp &&src) U_NOEXCEPT : fValue(src.fValue), - fLength(src.fLength), - fError(src.fError) { - // Take ownership away from src if necessary - src.fValue = nullptr; -} - -// Move assignment operator -StringProp &StringProp::operator=(StringProp &&src) U_NOEXCEPT { - if (this == &src) { - return *this; - } - if (fValue != nullptr) { - uprv_free(fValue); - } - fValue = src.fValue; - fLength = src.fLength; - fError = src.fError; - // Take ownership away from src if necessary - src.fValue = nullptr; - return *this; -} - -StringProp::~StringProp() { - if (fValue != nullptr) { - uprv_free(fValue); - fValue = nullptr; - } -} - -void StringProp::set(StringPiece value) { - if (fValue != nullptr) { - uprv_free(fValue); - fValue = nullptr; - } - fLength = value.length(); - fValue = (char *)uprv_malloc(fLength + 1); - if (fValue == nullptr) { - fLength = 0; - fError = U_MEMORY_ALLOCATION_ERROR; - return; - } - uprv_strncpy(fValue, value.data(), fLength); - fValue[fLength] = 0; -} - -// Populates micros.mixedMeasures and modifies quantity, based on the values in -// measures. -void mixedMeasuresToMicros(const MaybeStackVector &measures, DecimalQuantity *quantity, - MicroProps *micros, UErrorCode status) { - micros->mixedMeasuresCount = measures.length(); - - if (micros->mixedMeasures.getCapacity() < micros->mixedMeasuresCount) { - if (micros->mixedMeasures.resize(micros->mixedMeasuresCount) == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - - for (int32_t i = 0; i < micros->mixedMeasuresCount; i++) { - switch (measures[i]->getNumber().getType()) { - case Formattable::kInt64: - micros->mixedMeasures[i] = measures[i]->getNumber().getInt64(); - break; - - case Formattable::kDouble: - U_ASSERT(micros->indexOfQuantity < 0); - quantity->setToDouble(measures[i]->getNumber().getDouble()); - micros->indexOfQuantity = i; - break; - - default: - U_ASSERT(0 == "Found a Measure Number which is neither a double nor an int"); - UPRV_UNREACHABLE_EXIT; - break; - } - - if (U_FAILURE(status)) { - return; - } - } - - if (micros->indexOfQuantity < 0) { - // There is no quantity. - status = U_INTERNAL_PROGRAM_ERROR; - } -} - -UsagePrefsHandler::UsagePrefsHandler(const Locale &locale, - const MeasureUnit &inputUnit, - const StringPiece usage, - const MicroPropsGenerator *parent, - UErrorCode &status) - : fUnitsRouter(inputUnit, locale, usage, status), - fParent(parent) { -} - -void UsagePrefsHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const { - fParent->processQuantity(quantity, micros, status); - if (U_FAILURE(status)) { - return; - } - - quantity.roundToInfinity(); // Enables toDouble - const units::RouteResult routed = fUnitsRouter.route(quantity.toDouble(), µs.rounder, status); - if (U_FAILURE(status)) { - return; - } - const MaybeStackVector& routedMeasures = routed.measures; - micros.outputUnit = routed.outputUnit.copy(status).build(status); - if (U_FAILURE(status)) { - return; - } - - mixedMeasuresToMicros(routedMeasures, &quantity, µs, status); -} - -UnitConversionHandler::UnitConversionHandler(const MeasureUnit &targetUnit, - const MicroPropsGenerator *parent, UErrorCode &status) - : fOutputUnit(targetUnit), fParent(parent) { - MeasureUnitImpl tempInput, tempOutput; - - ConversionRates conversionRates(status); - if (U_FAILURE(status)) { - return; - } - - const MeasureUnitImpl &targetUnitImpl = - MeasureUnitImpl::forMeasureUnit(targetUnit, tempOutput, status); - fUnitConverter.adoptInsteadAndCheckErrorCode( - new ComplexUnitsConverter(targetUnitImpl, conversionRates, status), status); -} - -void UnitConversionHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const { - fParent->processQuantity(quantity, micros, status); - if (U_FAILURE(status)) { - return; - } - quantity.roundToInfinity(); // Enables toDouble - MaybeStackVector measures = - fUnitConverter->convert(quantity.toDouble(), µs.rounder, status); - micros.outputUnit = fOutputUnit; - if (U_FAILURE(status)) { - return; - } - - mixedMeasuresToMicros(measures, &quantity, µs, status); -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 2020 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "number_usageprefs.h" +#include "cstring.h" +#include "number_decimalquantity.h" +#include "number_microprops.h" +#include "number_roundingutils.h" +#include "number_skeletons.h" +#include "unicode/char16ptr.h" +#include "unicode/currunit.h" +#include "unicode/fmtable.h" +#include "unicode/measure.h" +#include "unicode/numberformatter.h" +#include "unicode/platform.h" +#include "unicode/unum.h" +#include "unicode/urename.h" +#include "units_data.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; +using icu::StringSegment; +using icu::units::ConversionRates; + +// Copy constructor +StringProp::StringProp(const StringProp &other) : StringProp() { + this->operator=(other); +} + +// Copy assignment operator +StringProp &StringProp::operator=(const StringProp &other) { + if (this == &other) { return *this; } // self-assignment: no-op + fLength = 0; + fError = other.fError; + if (fValue != nullptr) { + uprv_free(fValue); + fValue = nullptr; + } + if (other.fValue == nullptr) { + return *this; + } + if (U_FAILURE(other.fError)) { + // We don't bother trying to allocating memory if we're in any case busy + // copying an errored StringProp. + return *this; + } + fValue = (char *)uprv_malloc(other.fLength + 1); + if (fValue == nullptr) { + fError = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + fLength = other.fLength; + uprv_strncpy(fValue, other.fValue, fLength + 1); + return *this; +} + +// Move constructor +StringProp::StringProp(StringProp &&src) noexcept : fValue(src.fValue), + fLength(src.fLength), + fError(src.fError) { + // Take ownership away from src if necessary + src.fValue = nullptr; +} + +// Move assignment operator +StringProp &StringProp::operator=(StringProp &&src) noexcept { + if (this == &src) { + return *this; + } + if (fValue != nullptr) { + uprv_free(fValue); + } + fValue = src.fValue; + fLength = src.fLength; + fError = src.fError; + // Take ownership away from src if necessary + src.fValue = nullptr; + return *this; +} + +StringProp::~StringProp() { + if (fValue != nullptr) { + uprv_free(fValue); + fValue = nullptr; + } +} + +void StringProp::set(StringPiece value) { + if (fValue != nullptr) { + uprv_free(fValue); + fValue = nullptr; + } + fLength = value.length(); + fValue = (char *)uprv_malloc(fLength + 1); + if (fValue == nullptr) { + fLength = 0; + fError = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (fLength > 0) { + uprv_strncpy(fValue, value.data(), fLength); + } + fValue[fLength] = 0; +} + +// Populates micros.mixedMeasures and modifies quantity, based on the values in +// measures. +void mixedMeasuresToMicros(const MaybeStackVector &measures, DecimalQuantity *quantity, + MicroProps *micros, UErrorCode status) { + micros->mixedMeasuresCount = measures.length(); + + if (micros->mixedMeasures.getCapacity() < micros->mixedMeasuresCount) { + if (micros->mixedMeasures.resize(micros->mixedMeasuresCount) == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + + for (int32_t i = 0; i < micros->mixedMeasuresCount; i++) { + switch (measures[i]->getNumber().getType()) { + case Formattable::kInt64: + micros->mixedMeasures[i] = measures[i]->getNumber().getInt64(); + break; + + case Formattable::kDouble: + U_ASSERT(micros->indexOfQuantity < 0); + quantity->setToDouble(measures[i]->getNumber().getDouble()); + micros->indexOfQuantity = i; + break; + + default: + U_ASSERT(0 == "Found a Measure Number which is neither a double nor an int"); + UPRV_UNREACHABLE_EXIT; + break; + } + + if (U_FAILURE(status)) { + return; + } + } + + if (micros->indexOfQuantity < 0) { + // There is no quantity. + status = U_INTERNAL_PROGRAM_ERROR; + } +} + +UsagePrefsHandler::UsagePrefsHandler(const Locale &locale, + const MeasureUnit &inputUnit, + const StringPiece usage, + const MicroPropsGenerator *parent, + UErrorCode &status) + : fUnitsRouter(inputUnit, locale, usage, status), + fParent(parent) { +} + +void UsagePrefsHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, + UErrorCode &status) const { + fParent->processQuantity(quantity, micros, status); + if (U_FAILURE(status)) { + return; + } + + quantity.roundToInfinity(); // Enables toDouble + const units::RouteResult routed = fUnitsRouter.route(quantity.toDouble(), µs.rounder, status); + if (U_FAILURE(status)) { + return; + } + const MaybeStackVector& routedMeasures = routed.measures; + micros.outputUnit = routed.outputUnit.copy(status).build(status); + if (U_FAILURE(status)) { + return; + } + + mixedMeasuresToMicros(routedMeasures, &quantity, µs, status); +} + +UnitConversionHandler::UnitConversionHandler(const MeasureUnit &targetUnit, + const MicroPropsGenerator *parent, UErrorCode &status) + : fOutputUnit(targetUnit), fParent(parent) { + MeasureUnitImpl tempInput, tempOutput; + + ConversionRates conversionRates(status); + if (U_FAILURE(status)) { + return; + } + + const MeasureUnitImpl &targetUnitImpl = + MeasureUnitImpl::forMeasureUnit(targetUnit, tempOutput, status); + fUnitConverter.adoptInsteadAndCheckErrorCode( + new ComplexUnitsConverter(targetUnitImpl, conversionRates, status), status); +} + +void UnitConversionHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, + UErrorCode &status) const { + fParent->processQuantity(quantity, micros, status); + if (U_FAILURE(status)) { + return; + } + quantity.roundToInfinity(); // Enables toDouble + MaybeStackVector measures = + fUnitConverter->convert(quantity.toDouble(), µs.rounder, status); + micros.outputUnit = fOutputUnit; + if (U_FAILURE(status)) { + return; + } + + mixedMeasuresToMicros(measures, &quantity, µs, status); +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_usageprefs.h b/deps/icu-small/source/i18n/number_usageprefs.h index 70547225a00761..2730f011cab301 100644 --- a/deps/icu-small/source/i18n/number_usageprefs.h +++ b/deps/icu-small/source/i18n/number_usageprefs.h @@ -1,126 +1,126 @@ -// © 2020 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_USAGEPREFS_H__ -#define __NUMBER_USAGEPREFS_H__ - -#include "cmemory.h" -#include "number_types.h" -#include "unicode/listformatter.h" -#include "unicode/localpointer.h" -#include "unicode/locid.h" -#include "unicode/measunit.h" -#include "unicode/stringpiece.h" -#include "unicode/uobject.h" -#include "units_converter.h" -#include "units_router.h" - -U_NAMESPACE_BEGIN - -using ::icu::units::ComplexUnitsConverter; -using ::icu::units::UnitsRouter; - -namespace number { -namespace impl { - -/** - * A MicroPropsGenerator which uses UnitsRouter to produce output converted to a - * MeasureUnit appropriate for a particular localized usage: see - * NumberFormatterSettings::usage(). - */ -class U_I18N_API UsagePrefsHandler : public MicroPropsGenerator, public UMemory { - public: - UsagePrefsHandler(const Locale &locale, const MeasureUnit &inputUnit, const StringPiece usage, - const MicroPropsGenerator *parent, UErrorCode &status); - - /** - * Obtains the appropriate output value, MeasureUnit and - * rounding/precision behaviour from the UnitsRouter. - * - * The output unit is passed on to the LongNameHandler via - * micros.outputUnit. - */ - void processQuantity(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const U_OVERRIDE; - - /** - * Returns the list of possible output units, i.e. the full set of - * preferences, for the localized, usage-specific unit preferences. - * - * The returned pointer should be valid for the lifetime of the - * UsagePrefsHandler instance. - */ - const MaybeStackVector *getOutputUnits() const { - return fUnitsRouter.getOutputUnits(); - } - - private: - UnitsRouter fUnitsRouter; - const MicroPropsGenerator *fParent; -}; - -} // namespace impl -} // namespace number - -// Export explicit template instantiations of LocalPointerBase and LocalPointer. -// This is required when building DLLs for Windows. (See datefmt.h, -// collationiterator.h, erarules.h and others for similar examples.) -// -// Note: These need to be outside of the number::impl namespace, or Clang will -// generate a compile error. -#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN -#if defined(_MSC_VER) -// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!= -#pragma warning(push) -#pragma warning(disable: 4661) -#endif -template class U_I18N_API LocalPointerBase; -template class U_I18N_API LocalPointer; -#if defined(_MSC_VER) -#pragma warning(pop) -#endif -#endif - -namespace number { -namespace impl { - -/** - * A MicroPropsGenerator which converts a measurement from one MeasureUnit to - * another. In particular, the output MeasureUnit may be a mixed unit. (The - * input unit may not be a mixed unit.) - */ -class U_I18N_API UnitConversionHandler : public MicroPropsGenerator, public UMemory { - public: - /** - * Constructor. - * - * @param targetUnit Specifies the output MeasureUnit. The input MeasureUnit - * is derived from it: in case of a mixed unit, the biggest unit is - * taken as the input unit. If not a mixed unit, the input unit will be - * the same as the output unit and no unit conversion takes place. - * @param parent The parent MicroPropsGenerator. - * @param status Receives status. - */ - UnitConversionHandler(const MeasureUnit &targetUnit, const MicroPropsGenerator *parent, - UErrorCode &status); - - /** - * Obtains the appropriate output values from the Unit Converter. - */ - void processQuantity(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const U_OVERRIDE; - private: - MeasureUnit fOutputUnit; - LocalPointer fUnitConverter; - const MicroPropsGenerator *fParent; -}; - -} // namespace impl -} // namespace number -U_NAMESPACE_END - -#endif // __NUMBER_USAGEPREFS_H__ -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 2020 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_USAGEPREFS_H__ +#define __NUMBER_USAGEPREFS_H__ + +#include "cmemory.h" +#include "number_types.h" +#include "unicode/listformatter.h" +#include "unicode/localpointer.h" +#include "unicode/locid.h" +#include "unicode/measunit.h" +#include "unicode/stringpiece.h" +#include "unicode/uobject.h" +#include "units_converter.h" +#include "units_router.h" + +U_NAMESPACE_BEGIN + +using ::icu::units::ComplexUnitsConverter; +using ::icu::units::UnitsRouter; + +namespace number { +namespace impl { + +/** + * A MicroPropsGenerator which uses UnitsRouter to produce output converted to a + * MeasureUnit appropriate for a particular localized usage: see + * NumberFormatterSettings::usage(). + */ +class U_I18N_API UsagePrefsHandler : public MicroPropsGenerator, public UMemory { + public: + UsagePrefsHandler(const Locale &locale, const MeasureUnit &inputUnit, const StringPiece usage, + const MicroPropsGenerator *parent, UErrorCode &status); + + /** + * Obtains the appropriate output value, MeasureUnit and + * rounding/precision behaviour from the UnitsRouter. + * + * The output unit is passed on to the LongNameHandler via + * micros.outputUnit. + */ + void processQuantity(DecimalQuantity &quantity, MicroProps µs, + UErrorCode &status) const override; + + /** + * Returns the list of possible output units, i.e. the full set of + * preferences, for the localized, usage-specific unit preferences. + * + * The returned pointer should be valid for the lifetime of the + * UsagePrefsHandler instance. + */ + const MaybeStackVector *getOutputUnits() const { + return fUnitsRouter.getOutputUnits(); + } + + private: + UnitsRouter fUnitsRouter; + const MicroPropsGenerator *fParent; +}; + +} // namespace impl +} // namespace number + +// Export explicit template instantiations of LocalPointerBase and LocalPointer. +// This is required when building DLLs for Windows. (See datefmt.h, +// collationiterator.h, erarules.h and others for similar examples.) +// +// Note: These need to be outside of the number::impl namespace, or Clang will +// generate a compile error. +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +#if defined(_MSC_VER) +// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!= +#pragma warning(push) +#pragma warning(disable: 4661) +#endif +template class U_I18N_API LocalPointerBase; +template class U_I18N_API LocalPointer; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#endif + +namespace number { +namespace impl { + +/** + * A MicroPropsGenerator which converts a measurement from one MeasureUnit to + * another. In particular, the output MeasureUnit may be a mixed unit. (The + * input unit may not be a mixed unit.) + */ +class U_I18N_API UnitConversionHandler : public MicroPropsGenerator, public UMemory { + public: + /** + * Constructor. + * + * @param targetUnit Specifies the output MeasureUnit. The input MeasureUnit + * is derived from it: in case of a mixed unit, the biggest unit is + * taken as the input unit. If not a mixed unit, the input unit will be + * the same as the output unit and no unit conversion takes place. + * @param parent The parent MicroPropsGenerator. + * @param status Receives status. + */ + UnitConversionHandler(const MeasureUnit &targetUnit, const MicroPropsGenerator *parent, + UErrorCode &status); + + /** + * Obtains the appropriate output values from the Unit Converter. + */ + void processQuantity(DecimalQuantity &quantity, MicroProps µs, + UErrorCode &status) const override; + private: + MeasureUnit fOutputUnit; + LocalPointer fUnitConverter; + const MicroPropsGenerator *fParent; +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif // __NUMBER_USAGEPREFS_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_utils.cpp b/deps/icu-small/source/i18n/number_utils.cpp index ad7053214077b0..876d6471752842 100644 --- a/deps/icu-small/source/i18n/number_utils.cpp +++ b/deps/icu-small/source/i18n/number_utils.cpp @@ -1,275 +1,275 @@ -// © 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 - UPRV_UNREACHABLE_EXIT; - } - 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; - } -} - -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 & DEC_Inexact) != 0) { - // Ignore. - } else 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()); -} - -bool DecNum::isSpecial() const { - return decNumberIsSpecial(fData.getAlias()); -} - -bool DecNum::isInfinity() const { - return decNumberIsInfinite(fData.getAlias()); -} - -bool DecNum::isNaN() const { - return decNumberIsNaN(fData.getAlias()); -} - -void DecNum::toString(ByteSink& output, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - // "string must be at least dn->digits+14 characters long" - int32_t minCapacity = fData.getAlias()->digits + 14; - MaybeStackArray buffer(minCapacity, status); - if (U_FAILURE(status)) { - return; - } - uprv_decNumberToString(fData, buffer.getAlias()); - output.Append(buffer.getAlias(), static_cast(uprv_strlen(buffer.getAlias()))); -} - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 + UPRV_UNREACHABLE_EXIT; + } + 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; + } +} + +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 & DEC_Inexact) != 0) { + // Ignore. + } else 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()); +} + +bool DecNum::isSpecial() const { + return decNumberIsSpecial(fData.getAlias()); +} + +bool DecNum::isInfinity() const { + return decNumberIsInfinite(fData.getAlias()); +} + +bool DecNum::isNaN() const { + return decNumberIsNaN(fData.getAlias()); +} + +void DecNum::toString(ByteSink& output, UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + // "string must be at least dn->digits+14 characters long" + int32_t minCapacity = fData.getAlias()->digits + 14; + MaybeStackArray buffer(minCapacity, status); + if (U_FAILURE(status)) { + return; + } + uprv_decNumberToString(fData, buffer.getAlias()); + output.Append(buffer.getAlias(), static_cast(uprv_strlen(buffer.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 bc369c940f7962..1c1a09b46490c6 100644 --- a/deps/icu-small/source/i18n/number_utils.h +++ b/deps/icu-small/source/i18n/number_utils.h @@ -1,112 +1,112 @@ -// © 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_UTILS_H__ -#define __NUMBER_UTILS_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" -#include "formatted_string_builder.h" - -U_NAMESPACE_BEGIN - -namespace number { -namespace impl { - -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, -}; - -// Namespace for naked functions -namespace utils { - -inline int32_t insertDigitFromSymbols(FormattedStringBuilder& 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); -} - -inline bool unitIsCurrency(const MeasureUnit& unit) { - return uprv_strcmp("currency", unit.getType()) == 0; -} - -inline bool unitIsBaseUnit(const MeasureUnit& unit) { - return unit == MeasureUnit(); -} - -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; -} - -// NOTE: In Java, this method is in NumberFormat.java -const char16_t* -getPatternForStyle(const Locale& locale, const char* nsName, CldrPatternStyle style, UErrorCode& status); - -/** - * 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. - */ -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 { - UnicodeString ruleString = rules->select(fdec); - return StandardPlural::orOtherFromString(ruleString); - } -} - -/** - * Computes the plural form after copying the number and applying rounding rules. - */ -inline StandardPlural::Form getPluralSafe( - const RoundingImpl& rounder, - const PluralRules* rules, - const DecimalQuantity& dq, - UErrorCode& status) { - // TODO(ICU-20500): Avoid the copy? - DecimalQuantity copy(dq); - rounder.apply(copy, status); - if (U_FAILURE(status)) { - return StandardPlural::Form::OTHER; - } - return getStandardPlural(rules, copy); -} - -} // namespace utils - -} // namespace impl -} // namespace number - -U_NAMESPACE_END - -#endif //__NUMBER_UTILS_H__ - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_UTILS_H__ +#define __NUMBER_UTILS_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" +#include "formatted_string_builder.h" + +U_NAMESPACE_BEGIN + +namespace number { +namespace impl { + +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, +}; + +// Namespace for naked functions +namespace utils { + +inline int32_t insertDigitFromSymbols(FormattedStringBuilder& 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); +} + +inline bool unitIsCurrency(const MeasureUnit& unit) { + return uprv_strcmp("currency", unit.getType()) == 0; +} + +inline bool unitIsBaseUnit(const MeasureUnit& unit) { + return unit == MeasureUnit(); +} + +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; +} + +// NOTE: In Java, this method is in NumberFormat.java +const char16_t* +getPatternForStyle(const Locale& locale, const char* nsName, CldrPatternStyle style, UErrorCode& status); + +/** + * 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. + */ +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 { + UnicodeString ruleString = rules->select(fdec); + return StandardPlural::orOtherFromString(ruleString); + } +} + +/** + * Computes the plural form after copying the number and applying rounding rules. + */ +inline StandardPlural::Form getPluralSafe( + const RoundingImpl& rounder, + const PluralRules* rules, + const DecimalQuantity& dq, + UErrorCode& status) { + // TODO(ICU-20500): Avoid the copy? + DecimalQuantity copy(dq); + rounder.apply(copy, status); + if (U_FAILURE(status)) { + return StandardPlural::Form::OTHER; + } + return getStandardPlural(rules, copy); +} + +} // namespace utils + +} // namespace impl +} // namespace number + +U_NAMESPACE_END + +#endif //__NUMBER_UTILS_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_utypes.h b/deps/icu-small/source/i18n/number_utypes.h index 50c861787f4ed9..52769b4081248a 100644 --- a/deps/icu-small/source/i18n/number_utypes.h +++ b/deps/icu-small/source/i18n/number_utypes.h @@ -1,56 +1,59 @@ -// © 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 "formatted_string_builder.h" -#include "formattedval_impl.h" - -U_NAMESPACE_BEGIN namespace number { -namespace impl { - - -/** Helper function used in upluralrules.cpp */ -const DecimalQuantity* validateUFormattedNumberToDecimalQuantity( - const UFormattedNumber* uresult, UErrorCode& status); - - -/** - * Struct for data used by FormattedNumber. - * - * This struct is held internally by the C++ version FormattedNumber since the member types are not - * declared in the public header file. - * - * Exported as U_I18N_API for tests - */ -class U_I18N_API UFormattedNumberData : public FormattedValueStringBuilderImpl { -public: - UFormattedNumberData() : FormattedValueStringBuilderImpl(kUndefinedField) {} - virtual ~UFormattedNumberData(); - - // The formatted quantity. - DecimalQuantity quantity; - - // The output unit for the formatted quantity. - // TODO(units,hugovdm): populate this correctly for the general case - it's - // currently only implemented for the .usage() use case. - MeasureUnit outputUnit; - - // The gender of the formatted output. - const char *gender = ""; -}; - - -} // namespace impl -} // namespace number -U_NAMESPACE_END - -#endif //__SOURCE_NUMBER_UTYPES_H__ -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 "formatted_string_builder.h" +#include "formattedval_impl.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + + +/** Helper function used in upluralrules.cpp */ +const DecimalQuantity* validateUFormattedNumberToDecimalQuantity( + const UFormattedNumber* uresult, UErrorCode& status); + + +/** + * Struct for data used by FormattedNumber. + * + * This struct is held internally by the C++ version FormattedNumber since the member types are not + * declared in the public header file. + * + * Exported as U_I18N_API for tests + */ +class U_I18N_API UFormattedNumberData : public FormattedValueStringBuilderImpl { +public: + UFormattedNumberData() : FormattedValueStringBuilderImpl(kUndefinedField) {} + virtual ~UFormattedNumberData(); + + UFormattedNumberData(UFormattedNumberData&&) = default; + UFormattedNumberData& operator=(UFormattedNumberData&&) = default; + + // The formatted quantity. + DecimalQuantity quantity; + + // The output unit for the formatted quantity. + // TODO(units,hugovdm): populate this correctly for the general case - it's + // currently only implemented for the .usage() use case. + MeasureUnit outputUnit; + + // The gender of the formatted output. + const char *gender = ""; +}; + + +} // 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 9e841da74a44fa..fa55ff299dfe8f 100644 --- a/deps/icu-small/source/i18n/numfmt.cpp +++ b/deps/icu-small/source/i18n/numfmt.cpp @@ -1,1534 +1,1536 @@ -// © 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 NUMFMT.CPP -* -* Modification History: -* -* Date Name Description -* 02/19/97 aliu Converted from java. -* 03/18/97 clhuang Implemented with C++ APIs. -* 04/17/97 aliu Enlarged MAX_INTEGER_DIGITS to fully accommodate the -* largest double, by default. -* Changed DigitCount to int per code review. -* 07/20/98 stephen Changed operator== to check for grouping -* Changed setMaxIntegerDigits per Java implementation. -* Changed setMinIntegerDigits per Java implementation. -* Changed setMinFractionDigits per Java implementation. -* Changed setMaxFractionDigits per Java implementation. -******************************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/numfmt.h" -#include "unicode/locid.h" -#include "unicode/dcfmtsym.h" -#include "unicode/decimfmt.h" -#include "unicode/ustring.h" -#include "unicode/ucurr.h" -#include "unicode/curramt.h" -#include "unicode/numsys.h" -#include "unicode/rbnf.h" -#include "unicode/localpointer.h" -#include "unicode/udisplaycontext.h" -#include "charstr.h" -#include "winnmfmt.h" -#include "uresimp.h" -#include "uhash.h" -#include "cmemory.h" -#include "servloc.h" -#include "ucln_in.h" -#include "cstring.h" -#include "putilimp.h" -#include "uassert.h" -#include "umutex.h" -#include "mutex.h" -#include -#include "sharednumberformat.h" -#include "unifiedcache.h" -#include "number_decimalquantity.h" -#include "number_utils.h" - -//#define FMT_DEBUG - -#ifdef FMT_DEBUG -#include -static inline void debugout(UnicodeString s) { - char buf[2000]; - s.extract((int32_t) 0, s.length(), buf); - printf("%s", buf); -} -#define debug(x) printf("%s", x); -#else -#define debugout(x) -#define debug(x) -#endif - -// If no number pattern can be located for a locale, this is the last -// resort. The patterns are same as the ones in root locale. -static const UChar gLastResortDecimalPat[] = { - 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0 /* "#,##0.###" */ -}; -static const UChar gLastResortCurrencyPat[] = { - 0xA4, 0xA0, 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x30, 0x30, 0 /* "\u00A4\u00A0#,##0.00" */ -}; -static const UChar gLastResortPercentPat[] = { - 0x23, 0x2C, 0x23, 0x23, 0x30, 0x25, 0 /* "#,##0%" */ -}; -static const UChar gLastResortScientificPat[] = { - 0x23, 0x45, 0x30, 0 /* "#E0" */ -}; -static const UChar gLastResortIsoCurrencyPat[] = { - 0xA4, 0xA4, 0xA0, 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x30, 0x30, 0 /* "\u00A4\u00A4\u00A0#,##0.00" */ -}; -static const UChar gLastResortPluralCurrencyPat[] = { - 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0x20, 0xA4, 0xA4, 0xA4, 0 /* "#,##0.### \u00A4\u00A4\u00A4*/ -}; -static const UChar gLastResortAccountingCurrencyPat[] = { - 0xA4, 0xA0, 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x30, 0x30, 0 /* "\u00A4\u00A0#,##0.00" */ -}; - -static const UChar gSingleCurrencySign[] = {0xA4, 0}; -static const UChar gDoubleCurrencySign[] = {0xA4, 0xA4, 0}; - -static const UChar gSlash = 0x2f; - -// If the maximum base 10 exponent were 4, then the largest number would -// be 99,999 which has 5 digits. -// On IEEE754 systems gMaxIntegerDigits is 308 + possible denormalized 15 digits + rounding digit -// With big decimal, the max exponent is 999,999,999 and the max number of digits is the same, 999,999,999 -const int32_t icu::NumberFormat::gDefaultMaxIntegerDigits = 2000000000; -const int32_t icu::NumberFormat::gDefaultMinIntegerDigits = 127; - -static const UChar * const gLastResortNumberPatterns[UNUM_FORMAT_STYLE_COUNT] = { - NULL, // UNUM_PATTERN_DECIMAL - gLastResortDecimalPat, // UNUM_DECIMAL - gLastResortCurrencyPat, // UNUM_CURRENCY - gLastResortPercentPat, // UNUM_PERCENT - gLastResortScientificPat, // UNUM_SCIENTIFIC - NULL, // UNUM_SPELLOUT - NULL, // UNUM_ORDINAL - NULL, // UNUM_DURATION - gLastResortDecimalPat, // UNUM_NUMBERING_SYSTEM - NULL, // UNUM_PATTERN_RULEBASED - gLastResortIsoCurrencyPat, // UNUM_CURRENCY_ISO - gLastResortPluralCurrencyPat, // UNUM_CURRENCY_PLURAL - gLastResortAccountingCurrencyPat, // UNUM_CURRENCY_ACCOUNTING - gLastResortCurrencyPat, // UNUM_CASH_CURRENCY - NULL, // UNUM_DECIMAL_COMPACT_SHORT - NULL, // UNUM_DECIMAL_COMPACT_LONG - gLastResortCurrencyPat, // UNUM_CURRENCY_STANDARD -}; - -// Keys used for accessing resource bundles - -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. - 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 -static UHashtable * NumberingSystem_cache = NULL; -static icu::UInitOnce gNSCacheInitOnce {}; - -#if !UCONFIG_NO_SERVICE -static icu::ICULocaleService* gService = NULL; -static icu::UInitOnce gServiceInitOnce {}; -#endif - -/** - * Release all static memory held by Number Format. - */ -U_CDECL_BEGIN -static void U_CALLCONV -deleteNumberingSystem(void *obj) { - delete (icu::NumberingSystem *)obj; -} - -static UBool U_CALLCONV numfmt_cleanup(void) { -#if !UCONFIG_NO_SERVICE - gServiceInitOnce.reset(); - if (gService) { - delete gService; - gService = NULL; - } -#endif - gNSCacheInitOnce.reset(); - if (NumberingSystem_cache) { - // delete NumberingSystem_cache; - uhash_close(NumberingSystem_cache); - NumberingSystem_cache = NULL; - } - return true; -} -U_CDECL_END - -// ***************************************************************************** -// class NumberFormat -// ***************************************************************************** - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(NumberFormat) - -#if !UCONFIG_NO_SERVICE -// ------------------------------------- -// SimpleNumberFormatFactory implementation -NumberFormatFactory::~NumberFormatFactory() {} -SimpleNumberFormatFactory::SimpleNumberFormatFactory(const Locale& locale, UBool visible) - : _visible(visible) -{ - LocaleUtility::initNameFromLocale(locale, _id); -} - -SimpleNumberFormatFactory::~SimpleNumberFormatFactory() {} - -UBool SimpleNumberFormatFactory::visible(void) const { - return _visible; -} - -const UnicodeString * -SimpleNumberFormatFactory::getSupportedIDs(int32_t &count, UErrorCode& status) const -{ - if (U_SUCCESS(status)) { - count = 1; - return &_id; - } - count = 0; - return NULL; -} -#endif /* #if !UCONFIG_NO_SERVICE */ - -// ------------------------------------- -// default constructor -NumberFormat::NumberFormat() -: fGroupingUsed(true), - fMaxIntegerDigits(gDefaultMaxIntegerDigits), - fMinIntegerDigits(1), - fMaxFractionDigits(3), // invariant, >= minFractionDigits - fMinFractionDigits(0), - fParseIntegerOnly(false), - fLenient(false), - fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) -{ - fCurrency[0] = 0; -} - -// ------------------------------------- - -NumberFormat::~NumberFormat() -{ -} - -SharedNumberFormat::~SharedNumberFormat() { - delete ptr; -} - -// ------------------------------------- -// copy constructor - -NumberFormat::NumberFormat(const NumberFormat &source) -: Format(source) -{ - *this = source; -} - -// ------------------------------------- -// assignment operator - -NumberFormat& -NumberFormat::operator=(const NumberFormat& rhs) -{ - if (this != &rhs) - { - Format::operator=(rhs); - fGroupingUsed = rhs.fGroupingUsed; - fMaxIntegerDigits = rhs.fMaxIntegerDigits; - fMinIntegerDigits = rhs.fMinIntegerDigits; - fMaxFractionDigits = rhs.fMaxFractionDigits; - fMinFractionDigits = rhs.fMinFractionDigits; - fParseIntegerOnly = rhs.fParseIntegerOnly; - u_strncpy(fCurrency, rhs.fCurrency, 3); - fCurrency[3] = 0; - fLenient = rhs.fLenient; - fCapitalizationContext = rhs.fCapitalizationContext; - } - return *this; -} - -// ------------------------------------- - -bool -NumberFormat::operator==(const Format& that) const -{ - // Format::operator== guarantees this cast is safe - NumberFormat* other = (NumberFormat*)&that; - -#ifdef FMT_DEBUG - // This code makes it easy to determine why two format objects that should - // be equal aren't. - UBool first = true; - if (!Format::operator==(that)) { - if (first) { printf("[ "); first = false; } else { printf(", "); } - debug("Format::!="); - } - if (!(fMaxIntegerDigits == other->fMaxIntegerDigits && - fMinIntegerDigits == other->fMinIntegerDigits)) { - if (first) { printf("[ "); first = false; } else { printf(", "); } - debug("Integer digits !="); - } - if (!(fMaxFractionDigits == other->fMaxFractionDigits && - fMinFractionDigits == other->fMinFractionDigits)) { - if (first) { printf("[ "); first = false; } else { printf(", "); } - debug("Fraction digits !="); - } - if (!(fGroupingUsed == other->fGroupingUsed)) { - if (first) { printf("[ "); first = false; } else { printf(", "); } - debug("fGroupingUsed != "); - } - if (!(fParseIntegerOnly == other->fParseIntegerOnly)) { - if (first) { printf("[ "); first = false; } else { printf(", "); } - debug("fParseIntegerOnly != "); - } - if (!(u_strcmp(fCurrency, other->fCurrency) == 0)) { - if (first) { printf("[ "); first = false; } else { printf(", "); } - debug("fCurrency !="); - } - if (!(fLenient == other->fLenient)) { - if (first) { printf("[ "); first = false; } else { printf(", "); } - debug("fLenient != "); - } - if (!(fCapitalizationContext == other->fCapitalizationContext)) { - if (first) { printf("[ "); first = false; } else { printf(", "); } - debug("fCapitalizationContext != "); - } - if (!first) { printf(" ]"); } -#endif - - return ((this == &that) || - ((Format::operator==(that) && - fMaxIntegerDigits == other->fMaxIntegerDigits && - fMinIntegerDigits == other->fMinIntegerDigits && - fMaxFractionDigits == other->fMaxFractionDigits && - fMinFractionDigits == other->fMinFractionDigits && - fGroupingUsed == other->fGroupingUsed && - fParseIntegerOnly == other->fParseIntegerOnly && - u_strcmp(fCurrency, other->fCurrency) == 0 && - fLenient == other->fLenient && - fCapitalizationContext == other->fCapitalizationContext))); -} - -// ------------------------------------- -// Default implementation sets unsupported error; subclasses should -// override. - -UnicodeString& -NumberFormat::format(double /* unused number */, - UnicodeString& toAppendTo, - FieldPositionIterator* /* unused posIter */, - UErrorCode& status) const -{ - if (!U_FAILURE(status)) { - status = U_UNSUPPORTED_ERROR; - } - return toAppendTo; -} - -// ------------------------------------- -// Default implementation sets unsupported error; subclasses should -// override. - -UnicodeString& -NumberFormat::format(int32_t /* unused number */, - UnicodeString& toAppendTo, - FieldPositionIterator* /* unused posIter */, - UErrorCode& status) const -{ - if (!U_FAILURE(status)) { - status = U_UNSUPPORTED_ERROR; - } - return toAppendTo; -} - -// ------------------------------------- -// Default implementation sets unsupported error; subclasses should -// override. - -UnicodeString& -NumberFormat::format(int64_t /* unused number */, - UnicodeString& toAppendTo, - FieldPositionIterator* /* unused posIter */, - UErrorCode& status) const -{ - if (!U_FAILURE(status)) { - status = U_UNSUPPORTED_ERROR; - } - return toAppendTo; -} - -// ------------------------------------------ -// These functions add the status code, just fall back to the non-status versions -UnicodeString& -NumberFormat::format(double number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - if(U_SUCCESS(status)) { - return format(number,appendTo,pos); - } else { - return appendTo; - } -} - -UnicodeString& -NumberFormat::format(int32_t number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - if(U_SUCCESS(status)) { - return format(number,appendTo,pos); - } else { - return appendTo; - } -} - -UnicodeString& -NumberFormat::format(int64_t number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - if(U_SUCCESS(status)) { - return format(number,appendTo,pos); - } else { - return appendTo; - } -} - - - -// ------------------------------------- -// Decimal Number format() default implementation -// Subclasses do not normally override this function, but rather the DigitList -// formatting functions.. -// The expected call chain from here is -// this function -> -// NumberFormat::format(Formattable -> -// DecimalFormat::format(DigitList -// -// Or, for subclasses of Formattable that do not know about DigitList, -// this Function -> -// NumberFormat::format(Formattable -> -// NumberFormat::format(DigitList -> -// XXXFormat::format(double - -UnicodeString& -NumberFormat::format(StringPiece decimalNum, - UnicodeString& toAppendTo, - FieldPositionIterator* fpi, - UErrorCode& status) const -{ - Formattable f; - f.setDecimalNumber(decimalNum, status); - format(f, toAppendTo, fpi, status); - return toAppendTo; -} - -/** - * -// Formats the number object and save the format -// result in the toAppendTo string buffer. - -// utility to save/restore state, used in two overloads -// of format(const Formattable&...) below. -* -* Old purpose of ArgExtractor was to avoid const. Not thread safe! -* -* keeping it around as a shim. -*/ -class ArgExtractor { - const Formattable* num; - UChar save[4]; - UBool fWasCurrency; - - public: - ArgExtractor(const NumberFormat& nf, const Formattable& obj, UErrorCode& status); - ~ArgExtractor(); - - const Formattable* number(void) const; - const UChar *iso(void) const; - UBool wasCurrency(void) const; -}; - -inline const Formattable* -ArgExtractor::number(void) const { - return num; -} - -inline UBool -ArgExtractor::wasCurrency(void) const { - return fWasCurrency; -} - -inline const UChar * -ArgExtractor::iso(void) const { - return save; -} - -ArgExtractor::ArgExtractor(const NumberFormat& /*nf*/, const Formattable& obj, UErrorCode& /*status*/) - : num(&obj), fWasCurrency(false) { - - const UObject* o = obj.getObject(); // most commonly o==NULL - const CurrencyAmount* amt; - if (o != NULL && (amt = dynamic_cast(o)) != NULL) { - // getISOCurrency() returns a pointer to internal storage, so we - // copy it to retain it across the call to setCurrency(). - //const UChar* curr = amt->getISOCurrency(); - u_strcpy(save, amt->getISOCurrency()); - num = &amt->getNumber(); - fWasCurrency=true; - } else { - save[0]=0; - } -} - -ArgExtractor::~ArgExtractor() { -} - -UnicodeString& NumberFormat::format(const number::impl::DecimalQuantity &number, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const { - // DecimalFormat overrides this function, and handles DigitList based big decimals. - // Other subclasses (ChoiceFormat) do not (yet) handle DigitLists, - // so this default implementation falls back to formatting decimal numbers as doubles. - if (U_FAILURE(status)) { - return appendTo; - } - double dnum = number.toDouble(); - format(dnum, appendTo, posIter, status); - return appendTo; -} - - - -UnicodeString& -NumberFormat::format(const number::impl::DecimalQuantity &number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - // DecimalFormat overrides this function, and handles DigitList based big decimals. - // Other subclasses (ChoiceFormat) do not (yet) handle DigitLists, - // so this default implementation falls back to formatting decimal numbers as doubles. - if (U_FAILURE(status)) { - return appendTo; - } - double dnum = number.toDouble(); - format(dnum, appendTo, pos, status); - return appendTo; -} - -UnicodeString& -NumberFormat::format(const Formattable& obj, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode& status) const -{ - if (U_FAILURE(status)) return appendTo; - - ArgExtractor arg(*this, obj, status); - const Formattable *n = arg.number(); - const UChar *iso = arg.iso(); - - if(arg.wasCurrency() && u_strcmp(iso, getCurrency())) { - // trying to format a different currency. - // Right now, we clone. - LocalPointer cloneFmt(this->clone()); - cloneFmt->setCurrency(iso, status); - // next line should NOT recurse, because n is numeric whereas obj was a wrapper around currency amount. - return cloneFmt->format(*n, appendTo, pos, status); - } - - 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. - // - // The default implementation for formatting a DigitList converts it - // to a double, and formats that, allowing formatting classes that don't - // know about DigitList to continue to operate as they had. - // - // DecimalFormat overrides the DigitList formatting functions. - format(*n->getDecimalQuantity(), appendTo, pos, status); - } else { - switch (n->getType()) { - case Formattable::kDouble: - format(n->getDouble(), appendTo, pos, status); - break; - case Formattable::kLong: - format(n->getLong(), appendTo, pos, status); - break; - case Formattable::kInt64: - format(n->getInt64(), appendTo, pos, status); - break; - default: - status = U_INVALID_FORMAT_ERROR; - break; - } - } - - return appendTo; -} - -// -------------------------------------x -// Formats the number object and save the format -// result in the toAppendTo string buffer. - -UnicodeString& -NumberFormat::format(const Formattable& obj, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const -{ - if (U_FAILURE(status)) return appendTo; - - ArgExtractor arg(*this, obj, status); - const Formattable *n = arg.number(); - const UChar *iso = arg.iso(); - - if(arg.wasCurrency() && u_strcmp(iso, getCurrency())) { - // trying to format a different currency. - // Right now, we clone. - LocalPointer cloneFmt(this->clone()); - cloneFmt->setCurrency(iso, status); - // next line should NOT recurse, because n is numeric whereas obj was a wrapper around currency amount. - return cloneFmt->format(*n, appendTo, posIter, status); - } - - if (n->isNumeric() && n->getDecimalQuantity() != NULL) { - // Decimal Number - format(*n->getDecimalQuantity(), appendTo, posIter, status); - } else { - switch (n->getType()) { - case Formattable::kDouble: - format(n->getDouble(), appendTo, posIter, status); - break; - case Formattable::kLong: - format(n->getLong(), appendTo, posIter, status); - break; - case Formattable::kInt64: - format(n->getInt64(), appendTo, posIter, status); - break; - default: - status = U_INVALID_FORMAT_ERROR; - break; - } - } - - return appendTo; -} - -// ------------------------------------- - -UnicodeString& -NumberFormat::format(int64_t number, - UnicodeString& appendTo, - FieldPosition& pos) const -{ - // default so we don't introduce a new abstract method - return format((int32_t)number, appendTo, pos); -} - -// ------------------------------------- -// Parses the string and save the result object as well -// as the final parsed position. - -void -NumberFormat::parseObject(const UnicodeString& source, - Formattable& result, - ParsePosition& parse_pos) const -{ - parse(source, result, parse_pos); -} - -// ------------------------------------- -// Formats a double number and save the result in a string. - -UnicodeString& -NumberFormat::format(double number, UnicodeString& appendTo) const -{ - FieldPosition pos(FieldPosition::DONT_CARE); - return format(number, appendTo, pos); -} - -// ------------------------------------- -// Formats a long number and save the result in a string. - -UnicodeString& -NumberFormat::format(int32_t number, UnicodeString& appendTo) const -{ - FieldPosition pos(FieldPosition::DONT_CARE); - return format(number, appendTo, pos); -} - -// ------------------------------------- -// Formats a long number and save the result in a string. - -UnicodeString& -NumberFormat::format(int64_t number, UnicodeString& appendTo) const -{ - FieldPosition pos(FieldPosition::DONT_CARE); - return format(number, appendTo, pos); -} - -// ------------------------------------- -// Parses the text and save the result object. If the returned -// parse position is 0, that means the parsing failed, the status -// code needs to be set to failure. Ignores the returned parse -// position, otherwise. - -void -NumberFormat::parse(const UnicodeString& text, - Formattable& result, - UErrorCode& status) const -{ - if (U_FAILURE(status)) return; - - ParsePosition parsePosition(0); - parse(text, result, parsePosition); - if (parsePosition.getIndex() == 0) { - status = U_INVALID_FORMAT_ERROR; - } -} - -CurrencyAmount* NumberFormat::parseCurrency(const UnicodeString& text, - ParsePosition& pos) const { - // Default implementation only -- subclasses should override - Formattable parseResult; - int32_t start = pos.getIndex(); - parse(text, parseResult, pos); - if (pos.getIndex() != start) { - UChar curr[4]; - UErrorCode ec = U_ZERO_ERROR; - getEffectiveCurrency(curr, ec); - if (U_SUCCESS(ec)) { - LocalPointer currAmt(new CurrencyAmount(parseResult, curr, ec), ec); - if (U_FAILURE(ec)) { - pos.setIndex(start); // indicate failure - } else { - return currAmt.orphan(); - } - } - } - return NULL; -} - -// ------------------------------------- -// Sets to only parse integers. - -void -NumberFormat::setParseIntegerOnly(UBool value) -{ - fParseIntegerOnly = value; -} - -// ------------------------------------- -// Sets whether lenient parse is enabled. - -void -NumberFormat::setLenient(UBool enable) -{ - fLenient = enable; -} - -// ------------------------------------- -// Create a number style NumberFormat instance with the default locale. - -NumberFormat* U_EXPORT2 -NumberFormat::createInstance(UErrorCode& status) -{ - return createInstance(Locale::getDefault(), UNUM_DECIMAL, status); -} - -// ------------------------------------- -// Create a number style NumberFormat instance with the inLocale locale. - -NumberFormat* U_EXPORT2 -NumberFormat::createInstance(const Locale& inLocale, UErrorCode& status) -{ - return createInstance(inLocale, UNUM_DECIMAL, status); -} - -// ------------------------------------- -// Create a currency style NumberFormat instance with the default locale. - -NumberFormat* U_EXPORT2 -NumberFormat::createCurrencyInstance(UErrorCode& status) -{ - return createCurrencyInstance(Locale::getDefault(), status); -} - -// ------------------------------------- -// Create a currency style NumberFormat instance with the inLocale locale. - -NumberFormat* U_EXPORT2 -NumberFormat::createCurrencyInstance(const Locale& inLocale, UErrorCode& status) -{ - return createInstance(inLocale, UNUM_CURRENCY, status); -} - -// ------------------------------------- -// Create a percent style NumberFormat instance with the default locale. - -NumberFormat* U_EXPORT2 -NumberFormat::createPercentInstance(UErrorCode& status) -{ - return createInstance(Locale::getDefault(), UNUM_PERCENT, status); -} - -// ------------------------------------- -// Create a percent style NumberFormat instance with the inLocale locale. - -NumberFormat* U_EXPORT2 -NumberFormat::createPercentInstance(const Locale& inLocale, UErrorCode& status) -{ - return createInstance(inLocale, UNUM_PERCENT, status); -} - -// ------------------------------------- -// Create a scientific style NumberFormat instance with the default locale. - -NumberFormat* U_EXPORT2 -NumberFormat::createScientificInstance(UErrorCode& status) -{ - return createInstance(Locale::getDefault(), UNUM_SCIENTIFIC, status); -} - -// ------------------------------------- -// Create a scientific style NumberFormat instance with the inLocale locale. - -NumberFormat* U_EXPORT2 -NumberFormat::createScientificInstance(const Locale& inLocale, UErrorCode& status) -{ - return createInstance(inLocale, UNUM_SCIENTIFIC, status); -} - -// ------------------------------------- - -const Locale* U_EXPORT2 -NumberFormat::getAvailableLocales(int32_t& count) -{ - return Locale::getAvailableLocales(count); -} - -// ------------------------------------------ -// -// Registration -// -//------------------------------------------- - -#if !UCONFIG_NO_SERVICE - -// ------------------------------------- - -class ICUNumberFormatFactory : public ICUResourceBundleFactory { -public: - virtual ~ICUNumberFormatFactory(); -protected: - virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* /* service */, UErrorCode& status) const override { - return NumberFormat::makeInstance(loc, (UNumberFormatStyle)kind, status); - } -}; - -ICUNumberFormatFactory::~ICUNumberFormatFactory() {} - -// ------------------------------------- - -class NFFactory : public LocaleKeyFactory { -private: - NumberFormatFactory* _delegate; - Hashtable* _ids; - -public: - NFFactory(NumberFormatFactory* delegate) - : LocaleKeyFactory(delegate->visible() ? VISIBLE : INVISIBLE) - , _delegate(delegate) - , _ids(NULL) - { - } - - virtual ~NFFactory(); - - virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const override - { - if (handlesKey(key, status)) { - const LocaleKey& lkey = (const LocaleKey&)key; - Locale loc; - lkey.canonicalLocale(loc); - int32_t kind = lkey.kind(); - - UObject* result = _delegate->createFormat(loc, (UNumberFormatStyle)kind); - if (result == NULL) { - result = service->getKey((ICUServiceKey&)key /* cast away const */, NULL, this, status); - } - return result; - } - return NULL; - } - -protected: - /** - * Return the set of ids that this factory supports (visible or - * otherwise). This can be called often and might need to be - * cached if it is expensive to create. - */ - virtual const Hashtable* getSupportedIDs(UErrorCode& status) const override - { - if (U_SUCCESS(status)) { - if (!_ids) { - int32_t count = 0; - const UnicodeString * const idlist = _delegate->getSupportedIDs(count, status); - ((NFFactory*)this)->_ids = new Hashtable(status); /* cast away const */ - if (_ids) { - for (int i = 0; i < count; ++i) { - _ids->put(idlist[i], (void*)this, status); - } - } - } - return _ids; - } - return NULL; - } -}; - -NFFactory::~NFFactory() -{ - delete _delegate; - delete _ids; -} - -class ICUNumberFormatService : public ICULocaleService { -public: - ICUNumberFormatService() - : ICULocaleService(UNICODE_STRING_SIMPLE("Number Format")) - { - UErrorCode status = U_ZERO_ERROR; - registerFactory(new ICUNumberFormatFactory(), status); - } - - virtual ~ICUNumberFormatService(); - - virtual UObject* cloneInstance(UObject* instance) const override { - return ((NumberFormat*)instance)->clone(); - } - - virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /* actualID */, UErrorCode& status) const override { - LocaleKey& lkey = (LocaleKey&)key; - int32_t kind = lkey.kind(); - Locale loc; - lkey.currentLocale(loc); - return NumberFormat::makeInstance(loc, (UNumberFormatStyle)kind, status); - } - - virtual UBool isDefault() const override { - return countFactories() == 1; - } -}; - -ICUNumberFormatService::~ICUNumberFormatService() {} - -// ------------------------------------- - -static void U_CALLCONV initNumberFormatService() { - U_ASSERT(gService == NULL); - ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); - gService = new ICUNumberFormatService(); -} - -static ICULocaleService* -getNumberFormatService(void) -{ - umtx_initOnce(gServiceInitOnce, &initNumberFormatService); - return gService; -} - -static UBool haveService() { - return !gServiceInitOnce.isReset() && (getNumberFormatService() != NULL); -} - -// ------------------------------------- - -URegistryKey U_EXPORT2 -NumberFormat::registerFactory(NumberFormatFactory* toAdopt, UErrorCode& status) -{ - if (U_FAILURE(status)) { - delete toAdopt; - return nullptr; - } - ICULocaleService *service = getNumberFormatService(); - if (service) { - NFFactory *tempnnf = new NFFactory(toAdopt); - if (tempnnf != NULL) { - return service->registerFactory(tempnnf, status); - } - } - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; -} - -// ------------------------------------- - -UBool U_EXPORT2 -NumberFormat::unregister(URegistryKey key, UErrorCode& status) -{ - if (U_FAILURE(status)) { - return false; - } - if (haveService()) { - return gService->unregister(key, status); - } else { - status = U_ILLEGAL_ARGUMENT_ERROR; - return false; - } -} - -// ------------------------------------- -StringEnumeration* U_EXPORT2 -NumberFormat::getAvailableLocales(void) -{ - ICULocaleService *service = getNumberFormatService(); - if (service) { - return service->getAvailableLocales(); - } - return NULL; // no way to return error condition -} -#endif /* UCONFIG_NO_SERVICE */ -// ------------------------------------- - -enum { kKeyValueLenMax = 32 }; - -NumberFormat* -NumberFormat::internalCreateInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { - if (kind == UNUM_CURRENCY) { - char cfKeyValue[kKeyValueLenMax] = {0}; - UErrorCode kvStatus = U_ZERO_ERROR; - int32_t kLen = loc.getKeywordValue("cf", cfKeyValue, kKeyValueLenMax, kvStatus); - if (U_SUCCESS(kvStatus) && kLen > 0 && uprv_strcmp(cfKeyValue,"account")==0) { - kind = UNUM_CURRENCY_ACCOUNTING; - } - } -#if !UCONFIG_NO_SERVICE - if (haveService()) { - return (NumberFormat*)gService->get(loc, kind, status); - } -#endif - return makeInstance(loc, kind, status); -} - -NumberFormat* U_EXPORT2 -NumberFormat::createInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { - if (kind != UNUM_DECIMAL) { - return internalCreateInstance(loc, kind, status); - } - const SharedNumberFormat *shared = createSharedInstance(loc, kind, status); - if (U_FAILURE(status)) { - return NULL; - } - NumberFormat *result = (*shared)->clone(); - shared->removeRef(); - if (result == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return result; -} - - -// ------------------------------------- -// Checks if the thousand/10 thousand grouping is used in the -// NumberFormat instance. - -UBool -NumberFormat::isGroupingUsed() const -{ - return fGroupingUsed; -} - -// ------------------------------------- -// Sets to use the thousand/10 thousand grouping in the -// NumberFormat instance. - -void -NumberFormat::setGroupingUsed(UBool newValue) -{ - fGroupingUsed = newValue; -} - -// ------------------------------------- -// Gets the maximum number of digits for the integral part for -// this NumberFormat instance. - -int32_t NumberFormat::getMaximumIntegerDigits() const -{ - return fMaxIntegerDigits; -} - -// ------------------------------------- -// Sets the maximum number of digits for the integral part for -// this NumberFormat instance. - -void -NumberFormat::setMaximumIntegerDigits(int32_t newValue) -{ - fMaxIntegerDigits = uprv_max(0, uprv_min(newValue, gDefaultMaxIntegerDigits)); - if(fMinIntegerDigits > fMaxIntegerDigits) - fMinIntegerDigits = fMaxIntegerDigits; -} - -// ------------------------------------- -// Gets the minimum number of digits for the integral part for -// this NumberFormat instance. - -int32_t -NumberFormat::getMinimumIntegerDigits() const -{ - return fMinIntegerDigits; -} - -// ------------------------------------- -// Sets the minimum number of digits for the integral part for -// this NumberFormat instance. - -void -NumberFormat::setMinimumIntegerDigits(int32_t newValue) -{ - fMinIntegerDigits = uprv_max(0, uprv_min(newValue, gDefaultMinIntegerDigits)); - if(fMinIntegerDigits > fMaxIntegerDigits) - fMaxIntegerDigits = fMinIntegerDigits; -} - -// ------------------------------------- -// Gets the maximum number of digits for the fractional part for -// this NumberFormat instance. - -int32_t -NumberFormat::getMaximumFractionDigits() const -{ - return fMaxFractionDigits; -} - -// ------------------------------------- -// Sets the maximum number of digits for the fractional part for -// this NumberFormat instance. - -void -NumberFormat::setMaximumFractionDigits(int32_t newValue) -{ - fMaxFractionDigits = uprv_max(0, uprv_min(newValue, gDefaultMaxIntegerDigits)); - if(fMaxFractionDigits < fMinFractionDigits) - fMinFractionDigits = fMaxFractionDigits; -} - -// ------------------------------------- -// Gets the minimum number of digits for the fractional part for -// this NumberFormat instance. - -int32_t -NumberFormat::getMinimumFractionDigits() const -{ - return fMinFractionDigits; -} - -// ------------------------------------- -// Sets the minimum number of digits for the fractional part for -// this NumberFormat instance. - -void -NumberFormat::setMinimumFractionDigits(int32_t newValue) -{ - fMinFractionDigits = uprv_max(0, uprv_min(newValue, gDefaultMinIntegerDigits)); - if (fMaxFractionDigits < fMinFractionDigits) - fMaxFractionDigits = fMinFractionDigits; -} - -// ------------------------------------- - -void NumberFormat::setCurrency(const UChar* theCurrency, UErrorCode& ec) { - if (U_FAILURE(ec)) { - return; - } - if (theCurrency) { - u_strncpy(fCurrency, theCurrency, 3); - fCurrency[3] = 0; - } else { - fCurrency[0] = 0; - } -} - -const char16_t* NumberFormat::getCurrency() const { - return fCurrency; -} - -void NumberFormat::getEffectiveCurrency(UChar* result, UErrorCode& ec) const { - const UChar* c = getCurrency(); - if (*c != 0) { - u_strncpy(result, c, 3); - result[3] = 0; - } else { - const char* loc = getLocaleID(ULOC_VALID_LOCALE, ec); - if (loc == NULL) { - loc = uloc_getDefault(); - } - ucurr_forLocale(loc, result, 4, &ec); - } -} - -//---------------------------------------------------------------------- - - -void NumberFormat::setContext(UDisplayContext value, UErrorCode& status) -{ - if (U_FAILURE(status)) - return; - if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) { - fCapitalizationContext = value; - } else { - status = U_ILLEGAL_ARGUMENT_ERROR; - } -} - - -UDisplayContext NumberFormat::getContext(UDisplayContextType type, UErrorCode& status) const -{ - if (U_FAILURE(status)) - return (UDisplayContext)0; - if (type != UDISPCTX_TYPE_CAPITALIZATION) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return (UDisplayContext)0; - } - return fCapitalizationContext; -} - - -// ------------------------------------- -// Creates the NumberFormat instance of the specified style (number, currency, -// or percent) for the desired locale. - -static void U_CALLCONV nscacheInit() { - U_ASSERT(NumberingSystem_cache == NULL); - ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); - UErrorCode status = U_ZERO_ERROR; - NumberingSystem_cache = uhash_open(uhash_hashLong, - uhash_compareLong, - NULL, - &status); - if (U_FAILURE(status)) { - // Number Format code will run with no cache if creation fails. - NumberingSystem_cache = NULL; - return; - } - uhash_setValueDeleter(NumberingSystem_cache, deleteNumberingSystem); -} - -template<> U_I18N_API -const SharedNumberFormat *LocaleCacheKey::createObject( - const void * /*unused*/, UErrorCode &status) const { - const char *localeId = fLoc.getName(); - NumberFormat *nf = NumberFormat::internalCreateInstance( - localeId, UNUM_DECIMAL, status); - if (U_FAILURE(status)) { - return NULL; - } - SharedNumberFormat *result = new SharedNumberFormat(nf); - if (result == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - delete nf; - return NULL; - } - result->addRef(); - return result; -} - -const SharedNumberFormat* U_EXPORT2 -NumberFormat::createSharedInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { - if (U_FAILURE(status)) { - return NULL; - } - if (kind != UNUM_DECIMAL) { - status = U_UNSUPPORTED_ERROR; - return NULL; - } - const SharedNumberFormat *result = NULL; - UnifiedCache::getByLocale(loc, result, status); - return result; -} - -UBool -NumberFormat::isStyleSupported(UNumberFormatStyle style) { - return gLastResortNumberPatterns[style] != NULL; -} - -NumberFormat* -NumberFormat::makeInstance(const Locale& desiredLocale, - UNumberFormatStyle style, - UErrorCode& status) { - return makeInstance(desiredLocale, style, false, status); -} - -NumberFormat* -NumberFormat::makeInstance(const Locale& desiredLocale, - UNumberFormatStyle style, - UBool mustBeDecimalFormat, - UErrorCode& status) { - if (U_FAILURE(status)) return NULL; - - if (style < 0 || style >= UNUM_FORMAT_STYLE_COUNT) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - // For the purposes of general number formatting, UNUM_NUMBERING_SYSTEM should behave the same - // was as UNUM_DECIMAL. In both cases, you get either a DecimalFormat or a RuleBasedNumberFormat - // depending on the locale's numbering system (either the default one for the locale or a specific - // one specified by using the "@numbers=" or "-u-nu-" parameter in the locale ID. - if (style == UNUM_NUMBERING_SYSTEM) { - style = UNUM_DECIMAL; - } - - // Some styles are not supported. This is a result of merging - // the @draft ICU 4.2 NumberFormat::EStyles into the long-existing UNumberFormatStyle. - // Ticket #8503 is for reviewing/fixing/merging the two relevant implementations: - // this one and unum_open(). - // The UNUM_PATTERN_ styles are not supported here - // because this method does not take a pattern string. - if (!isStyleSupported(style)) { - status = U_UNSUPPORTED_ERROR; - return NULL; - } - -#if U_PLATFORM_USES_ONLY_WIN32_API - if (!mustBeDecimalFormat) { - char buffer[8]; - int32_t count = desiredLocale.getKeywordValue("compat", buffer, sizeof(buffer), status); - - // if the locale has "@compat=host", create a host-specific NumberFormat - if (U_SUCCESS(status) && count > 0 && uprv_strcmp(buffer, "host") == 0) { - UBool curr = true; - - switch (style) { - case UNUM_DECIMAL: - curr = false; - // fall-through - U_FALLTHROUGH; - - case UNUM_CURRENCY: - case UNUM_CURRENCY_ISO: // do not support plural formatting here - case UNUM_CURRENCY_PLURAL: - case UNUM_CURRENCY_ACCOUNTING: - case UNUM_CASH_CURRENCY: - case UNUM_CURRENCY_STANDARD: - { - LocalPointer f(new Win32NumberFormat(desiredLocale, curr, status), status); - if (U_SUCCESS(status)) { - return f.orphan(); - } - } - break; - default: - break; - } - } - } -#endif - // Use numbering system cache hashtable - umtx_initOnce(gNSCacheInitOnce, &nscacheInit); - - // Get cached numbering system - LocalPointer ownedNs; - NumberingSystem *ns = NULL; - if (NumberingSystem_cache != NULL) { - // TODO: Bad hash key usage, see ticket #8504. - int32_t hashKey = desiredLocale.hashCode(); - - static UMutex nscacheMutex; - Mutex lock(&nscacheMutex); - ns = (NumberingSystem *)uhash_iget(NumberingSystem_cache, hashKey); - if (ns == NULL) { - ns = NumberingSystem::createInstance(desiredLocale,status); - uhash_iput(NumberingSystem_cache, hashKey, (void*)ns, &status); - } - } else { - ownedNs.adoptInstead(NumberingSystem::createInstance(desiredLocale,status)); - ns = ownedNs.getAlias(); - } - - // check results of getting a numbering system - if (U_FAILURE(status)) { - return NULL; - } - - if (mustBeDecimalFormat && ns->isAlgorithmic()) { - status = U_UNSUPPORTED_ERROR; - return NULL; - } - - LocalPointer symbolsToAdopt; - UnicodeString pattern; - LocalUResourceBundlePointer ownedResource(ures_open(NULL, desiredLocale.getName(), &status)); - if (U_FAILURE(status)) { - return NULL; - } - else { - // Loads the decimal symbols of the desired locale. - symbolsToAdopt.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(desiredLocale, status), status); - if (U_FAILURE(status)) { - return NULL; - } - - // 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; - } - if(style==UNUM_CURRENCY || style == UNUM_CURRENCY_ISO || style == UNUM_CURRENCY_ACCOUNTING - || style == UNUM_CASH_CURRENCY || style == UNUM_CURRENCY_STANDARD){ - const UChar* currPattern = symbolsToAdopt->getCurrencyPattern(); - if(currPattern!=NULL){ - pattern.setTo(currPattern, u_strlen(currPattern)); - } - } - - LocalPointer f; - if (ns->isAlgorithmic()) { - UnicodeString nsDesc; - UnicodeString nsRuleSetGroup; - UnicodeString nsRuleSetName; - Locale nsLoc; - URBNFRuleSetTag desiredRulesType = URBNF_NUMBERING_SYSTEM; - - nsDesc.setTo(ns->getDescription()); - int32_t firstSlash = nsDesc.indexOf(gSlash); - int32_t lastSlash = nsDesc.lastIndexOf(gSlash); - if ( lastSlash > firstSlash ) { - CharString nsLocID; - - nsLocID.appendInvariantChars(nsDesc.tempSubString(0, firstSlash), status); - nsRuleSetGroup.setTo(nsDesc,firstSlash+1,lastSlash-firstSlash-1); - nsRuleSetName.setTo(nsDesc,lastSlash+1); - - nsLoc = Locale::createFromName(nsLocID.data()); - - UnicodeString SpelloutRules = UNICODE_STRING_SIMPLE("SpelloutRules"); - if ( nsRuleSetGroup.compare(SpelloutRules) == 0 ) { - desiredRulesType = URBNF_SPELLOUT; - } - } else { - nsLoc = desiredLocale; - nsRuleSetName.setTo(nsDesc); - } - - RuleBasedNumberFormat *r = new RuleBasedNumberFormat(desiredRulesType,nsLoc,status); - if (r == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - r->setDefaultRuleSet(nsRuleSetName,status); - f.adoptInstead(r); - } else { - // replace single currency sign in the pattern with double currency sign - // if the style is UNUM_CURRENCY_ISO - if (style == UNUM_CURRENCY_ISO) { - pattern.findAndReplace(UnicodeString(true, gSingleCurrencySign, 1), - UnicodeString(true, gDoubleCurrencySign, 2)); - } - - // "new DecimalFormat()" does not adopt the symbols argument if its memory allocation fails. - // So we can't use adoptInsteadAndCheckErrorCode as we need to know if the 'new' failed. - DecimalFormatSymbols *syms = symbolsToAdopt.getAlias(); - LocalPointer df(new DecimalFormat(pattern, syms, style, status)); - - if (df.isValid()) { - // if the DecimalFormat object was successfully new'ed, then it will own symbolsToAdopt, even if the status is a failure. - symbolsToAdopt.orphan(); - } - else { - status = U_MEMORY_ALLOCATION_ERROR; - } - - if (U_FAILURE(status)) { - return nullptr; - } - - // if it is cash currency style, setCurrencyUsage with usage - if (style == UNUM_CASH_CURRENCY){ - df->setCurrencyUsage(UCURR_USAGE_CASH, &status); - } - - if (U_FAILURE(status)) { - return nullptr; - } - - f.adoptInstead(df.orphan()); - } - - f->setLocaleIDs(ures_getLocaleByType(ownedResource.getAlias(), ULOC_VALID_LOCALE, &status), - ures_getLocaleByType(ownedResource.getAlias(), ULOC_ACTUAL_LOCALE, &status)); - if (U_FAILURE(status)) { - return NULL; - } - return f.orphan(); -} - -/** - * Get the rounding mode. - * @return A rounding mode - */ -NumberFormat::ERoundingMode NumberFormat::getRoundingMode() const { - // Default value. ICU4J throws an exception and we can't change this API. - return NumberFormat::ERoundingMode::kRoundUnnecessary; -} - -/** - * Set the rounding mode. This has no effect unless the rounding - * increment is greater than zero. - * @param roundingMode A rounding mode - */ -void NumberFormat::setRoundingMode(NumberFormat::ERoundingMode /*roundingMode*/) { - // No-op ICU4J throws an exception, and we can't change this API. -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 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 NUMFMT.CPP +* +* Modification History: +* +* Date Name Description +* 02/19/97 aliu Converted from java. +* 03/18/97 clhuang Implemented with C++ APIs. +* 04/17/97 aliu Enlarged MAX_INTEGER_DIGITS to fully accommodate the +* largest double, by default. +* Changed DigitCount to int per code review. +* 07/20/98 stephen Changed operator== to check for grouping +* Changed setMaxIntegerDigits per Java implementation. +* Changed setMinIntegerDigits per Java implementation. +* Changed setMinFractionDigits per Java implementation. +* Changed setMaxFractionDigits per Java implementation. +******************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/numfmt.h" +#include "unicode/locid.h" +#include "unicode/dcfmtsym.h" +#include "unicode/decimfmt.h" +#include "unicode/ustring.h" +#include "unicode/ucurr.h" +#include "unicode/curramt.h" +#include "unicode/numsys.h" +#include "unicode/rbnf.h" +#include "unicode/localpointer.h" +#include "unicode/udisplaycontext.h" +#include "charstr.h" +#include "winnmfmt.h" +#include "uresimp.h" +#include "uhash.h" +#include "cmemory.h" +#include "servloc.h" +#include "ucln_in.h" +#include "cstring.h" +#include "putilimp.h" +#include "uassert.h" +#include "umutex.h" +#include "mutex.h" +#include +#include "sharednumberformat.h" +#include "unifiedcache.h" +#include "number_decimalquantity.h" +#include "number_utils.h" + +//#define FMT_DEBUG + +#ifdef FMT_DEBUG +#include +static inline void debugout(UnicodeString s) { + char buf[2000]; + s.extract((int32_t) 0, s.length(), buf); + printf("%s", buf); +} +#define debug(x) printf("%s", x); +#else +#define debugout(x) +#define debug(x) +#endif + +// If no number pattern can be located for a locale, this is the last +// resort. The patterns are same as the ones in root locale. +static const char16_t gLastResortDecimalPat[] = { + 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0 /* "#,##0.###" */ +}; +static const char16_t gLastResortCurrencyPat[] = { + 0xA4, 0xA0, 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x30, 0x30, 0 /* "\u00A4\u00A0#,##0.00" */ +}; +static const char16_t gLastResortPercentPat[] = { + 0x23, 0x2C, 0x23, 0x23, 0x30, 0x25, 0 /* "#,##0%" */ +}; +static const char16_t gLastResortScientificPat[] = { + 0x23, 0x45, 0x30, 0 /* "#E0" */ +}; +static const char16_t gLastResortIsoCurrencyPat[] = { + 0xA4, 0xA4, 0xA0, 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x30, 0x30, 0 /* "\u00A4\u00A4\u00A0#,##0.00" */ +}; +static const char16_t gLastResortPluralCurrencyPat[] = { + 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0x20, 0xA4, 0xA4, 0xA4, 0 /* "#,##0.### \u00A4\u00A4\u00A4*/ +}; +static const char16_t gLastResortAccountingCurrencyPat[] = { + 0xA4, 0xA0, 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x30, 0x30, 0 /* "\u00A4\u00A0#,##0.00" */ +}; + +static const char16_t gSingleCurrencySign[] = {0xA4, 0}; +static const char16_t gDoubleCurrencySign[] = {0xA4, 0xA4, 0}; + +static const char16_t gSlash = 0x2f; + +// If the maximum base 10 exponent were 4, then the largest number would +// be 99,999 which has 5 digits. +// On IEEE754 systems gMaxIntegerDigits is 308 + possible denormalized 15 digits + rounding digit +// With big decimal, the max exponent is 999,999,999 and the max number of digits is the same, 999,999,999 +const int32_t icu::NumberFormat::gDefaultMaxIntegerDigits = 2000000000; +const int32_t icu::NumberFormat::gDefaultMinIntegerDigits = 127; + +static const char16_t * const gLastResortNumberPatterns[UNUM_FORMAT_STYLE_COUNT] = { + nullptr, // UNUM_PATTERN_DECIMAL + gLastResortDecimalPat, // UNUM_DECIMAL + gLastResortCurrencyPat, // UNUM_CURRENCY + gLastResortPercentPat, // UNUM_PERCENT + gLastResortScientificPat, // UNUM_SCIENTIFIC + nullptr, // UNUM_SPELLOUT + nullptr, // UNUM_ORDINAL + nullptr, // UNUM_DURATION + gLastResortDecimalPat, // UNUM_NUMBERING_SYSTEM + nullptr, // UNUM_PATTERN_RULEBASED + gLastResortIsoCurrencyPat, // UNUM_CURRENCY_ISO + gLastResortPluralCurrencyPat, // UNUM_CURRENCY_PLURAL + gLastResortAccountingCurrencyPat, // UNUM_CURRENCY_ACCOUNTING + gLastResortCurrencyPat, // UNUM_CASH_CURRENCY + nullptr, // UNUM_DECIMAL_COMPACT_SHORT + nullptr, // UNUM_DECIMAL_COMPACT_LONG + gLastResortCurrencyPat, // UNUM_CURRENCY_STANDARD +}; + +// Keys used for accessing resource bundles + +static const icu::number::impl::CldrPatternStyle gFormatCldrStyles[UNUM_FORMAT_STYLE_COUNT] = { + /* nullptr */ 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 + /* nullptr */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_SPELLOUT + /* nullptr */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_ORDINAL + /* nullptr */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_DURATION + /* nullptr */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_NUMBERING_SYSTEM + /* nullptr */ 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. + 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 + /* nullptr */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_DECIMAL_COMPACT_SHORT + /* nullptr */ 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 +static UHashtable * NumberingSystem_cache = nullptr; +static icu::UInitOnce gNSCacheInitOnce {}; + +#if !UCONFIG_NO_SERVICE +static icu::ICULocaleService* gService = nullptr; +static icu::UInitOnce gServiceInitOnce {}; +#endif + +/** + * Release all static memory held by Number Format. + */ +U_CDECL_BEGIN +static void U_CALLCONV +deleteNumberingSystem(void *obj) { + delete (icu::NumberingSystem *)obj; +} + +static UBool U_CALLCONV numfmt_cleanup() { +#if !UCONFIG_NO_SERVICE + gServiceInitOnce.reset(); + if (gService) { + delete gService; + gService = nullptr; + } +#endif + gNSCacheInitOnce.reset(); + if (NumberingSystem_cache) { + // delete NumberingSystem_cache; + uhash_close(NumberingSystem_cache); + NumberingSystem_cache = nullptr; + } + return true; +} +U_CDECL_END + +// ***************************************************************************** +// class NumberFormat +// ***************************************************************************** + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(NumberFormat) + +#if !UCONFIG_NO_SERVICE +// ------------------------------------- +// SimpleNumberFormatFactory implementation +NumberFormatFactory::~NumberFormatFactory() {} +SimpleNumberFormatFactory::SimpleNumberFormatFactory(const Locale& locale, UBool visible) + : _visible(visible) +{ + LocaleUtility::initNameFromLocale(locale, _id); +} + +SimpleNumberFormatFactory::~SimpleNumberFormatFactory() {} + +UBool SimpleNumberFormatFactory::visible() const { + return _visible; +} + +const UnicodeString * +SimpleNumberFormatFactory::getSupportedIDs(int32_t &count, UErrorCode& status) const +{ + if (U_SUCCESS(status)) { + count = 1; + return &_id; + } + count = 0; + return nullptr; +} +#endif /* #if !UCONFIG_NO_SERVICE */ + +// ------------------------------------- +// default constructor +NumberFormat::NumberFormat() +: fGroupingUsed(true), + fMaxIntegerDigits(gDefaultMaxIntegerDigits), + fMinIntegerDigits(1), + fMaxFractionDigits(3), // invariant, >= minFractionDigits + fMinFractionDigits(0), + fParseIntegerOnly(false), + fLenient(false), + fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) +{ + fCurrency[0] = 0; +} + +// ------------------------------------- + +NumberFormat::~NumberFormat() +{ +} + +SharedNumberFormat::~SharedNumberFormat() { + delete ptr; +} + +// ------------------------------------- +// copy constructor + +NumberFormat::NumberFormat(const NumberFormat &source) +: Format(source) +{ + *this = source; +} + +// ------------------------------------- +// assignment operator + +NumberFormat& +NumberFormat::operator=(const NumberFormat& rhs) +{ + if (this != &rhs) + { + Format::operator=(rhs); + fGroupingUsed = rhs.fGroupingUsed; + fMaxIntegerDigits = rhs.fMaxIntegerDigits; + fMinIntegerDigits = rhs.fMinIntegerDigits; + fMaxFractionDigits = rhs.fMaxFractionDigits; + fMinFractionDigits = rhs.fMinFractionDigits; + fParseIntegerOnly = rhs.fParseIntegerOnly; + u_strncpy(fCurrency, rhs.fCurrency, 3); + fCurrency[3] = 0; + fLenient = rhs.fLenient; + fCapitalizationContext = rhs.fCapitalizationContext; + } + return *this; +} + +// ------------------------------------- + +bool +NumberFormat::operator==(const Format& that) const +{ + // Format::operator== guarantees this cast is safe + NumberFormat* other = (NumberFormat*)&that; + +#ifdef FMT_DEBUG + // This code makes it easy to determine why two format objects that should + // be equal aren't. + UBool first = true; + if (!Format::operator==(that)) { + if (first) { printf("[ "); first = false; } else { printf(", "); } + debug("Format::!="); + } + if (!(fMaxIntegerDigits == other->fMaxIntegerDigits && + fMinIntegerDigits == other->fMinIntegerDigits)) { + if (first) { printf("[ "); first = false; } else { printf(", "); } + debug("Integer digits !="); + } + if (!(fMaxFractionDigits == other->fMaxFractionDigits && + fMinFractionDigits == other->fMinFractionDigits)) { + if (first) { printf("[ "); first = false; } else { printf(", "); } + debug("Fraction digits !="); + } + if (!(fGroupingUsed == other->fGroupingUsed)) { + if (first) { printf("[ "); first = false; } else { printf(", "); } + debug("fGroupingUsed != "); + } + if (!(fParseIntegerOnly == other->fParseIntegerOnly)) { + if (first) { printf("[ "); first = false; } else { printf(", "); } + debug("fParseIntegerOnly != "); + } + if (!(u_strcmp(fCurrency, other->fCurrency) == 0)) { + if (first) { printf("[ "); first = false; } else { printf(", "); } + debug("fCurrency !="); + } + if (!(fLenient == other->fLenient)) { + if (first) { printf("[ "); first = false; } else { printf(", "); } + debug("fLenient != "); + } + if (!(fCapitalizationContext == other->fCapitalizationContext)) { + if (first) { printf("[ "); first = false; } else { printf(", "); } + debug("fCapitalizationContext != "); + } + if (!first) { printf(" ]"); } +#endif + + return ((this == &that) || + ((Format::operator==(that) && + fMaxIntegerDigits == other->fMaxIntegerDigits && + fMinIntegerDigits == other->fMinIntegerDigits && + fMaxFractionDigits == other->fMaxFractionDigits && + fMinFractionDigits == other->fMinFractionDigits && + fGroupingUsed == other->fGroupingUsed && + fParseIntegerOnly == other->fParseIntegerOnly && + u_strcmp(fCurrency, other->fCurrency) == 0 && + fLenient == other->fLenient && + fCapitalizationContext == other->fCapitalizationContext))); +} + +// ------------------------------------- +// Default implementation sets unsupported error; subclasses should +// override. + +UnicodeString& +NumberFormat::format(double /* unused number */, + UnicodeString& toAppendTo, + FieldPositionIterator* /* unused posIter */, + UErrorCode& status) const +{ + if (!U_FAILURE(status)) { + status = U_UNSUPPORTED_ERROR; + } + return toAppendTo; +} + +// ------------------------------------- +// Default implementation sets unsupported error; subclasses should +// override. + +UnicodeString& +NumberFormat::format(int32_t /* unused number */, + UnicodeString& toAppendTo, + FieldPositionIterator* /* unused posIter */, + UErrorCode& status) const +{ + if (!U_FAILURE(status)) { + status = U_UNSUPPORTED_ERROR; + } + return toAppendTo; +} + +// ------------------------------------- +// Default implementation sets unsupported error; subclasses should +// override. + +UnicodeString& +NumberFormat::format(int64_t /* unused number */, + UnicodeString& toAppendTo, + FieldPositionIterator* /* unused posIter */, + UErrorCode& status) const +{ + if (!U_FAILURE(status)) { + status = U_UNSUPPORTED_ERROR; + } + return toAppendTo; +} + +// ------------------------------------------ +// These functions add the status code, just fall back to the non-status versions +UnicodeString& +NumberFormat::format(double number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode &status) const { + if(U_SUCCESS(status)) { + return format(number,appendTo,pos); + } else { + return appendTo; + } +} + +UnicodeString& +NumberFormat::format(int32_t number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode &status) const { + if(U_SUCCESS(status)) { + return format(number,appendTo,pos); + } else { + return appendTo; + } +} + +UnicodeString& +NumberFormat::format(int64_t number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode &status) const { + if(U_SUCCESS(status)) { + return format(number,appendTo,pos); + } else { + return appendTo; + } +} + + + +// ------------------------------------- +// Decimal Number format() default implementation +// Subclasses do not normally override this function, but rather the DigitList +// formatting functions.. +// The expected call chain from here is +// this function -> +// NumberFormat::format(Formattable -> +// DecimalFormat::format(DigitList +// +// Or, for subclasses of Formattable that do not know about DigitList, +// this Function -> +// NumberFormat::format(Formattable -> +// NumberFormat::format(DigitList -> +// XXXFormat::format(double + +UnicodeString& +NumberFormat::format(StringPiece decimalNum, + UnicodeString& toAppendTo, + FieldPositionIterator* fpi, + UErrorCode& status) const +{ + Formattable f; + f.setDecimalNumber(decimalNum, status); + format(f, toAppendTo, fpi, status); + return toAppendTo; +} + +/** + * +// Formats the number object and save the format +// result in the toAppendTo string buffer. + +// utility to save/restore state, used in two overloads +// of format(const Formattable&...) below. +* +* Old purpose of ArgExtractor was to avoid const. Not thread safe! +* +* keeping it around as a shim. +*/ +class ArgExtractor { + const Formattable* num; + char16_t save[4]; + UBool fWasCurrency; + + public: + ArgExtractor(const NumberFormat& nf, const Formattable& obj, UErrorCode& status); + ~ArgExtractor(); + + const Formattable* number() const; + const char16_t *iso() const; + UBool wasCurrency() const; +}; + +inline const Formattable* +ArgExtractor::number() const { + return num; +} + +inline UBool +ArgExtractor::wasCurrency() const { + return fWasCurrency; +} + +inline const char16_t * +ArgExtractor::iso() const { + return save; +} + +ArgExtractor::ArgExtractor(const NumberFormat& /*nf*/, const Formattable& obj, UErrorCode& /*status*/) + : num(&obj), fWasCurrency(false) { + + const UObject* o = obj.getObject(); // most commonly o==nullptr + const CurrencyAmount* amt; + if (o != nullptr && (amt = dynamic_cast(o)) != nullptr) { + // getISOCurrency() returns a pointer to internal storage, so we + // copy it to retain it across the call to setCurrency(). + //const char16_t* curr = amt->getISOCurrency(); + u_strcpy(save, amt->getISOCurrency()); + num = &amt->getNumber(); + fWasCurrency=true; + } else { + save[0]=0; + } +} + +ArgExtractor::~ArgExtractor() { +} + +UnicodeString& NumberFormat::format(const number::impl::DecimalQuantity &number, + UnicodeString& appendTo, + FieldPositionIterator* posIter, + UErrorCode& status) const { + // DecimalFormat overrides this function, and handles DigitList based big decimals. + // Other subclasses (ChoiceFormat) do not (yet) handle DigitLists, + // so this default implementation falls back to formatting decimal numbers as doubles. + if (U_FAILURE(status)) { + return appendTo; + } + double dnum = number.toDouble(); + format(dnum, appendTo, posIter, status); + return appendTo; +} + + + +UnicodeString& +NumberFormat::format(const number::impl::DecimalQuantity &number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode &status) const { + // DecimalFormat overrides this function, and handles DigitList based big decimals. + // Other subclasses (ChoiceFormat) do not (yet) handle DigitLists, + // so this default implementation falls back to formatting decimal numbers as doubles. + if (U_FAILURE(status)) { + return appendTo; + } + double dnum = number.toDouble(); + format(dnum, appendTo, pos, status); + return appendTo; +} + +UnicodeString& +NumberFormat::format(const Formattable& obj, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& status) const +{ + if (U_FAILURE(status)) return appendTo; + + ArgExtractor arg(*this, obj, status); + const Formattable *n = arg.number(); + const char16_t *iso = arg.iso(); + + if(arg.wasCurrency() && u_strcmp(iso, getCurrency())) { + // trying to format a different currency. + // Right now, we clone. + LocalPointer cloneFmt(this->clone()); + cloneFmt->setCurrency(iso, status); + // next line should NOT recurse, because n is numeric whereas obj was a wrapper around currency amount. + return cloneFmt->format(*n, appendTo, pos, status); + } + + if (n->isNumeric() && n->getDecimalQuantity() != nullptr) { + // 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. + // + // The default implementation for formatting a DigitList converts it + // to a double, and formats that, allowing formatting classes that don't + // know about DigitList to continue to operate as they had. + // + // DecimalFormat overrides the DigitList formatting functions. + format(*n->getDecimalQuantity(), appendTo, pos, status); + } else { + switch (n->getType()) { + case Formattable::kDouble: + format(n->getDouble(), appendTo, pos, status); + break; + case Formattable::kLong: + format(n->getLong(), appendTo, pos, status); + break; + case Formattable::kInt64: + format(n->getInt64(), appendTo, pos, status); + break; + default: + status = U_INVALID_FORMAT_ERROR; + break; + } + } + + return appendTo; +} + +// -------------------------------------x +// Formats the number object and save the format +// result in the toAppendTo string buffer. + +UnicodeString& +NumberFormat::format(const Formattable& obj, + UnicodeString& appendTo, + FieldPositionIterator* posIter, + UErrorCode& status) const +{ + if (U_FAILURE(status)) return appendTo; + + ArgExtractor arg(*this, obj, status); + const Formattable *n = arg.number(); + const char16_t *iso = arg.iso(); + + if(arg.wasCurrency() && u_strcmp(iso, getCurrency())) { + // trying to format a different currency. + // Right now, we clone. + LocalPointer cloneFmt(this->clone()); + cloneFmt->setCurrency(iso, status); + // next line should NOT recurse, because n is numeric whereas obj was a wrapper around currency amount. + return cloneFmt->format(*n, appendTo, posIter, status); + } + + if (n->isNumeric() && n->getDecimalQuantity() != nullptr) { + // Decimal Number + format(*n->getDecimalQuantity(), appendTo, posIter, status); + } else { + switch (n->getType()) { + case Formattable::kDouble: + format(n->getDouble(), appendTo, posIter, status); + break; + case Formattable::kLong: + format(n->getLong(), appendTo, posIter, status); + break; + case Formattable::kInt64: + format(n->getInt64(), appendTo, posIter, status); + break; + default: + status = U_INVALID_FORMAT_ERROR; + break; + } + } + + return appendTo; +} + +// ------------------------------------- + +UnicodeString& +NumberFormat::format(int64_t number, + UnicodeString& appendTo, + FieldPosition& pos) const +{ + // default so we don't introduce a new abstract method + return format((int32_t)number, appendTo, pos); +} + +// ------------------------------------- +// Parses the string and save the result object as well +// as the final parsed position. + +void +NumberFormat::parseObject(const UnicodeString& source, + Formattable& result, + ParsePosition& parse_pos) const +{ + parse(source, result, parse_pos); +} + +// ------------------------------------- +// Formats a double number and save the result in a string. + +UnicodeString& +NumberFormat::format(double number, UnicodeString& appendTo) const +{ + FieldPosition pos(FieldPosition::DONT_CARE); + return format(number, appendTo, pos); +} + +// ------------------------------------- +// Formats a long number and save the result in a string. + +UnicodeString& +NumberFormat::format(int32_t number, UnicodeString& appendTo) const +{ + FieldPosition pos(FieldPosition::DONT_CARE); + return format(number, appendTo, pos); +} + +// ------------------------------------- +// Formats a long number and save the result in a string. + +UnicodeString& +NumberFormat::format(int64_t number, UnicodeString& appendTo) const +{ + FieldPosition pos(FieldPosition::DONT_CARE); + return format(number, appendTo, pos); +} + +// ------------------------------------- +// Parses the text and save the result object. If the returned +// parse position is 0, that means the parsing failed, the status +// code needs to be set to failure. Ignores the returned parse +// position, otherwise. + +void +NumberFormat::parse(const UnicodeString& text, + Formattable& result, + UErrorCode& status) const +{ + if (U_FAILURE(status)) return; + + ParsePosition parsePosition(0); + parse(text, result, parsePosition); + if (parsePosition.getIndex() == 0) { + status = U_INVALID_FORMAT_ERROR; + } +} + +CurrencyAmount* NumberFormat::parseCurrency(const UnicodeString& text, + ParsePosition& pos) const { + // Default implementation only -- subclasses should override + Formattable parseResult; + int32_t start = pos.getIndex(); + parse(text, parseResult, pos); + if (pos.getIndex() != start) { + char16_t curr[4]; + UErrorCode ec = U_ZERO_ERROR; + getEffectiveCurrency(curr, ec); + if (U_SUCCESS(ec)) { + LocalPointer currAmt(new CurrencyAmount(parseResult, curr, ec), ec); + if (U_FAILURE(ec)) { + pos.setIndex(start); // indicate failure + } else { + return currAmt.orphan(); + } + } + } + return nullptr; +} + +// ------------------------------------- +// Sets to only parse integers. + +void +NumberFormat::setParseIntegerOnly(UBool value) +{ + fParseIntegerOnly = value; +} + +// ------------------------------------- +// Sets whether lenient parse is enabled. + +void +NumberFormat::setLenient(UBool enable) +{ + fLenient = enable; +} + +// ------------------------------------- +// Create a number style NumberFormat instance with the default locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createInstance(UErrorCode& status) +{ + return createInstance(Locale::getDefault(), UNUM_DECIMAL, status); +} + +// ------------------------------------- +// Create a number style NumberFormat instance with the inLocale locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createInstance(const Locale& inLocale, UErrorCode& status) +{ + return createInstance(inLocale, UNUM_DECIMAL, status); +} + +// ------------------------------------- +// Create a currency style NumberFormat instance with the default locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createCurrencyInstance(UErrorCode& status) +{ + return createCurrencyInstance(Locale::getDefault(), status); +} + +// ------------------------------------- +// Create a currency style NumberFormat instance with the inLocale locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createCurrencyInstance(const Locale& inLocale, UErrorCode& status) +{ + return createInstance(inLocale, UNUM_CURRENCY, status); +} + +// ------------------------------------- +// Create a percent style NumberFormat instance with the default locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createPercentInstance(UErrorCode& status) +{ + return createInstance(Locale::getDefault(), UNUM_PERCENT, status); +} + +// ------------------------------------- +// Create a percent style NumberFormat instance with the inLocale locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createPercentInstance(const Locale& inLocale, UErrorCode& status) +{ + return createInstance(inLocale, UNUM_PERCENT, status); +} + +// ------------------------------------- +// Create a scientific style NumberFormat instance with the default locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createScientificInstance(UErrorCode& status) +{ + return createInstance(Locale::getDefault(), UNUM_SCIENTIFIC, status); +} + +// ------------------------------------- +// Create a scientific style NumberFormat instance with the inLocale locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createScientificInstance(const Locale& inLocale, UErrorCode& status) +{ + return createInstance(inLocale, UNUM_SCIENTIFIC, status); +} + +// ------------------------------------- + +const Locale* U_EXPORT2 +NumberFormat::getAvailableLocales(int32_t& count) +{ + return Locale::getAvailableLocales(count); +} + +// ------------------------------------------ +// +// Registration +// +//------------------------------------------- + +#if !UCONFIG_NO_SERVICE + +// ------------------------------------- + +class ICUNumberFormatFactory : public ICUResourceBundleFactory { +public: + virtual ~ICUNumberFormatFactory(); +protected: + virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* /* service */, UErrorCode& status) const override { + return NumberFormat::makeInstance(loc, (UNumberFormatStyle)kind, status); + } +}; + +ICUNumberFormatFactory::~ICUNumberFormatFactory() {} + +// ------------------------------------- + +class NFFactory : public LocaleKeyFactory { +private: + NumberFormatFactory* _delegate; + Hashtable* _ids; + +public: + NFFactory(NumberFormatFactory* delegate) + : LocaleKeyFactory(delegate->visible() ? VISIBLE : INVISIBLE) + , _delegate(delegate) + , _ids(nullptr) + { + } + + virtual ~NFFactory(); + + virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const override + { + if (handlesKey(key, status)) { + const LocaleKey* lkey = dynamic_cast(&key); + U_ASSERT(lkey != nullptr); + Locale loc; + lkey->canonicalLocale(loc); + int32_t kind = lkey->kind(); + + UObject* result = _delegate->createFormat(loc, (UNumberFormatStyle)kind); + if (result == nullptr) { + result = service->getKey(const_cast(key) /* cast away const */, nullptr, this, status); + } + return result; + } + return nullptr; + } + +protected: + /** + * Return the set of ids that this factory supports (visible or + * otherwise). This can be called often and might need to be + * cached if it is expensive to create. + */ + virtual const Hashtable* getSupportedIDs(UErrorCode& status) const override + { + if (U_SUCCESS(status)) { + if (!_ids) { + int32_t count = 0; + const UnicodeString * const idlist = _delegate->getSupportedIDs(count, status); + ((NFFactory*)this)->_ids = new Hashtable(status); /* cast away const */ + if (_ids) { + for (int i = 0; i < count; ++i) { + _ids->put(idlist[i], (void*)this, status); + } + } + } + return _ids; + } + return nullptr; + } +}; + +NFFactory::~NFFactory() +{ + delete _delegate; + delete _ids; +} + +class ICUNumberFormatService : public ICULocaleService { +public: + ICUNumberFormatService() + : ICULocaleService(UNICODE_STRING_SIMPLE("Number Format")) + { + UErrorCode status = U_ZERO_ERROR; + registerFactory(new ICUNumberFormatFactory(), status); + } + + virtual ~ICUNumberFormatService(); + + virtual UObject* cloneInstance(UObject* instance) const override { + return ((NumberFormat*)instance)->clone(); + } + + virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /* actualID */, UErrorCode& status) const override { + const LocaleKey* lkey = dynamic_cast(&key); + U_ASSERT(lkey != nullptr); + int32_t kind = lkey->kind(); + Locale loc; + lkey->currentLocale(loc); + return NumberFormat::makeInstance(loc, (UNumberFormatStyle)kind, status); + } + + virtual UBool isDefault() const override { + return countFactories() == 1; + } +}; + +ICUNumberFormatService::~ICUNumberFormatService() {} + +// ------------------------------------- + +static void U_CALLCONV initNumberFormatService() { + U_ASSERT(gService == nullptr); + ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); + gService = new ICUNumberFormatService(); +} + +static ICULocaleService* +getNumberFormatService() +{ + umtx_initOnce(gServiceInitOnce, &initNumberFormatService); + return gService; +} + +static UBool haveService() { + return !gServiceInitOnce.isReset() && (getNumberFormatService() != nullptr); +} + +// ------------------------------------- + +URegistryKey U_EXPORT2 +NumberFormat::registerFactory(NumberFormatFactory* toAdopt, UErrorCode& status) +{ + if (U_FAILURE(status)) { + delete toAdopt; + return nullptr; + } + ICULocaleService *service = getNumberFormatService(); + if (service) { + NFFactory *tempnnf = new NFFactory(toAdopt); + if (tempnnf != nullptr) { + return service->registerFactory(tempnnf, status); + } + } + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; +} + +// ------------------------------------- + +UBool U_EXPORT2 +NumberFormat::unregister(URegistryKey key, UErrorCode& status) +{ + if (U_FAILURE(status)) { + return false; + } + if (haveService()) { + return gService->unregister(key, status); + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + return false; + } +} + +// ------------------------------------- +StringEnumeration* U_EXPORT2 +NumberFormat::getAvailableLocales() +{ + ICULocaleService *service = getNumberFormatService(); + if (service) { + return service->getAvailableLocales(); + } + return nullptr; // no way to return error condition +} +#endif /* UCONFIG_NO_SERVICE */ +// ------------------------------------- + +enum { kKeyValueLenMax = 32 }; + +NumberFormat* +NumberFormat::internalCreateInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { + if (kind == UNUM_CURRENCY) { + char cfKeyValue[kKeyValueLenMax] = {0}; + UErrorCode kvStatus = U_ZERO_ERROR; + int32_t kLen = loc.getKeywordValue("cf", cfKeyValue, kKeyValueLenMax, kvStatus); + if (U_SUCCESS(kvStatus) && kLen > 0 && uprv_strcmp(cfKeyValue,"account")==0) { + kind = UNUM_CURRENCY_ACCOUNTING; + } + } +#if !UCONFIG_NO_SERVICE + if (haveService()) { + return (NumberFormat*)gService->get(loc, kind, status); + } +#endif + return makeInstance(loc, kind, status); +} + +NumberFormat* U_EXPORT2 +NumberFormat::createInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { + if (kind != UNUM_DECIMAL) { + return internalCreateInstance(loc, kind, status); + } + const SharedNumberFormat *shared = createSharedInstance(loc, kind, status); + if (U_FAILURE(status)) { + return nullptr; + } + NumberFormat *result = (*shared)->clone(); + shared->removeRef(); + if (result == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return result; +} + + +// ------------------------------------- +// Checks if the thousand/10 thousand grouping is used in the +// NumberFormat instance. + +UBool +NumberFormat::isGroupingUsed() const +{ + return fGroupingUsed; +} + +// ------------------------------------- +// Sets to use the thousand/10 thousand grouping in the +// NumberFormat instance. + +void +NumberFormat::setGroupingUsed(UBool newValue) +{ + fGroupingUsed = newValue; +} + +// ------------------------------------- +// Gets the maximum number of digits for the integral part for +// this NumberFormat instance. + +int32_t NumberFormat::getMaximumIntegerDigits() const +{ + return fMaxIntegerDigits; +} + +// ------------------------------------- +// Sets the maximum number of digits for the integral part for +// this NumberFormat instance. + +void +NumberFormat::setMaximumIntegerDigits(int32_t newValue) +{ + fMaxIntegerDigits = uprv_max(0, uprv_min(newValue, gDefaultMaxIntegerDigits)); + if(fMinIntegerDigits > fMaxIntegerDigits) + fMinIntegerDigits = fMaxIntegerDigits; +} + +// ------------------------------------- +// Gets the minimum number of digits for the integral part for +// this NumberFormat instance. + +int32_t +NumberFormat::getMinimumIntegerDigits() const +{ + return fMinIntegerDigits; +} + +// ------------------------------------- +// Sets the minimum number of digits for the integral part for +// this NumberFormat instance. + +void +NumberFormat::setMinimumIntegerDigits(int32_t newValue) +{ + fMinIntegerDigits = uprv_max(0, uprv_min(newValue, gDefaultMinIntegerDigits)); + if(fMinIntegerDigits > fMaxIntegerDigits) + fMaxIntegerDigits = fMinIntegerDigits; +} + +// ------------------------------------- +// Gets the maximum number of digits for the fractional part for +// this NumberFormat instance. + +int32_t +NumberFormat::getMaximumFractionDigits() const +{ + return fMaxFractionDigits; +} + +// ------------------------------------- +// Sets the maximum number of digits for the fractional part for +// this NumberFormat instance. + +void +NumberFormat::setMaximumFractionDigits(int32_t newValue) +{ + fMaxFractionDigits = uprv_max(0, uprv_min(newValue, gDefaultMaxIntegerDigits)); + if(fMaxFractionDigits < fMinFractionDigits) + fMinFractionDigits = fMaxFractionDigits; +} + +// ------------------------------------- +// Gets the minimum number of digits for the fractional part for +// this NumberFormat instance. + +int32_t +NumberFormat::getMinimumFractionDigits() const +{ + return fMinFractionDigits; +} + +// ------------------------------------- +// Sets the minimum number of digits for the fractional part for +// this NumberFormat instance. + +void +NumberFormat::setMinimumFractionDigits(int32_t newValue) +{ + fMinFractionDigits = uprv_max(0, uprv_min(newValue, gDefaultMinIntegerDigits)); + if (fMaxFractionDigits < fMinFractionDigits) + fMaxFractionDigits = fMinFractionDigits; +} + +// ------------------------------------- + +void NumberFormat::setCurrency(const char16_t* theCurrency, UErrorCode& ec) { + if (U_FAILURE(ec)) { + return; + } + if (theCurrency) { + u_strncpy(fCurrency, theCurrency, 3); + fCurrency[3] = 0; + } else { + fCurrency[0] = 0; + } +} + +const char16_t* NumberFormat::getCurrency() const { + return fCurrency; +} + +void NumberFormat::getEffectiveCurrency(char16_t* result, UErrorCode& ec) const { + const char16_t* c = getCurrency(); + if (*c != 0) { + u_strncpy(result, c, 3); + result[3] = 0; + } else { + const char* loc = getLocaleID(ULOC_VALID_LOCALE, ec); + if (loc == nullptr) { + loc = uloc_getDefault(); + } + ucurr_forLocale(loc, result, 4, &ec); + } +} + +//---------------------------------------------------------------------- + + +void NumberFormat::setContext(UDisplayContext value, UErrorCode& status) +{ + if (U_FAILURE(status)) + return; + if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) { + fCapitalizationContext = value; + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + } +} + + +UDisplayContext NumberFormat::getContext(UDisplayContextType type, UErrorCode& status) const +{ + if (U_FAILURE(status)) + return (UDisplayContext)0; + if (type != UDISPCTX_TYPE_CAPITALIZATION) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return (UDisplayContext)0; + } + return fCapitalizationContext; +} + + +// ------------------------------------- +// Creates the NumberFormat instance of the specified style (number, currency, +// or percent) for the desired locale. + +static void U_CALLCONV nscacheInit() { + U_ASSERT(NumberingSystem_cache == nullptr); + ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); + UErrorCode status = U_ZERO_ERROR; + NumberingSystem_cache = uhash_open(uhash_hashLong, + uhash_compareLong, + nullptr, + &status); + if (U_FAILURE(status)) { + // Number Format code will run with no cache if creation fails. + NumberingSystem_cache = nullptr; + return; + } + uhash_setValueDeleter(NumberingSystem_cache, deleteNumberingSystem); +} + +template<> U_I18N_API +const SharedNumberFormat *LocaleCacheKey::createObject( + const void * /*unused*/, UErrorCode &status) const { + const char *localeId = fLoc.getName(); + NumberFormat *nf = NumberFormat::internalCreateInstance( + localeId, UNUM_DECIMAL, status); + if (U_FAILURE(status)) { + return nullptr; + } + SharedNumberFormat *result = new SharedNumberFormat(nf); + if (result == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + delete nf; + return nullptr; + } + result->addRef(); + return result; +} + +const SharedNumberFormat* U_EXPORT2 +NumberFormat::createSharedInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { + if (U_FAILURE(status)) { + return nullptr; + } + if (kind != UNUM_DECIMAL) { + status = U_UNSUPPORTED_ERROR; + return nullptr; + } + const SharedNumberFormat *result = nullptr; + UnifiedCache::getByLocale(loc, result, status); + return result; +} + +UBool +NumberFormat::isStyleSupported(UNumberFormatStyle style) { + return gLastResortNumberPatterns[style] != nullptr; +} + +NumberFormat* +NumberFormat::makeInstance(const Locale& desiredLocale, + UNumberFormatStyle style, + UErrorCode& status) { + return makeInstance(desiredLocale, style, false, status); +} + +NumberFormat* +NumberFormat::makeInstance(const Locale& desiredLocale, + UNumberFormatStyle style, + UBool mustBeDecimalFormat, + UErrorCode& status) { + if (U_FAILURE(status)) return nullptr; + + if (style < 0 || style >= UNUM_FORMAT_STYLE_COUNT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + // For the purposes of general number formatting, UNUM_NUMBERING_SYSTEM should behave the same + // was as UNUM_DECIMAL. In both cases, you get either a DecimalFormat or a RuleBasedNumberFormat + // depending on the locale's numbering system (either the default one for the locale or a specific + // one specified by using the "@numbers=" or "-u-nu-" parameter in the locale ID. + if (style == UNUM_NUMBERING_SYSTEM) { + style = UNUM_DECIMAL; + } + + // Some styles are not supported. This is a result of merging + // the @draft ICU 4.2 NumberFormat::EStyles into the long-existing UNumberFormatStyle. + // Ticket #8503 is for reviewing/fixing/merging the two relevant implementations: + // this one and unum_open(). + // The UNUM_PATTERN_ styles are not supported here + // because this method does not take a pattern string. + if (!isStyleSupported(style)) { + status = U_UNSUPPORTED_ERROR; + return nullptr; + } + +#if U_PLATFORM_USES_ONLY_WIN32_API + if (!mustBeDecimalFormat) { + char buffer[8]; + int32_t count = desiredLocale.getKeywordValue("compat", buffer, sizeof(buffer), status); + + // if the locale has "@compat=host", create a host-specific NumberFormat + if (U_SUCCESS(status) && count > 0 && uprv_strcmp(buffer, "host") == 0) { + UBool curr = true; + + switch (style) { + case UNUM_DECIMAL: + curr = false; + // fall-through + U_FALLTHROUGH; + + case UNUM_CURRENCY: + case UNUM_CURRENCY_ISO: // do not support plural formatting here + case UNUM_CURRENCY_PLURAL: + case UNUM_CURRENCY_ACCOUNTING: + case UNUM_CASH_CURRENCY: + case UNUM_CURRENCY_STANDARD: + { + LocalPointer f(new Win32NumberFormat(desiredLocale, curr, status), status); + if (U_SUCCESS(status)) { + return f.orphan(); + } + } + break; + default: + break; + } + } + } +#endif + // Use numbering system cache hashtable + umtx_initOnce(gNSCacheInitOnce, &nscacheInit); + + // Get cached numbering system + LocalPointer ownedNs; + NumberingSystem *ns = nullptr; + if (NumberingSystem_cache != nullptr) { + // TODO: Bad hash key usage, see ticket #8504. + int32_t hashKey = desiredLocale.hashCode(); + + static UMutex nscacheMutex; + Mutex lock(&nscacheMutex); + ns = (NumberingSystem *)uhash_iget(NumberingSystem_cache, hashKey); + if (ns == nullptr) { + ns = NumberingSystem::createInstance(desiredLocale,status); + uhash_iput(NumberingSystem_cache, hashKey, (void*)ns, &status); + } + } else { + ownedNs.adoptInstead(NumberingSystem::createInstance(desiredLocale,status)); + ns = ownedNs.getAlias(); + } + + // check results of getting a numbering system + if (U_FAILURE(status)) { + return nullptr; + } + + if (mustBeDecimalFormat && ns->isAlgorithmic()) { + status = U_UNSUPPORTED_ERROR; + return nullptr; + } + + LocalPointer symbolsToAdopt; + UnicodeString pattern; + LocalUResourceBundlePointer ownedResource(ures_open(nullptr, desiredLocale.getName(), &status)); + if (U_FAILURE(status)) { + return nullptr; + } + else { + // Loads the decimal symbols of the desired locale. + symbolsToAdopt.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(desiredLocale, status), status); + if (U_FAILURE(status)) { + return nullptr; + } + + // 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 nullptr; + } + if(style==UNUM_CURRENCY || style == UNUM_CURRENCY_ISO || style == UNUM_CURRENCY_ACCOUNTING + || style == UNUM_CASH_CURRENCY || style == UNUM_CURRENCY_STANDARD){ + const char16_t* currPattern = symbolsToAdopt->getCurrencyPattern(); + if(currPattern!=nullptr){ + pattern.setTo(currPattern, u_strlen(currPattern)); + } + } + + LocalPointer f; + if (ns->isAlgorithmic()) { + UnicodeString nsDesc; + UnicodeString nsRuleSetGroup; + UnicodeString nsRuleSetName; + Locale nsLoc; + URBNFRuleSetTag desiredRulesType = URBNF_NUMBERING_SYSTEM; + + nsDesc.setTo(ns->getDescription()); + int32_t firstSlash = nsDesc.indexOf(gSlash); + int32_t lastSlash = nsDesc.lastIndexOf(gSlash); + if ( lastSlash > firstSlash ) { + CharString nsLocID; + + nsLocID.appendInvariantChars(nsDesc.tempSubString(0, firstSlash), status); + nsRuleSetGroup.setTo(nsDesc,firstSlash+1,lastSlash-firstSlash-1); + nsRuleSetName.setTo(nsDesc,lastSlash+1); + + nsLoc = Locale::createFromName(nsLocID.data()); + + UnicodeString SpelloutRules = UNICODE_STRING_SIMPLE("SpelloutRules"); + if ( nsRuleSetGroup.compare(SpelloutRules) == 0 ) { + desiredRulesType = URBNF_SPELLOUT; + } + } else { + nsLoc = desiredLocale; + nsRuleSetName.setTo(nsDesc); + } + + RuleBasedNumberFormat *r = new RuleBasedNumberFormat(desiredRulesType,nsLoc,status); + if (r == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + r->setDefaultRuleSet(nsRuleSetName,status); + f.adoptInstead(r); + } else { + // replace single currency sign in the pattern with double currency sign + // if the style is UNUM_CURRENCY_ISO + if (style == UNUM_CURRENCY_ISO) { + pattern.findAndReplace(UnicodeString(true, gSingleCurrencySign, 1), + UnicodeString(true, gDoubleCurrencySign, 2)); + } + + // "new DecimalFormat()" does not adopt the symbols argument if its memory allocation fails. + // So we can't use adoptInsteadAndCheckErrorCode as we need to know if the 'new' failed. + DecimalFormatSymbols *syms = symbolsToAdopt.getAlias(); + LocalPointer df(new DecimalFormat(pattern, syms, style, status)); + + if (df.isValid()) { + // if the DecimalFormat object was successfully new'ed, then it will own symbolsToAdopt, even if the status is a failure. + symbolsToAdopt.orphan(); + } + else { + status = U_MEMORY_ALLOCATION_ERROR; + } + + if (U_FAILURE(status)) { + return nullptr; + } + + // if it is cash currency style, setCurrencyUsage with usage + if (style == UNUM_CASH_CURRENCY){ + df->setCurrencyUsage(UCURR_USAGE_CASH, &status); + } + + if (U_FAILURE(status)) { + return nullptr; + } + + f.adoptInstead(df.orphan()); + } + + f->setLocaleIDs(ures_getLocaleByType(ownedResource.getAlias(), ULOC_VALID_LOCALE, &status), + ures_getLocaleByType(ownedResource.getAlias(), ULOC_ACTUAL_LOCALE, &status)); + if (U_FAILURE(status)) { + return nullptr; + } + return f.orphan(); +} + +/** + * Get the rounding mode. + * @return A rounding mode + */ +NumberFormat::ERoundingMode NumberFormat::getRoundingMode() const { + // Default value. ICU4J throws an exception and we can't change this API. + return NumberFormat::ERoundingMode::kRoundUnnecessary; +} + +/** + * Set the rounding mode. This has no effect unless the rounding + * increment is greater than zero. + * @param roundingMode A rounding mode + */ +void NumberFormat::setRoundingMode(NumberFormat::ERoundingMode /*roundingMode*/) { + // No-op ICU4J throws an exception, and we can't change this API. +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/numparse_affixes.cpp b/deps/icu-small/source/i18n/numparse_affixes.cpp index ad3d48b4731f48..c11cff1d712d24 100644 --- a/deps/icu-small/source/i18n/numparse_affixes.cpp +++ b/deps/icu-small/source/i18n/numparse_affixes.cpp @@ -1,463 +1,463 @@ -// © 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" -#include "string_segment.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: - UPRV_UNREACHABLE_EXIT; - } - - } 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. - if (auto* ptr = fWarehouse.nextCodePointMatcher(cp, status)) { - addMatcher(*ptr); - } else { - // OOM; unwind the stack - return; - } - } - 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(UErrorCode& status) { - return AffixPatternMatcher(fMatchers, fMatchersLen, fPattern, status); -} - -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, UErrorCode& status) { - if (U_FAILURE(status)) { - return nullptr; - } - auto* result = fCodePoints.create(cp); - if (result == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return result; -} - -bool AffixTokenMatcherWarehouse::hasEmptyCurrencySymbol() const { - return fSetupData->currencySymbols.hasEmptyCurrencySymbol(); -} - - -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(status); -} - -AffixPatternMatcher::AffixPatternMatcher(MatcherArray& matchers, int32_t matchersLen, - const UnicodeString& pattern, UErrorCode& status) - : ArraySeriesMatcher(matchers, matchersLen), fPattern(pattern, status) { -} - -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); - - 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 typeInt = 0; typeInt < PATTERN_SIGN_TYPE_COUNT * 2; typeInt++) { - auto type = static_cast(typeInt / 2); - bool dropCurrencySymbols = (typeInt % 2) == 1; - - if (dropCurrencySymbols && !patternInfo.hasCurrencySign()) { - continue; - } - if (dropCurrencySymbols && !fTokenWarehouse->hasEmptyCurrencySymbol()) { - continue; - } - - // Skip affixes in some cases - if (type == PATTERN_SIGN_TYPE_POS - && 0 != (parseFlags & PARSE_FLAG_PLUS_SIGN_ALLOWED)) { - continue; - } - if (type == PATTERN_SIGN_TYPE_POS_SIGN - && 0 == (parseFlags & PARSE_FLAG_PLUS_SIGN_ALLOWED)) { - continue; - } - - // Generate Prefix - // TODO: Handle approximately sign? - bool hasPrefix = false; - PatternStringUtils::patternInfoToStringBuilder( - patternInfo, true, type, false, StandardPlural::OTHER, false, dropCurrencySymbols, sb); - fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern( - sb, *fTokenWarehouse, parseFlags, &hasPrefix, status); - AffixPatternMatcher* prefix = hasPrefix ? &fAffixPatternMatchers[numAffixPatternMatchers++] - : nullptr; - - // Generate Suffix - // TODO: Handle approximately sign? - bool hasSuffix = false; - PatternStringUtils::patternInfoToStringBuilder( - patternInfo, false, type, false, StandardPlural::OTHER, false, dropCurrencySymbols, sb); - fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern( - sb, *fTokenWarehouse, parseFlags, &hasSuffix, status); - AffixPatternMatcher* suffix = hasSuffix ? &fAffixPatternMatchers[numAffixPatternMatchers++] - : nullptr; - - if (type == PATTERN_SIGN_TYPE_POS) { - 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 = (type == PATTERN_SIGN_TYPE_NEG) ? 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 (type == PATTERN_SIGN_TYPE_POS || !equals(prefix, posPrefix)) { - fAffixMatchers[numAffixMatchers++] = {prefix, nullptr, flags}; - } - if (type == PATTERN_SIGN_TYPE_POS || !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 */ +// © 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" +#include "string_segment.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: + UPRV_UNREACHABLE_EXIT; + } + + } 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. + if (auto* ptr = fWarehouse.nextCodePointMatcher(cp, status)) { + addMatcher(*ptr); + } else { + // OOM; unwind the stack + return; + } + } + 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(UErrorCode& status) { + return AffixPatternMatcher(fMatchers, fMatchersLen, fPattern, status); +} + +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, UErrorCode& status) { + if (U_FAILURE(status)) { + return nullptr; + } + auto* result = fCodePoints.create(cp); + if (result == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return result; +} + +bool AffixTokenMatcherWarehouse::hasEmptyCurrencySymbol() const { + return fSetupData->currencySymbols.hasEmptyCurrencySymbol(); +} + + +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(status); +} + +AffixPatternMatcher::AffixPatternMatcher(MatcherArray& matchers, int32_t matchersLen, + const UnicodeString& pattern, UErrorCode& status) + : ArraySeriesMatcher(matchers, matchersLen), fPattern(pattern, status) { +} + +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); + + 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 typeInt = 0; typeInt < PATTERN_SIGN_TYPE_COUNT * 2; typeInt++) { + auto type = static_cast(typeInt / 2); + bool dropCurrencySymbols = (typeInt % 2) == 1; + + if (dropCurrencySymbols && !patternInfo.hasCurrencySign()) { + continue; + } + if (dropCurrencySymbols && !fTokenWarehouse->hasEmptyCurrencySymbol()) { + continue; + } + + // Skip affixes in some cases + if (type == PATTERN_SIGN_TYPE_POS + && 0 != (parseFlags & PARSE_FLAG_PLUS_SIGN_ALLOWED)) { + continue; + } + if (type == PATTERN_SIGN_TYPE_POS_SIGN + && 0 == (parseFlags & PARSE_FLAG_PLUS_SIGN_ALLOWED)) { + continue; + } + + // Generate Prefix + // TODO: Handle approximately sign? + bool hasPrefix = false; + PatternStringUtils::patternInfoToStringBuilder( + patternInfo, true, type, false, StandardPlural::OTHER, false, dropCurrencySymbols, sb); + fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern( + sb, *fTokenWarehouse, parseFlags, &hasPrefix, status); + AffixPatternMatcher* prefix = hasPrefix ? &fAffixPatternMatchers[numAffixPatternMatchers++] + : nullptr; + + // Generate Suffix + // TODO: Handle approximately sign? + bool hasSuffix = false; + PatternStringUtils::patternInfoToStringBuilder( + patternInfo, false, type, false, StandardPlural::OTHER, false, dropCurrencySymbols, sb); + fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern( + sb, *fTokenWarehouse, parseFlags, &hasSuffix, status); + AffixPatternMatcher* suffix = hasSuffix ? &fAffixPatternMatchers[numAffixPatternMatchers++] + : nullptr; + + if (type == PATTERN_SIGN_TYPE_POS) { + 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 = (type == PATTERN_SIGN_TYPE_NEG) ? 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 (type == PATTERN_SIGN_TYPE_POS || !equals(prefix, posPrefix)) { + fAffixMatchers[numAffixMatchers++] = {prefix, nullptr, flags}; + } + if (type == PATTERN_SIGN_TYPE_POS || !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 index ad731ed5d80e94..217d30a529e7e9 100644 --- a/deps/icu-small/source/i18n/numparse_affixes.h +++ b/deps/icu-small/source/i18n/numparse_affixes.h @@ -1,230 +1,230 @@ -// © 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 "cmemory.h" - -#include "numparse_types.h" -#include "numparse_symbols.h" -#include "numparse_currency.h" -#include "number_affixutils.h" -#include "number_currencysymbols.h" - -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 U_I18N_API 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, MemoryPool 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 numparse::impl 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 MemoryPool; -template class U_I18N_API numparse::impl::CompactUnicodeString<4>; -#endif - -namespace numparse { -namespace impl { - -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, UErrorCode& status); - - bool hasEmptyCurrencySymbol() const; - - 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. - MemoryPool 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(UErrorCode& status); - - 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, - UErrorCode& status); - - 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: - // 18 is the limit: positive, zero, and negative, each with prefix, suffix, and prefix+suffix, - // and doubled since there may be an empty currency symbol - AffixMatcher fAffixMatchers[18]; - // 6 is the limit: positive, zero, and negative, a prefix and a suffix for each, - // and doubled since there may be an empty currency symbol - AffixPatternMatcher fAffixPatternMatchers[12]; - // 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 */ +// © 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 "cmemory.h" + +#include "numparse_types.h" +#include "numparse_symbols.h" +#include "numparse_currency.h" +#include "number_affixutils.h" +#include "number_currencysymbols.h" + +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 U_I18N_API 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, MemoryPool 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 numparse::impl 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 MemoryPool; +template class U_I18N_API numparse::impl::CompactUnicodeString<4>; +#endif + +namespace numparse { +namespace impl { + +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, UErrorCode& status); + + bool hasEmptyCurrencySymbol() const; + + 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. + MemoryPool 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(UErrorCode& status); + + 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, + UErrorCode& status); + + 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: + // 18 is the limit: positive, zero, and negative, each with prefix, suffix, and prefix+suffix, + // and doubled since there may be an empty currency symbol + AffixMatcher fAffixMatchers[18]; + // 6 is the limit: positive, zero, and negative, a prefix and a suffix for each, + // and doubled since there may be an empty currency symbol + AffixPatternMatcher fAffixPatternMatchers[12]; + // 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 index 2f7e1ab28d1d22..c652c1c347334b 100644 --- a/deps/icu-small/source/i18n/numparse_compositions.cpp +++ b/deps/icu-small/source/i18n/numparse_compositions.cpp @@ -1,108 +1,108 @@ -// © 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 "string_segment.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 */ +// © 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 "string_segment.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 index f085912def1533..46da478fcc9204 100644 --- a/deps/icu-small/source/i18n/numparse_compositions.h +++ b/deps/icu-small/source/i18n/numparse_compositions.h @@ -1,124 +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 */ +// © 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 index 7bbb060f3de406..f3ad4fe15525b0 100644 --- a/deps/icu-small/source/i18n/numparse_currency.cpp +++ b/deps/icu-small/source/i18n/numparse_currency.cpp @@ -1,189 +1,189 @@ -// © 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" -#include "string_segment.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 https://unicode-org.atlassian.net/browse/ICU-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()) { - // ISO codes should be accepted case-insensitive. - // https://unicode-org.atlassian.net/browse/ICU-13696 - overlap2 = segment.getCommonPrefixLength(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 */ +// © 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" +#include "string_segment.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 https://unicode-org.atlassian.net/browse/ICU-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()) { + // ISO codes should be accepted case-insensitive. + // https://unicode-org.atlassian.net/browse/ICU-13696 + overlap2 = segment.getCommonPrefixLength(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 index a94943312fde9d..1f609b69abde40 100644 --- a/deps/icu-small/source/i18n/numparse_currency.h +++ b/deps/icu-small/source/i18n/numparse_currency.h @@ -1,74 +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 */ +// © 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: + char16_t 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 index 8b99fd7ad4fa10..26c4f1cfb89c6e 100644 --- a/deps/icu-small/source/i18n/numparse_decimal.cpp +++ b/deps/icu-small/source/i18n/numparse_decimal.cpp @@ -1,459 +1,459 @@ -// © 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" -#include "string_segment.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 https://unicode-org.atlassian.net/browse/ICU-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 */ +// © 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" +#include "string_segment.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 https://unicode-org.atlassian.net/browse/ICU-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 index 07c9afeccc9d64..a69a4aefccc7a9 100644 --- a/deps/icu-small/source/i18n/numparse_decimal.h +++ b/deps/icu-small/source/i18n/numparse_decimal.h @@ -1,76 +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 https://unicode-org.atlassian.net/browse/ICU-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 */ +// © 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 https://unicode-org.atlassian.net/browse/ICU-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 index 91c60747f2129f..0b9efb24026eef 100644 --- a/deps/icu-small/source/i18n/numparse_impl.cpp +++ b/deps/icu-small/source/i18n/numparse_impl.cpp @@ -1,365 +1,365 @@ -// © 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 = {parseFlags}; - 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(parser->fLocalValidators.number = {}); - - parser->freeze(); - return parser.orphan(); -} - -NumberParserImpl* -NumberParserImpl::createParserFromProperties(const number::impl::DecimalFormatProperties& properties, - const DecimalFormatSymbols& symbols, bool parseCurrency, - UErrorCode& status) { - Locale locale = symbols.getLocale(); - AutoAffixPatternProvider affixProvider(properties, status); - if (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 (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; - parseFlags |= PARSE_FLAG_STRICT_IGNORABLES; - } else { - parseFlags |= PARSE_FLAG_INCLUDE_UNPAIRED_AFFIXES; - } - if (grouper.getPrimary() <= 0) { - parseFlags |= PARSE_FLAG_GROUPING_DISABLED; - } - if (parseCurrency || affixProvider.get().hasCurrencySign()) { - parseFlags |= PARSE_FLAG_MONETARY_SEPARATORS; - } - if (!parseCurrency) { - parseFlags |= PARSE_FLAG_NO_FOREIGN_CURRENCY; - } - - LocalPointer parser(new NumberParserImpl(parseFlags)); - - parser->fLocalMatchers.ignorables = {parseFlags}; - 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.get(), *parser, ignorables, parseFlags, status); - - //////////////////////// - /// CURRENCY MATCHER /// - //////////////////////// - - if (parseCurrency || affixProvider.get().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 (!isStrict && affixProvider.get().containsSymbolType(AffixPatternType::TYPE_PERCENT, status)) { - parser->addMatcher(parser->fLocalMatchers.percent = {symbols}); - } - if (!isStrict && affixProvider.get().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) { - parseGreedy(segment, result, status); - } else if (0 != (fParseFlags & PARSE_FLAG_ALLOW_INFINITE_RECURSION)) { - // Start at 1 so that recursionLevels never gets to 0 - parseLongestRecursive(segment, result, 1, status); - } else { - // Arbitrary recursion safety limit: 100 levels. - parseLongestRecursive(segment, result, -100, status); - } - for (int32_t i = 0; i < fNumMatchers; i++) { - fMatchers[i]->postProcess(result); - } - result.postProcess(); -} - -void NumberParserImpl::parseGreedy(StringSegment& segment, ParsedNumber& result, - UErrorCode& status) const { - // Note: this method is not recursive in order to avoid stack overflow. - for (int i = 0; i smokeTest(segment)) { - // Matcher failed smoke test: try the next one - i++; - continue; - } - int32_t initialOffset = segment.getOffset(); - matcher->match(segment, result, status); - if (U_FAILURE(status)) { - return; - } - if (segment.getOffset() != initialOffset) { - // Greedy heuristic: accept the match and loop back - i = 0; - continue; - } else { - // Matcher did not match: try the next one - i++; - continue; - } - UPRV_UNREACHABLE_EXIT; - } - - // NOTE: If we get here, the greedy parse completed without consuming the entire string. -} - -void NumberParserImpl::parseLongestRecursive(StringSegment& segment, ParsedNumber& result, - int32_t recursionLevels, - UErrorCode& status) const { - // Base Case - if (segment.length() == 0) { - return; - } - - // Safety against stack overflow - if (recursionLevels == 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, recursionLevels + 1, 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 */ +// © 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 = {parseFlags}; + 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(parser->fLocalValidators.number = {}); + + parser->freeze(); + return parser.orphan(); +} + +NumberParserImpl* +NumberParserImpl::createParserFromProperties(const number::impl::DecimalFormatProperties& properties, + const DecimalFormatSymbols& symbols, bool parseCurrency, + UErrorCode& status) { + Locale locale = symbols.getLocale(); + AutoAffixPatternProvider affixProvider(properties, status); + if (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 (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; + parseFlags |= PARSE_FLAG_STRICT_IGNORABLES; + } else { + parseFlags |= PARSE_FLAG_INCLUDE_UNPAIRED_AFFIXES; + } + if (grouper.getPrimary() <= 0) { + parseFlags |= PARSE_FLAG_GROUPING_DISABLED; + } + if (parseCurrency || affixProvider.get().hasCurrencySign()) { + parseFlags |= PARSE_FLAG_MONETARY_SEPARATORS; + } + if (!parseCurrency) { + parseFlags |= PARSE_FLAG_NO_FOREIGN_CURRENCY; + } + + LocalPointer parser(new NumberParserImpl(parseFlags)); + + parser->fLocalMatchers.ignorables = {parseFlags}; + 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.get(), *parser, ignorables, parseFlags, status); + + //////////////////////// + /// CURRENCY MATCHER /// + //////////////////////// + + if (parseCurrency || affixProvider.get().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 (!isStrict && affixProvider.get().containsSymbolType(AffixPatternType::TYPE_PERCENT, status)) { + parser->addMatcher(parser->fLocalMatchers.percent = {symbols}); + } + if (!isStrict && affixProvider.get().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) { + parseGreedy(segment, result, status); + } else if (0 != (fParseFlags & PARSE_FLAG_ALLOW_INFINITE_RECURSION)) { + // Start at 1 so that recursionLevels never gets to 0 + parseLongestRecursive(segment, result, 1, status); + } else { + // Arbitrary recursion safety limit: 100 levels. + parseLongestRecursive(segment, result, -100, status); + } + for (int32_t i = 0; i < fNumMatchers; i++) { + fMatchers[i]->postProcess(result); + } + result.postProcess(); +} + +void NumberParserImpl::parseGreedy(StringSegment& segment, ParsedNumber& result, + UErrorCode& status) const { + // Note: this method is not recursive in order to avoid stack overflow. + for (int i = 0; i smokeTest(segment)) { + // Matcher failed smoke test: try the next one + i++; + continue; + } + int32_t initialOffset = segment.getOffset(); + matcher->match(segment, result, status); + if (U_FAILURE(status)) { + return; + } + if (segment.getOffset() != initialOffset) { + // Greedy heuristic: accept the match and loop back + i = 0; + continue; + } else { + // Matcher did not match: try the next one + i++; + continue; + } + UPRV_UNREACHABLE_EXIT; + } + + // NOTE: If we get here, the greedy parse completed without consuming the entire string. +} + +void NumberParserImpl::parseLongestRecursive(StringSegment& segment, ParsedNumber& result, + int32_t recursionLevels, + UErrorCode& status) const { + // Base Case + if (segment.length() == 0) { + return; + } + + // Safety against stack overflow + if (recursionLevels == 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, recursionLevels + 1, 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 index 380d9aa4c64289..89c259e0ed8c0d 100644 --- a/deps/icu-small/source/i18n/numparse_impl.h +++ b/deps/icu-small/source/i18n/numparse_impl.h @@ -1,111 +1,111 @@ -// © 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" -#include "string_segment.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 parseGreedy(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const; - - void parseLongestRecursive( - StringSegment& segment, ParsedNumber& result, int32_t recursionLevels, UErrorCode& status) const; -}; - - -} // namespace impl -} // namespace numparse -U_NAMESPACE_END - -#endif //__NUMPARSE_IMPL_H__ -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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" +#include "string_segment.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 parseGreedy(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const; + + void parseLongestRecursive( + StringSegment& segment, ParsedNumber& result, int32_t recursionLevels, 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 index 4b373a3c312254..79cfb55243923f 100644 --- a/deps/icu-small/source/i18n/numparse_parsednumber.cpp +++ b/deps/icu-small/source/i18n/numparse_parsednumber.cpp @@ -1,126 +1,126 @@ -// © 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 "string_segment.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(UErrorCode& status) 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; - } - } - if (quantity.bogus) { - status = U_INVALID_STATE_ERROR; - return 0.0; - } - if (quantity.isZeroish() && 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.isZeroish() && 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 */ +// © 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 "string_segment.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(UErrorCode& status) 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; + } + } + if (quantity.bogus) { + status = U_INVALID_STATE_ERROR; + return 0.0; + } + if (quantity.isZeroish() && 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.isZeroish() && 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 index 4b88cd998fee09..7bda230d69804d 100644 --- a/deps/icu-small/source/i18n/numparse_scientific.cpp +++ b/deps/icu-small/source/i18n/numparse_scientific.cpp @@ -1,163 +1,163 @@ -// © 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" -#include "string_segment.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), - fIgnorablesMatcher(PARSE_FLAG_STRICT_IGNORABLES) { - - 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; - } - - // Only accept one exponent per string. - if (0 != (result.flags & FLAG_HAS_EXPONENT)) { - 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. - int32_t initialOffset = segment.getOffset(); - int32_t overlap = segment.getCommonPrefixLength(fExponentSeparatorString); - if (overlap == fExponentSeparatorString.length()) { - // Full exponent separator match. - - // First attempt to get a code point, returning true if we can't get one. - if (segment.length() == overlap) { - return true; - } - segment.adjustOffset(overlap); - - // Allow ignorables before the sign. - // Note: call site is guarded by the segment.length() check above. - // Note: the ignorables matcher should not touch the result. - fIgnorablesMatcher.match(segment, result, status); - if (segment.length() == 0) { - segment.setOffset(initialOffset); - return true; - } - - // 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)) { - overlap = segment.getCommonPrefixLength(fCustomMinusSign); - if (overlap != fCustomMinusSign.length()) { - // Partial custom sign match - segment.setOffset(initialOffset); - return true; - } - exponentSign = -1; - segment.adjustOffset(overlap); - } else if (segment.startsWith(fCustomPlusSign)) { - overlap = segment.getCommonPrefixLength(fCustomPlusSign); - if (overlap != fCustomPlusSign.length()) { - // Partial custom sign match - segment.setOffset(initialOffset); - return true; - } - segment.adjustOffset(overlap); - } - - // Return true if the segment is empty. - if (segment.length() == 0) { - segment.setOffset(initialOffset); - return true; - } - - // Allow ignorables after the sign. - // Note: call site is guarded by the segment.length() check above. - // Note: the ignorables matcher should not touch the result. - fIgnorablesMatcher.match(segment, result, status); - if (segment.length() == 0) { - segment.setOffset(initialOffset); - return true; - } - - // 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 - segment.setOffset(initialOffset); - } - return digitsReturnValue; - - } else if (overlap == 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 */ +// © 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" +#include "string_segment.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), + fIgnorablesMatcher(PARSE_FLAG_STRICT_IGNORABLES) { + + 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; + } + + // Only accept one exponent per string. + if (0 != (result.flags & FLAG_HAS_EXPONENT)) { + 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. + int32_t initialOffset = segment.getOffset(); + int32_t overlap = segment.getCommonPrefixLength(fExponentSeparatorString); + if (overlap == fExponentSeparatorString.length()) { + // Full exponent separator match. + + // First attempt to get a code point, returning true if we can't get one. + if (segment.length() == overlap) { + return true; + } + segment.adjustOffset(overlap); + + // Allow ignorables before the sign. + // Note: call site is guarded by the segment.length() check above. + // Note: the ignorables matcher should not touch the result. + fIgnorablesMatcher.match(segment, result, status); + if (segment.length() == 0) { + segment.setOffset(initialOffset); + return true; + } + + // 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)) { + overlap = segment.getCommonPrefixLength(fCustomMinusSign); + if (overlap != fCustomMinusSign.length()) { + // Partial custom sign match + segment.setOffset(initialOffset); + return true; + } + exponentSign = -1; + segment.adjustOffset(overlap); + } else if (segment.startsWith(fCustomPlusSign)) { + overlap = segment.getCommonPrefixLength(fCustomPlusSign); + if (overlap != fCustomPlusSign.length()) { + // Partial custom sign match + segment.setOffset(initialOffset); + return true; + } + segment.adjustOffset(overlap); + } + + // Return true if the segment is empty. + if (segment.length() == 0) { + segment.setOffset(initialOffset); + return true; + } + + // Allow ignorables after the sign. + // Note: call site is guarded by the segment.length() check above. + // Note: the ignorables matcher should not touch the result. + fIgnorablesMatcher.match(segment, result, status); + if (segment.length() == 0) { + segment.setOffset(initialOffset); + return true; + } + + // 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 + segment.setOffset(initialOffset); + } + return digitsReturnValue; + + } else if (overlap == 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 index 5617c0c6a60822..754c7517d94af8 100644 --- a/deps/icu-small/source/i18n/numparse_scientific.h +++ b/deps/icu-small/source/i18n/numparse_scientific.h @@ -1,47 +1,47 @@ -// © 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 "numparse_symbols.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; - IgnorablesMatcher fIgnorablesMatcher; - UnicodeString fCustomMinusSign; - UnicodeString fCustomPlusSign; -}; - - -} // namespace impl -} // namespace numparse -U_NAMESPACE_END - -#endif //__NUMPARSE_SCIENTIFIC_H__ -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 "numparse_symbols.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; + IgnorablesMatcher fIgnorablesMatcher; + 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_symbols.cpp b/deps/icu-small/source/i18n/numparse_symbols.cpp index 608f4f5c8b0ee7..118835d5b1949d 100644 --- a/deps/icu-small/source/i18n/numparse_symbols.cpp +++ b/deps/icu-small/source/i18n/numparse_symbols.cpp @@ -1,198 +1,198 @@ -// © 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" -#include "string_segment.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(parse_flags_t parseFlags) : - SymbolMatcher( - {}, - (0 != (parseFlags & PARSE_FLAG_STRICT_IGNORABLES)) ? - unisets::STRICT_IGNORABLES : - unisets::DEFAULT_IGNORABLES) { -} - -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_SIGN) { -} - -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 */ +// © 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" +#include "string_segment.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(parse_flags_t parseFlags) : + SymbolMatcher( + {}, + (0 != (parseFlags & PARSE_FLAG_STRICT_IGNORABLES)) ? + unisets::STRICT_IGNORABLES : + unisets::DEFAULT_IGNORABLES) { +} + +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_SIGN) { +} + +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 index beb133f7d07a53..116f10774818ed 100644 --- a/deps/icu-small/source/i18n/numparse_symbols.h +++ b/deps/icu-small/source/i18n/numparse_symbols.h @@ -1,173 +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(parse_flags_t parseFlags); - - 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 */ +// © 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(parse_flags_t parseFlags); + + 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 index 623f0e80f1668a..1826f8a8f90597 100644 --- a/deps/icu-small/source/i18n/numparse_types.h +++ b/deps/icu-small/source/i18n/numparse_types.h @@ -1,272 +1,272 @@ -// © 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" -#include "string_segment.h" - -U_NAMESPACE_BEGIN -namespace numparse { -namespace impl { - -// Forward-declarations -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, - PARSE_FLAG_ALLOW_INFINITE_RECURSION = 0x4000, - PARSE_FLAG_STRICT_IGNORABLES = 0x8000, -}; - - -// 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, UErrorCode& status) - : fBuffer(text.length() + 1, status) { - if (U_FAILURE(status)) { return; } - uprv_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(UErrorCode& status) const; - - void populateFormattable(Formattable& output, parse_flags_t parseFlags) const; - - bool isBetterThan(const ParsedNumber& other); -}; - - -/** - * 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 */ +// © 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" +#include "string_segment.h" + +U_NAMESPACE_BEGIN +namespace numparse { +namespace impl { + +// Forward-declarations +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, + PARSE_FLAG_ALLOW_INFINITE_RECURSION = 0x4000, + PARSE_FLAG_STRICT_IGNORABLES = 0x8000, +}; + + +// 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, UErrorCode& status) + : fBuffer(text.length() + 1, status) { + if (U_FAILURE(status)) { return; } + uprv_memcpy(fBuffer.getAlias(), text.getBuffer(), sizeof(char16_t) * 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. + */ + char16_t 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(UErrorCode& status) const; + + void populateFormattable(Formattable& output, parse_flags_t parseFlags) const; + + bool isBetterThan(const ParsedNumber& other); +}; + + +/** + * 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 index 162954bae09444..bc03f78e77b453 100644 --- a/deps/icu-small/source/i18n/numparse_utils.h +++ b/deps/icu-small/source/i18n/numparse_utils.h @@ -1,43 +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 */ +// © 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(char16_t* dest, const char16_t* src) { + uprv_memcpy(dest, src, sizeof(char16_t) * 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 index 12d3465c4ef3a4..ee3d5035e75f03 100644 --- a/deps/icu-small/source/i18n/numparse_validators.cpp +++ b/deps/icu-small/source/i18n/numparse_validators.cpp @@ -1,85 +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 */ +// © 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 index 5d43b779d0bb67..c63b03cf37be5e 100644 --- a/deps/icu-small/source/i18n/numparse_validators.h +++ b/deps/icu-small/source/i18n/numparse_validators.h @@ -1,95 +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 */ +// © 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 override { + // No-op + return false; + } + + bool smokeTest(const StringSegment&) const override { + // No-op + return false; + } + + void postProcess(ParsedNumber& result) const override = 0; +}; + + +class RequireAffixValidator : public ValidationMatcher, public UMemory { + public: + void postProcess(ParsedNumber& result) const override; + + UnicodeString toString() const override; +}; + + +class RequireCurrencyValidator : public ValidationMatcher, public UMemory { + public: + void postProcess(ParsedNumber& result) const override; + + UnicodeString toString() const 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 override; + + UnicodeString toString() const override; + + private: + bool fPatternHasDecimalSeparator; +}; + + +class RequireNumberValidator : public ValidationMatcher, public UMemory { + public: + void postProcess(ParsedNumber& result) const override; + + UnicodeString toString() const 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 override; + + UnicodeString toString() const 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/numrange_capi.cpp b/deps/icu-small/source/i18n/numrange_capi.cpp index bd3a9ef5e82528..ebc42373cf3f46 100644 --- a/deps/icu-small/source/i18n/numrange_capi.cpp +++ b/deps/icu-small/source/i18n/numrange_capi.cpp @@ -1,197 +1,198 @@ -// © 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 "formattedval_impl.h" -#include "numrange_impl.h" -#include "number_decnum.h" -#include "unicode/numberrangeformatter.h" -#include "unicode/unumberrangeformatter.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - - -U_NAMESPACE_BEGIN -namespace number { -namespace impl { - -/** - * Implementation class for UNumberRangeFormatter. Wraps a LocalizedRangeNumberFormatter. - */ -struct UNumberRangeFormatterData : public UMemory, - // Magic number as ASCII == "NRF" (NumberRangeFormatter) - public IcuCApiHelper { - LocalizedNumberRangeFormatter fFormatter; -}; - -struct UFormattedNumberRangeImpl; - -// Magic number as ASCII == "FDN" (FormatteDNumber) -typedef IcuCApiHelper UFormattedNumberRangeApiHelper; - -struct UFormattedNumberRangeImpl : public UFormattedValueImpl, public UFormattedNumberRangeApiHelper { - UFormattedNumberRangeImpl(); - ~UFormattedNumberRangeImpl(); - - FormattedNumberRange fImpl; - UFormattedNumberRangeData fData; -}; - -UFormattedNumberRangeImpl::UFormattedNumberRangeImpl() - : fImpl(&fData) { - fFormattedValue = &fImpl; -} - -UFormattedNumberRangeImpl::~UFormattedNumberRangeImpl() { - // Disown the data from fImpl so it doesn't get deleted twice - fImpl.fData = nullptr; -} - -} // namespace impl -} // namespace number -U_NAMESPACE_END - - -UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL( - UFormattedNumberRange, - UFormattedNumberRangeImpl, - UFormattedNumberRangeApiHelper, - unumrf) - - -const UFormattedNumberRangeData* number::impl::validateUFormattedNumberRange( - const UFormattedNumberRange* uresult, UErrorCode& status) { - auto* result = UFormattedNumberRangeApiHelper::validate(uresult, status); - if (U_FAILURE(status)) { - return nullptr; - } - return &result->fData; -} - - -U_CAPI UNumberRangeFormatter* U_EXPORT2 -unumrf_openForSkeletonWithCollapseAndIdentityFallback( - const UChar* skeleton, - int32_t skeletonLen, - UNumberRangeCollapse collapse, - UNumberRangeIdentityFallback identityFallback, - const char* locale, - UParseError* perror, - UErrorCode* ec) { - auto* impl = new UNumberRangeFormatterData(); - 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 = NumberRangeFormatter::withLocale(locale) - .numberFormatterBoth(NumberFormatter::forSkeleton(skeletonString, *perror, *ec)) - .collapse(collapse) - .identityFallback(identityFallback); - return impl->exportForC(); -} - -U_CAPI void U_EXPORT2 -unumrf_formatDoubleRange( - const UNumberRangeFormatter* uformatter, - double first, - double second, - UFormattedNumberRange* uresult, - UErrorCode* ec) { - const UNumberRangeFormatterData* formatter = UNumberRangeFormatterData::validate(uformatter, *ec); - auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec); - if (U_FAILURE(*ec)) { return; } - - result->fData.resetString(); - result->fData.quantity1.clear(); - result->fData.quantity2.clear(); - result->fData.quantity1.setToDouble(first); - result->fData.quantity2.setToDouble(second); - formatter->fFormatter.formatImpl(result->fData, first == second, *ec); -} - -U_CAPI void U_EXPORT2 -unumrf_formatDecimalRange( - const UNumberRangeFormatter* uformatter, - const char* first, int32_t firstLen, - const char* second, int32_t secondLen, - UFormattedNumberRange* uresult, - UErrorCode* ec) { - const UNumberRangeFormatterData* formatter = UNumberRangeFormatterData::validate(uformatter, *ec); - auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec); - if (U_FAILURE(*ec)) { return; } - - result->fData.resetString(); - result->fData.quantity1.clear(); - result->fData.quantity2.clear(); - result->fData.quantity1.setToDecNumber({first, firstLen}, *ec); - result->fData.quantity2.setToDecNumber({second, secondLen}, *ec); - formatter->fFormatter.formatImpl(result->fData, first == second, *ec); -} - -U_CAPI UNumberRangeIdentityResult U_EXPORT2 -unumrf_resultGetIdentityResult( - const UFormattedNumberRange* uresult, - UErrorCode* ec) { - auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec); - if (U_FAILURE(*ec)) { - return UNUM_IDENTITY_RESULT_COUNT; - } - return result->fData.identityResult; -} - -U_CAPI int32_t U_EXPORT2 -unumrf_resultGetFirstDecimalNumber( - const UFormattedNumberRange* uresult, - char* dest, - int32_t destCapacity, - UErrorCode* ec) { - const auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec); - if (U_FAILURE(*ec)) { - return 0; - } - DecNum decnum; - return result->fData.quantity1.toDecNum(decnum, *ec) - .toCharString(*ec) - .extract(dest, destCapacity, *ec); -} - -U_CAPI int32_t U_EXPORT2 -unumrf_resultGetSecondDecimalNumber( - const UFormattedNumberRange* uresult, - char* dest, - int32_t destCapacity, - UErrorCode* ec) { - const auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec); - if (U_FAILURE(*ec)) { - return 0; - } - DecNum decnum; - return result->fData.quantity2 - .toDecNum(decnum, *ec) - .toCharString(*ec) - .extract(dest, destCapacity, *ec); -} - -U_CAPI void U_EXPORT2 -unumrf_close(UNumberRangeFormatter* f) { - UErrorCode localStatus = U_ZERO_ERROR; - const UNumberRangeFormatterData* impl = UNumberRangeFormatterData::validate(f, localStatus); - delete impl; -} - - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 "formattedval_impl.h" +#include "numrange_impl.h" +#include "number_decnum.h" +#include "unicode/numberrangeformatter.h" +#include "unicode/unumberrangeformatter.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +U_NAMESPACE_BEGIN +namespace number { +namespace impl { + +/** + * Implementation class for UNumberRangeFormatter. Wraps a LocalizedRangeNumberFormatter. + */ +struct UNumberRangeFormatterData : public UMemory, + // Magic number as ASCII == "NRF" (NumberRangeFormatter) + public IcuCApiHelper { + LocalizedNumberRangeFormatter fFormatter; +}; + +struct UFormattedNumberRangeImpl; + +// Magic number as ASCII == "FDN" (FormatteDNumber) +typedef IcuCApiHelper UFormattedNumberRangeApiHelper; + +struct UFormattedNumberRangeImpl : public UFormattedValueImpl, public UFormattedNumberRangeApiHelper { + UFormattedNumberRangeImpl(); + ~UFormattedNumberRangeImpl(); + + FormattedNumberRange fImpl; + UFormattedNumberRangeData fData; +}; + +UFormattedNumberRangeImpl::UFormattedNumberRangeImpl() + : fImpl(&fData) { + fFormattedValue = &fImpl; +} + +UFormattedNumberRangeImpl::~UFormattedNumberRangeImpl() { + // Disown the data from fImpl so it doesn't get deleted twice + fImpl.fData = nullptr; +} + +} // namespace impl +} // namespace number +U_NAMESPACE_END + + +UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL( + UFormattedNumberRange, + UFormattedNumberRangeImpl, + UFormattedNumberRangeApiHelper, + unumrf) + + +const UFormattedNumberRangeData* number::impl::validateUFormattedNumberRange( + const UFormattedNumberRange* uresult, UErrorCode& status) { + auto* result = UFormattedNumberRangeApiHelper::validate(uresult, status); + if (U_FAILURE(status)) { + return nullptr; + } + return &result->fData; +} + + +U_CAPI UNumberRangeFormatter* U_EXPORT2 +unumrf_openForSkeletonWithCollapseAndIdentityFallback( + const char16_t* skeleton, + int32_t skeletonLen, + UNumberRangeCollapse collapse, + UNumberRangeIdentityFallback identityFallback, + const char* locale, + UParseError* perror, + UErrorCode* ec) { + auto* impl = new UNumberRangeFormatterData(); + 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); + UParseError tempParseError; + impl->fFormatter = NumberRangeFormatter::withLocale(locale) + .numberFormatterBoth(NumberFormatter::forSkeleton(skeletonString, (perror == nullptr) ? tempParseError : *perror, *ec)) + .collapse(collapse) + .identityFallback(identityFallback); + return impl->exportForC(); +} + +U_CAPI void U_EXPORT2 +unumrf_formatDoubleRange( + const UNumberRangeFormatter* uformatter, + double first, + double second, + UFormattedNumberRange* uresult, + UErrorCode* ec) { + const UNumberRangeFormatterData* formatter = UNumberRangeFormatterData::validate(uformatter, *ec); + auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec); + if (U_FAILURE(*ec)) { return; } + + result->fData.resetString(); + result->fData.quantity1.clear(); + result->fData.quantity2.clear(); + result->fData.quantity1.setToDouble(first); + result->fData.quantity2.setToDouble(second); + formatter->fFormatter.formatImpl(result->fData, first == second, *ec); +} + +U_CAPI void U_EXPORT2 +unumrf_formatDecimalRange( + const UNumberRangeFormatter* uformatter, + const char* first, int32_t firstLen, + const char* second, int32_t secondLen, + UFormattedNumberRange* uresult, + UErrorCode* ec) { + const UNumberRangeFormatterData* formatter = UNumberRangeFormatterData::validate(uformatter, *ec); + auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec); + if (U_FAILURE(*ec)) { return; } + + result->fData.resetString(); + result->fData.quantity1.clear(); + result->fData.quantity2.clear(); + result->fData.quantity1.setToDecNumber({first, firstLen}, *ec); + result->fData.quantity2.setToDecNumber({second, secondLen}, *ec); + formatter->fFormatter.formatImpl(result->fData, first == second, *ec); +} + +U_CAPI UNumberRangeIdentityResult U_EXPORT2 +unumrf_resultGetIdentityResult( + const UFormattedNumberRange* uresult, + UErrorCode* ec) { + auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec); + if (U_FAILURE(*ec)) { + return UNUM_IDENTITY_RESULT_COUNT; + } + return result->fData.identityResult; +} + +U_CAPI int32_t U_EXPORT2 +unumrf_resultGetFirstDecimalNumber( + const UFormattedNumberRange* uresult, + char* dest, + int32_t destCapacity, + UErrorCode* ec) { + const auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec); + if (U_FAILURE(*ec)) { + return 0; + } + DecNum decnum; + return result->fData.quantity1.toDecNum(decnum, *ec) + .toCharString(*ec) + .extract(dest, destCapacity, *ec); +} + +U_CAPI int32_t U_EXPORT2 +unumrf_resultGetSecondDecimalNumber( + const UFormattedNumberRange* uresult, + char* dest, + int32_t destCapacity, + UErrorCode* ec) { + const auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec); + if (U_FAILURE(*ec)) { + return 0; + } + DecNum decnum; + return result->fData.quantity2 + .toDecNum(decnum, *ec) + .toCharString(*ec) + .extract(dest, destCapacity, *ec); +} + +U_CAPI void U_EXPORT2 +unumrf_close(UNumberRangeFormatter* f) { + UErrorCode localStatus = U_ZERO_ERROR; + const UNumberRangeFormatterData* impl = UNumberRangeFormatterData::validate(f, localStatus); + delete impl; +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numrange_fluent.cpp b/deps/icu-small/source/i18n/numrange_fluent.cpp index c36defa36994d7..36847b0a853972 100644 --- a/deps/icu-small/source/i18n/numrange_fluent.cpp +++ b/deps/icu-small/source/i18n/numrange_fluent.cpp @@ -1,381 +1,381 @@ -// © 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 "numrange_impl.h" -#include "util.h" -#include "number_utypes.h" -#include "number_decnum.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - - -// This function needs to be declared in this namespace so it can be friended. -// NOTE: In Java, this logic is handled in the resolve() function. -void icu::number::impl::touchRangeLocales(RangeMacroProps& macros) { - macros.formatter1.fMacros.locale = macros.locale; - macros.formatter2.fMacros.locale = macros.locale; -} - - -template -Derived NumberRangeFormatterSettings::numberFormatterBoth(const UnlocalizedNumberFormatter& formatter) const& { - Derived copy(*this); - copy.fMacros.formatter1 = formatter; - copy.fMacros.singleFormatter = true; - touchRangeLocales(copy.fMacros); - return copy; -} - -template -Derived NumberRangeFormatterSettings::numberFormatterBoth(const UnlocalizedNumberFormatter& formatter) && { - Derived move(std::move(*this)); - move.fMacros.formatter1 = formatter; - move.fMacros.singleFormatter = true; - touchRangeLocales(move.fMacros); - return move; -} - -template -Derived NumberRangeFormatterSettings::numberFormatterBoth(UnlocalizedNumberFormatter&& formatter) const& { - Derived copy(*this); - copy.fMacros.formatter1 = std::move(formatter); - copy.fMacros.singleFormatter = true; - touchRangeLocales(copy.fMacros); - return copy; -} - -template -Derived NumberRangeFormatterSettings::numberFormatterBoth(UnlocalizedNumberFormatter&& formatter) && { - Derived move(std::move(*this)); - move.fMacros.formatter1 = std::move(formatter); - move.fMacros.singleFormatter = true; - touchRangeLocales(move.fMacros); - return move; -} - -template -Derived NumberRangeFormatterSettings::numberFormatterFirst(const UnlocalizedNumberFormatter& formatter) const& { - Derived copy(*this); - copy.fMacros.formatter1 = formatter; - copy.fMacros.singleFormatter = false; - touchRangeLocales(copy.fMacros); - return copy; -} - -template -Derived NumberRangeFormatterSettings::numberFormatterFirst(const UnlocalizedNumberFormatter& formatter) && { - Derived move(std::move(*this)); - move.fMacros.formatter1 = formatter; - move.fMacros.singleFormatter = false; - touchRangeLocales(move.fMacros); - return move; -} - -template -Derived NumberRangeFormatterSettings::numberFormatterFirst(UnlocalizedNumberFormatter&& formatter) const& { - Derived copy(*this); - copy.fMacros.formatter1 = std::move(formatter); - copy.fMacros.singleFormatter = false; - touchRangeLocales(copy.fMacros); - return copy; -} - -template -Derived NumberRangeFormatterSettings::numberFormatterFirst(UnlocalizedNumberFormatter&& formatter) && { - Derived move(std::move(*this)); - move.fMacros.formatter1 = std::move(formatter); - move.fMacros.singleFormatter = false; - touchRangeLocales(move.fMacros); - return move; -} - -template -Derived NumberRangeFormatterSettings::numberFormatterSecond(const UnlocalizedNumberFormatter& formatter) const& { - Derived copy(*this); - copy.fMacros.formatter2 = formatter; - copy.fMacros.singleFormatter = false; - touchRangeLocales(copy.fMacros); - return copy; -} - -template -Derived NumberRangeFormatterSettings::numberFormatterSecond(const UnlocalizedNumberFormatter& formatter) && { - Derived move(std::move(*this)); - move.fMacros.formatter2 = formatter; - move.fMacros.singleFormatter = false; - touchRangeLocales(move.fMacros); - return move; -} - -template -Derived NumberRangeFormatterSettings::numberFormatterSecond(UnlocalizedNumberFormatter&& formatter) const& { - Derived copy(*this); - copy.fMacros.formatter2 = std::move(formatter); - copy.fMacros.singleFormatter = false; - touchRangeLocales(copy.fMacros); - return copy; -} - -template -Derived NumberRangeFormatterSettings::numberFormatterSecond(UnlocalizedNumberFormatter&& formatter) && { - Derived move(std::move(*this)); - move.fMacros.formatter2 = std::move(formatter); - move.fMacros.singleFormatter = false; - touchRangeLocales(move.fMacros); - return move; -} - -template -Derived NumberRangeFormatterSettings::collapse(UNumberRangeCollapse collapse) const& { - Derived copy(*this); - copy.fMacros.collapse = collapse; - return copy; -} - -template -Derived NumberRangeFormatterSettings::collapse(UNumberRangeCollapse collapse) && { - Derived move(std::move(*this)); - move.fMacros.collapse = collapse; - return move; -} - -template -Derived NumberRangeFormatterSettings::identityFallback(UNumberRangeIdentityFallback identityFallback) const& { - Derived copy(*this); - copy.fMacros.identityFallback = identityFallback; - return copy; -} - -template -Derived NumberRangeFormatterSettings::identityFallback(UNumberRangeIdentityFallback identityFallback) && { - Derived move(std::move(*this)); - move.fMacros.identityFallback = identityFallback; - return move; -} - -template -LocalPointer NumberRangeFormatterSettings::clone() const & { - return LocalPointer(new Derived(*this)); -} - -template -LocalPointer NumberRangeFormatterSettings::clone() && { - return LocalPointer(new Derived(std::move(*this))); -} - -// Declare all classes that implement NumberRangeFormatterSettings -// See https://stackoverflow.com/a/495056/1407170 -template -class icu::number::NumberRangeFormatterSettings; -template -class icu::number::NumberRangeFormatterSettings; - - -UnlocalizedNumberRangeFormatter NumberRangeFormatter::with() { - UnlocalizedNumberRangeFormatter result; - return result; -} - -LocalizedNumberRangeFormatter NumberRangeFormatter::withLocale(const Locale& locale) { - return with().locale(locale); -} - - -template using NFS = NumberRangeFormatterSettings; -using LNF = LocalizedNumberRangeFormatter; -using UNF = UnlocalizedNumberRangeFormatter; - -UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const UNF& other) - : UNF(static_cast&>(other)) {} - -UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const NFS& other) - : NFS(other) { - // No additional fields to assign -} - -// Make default copy constructor call the NumberRangeFormatterSettings copy constructor. -UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(UNF&& src) U_NOEXCEPT - : UNF(static_cast&&>(src)) {} - -UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(NFS&& src) U_NOEXCEPT - : NFS(std::move(src)) { - // No additional fields to assign -} - -UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(const UNF& other) { - NFS::operator=(static_cast&>(other)); - // No additional fields to assign - return *this; -} - -UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(UNF&& src) U_NOEXCEPT { - NFS::operator=(static_cast&&>(src)); - // No additional fields to assign - return *this; -} - -// Make default copy constructor call the NumberRangeFormatterSettings copy constructor. -LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const LNF& other) - : LNF(static_cast&>(other)) {} - -LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const NFS& other) - : NFS(other) { - // No additional fields to assign -} - -LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter&& src) U_NOEXCEPT - : LNF(static_cast&&>(src)) {} - -LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS&& src) U_NOEXCEPT - : NFS(std::move(src)) { - // Steal the compiled formatter - LNF&& _src = static_cast(src); - auto* stolen = _src.fAtomicFormatter.exchange(nullptr); - delete fAtomicFormatter.exchange(stolen); -} - -LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(const LNF& other) { - if (this == &other) { return *this; } // self-assignment: no-op - NFS::operator=(static_cast&>(other)); - // Do not steal; just clear - delete fAtomicFormatter.exchange(nullptr); - return *this; -} - -LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) U_NOEXCEPT { - NFS::operator=(static_cast&&>(src)); - // Steal the compiled formatter - auto* stolen = src.fAtomicFormatter.exchange(nullptr); - delete fAtomicFormatter.exchange(stolen); - return *this; -} - - -LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() { - delete fAtomicFormatter.exchange(nullptr); -} - -LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) { - fMacros = macros; - fMacros.locale = locale; - touchRangeLocales(fMacros); -} - -LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(RangeMacroProps&& macros, const Locale& locale) { - fMacros = std::move(macros); - fMacros.locale = locale; - touchRangeLocales(fMacros); -} - -LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale) const& { - return LocalizedNumberRangeFormatter(fMacros, locale); -} - -LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale)&& { - return LocalizedNumberRangeFormatter(std::move(fMacros), locale); -} - - -FormattedNumberRange LocalizedNumberRangeFormatter::formatFormattableRange( - const Formattable& first, const Formattable& second, UErrorCode& status) const { - if (U_FAILURE(status)) { - return FormattedNumberRange(U_ILLEGAL_ARGUMENT_ERROR); - } - - auto results = new UFormattedNumberRangeData(); - if (results == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return FormattedNumberRange(status); - } - - first.populateDecimalQuantity(results->quantity1, status); - if (U_FAILURE(status)) { - return FormattedNumberRange(status); - } - - second.populateDecimalQuantity(results->quantity2, status); - if (U_FAILURE(status)) { - return FormattedNumberRange(status); - } - - formatImpl(*results, first == second, status); - - // Do not save the results object if we encountered a failure. - if (U_SUCCESS(status)) { - return FormattedNumberRange(results); - } else { - delete results; - return FormattedNumberRange(status); - } -} - -void LocalizedNumberRangeFormatter::formatImpl( - UFormattedNumberRangeData& results, bool equalBeforeRounding, UErrorCode& status) const { - auto* impl = getFormatter(status); - if (U_FAILURE(status)) { - return; - } - if (impl == nullptr) { - status = U_INTERNAL_PROGRAM_ERROR; - return; - } - impl->format(results, equalBeforeRounding, status); - if (U_FAILURE(status)) { - return; - } - results.getStringRef().writeTerminator(status); -} - -const impl::NumberRangeFormatterImpl* -LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const { - // TODO: Move this into umutex.h? (similar logic also in decimfmt.cpp) - // See ICU-20146 - - if (U_FAILURE(status)) { - return nullptr; - } - - // First try to get the pre-computed formatter - auto* ptr = fAtomicFormatter.load(); - if (ptr != nullptr) { - return ptr; - } - - // Try computing the formatter on our own - auto* temp = new NumberRangeFormatterImpl(fMacros, status); - if (U_FAILURE(status)) { - delete temp; - return nullptr; - } - if (temp == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - - // 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 formatter object. - auto* nonConstThis = const_cast(this); - if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp)) { - // Another thread beat us to computing the formatter - delete temp; - return ptr; - } else { - // Our copy of the formatter got stored in the atomic - return temp; - } - -} - - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 "numrange_impl.h" +#include "util.h" +#include "number_utypes.h" +#include "number_decnum.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +// This function needs to be declared in this namespace so it can be friended. +// NOTE: In Java, this logic is handled in the resolve() function. +void icu::number::impl::touchRangeLocales(RangeMacroProps& macros) { + macros.formatter1.fMacros.locale = macros.locale; + macros.formatter2.fMacros.locale = macros.locale; +} + + +template +Derived NumberRangeFormatterSettings::numberFormatterBoth(const UnlocalizedNumberFormatter& formatter) const& { + Derived copy(*this); + copy.fMacros.formatter1 = formatter; + copy.fMacros.singleFormatter = true; + touchRangeLocales(copy.fMacros); + return copy; +} + +template +Derived NumberRangeFormatterSettings::numberFormatterBoth(const UnlocalizedNumberFormatter& formatter) && { + Derived move(std::move(*this)); + move.fMacros.formatter1 = formatter; + move.fMacros.singleFormatter = true; + touchRangeLocales(move.fMacros); + return move; +} + +template +Derived NumberRangeFormatterSettings::numberFormatterBoth(UnlocalizedNumberFormatter&& formatter) const& { + Derived copy(*this); + copy.fMacros.formatter1 = std::move(formatter); + copy.fMacros.singleFormatter = true; + touchRangeLocales(copy.fMacros); + return copy; +} + +template +Derived NumberRangeFormatterSettings::numberFormatterBoth(UnlocalizedNumberFormatter&& formatter) && { + Derived move(std::move(*this)); + move.fMacros.formatter1 = std::move(formatter); + move.fMacros.singleFormatter = true; + touchRangeLocales(move.fMacros); + return move; +} + +template +Derived NumberRangeFormatterSettings::numberFormatterFirst(const UnlocalizedNumberFormatter& formatter) const& { + Derived copy(*this); + copy.fMacros.formatter1 = formatter; + copy.fMacros.singleFormatter = false; + touchRangeLocales(copy.fMacros); + return copy; +} + +template +Derived NumberRangeFormatterSettings::numberFormatterFirst(const UnlocalizedNumberFormatter& formatter) && { + Derived move(std::move(*this)); + move.fMacros.formatter1 = formatter; + move.fMacros.singleFormatter = false; + touchRangeLocales(move.fMacros); + return move; +} + +template +Derived NumberRangeFormatterSettings::numberFormatterFirst(UnlocalizedNumberFormatter&& formatter) const& { + Derived copy(*this); + copy.fMacros.formatter1 = std::move(formatter); + copy.fMacros.singleFormatter = false; + touchRangeLocales(copy.fMacros); + return copy; +} + +template +Derived NumberRangeFormatterSettings::numberFormatterFirst(UnlocalizedNumberFormatter&& formatter) && { + Derived move(std::move(*this)); + move.fMacros.formatter1 = std::move(formatter); + move.fMacros.singleFormatter = false; + touchRangeLocales(move.fMacros); + return move; +} + +template +Derived NumberRangeFormatterSettings::numberFormatterSecond(const UnlocalizedNumberFormatter& formatter) const& { + Derived copy(*this); + copy.fMacros.formatter2 = formatter; + copy.fMacros.singleFormatter = false; + touchRangeLocales(copy.fMacros); + return copy; +} + +template +Derived NumberRangeFormatterSettings::numberFormatterSecond(const UnlocalizedNumberFormatter& formatter) && { + Derived move(std::move(*this)); + move.fMacros.formatter2 = formatter; + move.fMacros.singleFormatter = false; + touchRangeLocales(move.fMacros); + return move; +} + +template +Derived NumberRangeFormatterSettings::numberFormatterSecond(UnlocalizedNumberFormatter&& formatter) const& { + Derived copy(*this); + copy.fMacros.formatter2 = std::move(formatter); + copy.fMacros.singleFormatter = false; + touchRangeLocales(copy.fMacros); + return copy; +} + +template +Derived NumberRangeFormatterSettings::numberFormatterSecond(UnlocalizedNumberFormatter&& formatter) && { + Derived move(std::move(*this)); + move.fMacros.formatter2 = std::move(formatter); + move.fMacros.singleFormatter = false; + touchRangeLocales(move.fMacros); + return move; +} + +template +Derived NumberRangeFormatterSettings::collapse(UNumberRangeCollapse collapse) const& { + Derived copy(*this); + copy.fMacros.collapse = collapse; + return copy; +} + +template +Derived NumberRangeFormatterSettings::collapse(UNumberRangeCollapse collapse) && { + Derived move(std::move(*this)); + move.fMacros.collapse = collapse; + return move; +} + +template +Derived NumberRangeFormatterSettings::identityFallback(UNumberRangeIdentityFallback identityFallback) const& { + Derived copy(*this); + copy.fMacros.identityFallback = identityFallback; + return copy; +} + +template +Derived NumberRangeFormatterSettings::identityFallback(UNumberRangeIdentityFallback identityFallback) && { + Derived move(std::move(*this)); + move.fMacros.identityFallback = identityFallback; + return move; +} + +template +LocalPointer NumberRangeFormatterSettings::clone() const & { + return LocalPointer(new Derived(*this)); +} + +template +LocalPointer NumberRangeFormatterSettings::clone() && { + return LocalPointer(new Derived(std::move(*this))); +} + +// Declare all classes that implement NumberRangeFormatterSettings +// See https://stackoverflow.com/a/495056/1407170 +template +class icu::number::NumberRangeFormatterSettings; +template +class icu::number::NumberRangeFormatterSettings; + + +UnlocalizedNumberRangeFormatter NumberRangeFormatter::with() { + UnlocalizedNumberRangeFormatter result; + return result; +} + +LocalizedNumberRangeFormatter NumberRangeFormatter::withLocale(const Locale& locale) { + return with().locale(locale); +} + + +template using NFS = NumberRangeFormatterSettings; +using LNF = LocalizedNumberRangeFormatter; +using UNF = UnlocalizedNumberRangeFormatter; + +UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const UNF& other) + : UNF(static_cast&>(other)) {} + +UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const NFS& other) + : NFS(other) { + // No additional fields to assign +} + +// Make default copy constructor call the NumberRangeFormatterSettings copy constructor. +UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(UNF&& src) noexcept + : UNF(static_cast&&>(src)) {} + +UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(NFS&& src) noexcept + : NFS(std::move(src)) { + // No additional fields to assign +} + +UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(const UNF& other) { + NFS::operator=(static_cast&>(other)); + // No additional fields to assign + return *this; +} + +UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(UNF&& src) noexcept { + NFS::operator=(static_cast&&>(src)); + // No additional fields to assign + return *this; +} + +// Make default copy constructor call the NumberRangeFormatterSettings copy constructor. +LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const LNF& other) + : LNF(static_cast&>(other)) {} + +LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const NFS& other) + : NFS(other) { + // No additional fields to assign +} + +LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter&& src) noexcept + : LNF(static_cast&&>(src)) {} + +LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS&& src) noexcept + : NFS(std::move(src)) { + // Steal the compiled formatter + LNF&& _src = static_cast(src); + auto* stolen = _src.fAtomicFormatter.exchange(nullptr); + delete fAtomicFormatter.exchange(stolen); +} + +LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(const LNF& other) { + if (this == &other) { return *this; } // self-assignment: no-op + NFS::operator=(static_cast&>(other)); + // Do not steal; just clear + delete fAtomicFormatter.exchange(nullptr); + return *this; +} + +LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) noexcept { + NFS::operator=(static_cast&&>(src)); + // Steal the compiled formatter + auto* stolen = src.fAtomicFormatter.exchange(nullptr); + delete fAtomicFormatter.exchange(stolen); + return *this; +} + + +LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() { + delete fAtomicFormatter.exchange(nullptr); +} + +LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) { + fMacros = macros; + fMacros.locale = locale; + touchRangeLocales(fMacros); +} + +LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(RangeMacroProps&& macros, const Locale& locale) { + fMacros = std::move(macros); + fMacros.locale = locale; + touchRangeLocales(fMacros); +} + +LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale) const& { + return LocalizedNumberRangeFormatter(fMacros, locale); +} + +LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale)&& { + return LocalizedNumberRangeFormatter(std::move(fMacros), locale); +} + + +FormattedNumberRange LocalizedNumberRangeFormatter::formatFormattableRange( + const Formattable& first, const Formattable& second, UErrorCode& status) const { + if (U_FAILURE(status)) { + return FormattedNumberRange(U_ILLEGAL_ARGUMENT_ERROR); + } + + auto results = new UFormattedNumberRangeData(); + if (results == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return FormattedNumberRange(status); + } + + first.populateDecimalQuantity(results->quantity1, status); + if (U_FAILURE(status)) { + return FormattedNumberRange(status); + } + + second.populateDecimalQuantity(results->quantity2, status); + if (U_FAILURE(status)) { + return FormattedNumberRange(status); + } + + formatImpl(*results, first == second, status); + + // Do not save the results object if we encountered a failure. + if (U_SUCCESS(status)) { + return FormattedNumberRange(results); + } else { + delete results; + return FormattedNumberRange(status); + } +} + +void LocalizedNumberRangeFormatter::formatImpl( + UFormattedNumberRangeData& results, bool equalBeforeRounding, UErrorCode& status) const { + auto* impl = getFormatter(status); + if (U_FAILURE(status)) { + return; + } + if (impl == nullptr) { + status = U_INTERNAL_PROGRAM_ERROR; + return; + } + impl->format(results, equalBeforeRounding, status); + if (U_FAILURE(status)) { + return; + } + results.getStringRef().writeTerminator(status); +} + +const impl::NumberRangeFormatterImpl* +LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const { + // TODO: Move this into umutex.h? (similar logic also in decimfmt.cpp) + // See ICU-20146 + + if (U_FAILURE(status)) { + return nullptr; + } + + // First try to get the pre-computed formatter + auto* ptr = fAtomicFormatter.load(); + if (ptr != nullptr) { + return ptr; + } + + // Try computing the formatter on our own + auto* temp = new NumberRangeFormatterImpl(fMacros, status); + if (U_FAILURE(status)) { + delete temp; + return nullptr; + } + if (temp == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + + // 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 formatter object. + auto* nonConstThis = const_cast(this); + if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp)) { + // Another thread beat us to computing the formatter + delete temp; + return ptr; + } else { + // Our copy of the formatter got stored in the atomic + return temp; + } + +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numrange_impl.cpp b/deps/icu-small/source/i18n/numrange_impl.cpp index 06efc7b2815e7a..d13983439fd86b 100644 --- a/deps/icu-small/source/i18n/numrange_impl.cpp +++ b/deps/icu-small/source/i18n/numrange_impl.cpp @@ -1,459 +1,459 @@ -// © 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 "unicode/numberrangeformatter.h" -#include "numrange_impl.h" -#include "patternprops.h" -#include "pluralranges.h" -#include "uresimp.h" -#include "util.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; - -namespace { - -// Helper function for 2-dimensional switch statement -constexpr int8_t identity2d(UNumberRangeIdentityFallback a, UNumberRangeIdentityResult b) { - return static_cast(a) | (static_cast(b) << 4); -} - - -struct NumberRangeData { - SimpleFormatter rangePattern; - // Note: approximatelyPattern is unused since ICU 69. - // SimpleFormatter approximatelyPattern; -}; - -class NumberRangeDataSink : public ResourceSink { - public: - NumberRangeDataSink(NumberRangeData& data) : fData(data) {} - - void put(const char* key, ResourceValue& value, UBool /*noFallback*/, UErrorCode& status) U_OVERRIDE { - ResourceTable miscTable = value.getTable(status); - if (U_FAILURE(status)) { return; } - for (int i = 0; miscTable.getKeyAndValue(i, key, value); i++) { - if (uprv_strcmp(key, "range") == 0) { - if (hasRangeData()) { - continue; // have already seen this pattern - } - fData.rangePattern = {value.getUnicodeString(status), status}; - } - /* - // Note: approximatelyPattern is unused since ICU 69. - else if (uprv_strcmp(key, "approximately") == 0) { - if (hasApproxData()) { - continue; // have already seen this pattern - } - fData.approximatelyPattern = {value.getUnicodeString(status), status}; - } - */ - } - } - - bool hasRangeData() { - return fData.rangePattern.getArgumentLimit() != 0; - } - - /* - // Note: approximatelyPattern is unused since ICU 69. - bool hasApproxData() { - return fData.approximatelyPattern.getArgumentLimit() != 0; - } - */ - - bool isComplete() { - return hasRangeData() /* && hasApproxData() */; - } - - void fillInDefaults(UErrorCode& status) { - if (!hasRangeData()) { - fData.rangePattern = {u"{0}–{1}", status}; - } - /* - if (!hasApproxData()) { - fData.approximatelyPattern = {u"~{0}", status}; - } - */ - } - - private: - NumberRangeData& fData; -}; - -void getNumberRangeData(const char* localeName, const char* nsName, NumberRangeData& data, UErrorCode& status) { - if (U_FAILURE(status)) { return; } - LocalUResourceBundlePointer rb(ures_open(NULL, localeName, &status)); - if (U_FAILURE(status)) { return; } - NumberRangeDataSink sink(data); - - CharString dataPath; - dataPath.append("NumberElements/", -1, status); - dataPath.append(nsName, -1, status); - dataPath.append("/miscPatterns", -1, status); - if (U_FAILURE(status)) { return; } - - UErrorCode localStatus = U_ZERO_ERROR; - ures_getAllItemsWithFallback(rb.getAlias(), dataPath.data(), sink, localStatus); - if (U_FAILURE(localStatus) && localStatus != U_MISSING_RESOURCE_ERROR) { - status = localStatus; - return; - } - - // Fall back to latn if necessary - if (!sink.isComplete()) { - ures_getAllItemsWithFallback(rb.getAlias(), "NumberElements/latn/miscPatterns", sink, status); - } - - sink.fillInDefaults(status); -} - -} // namespace - - - -NumberRangeFormatterImpl::NumberRangeFormatterImpl(const RangeMacroProps& macros, UErrorCode& status) - : formatterImpl1(macros.formatter1.fMacros, status), - formatterImpl2(macros.formatter2.fMacros, status), - fSameFormatters(macros.singleFormatter), - fCollapse(macros.collapse), - fIdentityFallback(macros.identityFallback), - fApproximatelyFormatter(status) { - - const char* nsName = formatterImpl1.getRawMicroProps().nsName; - if (!fSameFormatters && uprv_strcmp(nsName, formatterImpl2.getRawMicroProps().nsName) != 0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - NumberRangeData data; - getNumberRangeData(macros.locale.getName(), nsName, data, status); - if (U_FAILURE(status)) { return; } - fRangeFormatter = data.rangePattern; - - if (fSameFormatters && ( - fIdentityFallback == UNUM_IDENTITY_FALLBACK_APPROXIMATELY || - fIdentityFallback == UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE)) { - MacroProps approximatelyMacros(macros.formatter1.fMacros); - approximatelyMacros.approximately = true; - // Use in-place construction because NumberFormatterImpl has internal self-pointers - fApproximatelyFormatter.~NumberFormatterImpl(); - new (&fApproximatelyFormatter) NumberFormatterImpl(approximatelyMacros, status); - } - - // TODO: Get locale from PluralRules instead? - fPluralRanges = StandardPluralRanges::forLocale(macros.locale, status); - if (U_FAILURE(status)) { return; } -} - -void NumberRangeFormatterImpl::format(UFormattedNumberRangeData& data, bool equalBeforeRounding, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - - MicroProps micros1; - MicroProps micros2; - formatterImpl1.preProcess(data.quantity1, micros1, status); - if (fSameFormatters) { - formatterImpl1.preProcess(data.quantity2, micros2, status); - } else { - formatterImpl2.preProcess(data.quantity2, micros2, status); - } - if (U_FAILURE(status)) { - return; - } - - // If any of the affixes are different, an identity is not possible - // and we must use formatRange(). - // TODO: Write this as MicroProps operator==() ? - // TODO: Avoid the redundancy of these equality operations with the - // ones in formatRange? - if (!micros1.modInner->semanticallyEquivalent(*micros2.modInner) - || !micros1.modMiddle->semanticallyEquivalent(*micros2.modMiddle) - || !micros1.modOuter->semanticallyEquivalent(*micros2.modOuter)) { - formatRange(data, micros1, micros2, status); - data.identityResult = UNUM_IDENTITY_RESULT_NOT_EQUAL; - return; - } - - // Check for identity - if (equalBeforeRounding) { - data.identityResult = UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING; - } else if (data.quantity1 == data.quantity2) { - data.identityResult = UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING; - } else { - data.identityResult = UNUM_IDENTITY_RESULT_NOT_EQUAL; - } - - switch (identity2d(fIdentityFallback, data.identityResult)) { - case identity2d(UNUM_IDENTITY_FALLBACK_RANGE, - UNUM_IDENTITY_RESULT_NOT_EQUAL): - case identity2d(UNUM_IDENTITY_FALLBACK_RANGE, - UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING): - case identity2d(UNUM_IDENTITY_FALLBACK_RANGE, - UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING): - case identity2d(UNUM_IDENTITY_FALLBACK_APPROXIMATELY, - UNUM_IDENTITY_RESULT_NOT_EQUAL): - case identity2d(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE, - UNUM_IDENTITY_RESULT_NOT_EQUAL): - case identity2d(UNUM_IDENTITY_FALLBACK_SINGLE_VALUE, - UNUM_IDENTITY_RESULT_NOT_EQUAL): - formatRange(data, micros1, micros2, status); - break; - - case identity2d(UNUM_IDENTITY_FALLBACK_APPROXIMATELY, - UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING): - case identity2d(UNUM_IDENTITY_FALLBACK_APPROXIMATELY, - UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING): - case identity2d(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE, - UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING): - formatApproximately(data, micros1, micros2, status); - break; - - case identity2d(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE, - UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING): - case identity2d(UNUM_IDENTITY_FALLBACK_SINGLE_VALUE, - UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING): - case identity2d(UNUM_IDENTITY_FALLBACK_SINGLE_VALUE, - UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING): - formatSingleValue(data, micros1, micros2, status); - break; - - default: - UPRV_UNREACHABLE_EXIT; - } -} - - -void NumberRangeFormatterImpl::formatSingleValue(UFormattedNumberRangeData& data, - MicroProps& micros1, MicroProps& micros2, - UErrorCode& status) const { - if (U_FAILURE(status)) { return; } - if (fSameFormatters) { - int32_t length = NumberFormatterImpl::writeNumber(micros1, data.quantity1, data.getStringRef(), 0, status); - NumberFormatterImpl::writeAffixes(micros1, data.getStringRef(), 0, length, status); - } else { - formatRange(data, micros1, micros2, status); - } -} - - -void NumberRangeFormatterImpl::formatApproximately (UFormattedNumberRangeData& data, - MicroProps& micros1, MicroProps& micros2, - UErrorCode& status) const { - if (U_FAILURE(status)) { return; } - if (fSameFormatters) { - // Re-format using the approximately formatter: - MicroProps microsAppx; - data.quantity1.resetExponent(); - fApproximatelyFormatter.preProcess(data.quantity1, microsAppx, status); - int32_t length = NumberFormatterImpl::writeNumber(microsAppx, data.quantity1, data.getStringRef(), 0, status); - length += microsAppx.modInner->apply(data.getStringRef(), 0, length, status); - length += microsAppx.modMiddle->apply(data.getStringRef(), 0, length, status); - microsAppx.modOuter->apply(data.getStringRef(), 0, length, status); - } else { - formatRange(data, micros1, micros2, status); - } -} - - -void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data, - MicroProps& micros1, MicroProps& micros2, - UErrorCode& status) const { - if (U_FAILURE(status)) { return; } - - // modInner is always notation (scientific); collapsable in ALL. - // modOuter is always units; collapsable in ALL, AUTO, and UNIT. - // modMiddle could be either; collapsable in ALL and sometimes AUTO and UNIT. - // Never collapse an outer mod but not an inner mod. - bool collapseOuter, collapseMiddle, collapseInner; - switch (fCollapse) { - case UNUM_RANGE_COLLAPSE_ALL: - case UNUM_RANGE_COLLAPSE_AUTO: - case UNUM_RANGE_COLLAPSE_UNIT: - { - // OUTER MODIFIER - collapseOuter = micros1.modOuter->semanticallyEquivalent(*micros2.modOuter); - - if (!collapseOuter) { - // Never collapse inner mods if outer mods are not collapsable - collapseMiddle = false; - collapseInner = false; - break; - } - - // MIDDLE MODIFIER - collapseMiddle = micros1.modMiddle->semanticallyEquivalent(*micros2.modMiddle); - - if (!collapseMiddle) { - // Never collapse inner mods if outer mods are not collapsable - collapseInner = false; - break; - } - - // MIDDLE MODIFIER HEURISTICS - // (could disable collapsing of the middle modifier) - // The modifiers are equal by this point, so we can look at just one of them. - const Modifier* mm = micros1.modMiddle; - if (fCollapse == UNUM_RANGE_COLLAPSE_UNIT) { - // Only collapse if the modifier is a unit. - // TODO: Make a better way to check for a unit? - // TODO: Handle case where the modifier has both notation and unit (compact currency)? - if (!mm->containsField({UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}) - && !mm->containsField({UFIELD_CATEGORY_NUMBER, UNUM_PERCENT_FIELD})) { - collapseMiddle = false; - } - } else if (fCollapse == UNUM_RANGE_COLLAPSE_AUTO) { - // Heuristic as of ICU 63: collapse only if the modifier is more than one code point. - if (mm->getCodePointCount() <= 1) { - collapseMiddle = false; - } - } - - if (!collapseMiddle || fCollapse != UNUM_RANGE_COLLAPSE_ALL) { - collapseInner = false; - break; - } - - // INNER MODIFIER - collapseInner = micros1.modInner->semanticallyEquivalent(*micros2.modInner); - - // All done checking for collapsibility. - break; - } - - default: - collapseOuter = false; - collapseMiddle = false; - collapseInner = false; - break; - } - - FormattedStringBuilder& string = data.getStringRef(); - int32_t lengthPrefix = 0; - int32_t length1 = 0; - int32_t lengthInfix = 0; - int32_t length2 = 0; - int32_t lengthSuffix = 0; - - // Use #define so that these are evaluated at the call site. - #define UPRV_INDEX_0 (lengthPrefix) - #define UPRV_INDEX_1 (lengthPrefix + length1) - #define UPRV_INDEX_2 (lengthPrefix + length1 + lengthInfix) - #define UPRV_INDEX_3 (lengthPrefix + length1 + lengthInfix + length2) - #define UPRV_INDEX_4 (lengthPrefix + length1 + lengthInfix + length2 + lengthSuffix) - - int32_t lengthRange = SimpleModifier::formatTwoArgPattern( - fRangeFormatter, - string, - 0, - &lengthPrefix, - &lengthSuffix, - kUndefinedField, - status); - if (U_FAILURE(status)) { return; } - lengthInfix = lengthRange - lengthPrefix - lengthSuffix; - U_ASSERT(lengthInfix > 0); - - // SPACING HEURISTIC - // Add spacing unless all modifiers are collapsed. - // TODO: add API to control this? - // TODO: Use a data-driven heuristic like currency spacing? - // TODO: Use Unicode [:whitespace:] instead of PatternProps whitespace? (consider speed implications) - { - bool repeatInner = !collapseInner && micros1.modInner->getCodePointCount() > 0; - bool repeatMiddle = !collapseMiddle && micros1.modMiddle->getCodePointCount() > 0; - bool repeatOuter = !collapseOuter && micros1.modOuter->getCodePointCount() > 0; - if (repeatInner || repeatMiddle || repeatOuter) { - // Add spacing if there is not already spacing - if (!PatternProps::isWhiteSpace(string.charAt(UPRV_INDEX_1))) { - lengthInfix += string.insertCodePoint(UPRV_INDEX_1, u'\u0020', kUndefinedField, status); - } - if (!PatternProps::isWhiteSpace(string.charAt(UPRV_INDEX_2 - 1))) { - lengthInfix += string.insertCodePoint(UPRV_INDEX_2, u'\u0020', kUndefinedField, status); - } - } - } - - length1 += NumberFormatterImpl::writeNumber(micros1, data.quantity1, string, UPRV_INDEX_0, status); - // ICU-21684: Write the second number to a temp string to avoid repeated insert operations - FormattedStringBuilder tempString; - NumberFormatterImpl::writeNumber(micros2, data.quantity2, tempString, 0, status); - length2 += string.insert(UPRV_INDEX_2, tempString, status); - - // TODO: Support padding? - - if (collapseInner) { - const Modifier& mod = resolveModifierPlurals(*micros1.modInner, *micros2.modInner); - lengthSuffix += mod.apply(string, UPRV_INDEX_0, UPRV_INDEX_4, status); - lengthPrefix += mod.getPrefixLength(); - lengthSuffix -= mod.getPrefixLength(); - } else { - length1 += micros1.modInner->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status); - length2 += micros2.modInner->apply(string, UPRV_INDEX_2, UPRV_INDEX_4, status); - } - - if (collapseMiddle) { - const Modifier& mod = resolveModifierPlurals(*micros1.modMiddle, *micros2.modMiddle); - lengthSuffix += mod.apply(string, UPRV_INDEX_0, UPRV_INDEX_4, status); - lengthPrefix += mod.getPrefixLength(); - lengthSuffix -= mod.getPrefixLength(); - } else { - length1 += micros1.modMiddle->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status); - length2 += micros2.modMiddle->apply(string, UPRV_INDEX_2, UPRV_INDEX_4, status); - } - - if (collapseOuter) { - const Modifier& mod = resolveModifierPlurals(*micros1.modOuter, *micros2.modOuter); - lengthSuffix += mod.apply(string, UPRV_INDEX_0, UPRV_INDEX_4, status); - lengthPrefix += mod.getPrefixLength(); - lengthSuffix -= mod.getPrefixLength(); - } else { - length1 += micros1.modOuter->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status); - length2 += micros2.modOuter->apply(string, UPRV_INDEX_2, UPRV_INDEX_4, status); - } - - // Now that all pieces are added, save the span info. - data.appendSpanInfo(UFIELD_CATEGORY_NUMBER_RANGE_SPAN, 0, UPRV_INDEX_0, length1, status); - data.appendSpanInfo(UFIELD_CATEGORY_NUMBER_RANGE_SPAN, 1, UPRV_INDEX_2, length2, status); -} - - -const Modifier& -NumberRangeFormatterImpl::resolveModifierPlurals(const Modifier& first, const Modifier& second) const { - Modifier::Parameters parameters; - first.getParameters(parameters); - if (parameters.obj == nullptr) { - // No plural form; return a fallback (e.g., the first) - return first; - } - StandardPlural::Form firstPlural = parameters.plural; - - second.getParameters(parameters); - if (parameters.obj == nullptr) { - // No plural form; return a fallback (e.g., the first) - return first; - } - StandardPlural::Form secondPlural = parameters.plural; - - // Get the required plural form from data - StandardPlural::Form resultPlural = fPluralRanges.resolve(firstPlural, secondPlural); - - // Get and return the new Modifier - const Modifier* mod = parameters.obj->getModifier(parameters.signum, resultPlural); - U_ASSERT(mod != nullptr); - return *mod; -} - - - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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 "unicode/numberrangeformatter.h" +#include "numrange_impl.h" +#include "patternprops.h" +#include "pluralranges.h" +#include "uresimp.h" +#include "util.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +namespace { + +// Helper function for 2-dimensional switch statement +constexpr int8_t identity2d(UNumberRangeIdentityFallback a, UNumberRangeIdentityResult b) { + return static_cast(a) | (static_cast(b) << 4); +} + + +struct NumberRangeData { + SimpleFormatter rangePattern; + // Note: approximatelyPattern is unused since ICU 69. + // SimpleFormatter approximatelyPattern; +}; + +class NumberRangeDataSink : public ResourceSink { + public: + NumberRangeDataSink(NumberRangeData& data) : fData(data) {} + + void put(const char* key, ResourceValue& value, UBool /*noFallback*/, UErrorCode& status) override { + ResourceTable miscTable = value.getTable(status); + if (U_FAILURE(status)) { return; } + for (int i = 0; miscTable.getKeyAndValue(i, key, value); i++) { + if (uprv_strcmp(key, "range") == 0) { + if (hasRangeData()) { + continue; // have already seen this pattern + } + fData.rangePattern = {value.getUnicodeString(status), status}; + } + /* + // Note: approximatelyPattern is unused since ICU 69. + else if (uprv_strcmp(key, "approximately") == 0) { + if (hasApproxData()) { + continue; // have already seen this pattern + } + fData.approximatelyPattern = {value.getUnicodeString(status), status}; + } + */ + } + } + + bool hasRangeData() { + return fData.rangePattern.getArgumentLimit() != 0; + } + + /* + // Note: approximatelyPattern is unused since ICU 69. + bool hasApproxData() { + return fData.approximatelyPattern.getArgumentLimit() != 0; + } + */ + + bool isComplete() { + return hasRangeData() /* && hasApproxData() */; + } + + void fillInDefaults(UErrorCode& status) { + if (!hasRangeData()) { + fData.rangePattern = {u"{0}–{1}", status}; + } + /* + if (!hasApproxData()) { + fData.approximatelyPattern = {u"~{0}", status}; + } + */ + } + + private: + NumberRangeData& fData; +}; + +void getNumberRangeData(const char* localeName, const char* nsName, NumberRangeData& data, UErrorCode& status) { + if (U_FAILURE(status)) { return; } + LocalUResourceBundlePointer rb(ures_open(nullptr, localeName, &status)); + if (U_FAILURE(status)) { return; } + NumberRangeDataSink sink(data); + + CharString dataPath; + dataPath.append("NumberElements/", -1, status); + dataPath.append(nsName, -1, status); + dataPath.append("/miscPatterns", -1, status); + if (U_FAILURE(status)) { return; } + + UErrorCode localStatus = U_ZERO_ERROR; + ures_getAllItemsWithFallback(rb.getAlias(), dataPath.data(), sink, localStatus); + if (U_FAILURE(localStatus) && localStatus != U_MISSING_RESOURCE_ERROR) { + status = localStatus; + return; + } + + // Fall back to latn if necessary + if (!sink.isComplete()) { + ures_getAllItemsWithFallback(rb.getAlias(), "NumberElements/latn/miscPatterns", sink, status); + } + + sink.fillInDefaults(status); +} + +} // namespace + + + +NumberRangeFormatterImpl::NumberRangeFormatterImpl(const RangeMacroProps& macros, UErrorCode& status) + : formatterImpl1(macros.formatter1.fMacros, status), + formatterImpl2(macros.formatter2.fMacros, status), + fSameFormatters(macros.singleFormatter), + fCollapse(macros.collapse), + fIdentityFallback(macros.identityFallback), + fApproximatelyFormatter(status) { + + const char* nsName = formatterImpl1.getRawMicroProps().nsName; + if (!fSameFormatters && uprv_strcmp(nsName, formatterImpl2.getRawMicroProps().nsName) != 0) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + NumberRangeData data; + getNumberRangeData(macros.locale.getName(), nsName, data, status); + if (U_FAILURE(status)) { return; } + fRangeFormatter = data.rangePattern; + + if (fSameFormatters && ( + fIdentityFallback == UNUM_IDENTITY_FALLBACK_APPROXIMATELY || + fIdentityFallback == UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE)) { + MacroProps approximatelyMacros(macros.formatter1.fMacros); + approximatelyMacros.approximately = true; + // Use in-place construction because NumberFormatterImpl has internal self-pointers + fApproximatelyFormatter.~NumberFormatterImpl(); + new (&fApproximatelyFormatter) NumberFormatterImpl(approximatelyMacros, status); + } + + // TODO: Get locale from PluralRules instead? + fPluralRanges = StandardPluralRanges::forLocale(macros.locale, status); + if (U_FAILURE(status)) { return; } +} + +void NumberRangeFormatterImpl::format(UFormattedNumberRangeData& data, bool equalBeforeRounding, UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + + MicroProps micros1; + MicroProps micros2; + formatterImpl1.preProcess(data.quantity1, micros1, status); + if (fSameFormatters) { + formatterImpl1.preProcess(data.quantity2, micros2, status); + } else { + formatterImpl2.preProcess(data.quantity2, micros2, status); + } + if (U_FAILURE(status)) { + return; + } + + // If any of the affixes are different, an identity is not possible + // and we must use formatRange(). + // TODO: Write this as MicroProps operator==() ? + // TODO: Avoid the redundancy of these equality operations with the + // ones in formatRange? + if (!micros1.modInner->semanticallyEquivalent(*micros2.modInner) + || !micros1.modMiddle->semanticallyEquivalent(*micros2.modMiddle) + || !micros1.modOuter->semanticallyEquivalent(*micros2.modOuter)) { + formatRange(data, micros1, micros2, status); + data.identityResult = UNUM_IDENTITY_RESULT_NOT_EQUAL; + return; + } + + // Check for identity + if (equalBeforeRounding) { + data.identityResult = UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING; + } else if (data.quantity1 == data.quantity2) { + data.identityResult = UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING; + } else { + data.identityResult = UNUM_IDENTITY_RESULT_NOT_EQUAL; + } + + switch (identity2d(fIdentityFallback, data.identityResult)) { + case identity2d(UNUM_IDENTITY_FALLBACK_RANGE, + UNUM_IDENTITY_RESULT_NOT_EQUAL): + case identity2d(UNUM_IDENTITY_FALLBACK_RANGE, + UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING): + case identity2d(UNUM_IDENTITY_FALLBACK_RANGE, + UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING): + case identity2d(UNUM_IDENTITY_FALLBACK_APPROXIMATELY, + UNUM_IDENTITY_RESULT_NOT_EQUAL): + case identity2d(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE, + UNUM_IDENTITY_RESULT_NOT_EQUAL): + case identity2d(UNUM_IDENTITY_FALLBACK_SINGLE_VALUE, + UNUM_IDENTITY_RESULT_NOT_EQUAL): + formatRange(data, micros1, micros2, status); + break; + + case identity2d(UNUM_IDENTITY_FALLBACK_APPROXIMATELY, + UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING): + case identity2d(UNUM_IDENTITY_FALLBACK_APPROXIMATELY, + UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING): + case identity2d(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE, + UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING): + formatApproximately(data, micros1, micros2, status); + break; + + case identity2d(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE, + UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING): + case identity2d(UNUM_IDENTITY_FALLBACK_SINGLE_VALUE, + UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING): + case identity2d(UNUM_IDENTITY_FALLBACK_SINGLE_VALUE, + UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING): + formatSingleValue(data, micros1, micros2, status); + break; + + default: + UPRV_UNREACHABLE_EXIT; + } +} + + +void NumberRangeFormatterImpl::formatSingleValue(UFormattedNumberRangeData& data, + MicroProps& micros1, MicroProps& micros2, + UErrorCode& status) const { + if (U_FAILURE(status)) { return; } + if (fSameFormatters) { + int32_t length = NumberFormatterImpl::writeNumber(micros1.simple, data.quantity1, data.getStringRef(), 0, status); + NumberFormatterImpl::writeAffixes(micros1, data.getStringRef(), 0, length, status); + } else { + formatRange(data, micros1, micros2, status); + } +} + + +void NumberRangeFormatterImpl::formatApproximately (UFormattedNumberRangeData& data, + MicroProps& micros1, MicroProps& micros2, + UErrorCode& status) const { + if (U_FAILURE(status)) { return; } + if (fSameFormatters) { + // Re-format using the approximately formatter: + MicroProps microsAppx; + data.quantity1.resetExponent(); + fApproximatelyFormatter.preProcess(data.quantity1, microsAppx, status); + int32_t length = NumberFormatterImpl::writeNumber(microsAppx.simple, data.quantity1, data.getStringRef(), 0, status); + length += microsAppx.modInner->apply(data.getStringRef(), 0, length, status); + length += microsAppx.modMiddle->apply(data.getStringRef(), 0, length, status); + microsAppx.modOuter->apply(data.getStringRef(), 0, length, status); + } else { + formatRange(data, micros1, micros2, status); + } +} + + +void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data, + MicroProps& micros1, MicroProps& micros2, + UErrorCode& status) const { + if (U_FAILURE(status)) { return; } + + // modInner is always notation (scientific); collapsable in ALL. + // modOuter is always units; collapsable in ALL, AUTO, and UNIT. + // modMiddle could be either; collapsable in ALL and sometimes AUTO and UNIT. + // Never collapse an outer mod but not an inner mod. + bool collapseOuter, collapseMiddle, collapseInner; + switch (fCollapse) { + case UNUM_RANGE_COLLAPSE_ALL: + case UNUM_RANGE_COLLAPSE_AUTO: + case UNUM_RANGE_COLLAPSE_UNIT: + { + // OUTER MODIFIER + collapseOuter = micros1.modOuter->semanticallyEquivalent(*micros2.modOuter); + + if (!collapseOuter) { + // Never collapse inner mods if outer mods are not collapsable + collapseMiddle = false; + collapseInner = false; + break; + } + + // MIDDLE MODIFIER + collapseMiddle = micros1.modMiddle->semanticallyEquivalent(*micros2.modMiddle); + + if (!collapseMiddle) { + // Never collapse inner mods if outer mods are not collapsable + collapseInner = false; + break; + } + + // MIDDLE MODIFIER HEURISTICS + // (could disable collapsing of the middle modifier) + // The modifiers are equal by this point, so we can look at just one of them. + const Modifier* mm = micros1.modMiddle; + if (fCollapse == UNUM_RANGE_COLLAPSE_UNIT) { + // Only collapse if the modifier is a unit. + // TODO: Make a better way to check for a unit? + // TODO: Handle case where the modifier has both notation and unit (compact currency)? + if (!mm->containsField({UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}) + && !mm->containsField({UFIELD_CATEGORY_NUMBER, UNUM_PERCENT_FIELD})) { + collapseMiddle = false; + } + } else if (fCollapse == UNUM_RANGE_COLLAPSE_AUTO) { + // Heuristic as of ICU 63: collapse only if the modifier is more than one code point. + if (mm->getCodePointCount() <= 1) { + collapseMiddle = false; + } + } + + if (!collapseMiddle || fCollapse != UNUM_RANGE_COLLAPSE_ALL) { + collapseInner = false; + break; + } + + // INNER MODIFIER + collapseInner = micros1.modInner->semanticallyEquivalent(*micros2.modInner); + + // All done checking for collapsibility. + break; + } + + default: + collapseOuter = false; + collapseMiddle = false; + collapseInner = false; + break; + } + + FormattedStringBuilder& string = data.getStringRef(); + int32_t lengthPrefix = 0; + int32_t length1 = 0; + int32_t lengthInfix = 0; + int32_t length2 = 0; + int32_t lengthSuffix = 0; + + // Use #define so that these are evaluated at the call site. + #define UPRV_INDEX_0 (lengthPrefix) + #define UPRV_INDEX_1 (lengthPrefix + length1) + #define UPRV_INDEX_2 (lengthPrefix + length1 + lengthInfix) + #define UPRV_INDEX_3 (lengthPrefix + length1 + lengthInfix + length2) + #define UPRV_INDEX_4 (lengthPrefix + length1 + lengthInfix + length2 + lengthSuffix) + + int32_t lengthRange = SimpleModifier::formatTwoArgPattern( + fRangeFormatter, + string, + 0, + &lengthPrefix, + &lengthSuffix, + kUndefinedField, + status); + if (U_FAILURE(status)) { return; } + lengthInfix = lengthRange - lengthPrefix - lengthSuffix; + U_ASSERT(lengthInfix > 0); + + // SPACING HEURISTIC + // Add spacing unless all modifiers are collapsed. + // TODO: add API to control this? + // TODO: Use a data-driven heuristic like currency spacing? + // TODO: Use Unicode [:whitespace:] instead of PatternProps whitespace? (consider speed implications) + { + bool repeatInner = !collapseInner && micros1.modInner->getCodePointCount() > 0; + bool repeatMiddle = !collapseMiddle && micros1.modMiddle->getCodePointCount() > 0; + bool repeatOuter = !collapseOuter && micros1.modOuter->getCodePointCount() > 0; + if (repeatInner || repeatMiddle || repeatOuter) { + // Add spacing if there is not already spacing + if (!PatternProps::isWhiteSpace(string.charAt(UPRV_INDEX_1))) { + lengthInfix += string.insertCodePoint(UPRV_INDEX_1, u'\u0020', kUndefinedField, status); + } + if (!PatternProps::isWhiteSpace(string.charAt(UPRV_INDEX_2 - 1))) { + lengthInfix += string.insertCodePoint(UPRV_INDEX_2, u'\u0020', kUndefinedField, status); + } + } + } + + length1 += NumberFormatterImpl::writeNumber(micros1.simple, data.quantity1, string, UPRV_INDEX_0, status); + // ICU-21684: Write the second number to a temp string to avoid repeated insert operations + FormattedStringBuilder tempString; + NumberFormatterImpl::writeNumber(micros2.simple, data.quantity2, tempString, 0, status); + length2 += string.insert(UPRV_INDEX_2, tempString, status); + + // TODO: Support padding? + + if (collapseInner) { + const Modifier& mod = resolveModifierPlurals(*micros1.modInner, *micros2.modInner); + lengthSuffix += mod.apply(string, UPRV_INDEX_0, UPRV_INDEX_4, status); + lengthPrefix += mod.getPrefixLength(); + lengthSuffix -= mod.getPrefixLength(); + } else { + length1 += micros1.modInner->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status); + length2 += micros2.modInner->apply(string, UPRV_INDEX_2, UPRV_INDEX_4, status); + } + + if (collapseMiddle) { + const Modifier& mod = resolveModifierPlurals(*micros1.modMiddle, *micros2.modMiddle); + lengthSuffix += mod.apply(string, UPRV_INDEX_0, UPRV_INDEX_4, status); + lengthPrefix += mod.getPrefixLength(); + lengthSuffix -= mod.getPrefixLength(); + } else { + length1 += micros1.modMiddle->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status); + length2 += micros2.modMiddle->apply(string, UPRV_INDEX_2, UPRV_INDEX_4, status); + } + + if (collapseOuter) { + const Modifier& mod = resolveModifierPlurals(*micros1.modOuter, *micros2.modOuter); + lengthSuffix += mod.apply(string, UPRV_INDEX_0, UPRV_INDEX_4, status); + lengthPrefix += mod.getPrefixLength(); + lengthSuffix -= mod.getPrefixLength(); + } else { + length1 += micros1.modOuter->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status); + length2 += micros2.modOuter->apply(string, UPRV_INDEX_2, UPRV_INDEX_4, status); + } + + // Now that all pieces are added, save the span info. + data.appendSpanInfo(UFIELD_CATEGORY_NUMBER_RANGE_SPAN, 0, UPRV_INDEX_0, length1, status); + data.appendSpanInfo(UFIELD_CATEGORY_NUMBER_RANGE_SPAN, 1, UPRV_INDEX_2, length2, status); +} + + +const Modifier& +NumberRangeFormatterImpl::resolveModifierPlurals(const Modifier& first, const Modifier& second) const { + Modifier::Parameters parameters; + first.getParameters(parameters); + if (parameters.obj == nullptr) { + // No plural form; return a fallback (e.g., the first) + return first; + } + StandardPlural::Form firstPlural = parameters.plural; + + second.getParameters(parameters); + if (parameters.obj == nullptr) { + // No plural form; return a fallback (e.g., the first) + return first; + } + StandardPlural::Form secondPlural = parameters.plural; + + // Get the required plural form from data + StandardPlural::Form resultPlural = fPluralRanges.resolve(firstPlural, secondPlural); + + // Get and return the new Modifier + const Modifier* mod = parameters.obj->getModifier(parameters.signum, resultPlural); + U_ASSERT(mod != nullptr); + return *mod; +} + + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numrange_impl.h b/deps/icu-small/source/i18n/numrange_impl.h index ac1d8a58972743..445b10050fc154 100644 --- a/deps/icu-small/source/i18n/numrange_impl.h +++ b/deps/icu-small/source/i18n/numrange_impl.h @@ -1,89 +1,89 @@ -// © 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_NUMRANGE_TYPES_H__ -#define __SOURCE_NUMRANGE_TYPES_H__ - -#include "unicode/numberformatter.h" -#include "unicode/numberrangeformatter.h" -#include "unicode/simpleformatter.h" -#include "number_types.h" -#include "number_decimalquantity.h" -#include "number_formatimpl.h" -#include "formatted_string_builder.h" -#include "formattedval_impl.h" -#include "pluralranges.h" - -U_NAMESPACE_BEGIN namespace number { -namespace impl { - - -/** - * Class similar to UFormattedNumberData. - * - * Has incomplete magic number logic that will need to be finished - * if this is to be exposed as C API in the future. - * - * Possible magic number: 0x46445200 - * Reads in ASCII as "FDR" (FormatteDnumberRange with room at the end) - */ -class UFormattedNumberRangeData : public FormattedValueStringBuilderImpl { -public: - UFormattedNumberRangeData() : FormattedValueStringBuilderImpl(kUndefinedField) {} - virtual ~UFormattedNumberRangeData(); - - DecimalQuantity quantity1; - DecimalQuantity quantity2; - UNumberRangeIdentityResult identityResult = UNUM_IDENTITY_RESULT_COUNT; -}; - - -class NumberRangeFormatterImpl : public UMemory { - public: - NumberRangeFormatterImpl(const RangeMacroProps& macros, UErrorCode& status); - - void format(UFormattedNumberRangeData& data, bool equalBeforeRounding, UErrorCode& status) const; - - private: - NumberFormatterImpl formatterImpl1; - NumberFormatterImpl formatterImpl2; - bool fSameFormatters; - - UNumberRangeCollapse fCollapse; - UNumberRangeIdentityFallback fIdentityFallback; - - SimpleFormatter fRangeFormatter; - NumberFormatterImpl fApproximatelyFormatter; - - StandardPluralRanges fPluralRanges; - - void formatSingleValue(UFormattedNumberRangeData& data, - MicroProps& micros1, MicroProps& micros2, - UErrorCode& status) const; - - void formatApproximately(UFormattedNumberRangeData& data, - MicroProps& micros1, MicroProps& micros2, - UErrorCode& status) const; - - void formatRange(UFormattedNumberRangeData& data, - MicroProps& micros1, MicroProps& micros2, - UErrorCode& status) const; - - const Modifier& resolveModifierPlurals(const Modifier& first, const Modifier& second) const; -}; - - -/** Helper function used in upluralrules.cpp */ -const UFormattedNumberRangeData* validateUFormattedNumberRange( - const UFormattedNumberRange* uresult, UErrorCode& status); - - -} // namespace impl -} // namespace number -U_NAMESPACE_END - -#endif //__SOURCE_NUMRANGE_TYPES_H__ -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 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_NUMRANGE_TYPES_H__ +#define __SOURCE_NUMRANGE_TYPES_H__ + +#include "unicode/numberformatter.h" +#include "unicode/numberrangeformatter.h" +#include "unicode/simpleformatter.h" +#include "number_types.h" +#include "number_decimalquantity.h" +#include "number_formatimpl.h" +#include "formatted_string_builder.h" +#include "formattedval_impl.h" +#include "pluralranges.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + + +/** + * Class similar to UFormattedNumberData. + * + * Has incomplete magic number logic that will need to be finished + * if this is to be exposed as C API in the future. + * + * Possible magic number: 0x46445200 + * Reads in ASCII as "FDR" (FormatteDnumberRange with room at the end) + */ +class UFormattedNumberRangeData : public FormattedValueStringBuilderImpl { +public: + UFormattedNumberRangeData() : FormattedValueStringBuilderImpl(kUndefinedField) {} + virtual ~UFormattedNumberRangeData(); + + DecimalQuantity quantity1; + DecimalQuantity quantity2; + UNumberRangeIdentityResult identityResult = UNUM_IDENTITY_RESULT_COUNT; +}; + + +class NumberRangeFormatterImpl : public UMemory { + public: + NumberRangeFormatterImpl(const RangeMacroProps& macros, UErrorCode& status); + + void format(UFormattedNumberRangeData& data, bool equalBeforeRounding, UErrorCode& status) const; + + private: + NumberFormatterImpl formatterImpl1; + NumberFormatterImpl formatterImpl2; + bool fSameFormatters; + + UNumberRangeCollapse fCollapse; + UNumberRangeIdentityFallback fIdentityFallback; + + SimpleFormatter fRangeFormatter; + NumberFormatterImpl fApproximatelyFormatter; + + StandardPluralRanges fPluralRanges; + + void formatSingleValue(UFormattedNumberRangeData& data, + MicroProps& micros1, MicroProps& micros2, + UErrorCode& status) const; + + void formatApproximately(UFormattedNumberRangeData& data, + MicroProps& micros1, MicroProps& micros2, + UErrorCode& status) const; + + void formatRange(UFormattedNumberRangeData& data, + MicroProps& micros1, MicroProps& micros2, + UErrorCode& status) const; + + const Modifier& resolveModifierPlurals(const Modifier& first, const Modifier& second) const; +}; + + +/** Helper function used in upluralrules.cpp */ +const UFormattedNumberRangeData* validateUFormattedNumberRange( + const UFormattedNumberRange* uresult, UErrorCode& status); + + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif //__SOURCE_NUMRANGE_TYPES_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numsys.cpp b/deps/icu-small/source/i18n/numsys.cpp index 015d2a4cb69a6e..bb4e86d9dcb0b3 100644 --- a/deps/icu-small/source/i18n/numsys.cpp +++ b/deps/icu-small/source/i18n/numsys.cpp @@ -1,362 +1,362 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2010-2015, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* -* File NUMSYS.CPP -* -* Modification History:* -* Date Name Description -* -******************************************************************************** -*/ - -#include "unicode/utypes.h" -#include "unicode/localpointer.h" -#include "unicode/uchar.h" -#include "unicode/unistr.h" -#include "unicode/ures.h" -#include "unicode/ustring.h" -#include "unicode/uloc.h" -#include "unicode/schriter.h" -#include "unicode/numsys.h" -#include "cstring.h" -#include "uassert.h" -#include "ucln_in.h" -#include "umutex.h" -#include "uresimp.h" -#include "numsys_impl.h" - -#if !UCONFIG_NO_FORMATTING - -U_NAMESPACE_BEGIN - -// Useful constants - -#define DEFAULT_DIGITS UNICODE_STRING_SIMPLE("0123456789") -static const char gNumberingSystems[] = "numberingSystems"; -static const char gNumberElements[] = "NumberElements"; -static const char gDefault[] = "default"; -static const char gNative[] = "native"; -static const char gTraditional[] = "traditional"; -static const char gFinance[] = "finance"; -static const char gDesc[] = "desc"; -static const char gRadix[] = "radix"; -static const char gAlgorithmic[] = "algorithmic"; -static const char gLatn[] = "latn"; - - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumberingSystem) -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumsysNameEnumeration) - - /** - * Default Constructor. - * - * @draft ICU 4.2 - */ - -NumberingSystem::NumberingSystem() { - radix = 10; - algorithmic = false; - UnicodeString defaultDigits = DEFAULT_DIGITS; - desc.setTo(defaultDigits); - uprv_strcpy(name,gLatn); -} - - /** - * Copy constructor. - * @draft ICU 4.2 - */ - -NumberingSystem::NumberingSystem(const NumberingSystem& other) -: UObject(other) { - *this=other; -} - -NumberingSystem* U_EXPORT2 -NumberingSystem::createInstance(int32_t radix_in, UBool isAlgorithmic_in, const UnicodeString & desc_in, UErrorCode &status) { - - if (U_FAILURE(status)) { - return nullptr; - } - - if ( radix_in < 2 ) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - - if ( !isAlgorithmic_in ) { - if ( desc_in.countChar32() != radix_in ) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - } - - LocalPointer ns(new NumberingSystem(), status); - if (U_FAILURE(status)) { - return nullptr; - } - - ns->setRadix(radix_in); - ns->setDesc(desc_in); - ns->setAlgorithmic(isAlgorithmic_in); - ns->setName(nullptr); - - return ns.orphan(); -} - -NumberingSystem* U_EXPORT2 -NumberingSystem::createInstance(const Locale & inLocale, UErrorCode& status) { - - if (U_FAILURE(status)) { - return nullptr; - } - - UBool nsResolved = true; - UBool usingFallback = false; - char buffer[ULOC_KEYWORDS_CAPACITY] = ""; - int32_t count = inLocale.getKeywordValue("numbers", buffer, sizeof(buffer), status); - if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { - // the "numbers" keyword exceeds ULOC_KEYWORDS_CAPACITY; ignore and use default. - count = 0; - status = U_ZERO_ERROR; - } - if ( count > 0 ) { // @numbers keyword was specified in the locale - U_ASSERT(count < ULOC_KEYWORDS_CAPACITY); - buffer[count] = '\0'; // Make sure it is null terminated. - if ( !uprv_strcmp(buffer,gDefault) || !uprv_strcmp(buffer,gNative) || - !uprv_strcmp(buffer,gTraditional) || !uprv_strcmp(buffer,gFinance)) { - nsResolved = false; - } - } else { - uprv_strcpy(buffer, gDefault); - nsResolved = false; - } - - if (!nsResolved) { // Resolve the numbering system ( default, native, traditional or finance ) into a "real" numbering system - UErrorCode localStatus = U_ZERO_ERROR; - LocalUResourceBundlePointer resource(ures_open(nullptr, inLocale.getName(), &localStatus)); - LocalUResourceBundlePointer numberElementsRes(ures_getByKey(resource.getAlias(), gNumberElements, nullptr, &localStatus)); - // Don't stomp on the catastrophic failure of OOM. - if (localStatus == U_MEMORY_ALLOCATION_ERROR) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - while (!nsResolved) { - localStatus = U_ZERO_ERROR; - count = 0; - const UChar *nsName = ures_getStringByKeyWithFallback(numberElementsRes.getAlias(), buffer, &count, &localStatus); - // Don't stomp on the catastrophic failure of OOM. - if (localStatus == U_MEMORY_ALLOCATION_ERROR) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - if ( count > 0 && count < ULOC_KEYWORDS_CAPACITY ) { // numbering system found - u_UCharsToChars(nsName, buffer, count); - buffer[count] = '\0'; // Make sure it is null terminated. - nsResolved = true; - } - - if (!nsResolved) { // Fallback behavior per TR35 - traditional falls back to native, finance and native fall back to default - if (!uprv_strcmp(buffer,gNative) || !uprv_strcmp(buffer,gFinance)) { - uprv_strcpy(buffer,gDefault); - } else if (!uprv_strcmp(buffer,gTraditional)) { - uprv_strcpy(buffer,gNative); - } else { // If we get here we couldn't find even the default numbering system - usingFallback = true; - nsResolved = true; - } - } - } - } - - if (usingFallback) { - status = U_USING_FALLBACK_WARNING; - NumberingSystem *ns = new NumberingSystem(); - if (ns == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return ns; - } else { - return NumberingSystem::createInstanceByName(buffer, status); - } - } - -NumberingSystem* U_EXPORT2 -NumberingSystem::createInstance(UErrorCode& status) { - return NumberingSystem::createInstance(Locale::getDefault(), status); -} - -NumberingSystem* U_EXPORT2 -NumberingSystem::createInstanceByName(const char *name, UErrorCode& status) { - int32_t radix = 10; - int32_t algorithmic = 0; - - LocalUResourceBundlePointer numberingSystemsInfo(ures_openDirect(nullptr, gNumberingSystems, &status)); - LocalUResourceBundlePointer nsCurrent(ures_getByKey(numberingSystemsInfo.getAlias(), gNumberingSystems, nullptr, &status)); - LocalUResourceBundlePointer nsTop(ures_getByKey(nsCurrent.getAlias(), name, nullptr, &status)); - - UnicodeString nsd = ures_getUnicodeStringByKey(nsTop.getAlias(), gDesc, &status); - - ures_getByKey(nsTop.getAlias(), gRadix, nsCurrent.getAlias(), &status); - radix = ures_getInt(nsCurrent.getAlias(), &status); - - ures_getByKey(nsTop.getAlias(), gAlgorithmic, nsCurrent.getAlias(), &status); - algorithmic = ures_getInt(nsCurrent.getAlias(), &status); - - UBool isAlgorithmic = ( algorithmic == 1 ); - - if (U_FAILURE(status)) { - // Don't stomp on the catastrophic failure of OOM. - if (status != U_MEMORY_ALLOCATION_ERROR) { - status = U_UNSUPPORTED_ERROR; - } - return nullptr; - } - - LocalPointer ns(NumberingSystem::createInstance(radix, isAlgorithmic, nsd, status), status); - if (U_FAILURE(status)) { - return nullptr; - } - ns->setName(name); - return ns.orphan(); -} - - /** - * Destructor. - * @draft ICU 4.2 - */ -NumberingSystem::~NumberingSystem() { -} - -int32_t NumberingSystem::getRadix() const { - return radix; -} - -UnicodeString NumberingSystem::getDescription() const { - return desc; -} - -const char * NumberingSystem::getName() const { - return name; -} - -void NumberingSystem::setRadix(int32_t r) { - radix = r; -} - -void NumberingSystem::setAlgorithmic(UBool c) { - algorithmic = c; -} - -void NumberingSystem::setDesc(const UnicodeString &d) { - desc.setTo(d); -} -void NumberingSystem::setName(const char *n) { - if ( n == nullptr ) { - name[0] = (char) 0; - } else { - uprv_strncpy(name,n,kInternalNumSysNameCapacity); - name[kInternalNumSysNameCapacity] = '\0'; // Make sure it is null terminated. - } -} -UBool NumberingSystem::isAlgorithmic() const { - return ( algorithmic ); -} - -namespace { - -UVector* gNumsysNames = nullptr; -UInitOnce gNumSysInitOnce {}; - -U_CFUNC UBool U_CALLCONV numSysCleanup() { - delete gNumsysNames; - gNumsysNames = nullptr; - gNumSysInitOnce.reset(); - return true; -} - -U_CFUNC void initNumsysNames(UErrorCode &status) { - U_ASSERT(gNumsysNames == nullptr); - ucln_i18n_registerCleanup(UCLN_I18N_NUMSYS, numSysCleanup); - - // TODO: Simple array of UnicodeString objects, based on length of table resource? - LocalPointer numsysNames(new UVector(uprv_deleteUObject, nullptr, status), status); - if (U_FAILURE(status)) { - return; - } - - UErrorCode rbstatus = U_ZERO_ERROR; - UResourceBundle *numberingSystemsInfo = ures_openDirect(nullptr, "numberingSystems", &rbstatus); - numberingSystemsInfo = - ures_getByKey(numberingSystemsInfo, "numberingSystems", numberingSystemsInfo, &rbstatus); - if (U_FAILURE(rbstatus)) { - // Don't stomp on the catastrophic failure of OOM. - if (rbstatus == U_MEMORY_ALLOCATION_ERROR) { - status = rbstatus; - } else { - status = U_MISSING_RESOURCE_ERROR; - } - ures_close(numberingSystemsInfo); - return; - } - - while ( ures_hasNext(numberingSystemsInfo) && U_SUCCESS(status) ) { - LocalUResourceBundlePointer nsCurrent(ures_getNextResource(numberingSystemsInfo, nullptr, &rbstatus)); - if (rbstatus == U_MEMORY_ALLOCATION_ERROR) { - status = rbstatus; // we want to report OOM failure back to the caller. - break; - } - const char *nsName = ures_getKey(nsCurrent.getAlias()); - LocalPointer newElem(new UnicodeString(nsName, -1, US_INV), status); - numsysNames->adoptElement(newElem.orphan(), status); - } - - ures_close(numberingSystemsInfo); - if (U_SUCCESS(status)) { - gNumsysNames = numsysNames.orphan(); - } - return; -} - -} // end anonymous namespace - -StringEnumeration* NumberingSystem::getAvailableNames(UErrorCode &status) { - umtx_initOnce(gNumSysInitOnce, &initNumsysNames, status); - LocalPointer result(new NumsysNameEnumeration(status), status); - return result.orphan(); -} - -NumsysNameEnumeration::NumsysNameEnumeration(UErrorCode& status) : pos(0) { - (void)status; -} - -const UnicodeString* -NumsysNameEnumeration::snext(UErrorCode& status) { - if (U_SUCCESS(status) && (gNumsysNames != nullptr) && (pos < gNumsysNames->size())) { - return (const UnicodeString*)gNumsysNames->elementAt(pos++); - } - return nullptr; -} - -void -NumsysNameEnumeration::reset(UErrorCode& /*status*/) { - pos=0; -} - -int32_t -NumsysNameEnumeration::count(UErrorCode& /*status*/) const { - return (gNumsysNames==nullptr) ? 0 : gNumsysNames->size(); -} - -NumsysNameEnumeration::~NumsysNameEnumeration() { -} -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2010-2015, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* +* File NUMSYS.CPP +* +* Modification History:* +* Date Name Description +* +******************************************************************************** +*/ + +#include "unicode/utypes.h" +#include "unicode/localpointer.h" +#include "unicode/uchar.h" +#include "unicode/unistr.h" +#include "unicode/ures.h" +#include "unicode/ustring.h" +#include "unicode/uloc.h" +#include "unicode/schriter.h" +#include "unicode/numsys.h" +#include "cstring.h" +#include "uassert.h" +#include "ucln_in.h" +#include "umutex.h" +#include "uresimp.h" +#include "numsys_impl.h" + +#if !UCONFIG_NO_FORMATTING + +U_NAMESPACE_BEGIN + +// Useful constants + +#define DEFAULT_DIGITS UNICODE_STRING_SIMPLE("0123456789") +static const char gNumberingSystems[] = "numberingSystems"; +static const char gNumberElements[] = "NumberElements"; +static const char gDefault[] = "default"; +static const char gNative[] = "native"; +static const char gTraditional[] = "traditional"; +static const char gFinance[] = "finance"; +static const char gDesc[] = "desc"; +static const char gRadix[] = "radix"; +static const char gAlgorithmic[] = "algorithmic"; +static const char gLatn[] = "latn"; + + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumberingSystem) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumsysNameEnumeration) + + /** + * Default Constructor. + * + * @draft ICU 4.2 + */ + +NumberingSystem::NumberingSystem() { + radix = 10; + algorithmic = false; + UnicodeString defaultDigits = DEFAULT_DIGITS; + desc.setTo(defaultDigits); + uprv_strcpy(name,gLatn); +} + + /** + * Copy constructor. + * @draft ICU 4.2 + */ + +NumberingSystem::NumberingSystem(const NumberingSystem& other) +: UObject(other) { + *this=other; +} + +NumberingSystem* U_EXPORT2 +NumberingSystem::createInstance(int32_t radix_in, UBool isAlgorithmic_in, const UnicodeString & desc_in, UErrorCode &status) { + + if (U_FAILURE(status)) { + return nullptr; + } + + if ( radix_in < 2 ) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + if ( !isAlgorithmic_in ) { + if ( desc_in.countChar32() != radix_in ) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + } + + LocalPointer ns(new NumberingSystem(), status); + if (U_FAILURE(status)) { + return nullptr; + } + + ns->setRadix(radix_in); + ns->setDesc(desc_in); + ns->setAlgorithmic(isAlgorithmic_in); + ns->setName(nullptr); + + return ns.orphan(); +} + +NumberingSystem* U_EXPORT2 +NumberingSystem::createInstance(const Locale & inLocale, UErrorCode& status) { + + if (U_FAILURE(status)) { + return nullptr; + } + + UBool nsResolved = true; + UBool usingFallback = false; + char buffer[ULOC_KEYWORDS_CAPACITY] = ""; + int32_t count = inLocale.getKeywordValue("numbers", buffer, sizeof(buffer), status); + if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { + // the "numbers" keyword exceeds ULOC_KEYWORDS_CAPACITY; ignore and use default. + count = 0; + status = U_ZERO_ERROR; + } + if ( count > 0 ) { // @numbers keyword was specified in the locale + U_ASSERT(count < ULOC_KEYWORDS_CAPACITY); + buffer[count] = '\0'; // Make sure it is null terminated. + if ( !uprv_strcmp(buffer,gDefault) || !uprv_strcmp(buffer,gNative) || + !uprv_strcmp(buffer,gTraditional) || !uprv_strcmp(buffer,gFinance)) { + nsResolved = false; + } + } else { + uprv_strcpy(buffer, gDefault); + nsResolved = false; + } + + if (!nsResolved) { // Resolve the numbering system ( default, native, traditional or finance ) into a "real" numbering system + UErrorCode localStatus = U_ZERO_ERROR; + LocalUResourceBundlePointer resource(ures_open(nullptr, inLocale.getName(), &localStatus)); + LocalUResourceBundlePointer numberElementsRes(ures_getByKey(resource.getAlias(), gNumberElements, nullptr, &localStatus)); + // Don't stomp on the catastrophic failure of OOM. + if (localStatus == U_MEMORY_ALLOCATION_ERROR) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + while (!nsResolved) { + localStatus = U_ZERO_ERROR; + count = 0; + const char16_t *nsName = ures_getStringByKeyWithFallback(numberElementsRes.getAlias(), buffer, &count, &localStatus); + // Don't stomp on the catastrophic failure of OOM. + if (localStatus == U_MEMORY_ALLOCATION_ERROR) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + if ( count > 0 && count < ULOC_KEYWORDS_CAPACITY ) { // numbering system found + u_UCharsToChars(nsName, buffer, count); + buffer[count] = '\0'; // Make sure it is null terminated. + nsResolved = true; + } + + if (!nsResolved) { // Fallback behavior per TR35 - traditional falls back to native, finance and native fall back to default + if (!uprv_strcmp(buffer,gNative) || !uprv_strcmp(buffer,gFinance)) { + uprv_strcpy(buffer,gDefault); + } else if (!uprv_strcmp(buffer,gTraditional)) { + uprv_strcpy(buffer,gNative); + } else { // If we get here we couldn't find even the default numbering system + usingFallback = true; + nsResolved = true; + } + } + } + } + + if (usingFallback) { + status = U_USING_FALLBACK_WARNING; + NumberingSystem *ns = new NumberingSystem(); + if (ns == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return ns; + } else { + return NumberingSystem::createInstanceByName(buffer, status); + } + } + +NumberingSystem* U_EXPORT2 +NumberingSystem::createInstance(UErrorCode& status) { + return NumberingSystem::createInstance(Locale::getDefault(), status); +} + +NumberingSystem* U_EXPORT2 +NumberingSystem::createInstanceByName(const char *name, UErrorCode& status) { + int32_t radix = 10; + int32_t algorithmic = 0; + + LocalUResourceBundlePointer numberingSystemsInfo(ures_openDirect(nullptr, gNumberingSystems, &status)); + LocalUResourceBundlePointer nsCurrent(ures_getByKey(numberingSystemsInfo.getAlias(), gNumberingSystems, nullptr, &status)); + LocalUResourceBundlePointer nsTop(ures_getByKey(nsCurrent.getAlias(), name, nullptr, &status)); + + UnicodeString nsd = ures_getUnicodeStringByKey(nsTop.getAlias(), gDesc, &status); + + ures_getByKey(nsTop.getAlias(), gRadix, nsCurrent.getAlias(), &status); + radix = ures_getInt(nsCurrent.getAlias(), &status); + + ures_getByKey(nsTop.getAlias(), gAlgorithmic, nsCurrent.getAlias(), &status); + algorithmic = ures_getInt(nsCurrent.getAlias(), &status); + + UBool isAlgorithmic = ( algorithmic == 1 ); + + if (U_FAILURE(status)) { + // Don't stomp on the catastrophic failure of OOM. + if (status != U_MEMORY_ALLOCATION_ERROR) { + status = U_UNSUPPORTED_ERROR; + } + return nullptr; + } + + LocalPointer ns(NumberingSystem::createInstance(radix, isAlgorithmic, nsd, status), status); + if (U_FAILURE(status)) { + return nullptr; + } + ns->setName(name); + return ns.orphan(); +} + + /** + * Destructor. + * @draft ICU 4.2 + */ +NumberingSystem::~NumberingSystem() { +} + +int32_t NumberingSystem::getRadix() const { + return radix; +} + +UnicodeString NumberingSystem::getDescription() const { + return desc; +} + +const char * NumberingSystem::getName() const { + return name; +} + +void NumberingSystem::setRadix(int32_t r) { + radix = r; +} + +void NumberingSystem::setAlgorithmic(UBool c) { + algorithmic = c; +} + +void NumberingSystem::setDesc(const UnicodeString &d) { + desc.setTo(d); +} +void NumberingSystem::setName(const char *n) { + if ( n == nullptr ) { + name[0] = (char) 0; + } else { + uprv_strncpy(name,n,kInternalNumSysNameCapacity); + name[kInternalNumSysNameCapacity] = '\0'; // Make sure it is null terminated. + } +} +UBool NumberingSystem::isAlgorithmic() const { + return ( algorithmic ); +} + +namespace { + +UVector* gNumsysNames = nullptr; +UInitOnce gNumSysInitOnce {}; + +U_CFUNC UBool U_CALLCONV numSysCleanup() { + delete gNumsysNames; + gNumsysNames = nullptr; + gNumSysInitOnce.reset(); + return true; +} + +U_CFUNC void initNumsysNames(UErrorCode &status) { + U_ASSERT(gNumsysNames == nullptr); + ucln_i18n_registerCleanup(UCLN_I18N_NUMSYS, numSysCleanup); + + // TODO: Simple array of UnicodeString objects, based on length of table resource? + LocalPointer numsysNames(new UVector(uprv_deleteUObject, nullptr, status), status); + if (U_FAILURE(status)) { + return; + } + + UErrorCode rbstatus = U_ZERO_ERROR; + UResourceBundle *numberingSystemsInfo = ures_openDirect(nullptr, "numberingSystems", &rbstatus); + numberingSystemsInfo = + ures_getByKey(numberingSystemsInfo, "numberingSystems", numberingSystemsInfo, &rbstatus); + if (U_FAILURE(rbstatus)) { + // Don't stomp on the catastrophic failure of OOM. + if (rbstatus == U_MEMORY_ALLOCATION_ERROR) { + status = rbstatus; + } else { + status = U_MISSING_RESOURCE_ERROR; + } + ures_close(numberingSystemsInfo); + return; + } + + while ( ures_hasNext(numberingSystemsInfo) && U_SUCCESS(status) ) { + LocalUResourceBundlePointer nsCurrent(ures_getNextResource(numberingSystemsInfo, nullptr, &rbstatus)); + if (rbstatus == U_MEMORY_ALLOCATION_ERROR) { + status = rbstatus; // we want to report OOM failure back to the caller. + break; + } + const char *nsName = ures_getKey(nsCurrent.getAlias()); + LocalPointer newElem(new UnicodeString(nsName, -1, US_INV), status); + numsysNames->adoptElement(newElem.orphan(), status); + } + + ures_close(numberingSystemsInfo); + if (U_SUCCESS(status)) { + gNumsysNames = numsysNames.orphan(); + } + return; +} + +} // end anonymous namespace + +StringEnumeration* NumberingSystem::getAvailableNames(UErrorCode &status) { + umtx_initOnce(gNumSysInitOnce, &initNumsysNames, status); + LocalPointer result(new NumsysNameEnumeration(status), status); + return result.orphan(); +} + +NumsysNameEnumeration::NumsysNameEnumeration(UErrorCode& status) : pos(0) { + (void)status; +} + +const UnicodeString* +NumsysNameEnumeration::snext(UErrorCode& status) { + if (U_SUCCESS(status) && (gNumsysNames != nullptr) && (pos < gNumsysNames->size())) { + return (const UnicodeString*)gNumsysNames->elementAt(pos++); + } + return nullptr; +} + +void +NumsysNameEnumeration::reset(UErrorCode& /*status*/) { + pos=0; +} + +int32_t +NumsysNameEnumeration::count(UErrorCode& /*status*/) const { + return (gNumsysNames==nullptr) ? 0 : gNumsysNames->size(); +} + +NumsysNameEnumeration::~NumsysNameEnumeration() { +} +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/numsys_impl.h b/deps/icu-small/source/i18n/numsys_impl.h index e76e634dd23c07..66f72f12a03fe0 100644 --- a/deps/icu-small/source/i18n/numsys_impl.h +++ b/deps/icu-small/source/i18n/numsys_impl.h @@ -1,45 +1,45 @@ -// © 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 NUMSYS_IMPL.H -* -******************************************************************************* -*/ - -#ifndef __NUMSYS_IMPL_H__ -#define __NUMSYS_IMPL_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/numsys.h" -#include "uvector.h" -#include "unicode/strenum.h" - -U_NAMESPACE_BEGIN - -class NumsysNameEnumeration : public StringEnumeration { -public: - NumsysNameEnumeration(UErrorCode& status); - - virtual ~NumsysNameEnumeration(); - static UClassID U_EXPORT2 getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; - virtual const UnicodeString* snext(UErrorCode& status) override; - virtual void reset(UErrorCode& status) override; - virtual int32_t count(UErrorCode& status) const override; -private: - int32_t pos; -}; - -U_NAMESPACE_END - -#endif - -#endif +// © 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 NUMSYS_IMPL.H +* +******************************************************************************* +*/ + +#ifndef __NUMSYS_IMPL_H__ +#define __NUMSYS_IMPL_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/numsys.h" +#include "uvector.h" +#include "unicode/strenum.h" + +U_NAMESPACE_BEGIN + +class NumsysNameEnumeration : public StringEnumeration { +public: + NumsysNameEnumeration(UErrorCode& status); + + virtual ~NumsysNameEnumeration(); + static UClassID U_EXPORT2 getStaticClassID(); + virtual UClassID getDynamicClassID() const override; + virtual const UnicodeString* snext(UErrorCode& status) override; + virtual void reset(UErrorCode& status) override; + virtual int32_t count(UErrorCode& status) const override; +private: + int32_t pos; +}; + +U_NAMESPACE_END + +#endif + +#endif diff --git a/deps/icu-small/source/i18n/olsontz.cpp b/deps/icu-small/source/i18n/olsontz.cpp index e5c60f8cbe0ae8..82899f2f8ed10c 100644 --- a/deps/icu-small/source/i18n/olsontz.cpp +++ b/deps/icu-small/source/i18n/olsontz.cpp @@ -1,1082 +1,1082 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2003-2013, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: July 21 2003 -* Since: ICU 2.8 -********************************************************************** -*/ - -#include "utypeinfo.h" // for 'typeid' to work - -#include "olsontz.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/ures.h" -#include "unicode/simpletz.h" -#include "unicode/gregocal.h" -#include "gregoimp.h" -#include "cmemory.h" -#include "uassert.h" -#include "uvector.h" -#include // DBL_MAX -#include "uresimp.h" -#include "zonemeta.h" -#include "umutex.h" - -#ifdef U_DEBUG_TZ -# include -# include "uresimp.h" // for debugging - -static void debug_tz_loc(const char *f, int32_t l) -{ - fprintf(stderr, "%s:%d: ", f, l); -} - -static void debug_tz_msg(const char *pat, ...) -{ - va_list ap; - va_start(ap, pat); - vfprintf(stderr, pat, ap); - fflush(stderr); -} -// must use double parens, i.e.: U_DEBUG_TZ_MSG(("four is: %d",4)); -#define U_DEBUG_TZ_MSG(x) {debug_tz_loc(__FILE__,__LINE__);debug_tz_msg x;} -#else -#define U_DEBUG_TZ_MSG(x) -#endif - -static UBool arrayEqual(const void *a1, const void *a2, int32_t size) { - if (a1 == NULL && a2 == NULL) { - return true; - } - if ((a1 != NULL && a2 == NULL) || (a1 == NULL && a2 != NULL)) { - return false; - } - if (a1 == a2) { - return true; - } - - return (uprv_memcmp(a1, a2, size) == 0); -} - -U_NAMESPACE_BEGIN - -#define kTRANS "trans" -#define kTRANSPRE32 "transPre32" -#define kTRANSPOST32 "transPost32" -#define kTYPEOFFSETS "typeOffsets" -#define kTYPEMAP "typeMap" -#define kLINKS "links" -#define kFINALRULE "finalRule" -#define kFINALRAW "finalRaw" -#define kFINALYEAR "finalYear" - -#define SECONDS_PER_DAY (24*60*60) - -static const int32_t ZEROS[] = {0,0}; - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OlsonTimeZone) - -/** - * Default constructor. Creates a time zone with an empty ID and - * a fixed GMT offset of zero. - */ -/*OlsonTimeZone::OlsonTimeZone() : finalYear(INT32_MAX), finalMillis(DBL_MAX), finalZone(0), transitionRulesInitialized(false) { - clearTransitionRules(); - constructEmpty(); -}*/ - -/** - * Construct a GMT+0 zone with no transitions. This is done when a - * constructor fails so the resultant object is well-behaved. - */ -void OlsonTimeZone::constructEmpty() { - canonicalID = NULL; - - transitionCountPre32 = transitionCount32 = transitionCountPost32 = 0; - transitionTimesPre32 = transitionTimes32 = transitionTimesPost32 = NULL; - - typeMapData = NULL; - - typeCount = 1; - typeOffsets = ZEROS; - - finalZone = NULL; -} - -/** - * Construct from a resource bundle - * @param top the top-level zoneinfo resource bundle. This is used - * to lookup the rule that `res' may refer to, if there is one. - * @param res the resource bundle of the zone to be constructed - * @param ec input-output error code - */ -OlsonTimeZone::OlsonTimeZone(const UResourceBundle* top, - const UResourceBundle* res, - const UnicodeString& tzid, - UErrorCode& ec) : - BasicTimeZone(tzid), finalZone(NULL) -{ - clearTransitionRules(); - U_DEBUG_TZ_MSG(("OlsonTimeZone(%s)\n", ures_getKey((UResourceBundle*)res))); - if ((top == NULL || res == NULL) && U_SUCCESS(ec)) { - ec = U_ILLEGAL_ARGUMENT_ERROR; - } - if (U_SUCCESS(ec)) { - // TODO -- clean up -- Doesn't work if res points to an alias - // // TODO remove nonconst casts below when ures_* API is fixed - // setID(ures_getKey((UResourceBundle*) res)); // cast away const - - int32_t len; - StackUResourceBundle r; - - // Pre-32bit second transitions - ures_getByKey(res, kTRANSPRE32, r.getAlias(), &ec); - transitionTimesPre32 = ures_getIntVector(r.getAlias(), &len, &ec); - transitionCountPre32 = static_cast(len >> 1); - if (ec == U_MISSING_RESOURCE_ERROR) { - // No pre-32bit transitions - transitionTimesPre32 = NULL; - transitionCountPre32 = 0; - ec = U_ZERO_ERROR; - } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF || (len & 1) != 0) /* len must be even */) { - ec = U_INVALID_FORMAT_ERROR; - } - - // 32bit second transitions - ures_getByKey(res, kTRANS, r.getAlias(), &ec); - transitionTimes32 = ures_getIntVector(r.getAlias(), &len, &ec); - transitionCount32 = static_cast(len); - if (ec == U_MISSING_RESOURCE_ERROR) { - // No 32bit transitions - transitionTimes32 = NULL; - transitionCount32 = 0; - ec = U_ZERO_ERROR; - } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF)) { - ec = U_INVALID_FORMAT_ERROR; - } - - // Post-32bit second transitions - ures_getByKey(res, kTRANSPOST32, r.getAlias(), &ec); - transitionTimesPost32 = ures_getIntVector(r.getAlias(), &len, &ec); - transitionCountPost32 = static_cast(len >> 1); - if (ec == U_MISSING_RESOURCE_ERROR) { - // No pre-32bit transitions - transitionTimesPost32 = NULL; - transitionCountPost32 = 0; - ec = U_ZERO_ERROR; - } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF || (len & 1) != 0) /* len must be even */) { - ec = U_INVALID_FORMAT_ERROR; - } - - // Type offsets list must be of even size, with size >= 2 - ures_getByKey(res, kTYPEOFFSETS, r.getAlias(), &ec); - typeOffsets = ures_getIntVector(r.getAlias(), &len, &ec); - if (U_SUCCESS(ec) && (len < 2 || len > 0x7FFE || (len & 1) != 0)) { - ec = U_INVALID_FORMAT_ERROR; - } - typeCount = (int16_t) len >> 1; - - // Type map data must be of the same size as the transition count - typeMapData = NULL; - if (transitionCount() > 0) { - ures_getByKey(res, kTYPEMAP, r.getAlias(), &ec); - typeMapData = ures_getBinary(r.getAlias(), &len, &ec); - if (ec == U_MISSING_RESOURCE_ERROR) { - // no type mapping data - ec = U_INVALID_FORMAT_ERROR; - } else if (U_SUCCESS(ec) && len != transitionCount()) { - ec = U_INVALID_FORMAT_ERROR; - } - } - - // Process final rule and data, if any - if (U_SUCCESS(ec)) { - const UChar *ruleIdUStr = ures_getStringByKey(res, kFINALRULE, &len, &ec); - ures_getByKey(res, kFINALRAW, r.getAlias(), &ec); - int32_t ruleRaw = ures_getInt(r.getAlias(), &ec); - ures_getByKey(res, kFINALYEAR, r.getAlias(), &ec); - int32_t ruleYear = ures_getInt(r.getAlias(), &ec); - if (U_SUCCESS(ec)) { - UnicodeString ruleID(true, ruleIdUStr, len); - UResourceBundle *rule = TimeZone::loadRule(top, ruleID, NULL, ec); - const int32_t *ruleData = ures_getIntVector(rule, &len, &ec); - if (U_SUCCESS(ec) && len == 11) { - UnicodeString emptyStr; - finalZone = new SimpleTimeZone( - ruleRaw * U_MILLIS_PER_SECOND, - emptyStr, - (int8_t)ruleData[0], (int8_t)ruleData[1], (int8_t)ruleData[2], - ruleData[3] * U_MILLIS_PER_SECOND, - (SimpleTimeZone::TimeMode) ruleData[4], - (int8_t)ruleData[5], (int8_t)ruleData[6], (int8_t)ruleData[7], - ruleData[8] * U_MILLIS_PER_SECOND, - (SimpleTimeZone::TimeMode) ruleData[9], - ruleData[10] * U_MILLIS_PER_SECOND, ec); - if (finalZone == NULL) { - ec = U_MEMORY_ALLOCATION_ERROR; - } else { - finalStartYear = ruleYear; - - // Note: Setting finalStartYear to the finalZone is problematic. When a date is around - // year boundary, SimpleTimeZone may return false result when DST is observed at the - // beginning of year. We could apply safe margin (day or two), but when one of recurrent - // rules falls around year boundary, it could return false result. Without setting the - // start year, finalZone works fine around the year boundary of the start year. - - // finalZone->setStartYear(finalStartYear); - - - // Compute the millis for Jan 1, 0:00 GMT of the finalYear - - // Note: finalStartMillis is used for detecting either if - // historic transition data or finalZone to be used. In an - // extreme edge case - for example, two transitions fall into - // small windows of time around the year boundary, this may - // result incorrect offset computation. But I think it will - // never happen practically. Yoshito - Feb 20, 2010 - finalStartMillis = Grego::fieldsToDay(finalStartYear, 0, 1) * U_MILLIS_PER_DAY; - } - } else { - ec = U_INVALID_FORMAT_ERROR; - } - ures_close(rule); - } else if (ec == U_MISSING_RESOURCE_ERROR) { - // No final zone - ec = U_ZERO_ERROR; - } - } - - // initialize canonical ID - canonicalID = ZoneMeta::getCanonicalCLDRID(tzid, ec); - } - - if (U_FAILURE(ec)) { - constructEmpty(); - } -} - -/** - * Copy constructor - */ -OlsonTimeZone::OlsonTimeZone(const OlsonTimeZone& other) : - BasicTimeZone(other), finalZone(0) { - *this = other; -} - -/** - * Assignment operator - */ -OlsonTimeZone& OlsonTimeZone::operator=(const OlsonTimeZone& other) { - if (this == &other) { return *this; } // self-assignment: no-op - canonicalID = other.canonicalID; - - transitionTimesPre32 = other.transitionTimesPre32; - transitionTimes32 = other.transitionTimes32; - transitionTimesPost32 = other.transitionTimesPost32; - - transitionCountPre32 = other.transitionCountPre32; - transitionCount32 = other.transitionCount32; - transitionCountPost32 = other.transitionCountPost32; - - typeCount = other.typeCount; - typeOffsets = other.typeOffsets; - typeMapData = other.typeMapData; - - delete finalZone; - finalZone = (other.finalZone != 0) ? other.finalZone->clone() : 0; - - finalStartYear = other.finalStartYear; - finalStartMillis = other.finalStartMillis; - - clearTransitionRules(); - - return *this; -} - -/** - * Destructor - */ -OlsonTimeZone::~OlsonTimeZone() { - deleteTransitionRules(); - delete finalZone; -} - -/** - * Returns true if the two TimeZone objects are equal. - */ -bool OlsonTimeZone::operator==(const TimeZone& other) const { - return ((this == &other) || - (typeid(*this) == typeid(other) && - TimeZone::operator==(other) && - hasSameRules(other))); -} - -/** - * TimeZone API. - */ -OlsonTimeZone* OlsonTimeZone::clone() const { - return new OlsonTimeZone(*this); -} - -/** - * TimeZone API. - */ -int32_t OlsonTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, - int32_t dom, uint8_t dow, - int32_t millis, UErrorCode& ec) const { - if (month < UCAL_JANUARY || month > UCAL_DECEMBER) { - if (U_SUCCESS(ec)) { - ec = U_ILLEGAL_ARGUMENT_ERROR; - } - return 0; - } else { - return getOffset(era, year, month, dom, dow, millis, - Grego::monthLength(year, month), - ec); - } -} - -/** - * TimeZone API. - */ -int32_t OlsonTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, - int32_t dom, uint8_t dow, - int32_t millis, int32_t monthLength, - UErrorCode& ec) const { - if (U_FAILURE(ec)) { - return 0; - } - - if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC) - || month < UCAL_JANUARY - || month > UCAL_DECEMBER - || dom < 1 - || dom > monthLength - || dow < UCAL_SUNDAY - || dow > UCAL_SATURDAY - || millis < 0 - || millis >= U_MILLIS_PER_DAY - || monthLength < 28 - || monthLength > 31) { - ec = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - if (era == GregorianCalendar::BC) { - year = -year; - } - - if (finalZone != NULL && year >= finalStartYear) { - return finalZone->getOffset(era, year, month, dom, dow, - millis, monthLength, ec); - } - - // Compute local epoch millis from input fields - UDate date = (UDate)(Grego::fieldsToDay(year, month, dom) * U_MILLIS_PER_DAY + millis); - int32_t rawoff, dstoff; - getHistoricalOffset(date, true, kDaylight, kStandard, rawoff, dstoff); - return rawoff + dstoff; -} - -/** - * TimeZone API. - */ -void OlsonTimeZone::getOffset(UDate date, UBool local, int32_t& rawoff, - int32_t& dstoff, UErrorCode& ec) const { - if (U_FAILURE(ec)) { - return; - } - if (finalZone != NULL && date >= finalStartMillis) { - finalZone->getOffset(date, local, rawoff, dstoff, ec); - } else { - getHistoricalOffset(date, local, kFormer, kLatter, rawoff, dstoff); - } -} - -void OlsonTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingTimeOpt, - UTimeZoneLocalOption duplicatedTimeOpt, - int32_t& rawoff, int32_t& dstoff, UErrorCode& ec) const { - if (U_FAILURE(ec)) { - return; - } - if (finalZone != NULL && date >= finalStartMillis) { - finalZone->getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff, ec); - } else { - getHistoricalOffset(date, true, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff); - } -} - - -/** - * TimeZone API. - */ -void OlsonTimeZone::setRawOffset(int32_t /*offsetMillis*/) { - // We don't support this operation, since OlsonTimeZones are - // immutable (except for the ID, which is in the base class). - - // Nothing to do! -} - -/** - * TimeZone API. - */ -int32_t OlsonTimeZone::getRawOffset() const { - UErrorCode ec = U_ZERO_ERROR; - int32_t raw, dst; - getOffset(uprv_getUTCtime(), false, raw, dst, ec); - return raw; -} - -#if defined U_DEBUG_TZ -void printTime(double ms) { - int32_t year, month, dom, dow; - double millis=0; - double days = ClockMath::floorDivide(((double)ms), (double)U_MILLIS_PER_DAY, millis); - - Grego::dayToFields(days, year, month, dom, dow); - U_DEBUG_TZ_MSG((" getHistoricalOffset: time %.1f (%04d.%02d.%02d+%.1fh)\n", ms, - year, month+1, dom, (millis/kOneHour))); - } -#endif - -int64_t -OlsonTimeZone::transitionTimeInSeconds(int16_t transIdx) const { - U_ASSERT(transIdx >= 0 && transIdx < transitionCount()); - - if (transIdx < transitionCountPre32) { - return (((int64_t)((uint32_t)transitionTimesPre32[transIdx << 1])) << 32) - | ((int64_t)((uint32_t)transitionTimesPre32[(transIdx << 1) + 1])); - } - - transIdx -= transitionCountPre32; - if (transIdx < transitionCount32) { - return (int64_t)transitionTimes32[transIdx]; - } - - transIdx -= transitionCount32; - return (((int64_t)((uint32_t)transitionTimesPost32[transIdx << 1])) << 32) - | ((int64_t)((uint32_t)transitionTimesPost32[(transIdx << 1) + 1])); -} - -// Maximum absolute offset in seconds (86400 seconds = 1 day) -// getHistoricalOffset uses this constant as safety margin of -// quick zone transition checking. -#define MAX_OFFSET_SECONDS 86400 - -void -OlsonTimeZone::getHistoricalOffset(UDate date, UBool local, - int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt, - int32_t& rawoff, int32_t& dstoff) const { - U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst)\n", - date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt)); -#if defined U_DEBUG_TZ - printTime(date*1000.0); -#endif - int16_t transCount = transitionCount(); - - if (transCount > 0) { - double sec = uprv_floor(date / U_MILLIS_PER_SECOND); - if (!local && sec < transitionTimeInSeconds(0)) { - // Before the first transition time - rawoff = initialRawOffset() * U_MILLIS_PER_SECOND; - dstoff = initialDstOffset() * U_MILLIS_PER_SECOND; - } else { - // Linear search from the end is the fastest approach, since - // most lookups will happen at/near the end. - int16_t transIdx; - for (transIdx = transCount - 1; transIdx >= 0; transIdx--) { - int64_t transition = transitionTimeInSeconds(transIdx); - - if (local && (sec >= (transition - MAX_OFFSET_SECONDS))) { - int32_t offsetBefore = zoneOffsetAt(transIdx - 1); - UBool dstBefore = dstOffsetAt(transIdx - 1) != 0; - - int32_t offsetAfter = zoneOffsetAt(transIdx); - UBool dstAfter = dstOffsetAt(transIdx) != 0; - - UBool dstToStd = dstBefore && !dstAfter; - UBool stdToDst = !dstBefore && dstAfter; - - if (offsetAfter - offsetBefore >= 0) { - // Positive transition, which makes a non-existing local time range - if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd) - || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) { - transition += offsetBefore; - } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst) - || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) { - transition += offsetAfter; - } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) { - transition += offsetBefore; - } else { - // Interprets the time with rule before the transition, - // default for non-existing time range - transition += offsetAfter; - } - } else { - // Negative transition, which makes a duplicated local time range - if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd) - || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) { - transition += offsetAfter; - } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst) - || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) { - transition += offsetBefore; - } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) { - transition += offsetBefore; - } else { - // Interprets the time with rule after the transition, - // default for duplicated local time range - transition += offsetAfter; - } - } - } - if (sec >= transition) { - break; - } - } - // transIdx could be -1 when local=true - rawoff = rawOffsetAt(transIdx) * U_MILLIS_PER_SECOND; - dstoff = dstOffsetAt(transIdx) * U_MILLIS_PER_SECOND; - } - } else { - // No transitions, single pair of offsets only - rawoff = initialRawOffset() * U_MILLIS_PER_SECOND; - dstoff = initialDstOffset() * U_MILLIS_PER_SECOND; - } - U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst) - raw=%d, dst=%d\n", - date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt, rawoff, dstoff)); -} - -/** - * TimeZone API. - */ -UBool OlsonTimeZone::useDaylightTime() const { - // If DST was observed in 1942 (for example) but has never been - // observed from 1943 to the present, most clients will expect - // this method to return false. This method determines whether - // DST is in use in the current year (at any point in the year) - // and returns true if so. - - UDate current = uprv_getUTCtime(); - if (finalZone != NULL && current >= finalStartMillis) { - return finalZone->useDaylightTime(); - } - - int32_t year, month, dom, dow, doy, mid; - Grego::timeToFields(current, year, month, dom, dow, doy, mid); - - // Find start of this year, and start of next year - double start = Grego::fieldsToDay(year, 0, 1) * SECONDS_PER_DAY; - double limit = Grego::fieldsToDay(year+1, 0, 1) * SECONDS_PER_DAY; - - // Return true if DST is observed at any time during the current - // year. - for (int16_t i = 0; i < transitionCount(); ++i) { - double transition = (double)transitionTimeInSeconds(i); - if (transition >= limit) { - break; - } - if ((transition >= start && dstOffsetAt(i) != 0) - || (transition > start && dstOffsetAt(i - 1) != 0)) { - return true; - } - } - return false; -} -int32_t -OlsonTimeZone::getDSTSavings() const{ - if (finalZone != NULL){ - return finalZone->getDSTSavings(); - } - return TimeZone::getDSTSavings(); -} -/** - * TimeZone API. - */ -UBool OlsonTimeZone::inDaylightTime(UDate date, UErrorCode& ec) const { - int32_t raw, dst; - getOffset(date, false, raw, dst, ec); - return dst != 0; -} - -UBool -OlsonTimeZone::hasSameRules(const TimeZone &other) const { - if (this == &other) { - return true; - } - const OlsonTimeZone* z = dynamic_cast(&other); - if (z == NULL) { - return false; - } - - // [sic] pointer comparison: typeMapData points into - // memory-mapped or DLL space, so if two zones have the same - // pointer, they are equal. - if (typeMapData == z->typeMapData) { - return true; - } - - // If the pointers are not equal, the zones may still - // be equal if their rules and transitions are equal - if ((finalZone == NULL && z->finalZone != NULL) - || (finalZone != NULL && z->finalZone == NULL) - || (finalZone != NULL && z->finalZone != NULL && *finalZone != *z->finalZone)) { - return false; - } - - if (finalZone != NULL) { - if (finalStartYear != z->finalStartYear || finalStartMillis != z->finalStartMillis) { - return false; - } - } - if (typeCount != z->typeCount - || transitionCountPre32 != z->transitionCountPre32 - || transitionCount32 != z->transitionCount32 - || transitionCountPost32 != z->transitionCountPost32) { - return false; - } - - return - arrayEqual(transitionTimesPre32, z->transitionTimesPre32, sizeof(transitionTimesPre32[0]) * transitionCountPre32 << 1) - && arrayEqual(transitionTimes32, z->transitionTimes32, sizeof(transitionTimes32[0]) * transitionCount32) - && arrayEqual(transitionTimesPost32, z->transitionTimesPost32, sizeof(transitionTimesPost32[0]) * transitionCountPost32 << 1) - && arrayEqual(typeOffsets, z->typeOffsets, sizeof(typeOffsets[0]) * typeCount << 1) - && arrayEqual(typeMapData, z->typeMapData, sizeof(typeMapData[0]) * transitionCount()); -} - -void -OlsonTimeZone::clearTransitionRules(void) { - initialRule = NULL; - firstTZTransition = NULL; - firstFinalTZTransition = NULL; - historicRules = NULL; - historicRuleCount = 0; - finalZoneWithStartYear = NULL; - firstTZTransitionIdx = 0; - transitionRulesInitOnce.reset(); -} - -void -OlsonTimeZone::deleteTransitionRules(void) { - if (initialRule != NULL) { - delete initialRule; - } - if (firstTZTransition != NULL) { - delete firstTZTransition; - } - if (firstFinalTZTransition != NULL) { - delete firstFinalTZTransition; - } - if (finalZoneWithStartYear != NULL) { - delete finalZoneWithStartYear; - } - if (historicRules != NULL) { - for (int i = 0; i < historicRuleCount; i++) { - if (historicRules[i] != NULL) { - delete historicRules[i]; - } - } - uprv_free(historicRules); - } - clearTransitionRules(); -} - -/* - * Lazy transition rules initializer - */ - -static void U_CALLCONV initRules(OlsonTimeZone *This, UErrorCode &status) { - This->initTransitionRules(status); -} - -void -OlsonTimeZone::checkTransitionRules(UErrorCode& status) const { - OlsonTimeZone *ncThis = const_cast(this); - umtx_initOnce(ncThis->transitionRulesInitOnce, &initRules, ncThis, status); -} - -void -OlsonTimeZone::initTransitionRules(UErrorCode& status) { - if(U_FAILURE(status)) { - return; - } - deleteTransitionRules(); - UnicodeString tzid; - getID(tzid); - - UnicodeString stdName = tzid + UNICODE_STRING_SIMPLE("(STD)"); - UnicodeString dstName = tzid + UNICODE_STRING_SIMPLE("(DST)"); - - int32_t raw, dst; - - // Create initial rule - raw = initialRawOffset() * U_MILLIS_PER_SECOND; - dst = initialDstOffset() * U_MILLIS_PER_SECOND; - initialRule = new InitialTimeZoneRule((dst == 0 ? stdName : dstName), raw, dst); - // Check to make sure initialRule was created - if (initialRule == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - deleteTransitionRules(); - return; - } - - int32_t transCount = transitionCount(); - if (transCount > 0) { - int16_t transitionIdx, typeIdx; - - // We probably no longer need to check the first "real" transition - // here, because the new tzcode remove such transitions already. - // For now, keeping this code for just in case. Feb 19, 2010 Yoshito - firstTZTransitionIdx = 0; - for (transitionIdx = 0; transitionIdx < transCount; transitionIdx++) { - if (typeMapData[transitionIdx] != 0) { // type 0 is the initial type - break; - } - firstTZTransitionIdx++; - } - if (transitionIdx == transCount) { - // Actually no transitions... - } else { - // Build historic rule array - UDate* times = (UDate*)uprv_malloc(sizeof(UDate)*transCount); /* large enough to store all transition times */ - if (times == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - deleteTransitionRules(); - return; - } - for (typeIdx = 0; typeIdx < typeCount; typeIdx++) { - // Gather all start times for each pair of offsets - int32_t nTimes = 0; - for (transitionIdx = firstTZTransitionIdx; transitionIdx < transCount; transitionIdx++) { - if (typeIdx == (int16_t)typeMapData[transitionIdx]) { - UDate tt = (UDate)transitionTime(transitionIdx); - if (finalZone == NULL || tt <= finalStartMillis) { - // Exclude transitions after finalMillis - times[nTimes++] = tt; - } - } - } - if (nTimes > 0) { - // Create a TimeArrayTimeZoneRule - raw = typeOffsets[typeIdx << 1] * U_MILLIS_PER_SECOND; - dst = typeOffsets[(typeIdx << 1) + 1] * U_MILLIS_PER_SECOND; - if (historicRules == NULL) { - historicRuleCount = typeCount; - historicRules = (TimeArrayTimeZoneRule**)uprv_malloc(sizeof(TimeArrayTimeZoneRule*)*historicRuleCount); - if (historicRules == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - deleteTransitionRules(); - uprv_free(times); - return; - } - for (int i = 0; i < historicRuleCount; i++) { - // Initialize TimeArrayTimeZoneRule pointers as NULL - historicRules[i] = NULL; - } - } - historicRules[typeIdx] = new TimeArrayTimeZoneRule((dst == 0 ? stdName : dstName), - raw, dst, times, nTimes, DateTimeRule::UTC_TIME); - // Check for memory allocation error - if (historicRules[typeIdx] == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - deleteTransitionRules(); - return; - } - } - } - uprv_free(times); - - // Create initial transition - typeIdx = (int16_t)typeMapData[firstTZTransitionIdx]; - firstTZTransition = new TimeZoneTransition((UDate)transitionTime(firstTZTransitionIdx), - *initialRule, *historicRules[typeIdx]); - // Check to make sure firstTZTransition was created. - if (firstTZTransition == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - deleteTransitionRules(); - return; - } - } - } - if (finalZone != NULL) { - // Get the first occurrence of final rule starts - UDate startTime = (UDate)finalStartMillis; - TimeZoneRule *firstFinalRule = NULL; - - if (finalZone->useDaylightTime()) { - /* - * Note: When an OlsonTimeZone is constructed, we should set the final year - * as the start year of finalZone. However, the boundary condition used for - * getting offset from finalZone has some problems. - * For now, we do not set the valid start year when the construction time - * and create a clone and set the start year when extracting rules. - */ - finalZoneWithStartYear = finalZone->clone(); - // Check to make sure finalZone was actually cloned. - if (finalZoneWithStartYear == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - deleteTransitionRules(); - return; - } - finalZoneWithStartYear->setStartYear(finalStartYear); - - TimeZoneTransition tzt; - finalZoneWithStartYear->getNextTransition(startTime, false, tzt); - firstFinalRule = tzt.getTo()->clone(); - // Check to make sure firstFinalRule received proper clone. - if (firstFinalRule == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - deleteTransitionRules(); - return; - } - startTime = tzt.getTime(); - } else { - // final rule with no transitions - finalZoneWithStartYear = finalZone->clone(); - // Check to make sure finalZone was actually cloned. - if (finalZoneWithStartYear == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - deleteTransitionRules(); - return; - } - finalZone->getID(tzid); - firstFinalRule = new TimeArrayTimeZoneRule(tzid, - finalZone->getRawOffset(), 0, &startTime, 1, DateTimeRule::UTC_TIME); - // Check firstFinalRule was properly created. - if (firstFinalRule == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - deleteTransitionRules(); - return; - } - } - TimeZoneRule *prevRule = NULL; - if (transCount > 0) { - prevRule = historicRules[typeMapData[transCount - 1]]; - } - if (prevRule == NULL) { - // No historic transitions, but only finalZone available - prevRule = initialRule; - } - firstFinalTZTransition = new TimeZoneTransition(); - // Check to make sure firstFinalTZTransition was created before dereferencing - if (firstFinalTZTransition == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - deleteTransitionRules(); - return; - } - firstFinalTZTransition->setTime(startTime); - firstFinalTZTransition->adoptFrom(prevRule->clone()); - firstFinalTZTransition->adoptTo(firstFinalRule); - } -} - -UBool -OlsonTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { - UErrorCode status = U_ZERO_ERROR; - checkTransitionRules(status); - if (U_FAILURE(status)) { - return false; - } - - if (finalZone != NULL) { - if (inclusive && base == firstFinalTZTransition->getTime()) { - result = *firstFinalTZTransition; - return true; - } else if (base >= firstFinalTZTransition->getTime()) { - if (finalZone->useDaylightTime()) { - //return finalZone->getNextTransition(base, inclusive, result); - return finalZoneWithStartYear->getNextTransition(base, inclusive, result); - } else { - // No more transitions - return false; - } - } - } - if (historicRules != NULL) { - // Find a historical transition - int16_t transCount = transitionCount(); - int16_t ttidx = transCount - 1; - for (; ttidx >= firstTZTransitionIdx; ttidx--) { - UDate t = (UDate)transitionTime(ttidx); - if (base > t || (!inclusive && base == t)) { - break; - } - } - if (ttidx == transCount - 1) { - if (firstFinalTZTransition != NULL) { - result = *firstFinalTZTransition; - return true; - } else { - return false; - } - } else if (ttidx < firstTZTransitionIdx) { - result = *firstTZTransition; - return true; - } else { - // Create a TimeZoneTransition - TimeZoneRule *to = historicRules[typeMapData[ttidx + 1]]; - TimeZoneRule *from = historicRules[typeMapData[ttidx]]; - UDate startTime = (UDate)transitionTime(ttidx+1); - - // The transitions loaded from zoneinfo.res may contain non-transition data - UnicodeString fromName, toName; - from->getName(fromName); - to->getName(toName); - if (fromName == toName && from->getRawOffset() == to->getRawOffset() - && from->getDSTSavings() == to->getDSTSavings()) { - return getNextTransition(startTime, false, result); - } - result.setTime(startTime); - result.adoptFrom(from->clone()); - result.adoptTo(to->clone()); - return true; - } - } - return false; -} - -UBool -OlsonTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { - UErrorCode status = U_ZERO_ERROR; - checkTransitionRules(status); - if (U_FAILURE(status)) { - return false; - } - - if (finalZone != NULL) { - if (inclusive && base == firstFinalTZTransition->getTime()) { - result = *firstFinalTZTransition; - return true; - } else if (base > firstFinalTZTransition->getTime()) { - if (finalZone->useDaylightTime()) { - //return finalZone->getPreviousTransition(base, inclusive, result); - return finalZoneWithStartYear->getPreviousTransition(base, inclusive, result); - } else { - result = *firstFinalTZTransition; - return true; - } - } - } - - if (historicRules != NULL) { - // Find a historical transition - int16_t ttidx = transitionCount() - 1; - for (; ttidx >= firstTZTransitionIdx; ttidx--) { - UDate t = (UDate)transitionTime(ttidx); - if (base > t || (inclusive && base == t)) { - break; - } - } - if (ttidx < firstTZTransitionIdx) { - // No more transitions - return false; - } else if (ttidx == firstTZTransitionIdx) { - result = *firstTZTransition; - return true; - } else { - // Create a TimeZoneTransition - TimeZoneRule *to = historicRules[typeMapData[ttidx]]; - TimeZoneRule *from = historicRules[typeMapData[ttidx-1]]; - UDate startTime = (UDate)transitionTime(ttidx); - - // The transitions loaded from zoneinfo.res may contain non-transition data - UnicodeString fromName, toName; - from->getName(fromName); - to->getName(toName); - if (fromName == toName && from->getRawOffset() == to->getRawOffset() - && from->getDSTSavings() == to->getDSTSavings()) { - return getPreviousTransition(startTime, false, result); - } - result.setTime(startTime); - result.adoptFrom(from->clone()); - result.adoptTo(to->clone()); - return true; - } - } - return false; -} - -int32_t -OlsonTimeZone::countTransitionRules(UErrorCode& status) const { - if (U_FAILURE(status)) { - return 0; - } - checkTransitionRules(status); - if (U_FAILURE(status)) { - return 0; - } - - int32_t count = 0; - if (historicRules != NULL) { - // historicRules may contain null entries when original zoneinfo data - // includes non transition data. - for (int32_t i = 0; i < historicRuleCount; i++) { - if (historicRules[i] != NULL) { - count++; - } - } - } - if (finalZone != NULL) { - if (finalZone->useDaylightTime()) { - count += 2; - } else { - count++; - } - } - return count; -} - -void -OlsonTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, - const TimeZoneRule* trsrules[], - int32_t& trscount, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - checkTransitionRules(status); - if (U_FAILURE(status)) { - return; - } - - // Initial rule - initial = initialRule; - - // Transition rules - int32_t cnt = 0; - if (historicRules != NULL && trscount > cnt) { - // historicRules may contain null entries when original zoneinfo data - // includes non transition data. - for (int32_t i = 0; i < historicRuleCount; i++) { - if (historicRules[i] != NULL) { - trsrules[cnt++] = historicRules[i]; - if (cnt >= trscount) { - break; - } - } - } - } - if (finalZoneWithStartYear != NULL && trscount > cnt) { - const InitialTimeZoneRule *tmpini; - int32_t tmpcnt = trscount - cnt; - finalZoneWithStartYear->getTimeZoneRules(tmpini, &trsrules[cnt], tmpcnt, status); - if (U_FAILURE(status)) { - return; - } - cnt += tmpcnt; - } - // Set the result length - trscount = cnt; -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_FORMATTING - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2003-2013, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: July 21 2003 +* Since: ICU 2.8 +********************************************************************** +*/ + +#include "utypeinfo.h" // for 'typeid' to work + +#include "olsontz.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/ures.h" +#include "unicode/simpletz.h" +#include "unicode/gregocal.h" +#include "gregoimp.h" +#include "cmemory.h" +#include "uassert.h" +#include "uvector.h" +#include // DBL_MAX +#include "uresimp.h" +#include "zonemeta.h" +#include "umutex.h" + +#ifdef U_DEBUG_TZ +# include +# include "uresimp.h" // for debugging + +static void debug_tz_loc(const char *f, int32_t l) +{ + fprintf(stderr, "%s:%d: ", f, l); +} + +static void debug_tz_msg(const char *pat, ...) +{ + va_list ap; + va_start(ap, pat); + vfprintf(stderr, pat, ap); + fflush(stderr); +} +// must use double parens, i.e.: U_DEBUG_TZ_MSG(("four is: %d",4)); +#define U_DEBUG_TZ_MSG(x) {debug_tz_loc(__FILE__,__LINE__);debug_tz_msg x;} +#else +#define U_DEBUG_TZ_MSG(x) +#endif + +static UBool arrayEqual(const void *a1, const void *a2, int32_t size) { + if (a1 == nullptr && a2 == nullptr) { + return true; + } + if ((a1 != nullptr && a2 == nullptr) || (a1 == nullptr && a2 != nullptr)) { + return false; + } + if (a1 == a2) { + return true; + } + + return (uprv_memcmp(a1, a2, size) == 0); +} + +U_NAMESPACE_BEGIN + +#define kTRANS "trans" +#define kTRANSPRE32 "transPre32" +#define kTRANSPOST32 "transPost32" +#define kTYPEOFFSETS "typeOffsets" +#define kTYPEMAP "typeMap" +#define kLINKS "links" +#define kFINALRULE "finalRule" +#define kFINALRAW "finalRaw" +#define kFINALYEAR "finalYear" + +#define SECONDS_PER_DAY (24*60*60) + +static const int32_t ZEROS[] = {0,0}; + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OlsonTimeZone) + +/** + * Default constructor. Creates a time zone with an empty ID and + * a fixed GMT offset of zero. + */ +/*OlsonTimeZone::OlsonTimeZone() : finalYear(INT32_MAX), finalMillis(DBL_MAX), finalZone(0), transitionRulesInitialized(false) { + clearTransitionRules(); + constructEmpty(); +}*/ + +/** + * Construct a GMT+0 zone with no transitions. This is done when a + * constructor fails so the resultant object is well-behaved. + */ +void OlsonTimeZone::constructEmpty() { + canonicalID = nullptr; + + transitionCountPre32 = transitionCount32 = transitionCountPost32 = 0; + transitionTimesPre32 = transitionTimes32 = transitionTimesPost32 = nullptr; + + typeMapData = nullptr; + + typeCount = 1; + typeOffsets = ZEROS; + + finalZone = nullptr; +} + +/** + * Construct from a resource bundle + * @param top the top-level zoneinfo resource bundle. This is used + * to lookup the rule that `res' may refer to, if there is one. + * @param res the resource bundle of the zone to be constructed + * @param ec input-output error code + */ +OlsonTimeZone::OlsonTimeZone(const UResourceBundle* top, + const UResourceBundle* res, + const UnicodeString& tzid, + UErrorCode& ec) : + BasicTimeZone(tzid), finalZone(nullptr) +{ + clearTransitionRules(); + U_DEBUG_TZ_MSG(("OlsonTimeZone(%s)\n", ures_getKey((UResourceBundle*)res))); + if ((top == nullptr || res == nullptr) && U_SUCCESS(ec)) { + ec = U_ILLEGAL_ARGUMENT_ERROR; + } + if (U_SUCCESS(ec)) { + // TODO -- clean up -- Doesn't work if res points to an alias + // // TODO remove nonconst casts below when ures_* API is fixed + // setID(ures_getKey((UResourceBundle*) res)); // cast away const + + int32_t len; + StackUResourceBundle r; + + // Pre-32bit second transitions + ures_getByKey(res, kTRANSPRE32, r.getAlias(), &ec); + transitionTimesPre32 = ures_getIntVector(r.getAlias(), &len, &ec); + transitionCountPre32 = static_cast(len >> 1); + if (ec == U_MISSING_RESOURCE_ERROR) { + // No pre-32bit transitions + transitionTimesPre32 = nullptr; + transitionCountPre32 = 0; + ec = U_ZERO_ERROR; + } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF || (len & 1) != 0) /* len must be even */) { + ec = U_INVALID_FORMAT_ERROR; + } + + // 32bit second transitions + ures_getByKey(res, kTRANS, r.getAlias(), &ec); + transitionTimes32 = ures_getIntVector(r.getAlias(), &len, &ec); + transitionCount32 = static_cast(len); + if (ec == U_MISSING_RESOURCE_ERROR) { + // No 32bit transitions + transitionTimes32 = nullptr; + transitionCount32 = 0; + ec = U_ZERO_ERROR; + } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF)) { + ec = U_INVALID_FORMAT_ERROR; + } + + // Post-32bit second transitions + ures_getByKey(res, kTRANSPOST32, r.getAlias(), &ec); + transitionTimesPost32 = ures_getIntVector(r.getAlias(), &len, &ec); + transitionCountPost32 = static_cast(len >> 1); + if (ec == U_MISSING_RESOURCE_ERROR) { + // No pre-32bit transitions + transitionTimesPost32 = nullptr; + transitionCountPost32 = 0; + ec = U_ZERO_ERROR; + } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF || (len & 1) != 0) /* len must be even */) { + ec = U_INVALID_FORMAT_ERROR; + } + + // Type offsets list must be of even size, with size >= 2 + ures_getByKey(res, kTYPEOFFSETS, r.getAlias(), &ec); + typeOffsets = ures_getIntVector(r.getAlias(), &len, &ec); + if (U_SUCCESS(ec) && (len < 2 || len > 0x7FFE || (len & 1) != 0)) { + ec = U_INVALID_FORMAT_ERROR; + } + typeCount = (int16_t) len >> 1; + + // Type map data must be of the same size as the transition count + typeMapData = nullptr; + if (transitionCount() > 0) { + ures_getByKey(res, kTYPEMAP, r.getAlias(), &ec); + typeMapData = ures_getBinary(r.getAlias(), &len, &ec); + if (ec == U_MISSING_RESOURCE_ERROR) { + // no type mapping data + ec = U_INVALID_FORMAT_ERROR; + } else if (U_SUCCESS(ec) && len != transitionCount()) { + ec = U_INVALID_FORMAT_ERROR; + } + } + + // Process final rule and data, if any + if (U_SUCCESS(ec)) { + const char16_t *ruleIdUStr = ures_getStringByKey(res, kFINALRULE, &len, &ec); + ures_getByKey(res, kFINALRAW, r.getAlias(), &ec); + int32_t ruleRaw = ures_getInt(r.getAlias(), &ec); + ures_getByKey(res, kFINALYEAR, r.getAlias(), &ec); + int32_t ruleYear = ures_getInt(r.getAlias(), &ec); + if (U_SUCCESS(ec)) { + UnicodeString ruleID(true, ruleIdUStr, len); + UResourceBundle *rule = TimeZone::loadRule(top, ruleID, nullptr, ec); + const int32_t *ruleData = ures_getIntVector(rule, &len, &ec); + if (U_SUCCESS(ec) && len == 11) { + UnicodeString emptyStr; + finalZone = new SimpleTimeZone( + ruleRaw * U_MILLIS_PER_SECOND, + emptyStr, + (int8_t)ruleData[0], (int8_t)ruleData[1], (int8_t)ruleData[2], + ruleData[3] * U_MILLIS_PER_SECOND, + (SimpleTimeZone::TimeMode) ruleData[4], + (int8_t)ruleData[5], (int8_t)ruleData[6], (int8_t)ruleData[7], + ruleData[8] * U_MILLIS_PER_SECOND, + (SimpleTimeZone::TimeMode) ruleData[9], + ruleData[10] * U_MILLIS_PER_SECOND, ec); + if (finalZone == nullptr) { + ec = U_MEMORY_ALLOCATION_ERROR; + } else { + finalStartYear = ruleYear; + + // Note: Setting finalStartYear to the finalZone is problematic. When a date is around + // year boundary, SimpleTimeZone may return false result when DST is observed at the + // beginning of year. We could apply safe margin (day or two), but when one of recurrent + // rules falls around year boundary, it could return false result. Without setting the + // start year, finalZone works fine around the year boundary of the start year. + + // finalZone->setStartYear(finalStartYear); + + + // Compute the millis for Jan 1, 0:00 GMT of the finalYear + + // Note: finalStartMillis is used for detecting either if + // historic transition data or finalZone to be used. In an + // extreme edge case - for example, two transitions fall into + // small windows of time around the year boundary, this may + // result incorrect offset computation. But I think it will + // never happen practically. Yoshito - Feb 20, 2010 + finalStartMillis = Grego::fieldsToDay(finalStartYear, 0, 1) * U_MILLIS_PER_DAY; + } + } else { + ec = U_INVALID_FORMAT_ERROR; + } + ures_close(rule); + } else if (ec == U_MISSING_RESOURCE_ERROR) { + // No final zone + ec = U_ZERO_ERROR; + } + } + + // initialize canonical ID + canonicalID = ZoneMeta::getCanonicalCLDRID(tzid, ec); + } + + if (U_FAILURE(ec)) { + constructEmpty(); + } +} + +/** + * Copy constructor + */ +OlsonTimeZone::OlsonTimeZone(const OlsonTimeZone& other) : + BasicTimeZone(other), finalZone(0) { + *this = other; +} + +/** + * Assignment operator + */ +OlsonTimeZone& OlsonTimeZone::operator=(const OlsonTimeZone& other) { + if (this == &other) { return *this; } // self-assignment: no-op + canonicalID = other.canonicalID; + + transitionTimesPre32 = other.transitionTimesPre32; + transitionTimes32 = other.transitionTimes32; + transitionTimesPost32 = other.transitionTimesPost32; + + transitionCountPre32 = other.transitionCountPre32; + transitionCount32 = other.transitionCount32; + transitionCountPost32 = other.transitionCountPost32; + + typeCount = other.typeCount; + typeOffsets = other.typeOffsets; + typeMapData = other.typeMapData; + + delete finalZone; + finalZone = (other.finalZone != 0) ? other.finalZone->clone() : 0; + + finalStartYear = other.finalStartYear; + finalStartMillis = other.finalStartMillis; + + clearTransitionRules(); + + return *this; +} + +/** + * Destructor + */ +OlsonTimeZone::~OlsonTimeZone() { + deleteTransitionRules(); + delete finalZone; +} + +/** + * Returns true if the two TimeZone objects are equal. + */ +bool OlsonTimeZone::operator==(const TimeZone& other) const { + return ((this == &other) || + (typeid(*this) == typeid(other) && + TimeZone::operator==(other) && + hasSameRules(other))); +} + +/** + * TimeZone API. + */ +OlsonTimeZone* OlsonTimeZone::clone() const { + return new OlsonTimeZone(*this); +} + +/** + * TimeZone API. + */ +int32_t OlsonTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, + int32_t dom, uint8_t dow, + int32_t millis, UErrorCode& ec) const { + if (month < UCAL_JANUARY || month > UCAL_DECEMBER) { + if (U_SUCCESS(ec)) { + ec = U_ILLEGAL_ARGUMENT_ERROR; + } + return 0; + } else { + return getOffset(era, year, month, dom, dow, millis, + Grego::monthLength(year, month), + ec); + } +} + +/** + * TimeZone API. + */ +int32_t OlsonTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, + int32_t dom, uint8_t dow, + int32_t millis, int32_t monthLength, + UErrorCode& ec) const { + if (U_FAILURE(ec)) { + return 0; + } + + if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC) + || month < UCAL_JANUARY + || month > UCAL_DECEMBER + || dom < 1 + || dom > monthLength + || dow < UCAL_SUNDAY + || dow > UCAL_SATURDAY + || millis < 0 + || millis >= U_MILLIS_PER_DAY + || monthLength < 28 + || monthLength > 31) { + ec = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + if (era == GregorianCalendar::BC) { + year = -year; + } + + if (finalZone != nullptr && year >= finalStartYear) { + return finalZone->getOffset(era, year, month, dom, dow, + millis, monthLength, ec); + } + + // Compute local epoch millis from input fields + UDate date = (UDate)(Grego::fieldsToDay(year, month, dom) * U_MILLIS_PER_DAY + millis); + int32_t rawoff, dstoff; + getHistoricalOffset(date, true, kDaylight, kStandard, rawoff, dstoff); + return rawoff + dstoff; +} + +/** + * TimeZone API. + */ +void OlsonTimeZone::getOffset(UDate date, UBool local, int32_t& rawoff, + int32_t& dstoff, UErrorCode& ec) const { + if (U_FAILURE(ec)) { + return; + } + if (finalZone != nullptr && date >= finalStartMillis) { + finalZone->getOffset(date, local, rawoff, dstoff, ec); + } else { + getHistoricalOffset(date, local, kFormer, kLatter, rawoff, dstoff); + } +} + +void OlsonTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingTimeOpt, + UTimeZoneLocalOption duplicatedTimeOpt, + int32_t& rawoff, int32_t& dstoff, UErrorCode& ec) const { + if (U_FAILURE(ec)) { + return; + } + if (finalZone != nullptr && date >= finalStartMillis) { + finalZone->getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff, ec); + } else { + getHistoricalOffset(date, true, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff); + } +} + + +/** + * TimeZone API. + */ +void OlsonTimeZone::setRawOffset(int32_t /*offsetMillis*/) { + // We don't support this operation, since OlsonTimeZones are + // immutable (except for the ID, which is in the base class). + + // Nothing to do! +} + +/** + * TimeZone API. + */ +int32_t OlsonTimeZone::getRawOffset() const { + UErrorCode ec = U_ZERO_ERROR; + int32_t raw, dst; + getOffset(uprv_getUTCtime(), false, raw, dst, ec); + return raw; +} + +#if defined U_DEBUG_TZ +void printTime(double ms) { + int32_t year, month, dom, dow; + double millis=0; + double days = ClockMath::floorDivide(((double)ms), (double)U_MILLIS_PER_DAY, millis); + + Grego::dayToFields(days, year, month, dom, dow); + U_DEBUG_TZ_MSG((" getHistoricalOffset: time %.1f (%04d.%02d.%02d+%.1fh)\n", ms, + year, month+1, dom, (millis/kOneHour))); + } +#endif + +int64_t +OlsonTimeZone::transitionTimeInSeconds(int16_t transIdx) const { + U_ASSERT(transIdx >= 0 && transIdx < transitionCount()); + + if (transIdx < transitionCountPre32) { + return (((int64_t)((uint32_t)transitionTimesPre32[transIdx << 1])) << 32) + | ((int64_t)((uint32_t)transitionTimesPre32[(transIdx << 1) + 1])); + } + + transIdx -= transitionCountPre32; + if (transIdx < transitionCount32) { + return (int64_t)transitionTimes32[transIdx]; + } + + transIdx -= transitionCount32; + return (((int64_t)((uint32_t)transitionTimesPost32[transIdx << 1])) << 32) + | ((int64_t)((uint32_t)transitionTimesPost32[(transIdx << 1) + 1])); +} + +// Maximum absolute offset in seconds (86400 seconds = 1 day) +// getHistoricalOffset uses this constant as safety margin of +// quick zone transition checking. +#define MAX_OFFSET_SECONDS 86400 + +void +OlsonTimeZone::getHistoricalOffset(UDate date, UBool local, + int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt, + int32_t& rawoff, int32_t& dstoff) const { + U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst)\n", + date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt)); +#if defined U_DEBUG_TZ + printTime(date*1000.0); +#endif + int16_t transCount = transitionCount(); + + if (transCount > 0) { + double sec = uprv_floor(date / U_MILLIS_PER_SECOND); + if (!local && sec < transitionTimeInSeconds(0)) { + // Before the first transition time + rawoff = initialRawOffset() * U_MILLIS_PER_SECOND; + dstoff = initialDstOffset() * U_MILLIS_PER_SECOND; + } else { + // Linear search from the end is the fastest approach, since + // most lookups will happen at/near the end. + int16_t transIdx; + for (transIdx = transCount - 1; transIdx >= 0; transIdx--) { + int64_t transition = transitionTimeInSeconds(transIdx); + + if (local && (sec >= (transition - MAX_OFFSET_SECONDS))) { + int32_t offsetBefore = zoneOffsetAt(transIdx - 1); + UBool dstBefore = dstOffsetAt(transIdx - 1) != 0; + + int32_t offsetAfter = zoneOffsetAt(transIdx); + UBool dstAfter = dstOffsetAt(transIdx) != 0; + + UBool dstToStd = dstBefore && !dstAfter; + UBool stdToDst = !dstBefore && dstAfter; + + if (offsetAfter - offsetBefore >= 0) { + // Positive transition, which makes a non-existing local time range + if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd) + || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) { + transition += offsetBefore; + } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst) + || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) { + transition += offsetAfter; + } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) { + transition += offsetBefore; + } else { + // Interprets the time with rule before the transition, + // default for non-existing time range + transition += offsetAfter; + } + } else { + // Negative transition, which makes a duplicated local time range + if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd) + || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) { + transition += offsetAfter; + } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst) + || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) { + transition += offsetBefore; + } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) { + transition += offsetBefore; + } else { + // Interprets the time with rule after the transition, + // default for duplicated local time range + transition += offsetAfter; + } + } + } + if (sec >= transition) { + break; + } + } + // transIdx could be -1 when local=true + rawoff = rawOffsetAt(transIdx) * U_MILLIS_PER_SECOND; + dstoff = dstOffsetAt(transIdx) * U_MILLIS_PER_SECOND; + } + } else { + // No transitions, single pair of offsets only + rawoff = initialRawOffset() * U_MILLIS_PER_SECOND; + dstoff = initialDstOffset() * U_MILLIS_PER_SECOND; + } + U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst) - raw=%d, dst=%d\n", + date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt, rawoff, dstoff)); +} + +/** + * TimeZone API. + */ +UBool OlsonTimeZone::useDaylightTime() const { + // If DST was observed in 1942 (for example) but has never been + // observed from 1943 to the present, most clients will expect + // this method to return false. This method determines whether + // DST is in use in the current year (at any point in the year) + // and returns true if so. + + UDate current = uprv_getUTCtime(); + if (finalZone != nullptr && current >= finalStartMillis) { + return finalZone->useDaylightTime(); + } + + int32_t year, month, dom, dow, doy, mid; + Grego::timeToFields(current, year, month, dom, dow, doy, mid); + + // Find start of this year, and start of next year + double start = Grego::fieldsToDay(year, 0, 1) * SECONDS_PER_DAY; + double limit = Grego::fieldsToDay(year+1, 0, 1) * SECONDS_PER_DAY; + + // Return true if DST is observed at any time during the current + // year. + for (int16_t i = 0; i < transitionCount(); ++i) { + double transition = (double)transitionTimeInSeconds(i); + if (transition >= limit) { + break; + } + if ((transition >= start && dstOffsetAt(i) != 0) + || (transition > start && dstOffsetAt(i - 1) != 0)) { + return true; + } + } + return false; +} +int32_t +OlsonTimeZone::getDSTSavings() const{ + if (finalZone != nullptr){ + return finalZone->getDSTSavings(); + } + return TimeZone::getDSTSavings(); +} +/** + * TimeZone API. + */ +UBool OlsonTimeZone::inDaylightTime(UDate date, UErrorCode& ec) const { + int32_t raw, dst; + getOffset(date, false, raw, dst, ec); + return dst != 0; +} + +UBool +OlsonTimeZone::hasSameRules(const TimeZone &other) const { + if (this == &other) { + return true; + } + const OlsonTimeZone* z = dynamic_cast(&other); + if (z == nullptr) { + return false; + } + + // [sic] pointer comparison: typeMapData points into + // memory-mapped or DLL space, so if two zones have the same + // pointer, they are equal. + if (typeMapData == z->typeMapData) { + return true; + } + + // If the pointers are not equal, the zones may still + // be equal if their rules and transitions are equal + if ((finalZone == nullptr && z->finalZone != nullptr) + || (finalZone != nullptr && z->finalZone == nullptr) + || (finalZone != nullptr && z->finalZone != nullptr && *finalZone != *z->finalZone)) { + return false; + } + + if (finalZone != nullptr) { + if (finalStartYear != z->finalStartYear || finalStartMillis != z->finalStartMillis) { + return false; + } + } + if (typeCount != z->typeCount + || transitionCountPre32 != z->transitionCountPre32 + || transitionCount32 != z->transitionCount32 + || transitionCountPost32 != z->transitionCountPost32) { + return false; + } + + return + arrayEqual(transitionTimesPre32, z->transitionTimesPre32, sizeof(transitionTimesPre32[0]) * transitionCountPre32 << 1) + && arrayEqual(transitionTimes32, z->transitionTimes32, sizeof(transitionTimes32[0]) * transitionCount32) + && arrayEqual(transitionTimesPost32, z->transitionTimesPost32, sizeof(transitionTimesPost32[0]) * transitionCountPost32 << 1) + && arrayEqual(typeOffsets, z->typeOffsets, sizeof(typeOffsets[0]) * typeCount << 1) + && arrayEqual(typeMapData, z->typeMapData, sizeof(typeMapData[0]) * transitionCount()); +} + +void +OlsonTimeZone::clearTransitionRules() { + initialRule = nullptr; + firstTZTransition = nullptr; + firstFinalTZTransition = nullptr; + historicRules = nullptr; + historicRuleCount = 0; + finalZoneWithStartYear = nullptr; + firstTZTransitionIdx = 0; + transitionRulesInitOnce.reset(); +} + +void +OlsonTimeZone::deleteTransitionRules() { + if (initialRule != nullptr) { + delete initialRule; + } + if (firstTZTransition != nullptr) { + delete firstTZTransition; + } + if (firstFinalTZTransition != nullptr) { + delete firstFinalTZTransition; + } + if (finalZoneWithStartYear != nullptr) { + delete finalZoneWithStartYear; + } + if (historicRules != nullptr) { + for (int i = 0; i < historicRuleCount; i++) { + if (historicRules[i] != nullptr) { + delete historicRules[i]; + } + } + uprv_free(historicRules); + } + clearTransitionRules(); +} + +/* + * Lazy transition rules initializer + */ + +static void U_CALLCONV initRules(OlsonTimeZone *This, UErrorCode &status) { + This->initTransitionRules(status); +} + +void +OlsonTimeZone::checkTransitionRules(UErrorCode& status) const { + OlsonTimeZone *ncThis = const_cast(this); + umtx_initOnce(ncThis->transitionRulesInitOnce, &initRules, ncThis, status); +} + +void +OlsonTimeZone::initTransitionRules(UErrorCode& status) { + if(U_FAILURE(status)) { + return; + } + deleteTransitionRules(); + UnicodeString tzid; + getID(tzid); + + UnicodeString stdName = tzid + UNICODE_STRING_SIMPLE("(STD)"); + UnicodeString dstName = tzid + UNICODE_STRING_SIMPLE("(DST)"); + + int32_t raw, dst; + + // Create initial rule + raw = initialRawOffset() * U_MILLIS_PER_SECOND; + dst = initialDstOffset() * U_MILLIS_PER_SECOND; + initialRule = new InitialTimeZoneRule((dst == 0 ? stdName : dstName), raw, dst); + // Check to make sure initialRule was created + if (initialRule == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + deleteTransitionRules(); + return; + } + + int32_t transCount = transitionCount(); + if (transCount > 0) { + int16_t transitionIdx, typeIdx; + + // We probably no longer need to check the first "real" transition + // here, because the new tzcode remove such transitions already. + // For now, keeping this code for just in case. Feb 19, 2010 Yoshito + firstTZTransitionIdx = 0; + for (transitionIdx = 0; transitionIdx < transCount; transitionIdx++) { + if (typeMapData[transitionIdx] != 0) { // type 0 is the initial type + break; + } + firstTZTransitionIdx++; + } + if (transitionIdx == transCount) { + // Actually no transitions... + } else { + // Build historic rule array + UDate* times = (UDate*)uprv_malloc(sizeof(UDate)*transCount); /* large enough to store all transition times */ + if (times == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + deleteTransitionRules(); + return; + } + for (typeIdx = 0; typeIdx < typeCount; typeIdx++) { + // Gather all start times for each pair of offsets + int32_t nTimes = 0; + for (transitionIdx = firstTZTransitionIdx; transitionIdx < transCount; transitionIdx++) { + if (typeIdx == (int16_t)typeMapData[transitionIdx]) { + UDate tt = (UDate)transitionTime(transitionIdx); + if (finalZone == nullptr || tt <= finalStartMillis) { + // Exclude transitions after finalMillis + times[nTimes++] = tt; + } + } + } + if (nTimes > 0) { + // Create a TimeArrayTimeZoneRule + raw = typeOffsets[typeIdx << 1] * U_MILLIS_PER_SECOND; + dst = typeOffsets[(typeIdx << 1) + 1] * U_MILLIS_PER_SECOND; + if (historicRules == nullptr) { + historicRuleCount = typeCount; + historicRules = (TimeArrayTimeZoneRule**)uprv_malloc(sizeof(TimeArrayTimeZoneRule*)*historicRuleCount); + if (historicRules == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + deleteTransitionRules(); + uprv_free(times); + return; + } + for (int i = 0; i < historicRuleCount; i++) { + // Initialize TimeArrayTimeZoneRule pointers as nullptr + historicRules[i] = nullptr; + } + } + historicRules[typeIdx] = new TimeArrayTimeZoneRule((dst == 0 ? stdName : dstName), + raw, dst, times, nTimes, DateTimeRule::UTC_TIME); + // Check for memory allocation error + if (historicRules[typeIdx] == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + deleteTransitionRules(); + return; + } + } + } + uprv_free(times); + + // Create initial transition + typeIdx = (int16_t)typeMapData[firstTZTransitionIdx]; + firstTZTransition = new TimeZoneTransition((UDate)transitionTime(firstTZTransitionIdx), + *initialRule, *historicRules[typeIdx]); + // Check to make sure firstTZTransition was created. + if (firstTZTransition == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + deleteTransitionRules(); + return; + } + } + } + if (finalZone != nullptr) { + // Get the first occurrence of final rule starts + UDate startTime = (UDate)finalStartMillis; + TimeZoneRule *firstFinalRule = nullptr; + + if (finalZone->useDaylightTime()) { + /* + * Note: When an OlsonTimeZone is constructed, we should set the final year + * as the start year of finalZone. However, the boundary condition used for + * getting offset from finalZone has some problems. + * For now, we do not set the valid start year when the construction time + * and create a clone and set the start year when extracting rules. + */ + finalZoneWithStartYear = finalZone->clone(); + // Check to make sure finalZone was actually cloned. + if (finalZoneWithStartYear == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + deleteTransitionRules(); + return; + } + finalZoneWithStartYear->setStartYear(finalStartYear); + + TimeZoneTransition tzt; + finalZoneWithStartYear->getNextTransition(startTime, false, tzt); + firstFinalRule = tzt.getTo()->clone(); + // Check to make sure firstFinalRule received proper clone. + if (firstFinalRule == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + deleteTransitionRules(); + return; + } + startTime = tzt.getTime(); + } else { + // final rule with no transitions + finalZoneWithStartYear = finalZone->clone(); + // Check to make sure finalZone was actually cloned. + if (finalZoneWithStartYear == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + deleteTransitionRules(); + return; + } + finalZone->getID(tzid); + firstFinalRule = new TimeArrayTimeZoneRule(tzid, + finalZone->getRawOffset(), 0, &startTime, 1, DateTimeRule::UTC_TIME); + // Check firstFinalRule was properly created. + if (firstFinalRule == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + deleteTransitionRules(); + return; + } + } + TimeZoneRule *prevRule = nullptr; + if (transCount > 0) { + prevRule = historicRules[typeMapData[transCount - 1]]; + } + if (prevRule == nullptr) { + // No historic transitions, but only finalZone available + prevRule = initialRule; + } + firstFinalTZTransition = new TimeZoneTransition(); + // Check to make sure firstFinalTZTransition was created before dereferencing + if (firstFinalTZTransition == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + deleteTransitionRules(); + return; + } + firstFinalTZTransition->setTime(startTime); + firstFinalTZTransition->adoptFrom(prevRule->clone()); + firstFinalTZTransition->adoptTo(firstFinalRule); + } +} + +UBool +OlsonTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { + UErrorCode status = U_ZERO_ERROR; + checkTransitionRules(status); + if (U_FAILURE(status)) { + return false; + } + + if (finalZone != nullptr) { + if (inclusive && base == firstFinalTZTransition->getTime()) { + result = *firstFinalTZTransition; + return true; + } else if (base >= firstFinalTZTransition->getTime()) { + if (finalZone->useDaylightTime()) { + //return finalZone->getNextTransition(base, inclusive, result); + return finalZoneWithStartYear->getNextTransition(base, inclusive, result); + } else { + // No more transitions + return false; + } + } + } + if (historicRules != nullptr) { + // Find a historical transition + int16_t transCount = transitionCount(); + int16_t ttidx = transCount - 1; + for (; ttidx >= firstTZTransitionIdx; ttidx--) { + UDate t = (UDate)transitionTime(ttidx); + if (base > t || (!inclusive && base == t)) { + break; + } + } + if (ttidx == transCount - 1) { + if (firstFinalTZTransition != nullptr) { + result = *firstFinalTZTransition; + return true; + } else { + return false; + } + } else if (ttidx < firstTZTransitionIdx) { + result = *firstTZTransition; + return true; + } else { + // Create a TimeZoneTransition + TimeZoneRule *to = historicRules[typeMapData[ttidx + 1]]; + TimeZoneRule *from = historicRules[typeMapData[ttidx]]; + UDate startTime = (UDate)transitionTime(ttidx+1); + + // The transitions loaded from zoneinfo.res may contain non-transition data + UnicodeString fromName, toName; + from->getName(fromName); + to->getName(toName); + if (fromName == toName && from->getRawOffset() == to->getRawOffset() + && from->getDSTSavings() == to->getDSTSavings()) { + return getNextTransition(startTime, false, result); + } + result.setTime(startTime); + result.adoptFrom(from->clone()); + result.adoptTo(to->clone()); + return true; + } + } + return false; +} + +UBool +OlsonTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { + UErrorCode status = U_ZERO_ERROR; + checkTransitionRules(status); + if (U_FAILURE(status)) { + return false; + } + + if (finalZone != nullptr) { + if (inclusive && base == firstFinalTZTransition->getTime()) { + result = *firstFinalTZTransition; + return true; + } else if (base > firstFinalTZTransition->getTime()) { + if (finalZone->useDaylightTime()) { + //return finalZone->getPreviousTransition(base, inclusive, result); + return finalZoneWithStartYear->getPreviousTransition(base, inclusive, result); + } else { + result = *firstFinalTZTransition; + return true; + } + } + } + + if (historicRules != nullptr) { + // Find a historical transition + int16_t ttidx = transitionCount() - 1; + for (; ttidx >= firstTZTransitionIdx; ttidx--) { + UDate t = (UDate)transitionTime(ttidx); + if (base > t || (inclusive && base == t)) { + break; + } + } + if (ttidx < firstTZTransitionIdx) { + // No more transitions + return false; + } else if (ttidx == firstTZTransitionIdx) { + result = *firstTZTransition; + return true; + } else { + // Create a TimeZoneTransition + TimeZoneRule *to = historicRules[typeMapData[ttidx]]; + TimeZoneRule *from = historicRules[typeMapData[ttidx-1]]; + UDate startTime = (UDate)transitionTime(ttidx); + + // The transitions loaded from zoneinfo.res may contain non-transition data + UnicodeString fromName, toName; + from->getName(fromName); + to->getName(toName); + if (fromName == toName && from->getRawOffset() == to->getRawOffset() + && from->getDSTSavings() == to->getDSTSavings()) { + return getPreviousTransition(startTime, false, result); + } + result.setTime(startTime); + result.adoptFrom(from->clone()); + result.adoptTo(to->clone()); + return true; + } + } + return false; +} + +int32_t +OlsonTimeZone::countTransitionRules(UErrorCode& status) const { + if (U_FAILURE(status)) { + return 0; + } + checkTransitionRules(status); + if (U_FAILURE(status)) { + return 0; + } + + int32_t count = 0; + if (historicRules != nullptr) { + // historicRules may contain null entries when original zoneinfo data + // includes non transition data. + for (int32_t i = 0; i < historicRuleCount; i++) { + if (historicRules[i] != nullptr) { + count++; + } + } + } + if (finalZone != nullptr) { + if (finalZone->useDaylightTime()) { + count += 2; + } else { + count++; + } + } + return count; +} + +void +OlsonTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, + const TimeZoneRule* trsrules[], + int32_t& trscount, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + checkTransitionRules(status); + if (U_FAILURE(status)) { + return; + } + + // Initial rule + initial = initialRule; + + // Transition rules + int32_t cnt = 0; + if (historicRules != nullptr && trscount > cnt) { + // historicRules may contain null entries when original zoneinfo data + // includes non transition data. + for (int32_t i = 0; i < historicRuleCount; i++) { + if (historicRules[i] != nullptr) { + trsrules[cnt++] = historicRules[i]; + if (cnt >= trscount) { + break; + } + } + } + } + if (finalZoneWithStartYear != nullptr && trscount > cnt) { + const InitialTimeZoneRule *tmpini; + int32_t tmpcnt = trscount - cnt; + finalZoneWithStartYear->getTimeZoneRules(tmpini, &trsrules[cnt], tmpcnt, status); + if (U_FAILURE(status)) { + return; + } + cnt += tmpcnt; + } + // Set the result length + trscount = cnt; +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING + +//eof diff --git a/deps/icu-small/source/i18n/olsontz.h b/deps/icu-small/source/i18n/olsontz.h index 525bbd2b129ccd..0537535c61fe0b 100644 --- a/deps/icu-small/source/i18n/olsontz.h +++ b/deps/icu-small/source/i18n/olsontz.h @@ -1,455 +1,455 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2003-2013, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Author: Alan Liu -* Created: July 21 2003 -* Since: ICU 2.8 -********************************************************************** -*/ -#ifndef OLSONTZ_H -#define OLSONTZ_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/basictz.h" -#include "umutex.h" - -struct UResourceBundle; - -U_NAMESPACE_BEGIN - -class SimpleTimeZone; - -/** - * A time zone based on the Olson tz database. Olson time zones change - * behavior over time. The raw offset, rules, presence or absence of - * daylight savings time, and even the daylight savings amount can all - * vary. - * - * This class uses a resource bundle named "zoneinfo". Zoneinfo is a - * table containing different kinds of resources. In several places, - * zones are referred to using integers. A zone's integer is a number - * from 0..n-1, where n is the number of zones, with the zones sorted - * in lexicographic order. - * - * 1. Zones. These have keys corresponding to the Olson IDs, e.g., - * "Asia/Shanghai". Each resource describes the behavior of the given - * zone. Zones come in two different formats. - * - * a. Zone (table). A zone is a table resource contains several - * type of resources below: - * - * - typeOffsets:intvector (Required) - * - * Sets of UTC raw/dst offset pairs in seconds. Entries at - * 2n represents raw offset and 2n+1 represents dst offset - * paired with the raw offset at 2n. The very first pair represents - * the initial zone offset (before the first transition) always. - * - * - trans:intvector (Optional) - * - * List of transition times represented by 32bit seconds from the - * epoch (1970-01-01T00:00Z) in ascending order. - * - * - transPre32/transPost32:intvector (Optional) - * - * List of transition times before/after 32bit minimum seconds. - * Each time is represented by a pair of 32bit integer. - * - * - typeMap:bin (Optional) - * - * Array of bytes representing the mapping between each transition - * time (transPre32/trans/transPost32) and its corresponding offset - * data (typeOffsets). - * - * - finalRule:string (Optional) - * - * If a recurrent transition rule is applicable to a zone forever - * after the final transition time, finalRule represents the rule - * in Rules data. - * - * - finalRaw:int (Optional) - * - * When finalRule is available, finalRaw is required and specifies - * the raw (base) offset of the rule. - * - * - finalYear:int (Optional) - * - * When finalRule is available, finalYear is required and specifies - * the start year of the rule. - * - * - links:intvector (Optional) - * - * When this zone data is shared with other zones, links specifies - * all zones including the zone itself. Each zone is referenced by - * integer index. - * - * b. Link (int, length 1). A link zone is an int resource. The - * integer is the zone number of the target zone. The key of this - * resource is an alternate name for the target zone. This data - * is corresponding to Link data in the tz database. - * - * - * 2. Rules. These have keys corresponding to the Olson rule IDs, - * with an underscore prepended, e.g., "_EU". Each resource describes - * the behavior of the given rule using an intvector, containing the - * onset list, the cessation list, and the DST savings. The onset and - * cessation lists consist of the month, dowim, dow, time, and time - * mode. The end result is that the 11 integers describing the rule - * can be passed directly into the SimpleTimeZone 13-argument - * constructor (the other two arguments will be the raw offset, taken - * from the complex zone element 5, and the ID string, which is not - * used), with the times and the DST savings multiplied by 1000 to - * scale from seconds to milliseconds. - * - * 3. Regions. An array specifies mapping between zones and regions. - * Each item is either a 2-letter ISO country code or "001" - * (UN M.49 - World). This data is generated from "zone.tab" - * in the tz database. - */ -class U_I18N_API OlsonTimeZone: public BasicTimeZone { - public: - /** - * Construct from a resource bundle. - * @param top the top-level zoneinfo resource bundle. This is used - * to lookup the rule that `res' may refer to, if there is one. - * @param res the resource bundle of the zone to be constructed - * @param tzid the time zone ID - * @param ec input-output error code - */ - OlsonTimeZone(const UResourceBundle* top, - const UResourceBundle* res, - const UnicodeString& tzid, - UErrorCode& ec); - - /** - * Copy constructor - */ - OlsonTimeZone(const OlsonTimeZone& other); - - /** - * Destructor - */ - virtual ~OlsonTimeZone(); - - /** - * Assignment operator - */ - OlsonTimeZone& operator=(const OlsonTimeZone& other); - - /** - * Returns true if the two TimeZone objects are equal. - */ - virtual bool operator==(const TimeZone& other) const override; - - /** - * TimeZone API. - */ - virtual OlsonTimeZone* clone() const override; - - /** - * TimeZone API. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - /** - * TimeZone API. - */ - virtual UClassID getDynamicClassID() const override; - - /** - * TimeZone API. Do not call this; prefer getOffset(UDate,...). - */ - virtual int32_t getOffset(uint8_t era, int32_t year, int32_t month, - int32_t day, uint8_t dayOfWeek, - int32_t millis, UErrorCode& ec) const override; - - /** - * TimeZone API. Do not call this; prefer getOffset(UDate,...). - */ - virtual int32_t getOffset(uint8_t era, int32_t year, int32_t month, - int32_t day, uint8_t dayOfWeek, - int32_t millis, int32_t monthLength, - UErrorCode& ec) const override; - - /** - * TimeZone API. - */ - virtual void getOffset(UDate date, UBool local, int32_t& rawOffset, - int32_t& dstOffset, UErrorCode& ec) const override; - - /** - * BasicTimeZone API. - */ - virtual void getOffsetFromLocal( - UDate date, UTimeZoneLocalOption nonExistingTimeOpt, - UTimeZoneLocalOption duplicatedTimeOpt, - int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) const override; - - /** - * TimeZone API. This method has no effect since objects of this - * class are quasi-immutable (the base class allows the ID to be - * changed). - */ - virtual void setRawOffset(int32_t offsetMillis) override; - - /** - * TimeZone API. For a historical zone, the raw offset can change - * over time, so this API is not useful. In order to approximate - * expected behavior, this method returns the raw offset for the - * current moment in time. - */ - virtual int32_t getRawOffset() const override; - - /** - * TimeZone API. For a historical zone, whether DST is used or - * not varies over time. In order to approximate expected - * behavior, this method returns true if DST is observed at any - * point in the current year. - */ - virtual UBool useDaylightTime() const override; - - /** - * TimeZone API. - */ - virtual UBool inDaylightTime(UDate date, UErrorCode& ec) const override; - - /** - * TimeZone API. - */ - virtual int32_t getDSTSavings() const override; - - /** - * TimeZone API. Also comare historic transitions. - */ - virtual UBool hasSameRules(const TimeZone& other) const override; - - /** - * BasicTimeZone API. - * Gets the first time zone transition after the base time. - * @param base The base time. - * @param inclusive Whether the base time is inclusive or not. - * @param result Receives the first transition after the base time. - * @return true if the transition is found. - */ - virtual UBool getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const override; - - /** - * BasicTimeZone API. - * Gets the most recent time zone transition before the base time. - * @param base The base time. - * @param inclusive Whether the base time is inclusive or not. - * @param result Receives the most recent transition before the base time. - * @return true if the transition is found. - */ - virtual UBool getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const override; - - /** - * BasicTimeZone API. - * Returns the number of TimeZoneRules which represents time transitions, - * for this time zone, that is, all TimeZoneRules for this time zone except - * InitialTimeZoneRule. The return value range is 0 or any positive value. - * @param status Receives error status code. - * @return The number of TimeZoneRules representing time transitions. - */ - virtual int32_t countTransitionRules(UErrorCode& status) const override; - - /** - * Gets the InitialTimeZoneRule and the set of TimeZoneRule - * which represent time transitions for this time zone. On successful return, - * the argument initial points to non-NULL InitialTimeZoneRule and - * the array trsrules is filled with 0 or multiple TimeZoneRule - * instances up to the size specified by trscount. The results are referencing the - * rule instance held by this time zone instance. Therefore, after this time zone - * is destructed, they are no longer available. - * @param initial Receives the initial timezone rule - * @param trsrules Receives the timezone transition rules - * @param trscount On input, specify the size of the array 'transitions' receiving - * the timezone transition rules. On output, actual number of - * rules filled in the array will be set. - * @param status Receives error status code. - */ - virtual void getTimeZoneRules(const InitialTimeZoneRule*& initial, - const TimeZoneRule* trsrules[], int32_t& trscount, UErrorCode& status) const override; - - /** - * Internal API returning the canonical ID of this zone. - * This ID won't be affected by setID(). - */ - const UChar *getCanonicalID() const; - -private: - /** - * Default constructor. Creates a time zone with an empty ID and - * a fixed GMT offset of zero. - */ - OlsonTimeZone(); - -private: - - void constructEmpty(); - - void getHistoricalOffset(UDate date, UBool local, - int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt, - int32_t& rawoff, int32_t& dstoff) const; - - int16_t transitionCount() const; - - int64_t transitionTimeInSeconds(int16_t transIdx) const; - double transitionTime(int16_t transIdx) const; - - /* - * Following 3 methods return an offset at the given transition time index. - * When the index is negative, return the initial offset. - */ - int32_t zoneOffsetAt(int16_t transIdx) const; - int32_t rawOffsetAt(int16_t transIdx) const; - int32_t dstOffsetAt(int16_t transIdx) const; - - /* - * Following methods return the initial offset. - */ - int32_t initialRawOffset() const; - int32_t initialDstOffset() const; - - /** - * Number of transitions in each time range - */ - int16_t transitionCountPre32; - int16_t transitionCount32; - int16_t transitionCountPost32; - - /** - * Time of each transition in seconds from 1970 epoch before 32bit second range (<= 1900). - * Each transition in this range is represented by a pair of int32_t. - * Length is transitionCount int32_t's. NULL if no transitions in this range. - */ - const int32_t *transitionTimesPre32; // alias into res; do not delete - - /** - * Time of each transition in seconds from 1970 epoch in 32bit second range. - * Length is transitionCount int32_t's. NULL if no transitions in this range. - */ - const int32_t *transitionTimes32; // alias into res; do not delete - - /** - * Time of each transition in seconds from 1970 epoch after 32bit second range (>= 2038). - * Each transition in this range is represented by a pair of int32_t. - * Length is transitionCount int32_t's. NULL if no transitions in this range. - */ - const int32_t *transitionTimesPost32; // alias into res; do not delete - - /** - * Number of types, 1..255 - */ - int16_t typeCount; - - /** - * Offset from GMT in seconds for each type. - * Length is typeCount int32_t's. At least one type (a pair of int32_t) - * is required. - */ - const int32_t *typeOffsets; // alias into res; do not delete - - /** - * Type description data, consisting of transitionCount uint8_t - * type indices (from 0..typeCount-1). - * Length is transitionCount int16_t's. NULL if no transitions. - */ - const uint8_t *typeMapData; // alias into res; do not delete - - /** - * A SimpleTimeZone that governs the behavior for date >= finalMillis. - */ - SimpleTimeZone *finalZone; // owned, may be NULL - - /** - * For date >= finalMillis, the finalZone will be used. - */ - double finalStartMillis; - - /** - * For year >= finalYear, the finalZone will be used. - */ - int32_t finalStartYear; - - /* - * Canonical (CLDR) ID of this zone - */ - const UChar *canonicalID; - - /* BasicTimeZone support */ - void clearTransitionRules(void); - void deleteTransitionRules(void); - void checkTransitionRules(UErrorCode& status) const; - - public: // Internal, for access from plain C code - void initTransitionRules(UErrorCode& status); - private: - - InitialTimeZoneRule *initialRule; - TimeZoneTransition *firstTZTransition; - int16_t firstTZTransitionIdx; - TimeZoneTransition *firstFinalTZTransition; - TimeArrayTimeZoneRule **historicRules; - int16_t historicRuleCount; - SimpleTimeZone *finalZoneWithStartYear; // hack - UInitOnce transitionRulesInitOnce {}; -}; - -inline int16_t -OlsonTimeZone::transitionCount() const { - return transitionCountPre32 + transitionCount32 + transitionCountPost32; -} - -inline double -OlsonTimeZone::transitionTime(int16_t transIdx) const { - return (double)transitionTimeInSeconds(transIdx) * U_MILLIS_PER_SECOND; -} - -inline int32_t -OlsonTimeZone::zoneOffsetAt(int16_t transIdx) const { - int16_t typeIdx = (transIdx >= 0 ? typeMapData[transIdx] : 0) << 1; - return typeOffsets[typeIdx] + typeOffsets[typeIdx + 1]; -} - -inline int32_t -OlsonTimeZone::rawOffsetAt(int16_t transIdx) const { - int16_t typeIdx = (transIdx >= 0 ? typeMapData[transIdx] : 0) << 1; - return typeOffsets[typeIdx]; -} - -inline int32_t -OlsonTimeZone::dstOffsetAt(int16_t transIdx) const { - int16_t typeIdx = (transIdx >= 0 ? typeMapData[transIdx] : 0) << 1; - return typeOffsets[typeIdx + 1]; -} - -inline int32_t -OlsonTimeZone::initialRawOffset() const { - return typeOffsets[0]; -} - -inline int32_t -OlsonTimeZone::initialDstOffset() const { - return typeOffsets[1]; -} - -inline const UChar* -OlsonTimeZone::getCanonicalID() const { - return canonicalID; -} - - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_FORMATTING -#endif // OLSONTZ_H - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2003-2013, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: July 21 2003 +* Since: ICU 2.8 +********************************************************************** +*/ +#ifndef OLSONTZ_H +#define OLSONTZ_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/basictz.h" +#include "umutex.h" + +struct UResourceBundle; + +U_NAMESPACE_BEGIN + +class SimpleTimeZone; + +/** + * A time zone based on the Olson tz database. Olson time zones change + * behavior over time. The raw offset, rules, presence or absence of + * daylight savings time, and even the daylight savings amount can all + * vary. + * + * This class uses a resource bundle named "zoneinfo". Zoneinfo is a + * table containing different kinds of resources. In several places, + * zones are referred to using integers. A zone's integer is a number + * from 0..n-1, where n is the number of zones, with the zones sorted + * in lexicographic order. + * + * 1. Zones. These have keys corresponding to the Olson IDs, e.g., + * "Asia/Shanghai". Each resource describes the behavior of the given + * zone. Zones come in two different formats. + * + * a. Zone (table). A zone is a table resource contains several + * type of resources below: + * + * - typeOffsets:intvector (Required) + * + * Sets of UTC raw/dst offset pairs in seconds. Entries at + * 2n represents raw offset and 2n+1 represents dst offset + * paired with the raw offset at 2n. The very first pair represents + * the initial zone offset (before the first transition) always. + * + * - trans:intvector (Optional) + * + * List of transition times represented by 32bit seconds from the + * epoch (1970-01-01T00:00Z) in ascending order. + * + * - transPre32/transPost32:intvector (Optional) + * + * List of transition times before/after 32bit minimum seconds. + * Each time is represented by a pair of 32bit integer. + * + * - typeMap:bin (Optional) + * + * Array of bytes representing the mapping between each transition + * time (transPre32/trans/transPost32) and its corresponding offset + * data (typeOffsets). + * + * - finalRule:string (Optional) + * + * If a recurrent transition rule is applicable to a zone forever + * after the final transition time, finalRule represents the rule + * in Rules data. + * + * - finalRaw:int (Optional) + * + * When finalRule is available, finalRaw is required and specifies + * the raw (base) offset of the rule. + * + * - finalYear:int (Optional) + * + * When finalRule is available, finalYear is required and specifies + * the start year of the rule. + * + * - links:intvector (Optional) + * + * When this zone data is shared with other zones, links specifies + * all zones including the zone itself. Each zone is referenced by + * integer index. + * + * b. Link (int, length 1). A link zone is an int resource. The + * integer is the zone number of the target zone. The key of this + * resource is an alternate name for the target zone. This data + * is corresponding to Link data in the tz database. + * + * + * 2. Rules. These have keys corresponding to the Olson rule IDs, + * with an underscore prepended, e.g., "_EU". Each resource describes + * the behavior of the given rule using an intvector, containing the + * onset list, the cessation list, and the DST savings. The onset and + * cessation lists consist of the month, dowim, dow, time, and time + * mode. The end result is that the 11 integers describing the rule + * can be passed directly into the SimpleTimeZone 13-argument + * constructor (the other two arguments will be the raw offset, taken + * from the complex zone element 5, and the ID string, which is not + * used), with the times and the DST savings multiplied by 1000 to + * scale from seconds to milliseconds. + * + * 3. Regions. An array specifies mapping between zones and regions. + * Each item is either a 2-letter ISO country code or "001" + * (UN M.49 - World). This data is generated from "zone.tab" + * in the tz database. + */ +class U_I18N_API OlsonTimeZone: public BasicTimeZone { + public: + /** + * Construct from a resource bundle. + * @param top the top-level zoneinfo resource bundle. This is used + * to lookup the rule that `res' may refer to, if there is one. + * @param res the resource bundle of the zone to be constructed + * @param tzid the time zone ID + * @param ec input-output error code + */ + OlsonTimeZone(const UResourceBundle* top, + const UResourceBundle* res, + const UnicodeString& tzid, + UErrorCode& ec); + + /** + * Copy constructor + */ + OlsonTimeZone(const OlsonTimeZone& other); + + /** + * Destructor + */ + virtual ~OlsonTimeZone(); + + /** + * Assignment operator + */ + OlsonTimeZone& operator=(const OlsonTimeZone& other); + + /** + * Returns true if the two TimeZone objects are equal. + */ + virtual bool operator==(const TimeZone& other) const override; + + /** + * TimeZone API. + */ + virtual OlsonTimeZone* clone() const override; + + /** + * TimeZone API. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + /** + * TimeZone API. + */ + virtual UClassID getDynamicClassID() const override; + + /** + * TimeZone API. Do not call this; prefer getOffset(UDate,...). + */ + virtual int32_t getOffset(uint8_t era, int32_t year, int32_t month, + int32_t day, uint8_t dayOfWeek, + int32_t millis, UErrorCode& ec) const override; + + /** + * TimeZone API. Do not call this; prefer getOffset(UDate,...). + */ + virtual int32_t getOffset(uint8_t era, int32_t year, int32_t month, + int32_t day, uint8_t dayOfWeek, + int32_t millis, int32_t monthLength, + UErrorCode& ec) const override; + + /** + * TimeZone API. + */ + virtual void getOffset(UDate date, UBool local, int32_t& rawOffset, + int32_t& dstOffset, UErrorCode& ec) const override; + + /** + * BasicTimeZone API. + */ + virtual void getOffsetFromLocal( + UDate date, UTimeZoneLocalOption nonExistingTimeOpt, + UTimeZoneLocalOption duplicatedTimeOpt, + int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) const override; + + /** + * TimeZone API. This method has no effect since objects of this + * class are quasi-immutable (the base class allows the ID to be + * changed). + */ + virtual void setRawOffset(int32_t offsetMillis) override; + + /** + * TimeZone API. For a historical zone, the raw offset can change + * over time, so this API is not useful. In order to approximate + * expected behavior, this method returns the raw offset for the + * current moment in time. + */ + virtual int32_t getRawOffset() const override; + + /** + * TimeZone API. For a historical zone, whether DST is used or + * not varies over time. In order to approximate expected + * behavior, this method returns true if DST is observed at any + * point in the current year. + */ + virtual UBool useDaylightTime() const override; + + /** + * TimeZone API. + */ + virtual UBool inDaylightTime(UDate date, UErrorCode& ec) const override; + + /** + * TimeZone API. + */ + virtual int32_t getDSTSavings() const override; + + /** + * TimeZone API. Also comare historic transitions. + */ + virtual UBool hasSameRules(const TimeZone& other) const override; + + /** + * BasicTimeZone API. + * Gets the first time zone transition after the base time. + * @param base The base time. + * @param inclusive Whether the base time is inclusive or not. + * @param result Receives the first transition after the base time. + * @return true if the transition is found. + */ + virtual UBool getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const override; + + /** + * BasicTimeZone API. + * Gets the most recent time zone transition before the base time. + * @param base The base time. + * @param inclusive Whether the base time is inclusive or not. + * @param result Receives the most recent transition before the base time. + * @return true if the transition is found. + */ + virtual UBool getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const override; + + /** + * BasicTimeZone API. + * Returns the number of TimeZoneRules which represents time transitions, + * for this time zone, that is, all TimeZoneRules for this time zone except + * InitialTimeZoneRule. The return value range is 0 or any positive value. + * @param status Receives error status code. + * @return The number of TimeZoneRules representing time transitions. + */ + virtual int32_t countTransitionRules(UErrorCode& status) const override; + + /** + * Gets the InitialTimeZoneRule and the set of TimeZoneRule + * which represent time transitions for this time zone. On successful return, + * the argument initial points to non-nullptr InitialTimeZoneRule and + * the array trsrules is filled with 0 or multiple TimeZoneRule + * instances up to the size specified by trscount. The results are referencing the + * rule instance held by this time zone instance. Therefore, after this time zone + * is destructed, they are no longer available. + * @param initial Receives the initial timezone rule + * @param trsrules Receives the timezone transition rules + * @param trscount On input, specify the size of the array 'transitions' receiving + * the timezone transition rules. On output, actual number of + * rules filled in the array will be set. + * @param status Receives error status code. + */ + virtual void getTimeZoneRules(const InitialTimeZoneRule*& initial, + const TimeZoneRule* trsrules[], int32_t& trscount, UErrorCode& status) const override; + + /** + * Internal API returning the canonical ID of this zone. + * This ID won't be affected by setID(). + */ + const char16_t *getCanonicalID() const; + +private: + /** + * Default constructor. Creates a time zone with an empty ID and + * a fixed GMT offset of zero. + */ + OlsonTimeZone(); + +private: + + void constructEmpty(); + + void getHistoricalOffset(UDate date, UBool local, + int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt, + int32_t& rawoff, int32_t& dstoff) const; + + int16_t transitionCount() const; + + int64_t transitionTimeInSeconds(int16_t transIdx) const; + double transitionTime(int16_t transIdx) const; + + /* + * Following 3 methods return an offset at the given transition time index. + * When the index is negative, return the initial offset. + */ + int32_t zoneOffsetAt(int16_t transIdx) const; + int32_t rawOffsetAt(int16_t transIdx) const; + int32_t dstOffsetAt(int16_t transIdx) const; + + /* + * Following methods return the initial offset. + */ + int32_t initialRawOffset() const; + int32_t initialDstOffset() const; + + /** + * Number of transitions in each time range + */ + int16_t transitionCountPre32; + int16_t transitionCount32; + int16_t transitionCountPost32; + + /** + * Time of each transition in seconds from 1970 epoch before 32bit second range (<= 1900). + * Each transition in this range is represented by a pair of int32_t. + * Length is transitionCount int32_t's. nullptr if no transitions in this range. + */ + const int32_t *transitionTimesPre32; // alias into res; do not delete + + /** + * Time of each transition in seconds from 1970 epoch in 32bit second range. + * Length is transitionCount int32_t's. nullptr if no transitions in this range. + */ + const int32_t *transitionTimes32; // alias into res; do not delete + + /** + * Time of each transition in seconds from 1970 epoch after 32bit second range (>= 2038). + * Each transition in this range is represented by a pair of int32_t. + * Length is transitionCount int32_t's. nullptr if no transitions in this range. + */ + const int32_t *transitionTimesPost32; // alias into res; do not delete + + /** + * Number of types, 1..255 + */ + int16_t typeCount; + + /** + * Offset from GMT in seconds for each type. + * Length is typeCount int32_t's. At least one type (a pair of int32_t) + * is required. + */ + const int32_t *typeOffsets; // alias into res; do not delete + + /** + * Type description data, consisting of transitionCount uint8_t + * type indices (from 0..typeCount-1). + * Length is transitionCount int16_t's. nullptr if no transitions. + */ + const uint8_t *typeMapData; // alias into res; do not delete + + /** + * A SimpleTimeZone that governs the behavior for date >= finalMillis. + */ + SimpleTimeZone *finalZone; // owned, may be nullptr + + /** + * For date >= finalMillis, the finalZone will be used. + */ + double finalStartMillis; + + /** + * For year >= finalYear, the finalZone will be used. + */ + int32_t finalStartYear; + + /* + * Canonical (CLDR) ID of this zone + */ + const char16_t *canonicalID; + + /* BasicTimeZone support */ + void clearTransitionRules(); + void deleteTransitionRules(); + void checkTransitionRules(UErrorCode& status) const; + + public: // Internal, for access from plain C code + void initTransitionRules(UErrorCode& status); + private: + + InitialTimeZoneRule *initialRule; + TimeZoneTransition *firstTZTransition; + int16_t firstTZTransitionIdx; + TimeZoneTransition *firstFinalTZTransition; + TimeArrayTimeZoneRule **historicRules; + int16_t historicRuleCount; + SimpleTimeZone *finalZoneWithStartYear; // hack + UInitOnce transitionRulesInitOnce {}; +}; + +inline int16_t +OlsonTimeZone::transitionCount() const { + return transitionCountPre32 + transitionCount32 + transitionCountPost32; +} + +inline double +OlsonTimeZone::transitionTime(int16_t transIdx) const { + return (double)transitionTimeInSeconds(transIdx) * U_MILLIS_PER_SECOND; +} + +inline int32_t +OlsonTimeZone::zoneOffsetAt(int16_t transIdx) const { + int16_t typeIdx = (transIdx >= 0 ? typeMapData[transIdx] : 0) << 1; + return typeOffsets[typeIdx] + typeOffsets[typeIdx + 1]; +} + +inline int32_t +OlsonTimeZone::rawOffsetAt(int16_t transIdx) const { + int16_t typeIdx = (transIdx >= 0 ? typeMapData[transIdx] : 0) << 1; + return typeOffsets[typeIdx]; +} + +inline int32_t +OlsonTimeZone::dstOffsetAt(int16_t transIdx) const { + int16_t typeIdx = (transIdx >= 0 ? typeMapData[transIdx] : 0) << 1; + return typeOffsets[typeIdx + 1]; +} + +inline int32_t +OlsonTimeZone::initialRawOffset() const { + return typeOffsets[0]; +} + +inline int32_t +OlsonTimeZone::initialDstOffset() const { + return typeOffsets[1]; +} + +inline const char16_t* +OlsonTimeZone::getCanonicalID() const { + return canonicalID; +} + + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING +#endif // OLSONTZ_H + +//eof diff --git a/deps/icu-small/source/i18n/persncal.cpp b/deps/icu-small/source/i18n/persncal.cpp index 9db47c9891200f..e2d0dc2cd49f51 100644 --- a/deps/icu-small/source/i18n/persncal.cpp +++ b/deps/icu-small/source/i18n/persncal.cpp @@ -1,295 +1,301 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ****************************************************************************** - * Copyright (C) 2003-2013, International Business Machines Corporation - * and others. All Rights Reserved. - ****************************************************************************** - * - * File PERSNCAL.CPP - * - * Modification History: - * - * Date Name Description - * 9/23/2003 mehran posted to icu-design - * 10/1/2012 roozbeh Fixed algorithm and heavily refactored and rewrote - * based on the implementation of Gregorian - ***************************************************************************** - */ - -#include "persncal.h" - -#if !UCONFIG_NO_FORMATTING - -#include "umutex.h" -#include "gregoimp.h" // Math -#include - -static const int16_t kPersianNumDays[] -= {0,31,62,93,124,155,186,216,246,276,306,336}; // 0-based, for day-in-year -static const int8_t kPersianMonthLength[] -= {31,31,31,31,31,31,30,30,30,30,30,29}; // 0-based -static const int8_t kPersianLeapMonthLength[] -= {31,31,31,31,31,31,30,30,30,30,30,30}; // 0-based - -static const int32_t kPersianCalendarLimits[UCAL_FIELD_COUNT][4] = { - // Minimum Greatest Least Maximum - // Minimum Maximum - { 0, 0, 0, 0}, // ERA - { -5000000, -5000000, 5000000, 5000000}, // YEAR - { 0, 0, 11, 11}, // MONTH - { 1, 1, 52, 53}, // WEEK_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH - { 1, 1, 29, 31}, // DAY_OF_MONTH - { 1, 1, 365, 366}, // DAY_OF_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK - { 1, 1, 5, 5}, // DAY_OF_WEEK_IN_MONTH - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET - { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL - { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY - {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH -}; - -U_NAMESPACE_BEGIN - -static const int32_t PERSIAN_EPOCH = 1948320; - -// Implementation of the PersianCalendar class - -//------------------------------------------------------------------------- -// Constructors... -//------------------------------------------------------------------------- - -const char *PersianCalendar::getType() const { - return "persian"; -} - -PersianCalendar* PersianCalendar::clone() const { - return new PersianCalendar(*this); -} - -PersianCalendar::PersianCalendar(const Locale& aLocale, UErrorCode& success) - : Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success) -{ - setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. -} - -PersianCalendar::PersianCalendar(const PersianCalendar& other) : Calendar(other) { -} - -PersianCalendar::~PersianCalendar() -{ -} - -//------------------------------------------------------------------------- -// Minimum / Maximum access functions -//------------------------------------------------------------------------- - - -int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { - return kPersianCalendarLimits[field][limitType]; -} - -//------------------------------------------------------------------------- -// Assorted calculation utilities -// - -/** - * Determine whether a year is a leap year in the Persian calendar - */ -UBool PersianCalendar::isLeapYear(int32_t year) -{ - int32_t remainder; - ClockMath::floorDivide(25 * year + 11, 33, &remainder); - return (remainder < 8); -} - -/** - * Return the day # on which the given year starts. Days are counted - * from the Persian epoch, origin 0. - */ -int32_t PersianCalendar::yearStart(int32_t year) { - return handleComputeMonthStart(year,0,false); -} - -/** - * Return the day # on which the given month starts. Days are counted - * from the Persian epoch, origin 0. - * - * @param year The Persian year - * @param year The Persian month, 0-based - */ -int32_t PersianCalendar::monthStart(int32_t year, int32_t month) const { - return handleComputeMonthStart(year,month,true); -} - -//---------------------------------------------------------------------- -// Calendar framework -//---------------------------------------------------------------------- - -/** - * Return the length (in days) of the given month. - * - * @param year The Persian year - * @param year The Persian month, 0-based - */ -int32_t PersianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { - // If the month is out of range, adjust it into range, and - // modify the extended year value accordingly. - if (month < 0 || month > 11) { - extendedYear += ClockMath::floorDivide(month, 12, &month); - } - - return isLeapYear(extendedYear) ? kPersianLeapMonthLength[month] : kPersianMonthLength[month]; -} - -/** - * Return the number of days in the given Persian year - */ -int32_t PersianCalendar::handleGetYearLength(int32_t extendedYear) const { - return isLeapYear(extendedYear) ? 366 : 365; -} - -//------------------------------------------------------------------------- -// Functions for converting from field values to milliseconds.... -//------------------------------------------------------------------------- - -// Return JD of start of given month/year -int32_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/) const { - // If the month is out of range, adjust it into range, and - // modify the extended year value accordingly. - if (month < 0 || month > 11) { - eyear += ClockMath::floorDivide(month, 12, &month); - } - - int32_t julianDay = PERSIAN_EPOCH - 1 + 365 * (eyear - 1) + ClockMath::floorDivide(8 * eyear + 21, 33); - - if (month != 0) { - julianDay += kPersianNumDays[month]; - } - - return julianDay; -} - -//------------------------------------------------------------------------- -// Functions for converting from milliseconds to field values -//------------------------------------------------------------------------- - -int32_t PersianCalendar::handleGetExtendedYear() { - int32_t year; - if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { - year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 - } else { - year = internalGet(UCAL_YEAR, 1); // Default to year 1 - } - return year; -} - -/** - * Override Calendar to compute several fields specific to the Persian - * calendar system. These are: - * - *

      • ERA - *
      • YEAR - *
      • MONTH - *
      • DAY_OF_MONTH - *
      • DAY_OF_YEAR - *
      • EXTENDED_YEAR
      - * - * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this - * method is called. - */ -void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) { - int32_t year, month, dayOfMonth, dayOfYear; - - int32_t daysSinceEpoch = julianDay - PERSIAN_EPOCH; - year = 1 + (int32_t)ClockMath::floorDivide(33 * (int64_t)daysSinceEpoch + 3, (int64_t)12053); - - int32_t farvardin1 = 365 * (year - 1) + ClockMath::floorDivide(8 * year + 21, 33); - dayOfYear = (daysSinceEpoch - farvardin1); // 0-based - if (dayOfYear < 216) { // Compute 0-based month - month = dayOfYear / 31; - } else { - month = (dayOfYear - 6) / 30; - } - dayOfMonth = dayOfYear - kPersianNumDays[month] + 1; - ++dayOfYear; // Make it 1-based now - - internalSet(UCAL_ERA, 0); - internalSet(UCAL_YEAR, year); - internalSet(UCAL_EXTENDED_YEAR, year); - internalSet(UCAL_MONTH, month); - internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); - internalSet(UCAL_DAY_OF_YEAR, dayOfYear); -} - -UBool -PersianCalendar::inDaylightTime(UErrorCode& status) const -{ - // copied from GregorianCalendar - if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) - return false; - - // Force an update of the state of the Calendar. - ((PersianCalendar*)this)->complete(status); // cast away const - - return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : false); -} - -// default century - -static UDate gSystemDefaultCenturyStart = DBL_MIN; -static int32_t gSystemDefaultCenturyStartYear = -1; -static icu::UInitOnce gSystemDefaultCenturyInit {}; - -UBool PersianCalendar::haveDefaultCentury() const -{ - return true; -} - -static void U_CALLCONV initializeSystemDefaultCentury() { - // initialize systemDefaultCentury and systemDefaultCenturyYear based - // on the current time. They'll be set to 80 years before - // the current time. - UErrorCode status = U_ZERO_ERROR; - PersianCalendar calendar(Locale("@calendar=persian"),status); - if (U_SUCCESS(status)) - { - calendar.setTime(Calendar::getNow(), status); - calendar.add(UCAL_YEAR, -80, status); - - gSystemDefaultCenturyStart = calendar.getTime(status); - gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); - } - // We have no recourse upon failure unless we want to propagate the failure - // out. -} - -UDate PersianCalendar::defaultCenturyStart() const { - // lazy-evaluate systemDefaultCenturyStart - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStart; -} - -int32_t PersianCalendar::defaultCenturyStartYear() const { - // lazy-evaluate systemDefaultCenturyStartYear - umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - return gSystemDefaultCenturyStartYear; -} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PersianCalendar) - -U_NAMESPACE_END - -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ****************************************************************************** + * Copyright (C) 2003-2013, International Business Machines Corporation + * and others. All Rights Reserved. + ****************************************************************************** + * + * File PERSNCAL.CPP + * + * Modification History: + * + * Date Name Description + * 9/23/2003 mehran posted to icu-design + * 10/1/2012 roozbeh Fixed algorithm and heavily refactored and rewrote + * based on the implementation of Gregorian + ***************************************************************************** + */ + +#include "persncal.h" + +#if !UCONFIG_NO_FORMATTING + +#include "umutex.h" +#include "gregoimp.h" // Math +#include + +static const int16_t kPersianNumDays[] += {0,31,62,93,124,155,186,216,246,276,306,336}; // 0-based, for day-in-year +static const int8_t kPersianMonthLength[] += {31,31,31,31,31,31,30,30,30,30,30,29}; // 0-based +static const int8_t kPersianLeapMonthLength[] += {31,31,31,31,31,31,30,30,30,30,30,30}; // 0-based + +static const int32_t kPersianCalendarLimits[UCAL_FIELD_COUNT][4] = { + // Minimum Greatest Least Maximum + // Minimum Maximum + { 0, 0, 0, 0}, // ERA + { -5000000, -5000000, 5000000, 5000000}, // YEAR + { 0, 0, 11, 11}, // MONTH + { 1, 1, 52, 53}, // WEEK_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH + { 1, 1, 29, 31}, // DAY_OF_MONTH + { 1, 1, 365, 366}, // DAY_OF_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK + { 1, 1, 5, 5}, // DAY_OF_WEEK_IN_MONTH + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET + { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL + { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH + { 0, 0, 11, 11}, // ORDINAL_MONTH +}; + +U_NAMESPACE_BEGIN + +static const int32_t PERSIAN_EPOCH = 1948320; + +// Implementation of the PersianCalendar class + +//------------------------------------------------------------------------- +// Constructors... +//------------------------------------------------------------------------- + +const char *PersianCalendar::getType() const { + return "persian"; +} + +PersianCalendar* PersianCalendar::clone() const { + return new PersianCalendar(*this); +} + +PersianCalendar::PersianCalendar(const Locale& aLocale, UErrorCode& success) + : Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success) +{ + setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. +} + +PersianCalendar::PersianCalendar(const PersianCalendar& other) : Calendar(other) { +} + +PersianCalendar::~PersianCalendar() +{ +} + +//------------------------------------------------------------------------- +// Minimum / Maximum access functions +//------------------------------------------------------------------------- + + +int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { + return kPersianCalendarLimits[field][limitType]; +} + +//------------------------------------------------------------------------- +// Assorted calculation utilities +// + +/** + * Determine whether a year is a leap year in the Persian calendar + */ +UBool PersianCalendar::isLeapYear(int32_t year) +{ + int32_t remainder; + ClockMath::floorDivide(25 * year + 11, 33, &remainder); + return (remainder < 8); +} + +/** + * Return the day # on which the given year starts. Days are counted + * from the Persian epoch, origin 0. + */ +int32_t PersianCalendar::yearStart(int32_t year) { + return handleComputeMonthStart(year,0,false); +} + +/** + * Return the day # on which the given month starts. Days are counted + * from the Persian epoch, origin 0. + * + * @param year The Persian year + * @param year The Persian month, 0-based + */ +int32_t PersianCalendar::monthStart(int32_t year, int32_t month) const { + return handleComputeMonthStart(year,month,true); +} + +//---------------------------------------------------------------------- +// Calendar framework +//---------------------------------------------------------------------- + +/** + * Return the length (in days) of the given month. + * + * @param year The Persian year + * @param year The Persian month, 0-based + */ +int32_t PersianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { + // If the month is out of range, adjust it into range, and + // modify the extended year value accordingly. + if (month < 0 || month > 11) { + extendedYear += ClockMath::floorDivide(month, 12, &month); + } + + return isLeapYear(extendedYear) ? kPersianLeapMonthLength[month] : kPersianMonthLength[month]; +} + +/** + * Return the number of days in the given Persian year + */ +int32_t PersianCalendar::handleGetYearLength(int32_t extendedYear) const { + return isLeapYear(extendedYear) ? 366 : 365; +} + +//------------------------------------------------------------------------- +// Functions for converting from field values to milliseconds.... +//------------------------------------------------------------------------- + +// Return JD of start of given month/year +int32_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/) const { + // If the month is out of range, adjust it into range, and + // modify the extended year value accordingly. + if (month < 0 || month > 11) { + eyear += ClockMath::floorDivide(month, 12, &month); + } + + int32_t julianDay = PERSIAN_EPOCH - 1 + 365 * (eyear - 1) + ClockMath::floorDivide(8 * eyear + 21, 33); + + if (month != 0) { + julianDay += kPersianNumDays[month]; + } + + return julianDay; +} + +//------------------------------------------------------------------------- +// Functions for converting from milliseconds to field values +//------------------------------------------------------------------------- + +int32_t PersianCalendar::handleGetExtendedYear() { + int32_t year; + if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { + year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 + } else { + year = internalGet(UCAL_YEAR, 1); // Default to year 1 + } + return year; +} + +/** + * Override Calendar to compute several fields specific to the Persian + * calendar system. These are: + * + *
      • ERA + *
      • YEAR + *
      • MONTH + *
      • DAY_OF_MONTH + *
      • DAY_OF_YEAR + *
      • EXTENDED_YEAR
      + * + * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this + * method is called. + */ +void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) { + int32_t year, month, dayOfMonth, dayOfYear; + + int32_t daysSinceEpoch = julianDay - PERSIAN_EPOCH; + year = 1 + (int32_t)ClockMath::floorDivide(33 * (int64_t)daysSinceEpoch + 3, (int64_t)12053); + + int32_t farvardin1 = 365 * (year - 1) + ClockMath::floorDivide(8 * year + 21, 33); + dayOfYear = (daysSinceEpoch - farvardin1); // 0-based + if (dayOfYear < 216) { // Compute 0-based month + month = dayOfYear / 31; + } else { + month = (dayOfYear - 6) / 30; + } + dayOfMonth = dayOfYear - kPersianNumDays[month] + 1; + ++dayOfYear; // Make it 1-based now + + internalSet(UCAL_ERA, 0); + internalSet(UCAL_YEAR, year); + internalSet(UCAL_EXTENDED_YEAR, year); + internalSet(UCAL_MONTH, month); + internalSet(UCAL_ORDINAL_MONTH, month); + internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); + internalSet(UCAL_DAY_OF_YEAR, dayOfYear); +} + +constexpr uint32_t kPersianRelatedYearDiff = 622; + +int32_t PersianCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kPersianRelatedYearDiff; +} + +void PersianCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kPersianRelatedYearDiff); +} + +// default century + +static UDate gSystemDefaultCenturyStart = DBL_MIN; +static int32_t gSystemDefaultCenturyStartYear = -1; +static icu::UInitOnce gSystemDefaultCenturyInit {}; + +UBool PersianCalendar::haveDefaultCentury() const +{ + return true; +} + +static void U_CALLCONV initializeSystemDefaultCentury() { + // initialize systemDefaultCentury and systemDefaultCenturyYear based + // on the current time. They'll be set to 80 years before + // the current time. + UErrorCode status = U_ZERO_ERROR; + PersianCalendar calendar(Locale("@calendar=persian"),status); + if (U_SUCCESS(status)) + { + calendar.setTime(Calendar::getNow(), status); + calendar.add(UCAL_YEAR, -80, status); + + gSystemDefaultCenturyStart = calendar.getTime(status); + gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); + } + // We have no recourse upon failure unless we want to propagate the failure + // out. +} + +UDate PersianCalendar::defaultCenturyStart() const { + // lazy-evaluate systemDefaultCenturyStart + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStart; +} + +int32_t PersianCalendar::defaultCenturyStartYear() const { + // lazy-evaluate systemDefaultCenturyStartYear + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); + return gSystemDefaultCenturyStartYear; +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PersianCalendar) + +U_NAMESPACE_END + +#endif + diff --git a/deps/icu-small/source/i18n/persncal.h b/deps/icu-small/source/i18n/persncal.h index d0f2ee5ec209e0..18494b7216fe76 100644 --- a/deps/icu-small/source/i18n/persncal.h +++ b/deps/icu-small/source/i18n/persncal.h @@ -1,323 +1,325 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ****************************************************************************** - * Copyright (C) 2003-2013, International Business Machines Corporation - * and others. All Rights Reserved. - ****************************************************************************** - * - * File PERSNCAL.H - * - * Modification History: - * - * Date Name Description - * 9/23/2003 mehran posted to icu-design - ***************************************************************************** - */ - -#ifndef PERSNCAL_H -#define PERSNCAL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/calendar.h" - -U_NAMESPACE_BEGIN - -/** - * PersianCalendar is a subclass of Calendar - * that implements the Persian calendar. It is used as the official - * calendar in Iran. This calendar is also known as the "Hijri Shamsi" - * calendar, since it starts at the time of Mohammed's emigration (or - * "hijra") to Medinah on Thursday, July 15, 622 AD (Julian) and is a - * solar calendar system (or "shamsi"). - *

      - * The Persian calendar is strictly solar, and thus a Persian year has twelve - * solar months. A Persian year is about 365 days long, except in leap years - * which is 366 days long. - *

      - * The six first months of Persian Calendar are 31 days long. The next five - * months are 30 days long. The last month is 29 days long in normal years, - * and 30 days long in leap years. - * - * @see GregorianCalendar - * - * @author Mehran Mehr - * @internal - */ -class PersianCalendar : public Calendar { - public: - //------------------------------------------------------------------------- - // Constants... - //------------------------------------------------------------------------- - /** - * Constants for the months - * @internal - */ - enum EMonths { - /** - * Constant for Farvardin, the 1st month of the Persian year. - * @internal - */ - FARVARDIN = 0, - - /** - * Constant for Ordibehesht, the 2nd month of the Persian year. - * @internal - */ - ORDIBEHESHT = 1, - - /** - * Constant for Khordad, the 3rd month of the Persian year. - * @internal - */ - KHORDAD = 2, - - /** - * Constant for Tir, the 4th month of the Persian year. - * @internal - */ - TIR = 3, - - /** - * Constant for Mordad, the 5th month of the Persian year. - * @internal - */ - MORDAD = 4, - - /** - * Constant for Shahrivar, the 6th month of the Persian year. - * @internal - */ - SHAHRIVAR = 5, - - /** - * Constant for Mehr, the 7th month of the Persian year. - * @internal - */ - MEHR = 6, - - /** - * Constant for Aban, the 8th month of the Persian year. - * @internal - */ - ABAN = 7, - - /** - * Constant for Azar, the 9th month of the Persian year. - * @internal - */ - AZAR = 8, - - /** - * Constant for Dei, the 10th month of the Persian year. - * @internal - */ - DEI = 9, - - /** - * Constant for Bahman, the 11th month of the Persian year. - * @internal - */ - BAHMAN = 10, - - /** - * Constant for Esfand, the 12th month of the Persian year. - * @internal - */ - ESFAND = 11, - - PERSIAN_MONTH_MAX - }; - - - - //------------------------------------------------------------------------- - // Constructors... - //------------------------------------------------------------------------- - - /** - * Constructs a PersianCalendar based on the current time in the default time zone - * with the given locale. - * - * @param aLocale The given locale. - * @param success Indicates the status of PersianCalendar object construction. - * Returns U_ZERO_ERROR if constructed successfully. - * @internal - */ - PersianCalendar(const Locale& aLocale, UErrorCode &success); - - /** - * Copy Constructor - * @internal - */ - PersianCalendar(const PersianCalendar& other); - - /** - * Destructor. - * @internal - */ - virtual ~PersianCalendar(); - - // TODO: copy c'tor, etc - - // clone - virtual PersianCalendar* clone() const override; - - private: - /** - * Determine whether a year is a leap year in the Persian calendar - */ - static UBool isLeapYear(int32_t year); - - /** - * Return the day # on which the given year starts. Days are counted - * from the Hijri epoch, origin 0. - */ - int32_t yearStart(int32_t year); - - /** - * Return the day # on which the given month starts. Days are counted - * from the Hijri epoch, origin 0. - * - * @param year The hijri shamsi year - * @param year The hijri shamsi month, 0-based - */ - int32_t monthStart(int32_t year, int32_t month) const; - - //---------------------------------------------------------------------- - // Calendar framework - //---------------------------------------------------------------------- - protected: - /** - * @internal - */ - virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; - - /** - * Return the length (in days) of the given month. - * - * @param year The hijri shamsi year - * @param year The hijri shamsi month, 0-based - * @internal - */ - virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; - - /** - * Return the number of days in the given Persian year - * @internal - */ - virtual int32_t handleGetYearLength(int32_t extendedYear) const override; - - //------------------------------------------------------------------------- - // Functions for converting from field values to milliseconds.... - //------------------------------------------------------------------------- - - // Return JD of start of given month/year - /** - * @internal - */ - virtual int32_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override; - - //------------------------------------------------------------------------- - // Functions for converting from milliseconds to field values - //------------------------------------------------------------------------- - - /** - * @internal - */ - virtual int32_t handleGetExtendedYear() override; - - /** - * Override Calendar to compute several fields specific to the Persian - * calendar system. These are: - * - *

      • ERA - *
      • YEAR - *
      • MONTH - *
      • DAY_OF_MONTH - *
      • DAY_OF_YEAR - *
      • EXTENDED_YEAR
      - * - * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this - * method is called. The getGregorianXxx() methods return Gregorian - * calendar equivalents for the given Julian day. - * @internal - */ - virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; - - // UObject stuff - public: - /** - * @return The class ID for this object. All objects of a given class have the - * same class ID. Objects of other classes have different class IDs. - * @internal - */ - virtual UClassID getDynamicClassID(void) const override; - - /** - * Return the class ID for this class. This is useful only for comparing to a return - * value from getDynamicClassID(). For example: - * - * Base* polymorphic_pointer = createPolymorphicObject(); - * if (polymorphic_pointer->getDynamicClassID() == - * Derived::getStaticClassID()) ... - * - * @return The class ID for all objects of this class. - * @internal - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * return the calendar type, "persian". - * - * @return calendar type - * @internal - */ - virtual const char * getType() const override; - - private: - PersianCalendar(); // default constructor not implemented - - protected: - - /** - * (Overrides Calendar) Return true if the current date for this Calendar is in - * Daylight Savings Time. Recognizes DST_OFFSET, if it is set. - * - * @param status Fill-in parameter which receives the status of this operation. - * @return True if the current date for this Calendar is in Daylight Savings Time, - * false, otherwise. - * @internal - */ - virtual UBool inDaylightTime(UErrorCode& status) const override; - - /** - * Returns true because the Persian Calendar does have a default century - * @internal - */ - virtual UBool haveDefaultCentury() const override; - - /** - * Returns the date of the start of the default century - * @return start of century - in milliseconds since epoch, 1970 - * @internal - */ - virtual UDate defaultCenturyStart() const override; - - /** - * Returns the year in which the default century begins - * @internal - */ - virtual int32_t defaultCenturyStartYear() const override; -}; - -U_NAMESPACE_END - -#endif -#endif - - - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ****************************************************************************** + * Copyright (C) 2003-2013, International Business Machines Corporation + * and others. All Rights Reserved. + ****************************************************************************** + * + * File PERSNCAL.H + * + * Modification History: + * + * Date Name Description + * 9/23/2003 mehran posted to icu-design + ***************************************************************************** + */ + +#ifndef PERSNCAL_H +#define PERSNCAL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" + +U_NAMESPACE_BEGIN + +/** + * PersianCalendar is a subclass of Calendar + * that implements the Persian calendar. It is used as the official + * calendar in Iran. This calendar is also known as the "Hijri Shamsi" + * calendar, since it starts at the time of Mohammed's emigration (or + * "hijra") to Medinah on Thursday, July 15, 622 AD (Julian) and is a + * solar calendar system (or "shamsi"). + *

      + * The Persian calendar is strictly solar, and thus a Persian year has twelve + * solar months. A Persian year is about 365 days long, except in leap years + * which is 366 days long. + *

      + * The six first months of Persian Calendar are 31 days long. The next five + * months are 30 days long. The last month is 29 days long in normal years, + * and 30 days long in leap years. + * + * @see GregorianCalendar + * + * @author Mehran Mehr + * @internal + */ +class PersianCalendar : public Calendar { + public: + //------------------------------------------------------------------------- + // Constants... + //------------------------------------------------------------------------- + /** + * Constants for the months + * @internal + */ + enum EMonths { + /** + * Constant for Farvardin, the 1st month of the Persian year. + * @internal + */ + FARVARDIN = 0, + + /** + * Constant for Ordibehesht, the 2nd month of the Persian year. + * @internal + */ + ORDIBEHESHT = 1, + + /** + * Constant for Khordad, the 3rd month of the Persian year. + * @internal + */ + KHORDAD = 2, + + /** + * Constant for Tir, the 4th month of the Persian year. + * @internal + */ + TIR = 3, + + /** + * Constant for Mordad, the 5th month of the Persian year. + * @internal + */ + MORDAD = 4, + + /** + * Constant for Shahrivar, the 6th month of the Persian year. + * @internal + */ + SHAHRIVAR = 5, + + /** + * Constant for Mehr, the 7th month of the Persian year. + * @internal + */ + MEHR = 6, + + /** + * Constant for Aban, the 8th month of the Persian year. + * @internal + */ + ABAN = 7, + + /** + * Constant for Azar, the 9th month of the Persian year. + * @internal + */ + AZAR = 8, + + /** + * Constant for Dei, the 10th month of the Persian year. + * @internal + */ + DEI = 9, + + /** + * Constant for Bahman, the 11th month of the Persian year. + * @internal + */ + BAHMAN = 10, + + /** + * Constant for Esfand, the 12th month of the Persian year. + * @internal + */ + ESFAND = 11, + + PERSIAN_MONTH_MAX + }; + + + + //------------------------------------------------------------------------- + // Constructors... + //------------------------------------------------------------------------- + + /** + * Constructs a PersianCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of PersianCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + PersianCalendar(const Locale& aLocale, UErrorCode &success); + + /** + * Copy Constructor + * @internal + */ + PersianCalendar(const PersianCalendar& other); + + /** + * Destructor. + * @internal + */ + virtual ~PersianCalendar(); + + // TODO: copy c'tor, etc + + // clone + virtual PersianCalendar* clone() const override; + + private: + /** + * Determine whether a year is a leap year in the Persian calendar + */ + static UBool isLeapYear(int32_t year); + + /** + * Return the day # on which the given year starts. Days are counted + * from the Hijri epoch, origin 0. + */ + int32_t yearStart(int32_t year); + + /** + * Return the day # on which the given month starts. Days are counted + * from the Hijri epoch, origin 0. + * + * @param year The hijri shamsi year + * @param year The hijri shamsi month, 0-based + */ + int32_t monthStart(int32_t year, int32_t month) const; + + //---------------------------------------------------------------------- + // Calendar framework + //---------------------------------------------------------------------- + protected: + /** + * @internal + */ + virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; + + /** + * Return the length (in days) of the given month. + * + * @param year The hijri shamsi year + * @param year The hijri shamsi month, 0-based + * @internal + */ + virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; + + /** + * Return the number of days in the given Persian year + * @internal + */ + virtual int32_t handleGetYearLength(int32_t extendedYear) const override; + + //------------------------------------------------------------------------- + // Functions for converting from field values to milliseconds.... + //------------------------------------------------------------------------- + + // Return JD of start of given month/year + /** + * @internal + */ + virtual int32_t handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const override; + + //------------------------------------------------------------------------- + // Functions for converting from milliseconds to field values + //------------------------------------------------------------------------- + + /** + * @internal + */ + virtual int32_t handleGetExtendedYear() override; + + /** + * Override Calendar to compute several fields specific to the Persian + * calendar system. These are: + * + *

      • ERA + *
      • YEAR + *
      • MONTH + *
      • DAY_OF_MONTH + *
      • DAY_OF_YEAR + *
      • EXTENDED_YEAR
      + * + * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this + * method is called. The getGregorianXxx() methods return Gregorian + * calendar equivalents for the given Julian day. + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; + + // UObject stuff + public: + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + + /** + * return the calendar type, "persian". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + /** + * @return The related Gregorian year; will be obtained by modifying the value + * obtained by get from UCAL_EXTENDED_YEAR field + * @internal + */ + virtual int32_t getRelatedYear(UErrorCode &status) const override; + + /** + * @param year The related Gregorian year to set; will be modified as necessary then + * set in UCAL_EXTENDED_YEAR field + * @internal + */ + virtual void setRelatedYear(int32_t year) override; + + private: + PersianCalendar(); // default constructor not implemented + + protected: + /** + * Returns true because the Persian Calendar does have a default century + * @internal + */ + virtual UBool haveDefaultCentury() const override; + + /** + * Returns the date of the start of the default century + * @return start of century - in milliseconds since epoch, 1970 + * @internal + */ + virtual UDate defaultCenturyStart() const override; + + /** + * Returns the year in which the default century begins + * @internal + */ + virtual int32_t defaultCenturyStartYear() const override; +}; + +U_NAMESPACE_END + +#endif +#endif + + + diff --git a/deps/icu-small/source/i18n/pluralranges.cpp b/deps/icu-small/source/i18n/pluralranges.cpp index da10e2117d04ad..e0d7b5c3edfa60 100644 --- a/deps/icu-small/source/i18n/pluralranges.cpp +++ b/deps/icu-small/source/i18n/pluralranges.cpp @@ -1,144 +1,144 @@ -// © 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 "unicode/numberrangeformatter.h" -#include "pluralranges.h" -#include "uresimp.h" -#include "charstr.h" -#include "uassert.h" -#include "util.h" -#include "numrange_impl.h" - -U_NAMESPACE_BEGIN - - -namespace { - -class PluralRangesDataSink : public ResourceSink { - public: - PluralRangesDataSink(StandardPluralRanges& output) : fOutput(output) {} - - void put(const char* /*key*/, ResourceValue& value, UBool /*noFallback*/, UErrorCode& status) U_OVERRIDE { - ResourceArray entriesArray = value.getArray(status); - if (U_FAILURE(status)) { return; } - fOutput.setCapacity(entriesArray.getSize(), status); - if (U_FAILURE(status)) { return; } - for (int i = 0; entriesArray.getValue(i, value); i++) { - ResourceArray pluralFormsArray = value.getArray(status); - if (U_FAILURE(status)) { return; } - if (pluralFormsArray.getSize() != 3) { - status = U_RESOURCE_TYPE_MISMATCH; - return; - } - pluralFormsArray.getValue(0, value); - StandardPlural::Form first = StandardPlural::fromString(value.getUnicodeString(status), status); - if (U_FAILURE(status)) { return; } - pluralFormsArray.getValue(1, value); - StandardPlural::Form second = StandardPlural::fromString(value.getUnicodeString(status), status); - if (U_FAILURE(status)) { return; } - pluralFormsArray.getValue(2, value); - StandardPlural::Form result = StandardPlural::fromString(value.getUnicodeString(status), status); - if (U_FAILURE(status)) { return; } - fOutput.addPluralRange(first, second, result); - } - } - - private: - StandardPluralRanges& fOutput; -}; - -void getPluralRangesData(const Locale& locale, StandardPluralRanges& output, UErrorCode& status) { - LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "pluralRanges", &status)); - if (U_FAILURE(status)) { return; } - - CharString dataPath; - dataPath.append("locales/", -1, status); - dataPath.append(locale.getLanguage(), -1, status); - if (U_FAILURE(status)) { return; } - int32_t setLen; - // Not all languages are covered: fail gracefully - UErrorCode internalStatus = U_ZERO_ERROR; - const UChar* set = ures_getStringByKeyWithFallback(rb.getAlias(), dataPath.data(), &setLen, &internalStatus); - if (U_FAILURE(internalStatus)) { return; } - - dataPath.clear(); - dataPath.append("rules/", -1, status); - dataPath.appendInvariantChars(set, setLen, status); - if (U_FAILURE(status)) { return; } - PluralRangesDataSink sink(output); - ures_getAllItemsWithFallback(rb.getAlias(), dataPath.data(), sink, status); -} - -} // namespace - - -StandardPluralRanges -StandardPluralRanges::forLocale(const Locale& locale, UErrorCode& status) { - StandardPluralRanges result; - getPluralRangesData(locale, result, status); - return result; -} - -StandardPluralRanges -StandardPluralRanges::copy(UErrorCode& status) const { - StandardPluralRanges result; - if (fTriplesLen > result.fTriples.getCapacity()) { - if (result.fTriples.resize(fTriplesLen) == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return result; - } - } - uprv_memcpy(result.fTriples.getAlias(), - fTriples.getAlias(), - fTriplesLen * sizeof(fTriples[0])); - result.fTriplesLen = fTriplesLen; - return result; -} - -LocalPointer -StandardPluralRanges::toPointer(UErrorCode& status) && noexcept { - return LocalPointer(new StandardPluralRanges(std::move(*this)), status); -} - -void StandardPluralRanges::addPluralRange( - StandardPlural::Form first, - StandardPlural::Form second, - StandardPlural::Form result) { - U_ASSERT(fTriplesLen < fTriples.getCapacity()); - fTriples[fTriplesLen] = {first, second, result}; - fTriplesLen++; -} - -void StandardPluralRanges::setCapacity(int32_t length, UErrorCode& status) { - if (U_FAILURE(status)) { return; } - if (length > fTriples.getCapacity()) { - if (fTriples.resize(length, 0) == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - } -} - -StandardPlural::Form -StandardPluralRanges::resolve(StandardPlural::Form first, StandardPlural::Form second) const { - for (int32_t i=0; i result.fTriples.getCapacity()) { + if (result.fTriples.resize(fTriplesLen) == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return result; + } + } + uprv_memcpy(result.fTriples.getAlias(), + fTriples.getAlias(), + fTriplesLen * sizeof(fTriples[0])); + result.fTriplesLen = fTriplesLen; + return result; +} + +LocalPointer +StandardPluralRanges::toPointer(UErrorCode& status) && noexcept { + return LocalPointer(new StandardPluralRanges(std::move(*this)), status); +} + +void StandardPluralRanges::addPluralRange( + StandardPlural::Form first, + StandardPlural::Form second, + StandardPlural::Form result) { + U_ASSERT(fTriplesLen < fTriples.getCapacity()); + fTriples[fTriplesLen] = {first, second, result}; + fTriplesLen++; +} + +void StandardPluralRanges::setCapacity(int32_t length, UErrorCode& status) { + if (U_FAILURE(status)) { return; } + if (length > fTriples.getCapacity()) { + if (fTriples.resize(length, 0) == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + } +} + +StandardPlural::Form +StandardPluralRanges::resolve(StandardPlural::Form first, StandardPlural::Form second) const { + for (int32_t i=0; i toPointer(UErrorCode& status) && noexcept; - - /** Select rule based on the first and second forms */ - StandardPlural::Form resolve(StandardPlural::Form first, StandardPlural::Form second) const; - - /** Used for data loading. */ - void addPluralRange( - StandardPlural::Form first, - StandardPlural::Form second, - StandardPlural::Form result); - - /** Used for data loading. */ - void setCapacity(int32_t length, UErrorCode& status); - - private: - struct StandardPluralRangeTriple { - StandardPlural::Form first; - StandardPlural::Form second; - StandardPlural::Form result; - }; - - // TODO: An array is simple here, but it results in linear lookup time. - // Certain locales have 20-30 entries in this list. - // Consider changing to a smarter data structure. - typedef MaybeStackArray PluralRangeTriples; - PluralRangeTriples fTriples; - int32_t fTriplesLen = 0; -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ -#endif //__PLURALRANGES_H__ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#ifndef __PLURALRANGES_H__ +#define __PLURALRANGES_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/uobject.h" +#include "unicode/locid.h" +#include "unicode/plurrule.h" +#include "standardplural.h" +#include "cmemory.h" + +U_NAMESPACE_BEGIN + +// Forward declarations +namespace number { +namespace impl { +class UFormattedNumberRangeData; +} +} + +class StandardPluralRanges : public UMemory { + public: + /** Create a new StandardPluralRanges for the given locale */ + static StandardPluralRanges forLocale(const Locale& locale, UErrorCode& status); + + /** Explicit copy constructor */ + StandardPluralRanges copy(UErrorCode& status) const; + + /** Create an object (called on an rvalue) */ + LocalPointer toPointer(UErrorCode& status) && noexcept; + + /** Select rule based on the first and second forms */ + StandardPlural::Form resolve(StandardPlural::Form first, StandardPlural::Form second) const; + + /** Used for data loading. */ + void addPluralRange( + StandardPlural::Form first, + StandardPlural::Form second, + StandardPlural::Form result); + + /** Used for data loading. */ + void setCapacity(int32_t length, UErrorCode& status); + + private: + struct StandardPluralRangeTriple { + StandardPlural::Form first; + StandardPlural::Form second; + StandardPlural::Form result; + }; + + // TODO: An array is simple here, but it results in linear lookup time. + // Certain locales have 20-30 entries in this list. + // Consider changing to a smarter data structure. + typedef MaybeStackArray PluralRangeTriples; + PluralRangeTriples fTriples; + int32_t fTriplesLen = 0; +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ +#endif //__PLURALRANGES_H__ diff --git a/deps/icu-small/source/i18n/plurfmt.cpp b/deps/icu-small/source/i18n/plurfmt.cpp index 3b8f3a660e575c..410cd698f37ebb 100644 --- a/deps/icu-small/source/i18n/plurfmt.cpp +++ b/deps/icu-small/source/i18n/plurfmt.cpp @@ -1,607 +1,607 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2009-2015, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File PLURFMT.CPP -******************************************************************************* -*/ - -#include "unicode/decimfmt.h" -#include "unicode/messagepattern.h" -#include "unicode/plurfmt.h" -#include "unicode/plurrule.h" -#include "unicode/utypes.h" -#include "cmemory.h" -#include "messageimpl.h" -#include "nfrule.h" -#include "plurrule_impl.h" -#include "uassert.h" -#include "uhash.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" -}; - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat) - -PluralFormat::PluralFormat(UErrorCode& status) - : locale(Locale::getDefault()), - msgPattern(status), - numberFormat(NULL), - offset(0) { - init(NULL, UPLURAL_TYPE_CARDINAL, status); -} - -PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status) - : locale(loc), - msgPattern(status), - numberFormat(NULL), - offset(0) { - init(NULL, UPLURAL_TYPE_CARDINAL, status); -} - -PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status) - : locale(Locale::getDefault()), - msgPattern(status), - numberFormat(NULL), - offset(0) { - init(&rules, UPLURAL_TYPE_COUNT, status); -} - -PluralFormat::PluralFormat(const Locale& loc, - const PluralRules& rules, - UErrorCode& status) - : locale(loc), - msgPattern(status), - numberFormat(NULL), - offset(0) { - init(&rules, UPLURAL_TYPE_COUNT, status); -} - -PluralFormat::PluralFormat(const Locale& loc, - UPluralType type, - UErrorCode& status) - : locale(loc), - msgPattern(status), - numberFormat(NULL), - offset(0) { - init(NULL, type, status); -} - -PluralFormat::PluralFormat(const UnicodeString& pat, - UErrorCode& status) - : locale(Locale::getDefault()), - msgPattern(status), - numberFormat(NULL), - offset(0) { - init(NULL, UPLURAL_TYPE_CARDINAL, status); - applyPattern(pat, status); -} - -PluralFormat::PluralFormat(const Locale& loc, - const UnicodeString& pat, - UErrorCode& status) - : locale(loc), - msgPattern(status), - numberFormat(NULL), - offset(0) { - init(NULL, UPLURAL_TYPE_CARDINAL, status); - applyPattern(pat, status); -} - -PluralFormat::PluralFormat(const PluralRules& rules, - const UnicodeString& pat, - UErrorCode& status) - : locale(Locale::getDefault()), - msgPattern(status), - numberFormat(NULL), - offset(0) { - init(&rules, UPLURAL_TYPE_COUNT, status); - applyPattern(pat, status); -} - -PluralFormat::PluralFormat(const Locale& loc, - const PluralRules& rules, - const UnicodeString& pat, - UErrorCode& status) - : locale(loc), - msgPattern(status), - numberFormat(NULL), - offset(0) { - init(&rules, UPLURAL_TYPE_COUNT, status); - applyPattern(pat, status); -} - -PluralFormat::PluralFormat(const Locale& loc, - UPluralType type, - const UnicodeString& pat, - UErrorCode& status) - : locale(loc), - msgPattern(status), - numberFormat(NULL), - offset(0) { - init(NULL, type, status); - applyPattern(pat, status); -} - -PluralFormat::PluralFormat(const PluralFormat& other) - : Format(other), - locale(other.locale), - msgPattern(other.msgPattern), - numberFormat(NULL), - offset(other.offset) { - copyObjects(other); -} - -void -PluralFormat::copyObjects(const PluralFormat& other) { - UErrorCode status = U_ZERO_ERROR; - if (numberFormat != NULL) { - delete numberFormat; - } - if (pluralRulesWrapper.pluralRules != NULL) { - delete pluralRulesWrapper.pluralRules; - } - - if (other.numberFormat == NULL) { - numberFormat = NumberFormat::createInstance(locale, status); - } else { - numberFormat = other.numberFormat->clone(); - } - if (other.pluralRulesWrapper.pluralRules == NULL) { - pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, status); - } else { - pluralRulesWrapper.pluralRules = other.pluralRulesWrapper.pluralRules->clone(); - } -} - - -PluralFormat::~PluralFormat() { - delete numberFormat; -} - -void -PluralFormat::init(const PluralRules* rules, UPluralType type, UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - - if (rules==NULL) { - pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, type, status); - } else { - pluralRulesWrapper.pluralRules = rules->clone(); - if (pluralRulesWrapper.pluralRules == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - - numberFormat= NumberFormat::createInstance(locale, status); -} - -void -PluralFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) { - msgPattern.parsePluralStyle(newPattern, NULL, status); - if (U_FAILURE(status)) { - msgPattern.clear(); - offset = 0; - return; - } - offset = msgPattern.getPluralOffset(0); -} - -UnicodeString& -PluralFormat::format(const Formattable& obj, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode& status) const -{ - if (U_FAILURE(status)) return appendTo; - - if (obj.isNumeric()) { - return format(obj, obj.getDouble(), appendTo, pos, status); - } else { - status = U_ILLEGAL_ARGUMENT_ERROR; - return appendTo; - } -} - -UnicodeString -PluralFormat::format(int32_t number, UErrorCode& status) const { - FieldPosition fpos(FieldPosition::DONT_CARE); - UnicodeString result; - return format(Formattable(number), number, result, fpos, status); -} - -UnicodeString -PluralFormat::format(double number, UErrorCode& status) const { - FieldPosition fpos(FieldPosition::DONT_CARE); - UnicodeString result; - return format(Formattable(number), number, result, fpos, status); -} - - -UnicodeString& -PluralFormat::format(int32_t number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode& status) const { - return format(Formattable(number), (double)number, appendTo, pos, status); -} - -UnicodeString& -PluralFormat::format(double number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode& status) const { - return format(Formattable(number), (double)number, appendTo, pos, status); -} - -UnicodeString& -PluralFormat::format(const Formattable& numberObject, double number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return appendTo; - } - 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; - // 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) { - // could be BigDecimal etc. - numberObject.populateDecimalQuantity(data.quantity, status); - } else { - data.quantity.setToDouble(numberMinusOffset); - } - UnicodeString numberString; - auto *decFmt = dynamic_cast(numberFormat); - if(decFmt != nullptr) { - const number::LocalizedNumberFormatter* lnf = decFmt->toNumberFormatter(status); - if (U_FAILURE(status)) { - return appendTo; - } - lnf->formatImpl(&data, status); // mutates &data - if (U_FAILURE(status)) { - return appendTo; - } - numberString = data.getStringRef().toUnicodeString(); - } else { - if (offset == 0) { - numberFormat->format(numberObject, numberString, status); - } else { - numberFormat->format(numberMinusOffset, numberString, 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. - const UnicodeString& pattern = msgPattern.getPatternString(); - int32_t prevIndex = msgPattern.getPart(partIndex).getLimit(); - for (;;) { - const MessagePattern::Part& part = msgPattern.getPart(++partIndex); - const UMessagePatternPartType type = part.getType(); - int32_t index = part.getIndex(); - if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { - return appendTo.append(pattern, prevIndex, index - prevIndex); - } else if ((type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) || - (type == UMSGPAT_PART_TYPE_SKIP_SYNTAX && MessageImpl::jdkAposMode(msgPattern))) { - appendTo.append(pattern, prevIndex, index - prevIndex); - if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) { - appendTo.append(numberString); - } - prevIndex = part.getLimit(); - } else if (type == UMSGPAT_PART_TYPE_ARG_START) { - appendTo.append(pattern, prevIndex, index - prevIndex); - prevIndex = index; - partIndex = msgPattern.getLimitPartIndex(partIndex); - index = msgPattern.getPart(partIndex).getLimit(); - MessageImpl::appendReducedApostrophes(pattern, prevIndex, index, appendTo); - prevIndex = index; - } - } -} - -UnicodeString& -PluralFormat::toPattern(UnicodeString& appendTo) { - if (0 == msgPattern.countParts()) { - appendTo.setToBogus(); - } else { - appendTo.append(msgPattern.getPatternString()); - } - return appendTo; -} - -void -PluralFormat::setLocale(const Locale& loc, UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - locale = loc; - msgPattern.clear(); - delete numberFormat; - offset = 0; - numberFormat = NULL; - pluralRulesWrapper.reset(); - init(NULL, UPLURAL_TYPE_CARDINAL, status); -} - -void -PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - NumberFormat* nf = format->clone(); - if (nf != NULL) { - delete numberFormat; - numberFormat = nf; - } else { - status = U_MEMORY_ALLOCATION_ERROR; - } -} - -PluralFormat* -PluralFormat::clone() const -{ - return new PluralFormat(*this); -} - - -PluralFormat& -PluralFormat::operator=(const PluralFormat& other) { - if (this != &other) { - locale = other.locale; - msgPattern = other.msgPattern; - offset = other.offset; - copyObjects(other); - } - - return *this; -} - -bool -PluralFormat::operator==(const Format& other) const { - if (this == &other) { - return true; - } - if (!Format::operator==(other)) { - return false; - } - const PluralFormat& o = (const PluralFormat&)other; - return - locale == o.locale && - msgPattern == o.msgPattern && // implies same offset - (numberFormat == NULL) == (o.numberFormat == NULL) && - (numberFormat == NULL || *numberFormat == *o.numberFormat) && - (pluralRulesWrapper.pluralRules == NULL) == (o.pluralRulesWrapper.pluralRules == NULL) && - (pluralRulesWrapper.pluralRules == NULL || - *pluralRulesWrapper.pluralRules == *o.pluralRulesWrapper.pluralRules); -} - -bool -PluralFormat::operator!=(const Format& other) const { - return !operator==(other); -} - -void -PluralFormat::parseObject(const UnicodeString& /*source*/, - Formattable& /*result*/, - ParsePosition& pos) const -{ - // Parsing not supported. - pos.setErrorIndex(pos.getIndex()); -} - -int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t partIndex, - const PluralSelector& selector, void *context, - double number, UErrorCode& ec) { - if (U_FAILURE(ec)) { - return 0; - } - int32_t count=pattern.countParts(); - double offset; - const MessagePattern::Part* part=&pattern.getPart(partIndex); - if (MessagePattern::Part::hasNumericValue(part->getType())) { - offset=pattern.getNumericValue(*part); - ++partIndex; - } else { - offset=0; - } - // The keyword is empty until we need to match against a non-explicit, not-"other" value. - // Then we get the keyword from the selector. - // (In other words, we never call the selector if we match against an explicit value, - // or if the only non-explicit keyword is "other".) - UnicodeString keyword; - UnicodeString other(false, OTHER_STRING, 5); - // When we find a match, we set msgStart>0 and also set this boolean to true - // to avoid matching the keyword again (duplicates are allowed) - // while we continue to look for an explicit-value match. - UBool haveKeywordMatch=false; - // msgStart is 0 until we find any appropriate sub-message. - // We remember the first "other" sub-message if we have not seen any - // appropriate sub-message before. - // We remember the first matching-keyword sub-message if we have not seen - // one of those before. - // (The parser allows [does not check for] duplicate keywords. - // We just have to make sure to take the first one.) - // We avoid matching the keyword twice by also setting haveKeywordMatch=true - // at the first keyword match. - // We keep going until we find an explicit-value match or reach the end of the plural style. - int32_t msgStart=0; - // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples - // until ARG_LIMIT or end of plural-only pattern. - do { - part=&pattern.getPart(partIndex++); - const UMessagePatternPartType type = part->getType(); - if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) { - break; - } - U_ASSERT (type==UMSGPAT_PART_TYPE_ARG_SELECTOR); - // part is an ARG_SELECTOR followed by an optional explicit value, and then a message - if(MessagePattern::Part::hasNumericValue(pattern.getPartType(partIndex))) { - // explicit value like "=2" - part=&pattern.getPart(partIndex++); - if(number==pattern.getNumericValue(*part)) { - // matches explicit value - return partIndex; - } - } else if(!haveKeywordMatch) { - // plural keyword like "few" or "other" - // Compare "other" first and call the selector if this is not "other". - if(pattern.partSubstringMatches(*part, other)) { - if(msgStart==0) { - msgStart=partIndex; - if(0 == keyword.compare(other)) { - // This is the first "other" sub-message, - // and the selected keyword is also "other". - // Do not match "other" again. - haveKeywordMatch=true; - } - } - } else { - if(keyword.isEmpty()) { - keyword=selector.select(context, number-offset, ec); - if(msgStart!=0 && (0 == keyword.compare(other))) { - // We have already seen an "other" sub-message. - // Do not match "other" again. - haveKeywordMatch=true; - // Skip keyword matching but do getLimitPartIndex(). - } - } - if(!haveKeywordMatch && pattern.partSubstringMatches(*part, keyword)) { - // keyword matches - msgStart=partIndex; - // Do not match this keyword again. - haveKeywordMatch=true; - } - } - } - partIndex=pattern.getLimitPartIndex(partIndex); - } while(++partIndexgetType() != UMSGPAT_PART_TYPE_ARG_SELECTOR) { - // Bad format - continue; - } - - const MessagePattern::Part* partStart = &msgPattern.getPart(partIndex++); - if (partStart->getType() != UMSGPAT_PART_TYPE_MSG_START) { - // Bad format - continue; - } - - const MessagePattern::Part* partLimit = &msgPattern.getPart(partIndex++); - if (partLimit->getType() != UMSGPAT_PART_TYPE_MSG_LIMIT) { - // Bad format - continue; - } - - UnicodeString currArg = pattern.tempSubString(partStart->getLimit(), partLimit->getIndex() - partStart->getLimit()); - if (rbnfLenientScanner != NULL) { - // Check if non-lenient rule finds the text before call lenient parsing - int32_t tempIndex = source.indexOf(currArg, startingAt); - if (tempIndex >= 0) { - currMatchIndex = tempIndex; - } else { - // If lenient parsing is turned ON, we've got some time consuming parsing ahead of us. - int32_t length = -1; - currMatchIndex = rbnfLenientScanner->findTextLenient(source, currArg, startingAt, &length); - } - } - else { - currMatchIndex = source.indexOf(currArg, startingAt); - } - if (currMatchIndex >= 0 && currMatchIndex >= matchedIndex && currArg.length() > matchedWord.length()) { - matchedIndex = currMatchIndex; - matchedWord = currArg; - keyword = pattern.tempSubString(partStart->getLimit(), partLimit->getIndex() - partStart->getLimit()); - } - } - if (matchedIndex >= 0) { - pos.setBeginIndex(matchedIndex); - pos.setEndIndex(matchedIndex + matchedWord.length()); - result.setString(keyword); - return; - } - - // Not found! - pos.setBeginIndex(-1); - pos.setEndIndex(-1); -} - -PluralFormat::PluralSelector::~PluralSelector() {} - -PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() { - delete pluralRules; -} - -UnicodeString PluralFormat::PluralSelectorAdapter::select(void *context, double number, - UErrorCode& /*ec*/) const { - (void)number; // unused except in the assertion - IFixedDecimal *dec=static_cast(context); - return pluralRules->select(*dec); -} - -void PluralFormat::PluralSelectorAdapter::reset() { - delete pluralRules; - pluralRules = NULL; -} - - -U_NAMESPACE_END - - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2009-2015, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File PLURFMT.CPP +******************************************************************************* +*/ + +#include "unicode/decimfmt.h" +#include "unicode/messagepattern.h" +#include "unicode/plurfmt.h" +#include "unicode/plurrule.h" +#include "unicode/utypes.h" +#include "cmemory.h" +#include "messageimpl.h" +#include "nfrule.h" +#include "plurrule_impl.h" +#include "uassert.h" +#include "uhash.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 char16_t OTHER_STRING[] = { + 0x6F, 0x74, 0x68, 0x65, 0x72, 0 // "other" +}; + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat) + +PluralFormat::PluralFormat(UErrorCode& status) + : locale(Locale::getDefault()), + msgPattern(status), + numberFormat(nullptr), + offset(0) { + init(nullptr, UPLURAL_TYPE_CARDINAL, status); +} + +PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status) + : locale(loc), + msgPattern(status), + numberFormat(nullptr), + offset(0) { + init(nullptr, UPLURAL_TYPE_CARDINAL, status); +} + +PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status) + : locale(Locale::getDefault()), + msgPattern(status), + numberFormat(nullptr), + offset(0) { + init(&rules, UPLURAL_TYPE_COUNT, status); +} + +PluralFormat::PluralFormat(const Locale& loc, + const PluralRules& rules, + UErrorCode& status) + : locale(loc), + msgPattern(status), + numberFormat(nullptr), + offset(0) { + init(&rules, UPLURAL_TYPE_COUNT, status); +} + +PluralFormat::PluralFormat(const Locale& loc, + UPluralType type, + UErrorCode& status) + : locale(loc), + msgPattern(status), + numberFormat(nullptr), + offset(0) { + init(nullptr, type, status); +} + +PluralFormat::PluralFormat(const UnicodeString& pat, + UErrorCode& status) + : locale(Locale::getDefault()), + msgPattern(status), + numberFormat(nullptr), + offset(0) { + init(nullptr, UPLURAL_TYPE_CARDINAL, status); + applyPattern(pat, status); +} + +PluralFormat::PluralFormat(const Locale& loc, + const UnicodeString& pat, + UErrorCode& status) + : locale(loc), + msgPattern(status), + numberFormat(nullptr), + offset(0) { + init(nullptr, UPLURAL_TYPE_CARDINAL, status); + applyPattern(pat, status); +} + +PluralFormat::PluralFormat(const PluralRules& rules, + const UnicodeString& pat, + UErrorCode& status) + : locale(Locale::getDefault()), + msgPattern(status), + numberFormat(nullptr), + offset(0) { + init(&rules, UPLURAL_TYPE_COUNT, status); + applyPattern(pat, status); +} + +PluralFormat::PluralFormat(const Locale& loc, + const PluralRules& rules, + const UnicodeString& pat, + UErrorCode& status) + : locale(loc), + msgPattern(status), + numberFormat(nullptr), + offset(0) { + init(&rules, UPLURAL_TYPE_COUNT, status); + applyPattern(pat, status); +} + +PluralFormat::PluralFormat(const Locale& loc, + UPluralType type, + const UnicodeString& pat, + UErrorCode& status) + : locale(loc), + msgPattern(status), + numberFormat(nullptr), + offset(0) { + init(nullptr, type, status); + applyPattern(pat, status); +} + +PluralFormat::PluralFormat(const PluralFormat& other) + : Format(other), + locale(other.locale), + msgPattern(other.msgPattern), + numberFormat(nullptr), + offset(other.offset) { + copyObjects(other); +} + +void +PluralFormat::copyObjects(const PluralFormat& other) { + UErrorCode status = U_ZERO_ERROR; + if (numberFormat != nullptr) { + delete numberFormat; + } + if (pluralRulesWrapper.pluralRules != nullptr) { + delete pluralRulesWrapper.pluralRules; + } + + if (other.numberFormat == nullptr) { + numberFormat = NumberFormat::createInstance(locale, status); + } else { + numberFormat = other.numberFormat->clone(); + } + if (other.pluralRulesWrapper.pluralRules == nullptr) { + pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, status); + } else { + pluralRulesWrapper.pluralRules = other.pluralRulesWrapper.pluralRules->clone(); + } +} + + +PluralFormat::~PluralFormat() { + delete numberFormat; +} + +void +PluralFormat::init(const PluralRules* rules, UPluralType type, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + + if (rules==nullptr) { + pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, type, status); + } else { + pluralRulesWrapper.pluralRules = rules->clone(); + if (pluralRulesWrapper.pluralRules == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + + numberFormat= NumberFormat::createInstance(locale, status); +} + +void +PluralFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) { + msgPattern.parsePluralStyle(newPattern, nullptr, status); + if (U_FAILURE(status)) { + msgPattern.clear(); + offset = 0; + return; + } + offset = msgPattern.getPluralOffset(0); +} + +UnicodeString& +PluralFormat::format(const Formattable& obj, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& status) const +{ + if (U_FAILURE(status)) return appendTo; + + if (obj.isNumeric()) { + return format(obj, obj.getDouble(), appendTo, pos, status); + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + return appendTo; + } +} + +UnicodeString +PluralFormat::format(int32_t number, UErrorCode& status) const { + FieldPosition fpos(FieldPosition::DONT_CARE); + UnicodeString result; + return format(Formattable(number), number, result, fpos, status); +} + +UnicodeString +PluralFormat::format(double number, UErrorCode& status) const { + FieldPosition fpos(FieldPosition::DONT_CARE); + UnicodeString result; + return format(Formattable(number), number, result, fpos, status); +} + + +UnicodeString& +PluralFormat::format(int32_t number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& status) const { + return format(Formattable(number), (double)number, appendTo, pos, status); +} + +UnicodeString& +PluralFormat::format(double number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& status) const { + return format(Formattable(number), (double)number, appendTo, pos, status); +} + +UnicodeString& +PluralFormat::format(const Formattable& numberObject, double number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return appendTo; + } + 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; + // 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) { + // could be BigDecimal etc. + numberObject.populateDecimalQuantity(data.quantity, status); + } else { + data.quantity.setToDouble(numberMinusOffset); + } + UnicodeString numberString; + auto *decFmt = dynamic_cast(numberFormat); + if(decFmt != nullptr) { + const number::LocalizedNumberFormatter* lnf = decFmt->toNumberFormatter(status); + if (U_FAILURE(status)) { + return appendTo; + } + lnf->formatImpl(&data, status); // mutates &data + if (U_FAILURE(status)) { + return appendTo; + } + numberString = data.getStringRef().toUnicodeString(); + } else { + if (offset == 0) { + numberFormat->format(numberObject, numberString, status); + } else { + numberFormat->format(numberMinusOffset, numberString, 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. + const UnicodeString& pattern = msgPattern.getPatternString(); + int32_t prevIndex = msgPattern.getPart(partIndex).getLimit(); + for (;;) { + const MessagePattern::Part& part = msgPattern.getPart(++partIndex); + const UMessagePatternPartType type = part.getType(); + int32_t index = part.getIndex(); + if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { + return appendTo.append(pattern, prevIndex, index - prevIndex); + } else if ((type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) || + (type == UMSGPAT_PART_TYPE_SKIP_SYNTAX && MessageImpl::jdkAposMode(msgPattern))) { + appendTo.append(pattern, prevIndex, index - prevIndex); + if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) { + appendTo.append(numberString); + } + prevIndex = part.getLimit(); + } else if (type == UMSGPAT_PART_TYPE_ARG_START) { + appendTo.append(pattern, prevIndex, index - prevIndex); + prevIndex = index; + partIndex = msgPattern.getLimitPartIndex(partIndex); + index = msgPattern.getPart(partIndex).getLimit(); + MessageImpl::appendReducedApostrophes(pattern, prevIndex, index, appendTo); + prevIndex = index; + } + } +} + +UnicodeString& +PluralFormat::toPattern(UnicodeString& appendTo) { + if (0 == msgPattern.countParts()) { + appendTo.setToBogus(); + } else { + appendTo.append(msgPattern.getPatternString()); + } + return appendTo; +} + +void +PluralFormat::setLocale(const Locale& loc, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + locale = loc; + msgPattern.clear(); + delete numberFormat; + offset = 0; + numberFormat = nullptr; + pluralRulesWrapper.reset(); + init(nullptr, UPLURAL_TYPE_CARDINAL, status); +} + +void +PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + NumberFormat* nf = format->clone(); + if (nf != nullptr) { + delete numberFormat; + numberFormat = nf; + } else { + status = U_MEMORY_ALLOCATION_ERROR; + } +} + +PluralFormat* +PluralFormat::clone() const +{ + return new PluralFormat(*this); +} + + +PluralFormat& +PluralFormat::operator=(const PluralFormat& other) { + if (this != &other) { + locale = other.locale; + msgPattern = other.msgPattern; + offset = other.offset; + copyObjects(other); + } + + return *this; +} + +bool +PluralFormat::operator==(const Format& other) const { + if (this == &other) { + return true; + } + if (!Format::operator==(other)) { + return false; + } + const PluralFormat& o = (const PluralFormat&)other; + return + locale == o.locale && + msgPattern == o.msgPattern && // implies same offset + (numberFormat == nullptr) == (o.numberFormat == nullptr) && + (numberFormat == nullptr || *numberFormat == *o.numberFormat) && + (pluralRulesWrapper.pluralRules == nullptr) == (o.pluralRulesWrapper.pluralRules == nullptr) && + (pluralRulesWrapper.pluralRules == nullptr || + *pluralRulesWrapper.pluralRules == *o.pluralRulesWrapper.pluralRules); +} + +bool +PluralFormat::operator!=(const Format& other) const { + return !operator==(other); +} + +void +PluralFormat::parseObject(const UnicodeString& /*source*/, + Formattable& /*result*/, + ParsePosition& pos) const +{ + // Parsing not supported. + pos.setErrorIndex(pos.getIndex()); +} + +int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t partIndex, + const PluralSelector& selector, void *context, + double number, UErrorCode& ec) { + if (U_FAILURE(ec)) { + return 0; + } + int32_t count=pattern.countParts(); + double offset; + const MessagePattern::Part* part=&pattern.getPart(partIndex); + if (MessagePattern::Part::hasNumericValue(part->getType())) { + offset=pattern.getNumericValue(*part); + ++partIndex; + } else { + offset=0; + } + // The keyword is empty until we need to match against a non-explicit, not-"other" value. + // Then we get the keyword from the selector. + // (In other words, we never call the selector if we match against an explicit value, + // or if the only non-explicit keyword is "other".) + UnicodeString keyword; + UnicodeString other(false, OTHER_STRING, 5); + // When we find a match, we set msgStart>0 and also set this boolean to true + // to avoid matching the keyword again (duplicates are allowed) + // while we continue to look for an explicit-value match. + UBool haveKeywordMatch=false; + // msgStart is 0 until we find any appropriate sub-message. + // We remember the first "other" sub-message if we have not seen any + // appropriate sub-message before. + // We remember the first matching-keyword sub-message if we have not seen + // one of those before. + // (The parser allows [does not check for] duplicate keywords. + // We just have to make sure to take the first one.) + // We avoid matching the keyword twice by also setting haveKeywordMatch=true + // at the first keyword match. + // We keep going until we find an explicit-value match or reach the end of the plural style. + int32_t msgStart=0; + // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples + // until ARG_LIMIT or end of plural-only pattern. + do { + part=&pattern.getPart(partIndex++); + const UMessagePatternPartType type = part->getType(); + if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) { + break; + } + U_ASSERT (type==UMSGPAT_PART_TYPE_ARG_SELECTOR); + // part is an ARG_SELECTOR followed by an optional explicit value, and then a message + if(MessagePattern::Part::hasNumericValue(pattern.getPartType(partIndex))) { + // explicit value like "=2" + part=&pattern.getPart(partIndex++); + if(number==pattern.getNumericValue(*part)) { + // matches explicit value + return partIndex; + } + } else if(!haveKeywordMatch) { + // plural keyword like "few" or "other" + // Compare "other" first and call the selector if this is not "other". + if(pattern.partSubstringMatches(*part, other)) { + if(msgStart==0) { + msgStart=partIndex; + if(0 == keyword.compare(other)) { + // This is the first "other" sub-message, + // and the selected keyword is also "other". + // Do not match "other" again. + haveKeywordMatch=true; + } + } + } else { + if(keyword.isEmpty()) { + keyword=selector.select(context, number-offset, ec); + if(msgStart!=0 && (0 == keyword.compare(other))) { + // We have already seen an "other" sub-message. + // Do not match "other" again. + haveKeywordMatch=true; + // Skip keyword matching but do getLimitPartIndex(). + } + } + if(!haveKeywordMatch && pattern.partSubstringMatches(*part, keyword)) { + // keyword matches + msgStart=partIndex; + // Do not match this keyword again. + haveKeywordMatch=true; + } + } + } + partIndex=pattern.getLimitPartIndex(partIndex); + } while(++partIndexgetType() != UMSGPAT_PART_TYPE_ARG_SELECTOR) { + // Bad format + continue; + } + + const MessagePattern::Part* partStart = &msgPattern.getPart(partIndex++); + if (partStart->getType() != UMSGPAT_PART_TYPE_MSG_START) { + // Bad format + continue; + } + + const MessagePattern::Part* partLimit = &msgPattern.getPart(partIndex++); + if (partLimit->getType() != UMSGPAT_PART_TYPE_MSG_LIMIT) { + // Bad format + continue; + } + + UnicodeString currArg = pattern.tempSubString(partStart->getLimit(), partLimit->getIndex() - partStart->getLimit()); + if (rbnfLenientScanner != nullptr) { + // Check if non-lenient rule finds the text before call lenient parsing + int32_t tempIndex = source.indexOf(currArg, startingAt); + if (tempIndex >= 0) { + currMatchIndex = tempIndex; + } else { + // If lenient parsing is turned ON, we've got some time consuming parsing ahead of us. + int32_t length = -1; + currMatchIndex = rbnfLenientScanner->findTextLenient(source, currArg, startingAt, &length); + } + } + else { + currMatchIndex = source.indexOf(currArg, startingAt); + } + if (currMatchIndex >= 0 && currMatchIndex >= matchedIndex && currArg.length() > matchedWord.length()) { + matchedIndex = currMatchIndex; + matchedWord = currArg; + keyword = pattern.tempSubString(partStart->getLimit(), partLimit->getIndex() - partStart->getLimit()); + } + } + if (matchedIndex >= 0) { + pos.setBeginIndex(matchedIndex); + pos.setEndIndex(matchedIndex + matchedWord.length()); + result.setString(keyword); + return; + } + + // Not found! + pos.setBeginIndex(-1); + pos.setEndIndex(-1); +} + +PluralFormat::PluralSelector::~PluralSelector() {} + +PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() { + delete pluralRules; +} + +UnicodeString PluralFormat::PluralSelectorAdapter::select(void *context, double number, + UErrorCode& /*ec*/) const { + (void)number; // unused except in the assertion + IFixedDecimal *dec=static_cast(context); + return pluralRules->select(*dec); +} + +void PluralFormat::PluralSelectorAdapter::reset() { + delete pluralRules; + pluralRules = nullptr; +} + + +U_NAMESPACE_END + + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/plurrule.cpp b/deps/icu-small/source/i18n/plurrule.cpp index 431a3ce04bac6c..c5daadeb9c67de 100644 --- a/deps/icu-small/source/i18n/plurrule.cpp +++ b/deps/icu-small/source/i18n/plurrule.cpp @@ -1,2006 +1,2006 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2007-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File plurrule.cpp -*/ - -#include -#include - -#include "unicode/utypes.h" -#include "unicode/localpointer.h" -#include "unicode/plurrule.h" -#include "unicode/upluralrules.h" -#include "unicode/ures.h" -#include "unicode/numfmt.h" -#include "unicode/decimfmt.h" -#include "unicode/numberrangeformatter.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "hash.h" -#include "locutil.h" -#include "mutex.h" -#include "number_decnum.h" -#include "patternprops.h" -#include "plurrule_impl.h" -#include "putilimp.h" -#include "ucln_in.h" -#include "ustrfmt.h" -#include "uassert.h" -#include "uvectr32.h" -#include "sharedpluralrules.h" -#include "unifiedcache.h" -#include "number_decimalquantity.h" -#include "util.h" -#include "pluralranges.h" -#include "numrange_impl.h" - -#if !UCONFIG_NO_FORMATTING - -U_NAMESPACE_BEGIN - -using namespace icu::pluralimpl; -using icu::number::impl::DecNum; -using icu::number::impl::DecimalQuantity; -using icu::number::impl::RoundingMode; - -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}; -static const UChar PK_NOT[]={LOW_N,LOW_O,LOW_T,0}; -static const UChar PK_IS[]={LOW_I,LOW_S,0}; -static const UChar PK_MOD[]={LOW_M,LOW_O,LOW_D,0}; -static const UChar PK_AND[]={LOW_A,LOW_N,LOW_D,0}; -static const UChar PK_OR[]={LOW_O,LOW_R,0}; -static const UChar PK_VAR_N[]={LOW_N,0}; -static const UChar PK_VAR_I[]={LOW_I,0}; -static const UChar PK_VAR_F[]={LOW_F,0}; -static const UChar PK_VAR_T[]={LOW_T,0}; -static const UChar PK_VAR_E[]={LOW_E,0}; -static const UChar PK_VAR_C[]={LOW_C,0}; -static const UChar PK_VAR_V[]={LOW_V,0}; -static const UChar PK_WITHIN[]={LOW_W,LOW_I,LOW_T,LOW_H,LOW_I,LOW_N,0}; -static const UChar PK_DECIMAL[]={LOW_D,LOW_E,LOW_C,LOW_I,LOW_M,LOW_A,LOW_L,0}; -static const UChar PK_INTEGER[]={LOW_I,LOW_N,LOW_T,LOW_E,LOW_G,LOW_E,LOW_R,0}; - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralRules) -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration) - -PluralRules::PluralRules(UErrorCode& /*status*/) -: UObject(), - mRules(nullptr), - mStandardPluralRanges(nullptr), - mInternalStatus(U_ZERO_ERROR) -{ -} - -PluralRules::PluralRules(const PluralRules& other) -: UObject(other), - mRules(nullptr), - mStandardPluralRanges(nullptr), - mInternalStatus(U_ZERO_ERROR) -{ - *this=other; -} - -PluralRules::~PluralRules() { - delete mRules; - delete mStandardPluralRanges; -} - -SharedPluralRules::~SharedPluralRules() { - delete ptr; -} - -PluralRules* -PluralRules::clone() const { - // Since clone doesn't have a 'status' parameter, the best we can do is return nullptr if - // the newly created object was not fully constructed properly (an error occurred). - UErrorCode localStatus = U_ZERO_ERROR; - return clone(localStatus); -} - -PluralRules* -PluralRules::clone(UErrorCode& status) const { - LocalPointer newObj(new PluralRules(*this), status); - if (U_SUCCESS(status) && U_FAILURE(newObj->mInternalStatus)) { - status = newObj->mInternalStatus; - newObj.adoptInstead(nullptr); - } - return newObj.orphan(); -} - -PluralRules& -PluralRules::operator=(const PluralRules& other) { - if (this != &other) { - delete mRules; - mRules = nullptr; - delete mStandardPluralRanges; - mStandardPluralRanges = nullptr; - mInternalStatus = other.mInternalStatus; - if (U_FAILURE(mInternalStatus)) { - // bail out early if the object we were copying from was already 'invalid'. - return *this; - } - if (other.mRules != nullptr) { - mRules = new RuleChain(*other.mRules); - if (mRules == nullptr) { - mInternalStatus = U_MEMORY_ALLOCATION_ERROR; - } - else if (U_FAILURE(mRules->fInternalStatus)) { - // If the RuleChain wasn't fully copied, then set our status to failure as well. - mInternalStatus = mRules->fInternalStatus; - } - } - if (other.mStandardPluralRanges != nullptr) { - mStandardPluralRanges = other.mStandardPluralRanges->copy(mInternalStatus) - .toPointer(mInternalStatus) - .orphan(); - } - } - return *this; -} - -StringEnumeration* PluralRules::getAvailableLocales(UErrorCode &status) { - if (U_FAILURE(status)) { - return nullptr; - } - LocalPointer result(new PluralAvailableLocalesEnumeration(status), status); - if (U_FAILURE(status)) { - return nullptr; - } - return result.orphan(); -} - - -PluralRules* U_EXPORT2 -PluralRules::createRules(const UnicodeString& description, UErrorCode& status) { - if (U_FAILURE(status)) { - return nullptr; - } - PluralRuleParser parser; - LocalPointer newRules(new PluralRules(status), status); - if (U_FAILURE(status)) { - return nullptr; - } - parser.parse(description, newRules.getAlias(), status); - if (U_FAILURE(status)) { - newRules.adoptInstead(nullptr); - } - return newRules.orphan(); -} - - -PluralRules* U_EXPORT2 -PluralRules::createDefaultRules(UErrorCode& status) { - return createRules(UnicodeString(true, PLURAL_DEFAULT_RULE, -1), status); -} - -/******************************************************************************/ -/* Create PluralRules cache */ - -template<> U_I18N_API -const SharedPluralRules *LocaleCacheKey::createObject( - const void * /*unused*/, UErrorCode &status) const { - const char *localeId = fLoc.getName(); - LocalPointer pr(PluralRules::internalForLocale(localeId, UPLURAL_TYPE_CARDINAL, status), status); - if (U_FAILURE(status)) { - return nullptr; - } - LocalPointer result(new SharedPluralRules(pr.getAlias()), status); - if (U_FAILURE(status)) { - return nullptr; - } - pr.orphan(); // result was successfully created so it nows pr. - result->addRef(); - return result.orphan(); -} - -/* end plural rules cache */ -/******************************************************************************/ - -const SharedPluralRules* U_EXPORT2 -PluralRules::createSharedInstance( - const Locale& locale, UPluralType type, UErrorCode& status) { - if (U_FAILURE(status)) { - return nullptr; - } - if (type != UPLURAL_TYPE_CARDINAL) { - status = U_UNSUPPORTED_ERROR; - return nullptr; - } - const SharedPluralRules *result = nullptr; - UnifiedCache::getByLocale(locale, result, status); - return result; -} - -PluralRules* U_EXPORT2 -PluralRules::forLocale(const Locale& locale, UErrorCode& status) { - return forLocale(locale, UPLURAL_TYPE_CARDINAL, status); -} - -PluralRules* U_EXPORT2 -PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& status) { - if (type != UPLURAL_TYPE_CARDINAL) { - return internalForLocale(locale, type, status); - } - const SharedPluralRules *shared = createSharedInstance( - locale, type, status); - if (U_FAILURE(status)) { - return nullptr; - } - PluralRules *result = (*shared)->clone(status); - shared->removeRef(); - return result; -} - -PluralRules* U_EXPORT2 -PluralRules::internalForLocale(const Locale& locale, UPluralType type, UErrorCode& status) { - if (U_FAILURE(status)) { - return nullptr; - } - if (type >= UPLURAL_TYPE_COUNT) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - LocalPointer newObj(new PluralRules(status), status); - if (U_FAILURE(status)) { - return nullptr; - } - UnicodeString locRule = newObj->getRuleFromResource(locale, type, status); - // TODO: which other errors, if any, should be returned? - if (locRule.length() == 0) { - // If an out-of-memory error occurred, then stop and report the failure. - if (status == U_MEMORY_ALLOCATION_ERROR) { - return nullptr; - } - // Locales with no specific rules (all numbers have the "other" category - // will return a U_MISSING_RESOURCE_ERROR at this point. This is not - // an error. - locRule = UnicodeString(PLURAL_DEFAULT_RULE); - status = U_ZERO_ERROR; - } - PluralRuleParser parser; - parser.parse(locRule, newObj.getAlias(), status); - // TODO: should rule parse errors be returned, or - // should we silently use default rules? - // Original impl used default rules. - // Ask the question to ICU Core. - - newObj->mStandardPluralRanges = StandardPluralRanges::forLocale(locale, status) - .toPointer(status) - .orphan(); - - return newObj.orphan(); -} - -UnicodeString -PluralRules::select(int32_t number) const { - return select(FixedDecimal(number)); -} - -UnicodeString -PluralRules::select(double number) const { - return select(FixedDecimal(number)); -} - -UnicodeString -PluralRules::select(const number::FormattedNumber& number, UErrorCode& status) const { - DecimalQuantity dq; - number.getDecimalQuantity(dq, status); - if (U_FAILURE(status)) { - return ICU_Utility::makeBogusString(); - } - if (U_FAILURE(mInternalStatus)) { - status = mInternalStatus; - return ICU_Utility::makeBogusString(); - } - return select(dq); -} - -UnicodeString -PluralRules::select(const IFixedDecimal &number) const { - if (mRules == nullptr) { - return UnicodeString(true, PLURAL_DEFAULT_RULE, -1); - } - else { - return mRules->select(number); - } -} - -UnicodeString -PluralRules::select(const number::FormattedNumberRange& range, UErrorCode& status) const { - return select(range.getData(status), status); -} - -UnicodeString -PluralRules::select(const number::impl::UFormattedNumberRangeData* impl, UErrorCode& status) const { - if (U_FAILURE(status)) { - return ICU_Utility::makeBogusString(); - } - if (U_FAILURE(mInternalStatus)) { - status = mInternalStatus; - return ICU_Utility::makeBogusString(); - } - if (mStandardPluralRanges == nullptr) { - // Happens if PluralRules was constructed via createRules() - status = U_UNSUPPORTED_ERROR; - return ICU_Utility::makeBogusString(); - } - auto form1 = StandardPlural::fromString(select(impl->quantity1), status); - auto form2 = StandardPlural::fromString(select(impl->quantity2), status); - if (U_FAILURE(status)) { - return ICU_Utility::makeBogusString(); - } - auto result = mStandardPluralRanges->resolve(form1, form2); - return UnicodeString(StandardPlural::getKeyword(result), -1, US_INV); -} - - -StringEnumeration* -PluralRules::getKeywords(UErrorCode& status) const { - if (U_FAILURE(status)) { - return nullptr; - } - if (U_FAILURE(mInternalStatus)) { - status = mInternalStatus; - return nullptr; - } - LocalPointer nameEnumerator(new PluralKeywordEnumeration(mRules, status), status); - if (U_FAILURE(status)) { - return nullptr; - } - return nameEnumerator.orphan(); -} - -double -PluralRules::getUniqueKeywordValue(const UnicodeString& /* keyword */) { - // Not Implemented. - return UPLRULES_NO_UNIQUE_VALUE; -} - -int32_t -PluralRules::getAllKeywordValues(const UnicodeString & /* keyword */, double * /* dest */, - int32_t /* destCapacity */, UErrorCode& error) { - error = U_UNSUPPORTED_ERROR; - return 0; -} - -/** - * Helper method for the overrides of getSamples() for double and DecimalQuantity - * return value types. Provide only one of an allocated array of double or - * DecimalQuantity, and a nullptr for the other. - */ -static int32_t -getSamplesFromString(const UnicodeString &samples, double *destDbl, - DecimalQuantity* destDq, int32_t destCapacity, - UErrorCode& status) { - - if ((destDbl == nullptr && destDq == nullptr) - || (destDbl != nullptr && destDq != nullptr)) { - status = U_INTERNAL_PROGRAM_ERROR; - return 0; - } - - bool isDouble = destDbl != nullptr; - int32_t sampleCount = 0; - int32_t sampleStartIdx = 0; - int32_t sampleEndIdx = 0; - - //std::string ss; // TODO: debugging. - // std::cout << "PluralRules::getSamples(), samples = \"" << samples.toUTF8String(ss) << "\"\n"; - for (sampleCount = 0; sampleCount < destCapacity && sampleStartIdx < samples.length(); ) { - sampleEndIdx = samples.indexOf(COMMA, sampleStartIdx); - if (sampleEndIdx == -1) { - sampleEndIdx = samples.length(); - } - const UnicodeString &sampleRange = samples.tempSubStringBetween(sampleStartIdx, sampleEndIdx); - // ss.erase(); - // std::cout << "PluralRules::getSamples(), samplesRange = \"" << sampleRange.toUTF8String(ss) << "\"\n"; - int32_t tildeIndex = sampleRange.indexOf(TILDE); - if (tildeIndex < 0) { - DecimalQuantity dq = DecimalQuantity::fromExponentString(sampleRange, status); - if (isDouble) { - // See warning note below about lack of precision for floating point samples for numbers with - // trailing zeroes in the decimal fraction representation. - double dblValue = dq.toDouble(); - if (!(dblValue == floor(dblValue) && dq.fractionCount() > 0)) { - destDbl[sampleCount++] = dblValue; - } - } else { - destDq[sampleCount++] = dq; - } - } else { - DecimalQuantity rangeLo = - DecimalQuantity::fromExponentString(sampleRange.tempSubStringBetween(0, tildeIndex), status); - DecimalQuantity rangeHi = DecimalQuantity::fromExponentString(sampleRange.tempSubStringBetween(tildeIndex+1), status); - if (U_FAILURE(status)) { - break; - } - if (rangeHi.toDouble() < rangeLo.toDouble()) { - status = U_INVALID_FORMAT_ERROR; - break; - } - - DecimalQuantity incrementDq; - incrementDq.setToInt(1); - int32_t lowerDispMag = rangeLo.getLowerDisplayMagnitude(); - int32_t exponent = rangeLo.getExponent(); - int32_t incrementScale = lowerDispMag + exponent; - incrementDq.adjustMagnitude(incrementScale); - double incrementVal = incrementDq.toDouble(); // 10 ^ incrementScale - - - DecimalQuantity dq(rangeLo); - double dblValue = dq.toDouble(); - double end = rangeHi.toDouble(); - - while (dblValue <= end) { - if (isDouble) { - // Hack Alert: don't return any decimal samples with integer values that - // originated from a format with trailing decimals. - // This API is returning doubles, which can't distinguish having displayed - // zeros to the right of the decimal. - // This results in test failures with values mapping back to a different keyword. - if (!(dblValue == floor(dblValue) && dq.fractionCount() > 0)) { - destDbl[sampleCount++] = dblValue; - } - } else { - destDq[sampleCount++] = dq; - } - if (sampleCount >= destCapacity) { - break; - } - - // Increment dq for next iteration - - // Because DecNum and DecimalQuantity do not support - // add operations, we need to convert to/from double, - // despite precision lossiness for decimal fractions like 0.1. - dblValue += incrementVal; - DecNum newDqDecNum; - newDqDecNum.setTo(dblValue, status); - DecimalQuantity newDq; - newDq.setToDecNum(newDqDecNum, status); - newDq.setMinFraction(-lowerDispMag); - newDq.roundToMagnitude(lowerDispMag, RoundingMode::UNUM_ROUND_HALFEVEN, status); - newDq.adjustMagnitude(-exponent); - newDq.adjustExponent(exponent); - dblValue = newDq.toDouble(); - dq = newDq; - } - } - sampleStartIdx = sampleEndIdx + 1; - } - return sampleCount; -} - -int32_t -PluralRules::getSamples(const UnicodeString &keyword, double *dest, - int32_t destCapacity, UErrorCode& status) { - if (U_FAILURE(status)) { - return 0; - } - if (U_FAILURE(mInternalStatus)) { - status = mInternalStatus; - return 0; - } - if (dest != nullptr ? destCapacity < 0 : destCapacity != 0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - RuleChain *rc = rulesForKeyword(keyword); - if (rc == nullptr) { - return 0; - } - int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, dest, nullptr, destCapacity, status); - if (numSamples == 0) { - numSamples = getSamplesFromString(rc->fDecimalSamples, dest, nullptr, destCapacity, status); - } - return numSamples; -} - -int32_t -PluralRules::getSamples(const UnicodeString &keyword, DecimalQuantity *dest, - int32_t destCapacity, UErrorCode& status) { - if (U_FAILURE(status)) { - return 0; - } - if (U_FAILURE(mInternalStatus)) { - status = mInternalStatus; - return 0; - } - if (dest != nullptr ? destCapacity < 0 : destCapacity != 0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - RuleChain *rc = rulesForKeyword(keyword); - if (rc == nullptr) { - return 0; - } - - int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, nullptr, dest, destCapacity, status); - if (numSamples == 0) { - numSamples = getSamplesFromString(rc->fDecimalSamples, nullptr, dest, destCapacity, status); - } - return numSamples; -} - - -RuleChain *PluralRules::rulesForKeyword(const UnicodeString &keyword) const { - RuleChain *rc; - for (rc = mRules; rc != nullptr; rc = rc->fNext) { - if (rc->fKeyword == keyword) { - break; - } - } - return rc; -} - - -UBool -PluralRules::isKeyword(const UnicodeString& keyword) const { - if (0 == keyword.compare(PLURAL_KEYWORD_OTHER, 5)) { - return true; - } - return rulesForKeyword(keyword) != nullptr; -} - -UnicodeString -PluralRules::getKeywordOther() const { - return UnicodeString(true, PLURAL_KEYWORD_OTHER, 5); -} - -bool -PluralRules::operator==(const PluralRules& other) const { - const UnicodeString *ptrKeyword; - UErrorCode status= U_ZERO_ERROR; - - if ( this == &other ) { - return true; - } - LocalPointer myKeywordList(getKeywords(status)); - LocalPointer otherKeywordList(other.getKeywords(status)); - if (U_FAILURE(status)) { - return false; - } - - if (myKeywordList->count(status)!=otherKeywordList->count(status)) { - return false; - } - myKeywordList->reset(status); - while ((ptrKeyword=myKeywordList->snext(status))!=nullptr) { - if (!other.isKeyword(*ptrKeyword)) { - return false; - } - } - otherKeywordList->reset(status); - while ((ptrKeyword=otherKeywordList->snext(status))!=nullptr) { - if (!this->isKeyword(*ptrKeyword)) { - return false; - } - } - if (U_FAILURE(status)) { - return false; - } - - return true; -} - - -void -PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErrorCode &status) -{ - if (U_FAILURE(status)) { - return; - } - U_ASSERT(ruleIndex == 0); // Parsers are good for a single use only! - ruleSrc = &ruleData; - - while (ruleIndex< ruleSrc->length()) { - getNextToken(status); - if (U_FAILURE(status)) { - return; - } - checkSyntax(status); - if (U_FAILURE(status)) { - return; - } - switch (type) { - case tAnd: - U_ASSERT(curAndConstraint != nullptr); - curAndConstraint = curAndConstraint->add(status); - break; - case tOr: - { - U_ASSERT(currentChain != nullptr); - OrConstraint *orNode=currentChain->ruleHeader; - while (orNode->next != nullptr) { - orNode = orNode->next; - } - orNode->next= new OrConstraint(); - if (orNode->next == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - orNode=orNode->next; - orNode->next=nullptr; - curAndConstraint = orNode->add(status); - } - break; - case tIs: - U_ASSERT(curAndConstraint != nullptr); - U_ASSERT(curAndConstraint->value == -1); - U_ASSERT(curAndConstraint->rangeList == nullptr); - break; - case tNot: - U_ASSERT(curAndConstraint != nullptr); - curAndConstraint->negated=true; - break; - - case tNotEqual: - curAndConstraint->negated=true; - U_FALLTHROUGH; - case tIn: - case tWithin: - case tEqual: - { - U_ASSERT(curAndConstraint != nullptr); - LocalPointer newRangeList(new UVector32(status), status); - if (U_FAILURE(status)) { - break; - } - curAndConstraint->rangeList = newRangeList.orphan(); - curAndConstraint->rangeList->addElement(-1, status); // range Low - curAndConstraint->rangeList->addElement(-1, status); // range Hi - rangeLowIdx = 0; - rangeHiIdx = 1; - curAndConstraint->value=PLURAL_RANGE_HIGH; - curAndConstraint->integerOnly = (type != tWithin); - } - break; - case tNumber: - U_ASSERT(curAndConstraint != nullptr); - if ( (curAndConstraint->op==AndConstraint::MOD)&& - (curAndConstraint->opNum == -1 ) ) { - curAndConstraint->opNum=getNumberValue(token); - } - else { - if (curAndConstraint->rangeList == nullptr) { - // this is for an 'is' rule - curAndConstraint->value = getNumberValue(token); - } else { - // this is for an 'in' or 'within' rule - if (curAndConstraint->rangeList->elementAti(rangeLowIdx) == -1) { - curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeLowIdx); - curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeHiIdx); - } - else { - curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeHiIdx); - if (curAndConstraint->rangeList->elementAti(rangeLowIdx) > - curAndConstraint->rangeList->elementAti(rangeHiIdx)) { - // Range Lower bound > Range Upper bound. - // U_UNEXPECTED_TOKEN seems a little funny, but it is consistently - // used for all plural rule parse errors. - status = U_UNEXPECTED_TOKEN; - break; - } - } - } - } - break; - case tComma: - // TODO: rule syntax checking is inadequate, can happen with badly formed rules. - // Catch cases like "n mod 10, is 1" here instead. - if (curAndConstraint == nullptr || curAndConstraint->rangeList == nullptr) { - status = U_UNEXPECTED_TOKEN; - break; - } - U_ASSERT(curAndConstraint->rangeList->size() >= 2); - rangeLowIdx = curAndConstraint->rangeList->size(); - curAndConstraint->rangeList->addElement(-1, status); // range Low - rangeHiIdx = curAndConstraint->rangeList->size(); - curAndConstraint->rangeList->addElement(-1, status); // range Hi - break; - case tMod: - U_ASSERT(curAndConstraint != nullptr); - curAndConstraint->op=AndConstraint::MOD; - break; - case tVariableN: - case tVariableI: - case tVariableF: - case tVariableT: - case tVariableE: - case tVariableC: - case tVariableV: - U_ASSERT(curAndConstraint != nullptr); - curAndConstraint->digitsType = type; - break; - case tKeyword: - { - RuleChain *newChain = new RuleChain; - if (newChain == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - newChain->fKeyword = token; - if (prules->mRules == nullptr) { - prules->mRules = newChain; - } else { - // The new rule chain goes at the end of the linked list of rule chains, - // unless there is an "other" keyword & chain. "other" must remain last. - RuleChain *insertAfter = prules->mRules; - while (insertAfter->fNext!=nullptr && - insertAfter->fNext->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5) != 0 ){ - insertAfter=insertAfter->fNext; - } - newChain->fNext = insertAfter->fNext; - insertAfter->fNext = newChain; - } - OrConstraint *orNode = new OrConstraint(); - if (orNode == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - newChain->ruleHeader = orNode; - curAndConstraint = orNode->add(status); - currentChain = newChain; - } - break; - - case tInteger: - for (;;) { - getNextToken(status); - if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) { - break; - } - if (type == tEllipsis) { - currentChain->fIntegerSamplesUnbounded = true; - continue; - } - currentChain->fIntegerSamples.append(token); - } - break; - - case tDecimal: - for (;;) { - getNextToken(status); - if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) { - break; - } - if (type == tEllipsis) { - currentChain->fDecimalSamplesUnbounded = true; - continue; - } - currentChain->fDecimalSamples.append(token); - } - break; - - default: - break; - } - prevType=type; - if (U_FAILURE(status)) { - break; - } - } -} - -UnicodeString -PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorCode& errCode) { - UnicodeString emptyStr; - - if (U_FAILURE(errCode)) { - return emptyStr; - } - LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "plurals", &errCode)); - if(U_FAILURE(errCode)) { - return emptyStr; - } - const char *typeKey; - switch (type) { - case UPLURAL_TYPE_CARDINAL: - typeKey = "locales"; - break; - case UPLURAL_TYPE_ORDINAL: - typeKey = "locales_ordinals"; - break; - default: - // Must not occur: The caller should have checked for valid types. - errCode = U_ILLEGAL_ARGUMENT_ERROR; - return emptyStr; - } - LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), typeKey, nullptr, &errCode)); - if(U_FAILURE(errCode)) { - return emptyStr; - } - int32_t resLen=0; - const char *curLocaleName=locale.getBaseName(); - const UChar* s = ures_getStringByKey(locRes.getAlias(), curLocaleName, &resLen, &errCode); - - if (s == nullptr) { - // Check parent locales. - UErrorCode status = U_ZERO_ERROR; - char parentLocaleName[ULOC_FULLNAME_CAPACITY]; - const char *curLocaleName2=locale.getBaseName(); - uprv_strcpy(parentLocaleName, curLocaleName2); - - while (uloc_getParent(parentLocaleName, parentLocaleName, - ULOC_FULLNAME_CAPACITY, &status) > 0) { - resLen=0; - s = ures_getStringByKey(locRes.getAlias(), parentLocaleName, &resLen, &status); - if (s != nullptr) { - errCode = U_ZERO_ERROR; - break; - } - status = U_ZERO_ERROR; - } - } - if (s==nullptr) { - return emptyStr; - } - - char setKey[256]; - u_UCharsToChars(s, setKey, resLen + 1); - // printf("\n PluralRule: %s\n", setKey); - - LocalUResourceBundlePointer ruleRes(ures_getByKey(rb.getAlias(), "rules", nullptr, &errCode)); - if(U_FAILURE(errCode)) { - return emptyStr; - } - LocalUResourceBundlePointer setRes(ures_getByKey(ruleRes.getAlias(), setKey, nullptr, &errCode)); - if (U_FAILURE(errCode)) { - return emptyStr; - } - - int32_t numberKeys = ures_getSize(setRes.getAlias()); - UnicodeString result; - const char *key=nullptr; - for(int32_t i=0; idumpRules(rules); - } - return rules; -} - -AndConstraint::AndConstraint(const AndConstraint& other) { - this->fInternalStatus = other.fInternalStatus; - if (U_FAILURE(fInternalStatus)) { - return; // stop early if the object we are copying from is invalid. - } - this->op = other.op; - this->opNum=other.opNum; - this->value=other.value; - if (other.rangeList != nullptr) { - LocalPointer newRangeList(new UVector32(fInternalStatus), fInternalStatus); - if (U_FAILURE(fInternalStatus)) { - return; - } - this->rangeList = newRangeList.orphan(); - this->rangeList->assign(*other.rangeList, fInternalStatus); - } - this->integerOnly=other.integerOnly; - this->negated=other.negated; - this->digitsType = other.digitsType; - if (other.next != nullptr) { - this->next = new AndConstraint(*other.next); - if (this->next == nullptr) { - fInternalStatus = U_MEMORY_ALLOCATION_ERROR; - } - } -} - -AndConstraint::~AndConstraint() { - delete rangeList; - rangeList = nullptr; - delete next; - next = nullptr; -} - -UBool -AndConstraint::isFulfilled(const IFixedDecimal &number) { - UBool result = true; - if (digitsType == none) { - // An empty AndConstraint, created by a rule with a keyword but no following expression. - return true; - } - - PluralOperand operand = tokenTypeToPluralOperand(digitsType); - double n = number.getPluralOperand(operand); // pulls n | i | v | f value for the number. - // Will always be positive. - // May be non-integer (n option only) - do { - if (integerOnly && n != uprv_floor(n)) { - result = false; - break; - } - - if (op == MOD) { - n = fmod(n, opNum); - } - if (rangeList == nullptr) { - result = value == -1 || // empty rule - n == value; // 'is' rule - break; - } - result = false; // 'in' or 'within' rule - for (int32_t r=0; rsize(); r+=2) { - if (rangeList->elementAti(r) <= n && n <= rangeList->elementAti(r+1)) { - result = true; - break; - } - } - } while (false); - - if (negated) { - result = !result; - } - return result; -} - -AndConstraint* -AndConstraint::add(UErrorCode& status) { - if (U_FAILURE(fInternalStatus)) { - status = fInternalStatus; - return nullptr; - } - this->next = new AndConstraint(); - if (this->next == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return this->next; -} - - -OrConstraint::OrConstraint(const OrConstraint& other) { - this->fInternalStatus = other.fInternalStatus; - if (U_FAILURE(fInternalStatus)) { - return; // stop early if the object we are copying from is invalid. - } - if ( other.childNode != nullptr ) { - this->childNode = new AndConstraint(*(other.childNode)); - if (this->childNode == nullptr) { - fInternalStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - if (other.next != nullptr ) { - this->next = new OrConstraint(*(other.next)); - if (this->next == nullptr) { - fInternalStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (U_FAILURE(this->next->fInternalStatus)) { - this->fInternalStatus = this->next->fInternalStatus; - } - } -} - -OrConstraint::~OrConstraint() { - delete childNode; - childNode = nullptr; - delete next; - next = nullptr; -} - -AndConstraint* -OrConstraint::add(UErrorCode& status) { - if (U_FAILURE(fInternalStatus)) { - status = fInternalStatus; - return nullptr; - } - OrConstraint *curOrConstraint=this; - { - while (curOrConstraint->next!=nullptr) { - curOrConstraint = curOrConstraint->next; - } - U_ASSERT(curOrConstraint->childNode == nullptr); - curOrConstraint->childNode = new AndConstraint(); - if (curOrConstraint->childNode == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - } - return curOrConstraint->childNode; -} - -UBool -OrConstraint::isFulfilled(const IFixedDecimal &number) { - OrConstraint* orRule=this; - UBool result=false; - - while (orRule!=nullptr && !result) { - result=true; - AndConstraint* andRule = orRule->childNode; - while (andRule!=nullptr && result) { - result = andRule->isFulfilled(number); - andRule=andRule->next; - } - orRule = orRule->next; - } - - return result; -} - - -RuleChain::RuleChain(const RuleChain& other) : - fKeyword(other.fKeyword), fDecimalSamples(other.fDecimalSamples), - fIntegerSamples(other.fIntegerSamples), fDecimalSamplesUnbounded(other.fDecimalSamplesUnbounded), - fIntegerSamplesUnbounded(other.fIntegerSamplesUnbounded), fInternalStatus(other.fInternalStatus) { - if (U_FAILURE(this->fInternalStatus)) { - return; // stop early if the object we are copying from is invalid. - } - if (other.ruleHeader != nullptr) { - this->ruleHeader = new OrConstraint(*(other.ruleHeader)); - if (this->ruleHeader == nullptr) { - this->fInternalStatus = U_MEMORY_ALLOCATION_ERROR; - } - else if (U_FAILURE(this->ruleHeader->fInternalStatus)) { - // If the OrConstraint wasn't fully copied, then set our status to failure as well. - this->fInternalStatus = this->ruleHeader->fInternalStatus; - return; // exit early. - } - } - if (other.fNext != nullptr ) { - this->fNext = new RuleChain(*other.fNext); - if (this->fNext == nullptr) { - this->fInternalStatus = U_MEMORY_ALLOCATION_ERROR; - } - else if (U_FAILURE(this->fNext->fInternalStatus)) { - // If the RuleChain wasn't fully copied, then set our status to failure as well. - this->fInternalStatus = this->fNext->fInternalStatus; - } - } -} - -RuleChain::~RuleChain() { - delete fNext; - delete ruleHeader; -} - -UnicodeString -RuleChain::select(const IFixedDecimal &number) const { - if (!number.isNaN() && !number.isInfinite()) { - for (const RuleChain *rules = this; rules != nullptr; rules = rules->fNext) { - if (rules->ruleHeader->isFulfilled(number)) { - return rules->fKeyword; - } - } - } - return UnicodeString(true, PLURAL_KEYWORD_OTHER, 5); -} - -static UnicodeString tokenString(tokenType tok) { - UnicodeString s; - switch (tok) { - case tVariableN: - s.append(LOW_N); break; - case tVariableI: - s.append(LOW_I); break; - case tVariableF: - s.append(LOW_F); break; - case tVariableV: - s.append(LOW_V); break; - case tVariableT: - s.append(LOW_T); break; - case tVariableE: - s.append(LOW_E); break; - case tVariableC: - s.append(LOW_C); break; - default: - s.append(TILDE); - } - return s; -} - -void -RuleChain::dumpRules(UnicodeString& result) { - UChar digitString[16]; - - if ( ruleHeader != nullptr ) { - result += fKeyword; - result += COLON; - result += SPACE; - OrConstraint* orRule=ruleHeader; - while ( orRule != nullptr ) { - AndConstraint* andRule=orRule->childNode; - while ( andRule != nullptr ) { - if ((andRule->op==AndConstraint::NONE) && (andRule->rangeList==nullptr) && (andRule->value == -1)) { - // Empty Rules. - } else if ( (andRule->op==AndConstraint::NONE) && (andRule->rangeList==nullptr) ) { - result += tokenString(andRule->digitsType); - result += UNICODE_STRING_SIMPLE(" is "); - if (andRule->negated) { - result += UNICODE_STRING_SIMPLE("not "); - } - uprv_itou(digitString,16, andRule->value,10,0); - result += UnicodeString(digitString); - } - else { - result += tokenString(andRule->digitsType); - result += SPACE; - if (andRule->op==AndConstraint::MOD) { - result += UNICODE_STRING_SIMPLE("mod "); - uprv_itou(digitString,16, andRule->opNum,10,0); - result += UnicodeString(digitString); - } - if (andRule->rangeList==nullptr) { - if (andRule->negated) { - result += UNICODE_STRING_SIMPLE(" is not "); - uprv_itou(digitString,16, andRule->value,10,0); - result += UnicodeString(digitString); - } - else { - result += UNICODE_STRING_SIMPLE(" is "); - uprv_itou(digitString,16, andRule->value,10,0); - result += UnicodeString(digitString); - } - } - else { - if (andRule->negated) { - if ( andRule->integerOnly ) { - result += UNICODE_STRING_SIMPLE(" not in "); - } - else { - result += UNICODE_STRING_SIMPLE(" not within "); - } - } - else { - if ( andRule->integerOnly ) { - result += UNICODE_STRING_SIMPLE(" in "); - } - else { - result += UNICODE_STRING_SIMPLE(" within "); - } - } - for (int32_t r=0; rrangeList->size(); r+=2) { - int32_t rangeLo = andRule->rangeList->elementAti(r); - int32_t rangeHi = andRule->rangeList->elementAti(r+1); - uprv_itou(digitString,16, rangeLo, 10, 0); - result += UnicodeString(digitString); - result += UNICODE_STRING_SIMPLE(".."); - uprv_itou(digitString,16, rangeHi, 10,0); - result += UnicodeString(digitString); - if (r+2 < andRule->rangeList->size()) { - result += UNICODE_STRING_SIMPLE(", "); - } - } - } - } - if ( (andRule=andRule->next) != nullptr) { - result += UNICODE_STRING_SIMPLE(" and "); - } - } - if ( (orRule = orRule->next) != nullptr ) { - result += UNICODE_STRING_SIMPLE(" or "); - } - } - } - if ( fNext != nullptr ) { - result += UNICODE_STRING_SIMPLE("; "); - fNext->dumpRules(result); - } -} - - -UErrorCode -RuleChain::getKeywords(int32_t capacityOfKeywords, UnicodeString* keywords, int32_t& arraySize) const { - if (U_FAILURE(fInternalStatus)) { - return fInternalStatus; - } - if ( arraySize < capacityOfKeywords-1 ) { - keywords[arraySize++]=fKeyword; - } - else { - return U_BUFFER_OVERFLOW_ERROR; - } - - if ( fNext != nullptr ) { - return fNext->getKeywords(capacityOfKeywords, keywords, arraySize); - } - else { - return U_ZERO_ERROR; - } -} - -UBool -RuleChain::isKeyword(const UnicodeString& keywordParam) const { - if ( fKeyword == keywordParam ) { - return true; - } - - if ( fNext != nullptr ) { - return fNext->isKeyword(keywordParam); - } - else { - return false; - } -} - - -PluralRuleParser::PluralRuleParser() : - ruleIndex(0), token(), type(none), prevType(none), - curAndConstraint(nullptr), currentChain(nullptr), rangeLowIdx(-1), rangeHiIdx(-1) -{ -} - -PluralRuleParser::~PluralRuleParser() { -} - - -int32_t -PluralRuleParser::getNumberValue(const UnicodeString& token) { - int32_t i; - char digits[128]; - - i = token.extract(0, token.length(), digits, UPRV_LENGTHOF(digits), US_INV); - digits[i]='\0'; - - return((int32_t)atoi(digits)); -} - - -void -PluralRuleParser::checkSyntax(UErrorCode &status) -{ - if (U_FAILURE(status)) { - return; - } - if (!(prevType==none || prevType==tSemiColon)) { - type = getKeyType(token, type); // Switch token type from tKeyword if we scanned a reserved word, - // and we are not at the start of a rule, where a - // keyword is expected. - } - - switch(prevType) { - case none: - case tSemiColon: - if (type!=tKeyword && type != tEOF) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tVariableN: - case tVariableI: - case tVariableF: - case tVariableT: - case tVariableE: - case tVariableC: - case tVariableV: - if (type != tIs && type != tMod && type != tIn && - type != tNot && type != tWithin && type != tEqual && type != tNotEqual) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tKeyword: - if (type != tColon) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tColon: - if (!(type == tVariableN || - type == tVariableI || - type == tVariableF || - type == tVariableT || - type == tVariableE || - type == tVariableC || - type == tVariableV || - type == tAt)) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tIs: - if ( type != tNumber && type != tNot) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tNot: - if (type != tNumber && type != tIn && type != tWithin) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tMod: - case tDot2: - case tIn: - case tWithin: - case tEqual: - case tNotEqual: - if (type != tNumber) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tAnd: - case tOr: - if ( type != tVariableN && - type != tVariableI && - type != tVariableF && - type != tVariableT && - type != tVariableE && - type != tVariableC && - type != tVariableV) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tComma: - if (type != tNumber) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tNumber: - if (type != tDot2 && type != tSemiColon && type != tIs && type != tNot && - type != tIn && type != tEqual && type != tNotEqual && type != tWithin && - type != tAnd && type != tOr && type != tComma && type != tAt && - type != tEOF) - { - status = U_UNEXPECTED_TOKEN; - } - // TODO: a comma following a number that is not part of a range will be allowed. - // It's not the only case of this sort of thing. Parser needs a re-write. - break; - case tAt: - if (type != tDecimal && type != tInteger) { - status = U_UNEXPECTED_TOKEN; - } - break; - default: - status = U_UNEXPECTED_TOKEN; - break; - } -} - - -/* - * Scan the next token from the input rules. - * rules and returned token type are in the parser state variables. - */ -void -PluralRuleParser::getNextToken(UErrorCode &status) -{ - if (U_FAILURE(status)) { - return; - } - - UChar ch; - while (ruleIndex < ruleSrc->length()) { - ch = ruleSrc->charAt(ruleIndex); - type = charType(ch); - if (type != tSpace) { - break; - } - ++(ruleIndex); - } - if (ruleIndex >= ruleSrc->length()) { - type = tEOF; - return; - } - int32_t curIndex= ruleIndex; - - switch (type) { - case tColon: - case tSemiColon: - case tComma: - case tEllipsis: - case tTilde: // scanned '~' - case tAt: // scanned '@' - case tEqual: // scanned '=' - case tMod: // scanned '%' - // Single character tokens. - ++curIndex; - break; - - case tNotEqual: // scanned '!' - if (ruleSrc->charAt(curIndex+1) == EQUALS) { - curIndex += 2; - } else { - type = none; - curIndex += 1; - } - break; - - case tKeyword: - while (type == tKeyword && ++curIndex < ruleSrc->length()) { - ch = ruleSrc->charAt(curIndex); - type = charType(ch); - } - type = tKeyword; - break; - - case tNumber: - while (type == tNumber && ++curIndex < ruleSrc->length()) { - ch = ruleSrc->charAt(curIndex); - type = charType(ch); - } - type = tNumber; - break; - - case tDot: - // We could be looking at either ".." in a range, or "..." at the end of a sample. - if (curIndex+1 >= ruleSrc->length() || ruleSrc->charAt(curIndex+1) != DOT) { - ++curIndex; - break; // Single dot - } - if (curIndex+2 >= ruleSrc->length() || ruleSrc->charAt(curIndex+2) != DOT) { - curIndex += 2; - type = tDot2; - break; // double dot - } - type = tEllipsis; - curIndex += 3; - break; // triple dot - - default: - status = U_UNEXPECTED_TOKEN; - ++curIndex; - break; - } - - U_ASSERT(ruleIndex <= ruleSrc->length()); - U_ASSERT(curIndex <= ruleSrc->length()); - token=UnicodeString(*ruleSrc, ruleIndex, curIndex-ruleIndex); - ruleIndex = curIndex; -} - -tokenType -PluralRuleParser::charType(UChar ch) { - if ((ch>=U_ZERO) && (ch<=U_NINE)) { - return tNumber; - } - if (ch>=LOW_A && ch<=LOW_Z) { - return tKeyword; - } - switch (ch) { - case COLON: - return tColon; - case SPACE: - return tSpace; - case SEMI_COLON: - return tSemiColon; - case DOT: - return tDot; - case COMMA: - return tComma; - case EXCLAMATION: - return tNotEqual; - case EQUALS: - return tEqual; - case PERCENT_SIGN: - return tMod; - case AT: - return tAt; - case ELLIPSIS: - return tEllipsis; - case TILDE: - return tTilde; - default : - return none; - } -} - - -// Set token type for reserved words in the Plural Rule syntax. - -tokenType -PluralRuleParser::getKeyType(const UnicodeString &token, tokenType keyType) -{ - if (keyType != tKeyword) { - return keyType; - } - - if (0 == token.compare(PK_VAR_N, 1)) { - keyType = tVariableN; - } else if (0 == token.compare(PK_VAR_I, 1)) { - keyType = tVariableI; - } else if (0 == token.compare(PK_VAR_F, 1)) { - keyType = tVariableF; - } else if (0 == token.compare(PK_VAR_T, 1)) { - keyType = tVariableT; - } else if (0 == token.compare(PK_VAR_E, 1)) { - keyType = tVariableE; - } else if (0 == token.compare(PK_VAR_C, 1)) { - keyType = tVariableC; - } else if (0 == token.compare(PK_VAR_V, 1)) { - keyType = tVariableV; - } else if (0 == token.compare(PK_IS, 2)) { - keyType = tIs; - } else if (0 == token.compare(PK_AND, 3)) { - keyType = tAnd; - } else if (0 == token.compare(PK_IN, 2)) { - keyType = tIn; - } else if (0 == token.compare(PK_WITHIN, 6)) { - keyType = tWithin; - } else if (0 == token.compare(PK_NOT, 3)) { - keyType = tNot; - } else if (0 == token.compare(PK_MOD, 3)) { - keyType = tMod; - } else if (0 == token.compare(PK_OR, 2)) { - keyType = tOr; - } else if (0 == token.compare(PK_DECIMAL, 7)) { - keyType = tDecimal; - } else if (0 == token.compare(PK_INTEGER, 7)) { - keyType = tInteger; - } - return keyType; -} - - -PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode& status) - : pos(0), fKeywordNames(status) { - if (U_FAILURE(status)) { - return; - } - fKeywordNames.setDeleter(uprv_deleteUObject); - UBool addKeywordOther = true; - RuleChain *node = header; - while (node != nullptr) { - LocalPointer newElem(node->fKeyword.clone(), status); - fKeywordNames.adoptElement(newElem.orphan(), status); - if (U_FAILURE(status)) { - return; - } - if (0 == node->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5)) { - addKeywordOther = false; - } - node = node->fNext; - } - - if (addKeywordOther) { - LocalPointer newElem(new UnicodeString(PLURAL_KEYWORD_OTHER), status); - fKeywordNames.adoptElement(newElem.orphan(), status); - if (U_FAILURE(status)) { - return; - } - } -} - -const UnicodeString* -PluralKeywordEnumeration::snext(UErrorCode& status) { - if (U_SUCCESS(status) && pos < fKeywordNames.size()) { - return (const UnicodeString*)fKeywordNames.elementAt(pos++); - } - return nullptr; -} - -void -PluralKeywordEnumeration::reset(UErrorCode& /*status*/) { - pos=0; -} - -int32_t -PluralKeywordEnumeration::count(UErrorCode& /*status*/) const { - return fKeywordNames.size(); -} - -PluralKeywordEnumeration::~PluralKeywordEnumeration() { -} - -PluralOperand tokenTypeToPluralOperand(tokenType tt) { - switch(tt) { - case tVariableN: - return PLURAL_OPERAND_N; - case tVariableI: - return PLURAL_OPERAND_I; - case tVariableF: - return PLURAL_OPERAND_F; - case tVariableV: - return PLURAL_OPERAND_V; - case tVariableT: - return PLURAL_OPERAND_T; - case tVariableE: - return PLURAL_OPERAND_E; - case tVariableC: - return PLURAL_OPERAND_E; - default: - UPRV_UNREACHABLE_EXIT; // unexpected. - } -} - -FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f, int32_t e, int32_t c) { - init(n, v, f, e, c); -} - -FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f, int32_t e) { - init(n, v, f, e); - // check values. TODO make into unit test. - // - // long visiblePower = (int) Math.pow(10.0, v); - // if (decimalDigits > visiblePower) { - // throw new IllegalArgumentException(); - // } - // double fraction = intValue + (decimalDigits / (double) visiblePower); - // if (fraction != source) { - // double diff = Math.abs(fraction - source)/(Math.abs(fraction) + Math.abs(source)); - // if (diff > 0.00000001d) { - // throw new IllegalArgumentException(); - // } - // } -} - -FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f) { - init(n, v, f); -} - -FixedDecimal::FixedDecimal(double n, int32_t v) { - // Ugly, but for samples we don't care. - init(n, v, getFractionalDigits(n, v)); -} - -FixedDecimal::FixedDecimal(double n) { - init(n); -} - -FixedDecimal::FixedDecimal() { - init(0, 0, 0); -} - - -// Create a FixedDecimal from a UnicodeString containing a number. -// Inefficient, but only used for samples, so simplicity trumps efficiency. - -FixedDecimal::FixedDecimal(const UnicodeString &num, UErrorCode &status) { - CharString cs; - int32_t parsedExponent = 0; - int32_t parsedCompactExponent = 0; - - int32_t exponentIdx = num.indexOf(u'e'); - if (exponentIdx < 0) { - exponentIdx = num.indexOf(u'E'); - } - int32_t compactExponentIdx = num.indexOf(u'c'); - if (compactExponentIdx < 0) { - compactExponentIdx = num.indexOf(u'C'); - } - - if (exponentIdx >= 0) { - cs.appendInvariantChars(num.tempSubString(0, exponentIdx), status); - int32_t expSubstrStart = exponentIdx + 1; - parsedExponent = ICU_Utility::parseAsciiInteger(num, expSubstrStart); - } - else if (compactExponentIdx >= 0) { - cs.appendInvariantChars(num.tempSubString(0, compactExponentIdx), status); - int32_t expSubstrStart = compactExponentIdx + 1; - parsedCompactExponent = ICU_Utility::parseAsciiInteger(num, expSubstrStart); - - parsedExponent = parsedCompactExponent; - exponentIdx = compactExponentIdx; - } - else { - cs.appendInvariantChars(num, 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.toDouble(); - if (decimalPoint == -1) { - init(n, 0, 0, parsedExponent); - } else { - int32_t fractionNumLength = exponentIdx < 0 ? num.length() : cs.length(); - int32_t v = fractionNumLength - decimalPoint - 1; - init(n, v, getFractionalDigits(n, v), parsedExponent); - } -} - - -FixedDecimal::FixedDecimal(const FixedDecimal &other) { - source = other.source; - visibleDecimalDigitCount = other.visibleDecimalDigitCount; - decimalDigits = other.decimalDigits; - decimalDigitsWithoutTrailingZeros = other.decimalDigitsWithoutTrailingZeros; - intValue = other.intValue; - exponent = other.exponent; - _hasIntegerValue = other._hasIntegerValue; - isNegative = other.isNegative; - _isNaN = other._isNaN; - _isInfinite = other._isInfinite; -} - -FixedDecimal::~FixedDecimal() = default; - -FixedDecimal FixedDecimal::createWithExponent(double n, int32_t v, int32_t e) { - return FixedDecimal(n, v, getFractionalDigits(n, v), e); -} - - -void FixedDecimal::init(double n) { - int32_t numFractionDigits = decimals(n); - init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits)); -} - - -void FixedDecimal::init(double n, int32_t v, int64_t f) { - int32_t exponent = 0; - init(n, v, f, exponent); -} - -void FixedDecimal::init(double n, int32_t v, int64_t f, int32_t e) { - // Currently, `c` is an alias for `e` - init(n, v, f, e, e); -} - -void FixedDecimal::init(double n, int32_t v, int64_t f, int32_t e, int32_t c) { - isNegative = n < 0.0; - source = fabs(n); - _isNaN = uprv_isNaN(source); - _isInfinite = uprv_isInfinite(source); - exponent = e; - if (exponent == 0) { - exponent = c; - } - if (_isNaN || _isInfinite) { - v = 0; - f = 0; - intValue = 0; - _hasIntegerValue = false; - } else { - intValue = (int64_t)source; - _hasIntegerValue = (source == intValue); - } - - visibleDecimalDigitCount = v; - decimalDigits = f; - if (f == 0) { - decimalDigitsWithoutTrailingZeros = 0; - } else { - int64_t fdwtz = f; - while ((fdwtz%10) == 0) { - fdwtz /= 10; - } - decimalDigitsWithoutTrailingZeros = fdwtz; - } -} - - -// Fast path only exact initialization. Return true if successful. -// Note: Do not multiply by 10 each time through loop, rounding cruft can build -// up that makes the check for an integer result fail. -// A single multiply of the original number works more reliably. -static int32_t p10[] = {1, 10, 100, 1000, 10000}; -UBool FixedDecimal::quickInit(double n) { - UBool success = false; - n = fabs(n); - int32_t numFractionDigits; - for (numFractionDigits = 0; numFractionDigits <= 3; numFractionDigits++) { - double scaledN = n * p10[numFractionDigits]; - if (scaledN == floor(scaledN)) { - success = true; - break; - } - } - if (success) { - init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits)); - } - return success; -} - - - -int32_t FixedDecimal::decimals(double n) { - // Count the number of decimal digits in the fraction part of the number, excluding trailing zeros. - // fastpath the common cases, integers or fractions with 3 or fewer digits - n = fabs(n); - for (int ndigits=0; ndigits<=3; ndigits++) { - double scaledN = n * p10[ndigits]; - if (scaledN == floor(scaledN)) { - return ndigits; - } - } - - // Slow path, convert with sprintf, parse converted output. - char buf[30] = {0}; - sprintf(buf, "%1.15e", n); - // formatted number looks like this: 1.234567890123457e-01 - int exponent = atoi(buf+18); - int numFractionDigits = 15; - for (int i=16; ; --i) { - if (buf[i] != '0') { - break; - } - --numFractionDigits; - } - numFractionDigits -= exponent; // Fraction part of fixed point representation. - return numFractionDigits; -} - - -// Get the fraction digits of a double, represented as an integer. -// v is the number of visible fraction digits in the displayed form of the number. -// Example: n = 1001.234, v = 6, result = 234000 -// TODO: need to think through how this is used in the plural rule context. -// This function can easily encounter integer overflow, -// and can easily return noise digits when the precision of a double is exceeded. - -int64_t FixedDecimal::getFractionalDigits(double n, int32_t v) { - if (v == 0 || n == floor(n) || uprv_isNaN(n) || uprv_isPositiveInfinity(n)) { - return 0; - } - n = fabs(n); - double fract = n - floor(n); - switch (v) { - case 1: return (int64_t)(fract*10.0 + 0.5); - case 2: return (int64_t)(fract*100.0 + 0.5); - case 3: return (int64_t)(fract*1000.0 + 0.5); - default: - double scaled = floor(fract * pow(10.0, (double)v) + 0.5); - if (scaled >= static_cast(U_INT64_MAX)) { - // Note: a double cannot accurately represent U_INT64_MAX. Casting it to double - // will round up to the next representable value, which is U_INT64_MAX + 1. - return U_INT64_MAX; - } else { - return (int64_t)scaled; - } - } -} - - -void FixedDecimal::adjustForMinFractionDigits(int32_t minFractionDigits) { - int32_t numTrailingFractionZeros = minFractionDigits - visibleDecimalDigitCount; - if (numTrailingFractionZeros > 0) { - for (int32_t i=0; i= 100000000000000000LL) { - break; - } - decimalDigits *= 10; - } - visibleDecimalDigitCount += numTrailingFractionZeros; - } -} - - -double FixedDecimal::getPluralOperand(PluralOperand operand) const { - switch(operand) { - case PLURAL_OPERAND_N: return (exponent == 0 ? source : source * pow(10.0, exponent)); - case PLURAL_OPERAND_I: return (double) longValue(); - case PLURAL_OPERAND_F: return static_cast(decimalDigits); - case PLURAL_OPERAND_T: return static_cast(decimalDigitsWithoutTrailingZeros); - case PLURAL_OPERAND_V: return visibleDecimalDigitCount; - case PLURAL_OPERAND_E: return exponent; - case PLURAL_OPERAND_C: return exponent; - default: - UPRV_UNREACHABLE_EXIT; // unexpected. - } -} - -bool FixedDecimal::isNaN() const { - return _isNaN; -} - -bool FixedDecimal::isInfinite() const { - return _isInfinite; -} - -bool FixedDecimal::hasIntegerValue() const { - return _hasIntegerValue; -} - -bool FixedDecimal::isNanOrInfinity() const { - return _isNaN || _isInfinite; -} - -int32_t FixedDecimal::getVisibleFractionDigitCount() const { - return visibleDecimalDigitCount; -} - -bool FixedDecimal::operator==(const FixedDecimal &other) const { - return source == other.source && visibleDecimalDigitCount == other.visibleDecimalDigitCount - && decimalDigits == other.decimalDigits && exponent == other.exponent; -} - -UnicodeString FixedDecimal::toString() const { - char pattern[15]; - char buffer[20]; - if (exponent != 0) { - snprintf(pattern, sizeof(pattern), "%%.%dfe%%d", visibleDecimalDigitCount); - snprintf(buffer, sizeof(buffer), pattern, source, exponent); - } else { - snprintf(pattern, sizeof(pattern), "%%.%df", visibleDecimalDigitCount); - snprintf(buffer, sizeof(buffer), pattern, source); - } - return UnicodeString(buffer, -1, US_INV); -} - -double FixedDecimal::doubleValue() const { - return (isNegative ? -source : source) * pow(10.0, exponent); -} - -int64_t FixedDecimal::longValue() const { - if (exponent == 0) { - return intValue; - } else { - return (long) (pow(10.0, exponent) * intValue); - } -} - - -PluralAvailableLocalesEnumeration::PluralAvailableLocalesEnumeration(UErrorCode &status) { - fOpenStatus = status; - if (U_FAILURE(status)) { - return; - } - fOpenStatus = U_ZERO_ERROR; // clear any warnings. - LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "plurals", &fOpenStatus)); - fLocales = ures_getByKey(rb.getAlias(), "locales", nullptr, &fOpenStatus); -} - -PluralAvailableLocalesEnumeration::~PluralAvailableLocalesEnumeration() { - ures_close(fLocales); - ures_close(fRes); - fLocales = nullptr; - fRes = nullptr; -} - -const char *PluralAvailableLocalesEnumeration::next(int32_t *resultLength, UErrorCode &status) { - if (U_FAILURE(status)) { - return nullptr; - } - if (U_FAILURE(fOpenStatus)) { - status = fOpenStatus; - return nullptr; - } - fRes = ures_getNextResource(fLocales, fRes, &status); - if (fRes == nullptr || U_FAILURE(status)) { - if (status == U_INDEX_OUTOFBOUNDS_ERROR) { - status = U_ZERO_ERROR; - } - return nullptr; - } - const char *result = ures_getKey(fRes); - if (resultLength != nullptr) { - *resultLength = static_cast(uprv_strlen(result)); - } - return result; -} - - -void PluralAvailableLocalesEnumeration::reset(UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if (U_FAILURE(fOpenStatus)) { - status = fOpenStatus; - return; - } - ures_resetIterator(fLocales); -} - -int32_t PluralAvailableLocalesEnumeration::count(UErrorCode &status) const { - if (U_FAILURE(status)) { - return 0; - } - if (U_FAILURE(fOpenStatus)) { - status = fOpenStatus; - return 0; - } - return ures_getSize(fLocales); -} - -U_NAMESPACE_END - - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2007-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File plurrule.cpp +*/ + +#include +#include + +#include "unicode/utypes.h" +#include "unicode/localpointer.h" +#include "unicode/plurrule.h" +#include "unicode/upluralrules.h" +#include "unicode/ures.h" +#include "unicode/numfmt.h" +#include "unicode/decimfmt.h" +#include "unicode/numberrangeformatter.h" +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "hash.h" +#include "locutil.h" +#include "mutex.h" +#include "number_decnum.h" +#include "patternprops.h" +#include "plurrule_impl.h" +#include "putilimp.h" +#include "ucln_in.h" +#include "ustrfmt.h" +#include "uassert.h" +#include "uvectr32.h" +#include "sharedpluralrules.h" +#include "unifiedcache.h" +#include "number_decimalquantity.h" +#include "util.h" +#include "pluralranges.h" +#include "numrange_impl.h" + +#if !UCONFIG_NO_FORMATTING + +U_NAMESPACE_BEGIN + +using namespace icu::pluralimpl; +using icu::number::impl::DecNum; +using icu::number::impl::DecimalQuantity; +using icu::number::impl::RoundingMode; + +static const char16_t PLURAL_KEYWORD_OTHER[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,0}; +static const char16_t PLURAL_DEFAULT_RULE[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,COLON,SPACE,LOW_N,0}; +static const char16_t PK_IN[]={LOW_I,LOW_N,0}; +static const char16_t PK_NOT[]={LOW_N,LOW_O,LOW_T,0}; +static const char16_t PK_IS[]={LOW_I,LOW_S,0}; +static const char16_t PK_MOD[]={LOW_M,LOW_O,LOW_D,0}; +static const char16_t PK_AND[]={LOW_A,LOW_N,LOW_D,0}; +static const char16_t PK_OR[]={LOW_O,LOW_R,0}; +static const char16_t PK_VAR_N[]={LOW_N,0}; +static const char16_t PK_VAR_I[]={LOW_I,0}; +static const char16_t PK_VAR_F[]={LOW_F,0}; +static const char16_t PK_VAR_T[]={LOW_T,0}; +static const char16_t PK_VAR_E[]={LOW_E,0}; +static const char16_t PK_VAR_C[]={LOW_C,0}; +static const char16_t PK_VAR_V[]={LOW_V,0}; +static const char16_t PK_WITHIN[]={LOW_W,LOW_I,LOW_T,LOW_H,LOW_I,LOW_N,0}; +static const char16_t PK_DECIMAL[]={LOW_D,LOW_E,LOW_C,LOW_I,LOW_M,LOW_A,LOW_L,0}; +static const char16_t PK_INTEGER[]={LOW_I,LOW_N,LOW_T,LOW_E,LOW_G,LOW_E,LOW_R,0}; + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralRules) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration) + +PluralRules::PluralRules(UErrorCode& /*status*/) +: UObject(), + mRules(nullptr), + mStandardPluralRanges(nullptr), + mInternalStatus(U_ZERO_ERROR) +{ +} + +PluralRules::PluralRules(const PluralRules& other) +: UObject(other), + mRules(nullptr), + mStandardPluralRanges(nullptr), + mInternalStatus(U_ZERO_ERROR) +{ + *this=other; +} + +PluralRules::~PluralRules() { + delete mRules; + delete mStandardPluralRanges; +} + +SharedPluralRules::~SharedPluralRules() { + delete ptr; +} + +PluralRules* +PluralRules::clone() const { + // Since clone doesn't have a 'status' parameter, the best we can do is return nullptr if + // the newly created object was not fully constructed properly (an error occurred). + UErrorCode localStatus = U_ZERO_ERROR; + return clone(localStatus); +} + +PluralRules* +PluralRules::clone(UErrorCode& status) const { + LocalPointer newObj(new PluralRules(*this), status); + if (U_SUCCESS(status) && U_FAILURE(newObj->mInternalStatus)) { + status = newObj->mInternalStatus; + newObj.adoptInstead(nullptr); + } + return newObj.orphan(); +} + +PluralRules& +PluralRules::operator=(const PluralRules& other) { + if (this != &other) { + delete mRules; + mRules = nullptr; + delete mStandardPluralRanges; + mStandardPluralRanges = nullptr; + mInternalStatus = other.mInternalStatus; + if (U_FAILURE(mInternalStatus)) { + // bail out early if the object we were copying from was already 'invalid'. + return *this; + } + if (other.mRules != nullptr) { + mRules = new RuleChain(*other.mRules); + if (mRules == nullptr) { + mInternalStatus = U_MEMORY_ALLOCATION_ERROR; + } + else if (U_FAILURE(mRules->fInternalStatus)) { + // If the RuleChain wasn't fully copied, then set our status to failure as well. + mInternalStatus = mRules->fInternalStatus; + } + } + if (other.mStandardPluralRanges != nullptr) { + mStandardPluralRanges = other.mStandardPluralRanges->copy(mInternalStatus) + .toPointer(mInternalStatus) + .orphan(); + } + } + return *this; +} + +StringEnumeration* PluralRules::getAvailableLocales(UErrorCode &status) { + if (U_FAILURE(status)) { + return nullptr; + } + LocalPointer result(new PluralAvailableLocalesEnumeration(status), status); + if (U_FAILURE(status)) { + return nullptr; + } + return result.orphan(); +} + + +PluralRules* U_EXPORT2 +PluralRules::createRules(const UnicodeString& description, UErrorCode& status) { + if (U_FAILURE(status)) { + return nullptr; + } + PluralRuleParser parser; + LocalPointer newRules(new PluralRules(status), status); + if (U_FAILURE(status)) { + return nullptr; + } + parser.parse(description, newRules.getAlias(), status); + if (U_FAILURE(status)) { + newRules.adoptInstead(nullptr); + } + return newRules.orphan(); +} + + +PluralRules* U_EXPORT2 +PluralRules::createDefaultRules(UErrorCode& status) { + return createRules(UnicodeString(true, PLURAL_DEFAULT_RULE, -1), status); +} + +/******************************************************************************/ +/* Create PluralRules cache */ + +template<> U_I18N_API +const SharedPluralRules *LocaleCacheKey::createObject( + const void * /*unused*/, UErrorCode &status) const { + const char *localeId = fLoc.getName(); + LocalPointer pr(PluralRules::internalForLocale(localeId, UPLURAL_TYPE_CARDINAL, status), status); + if (U_FAILURE(status)) { + return nullptr; + } + LocalPointer result(new SharedPluralRules(pr.getAlias()), status); + if (U_FAILURE(status)) { + return nullptr; + } + pr.orphan(); // result was successfully created so it nows pr. + result->addRef(); + return result.orphan(); +} + +/* end plural rules cache */ +/******************************************************************************/ + +const SharedPluralRules* U_EXPORT2 +PluralRules::createSharedInstance( + const Locale& locale, UPluralType type, UErrorCode& status) { + if (U_FAILURE(status)) { + return nullptr; + } + if (type != UPLURAL_TYPE_CARDINAL) { + status = U_UNSUPPORTED_ERROR; + return nullptr; + } + const SharedPluralRules *result = nullptr; + UnifiedCache::getByLocale(locale, result, status); + return result; +} + +PluralRules* U_EXPORT2 +PluralRules::forLocale(const Locale& locale, UErrorCode& status) { + return forLocale(locale, UPLURAL_TYPE_CARDINAL, status); +} + +PluralRules* U_EXPORT2 +PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& status) { + if (type != UPLURAL_TYPE_CARDINAL) { + return internalForLocale(locale, type, status); + } + const SharedPluralRules *shared = createSharedInstance( + locale, type, status); + if (U_FAILURE(status)) { + return nullptr; + } + PluralRules *result = (*shared)->clone(status); + shared->removeRef(); + return result; +} + +PluralRules* U_EXPORT2 +PluralRules::internalForLocale(const Locale& locale, UPluralType type, UErrorCode& status) { + if (U_FAILURE(status)) { + return nullptr; + } + if (type >= UPLURAL_TYPE_COUNT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + LocalPointer newObj(new PluralRules(status), status); + if (U_FAILURE(status)) { + return nullptr; + } + UnicodeString locRule = newObj->getRuleFromResource(locale, type, status); + // TODO: which other errors, if any, should be returned? + if (locRule.length() == 0) { + // If an out-of-memory error occurred, then stop and report the failure. + if (status == U_MEMORY_ALLOCATION_ERROR) { + return nullptr; + } + // Locales with no specific rules (all numbers have the "other" category + // will return a U_MISSING_RESOURCE_ERROR at this point. This is not + // an error. + locRule = UnicodeString(PLURAL_DEFAULT_RULE); + status = U_ZERO_ERROR; + } + PluralRuleParser parser; + parser.parse(locRule, newObj.getAlias(), status); + // TODO: should rule parse errors be returned, or + // should we silently use default rules? + // Original impl used default rules. + // Ask the question to ICU Core. + + newObj->mStandardPluralRanges = StandardPluralRanges::forLocale(locale, status) + .toPointer(status) + .orphan(); + + return newObj.orphan(); +} + +UnicodeString +PluralRules::select(int32_t number) const { + return select(FixedDecimal(number)); +} + +UnicodeString +PluralRules::select(double number) const { + return select(FixedDecimal(number)); +} + +UnicodeString +PluralRules::select(const number::FormattedNumber& number, UErrorCode& status) const { + DecimalQuantity dq; + number.getDecimalQuantity(dq, status); + if (U_FAILURE(status)) { + return ICU_Utility::makeBogusString(); + } + if (U_FAILURE(mInternalStatus)) { + status = mInternalStatus; + return ICU_Utility::makeBogusString(); + } + return select(dq); +} + +UnicodeString +PluralRules::select(const IFixedDecimal &number) const { + if (mRules == nullptr) { + return UnicodeString(true, PLURAL_DEFAULT_RULE, -1); + } + else { + return mRules->select(number); + } +} + +UnicodeString +PluralRules::select(const number::FormattedNumberRange& range, UErrorCode& status) const { + return select(range.getData(status), status); +} + +UnicodeString +PluralRules::select(const number::impl::UFormattedNumberRangeData* impl, UErrorCode& status) const { + if (U_FAILURE(status)) { + return ICU_Utility::makeBogusString(); + } + if (U_FAILURE(mInternalStatus)) { + status = mInternalStatus; + return ICU_Utility::makeBogusString(); + } + if (mStandardPluralRanges == nullptr) { + // Happens if PluralRules was constructed via createRules() + status = U_UNSUPPORTED_ERROR; + return ICU_Utility::makeBogusString(); + } + auto form1 = StandardPlural::fromString(select(impl->quantity1), status); + auto form2 = StandardPlural::fromString(select(impl->quantity2), status); + if (U_FAILURE(status)) { + return ICU_Utility::makeBogusString(); + } + auto result = mStandardPluralRanges->resolve(form1, form2); + return UnicodeString(StandardPlural::getKeyword(result), -1, US_INV); +} + + +StringEnumeration* +PluralRules::getKeywords(UErrorCode& status) const { + if (U_FAILURE(status)) { + return nullptr; + } + if (U_FAILURE(mInternalStatus)) { + status = mInternalStatus; + return nullptr; + } + LocalPointer nameEnumerator(new PluralKeywordEnumeration(mRules, status), status); + if (U_FAILURE(status)) { + return nullptr; + } + return nameEnumerator.orphan(); +} + +double +PluralRules::getUniqueKeywordValue(const UnicodeString& /* keyword */) { + // Not Implemented. + return UPLRULES_NO_UNIQUE_VALUE; +} + +int32_t +PluralRules::getAllKeywordValues(const UnicodeString & /* keyword */, double * /* dest */, + int32_t /* destCapacity */, UErrorCode& error) { + error = U_UNSUPPORTED_ERROR; + return 0; +} + +/** + * Helper method for the overrides of getSamples() for double and DecimalQuantity + * return value types. Provide only one of an allocated array of double or + * DecimalQuantity, and a nullptr for the other. + */ +static int32_t +getSamplesFromString(const UnicodeString &samples, double *destDbl, + DecimalQuantity* destDq, int32_t destCapacity, + UErrorCode& status) { + + if ((destDbl == nullptr && destDq == nullptr) + || (destDbl != nullptr && destDq != nullptr)) { + status = U_INTERNAL_PROGRAM_ERROR; + return 0; + } + + bool isDouble = destDbl != nullptr; + int32_t sampleCount = 0; + int32_t sampleStartIdx = 0; + int32_t sampleEndIdx = 0; + + //std::string ss; // TODO: debugging. + // std::cout << "PluralRules::getSamples(), samples = \"" << samples.toUTF8String(ss) << "\"\n"; + for (sampleCount = 0; sampleCount < destCapacity && sampleStartIdx < samples.length(); ) { + sampleEndIdx = samples.indexOf(COMMA, sampleStartIdx); + if (sampleEndIdx == -1) { + sampleEndIdx = samples.length(); + } + const UnicodeString &sampleRange = samples.tempSubStringBetween(sampleStartIdx, sampleEndIdx); + // ss.erase(); + // std::cout << "PluralRules::getSamples(), samplesRange = \"" << sampleRange.toUTF8String(ss) << "\"\n"; + int32_t tildeIndex = sampleRange.indexOf(TILDE); + if (tildeIndex < 0) { + DecimalQuantity dq = DecimalQuantity::fromExponentString(sampleRange, status); + if (isDouble) { + // See warning note below about lack of precision for floating point samples for numbers with + // trailing zeroes in the decimal fraction representation. + double dblValue = dq.toDouble(); + if (!(dblValue == floor(dblValue) && dq.fractionCount() > 0)) { + destDbl[sampleCount++] = dblValue; + } + } else { + destDq[sampleCount++] = dq; + } + } else { + DecimalQuantity rangeLo = + DecimalQuantity::fromExponentString(sampleRange.tempSubStringBetween(0, tildeIndex), status); + DecimalQuantity rangeHi = DecimalQuantity::fromExponentString(sampleRange.tempSubStringBetween(tildeIndex+1), status); + if (U_FAILURE(status)) { + break; + } + if (rangeHi.toDouble() < rangeLo.toDouble()) { + status = U_INVALID_FORMAT_ERROR; + break; + } + + DecimalQuantity incrementDq; + incrementDq.setToInt(1); + int32_t lowerDispMag = rangeLo.getLowerDisplayMagnitude(); + int32_t exponent = rangeLo.getExponent(); + int32_t incrementScale = lowerDispMag + exponent; + incrementDq.adjustMagnitude(incrementScale); + double incrementVal = incrementDq.toDouble(); // 10 ^ incrementScale + + + DecimalQuantity dq(rangeLo); + double dblValue = dq.toDouble(); + double end = rangeHi.toDouble(); + + while (dblValue <= end) { + if (isDouble) { + // Hack Alert: don't return any decimal samples with integer values that + // originated from a format with trailing decimals. + // This API is returning doubles, which can't distinguish having displayed + // zeros to the right of the decimal. + // This results in test failures with values mapping back to a different keyword. + if (!(dblValue == floor(dblValue) && dq.fractionCount() > 0)) { + destDbl[sampleCount++] = dblValue; + } + } else { + destDq[sampleCount++] = dq; + } + if (sampleCount >= destCapacity) { + break; + } + + // Increment dq for next iteration + + // Because DecNum and DecimalQuantity do not support + // add operations, we need to convert to/from double, + // despite precision lossiness for decimal fractions like 0.1. + dblValue += incrementVal; + DecNum newDqDecNum; + newDqDecNum.setTo(dblValue, status); + DecimalQuantity newDq; + newDq.setToDecNum(newDqDecNum, status); + newDq.setMinFraction(-lowerDispMag); + newDq.roundToMagnitude(lowerDispMag, RoundingMode::UNUM_ROUND_HALFEVEN, status); + newDq.adjustMagnitude(-exponent); + newDq.adjustExponent(exponent); + dblValue = newDq.toDouble(); + dq = newDq; + } + } + sampleStartIdx = sampleEndIdx + 1; + } + return sampleCount; +} + +int32_t +PluralRules::getSamples(const UnicodeString &keyword, double *dest, + int32_t destCapacity, UErrorCode& status) { + if (U_FAILURE(status)) { + return 0; + } + if (U_FAILURE(mInternalStatus)) { + status = mInternalStatus; + return 0; + } + if (dest != nullptr ? destCapacity < 0 : destCapacity != 0) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + RuleChain *rc = rulesForKeyword(keyword); + if (rc == nullptr) { + return 0; + } + int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, dest, nullptr, destCapacity, status); + if (numSamples == 0) { + numSamples = getSamplesFromString(rc->fDecimalSamples, dest, nullptr, destCapacity, status); + } + return numSamples; +} + +int32_t +PluralRules::getSamples(const UnicodeString &keyword, DecimalQuantity *dest, + int32_t destCapacity, UErrorCode& status) { + if (U_FAILURE(status)) { + return 0; + } + if (U_FAILURE(mInternalStatus)) { + status = mInternalStatus; + return 0; + } + if (dest != nullptr ? destCapacity < 0 : destCapacity != 0) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + RuleChain *rc = rulesForKeyword(keyword); + if (rc == nullptr) { + return 0; + } + + int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, nullptr, dest, destCapacity, status); + if (numSamples == 0) { + numSamples = getSamplesFromString(rc->fDecimalSamples, nullptr, dest, destCapacity, status); + } + return numSamples; +} + + +RuleChain *PluralRules::rulesForKeyword(const UnicodeString &keyword) const { + RuleChain *rc; + for (rc = mRules; rc != nullptr; rc = rc->fNext) { + if (rc->fKeyword == keyword) { + break; + } + } + return rc; +} + + +UBool +PluralRules::isKeyword(const UnicodeString& keyword) const { + if (0 == keyword.compare(PLURAL_KEYWORD_OTHER, 5)) { + return true; + } + return rulesForKeyword(keyword) != nullptr; +} + +UnicodeString +PluralRules::getKeywordOther() const { + return UnicodeString(true, PLURAL_KEYWORD_OTHER, 5); +} + +bool +PluralRules::operator==(const PluralRules& other) const { + const UnicodeString *ptrKeyword; + UErrorCode status= U_ZERO_ERROR; + + if ( this == &other ) { + return true; + } + LocalPointer myKeywordList(getKeywords(status)); + LocalPointer otherKeywordList(other.getKeywords(status)); + if (U_FAILURE(status)) { + return false; + } + + if (myKeywordList->count(status)!=otherKeywordList->count(status)) { + return false; + } + myKeywordList->reset(status); + while ((ptrKeyword=myKeywordList->snext(status))!=nullptr) { + if (!other.isKeyword(*ptrKeyword)) { + return false; + } + } + otherKeywordList->reset(status); + while ((ptrKeyword=otherKeywordList->snext(status))!=nullptr) { + if (!this->isKeyword(*ptrKeyword)) { + return false; + } + } + if (U_FAILURE(status)) { + return false; + } + + return true; +} + + +void +PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErrorCode &status) +{ + if (U_FAILURE(status)) { + return; + } + U_ASSERT(ruleIndex == 0); // Parsers are good for a single use only! + ruleSrc = &ruleData; + + while (ruleIndex< ruleSrc->length()) { + getNextToken(status); + if (U_FAILURE(status)) { + return; + } + checkSyntax(status); + if (U_FAILURE(status)) { + return; + } + switch (type) { + case tAnd: + U_ASSERT(curAndConstraint != nullptr); + curAndConstraint = curAndConstraint->add(status); + break; + case tOr: + { + U_ASSERT(currentChain != nullptr); + OrConstraint *orNode=currentChain->ruleHeader; + while (orNode->next != nullptr) { + orNode = orNode->next; + } + orNode->next= new OrConstraint(); + if (orNode->next == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + break; + } + orNode=orNode->next; + orNode->next=nullptr; + curAndConstraint = orNode->add(status); + } + break; + case tIs: + U_ASSERT(curAndConstraint != nullptr); + U_ASSERT(curAndConstraint->value == -1); + U_ASSERT(curAndConstraint->rangeList == nullptr); + break; + case tNot: + U_ASSERT(curAndConstraint != nullptr); + curAndConstraint->negated=true; + break; + + case tNotEqual: + curAndConstraint->negated=true; + U_FALLTHROUGH; + case tIn: + case tWithin: + case tEqual: + { + U_ASSERT(curAndConstraint != nullptr); + LocalPointer newRangeList(new UVector32(status), status); + if (U_FAILURE(status)) { + break; + } + curAndConstraint->rangeList = newRangeList.orphan(); + curAndConstraint->rangeList->addElement(-1, status); // range Low + curAndConstraint->rangeList->addElement(-1, status); // range Hi + rangeLowIdx = 0; + rangeHiIdx = 1; + curAndConstraint->value=PLURAL_RANGE_HIGH; + curAndConstraint->integerOnly = (type != tWithin); + } + break; + case tNumber: + U_ASSERT(curAndConstraint != nullptr); + if ( (curAndConstraint->op==AndConstraint::MOD)&& + (curAndConstraint->opNum == -1 ) ) { + curAndConstraint->opNum=getNumberValue(token); + } + else { + if (curAndConstraint->rangeList == nullptr) { + // this is for an 'is' rule + curAndConstraint->value = getNumberValue(token); + } else { + // this is for an 'in' or 'within' rule + if (curAndConstraint->rangeList->elementAti(rangeLowIdx) == -1) { + curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeLowIdx); + curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeHiIdx); + } + else { + curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeHiIdx); + if (curAndConstraint->rangeList->elementAti(rangeLowIdx) > + curAndConstraint->rangeList->elementAti(rangeHiIdx)) { + // Range Lower bound > Range Upper bound. + // U_UNEXPECTED_TOKEN seems a little funny, but it is consistently + // used for all plural rule parse errors. + status = U_UNEXPECTED_TOKEN; + break; + } + } + } + } + break; + case tComma: + // TODO: rule syntax checking is inadequate, can happen with badly formed rules. + // Catch cases like "n mod 10, is 1" here instead. + if (curAndConstraint == nullptr || curAndConstraint->rangeList == nullptr) { + status = U_UNEXPECTED_TOKEN; + break; + } + U_ASSERT(curAndConstraint->rangeList->size() >= 2); + rangeLowIdx = curAndConstraint->rangeList->size(); + curAndConstraint->rangeList->addElement(-1, status); // range Low + rangeHiIdx = curAndConstraint->rangeList->size(); + curAndConstraint->rangeList->addElement(-1, status); // range Hi + break; + case tMod: + U_ASSERT(curAndConstraint != nullptr); + curAndConstraint->op=AndConstraint::MOD; + break; + case tVariableN: + case tVariableI: + case tVariableF: + case tVariableT: + case tVariableE: + case tVariableC: + case tVariableV: + U_ASSERT(curAndConstraint != nullptr); + curAndConstraint->digitsType = type; + break; + case tKeyword: + { + RuleChain *newChain = new RuleChain; + if (newChain == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + break; + } + newChain->fKeyword = token; + if (prules->mRules == nullptr) { + prules->mRules = newChain; + } else { + // The new rule chain goes at the end of the linked list of rule chains, + // unless there is an "other" keyword & chain. "other" must remain last. + RuleChain *insertAfter = prules->mRules; + while (insertAfter->fNext!=nullptr && + insertAfter->fNext->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5) != 0 ){ + insertAfter=insertAfter->fNext; + } + newChain->fNext = insertAfter->fNext; + insertAfter->fNext = newChain; + } + OrConstraint *orNode = new OrConstraint(); + if (orNode == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + break; + } + newChain->ruleHeader = orNode; + curAndConstraint = orNode->add(status); + currentChain = newChain; + } + break; + + case tInteger: + for (;;) { + getNextToken(status); + if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) { + break; + } + if (type == tEllipsis) { + currentChain->fIntegerSamplesUnbounded = true; + continue; + } + currentChain->fIntegerSamples.append(token); + } + break; + + case tDecimal: + for (;;) { + getNextToken(status); + if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) { + break; + } + if (type == tEllipsis) { + currentChain->fDecimalSamplesUnbounded = true; + continue; + } + currentChain->fDecimalSamples.append(token); + } + break; + + default: + break; + } + prevType=type; + if (U_FAILURE(status)) { + break; + } + } +} + +UnicodeString +PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorCode& errCode) { + UnicodeString emptyStr; + + if (U_FAILURE(errCode)) { + return emptyStr; + } + LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "plurals", &errCode)); + if(U_FAILURE(errCode)) { + return emptyStr; + } + const char *typeKey; + switch (type) { + case UPLURAL_TYPE_CARDINAL: + typeKey = "locales"; + break; + case UPLURAL_TYPE_ORDINAL: + typeKey = "locales_ordinals"; + break; + default: + // Must not occur: The caller should have checked for valid types. + errCode = U_ILLEGAL_ARGUMENT_ERROR; + return emptyStr; + } + LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), typeKey, nullptr, &errCode)); + if(U_FAILURE(errCode)) { + return emptyStr; + } + int32_t resLen=0; + const char *curLocaleName=locale.getBaseName(); + const char16_t* s = ures_getStringByKey(locRes.getAlias(), curLocaleName, &resLen, &errCode); + + if (s == nullptr) { + // Check parent locales. + UErrorCode status = U_ZERO_ERROR; + char parentLocaleName[ULOC_FULLNAME_CAPACITY]; + const char *curLocaleName2=locale.getBaseName(); + uprv_strcpy(parentLocaleName, curLocaleName2); + + while (uloc_getParent(parentLocaleName, parentLocaleName, + ULOC_FULLNAME_CAPACITY, &status) > 0) { + resLen=0; + s = ures_getStringByKey(locRes.getAlias(), parentLocaleName, &resLen, &status); + if (s != nullptr) { + errCode = U_ZERO_ERROR; + break; + } + status = U_ZERO_ERROR; + } + } + if (s==nullptr) { + return emptyStr; + } + + char setKey[256]; + u_UCharsToChars(s, setKey, resLen + 1); + // printf("\n PluralRule: %s\n", setKey); + + LocalUResourceBundlePointer ruleRes(ures_getByKey(rb.getAlias(), "rules", nullptr, &errCode)); + if(U_FAILURE(errCode)) { + return emptyStr; + } + LocalUResourceBundlePointer setRes(ures_getByKey(ruleRes.getAlias(), setKey, nullptr, &errCode)); + if (U_FAILURE(errCode)) { + return emptyStr; + } + + int32_t numberKeys = ures_getSize(setRes.getAlias()); + UnicodeString result; + const char *key=nullptr; + for(int32_t i=0; idumpRules(rules); + } + return rules; +} + +AndConstraint::AndConstraint(const AndConstraint& other) { + this->fInternalStatus = other.fInternalStatus; + if (U_FAILURE(fInternalStatus)) { + return; // stop early if the object we are copying from is invalid. + } + this->op = other.op; + this->opNum=other.opNum; + this->value=other.value; + if (other.rangeList != nullptr) { + LocalPointer newRangeList(new UVector32(fInternalStatus), fInternalStatus); + if (U_FAILURE(fInternalStatus)) { + return; + } + this->rangeList = newRangeList.orphan(); + this->rangeList->assign(*other.rangeList, fInternalStatus); + } + this->integerOnly=other.integerOnly; + this->negated=other.negated; + this->digitsType = other.digitsType; + if (other.next != nullptr) { + this->next = new AndConstraint(*other.next); + if (this->next == nullptr) { + fInternalStatus = U_MEMORY_ALLOCATION_ERROR; + } + } +} + +AndConstraint::~AndConstraint() { + delete rangeList; + rangeList = nullptr; + delete next; + next = nullptr; +} + +UBool +AndConstraint::isFulfilled(const IFixedDecimal &number) { + UBool result = true; + if (digitsType == none) { + // An empty AndConstraint, created by a rule with a keyword but no following expression. + return true; + } + + PluralOperand operand = tokenTypeToPluralOperand(digitsType); + double n = number.getPluralOperand(operand); // pulls n | i | v | f value for the number. + // Will always be positive. + // May be non-integer (n option only) + do { + if (integerOnly && n != uprv_floor(n)) { + result = false; + break; + } + + if (op == MOD) { + n = fmod(n, opNum); + } + if (rangeList == nullptr) { + result = value == -1 || // empty rule + n == value; // 'is' rule + break; + } + result = false; // 'in' or 'within' rule + for (int32_t r=0; rsize(); r+=2) { + if (rangeList->elementAti(r) <= n && n <= rangeList->elementAti(r+1)) { + result = true; + break; + } + } + } while (false); + + if (negated) { + result = !result; + } + return result; +} + +AndConstraint* +AndConstraint::add(UErrorCode& status) { + if (U_FAILURE(fInternalStatus)) { + status = fInternalStatus; + return nullptr; + } + this->next = new AndConstraint(); + if (this->next == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return this->next; +} + + +OrConstraint::OrConstraint(const OrConstraint& other) { + this->fInternalStatus = other.fInternalStatus; + if (U_FAILURE(fInternalStatus)) { + return; // stop early if the object we are copying from is invalid. + } + if ( other.childNode != nullptr ) { + this->childNode = new AndConstraint(*(other.childNode)); + if (this->childNode == nullptr) { + fInternalStatus = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + if (other.next != nullptr ) { + this->next = new OrConstraint(*(other.next)); + if (this->next == nullptr) { + fInternalStatus = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (U_FAILURE(this->next->fInternalStatus)) { + this->fInternalStatus = this->next->fInternalStatus; + } + } +} + +OrConstraint::~OrConstraint() { + delete childNode; + childNode = nullptr; + delete next; + next = nullptr; +} + +AndConstraint* +OrConstraint::add(UErrorCode& status) { + if (U_FAILURE(fInternalStatus)) { + status = fInternalStatus; + return nullptr; + } + OrConstraint *curOrConstraint=this; + { + while (curOrConstraint->next!=nullptr) { + curOrConstraint = curOrConstraint->next; + } + U_ASSERT(curOrConstraint->childNode == nullptr); + curOrConstraint->childNode = new AndConstraint(); + if (curOrConstraint->childNode == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + } + return curOrConstraint->childNode; +} + +UBool +OrConstraint::isFulfilled(const IFixedDecimal &number) { + OrConstraint* orRule=this; + UBool result=false; + + while (orRule!=nullptr && !result) { + result=true; + AndConstraint* andRule = orRule->childNode; + while (andRule!=nullptr && result) { + result = andRule->isFulfilled(number); + andRule=andRule->next; + } + orRule = orRule->next; + } + + return result; +} + + +RuleChain::RuleChain(const RuleChain& other) : + fKeyword(other.fKeyword), fDecimalSamples(other.fDecimalSamples), + fIntegerSamples(other.fIntegerSamples), fDecimalSamplesUnbounded(other.fDecimalSamplesUnbounded), + fIntegerSamplesUnbounded(other.fIntegerSamplesUnbounded), fInternalStatus(other.fInternalStatus) { + if (U_FAILURE(this->fInternalStatus)) { + return; // stop early if the object we are copying from is invalid. + } + if (other.ruleHeader != nullptr) { + this->ruleHeader = new OrConstraint(*(other.ruleHeader)); + if (this->ruleHeader == nullptr) { + this->fInternalStatus = U_MEMORY_ALLOCATION_ERROR; + } + else if (U_FAILURE(this->ruleHeader->fInternalStatus)) { + // If the OrConstraint wasn't fully copied, then set our status to failure as well. + this->fInternalStatus = this->ruleHeader->fInternalStatus; + return; // exit early. + } + } + if (other.fNext != nullptr ) { + this->fNext = new RuleChain(*other.fNext); + if (this->fNext == nullptr) { + this->fInternalStatus = U_MEMORY_ALLOCATION_ERROR; + } + else if (U_FAILURE(this->fNext->fInternalStatus)) { + // If the RuleChain wasn't fully copied, then set our status to failure as well. + this->fInternalStatus = this->fNext->fInternalStatus; + } + } +} + +RuleChain::~RuleChain() { + delete fNext; + delete ruleHeader; +} + +UnicodeString +RuleChain::select(const IFixedDecimal &number) const { + if (!number.isNaN() && !number.isInfinite()) { + for (const RuleChain *rules = this; rules != nullptr; rules = rules->fNext) { + if (rules->ruleHeader->isFulfilled(number)) { + return rules->fKeyword; + } + } + } + return UnicodeString(true, PLURAL_KEYWORD_OTHER, 5); +} + +static UnicodeString tokenString(tokenType tok) { + UnicodeString s; + switch (tok) { + case tVariableN: + s.append(LOW_N); break; + case tVariableI: + s.append(LOW_I); break; + case tVariableF: + s.append(LOW_F); break; + case tVariableV: + s.append(LOW_V); break; + case tVariableT: + s.append(LOW_T); break; + case tVariableE: + s.append(LOW_E); break; + case tVariableC: + s.append(LOW_C); break; + default: + s.append(TILDE); + } + return s; +} + +void +RuleChain::dumpRules(UnicodeString& result) { + char16_t digitString[16]; + + if ( ruleHeader != nullptr ) { + result += fKeyword; + result += COLON; + result += SPACE; + OrConstraint* orRule=ruleHeader; + while ( orRule != nullptr ) { + AndConstraint* andRule=orRule->childNode; + while ( andRule != nullptr ) { + if ((andRule->op==AndConstraint::NONE) && (andRule->rangeList==nullptr) && (andRule->value == -1)) { + // Empty Rules. + } else if ( (andRule->op==AndConstraint::NONE) && (andRule->rangeList==nullptr) ) { + result += tokenString(andRule->digitsType); + result += UNICODE_STRING_SIMPLE(" is "); + if (andRule->negated) { + result += UNICODE_STRING_SIMPLE("not "); + } + uprv_itou(digitString,16, andRule->value,10,0); + result += UnicodeString(digitString); + } + else { + result += tokenString(andRule->digitsType); + result += SPACE; + if (andRule->op==AndConstraint::MOD) { + result += UNICODE_STRING_SIMPLE("mod "); + uprv_itou(digitString,16, andRule->opNum,10,0); + result += UnicodeString(digitString); + } + if (andRule->rangeList==nullptr) { + if (andRule->negated) { + result += UNICODE_STRING_SIMPLE(" is not "); + uprv_itou(digitString,16, andRule->value,10,0); + result += UnicodeString(digitString); + } + else { + result += UNICODE_STRING_SIMPLE(" is "); + uprv_itou(digitString,16, andRule->value,10,0); + result += UnicodeString(digitString); + } + } + else { + if (andRule->negated) { + if ( andRule->integerOnly ) { + result += UNICODE_STRING_SIMPLE(" not in "); + } + else { + result += UNICODE_STRING_SIMPLE(" not within "); + } + } + else { + if ( andRule->integerOnly ) { + result += UNICODE_STRING_SIMPLE(" in "); + } + else { + result += UNICODE_STRING_SIMPLE(" within "); + } + } + for (int32_t r=0; rrangeList->size(); r+=2) { + int32_t rangeLo = andRule->rangeList->elementAti(r); + int32_t rangeHi = andRule->rangeList->elementAti(r+1); + uprv_itou(digitString,16, rangeLo, 10, 0); + result += UnicodeString(digitString); + result += UNICODE_STRING_SIMPLE(".."); + uprv_itou(digitString,16, rangeHi, 10,0); + result += UnicodeString(digitString); + if (r+2 < andRule->rangeList->size()) { + result += UNICODE_STRING_SIMPLE(", "); + } + } + } + } + if ( (andRule=andRule->next) != nullptr) { + result += UNICODE_STRING_SIMPLE(" and "); + } + } + if ( (orRule = orRule->next) != nullptr ) { + result += UNICODE_STRING_SIMPLE(" or "); + } + } + } + if ( fNext != nullptr ) { + result += UNICODE_STRING_SIMPLE("; "); + fNext->dumpRules(result); + } +} + + +UErrorCode +RuleChain::getKeywords(int32_t capacityOfKeywords, UnicodeString* keywords, int32_t& arraySize) const { + if (U_FAILURE(fInternalStatus)) { + return fInternalStatus; + } + if ( arraySize < capacityOfKeywords-1 ) { + keywords[arraySize++]=fKeyword; + } + else { + return U_BUFFER_OVERFLOW_ERROR; + } + + if ( fNext != nullptr ) { + return fNext->getKeywords(capacityOfKeywords, keywords, arraySize); + } + else { + return U_ZERO_ERROR; + } +} + +UBool +RuleChain::isKeyword(const UnicodeString& keywordParam) const { + if ( fKeyword == keywordParam ) { + return true; + } + + if ( fNext != nullptr ) { + return fNext->isKeyword(keywordParam); + } + else { + return false; + } +} + + +PluralRuleParser::PluralRuleParser() : + ruleIndex(0), token(), type(none), prevType(none), + curAndConstraint(nullptr), currentChain(nullptr), rangeLowIdx(-1), rangeHiIdx(-1) +{ +} + +PluralRuleParser::~PluralRuleParser() { +} + + +int32_t +PluralRuleParser::getNumberValue(const UnicodeString& token) { + int32_t i; + char digits[128]; + + i = token.extract(0, token.length(), digits, UPRV_LENGTHOF(digits), US_INV); + digits[i]='\0'; + + return((int32_t)atoi(digits)); +} + + +void +PluralRuleParser::checkSyntax(UErrorCode &status) +{ + if (U_FAILURE(status)) { + return; + } + if (!(prevType==none || prevType==tSemiColon)) { + type = getKeyType(token, type); // Switch token type from tKeyword if we scanned a reserved word, + // and we are not at the start of a rule, where a + // keyword is expected. + } + + switch(prevType) { + case none: + case tSemiColon: + if (type!=tKeyword && type != tEOF) { + status = U_UNEXPECTED_TOKEN; + } + break; + case tVariableN: + case tVariableI: + case tVariableF: + case tVariableT: + case tVariableE: + case tVariableC: + case tVariableV: + if (type != tIs && type != tMod && type != tIn && + type != tNot && type != tWithin && type != tEqual && type != tNotEqual) { + status = U_UNEXPECTED_TOKEN; + } + break; + case tKeyword: + if (type != tColon) { + status = U_UNEXPECTED_TOKEN; + } + break; + case tColon: + if (!(type == tVariableN || + type == tVariableI || + type == tVariableF || + type == tVariableT || + type == tVariableE || + type == tVariableC || + type == tVariableV || + type == tAt)) { + status = U_UNEXPECTED_TOKEN; + } + break; + case tIs: + if ( type != tNumber && type != tNot) { + status = U_UNEXPECTED_TOKEN; + } + break; + case tNot: + if (type != tNumber && type != tIn && type != tWithin) { + status = U_UNEXPECTED_TOKEN; + } + break; + case tMod: + case tDot2: + case tIn: + case tWithin: + case tEqual: + case tNotEqual: + if (type != tNumber) { + status = U_UNEXPECTED_TOKEN; + } + break; + case tAnd: + case tOr: + if ( type != tVariableN && + type != tVariableI && + type != tVariableF && + type != tVariableT && + type != tVariableE && + type != tVariableC && + type != tVariableV) { + status = U_UNEXPECTED_TOKEN; + } + break; + case tComma: + if (type != tNumber) { + status = U_UNEXPECTED_TOKEN; + } + break; + case tNumber: + if (type != tDot2 && type != tSemiColon && type != tIs && type != tNot && + type != tIn && type != tEqual && type != tNotEqual && type != tWithin && + type != tAnd && type != tOr && type != tComma && type != tAt && + type != tEOF) + { + status = U_UNEXPECTED_TOKEN; + } + // TODO: a comma following a number that is not part of a range will be allowed. + // It's not the only case of this sort of thing. Parser needs a re-write. + break; + case tAt: + if (type != tDecimal && type != tInteger) { + status = U_UNEXPECTED_TOKEN; + } + break; + default: + status = U_UNEXPECTED_TOKEN; + break; + } +} + + +/* + * Scan the next token from the input rules. + * rules and returned token type are in the parser state variables. + */ +void +PluralRuleParser::getNextToken(UErrorCode &status) +{ + if (U_FAILURE(status)) { + return; + } + + char16_t ch; + while (ruleIndex < ruleSrc->length()) { + ch = ruleSrc->charAt(ruleIndex); + type = charType(ch); + if (type != tSpace) { + break; + } + ++(ruleIndex); + } + if (ruleIndex >= ruleSrc->length()) { + type = tEOF; + return; + } + int32_t curIndex= ruleIndex; + + switch (type) { + case tColon: + case tSemiColon: + case tComma: + case tEllipsis: + case tTilde: // scanned '~' + case tAt: // scanned '@' + case tEqual: // scanned '=' + case tMod: // scanned '%' + // Single character tokens. + ++curIndex; + break; + + case tNotEqual: // scanned '!' + if (ruleSrc->charAt(curIndex+1) == EQUALS) { + curIndex += 2; + } else { + type = none; + curIndex += 1; + } + break; + + case tKeyword: + while (type == tKeyword && ++curIndex < ruleSrc->length()) { + ch = ruleSrc->charAt(curIndex); + type = charType(ch); + } + type = tKeyword; + break; + + case tNumber: + while (type == tNumber && ++curIndex < ruleSrc->length()) { + ch = ruleSrc->charAt(curIndex); + type = charType(ch); + } + type = tNumber; + break; + + case tDot: + // We could be looking at either ".." in a range, or "..." at the end of a sample. + if (curIndex+1 >= ruleSrc->length() || ruleSrc->charAt(curIndex+1) != DOT) { + ++curIndex; + break; // Single dot + } + if (curIndex+2 >= ruleSrc->length() || ruleSrc->charAt(curIndex+2) != DOT) { + curIndex += 2; + type = tDot2; + break; // double dot + } + type = tEllipsis; + curIndex += 3; + break; // triple dot + + default: + status = U_UNEXPECTED_TOKEN; + ++curIndex; + break; + } + + U_ASSERT(ruleIndex <= ruleSrc->length()); + U_ASSERT(curIndex <= ruleSrc->length()); + token=UnicodeString(*ruleSrc, ruleIndex, curIndex-ruleIndex); + ruleIndex = curIndex; +} + +tokenType +PluralRuleParser::charType(char16_t ch) { + if ((ch>=U_ZERO) && (ch<=U_NINE)) { + return tNumber; + } + if (ch>=LOW_A && ch<=LOW_Z) { + return tKeyword; + } + switch (ch) { + case COLON: + return tColon; + case SPACE: + return tSpace; + case SEMI_COLON: + return tSemiColon; + case DOT: + return tDot; + case COMMA: + return tComma; + case EXCLAMATION: + return tNotEqual; + case EQUALS: + return tEqual; + case PERCENT_SIGN: + return tMod; + case AT: + return tAt; + case ELLIPSIS: + return tEllipsis; + case TILDE: + return tTilde; + default : + return none; + } +} + + +// Set token type for reserved words in the Plural Rule syntax. + +tokenType +PluralRuleParser::getKeyType(const UnicodeString &token, tokenType keyType) +{ + if (keyType != tKeyword) { + return keyType; + } + + if (0 == token.compare(PK_VAR_N, 1)) { + keyType = tVariableN; + } else if (0 == token.compare(PK_VAR_I, 1)) { + keyType = tVariableI; + } else if (0 == token.compare(PK_VAR_F, 1)) { + keyType = tVariableF; + } else if (0 == token.compare(PK_VAR_T, 1)) { + keyType = tVariableT; + } else if (0 == token.compare(PK_VAR_E, 1)) { + keyType = tVariableE; + } else if (0 == token.compare(PK_VAR_C, 1)) { + keyType = tVariableC; + } else if (0 == token.compare(PK_VAR_V, 1)) { + keyType = tVariableV; + } else if (0 == token.compare(PK_IS, 2)) { + keyType = tIs; + } else if (0 == token.compare(PK_AND, 3)) { + keyType = tAnd; + } else if (0 == token.compare(PK_IN, 2)) { + keyType = tIn; + } else if (0 == token.compare(PK_WITHIN, 6)) { + keyType = tWithin; + } else if (0 == token.compare(PK_NOT, 3)) { + keyType = tNot; + } else if (0 == token.compare(PK_MOD, 3)) { + keyType = tMod; + } else if (0 == token.compare(PK_OR, 2)) { + keyType = tOr; + } else if (0 == token.compare(PK_DECIMAL, 7)) { + keyType = tDecimal; + } else if (0 == token.compare(PK_INTEGER, 7)) { + keyType = tInteger; + } + return keyType; +} + + +PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode& status) + : pos(0), fKeywordNames(status) { + if (U_FAILURE(status)) { + return; + } + fKeywordNames.setDeleter(uprv_deleteUObject); + UBool addKeywordOther = true; + RuleChain *node = header; + while (node != nullptr) { + LocalPointer newElem(node->fKeyword.clone(), status); + fKeywordNames.adoptElement(newElem.orphan(), status); + if (U_FAILURE(status)) { + return; + } + if (0 == node->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5)) { + addKeywordOther = false; + } + node = node->fNext; + } + + if (addKeywordOther) { + LocalPointer newElem(new UnicodeString(PLURAL_KEYWORD_OTHER), status); + fKeywordNames.adoptElement(newElem.orphan(), status); + if (U_FAILURE(status)) { + return; + } + } +} + +const UnicodeString* +PluralKeywordEnumeration::snext(UErrorCode& status) { + if (U_SUCCESS(status) && pos < fKeywordNames.size()) { + return (const UnicodeString*)fKeywordNames.elementAt(pos++); + } + return nullptr; +} + +void +PluralKeywordEnumeration::reset(UErrorCode& /*status*/) { + pos=0; +} + +int32_t +PluralKeywordEnumeration::count(UErrorCode& /*status*/) const { + return fKeywordNames.size(); +} + +PluralKeywordEnumeration::~PluralKeywordEnumeration() { +} + +PluralOperand tokenTypeToPluralOperand(tokenType tt) { + switch(tt) { + case tVariableN: + return PLURAL_OPERAND_N; + case tVariableI: + return PLURAL_OPERAND_I; + case tVariableF: + return PLURAL_OPERAND_F; + case tVariableV: + return PLURAL_OPERAND_V; + case tVariableT: + return PLURAL_OPERAND_T; + case tVariableE: + return PLURAL_OPERAND_E; + case tVariableC: + return PLURAL_OPERAND_E; + default: + UPRV_UNREACHABLE_EXIT; // unexpected. + } +} + +FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f, int32_t e, int32_t c) { + init(n, v, f, e, c); +} + +FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f, int32_t e) { + init(n, v, f, e); + // check values. TODO make into unit test. + // + // long visiblePower = (int) Math.pow(10.0, v); + // if (decimalDigits > visiblePower) { + // throw new IllegalArgumentException(); + // } + // double fraction = intValue + (decimalDigits / (double) visiblePower); + // if (fraction != source) { + // double diff = Math.abs(fraction - source)/(Math.abs(fraction) + Math.abs(source)); + // if (diff > 0.00000001d) { + // throw new IllegalArgumentException(); + // } + // } +} + +FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f) { + init(n, v, f); +} + +FixedDecimal::FixedDecimal(double n, int32_t v) { + // Ugly, but for samples we don't care. + init(n, v, getFractionalDigits(n, v)); +} + +FixedDecimal::FixedDecimal(double n) { + init(n); +} + +FixedDecimal::FixedDecimal() { + init(0, 0, 0); +} + + +// Create a FixedDecimal from a UnicodeString containing a number. +// Inefficient, but only used for samples, so simplicity trumps efficiency. + +FixedDecimal::FixedDecimal(const UnicodeString &num, UErrorCode &status) { + CharString cs; + int32_t parsedExponent = 0; + int32_t parsedCompactExponent = 0; + + int32_t exponentIdx = num.indexOf(u'e'); + if (exponentIdx < 0) { + exponentIdx = num.indexOf(u'E'); + } + int32_t compactExponentIdx = num.indexOf(u'c'); + if (compactExponentIdx < 0) { + compactExponentIdx = num.indexOf(u'C'); + } + + if (exponentIdx >= 0) { + cs.appendInvariantChars(num.tempSubString(0, exponentIdx), status); + int32_t expSubstrStart = exponentIdx + 1; + parsedExponent = ICU_Utility::parseAsciiInteger(num, expSubstrStart); + } + else if (compactExponentIdx >= 0) { + cs.appendInvariantChars(num.tempSubString(0, compactExponentIdx), status); + int32_t expSubstrStart = compactExponentIdx + 1; + parsedCompactExponent = ICU_Utility::parseAsciiInteger(num, expSubstrStart); + + parsedExponent = parsedCompactExponent; + exponentIdx = compactExponentIdx; + } + else { + cs.appendInvariantChars(num, 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.toDouble(); + if (decimalPoint == -1) { + init(n, 0, 0, parsedExponent); + } else { + int32_t fractionNumLength = exponentIdx < 0 ? num.length() : cs.length(); + int32_t v = fractionNumLength - decimalPoint - 1; + init(n, v, getFractionalDigits(n, v), parsedExponent); + } +} + + +FixedDecimal::FixedDecimal(const FixedDecimal &other) { + source = other.source; + visibleDecimalDigitCount = other.visibleDecimalDigitCount; + decimalDigits = other.decimalDigits; + decimalDigitsWithoutTrailingZeros = other.decimalDigitsWithoutTrailingZeros; + intValue = other.intValue; + exponent = other.exponent; + _hasIntegerValue = other._hasIntegerValue; + isNegative = other.isNegative; + _isNaN = other._isNaN; + _isInfinite = other._isInfinite; +} + +FixedDecimal::~FixedDecimal() = default; + +FixedDecimal FixedDecimal::createWithExponent(double n, int32_t v, int32_t e) { + return FixedDecimal(n, v, getFractionalDigits(n, v), e); +} + + +void FixedDecimal::init(double n) { + int32_t numFractionDigits = decimals(n); + init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits)); +} + + +void FixedDecimal::init(double n, int32_t v, int64_t f) { + int32_t exponent = 0; + init(n, v, f, exponent); +} + +void FixedDecimal::init(double n, int32_t v, int64_t f, int32_t e) { + // Currently, `c` is an alias for `e` + init(n, v, f, e, e); +} + +void FixedDecimal::init(double n, int32_t v, int64_t f, int32_t e, int32_t c) { + isNegative = n < 0.0; + source = fabs(n); + _isNaN = uprv_isNaN(source); + _isInfinite = uprv_isInfinite(source); + exponent = e; + if (exponent == 0) { + exponent = c; + } + if (_isNaN || _isInfinite) { + v = 0; + f = 0; + intValue = 0; + _hasIntegerValue = false; + } else { + intValue = (int64_t)source; + _hasIntegerValue = (source == intValue); + } + + visibleDecimalDigitCount = v; + decimalDigits = f; + if (f == 0) { + decimalDigitsWithoutTrailingZeros = 0; + } else { + int64_t fdwtz = f; + while ((fdwtz%10) == 0) { + fdwtz /= 10; + } + decimalDigitsWithoutTrailingZeros = fdwtz; + } +} + + +// Fast path only exact initialization. Return true if successful. +// Note: Do not multiply by 10 each time through loop, rounding cruft can build +// up that makes the check for an integer result fail. +// A single multiply of the original number works more reliably. +static int32_t p10[] = {1, 10, 100, 1000, 10000}; +UBool FixedDecimal::quickInit(double n) { + UBool success = false; + n = fabs(n); + int32_t numFractionDigits; + for (numFractionDigits = 0; numFractionDigits <= 3; numFractionDigits++) { + double scaledN = n * p10[numFractionDigits]; + if (scaledN == floor(scaledN)) { + success = true; + break; + } + } + if (success) { + init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits)); + } + return success; +} + + + +int32_t FixedDecimal::decimals(double n) { + // Count the number of decimal digits in the fraction part of the number, excluding trailing zeros. + // fastpath the common cases, integers or fractions with 3 or fewer digits + n = fabs(n); + for (int ndigits=0; ndigits<=3; ndigits++) { + double scaledN = n * p10[ndigits]; + if (scaledN == floor(scaledN)) { + return ndigits; + } + } + + // Slow path, convert with snprintf, parse converted output. + char buf[30] = {0}; + snprintf(buf, sizeof(buf), "%1.15e", n); + // formatted number looks like this: 1.234567890123457e-01 + int exponent = atoi(buf+18); + int numFractionDigits = 15; + for (int i=16; ; --i) { + if (buf[i] != '0') { + break; + } + --numFractionDigits; + } + numFractionDigits -= exponent; // Fraction part of fixed point representation. + return numFractionDigits; +} + + +// Get the fraction digits of a double, represented as an integer. +// v is the number of visible fraction digits in the displayed form of the number. +// Example: n = 1001.234, v = 6, result = 234000 +// TODO: need to think through how this is used in the plural rule context. +// This function can easily encounter integer overflow, +// and can easily return noise digits when the precision of a double is exceeded. + +int64_t FixedDecimal::getFractionalDigits(double n, int32_t v) { + if (v == 0 || n == floor(n) || uprv_isNaN(n) || uprv_isPositiveInfinity(n)) { + return 0; + } + n = fabs(n); + double fract = n - floor(n); + switch (v) { + case 1: return (int64_t)(fract*10.0 + 0.5); + case 2: return (int64_t)(fract*100.0 + 0.5); + case 3: return (int64_t)(fract*1000.0 + 0.5); + default: + double scaled = floor(fract * pow(10.0, (double)v) + 0.5); + if (scaled >= static_cast(U_INT64_MAX)) { + // Note: a double cannot accurately represent U_INT64_MAX. Casting it to double + // will round up to the next representable value, which is U_INT64_MAX + 1. + return U_INT64_MAX; + } else { + return (int64_t)scaled; + } + } +} + + +void FixedDecimal::adjustForMinFractionDigits(int32_t minFractionDigits) { + int32_t numTrailingFractionZeros = minFractionDigits - visibleDecimalDigitCount; + if (numTrailingFractionZeros > 0) { + for (int32_t i=0; i= 100000000000000000LL) { + break; + } + decimalDigits *= 10; + } + visibleDecimalDigitCount += numTrailingFractionZeros; + } +} + + +double FixedDecimal::getPluralOperand(PluralOperand operand) const { + switch(operand) { + case PLURAL_OPERAND_N: return (exponent == 0 ? source : source * pow(10.0, exponent)); + case PLURAL_OPERAND_I: return (double) longValue(); + case PLURAL_OPERAND_F: return static_cast(decimalDigits); + case PLURAL_OPERAND_T: return static_cast(decimalDigitsWithoutTrailingZeros); + case PLURAL_OPERAND_V: return visibleDecimalDigitCount; + case PLURAL_OPERAND_E: return exponent; + case PLURAL_OPERAND_C: return exponent; + default: + UPRV_UNREACHABLE_EXIT; // unexpected. + } +} + +bool FixedDecimal::isNaN() const { + return _isNaN; +} + +bool FixedDecimal::isInfinite() const { + return _isInfinite; +} + +bool FixedDecimal::hasIntegerValue() const { + return _hasIntegerValue; +} + +bool FixedDecimal::isNanOrInfinity() const { + return _isNaN || _isInfinite; +} + +int32_t FixedDecimal::getVisibleFractionDigitCount() const { + return visibleDecimalDigitCount; +} + +bool FixedDecimal::operator==(const FixedDecimal &other) const { + return source == other.source && visibleDecimalDigitCount == other.visibleDecimalDigitCount + && decimalDigits == other.decimalDigits && exponent == other.exponent; +} + +UnicodeString FixedDecimal::toString() const { + char pattern[15]; + char buffer[20]; + if (exponent != 0) { + snprintf(pattern, sizeof(pattern), "%%.%dfe%%d", visibleDecimalDigitCount); + snprintf(buffer, sizeof(buffer), pattern, source, exponent); + } else { + snprintf(pattern, sizeof(pattern), "%%.%df", visibleDecimalDigitCount); + snprintf(buffer, sizeof(buffer), pattern, source); + } + return UnicodeString(buffer, -1, US_INV); +} + +double FixedDecimal::doubleValue() const { + return (isNegative ? -source : source) * pow(10.0, exponent); +} + +int64_t FixedDecimal::longValue() const { + if (exponent == 0) { + return intValue; + } else { + return (long) (pow(10.0, exponent) * intValue); + } +} + + +PluralAvailableLocalesEnumeration::PluralAvailableLocalesEnumeration(UErrorCode &status) { + fOpenStatus = status; + if (U_FAILURE(status)) { + return; + } + fOpenStatus = U_ZERO_ERROR; // clear any warnings. + LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "plurals", &fOpenStatus)); + fLocales = ures_getByKey(rb.getAlias(), "locales", nullptr, &fOpenStatus); +} + +PluralAvailableLocalesEnumeration::~PluralAvailableLocalesEnumeration() { + ures_close(fLocales); + ures_close(fRes); + fLocales = nullptr; + fRes = nullptr; +} + +const char *PluralAvailableLocalesEnumeration::next(int32_t *resultLength, UErrorCode &status) { + if (U_FAILURE(status)) { + return nullptr; + } + if (U_FAILURE(fOpenStatus)) { + status = fOpenStatus; + return nullptr; + } + fRes = ures_getNextResource(fLocales, fRes, &status); + if (fRes == nullptr || U_FAILURE(status)) { + if (status == U_INDEX_OUTOFBOUNDS_ERROR) { + status = U_ZERO_ERROR; + } + return nullptr; + } + const char *result = ures_getKey(fRes); + if (resultLength != nullptr) { + *resultLength = static_cast(uprv_strlen(result)); + } + return result; +} + + +void PluralAvailableLocalesEnumeration::reset(UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + if (U_FAILURE(fOpenStatus)) { + status = fOpenStatus; + return; + } + ures_resetIterator(fLocales); +} + +int32_t PluralAvailableLocalesEnumeration::count(UErrorCode &status) const { + if (U_FAILURE(status)) { + return 0; + } + if (U_FAILURE(fOpenStatus)) { + status = fOpenStatus; + return 0; + } + return ures_getSize(fLocales); +} + +U_NAMESPACE_END + + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/plurrule_impl.h b/deps/icu-small/source/i18n/plurrule_impl.h index c27b655fcde43f..eb79249dee330a 100644 --- a/deps/icu-small/source/i18n/plurrule_impl.h +++ b/deps/icu-small/source/i18n/plurrule_impl.h @@ -1,442 +1,442 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2007-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File PLURRULE_IMPL.H -* -******************************************************************************* -*/ - - -#ifndef PLURRULE_IMPL -#define PLURRULE_IMPL - -// Internal definitions for the PluralRules implementation. - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/format.h" -#include "unicode/locid.h" -#include "unicode/parseerr.h" -#include "unicode/strenum.h" -#include "unicode/ures.h" -#include "uvector.h" -#include "hash.h" -#include "uassert.h" - -/** - * A FixedDecimal version of UPLRULES_NO_UNIQUE_VALUE used in PluralRulesTest - * for parsing of samples. - */ -#define UPLRULES_NO_UNIQUE_VALUE_DECIMAL(ERROR_CODE) (DecimalQuantity::fromExponentString(u"-0.00123456777", ERROR_CODE)) - -class PluralRulesTest; - -U_NAMESPACE_BEGIN - -class AndConstraint; -class RuleChain; -class DigitInterval; -class PluralRules; -class VisibleDigits; - -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; - -enum tokenType { - none, - tNumber, - tComma, - tSemiColon, - tSpace, - tColon, - tAt, // '@' - tDot, - tDot2, - tEllipsis, - tKeyword, - tAnd, - tOr, - tMod, // 'mod' or '%' - tNot, // 'not' only. - tIn, // 'in' only. - tEqual, // '=' only. - tNotEqual, // '!=' - tTilde, - tWithin, - tIs, - tVariableN, - tVariableI, - tVariableF, - tVariableV, - tVariableT, - tVariableE, - tVariableC, - tDecimal, - tInteger, - tEOF -}; - - -class PluralRuleParser: public UMemory { -public: - PluralRuleParser(); - virtual ~PluralRuleParser(); - - void parse(const UnicodeString &rules, PluralRules *dest, UErrorCode &status); - void getNextToken(UErrorCode &status); - void checkSyntax(UErrorCode &status); - static int32_t getNumberValue(const UnicodeString &token); - -private: - static tokenType getKeyType(const UnicodeString& token, tokenType type); - static tokenType charType(UChar ch); - static UBool isValidKeyword(const UnicodeString& token); - - const UnicodeString *ruleSrc; // The rules string. - int32_t ruleIndex; // String index in the input rules, the current parse position. - UnicodeString token; // Token most recently scanned. - tokenType type; - tokenType prevType; - - // The items currently being parsed & built. - // Note: currentChain may not be the last RuleChain in the - // list because the "other" chain is forced to the end. - AndConstraint *curAndConstraint; - RuleChain *currentChain; - - int32_t rangeLowIdx; // Indices in the UVector of ranges of the - int32_t rangeHiIdx; // low and hi values currently being parsed. - - enum EParseState { - kKeyword, - kExpr, - kValue, - kRangeList, - kSamples - }; -}; - -enum PluralOperand { - /** - * The double value of the entire number. - */ - PLURAL_OPERAND_N, - - /** - * The integer value, with the fraction digits truncated off. - */ - PLURAL_OPERAND_I, - - /** - * All visible fraction digits as an integer, including trailing zeros. - */ - PLURAL_OPERAND_F, - - /** - * Visible fraction digits as an integer, not including trailing zeros. - */ - PLURAL_OPERAND_T, - - /** - * Number of visible fraction digits. - */ - PLURAL_OPERAND_V, - - /** - * Number of visible fraction digits, not including trailing zeros. - */ - PLURAL_OPERAND_W, - - /** - * Suppressed exponent for scientific notation (exponent needed in - * scientific notation to approximate i). - */ - PLURAL_OPERAND_E, - - /** - * This operand is currently treated as an alias for `PLURAL_OPERAND_E`. - * In the future, it will represent: - * - * Suppressed exponent for compact notation (exponent needed in - * compact notation to approximate i). - */ - PLURAL_OPERAND_C, - - /** - * THIS OPERAND IS DEPRECATED AND HAS BEEN REMOVED FROM THE SPEC. - * - *

      Returns the integer value, but will fail if the number has fraction digits. - * That is, using "j" instead of "i" is like implicitly adding "v is 0". - * - *

      For example, "j is 3" is equivalent to "i is 3 and v is 0": it matches - * "3" but not "3.1" or "3.0". - */ - PLURAL_OPERAND_J -}; - -/** - * Converts from the tokenType enum to PluralOperand. Asserts that the given - * tokenType can be mapped to a PluralOperand. - */ -PluralOperand tokenTypeToPluralOperand(tokenType tt); - -/** - * An interface to FixedDecimal, allowing for other implementations. - * @internal - */ -class U_I18N_API IFixedDecimal { - public: - virtual ~IFixedDecimal(); - - /** - * Returns the value corresponding to the specified operand (n, i, f, t, v, or w). - * If the operand is 'n', returns a double; otherwise, returns an integer. - */ - virtual double getPluralOperand(PluralOperand operand) const = 0; - - virtual bool isNaN() const = 0; - - virtual bool isInfinite() const = 0; - - /** Whether the number has no nonzero fraction digits. */ - virtual bool hasIntegerValue() const = 0; -}; - -/** - * class FixedDecimal serves to communicate the properties - * of a formatted number from a decimal formatter to PluralRules::select() - * - * see DecimalFormat::getFixedDecimal() - * @internal - */ -class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject { - public: - /** - * @param n the number, e.g. 12.345 - * @param v The number of visible fraction digits, e.g. 3 - * @param f The fraction digits, e.g. 345 - * @param e The exponent, e.g. 7 in 1.2e7, for scientific notation - * @param c Currently: an alias for param `e`. - */ - FixedDecimal(double n, int32_t v, int64_t f, int32_t e, int32_t c); - FixedDecimal(double n, int32_t v, int64_t f, int32_t e); - FixedDecimal(double n, int32_t v, int64_t f); - FixedDecimal(double n, int32_t); - explicit FixedDecimal(double n); - FixedDecimal(); - ~FixedDecimal() U_OVERRIDE; - FixedDecimal(const UnicodeString &s, UErrorCode &ec); - FixedDecimal(const FixedDecimal &other); - - static FixedDecimal createWithExponent(double n, int32_t v, int32_t e); - - 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 - - int32_t getVisibleFractionDigitCount() const; - - void init(double n, int32_t v, int64_t f, int32_t e, int32_t c); - void init(double n, int32_t v, int64_t f, int32_t e); - void init(double n, int32_t v, int64_t f); - void init(double n); - UBool quickInit(double n); // Try a fast-path only initialization, - // return true if successful. - void adjustForMinFractionDigits(int32_t min); - static int64_t getFractionalDigits(double n, int32_t v); - static int32_t decimals(double n); - - FixedDecimal& operator=(const FixedDecimal& other) = default; - bool operator==(const FixedDecimal &other) const; - - UnicodeString toString() const; - - double doubleValue() const; - int64_t longValue() const; - - double source; - int32_t visibleDecimalDigitCount; - int64_t decimalDigits; - int64_t decimalDigitsWithoutTrailingZeros; - int64_t intValue; - int32_t exponent; - UBool _hasIntegerValue; - UBool isNegative; - UBool _isNaN; - UBool _isInfinite; -}; - -class AndConstraint : public UMemory { -public: - typedef enum RuleOp { - NONE, - MOD - } RuleOp; - RuleOp op = AndConstraint::NONE; - int32_t opNum = -1; // for mod expressions, the right operand of the mod. - int32_t value = -1; // valid for 'is' rules only. - UVector32 *rangeList = nullptr; // for 'in', 'within' rules. Null otherwise. - UBool negated = false; // true for negated rules. - UBool integerOnly = false; // true for 'within' rules. - tokenType digitsType = none; // n | i | v | f constraint. - AndConstraint *next = nullptr; - // Internal error status, used for errors that occur during the copy constructor. - UErrorCode fInternalStatus = U_ZERO_ERROR; - - AndConstraint() = default; - AndConstraint(const AndConstraint& other); - virtual ~AndConstraint(); - AndConstraint* add(UErrorCode& status); - // UBool isFulfilled(double number); - UBool isFulfilled(const IFixedDecimal &number); -}; - -class OrConstraint : public UMemory { -public: - AndConstraint *childNode = nullptr; - OrConstraint *next = nullptr; - // Internal error status, used for errors that occur during the copy constructor. - UErrorCode fInternalStatus = U_ZERO_ERROR; - - OrConstraint() = default; - OrConstraint(const OrConstraint& other); - virtual ~OrConstraint(); - AndConstraint* add(UErrorCode& status); - // UBool isFulfilled(double number); - UBool isFulfilled(const IFixedDecimal &number); -}; - -class RuleChain : public UMemory { -public: - UnicodeString fKeyword; - RuleChain *fNext = nullptr; - OrConstraint *ruleHeader = nullptr; - UnicodeString fDecimalSamples; // Samples strings from rule source - UnicodeString fIntegerSamples; // without @decimal or @integer, otherwise unprocessed. - UBool fDecimalSamplesUnbounded = false; - UBool fIntegerSamplesUnbounded = false; - // Internal error status, used for errors that occur during the copy constructor. - UErrorCode fInternalStatus = U_ZERO_ERROR; - - RuleChain() = default; - RuleChain(const RuleChain& other); - virtual ~RuleChain(); - - UnicodeString select(const IFixedDecimal &number) const; - void dumpRules(UnicodeString& result); - UErrorCode getKeywords(int32_t maxArraySize, UnicodeString *keywords, int32_t& arraySize) const; - UBool isKeyword(const UnicodeString& keyword) const; -}; - -class PluralKeywordEnumeration : public StringEnumeration { -public: - PluralKeywordEnumeration(RuleChain *header, UErrorCode& status); - virtual ~PluralKeywordEnumeration(); - static UClassID U_EXPORT2 getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; - virtual const UnicodeString* snext(UErrorCode& status) override; - virtual void reset(UErrorCode& status) override; - virtual int32_t count(UErrorCode& status) const override; -private: - int32_t pos; - UVector fKeywordNames; -}; - - -class U_I18N_API PluralAvailableLocalesEnumeration: public StringEnumeration { - public: - PluralAvailableLocalesEnumeration(UErrorCode &status); - virtual ~PluralAvailableLocalesEnumeration(); - virtual const char* next(int32_t *resultLength, UErrorCode& status) override; - virtual void reset(UErrorCode& status) override; - virtual int32_t count(UErrorCode& status) const override; - private: - UErrorCode fOpenStatus; - UResourceBundle *fLocales = nullptr; - UResourceBundle *fRes = nullptr; -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -#endif // _PLURRULE_IMPL -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2007-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File PLURRULE_IMPL.H +* +******************************************************************************* +*/ + + +#ifndef PLURRULE_IMPL +#define PLURRULE_IMPL + +// Internal definitions for the PluralRules implementation. + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/format.h" +#include "unicode/locid.h" +#include "unicode/parseerr.h" +#include "unicode/strenum.h" +#include "unicode/ures.h" +#include "uvector.h" +#include "hash.h" +#include "uassert.h" + +/** + * A FixedDecimal version of UPLRULES_NO_UNIQUE_VALUE used in PluralRulesTest + * for parsing of samples. + */ +#define UPLRULES_NO_UNIQUE_VALUE_DECIMAL(ERROR_CODE) (DecimalQuantity::fromExponentString(u"-0.00123456777", ERROR_CODE)) + +class PluralRulesTest; + +U_NAMESPACE_BEGIN + +class AndConstraint; +class RuleChain; +class DigitInterval; +class PluralRules; +class VisibleDigits; + +namespace pluralimpl { + +// TODO: Remove this and replace with u"" literals. Was for EBCDIC compatibility. + +static const char16_t DOT = ((char16_t) 0x002E); +static const char16_t SINGLE_QUOTE = ((char16_t) 0x0027); +static const char16_t SLASH = ((char16_t) 0x002F); +static const char16_t BACKSLASH = ((char16_t) 0x005C); +static const char16_t SPACE = ((char16_t) 0x0020); +static const char16_t EXCLAMATION = ((char16_t) 0x0021); +static const char16_t QUOTATION_MARK = ((char16_t) 0x0022); +static const char16_t NUMBER_SIGN = ((char16_t) 0x0023); +static const char16_t PERCENT_SIGN = ((char16_t) 0x0025); +static const char16_t ASTERISK = ((char16_t) 0x002A); +static const char16_t COMMA = ((char16_t) 0x002C); +static const char16_t HYPHEN = ((char16_t) 0x002D); +static const char16_t U_ZERO = ((char16_t) 0x0030); +static const char16_t U_ONE = ((char16_t) 0x0031); +static const char16_t U_TWO = ((char16_t) 0x0032); +static const char16_t U_THREE = ((char16_t) 0x0033); +static const char16_t U_FOUR = ((char16_t) 0x0034); +static const char16_t U_FIVE = ((char16_t) 0x0035); +static const char16_t U_SIX = ((char16_t) 0x0036); +static const char16_t U_SEVEN = ((char16_t) 0x0037); +static const char16_t U_EIGHT = ((char16_t) 0x0038); +static const char16_t U_NINE = ((char16_t) 0x0039); +static const char16_t COLON = ((char16_t) 0x003A); +static const char16_t SEMI_COLON = ((char16_t) 0x003B); +static const char16_t EQUALS = ((char16_t) 0x003D); +static const char16_t AT = ((char16_t) 0x0040); +static const char16_t CAP_A = ((char16_t) 0x0041); +static const char16_t CAP_B = ((char16_t) 0x0042); +static const char16_t CAP_R = ((char16_t) 0x0052); +static const char16_t CAP_Z = ((char16_t) 0x005A); +static const char16_t LOWLINE = ((char16_t) 0x005F); +static const char16_t LEFTBRACE = ((char16_t) 0x007B); +static const char16_t RIGHTBRACE = ((char16_t) 0x007D); +static const char16_t TILDE = ((char16_t) 0x007E); +static const char16_t ELLIPSIS = ((char16_t) 0x2026); + +static const char16_t LOW_A = ((char16_t) 0x0061); +static const char16_t LOW_B = ((char16_t) 0x0062); +static const char16_t LOW_C = ((char16_t) 0x0063); +static const char16_t LOW_D = ((char16_t) 0x0064); +static const char16_t LOW_E = ((char16_t) 0x0065); +static const char16_t LOW_F = ((char16_t) 0x0066); +static const char16_t LOW_G = ((char16_t) 0x0067); +static const char16_t LOW_H = ((char16_t) 0x0068); +static const char16_t LOW_I = ((char16_t) 0x0069); +static const char16_t LOW_J = ((char16_t) 0x006a); +static const char16_t LOW_K = ((char16_t) 0x006B); +static const char16_t LOW_L = ((char16_t) 0x006C); +static const char16_t LOW_M = ((char16_t) 0x006D); +static const char16_t LOW_N = ((char16_t) 0x006E); +static const char16_t LOW_O = ((char16_t) 0x006F); +static const char16_t LOW_P = ((char16_t) 0x0070); +static const char16_t LOW_Q = ((char16_t) 0x0071); +static const char16_t LOW_R = ((char16_t) 0x0072); +static const char16_t LOW_S = ((char16_t) 0x0073); +static const char16_t LOW_T = ((char16_t) 0x0074); +static const char16_t LOW_U = ((char16_t) 0x0075); +static const char16_t LOW_V = ((char16_t) 0x0076); +static const char16_t LOW_W = ((char16_t) 0x0077); +static const char16_t LOW_Y = ((char16_t) 0x0079); +static const char16_t LOW_Z = ((char16_t) 0x007A); + +} + + +static const int32_t PLURAL_RANGE_HIGH = 0x7fffffff; + +enum tokenType { + none, + tNumber, + tComma, + tSemiColon, + tSpace, + tColon, + tAt, // '@' + tDot, + tDot2, + tEllipsis, + tKeyword, + tAnd, + tOr, + tMod, // 'mod' or '%' + tNot, // 'not' only. + tIn, // 'in' only. + tEqual, // '=' only. + tNotEqual, // '!=' + tTilde, + tWithin, + tIs, + tVariableN, + tVariableI, + tVariableF, + tVariableV, + tVariableT, + tVariableE, + tVariableC, + tDecimal, + tInteger, + tEOF +}; + + +class PluralRuleParser: public UMemory { +public: + PluralRuleParser(); + virtual ~PluralRuleParser(); + + void parse(const UnicodeString &rules, PluralRules *dest, UErrorCode &status); + void getNextToken(UErrorCode &status); + void checkSyntax(UErrorCode &status); + static int32_t getNumberValue(const UnicodeString &token); + +private: + static tokenType getKeyType(const UnicodeString& token, tokenType type); + static tokenType charType(char16_t ch); + static UBool isValidKeyword(const UnicodeString& token); + + const UnicodeString *ruleSrc; // The rules string. + int32_t ruleIndex; // String index in the input rules, the current parse position. + UnicodeString token; // Token most recently scanned. + tokenType type; + tokenType prevType; + + // The items currently being parsed & built. + // Note: currentChain may not be the last RuleChain in the + // list because the "other" chain is forced to the end. + AndConstraint *curAndConstraint; + RuleChain *currentChain; + + int32_t rangeLowIdx; // Indices in the UVector of ranges of the + int32_t rangeHiIdx; // low and hi values currently being parsed. + + enum EParseState { + kKeyword, + kExpr, + kValue, + kRangeList, + kSamples + }; +}; + +enum PluralOperand { + /** + * The double value of the entire number. + */ + PLURAL_OPERAND_N, + + /** + * The integer value, with the fraction digits truncated off. + */ + PLURAL_OPERAND_I, + + /** + * All visible fraction digits as an integer, including trailing zeros. + */ + PLURAL_OPERAND_F, + + /** + * Visible fraction digits as an integer, not including trailing zeros. + */ + PLURAL_OPERAND_T, + + /** + * Number of visible fraction digits. + */ + PLURAL_OPERAND_V, + + /** + * Number of visible fraction digits, not including trailing zeros. + */ + PLURAL_OPERAND_W, + + /** + * Suppressed exponent for scientific notation (exponent needed in + * scientific notation to approximate i). + */ + PLURAL_OPERAND_E, + + /** + * This operand is currently treated as an alias for `PLURAL_OPERAND_E`. + * In the future, it will represent: + * + * Suppressed exponent for compact notation (exponent needed in + * compact notation to approximate i). + */ + PLURAL_OPERAND_C, + + /** + * THIS OPERAND IS DEPRECATED AND HAS BEEN REMOVED FROM THE SPEC. + * + *

      Returns the integer value, but will fail if the number has fraction digits. + * That is, using "j" instead of "i" is like implicitly adding "v is 0". + * + *

      For example, "j is 3" is equivalent to "i is 3 and v is 0": it matches + * "3" but not "3.1" or "3.0". + */ + PLURAL_OPERAND_J +}; + +/** + * Converts from the tokenType enum to PluralOperand. Asserts that the given + * tokenType can be mapped to a PluralOperand. + */ +PluralOperand tokenTypeToPluralOperand(tokenType tt); + +/** + * An interface to FixedDecimal, allowing for other implementations. + * @internal + */ +class U_I18N_API IFixedDecimal { + public: + virtual ~IFixedDecimal(); + + /** + * Returns the value corresponding to the specified operand (n, i, f, t, v, or w). + * If the operand is 'n', returns a double; otherwise, returns an integer. + */ + virtual double getPluralOperand(PluralOperand operand) const = 0; + + virtual bool isNaN() const = 0; + + virtual bool isInfinite() const = 0; + + /** Whether the number has no nonzero fraction digits. */ + virtual bool hasIntegerValue() const = 0; +}; + +/** + * class FixedDecimal serves to communicate the properties + * of a formatted number from a decimal formatter to PluralRules::select() + * + * see DecimalFormat::getFixedDecimal() + * @internal + */ +class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject { + public: + /** + * @param n the number, e.g. 12.345 + * @param v The number of visible fraction digits, e.g. 3 + * @param f The fraction digits, e.g. 345 + * @param e The exponent, e.g. 7 in 1.2e7, for scientific notation + * @param c Currently: an alias for param `e`. + */ + FixedDecimal(double n, int32_t v, int64_t f, int32_t e, int32_t c); + FixedDecimal(double n, int32_t v, int64_t f, int32_t e); + FixedDecimal(double n, int32_t v, int64_t f); + FixedDecimal(double n, int32_t); + explicit FixedDecimal(double n); + FixedDecimal(); + ~FixedDecimal() override; + FixedDecimal(const UnicodeString &s, UErrorCode &ec); + FixedDecimal(const FixedDecimal &other); + + static FixedDecimal createWithExponent(double n, int32_t v, int32_t e); + + double getPluralOperand(PluralOperand operand) const override; + bool isNaN() const override; + bool isInfinite() const override; + bool hasIntegerValue() const override; + + bool isNanOrInfinity() const; // used in decimfmtimpl.cpp + + int32_t getVisibleFractionDigitCount() const; + + void init(double n, int32_t v, int64_t f, int32_t e, int32_t c); + void init(double n, int32_t v, int64_t f, int32_t e); + void init(double n, int32_t v, int64_t f); + void init(double n); + UBool quickInit(double n); // Try a fast-path only initialization, + // return true if successful. + void adjustForMinFractionDigits(int32_t min); + static int64_t getFractionalDigits(double n, int32_t v); + static int32_t decimals(double n); + + FixedDecimal& operator=(const FixedDecimal& other) = default; + bool operator==(const FixedDecimal &other) const; + + UnicodeString toString() const; + + double doubleValue() const; + int64_t longValue() const; + + double source; + int32_t visibleDecimalDigitCount; + int64_t decimalDigits; + int64_t decimalDigitsWithoutTrailingZeros; + int64_t intValue; + int32_t exponent; + UBool _hasIntegerValue; + UBool isNegative; + UBool _isNaN; + UBool _isInfinite; +}; + +class AndConstraint : public UMemory { +public: + typedef enum RuleOp { + NONE, + MOD + } RuleOp; + RuleOp op = AndConstraint::NONE; + int32_t opNum = -1; // for mod expressions, the right operand of the mod. + int32_t value = -1; // valid for 'is' rules only. + UVector32 *rangeList = nullptr; // for 'in', 'within' rules. Null otherwise. + UBool negated = false; // true for negated rules. + UBool integerOnly = false; // true for 'within' rules. + tokenType digitsType = none; // n | i | v | f constraint. + AndConstraint *next = nullptr; + // Internal error status, used for errors that occur during the copy constructor. + UErrorCode fInternalStatus = U_ZERO_ERROR; + + AndConstraint() = default; + AndConstraint(const AndConstraint& other); + virtual ~AndConstraint(); + AndConstraint* add(UErrorCode& status); + // UBool isFulfilled(double number); + UBool isFulfilled(const IFixedDecimal &number); +}; + +class OrConstraint : public UMemory { +public: + AndConstraint *childNode = nullptr; + OrConstraint *next = nullptr; + // Internal error status, used for errors that occur during the copy constructor. + UErrorCode fInternalStatus = U_ZERO_ERROR; + + OrConstraint() = default; + OrConstraint(const OrConstraint& other); + virtual ~OrConstraint(); + AndConstraint* add(UErrorCode& status); + // UBool isFulfilled(double number); + UBool isFulfilled(const IFixedDecimal &number); +}; + +class RuleChain : public UMemory { +public: + UnicodeString fKeyword; + RuleChain *fNext = nullptr; + OrConstraint *ruleHeader = nullptr; + UnicodeString fDecimalSamples; // Samples strings from rule source + UnicodeString fIntegerSamples; // without @decimal or @integer, otherwise unprocessed. + UBool fDecimalSamplesUnbounded = false; + UBool fIntegerSamplesUnbounded = false; + // Internal error status, used for errors that occur during the copy constructor. + UErrorCode fInternalStatus = U_ZERO_ERROR; + + RuleChain() = default; + RuleChain(const RuleChain& other); + virtual ~RuleChain(); + + UnicodeString select(const IFixedDecimal &number) const; + void dumpRules(UnicodeString& result); + UErrorCode getKeywords(int32_t maxArraySize, UnicodeString *keywords, int32_t& arraySize) const; + UBool isKeyword(const UnicodeString& keyword) const; +}; + +class PluralKeywordEnumeration : public StringEnumeration { +public: + PluralKeywordEnumeration(RuleChain *header, UErrorCode& status); + virtual ~PluralKeywordEnumeration(); + static UClassID U_EXPORT2 getStaticClassID(); + virtual UClassID getDynamicClassID() const override; + virtual const UnicodeString* snext(UErrorCode& status) override; + virtual void reset(UErrorCode& status) override; + virtual int32_t count(UErrorCode& status) const override; +private: + int32_t pos; + UVector fKeywordNames; +}; + + +class U_I18N_API PluralAvailableLocalesEnumeration: public StringEnumeration { + public: + PluralAvailableLocalesEnumeration(UErrorCode &status); + virtual ~PluralAvailableLocalesEnumeration(); + virtual const char* next(int32_t *resultLength, UErrorCode& status) override; + virtual void reset(UErrorCode& status) override; + virtual int32_t count(UErrorCode& status) const override; + private: + UErrorCode fOpenStatus; + UResourceBundle *fLocales = nullptr; + UResourceBundle *fRes = nullptr; +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif // _PLURRULE_IMPL +//eof diff --git a/deps/icu-small/source/i18n/quant.cpp b/deps/icu-small/source/i18n/quant.cpp index ed3393338775e9..85af9dcacc9328 100644 --- a/deps/icu-small/source/i18n/quant.cpp +++ b/deps/icu-small/source/i18n/quant.cpp @@ -1,151 +1,151 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 2001-2012, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 07/26/01 aliu Creation. -********************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "quant.h" -#include "unicode/unistr.h" -#include "util.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Quantifier) - -Quantifier::Quantifier(UnicodeFunctor *adoptedMatcher, - uint32_t _minCount, uint32_t _maxCount) { - // assert(adopted != 0); - // assert(minCount <= maxCount); - matcher = adoptedMatcher; - this->minCount = _minCount; - this->maxCount = _maxCount; -} - -Quantifier::Quantifier(const Quantifier& o) : - UnicodeFunctor(o), - UnicodeMatcher(o), - matcher(o.matcher->clone()), - minCount(o.minCount), - maxCount(o.maxCount) -{ -} - -Quantifier::~Quantifier() { - delete matcher; -} - -/** - * Implement UnicodeFunctor - */ -Quantifier* Quantifier::clone() const { - return new Quantifier(*this); -} - -/** - * UnicodeFunctor API. Cast 'this' to a UnicodeMatcher* pointer - * and return the pointer. - */ -UnicodeMatcher* Quantifier::toMatcher() const { - Quantifier *nonconst_this = const_cast(this); - UnicodeMatcher *nonconst_base = static_cast(nonconst_this); - - return nonconst_base; -} - -UMatchDegree Quantifier::matches(const Replaceable& text, - int32_t& offset, - int32_t limit, - UBool incremental) { - int32_t start = offset; - uint32_t count = 0; - while (count < maxCount) { - int32_t pos = offset; - UMatchDegree m = matcher->toMatcher()->matches(text, offset, limit, incremental); - if (m == U_MATCH) { - ++count; - if (pos == offset) { - // If offset has not moved we have a zero-width match. - // Don't keep matching it infinitely. - break; - } - } else if (incremental && m == U_PARTIAL_MATCH) { - return U_PARTIAL_MATCH; - } else { - break; - } - } - if (incremental && offset == limit) { - return U_PARTIAL_MATCH; - } - if (count >= minCount) { - return U_MATCH; - } - offset = start; - return U_MISMATCH; -} - -/** - * Implement UnicodeMatcher - */ -UnicodeString& Quantifier::toPattern(UnicodeString& result, - UBool escapeUnprintable) const { - result.truncate(0); - matcher->toMatcher()->toPattern(result, escapeUnprintable); - if (minCount == 0) { - if (maxCount == 1) { - return result.append((UChar)63); /*?*/ - } else if (maxCount == MAX) { - return result.append((UChar)42); /***/ - } - // else fall through - } else if (minCount == 1 && maxCount == MAX) { - return result.append((UChar)43); /*+*/ - } - result.append((UChar)123); /*{*/ - ICU_Utility::appendNumber(result, minCount); - result.append((UChar)44); /*,*/ - if (maxCount != MAX) { - ICU_Utility::appendNumber(result, maxCount); - } - result.append((UChar)125); /*}*/ - return result; -} - -/** - * Implement UnicodeMatcher - */ -UBool Quantifier::matchesIndexValue(uint8_t v) const { - return (minCount == 0) || matcher->toMatcher()->matchesIndexValue(v); -} - -/** - * Implement UnicodeMatcher - */ -void Quantifier::addMatchSetTo(UnicodeSet& toUnionTo) const { - if (maxCount > 0) { - matcher->toMatcher()->addMatchSetTo(toUnionTo); - } -} - -/** - * Implement UnicodeFunctor - */ -void Quantifier::setData(const TransliterationRuleData* d) { - matcher->setData(d); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2001-2012, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 07/26/01 aliu Creation. +********************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "quant.h" +#include "unicode/unistr.h" +#include "util.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Quantifier) + +Quantifier::Quantifier(UnicodeFunctor *adoptedMatcher, + uint32_t _minCount, uint32_t _maxCount) { + // assert(adopted != 0); + // assert(minCount <= maxCount); + matcher = adoptedMatcher; + this->minCount = _minCount; + this->maxCount = _maxCount; +} + +Quantifier::Quantifier(const Quantifier& o) : + UnicodeFunctor(o), + UnicodeMatcher(o), + matcher(o.matcher->clone()), + minCount(o.minCount), + maxCount(o.maxCount) +{ +} + +Quantifier::~Quantifier() { + delete matcher; +} + +/** + * Implement UnicodeFunctor + */ +Quantifier* Quantifier::clone() const { + return new Quantifier(*this); +} + +/** + * UnicodeFunctor API. Cast 'this' to a UnicodeMatcher* pointer + * and return the pointer. + */ +UnicodeMatcher* Quantifier::toMatcher() const { + Quantifier *nonconst_this = const_cast(this); + UnicodeMatcher *nonconst_base = static_cast(nonconst_this); + + return nonconst_base; +} + +UMatchDegree Quantifier::matches(const Replaceable& text, + int32_t& offset, + int32_t limit, + UBool incremental) { + int32_t start = offset; + uint32_t count = 0; + while (count < maxCount) { + int32_t pos = offset; + UMatchDegree m = matcher->toMatcher()->matches(text, offset, limit, incremental); + if (m == U_MATCH) { + ++count; + if (pos == offset) { + // If offset has not moved we have a zero-width match. + // Don't keep matching it infinitely. + break; + } + } else if (incremental && m == U_PARTIAL_MATCH) { + return U_PARTIAL_MATCH; + } else { + break; + } + } + if (incremental && offset == limit) { + return U_PARTIAL_MATCH; + } + if (count >= minCount) { + return U_MATCH; + } + offset = start; + return U_MISMATCH; +} + +/** + * Implement UnicodeMatcher + */ +UnicodeString& Quantifier::toPattern(UnicodeString& result, + UBool escapeUnprintable) const { + result.truncate(0); + matcher->toMatcher()->toPattern(result, escapeUnprintable); + if (minCount == 0) { + if (maxCount == 1) { + return result.append((char16_t)63); /*?*/ + } else if (maxCount == MAX) { + return result.append((char16_t)42); /***/ + } + // else fall through + } else if (minCount == 1 && maxCount == MAX) { + return result.append((char16_t)43); /*+*/ + } + result.append((char16_t)123); /*{*/ + ICU_Utility::appendNumber(result, minCount); + result.append((char16_t)44); /*,*/ + if (maxCount != MAX) { + ICU_Utility::appendNumber(result, maxCount); + } + result.append((char16_t)125); /*}*/ + return result; +} + +/** + * Implement UnicodeMatcher + */ +UBool Quantifier::matchesIndexValue(uint8_t v) const { + return (minCount == 0) || matcher->toMatcher()->matchesIndexValue(v); +} + +/** + * Implement UnicodeMatcher + */ +void Quantifier::addMatchSetTo(UnicodeSet& toUnionTo) const { + if (maxCount > 0) { + matcher->toMatcher()->addMatchSetTo(toUnionTo); + } +} + +/** + * Implement UnicodeFunctor + */ +void Quantifier::setData(const TransliterationRuleData* d) { + matcher->setData(d); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +//eof diff --git a/deps/icu-small/source/i18n/quant.h b/deps/icu-small/source/i18n/quant.h index 427a6b048081ee..4f36971689a3f4 100644 --- a/deps/icu-small/source/i18n/quant.h +++ b/deps/icu-small/source/i18n/quant.h @@ -1,126 +1,126 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 2001-2011, International Business Machines Corporation - * and others. All Rights Reserved. - ********************************************************************** - * Date Name Description - * 07/26/01 aliu Creation. - ********************************************************************** - */ -#ifndef QUANT_H -#define QUANT_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/unifunct.h" -#include "unicode/unimatch.h" - -U_NAMESPACE_BEGIN - -class Quantifier : public UnicodeFunctor, public UnicodeMatcher { - - public: - - enum { MAX = 0x7FFFFFFF }; - - Quantifier(UnicodeFunctor *adoptedMatcher, - uint32_t minCount, uint32_t maxCount); - - Quantifier(const Quantifier& o); - - virtual ~Quantifier(); - - /** - * UnicodeFunctor API. Cast 'this' to a UnicodeMatcher* pointer - * and return the pointer. - * @return the UnicodeMatcher pointer. - */ - virtual UnicodeMatcher* toMatcher() const override; - - /** - * Implement UnicodeFunctor - * @return a copy of the object. - */ - virtual Quantifier* clone() const override; - - /** - * Implement UnicodeMatcher - * @param text the text to be matched - * @param offset on input, the index into text at which to begin - * matching. On output, the limit of the matched text. The - * number of matched characters is the output value of offset - * minus the input value. Offset should always point to the - * HIGH SURROGATE (leading code unit) of a pair of surrogates, - * both on entry and upon return. - * @param limit the limit index of text to be matched. Greater - * than offset for a forward direction match, less than offset for - * a backward direction match. The last character to be - * considered for matching will be text.charAt(limit-1) in the - * forward direction or text.charAt(limit+1) in the backward - * direction. - * @param incremental if true, then assume further characters may - * be inserted at limit and check for partial matching. Otherwise - * assume the text as given is complete. - * @return a match degree value indicating a full match, a partial - * match, or a mismatch. If incremental is false then - * U_PARTIAL_MATCH should never be returned. - */ - virtual UMatchDegree matches(const Replaceable& text, - int32_t& offset, - int32_t limit, - UBool incremental) override; - - /** - * Implement UnicodeMatcher - * @param result Output param to receive the pattern. - * @param escapeUnprintable if True then escape the unprintable characters. - * @return A reference to 'result'. - */ - virtual UnicodeString& toPattern(UnicodeString& result, - UBool escapeUnprintable = false) const override; - - /** - * Implement UnicodeMatcher - * @param v the given index value. - * @return true if this rule matches the given index value. - */ - virtual UBool matchesIndexValue(uint8_t v) const override; - - /** - * Implement UnicodeMatcher - */ - virtual void addMatchSetTo(UnicodeSet& toUnionTo) const override; - - /** - * UnicodeFunctor API - */ - virtual void setData(const TransliterationRuleData*) override; - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - virtual UClassID getDynamicClassID() const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - static UClassID U_EXPORT2 getStaticClassID(); - - private: - - UnicodeFunctor* matcher; // owned - - uint32_t minCount; - - uint32_t maxCount; -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 2001-2011, International Business Machines Corporation + * and others. All Rights Reserved. + ********************************************************************** + * Date Name Description + * 07/26/01 aliu Creation. + ********************************************************************** + */ +#ifndef QUANT_H +#define QUANT_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/unifunct.h" +#include "unicode/unimatch.h" + +U_NAMESPACE_BEGIN + +class Quantifier : public UnicodeFunctor, public UnicodeMatcher { + + public: + + enum { MAX = 0x7FFFFFFF }; + + Quantifier(UnicodeFunctor *adoptedMatcher, + uint32_t minCount, uint32_t maxCount); + + Quantifier(const Quantifier& o); + + virtual ~Quantifier(); + + /** + * UnicodeFunctor API. Cast 'this' to a UnicodeMatcher* pointer + * and return the pointer. + * @return the UnicodeMatcher pointer. + */ + virtual UnicodeMatcher* toMatcher() const override; + + /** + * Implement UnicodeFunctor + * @return a copy of the object. + */ + virtual Quantifier* clone() const override; + + /** + * Implement UnicodeMatcher + * @param text the text to be matched + * @param offset on input, the index into text at which to begin + * matching. On output, the limit of the matched text. The + * number of matched characters is the output value of offset + * minus the input value. Offset should always point to the + * HIGH SURROGATE (leading code unit) of a pair of surrogates, + * both on entry and upon return. + * @param limit the limit index of text to be matched. Greater + * than offset for a forward direction match, less than offset for + * a backward direction match. The last character to be + * considered for matching will be text.charAt(limit-1) in the + * forward direction or text.charAt(limit+1) in the backward + * direction. + * @param incremental if true, then assume further characters may + * be inserted at limit and check for partial matching. Otherwise + * assume the text as given is complete. + * @return a match degree value indicating a full match, a partial + * match, or a mismatch. If incremental is false then + * U_PARTIAL_MATCH should never be returned. + */ + virtual UMatchDegree matches(const Replaceable& text, + int32_t& offset, + int32_t limit, + UBool incremental) override; + + /** + * Implement UnicodeMatcher + * @param result Output param to receive the pattern. + * @param escapeUnprintable if True then escape the unprintable characters. + * @return A reference to 'result'. + */ + virtual UnicodeString& toPattern(UnicodeString& result, + UBool escapeUnprintable = false) const override; + + /** + * Implement UnicodeMatcher + * @param v the given index value. + * @return true if this rule matches the given index value. + */ + virtual UBool matchesIndexValue(uint8_t v) const override; + + /** + * Implement UnicodeMatcher + */ + virtual void addMatchSetTo(UnicodeSet& toUnionTo) const override; + + /** + * UnicodeFunctor API + */ + virtual void setData(const TransliterationRuleData*) override; + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + virtual UClassID getDynamicClassID() const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + static UClassID U_EXPORT2 getStaticClassID(); + + private: + + UnicodeFunctor* matcher; // owned + + uint32_t minCount; + + uint32_t maxCount; +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/quantityformatter.cpp b/deps/icu-small/source/i18n/quantityformatter.cpp index 7b4d51e803f045..4b92e7be1bb938 100644 --- a/deps/icu-small/source/i18n/quantityformatter.cpp +++ b/deps/icu-small/source/i18n/quantityformatter.cpp @@ -1,243 +1,243 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 2014-2016, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* quantityformatter.cpp -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/simpleformatter.h" -#include "quantityformatter.h" -#include "uassert.h" -#include "unicode/unistr.h" -#include "unicode/decimfmt.h" -#include "cstring.h" -#include "unicode/plurrule.h" -#include "charstr.h" -#include "unicode/fmtable.h" -#include "unicode/fieldpos.h" -#include "standardplural.h" -#include "uassert.h" -#include "number_decimalquantity.h" -#include "number_utypes.h" -#include "formatted_string_builder.h" - -U_NAMESPACE_BEGIN - -QuantityFormatter::QuantityFormatter() { - for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { - formatters[i] = NULL; - } -} - -QuantityFormatter::QuantityFormatter(const QuantityFormatter &other) { - for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { - if (other.formatters[i] == NULL) { - formatters[i] = NULL; - } else { - formatters[i] = new SimpleFormatter(*other.formatters[i]); - } - } -} - -QuantityFormatter &QuantityFormatter::operator=( - const QuantityFormatter& other) { - if (this == &other) { - return *this; - } - for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { - delete formatters[i]; - if (other.formatters[i] == NULL) { - formatters[i] = NULL; - } else { - formatters[i] = new SimpleFormatter(*other.formatters[i]); - } - } - return *this; -} - -QuantityFormatter::~QuantityFormatter() { - for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { - delete formatters[i]; - } -} - -void QuantityFormatter::reset() { - for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { - delete formatters[i]; - formatters[i] = NULL; - } -} - -UBool QuantityFormatter::addIfAbsent( - const char *variant, - const UnicodeString &rawPattern, - UErrorCode &status) { - int32_t pluralIndex = StandardPlural::indexFromString(variant, status); - if (U_FAILURE(status)) { - return false; - } - if (formatters[pluralIndex] != NULL) { - return true; - } - SimpleFormatter *newFmt = new SimpleFormatter(rawPattern, 0, 1, status); - if (newFmt == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return false; - } - if (U_FAILURE(status)) { - delete newFmt; - return false; - } - formatters[pluralIndex] = newFmt; - return true; -} - -UBool QuantityFormatter::isValid() const { - return formatters[StandardPlural::OTHER] != NULL; -} - -const SimpleFormatter *QuantityFormatter::getByVariant( - const char *variant) const { - U_ASSERT(isValid()); - int32_t pluralIndex = StandardPlural::indexOrOtherIndexFromString(variant); - const SimpleFormatter *pattern = formatters[pluralIndex]; - if (pattern == NULL) { - pattern = formatters[StandardPlural::OTHER]; - } - return pattern; -} - -UnicodeString &QuantityFormatter::format( - const Formattable &number, - const NumberFormat &fmt, - const PluralRules &rules, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const { - UnicodeString formattedNumber; - StandardPlural::Form p = selectPlural(number, fmt, rules, formattedNumber, pos, status); - if (U_FAILURE(status)) { - return appendTo; - } - const SimpleFormatter *pattern = formatters[p]; - if (pattern == NULL) { - pattern = formatters[StandardPlural::OTHER]; - if (pattern == NULL) { - status = U_INVALID_STATE_ERROR; - return appendTo; - } - } - return format(*pattern, formattedNumber, appendTo, pos, status); -} - -// The following methods live here so that class PluralRules does not depend on number formatting, -// and the SimpleFormatter does not depend on FieldPosition. - -StandardPlural::Form QuantityFormatter::selectPlural( - const Formattable &number, - const NumberFormat &fmt, - const PluralRules &rules, - UnicodeString &formattedNumber, - FieldPosition &pos, - UErrorCode &status) { - if (U_FAILURE(status)) { - return StandardPlural::OTHER; - } - UnicodeString pluralKeyword; - const DecimalFormat *decFmt = dynamic_cast(&fmt); - if (decFmt != NULL) { - number::impl::DecimalQuantity dq; - decFmt->formatToDecimalQuantity(number, dq, status); - if (U_FAILURE(status)) { - return StandardPlural::OTHER; - } - pluralKeyword = rules.select(dq); - decFmt->format(number, formattedNumber, pos, status); - } else { - if (number.getType() == Formattable::kDouble) { - pluralKeyword = rules.select(number.getDouble()); - } else if (number.getType() == Formattable::kLong) { - pluralKeyword = rules.select(number.getLong()); - } else if (number.getType() == Formattable::kInt64) { - pluralKeyword = rules.select((double) number.getInt64()); - } else { - status = U_ILLEGAL_ARGUMENT_ERROR; - return StandardPlural::OTHER; - } - fmt.format(number, formattedNumber, pos, status); - } - return StandardPlural::orOtherFromString(pluralKeyword); -} - -void QuantityFormatter::formatAndSelect( - double quantity, - const NumberFormat& fmt, - const PluralRules& rules, - FormattedStringBuilder& output, - StandardPlural::Form& pluralForm, - UErrorCode& status) { - UnicodeString pluralKeyword; - const DecimalFormat* df = dynamic_cast(&fmt); - if (df != nullptr) { - number::impl::UFormattedNumberData fn; - fn.quantity.setToDouble(quantity); - const number::LocalizedNumberFormatter* lnf = df->toNumberFormatter(status); - if (U_FAILURE(status)) { - return; - } - lnf->formatImpl(&fn, status); - if (U_FAILURE(status)) { - return; - } - output = std::move(fn.getStringRef()); - pluralKeyword = rules.select(fn.quantity); - } else { - UnicodeString result; - fmt.format(quantity, result, status); - if (U_FAILURE(status)) { - return; - } - // This code path is probably RBNF. Use the generic numeric field. - output.append(result, kGeneralNumericField, status); - if (U_FAILURE(status)) { - return; - } - pluralKeyword = rules.select(quantity); - } - pluralForm = StandardPlural::orOtherFromString(pluralKeyword); -} - -UnicodeString &QuantityFormatter::format( - const SimpleFormatter &pattern, - const UnicodeString &value, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) { - if (U_FAILURE(status)) { - return appendTo; - } - const UnicodeString *param = &value; - int32_t offset; - pattern.formatAndAppend(¶m, 1, appendTo, &offset, 1, status); - if (pos.getBeginIndex() != 0 || pos.getEndIndex() != 0) { - if (offset >= 0) { - pos.setBeginIndex(pos.getBeginIndex() + offset); - pos.setEndIndex(pos.getEndIndex() + offset); - } else { - pos.setBeginIndex(0); - pos.setEndIndex(0); - } - } - return appendTo; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 2014-2016, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* quantityformatter.cpp +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/simpleformatter.h" +#include "quantityformatter.h" +#include "uassert.h" +#include "unicode/unistr.h" +#include "unicode/decimfmt.h" +#include "cstring.h" +#include "unicode/plurrule.h" +#include "charstr.h" +#include "unicode/fmtable.h" +#include "unicode/fieldpos.h" +#include "standardplural.h" +#include "uassert.h" +#include "number_decimalquantity.h" +#include "number_utypes.h" +#include "formatted_string_builder.h" + +U_NAMESPACE_BEGIN + +QuantityFormatter::QuantityFormatter() { + for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { + formatters[i] = nullptr; + } +} + +QuantityFormatter::QuantityFormatter(const QuantityFormatter &other) { + for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { + if (other.formatters[i] == nullptr) { + formatters[i] = nullptr; + } else { + formatters[i] = new SimpleFormatter(*other.formatters[i]); + } + } +} + +QuantityFormatter &QuantityFormatter::operator=( + const QuantityFormatter& other) { + if (this == &other) { + return *this; + } + for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { + delete formatters[i]; + if (other.formatters[i] == nullptr) { + formatters[i] = nullptr; + } else { + formatters[i] = new SimpleFormatter(*other.formatters[i]); + } + } + return *this; +} + +QuantityFormatter::~QuantityFormatter() { + for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { + delete formatters[i]; + } +} + +void QuantityFormatter::reset() { + for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { + delete formatters[i]; + formatters[i] = nullptr; + } +} + +UBool QuantityFormatter::addIfAbsent( + const char *variant, + const UnicodeString &rawPattern, + UErrorCode &status) { + int32_t pluralIndex = StandardPlural::indexFromString(variant, status); + if (U_FAILURE(status)) { + return false; + } + if (formatters[pluralIndex] != nullptr) { + return true; + } + SimpleFormatter *newFmt = new SimpleFormatter(rawPattern, 0, 1, status); + if (newFmt == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return false; + } + if (U_FAILURE(status)) { + delete newFmt; + return false; + } + formatters[pluralIndex] = newFmt; + return true; +} + +UBool QuantityFormatter::isValid() const { + return formatters[StandardPlural::OTHER] != nullptr; +} + +const SimpleFormatter *QuantityFormatter::getByVariant( + const char *variant) const { + U_ASSERT(isValid()); + int32_t pluralIndex = StandardPlural::indexOrOtherIndexFromString(variant); + const SimpleFormatter *pattern = formatters[pluralIndex]; + if (pattern == nullptr) { + pattern = formatters[StandardPlural::OTHER]; + } + return pattern; +} + +UnicodeString &QuantityFormatter::format( + const Formattable &number, + const NumberFormat &fmt, + const PluralRules &rules, + UnicodeString &appendTo, + FieldPosition &pos, + UErrorCode &status) const { + UnicodeString formattedNumber; + StandardPlural::Form p = selectPlural(number, fmt, rules, formattedNumber, pos, status); + if (U_FAILURE(status)) { + return appendTo; + } + const SimpleFormatter *pattern = formatters[p]; + if (pattern == nullptr) { + pattern = formatters[StandardPlural::OTHER]; + if (pattern == nullptr) { + status = U_INVALID_STATE_ERROR; + return appendTo; + } + } + return format(*pattern, formattedNumber, appendTo, pos, status); +} + +// The following methods live here so that class PluralRules does not depend on number formatting, +// and the SimpleFormatter does not depend on FieldPosition. + +StandardPlural::Form QuantityFormatter::selectPlural( + const Formattable &number, + const NumberFormat &fmt, + const PluralRules &rules, + UnicodeString &formattedNumber, + FieldPosition &pos, + UErrorCode &status) { + if (U_FAILURE(status)) { + return StandardPlural::OTHER; + } + UnicodeString pluralKeyword; + const DecimalFormat *decFmt = dynamic_cast(&fmt); + if (decFmt != nullptr) { + number::impl::DecimalQuantity dq; + decFmt->formatToDecimalQuantity(number, dq, status); + if (U_FAILURE(status)) { + return StandardPlural::OTHER; + } + pluralKeyword = rules.select(dq); + decFmt->format(number, formattedNumber, pos, status); + } else { + if (number.getType() == Formattable::kDouble) { + pluralKeyword = rules.select(number.getDouble()); + } else if (number.getType() == Formattable::kLong) { + pluralKeyword = rules.select(number.getLong()); + } else if (number.getType() == Formattable::kInt64) { + pluralKeyword = rules.select((double) number.getInt64()); + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + return StandardPlural::OTHER; + } + fmt.format(number, formattedNumber, pos, status); + } + return StandardPlural::orOtherFromString(pluralKeyword); +} + +void QuantityFormatter::formatAndSelect( + double quantity, + const NumberFormat& fmt, + const PluralRules& rules, + FormattedStringBuilder& output, + StandardPlural::Form& pluralForm, + UErrorCode& status) { + UnicodeString pluralKeyword; + const DecimalFormat* df = dynamic_cast(&fmt); + if (df != nullptr) { + number::impl::UFormattedNumberData fn; + fn.quantity.setToDouble(quantity); + const number::LocalizedNumberFormatter* lnf = df->toNumberFormatter(status); + if (U_FAILURE(status)) { + return; + } + lnf->formatImpl(&fn, status); + if (U_FAILURE(status)) { + return; + } + output = std::move(fn.getStringRef()); + pluralKeyword = rules.select(fn.quantity); + } else { + UnicodeString result; + fmt.format(quantity, result, status); + if (U_FAILURE(status)) { + return; + } + // This code path is probably RBNF. Use the generic numeric field. + output.append(result, kGeneralNumericField, status); + if (U_FAILURE(status)) { + return; + } + pluralKeyword = rules.select(quantity); + } + pluralForm = StandardPlural::orOtherFromString(pluralKeyword); +} + +UnicodeString &QuantityFormatter::format( + const SimpleFormatter &pattern, + const UnicodeString &value, + UnicodeString &appendTo, + FieldPosition &pos, + UErrorCode &status) { + if (U_FAILURE(status)) { + return appendTo; + } + const UnicodeString *param = &value; + int32_t offset; + pattern.formatAndAppend(¶m, 1, appendTo, &offset, 1, status); + if (pos.getBeginIndex() != 0 || pos.getEndIndex() != 0) { + if (offset >= 0) { + pos.setBeginIndex(pos.getBeginIndex() + offset); + pos.setEndIndex(pos.getEndIndex() + offset); + } else { + pos.setBeginIndex(0); + pos.setEndIndex(0); + } + } + return appendTo; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/quantityformatter.h b/deps/icu-small/source/i18n/quantityformatter.h index 841798cf889255..9fa5372dd8b0d6 100644 --- a/deps/icu-small/source/i18n/quantityformatter.h +++ b/deps/icu-small/source/i18n/quantityformatter.h @@ -1,165 +1,165 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 2014-2016, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* quantityformatter.h -*/ - -#ifndef __QUANTITY_FORMATTER_H__ -#define __QUANTITY_FORMATTER_H__ - -#include "unicode/utypes.h" -#include "unicode/uobject.h" - -#if !UCONFIG_NO_FORMATTING - -#include "standardplural.h" - -U_NAMESPACE_BEGIN - -class SimpleFormatter; -class UnicodeString; -class PluralRules; -class NumberFormat; -class Formattable; -class FieldPosition; -class FormattedStringBuilder; - -/** - * A plural aware formatter that is good for expressing a single quantity and - * a unit. - *

      - * First use the add() methods to add a pattern for each plural variant. - * There must be a pattern for the "other" variant. - * Then use the format() method. - *

      - * Concurrent calls only to const methods on a QuantityFormatter object are - * safe, but concurrent const and non-const method calls on a QuantityFormatter - * object are not safe and require synchronization. - * - */ -class U_I18N_API QuantityFormatter : public UMemory { -public: - /** - * Default constructor. - */ - QuantityFormatter(); - - /** - * Copy constructor. - */ - QuantityFormatter(const QuantityFormatter& other); - - /** - * Assignment operator - */ - QuantityFormatter &operator=(const QuantityFormatter& other); - - /** - * Destructor. - */ - ~QuantityFormatter(); - - /** - * Removes all variants from this object including the "other" variant. - */ - void reset(); - - /** - * Adds a plural variant if there is none yet for the plural form. - * - * @param variant "zero", "one", "two", "few", "many", "other" - * @param rawPattern the pattern for the variant e.g "{0} meters" - * @param status any error returned here. - * @return true on success; false if status was set to a non zero error. - */ - UBool addIfAbsent(const char *variant, const UnicodeString &rawPattern, UErrorCode &status); - - /** - * returns true if this object has at least the "other" variant. - */ - UBool isValid() const; - - /** - * Gets the pattern formatter that would be used for a particular variant. - * If isValid() returns true, this method is guaranteed to return a - * non-NULL value. - */ - const SimpleFormatter *getByVariant(const char *variant) const; - - /** - * Formats a number with this object appending the result to appendTo. - * At least the "other" variant must be added to this object for this - * method to work. - * - * @param number the single number. - * @param fmt formats the number - * @param rules computes the plural variant to use. - * @param appendTo result appended here. - * @param status any error returned here. - * @return appendTo - */ - UnicodeString &format( - const Formattable &number, - const NumberFormat &fmt, - const PluralRules &rules, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const; - - /** - * Selects the standard plural form for the number/formatter/rules. - * Used in MeasureFormat for backwards compatibility with NumberFormat. - */ - static StandardPlural::Form selectPlural( - const Formattable &number, - const NumberFormat &fmt, - const PluralRules &rules, - UnicodeString &formattedNumber, - FieldPosition &pos, - UErrorCode &status); - - /** - * Formats a quantity and selects its plural form. The output is appended - * to a FormattedStringBuilder in order to retain field information. - * - * @param quantity The number to format. - * @param fmt The formatter to use to format the number. - * @param rules The rules to use to select the plural form of the - * formatted number. - * @param output Where to append the result of the format operation. - * @param pluralForm Output variable populated with the plural form of the - * formatted number. - * @param status Set if an error occurs. - */ - static void formatAndSelect( - double quantity, - const NumberFormat& fmt, - const PluralRules& rules, - FormattedStringBuilder& output, - StandardPlural::Form& pluralForm, - UErrorCode& status); - - /** - * Formats the pattern with the value and adjusts the FieldPosition. - * TODO: Remove? - */ - static UnicodeString &format( - const SimpleFormatter &pattern, - const UnicodeString &value, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status); - -private: - SimpleFormatter *formatters[StandardPlural::COUNT]; -}; - -U_NAMESPACE_END - -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 2014-2016, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* quantityformatter.h +*/ + +#ifndef __QUANTITY_FORMATTER_H__ +#define __QUANTITY_FORMATTER_H__ + +#include "unicode/utypes.h" +#include "unicode/uobject.h" + +#if !UCONFIG_NO_FORMATTING + +#include "standardplural.h" + +U_NAMESPACE_BEGIN + +class SimpleFormatter; +class UnicodeString; +class PluralRules; +class NumberFormat; +class Formattable; +class FieldPosition; +class FormattedStringBuilder; + +/** + * A plural aware formatter that is good for expressing a single quantity and + * a unit. + *

      + * First use the add() methods to add a pattern for each plural variant. + * There must be a pattern for the "other" variant. + * Then use the format() method. + *

      + * Concurrent calls only to const methods on a QuantityFormatter object are + * safe, but concurrent const and non-const method calls on a QuantityFormatter + * object are not safe and require synchronization. + * + */ +class U_I18N_API QuantityFormatter : public UMemory { +public: + /** + * Default constructor. + */ + QuantityFormatter(); + + /** + * Copy constructor. + */ + QuantityFormatter(const QuantityFormatter& other); + + /** + * Assignment operator + */ + QuantityFormatter &operator=(const QuantityFormatter& other); + + /** + * Destructor. + */ + ~QuantityFormatter(); + + /** + * Removes all variants from this object including the "other" variant. + */ + void reset(); + + /** + * Adds a plural variant if there is none yet for the plural form. + * + * @param variant "zero", "one", "two", "few", "many", "other" + * @param rawPattern the pattern for the variant e.g "{0} meters" + * @param status any error returned here. + * @return true on success; false if status was set to a non zero error. + */ + UBool addIfAbsent(const char *variant, const UnicodeString &rawPattern, UErrorCode &status); + + /** + * returns true if this object has at least the "other" variant. + */ + UBool isValid() const; + + /** + * Gets the pattern formatter that would be used for a particular variant. + * If isValid() returns true, this method is guaranteed to return a + * non-nullptr value. + */ + const SimpleFormatter *getByVariant(const char *variant) const; + + /** + * Formats a number with this object appending the result to appendTo. + * At least the "other" variant must be added to this object for this + * method to work. + * + * @param number the single number. + * @param fmt formats the number + * @param rules computes the plural variant to use. + * @param appendTo result appended here. + * @param status any error returned here. + * @return appendTo + */ + UnicodeString &format( + const Formattable &number, + const NumberFormat &fmt, + const PluralRules &rules, + UnicodeString &appendTo, + FieldPosition &pos, + UErrorCode &status) const; + + /** + * Selects the standard plural form for the number/formatter/rules. + * Used in MeasureFormat for backwards compatibility with NumberFormat. + */ + static StandardPlural::Form selectPlural( + const Formattable &number, + const NumberFormat &fmt, + const PluralRules &rules, + UnicodeString &formattedNumber, + FieldPosition &pos, + UErrorCode &status); + + /** + * Formats a quantity and selects its plural form. The output is appended + * to a FormattedStringBuilder in order to retain field information. + * + * @param quantity The number to format. + * @param fmt The formatter to use to format the number. + * @param rules The rules to use to select the plural form of the + * formatted number. + * @param output Where to append the result of the format operation. + * @param pluralForm Output variable populated with the plural form of the + * formatted number. + * @param status Set if an error occurs. + */ + static void formatAndSelect( + double quantity, + const NumberFormat& fmt, + const PluralRules& rules, + FormattedStringBuilder& output, + StandardPlural::Form& pluralForm, + UErrorCode& status); + + /** + * Formats the pattern with the value and adjusts the FieldPosition. + * TODO: Remove? + */ + static UnicodeString &format( + const SimpleFormatter &pattern, + const UnicodeString &value, + UnicodeString &appendTo, + FieldPosition &pos, + UErrorCode &status); + +private: + SimpleFormatter *formatters[StandardPlural::COUNT]; +}; + +U_NAMESPACE_END + +#endif + +#endif diff --git a/deps/icu-small/source/i18n/rbnf.cpp b/deps/icu-small/source/i18n/rbnf.cpp index 3d0da00bdb8317..d7e5fb00fa401a 100644 --- a/deps/icu-small/source/i18n/rbnf.cpp +++ b/deps/icu-small/source/i18n/rbnf.cpp @@ -1,1991 +1,1991 @@ -// © 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 "unicode/utypes.h" -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/rbnf.h" - -#if U_HAVE_RBNF - -#include "unicode/normlzr.h" -#include "unicode/plurfmt.h" -#include "unicode/tblcoll.h" -#include "unicode/uchar.h" -#include "unicode/ucol.h" -#include "unicode/uloc.h" -#include "unicode/unum.h" -#include "unicode/ures.h" -#include "unicode/ustring.h" -#include "unicode/utf16.h" -#include "unicode/udata.h" -#include "unicode/udisplaycontext.h" -#include "unicode/brkiter.h" -#include "unicode/ucasemap.h" - -#include "cmemory.h" -#include "cstring.h" -#include "patternprops.h" -#include "uresimp.h" -#include "nfrs.h" -#include "number_decimalquantity.h" - -// debugging -// #define RBNF_DEBUG - -#ifdef RBNF_DEBUG -#include -#endif - -#define U_ICUDATA_RBNF U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "rbnf" - -static const UChar gPercentPercent[] = -{ - 0x25, 0x25, 0 -}; /* "%%" */ - -// All urbnf objects are created through openRules, so we init all of the -// Unicode string constants required by rbnf, nfrs, or nfr here. -static const UChar gLenientParse[] = -{ - 0x25, 0x25, 0x6C, 0x65, 0x6E, 0x69, 0x65, 0x6E, 0x74, 0x2D, 0x70, 0x61, 0x72, 0x73, 0x65, 0x3A, 0 -}; /* "%%lenient-parse:" */ -static const UChar gSemiColon = 0x003B; -static const UChar gSemiPercent[] = -{ - 0x3B, 0x25, 0 -}; /* ";%" */ - -#define kSomeNumberOfBitsDiv2 22 -#define kHalfMaxDouble (double)(1 << kSomeNumberOfBitsDiv2) -#define kMaxDouble (kHalfMaxDouble * kHalfMaxDouble) - -U_NAMESPACE_BEGIN - -using number::impl::DecimalQuantity; - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat) - -/* -This is a utility class. It does not use ICU's RTTI. -If ICU's RTTI is needed again, you can uncomment the RTTI code and derive from UObject. -Please make sure that intltest passes on Windows in Release mode, -since the string pooling per compilation unit will mess up how RTTI works. -The RTTI code was also removed due to lack of code coverage. -*/ -class LocalizationInfo : public UMemory { -protected: - virtual ~LocalizationInfo(); - uint32_t refcount; - -public: - LocalizationInfo() : refcount(0) {} - - LocalizationInfo* ref(void) { - ++refcount; - return this; - } - - LocalizationInfo* unref(void) { - if (refcount && --refcount == 0) { - delete this; - } - return NULL; - } - - virtual bool operator==(const LocalizationInfo* rhs) const; - inline bool operator!=(const LocalizationInfo* rhs) const { return !operator==(rhs); } - - virtual int32_t getNumberOfRuleSets(void) const = 0; - virtual const UChar* getRuleSetName(int32_t index) const = 0; - virtual int32_t getNumberOfDisplayLocales(void) const = 0; - virtual const UChar* getLocaleName(int32_t index) const = 0; - virtual const UChar* getDisplayName(int32_t localeIndex, int32_t ruleIndex) const = 0; - - virtual int32_t indexForLocale(const UChar* locale) const; - virtual int32_t indexForRuleSet(const UChar* ruleset) const; - -// virtual UClassID getDynamicClassID() const = 0; -// static UClassID getStaticClassID(void); -}; - -LocalizationInfo::~LocalizationInfo() {} - -//UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo) - -// if both strings are NULL, this returns true -static UBool -streq(const UChar* lhs, const UChar* rhs) { - if (rhs == lhs) { - return true; - } - if (lhs && rhs) { - return u_strcmp(lhs, rhs) == 0; - } - return false; -} - -bool -LocalizationInfo::operator==(const LocalizationInfo* rhs) const { - if (rhs) { - if (this == rhs) { - return true; - } - - int32_t rsc = getNumberOfRuleSets(); - if (rsc == rhs->getNumberOfRuleSets()) { - for (int i = 0; i < rsc; ++i) { - if (!streq(getRuleSetName(i), rhs->getRuleSetName(i))) { - return false; - } - } - int32_t dlc = getNumberOfDisplayLocales(); - if (dlc == rhs->getNumberOfDisplayLocales()) { - for (int i = 0; i < dlc; ++i) { - const UChar* locale = getLocaleName(i); - int32_t ix = rhs->indexForLocale(locale); - // if no locale, ix is -1, getLocaleName returns null, so streq returns false - if (!streq(locale, rhs->getLocaleName(ix))) { - return false; - } - for (int j = 0; j < rsc; ++j) { - if (!streq(getDisplayName(i, j), rhs->getDisplayName(ix, j))) { - return false; - } - } - } - return true; - } - } - } - return false; -} - -int32_t -LocalizationInfo::indexForLocale(const UChar* locale) const { - for (int i = 0; i < getNumberOfDisplayLocales(); ++i) { - if (streq(locale, getLocaleName(i))) { - return i; - } - } - return -1; -} - -int32_t -LocalizationInfo::indexForRuleSet(const UChar* ruleset) const { - if (ruleset) { - for (int i = 0; i < getNumberOfRuleSets(); ++i) { - if (streq(ruleset, getRuleSetName(i))) { - return i; - } - } - } - return -1; -} - - -typedef void (*Fn_Deleter)(void*); - -class VArray { - void** buf; - int32_t cap; - int32_t size; - Fn_Deleter deleter; -public: - VArray() : buf(NULL), cap(0), size(0), deleter(NULL) {} - - VArray(Fn_Deleter del) : buf(NULL), cap(0), size(0), deleter(del) {} - - ~VArray() { - if (deleter) { - for (int i = 0; i < size; ++i) { - (*deleter)(buf[i]); - } - } - uprv_free(buf); - } - - int32_t length() { - return size; - } - - void add(void* elem, UErrorCode& status) { - if (U_SUCCESS(status)) { - if (size == cap) { - if (cap == 0) { - cap = 1; - } else if (cap < 256) { - cap *= 2; - } else { - cap += 256; - } - if (buf == NULL) { - buf = (void**)uprv_malloc(cap * sizeof(void*)); - } else { - buf = (void**)uprv_realloc(buf, cap * sizeof(void*)); - } - if (buf == NULL) { - // if we couldn't realloc, we leak the memory we've already allocated, but we're in deep trouble anyway - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - void* start = &buf[size]; - size_t count = (cap - size) * sizeof(void*); - uprv_memset(start, 0, count); // fill with nulls, just because - } - buf[size++] = elem; - } - } - - void** release(void) { - void** result = buf; - buf = NULL; - cap = 0; - size = 0; - return result; - } -}; - -class LocDataParser; - -class StringLocalizationInfo : public LocalizationInfo { - UChar* info; - UChar*** data; - int32_t numRuleSets; - int32_t numLocales; - -friend class LocDataParser; - - StringLocalizationInfo(UChar* i, UChar*** d, int32_t numRS, int32_t numLocs) - : info(i), data(d), numRuleSets(numRS), numLocales(numLocs) - { - } - -public: - static StringLocalizationInfo* create(const UnicodeString& info, UParseError& perror, UErrorCode& status); - - virtual ~StringLocalizationInfo(); - virtual int32_t getNumberOfRuleSets(void) const override { return numRuleSets; } - virtual const UChar* getRuleSetName(int32_t index) const override; - virtual int32_t getNumberOfDisplayLocales(void) const override { return numLocales; } - virtual const UChar* getLocaleName(int32_t index) const override; - virtual const UChar* getDisplayName(int32_t localeIndex, int32_t ruleIndex) const override; - -// virtual UClassID getDynamicClassID() const; -// static UClassID getStaticClassID(void); - -private: - void init(UErrorCode& status) const; -}; - - -enum { - OPEN_ANGLE = 0x003c, /* '<' */ - CLOSE_ANGLE = 0x003e, /* '>' */ - COMMA = 0x002c, - TICK = 0x0027, - QUOTE = 0x0022, - SPACE = 0x0020 -}; - -/** - * Utility for parsing a localization string and returning a StringLocalizationInfo*. - */ -class LocDataParser { - UChar* data; - const UChar* e; - UChar* p; - UChar ch; - UParseError& pe; - UErrorCode& ec; - -public: - LocDataParser(UParseError& parseError, UErrorCode& status) - : data(NULL), e(NULL), p(NULL), ch(0xffff), pe(parseError), ec(status) {} - ~LocDataParser() {} - - /* - * On a successful parse, return a StringLocalizationInfo*, otherwise delete locData, set perror and status, - * and return NULL. The StringLocalizationInfo will adopt locData if it is created. - */ - StringLocalizationInfo* parse(UChar* data, int32_t len); - -private: - - inline void inc(void) { - ++p; - ch = 0xffff; - } - inline UBool checkInc(UChar c) { - if (p < e && (ch == c || *p == c)) { - inc(); - return true; - } - return false; - } - inline UBool check(UChar c) { - return p < e && (ch == c || *p == c); - } - inline void skipWhitespace(void) { - while (p < e && PatternProps::isWhiteSpace(ch != 0xffff ? ch : *p)) { - inc(); - } - } - inline UBool inList(UChar c, const UChar* list) const { - if (*list == SPACE && PatternProps::isWhiteSpace(c)) { - return true; - } - while (*list && *list != c) { - ++list; - } - return *list == c; - } - void parseError(const char* msg); - - StringLocalizationInfo* doParse(void); - - UChar** nextArray(int32_t& requiredLength); - UChar* nextString(void); -}; - -#ifdef RBNF_DEBUG -#define ERROR(msg) UPRV_BLOCK_MACRO_BEGIN { \ - parseError(msg); \ - return NULL; \ -} UPRV_BLOCK_MACRO_END -#define EXPLANATION_ARG explanationArg -#else -#define ERROR(msg) UPRV_BLOCK_MACRO_BEGIN { \ - parseError(NULL); \ - return NULL; \ -} UPRV_BLOCK_MACRO_END -#define EXPLANATION_ARG -#endif - - -static const UChar DQUOTE_STOPLIST[] = { - QUOTE, 0 -}; - -static const UChar SQUOTE_STOPLIST[] = { - TICK, 0 -}; - -static const UChar NOQUOTE_STOPLIST[] = { - SPACE, COMMA, CLOSE_ANGLE, OPEN_ANGLE, TICK, QUOTE, 0 -}; - -static void -DeleteFn(void* p) { - uprv_free(p); -} - -StringLocalizationInfo* -LocDataParser::parse(UChar* _data, int32_t len) { - if (U_FAILURE(ec)) { - if (_data) uprv_free(_data); - return NULL; - } - - pe.line = 0; - pe.offset = -1; - pe.postContext[0] = 0; - pe.preContext[0] = 0; - - if (_data == NULL) { - ec = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - if (len <= 0) { - ec = U_ILLEGAL_ARGUMENT_ERROR; - uprv_free(_data); - return NULL; - } - - data = _data; - e = data + len; - p = _data; - ch = 0xffff; - - return doParse(); -} - - -StringLocalizationInfo* -LocDataParser::doParse(void) { - skipWhitespace(); - if (!checkInc(OPEN_ANGLE)) { - ERROR("Missing open angle"); - } else { - VArray array(DeleteFn); - UBool mightHaveNext = true; - int32_t requiredLength = -1; - while (mightHaveNext) { - mightHaveNext = false; - UChar** elem = nextArray(requiredLength); - skipWhitespace(); - UBool haveComma = check(COMMA); - if (elem) { - array.add(elem, ec); - if (haveComma) { - inc(); - mightHaveNext = true; - } - } else if (haveComma) { - ERROR("Unexpected character"); - } - } - - skipWhitespace(); - if (!checkInc(CLOSE_ANGLE)) { - if (check(OPEN_ANGLE)) { - ERROR("Missing comma in outer array"); - } else { - ERROR("Missing close angle bracket in outer array"); - } - } - - skipWhitespace(); - if (p != e) { - ERROR("Extra text after close of localization data"); - } - - array.add(NULL, ec); - if (U_SUCCESS(ec)) { - int32_t numLocs = array.length() - 2; // subtract first, NULL - UChar*** result = (UChar***)array.release(); - - return new StringLocalizationInfo(data, result, requiredLength-2, numLocs); // subtract first, NULL - } - } - - ERROR("Unknown error"); -} - -UChar** -LocDataParser::nextArray(int32_t& requiredLength) { - if (U_FAILURE(ec)) { - return NULL; - } - - skipWhitespace(); - if (!checkInc(OPEN_ANGLE)) { - ERROR("Missing open angle"); - } - - VArray array; - UBool mightHaveNext = true; - while (mightHaveNext) { - mightHaveNext = false; - UChar* elem = nextString(); - skipWhitespace(); - UBool haveComma = check(COMMA); - if (elem) { - array.add(elem, ec); - if (haveComma) { - inc(); - mightHaveNext = true; - } - } else if (haveComma) { - ERROR("Unexpected comma"); - } - } - skipWhitespace(); - if (!checkInc(CLOSE_ANGLE)) { - if (check(OPEN_ANGLE)) { - ERROR("Missing close angle bracket in inner array"); - } else { - ERROR("Missing comma in inner array"); - } - } - - array.add(NULL, ec); - if (U_SUCCESS(ec)) { - if (requiredLength == -1) { - requiredLength = array.length() + 1; - } else if (array.length() != requiredLength) { - ec = U_ILLEGAL_ARGUMENT_ERROR; - ERROR("Array not of required length"); - } - - return (UChar**)array.release(); - } - ERROR("Unknown Error"); -} - -UChar* -LocDataParser::nextString() { - UChar* result = NULL; - - skipWhitespace(); - if (p < e) { - const UChar* terminators; - UChar c = *p; - UBool haveQuote = c == QUOTE || c == TICK; - if (haveQuote) { - inc(); - terminators = c == QUOTE ? DQUOTE_STOPLIST : SQUOTE_STOPLIST; - } else { - terminators = NOQUOTE_STOPLIST; - } - UChar* start = p; - while (p < e && !inList(*p, terminators)) ++p; - if (p == e) { - ERROR("Unexpected end of data"); - } - - UChar x = *p; - if (p > start) { - ch = x; - *p = 0x0; // terminate by writing to data - result = start; // just point into data - } - if (haveQuote) { - if (x != c) { - ERROR("Missing matching quote"); - } else if (p == start) { - ERROR("Empty string"); - } - inc(); - } else if (x == OPEN_ANGLE || x == TICK || x == QUOTE) { - ERROR("Unexpected character in string"); - } - } - - // ok for there to be no next string - return result; -} - -void LocDataParser::parseError(const char* EXPLANATION_ARG) -{ - if (!data) { - return; - } - - const UChar* start = p - U_PARSE_CONTEXT_LEN - 1; - if (start < data) { - start = data; - } - for (UChar* x = p; --x >= start;) { - if (!*x) { - start = x+1; - break; - } - } - const UChar* limit = p + U_PARSE_CONTEXT_LEN - 1; - if (limit > e) { - limit = e; - } - u_strncpy(pe.preContext, start, (int32_t)(p-start)); - pe.preContext[p-start] = 0; - u_strncpy(pe.postContext, p, (int32_t)(limit-p)); - pe.postContext[limit-p] = 0; - pe.offset = (int32_t)(p - data); - -#ifdef RBNF_DEBUG - fprintf(stderr, "%s at or near character %ld: ", EXPLANATION_ARG, p-data); - - UnicodeString msg; - msg.append(start, p - start); - msg.append((UChar)0x002f); /* SOLIDUS/SLASH */ - msg.append(p, limit-p); - msg.append(UNICODE_STRING_SIMPLE("'")); - - char buf[128]; - int32_t len = msg.extract(0, msg.length(), buf, 128); - if (len >= 128) { - buf[127] = 0; - } else { - buf[len] = 0; - } - fprintf(stderr, "%s\n", buf); - fflush(stderr); -#endif - - uprv_free(data); - data = NULL; - p = NULL; - e = NULL; - - if (U_SUCCESS(ec)) { - ec = U_PARSE_ERROR; - } -} - -//UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo) - -StringLocalizationInfo* -StringLocalizationInfo::create(const UnicodeString& info, UParseError& perror, UErrorCode& status) { - if (U_FAILURE(status)) { - return NULL; - } - - int32_t len = info.length(); - if (len == 0) { - return NULL; // no error; - } - - UChar* p = (UChar*)uprv_malloc(len * sizeof(UChar)); - if (!p) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - info.extract(p, len, status); - if (!U_FAILURE(status)) { - status = U_ZERO_ERROR; // clear warning about non-termination - } - - LocDataParser parser(perror, status); - return parser.parse(p, len); -} - -StringLocalizationInfo::~StringLocalizationInfo() { - for (UChar*** p = (UChar***)data; *p; ++p) { - // remaining data is simply pointer into our unicode string data. - if (*p) uprv_free(*p); - } - if (data) uprv_free(data); - if (info) uprv_free(info); -} - - -const UChar* -StringLocalizationInfo::getRuleSetName(int32_t index) const { - if (index >= 0 && index < getNumberOfRuleSets()) { - return data[0][index]; - } - return NULL; -} - -const UChar* -StringLocalizationInfo::getLocaleName(int32_t index) const { - if (index >= 0 && index < getNumberOfDisplayLocales()) { - return data[index+1][0]; - } - return NULL; -} - -const UChar* -StringLocalizationInfo::getDisplayName(int32_t localeIndex, int32_t ruleIndex) const { - if (localeIndex >= 0 && localeIndex < getNumberOfDisplayLocales() && - ruleIndex >= 0 && ruleIndex < getNumberOfRuleSets()) { - return data[localeIndex+1][ruleIndex+1]; - } - return NULL; -} - -// ---------- - -RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, - const UnicodeString& locs, - const Locale& alocale, UParseError& perror, UErrorCode& status) - : fRuleSets(NULL) - , ruleSetDescriptions(NULL) - , numRuleSets(0) - , defaultRuleSet(NULL) - , locale(alocale) - , collator(NULL) - , decimalFormatSymbols(NULL) - , defaultInfinityRule(NULL) - , defaultNaNRule(NULL) - , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) - , lenient(false) - , lenientParseRules(NULL) - , localizations(NULL) - , capitalizationInfoSet(false) - , capitalizationForUIListMenu(false) - , capitalizationForStandAlone(false) - , capitalizationBrkIter(NULL) -{ - LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status); - init(description, locinfo, perror, status); -} - -RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, - const UnicodeString& locs, - UParseError& perror, UErrorCode& status) - : fRuleSets(NULL) - , ruleSetDescriptions(NULL) - , numRuleSets(0) - , defaultRuleSet(NULL) - , locale(Locale::getDefault()) - , collator(NULL) - , decimalFormatSymbols(NULL) - , defaultInfinityRule(NULL) - , defaultNaNRule(NULL) - , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) - , lenient(false) - , lenientParseRules(NULL) - , localizations(NULL) - , capitalizationInfoSet(false) - , capitalizationForUIListMenu(false) - , capitalizationForStandAlone(false) - , capitalizationBrkIter(NULL) -{ - LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status); - init(description, locinfo, perror, status); -} - -RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, - LocalizationInfo* info, - const Locale& alocale, UParseError& perror, UErrorCode& status) - : fRuleSets(NULL) - , ruleSetDescriptions(NULL) - , numRuleSets(0) - , defaultRuleSet(NULL) - , locale(alocale) - , collator(NULL) - , decimalFormatSymbols(NULL) - , defaultInfinityRule(NULL) - , defaultNaNRule(NULL) - , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) - , lenient(false) - , lenientParseRules(NULL) - , localizations(NULL) - , capitalizationInfoSet(false) - , capitalizationForUIListMenu(false) - , capitalizationForStandAlone(false) - , capitalizationBrkIter(NULL) -{ - init(description, info, perror, status); -} - -RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, - UParseError& perror, - UErrorCode& status) - : fRuleSets(NULL) - , ruleSetDescriptions(NULL) - , numRuleSets(0) - , defaultRuleSet(NULL) - , locale(Locale::getDefault()) - , collator(NULL) - , decimalFormatSymbols(NULL) - , defaultInfinityRule(NULL) - , defaultNaNRule(NULL) - , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) - , lenient(false) - , lenientParseRules(NULL) - , localizations(NULL) - , capitalizationInfoSet(false) - , capitalizationForUIListMenu(false) - , capitalizationForStandAlone(false) - , capitalizationBrkIter(NULL) -{ - init(description, NULL, perror, status); -} - -RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, - const Locale& aLocale, - UParseError& perror, - UErrorCode& status) - : fRuleSets(NULL) - , ruleSetDescriptions(NULL) - , numRuleSets(0) - , defaultRuleSet(NULL) - , locale(aLocale) - , collator(NULL) - , decimalFormatSymbols(NULL) - , defaultInfinityRule(NULL) - , defaultNaNRule(NULL) - , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) - , lenient(false) - , lenientParseRules(NULL) - , localizations(NULL) - , capitalizationInfoSet(false) - , capitalizationForUIListMenu(false) - , capitalizationForStandAlone(false) - , capitalizationBrkIter(NULL) -{ - init(description, NULL, perror, status); -} - -RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale& alocale, UErrorCode& status) - : fRuleSets(NULL) - , ruleSetDescriptions(NULL) - , numRuleSets(0) - , defaultRuleSet(NULL) - , locale(alocale) - , collator(NULL) - , decimalFormatSymbols(NULL) - , defaultInfinityRule(NULL) - , defaultNaNRule(NULL) - , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) - , lenient(false) - , lenientParseRules(NULL) - , localizations(NULL) - , capitalizationInfoSet(false) - , capitalizationForUIListMenu(false) - , capitalizationForStandAlone(false) - , capitalizationBrkIter(NULL) -{ - if (U_FAILURE(status)) { - return; - } - - const char* rules_tag = "RBNFRules"; - const char* fmt_tag = ""; - switch (tag) { - case URBNF_SPELLOUT: fmt_tag = "SpelloutRules"; break; - case URBNF_ORDINAL: fmt_tag = "OrdinalRules"; break; - case URBNF_DURATION: fmt_tag = "DurationRules"; break; - case URBNF_NUMBERING_SYSTEM: fmt_tag = "NumberingSystemRules"; break; - default: status = U_ILLEGAL_ARGUMENT_ERROR; return; - } - - // TODO: read localization info from resource - LocalizationInfo* locinfo = NULL; - - UResourceBundle* nfrb = ures_open(U_ICUDATA_RBNF, locale.getName(), &status); - if (U_SUCCESS(status)) { - setLocaleIDs(ures_getLocaleByType(nfrb, ULOC_VALID_LOCALE, &status), - ures_getLocaleByType(nfrb, ULOC_ACTUAL_LOCALE, &status)); - - UResourceBundle* rbnfRules = ures_getByKeyWithFallback(nfrb, rules_tag, NULL, &status); - if (U_FAILURE(status)) { - ures_close(nfrb); - } - UResourceBundle* ruleSets = ures_getByKeyWithFallback(rbnfRules, fmt_tag, NULL, &status); - if (U_FAILURE(status)) { - ures_close(rbnfRules); - ures_close(nfrb); - return; - } - - UnicodeString desc; - while (ures_hasNext(ruleSets)) { - desc.append(ures_getNextUnicodeString(ruleSets,NULL,&status)); - } - UParseError perror; - - init(desc, locinfo, perror, status); - - ures_close(ruleSets); - ures_close(rbnfRules); - } - ures_close(nfrb); -} - -RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat& rhs) - : NumberFormat(rhs) - , fRuleSets(NULL) - , ruleSetDescriptions(NULL) - , numRuleSets(0) - , defaultRuleSet(NULL) - , locale(rhs.locale) - , collator(NULL) - , decimalFormatSymbols(NULL) - , defaultInfinityRule(NULL) - , defaultNaNRule(NULL) - , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) - , lenient(false) - , lenientParseRules(NULL) - , localizations(NULL) - , capitalizationInfoSet(false) - , capitalizationForUIListMenu(false) - , capitalizationForStandAlone(false) - , capitalizationBrkIter(NULL) -{ - this->operator=(rhs); -} - -// -------- - -RuleBasedNumberFormat& -RuleBasedNumberFormat::operator=(const RuleBasedNumberFormat& rhs) -{ - if (this == &rhs) { - return *this; - } - NumberFormat::operator=(rhs); - UErrorCode status = U_ZERO_ERROR; - dispose(); - locale = rhs.locale; - lenient = rhs.lenient; - - UParseError perror; - setDecimalFormatSymbols(*rhs.getDecimalFormatSymbols()); - init(rhs.originalDescription, rhs.localizations ? rhs.localizations->ref() : NULL, perror, status); - setDefaultRuleSet(rhs.getDefaultRuleSetName(), status); - setRoundingMode(rhs.getRoundingMode()); - - capitalizationInfoSet = rhs.capitalizationInfoSet; - capitalizationForUIListMenu = rhs.capitalizationForUIListMenu; - capitalizationForStandAlone = rhs.capitalizationForStandAlone; -#if !UCONFIG_NO_BREAK_ITERATION - capitalizationBrkIter = (rhs.capitalizationBrkIter!=NULL)? rhs.capitalizationBrkIter->clone(): NULL; -#endif - - return *this; -} - -RuleBasedNumberFormat::~RuleBasedNumberFormat() -{ - dispose(); -} - -RuleBasedNumberFormat* -RuleBasedNumberFormat::clone() const -{ - return new RuleBasedNumberFormat(*this); -} - -bool -RuleBasedNumberFormat::operator==(const Format& other) const -{ - if (this == &other) { - return true; - } - - if (typeid(*this) == typeid(other)) { - const RuleBasedNumberFormat& rhs = (const RuleBasedNumberFormat&)other; - // test for capitalization info equality is adequately handled - // by the NumberFormat test for fCapitalizationContext equality; - // the info here is just derived from that. - if (locale == rhs.locale && - lenient == rhs.lenient && - (localizations == NULL - ? rhs.localizations == NULL - : (rhs.localizations == NULL - ? false - : *localizations == rhs.localizations))) { - - NFRuleSet** p = fRuleSets; - NFRuleSet** q = rhs.fRuleSets; - if (p == NULL) { - return q == NULL; - } else if (q == NULL) { - return false; - } - while (*p && *q && (**p == **q)) { - ++p; - ++q; - } - return *q == NULL && *p == NULL; - } - } - - return false; -} - -UnicodeString -RuleBasedNumberFormat::getRules() const -{ - UnicodeString result; - if (fRuleSets != NULL) { - for (NFRuleSet** p = fRuleSets; *p; ++p) { - (*p)->appendRules(result); - } - } - return result; -} - -UnicodeString -RuleBasedNumberFormat::getRuleSetName(int32_t index) const -{ - if (localizations) { - UnicodeString string(true, localizations->getRuleSetName(index), (int32_t)-1); - return string; - } - else if (fRuleSets) { - UnicodeString result; - for (NFRuleSet** p = fRuleSets; *p; ++p) { - NFRuleSet* rs = *p; - if (rs->isPublic()) { - if (--index == -1) { - rs->getName(result); - return result; - } - } - } - } - UnicodeString empty; - return empty; -} - -int32_t -RuleBasedNumberFormat::getNumberOfRuleSetNames() const -{ - int32_t result = 0; - if (localizations) { - result = localizations->getNumberOfRuleSets(); - } - else if (fRuleSets) { - for (NFRuleSet** p = fRuleSets; *p; ++p) { - if ((**p).isPublic()) { - ++result; - } - } - } - return result; -} - -int32_t -RuleBasedNumberFormat::getNumberOfRuleSetDisplayNameLocales(void) const { - if (localizations) { - return localizations->getNumberOfDisplayLocales(); - } - return 0; -} - -Locale -RuleBasedNumberFormat::getRuleSetDisplayNameLocale(int32_t index, UErrorCode& status) const { - if (U_FAILURE(status)) { - return Locale(""); - } - if (localizations && index >= 0 && index < localizations->getNumberOfDisplayLocales()) { - UnicodeString name(true, localizations->getLocaleName(index), -1); - char buffer[64]; - int32_t cap = name.length() + 1; - char* bp = buffer; - if (cap > 64) { - bp = (char *)uprv_malloc(cap); - if (bp == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return Locale(""); - } - } - name.extract(0, name.length(), bp, cap, UnicodeString::kInvariant); - Locale retLocale(bp); - if (bp != buffer) { - uprv_free(bp); - } - return retLocale; - } - status = U_ILLEGAL_ARGUMENT_ERROR; - Locale retLocale; - return retLocale; -} - -UnicodeString -RuleBasedNumberFormat::getRuleSetDisplayName(int32_t index, const Locale& localeParam) { - if (localizations && index >= 0 && index < localizations->getNumberOfRuleSets()) { - UnicodeString localeName(localeParam.getBaseName(), -1, UnicodeString::kInvariant); - int32_t len = localeName.length(); - UChar* localeStr = localeName.getBuffer(len + 1); - while (len >= 0) { - localeStr[len] = 0; - int32_t ix = localizations->indexForLocale(localeStr); - if (ix >= 0) { - UnicodeString name(true, localizations->getDisplayName(ix, index), -1); - return name; - } - - // trim trailing portion, skipping over omitted sections - do { --len;} while (len > 0 && localeStr[len] != 0x005f); // underscore - while (len > 0 && localeStr[len-1] == 0x005F) --len; - } - UnicodeString name(true, localizations->getRuleSetName(index), -1); - return name; - } - UnicodeString bogus; - bogus.setToBogus(); - return bogus; -} - -UnicodeString -RuleBasedNumberFormat::getRuleSetDisplayName(const UnicodeString& ruleSetName, const Locale& localeParam) { - if (localizations) { - UnicodeString rsn(ruleSetName); - int32_t ix = localizations->indexForRuleSet(rsn.getTerminatedBuffer()); - return getRuleSetDisplayName(ix, localeParam); - } - UnicodeString bogus; - bogus.setToBogus(); - return bogus; -} - -NFRuleSet* -RuleBasedNumberFormat::findRuleSet(const UnicodeString& name, UErrorCode& status) const -{ - if (U_SUCCESS(status) && fRuleSets) { - for (NFRuleSet** p = fRuleSets; *p; ++p) { - NFRuleSet* rs = *p; - if (rs->isNamed(name)) { - return rs; - } - } - status = U_ILLEGAL_ARGUMENT_ERROR; - } - return NULL; -} - -UnicodeString& -RuleBasedNumberFormat::format(const DecimalQuantity &number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return appendTo; - } - DecimalQuantity copy(number); - if (copy.fitsInLong()) { - format(number.toLong(), appendTo, pos, status); - } - else { - 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. - // The DecimalFormat will provide more accurate results. - - // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J. - LocalPointer decimalFormat(NumberFormat::createInstance(locale, UNUM_DECIMAL, status), status); - if (decimalFormat.isNull()) { - return appendTo; - } - Formattable f; - LocalPointer decimalQuantity(new DecimalQuantity(number), status); - if (decimalQuantity.isNull()) { - return appendTo; - } - f.adoptDecimalQuantity(decimalQuantity.orphan()); // f now owns decimalQuantity. - decimalFormat->format(f, appendTo, pos, status); - } - } - return appendTo; -} - -UnicodeString& -RuleBasedNumberFormat::format(int32_t number, - UnicodeString& toAppendTo, - FieldPosition& pos) const -{ - return format((int64_t)number, toAppendTo, pos); -} - - -UnicodeString& -RuleBasedNumberFormat::format(int64_t number, - UnicodeString& toAppendTo, - FieldPosition& /* pos */) const -{ - if (defaultRuleSet) { - UErrorCode status = U_ZERO_ERROR; - format(number, defaultRuleSet, toAppendTo, status); - } - return toAppendTo; -} - - -UnicodeString& -RuleBasedNumberFormat::format(double number, - UnicodeString& toAppendTo, - FieldPosition& /* pos */) const -{ - UErrorCode status = U_ZERO_ERROR; - if (defaultRuleSet) { - format(number, *defaultRuleSet, toAppendTo, status); - } - return toAppendTo; -} - - -UnicodeString& -RuleBasedNumberFormat::format(int32_t number, - const UnicodeString& ruleSetName, - UnicodeString& toAppendTo, - FieldPosition& pos, - UErrorCode& status) const -{ - return format((int64_t)number, ruleSetName, toAppendTo, pos, status); -} - - -UnicodeString& -RuleBasedNumberFormat::format(int64_t number, - const UnicodeString& ruleSetName, - UnicodeString& toAppendTo, - FieldPosition& /* pos */, - UErrorCode& status) const -{ - if (U_SUCCESS(status)) { - if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) { - // throw new IllegalArgumentException("Can't use internal rule set"); - status = U_ILLEGAL_ARGUMENT_ERROR; - } else { - NFRuleSet *rs = findRuleSet(ruleSetName, status); - if (rs) { - format(number, rs, toAppendTo, status); - } - } - } - return toAppendTo; -} - - -UnicodeString& -RuleBasedNumberFormat::format(double number, - const UnicodeString& ruleSetName, - UnicodeString& toAppendTo, - FieldPosition& /* pos */, - UErrorCode& status) const -{ - if (U_SUCCESS(status)) { - if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) { - // throw new IllegalArgumentException("Can't use internal rule set"); - status = U_ILLEGAL_ARGUMENT_ERROR; - } else { - NFRuleSet *rs = findRuleSet(ruleSetName, status); - if (rs) { - format(number, *rs, toAppendTo, status); - } - } - } - return toAppendTo; -} - -void -RuleBasedNumberFormat::format(double number, - NFRuleSet& rs, - UnicodeString& toAppendTo, - UErrorCode& status) const -{ - int32_t startPos = toAppendTo.length(); - if (getRoundingMode() != DecimalFormat::ERoundingMode::kRoundUnnecessary && !uprv_isNaN(number) && !uprv_isInfinite(number)) { - 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); -} - -/** - * Bottleneck through which all the public format() methods - * that take a long pass. By the time we get here, we know - * which rule set we're using to do the formatting. - * @param number The number to format - * @param ruleSet The rule set to use to format the number - * @return The text that resulted from formatting the number - */ -UnicodeString& -RuleBasedNumberFormat::format(int64_t number, NFRuleSet *ruleSet, UnicodeString& toAppendTo, UErrorCode& status) const -{ - // all API format() routines that take a double vector through - // here. We have these two identical functions-- one taking a - // double and one taking a long-- the couple digits of precision - // that long has but double doesn't (both types are 8 bytes long, - // but double has to borrow some of the mantissa bits to hold - // the exponent). - // Create an empty string buffer where the result will - // be built, and pass it to the rule set (along with an insertion - // position of 0 and the number being formatted) to the rule set - // for formatting - - if (U_SUCCESS(status)) { - if (number == U_INT64_MIN) { - // We can't handle this value right now. Provide an accurate default value. - - // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J. - NumberFormat *decimalFormat = NumberFormat::createInstance(locale, UNUM_DECIMAL, status); - if (decimalFormat == nullptr) { - return toAppendTo; - } - Formattable f; - FieldPosition pos(FieldPosition::DONT_CARE); - DecimalQuantity *decimalQuantity = new DecimalQuantity(); - if (decimalQuantity == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - delete decimalFormat; - return toAppendTo; - } - decimalQuantity->setToLong(number); - f.adoptDecimalQuantity(decimalQuantity); // f now owns decimalQuantity. - decimalFormat->format(f, toAppendTo, pos, status); - delete decimalFormat; - } - else { - int32_t startPos = toAppendTo.length(); - ruleSet->format(number, toAppendTo, toAppendTo.length(), 0, status); - adjustForCapitalizationContext(startPos, toAppendTo, status); - } - } - return toAppendTo; -} - -UnicodeString& -RuleBasedNumberFormat::adjustForCapitalizationContext(int32_t startPos, - UnicodeString& currentResult, - UErrorCode& status) const -{ -#if !UCONFIG_NO_BREAK_ITERATION - UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status); - if (capitalizationContext != UDISPCTX_CAPITALIZATION_NONE && startPos == 0 && currentResult.length() > 0) { - // capitalize currentResult according to context - UChar32 ch = currentResult.char32At(0); - if (u_islower(ch) && U_SUCCESS(status) && capitalizationBrkIter != NULL && - ( capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || - (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForUIListMenu) || - (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone)) ) { - // titlecase first word of currentResult, here use sentence iterator unlike current implementations - // in LocaleDisplayNamesImpl::adjustForUsageAndContext and RelativeDateFormat::format - currentResult.toTitle(capitalizationBrkIter, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); - } - } -#endif - return currentResult; -} - - -void -RuleBasedNumberFormat::parse(const UnicodeString& text, - Formattable& result, - ParsePosition& parsePosition) const -{ - if (!fRuleSets) { - parsePosition.setErrorIndex(0); - return; - } - - UnicodeString workingText(text, parsePosition.getIndex()); - ParsePosition workingPos(0); - - ParsePosition high_pp(0); - Formattable high_result; - - for (NFRuleSet** p = fRuleSets; *p; ++p) { - NFRuleSet *rp = *p; - if (rp->isPublic() && rp->isParseable()) { - ParsePosition working_pp(0); - Formattable working_result; - - rp->parse(workingText, working_pp, kMaxDouble, 0, working_result); - if (working_pp.getIndex() > high_pp.getIndex()) { - high_pp = working_pp; - high_result = working_result; - - if (high_pp.getIndex() == workingText.length()) { - break; - } - } - } - } - - int32_t startIndex = parsePosition.getIndex(); - parsePosition.setIndex(startIndex + high_pp.getIndex()); - if (high_pp.getIndex() > 0) { - parsePosition.setErrorIndex(-1); - } else { - int32_t errorIndex = (high_pp.getErrorIndex()>0)? high_pp.getErrorIndex(): 0; - parsePosition.setErrorIndex(startIndex + errorIndex); - } - result = high_result; - if (result.getType() == Formattable::kDouble) { - double d = result.getDouble(); - if (!uprv_isNaN(d) && d == uprv_trunc(d) && INT32_MIN <= d && d <= INT32_MAX) { - // Note: casting a double to an int when the double is too large or small - // to fit the destination is undefined behavior. The explicit range checks, - // above, are required. Just casting and checking the result value is undefined. - result.setLong(static_cast(d)); - } - } -} - -#if !UCONFIG_NO_COLLATION - -void -RuleBasedNumberFormat::setLenient(UBool enabled) -{ - lenient = enabled; - if (!enabled && collator) { - delete collator; - collator = NULL; - } -} - -#endif - -void -RuleBasedNumberFormat::setDefaultRuleSet(const UnicodeString& ruleSetName, UErrorCode& status) { - if (U_SUCCESS(status)) { - if (ruleSetName.isEmpty()) { - if (localizations) { - UnicodeString name(true, localizations->getRuleSetName(0), -1); - defaultRuleSet = findRuleSet(name, status); - } else { - initDefaultRuleSet(); - } - } else if (ruleSetName.startsWith(UNICODE_STRING_SIMPLE("%%"))) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } else { - NFRuleSet* result = findRuleSet(ruleSetName, status); - if (result != NULL) { - defaultRuleSet = result; - } - } - } -} - -UnicodeString -RuleBasedNumberFormat::getDefaultRuleSetName() const { - UnicodeString result; - if (defaultRuleSet && defaultRuleSet->isPublic()) { - defaultRuleSet->getName(result); - } else { - result.setToBogus(); - } - return result; -} - -void -RuleBasedNumberFormat::initDefaultRuleSet() -{ - defaultRuleSet = NULL; - if (!fRuleSets) { - return; - } - - const UnicodeString spellout(UNICODE_STRING_SIMPLE("%spellout-numbering")); - const UnicodeString ordinal(UNICODE_STRING_SIMPLE("%digits-ordinal")); - const UnicodeString duration(UNICODE_STRING_SIMPLE("%duration")); - - NFRuleSet**p = &fRuleSets[0]; - while (*p) { - if ((*p)->isNamed(spellout) || (*p)->isNamed(ordinal) || (*p)->isNamed(duration)) { - defaultRuleSet = *p; - return; - } else { - ++p; - } - } - - defaultRuleSet = *--p; - if (!defaultRuleSet->isPublic()) { - while (p != fRuleSets) { - if ((*--p)->isPublic()) { - defaultRuleSet = *p; - break; - } - } - } -} - - -void -RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* localizationInfos, - UParseError& pErr, UErrorCode& status) -{ - // TODO: implement UParseError - uprv_memset(&pErr, 0, sizeof(UParseError)); - // Note: this can leave ruleSets == NULL, so remaining code should check - if (U_FAILURE(status)) { - return; - } - - initializeDecimalFormatSymbols(status); - initializeDefaultInfinityRule(status); - initializeDefaultNaNRule(status); - if (U_FAILURE(status)) { - return; - } - - this->localizations = localizationInfos == NULL ? NULL : localizationInfos->ref(); - - UnicodeString description(rules); - if (!description.length()) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - // start by stripping the trailing whitespace from all the rules - // (this is all the whitespace following each semicolon in the - // description). This allows us to look for rule-set boundaries - // by searching for ";%" without having to worry about whitespace - // between the ; and the % - stripWhitespace(description); - - // check to see if there's a set of lenient-parse rules. If there - // is, pull them out into our temporary holding place for them, - // and delete them from the description before the real desciption- - // parsing code sees them - int32_t lp = description.indexOf(gLenientParse, -1, 0); - if (lp != -1) { - // we've got to make sure we're not in the middle of a rule - // (where "%%lenient-parse" would actually get treated as - // rule text) - if (lp == 0 || description.charAt(lp - 1) == gSemiColon) { - // locate the beginning and end of the actual collation - // rules (there may be whitespace between the name and - // the first token in the description) - int lpEnd = description.indexOf(gSemiPercent, 2, lp); - - if (lpEnd == -1) { - lpEnd = description.length() - 1; - } - int lpStart = lp + u_strlen(gLenientParse); - while (PatternProps::isWhiteSpace(description.charAt(lpStart))) { - ++lpStart; - } - - // copy out the lenient-parse rules and delete them - // from the description - lenientParseRules = new UnicodeString(); - /* test for NULL */ - if (lenientParseRules == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - lenientParseRules->setTo(description, lpStart, lpEnd - lpStart); - - description.remove(lp, lpEnd + 1 - lp); - } - } - - // pre-flight parsing the description and count the number of - // rule sets (";%" marks the end of one rule set and the beginning - // of the next) - numRuleSets = 0; - for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, p)) { - ++numRuleSets; - ++p; - } - ++numRuleSets; - - // our rule list is an array of the appropriate size - fRuleSets = (NFRuleSet **)uprv_malloc((numRuleSets + 1) * sizeof(NFRuleSet *)); - /* test for NULL */ - if (fRuleSets == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - for (int i = 0; i <= numRuleSets; ++i) { - fRuleSets[i] = NULL; - } - - // divide up the descriptions into individual rule-set descriptions - // and store them in a temporary array. At each step, we also - // new up a rule set, but all this does is initialize its name - // and remove it from its description. We can't actually parse - // the rest of the descriptions and finish initializing everything - // because we have to know the names and locations of all the rule - // sets before we can actually set everything up - if(!numRuleSets) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - ruleSetDescriptions = new UnicodeString[numRuleSets]; - if (ruleSetDescriptions == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - { - int curRuleSet = 0; - int32_t start = 0; - for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, start)) { - ruleSetDescriptions[curRuleSet].setTo(description, start, p + 1 - start); - fRuleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status); - if (fRuleSets[curRuleSet] == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - ++curRuleSet; - start = p + 1; - } - ruleSetDescriptions[curRuleSet].setTo(description, start, description.length() - start); - fRuleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status); - if (fRuleSets[curRuleSet] == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - - // now we can take note of the formatter's default rule set, which - // is the last public rule set in the description (it's the last - // rather than the first so that a user can create a new formatter - // from an existing formatter and change its default behavior just - // by appending more rule sets to the end) - - // {dlf} Initialization of a fraction rule set requires the default rule - // set to be known. For purposes of initialization, this is always the - // last public rule set, no matter what the localization data says. - initDefaultRuleSet(); - - // finally, we can go back through the temporary descriptions - // list and finish setting up the substructure (and we throw - // away the temporary descriptions as we go) - { - for (int i = 0; i < numRuleSets; i++) { - fRuleSets[i]->parseRules(ruleSetDescriptions[i], status); - } - } - - // Now that the rules are initialized, the 'real' default rule - // set can be adjusted by the localization data. - - // The C code keeps the localization array as is, rather than building - // a separate array of the public rule set names, so we have less work - // to do here-- but we still need to check the names. - - if (localizationInfos) { - // confirm the names, if any aren't in the rules, that's an error - // it is ok if the rules contain public rule sets that are not in this list - for (int32_t i = 0; i < localizationInfos->getNumberOfRuleSets(); ++i) { - UnicodeString name(true, localizationInfos->getRuleSetName(i), -1); - NFRuleSet* rs = findRuleSet(name, status); - if (rs == NULL) { - break; // error - } - if (i == 0) { - defaultRuleSet = rs; - } - } - } else { - defaultRuleSet = getDefaultRuleSet(); - } - originalDescription = rules; -} - -// override the NumberFormat implementation in order to -// lazily initialize relevant items -void -RuleBasedNumberFormat::setContext(UDisplayContext value, UErrorCode& status) -{ - NumberFormat::setContext(value, status); - if (U_SUCCESS(status)) { - if (!capitalizationInfoSet && - (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE)) { - initCapitalizationContextInfo(locale); - capitalizationInfoSet = true; - } -#if !UCONFIG_NO_BREAK_ITERATION - if ( capitalizationBrkIter == NULL && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || - (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForUIListMenu) || - (value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone)) ) { - status = U_ZERO_ERROR; - capitalizationBrkIter = BreakIterator::createSentenceInstance(locale, status); - if (U_FAILURE(status)) { - delete capitalizationBrkIter; - capitalizationBrkIter = NULL; - } - } -#endif - } -} - -void -RuleBasedNumberFormat::initCapitalizationContextInfo(const Locale& thelocale) -{ -#if !UCONFIG_NO_BREAK_ITERATION - const char * localeID = (thelocale != NULL)? thelocale.getBaseName(): NULL; - UErrorCode status = U_ZERO_ERROR; - UResourceBundle *rb = ures_open(NULL, localeID, &status); - rb = ures_getByKeyWithFallback(rb, "contextTransforms", rb, &status); - rb = ures_getByKeyWithFallback(rb, "number-spellout", rb, &status); - if (U_SUCCESS(status) && rb != NULL) { - int32_t len = 0; - const int32_t * intVector = ures_getIntVector(rb, &len, &status); - if (U_SUCCESS(status) && intVector != NULL && len >= 2) { - capitalizationForUIListMenu = static_cast(intVector[0]); - capitalizationForStandAlone = static_cast(intVector[1]); - } - } - ures_close(rb); -#endif -} - -void -RuleBasedNumberFormat::stripWhitespace(UnicodeString& description) -{ - // iterate through the characters... - UnicodeString result; - - int start = 0; - while (start != -1 && start < description.length()) { - // seek to the first non-whitespace character... - while (start < description.length() - && PatternProps::isWhiteSpace(description.charAt(start))) { - ++start; - } - - // locate the next semicolon in the text and copy the text from - // our current position up to that semicolon into the result - int32_t p = description.indexOf(gSemiColon, start); - if (p == -1) { - // or if we don't find a semicolon, just copy the rest of - // the string into the result - result.append(description, start, description.length() - start); - start = -1; - } - else if (p < description.length()) { - result.append(description, start, p + 1 - start); - start = p + 1; - } - - // when we get here, we've seeked off the end of the string, and - // we terminate the loop (we continue until *start* is -1 rather - // than until *p* is -1, because otherwise we'd miss the last - // rule in the description) - else { - start = -1; - } - } - - description.setTo(result); -} - - -void -RuleBasedNumberFormat::dispose() -{ - if (fRuleSets) { - for (NFRuleSet** p = fRuleSets; *p; ++p) { - delete *p; - } - uprv_free(fRuleSets); - fRuleSets = NULL; - } - - if (ruleSetDescriptions) { - delete [] ruleSetDescriptions; - ruleSetDescriptions = NULL; - } - -#if !UCONFIG_NO_COLLATION - delete collator; -#endif - collator = NULL; - - delete decimalFormatSymbols; - decimalFormatSymbols = NULL; - - delete defaultInfinityRule; - defaultInfinityRule = NULL; - - delete defaultNaNRule; - defaultNaNRule = NULL; - - delete lenientParseRules; - lenientParseRules = NULL; - -#if !UCONFIG_NO_BREAK_ITERATION - delete capitalizationBrkIter; - capitalizationBrkIter = NULL; -#endif - - if (localizations) { - localizations = localizations->unref(); - } -} - - -//----------------------------------------------------------------------- -// package-internal API -//----------------------------------------------------------------------- - -/** - * Returns the collator to use for lenient parsing. The collator is lazily created: - * this function creates it the first time it's called. - * @return The collator to use for lenient parsing, or null if lenient parsing - * is turned off. -*/ -const RuleBasedCollator* -RuleBasedNumberFormat::getCollator() const -{ -#if !UCONFIG_NO_COLLATION - if (!fRuleSets) { - return NULL; - } - - // lazy-evaluate the collator - if (collator == NULL && lenient) { - // create a default collator based on the formatter's locale, - // then pull out that collator's rules, append any additional - // rules specified in the description, and create a _new_ - // collator based on the combination of those rules - - UErrorCode status = U_ZERO_ERROR; - - Collator* temp = Collator::createInstance(locale, status); - RuleBasedCollator* newCollator; - if (U_SUCCESS(status) && (newCollator = dynamic_cast(temp)) != NULL) { - if (lenientParseRules) { - UnicodeString rules(newCollator->getRules()); - rules.append(*lenientParseRules); - - newCollator = new RuleBasedCollator(rules, status); - // Exit if newCollator could not be created. - if (newCollator == NULL) { - return NULL; - } - } else { - temp = NULL; - } - if (U_SUCCESS(status)) { - newCollator->setAttribute(UCOL_DECOMPOSITION_MODE, UCOL_ON, status); - // cast away const - ((RuleBasedNumberFormat*)this)->collator = newCollator; - } else { - delete newCollator; - } - } - delete temp; - } -#endif - - // if lenient-parse mode is off, this will be null - // (see setLenientParseMode()) - return collator; -} - - -DecimalFormatSymbols* -RuleBasedNumberFormat::initializeDecimalFormatSymbols(UErrorCode &status) -{ - // lazy-evaluate the DecimalFormatSymbols object. This object - // is shared by all DecimalFormat instances belonging to this - // formatter - if (decimalFormatSymbols == nullptr) { - LocalPointer temp(new DecimalFormatSymbols(locale, status), status); - if (U_SUCCESS(status)) { - decimalFormatSymbols = temp.orphan(); - } - } - return decimalFormatSymbols; -} - -/** - * Returns the DecimalFormatSymbols object that should be used by all DecimalFormat - * instances owned by this formatter. -*/ -const DecimalFormatSymbols* -RuleBasedNumberFormat::getDecimalFormatSymbols() const -{ - return decimalFormatSymbols; -} - -NFRule* -RuleBasedNumberFormat::initializeDefaultInfinityRule(UErrorCode &status) -{ - if (U_FAILURE(status)) { - return nullptr; - } - if (defaultInfinityRule == NULL) { - UnicodeString rule(UNICODE_STRING_SIMPLE("Inf: ")); - rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kInfinitySymbol)); - LocalPointer temp(new NFRule(this, rule, status), status); - if (U_SUCCESS(status)) { - defaultInfinityRule = temp.orphan(); - } - } - return defaultInfinityRule; -} - -const NFRule* -RuleBasedNumberFormat::getDefaultInfinityRule() const -{ - return defaultInfinityRule; -} - -NFRule* -RuleBasedNumberFormat::initializeDefaultNaNRule(UErrorCode &status) -{ - if (U_FAILURE(status)) { - return nullptr; - } - if (defaultNaNRule == nullptr) { - UnicodeString rule(UNICODE_STRING_SIMPLE("NaN: ")); - rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kNaNSymbol)); - LocalPointer temp(new NFRule(this, rule, status), status); - if (U_SUCCESS(status)) { - defaultNaNRule = temp.orphan(); - } - } - return defaultNaNRule; -} - -const NFRule* -RuleBasedNumberFormat::getDefaultNaNRule() const -{ - return defaultNaNRule; -} - -// De-owning the current localized symbols and adopt the new symbols. -void -RuleBasedNumberFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) -{ - if (symbolsToAdopt == NULL) { - return; // do not allow caller to set decimalFormatSymbols to NULL - } - - if (decimalFormatSymbols != NULL) { - delete decimalFormatSymbols; - } - - decimalFormatSymbols = symbolsToAdopt; - - { - // Apply the new decimalFormatSymbols by reparsing the rulesets - UErrorCode status = U_ZERO_ERROR; - - delete defaultInfinityRule; - defaultInfinityRule = NULL; - initializeDefaultInfinityRule(status); // Reset with the new DecimalFormatSymbols - - delete defaultNaNRule; - defaultNaNRule = NULL; - initializeDefaultNaNRule(status); // Reset with the new DecimalFormatSymbols - - if (fRuleSets) { - for (int32_t i = 0; i < numRuleSets; i++) { - fRuleSets[i]->setDecimalFormatSymbols(*symbolsToAdopt, status); - } - } - } -} - -// Setting the symbols is equivalent to adopting a newly created localized symbols. -void -RuleBasedNumberFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) -{ - adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols)); -} - -PluralFormat * -RuleBasedNumberFormat::createPluralFormat(UPluralType pluralType, - const UnicodeString &pattern, - UErrorCode& status) const -{ - auto *pf = new PluralFormat(locale, pluralType, pattern, status); - if (pf == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return pf; -} - -/** - * Get the rounding mode. - * @return A rounding mode - */ -DecimalFormat::ERoundingMode RuleBasedNumberFormat::getRoundingMode() const { - return fRoundingMode; -} - -/** - * Set the rounding mode. This has no effect unless the rounding - * increment is greater than zero. - * @param roundingMode A rounding mode - */ -void RuleBasedNumberFormat::setRoundingMode(DecimalFormat::ERoundingMode roundingMode) { - fRoundingMode = roundingMode; -} - -U_NAMESPACE_END - -/* U_HAVE_RBNF */ -#endif +// © 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 "unicode/utypes.h" +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/rbnf.h" + +#if U_HAVE_RBNF + +#include "unicode/normlzr.h" +#include "unicode/plurfmt.h" +#include "unicode/tblcoll.h" +#include "unicode/uchar.h" +#include "unicode/ucol.h" +#include "unicode/uloc.h" +#include "unicode/unum.h" +#include "unicode/ures.h" +#include "unicode/ustring.h" +#include "unicode/utf16.h" +#include "unicode/udata.h" +#include "unicode/udisplaycontext.h" +#include "unicode/brkiter.h" +#include "unicode/ucasemap.h" + +#include "cmemory.h" +#include "cstring.h" +#include "patternprops.h" +#include "uresimp.h" +#include "nfrs.h" +#include "number_decimalquantity.h" + +// debugging +// #define RBNF_DEBUG + +#ifdef RBNF_DEBUG +#include +#endif + +#define U_ICUDATA_RBNF U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "rbnf" + +static const char16_t gPercentPercent[] = +{ + 0x25, 0x25, 0 +}; /* "%%" */ + +// All urbnf objects are created through openRules, so we init all of the +// Unicode string constants required by rbnf, nfrs, or nfr here. +static const char16_t gLenientParse[] = +{ + 0x25, 0x25, 0x6C, 0x65, 0x6E, 0x69, 0x65, 0x6E, 0x74, 0x2D, 0x70, 0x61, 0x72, 0x73, 0x65, 0x3A, 0 +}; /* "%%lenient-parse:" */ +static const char16_t gSemiColon = 0x003B; +static const char16_t gSemiPercent[] = +{ + 0x3B, 0x25, 0 +}; /* ";%" */ + +#define kSomeNumberOfBitsDiv2 22 +#define kHalfMaxDouble (double)(1 << kSomeNumberOfBitsDiv2) +#define kMaxDouble (kHalfMaxDouble * kHalfMaxDouble) + +U_NAMESPACE_BEGIN + +using number::impl::DecimalQuantity; + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat) + +/* +This is a utility class. It does not use ICU's RTTI. +If ICU's RTTI is needed again, you can uncomment the RTTI code and derive from UObject. +Please make sure that intltest passes on Windows in Release mode, +since the string pooling per compilation unit will mess up how RTTI works. +The RTTI code was also removed due to lack of code coverage. +*/ +class LocalizationInfo : public UMemory { +protected: + virtual ~LocalizationInfo(); + uint32_t refcount; + +public: + LocalizationInfo() : refcount(0) {} + + LocalizationInfo* ref() { + ++refcount; + return this; + } + + LocalizationInfo* unref() { + if (refcount && --refcount == 0) { + delete this; + } + return nullptr; + } + + virtual bool operator==(const LocalizationInfo* rhs) const; + inline bool operator!=(const LocalizationInfo* rhs) const { return !operator==(rhs); } + + virtual int32_t getNumberOfRuleSets() const = 0; + virtual const char16_t* getRuleSetName(int32_t index) const = 0; + virtual int32_t getNumberOfDisplayLocales() const = 0; + virtual const char16_t* getLocaleName(int32_t index) const = 0; + virtual const char16_t* getDisplayName(int32_t localeIndex, int32_t ruleIndex) const = 0; + + virtual int32_t indexForLocale(const char16_t* locale) const; + virtual int32_t indexForRuleSet(const char16_t* ruleset) const; + +// virtual UClassID getDynamicClassID() const = 0; +// static UClassID getStaticClassID(); +}; + +LocalizationInfo::~LocalizationInfo() {} + +//UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo) + +// if both strings are nullptr, this returns true +static UBool +streq(const char16_t* lhs, const char16_t* rhs) { + if (rhs == lhs) { + return true; + } + if (lhs && rhs) { + return u_strcmp(lhs, rhs) == 0; + } + return false; +} + +bool +LocalizationInfo::operator==(const LocalizationInfo* rhs) const { + if (rhs) { + if (this == rhs) { + return true; + } + + int32_t rsc = getNumberOfRuleSets(); + if (rsc == rhs->getNumberOfRuleSets()) { + for (int i = 0; i < rsc; ++i) { + if (!streq(getRuleSetName(i), rhs->getRuleSetName(i))) { + return false; + } + } + int32_t dlc = getNumberOfDisplayLocales(); + if (dlc == rhs->getNumberOfDisplayLocales()) { + for (int i = 0; i < dlc; ++i) { + const char16_t* locale = getLocaleName(i); + int32_t ix = rhs->indexForLocale(locale); + // if no locale, ix is -1, getLocaleName returns null, so streq returns false + if (!streq(locale, rhs->getLocaleName(ix))) { + return false; + } + for (int j = 0; j < rsc; ++j) { + if (!streq(getDisplayName(i, j), rhs->getDisplayName(ix, j))) { + return false; + } + } + } + return true; + } + } + } + return false; +} + +int32_t +LocalizationInfo::indexForLocale(const char16_t* locale) const { + for (int i = 0; i < getNumberOfDisplayLocales(); ++i) { + if (streq(locale, getLocaleName(i))) { + return i; + } + } + return -1; +} + +int32_t +LocalizationInfo::indexForRuleSet(const char16_t* ruleset) const { + if (ruleset) { + for (int i = 0; i < getNumberOfRuleSets(); ++i) { + if (streq(ruleset, getRuleSetName(i))) { + return i; + } + } + } + return -1; +} + + +typedef void (*Fn_Deleter)(void*); + +class VArray { + void** buf; + int32_t cap; + int32_t size; + Fn_Deleter deleter; +public: + VArray() : buf(nullptr), cap(0), size(0), deleter(nullptr) {} + + VArray(Fn_Deleter del) : buf(nullptr), cap(0), size(0), deleter(del) {} + + ~VArray() { + if (deleter) { + for (int i = 0; i < size; ++i) { + (*deleter)(buf[i]); + } + } + uprv_free(buf); + } + + int32_t length() { + return size; + } + + void add(void* elem, UErrorCode& status) { + if (U_SUCCESS(status)) { + if (size == cap) { + if (cap == 0) { + cap = 1; + } else if (cap < 256) { + cap *= 2; + } else { + cap += 256; + } + if (buf == nullptr) { + buf = (void**)uprv_malloc(cap * sizeof(void*)); + } else { + buf = (void**)uprv_realloc(buf, cap * sizeof(void*)); + } + if (buf == nullptr) { + // if we couldn't realloc, we leak the memory we've already allocated, but we're in deep trouble anyway + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + void* start = &buf[size]; + size_t count = (cap - size) * sizeof(void*); + uprv_memset(start, 0, count); // fill with nulls, just because + } + buf[size++] = elem; + } + } + + void** release() { + void** result = buf; + buf = nullptr; + cap = 0; + size = 0; + return result; + } +}; + +class LocDataParser; + +class StringLocalizationInfo : public LocalizationInfo { + char16_t* info; + char16_t*** data; + int32_t numRuleSets; + int32_t numLocales; + +friend class LocDataParser; + + StringLocalizationInfo(char16_t* i, char16_t*** d, int32_t numRS, int32_t numLocs) + : info(i), data(d), numRuleSets(numRS), numLocales(numLocs) + { + } + +public: + static StringLocalizationInfo* create(const UnicodeString& info, UParseError& perror, UErrorCode& status); + + virtual ~StringLocalizationInfo(); + virtual int32_t getNumberOfRuleSets() const override { return numRuleSets; } + virtual const char16_t* getRuleSetName(int32_t index) const override; + virtual int32_t getNumberOfDisplayLocales() const override { return numLocales; } + virtual const char16_t* getLocaleName(int32_t index) const override; + virtual const char16_t* getDisplayName(int32_t localeIndex, int32_t ruleIndex) const override; + +// virtual UClassID getDynamicClassID() const; +// static UClassID getStaticClassID(); + +private: + void init(UErrorCode& status) const; +}; + + +enum { + OPEN_ANGLE = 0x003c, /* '<' */ + CLOSE_ANGLE = 0x003e, /* '>' */ + COMMA = 0x002c, + TICK = 0x0027, + QUOTE = 0x0022, + SPACE = 0x0020 +}; + +/** + * Utility for parsing a localization string and returning a StringLocalizationInfo*. + */ +class LocDataParser { + char16_t* data; + const char16_t* e; + char16_t* p; + char16_t ch; + UParseError& pe; + UErrorCode& ec; + +public: + LocDataParser(UParseError& parseError, UErrorCode& status) + : data(nullptr), e(nullptr), p(nullptr), ch(0xffff), pe(parseError), ec(status) {} + ~LocDataParser() {} + + /* + * On a successful parse, return a StringLocalizationInfo*, otherwise delete locData, set perror and status, + * and return nullptr. The StringLocalizationInfo will adopt locData if it is created. + */ + StringLocalizationInfo* parse(char16_t* data, int32_t len); + +private: + + inline void inc() { + ++p; + ch = 0xffff; + } + inline UBool checkInc(char16_t c) { + if (p < e && (ch == c || *p == c)) { + inc(); + return true; + } + return false; + } + inline UBool check(char16_t c) { + return p < e && (ch == c || *p == c); + } + inline void skipWhitespace() { + while (p < e && PatternProps::isWhiteSpace(ch != 0xffff ? ch : *p)) { + inc(); + } + } + inline UBool inList(char16_t c, const char16_t* list) const { + if (*list == SPACE && PatternProps::isWhiteSpace(c)) { + return true; + } + while (*list && *list != c) { + ++list; + } + return *list == c; + } + void parseError(const char* msg); + + StringLocalizationInfo* doParse(); + + char16_t** nextArray(int32_t& requiredLength); + char16_t* nextString(); +}; + +#ifdef RBNF_DEBUG +#define ERROR(msg) UPRV_BLOCK_MACRO_BEGIN { \ + parseError(msg); \ + return nullptr; \ +} UPRV_BLOCK_MACRO_END +#define EXPLANATION_ARG explanationArg +#else +#define ERROR(msg) UPRV_BLOCK_MACRO_BEGIN { \ + parseError(nullptr); \ + return nullptr; \ +} UPRV_BLOCK_MACRO_END +#define EXPLANATION_ARG +#endif + + +static const char16_t DQUOTE_STOPLIST[] = { + QUOTE, 0 +}; + +static const char16_t SQUOTE_STOPLIST[] = { + TICK, 0 +}; + +static const char16_t NOQUOTE_STOPLIST[] = { + SPACE, COMMA, CLOSE_ANGLE, OPEN_ANGLE, TICK, QUOTE, 0 +}; + +static void +DeleteFn(void* p) { + uprv_free(p); +} + +StringLocalizationInfo* +LocDataParser::parse(char16_t* _data, int32_t len) { + if (U_FAILURE(ec)) { + if (_data) uprv_free(_data); + return nullptr; + } + + pe.line = 0; + pe.offset = -1; + pe.postContext[0] = 0; + pe.preContext[0] = 0; + + if (_data == nullptr) { + ec = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + if (len <= 0) { + ec = U_ILLEGAL_ARGUMENT_ERROR; + uprv_free(_data); + return nullptr; + } + + data = _data; + e = data + len; + p = _data; + ch = 0xffff; + + return doParse(); +} + + +StringLocalizationInfo* +LocDataParser::doParse() { + skipWhitespace(); + if (!checkInc(OPEN_ANGLE)) { + ERROR("Missing open angle"); + } else { + VArray array(DeleteFn); + UBool mightHaveNext = true; + int32_t requiredLength = -1; + while (mightHaveNext) { + mightHaveNext = false; + char16_t** elem = nextArray(requiredLength); + skipWhitespace(); + UBool haveComma = check(COMMA); + if (elem) { + array.add(elem, ec); + if (haveComma) { + inc(); + mightHaveNext = true; + } + } else if (haveComma) { + ERROR("Unexpected character"); + } + } + + skipWhitespace(); + if (!checkInc(CLOSE_ANGLE)) { + if (check(OPEN_ANGLE)) { + ERROR("Missing comma in outer array"); + } else { + ERROR("Missing close angle bracket in outer array"); + } + } + + skipWhitespace(); + if (p != e) { + ERROR("Extra text after close of localization data"); + } + + array.add(nullptr, ec); + if (U_SUCCESS(ec)) { + int32_t numLocs = array.length() - 2; // subtract first, nullptr + char16_t*** result = (char16_t***)array.release(); + + return new StringLocalizationInfo(data, result, requiredLength-2, numLocs); // subtract first, nullptr + } + } + + ERROR("Unknown error"); +} + +char16_t** +LocDataParser::nextArray(int32_t& requiredLength) { + if (U_FAILURE(ec)) { + return nullptr; + } + + skipWhitespace(); + if (!checkInc(OPEN_ANGLE)) { + ERROR("Missing open angle"); + } + + VArray array; + UBool mightHaveNext = true; + while (mightHaveNext) { + mightHaveNext = false; + char16_t* elem = nextString(); + skipWhitespace(); + UBool haveComma = check(COMMA); + if (elem) { + array.add(elem, ec); + if (haveComma) { + inc(); + mightHaveNext = true; + } + } else if (haveComma) { + ERROR("Unexpected comma"); + } + } + skipWhitespace(); + if (!checkInc(CLOSE_ANGLE)) { + if (check(OPEN_ANGLE)) { + ERROR("Missing close angle bracket in inner array"); + } else { + ERROR("Missing comma in inner array"); + } + } + + array.add(nullptr, ec); + if (U_SUCCESS(ec)) { + if (requiredLength == -1) { + requiredLength = array.length() + 1; + } else if (array.length() != requiredLength) { + ec = U_ILLEGAL_ARGUMENT_ERROR; + ERROR("Array not of required length"); + } + + return (char16_t**)array.release(); + } + ERROR("Unknown Error"); +} + +char16_t* +LocDataParser::nextString() { + char16_t* result = nullptr; + + skipWhitespace(); + if (p < e) { + const char16_t* terminators; + char16_t c = *p; + UBool haveQuote = c == QUOTE || c == TICK; + if (haveQuote) { + inc(); + terminators = c == QUOTE ? DQUOTE_STOPLIST : SQUOTE_STOPLIST; + } else { + terminators = NOQUOTE_STOPLIST; + } + char16_t* start = p; + while (p < e && !inList(*p, terminators)) ++p; + if (p == e) { + ERROR("Unexpected end of data"); + } + + char16_t x = *p; + if (p > start) { + ch = x; + *p = 0x0; // terminate by writing to data + result = start; // just point into data + } + if (haveQuote) { + if (x != c) { + ERROR("Missing matching quote"); + } else if (p == start) { + ERROR("Empty string"); + } + inc(); + } else if (x == OPEN_ANGLE || x == TICK || x == QUOTE) { + ERROR("Unexpected character in string"); + } + } + + // ok for there to be no next string + return result; +} + +void LocDataParser::parseError(const char* EXPLANATION_ARG) +{ + if (!data) { + return; + } + + const char16_t* start = p - U_PARSE_CONTEXT_LEN - 1; + if (start < data) { + start = data; + } + for (char16_t* x = p; --x >= start;) { + if (!*x) { + start = x+1; + break; + } + } + const char16_t* limit = p + U_PARSE_CONTEXT_LEN - 1; + if (limit > e) { + limit = e; + } + u_strncpy(pe.preContext, start, (int32_t)(p-start)); + pe.preContext[p-start] = 0; + u_strncpy(pe.postContext, p, (int32_t)(limit-p)); + pe.postContext[limit-p] = 0; + pe.offset = (int32_t)(p - data); + +#ifdef RBNF_DEBUG + fprintf(stderr, "%s at or near character %ld: ", EXPLANATION_ARG, p-data); + + UnicodeString msg; + msg.append(start, p - start); + msg.append((char16_t)0x002f); /* SOLIDUS/SLASH */ + msg.append(p, limit-p); + msg.append(UNICODE_STRING_SIMPLE("'")); + + char buf[128]; + int32_t len = msg.extract(0, msg.length(), buf, 128); + if (len >= 128) { + buf[127] = 0; + } else { + buf[len] = 0; + } + fprintf(stderr, "%s\n", buf); + fflush(stderr); +#endif + + uprv_free(data); + data = nullptr; + p = nullptr; + e = nullptr; + + if (U_SUCCESS(ec)) { + ec = U_PARSE_ERROR; + } +} + +//UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo) + +StringLocalizationInfo* +StringLocalizationInfo::create(const UnicodeString& info, UParseError& perror, UErrorCode& status) { + if (U_FAILURE(status)) { + return nullptr; + } + + int32_t len = info.length(); + if (len == 0) { + return nullptr; // no error; + } + + char16_t* p = (char16_t*)uprv_malloc(len * sizeof(char16_t)); + if (!p) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + info.extract(p, len, status); + if (!U_FAILURE(status)) { + status = U_ZERO_ERROR; // clear warning about non-termination + } + + LocDataParser parser(perror, status); + return parser.parse(p, len); +} + +StringLocalizationInfo::~StringLocalizationInfo() { + for (char16_t*** p = (char16_t***)data; *p; ++p) { + // remaining data is simply pointer into our unicode string data. + if (*p) uprv_free(*p); + } + if (data) uprv_free(data); + if (info) uprv_free(info); +} + + +const char16_t* +StringLocalizationInfo::getRuleSetName(int32_t index) const { + if (index >= 0 && index < getNumberOfRuleSets()) { + return data[0][index]; + } + return nullptr; +} + +const char16_t* +StringLocalizationInfo::getLocaleName(int32_t index) const { + if (index >= 0 && index < getNumberOfDisplayLocales()) { + return data[index+1][0]; + } + return nullptr; +} + +const char16_t* +StringLocalizationInfo::getDisplayName(int32_t localeIndex, int32_t ruleIndex) const { + if (localeIndex >= 0 && localeIndex < getNumberOfDisplayLocales() && + ruleIndex >= 0 && ruleIndex < getNumberOfRuleSets()) { + return data[localeIndex+1][ruleIndex+1]; + } + return nullptr; +} + +// ---------- + +RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, + const UnicodeString& locs, + const Locale& alocale, UParseError& perror, UErrorCode& status) + : fRuleSets(nullptr) + , ruleSetDescriptions(nullptr) + , numRuleSets(0) + , defaultRuleSet(nullptr) + , locale(alocale) + , collator(nullptr) + , decimalFormatSymbols(nullptr) + , defaultInfinityRule(nullptr) + , defaultNaNRule(nullptr) + , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) + , lenient(false) + , lenientParseRules(nullptr) + , localizations(nullptr) + , capitalizationInfoSet(false) + , capitalizationForUIListMenu(false) + , capitalizationForStandAlone(false) + , capitalizationBrkIter(nullptr) +{ + LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status); + init(description, locinfo, perror, status); +} + +RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, + const UnicodeString& locs, + UParseError& perror, UErrorCode& status) + : fRuleSets(nullptr) + , ruleSetDescriptions(nullptr) + , numRuleSets(0) + , defaultRuleSet(nullptr) + , locale(Locale::getDefault()) + , collator(nullptr) + , decimalFormatSymbols(nullptr) + , defaultInfinityRule(nullptr) + , defaultNaNRule(nullptr) + , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) + , lenient(false) + , lenientParseRules(nullptr) + , localizations(nullptr) + , capitalizationInfoSet(false) + , capitalizationForUIListMenu(false) + , capitalizationForStandAlone(false) + , capitalizationBrkIter(nullptr) +{ + LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status); + init(description, locinfo, perror, status); +} + +RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, + LocalizationInfo* info, + const Locale& alocale, UParseError& perror, UErrorCode& status) + : fRuleSets(nullptr) + , ruleSetDescriptions(nullptr) + , numRuleSets(0) + , defaultRuleSet(nullptr) + , locale(alocale) + , collator(nullptr) + , decimalFormatSymbols(nullptr) + , defaultInfinityRule(nullptr) + , defaultNaNRule(nullptr) + , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) + , lenient(false) + , lenientParseRules(nullptr) + , localizations(nullptr) + , capitalizationInfoSet(false) + , capitalizationForUIListMenu(false) + , capitalizationForStandAlone(false) + , capitalizationBrkIter(nullptr) +{ + init(description, info, perror, status); +} + +RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, + UParseError& perror, + UErrorCode& status) + : fRuleSets(nullptr) + , ruleSetDescriptions(nullptr) + , numRuleSets(0) + , defaultRuleSet(nullptr) + , locale(Locale::getDefault()) + , collator(nullptr) + , decimalFormatSymbols(nullptr) + , defaultInfinityRule(nullptr) + , defaultNaNRule(nullptr) + , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) + , lenient(false) + , lenientParseRules(nullptr) + , localizations(nullptr) + , capitalizationInfoSet(false) + , capitalizationForUIListMenu(false) + , capitalizationForStandAlone(false) + , capitalizationBrkIter(nullptr) +{ + init(description, nullptr, perror, status); +} + +RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, + const Locale& aLocale, + UParseError& perror, + UErrorCode& status) + : fRuleSets(nullptr) + , ruleSetDescriptions(nullptr) + , numRuleSets(0) + , defaultRuleSet(nullptr) + , locale(aLocale) + , collator(nullptr) + , decimalFormatSymbols(nullptr) + , defaultInfinityRule(nullptr) + , defaultNaNRule(nullptr) + , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) + , lenient(false) + , lenientParseRules(nullptr) + , localizations(nullptr) + , capitalizationInfoSet(false) + , capitalizationForUIListMenu(false) + , capitalizationForStandAlone(false) + , capitalizationBrkIter(nullptr) +{ + init(description, nullptr, perror, status); +} + +RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale& alocale, UErrorCode& status) + : fRuleSets(nullptr) + , ruleSetDescriptions(nullptr) + , numRuleSets(0) + , defaultRuleSet(nullptr) + , locale(alocale) + , collator(nullptr) + , decimalFormatSymbols(nullptr) + , defaultInfinityRule(nullptr) + , defaultNaNRule(nullptr) + , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) + , lenient(false) + , lenientParseRules(nullptr) + , localizations(nullptr) + , capitalizationInfoSet(false) + , capitalizationForUIListMenu(false) + , capitalizationForStandAlone(false) + , capitalizationBrkIter(nullptr) +{ + if (U_FAILURE(status)) { + return; + } + + const char* rules_tag = "RBNFRules"; + const char* fmt_tag = ""; + switch (tag) { + case URBNF_SPELLOUT: fmt_tag = "SpelloutRules"; break; + case URBNF_ORDINAL: fmt_tag = "OrdinalRules"; break; + case URBNF_DURATION: fmt_tag = "DurationRules"; break; + case URBNF_NUMBERING_SYSTEM: fmt_tag = "NumberingSystemRules"; break; + default: status = U_ILLEGAL_ARGUMENT_ERROR; return; + } + + // TODO: read localization info from resource + LocalizationInfo* locinfo = nullptr; + + UResourceBundle* nfrb = ures_open(U_ICUDATA_RBNF, locale.getName(), &status); + if (U_SUCCESS(status)) { + setLocaleIDs(ures_getLocaleByType(nfrb, ULOC_VALID_LOCALE, &status), + ures_getLocaleByType(nfrb, ULOC_ACTUAL_LOCALE, &status)); + + UResourceBundle* rbnfRules = ures_getByKeyWithFallback(nfrb, rules_tag, nullptr, &status); + if (U_FAILURE(status)) { + ures_close(nfrb); + } + UResourceBundle* ruleSets = ures_getByKeyWithFallback(rbnfRules, fmt_tag, nullptr, &status); + if (U_FAILURE(status)) { + ures_close(rbnfRules); + ures_close(nfrb); + return; + } + + UnicodeString desc; + while (ures_hasNext(ruleSets)) { + desc.append(ures_getNextUnicodeString(ruleSets,nullptr,&status)); + } + UParseError perror; + + init(desc, locinfo, perror, status); + + ures_close(ruleSets); + ures_close(rbnfRules); + } + ures_close(nfrb); +} + +RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat& rhs) + : NumberFormat(rhs) + , fRuleSets(nullptr) + , ruleSetDescriptions(nullptr) + , numRuleSets(0) + , defaultRuleSet(nullptr) + , locale(rhs.locale) + , collator(nullptr) + , decimalFormatSymbols(nullptr) + , defaultInfinityRule(nullptr) + , defaultNaNRule(nullptr) + , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary) + , lenient(false) + , lenientParseRules(nullptr) + , localizations(nullptr) + , capitalizationInfoSet(false) + , capitalizationForUIListMenu(false) + , capitalizationForStandAlone(false) + , capitalizationBrkIter(nullptr) +{ + this->operator=(rhs); +} + +// -------- + +RuleBasedNumberFormat& +RuleBasedNumberFormat::operator=(const RuleBasedNumberFormat& rhs) +{ + if (this == &rhs) { + return *this; + } + NumberFormat::operator=(rhs); + UErrorCode status = U_ZERO_ERROR; + dispose(); + locale = rhs.locale; + lenient = rhs.lenient; + + UParseError perror; + setDecimalFormatSymbols(*rhs.getDecimalFormatSymbols()); + init(rhs.originalDescription, rhs.localizations ? rhs.localizations->ref() : nullptr, perror, status); + setDefaultRuleSet(rhs.getDefaultRuleSetName(), status); + setRoundingMode(rhs.getRoundingMode()); + + capitalizationInfoSet = rhs.capitalizationInfoSet; + capitalizationForUIListMenu = rhs.capitalizationForUIListMenu; + capitalizationForStandAlone = rhs.capitalizationForStandAlone; +#if !UCONFIG_NO_BREAK_ITERATION + capitalizationBrkIter = (rhs.capitalizationBrkIter!=nullptr)? rhs.capitalizationBrkIter->clone(): nullptr; +#endif + + return *this; +} + +RuleBasedNumberFormat::~RuleBasedNumberFormat() +{ + dispose(); +} + +RuleBasedNumberFormat* +RuleBasedNumberFormat::clone() const +{ + return new RuleBasedNumberFormat(*this); +} + +bool +RuleBasedNumberFormat::operator==(const Format& other) const +{ + if (this == &other) { + return true; + } + + if (typeid(*this) == typeid(other)) { + const RuleBasedNumberFormat& rhs = static_cast(other); + // test for capitalization info equality is adequately handled + // by the NumberFormat test for fCapitalizationContext equality; + // the info here is just derived from that. + if (locale == rhs.locale && + lenient == rhs.lenient && + (localizations == nullptr + ? rhs.localizations == nullptr + : (rhs.localizations == nullptr + ? false + : *localizations == rhs.localizations))) { + + NFRuleSet** p = fRuleSets; + NFRuleSet** q = rhs.fRuleSets; + if (p == nullptr) { + return q == nullptr; + } else if (q == nullptr) { + return false; + } + while (*p && *q && (**p == **q)) { + ++p; + ++q; + } + return *q == nullptr && *p == nullptr; + } + } + + return false; +} + +UnicodeString +RuleBasedNumberFormat::getRules() const +{ + UnicodeString result; + if (fRuleSets != nullptr) { + for (NFRuleSet** p = fRuleSets; *p; ++p) { + (*p)->appendRules(result); + } + } + return result; +} + +UnicodeString +RuleBasedNumberFormat::getRuleSetName(int32_t index) const +{ + if (localizations) { + UnicodeString string(true, localizations->getRuleSetName(index), (int32_t)-1); + return string; + } + else if (fRuleSets) { + UnicodeString result; + for (NFRuleSet** p = fRuleSets; *p; ++p) { + NFRuleSet* rs = *p; + if (rs->isPublic()) { + if (--index == -1) { + rs->getName(result); + return result; + } + } + } + } + UnicodeString empty; + return empty; +} + +int32_t +RuleBasedNumberFormat::getNumberOfRuleSetNames() const +{ + int32_t result = 0; + if (localizations) { + result = localizations->getNumberOfRuleSets(); + } + else if (fRuleSets) { + for (NFRuleSet** p = fRuleSets; *p; ++p) { + if ((**p).isPublic()) { + ++result; + } + } + } + return result; +} + +int32_t +RuleBasedNumberFormat::getNumberOfRuleSetDisplayNameLocales() const { + if (localizations) { + return localizations->getNumberOfDisplayLocales(); + } + return 0; +} + +Locale +RuleBasedNumberFormat::getRuleSetDisplayNameLocale(int32_t index, UErrorCode& status) const { + if (U_FAILURE(status)) { + return Locale(""); + } + if (localizations && index >= 0 && index < localizations->getNumberOfDisplayLocales()) { + UnicodeString name(true, localizations->getLocaleName(index), -1); + char buffer[64]; + int32_t cap = name.length() + 1; + char* bp = buffer; + if (cap > 64) { + bp = (char *)uprv_malloc(cap); + if (bp == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return Locale(""); + } + } + name.extract(0, name.length(), bp, cap, UnicodeString::kInvariant); + Locale retLocale(bp); + if (bp != buffer) { + uprv_free(bp); + } + return retLocale; + } + status = U_ILLEGAL_ARGUMENT_ERROR; + Locale retLocale; + return retLocale; +} + +UnicodeString +RuleBasedNumberFormat::getRuleSetDisplayName(int32_t index, const Locale& localeParam) { + if (localizations && index >= 0 && index < localizations->getNumberOfRuleSets()) { + UnicodeString localeName(localeParam.getBaseName(), -1, UnicodeString::kInvariant); + int32_t len = localeName.length(); + char16_t* localeStr = localeName.getBuffer(len + 1); + while (len >= 0) { + localeStr[len] = 0; + int32_t ix = localizations->indexForLocale(localeStr); + if (ix >= 0) { + UnicodeString name(true, localizations->getDisplayName(ix, index), -1); + return name; + } + + // trim trailing portion, skipping over omitted sections + do { --len;} while (len > 0 && localeStr[len] != 0x005f); // underscore + while (len > 0 && localeStr[len-1] == 0x005F) --len; + } + UnicodeString name(true, localizations->getRuleSetName(index), -1); + return name; + } + UnicodeString bogus; + bogus.setToBogus(); + return bogus; +} + +UnicodeString +RuleBasedNumberFormat::getRuleSetDisplayName(const UnicodeString& ruleSetName, const Locale& localeParam) { + if (localizations) { + UnicodeString rsn(ruleSetName); + int32_t ix = localizations->indexForRuleSet(rsn.getTerminatedBuffer()); + return getRuleSetDisplayName(ix, localeParam); + } + UnicodeString bogus; + bogus.setToBogus(); + return bogus; +} + +NFRuleSet* +RuleBasedNumberFormat::findRuleSet(const UnicodeString& name, UErrorCode& status) const +{ + if (U_SUCCESS(status) && fRuleSets) { + for (NFRuleSet** p = fRuleSets; *p; ++p) { + NFRuleSet* rs = *p; + if (rs->isNamed(name)) { + return rs; + } + } + status = U_ILLEGAL_ARGUMENT_ERROR; + } + return nullptr; +} + +UnicodeString& +RuleBasedNumberFormat::format(const DecimalQuantity &number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode &status) const { + if (U_FAILURE(status)) { + return appendTo; + } + DecimalQuantity copy(number); + if (copy.fitsInLong()) { + format(number.toLong(), appendTo, pos, status); + } + else { + 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. + // The DecimalFormat will provide more accurate results. + + // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J. + LocalPointer decimalFormat(NumberFormat::createInstance(locale, UNUM_DECIMAL, status), status); + if (decimalFormat.isNull()) { + return appendTo; + } + Formattable f; + LocalPointer decimalQuantity(new DecimalQuantity(number), status); + if (decimalQuantity.isNull()) { + return appendTo; + } + f.adoptDecimalQuantity(decimalQuantity.orphan()); // f now owns decimalQuantity. + decimalFormat->format(f, appendTo, pos, status); + } + } + return appendTo; +} + +UnicodeString& +RuleBasedNumberFormat::format(int32_t number, + UnicodeString& toAppendTo, + FieldPosition& pos) const +{ + return format((int64_t)number, toAppendTo, pos); +} + + +UnicodeString& +RuleBasedNumberFormat::format(int64_t number, + UnicodeString& toAppendTo, + FieldPosition& /* pos */) const +{ + if (defaultRuleSet) { + UErrorCode status = U_ZERO_ERROR; + format(number, defaultRuleSet, toAppendTo, status); + } + return toAppendTo; +} + + +UnicodeString& +RuleBasedNumberFormat::format(double number, + UnicodeString& toAppendTo, + FieldPosition& /* pos */) const +{ + UErrorCode status = U_ZERO_ERROR; + if (defaultRuleSet) { + format(number, *defaultRuleSet, toAppendTo, status); + } + return toAppendTo; +} + + +UnicodeString& +RuleBasedNumberFormat::format(int32_t number, + const UnicodeString& ruleSetName, + UnicodeString& toAppendTo, + FieldPosition& pos, + UErrorCode& status) const +{ + return format((int64_t)number, ruleSetName, toAppendTo, pos, status); +} + + +UnicodeString& +RuleBasedNumberFormat::format(int64_t number, + const UnicodeString& ruleSetName, + UnicodeString& toAppendTo, + FieldPosition& /* pos */, + UErrorCode& status) const +{ + if (U_SUCCESS(status)) { + if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) { + // throw new IllegalArgumentException("Can't use internal rule set"); + status = U_ILLEGAL_ARGUMENT_ERROR; + } else { + NFRuleSet *rs = findRuleSet(ruleSetName, status); + if (rs) { + format(number, rs, toAppendTo, status); + } + } + } + return toAppendTo; +} + + +UnicodeString& +RuleBasedNumberFormat::format(double number, + const UnicodeString& ruleSetName, + UnicodeString& toAppendTo, + FieldPosition& /* pos */, + UErrorCode& status) const +{ + if (U_SUCCESS(status)) { + if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) { + // throw new IllegalArgumentException("Can't use internal rule set"); + status = U_ILLEGAL_ARGUMENT_ERROR; + } else { + NFRuleSet *rs = findRuleSet(ruleSetName, status); + if (rs) { + format(number, *rs, toAppendTo, status); + } + } + } + return toAppendTo; +} + +void +RuleBasedNumberFormat::format(double number, + NFRuleSet& rs, + UnicodeString& toAppendTo, + UErrorCode& status) const +{ + int32_t startPos = toAppendTo.length(); + if (getRoundingMode() != DecimalFormat::ERoundingMode::kRoundUnnecessary && !uprv_isNaN(number) && !uprv_isInfinite(number)) { + 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); +} + +/** + * Bottleneck through which all the public format() methods + * that take a long pass. By the time we get here, we know + * which rule set we're using to do the formatting. + * @param number The number to format + * @param ruleSet The rule set to use to format the number + * @return The text that resulted from formatting the number + */ +UnicodeString& +RuleBasedNumberFormat::format(int64_t number, NFRuleSet *ruleSet, UnicodeString& toAppendTo, UErrorCode& status) const +{ + // all API format() routines that take a double vector through + // here. We have these two identical functions-- one taking a + // double and one taking a long-- the couple digits of precision + // that long has but double doesn't (both types are 8 bytes long, + // but double has to borrow some of the mantissa bits to hold + // the exponent). + // Create an empty string buffer where the result will + // be built, and pass it to the rule set (along with an insertion + // position of 0 and the number being formatted) to the rule set + // for formatting + + if (U_SUCCESS(status)) { + if (number == U_INT64_MIN) { + // We can't handle this value right now. Provide an accurate default value. + + // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J. + NumberFormat *decimalFormat = NumberFormat::createInstance(locale, UNUM_DECIMAL, status); + if (decimalFormat == nullptr) { + return toAppendTo; + } + Formattable f; + FieldPosition pos(FieldPosition::DONT_CARE); + DecimalQuantity *decimalQuantity = new DecimalQuantity(); + if (decimalQuantity == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + delete decimalFormat; + return toAppendTo; + } + decimalQuantity->setToLong(number); + f.adoptDecimalQuantity(decimalQuantity); // f now owns decimalQuantity. + decimalFormat->format(f, toAppendTo, pos, status); + delete decimalFormat; + } + else { + int32_t startPos = toAppendTo.length(); + ruleSet->format(number, toAppendTo, toAppendTo.length(), 0, status); + adjustForCapitalizationContext(startPos, toAppendTo, status); + } + } + return toAppendTo; +} + +UnicodeString& +RuleBasedNumberFormat::adjustForCapitalizationContext(int32_t startPos, + UnicodeString& currentResult, + UErrorCode& status) const +{ +#if !UCONFIG_NO_BREAK_ITERATION + UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status); + if (capitalizationContext != UDISPCTX_CAPITALIZATION_NONE && startPos == 0 && currentResult.length() > 0) { + // capitalize currentResult according to context + UChar32 ch = currentResult.char32At(0); + if (u_islower(ch) && U_SUCCESS(status) && capitalizationBrkIter != nullptr && + ( capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || + (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForUIListMenu) || + (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone)) ) { + // titlecase first word of currentResult, here use sentence iterator unlike current implementations + // in LocaleDisplayNamesImpl::adjustForUsageAndContext and RelativeDateFormat::format + currentResult.toTitle(capitalizationBrkIter, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); + } + } +#endif + return currentResult; +} + + +void +RuleBasedNumberFormat::parse(const UnicodeString& text, + Formattable& result, + ParsePosition& parsePosition) const +{ + if (!fRuleSets) { + parsePosition.setErrorIndex(0); + return; + } + + UnicodeString workingText(text, parsePosition.getIndex()); + ParsePosition workingPos(0); + + ParsePosition high_pp(0); + Formattable high_result; + + for (NFRuleSet** p = fRuleSets; *p; ++p) { + NFRuleSet *rp = *p; + if (rp->isPublic() && rp->isParseable()) { + ParsePosition working_pp(0); + Formattable working_result; + + rp->parse(workingText, working_pp, kMaxDouble, 0, working_result); + if (working_pp.getIndex() > high_pp.getIndex()) { + high_pp = working_pp; + high_result = working_result; + + if (high_pp.getIndex() == workingText.length()) { + break; + } + } + } + } + + int32_t startIndex = parsePosition.getIndex(); + parsePosition.setIndex(startIndex + high_pp.getIndex()); + if (high_pp.getIndex() > 0) { + parsePosition.setErrorIndex(-1); + } else { + int32_t errorIndex = (high_pp.getErrorIndex()>0)? high_pp.getErrorIndex(): 0; + parsePosition.setErrorIndex(startIndex + errorIndex); + } + result = high_result; + if (result.getType() == Formattable::kDouble) { + double d = result.getDouble(); + if (!uprv_isNaN(d) && d == uprv_trunc(d) && INT32_MIN <= d && d <= INT32_MAX) { + // Note: casting a double to an int when the double is too large or small + // to fit the destination is undefined behavior. The explicit range checks, + // above, are required. Just casting and checking the result value is undefined. + result.setLong(static_cast(d)); + } + } +} + +#if !UCONFIG_NO_COLLATION + +void +RuleBasedNumberFormat::setLenient(UBool enabled) +{ + lenient = enabled; + if (!enabled && collator) { + delete collator; + collator = nullptr; + } +} + +#endif + +void +RuleBasedNumberFormat::setDefaultRuleSet(const UnicodeString& ruleSetName, UErrorCode& status) { + if (U_SUCCESS(status)) { + if (ruleSetName.isEmpty()) { + if (localizations) { + UnicodeString name(true, localizations->getRuleSetName(0), -1); + defaultRuleSet = findRuleSet(name, status); + } else { + initDefaultRuleSet(); + } + } else if (ruleSetName.startsWith(UNICODE_STRING_SIMPLE("%%"))) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } else { + NFRuleSet* result = findRuleSet(ruleSetName, status); + if (result != nullptr) { + defaultRuleSet = result; + } + } + } +} + +UnicodeString +RuleBasedNumberFormat::getDefaultRuleSetName() const { + UnicodeString result; + if (defaultRuleSet && defaultRuleSet->isPublic()) { + defaultRuleSet->getName(result); + } else { + result.setToBogus(); + } + return result; +} + +void +RuleBasedNumberFormat::initDefaultRuleSet() +{ + defaultRuleSet = nullptr; + if (!fRuleSets) { + return; + } + + const UnicodeString spellout(UNICODE_STRING_SIMPLE("%spellout-numbering")); + const UnicodeString ordinal(UNICODE_STRING_SIMPLE("%digits-ordinal")); + const UnicodeString duration(UNICODE_STRING_SIMPLE("%duration")); + + NFRuleSet**p = &fRuleSets[0]; + while (*p) { + if ((*p)->isNamed(spellout) || (*p)->isNamed(ordinal) || (*p)->isNamed(duration)) { + defaultRuleSet = *p; + return; + } else { + ++p; + } + } + + defaultRuleSet = *--p; + if (!defaultRuleSet->isPublic()) { + while (p != fRuleSets) { + if ((*--p)->isPublic()) { + defaultRuleSet = *p; + break; + } + } + } +} + + +void +RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* localizationInfos, + UParseError& pErr, UErrorCode& status) +{ + // TODO: implement UParseError + uprv_memset(&pErr, 0, sizeof(UParseError)); + // Note: this can leave ruleSets == nullptr, so remaining code should check + if (U_FAILURE(status)) { + return; + } + + initializeDecimalFormatSymbols(status); + initializeDefaultInfinityRule(status); + initializeDefaultNaNRule(status); + if (U_FAILURE(status)) { + return; + } + + this->localizations = localizationInfos == nullptr ? nullptr : localizationInfos->ref(); + + UnicodeString description(rules); + if (!description.length()) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + // start by stripping the trailing whitespace from all the rules + // (this is all the whitespace following each semicolon in the + // description). This allows us to look for rule-set boundaries + // by searching for ";%" without having to worry about whitespace + // between the ; and the % + stripWhitespace(description); + + // check to see if there's a set of lenient-parse rules. If there + // is, pull them out into our temporary holding place for them, + // and delete them from the description before the real desciption- + // parsing code sees them + int32_t lp = description.indexOf(gLenientParse, -1, 0); + if (lp != -1) { + // we've got to make sure we're not in the middle of a rule + // (where "%%lenient-parse" would actually get treated as + // rule text) + if (lp == 0 || description.charAt(lp - 1) == gSemiColon) { + // locate the beginning and end of the actual collation + // rules (there may be whitespace between the name and + // the first token in the description) + int lpEnd = description.indexOf(gSemiPercent, 2, lp); + + if (lpEnd == -1) { + lpEnd = description.length() - 1; + } + int lpStart = lp + u_strlen(gLenientParse); + while (PatternProps::isWhiteSpace(description.charAt(lpStart))) { + ++lpStart; + } + + // copy out the lenient-parse rules and delete them + // from the description + lenientParseRules = new UnicodeString(); + /* test for nullptr */ + if (lenientParseRules == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + lenientParseRules->setTo(description, lpStart, lpEnd - lpStart); + + description.remove(lp, lpEnd + 1 - lp); + } + } + + // pre-flight parsing the description and count the number of + // rule sets (";%" marks the end of one rule set and the beginning + // of the next) + numRuleSets = 0; + for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, p)) { + ++numRuleSets; + ++p; + } + ++numRuleSets; + + // our rule list is an array of the appropriate size + fRuleSets = (NFRuleSet **)uprv_malloc((numRuleSets + 1) * sizeof(NFRuleSet *)); + /* test for nullptr */ + if (fRuleSets == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + for (int i = 0; i <= numRuleSets; ++i) { + fRuleSets[i] = nullptr; + } + + // divide up the descriptions into individual rule-set descriptions + // and store them in a temporary array. At each step, we also + // new up a rule set, but all this does is initialize its name + // and remove it from its description. We can't actually parse + // the rest of the descriptions and finish initializing everything + // because we have to know the names and locations of all the rule + // sets before we can actually set everything up + if(!numRuleSets) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + ruleSetDescriptions = new UnicodeString[numRuleSets]; + if (ruleSetDescriptions == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + { + int curRuleSet = 0; + int32_t start = 0; + for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, start)) { + ruleSetDescriptions[curRuleSet].setTo(description, start, p + 1 - start); + fRuleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status); + if (fRuleSets[curRuleSet] == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + ++curRuleSet; + start = p + 1; + } + ruleSetDescriptions[curRuleSet].setTo(description, start, description.length() - start); + fRuleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status); + if (fRuleSets[curRuleSet] == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + + // now we can take note of the formatter's default rule set, which + // is the last public rule set in the description (it's the last + // rather than the first so that a user can create a new formatter + // from an existing formatter and change its default behavior just + // by appending more rule sets to the end) + + // {dlf} Initialization of a fraction rule set requires the default rule + // set to be known. For purposes of initialization, this is always the + // last public rule set, no matter what the localization data says. + initDefaultRuleSet(); + + // finally, we can go back through the temporary descriptions + // list and finish setting up the substructure (and we throw + // away the temporary descriptions as we go) + { + for (int i = 0; i < numRuleSets; i++) { + fRuleSets[i]->parseRules(ruleSetDescriptions[i], status); + } + } + + // Now that the rules are initialized, the 'real' default rule + // set can be adjusted by the localization data. + + // The C code keeps the localization array as is, rather than building + // a separate array of the public rule set names, so we have less work + // to do here-- but we still need to check the names. + + if (localizationInfos) { + // confirm the names, if any aren't in the rules, that's an error + // it is ok if the rules contain public rule sets that are not in this list + for (int32_t i = 0; i < localizationInfos->getNumberOfRuleSets(); ++i) { + UnicodeString name(true, localizationInfos->getRuleSetName(i), -1); + NFRuleSet* rs = findRuleSet(name, status); + if (rs == nullptr) { + break; // error + } + if (i == 0) { + defaultRuleSet = rs; + } + } + } else { + defaultRuleSet = getDefaultRuleSet(); + } + originalDescription = rules; +} + +// override the NumberFormat implementation in order to +// lazily initialize relevant items +void +RuleBasedNumberFormat::setContext(UDisplayContext value, UErrorCode& status) +{ + NumberFormat::setContext(value, status); + if (U_SUCCESS(status)) { + if (!capitalizationInfoSet && + (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE)) { + initCapitalizationContextInfo(locale); + capitalizationInfoSet = true; + } +#if !UCONFIG_NO_BREAK_ITERATION + if ( capitalizationBrkIter == nullptr && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || + (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForUIListMenu) || + (value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone)) ) { + status = U_ZERO_ERROR; + capitalizationBrkIter = BreakIterator::createSentenceInstance(locale, status); + if (U_FAILURE(status)) { + delete capitalizationBrkIter; + capitalizationBrkIter = nullptr; + } + } +#endif + } +} + +void +RuleBasedNumberFormat::initCapitalizationContextInfo(const Locale& thelocale) +{ +#if !UCONFIG_NO_BREAK_ITERATION + const char * localeID = (thelocale != nullptr)? thelocale.getBaseName(): nullptr; + UErrorCode status = U_ZERO_ERROR; + UResourceBundle *rb = ures_open(nullptr, localeID, &status); + rb = ures_getByKeyWithFallback(rb, "contextTransforms", rb, &status); + rb = ures_getByKeyWithFallback(rb, "number-spellout", rb, &status); + if (U_SUCCESS(status) && rb != nullptr) { + int32_t len = 0; + const int32_t * intVector = ures_getIntVector(rb, &len, &status); + if (U_SUCCESS(status) && intVector != nullptr && len >= 2) { + capitalizationForUIListMenu = static_cast(intVector[0]); + capitalizationForStandAlone = static_cast(intVector[1]); + } + } + ures_close(rb); +#endif +} + +void +RuleBasedNumberFormat::stripWhitespace(UnicodeString& description) +{ + // iterate through the characters... + UnicodeString result; + + int start = 0; + while (start != -1 && start < description.length()) { + // seek to the first non-whitespace character... + while (start < description.length() + && PatternProps::isWhiteSpace(description.charAt(start))) { + ++start; + } + + // locate the next semicolon in the text and copy the text from + // our current position up to that semicolon into the result + int32_t p = description.indexOf(gSemiColon, start); + if (p == -1) { + // or if we don't find a semicolon, just copy the rest of + // the string into the result + result.append(description, start, description.length() - start); + start = -1; + } + else if (p < description.length()) { + result.append(description, start, p + 1 - start); + start = p + 1; + } + + // when we get here, we've seeked off the end of the string, and + // we terminate the loop (we continue until *start* is -1 rather + // than until *p* is -1, because otherwise we'd miss the last + // rule in the description) + else { + start = -1; + } + } + + description.setTo(result); +} + + +void +RuleBasedNumberFormat::dispose() +{ + if (fRuleSets) { + for (NFRuleSet** p = fRuleSets; *p; ++p) { + delete *p; + } + uprv_free(fRuleSets); + fRuleSets = nullptr; + } + + if (ruleSetDescriptions) { + delete [] ruleSetDescriptions; + ruleSetDescriptions = nullptr; + } + +#if !UCONFIG_NO_COLLATION + delete collator; +#endif + collator = nullptr; + + delete decimalFormatSymbols; + decimalFormatSymbols = nullptr; + + delete defaultInfinityRule; + defaultInfinityRule = nullptr; + + delete defaultNaNRule; + defaultNaNRule = nullptr; + + delete lenientParseRules; + lenientParseRules = nullptr; + +#if !UCONFIG_NO_BREAK_ITERATION + delete capitalizationBrkIter; + capitalizationBrkIter = nullptr; +#endif + + if (localizations) { + localizations = localizations->unref(); + } +} + + +//----------------------------------------------------------------------- +// package-internal API +//----------------------------------------------------------------------- + +/** + * Returns the collator to use for lenient parsing. The collator is lazily created: + * this function creates it the first time it's called. + * @return The collator to use for lenient parsing, or null if lenient parsing + * is turned off. +*/ +const RuleBasedCollator* +RuleBasedNumberFormat::getCollator() const +{ +#if !UCONFIG_NO_COLLATION + if (!fRuleSets) { + return nullptr; + } + + // lazy-evaluate the collator + if (collator == nullptr && lenient) { + // create a default collator based on the formatter's locale, + // then pull out that collator's rules, append any additional + // rules specified in the description, and create a _new_ + // collator based on the combination of those rules + + UErrorCode status = U_ZERO_ERROR; + + Collator* temp = Collator::createInstance(locale, status); + RuleBasedCollator* newCollator; + if (U_SUCCESS(status) && (newCollator = dynamic_cast(temp)) != nullptr) { + if (lenientParseRules) { + UnicodeString rules(newCollator->getRules()); + rules.append(*lenientParseRules); + + newCollator = new RuleBasedCollator(rules, status); + // Exit if newCollator could not be created. + if (newCollator == nullptr) { + return nullptr; + } + } else { + temp = nullptr; + } + if (U_SUCCESS(status)) { + newCollator->setAttribute(UCOL_DECOMPOSITION_MODE, UCOL_ON, status); + // cast away const + ((RuleBasedNumberFormat*)this)->collator = newCollator; + } else { + delete newCollator; + } + } + delete temp; + } +#endif + + // if lenient-parse mode is off, this will be null + // (see setLenientParseMode()) + return collator; +} + + +DecimalFormatSymbols* +RuleBasedNumberFormat::initializeDecimalFormatSymbols(UErrorCode &status) +{ + // lazy-evaluate the DecimalFormatSymbols object. This object + // is shared by all DecimalFormat instances belonging to this + // formatter + if (decimalFormatSymbols == nullptr) { + LocalPointer temp(new DecimalFormatSymbols(locale, status), status); + if (U_SUCCESS(status)) { + decimalFormatSymbols = temp.orphan(); + } + } + return decimalFormatSymbols; +} + +/** + * Returns the DecimalFormatSymbols object that should be used by all DecimalFormat + * instances owned by this formatter. +*/ +const DecimalFormatSymbols* +RuleBasedNumberFormat::getDecimalFormatSymbols() const +{ + return decimalFormatSymbols; +} + +NFRule* +RuleBasedNumberFormat::initializeDefaultInfinityRule(UErrorCode &status) +{ + if (U_FAILURE(status)) { + return nullptr; + } + if (defaultInfinityRule == nullptr) { + UnicodeString rule(UNICODE_STRING_SIMPLE("Inf: ")); + rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kInfinitySymbol)); + LocalPointer temp(new NFRule(this, rule, status), status); + if (U_SUCCESS(status)) { + defaultInfinityRule = temp.orphan(); + } + } + return defaultInfinityRule; +} + +const NFRule* +RuleBasedNumberFormat::getDefaultInfinityRule() const +{ + return defaultInfinityRule; +} + +NFRule* +RuleBasedNumberFormat::initializeDefaultNaNRule(UErrorCode &status) +{ + if (U_FAILURE(status)) { + return nullptr; + } + if (defaultNaNRule == nullptr) { + UnicodeString rule(UNICODE_STRING_SIMPLE("NaN: ")); + rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kNaNSymbol)); + LocalPointer temp(new NFRule(this, rule, status), status); + if (U_SUCCESS(status)) { + defaultNaNRule = temp.orphan(); + } + } + return defaultNaNRule; +} + +const NFRule* +RuleBasedNumberFormat::getDefaultNaNRule() const +{ + return defaultNaNRule; +} + +// De-owning the current localized symbols and adopt the new symbols. +void +RuleBasedNumberFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) +{ + if (symbolsToAdopt == nullptr) { + return; // do not allow caller to set decimalFormatSymbols to nullptr + } + + if (decimalFormatSymbols != nullptr) { + delete decimalFormatSymbols; + } + + decimalFormatSymbols = symbolsToAdopt; + + { + // Apply the new decimalFormatSymbols by reparsing the rulesets + UErrorCode status = U_ZERO_ERROR; + + delete defaultInfinityRule; + defaultInfinityRule = nullptr; + initializeDefaultInfinityRule(status); // Reset with the new DecimalFormatSymbols + + delete defaultNaNRule; + defaultNaNRule = nullptr; + initializeDefaultNaNRule(status); // Reset with the new DecimalFormatSymbols + + if (fRuleSets) { + for (int32_t i = 0; i < numRuleSets; i++) { + fRuleSets[i]->setDecimalFormatSymbols(*symbolsToAdopt, status); + } + } + } +} + +// Setting the symbols is equivalent to adopting a newly created localized symbols. +void +RuleBasedNumberFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) +{ + adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols)); +} + +PluralFormat * +RuleBasedNumberFormat::createPluralFormat(UPluralType pluralType, + const UnicodeString &pattern, + UErrorCode& status) const +{ + auto *pf = new PluralFormat(locale, pluralType, pattern, status); + if (pf == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return pf; +} + +/** + * Get the rounding mode. + * @return A rounding mode + */ +DecimalFormat::ERoundingMode RuleBasedNumberFormat::getRoundingMode() const { + return fRoundingMode; +} + +/** + * Set the rounding mode. This has no effect unless the rounding + * increment is greater than zero. + * @param roundingMode A rounding mode + */ +void RuleBasedNumberFormat::setRoundingMode(DecimalFormat::ERoundingMode roundingMode) { + fRoundingMode = roundingMode; +} + +U_NAMESPACE_END + +/* U_HAVE_RBNF */ +#endif diff --git a/deps/icu-small/source/i18n/rbt.cpp b/deps/icu-small/source/i18n/rbt.cpp index 86d6cbd8bda5f0..3b2c61b6854c0a 100644 --- a/deps/icu-small/source/i18n/rbt.cpp +++ b/deps/icu-small/source/i18n/rbt.cpp @@ -1,307 +1,307 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2015, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/17/99 aliu Creation. -********************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/rep.h" -#include "unicode/uniset.h" -#include "rbt_pars.h" -#include "rbt_data.h" -#include "rbt_rule.h" -#include "rbt.h" -#include "mutex.h" -#include "umutex.h" - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTransliterator) - -static Replaceable *gLockedText = NULL; - -void RuleBasedTransliterator::_construct(const UnicodeString& rules, - UTransDirection direction, - UParseError& parseError, - UErrorCode& status) { - fData = 0; - isDataOwned = true; - if (U_FAILURE(status)) { - return; - } - - TransliteratorParser parser(status); - parser.parse(rules, direction, parseError, status); - if (U_FAILURE(status)) { - return; - } - - if (parser.idBlockVector.size() != 0 || - parser.compoundFilter != NULL || - parser.dataVector.size() == 0) { - status = U_INVALID_RBT_SYNTAX; // ::ID blocks disallowed in RBT - return; - } - - fData = (TransliterationRuleData*)parser.dataVector.orphanElementAt(0); - setMaximumContextLength(fData->ruleSet.getMaximumContextLength()); -} - -/** - * Constructs a new transliterator from the given rules. - * @param id the id for the transliterator. - * @param rules rules, separated by ';' - * @param direction either FORWARD or REVERSE. - * @param adoptedFilter the filter for this transliterator. - * @param parseError Struct to receive information on position - * of error if an error is encountered - * @param status Output param set to success/failure code. - * @exception IllegalArgumentException if rules are malformed - * or direction is invalid. - */ -RuleBasedTransliterator::RuleBasedTransliterator( - const UnicodeString& id, - const UnicodeString& rules, - UTransDirection direction, - UnicodeFilter* adoptedFilter, - UParseError& parseError, - UErrorCode& status) : - Transliterator(id, adoptedFilter) { - _construct(rules, direction,parseError,status); -} - -/** - * Constructs a new transliterator from the given rules. - * @param id the id for the transliterator. - * @param rules rules, separated by ';' - * @param direction either FORWARD or REVERSE. - * @param adoptedFilter the filter for this transliterator. - * @param status Output param set to success/failure code. - * @exception IllegalArgumentException if rules are malformed - * or direction is invalid. - */ -/*RuleBasedTransliterator::RuleBasedTransliterator( - const UnicodeString& id, - const UnicodeString& rules, - UTransDirection direction, - UnicodeFilter* adoptedFilter, - UErrorCode& status) : - Transliterator(id, adoptedFilter) { - UParseError parseError; - _construct(rules, direction,parseError, status); -}*/ - -/** - * Convenience constructor with no filter. - */ -/*RuleBasedTransliterator::RuleBasedTransliterator( - const UnicodeString& id, - const UnicodeString& rules, - UTransDirection direction, - UErrorCode& status) : - Transliterator(id, 0) { - UParseError parseError; - _construct(rules, direction,parseError, status); -}*/ - -/** - * Convenience constructor with no filter and FORWARD direction. - */ -/*RuleBasedTransliterator::RuleBasedTransliterator( - const UnicodeString& id, - const UnicodeString& rules, - UErrorCode& status) : - Transliterator(id, 0) { - UParseError parseError; - _construct(rules, UTRANS_FORWARD, parseError, status); -}*/ - -/** - * Convenience constructor with FORWARD direction. - */ -/*RuleBasedTransliterator::RuleBasedTransliterator( - const UnicodeString& id, - const UnicodeString& rules, - UnicodeFilter* adoptedFilter, - UErrorCode& status) : - Transliterator(id, adoptedFilter) { - UParseError parseError; - _construct(rules, UTRANS_FORWARD,parseError, status); -}*/ - -RuleBasedTransliterator::RuleBasedTransliterator(const UnicodeString& id, - const TransliterationRuleData* theData, - UnicodeFilter* adoptedFilter) : - Transliterator(id, adoptedFilter), - fData((TransliterationRuleData*)theData), // cast away const - isDataOwned(false) { - setMaximumContextLength(fData->ruleSet.getMaximumContextLength()); -} - -/** - * Internal constructor. - */ -RuleBasedTransliterator::RuleBasedTransliterator(const UnicodeString& id, - TransliterationRuleData* theData, - UBool isDataAdopted) : - Transliterator(id, 0), - fData(theData), - isDataOwned(isDataAdopted) { - setMaximumContextLength(fData->ruleSet.getMaximumContextLength()); -} - -/** - * Copy constructor. - */ -RuleBasedTransliterator::RuleBasedTransliterator( - const RuleBasedTransliterator& other) : - Transliterator(other), fData(other.fData), - isDataOwned(other.isDataOwned) { - - // The data object may or may not be owned. If it is not owned we - // share it; it is invariant. If it is owned, it's still - // invariant, but we need to copy it to prevent double-deletion. - // If this becomes a performance issue (if people do a lot of RBT - // copying -- unlikely) we can reference count the data object. - - // Only do a deep copy if this is owned data, that is, data that - // will be later deleted. System transliterators contain - // non-owned data. - if (isDataOwned) { - fData = new TransliterationRuleData(*other.fData); - } -} - -/** - * Destructor. - */ -RuleBasedTransliterator::~RuleBasedTransliterator() { - // Delete the data object only if we own it. - if (isDataOwned) { - delete fData; - } -} - -RuleBasedTransliterator* -RuleBasedTransliterator::clone() const { - return new RuleBasedTransliterator(*this); -} - -/** - * Implements {@link Transliterator#handleTransliterate}. - */ -void -RuleBasedTransliterator::handleTransliterate(Replaceable& text, UTransPosition& index, - UBool isIncremental) const { - /* We keep contextStart and contextLimit fixed the entire time, - * relative to the text -- contextLimit may move numerically if - * text is inserted or removed. The start offset moves toward - * limit, with replacements happening under it. - * - * Example: rules 1. ab>x|y - * 2. yc>z - * - * |eabcd begin - no match, advance start - * e|abcd match rule 1 - change text & adjust start - * ex|ycd match rule 2 - change text & adjust start - * exz|d no match, advance start - * exzd| done - */ - - /* A rule like - * a>b|a - * creates an infinite loop. To prevent that, we put an arbitrary - * limit on the number of iterations that we take, one that is - * high enough that any reasonable rules are ok, but low enough to - * prevent a server from hanging. The limit is 16 times the - * number of characters n, unless n is so large that 16n exceeds a - * uint32_t. - */ - uint32_t loopCount = 0; - uint32_t loopLimit = index.limit - index.start; - if (loopLimit >= 0x10000000) { - loopLimit = 0xFFFFFFFF; - } else { - loopLimit <<= 4; - } - - // Transliterator locking. Rule-based Transliterators are not thread safe; concurrent - // operations must be prevented. - // A Complication: compound transliterators can result in recursive entries to this - // function, sometimes with different "This" objects, always with the same text. - // Double-locking must be prevented in these cases. - // - - UBool lockedMutexAtThisLevel = false; - - // Test whether this request is operating on the same text string as - // some other transliteration that is still in progress and holding the - // transliteration mutex. If so, do not lock the transliteration - // mutex again. - // - // gLockedText variable is protected by the global ICU mutex. - // Shared RBT data protected by transliteratorDataMutex. - // - // TODO(andy): Need a better scheme for handling this. - - static UMutex transliteratorDataMutex; - UBool needToLock; - { - Mutex m; - needToLock = (&text != gLockedText); - } - if (needToLock) { - umtx_lock(&transliteratorDataMutex); // Contention, longish waits possible here. - Mutex m; - gLockedText = &text; - lockedMutexAtThisLevel = true; - } - - // Check to make sure we don't dereference a null pointer. - if (fData != NULL) { - while (index.start < index.limit && - loopCount <= loopLimit && - fData->ruleSet.transliterate(text, index, isIncremental)) { - ++loopCount; - } - } - if (lockedMutexAtThisLevel) { - { - Mutex m; - gLockedText = NULL; - } - umtx_unlock(&transliteratorDataMutex); - } -} - -UnicodeString& RuleBasedTransliterator::toRules(UnicodeString& rulesSource, - UBool escapeUnprintable) const { - return fData->ruleSet.toRules(rulesSource, escapeUnprintable); -} - -/** - * Implement Transliterator framework - */ -void RuleBasedTransliterator::handleGetSourceSet(UnicodeSet& result) const { - fData->ruleSet.getSourceTargetSet(result, false); -} - -/** - * Override Transliterator framework - */ -UnicodeSet& RuleBasedTransliterator::getTargetSet(UnicodeSet& result) const { - return fData->ruleSet.getSourceTargetSet(result, true); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2015, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/17/99 aliu Creation. +********************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/rep.h" +#include "unicode/uniset.h" +#include "rbt_pars.h" +#include "rbt_data.h" +#include "rbt_rule.h" +#include "rbt.h" +#include "mutex.h" +#include "umutex.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTransliterator) + +static Replaceable *gLockedText = nullptr; + +void RuleBasedTransliterator::_construct(const UnicodeString& rules, + UTransDirection direction, + UParseError& parseError, + UErrorCode& status) { + fData = 0; + isDataOwned = true; + if (U_FAILURE(status)) { + return; + } + + TransliteratorParser parser(status); + parser.parse(rules, direction, parseError, status); + if (U_FAILURE(status)) { + return; + } + + if (parser.idBlockVector.size() != 0 || + parser.compoundFilter != nullptr || + parser.dataVector.size() == 0) { + status = U_INVALID_RBT_SYNTAX; // ::ID blocks disallowed in RBT + return; + } + + fData = (TransliterationRuleData*)parser.dataVector.orphanElementAt(0); + setMaximumContextLength(fData->ruleSet.getMaximumContextLength()); +} + +/** + * Constructs a new transliterator from the given rules. + * @param id the id for the transliterator. + * @param rules rules, separated by ';' + * @param direction either FORWARD or REVERSE. + * @param adoptedFilter the filter for this transliterator. + * @param parseError Struct to receive information on position + * of error if an error is encountered + * @param status Output param set to success/failure code. + * @exception IllegalArgumentException if rules are malformed + * or direction is invalid. + */ +RuleBasedTransliterator::RuleBasedTransliterator( + const UnicodeString& id, + const UnicodeString& rules, + UTransDirection direction, + UnicodeFilter* adoptedFilter, + UParseError& parseError, + UErrorCode& status) : + Transliterator(id, adoptedFilter) { + _construct(rules, direction,parseError,status); +} + +/** + * Constructs a new transliterator from the given rules. + * @param id the id for the transliterator. + * @param rules rules, separated by ';' + * @param direction either FORWARD or REVERSE. + * @param adoptedFilter the filter for this transliterator. + * @param status Output param set to success/failure code. + * @exception IllegalArgumentException if rules are malformed + * or direction is invalid. + */ +/*RuleBasedTransliterator::RuleBasedTransliterator( + const UnicodeString& id, + const UnicodeString& rules, + UTransDirection direction, + UnicodeFilter* adoptedFilter, + UErrorCode& status) : + Transliterator(id, adoptedFilter) { + UParseError parseError; + _construct(rules, direction,parseError, status); +}*/ + +/** + * Convenience constructor with no filter. + */ +/*RuleBasedTransliterator::RuleBasedTransliterator( + const UnicodeString& id, + const UnicodeString& rules, + UTransDirection direction, + UErrorCode& status) : + Transliterator(id, 0) { + UParseError parseError; + _construct(rules, direction,parseError, status); +}*/ + +/** + * Convenience constructor with no filter and FORWARD direction. + */ +/*RuleBasedTransliterator::RuleBasedTransliterator( + const UnicodeString& id, + const UnicodeString& rules, + UErrorCode& status) : + Transliterator(id, 0) { + UParseError parseError; + _construct(rules, UTRANS_FORWARD, parseError, status); +}*/ + +/** + * Convenience constructor with FORWARD direction. + */ +/*RuleBasedTransliterator::RuleBasedTransliterator( + const UnicodeString& id, + const UnicodeString& rules, + UnicodeFilter* adoptedFilter, + UErrorCode& status) : + Transliterator(id, adoptedFilter) { + UParseError parseError; + _construct(rules, UTRANS_FORWARD,parseError, status); +}*/ + +RuleBasedTransliterator::RuleBasedTransliterator(const UnicodeString& id, + const TransliterationRuleData* theData, + UnicodeFilter* adoptedFilter) : + Transliterator(id, adoptedFilter), + fData((TransliterationRuleData*)theData), // cast away const + isDataOwned(false) { + setMaximumContextLength(fData->ruleSet.getMaximumContextLength()); +} + +/** + * Internal constructor. + */ +RuleBasedTransliterator::RuleBasedTransliterator(const UnicodeString& id, + TransliterationRuleData* theData, + UBool isDataAdopted) : + Transliterator(id, 0), + fData(theData), + isDataOwned(isDataAdopted) { + setMaximumContextLength(fData->ruleSet.getMaximumContextLength()); +} + +/** + * Copy constructor. + */ +RuleBasedTransliterator::RuleBasedTransliterator( + const RuleBasedTransliterator& other) : + Transliterator(other), fData(other.fData), + isDataOwned(other.isDataOwned) { + + // The data object may or may not be owned. If it is not owned we + // share it; it is invariant. If it is owned, it's still + // invariant, but we need to copy it to prevent double-deletion. + // If this becomes a performance issue (if people do a lot of RBT + // copying -- unlikely) we can reference count the data object. + + // Only do a deep copy if this is owned data, that is, data that + // will be later deleted. System transliterators contain + // non-owned data. + if (isDataOwned) { + fData = new TransliterationRuleData(*other.fData); + } +} + +/** + * Destructor. + */ +RuleBasedTransliterator::~RuleBasedTransliterator() { + // Delete the data object only if we own it. + if (isDataOwned) { + delete fData; + } +} + +RuleBasedTransliterator* +RuleBasedTransliterator::clone() const { + return new RuleBasedTransliterator(*this); +} + +/** + * Implements {@link Transliterator#handleTransliterate}. + */ +void +RuleBasedTransliterator::handleTransliterate(Replaceable& text, UTransPosition& index, + UBool isIncremental) const { + /* We keep contextStart and contextLimit fixed the entire time, + * relative to the text -- contextLimit may move numerically if + * text is inserted or removed. The start offset moves toward + * limit, with replacements happening under it. + * + * Example: rules 1. ab>x|y + * 2. yc>z + * + * |eabcd begin - no match, advance start + * e|abcd match rule 1 - change text & adjust start + * ex|ycd match rule 2 - change text & adjust start + * exz|d no match, advance start + * exzd| done + */ + + /* A rule like + * a>b|a + * creates an infinite loop. To prevent that, we put an arbitrary + * limit on the number of iterations that we take, one that is + * high enough that any reasonable rules are ok, but low enough to + * prevent a server from hanging. The limit is 16 times the + * number of characters n, unless n is so large that 16n exceeds a + * uint32_t. + */ + uint32_t loopCount = 0; + uint32_t loopLimit = index.limit - index.start; + if (loopLimit >= 0x10000000) { + loopLimit = 0xFFFFFFFF; + } else { + loopLimit <<= 4; + } + + // Transliterator locking. Rule-based Transliterators are not thread safe; concurrent + // operations must be prevented. + // A Complication: compound transliterators can result in recursive entries to this + // function, sometimes with different "This" objects, always with the same text. + // Double-locking must be prevented in these cases. + // + + UBool lockedMutexAtThisLevel = false; + + // Test whether this request is operating on the same text string as + // some other transliteration that is still in progress and holding the + // transliteration mutex. If so, do not lock the transliteration + // mutex again. + // + // gLockedText variable is protected by the global ICU mutex. + // Shared RBT data protected by transliteratorDataMutex. + // + // TODO(andy): Need a better scheme for handling this. + + static UMutex transliteratorDataMutex; + UBool needToLock; + { + Mutex m; + needToLock = (&text != gLockedText); + } + if (needToLock) { + umtx_lock(&transliteratorDataMutex); // Contention, longish waits possible here. + Mutex m; + gLockedText = &text; + lockedMutexAtThisLevel = true; + } + + // Check to make sure we don't dereference a null pointer. + if (fData != nullptr) { + while (index.start < index.limit && + loopCount <= loopLimit && + fData->ruleSet.transliterate(text, index, isIncremental)) { + ++loopCount; + } + } + if (lockedMutexAtThisLevel) { + { + Mutex m; + gLockedText = nullptr; + } + umtx_unlock(&transliteratorDataMutex); + } +} + +UnicodeString& RuleBasedTransliterator::toRules(UnicodeString& rulesSource, + UBool escapeUnprintable) const { + return fData->ruleSet.toRules(rulesSource, escapeUnprintable); +} + +/** + * Implement Transliterator framework + */ +void RuleBasedTransliterator::handleGetSourceSet(UnicodeSet& result) const { + fData->ruleSet.getSourceTargetSet(result, false); +} + +/** + * Override Transliterator framework + */ +UnicodeSet& RuleBasedTransliterator::getTargetSet(UnicodeSet& result) const { + return fData->ruleSet.getSourceTargetSet(result, true); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ diff --git a/deps/icu-small/source/i18n/rbt.h b/deps/icu-small/source/i18n/rbt.h index 8a43c90d462bee..53fa4b62381e3f 100644 --- a/deps/icu-small/source/i18n/rbt.h +++ b/deps/icu-small/source/i18n/rbt.h @@ -1,223 +1,223 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2007, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/17/99 aliu Creation. -********************************************************************** -*/ -#ifndef RBT_H -#define RBT_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/translit.h" -#include "unicode/utypes.h" -#include "unicode/parseerr.h" -#include "unicode/udata.h" - -#define U_ICUDATA_TRANSLIT U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "translit" - -U_NAMESPACE_BEGIN - -class TransliterationRuleData; - -/** - * RuleBasedTransliterator is a transliterator - * built from a set of rules as defined for - * Transliterator::createFromRules(). - * See the C++ class Transliterator documentation for the rule syntax. - * - * @author Alan Liu - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ -class RuleBasedTransliterator : public Transliterator { -private: - /** - * The data object is immutable, so we can freely share it with - * other instances of RBT, as long as we do NOT own this object. - * TODO: data is no longer immutable. See bugs #1866, 2155 - */ - TransliterationRuleData* fData; - - /** - * If true, we own the data object and must delete it. - */ - UBool isDataOwned; - -public: - - /** - * Constructs a new transliterator from the given rules. - * @param rules rules, separated by ';' - * @param direction either FORWARD or REVERSE. - * @exception IllegalArgumentException if rules are malformed. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - RuleBasedTransliterator(const UnicodeString& id, - const UnicodeString& rules, - UTransDirection direction, - UnicodeFilter* adoptedFilter, - UParseError& parseError, - UErrorCode& status); - - /** - * Constructs a new transliterator from the given rules. - * @param rules rules, separated by ';' - * @param direction either FORWARD or REVERSE. - * @exception IllegalArgumentException if rules are malformed. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - /*RuleBasedTransliterator(const UnicodeString& id, - const UnicodeString& rules, - UTransDirection direction, - UnicodeFilter* adoptedFilter, - UErrorCode& status);*/ - - /** - * Convenience constructor with no filter. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - /*RuleBasedTransliterator(const UnicodeString& id, - const UnicodeString& rules, - UTransDirection direction, - UErrorCode& status);*/ - - /** - * Convenience constructor with no filter and FORWARD direction. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - /*RuleBasedTransliterator(const UnicodeString& id, - const UnicodeString& rules, - UErrorCode& status);*/ - - /** - * Convenience constructor with FORWARD direction. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - /*RuleBasedTransliterator(const UnicodeString& id, - const UnicodeString& rules, - UnicodeFilter* adoptedFilter, - UErrorCode& status);*/ -private: - - friend class TransliteratorRegistry; // to access TransliterationRuleData convenience ctor - /** - * Convenience constructor. - * @param id the id for the transliterator. - * @param theData the rule data for the transliterator. - * @param adoptedFilter the filter for the transliterator - */ - RuleBasedTransliterator(const UnicodeString& id, - const TransliterationRuleData* theData, - UnicodeFilter* adoptedFilter = 0); - - - friend class Transliterator; // to access following ct - - /** - * Internal constructor. - * @param id the id for the transliterator. - * @param theData the rule data for the transliterator. - * @param isDataAdopted determine who will own the 'data' object. True, the caller should not delete 'data'. - */ - RuleBasedTransliterator(const UnicodeString& id, - TransliterationRuleData* data, - UBool isDataAdopted); - -public: - - /** - * Copy constructor. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - RuleBasedTransliterator(const RuleBasedTransliterator&); - - virtual ~RuleBasedTransliterator(); - - /** - * Implement Transliterator API. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - virtual RuleBasedTransliterator* clone() const override; - -protected: - /** - * Implements {@link Transliterator#handleTransliterate}. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - virtual void handleTransliterate(Replaceable& text, UTransPosition& offsets, - UBool isIncremental) const override; - -public: - /** - * Return a representation of this transliterator as source rules. - * These rules will produce an equivalent transliterator if used - * to construct a new transliterator. - * @param result the string to receive the rules. Previous - * contents will be deleted. - * @param escapeUnprintable if true then convert unprintable - * character to their hex escape representations, \uxxxx or - * \Uxxxxxxxx. Unprintable characters are those other than - * U+000A, U+0020..U+007E. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - virtual UnicodeString& toRules(UnicodeString& result, - UBool escapeUnprintable) const override; - -protected: - /** - * Implement Transliterator framework - */ - virtual void handleGetSourceSet(UnicodeSet& result) const override; - -public: - /** - * Override Transliterator framework - */ - virtual UnicodeSet& getTargetSet(UnicodeSet& result) const override; - - /** - * Return the class ID for this class. This is useful only for - * comparing to a return value from getDynamicClassID(). For example: - *

      -     * .      Base* polymorphic_pointer = createPolymorphicObject();
      -     * .      if (polymorphic_pointer->getDynamicClassID() ==
      -     * .          Derived::getStaticClassID()) ...
      -     * 
      - * @return The class ID for all objects of this class. - * @internal Use transliterator factory methods instead since this class will be removed in that release. - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * Returns a unique class ID polymorphically. This method - * is to implement a simple version of RTTI, since not all C++ - * compilers support genuine RTTI. Polymorphic operator==() and - * clone() methods call this method. - * - * @return The class ID for this object. All objects of a given - * class have the same class ID. Objects of other classes have - * different class IDs. - */ - virtual UClassID getDynamicClassID(void) const override; - -private: - - void _construct(const UnicodeString& rules, - UTransDirection direction, - UParseError& parseError, - UErrorCode& status); -}; - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2007, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/17/99 aliu Creation. +********************************************************************** +*/ +#ifndef RBT_H +#define RBT_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/translit.h" +#include "unicode/utypes.h" +#include "unicode/parseerr.h" +#include "unicode/udata.h" + +#define U_ICUDATA_TRANSLIT U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "translit" + +U_NAMESPACE_BEGIN + +class TransliterationRuleData; + +/** + * RuleBasedTransliterator is a transliterator + * built from a set of rules as defined for + * Transliterator::createFromRules(). + * See the C++ class Transliterator documentation for the rule syntax. + * + * @author Alan Liu + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ +class RuleBasedTransliterator : public Transliterator { +private: + /** + * The data object is immutable, so we can freely share it with + * other instances of RBT, as long as we do NOT own this object. + * TODO: data is no longer immutable. See bugs #1866, 2155 + */ + TransliterationRuleData* fData; + + /** + * If true, we own the data object and must delete it. + */ + UBool isDataOwned; + +public: + + /** + * Constructs a new transliterator from the given rules. + * @param rules rules, separated by ';' + * @param direction either FORWARD or REVERSE. + * @exception IllegalArgumentException if rules are malformed. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + RuleBasedTransliterator(const UnicodeString& id, + const UnicodeString& rules, + UTransDirection direction, + UnicodeFilter* adoptedFilter, + UParseError& parseError, + UErrorCode& status); + + /** + * Constructs a new transliterator from the given rules. + * @param rules rules, separated by ';' + * @param direction either FORWARD or REVERSE. + * @exception IllegalArgumentException if rules are malformed. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + /*RuleBasedTransliterator(const UnicodeString& id, + const UnicodeString& rules, + UTransDirection direction, + UnicodeFilter* adoptedFilter, + UErrorCode& status);*/ + + /** + * Convenience constructor with no filter. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + /*RuleBasedTransliterator(const UnicodeString& id, + const UnicodeString& rules, + UTransDirection direction, + UErrorCode& status);*/ + + /** + * Convenience constructor with no filter and FORWARD direction. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + /*RuleBasedTransliterator(const UnicodeString& id, + const UnicodeString& rules, + UErrorCode& status);*/ + + /** + * Convenience constructor with FORWARD direction. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + /*RuleBasedTransliterator(const UnicodeString& id, + const UnicodeString& rules, + UnicodeFilter* adoptedFilter, + UErrorCode& status);*/ +private: + + friend class TransliteratorRegistry; // to access TransliterationRuleData convenience ctor + /** + * Convenience constructor. + * @param id the id for the transliterator. + * @param theData the rule data for the transliterator. + * @param adoptedFilter the filter for the transliterator + */ + RuleBasedTransliterator(const UnicodeString& id, + const TransliterationRuleData* theData, + UnicodeFilter* adoptedFilter = 0); + + + friend class Transliterator; // to access following ct + + /** + * Internal constructor. + * @param id the id for the transliterator. + * @param theData the rule data for the transliterator. + * @param isDataAdopted determine who will own the 'data' object. True, the caller should not delete 'data'. + */ + RuleBasedTransliterator(const UnicodeString& id, + TransliterationRuleData* data, + UBool isDataAdopted); + +public: + + /** + * Copy constructor. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + RuleBasedTransliterator(const RuleBasedTransliterator&); + + virtual ~RuleBasedTransliterator(); + + /** + * Implement Transliterator API. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + virtual RuleBasedTransliterator* clone() const override; + +protected: + /** + * Implements {@link Transliterator#handleTransliterate}. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + virtual void handleTransliterate(Replaceable& text, UTransPosition& offsets, + UBool isIncremental) const override; + +public: + /** + * Return a representation of this transliterator as source rules. + * These rules will produce an equivalent transliterator if used + * to construct a new transliterator. + * @param result the string to receive the rules. Previous + * contents will be deleted. + * @param escapeUnprintable if true then convert unprintable + * character to their hex escape representations, \uxxxx or + * \Uxxxxxxxx. Unprintable characters are those other than + * U+000A, U+0020..U+007E. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + virtual UnicodeString& toRules(UnicodeString& result, + UBool escapeUnprintable) const override; + +protected: + /** + * Implement Transliterator framework + */ + virtual void handleGetSourceSet(UnicodeSet& result) const override; + +public: + /** + * Override Transliterator framework + */ + virtual UnicodeSet& getTargetSet(UnicodeSet& result) const override; + + /** + * Return the class ID for this class. This is useful only for + * comparing to a return value from getDynamicClassID(). For example: + *
      +     * .      Base* polymorphic_pointer = createPolymorphicObject();
      +     * .      if (polymorphic_pointer->getDynamicClassID() ==
      +     * .          Derived::getStaticClassID()) ...
      +     * 
      + * @return The class ID for all objects of this class. + * @internal Use transliterator factory methods instead since this class will be removed in that release. + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + + /** + * Returns a unique class ID polymorphically. This method + * is to implement a simple version of RTTI, since not all C++ + * compilers support genuine RTTI. Polymorphic operator==() and + * clone() methods call this method. + * + * @return The class ID for this object. All objects of a given + * class have the same class ID. Objects of other classes have + * different class IDs. + */ + virtual UClassID getDynamicClassID() const override; + +private: + + void _construct(const UnicodeString& rules, + UTransDirection direction, + UParseError& parseError, + UErrorCode& status); +}; + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/rbt_data.cpp b/deps/icu-small/source/i18n/rbt_data.cpp index 866f0c2bfa1c4b..46e72a75291e4a 100644 --- a/deps/icu-small/source/i18n/rbt_data.cpp +++ b/deps/icu-small/source/i18n/rbt_data.cpp @@ -1,119 +1,119 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/17/99 aliu Creation. -********************************************************************** -*/ - -#include "unicode/utypes.h" -#include "umutex.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/unistr.h" -#include "unicode/uniset.h" -#include "rbt_data.h" -#include "hash.h" -#include "cmemory.h" - -U_NAMESPACE_BEGIN - -TransliterationRuleData::TransliterationRuleData(UErrorCode& status) - : UMemory(), ruleSet(status), variableNames(status), - variables(0), variablesAreOwned(true) -{ - if (U_FAILURE(status)) { - return; - } - variableNames.setValueDeleter(uprv_deleteUObject); - variables = 0; - variablesLength = 0; -} - -TransliterationRuleData::TransliterationRuleData(const TransliterationRuleData& other) : - UMemory(other), ruleSet(other.ruleSet), - variablesAreOwned(true), - variablesBase(other.variablesBase), - variablesLength(other.variablesLength) -{ - UErrorCode status = U_ZERO_ERROR; - int32_t i = 0; - variableNames.setValueDeleter(uprv_deleteUObject); - int32_t pos = UHASH_FIRST; - const UHashElement *e; - while ((e = other.variableNames.nextElement(pos)) != 0) { - UnicodeString* value = - new UnicodeString(*(const UnicodeString*)e->value.pointer); - // Exit out if value could not be created. - if (value == NULL) { - return; - } - variableNames.put(*(UnicodeString*)e->key.pointer, value, status); - } - - variables = 0; - if (other.variables != 0) { - variables = (UnicodeFunctor **)uprv_malloc(variablesLength * sizeof(UnicodeFunctor *)); - /* test for NULL */ - if (variables == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - for (i=0; iclone(); - if (variables[i] == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - } - } - // Remove the array and exit if memory allocation error occurred. - if (U_FAILURE(status)) { - for (int32_t n = i-1; n >= 0; n--) { - delete variables[n]; - } - uprv_free(variables); - variables = NULL; - return; - } - - // Do this last, _after_ setting up variables[]. - ruleSet.setData(this); // ruleSet must already be frozen -} - -TransliterationRuleData::~TransliterationRuleData() { - if (variablesAreOwned && variables != 0) { - for (int32_t i=0; i= 0 && i < variablesLength) ? variables[i] : 0; -} - -UnicodeMatcher* -TransliterationRuleData::lookupMatcher(UChar32 standIn) const { - UnicodeFunctor *f = lookup(standIn); - return (f != 0) ? f->toMatcher() : 0; -} - -UnicodeReplacer* -TransliterationRuleData::lookupReplacer(UChar32 standIn) const { - UnicodeFunctor *f = lookup(standIn); - return (f != 0) ? f->toReplacer() : 0; -} - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/17/99 aliu Creation. +********************************************************************** +*/ + +#include "unicode/utypes.h" +#include "umutex.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/unistr.h" +#include "unicode/uniset.h" +#include "rbt_data.h" +#include "hash.h" +#include "cmemory.h" + +U_NAMESPACE_BEGIN + +TransliterationRuleData::TransliterationRuleData(UErrorCode& status) + : UMemory(), ruleSet(status), variableNames(status), + variables(0), variablesAreOwned(true) +{ + if (U_FAILURE(status)) { + return; + } + variableNames.setValueDeleter(uprv_deleteUObject); + variables = 0; + variablesLength = 0; +} + +TransliterationRuleData::TransliterationRuleData(const TransliterationRuleData& other) : + UMemory(other), ruleSet(other.ruleSet), + variablesAreOwned(true), + variablesBase(other.variablesBase), + variablesLength(other.variablesLength) +{ + UErrorCode status = U_ZERO_ERROR; + int32_t i = 0; + variableNames.setValueDeleter(uprv_deleteUObject); + int32_t pos = UHASH_FIRST; + const UHashElement *e; + while ((e = other.variableNames.nextElement(pos)) != 0) { + UnicodeString* value = + new UnicodeString(*(const UnicodeString*)e->value.pointer); + // Exit out if value could not be created. + if (value == nullptr) { + return; + } + variableNames.put(*(UnicodeString*)e->key.pointer, value, status); + } + + variables = 0; + if (other.variables != 0) { + variables = (UnicodeFunctor **)uprv_malloc(variablesLength * sizeof(UnicodeFunctor *)); + /* test for nullptr */ + if (variables == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + for (i=0; iclone(); + if (variables[i] == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + break; + } + } + } + // Remove the array and exit if memory allocation error occurred. + if (U_FAILURE(status)) { + for (int32_t n = i-1; n >= 0; n--) { + delete variables[n]; + } + uprv_free(variables); + variables = nullptr; + return; + } + + // Do this last, _after_ setting up variables[]. + ruleSet.setData(this); // ruleSet must already be frozen +} + +TransliterationRuleData::~TransliterationRuleData() { + if (variablesAreOwned && variables != 0) { + for (int32_t i=0; i= 0 && i < variablesLength) ? variables[i] : 0; +} + +UnicodeMatcher* +TransliterationRuleData::lookupMatcher(UChar32 standIn) const { + UnicodeFunctor *f = lookup(standIn); + return (f != 0) ? f->toMatcher() : 0; +} + +UnicodeReplacer* +TransliterationRuleData::lookupReplacer(UChar32 standIn) const { + UnicodeFunctor *f = lookup(standIn); + return (f != 0) ? f->toReplacer() : 0; +} + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ diff --git a/deps/icu-small/source/i18n/rbt_data.h b/deps/icu-small/source/i18n/rbt_data.h index 52a961dde010d3..7dfaee60d7bba1 100644 --- a/deps/icu-small/source/i18n/rbt_data.h +++ b/deps/icu-small/source/i18n/rbt_data.h @@ -1,154 +1,154 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2007, International Business Machines Corporation -* and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/17/99 aliu Creation. -********************************************************************** -*/ -#ifndef RBT_DATA_H -#define RBT_DATA_H - -#include "unicode/utypes.h" -#include "unicode/uclean.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/uobject.h" -#include "rbt_set.h" -#include "hash.h" - -U_NAMESPACE_BEGIN - -class UnicodeFunctor; -class UnicodeMatcher; -class UnicodeReplacer; - -/** - * The rule data for a RuleBasedTransliterators. RBT objects hold - * a const pointer to a TRD object that they do not own. TRD objects - * are essentially the parsed rules in compact, usable form. The - * TRD objects themselves are held for the life of the process in - * a static cache owned by Transliterator. - * - * This class' API is a little asymmetric. There is a method to - * define a variable, but no way to define a set. This is because the - * sets are defined by the parser in a UVector, and the vector is - * copied into a fixed-size array here. Once this is done, no new - * sets may be defined. In practice, there is no need to do so, since - * generating the data and using it are discrete phases. When there - * is a need to access the set data during the parse phase, another - * data structure handles this. See the parsing code for more - * details. - */ -class TransliterationRuleData : public UMemory { - -public: - - // PUBLIC DATA MEMBERS - - /** - * Rule table. May be empty. - */ - TransliterationRuleSet ruleSet; - - /** - * Map variable name (String) to variable (UnicodeString). A variable name - * corresponds to zero or more characters, stored in a UnicodeString in - * this hash. One or more of these chars may also correspond to a - * UnicodeMatcher, in which case the character in the UnicodeString in this hash is - * a stand-in: it is an index for a secondary lookup in - * data.variables. The stand-in also represents the UnicodeMatcher in - * the stored rules. - */ - Hashtable variableNames; - - /** - * Map category variable (UChar) to set (UnicodeFunctor). - * Variables that correspond to a set of characters are mapped - * from variable name to a stand-in character in data.variableNames. - * The stand-in then serves as a key in this hash to lookup the - * actual UnicodeFunctor object. In addition, the stand-in is - * stored in the rule text to represent the set of characters. - * variables[i] represents character (variablesBase + i). - */ - UnicodeFunctor** variables; - - /** - * Flag that indicates whether the variables are owned (if a single - * call to Transliterator::createFromRules() produces a CompoundTransliterator - * with more than one RuleBasedTransliterator as children, they all share - * the same variables list, so only the first one is considered to own - * the variables) - */ - UBool variablesAreOwned; - - /** - * The character that represents variables[0]. Characters - * variablesBase through variablesBase + - * variablesLength - 1 represent UnicodeFunctor objects. - */ - UChar variablesBase; - - /** - * The length of variables. - */ - int32_t variablesLength; - -public: - - /** - * Constructor - * @param status Output param set to success/failure code on exit. - */ - TransliterationRuleData(UErrorCode& status); - - /** - * Copy Constructor - */ - TransliterationRuleData(const TransliterationRuleData&); - - /** - * destructor - */ - ~TransliterationRuleData(); - - /** - * Given a stand-in character, return the UnicodeFunctor that it - * represents, or NULL if it doesn't represent anything. - * @param standIn the given stand-in character. - * @return the UnicodeFunctor that 'standIn' represents - */ - UnicodeFunctor* lookup(UChar32 standIn) const; - - /** - * Given a stand-in character, return the UnicodeMatcher that it - * represents, or NULL if it doesn't represent anything or if it - * represents something that is not a matcher. - * @param standIn the given stand-in character. - * @return return the UnicodeMatcher that 'standIn' represents - */ - UnicodeMatcher* lookupMatcher(UChar32 standIn) const; - - /** - * Given a stand-in character, return the UnicodeReplacer that it - * represents, or NULL if it doesn't represent anything or if it - * represents something that is not a replacer. - * @param standIn the given stand-in character. - * @return return the UnicodeReplacer that 'standIn' represents - */ - UnicodeReplacer* lookupReplacer(UChar32 standIn) const; - - -private: - TransliterationRuleData &operator=(const TransliterationRuleData &other); // forbid copying of this class -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2007, International Business Machines Corporation +* and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/17/99 aliu Creation. +********************************************************************** +*/ +#ifndef RBT_DATA_H +#define RBT_DATA_H + +#include "unicode/utypes.h" +#include "unicode/uclean.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/uobject.h" +#include "rbt_set.h" +#include "hash.h" + +U_NAMESPACE_BEGIN + +class UnicodeFunctor; +class UnicodeMatcher; +class UnicodeReplacer; + +/** + * The rule data for a RuleBasedTransliterators. RBT objects hold + * a const pointer to a TRD object that they do not own. TRD objects + * are essentially the parsed rules in compact, usable form. The + * TRD objects themselves are held for the life of the process in + * a static cache owned by Transliterator. + * + * This class' API is a little asymmetric. There is a method to + * define a variable, but no way to define a set. This is because the + * sets are defined by the parser in a UVector, and the vector is + * copied into a fixed-size array here. Once this is done, no new + * sets may be defined. In practice, there is no need to do so, since + * generating the data and using it are discrete phases. When there + * is a need to access the set data during the parse phase, another + * data structure handles this. See the parsing code for more + * details. + */ +class TransliterationRuleData : public UMemory { + +public: + + // PUBLIC DATA MEMBERS + + /** + * Rule table. May be empty. + */ + TransliterationRuleSet ruleSet; + + /** + * Map variable name (String) to variable (UnicodeString). A variable name + * corresponds to zero or more characters, stored in a UnicodeString in + * this hash. One or more of these chars may also correspond to a + * UnicodeMatcher, in which case the character in the UnicodeString in this hash is + * a stand-in: it is an index for a secondary lookup in + * data.variables. The stand-in also represents the UnicodeMatcher in + * the stored rules. + */ + Hashtable variableNames; + + /** + * Map category variable (char16_t) to set (UnicodeFunctor). + * Variables that correspond to a set of characters are mapped + * from variable name to a stand-in character in data.variableNames. + * The stand-in then serves as a key in this hash to lookup the + * actual UnicodeFunctor object. In addition, the stand-in is + * stored in the rule text to represent the set of characters. + * variables[i] represents character (variablesBase + i). + */ + UnicodeFunctor** variables; + + /** + * Flag that indicates whether the variables are owned (if a single + * call to Transliterator::createFromRules() produces a CompoundTransliterator + * with more than one RuleBasedTransliterator as children, they all share + * the same variables list, so only the first one is considered to own + * the variables) + */ + UBool variablesAreOwned; + + /** + * The character that represents variables[0]. Characters + * variablesBase through variablesBase + + * variablesLength - 1 represent UnicodeFunctor objects. + */ + char16_t variablesBase; + + /** + * The length of variables. + */ + int32_t variablesLength; + +public: + + /** + * Constructor + * @param status Output param set to success/failure code on exit. + */ + TransliterationRuleData(UErrorCode& status); + + /** + * Copy Constructor + */ + TransliterationRuleData(const TransliterationRuleData&); + + /** + * destructor + */ + ~TransliterationRuleData(); + + /** + * Given a stand-in character, return the UnicodeFunctor that it + * represents, or nullptr if it doesn't represent anything. + * @param standIn the given stand-in character. + * @return the UnicodeFunctor that 'standIn' represents + */ + UnicodeFunctor* lookup(UChar32 standIn) const; + + /** + * Given a stand-in character, return the UnicodeMatcher that it + * represents, or nullptr if it doesn't represent anything or if it + * represents something that is not a matcher. + * @param standIn the given stand-in character. + * @return return the UnicodeMatcher that 'standIn' represents + */ + UnicodeMatcher* lookupMatcher(UChar32 standIn) const; + + /** + * Given a stand-in character, return the UnicodeReplacer that it + * represents, or nullptr if it doesn't represent anything or if it + * represents something that is not a replacer. + * @param standIn the given stand-in character. + * @return return the UnicodeReplacer that 'standIn' represents + */ + UnicodeReplacer* lookupReplacer(UChar32 standIn) const; + + +private: + TransliterationRuleData &operator=(const TransliterationRuleData &other); // forbid copying of this class +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/rbt_pars.cpp b/deps/icu-small/source/i18n/rbt_pars.cpp index f13bf1c227a16b..a9f3b1117c2e42 100644 --- a/deps/icu-small/source/i18n/rbt_pars.cpp +++ b/deps/icu-small/source/i18n/rbt_pars.cpp @@ -1,1773 +1,1773 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 1999-2016, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - * Date Name Description - * 11/17/99 aliu Creation. - ********************************************************************** - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/uobject.h" -#include "unicode/parseerr.h" -#include "unicode/parsepos.h" -#include "unicode/putil.h" -#include "unicode/uchar.h" -#include "unicode/ustring.h" -#include "unicode/uniset.h" -#include "unicode/utf16.h" -#include "cstring.h" -#include "funcrepl.h" -#include "hash.h" -#include "quant.h" -#include "rbt.h" -#include "rbt_data.h" -#include "rbt_pars.h" -#include "rbt_rule.h" -#include "strmatch.h" -#include "strrepl.h" -#include "unicode/symtable.h" -#include "tridpars.h" -#include "uvector.h" -#include "hash.h" -#include "patternprops.h" -#include "util.h" -#include "cmemory.h" -#include "uprops.h" -#include "putilimp.h" - -// Operators -#define VARIABLE_DEF_OP ((UChar)0x003D) /*=*/ -#define FORWARD_RULE_OP ((UChar)0x003E) /*>*/ -#define REVERSE_RULE_OP ((UChar)0x003C) /*<*/ -#define FWDREV_RULE_OP ((UChar)0x007E) /*~*/ // internal rep of <> op - -// Other special characters -#define QUOTE ((UChar)0x0027) /*'*/ -#define ESCAPE ((UChar)0x005C) /*\*/ -#define END_OF_RULE ((UChar)0x003B) /*;*/ -#define RULE_COMMENT_CHAR ((UChar)0x0023) /*#*/ - -#define SEGMENT_OPEN ((UChar)0x0028) /*(*/ -#define SEGMENT_CLOSE ((UChar)0x0029) /*)*/ -#define CONTEXT_ANTE ((UChar)0x007B) /*{*/ -#define CONTEXT_POST ((UChar)0x007D) /*}*/ -#define CURSOR_POS ((UChar)0x007C) /*|*/ -#define CURSOR_OFFSET ((UChar)0x0040) /*@*/ -#define ANCHOR_START ((UChar)0x005E) /*^*/ -#define KLEENE_STAR ((UChar)0x002A) /***/ -#define ONE_OR_MORE ((UChar)0x002B) /*+*/ -#define ZERO_OR_ONE ((UChar)0x003F) /*?*/ - -#define DOT ((UChar)46) /*.*/ - -static const UChar DOT_SET[] = { // "[^[:Zp:][:Zl:]\r\n$]"; - 91, 94, 91, 58, 90, 112, 58, 93, 91, 58, 90, - 108, 58, 93, 92, 114, 92, 110, 36, 93, 0 -}; - -// A function is denoted &Source-Target/Variant(text) -#define FUNCTION ((UChar)38) /*&*/ - -// Aliases for some of the syntax characters. These are provided so -// transliteration rules can be expressed in XML without clashing with -// XML syntax characters '<', '>', and '&'. -#define ALT_REVERSE_RULE_OP ((UChar)0x2190) // Left Arrow -#define ALT_FORWARD_RULE_OP ((UChar)0x2192) // Right Arrow -#define ALT_FWDREV_RULE_OP ((UChar)0x2194) // Left Right Arrow -#define ALT_FUNCTION ((UChar)0x2206) // Increment (~Greek Capital Delta) - -// Special characters disallowed at the top level -static const UChar ILLEGAL_TOP[] = {41,0}; // ")" - -// Special characters disallowed within a segment -static const UChar ILLEGAL_SEG[] = {123,125,124,64,0}; // "{}|@" - -// Special characters disallowed within a function argument -static const UChar ILLEGAL_FUNC[] = {94,40,46,42,43,63,123,125,124,64,0}; // "^(.*+?{}|@" - -// By definition, the ANCHOR_END special character is a -// trailing SymbolTable.SYMBOL_REF character. -// private static final char ANCHOR_END = '$'; - -static const UChar gOPERATORS[] = { // "=><" - VARIABLE_DEF_OP, FORWARD_RULE_OP, REVERSE_RULE_OP, - ALT_FORWARD_RULE_OP, ALT_REVERSE_RULE_OP, ALT_FWDREV_RULE_OP, - 0 -}; - -static const UChar HALF_ENDERS[] = { // "=><;" - VARIABLE_DEF_OP, FORWARD_RULE_OP, REVERSE_RULE_OP, - ALT_FORWARD_RULE_OP, ALT_REVERSE_RULE_OP, ALT_FWDREV_RULE_OP, - END_OF_RULE, - 0 -}; - -// These are also used in Transliterator::toRules() -static const int32_t ID_TOKEN_LEN = 2; -static const UChar ID_TOKEN[] = { 0x3A, 0x3A }; // ':', ':' - -/* -commented out until we do real ::BEGIN/::END functionality -static const int32_t BEGIN_TOKEN_LEN = 5; -static const UChar BEGIN_TOKEN[] = { 0x42, 0x45, 0x47, 0x49, 0x4e }; // 'BEGIN' - -static const int32_t END_TOKEN_LEN = 3; -static const UChar END_TOKEN[] = { 0x45, 0x4e, 0x44 }; // 'END' -*/ - -U_NAMESPACE_BEGIN - -//---------------------------------------------------------------------- -// BEGIN ParseData -//---------------------------------------------------------------------- - -/** - * This class implements the SymbolTable interface. It is used - * during parsing to give UnicodeSet access to variables that - * have been defined so far. Note that it uses variablesVector, - * _not_ data.setVariables. - */ -class ParseData : public UMemory, public SymbolTable { -public: - const TransliterationRuleData* data; // alias - - const UVector* variablesVector; // alias - - const Hashtable* variableNames; // alias - - ParseData(const TransliterationRuleData* data = 0, - const UVector* variablesVector = 0, - const Hashtable* variableNames = 0); - - virtual ~ParseData(); - - virtual const UnicodeString* lookup(const UnicodeString& s) const override; - - virtual const UnicodeFunctor* lookupMatcher(UChar32 ch) const override; - - virtual UnicodeString parseReference(const UnicodeString& text, - ParsePosition& pos, int32_t limit) const override; - /** - * Return true if the given character is a matcher standin or a plain - * character (non standin). - */ - UBool isMatcher(UChar32 ch); - - /** - * Return true if the given character is a replacer standin or a plain - * character (non standin). - */ - UBool isReplacer(UChar32 ch); - -private: - ParseData(const ParseData &other); // forbid copying of this class - ParseData &operator=(const ParseData &other); // forbid copying of this class -}; - -ParseData::ParseData(const TransliterationRuleData* d, - const UVector* sets, - const Hashtable* vNames) : - data(d), variablesVector(sets), variableNames(vNames) {} - -ParseData::~ParseData() {} - -/** - * Implement SymbolTable API. - */ -const UnicodeString* ParseData::lookup(const UnicodeString& name) const { - return (const UnicodeString*) variableNames->get(name); -} - -/** - * Implement SymbolTable API. - */ -const UnicodeFunctor* ParseData::lookupMatcher(UChar32 ch) const { - // Note that we cannot use data.lookupSet() because the - // set array has not been constructed yet. - const UnicodeFunctor* set = NULL; - int32_t i = ch - data->variablesBase; - if (i >= 0 && i < variablesVector->size()) { - int32_t j = ch - data->variablesBase; - set = (j < variablesVector->size()) ? - (UnicodeFunctor*) variablesVector->elementAt(j) : 0; - } - return set; -} - -/** - * Implement SymbolTable API. Parse out a symbol reference - * name. - */ -UnicodeString ParseData::parseReference(const UnicodeString& text, - ParsePosition& pos, int32_t limit) const { - int32_t start = pos.getIndex(); - int32_t i = start; - UnicodeString result; - while (i < limit) { - UChar c = text.charAt(i); - if ((i==start && !u_isIDStart(c)) || !u_isIDPart(c)) { - break; - } - ++i; - } - if (i == start) { // No valid name chars - return result; // Indicate failure with empty string - } - pos.setIndex(i); - text.extractBetween(start, i, result); - return result; -} - -UBool ParseData::isMatcher(UChar32 ch) { - // Note that we cannot use data.lookup() because the - // set array has not been constructed yet. - int32_t i = ch - data->variablesBase; - if (i >= 0 && i < variablesVector->size()) { - UnicodeFunctor *f = (UnicodeFunctor*) variablesVector->elementAt(i); - return f != NULL && f->toMatcher() != NULL; - } - return true; -} - -/** - * Return true if the given character is a replacer standin or a plain - * character (non standin). - */ -UBool ParseData::isReplacer(UChar32 ch) { - // Note that we cannot use data.lookup() because the - // set array has not been constructed yet. - int i = ch - data->variablesBase; - if (i >= 0 && i < variablesVector->size()) { - UnicodeFunctor *f = (UnicodeFunctor*) variablesVector->elementAt(i); - return f != NULL && f->toReplacer() != NULL; - } - return true; -} - -//---------------------------------------------------------------------- -// BEGIN RuleHalf -//---------------------------------------------------------------------- - -/** - * A class representing one side of a rule. This class knows how to - * parse half of a rule. It is tightly coupled to the method - * RuleBasedTransliterator.Parser.parseRule(). - */ -class RuleHalf : public UMemory { - -public: - - UnicodeString text; - - int32_t cursor; // position of cursor in text - int32_t ante; // position of ante context marker '{' in text - int32_t post; // position of post context marker '}' in text - - // Record the offset to the cursor either to the left or to the - // right of the key. This is indicated by characters on the output - // side that allow the cursor to be positioned arbitrarily within - // the matching text. For example, abc{def} > | @@@ xyz; changes - // def to xyz and moves the cursor to before abc. Offset characters - // must be at the start or end, and they cannot move the cursor past - // the ante- or postcontext text. Placeholders are only valid in - // output text. The length of the ante and post context is - // determined at runtime, because of supplementals and quantifiers. - int32_t cursorOffset; // only nonzero on output side - - // Position of first CURSOR_OFFSET on _right_. This will be -1 - // for |@, -2 for |@@, etc., and 1 for @|, 2 for @@|, etc. - int32_t cursorOffsetPos; - - UBool anchorStart; - UBool anchorEnd; - - /** - * The segment number from 1..n of the next '(' we see - * during parsing; 1-based. - */ - int32_t nextSegmentNumber; - - TransliteratorParser& parser; - - //-------------------------------------------------- - // Methods - - RuleHalf(TransliteratorParser& parser); - ~RuleHalf(); - - int32_t parse(const UnicodeString& rule, int32_t pos, int32_t limit, UErrorCode& status); - - int32_t parseSection(const UnicodeString& rule, int32_t pos, int32_t limit, - UnicodeString& buf, - const UnicodeString& illegal, - UBool isSegment, - UErrorCode& status); - - /** - * Remove context. - */ - void removeContext(); - - /** - * Return true if this half looks like valid output, that is, does not - * contain quantifiers or other special input-only elements. - */ - UBool isValidOutput(TransliteratorParser& parser); - - /** - * Return true if this half looks like valid input, that is, does not - * contain functions or other special output-only elements. - */ - UBool isValidInput(TransliteratorParser& parser); - - int syntaxError(UErrorCode code, - const UnicodeString& rule, - int32_t start, - UErrorCode& status) { - return parser.syntaxError(code, rule, start, status); - } - -private: - // Disallowed methods; no impl. - RuleHalf(const RuleHalf&); - RuleHalf& operator=(const RuleHalf&); -}; - -RuleHalf::RuleHalf(TransliteratorParser& p) : - parser(p) -{ - cursor = -1; - ante = -1; - post = -1; - cursorOffset = 0; - cursorOffsetPos = 0; - anchorStart = anchorEnd = false; - nextSegmentNumber = 1; -} - -RuleHalf::~RuleHalf() { -} - -/** - * Parse one side of a rule, stopping at either the limit, - * the END_OF_RULE character, or an operator. - * @return the index after the terminating character, or - * if limit was reached, limit - */ -int32_t RuleHalf::parse(const UnicodeString& rule, int32_t pos, int32_t limit, UErrorCode& status) { - int32_t start = pos; - text.truncate(0); - pos = parseSection(rule, pos, limit, text, UnicodeString(true, ILLEGAL_TOP, -1), false, status); - - if (cursorOffset > 0 && cursor != cursorOffsetPos) { - return syntaxError(U_MISPLACED_CURSOR_OFFSET, rule, start, status); - } - - return pos; -} - -/** - * Parse a section of one side of a rule, stopping at either - * the limit, the END_OF_RULE character, an operator, or a - * segment close character. This method parses both a - * top-level rule half and a segment within such a rule half. - * It calls itself recursively to parse segments and nested - * segments. - * @param buf buffer into which to accumulate the rule pattern - * characters, either literal characters from the rule or - * standins for UnicodeMatcher objects including segments. - * @param illegal the set of special characters that is illegal during - * this parse. - * @param isSegment if true, then we've already seen a '(' and - * pos on entry points right after it. Accumulate everything - * up to the closing ')', put it in a segment matcher object, - * generate a standin for it, and add the standin to buf. As - * a side effect, update the segments vector with a reference - * to the segment matcher. This works recursively for nested - * segments. If isSegment is false, just accumulate - * characters into buf. - * @return the index after the terminating character, or - * if limit was reached, limit - */ -int32_t RuleHalf::parseSection(const UnicodeString& rule, int32_t pos, int32_t limit, - UnicodeString& buf, - const UnicodeString& illegal, - UBool isSegment, UErrorCode& status) { - int32_t start = pos; - ParsePosition pp; - UnicodeString scratch; - UBool done = false; - int32_t quoteStart = -1; // Most recent 'single quoted string' - int32_t quoteLimit = -1; - int32_t varStart = -1; // Most recent $variableReference - int32_t varLimit = -1; - int32_t bufStart = buf.length(); - - while (pos < limit && !done) { - // Since all syntax characters are in the BMP, fetching - // 16-bit code units suffices here. - UChar c = rule.charAt(pos++); - if (PatternProps::isWhiteSpace(c)) { - // Ignore whitespace. Note that this is not Unicode - // spaces, but Java spaces -- a subset, representing - // whitespace likely to be seen in code. - continue; - } - if (u_strchr(HALF_ENDERS, c) != NULL) { - if (isSegment) { - // Unclosed segment - return syntaxError(U_UNCLOSED_SEGMENT, rule, start, status); - } - break; - } - if (anchorEnd) { - // Text after a presumed end anchor is a syntax err - return syntaxError(U_MALFORMED_VARIABLE_REFERENCE, rule, start, status); - } - if (UnicodeSet::resemblesPattern(rule, pos-1)) { - pp.setIndex(pos-1); // Backup to opening '[' - buf.append(parser.parseSet(rule, pp, status)); - if (U_FAILURE(status)) { - return syntaxError(U_MALFORMED_SET, rule, start, status); - } - pos = pp.getIndex(); - continue; - } - // Handle escapes - if (c == ESCAPE) { - if (pos == limit) { - return syntaxError(U_TRAILING_BACKSLASH, rule, start, status); - } - UChar32 escaped = rule.unescapeAt(pos); // pos is already past '\\' - if (escaped == (UChar32) -1) { - return syntaxError(U_MALFORMED_UNICODE_ESCAPE, rule, start, status); - } - if (!parser.checkVariableRange(escaped)) { - return syntaxError(U_VARIABLE_RANGE_OVERLAP, rule, start, status); - } - buf.append(escaped); - continue; - } - // Handle quoted matter - if (c == QUOTE) { - int32_t iq = rule.indexOf(QUOTE, pos); - if (iq == pos) { - buf.append(c); // Parse [''] outside quotes as ['] - ++pos; - } else { - /* This loop picks up a run of quoted text of the - * form 'aaaa' each time through. If this run - * hasn't really ended ('aaaa''bbbb') then it keeps - * looping, each time adding on a new run. When it - * reaches the final quote it breaks. - */ - quoteStart = buf.length(); - for (;;) { - if (iq < 0) { - return syntaxError(U_UNTERMINATED_QUOTE, rule, start, status); - } - scratch.truncate(0); - rule.extractBetween(pos, iq, scratch); - buf.append(scratch); - pos = iq+1; - if (pos < limit && rule.charAt(pos) == QUOTE) { - // Parse [''] inside quotes as ['] - iq = rule.indexOf(QUOTE, pos+1); - // Continue looping - } else { - break; - } - } - quoteLimit = buf.length(); - - for (iq=quoteStart; iq= 0) { - syntaxError(U_ILLEGAL_CHARACTER, rule, start, status); - } - - switch (c) { - - //------------------------------------------------------ - // Elements allowed within and out of segments - //------------------------------------------------------ - case ANCHOR_START: - if (buf.length() == 0 && !anchorStart) { - anchorStart = true; - } else { - return syntaxError(U_MISPLACED_ANCHOR_START, - rule, start, status); - } - break; - case SEGMENT_OPEN: - { - // bufSegStart is the offset in buf to the first - // character of the segment we are parsing. - int32_t bufSegStart = buf.length(); - - // Record segment number now, since nextSegmentNumber - // will be incremented during the call to parseSection - // if there are nested segments. - int32_t segmentNumber = nextSegmentNumber++; // 1-based - - // Parse the segment - pos = parseSection(rule, pos, limit, buf, UnicodeString(true, ILLEGAL_SEG, -1), true, status); - - // After parsing a segment, the relevant characters are - // in buf, starting at offset bufSegStart. Extract them - // into a string matcher, and replace them with a - // standin for that matcher. - StringMatcher* m = - new StringMatcher(buf, bufSegStart, buf.length(), - segmentNumber, *parser.curData); - if (m == NULL) { - return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); - } - - // Record and associate object and segment number - parser.setSegmentObject(segmentNumber, m, status); - buf.truncate(bufSegStart); - buf.append(parser.getSegmentStandin(segmentNumber, status)); - } - break; - case FUNCTION: - case ALT_FUNCTION: - { - int32_t iref = pos; - TransliteratorIDParser::SingleID* single = - TransliteratorIDParser::parseFilterID(rule, iref); - // The next character MUST be a segment open - if (single == NULL || - !ICU_Utility::parseChar(rule, iref, SEGMENT_OPEN)) { - return syntaxError(U_INVALID_FUNCTION, rule, start, status); - } - - Transliterator *t = single->createInstance(); - delete single; - if (t == NULL) { - return syntaxError(U_INVALID_FUNCTION, rule, start, status); - } - - // bufSegStart is the offset in buf to the first - // character of the segment we are parsing. - int32_t bufSegStart = buf.length(); - - // Parse the segment - pos = parseSection(rule, iref, limit, buf, UnicodeString(true, ILLEGAL_FUNC, -1), true, status); - - // After parsing a segment, the relevant characters are - // in buf, starting at offset bufSegStart. - UnicodeString output; - buf.extractBetween(bufSegStart, buf.length(), output); - FunctionReplacer *r = - new FunctionReplacer(t, new StringReplacer(output, parser.curData)); - if (r == NULL) { - return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); - } - - // Replace the buffer contents with a stand-in - buf.truncate(bufSegStart); - buf.append(parser.generateStandInFor(r, status)); - } - break; - case SymbolTable::SYMBOL_REF: - // Handle variable references and segment references "$1" .. "$9" - { - // A variable reference must be followed immediately - // by a Unicode identifier start and zero or more - // Unicode identifier part characters, or by a digit - // 1..9 if it is a segment reference. - if (pos == limit) { - // A variable ref character at the end acts as - // an anchor to the context limit, as in perl. - anchorEnd = true; - break; - } - // Parse "$1" "$2" .. "$9" .. (no upper limit) - c = rule.charAt(pos); - int32_t r = u_digit(c, 10); - if (r >= 1 && r <= 9) { - r = ICU_Utility::parseNumber(rule, pos, 10); - if (r < 0) { - return syntaxError(U_UNDEFINED_SEGMENT_REFERENCE, - rule, start, status); - } - buf.append(parser.getSegmentStandin(r, status)); - } else { - pp.setIndex(pos); - UnicodeString name = parser.parseData-> - parseReference(rule, pp, limit); - if (name.length() == 0) { - // This means the '$' was not followed by a - // valid name. Try to interpret it as an - // end anchor then. If this also doesn't work - // (if we see a following character) then signal - // an error. - anchorEnd = true; - break; - } - pos = pp.getIndex(); - // If this is a variable definition statement, - // then the LHS variable will be undefined. In - // that case appendVariableDef() will append the - // special placeholder char variableLimit-1. - varStart = buf.length(); - parser.appendVariableDef(name, buf, status); - varLimit = buf.length(); - } - } - break; - case DOT: - buf.append(parser.getDotStandIn(status)); - break; - case KLEENE_STAR: - case ONE_OR_MORE: - case ZERO_OR_ONE: - // Quantifiers. We handle single characters, quoted strings, - // variable references, and segments. - // a+ matches aaa - // 'foo'+ matches foofoofoo - // $v+ matches xyxyxy if $v == xy - // (seg)+ matches segsegseg - { - if (isSegment && buf.length() == bufStart) { - // The */+ immediately follows '(' - return syntaxError(U_MISPLACED_QUANTIFIER, rule, start, status); - } - - int32_t qstart, qlimit; - // The */+ follows an isolated character or quote - // or variable reference - if (buf.length() == quoteLimit) { - // The */+ follows a 'quoted string' - qstart = quoteStart; - qlimit = quoteLimit; - } else if (buf.length() == varLimit) { - // The */+ follows a $variableReference - qstart = varStart; - qlimit = varLimit; - } else { - // The */+ follows a single character, possibly - // a segment standin - qstart = buf.length() - 1; - qlimit = qstart + 1; - } - - UnicodeFunctor *m = - new StringMatcher(buf, qstart, qlimit, 0, *parser.curData); - if (m == NULL) { - return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); - } - int32_t min = 0; - int32_t max = Quantifier::MAX; - switch (c) { - case ONE_OR_MORE: - min = 1; - break; - case ZERO_OR_ONE: - min = 0; - max = 1; - break; - // case KLEENE_STAR: - // do nothing -- min, max already set - } - m = new Quantifier(m, min, max); - if (m == NULL) { - return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); - } - buf.truncate(qstart); - buf.append(parser.generateStandInFor(m, status)); - } - break; - - //------------------------------------------------------ - // Elements allowed ONLY WITHIN segments - //------------------------------------------------------ - case SEGMENT_CLOSE: - // assert(isSegment); - // We're done parsing a segment. - done = true; - break; - - //------------------------------------------------------ - // Elements allowed ONLY OUTSIDE segments - //------------------------------------------------------ - case CONTEXT_ANTE: - if (ante >= 0) { - return syntaxError(U_MULTIPLE_ANTE_CONTEXTS, rule, start, status); - } - ante = buf.length(); - break; - case CONTEXT_POST: - if (post >= 0) { - return syntaxError(U_MULTIPLE_POST_CONTEXTS, rule, start, status); - } - post = buf.length(); - break; - case CURSOR_POS: - if (cursor >= 0) { - return syntaxError(U_MULTIPLE_CURSORS, rule, start, status); - } - cursor = buf.length(); - break; - case CURSOR_OFFSET: - if (cursorOffset < 0) { - if (buf.length() > 0) { - return syntaxError(U_MISPLACED_CURSOR_OFFSET, rule, start, status); - } - --cursorOffset; - } else if (cursorOffset > 0) { - if (buf.length() != cursorOffsetPos || cursor >= 0) { - return syntaxError(U_MISPLACED_CURSOR_OFFSET, rule, start, status); - } - ++cursorOffset; - } else { - if (cursor == 0 && buf.length() == 0) { - cursorOffset = -1; - } else if (cursor < 0) { - cursorOffsetPos = buf.length(); - cursorOffset = 1; - } else { - return syntaxError(U_MISPLACED_CURSOR_OFFSET, rule, start, status); - } - } - break; - - - //------------------------------------------------------ - // Non-special characters - //------------------------------------------------------ - default: - // Disallow unquoted characters other than [0-9A-Za-z] - // in the printable ASCII range. These characters are - // reserved for possible future use. - if (c >= 0x0021 && c <= 0x007E && - !((c >= 0x0030/*'0'*/ && c <= 0x0039/*'9'*/) || - (c >= 0x0041/*'A'*/ && c <= 0x005A/*'Z'*/) || - (c >= 0x0061/*'a'*/ && c <= 0x007A/*'z'*/))) { - return syntaxError(U_UNQUOTED_SPECIAL, rule, start, status); - } - buf.append(c); - break; - } - } - - return pos; -} - -/** - * Remove context. - */ -void RuleHalf::removeContext() { - //text = text.substring(ante < 0 ? 0 : ante, - // post < 0 ? text.length() : post); - if (post >= 0) { - text.remove(post); - } - if (ante >= 0) { - text.removeBetween(0, ante); - } - ante = post = -1; - anchorStart = anchorEnd = false; -} - -/** - * Return true if this half looks like valid output, that is, does not - * contain quantifiers or other special input-only elements. - */ -UBool RuleHalf::isValidOutput(TransliteratorParser& transParser) { - for (int32_t i=0; iisReplacer(c)) { - return false; - } - } - return true; -} - -/** - * Return true if this half looks like valid input, that is, does not - * contain functions or other special output-only elements. - */ -UBool RuleHalf::isValidInput(TransliteratorParser& transParser) { - for (int32_t i=0; iisMatcher(c)) { - return false; - } - } - return true; -} - -//---------------------------------------------------------------------- -// PUBLIC API -//---------------------------------------------------------------------- - -/** - * Constructor. - */ -TransliteratorParser::TransliteratorParser(UErrorCode &statusReturn) : -dataVector(statusReturn), -idBlockVector(statusReturn), -variablesVector(statusReturn), -segmentObjects(statusReturn) -{ - idBlockVector.setDeleter(uprv_deleteUObject); - curData = NULL; - compoundFilter = NULL; - parseData = NULL; - variableNames.setValueDeleter(uprv_deleteUObject); -} - -/** - * Destructor. - */ -TransliteratorParser::~TransliteratorParser() { - while (!dataVector.isEmpty()) - delete (TransliterationRuleData*)(dataVector.orphanElementAt(0)); - delete compoundFilter; - delete parseData; - while (!variablesVector.isEmpty()) - delete (UnicodeFunctor*)variablesVector.orphanElementAt(0); -} - -void -TransliteratorParser::parse(const UnicodeString& rules, - UTransDirection transDirection, - UParseError& pe, - UErrorCode& ec) { - if (U_SUCCESS(ec)) { - parseRules(rules, transDirection, ec); - pe = parseError; - } -} - -/** - * Return the compound filter parsed by parse(). Caller owns result. - */ -UnicodeSet* TransliteratorParser::orphanCompoundFilter() { - UnicodeSet* f = compoundFilter; - compoundFilter = NULL; - return f; -} - -//---------------------------------------------------------------------- -// Private implementation -//---------------------------------------------------------------------- - -/** - * Parse the given string as a sequence of rules, separated by newline - * characters ('\n'), and cause this object to implement those rules. Any - * previous rules are discarded. Typically this method is called exactly - * once, during construction. - * @exception IllegalArgumentException if there is a syntax error in the - * rules - */ -void TransliteratorParser::parseRules(const UnicodeString& rule, - UTransDirection theDirection, - UErrorCode& status) -{ - // Clear error struct - uprv_memset(&parseError, 0, sizeof(parseError)); - parseError.line = parseError.offset = -1; - - UBool parsingIDs = true; - int32_t ruleCount = 0; - - while (!dataVector.isEmpty()) { - delete (TransliterationRuleData*)(dataVector.orphanElementAt(0)); - } - if (U_FAILURE(status)) { - return; - } - - idBlockVector.removeAllElements(); - curData = NULL; - direction = theDirection; - ruleCount = 0; - - delete compoundFilter; - compoundFilter = NULL; - - while (!variablesVector.isEmpty()) { - delete (UnicodeFunctor*)variablesVector.orphanElementAt(0); - } - variableNames.removeAll(); - parseData = new ParseData(0, &variablesVector, &variableNames); - if (parseData == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - dotStandIn = (UChar) -1; - - UnicodeString *tempstr = NULL; // used for memory allocation error checking - UnicodeString str; // scratch - UnicodeString idBlockResult; - int32_t pos = 0; - int32_t limit = rule.length(); - - // The compound filter offset is an index into idBlockResult. - // If it is 0, then the compound filter occurred at the start, - // and it is the offset to the _start_ of the compound filter - // pattern. Otherwise it is the offset to the _limit_ of the - // compound filter pattern within idBlockResult. - compoundFilter = NULL; - int32_t compoundFilterOffset = -1; - - while (pos < limit && U_SUCCESS(status)) { - UChar c = rule.charAt(pos++); - if (PatternProps::isWhiteSpace(c)) { - // Ignore leading whitespace. - continue; - } - // Skip lines starting with the comment character - if (c == RULE_COMMENT_CHAR) { - pos = rule.indexOf((UChar)0x000A /*\n*/, pos) + 1; - if (pos == 0) { - break; // No "\n" found; rest of rule is a comment - } - continue; // Either fall out or restart with next line - } - - // skip empty rules - if (c == END_OF_RULE) - continue; - - // keep track of how many rules we've seen - ++ruleCount; - - // We've found the start of a rule or ID. c is its first - // character, and pos points past c. - --pos; - // Look for an ID token. Must have at least ID_TOKEN_LEN + 1 - // chars left. - if ((pos + ID_TOKEN_LEN + 1) <= limit && - rule.compare(pos, ID_TOKEN_LEN, ID_TOKEN) == 0) { - pos += ID_TOKEN_LEN; - c = rule.charAt(pos); - while (PatternProps::isWhiteSpace(c) && pos < limit) { - ++pos; - c = rule.charAt(pos); - } - - int32_t p = pos; - - if (!parsingIDs) { - if (curData != NULL) { - U_ASSERT(!dataVector.hasDeleter()); - if (direction == UTRANS_FORWARD) - dataVector.addElement(curData, status); - else - dataVector.insertElementAt(curData, 0, status); - if (U_FAILURE(status)) { - delete curData; - } - curData = NULL; - } - parsingIDs = true; - } - - TransliteratorIDParser::SingleID* id = - TransliteratorIDParser::parseSingleID(rule, p, direction, status); - if (p != pos && ICU_Utility::parseChar(rule, p, END_OF_RULE)) { - // Successful ::ID parse. - - if (direction == UTRANS_FORWARD) { - idBlockResult.append(id->canonID).append(END_OF_RULE); - } else { - idBlockResult.insert(0, END_OF_RULE); - idBlockResult.insert(0, id->canonID); - } - - } else { - // Couldn't parse an ID. Try to parse a global filter - int32_t withParens = -1; - UnicodeSet* f = TransliteratorIDParser::parseGlobalFilter(rule, p, direction, withParens, NULL); - if (f != NULL) { - if (ICU_Utility::parseChar(rule, p, END_OF_RULE) - && (direction == UTRANS_FORWARD) == (withParens == 0)) - { - if (compoundFilter != NULL) { - // Multiple compound filters - syntaxError(U_MULTIPLE_COMPOUND_FILTERS, rule, pos, status); - delete f; - } else { - compoundFilter = f; - compoundFilterOffset = ruleCount; - } - } else { - delete f; - } - } else { - // Invalid ::id - // Can be parsed as neither an ID nor a global filter - syntaxError(U_INVALID_ID, rule, pos, status); - } - } - delete id; - pos = p; - } else { - if (parsingIDs) { - tempstr = new UnicodeString(idBlockResult); - // NULL pointer check - if (tempstr == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - U_ASSERT(idBlockVector.hasDeleter()); - if (direction == UTRANS_FORWARD) - idBlockVector.adoptElement(tempstr, status); - else - idBlockVector.insertElementAt(tempstr, 0, status); - if (U_FAILURE(status)) { - return; - } - idBlockResult.remove(); - parsingIDs = false; - curData = new TransliterationRuleData(status); - // NULL pointer check - if (curData == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - parseData->data = curData; - - // By default, rules use part of the private use area - // E000..F8FF for variables and other stand-ins. Currently - // the range F000..F8FF is typically sufficient. The 'use - // variable range' pragma allows rule sets to modify this. - setVariableRange(0xF000, 0xF8FF, status); - } - - if (resemblesPragma(rule, pos, limit)) { - int32_t ppp = parsePragma(rule, pos, limit, status); - if (ppp < 0) { - syntaxError(U_MALFORMED_PRAGMA, rule, pos, status); - } - pos = ppp; - // Parse a rule - } else { - pos = parseRule(rule, pos, limit, status); - } - } - } - - if (parsingIDs && idBlockResult.length() > 0) { - tempstr = new UnicodeString(idBlockResult); - // NULL pointer check - if (tempstr == NULL) { - // TODO: Testing, forcing this path, shows many memory leaks. ICU-21701 - // intltest translit/TransliteratorTest/TestInstantiation - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (direction == UTRANS_FORWARD) - idBlockVector.adoptElement(tempstr, status); - else - idBlockVector.insertElementAt(tempstr, 0, status); - if (U_FAILURE(status)) { - return; - } - } - else if (!parsingIDs && curData != NULL) { - if (direction == UTRANS_FORWARD) { - dataVector.addElement(curData, status); - } else { - dataVector.insertElementAt(curData, 0, status); - } - if (U_FAILURE(status)) { - delete curData; - curData = nullptr; - } - } - - if (U_SUCCESS(status)) { - // Convert the set vector to an array - int32_t i, dataVectorSize = dataVector.size(); - for (i = 0; i < dataVectorSize; i++) { - TransliterationRuleData* data = (TransliterationRuleData*)dataVector.elementAt(i); - data->variablesLength = variablesVector.size(); - if (data->variablesLength == 0) { - data->variables = 0; - } else { - data->variables = (UnicodeFunctor**)uprv_malloc(data->variablesLength * sizeof(UnicodeFunctor*)); - // NULL pointer check - if (data->variables == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - data->variablesAreOwned = (i == 0); - } - - for (int32_t j = 0; j < data->variablesLength; j++) { - data->variables[j] = - static_cast(variablesVector.elementAt(j)); - } - - data->variableNames.removeAll(); - int32_t p = UHASH_FIRST; - const UHashElement* he = variableNames.nextElement(p); - while (he != NULL) { - UnicodeString* tempus = ((UnicodeString*)(he->value.pointer))->clone(); - if (tempus == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - data->variableNames.put(*((UnicodeString*)(he->key.pointer)), - tempus, status); - he = variableNames.nextElement(p); - } - } - variablesVector.removeAllElements(); // keeps them from getting deleted when we succeed - - // Index the rules - if (compoundFilter != NULL) { - if ((direction == UTRANS_FORWARD && compoundFilterOffset != 1) || - (direction == UTRANS_REVERSE && compoundFilterOffset != ruleCount)) { - status = U_MISPLACED_COMPOUND_FILTER; - } - } - - for (i = 0; i < dataVectorSize; i++) { - TransliterationRuleData* data = (TransliterationRuleData*)dataVector.elementAt(i); - data->ruleSet.freeze(parseError, status); - } - if (idBlockVector.size() == 1 && ((UnicodeString*)idBlockVector.elementAt(0))->isEmpty()) { - idBlockVector.removeElementAt(0); - } - } -} - -/** - * Set the variable range to [start, end] (inclusive). - */ -void TransliteratorParser::setVariableRange(int32_t start, int32_t end, UErrorCode& status) { - if (start > end || start < 0 || end > 0xFFFF) { - status = U_MALFORMED_PRAGMA; - return; - } - - curData->variablesBase = (UChar) start; - if (dataVector.size() == 0) { - variableNext = (UChar) start; - variableLimit = (UChar) (end + 1); - } -} - -/** - * Assert that the given character is NOT within the variable range. - * If it is, return false. This is necessary to ensure that the - * variable range does not overlap characters used in a rule. - */ -UBool TransliteratorParser::checkVariableRange(UChar32 ch) const { - return !(ch >= curData->variablesBase && ch < variableLimit); -} - -/** - * Set the maximum backup to 'backup', in response to a pragma - * statement. - */ -void TransliteratorParser::pragmaMaximumBackup(int32_t /*backup*/) { - //TODO Finish -} - -/** - * Begin normalizing all rules using the given mode, in response - * to a pragma statement. - */ -void TransliteratorParser::pragmaNormalizeRules(UNormalizationMode /*mode*/) { - //TODO Finish -} - -static const UChar PRAGMA_USE[] = {0x75,0x73,0x65,0x20,0}; // "use " - -static const UChar PRAGMA_VARIABLE_RANGE[] = {0x7E,0x76,0x61,0x72,0x69,0x61,0x62,0x6C,0x65,0x20,0x72,0x61,0x6E,0x67,0x65,0x20,0x23,0x20,0x23,0x7E,0x3B,0}; // "~variable range # #~;" - -static const UChar PRAGMA_MAXIMUM_BACKUP[] = {0x7E,0x6D,0x61,0x78,0x69,0x6D,0x75,0x6D,0x20,0x62,0x61,0x63,0x6B,0x75,0x70,0x20,0x23,0x7E,0x3B,0}; // "~maximum backup #~;" - -static const UChar PRAGMA_NFD_RULES[] = {0x7E,0x6E,0x66,0x64,0x20,0x72,0x75,0x6C,0x65,0x73,0x7E,0x3B,0}; // "~nfd rules~;" - -static const UChar PRAGMA_NFC_RULES[] = {0x7E,0x6E,0x66,0x63,0x20,0x72,0x75,0x6C,0x65,0x73,0x7E,0x3B,0}; // "~nfc rules~;" - -/** - * Return true if the given rule looks like a pragma. - * @param pos offset to the first non-whitespace character - * of the rule. - * @param limit pointer past the last character of the rule. - */ -UBool TransliteratorParser::resemblesPragma(const UnicodeString& rule, int32_t pos, int32_t limit) { - // Must start with /use\s/i - return ICU_Utility::parsePattern(rule, pos, limit, UnicodeString(true, PRAGMA_USE, 4), NULL) >= 0; -} - -/** - * Parse a pragma. This method assumes resemblesPragma() has - * already returned true. - * @param pos offset to the first non-whitespace character - * of the rule. - * @param limit pointer past the last character of the rule. - * @return the position index after the final ';' of the pragma, - * or -1 on failure. - */ -int32_t TransliteratorParser::parsePragma(const UnicodeString& rule, int32_t pos, int32_t limit, UErrorCode& status) { - int32_t array[2]; - - // resemblesPragma() has already returned true, so we - // know that pos points to /use\s/i; we can skip 4 characters - // immediately - pos += 4; - - // Here are the pragmas we recognize: - // use variable range 0xE000 0xEFFF; - // use maximum backup 16; - // use nfd rules; - // use nfc rules; - int p = ICU_Utility::parsePattern(rule, pos, limit, UnicodeString(true, PRAGMA_VARIABLE_RANGE, -1), array); - if (p >= 0) { - setVariableRange(array[0], array[1], status); - return p; - } - - p = ICU_Utility::parsePattern(rule, pos, limit, UnicodeString(true, PRAGMA_MAXIMUM_BACKUP, -1), array); - if (p >= 0) { - pragmaMaximumBackup(array[0]); - return p; - } - - p = ICU_Utility::parsePattern(rule, pos, limit, UnicodeString(true, PRAGMA_NFD_RULES, -1), NULL); - if (p >= 0) { - pragmaNormalizeRules(UNORM_NFD); - return p; - } - - p = ICU_Utility::parsePattern(rule, pos, limit, UnicodeString(true, PRAGMA_NFC_RULES, -1), NULL); - if (p >= 0) { - pragmaNormalizeRules(UNORM_NFC); - return p; - } - - // Syntax error: unable to parse pragma - return -1; -} - -/** - * MAIN PARSER. Parse the next rule in the given rule string, starting - * at pos. Return the index after the last character parsed. Do not - * parse characters at or after limit. - * - * Important: The character at pos must be a non-whitespace character - * that is not the comment character. - * - * This method handles quoting, escaping, and whitespace removal. It - * parses the end-of-rule character. It recognizes context and cursor - * indicators. Once it does a lexical breakdown of the rule at pos, it - * creates a rule object and adds it to our rule list. - */ -int32_t TransliteratorParser::parseRule(const UnicodeString& rule, int32_t pos, int32_t limit, UErrorCode& status) { - // Locate the left side, operator, and right side - int32_t start = pos; - UChar op = 0; - int32_t i; - - // Set up segments data - segmentStandins.truncate(0); - segmentObjects.removeAllElements(); - - // Use pointers to automatics to make swapping possible. - RuleHalf _left(*this), _right(*this); - RuleHalf* left = &_left; - RuleHalf* right = &_right; - - undefinedVariableName.remove(); - pos = left->parse(rule, pos, limit, status); - if (U_FAILURE(status)) { - return start; - } - - if (pos == limit || u_strchr(gOPERATORS, (op = rule.charAt(--pos))) == NULL) { - return syntaxError(U_MISSING_OPERATOR, rule, start, status); - } - ++pos; - - // Found an operator char. Check for forward-reverse operator. - if (op == REVERSE_RULE_OP && - (pos < limit && rule.charAt(pos) == FORWARD_RULE_OP)) { - ++pos; - op = FWDREV_RULE_OP; - } - - // Translate alternate op characters. - switch (op) { - case ALT_FORWARD_RULE_OP: - op = FORWARD_RULE_OP; - break; - case ALT_REVERSE_RULE_OP: - op = REVERSE_RULE_OP; - break; - case ALT_FWDREV_RULE_OP: - op = FWDREV_RULE_OP; - break; - } - - pos = right->parse(rule, pos, limit, status); - if (U_FAILURE(status)) { - return start; - } - - if (pos < limit) { - if (rule.charAt(--pos) == END_OF_RULE) { - ++pos; - } else { - // RuleHalf parser must have terminated at an operator - return syntaxError(U_UNQUOTED_SPECIAL, rule, start, status); - } - } - - if (op == VARIABLE_DEF_OP) { - // LHS is the name. RHS is a single character, either a literal - // or a set (already parsed). If RHS is longer than one - // character, it is either a multi-character string, or multiple - // sets, or a mixture of chars and sets -- syntax error. - - // We expect to see a single undefined variable (the one being - // defined). - if (undefinedVariableName.length() == 0) { - // "Missing '$' or duplicate definition" - return syntaxError(U_BAD_VARIABLE_DEFINITION, rule, start, status); - } - if (left->text.length() != 1 || left->text.charAt(0) != variableLimit) { - // "Malformed LHS" - return syntaxError(U_MALFORMED_VARIABLE_DEFINITION, rule, start, status); - } - if (left->anchorStart || left->anchorEnd || - right->anchorStart || right->anchorEnd) { - return syntaxError(U_MALFORMED_VARIABLE_DEFINITION, rule, start, status); - } - // We allow anything on the right, including an empty string. - UnicodeString* value = new UnicodeString(right->text); - // NULL pointer check - if (value == NULL) { - return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); - } - variableNames.put(undefinedVariableName, value, status); - ++variableLimit; - return pos; - } - - // If this is not a variable definition rule, we shouldn't have - // any undefined variable names. - if (undefinedVariableName.length() != 0) { - return syntaxError(// "Undefined variable $" + undefinedVariableName, - U_UNDEFINED_VARIABLE, - rule, start, status); - } - - // Verify segments - if (segmentStandins.length() > segmentObjects.size()) { - syntaxError(U_UNDEFINED_SEGMENT_REFERENCE, rule, start, status); - } - for (i=0; iremoveContext(); - left->cursor = -1; - left->cursorOffset = 0; - } - - // Normalize context - if (left->ante < 0) { - left->ante = 0; - } - if (left->post < 0) { - left->post = left->text.length(); - } - - // Context is only allowed on the input side. Cursors are only - // allowed on the output side. Segment delimiters can only appear - // on the left, and references on the right. Cursor offset - // cannot appear without an explicit cursor. Cursor offset - // cannot place the cursor outside the limits of the context. - // Anchors are only allowed on the input side. - if (right->ante >= 0 || right->post >= 0 || left->cursor >= 0 || - (right->cursorOffset != 0 && right->cursor < 0) || - // - The following two checks were used to ensure that the - // - the cursor offset stayed within the ante- or postcontext. - // - However, with the addition of quantifiers, we have to - // - allow arbitrary cursor offsets and do runtime checking. - //(right->cursorOffset > (left->text.length() - left->post)) || - //(-right->cursorOffset > left->ante) || - right->anchorStart || right->anchorEnd || - !left->isValidInput(*this) || !right->isValidOutput(*this) || - left->ante > left->post) { - - return syntaxError(U_MALFORMED_RULE, rule, start, status); - } - - // Flatten segment objects vector to an array - UnicodeFunctor** segmentsArray = NULL; - if (segmentObjects.size() > 0) { - segmentsArray = (UnicodeFunctor **)uprv_malloc(segmentObjects.size() * sizeof(UnicodeFunctor *)); - // Null pointer check - if (segmentsArray == NULL) { - return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); - } - segmentObjects.toArray((void**) segmentsArray); - } - TransliterationRule* temptr = new TransliterationRule( - left->text, left->ante, left->post, - right->text, right->cursor, right->cursorOffset, - segmentsArray, - segmentObjects.size(), - left->anchorStart, left->anchorEnd, - curData, - status); - //Null pointer check - if (temptr == NULL) { - uprv_free(segmentsArray); - return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); - } - - curData->ruleSet.addRule(temptr, status); - - return pos; -} - -/** - * Called by main parser upon syntax error. Search the rule string - * for the probable end of the rule. Of course, if the error is that - * the end of rule marker is missing, then the rule end will not be found. - * In any case the rule start will be correctly reported. - * @param msg error description - * @param rule pattern string - * @param start position of first character of current rule - */ -int32_t TransliteratorParser::syntaxError(UErrorCode parseErrorCode, - const UnicodeString& rule, - int32_t pos, - UErrorCode& status) -{ - parseError.offset = pos; - parseError.line = 0 ; /* we are not using line numbers */ - - // for pre-context - const int32_t LEN = U_PARSE_CONTEXT_LEN - 1; - int32_t start = uprv_max(pos - LEN, 0); - int32_t stop = pos; - - rule.extract(start,stop-start,parseError.preContext); - //null terminate the buffer - parseError.preContext[stop-start] = 0; - - //for post-context - start = pos; - stop = uprv_min(pos + LEN, rule.length()); - - rule.extract(start,stop-start,parseError.postContext); - //null terminate the buffer - parseError.postContext[stop-start]= 0; - - status = (UErrorCode)parseErrorCode; - return pos; - -} - -/** - * Parse a UnicodeSet out, store it, and return the stand-in character - * used to represent it. - */ -UChar TransliteratorParser::parseSet(const UnicodeString& rule, - ParsePosition& pos, - UErrorCode& status) { - UnicodeSet* set = new UnicodeSet(rule, pos, USET_IGNORE_SPACE, parseData, status); - // Null pointer check - if (set == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return (UChar)0x0000; // Return empty character with error. - } - set->compact(); - return generateStandInFor(set, status); -} - -/** - * Generate and return a stand-in for a new UnicodeFunctor. Store - * the matcher (adopt it). - */ -UChar TransliteratorParser::generateStandInFor(UnicodeFunctor* adopted, UErrorCode& status) { - // assert(obj != null); - - // Look up previous stand-in, if any. This is a short list - // (typical n is 0, 1, or 2); linear search is optimal. - for (int32_t i=0; ivariablesBase + i); - } - } - - if (variableNext >= variableLimit) { - delete adopted; - status = U_VARIABLE_RANGE_EXHAUSTED; - return 0; - } - variablesVector.addElement(adopted, status); - if (U_FAILURE(status)) { - delete adopted; - return 0; - } - return variableNext++; -} - -/** - * Return the standin for segment seg (1-based). - */ -UChar TransliteratorParser::getSegmentStandin(int32_t seg, UErrorCode& status) { - // Special character used to indicate an empty spot - UChar empty = curData->variablesBase - 1; - while (segmentStandins.length() < seg) { - segmentStandins.append(empty); - } - UChar c = segmentStandins.charAt(seg-1); - if (c == empty) { - if (variableNext >= variableLimit) { - status = U_VARIABLE_RANGE_EXHAUSTED; - return 0; - } - c = variableNext++; - // Set a placeholder in the primary variables vector that will be - // filled in later by setSegmentObject(). We know that we will get - // called first because setSegmentObject() will call us. - variablesVector.addElement((void*) NULL, status); - segmentStandins.setCharAt(seg-1, c); - } - return c; -} - -/** - * Set the object for segment seg (1-based). - */ -void TransliteratorParser::setSegmentObject(int32_t seg, StringMatcher* adopted, UErrorCode& status) { - // Since we call parseSection() recursively, nested - // segments will result in segment i+1 getting parsed - // and stored before segment i; be careful with the - // vector handling here. - if (segmentObjects.size() < seg) { - segmentObjects.setSize(seg, status); - } - if (U_FAILURE(status)) { - return; - } - int32_t index = getSegmentStandin(seg, status) - curData->variablesBase; - if (segmentObjects.elementAt(seg-1) != NULL || - variablesVector.elementAt(index) != NULL) { - // should never happen - if (U_SUCCESS(status)) {status = U_INTERNAL_TRANSLITERATOR_ERROR;} - return; - } - // Note: neither segmentObjects or variablesVector has an object deleter function. - segmentObjects.setElementAt(adopted, seg-1); - variablesVector.setElementAt(adopted, index); -} - -/** - * Return the stand-in for the dot set. It is allocated the first - * time and reused thereafter. - */ -UChar TransliteratorParser::getDotStandIn(UErrorCode& status) { - if (dotStandIn == (UChar) -1) { - UnicodeSet* tempus = new UnicodeSet(UnicodeString(true, DOT_SET, -1), status); - // Null pointer check. - if (tempus == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return (UChar)0x0000; - } - dotStandIn = generateStandInFor(tempus, status); - } - return dotStandIn; -} - -/** - * Append the value of the given variable name to the given - * UnicodeString. - */ -void TransliteratorParser::appendVariableDef(const UnicodeString& name, - UnicodeString& buf, - UErrorCode& status) { - const UnicodeString* s = (const UnicodeString*) variableNames.get(name); - if (s == NULL) { - // We allow one undefined variable so that variable definition - // statements work. For the first undefined variable we return - // the special placeholder variableLimit-1, and save the variable - // name. - if (undefinedVariableName.length() == 0) { - undefinedVariableName = name; - if (variableNext >= variableLimit) { - // throw new RuntimeException("Private use variables exhausted"); - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - buf.append((UChar) --variableLimit); - } else { - //throw new IllegalArgumentException("Undefined variable $" - // + name); - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - } else { - buf.append(*s); - } -} - -/** - * Glue method to get around access restrictions in C++. - */ -/*Transliterator* TransliteratorParser::createBasicInstance(const UnicodeString& id, const UnicodeString* canonID) { - return Transliterator::createBasicInstance(id, canonID); -}*/ - -U_NAMESPACE_END - -U_CAPI int32_t -utrans_stripRules(const UChar *source, int32_t sourceLen, UChar *target, UErrorCode *status) { - U_NAMESPACE_USE - - //const UChar *sourceStart = source; - const UChar *targetStart = target; - const UChar *sourceLimit = source+sourceLen; - UChar *targetLimit = target+sourceLen; - UChar32 c = 0; - UBool quoted = false; - int32_t index; - - uprv_memset(target, 0, sourceLen*U_SIZEOF_UCHAR); - - /* read the rules into the buffer */ - while (source < sourceLimit) - { - index=0; - U16_NEXT_UNSAFE(source, index, c); - source+=index; - if(c == QUOTE) { - quoted = (UBool)!quoted; - } - else if (!quoted) { - if (c == RULE_COMMENT_CHAR) { - /* skip comments and all preceding spaces */ - while (targetStart < target && *(target - 1) == 0x0020) { - target--; - } - do { - if (source == sourceLimit) { - c = U_SENTINEL; - break; - } - c = *(source++); - } - while (c != CR && c != LF); - if (c < 0) { - break; - } - } - else if (c == ESCAPE && source < sourceLimit) { - UChar32 c2 = *source; - if (c2 == CR || c2 == LF) { - /* A backslash at the end of a line. */ - /* Since we're stripping lines, ignore the backslash. */ - source++; - continue; - } - if (c2 == 0x0075 && source+5 < sourceLimit) { /* \u seen. \U isn't unescaped. */ - int32_t escapeOffset = 0; - UnicodeString escapedStr(source, 5); - c2 = escapedStr.unescapeAt(escapeOffset); - - if (c2 == (UChar32)0xFFFFFFFF || escapeOffset == 0) - { - *status = U_PARSE_ERROR; - return 0; - } - if (!PatternProps::isWhiteSpace(c2) && !u_iscntrl(c2) && !u_ispunct(c2)) { - /* It was escaped for a reason. Write what it was suppose to be. */ - source+=5; - c = c2; - } - } - else if (c2 == QUOTE) { - /* \' seen. Make sure we don't do anything when we see it again. */ - quoted = (UBool)!quoted; - } - } - } - if (c == CR || c == LF) - { - /* ignore spaces carriage returns, and all leading spaces on the next line. - * and line feed unless in the form \uXXXX - */ - quoted = false; - while (source < sourceLimit) { - c = *(source); - if (c != CR && c != LF && c != 0x0020) { - break; - } - source++; - } - continue; - } - - /* Append UChar * after dissembling if c > 0xffff*/ - index=0; - U16_APPEND_UNSAFE(target, index, c); - target+=index; - } - if (target < targetLimit) { - *target = 0; - } - return (int32_t)(target-targetStart); -} - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 1999-2016, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + * Date Name Description + * 11/17/99 aliu Creation. + ********************************************************************** + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/uobject.h" +#include "unicode/parseerr.h" +#include "unicode/parsepos.h" +#include "unicode/putil.h" +#include "unicode/uchar.h" +#include "unicode/ustring.h" +#include "unicode/uniset.h" +#include "unicode/utf16.h" +#include "cstring.h" +#include "funcrepl.h" +#include "hash.h" +#include "quant.h" +#include "rbt.h" +#include "rbt_data.h" +#include "rbt_pars.h" +#include "rbt_rule.h" +#include "strmatch.h" +#include "strrepl.h" +#include "unicode/symtable.h" +#include "tridpars.h" +#include "uvector.h" +#include "hash.h" +#include "patternprops.h" +#include "util.h" +#include "cmemory.h" +#include "uprops.h" +#include "putilimp.h" + +// Operators +#define VARIABLE_DEF_OP ((char16_t)0x003D) /*=*/ +#define FORWARD_RULE_OP ((char16_t)0x003E) /*>*/ +#define REVERSE_RULE_OP ((char16_t)0x003C) /*<*/ +#define FWDREV_RULE_OP ((char16_t)0x007E) /*~*/ // internal rep of <> op + +// Other special characters +#define QUOTE ((char16_t)0x0027) /*'*/ +#define ESCAPE ((char16_t)0x005C) /*\*/ +#define END_OF_RULE ((char16_t)0x003B) /*;*/ +#define RULE_COMMENT_CHAR ((char16_t)0x0023) /*#*/ + +#define SEGMENT_OPEN ((char16_t)0x0028) /*(*/ +#define SEGMENT_CLOSE ((char16_t)0x0029) /*)*/ +#define CONTEXT_ANTE ((char16_t)0x007B) /*{*/ +#define CONTEXT_POST ((char16_t)0x007D) /*}*/ +#define CURSOR_POS ((char16_t)0x007C) /*|*/ +#define CURSOR_OFFSET ((char16_t)0x0040) /*@*/ +#define ANCHOR_START ((char16_t)0x005E) /*^*/ +#define KLEENE_STAR ((char16_t)0x002A) /***/ +#define ONE_OR_MORE ((char16_t)0x002B) /*+*/ +#define ZERO_OR_ONE ((char16_t)0x003F) /*?*/ + +#define DOT ((char16_t)46) /*.*/ + +static const char16_t DOT_SET[] = { // "[^[:Zp:][:Zl:]\r\n$]"; + 91, 94, 91, 58, 90, 112, 58, 93, 91, 58, 90, + 108, 58, 93, 92, 114, 92, 110, 36, 93, 0 +}; + +// A function is denoted &Source-Target/Variant(text) +#define FUNCTION ((char16_t)38) /*&*/ + +// Aliases for some of the syntax characters. These are provided so +// transliteration rules can be expressed in XML without clashing with +// XML syntax characters '<', '>', and '&'. +#define ALT_REVERSE_RULE_OP ((char16_t)0x2190) // Left Arrow +#define ALT_FORWARD_RULE_OP ((char16_t)0x2192) // Right Arrow +#define ALT_FWDREV_RULE_OP ((char16_t)0x2194) // Left Right Arrow +#define ALT_FUNCTION ((char16_t)0x2206) // Increment (~Greek Capital Delta) + +// Special characters disallowed at the top level +static const char16_t ILLEGAL_TOP[] = {41,0}; // ")" + +// Special characters disallowed within a segment +static const char16_t ILLEGAL_SEG[] = {123,125,124,64,0}; // "{}|@" + +// Special characters disallowed within a function argument +static const char16_t ILLEGAL_FUNC[] = {94,40,46,42,43,63,123,125,124,64,0}; // "^(.*+?{}|@" + +// By definition, the ANCHOR_END special character is a +// trailing SymbolTable.SYMBOL_REF character. +// private static final char ANCHOR_END = '$'; + +static const char16_t gOPERATORS[] = { // "=><" + VARIABLE_DEF_OP, FORWARD_RULE_OP, REVERSE_RULE_OP, + ALT_FORWARD_RULE_OP, ALT_REVERSE_RULE_OP, ALT_FWDREV_RULE_OP, + 0 +}; + +static const char16_t HALF_ENDERS[] = { // "=><;" + VARIABLE_DEF_OP, FORWARD_RULE_OP, REVERSE_RULE_OP, + ALT_FORWARD_RULE_OP, ALT_REVERSE_RULE_OP, ALT_FWDREV_RULE_OP, + END_OF_RULE, + 0 +}; + +// These are also used in Transliterator::toRules() +static const int32_t ID_TOKEN_LEN = 2; +static const char16_t ID_TOKEN[] = { 0x3A, 0x3A }; // ':', ':' + +/* +commented out until we do real ::BEGIN/::END functionality +static const int32_t BEGIN_TOKEN_LEN = 5; +static const char16_t BEGIN_TOKEN[] = { 0x42, 0x45, 0x47, 0x49, 0x4e }; // 'BEGIN' + +static const int32_t END_TOKEN_LEN = 3; +static const char16_t END_TOKEN[] = { 0x45, 0x4e, 0x44 }; // 'END' +*/ + +U_NAMESPACE_BEGIN + +//---------------------------------------------------------------------- +// BEGIN ParseData +//---------------------------------------------------------------------- + +/** + * This class implements the SymbolTable interface. It is used + * during parsing to give UnicodeSet access to variables that + * have been defined so far. Note that it uses variablesVector, + * _not_ data.setVariables. + */ +class ParseData : public UMemory, public SymbolTable { +public: + const TransliterationRuleData* data; // alias + + const UVector* variablesVector; // alias + + const Hashtable* variableNames; // alias + + ParseData(const TransliterationRuleData* data = 0, + const UVector* variablesVector = 0, + const Hashtable* variableNames = 0); + + virtual ~ParseData(); + + virtual const UnicodeString* lookup(const UnicodeString& s) const override; + + virtual const UnicodeFunctor* lookupMatcher(UChar32 ch) const override; + + virtual UnicodeString parseReference(const UnicodeString& text, + ParsePosition& pos, int32_t limit) const override; + /** + * Return true if the given character is a matcher standin or a plain + * character (non standin). + */ + UBool isMatcher(UChar32 ch); + + /** + * Return true if the given character is a replacer standin or a plain + * character (non standin). + */ + UBool isReplacer(UChar32 ch); + +private: + ParseData(const ParseData &other); // forbid copying of this class + ParseData &operator=(const ParseData &other); // forbid copying of this class +}; + +ParseData::ParseData(const TransliterationRuleData* d, + const UVector* sets, + const Hashtable* vNames) : + data(d), variablesVector(sets), variableNames(vNames) {} + +ParseData::~ParseData() {} + +/** + * Implement SymbolTable API. + */ +const UnicodeString* ParseData::lookup(const UnicodeString& name) const { + return (const UnicodeString*) variableNames->get(name); +} + +/** + * Implement SymbolTable API. + */ +const UnicodeFunctor* ParseData::lookupMatcher(UChar32 ch) const { + // Note that we cannot use data.lookupSet() because the + // set array has not been constructed yet. + const UnicodeFunctor* set = nullptr; + int32_t i = ch - data->variablesBase; + if (i >= 0 && i < variablesVector->size()) { + int32_t j = ch - data->variablesBase; + set = (j < variablesVector->size()) ? + (UnicodeFunctor*) variablesVector->elementAt(j) : 0; + } + return set; +} + +/** + * Implement SymbolTable API. Parse out a symbol reference + * name. + */ +UnicodeString ParseData::parseReference(const UnicodeString& text, + ParsePosition& pos, int32_t limit) const { + int32_t start = pos.getIndex(); + int32_t i = start; + UnicodeString result; + while (i < limit) { + char16_t c = text.charAt(i); + if ((i==start && !u_isIDStart(c)) || !u_isIDPart(c)) { + break; + } + ++i; + } + if (i == start) { // No valid name chars + return result; // Indicate failure with empty string + } + pos.setIndex(i); + text.extractBetween(start, i, result); + return result; +} + +UBool ParseData::isMatcher(UChar32 ch) { + // Note that we cannot use data.lookup() because the + // set array has not been constructed yet. + int32_t i = ch - data->variablesBase; + if (i >= 0 && i < variablesVector->size()) { + UnicodeFunctor *f = (UnicodeFunctor*) variablesVector->elementAt(i); + return f != nullptr && f->toMatcher() != nullptr; + } + return true; +} + +/** + * Return true if the given character is a replacer standin or a plain + * character (non standin). + */ +UBool ParseData::isReplacer(UChar32 ch) { + // Note that we cannot use data.lookup() because the + // set array has not been constructed yet. + int i = ch - data->variablesBase; + if (i >= 0 && i < variablesVector->size()) { + UnicodeFunctor *f = (UnicodeFunctor*) variablesVector->elementAt(i); + return f != nullptr && f->toReplacer() != nullptr; + } + return true; +} + +//---------------------------------------------------------------------- +// BEGIN RuleHalf +//---------------------------------------------------------------------- + +/** + * A class representing one side of a rule. This class knows how to + * parse half of a rule. It is tightly coupled to the method + * RuleBasedTransliterator.Parser.parseRule(). + */ +class RuleHalf : public UMemory { + +public: + + UnicodeString text; + + int32_t cursor; // position of cursor in text + int32_t ante; // position of ante context marker '{' in text + int32_t post; // position of post context marker '}' in text + + // Record the offset to the cursor either to the left or to the + // right of the key. This is indicated by characters on the output + // side that allow the cursor to be positioned arbitrarily within + // the matching text. For example, abc{def} > | @@@ xyz; changes + // def to xyz and moves the cursor to before abc. Offset characters + // must be at the start or end, and they cannot move the cursor past + // the ante- or postcontext text. Placeholders are only valid in + // output text. The length of the ante and post context is + // determined at runtime, because of supplementals and quantifiers. + int32_t cursorOffset; // only nonzero on output side + + // Position of first CURSOR_OFFSET on _right_. This will be -1 + // for |@, -2 for |@@, etc., and 1 for @|, 2 for @@|, etc. + int32_t cursorOffsetPos; + + UBool anchorStart; + UBool anchorEnd; + + /** + * The segment number from 1..n of the next '(' we see + * during parsing; 1-based. + */ + int32_t nextSegmentNumber; + + TransliteratorParser& parser; + + //-------------------------------------------------- + // Methods + + RuleHalf(TransliteratorParser& parser); + ~RuleHalf(); + + int32_t parse(const UnicodeString& rule, int32_t pos, int32_t limit, UErrorCode& status); + + int32_t parseSection(const UnicodeString& rule, int32_t pos, int32_t limit, + UnicodeString& buf, + const UnicodeString& illegal, + UBool isSegment, + UErrorCode& status); + + /** + * Remove context. + */ + void removeContext(); + + /** + * Return true if this half looks like valid output, that is, does not + * contain quantifiers or other special input-only elements. + */ + UBool isValidOutput(TransliteratorParser& parser); + + /** + * Return true if this half looks like valid input, that is, does not + * contain functions or other special output-only elements. + */ + UBool isValidInput(TransliteratorParser& parser); + + int syntaxError(UErrorCode code, + const UnicodeString& rule, + int32_t start, + UErrorCode& status) { + return parser.syntaxError(code, rule, start, status); + } + +private: + // Disallowed methods; no impl. + RuleHalf(const RuleHalf&); + RuleHalf& operator=(const RuleHalf&); +}; + +RuleHalf::RuleHalf(TransliteratorParser& p) : + parser(p) +{ + cursor = -1; + ante = -1; + post = -1; + cursorOffset = 0; + cursorOffsetPos = 0; + anchorStart = anchorEnd = false; + nextSegmentNumber = 1; +} + +RuleHalf::~RuleHalf() { +} + +/** + * Parse one side of a rule, stopping at either the limit, + * the END_OF_RULE character, or an operator. + * @return the index after the terminating character, or + * if limit was reached, limit + */ +int32_t RuleHalf::parse(const UnicodeString& rule, int32_t pos, int32_t limit, UErrorCode& status) { + int32_t start = pos; + text.truncate(0); + pos = parseSection(rule, pos, limit, text, UnicodeString(true, ILLEGAL_TOP, -1), false, status); + + if (cursorOffset > 0 && cursor != cursorOffsetPos) { + return syntaxError(U_MISPLACED_CURSOR_OFFSET, rule, start, status); + } + + return pos; +} + +/** + * Parse a section of one side of a rule, stopping at either + * the limit, the END_OF_RULE character, an operator, or a + * segment close character. This method parses both a + * top-level rule half and a segment within such a rule half. + * It calls itself recursively to parse segments and nested + * segments. + * @param buf buffer into which to accumulate the rule pattern + * characters, either literal characters from the rule or + * standins for UnicodeMatcher objects including segments. + * @param illegal the set of special characters that is illegal during + * this parse. + * @param isSegment if true, then we've already seen a '(' and + * pos on entry points right after it. Accumulate everything + * up to the closing ')', put it in a segment matcher object, + * generate a standin for it, and add the standin to buf. As + * a side effect, update the segments vector with a reference + * to the segment matcher. This works recursively for nested + * segments. If isSegment is false, just accumulate + * characters into buf. + * @return the index after the terminating character, or + * if limit was reached, limit + */ +int32_t RuleHalf::parseSection(const UnicodeString& rule, int32_t pos, int32_t limit, + UnicodeString& buf, + const UnicodeString& illegal, + UBool isSegment, UErrorCode& status) { + int32_t start = pos; + ParsePosition pp; + UnicodeString scratch; + UBool done = false; + int32_t quoteStart = -1; // Most recent 'single quoted string' + int32_t quoteLimit = -1; + int32_t varStart = -1; // Most recent $variableReference + int32_t varLimit = -1; + int32_t bufStart = buf.length(); + + while (pos < limit && !done) { + // Since all syntax characters are in the BMP, fetching + // 16-bit code units suffices here. + char16_t c = rule.charAt(pos++); + if (PatternProps::isWhiteSpace(c)) { + // Ignore whitespace. Note that this is not Unicode + // spaces, but Java spaces -- a subset, representing + // whitespace likely to be seen in code. + continue; + } + if (u_strchr(HALF_ENDERS, c) != nullptr) { + if (isSegment) { + // Unclosed segment + return syntaxError(U_UNCLOSED_SEGMENT, rule, start, status); + } + break; + } + if (anchorEnd) { + // Text after a presumed end anchor is a syntax err + return syntaxError(U_MALFORMED_VARIABLE_REFERENCE, rule, start, status); + } + if (UnicodeSet::resemblesPattern(rule, pos-1)) { + pp.setIndex(pos-1); // Backup to opening '[' + buf.append(parser.parseSet(rule, pp, status)); + if (U_FAILURE(status)) { + return syntaxError(U_MALFORMED_SET, rule, start, status); + } + pos = pp.getIndex(); + continue; + } + // Handle escapes + if (c == ESCAPE) { + if (pos == limit) { + return syntaxError(U_TRAILING_BACKSLASH, rule, start, status); + } + UChar32 escaped = rule.unescapeAt(pos); // pos is already past '\\' + if (escaped == (UChar32) -1) { + return syntaxError(U_MALFORMED_UNICODE_ESCAPE, rule, start, status); + } + if (!parser.checkVariableRange(escaped)) { + return syntaxError(U_VARIABLE_RANGE_OVERLAP, rule, start, status); + } + buf.append(escaped); + continue; + } + // Handle quoted matter + if (c == QUOTE) { + int32_t iq = rule.indexOf(QUOTE, pos); + if (iq == pos) { + buf.append(c); // Parse [''] outside quotes as ['] + ++pos; + } else { + /* This loop picks up a run of quoted text of the + * form 'aaaa' each time through. If this run + * hasn't really ended ('aaaa''bbbb') then it keeps + * looping, each time adding on a new run. When it + * reaches the final quote it breaks. + */ + quoteStart = buf.length(); + for (;;) { + if (iq < 0) { + return syntaxError(U_UNTERMINATED_QUOTE, rule, start, status); + } + scratch.truncate(0); + rule.extractBetween(pos, iq, scratch); + buf.append(scratch); + pos = iq+1; + if (pos < limit && rule.charAt(pos) == QUOTE) { + // Parse [''] inside quotes as ['] + iq = rule.indexOf(QUOTE, pos+1); + // Continue looping + } else { + break; + } + } + quoteLimit = buf.length(); + + for (iq=quoteStart; iq= 0) { + syntaxError(U_ILLEGAL_CHARACTER, rule, start, status); + } + + switch (c) { + + //------------------------------------------------------ + // Elements allowed within and out of segments + //------------------------------------------------------ + case ANCHOR_START: + if (buf.length() == 0 && !anchorStart) { + anchorStart = true; + } else { + return syntaxError(U_MISPLACED_ANCHOR_START, + rule, start, status); + } + break; + case SEGMENT_OPEN: + { + // bufSegStart is the offset in buf to the first + // character of the segment we are parsing. + int32_t bufSegStart = buf.length(); + + // Record segment number now, since nextSegmentNumber + // will be incremented during the call to parseSection + // if there are nested segments. + int32_t segmentNumber = nextSegmentNumber++; // 1-based + + // Parse the segment + pos = parseSection(rule, pos, limit, buf, UnicodeString(true, ILLEGAL_SEG, -1), true, status); + + // After parsing a segment, the relevant characters are + // in buf, starting at offset bufSegStart. Extract them + // into a string matcher, and replace them with a + // standin for that matcher. + StringMatcher* m = + new StringMatcher(buf, bufSegStart, buf.length(), + segmentNumber, *parser.curData); + if (m == nullptr) { + return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); + } + + // Record and associate object and segment number + parser.setSegmentObject(segmentNumber, m, status); + buf.truncate(bufSegStart); + buf.append(parser.getSegmentStandin(segmentNumber, status)); + } + break; + case FUNCTION: + case ALT_FUNCTION: + { + int32_t iref = pos; + TransliteratorIDParser::SingleID* single = + TransliteratorIDParser::parseFilterID(rule, iref); + // The next character MUST be a segment open + if (single == nullptr || + !ICU_Utility::parseChar(rule, iref, SEGMENT_OPEN)) { + return syntaxError(U_INVALID_FUNCTION, rule, start, status); + } + + Transliterator *t = single->createInstance(); + delete single; + if (t == nullptr) { + return syntaxError(U_INVALID_FUNCTION, rule, start, status); + } + + // bufSegStart is the offset in buf to the first + // character of the segment we are parsing. + int32_t bufSegStart = buf.length(); + + // Parse the segment + pos = parseSection(rule, iref, limit, buf, UnicodeString(true, ILLEGAL_FUNC, -1), true, status); + + // After parsing a segment, the relevant characters are + // in buf, starting at offset bufSegStart. + UnicodeString output; + buf.extractBetween(bufSegStart, buf.length(), output); + FunctionReplacer *r = + new FunctionReplacer(t, new StringReplacer(output, parser.curData)); + if (r == nullptr) { + return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); + } + + // Replace the buffer contents with a stand-in + buf.truncate(bufSegStart); + buf.append(parser.generateStandInFor(r, status)); + } + break; + case SymbolTable::SYMBOL_REF: + // Handle variable references and segment references "$1" .. "$9" + { + // A variable reference must be followed immediately + // by a Unicode identifier start and zero or more + // Unicode identifier part characters, or by a digit + // 1..9 if it is a segment reference. + if (pos == limit) { + // A variable ref character at the end acts as + // an anchor to the context limit, as in perl. + anchorEnd = true; + break; + } + // Parse "$1" "$2" .. "$9" .. (no upper limit) + c = rule.charAt(pos); + int32_t r = u_digit(c, 10); + if (r >= 1 && r <= 9) { + r = ICU_Utility::parseNumber(rule, pos, 10); + if (r < 0) { + return syntaxError(U_UNDEFINED_SEGMENT_REFERENCE, + rule, start, status); + } + buf.append(parser.getSegmentStandin(r, status)); + } else { + pp.setIndex(pos); + UnicodeString name = parser.parseData-> + parseReference(rule, pp, limit); + if (name.length() == 0) { + // This means the '$' was not followed by a + // valid name. Try to interpret it as an + // end anchor then. If this also doesn't work + // (if we see a following character) then signal + // an error. + anchorEnd = true; + break; + } + pos = pp.getIndex(); + // If this is a variable definition statement, + // then the LHS variable will be undefined. In + // that case appendVariableDef() will append the + // special placeholder char variableLimit-1. + varStart = buf.length(); + parser.appendVariableDef(name, buf, status); + varLimit = buf.length(); + } + } + break; + case DOT: + buf.append(parser.getDotStandIn(status)); + break; + case KLEENE_STAR: + case ONE_OR_MORE: + case ZERO_OR_ONE: + // Quantifiers. We handle single characters, quoted strings, + // variable references, and segments. + // a+ matches aaa + // 'foo'+ matches foofoofoo + // $v+ matches xyxyxy if $v == xy + // (seg)+ matches segsegseg + { + if (isSegment && buf.length() == bufStart) { + // The */+ immediately follows '(' + return syntaxError(U_MISPLACED_QUANTIFIER, rule, start, status); + } + + int32_t qstart, qlimit; + // The */+ follows an isolated character or quote + // or variable reference + if (buf.length() == quoteLimit) { + // The */+ follows a 'quoted string' + qstart = quoteStart; + qlimit = quoteLimit; + } else if (buf.length() == varLimit) { + // The */+ follows a $variableReference + qstart = varStart; + qlimit = varLimit; + } else { + // The */+ follows a single character, possibly + // a segment standin + qstart = buf.length() - 1; + qlimit = qstart + 1; + } + + UnicodeFunctor *m = + new StringMatcher(buf, qstart, qlimit, 0, *parser.curData); + if (m == nullptr) { + return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); + } + int32_t min = 0; + int32_t max = Quantifier::MAX; + switch (c) { + case ONE_OR_MORE: + min = 1; + break; + case ZERO_OR_ONE: + min = 0; + max = 1; + break; + // case KLEENE_STAR: + // do nothing -- min, max already set + } + m = new Quantifier(m, min, max); + if (m == nullptr) { + return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); + } + buf.truncate(qstart); + buf.append(parser.generateStandInFor(m, status)); + } + break; + + //------------------------------------------------------ + // Elements allowed ONLY WITHIN segments + //------------------------------------------------------ + case SEGMENT_CLOSE: + // assert(isSegment); + // We're done parsing a segment. + done = true; + break; + + //------------------------------------------------------ + // Elements allowed ONLY OUTSIDE segments + //------------------------------------------------------ + case CONTEXT_ANTE: + if (ante >= 0) { + return syntaxError(U_MULTIPLE_ANTE_CONTEXTS, rule, start, status); + } + ante = buf.length(); + break; + case CONTEXT_POST: + if (post >= 0) { + return syntaxError(U_MULTIPLE_POST_CONTEXTS, rule, start, status); + } + post = buf.length(); + break; + case CURSOR_POS: + if (cursor >= 0) { + return syntaxError(U_MULTIPLE_CURSORS, rule, start, status); + } + cursor = buf.length(); + break; + case CURSOR_OFFSET: + if (cursorOffset < 0) { + if (buf.length() > 0) { + return syntaxError(U_MISPLACED_CURSOR_OFFSET, rule, start, status); + } + --cursorOffset; + } else if (cursorOffset > 0) { + if (buf.length() != cursorOffsetPos || cursor >= 0) { + return syntaxError(U_MISPLACED_CURSOR_OFFSET, rule, start, status); + } + ++cursorOffset; + } else { + if (cursor == 0 && buf.length() == 0) { + cursorOffset = -1; + } else if (cursor < 0) { + cursorOffsetPos = buf.length(); + cursorOffset = 1; + } else { + return syntaxError(U_MISPLACED_CURSOR_OFFSET, rule, start, status); + } + } + break; + + + //------------------------------------------------------ + // Non-special characters + //------------------------------------------------------ + default: + // Disallow unquoted characters other than [0-9A-Za-z] + // in the printable ASCII range. These characters are + // reserved for possible future use. + if (c >= 0x0021 && c <= 0x007E && + !((c >= 0x0030/*'0'*/ && c <= 0x0039/*'9'*/) || + (c >= 0x0041/*'A'*/ && c <= 0x005A/*'Z'*/) || + (c >= 0x0061/*'a'*/ && c <= 0x007A/*'z'*/))) { + return syntaxError(U_UNQUOTED_SPECIAL, rule, start, status); + } + buf.append(c); + break; + } + } + + return pos; +} + +/** + * Remove context. + */ +void RuleHalf::removeContext() { + //text = text.substring(ante < 0 ? 0 : ante, + // post < 0 ? text.length() : post); + if (post >= 0) { + text.remove(post); + } + if (ante >= 0) { + text.removeBetween(0, ante); + } + ante = post = -1; + anchorStart = anchorEnd = false; +} + +/** + * Return true if this half looks like valid output, that is, does not + * contain quantifiers or other special input-only elements. + */ +UBool RuleHalf::isValidOutput(TransliteratorParser& transParser) { + for (int32_t i=0; iisReplacer(c)) { + return false; + } + } + return true; +} + +/** + * Return true if this half looks like valid input, that is, does not + * contain functions or other special output-only elements. + */ +UBool RuleHalf::isValidInput(TransliteratorParser& transParser) { + for (int32_t i=0; iisMatcher(c)) { + return false; + } + } + return true; +} + +//---------------------------------------------------------------------- +// PUBLIC API +//---------------------------------------------------------------------- + +/** + * Constructor. + */ +TransliteratorParser::TransliteratorParser(UErrorCode &statusReturn) : +dataVector(statusReturn), +idBlockVector(statusReturn), +variablesVector(statusReturn), +segmentObjects(statusReturn) +{ + idBlockVector.setDeleter(uprv_deleteUObject); + curData = nullptr; + compoundFilter = nullptr; + parseData = nullptr; + variableNames.setValueDeleter(uprv_deleteUObject); +} + +/** + * Destructor. + */ +TransliteratorParser::~TransliteratorParser() { + while (!dataVector.isEmpty()) + delete (TransliterationRuleData*)(dataVector.orphanElementAt(0)); + delete compoundFilter; + delete parseData; + while (!variablesVector.isEmpty()) + delete (UnicodeFunctor*)variablesVector.orphanElementAt(0); +} + +void +TransliteratorParser::parse(const UnicodeString& rules, + UTransDirection transDirection, + UParseError& pe, + UErrorCode& ec) { + if (U_SUCCESS(ec)) { + parseRules(rules, transDirection, ec); + pe = parseError; + } +} + +/** + * Return the compound filter parsed by parse(). Caller owns result. + */ +UnicodeSet* TransliteratorParser::orphanCompoundFilter() { + UnicodeSet* f = compoundFilter; + compoundFilter = nullptr; + return f; +} + +//---------------------------------------------------------------------- +// Private implementation +//---------------------------------------------------------------------- + +/** + * Parse the given string as a sequence of rules, separated by newline + * characters ('\n'), and cause this object to implement those rules. Any + * previous rules are discarded. Typically this method is called exactly + * once, during construction. + * @exception IllegalArgumentException if there is a syntax error in the + * rules + */ +void TransliteratorParser::parseRules(const UnicodeString& rule, + UTransDirection theDirection, + UErrorCode& status) +{ + // Clear error struct + uprv_memset(&parseError, 0, sizeof(parseError)); + parseError.line = parseError.offset = -1; + + UBool parsingIDs = true; + int32_t ruleCount = 0; + + while (!dataVector.isEmpty()) { + delete (TransliterationRuleData*)(dataVector.orphanElementAt(0)); + } + if (U_FAILURE(status)) { + return; + } + + idBlockVector.removeAllElements(); + curData = nullptr; + direction = theDirection; + ruleCount = 0; + + delete compoundFilter; + compoundFilter = nullptr; + + while (!variablesVector.isEmpty()) { + delete (UnicodeFunctor*)variablesVector.orphanElementAt(0); + } + variableNames.removeAll(); + parseData = new ParseData(0, &variablesVector, &variableNames); + if (parseData == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + dotStandIn = (char16_t) -1; + + UnicodeString *tempstr = nullptr; // used for memory allocation error checking + UnicodeString str; // scratch + UnicodeString idBlockResult; + int32_t pos = 0; + int32_t limit = rule.length(); + + // The compound filter offset is an index into idBlockResult. + // If it is 0, then the compound filter occurred at the start, + // and it is the offset to the _start_ of the compound filter + // pattern. Otherwise it is the offset to the _limit_ of the + // compound filter pattern within idBlockResult. + compoundFilter = nullptr; + int32_t compoundFilterOffset = -1; + + while (pos < limit && U_SUCCESS(status)) { + char16_t c = rule.charAt(pos++); + if (PatternProps::isWhiteSpace(c)) { + // Ignore leading whitespace. + continue; + } + // Skip lines starting with the comment character + if (c == RULE_COMMENT_CHAR) { + pos = rule.indexOf((char16_t)0x000A /*\n*/, pos) + 1; + if (pos == 0) { + break; // No "\n" found; rest of rule is a comment + } + continue; // Either fall out or restart with next line + } + + // skip empty rules + if (c == END_OF_RULE) + continue; + + // keep track of how many rules we've seen + ++ruleCount; + + // We've found the start of a rule or ID. c is its first + // character, and pos points past c. + --pos; + // Look for an ID token. Must have at least ID_TOKEN_LEN + 1 + // chars left. + if ((pos + ID_TOKEN_LEN + 1) <= limit && + rule.compare(pos, ID_TOKEN_LEN, ID_TOKEN) == 0) { + pos += ID_TOKEN_LEN; + c = rule.charAt(pos); + while (PatternProps::isWhiteSpace(c) && pos < limit) { + ++pos; + c = rule.charAt(pos); + } + + int32_t p = pos; + + if (!parsingIDs) { + if (curData != nullptr) { + U_ASSERT(!dataVector.hasDeleter()); + if (direction == UTRANS_FORWARD) + dataVector.addElement(curData, status); + else + dataVector.insertElementAt(curData, 0, status); + if (U_FAILURE(status)) { + delete curData; + } + curData = nullptr; + } + parsingIDs = true; + } + + TransliteratorIDParser::SingleID* id = + TransliteratorIDParser::parseSingleID(rule, p, direction, status); + if (p != pos && ICU_Utility::parseChar(rule, p, END_OF_RULE)) { + // Successful ::ID parse. + + if (direction == UTRANS_FORWARD) { + idBlockResult.append(id->canonID).append(END_OF_RULE); + } else { + idBlockResult.insert(0, END_OF_RULE); + idBlockResult.insert(0, id->canonID); + } + + } else { + // Couldn't parse an ID. Try to parse a global filter + int32_t withParens = -1; + UnicodeSet* f = TransliteratorIDParser::parseGlobalFilter(rule, p, direction, withParens, nullptr); + if (f != nullptr) { + if (ICU_Utility::parseChar(rule, p, END_OF_RULE) + && (direction == UTRANS_FORWARD) == (withParens == 0)) + { + if (compoundFilter != nullptr) { + // Multiple compound filters + syntaxError(U_MULTIPLE_COMPOUND_FILTERS, rule, pos, status); + delete f; + } else { + compoundFilter = f; + compoundFilterOffset = ruleCount; + } + } else { + delete f; + } + } else { + // Invalid ::id + // Can be parsed as neither an ID nor a global filter + syntaxError(U_INVALID_ID, rule, pos, status); + } + } + delete id; + pos = p; + } else { + if (parsingIDs) { + tempstr = new UnicodeString(idBlockResult); + // nullptr pointer check + if (tempstr == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + U_ASSERT(idBlockVector.hasDeleter()); + if (direction == UTRANS_FORWARD) + idBlockVector.adoptElement(tempstr, status); + else + idBlockVector.insertElementAt(tempstr, 0, status); + if (U_FAILURE(status)) { + return; + } + idBlockResult.remove(); + parsingIDs = false; + curData = new TransliterationRuleData(status); + // nullptr pointer check + if (curData == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + parseData->data = curData; + + // By default, rules use part of the private use area + // E000..F8FF for variables and other stand-ins. Currently + // the range F000..F8FF is typically sufficient. The 'use + // variable range' pragma allows rule sets to modify this. + setVariableRange(0xF000, 0xF8FF, status); + } + + if (resemblesPragma(rule, pos, limit)) { + int32_t ppp = parsePragma(rule, pos, limit, status); + if (ppp < 0) { + syntaxError(U_MALFORMED_PRAGMA, rule, pos, status); + } + pos = ppp; + // Parse a rule + } else { + pos = parseRule(rule, pos, limit, status); + } + } + } + + if (parsingIDs && idBlockResult.length() > 0) { + tempstr = new UnicodeString(idBlockResult); + // nullptr pointer check + if (tempstr == nullptr) { + // TODO: Testing, forcing this path, shows many memory leaks. ICU-21701 + // intltest translit/TransliteratorTest/TestInstantiation + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (direction == UTRANS_FORWARD) + idBlockVector.adoptElement(tempstr, status); + else + idBlockVector.insertElementAt(tempstr, 0, status); + if (U_FAILURE(status)) { + return; + } + } + else if (!parsingIDs && curData != nullptr) { + if (direction == UTRANS_FORWARD) { + dataVector.addElement(curData, status); + } else { + dataVector.insertElementAt(curData, 0, status); + } + if (U_FAILURE(status)) { + delete curData; + curData = nullptr; + } + } + + if (U_SUCCESS(status)) { + // Convert the set vector to an array + int32_t i, dataVectorSize = dataVector.size(); + for (i = 0; i < dataVectorSize; i++) { + TransliterationRuleData* data = (TransliterationRuleData*)dataVector.elementAt(i); + data->variablesLength = variablesVector.size(); + if (data->variablesLength == 0) { + data->variables = 0; + } else { + data->variables = (UnicodeFunctor**)uprv_malloc(data->variablesLength * sizeof(UnicodeFunctor*)); + // nullptr pointer check + if (data->variables == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + data->variablesAreOwned = (i == 0); + } + + for (int32_t j = 0; j < data->variablesLength; j++) { + data->variables[j] = + static_cast(variablesVector.elementAt(j)); + } + + data->variableNames.removeAll(); + int32_t p = UHASH_FIRST; + const UHashElement* he = variableNames.nextElement(p); + while (he != nullptr) { + UnicodeString* tempus = ((UnicodeString*)(he->value.pointer))->clone(); + if (tempus == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + data->variableNames.put(*((UnicodeString*)(he->key.pointer)), + tempus, status); + he = variableNames.nextElement(p); + } + } + variablesVector.removeAllElements(); // keeps them from getting deleted when we succeed + + // Index the rules + if (compoundFilter != nullptr) { + if ((direction == UTRANS_FORWARD && compoundFilterOffset != 1) || + (direction == UTRANS_REVERSE && compoundFilterOffset != ruleCount)) { + status = U_MISPLACED_COMPOUND_FILTER; + } + } + + for (i = 0; i < dataVectorSize; i++) { + TransliterationRuleData* data = (TransliterationRuleData*)dataVector.elementAt(i); + data->ruleSet.freeze(parseError, status); + } + if (idBlockVector.size() == 1 && ((UnicodeString*)idBlockVector.elementAt(0))->isEmpty()) { + idBlockVector.removeElementAt(0); + } + } +} + +/** + * Set the variable range to [start, end] (inclusive). + */ +void TransliteratorParser::setVariableRange(int32_t start, int32_t end, UErrorCode& status) { + if (start > end || start < 0 || end > 0xFFFF) { + status = U_MALFORMED_PRAGMA; + return; + } + + curData->variablesBase = (char16_t) start; + if (dataVector.size() == 0) { + variableNext = (char16_t) start; + variableLimit = (char16_t) (end + 1); + } +} + +/** + * Assert that the given character is NOT within the variable range. + * If it is, return false. This is necessary to ensure that the + * variable range does not overlap characters used in a rule. + */ +UBool TransliteratorParser::checkVariableRange(UChar32 ch) const { + return !(ch >= curData->variablesBase && ch < variableLimit); +} + +/** + * Set the maximum backup to 'backup', in response to a pragma + * statement. + */ +void TransliteratorParser::pragmaMaximumBackup(int32_t /*backup*/) { + //TODO Finish +} + +/** + * Begin normalizing all rules using the given mode, in response + * to a pragma statement. + */ +void TransliteratorParser::pragmaNormalizeRules(UNormalizationMode /*mode*/) { + //TODO Finish +} + +static const char16_t PRAGMA_USE[] = {0x75,0x73,0x65,0x20,0}; // "use " + +static const char16_t PRAGMA_VARIABLE_RANGE[] = {0x7E,0x76,0x61,0x72,0x69,0x61,0x62,0x6C,0x65,0x20,0x72,0x61,0x6E,0x67,0x65,0x20,0x23,0x20,0x23,0x7E,0x3B,0}; // "~variable range # #~;" + +static const char16_t PRAGMA_MAXIMUM_BACKUP[] = {0x7E,0x6D,0x61,0x78,0x69,0x6D,0x75,0x6D,0x20,0x62,0x61,0x63,0x6B,0x75,0x70,0x20,0x23,0x7E,0x3B,0}; // "~maximum backup #~;" + +static const char16_t PRAGMA_NFD_RULES[] = {0x7E,0x6E,0x66,0x64,0x20,0x72,0x75,0x6C,0x65,0x73,0x7E,0x3B,0}; // "~nfd rules~;" + +static const char16_t PRAGMA_NFC_RULES[] = {0x7E,0x6E,0x66,0x63,0x20,0x72,0x75,0x6C,0x65,0x73,0x7E,0x3B,0}; // "~nfc rules~;" + +/** + * Return true if the given rule looks like a pragma. + * @param pos offset to the first non-whitespace character + * of the rule. + * @param limit pointer past the last character of the rule. + */ +UBool TransliteratorParser::resemblesPragma(const UnicodeString& rule, int32_t pos, int32_t limit) { + // Must start with /use\s/i + return ICU_Utility::parsePattern(rule, pos, limit, UnicodeString(true, PRAGMA_USE, 4), nullptr) >= 0; +} + +/** + * Parse a pragma. This method assumes resemblesPragma() has + * already returned true. + * @param pos offset to the first non-whitespace character + * of the rule. + * @param limit pointer past the last character of the rule. + * @return the position index after the final ';' of the pragma, + * or -1 on failure. + */ +int32_t TransliteratorParser::parsePragma(const UnicodeString& rule, int32_t pos, int32_t limit, UErrorCode& status) { + int32_t array[2]; + + // resemblesPragma() has already returned true, so we + // know that pos points to /use\s/i; we can skip 4 characters + // immediately + pos += 4; + + // Here are the pragmas we recognize: + // use variable range 0xE000 0xEFFF; + // use maximum backup 16; + // use nfd rules; + // use nfc rules; + int p = ICU_Utility::parsePattern(rule, pos, limit, UnicodeString(true, PRAGMA_VARIABLE_RANGE, -1), array); + if (p >= 0) { + setVariableRange(array[0], array[1], status); + return p; + } + + p = ICU_Utility::parsePattern(rule, pos, limit, UnicodeString(true, PRAGMA_MAXIMUM_BACKUP, -1), array); + if (p >= 0) { + pragmaMaximumBackup(array[0]); + return p; + } + + p = ICU_Utility::parsePattern(rule, pos, limit, UnicodeString(true, PRAGMA_NFD_RULES, -1), nullptr); + if (p >= 0) { + pragmaNormalizeRules(UNORM_NFD); + return p; + } + + p = ICU_Utility::parsePattern(rule, pos, limit, UnicodeString(true, PRAGMA_NFC_RULES, -1), nullptr); + if (p >= 0) { + pragmaNormalizeRules(UNORM_NFC); + return p; + } + + // Syntax error: unable to parse pragma + return -1; +} + +/** + * MAIN PARSER. Parse the next rule in the given rule string, starting + * at pos. Return the index after the last character parsed. Do not + * parse characters at or after limit. + * + * Important: The character at pos must be a non-whitespace character + * that is not the comment character. + * + * This method handles quoting, escaping, and whitespace removal. It + * parses the end-of-rule character. It recognizes context and cursor + * indicators. Once it does a lexical breakdown of the rule at pos, it + * creates a rule object and adds it to our rule list. + */ +int32_t TransliteratorParser::parseRule(const UnicodeString& rule, int32_t pos, int32_t limit, UErrorCode& status) { + // Locate the left side, operator, and right side + int32_t start = pos; + char16_t op = 0; + int32_t i; + + // Set up segments data + segmentStandins.truncate(0); + segmentObjects.removeAllElements(); + + // Use pointers to automatics to make swapping possible. + RuleHalf _left(*this), _right(*this); + RuleHalf* left = &_left; + RuleHalf* right = &_right; + + undefinedVariableName.remove(); + pos = left->parse(rule, pos, limit, status); + if (U_FAILURE(status)) { + return start; + } + + if (pos == limit || u_strchr(gOPERATORS, (op = rule.charAt(--pos))) == nullptr) { + return syntaxError(U_MISSING_OPERATOR, rule, start, status); + } + ++pos; + + // Found an operator char. Check for forward-reverse operator. + if (op == REVERSE_RULE_OP && + (pos < limit && rule.charAt(pos) == FORWARD_RULE_OP)) { + ++pos; + op = FWDREV_RULE_OP; + } + + // Translate alternate op characters. + switch (op) { + case ALT_FORWARD_RULE_OP: + op = FORWARD_RULE_OP; + break; + case ALT_REVERSE_RULE_OP: + op = REVERSE_RULE_OP; + break; + case ALT_FWDREV_RULE_OP: + op = FWDREV_RULE_OP; + break; + } + + pos = right->parse(rule, pos, limit, status); + if (U_FAILURE(status)) { + return start; + } + + if (pos < limit) { + if (rule.charAt(--pos) == END_OF_RULE) { + ++pos; + } else { + // RuleHalf parser must have terminated at an operator + return syntaxError(U_UNQUOTED_SPECIAL, rule, start, status); + } + } + + if (op == VARIABLE_DEF_OP) { + // LHS is the name. RHS is a single character, either a literal + // or a set (already parsed). If RHS is longer than one + // character, it is either a multi-character string, or multiple + // sets, or a mixture of chars and sets -- syntax error. + + // We expect to see a single undefined variable (the one being + // defined). + if (undefinedVariableName.length() == 0) { + // "Missing '$' or duplicate definition" + return syntaxError(U_BAD_VARIABLE_DEFINITION, rule, start, status); + } + if (left->text.length() != 1 || left->text.charAt(0) != variableLimit) { + // "Malformed LHS" + return syntaxError(U_MALFORMED_VARIABLE_DEFINITION, rule, start, status); + } + if (left->anchorStart || left->anchorEnd || + right->anchorStart || right->anchorEnd) { + return syntaxError(U_MALFORMED_VARIABLE_DEFINITION, rule, start, status); + } + // We allow anything on the right, including an empty string. + UnicodeString* value = new UnicodeString(right->text); + // nullptr pointer check + if (value == nullptr) { + return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); + } + variableNames.put(undefinedVariableName, value, status); + ++variableLimit; + return pos; + } + + // If this is not a variable definition rule, we shouldn't have + // any undefined variable names. + if (undefinedVariableName.length() != 0) { + return syntaxError(// "Undefined variable $" + undefinedVariableName, + U_UNDEFINED_VARIABLE, + rule, start, status); + } + + // Verify segments + if (segmentStandins.length() > segmentObjects.size()) { + syntaxError(U_UNDEFINED_SEGMENT_REFERENCE, rule, start, status); + } + for (i=0; iremoveContext(); + left->cursor = -1; + left->cursorOffset = 0; + } + + // Normalize context + if (left->ante < 0) { + left->ante = 0; + } + if (left->post < 0) { + left->post = left->text.length(); + } + + // Context is only allowed on the input side. Cursors are only + // allowed on the output side. Segment delimiters can only appear + // on the left, and references on the right. Cursor offset + // cannot appear without an explicit cursor. Cursor offset + // cannot place the cursor outside the limits of the context. + // Anchors are only allowed on the input side. + if (right->ante >= 0 || right->post >= 0 || left->cursor >= 0 || + (right->cursorOffset != 0 && right->cursor < 0) || + // - The following two checks were used to ensure that the + // - the cursor offset stayed within the ante- or postcontext. + // - However, with the addition of quantifiers, we have to + // - allow arbitrary cursor offsets and do runtime checking. + //(right->cursorOffset > (left->text.length() - left->post)) || + //(-right->cursorOffset > left->ante) || + right->anchorStart || right->anchorEnd || + !left->isValidInput(*this) || !right->isValidOutput(*this) || + left->ante > left->post) { + + return syntaxError(U_MALFORMED_RULE, rule, start, status); + } + + // Flatten segment objects vector to an array + UnicodeFunctor** segmentsArray = nullptr; + if (segmentObjects.size() > 0) { + segmentsArray = (UnicodeFunctor **)uprv_malloc(segmentObjects.size() * sizeof(UnicodeFunctor *)); + // Null pointer check + if (segmentsArray == nullptr) { + return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); + } + segmentObjects.toArray((void**) segmentsArray); + } + TransliterationRule* temptr = new TransliterationRule( + left->text, left->ante, left->post, + right->text, right->cursor, right->cursorOffset, + segmentsArray, + segmentObjects.size(), + left->anchorStart, left->anchorEnd, + curData, + status); + //Null pointer check + if (temptr == nullptr) { + uprv_free(segmentsArray); + return syntaxError(U_MEMORY_ALLOCATION_ERROR, rule, start, status); + } + + curData->ruleSet.addRule(temptr, status); + + return pos; +} + +/** + * Called by main parser upon syntax error. Search the rule string + * for the probable end of the rule. Of course, if the error is that + * the end of rule marker is missing, then the rule end will not be found. + * In any case the rule start will be correctly reported. + * @param msg error description + * @param rule pattern string + * @param start position of first character of current rule + */ +int32_t TransliteratorParser::syntaxError(UErrorCode parseErrorCode, + const UnicodeString& rule, + int32_t pos, + UErrorCode& status) +{ + parseError.offset = pos; + parseError.line = 0 ; /* we are not using line numbers */ + + // for pre-context + const int32_t LEN = U_PARSE_CONTEXT_LEN - 1; + int32_t start = uprv_max(pos - LEN, 0); + int32_t stop = pos; + + rule.extract(start,stop-start,parseError.preContext); + //null terminate the buffer + parseError.preContext[stop-start] = 0; + + //for post-context + start = pos; + stop = uprv_min(pos + LEN, rule.length()); + + rule.extract(start,stop-start,parseError.postContext); + //null terminate the buffer + parseError.postContext[stop-start]= 0; + + status = (UErrorCode)parseErrorCode; + return pos; + +} + +/** + * Parse a UnicodeSet out, store it, and return the stand-in character + * used to represent it. + */ +char16_t TransliteratorParser::parseSet(const UnicodeString& rule, + ParsePosition& pos, + UErrorCode& status) { + UnicodeSet* set = new UnicodeSet(rule, pos, USET_IGNORE_SPACE, parseData, status); + // Null pointer check + if (set == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return (char16_t)0x0000; // Return empty character with error. + } + set->compact(); + return generateStandInFor(set, status); +} + +/** + * Generate and return a stand-in for a new UnicodeFunctor. Store + * the matcher (adopt it). + */ +char16_t TransliteratorParser::generateStandInFor(UnicodeFunctor* adopted, UErrorCode& status) { + // assert(obj != null); + + // Look up previous stand-in, if any. This is a short list + // (typical n is 0, 1, or 2); linear search is optimal. + for (int32_t i=0; ivariablesBase + i); + } + } + + if (variableNext >= variableLimit) { + delete adopted; + status = U_VARIABLE_RANGE_EXHAUSTED; + return 0; + } + variablesVector.addElement(adopted, status); + if (U_FAILURE(status)) { + delete adopted; + return 0; + } + return variableNext++; +} + +/** + * Return the standin for segment seg (1-based). + */ +char16_t TransliteratorParser::getSegmentStandin(int32_t seg, UErrorCode& status) { + // Special character used to indicate an empty spot + char16_t empty = curData->variablesBase - 1; + while (segmentStandins.length() < seg) { + segmentStandins.append(empty); + } + char16_t c = segmentStandins.charAt(seg-1); + if (c == empty) { + if (variableNext >= variableLimit) { + status = U_VARIABLE_RANGE_EXHAUSTED; + return 0; + } + c = variableNext++; + // Set a placeholder in the primary variables vector that will be + // filled in later by setSegmentObject(). We know that we will get + // called first because setSegmentObject() will call us. + variablesVector.addElement((void*) nullptr, status); + segmentStandins.setCharAt(seg-1, c); + } + return c; +} + +/** + * Set the object for segment seg (1-based). + */ +void TransliteratorParser::setSegmentObject(int32_t seg, StringMatcher* adopted, UErrorCode& status) { + // Since we call parseSection() recursively, nested + // segments will result in segment i+1 getting parsed + // and stored before segment i; be careful with the + // vector handling here. + if (segmentObjects.size() < seg) { + segmentObjects.setSize(seg, status); + } + if (U_FAILURE(status)) { + return; + } + int32_t index = getSegmentStandin(seg, status) - curData->variablesBase; + if (segmentObjects.elementAt(seg-1) != nullptr || + variablesVector.elementAt(index) != nullptr) { + // should never happen + if (U_SUCCESS(status)) {status = U_INTERNAL_TRANSLITERATOR_ERROR;} + return; + } + // Note: neither segmentObjects or variablesVector has an object deleter function. + segmentObjects.setElementAt(adopted, seg-1); + variablesVector.setElementAt(adopted, index); +} + +/** + * Return the stand-in for the dot set. It is allocated the first + * time and reused thereafter. + */ +char16_t TransliteratorParser::getDotStandIn(UErrorCode& status) { + if (dotStandIn == (char16_t) -1) { + UnicodeSet* tempus = new UnicodeSet(UnicodeString(true, DOT_SET, -1), status); + // Null pointer check. + if (tempus == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return (char16_t)0x0000; + } + dotStandIn = generateStandInFor(tempus, status); + } + return dotStandIn; +} + +/** + * Append the value of the given variable name to the given + * UnicodeString. + */ +void TransliteratorParser::appendVariableDef(const UnicodeString& name, + UnicodeString& buf, + UErrorCode& status) { + const UnicodeString* s = (const UnicodeString*) variableNames.get(name); + if (s == nullptr) { + // We allow one undefined variable so that variable definition + // statements work. For the first undefined variable we return + // the special placeholder variableLimit-1, and save the variable + // name. + if (undefinedVariableName.length() == 0) { + undefinedVariableName = name; + if (variableNext >= variableLimit) { + // throw new RuntimeException("Private use variables exhausted"); + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + buf.append((char16_t) --variableLimit); + } else { + //throw new IllegalArgumentException("Undefined variable $" + // + name); + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + } else { + buf.append(*s); + } +} + +/** + * Glue method to get around access restrictions in C++. + */ +/*Transliterator* TransliteratorParser::createBasicInstance(const UnicodeString& id, const UnicodeString* canonID) { + return Transliterator::createBasicInstance(id, canonID); +}*/ + +U_NAMESPACE_END + +U_CAPI int32_t +utrans_stripRules(const char16_t *source, int32_t sourceLen, char16_t *target, UErrorCode *status) { + U_NAMESPACE_USE + + //const char16_t *sourceStart = source; + const char16_t *targetStart = target; + const char16_t *sourceLimit = source+sourceLen; + char16_t *targetLimit = target+sourceLen; + UChar32 c = 0; + UBool quoted = false; + int32_t index; + + uprv_memset(target, 0, sourceLen*U_SIZEOF_UCHAR); + + /* read the rules into the buffer */ + while (source < sourceLimit) + { + index=0; + U16_NEXT_UNSAFE(source, index, c); + source+=index; + if(c == QUOTE) { + quoted = (UBool)!quoted; + } + else if (!quoted) { + if (c == RULE_COMMENT_CHAR) { + /* skip comments and all preceding spaces */ + while (targetStart < target && *(target - 1) == 0x0020) { + target--; + } + do { + if (source == sourceLimit) { + c = U_SENTINEL; + break; + } + c = *(source++); + } + while (c != CR && c != LF); + if (c < 0) { + break; + } + } + else if (c == ESCAPE && source < sourceLimit) { + UChar32 c2 = *source; + if (c2 == CR || c2 == LF) { + /* A backslash at the end of a line. */ + /* Since we're stripping lines, ignore the backslash. */ + source++; + continue; + } + if (c2 == 0x0075 && source+5 < sourceLimit) { /* \u seen. \U isn't unescaped. */ + int32_t escapeOffset = 0; + UnicodeString escapedStr(source, 5); + c2 = escapedStr.unescapeAt(escapeOffset); + + if (c2 == (UChar32)0xFFFFFFFF || escapeOffset == 0) + { + *status = U_PARSE_ERROR; + return 0; + } + if (!PatternProps::isWhiteSpace(c2) && !u_iscntrl(c2) && !u_ispunct(c2)) { + /* It was escaped for a reason. Write what it was suppose to be. */ + source+=5; + c = c2; + } + } + else if (c2 == QUOTE) { + /* \' seen. Make sure we don't do anything when we see it again. */ + quoted = (UBool)!quoted; + } + } + } + if (c == CR || c == LF) + { + /* ignore spaces carriage returns, and all leading spaces on the next line. + * and line feed unless in the form \uXXXX + */ + quoted = false; + while (source < sourceLimit) { + c = *(source); + if (c != CR && c != LF && c != 0x0020) { + break; + } + source++; + } + continue; + } + + /* Append char16_t * after dissembling if c > 0xffff*/ + index=0; + U16_APPEND_UNSAFE(target, index, c); + target+=index; + } + if (target < targetLimit) { + *target = 0; + } + return (int32_t)(target-targetStart); +} + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ diff --git a/deps/icu-small/source/i18n/rbt_pars.h b/deps/icu-small/source/i18n/rbt_pars.h index d1a4cd6997c6da..9f215a2119b26e 100644 --- a/deps/icu-small/source/i18n/rbt_pars.h +++ b/deps/icu-small/source/i18n/rbt_pars.h @@ -1,357 +1,357 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2011, International Business Machines Corporation -* and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/17/99 aliu Creation. -********************************************************************** -*/ -#ifndef RBT_PARS_H -#define RBT_PARS_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION -#ifdef __cplusplus - -#include "unicode/uobject.h" -#include "unicode/parseerr.h" -#include "unicode/unorm.h" -#include "rbt.h" -#include "hash.h" -#include "uvector.h" - -U_NAMESPACE_BEGIN - -class TransliterationRuleData; -class UnicodeFunctor; -class ParseData; -class RuleHalf; -class ParsePosition; -class StringMatcher; - -class TransliteratorParser : public UMemory { - - public: - - /** - * A Vector of TransliterationRuleData objects, one for each discrete group - * of rules in the rule set - */ - UVector dataVector; - - /** - * PUBLIC data member. - * A Vector of UnicodeStrings containing all of the ID blocks in the rule set - */ - UVector idBlockVector; - - /** - * PUBLIC data member containing the parsed compound filter, if any. - */ - UnicodeSet* compoundFilter; - - private: - - /** - * The current data object for which we are parsing rules - */ - TransliterationRuleData* curData; - - UTransDirection direction; - - /** - * Parse error information. - */ - UParseError parseError; - - /** - * Temporary symbol table used during parsing. - */ - ParseData* parseData; - - /** - * Temporary vector of matcher variables. When parsing is complete, this - * is copied into the array data.variables. As with data.variables, - * element 0 corresponds to character data.variablesBase. - */ - UVector variablesVector; - - /** - * Temporary table of variable names. When parsing is complete, this is - * copied into data.variableNames. - */ - Hashtable variableNames; - - /** - * String of standins for segments. Used during the parsing of a single - * rule. segmentStandins.charAt(0) is the standin for "$1" and corresponds - * to StringMatcher object segmentObjects.elementAt(0), etc. - */ - UnicodeString segmentStandins; - - /** - * Vector of StringMatcher objects for segments. Used during the - * parsing of a single rule. - * segmentStandins.charAt(0) is the standin for "$1" and corresponds - * to StringMatcher object segmentObjects.elementAt(0), etc. - */ - UVector segmentObjects; - - /** - * The next available stand-in for variables. This starts at some point in - * the private use area (discovered dynamically) and increments up toward - * variableLimit. At any point during parsing, available - * variables are variableNext..variableLimit-1. - */ - UChar variableNext; - - /** - * The last available stand-in for variables. This is discovered - * dynamically. At any point during parsing, available variables are - * variableNext..variableLimit-1. - */ - UChar variableLimit; - - /** - * When we encounter an undefined variable, we do not immediately signal - * an error, in case we are defining this variable, e.g., "$a = [a-z];". - * Instead, we save the name of the undefined variable, and substitute - * in the placeholder char variableLimit - 1, and decrement - * variableLimit. - */ - UnicodeString undefinedVariableName; - - /** - * The stand-in character for the 'dot' set, represented by '.' in - * patterns. This is allocated the first time it is needed, and - * reused thereafter. - */ - UChar dotStandIn; - -public: - - /** - * Constructor. - */ - TransliteratorParser(UErrorCode &statusReturn); - - /** - * Destructor. - */ - ~TransliteratorParser(); - - /** - * Parse the given string as a sequence of rules, separated by newline - * characters ('\n'), and cause this object to implement those rules. Any - * previous rules are discarded. Typically this method is called exactly - * once after construction. - * - * Parse the given rules, in the given direction. After this call - * returns, query the public data members for results. The caller - * owns the 'data' and 'compoundFilter' data members after this - * call returns. - * @param rules rules, separated by ';' - * @param direction either FORWARD or REVERSE. - * @param pe Struct to receive information on position - * of error if an error is encountered - * @param ec Output param set to success/failure code. - */ - void parse(const UnicodeString& rules, - UTransDirection direction, - UParseError& pe, - UErrorCode& ec); - - /** - * Return the compound filter parsed by parse(). Caller owns result. - * @return the compound filter parsed by parse(). - */ - UnicodeSet* orphanCompoundFilter(); - -private: - - /** - * Return a representation of this transliterator as source rules. - * @param rules Output param to receive the rules. - * @param direction either FORWARD or REVERSE. - */ - void parseRules(const UnicodeString& rules, - UTransDirection direction, - UErrorCode& status); - - /** - * MAIN PARSER. Parse the next rule in the given rule string, starting - * at pos. Return the index after the last character parsed. Do not - * parse characters at or after limit. - * - * Important: The character at pos must be a non-whitespace character - * that is not the comment character. - * - * This method handles quoting, escaping, and whitespace removal. It - * parses the end-of-rule character. It recognizes context and cursor - * indicators. Once it does a lexical breakdown of the rule at pos, it - * creates a rule object and adds it to our rule list. - * @param rules Output param to receive the rules. - * @param pos the starting position. - * @param limit pointer past the last character of the rule. - * @return the index after the last character parsed. - */ - int32_t parseRule(const UnicodeString& rule, int32_t pos, int32_t limit, UErrorCode& status); - - /** - * Set the variable range to [start, end] (inclusive). - * @param start the start value of the range. - * @param end the end value of the range. - */ - void setVariableRange(int32_t start, int32_t end, UErrorCode& status); - - /** - * Assert that the given character is NOT within the variable range. - * If it is, return false. This is necessary to ensure that the - * variable range does not overlap characters used in a rule. - * @param ch the given character. - * @return True, if the given character is NOT within the variable range. - */ - UBool checkVariableRange(UChar32 ch) const; - - /** - * Set the maximum backup to 'backup', in response to a pragma - * statement. - * @param backup the new value to be set. - */ - void pragmaMaximumBackup(int32_t backup); - - /** - * Begin normalizing all rules using the given mode, in response - * to a pragma statement. - * @param mode the given mode. - */ - void pragmaNormalizeRules(UNormalizationMode mode); - - /** - * Return true if the given rule looks like a pragma. - * @param pos offset to the first non-whitespace character - * of the rule. - * @param limit pointer past the last character of the rule. - * @return true if the given rule looks like a pragma. - */ - static UBool resemblesPragma(const UnicodeString& rule, int32_t pos, int32_t limit); - - /** - * Parse a pragma. This method assumes resemblesPragma() has - * already returned true. - * @param pos offset to the first non-whitespace character - * of the rule. - * @param limit pointer past the last character of the rule. - * @return the position index after the final ';' of the pragma, - * or -1 on failure. - */ - int32_t parsePragma(const UnicodeString& rule, int32_t pos, int32_t limit, UErrorCode& status); - - /** - * Called by main parser upon syntax error. Search the rule string - * for the probable end of the rule. Of course, if the error is that - * the end of rule marker is missing, then the rule end will not be found. - * In any case the rule start will be correctly reported. - * @param parseErrorCode error code. - * @param msg error description. - * @param start position of first character of current rule. - * @return start position of first character of current rule. - */ - int32_t syntaxError(UErrorCode parseErrorCode, const UnicodeString&, int32_t start, - UErrorCode& status); - - /** - * Parse a UnicodeSet out, store it, and return the stand-in character - * used to represent it. - * - * @param rule the rule for UnicodeSet. - * @param pos the position in pattern at which to start parsing. - * @return the stand-in character used to represent it. - */ - UChar parseSet(const UnicodeString& rule, - ParsePosition& pos, - UErrorCode& status); - - /** - * Generate and return a stand-in for a new UnicodeFunctor. Store - * the matcher (adopt it). - * @param adopted the UnicodeFunctor to be adopted. - * @return a stand-in for a new UnicodeFunctor. - */ - UChar generateStandInFor(UnicodeFunctor* adopted, UErrorCode& status); - - /** - * Return the standin for segment seg (1-based). - * @param seg the given segment. - * @return the standIn character for the given segment. - */ - UChar getSegmentStandin(int32_t seg, UErrorCode& status); - - /** - * Set the object for segment seg (1-based). - * @param seg the given segment. - * @param adopted the StringMatcher to be adopted. - */ - void setSegmentObject(int32_t seg, StringMatcher* adopted, UErrorCode& status); - - /** - * Return the stand-in for the dot set. It is allocated the first - * time and reused thereafter. - * @return the stand-in for the dot set. - */ - UChar getDotStandIn(UErrorCode& status); - - /** - * Append the value of the given variable name to the given - * UnicodeString. - * @param name the variable name to be appended. - * @param buf the given UnicodeString to append to. - */ - void appendVariableDef(const UnicodeString& name, - UnicodeString& buf, - UErrorCode& status); - - /** - * Glue method to get around access restrictions in C++. - */ - /*static Transliterator* createBasicInstance(const UnicodeString& id, - const UnicodeString* canonID);*/ - - friend class RuleHalf; - - // Disallowed methods; no impl. - /** - * Copy constructor - */ - TransliteratorParser(const TransliteratorParser&); - - /** - * Assignment operator - */ - TransliteratorParser& operator=(const TransliteratorParser&); -}; - -U_NAMESPACE_END - -#endif /* #ifdef __cplusplus */ - -/** - * Strip/convert the following from the transliterator rules: - * comments - * newlines - * white space at the beginning and end of a line - * unescape \u notation - * - * The target must be equal in size as the source. - * @internal - */ -U_CAPI int32_t -utrans_stripRules(const UChar *source, int32_t sourceLen, UChar *target, UErrorCode *status); - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2011, International Business Machines Corporation +* and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/17/99 aliu Creation. +********************************************************************** +*/ +#ifndef RBT_PARS_H +#define RBT_PARS_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION +#ifdef __cplusplus + +#include "unicode/uobject.h" +#include "unicode/parseerr.h" +#include "unicode/unorm.h" +#include "rbt.h" +#include "hash.h" +#include "uvector.h" + +U_NAMESPACE_BEGIN + +class TransliterationRuleData; +class UnicodeFunctor; +class ParseData; +class RuleHalf; +class ParsePosition; +class StringMatcher; + +class TransliteratorParser : public UMemory { + + public: + + /** + * A Vector of TransliterationRuleData objects, one for each discrete group + * of rules in the rule set + */ + UVector dataVector; + + /** + * PUBLIC data member. + * A Vector of UnicodeStrings containing all of the ID blocks in the rule set + */ + UVector idBlockVector; + + /** + * PUBLIC data member containing the parsed compound filter, if any. + */ + UnicodeSet* compoundFilter; + + private: + + /** + * The current data object for which we are parsing rules + */ + TransliterationRuleData* curData; + + UTransDirection direction; + + /** + * Parse error information. + */ + UParseError parseError; + + /** + * Temporary symbol table used during parsing. + */ + ParseData* parseData; + + /** + * Temporary vector of matcher variables. When parsing is complete, this + * is copied into the array data.variables. As with data.variables, + * element 0 corresponds to character data.variablesBase. + */ + UVector variablesVector; + + /** + * Temporary table of variable names. When parsing is complete, this is + * copied into data.variableNames. + */ + Hashtable variableNames; + + /** + * String of standins for segments. Used during the parsing of a single + * rule. segmentStandins.charAt(0) is the standin for "$1" and corresponds + * to StringMatcher object segmentObjects.elementAt(0), etc. + */ + UnicodeString segmentStandins; + + /** + * Vector of StringMatcher objects for segments. Used during the + * parsing of a single rule. + * segmentStandins.charAt(0) is the standin for "$1" and corresponds + * to StringMatcher object segmentObjects.elementAt(0), etc. + */ + UVector segmentObjects; + + /** + * The next available stand-in for variables. This starts at some point in + * the private use area (discovered dynamically) and increments up toward + * variableLimit. At any point during parsing, available + * variables are variableNext..variableLimit-1. + */ + char16_t variableNext; + + /** + * The last available stand-in for variables. This is discovered + * dynamically. At any point during parsing, available variables are + * variableNext..variableLimit-1. + */ + char16_t variableLimit; + + /** + * When we encounter an undefined variable, we do not immediately signal + * an error, in case we are defining this variable, e.g., "$a = [a-z];". + * Instead, we save the name of the undefined variable, and substitute + * in the placeholder char variableLimit - 1, and decrement + * variableLimit. + */ + UnicodeString undefinedVariableName; + + /** + * The stand-in character for the 'dot' set, represented by '.' in + * patterns. This is allocated the first time it is needed, and + * reused thereafter. + */ + char16_t dotStandIn; + +public: + + /** + * Constructor. + */ + TransliteratorParser(UErrorCode &statusReturn); + + /** + * Destructor. + */ + ~TransliteratorParser(); + + /** + * Parse the given string as a sequence of rules, separated by newline + * characters ('\n'), and cause this object to implement those rules. Any + * previous rules are discarded. Typically this method is called exactly + * once after construction. + * + * Parse the given rules, in the given direction. After this call + * returns, query the public data members for results. The caller + * owns the 'data' and 'compoundFilter' data members after this + * call returns. + * @param rules rules, separated by ';' + * @param direction either FORWARD or REVERSE. + * @param pe Struct to receive information on position + * of error if an error is encountered + * @param ec Output param set to success/failure code. + */ + void parse(const UnicodeString& rules, + UTransDirection direction, + UParseError& pe, + UErrorCode& ec); + + /** + * Return the compound filter parsed by parse(). Caller owns result. + * @return the compound filter parsed by parse(). + */ + UnicodeSet* orphanCompoundFilter(); + +private: + + /** + * Return a representation of this transliterator as source rules. + * @param rules Output param to receive the rules. + * @param direction either FORWARD or REVERSE. + */ + void parseRules(const UnicodeString& rules, + UTransDirection direction, + UErrorCode& status); + + /** + * MAIN PARSER. Parse the next rule in the given rule string, starting + * at pos. Return the index after the last character parsed. Do not + * parse characters at or after limit. + * + * Important: The character at pos must be a non-whitespace character + * that is not the comment character. + * + * This method handles quoting, escaping, and whitespace removal. It + * parses the end-of-rule character. It recognizes context and cursor + * indicators. Once it does a lexical breakdown of the rule at pos, it + * creates a rule object and adds it to our rule list. + * @param rules Output param to receive the rules. + * @param pos the starting position. + * @param limit pointer past the last character of the rule. + * @return the index after the last character parsed. + */ + int32_t parseRule(const UnicodeString& rule, int32_t pos, int32_t limit, UErrorCode& status); + + /** + * Set the variable range to [start, end] (inclusive). + * @param start the start value of the range. + * @param end the end value of the range. + */ + void setVariableRange(int32_t start, int32_t end, UErrorCode& status); + + /** + * Assert that the given character is NOT within the variable range. + * If it is, return false. This is necessary to ensure that the + * variable range does not overlap characters used in a rule. + * @param ch the given character. + * @return True, if the given character is NOT within the variable range. + */ + UBool checkVariableRange(UChar32 ch) const; + + /** + * Set the maximum backup to 'backup', in response to a pragma + * statement. + * @param backup the new value to be set. + */ + void pragmaMaximumBackup(int32_t backup); + + /** + * Begin normalizing all rules using the given mode, in response + * to a pragma statement. + * @param mode the given mode. + */ + void pragmaNormalizeRules(UNormalizationMode mode); + + /** + * Return true if the given rule looks like a pragma. + * @param pos offset to the first non-whitespace character + * of the rule. + * @param limit pointer past the last character of the rule. + * @return true if the given rule looks like a pragma. + */ + static UBool resemblesPragma(const UnicodeString& rule, int32_t pos, int32_t limit); + + /** + * Parse a pragma. This method assumes resemblesPragma() has + * already returned true. + * @param pos offset to the first non-whitespace character + * of the rule. + * @param limit pointer past the last character of the rule. + * @return the position index after the final ';' of the pragma, + * or -1 on failure. + */ + int32_t parsePragma(const UnicodeString& rule, int32_t pos, int32_t limit, UErrorCode& status); + + /** + * Called by main parser upon syntax error. Search the rule string + * for the probable end of the rule. Of course, if the error is that + * the end of rule marker is missing, then the rule end will not be found. + * In any case the rule start will be correctly reported. + * @param parseErrorCode error code. + * @param msg error description. + * @param start position of first character of current rule. + * @return start position of first character of current rule. + */ + int32_t syntaxError(UErrorCode parseErrorCode, const UnicodeString&, int32_t start, + UErrorCode& status); + + /** + * Parse a UnicodeSet out, store it, and return the stand-in character + * used to represent it. + * + * @param rule the rule for UnicodeSet. + * @param pos the position in pattern at which to start parsing. + * @return the stand-in character used to represent it. + */ + char16_t parseSet(const UnicodeString& rule, + ParsePosition& pos, + UErrorCode& status); + + /** + * Generate and return a stand-in for a new UnicodeFunctor. Store + * the matcher (adopt it). + * @param adopted the UnicodeFunctor to be adopted. + * @return a stand-in for a new UnicodeFunctor. + */ + char16_t generateStandInFor(UnicodeFunctor* adopted, UErrorCode& status); + + /** + * Return the standin for segment seg (1-based). + * @param seg the given segment. + * @return the standIn character for the given segment. + */ + char16_t getSegmentStandin(int32_t seg, UErrorCode& status); + + /** + * Set the object for segment seg (1-based). + * @param seg the given segment. + * @param adopted the StringMatcher to be adopted. + */ + void setSegmentObject(int32_t seg, StringMatcher* adopted, UErrorCode& status); + + /** + * Return the stand-in for the dot set. It is allocated the first + * time and reused thereafter. + * @return the stand-in for the dot set. + */ + char16_t getDotStandIn(UErrorCode& status); + + /** + * Append the value of the given variable name to the given + * UnicodeString. + * @param name the variable name to be appended. + * @param buf the given UnicodeString to append to. + */ + void appendVariableDef(const UnicodeString& name, + UnicodeString& buf, + UErrorCode& status); + + /** + * Glue method to get around access restrictions in C++. + */ + /*static Transliterator* createBasicInstance(const UnicodeString& id, + const UnicodeString* canonID);*/ + + friend class RuleHalf; + + // Disallowed methods; no impl. + /** + * Copy constructor + */ + TransliteratorParser(const TransliteratorParser&); + + /** + * Assignment operator + */ + TransliteratorParser& operator=(const TransliteratorParser&); +}; + +U_NAMESPACE_END + +#endif /* #ifdef __cplusplus */ + +/** + * Strip/convert the following from the transliterator rules: + * comments + * newlines + * white space at the beginning and end of a line + * unescape \u notation + * + * The target must be equal in size as the source. + * @internal + */ +U_CAPI int32_t +utrans_stripRules(const UChar *source, int32_t sourceLen, UChar *target, UErrorCode *status); + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/rbt_rule.cpp b/deps/icu-small/source/i18n/rbt_rule.cpp index ee0d938ca95aa7..2e07abd852fb56 100644 --- a/deps/icu-small/source/i18n/rbt_rule.cpp +++ b/deps/icu-small/source/i18n/rbt_rule.cpp @@ -1,559 +1,559 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 1999-2011, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - * Date Name Description - * 11/17/99 aliu Creation. - ********************************************************************** - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/rep.h" -#include "unicode/unifilt.h" -#include "unicode/uniset.h" -#include "unicode/utf16.h" -#include "rbt_rule.h" -#include "rbt_data.h" -#include "cmemory.h" -#include "strmatch.h" -#include "strrepl.h" -#include "util.h" -#include "putilimp.h" - -static const UChar FORWARD_OP[] = {32,62,32,0}; // " > " - -U_NAMESPACE_BEGIN - -/** - * Construct a new rule with the given input, output text, and other - * attributes. A cursor position may be specified for the output text. - * @param input input string, including key and optional ante and - * post context - * @param anteContextPos offset into input to end of ante context, or -1 if - * none. Must be <= input.length() if not -1. - * @param postContextPos offset into input to start of post context, or -1 - * if none. Must be <= input.length() if not -1, and must be >= - * anteContextPos. - * @param output output string - * @param cursorPosition offset into output at which cursor is located, or -1 if - * none. If less than zero, then the cursor is placed after the - * output; that is, -1 is equivalent to - * output.length(). If greater than - * output.length() then an exception is thrown. - * @param segs array of UnicodeFunctors corresponding to input pattern - * segments, or null if there are none. The array itself is adopted, - * but the pointers within it are not. - * @param segsCount number of elements in segs[] - * @param anchorStart true if the the rule is anchored on the left to - * the context start - * @param anchorEnd true if the rule is anchored on the right to the - * context limit - */ -TransliterationRule::TransliterationRule(const UnicodeString& input, - int32_t anteContextPos, int32_t postContextPos, - const UnicodeString& outputStr, - int32_t cursorPosition, int32_t cursorOffset, - UnicodeFunctor** segs, - int32_t segsCount, - UBool anchorStart, UBool anchorEnd, - const TransliterationRuleData* theData, - UErrorCode& status) : - UMemory(), - segments(0), - data(theData) { - - if (U_FAILURE(status)) { - return; - } - // Do range checks only when warranted to save time - if (anteContextPos < 0) { - anteContextLength = 0; - } else { - if (anteContextPos > input.length()) { - // throw new IllegalArgumentException("Invalid ante context"); - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - anteContextLength = anteContextPos; - } - if (postContextPos < 0) { - keyLength = input.length() - anteContextLength; - } else { - if (postContextPos < anteContextLength || - postContextPos > input.length()) { - // throw new IllegalArgumentException("Invalid post context"); - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - keyLength = postContextPos - anteContextLength; - } - if (cursorPosition < 0) { - cursorPosition = outputStr.length(); - } else if (cursorPosition > outputStr.length()) { - // throw new IllegalArgumentException("Invalid cursor position"); - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - // We don't validate the segments array. The caller must - // guarantee that the segments are well-formed (that is, that - // all $n references in the output refer to indices of this - // array, and that no array elements are null). - this->segments = segs; - this->segmentsCount = segsCount; - - pattern = input; - flags = 0; - if (anchorStart) { - flags |= ANCHOR_START; - } - if (anchorEnd) { - flags |= ANCHOR_END; - } - - anteContext = NULL; - if (anteContextLength > 0) { - anteContext = new StringMatcher(pattern, 0, anteContextLength, - false, *data); - /* test for NULL */ - if (anteContext == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - - key = NULL; - if (keyLength > 0) { - key = new StringMatcher(pattern, anteContextLength, anteContextLength + keyLength, - false, *data); - /* test for NULL */ - if (key == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - - int32_t postContextLength = pattern.length() - keyLength - anteContextLength; - postContext = NULL; - if (postContextLength > 0) { - postContext = new StringMatcher(pattern, anteContextLength + keyLength, pattern.length(), - false, *data); - /* test for NULL */ - if (postContext == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - - this->output = new StringReplacer(outputStr, cursorPosition + cursorOffset, data); - /* test for NULL */ - if (this->output == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } -} - -/** - * Copy constructor. - */ -TransliterationRule::TransliterationRule(TransliterationRule& other) : - UMemory(other), - anteContext(NULL), - key(NULL), - postContext(NULL), - pattern(other.pattern), - anteContextLength(other.anteContextLength), - keyLength(other.keyLength), - flags(other.flags), - data(other.data) { - - segments = NULL; - segmentsCount = 0; - if (other.segmentsCount > 0) { - segments = (UnicodeFunctor **)uprv_malloc(other.segmentsCount * sizeof(UnicodeFunctor *)); - uprv_memcpy(segments, other.segments, (size_t)other.segmentsCount*sizeof(segments[0])); - } - - if (other.anteContext != NULL) { - anteContext = other.anteContext->clone(); - } - if (other.key != NULL) { - key = other.key->clone(); - } - if (other.postContext != NULL) { - postContext = other.postContext->clone(); - } - output = other.output->clone(); -} - -TransliterationRule::~TransliterationRule() { - uprv_free(segments); - delete anteContext; - delete key; - delete postContext; - delete output; -} - -/** - * Return the preceding context length. This method is needed to - * support the Transliterator method - * getMaximumContextLength(). Internally, this is - * implemented as the anteContextLength, optionally plus one if - * there is a start anchor. The one character anchor gap is - * needed to make repeated incremental transliteration with - * anchors work. - */ -int32_t TransliterationRule::getContextLength(void) const { - return anteContextLength + ((flags & ANCHOR_START) ? 1 : 0); -} - -/** - * Internal method. Returns 8-bit index value for this rule. - * This is the low byte of the first character of the key, - * unless the first character of the key is a set. If it's a - * set, or otherwise can match multiple keys, the index value is -1. - */ -int16_t TransliterationRule::getIndexValue() const { - if (anteContextLength == pattern.length()) { - // A pattern with just ante context {such as foo)>bar} can - // match any key. - return -1; - } - UChar32 c = pattern.char32At(anteContextLength); - return (int16_t)(data->lookupMatcher(c) == NULL ? (c & 0xFF) : -1); -} - -/** - * Internal method. Returns true if this rule matches the given - * index value. The index value is an 8-bit integer, 0..255, - * representing the low byte of the first character of the key. - * It matches this rule if it matches the first character of the - * key, or if the first character of the key is a set, and the set - * contains any character with a low byte equal to the index - * value. If the rule contains only ante context, as in foo)>bar, - * then it will match any key. - */ -UBool TransliterationRule::matchesIndexValue(uint8_t v) const { - // Delegate to the key, or if there is none, to the postContext. - // If there is neither then we match any key; return true. - UnicodeMatcher *m = (key != NULL) ? key : postContext; - return (m != NULL) ? m->matchesIndexValue(v) : true; -} - -/** - * Return true if this rule masks another rule. If r1 masks r2 then - * r1 matches any input string that r2 matches. If r1 masks r2 and r2 masks - * r1 then r1 == r2. Examples: "a>x" masks "ab>y". "a>x" masks "a[b]>y". - * "[c]a>x" masks "[dc]a>y". - */ -UBool TransliterationRule::masks(const TransliterationRule& r2) const { - /* Rule r1 masks rule r2 if the string formed of the - * antecontext, key, and postcontext overlaps in the following - * way: - * - * r1: aakkkpppp - * r2: aaakkkkkpppp - * ^ - * - * The strings must be aligned at the first character of the - * key. The length of r1 to the left of the alignment point - * must be <= the length of r2 to the left; ditto for the - * right. The characters of r1 must equal (or be a superset - * of) the corresponding characters of r2. The superset - * operation should be performed to check for UnicodeSet - * masking. - * - * Anchors: Two patterns that differ only in anchors only - * mask one another if they are exactly equal, and r2 has - * all the anchors r1 has (optionally, plus some). Here Y - * means the row masks the column, N means it doesn't. - * - * ab ^ab ab$ ^ab$ - * ab Y Y Y Y - * ^ab N Y N Y - * ab$ N N Y Y - * ^ab$ N N N Y - * - * Post context: {a}b masks ab, but not vice versa, since {a}b - * matches everything ab matches, and {a}b matches {|a|}b but ab - * does not. Pre context is different (a{b} does not align with - * ab). - */ - - /* LIMITATION of the current mask algorithm: Some rule - * maskings are currently not detected. For example, - * "{Lu}]a>x" masks "A]a>y". This can be added later. TODO - */ - - int32_t len = pattern.length(); - int32_t left = anteContextLength; - int32_t left2 = r2.anteContextLength; - int32_t right = len - left; - int32_t right2 = r2.pattern.length() - left2; - int32_t cachedCompare = r2.pattern.compare(left2 - left, len, pattern); - - // TODO Clean this up -- some logic might be combinable with the - // next statement. - - // Test for anchor masking - if (left == left2 && right == right2 && - keyLength <= r2.keyLength && - 0 == cachedCompare) { - // The following boolean logic implements the table above - return (flags == r2.flags) || - (!(flags & ANCHOR_START) && !(flags & ANCHOR_END)) || - ((r2.flags & ANCHOR_START) && (r2.flags & ANCHOR_END)); - } - - return left <= left2 && - (right < right2 || - (right == right2 && keyLength <= r2.keyLength)) && - (0 == cachedCompare); -} - -static inline int32_t posBefore(const Replaceable& str, int32_t pos) { - return (pos > 0) ? - pos - U16_LENGTH(str.char32At(pos-1)) : - pos - 1; -} - -static inline int32_t posAfter(const Replaceable& str, int32_t pos) { - return (pos >= 0 && pos < str.length()) ? - pos + U16_LENGTH(str.char32At(pos)) : - pos + 1; -} - -/** - * Attempt a match and replacement at the given position. Return - * the degree of match between this rule and the given text. The - * degree of match may be mismatch, a partial match, or a full - * match. A mismatch means at least one character of the text - * does not match the context or key. A partial match means some - * context and key characters match, but the text is not long - * enough to match all of them. A full match means all context - * and key characters match. - * - * If a full match is obtained, perform a replacement, update pos, - * and return U_MATCH. Otherwise both text and pos are unchanged. - * - * @param text the text - * @param pos the position indices - * @param incremental if true, test for partial matches that may - * be completed by additional text inserted at pos.limit. - * @return one of U_MISMATCH, - * U_PARTIAL_MATCH, or U_MATCH. If - * incremental is false then U_PARTIAL_MATCH will not be returned. - */ -UMatchDegree TransliterationRule::matchAndReplace(Replaceable& text, - UTransPosition& pos, - UBool incremental) const { - // Matching and replacing are done in one method because the - // replacement operation needs information obtained during the - // match. Another way to do this is to have the match method - // create a match result struct with relevant offsets, and to pass - // this into the replace method. - - // ============================ MATCH =========================== - - // Reset segment match data - if (segments != NULL) { - for (int32_t i=0; iresetMatch(); - } - } - -// int32_t lenDelta, keyLimit; - int32_t keyLimit; - - // ------------------------ Ante Context ------------------------ - - // A mismatch in the ante context, or with the start anchor, - // is an outright U_MISMATCH regardless of whether we are - // incremental or not. - int32_t oText; // offset into 'text' -// int32_t newStart = 0; - int32_t minOText; - - // Note (1): We process text in 16-bit code units, rather than - // 32-bit code points. This works because stand-ins are - // always in the BMP and because we are doing a literal match - // operation, which can be done 16-bits at a time. - - int32_t anteLimit = posBefore(text, pos.contextStart); - - UMatchDegree match; - - // Start reverse match at char before pos.start - oText = posBefore(text, pos.start); - - if (anteContext != NULL) { - match = anteContext->matches(text, oText, anteLimit, false); - if (match != U_MATCH) { - return U_MISMATCH; - } - } - - minOText = posAfter(text, oText); - - // ------------------------ Start Anchor ------------------------ - - if (((flags & ANCHOR_START) != 0) && oText != anteLimit) { - return U_MISMATCH; - } - - // -------------------- Key and Post Context -------------------- - - oText = pos.start; - - if (key != NULL) { - match = key->matches(text, oText, pos.limit, incremental); - if (match != U_MATCH) { - return match; - } - } - - keyLimit = oText; - - if (postContext != NULL) { - if (incremental && keyLimit == pos.limit) { - // The key matches just before pos.limit, and there is - // a postContext. Since we are in incremental mode, - // we must assume more characters may be inserted at - // pos.limit -- this is a partial match. - return U_PARTIAL_MATCH; - } - - match = postContext->matches(text, oText, pos.contextLimit, incremental); - if (match != U_MATCH) { - return match; - } - } - - // ------------------------- Stop Anchor ------------------------ - - if (((flags & ANCHOR_END)) != 0) { - if (oText != pos.contextLimit) { - return U_MISMATCH; - } - if (incremental) { - return U_PARTIAL_MATCH; - } - } - - // =========================== REPLACE ========================== - - // We have a full match. The key is between pos.start and - // keyLimit. - - int32_t newStart; - int32_t newLength = output->toReplacer()->replace(text, pos.start, keyLimit, newStart); - int32_t lenDelta = newLength - (keyLimit - pos.start); - - oText += lenDelta; - pos.limit += lenDelta; - pos.contextLimit += lenDelta; - // Restrict new value of start to [minOText, min(oText, pos.limit)]. - pos.start = uprv_max(minOText, uprv_min(uprv_min(oText, pos.limit), newStart)); - return U_MATCH; -} - -/** - * Create a source string that represents this rule. Append it to the - * given string. - */ -UnicodeString& TransliterationRule::toRule(UnicodeString& rule, - UBool escapeUnprintable) const { - - // Accumulate special characters (and non-specials following them) - // into quoteBuf. Append quoteBuf, within single quotes, when - // a non-quoted element must be inserted. - UnicodeString str, quoteBuf; - - // Do not emit the braces '{' '}' around the pattern if there - // is neither anteContext nor postContext. - UBool emitBraces = - (anteContext != NULL) || (postContext != NULL); - - // Emit start anchor - if ((flags & ANCHOR_START) != 0) { - rule.append((UChar)94/*^*/); - } - - // Emit the input pattern - ICU_Utility::appendToRule(rule, anteContext, escapeUnprintable, quoteBuf); - - if (emitBraces) { - ICU_Utility::appendToRule(rule, (UChar) 0x007B /*{*/, true, escapeUnprintable, quoteBuf); - } - - ICU_Utility::appendToRule(rule, key, escapeUnprintable, quoteBuf); - - if (emitBraces) { - ICU_Utility::appendToRule(rule, (UChar) 0x007D /*}*/, true, escapeUnprintable, quoteBuf); - } - - ICU_Utility::appendToRule(rule, postContext, escapeUnprintable, quoteBuf); - - // Emit end anchor - if ((flags & ANCHOR_END) != 0) { - rule.append((UChar)36/*$*/); - } - - ICU_Utility::appendToRule(rule, UnicodeString(true, FORWARD_OP, 3), true, escapeUnprintable, quoteBuf); - - // Emit the output pattern - - ICU_Utility::appendToRule(rule, output->toReplacer()->toReplacerPattern(str, escapeUnprintable), - true, escapeUnprintable, quoteBuf); - - ICU_Utility::appendToRule(rule, (UChar) 0x003B /*;*/, true, escapeUnprintable, quoteBuf); - - return rule; -} - -void TransliterationRule::setData(const TransliterationRuleData* d) { - data = d; - if (anteContext != NULL) anteContext->setData(d); - if (postContext != NULL) postContext->setData(d); - if (key != NULL) key->setData(d); - // assert(output != NULL); - output->setData(d); - // Don't have to do segments since they are in the context or key -} - -/** - * Union the set of all characters that may be modified by this rule - * into the given set. - */ -void TransliterationRule::addSourceSetTo(UnicodeSet& toUnionTo) const { - int32_t limit = anteContextLength + keyLength; - for (int32_t i=anteContextLength; ilookupMatcher(ch); - if (matcher == NULL) { - toUnionTo.add(ch); - } else { - matcher->addMatchSetTo(toUnionTo); - } - } -} - -/** - * Union the set of all characters that may be emitted by this rule - * into the given set. - */ -void TransliterationRule::addTargetSetTo(UnicodeSet& toUnionTo) const { - output->toReplacer()->addReplacementSetTo(toUnionTo); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 1999-2011, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + * Date Name Description + * 11/17/99 aliu Creation. + ********************************************************************** + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/rep.h" +#include "unicode/unifilt.h" +#include "unicode/uniset.h" +#include "unicode/utf16.h" +#include "rbt_rule.h" +#include "rbt_data.h" +#include "cmemory.h" +#include "strmatch.h" +#include "strrepl.h" +#include "util.h" +#include "putilimp.h" + +static const char16_t FORWARD_OP[] = {32,62,32,0}; // " > " + +U_NAMESPACE_BEGIN + +/** + * Construct a new rule with the given input, output text, and other + * attributes. A cursor position may be specified for the output text. + * @param input input string, including key and optional ante and + * post context + * @param anteContextPos offset into input to end of ante context, or -1 if + * none. Must be <= input.length() if not -1. + * @param postContextPos offset into input to start of post context, or -1 + * if none. Must be <= input.length() if not -1, and must be >= + * anteContextPos. + * @param output output string + * @param cursorPosition offset into output at which cursor is located, or -1 if + * none. If less than zero, then the cursor is placed after the + * output; that is, -1 is equivalent to + * output.length(). If greater than + * output.length() then an exception is thrown. + * @param segs array of UnicodeFunctors corresponding to input pattern + * segments, or null if there are none. The array itself is adopted, + * but the pointers within it are not. + * @param segsCount number of elements in segs[] + * @param anchorStart true if the the rule is anchored on the left to + * the context start + * @param anchorEnd true if the rule is anchored on the right to the + * context limit + */ +TransliterationRule::TransliterationRule(const UnicodeString& input, + int32_t anteContextPos, int32_t postContextPos, + const UnicodeString& outputStr, + int32_t cursorPosition, int32_t cursorOffset, + UnicodeFunctor** segs, + int32_t segsCount, + UBool anchorStart, UBool anchorEnd, + const TransliterationRuleData* theData, + UErrorCode& status) : + UMemory(), + segments(0), + data(theData) { + + if (U_FAILURE(status)) { + return; + } + // Do range checks only when warranted to save time + if (anteContextPos < 0) { + anteContextLength = 0; + } else { + if (anteContextPos > input.length()) { + // throw new IllegalArgumentException("Invalid ante context"); + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + anteContextLength = anteContextPos; + } + if (postContextPos < 0) { + keyLength = input.length() - anteContextLength; + } else { + if (postContextPos < anteContextLength || + postContextPos > input.length()) { + // throw new IllegalArgumentException("Invalid post context"); + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + keyLength = postContextPos - anteContextLength; + } + if (cursorPosition < 0) { + cursorPosition = outputStr.length(); + } else if (cursorPosition > outputStr.length()) { + // throw new IllegalArgumentException("Invalid cursor position"); + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + // We don't validate the segments array. The caller must + // guarantee that the segments are well-formed (that is, that + // all $n references in the output refer to indices of this + // array, and that no array elements are null). + this->segments = segs; + this->segmentsCount = segsCount; + + pattern = input; + flags = 0; + if (anchorStart) { + flags |= ANCHOR_START; + } + if (anchorEnd) { + flags |= ANCHOR_END; + } + + anteContext = nullptr; + if (anteContextLength > 0) { + anteContext = new StringMatcher(pattern, 0, anteContextLength, + false, *data); + /* test for nullptr */ + if (anteContext == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + + key = nullptr; + if (keyLength > 0) { + key = new StringMatcher(pattern, anteContextLength, anteContextLength + keyLength, + false, *data); + /* test for nullptr */ + if (key == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + + int32_t postContextLength = pattern.length() - keyLength - anteContextLength; + postContext = nullptr; + if (postContextLength > 0) { + postContext = new StringMatcher(pattern, anteContextLength + keyLength, pattern.length(), + false, *data); + /* test for nullptr */ + if (postContext == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + + this->output = new StringReplacer(outputStr, cursorPosition + cursorOffset, data); + /* test for nullptr */ + if (this->output == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } +} + +/** + * Copy constructor. + */ +TransliterationRule::TransliterationRule(TransliterationRule& other) : + UMemory(other), + anteContext(nullptr), + key(nullptr), + postContext(nullptr), + pattern(other.pattern), + anteContextLength(other.anteContextLength), + keyLength(other.keyLength), + flags(other.flags), + data(other.data) { + + segments = nullptr; + segmentsCount = 0; + if (other.segmentsCount > 0) { + segments = (UnicodeFunctor **)uprv_malloc(other.segmentsCount * sizeof(UnicodeFunctor *)); + uprv_memcpy(segments, other.segments, (size_t)other.segmentsCount*sizeof(segments[0])); + } + + if (other.anteContext != nullptr) { + anteContext = other.anteContext->clone(); + } + if (other.key != nullptr) { + key = other.key->clone(); + } + if (other.postContext != nullptr) { + postContext = other.postContext->clone(); + } + output = other.output->clone(); +} + +TransliterationRule::~TransliterationRule() { + uprv_free(segments); + delete anteContext; + delete key; + delete postContext; + delete output; +} + +/** + * Return the preceding context length. This method is needed to + * support the Transliterator method + * getMaximumContextLength(). Internally, this is + * implemented as the anteContextLength, optionally plus one if + * there is a start anchor. The one character anchor gap is + * needed to make repeated incremental transliteration with + * anchors work. + */ +int32_t TransliterationRule::getContextLength() const { + return anteContextLength + ((flags & ANCHOR_START) ? 1 : 0); +} + +/** + * Internal method. Returns 8-bit index value for this rule. + * This is the low byte of the first character of the key, + * unless the first character of the key is a set. If it's a + * set, or otherwise can match multiple keys, the index value is -1. + */ +int16_t TransliterationRule::getIndexValue() const { + if (anteContextLength == pattern.length()) { + // A pattern with just ante context {such as foo)>bar} can + // match any key. + return -1; + } + UChar32 c = pattern.char32At(anteContextLength); + return (int16_t)(data->lookupMatcher(c) == nullptr ? (c & 0xFF) : -1); +} + +/** + * Internal method. Returns true if this rule matches the given + * index value. The index value is an 8-bit integer, 0..255, + * representing the low byte of the first character of the key. + * It matches this rule if it matches the first character of the + * key, or if the first character of the key is a set, and the set + * contains any character with a low byte equal to the index + * value. If the rule contains only ante context, as in foo)>bar, + * then it will match any key. + */ +UBool TransliterationRule::matchesIndexValue(uint8_t v) const { + // Delegate to the key, or if there is none, to the postContext. + // If there is neither then we match any key; return true. + UnicodeMatcher *m = (key != nullptr) ? key : postContext; + return (m != nullptr) ? m->matchesIndexValue(v) : true; +} + +/** + * Return true if this rule masks another rule. If r1 masks r2 then + * r1 matches any input string that r2 matches. If r1 masks r2 and r2 masks + * r1 then r1 == r2. Examples: "a>x" masks "ab>y". "a>x" masks "a[b]>y". + * "[c]a>x" masks "[dc]a>y". + */ +UBool TransliterationRule::masks(const TransliterationRule& r2) const { + /* Rule r1 masks rule r2 if the string formed of the + * antecontext, key, and postcontext overlaps in the following + * way: + * + * r1: aakkkpppp + * r2: aaakkkkkpppp + * ^ + * + * The strings must be aligned at the first character of the + * key. The length of r1 to the left of the alignment point + * must be <= the length of r2 to the left; ditto for the + * right. The characters of r1 must equal (or be a superset + * of) the corresponding characters of r2. The superset + * operation should be performed to check for UnicodeSet + * masking. + * + * Anchors: Two patterns that differ only in anchors only + * mask one another if they are exactly equal, and r2 has + * all the anchors r1 has (optionally, plus some). Here Y + * means the row masks the column, N means it doesn't. + * + * ab ^ab ab$ ^ab$ + * ab Y Y Y Y + * ^ab N Y N Y + * ab$ N N Y Y + * ^ab$ N N N Y + * + * Post context: {a}b masks ab, but not vice versa, since {a}b + * matches everything ab matches, and {a}b matches {|a|}b but ab + * does not. Pre context is different (a{b} does not align with + * ab). + */ + + /* LIMITATION of the current mask algorithm: Some rule + * maskings are currently not detected. For example, + * "{Lu}]a>x" masks "A]a>y". This can be added later. TODO + */ + + int32_t len = pattern.length(); + int32_t left = anteContextLength; + int32_t left2 = r2.anteContextLength; + int32_t right = len - left; + int32_t right2 = r2.pattern.length() - left2; + int32_t cachedCompare = r2.pattern.compare(left2 - left, len, pattern); + + // TODO Clean this up -- some logic might be combinable with the + // next statement. + + // Test for anchor masking + if (left == left2 && right == right2 && + keyLength <= r2.keyLength && + 0 == cachedCompare) { + // The following boolean logic implements the table above + return (flags == r2.flags) || + (!(flags & ANCHOR_START) && !(flags & ANCHOR_END)) || + ((r2.flags & ANCHOR_START) && (r2.flags & ANCHOR_END)); + } + + return left <= left2 && + (right < right2 || + (right == right2 && keyLength <= r2.keyLength)) && + (0 == cachedCompare); +} + +static inline int32_t posBefore(const Replaceable& str, int32_t pos) { + return (pos > 0) ? + pos - U16_LENGTH(str.char32At(pos-1)) : + pos - 1; +} + +static inline int32_t posAfter(const Replaceable& str, int32_t pos) { + return (pos >= 0 && pos < str.length()) ? + pos + U16_LENGTH(str.char32At(pos)) : + pos + 1; +} + +/** + * Attempt a match and replacement at the given position. Return + * the degree of match between this rule and the given text. The + * degree of match may be mismatch, a partial match, or a full + * match. A mismatch means at least one character of the text + * does not match the context or key. A partial match means some + * context and key characters match, but the text is not long + * enough to match all of them. A full match means all context + * and key characters match. + * + * If a full match is obtained, perform a replacement, update pos, + * and return U_MATCH. Otherwise both text and pos are unchanged. + * + * @param text the text + * @param pos the position indices + * @param incremental if true, test for partial matches that may + * be completed by additional text inserted at pos.limit. + * @return one of U_MISMATCH, + * U_PARTIAL_MATCH, or U_MATCH. If + * incremental is false then U_PARTIAL_MATCH will not be returned. + */ +UMatchDegree TransliterationRule::matchAndReplace(Replaceable& text, + UTransPosition& pos, + UBool incremental) const { + // Matching and replacing are done in one method because the + // replacement operation needs information obtained during the + // match. Another way to do this is to have the match method + // create a match result struct with relevant offsets, and to pass + // this into the replace method. + + // ============================ MATCH =========================== + + // Reset segment match data + if (segments != nullptr) { + for (int32_t i=0; iresetMatch(); + } + } + +// int32_t lenDelta, keyLimit; + int32_t keyLimit; + + // ------------------------ Ante Context ------------------------ + + // A mismatch in the ante context, or with the start anchor, + // is an outright U_MISMATCH regardless of whether we are + // incremental or not. + int32_t oText; // offset into 'text' +// int32_t newStart = 0; + int32_t minOText; + + // Note (1): We process text in 16-bit code units, rather than + // 32-bit code points. This works because stand-ins are + // always in the BMP and because we are doing a literal match + // operation, which can be done 16-bits at a time. + + int32_t anteLimit = posBefore(text, pos.contextStart); + + UMatchDegree match; + + // Start reverse match at char before pos.start + oText = posBefore(text, pos.start); + + if (anteContext != nullptr) { + match = anteContext->matches(text, oText, anteLimit, false); + if (match != U_MATCH) { + return U_MISMATCH; + } + } + + minOText = posAfter(text, oText); + + // ------------------------ Start Anchor ------------------------ + + if (((flags & ANCHOR_START) != 0) && oText != anteLimit) { + return U_MISMATCH; + } + + // -------------------- Key and Post Context -------------------- + + oText = pos.start; + + if (key != nullptr) { + match = key->matches(text, oText, pos.limit, incremental); + if (match != U_MATCH) { + return match; + } + } + + keyLimit = oText; + + if (postContext != nullptr) { + if (incremental && keyLimit == pos.limit) { + // The key matches just before pos.limit, and there is + // a postContext. Since we are in incremental mode, + // we must assume more characters may be inserted at + // pos.limit -- this is a partial match. + return U_PARTIAL_MATCH; + } + + match = postContext->matches(text, oText, pos.contextLimit, incremental); + if (match != U_MATCH) { + return match; + } + } + + // ------------------------- Stop Anchor ------------------------ + + if (((flags & ANCHOR_END)) != 0) { + if (oText != pos.contextLimit) { + return U_MISMATCH; + } + if (incremental) { + return U_PARTIAL_MATCH; + } + } + + // =========================== REPLACE ========================== + + // We have a full match. The key is between pos.start and + // keyLimit. + + int32_t newStart; + int32_t newLength = output->toReplacer()->replace(text, pos.start, keyLimit, newStart); + int32_t lenDelta = newLength - (keyLimit - pos.start); + + oText += lenDelta; + pos.limit += lenDelta; + pos.contextLimit += lenDelta; + // Restrict new value of start to [minOText, min(oText, pos.limit)]. + pos.start = uprv_max(minOText, uprv_min(uprv_min(oText, pos.limit), newStart)); + return U_MATCH; +} + +/** + * Create a source string that represents this rule. Append it to the + * given string. + */ +UnicodeString& TransliterationRule::toRule(UnicodeString& rule, + UBool escapeUnprintable) const { + + // Accumulate special characters (and non-specials following them) + // into quoteBuf. Append quoteBuf, within single quotes, when + // a non-quoted element must be inserted. + UnicodeString str, quoteBuf; + + // Do not emit the braces '{' '}' around the pattern if there + // is neither anteContext nor postContext. + UBool emitBraces = + (anteContext != nullptr) || (postContext != nullptr); + + // Emit start anchor + if ((flags & ANCHOR_START) != 0) { + rule.append((char16_t)94/*^*/); + } + + // Emit the input pattern + ICU_Utility::appendToRule(rule, anteContext, escapeUnprintable, quoteBuf); + + if (emitBraces) { + ICU_Utility::appendToRule(rule, (char16_t) 0x007B /*{*/, true, escapeUnprintable, quoteBuf); + } + + ICU_Utility::appendToRule(rule, key, escapeUnprintable, quoteBuf); + + if (emitBraces) { + ICU_Utility::appendToRule(rule, (char16_t) 0x007D /*}*/, true, escapeUnprintable, quoteBuf); + } + + ICU_Utility::appendToRule(rule, postContext, escapeUnprintable, quoteBuf); + + // Emit end anchor + if ((flags & ANCHOR_END) != 0) { + rule.append((char16_t)36/*$*/); + } + + ICU_Utility::appendToRule(rule, UnicodeString(true, FORWARD_OP, 3), true, escapeUnprintable, quoteBuf); + + // Emit the output pattern + + ICU_Utility::appendToRule(rule, output->toReplacer()->toReplacerPattern(str, escapeUnprintable), + true, escapeUnprintable, quoteBuf); + + ICU_Utility::appendToRule(rule, (char16_t) 0x003B /*;*/, true, escapeUnprintable, quoteBuf); + + return rule; +} + +void TransliterationRule::setData(const TransliterationRuleData* d) { + data = d; + if (anteContext != nullptr) anteContext->setData(d); + if (postContext != nullptr) postContext->setData(d); + if (key != nullptr) key->setData(d); + // assert(output != nullptr); + output->setData(d); + // Don't have to do segments since they are in the context or key +} + +/** + * Union the set of all characters that may be modified by this rule + * into the given set. + */ +void TransliterationRule::addSourceSetTo(UnicodeSet& toUnionTo) const { + int32_t limit = anteContextLength + keyLength; + for (int32_t i=anteContextLength; ilookupMatcher(ch); + if (matcher == nullptr) { + toUnionTo.add(ch); + } else { + matcher->addMatchSetTo(toUnionTo); + } + } +} + +/** + * Union the set of all characters that may be emitted by this rule + * into the given set. + */ +void TransliterationRule::addTargetSetTo(UnicodeSet& toUnionTo) const { + output->toReplacer()->addReplacementSetTo(toUnionTo); +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +//eof diff --git a/deps/icu-small/source/i18n/rbt_rule.h b/deps/icu-small/source/i18n/rbt_rule.h index b927f5d6c05c11..43617349031008 100644 --- a/deps/icu-small/source/i18n/rbt_rule.h +++ b/deps/icu-small/source/i18n/rbt_rule.h @@ -1,310 +1,310 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -* Copyright (C) {1999-2001}, International Business Machines Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/17/99 aliu Creation. -********************************************************************** -*/ -#ifndef RBT_RULE_H -#define RBT_RULE_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/uobject.h" -#include "unicode/unistr.h" -#include "unicode/utrans.h" -#include "unicode/unimatch.h" - -U_NAMESPACE_BEGIN - -class Replaceable; -class TransliterationRuleData; -class StringMatcher; -class UnicodeFunctor; - -/** - * A transliteration rule used by - * RuleBasedTransliterator. - * TransliterationRule is an immutable object. - * - *

      A rule consists of an input pattern and an output string. When - * the input pattern is matched, the output string is emitted. The - * input pattern consists of zero or more characters which are matched - * exactly (the key) and optional context. Context must match if it - * is specified. Context may be specified before the key, after the - * key, or both. The key, preceding context, and following context - * may contain variables. Variables represent a set of Unicode - * characters, such as the letters a through z. - * Variables are detected by looking up each character in a supplied - * variable list to see if it has been so defined. - * - *

      A rule may contain segments in its input string and segment - * references in its output string. A segment is a substring of the - * input pattern, indicated by an offset and limit. The segment may - * be in the preceding or following context. It may not span a - * context boundary. A segment reference is a special character in - * the output string that causes a segment of the input string (not - * the input pattern) to be copied to the output string. The range of - * special characters that represent segment references is defined by - * RuleBasedTransliterator.Data. - * - * @author Alan Liu - */ -class TransliterationRule : public UMemory { - -private: - - // TODO Eliminate the pattern and keyLength data members. They - // are used only by masks() and getIndexValue() which are called - // only during build time, not during run-time. Perhaps these - // methods and pattern/keyLength can be isolated into a separate - // object. - - /** - * The match that must occur before the key, or null if there is no - * preceding context. - */ - StringMatcher *anteContext; - - /** - * The matcher object for the key. If null, then the key is empty. - */ - StringMatcher *key; - - /** - * The match that must occur after the key, or null if there is no - * following context. - */ - StringMatcher *postContext; - - /** - * The object that performs the replacement if the key, - * anteContext, and postContext are matched. Never null. - */ - UnicodeFunctor* output; - - /** - * The string that must be matched, consisting of the anteContext, key, - * and postContext, concatenated together, in that order. Some components - * may be empty (zero length). - * @see anteContextLength - * @see keyLength - */ - UnicodeString pattern; - - /** - * An array of matcher objects corresponding to the input pattern - * segments. If there are no segments this is null. N.B. This is - * a UnicodeMatcher for generality, but in practice it is always a - * StringMatcher. In the future we may generalize this, but for - * now we sometimes cast down to StringMatcher. - * - * The array is owned, but the pointers within it are not. - */ - UnicodeFunctor** segments; - - /** - * The number of elements in segments[] or zero if segments is NULL. - */ - int32_t segmentsCount; - - /** - * The length of the string that must match before the key. If - * zero, then there is no matching requirement before the key. - * Substring [0,anteContextLength) of pattern is the anteContext. - */ - int32_t anteContextLength; - - /** - * The length of the key. Substring [anteContextLength, - * anteContextLength + keyLength) is the key. - - */ - int32_t keyLength; - - /** - * Miscellaneous attributes. - */ - int8_t flags; - - /** - * Flag attributes. - */ - enum { - ANCHOR_START = 1, - ANCHOR_END = 2 - }; - - /** - * An alias pointer to the data for this rule. The data provides - * lookup services for matchers and segments. - */ - const TransliterationRuleData* data; - -public: - - /** - * Construct a new rule with the given input, output text, and other - * attributes. A cursor position may be specified for the output text. - * @param input input string, including key and optional ante and - * post context. - * @param anteContextPos offset into input to end of ante context, or -1 if - * none. Must be <= input.length() if not -1. - * @param postContextPos offset into input to start of post context, or -1 - * if none. Must be <= input.length() if not -1, and must be >= - * anteContextPos. - * @param outputStr output string. - * @param cursorPosition offset into output at which cursor is located, or -1 if - * none. If less than zero, then the cursor is placed after the - * output; that is, -1 is equivalent to - * output.length(). If greater than - * output.length() then an exception is thrown. - * @param cursorOffset an offset to be added to cursorPos to position the - * cursor either in the ante context, if < 0, or in the post context, if > - * 0. For example, the rule "abc{def} > | @@@ xyz;" changes "def" to - * "xyz" and moves the cursor to before "a". It would have a cursorOffset - * of -3. - * @param segs array of UnicodeMatcher corresponding to input pattern - * segments, or null if there are none. The array itself is adopted, - * but the pointers within it are not. - * @param segsCount number of elements in segs[]. - * @param anchorStart true if the the rule is anchored on the left to - * the context start. - * @param anchorEnd true if the rule is anchored on the right to the - * context limit. - * @param data the rule data. - * @param status Output parameter filled in with success or failure status. - */ - TransliterationRule(const UnicodeString& input, - int32_t anteContextPos, int32_t postContextPos, - const UnicodeString& outputStr, - int32_t cursorPosition, int32_t cursorOffset, - UnicodeFunctor** segs, - int32_t segsCount, - UBool anchorStart, UBool anchorEnd, - const TransliterationRuleData* data, - UErrorCode& status); - - /** - * Copy constructor. - * @param other the object to be copied. - */ - TransliterationRule(TransliterationRule& other); - - /** - * Destructor. - */ - virtual ~TransliterationRule(); - - /** - * Change the data object that this rule belongs to. Used - * internally by the TransliterationRuleData copy constructor. - * @param data the new data value to be set. - */ - void setData(const TransliterationRuleData* data); - - /** - * Return the preceding context length. This method is needed to - * support the Transliterator method - * getMaximumContextLength(). Internally, this is - * implemented as the anteContextLength, optionally plus one if - * there is a start anchor. The one character anchor gap is - * needed to make repeated incremental transliteration with - * anchors work. - * @return the preceding context length. - */ - virtual int32_t getContextLength(void) const; - - /** - * Internal method. Returns 8-bit index value for this rule. - * This is the low byte of the first character of the key, - * unless the first character of the key is a set. If it's a - * set, or otherwise can match multiple keys, the index value is -1. - * @return 8-bit index value for this rule. - */ - int16_t getIndexValue() const; - - /** - * Internal method. Returns true if this rule matches the given - * index value. The index value is an 8-bit integer, 0..255, - * representing the low byte of the first character of the key. - * It matches this rule if it matches the first character of the - * key, or if the first character of the key is a set, and the set - * contains any character with a low byte equal to the index - * value. If the rule contains only ante context, as in foo)>bar, - * then it will match any key. - * @param v the given index value. - * @return true if this rule matches the given index value. - */ - UBool matchesIndexValue(uint8_t v) const; - - /** - * Return true if this rule masks another rule. If r1 masks r2 then - * r1 matches any input string that r2 matches. If r1 masks r2 and r2 masks - * r1 then r1 == r2. Examples: "a>x" masks "ab>y". "a>x" masks "a[b]>y". - * "[c]a>x" masks "[dc]a>y". - * @param r2 the given rule to be compared with. - * @return true if this rule masks 'r2' - */ - virtual UBool masks(const TransliterationRule& r2) const; - - /** - * Attempt a match and replacement at the given position. Return - * the degree of match between this rule and the given text. The - * degree of match may be mismatch, a partial match, or a full - * match. A mismatch means at least one character of the text - * does not match the context or key. A partial match means some - * context and key characters match, but the text is not long - * enough to match all of them. A full match means all context - * and key characters match. - * - * If a full match is obtained, perform a replacement, update pos, - * and return U_MATCH. Otherwise both text and pos are unchanged. - * - * @param text the text - * @param pos the position indices - * @param incremental if true, test for partial matches that may - * be completed by additional text inserted at pos.limit. - * @return one of U_MISMATCH, - * U_PARTIAL_MATCH, or U_MATCH. If - * incremental is false then U_PARTIAL_MATCH will not be returned. - */ - UMatchDegree matchAndReplace(Replaceable& text, - UTransPosition& pos, - UBool incremental) const; - - /** - * Create a rule string that represents this rule object. Append - * it to the given string. - */ - virtual UnicodeString& toRule(UnicodeString& pat, - UBool escapeUnprintable) const; - - /** - * Union the set of all characters that may be modified by this rule - * into the given set. - */ - void addSourceSetTo(UnicodeSet& toUnionTo) const; - - /** - * Union the set of all characters that may be emitted by this rule - * into the given set. - */ - void addTargetSetTo(UnicodeSet& toUnionTo) const; - - private: - - friend class StringMatcher; - - TransliterationRule &operator=(const TransliterationRule &other); // forbid copying of this class -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +* Copyright (C) {1999-2001}, International Business Machines Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/17/99 aliu Creation. +********************************************************************** +*/ +#ifndef RBT_RULE_H +#define RBT_RULE_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/uobject.h" +#include "unicode/unistr.h" +#include "unicode/utrans.h" +#include "unicode/unimatch.h" + +U_NAMESPACE_BEGIN + +class Replaceable; +class TransliterationRuleData; +class StringMatcher; +class UnicodeFunctor; + +/** + * A transliteration rule used by + * RuleBasedTransliterator. + * TransliterationRule is an immutable object. + * + *

      A rule consists of an input pattern and an output string. When + * the input pattern is matched, the output string is emitted. The + * input pattern consists of zero or more characters which are matched + * exactly (the key) and optional context. Context must match if it + * is specified. Context may be specified before the key, after the + * key, or both. The key, preceding context, and following context + * may contain variables. Variables represent a set of Unicode + * characters, such as the letters a through z. + * Variables are detected by looking up each character in a supplied + * variable list to see if it has been so defined. + * + *

      A rule may contain segments in its input string and segment + * references in its output string. A segment is a substring of the + * input pattern, indicated by an offset and limit. The segment may + * be in the preceding or following context. It may not span a + * context boundary. A segment reference is a special character in + * the output string that causes a segment of the input string (not + * the input pattern) to be copied to the output string. The range of + * special characters that represent segment references is defined by + * RuleBasedTransliterator.Data. + * + * @author Alan Liu + */ +class TransliterationRule : public UMemory { + +private: + + // TODO Eliminate the pattern and keyLength data members. They + // are used only by masks() and getIndexValue() which are called + // only during build time, not during run-time. Perhaps these + // methods and pattern/keyLength can be isolated into a separate + // object. + + /** + * The match that must occur before the key, or null if there is no + * preceding context. + */ + StringMatcher *anteContext; + + /** + * The matcher object for the key. If null, then the key is empty. + */ + StringMatcher *key; + + /** + * The match that must occur after the key, or null if there is no + * following context. + */ + StringMatcher *postContext; + + /** + * The object that performs the replacement if the key, + * anteContext, and postContext are matched. Never null. + */ + UnicodeFunctor* output; + + /** + * The string that must be matched, consisting of the anteContext, key, + * and postContext, concatenated together, in that order. Some components + * may be empty (zero length). + * @see anteContextLength + * @see keyLength + */ + UnicodeString pattern; + + /** + * An array of matcher objects corresponding to the input pattern + * segments. If there are no segments this is null. N.B. This is + * a UnicodeMatcher for generality, but in practice it is always a + * StringMatcher. In the future we may generalize this, but for + * now we sometimes cast down to StringMatcher. + * + * The array is owned, but the pointers within it are not. + */ + UnicodeFunctor** segments; + + /** + * The number of elements in segments[] or zero if segments is nullptr. + */ + int32_t segmentsCount; + + /** + * The length of the string that must match before the key. If + * zero, then there is no matching requirement before the key. + * Substring [0,anteContextLength) of pattern is the anteContext. + */ + int32_t anteContextLength; + + /** + * The length of the key. Substring [anteContextLength, + * anteContextLength + keyLength) is the key. + + */ + int32_t keyLength; + + /** + * Miscellaneous attributes. + */ + int8_t flags; + + /** + * Flag attributes. + */ + enum { + ANCHOR_START = 1, + ANCHOR_END = 2 + }; + + /** + * An alias pointer to the data for this rule. The data provides + * lookup services for matchers and segments. + */ + const TransliterationRuleData* data; + +public: + + /** + * Construct a new rule with the given input, output text, and other + * attributes. A cursor position may be specified for the output text. + * @param input input string, including key and optional ante and + * post context. + * @param anteContextPos offset into input to end of ante context, or -1 if + * none. Must be <= input.length() if not -1. + * @param postContextPos offset into input to start of post context, or -1 + * if none. Must be <= input.length() if not -1, and must be >= + * anteContextPos. + * @param outputStr output string. + * @param cursorPosition offset into output at which cursor is located, or -1 if + * none. If less than zero, then the cursor is placed after the + * output; that is, -1 is equivalent to + * output.length(). If greater than + * output.length() then an exception is thrown. + * @param cursorOffset an offset to be added to cursorPos to position the + * cursor either in the ante context, if < 0, or in the post context, if > + * 0. For example, the rule "abc{def} > | @@@ xyz;" changes "def" to + * "xyz" and moves the cursor to before "a". It would have a cursorOffset + * of -3. + * @param segs array of UnicodeMatcher corresponding to input pattern + * segments, or null if there are none. The array itself is adopted, + * but the pointers within it are not. + * @param segsCount number of elements in segs[]. + * @param anchorStart true if the the rule is anchored on the left to + * the context start. + * @param anchorEnd true if the rule is anchored on the right to the + * context limit. + * @param data the rule data. + * @param status Output parameter filled in with success or failure status. + */ + TransliterationRule(const UnicodeString& input, + int32_t anteContextPos, int32_t postContextPos, + const UnicodeString& outputStr, + int32_t cursorPosition, int32_t cursorOffset, + UnicodeFunctor** segs, + int32_t segsCount, + UBool anchorStart, UBool anchorEnd, + const TransliterationRuleData* data, + UErrorCode& status); + + /** + * Copy constructor. + * @param other the object to be copied. + */ + TransliterationRule(TransliterationRule& other); + + /** + * Destructor. + */ + virtual ~TransliterationRule(); + + /** + * Change the data object that this rule belongs to. Used + * internally by the TransliterationRuleData copy constructor. + * @param data the new data value to be set. + */ + void setData(const TransliterationRuleData* data); + + /** + * Return the preceding context length. This method is needed to + * support the Transliterator method + * getMaximumContextLength(). Internally, this is + * implemented as the anteContextLength, optionally plus one if + * there is a start anchor. The one character anchor gap is + * needed to make repeated incremental transliteration with + * anchors work. + * @return the preceding context length. + */ + virtual int32_t getContextLength() const; + + /** + * Internal method. Returns 8-bit index value for this rule. + * This is the low byte of the first character of the key, + * unless the first character of the key is a set. If it's a + * set, or otherwise can match multiple keys, the index value is -1. + * @return 8-bit index value for this rule. + */ + int16_t getIndexValue() const; + + /** + * Internal method. Returns true if this rule matches the given + * index value. The index value is an 8-bit integer, 0..255, + * representing the low byte of the first character of the key. + * It matches this rule if it matches the first character of the + * key, or if the first character of the key is a set, and the set + * contains any character with a low byte equal to the index + * value. If the rule contains only ante context, as in foo)>bar, + * then it will match any key. + * @param v the given index value. + * @return true if this rule matches the given index value. + */ + UBool matchesIndexValue(uint8_t v) const; + + /** + * Return true if this rule masks another rule. If r1 masks r2 then + * r1 matches any input string that r2 matches. If r1 masks r2 and r2 masks + * r1 then r1 == r2. Examples: "a>x" masks "ab>y". "a>x" masks "a[b]>y". + * "[c]a>x" masks "[dc]a>y". + * @param r2 the given rule to be compared with. + * @return true if this rule masks 'r2' + */ + virtual UBool masks(const TransliterationRule& r2) const; + + /** + * Attempt a match and replacement at the given position. Return + * the degree of match between this rule and the given text. The + * degree of match may be mismatch, a partial match, or a full + * match. A mismatch means at least one character of the text + * does not match the context or key. A partial match means some + * context and key characters match, but the text is not long + * enough to match all of them. A full match means all context + * and key characters match. + * + * If a full match is obtained, perform a replacement, update pos, + * and return U_MATCH. Otherwise both text and pos are unchanged. + * + * @param text the text + * @param pos the position indices + * @param incremental if true, test for partial matches that may + * be completed by additional text inserted at pos.limit. + * @return one of U_MISMATCH, + * U_PARTIAL_MATCH, or U_MATCH. If + * incremental is false then U_PARTIAL_MATCH will not be returned. + */ + UMatchDegree matchAndReplace(Replaceable& text, + UTransPosition& pos, + UBool incremental) const; + + /** + * Create a rule string that represents this rule object. Append + * it to the given string. + */ + virtual UnicodeString& toRule(UnicodeString& pat, + UBool escapeUnprintable) const; + + /** + * Union the set of all characters that may be modified by this rule + * into the given set. + */ + void addSourceSetTo(UnicodeSet& toUnionTo) const; + + /** + * Union the set of all characters that may be emitted by this rule + * into the given set. + */ + void addTargetSetTo(UnicodeSet& toUnionTo) const; + + private: + + friend class StringMatcher; + + TransliterationRule &operator=(const TransliterationRule &other); // forbid copying of this class +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/rbt_set.cpp b/deps/icu-small/source/i18n/rbt_set.cpp index c5174674361584..21e484db04d5b3 100644 --- a/deps/icu-small/source/i18n/rbt_set.cpp +++ b/deps/icu-small/source/i18n/rbt_set.cpp @@ -1,466 +1,466 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - ********************************************************************** - * Copyright (C) 1999-2011, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - * Date Name Description - * 11/17/99 aliu Creation. - ********************************************************************** - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/unistr.h" -#include "unicode/uniset.h" -#include "unicode/utf16.h" -#include "rbt_set.h" -#include "rbt_rule.h" -#include "cmemory.h" -#include "putilimp.h" - -U_CDECL_BEGIN -static void U_CALLCONV _deleteRule(void *rule) { - delete (icu::TransliterationRule *)rule; -} -U_CDECL_END - -//---------------------------------------------------------------------- -// BEGIN Debugging support -//---------------------------------------------------------------------- - -// #define DEBUG_RBT - -#ifdef DEBUG_RBT -#include -#include "charstr.h" - -/** - * @param appendTo result is appended to this param. - * @param input the string being transliterated - * @param pos the index struct - */ -static UnicodeString& _formatInput(UnicodeString &appendTo, - const UnicodeString& input, - const UTransPosition& pos) { - // Output a string of the form aaa{bbb|ccc|ddd}eee, where - // the {} indicate the context start and limit, and the || - // indicate the start and limit. - if (0 <= pos.contextStart && - pos.contextStart <= pos.start && - pos.start <= pos.limit && - pos.limit <= pos.contextLimit && - pos.contextLimit <= input.length()) { - - UnicodeString a, b, c, d, e; - input.extractBetween(0, pos.contextStart, a); - input.extractBetween(pos.contextStart, pos.start, b); - input.extractBetween(pos.start, pos.limit, c); - input.extractBetween(pos.limit, pos.contextLimit, d); - input.extractBetween(pos.contextLimit, input.length(), e); - appendTo.append(a).append((UChar)123/*{*/).append(b). - append((UChar)124/*|*/).append(c).append((UChar)124/*|*/).append(d). - append((UChar)125/*}*/).append(e); - } else { - appendTo.append("INVALID UTransPosition"); - //appendTo.append((UnicodeString)"INVALID UTransPosition {cs=" + - // pos.contextStart + ", s=" + pos.start + ", l=" + - // pos.limit + ", cl=" + pos.contextLimit + "} on " + - // input); - } - return appendTo; -} - -// Append a hex string to the target -UnicodeString& _appendHex(uint32_t number, - int32_t digits, - UnicodeString& target) { - static const UChar digitString[] = { - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0 - }; - while (digits--) { - target += digitString[(number >> (digits*4)) & 0xF]; - } - return target; -} - -// Replace nonprintable characters with unicode escapes -UnicodeString& _escape(const UnicodeString &source, - UnicodeString &target) { - for (int32_t i = 0; i < source.length(); ) { - UChar32 ch = source.char32At(i); - i += U16_LENGTH(ch); - if (ch < 0x09 || (ch > 0x0A && ch < 0x20)|| ch > 0x7E) { - if (ch <= 0xFFFF) { - target += "\\u"; - _appendHex(ch, 4, target); - } else { - target += "\\U"; - _appendHex(ch, 8, target); - } - } else { - target += ch; - } - } - return target; -} - -inline void _debugOut(const char* msg, TransliterationRule* rule, - const Replaceable& theText, UTransPosition& pos) { - UnicodeString buf(msg, ""); - if (rule) { - UnicodeString r; - rule->toRule(r, true); - buf.append((UChar)32).append(r); - } - buf.append(UnicodeString(" => ", "")); - UnicodeString* text = (UnicodeString*)&theText; - _formatInput(buf, *text, pos); - UnicodeString esc; - _escape(buf, esc); - CharString cbuf(esc); - printf("%s\n", (const char*) cbuf); -} - -#else -#define _debugOut(msg, rule, theText, pos) -#endif - -//---------------------------------------------------------------------- -// END Debugging support -//---------------------------------------------------------------------- - -// Fill the precontext and postcontext with the patterns of the rules -// that are masking one another. -static void maskingError(const icu::TransliterationRule& rule1, - const icu::TransliterationRule& rule2, - UParseError& parseError) { - icu::UnicodeString r; - int32_t len; - - parseError.line = parseError.offset = -1; - - // for pre-context - rule1.toRule(r, false); - len = uprv_min(r.length(), U_PARSE_CONTEXT_LEN-1); - r.extract(0, len, parseError.preContext); - parseError.preContext[len] = 0; - - //for post-context - r.truncate(0); - rule2.toRule(r, false); - len = uprv_min(r.length(), U_PARSE_CONTEXT_LEN-1); - r.extract(0, len, parseError.postContext); - parseError.postContext[len] = 0; -} - -U_NAMESPACE_BEGIN - -/** - * Construct a new empty rule set. - */ -TransliterationRuleSet::TransliterationRuleSet(UErrorCode& status) : - UMemory(), ruleVector(nullptr), rules(nullptr), index {}, maxContextLength(0) { - LocalPointer lpRuleVector(new UVector(_deleteRule, nullptr, status), status); - if (U_FAILURE(status)) { - return; - } - ruleVector = lpRuleVector.orphan(); -} - -/** - * Copy constructor. - */ -TransliterationRuleSet::TransliterationRuleSet(const TransliterationRuleSet& other) : - UMemory(other), - ruleVector(nullptr), - rules(nullptr), - maxContextLength(other.maxContextLength) { - - int32_t i, len; - uprv_memcpy(index, other.index, sizeof(index)); - UErrorCode status = U_ZERO_ERROR; - LocalPointer lpRuleVector(new UVector(_deleteRule, nullptr, status), status); - if (U_FAILURE(status)) { - return; - } - ruleVector = lpRuleVector.orphan(); - if (other.ruleVector != nullptr && U_SUCCESS(status)) { - len = other.ruleVector->size(); - for (i=0; i tempTranslitRule( - new TransliterationRule(*(TransliterationRule*)other.ruleVector->elementAt(i)), status); - ruleVector->adoptElement(tempTranslitRule.orphan(), status); - } - } - if (other.rules != 0 && U_SUCCESS(status)) { - UParseError p; - freeze(p, status); - } -} - -/** - * Destructor. - */ -TransliterationRuleSet::~TransliterationRuleSet() { - delete ruleVector; // This deletes the contained rules - uprv_free(rules); -} - -void TransliterationRuleSet::setData(const TransliterationRuleData* d) { - /** - * We assume that the ruleset has already been frozen. - */ - int32_t len = index[256]; // see freeze() - for (int32_t i=0; isetData(d); - } -} - -/** - * Return the maximum context length. - * @return the length of the longest preceding context. - */ -int32_t TransliterationRuleSet::getMaximumContextLength(void) const { - return maxContextLength; -} - -/** - * Add a rule to this set. Rules are added in order, and order is - * significant. The last call to this method must be followed by - * a call to freeze() before the rule set is used. - * - *

      If freeze() has already been called, calling addRule() - * unfreezes the rules, and freeze() must be called again. - * - * @param adoptedRule the rule to add - */ -void TransliterationRuleSet::addRule(TransliterationRule* adoptedRule, - UErrorCode& status) { - LocalPointer lpAdoptedRule(adoptedRule); - ruleVector->adoptElement(lpAdoptedRule.orphan(), status); - if (U_FAILURE(status)) { - return; - } - - int32_t len; - if ((len = adoptedRule->getContextLength()) > maxContextLength) { - maxContextLength = len; - } - - uprv_free(rules); - rules = 0; -} - -/** - * Check this for masked rules and index it to optimize performance. - * The sequence of operations is: (1) add rules to a set using - * addRule(); (2) freeze the set using - * freeze(); (3) use the rule set. If - * addRule() is called after calling this method, it - * invalidates this object, and this method must be called again. - * That is, freeze() may be called multiple times, - * although for optimal performance it shouldn't be. - */ -void TransliterationRuleSet::freeze(UParseError& parseError,UErrorCode& status) { - /* Construct the rule array and index table. We reorder the - * rules by sorting them into 256 bins. Each bin contains all - * rules matching the index value for that bin. A rule - * matches an index value if string whose first key character - * has a low byte equal to the index value can match the rule. - * - * Each bin contains zero or more rules, in the same order - * they were found originally. However, the total rules in - * the bins may exceed the number in the original vector, - * since rules that have a variable as their first key - * character will generally fall into more than one bin. - * - * That is, each bin contains all rules that either have that - * first index value as their first key character, or have - * a set containing the index value as their first character. - */ - int32_t n = ruleVector->size(); - int32_t j; - int16_t x; - UVector v(2*n, status); // heuristic; adjust as needed - - if (U_FAILURE(status)) { - return; - } - - /* Precompute the index values. This saves a LOT of time. - * Be careful not to call malloc(0). - */ - int16_t* indexValue = (int16_t*) uprv_malloc( sizeof(int16_t) * (n > 0 ? n : 1) ); - /* test for NULL */ - if (indexValue == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - for (j=0; jelementAt(j); - indexValue[j] = r->getIndexValue(); - } - for (x=0; x<256; ++x) { - index[x] = v.size(); - for (j=0; j= 0) { - if (indexValue[j] == x) { - v.addElement(ruleVector->elementAt(j), status); - } - } else { - // If the indexValue is < 0, then the first key character is - // a set, and we must use the more time-consuming - // matchesIndexValue check. In practice this happens - // rarely, so we seldom treat this code path. - TransliterationRule* r = (TransliterationRule*) ruleVector->elementAt(j); - if (r->matchesIndexValue((uint8_t)x)) { - v.addElement(r, status); - } - } - } - } - uprv_free(indexValue); - index[256] = v.size(); - if (U_FAILURE(status)) { - return; - } - - /* Freeze things into an array. - */ - uprv_free(rules); // Contains alias pointers - - /* You can't do malloc(0)! */ - if (v.size() == 0) { - rules = NULL; - return; - } - rules = (TransliterationRule **)uprv_malloc(v.size() * sizeof(TransliterationRule *)); - /* test for NULL */ - if (rules == 0) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - for (j=0; jmasks(*r2)) { -//| if (errors == null) { -//| errors = new StringBuffer(); -//| } else { -//| errors.append("\n"); -//| } -//| errors.append("Rule " + r1 + " masks " + r2); - status = U_RULE_MASK_ERROR; - maskingError(*r1, *r2, parseError); - return; - } - } - } - } - - //if (errors != null) { - // throw new IllegalArgumentException(errors.toString()); - //} -} - -/** - * Transliterate the given text with the given UTransPosition - * indices. Return true if the transliteration should continue - * or false if it should halt (because of a U_PARTIAL_MATCH match). - * Note that false is only ever returned if isIncremental is true. - * @param text the text to be transliterated - * @param pos the position indices, which will be updated - * @param incremental if true, assume new text may be inserted - * at index.limit, and return false if there is a partial match. - * @return true unless a U_PARTIAL_MATCH has been obtained, - * indicating that transliteration should stop until more text - * arrives. - */ -UBool TransliterationRuleSet::transliterate(Replaceable& text, - UTransPosition& pos, - UBool incremental) { - int16_t indexByte = (int16_t) (text.char32At(pos.start) & 0xFF); - for (int32_t i=index[indexByte]; imatchAndReplace(text, pos, incremental); - switch (m) { - case U_MATCH: - _debugOut("match", rules[i], text, pos); - return true; - case U_PARTIAL_MATCH: - _debugOut("partial match", rules[i], text, pos); - return false; - default: /* Ram: added default to make GCC happy */ - break; - } - } - // No match or partial match from any rule - pos.start += U16_LENGTH(text.char32At(pos.start)); - _debugOut("no match", NULL, text, pos); - return true; -} - -/** - * Create rule strings that represents this rule set. - */ -UnicodeString& TransliterationRuleSet::toRules(UnicodeString& ruleSource, - UBool escapeUnprintable) const { - int32_t i; - int32_t count = ruleVector->size(); - ruleSource.truncate(0); - for (i=0; ielementAt(i); - r->toRule(ruleSource, escapeUnprintable); - } - return ruleSource; -} - -/** - * Return the set of all characters that may be modified - * (getTarget=false) or emitted (getTarget=true) by this set. - */ -UnicodeSet& TransliterationRuleSet::getSourceTargetSet(UnicodeSet& result, - UBool getTarget) const -{ - result.clear(); - int32_t count = ruleVector->size(); - for (int32_t i=0; ielementAt(i); - if (getTarget) { - r->addTargetSetTo(result); - } else { - r->addSourceSetTo(result); - } - } - return result; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* + ********************************************************************** + * Copyright (C) 1999-2011, International Business Machines + * Corporation and others. All Rights Reserved. + ********************************************************************** + * Date Name Description + * 11/17/99 aliu Creation. + ********************************************************************** + */ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/unistr.h" +#include "unicode/uniset.h" +#include "unicode/utf16.h" +#include "rbt_set.h" +#include "rbt_rule.h" +#include "cmemory.h" +#include "putilimp.h" + +U_CDECL_BEGIN +static void U_CALLCONV _deleteRule(void *rule) { + delete (icu::TransliterationRule *)rule; +} +U_CDECL_END + +//---------------------------------------------------------------------- +// BEGIN Debugging support +//---------------------------------------------------------------------- + +// #define DEBUG_RBT + +#ifdef DEBUG_RBT +#include +#include "charstr.h" + +/** + * @param appendTo result is appended to this param. + * @param input the string being transliterated + * @param pos the index struct + */ +static UnicodeString& _formatInput(UnicodeString &appendTo, + const UnicodeString& input, + const UTransPosition& pos) { + // Output a string of the form aaa{bbb|ccc|ddd}eee, where + // the {} indicate the context start and limit, and the || + // indicate the start and limit. + if (0 <= pos.contextStart && + pos.contextStart <= pos.start && + pos.start <= pos.limit && + pos.limit <= pos.contextLimit && + pos.contextLimit <= input.length()) { + + UnicodeString a, b, c, d, e; + input.extractBetween(0, pos.contextStart, a); + input.extractBetween(pos.contextStart, pos.start, b); + input.extractBetween(pos.start, pos.limit, c); + input.extractBetween(pos.limit, pos.contextLimit, d); + input.extractBetween(pos.contextLimit, input.length(), e); + appendTo.append(a).append((char16_t)123/*{*/).append(b). + append((char16_t)124/*|*/).append(c).append((char16_t)124/*|*/).append(d). + append((char16_t)125/*}*/).append(e); + } else { + appendTo.append("INVALID UTransPosition"); + //appendTo.append((UnicodeString)"INVALID UTransPosition {cs=" + + // pos.contextStart + ", s=" + pos.start + ", l=" + + // pos.limit + ", cl=" + pos.contextLimit + "} on " + + // input); + } + return appendTo; +} + +// Append a hex string to the target +UnicodeString& _appendHex(uint32_t number, + int32_t digits, + UnicodeString& target) { + static const char16_t digitString[] = { + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0 + }; + while (digits--) { + target += digitString[(number >> (digits*4)) & 0xF]; + } + return target; +} + +// Replace nonprintable characters with unicode escapes +UnicodeString& _escape(const UnicodeString &source, + UnicodeString &target) { + for (int32_t i = 0; i < source.length(); ) { + UChar32 ch = source.char32At(i); + i += U16_LENGTH(ch); + if (ch < 0x09 || (ch > 0x0A && ch < 0x20)|| ch > 0x7E) { + if (ch <= 0xFFFF) { + target += "\\u"; + _appendHex(ch, 4, target); + } else { + target += "\\U"; + _appendHex(ch, 8, target); + } + } else { + target += ch; + } + } + return target; +} + +inline void _debugOut(const char* msg, TransliterationRule* rule, + const Replaceable& theText, UTransPosition& pos) { + UnicodeString buf(msg, ""); + if (rule) { + UnicodeString r; + rule->toRule(r, true); + buf.append((char16_t)32).append(r); + } + buf.append(UnicodeString(" => ", "")); + UnicodeString* text = (UnicodeString*)&theText; + _formatInput(buf, *text, pos); + UnicodeString esc; + _escape(buf, esc); + CharString cbuf(esc); + printf("%s\n", (const char*) cbuf); +} + +#else +#define _debugOut(msg, rule, theText, pos) +#endif + +//---------------------------------------------------------------------- +// END Debugging support +//---------------------------------------------------------------------- + +// Fill the precontext and postcontext with the patterns of the rules +// that are masking one another. +static void maskingError(const icu::TransliterationRule& rule1, + const icu::TransliterationRule& rule2, + UParseError& parseError) { + icu::UnicodeString r; + int32_t len; + + parseError.line = parseError.offset = -1; + + // for pre-context + rule1.toRule(r, false); + len = uprv_min(r.length(), U_PARSE_CONTEXT_LEN-1); + r.extract(0, len, parseError.preContext); + parseError.preContext[len] = 0; + + //for post-context + r.truncate(0); + rule2.toRule(r, false); + len = uprv_min(r.length(), U_PARSE_CONTEXT_LEN-1); + r.extract(0, len, parseError.postContext); + parseError.postContext[len] = 0; +} + +U_NAMESPACE_BEGIN + +/** + * Construct a new empty rule set. + */ +TransliterationRuleSet::TransliterationRuleSet(UErrorCode& status) : + UMemory(), ruleVector(nullptr), rules(nullptr), index {}, maxContextLength(0) { + LocalPointer lpRuleVector(new UVector(_deleteRule, nullptr, status), status); + if (U_FAILURE(status)) { + return; + } + ruleVector = lpRuleVector.orphan(); +} + +/** + * Copy constructor. + */ +TransliterationRuleSet::TransliterationRuleSet(const TransliterationRuleSet& other) : + UMemory(other), + ruleVector(nullptr), + rules(nullptr), + maxContextLength(other.maxContextLength) { + + int32_t i, len; + uprv_memcpy(index, other.index, sizeof(index)); + UErrorCode status = U_ZERO_ERROR; + LocalPointer lpRuleVector(new UVector(_deleteRule, nullptr, status), status); + if (U_FAILURE(status)) { + return; + } + ruleVector = lpRuleVector.orphan(); + if (other.ruleVector != nullptr && U_SUCCESS(status)) { + len = other.ruleVector->size(); + for (i=0; i tempTranslitRule( + new TransliterationRule(*(TransliterationRule*)other.ruleVector->elementAt(i)), status); + ruleVector->adoptElement(tempTranslitRule.orphan(), status); + } + } + if (other.rules != 0 && U_SUCCESS(status)) { + UParseError p; + freeze(p, status); + } +} + +/** + * Destructor. + */ +TransliterationRuleSet::~TransliterationRuleSet() { + delete ruleVector; // This deletes the contained rules + uprv_free(rules); +} + +void TransliterationRuleSet::setData(const TransliterationRuleData* d) { + /** + * We assume that the ruleset has already been frozen. + */ + int32_t len = index[256]; // see freeze() + for (int32_t i=0; isetData(d); + } +} + +/** + * Return the maximum context length. + * @return the length of the longest preceding context. + */ +int32_t TransliterationRuleSet::getMaximumContextLength() const { + return maxContextLength; +} + +/** + * Add a rule to this set. Rules are added in order, and order is + * significant. The last call to this method must be followed by + * a call to freeze() before the rule set is used. + * + *

      If freeze() has already been called, calling addRule() + * unfreezes the rules, and freeze() must be called again. + * + * @param adoptedRule the rule to add + */ +void TransliterationRuleSet::addRule(TransliterationRule* adoptedRule, + UErrorCode& status) { + LocalPointer lpAdoptedRule(adoptedRule); + ruleVector->adoptElement(lpAdoptedRule.orphan(), status); + if (U_FAILURE(status)) { + return; + } + + int32_t len; + if ((len = adoptedRule->getContextLength()) > maxContextLength) { + maxContextLength = len; + } + + uprv_free(rules); + rules = 0; +} + +/** + * Check this for masked rules and index it to optimize performance. + * The sequence of operations is: (1) add rules to a set using + * addRule(); (2) freeze the set using + * freeze(); (3) use the rule set. If + * addRule() is called after calling this method, it + * invalidates this object, and this method must be called again. + * That is, freeze() may be called multiple times, + * although for optimal performance it shouldn't be. + */ +void TransliterationRuleSet::freeze(UParseError& parseError,UErrorCode& status) { + /* Construct the rule array and index table. We reorder the + * rules by sorting them into 256 bins. Each bin contains all + * rules matching the index value for that bin. A rule + * matches an index value if string whose first key character + * has a low byte equal to the index value can match the rule. + * + * Each bin contains zero or more rules, in the same order + * they were found originally. However, the total rules in + * the bins may exceed the number in the original vector, + * since rules that have a variable as their first key + * character will generally fall into more than one bin. + * + * That is, each bin contains all rules that either have that + * first index value as their first key character, or have + * a set containing the index value as their first character. + */ + int32_t n = ruleVector->size(); + int32_t j; + int16_t x; + UVector v(2*n, status); // heuristic; adjust as needed + + if (U_FAILURE(status)) { + return; + } + + /* Precompute the index values. This saves a LOT of time. + * Be careful not to call malloc(0). + */ + int16_t* indexValue = (int16_t*) uprv_malloc( sizeof(int16_t) * (n > 0 ? n : 1) ); + /* test for nullptr */ + if (indexValue == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + for (j=0; jelementAt(j); + indexValue[j] = r->getIndexValue(); + } + for (x=0; x<256; ++x) { + index[x] = v.size(); + for (j=0; j= 0) { + if (indexValue[j] == x) { + v.addElement(ruleVector->elementAt(j), status); + } + } else { + // If the indexValue is < 0, then the first key character is + // a set, and we must use the more time-consuming + // matchesIndexValue check. In practice this happens + // rarely, so we seldom treat this code path. + TransliterationRule* r = (TransliterationRule*) ruleVector->elementAt(j); + if (r->matchesIndexValue((uint8_t)x)) { + v.addElement(r, status); + } + } + } + } + uprv_free(indexValue); + index[256] = v.size(); + if (U_FAILURE(status)) { + return; + } + + /* Freeze things into an array. + */ + uprv_free(rules); // Contains alias pointers + + /* You can't do malloc(0)! */ + if (v.size() == 0) { + rules = nullptr; + return; + } + rules = (TransliterationRule **)uprv_malloc(v.size() * sizeof(TransliterationRule *)); + /* test for nullptr */ + if (rules == 0) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + for (j=0; jmasks(*r2)) { +//| if (errors == null) { +//| errors = new StringBuffer(); +//| } else { +//| errors.append("\n"); +//| } +//| errors.append("Rule " + r1 + " masks " + r2); + status = U_RULE_MASK_ERROR; + maskingError(*r1, *r2, parseError); + return; + } + } + } + } + + //if (errors != null) { + // throw new IllegalArgumentException(errors.toString()); + //} +} + +/** + * Transliterate the given text with the given UTransPosition + * indices. Return true if the transliteration should continue + * or false if it should halt (because of a U_PARTIAL_MATCH match). + * Note that false is only ever returned if isIncremental is true. + * @param text the text to be transliterated + * @param pos the position indices, which will be updated + * @param incremental if true, assume new text may be inserted + * at index.limit, and return false if there is a partial match. + * @return true unless a U_PARTIAL_MATCH has been obtained, + * indicating that transliteration should stop until more text + * arrives. + */ +UBool TransliterationRuleSet::transliterate(Replaceable& text, + UTransPosition& pos, + UBool incremental) { + int16_t indexByte = (int16_t) (text.char32At(pos.start) & 0xFF); + for (int32_t i=index[indexByte]; imatchAndReplace(text, pos, incremental); + switch (m) { + case U_MATCH: + _debugOut("match", rules[i], text, pos); + return true; + case U_PARTIAL_MATCH: + _debugOut("partial match", rules[i], text, pos); + return false; + default: /* Ram: added default to make GCC happy */ + break; + } + } + // No match or partial match from any rule + pos.start += U16_LENGTH(text.char32At(pos.start)); + _debugOut("no match", nullptr, text, pos); + return true; +} + +/** + * Create rule strings that represents this rule set. + */ +UnicodeString& TransliterationRuleSet::toRules(UnicodeString& ruleSource, + UBool escapeUnprintable) const { + int32_t i; + int32_t count = ruleVector->size(); + ruleSource.truncate(0); + for (i=0; ielementAt(i); + r->toRule(ruleSource, escapeUnprintable); + } + return ruleSource; +} + +/** + * Return the set of all characters that may be modified + * (getTarget=false) or emitted (getTarget=true) by this set. + */ +UnicodeSet& TransliterationRuleSet::getSourceTargetSet(UnicodeSet& result, + UBool getTarget) const +{ + result.clear(); + int32_t count = ruleVector->size(); + for (int32_t i=0; ielementAt(i); + if (getTarget) { + r->addTargetSetTo(result); + } else { + r->addSourceSetTo(result); + } + } + return result; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ diff --git a/deps/icu-small/source/i18n/rbt_set.h b/deps/icu-small/source/i18n/rbt_set.h index 3a2890e8ec7973..348491871dfdc0 100644 --- a/deps/icu-small/source/i18n/rbt_set.h +++ b/deps/icu-small/source/i18n/rbt_set.h @@ -1,167 +1,167 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1999-2007, International Business Machines Corporation -* and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 11/17/99 aliu Creation. -********************************************************************** -*/ -#ifndef RBT_SET_H -#define RBT_SET_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/uobject.h" -#include "unicode/utrans.h" -#include "uvector.h" - -U_NAMESPACE_BEGIN - -class Replaceable; -class TransliterationRule; -class TransliterationRuleData; -class UnicodeFilter; -class UnicodeString; -class UnicodeSet; - -/** - * A set of rules for a RuleBasedTransliterator. - * @author Alan Liu - */ -class TransliterationRuleSet : public UMemory { - /** - * Vector of rules, in the order added. This is used while the - * rule set is getting built. After that, freeze() reorders and - * indexes the rules into rules[]. Any given rule is stored once - * in ruleVector, and one or more times in rules[]. ruleVector - * owns and deletes the rules. - */ - UVector* ruleVector; - - /** - * Sorted and indexed table of rules. This is created by freeze() - * from the rules in ruleVector. It contains alias pointers to - * the rules in ruleVector. It is zero before freeze() is called - * and non-zero thereafter. - */ - TransliterationRule** rules; - - /** - * Index table. For text having a first character c, compute x = c&0xFF. - * Now use rules[index[x]..index[x+1]-1]. This index table is created by - * freeze(). Before freeze() is called it contains garbage. - */ - int32_t index[257]; - - /** - * Length of the longest preceding context - */ - int32_t maxContextLength; - -public: - - /** - * Construct a new empty rule set. - * @param status Output parameter filled in with success or failure status. - */ - TransliterationRuleSet(UErrorCode& status); - - /** - * Copy constructor. - */ - TransliterationRuleSet(const TransliterationRuleSet&); - - /** - * Destructor. - */ - virtual ~TransliterationRuleSet(); - - /** - * Change the data object that this rule belongs to. Used - * internally by the TransliterationRuleData copy constructor. - * @param data the new data value to be set. - */ - void setData(const TransliterationRuleData* data); - - /** - * Return the maximum context length. - * @return the length of the longest preceding context. - */ - virtual int32_t getMaximumContextLength(void) const; - - /** - * Add a rule to this set. Rules are added in order, and order is - * significant. The last call to this method must be followed by - * a call to freeze() before the rule set is used. - * This method must not be called after freeze() has been - * called. - * - * @param adoptedRule the rule to add - */ - virtual void addRule(TransliterationRule* adoptedRule, - UErrorCode& status); - - /** - * Check this for masked rules and index it to optimize performance. - * The sequence of operations is: (1) add rules to a set using - * addRule(); (2) freeze the set using - * freeze(); (3) use the rule set. If - * addRule() is called after calling this method, it - * invalidates this object, and this method must be called again. - * That is, freeze() may be called multiple times, - * although for optimal performance it shouldn't be. - * @param parseError A pointer to UParseError to receive information about errors - * occurred. - * @param status Output parameter filled in with success or failure status. - */ - virtual void freeze(UParseError& parseError, UErrorCode& status); - - /** - * Transliterate the given text with the given UTransPosition - * indices. Return true if the transliteration should continue - * or false if it should halt (because of a U_PARTIAL_MATCH match). - * Note that false is only ever returned if isIncremental is true. - * @param text the text to be transliterated - * @param index the position indices, which will be updated - * @param isIncremental if true, assume new text may be inserted - * at index.limit, and return false if thrre is a partial match. - * @return true unless a U_PARTIAL_MATCH has been obtained, - * indicating that transliteration should stop until more text - * arrives. - */ - UBool transliterate(Replaceable& text, - UTransPosition& index, - UBool isIncremental); - - /** - * Create rule strings that represents this rule set. - * @param result string to receive the rule strings. Current - * contents will be deleted. - * @param escapeUnprintable True, will escape the unprintable characters - * @return A reference to 'result'. - */ - virtual UnicodeString& toRules(UnicodeString& result, - UBool escapeUnprintable) const; - - /** - * Return the set of all characters that may be modified - * (getTarget=false) or emitted (getTarget=true) by this set. - */ - UnicodeSet& getSourceTargetSet(UnicodeSet& result, - UBool getTarget) const; - -private: - - TransliterationRuleSet &operator=(const TransliterationRuleSet &other); // forbid copying of this class -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 1999-2007, International Business Machines Corporation +* and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 11/17/99 aliu Creation. +********************************************************************** +*/ +#ifndef RBT_SET_H +#define RBT_SET_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/uobject.h" +#include "unicode/utrans.h" +#include "uvector.h" + +U_NAMESPACE_BEGIN + +class Replaceable; +class TransliterationRule; +class TransliterationRuleData; +class UnicodeFilter; +class UnicodeString; +class UnicodeSet; + +/** + * A set of rules for a RuleBasedTransliterator. + * @author Alan Liu + */ +class TransliterationRuleSet : public UMemory { + /** + * Vector of rules, in the order added. This is used while the + * rule set is getting built. After that, freeze() reorders and + * indexes the rules into rules[]. Any given rule is stored once + * in ruleVector, and one or more times in rules[]. ruleVector + * owns and deletes the rules. + */ + UVector* ruleVector; + + /** + * Sorted and indexed table of rules. This is created by freeze() + * from the rules in ruleVector. It contains alias pointers to + * the rules in ruleVector. It is zero before freeze() is called + * and non-zero thereafter. + */ + TransliterationRule** rules; + + /** + * Index table. For text having a first character c, compute x = c&0xFF. + * Now use rules[index[x]..index[x+1]-1]. This index table is created by + * freeze(). Before freeze() is called it contains garbage. + */ + int32_t index[257]; + + /** + * Length of the longest preceding context + */ + int32_t maxContextLength; + +public: + + /** + * Construct a new empty rule set. + * @param status Output parameter filled in with success or failure status. + */ + TransliterationRuleSet(UErrorCode& status); + + /** + * Copy constructor. + */ + TransliterationRuleSet(const TransliterationRuleSet&); + + /** + * Destructor. + */ + virtual ~TransliterationRuleSet(); + + /** + * Change the data object that this rule belongs to. Used + * internally by the TransliterationRuleData copy constructor. + * @param data the new data value to be set. + */ + void setData(const TransliterationRuleData* data); + + /** + * Return the maximum context length. + * @return the length of the longest preceding context. + */ + virtual int32_t getMaximumContextLength() const; + + /** + * Add a rule to this set. Rules are added in order, and order is + * significant. The last call to this method must be followed by + * a call to freeze() before the rule set is used. + * This method must not be called after freeze() has been + * called. + * + * @param adoptedRule the rule to add + */ + virtual void addRule(TransliterationRule* adoptedRule, + UErrorCode& status); + + /** + * Check this for masked rules and index it to optimize performance. + * The sequence of operations is: (1) add rules to a set using + * addRule(); (2) freeze the set using + * freeze(); (3) use the rule set. If + * addRule() is called after calling this method, it + * invalidates this object, and this method must be called again. + * That is, freeze() may be called multiple times, + * although for optimal performance it shouldn't be. + * @param parseError A pointer to UParseError to receive information about errors + * occurred. + * @param status Output parameter filled in with success or failure status. + */ + virtual void freeze(UParseError& parseError, UErrorCode& status); + + /** + * Transliterate the given text with the given UTransPosition + * indices. Return true if the transliteration should continue + * or false if it should halt (because of a U_PARTIAL_MATCH match). + * Note that false is only ever returned if isIncremental is true. + * @param text the text to be transliterated + * @param index the position indices, which will be updated + * @param isIncremental if true, assume new text may be inserted + * at index.limit, and return false if thrre is a partial match. + * @return true unless a U_PARTIAL_MATCH has been obtained, + * indicating that transliteration should stop until more text + * arrives. + */ + UBool transliterate(Replaceable& text, + UTransPosition& index, + UBool isIncremental); + + /** + * Create rule strings that represents this rule set. + * @param result string to receive the rule strings. Current + * contents will be deleted. + * @param escapeUnprintable True, will escape the unprintable characters + * @return A reference to 'result'. + */ + virtual UnicodeString& toRules(UnicodeString& result, + UBool escapeUnprintable) const; + + /** + * Return the set of all characters that may be modified + * (getTarget=false) or emitted (getTarget=true) by this set. + */ + UnicodeSet& getSourceTargetSet(UnicodeSet& result, + UBool getTarget) const; + +private: + + TransliterationRuleSet &operator=(const TransliterationRuleSet &other); // forbid copying of this class +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/rbtz.cpp b/deps/icu-small/source/i18n/rbtz.cpp index 0e174bab38a015..41f53aafca694a 100644 --- a/deps/icu-small/source/i18n/rbtz.cpp +++ b/deps/icu-small/source/i18n/rbtz.cpp @@ -1,936 +1,936 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2007-2013, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#include "utypeinfo.h" // for 'typeid' to work - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/rbtz.h" -#include "unicode/gregocal.h" -#include "uvector.h" -#include "gregoimp.h" -#include "cmemory.h" -#include "umutex.h" - -U_NAMESPACE_BEGIN - -/** - * A struct representing a time zone transition - */ -struct Transition : public UMemory { - UDate time; - TimeZoneRule* from; - TimeZoneRule* to; -}; - -U_CDECL_BEGIN -static void U_CALLCONV -deleteTransition(void* obj) { - delete static_cast(obj); -} -U_CDECL_END - -static UBool compareRules(UVector* rules1, UVector* rules2) { - if (rules1 == NULL && rules2 == NULL) { - return true; - } else if (rules1 == NULL || rules2 == NULL) { - return false; - } - int32_t size = rules1->size(); - if (size != rules2->size()) { - return false; - } - for (int32_t i = 0; i < size; i++) { - TimeZoneRule *r1 = (TimeZoneRule*)rules1->elementAt(i); - TimeZoneRule *r2 = (TimeZoneRule*)rules2->elementAt(i); - if (*r1 != *r2) { - return false; - } - } - return true; -} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTimeZone) - -RuleBasedTimeZone::RuleBasedTimeZone(const UnicodeString& id, InitialTimeZoneRule* initialRule) -: BasicTimeZone(id), fInitialRule(initialRule), fHistoricRules(NULL), fFinalRules(NULL), - fHistoricTransitions(NULL), fUpToDate(false) { -} - -RuleBasedTimeZone::RuleBasedTimeZone(const RuleBasedTimeZone& source) -: BasicTimeZone(source), fInitialRule(source.fInitialRule->clone()), - fHistoricTransitions(NULL), fUpToDate(false) { - fHistoricRules = copyRules(source.fHistoricRules); - fFinalRules = copyRules(source.fFinalRules); - if (source.fUpToDate) { - UErrorCode status = U_ZERO_ERROR; - complete(status); - } -} - -RuleBasedTimeZone::~RuleBasedTimeZone() { - deleteTransitions(); - deleteRules(); -} - -RuleBasedTimeZone& -RuleBasedTimeZone::operator=(const RuleBasedTimeZone& right) { - if (*this != right) { - BasicTimeZone::operator=(right); - deleteRules(); - fInitialRule = right.fInitialRule->clone(); - fHistoricRules = copyRules(right.fHistoricRules); - fFinalRules = copyRules(right.fFinalRules); - deleteTransitions(); - fUpToDate = false; - } - return *this; -} - -bool -RuleBasedTimeZone::operator==(const TimeZone& that) const { - if (this == &that) { - return true; - } - if (typeid(*this) != typeid(that) || !BasicTimeZone::operator==(that)) { - return false; - } - RuleBasedTimeZone *rbtz = (RuleBasedTimeZone*)&that; - if (*fInitialRule != *(rbtz->fInitialRule)) { - return false; - } - if (compareRules(fHistoricRules, rbtz->fHistoricRules) - && compareRules(fFinalRules, rbtz->fFinalRules)) { - return true; - } - return false; -} - -bool -RuleBasedTimeZone::operator!=(const TimeZone& that) const { - return !operator==(that); -} - -void -RuleBasedTimeZone::addTransitionRule(TimeZoneRule* rule, UErrorCode& status) { - LocalPointerlpRule(rule); - if (U_FAILURE(status)) { - return; - } - AnnualTimeZoneRule* atzrule = dynamic_cast(rule); - if (atzrule != nullptr && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) { - // A final rule - if (fFinalRules == nullptr) { - LocalPointer lpFinalRules(new UVector(uprv_deleteUObject, nullptr, status), status); - if (U_FAILURE(status)) { - return; - } - fFinalRules = lpFinalRules.orphan(); - } else if (fFinalRules->size() >= 2) { - // Cannot handle more than two final rules - status = U_INVALID_STATE_ERROR; - return; - } - fFinalRules->adoptElement(lpRule.orphan(), status); - } else { - // Non-final rule - if (fHistoricRules == nullptr) { - LocalPointer lpHistoricRules(new UVector(uprv_deleteUObject, nullptr, status), status); - if (U_FAILURE(status)) { - return; - } - fHistoricRules = lpHistoricRules.orphan(); - } - fHistoricRules->adoptElement(lpRule.orphan(), status); - } - // Mark dirty, so transitions are recalculated at next complete() call - fUpToDate = false; -} - - -void -RuleBasedTimeZone::completeConst(UErrorCode& status) const { - static UMutex gLock; - if (U_FAILURE(status)) { - return; - } - umtx_lock(&gLock); - if (!fUpToDate) { - RuleBasedTimeZone *ncThis = const_cast(this); - ncThis->complete(status); - } - umtx_unlock(&gLock); -} - -void -RuleBasedTimeZone::complete(UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - if (fUpToDate) { - return; - } - // Make sure either no final rules or a pair of AnnualTimeZoneRules - // are available. - if (fFinalRules != NULL && fFinalRules->size() != 2) { - status = U_INVALID_STATE_ERROR; - return; - } - - // Create a TimezoneTransition and add to the list - if (fHistoricRules != NULL || fFinalRules != NULL) { - TimeZoneRule *curRule = fInitialRule; - UDate lastTransitionTime = MIN_MILLIS; - - // Build the transition array which represents historical time zone - // transitions. - if (fHistoricRules != NULL && fHistoricRules->size() > 0) { - int32_t i; - int32_t historicCount = fHistoricRules->size(); - LocalMemory done((bool *)uprv_malloc(sizeof(bool) * historicCount)); - if (done == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - goto cleanup; - } - for (i = 0; i < historicCount; i++) { - done[i] = false; - } - while (true) { - int32_t curStdOffset = curRule->getRawOffset(); - int32_t curDstSavings = curRule->getDSTSavings(); - UDate nextTransitionTime = MAX_MILLIS; - TimeZoneRule *nextRule = NULL; - TimeZoneRule *r = NULL; - UBool avail; - UDate tt; - UnicodeString curName, name; - curRule->getName(curName); - - for (i = 0; i < historicCount; i++) { - if (done[i]) { - continue; - } - r = (TimeZoneRule*)fHistoricRules->elementAt(i); - avail = r->getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false, tt); - if (!avail) { - // No more transitions from this rule - skip this rule next time - done[i] = true; - } else { - r->getName(name); - if (*r == *curRule || - (name == curName && r->getRawOffset() == curRule->getRawOffset() - && r->getDSTSavings() == curRule->getDSTSavings())) { - continue; - } - if (tt < nextTransitionTime) { - nextTransitionTime = tt; - nextRule = r; - } - } - } - - if (nextRule == NULL) { - // Check if all historic rules are done - UBool bDoneAll = true; - for (int32_t j = 0; j < historicCount; j++) { - if (!done[j]) { - bDoneAll = false; - break; - } - } - if (bDoneAll) { - break; - } - } - - if (fFinalRules != NULL) { - // Check if one of final rules has earlier transition date - for (i = 0; i < 2 /* fFinalRules->size() */; i++) { - TimeZoneRule *fr = (TimeZoneRule*)fFinalRules->elementAt(i); - if (*fr == *curRule) { - continue; - } - r = (TimeZoneRule*)fFinalRules->elementAt(i); - avail = r->getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false, tt); - if (avail) { - if (tt < nextTransitionTime) { - nextTransitionTime = tt; - nextRule = r; - } - } - } - } - - if (nextRule == NULL) { - // Nothing more - break; - } - - if (fHistoricTransitions == NULL) { - LocalPointer lpHistoricTransitions( - new UVector(deleteTransition, nullptr, status), status); - if (U_FAILURE(status)) { - goto cleanup; - } - fHistoricTransitions = lpHistoricTransitions.orphan(); - } - LocalPointer trst(new Transition, status); - if (U_FAILURE(status)) { - goto cleanup; - } - trst->time = nextTransitionTime; - trst->from = curRule; - trst->to = nextRule; - fHistoricTransitions->adoptElement(trst.orphan(), status); - if (U_FAILURE(status)) { - goto cleanup; - } - lastTransitionTime = nextTransitionTime; - curRule = nextRule; - } - } - if (fFinalRules != NULL) { - if (fHistoricTransitions == NULL) { - LocalPointer lpHistoricTransitions( - new UVector(deleteTransition, nullptr, status), status); - if (U_FAILURE(status)) { - goto cleanup; - } - fHistoricTransitions = lpHistoricTransitions.orphan(); - } - // Append the first transition for each - TimeZoneRule *rule0 = (TimeZoneRule*)fFinalRules->elementAt(0); - TimeZoneRule *rule1 = (TimeZoneRule*)fFinalRules->elementAt(1); - UDate tt0, tt1; - UBool avail0 = rule0->getNextStart(lastTransitionTime, curRule->getRawOffset(), curRule->getDSTSavings(), false, tt0); - UBool avail1 = rule1->getNextStart(lastTransitionTime, curRule->getRawOffset(), curRule->getDSTSavings(), false, tt1); - if (!avail0 || !avail1) { - // Should not happen, because both rules are permanent - status = U_INVALID_STATE_ERROR; - goto cleanup; - } - LocalPointer final0(new Transition, status); - LocalPointer final1(new Transition, status); - if (U_FAILURE(status)) { - goto cleanup; - } - if (tt0 < tt1) { - final0->time = tt0; - final0->from = curRule; - final0->to = rule0; - rule1->getNextStart(tt0, rule0->getRawOffset(), rule0->getDSTSavings(), false, final1->time); - final1->from = rule0; - final1->to = rule1; - } else { - final0->time = tt1; - final0->from = curRule; - final0->to = rule1; - rule0->getNextStart(tt1, rule1->getRawOffset(), rule1->getDSTSavings(), false, final1->time); - final1->from = rule1; - final1->to = rule0; - } - fHistoricTransitions->adoptElement(final0.orphan(), status); - fHistoricTransitions->adoptElement(final1.orphan(), status); - if (U_FAILURE(status)) { - goto cleanup; - } - } - } - fUpToDate = true; - return; - -cleanup: - deleteTransitions(); - fUpToDate = false; -} - -RuleBasedTimeZone* -RuleBasedTimeZone::clone() const { - return new RuleBasedTimeZone(*this); -} - -int32_t -RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, - uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const { - if (U_FAILURE(status)) { - return 0; - } - if (month < UCAL_JANUARY || month > UCAL_DECEMBER) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } else { - return getOffset(era, year, month, day, dayOfWeek, millis, - Grego::monthLength(year, month), status); - } -} - -int32_t -RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, - uint8_t /*dayOfWeek*/, int32_t millis, - int32_t /*monthLength*/, UErrorCode& status) const { - // dayOfWeek and monthLength are unused - if (U_FAILURE(status)) { - return 0; - } - if (era == GregorianCalendar::BC) { - // Convert to extended year - year = 1 - year; - } - int32_t rawOffset, dstOffset; - UDate time = (UDate)Grego::fieldsToDay(year, month, day) * U_MILLIS_PER_DAY + millis; - getOffsetInternal(time, true, kDaylight, kStandard, rawOffset, dstOffset, status); - if (U_FAILURE(status)) { - return 0; - } - return (rawOffset + dstOffset); -} - -void -RuleBasedTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset, - int32_t& dstOffset, UErrorCode& status) const { - getOffsetInternal(date, local, kFormer, kLatter, rawOffset, dstOffset, status); -} - -void RuleBasedTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingTimeOpt, - UTimeZoneLocalOption duplicatedTimeOpt, - int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) const { - getOffsetInternal(date, true, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, status); -} - - -/* - * The internal getOffset implementation - */ -void -RuleBasedTimeZone::getOffsetInternal(UDate date, UBool local, - int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt, - int32_t& rawOffset, int32_t& dstOffset, - UErrorCode& status) const { - rawOffset = 0; - dstOffset = 0; - - if (U_FAILURE(status)) { - return; - } - if (!fUpToDate) { - // Transitions are not yet resolved. We cannot do it here - // because this method is const. Thus, do nothing and return - // error status. - status = U_INVALID_STATE_ERROR; - return; - } - const TimeZoneRule *rule = NULL; - if (fHistoricTransitions == NULL) { - rule = fInitialRule; - } else { - UDate tstart = getTransitionTime((Transition*)fHistoricTransitions->elementAt(0), - local, NonExistingTimeOpt, DuplicatedTimeOpt); - if (date < tstart) { - rule = fInitialRule; - } else { - int32_t idx = fHistoricTransitions->size() - 1; - UDate tend = getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx), - local, NonExistingTimeOpt, DuplicatedTimeOpt); - if (date > tend) { - if (fFinalRules != NULL) { - rule = findRuleInFinal(date, local, NonExistingTimeOpt, DuplicatedTimeOpt); - } - if (rule == NULL) { - // no final rules or the given time is before the first transition - // specified by the final rules -> use the last rule - rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to; - } - } else { - // Find a historical transition - while (idx >= 0) { - if (date >= getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx), - local, NonExistingTimeOpt, DuplicatedTimeOpt)) { - break; - } - idx--; - } - rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to; - } - } - } - if (rule != NULL) { - rawOffset = rule->getRawOffset(); - dstOffset = rule->getDSTSavings(); - } -} - -void -RuleBasedTimeZone::setRawOffset(int32_t /*offsetMillis*/) { - // We don't support this operation at this moment. - // Nothing to do! -} - -int32_t -RuleBasedTimeZone::getRawOffset(void) const { - // Note: This implementation returns standard GMT offset - // as of current time. - UErrorCode status = U_ZERO_ERROR; - int32_t raw, dst; - getOffset(uprv_getUTCtime(), false, raw, dst, status); - return raw; -} - -UBool -RuleBasedTimeZone::useDaylightTime(void) const { - // Note: This implementation returns true when - // daylight saving time is used as of now or - // after the next transition. - UErrorCode status = U_ZERO_ERROR; - UDate now = uprv_getUTCtime(); - int32_t raw, dst; - getOffset(now, false, raw, dst, status); - if (dst != 0) { - return true; - } - // If DST is not used now, check if DST is used after the next transition - UDate time; - TimeZoneRule *from, *to; - UBool avail = findNext(now, false, time, from, to); - if (avail && to->getDSTSavings() != 0) { - return true; - } - return false; -} - -UBool -RuleBasedTimeZone::inDaylightTime(UDate date, UErrorCode& status) const { - if (U_FAILURE(status)) { - return false; - } - int32_t raw, dst; - getOffset(date, false, raw, dst, status); - if (dst != 0) { - return true; - } - return false; -} - -UBool -RuleBasedTimeZone::hasSameRules(const TimeZone& other) const { - if (this == &other) { - return true; - } - if (typeid(*this) != typeid(other)) { - return false; - } - const RuleBasedTimeZone& that = (const RuleBasedTimeZone&)other; - if (*fInitialRule != *(that.fInitialRule)) { - return false; - } - if (compareRules(fHistoricRules, that.fHistoricRules) - && compareRules(fFinalRules, that.fFinalRules)) { - return true; - } - return false; -} - -UBool -RuleBasedTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { - UErrorCode status = U_ZERO_ERROR; - completeConst(status); - if (U_FAILURE(status)) { - return false; - } - UDate transitionTime; - TimeZoneRule *fromRule, *toRule; - UBool found = findNext(base, inclusive, transitionTime, fromRule, toRule); - if (found) { - result.setTime(transitionTime); - result.setFrom((const TimeZoneRule&)*fromRule); - result.setTo((const TimeZoneRule&)*toRule); - return true; - } - return false; -} - -UBool -RuleBasedTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { - UErrorCode status = U_ZERO_ERROR; - completeConst(status); - if (U_FAILURE(status)) { - return false; - } - UDate transitionTime; - TimeZoneRule *fromRule, *toRule; - UBool found = findPrev(base, inclusive, transitionTime, fromRule, toRule); - if (found) { - result.setTime(transitionTime); - result.setFrom((const TimeZoneRule&)*fromRule); - result.setTo((const TimeZoneRule&)*toRule); - return true; - } - return false; -} - -int32_t -RuleBasedTimeZone::countTransitionRules(UErrorCode& /*status*/) const { - int32_t count = 0; - if (fHistoricRules != NULL) { - count += fHistoricRules->size(); - } - if (fFinalRules != NULL) { - count += fFinalRules->size(); - } - return count; -} - -void -RuleBasedTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, - const TimeZoneRule* trsrules[], - int32_t& trscount, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - // Initial rule - initial = fInitialRule; - - // Transition rules - int32_t cnt = 0; - int32_t idx; - if (fHistoricRules != NULL && cnt < trscount) { - int32_t historicCount = fHistoricRules->size(); - idx = 0; - while (cnt < trscount && idx < historicCount) { - trsrules[cnt++] = (const TimeZoneRule*)fHistoricRules->elementAt(idx++); - } - } - if (fFinalRules != NULL && cnt < trscount) { - int32_t finalCount = fFinalRules->size(); - idx = 0; - while (cnt < trscount && idx < finalCount) { - trsrules[cnt++] = (const TimeZoneRule*)fFinalRules->elementAt(idx++); - } - } - // Set the result length - trscount = cnt; -} - -void -RuleBasedTimeZone::deleteRules(void) { - delete fInitialRule; - fInitialRule = NULL; - if (fHistoricRules != NULL) { - delete fHistoricRules; - fHistoricRules = NULL; - } - if (fFinalRules != NULL) { - delete fFinalRules; - fFinalRules = NULL; - } -} - -void -RuleBasedTimeZone::deleteTransitions(void) { - if (fHistoricTransitions != NULL) { - delete fHistoricTransitions; - } - fHistoricTransitions = NULL; -} - -UVector* -RuleBasedTimeZone::copyRules(UVector* source) { - if (source == nullptr) { - return nullptr; - } - UErrorCode ec = U_ZERO_ERROR; - int32_t size = source->size(); - LocalPointer rules(new UVector(uprv_deleteUObject, nullptr, size, ec), ec); - if (U_FAILURE(ec)) { - return nullptr; - } - int32_t i; - for (i = 0; i < size; i++) { - LocalPointer rule(((TimeZoneRule*)source->elementAt(i))->clone(), ec); - rules->adoptElement(rule.orphan(), ec); - if (U_FAILURE(ec)) { - return nullptr; - } - } - return rules.orphan(); -} - -TimeZoneRule* -RuleBasedTimeZone::findRuleInFinal(UDate date, UBool local, - int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const { - if (fFinalRules == NULL) { - return NULL; - } - - AnnualTimeZoneRule* fr0 = (AnnualTimeZoneRule*)fFinalRules->elementAt(0); - AnnualTimeZoneRule* fr1 = (AnnualTimeZoneRule*)fFinalRules->elementAt(1); - if (fr0 == NULL || fr1 == NULL) { - return NULL; - } - - UDate start0, start1; - UDate base; - int32_t localDelta; - - base = date; - if (local) { - localDelta = getLocalDelta(fr1->getRawOffset(), fr1->getDSTSavings(), - fr0->getRawOffset(), fr0->getDSTSavings(), - NonExistingTimeOpt, DuplicatedTimeOpt); - base -= localDelta; - } - UBool avail0 = fr0->getPreviousStart(base, fr1->getRawOffset(), fr1->getDSTSavings(), true, start0); - - base = date; - if (local) { - localDelta = getLocalDelta(fr0->getRawOffset(), fr0->getDSTSavings(), - fr1->getRawOffset(), fr1->getDSTSavings(), - NonExistingTimeOpt, DuplicatedTimeOpt); - base -= localDelta; - } - UBool avail1 = fr1->getPreviousStart(base, fr0->getRawOffset(), fr0->getDSTSavings(), true, start1); - - if (!avail0 || !avail1) { - if (avail0) { - return fr0; - } else if (avail1) { - return fr1; - } - // Both rules take effect after the given time - return NULL; - } - - return (start0 > start1) ? fr0 : fr1; -} - -UBool -RuleBasedTimeZone::findNext(UDate base, UBool inclusive, UDate& transitionTime, - TimeZoneRule*& fromRule, TimeZoneRule*& toRule) const { - if (fHistoricTransitions == NULL) { - return false; - } - UBool isFinal = false; - UBool found = false; - Transition result; - Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0); - UDate tt = tzt->time; - if (tt > base || (inclusive && tt == base)) { - result = *tzt; - found = true; - } else { - int32_t idx = fHistoricTransitions->size() - 1; - tzt = (Transition*)fHistoricTransitions->elementAt(idx); - tt = tzt->time; - if (inclusive && tt == base) { - result = *tzt; - found = true; - } else if (tt <= base) { - if (fFinalRules != NULL) { - // Find a transion time with finalRules - TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0); - TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1); - UDate start0, start1; - UBool avail0 = r0->getNextStart(base, r1->getRawOffset(), r1->getDSTSavings(), inclusive, start0); - UBool avail1 = r1->getNextStart(base, r0->getRawOffset(), r0->getDSTSavings(), inclusive, start1); - // avail0/avail1 should be always true - if (!avail0 && !avail1) { - return false; - } - if (!avail1 || start0 < start1) { - result.time = start0; - result.from = r1; - result.to = r0; - } else { - result.time = start1; - result.from = r0; - result.to = r1; - } - isFinal = true; - found = true; - } - } else { - // Find a transition within the historic transitions - idx--; - Transition *prev = tzt; - while (idx > 0) { - tzt = (Transition*)fHistoricTransitions->elementAt(idx); - tt = tzt->time; - if (tt < base || (!inclusive && tt == base)) { - break; - } - idx--; - prev = tzt; - } - result.time = prev->time; - result.from = prev->from; - result.to = prev->to; - found = true; - } - } - if (found) { - // For now, this implementation ignore transitions with only zone name changes. - if (result.from->getRawOffset() == result.to->getRawOffset() - && result.from->getDSTSavings() == result.to->getDSTSavings()) { - if (isFinal) { - return false; - } else { - // No offset changes. Try next one if not final - return findNext(result.time, false /* always exclusive */, - transitionTime, fromRule, toRule); - } - } - transitionTime = result.time; - fromRule = result.from; - toRule = result.to; - return true; - } - return false; -} - -UBool -RuleBasedTimeZone::findPrev(UDate base, UBool inclusive, UDate& transitionTime, - TimeZoneRule*& fromRule, TimeZoneRule*& toRule) const { - if (fHistoricTransitions == NULL) { - return false; - } - UBool found = false; - Transition result; - Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0); - UDate tt = tzt->time; - if (inclusive && tt == base) { - result = *tzt; - found = true; - } else if (tt < base) { - int32_t idx = fHistoricTransitions->size() - 1; - tzt = (Transition*)fHistoricTransitions->elementAt(idx); - tt = tzt->time; - if (inclusive && tt == base) { - result = *tzt; - found = true; - } else if (tt < base) { - if (fFinalRules != NULL) { - // Find a transion time with finalRules - TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0); - TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1); - UDate start0, start1; - UBool avail0 = r0->getPreviousStart(base, r1->getRawOffset(), r1->getDSTSavings(), inclusive, start0); - UBool avail1 = r1->getPreviousStart(base, r0->getRawOffset(), r0->getDSTSavings(), inclusive, start1); - // avail0/avail1 should be always true - if (!avail0 && !avail1) { - return false; - } - if (!avail1 || start0 > start1) { - result.time = start0; - result.from = r1; - result.to = r0; - } else { - result.time = start1; - result.from = r0; - result.to = r1; - } - } else { - result = *tzt; - } - found = true; - } else { - // Find a transition within the historic transitions - idx--; - while (idx >= 0) { - tzt = (Transition*)fHistoricTransitions->elementAt(idx); - tt = tzt->time; - if (tt < base || (inclusive && tt == base)) { - break; - } - idx--; - } - result = *tzt; - found = true; - } - } - if (found) { - // For now, this implementation ignore transitions with only zone name changes. - if (result.from->getRawOffset() == result.to->getRawOffset() - && result.from->getDSTSavings() == result.to->getDSTSavings()) { - // No offset changes. Try next one if not final - return findPrev(result.time, false /* always exclusive */, - transitionTime, fromRule, toRule); - } - transitionTime = result.time; - fromRule = result.from; - toRule = result.to; - return true; - } - return false; -} - -UDate -RuleBasedTimeZone::getTransitionTime(Transition* transition, UBool local, - int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const { - UDate time = transition->time; - if (local) { - time += getLocalDelta(transition->from->getRawOffset(), transition->from->getDSTSavings(), - transition->to->getRawOffset(), transition->to->getDSTSavings(), - NonExistingTimeOpt, DuplicatedTimeOpt); - } - return time; -} - -int32_t -RuleBasedTimeZone::getLocalDelta(int32_t rawBefore, int32_t dstBefore, int32_t rawAfter, int32_t dstAfter, - int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const { - int32_t delta = 0; - - int32_t offsetBefore = rawBefore + dstBefore; - int32_t offsetAfter = rawAfter + dstAfter; - - UBool dstToStd = (dstBefore != 0) && (dstAfter == 0); - UBool stdToDst = (dstBefore == 0) && (dstAfter != 0); - - if (offsetAfter - offsetBefore >= 0) { - // Positive transition, which makes a non-existing local time range - if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd) - || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) { - delta = offsetBefore; - } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst) - || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) { - delta = offsetAfter; - } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) { - delta = offsetBefore; - } else { - // Interprets the time with rule before the transition, - // default for non-existing time range - delta = offsetAfter; - } - } else { - // Negative transition, which makes a duplicated local time range - if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd) - || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) { - delta = offsetAfter; - } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst) - || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) { - delta = offsetBefore; - } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) { - delta = offsetBefore; - } else { - // Interprets the time with rule after the transition, - // default for duplicated local time range - delta = offsetAfter; - } - } - return delta; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2007-2013, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +*/ + +#include "utypeinfo.h" // for 'typeid' to work + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/rbtz.h" +#include "unicode/gregocal.h" +#include "uvector.h" +#include "gregoimp.h" +#include "cmemory.h" +#include "umutex.h" + +U_NAMESPACE_BEGIN + +/** + * A struct representing a time zone transition + */ +struct Transition : public UMemory { + UDate time; + TimeZoneRule* from; + TimeZoneRule* to; +}; + +U_CDECL_BEGIN +static void U_CALLCONV +deleteTransition(void* obj) { + delete static_cast(obj); +} +U_CDECL_END + +static UBool compareRules(UVector* rules1, UVector* rules2) { + if (rules1 == nullptr && rules2 == nullptr) { + return true; + } else if (rules1 == nullptr || rules2 == nullptr) { + return false; + } + int32_t size = rules1->size(); + if (size != rules2->size()) { + return false; + } + for (int32_t i = 0; i < size; i++) { + TimeZoneRule *r1 = (TimeZoneRule*)rules1->elementAt(i); + TimeZoneRule *r2 = (TimeZoneRule*)rules2->elementAt(i); + if (*r1 != *r2) { + return false; + } + } + return true; +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTimeZone) + +RuleBasedTimeZone::RuleBasedTimeZone(const UnicodeString& id, InitialTimeZoneRule* initialRule) +: BasicTimeZone(id), fInitialRule(initialRule), fHistoricRules(nullptr), fFinalRules(nullptr), + fHistoricTransitions(nullptr), fUpToDate(false) { +} + +RuleBasedTimeZone::RuleBasedTimeZone(const RuleBasedTimeZone& source) +: BasicTimeZone(source), fInitialRule(source.fInitialRule->clone()), + fHistoricTransitions(nullptr), fUpToDate(false) { + fHistoricRules = copyRules(source.fHistoricRules); + fFinalRules = copyRules(source.fFinalRules); + if (source.fUpToDate) { + UErrorCode status = U_ZERO_ERROR; + complete(status); + } +} + +RuleBasedTimeZone::~RuleBasedTimeZone() { + deleteTransitions(); + deleteRules(); +} + +RuleBasedTimeZone& +RuleBasedTimeZone::operator=(const RuleBasedTimeZone& right) { + if (*this != right) { + BasicTimeZone::operator=(right); + deleteRules(); + fInitialRule = right.fInitialRule->clone(); + fHistoricRules = copyRules(right.fHistoricRules); + fFinalRules = copyRules(right.fFinalRules); + deleteTransitions(); + fUpToDate = false; + } + return *this; +} + +bool +RuleBasedTimeZone::operator==(const TimeZone& that) const { + if (this == &that) { + return true; + } + if (typeid(*this) != typeid(that) || !BasicTimeZone::operator==(that)) { + return false; + } + RuleBasedTimeZone *rbtz = (RuleBasedTimeZone*)&that; + if (*fInitialRule != *(rbtz->fInitialRule)) { + return false; + } + if (compareRules(fHistoricRules, rbtz->fHistoricRules) + && compareRules(fFinalRules, rbtz->fFinalRules)) { + return true; + } + return false; +} + +bool +RuleBasedTimeZone::operator!=(const TimeZone& that) const { + return !operator==(that); +} + +void +RuleBasedTimeZone::addTransitionRule(TimeZoneRule* rule, UErrorCode& status) { + LocalPointerlpRule(rule); + if (U_FAILURE(status)) { + return; + } + AnnualTimeZoneRule* atzrule = dynamic_cast(rule); + if (atzrule != nullptr && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) { + // A final rule + if (fFinalRules == nullptr) { + LocalPointer lpFinalRules(new UVector(uprv_deleteUObject, nullptr, status), status); + if (U_FAILURE(status)) { + return; + } + fFinalRules = lpFinalRules.orphan(); + } else if (fFinalRules->size() >= 2) { + // Cannot handle more than two final rules + status = U_INVALID_STATE_ERROR; + return; + } + fFinalRules->adoptElement(lpRule.orphan(), status); + } else { + // Non-final rule + if (fHistoricRules == nullptr) { + LocalPointer lpHistoricRules(new UVector(uprv_deleteUObject, nullptr, status), status); + if (U_FAILURE(status)) { + return; + } + fHistoricRules = lpHistoricRules.orphan(); + } + fHistoricRules->adoptElement(lpRule.orphan(), status); + } + // Mark dirty, so transitions are recalculated at next complete() call + fUpToDate = false; +} + + +void +RuleBasedTimeZone::completeConst(UErrorCode& status) const { + static UMutex gLock; + if (U_FAILURE(status)) { + return; + } + umtx_lock(&gLock); + if (!fUpToDate) { + RuleBasedTimeZone *ncThis = const_cast(this); + ncThis->complete(status); + } + umtx_unlock(&gLock); +} + +void +RuleBasedTimeZone::complete(UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (fUpToDate) { + return; + } + // Make sure either no final rules or a pair of AnnualTimeZoneRules + // are available. + if (fFinalRules != nullptr && fFinalRules->size() != 2) { + status = U_INVALID_STATE_ERROR; + return; + } + + // Create a TimezoneTransition and add to the list + if (fHistoricRules != nullptr || fFinalRules != nullptr) { + TimeZoneRule *curRule = fInitialRule; + UDate lastTransitionTime = MIN_MILLIS; + + // Build the transition array which represents historical time zone + // transitions. + if (fHistoricRules != nullptr && fHistoricRules->size() > 0) { + int32_t i; + int32_t historicCount = fHistoricRules->size(); + LocalMemory done((bool *)uprv_malloc(sizeof(bool) * historicCount)); + if (done == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + goto cleanup; + } + for (i = 0; i < historicCount; i++) { + done[i] = false; + } + while (true) { + int32_t curStdOffset = curRule->getRawOffset(); + int32_t curDstSavings = curRule->getDSTSavings(); + UDate nextTransitionTime = MAX_MILLIS; + TimeZoneRule *nextRule = nullptr; + TimeZoneRule *r = nullptr; + UBool avail; + UDate tt; + UnicodeString curName, name; + curRule->getName(curName); + + for (i = 0; i < historicCount; i++) { + if (done[i]) { + continue; + } + r = (TimeZoneRule*)fHistoricRules->elementAt(i); + avail = r->getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false, tt); + if (!avail) { + // No more transitions from this rule - skip this rule next time + done[i] = true; + } else { + r->getName(name); + if (*r == *curRule || + (name == curName && r->getRawOffset() == curRule->getRawOffset() + && r->getDSTSavings() == curRule->getDSTSavings())) { + continue; + } + if (tt < nextTransitionTime) { + nextTransitionTime = tt; + nextRule = r; + } + } + } + + if (nextRule == nullptr) { + // Check if all historic rules are done + UBool bDoneAll = true; + for (int32_t j = 0; j < historicCount; j++) { + if (!done[j]) { + bDoneAll = false; + break; + } + } + if (bDoneAll) { + break; + } + } + + if (fFinalRules != nullptr) { + // Check if one of final rules has earlier transition date + for (i = 0; i < 2 /* fFinalRules->size() */; i++) { + TimeZoneRule *fr = (TimeZoneRule*)fFinalRules->elementAt(i); + if (*fr == *curRule) { + continue; + } + r = (TimeZoneRule*)fFinalRules->elementAt(i); + avail = r->getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false, tt); + if (avail) { + if (tt < nextTransitionTime) { + nextTransitionTime = tt; + nextRule = r; + } + } + } + } + + if (nextRule == nullptr) { + // Nothing more + break; + } + + if (fHistoricTransitions == nullptr) { + LocalPointer lpHistoricTransitions( + new UVector(deleteTransition, nullptr, status), status); + if (U_FAILURE(status)) { + goto cleanup; + } + fHistoricTransitions = lpHistoricTransitions.orphan(); + } + LocalPointer trst(new Transition, status); + if (U_FAILURE(status)) { + goto cleanup; + } + trst->time = nextTransitionTime; + trst->from = curRule; + trst->to = nextRule; + fHistoricTransitions->adoptElement(trst.orphan(), status); + if (U_FAILURE(status)) { + goto cleanup; + } + lastTransitionTime = nextTransitionTime; + curRule = nextRule; + } + } + if (fFinalRules != nullptr) { + if (fHistoricTransitions == nullptr) { + LocalPointer lpHistoricTransitions( + new UVector(deleteTransition, nullptr, status), status); + if (U_FAILURE(status)) { + goto cleanup; + } + fHistoricTransitions = lpHistoricTransitions.orphan(); + } + // Append the first transition for each + TimeZoneRule *rule0 = (TimeZoneRule*)fFinalRules->elementAt(0); + TimeZoneRule *rule1 = (TimeZoneRule*)fFinalRules->elementAt(1); + UDate tt0, tt1; + UBool avail0 = rule0->getNextStart(lastTransitionTime, curRule->getRawOffset(), curRule->getDSTSavings(), false, tt0); + UBool avail1 = rule1->getNextStart(lastTransitionTime, curRule->getRawOffset(), curRule->getDSTSavings(), false, tt1); + if (!avail0 || !avail1) { + // Should not happen, because both rules are permanent + status = U_INVALID_STATE_ERROR; + goto cleanup; + } + LocalPointer final0(new Transition, status); + LocalPointer final1(new Transition, status); + if (U_FAILURE(status)) { + goto cleanup; + } + if (tt0 < tt1) { + final0->time = tt0; + final0->from = curRule; + final0->to = rule0; + rule1->getNextStart(tt0, rule0->getRawOffset(), rule0->getDSTSavings(), false, final1->time); + final1->from = rule0; + final1->to = rule1; + } else { + final0->time = tt1; + final0->from = curRule; + final0->to = rule1; + rule0->getNextStart(tt1, rule1->getRawOffset(), rule1->getDSTSavings(), false, final1->time); + final1->from = rule1; + final1->to = rule0; + } + fHistoricTransitions->adoptElement(final0.orphan(), status); + fHistoricTransitions->adoptElement(final1.orphan(), status); + if (U_FAILURE(status)) { + goto cleanup; + } + } + } + fUpToDate = true; + return; + +cleanup: + deleteTransitions(); + fUpToDate = false; +} + +RuleBasedTimeZone* +RuleBasedTimeZone::clone() const { + return new RuleBasedTimeZone(*this); +} + +int32_t +RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, + uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const { + if (U_FAILURE(status)) { + return 0; + } + if (month < UCAL_JANUARY || month > UCAL_DECEMBER) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } else { + return getOffset(era, year, month, day, dayOfWeek, millis, + Grego::monthLength(year, month), status); + } +} + +int32_t +RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, + uint8_t /*dayOfWeek*/, int32_t millis, + int32_t /*monthLength*/, UErrorCode& status) const { + // dayOfWeek and monthLength are unused + if (U_FAILURE(status)) { + return 0; + } + if (era == GregorianCalendar::BC) { + // Convert to extended year + year = 1 - year; + } + int32_t rawOffset, dstOffset; + UDate time = (UDate)Grego::fieldsToDay(year, month, day) * U_MILLIS_PER_DAY + millis; + getOffsetInternal(time, true, kDaylight, kStandard, rawOffset, dstOffset, status); + if (U_FAILURE(status)) { + return 0; + } + return (rawOffset + dstOffset); +} + +void +RuleBasedTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset, + int32_t& dstOffset, UErrorCode& status) const { + getOffsetInternal(date, local, kFormer, kLatter, rawOffset, dstOffset, status); +} + +void RuleBasedTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingTimeOpt, + UTimeZoneLocalOption duplicatedTimeOpt, + int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) const { + getOffsetInternal(date, true, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, status); +} + + +/* + * The internal getOffset implementation + */ +void +RuleBasedTimeZone::getOffsetInternal(UDate date, UBool local, + int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt, + int32_t& rawOffset, int32_t& dstOffset, + UErrorCode& status) const { + rawOffset = 0; + dstOffset = 0; + + if (U_FAILURE(status)) { + return; + } + if (!fUpToDate) { + // Transitions are not yet resolved. We cannot do it here + // because this method is const. Thus, do nothing and return + // error status. + status = U_INVALID_STATE_ERROR; + return; + } + const TimeZoneRule *rule = nullptr; + if (fHistoricTransitions == nullptr) { + rule = fInitialRule; + } else { + UDate tstart = getTransitionTime((Transition*)fHistoricTransitions->elementAt(0), + local, NonExistingTimeOpt, DuplicatedTimeOpt); + if (date < tstart) { + rule = fInitialRule; + } else { + int32_t idx = fHistoricTransitions->size() - 1; + UDate tend = getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx), + local, NonExistingTimeOpt, DuplicatedTimeOpt); + if (date > tend) { + if (fFinalRules != nullptr) { + rule = findRuleInFinal(date, local, NonExistingTimeOpt, DuplicatedTimeOpt); + } + if (rule == nullptr) { + // no final rules or the given time is before the first transition + // specified by the final rules -> use the last rule + rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to; + } + } else { + // Find a historical transition + while (idx >= 0) { + if (date >= getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx), + local, NonExistingTimeOpt, DuplicatedTimeOpt)) { + break; + } + idx--; + } + rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to; + } + } + } + if (rule != nullptr) { + rawOffset = rule->getRawOffset(); + dstOffset = rule->getDSTSavings(); + } +} + +void +RuleBasedTimeZone::setRawOffset(int32_t /*offsetMillis*/) { + // We don't support this operation at this moment. + // Nothing to do! +} + +int32_t +RuleBasedTimeZone::getRawOffset() const { + // Note: This implementation returns standard GMT offset + // as of current time. + UErrorCode status = U_ZERO_ERROR; + int32_t raw, dst; + getOffset(uprv_getUTCtime(), false, raw, dst, status); + return raw; +} + +UBool +RuleBasedTimeZone::useDaylightTime() const { + // Note: This implementation returns true when + // daylight saving time is used as of now or + // after the next transition. + UErrorCode status = U_ZERO_ERROR; + UDate now = uprv_getUTCtime(); + int32_t raw, dst; + getOffset(now, false, raw, dst, status); + if (dst != 0) { + return true; + } + // If DST is not used now, check if DST is used after the next transition + UDate time; + TimeZoneRule *from, *to; + UBool avail = findNext(now, false, time, from, to); + if (avail && to->getDSTSavings() != 0) { + return true; + } + return false; +} + +UBool +RuleBasedTimeZone::inDaylightTime(UDate date, UErrorCode& status) const { + if (U_FAILURE(status)) { + return false; + } + int32_t raw, dst; + getOffset(date, false, raw, dst, status); + if (dst != 0) { + return true; + } + return false; +} + +UBool +RuleBasedTimeZone::hasSameRules(const TimeZone& other) const { + if (this == &other) { + return true; + } + if (typeid(*this) != typeid(other)) { + return false; + } + const RuleBasedTimeZone& that = static_cast(other); + if (*fInitialRule != *(that.fInitialRule)) { + return false; + } + if (compareRules(fHistoricRules, that.fHistoricRules) + && compareRules(fFinalRules, that.fFinalRules)) { + return true; + } + return false; +} + +UBool +RuleBasedTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { + UErrorCode status = U_ZERO_ERROR; + completeConst(status); + if (U_FAILURE(status)) { + return false; + } + UDate transitionTime; + TimeZoneRule *fromRule, *toRule; + UBool found = findNext(base, inclusive, transitionTime, fromRule, toRule); + if (found) { + result.setTime(transitionTime); + result.setFrom(*fromRule); + result.setTo(*toRule); + return true; + } + return false; +} + +UBool +RuleBasedTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { + UErrorCode status = U_ZERO_ERROR; + completeConst(status); + if (U_FAILURE(status)) { + return false; + } + UDate transitionTime; + TimeZoneRule *fromRule, *toRule; + UBool found = findPrev(base, inclusive, transitionTime, fromRule, toRule); + if (found) { + result.setTime(transitionTime); + result.setFrom(*fromRule); + result.setTo(*toRule); + return true; + } + return false; +} + +int32_t +RuleBasedTimeZone::countTransitionRules(UErrorCode& /*status*/) const { + int32_t count = 0; + if (fHistoricRules != nullptr) { + count += fHistoricRules->size(); + } + if (fFinalRules != nullptr) { + count += fFinalRules->size(); + } + return count; +} + +void +RuleBasedTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, + const TimeZoneRule* trsrules[], + int32_t& trscount, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + // Initial rule + initial = fInitialRule; + + // Transition rules + int32_t cnt = 0; + int32_t idx; + if (fHistoricRules != nullptr && cnt < trscount) { + int32_t historicCount = fHistoricRules->size(); + idx = 0; + while (cnt < trscount && idx < historicCount) { + trsrules[cnt++] = (const TimeZoneRule*)fHistoricRules->elementAt(idx++); + } + } + if (fFinalRules != nullptr && cnt < trscount) { + int32_t finalCount = fFinalRules->size(); + idx = 0; + while (cnt < trscount && idx < finalCount) { + trsrules[cnt++] = (const TimeZoneRule*)fFinalRules->elementAt(idx++); + } + } + // Set the result length + trscount = cnt; +} + +void +RuleBasedTimeZone::deleteRules() { + delete fInitialRule; + fInitialRule = nullptr; + if (fHistoricRules != nullptr) { + delete fHistoricRules; + fHistoricRules = nullptr; + } + if (fFinalRules != nullptr) { + delete fFinalRules; + fFinalRules = nullptr; + } +} + +void +RuleBasedTimeZone::deleteTransitions() { + if (fHistoricTransitions != nullptr) { + delete fHistoricTransitions; + } + fHistoricTransitions = nullptr; +} + +UVector* +RuleBasedTimeZone::copyRules(UVector* source) { + if (source == nullptr) { + return nullptr; + } + UErrorCode ec = U_ZERO_ERROR; + int32_t size = source->size(); + LocalPointer rules(new UVector(uprv_deleteUObject, nullptr, size, ec), ec); + if (U_FAILURE(ec)) { + return nullptr; + } + int32_t i; + for (i = 0; i < size; i++) { + LocalPointer rule(((TimeZoneRule*)source->elementAt(i))->clone(), ec); + rules->adoptElement(rule.orphan(), ec); + if (U_FAILURE(ec)) { + return nullptr; + } + } + return rules.orphan(); +} + +TimeZoneRule* +RuleBasedTimeZone::findRuleInFinal(UDate date, UBool local, + int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const { + if (fFinalRules == nullptr) { + return nullptr; + } + + AnnualTimeZoneRule* fr0 = (AnnualTimeZoneRule*)fFinalRules->elementAt(0); + AnnualTimeZoneRule* fr1 = (AnnualTimeZoneRule*)fFinalRules->elementAt(1); + if (fr0 == nullptr || fr1 == nullptr) { + return nullptr; + } + + UDate start0, start1; + UDate base; + int32_t localDelta; + + base = date; + if (local) { + localDelta = getLocalDelta(fr1->getRawOffset(), fr1->getDSTSavings(), + fr0->getRawOffset(), fr0->getDSTSavings(), + NonExistingTimeOpt, DuplicatedTimeOpt); + base -= localDelta; + } + UBool avail0 = fr0->getPreviousStart(base, fr1->getRawOffset(), fr1->getDSTSavings(), true, start0); + + base = date; + if (local) { + localDelta = getLocalDelta(fr0->getRawOffset(), fr0->getDSTSavings(), + fr1->getRawOffset(), fr1->getDSTSavings(), + NonExistingTimeOpt, DuplicatedTimeOpt); + base -= localDelta; + } + UBool avail1 = fr1->getPreviousStart(base, fr0->getRawOffset(), fr0->getDSTSavings(), true, start1); + + if (!avail0 || !avail1) { + if (avail0) { + return fr0; + } else if (avail1) { + return fr1; + } + // Both rules take effect after the given time + return nullptr; + } + + return (start0 > start1) ? fr0 : fr1; +} + +UBool +RuleBasedTimeZone::findNext(UDate base, UBool inclusive, UDate& transitionTime, + TimeZoneRule*& fromRule, TimeZoneRule*& toRule) const { + if (fHistoricTransitions == nullptr) { + return false; + } + UBool isFinal = false; + UBool found = false; + Transition result; + Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0); + UDate tt = tzt->time; + if (tt > base || (inclusive && tt == base)) { + result = *tzt; + found = true; + } else { + int32_t idx = fHistoricTransitions->size() - 1; + tzt = (Transition*)fHistoricTransitions->elementAt(idx); + tt = tzt->time; + if (inclusive && tt == base) { + result = *tzt; + found = true; + } else if (tt <= base) { + if (fFinalRules != nullptr) { + // Find a transion time with finalRules + TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0); + TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1); + UDate start0, start1; + UBool avail0 = r0->getNextStart(base, r1->getRawOffset(), r1->getDSTSavings(), inclusive, start0); + UBool avail1 = r1->getNextStart(base, r0->getRawOffset(), r0->getDSTSavings(), inclusive, start1); + // avail0/avail1 should be always true + if (!avail0 && !avail1) { + return false; + } + if (!avail1 || start0 < start1) { + result.time = start0; + result.from = r1; + result.to = r0; + } else { + result.time = start1; + result.from = r0; + result.to = r1; + } + isFinal = true; + found = true; + } + } else { + // Find a transition within the historic transitions + idx--; + Transition *prev = tzt; + while (idx > 0) { + tzt = (Transition*)fHistoricTransitions->elementAt(idx); + tt = tzt->time; + if (tt < base || (!inclusive && tt == base)) { + break; + } + idx--; + prev = tzt; + } + result.time = prev->time; + result.from = prev->from; + result.to = prev->to; + found = true; + } + } + if (found) { + // For now, this implementation ignore transitions with only zone name changes. + if (result.from->getRawOffset() == result.to->getRawOffset() + && result.from->getDSTSavings() == result.to->getDSTSavings()) { + if (isFinal) { + return false; + } else { + // No offset changes. Try next one if not final + return findNext(result.time, false /* always exclusive */, + transitionTime, fromRule, toRule); + } + } + transitionTime = result.time; + fromRule = result.from; + toRule = result.to; + return true; + } + return false; +} + +UBool +RuleBasedTimeZone::findPrev(UDate base, UBool inclusive, UDate& transitionTime, + TimeZoneRule*& fromRule, TimeZoneRule*& toRule) const { + if (fHistoricTransitions == nullptr) { + return false; + } + UBool found = false; + Transition result; + Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0); + UDate tt = tzt->time; + if (inclusive && tt == base) { + result = *tzt; + found = true; + } else if (tt < base) { + int32_t idx = fHistoricTransitions->size() - 1; + tzt = (Transition*)fHistoricTransitions->elementAt(idx); + tt = tzt->time; + if (inclusive && tt == base) { + result = *tzt; + found = true; + } else if (tt < base) { + if (fFinalRules != nullptr) { + // Find a transion time with finalRules + TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0); + TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1); + UDate start0, start1; + UBool avail0 = r0->getPreviousStart(base, r1->getRawOffset(), r1->getDSTSavings(), inclusive, start0); + UBool avail1 = r1->getPreviousStart(base, r0->getRawOffset(), r0->getDSTSavings(), inclusive, start1); + // avail0/avail1 should be always true + if (!avail0 && !avail1) { + return false; + } + if (!avail1 || start0 > start1) { + result.time = start0; + result.from = r1; + result.to = r0; + } else { + result.time = start1; + result.from = r0; + result.to = r1; + } + } else { + result = *tzt; + } + found = true; + } else { + // Find a transition within the historic transitions + idx--; + while (idx >= 0) { + tzt = (Transition*)fHistoricTransitions->elementAt(idx); + tt = tzt->time; + if (tt < base || (inclusive && tt == base)) { + break; + } + idx--; + } + result = *tzt; + found = true; + } + } + if (found) { + // For now, this implementation ignore transitions with only zone name changes. + if (result.from->getRawOffset() == result.to->getRawOffset() + && result.from->getDSTSavings() == result.to->getDSTSavings()) { + // No offset changes. Try next one if not final + return findPrev(result.time, false /* always exclusive */, + transitionTime, fromRule, toRule); + } + transitionTime = result.time; + fromRule = result.from; + toRule = result.to; + return true; + } + return false; +} + +UDate +RuleBasedTimeZone::getTransitionTime(Transition* transition, UBool local, + int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const { + UDate time = transition->time; + if (local) { + time += getLocalDelta(transition->from->getRawOffset(), transition->from->getDSTSavings(), + transition->to->getRawOffset(), transition->to->getDSTSavings(), + NonExistingTimeOpt, DuplicatedTimeOpt); + } + return time; +} + +int32_t +RuleBasedTimeZone::getLocalDelta(int32_t rawBefore, int32_t dstBefore, int32_t rawAfter, int32_t dstAfter, + int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const { + int32_t delta = 0; + + int32_t offsetBefore = rawBefore + dstBefore; + int32_t offsetAfter = rawAfter + dstAfter; + + UBool dstToStd = (dstBefore != 0) && (dstAfter == 0); + UBool stdToDst = (dstBefore == 0) && (dstAfter != 0); + + if (offsetAfter - offsetBefore >= 0) { + // Positive transition, which makes a non-existing local time range + if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd) + || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) { + delta = offsetBefore; + } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst) + || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) { + delta = offsetAfter; + } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) { + delta = offsetBefore; + } else { + // Interprets the time with rule before the transition, + // default for non-existing time range + delta = offsetAfter; + } + } else { + // Negative transition, which makes a duplicated local time range + if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd) + || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) { + delta = offsetAfter; + } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst) + || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) { + delta = offsetBefore; + } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) { + delta = offsetBefore; + } else { + // Interprets the time with rule after the transition, + // default for duplicated local time range + delta = offsetAfter; + } + } + return delta; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof + diff --git a/deps/icu-small/source/i18n/regexcmp.cpp b/deps/icu-small/source/i18n/regexcmp.cpp index 4b507002d63a40..b7dd7764fd3054 100644 --- a/deps/icu-small/source/i18n/regexcmp.cpp +++ b/deps/icu-small/source/i18n/regexcmp.cpp @@ -1,4675 +1,4675 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// file: regexcmp.cpp -// -// Copyright (C) 2002-2016 International Business Machines Corporation and others. -// All Rights Reserved. -// -// This file contains the ICU regular expression compiler, which is responsible -// for processing a regular expression pattern into the compiled form that -// is used by the match finding engine. -// - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_REGULAR_EXPRESSIONS - -#include "unicode/ustring.h" -#include "unicode/unistr.h" -#include "unicode/uniset.h" -#include "unicode/uchar.h" -#include "unicode/uchriter.h" -#include "unicode/parsepos.h" -#include "unicode/parseerr.h" -#include "unicode/regex.h" -#include "unicode/utf.h" -#include "unicode/utf16.h" -#include "patternprops.h" -#include "putilimp.h" -#include "cmemory.h" -#include "cstr.h" -#include "cstring.h" -#include "uvectr32.h" -#include "uvectr64.h" -#include "uassert.h" -#include "uinvchar.h" - -#include "regeximp.h" -#include "regexcst.h" // Contains state table for the regex pattern parser. - // generated by a Perl script. -#include "regexcmp.h" -#include "regexst.h" -#include "regextxt.h" - - - -U_NAMESPACE_BEGIN - - -//------------------------------------------------------------------------------ -// -// Constructor. -// -//------------------------------------------------------------------------------ -RegexCompile::RegexCompile(RegexPattern *rxp, UErrorCode &status) : - fParenStack(status), fSetStack(uprv_deleteUObject, nullptr, status), fSetOpStack(status) -{ - // Lazy init of all shared global sets (needed for init()'s empty text) - RegexStaticSets::initGlobals(&status); - - fStatus = &status; - - fRXPat = rxp; - fScanIndex = 0; - fLastChar = -1; - fPeekChar = -1; - fLineNum = 1; - fCharNum = 0; - fQuoteMode = false; - fInBackslashQuote = false; - fModeFlags = fRXPat->fFlags | 0x80000000; - fEOLComments = true; - - fMatchOpenParen = -1; - fMatchCloseParen = -1; - fCaptureName = NULL; - fLastSetLiteral = U_SENTINEL; - - if (U_SUCCESS(status) && U_FAILURE(rxp->fDeferredStatus)) { - status = rxp->fDeferredStatus; - } -} - -static const UChar chAmp = 0x26; // '&' -static const UChar chDash = 0x2d; // '-' - - -//------------------------------------------------------------------------------ -// -// Destructor -// -//------------------------------------------------------------------------------ -RegexCompile::~RegexCompile() { - delete fCaptureName; // Normally will be NULL, but can exist if pattern - // compilation stops with a syntax error. -} - -static inline void addCategory(UnicodeSet *set, int32_t value, UErrorCode& ec) { - set->addAll(UnicodeSet().applyIntPropertyValue(UCHAR_GENERAL_CATEGORY_MASK, value, ec)); -} - -//------------------------------------------------------------------------------ -// -// Compile regex pattern. The state machine for rexexp pattern parsing is here. -// The state tables are hand-written in the file regexcst.txt, -// and converted to the form used here by a perl -// script regexcst.pl -// -//------------------------------------------------------------------------------ -void RegexCompile::compile( - const UnicodeString &pat, // Source pat to be compiled. - UParseError &pp, // Error position info - UErrorCode &e) // Error Code -{ - fRXPat->fPatternString = new UnicodeString(pat); - UText patternText = UTEXT_INITIALIZER; - utext_openConstUnicodeString(&patternText, fRXPat->fPatternString, &e); - - if (U_SUCCESS(e)) { - compile(&patternText, pp, e); - utext_close(&patternText); - } -} - -// -// compile, UText mode -// All the work is actually done here. -// -void RegexCompile::compile( - UText *pat, // Source pat to be compiled. - UParseError &pp, // Error position info - UErrorCode &e) // Error Code -{ - fStatus = &e; - fParseErr = &pp; - fStackPtr = 0; - fStack[fStackPtr] = 0; - - if (U_FAILURE(*fStatus)) { - return; - } - - // There should be no pattern stuff in the RegexPattern object. They can not be reused. - U_ASSERT(fRXPat->fPattern == NULL || utext_nativeLength(fRXPat->fPattern) == 0); - - // Prepare the RegexPattern object to receive the compiled pattern. - fRXPat->fPattern = utext_clone(fRXPat->fPattern, pat, false, true, fStatus); - if (U_FAILURE(*fStatus)) { - return; - } - - // Initialize the pattern scanning state machine - fPatternLength = utext_nativeLength(pat); - uint16_t state = 1; - const RegexTableEl *tableEl; - - // UREGEX_LITERAL force entire pattern to be treated as a literal string. - if (fModeFlags & UREGEX_LITERAL) { - fQuoteMode = true; - } - - nextChar(fC); // Fetch the first char from the pattern string. - - // - // Main loop for the regex pattern parsing state machine. - // Runs once per state transition. - // Each time through optionally performs, depending on the state table, - // - an advance to the the next pattern char - // - an action to be performed. - // - pushing or popping a state to/from the local state return stack. - // file regexcst.txt is the source for the state table. The logic behind - // recongizing the pattern syntax is there, not here. - // - for (;;) { - // Bail out if anything has gone wrong. - // Regex pattern parsing stops on the first error encountered. - if (U_FAILURE(*fStatus)) { - break; - } - - U_ASSERT(state != 0); - - // Find the state table element that matches the input char from the pattern, or the - // class of the input character. Start with the first table row for this - // state, then linearly scan forward until we find a row that matches the - // character. The last row for each state always matches all characters, so - // the search will stop there, if not before. - // - tableEl = &gRuleParseStateTable[state]; - REGEX_SCAN_DEBUG_PRINTF(("char, line, col = (\'%c\', %d, %d) state=%s ", - fC.fChar, fLineNum, fCharNum, RegexStateNames[state])); - - for (;;) { // loop through table rows belonging to this state, looking for one - // that matches the current input char. - REGEX_SCAN_DEBUG_PRINTF((".")); - if (tableEl->fCharClass < 127 && fC.fQuoted == false && tableEl->fCharClass == fC.fChar) { - // Table row specified an individual character, not a set, and - // the input character is not quoted, and - // the input character matched it. - break; - } - if (tableEl->fCharClass == 255) { - // Table row specified default, match anything character class. - break; - } - if (tableEl->fCharClass == 254 && fC.fQuoted) { - // Table row specified "quoted" and the char was quoted. - break; - } - if (tableEl->fCharClass == 253 && fC.fChar == (UChar32)-1) { - // Table row specified eof and we hit eof on the input. - break; - } - - if (tableEl->fCharClass >= 128 && tableEl->fCharClass < 240 && // Table specs a char class && - fC.fQuoted == false && // char is not escaped && - fC.fChar != (UChar32)-1) { // char is not EOF - U_ASSERT(tableEl->fCharClass <= 137); - if (RegexStaticSets::gStaticSets->fRuleSets[tableEl->fCharClass-128].contains(fC.fChar)) { - // Table row specified a character class, or set of characters, - // and the current char matches it. - break; - } - } - - // No match on this row, advance to the next row for this state, - tableEl++; - } - REGEX_SCAN_DEBUG_PRINTF(("\n")); - - // - // We've found the row of the state table that matches the current input - // character from the rules string. - // Perform any action specified by this row in the state table. - if (doParseActions(tableEl->fAction) == false) { - // Break out of the state machine loop if the - // the action signalled some kind of error, or - // the action was to exit, occurs on normal end-of-rules-input. - break; - } - - if (tableEl->fPushState != 0) { - fStackPtr++; - if (fStackPtr >= kStackSize) { - error(U_REGEX_INTERNAL_ERROR); - REGEX_SCAN_DEBUG_PRINTF(("RegexCompile::parse() - state stack overflow.\n")); - fStackPtr--; - } - fStack[fStackPtr] = tableEl->fPushState; - } - - // - // NextChar. This is where characters are actually fetched from the pattern. - // Happens under control of the 'n' tag in the state table. - // - if (tableEl->fNextChar) { - nextChar(fC); - } - - // Get the next state from the table entry, or from the - // state stack if the next state was specified as "pop". - if (tableEl->fNextState != 255) { - state = tableEl->fNextState; - } else { - state = fStack[fStackPtr]; - fStackPtr--; - if (fStackPtr < 0) { - // state stack underflow - // This will occur if the user pattern has mis-matched parentheses, - // with extra close parens. - // - fStackPtr++; - error(U_REGEX_MISMATCHED_PAREN); - } - } - - } - - if (U_FAILURE(*fStatus)) { - // Bail out if the pattern had errors. - return; - } - - // - // The pattern has now been read and processed, and the compiled code generated. - // - - // - // The pattern's fFrameSize so far has accumulated the requirements for - // storage for capture parentheses, counters, etc. that are encountered - // in the pattern. Add space for the two variables that are always - // present in the saved state: the input string position (int64_t) and - // the position in the compiled pattern. - // - allocateStackData(RESTACKFRAME_HDRCOUNT); - - // - // Optimization pass 1: NOPs, back-references, and case-folding - // - stripNOPs(); - - // - // Get bounds for the minimum and maximum length of a string that this - // pattern can match. Used to avoid looking for matches in strings that - // are too short. - // - fRXPat->fMinMatchLen = minMatchLength(3, fRXPat->fCompiledPat->size()-1); - - // - // Optimization pass 2: match start type - // - matchStartType(); - - // - // Set up fast latin-1 range sets - // - int32_t numSets = fRXPat->fSets->size(); - fRXPat->fSets8 = new Regex8BitSet[numSets]; - // Null pointer check. - if (fRXPat->fSets8 == NULL) { - e = *fStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - int32_t i; - for (i=0; ifSets->elementAt(i); - fRXPat->fSets8[i].init(s); - } - -} - - - - - -//------------------------------------------------------------------------------ -// -// doParseAction Do some action during regex pattern parsing. -// Called by the parse state machine. -// -// Generation of the match engine PCode happens here, or -// in functions called from the parse actions defined here. -// -// -//------------------------------------------------------------------------------ -UBool RegexCompile::doParseActions(int32_t action) -{ - UBool returnVal = true; - - switch ((Regex_PatternParseAction)action) { - - case doPatStart: - // Start of pattern compiles to: - //0 SAVE 2 Fall back to position of FAIL - //1 jmp 3 - //2 FAIL Stop if we ever reach here. - //3 NOP Dummy, so start of pattern looks the same as - // the start of an ( grouping. - //4 NOP Resreved, will be replaced by a save if there are - // OR | operators at the top level - appendOp(URX_STATE_SAVE, 2); - appendOp(URX_JMP, 3); - appendOp(URX_FAIL, 0); - - // Standard open nonCapture paren action emits the two NOPs and - // sets up the paren stack frame. - doParseActions(doOpenNonCaptureParen); - break; - - case doPatFinish: - // We've scanned to the end of the pattern - // The end of pattern compiles to: - // URX_END - // which will stop the runtime match engine. - // Encountering end of pattern also behaves like a close paren, - // and forces fixups of the State Save at the beginning of the compiled pattern - // and of any OR operations at the top level. - // - handleCloseParen(); - if (fParenStack.size() > 0) { - // Missing close paren in pattern. - error(U_REGEX_MISMATCHED_PAREN); - } - - // add the END operation to the compiled pattern. - appendOp(URX_END, 0); - - // Terminate the pattern compilation state machine. - returnVal = false; - break; - - - - case doOrOperator: - // Scanning a '|', as in (A|B) - { - // Generate code for any pending literals preceding the '|' - fixLiterals(false); - - // Insert a SAVE operation at the start of the pattern section preceding - // this OR at this level. This SAVE will branch the match forward - // to the right hand side of the OR in the event that the left hand - // side fails to match and backtracks. Locate the position for the - // save from the location on the top of the parentheses stack. - int32_t savePosition = fParenStack.popi(); - int32_t op = (int32_t)fRXPat->fCompiledPat->elementAti(savePosition); - U_ASSERT(URX_TYPE(op) == URX_NOP); // original contents of reserved location - op = buildOp(URX_STATE_SAVE, fRXPat->fCompiledPat->size()+1); - fRXPat->fCompiledPat->setElementAt(op, savePosition); - - // Append an JMP operation into the compiled pattern. The operand for - // the JMP will eventually be the location following the ')' for the - // group. This will be patched in later, when the ')' is encountered. - appendOp(URX_JMP, 0); - - // Push the position of the newly added JMP op onto the parentheses stack. - // This registers if for fixup when this block's close paren is encountered. - fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); - - // Append a NOP to the compiled pattern. This is the slot reserved - // for a SAVE in the event that there is yet another '|' following - // this one. - appendOp(URX_NOP, 0); - fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); - } - break; - - - case doBeginNamedCapture: - // Scanning (?append(fC.fChar); - break; - - case doBadNamedCapture: - error(U_REGEX_INVALID_CAPTURE_GROUP_NAME); - break; - - case doOpenCaptureParen: - // Open Capturing Paren, possibly named. - // Compile to a - // - NOP, which later may be replaced by a save-state if the - // parenthesized group gets a * quantifier, followed by - // - START_CAPTURE n where n is stack frame offset to the capture group variables. - // - NOP, which may later be replaced by a save-state if there - // is an '|' alternation within the parens. - // - // Each capture group gets three slots in the save stack frame: - // 0: Capture Group start position (in input string being matched.) - // 1: Capture Group end position. - // 2: Start of Match-in-progress. - // The first two locations are for a completed capture group, and are - // referred to by back references and the like. - // The third location stores the capture start position when an START_CAPTURE is - // encountered. This will be promoted to a completed capture when (and if) the corresponding - // END_CAPTURE is encountered. - { - fixLiterals(); - appendOp(URX_NOP, 0); - int32_t varsLoc = allocateStackData(3); // Reserve three slots in match stack frame. - appendOp(URX_START_CAPTURE, varsLoc); - appendOp(URX_NOP, 0); - - // On the Parentheses stack, start a new frame and add the positions - // of the two NOPs. Depending on what follows in the pattern, the - // NOPs may be changed to SAVE_STATE or JMP ops, with a target - // address of the end of the parenthesized group. - fParenStack.push(fModeFlags, *fStatus); // Match mode state - fParenStack.push(capturing, *fStatus); // Frame type. - fParenStack.push(fRXPat->fCompiledPat->size()-3, *fStatus); // The first NOP location - fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP loc - - // Save the mapping from group number to stack frame variable position. - fRXPat->fGroupMap->addElement(varsLoc, *fStatus); - - // If this is a named capture group, add the name->group number mapping. - if (fCaptureName != NULL) { - if (!fRXPat->initNamedCaptureMap()) { - if (U_SUCCESS(*fStatus)) { - error(fRXPat->fDeferredStatus); - } - break; - } - int32_t groupNumber = fRXPat->fGroupMap->size(); - int32_t previousMapping = uhash_puti(fRXPat->fNamedCaptureMap, fCaptureName, groupNumber, fStatus); - fCaptureName = NULL; // hash table takes ownership of the name (key) string. - if (previousMapping > 0 && U_SUCCESS(*fStatus)) { - error(U_REGEX_INVALID_CAPTURE_GROUP_NAME); - } - } - } - break; - - case doOpenNonCaptureParen: - // Open non-caputuring (grouping only) Paren. - // Compile to a - // - NOP, which later may be replaced by a save-state if the - // parenthesized group gets a * quantifier, followed by - // - NOP, which may later be replaced by a save-state if there - // is an '|' alternation within the parens. - { - fixLiterals(); - appendOp(URX_NOP, 0); - appendOp(URX_NOP, 0); - - // On the Parentheses stack, start a new frame and add the positions - // of the two NOPs. - fParenStack.push(fModeFlags, *fStatus); // Match mode state - fParenStack.push(plain, *fStatus); // Begin a new frame. - fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP location - fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP loc - } - break; - - - case doOpenAtomicParen: - // Open Atomic Paren. (?> - // Compile to a - // - NOP, which later may be replaced if the parenthesized group - // has a quantifier, followed by - // - STO_SP save state stack position, so it can be restored at the ")" - // - NOP, which may later be replaced by a save-state if there - // is an '|' alternation within the parens. - { - fixLiterals(); - appendOp(URX_NOP, 0); - int32_t varLoc = allocateData(1); // Reserve a data location for saving the state stack ptr. - appendOp(URX_STO_SP, varLoc); - appendOp(URX_NOP, 0); - - // On the Parentheses stack, start a new frame and add the positions - // of the two NOPs. Depending on what follows in the pattern, the - // NOPs may be changed to SAVE_STATE or JMP ops, with a target - // address of the end of the parenthesized group. - fParenStack.push(fModeFlags, *fStatus); // Match mode state - fParenStack.push(atomic, *fStatus); // Frame type. - fParenStack.push(fRXPat->fCompiledPat->size()-3, *fStatus); // The first NOP - fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP - } - break; - - - case doOpenLookAhead: - // Positive Look-ahead (?= stuff ) - // - // Note: Addition of transparent input regions, with the need to - // restore the original regions when failing out of a lookahead - // block, complicated this sequence. Some combined opcodes - // might make sense - or might not, lookahead aren't that common. - // - // Caution: min match length optimization knows about this - // sequence; don't change without making updates there too. - // - // Compiles to - // 1 LA_START dataLoc Saves SP, Input Pos, Active input region. - // 2. STATE_SAVE 4 on failure of lookahead, goto 4 - // 3 JMP 6 continue ... - // - // 4. LA_END Look Ahead failed. Restore regions. - // 5. BACKTRACK and back track again. - // - // 6. NOP reserved for use by quantifiers on the block. - // Look-ahead can't have quantifiers, but paren stack - // compile time conventions require the slot anyhow. - // 7. NOP may be replaced if there is are '|' ops in the block. - // 8. code for parenthesized stuff. - // 9. LA_END - // - // Four data slots are reserved, for saving state on entry to the look-around - // 0: stack pointer on entry. - // 1: input position on entry. - // 2: fActiveStart, the active bounds start on entry. - // 3: fActiveLimit, the active bounds limit on entry. - { - fixLiterals(); - int32_t dataLoc = allocateData(4); - appendOp(URX_LA_START, dataLoc); - appendOp(URX_STATE_SAVE, fRXPat->fCompiledPat->size()+ 2); - appendOp(URX_JMP, fRXPat->fCompiledPat->size()+ 3); - appendOp(URX_LA_END, dataLoc); - appendOp(URX_BACKTRACK, 0); - appendOp(URX_NOP, 0); - appendOp(URX_NOP, 0); - - // On the Parentheses stack, start a new frame and add the positions - // of the NOPs. - fParenStack.push(fModeFlags, *fStatus); // Match mode state - fParenStack.push(lookAhead, *fStatus); // Frame type. - fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP location - fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP location - } - break; - - case doOpenLookAheadNeg: - // Negated Lookahead. (?! stuff ) - // Compiles to - // 1. LA_START dataloc - // 2. SAVE_STATE 7 // Fail within look-ahead block restores to this state, - // // which continues with the match. - // 3. NOP // Std. Open Paren sequence, for possible '|' - // 4. code for parenthesized stuff. - // 5. LA_END // Cut back stack, remove saved state from step 2. - // 6. BACKTRACK // code in block succeeded, so neg. lookahead fails. - // 7. END_LA // Restore match region, in case look-ahead was using - // an alternate (transparent) region. - // Four data slots are reserved, for saving state on entry to the look-around - // 0: stack pointer on entry. - // 1: input position on entry. - // 2: fActiveStart, the active bounds start on entry. - // 3: fActiveLimit, the active bounds limit on entry. - { - fixLiterals(); - int32_t dataLoc = allocateData(4); - appendOp(URX_LA_START, dataLoc); - appendOp(URX_STATE_SAVE, 0); // dest address will be patched later. - appendOp(URX_NOP, 0); - - // On the Parentheses stack, start a new frame and add the positions - // of the StateSave and NOP. - fParenStack.push(fModeFlags, *fStatus); // Match mode state - fParenStack.push(negLookAhead, *fStatus); // Frame type - fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The STATE_SAVE location - fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP location - - // Instructions #5 - #7 will be added when the ')' is encountered. - } - break; - - case doOpenLookBehind: - { - // Compile a (?<= look-behind open paren. - // - // Compiles to - // 0 URX_LB_START dataLoc - // 1 URX_LB_CONT dataLoc - // 2 MinMatchLen - // 3 MaxMatchLen - // 4 URX_NOP Standard '(' boilerplate. - // 5 URX_NOP Reserved slot for use with '|' ops within (block). - // 6 - // 7 URX_LB_END dataLoc # Check match len, restore input len - // 8 URX_LA_END dataLoc # Restore stack, input pos - // - // Allocate a block of matcher data, to contain (when running a match) - // 0: Stack ptr on entry - // 1: Input Index on entry - // 2: fActiveStart, the active bounds start on entry. - // 3: fActiveLimit, the active bounds limit on entry. - // 4: Start index of match current match attempt. - // The first four items must match the layout of data for LA_START / LA_END - - // Generate match code for any pending literals. - fixLiterals(); - - // Allocate data space - int32_t dataLoc = allocateData(5); - - // Emit URX_LB_START - appendOp(URX_LB_START, dataLoc); - - // Emit URX_LB_CONT - appendOp(URX_LB_CONT, dataLoc); - appendOp(URX_RESERVED_OP, 0); // MinMatchLength. To be filled later. - appendOp(URX_RESERVED_OP, 0); // MaxMatchLength. To be filled later. - - // Emit the NOPs - appendOp(URX_NOP, 0); - appendOp(URX_NOP, 0); - - // On the Parentheses stack, start a new frame and add the positions - // of the URX_LB_CONT and the NOP. - fParenStack.push(fModeFlags, *fStatus); // Match mode state - fParenStack.push(lookBehind, *fStatus); // Frame type - fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP location - fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The 2nd NOP location - - // The final two instructions will be added when the ')' is encountered. - } - - break; - - case doOpenLookBehindNeg: - { - // Compile a (? - // 8 URX_LBN_END dataLoc # Check match len, cause a FAIL - // 9 ... - // - // Allocate a block of matcher data, to contain (when running a match) - // 0: Stack ptr on entry - // 1: Input Index on entry - // 2: fActiveStart, the active bounds start on entry. - // 3: fActiveLimit, the active bounds limit on entry. - // 4: Start index of match current match attempt. - // The first four items must match the layout of data for LA_START / LA_END - - // Generate match code for any pending literals. - fixLiterals(); - - // Allocate data space - int32_t dataLoc = allocateData(5); - - // Emit URX_LB_START - appendOp(URX_LB_START, dataLoc); - - // Emit URX_LBN_CONT - appendOp(URX_LBN_CONT, dataLoc); - appendOp(URX_RESERVED_OP, 0); // MinMatchLength. To be filled later. - appendOp(URX_RESERVED_OP, 0); // MaxMatchLength. To be filled later. - appendOp(URX_RESERVED_OP, 0); // Continue Loc. To be filled later. - - // Emit the NOPs - appendOp(URX_NOP, 0); - appendOp(URX_NOP, 0); - - // On the Parentheses stack, start a new frame and add the positions - // of the URX_LB_CONT and the NOP. - fParenStack.push(fModeFlags, *fStatus); // Match mode state - fParenStack.push(lookBehindN, *fStatus); // Frame type - fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP location - fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The 2nd NOP location - - // The final two instructions will be added when the ')' is encountered. - } - break; - - case doConditionalExpr: - // Conditionals such as (?(1)a:b) - case doPerlInline: - // Perl inline-conditionals. (?{perl code}a|b) We're not perl, no way to do them. - error(U_REGEX_UNIMPLEMENTED); - break; - - - case doCloseParen: - handleCloseParen(); - if (fParenStack.size() <= 0) { - // Extra close paren, or missing open paren. - error(U_REGEX_MISMATCHED_PAREN); - } - break; - - case doNOP: - break; - - - case doBadOpenParenType: - case doRuleError: - error(U_REGEX_RULE_SYNTAX); - break; - - - case doMismatchedParenErr: - error(U_REGEX_MISMATCHED_PAREN); - break; - - case doPlus: - // Normal '+' compiles to - // 1. stuff to be repeated (already built) - // 2. jmp-sav 1 - // 3. ... - // - // Or, if the item to be repeated can match a zero length string, - // 1. STO_INP_LOC data-loc - // 2. body of stuff to be repeated - // 3. JMP_SAV_X 2 - // 4. ... - - // - // Or, if the item to be repeated is simple - // 1. Item to be repeated. - // 2. LOOP_SR_I set number (assuming repeated item is a set ref) - // 3. LOOP_C stack location - { - int32_t topLoc = blockTopLoc(false); // location of item #1 - int32_t frameLoc; - - // Check for simple constructs, which may get special optimized code. - if (topLoc == fRXPat->fCompiledPat->size() - 1) { - int32_t repeatedOp = (int32_t)fRXPat->fCompiledPat->elementAti(topLoc); - - if (URX_TYPE(repeatedOp) == URX_SETREF) { - // Emit optimized code for [char set]+ - appendOp(URX_LOOP_SR_I, URX_VAL(repeatedOp)); - frameLoc = allocateStackData(1); - appendOp(URX_LOOP_C, frameLoc); - break; - } - - if (URX_TYPE(repeatedOp) == URX_DOTANY || - URX_TYPE(repeatedOp) == URX_DOTANY_ALL || - URX_TYPE(repeatedOp) == URX_DOTANY_UNIX) { - // Emit Optimized code for .+ operations. - int32_t loopOpI = buildOp(URX_LOOP_DOT_I, 0); - if (URX_TYPE(repeatedOp) == URX_DOTANY_ALL) { - // URX_LOOP_DOT_I operand is a flag indicating ". matches any" mode. - loopOpI |= 1; - } - if (fModeFlags & UREGEX_UNIX_LINES) { - loopOpI |= 2; - } - appendOp(loopOpI); - frameLoc = allocateStackData(1); - appendOp(URX_LOOP_C, frameLoc); - break; - } - - } - - // General case. - - // Check for minimum match length of zero, which requires - // extra loop-breaking code. - if (minMatchLength(topLoc, fRXPat->fCompiledPat->size()-1) == 0) { - // Zero length match is possible. - // Emit the code sequence that can handle it. - insertOp(topLoc); - frameLoc = allocateStackData(1); - - int32_t op = buildOp(URX_STO_INP_LOC, frameLoc); - fRXPat->fCompiledPat->setElementAt(op, topLoc); - - appendOp(URX_JMP_SAV_X, topLoc+1); - } else { - // Simpler code when the repeated body must match something non-empty - appendOp(URX_JMP_SAV, topLoc); - } - } - break; - - case doNGPlus: - // Non-greedy '+?' compiles to - // 1. stuff to be repeated (already built) - // 2. state-save 1 - // 3. ... - { - int32_t topLoc = blockTopLoc(false); - appendOp(URX_STATE_SAVE, topLoc); - } - break; - - - case doOpt: - // Normal (greedy) ? quantifier. - // Compiles to - // 1. state save 3 - // 2. body of optional block - // 3. ... - // Insert the state save into the compiled pattern, and we're done. - { - int32_t saveStateLoc = blockTopLoc(true); - int32_t saveStateOp = buildOp(URX_STATE_SAVE, fRXPat->fCompiledPat->size()); - fRXPat->fCompiledPat->setElementAt(saveStateOp, saveStateLoc); - } - break; - - case doNGOpt: - // Non-greedy ?? quantifier - // compiles to - // 1. jmp 4 - // 2. body of optional block - // 3 jmp 5 - // 4. state save 2 - // 5 ... - // This code is less than ideal, with two jmps instead of one, because we can only - // insert one instruction at the top of the block being iterated. - { - int32_t jmp1_loc = blockTopLoc(true); - int32_t jmp2_loc = fRXPat->fCompiledPat->size(); - - int32_t jmp1_op = buildOp(URX_JMP, jmp2_loc+1); - fRXPat->fCompiledPat->setElementAt(jmp1_op, jmp1_loc); - - appendOp(URX_JMP, jmp2_loc+2); - - appendOp(URX_STATE_SAVE, jmp1_loc+1); - } - break; - - - case doStar: - // Normal (greedy) * quantifier. - // Compiles to - // 1. STATE_SAVE 4 - // 2. body of stuff being iterated over - // 3. JMP_SAV 2 - // 4. ... - // - // Or, if the body is a simple [Set], - // 1. LOOP_SR_I set number - // 2. LOOP_C stack location - // ... - // - // Or if this is a .* - // 1. LOOP_DOT_I (. matches all mode flag) - // 2. LOOP_C stack location - // - // Or, if the body can match a zero-length string, to inhibit infinite loops, - // 1. STATE_SAVE 5 - // 2. STO_INP_LOC data-loc - // 3. body of stuff - // 4. JMP_SAV_X 2 - // 5. ... - { - // location of item #1, the STATE_SAVE - int32_t topLoc = blockTopLoc(false); - int32_t dataLoc = -1; - - // Check for simple *, where the construct being repeated - // compiled to single opcode, and might be optimizable. - if (topLoc == fRXPat->fCompiledPat->size() - 1) { - int32_t repeatedOp = (int32_t)fRXPat->fCompiledPat->elementAti(topLoc); - - if (URX_TYPE(repeatedOp) == URX_SETREF) { - // Emit optimized code for a [char set]* - int32_t loopOpI = buildOp(URX_LOOP_SR_I, URX_VAL(repeatedOp)); - fRXPat->fCompiledPat->setElementAt(loopOpI, topLoc); - dataLoc = allocateStackData(1); - appendOp(URX_LOOP_C, dataLoc); - break; - } - - if (URX_TYPE(repeatedOp) == URX_DOTANY || - URX_TYPE(repeatedOp) == URX_DOTANY_ALL || - URX_TYPE(repeatedOp) == URX_DOTANY_UNIX) { - // Emit Optimized code for .* operations. - int32_t loopOpI = buildOp(URX_LOOP_DOT_I, 0); - if (URX_TYPE(repeatedOp) == URX_DOTANY_ALL) { - // URX_LOOP_DOT_I operand is a flag indicating . matches any mode. - loopOpI |= 1; - } - if ((fModeFlags & UREGEX_UNIX_LINES) != 0) { - loopOpI |= 2; - } - fRXPat->fCompiledPat->setElementAt(loopOpI, topLoc); - dataLoc = allocateStackData(1); - appendOp(URX_LOOP_C, dataLoc); - break; - } - } - - // Emit general case code for this * - // The optimizations did not apply. - - int32_t saveStateLoc = blockTopLoc(true); - int32_t jmpOp = buildOp(URX_JMP_SAV, saveStateLoc+1); - - // Check for minimum match length of zero, which requires - // extra loop-breaking code. - if (minMatchLength(saveStateLoc, fRXPat->fCompiledPat->size()-1) == 0) { - insertOp(saveStateLoc); - dataLoc = allocateStackData(1); - - int32_t op = buildOp(URX_STO_INP_LOC, dataLoc); - fRXPat->fCompiledPat->setElementAt(op, saveStateLoc+1); - jmpOp = buildOp(URX_JMP_SAV_X, saveStateLoc+2); - } - - // Locate the position in the compiled pattern where the match will continue - // after completing the *. (4 or 5 in the comment above) - int32_t continueLoc = fRXPat->fCompiledPat->size()+1; - - // Put together the save state op and store it into the compiled code. - int32_t saveStateOp = buildOp(URX_STATE_SAVE, continueLoc); - fRXPat->fCompiledPat->setElementAt(saveStateOp, saveStateLoc); - - // Append the URX_JMP_SAV or URX_JMPX operation to the compiled pattern. - appendOp(jmpOp); - } - break; - - case doNGStar: - // Non-greedy *? quantifier - // compiles to - // 1. JMP 3 - // 2. body of stuff being iterated over - // 3. STATE_SAVE 2 - // 4 ... - { - int32_t jmpLoc = blockTopLoc(true); // loc 1. - int32_t saveLoc = fRXPat->fCompiledPat->size(); // loc 3. - int32_t jmpOp = buildOp(URX_JMP, saveLoc); - fRXPat->fCompiledPat->setElementAt(jmpOp, jmpLoc); - appendOp(URX_STATE_SAVE, jmpLoc+1); - } - break; - - - case doIntervalInit: - // The '{' opening an interval quantifier was just scanned. - // Init the counter variables that will accumulate the values as the digits - // are scanned. - fIntervalLow = 0; - fIntervalUpper = -1; - break; - - case doIntevalLowerDigit: - // Scanned a digit from the lower value of an {lower,upper} interval - { - int32_t digitValue = u_charDigitValue(fC.fChar); - U_ASSERT(digitValue >= 0); - int64_t val = (int64_t)fIntervalLow*10 + digitValue; - if (val > INT32_MAX) { - error(U_REGEX_NUMBER_TOO_BIG); - } else { - fIntervalLow = (int32_t)val; - } - } - break; - - case doIntervalUpperDigit: - // Scanned a digit from the upper value of an {lower,upper} interval - { - if (fIntervalUpper < 0) { - fIntervalUpper = 0; - } - int32_t digitValue = u_charDigitValue(fC.fChar); - U_ASSERT(digitValue >= 0); - int64_t val = (int64_t)fIntervalUpper*10 + digitValue; - if (val > INT32_MAX) { - error(U_REGEX_NUMBER_TOO_BIG); - } else { - fIntervalUpper = (int32_t)val; - } - } - break; - - case doIntervalSame: - // Scanned a single value interval like {27}. Upper = Lower. - fIntervalUpper = fIntervalLow; - break; - - case doInterval: - // Finished scanning a normal {lower,upper} interval. Generate the code for it. - if (compileInlineInterval() == false) { - compileInterval(URX_CTR_INIT, URX_CTR_LOOP); - } - break; - - case doPossessiveInterval: - // Finished scanning a Possessive {lower,upper}+ interval. Generate the code for it. - { - // Remember the loc for the top of the block being looped over. - // (Can not reserve a slot in the compiled pattern at this time, because - // compileInterval needs to reserve also, and blockTopLoc can only reserve - // once per block.) - int32_t topLoc = blockTopLoc(false); - - // Produce normal looping code. - compileInterval(URX_CTR_INIT, URX_CTR_LOOP); - - // Surround the just-emitted normal looping code with a STO_SP ... LD_SP - // just as if the loop was inclosed in atomic parentheses. - - // First the STO_SP before the start of the loop - insertOp(topLoc); - - int32_t varLoc = allocateData(1); // Reserve a data location for saving the - int32_t op = buildOp(URX_STO_SP, varLoc); - fRXPat->fCompiledPat->setElementAt(op, topLoc); - - int32_t loopOp = (int32_t)fRXPat->fCompiledPat->popi(); - U_ASSERT(URX_TYPE(loopOp) == URX_CTR_LOOP && URX_VAL(loopOp) == topLoc); - loopOp++; // point LoopOp after the just-inserted STO_SP - fRXPat->fCompiledPat->push(loopOp, *fStatus); - - // Then the LD_SP after the end of the loop - appendOp(URX_LD_SP, varLoc); - } - - break; - - case doNGInterval: - // Finished scanning a non-greedy {lower,upper}? interval. Generate the code for it. - compileInterval(URX_CTR_INIT_NG, URX_CTR_LOOP_NG); - break; - - case doIntervalError: - error(U_REGEX_BAD_INTERVAL); - break; - - case doLiteralChar: - // We've just scanned a "normal" character from the pattern, - literalChar(fC.fChar); - break; - - - case doEscapedLiteralChar: - // We've just scanned an backslashed escaped character with no - // special meaning. It represents itself. - if ((fModeFlags & UREGEX_ERROR_ON_UNKNOWN_ESCAPES) != 0 && - ((fC.fChar >= 0x41 && fC.fChar<= 0x5A) || // in [A-Z] - (fC.fChar >= 0x61 && fC.fChar <= 0x7a))) { // in [a-z] - error(U_REGEX_BAD_ESCAPE_SEQUENCE); - } - literalChar(fC.fChar); - break; - - - case doDotAny: - // scanned a ".", match any single character. - { - fixLiterals(false); - if (fModeFlags & UREGEX_DOTALL) { - appendOp(URX_DOTANY_ALL, 0); - } else if (fModeFlags & UREGEX_UNIX_LINES) { - appendOp(URX_DOTANY_UNIX, 0); - } else { - appendOp(URX_DOTANY, 0); - } - } - break; - - case doCaret: - { - fixLiterals(false); - if ( (fModeFlags & UREGEX_MULTILINE) == 0 && (fModeFlags & UREGEX_UNIX_LINES) == 0) { - appendOp(URX_CARET, 0); - } else if ((fModeFlags & UREGEX_MULTILINE) != 0 && (fModeFlags & UREGEX_UNIX_LINES) == 0) { - appendOp(URX_CARET_M, 0); - } else if ((fModeFlags & UREGEX_MULTILINE) == 0 && (fModeFlags & UREGEX_UNIX_LINES) != 0) { - appendOp(URX_CARET, 0); // Only testing true start of input. - } else if ((fModeFlags & UREGEX_MULTILINE) != 0 && (fModeFlags & UREGEX_UNIX_LINES) != 0) { - appendOp(URX_CARET_M_UNIX, 0); - } - } - break; - - case doDollar: - { - fixLiterals(false); - if ( (fModeFlags & UREGEX_MULTILINE) == 0 && (fModeFlags & UREGEX_UNIX_LINES) == 0) { - appendOp(URX_DOLLAR, 0); - } else if ((fModeFlags & UREGEX_MULTILINE) != 0 && (fModeFlags & UREGEX_UNIX_LINES) == 0) { - appendOp(URX_DOLLAR_M, 0); - } else if ((fModeFlags & UREGEX_MULTILINE) == 0 && (fModeFlags & UREGEX_UNIX_LINES) != 0) { - appendOp(URX_DOLLAR_D, 0); - } else if ((fModeFlags & UREGEX_MULTILINE) != 0 && (fModeFlags & UREGEX_UNIX_LINES) != 0) { - appendOp(URX_DOLLAR_MD, 0); - } - } - break; - - case doBackslashA: - fixLiterals(false); - appendOp(URX_CARET, 0); - break; - - case doBackslashB: - { - #if UCONFIG_NO_BREAK_ITERATION==1 - if (fModeFlags & UREGEX_UWORD) { - error(U_UNSUPPORTED_ERROR); - } - #endif - fixLiterals(false); - int32_t op = (fModeFlags & UREGEX_UWORD)? URX_BACKSLASH_BU : URX_BACKSLASH_B; - appendOp(op, 1); - } - break; - - case doBackslashb: - { - #if UCONFIG_NO_BREAK_ITERATION==1 - if (fModeFlags & UREGEX_UWORD) { - error(U_UNSUPPORTED_ERROR); - } - #endif - fixLiterals(false); - int32_t op = (fModeFlags & UREGEX_UWORD)? URX_BACKSLASH_BU : URX_BACKSLASH_B; - appendOp(op, 0); - } - break; - - case doBackslashD: - fixLiterals(false); - appendOp(URX_BACKSLASH_D, 1); - break; - - case doBackslashd: - fixLiterals(false); - appendOp(URX_BACKSLASH_D, 0); - break; - - case doBackslashG: - fixLiterals(false); - appendOp(URX_BACKSLASH_G, 0); - break; - - case doBackslashH: - fixLiterals(false); - appendOp(URX_BACKSLASH_H, 1); - break; - - case doBackslashh: - fixLiterals(false); - appendOp(URX_BACKSLASH_H, 0); - break; - - case doBackslashR: - fixLiterals(false); - appendOp(URX_BACKSLASH_R, 0); - break; - - case doBackslashS: - fixLiterals(false); - appendOp(URX_STAT_SETREF_N, URX_ISSPACE_SET); - break; - - case doBackslashs: - fixLiterals(false); - appendOp(URX_STATIC_SETREF, URX_ISSPACE_SET); - break; - - case doBackslashV: - fixLiterals(false); - appendOp(URX_BACKSLASH_V, 1); - break; - - case doBackslashv: - fixLiterals(false); - appendOp(URX_BACKSLASH_V, 0); - break; - - case doBackslashW: - fixLiterals(false); - appendOp(URX_STAT_SETREF_N, URX_ISWORD_SET); - break; - - case doBackslashw: - fixLiterals(false); - appendOp(URX_STATIC_SETREF, URX_ISWORD_SET); - break; - - case doBackslashX: - #if UCONFIG_NO_BREAK_ITERATION==1 - // Grapheme Cluster Boundary requires ICU break iteration. - error(U_UNSUPPORTED_ERROR); - #endif - fixLiterals(false); - appendOp(URX_BACKSLASH_X, 0); - break; - - case doBackslashZ: - fixLiterals(false); - appendOp(URX_DOLLAR, 0); - break; - - case doBackslashz: - fixLiterals(false); - appendOp(URX_BACKSLASH_Z, 0); - break; - - case doEscapeError: - error(U_REGEX_BAD_ESCAPE_SEQUENCE); - break; - - case doExit: - fixLiterals(false); - returnVal = false; - break; - - case doProperty: - { - fixLiterals(false); - UnicodeSet *theSet = scanProp(); - compileSet(theSet); - } - break; - - case doNamedChar: - { - UChar32 c = scanNamedChar(); - literalChar(c); - } - break; - - - case doBackRef: - // BackReference. Somewhat unusual in that the front-end can not completely parse - // the regular expression, because the number of digits to be consumed - // depends on the number of capture groups that have been defined. So - // we have to do it here instead. - { - int32_t numCaptureGroups = fRXPat->fGroupMap->size(); - int32_t groupNum = 0; - UChar32 c = fC.fChar; - - for (;;) { - // Loop once per digit, for max allowed number of digits in a back reference. - int32_t digit = u_charDigitValue(c); - groupNum = groupNum * 10 + digit; - if (groupNum >= numCaptureGroups) { - break; - } - c = peekCharLL(); - if (RegexStaticSets::gStaticSets->fRuleDigitsAlias->contains(c) == false) { - break; - } - nextCharLL(); - } - - // Scan of the back reference in the source regexp is complete. Now generate - // the compiled code for it. - // Because capture groups can be forward-referenced by back-references, - // we fill the operand with the capture group number. At the end - // of compilation, it will be changed to the variable's location. - U_ASSERT(groupNum > 0); // Shouldn't happen. '\0' begins an octal escape sequence, - // and shouldn't enter this code path at all. - fixLiterals(false); - if (fModeFlags & UREGEX_CASE_INSENSITIVE) { - appendOp(URX_BACKREF_I, groupNum); - } else { - appendOp(URX_BACKREF, groupNum); - } - } - break; - - case doBeginNamedBackRef: - U_ASSERT(fCaptureName == NULL); - fCaptureName = new UnicodeString; - if (fCaptureName == NULL) { - error(U_MEMORY_ALLOCATION_ERROR); - } - break; - - case doContinueNamedBackRef: - fCaptureName->append(fC.fChar); - break; - - case doCompleteNamedBackRef: - { - int32_t groupNumber = - fRXPat->fNamedCaptureMap ? uhash_geti(fRXPat->fNamedCaptureMap, fCaptureName) : 0; - if (groupNumber == 0) { - // Group name has not been defined. - // Could be a forward reference. If we choose to support them at some - // future time, extra mechanism will be required at this point. - error(U_REGEX_INVALID_CAPTURE_GROUP_NAME); - } else { - // Given the number, handle identically to a \n numbered back reference. - // See comments above, under doBackRef - fixLiterals(false); - if (fModeFlags & UREGEX_CASE_INSENSITIVE) { - appendOp(URX_BACKREF_I, groupNumber); - } else { - appendOp(URX_BACKREF, groupNumber); - } - } - delete fCaptureName; - fCaptureName = NULL; - break; - } - - case doPossessivePlus: - // Possessive ++ quantifier. - // Compiles to - // 1. STO_SP - // 2. body of stuff being iterated over - // 3. STATE_SAVE 5 - // 4. JMP 2 - // 5. LD_SP - // 6. ... - // - // Note: TODO: This is pretty inefficient. A mass of saved state is built up - // then unconditionally discarded. Perhaps introduce a new opcode. Ticket 6056 - // - { - // Emit the STO_SP - int32_t topLoc = blockTopLoc(true); - int32_t stoLoc = allocateData(1); // Reserve the data location for storing save stack ptr. - int32_t op = buildOp(URX_STO_SP, stoLoc); - fRXPat->fCompiledPat->setElementAt(op, topLoc); - - // Emit the STATE_SAVE - appendOp(URX_STATE_SAVE, fRXPat->fCompiledPat->size()+2); - - // Emit the JMP - appendOp(URX_JMP, topLoc+1); - - // Emit the LD_SP - appendOp(URX_LD_SP, stoLoc); - } - break; - - case doPossessiveStar: - // Possessive *+ quantifier. - // Compiles to - // 1. STO_SP loc - // 2. STATE_SAVE 5 - // 3. body of stuff being iterated over - // 4. JMP 2 - // 5. LD_SP loc - // 6 ... - // TODO: do something to cut back the state stack each time through the loop. - { - // Reserve two slots at the top of the block. - int32_t topLoc = blockTopLoc(true); - insertOp(topLoc); - - // emit STO_SP loc - int32_t stoLoc = allocateData(1); // Reserve the data location for storing save stack ptr. - int32_t op = buildOp(URX_STO_SP, stoLoc); - fRXPat->fCompiledPat->setElementAt(op, topLoc); - - // Emit the SAVE_STATE 5 - int32_t L7 = fRXPat->fCompiledPat->size()+1; - op = buildOp(URX_STATE_SAVE, L7); - fRXPat->fCompiledPat->setElementAt(op, topLoc+1); - - // Append the JMP operation. - appendOp(URX_JMP, topLoc+1); - - // Emit the LD_SP loc - appendOp(URX_LD_SP, stoLoc); - } - break; - - case doPossessiveOpt: - // Possessive ?+ quantifier. - // Compiles to - // 1. STO_SP loc - // 2. SAVE_STATE 5 - // 3. body of optional block - // 4. LD_SP loc - // 5. ... - // - { - // Reserve two slots at the top of the block. - int32_t topLoc = blockTopLoc(true); - insertOp(topLoc); - - // Emit the STO_SP - int32_t stoLoc = allocateData(1); // Reserve the data location for storing save stack ptr. - int32_t op = buildOp(URX_STO_SP, stoLoc); - fRXPat->fCompiledPat->setElementAt(op, topLoc); - - // Emit the SAVE_STATE - int32_t continueLoc = fRXPat->fCompiledPat->size()+1; - op = buildOp(URX_STATE_SAVE, continueLoc); - fRXPat->fCompiledPat->setElementAt(op, topLoc+1); - - // Emit the LD_SP - appendOp(URX_LD_SP, stoLoc); - } - break; - - - case doBeginMatchMode: - fNewModeFlags = fModeFlags; - fSetModeFlag = true; - break; - - case doMatchMode: // (?i) and similar - { - int32_t bit = 0; - switch (fC.fChar) { - case 0x69: /* 'i' */ bit = UREGEX_CASE_INSENSITIVE; break; - case 0x64: /* 'd' */ bit = UREGEX_UNIX_LINES; break; - case 0x6d: /* 'm' */ bit = UREGEX_MULTILINE; break; - case 0x73: /* 's' */ bit = UREGEX_DOTALL; break; - case 0x75: /* 'u' */ bit = 0; /* Unicode casing */ break; - case 0x77: /* 'w' */ bit = UREGEX_UWORD; break; - case 0x78: /* 'x' */ bit = UREGEX_COMMENTS; break; - case 0x2d: /* '-' */ fSetModeFlag = false; break; - default: - UPRV_UNREACHABLE_EXIT; // Should never happen. Other chars are filtered out - // by the scanner. - } - if (fSetModeFlag) { - fNewModeFlags |= bit; - } else { - fNewModeFlags &= ~bit; - } - } - break; - - case doSetMatchMode: - // Emit code to match any pending literals, using the not-yet changed match mode. - fixLiterals(); - - // We've got a (?i) or similar. The match mode is being changed, but - // the change is not scoped to a parenthesized block. - U_ASSERT(fNewModeFlags < 0); - fModeFlags = fNewModeFlags; - - break; - - - case doMatchModeParen: - // We've got a (?i: or similar. Begin a parenthesized block, save old - // mode flags so they can be restored at the close of the block. - // - // Compile to a - // - NOP, which later may be replaced by a save-state if the - // parenthesized group gets a * quantifier, followed by - // - NOP, which may later be replaced by a save-state if there - // is an '|' alternation within the parens. - { - fixLiterals(false); - appendOp(URX_NOP, 0); - appendOp(URX_NOP, 0); - - // On the Parentheses stack, start a new frame and add the positions - // of the two NOPs (a normal non-capturing () frame, except for the - // saving of the original mode flags.) - fParenStack.push(fModeFlags, *fStatus); - fParenStack.push(flags, *fStatus); // Frame Marker - fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP - fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP - - // Set the current mode flags to the new values. - U_ASSERT(fNewModeFlags < 0); - fModeFlags = fNewModeFlags; - } - break; - - case doBadModeFlag: - error(U_REGEX_INVALID_FLAG); - break; - - case doSuppressComments: - // We have just scanned a '(?'. We now need to prevent the character scanner from - // treating a '#' as a to-the-end-of-line comment. - // (This Perl compatibility just gets uglier and uglier to do...) - fEOLComments = false; - break; - - - case doSetAddAmp: - { - UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); - set->add(chAmp); - } - break; - - case doSetAddDash: - { - UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); - set->add(chDash); - } - break; - - case doSetBackslash_s: - { - UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); - set->addAll(RegexStaticSets::gStaticSets->fPropSets[URX_ISSPACE_SET]); - break; - } - - case doSetBackslash_S: - { - UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); - UnicodeSet SSet; - SSet.addAll(RegexStaticSets::gStaticSets->fPropSets[URX_ISSPACE_SET]).complement(); - set->addAll(SSet); - break; - } - - case doSetBackslash_d: - { - UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); - // TODO - make a static set, ticket 6058. - addCategory(set, U_GC_ND_MASK, *fStatus); - break; - } - - case doSetBackslash_D: - { - UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); - UnicodeSet digits; - // TODO - make a static set, ticket 6058. - digits.applyIntPropertyValue(UCHAR_GENERAL_CATEGORY_MASK, U_GC_ND_MASK, *fStatus); - digits.complement(); - set->addAll(digits); - break; - } - - case doSetBackslash_h: - { - UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); - UnicodeSet h; - h.applyIntPropertyValue(UCHAR_GENERAL_CATEGORY_MASK, U_GC_ZS_MASK, *fStatus); - h.add((UChar32)9); // Tab - set->addAll(h); - break; - } - - case doSetBackslash_H: - { - UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); - UnicodeSet h; - h.applyIntPropertyValue(UCHAR_GENERAL_CATEGORY_MASK, U_GC_ZS_MASK, *fStatus); - h.add((UChar32)9); // Tab - h.complement(); - set->addAll(h); - break; - } - - case doSetBackslash_v: - { - UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); - set->add((UChar32)0x0a, (UChar32)0x0d); // add range - set->add((UChar32)0x85); - set->add((UChar32)0x2028, (UChar32)0x2029); - break; - } - - case doSetBackslash_V: - { - UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); - UnicodeSet v; - v.add((UChar32)0x0a, (UChar32)0x0d); // add range - v.add((UChar32)0x85); - v.add((UChar32)0x2028, (UChar32)0x2029); - v.complement(); - set->addAll(v); - break; - } - - case doSetBackslash_w: - { - UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); - set->addAll(RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET]); - break; - } - - case doSetBackslash_W: - { - UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); - UnicodeSet SSet; - SSet.addAll(RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET]).complement(); - set->addAll(SSet); - break; - } - - case doSetBegin: - { - fixLiterals(false); - LocalPointer lpSet(new UnicodeSet(), *fStatus); - fSetStack.push(lpSet.orphan(), *fStatus); - fSetOpStack.push(setStart, *fStatus); - if ((fModeFlags & UREGEX_CASE_INSENSITIVE) != 0) { - fSetOpStack.push(setCaseClose, *fStatus); - } - break; - } - - case doSetBeginDifference1: - // We have scanned something like [[abc]-[ - // Set up a new UnicodeSet for the set beginning with the just-scanned '[' - // Push a Difference operator, which will cause the new set to be subtracted from what - // went before once it is created. - setPushOp(setDifference1); - fSetOpStack.push(setStart, *fStatus); - if ((fModeFlags & UREGEX_CASE_INSENSITIVE) != 0) { - fSetOpStack.push(setCaseClose, *fStatus); - } - break; - - case doSetBeginIntersection1: - // We have scanned something like [[abc]&[ - // Need both the '&' operator and the open '[' operator. - setPushOp(setIntersection1); - fSetOpStack.push(setStart, *fStatus); - if ((fModeFlags & UREGEX_CASE_INSENSITIVE) != 0) { - fSetOpStack.push(setCaseClose, *fStatus); - } - break; - - case doSetBeginUnion: - // We have scanned something like [[abc][ - // Need to handle the union operation explicitly [[abc] | [ - setPushOp(setUnion); - fSetOpStack.push(setStart, *fStatus); - if ((fModeFlags & UREGEX_CASE_INSENSITIVE) != 0) { - fSetOpStack.push(setCaseClose, *fStatus); - } - break; - - case doSetDifference2: - // We have scanned something like [abc-- - // Consider this to unambiguously be a set difference operator. - setPushOp(setDifference2); - break; - - case doSetEnd: - // Have encountered the ']' that closes a set. - // Force the evaluation of any pending operations within this set, - // leave the completed set on the top of the set stack. - setEval(setEnd); - U_ASSERT(fSetOpStack.peeki()==setStart); - fSetOpStack.popi(); - break; - - case doSetFinish: - { - // Finished a complete set expression, including all nested sets. - // The close bracket has already triggered clearing out pending set operators, - // the operator stack should be empty and the operand stack should have just - // one entry, the result set. - U_ASSERT(fSetOpStack.empty()); - UnicodeSet *theSet = (UnicodeSet *)fSetStack.pop(); - U_ASSERT(fSetStack.empty()); - compileSet(theSet); - break; - } - - case doSetIntersection2: - // Have scanned something like [abc&& - setPushOp(setIntersection2); - break; - - case doSetLiteral: - // Union the just-scanned literal character into the set being built. - // This operation is the highest precedence set operation, so we can always do - // it immediately, without waiting to see what follows. It is necessary to perform - // any pending '-' or '&' operation first, because these have the same precedence - // as union-ing in a literal' - { - setEval(setUnion); - UnicodeSet *s = (UnicodeSet *)fSetStack.peek(); - s->add(fC.fChar); - fLastSetLiteral = fC.fChar; - break; - } - - case doSetLiteralEscaped: - // A back-slash escaped literal character was encountered. - // Processing is the same as with setLiteral, above, with the addition of - // the optional check for errors on escaped ASCII letters. - { - if ((fModeFlags & UREGEX_ERROR_ON_UNKNOWN_ESCAPES) != 0 && - ((fC.fChar >= 0x41 && fC.fChar<= 0x5A) || // in [A-Z] - (fC.fChar >= 0x61 && fC.fChar <= 0x7a))) { // in [a-z] - error(U_REGEX_BAD_ESCAPE_SEQUENCE); - } - setEval(setUnion); - UnicodeSet *s = (UnicodeSet *)fSetStack.peek(); - s->add(fC.fChar); - fLastSetLiteral = fC.fChar; - break; - } - - case doSetNamedChar: - // Scanning a \N{UNICODE CHARACTER NAME} - // Aside from the source of the character, the processing is identical to doSetLiteral, - // above. - { - UChar32 c = scanNamedChar(); - setEval(setUnion); - UnicodeSet *s = (UnicodeSet *)fSetStack.peek(); - s->add(c); - fLastSetLiteral = c; - break; - } - - case doSetNamedRange: - // We have scanned literal-\N{CHAR NAME}. Add the range to the set. - // The left character is already in the set, and is saved in fLastSetLiteral. - // The right side needs to be picked up, the scan is at the 'N'. - // Lower Limit > Upper limit being an error matches both Java - // and ICU UnicodeSet behavior. - { - UChar32 c = scanNamedChar(); - if (U_SUCCESS(*fStatus) && (fLastSetLiteral == U_SENTINEL || fLastSetLiteral > c)) { - error(U_REGEX_INVALID_RANGE); - } - UnicodeSet *s = (UnicodeSet *)fSetStack.peek(); - s->add(fLastSetLiteral, c); - fLastSetLiteral = c; - break; - } - - - case doSetNegate: - // Scanned a '^' at the start of a set. - // Push the negation operator onto the set op stack. - // A twist for case-insensitive matching: - // the case closure operation must happen _before_ negation. - // But the case closure operation will already be on the stack if it's required. - // This requires checking for case closure, and swapping the stack order - // if it is present. - { - int32_t tosOp = fSetOpStack.peeki(); - if (tosOp == setCaseClose) { - fSetOpStack.popi(); - fSetOpStack.push(setNegation, *fStatus); - fSetOpStack.push(setCaseClose, *fStatus); - } else { - fSetOpStack.push(setNegation, *fStatus); - } - } - break; - - case doSetNoCloseError: - error(U_REGEX_MISSING_CLOSE_BRACKET); - break; - - case doSetOpError: - error(U_REGEX_RULE_SYNTAX); // -- or && at the end of a set. Illegal. - break; - - case doSetPosixProp: - { - UnicodeSet *s = scanPosixProp(); - if (s != NULL) { - UnicodeSet *tos = (UnicodeSet *)fSetStack.peek(); - tos->addAll(*s); - delete s; - } // else error. scanProp() reported the error status already. - } - break; - - case doSetProp: - // Scanned a \p \P within [brackets]. - { - UnicodeSet *s = scanProp(); - if (s != NULL) { - UnicodeSet *tos = (UnicodeSet *)fSetStack.peek(); - tos->addAll(*s); - delete s; - } // else error. scanProp() reported the error status already. - } - break; - - - case doSetRange: - // We have scanned literal-literal. Add the range to the set. - // The left character is already in the set, and is saved in fLastSetLiteral. - // The right side is the current character. - // Lower Limit > Upper limit being an error matches both Java - // and ICU UnicodeSet behavior. - { - - if (fLastSetLiteral == U_SENTINEL || fLastSetLiteral > fC.fChar) { - error(U_REGEX_INVALID_RANGE); - } - UnicodeSet *s = (UnicodeSet *)fSetStack.peek(); - s->add(fLastSetLiteral, fC.fChar); - break; - } - - default: - UPRV_UNREACHABLE_EXIT; - } - - if (U_FAILURE(*fStatus)) { - returnVal = false; - } - - return returnVal; -} - - - -//------------------------------------------------------------------------------ -// -// literalChar We've encountered a literal character from the pattern, -// or an escape sequence that reduces to a character. -// Add it to the string containing all literal chars/strings from -// the pattern. -// -//------------------------------------------------------------------------------ -void RegexCompile::literalChar(UChar32 c) { - fLiteralChars.append(c); -} - - -//------------------------------------------------------------------------------ -// -// fixLiterals When compiling something that can follow a literal -// string in a pattern, emit the code to match the -// accumulated literal string. -// -// Optionally, split the last char of the string off into -// a single "ONE_CHAR" operation, so that quantifiers can -// apply to that char alone. Example: abc* -// The * must apply to the 'c' only. -// -//------------------------------------------------------------------------------ -void RegexCompile::fixLiterals(UBool split) { - - // If no literal characters have been scanned but not yet had code generated - // for them, nothing needs to be done. - if (fLiteralChars.length() == 0) { - return; - } - - int32_t indexOfLastCodePoint = fLiteralChars.moveIndex32(fLiteralChars.length(), -1); - UChar32 lastCodePoint = fLiteralChars.char32At(indexOfLastCodePoint); - - // Split: We need to ensure that the last item in the compiled pattern - // refers only to the last literal scanned in the pattern, so that - // quantifiers (*, +, etc.) affect only it, and not a longer string. - // Split before case folding for case insensitive matches. - - if (split) { - fLiteralChars.truncate(indexOfLastCodePoint); - fixLiterals(false); // Recursive call, emit code to match the first part of the string. - // Note that the truncated literal string may be empty, in which case - // nothing will be emitted. - - literalChar(lastCodePoint); // Re-add the last code point as if it were a new literal. - fixLiterals(false); // Second recursive call, code for the final code point. - return; - } - - // If we are doing case-insensitive matching, case fold the string. This may expand - // the string, e.g. the German sharp-s turns into "ss" - if (fModeFlags & UREGEX_CASE_INSENSITIVE) { - fLiteralChars.foldCase(); - indexOfLastCodePoint = fLiteralChars.moveIndex32(fLiteralChars.length(), -1); - lastCodePoint = fLiteralChars.char32At(indexOfLastCodePoint); - } - - if (indexOfLastCodePoint == 0) { - // Single character, emit a URX_ONECHAR op to match it. - if ((fModeFlags & UREGEX_CASE_INSENSITIVE) && - u_hasBinaryProperty(lastCodePoint, UCHAR_CASE_SENSITIVE)) { - appendOp(URX_ONECHAR_I, lastCodePoint); - } else { - appendOp(URX_ONECHAR, lastCodePoint); - } - } else { - // Two or more chars, emit a URX_STRING to match them. - if (fLiteralChars.length() > 0x00ffffff || fRXPat->fLiteralText.length() > 0x00ffffff) { - error(U_REGEX_PATTERN_TOO_BIG); - } - if (fModeFlags & UREGEX_CASE_INSENSITIVE) { - appendOp(URX_STRING_I, fRXPat->fLiteralText.length()); - } else { - // TODO here: add optimization to split case sensitive strings of length two - // into two single char ops, for efficiency. - appendOp(URX_STRING, fRXPat->fLiteralText.length()); - } - appendOp(URX_STRING_LEN, fLiteralChars.length()); - - // Add this string into the accumulated strings of the compiled pattern. - fRXPat->fLiteralText.append(fLiteralChars); - } - - fLiteralChars.remove(); -} - - -int32_t RegexCompile::buildOp(int32_t type, int32_t val) { - if (U_FAILURE(*fStatus)) { - return 0; - } - if (type < 0 || type > 255) { - UPRV_UNREACHABLE_EXIT; - } - if (val > 0x00ffffff) { - UPRV_UNREACHABLE_EXIT; - } - if (val < 0) { - if (!(type == URX_RESERVED_OP_N || type == URX_RESERVED_OP)) { - UPRV_UNREACHABLE_EXIT; - } - if (URX_TYPE(val) != 0xff) { - UPRV_UNREACHABLE_EXIT; - } - type = URX_RESERVED_OP_N; - } - return (type << 24) | val; -} - - -//------------------------------------------------------------------------------ -// -// appendOp() Append a new instruction onto the compiled pattern -// Includes error checking, limiting the size of the -// pattern to lengths that can be represented in the -// 24 bit operand field of an instruction. -// -//------------------------------------------------------------------------------ -void RegexCompile::appendOp(int32_t op) { - if (U_FAILURE(*fStatus)) { - return; - } - fRXPat->fCompiledPat->addElement(op, *fStatus); - if ((fRXPat->fCompiledPat->size() > 0x00fffff0) && U_SUCCESS(*fStatus)) { - error(U_REGEX_PATTERN_TOO_BIG); - } -} - -void RegexCompile::appendOp(int32_t type, int32_t val) { - appendOp(buildOp(type, val)); -} - - -//------------------------------------------------------------------------------ -// -// insertOp() Insert a slot for a new opcode into the already -// compiled pattern code. -// -// Fill the slot with a NOP. Our caller will replace it -// with what they really wanted. -// -//------------------------------------------------------------------------------ -void RegexCompile::insertOp(int32_t where) { - UVector64 *code = fRXPat->fCompiledPat; - U_ASSERT(where>0 && where < code->size()); - - int32_t nop = buildOp(URX_NOP, 0); - code->insertElementAt(nop, where, *fStatus); - - // Walk through the pattern, looking for any ops with targets that - // were moved down by the insert. Fix them. - int32_t loc; - for (loc=0; locsize(); loc++) { - int32_t op = (int32_t)code->elementAti(loc); - int32_t opType = URX_TYPE(op); - int32_t opValue = URX_VAL(op); - if ((opType == URX_JMP || - opType == URX_JMPX || - opType == URX_STATE_SAVE || - opType == URX_CTR_LOOP || - opType == URX_CTR_LOOP_NG || - opType == URX_JMP_SAV || - opType == URX_JMP_SAV_X || - opType == URX_RELOC_OPRND) && opValue > where) { - // Target location for this opcode is after the insertion point and - // needs to be incremented to adjust for the insertion. - opValue++; - op = buildOp(opType, opValue); - code->setElementAt(op, loc); - } - } - - // Now fix up the parentheses stack. All positive values in it are locations in - // the compiled pattern. (Negative values are frame boundaries, and don't need fixing.) - for (loc=0; locsize()); - if (x>where) { - x++; - fParenStack.setElementAt(x, loc); - } - } - - if (fMatchCloseParen > where) { - fMatchCloseParen++; - } - if (fMatchOpenParen > where) { - fMatchOpenParen++; - } -} - - -//------------------------------------------------------------------------------ -// -// allocateData() Allocate storage in the matcher's static data area. -// Return the index for the newly allocated data. -// The storage won't actually exist until we are running a match -// operation, but the storage indexes are inserted into various -// opcodes while compiling the pattern. -// -//------------------------------------------------------------------------------ -int32_t RegexCompile::allocateData(int32_t size) { - if (U_FAILURE(*fStatus)) { - return 0; - } - if (size <= 0 || size > 0x100 || fRXPat->fDataSize < 0) { - error(U_REGEX_INTERNAL_ERROR); - return 0; - } - int32_t dataIndex = fRXPat->fDataSize; - fRXPat->fDataSize += size; - if (fRXPat->fDataSize >= 0x00fffff0) { - error(U_REGEX_INTERNAL_ERROR); - } - return dataIndex; -} - - -//------------------------------------------------------------------------------ -// -// allocateStackData() Allocate space in the back-tracking stack frame. -// Return the index for the newly allocated data. -// The frame indexes are inserted into various -// opcodes while compiling the pattern, meaning that frame -// size must be restricted to the size that will fit -// as an operand (24 bits). -// -//------------------------------------------------------------------------------ -int32_t RegexCompile::allocateStackData(int32_t size) { - if (U_FAILURE(*fStatus)) { - return 0; - } - if (size <= 0 || size > 0x100 || fRXPat->fFrameSize < 0) { - error(U_REGEX_INTERNAL_ERROR); - return 0; - } - int32_t dataIndex = fRXPat->fFrameSize; - fRXPat->fFrameSize += size; - if (fRXPat->fFrameSize >= 0x00fffff0) { - error(U_REGEX_PATTERN_TOO_BIG); - } - return dataIndex; -} - - -//------------------------------------------------------------------------------ -// -// blockTopLoc() Find or create a location in the compiled pattern -// at the start of the operation or block that has -// just been compiled. Needed when a quantifier (* or -// whatever) appears, and we need to add an operation -// at the start of the thing being quantified. -// -// (Parenthesized Blocks) have a slot with a NOP that -// is reserved for this purpose. .* or similar don't -// and a slot needs to be added. -// -// parameter reserveLoc : true - ensure that there is space to add an opcode -// at the returned location. -// false - just return the address, -// do not reserve a location there. -// -//------------------------------------------------------------------------------ -int32_t RegexCompile::blockTopLoc(UBool reserveLoc) { - int32_t theLoc; - fixLiterals(true); // Emit code for any pending literals. - // If last item was a string, emit separate op for the its last char. - if (fRXPat->fCompiledPat->size() == fMatchCloseParen) - { - // The item just processed is a parenthesized block. - theLoc = fMatchOpenParen; // A slot is already reserved for us. - U_ASSERT(theLoc > 0); - U_ASSERT(URX_TYPE(((uint32_t)fRXPat->fCompiledPat->elementAti(theLoc))) == URX_NOP); - } - else { - // Item just compiled is a single thing, a ".", or a single char, a string or a set reference. - // No slot for STATE_SAVE was pre-reserved in the compiled code. - // We need to make space now. - theLoc = fRXPat->fCompiledPat->size()-1; - int32_t opAtTheLoc = (int32_t)fRXPat->fCompiledPat->elementAti(theLoc); - if (URX_TYPE(opAtTheLoc) == URX_STRING_LEN) { - // Strings take two opcode, we want the position of the first one. - // We can have a string at this point if a single character case-folded to two. - theLoc--; - } - if (reserveLoc) { - int32_t nop = buildOp(URX_NOP, 0); - fRXPat->fCompiledPat->insertElementAt(nop, theLoc, *fStatus); - } - } - return theLoc; -} - - - -//------------------------------------------------------------------------------ -// -// handleCloseParen When compiling a close paren, we need to go back -// and fix up any JMP or SAVE operations within the -// parenthesized block that need to target the end -// of the block. The locations of these are kept on -// the paretheses stack. -// -// This function is called both when encountering a -// real ) and at the end of the pattern. -// -//------------------------------------------------------------------------------ -void RegexCompile::handleCloseParen() { - int32_t patIdx; - int32_t patOp; - if (fParenStack.size() <= 0) { - error(U_REGEX_MISMATCHED_PAREN); - return; - } - - // Emit code for any pending literals. - fixLiterals(false); - - // Fixup any operations within the just-closed parenthesized group - // that need to reference the end of the (block). - // (The first one popped from the stack is an unused slot for - // alternation (OR) state save, but applying the fixup to it does no harm.) - for (;;) { - patIdx = fParenStack.popi(); - if (patIdx < 0) { - // value < 0 flags the start of the frame on the paren stack. - break; - } - U_ASSERT(patIdx>0 && patIdx <= fRXPat->fCompiledPat->size()); - patOp = (int32_t)fRXPat->fCompiledPat->elementAti(patIdx); - U_ASSERT(URX_VAL(patOp) == 0); // Branch target for JMP should not be set. - patOp |= fRXPat->fCompiledPat->size(); // Set it now. - fRXPat->fCompiledPat->setElementAt(patOp, patIdx); - fMatchOpenParen = patIdx; - } - - // At the close of any parenthesized block, restore the match mode flags to - // the value they had at the open paren. Saved value is - // at the top of the paren stack. - fModeFlags = fParenStack.popi(); - U_ASSERT(fModeFlags < 0); - - // DO any additional fixups, depending on the specific kind of - // parentesized grouping this is - - switch (patIdx) { - case plain: - case flags: - // No additional fixups required. - // (Grouping-only parentheses) - break; - case capturing: - // Capturing Parentheses. - // Insert a End Capture op into the pattern. - // The frame offset of the variables for this cg is obtained from the - // start capture op and put it into the end-capture op. - { - int32_t captureOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen+1); - U_ASSERT(URX_TYPE(captureOp) == URX_START_CAPTURE); - - int32_t frameVarLocation = URX_VAL(captureOp); - appendOp(URX_END_CAPTURE, frameVarLocation); - } - break; - case atomic: - // Atomic Parenthesis. - // Insert a LD_SP operation to restore the state stack to the position - // it was when the atomic parens were entered. - { - int32_t stoOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen+1); - U_ASSERT(URX_TYPE(stoOp) == URX_STO_SP); - int32_t stoLoc = URX_VAL(stoOp); - appendOp(URX_LD_SP, stoLoc); - } - break; - - case lookAhead: - { - int32_t startOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen-5); - U_ASSERT(URX_TYPE(startOp) == URX_LA_START); - int32_t dataLoc = URX_VAL(startOp); - appendOp(URX_LA_END, dataLoc); - } - break; - - case negLookAhead: - { - // See comment at doOpenLookAheadNeg - int32_t startOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen-1); - U_ASSERT(URX_TYPE(startOp) == URX_LA_START); - int32_t dataLoc = URX_VAL(startOp); - appendOp(URX_LA_END, dataLoc); - appendOp(URX_BACKTRACK, 0); - appendOp(URX_LA_END, dataLoc); - - // Patch the URX_SAVE near the top of the block. - // The destination of the SAVE is the final LA_END that was just added. - int32_t saveOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen); - U_ASSERT(URX_TYPE(saveOp) == URX_STATE_SAVE); - int32_t dest = fRXPat->fCompiledPat->size()-1; - saveOp = buildOp(URX_STATE_SAVE, dest); - fRXPat->fCompiledPat->setElementAt(saveOp, fMatchOpenParen); - } - break; - - case lookBehind: - { - // See comment at doOpenLookBehind. - - // Append the URX_LB_END and URX_LA_END to the compiled pattern. - int32_t startOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen-4); - U_ASSERT(URX_TYPE(startOp) == URX_LB_START); - int32_t dataLoc = URX_VAL(startOp); - appendOp(URX_LB_END, dataLoc); - appendOp(URX_LA_END, dataLoc); - - // Determine the min and max bounds for the length of the - // string that the pattern can match. - // An unbounded upper limit is an error. - int32_t patEnd = fRXPat->fCompiledPat->size() - 1; - int32_t minML = minMatchLength(fMatchOpenParen, patEnd); - int32_t maxML = maxMatchLength(fMatchOpenParen, patEnd); - if (URX_TYPE(maxML) != 0) { - error(U_REGEX_LOOK_BEHIND_LIMIT); - break; - } - if (maxML == INT32_MAX) { - error(U_REGEX_LOOK_BEHIND_LIMIT); - break; - } - if (minML == INT32_MAX) { - // This condition happens when no match is possible, such as with a - // [set] expression containing no elements. - // In principle, the generated code to evaluate the expression could be deleted, - // but it's probably not worth the complication. - minML = 0; - } - U_ASSERT(minML <= maxML); - - // Insert the min and max match len bounds into the URX_LB_CONT op that - // appears at the top of the look-behind block, at location fMatchOpenParen+1 - fRXPat->fCompiledPat->setElementAt(minML, fMatchOpenParen-2); - fRXPat->fCompiledPat->setElementAt(maxML, fMatchOpenParen-1); - - } - break; - - - - case lookBehindN: - { - // See comment at doOpenLookBehindNeg. - - // Append the URX_LBN_END to the compiled pattern. - int32_t startOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen-5); - U_ASSERT(URX_TYPE(startOp) == URX_LB_START); - int32_t dataLoc = URX_VAL(startOp); - appendOp(URX_LBN_END, dataLoc); - - // Determine the min and max bounds for the length of the - // string that the pattern can match. - // An unbounded upper limit is an error. - int32_t patEnd = fRXPat->fCompiledPat->size() - 1; - int32_t minML = minMatchLength(fMatchOpenParen, patEnd); - int32_t maxML = maxMatchLength(fMatchOpenParen, patEnd); - if (URX_TYPE(maxML) != 0) { - error(U_REGEX_LOOK_BEHIND_LIMIT); - break; - } - if (maxML == INT32_MAX) { - error(U_REGEX_LOOK_BEHIND_LIMIT); - break; - } - if (minML == INT32_MAX) { - // This condition happens when no match is possible, such as with a - // [set] expression containing no elements. - // In principle, the generated code to evaluate the expression could be deleted, - // but it's probably not worth the complication. - minML = 0; - } - - U_ASSERT(minML <= maxML); - - // Insert the min and max match len bounds into the URX_LB_CONT op that - // appears at the top of the look-behind block, at location fMatchOpenParen+1 - fRXPat->fCompiledPat->setElementAt(minML, fMatchOpenParen-3); - fRXPat->fCompiledPat->setElementAt(maxML, fMatchOpenParen-2); - - // Insert the pattern location to continue at after a successful match - // as the last operand of the URX_LBN_CONT - int32_t op = buildOp(URX_RELOC_OPRND, fRXPat->fCompiledPat->size()); - fRXPat->fCompiledPat->setElementAt(op, fMatchOpenParen-1); - } - break; - - - - default: - UPRV_UNREACHABLE_EXIT; - } - - // remember the next location in the compiled pattern. - // The compilation of Quantifiers will look at this to see whether its looping - // over a parenthesized block or a single item - fMatchCloseParen = fRXPat->fCompiledPat->size(); -} - - - -//------------------------------------------------------------------------------ -// -// compileSet Compile the pattern operations for a reference to a -// UnicodeSet. -// -//------------------------------------------------------------------------------ -void RegexCompile::compileSet(UnicodeSet *theSet) -{ - if (theSet == NULL) { - return; - } - // Remove any strings from the set. - // There shouldn't be any, but just in case. - // (Case Closure can add them; if we had a simple case closure available that - // ignored strings, that would be better.) - theSet->removeAllStrings(); - int32_t setSize = theSet->size(); - - switch (setSize) { - case 0: - { - // Set of no elements. Always fails to match. - appendOp(URX_BACKTRACK, 0); - delete theSet; - } - break; - - case 1: - { - // The set contains only a single code point. Put it into - // the compiled pattern as a single char operation rather - // than a set, and discard the set itself. - literalChar(theSet->charAt(0)); - delete theSet; - } - break; - - default: - { - // The set contains two or more chars. (the normal case) - // Put it into the compiled pattern as a set. - theSet->freeze(); - int32_t setNumber = fRXPat->fSets->size(); - fRXPat->fSets->addElement(theSet, *fStatus); - if (U_SUCCESS(*fStatus)) { - appendOp(URX_SETREF, setNumber); - } else { - delete theSet; - } - } - } -} - - -//------------------------------------------------------------------------------ -// -// compileInterval Generate the code for a {min, max} style interval quantifier. -// Except for the specific opcodes used, the code is the same -// for all three types (greedy, non-greedy, possessive) of -// intervals. The opcodes are supplied as parameters. -// (There are two sets of opcodes - greedy & possessive use the -// same ones, while non-greedy has it's own.) -// -// The code for interval loops has this form: -// 0 CTR_INIT counter loc (in stack frame) -// 1 5 patt address of CTR_LOOP at bottom of block -// 2 min count -// 3 max count (-1 for unbounded) -// 4 ... block to be iterated over -// 5 CTR_LOOP -// -// In -//------------------------------------------------------------------------------ -void RegexCompile::compileInterval(int32_t InitOp, int32_t LoopOp) -{ - // The CTR_INIT op at the top of the block with the {n,m} quantifier takes - // four slots in the compiled code. Reserve them. - int32_t topOfBlock = blockTopLoc(true); - insertOp(topOfBlock); - insertOp(topOfBlock); - insertOp(topOfBlock); - - // The operands for the CTR_INIT opcode include the index in the matcher data - // of the counter. Allocate it now. There are two data items - // counterLoc --> Loop counter - // +1 --> Input index (for breaking non-progressing loops) - // (Only present if unbounded upper limit on loop) - int32_t dataSize = fIntervalUpper < 0 ? 2 : 1; - int32_t counterLoc = allocateStackData(dataSize); - - int32_t op = buildOp(InitOp, counterLoc); - fRXPat->fCompiledPat->setElementAt(op, topOfBlock); - - // The second operand of CTR_INIT is the location following the end of the loop. - // Must put in as a URX_RELOC_OPRND so that the value will be adjusted if the - // compilation of something later on causes the code to grow and the target - // position to move. - int32_t loopEnd = fRXPat->fCompiledPat->size(); - op = buildOp(URX_RELOC_OPRND, loopEnd); - fRXPat->fCompiledPat->setElementAt(op, topOfBlock+1); - - // Followed by the min and max counts. - fRXPat->fCompiledPat->setElementAt(fIntervalLow, topOfBlock+2); - fRXPat->fCompiledPat->setElementAt(fIntervalUpper, topOfBlock+3); - - // Append the CTR_LOOP op. The operand is the location of the CTR_INIT op. - // Goes at end of the block being looped over, so just append to the code so far. - appendOp(LoopOp, topOfBlock); - - if ((fIntervalLow & 0xff000000) != 0 || - (fIntervalUpper > 0 && (fIntervalUpper & 0xff000000) != 0)) { - error(U_REGEX_NUMBER_TOO_BIG); - } - - if (fIntervalLow > fIntervalUpper && fIntervalUpper != -1) { - error(U_REGEX_MAX_LT_MIN); - } -} - - - -UBool RegexCompile::compileInlineInterval() { - if (fIntervalUpper > 10 || fIntervalUpper < fIntervalLow) { - // Too big to inline. Fail, which will cause looping code to be generated. - // (Upper < Lower picks up unbounded upper and errors, both.) - return false; - } - - int32_t topOfBlock = blockTopLoc(false); - if (fIntervalUpper == 0) { - // Pathological case. Attempt no matches, as if the block doesn't exist. - // Discard the generated code for the block. - // If the block included parens, discard the info pertaining to them as well. - fRXPat->fCompiledPat->setSize(topOfBlock); - if (fMatchOpenParen >= topOfBlock) { - fMatchOpenParen = -1; - } - if (fMatchCloseParen >= topOfBlock) { - fMatchCloseParen = -1; - } - return true; - } - - if (topOfBlock != fRXPat->fCompiledPat->size()-1 && fIntervalUpper != 1) { - // The thing being repeated is not a single op, but some - // more complex block. Do it as a loop, not inlines. - // Note that things "repeated" a max of once are handled as inline, because - // the one copy of the code already generated is just fine. - return false; - } - - // Pick up the opcode that is to be repeated - // - int32_t op = (int32_t)fRXPat->fCompiledPat->elementAti(topOfBlock); - - // Compute the pattern location where the inline sequence - // will end, and set up the state save op that will be needed. - // - int32_t endOfSequenceLoc = fRXPat->fCompiledPat->size()-1 - + fIntervalUpper + (fIntervalUpper-fIntervalLow); - int32_t saveOp = buildOp(URX_STATE_SAVE, endOfSequenceLoc); - if (fIntervalLow == 0) { - insertOp(topOfBlock); - fRXPat->fCompiledPat->setElementAt(saveOp, topOfBlock); - } - - - - // Loop, emitting the op for the thing being repeated each time. - // Loop starts at 1 because one instance of the op already exists in the pattern, - // it was put there when it was originally encountered. - int32_t i; - for (i=1; i= fIntervalLow) { - appendOp(saveOp); - } - appendOp(op); - } - return true; -} - - - -//------------------------------------------------------------------------------ -// -// caseInsensitiveStart given a single code point from a pattern string, determine the -// set of characters that could potentially begin a case-insensitive -// match of a string beginning with that character, using full Unicode -// case insensitive matching. -// -// This is used in optimizing find(). -// -// closeOver(USET_CASE_INSENSITIVE) does most of what is needed, but -// misses cases like this: -// A string from the pattern begins with 'ss' (although all we know -// in this context is that it begins with 's') -// The pattern could match a string beginning with a German sharp-s -// -// To the ordinary case closure for a character c, we add all other -// characters cx where the case closure of cx includes a string form that begins -// with the original character c. -// -// This function could be made smarter. The full pattern string is available -// and it would be possible to verify that the extra characters being added -// to the starting set fully match, rather than having just a first-char of the -// folded form match. -// -//------------------------------------------------------------------------------ -void RegexCompile::findCaseInsensitiveStarters(UChar32 c, UnicodeSet *starterChars) { - -// Machine Generated below. -// It may need updating with new versions of Unicode. -// Intltest test RegexTest::TestCaseInsensitiveStarters will fail if an update is needed. -// The update tool is here: -// https://github.com/unicode-org/icu/tree/main/tools/unicode/c/genregexcasing - -// Machine Generated Data. Do not hand edit. - static const UChar32 RECaseFixCodePoints[] = { - 0x61, 0x66, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x77, 0x79, 0x2bc, - 0x3ac, 0x3ae, 0x3b1, 0x3b7, 0x3b9, 0x3c1, 0x3c5, 0x3c9, 0x3ce, 0x565, - 0x574, 0x57e, 0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07, - 0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27, 0x1f60, 0x1f61, - 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67, 0x1f70, 0x1f74, 0x1f7c, 0x110000}; - - static const int16_t RECaseFixStringOffsets[] = { - 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xd, 0xe, 0xf, 0x10, - 0x11, 0x12, 0x13, 0x17, 0x1b, 0x20, 0x21, 0x2a, 0x2e, 0x2f, - 0x30, 0x34, 0x35, 0x37, 0x39, 0x3b, 0x3d, 0x3f, 0x41, 0x43, - 0x45, 0x47, 0x49, 0x4b, 0x4d, 0x4f, 0x51, 0x53, 0x55, 0x57, - 0x59, 0x5b, 0x5d, 0x5f, 0x61, 0x63, 0x65, 0x66, 0x67, 0}; - - static const int16_t RECaseFixCounts[] = { - 0x1, 0x5, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, - 0x1, 0x1, 0x4, 0x4, 0x5, 0x1, 0x9, 0x4, 0x1, 0x1, - 0x4, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0}; - - static const UChar RECaseFixData[] = { - 0x1e9a, 0xfb00, 0xfb01, 0xfb02, 0xfb03, 0xfb04, 0x1e96, 0x130, 0x1f0, 0xdf, - 0x1e9e, 0xfb05, 0xfb06, 0x1e97, 0x1e98, 0x1e99, 0x149, 0x1fb4, 0x1fc4, 0x1fb3, - 0x1fb6, 0x1fb7, 0x1fbc, 0x1fc3, 0x1fc6, 0x1fc7, 0x1fcc, 0x390, 0x1fd2, 0x1fd3, - 0x1fd6, 0x1fd7, 0x1fe4, 0x3b0, 0x1f50, 0x1f52, 0x1f54, 0x1f56, 0x1fe2, 0x1fe3, - 0x1fe6, 0x1fe7, 0x1ff3, 0x1ff6, 0x1ff7, 0x1ffc, 0x1ff4, 0x587, 0xfb13, 0xfb14, - 0xfb15, 0xfb17, 0xfb16, 0x1f80, 0x1f88, 0x1f81, 0x1f89, 0x1f82, 0x1f8a, 0x1f83, - 0x1f8b, 0x1f84, 0x1f8c, 0x1f85, 0x1f8d, 0x1f86, 0x1f8e, 0x1f87, 0x1f8f, 0x1f90, - 0x1f98, 0x1f91, 0x1f99, 0x1f92, 0x1f9a, 0x1f93, 0x1f9b, 0x1f94, 0x1f9c, 0x1f95, - 0x1f9d, 0x1f96, 0x1f9e, 0x1f97, 0x1f9f, 0x1fa0, 0x1fa8, 0x1fa1, 0x1fa9, 0x1fa2, - 0x1faa, 0x1fa3, 0x1fab, 0x1fa4, 0x1fac, 0x1fa5, 0x1fad, 0x1fa6, 0x1fae, 0x1fa7, - 0x1faf, 0x1fb2, 0x1fc2, 0x1ff2, 0}; - -// End of machine generated data. - - if (c < UCHAR_MIN_VALUE || c > UCHAR_MAX_VALUE) { - // This function should never be called with an invalid input character. - UPRV_UNREACHABLE_EXIT; - } else if (u_hasBinaryProperty(c, UCHAR_CASE_SENSITIVE)) { - UChar32 caseFoldedC = u_foldCase(c, U_FOLD_CASE_DEFAULT); - starterChars->set(caseFoldedC, caseFoldedC); - - int32_t i; - for (i=0; RECaseFixCodePoints[i]add(cpToAdd); - } - } - - starterChars->closeOver(USET_CASE_INSENSITIVE); - starterChars->removeAllStrings(); - } else { - // Not a cased character. Just return it alone. - starterChars->set(c, c); - } -} - - -// Increment with overflow check. -// val and delta will both be positive. - -static int32_t safeIncrement(int32_t val, int32_t delta) { - if (INT32_MAX - val > delta) { - return val + delta; - } else { - return INT32_MAX; - } -} - - -//------------------------------------------------------------------------------ -// -// matchStartType Determine how a match can start. -// Used to optimize find() operations. -// -// Operation is very similar to minMatchLength(). Walk the compiled -// pattern, keeping an on-going minimum-match-length. For any -// op where the min match coming in is zero, add that ops possible -// starting matches to the possible starts for the overall pattern. -// -//------------------------------------------------------------------------------ -void RegexCompile::matchStartType() { - if (U_FAILURE(*fStatus)) { - return; - } - - - int32_t loc; // Location in the pattern of the current op being processed. - int32_t op; // The op being processed - int32_t opType; // The opcode type of the op - int32_t currentLen = 0; // Minimum length of a match to this point (loc) in the pattern - int32_t numInitialStrings = 0; // Number of strings encountered that could match at start. - - UBool atStart = true; // True if no part of the pattern yet encountered - // could have advanced the position in a match. - // (Maximum match length so far == 0) - - // forwardedLength is a vector holding minimum-match-length values that - // are propagated forward in the pattern by JMP or STATE_SAVE operations. - // It must be one longer than the pattern being checked because some ops - // will jmp to a end-of-block+1 location from within a block, and we must - // count those when checking the block. - int32_t end = fRXPat->fCompiledPat->size(); - UVector32 forwardedLength(end+1, *fStatus); - forwardedLength.setSize(end+1); - for (loc=3; locfCompiledPat->elementAti(loc); - opType = URX_TYPE(op); - - // The loop is advancing linearly through the pattern. - // If the op we are now at was the destination of a branch in the pattern, - // and that path has a shorter minimum length than the current accumulated value, - // replace the current accumulated value. - if (forwardedLength.elementAti(loc) < currentLen) { - currentLen = forwardedLength.elementAti(loc); - U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); - } - - switch (opType) { - // Ops that don't change the total length matched - case URX_RESERVED_OP: - case URX_END: - case URX_FAIL: - case URX_STRING_LEN: - case URX_NOP: - case URX_START_CAPTURE: - case URX_END_CAPTURE: - case URX_BACKSLASH_B: - case URX_BACKSLASH_BU: - case URX_BACKSLASH_G: - case URX_BACKSLASH_Z: - case URX_DOLLAR: - case URX_DOLLAR_M: - case URX_DOLLAR_D: - case URX_DOLLAR_MD: - case URX_RELOC_OPRND: - case URX_STO_INP_LOC: - case URX_BACKREF: // BackRef. Must assume that it might be a zero length match - case URX_BACKREF_I: - - case URX_STO_SP: // Setup for atomic or possessive blocks. Doesn't change what can match. - case URX_LD_SP: - break; - - case URX_CARET: - if (atStart) { - fRXPat->fStartType = START_START; - } - break; - - case URX_CARET_M: - case URX_CARET_M_UNIX: - if (atStart) { - fRXPat->fStartType = START_LINE; - } - break; - - case URX_ONECHAR: - if (currentLen == 0) { - // This character could appear at the start of a match. - // Add it to the set of possible starting characters. - fRXPat->fInitialChars->add(URX_VAL(op)); - numInitialStrings += 2; - } - currentLen = safeIncrement(currentLen, 1); - atStart = false; - break; - - - case URX_SETREF: - if (currentLen == 0) { - int32_t sn = URX_VAL(op); - U_ASSERT(sn > 0 && sn < fRXPat->fSets->size()); - const UnicodeSet *s = (UnicodeSet *)fRXPat->fSets->elementAt(sn); - fRXPat->fInitialChars->addAll(*s); - numInitialStrings += 2; - } - currentLen = safeIncrement(currentLen, 1); - atStart = false; - break; - - case URX_LOOP_SR_I: - // [Set]*, like a SETREF, above, in what it can match, - // but may not match at all, so currentLen is not incremented. - if (currentLen == 0) { - int32_t sn = URX_VAL(op); - U_ASSERT(sn > 0 && sn < fRXPat->fSets->size()); - const UnicodeSet *s = (UnicodeSet *)fRXPat->fSets->elementAt(sn); - fRXPat->fInitialChars->addAll(*s); - numInitialStrings += 2; - } - atStart = false; - break; - - case URX_LOOP_DOT_I: - if (currentLen == 0) { - // .* at the start of a pattern. - // Any character can begin the match. - fRXPat->fInitialChars->clear(); - fRXPat->fInitialChars->complement(); - numInitialStrings += 2; - } - atStart = false; - break; - - - case URX_STATIC_SETREF: - if (currentLen == 0) { - int32_t sn = URX_VAL(op); - U_ASSERT(sn>0 && snfPropSets[sn]; - fRXPat->fInitialChars->addAll(s); - numInitialStrings += 2; - } - currentLen = safeIncrement(currentLen, 1); - atStart = false; - break; - - - - case URX_STAT_SETREF_N: - if (currentLen == 0) { - int32_t sn = URX_VAL(op); - UnicodeSet sc; - sc.addAll(RegexStaticSets::gStaticSets->fPropSets[sn]).complement(); - fRXPat->fInitialChars->addAll(sc); - numInitialStrings += 2; - } - currentLen = safeIncrement(currentLen, 1); - atStart = false; - break; - - - - case URX_BACKSLASH_D: - // Digit Char - if (currentLen == 0) { - UnicodeSet s; - s.applyIntPropertyValue(UCHAR_GENERAL_CATEGORY_MASK, U_GC_ND_MASK, *fStatus); - if (URX_VAL(op) != 0) { - s.complement(); - } - fRXPat->fInitialChars->addAll(s); - numInitialStrings += 2; - } - currentLen = safeIncrement(currentLen, 1); - atStart = false; - break; - - - case URX_BACKSLASH_H: - // Horiz white space - if (currentLen == 0) { - UnicodeSet s; - s.applyIntPropertyValue(UCHAR_GENERAL_CATEGORY_MASK, U_GC_ZS_MASK, *fStatus); - s.add((UChar32)9); // Tab - if (URX_VAL(op) != 0) { - s.complement(); - } - fRXPat->fInitialChars->addAll(s); - numInitialStrings += 2; - } - currentLen = safeIncrement(currentLen, 1); - atStart = false; - break; - - - case URX_BACKSLASH_R: // Any line ending sequence - case URX_BACKSLASH_V: // Any line ending code point, with optional negation - if (currentLen == 0) { - UnicodeSet s; - s.add((UChar32)0x0a, (UChar32)0x0d); // add range - s.add((UChar32)0x85); - s.add((UChar32)0x2028, (UChar32)0x2029); - if (URX_VAL(op) != 0) { - // Complement option applies to URX_BACKSLASH_V only. - s.complement(); - } - fRXPat->fInitialChars->addAll(s); - numInitialStrings += 2; - } - currentLen = safeIncrement(currentLen, 1); - atStart = false; - break; - - - - case URX_ONECHAR_I: - // Case Insensitive Single Character. - if (currentLen == 0) { - UChar32 c = URX_VAL(op); - if (u_hasBinaryProperty(c, UCHAR_CASE_SENSITIVE)) { - UnicodeSet starters(c, c); - starters.closeOver(USET_CASE_INSENSITIVE); - // findCaseInsensitiveStarters(c, &starters); - // For ONECHAR_I, no need to worry about text chars that expand on folding into strings. - // The expanded folding can't match the pattern. - fRXPat->fInitialChars->addAll(starters); - } else { - // Char has no case variants. Just add it as-is to the - // set of possible starting chars. - fRXPat->fInitialChars->add(c); - } - numInitialStrings += 2; - } - currentLen = safeIncrement(currentLen, 1); - atStart = false; - break; - - - case URX_BACKSLASH_X: // Grapheme Cluster. Minimum is 1, max unbounded. - case URX_DOTANY_ALL: // . matches one or two. - case URX_DOTANY: - case URX_DOTANY_UNIX: - if (currentLen == 0) { - // These constructs are all bad news when they appear at the start - // of a match. Any character can begin the match. - fRXPat->fInitialChars->clear(); - fRXPat->fInitialChars->complement(); - numInitialStrings += 2; - } - currentLen = safeIncrement(currentLen, 1); - atStart = false; - break; - - - case URX_JMPX: - loc++; // Except for extra operand on URX_JMPX, same as URX_JMP. - U_FALLTHROUGH; - case URX_JMP: - { - int32_t jmpDest = URX_VAL(op); - if (jmpDest < loc) { - // Loop of some kind. Can safely ignore, the worst that will happen - // is that we understate the true minimum length - currentLen = forwardedLength.elementAti(loc+1); - - } else { - // Forward jump. Propagate the current min length to the target loc of the jump. - U_ASSERT(jmpDest <= end+1); - if (forwardedLength.elementAti(jmpDest) > currentLen) { - forwardedLength.setElementAt(currentLen, jmpDest); - } - } - } - atStart = false; - break; - - case URX_JMP_SAV: - case URX_JMP_SAV_X: - // Combo of state save to the next loc, + jmp backwards. - // Net effect on min. length computation is nothing. - atStart = false; - break; - - case URX_BACKTRACK: - // Fails are kind of like a branch, except that the min length was - // propagated already, by the state save. - currentLen = forwardedLength.elementAti(loc+1); - atStart = false; - break; - - - case URX_STATE_SAVE: - { - // State Save, for forward jumps, propagate the current minimum. - // of the state save. - int32_t jmpDest = URX_VAL(op); - if (jmpDest > loc) { - if (currentLen < forwardedLength.elementAti(jmpDest)) { - forwardedLength.setElementAt(currentLen, jmpDest); - } - } - } - atStart = false; - break; - - - - - case URX_STRING: - { - loc++; - int32_t stringLenOp = (int32_t)fRXPat->fCompiledPat->elementAti(loc); - int32_t stringLen = URX_VAL(stringLenOp); - U_ASSERT(URX_TYPE(stringLenOp) == URX_STRING_LEN); - U_ASSERT(stringLenOp >= 2); - if (currentLen == 0) { - // Add the starting character of this string to the set of possible starting - // characters for this pattern. - int32_t stringStartIdx = URX_VAL(op); - UChar32 c = fRXPat->fLiteralText.char32At(stringStartIdx); - fRXPat->fInitialChars->add(c); - - // Remember this string. After the entire pattern has been checked, - // if nothing else is identified that can start a match, we'll use it. - numInitialStrings++; - fRXPat->fInitialStringIdx = stringStartIdx; - fRXPat->fInitialStringLen = stringLen; - } - - currentLen = safeIncrement(currentLen, stringLen); - atStart = false; - } - break; - - case URX_STRING_I: - { - // Case-insensitive string. Unlike exact-match strings, we won't - // attempt a string search for possible match positions. But we - // do update the set of possible starting characters. - loc++; - int32_t stringLenOp = (int32_t)fRXPat->fCompiledPat->elementAti(loc); - int32_t stringLen = URX_VAL(stringLenOp); - U_ASSERT(URX_TYPE(stringLenOp) == URX_STRING_LEN); - U_ASSERT(stringLenOp >= 2); - if (currentLen == 0) { - // Add the starting character of this string to the set of possible starting - // characters for this pattern. - int32_t stringStartIdx = URX_VAL(op); - UChar32 c = fRXPat->fLiteralText.char32At(stringStartIdx); - UnicodeSet s; - findCaseInsensitiveStarters(c, &s); - fRXPat->fInitialChars->addAll(s); - numInitialStrings += 2; // Matching on an initial string not possible. - } - currentLen = safeIncrement(currentLen, stringLen); - atStart = false; - } - break; - - case URX_CTR_INIT: - case URX_CTR_INIT_NG: - { - // Loop Init Ops. These don't change the min length, but they are 4 word ops - // so location must be updated accordingly. - // Loop Init Ops. - // If the min loop count == 0 - // move loc forwards to the end of the loop, skipping over the body. - // If the min count is > 0, - // continue normal processing of the body of the loop. - int32_t loopEndLoc = (int32_t)fRXPat->fCompiledPat->elementAti(loc+1); - loopEndLoc = URX_VAL(loopEndLoc); - int32_t minLoopCount = (int32_t)fRXPat->fCompiledPat->elementAti(loc+2); - if (minLoopCount == 0) { - // Min Loop Count of 0, treat like a forward branch and - // move the current minimum length up to the target - // (end of loop) location. - U_ASSERT(loopEndLoc <= end+1); - if (forwardedLength.elementAti(loopEndLoc) > currentLen) { - forwardedLength.setElementAt(currentLen, loopEndLoc); - } - } - loc+=3; // Skips over operands of CTR_INIT - } - atStart = false; - break; - - - case URX_CTR_LOOP: - case URX_CTR_LOOP_NG: - // Loop ops. - // The jump is conditional, backwards only. - atStart = false; - break; - - case URX_LOOP_C: - // More loop ops. These state-save to themselves. - // don't change the minimum match - atStart = false; - break; - - - case URX_LA_START: - case URX_LB_START: - { - // Look-around. Scan forward until the matching look-ahead end, - // without processing the look-around block. This is overly pessimistic. - - // Keep track of the nesting depth of look-around blocks. Boilerplate code for - // lookahead contains two LA_END instructions, so count goes up by two - // for each LA_START. - int32_t depth = (opType == URX_LA_START? 2: 1); - for (;;) { - loc++; - op = (int32_t)fRXPat->fCompiledPat->elementAti(loc); - if (URX_TYPE(op) == URX_LA_START) { - depth+=2; - } - if (URX_TYPE(op) == URX_LB_START) { - depth++; - } - if (URX_TYPE(op) == URX_LA_END || URX_TYPE(op)==URX_LBN_END) { - depth--; - if (depth == 0) { - break; - } - } - if (URX_TYPE(op) == URX_STATE_SAVE) { - // Need this because neg lookahead blocks will FAIL to outside - // of the block. - int32_t jmpDest = URX_VAL(op); - if (jmpDest > loc) { - if (currentLen < forwardedLength.elementAti(jmpDest)) { - forwardedLength.setElementAt(currentLen, jmpDest); - } - } - } - U_ASSERT(loc <= end); - } - } - break; - - case URX_LA_END: - case URX_LB_CONT: - case URX_LB_END: - case URX_LBN_CONT: - case URX_LBN_END: - UPRV_UNREACHABLE_EXIT; // Shouldn't get here. These ops should be - // consumed by the scan in URX_LA_START and LB_START - default: - UPRV_UNREACHABLE_EXIT; - } - - } - - - // We have finished walking through the ops. Check whether some forward jump - // propagated a shorter length to location end+1. - if (forwardedLength.elementAti(end+1) < currentLen) { - currentLen = forwardedLength.elementAti(end+1); - } - - - fRXPat->fInitialChars8->init(fRXPat->fInitialChars); - - - // Sort out what we should check for when looking for candidate match start positions. - // In order of preference, - // 1. Start of input text buffer. - // 2. A literal string. - // 3. Start of line in multi-line mode. - // 4. A single literal character. - // 5. A character from a set of characters. - // - if (fRXPat->fStartType == START_START) { - // Match only at the start of an input text string. - // start type is already set. We're done. - } else if (numInitialStrings == 1 && fRXPat->fMinMatchLen > 0) { - // Match beginning only with a literal string. - UChar32 c = fRXPat->fLiteralText.char32At(fRXPat->fInitialStringIdx); - U_ASSERT(fRXPat->fInitialChars->contains(c)); - fRXPat->fStartType = START_STRING; - fRXPat->fInitialChar = c; - } else if (fRXPat->fStartType == START_LINE) { - // Match at start of line in Multi-Line mode. - // Nothing to do here; everything is already set. - } else if (fRXPat->fMinMatchLen == 0) { - // Zero length match possible. We could start anywhere. - fRXPat->fStartType = START_NO_INFO; - } else if (fRXPat->fInitialChars->size() == 1) { - // All matches begin with the same char. - fRXPat->fStartType = START_CHAR; - fRXPat->fInitialChar = fRXPat->fInitialChars->charAt(0); - U_ASSERT(fRXPat->fInitialChar != (UChar32)-1); - } else if (fRXPat->fInitialChars->contains((UChar32)0, (UChar32)0x10ffff) == false && - fRXPat->fMinMatchLen > 0) { - // Matches start with a set of character smaller than the set of all chars. - fRXPat->fStartType = START_SET; - } else { - // Matches can start with anything - fRXPat->fStartType = START_NO_INFO; - } - - return; -} - - - -//------------------------------------------------------------------------------ -// -// minMatchLength Calculate the length of the shortest string that could -// match the specified pattern. -// Length is in 16 bit code units, not code points. -// -// The calculated length may not be exact. The returned -// value may be shorter than the actual minimum; it must -// never be longer. -// -// start and end are the range of p-code operations to be -// examined. The endpoints are included in the range. -// -//------------------------------------------------------------------------------ -int32_t RegexCompile::minMatchLength(int32_t start, int32_t end) { - if (U_FAILURE(*fStatus)) { - return 0; - } - - U_ASSERT(start <= end); - U_ASSERT(end < fRXPat->fCompiledPat->size()); - - - int32_t loc; - int32_t op; - int32_t opType; - int32_t currentLen = 0; - - - // forwardedLength is a vector holding minimum-match-length values that - // are propagated forward in the pattern by JMP or STATE_SAVE operations. - // It must be one longer than the pattern being checked because some ops - // will jmp to a end-of-block+1 location from within a block, and we must - // count those when checking the block. - UVector32 forwardedLength(end+2, *fStatus); - forwardedLength.setSize(end+2); - for (loc=start; loc<=end+1; loc++) { - forwardedLength.setElementAt(INT32_MAX, loc); - } - - for (loc = start; loc<=end; loc++) { - op = (int32_t)fRXPat->fCompiledPat->elementAti(loc); - opType = URX_TYPE(op); - - // The loop is advancing linearly through the pattern. - // If the op we are now at was the destination of a branch in the pattern, - // and that path has a shorter minimum length than the current accumulated value, - // replace the current accumulated value. - // U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); // MinLength == INT32_MAX for some - // no-match-possible cases. - if (forwardedLength.elementAti(loc) < currentLen) { - currentLen = forwardedLength.elementAti(loc); - U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); - } - - switch (opType) { - // Ops that don't change the total length matched - case URX_RESERVED_OP: - case URX_END: - case URX_STRING_LEN: - case URX_NOP: - case URX_START_CAPTURE: - case URX_END_CAPTURE: - case URX_BACKSLASH_B: - case URX_BACKSLASH_BU: - case URX_BACKSLASH_G: - case URX_BACKSLASH_Z: - case URX_CARET: - case URX_DOLLAR: - case URX_DOLLAR_M: - case URX_DOLLAR_D: - case URX_DOLLAR_MD: - case URX_RELOC_OPRND: - case URX_STO_INP_LOC: - case URX_CARET_M: - case URX_CARET_M_UNIX: - case URX_BACKREF: // BackRef. Must assume that it might be a zero length match - case URX_BACKREF_I: - - case URX_STO_SP: // Setup for atomic or possessive blocks. Doesn't change what can match. - case URX_LD_SP: - - case URX_JMP_SAV: - case URX_JMP_SAV_X: - break; - - - // Ops that match a minimum of one character (one or two 16 bit code units.) - // - case URX_ONECHAR: - case URX_STATIC_SETREF: - case URX_STAT_SETREF_N: - case URX_SETREF: - case URX_BACKSLASH_D: - case URX_BACKSLASH_H: - case URX_BACKSLASH_R: - case URX_BACKSLASH_V: - case URX_ONECHAR_I: - case URX_BACKSLASH_X: // Grapheme Cluster. Minimum is 1, max unbounded. - case URX_DOTANY_ALL: // . matches one or two. - case URX_DOTANY: - case URX_DOTANY_UNIX: - currentLen = safeIncrement(currentLen, 1); - break; - - - case URX_JMPX: - loc++; // URX_JMPX has an extra operand, ignored here, - // otherwise processed identically to URX_JMP. - U_FALLTHROUGH; - case URX_JMP: - { - int32_t jmpDest = URX_VAL(op); - if (jmpDest < loc) { - // Loop of some kind. Can safely ignore, the worst that will happen - // is that we understate the true minimum length - currentLen = forwardedLength.elementAti(loc+1); - } else { - // Forward jump. Propagate the current min length to the target loc of the jump. - U_ASSERT(jmpDest <= end+1); - if (forwardedLength.elementAti(jmpDest) > currentLen) { - forwardedLength.setElementAt(currentLen, jmpDest); - } - } - } - break; - - case URX_BACKTRACK: - { - // Back-tracks are kind of like a branch, except that the min length was - // propagated already, by the state save. - currentLen = forwardedLength.elementAti(loc+1); - } - break; - - - case URX_STATE_SAVE: - { - // State Save, for forward jumps, propagate the current minimum. - // of the state save. - int32_t jmpDest = URX_VAL(op); - if (jmpDest > loc) { - if (currentLen < forwardedLength.elementAti(jmpDest)) { - forwardedLength.setElementAt(currentLen, jmpDest); - } - } - } - break; - - - case URX_STRING: - { - loc++; - int32_t stringLenOp = (int32_t)fRXPat->fCompiledPat->elementAti(loc); - currentLen = safeIncrement(currentLen, URX_VAL(stringLenOp)); - } - break; - - - case URX_STRING_I: - { - loc++; - // TODO: with full case folding, matching input text may be shorter than - // the string we have here. More smarts could put some bounds on it. - // Assume a min length of one for now. A min length of zero causes - // optimization failures for a pattern like "string"+ - // currentLen += URX_VAL(stringLenOp); - currentLen = safeIncrement(currentLen, 1); - } - break; - - case URX_CTR_INIT: - case URX_CTR_INIT_NG: - { - // Loop Init Ops. - // If the min loop count == 0 - // move loc forwards to the end of the loop, skipping over the body. - // If the min count is > 0, - // continue normal processing of the body of the loop. - int32_t loopEndLoc = (int32_t)fRXPat->fCompiledPat->elementAti(loc+1); - loopEndLoc = URX_VAL(loopEndLoc); - int32_t minLoopCount = (int32_t)fRXPat->fCompiledPat->elementAti(loc+2); - if (minLoopCount == 0) { - loc = loopEndLoc; - } else { - loc+=3; // Skips over operands of CTR_INIT - } - } - break; - - - case URX_CTR_LOOP: - case URX_CTR_LOOP_NG: - // Loop ops. - // The jump is conditional, backwards only. - break; - - case URX_LOOP_SR_I: - case URX_LOOP_DOT_I: - case URX_LOOP_C: - // More loop ops. These state-save to themselves. - // don't change the minimum match - could match nothing at all. - break; - - - case URX_LA_START: - case URX_LB_START: - { - // Look-around. Scan forward until the matching look-ahead end, - // without processing the look-around block. This is overly pessimistic for look-ahead, - // it assumes that the look-ahead match might be zero-length. - // TODO: Positive lookahead could recursively do the block, then continue - // with the longer of the block or the value coming in. Ticket 6060 - int32_t depth = (opType == URX_LA_START? 2: 1); - for (;;) { - loc++; - op = (int32_t)fRXPat->fCompiledPat->elementAti(loc); - if (URX_TYPE(op) == URX_LA_START) { - // The boilerplate for look-ahead includes two LA_END instructions, - // Depth will be decremented by each one when it is seen. - depth += 2; - } - if (URX_TYPE(op) == URX_LB_START) { - depth++; - } - if (URX_TYPE(op) == URX_LA_END) { - depth--; - if (depth == 0) { - break; - } - } - if (URX_TYPE(op)==URX_LBN_END) { - depth--; - if (depth == 0) { - break; - } - } - if (URX_TYPE(op) == URX_STATE_SAVE) { - // Need this because neg lookahead blocks will FAIL to outside - // of the block. - int32_t jmpDest = URX_VAL(op); - if (jmpDest > loc) { - if (currentLen < forwardedLength.elementAti(jmpDest)) { - forwardedLength.setElementAt(currentLen, jmpDest); - } - } - } - U_ASSERT(loc <= end); - } - } - break; - - case URX_LA_END: - case URX_LB_CONT: - case URX_LB_END: - case URX_LBN_CONT: - case URX_LBN_END: - // Only come here if the matching URX_LA_START or URX_LB_START was not in the - // range being sized, which happens when measuring size of look-behind blocks. - break; - - default: - UPRV_UNREACHABLE_EXIT; - } - - } - - // We have finished walking through the ops. Check whether some forward jump - // propagated a shorter length to location end+1. - if (forwardedLength.elementAti(end+1) < currentLen) { - currentLen = forwardedLength.elementAti(end+1); - U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); - } - - return currentLen; -} - -//------------------------------------------------------------------------------ -// -// maxMatchLength Calculate the length of the longest string that could -// match the specified pattern. -// Length is in 16 bit code units, not code points. -// -// The calculated length may not be exact. The returned -// value may be longer than the actual maximum; it must -// never be shorter. -// -// start, end: the range of the pattern to check. -// end is inclusive. -// -//------------------------------------------------------------------------------ -int32_t RegexCompile::maxMatchLength(int32_t start, int32_t end) { - if (U_FAILURE(*fStatus)) { - return 0; - } - U_ASSERT(start <= end); - U_ASSERT(end < fRXPat->fCompiledPat->size()); - - int32_t loc; - int32_t op; - int32_t opType; - int32_t currentLen = 0; - UVector32 forwardedLength(end+1, *fStatus); - forwardedLength.setSize(end+1); - - for (loc=start; loc<=end; loc++) { - forwardedLength.setElementAt(0, loc); - } - - for (loc = start; loc<=end; loc++) { - op = (int32_t)fRXPat->fCompiledPat->elementAti(loc); - opType = URX_TYPE(op); - - // The loop is advancing linearly through the pattern. - // If the op we are now at was the destination of a branch in the pattern, - // and that path has a longer maximum length than the current accumulated value, - // replace the current accumulated value. - if (forwardedLength.elementAti(loc) > currentLen) { - currentLen = forwardedLength.elementAti(loc); - } - - switch (opType) { - // Ops that don't change the total length matched - case URX_RESERVED_OP: - case URX_END: - case URX_STRING_LEN: - case URX_NOP: - case URX_START_CAPTURE: - case URX_END_CAPTURE: - case URX_BACKSLASH_B: - case URX_BACKSLASH_BU: - case URX_BACKSLASH_G: - case URX_BACKSLASH_Z: - case URX_CARET: - case URX_DOLLAR: - case URX_DOLLAR_M: - case URX_DOLLAR_D: - case URX_DOLLAR_MD: - case URX_RELOC_OPRND: - case URX_STO_INP_LOC: - case URX_CARET_M: - case URX_CARET_M_UNIX: - - case URX_STO_SP: // Setup for atomic or possessive blocks. Doesn't change what can match. - case URX_LD_SP: - - case URX_LB_END: - case URX_LB_CONT: - case URX_LBN_CONT: - case URX_LBN_END: - break; - - - // Ops that increase that cause an unbounded increase in the length - // of a matched string, or that increase it a hard to characterize way. - // Call the max length unbounded, and stop further checking. - case URX_BACKREF: // BackRef. Must assume that it might be a zero length match - case URX_BACKREF_I: - case URX_BACKSLASH_X: // Grapheme Cluster. Minimum is 1, max unbounded. - currentLen = INT32_MAX; - break; - - - // Ops that match a max of one character (possibly two 16 bit code units.) - // - case URX_STATIC_SETREF: - case URX_STAT_SETREF_N: - case URX_SETREF: - case URX_BACKSLASH_D: - case URX_BACKSLASH_H: - case URX_BACKSLASH_R: - case URX_BACKSLASH_V: - case URX_ONECHAR_I: - case URX_DOTANY_ALL: - case URX_DOTANY: - case URX_DOTANY_UNIX: - currentLen = safeIncrement(currentLen, 2); - break; - - // Single literal character. Increase current max length by one or two, - // depending on whether the char is in the supplementary range. - case URX_ONECHAR: - currentLen = safeIncrement(currentLen, 1); - if (URX_VAL(op) > 0x10000) { - currentLen = safeIncrement(currentLen, 1); - } - break; - - // Jumps. - // - case URX_JMP: - case URX_JMPX: - case URX_JMP_SAV: - case URX_JMP_SAV_X: - { - int32_t jmpDest = URX_VAL(op); - if (jmpDest < loc) { - // Loop of some kind. Max match length is unbounded. - currentLen = INT32_MAX; - } else { - // Forward jump. Propagate the current min length to the target loc of the jump. - if (forwardedLength.elementAti(jmpDest) < currentLen) { - forwardedLength.setElementAt(currentLen, jmpDest); - } - currentLen = 0; - } - } - break; - - case URX_BACKTRACK: - // back-tracks are kind of like a branch, except that the max length was - // propagated already, by the state save. - currentLen = forwardedLength.elementAti(loc+1); - break; - - - case URX_STATE_SAVE: - { - // State Save, for forward jumps, propagate the current minimum. - // of the state save. - // For backwards jumps, they create a loop, maximum - // match length is unbounded. - int32_t jmpDest = URX_VAL(op); - if (jmpDest > loc) { - if (currentLen > forwardedLength.elementAti(jmpDest)) { - forwardedLength.setElementAt(currentLen, jmpDest); - } - } else { - currentLen = INT32_MAX; - } - } - break; - - - - - case URX_STRING: - { - loc++; - int32_t stringLenOp = (int32_t)fRXPat->fCompiledPat->elementAti(loc); - currentLen = safeIncrement(currentLen, URX_VAL(stringLenOp)); - break; - } - - case URX_STRING_I: - // TODO: This code assumes that any user string that matches will be no longer - // than our compiled string, with case insensitive matching. - // Our compiled string has been case-folded already. - // - // Any matching user string will have no more code points than our - // compiled (folded) string. Folding may add code points, but - // not remove them. - // - // There is a potential problem if a supplemental code point - // case-folds to a BMP code point. In this case our compiled string - // could be shorter (in code units) than a matching user string. - // - // At this time (Unicode 6.1) there are no such characters, and this case - // is not being handled. A test, intltest regex/Bug9283, will fail if - // any problematic characters are added to Unicode. - // - // If this happens, we can make a set of the BMP chars that the - // troublesome supplementals fold to, scan our string, and bump the - // currentLen one extra for each that is found. - // - { - loc++; - int32_t stringLenOp = (int32_t)fRXPat->fCompiledPat->elementAti(loc); - currentLen = safeIncrement(currentLen, URX_VAL(stringLenOp)); - } - break; - - case URX_CTR_INIT: - case URX_CTR_INIT_NG: - // For Loops, recursively call this function on the pattern for the loop body, - // then multiply the result by the maximum loop count. - { - int32_t loopEndLoc = URX_VAL(fRXPat->fCompiledPat->elementAti(loc+1)); - if (loopEndLoc == loc+4) { - // Loop has an empty body. No affect on max match length. - // Continue processing with code after the loop end. - loc = loopEndLoc; - break; - } - - int32_t maxLoopCount = static_cast(fRXPat->fCompiledPat->elementAti(loc+3)); - if (maxLoopCount == -1) { - // Unbounded Loop. No upper bound on match length. - currentLen = INT32_MAX; - break; - } - - U_ASSERT(loopEndLoc >= loc+4); - int64_t blockLen = maxMatchLength(loc+4, loopEndLoc-1); // Recursive call. - int64_t updatedLen = (int64_t)currentLen + blockLen * maxLoopCount; - if (updatedLen >= INT32_MAX) { - currentLen = INT32_MAX; - break; - } - currentLen = (int32_t)updatedLen; - loc = loopEndLoc; - break; - } - - case URX_CTR_LOOP: - case URX_CTR_LOOP_NG: - // These opcodes will be skipped over by code for URX_CTR_INIT. - // We shouldn't encounter them here. - UPRV_UNREACHABLE_EXIT; - - case URX_LOOP_SR_I: - case URX_LOOP_DOT_I: - case URX_LOOP_C: - // For anything to do with loops, make the match length unbounded. - currentLen = INT32_MAX; - break; - - - - case URX_LA_START: - case URX_LA_END: - // Look-ahead. Just ignore, treat the look-ahead block as if - // it were normal pattern. Gives a too-long match length, - // but good enough for now. - break; - - // End of look-ahead ops should always be consumed by the processing at - // the URX_LA_START op. - // UPRV_UNREACHABLE_EXIT; - - case URX_LB_START: - { - // Look-behind. Scan forward until the matching look-around end, - // without processing the look-behind block. - int32_t dataLoc = URX_VAL(op); - for (loc = loc + 1; loc <= end; ++loc) { - op = (int32_t)fRXPat->fCompiledPat->elementAti(loc); - int32_t opType = URX_TYPE(op); - if ((opType == URX_LA_END || opType == URX_LBN_END) && (URX_VAL(op) == dataLoc)) { - break; - } - } - U_ASSERT(loc <= end); - } - break; - - default: - UPRV_UNREACHABLE_EXIT; - } - - - if (currentLen == INT32_MAX) { - // The maximum length is unbounded. - // Stop further processing of the pattern. - break; - } - - } - return currentLen; - -} - - -//------------------------------------------------------------------------------ -// -// stripNOPs Remove any NOP operations from the compiled pattern code. -// Extra NOPs are inserted for some constructs during the initial -// code generation to provide locations that may be patched later. -// Many end up unneeded, and are removed by this function. -// -// In order to minimize the number of passes through the pattern, -// back-reference fixup is also performed here (adjusting -// back-reference operands to point to the correct frame offsets). -// -//------------------------------------------------------------------------------ -void RegexCompile::stripNOPs() { - - if (U_FAILURE(*fStatus)) { - return; - } - - int32_t end = fRXPat->fCompiledPat->size(); - UVector32 deltas(end, *fStatus); - - // Make a first pass over the code, computing the amount that things - // will be offset at each location in the original code. - int32_t loc; - int32_t d = 0; - for (loc=0; locfCompiledPat->elementAti(loc); - if (URX_TYPE(op) == URX_NOP) { - d++; - } - } - - UnicodeString caseStringBuffer; - - // Make a second pass over the code, removing the NOPs by moving following - // code up, and patching operands that refer to code locations that - // are being moved. The array of offsets from the first step is used - // to compute the new operand values. - int32_t src; - int32_t dst = 0; - for (src=0; srcfCompiledPat->elementAti(src); - int32_t opType = URX_TYPE(op); - switch (opType) { - case URX_NOP: - break; - - case URX_STATE_SAVE: - case URX_JMP: - case URX_CTR_LOOP: - case URX_CTR_LOOP_NG: - case URX_RELOC_OPRND: - case URX_JMPX: - case URX_JMP_SAV: - case URX_JMP_SAV_X: - // These are instructions with operands that refer to code locations. - { - int32_t operandAddress = URX_VAL(op); - U_ASSERT(operandAddress>=0 && operandAddressfCompiledPat->setElementAt(op, dst); - dst++; - break; - } - - case URX_BACKREF: - case URX_BACKREF_I: - { - int32_t where = URX_VAL(op); - if (where > fRXPat->fGroupMap->size()) { - error(U_REGEX_INVALID_BACK_REF); - break; - } - where = fRXPat->fGroupMap->elementAti(where-1); - op = buildOp(opType, where); - fRXPat->fCompiledPat->setElementAt(op, dst); - dst++; - - fRXPat->fNeedsAltInput = true; - break; - } - case URX_RESERVED_OP: - case URX_RESERVED_OP_N: - case URX_BACKTRACK: - case URX_END: - case URX_ONECHAR: - case URX_STRING: - case URX_STRING_LEN: - case URX_START_CAPTURE: - case URX_END_CAPTURE: - case URX_STATIC_SETREF: - case URX_STAT_SETREF_N: - case URX_SETREF: - case URX_DOTANY: - case URX_FAIL: - case URX_BACKSLASH_B: - case URX_BACKSLASH_BU: - case URX_BACKSLASH_G: - case URX_BACKSLASH_X: - case URX_BACKSLASH_Z: - case URX_DOTANY_ALL: - case URX_BACKSLASH_D: - case URX_CARET: - case URX_DOLLAR: - case URX_CTR_INIT: - case URX_CTR_INIT_NG: - case URX_DOTANY_UNIX: - case URX_STO_SP: - case URX_LD_SP: - case URX_STO_INP_LOC: - case URX_LA_START: - case URX_LA_END: - case URX_ONECHAR_I: - case URX_STRING_I: - case URX_DOLLAR_M: - case URX_CARET_M: - case URX_CARET_M_UNIX: - case URX_LB_START: - case URX_LB_CONT: - case URX_LB_END: - case URX_LBN_CONT: - case URX_LBN_END: - case URX_LOOP_SR_I: - case URX_LOOP_DOT_I: - case URX_LOOP_C: - case URX_DOLLAR_D: - case URX_DOLLAR_MD: - case URX_BACKSLASH_H: - case URX_BACKSLASH_R: - case URX_BACKSLASH_V: - // These instructions are unaltered by the relocation. - fRXPat->fCompiledPat->setElementAt(op, dst); - dst++; - break; - - default: - // Some op is unaccounted for. - UPRV_UNREACHABLE_EXIT; - } - } - - fRXPat->fCompiledPat->setSize(dst); -} - - - - -//------------------------------------------------------------------------------ -// -// Error Report a rule parse error. -// Only report it if no previous error has been recorded. -// -//------------------------------------------------------------------------------ -void RegexCompile::error(UErrorCode e) { - if (U_SUCCESS(*fStatus) || e == U_MEMORY_ALLOCATION_ERROR) { - *fStatus = e; - // Hmm. fParseErr (UParseError) line & offset fields are int32_t in public - // API (see common/unicode/parseerr.h), while fLineNum and fCharNum are - // int64_t. If the values of the latter are out of range for the former, - // set them to the appropriate "field not supported" values. - if (fLineNum > 0x7FFFFFFF) { - fParseErr->line = 0; - fParseErr->offset = -1; - } else if (fCharNum > 0x7FFFFFFF) { - fParseErr->line = (int32_t)fLineNum; - fParseErr->offset = -1; - } else { - fParseErr->line = (int32_t)fLineNum; - fParseErr->offset = (int32_t)fCharNum; - } - - UErrorCode status = U_ZERO_ERROR; // throwaway status for extracting context - - // Fill in the context. - // Note: extractBetween() pins supplied indices to the string bounds. - uprv_memset(fParseErr->preContext, 0, sizeof(fParseErr->preContext)); - uprv_memset(fParseErr->postContext, 0, sizeof(fParseErr->postContext)); - utext_extract(fRXPat->fPattern, fScanIndex-U_PARSE_CONTEXT_LEN+1, fScanIndex, fParseErr->preContext, U_PARSE_CONTEXT_LEN, &status); - utext_extract(fRXPat->fPattern, fScanIndex, fScanIndex+U_PARSE_CONTEXT_LEN-1, fParseErr->postContext, U_PARSE_CONTEXT_LEN, &status); - } -} - - -// -// Assorted Unicode character constants. -// Numeric because there is no portable way to enter them as literals. -// (Think EBCDIC). -// -static const UChar chCR = 0x0d; // New lines, for terminating comments. -static const UChar chLF = 0x0a; // Line Feed -static const UChar chPound = 0x23; // '#', introduces a comment. -static const UChar chDigit0 = 0x30; // '0' -static const UChar chDigit7 = 0x37; // '9' -static const UChar chColon = 0x3A; // ':' -static const UChar chE = 0x45; // 'E' -static const UChar chQ = 0x51; // 'Q' -//static const UChar chN = 0x4E; // 'N' -static const UChar chP = 0x50; // 'P' -static const UChar chBackSlash = 0x5c; // '\' introduces a char escape -//static const UChar chLBracket = 0x5b; // '[' -static const UChar chRBracket = 0x5d; // ']' -static const UChar chUp = 0x5e; // '^' -static const UChar chLowerP = 0x70; -static const UChar chLBrace = 0x7b; // '{' -static const UChar chRBrace = 0x7d; // '}' -static const UChar chNEL = 0x85; // NEL newline variant -static const UChar chLS = 0x2028; // Unicode Line Separator - - -//------------------------------------------------------------------------------ -// -// nextCharLL Low Level Next Char from the regex pattern. -// Get a char from the string, keep track of input position -// for error reporting. -// -//------------------------------------------------------------------------------ -UChar32 RegexCompile::nextCharLL() { - UChar32 ch; - - if (fPeekChar != -1) { - ch = fPeekChar; - fPeekChar = -1; - return ch; - } - - // assume we're already in the right place - ch = UTEXT_NEXT32(fRXPat->fPattern); - if (ch == U_SENTINEL) { - return ch; - } - - if (ch == chCR || - ch == chNEL || - ch == chLS || - (ch == chLF && fLastChar != chCR)) { - // Character is starting a new line. Bump up the line number, and - // reset the column to 0. - fLineNum++; - fCharNum=0; - } - else { - // Character is not starting a new line. Except in the case of a - // LF following a CR, increment the column position. - if (ch != chLF) { - fCharNum++; - } - } - fLastChar = ch; - return ch; -} - -//------------------------------------------------------------------------------ -// -// peekCharLL Low Level Character Scanning, sneak a peek at the next -// character without actually getting it. -// -//------------------------------------------------------------------------------ -UChar32 RegexCompile::peekCharLL() { - if (fPeekChar == -1) { - fPeekChar = nextCharLL(); - } - return fPeekChar; -} - - -//------------------------------------------------------------------------------ -// -// nextChar for pattern scanning. At this level, we handle stripping -// out comments and processing some backslash character escapes. -// The rest of the pattern grammar is handled at the next level up. -// -//------------------------------------------------------------------------------ -void RegexCompile::nextChar(RegexPatternChar &c) { - tailRecursion: - fScanIndex = UTEXT_GETNATIVEINDEX(fRXPat->fPattern); - c.fChar = nextCharLL(); - c.fQuoted = false; - - if (fQuoteMode) { - c.fQuoted = true; - if ((c.fChar==chBackSlash && peekCharLL()==chE && ((fModeFlags & UREGEX_LITERAL) == 0)) || - c.fChar == (UChar32)-1) { - fQuoteMode = false; // Exit quote mode, - nextCharLL(); // discard the E - // nextChar(c); // recurse to get the real next char - goto tailRecursion; // Note: fuzz testing produced testcases that - // resulted in stack overflow here. - } - } - else if (fInBackslashQuote) { - // The current character immediately follows a '\' - // Don't check for any further escapes, just return it as-is. - // Don't set c.fQuoted, because that would prevent the state machine from - // dispatching on the character. - fInBackslashQuote = false; - } - else - { - // We are not in a \Q quoted region \E of the source. - // - if (fModeFlags & UREGEX_COMMENTS) { - // - // We are in free-spacing and comments mode. - // Scan through any white space and comments, until we - // reach a significant character or the end of input. - for (;;) { - if (c.fChar == (UChar32)-1) { - break; // End of Input - } - if (c.fChar == chPound && fEOLComments == true) { - // Start of a comment. Consume the rest of it, until EOF or a new line - for (;;) { - c.fChar = nextCharLL(); - if (c.fChar == (UChar32)-1 || // EOF - c.fChar == chCR || - c.fChar == chLF || - c.fChar == chNEL || - c.fChar == chLS) { - break; - } - } - } - // TODO: check what Java & Perl do with non-ASCII white spaces. Ticket 6061. - if (PatternProps::isWhiteSpace(c.fChar) == false) { - break; - } - c.fChar = nextCharLL(); - } - } - - // - // check for backslash escaped characters. - // - if (c.fChar == chBackSlash) { - int64_t pos = UTEXT_GETNATIVEINDEX(fRXPat->fPattern); - if (RegexStaticSets::gStaticSets->fUnescapeCharSet.contains(peekCharLL())) { - // - // A '\' sequence that is handled by ICU's standard unescapeAt function. - // Includes \uxxxx, \n, \r, many others. - // Return the single equivalent character. - // - nextCharLL(); // get & discard the peeked char. - c.fQuoted = true; - - if (UTEXT_FULL_TEXT_IN_CHUNK(fRXPat->fPattern, fPatternLength)) { - int32_t endIndex = (int32_t)pos; - c.fChar = u_unescapeAt(uregex_ucstr_unescape_charAt, &endIndex, (int32_t)fPatternLength, (void *)fRXPat->fPattern->chunkContents); - - if (endIndex == pos) { - error(U_REGEX_BAD_ESCAPE_SEQUENCE); - } - fCharNum += endIndex - pos; - UTEXT_SETNATIVEINDEX(fRXPat->fPattern, endIndex); - } else { - int32_t offset = 0; - struct URegexUTextUnescapeCharContext context = U_REGEX_UTEXT_UNESCAPE_CONTEXT(fRXPat->fPattern); - - UTEXT_SETNATIVEINDEX(fRXPat->fPattern, pos); - c.fChar = u_unescapeAt(uregex_utext_unescape_charAt, &offset, INT32_MAX, &context); - - if (offset == 0) { - error(U_REGEX_BAD_ESCAPE_SEQUENCE); - } else if (context.lastOffset == offset) { - UTEXT_PREVIOUS32(fRXPat->fPattern); - } else if (context.lastOffset != offset-1) { - utext_moveIndex32(fRXPat->fPattern, offset - context.lastOffset - 1); - } - fCharNum += offset; - } - } - else if (peekCharLL() == chDigit0) { - // Octal Escape, using Java Regexp Conventions - // which are \0 followed by 1-3 octal digits. - // Different from ICU Unescape handling of Octal, which does not - // require the leading 0. - // Java also has the convention of only consuming 2 octal digits if - // the three digit number would be > 0xff - // - c.fChar = 0; - nextCharLL(); // Consume the initial 0. - int index; - for (index=0; index<3; index++) { - int32_t ch = peekCharLL(); - if (chchDigit7) { - if (index==0) { - // \0 is not followed by any octal digits. - error(U_REGEX_BAD_ESCAPE_SEQUENCE); - } - break; - } - c.fChar <<= 3; - c.fChar += ch&7; - if (c.fChar <= 255) { - nextCharLL(); - } else { - // The last digit made the number too big. Forget we saw it. - c.fChar >>= 3; - } - } - c.fQuoted = true; - } - else if (peekCharLL() == chQ) { - // "\Q" enter quote mode, which will continue until "\E" - fQuoteMode = true; - nextCharLL(); // discard the 'Q'. - // nextChar(c); // recurse to get the real next char. - goto tailRecursion; // Note: fuzz testing produced test cases that - // resulted in stack overflow here. - } - else - { - // We are in a '\' escape that will be handled by the state table scanner. - // Just return the backslash, but remember that the following char is to - // be taken literally. - fInBackslashQuote = true; - } - } - } - - // re-enable # to end-of-line comments, in case they were disabled. - // They are disabled by the parser upon seeing '(?', but this lasts for - // the fetching of the next character only. - fEOLComments = true; - - // putc(c.fChar, stdout); -} - - - -//------------------------------------------------------------------------------ -// -// scanNamedChar -// Get a UChar32 from a \N{UNICODE CHARACTER NAME} in the pattern. -// -// The scan position will be at the 'N'. On return -// the scan position should be just after the '}' -// -// Return the UChar32 -// -//------------------------------------------------------------------------------ -UChar32 RegexCompile::scanNamedChar() { - if (U_FAILURE(*fStatus)) { - return 0; - } - - nextChar(fC); - if (fC.fChar != chLBrace) { - error(U_REGEX_PROPERTY_SYNTAX); - return 0; - } - - UnicodeString charName; - for (;;) { - nextChar(fC); - if (fC.fChar == chRBrace) { - break; - } - if (fC.fChar == -1) { - error(U_REGEX_PROPERTY_SYNTAX); - return 0; - } - charName.append(fC.fChar); - } - - char name[100]; - if (!uprv_isInvariantUString(charName.getBuffer(), charName.length()) || - (uint32_t)charName.length()>=sizeof(name)) { - // All Unicode character names have only invariant characters. - // The API to get a character, given a name, accepts only char *, forcing us to convert, - // which requires this error check - error(U_REGEX_PROPERTY_SYNTAX); - return 0; - } - charName.extract(0, charName.length(), name, sizeof(name), US_INV); - - UChar32 theChar = u_charFromName(U_UNICODE_CHAR_NAME, name, fStatus); - if (U_FAILURE(*fStatus)) { - error(U_REGEX_PROPERTY_SYNTAX); - } - - nextChar(fC); // Continue overall regex pattern processing with char after the '}' - return theChar; -} - -//------------------------------------------------------------------------------ -// -// scanProp Construct a UnicodeSet from the text at the current scan -// position, which will be of the form \p{whaterver} -// -// The scan position will be at the 'p' or 'P'. On return -// the scan position should be just after the '}' -// -// Return a UnicodeSet, constructed from the \P pattern, -// or NULL if the pattern is invalid. -// -//------------------------------------------------------------------------------ -UnicodeSet *RegexCompile::scanProp() { - UnicodeSet *uset = NULL; - - if (U_FAILURE(*fStatus)) { - return NULL; - } - (void)chLowerP; // Suppress compiler unused variable warning. - U_ASSERT(fC.fChar == chLowerP || fC.fChar == chP); - UBool negated = (fC.fChar == chP); - - UnicodeString propertyName; - nextChar(fC); - if (fC.fChar != chLBrace) { - error(U_REGEX_PROPERTY_SYNTAX); - return NULL; - } - for (;;) { - nextChar(fC); - if (fC.fChar == chRBrace) { - break; - } - if (fC.fChar == -1) { - // Hit the end of the input string without finding the closing '}' - error(U_REGEX_PROPERTY_SYNTAX); - return NULL; - } - propertyName.append(fC.fChar); - } - uset = createSetForProperty(propertyName, negated); - nextChar(fC); // Move input scan to position following the closing '}' - return uset; -} - -//------------------------------------------------------------------------------ -// -// scanPosixProp Construct a UnicodeSet from the text at the current scan -// position, which is expected be of the form [:property expression:] -// -// The scan position will be at the opening ':'. On return -// the scan position must be on the closing ']' -// -// Return a UnicodeSet constructed from the pattern, -// or NULL if this is not a valid POSIX-style set expression. -// If not a property expression, restore the initial scan position -// (to the opening ':') -// -// Note: the opening '[:' is not sufficient to guarantee that -// this is a [:property:] expression. -// [:'+=,] is a perfectly good ordinary set expression that -// happens to include ':' as one of its characters. -// -//------------------------------------------------------------------------------ -UnicodeSet *RegexCompile::scanPosixProp() { - UnicodeSet *uset = NULL; - - if (U_FAILURE(*fStatus)) { - return NULL; - } - - U_ASSERT(fC.fChar == chColon); - - // Save the scanner state. - // TODO: move this into the scanner, with the state encapsulated in some way. Ticket 6062 - int64_t savedScanIndex = fScanIndex; - int64_t savedNextIndex = UTEXT_GETNATIVEINDEX(fRXPat->fPattern); - UBool savedQuoteMode = fQuoteMode; - UBool savedInBackslashQuote = fInBackslashQuote; - UBool savedEOLComments = fEOLComments; - int64_t savedLineNum = fLineNum; - int64_t savedCharNum = fCharNum; - UChar32 savedLastChar = fLastChar; - UChar32 savedPeekChar = fPeekChar; - RegexPatternChar savedfC = fC; - - // Scan for a closing ]. A little tricky because there are some perverse - // edge cases possible. "[:abc\Qdef:] \E]" is a valid non-property expression, - // ending on the second closing ]. - - UnicodeString propName; - UBool negated = false; - - // Check for and consume the '^' in a negated POSIX property, e.g. [:^Letter:] - nextChar(fC); - if (fC.fChar == chUp) { - negated = true; - nextChar(fC); - } - - // Scan for the closing ":]", collecting the property name along the way. - UBool sawPropSetTerminator = false; - for (;;) { - propName.append(fC.fChar); - nextChar(fC); - if (fC.fQuoted || fC.fChar == -1) { - // Escaped characters or end of input - either says this isn't a [:Property:] - break; - } - if (fC.fChar == chColon) { - nextChar(fC); - if (fC.fChar == chRBracket) { - sawPropSetTerminator = true; - } - break; - } - } - - if (sawPropSetTerminator) { - uset = createSetForProperty(propName, negated); - } - else - { - // No closing ":]". - // Restore the original scan position. - // The main scanner will retry the input as a normal set expression, - // not a [:Property:] expression. - fScanIndex = savedScanIndex; - fQuoteMode = savedQuoteMode; - fInBackslashQuote = savedInBackslashQuote; - fEOLComments = savedEOLComments; - fLineNum = savedLineNum; - fCharNum = savedCharNum; - fLastChar = savedLastChar; - fPeekChar = savedPeekChar; - fC = savedfC; - UTEXT_SETNATIVEINDEX(fRXPat->fPattern, savedNextIndex); - } - return uset; -} - -static inline void addIdentifierIgnorable(UnicodeSet *set, UErrorCode& ec) { - set->add(0, 8).add(0x0e, 0x1b).add(0x7f, 0x9f); - addCategory(set, U_GC_CF_MASK, ec); -} - -// -// Create a Unicode Set from a Unicode Property expression. -// This is common code underlying both \p{...} and [:...:] expressions. -// Includes trying the Java "properties" that aren't supported as -// normal ICU UnicodeSet properties -// -UnicodeSet *RegexCompile::createSetForProperty(const UnicodeString &propName, UBool negated) { - - if (U_FAILURE(*fStatus)) { - return nullptr; - } - LocalPointer set; - UErrorCode status = U_ZERO_ERROR; - - do { // non-loop, exists to allow breaks from the block. - // - // First try the property as we received it - // - UnicodeString setExpr; - uint32_t usetFlags = 0; - setExpr.append(u"[\\p{", -1); - setExpr.append(propName); - setExpr.append(u"}]", -1); - if (fModeFlags & UREGEX_CASE_INSENSITIVE) { - usetFlags |= USET_CASE_INSENSITIVE; - } - set.adoptInsteadAndCheckErrorCode(new UnicodeSet(setExpr, usetFlags, NULL, status), status); - if (U_SUCCESS(status) || status == U_MEMORY_ALLOCATION_ERROR) { - break; - } - - // - // The incoming property wasn't directly recognized by ICU. - - // Check [:word:] and [:all:]. These are not recognized as a properties by ICU UnicodeSet. - // Java accepts 'word' with mixed case. - // Java accepts 'all' only in all lower case. - - status = U_ZERO_ERROR; - if (propName.caseCompare(u"word", -1, 0) == 0) { - set.adoptInsteadAndCheckErrorCode( - RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET].cloneAsThawed(), status); - break; - } - if (propName.compare(u"all", -1) == 0) { - set.adoptInsteadAndCheckErrorCode(new UnicodeSet(0, 0x10ffff), status); - break; - } - - - // Do Java InBlock expressions - // - UnicodeString mPropName = propName; - if (mPropName.startsWith(u"In", 2) && mPropName.length() >= 3) { - status = U_ZERO_ERROR; - set.adoptInsteadAndCheckErrorCode(new UnicodeSet(), status); - if (U_FAILURE(status)) { - break; - } - UnicodeString blockName(mPropName, 2); // Property with the leading "In" removed. - set->applyPropertyAlias(UnicodeString(u"Block"), blockName, status); - break; - } - - // Check for the Java form "IsBooleanPropertyValue", which we will recast - // as "BooleanPropertyValue". The property value can be either a - // a General Category or a Script Name. - - if (propName.startsWith(u"Is", 2) && propName.length()>=3) { - mPropName.remove(0, 2); // Strip the "Is" - if (mPropName.indexOf(u'=') >= 0) { - // Reject any "Is..." property expression containing an '=', that is, - // any non-binary property expression. - status = U_REGEX_PROPERTY_SYNTAX; - break; - } - - if (mPropName.caseCompare(u"assigned", -1, 0) == 0) { - mPropName.setTo(u"unassigned", -1); - negated = !negated; - } else if (mPropName.caseCompare(u"TitleCase", -1, 0) == 0) { - mPropName.setTo(u"Titlecase_Letter", -1); - } - - mPropName.insert(0, u"[\\p{", -1); - mPropName.append(u"}]", -1); - set.adoptInsteadAndCheckErrorCode(new UnicodeSet(mPropName, *fStatus), status); - - if (U_SUCCESS(status) && !set->isEmpty() && (usetFlags & USET_CASE_INSENSITIVE)) { - set->closeOver(USET_CASE_INSENSITIVE); - } - break; - - } - - if (propName.startsWith(u"java", -1)) { - status = U_ZERO_ERROR; - set.adoptInsteadAndCheckErrorCode(new UnicodeSet(), status); - if (U_FAILURE(status)) { - break; - } - // - // Try the various Java specific properties. - // These all begin with "java" - // - if (propName.compare(u"javaDefined", -1) == 0) { - addCategory(set.getAlias(), U_GC_CN_MASK, status); - set->complement(); - } - else if (propName.compare(u"javaDigit", -1) == 0) { - addCategory(set.getAlias(), U_GC_ND_MASK, status); - } - else if (propName.compare(u"javaIdentifierIgnorable", -1) == 0) { - addIdentifierIgnorable(set.getAlias(), status); - } - else if (propName.compare(u"javaISOControl", -1) == 0) { - set->add(0, 0x1F).add(0x7F, 0x9F); - } - else if (propName.compare(u"javaJavaIdentifierPart", -1) == 0) { - addCategory(set.getAlias(), U_GC_L_MASK, status); - addCategory(set.getAlias(), U_GC_SC_MASK, status); - addCategory(set.getAlias(), U_GC_PC_MASK, status); - addCategory(set.getAlias(), U_GC_ND_MASK, status); - addCategory(set.getAlias(), U_GC_NL_MASK, status); - addCategory(set.getAlias(), U_GC_MC_MASK, status); - addCategory(set.getAlias(), U_GC_MN_MASK, status); - addIdentifierIgnorable(set.getAlias(), status); - } - else if (propName.compare(u"javaJavaIdentifierStart", -1) == 0) { - addCategory(set.getAlias(), U_GC_L_MASK, status); - addCategory(set.getAlias(), U_GC_NL_MASK, status); - addCategory(set.getAlias(), U_GC_SC_MASK, status); - addCategory(set.getAlias(), U_GC_PC_MASK, status); - } - else if (propName.compare(u"javaLetter", -1) == 0) { - addCategory(set.getAlias(), U_GC_L_MASK, status); - } - else if (propName.compare(u"javaLetterOrDigit", -1) == 0) { - addCategory(set.getAlias(), U_GC_L_MASK, status); - addCategory(set.getAlias(), U_GC_ND_MASK, status); - } - else if (propName.compare(u"javaLowerCase", -1) == 0) { - addCategory(set.getAlias(), U_GC_LL_MASK, status); - } - else if (propName.compare(u"javaMirrored", -1) == 0) { - set->applyIntPropertyValue(UCHAR_BIDI_MIRRORED, 1, status); - } - else if (propName.compare(u"javaSpaceChar", -1) == 0) { - addCategory(set.getAlias(), U_GC_Z_MASK, status); - } - else if (propName.compare(u"javaSupplementaryCodePoint", -1) == 0) { - set->add(0x10000, UnicodeSet::MAX_VALUE); - } - else if (propName.compare(u"javaTitleCase", -1) == 0) { - addCategory(set.getAlias(), U_GC_LT_MASK, status); - } - else if (propName.compare(u"javaUnicodeIdentifierStart", -1) == 0) { - addCategory(set.getAlias(), U_GC_L_MASK, status); - addCategory(set.getAlias(), U_GC_NL_MASK, status); - } - else if (propName.compare(u"javaUnicodeIdentifierPart", -1) == 0) { - addCategory(set.getAlias(), U_GC_L_MASK, status); - addCategory(set.getAlias(), U_GC_PC_MASK, status); - addCategory(set.getAlias(), U_GC_ND_MASK, status); - addCategory(set.getAlias(), U_GC_NL_MASK, status); - addCategory(set.getAlias(), U_GC_MC_MASK, status); - addCategory(set.getAlias(), U_GC_MN_MASK, status); - addIdentifierIgnorable(set.getAlias(), status); - } - else if (propName.compare(u"javaUpperCase", -1) == 0) { - addCategory(set.getAlias(), U_GC_LU_MASK, status); - } - else if (propName.compare(u"javaValidCodePoint", -1) == 0) { - set->add(0, UnicodeSet::MAX_VALUE); - } - else if (propName.compare(u"javaWhitespace", -1) == 0) { - addCategory(set.getAlias(), U_GC_Z_MASK, status); - set->removeAll(UnicodeSet().add(0xa0).add(0x2007).add(0x202f)); - set->add(9, 0x0d).add(0x1c, 0x1f); - } else { - status = U_REGEX_PROPERTY_SYNTAX; - } - - if (U_SUCCESS(status) && !set->isEmpty() && (usetFlags & USET_CASE_INSENSITIVE)) { - set->closeOver(USET_CASE_INSENSITIVE); - } - break; - } - - // Unrecognized property. ICU didn't like it as it was, and none of the Java compatibility - // extensions matched it. - status = U_REGEX_PROPERTY_SYNTAX; - } while (false); // End of do loop block. Code above breaks out of the block on success or hard failure. - - if (U_SUCCESS(status)) { - // ICU 70 adds emoji properties of strings, but as long as Java does not say how to - // deal with properties of strings and character classes with strings, we ignore them. - // Just in case something downstream might stumble over the strings, - // we remove them from the set. - // Note that when we support strings, the complement of a property (as with \P) - // should be implemented as .complement().removeAllStrings() (code point complement). - set->removeAllStrings(); - U_ASSERT(set.isValid()); - if (negated) { - set->complement(); - } - return set.orphan(); - } else { - if (status == U_ILLEGAL_ARGUMENT_ERROR) { - status = U_REGEX_PROPERTY_SYNTAX; - } - error(status); - return nullptr; - } -} - - -// -// SetEval Part of the evaluation of [set expressions]. -// Perform any pending (stacked) operations with precedence -// equal or greater to that of the next operator encountered -// in the expression. -// -void RegexCompile::setEval(int32_t nextOp) { - UnicodeSet *rightOperand = NULL; - UnicodeSet *leftOperand = NULL; - for (;;) { - U_ASSERT(fSetOpStack.empty()==false); - int32_t pendingSetOperation = fSetOpStack.peeki(); - if ((pendingSetOperation&0xffff0000) < (nextOp&0xffff0000)) { - break; - } - fSetOpStack.popi(); - U_ASSERT(fSetStack.empty() == false); - rightOperand = (UnicodeSet *)fSetStack.peek(); - // ICU 70 adds emoji properties of strings, but createSetForProperty() removes all strings - // (see comments there). - // We also do not yet support string literals in character classes, - // so there should not be any strings. - // Note that when we support strings, the complement of a set (as with ^ or \P) - // should be implemented as .complement().removeAllStrings() (code point complement). - U_ASSERT(!rightOperand->hasStrings()); - switch (pendingSetOperation) { - case setNegation: - rightOperand->complement(); - break; - case setCaseClose: - // TODO: need a simple close function. Ticket 6065 - rightOperand->closeOver(USET_CASE_INSENSITIVE); - rightOperand->removeAllStrings(); - break; - case setDifference1: - case setDifference2: - fSetStack.pop(); - leftOperand = (UnicodeSet *)fSetStack.peek(); - leftOperand->removeAll(*rightOperand); - delete rightOperand; - break; - case setIntersection1: - case setIntersection2: - fSetStack.pop(); - leftOperand = (UnicodeSet *)fSetStack.peek(); - leftOperand->retainAll(*rightOperand); - delete rightOperand; - break; - case setUnion: - fSetStack.pop(); - leftOperand = (UnicodeSet *)fSetStack.peek(); - leftOperand->addAll(*rightOperand); - delete rightOperand; - break; - default: - UPRV_UNREACHABLE_EXIT; - } - } - } - -void RegexCompile::setPushOp(int32_t op) { - setEval(op); - fSetOpStack.push(op, *fStatus); - LocalPointer lpSet(new UnicodeSet(), *fStatus); - fSetStack.push(lpSet.orphan(), *fStatus); -} - -U_NAMESPACE_END -#endif // !UCONFIG_NO_REGULAR_EXPRESSIONS - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// file: regexcmp.cpp +// +// Copyright (C) 2002-2016 International Business Machines Corporation and others. +// All Rights Reserved. +// +// This file contains the ICU regular expression compiler, which is responsible +// for processing a regular expression pattern into the compiled form that +// is used by the match finding engine. +// + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_REGULAR_EXPRESSIONS + +#include "unicode/ustring.h" +#include "unicode/unistr.h" +#include "unicode/uniset.h" +#include "unicode/uchar.h" +#include "unicode/uchriter.h" +#include "unicode/parsepos.h" +#include "unicode/parseerr.h" +#include "unicode/regex.h" +#include "unicode/utf.h" +#include "unicode/utf16.h" +#include "patternprops.h" +#include "putilimp.h" +#include "cmemory.h" +#include "cstr.h" +#include "cstring.h" +#include "uvectr32.h" +#include "uvectr64.h" +#include "uassert.h" +#include "uinvchar.h" + +#include "regeximp.h" +#include "regexcst.h" // Contains state table for the regex pattern parser. + // generated by a Perl script. +#include "regexcmp.h" +#include "regexst.h" +#include "regextxt.h" + + + +U_NAMESPACE_BEGIN + + +//------------------------------------------------------------------------------ +// +// Constructor. +// +//------------------------------------------------------------------------------ +RegexCompile::RegexCompile(RegexPattern *rxp, UErrorCode &status) : + fParenStack(status), fSetStack(uprv_deleteUObject, nullptr, status), fSetOpStack(status) +{ + // Lazy init of all shared global sets (needed for init()'s empty text) + RegexStaticSets::initGlobals(&status); + + fStatus = &status; + + fRXPat = rxp; + fScanIndex = 0; + fLastChar = -1; + fPeekChar = -1; + fLineNum = 1; + fCharNum = 0; + fQuoteMode = false; + fInBackslashQuote = false; + fModeFlags = fRXPat->fFlags | 0x80000000; + fEOLComments = true; + + fMatchOpenParen = -1; + fMatchCloseParen = -1; + fCaptureName = nullptr; + fLastSetLiteral = U_SENTINEL; + + if (U_SUCCESS(status) && U_FAILURE(rxp->fDeferredStatus)) { + status = rxp->fDeferredStatus; + } +} + +static const char16_t chAmp = 0x26; // '&' +static const char16_t chDash = 0x2d; // '-' + + +//------------------------------------------------------------------------------ +// +// Destructor +// +//------------------------------------------------------------------------------ +RegexCompile::~RegexCompile() { + delete fCaptureName; // Normally will be nullptr, but can exist if pattern + // compilation stops with a syntax error. +} + +static inline void addCategory(UnicodeSet *set, int32_t value, UErrorCode& ec) { + set->addAll(UnicodeSet().applyIntPropertyValue(UCHAR_GENERAL_CATEGORY_MASK, value, ec)); +} + +//------------------------------------------------------------------------------ +// +// Compile regex pattern. The state machine for rexexp pattern parsing is here. +// The state tables are hand-written in the file regexcst.txt, +// and converted to the form used here by a perl +// script regexcst.pl +// +//------------------------------------------------------------------------------ +void RegexCompile::compile( + const UnicodeString &pat, // Source pat to be compiled. + UParseError &pp, // Error position info + UErrorCode &e) // Error Code +{ + fRXPat->fPatternString = new UnicodeString(pat); + UText patternText = UTEXT_INITIALIZER; + utext_openConstUnicodeString(&patternText, fRXPat->fPatternString, &e); + + if (U_SUCCESS(e)) { + compile(&patternText, pp, e); + utext_close(&patternText); + } +} + +// +// compile, UText mode +// All the work is actually done here. +// +void RegexCompile::compile( + UText *pat, // Source pat to be compiled. + UParseError &pp, // Error position info + UErrorCode &e) // Error Code +{ + fStatus = &e; + fParseErr = &pp; + fStackPtr = 0; + fStack[fStackPtr] = 0; + + if (U_FAILURE(*fStatus)) { + return; + } + + // There should be no pattern stuff in the RegexPattern object. They can not be reused. + U_ASSERT(fRXPat->fPattern == nullptr || utext_nativeLength(fRXPat->fPattern) == 0); + + // Prepare the RegexPattern object to receive the compiled pattern. + fRXPat->fPattern = utext_clone(fRXPat->fPattern, pat, false, true, fStatus); + if (U_FAILURE(*fStatus)) { + return; + } + + // Initialize the pattern scanning state machine + fPatternLength = utext_nativeLength(pat); + uint16_t state = 1; + const RegexTableEl *tableEl; + + // UREGEX_LITERAL force entire pattern to be treated as a literal string. + if (fModeFlags & UREGEX_LITERAL) { + fQuoteMode = true; + } + + nextChar(fC); // Fetch the first char from the pattern string. + + // + // Main loop for the regex pattern parsing state machine. + // Runs once per state transition. + // Each time through optionally performs, depending on the state table, + // - an advance to the the next pattern char + // - an action to be performed. + // - pushing or popping a state to/from the local state return stack. + // file regexcst.txt is the source for the state table. The logic behind + // recongizing the pattern syntax is there, not here. + // + for (;;) { + // Bail out if anything has gone wrong. + // Regex pattern parsing stops on the first error encountered. + if (U_FAILURE(*fStatus)) { + break; + } + + U_ASSERT(state != 0); + + // Find the state table element that matches the input char from the pattern, or the + // class of the input character. Start with the first table row for this + // state, then linearly scan forward until we find a row that matches the + // character. The last row for each state always matches all characters, so + // the search will stop there, if not before. + // + tableEl = &gRuleParseStateTable[state]; + REGEX_SCAN_DEBUG_PRINTF(("char, line, col = (\'%c\', %d, %d) state=%s ", + fC.fChar, fLineNum, fCharNum, RegexStateNames[state])); + + for (;;) { // loop through table rows belonging to this state, looking for one + // that matches the current input char. + REGEX_SCAN_DEBUG_PRINTF((".")); + if (tableEl->fCharClass < 127 && fC.fQuoted == false && tableEl->fCharClass == fC.fChar) { + // Table row specified an individual character, not a set, and + // the input character is not quoted, and + // the input character matched it. + break; + } + if (tableEl->fCharClass == 255) { + // Table row specified default, match anything character class. + break; + } + if (tableEl->fCharClass == 254 && fC.fQuoted) { + // Table row specified "quoted" and the char was quoted. + break; + } + if (tableEl->fCharClass == 253 && fC.fChar == (UChar32)-1) { + // Table row specified eof and we hit eof on the input. + break; + } + + if (tableEl->fCharClass >= 128 && tableEl->fCharClass < 240 && // Table specs a char class && + fC.fQuoted == false && // char is not escaped && + fC.fChar != (UChar32)-1) { // char is not EOF + U_ASSERT(tableEl->fCharClass <= 137); + if (RegexStaticSets::gStaticSets->fRuleSets[tableEl->fCharClass-128].contains(fC.fChar)) { + // Table row specified a character class, or set of characters, + // and the current char matches it. + break; + } + } + + // No match on this row, advance to the next row for this state, + tableEl++; + } + REGEX_SCAN_DEBUG_PRINTF(("\n")); + + // + // We've found the row of the state table that matches the current input + // character from the rules string. + // Perform any action specified by this row in the state table. + if (doParseActions(tableEl->fAction) == false) { + // Break out of the state machine loop if the + // the action signalled some kind of error, or + // the action was to exit, occurs on normal end-of-rules-input. + break; + } + + if (tableEl->fPushState != 0) { + fStackPtr++; + if (fStackPtr >= kStackSize) { + error(U_REGEX_INTERNAL_ERROR); + REGEX_SCAN_DEBUG_PRINTF(("RegexCompile::parse() - state stack overflow.\n")); + fStackPtr--; + } + fStack[fStackPtr] = tableEl->fPushState; + } + + // + // NextChar. This is where characters are actually fetched from the pattern. + // Happens under control of the 'n' tag in the state table. + // + if (tableEl->fNextChar) { + nextChar(fC); + } + + // Get the next state from the table entry, or from the + // state stack if the next state was specified as "pop". + if (tableEl->fNextState != 255) { + state = tableEl->fNextState; + } else { + state = fStack[fStackPtr]; + fStackPtr--; + if (fStackPtr < 0) { + // state stack underflow + // This will occur if the user pattern has mis-matched parentheses, + // with extra close parens. + // + fStackPtr++; + error(U_REGEX_MISMATCHED_PAREN); + } + } + + } + + if (U_FAILURE(*fStatus)) { + // Bail out if the pattern had errors. + return; + } + + // + // The pattern has now been read and processed, and the compiled code generated. + // + + // + // The pattern's fFrameSize so far has accumulated the requirements for + // storage for capture parentheses, counters, etc. that are encountered + // in the pattern. Add space for the two variables that are always + // present in the saved state: the input string position (int64_t) and + // the position in the compiled pattern. + // + allocateStackData(RESTACKFRAME_HDRCOUNT); + + // + // Optimization pass 1: NOPs, back-references, and case-folding + // + stripNOPs(); + + // + // Get bounds for the minimum and maximum length of a string that this + // pattern can match. Used to avoid looking for matches in strings that + // are too short. + // + fRXPat->fMinMatchLen = minMatchLength(3, fRXPat->fCompiledPat->size()-1); + + // + // Optimization pass 2: match start type + // + matchStartType(); + + // + // Set up fast latin-1 range sets + // + int32_t numSets = fRXPat->fSets->size(); + fRXPat->fSets8 = new Regex8BitSet[numSets]; + // Null pointer check. + if (fRXPat->fSets8 == nullptr) { + e = *fStatus = U_MEMORY_ALLOCATION_ERROR; + return; + } + int32_t i; + for (i=0; ifSets->elementAt(i); + fRXPat->fSets8[i].init(s); + } + +} + + + + + +//------------------------------------------------------------------------------ +// +// doParseAction Do some action during regex pattern parsing. +// Called by the parse state machine. +// +// Generation of the match engine PCode happens here, or +// in functions called from the parse actions defined here. +// +// +//------------------------------------------------------------------------------ +UBool RegexCompile::doParseActions(int32_t action) +{ + UBool returnVal = true; + + switch ((Regex_PatternParseAction)action) { + + case doPatStart: + // Start of pattern compiles to: + //0 SAVE 2 Fall back to position of FAIL + //1 jmp 3 + //2 FAIL Stop if we ever reach here. + //3 NOP Dummy, so start of pattern looks the same as + // the start of an ( grouping. + //4 NOP Resreved, will be replaced by a save if there are + // OR | operators at the top level + appendOp(URX_STATE_SAVE, 2); + appendOp(URX_JMP, 3); + appendOp(URX_FAIL, 0); + + // Standard open nonCapture paren action emits the two NOPs and + // sets up the paren stack frame. + doParseActions(doOpenNonCaptureParen); + break; + + case doPatFinish: + // We've scanned to the end of the pattern + // The end of pattern compiles to: + // URX_END + // which will stop the runtime match engine. + // Encountering end of pattern also behaves like a close paren, + // and forces fixups of the State Save at the beginning of the compiled pattern + // and of any OR operations at the top level. + // + handleCloseParen(); + if (fParenStack.size() > 0) { + // Missing close paren in pattern. + error(U_REGEX_MISMATCHED_PAREN); + } + + // add the END operation to the compiled pattern. + appendOp(URX_END, 0); + + // Terminate the pattern compilation state machine. + returnVal = false; + break; + + + + case doOrOperator: + // Scanning a '|', as in (A|B) + { + // Generate code for any pending literals preceding the '|' + fixLiterals(false); + + // Insert a SAVE operation at the start of the pattern section preceding + // this OR at this level. This SAVE will branch the match forward + // to the right hand side of the OR in the event that the left hand + // side fails to match and backtracks. Locate the position for the + // save from the location on the top of the parentheses stack. + int32_t savePosition = fParenStack.popi(); + int32_t op = (int32_t)fRXPat->fCompiledPat->elementAti(savePosition); + U_ASSERT(URX_TYPE(op) == URX_NOP); // original contents of reserved location + op = buildOp(URX_STATE_SAVE, fRXPat->fCompiledPat->size()+1); + fRXPat->fCompiledPat->setElementAt(op, savePosition); + + // Append an JMP operation into the compiled pattern. The operand for + // the JMP will eventually be the location following the ')' for the + // group. This will be patched in later, when the ')' is encountered. + appendOp(URX_JMP, 0); + + // Push the position of the newly added JMP op onto the parentheses stack. + // This registers if for fixup when this block's close paren is encountered. + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); + + // Append a NOP to the compiled pattern. This is the slot reserved + // for a SAVE in the event that there is yet another '|' following + // this one. + appendOp(URX_NOP, 0); + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); + } + break; + + + case doBeginNamedCapture: + // Scanning (?append(fC.fChar); + break; + + case doBadNamedCapture: + error(U_REGEX_INVALID_CAPTURE_GROUP_NAME); + break; + + case doOpenCaptureParen: + // Open Capturing Paren, possibly named. + // Compile to a + // - NOP, which later may be replaced by a save-state if the + // parenthesized group gets a * quantifier, followed by + // - START_CAPTURE n where n is stack frame offset to the capture group variables. + // - NOP, which may later be replaced by a save-state if there + // is an '|' alternation within the parens. + // + // Each capture group gets three slots in the save stack frame: + // 0: Capture Group start position (in input string being matched.) + // 1: Capture Group end position. + // 2: Start of Match-in-progress. + // The first two locations are for a completed capture group, and are + // referred to by back references and the like. + // The third location stores the capture start position when an START_CAPTURE is + // encountered. This will be promoted to a completed capture when (and if) the corresponding + // END_CAPTURE is encountered. + { + fixLiterals(); + appendOp(URX_NOP, 0); + int32_t varsLoc = allocateStackData(3); // Reserve three slots in match stack frame. + appendOp(URX_START_CAPTURE, varsLoc); + appendOp(URX_NOP, 0); + + // On the Parentheses stack, start a new frame and add the positions + // of the two NOPs. Depending on what follows in the pattern, the + // NOPs may be changed to SAVE_STATE or JMP ops, with a target + // address of the end of the parenthesized group. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push(capturing, *fStatus); // Frame type. + fParenStack.push(fRXPat->fCompiledPat->size()-3, *fStatus); // The first NOP location + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP loc + + // Save the mapping from group number to stack frame variable position. + fRXPat->fGroupMap->addElement(varsLoc, *fStatus); + + // If this is a named capture group, add the name->group number mapping. + if (fCaptureName != nullptr) { + if (!fRXPat->initNamedCaptureMap()) { + if (U_SUCCESS(*fStatus)) { + error(fRXPat->fDeferredStatus); + } + break; + } + int32_t groupNumber = fRXPat->fGroupMap->size(); + int32_t previousMapping = uhash_puti(fRXPat->fNamedCaptureMap, fCaptureName, groupNumber, fStatus); + fCaptureName = nullptr; // hash table takes ownership of the name (key) string. + if (previousMapping > 0 && U_SUCCESS(*fStatus)) { + error(U_REGEX_INVALID_CAPTURE_GROUP_NAME); + } + } + } + break; + + case doOpenNonCaptureParen: + // Open non-caputuring (grouping only) Paren. + // Compile to a + // - NOP, which later may be replaced by a save-state if the + // parenthesized group gets a * quantifier, followed by + // - NOP, which may later be replaced by a save-state if there + // is an '|' alternation within the parens. + { + fixLiterals(); + appendOp(URX_NOP, 0); + appendOp(URX_NOP, 0); + + // On the Parentheses stack, start a new frame and add the positions + // of the two NOPs. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push(plain, *fStatus); // Begin a new frame. + fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP location + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP loc + } + break; + + + case doOpenAtomicParen: + // Open Atomic Paren. (?> + // Compile to a + // - NOP, which later may be replaced if the parenthesized group + // has a quantifier, followed by + // - STO_SP save state stack position, so it can be restored at the ")" + // - NOP, which may later be replaced by a save-state if there + // is an '|' alternation within the parens. + { + fixLiterals(); + appendOp(URX_NOP, 0); + int32_t varLoc = allocateData(1); // Reserve a data location for saving the state stack ptr. + appendOp(URX_STO_SP, varLoc); + appendOp(URX_NOP, 0); + + // On the Parentheses stack, start a new frame and add the positions + // of the two NOPs. Depending on what follows in the pattern, the + // NOPs may be changed to SAVE_STATE or JMP ops, with a target + // address of the end of the parenthesized group. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push(atomic, *fStatus); // Frame type. + fParenStack.push(fRXPat->fCompiledPat->size()-3, *fStatus); // The first NOP + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP + } + break; + + + case doOpenLookAhead: + // Positive Look-ahead (?= stuff ) + // + // Note: Addition of transparent input regions, with the need to + // restore the original regions when failing out of a lookahead + // block, complicated this sequence. Some combined opcodes + // might make sense - or might not, lookahead aren't that common. + // + // Caution: min match length optimization knows about this + // sequence; don't change without making updates there too. + // + // Compiles to + // 1 LA_START dataLoc Saves SP, Input Pos, Active input region. + // 2. STATE_SAVE 4 on failure of lookahead, goto 4 + // 3 JMP 6 continue ... + // + // 4. LA_END Look Ahead failed. Restore regions. + // 5. BACKTRACK and back track again. + // + // 6. NOP reserved for use by quantifiers on the block. + // Look-ahead can't have quantifiers, but paren stack + // compile time conventions require the slot anyhow. + // 7. NOP may be replaced if there is are '|' ops in the block. + // 8. code for parenthesized stuff. + // 9. LA_END + // + // Four data slots are reserved, for saving state on entry to the look-around + // 0: stack pointer on entry. + // 1: input position on entry. + // 2: fActiveStart, the active bounds start on entry. + // 3: fActiveLimit, the active bounds limit on entry. + { + fixLiterals(); + int32_t dataLoc = allocateData(4); + appendOp(URX_LA_START, dataLoc); + appendOp(URX_STATE_SAVE, fRXPat->fCompiledPat->size()+ 2); + appendOp(URX_JMP, fRXPat->fCompiledPat->size()+ 3); + appendOp(URX_LA_END, dataLoc); + appendOp(URX_BACKTRACK, 0); + appendOp(URX_NOP, 0); + appendOp(URX_NOP, 0); + + // On the Parentheses stack, start a new frame and add the positions + // of the NOPs. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push(lookAhead, *fStatus); // Frame type. + fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP location + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP location + } + break; + + case doOpenLookAheadNeg: + // Negated Lookahead. (?! stuff ) + // Compiles to + // 1. LA_START dataloc + // 2. SAVE_STATE 7 // Fail within look-ahead block restores to this state, + // // which continues with the match. + // 3. NOP // Std. Open Paren sequence, for possible '|' + // 4. code for parenthesized stuff. + // 5. LA_END // Cut back stack, remove saved state from step 2. + // 6. BACKTRACK // code in block succeeded, so neg. lookahead fails. + // 7. END_LA // Restore match region, in case look-ahead was using + // an alternate (transparent) region. + // Four data slots are reserved, for saving state on entry to the look-around + // 0: stack pointer on entry. + // 1: input position on entry. + // 2: fActiveStart, the active bounds start on entry. + // 3: fActiveLimit, the active bounds limit on entry. + { + fixLiterals(); + int32_t dataLoc = allocateData(4); + appendOp(URX_LA_START, dataLoc); + appendOp(URX_STATE_SAVE, 0); // dest address will be patched later. + appendOp(URX_NOP, 0); + + // On the Parentheses stack, start a new frame and add the positions + // of the StateSave and NOP. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push(negLookAhead, *fStatus); // Frame type + fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The STATE_SAVE location + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP location + + // Instructions #5 - #7 will be added when the ')' is encountered. + } + break; + + case doOpenLookBehind: + { + // Compile a (?<= look-behind open paren. + // + // Compiles to + // 0 URX_LB_START dataLoc + // 1 URX_LB_CONT dataLoc + // 2 MinMatchLen + // 3 MaxMatchLen + // 4 URX_NOP Standard '(' boilerplate. + // 5 URX_NOP Reserved slot for use with '|' ops within (block). + // 6 + // 7 URX_LB_END dataLoc # Check match len, restore input len + // 8 URX_LA_END dataLoc # Restore stack, input pos + // + // Allocate a block of matcher data, to contain (when running a match) + // 0: Stack ptr on entry + // 1: Input Index on entry + // 2: fActiveStart, the active bounds start on entry. + // 3: fActiveLimit, the active bounds limit on entry. + // 4: Start index of match current match attempt. + // The first four items must match the layout of data for LA_START / LA_END + + // Generate match code for any pending literals. + fixLiterals(); + + // Allocate data space + int32_t dataLoc = allocateData(5); + + // Emit URX_LB_START + appendOp(URX_LB_START, dataLoc); + + // Emit URX_LB_CONT + appendOp(URX_LB_CONT, dataLoc); + appendOp(URX_RESERVED_OP, 0); // MinMatchLength. To be filled later. + appendOp(URX_RESERVED_OP, 0); // MaxMatchLength. To be filled later. + + // Emit the NOPs + appendOp(URX_NOP, 0); + appendOp(URX_NOP, 0); + + // On the Parentheses stack, start a new frame and add the positions + // of the URX_LB_CONT and the NOP. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push(lookBehind, *fStatus); // Frame type + fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP location + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The 2nd NOP location + + // The final two instructions will be added when the ')' is encountered. + } + + break; + + case doOpenLookBehindNeg: + { + // Compile a (? + // 8 URX_LBN_END dataLoc # Check match len, cause a FAIL + // 9 ... + // + // Allocate a block of matcher data, to contain (when running a match) + // 0: Stack ptr on entry + // 1: Input Index on entry + // 2: fActiveStart, the active bounds start on entry. + // 3: fActiveLimit, the active bounds limit on entry. + // 4: Start index of match current match attempt. + // The first four items must match the layout of data for LA_START / LA_END + + // Generate match code for any pending literals. + fixLiterals(); + + // Allocate data space + int32_t dataLoc = allocateData(5); + + // Emit URX_LB_START + appendOp(URX_LB_START, dataLoc); + + // Emit URX_LBN_CONT + appendOp(URX_LBN_CONT, dataLoc); + appendOp(URX_RESERVED_OP, 0); // MinMatchLength. To be filled later. + appendOp(URX_RESERVED_OP, 0); // MaxMatchLength. To be filled later. + appendOp(URX_RESERVED_OP, 0); // Continue Loc. To be filled later. + + // Emit the NOPs + appendOp(URX_NOP, 0); + appendOp(URX_NOP, 0); + + // On the Parentheses stack, start a new frame and add the positions + // of the URX_LB_CONT and the NOP. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push(lookBehindN, *fStatus); // Frame type + fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP location + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The 2nd NOP location + + // The final two instructions will be added when the ')' is encountered. + } + break; + + case doConditionalExpr: + // Conditionals such as (?(1)a:b) + case doPerlInline: + // Perl inline-conditionals. (?{perl code}a|b) We're not perl, no way to do them. + error(U_REGEX_UNIMPLEMENTED); + break; + + + case doCloseParen: + handleCloseParen(); + if (fParenStack.size() <= 0) { + // Extra close paren, or missing open paren. + error(U_REGEX_MISMATCHED_PAREN); + } + break; + + case doNOP: + break; + + + case doBadOpenParenType: + case doRuleError: + error(U_REGEX_RULE_SYNTAX); + break; + + + case doMismatchedParenErr: + error(U_REGEX_MISMATCHED_PAREN); + break; + + case doPlus: + // Normal '+' compiles to + // 1. stuff to be repeated (already built) + // 2. jmp-sav 1 + // 3. ... + // + // Or, if the item to be repeated can match a zero length string, + // 1. STO_INP_LOC data-loc + // 2. body of stuff to be repeated + // 3. JMP_SAV_X 2 + // 4. ... + + // + // Or, if the item to be repeated is simple + // 1. Item to be repeated. + // 2. LOOP_SR_I set number (assuming repeated item is a set ref) + // 3. LOOP_C stack location + { + int32_t topLoc = blockTopLoc(false); // location of item #1 + int32_t frameLoc; + + // Check for simple constructs, which may get special optimized code. + if (topLoc == fRXPat->fCompiledPat->size() - 1) { + int32_t repeatedOp = (int32_t)fRXPat->fCompiledPat->elementAti(topLoc); + + if (URX_TYPE(repeatedOp) == URX_SETREF) { + // Emit optimized code for [char set]+ + appendOp(URX_LOOP_SR_I, URX_VAL(repeatedOp)); + frameLoc = allocateStackData(1); + appendOp(URX_LOOP_C, frameLoc); + break; + } + + if (URX_TYPE(repeatedOp) == URX_DOTANY || + URX_TYPE(repeatedOp) == URX_DOTANY_ALL || + URX_TYPE(repeatedOp) == URX_DOTANY_UNIX) { + // Emit Optimized code for .+ operations. + int32_t loopOpI = buildOp(URX_LOOP_DOT_I, 0); + if (URX_TYPE(repeatedOp) == URX_DOTANY_ALL) { + // URX_LOOP_DOT_I operand is a flag indicating ". matches any" mode. + loopOpI |= 1; + } + if (fModeFlags & UREGEX_UNIX_LINES) { + loopOpI |= 2; + } + appendOp(loopOpI); + frameLoc = allocateStackData(1); + appendOp(URX_LOOP_C, frameLoc); + break; + } + + } + + // General case. + + // Check for minimum match length of zero, which requires + // extra loop-breaking code. + if (minMatchLength(topLoc, fRXPat->fCompiledPat->size()-1) == 0) { + // Zero length match is possible. + // Emit the code sequence that can handle it. + insertOp(topLoc); + frameLoc = allocateStackData(1); + + int32_t op = buildOp(URX_STO_INP_LOC, frameLoc); + fRXPat->fCompiledPat->setElementAt(op, topLoc); + + appendOp(URX_JMP_SAV_X, topLoc+1); + } else { + // Simpler code when the repeated body must match something non-empty + appendOp(URX_JMP_SAV, topLoc); + } + } + break; + + case doNGPlus: + // Non-greedy '+?' compiles to + // 1. stuff to be repeated (already built) + // 2. state-save 1 + // 3. ... + { + int32_t topLoc = blockTopLoc(false); + appendOp(URX_STATE_SAVE, topLoc); + } + break; + + + case doOpt: + // Normal (greedy) ? quantifier. + // Compiles to + // 1. state save 3 + // 2. body of optional block + // 3. ... + // Insert the state save into the compiled pattern, and we're done. + { + int32_t saveStateLoc = blockTopLoc(true); + int32_t saveStateOp = buildOp(URX_STATE_SAVE, fRXPat->fCompiledPat->size()); + fRXPat->fCompiledPat->setElementAt(saveStateOp, saveStateLoc); + } + break; + + case doNGOpt: + // Non-greedy ?? quantifier + // compiles to + // 1. jmp 4 + // 2. body of optional block + // 3 jmp 5 + // 4. state save 2 + // 5 ... + // This code is less than ideal, with two jmps instead of one, because we can only + // insert one instruction at the top of the block being iterated. + { + int32_t jmp1_loc = blockTopLoc(true); + int32_t jmp2_loc = fRXPat->fCompiledPat->size(); + + int32_t jmp1_op = buildOp(URX_JMP, jmp2_loc+1); + fRXPat->fCompiledPat->setElementAt(jmp1_op, jmp1_loc); + + appendOp(URX_JMP, jmp2_loc+2); + + appendOp(URX_STATE_SAVE, jmp1_loc+1); + } + break; + + + case doStar: + // Normal (greedy) * quantifier. + // Compiles to + // 1. STATE_SAVE 4 + // 2. body of stuff being iterated over + // 3. JMP_SAV 2 + // 4. ... + // + // Or, if the body is a simple [Set], + // 1. LOOP_SR_I set number + // 2. LOOP_C stack location + // ... + // + // Or if this is a .* + // 1. LOOP_DOT_I (. matches all mode flag) + // 2. LOOP_C stack location + // + // Or, if the body can match a zero-length string, to inhibit infinite loops, + // 1. STATE_SAVE 5 + // 2. STO_INP_LOC data-loc + // 3. body of stuff + // 4. JMP_SAV_X 2 + // 5. ... + { + // location of item #1, the STATE_SAVE + int32_t topLoc = blockTopLoc(false); + int32_t dataLoc = -1; + + // Check for simple *, where the construct being repeated + // compiled to single opcode, and might be optimizable. + if (topLoc == fRXPat->fCompiledPat->size() - 1) { + int32_t repeatedOp = (int32_t)fRXPat->fCompiledPat->elementAti(topLoc); + + if (URX_TYPE(repeatedOp) == URX_SETREF) { + // Emit optimized code for a [char set]* + int32_t loopOpI = buildOp(URX_LOOP_SR_I, URX_VAL(repeatedOp)); + fRXPat->fCompiledPat->setElementAt(loopOpI, topLoc); + dataLoc = allocateStackData(1); + appendOp(URX_LOOP_C, dataLoc); + break; + } + + if (URX_TYPE(repeatedOp) == URX_DOTANY || + URX_TYPE(repeatedOp) == URX_DOTANY_ALL || + URX_TYPE(repeatedOp) == URX_DOTANY_UNIX) { + // Emit Optimized code for .* operations. + int32_t loopOpI = buildOp(URX_LOOP_DOT_I, 0); + if (URX_TYPE(repeatedOp) == URX_DOTANY_ALL) { + // URX_LOOP_DOT_I operand is a flag indicating . matches any mode. + loopOpI |= 1; + } + if ((fModeFlags & UREGEX_UNIX_LINES) != 0) { + loopOpI |= 2; + } + fRXPat->fCompiledPat->setElementAt(loopOpI, topLoc); + dataLoc = allocateStackData(1); + appendOp(URX_LOOP_C, dataLoc); + break; + } + } + + // Emit general case code for this * + // The optimizations did not apply. + + int32_t saveStateLoc = blockTopLoc(true); + int32_t jmpOp = buildOp(URX_JMP_SAV, saveStateLoc+1); + + // Check for minimum match length of zero, which requires + // extra loop-breaking code. + if (minMatchLength(saveStateLoc, fRXPat->fCompiledPat->size()-1) == 0) { + insertOp(saveStateLoc); + dataLoc = allocateStackData(1); + + int32_t op = buildOp(URX_STO_INP_LOC, dataLoc); + fRXPat->fCompiledPat->setElementAt(op, saveStateLoc+1); + jmpOp = buildOp(URX_JMP_SAV_X, saveStateLoc+2); + } + + // Locate the position in the compiled pattern where the match will continue + // after completing the *. (4 or 5 in the comment above) + int32_t continueLoc = fRXPat->fCompiledPat->size()+1; + + // Put together the save state op and store it into the compiled code. + int32_t saveStateOp = buildOp(URX_STATE_SAVE, continueLoc); + fRXPat->fCompiledPat->setElementAt(saveStateOp, saveStateLoc); + + // Append the URX_JMP_SAV or URX_JMPX operation to the compiled pattern. + appendOp(jmpOp); + } + break; + + case doNGStar: + // Non-greedy *? quantifier + // compiles to + // 1. JMP 3 + // 2. body of stuff being iterated over + // 3. STATE_SAVE 2 + // 4 ... + { + int32_t jmpLoc = blockTopLoc(true); // loc 1. + int32_t saveLoc = fRXPat->fCompiledPat->size(); // loc 3. + int32_t jmpOp = buildOp(URX_JMP, saveLoc); + fRXPat->fCompiledPat->setElementAt(jmpOp, jmpLoc); + appendOp(URX_STATE_SAVE, jmpLoc+1); + } + break; + + + case doIntervalInit: + // The '{' opening an interval quantifier was just scanned. + // Init the counter variables that will accumulate the values as the digits + // are scanned. + fIntervalLow = 0; + fIntervalUpper = -1; + break; + + case doIntevalLowerDigit: + // Scanned a digit from the lower value of an {lower,upper} interval + { + int32_t digitValue = u_charDigitValue(fC.fChar); + U_ASSERT(digitValue >= 0); + int64_t val = (int64_t)fIntervalLow*10 + digitValue; + if (val > INT32_MAX) { + error(U_REGEX_NUMBER_TOO_BIG); + } else { + fIntervalLow = (int32_t)val; + } + } + break; + + case doIntervalUpperDigit: + // Scanned a digit from the upper value of an {lower,upper} interval + { + if (fIntervalUpper < 0) { + fIntervalUpper = 0; + } + int32_t digitValue = u_charDigitValue(fC.fChar); + U_ASSERT(digitValue >= 0); + int64_t val = (int64_t)fIntervalUpper*10 + digitValue; + if (val > INT32_MAX) { + error(U_REGEX_NUMBER_TOO_BIG); + } else { + fIntervalUpper = (int32_t)val; + } + } + break; + + case doIntervalSame: + // Scanned a single value interval like {27}. Upper = Lower. + fIntervalUpper = fIntervalLow; + break; + + case doInterval: + // Finished scanning a normal {lower,upper} interval. Generate the code for it. + if (compileInlineInterval() == false) { + compileInterval(URX_CTR_INIT, URX_CTR_LOOP); + } + break; + + case doPossessiveInterval: + // Finished scanning a Possessive {lower,upper}+ interval. Generate the code for it. + { + // Remember the loc for the top of the block being looped over. + // (Can not reserve a slot in the compiled pattern at this time, because + // compileInterval needs to reserve also, and blockTopLoc can only reserve + // once per block.) + int32_t topLoc = blockTopLoc(false); + + // Produce normal looping code. + compileInterval(URX_CTR_INIT, URX_CTR_LOOP); + + // Surround the just-emitted normal looping code with a STO_SP ... LD_SP + // just as if the loop was inclosed in atomic parentheses. + + // First the STO_SP before the start of the loop + insertOp(topLoc); + + int32_t varLoc = allocateData(1); // Reserve a data location for saving the + int32_t op = buildOp(URX_STO_SP, varLoc); + fRXPat->fCompiledPat->setElementAt(op, topLoc); + + int32_t loopOp = (int32_t)fRXPat->fCompiledPat->popi(); + U_ASSERT(URX_TYPE(loopOp) == URX_CTR_LOOP && URX_VAL(loopOp) == topLoc); + loopOp++; // point LoopOp after the just-inserted STO_SP + fRXPat->fCompiledPat->push(loopOp, *fStatus); + + // Then the LD_SP after the end of the loop + appendOp(URX_LD_SP, varLoc); + } + + break; + + case doNGInterval: + // Finished scanning a non-greedy {lower,upper}? interval. Generate the code for it. + compileInterval(URX_CTR_INIT_NG, URX_CTR_LOOP_NG); + break; + + case doIntervalError: + error(U_REGEX_BAD_INTERVAL); + break; + + case doLiteralChar: + // We've just scanned a "normal" character from the pattern, + literalChar(fC.fChar); + break; + + + case doEscapedLiteralChar: + // We've just scanned an backslashed escaped character with no + // special meaning. It represents itself. + if ((fModeFlags & UREGEX_ERROR_ON_UNKNOWN_ESCAPES) != 0 && + ((fC.fChar >= 0x41 && fC.fChar<= 0x5A) || // in [A-Z] + (fC.fChar >= 0x61 && fC.fChar <= 0x7a))) { // in [a-z] + error(U_REGEX_BAD_ESCAPE_SEQUENCE); + } + literalChar(fC.fChar); + break; + + + case doDotAny: + // scanned a ".", match any single character. + { + fixLiterals(false); + if (fModeFlags & UREGEX_DOTALL) { + appendOp(URX_DOTANY_ALL, 0); + } else if (fModeFlags & UREGEX_UNIX_LINES) { + appendOp(URX_DOTANY_UNIX, 0); + } else { + appendOp(URX_DOTANY, 0); + } + } + break; + + case doCaret: + { + fixLiterals(false); + if ( (fModeFlags & UREGEX_MULTILINE) == 0 && (fModeFlags & UREGEX_UNIX_LINES) == 0) { + appendOp(URX_CARET, 0); + } else if ((fModeFlags & UREGEX_MULTILINE) != 0 && (fModeFlags & UREGEX_UNIX_LINES) == 0) { + appendOp(URX_CARET_M, 0); + } else if ((fModeFlags & UREGEX_MULTILINE) == 0 && (fModeFlags & UREGEX_UNIX_LINES) != 0) { + appendOp(URX_CARET, 0); // Only testing true start of input. + } else if ((fModeFlags & UREGEX_MULTILINE) != 0 && (fModeFlags & UREGEX_UNIX_LINES) != 0) { + appendOp(URX_CARET_M_UNIX, 0); + } + } + break; + + case doDollar: + { + fixLiterals(false); + if ( (fModeFlags & UREGEX_MULTILINE) == 0 && (fModeFlags & UREGEX_UNIX_LINES) == 0) { + appendOp(URX_DOLLAR, 0); + } else if ((fModeFlags & UREGEX_MULTILINE) != 0 && (fModeFlags & UREGEX_UNIX_LINES) == 0) { + appendOp(URX_DOLLAR_M, 0); + } else if ((fModeFlags & UREGEX_MULTILINE) == 0 && (fModeFlags & UREGEX_UNIX_LINES) != 0) { + appendOp(URX_DOLLAR_D, 0); + } else if ((fModeFlags & UREGEX_MULTILINE) != 0 && (fModeFlags & UREGEX_UNIX_LINES) != 0) { + appendOp(URX_DOLLAR_MD, 0); + } + } + break; + + case doBackslashA: + fixLiterals(false); + appendOp(URX_CARET, 0); + break; + + case doBackslashB: + { + #if UCONFIG_NO_BREAK_ITERATION==1 + if (fModeFlags & UREGEX_UWORD) { + error(U_UNSUPPORTED_ERROR); + } + #endif + fixLiterals(false); + int32_t op = (fModeFlags & UREGEX_UWORD)? URX_BACKSLASH_BU : URX_BACKSLASH_B; + appendOp(op, 1); + } + break; + + case doBackslashb: + { + #if UCONFIG_NO_BREAK_ITERATION==1 + if (fModeFlags & UREGEX_UWORD) { + error(U_UNSUPPORTED_ERROR); + } + #endif + fixLiterals(false); + int32_t op = (fModeFlags & UREGEX_UWORD)? URX_BACKSLASH_BU : URX_BACKSLASH_B; + appendOp(op, 0); + } + break; + + case doBackslashD: + fixLiterals(false); + appendOp(URX_BACKSLASH_D, 1); + break; + + case doBackslashd: + fixLiterals(false); + appendOp(URX_BACKSLASH_D, 0); + break; + + case doBackslashG: + fixLiterals(false); + appendOp(URX_BACKSLASH_G, 0); + break; + + case doBackslashH: + fixLiterals(false); + appendOp(URX_BACKSLASH_H, 1); + break; + + case doBackslashh: + fixLiterals(false); + appendOp(URX_BACKSLASH_H, 0); + break; + + case doBackslashR: + fixLiterals(false); + appendOp(URX_BACKSLASH_R, 0); + break; + + case doBackslashS: + fixLiterals(false); + appendOp(URX_STAT_SETREF_N, URX_ISSPACE_SET); + break; + + case doBackslashs: + fixLiterals(false); + appendOp(URX_STATIC_SETREF, URX_ISSPACE_SET); + break; + + case doBackslashV: + fixLiterals(false); + appendOp(URX_BACKSLASH_V, 1); + break; + + case doBackslashv: + fixLiterals(false); + appendOp(URX_BACKSLASH_V, 0); + break; + + case doBackslashW: + fixLiterals(false); + appendOp(URX_STAT_SETREF_N, URX_ISWORD_SET); + break; + + case doBackslashw: + fixLiterals(false); + appendOp(URX_STATIC_SETREF, URX_ISWORD_SET); + break; + + case doBackslashX: + #if UCONFIG_NO_BREAK_ITERATION==1 + // Grapheme Cluster Boundary requires ICU break iteration. + error(U_UNSUPPORTED_ERROR); + #endif + fixLiterals(false); + appendOp(URX_BACKSLASH_X, 0); + break; + + case doBackslashZ: + fixLiterals(false); + appendOp(URX_DOLLAR, 0); + break; + + case doBackslashz: + fixLiterals(false); + appendOp(URX_BACKSLASH_Z, 0); + break; + + case doEscapeError: + error(U_REGEX_BAD_ESCAPE_SEQUENCE); + break; + + case doExit: + fixLiterals(false); + returnVal = false; + break; + + case doProperty: + { + fixLiterals(false); + UnicodeSet *theSet = scanProp(); + compileSet(theSet); + } + break; + + case doNamedChar: + { + UChar32 c = scanNamedChar(); + literalChar(c); + } + break; + + + case doBackRef: + // BackReference. Somewhat unusual in that the front-end can not completely parse + // the regular expression, because the number of digits to be consumed + // depends on the number of capture groups that have been defined. So + // we have to do it here instead. + { + int32_t numCaptureGroups = fRXPat->fGroupMap->size(); + int32_t groupNum = 0; + UChar32 c = fC.fChar; + + for (;;) { + // Loop once per digit, for max allowed number of digits in a back reference. + int32_t digit = u_charDigitValue(c); + groupNum = groupNum * 10 + digit; + if (groupNum >= numCaptureGroups) { + break; + } + c = peekCharLL(); + if (RegexStaticSets::gStaticSets->fRuleDigitsAlias->contains(c) == false) { + break; + } + nextCharLL(); + } + + // Scan of the back reference in the source regexp is complete. Now generate + // the compiled code for it. + // Because capture groups can be forward-referenced by back-references, + // we fill the operand with the capture group number. At the end + // of compilation, it will be changed to the variable's location. + U_ASSERT(groupNum > 0); // Shouldn't happen. '\0' begins an octal escape sequence, + // and shouldn't enter this code path at all. + fixLiterals(false); + if (fModeFlags & UREGEX_CASE_INSENSITIVE) { + appendOp(URX_BACKREF_I, groupNum); + } else { + appendOp(URX_BACKREF, groupNum); + } + } + break; + + case doBeginNamedBackRef: + U_ASSERT(fCaptureName == nullptr); + fCaptureName = new UnicodeString; + if (fCaptureName == nullptr) { + error(U_MEMORY_ALLOCATION_ERROR); + } + break; + + case doContinueNamedBackRef: + fCaptureName->append(fC.fChar); + break; + + case doCompleteNamedBackRef: + { + int32_t groupNumber = + fRXPat->fNamedCaptureMap ? uhash_geti(fRXPat->fNamedCaptureMap, fCaptureName) : 0; + if (groupNumber == 0) { + // Group name has not been defined. + // Could be a forward reference. If we choose to support them at some + // future time, extra mechanism will be required at this point. + error(U_REGEX_INVALID_CAPTURE_GROUP_NAME); + } else { + // Given the number, handle identically to a \n numbered back reference. + // See comments above, under doBackRef + fixLiterals(false); + if (fModeFlags & UREGEX_CASE_INSENSITIVE) { + appendOp(URX_BACKREF_I, groupNumber); + } else { + appendOp(URX_BACKREF, groupNumber); + } + } + delete fCaptureName; + fCaptureName = nullptr; + break; + } + + case doPossessivePlus: + // Possessive ++ quantifier. + // Compiles to + // 1. STO_SP + // 2. body of stuff being iterated over + // 3. STATE_SAVE 5 + // 4. JMP 2 + // 5. LD_SP + // 6. ... + // + // Note: TODO: This is pretty inefficient. A mass of saved state is built up + // then unconditionally discarded. Perhaps introduce a new opcode. Ticket 6056 + // + { + // Emit the STO_SP + int32_t topLoc = blockTopLoc(true); + int32_t stoLoc = allocateData(1); // Reserve the data location for storing save stack ptr. + int32_t op = buildOp(URX_STO_SP, stoLoc); + fRXPat->fCompiledPat->setElementAt(op, topLoc); + + // Emit the STATE_SAVE + appendOp(URX_STATE_SAVE, fRXPat->fCompiledPat->size()+2); + + // Emit the JMP + appendOp(URX_JMP, topLoc+1); + + // Emit the LD_SP + appendOp(URX_LD_SP, stoLoc); + } + break; + + case doPossessiveStar: + // Possessive *+ quantifier. + // Compiles to + // 1. STO_SP loc + // 2. STATE_SAVE 5 + // 3. body of stuff being iterated over + // 4. JMP 2 + // 5. LD_SP loc + // 6 ... + // TODO: do something to cut back the state stack each time through the loop. + { + // Reserve two slots at the top of the block. + int32_t topLoc = blockTopLoc(true); + insertOp(topLoc); + + // emit STO_SP loc + int32_t stoLoc = allocateData(1); // Reserve the data location for storing save stack ptr. + int32_t op = buildOp(URX_STO_SP, stoLoc); + fRXPat->fCompiledPat->setElementAt(op, topLoc); + + // Emit the SAVE_STATE 5 + int32_t L7 = fRXPat->fCompiledPat->size()+1; + op = buildOp(URX_STATE_SAVE, L7); + fRXPat->fCompiledPat->setElementAt(op, topLoc+1); + + // Append the JMP operation. + appendOp(URX_JMP, topLoc+1); + + // Emit the LD_SP loc + appendOp(URX_LD_SP, stoLoc); + } + break; + + case doPossessiveOpt: + // Possessive ?+ quantifier. + // Compiles to + // 1. STO_SP loc + // 2. SAVE_STATE 5 + // 3. body of optional block + // 4. LD_SP loc + // 5. ... + // + { + // Reserve two slots at the top of the block. + int32_t topLoc = blockTopLoc(true); + insertOp(topLoc); + + // Emit the STO_SP + int32_t stoLoc = allocateData(1); // Reserve the data location for storing save stack ptr. + int32_t op = buildOp(URX_STO_SP, stoLoc); + fRXPat->fCompiledPat->setElementAt(op, topLoc); + + // Emit the SAVE_STATE + int32_t continueLoc = fRXPat->fCompiledPat->size()+1; + op = buildOp(URX_STATE_SAVE, continueLoc); + fRXPat->fCompiledPat->setElementAt(op, topLoc+1); + + // Emit the LD_SP + appendOp(URX_LD_SP, stoLoc); + } + break; + + + case doBeginMatchMode: + fNewModeFlags = fModeFlags; + fSetModeFlag = true; + break; + + case doMatchMode: // (?i) and similar + { + int32_t bit = 0; + switch (fC.fChar) { + case 0x69: /* 'i' */ bit = UREGEX_CASE_INSENSITIVE; break; + case 0x64: /* 'd' */ bit = UREGEX_UNIX_LINES; break; + case 0x6d: /* 'm' */ bit = UREGEX_MULTILINE; break; + case 0x73: /* 's' */ bit = UREGEX_DOTALL; break; + case 0x75: /* 'u' */ bit = 0; /* Unicode casing */ break; + case 0x77: /* 'w' */ bit = UREGEX_UWORD; break; + case 0x78: /* 'x' */ bit = UREGEX_COMMENTS; break; + case 0x2d: /* '-' */ fSetModeFlag = false; break; + default: + UPRV_UNREACHABLE_EXIT; // Should never happen. Other chars are filtered out + // by the scanner. + } + if (fSetModeFlag) { + fNewModeFlags |= bit; + } else { + fNewModeFlags &= ~bit; + } + } + break; + + case doSetMatchMode: + // Emit code to match any pending literals, using the not-yet changed match mode. + fixLiterals(); + + // We've got a (?i) or similar. The match mode is being changed, but + // the change is not scoped to a parenthesized block. + U_ASSERT(fNewModeFlags < 0); + fModeFlags = fNewModeFlags; + + break; + + + case doMatchModeParen: + // We've got a (?i: or similar. Begin a parenthesized block, save old + // mode flags so they can be restored at the close of the block. + // + // Compile to a + // - NOP, which later may be replaced by a save-state if the + // parenthesized group gets a * quantifier, followed by + // - NOP, which may later be replaced by a save-state if there + // is an '|' alternation within the parens. + { + fixLiterals(false); + appendOp(URX_NOP, 0); + appendOp(URX_NOP, 0); + + // On the Parentheses stack, start a new frame and add the positions + // of the two NOPs (a normal non-capturing () frame, except for the + // saving of the original mode flags.) + fParenStack.push(fModeFlags, *fStatus); + fParenStack.push(flags, *fStatus); // Frame Marker + fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP + + // Set the current mode flags to the new values. + U_ASSERT(fNewModeFlags < 0); + fModeFlags = fNewModeFlags; + } + break; + + case doBadModeFlag: + error(U_REGEX_INVALID_FLAG); + break; + + case doSuppressComments: + // We have just scanned a '(?'. We now need to prevent the character scanner from + // treating a '#' as a to-the-end-of-line comment. + // (This Perl compatibility just gets uglier and uglier to do...) + fEOLComments = false; + break; + + + case doSetAddAmp: + { + UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); + set->add(chAmp); + } + break; + + case doSetAddDash: + { + UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); + set->add(chDash); + } + break; + + case doSetBackslash_s: + { + UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); + set->addAll(RegexStaticSets::gStaticSets->fPropSets[URX_ISSPACE_SET]); + break; + } + + case doSetBackslash_S: + { + UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); + UnicodeSet SSet; + SSet.addAll(RegexStaticSets::gStaticSets->fPropSets[URX_ISSPACE_SET]).complement(); + set->addAll(SSet); + break; + } + + case doSetBackslash_d: + { + UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); + // TODO - make a static set, ticket 6058. + addCategory(set, U_GC_ND_MASK, *fStatus); + break; + } + + case doSetBackslash_D: + { + UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); + UnicodeSet digits; + // TODO - make a static set, ticket 6058. + digits.applyIntPropertyValue(UCHAR_GENERAL_CATEGORY_MASK, U_GC_ND_MASK, *fStatus); + digits.complement(); + set->addAll(digits); + break; + } + + case doSetBackslash_h: + { + UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); + UnicodeSet h; + h.applyIntPropertyValue(UCHAR_GENERAL_CATEGORY_MASK, U_GC_ZS_MASK, *fStatus); + h.add((UChar32)9); // Tab + set->addAll(h); + break; + } + + case doSetBackslash_H: + { + UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); + UnicodeSet h; + h.applyIntPropertyValue(UCHAR_GENERAL_CATEGORY_MASK, U_GC_ZS_MASK, *fStatus); + h.add((UChar32)9); // Tab + h.complement(); + set->addAll(h); + break; + } + + case doSetBackslash_v: + { + UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); + set->add((UChar32)0x0a, (UChar32)0x0d); // add range + set->add((UChar32)0x85); + set->add((UChar32)0x2028, (UChar32)0x2029); + break; + } + + case doSetBackslash_V: + { + UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); + UnicodeSet v; + v.add((UChar32)0x0a, (UChar32)0x0d); // add range + v.add((UChar32)0x85); + v.add((UChar32)0x2028, (UChar32)0x2029); + v.complement(); + set->addAll(v); + break; + } + + case doSetBackslash_w: + { + UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); + set->addAll(RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET]); + break; + } + + case doSetBackslash_W: + { + UnicodeSet *set = (UnicodeSet *)fSetStack.peek(); + UnicodeSet SSet; + SSet.addAll(RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET]).complement(); + set->addAll(SSet); + break; + } + + case doSetBegin: + { + fixLiterals(false); + LocalPointer lpSet(new UnicodeSet(), *fStatus); + fSetStack.push(lpSet.orphan(), *fStatus); + fSetOpStack.push(setStart, *fStatus); + if ((fModeFlags & UREGEX_CASE_INSENSITIVE) != 0) { + fSetOpStack.push(setCaseClose, *fStatus); + } + break; + } + + case doSetBeginDifference1: + // We have scanned something like [[abc]-[ + // Set up a new UnicodeSet for the set beginning with the just-scanned '[' + // Push a Difference operator, which will cause the new set to be subtracted from what + // went before once it is created. + setPushOp(setDifference1); + fSetOpStack.push(setStart, *fStatus); + if ((fModeFlags & UREGEX_CASE_INSENSITIVE) != 0) { + fSetOpStack.push(setCaseClose, *fStatus); + } + break; + + case doSetBeginIntersection1: + // We have scanned something like [[abc]&[ + // Need both the '&' operator and the open '[' operator. + setPushOp(setIntersection1); + fSetOpStack.push(setStart, *fStatus); + if ((fModeFlags & UREGEX_CASE_INSENSITIVE) != 0) { + fSetOpStack.push(setCaseClose, *fStatus); + } + break; + + case doSetBeginUnion: + // We have scanned something like [[abc][ + // Need to handle the union operation explicitly [[abc] | [ + setPushOp(setUnion); + fSetOpStack.push(setStart, *fStatus); + if ((fModeFlags & UREGEX_CASE_INSENSITIVE) != 0) { + fSetOpStack.push(setCaseClose, *fStatus); + } + break; + + case doSetDifference2: + // We have scanned something like [abc-- + // Consider this to unambiguously be a set difference operator. + setPushOp(setDifference2); + break; + + case doSetEnd: + // Have encountered the ']' that closes a set. + // Force the evaluation of any pending operations within this set, + // leave the completed set on the top of the set stack. + setEval(setEnd); + U_ASSERT(fSetOpStack.peeki()==setStart); + fSetOpStack.popi(); + break; + + case doSetFinish: + { + // Finished a complete set expression, including all nested sets. + // The close bracket has already triggered clearing out pending set operators, + // the operator stack should be empty and the operand stack should have just + // one entry, the result set. + U_ASSERT(fSetOpStack.empty()); + UnicodeSet *theSet = (UnicodeSet *)fSetStack.pop(); + U_ASSERT(fSetStack.empty()); + compileSet(theSet); + break; + } + + case doSetIntersection2: + // Have scanned something like [abc&& + setPushOp(setIntersection2); + break; + + case doSetLiteral: + // Union the just-scanned literal character into the set being built. + // This operation is the highest precedence set operation, so we can always do + // it immediately, without waiting to see what follows. It is necessary to perform + // any pending '-' or '&' operation first, because these have the same precedence + // as union-ing in a literal' + { + setEval(setUnion); + UnicodeSet *s = (UnicodeSet *)fSetStack.peek(); + s->add(fC.fChar); + fLastSetLiteral = fC.fChar; + break; + } + + case doSetLiteralEscaped: + // A back-slash escaped literal character was encountered. + // Processing is the same as with setLiteral, above, with the addition of + // the optional check for errors on escaped ASCII letters. + { + if ((fModeFlags & UREGEX_ERROR_ON_UNKNOWN_ESCAPES) != 0 && + ((fC.fChar >= 0x41 && fC.fChar<= 0x5A) || // in [A-Z] + (fC.fChar >= 0x61 && fC.fChar <= 0x7a))) { // in [a-z] + error(U_REGEX_BAD_ESCAPE_SEQUENCE); + } + setEval(setUnion); + UnicodeSet *s = (UnicodeSet *)fSetStack.peek(); + s->add(fC.fChar); + fLastSetLiteral = fC.fChar; + break; + } + + case doSetNamedChar: + // Scanning a \N{UNICODE CHARACTER NAME} + // Aside from the source of the character, the processing is identical to doSetLiteral, + // above. + { + UChar32 c = scanNamedChar(); + setEval(setUnion); + UnicodeSet *s = (UnicodeSet *)fSetStack.peek(); + s->add(c); + fLastSetLiteral = c; + break; + } + + case doSetNamedRange: + // We have scanned literal-\N{CHAR NAME}. Add the range to the set. + // The left character is already in the set, and is saved in fLastSetLiteral. + // The right side needs to be picked up, the scan is at the 'N'. + // Lower Limit > Upper limit being an error matches both Java + // and ICU UnicodeSet behavior. + { + UChar32 c = scanNamedChar(); + if (U_SUCCESS(*fStatus) && (fLastSetLiteral == U_SENTINEL || fLastSetLiteral > c)) { + error(U_REGEX_INVALID_RANGE); + } + UnicodeSet *s = (UnicodeSet *)fSetStack.peek(); + s->add(fLastSetLiteral, c); + fLastSetLiteral = c; + break; + } + + + case doSetNegate: + // Scanned a '^' at the start of a set. + // Push the negation operator onto the set op stack. + // A twist for case-insensitive matching: + // the case closure operation must happen _before_ negation. + // But the case closure operation will already be on the stack if it's required. + // This requires checking for case closure, and swapping the stack order + // if it is present. + { + int32_t tosOp = fSetOpStack.peeki(); + if (tosOp == setCaseClose) { + fSetOpStack.popi(); + fSetOpStack.push(setNegation, *fStatus); + fSetOpStack.push(setCaseClose, *fStatus); + } else { + fSetOpStack.push(setNegation, *fStatus); + } + } + break; + + case doSetNoCloseError: + error(U_REGEX_MISSING_CLOSE_BRACKET); + break; + + case doSetOpError: + error(U_REGEX_RULE_SYNTAX); // -- or && at the end of a set. Illegal. + break; + + case doSetPosixProp: + { + UnicodeSet *s = scanPosixProp(); + if (s != nullptr) { + UnicodeSet *tos = (UnicodeSet *)fSetStack.peek(); + tos->addAll(*s); + delete s; + } // else error. scanProp() reported the error status already. + } + break; + + case doSetProp: + // Scanned a \p \P within [brackets]. + { + UnicodeSet *s = scanProp(); + if (s != nullptr) { + UnicodeSet *tos = (UnicodeSet *)fSetStack.peek(); + tos->addAll(*s); + delete s; + } // else error. scanProp() reported the error status already. + } + break; + + + case doSetRange: + // We have scanned literal-literal. Add the range to the set. + // The left character is already in the set, and is saved in fLastSetLiteral. + // The right side is the current character. + // Lower Limit > Upper limit being an error matches both Java + // and ICU UnicodeSet behavior. + { + + if (fLastSetLiteral == U_SENTINEL || fLastSetLiteral > fC.fChar) { + error(U_REGEX_INVALID_RANGE); + } + UnicodeSet *s = (UnicodeSet *)fSetStack.peek(); + s->add(fLastSetLiteral, fC.fChar); + break; + } + + default: + UPRV_UNREACHABLE_EXIT; + } + + if (U_FAILURE(*fStatus)) { + returnVal = false; + } + + return returnVal; +} + + + +//------------------------------------------------------------------------------ +// +// literalChar We've encountered a literal character from the pattern, +// or an escape sequence that reduces to a character. +// Add it to the string containing all literal chars/strings from +// the pattern. +// +//------------------------------------------------------------------------------ +void RegexCompile::literalChar(UChar32 c) { + fLiteralChars.append(c); +} + + +//------------------------------------------------------------------------------ +// +// fixLiterals When compiling something that can follow a literal +// string in a pattern, emit the code to match the +// accumulated literal string. +// +// Optionally, split the last char of the string off into +// a single "ONE_CHAR" operation, so that quantifiers can +// apply to that char alone. Example: abc* +// The * must apply to the 'c' only. +// +//------------------------------------------------------------------------------ +void RegexCompile::fixLiterals(UBool split) { + + // If no literal characters have been scanned but not yet had code generated + // for them, nothing needs to be done. + if (fLiteralChars.length() == 0) { + return; + } + + int32_t indexOfLastCodePoint = fLiteralChars.moveIndex32(fLiteralChars.length(), -1); + UChar32 lastCodePoint = fLiteralChars.char32At(indexOfLastCodePoint); + + // Split: We need to ensure that the last item in the compiled pattern + // refers only to the last literal scanned in the pattern, so that + // quantifiers (*, +, etc.) affect only it, and not a longer string. + // Split before case folding for case insensitive matches. + + if (split) { + fLiteralChars.truncate(indexOfLastCodePoint); + fixLiterals(false); // Recursive call, emit code to match the first part of the string. + // Note that the truncated literal string may be empty, in which case + // nothing will be emitted. + + literalChar(lastCodePoint); // Re-add the last code point as if it were a new literal. + fixLiterals(false); // Second recursive call, code for the final code point. + return; + } + + // If we are doing case-insensitive matching, case fold the string. This may expand + // the string, e.g. the German sharp-s turns into "ss" + if (fModeFlags & UREGEX_CASE_INSENSITIVE) { + fLiteralChars.foldCase(); + indexOfLastCodePoint = fLiteralChars.moveIndex32(fLiteralChars.length(), -1); + lastCodePoint = fLiteralChars.char32At(indexOfLastCodePoint); + } + + if (indexOfLastCodePoint == 0) { + // Single character, emit a URX_ONECHAR op to match it. + if ((fModeFlags & UREGEX_CASE_INSENSITIVE) && + u_hasBinaryProperty(lastCodePoint, UCHAR_CASE_SENSITIVE)) { + appendOp(URX_ONECHAR_I, lastCodePoint); + } else { + appendOp(URX_ONECHAR, lastCodePoint); + } + } else { + // Two or more chars, emit a URX_STRING to match them. + if (fLiteralChars.length() > 0x00ffffff || fRXPat->fLiteralText.length() > 0x00ffffff) { + error(U_REGEX_PATTERN_TOO_BIG); + } + if (fModeFlags & UREGEX_CASE_INSENSITIVE) { + appendOp(URX_STRING_I, fRXPat->fLiteralText.length()); + } else { + // TODO here: add optimization to split case sensitive strings of length two + // into two single char ops, for efficiency. + appendOp(URX_STRING, fRXPat->fLiteralText.length()); + } + appendOp(URX_STRING_LEN, fLiteralChars.length()); + + // Add this string into the accumulated strings of the compiled pattern. + fRXPat->fLiteralText.append(fLiteralChars); + } + + fLiteralChars.remove(); +} + + +int32_t RegexCompile::buildOp(int32_t type, int32_t val) { + if (U_FAILURE(*fStatus)) { + return 0; + } + if (type < 0 || type > 255) { + UPRV_UNREACHABLE_EXIT; + } + if (val > 0x00ffffff) { + UPRV_UNREACHABLE_EXIT; + } + if (val < 0) { + if (!(type == URX_RESERVED_OP_N || type == URX_RESERVED_OP)) { + UPRV_UNREACHABLE_EXIT; + } + if (URX_TYPE(val) != 0xff) { + UPRV_UNREACHABLE_EXIT; + } + type = URX_RESERVED_OP_N; + } + return (type << 24) | val; +} + + +//------------------------------------------------------------------------------ +// +// appendOp() Append a new instruction onto the compiled pattern +// Includes error checking, limiting the size of the +// pattern to lengths that can be represented in the +// 24 bit operand field of an instruction. +// +//------------------------------------------------------------------------------ +void RegexCompile::appendOp(int32_t op) { + if (U_FAILURE(*fStatus)) { + return; + } + fRXPat->fCompiledPat->addElement(op, *fStatus); + if ((fRXPat->fCompiledPat->size() > 0x00fffff0) && U_SUCCESS(*fStatus)) { + error(U_REGEX_PATTERN_TOO_BIG); + } +} + +void RegexCompile::appendOp(int32_t type, int32_t val) { + appendOp(buildOp(type, val)); +} + + +//------------------------------------------------------------------------------ +// +// insertOp() Insert a slot for a new opcode into the already +// compiled pattern code. +// +// Fill the slot with a NOP. Our caller will replace it +// with what they really wanted. +// +//------------------------------------------------------------------------------ +void RegexCompile::insertOp(int32_t where) { + UVector64 *code = fRXPat->fCompiledPat; + U_ASSERT(where>0 && where < code->size()); + + int32_t nop = buildOp(URX_NOP, 0); + code->insertElementAt(nop, where, *fStatus); + + // Walk through the pattern, looking for any ops with targets that + // were moved down by the insert. Fix them. + int32_t loc; + for (loc=0; locsize(); loc++) { + int32_t op = (int32_t)code->elementAti(loc); + int32_t opType = URX_TYPE(op); + int32_t opValue = URX_VAL(op); + if ((opType == URX_JMP || + opType == URX_JMPX || + opType == URX_STATE_SAVE || + opType == URX_CTR_LOOP || + opType == URX_CTR_LOOP_NG || + opType == URX_JMP_SAV || + opType == URX_JMP_SAV_X || + opType == URX_RELOC_OPRND) && opValue > where) { + // Target location for this opcode is after the insertion point and + // needs to be incremented to adjust for the insertion. + opValue++; + op = buildOp(opType, opValue); + code->setElementAt(op, loc); + } + } + + // Now fix up the parentheses stack. All positive values in it are locations in + // the compiled pattern. (Negative values are frame boundaries, and don't need fixing.) + for (loc=0; locsize()); + if (x>where) { + x++; + fParenStack.setElementAt(x, loc); + } + } + + if (fMatchCloseParen > where) { + fMatchCloseParen++; + } + if (fMatchOpenParen > where) { + fMatchOpenParen++; + } +} + + +//------------------------------------------------------------------------------ +// +// allocateData() Allocate storage in the matcher's static data area. +// Return the index for the newly allocated data. +// The storage won't actually exist until we are running a match +// operation, but the storage indexes are inserted into various +// opcodes while compiling the pattern. +// +//------------------------------------------------------------------------------ +int32_t RegexCompile::allocateData(int32_t size) { + if (U_FAILURE(*fStatus)) { + return 0; + } + if (size <= 0 || size > 0x100 || fRXPat->fDataSize < 0) { + error(U_REGEX_INTERNAL_ERROR); + return 0; + } + int32_t dataIndex = fRXPat->fDataSize; + fRXPat->fDataSize += size; + if (fRXPat->fDataSize >= 0x00fffff0) { + error(U_REGEX_INTERNAL_ERROR); + } + return dataIndex; +} + + +//------------------------------------------------------------------------------ +// +// allocateStackData() Allocate space in the back-tracking stack frame. +// Return the index for the newly allocated data. +// The frame indexes are inserted into various +// opcodes while compiling the pattern, meaning that frame +// size must be restricted to the size that will fit +// as an operand (24 bits). +// +//------------------------------------------------------------------------------ +int32_t RegexCompile::allocateStackData(int32_t size) { + if (U_FAILURE(*fStatus)) { + return 0; + } + if (size <= 0 || size > 0x100 || fRXPat->fFrameSize < 0) { + error(U_REGEX_INTERNAL_ERROR); + return 0; + } + int32_t dataIndex = fRXPat->fFrameSize; + fRXPat->fFrameSize += size; + if (fRXPat->fFrameSize >= 0x00fffff0) { + error(U_REGEX_PATTERN_TOO_BIG); + } + return dataIndex; +} + + +//------------------------------------------------------------------------------ +// +// blockTopLoc() Find or create a location in the compiled pattern +// at the start of the operation or block that has +// just been compiled. Needed when a quantifier (* or +// whatever) appears, and we need to add an operation +// at the start of the thing being quantified. +// +// (Parenthesized Blocks) have a slot with a NOP that +// is reserved for this purpose. .* or similar don't +// and a slot needs to be added. +// +// parameter reserveLoc : true - ensure that there is space to add an opcode +// at the returned location. +// false - just return the address, +// do not reserve a location there. +// +//------------------------------------------------------------------------------ +int32_t RegexCompile::blockTopLoc(UBool reserveLoc) { + int32_t theLoc; + fixLiterals(true); // Emit code for any pending literals. + // If last item was a string, emit separate op for the its last char. + if (fRXPat->fCompiledPat->size() == fMatchCloseParen) + { + // The item just processed is a parenthesized block. + theLoc = fMatchOpenParen; // A slot is already reserved for us. + U_ASSERT(theLoc > 0); + U_ASSERT(URX_TYPE(((uint32_t)fRXPat->fCompiledPat->elementAti(theLoc))) == URX_NOP); + } + else { + // Item just compiled is a single thing, a ".", or a single char, a string or a set reference. + // No slot for STATE_SAVE was pre-reserved in the compiled code. + // We need to make space now. + theLoc = fRXPat->fCompiledPat->size()-1; + int32_t opAtTheLoc = (int32_t)fRXPat->fCompiledPat->elementAti(theLoc); + if (URX_TYPE(opAtTheLoc) == URX_STRING_LEN) { + // Strings take two opcode, we want the position of the first one. + // We can have a string at this point if a single character case-folded to two. + theLoc--; + } + if (reserveLoc) { + int32_t nop = buildOp(URX_NOP, 0); + fRXPat->fCompiledPat->insertElementAt(nop, theLoc, *fStatus); + } + } + return theLoc; +} + + + +//------------------------------------------------------------------------------ +// +// handleCloseParen When compiling a close paren, we need to go back +// and fix up any JMP or SAVE operations within the +// parenthesized block that need to target the end +// of the block. The locations of these are kept on +// the paretheses stack. +// +// This function is called both when encountering a +// real ) and at the end of the pattern. +// +//------------------------------------------------------------------------------ +void RegexCompile::handleCloseParen() { + int32_t patIdx; + int32_t patOp; + if (fParenStack.size() <= 0) { + error(U_REGEX_MISMATCHED_PAREN); + return; + } + + // Emit code for any pending literals. + fixLiterals(false); + + // Fixup any operations within the just-closed parenthesized group + // that need to reference the end of the (block). + // (The first one popped from the stack is an unused slot for + // alternation (OR) state save, but applying the fixup to it does no harm.) + for (;;) { + patIdx = fParenStack.popi(); + if (patIdx < 0) { + // value < 0 flags the start of the frame on the paren stack. + break; + } + U_ASSERT(patIdx>0 && patIdx <= fRXPat->fCompiledPat->size()); + patOp = (int32_t)fRXPat->fCompiledPat->elementAti(patIdx); + U_ASSERT(URX_VAL(patOp) == 0); // Branch target for JMP should not be set. + patOp |= fRXPat->fCompiledPat->size(); // Set it now. + fRXPat->fCompiledPat->setElementAt(patOp, patIdx); + fMatchOpenParen = patIdx; + } + + // At the close of any parenthesized block, restore the match mode flags to + // the value they had at the open paren. Saved value is + // at the top of the paren stack. + fModeFlags = fParenStack.popi(); + U_ASSERT(fModeFlags < 0); + + // DO any additional fixups, depending on the specific kind of + // parentesized grouping this is + + switch (patIdx) { + case plain: + case flags: + // No additional fixups required. + // (Grouping-only parentheses) + break; + case capturing: + // Capturing Parentheses. + // Insert a End Capture op into the pattern. + // The frame offset of the variables for this cg is obtained from the + // start capture op and put it into the end-capture op. + { + int32_t captureOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen+1); + U_ASSERT(URX_TYPE(captureOp) == URX_START_CAPTURE); + + int32_t frameVarLocation = URX_VAL(captureOp); + appendOp(URX_END_CAPTURE, frameVarLocation); + } + break; + case atomic: + // Atomic Parenthesis. + // Insert a LD_SP operation to restore the state stack to the position + // it was when the atomic parens were entered. + { + int32_t stoOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen+1); + U_ASSERT(URX_TYPE(stoOp) == URX_STO_SP); + int32_t stoLoc = URX_VAL(stoOp); + appendOp(URX_LD_SP, stoLoc); + } + break; + + case lookAhead: + { + int32_t startOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen-5); + U_ASSERT(URX_TYPE(startOp) == URX_LA_START); + int32_t dataLoc = URX_VAL(startOp); + appendOp(URX_LA_END, dataLoc); + } + break; + + case negLookAhead: + { + // See comment at doOpenLookAheadNeg + int32_t startOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen-1); + U_ASSERT(URX_TYPE(startOp) == URX_LA_START); + int32_t dataLoc = URX_VAL(startOp); + appendOp(URX_LA_END, dataLoc); + appendOp(URX_BACKTRACK, 0); + appendOp(URX_LA_END, dataLoc); + + // Patch the URX_SAVE near the top of the block. + // The destination of the SAVE is the final LA_END that was just added. + int32_t saveOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen); + U_ASSERT(URX_TYPE(saveOp) == URX_STATE_SAVE); + int32_t dest = fRXPat->fCompiledPat->size()-1; + saveOp = buildOp(URX_STATE_SAVE, dest); + fRXPat->fCompiledPat->setElementAt(saveOp, fMatchOpenParen); + } + break; + + case lookBehind: + { + // See comment at doOpenLookBehind. + + // Append the URX_LB_END and URX_LA_END to the compiled pattern. + int32_t startOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen-4); + U_ASSERT(URX_TYPE(startOp) == URX_LB_START); + int32_t dataLoc = URX_VAL(startOp); + appendOp(URX_LB_END, dataLoc); + appendOp(URX_LA_END, dataLoc); + + // Determine the min and max bounds for the length of the + // string that the pattern can match. + // An unbounded upper limit is an error. + int32_t patEnd = fRXPat->fCompiledPat->size() - 1; + int32_t minML = minMatchLength(fMatchOpenParen, patEnd); + int32_t maxML = maxMatchLength(fMatchOpenParen, patEnd); + if (URX_TYPE(maxML) != 0) { + error(U_REGEX_LOOK_BEHIND_LIMIT); + break; + } + if (maxML == INT32_MAX) { + error(U_REGEX_LOOK_BEHIND_LIMIT); + break; + } + if (minML == INT32_MAX) { + // This condition happens when no match is possible, such as with a + // [set] expression containing no elements. + // In principle, the generated code to evaluate the expression could be deleted, + // but it's probably not worth the complication. + minML = 0; + } + U_ASSERT(minML <= maxML); + + // Insert the min and max match len bounds into the URX_LB_CONT op that + // appears at the top of the look-behind block, at location fMatchOpenParen+1 + fRXPat->fCompiledPat->setElementAt(minML, fMatchOpenParen-2); + fRXPat->fCompiledPat->setElementAt(maxML, fMatchOpenParen-1); + + } + break; + + + + case lookBehindN: + { + // See comment at doOpenLookBehindNeg. + + // Append the URX_LBN_END to the compiled pattern. + int32_t startOp = (int32_t)fRXPat->fCompiledPat->elementAti(fMatchOpenParen-5); + U_ASSERT(URX_TYPE(startOp) == URX_LB_START); + int32_t dataLoc = URX_VAL(startOp); + appendOp(URX_LBN_END, dataLoc); + + // Determine the min and max bounds for the length of the + // string that the pattern can match. + // An unbounded upper limit is an error. + int32_t patEnd = fRXPat->fCompiledPat->size() - 1; + int32_t minML = minMatchLength(fMatchOpenParen, patEnd); + int32_t maxML = maxMatchLength(fMatchOpenParen, patEnd); + if (URX_TYPE(maxML) != 0) { + error(U_REGEX_LOOK_BEHIND_LIMIT); + break; + } + if (maxML == INT32_MAX) { + error(U_REGEX_LOOK_BEHIND_LIMIT); + break; + } + if (minML == INT32_MAX) { + // This condition happens when no match is possible, such as with a + // [set] expression containing no elements. + // In principle, the generated code to evaluate the expression could be deleted, + // but it's probably not worth the complication. + minML = 0; + } + + U_ASSERT(minML <= maxML); + + // Insert the min and max match len bounds into the URX_LB_CONT op that + // appears at the top of the look-behind block, at location fMatchOpenParen+1 + fRXPat->fCompiledPat->setElementAt(minML, fMatchOpenParen-3); + fRXPat->fCompiledPat->setElementAt(maxML, fMatchOpenParen-2); + + // Insert the pattern location to continue at after a successful match + // as the last operand of the URX_LBN_CONT + int32_t op = buildOp(URX_RELOC_OPRND, fRXPat->fCompiledPat->size()); + fRXPat->fCompiledPat->setElementAt(op, fMatchOpenParen-1); + } + break; + + + + default: + UPRV_UNREACHABLE_EXIT; + } + + // remember the next location in the compiled pattern. + // The compilation of Quantifiers will look at this to see whether its looping + // over a parenthesized block or a single item + fMatchCloseParen = fRXPat->fCompiledPat->size(); +} + + + +//------------------------------------------------------------------------------ +// +// compileSet Compile the pattern operations for a reference to a +// UnicodeSet. +// +//------------------------------------------------------------------------------ +void RegexCompile::compileSet(UnicodeSet *theSet) +{ + if (theSet == nullptr) { + return; + } + // Remove any strings from the set. + // There shouldn't be any, but just in case. + // (Case Closure can add them; if we had a simple case closure available that + // ignored strings, that would be better.) + theSet->removeAllStrings(); + int32_t setSize = theSet->size(); + + switch (setSize) { + case 0: + { + // Set of no elements. Always fails to match. + appendOp(URX_BACKTRACK, 0); + delete theSet; + } + break; + + case 1: + { + // The set contains only a single code point. Put it into + // the compiled pattern as a single char operation rather + // than a set, and discard the set itself. + literalChar(theSet->charAt(0)); + delete theSet; + } + break; + + default: + { + // The set contains two or more chars. (the normal case) + // Put it into the compiled pattern as a set. + theSet->freeze(); + int32_t setNumber = fRXPat->fSets->size(); + fRXPat->fSets->addElement(theSet, *fStatus); + if (U_SUCCESS(*fStatus)) { + appendOp(URX_SETREF, setNumber); + } else { + delete theSet; + } + } + } +} + + +//------------------------------------------------------------------------------ +// +// compileInterval Generate the code for a {min, max} style interval quantifier. +// Except for the specific opcodes used, the code is the same +// for all three types (greedy, non-greedy, possessive) of +// intervals. The opcodes are supplied as parameters. +// (There are two sets of opcodes - greedy & possessive use the +// same ones, while non-greedy has it's own.) +// +// The code for interval loops has this form: +// 0 CTR_INIT counter loc (in stack frame) +// 1 5 patt address of CTR_LOOP at bottom of block +// 2 min count +// 3 max count (-1 for unbounded) +// 4 ... block to be iterated over +// 5 CTR_LOOP +// +// In +//------------------------------------------------------------------------------ +void RegexCompile::compileInterval(int32_t InitOp, int32_t LoopOp) +{ + // The CTR_INIT op at the top of the block with the {n,m} quantifier takes + // four slots in the compiled code. Reserve them. + int32_t topOfBlock = blockTopLoc(true); + insertOp(topOfBlock); + insertOp(topOfBlock); + insertOp(topOfBlock); + + // The operands for the CTR_INIT opcode include the index in the matcher data + // of the counter. Allocate it now. There are two data items + // counterLoc --> Loop counter + // +1 --> Input index (for breaking non-progressing loops) + // (Only present if unbounded upper limit on loop) + int32_t dataSize = fIntervalUpper < 0 ? 2 : 1; + int32_t counterLoc = allocateStackData(dataSize); + + int32_t op = buildOp(InitOp, counterLoc); + fRXPat->fCompiledPat->setElementAt(op, topOfBlock); + + // The second operand of CTR_INIT is the location following the end of the loop. + // Must put in as a URX_RELOC_OPRND so that the value will be adjusted if the + // compilation of something later on causes the code to grow and the target + // position to move. + int32_t loopEnd = fRXPat->fCompiledPat->size(); + op = buildOp(URX_RELOC_OPRND, loopEnd); + fRXPat->fCompiledPat->setElementAt(op, topOfBlock+1); + + // Followed by the min and max counts. + fRXPat->fCompiledPat->setElementAt(fIntervalLow, topOfBlock+2); + fRXPat->fCompiledPat->setElementAt(fIntervalUpper, topOfBlock+3); + + // Append the CTR_LOOP op. The operand is the location of the CTR_INIT op. + // Goes at end of the block being looped over, so just append to the code so far. + appendOp(LoopOp, topOfBlock); + + if ((fIntervalLow & 0xff000000) != 0 || + (fIntervalUpper > 0 && (fIntervalUpper & 0xff000000) != 0)) { + error(U_REGEX_NUMBER_TOO_BIG); + } + + if (fIntervalLow > fIntervalUpper && fIntervalUpper != -1) { + error(U_REGEX_MAX_LT_MIN); + } +} + + + +UBool RegexCompile::compileInlineInterval() { + if (fIntervalUpper > 10 || fIntervalUpper < fIntervalLow) { + // Too big to inline. Fail, which will cause looping code to be generated. + // (Upper < Lower picks up unbounded upper and errors, both.) + return false; + } + + int32_t topOfBlock = blockTopLoc(false); + if (fIntervalUpper == 0) { + // Pathological case. Attempt no matches, as if the block doesn't exist. + // Discard the generated code for the block. + // If the block included parens, discard the info pertaining to them as well. + fRXPat->fCompiledPat->setSize(topOfBlock); + if (fMatchOpenParen >= topOfBlock) { + fMatchOpenParen = -1; + } + if (fMatchCloseParen >= topOfBlock) { + fMatchCloseParen = -1; + } + return true; + } + + if (topOfBlock != fRXPat->fCompiledPat->size()-1 && fIntervalUpper != 1) { + // The thing being repeated is not a single op, but some + // more complex block. Do it as a loop, not inlines. + // Note that things "repeated" a max of once are handled as inline, because + // the one copy of the code already generated is just fine. + return false; + } + + // Pick up the opcode that is to be repeated + // + int32_t op = (int32_t)fRXPat->fCompiledPat->elementAti(topOfBlock); + + // Compute the pattern location where the inline sequence + // will end, and set up the state save op that will be needed. + // + int32_t endOfSequenceLoc = fRXPat->fCompiledPat->size()-1 + + fIntervalUpper + (fIntervalUpper-fIntervalLow); + int32_t saveOp = buildOp(URX_STATE_SAVE, endOfSequenceLoc); + if (fIntervalLow == 0) { + insertOp(topOfBlock); + fRXPat->fCompiledPat->setElementAt(saveOp, topOfBlock); + } + + + + // Loop, emitting the op for the thing being repeated each time. + // Loop starts at 1 because one instance of the op already exists in the pattern, + // it was put there when it was originally encountered. + int32_t i; + for (i=1; i= fIntervalLow) { + appendOp(saveOp); + } + appendOp(op); + } + return true; +} + + + +//------------------------------------------------------------------------------ +// +// caseInsensitiveStart given a single code point from a pattern string, determine the +// set of characters that could potentially begin a case-insensitive +// match of a string beginning with that character, using full Unicode +// case insensitive matching. +// +// This is used in optimizing find(). +// +// closeOver(USET_CASE_INSENSITIVE) does most of what is needed, but +// misses cases like this: +// A string from the pattern begins with 'ss' (although all we know +// in this context is that it begins with 's') +// The pattern could match a string beginning with a German sharp-s +// +// To the ordinary case closure for a character c, we add all other +// characters cx where the case closure of cx includes a string form that begins +// with the original character c. +// +// This function could be made smarter. The full pattern string is available +// and it would be possible to verify that the extra characters being added +// to the starting set fully match, rather than having just a first-char of the +// folded form match. +// +//------------------------------------------------------------------------------ +void RegexCompile::findCaseInsensitiveStarters(UChar32 c, UnicodeSet *starterChars) { + +// Machine Generated below. +// It may need updating with new versions of Unicode. +// Intltest test RegexTest::TestCaseInsensitiveStarters will fail if an update is needed. +// The update tool is here: +// https://github.com/unicode-org/icu/tree/main/tools/unicode/c/genregexcasing + +// Machine Generated Data. Do not hand edit. + static const UChar32 RECaseFixCodePoints[] = { + 0x61, 0x66, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x77, 0x79, 0x2bc, + 0x3ac, 0x3ae, 0x3b1, 0x3b7, 0x3b9, 0x3c1, 0x3c5, 0x3c9, 0x3ce, 0x565, + 0x574, 0x57e, 0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07, + 0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27, 0x1f60, 0x1f61, + 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67, 0x1f70, 0x1f74, 0x1f7c, 0x110000}; + + static const int16_t RECaseFixStringOffsets[] = { + 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xd, 0xe, 0xf, 0x10, + 0x11, 0x12, 0x13, 0x17, 0x1b, 0x20, 0x21, 0x2a, 0x2e, 0x2f, + 0x30, 0x34, 0x35, 0x37, 0x39, 0x3b, 0x3d, 0x3f, 0x41, 0x43, + 0x45, 0x47, 0x49, 0x4b, 0x4d, 0x4f, 0x51, 0x53, 0x55, 0x57, + 0x59, 0x5b, 0x5d, 0x5f, 0x61, 0x63, 0x65, 0x66, 0x67, 0}; + + static const int16_t RECaseFixCounts[] = { + 0x1, 0x5, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x4, 0x4, 0x5, 0x1, 0x9, 0x4, 0x1, 0x1, + 0x4, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, + 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, + 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0}; + + static const char16_t RECaseFixData[] = { + 0x1e9a, 0xfb00, 0xfb01, 0xfb02, 0xfb03, 0xfb04, 0x1e96, 0x130, 0x1f0, 0xdf, + 0x1e9e, 0xfb05, 0xfb06, 0x1e97, 0x1e98, 0x1e99, 0x149, 0x1fb4, 0x1fc4, 0x1fb3, + 0x1fb6, 0x1fb7, 0x1fbc, 0x1fc3, 0x1fc6, 0x1fc7, 0x1fcc, 0x390, 0x1fd2, 0x1fd3, + 0x1fd6, 0x1fd7, 0x1fe4, 0x3b0, 0x1f50, 0x1f52, 0x1f54, 0x1f56, 0x1fe2, 0x1fe3, + 0x1fe6, 0x1fe7, 0x1ff3, 0x1ff6, 0x1ff7, 0x1ffc, 0x1ff4, 0x587, 0xfb13, 0xfb14, + 0xfb15, 0xfb17, 0xfb16, 0x1f80, 0x1f88, 0x1f81, 0x1f89, 0x1f82, 0x1f8a, 0x1f83, + 0x1f8b, 0x1f84, 0x1f8c, 0x1f85, 0x1f8d, 0x1f86, 0x1f8e, 0x1f87, 0x1f8f, 0x1f90, + 0x1f98, 0x1f91, 0x1f99, 0x1f92, 0x1f9a, 0x1f93, 0x1f9b, 0x1f94, 0x1f9c, 0x1f95, + 0x1f9d, 0x1f96, 0x1f9e, 0x1f97, 0x1f9f, 0x1fa0, 0x1fa8, 0x1fa1, 0x1fa9, 0x1fa2, + 0x1faa, 0x1fa3, 0x1fab, 0x1fa4, 0x1fac, 0x1fa5, 0x1fad, 0x1fa6, 0x1fae, 0x1fa7, + 0x1faf, 0x1fb2, 0x1fc2, 0x1ff2, 0}; + +// End of machine generated data. + + if (c < UCHAR_MIN_VALUE || c > UCHAR_MAX_VALUE) { + // This function should never be called with an invalid input character. + UPRV_UNREACHABLE_EXIT; + } else if (u_hasBinaryProperty(c, UCHAR_CASE_SENSITIVE)) { + UChar32 caseFoldedC = u_foldCase(c, U_FOLD_CASE_DEFAULT); + starterChars->set(caseFoldedC, caseFoldedC); + + int32_t i; + for (i=0; RECaseFixCodePoints[i]add(cpToAdd); + } + } + + starterChars->closeOver(USET_CASE_INSENSITIVE); + starterChars->removeAllStrings(); + } else { + // Not a cased character. Just return it alone. + starterChars->set(c, c); + } +} + + +// Increment with overflow check. +// val and delta will both be positive. + +static int32_t safeIncrement(int32_t val, int32_t delta) { + if (INT32_MAX - val > delta) { + return val + delta; + } else { + return INT32_MAX; + } +} + + +//------------------------------------------------------------------------------ +// +// matchStartType Determine how a match can start. +// Used to optimize find() operations. +// +// Operation is very similar to minMatchLength(). Walk the compiled +// pattern, keeping an on-going minimum-match-length. For any +// op where the min match coming in is zero, add that ops possible +// starting matches to the possible starts for the overall pattern. +// +//------------------------------------------------------------------------------ +void RegexCompile::matchStartType() { + if (U_FAILURE(*fStatus)) { + return; + } + + + int32_t loc; // Location in the pattern of the current op being processed. + int32_t op; // The op being processed + int32_t opType; // The opcode type of the op + int32_t currentLen = 0; // Minimum length of a match to this point (loc) in the pattern + int32_t numInitialStrings = 0; // Number of strings encountered that could match at start. + + UBool atStart = true; // True if no part of the pattern yet encountered + // could have advanced the position in a match. + // (Maximum match length so far == 0) + + // forwardedLength is a vector holding minimum-match-length values that + // are propagated forward in the pattern by JMP or STATE_SAVE operations. + // It must be one longer than the pattern being checked because some ops + // will jmp to a end-of-block+1 location from within a block, and we must + // count those when checking the block. + int32_t end = fRXPat->fCompiledPat->size(); + UVector32 forwardedLength(end+1, *fStatus); + forwardedLength.setSize(end+1); + for (loc=3; locfCompiledPat->elementAti(loc); + opType = URX_TYPE(op); + + // The loop is advancing linearly through the pattern. + // If the op we are now at was the destination of a branch in the pattern, + // and that path has a shorter minimum length than the current accumulated value, + // replace the current accumulated value. + if (forwardedLength.elementAti(loc) < currentLen) { + currentLen = forwardedLength.elementAti(loc); + U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); + } + + switch (opType) { + // Ops that don't change the total length matched + case URX_RESERVED_OP: + case URX_END: + case URX_FAIL: + case URX_STRING_LEN: + case URX_NOP: + case URX_START_CAPTURE: + case URX_END_CAPTURE: + case URX_BACKSLASH_B: + case URX_BACKSLASH_BU: + case URX_BACKSLASH_G: + case URX_BACKSLASH_Z: + case URX_DOLLAR: + case URX_DOLLAR_M: + case URX_DOLLAR_D: + case URX_DOLLAR_MD: + case URX_RELOC_OPRND: + case URX_STO_INP_LOC: + case URX_BACKREF: // BackRef. Must assume that it might be a zero length match + case URX_BACKREF_I: + + case URX_STO_SP: // Setup for atomic or possessive blocks. Doesn't change what can match. + case URX_LD_SP: + break; + + case URX_CARET: + if (atStart) { + fRXPat->fStartType = START_START; + } + break; + + case URX_CARET_M: + case URX_CARET_M_UNIX: + if (atStart) { + fRXPat->fStartType = START_LINE; + } + break; + + case URX_ONECHAR: + if (currentLen == 0) { + // This character could appear at the start of a match. + // Add it to the set of possible starting characters. + fRXPat->fInitialChars->add(URX_VAL(op)); + numInitialStrings += 2; + } + currentLen = safeIncrement(currentLen, 1); + atStart = false; + break; + + + case URX_SETREF: + if (currentLen == 0) { + int32_t sn = URX_VAL(op); + U_ASSERT(sn > 0 && sn < fRXPat->fSets->size()); + const UnicodeSet *s = (UnicodeSet *)fRXPat->fSets->elementAt(sn); + fRXPat->fInitialChars->addAll(*s); + numInitialStrings += 2; + } + currentLen = safeIncrement(currentLen, 1); + atStart = false; + break; + + case URX_LOOP_SR_I: + // [Set]*, like a SETREF, above, in what it can match, + // but may not match at all, so currentLen is not incremented. + if (currentLen == 0) { + int32_t sn = URX_VAL(op); + U_ASSERT(sn > 0 && sn < fRXPat->fSets->size()); + const UnicodeSet *s = (UnicodeSet *)fRXPat->fSets->elementAt(sn); + fRXPat->fInitialChars->addAll(*s); + numInitialStrings += 2; + } + atStart = false; + break; + + case URX_LOOP_DOT_I: + if (currentLen == 0) { + // .* at the start of a pattern. + // Any character can begin the match. + fRXPat->fInitialChars->clear(); + fRXPat->fInitialChars->complement(); + numInitialStrings += 2; + } + atStart = false; + break; + + + case URX_STATIC_SETREF: + if (currentLen == 0) { + int32_t sn = URX_VAL(op); + U_ASSERT(sn>0 && snfPropSets[sn]; + fRXPat->fInitialChars->addAll(s); + numInitialStrings += 2; + } + currentLen = safeIncrement(currentLen, 1); + atStart = false; + break; + + + + case URX_STAT_SETREF_N: + if (currentLen == 0) { + int32_t sn = URX_VAL(op); + UnicodeSet sc; + sc.addAll(RegexStaticSets::gStaticSets->fPropSets[sn]).complement(); + fRXPat->fInitialChars->addAll(sc); + numInitialStrings += 2; + } + currentLen = safeIncrement(currentLen, 1); + atStart = false; + break; + + + + case URX_BACKSLASH_D: + // Digit Char + if (currentLen == 0) { + UnicodeSet s; + s.applyIntPropertyValue(UCHAR_GENERAL_CATEGORY_MASK, U_GC_ND_MASK, *fStatus); + if (URX_VAL(op) != 0) { + s.complement(); + } + fRXPat->fInitialChars->addAll(s); + numInitialStrings += 2; + } + currentLen = safeIncrement(currentLen, 1); + atStart = false; + break; + + + case URX_BACKSLASH_H: + // Horiz white space + if (currentLen == 0) { + UnicodeSet s; + s.applyIntPropertyValue(UCHAR_GENERAL_CATEGORY_MASK, U_GC_ZS_MASK, *fStatus); + s.add((UChar32)9); // Tab + if (URX_VAL(op) != 0) { + s.complement(); + } + fRXPat->fInitialChars->addAll(s); + numInitialStrings += 2; + } + currentLen = safeIncrement(currentLen, 1); + atStart = false; + break; + + + case URX_BACKSLASH_R: // Any line ending sequence + case URX_BACKSLASH_V: // Any line ending code point, with optional negation + if (currentLen == 0) { + UnicodeSet s; + s.add((UChar32)0x0a, (UChar32)0x0d); // add range + s.add((UChar32)0x85); + s.add((UChar32)0x2028, (UChar32)0x2029); + if (URX_VAL(op) != 0) { + // Complement option applies to URX_BACKSLASH_V only. + s.complement(); + } + fRXPat->fInitialChars->addAll(s); + numInitialStrings += 2; + } + currentLen = safeIncrement(currentLen, 1); + atStart = false; + break; + + + + case URX_ONECHAR_I: + // Case Insensitive Single Character. + if (currentLen == 0) { + UChar32 c = URX_VAL(op); + if (u_hasBinaryProperty(c, UCHAR_CASE_SENSITIVE)) { + UnicodeSet starters(c, c); + starters.closeOver(USET_CASE_INSENSITIVE); + // findCaseInsensitiveStarters(c, &starters); + // For ONECHAR_I, no need to worry about text chars that expand on folding into strings. + // The expanded folding can't match the pattern. + fRXPat->fInitialChars->addAll(starters); + } else { + // Char has no case variants. Just add it as-is to the + // set of possible starting chars. + fRXPat->fInitialChars->add(c); + } + numInitialStrings += 2; + } + currentLen = safeIncrement(currentLen, 1); + atStart = false; + break; + + + case URX_BACKSLASH_X: // Grapheme Cluster. Minimum is 1, max unbounded. + case URX_DOTANY_ALL: // . matches one or two. + case URX_DOTANY: + case URX_DOTANY_UNIX: + if (currentLen == 0) { + // These constructs are all bad news when they appear at the start + // of a match. Any character can begin the match. + fRXPat->fInitialChars->clear(); + fRXPat->fInitialChars->complement(); + numInitialStrings += 2; + } + currentLen = safeIncrement(currentLen, 1); + atStart = false; + break; + + + case URX_JMPX: + loc++; // Except for extra operand on URX_JMPX, same as URX_JMP. + U_FALLTHROUGH; + case URX_JMP: + { + int32_t jmpDest = URX_VAL(op); + if (jmpDest < loc) { + // Loop of some kind. Can safely ignore, the worst that will happen + // is that we understate the true minimum length + currentLen = forwardedLength.elementAti(loc+1); + + } else { + // Forward jump. Propagate the current min length to the target loc of the jump. + U_ASSERT(jmpDest <= end+1); + if (forwardedLength.elementAti(jmpDest) > currentLen) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } + } + atStart = false; + break; + + case URX_JMP_SAV: + case URX_JMP_SAV_X: + // Combo of state save to the next loc, + jmp backwards. + // Net effect on min. length computation is nothing. + atStart = false; + break; + + case URX_BACKTRACK: + // Fails are kind of like a branch, except that the min length was + // propagated already, by the state save. + currentLen = forwardedLength.elementAti(loc+1); + atStart = false; + break; + + + case URX_STATE_SAVE: + { + // State Save, for forward jumps, propagate the current minimum. + // of the state save. + int32_t jmpDest = URX_VAL(op); + if (jmpDest > loc) { + if (currentLen < forwardedLength.elementAti(jmpDest)) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } + } + atStart = false; + break; + + + + + case URX_STRING: + { + loc++; + int32_t stringLenOp = (int32_t)fRXPat->fCompiledPat->elementAti(loc); + int32_t stringLen = URX_VAL(stringLenOp); + U_ASSERT(URX_TYPE(stringLenOp) == URX_STRING_LEN); + U_ASSERT(stringLenOp >= 2); + if (currentLen == 0) { + // Add the starting character of this string to the set of possible starting + // characters for this pattern. + int32_t stringStartIdx = URX_VAL(op); + UChar32 c = fRXPat->fLiteralText.char32At(stringStartIdx); + fRXPat->fInitialChars->add(c); + + // Remember this string. After the entire pattern has been checked, + // if nothing else is identified that can start a match, we'll use it. + numInitialStrings++; + fRXPat->fInitialStringIdx = stringStartIdx; + fRXPat->fInitialStringLen = stringLen; + } + + currentLen = safeIncrement(currentLen, stringLen); + atStart = false; + } + break; + + case URX_STRING_I: + { + // Case-insensitive string. Unlike exact-match strings, we won't + // attempt a string search for possible match positions. But we + // do update the set of possible starting characters. + loc++; + int32_t stringLenOp = (int32_t)fRXPat->fCompiledPat->elementAti(loc); + int32_t stringLen = URX_VAL(stringLenOp); + U_ASSERT(URX_TYPE(stringLenOp) == URX_STRING_LEN); + U_ASSERT(stringLenOp >= 2); + if (currentLen == 0) { + // Add the starting character of this string to the set of possible starting + // characters for this pattern. + int32_t stringStartIdx = URX_VAL(op); + UChar32 c = fRXPat->fLiteralText.char32At(stringStartIdx); + UnicodeSet s; + findCaseInsensitiveStarters(c, &s); + fRXPat->fInitialChars->addAll(s); + numInitialStrings += 2; // Matching on an initial string not possible. + } + currentLen = safeIncrement(currentLen, stringLen); + atStart = false; + } + break; + + case URX_CTR_INIT: + case URX_CTR_INIT_NG: + { + // Loop Init Ops. These don't change the min length, but they are 4 word ops + // so location must be updated accordingly. + // Loop Init Ops. + // If the min loop count == 0 + // move loc forwards to the end of the loop, skipping over the body. + // If the min count is > 0, + // continue normal processing of the body of the loop. + int32_t loopEndLoc = (int32_t)fRXPat->fCompiledPat->elementAti(loc+1); + loopEndLoc = URX_VAL(loopEndLoc); + int32_t minLoopCount = (int32_t)fRXPat->fCompiledPat->elementAti(loc+2); + if (minLoopCount == 0) { + // Min Loop Count of 0, treat like a forward branch and + // move the current minimum length up to the target + // (end of loop) location. + U_ASSERT(loopEndLoc <= end+1); + if (forwardedLength.elementAti(loopEndLoc) > currentLen) { + forwardedLength.setElementAt(currentLen, loopEndLoc); + } + } + loc+=3; // Skips over operands of CTR_INIT + } + atStart = false; + break; + + + case URX_CTR_LOOP: + case URX_CTR_LOOP_NG: + // Loop ops. + // The jump is conditional, backwards only. + atStart = false; + break; + + case URX_LOOP_C: + // More loop ops. These state-save to themselves. + // don't change the minimum match + atStart = false; + break; + + + case URX_LA_START: + case URX_LB_START: + { + // Look-around. Scan forward until the matching look-ahead end, + // without processing the look-around block. This is overly pessimistic. + + // Keep track of the nesting depth of look-around blocks. Boilerplate code for + // lookahead contains two LA_END instructions, so count goes up by two + // for each LA_START. + int32_t depth = (opType == URX_LA_START? 2: 1); + for (;;) { + loc++; + op = (int32_t)fRXPat->fCompiledPat->elementAti(loc); + if (URX_TYPE(op) == URX_LA_START) { + depth+=2; + } + if (URX_TYPE(op) == URX_LB_START) { + depth++; + } + if (URX_TYPE(op) == URX_LA_END || URX_TYPE(op)==URX_LBN_END) { + depth--; + if (depth == 0) { + break; + } + } + if (URX_TYPE(op) == URX_STATE_SAVE) { + // Need this because neg lookahead blocks will FAIL to outside + // of the block. + int32_t jmpDest = URX_VAL(op); + if (jmpDest > loc) { + if (currentLen < forwardedLength.elementAti(jmpDest)) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } + } + U_ASSERT(loc <= end); + } + } + break; + + case URX_LA_END: + case URX_LB_CONT: + case URX_LB_END: + case URX_LBN_CONT: + case URX_LBN_END: + UPRV_UNREACHABLE_EXIT; // Shouldn't get here. These ops should be + // consumed by the scan in URX_LA_START and LB_START + default: + UPRV_UNREACHABLE_EXIT; + } + + } + + + // We have finished walking through the ops. Check whether some forward jump + // propagated a shorter length to location end+1. + if (forwardedLength.elementAti(end+1) < currentLen) { + currentLen = forwardedLength.elementAti(end+1); + } + + + fRXPat->fInitialChars8->init(fRXPat->fInitialChars); + + + // Sort out what we should check for when looking for candidate match start positions. + // In order of preference, + // 1. Start of input text buffer. + // 2. A literal string. + // 3. Start of line in multi-line mode. + // 4. A single literal character. + // 5. A character from a set of characters. + // + if (fRXPat->fStartType == START_START) { + // Match only at the start of an input text string. + // start type is already set. We're done. + } else if (numInitialStrings == 1 && fRXPat->fMinMatchLen > 0) { + // Match beginning only with a literal string. + UChar32 c = fRXPat->fLiteralText.char32At(fRXPat->fInitialStringIdx); + U_ASSERT(fRXPat->fInitialChars->contains(c)); + fRXPat->fStartType = START_STRING; + fRXPat->fInitialChar = c; + } else if (fRXPat->fStartType == START_LINE) { + // Match at start of line in Multi-Line mode. + // Nothing to do here; everything is already set. + } else if (fRXPat->fMinMatchLen == 0) { + // Zero length match possible. We could start anywhere. + fRXPat->fStartType = START_NO_INFO; + } else if (fRXPat->fInitialChars->size() == 1) { + // All matches begin with the same char. + fRXPat->fStartType = START_CHAR; + fRXPat->fInitialChar = fRXPat->fInitialChars->charAt(0); + U_ASSERT(fRXPat->fInitialChar != (UChar32)-1); + } else if (fRXPat->fInitialChars->contains((UChar32)0, (UChar32)0x10ffff) == false && + fRXPat->fMinMatchLen > 0) { + // Matches start with a set of character smaller than the set of all chars. + fRXPat->fStartType = START_SET; + } else { + // Matches can start with anything + fRXPat->fStartType = START_NO_INFO; + } + + return; +} + + + +//------------------------------------------------------------------------------ +// +// minMatchLength Calculate the length of the shortest string that could +// match the specified pattern. +// Length is in 16 bit code units, not code points. +// +// The calculated length may not be exact. The returned +// value may be shorter than the actual minimum; it must +// never be longer. +// +// start and end are the range of p-code operations to be +// examined. The endpoints are included in the range. +// +//------------------------------------------------------------------------------ +int32_t RegexCompile::minMatchLength(int32_t start, int32_t end) { + if (U_FAILURE(*fStatus)) { + return 0; + } + + U_ASSERT(start <= end); + U_ASSERT(end < fRXPat->fCompiledPat->size()); + + + int32_t loc; + int32_t op; + int32_t opType; + int32_t currentLen = 0; + + + // forwardedLength is a vector holding minimum-match-length values that + // are propagated forward in the pattern by JMP or STATE_SAVE operations. + // It must be one longer than the pattern being checked because some ops + // will jmp to a end-of-block+1 location from within a block, and we must + // count those when checking the block. + UVector32 forwardedLength(end+2, *fStatus); + forwardedLength.setSize(end+2); + for (loc=start; loc<=end+1; loc++) { + forwardedLength.setElementAt(INT32_MAX, loc); + } + + for (loc = start; loc<=end; loc++) { + op = (int32_t)fRXPat->fCompiledPat->elementAti(loc); + opType = URX_TYPE(op); + + // The loop is advancing linearly through the pattern. + // If the op we are now at was the destination of a branch in the pattern, + // and that path has a shorter minimum length than the current accumulated value, + // replace the current accumulated value. + // U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); // MinLength == INT32_MAX for some + // no-match-possible cases. + if (forwardedLength.elementAti(loc) < currentLen) { + currentLen = forwardedLength.elementAti(loc); + U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); + } + + switch (opType) { + // Ops that don't change the total length matched + case URX_RESERVED_OP: + case URX_END: + case URX_STRING_LEN: + case URX_NOP: + case URX_START_CAPTURE: + case URX_END_CAPTURE: + case URX_BACKSLASH_B: + case URX_BACKSLASH_BU: + case URX_BACKSLASH_G: + case URX_BACKSLASH_Z: + case URX_CARET: + case URX_DOLLAR: + case URX_DOLLAR_M: + case URX_DOLLAR_D: + case URX_DOLLAR_MD: + case URX_RELOC_OPRND: + case URX_STO_INP_LOC: + case URX_CARET_M: + case URX_CARET_M_UNIX: + case URX_BACKREF: // BackRef. Must assume that it might be a zero length match + case URX_BACKREF_I: + + case URX_STO_SP: // Setup for atomic or possessive blocks. Doesn't change what can match. + case URX_LD_SP: + + case URX_JMP_SAV: + case URX_JMP_SAV_X: + break; + + + // Ops that match a minimum of one character (one or two 16 bit code units.) + // + case URX_ONECHAR: + case URX_STATIC_SETREF: + case URX_STAT_SETREF_N: + case URX_SETREF: + case URX_BACKSLASH_D: + case URX_BACKSLASH_H: + case URX_BACKSLASH_R: + case URX_BACKSLASH_V: + case URX_ONECHAR_I: + case URX_BACKSLASH_X: // Grapheme Cluster. Minimum is 1, max unbounded. + case URX_DOTANY_ALL: // . matches one or two. + case URX_DOTANY: + case URX_DOTANY_UNIX: + currentLen = safeIncrement(currentLen, 1); + break; + + + case URX_JMPX: + loc++; // URX_JMPX has an extra operand, ignored here, + // otherwise processed identically to URX_JMP. + U_FALLTHROUGH; + case URX_JMP: + { + int32_t jmpDest = URX_VAL(op); + if (jmpDest < loc) { + // Loop of some kind. Can safely ignore, the worst that will happen + // is that we understate the true minimum length + currentLen = forwardedLength.elementAti(loc+1); + } else { + // Forward jump. Propagate the current min length to the target loc of the jump. + U_ASSERT(jmpDest <= end+1); + if (forwardedLength.elementAti(jmpDest) > currentLen) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } + } + break; + + case URX_BACKTRACK: + { + // Back-tracks are kind of like a branch, except that the min length was + // propagated already, by the state save. + currentLen = forwardedLength.elementAti(loc+1); + } + break; + + + case URX_STATE_SAVE: + { + // State Save, for forward jumps, propagate the current minimum. + // of the state save. + int32_t jmpDest = URX_VAL(op); + if (jmpDest > loc) { + if (currentLen < forwardedLength.elementAti(jmpDest)) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } + } + break; + + + case URX_STRING: + { + loc++; + int32_t stringLenOp = (int32_t)fRXPat->fCompiledPat->elementAti(loc); + currentLen = safeIncrement(currentLen, URX_VAL(stringLenOp)); + } + break; + + + case URX_STRING_I: + { + loc++; + // TODO: with full case folding, matching input text may be shorter than + // the string we have here. More smarts could put some bounds on it. + // Assume a min length of one for now. A min length of zero causes + // optimization failures for a pattern like "string"+ + // currentLen += URX_VAL(stringLenOp); + currentLen = safeIncrement(currentLen, 1); + } + break; + + case URX_CTR_INIT: + case URX_CTR_INIT_NG: + { + // Loop Init Ops. + // If the min loop count == 0 + // move loc forwards to the end of the loop, skipping over the body. + // If the min count is > 0, + // continue normal processing of the body of the loop. + int32_t loopEndLoc = (int32_t)fRXPat->fCompiledPat->elementAti(loc+1); + loopEndLoc = URX_VAL(loopEndLoc); + int32_t minLoopCount = (int32_t)fRXPat->fCompiledPat->elementAti(loc+2); + if (minLoopCount == 0) { + loc = loopEndLoc; + } else { + loc+=3; // Skips over operands of CTR_INIT + } + } + break; + + + case URX_CTR_LOOP: + case URX_CTR_LOOP_NG: + // Loop ops. + // The jump is conditional, backwards only. + break; + + case URX_LOOP_SR_I: + case URX_LOOP_DOT_I: + case URX_LOOP_C: + // More loop ops. These state-save to themselves. + // don't change the minimum match - could match nothing at all. + break; + + + case URX_LA_START: + case URX_LB_START: + { + // Look-around. Scan forward until the matching look-ahead end, + // without processing the look-around block. This is overly pessimistic for look-ahead, + // it assumes that the look-ahead match might be zero-length. + // TODO: Positive lookahead could recursively do the block, then continue + // with the longer of the block or the value coming in. Ticket 6060 + int32_t depth = (opType == URX_LA_START? 2: 1); + for (;;) { + loc++; + op = (int32_t)fRXPat->fCompiledPat->elementAti(loc); + if (URX_TYPE(op) == URX_LA_START) { + // The boilerplate for look-ahead includes two LA_END instructions, + // Depth will be decremented by each one when it is seen. + depth += 2; + } + if (URX_TYPE(op) == URX_LB_START) { + depth++; + } + if (URX_TYPE(op) == URX_LA_END) { + depth--; + if (depth == 0) { + break; + } + } + if (URX_TYPE(op)==URX_LBN_END) { + depth--; + if (depth == 0) { + break; + } + } + if (URX_TYPE(op) == URX_STATE_SAVE) { + // Need this because neg lookahead blocks will FAIL to outside + // of the block. + int32_t jmpDest = URX_VAL(op); + if (jmpDest > loc) { + if (currentLen < forwardedLength.elementAti(jmpDest)) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } + } + U_ASSERT(loc <= end); + } + } + break; + + case URX_LA_END: + case URX_LB_CONT: + case URX_LB_END: + case URX_LBN_CONT: + case URX_LBN_END: + // Only come here if the matching URX_LA_START or URX_LB_START was not in the + // range being sized, which happens when measuring size of look-behind blocks. + break; + + default: + UPRV_UNREACHABLE_EXIT; + } + + } + + // We have finished walking through the ops. Check whether some forward jump + // propagated a shorter length to location end+1. + if (forwardedLength.elementAti(end+1) < currentLen) { + currentLen = forwardedLength.elementAti(end+1); + U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); + } + + return currentLen; +} + +//------------------------------------------------------------------------------ +// +// maxMatchLength Calculate the length of the longest string that could +// match the specified pattern. +// Length is in 16 bit code units, not code points. +// +// The calculated length may not be exact. The returned +// value may be longer than the actual maximum; it must +// never be shorter. +// +// start, end: the range of the pattern to check. +// end is inclusive. +// +//------------------------------------------------------------------------------ +int32_t RegexCompile::maxMatchLength(int32_t start, int32_t end) { + if (U_FAILURE(*fStatus)) { + return 0; + } + U_ASSERT(start <= end); + U_ASSERT(end < fRXPat->fCompiledPat->size()); + + int32_t loc; + int32_t op; + int32_t opType; + int32_t currentLen = 0; + UVector32 forwardedLength(end+1, *fStatus); + forwardedLength.setSize(end+1); + + for (loc=start; loc<=end; loc++) { + forwardedLength.setElementAt(0, loc); + } + + for (loc = start; loc<=end; loc++) { + op = (int32_t)fRXPat->fCompiledPat->elementAti(loc); + opType = URX_TYPE(op); + + // The loop is advancing linearly through the pattern. + // If the op we are now at was the destination of a branch in the pattern, + // and that path has a longer maximum length than the current accumulated value, + // replace the current accumulated value. + if (forwardedLength.elementAti(loc) > currentLen) { + currentLen = forwardedLength.elementAti(loc); + } + + switch (opType) { + // Ops that don't change the total length matched + case URX_RESERVED_OP: + case URX_END: + case URX_STRING_LEN: + case URX_NOP: + case URX_START_CAPTURE: + case URX_END_CAPTURE: + case URX_BACKSLASH_B: + case URX_BACKSLASH_BU: + case URX_BACKSLASH_G: + case URX_BACKSLASH_Z: + case URX_CARET: + case URX_DOLLAR: + case URX_DOLLAR_M: + case URX_DOLLAR_D: + case URX_DOLLAR_MD: + case URX_RELOC_OPRND: + case URX_STO_INP_LOC: + case URX_CARET_M: + case URX_CARET_M_UNIX: + + case URX_STO_SP: // Setup for atomic or possessive blocks. Doesn't change what can match. + case URX_LD_SP: + + case URX_LB_END: + case URX_LB_CONT: + case URX_LBN_CONT: + case URX_LBN_END: + break; + + + // Ops that increase that cause an unbounded increase in the length + // of a matched string, or that increase it a hard to characterize way. + // Call the max length unbounded, and stop further checking. + case URX_BACKREF: // BackRef. Must assume that it might be a zero length match + case URX_BACKREF_I: + case URX_BACKSLASH_X: // Grapheme Cluster. Minimum is 1, max unbounded. + currentLen = INT32_MAX; + break; + + + // Ops that match a max of one character (possibly two 16 bit code units.) + // + case URX_STATIC_SETREF: + case URX_STAT_SETREF_N: + case URX_SETREF: + case URX_BACKSLASH_D: + case URX_BACKSLASH_H: + case URX_BACKSLASH_R: + case URX_BACKSLASH_V: + case URX_ONECHAR_I: + case URX_DOTANY_ALL: + case URX_DOTANY: + case URX_DOTANY_UNIX: + currentLen = safeIncrement(currentLen, 2); + break; + + // Single literal character. Increase current max length by one or two, + // depending on whether the char is in the supplementary range. + case URX_ONECHAR: + currentLen = safeIncrement(currentLen, 1); + if (URX_VAL(op) > 0x10000) { + currentLen = safeIncrement(currentLen, 1); + } + break; + + // Jumps. + // + case URX_JMP: + case URX_JMPX: + case URX_JMP_SAV: + case URX_JMP_SAV_X: + { + int32_t jmpDest = URX_VAL(op); + if (jmpDest < loc) { + // Loop of some kind. Max match length is unbounded. + currentLen = INT32_MAX; + } else { + // Forward jump. Propagate the current min length to the target loc of the jump. + if (forwardedLength.elementAti(jmpDest) < currentLen) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + currentLen = 0; + } + } + break; + + case URX_BACKTRACK: + // back-tracks are kind of like a branch, except that the max length was + // propagated already, by the state save. + currentLen = forwardedLength.elementAti(loc+1); + break; + + + case URX_STATE_SAVE: + { + // State Save, for forward jumps, propagate the current minimum. + // of the state save. + // For backwards jumps, they create a loop, maximum + // match length is unbounded. + int32_t jmpDest = URX_VAL(op); + if (jmpDest > loc) { + if (currentLen > forwardedLength.elementAti(jmpDest)) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } else { + currentLen = INT32_MAX; + } + } + break; + + + + + case URX_STRING: + { + loc++; + int32_t stringLenOp = (int32_t)fRXPat->fCompiledPat->elementAti(loc); + currentLen = safeIncrement(currentLen, URX_VAL(stringLenOp)); + break; + } + + case URX_STRING_I: + // TODO: This code assumes that any user string that matches will be no longer + // than our compiled string, with case insensitive matching. + // Our compiled string has been case-folded already. + // + // Any matching user string will have no more code points than our + // compiled (folded) string. Folding may add code points, but + // not remove them. + // + // There is a potential problem if a supplemental code point + // case-folds to a BMP code point. In this case our compiled string + // could be shorter (in code units) than a matching user string. + // + // At this time (Unicode 6.1) there are no such characters, and this case + // is not being handled. A test, intltest regex/Bug9283, will fail if + // any problematic characters are added to Unicode. + // + // If this happens, we can make a set of the BMP chars that the + // troublesome supplementals fold to, scan our string, and bump the + // currentLen one extra for each that is found. + // + { + loc++; + int32_t stringLenOp = (int32_t)fRXPat->fCompiledPat->elementAti(loc); + currentLen = safeIncrement(currentLen, URX_VAL(stringLenOp)); + } + break; + + case URX_CTR_INIT: + case URX_CTR_INIT_NG: + // For Loops, recursively call this function on the pattern for the loop body, + // then multiply the result by the maximum loop count. + { + int32_t loopEndLoc = URX_VAL(fRXPat->fCompiledPat->elementAti(loc+1)); + if (loopEndLoc == loc+4) { + // Loop has an empty body. No affect on max match length. + // Continue processing with code after the loop end. + loc = loopEndLoc; + break; + } + + int32_t maxLoopCount = static_cast(fRXPat->fCompiledPat->elementAti(loc+3)); + if (maxLoopCount == -1) { + // Unbounded Loop. No upper bound on match length. + currentLen = INT32_MAX; + break; + } + + U_ASSERT(loopEndLoc >= loc+4); + int64_t blockLen = maxMatchLength(loc+4, loopEndLoc-1); // Recursive call. + int64_t updatedLen = (int64_t)currentLen + blockLen * maxLoopCount; + if (updatedLen >= INT32_MAX) { + currentLen = INT32_MAX; + break; + } + currentLen = (int32_t)updatedLen; + loc = loopEndLoc; + break; + } + + case URX_CTR_LOOP: + case URX_CTR_LOOP_NG: + // These opcodes will be skipped over by code for URX_CTR_INIT. + // We shouldn't encounter them here. + UPRV_UNREACHABLE_EXIT; + + case URX_LOOP_SR_I: + case URX_LOOP_DOT_I: + case URX_LOOP_C: + // For anything to do with loops, make the match length unbounded. + currentLen = INT32_MAX; + break; + + + + case URX_LA_START: + case URX_LA_END: + // Look-ahead. Just ignore, treat the look-ahead block as if + // it were normal pattern. Gives a too-long match length, + // but good enough for now. + break; + + // End of look-ahead ops should always be consumed by the processing at + // the URX_LA_START op. + // UPRV_UNREACHABLE_EXIT; + + case URX_LB_START: + { + // Look-behind. Scan forward until the matching look-around end, + // without processing the look-behind block. + int32_t dataLoc = URX_VAL(op); + for (loc = loc + 1; loc <= end; ++loc) { + op = (int32_t)fRXPat->fCompiledPat->elementAti(loc); + int32_t opType = URX_TYPE(op); + if ((opType == URX_LA_END || opType == URX_LBN_END) && (URX_VAL(op) == dataLoc)) { + break; + } + } + U_ASSERT(loc <= end); + } + break; + + default: + UPRV_UNREACHABLE_EXIT; + } + + + if (currentLen == INT32_MAX) { + // The maximum length is unbounded. + // Stop further processing of the pattern. + break; + } + + } + return currentLen; + +} + + +//------------------------------------------------------------------------------ +// +// stripNOPs Remove any NOP operations from the compiled pattern code. +// Extra NOPs are inserted for some constructs during the initial +// code generation to provide locations that may be patched later. +// Many end up unneeded, and are removed by this function. +// +// In order to minimize the number of passes through the pattern, +// back-reference fixup is also performed here (adjusting +// back-reference operands to point to the correct frame offsets). +// +//------------------------------------------------------------------------------ +void RegexCompile::stripNOPs() { + + if (U_FAILURE(*fStatus)) { + return; + } + + int32_t end = fRXPat->fCompiledPat->size(); + UVector32 deltas(end, *fStatus); + + // Make a first pass over the code, computing the amount that things + // will be offset at each location in the original code. + int32_t loc; + int32_t d = 0; + for (loc=0; locfCompiledPat->elementAti(loc); + if (URX_TYPE(op) == URX_NOP) { + d++; + } + } + + UnicodeString caseStringBuffer; + + // Make a second pass over the code, removing the NOPs by moving following + // code up, and patching operands that refer to code locations that + // are being moved. The array of offsets from the first step is used + // to compute the new operand values. + int32_t src; + int32_t dst = 0; + for (src=0; srcfCompiledPat->elementAti(src); + int32_t opType = URX_TYPE(op); + switch (opType) { + case URX_NOP: + break; + + case URX_STATE_SAVE: + case URX_JMP: + case URX_CTR_LOOP: + case URX_CTR_LOOP_NG: + case URX_RELOC_OPRND: + case URX_JMPX: + case URX_JMP_SAV: + case URX_JMP_SAV_X: + // These are instructions with operands that refer to code locations. + { + int32_t operandAddress = URX_VAL(op); + U_ASSERT(operandAddress>=0 && operandAddressfCompiledPat->setElementAt(op, dst); + dst++; + break; + } + + case URX_BACKREF: + case URX_BACKREF_I: + { + int32_t where = URX_VAL(op); + if (where > fRXPat->fGroupMap->size()) { + error(U_REGEX_INVALID_BACK_REF); + break; + } + where = fRXPat->fGroupMap->elementAti(where-1); + op = buildOp(opType, where); + fRXPat->fCompiledPat->setElementAt(op, dst); + dst++; + + fRXPat->fNeedsAltInput = true; + break; + } + case URX_RESERVED_OP: + case URX_RESERVED_OP_N: + case URX_BACKTRACK: + case URX_END: + case URX_ONECHAR: + case URX_STRING: + case URX_STRING_LEN: + case URX_START_CAPTURE: + case URX_END_CAPTURE: + case URX_STATIC_SETREF: + case URX_STAT_SETREF_N: + case URX_SETREF: + case URX_DOTANY: + case URX_FAIL: + case URX_BACKSLASH_B: + case URX_BACKSLASH_BU: + case URX_BACKSLASH_G: + case URX_BACKSLASH_X: + case URX_BACKSLASH_Z: + case URX_DOTANY_ALL: + case URX_BACKSLASH_D: + case URX_CARET: + case URX_DOLLAR: + case URX_CTR_INIT: + case URX_CTR_INIT_NG: + case URX_DOTANY_UNIX: + case URX_STO_SP: + case URX_LD_SP: + case URX_STO_INP_LOC: + case URX_LA_START: + case URX_LA_END: + case URX_ONECHAR_I: + case URX_STRING_I: + case URX_DOLLAR_M: + case URX_CARET_M: + case URX_CARET_M_UNIX: + case URX_LB_START: + case URX_LB_CONT: + case URX_LB_END: + case URX_LBN_CONT: + case URX_LBN_END: + case URX_LOOP_SR_I: + case URX_LOOP_DOT_I: + case URX_LOOP_C: + case URX_DOLLAR_D: + case URX_DOLLAR_MD: + case URX_BACKSLASH_H: + case URX_BACKSLASH_R: + case URX_BACKSLASH_V: + // These instructions are unaltered by the relocation. + fRXPat->fCompiledPat->setElementAt(op, dst); + dst++; + break; + + default: + // Some op is unaccounted for. + UPRV_UNREACHABLE_EXIT; + } + } + + fRXPat->fCompiledPat->setSize(dst); +} + + + + +//------------------------------------------------------------------------------ +// +// Error Report a rule parse error. +// Only report it if no previous error has been recorded. +// +//------------------------------------------------------------------------------ +void RegexCompile::error(UErrorCode e) { + if (U_SUCCESS(*fStatus) || e == U_MEMORY_ALLOCATION_ERROR) { + *fStatus = e; + // Hmm. fParseErr (UParseError) line & offset fields are int32_t in public + // API (see common/unicode/parseerr.h), while fLineNum and fCharNum are + // int64_t. If the values of the latter are out of range for the former, + // set them to the appropriate "field not supported" values. + if (fLineNum > 0x7FFFFFFF) { + fParseErr->line = 0; + fParseErr->offset = -1; + } else if (fCharNum > 0x7FFFFFFF) { + fParseErr->line = (int32_t)fLineNum; + fParseErr->offset = -1; + } else { + fParseErr->line = (int32_t)fLineNum; + fParseErr->offset = (int32_t)fCharNum; + } + + UErrorCode status = U_ZERO_ERROR; // throwaway status for extracting context + + // Fill in the context. + // Note: extractBetween() pins supplied indices to the string bounds. + uprv_memset(fParseErr->preContext, 0, sizeof(fParseErr->preContext)); + uprv_memset(fParseErr->postContext, 0, sizeof(fParseErr->postContext)); + utext_extract(fRXPat->fPattern, fScanIndex-U_PARSE_CONTEXT_LEN+1, fScanIndex, fParseErr->preContext, U_PARSE_CONTEXT_LEN, &status); + utext_extract(fRXPat->fPattern, fScanIndex, fScanIndex+U_PARSE_CONTEXT_LEN-1, fParseErr->postContext, U_PARSE_CONTEXT_LEN, &status); + } +} + + +// +// Assorted Unicode character constants. +// Numeric because there is no portable way to enter them as literals. +// (Think EBCDIC). +// +static const char16_t chCR = 0x0d; // New lines, for terminating comments. +static const char16_t chLF = 0x0a; // Line Feed +static const char16_t chPound = 0x23; // '#', introduces a comment. +static const char16_t chDigit0 = 0x30; // '0' +static const char16_t chDigit7 = 0x37; // '9' +static const char16_t chColon = 0x3A; // ':' +static const char16_t chE = 0x45; // 'E' +static const char16_t chQ = 0x51; // 'Q' +//static const char16_t chN = 0x4E; // 'N' +static const char16_t chP = 0x50; // 'P' +static const char16_t chBackSlash = 0x5c; // '\' introduces a char escape +//static const char16_t chLBracket = 0x5b; // '[' +static const char16_t chRBracket = 0x5d; // ']' +static const char16_t chUp = 0x5e; // '^' +static const char16_t chLowerP = 0x70; +static const char16_t chLBrace = 0x7b; // '{' +static const char16_t chRBrace = 0x7d; // '}' +static const char16_t chNEL = 0x85; // NEL newline variant +static const char16_t chLS = 0x2028; // Unicode Line Separator + + +//------------------------------------------------------------------------------ +// +// nextCharLL Low Level Next Char from the regex pattern. +// Get a char from the string, keep track of input position +// for error reporting. +// +//------------------------------------------------------------------------------ +UChar32 RegexCompile::nextCharLL() { + UChar32 ch; + + if (fPeekChar != -1) { + ch = fPeekChar; + fPeekChar = -1; + return ch; + } + + // assume we're already in the right place + ch = UTEXT_NEXT32(fRXPat->fPattern); + if (ch == U_SENTINEL) { + return ch; + } + + if (ch == chCR || + ch == chNEL || + ch == chLS || + (ch == chLF && fLastChar != chCR)) { + // Character is starting a new line. Bump up the line number, and + // reset the column to 0. + fLineNum++; + fCharNum=0; + } + else { + // Character is not starting a new line. Except in the case of a + // LF following a CR, increment the column position. + if (ch != chLF) { + fCharNum++; + } + } + fLastChar = ch; + return ch; +} + +//------------------------------------------------------------------------------ +// +// peekCharLL Low Level Character Scanning, sneak a peek at the next +// character without actually getting it. +// +//------------------------------------------------------------------------------ +UChar32 RegexCompile::peekCharLL() { + if (fPeekChar == -1) { + fPeekChar = nextCharLL(); + } + return fPeekChar; +} + + +//------------------------------------------------------------------------------ +// +// nextChar for pattern scanning. At this level, we handle stripping +// out comments and processing some backslash character escapes. +// The rest of the pattern grammar is handled at the next level up. +// +//------------------------------------------------------------------------------ +void RegexCompile::nextChar(RegexPatternChar &c) { + tailRecursion: + fScanIndex = UTEXT_GETNATIVEINDEX(fRXPat->fPattern); + c.fChar = nextCharLL(); + c.fQuoted = false; + + if (fQuoteMode) { + c.fQuoted = true; + if ((c.fChar==chBackSlash && peekCharLL()==chE && ((fModeFlags & UREGEX_LITERAL) == 0)) || + c.fChar == (UChar32)-1) { + fQuoteMode = false; // Exit quote mode, + nextCharLL(); // discard the E + // nextChar(c); // recurse to get the real next char + goto tailRecursion; // Note: fuzz testing produced testcases that + // resulted in stack overflow here. + } + } + else if (fInBackslashQuote) { + // The current character immediately follows a '\' + // Don't check for any further escapes, just return it as-is. + // Don't set c.fQuoted, because that would prevent the state machine from + // dispatching on the character. + fInBackslashQuote = false; + } + else + { + // We are not in a \Q quoted region \E of the source. + // + if (fModeFlags & UREGEX_COMMENTS) { + // + // We are in free-spacing and comments mode. + // Scan through any white space and comments, until we + // reach a significant character or the end of input. + for (;;) { + if (c.fChar == (UChar32)-1) { + break; // End of Input + } + if (c.fChar == chPound && fEOLComments) { + // Start of a comment. Consume the rest of it, until EOF or a new line + for (;;) { + c.fChar = nextCharLL(); + if (c.fChar == (UChar32)-1 || // EOF + c.fChar == chCR || + c.fChar == chLF || + c.fChar == chNEL || + c.fChar == chLS) { + break; + } + } + } + // TODO: check what Java & Perl do with non-ASCII white spaces. Ticket 6061. + if (PatternProps::isWhiteSpace(c.fChar) == false) { + break; + } + c.fChar = nextCharLL(); + } + } + + // + // check for backslash escaped characters. + // + if (c.fChar == chBackSlash) { + int64_t pos = UTEXT_GETNATIVEINDEX(fRXPat->fPattern); + if (RegexStaticSets::gStaticSets->fUnescapeCharSet.contains(peekCharLL())) { + // + // A '\' sequence that is handled by ICU's standard unescapeAt function. + // Includes \uxxxx, \n, \r, many others. + // Return the single equivalent character. + // + nextCharLL(); // get & discard the peeked char. + c.fQuoted = true; + + if (UTEXT_FULL_TEXT_IN_CHUNK(fRXPat->fPattern, fPatternLength)) { + int32_t endIndex = (int32_t)pos; + c.fChar = u_unescapeAt(uregex_ucstr_unescape_charAt, &endIndex, (int32_t)fPatternLength, (void *)fRXPat->fPattern->chunkContents); + + if (endIndex == pos) { + error(U_REGEX_BAD_ESCAPE_SEQUENCE); + } + fCharNum += endIndex - pos; + UTEXT_SETNATIVEINDEX(fRXPat->fPattern, endIndex); + } else { + int32_t offset = 0; + struct URegexUTextUnescapeCharContext context = U_REGEX_UTEXT_UNESCAPE_CONTEXT(fRXPat->fPattern); + + UTEXT_SETNATIVEINDEX(fRXPat->fPattern, pos); + c.fChar = u_unescapeAt(uregex_utext_unescape_charAt, &offset, INT32_MAX, &context); + + if (offset == 0) { + error(U_REGEX_BAD_ESCAPE_SEQUENCE); + } else if (context.lastOffset == offset) { + UTEXT_PREVIOUS32(fRXPat->fPattern); + } else if (context.lastOffset != offset-1) { + utext_moveIndex32(fRXPat->fPattern, offset - context.lastOffset - 1); + } + fCharNum += offset; + } + } + else if (peekCharLL() == chDigit0) { + // Octal Escape, using Java Regexp Conventions + // which are \0 followed by 1-3 octal digits. + // Different from ICU Unescape handling of Octal, which does not + // require the leading 0. + // Java also has the convention of only consuming 2 octal digits if + // the three digit number would be > 0xff + // + c.fChar = 0; + nextCharLL(); // Consume the initial 0. + int index; + for (index=0; index<3; index++) { + int32_t ch = peekCharLL(); + if (chchDigit7) { + if (index==0) { + // \0 is not followed by any octal digits. + error(U_REGEX_BAD_ESCAPE_SEQUENCE); + } + break; + } + c.fChar <<= 3; + c.fChar += ch&7; + if (c.fChar <= 255) { + nextCharLL(); + } else { + // The last digit made the number too big. Forget we saw it. + c.fChar >>= 3; + } + } + c.fQuoted = true; + } + else if (peekCharLL() == chQ) { + // "\Q" enter quote mode, which will continue until "\E" + fQuoteMode = true; + nextCharLL(); // discard the 'Q'. + // nextChar(c); // recurse to get the real next char. + goto tailRecursion; // Note: fuzz testing produced test cases that + // resulted in stack overflow here. + } + else + { + // We are in a '\' escape that will be handled by the state table scanner. + // Just return the backslash, but remember that the following char is to + // be taken literally. + fInBackslashQuote = true; + } + } + } + + // re-enable # to end-of-line comments, in case they were disabled. + // They are disabled by the parser upon seeing '(?', but this lasts for + // the fetching of the next character only. + fEOLComments = true; + + // putc(c.fChar, stdout); +} + + + +//------------------------------------------------------------------------------ +// +// scanNamedChar +// Get a UChar32 from a \N{UNICODE CHARACTER NAME} in the pattern. +// +// The scan position will be at the 'N'. On return +// the scan position should be just after the '}' +// +// Return the UChar32 +// +//------------------------------------------------------------------------------ +UChar32 RegexCompile::scanNamedChar() { + if (U_FAILURE(*fStatus)) { + return 0; + } + + nextChar(fC); + if (fC.fChar != chLBrace) { + error(U_REGEX_PROPERTY_SYNTAX); + return 0; + } + + UnicodeString charName; + for (;;) { + nextChar(fC); + if (fC.fChar == chRBrace) { + break; + } + if (fC.fChar == -1) { + error(U_REGEX_PROPERTY_SYNTAX); + return 0; + } + charName.append(fC.fChar); + } + + char name[100]; + if (!uprv_isInvariantUString(charName.getBuffer(), charName.length()) || + (uint32_t)charName.length()>=sizeof(name)) { + // All Unicode character names have only invariant characters. + // The API to get a character, given a name, accepts only char *, forcing us to convert, + // which requires this error check + error(U_REGEX_PROPERTY_SYNTAX); + return 0; + } + charName.extract(0, charName.length(), name, sizeof(name), US_INV); + + UChar32 theChar = u_charFromName(U_UNICODE_CHAR_NAME, name, fStatus); + if (U_FAILURE(*fStatus)) { + error(U_REGEX_PROPERTY_SYNTAX); + } + + nextChar(fC); // Continue overall regex pattern processing with char after the '}' + return theChar; +} + +//------------------------------------------------------------------------------ +// +// scanProp Construct a UnicodeSet from the text at the current scan +// position, which will be of the form \p{whaterver} +// +// The scan position will be at the 'p' or 'P'. On return +// the scan position should be just after the '}' +// +// Return a UnicodeSet, constructed from the \P pattern, +// or nullptr if the pattern is invalid. +// +//------------------------------------------------------------------------------ +UnicodeSet *RegexCompile::scanProp() { + UnicodeSet *uset = nullptr; + + if (U_FAILURE(*fStatus)) { + return nullptr; + } + (void)chLowerP; // Suppress compiler unused variable warning. + U_ASSERT(fC.fChar == chLowerP || fC.fChar == chP); + UBool negated = (fC.fChar == chP); + + UnicodeString propertyName; + nextChar(fC); + if (fC.fChar != chLBrace) { + error(U_REGEX_PROPERTY_SYNTAX); + return nullptr; + } + for (;;) { + nextChar(fC); + if (fC.fChar == chRBrace) { + break; + } + if (fC.fChar == -1) { + // Hit the end of the input string without finding the closing '}' + error(U_REGEX_PROPERTY_SYNTAX); + return nullptr; + } + propertyName.append(fC.fChar); + } + uset = createSetForProperty(propertyName, negated); + nextChar(fC); // Move input scan to position following the closing '}' + return uset; +} + +//------------------------------------------------------------------------------ +// +// scanPosixProp Construct a UnicodeSet from the text at the current scan +// position, which is expected be of the form [:property expression:] +// +// The scan position will be at the opening ':'. On return +// the scan position must be on the closing ']' +// +// Return a UnicodeSet constructed from the pattern, +// or nullptr if this is not a valid POSIX-style set expression. +// If not a property expression, restore the initial scan position +// (to the opening ':') +// +// Note: the opening '[:' is not sufficient to guarantee that +// this is a [:property:] expression. +// [:'+=,] is a perfectly good ordinary set expression that +// happens to include ':' as one of its characters. +// +//------------------------------------------------------------------------------ +UnicodeSet *RegexCompile::scanPosixProp() { + UnicodeSet *uset = nullptr; + + if (U_FAILURE(*fStatus)) { + return nullptr; + } + + U_ASSERT(fC.fChar == chColon); + + // Save the scanner state. + // TODO: move this into the scanner, with the state encapsulated in some way. Ticket 6062 + int64_t savedScanIndex = fScanIndex; + int64_t savedNextIndex = UTEXT_GETNATIVEINDEX(fRXPat->fPattern); + UBool savedQuoteMode = fQuoteMode; + UBool savedInBackslashQuote = fInBackslashQuote; + UBool savedEOLComments = fEOLComments; + int64_t savedLineNum = fLineNum; + int64_t savedCharNum = fCharNum; + UChar32 savedLastChar = fLastChar; + UChar32 savedPeekChar = fPeekChar; + RegexPatternChar savedfC = fC; + + // Scan for a closing ]. A little tricky because there are some perverse + // edge cases possible. "[:abc\Qdef:] \E]" is a valid non-property expression, + // ending on the second closing ]. + + UnicodeString propName; + UBool negated = false; + + // Check for and consume the '^' in a negated POSIX property, e.g. [:^Letter:] + nextChar(fC); + if (fC.fChar == chUp) { + negated = true; + nextChar(fC); + } + + // Scan for the closing ":]", collecting the property name along the way. + UBool sawPropSetTerminator = false; + for (;;) { + propName.append(fC.fChar); + nextChar(fC); + if (fC.fQuoted || fC.fChar == -1) { + // Escaped characters or end of input - either says this isn't a [:Property:] + break; + } + if (fC.fChar == chColon) { + nextChar(fC); + if (fC.fChar == chRBracket) { + sawPropSetTerminator = true; + } + break; + } + } + + if (sawPropSetTerminator) { + uset = createSetForProperty(propName, negated); + } + else + { + // No closing ":]". + // Restore the original scan position. + // The main scanner will retry the input as a normal set expression, + // not a [:Property:] expression. + fScanIndex = savedScanIndex; + fQuoteMode = savedQuoteMode; + fInBackslashQuote = savedInBackslashQuote; + fEOLComments = savedEOLComments; + fLineNum = savedLineNum; + fCharNum = savedCharNum; + fLastChar = savedLastChar; + fPeekChar = savedPeekChar; + fC = savedfC; + UTEXT_SETNATIVEINDEX(fRXPat->fPattern, savedNextIndex); + } + return uset; +} + +static inline void addIdentifierIgnorable(UnicodeSet *set, UErrorCode& ec) { + set->add(0, 8).add(0x0e, 0x1b).add(0x7f, 0x9f); + addCategory(set, U_GC_CF_MASK, ec); +} + +// +// Create a Unicode Set from a Unicode Property expression. +// This is common code underlying both \p{...} and [:...:] expressions. +// Includes trying the Java "properties" that aren't supported as +// normal ICU UnicodeSet properties +// +UnicodeSet *RegexCompile::createSetForProperty(const UnicodeString &propName, UBool negated) { + + if (U_FAILURE(*fStatus)) { + return nullptr; + } + LocalPointer set; + UErrorCode status = U_ZERO_ERROR; + + do { // non-loop, exists to allow breaks from the block. + // + // First try the property as we received it + // + UnicodeString setExpr; + uint32_t usetFlags = 0; + setExpr.append(u"[\\p{", -1); + setExpr.append(propName); + setExpr.append(u"}]", -1); + if (fModeFlags & UREGEX_CASE_INSENSITIVE) { + usetFlags |= USET_CASE_INSENSITIVE; + } + set.adoptInsteadAndCheckErrorCode(new UnicodeSet(setExpr, usetFlags, nullptr, status), status); + if (U_SUCCESS(status) || status == U_MEMORY_ALLOCATION_ERROR) { + break; + } + + // + // The incoming property wasn't directly recognized by ICU. + + // Check [:word:] and [:all:]. These are not recognized as a properties by ICU UnicodeSet. + // Java accepts 'word' with mixed case. + // Java accepts 'all' only in all lower case. + + status = U_ZERO_ERROR; + if (propName.caseCompare(u"word", -1, 0) == 0) { + set.adoptInsteadAndCheckErrorCode( + RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET].cloneAsThawed(), status); + break; + } + if (propName.compare(u"all", -1) == 0) { + set.adoptInsteadAndCheckErrorCode(new UnicodeSet(0, 0x10ffff), status); + break; + } + + + // Do Java InBlock expressions + // + UnicodeString mPropName = propName; + if (mPropName.startsWith(u"In", 2) && mPropName.length() >= 3) { + status = U_ZERO_ERROR; + set.adoptInsteadAndCheckErrorCode(new UnicodeSet(), status); + if (U_FAILURE(status)) { + break; + } + UnicodeString blockName(mPropName, 2); // Property with the leading "In" removed. + set->applyPropertyAlias(UnicodeString(u"Block"), blockName, status); + break; + } + + // Check for the Java form "IsBooleanPropertyValue", which we will recast + // as "BooleanPropertyValue". The property value can be either a + // a General Category or a Script Name. + + if (propName.startsWith(u"Is", 2) && propName.length()>=3) { + mPropName.remove(0, 2); // Strip the "Is" + if (mPropName.indexOf(u'=') >= 0) { + // Reject any "Is..." property expression containing an '=', that is, + // any non-binary property expression. + status = U_REGEX_PROPERTY_SYNTAX; + break; + } + + if (mPropName.caseCompare(u"assigned", -1, 0) == 0) { + mPropName.setTo(u"unassigned", -1); + negated = !negated; + } else if (mPropName.caseCompare(u"TitleCase", -1, 0) == 0) { + mPropName.setTo(u"Titlecase_Letter", -1); + } + + mPropName.insert(0, u"[\\p{", -1); + mPropName.append(u"}]", -1); + set.adoptInsteadAndCheckErrorCode(new UnicodeSet(mPropName, *fStatus), status); + + if (U_SUCCESS(status) && !set->isEmpty() && (usetFlags & USET_CASE_INSENSITIVE)) { + set->closeOver(USET_CASE_INSENSITIVE); + } + break; + + } + + if (propName.startsWith(u"java", -1)) { + status = U_ZERO_ERROR; + set.adoptInsteadAndCheckErrorCode(new UnicodeSet(), status); + if (U_FAILURE(status)) { + break; + } + // + // Try the various Java specific properties. + // These all begin with "java" + // + if (propName.compare(u"javaDefined", -1) == 0) { + addCategory(set.getAlias(), U_GC_CN_MASK, status); + set->complement(); + } + else if (propName.compare(u"javaDigit", -1) == 0) { + addCategory(set.getAlias(), U_GC_ND_MASK, status); + } + else if (propName.compare(u"javaIdentifierIgnorable", -1) == 0) { + addIdentifierIgnorable(set.getAlias(), status); + } + else if (propName.compare(u"javaISOControl", -1) == 0) { + set->add(0, 0x1F).add(0x7F, 0x9F); + } + else if (propName.compare(u"javaJavaIdentifierPart", -1) == 0) { + addCategory(set.getAlias(), U_GC_L_MASK, status); + addCategory(set.getAlias(), U_GC_SC_MASK, status); + addCategory(set.getAlias(), U_GC_PC_MASK, status); + addCategory(set.getAlias(), U_GC_ND_MASK, status); + addCategory(set.getAlias(), U_GC_NL_MASK, status); + addCategory(set.getAlias(), U_GC_MC_MASK, status); + addCategory(set.getAlias(), U_GC_MN_MASK, status); + addIdentifierIgnorable(set.getAlias(), status); + } + else if (propName.compare(u"javaJavaIdentifierStart", -1) == 0) { + addCategory(set.getAlias(), U_GC_L_MASK, status); + addCategory(set.getAlias(), U_GC_NL_MASK, status); + addCategory(set.getAlias(), U_GC_SC_MASK, status); + addCategory(set.getAlias(), U_GC_PC_MASK, status); + } + else if (propName.compare(u"javaLetter", -1) == 0) { + addCategory(set.getAlias(), U_GC_L_MASK, status); + } + else if (propName.compare(u"javaLetterOrDigit", -1) == 0) { + addCategory(set.getAlias(), U_GC_L_MASK, status); + addCategory(set.getAlias(), U_GC_ND_MASK, status); + } + else if (propName.compare(u"javaLowerCase", -1) == 0) { + addCategory(set.getAlias(), U_GC_LL_MASK, status); + } + else if (propName.compare(u"javaMirrored", -1) == 0) { + set->applyIntPropertyValue(UCHAR_BIDI_MIRRORED, 1, status); + } + else if (propName.compare(u"javaSpaceChar", -1) == 0) { + addCategory(set.getAlias(), U_GC_Z_MASK, status); + } + else if (propName.compare(u"javaSupplementaryCodePoint", -1) == 0) { + set->add(0x10000, UnicodeSet::MAX_VALUE); + } + else if (propName.compare(u"javaTitleCase", -1) == 0) { + addCategory(set.getAlias(), U_GC_LT_MASK, status); + } + else if (propName.compare(u"javaUnicodeIdentifierStart", -1) == 0) { + addCategory(set.getAlias(), U_GC_L_MASK, status); + addCategory(set.getAlias(), U_GC_NL_MASK, status); + } + else if (propName.compare(u"javaUnicodeIdentifierPart", -1) == 0) { + addCategory(set.getAlias(), U_GC_L_MASK, status); + addCategory(set.getAlias(), U_GC_PC_MASK, status); + addCategory(set.getAlias(), U_GC_ND_MASK, status); + addCategory(set.getAlias(), U_GC_NL_MASK, status); + addCategory(set.getAlias(), U_GC_MC_MASK, status); + addCategory(set.getAlias(), U_GC_MN_MASK, status); + addIdentifierIgnorable(set.getAlias(), status); + } + else if (propName.compare(u"javaUpperCase", -1) == 0) { + addCategory(set.getAlias(), U_GC_LU_MASK, status); + } + else if (propName.compare(u"javaValidCodePoint", -1) == 0) { + set->add(0, UnicodeSet::MAX_VALUE); + } + else if (propName.compare(u"javaWhitespace", -1) == 0) { + addCategory(set.getAlias(), U_GC_Z_MASK, status); + set->removeAll(UnicodeSet().add(0xa0).add(0x2007).add(0x202f)); + set->add(9, 0x0d).add(0x1c, 0x1f); + } else { + status = U_REGEX_PROPERTY_SYNTAX; + } + + if (U_SUCCESS(status) && !set->isEmpty() && (usetFlags & USET_CASE_INSENSITIVE)) { + set->closeOver(USET_CASE_INSENSITIVE); + } + break; + } + + // Unrecognized property. ICU didn't like it as it was, and none of the Java compatibility + // extensions matched it. + status = U_REGEX_PROPERTY_SYNTAX; + } while (false); // End of do loop block. Code above breaks out of the block on success or hard failure. + + if (U_SUCCESS(status)) { + // ICU 70 adds emoji properties of strings, but as long as Java does not say how to + // deal with properties of strings and character classes with strings, we ignore them. + // Just in case something downstream might stumble over the strings, + // we remove them from the set. + // Note that when we support strings, the complement of a property (as with \P) + // should be implemented as .complement().removeAllStrings() (code point complement). + set->removeAllStrings(); + U_ASSERT(set.isValid()); + if (negated) { + set->complement(); + } + return set.orphan(); + } else { + if (status == U_ILLEGAL_ARGUMENT_ERROR) { + status = U_REGEX_PROPERTY_SYNTAX; + } + error(status); + return nullptr; + } +} + + +// +// SetEval Part of the evaluation of [set expressions]. +// Perform any pending (stacked) operations with precedence +// equal or greater to that of the next operator encountered +// in the expression. +// +void RegexCompile::setEval(int32_t nextOp) { + UnicodeSet *rightOperand = nullptr; + UnicodeSet *leftOperand = nullptr; + for (;;) { + U_ASSERT(fSetOpStack.empty()==false); + int32_t pendingSetOperation = fSetOpStack.peeki(); + if ((pendingSetOperation&0xffff0000) < (nextOp&0xffff0000)) { + break; + } + fSetOpStack.popi(); + U_ASSERT(fSetStack.empty() == false); + rightOperand = (UnicodeSet *)fSetStack.peek(); + // ICU 70 adds emoji properties of strings, but createSetForProperty() removes all strings + // (see comments there). + // We also do not yet support string literals in character classes, + // so there should not be any strings. + // Note that when we support strings, the complement of a set (as with ^ or \P) + // should be implemented as .complement().removeAllStrings() (code point complement). + U_ASSERT(!rightOperand->hasStrings()); + switch (pendingSetOperation) { + case setNegation: + rightOperand->complement(); + break; + case setCaseClose: + // TODO: need a simple close function. Ticket 6065 + rightOperand->closeOver(USET_CASE_INSENSITIVE); + rightOperand->removeAllStrings(); + break; + case setDifference1: + case setDifference2: + fSetStack.pop(); + leftOperand = (UnicodeSet *)fSetStack.peek(); + leftOperand->removeAll(*rightOperand); + delete rightOperand; + break; + case setIntersection1: + case setIntersection2: + fSetStack.pop(); + leftOperand = (UnicodeSet *)fSetStack.peek(); + leftOperand->retainAll(*rightOperand); + delete rightOperand; + break; + case setUnion: + fSetStack.pop(); + leftOperand = (UnicodeSet *)fSetStack.peek(); + leftOperand->addAll(*rightOperand); + delete rightOperand; + break; + default: + UPRV_UNREACHABLE_EXIT; + } + } + } + +void RegexCompile::setPushOp(int32_t op) { + setEval(op); + fSetOpStack.push(op, *fStatus); + LocalPointer lpSet(new UnicodeSet(), *fStatus); + fSetStack.push(lpSet.orphan(), *fStatus); +} + +U_NAMESPACE_END +#endif // !UCONFIG_NO_REGULAR_EXPRESSIONS + diff --git a/deps/icu-small/source/i18n/regexcmp.h b/deps/icu-small/source/i18n/regexcmp.h index 81ac9e51784398..315b1d043f5e95 100644 --- a/deps/icu-small/source/i18n/regexcmp.h +++ b/deps/icu-small/source/i18n/regexcmp.h @@ -1,234 +1,234 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// regexcmp.h -// -// Copyright (C) 2002-2016, International Business Machines Corporation and others. -// All Rights Reserved. -// -// This file contains declarations for the class RegexCompile -// -// This class is internal to the regular expression implementation. -// For the public Regular Expression API, see the file "unicode/regex.h" -// - - -#ifndef RBBISCAN_H -#define RBBISCAN_H - -#include "unicode/utypes.h" -#if !UCONFIG_NO_REGULAR_EXPRESSIONS - -#include "unicode/parseerr.h" -#include "unicode/uniset.h" -#include "unicode/uobject.h" -#include "unicode/utext.h" -#include "uhash.h" -#include "uvector.h" -#include "uvectr32.h" - - - -U_NAMESPACE_BEGIN - - -//-------------------------------------------------------------------------------- -// -// class RegexCompile Contains the regular expression compiler. -// -//-------------------------------------------------------------------------------- -class RegexPattern; - - -class U_I18N_API RegexCompile : public UMemory { -public: - - enum { - kStackSize = 100 // The size of the state stack for - }; // pattern parsing. Corresponds roughly - // to the depth of parentheses nesting - // that is allowed in the rules. - - struct RegexPatternChar { - UChar32 fChar; - UBool fQuoted; - }; - - RegexCompile(RegexPattern *rp, UErrorCode &e); - - void compile(const UnicodeString &pat, UParseError &pp, UErrorCode &e); - void compile(UText *pat, UParseError &pp, UErrorCode &e); - - - virtual ~RegexCompile(); - - void nextChar(RegexPatternChar &c); // Get the next char from the input stream. - - - // Categories of parentheses in pattern. - // The category is saved in the compile-time parentheses stack frame, and - // determines the code to be generated when the matching close ) is encountered. - enum EParenClass { - plain = -1, // No special handling - capturing = -2, - atomic = -3, - lookAhead = -4, - negLookAhead = -5, - flags = -6, - lookBehind = -7, - lookBehindN = -8 - }; - -private: - - - UBool doParseActions(int32_t a); - void error(UErrorCode e); // error reporting convenience function. - - UChar32 nextCharLL(); - UChar32 peekCharLL(); - UnicodeSet *scanProp(); - UnicodeSet *scanPosixProp(); - void handleCloseParen(); - int32_t blockTopLoc(UBool reserve); // Locate a position in the compiled pattern - // at the top of the just completed block - // or operation, and optionally ensure that - // there is space to add an opcode there. - void compileSet(UnicodeSet *theSet); // Generate the compiled pattern for - // a reference to a UnicodeSet. - void compileInterval(int32_t InitOp, // Generate the code for a {min,max} quantifier. - int32_t LoopOp); - UBool compileInlineInterval(); // Generate inline code for a {min,max} quantifier - void literalChar(UChar32 c); // Compile a literal char - void fixLiterals(UBool split=false); // Generate code for pending literal characters. - void insertOp(int32_t where); // Open up a slot for a new op in the - // generated code at the specified location. - void appendOp(int32_t op); // Append a new op to the compiled pattern. - void appendOp(int32_t type, int32_t val); // Build & append a new op to the compiled pattern. - int32_t buildOp(int32_t type, int32_t val); // Construct a new pcode instruction. - int32_t allocateData(int32_t size); // Allocate space in the matcher data area. - // Return index of the newly allocated data. - int32_t allocateStackData(int32_t size); // Allocate space in the match back-track stack frame. - // Return offset index in the frame. - int32_t minMatchLength(int32_t start, - int32_t end); - int32_t maxMatchLength(int32_t start, - int32_t end); - void matchStartType(); - void stripNOPs(); - - void setEval(int32_t op); - void setPushOp(int32_t op); - UChar32 scanNamedChar(); - UnicodeSet *createSetForProperty(const UnicodeString &propName, UBool negated); - -public: // Public for testing only. - static void U_EXPORT2 findCaseInsensitiveStarters(UChar32 c, UnicodeSet *starterChars); -private: - - - UErrorCode *fStatus; - RegexPattern *fRXPat; - UParseError *fParseErr; - - // - // Data associated with low level character scanning - // - int64_t fScanIndex; // Index of current character being processed - // in the rule input string. - UBool fQuoteMode; // Scan is in a \Q...\E quoted region - UBool fInBackslashQuote; // Scan is between a '\' and the following char. - UBool fEOLComments; // When scan is just after '(?', inhibit #... to - // end of line comments, in favor of (?#...) comments. - int64_t fLineNum; // Line number in input file. - int64_t fCharNum; // Char position within the line. - UChar32 fLastChar; // Previous char, needed to count CR-LF - // as a single line, not two. - UChar32 fPeekChar; // Saved char, if we've scanned ahead. - - - RegexPatternChar fC; // Current char for parse state machine - // processing. - - uint16_t fStack[kStackSize]; // State stack, holds state pushes - int32_t fStackPtr; // and pops as specified in the state - // transition rules. - - // - // Data associated with the generation of the pcode for the match engine - // - int32_t fModeFlags; // Match Flags. (Case Insensitive, etc.) - // Always has high bit (31) set so that flag values - // on the paren stack are distinguished from relocatable - // pcode addresses. - int32_t fNewModeFlags; // New flags, while compiling (?i, holds state - // until last flag is scanned. - UBool fSetModeFlag; // true for (?ismx, false for (?-ismx - - UnicodeString fLiteralChars; // Literal chars or strings from the pattern are accumulated here. - // Once completed, meaning that some non-literal pattern - // construct is encountered, the appropriate opcodes - // to match the literal will be generated, and this - // string will be cleared. - - int64_t fPatternLength; // Length of the input pattern string. - - UVector32 fParenStack; // parentheses stack. Each frame consists of - // the positions of compiled pattern operations - // needing fixup, followed by negative value. The - // first entry in each frame is the position of the - // spot reserved for use when a quantifier - // needs to add a SAVE at the start of a (block) - // The negative value (-1, -2,...) indicates - // the kind of paren that opened the frame. Some - // need special handling on close. - - - int32_t fMatchOpenParen; // The position in the compiled pattern - // of the slot reserved for a state save - // at the start of the most recently processed - // parenthesized block. Updated when processing - // a close to the location for the corresponding open. - - int32_t fMatchCloseParen; // The position in the pattern of the first - // location after the most recently processed - // parenthesized block. - - int32_t fIntervalLow; // {lower, upper} interval quantifier values. - int32_t fIntervalUpper; // Placed here temporarily, when pattern is - // initially scanned. Each new interval - // encountered overwrites these values. - // -1 for the upper interval value means none - // was specified (unlimited occurrences.) - - UStack fSetStack; // Stack of UnicodeSets, used while evaluating - // (at compile time) set expressions within - // the pattern. - UStack fSetOpStack; // Stack of pending set operators (&&, --, union) - - UChar32 fLastSetLiteral; // The last single code point added to a set. - // needed when "-y" is scanned, and we need - // to turn "x-y" into a range. - - UnicodeString *fCaptureName; // Named Capture, the group name is built up - // in this string while being scanned. -}; - -// Constant values to be pushed onto fSetOpStack while scanning & evaluating [set expressions] -// The high 16 bits are the operator precedence, and the low 16 are a code for the operation itself. - -enum SetOperations { - setStart = 0 << 16 | 1, - setEnd = 1 << 16 | 2, - setNegation = 2 << 16 | 3, - setCaseClose = 2 << 16 | 9, - setDifference2 = 3 << 16 | 4, // '--' set difference operator - setIntersection2 = 3 << 16 | 5, // '&&' set intersection operator - setUnion = 4 << 16 | 6, // implicit union of adjacent items - setDifference1 = 4 << 16 | 7, // '-', single dash difference op, for compatibility with old UnicodeSet. - setIntersection1 = 4 << 16 | 8 // '&', single amp intersection op, for compatibility with old UnicodeSet. - }; - -U_NAMESPACE_END -#endif // !UCONFIG_NO_REGULAR_EXPRESSIONS -#endif // RBBISCAN_H +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// regexcmp.h +// +// Copyright (C) 2002-2016, International Business Machines Corporation and others. +// All Rights Reserved. +// +// This file contains declarations for the class RegexCompile +// +// This class is internal to the regular expression implementation. +// For the public Regular Expression API, see the file "unicode/regex.h" +// + + +#ifndef RBBISCAN_H +#define RBBISCAN_H + +#include "unicode/utypes.h" +#if !UCONFIG_NO_REGULAR_EXPRESSIONS + +#include "unicode/parseerr.h" +#include "unicode/uniset.h" +#include "unicode/uobject.h" +#include "unicode/utext.h" +#include "uhash.h" +#include "uvector.h" +#include "uvectr32.h" + + + +U_NAMESPACE_BEGIN + + +//-------------------------------------------------------------------------------- +// +// class RegexCompile Contains the regular expression compiler. +// +//-------------------------------------------------------------------------------- +class RegexPattern; + + +class U_I18N_API RegexCompile : public UMemory { +public: + + enum { + kStackSize = 100 // The size of the state stack for + }; // pattern parsing. Corresponds roughly + // to the depth of parentheses nesting + // that is allowed in the rules. + + struct RegexPatternChar { + UChar32 fChar; + UBool fQuoted; + }; + + RegexCompile(RegexPattern *rp, UErrorCode &e); + + void compile(const UnicodeString &pat, UParseError &pp, UErrorCode &e); + void compile(UText *pat, UParseError &pp, UErrorCode &e); + + + virtual ~RegexCompile(); + + void nextChar(RegexPatternChar &c); // Get the next char from the input stream. + + + // Categories of parentheses in pattern. + // The category is saved in the compile-time parentheses stack frame, and + // determines the code to be generated when the matching close ) is encountered. + enum EParenClass { + plain = -1, // No special handling + capturing = -2, + atomic = -3, + lookAhead = -4, + negLookAhead = -5, + flags = -6, + lookBehind = -7, + lookBehindN = -8 + }; + +private: + + + UBool doParseActions(int32_t a); + void error(UErrorCode e); // error reporting convenience function. + + UChar32 nextCharLL(); + UChar32 peekCharLL(); + UnicodeSet *scanProp(); + UnicodeSet *scanPosixProp(); + void handleCloseParen(); + int32_t blockTopLoc(UBool reserve); // Locate a position in the compiled pattern + // at the top of the just completed block + // or operation, and optionally ensure that + // there is space to add an opcode there. + void compileSet(UnicodeSet *theSet); // Generate the compiled pattern for + // a reference to a UnicodeSet. + void compileInterval(int32_t InitOp, // Generate the code for a {min,max} quantifier. + int32_t LoopOp); + UBool compileInlineInterval(); // Generate inline code for a {min,max} quantifier + void literalChar(UChar32 c); // Compile a literal char + void fixLiterals(UBool split=false); // Generate code for pending literal characters. + void insertOp(int32_t where); // Open up a slot for a new op in the + // generated code at the specified location. + void appendOp(int32_t op); // Append a new op to the compiled pattern. + void appendOp(int32_t type, int32_t val); // Build & append a new op to the compiled pattern. + int32_t buildOp(int32_t type, int32_t val); // Construct a new pcode instruction. + int32_t allocateData(int32_t size); // Allocate space in the matcher data area. + // Return index of the newly allocated data. + int32_t allocateStackData(int32_t size); // Allocate space in the match back-track stack frame. + // Return offset index in the frame. + int32_t minMatchLength(int32_t start, + int32_t end); + int32_t maxMatchLength(int32_t start, + int32_t end); + void matchStartType(); + void stripNOPs(); + + void setEval(int32_t op); + void setPushOp(int32_t op); + UChar32 scanNamedChar(); + UnicodeSet *createSetForProperty(const UnicodeString &propName, UBool negated); + +public: // Public for testing only. + static void U_EXPORT2 findCaseInsensitiveStarters(UChar32 c, UnicodeSet *starterChars); +private: + + + UErrorCode *fStatus; + RegexPattern *fRXPat; + UParseError *fParseErr; + + // + // Data associated with low level character scanning + // + int64_t fScanIndex; // Index of current character being processed + // in the rule input string. + UBool fQuoteMode; // Scan is in a \Q...\E quoted region + UBool fInBackslashQuote; // Scan is between a '\' and the following char. + UBool fEOLComments; // When scan is just after '(?', inhibit #... to + // end of line comments, in favor of (?#...) comments. + int64_t fLineNum; // Line number in input file. + int64_t fCharNum; // Char position within the line. + UChar32 fLastChar; // Previous char, needed to count CR-LF + // as a single line, not two. + UChar32 fPeekChar; // Saved char, if we've scanned ahead. + + + RegexPatternChar fC; // Current char for parse state machine + // processing. + + uint16_t fStack[kStackSize]; // State stack, holds state pushes + int32_t fStackPtr; // and pops as specified in the state + // transition rules. + + // + // Data associated with the generation of the pcode for the match engine + // + int32_t fModeFlags; // Match Flags. (Case Insensitive, etc.) + // Always has high bit (31) set so that flag values + // on the paren stack are distinguished from relocatable + // pcode addresses. + int32_t fNewModeFlags; // New flags, while compiling (?i, holds state + // until last flag is scanned. + UBool fSetModeFlag; // true for (?ismx, false for (?-ismx + + UnicodeString fLiteralChars; // Literal chars or strings from the pattern are accumulated here. + // Once completed, meaning that some non-literal pattern + // construct is encountered, the appropriate opcodes + // to match the literal will be generated, and this + // string will be cleared. + + int64_t fPatternLength; // Length of the input pattern string. + + UVector32 fParenStack; // parentheses stack. Each frame consists of + // the positions of compiled pattern operations + // needing fixup, followed by negative value. The + // first entry in each frame is the position of the + // spot reserved for use when a quantifier + // needs to add a SAVE at the start of a (block) + // The negative value (-1, -2,...) indicates + // the kind of paren that opened the frame. Some + // need special handling on close. + + + int32_t fMatchOpenParen; // The position in the compiled pattern + // of the slot reserved for a state save + // at the start of the most recently processed + // parenthesized block. Updated when processing + // a close to the location for the corresponding open. + + int32_t fMatchCloseParen; // The position in the pattern of the first + // location after the most recently processed + // parenthesized block. + + int32_t fIntervalLow; // {lower, upper} interval quantifier values. + int32_t fIntervalUpper; // Placed here temporarily, when pattern is + // initially scanned. Each new interval + // encountered overwrites these values. + // -1 for the upper interval value means none + // was specified (unlimited occurrences.) + + UStack fSetStack; // Stack of UnicodeSets, used while evaluating + // (at compile time) set expressions within + // the pattern. + UStack fSetOpStack; // Stack of pending set operators (&&, --, union) + + UChar32 fLastSetLiteral; // The last single code point added to a set. + // needed when "-y" is scanned, and we need + // to turn "x-y" into a range. + + UnicodeString *fCaptureName; // Named Capture, the group name is built up + // in this string while being scanned. +}; + +// Constant values to be pushed onto fSetOpStack while scanning & evaluating [set expressions] +// The high 16 bits are the operator precedence, and the low 16 are a code for the operation itself. + +enum SetOperations { + setStart = 0 << 16 | 1, + setEnd = 1 << 16 | 2, + setNegation = 2 << 16 | 3, + setCaseClose = 2 << 16 | 9, + setDifference2 = 3 << 16 | 4, // '--' set difference operator + setIntersection2 = 3 << 16 | 5, // '&&' set intersection operator + setUnion = 4 << 16 | 6, // implicit union of adjacent items + setDifference1 = 4 << 16 | 7, // '-', single dash difference op, for compatibility with old UnicodeSet. + setIntersection1 = 4 << 16 | 8 // '&', single amp intersection op, for compatibility with old UnicodeSet. + }; + +U_NAMESPACE_END +#endif // !UCONFIG_NO_REGULAR_EXPRESSIONS +#endif // RBBISCAN_H diff --git a/deps/icu-small/source/i18n/regexcst.h b/deps/icu-small/source/i18n/regexcst.h index a475b6b363ed8f..694ef317df3d36 100644 --- a/deps/icu-small/source/i18n/regexcst.h +++ b/deps/icu-small/source/i18n/regexcst.h @@ -1,570 +1,570 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -//--------------------------------------------------------------------------------- -// -// Generated Header File. Do not edit by hand. -// This file contains the state table for the ICU Regular Expression Pattern Parser -// It is generated by the Perl script "regexcst.pl" from -// the rule parser state definitions file "regexcst.txt". -// -// Copyright (C) 2002-2016 International Business Machines Corporation -// and others. All rights reserved. -// -//--------------------------------------------------------------------------------- -#ifndef RBBIRPT_H -#define RBBIRPT_H - -#include "unicode/utypes.h" - -U_NAMESPACE_BEGIN -// -// Character classes for regex pattern scanning. -// - static const uint8_t kRuleSet_digit_char = 128; - static const uint8_t kRuleSet_ascii_letter = 129; - static const uint8_t kRuleSet_rule_char = 130; - constexpr uint32_t kRuleSet_count = 131-128; - -enum Regex_PatternParseAction { - doSetBackslash_D, - doBackslashh, - doBackslashH, - doSetLiteralEscaped, - doOpenLookAheadNeg, - doCompleteNamedBackRef, - doPatStart, - doBackslashS, - doBackslashD, - doNGStar, - doNOP, - doBackslashX, - doSetLiteral, - doContinueNamedCapture, - doBackslashG, - doBackslashR, - doSetBegin, - doSetBackslash_v, - doPossessivePlus, - doPerlInline, - doBackslashZ, - doSetAddAmp, - doSetBeginDifference1, - doIntervalError, - doSetNegate, - doIntervalInit, - doSetIntersection2, - doPossessiveInterval, - doRuleError, - doBackslashW, - doContinueNamedBackRef, - doOpenNonCaptureParen, - doExit, - doSetNamedChar, - doSetBackslash_V, - doConditionalExpr, - doEscapeError, - doBadOpenParenType, - doPossessiveStar, - doSetAddDash, - doEscapedLiteralChar, - doSetBackslash_w, - doIntervalUpperDigit, - doBackslashv, - doSetBackslash_S, - doSetNoCloseError, - doSetProp, - doBackslashB, - doSetEnd, - doSetRange, - doMatchModeParen, - doPlus, - doBackslashV, - doSetMatchMode, - doBackslashz, - doSetNamedRange, - doOpenLookBehindNeg, - doInterval, - doBadNamedCapture, - doBeginMatchMode, - doBackslashd, - doPatFinish, - doNamedChar, - doNGPlus, - doSetDifference2, - doSetBackslash_H, - doCloseParen, - doDotAny, - doOpenCaptureParen, - doEnterQuoteMode, - doOpenAtomicParen, - doBadModeFlag, - doSetBackslash_d, - doSetFinish, - doProperty, - doBeginNamedBackRef, - doBackRef, - doOpt, - doDollar, - doBeginNamedCapture, - doNGInterval, - doSetOpError, - doSetPosixProp, - doSetBeginIntersection1, - doBackslashb, - doSetBeginUnion, - doIntevalLowerDigit, - doSetBackslash_h, - doStar, - doMatchMode, - doBackslashA, - doOpenLookBehind, - doPossessiveOpt, - doOrOperator, - doBackslashw, - doBackslashs, - doLiteralChar, - doSuppressComments, - doCaret, - doIntervalSame, - doNGOpt, - doOpenLookAhead, - doSetBackslash_W, - doMismatchedParenErr, - doSetBackslash_s, - rbbiLastAction}; - -//------------------------------------------------------------------------------- -// -// RegexTableEl represents the structure of a row in the transition table -// for the pattern parser state machine. -//------------------------------------------------------------------------------- -struct RegexTableEl { - Regex_PatternParseAction fAction; - uint8_t fCharClass; // 0-127: an individual ASCII character - // 128-255: character class index - uint8_t fNextState; // 0-250: normal next-state numbers - // 255: pop next-state from stack. - uint8_t fPushState; - UBool fNextChar; -}; - -static const struct RegexTableEl gRuleParseStateTable[] = { - {doNOP, 0, 0, 0, true} - , {doPatStart, 255, 2,0, false} // 1 start - , {doLiteralChar, 254, 14,0, true} // 2 term - , {doLiteralChar, 130, 14,0, true} // 3 - , {doSetBegin, 91 /* [ */, 123, 205, true} // 4 - , {doNOP, 40 /* ( */, 27,0, true} // 5 - , {doDotAny, 46 /* . */, 14,0, true} // 6 - , {doCaret, 94 /* ^ */, 14,0, true} // 7 - , {doDollar, 36 /* $ */, 14,0, true} // 8 - , {doNOP, 92 /* \ */, 89,0, true} // 9 - , {doOrOperator, 124 /* | */, 2,0, true} // 10 - , {doCloseParen, 41 /* ) */, 255,0, true} // 11 - , {doPatFinish, 253, 2,0, false} // 12 - , {doRuleError, 255, 206,0, false} // 13 - , {doNOP, 42 /* * */, 68,0, true} // 14 expr-quant - , {doNOP, 43 /* + */, 71,0, true} // 15 - , {doNOP, 63 /* ? */, 74,0, true} // 16 - , {doIntervalInit, 123 /* { */, 77,0, true} // 17 - , {doNOP, 40 /* ( */, 23,0, true} // 18 - , {doNOP, 255, 20,0, false} // 19 - , {doOrOperator, 124 /* | */, 2,0, true} // 20 expr-cont - , {doCloseParen, 41 /* ) */, 255,0, true} // 21 - , {doNOP, 255, 2,0, false} // 22 - , {doSuppressComments, 63 /* ? */, 25,0, true} // 23 open-paren-quant - , {doNOP, 255, 27,0, false} // 24 - , {doNOP, 35 /* # */, 50, 14, true} // 25 open-paren-quant2 - , {doNOP, 255, 29,0, false} // 26 - , {doSuppressComments, 63 /* ? */, 29,0, true} // 27 open-paren - , {doOpenCaptureParen, 255, 2, 14, false} // 28 - , {doOpenNonCaptureParen, 58 /* : */, 2, 14, true} // 29 open-paren-extended - , {doOpenAtomicParen, 62 /* > */, 2, 14, true} // 30 - , {doOpenLookAhead, 61 /* = */, 2, 20, true} // 31 - , {doOpenLookAheadNeg, 33 /* ! */, 2, 20, true} // 32 - , {doNOP, 60 /* < */, 46,0, true} // 33 - , {doNOP, 35 /* # */, 50, 2, true} // 34 - , {doBeginMatchMode, 105 /* i */, 53,0, false} // 35 - , {doBeginMatchMode, 100 /* d */, 53,0, false} // 36 - , {doBeginMatchMode, 109 /* m */, 53,0, false} // 37 - , {doBeginMatchMode, 115 /* s */, 53,0, false} // 38 - , {doBeginMatchMode, 117 /* u */, 53,0, false} // 39 - , {doBeginMatchMode, 119 /* w */, 53,0, false} // 40 - , {doBeginMatchMode, 120 /* x */, 53,0, false} // 41 - , {doBeginMatchMode, 45 /* - */, 53,0, false} // 42 - , {doConditionalExpr, 40 /* ( */, 206,0, true} // 43 - , {doPerlInline, 123 /* { */, 206,0, true} // 44 - , {doBadOpenParenType, 255, 206,0, false} // 45 - , {doOpenLookBehind, 61 /* = */, 2, 20, true} // 46 open-paren-lookbehind - , {doOpenLookBehindNeg, 33 /* ! */, 2, 20, true} // 47 - , {doBeginNamedCapture, 129, 64,0, false} // 48 - , {doBadOpenParenType, 255, 206,0, false} // 49 - , {doNOP, 41 /* ) */, 255,0, true} // 50 paren-comment - , {doMismatchedParenErr, 253, 206,0, false} // 51 - , {doNOP, 255, 50,0, true} // 52 - , {doMatchMode, 105 /* i */, 53,0, true} // 53 paren-flag - , {doMatchMode, 100 /* d */, 53,0, true} // 54 - , {doMatchMode, 109 /* m */, 53,0, true} // 55 - , {doMatchMode, 115 /* s */, 53,0, true} // 56 - , {doMatchMode, 117 /* u */, 53,0, true} // 57 - , {doMatchMode, 119 /* w */, 53,0, true} // 58 - , {doMatchMode, 120 /* x */, 53,0, true} // 59 - , {doMatchMode, 45 /* - */, 53,0, true} // 60 - , {doSetMatchMode, 41 /* ) */, 2,0, true} // 61 - , {doMatchModeParen, 58 /* : */, 2, 14, true} // 62 - , {doBadModeFlag, 255, 206,0, false} // 63 - , {doContinueNamedCapture, 129, 64,0, true} // 64 named-capture - , {doContinueNamedCapture, 128, 64,0, true} // 65 - , {doOpenCaptureParen, 62 /* > */, 2, 14, true} // 66 - , {doBadNamedCapture, 255, 206,0, false} // 67 - , {doNGStar, 63 /* ? */, 20,0, true} // 68 quant-star - , {doPossessiveStar, 43 /* + */, 20,0, true} // 69 - , {doStar, 255, 20,0, false} // 70 - , {doNGPlus, 63 /* ? */, 20,0, true} // 71 quant-plus - , {doPossessivePlus, 43 /* + */, 20,0, true} // 72 - , {doPlus, 255, 20,0, false} // 73 - , {doNGOpt, 63 /* ? */, 20,0, true} // 74 quant-opt - , {doPossessiveOpt, 43 /* + */, 20,0, true} // 75 - , {doOpt, 255, 20,0, false} // 76 - , {doNOP, 128, 79,0, false} // 77 interval-open - , {doIntervalError, 255, 206,0, false} // 78 - , {doIntevalLowerDigit, 128, 79,0, true} // 79 interval-lower - , {doNOP, 44 /* , */, 83,0, true} // 80 - , {doIntervalSame, 125 /* } */, 86,0, true} // 81 - , {doIntervalError, 255, 206,0, false} // 82 - , {doIntervalUpperDigit, 128, 83,0, true} // 83 interval-upper - , {doNOP, 125 /* } */, 86,0, true} // 84 - , {doIntervalError, 255, 206,0, false} // 85 - , {doNGInterval, 63 /* ? */, 20,0, true} // 86 interval-type - , {doPossessiveInterval, 43 /* + */, 20,0, true} // 87 - , {doInterval, 255, 20,0, false} // 88 - , {doBackslashA, 65 /* A */, 2,0, true} // 89 backslash - , {doBackslashB, 66 /* B */, 2,0, true} // 90 - , {doBackslashb, 98 /* b */, 2,0, true} // 91 - , {doBackslashd, 100 /* d */, 14,0, true} // 92 - , {doBackslashD, 68 /* D */, 14,0, true} // 93 - , {doBackslashG, 71 /* G */, 2,0, true} // 94 - , {doBackslashh, 104 /* h */, 14,0, true} // 95 - , {doBackslashH, 72 /* H */, 14,0, true} // 96 - , {doNOP, 107 /* k */, 115,0, true} // 97 - , {doNamedChar, 78 /* N */, 14,0, false} // 98 - , {doProperty, 112 /* p */, 14,0, false} // 99 - , {doProperty, 80 /* P */, 14,0, false} // 100 - , {doBackslashR, 82 /* R */, 14,0, true} // 101 - , {doEnterQuoteMode, 81 /* Q */, 2,0, true} // 102 - , {doBackslashS, 83 /* S */, 14,0, true} // 103 - , {doBackslashs, 115 /* s */, 14,0, true} // 104 - , {doBackslashv, 118 /* v */, 14,0, true} // 105 - , {doBackslashV, 86 /* V */, 14,0, true} // 106 - , {doBackslashW, 87 /* W */, 14,0, true} // 107 - , {doBackslashw, 119 /* w */, 14,0, true} // 108 - , {doBackslashX, 88 /* X */, 14,0, true} // 109 - , {doBackslashZ, 90 /* Z */, 2,0, true} // 110 - , {doBackslashz, 122 /* z */, 2,0, true} // 111 - , {doBackRef, 128, 14,0, true} // 112 - , {doEscapeError, 253, 206,0, false} // 113 - , {doEscapedLiteralChar, 255, 14,0, true} // 114 - , {doBeginNamedBackRef, 60 /* < */, 117,0, true} // 115 named-backref - , {doBadNamedCapture, 255, 206,0, false} // 116 - , {doContinueNamedBackRef, 129, 119,0, true} // 117 named-backref-2 - , {doBadNamedCapture, 255, 206,0, false} // 118 - , {doContinueNamedBackRef, 129, 119,0, true} // 119 named-backref-3 - , {doContinueNamedBackRef, 128, 119,0, true} // 120 - , {doCompleteNamedBackRef, 62 /* > */, 14,0, true} // 121 - , {doBadNamedCapture, 255, 206,0, false} // 122 - , {doSetNegate, 94 /* ^ */, 126,0, true} // 123 set-open - , {doSetPosixProp, 58 /* : */, 128,0, false} // 124 - , {doNOP, 255, 126,0, false} // 125 - , {doSetLiteral, 93 /* ] */, 141,0, true} // 126 set-open2 - , {doNOP, 255, 131,0, false} // 127 - , {doSetEnd, 93 /* ] */, 255,0, true} // 128 set-posix - , {doNOP, 58 /* : */, 131,0, false} // 129 - , {doRuleError, 255, 206,0, false} // 130 - , {doSetEnd, 93 /* ] */, 255,0, true} // 131 set-start - , {doSetBeginUnion, 91 /* [ */, 123, 148, true} // 132 - , {doNOP, 92 /* \ */, 191,0, true} // 133 - , {doNOP, 45 /* - */, 137,0, true} // 134 - , {doNOP, 38 /* & */, 139,0, true} // 135 - , {doSetLiteral, 255, 141,0, true} // 136 - , {doRuleError, 45 /* - */, 206,0, false} // 137 set-start-dash - , {doSetAddDash, 255, 141,0, false} // 138 - , {doRuleError, 38 /* & */, 206,0, false} // 139 set-start-amp - , {doSetAddAmp, 255, 141,0, false} // 140 - , {doSetEnd, 93 /* ] */, 255,0, true} // 141 set-after-lit - , {doSetBeginUnion, 91 /* [ */, 123, 148, true} // 142 - , {doNOP, 45 /* - */, 178,0, true} // 143 - , {doNOP, 38 /* & */, 169,0, true} // 144 - , {doNOP, 92 /* \ */, 191,0, true} // 145 - , {doSetNoCloseError, 253, 206,0, false} // 146 - , {doSetLiteral, 255, 141,0, true} // 147 - , {doSetEnd, 93 /* ] */, 255,0, true} // 148 set-after-set - , {doSetBeginUnion, 91 /* [ */, 123, 148, true} // 149 - , {doNOP, 45 /* - */, 171,0, true} // 150 - , {doNOP, 38 /* & */, 166,0, true} // 151 - , {doNOP, 92 /* \ */, 191,0, true} // 152 - , {doSetNoCloseError, 253, 206,0, false} // 153 - , {doSetLiteral, 255, 141,0, true} // 154 - , {doSetEnd, 93 /* ] */, 255,0, true} // 155 set-after-range - , {doSetBeginUnion, 91 /* [ */, 123, 148, true} // 156 - , {doNOP, 45 /* - */, 174,0, true} // 157 - , {doNOP, 38 /* & */, 176,0, true} // 158 - , {doNOP, 92 /* \ */, 191,0, true} // 159 - , {doSetNoCloseError, 253, 206,0, false} // 160 - , {doSetLiteral, 255, 141,0, true} // 161 - , {doSetBeginUnion, 91 /* [ */, 123, 148, true} // 162 set-after-op - , {doSetOpError, 93 /* ] */, 206,0, false} // 163 - , {doNOP, 92 /* \ */, 191,0, true} // 164 - , {doSetLiteral, 255, 141,0, true} // 165 - , {doSetBeginIntersection1, 91 /* [ */, 123, 148, true} // 166 set-set-amp - , {doSetIntersection2, 38 /* & */, 162,0, true} // 167 - , {doSetAddAmp, 255, 141,0, false} // 168 - , {doSetIntersection2, 38 /* & */, 162,0, true} // 169 set-lit-amp - , {doSetAddAmp, 255, 141,0, false} // 170 - , {doSetBeginDifference1, 91 /* [ */, 123, 148, true} // 171 set-set-dash - , {doSetDifference2, 45 /* - */, 162,0, true} // 172 - , {doSetAddDash, 255, 141,0, false} // 173 - , {doSetDifference2, 45 /* - */, 162,0, true} // 174 set-range-dash - , {doSetAddDash, 255, 141,0, false} // 175 - , {doSetIntersection2, 38 /* & */, 162,0, true} // 176 set-range-amp - , {doSetAddAmp, 255, 141,0, false} // 177 - , {doSetDifference2, 45 /* - */, 162,0, true} // 178 set-lit-dash - , {doSetAddDash, 91 /* [ */, 141,0, false} // 179 - , {doSetAddDash, 93 /* ] */, 141,0, false} // 180 - , {doNOP, 92 /* \ */, 183,0, true} // 181 - , {doSetRange, 255, 155,0, true} // 182 - , {doSetOpError, 115 /* s */, 206,0, false} // 183 set-lit-dash-escape - , {doSetOpError, 83 /* S */, 206,0, false} // 184 - , {doSetOpError, 119 /* w */, 206,0, false} // 185 - , {doSetOpError, 87 /* W */, 206,0, false} // 186 - , {doSetOpError, 100 /* d */, 206,0, false} // 187 - , {doSetOpError, 68 /* D */, 206,0, false} // 188 - , {doSetNamedRange, 78 /* N */, 155,0, false} // 189 - , {doSetRange, 255, 155,0, true} // 190 - , {doSetProp, 112 /* p */, 148,0, false} // 191 set-escape - , {doSetProp, 80 /* P */, 148,0, false} // 192 - , {doSetNamedChar, 78 /* N */, 141,0, false} // 193 - , {doSetBackslash_s, 115 /* s */, 155,0, true} // 194 - , {doSetBackslash_S, 83 /* S */, 155,0, true} // 195 - , {doSetBackslash_w, 119 /* w */, 155,0, true} // 196 - , {doSetBackslash_W, 87 /* W */, 155,0, true} // 197 - , {doSetBackslash_d, 100 /* d */, 155,0, true} // 198 - , {doSetBackslash_D, 68 /* D */, 155,0, true} // 199 - , {doSetBackslash_h, 104 /* h */, 155,0, true} // 200 - , {doSetBackslash_H, 72 /* H */, 155,0, true} // 201 - , {doSetBackslash_v, 118 /* v */, 155,0, true} // 202 - , {doSetBackslash_V, 86 /* V */, 155,0, true} // 203 - , {doSetLiteralEscaped, 255, 141,0, true} // 204 - , {doSetFinish, 255, 14,0, false} // 205 set-finish - , {doExit, 255, 206,0, true} // 206 errorDeath - }; -static const char * const RegexStateNames[] = { 0, - "start", - "term", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "expr-quant", - 0, - 0, - 0, - 0, - 0, - "expr-cont", - 0, - 0, - "open-paren-quant", - 0, - "open-paren-quant2", - 0, - "open-paren", - 0, - "open-paren-extended", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "open-paren-lookbehind", - 0, - 0, - 0, - "paren-comment", - 0, - 0, - "paren-flag", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "named-capture", - 0, - 0, - 0, - "quant-star", - 0, - 0, - "quant-plus", - 0, - 0, - "quant-opt", - 0, - 0, - "interval-open", - 0, - "interval-lower", - 0, - 0, - 0, - "interval-upper", - 0, - 0, - "interval-type", - 0, - 0, - "backslash", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "named-backref", - 0, - "named-backref-2", - 0, - "named-backref-3", - 0, - 0, - 0, - "set-open", - 0, - 0, - "set-open2", - 0, - "set-posix", - 0, - 0, - "set-start", - 0, - 0, - 0, - 0, - 0, - "set-start-dash", - 0, - "set-start-amp", - 0, - "set-after-lit", - 0, - 0, - 0, - 0, - 0, - 0, - "set-after-set", - 0, - 0, - 0, - 0, - 0, - 0, - "set-after-range", - 0, - 0, - 0, - 0, - 0, - 0, - "set-after-op", - 0, - 0, - 0, - "set-set-amp", - 0, - 0, - "set-lit-amp", - 0, - "set-set-dash", - 0, - 0, - "set-range-dash", - 0, - "set-range-amp", - 0, - "set-lit-dash", - 0, - 0, - 0, - 0, - "set-lit-dash-escape", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "set-escape", - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - "set-finish", - "errorDeath", - 0}; - -U_NAMESPACE_END -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +//--------------------------------------------------------------------------------- +// +// Generated Header File. Do not edit by hand. +// This file contains the state table for the ICU Regular Expression Pattern Parser +// It is generated by the Perl script "regexcst.pl" from +// the rule parser state definitions file "regexcst.txt". +// +// Copyright (C) 2002-2016 International Business Machines Corporation +// and others. All rights reserved. +// +//--------------------------------------------------------------------------------- +#ifndef RBBIRPT_H +#define RBBIRPT_H + +#include "unicode/utypes.h" + +U_NAMESPACE_BEGIN +// +// Character classes for regex pattern scanning. +// + static const uint8_t kRuleSet_digit_char = 128; + static const uint8_t kRuleSet_ascii_letter = 129; + static const uint8_t kRuleSet_rule_char = 130; + constexpr uint32_t kRuleSet_count = 131-128; + +enum Regex_PatternParseAction { + doSetBackslash_D, + doBackslashh, + doBackslashH, + doSetLiteralEscaped, + doOpenLookAheadNeg, + doCompleteNamedBackRef, + doPatStart, + doBackslashS, + doBackslashD, + doNGStar, + doNOP, + doBackslashX, + doSetLiteral, + doContinueNamedCapture, + doBackslashG, + doBackslashR, + doSetBegin, + doSetBackslash_v, + doPossessivePlus, + doPerlInline, + doBackslashZ, + doSetAddAmp, + doSetBeginDifference1, + doIntervalError, + doSetNegate, + doIntervalInit, + doSetIntersection2, + doPossessiveInterval, + doRuleError, + doBackslashW, + doContinueNamedBackRef, + doOpenNonCaptureParen, + doExit, + doSetNamedChar, + doSetBackslash_V, + doConditionalExpr, + doEscapeError, + doBadOpenParenType, + doPossessiveStar, + doSetAddDash, + doEscapedLiteralChar, + doSetBackslash_w, + doIntervalUpperDigit, + doBackslashv, + doSetBackslash_S, + doSetNoCloseError, + doSetProp, + doBackslashB, + doSetEnd, + doSetRange, + doMatchModeParen, + doPlus, + doBackslashV, + doSetMatchMode, + doBackslashz, + doSetNamedRange, + doOpenLookBehindNeg, + doInterval, + doBadNamedCapture, + doBeginMatchMode, + doBackslashd, + doPatFinish, + doNamedChar, + doNGPlus, + doSetDifference2, + doSetBackslash_H, + doCloseParen, + doDotAny, + doOpenCaptureParen, + doEnterQuoteMode, + doOpenAtomicParen, + doBadModeFlag, + doSetBackslash_d, + doSetFinish, + doProperty, + doBeginNamedBackRef, + doBackRef, + doOpt, + doDollar, + doBeginNamedCapture, + doNGInterval, + doSetOpError, + doSetPosixProp, + doSetBeginIntersection1, + doBackslashb, + doSetBeginUnion, + doIntevalLowerDigit, + doSetBackslash_h, + doStar, + doMatchMode, + doBackslashA, + doOpenLookBehind, + doPossessiveOpt, + doOrOperator, + doBackslashw, + doBackslashs, + doLiteralChar, + doSuppressComments, + doCaret, + doIntervalSame, + doNGOpt, + doOpenLookAhead, + doSetBackslash_W, + doMismatchedParenErr, + doSetBackslash_s, + rbbiLastAction}; + +//------------------------------------------------------------------------------- +// +// RegexTableEl represents the structure of a row in the transition table +// for the pattern parser state machine. +//------------------------------------------------------------------------------- +struct RegexTableEl { + Regex_PatternParseAction fAction; + uint8_t fCharClass; // 0-127: an individual ASCII character + // 128-255: character class index + uint8_t fNextState; // 0-250: normal next-state numbers + // 255: pop next-state from stack. + uint8_t fPushState; + UBool fNextChar; +}; + +static const struct RegexTableEl gRuleParseStateTable[] = { + {doNOP, 0, 0, 0, true} + , {doPatStart, 255, 2,0, false} // 1 start + , {doLiteralChar, 254, 14,0, true} // 2 term + , {doLiteralChar, 130, 14,0, true} // 3 + , {doSetBegin, 91 /* [ */, 123, 205, true} // 4 + , {doNOP, 40 /* ( */, 27,0, true} // 5 + , {doDotAny, 46 /* . */, 14,0, true} // 6 + , {doCaret, 94 /* ^ */, 14,0, true} // 7 + , {doDollar, 36 /* $ */, 14,0, true} // 8 + , {doNOP, 92 /* \ */, 89,0, true} // 9 + , {doOrOperator, 124 /* | */, 2,0, true} // 10 + , {doCloseParen, 41 /* ) */, 255,0, true} // 11 + , {doPatFinish, 253, 2,0, false} // 12 + , {doRuleError, 255, 206,0, false} // 13 + , {doNOP, 42 /* * */, 68,0, true} // 14 expr-quant + , {doNOP, 43 /* + */, 71,0, true} // 15 + , {doNOP, 63 /* ? */, 74,0, true} // 16 + , {doIntervalInit, 123 /* { */, 77,0, true} // 17 + , {doNOP, 40 /* ( */, 23,0, true} // 18 + , {doNOP, 255, 20,0, false} // 19 + , {doOrOperator, 124 /* | */, 2,0, true} // 20 expr-cont + , {doCloseParen, 41 /* ) */, 255,0, true} // 21 + , {doNOP, 255, 2,0, false} // 22 + , {doSuppressComments, 63 /* ? */, 25,0, true} // 23 open-paren-quant + , {doNOP, 255, 27,0, false} // 24 + , {doNOP, 35 /* # */, 50, 14, true} // 25 open-paren-quant2 + , {doNOP, 255, 29,0, false} // 26 + , {doSuppressComments, 63 /* ? */, 29,0, true} // 27 open-paren + , {doOpenCaptureParen, 255, 2, 14, false} // 28 + , {doOpenNonCaptureParen, 58 /* : */, 2, 14, true} // 29 open-paren-extended + , {doOpenAtomicParen, 62 /* > */, 2, 14, true} // 30 + , {doOpenLookAhead, 61 /* = */, 2, 20, true} // 31 + , {doOpenLookAheadNeg, 33 /* ! */, 2, 20, true} // 32 + , {doNOP, 60 /* < */, 46,0, true} // 33 + , {doNOP, 35 /* # */, 50, 2, true} // 34 + , {doBeginMatchMode, 105 /* i */, 53,0, false} // 35 + , {doBeginMatchMode, 100 /* d */, 53,0, false} // 36 + , {doBeginMatchMode, 109 /* m */, 53,0, false} // 37 + , {doBeginMatchMode, 115 /* s */, 53,0, false} // 38 + , {doBeginMatchMode, 117 /* u */, 53,0, false} // 39 + , {doBeginMatchMode, 119 /* w */, 53,0, false} // 40 + , {doBeginMatchMode, 120 /* x */, 53,0, false} // 41 + , {doBeginMatchMode, 45 /* - */, 53,0, false} // 42 + , {doConditionalExpr, 40 /* ( */, 206,0, true} // 43 + , {doPerlInline, 123 /* { */, 206,0, true} // 44 + , {doBadOpenParenType, 255, 206,0, false} // 45 + , {doOpenLookBehind, 61 /* = */, 2, 20, true} // 46 open-paren-lookbehind + , {doOpenLookBehindNeg, 33 /* ! */, 2, 20, true} // 47 + , {doBeginNamedCapture, 129, 64,0, false} // 48 + , {doBadOpenParenType, 255, 206,0, false} // 49 + , {doNOP, 41 /* ) */, 255,0, true} // 50 paren-comment + , {doMismatchedParenErr, 253, 206,0, false} // 51 + , {doNOP, 255, 50,0, true} // 52 + , {doMatchMode, 105 /* i */, 53,0, true} // 53 paren-flag + , {doMatchMode, 100 /* d */, 53,0, true} // 54 + , {doMatchMode, 109 /* m */, 53,0, true} // 55 + , {doMatchMode, 115 /* s */, 53,0, true} // 56 + , {doMatchMode, 117 /* u */, 53,0, true} // 57 + , {doMatchMode, 119 /* w */, 53,0, true} // 58 + , {doMatchMode, 120 /* x */, 53,0, true} // 59 + , {doMatchMode, 45 /* - */, 53,0, true} // 60 + , {doSetMatchMode, 41 /* ) */, 2,0, true} // 61 + , {doMatchModeParen, 58 /* : */, 2, 14, true} // 62 + , {doBadModeFlag, 255, 206,0, false} // 63 + , {doContinueNamedCapture, 129, 64,0, true} // 64 named-capture + , {doContinueNamedCapture, 128, 64,0, true} // 65 + , {doOpenCaptureParen, 62 /* > */, 2, 14, true} // 66 + , {doBadNamedCapture, 255, 206,0, false} // 67 + , {doNGStar, 63 /* ? */, 20,0, true} // 68 quant-star + , {doPossessiveStar, 43 /* + */, 20,0, true} // 69 + , {doStar, 255, 20,0, false} // 70 + , {doNGPlus, 63 /* ? */, 20,0, true} // 71 quant-plus + , {doPossessivePlus, 43 /* + */, 20,0, true} // 72 + , {doPlus, 255, 20,0, false} // 73 + , {doNGOpt, 63 /* ? */, 20,0, true} // 74 quant-opt + , {doPossessiveOpt, 43 /* + */, 20,0, true} // 75 + , {doOpt, 255, 20,0, false} // 76 + , {doNOP, 128, 79,0, false} // 77 interval-open + , {doIntervalError, 255, 206,0, false} // 78 + , {doIntevalLowerDigit, 128, 79,0, true} // 79 interval-lower + , {doNOP, 44 /* , */, 83,0, true} // 80 + , {doIntervalSame, 125 /* } */, 86,0, true} // 81 + , {doIntervalError, 255, 206,0, false} // 82 + , {doIntervalUpperDigit, 128, 83,0, true} // 83 interval-upper + , {doNOP, 125 /* } */, 86,0, true} // 84 + , {doIntervalError, 255, 206,0, false} // 85 + , {doNGInterval, 63 /* ? */, 20,0, true} // 86 interval-type + , {doPossessiveInterval, 43 /* + */, 20,0, true} // 87 + , {doInterval, 255, 20,0, false} // 88 + , {doBackslashA, 65 /* A */, 2,0, true} // 89 backslash + , {doBackslashB, 66 /* B */, 2,0, true} // 90 + , {doBackslashb, 98 /* b */, 2,0, true} // 91 + , {doBackslashd, 100 /* d */, 14,0, true} // 92 + , {doBackslashD, 68 /* D */, 14,0, true} // 93 + , {doBackslashG, 71 /* G */, 2,0, true} // 94 + , {doBackslashh, 104 /* h */, 14,0, true} // 95 + , {doBackslashH, 72 /* H */, 14,0, true} // 96 + , {doNOP, 107 /* k */, 115,0, true} // 97 + , {doNamedChar, 78 /* N */, 14,0, false} // 98 + , {doProperty, 112 /* p */, 14,0, false} // 99 + , {doProperty, 80 /* P */, 14,0, false} // 100 + , {doBackslashR, 82 /* R */, 14,0, true} // 101 + , {doEnterQuoteMode, 81 /* Q */, 2,0, true} // 102 + , {doBackslashS, 83 /* S */, 14,0, true} // 103 + , {doBackslashs, 115 /* s */, 14,0, true} // 104 + , {doBackslashv, 118 /* v */, 14,0, true} // 105 + , {doBackslashV, 86 /* V */, 14,0, true} // 106 + , {doBackslashW, 87 /* W */, 14,0, true} // 107 + , {doBackslashw, 119 /* w */, 14,0, true} // 108 + , {doBackslashX, 88 /* X */, 14,0, true} // 109 + , {doBackslashZ, 90 /* Z */, 2,0, true} // 110 + , {doBackslashz, 122 /* z */, 2,0, true} // 111 + , {doBackRef, 128, 14,0, true} // 112 + , {doEscapeError, 253, 206,0, false} // 113 + , {doEscapedLiteralChar, 255, 14,0, true} // 114 + , {doBeginNamedBackRef, 60 /* < */, 117,0, true} // 115 named-backref + , {doBadNamedCapture, 255, 206,0, false} // 116 + , {doContinueNamedBackRef, 129, 119,0, true} // 117 named-backref-2 + , {doBadNamedCapture, 255, 206,0, false} // 118 + , {doContinueNamedBackRef, 129, 119,0, true} // 119 named-backref-3 + , {doContinueNamedBackRef, 128, 119,0, true} // 120 + , {doCompleteNamedBackRef, 62 /* > */, 14,0, true} // 121 + , {doBadNamedCapture, 255, 206,0, false} // 122 + , {doSetNegate, 94 /* ^ */, 126,0, true} // 123 set-open + , {doSetPosixProp, 58 /* : */, 128,0, false} // 124 + , {doNOP, 255, 126,0, false} // 125 + , {doSetLiteral, 93 /* ] */, 141,0, true} // 126 set-open2 + , {doNOP, 255, 131,0, false} // 127 + , {doSetEnd, 93 /* ] */, 255,0, true} // 128 set-posix + , {doNOP, 58 /* : */, 131,0, false} // 129 + , {doRuleError, 255, 206,0, false} // 130 + , {doSetEnd, 93 /* ] */, 255,0, true} // 131 set-start + , {doSetBeginUnion, 91 /* [ */, 123, 148, true} // 132 + , {doNOP, 92 /* \ */, 191,0, true} // 133 + , {doNOP, 45 /* - */, 137,0, true} // 134 + , {doNOP, 38 /* & */, 139,0, true} // 135 + , {doSetLiteral, 255, 141,0, true} // 136 + , {doRuleError, 45 /* - */, 206,0, false} // 137 set-start-dash + , {doSetAddDash, 255, 141,0, false} // 138 + , {doRuleError, 38 /* & */, 206,0, false} // 139 set-start-amp + , {doSetAddAmp, 255, 141,0, false} // 140 + , {doSetEnd, 93 /* ] */, 255,0, true} // 141 set-after-lit + , {doSetBeginUnion, 91 /* [ */, 123, 148, true} // 142 + , {doNOP, 45 /* - */, 178,0, true} // 143 + , {doNOP, 38 /* & */, 169,0, true} // 144 + , {doNOP, 92 /* \ */, 191,0, true} // 145 + , {doSetNoCloseError, 253, 206,0, false} // 146 + , {doSetLiteral, 255, 141,0, true} // 147 + , {doSetEnd, 93 /* ] */, 255,0, true} // 148 set-after-set + , {doSetBeginUnion, 91 /* [ */, 123, 148, true} // 149 + , {doNOP, 45 /* - */, 171,0, true} // 150 + , {doNOP, 38 /* & */, 166,0, true} // 151 + , {doNOP, 92 /* \ */, 191,0, true} // 152 + , {doSetNoCloseError, 253, 206,0, false} // 153 + , {doSetLiteral, 255, 141,0, true} // 154 + , {doSetEnd, 93 /* ] */, 255,0, true} // 155 set-after-range + , {doSetBeginUnion, 91 /* [ */, 123, 148, true} // 156 + , {doNOP, 45 /* - */, 174,0, true} // 157 + , {doNOP, 38 /* & */, 176,0, true} // 158 + , {doNOP, 92 /* \ */, 191,0, true} // 159 + , {doSetNoCloseError, 253, 206,0, false} // 160 + , {doSetLiteral, 255, 141,0, true} // 161 + , {doSetBeginUnion, 91 /* [ */, 123, 148, true} // 162 set-after-op + , {doSetOpError, 93 /* ] */, 206,0, false} // 163 + , {doNOP, 92 /* \ */, 191,0, true} // 164 + , {doSetLiteral, 255, 141,0, true} // 165 + , {doSetBeginIntersection1, 91 /* [ */, 123, 148, true} // 166 set-set-amp + , {doSetIntersection2, 38 /* & */, 162,0, true} // 167 + , {doSetAddAmp, 255, 141,0, false} // 168 + , {doSetIntersection2, 38 /* & */, 162,0, true} // 169 set-lit-amp + , {doSetAddAmp, 255, 141,0, false} // 170 + , {doSetBeginDifference1, 91 /* [ */, 123, 148, true} // 171 set-set-dash + , {doSetDifference2, 45 /* - */, 162,0, true} // 172 + , {doSetAddDash, 255, 141,0, false} // 173 + , {doSetDifference2, 45 /* - */, 162,0, true} // 174 set-range-dash + , {doSetAddDash, 255, 141,0, false} // 175 + , {doSetIntersection2, 38 /* & */, 162,0, true} // 176 set-range-amp + , {doSetAddAmp, 255, 141,0, false} // 177 + , {doSetDifference2, 45 /* - */, 162,0, true} // 178 set-lit-dash + , {doSetAddDash, 91 /* [ */, 141,0, false} // 179 + , {doSetAddDash, 93 /* ] */, 141,0, false} // 180 + , {doNOP, 92 /* \ */, 183,0, true} // 181 + , {doSetRange, 255, 155,0, true} // 182 + , {doSetOpError, 115 /* s */, 206,0, false} // 183 set-lit-dash-escape + , {doSetOpError, 83 /* S */, 206,0, false} // 184 + , {doSetOpError, 119 /* w */, 206,0, false} // 185 + , {doSetOpError, 87 /* W */, 206,0, false} // 186 + , {doSetOpError, 100 /* d */, 206,0, false} // 187 + , {doSetOpError, 68 /* D */, 206,0, false} // 188 + , {doSetNamedRange, 78 /* N */, 155,0, false} // 189 + , {doSetRange, 255, 155,0, true} // 190 + , {doSetProp, 112 /* p */, 148,0, false} // 191 set-escape + , {doSetProp, 80 /* P */, 148,0, false} // 192 + , {doSetNamedChar, 78 /* N */, 141,0, false} // 193 + , {doSetBackslash_s, 115 /* s */, 155,0, true} // 194 + , {doSetBackslash_S, 83 /* S */, 155,0, true} // 195 + , {doSetBackslash_w, 119 /* w */, 155,0, true} // 196 + , {doSetBackslash_W, 87 /* W */, 155,0, true} // 197 + , {doSetBackslash_d, 100 /* d */, 155,0, true} // 198 + , {doSetBackslash_D, 68 /* D */, 155,0, true} // 199 + , {doSetBackslash_h, 104 /* h */, 155,0, true} // 200 + , {doSetBackslash_H, 72 /* H */, 155,0, true} // 201 + , {doSetBackslash_v, 118 /* v */, 155,0, true} // 202 + , {doSetBackslash_V, 86 /* V */, 155,0, true} // 203 + , {doSetLiteralEscaped, 255, 141,0, true} // 204 + , {doSetFinish, 255, 14,0, false} // 205 set-finish + , {doExit, 255, 206,0, true} // 206 errorDeath + }; +static const char * const RegexStateNames[] = { 0, + "start", + "term", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "expr-quant", + 0, + 0, + 0, + 0, + 0, + "expr-cont", + 0, + 0, + "open-paren-quant", + 0, + "open-paren-quant2", + 0, + "open-paren", + 0, + "open-paren-extended", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "open-paren-lookbehind", + 0, + 0, + 0, + "paren-comment", + 0, + 0, + "paren-flag", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "named-capture", + 0, + 0, + 0, + "quant-star", + 0, + 0, + "quant-plus", + 0, + 0, + "quant-opt", + 0, + 0, + "interval-open", + 0, + "interval-lower", + 0, + 0, + 0, + "interval-upper", + 0, + 0, + "interval-type", + 0, + 0, + "backslash", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "named-backref", + 0, + "named-backref-2", + 0, + "named-backref-3", + 0, + 0, + 0, + "set-open", + 0, + 0, + "set-open2", + 0, + "set-posix", + 0, + 0, + "set-start", + 0, + 0, + 0, + 0, + 0, + "set-start-dash", + 0, + "set-start-amp", + 0, + "set-after-lit", + 0, + 0, + 0, + 0, + 0, + 0, + "set-after-set", + 0, + 0, + 0, + 0, + 0, + 0, + "set-after-range", + 0, + 0, + 0, + 0, + 0, + 0, + "set-after-op", + 0, + 0, + 0, + "set-set-amp", + 0, + 0, + "set-lit-amp", + 0, + "set-set-dash", + 0, + 0, + "set-range-dash", + 0, + "set-range-amp", + 0, + "set-lit-dash", + 0, + 0, + 0, + 0, + "set-lit-dash-escape", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "set-escape", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "set-finish", + "errorDeath", + 0}; + +U_NAMESPACE_END +#endif diff --git a/deps/icu-small/source/i18n/regexcst.pl b/deps/icu-small/source/i18n/regexcst.pl old mode 100755 new mode 100644 index 24596d4122d26e..f252673c3b071a --- a/deps/icu-small/source/i18n/regexcst.pl +++ b/deps/icu-small/source/i18n/regexcst.pl @@ -1,335 +1,335 @@ -#!/usr/bin/perl -# Copyright (C) 2016 and later: Unicode, Inc. and others. -# License & terms of use: http://www.unicode.org/copyright.html -# ******************************************************************** -# * COPYRIGHT: -# * Copyright (c) 2002-2016, International Business Machines Corporation and -# * others. All Rights Reserved. -# ******************************************************************** -# -# regexcst.pl -# Compile the regular expression parser state table data into initialized C data. -# Usage: -# cd icu4c/source/i18n -# perl regexcst.pl < regexcst.txt > regexcst.h -# -# The output file, regexcst.h, is included by some of the .cpp regex -# implementation files. This perl script is NOT run as part -# of a normal ICU build. It is run by hand when needed, and the -# regexcst.h generated file is put back into the source code repository. -# -# See regexcst.txt for a description of the input format for this script. -# -# This script is derived from rbbicst.pl, which peforms the same function -# for the Rule Based Break Iterator Rule Parser. Perhaps they could be -# merged? -# - - -$num_states = 1; # Always the state number for the line being compiled. -$line_num = 0; # The line number in the input file. - -$states{"pop"} = 255; # Add the "pop" to the list of defined state names. - # This prevents any state from being labelled with "pop", - # and resolves references to "pop" in the next state field. - -line_loop: while (<>) { - chomp(); - $line = $_; - @fields = split(); - $line_num++; - - # Remove # comments, which are any fields beginning with a #, plus all - # that follow on the line. - for ($i=0; $i<@fields; $i++) { - if ($fields[$i] =~ /^#/) { - @fields = @fields[0 .. $i-1]; - last; - } - } - # ignore blank lines, and those with no fields left after stripping comments.. - if (@fields == 0) { - next; - } - - # - # State Label: handling. - # Does the first token end with a ":"? If so, it's the name of a state. - # Put in a hash, together with the current state number, - # so that we can later look up the number from the name. - # - if (@fields[0] =~ /.*:$/) { - $state_name = @fields[0]; - $state_name =~ s/://; # strip off the colon from the state name. - - if ($states{$state_name} != 0) { - print " rbbicst: at line $line-num duplicate definition of state $state_name\n"; - } - $states{$state_name} = $num_states; - $stateNames[$num_states] = $state_name; - - # if the label was the only thing on this line, go on to the next line, - # otherwise assume that a state definition is on the same line and fall through. - if (@fields == 1) { - next line_loop; - } - shift @fields; # shift off label field in preparation - # for handling the rest of the line. - } - - # - # State Transition line. - # syntax is this, - # character [n] target-state [^push-state] [function-name] - # where - # [something] is an optional something - # character is either a single quoted character e.g. '[' - # or a name of a character class, e.g. white_space - # - - $state_line_num[$num_states] = $line_num; # remember line number with each state - # so we can make better error messages later. - # - # First field, character class or literal character for this transition. - # - if ($fields[0] =~ /^'.'$/) { - # We've got a quoted literal character. - $state_literal_chars[$num_states] = $fields[0]; - $state_literal_chars[$num_states] =~ s/'//g; - } else { - # We've got the name of a character class. - $state_char_class[$num_states] = $fields[0]; - if ($fields[0] =~ /[\W]/) { - print " rbbicsts: at line $line_num, bad character literal or character class name.\n"; - print " scanning $fields[0]\n"; - exit(-1); - } - } - shift @fields; - - # - # do the 'n' flag - # - $state_flag[$num_states] = "false"; - if ($fields[0] eq "n") { - $state_flag[$num_states] = "true"; - shift @fields; - } - - # - # do the destination state. - # - $state_dest_state[$num_states] = $fields[0]; - if ($fields[0] eq "") { - print " rbbicsts: at line $line_num, destination state missing.\n"; - exit(-1); - } - shift @fields; - - # - # do the push state, if present. - # - if ($fields[0] =~ /^\^/) { - $fields[0] =~ s/^\^//; - $state_push_state[$num_states] = $fields[0]; - if ($fields[0] eq "" ) { - print " rbbicsts: at line $line_num, expected state after ^ (no spaces).\n"; - exit(-1); - } - shift @fields; - } - - # - # Lastly, do the optional action name. - # - if ($fields[0] ne "") { - $state_func_name[$num_states] = $fields[0]; - shift @fields; - } - - # - # There should be no fields left on the line at this point. - # - if (@fields > 0) { - print " rbbicsts: at line $line_num, unexpected extra stuff on input line.\n"; - print " scanning $fields[0]\n"; - } - $num_states++; -} - -# -# We've read in the whole file, now go back and output the -# C source code for the state transition table. -# -# We read all states first, before writing anything, so that the state numbers -# for the destination states are all available to be written. -# - -# -# Make hashes for the names of the character classes and -# for the names of the actions that appeared. -# -for ($state=1; $state < $num_states; $state++) { - if ($state_char_class[$state] ne "") { - if ($charClasses{$state_char_class[$state]} == 0) { - $charClasses{$state_char_class[$state]} = 1; - } - } - if ($state_func_name[$state] eq "") { - $state_func_name[$state] = "doNOP"; - } - if ($actions{$state_action_name[$state]} == 0) { - $actions{$state_func_name[$state]} = 1; - } -} - -# -# Check that all of the destination states have been defined -# -# -$states{"exit"} = 0; # Predefined state name, terminates state machine. -for ($state=1; $state<$num_states; $state++) { - if ($states{$state_dest_state[$state]} == 0 && $state_dest_state[$state] ne "exit") { - print "Error at line $state_line_num[$state]: target state \"$state_dest_state[$state]\" is not defined.\n"; - $errors++; - } - if ($state_push_state[$state] ne "" && $states{$state_push_state[$state]} == 0) { - print "Error at line $state_line_num[$state]: target state \"$state_push_state[$state]\" is not defined.\n"; - $errors++; - } -} - -die if ($errors>0); - -print "// © 2016 and later: Unicode, Inc. and others.\n"; -print "// License & terms of use: http://www.unicode.org/copyright.html\n"; -print "//---------------------------------------------------------------------------------\n"; -print "//\n"; -print "// Generated Header File. Do not edit by hand.\n"; -print "// This file contains the state table for the ICU Regular Expression Pattern Parser\n"; -print "// It is generated by the Perl script \"regexcst.pl\" from\n"; -print "// the rule parser state definitions file \"regexcst.txt\".\n"; -print "//\n"; -print "// Copyright (C) 2002-2016 International Business Machines Corporation \n"; -print "// and others. All rights reserved. \n"; -print "//\n"; -print "//---------------------------------------------------------------------------------\n"; -print "#ifndef RBBIRPT_H\n"; -print "#define RBBIRPT_H\n"; -print "\n"; -print "#include \"unicode/utypes.h\"\n"; -print "\n"; -print "U_NAMESPACE_BEGIN\n"; - -# -# Emit the constants for indices of Unicode Sets -# Define one constant for each of the character classes encountered. -# At the same time, store the index corresponding to the set name back into hash. -# -print "//\n"; -print "// Character classes for regex pattern scanning.\n"; -print "//\n"; -$i = 128; # State Table values for Unicode char sets range from 128-250. - # Sets "default", "quoted", etc. get special handling. - # They have no corresponding UnicodeSet object in the state machine, - # but are handled by special case code. So we emit no reference - # to a UnicodeSet object to them here. -foreach $setName (keys %charClasses) { - if ($setName eq "default") { - $charClasses{$setName} = 255;} - elsif ($setName eq "quoted") { - $charClasses{$setName} = 254;} - elsif ($setName eq "eof") { - $charClasses{$setName} = 253;} - else { - # Normal character class. Fill in array with a ptr to the corresponding UnicodeSet in the state machine. - print " static const uint8_t kRuleSet_$setName = $i;\n"; - $charClasses{$setName} = $i; - $i++; - } -} -print " constexpr uint32_t kRuleSet_count = $i-128;"; -print "\n\n"; - -# -# Emit the enum for the actions to be performed. -# -print "enum Regex_PatternParseAction {\n"; -foreach $act (keys %actions) { - print " $act,\n"; -} -print " rbbiLastAction};\n\n"; - -# -# Emit the struct definition for transition table elements. -# -print "//-------------------------------------------------------------------------------\n"; -print "//\n"; -print "// RegexTableEl represents the structure of a row in the transition table\n"; -print "// for the pattern parser state machine.\n"; -print "//-------------------------------------------------------------------------------\n"; -print "struct RegexTableEl {\n"; -print " Regex_PatternParseAction fAction;\n"; -print " uint8_t fCharClass; // 0-127: an individual ASCII character\n"; -print " // 128-255: character class index\n"; -print " uint8_t fNextState; // 0-250: normal next-state numbers\n"; -print " // 255: pop next-state from stack.\n"; -print " uint8_t fPushState;\n"; -print " UBool fNextChar;\n"; -print "};\n\n"; - -# -# emit the state transition table -# -print "static const struct RegexTableEl gRuleParseStateTable[] = {\n"; -print " {doNOP, 0, 0, 0, true}\n"; # State 0 is a dummy. Real states start with index = 1. -for ($state=1; $state < $num_states; $state++) { - print " , {$state_func_name[$state],"; - if ($state_literal_chars[$state] ne "") { - $c = $state_literal_chars[$state]; - printf(" %d /* $c */,", ord($c)); # use numeric value, so EBCDIC machines are ok. - }else { - print " $charClasses{$state_char_class[$state]},"; - } - print " $states{$state_dest_state[$state]},"; - - # The push-state field is optional. If omitted, fill field with a zero, which flags - # the state machine that there is no push state. - if ($state_push_state[$state] eq "") { - print "0, "; - } else { - print " $states{$state_push_state[$state]},"; - } - print " $state_flag[$state]} "; - - # Put out a C++ comment showing the number (index) of this state row, - # and, if this is the first row of the table for this state, the state name. - print " // $state "; - if ($stateNames[$state] ne "") { - print " $stateNames[$state]"; - } - print "\n"; -}; -print " };\n"; - - -# -# emit a mapping array from state numbers to state names. -# -# This array is used for producing debugging output from the pattern parser. -# -print "static const char * const RegexStateNames[] = {"; -for ($state=0; $state<$num_states; $state++) { - if ($stateNames[$state] ne "") { - print " \"$stateNames[$state]\",\n"; - } else { - print " 0,\n"; - } -} -print " 0};\n\n"; - -print "U_NAMESPACE_END\n"; -print "#endif\n"; - - - +#!/usr/bin/perl +# Copyright (C) 2016 and later: Unicode, Inc. and others. +# License & terms of use: http://www.unicode.org/copyright.html +# ******************************************************************** +# * COPYRIGHT: +# * Copyright (c) 2002-2016, International Business Machines Corporation and +# * others. All Rights Reserved. +# ******************************************************************** +# +# regexcst.pl +# Compile the regular expression parser state table data into initialized C data. +# Usage: +# cd icu4c/source/i18n +# perl regexcst.pl < regexcst.txt > regexcst.h +# +# The output file, regexcst.h, is included by some of the .cpp regex +# implementation files. This perl script is NOT run as part +# of a normal ICU build. It is run by hand when needed, and the +# regexcst.h generated file is put back into the source code repository. +# +# See regexcst.txt for a description of the input format for this script. +# +# This script is derived from rbbicst.pl, which peforms the same function +# for the Rule Based Break Iterator Rule Parser. Perhaps they could be +# merged? +# + + +$num_states = 1; # Always the state number for the line being compiled. +$line_num = 0; # The line number in the input file. + +$states{"pop"} = 255; # Add the "pop" to the list of defined state names. + # This prevents any state from being labelled with "pop", + # and resolves references to "pop" in the next state field. + +line_loop: while (<>) { + chomp(); + $line = $_; + @fields = split(); + $line_num++; + + # Remove # comments, which are any fields beginning with a #, plus all + # that follow on the line. + for ($i=0; $i<@fields; $i++) { + if ($fields[$i] =~ /^#/) { + @fields = @fields[0 .. $i-1]; + last; + } + } + # ignore blank lines, and those with no fields left after stripping comments.. + if (@fields == 0) { + next; + } + + # + # State Label: handling. + # Does the first token end with a ":"? If so, it's the name of a state. + # Put in a hash, together with the current state number, + # so that we can later look up the number from the name. + # + if (@fields[0] =~ /.*:$/) { + $state_name = @fields[0]; + $state_name =~ s/://; # strip off the colon from the state name. + + if ($states{$state_name} != 0) { + print " rbbicst: at line $line-num duplicate definition of state $state_name\n"; + } + $states{$state_name} = $num_states; + $stateNames[$num_states] = $state_name; + + # if the label was the only thing on this line, go on to the next line, + # otherwise assume that a state definition is on the same line and fall through. + if (@fields == 1) { + next line_loop; + } + shift @fields; # shift off label field in preparation + # for handling the rest of the line. + } + + # + # State Transition line. + # syntax is this, + # character [n] target-state [^push-state] [function-name] + # where + # [something] is an optional something + # character is either a single quoted character e.g. '[' + # or a name of a character class, e.g. white_space + # + + $state_line_num[$num_states] = $line_num; # remember line number with each state + # so we can make better error messages later. + # + # First field, character class or literal character for this transition. + # + if ($fields[0] =~ /^'.'$/) { + # We've got a quoted literal character. + $state_literal_chars[$num_states] = $fields[0]; + $state_literal_chars[$num_states] =~ s/'//g; + } else { + # We've got the name of a character class. + $state_char_class[$num_states] = $fields[0]; + if ($fields[0] =~ /[\W]/) { + print " rbbicsts: at line $line_num, bad character literal or character class name.\n"; + print " scanning $fields[0]\n"; + exit(-1); + } + } + shift @fields; + + # + # do the 'n' flag + # + $state_flag[$num_states] = "false"; + if ($fields[0] eq "n") { + $state_flag[$num_states] = "true"; + shift @fields; + } + + # + # do the destination state. + # + $state_dest_state[$num_states] = $fields[0]; + if ($fields[0] eq "") { + print " rbbicsts: at line $line_num, destination state missing.\n"; + exit(-1); + } + shift @fields; + + # + # do the push state, if present. + # + if ($fields[0] =~ /^\^/) { + $fields[0] =~ s/^\^//; + $state_push_state[$num_states] = $fields[0]; + if ($fields[0] eq "" ) { + print " rbbicsts: at line $line_num, expected state after ^ (no spaces).\n"; + exit(-1); + } + shift @fields; + } + + # + # Lastly, do the optional action name. + # + if ($fields[0] ne "") { + $state_func_name[$num_states] = $fields[0]; + shift @fields; + } + + # + # There should be no fields left on the line at this point. + # + if (@fields > 0) { + print " rbbicsts: at line $line_num, unexpected extra stuff on input line.\n"; + print " scanning $fields[0]\n"; + } + $num_states++; +} + +# +# We've read in the whole file, now go back and output the +# C source code for the state transition table. +# +# We read all states first, before writing anything, so that the state numbers +# for the destination states are all available to be written. +# + +# +# Make hashes for the names of the character classes and +# for the names of the actions that appeared. +# +for ($state=1; $state < $num_states; $state++) { + if ($state_char_class[$state] ne "") { + if ($charClasses{$state_char_class[$state]} == 0) { + $charClasses{$state_char_class[$state]} = 1; + } + } + if ($state_func_name[$state] eq "") { + $state_func_name[$state] = "doNOP"; + } + if ($actions{$state_action_name[$state]} == 0) { + $actions{$state_func_name[$state]} = 1; + } +} + +# +# Check that all of the destination states have been defined +# +# +$states{"exit"} = 0; # Predefined state name, terminates state machine. +for ($state=1; $state<$num_states; $state++) { + if ($states{$state_dest_state[$state]} == 0 && $state_dest_state[$state] ne "exit") { + print "Error at line $state_line_num[$state]: target state \"$state_dest_state[$state]\" is not defined.\n"; + $errors++; + } + if ($state_push_state[$state] ne "" && $states{$state_push_state[$state]} == 0) { + print "Error at line $state_line_num[$state]: target state \"$state_push_state[$state]\" is not defined.\n"; + $errors++; + } +} + +die if ($errors>0); + +print "// © 2016 and later: Unicode, Inc. and others.\n"; +print "// License & terms of use: http://www.unicode.org/copyright.html\n"; +print "//---------------------------------------------------------------------------------\n"; +print "//\n"; +print "// Generated Header File. Do not edit by hand.\n"; +print "// This file contains the state table for the ICU Regular Expression Pattern Parser\n"; +print "// It is generated by the Perl script \"regexcst.pl\" from\n"; +print "// the rule parser state definitions file \"regexcst.txt\".\n"; +print "//\n"; +print "// Copyright (C) 2002-2016 International Business Machines Corporation \n"; +print "// and others. All rights reserved. \n"; +print "//\n"; +print "//---------------------------------------------------------------------------------\n"; +print "#ifndef RBBIRPT_H\n"; +print "#define RBBIRPT_H\n"; +print "\n"; +print "#include \"unicode/utypes.h\"\n"; +print "\n"; +print "U_NAMESPACE_BEGIN\n"; + +# +# Emit the constants for indices of Unicode Sets +# Define one constant for each of the character classes encountered. +# At the same time, store the index corresponding to the set name back into hash. +# +print "//\n"; +print "// Character classes for regex pattern scanning.\n"; +print "//\n"; +$i = 128; # State Table values for Unicode char sets range from 128-250. + # Sets "default", "quoted", etc. get special handling. + # They have no corresponding UnicodeSet object in the state machine, + # but are handled by special case code. So we emit no reference + # to a UnicodeSet object to them here. +foreach $setName (keys %charClasses) { + if ($setName eq "default") { + $charClasses{$setName} = 255;} + elsif ($setName eq "quoted") { + $charClasses{$setName} = 254;} + elsif ($setName eq "eof") { + $charClasses{$setName} = 253;} + else { + # Normal character class. Fill in array with a ptr to the corresponding UnicodeSet in the state machine. + print " static const uint8_t kRuleSet_$setName = $i;\n"; + $charClasses{$setName} = $i; + $i++; + } +} +print " constexpr uint32_t kRuleSet_count = $i-128;"; +print "\n\n"; + +# +# Emit the enum for the actions to be performed. +# +print "enum Regex_PatternParseAction {\n"; +foreach $act (keys %actions) { + print " $act,\n"; +} +print " rbbiLastAction};\n\n"; + +# +# Emit the struct definition for transition table elements. +# +print "//-------------------------------------------------------------------------------\n"; +print "//\n"; +print "// RegexTableEl represents the structure of a row in the transition table\n"; +print "// for the pattern parser state machine.\n"; +print "//-------------------------------------------------------------------------------\n"; +print "struct RegexTableEl {\n"; +print " Regex_PatternParseAction fAction;\n"; +print " uint8_t fCharClass; // 0-127: an individual ASCII character\n"; +print " // 128-255: character class index\n"; +print " uint8_t fNextState; // 0-250: normal next-state numbers\n"; +print " // 255: pop next-state from stack.\n"; +print " uint8_t fPushState;\n"; +print " UBool fNextChar;\n"; +print "};\n\n"; + +# +# emit the state transition table +# +print "static const struct RegexTableEl gRuleParseStateTable[] = {\n"; +print " {doNOP, 0, 0, 0, true}\n"; # State 0 is a dummy. Real states start with index = 1. +for ($state=1; $state < $num_states; $state++) { + print " , {$state_func_name[$state],"; + if ($state_literal_chars[$state] ne "") { + $c = $state_literal_chars[$state]; + printf(" %d /* $c */,", ord($c)); # use numeric value, so EBCDIC machines are ok. + }else { + print " $charClasses{$state_char_class[$state]},"; + } + print " $states{$state_dest_state[$state]},"; + + # The push-state field is optional. If omitted, fill field with a zero, which flags + # the state machine that there is no push state. + if ($state_push_state[$state] eq "") { + print "0, "; + } else { + print " $states{$state_push_state[$state]},"; + } + print " $state_flag[$state]} "; + + # Put out a C++ comment showing the number (index) of this state row, + # and, if this is the first row of the table for this state, the state name. + print " // $state "; + if ($stateNames[$state] ne "") { + print " $stateNames[$state]"; + } + print "\n"; +}; +print " };\n"; + + +# +# emit a mapping array from state numbers to state names. +# +# This array is used for producing debugging output from the pattern parser. +# +print "static const char * const RegexStateNames[] = {"; +for ($state=0; $state<$num_states; $state++) { + if ($stateNames[$state] ne "") { + print " \"$stateNames[$state]\",\n"; + } else { + print " 0,\n"; + } +} +print " 0};\n\n"; + +print "U_NAMESPACE_END\n"; +print "#endif\n"; + + + diff --git a/deps/icu-small/source/i18n/regeximp.cpp b/deps/icu-small/source/i18n/regeximp.cpp index d55566962587e4..354ffbf9bfe70a 100644 --- a/deps/icu-small/source/i18n/regeximp.cpp +++ b/deps/icu-small/source/i18n/regeximp.cpp @@ -1,120 +1,120 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// Copyright (C) 2012 International Business Machines Corporation -// and others. All rights reserved. -// -// file: regeximp.cpp -// -// ICU Regular Expressions, -// miscellaneous implementation functions. -// - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_REGULAR_EXPRESSIONS -#include "regeximp.h" -#include "unicode/utf16.h" - -U_NAMESPACE_BEGIN - -CaseFoldingUTextIterator::CaseFoldingUTextIterator(UText &text) : - fUText(text), fFoldChars(NULL), fFoldLength(0) { -} - -CaseFoldingUTextIterator::~CaseFoldingUTextIterator() {} - -UChar32 CaseFoldingUTextIterator::next() { - UChar32 foldedC; - UChar32 originalC; - if (fFoldChars == NULL) { - // We are not in a string folding of an earlier character. - // Start handling the next char from the input UText. - originalC = UTEXT_NEXT32(&fUText); - if (originalC == U_SENTINEL) { - return originalC; - } - fFoldLength = ucase_toFullFolding(originalC, &fFoldChars, U_FOLD_CASE_DEFAULT); - if (fFoldLength >= UCASE_MAX_STRING_LENGTH || fFoldLength < 0) { - // input code point folds to a single code point, possibly itself. - // See comment in ucase.h for explanation of return values from ucase_toFullFoldings. - if (fFoldLength < 0) { - fFoldLength = ~fFoldLength; - } - foldedC = (UChar32)fFoldLength; - fFoldChars = NULL; - return foldedC; - } - // String foldings fall through here. - fFoldIndex = 0; - } - - U16_NEXT(fFoldChars, fFoldIndex, fFoldLength, foldedC); - if (fFoldIndex >= fFoldLength) { - fFoldChars = NULL; - } - return foldedC; -} - - -UBool CaseFoldingUTextIterator::inExpansion() { - return fFoldChars != NULL; -} - - - -CaseFoldingUCharIterator::CaseFoldingUCharIterator(const UChar *chars, int64_t start, int64_t limit) : - fChars(chars), fIndex(start), fLimit(limit), fFoldChars(NULL), fFoldLength(0) { -} - - -CaseFoldingUCharIterator::~CaseFoldingUCharIterator() {} - - -UChar32 CaseFoldingUCharIterator::next() { - UChar32 foldedC; - UChar32 originalC; - if (fFoldChars == NULL) { - // We are not in a string folding of an earlier character. - // Start handling the next char from the input UText. - if (fIndex >= fLimit) { - return U_SENTINEL; - } - U16_NEXT(fChars, fIndex, fLimit, originalC); - - fFoldLength = ucase_toFullFolding(originalC, &fFoldChars, U_FOLD_CASE_DEFAULT); - if (fFoldLength >= UCASE_MAX_STRING_LENGTH || fFoldLength < 0) { - // input code point folds to a single code point, possibly itself. - // See comment in ucase.h for explanation of return values from ucase_toFullFoldings. - if (fFoldLength < 0) { - fFoldLength = ~fFoldLength; - } - foldedC = (UChar32)fFoldLength; - fFoldChars = NULL; - return foldedC; - } - // String foldings fall through here. - fFoldIndex = 0; - } - - U16_NEXT(fFoldChars, fFoldIndex, fFoldLength, foldedC); - if (fFoldIndex >= fFoldLength) { - fFoldChars = NULL; - } - return foldedC; -} - - -UBool CaseFoldingUCharIterator::inExpansion() { - return fFoldChars != NULL; -} - -int64_t CaseFoldingUCharIterator::getIndex() { - return fIndex; -} - - -U_NAMESPACE_END - -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// Copyright (C) 2012 International Business Machines Corporation +// and others. All rights reserved. +// +// file: regeximp.cpp +// +// ICU Regular Expressions, +// miscellaneous implementation functions. +// + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_REGULAR_EXPRESSIONS +#include "regeximp.h" +#include "unicode/utf16.h" + +U_NAMESPACE_BEGIN + +CaseFoldingUTextIterator::CaseFoldingUTextIterator(UText &text) : + fUText(text), fFoldChars(nullptr), fFoldLength(0) { +} + +CaseFoldingUTextIterator::~CaseFoldingUTextIterator() {} + +UChar32 CaseFoldingUTextIterator::next() { + UChar32 foldedC; + UChar32 originalC; + if (fFoldChars == nullptr) { + // We are not in a string folding of an earlier character. + // Start handling the next char from the input UText. + originalC = UTEXT_NEXT32(&fUText); + if (originalC == U_SENTINEL) { + return originalC; + } + fFoldLength = ucase_toFullFolding(originalC, &fFoldChars, U_FOLD_CASE_DEFAULT); + if (fFoldLength >= UCASE_MAX_STRING_LENGTH || fFoldLength < 0) { + // input code point folds to a single code point, possibly itself. + // See comment in ucase.h for explanation of return values from ucase_toFullFoldings. + if (fFoldLength < 0) { + fFoldLength = ~fFoldLength; + } + foldedC = (UChar32)fFoldLength; + fFoldChars = nullptr; + return foldedC; + } + // String foldings fall through here. + fFoldIndex = 0; + } + + U16_NEXT(fFoldChars, fFoldIndex, fFoldLength, foldedC); + if (fFoldIndex >= fFoldLength) { + fFoldChars = nullptr; + } + return foldedC; +} + + +UBool CaseFoldingUTextIterator::inExpansion() { + return fFoldChars != nullptr; +} + + + +CaseFoldingUCharIterator::CaseFoldingUCharIterator(const char16_t *chars, int64_t start, int64_t limit) : + fChars(chars), fIndex(start), fLimit(limit), fFoldChars(nullptr), fFoldLength(0) { +} + + +CaseFoldingUCharIterator::~CaseFoldingUCharIterator() {} + + +UChar32 CaseFoldingUCharIterator::next() { + UChar32 foldedC; + UChar32 originalC; + if (fFoldChars == nullptr) { + // We are not in a string folding of an earlier character. + // Start handling the next char from the input UText. + if (fIndex >= fLimit) { + return U_SENTINEL; + } + U16_NEXT(fChars, fIndex, fLimit, originalC); + + fFoldLength = ucase_toFullFolding(originalC, &fFoldChars, U_FOLD_CASE_DEFAULT); + if (fFoldLength >= UCASE_MAX_STRING_LENGTH || fFoldLength < 0) { + // input code point folds to a single code point, possibly itself. + // See comment in ucase.h for explanation of return values from ucase_toFullFoldings. + if (fFoldLength < 0) { + fFoldLength = ~fFoldLength; + } + foldedC = (UChar32)fFoldLength; + fFoldChars = nullptr; + return foldedC; + } + // String foldings fall through here. + fFoldIndex = 0; + } + + U16_NEXT(fFoldChars, fFoldIndex, fFoldLength, foldedC); + if (fFoldIndex >= fFoldLength) { + fFoldChars = nullptr; + } + return foldedC; +} + + +UBool CaseFoldingUCharIterator::inExpansion() { + return fFoldChars != nullptr; +} + +int64_t CaseFoldingUCharIterator::getIndex() { + return fIndex; +} + + +U_NAMESPACE_END + +#endif + diff --git a/deps/icu-small/source/i18n/regeximp.h b/deps/icu-small/source/i18n/regeximp.h index bb0e1e838de651..fa0968a92b438e 100644 --- a/deps/icu-small/source/i18n/regeximp.h +++ b/deps/icu-small/source/i18n/regeximp.h @@ -1,414 +1,414 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// Copyright (C) 2002-2015 International Business Machines Corporation -// and others. All rights reserved. -// -// file: regeximp.h -// -// ICU Regular Expressions, -// Definitions of constant values used in the compiled form of -// a regular expression pattern. -// - -#ifndef _REGEXIMP_H -#define _REGEXIMP_H - -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "unicode/uniset.h" -#include "unicode/utext.h" - -#include "cmemory.h" -#include "ucase.h" - -U_NAMESPACE_BEGIN - -// For debugging, define REGEX_DEBUG -// To define with configure, -// CPPFLAGS="-DREGEX_DEBUG" ./runConfigureICU --enable-debug --disable-release Linux - -#ifdef REGEX_DEBUG -// -// debugging options. Enable one or more of the three #defines immediately following -// - -//#define REGEX_SCAN_DEBUG -#define REGEX_DUMP_DEBUG -#define REGEX_RUN_DEBUG - -// End of #defines intended to be directly set. - -#include -#endif - -#ifdef REGEX_SCAN_DEBUG -#define REGEX_SCAN_DEBUG_PRINTF(a) printf a -#else -#define REGEX_SCAN_DEBUG_PRINTF(a) -#endif - - -// -// Opcode types In the compiled form of the regexp, these are the type, or opcodes, -// of the entries. -// -enum { - URX_RESERVED_OP = 0, // For multi-operand ops, most non-first words. - URX_RESERVED_OP_N = 255, // For multi-operand ops, negative operand values. - URX_BACKTRACK = 1, // Force a backtrack, as if a match test had failed. - URX_END = 2, - URX_ONECHAR = 3, // Value field is the 21 bit unicode char to match - URX_STRING = 4, // Value field is index of string start - URX_STRING_LEN = 5, // Value field is string length (code units) - URX_STATE_SAVE = 6, // Value field is pattern position to push - URX_NOP = 7, - URX_START_CAPTURE = 8, // Value field is capture group number. - URX_END_CAPTURE = 9, // Value field is capture group number - URX_STATIC_SETREF = 10, // Value field is index of set in array of sets. - URX_SETREF = 11, // Value field is index of set in array of sets. - URX_DOTANY = 12, - URX_JMP = 13, // Value field is destination position in - // the pattern. - URX_FAIL = 14, // Stop match operation, No match. - - URX_JMP_SAV = 15, // Operand: JMP destination location - URX_BACKSLASH_B = 16, // Value field: 0: \b 1: \B - URX_BACKSLASH_G = 17, - URX_JMP_SAV_X = 18, // Conditional JMP_SAV, - // Used in (x)+, breaks loop on zero length match. - // Operand: Jmp destination. - URX_BACKSLASH_X = 19, - URX_BACKSLASH_Z = 20, // \z Unconditional end of line. - - URX_DOTANY_ALL = 21, // ., in the . matches any mode. - URX_BACKSLASH_D = 22, // Value field: 0: \d 1: \D - URX_CARET = 23, // Value field: 1: multi-line mode. - URX_DOLLAR = 24, // Also for \Z - - URX_CTR_INIT = 25, // Counter Inits for {Interval} loops. - URX_CTR_INIT_NG = 26, // 2 kinds, normal and non-greedy. - // These are 4 word opcodes. See description. - // First Operand: Data loc of counter variable - // 2nd Operand: Pat loc of the URX_CTR_LOOPx - // at the end of the loop. - // 3rd Operand: Minimum count. - // 4th Operand: Max count, -1 for unbounded. - - URX_DOTANY_UNIX = 27, // '.' operator in UNIX_LINES mode, only \n marks end of line. - - URX_CTR_LOOP = 28, // Loop Ops for {interval} loops. - URX_CTR_LOOP_NG = 29, // Also in three flavors. - // Operand is loc of corresponding CTR_INIT. - - URX_CARET_M_UNIX = 30, // '^' operator, test for start of line in multi-line - // plus UNIX_LINES mode. - - URX_RELOC_OPRND = 31, // Operand value in multi-operand ops that refers - // back into compiled pattern code, and thus must - // be relocated when inserting/deleting ops in code. - - URX_STO_SP = 32, // Store the stack ptr. Operand is location within - // matcher data (not stack data) to store it. - URX_LD_SP = 33, // Load the stack pointer. Operand is location - // to load from. - URX_BACKREF = 34, // Back Reference. Parameter is the index of the - // capture group variables in the state stack frame. - URX_STO_INP_LOC = 35, // Store the input location. Operand is location - // within the matcher stack frame. - URX_JMPX = 36, // Conditional JMP. - // First Operand: JMP target location. - // Second Operand: Data location containing an - // input position. If current input position == - // saved input position, FAIL rather than taking - // the JMP - URX_LA_START = 37, // Starting a LookAround expression. - // Save InputPos, SP and active region in static data. - // Operand: Static data offset for the save - URX_LA_END = 38, // Ending a Lookaround expression. - // Restore InputPos and Stack to saved values. - // Operand: Static data offset for saved data. - URX_ONECHAR_I = 39, // Test for case-insensitive match of a literal character. - // Operand: the literal char. - URX_STRING_I = 40, // Case insensitive string compare. - // First Operand: Index of start of string in string literals - // Second Operand (next word in compiled code): - // the length of the string. - URX_BACKREF_I = 41, // Case insensitive back reference. - // Parameter is the index of the - // capture group variables in the state stack frame. - URX_DOLLAR_M = 42, // $ in multi-line mode. - URX_CARET_M = 43, // ^ in multi-line mode. - URX_LB_START = 44, // LookBehind Start. - // Parameter is data location - URX_LB_CONT = 45, // LookBehind Continue. - // Param 0: the data location - // Param 1: The minimum length of the look-behind match - // Param 2: The max length of the look-behind match - URX_LB_END = 46, // LookBehind End. - // Parameter is the data location. - // Check that match ended at the right spot, - // Restore original input string len. - URX_LBN_CONT = 47, // Negative LookBehind Continue - // Param 0: the data location - // Param 1: The minimum length of the look-behind match - // Param 2: The max length of the look-behind match - // Param 3: The pattern loc following the look-behind block. - URX_LBN_END = 48, // Negative LookBehind end - // Parameter is the data location. - // Check that the match ended at the right spot. - URX_STAT_SETREF_N = 49, // Reference to a prebuilt set (e.g. \w), negated - // Operand is index of set in array of sets. - URX_LOOP_SR_I = 50, // Init a [set]* loop. - // Operand is the sets index in array of user sets. - URX_LOOP_C = 51, // Continue a [set]* or OneChar* loop. - // Operand is a matcher static data location. - // Must always immediately follow LOOP_x_I instruction. - URX_LOOP_DOT_I = 52, // .*, initialization of the optimized loop. - // Operand value: - // bit 0: - // 0: Normal (. doesn't match new-line) mode. - // 1: . matches new-line mode. - // bit 1: controls what new-lines are recognized by this operation. - // 0: All Unicode New-lines - // 1: UNIX_LINES, \u000a only. - URX_BACKSLASH_BU = 53, // \b or \B in UREGEX_UWORD mode, using Unicode style - // word boundaries. - URX_DOLLAR_D = 54, // $ end of input test, in UNIX_LINES mode. - URX_DOLLAR_MD = 55, // $ end of input test, in MULTI_LINE and UNIX_LINES mode. - URX_BACKSLASH_H = 56, // Value field: 0: \h 1: \H - URX_BACKSLASH_R = 57, // Any line break sequence. - URX_BACKSLASH_V = 58 // Value field: 0: \v 1: \V - -}; - -// Keep this list of opcode names in sync with the above enum -// Used for debug printing only. -#define URX_OPCODE_NAMES \ - " ", \ - "BACKTRACK", \ - "END", \ - "ONECHAR", \ - "STRING", \ - "STRING_LEN", \ - "STATE_SAVE", \ - "NOP", \ - "START_CAPTURE", \ - "END_CAPTURE", \ - "URX_STATIC_SETREF", \ - "SETREF", \ - "DOTANY", \ - "JMP", \ - "FAIL", \ - "JMP_SAV", \ - "BACKSLASH_B", \ - "BACKSLASH_G", \ - "JMP_SAV_X", \ - "BACKSLASH_X", \ - "BACKSLASH_Z", \ - "DOTANY_ALL", \ - "BACKSLASH_D", \ - "CARET", \ - "DOLLAR", \ - "CTR_INIT", \ - "CTR_INIT_NG", \ - "DOTANY_UNIX", \ - "CTR_LOOP", \ - "CTR_LOOP_NG", \ - "URX_CARET_M_UNIX", \ - "RELOC_OPRND", \ - "STO_SP", \ - "LD_SP", \ - "BACKREF", \ - "STO_INP_LOC", \ - "JMPX", \ - "LA_START", \ - "LA_END", \ - "ONECHAR_I", \ - "STRING_I", \ - "BACKREF_I", \ - "DOLLAR_M", \ - "CARET_M", \ - "LB_START", \ - "LB_CONT", \ - "LB_END", \ - "LBN_CONT", \ - "LBN_END", \ - "STAT_SETREF_N", \ - "LOOP_SR_I", \ - "LOOP_C", \ - "LOOP_DOT_I", \ - "BACKSLASH_BU", \ - "DOLLAR_D", \ - "DOLLAR_MD", \ - "URX_BACKSLASH_H", \ - "URX_BACKSLASH_R", \ - "URX_BACKSLASH_V" - - -// -// Convenience macros for assembling and disassembling a compiled operation. -// -#define URX_TYPE(x) ((uint32_t)(x) >> 24) -#define URX_VAL(x) ((x) & 0xffffff) - - -// -// Access to Unicode Sets composite character properties -// The sets are accessed by the match engine for things like \w (word boundary) -// -enum { - URX_ISWORD_SET = 1, - URX_ISALNUM_SET = 2, - URX_ISALPHA_SET = 3, - URX_ISSPACE_SET = 4, - - URX_GC_NORMAL, // Sets for finding grapheme cluster boundaries. - URX_GC_EXTEND, - URX_GC_CONTROL, - URX_GC_L, - URX_GC_LV, - URX_GC_LVT, - URX_GC_V, - URX_GC_T, - - URX_LAST_SET, - - URX_NEG_SET = 0x800000 // Flag bit to reverse sense of set - // membership test. -}; - - -// -// Match Engine State Stack Frame Layout. -// -struct REStackFrame { - // Header - int64_t fInputIdx; // Position of next character in the input string - int64_t fPatIdx; // Position of next Op in the compiled pattern - // (int64_t for UVector64, values fit in an int32_t) - // Remainder - int64_t fExtra[1]; // Extra state, for capture group start/ends - // atomic parentheses, repeat counts, etc. - // Locations assigned at pattern compile time. - // Variable-length array. -}; -// number of UVector elements in the header -#define RESTACKFRAME_HDRCOUNT 2 - -// -// Start-Of-Match type. Used by find() to quickly scan to positions where a -// match might start before firing up the full match engine. -// -enum StartOfMatch { - START_NO_INFO, // No hint available. - START_CHAR, // Match starts with a literal code point. - START_SET, // Match starts with something matching a set. - START_START, // Match starts at start of buffer only (^ or \A) - START_LINE, // Match starts with ^ in multi-line mode. - START_STRING // Match starts with a literal string. -}; - -#define START_OF_MATCH_STR(v) ((v)==START_NO_INFO? "START_NO_INFO" : \ - (v)==START_CHAR? "START_CHAR" : \ - (v)==START_SET? "START_SET" : \ - (v)==START_START? "START_START" : \ - (v)==START_LINE? "START_LINE" : \ - (v)==START_STRING? "START_STRING" : \ - "ILLEGAL") - -// -// 8 bit set, to fast-path latin-1 set membership tests. -// -struct Regex8BitSet : public UMemory { - inline Regex8BitSet(); - inline void operator = (const Regex8BitSet &s); - inline void init(const UnicodeSet *src); - inline UBool contains(UChar32 c); - inline void add(UChar32 c); - int8_t d[32]; -}; - -inline Regex8BitSet::Regex8BitSet() { - uprv_memset(d, 0, sizeof(d)); -} - -inline UBool Regex8BitSet::contains(UChar32 c) { - // No bounds checking! This is deliberate. - return ((d[c>>3] & 1 <<(c&7)) != 0); -} - -inline void Regex8BitSet::add(UChar32 c) { - d[c>>3] |= 1 << (c&7); -} - -inline void Regex8BitSet::init(const UnicodeSet *s) { - if (s != NULL) { - for (int32_t i=0; i<=255; i++) { - if (s->contains(i)) { - this->add(i); - } - } - } -} - -inline void Regex8BitSet::operator = (const Regex8BitSet &s) { - uprv_memcpy(d, s.d, sizeof(d)); -} - - -// Case folded UText Iterator helper class. -// Wraps a UText, provides a case-folded enumeration over its contents. -// Used in implementing case insensitive matching constructs. -// Implementation in rematch.cpp - -class CaseFoldingUTextIterator: public UMemory { - public: - CaseFoldingUTextIterator(UText &text); - ~CaseFoldingUTextIterator(); - - UChar32 next(); // Next case folded character - - UBool inExpansion(); // True if last char returned from next() and the - // next to be returned both originated from a string - // folding of the same code point from the original UText. - private: - UText &fUText; - const UChar *fFoldChars; - int32_t fFoldLength; - int32_t fFoldIndex; - -}; - - -// Case folded UChar * string iterator. -// Wraps a UChar *, provides a case-folded enumeration over its contents. -// Used in implementing case insensitive matching constructs. -// Implementation in rematch.cpp - -class CaseFoldingUCharIterator: public UMemory { - public: - CaseFoldingUCharIterator(const UChar *chars, int64_t start, int64_t limit); - ~CaseFoldingUCharIterator(); - - UChar32 next(); // Next case folded character - - UBool inExpansion(); // True if last char returned from next() and the - // next to be returned both originated from a string - // folding of the same code point from the original UText. - - int64_t getIndex(); // Return the current input buffer index. - - private: - const UChar *fChars; - int64_t fIndex; - int64_t fLimit; - const UChar *fFoldChars; - int32_t fFoldLength; - int32_t fFoldIndex; - -}; - -U_NAMESPACE_END -#endif - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// Copyright (C) 2002-2015 International Business Machines Corporation +// and others. All rights reserved. +// +// file: regeximp.h +// +// ICU Regular Expressions, +// Definitions of constant values used in the compiled form of +// a regular expression pattern. +// + +#ifndef _REGEXIMP_H +#define _REGEXIMP_H + +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "unicode/uniset.h" +#include "unicode/utext.h" + +#include "cmemory.h" +#include "ucase.h" + +U_NAMESPACE_BEGIN + +// For debugging, define REGEX_DEBUG +// To define with configure, +// CPPFLAGS="-DREGEX_DEBUG" ./runConfigureICU --enable-debug --disable-release Linux + +#ifdef REGEX_DEBUG +// +// debugging options. Enable one or more of the three #defines immediately following +// + +//#define REGEX_SCAN_DEBUG +#define REGEX_DUMP_DEBUG +#define REGEX_RUN_DEBUG + +// End of #defines intended to be directly set. + +#include +#endif + +#ifdef REGEX_SCAN_DEBUG +#define REGEX_SCAN_DEBUG_PRINTF(a) printf a +#else +#define REGEX_SCAN_DEBUG_PRINTF(a) +#endif + + +// +// Opcode types In the compiled form of the regexp, these are the type, or opcodes, +// of the entries. +// +enum { + URX_RESERVED_OP = 0, // For multi-operand ops, most non-first words. + URX_RESERVED_OP_N = 255, // For multi-operand ops, negative operand values. + URX_BACKTRACK = 1, // Force a backtrack, as if a match test had failed. + URX_END = 2, + URX_ONECHAR = 3, // Value field is the 21 bit unicode char to match + URX_STRING = 4, // Value field is index of string start + URX_STRING_LEN = 5, // Value field is string length (code units) + URX_STATE_SAVE = 6, // Value field is pattern position to push + URX_NOP = 7, + URX_START_CAPTURE = 8, // Value field is capture group number. + URX_END_CAPTURE = 9, // Value field is capture group number + URX_STATIC_SETREF = 10, // Value field is index of set in array of sets. + URX_SETREF = 11, // Value field is index of set in array of sets. + URX_DOTANY = 12, + URX_JMP = 13, // Value field is destination position in + // the pattern. + URX_FAIL = 14, // Stop match operation, No match. + + URX_JMP_SAV = 15, // Operand: JMP destination location + URX_BACKSLASH_B = 16, // Value field: 0: \b 1: \B + URX_BACKSLASH_G = 17, + URX_JMP_SAV_X = 18, // Conditional JMP_SAV, + // Used in (x)+, breaks loop on zero length match. + // Operand: Jmp destination. + URX_BACKSLASH_X = 19, + URX_BACKSLASH_Z = 20, // \z Unconditional end of line. + + URX_DOTANY_ALL = 21, // ., in the . matches any mode. + URX_BACKSLASH_D = 22, // Value field: 0: \d 1: \D + URX_CARET = 23, // Value field: 1: multi-line mode. + URX_DOLLAR = 24, // Also for \Z + + URX_CTR_INIT = 25, // Counter Inits for {Interval} loops. + URX_CTR_INIT_NG = 26, // 2 kinds, normal and non-greedy. + // These are 4 word opcodes. See description. + // First Operand: Data loc of counter variable + // 2nd Operand: Pat loc of the URX_CTR_LOOPx + // at the end of the loop. + // 3rd Operand: Minimum count. + // 4th Operand: Max count, -1 for unbounded. + + URX_DOTANY_UNIX = 27, // '.' operator in UNIX_LINES mode, only \n marks end of line. + + URX_CTR_LOOP = 28, // Loop Ops for {interval} loops. + URX_CTR_LOOP_NG = 29, // Also in three flavors. + // Operand is loc of corresponding CTR_INIT. + + URX_CARET_M_UNIX = 30, // '^' operator, test for start of line in multi-line + // plus UNIX_LINES mode. + + URX_RELOC_OPRND = 31, // Operand value in multi-operand ops that refers + // back into compiled pattern code, and thus must + // be relocated when inserting/deleting ops in code. + + URX_STO_SP = 32, // Store the stack ptr. Operand is location within + // matcher data (not stack data) to store it. + URX_LD_SP = 33, // Load the stack pointer. Operand is location + // to load from. + URX_BACKREF = 34, // Back Reference. Parameter is the index of the + // capture group variables in the state stack frame. + URX_STO_INP_LOC = 35, // Store the input location. Operand is location + // within the matcher stack frame. + URX_JMPX = 36, // Conditional JMP. + // First Operand: JMP target location. + // Second Operand: Data location containing an + // input position. If current input position == + // saved input position, FAIL rather than taking + // the JMP + URX_LA_START = 37, // Starting a LookAround expression. + // Save InputPos, SP and active region in static data. + // Operand: Static data offset for the save + URX_LA_END = 38, // Ending a Lookaround expression. + // Restore InputPos and Stack to saved values. + // Operand: Static data offset for saved data. + URX_ONECHAR_I = 39, // Test for case-insensitive match of a literal character. + // Operand: the literal char. + URX_STRING_I = 40, // Case insensitive string compare. + // First Operand: Index of start of string in string literals + // Second Operand (next word in compiled code): + // the length of the string. + URX_BACKREF_I = 41, // Case insensitive back reference. + // Parameter is the index of the + // capture group variables in the state stack frame. + URX_DOLLAR_M = 42, // $ in multi-line mode. + URX_CARET_M = 43, // ^ in multi-line mode. + URX_LB_START = 44, // LookBehind Start. + // Parameter is data location + URX_LB_CONT = 45, // LookBehind Continue. + // Param 0: the data location + // Param 1: The minimum length of the look-behind match + // Param 2: The max length of the look-behind match + URX_LB_END = 46, // LookBehind End. + // Parameter is the data location. + // Check that match ended at the right spot, + // Restore original input string len. + URX_LBN_CONT = 47, // Negative LookBehind Continue + // Param 0: the data location + // Param 1: The minimum length of the look-behind match + // Param 2: The max length of the look-behind match + // Param 3: The pattern loc following the look-behind block. + URX_LBN_END = 48, // Negative LookBehind end + // Parameter is the data location. + // Check that the match ended at the right spot. + URX_STAT_SETREF_N = 49, // Reference to a prebuilt set (e.g. \w), negated + // Operand is index of set in array of sets. + URX_LOOP_SR_I = 50, // Init a [set]* loop. + // Operand is the sets index in array of user sets. + URX_LOOP_C = 51, // Continue a [set]* or OneChar* loop. + // Operand is a matcher static data location. + // Must always immediately follow LOOP_x_I instruction. + URX_LOOP_DOT_I = 52, // .*, initialization of the optimized loop. + // Operand value: + // bit 0: + // 0: Normal (. doesn't match new-line) mode. + // 1: . matches new-line mode. + // bit 1: controls what new-lines are recognized by this operation. + // 0: All Unicode New-lines + // 1: UNIX_LINES, \u000a only. + URX_BACKSLASH_BU = 53, // \b or \B in UREGEX_UWORD mode, using Unicode style + // word boundaries. + URX_DOLLAR_D = 54, // $ end of input test, in UNIX_LINES mode. + URX_DOLLAR_MD = 55, // $ end of input test, in MULTI_LINE and UNIX_LINES mode. + URX_BACKSLASH_H = 56, // Value field: 0: \h 1: \H + URX_BACKSLASH_R = 57, // Any line break sequence. + URX_BACKSLASH_V = 58 // Value field: 0: \v 1: \V + +}; + +// Keep this list of opcode names in sync with the above enum +// Used for debug printing only. +#define URX_OPCODE_NAMES \ + " ", \ + "BACKTRACK", \ + "END", \ + "ONECHAR", \ + "STRING", \ + "STRING_LEN", \ + "STATE_SAVE", \ + "NOP", \ + "START_CAPTURE", \ + "END_CAPTURE", \ + "URX_STATIC_SETREF", \ + "SETREF", \ + "DOTANY", \ + "JMP", \ + "FAIL", \ + "JMP_SAV", \ + "BACKSLASH_B", \ + "BACKSLASH_G", \ + "JMP_SAV_X", \ + "BACKSLASH_X", \ + "BACKSLASH_Z", \ + "DOTANY_ALL", \ + "BACKSLASH_D", \ + "CARET", \ + "DOLLAR", \ + "CTR_INIT", \ + "CTR_INIT_NG", \ + "DOTANY_UNIX", \ + "CTR_LOOP", \ + "CTR_LOOP_NG", \ + "URX_CARET_M_UNIX", \ + "RELOC_OPRND", \ + "STO_SP", \ + "LD_SP", \ + "BACKREF", \ + "STO_INP_LOC", \ + "JMPX", \ + "LA_START", \ + "LA_END", \ + "ONECHAR_I", \ + "STRING_I", \ + "BACKREF_I", \ + "DOLLAR_M", \ + "CARET_M", \ + "LB_START", \ + "LB_CONT", \ + "LB_END", \ + "LBN_CONT", \ + "LBN_END", \ + "STAT_SETREF_N", \ + "LOOP_SR_I", \ + "LOOP_C", \ + "LOOP_DOT_I", \ + "BACKSLASH_BU", \ + "DOLLAR_D", \ + "DOLLAR_MD", \ + "URX_BACKSLASH_H", \ + "URX_BACKSLASH_R", \ + "URX_BACKSLASH_V" + + +// +// Convenience macros for assembling and disassembling a compiled operation. +// +#define URX_TYPE(x) ((uint32_t)(x) >> 24) +#define URX_VAL(x) ((x) & 0xffffff) + + +// +// Access to Unicode Sets composite character properties +// The sets are accessed by the match engine for things like \w (word boundary) +// +enum { + URX_ISWORD_SET = 1, + URX_ISALNUM_SET = 2, + URX_ISALPHA_SET = 3, + URX_ISSPACE_SET = 4, + + URX_GC_NORMAL, // Sets for finding grapheme cluster boundaries. + URX_GC_EXTEND, + URX_GC_CONTROL, + URX_GC_L, + URX_GC_LV, + URX_GC_LVT, + URX_GC_V, + URX_GC_T, + + URX_LAST_SET, + + URX_NEG_SET = 0x800000 // Flag bit to reverse sense of set + // membership test. +}; + + +// +// Match Engine State Stack Frame Layout. +// +struct REStackFrame { + // Header + int64_t fInputIdx; // Position of next character in the input string + int64_t fPatIdx; // Position of next Op in the compiled pattern + // (int64_t for UVector64, values fit in an int32_t) + // Remainder + int64_t fExtra[1]; // Extra state, for capture group start/ends + // atomic parentheses, repeat counts, etc. + // Locations assigned at pattern compile time. + // Variable-length array. +}; +// number of UVector elements in the header +#define RESTACKFRAME_HDRCOUNT 2 + +// +// Start-Of-Match type. Used by find() to quickly scan to positions where a +// match might start before firing up the full match engine. +// +enum StartOfMatch { + START_NO_INFO, // No hint available. + START_CHAR, // Match starts with a literal code point. + START_SET, // Match starts with something matching a set. + START_START, // Match starts at start of buffer only (^ or \A) + START_LINE, // Match starts with ^ in multi-line mode. + START_STRING // Match starts with a literal string. +}; + +#define START_OF_MATCH_STR(v) ((v)==START_NO_INFO? "START_NO_INFO" : \ + (v)==START_CHAR? "START_CHAR" : \ + (v)==START_SET? "START_SET" : \ + (v)==START_START? "START_START" : \ + (v)==START_LINE? "START_LINE" : \ + (v)==START_STRING? "START_STRING" : \ + "ILLEGAL") + +// +// 8 bit set, to fast-path latin-1 set membership tests. +// +struct Regex8BitSet : public UMemory { + inline Regex8BitSet(); + inline void operator = (const Regex8BitSet &s); + inline void init(const UnicodeSet *src); + inline UBool contains(UChar32 c); + inline void add(UChar32 c); + int8_t d[32]; +}; + +inline Regex8BitSet::Regex8BitSet() { + uprv_memset(d, 0, sizeof(d)); +} + +inline UBool Regex8BitSet::contains(UChar32 c) { + // No bounds checking! This is deliberate. + return ((d[c>>3] & 1 <<(c&7)) != 0); +} + +inline void Regex8BitSet::add(UChar32 c) { + d[c>>3] |= 1 << (c&7); +} + +inline void Regex8BitSet::init(const UnicodeSet *s) { + if (s != nullptr) { + for (int32_t i=0; i<=255; i++) { + if (s->contains(i)) { + this->add(i); + } + } + } +} + +inline void Regex8BitSet::operator = (const Regex8BitSet &s) { + uprv_memcpy(d, s.d, sizeof(d)); +} + + +// Case folded UText Iterator helper class. +// Wraps a UText, provides a case-folded enumeration over its contents. +// Used in implementing case insensitive matching constructs. +// Implementation in rematch.cpp + +class CaseFoldingUTextIterator: public UMemory { + public: + CaseFoldingUTextIterator(UText &text); + ~CaseFoldingUTextIterator(); + + UChar32 next(); // Next case folded character + + UBool inExpansion(); // True if last char returned from next() and the + // next to be returned both originated from a string + // folding of the same code point from the original UText. + private: + UText &fUText; + const char16_t *fFoldChars; + int32_t fFoldLength; + int32_t fFoldIndex; + +}; + + +// Case folded char16_t * string iterator. +// Wraps a char16_t *, provides a case-folded enumeration over its contents. +// Used in implementing case insensitive matching constructs. +// Implementation in rematch.cpp + +class CaseFoldingUCharIterator: public UMemory { + public: + CaseFoldingUCharIterator(const char16_t *chars, int64_t start, int64_t limit); + ~CaseFoldingUCharIterator(); + + UChar32 next(); // Next case folded character + + UBool inExpansion(); // True if last char returned from next() and the + // next to be returned both originated from a string + // folding of the same code point from the original UText. + + int64_t getIndex(); // Return the current input buffer index. + + private: + const char16_t *fChars; + int64_t fIndex; + int64_t fLimit; + const char16_t *fFoldChars; + int32_t fFoldLength; + int32_t fFoldIndex; + +}; + +U_NAMESPACE_END +#endif + diff --git a/deps/icu-small/source/i18n/regexst.cpp b/deps/icu-small/source/i18n/regexst.cpp index dc01327a02c1cf..3fd9ab3d8f01e1 100644 --- a/deps/icu-small/source/i18n/regexst.cpp +++ b/deps/icu-small/source/i18n/regexst.cpp @@ -1,172 +1,172 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// regexst.h -// -// Copyright (C) 2004-2015, International Business Machines Corporation and others. -// All Rights Reserved. -// -// This file contains class RegexStaticSets -// -// This class is internal to the regular expression implementation. -// For the public Regular Expression API, see the file "unicode/regex.h" -// -// RegexStaticSets groups together the common UnicodeSets that are needed -// for compiling or executing RegularExpressions. This grouping simplifies -// the thread safe lazy creation and sharing of these sets across -// all instances of regular expressions. -// -#include "unicode/utypes.h" - -#if !UCONFIG_NO_REGULAR_EXPRESSIONS - -#include "unicode/unistr.h" -#include "unicode/uniset.h" -#include "unicode/uchar.h" -#include "unicode/regex.h" -#include "uprops.h" -#include "cmemory.h" -#include "cstring.h" -#include "uassert.h" -#include "ucln_in.h" -#include "umutex.h" - -#include "regexcst.h" // Contains state table for the regex pattern parser. - // generated by a Perl script. -#include "regexst.h" - -U_NAMESPACE_BEGIN - -// "Rule Char" Characters are those with special meaning, and therefore -// need to be escaped to appear as literals in a regexp. -constexpr char16_t const *gRuleSet_rule_chars = u"*?+[(){}^$|\\."; - -// -// The backslash escape characters that ICU's unescape() function will handle. -// -constexpr char16_t const *gUnescapeChars = u"acefnrtuUx"; - -// -// Unicode Set pattern for Regular Expression \w -// -constexpr char16_t const *gIsWordPattern = u"[\\p{Alphabetic}\\p{M}\\p{Nd}\\p{Pc}\\u200c\\u200d]"; - -// -// Unicode Set Definitions for Regular Expression \s -// -constexpr char16_t const *gIsSpacePattern = u"[\\p{WhiteSpace}]"; - -// -// UnicodeSets used in implementation of Grapheme Cluster detection, \X -// -constexpr char16_t const *gGC_ControlPattern = u"[[:Zl:][:Zp:][:Cc:][:Cf:]-[:Grapheme_Extend:]]"; -constexpr char16_t const *gGC_ExtendPattern = u"[\\p{Grapheme_Extend}]"; -constexpr char16_t const *gGC_LPattern = u"[\\p{Hangul_Syllable_Type=L}]"; -constexpr char16_t const *gGC_VPattern = u"[\\p{Hangul_Syllable_Type=V}]"; -constexpr char16_t const *gGC_TPattern = u"[\\p{Hangul_Syllable_Type=T}]"; -constexpr char16_t const *gGC_LVPattern = u"[\\p{Hangul_Syllable_Type=LV}]"; -constexpr char16_t const *gGC_LVTPattern = u"[\\p{Hangul_Syllable_Type=LVT}]"; - - -RegexStaticSets *RegexStaticSets::gStaticSets = nullptr; -UInitOnce gStaticSetsInitOnce {}; - - -RegexStaticSets::RegexStaticSets(UErrorCode *status) { - // Initialize the shared static sets to their correct values. - fUnescapeCharSet.addAll(UnicodeString(true, gUnescapeChars, -1)).freeze(); - fPropSets[URX_ISWORD_SET].applyPattern(UnicodeString(true, gIsWordPattern, -1), *status).freeze(); - fPropSets[URX_ISSPACE_SET].applyPattern(UnicodeString(true, gIsSpacePattern, -1), *status).freeze(); - fPropSets[URX_GC_EXTEND].applyPattern(UnicodeString(true, gGC_ExtendPattern, -1), *status).freeze(); - fPropSets[URX_GC_CONTROL].applyPattern(UnicodeString(true, gGC_ControlPattern, -1), *status).freeze(); - fPropSets[URX_GC_L].applyPattern(UnicodeString(true, gGC_LPattern, -1), *status).freeze(); - fPropSets[URX_GC_V].applyPattern(UnicodeString(true, gGC_VPattern, -1), *status).freeze(); - fPropSets[URX_GC_T].applyPattern(UnicodeString(true, gGC_TPattern, -1), *status).freeze(); - fPropSets[URX_GC_LV].applyPattern(UnicodeString(true, gGC_LVPattern, -1), *status).freeze(); - fPropSets[URX_GC_LVT].applyPattern(UnicodeString(true, gGC_LVTPattern, -1), *status).freeze(); - - - // - // "Normal" is the set of characters that don't need special handling - // when finding grapheme cluster boundaries. - // - fPropSets[URX_GC_NORMAL].complement(); - fPropSets[URX_GC_NORMAL].remove(0xac00, 0xd7a4); - fPropSets[URX_GC_NORMAL].removeAll(fPropSets[URX_GC_CONTROL]); - fPropSets[URX_GC_NORMAL].removeAll(fPropSets[URX_GC_L]); - fPropSets[URX_GC_NORMAL].removeAll(fPropSets[URX_GC_V]); - fPropSets[URX_GC_NORMAL].removeAll(fPropSets[URX_GC_T]); - fPropSets[URX_GC_NORMAL].freeze(); - - // Initialize the 8-bit fast bit sets from the parallel full - // UnicodeSets. - // - // TODO: 25 Oct 2019 are these fast 8-bit sets worth keeping? - // Measured 3.5% gain on (non) matching with the pattern "x(?:\\S+)+x" - // This runs in exponential time, making it easy to adjust the time for - // convenient measuring. - // - // This 8 bit optimization dates from the early days of ICU, - // with a less optimized UnicodeSet. At the time, the difference - // was substantial. - - for (int32_t i=0; ilastOffset + 1) { - c = UTEXT_NEXT32(context->text); - context->lastOffset++; - } else if (offset == context->lastOffset) { - c = UTEXT_PREVIOUS32(context->text); - UTEXT_NEXT32(context->text); - } else { - utext_moveIndex32(context->text, offset - context->lastOffset - 1); - c = UTEXT_NEXT32(context->text); - context->lastOffset = offset; - } - - // !!!: Doesn't handle characters outside BMP - if (U_IS_BMP(c)) { - return (UChar)c; - } else { - return 0; - } -} - -U_CFUNC UChar U_CALLCONV -uregex_ucstr_unescape_charAt(int32_t offset, void *context) { - return ((UChar *)context)[offset]; -} - -U_NAMESPACE_END +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2008-2011, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ +// +// file: regextxt.cpp +// +// This file contains utility code for supporting UText in the regular expression engine. +// + +#include "unicode/utf.h" +#include "regextxt.h" + +U_NAMESPACE_BEGIN + +U_CFUNC char16_t U_CALLCONV +uregex_utext_unescape_charAt(int32_t offset, void *ct) { + struct URegexUTextUnescapeCharContext *context = (struct URegexUTextUnescapeCharContext *)ct; + UChar32 c; + if (offset == context->lastOffset + 1) { + c = UTEXT_NEXT32(context->text); + context->lastOffset++; + } else if (offset == context->lastOffset) { + c = UTEXT_PREVIOUS32(context->text); + UTEXT_NEXT32(context->text); + } else { + utext_moveIndex32(context->text, offset - context->lastOffset - 1); + c = UTEXT_NEXT32(context->text); + context->lastOffset = offset; + } + + // !!!: Doesn't handle characters outside BMP + if (U_IS_BMP(c)) { + return (char16_t)c; + } else { + return 0; + } +} + +U_CFUNC char16_t U_CALLCONV +uregex_ucstr_unescape_charAt(int32_t offset, void *context) { + return ((char16_t *)context)[offset]; +} + +U_NAMESPACE_END diff --git a/deps/icu-small/source/i18n/regextxt.h b/deps/icu-small/source/i18n/regextxt.h index 0f64b8437e7c32..22274a7969ca53 100644 --- a/deps/icu-small/source/i18n/regextxt.h +++ b/deps/icu-small/source/i18n/regextxt.h @@ -1,50 +1,50 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/******************************************************************** - * COPYRIGHT: - * Copyright (c) 2008-2010, International Business Machines Corporation and - * others. All Rights Reserved. - ********************************************************************/ -// -// file: regextxt.h -// -// This file contains utility code for supporting UText in the regular expression engine. -// -// This class is internal to the regular expression implementation. -// For the public Regular Expression API, see the file "unicode/regex.h" -// - -#ifndef _REGEXTXT_H -#define _REGEXTXT_H - -#include "unicode/utypes.h" -#include "unicode/utext.h" - -U_NAMESPACE_BEGIN - -#define UTEXT_USES_U16(ut) (NULL==((ut)->pFuncs->mapNativeIndexToUTF16)) - -#if 0 -#define REGEX_DISABLE_CHUNK_MODE 1 -#endif - -#ifdef REGEX_DISABLE_CHUNK_MODE -# define UTEXT_FULL_TEXT_IN_CHUNK(ut,len) (false) -#else -# define UTEXT_FULL_TEXT_IN_CHUNK(ut,len) ((0==((ut)->chunkNativeStart))&&((len)==((ut)->chunkNativeLimit))&&((len)==((ut)->nativeIndexingLimit))) -#endif - -struct URegexUTextUnescapeCharContext { - UText *text; - int32_t lastOffset; -}; -#define U_REGEX_UTEXT_UNESCAPE_CONTEXT(text) { (text), -1 } - -U_CFUNC UChar U_CALLCONV -uregex_utext_unescape_charAt(int32_t offset, void * /* struct URegexUTextUnescapeCharContext* */ context); -U_CFUNC UChar U_CALLCONV -uregex_ucstr_unescape_charAt(int32_t offset, void * /* UChar* */ context); - -U_NAMESPACE_END - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2008-2010, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ +// +// file: regextxt.h +// +// This file contains utility code for supporting UText in the regular expression engine. +// +// This class is internal to the regular expression implementation. +// For the public Regular Expression API, see the file "unicode/regex.h" +// + +#ifndef _REGEXTXT_H +#define _REGEXTXT_H + +#include "unicode/utypes.h" +#include "unicode/utext.h" + +U_NAMESPACE_BEGIN + +#define UTEXT_USES_U16(ut) (NULL==((ut)->pFuncs->mapNativeIndexToUTF16)) + +#if 0 +#define REGEX_DISABLE_CHUNK_MODE 1 +#endif + +#ifdef REGEX_DISABLE_CHUNK_MODE +# define UTEXT_FULL_TEXT_IN_CHUNK(ut,len) (false) +#else +# define UTEXT_FULL_TEXT_IN_CHUNK(ut,len) ((0==((ut)->chunkNativeStart))&&((len)==((ut)->chunkNativeLimit))&&((len)==((ut)->nativeIndexingLimit))) +#endif + +struct URegexUTextUnescapeCharContext { + UText *text; + int32_t lastOffset; +}; +#define U_REGEX_UTEXT_UNESCAPE_CONTEXT(text) { (text), -1 } + +U_CFUNC UChar U_CALLCONV +uregex_utext_unescape_charAt(int32_t offset, void * /* struct URegexUTextUnescapeCharContext* */ context); +U_CFUNC UChar U_CALLCONV +uregex_ucstr_unescape_charAt(int32_t offset, void * /* UChar* */ context); + +U_NAMESPACE_END + +#endif diff --git a/deps/icu-small/source/i18n/region.cpp b/deps/icu-small/source/i18n/region.cpp index 6a0c05fc78f297..ad8cffe42e4785 100644 --- a/deps/icu-small/source/i18n/region.cpp +++ b/deps/icu-small/source/i18n/region.cpp @@ -1,772 +1,772 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2014-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* -* File REGION.CPP -* -* Modification History:* -* Date Name Description -* 01/15/13 Emmons Original Port from ICU4J -******************************************************************************** -*/ - -/** - * \file - * \brief C++ API: Region classes (territory containment) - */ - -#include "unicode/region.h" -#include "unicode/utypes.h" -#include "unicode/uobject.h" -#include "unicode/unistr.h" -#include "unicode/ures.h" -#include "ucln_in.h" -#include "cstring.h" -#include "mutex.h" -#include "uhash.h" -#include "umutex.h" -#include "uresimp.h" -#include "region_impl.h" -#include "util.h" - -#if !UCONFIG_NO_FORMATTING - - -U_CDECL_BEGIN - -/** - * Cleanup callback func - */ -static UBool U_CALLCONV region_cleanup(void) -{ - icu::Region::cleanupRegionData(); - - return true; -} - -U_CDECL_END - -U_NAMESPACE_BEGIN - -static UInitOnce gRegionDataInitOnce {}; -static UVector* availableRegions[URGN_LIMIT]; - -static UHashtable *regionAliases = NULL; -static UHashtable *regionIDMap = NULL; -static UHashtable *numericCodeMap = NULL; -static UVector *allRegions = NULL; - -static const UChar UNKNOWN_REGION_ID [] = { 0x5A, 0x5A, 0 }; /* "ZZ" */ -static const UChar OUTLYING_OCEANIA_REGION_ID [] = { 0x51, 0x4F, 0 }; /* "QO" */ -static const UChar WORLD_ID [] = { 0x30, 0x30, 0x31, 0 }; /* "001" */ -static const UChar RANGE_MARKER = 0x7E; /* '~' */ - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RegionNameEnumeration) - -/* - * Initializes the region data from the ICU resource bundles. The region data - * contains the basic relationships such as which regions are known, what the numeric - * codes are, any known aliases, and the territory containment data. - * - * If the region data has already loaded, then this method simply returns without doing - * anything meaningful. - */ -void U_CALLCONV Region::loadRegionData(UErrorCode &status) { - - // Construct service objs first - LocalUHashtablePointer newRegionIDMap(uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status)); - LocalUHashtablePointer newNumericCodeMap(uhash_open(uhash_hashLong,uhash_compareLong,NULL,&status)); - LocalUHashtablePointer newRegionAliases(uhash_open(uhash_hashUnicodeString,uhash_compareUnicodeString,NULL,&status)); - - LocalPointer continents(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); - LocalPointer groupings(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); - LocalPointer lpAllRegions(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); - allRegions = lpAllRegions.orphan(); - - LocalUResourceBundlePointer metadata(ures_openDirect(NULL,"metadata",&status)); - LocalUResourceBundlePointer metadataAlias(ures_getByKey(metadata.getAlias(),"alias",NULL,&status)); - LocalUResourceBundlePointer territoryAlias(ures_getByKey(metadataAlias.getAlias(),"territory",NULL,&status)); - - LocalUResourceBundlePointer supplementalData(ures_openDirect(NULL,"supplementalData",&status)); - LocalUResourceBundlePointer codeMappings(ures_getByKey(supplementalData.getAlias(),"codeMappings",NULL,&status)); - - LocalUResourceBundlePointer idValidity(ures_getByKey(supplementalData.getAlias(),"idValidity",NULL,&status)); - LocalUResourceBundlePointer regionList(ures_getByKey(idValidity.getAlias(),"region",NULL,&status)); - LocalUResourceBundlePointer regionRegular(ures_getByKey(regionList.getAlias(),"regular",NULL,&status)); - LocalUResourceBundlePointer regionMacro(ures_getByKey(regionList.getAlias(),"macroregion",NULL,&status)); - LocalUResourceBundlePointer regionUnknown(ures_getByKey(regionList.getAlias(),"unknown",NULL,&status)); - - LocalUResourceBundlePointer territoryContainment(ures_getByKey(supplementalData.getAlias(),"territoryContainment",NULL,&status)); - LocalUResourceBundlePointer worldContainment(ures_getByKey(territoryContainment.getAlias(),"001",NULL,&status)); - LocalUResourceBundlePointer groupingContainment(ures_getByKey(territoryContainment.getAlias(),"grouping",NULL,&status)); - - ucln_i18n_registerCleanup(UCLN_I18N_REGION, region_cleanup); - if (U_FAILURE(status)) { - return; - } - - // now, initialize - uhash_setValueDeleter(newRegionIDMap.getAlias(), uprv_deleteUObject); // regionIDMap owns objs - uhash_setKeyDeleter(newRegionAliases.getAlias(), uprv_deleteUObject); // regionAliases owns the string keys - - - while (U_SUCCESS(status) && ures_hasNext(regionRegular.getAlias())) { - UnicodeString regionName = ures_getNextUnicodeString(regionRegular.getAlias(),NULL,&status); - int32_t rangeMarkerLocation = regionName.indexOf(RANGE_MARKER); - UChar buf[6]; - regionName.extract(buf,6,status); - if ( rangeMarkerLocation > 0 ) { - UChar endRange = regionName.charAt(rangeMarkerLocation+1); - buf[rangeMarkerLocation] = 0; - while (U_SUCCESS(status) && buf[rangeMarkerLocation-1] <= endRange) { - LocalPointer newRegion(new UnicodeString(buf), status); - allRegions->adoptElement(newRegion.orphan(), status); - buf[rangeMarkerLocation-1]++; - } - } else { - LocalPointer newRegion(new UnicodeString(regionName), status); - allRegions->adoptElement(newRegion.orphan(), status); - } - } - - while (U_SUCCESS(status) && ures_hasNext(regionMacro.getAlias())) { - UnicodeString regionName = ures_getNextUnicodeString(regionMacro.getAlias(),NULL,&status); - int32_t rangeMarkerLocation = regionName.indexOf(RANGE_MARKER); - UChar buf[6]; - regionName.extract(buf,6,status); - if ( rangeMarkerLocation > 0 ) { - UChar endRange = regionName.charAt(rangeMarkerLocation+1); - buf[rangeMarkerLocation] = 0; - while ( buf[rangeMarkerLocation-1] <= endRange && U_SUCCESS(status)) { - LocalPointer newRegion(new UnicodeString(buf), status); - allRegions->adoptElement(newRegion.orphan(),status); - buf[rangeMarkerLocation-1]++; - } - } else { - LocalPointer newRegion(new UnicodeString(regionName), status); - allRegions->adoptElement(newRegion.orphan(),status); - } - } - - while (U_SUCCESS(status) && ures_hasNext(regionUnknown.getAlias())) { - LocalPointer regionName ( - new UnicodeString(ures_getNextUnicodeString(regionUnknown.getAlias(), nullptr, &status), status)); - allRegions->adoptElement(regionName.orphan(),status); - } - - while (U_SUCCESS(status) && ures_hasNext(worldContainment.getAlias())) { - UnicodeString *continentName = new UnicodeString(ures_getNextUnicodeString(worldContainment.getAlias(),NULL,&status)); - continents->adoptElement(continentName,status); - } - if (U_FAILURE(status)) { - return; - } - - for ( int32_t i = 0 ; i < allRegions->size() ; i++ ) { - LocalPointer r(new Region(), status); - if ( U_FAILURE(status) ) { - return; - } - UnicodeString *regionName = (UnicodeString *)allRegions->elementAt(i); - r->idStr = *regionName; - - r->idStr.extract(0,r->idStr.length(),r->id,sizeof(r->id),US_INV); - r->fType = URGN_TERRITORY; // Only temporary - figure out the real type later once the aliases are known. - - int32_t pos = 0; - int32_t result = ICU_Utility::parseAsciiInteger(r->idStr, pos); - if (pos > 0) { - r->code = result; // Convert string to number - uhash_iput(newNumericCodeMap.getAlias(),r->code,(void *)(r.getAlias()),&status); - r->fType = URGN_SUBCONTINENT; - } else { - r->code = -1; - } - void* idStrAlias = (void*)&(r->idStr); // about to orphan 'r'. Save this off. - uhash_put(newRegionIDMap.getAlias(),idStrAlias,(void *)(r.orphan()),&status); // regionIDMap takes ownership - } - - UResourceBundle *groupingBundle = nullptr; - while (U_SUCCESS(status) && ures_hasNext(groupingContainment.getAlias())) { - groupingBundle = ures_getNextResource(groupingContainment.getAlias(), groupingBundle, &status); - if (U_FAILURE(status)) { - break; - } - UnicodeString *groupingName = new UnicodeString(ures_getKey(groupingBundle), -1, US_INV); - LocalPointer lpGroupingName(groupingName, status); - groupings->adoptElement(lpGroupingName.orphan(), status); - if (U_FAILURE(status)) { - break; - } - Region *grouping = (Region *) uhash_get(newRegionIDMap.getAlias(), groupingName); - if (grouping != NULL) { - for (int32_t i = 0; i < ures_getSize(groupingBundle) && U_SUCCESS(status); i++) { - UnicodeString child = ures_getUnicodeStringByIndex(groupingBundle, i, &status); - if (U_SUCCESS(status)) { - if (grouping->containedRegions == NULL) { - LocalPointer lpContainedRegions( - new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); - grouping->containedRegions = lpContainedRegions.orphan(); - if (U_FAILURE(status)) { - break; - } - } - LocalPointer lpChildCopy(new UnicodeString(child), status); - grouping->containedRegions->adoptElement(lpChildCopy.orphan(), status); - } - } - } - } - ures_close(groupingBundle); - - // Process the territory aliases - while (U_SUCCESS(status) && ures_hasNext(territoryAlias.getAlias())) { - LocalUResourceBundlePointer res(ures_getNextResource(territoryAlias.getAlias(),NULL,&status)); - const char *aliasFrom = ures_getKey(res.getAlias()); - LocalPointer aliasFromStr(new UnicodeString(aliasFrom, -1, US_INV), status); - UnicodeString aliasTo = ures_getUnicodeStringByKey(res.getAlias(),"replacement",&status); - res.adoptInstead(NULL); - - const Region *aliasToRegion = (Region *) uhash_get(newRegionIDMap.getAlias(),&aliasTo); - Region *aliasFromRegion = (Region *)uhash_get(newRegionIDMap.getAlias(),aliasFromStr.getAlias()); - - if ( aliasToRegion != NULL && aliasFromRegion == NULL ) { // This is just an alias from some string to a region - uhash_put(newRegionAliases.getAlias(),(void *)aliasFromStr.orphan(), (void *)aliasToRegion,&status); - } else { - if ( aliasFromRegion == NULL ) { // Deprecated region code not in the primary codes list - so need to create a deprecated region for it. - LocalPointer newRgn(new Region, status); - if ( U_SUCCESS(status) ) { - aliasFromRegion = newRgn.orphan(); - } else { - return; // error out - } - aliasFromRegion->idStr.setTo(*aliasFromStr); - aliasFromRegion->idStr.extract(0,aliasFromRegion->idStr.length(),aliasFromRegion->id,sizeof(aliasFromRegion->id),US_INV); - uhash_put(newRegionIDMap.getAlias(),(void *)&(aliasFromRegion->idStr),(void *)aliasFromRegion,&status); - int32_t pos = 0; - int32_t result = ICU_Utility::parseAsciiInteger(aliasFromRegion->idStr, pos); - if ( pos > 0 ) { - aliasFromRegion->code = result; // Convert string to number - uhash_iput(newNumericCodeMap.getAlias(),aliasFromRegion->code,(void *)aliasFromRegion,&status); - } else { - aliasFromRegion->code = -1; - } - aliasFromRegion->fType = URGN_DEPRECATED; - } else { - aliasFromRegion->fType = URGN_DEPRECATED; - } - - { - LocalPointer newPreferredValues(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); - aliasFromRegion->preferredValues = newPreferredValues.orphan(); - } - if( U_FAILURE(status)) { - return; - } - UnicodeString currentRegion; - //currentRegion.remove(); TODO: was already 0 length? - for (int32_t i = 0 ; i < aliasTo.length() && U_SUCCESS(status); i++ ) { - if ( aliasTo.charAt(i) != 0x0020 ) { - currentRegion.append(aliasTo.charAt(i)); - } - if ( aliasTo.charAt(i) == 0x0020 || i+1 == aliasTo.length() ) { - Region *target = (Region *)uhash_get(newRegionIDMap.getAlias(),(void *)¤tRegion); - if (target) { - LocalPointer preferredValue(new UnicodeString(target->idStr), status); - aliasFromRegion->preferredValues->adoptElement(preferredValue.orphan(),status); // may add null if err - } - currentRegion.remove(); - } - } - } - } - - // Process the code mappings - This will allow us to assign numeric codes to most of the territories. - while (U_SUCCESS(status) && ures_hasNext(codeMappings.getAlias())) { - UResourceBundle *mapping = ures_getNextResource(codeMappings.getAlias(),NULL,&status); - if (U_SUCCESS(status) && ures_getType(mapping) == URES_ARRAY && ures_getSize(mapping) == 3) { - UnicodeString codeMappingID = ures_getUnicodeStringByIndex(mapping,0,&status); - UnicodeString codeMappingNumber = ures_getUnicodeStringByIndex(mapping,1,&status); - UnicodeString codeMapping3Letter = ures_getUnicodeStringByIndex(mapping,2,&status); - - Region *r = (Region *)uhash_get(newRegionIDMap.getAlias(),(void *)&codeMappingID); - if ( r ) { - int32_t pos = 0; - int32_t result = ICU_Utility::parseAsciiInteger(codeMappingNumber, pos); - if ( pos > 0 ) { - r->code = result; // Convert string to number - uhash_iput(newNumericCodeMap.getAlias(),r->code,(void *)r,&status); - } - LocalPointer code3(new UnicodeString(codeMapping3Letter), status); - uhash_put(newRegionAliases.getAlias(),(void *)code3.orphan(), (void *)r,&status); - } - } - ures_close(mapping); - } - - // Now fill in the special cases for WORLD, UNKNOWN, CONTINENTS, and GROUPINGS - Region *r; - UnicodeString WORLD_ID_STRING(WORLD_ID); - r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&WORLD_ID_STRING); - if ( r ) { - r->fType = URGN_WORLD; - } - - UnicodeString UNKNOWN_REGION_ID_STRING(UNKNOWN_REGION_ID); - r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&UNKNOWN_REGION_ID_STRING); - if ( r ) { - r->fType = URGN_UNKNOWN; - } - - for ( int32_t i = 0 ; i < continents->size() ; i++ ) { - r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)continents->elementAt(i)); - if ( r ) { - r->fType = URGN_CONTINENT; - } - } - - for ( int32_t i = 0 ; i < groupings->size() ; i++ ) { - r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)groupings->elementAt(i)); - if ( r ) { - r->fType = URGN_GROUPING; - } - } - - // Special case: The region code "QO" (Outlying Oceania) is a subcontinent code added by CLDR - // even though it looks like a territory code. Need to handle it here. - - UnicodeString OUTLYING_OCEANIA_REGION_ID_STRING(OUTLYING_OCEANIA_REGION_ID); - r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&OUTLYING_OCEANIA_REGION_ID_STRING); - if ( r ) { - r->fType = URGN_SUBCONTINENT; - } - - // Load territory containment info from the supplemental data. - while ( ures_hasNext(territoryContainment.getAlias()) ) { - LocalUResourceBundlePointer mapping(ures_getNextResource(territoryContainment.getAlias(),NULL,&status)); - if( U_FAILURE(status) ) { - return; // error out - } - const char *parent = ures_getKey(mapping.getAlias()); - if (uprv_strcmp(parent, "containedGroupings") == 0 || uprv_strcmp(parent, "deprecated") == 0) { - continue; // handle new pseudo-parent types added in ICU data per cldrbug 7808; for now just skip. - // #11232 is to do something useful with these. - } - UnicodeString parentStr = UnicodeString(parent, -1 , US_INV); - Region *parentRegion = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&parentStr); - - for ( int j = 0 ; j < ures_getSize(mapping.getAlias()); j++ ) { - UnicodeString child = ures_getUnicodeStringByIndex(mapping.getAlias(),j,&status); - Region *childRegion = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&child); - if ( parentRegion != NULL && childRegion != NULL ) { - - // Add the child region to the set of regions contained by the parent - if (parentRegion->containedRegions == NULL) { - LocalPointer lpContainedRegions( - new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); - parentRegion->containedRegions = lpContainedRegions.orphan(); - if (U_FAILURE(status)) { - return; - } - } - - LocalPointer childStr(new UnicodeString(), status); - if (U_FAILURE(status)) { - return; // error out - } - childStr->fastCopyFrom(childRegion->idStr); - parentRegion->containedRegions->adoptElement(childStr.orphan(),status); - if (U_FAILURE(status)) { - return; - } - - // Set the parent region to be the containing region of the child. - // Regions of type GROUPING can't be set as the parent, since another region - // such as a SUBCONTINENT, CONTINENT, or WORLD must always be the parent. - if ( parentRegion->fType != URGN_GROUPING) { - childRegion->containingRegion = parentRegion; - } - } - } - } - - // Create the availableRegions lists - int32_t pos = UHASH_FIRST; - while ( const UHashElement* element = uhash_nextElement(newRegionIDMap.getAlias(),&pos)) { - Region *ar = (Region *)element->value.pointer; - if ( availableRegions[ar->fType] == NULL ) { - LocalPointer newAr(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); - availableRegions[ar->fType] = newAr.orphan(); - } - LocalPointer arString(new UnicodeString(ar->idStr), status); - if( U_FAILURE(status) ) { - return; // error out - } - availableRegions[ar->fType]->adoptElement(arString.orphan(), status); - } - - // copy hashtables - numericCodeMap = newNumericCodeMap.orphan(); - regionIDMap = newRegionIDMap.orphan(); - regionAliases = newRegionAliases.orphan(); -} - -void Region::cleanupRegionData() { - for (int32_t i = 0 ; i < URGN_LIMIT ; i++ ) { - if ( availableRegions[i] ) { - delete availableRegions[i]; - availableRegions[i] = nullptr; - } - } - - if (regionAliases) { - uhash_close(regionAliases); - } - - if (numericCodeMap) { - uhash_close(numericCodeMap); - } - - if (regionIDMap) { - uhash_close(regionIDMap); - } - if (allRegions) { - delete allRegions; - allRegions = NULL; - } - - regionAliases = numericCodeMap = regionIDMap = NULL; - - gRegionDataInitOnce.reset(); -} - -Region::Region () - : code(-1), - fType(URGN_UNKNOWN), - containingRegion(NULL), - containedRegions(NULL), - preferredValues(NULL) { - id[0] = 0; -} - -Region::~Region () { - if (containedRegions) { - delete containedRegions; - } - if (preferredValues) { - delete preferredValues; - } -} - -/** - * Returns true if the two regions are equal. - * Per PMC, just use pointer compare, since we have at most one instance of each Region. - */ -bool -Region::operator==(const Region &that) const { - return (idStr == that.idStr); -} - -/** - * Returns true if the two regions are NOT equal; that is, if operator ==() returns false. - * Per PMC, just use pointer compare, since we have at most one instance of each Region. - */ -bool -Region::operator!=(const Region &that) const { - return (idStr != that.idStr); -} - -/** - * Returns a pointer to a Region using the given region code. The region code can be either 2-letter ISO code, - * 3-letter ISO code, UNM.49 numeric code, or other valid Unicode Region Code as defined by the LDML specification. - * The identifier will be canonicalized internally using the supplemental metadata as defined in the CLDR. - * If the region code is NULL or not recognized, the appropriate error code will be set ( U_ILLEGAL_ARGUMENT_ERROR ) - */ -const Region* U_EXPORT2 -Region::getInstance(const char *region_code, UErrorCode &status) { - - umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); - if (U_FAILURE(status)) { - return NULL; - } - - if ( !region_code ) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - UnicodeString regionCodeString = UnicodeString(region_code, -1, US_INV); - Region *r = (Region *)uhash_get(regionIDMap,(void *)®ionCodeString); - - if ( !r ) { - r = (Region *)uhash_get(regionAliases,(void *)®ionCodeString); - } - - if ( !r ) { // Unknown region code - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - if ( r->fType == URGN_DEPRECATED && r->preferredValues->size() == 1) { - StringEnumeration *pv = r->getPreferredValues(status); - pv->reset(status); - const UnicodeString *ustr = pv->snext(status); - r = (Region *)uhash_get(regionIDMap,(void *)ustr); - delete pv; - } - - return r; - -} - -/** - * Returns a pointer to a Region using the given numeric region code. If the numeric region code is not recognized, - * the appropriate error code will be set ( U_ILLEGAL_ARGUMENT_ERROR ). - */ -const Region* U_EXPORT2 -Region::getInstance (int32_t code, UErrorCode &status) { - - umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); - if (U_FAILURE(status)) { - return NULL; - } - - Region *r = (Region *)uhash_iget(numericCodeMap,code); - - if ( !r ) { // Just in case there's an alias that's numeric, try to find it. - UnicodeString id; - ICU_Utility::appendNumber(id, code, 10, 1); - r = (Region *)uhash_get(regionAliases,&id); - } - - if( U_FAILURE(status) ) { - return NULL; - } - - if ( !r ) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - if ( r->fType == URGN_DEPRECATED && r->preferredValues->size() == 1) { - StringEnumeration *pv = r->getPreferredValues(status); - pv->reset(status); - const UnicodeString *ustr = pv->snext(status); - r = (Region *)uhash_get(regionIDMap,(void *)ustr); - delete pv; - } - - return r; -} - - -/** - * Returns an enumeration over the IDs of all known regions that match the given type. - */ -StringEnumeration* U_EXPORT2 -Region::getAvailable(URegionType type, UErrorCode &status) { - umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); // returns immediately if U_FAILURE(status) - if (U_FAILURE(status)) { - return NULL; - } - return new RegionNameEnumeration(availableRegions[type],status); -} - -/** - * Returns a pointer to the region that contains this region. Returns NULL if this region is code "001" (World) - * or "ZZ" (Unknown region). For example, calling this method with region "IT" (Italy) returns the - * region "039" (Southern Europe). - */ -const Region* -Region::getContainingRegion() const { - UErrorCode status = U_ZERO_ERROR; - umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); - return containingRegion; -} - -/** - * Return a pointer to the region that geographically contains this region and matches the given type, - * moving multiple steps up the containment chain if necessary. Returns NULL if no containing region can be found - * that matches the given type. Note: The URegionTypes = "URGN_GROUPING", "URGN_DEPRECATED", or "URGN_UNKNOWN" - * are not appropriate for use in this API. NULL will be returned in this case. For example, calling this method - * with region "IT" (Italy) for type "URGN_CONTINENT" returns the region "150" ( Europe ). - */ -const Region* -Region::getContainingRegion(URegionType type) const { - UErrorCode status = U_ZERO_ERROR; - umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); - if ( containingRegion == NULL ) { - return NULL; - } - - return ( containingRegion->fType == type)? containingRegion: containingRegion->getContainingRegion(type); -} - -/** - * Return an enumeration over the IDs of all the regions that are immediate children of this region in the - * region hierarchy. These returned regions could be either macro regions, territories, or a mixture of the two, - * depending on the containment data as defined in CLDR. This API may return NULL if this region doesn't have - * any sub-regions. For example, calling this method with region "150" (Europe) returns an enumeration containing - * the various sub regions of Europe - "039" (Southern Europe) - "151" (Eastern Europe) - "154" (Northern Europe) - * and "155" (Western Europe). - */ -StringEnumeration* -Region::getContainedRegions(UErrorCode &status) const { - umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); // returns immediately if U_FAILURE(status) - if (U_FAILURE(status)) { - return NULL; - } - return new RegionNameEnumeration(containedRegions,status); -} - -/** - * Returns an enumeration over the IDs of all the regions that are children of this region anywhere in the region - * hierarchy and match the given type. This API may return an empty enumeration if this region doesn't have any - * sub-regions that match the given type. For example, calling this method with region "150" (Europe) and type - * "URGN_TERRITORY" returns a set containing all the territories in Europe ( "FR" (France) - "IT" (Italy) - "DE" (Germany) etc. ) - */ -StringEnumeration* -Region::getContainedRegions( URegionType type, UErrorCode &status ) const { - umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); // returns immediately if U_FAILURE(status) - - UVector result(nullptr, uhash_compareChars, status); - LocalPointer cr(getContainedRegions(status), status); - if (U_FAILURE(status)) { - return nullptr; - } - - const char *regionId; - while((regionId = cr->next(nullptr, status)) != nullptr && U_SUCCESS(status)) { - const Region *r = Region::getInstance(regionId, status); - if ( r->getType() == type) { - result.addElement(const_cast(&r->idStr), status); - } else { - LocalPointer children(r->getContainedRegions(type, status)); - const char *id2; - while(U_SUCCESS(status) && ((id2 = children->next(nullptr, status)) != nullptr)) { - const Region *r2 = Region::getInstance(id2,status); - result.addElement(const_cast(&r2->idStr), status); - } - } - } - LocalPointer resultEnumeration( - new RegionNameEnumeration(&result, status), status); - return U_SUCCESS(status) ? resultEnumeration.orphan() : nullptr; -} - -/** - * Returns true if this region contains the supplied other region anywhere in the region hierarchy. - */ -UBool -Region::contains(const Region &other) const { - UErrorCode status = U_ZERO_ERROR; - umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); - - if (!containedRegions) { - return false; - } - if (containedRegions->contains((void *)&other.idStr)) { - return true; - } else { - for ( int32_t i = 0 ; i < containedRegions->size() ; i++ ) { - UnicodeString *crStr = (UnicodeString *)containedRegions->elementAt(i); - Region *cr = (Region *) uhash_get(regionIDMap,(void *)crStr); - if ( cr && cr->contains(other) ) { - return true; - } - } - } - - return false; -} - -/** - * For deprecated regions, return an enumeration over the IDs of the regions that are the preferred replacement - * regions for this region. Returns NULL for a non-deprecated region. For example, calling this method with region - * "SU" (Soviet Union) would return a list of the regions containing "RU" (Russia), "AM" (Armenia), "AZ" (Azerbaijan), etc... - */ -StringEnumeration* -Region::getPreferredValues(UErrorCode &status) const { - umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); // returns immediately if U_FAILURE(status) - if (U_FAILURE(status) || fType != URGN_DEPRECATED) { - return NULL; - } - return new RegionNameEnumeration(preferredValues,status); -} - - -/** - * Return this region's canonical region code. - */ -const char* -Region::getRegionCode() const { - return id; -} - -int32_t -Region::getNumericCode() const { - return code; -} - -/** - * Returns the region type of this region. - */ -URegionType -Region::getType() const { - return fType; -} - -RegionNameEnumeration::RegionNameEnumeration(UVector *nameList, UErrorCode& status) : - pos(0), fRegionNames(nullptr) { - // TODO: https://unicode-org.atlassian.net/browse/ICU-21829 - // Is all of the copying going on here really necessary? - if (nameList && U_SUCCESS(status)) { - LocalPointer regionNames( - new UVector(uprv_deleteUObject, uhash_compareUnicodeString, nameList->size(), status), status); - for ( int32_t i = 0 ; U_SUCCESS(status) && i < nameList->size() ; i++ ) { - UnicodeString* this_region_name = (UnicodeString *)nameList->elementAt(i); - LocalPointer new_region_name(new UnicodeString(*this_region_name), status); - regionNames->adoptElement(new_region_name.orphan(), status); - } - if (U_SUCCESS(status)) { - fRegionNames = regionNames.orphan(); - } - } -} - -const UnicodeString* -RegionNameEnumeration::snext(UErrorCode& status) { - if (U_FAILURE(status) || (fRegionNames==NULL)) { - return NULL; - } - const UnicodeString* nextStr = (const UnicodeString *)fRegionNames->elementAt(pos); - if (nextStr!=NULL) { - pos++; - } - return nextStr; -} - -void -RegionNameEnumeration::reset(UErrorCode& /*status*/) { - pos=0; -} - -int32_t -RegionNameEnumeration::count(UErrorCode& /*status*/) const { - return (fRegionNames==NULL) ? 0 : fRegionNames->size(); -} - -RegionNameEnumeration::~RegionNameEnumeration() { - delete fRegionNames; -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2014-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* +* File REGION.CPP +* +* Modification History:* +* Date Name Description +* 01/15/13 Emmons Original Port from ICU4J +******************************************************************************** +*/ + +/** + * \file + * \brief C++ API: Region classes (territory containment) + */ + +#include "unicode/region.h" +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "unicode/unistr.h" +#include "unicode/ures.h" +#include "ucln_in.h" +#include "cstring.h" +#include "mutex.h" +#include "uhash.h" +#include "umutex.h" +#include "uresimp.h" +#include "region_impl.h" +#include "util.h" + +#if !UCONFIG_NO_FORMATTING + + +U_CDECL_BEGIN + +/** + * Cleanup callback func + */ +static UBool U_CALLCONV region_cleanup() +{ + icu::Region::cleanupRegionData(); + + return true; +} + +U_CDECL_END + +U_NAMESPACE_BEGIN + +static UInitOnce gRegionDataInitOnce {}; +static UVector* availableRegions[URGN_LIMIT]; + +static UHashtable *regionAliases = nullptr; +static UHashtable *regionIDMap = nullptr; +static UHashtable *numericCodeMap = nullptr; +static UVector *allRegions = nullptr; + +static const char16_t UNKNOWN_REGION_ID [] = { 0x5A, 0x5A, 0 }; /* "ZZ" */ +static const char16_t OUTLYING_OCEANIA_REGION_ID [] = { 0x51, 0x4F, 0 }; /* "QO" */ +static const char16_t WORLD_ID [] = { 0x30, 0x30, 0x31, 0 }; /* "001" */ +static const char16_t RANGE_MARKER = 0x7E; /* '~' */ + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RegionNameEnumeration) + +/* + * Initializes the region data from the ICU resource bundles. The region data + * contains the basic relationships such as which regions are known, what the numeric + * codes are, any known aliases, and the territory containment data. + * + * If the region data has already loaded, then this method simply returns without doing + * anything meaningful. + */ +void U_CALLCONV Region::loadRegionData(UErrorCode &status) { + + // Construct service objs first + LocalUHashtablePointer newRegionIDMap(uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, nullptr, &status)); + LocalUHashtablePointer newNumericCodeMap(uhash_open(uhash_hashLong,uhash_compareLong,nullptr,&status)); + LocalUHashtablePointer newRegionAliases(uhash_open(uhash_hashUnicodeString,uhash_compareUnicodeString,nullptr,&status)); + + LocalPointer continents(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); + LocalPointer groupings(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); + LocalPointer lpAllRegions(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); + allRegions = lpAllRegions.orphan(); + + LocalUResourceBundlePointer metadata(ures_openDirect(nullptr,"metadata",&status)); + LocalUResourceBundlePointer metadataAlias(ures_getByKey(metadata.getAlias(),"alias",nullptr,&status)); + LocalUResourceBundlePointer territoryAlias(ures_getByKey(metadataAlias.getAlias(),"territory",nullptr,&status)); + + LocalUResourceBundlePointer supplementalData(ures_openDirect(nullptr,"supplementalData",&status)); + LocalUResourceBundlePointer codeMappings(ures_getByKey(supplementalData.getAlias(),"codeMappings",nullptr,&status)); + + LocalUResourceBundlePointer idValidity(ures_getByKey(supplementalData.getAlias(),"idValidity",nullptr,&status)); + LocalUResourceBundlePointer regionList(ures_getByKey(idValidity.getAlias(),"region",nullptr,&status)); + LocalUResourceBundlePointer regionRegular(ures_getByKey(regionList.getAlias(),"regular",nullptr,&status)); + LocalUResourceBundlePointer regionMacro(ures_getByKey(regionList.getAlias(),"macroregion",nullptr,&status)); + LocalUResourceBundlePointer regionUnknown(ures_getByKey(regionList.getAlias(),"unknown",nullptr,&status)); + + LocalUResourceBundlePointer territoryContainment(ures_getByKey(supplementalData.getAlias(),"territoryContainment",nullptr,&status)); + LocalUResourceBundlePointer worldContainment(ures_getByKey(territoryContainment.getAlias(),"001",nullptr,&status)); + LocalUResourceBundlePointer groupingContainment(ures_getByKey(territoryContainment.getAlias(),"grouping",nullptr,&status)); + + ucln_i18n_registerCleanup(UCLN_I18N_REGION, region_cleanup); + if (U_FAILURE(status)) { + return; + } + + // now, initialize + uhash_setValueDeleter(newRegionIDMap.getAlias(), uprv_deleteUObject); // regionIDMap owns objs + uhash_setKeyDeleter(newRegionAliases.getAlias(), uprv_deleteUObject); // regionAliases owns the string keys + + + while (U_SUCCESS(status) && ures_hasNext(regionRegular.getAlias())) { + UnicodeString regionName = ures_getNextUnicodeString(regionRegular.getAlias(),nullptr,&status); + int32_t rangeMarkerLocation = regionName.indexOf(RANGE_MARKER); + char16_t buf[6]; + regionName.extract(buf,6,status); + if ( rangeMarkerLocation > 0 ) { + char16_t endRange = regionName.charAt(rangeMarkerLocation+1); + buf[rangeMarkerLocation] = 0; + while (U_SUCCESS(status) && buf[rangeMarkerLocation-1] <= endRange) { + LocalPointer newRegion(new UnicodeString(buf), status); + allRegions->adoptElement(newRegion.orphan(), status); + buf[rangeMarkerLocation-1]++; + } + } else { + LocalPointer newRegion(new UnicodeString(regionName), status); + allRegions->adoptElement(newRegion.orphan(), status); + } + } + + while (U_SUCCESS(status) && ures_hasNext(regionMacro.getAlias())) { + UnicodeString regionName = ures_getNextUnicodeString(regionMacro.getAlias(),nullptr,&status); + int32_t rangeMarkerLocation = regionName.indexOf(RANGE_MARKER); + char16_t buf[6]; + regionName.extract(buf,6,status); + if ( rangeMarkerLocation > 0 ) { + char16_t endRange = regionName.charAt(rangeMarkerLocation+1); + buf[rangeMarkerLocation] = 0; + while ( buf[rangeMarkerLocation-1] <= endRange && U_SUCCESS(status)) { + LocalPointer newRegion(new UnicodeString(buf), status); + allRegions->adoptElement(newRegion.orphan(),status); + buf[rangeMarkerLocation-1]++; + } + } else { + LocalPointer newRegion(new UnicodeString(regionName), status); + allRegions->adoptElement(newRegion.orphan(),status); + } + } + + while (U_SUCCESS(status) && ures_hasNext(regionUnknown.getAlias())) { + LocalPointer regionName ( + new UnicodeString(ures_getNextUnicodeString(regionUnknown.getAlias(), nullptr, &status), status)); + allRegions->adoptElement(regionName.orphan(),status); + } + + while (U_SUCCESS(status) && ures_hasNext(worldContainment.getAlias())) { + UnicodeString *continentName = new UnicodeString(ures_getNextUnicodeString(worldContainment.getAlias(),nullptr,&status)); + continents->adoptElement(continentName,status); + } + if (U_FAILURE(status)) { + return; + } + + for ( int32_t i = 0 ; i < allRegions->size() ; i++ ) { + LocalPointer r(new Region(), status); + if ( U_FAILURE(status) ) { + return; + } + UnicodeString *regionName = (UnicodeString *)allRegions->elementAt(i); + r->idStr = *regionName; + + r->idStr.extract(0,r->idStr.length(),r->id,sizeof(r->id),US_INV); + r->fType = URGN_TERRITORY; // Only temporary - figure out the real type later once the aliases are known. + + int32_t pos = 0; + int32_t result = ICU_Utility::parseAsciiInteger(r->idStr, pos); + if (pos > 0) { + r->code = result; // Convert string to number + uhash_iput(newNumericCodeMap.getAlias(),r->code,(void *)(r.getAlias()),&status); + r->fType = URGN_SUBCONTINENT; + } else { + r->code = -1; + } + void* idStrAlias = (void*)&(r->idStr); // about to orphan 'r'. Save this off. + uhash_put(newRegionIDMap.getAlias(),idStrAlias,(void *)(r.orphan()),&status); // regionIDMap takes ownership + } + + UResourceBundle *groupingBundle = nullptr; + while (U_SUCCESS(status) && ures_hasNext(groupingContainment.getAlias())) { + groupingBundle = ures_getNextResource(groupingContainment.getAlias(), groupingBundle, &status); + if (U_FAILURE(status)) { + break; + } + UnicodeString *groupingName = new UnicodeString(ures_getKey(groupingBundle), -1, US_INV); + LocalPointer lpGroupingName(groupingName, status); + groupings->adoptElement(lpGroupingName.orphan(), status); + if (U_FAILURE(status)) { + break; + } + Region *grouping = (Region *) uhash_get(newRegionIDMap.getAlias(), groupingName); + if (grouping != nullptr) { + for (int32_t i = 0; i < ures_getSize(groupingBundle) && U_SUCCESS(status); i++) { + UnicodeString child = ures_getUnicodeStringByIndex(groupingBundle, i, &status); + if (U_SUCCESS(status)) { + if (grouping->containedRegions == nullptr) { + LocalPointer lpContainedRegions( + new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); + grouping->containedRegions = lpContainedRegions.orphan(); + if (U_FAILURE(status)) { + break; + } + } + LocalPointer lpChildCopy(new UnicodeString(child), status); + grouping->containedRegions->adoptElement(lpChildCopy.orphan(), status); + } + } + } + } + ures_close(groupingBundle); + + // Process the territory aliases + while (U_SUCCESS(status) && ures_hasNext(territoryAlias.getAlias())) { + LocalUResourceBundlePointer res(ures_getNextResource(territoryAlias.getAlias(),nullptr,&status)); + const char *aliasFrom = ures_getKey(res.getAlias()); + LocalPointer aliasFromStr(new UnicodeString(aliasFrom, -1, US_INV), status); + UnicodeString aliasTo = ures_getUnicodeStringByKey(res.getAlias(),"replacement",&status); + res.adoptInstead(nullptr); + + const Region *aliasToRegion = (Region *) uhash_get(newRegionIDMap.getAlias(),&aliasTo); + Region *aliasFromRegion = (Region *)uhash_get(newRegionIDMap.getAlias(),aliasFromStr.getAlias()); + + if ( aliasToRegion != nullptr && aliasFromRegion == nullptr ) { // This is just an alias from some string to a region + uhash_put(newRegionAliases.getAlias(),(void *)aliasFromStr.orphan(), (void *)aliasToRegion,&status); + } else { + if ( aliasFromRegion == nullptr ) { // Deprecated region code not in the primary codes list - so need to create a deprecated region for it. + LocalPointer newRgn(new Region, status); + if ( U_SUCCESS(status) ) { + aliasFromRegion = newRgn.orphan(); + } else { + return; // error out + } + aliasFromRegion->idStr.setTo(*aliasFromStr); + aliasFromRegion->idStr.extract(0,aliasFromRegion->idStr.length(),aliasFromRegion->id,sizeof(aliasFromRegion->id),US_INV); + uhash_put(newRegionIDMap.getAlias(),(void *)&(aliasFromRegion->idStr),(void *)aliasFromRegion,&status); + int32_t pos = 0; + int32_t result = ICU_Utility::parseAsciiInteger(aliasFromRegion->idStr, pos); + if ( pos > 0 ) { + aliasFromRegion->code = result; // Convert string to number + uhash_iput(newNumericCodeMap.getAlias(),aliasFromRegion->code,(void *)aliasFromRegion,&status); + } else { + aliasFromRegion->code = -1; + } + aliasFromRegion->fType = URGN_DEPRECATED; + } else { + aliasFromRegion->fType = URGN_DEPRECATED; + } + + { + LocalPointer newPreferredValues(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); + aliasFromRegion->preferredValues = newPreferredValues.orphan(); + } + if( U_FAILURE(status)) { + return; + } + UnicodeString currentRegion; + //currentRegion.remove(); TODO: was already 0 length? + for (int32_t i = 0 ; i < aliasTo.length() && U_SUCCESS(status); i++ ) { + if ( aliasTo.charAt(i) != 0x0020 ) { + currentRegion.append(aliasTo.charAt(i)); + } + if ( aliasTo.charAt(i) == 0x0020 || i+1 == aliasTo.length() ) { + Region *target = (Region *)uhash_get(newRegionIDMap.getAlias(),(void *)¤tRegion); + if (target) { + LocalPointer preferredValue(new UnicodeString(target->idStr), status); + aliasFromRegion->preferredValues->adoptElement(preferredValue.orphan(),status); // may add null if err + } + currentRegion.remove(); + } + } + } + } + + // Process the code mappings - This will allow us to assign numeric codes to most of the territories. + while (U_SUCCESS(status) && ures_hasNext(codeMappings.getAlias())) { + UResourceBundle *mapping = ures_getNextResource(codeMappings.getAlias(),nullptr,&status); + if (U_SUCCESS(status) && ures_getType(mapping) == URES_ARRAY && ures_getSize(mapping) == 3) { + UnicodeString codeMappingID = ures_getUnicodeStringByIndex(mapping,0,&status); + UnicodeString codeMappingNumber = ures_getUnicodeStringByIndex(mapping,1,&status); + UnicodeString codeMapping3Letter = ures_getUnicodeStringByIndex(mapping,2,&status); + + Region *r = (Region *)uhash_get(newRegionIDMap.getAlias(),(void *)&codeMappingID); + if ( r ) { + int32_t pos = 0; + int32_t result = ICU_Utility::parseAsciiInteger(codeMappingNumber, pos); + if ( pos > 0 ) { + r->code = result; // Convert string to number + uhash_iput(newNumericCodeMap.getAlias(),r->code,(void *)r,&status); + } + LocalPointer code3(new UnicodeString(codeMapping3Letter), status); + uhash_put(newRegionAliases.getAlias(),(void *)code3.orphan(), (void *)r,&status); + } + } + ures_close(mapping); + } + + // Now fill in the special cases for WORLD, UNKNOWN, CONTINENTS, and GROUPINGS + Region *r; + UnicodeString WORLD_ID_STRING(WORLD_ID); + r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&WORLD_ID_STRING); + if ( r ) { + r->fType = URGN_WORLD; + } + + UnicodeString UNKNOWN_REGION_ID_STRING(UNKNOWN_REGION_ID); + r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&UNKNOWN_REGION_ID_STRING); + if ( r ) { + r->fType = URGN_UNKNOWN; + } + + for ( int32_t i = 0 ; i < continents->size() ; i++ ) { + r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)continents->elementAt(i)); + if ( r ) { + r->fType = URGN_CONTINENT; + } + } + + for ( int32_t i = 0 ; i < groupings->size() ; i++ ) { + r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)groupings->elementAt(i)); + if ( r ) { + r->fType = URGN_GROUPING; + } + } + + // Special case: The region code "QO" (Outlying Oceania) is a subcontinent code added by CLDR + // even though it looks like a territory code. Need to handle it here. + + UnicodeString OUTLYING_OCEANIA_REGION_ID_STRING(OUTLYING_OCEANIA_REGION_ID); + r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&OUTLYING_OCEANIA_REGION_ID_STRING); + if ( r ) { + r->fType = URGN_SUBCONTINENT; + } + + // Load territory containment info from the supplemental data. + while ( ures_hasNext(territoryContainment.getAlias()) ) { + LocalUResourceBundlePointer mapping(ures_getNextResource(territoryContainment.getAlias(),nullptr,&status)); + if( U_FAILURE(status) ) { + return; // error out + } + const char *parent = ures_getKey(mapping.getAlias()); + if (uprv_strcmp(parent, "containedGroupings") == 0 || uprv_strcmp(parent, "deprecated") == 0) { + continue; // handle new pseudo-parent types added in ICU data per cldrbug 7808; for now just skip. + // #11232 is to do something useful with these. + } + UnicodeString parentStr = UnicodeString(parent, -1 , US_INV); + Region *parentRegion = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&parentStr); + + for ( int j = 0 ; j < ures_getSize(mapping.getAlias()); j++ ) { + UnicodeString child = ures_getUnicodeStringByIndex(mapping.getAlias(),j,&status); + Region *childRegion = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&child); + if ( parentRegion != nullptr && childRegion != nullptr ) { + + // Add the child region to the set of regions contained by the parent + if (parentRegion->containedRegions == nullptr) { + LocalPointer lpContainedRegions( + new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); + parentRegion->containedRegions = lpContainedRegions.orphan(); + if (U_FAILURE(status)) { + return; + } + } + + LocalPointer childStr(new UnicodeString(), status); + if (U_FAILURE(status)) { + return; // error out + } + childStr->fastCopyFrom(childRegion->idStr); + parentRegion->containedRegions->adoptElement(childStr.orphan(),status); + if (U_FAILURE(status)) { + return; + } + + // Set the parent region to be the containing region of the child. + // Regions of type GROUPING can't be set as the parent, since another region + // such as a SUBCONTINENT, CONTINENT, or WORLD must always be the parent. + if ( parentRegion->fType != URGN_GROUPING) { + childRegion->containingRegion = parentRegion; + } + } + } + } + + // Create the availableRegions lists + int32_t pos = UHASH_FIRST; + while ( const UHashElement* element = uhash_nextElement(newRegionIDMap.getAlias(),&pos)) { + Region *ar = (Region *)element->value.pointer; + if ( availableRegions[ar->fType] == nullptr ) { + LocalPointer newAr(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); + availableRegions[ar->fType] = newAr.orphan(); + } + LocalPointer arString(new UnicodeString(ar->idStr), status); + if( U_FAILURE(status) ) { + return; // error out + } + availableRegions[ar->fType]->adoptElement(arString.orphan(), status); + } + + // copy hashtables + numericCodeMap = newNumericCodeMap.orphan(); + regionIDMap = newRegionIDMap.orphan(); + regionAliases = newRegionAliases.orphan(); +} + +void Region::cleanupRegionData() { + for (int32_t i = 0 ; i < URGN_LIMIT ; i++ ) { + if ( availableRegions[i] ) { + delete availableRegions[i]; + availableRegions[i] = nullptr; + } + } + + if (regionAliases) { + uhash_close(regionAliases); + } + + if (numericCodeMap) { + uhash_close(numericCodeMap); + } + + if (regionIDMap) { + uhash_close(regionIDMap); + } + if (allRegions) { + delete allRegions; + allRegions = nullptr; + } + + regionAliases = numericCodeMap = regionIDMap = nullptr; + + gRegionDataInitOnce.reset(); +} + +Region::Region () + : code(-1), + fType(URGN_UNKNOWN), + containingRegion(nullptr), + containedRegions(nullptr), + preferredValues(nullptr) { + id[0] = 0; +} + +Region::~Region () { + if (containedRegions) { + delete containedRegions; + } + if (preferredValues) { + delete preferredValues; + } +} + +/** + * Returns true if the two regions are equal. + * Per PMC, just use pointer compare, since we have at most one instance of each Region. + */ +bool +Region::operator==(const Region &that) const { + return (idStr == that.idStr); +} + +/** + * Returns true if the two regions are NOT equal; that is, if operator ==() returns false. + * Per PMC, just use pointer compare, since we have at most one instance of each Region. + */ +bool +Region::operator!=(const Region &that) const { + return (idStr != that.idStr); +} + +/** + * Returns a pointer to a Region using the given region code. The region code can be either 2-letter ISO code, + * 3-letter ISO code, UNM.49 numeric code, or other valid Unicode Region Code as defined by the LDML specification. + * The identifier will be canonicalized internally using the supplemental metadata as defined in the CLDR. + * If the region code is nullptr or not recognized, the appropriate error code will be set ( U_ILLEGAL_ARGUMENT_ERROR ) + */ +const Region* U_EXPORT2 +Region::getInstance(const char *region_code, UErrorCode &status) { + + umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); + if (U_FAILURE(status)) { + return nullptr; + } + + if ( !region_code ) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + UnicodeString regionCodeString = UnicodeString(region_code, -1, US_INV); + Region *r = (Region *)uhash_get(regionIDMap,(void *)®ionCodeString); + + if ( !r ) { + r = (Region *)uhash_get(regionAliases,(void *)®ionCodeString); + } + + if ( !r ) { // Unknown region code + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + if ( r->fType == URGN_DEPRECATED && r->preferredValues->size() == 1) { + StringEnumeration *pv = r->getPreferredValues(status); + pv->reset(status); + const UnicodeString *ustr = pv->snext(status); + r = (Region *)uhash_get(regionIDMap,(void *)ustr); + delete pv; + } + + return r; + +} + +/** + * Returns a pointer to a Region using the given numeric region code. If the numeric region code is not recognized, + * the appropriate error code will be set ( U_ILLEGAL_ARGUMENT_ERROR ). + */ +const Region* U_EXPORT2 +Region::getInstance (int32_t code, UErrorCode &status) { + + umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); + if (U_FAILURE(status)) { + return nullptr; + } + + Region *r = (Region *)uhash_iget(numericCodeMap,code); + + if ( !r ) { // Just in case there's an alias that's numeric, try to find it. + UnicodeString id; + ICU_Utility::appendNumber(id, code, 10, 1); + r = (Region *)uhash_get(regionAliases,&id); + } + + if( U_FAILURE(status) ) { + return nullptr; + } + + if ( !r ) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + + if ( r->fType == URGN_DEPRECATED && r->preferredValues->size() == 1) { + StringEnumeration *pv = r->getPreferredValues(status); + pv->reset(status); + const UnicodeString *ustr = pv->snext(status); + r = (Region *)uhash_get(regionIDMap,(void *)ustr); + delete pv; + } + + return r; +} + + +/** + * Returns an enumeration over the IDs of all known regions that match the given type. + */ +StringEnumeration* U_EXPORT2 +Region::getAvailable(URegionType type, UErrorCode &status) { + umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); // returns immediately if U_FAILURE(status) + if (U_FAILURE(status)) { + return nullptr; + } + return new RegionNameEnumeration(availableRegions[type],status); +} + +/** + * Returns a pointer to the region that contains this region. Returns nullptr if this region is code "001" (World) + * or "ZZ" (Unknown region). For example, calling this method with region "IT" (Italy) returns the + * region "039" (Southern Europe). + */ +const Region* +Region::getContainingRegion() const { + UErrorCode status = U_ZERO_ERROR; + umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); + return containingRegion; +} + +/** + * Return a pointer to the region that geographically contains this region and matches the given type, + * moving multiple steps up the containment chain if necessary. Returns nullptr if no containing region can be found + * that matches the given type. Note: The URegionTypes = "URGN_GROUPING", "URGN_DEPRECATED", or "URGN_UNKNOWN" + * are not appropriate for use in this API. nullptr will be returned in this case. For example, calling this method + * with region "IT" (Italy) for type "URGN_CONTINENT" returns the region "150" ( Europe ). + */ +const Region* +Region::getContainingRegion(URegionType type) const { + UErrorCode status = U_ZERO_ERROR; + umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); + if ( containingRegion == nullptr ) { + return nullptr; + } + + return ( containingRegion->fType == type)? containingRegion: containingRegion->getContainingRegion(type); +} + +/** + * Return an enumeration over the IDs of all the regions that are immediate children of this region in the + * region hierarchy. These returned regions could be either macro regions, territories, or a mixture of the two, + * depending on the containment data as defined in CLDR. This API may return nullptr if this region doesn't have + * any sub-regions. For example, calling this method with region "150" (Europe) returns an enumeration containing + * the various sub regions of Europe - "039" (Southern Europe) - "151" (Eastern Europe) - "154" (Northern Europe) + * and "155" (Western Europe). + */ +StringEnumeration* +Region::getContainedRegions(UErrorCode &status) const { + umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); // returns immediately if U_FAILURE(status) + if (U_FAILURE(status)) { + return nullptr; + } + return new RegionNameEnumeration(containedRegions,status); +} + +/** + * Returns an enumeration over the IDs of all the regions that are children of this region anywhere in the region + * hierarchy and match the given type. This API may return an empty enumeration if this region doesn't have any + * sub-regions that match the given type. For example, calling this method with region "150" (Europe) and type + * "URGN_TERRITORY" returns a set containing all the territories in Europe ( "FR" (France) - "IT" (Italy) - "DE" (Germany) etc. ) + */ +StringEnumeration* +Region::getContainedRegions( URegionType type, UErrorCode &status ) const { + umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); // returns immediately if U_FAILURE(status) + + UVector result(nullptr, uhash_compareChars, status); + LocalPointer cr(getContainedRegions(status), status); + if (U_FAILURE(status)) { + return nullptr; + } + + const char *regionId; + while((regionId = cr->next(nullptr, status)) != nullptr && U_SUCCESS(status)) { + const Region *r = Region::getInstance(regionId, status); + if ( r->getType() == type) { + result.addElement(const_cast(&r->idStr), status); + } else { + LocalPointer children(r->getContainedRegions(type, status)); + const char *id2; + while(U_SUCCESS(status) && ((id2 = children->next(nullptr, status)) != nullptr)) { + const Region *r2 = Region::getInstance(id2,status); + result.addElement(const_cast(&r2->idStr), status); + } + } + } + LocalPointer resultEnumeration( + new RegionNameEnumeration(&result, status), status); + return U_SUCCESS(status) ? resultEnumeration.orphan() : nullptr; +} + +/** + * Returns true if this region contains the supplied other region anywhere in the region hierarchy. + */ +UBool +Region::contains(const Region &other) const { + UErrorCode status = U_ZERO_ERROR; + umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); + + if (!containedRegions) { + return false; + } + if (containedRegions->contains((void *)&other.idStr)) { + return true; + } else { + for ( int32_t i = 0 ; i < containedRegions->size() ; i++ ) { + UnicodeString *crStr = (UnicodeString *)containedRegions->elementAt(i); + Region *cr = (Region *) uhash_get(regionIDMap,(void *)crStr); + if ( cr && cr->contains(other) ) { + return true; + } + } + } + + return false; +} + +/** + * For deprecated regions, return an enumeration over the IDs of the regions that are the preferred replacement + * regions for this region. Returns nullptr for a non-deprecated region. For example, calling this method with region + * "SU" (Soviet Union) would return a list of the regions containing "RU" (Russia), "AM" (Armenia), "AZ" (Azerbaijan), etc... + */ +StringEnumeration* +Region::getPreferredValues(UErrorCode &status) const { + umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); // returns immediately if U_FAILURE(status) + if (U_FAILURE(status) || fType != URGN_DEPRECATED) { + return nullptr; + } + return new RegionNameEnumeration(preferredValues,status); +} + + +/** + * Return this region's canonical region code. + */ +const char* +Region::getRegionCode() const { + return id; +} + +int32_t +Region::getNumericCode() const { + return code; +} + +/** + * Returns the region type of this region. + */ +URegionType +Region::getType() const { + return fType; +} + +RegionNameEnumeration::RegionNameEnumeration(UVector *nameList, UErrorCode& status) : + pos(0), fRegionNames(nullptr) { + // TODO: https://unicode-org.atlassian.net/browse/ICU-21829 + // Is all of the copying going on here really necessary? + if (nameList && U_SUCCESS(status)) { + LocalPointer regionNames( + new UVector(uprv_deleteUObject, uhash_compareUnicodeString, nameList->size(), status), status); + for ( int32_t i = 0 ; U_SUCCESS(status) && i < nameList->size() ; i++ ) { + UnicodeString* this_region_name = (UnicodeString *)nameList->elementAt(i); + LocalPointer new_region_name(new UnicodeString(*this_region_name), status); + regionNames->adoptElement(new_region_name.orphan(), status); + } + if (U_SUCCESS(status)) { + fRegionNames = regionNames.orphan(); + } + } +} + +const UnicodeString* +RegionNameEnumeration::snext(UErrorCode& status) { + if (U_FAILURE(status) || (fRegionNames==nullptr)) { + return nullptr; + } + const UnicodeString* nextStr = (const UnicodeString *)fRegionNames->elementAt(pos); + if (nextStr!=nullptr) { + pos++; + } + return nextStr; +} + +void +RegionNameEnumeration::reset(UErrorCode& /*status*/) { + pos=0; +} + +int32_t +RegionNameEnumeration::count(UErrorCode& /*status*/) const { + return (fRegionNames==nullptr) ? 0 : fRegionNames->size(); +} + +RegionNameEnumeration::~RegionNameEnumeration() { + delete fRegionNames; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/deps/icu-small/source/i18n/region_impl.h b/deps/icu-small/source/i18n/region_impl.h index b6a281393f8911..1737936008bb0d 100644 --- a/deps/icu-small/source/i18n/region_impl.h +++ b/deps/icu-small/source/i18n/region_impl.h @@ -1,49 +1,49 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2013, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -* -* File REGION_IMPL.H -* -******************************************************************************* -*/ - -#ifndef __REGION_IMPL_H__ -#define __REGION_IMPL_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "uvector.h" -#include "unicode/strenum.h" - -U_NAMESPACE_BEGIN - - -class RegionNameEnumeration : public StringEnumeration { -public: - /** - * Construct an string enumeration over the supplied name list. - * Makes a copy of the supplied input name list; does not retain a reference to the original. - */ - RegionNameEnumeration(UVector *nameList, UErrorCode& status); - virtual ~RegionNameEnumeration(); - static UClassID U_EXPORT2 getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const override; - virtual const UnicodeString* snext(UErrorCode& status) override; - virtual void reset(UErrorCode& status) override; - virtual int32_t count(UErrorCode& status) const override; -private: - int32_t pos; - UVector *fRegionNames; -}; - -U_NAMESPACE_END - -#endif - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2013, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* File REGION_IMPL.H +* +******************************************************************************* +*/ + +#ifndef __REGION_IMPL_H__ +#define __REGION_IMPL_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "uvector.h" +#include "unicode/strenum.h" + +U_NAMESPACE_BEGIN + + +class RegionNameEnumeration : public StringEnumeration { +public: + /** + * Construct an string enumeration over the supplied name list. + * Makes a copy of the supplied input name list; does not retain a reference to the original. + */ + RegionNameEnumeration(UVector *nameList, UErrorCode& status); + virtual ~RegionNameEnumeration(); + static UClassID U_EXPORT2 getStaticClassID(); + virtual UClassID getDynamicClassID() const override; + virtual const UnicodeString* snext(UErrorCode& status) override; + virtual void reset(UErrorCode& status) override; + virtual int32_t count(UErrorCode& status) const override; +private: + int32_t pos; + UVector *fRegionNames; +}; + +U_NAMESPACE_END + +#endif + +#endif diff --git a/deps/icu-small/source/i18n/reldatefmt.cpp b/deps/icu-small/source/i18n/reldatefmt.cpp index e811eac2c8d7e6..2c44f055566450 100644 --- a/deps/icu-small/source/i18n/reldatefmt.cpp +++ b/deps/icu-small/source/i18n/reldatefmt.cpp @@ -1,1424 +1,1424 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* Copyright (C) 2014-2016, International Business Machines Corporation and -* others. All Rights Reserved. -****************************************************************************** -* -* File reldatefmt.cpp -****************************************************************************** -*/ - -#include "unicode/reldatefmt.h" - -#if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION - -#include -#include -#include "unicode/calendar.h" -#include "unicode/datefmt.h" -#include "unicode/dtfmtsym.h" -#include "unicode/ucasemap.h" -#include "unicode/ureldatefmt.h" -#include "unicode/udisplaycontext.h" -#include "unicode/unum.h" -#include "unicode/localpointer.h" -#include "unicode/plurrule.h" -#include "unicode/simpleformatter.h" -#include "unicode/decimfmt.h" -#include "unicode/numfmt.h" -#include "unicode/brkiter.h" -#include "unicode/simpleformatter.h" -#include "uresimp.h" -#include "unicode/ures.h" -#include "cstring.h" -#include "ucln_in.h" -#include "mutex.h" -#include "charstr.h" -#include "uassert.h" -#include "quantityformatter.h" -#include "resource.h" -#include "sharedbreakiterator.h" -#include "sharedpluralrules.h" -#include "sharednumberformat.h" -#include "standardplural.h" -#include "unifiedcache.h" -#include "util.h" -#include "formatted_string_builder.h" -#include "number_utypes.h" -#include "number_modifiers.h" -#include "formattedval_impl.h" -#include "number_utils.h" - -// Copied from uscript_props.cpp - -U_NAMESPACE_BEGIN - -// RelativeDateTimeFormatter specific data for a single locale -class RelativeDateTimeCacheData: public SharedObject { -public: - RelativeDateTimeCacheData() : combinedDateAndTime(nullptr) { - // Initialize the cache arrays - for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) { - for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) { - for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) { - relativeUnitsFormatters[style][relUnit][0][pl] = nullptr; - relativeUnitsFormatters[style][relUnit][1][pl] = nullptr; - } - } - } - for (int32_t i = 0; i < UDAT_STYLE_COUNT; ++i) { - fallBackCache[i] = -1; - } - } - virtual ~RelativeDateTimeCacheData(); - - // no numbers: e.g Next Tuesday; Yesterday; etc. - UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT]; - - // SimpleFormatter pointers for relative unit format, - // e.g., Next Tuesday; Yesterday; etc. For third index, 0 - // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days. - SimpleFormatter *relativeUnitsFormatters[UDAT_STYLE_COUNT] - [UDAT_REL_UNIT_COUNT][2][StandardPlural::COUNT]; - - const UnicodeString& getAbsoluteUnitString(int32_t fStyle, - UDateAbsoluteUnit unit, - UDateDirection direction) const; - const SimpleFormatter* getRelativeUnitFormatter(int32_t fStyle, - UDateRelativeUnit unit, - int32_t pastFutureIndex, - int32_t pluralUnit) const; - const SimpleFormatter* getRelativeDateTimeUnitFormatter(int32_t fStyle, - URelativeDateTimeUnit unit, - int32_t pastFutureIndex, - int32_t pluralUnit) const; - - const UnicodeString emptyString; - - // Mapping from source to target styles for alias fallback. - int32_t fallBackCache[UDAT_STYLE_COUNT]; - - void adoptCombinedDateAndTime(SimpleFormatter *fmtToAdopt) { - delete combinedDateAndTime; - combinedDateAndTime = fmtToAdopt; - } - const SimpleFormatter *getCombinedDateAndTime() const { - return combinedDateAndTime; - } - -private: - SimpleFormatter *combinedDateAndTime; - RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other); - RelativeDateTimeCacheData& operator=( - const RelativeDateTimeCacheData &other); -}; - -RelativeDateTimeCacheData::~RelativeDateTimeCacheData() { - // clear out the cache arrays - for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) { - for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) { - for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) { - delete relativeUnitsFormatters[style][relUnit][0][pl]; - delete relativeUnitsFormatters[style][relUnit][1][pl]; - } - } - } - delete combinedDateAndTime; -} - - -// Use fallback cache for absolute units. -const UnicodeString& RelativeDateTimeCacheData::getAbsoluteUnitString( - int32_t fStyle, UDateAbsoluteUnit unit, UDateDirection direction) const { - int32_t style = fStyle; - do { - if (!absoluteUnits[style][unit][direction].isEmpty()) { - return absoluteUnits[style][unit][direction]; - } - style = fallBackCache[style]; - } while (style != -1); - return emptyString; -} - - const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter( - int32_t fStyle, - UDateRelativeUnit unit, - int32_t pastFutureIndex, - int32_t pluralUnit) const { - URelativeDateTimeUnit rdtunit = UDAT_REL_UNIT_COUNT; - switch (unit) { - case UDAT_RELATIVE_YEARS: rdtunit = UDAT_REL_UNIT_YEAR; break; - case UDAT_RELATIVE_MONTHS: rdtunit = UDAT_REL_UNIT_MONTH; break; - case UDAT_RELATIVE_WEEKS: rdtunit = UDAT_REL_UNIT_WEEK; break; - case UDAT_RELATIVE_DAYS: rdtunit = UDAT_REL_UNIT_DAY; break; - case UDAT_RELATIVE_HOURS: rdtunit = UDAT_REL_UNIT_HOUR; break; - case UDAT_RELATIVE_MINUTES: rdtunit = UDAT_REL_UNIT_MINUTE; break; - case UDAT_RELATIVE_SECONDS: rdtunit = UDAT_REL_UNIT_SECOND; break; - default: // a unit that the above method does not handle - return nullptr; - } - - return getRelativeDateTimeUnitFormatter(fStyle, rdtunit, pastFutureIndex, pluralUnit); - } - - // Use fallback cache for SimpleFormatter relativeUnits. - const SimpleFormatter* RelativeDateTimeCacheData::getRelativeDateTimeUnitFormatter( - int32_t fStyle, - URelativeDateTimeUnit unit, - int32_t pastFutureIndex, - int32_t pluralUnit) const { - while (true) { - int32_t style = fStyle; - do { - if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != nullptr) { - return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit]; - } - style = fallBackCache[style]; - } while (style != -1); - - if (pluralUnit == StandardPlural::OTHER) { - break; - } - pluralUnit = StandardPlural::OTHER; - } - return nullptr; // No formatter found. - } - -static UBool getStringByIndex( - const UResourceBundle *resource, - int32_t idx, - UnicodeString &result, - UErrorCode &status) { - int32_t len = 0; - const UChar *resStr = ures_getStringByIndex( - resource, idx, &len, &status); - if (U_FAILURE(status)) { - return false; - } - result.setTo(true, resStr, len); - return true; -} - -namespace { - -/** - * Sink for enumerating all of the measurement unit display names. - * - * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root): - * Only store a value if it is still missing, that is, it has not been overridden. - */ -struct RelDateTimeFmtDataSink : public ResourceSink { - - /** - * Sink for patterns for relative dates and times. For example, - * fields/relative/... - */ - - // Generic unit enum for storing Unit info. - typedef enum RelAbsUnit { - INVALID_UNIT = -1, - SECOND, - MINUTE, - HOUR, - DAY, - WEEK, - MONTH, - QUARTER, - YEAR, - SUNDAY, - MONDAY, - TUESDAY, - WEDNESDAY, - THURSDAY, - FRIDAY, - SATURDAY - } RelAbsUnit; - - static int32_t relUnitFromGeneric(RelAbsUnit genUnit) { - // Converts the generic units to UDAT_RELATIVE version. - switch (genUnit) { - case SECOND: - return UDAT_REL_UNIT_SECOND; - case MINUTE: - return UDAT_REL_UNIT_MINUTE; - case HOUR: - return UDAT_REL_UNIT_HOUR; - case DAY: - return UDAT_REL_UNIT_DAY; - case WEEK: - return UDAT_REL_UNIT_WEEK; - case MONTH: - return UDAT_REL_UNIT_MONTH; - case QUARTER: - return UDAT_REL_UNIT_QUARTER; - case YEAR: - return UDAT_REL_UNIT_YEAR; - case SUNDAY: - return UDAT_REL_UNIT_SUNDAY; - case MONDAY: - return UDAT_REL_UNIT_MONDAY; - case TUESDAY: - return UDAT_REL_UNIT_TUESDAY; - case WEDNESDAY: - return UDAT_REL_UNIT_WEDNESDAY; - case THURSDAY: - return UDAT_REL_UNIT_THURSDAY; - case FRIDAY: - return UDAT_REL_UNIT_FRIDAY; - case SATURDAY: - return UDAT_REL_UNIT_SATURDAY; - default: - return -1; - } - } - - static int32_t absUnitFromGeneric(RelAbsUnit genUnit) { - // Converts the generic units to UDAT_RELATIVE version. - switch (genUnit) { - case DAY: - return UDAT_ABSOLUTE_DAY; - case WEEK: - return UDAT_ABSOLUTE_WEEK; - case MONTH: - return UDAT_ABSOLUTE_MONTH; - case QUARTER: - return UDAT_ABSOLUTE_QUARTER; - case YEAR: - return UDAT_ABSOLUTE_YEAR; - case SUNDAY: - return UDAT_ABSOLUTE_SUNDAY; - case MONDAY: - return UDAT_ABSOLUTE_MONDAY; - case TUESDAY: - return UDAT_ABSOLUTE_TUESDAY; - case WEDNESDAY: - return UDAT_ABSOLUTE_WEDNESDAY; - case THURSDAY: - return UDAT_ABSOLUTE_THURSDAY; - case FRIDAY: - return UDAT_ABSOLUTE_FRIDAY; - case SATURDAY: - return UDAT_ABSOLUTE_SATURDAY; - case HOUR: - return UDAT_ABSOLUTE_HOUR; - case MINUTE: - return UDAT_ABSOLUTE_MINUTE; - default: - return -1; - } - } - - static int32_t keyToDirection(const char* key) { - if (uprv_strcmp(key, "-2") == 0) { - return UDAT_DIRECTION_LAST_2; - } - if (uprv_strcmp(key, "-1") == 0) { - return UDAT_DIRECTION_LAST; - } - if (uprv_strcmp(key, "0") == 0) { - return UDAT_DIRECTION_THIS; - } - if (uprv_strcmp(key, "1") == 0) { - return UDAT_DIRECTION_NEXT; - } - if (uprv_strcmp(key, "2") == 0) { - return UDAT_DIRECTION_NEXT_2; - } - return -1; - } - - // Values kept between levels of parsing the CLDR data. - int32_t pastFutureIndex; // 0 == past or 1 == future - UDateRelativeDateTimeFormatterStyle style; // {LONG, SHORT, NARROW} - RelAbsUnit genericUnit; - - RelativeDateTimeCacheData &outputData; - - // Constructor - RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData) - : outputData(cacheData) { - // Clear cacheData.fallBackCache - cacheData.fallBackCache[UDAT_STYLE_LONG] = -1; - cacheData.fallBackCache[UDAT_STYLE_SHORT] = -1; - cacheData.fallBackCache[UDAT_STYLE_NARROW] = -1; - } - - ~RelDateTimeFmtDataSink(); - - // Utility functions - static UDateRelativeDateTimeFormatterStyle styleFromString(const char *s) { - int32_t len = static_cast(uprv_strlen(s)); - if (len >= 7 && uprv_strcmp(s + len - 7, "-narrow") == 0) { - return UDAT_STYLE_NARROW; - } - if (len >= 6 && uprv_strcmp(s + len - 6, "-short") == 0) { - return UDAT_STYLE_SHORT; - } - return UDAT_STYLE_LONG; - } - - static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style) { - switch (style) { - case UDAT_STYLE_NARROW: - return 7; - case UDAT_STYLE_SHORT: - return 6; - default: - return 0; - } - } - - // Utility functions - static UDateRelativeDateTimeFormatterStyle styleFromAliasUnicodeString(UnicodeString s) { - static const UChar narrow[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077}; - static const UChar sshort[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,}; - if (s.endsWith(narrow, 7)) { - return UDAT_STYLE_NARROW; - } - if (s.endsWith(sshort, 6)) { - return UDAT_STYLE_SHORT; - } - return UDAT_STYLE_LONG; - } - - static RelAbsUnit unitOrNegativeFromString(const char* keyword, int32_t length) { - // Quick check from string to enum. - switch (length) { - case 3: - if (uprv_strncmp(keyword, "day", length) == 0) { - return DAY; - } else if (uprv_strncmp(keyword, "sun", length) == 0) { - return SUNDAY; - } else if (uprv_strncmp(keyword, "mon", length) == 0) { - return MONDAY; - } else if (uprv_strncmp(keyword, "tue", length) == 0) { - return TUESDAY; - } else if (uprv_strncmp(keyword, "wed", length) == 0) { - return WEDNESDAY; - } else if (uprv_strncmp(keyword, "thu", length) == 0) { - return THURSDAY; - } else if (uprv_strncmp(keyword, "fri", length) == 0) { - return FRIDAY; - } else if (uprv_strncmp(keyword, "sat", length) == 0) { - return SATURDAY; - } - break; - case 4: - if (uprv_strncmp(keyword, "hour", length) == 0) { - return HOUR; - } else if (uprv_strncmp(keyword, "week", length) == 0) { - return WEEK; - } else if (uprv_strncmp(keyword, "year", length) == 0) { - return YEAR; - } - break; - case 5: - if (uprv_strncmp(keyword, "month", length) == 0) { - return MONTH; - } - break; - case 6: - if (uprv_strncmp(keyword, "minute", length) == 0) { - return MINUTE; - } else if (uprv_strncmp(keyword, "second", length) == 0) { - return SECOND; - } - break; - case 7: - if (uprv_strncmp(keyword, "quarter", length) == 0) { - return QUARTER; // TODO: Check @provisional - } - break; - default: - break; - } - return INVALID_UNIT; - } - - void handlePlainDirection(ResourceValue &value, UErrorCode &errorCode) { - // Handle Display Name for PLAIN direction for some units. - if (U_FAILURE(errorCode)) { return; } - - int32_t absUnit = absUnitFromGeneric(genericUnit); - if (absUnit < 0) { - return; // Not interesting. - } - - // Store displayname if not set. - if (outputData.absoluteUnits[style] - [absUnit][UDAT_DIRECTION_PLAIN].isEmpty()) { - outputData.absoluteUnits[style] - [absUnit][UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode)); - return; - } - } - - void consumeTableRelative(const char *key, ResourceValue &value, UErrorCode &errorCode) { - ResourceTable unitTypesTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - - for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) { - if (value.getType() == URES_STRING) { - int32_t direction = keyToDirection(key); - if (direction < 0) { - continue; - } - - int32_t relUnitIndex = relUnitFromGeneric(genericUnit); - if (relUnitIndex == UDAT_REL_UNIT_SECOND && uprv_strcmp(key, "0") == 0 && - outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN].isEmpty()) { - // Handle "NOW" - outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW] - [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode)); - } - - int32_t absUnitIndex = absUnitFromGeneric(genericUnit); - if (absUnitIndex < 0) { - continue; - } - // Only reset if slot is empty. - if (outputData.absoluteUnits[style][absUnitIndex][direction].isEmpty()) { - outputData.absoluteUnits[style][absUnitIndex] - [direction].fastCopyFrom(value.getUnicodeString(errorCode)); - } - } - } - } - - void consumeTimeDetail(int32_t relUnitIndex, - const char *key, ResourceValue &value, UErrorCode &errorCode) { - ResourceTable unitTypesTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - - for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) { - if (value.getType() == URES_STRING) { - int32_t pluralIndex = StandardPlural::indexOrNegativeFromString(key); - if (pluralIndex >= 0) { - SimpleFormatter **patterns = - outputData.relativeUnitsFormatters[style][relUnitIndex] - [pastFutureIndex]; - // Only set if not already established. - if (patterns[pluralIndex] == nullptr) { - patterns[pluralIndex] = new SimpleFormatter( - value.getUnicodeString(errorCode), 0, 1, errorCode); - if (patterns[pluralIndex] == nullptr) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - } - } - } - } - } - } - - void consumeTableRelativeTime(const char *key, ResourceValue &value, UErrorCode &errorCode) { - ResourceTable relativeTimeTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - - int32_t relUnitIndex = relUnitFromGeneric(genericUnit); - if (relUnitIndex < 0) { - return; - } - for (int32_t i = 0; relativeTimeTable.getKeyAndValue(i, key, value); ++i) { - if (uprv_strcmp(key, "past") == 0) { - pastFutureIndex = 0; - } else if (uprv_strcmp(key, "future") == 0) { - pastFutureIndex = 1; - } else { - // Unknown key. - continue; - } - consumeTimeDetail(relUnitIndex, key, value, errorCode); - } - } - - void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) { - - UDateRelativeDateTimeFormatterStyle sourceStyle = styleFromString(key); - const UnicodeString valueStr = value.getAliasUnicodeString(errorCode); - if (U_FAILURE(errorCode)) { return; } - - UDateRelativeDateTimeFormatterStyle targetStyle = - styleFromAliasUnicodeString(valueStr); - - if (sourceStyle == targetStyle) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - if (outputData.fallBackCache[sourceStyle] != -1 && - outputData.fallBackCache[sourceStyle] != targetStyle) { - errorCode = U_INVALID_FORMAT_ERROR; - return; - } - outputData.fallBackCache[sourceStyle] = targetStyle; - } - - void consumeTimeUnit(const char *key, ResourceValue &value, UErrorCode &errorCode) { - ResourceTable unitTypesTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - - for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) { - // Handle display name. - if (uprv_strcmp(key, "dn") == 0 && value.getType() == URES_STRING) { - handlePlainDirection(value, errorCode); - } - if (value.getType() == URES_TABLE) { - if (uprv_strcmp(key, "relative") == 0) { - consumeTableRelative(key, value, errorCode); - } else if (uprv_strcmp(key, "relativeTime") == 0) { - consumeTableRelativeTime(key, value, errorCode); - } - } - } - } - - virtual void put(const char *key, ResourceValue &value, - UBool /*noFallback*/, UErrorCode &errorCode) override { - // Main entry point to sink - ResourceTable table = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int32_t i = 0; table.getKeyAndValue(i, key, value); ++i) { - if (value.getType() == URES_ALIAS) { - consumeAlias(key, value, errorCode); - } else { - style = styleFromString(key); - int32_t unitSize = static_cast(uprv_strlen(key)) - styleSuffixLength(style); - genericUnit = unitOrNegativeFromString(key, unitSize); - if (style >= 0 && genericUnit != INVALID_UNIT) { - consumeTimeUnit(key, value, errorCode); - } - } - } - } - -}; - -// Virtual destructors must be defined out of line. -RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {} -} // namespace - -static const DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = { - DateFormatSymbols::WIDE, DateFormatSymbols::SHORT, DateFormatSymbols::NARROW -}; - -// Get days of weeks from the DateFormatSymbols class. -static void loadWeekdayNames(UnicodeString absoluteUnits[UDAT_STYLE_COUNT] - [UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT], - const char* localeId, - UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - Locale locale(localeId); - DateFormatSymbols dfSym(locale, status); - if (U_FAILURE(status)) { - return; - } - for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) { - DateFormatSymbols::DtWidthType dtfmtWidth = styleToDateFormatSymbolWidth[style]; - int32_t count; - const UnicodeString* weekdayNames = - dfSym.getWeekdays(count, DateFormatSymbols::STANDALONE, dtfmtWidth); - for (int32_t dayIndex = UDAT_ABSOLUTE_SUNDAY; - dayIndex <= UDAT_ABSOLUTE_SATURDAY; ++ dayIndex) { - int32_t dateSymbolIndex = (dayIndex - UDAT_ABSOLUTE_SUNDAY) + UCAL_SUNDAY; - absoluteUnits[style][dayIndex][UDAT_DIRECTION_PLAIN].fastCopyFrom( - weekdayNames[dateSymbolIndex]); - } - } -} - -static UBool loadUnitData( - const UResourceBundle *resource, - RelativeDateTimeCacheData &cacheData, - const char* localeId, - UErrorCode &status) { - - RelDateTimeFmtDataSink sink(cacheData); - - ures_getAllItemsWithFallback(resource, "fields", sink, status); - if (U_FAILURE(status)) { - return false; - } - - // Get the weekday names from DateFormatSymbols. - loadWeekdayNames(cacheData.absoluteUnits, localeId, status); - return U_SUCCESS(status); -} - -static const int32_t cTypeBufMax = 32; - -static UBool getDateTimePattern( - Locale locale, - const UResourceBundle *resource, - UnicodeString &result, - UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - char cType[cTypeBufMax + 1]; - Calendar::getCalendarTypeFromLocale(locale, cType, cTypeBufMax, status); - cType[cTypeBufMax] = 0; - if (U_FAILURE(status) || cType[0] == 0) { - status = U_ZERO_ERROR; - uprv_strcpy(cType, "gregorian"); - } - - LocalUResourceBundlePointer topLevel; - int32_t dateTimeFormatOffset = DateFormat::kMedium; - CharString pathBuffer; - // Currently, for compatibility with pre-CLDR-42 data, we default to the "atTime" - // combining patterns. Depending on guidance in CLDR 42 spec and on DisplayOptions, - // we may change this. - pathBuffer.append("calendar/", status) - .append(cType, status) - .append("/DateTimePatterns%atTime", status); - topLevel.adoptInstead( - ures_getByKeyWithFallback( - resource, pathBuffer.data(), nullptr, &status)); - if (U_FAILURE(status) || ures_getSize(topLevel.getAlias()) < 4) { - // Fall back to standard combining patterns - status = U_ZERO_ERROR; - dateTimeFormatOffset = DateFormat::kDateTime; - pathBuffer.clear(); - pathBuffer.append("calendar/", status) - .append(cType, status) - .append("/DateTimePatterns", status); - topLevel.adoptInstead( - ures_getByKeyWithFallback( - resource, pathBuffer.data(), nullptr, &status)); - } - if (U_FAILURE(status)) { - return false; - } - if (dateTimeFormatOffset == DateFormat::kDateTime && ures_getSize(topLevel.getAlias()) <= DateFormat::kDateTime) { - // Oops, size is too small to access the index that we want, fallback - // to a hard-coded value. - result = UNICODE_STRING_SIMPLE("{1} {0}"); - return true; - } - return getStringByIndex(topLevel.getAlias(), dateTimeFormatOffset, result, status); -} - -template<> -const RelativeDateTimeCacheData *LocaleCacheKey::createObject(const void * /*unused*/, UErrorCode &status) const { - const char *localeId = fLoc.getName(); - LocalUResourceBundlePointer topLevel(ures_open(nullptr, localeId, &status)); - if (U_FAILURE(status)) { - return nullptr; - } - LocalPointer result( - new RelativeDateTimeCacheData()); - if (result.isNull()) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - if (!loadUnitData( - topLevel.getAlias(), - *result, - localeId, - status)) { - return nullptr; - } - UnicodeString dateTimePattern; - if (!getDateTimePattern(fLoc, topLevel.getAlias(), dateTimePattern, status)) { - return nullptr; - } - result->adoptCombinedDateAndTime( - new SimpleFormatter(dateTimePattern, 2, 2, status)); - if (U_FAILURE(status)) { - return nullptr; - } - result->addRef(); - return result.orphan(); -} - - - -static constexpr FormattedStringBuilder::Field kRDTNumericField - = {UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_NUMERIC_FIELD}; - -static constexpr FormattedStringBuilder::Field kRDTLiteralField - = {UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_LITERAL_FIELD}; - -class FormattedRelativeDateTimeData : public FormattedValueStringBuilderImpl { -public: - FormattedRelativeDateTimeData() : FormattedValueStringBuilderImpl(kRDTNumericField) {} - virtual ~FormattedRelativeDateTimeData(); -}; - -FormattedRelativeDateTimeData::~FormattedRelativeDateTimeData() = default; - - -UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedRelativeDateTime) - - -RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) : - fCache(nullptr), - fNumberFormat(nullptr), - fPluralRules(nullptr), - fStyle(UDAT_STYLE_LONG), - fContext(UDISPCTX_CAPITALIZATION_NONE), - fOptBreakIterator(nullptr) { - init(nullptr, nullptr, status); -} - -RelativeDateTimeFormatter::RelativeDateTimeFormatter( - const Locale& locale, UErrorCode& status) : - fCache(nullptr), - fNumberFormat(nullptr), - fPluralRules(nullptr), - fStyle(UDAT_STYLE_LONG), - fContext(UDISPCTX_CAPITALIZATION_NONE), - fOptBreakIterator(nullptr), - fLocale(locale) { - init(nullptr, nullptr, status); -} - -RelativeDateTimeFormatter::RelativeDateTimeFormatter( - const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) : - fCache(nullptr), - fNumberFormat(nullptr), - fPluralRules(nullptr), - fStyle(UDAT_STYLE_LONG), - fContext(UDISPCTX_CAPITALIZATION_NONE), - fOptBreakIterator(nullptr), - fLocale(locale) { - init(nfToAdopt, nullptr, status); -} - -RelativeDateTimeFormatter::RelativeDateTimeFormatter( - const Locale& locale, - NumberFormat *nfToAdopt, - UDateRelativeDateTimeFormatterStyle styl, - UDisplayContext capitalizationContext, - UErrorCode& status) : - fCache(nullptr), - fNumberFormat(nullptr), - fPluralRules(nullptr), - fStyle(styl), - fContext(capitalizationContext), - fOptBreakIterator(nullptr), - fLocale(locale) { - if (U_FAILURE(status)) { - return; - } - if ((capitalizationContext >> 8) != UDISPCTX_TYPE_CAPITALIZATION) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) { - BreakIterator *bi = BreakIterator::createSentenceInstance(locale, status); - if (U_FAILURE(status)) { - return; - } - init(nfToAdopt, bi, status); - } else { - init(nfToAdopt, nullptr, status); - } -} - -RelativeDateTimeFormatter::RelativeDateTimeFormatter( - const RelativeDateTimeFormatter& other) - : UObject(other), - fCache(other.fCache), - fNumberFormat(other.fNumberFormat), - fPluralRules(other.fPluralRules), - fStyle(other.fStyle), - fContext(other.fContext), - fOptBreakIterator(other.fOptBreakIterator), - fLocale(other.fLocale) { - fCache->addRef(); - fNumberFormat->addRef(); - fPluralRules->addRef(); - if (fOptBreakIterator != nullptr) { - fOptBreakIterator->addRef(); - } -} - -RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=( - const RelativeDateTimeFormatter& other) { - if (this != &other) { - SharedObject::copyPtr(other.fCache, fCache); - SharedObject::copyPtr(other.fNumberFormat, fNumberFormat); - SharedObject::copyPtr(other.fPluralRules, fPluralRules); - SharedObject::copyPtr(other.fOptBreakIterator, fOptBreakIterator); - fStyle = other.fStyle; - fContext = other.fContext; - fLocale = other.fLocale; - } - return *this; -} - -RelativeDateTimeFormatter::~RelativeDateTimeFormatter() { - if (fCache != nullptr) { - fCache->removeRef(); - } - if (fNumberFormat != nullptr) { - fNumberFormat->removeRef(); - } - if (fPluralRules != nullptr) { - fPluralRules->removeRef(); - } - if (fOptBreakIterator != nullptr) { - fOptBreakIterator->removeRef(); - } -} - -const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const { - return **fNumberFormat; -} - -UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const { - return fContext; -} - -UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const { - return fStyle; -} - - -// To reduce boilerplate code, we use a helper function that forwards variadic -// arguments to the formatImpl function. - -template -UnicodeString& RelativeDateTimeFormatter::doFormat( - F callback, - UnicodeString& appendTo, - UErrorCode& status, - Args... args) const { - FormattedRelativeDateTimeData output; - (this->*callback)(std::forward(args)..., output, status); - if (U_FAILURE(status)) { - return appendTo; - } - UnicodeString result = output.getStringRef().toUnicodeString(); - return appendTo.append(adjustForContext(result)); -} - -template -FormattedRelativeDateTime RelativeDateTimeFormatter::doFormatToValue( - F callback, - UErrorCode& status, - Args... args) const { - if (!checkNoAdjustForContext(status)) { - return FormattedRelativeDateTime(status); - } - LocalPointer output( - new FormattedRelativeDateTimeData(), status); - if (U_FAILURE(status)) { - return FormattedRelativeDateTime(status); - } - (this->*callback)(std::forward(args)..., *output, status); - output->getStringRef().writeTerminator(status); - return FormattedRelativeDateTime(output.orphan()); -} - -UnicodeString& RelativeDateTimeFormatter::format( - double quantity, - UDateDirection direction, - UDateRelativeUnit unit, - UnicodeString& appendTo, - UErrorCode& status) const { - return doFormat( - &RelativeDateTimeFormatter::formatImpl, - appendTo, - status, - quantity, - direction, - unit); -} - -FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue( - double quantity, - UDateDirection direction, - UDateRelativeUnit unit, - UErrorCode& status) const { - return doFormatToValue( - &RelativeDateTimeFormatter::formatImpl, - status, - quantity, - direction, - unit); -} - -void RelativeDateTimeFormatter::formatImpl( - double quantity, - UDateDirection direction, - UDateRelativeUnit unit, - FormattedRelativeDateTimeData& output, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0; - - StandardPlural::Form pluralForm; - QuantityFormatter::formatAndSelect( - quantity, - **fNumberFormat, - **fPluralRules, - output.getStringRef(), - pluralForm, - status); - if (U_FAILURE(status)) { - return; - } - - const SimpleFormatter* formatter = - fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralForm); - if (formatter == nullptr) { - // TODO: WARN - look at quantity formatter's action with an error. - status = U_INVALID_FORMAT_ERROR; - return; - } - - number::impl::SimpleModifier modifier(*formatter, kRDTLiteralField, false); - modifier.formatAsPrefixSuffix( - output.getStringRef(), 0, output.getStringRef().length(), status); -} - -UnicodeString& RelativeDateTimeFormatter::formatNumeric( - double offset, - URelativeDateTimeUnit unit, - UnicodeString& appendTo, - UErrorCode& status) const { - return doFormat( - &RelativeDateTimeFormatter::formatNumericImpl, - appendTo, - status, - offset, - unit); -} - -FormattedRelativeDateTime RelativeDateTimeFormatter::formatNumericToValue( - double offset, - URelativeDateTimeUnit unit, - UErrorCode& status) const { - return doFormatToValue( - &RelativeDateTimeFormatter::formatNumericImpl, - status, - offset, - unit); -} - -void RelativeDateTimeFormatter::formatNumericImpl( - double offset, - URelativeDateTimeUnit unit, - FormattedRelativeDateTimeData& output, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - UDateDirection direction = UDAT_DIRECTION_NEXT; - if (std::signbit(offset)) { // needed to handle -0.0 - direction = UDAT_DIRECTION_LAST; - offset = -offset; - } - if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0; - - StandardPlural::Form pluralForm; - QuantityFormatter::formatAndSelect( - offset, - **fNumberFormat, - **fPluralRules, - output.getStringRef(), - pluralForm, - status); - if (U_FAILURE(status)) { - return; - } - - const SimpleFormatter* formatter = - fCache->getRelativeDateTimeUnitFormatter(fStyle, unit, bFuture, pluralForm); - if (formatter == nullptr) { - // TODO: WARN - look at quantity formatter's action with an error. - status = U_INVALID_FORMAT_ERROR; - return; - } - - number::impl::SimpleModifier modifier(*formatter, kRDTLiteralField, false); - modifier.formatAsPrefixSuffix( - output.getStringRef(), 0, output.getStringRef().length(), status); -} - -UnicodeString& RelativeDateTimeFormatter::format( - UDateDirection direction, - UDateAbsoluteUnit unit, - UnicodeString& appendTo, - UErrorCode& status) const { - return doFormat( - &RelativeDateTimeFormatter::formatAbsoluteImpl, - appendTo, - status, - direction, - unit); -} - -FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue( - UDateDirection direction, - UDateAbsoluteUnit unit, - UErrorCode& status) const { - return doFormatToValue( - &RelativeDateTimeFormatter::formatAbsoluteImpl, - status, - direction, - unit); -} - -void RelativeDateTimeFormatter::formatAbsoluteImpl( - UDateDirection direction, - UDateAbsoluteUnit unit, - FormattedRelativeDateTimeData& output, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - // Get string using fallback. - output.getStringRef().append( - fCache->getAbsoluteUnitString(fStyle, unit, direction), - kRDTLiteralField, - status); -} - -UnicodeString& RelativeDateTimeFormatter::format( - double offset, - URelativeDateTimeUnit unit, - UnicodeString& appendTo, - UErrorCode& status) const { - return doFormat( - &RelativeDateTimeFormatter::formatRelativeImpl, - appendTo, - status, - offset, - unit); -} - -FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue( - double offset, - URelativeDateTimeUnit unit, - UErrorCode& status) const { - return doFormatToValue( - &RelativeDateTimeFormatter::formatRelativeImpl, - status, - offset, - unit); -} - -void RelativeDateTimeFormatter::formatRelativeImpl( - double offset, - URelativeDateTimeUnit unit, - FormattedRelativeDateTimeData& output, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - // TODO: - // The full implementation of this depends on CLDR data that is not yet available, - // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data. - // In the meantime do a quick bring-up by calling the old format method; this - // leaves some holes (even for data that is currently available, such as quarter). - // When the new CLDR data is available, update the data storage accordingly, - // rewrite this to use it directly, and rewrite the old format method to call this - // new one; that is covered by https://unicode-org.atlassian.net/browse/ICU-12171. - UDateDirection direction = UDAT_DIRECTION_COUNT; - if (offset > -2.1 && offset < 2.1) { - // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST - double offsetx100 = offset * 100.0; - int32_t intoffset = (offsetx100 < 0)? (int32_t)(offsetx100-0.5) : (int32_t)(offsetx100+0.5); - switch (intoffset) { - case -200/*-2*/: direction = UDAT_DIRECTION_LAST_2; break; - case -100/*-1*/: direction = UDAT_DIRECTION_LAST; break; - case 0/* 0*/: direction = UDAT_DIRECTION_THIS; break; - case 100/* 1*/: direction = UDAT_DIRECTION_NEXT; break; - case 200/* 2*/: direction = UDAT_DIRECTION_NEXT_2; break; - default: break; - } - } - UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT; - switch (unit) { - case UDAT_REL_UNIT_YEAR: absunit = UDAT_ABSOLUTE_YEAR; break; - case UDAT_REL_UNIT_QUARTER: absunit = UDAT_ABSOLUTE_QUARTER; break; - case UDAT_REL_UNIT_MONTH: absunit = UDAT_ABSOLUTE_MONTH; break; - case UDAT_REL_UNIT_WEEK: absunit = UDAT_ABSOLUTE_WEEK; break; - case UDAT_REL_UNIT_DAY: absunit = UDAT_ABSOLUTE_DAY; break; - case UDAT_REL_UNIT_SECOND: - if (direction == UDAT_DIRECTION_THIS) { - absunit = UDAT_ABSOLUTE_NOW; - direction = UDAT_DIRECTION_PLAIN; - } - break; - case UDAT_REL_UNIT_SUNDAY: absunit = UDAT_ABSOLUTE_SUNDAY; break; - case UDAT_REL_UNIT_MONDAY: absunit = UDAT_ABSOLUTE_MONDAY; break; - case UDAT_REL_UNIT_TUESDAY: absunit = UDAT_ABSOLUTE_TUESDAY; break; - case UDAT_REL_UNIT_WEDNESDAY: absunit = UDAT_ABSOLUTE_WEDNESDAY; break; - case UDAT_REL_UNIT_THURSDAY: absunit = UDAT_ABSOLUTE_THURSDAY; break; - case UDAT_REL_UNIT_FRIDAY: absunit = UDAT_ABSOLUTE_FRIDAY; break; - case UDAT_REL_UNIT_SATURDAY: absunit = UDAT_ABSOLUTE_SATURDAY; break; - case UDAT_REL_UNIT_HOUR: absunit = UDAT_ABSOLUTE_HOUR; break; - case UDAT_REL_UNIT_MINUTE: absunit = UDAT_ABSOLUTE_MINUTE; break; - default: break; - } - if (direction != UDAT_DIRECTION_COUNT && absunit != UDAT_ABSOLUTE_UNIT_COUNT) { - formatAbsoluteImpl(direction, absunit, output, status); - if (output.getStringRef().length() != 0) { - return; - } - } - // otherwise fallback to formatNumeric - formatNumericImpl(offset, unit, output, status); -} - -UnicodeString& RelativeDateTimeFormatter::combineDateAndTime( - const UnicodeString& relativeDateString, const UnicodeString& timeString, - UnicodeString& appendTo, UErrorCode& status) const { - return fCache->getCombinedDateAndTime()->format( - timeString, relativeDateString, appendTo, status); -} - -UnicodeString& RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const { - if (fOptBreakIterator == nullptr - || str.length() == 0 || !u_islower(str.char32At(0))) { - return str; - } - - // Must guarantee that one thread at a time accesses the shared break - // iterator. - static UMutex gBrkIterMutex; - Mutex lock(&gBrkIterMutex); - str.toTitle( - fOptBreakIterator->get(), - fLocale, - U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); - return str; -} - -UBool RelativeDateTimeFormatter::checkNoAdjustForContext(UErrorCode& status) const { - // This is unsupported because it's hard to keep fields in sync with title - // casing. The code could be written and tested if there is demand. - if (fOptBreakIterator != nullptr) { - status = U_UNSUPPORTED_ERROR; - return false; - } - return true; -} - -void RelativeDateTimeFormatter::init( - NumberFormat *nfToAdopt, - BreakIterator *biToAdopt, - UErrorCode &status) { - LocalPointer nf(nfToAdopt); - LocalPointer bi(biToAdopt); - UnifiedCache::getByLocale(fLocale, fCache, status); - if (U_FAILURE(status)) { - return; - } - const SharedPluralRules *pr = PluralRules::createSharedInstance( - fLocale, UPLURAL_TYPE_CARDINAL, status); - if (U_FAILURE(status)) { - return; - } - SharedObject::copyPtr(pr, fPluralRules); - pr->removeRef(); - if (nf.isNull()) { - const SharedNumberFormat *shared = NumberFormat::createSharedInstance( - fLocale, UNUM_DECIMAL, status); - if (U_FAILURE(status)) { - return; - } - SharedObject::copyPtr(shared, fNumberFormat); - shared->removeRef(); - } else { - SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias()); - if (shared == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - nf.orphan(); - SharedObject::copyPtr(shared, fNumberFormat); - } - if (bi.isNull()) { - SharedObject::clearPtr(fOptBreakIterator); - } else { - SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias()); - if (shared == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - bi.orphan(); - SharedObject::copyPtr(shared, fOptBreakIterator); - } -} - -U_NAMESPACE_END - -// Plain C API - -U_NAMESPACE_USE - - -// Magic number: "FRDT" (FormattedRelativeDateTime) in ASCII -UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL( - FormattedRelativeDateTime, - UFormattedRelativeDateTime, - UFormattedRelativeDateTimeImpl, - UFormattedRelativeDateTimeApiHelper, - ureldatefmt, - 0x46524454) - - -U_CAPI URelativeDateTimeFormatter* U_EXPORT2 -ureldatefmt_open( const char* locale, - UNumberFormat* nfToAdopt, - UDateRelativeDateTimeFormatterStyle width, - UDisplayContext capitalizationContext, - UErrorCode* status ) -{ - if (U_FAILURE(*status)) { - return nullptr; - } - LocalPointer formatter(new RelativeDateTimeFormatter(Locale(locale), - (NumberFormat*)nfToAdopt, width, - capitalizationContext, *status), *status); - if (U_FAILURE(*status)) { - return nullptr; - } - return (URelativeDateTimeFormatter*)formatter.orphan(); -} - -U_CAPI void U_EXPORT2 -ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt) -{ - delete (RelativeDateTimeFormatter*)reldatefmt; -} - -U_CAPI int32_t U_EXPORT2 -ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt, - double offset, - URelativeDateTimeUnit unit, - UChar* result, - int32_t resultCapacity, - UErrorCode* status) -{ - if (U_FAILURE(*status)) { - return 0; - } - if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString res; - if (result != nullptr) { - // nullptr destination for pure preflighting: empty dummy string - // otherwise, alias the destination buffer (copied from udat_format) - res.setTo(result, 0, resultCapacity); - } - ((RelativeDateTimeFormatter*)reldatefmt)->formatNumeric(offset, unit, res, *status); - if (U_FAILURE(*status)) { - return 0; - } - return res.extract(result, resultCapacity, *status); -} - -U_CAPI void U_EXPORT2 -ureldatefmt_formatNumericToResult( - const URelativeDateTimeFormatter* reldatefmt, - double offset, - URelativeDateTimeUnit unit, - UFormattedRelativeDateTime* result, - UErrorCode* status) { - if (U_FAILURE(*status)) { - return; - } - auto* fmt = reinterpret_cast(reldatefmt); - auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status); - resultImpl->fImpl = fmt->formatNumericToValue(offset, unit, *status); -} - -U_CAPI int32_t U_EXPORT2 -ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt, - double offset, - URelativeDateTimeUnit unit, - UChar* result, - int32_t resultCapacity, - UErrorCode* status) -{ - if (U_FAILURE(*status)) { - return 0; - } - if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString res; - if (result != nullptr) { - // nullptr destination for pure preflighting: empty dummy string - // otherwise, alias the destination buffer (copied from udat_format) - res.setTo(result, 0, resultCapacity); - } - ((RelativeDateTimeFormatter*)reldatefmt)->format(offset, unit, res, *status); - if (U_FAILURE(*status)) { - return 0; - } - return res.extract(result, resultCapacity, *status); -} - -U_CAPI void U_EXPORT2 -ureldatefmt_formatToResult( - const URelativeDateTimeFormatter* reldatefmt, - double offset, - URelativeDateTimeUnit unit, - UFormattedRelativeDateTime* result, - UErrorCode* status) { - if (U_FAILURE(*status)) { - return; - } - auto* fmt = reinterpret_cast(reldatefmt); - auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status); - resultImpl->fImpl = fmt->formatToValue(offset, unit, *status); -} - -U_CAPI int32_t U_EXPORT2 -ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt, - const UChar * relativeDateString, - int32_t relativeDateStringLen, - const UChar * timeString, - int32_t timeStringLen, - UChar* result, - int32_t resultCapacity, - UErrorCode* status ) -{ - if (U_FAILURE(*status)) { - return 0; - } - if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0 || - (relativeDateString == nullptr ? relativeDateStringLen != 0 : relativeDateStringLen < -1) || - (timeString == nullptr ? timeStringLen != 0 : timeStringLen < -1)) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UnicodeString relDateStr((UBool)(relativeDateStringLen == -1), relativeDateString, relativeDateStringLen); - UnicodeString timeStr((UBool)(timeStringLen == -1), timeString, timeStringLen); - UnicodeString res(result, 0, resultCapacity); - ((RelativeDateTimeFormatter*)reldatefmt)->combineDateAndTime(relDateStr, timeStr, res, *status); - if (U_FAILURE(*status)) { - return 0; - } - return res.extract(result, resultCapacity, *status); -} - -#endif /* !UCONFIG_NO_FORMATTING */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* Copyright (C) 2014-2016, International Business Machines Corporation and +* others. All Rights Reserved. +****************************************************************************** +* +* File reldatefmt.cpp +****************************************************************************** +*/ + +#include "unicode/reldatefmt.h" + +#if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION + +#include +#include +#include "unicode/calendar.h" +#include "unicode/datefmt.h" +#include "unicode/dtfmtsym.h" +#include "unicode/ucasemap.h" +#include "unicode/ureldatefmt.h" +#include "unicode/udisplaycontext.h" +#include "unicode/unum.h" +#include "unicode/localpointer.h" +#include "unicode/plurrule.h" +#include "unicode/simpleformatter.h" +#include "unicode/decimfmt.h" +#include "unicode/numfmt.h" +#include "unicode/brkiter.h" +#include "unicode/simpleformatter.h" +#include "uresimp.h" +#include "unicode/ures.h" +#include "cstring.h" +#include "ucln_in.h" +#include "mutex.h" +#include "charstr.h" +#include "uassert.h" +#include "quantityformatter.h" +#include "resource.h" +#include "sharedbreakiterator.h" +#include "sharedpluralrules.h" +#include "sharednumberformat.h" +#include "standardplural.h" +#include "unifiedcache.h" +#include "util.h" +#include "formatted_string_builder.h" +#include "number_utypes.h" +#include "number_modifiers.h" +#include "formattedval_impl.h" +#include "number_utils.h" + +// Copied from uscript_props.cpp + +U_NAMESPACE_BEGIN + +// RelativeDateTimeFormatter specific data for a single locale +class RelativeDateTimeCacheData: public SharedObject { +public: + RelativeDateTimeCacheData() : combinedDateAndTime(nullptr) { + // Initialize the cache arrays + for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) { + for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) { + for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) { + relativeUnitsFormatters[style][relUnit][0][pl] = nullptr; + relativeUnitsFormatters[style][relUnit][1][pl] = nullptr; + } + } + } + for (int32_t i = 0; i < UDAT_STYLE_COUNT; ++i) { + fallBackCache[i] = -1; + } + } + virtual ~RelativeDateTimeCacheData(); + + // no numbers: e.g Next Tuesday; Yesterday; etc. + UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT]; + + // SimpleFormatter pointers for relative unit format, + // e.g., Next Tuesday; Yesterday; etc. For third index, 0 + // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days. + SimpleFormatter *relativeUnitsFormatters[UDAT_STYLE_COUNT] + [UDAT_REL_UNIT_COUNT][2][StandardPlural::COUNT]; + + const UnicodeString& getAbsoluteUnitString(int32_t fStyle, + UDateAbsoluteUnit unit, + UDateDirection direction) const; + const SimpleFormatter* getRelativeUnitFormatter(int32_t fStyle, + UDateRelativeUnit unit, + int32_t pastFutureIndex, + int32_t pluralUnit) const; + const SimpleFormatter* getRelativeDateTimeUnitFormatter(int32_t fStyle, + URelativeDateTimeUnit unit, + int32_t pastFutureIndex, + int32_t pluralUnit) const; + + const UnicodeString emptyString; + + // Mapping from source to target styles for alias fallback. + int32_t fallBackCache[UDAT_STYLE_COUNT]; + + void adoptCombinedDateAndTime(SimpleFormatter *fmtToAdopt) { + delete combinedDateAndTime; + combinedDateAndTime = fmtToAdopt; + } + const SimpleFormatter *getCombinedDateAndTime() const { + return combinedDateAndTime; + } + +private: + SimpleFormatter *combinedDateAndTime; + RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other); + RelativeDateTimeCacheData& operator=( + const RelativeDateTimeCacheData &other); +}; + +RelativeDateTimeCacheData::~RelativeDateTimeCacheData() { + // clear out the cache arrays + for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) { + for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) { + for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) { + delete relativeUnitsFormatters[style][relUnit][0][pl]; + delete relativeUnitsFormatters[style][relUnit][1][pl]; + } + } + } + delete combinedDateAndTime; +} + + +// Use fallback cache for absolute units. +const UnicodeString& RelativeDateTimeCacheData::getAbsoluteUnitString( + int32_t fStyle, UDateAbsoluteUnit unit, UDateDirection direction) const { + int32_t style = fStyle; + do { + if (!absoluteUnits[style][unit][direction].isEmpty()) { + return absoluteUnits[style][unit][direction]; + } + style = fallBackCache[style]; + } while (style != -1); + return emptyString; +} + + const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter( + int32_t fStyle, + UDateRelativeUnit unit, + int32_t pastFutureIndex, + int32_t pluralUnit) const { + URelativeDateTimeUnit rdtunit = UDAT_REL_UNIT_COUNT; + switch (unit) { + case UDAT_RELATIVE_YEARS: rdtunit = UDAT_REL_UNIT_YEAR; break; + case UDAT_RELATIVE_MONTHS: rdtunit = UDAT_REL_UNIT_MONTH; break; + case UDAT_RELATIVE_WEEKS: rdtunit = UDAT_REL_UNIT_WEEK; break; + case UDAT_RELATIVE_DAYS: rdtunit = UDAT_REL_UNIT_DAY; break; + case UDAT_RELATIVE_HOURS: rdtunit = UDAT_REL_UNIT_HOUR; break; + case UDAT_RELATIVE_MINUTES: rdtunit = UDAT_REL_UNIT_MINUTE; break; + case UDAT_RELATIVE_SECONDS: rdtunit = UDAT_REL_UNIT_SECOND; break; + default: // a unit that the above method does not handle + return nullptr; + } + + return getRelativeDateTimeUnitFormatter(fStyle, rdtunit, pastFutureIndex, pluralUnit); + } + + // Use fallback cache for SimpleFormatter relativeUnits. + const SimpleFormatter* RelativeDateTimeCacheData::getRelativeDateTimeUnitFormatter( + int32_t fStyle, + URelativeDateTimeUnit unit, + int32_t pastFutureIndex, + int32_t pluralUnit) const { + while (true) { + int32_t style = fStyle; + do { + if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != nullptr) { + return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit]; + } + style = fallBackCache[style]; + } while (style != -1); + + if (pluralUnit == StandardPlural::OTHER) { + break; + } + pluralUnit = StandardPlural::OTHER; + } + return nullptr; // No formatter found. + } + +static UBool getStringByIndex( + const UResourceBundle *resource, + int32_t idx, + UnicodeString &result, + UErrorCode &status) { + int32_t len = 0; + const char16_t *resStr = ures_getStringByIndex( + resource, idx, &len, &status); + if (U_FAILURE(status)) { + return false; + } + result.setTo(true, resStr, len); + return true; +} + +namespace { + +/** + * Sink for enumerating all of the measurement unit display names. + * + * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root): + * Only store a value if it is still missing, that is, it has not been overridden. + */ +struct RelDateTimeFmtDataSink : public ResourceSink { + + /** + * Sink for patterns for relative dates and times. For example, + * fields/relative/... + */ + + // Generic unit enum for storing Unit info. + typedef enum RelAbsUnit { + INVALID_UNIT = -1, + SECOND, + MINUTE, + HOUR, + DAY, + WEEK, + MONTH, + QUARTER, + YEAR, + SUNDAY, + MONDAY, + TUESDAY, + WEDNESDAY, + THURSDAY, + FRIDAY, + SATURDAY + } RelAbsUnit; + + static int32_t relUnitFromGeneric(RelAbsUnit genUnit) { + // Converts the generic units to UDAT_RELATIVE version. + switch (genUnit) { + case SECOND: + return UDAT_REL_UNIT_SECOND; + case MINUTE: + return UDAT_REL_UNIT_MINUTE; + case HOUR: + return UDAT_REL_UNIT_HOUR; + case DAY: + return UDAT_REL_UNIT_DAY; + case WEEK: + return UDAT_REL_UNIT_WEEK; + case MONTH: + return UDAT_REL_UNIT_MONTH; + case QUARTER: + return UDAT_REL_UNIT_QUARTER; + case YEAR: + return UDAT_REL_UNIT_YEAR; + case SUNDAY: + return UDAT_REL_UNIT_SUNDAY; + case MONDAY: + return UDAT_REL_UNIT_MONDAY; + case TUESDAY: + return UDAT_REL_UNIT_TUESDAY; + case WEDNESDAY: + return UDAT_REL_UNIT_WEDNESDAY; + case THURSDAY: + return UDAT_REL_UNIT_THURSDAY; + case FRIDAY: + return UDAT_REL_UNIT_FRIDAY; + case SATURDAY: + return UDAT_REL_UNIT_SATURDAY; + default: + return -1; + } + } + + static int32_t absUnitFromGeneric(RelAbsUnit genUnit) { + // Converts the generic units to UDAT_RELATIVE version. + switch (genUnit) { + case DAY: + return UDAT_ABSOLUTE_DAY; + case WEEK: + return UDAT_ABSOLUTE_WEEK; + case MONTH: + return UDAT_ABSOLUTE_MONTH; + case QUARTER: + return UDAT_ABSOLUTE_QUARTER; + case YEAR: + return UDAT_ABSOLUTE_YEAR; + case SUNDAY: + return UDAT_ABSOLUTE_SUNDAY; + case MONDAY: + return UDAT_ABSOLUTE_MONDAY; + case TUESDAY: + return UDAT_ABSOLUTE_TUESDAY; + case WEDNESDAY: + return UDAT_ABSOLUTE_WEDNESDAY; + case THURSDAY: + return UDAT_ABSOLUTE_THURSDAY; + case FRIDAY: + return UDAT_ABSOLUTE_FRIDAY; + case SATURDAY: + return UDAT_ABSOLUTE_SATURDAY; + case HOUR: + return UDAT_ABSOLUTE_HOUR; + case MINUTE: + return UDAT_ABSOLUTE_MINUTE; + default: + return -1; + } + } + + static int32_t keyToDirection(const char* key) { + if (uprv_strcmp(key, "-2") == 0) { + return UDAT_DIRECTION_LAST_2; + } + if (uprv_strcmp(key, "-1") == 0) { + return UDAT_DIRECTION_LAST; + } + if (uprv_strcmp(key, "0") == 0) { + return UDAT_DIRECTION_THIS; + } + if (uprv_strcmp(key, "1") == 0) { + return UDAT_DIRECTION_NEXT; + } + if (uprv_strcmp(key, "2") == 0) { + return UDAT_DIRECTION_NEXT_2; + } + return -1; + } + + // Values kept between levels of parsing the CLDR data. + int32_t pastFutureIndex; // 0 == past or 1 == future + UDateRelativeDateTimeFormatterStyle style; // {LONG, SHORT, NARROW} + RelAbsUnit genericUnit; + + RelativeDateTimeCacheData &outputData; + + // Constructor + RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData) + : outputData(cacheData) { + // Clear cacheData.fallBackCache + cacheData.fallBackCache[UDAT_STYLE_LONG] = -1; + cacheData.fallBackCache[UDAT_STYLE_SHORT] = -1; + cacheData.fallBackCache[UDAT_STYLE_NARROW] = -1; + } + + ~RelDateTimeFmtDataSink(); + + // Utility functions + static UDateRelativeDateTimeFormatterStyle styleFromString(const char *s) { + int32_t len = static_cast(uprv_strlen(s)); + if (len >= 7 && uprv_strcmp(s + len - 7, "-narrow") == 0) { + return UDAT_STYLE_NARROW; + } + if (len >= 6 && uprv_strcmp(s + len - 6, "-short") == 0) { + return UDAT_STYLE_SHORT; + } + return UDAT_STYLE_LONG; + } + + static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style) { + switch (style) { + case UDAT_STYLE_NARROW: + return 7; + case UDAT_STYLE_SHORT: + return 6; + default: + return 0; + } + } + + // Utility functions + static UDateRelativeDateTimeFormatterStyle styleFromAliasUnicodeString(UnicodeString s) { + static const char16_t narrow[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077}; + static const char16_t sshort[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,}; + if (s.endsWith(narrow, 7)) { + return UDAT_STYLE_NARROW; + } + if (s.endsWith(sshort, 6)) { + return UDAT_STYLE_SHORT; + } + return UDAT_STYLE_LONG; + } + + static RelAbsUnit unitOrNegativeFromString(const char* keyword, int32_t length) { + // Quick check from string to enum. + switch (length) { + case 3: + if (uprv_strncmp(keyword, "day", length) == 0) { + return DAY; + } else if (uprv_strncmp(keyword, "sun", length) == 0) { + return SUNDAY; + } else if (uprv_strncmp(keyword, "mon", length) == 0) { + return MONDAY; + } else if (uprv_strncmp(keyword, "tue", length) == 0) { + return TUESDAY; + } else if (uprv_strncmp(keyword, "wed", length) == 0) { + return WEDNESDAY; + } else if (uprv_strncmp(keyword, "thu", length) == 0) { + return THURSDAY; + } else if (uprv_strncmp(keyword, "fri", length) == 0) { + return FRIDAY; + } else if (uprv_strncmp(keyword, "sat", length) == 0) { + return SATURDAY; + } + break; + case 4: + if (uprv_strncmp(keyword, "hour", length) == 0) { + return HOUR; + } else if (uprv_strncmp(keyword, "week", length) == 0) { + return WEEK; + } else if (uprv_strncmp(keyword, "year", length) == 0) { + return YEAR; + } + break; + case 5: + if (uprv_strncmp(keyword, "month", length) == 0) { + return MONTH; + } + break; + case 6: + if (uprv_strncmp(keyword, "minute", length) == 0) { + return MINUTE; + } else if (uprv_strncmp(keyword, "second", length) == 0) { + return SECOND; + } + break; + case 7: + if (uprv_strncmp(keyword, "quarter", length) == 0) { + return QUARTER; // TODO: Check @provisional + } + break; + default: + break; + } + return INVALID_UNIT; + } + + void handlePlainDirection(ResourceValue &value, UErrorCode &errorCode) { + // Handle Display Name for PLAIN direction for some units. + if (U_FAILURE(errorCode)) { return; } + + int32_t absUnit = absUnitFromGeneric(genericUnit); + if (absUnit < 0) { + return; // Not interesting. + } + + // Store displayname if not set. + if (outputData.absoluteUnits[style] + [absUnit][UDAT_DIRECTION_PLAIN].isEmpty()) { + outputData.absoluteUnits[style] + [absUnit][UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode)); + return; + } + } + + void consumeTableRelative(const char *key, ResourceValue &value, UErrorCode &errorCode) { + ResourceTable unitTypesTable = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + + for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) { + if (value.getType() == URES_STRING) { + int32_t direction = keyToDirection(key); + if (direction < 0) { + continue; + } + + int32_t relUnitIndex = relUnitFromGeneric(genericUnit); + if (relUnitIndex == UDAT_REL_UNIT_SECOND && uprv_strcmp(key, "0") == 0 && + outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN].isEmpty()) { + // Handle "NOW" + outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW] + [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode)); + } + + int32_t absUnitIndex = absUnitFromGeneric(genericUnit); + if (absUnitIndex < 0) { + continue; + } + // Only reset if slot is empty. + if (outputData.absoluteUnits[style][absUnitIndex][direction].isEmpty()) { + outputData.absoluteUnits[style][absUnitIndex] + [direction].fastCopyFrom(value.getUnicodeString(errorCode)); + } + } + } + } + + void consumeTimeDetail(int32_t relUnitIndex, + const char *key, ResourceValue &value, UErrorCode &errorCode) { + ResourceTable unitTypesTable = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + + for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) { + if (value.getType() == URES_STRING) { + int32_t pluralIndex = StandardPlural::indexOrNegativeFromString(key); + if (pluralIndex >= 0) { + SimpleFormatter **patterns = + outputData.relativeUnitsFormatters[style][relUnitIndex] + [pastFutureIndex]; + // Only set if not already established. + if (patterns[pluralIndex] == nullptr) { + patterns[pluralIndex] = new SimpleFormatter( + value.getUnicodeString(errorCode), 0, 1, errorCode); + if (patterns[pluralIndex] == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + } + } + } + } + } + } + + void consumeTableRelativeTime(const char *key, ResourceValue &value, UErrorCode &errorCode) { + ResourceTable relativeTimeTable = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + + int32_t relUnitIndex = relUnitFromGeneric(genericUnit); + if (relUnitIndex < 0) { + return; + } + for (int32_t i = 0; relativeTimeTable.getKeyAndValue(i, key, value); ++i) { + if (uprv_strcmp(key, "past") == 0) { + pastFutureIndex = 0; + } else if (uprv_strcmp(key, "future") == 0) { + pastFutureIndex = 1; + } else { + // Unknown key. + continue; + } + consumeTimeDetail(relUnitIndex, key, value, errorCode); + } + } + + void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) { + + UDateRelativeDateTimeFormatterStyle sourceStyle = styleFromString(key); + const UnicodeString valueStr = value.getAliasUnicodeString(errorCode); + if (U_FAILURE(errorCode)) { return; } + + UDateRelativeDateTimeFormatterStyle targetStyle = + styleFromAliasUnicodeString(valueStr); + + if (sourceStyle == targetStyle) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + if (outputData.fallBackCache[sourceStyle] != -1 && + outputData.fallBackCache[sourceStyle] != targetStyle) { + errorCode = U_INVALID_FORMAT_ERROR; + return; + } + outputData.fallBackCache[sourceStyle] = targetStyle; + } + + void consumeTimeUnit(const char *key, ResourceValue &value, UErrorCode &errorCode) { + ResourceTable unitTypesTable = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + + for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) { + // Handle display name. + if (uprv_strcmp(key, "dn") == 0 && value.getType() == URES_STRING) { + handlePlainDirection(value, errorCode); + } + if (value.getType() == URES_TABLE) { + if (uprv_strcmp(key, "relative") == 0) { + consumeTableRelative(key, value, errorCode); + } else if (uprv_strcmp(key, "relativeTime") == 0) { + consumeTableRelativeTime(key, value, errorCode); + } + } + } + } + + virtual void put(const char *key, ResourceValue &value, + UBool /*noFallback*/, UErrorCode &errorCode) override { + // Main entry point to sink + ResourceTable table = value.getTable(errorCode); + if (U_FAILURE(errorCode)) { return; } + for (int32_t i = 0; table.getKeyAndValue(i, key, value); ++i) { + if (value.getType() == URES_ALIAS) { + consumeAlias(key, value, errorCode); + } else { + style = styleFromString(key); + int32_t unitSize = static_cast(uprv_strlen(key)) - styleSuffixLength(style); + genericUnit = unitOrNegativeFromString(key, unitSize); + if (style >= 0 && genericUnit != INVALID_UNIT) { + consumeTimeUnit(key, value, errorCode); + } + } + } + } + +}; + +// Virtual destructors must be defined out of line. +RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {} +} // namespace + +static const DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = { + DateFormatSymbols::WIDE, DateFormatSymbols::SHORT, DateFormatSymbols::NARROW +}; + +// Get days of weeks from the DateFormatSymbols class. +static void loadWeekdayNames(UnicodeString absoluteUnits[UDAT_STYLE_COUNT] + [UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT], + const char* localeId, + UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + Locale locale(localeId); + DateFormatSymbols dfSym(locale, status); + if (U_FAILURE(status)) { + return; + } + for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) { + DateFormatSymbols::DtWidthType dtfmtWidth = styleToDateFormatSymbolWidth[style]; + int32_t count; + const UnicodeString* weekdayNames = + dfSym.getWeekdays(count, DateFormatSymbols::STANDALONE, dtfmtWidth); + for (int32_t dayIndex = UDAT_ABSOLUTE_SUNDAY; + dayIndex <= UDAT_ABSOLUTE_SATURDAY; ++ dayIndex) { + int32_t dateSymbolIndex = (dayIndex - UDAT_ABSOLUTE_SUNDAY) + UCAL_SUNDAY; + absoluteUnits[style][dayIndex][UDAT_DIRECTION_PLAIN].fastCopyFrom( + weekdayNames[dateSymbolIndex]); + } + } +} + +static UBool loadUnitData( + const UResourceBundle *resource, + RelativeDateTimeCacheData &cacheData, + const char* localeId, + UErrorCode &status) { + + RelDateTimeFmtDataSink sink(cacheData); + + ures_getAllItemsWithFallback(resource, "fields", sink, status); + if (U_FAILURE(status)) { + return false; + } + + // Get the weekday names from DateFormatSymbols. + loadWeekdayNames(cacheData.absoluteUnits, localeId, status); + return U_SUCCESS(status); +} + +static const int32_t cTypeBufMax = 32; + +static UBool getDateTimePattern( + Locale locale, + const UResourceBundle *resource, + UnicodeString &result, + UErrorCode &status) { + if (U_FAILURE(status)) { + return false; + } + char cType[cTypeBufMax + 1]; + Calendar::getCalendarTypeFromLocale(locale, cType, cTypeBufMax, status); + cType[cTypeBufMax] = 0; + if (U_FAILURE(status) || cType[0] == 0) { + status = U_ZERO_ERROR; + uprv_strcpy(cType, "gregorian"); + } + + LocalUResourceBundlePointer topLevel; + int32_t dateTimeFormatOffset = DateFormat::kMedium; + CharString pathBuffer; + // Currently, for compatibility with pre-CLDR-42 data, we default to the "atTime" + // combining patterns. Depending on guidance in CLDR 42 spec and on DisplayOptions, + // we may change this. + pathBuffer.append("calendar/", status) + .append(cType, status) + .append("/DateTimePatterns%atTime", status); + topLevel.adoptInstead( + ures_getByKeyWithFallback( + resource, pathBuffer.data(), nullptr, &status)); + if (U_FAILURE(status) || ures_getSize(topLevel.getAlias()) < 4) { + // Fall back to standard combining patterns + status = U_ZERO_ERROR; + dateTimeFormatOffset = DateFormat::kDateTime; + pathBuffer.clear(); + pathBuffer.append("calendar/", status) + .append(cType, status) + .append("/DateTimePatterns", status); + topLevel.adoptInstead( + ures_getByKeyWithFallback( + resource, pathBuffer.data(), nullptr, &status)); + } + if (U_FAILURE(status)) { + return false; + } + if (dateTimeFormatOffset == DateFormat::kDateTime && ures_getSize(topLevel.getAlias()) <= DateFormat::kDateTime) { + // Oops, size is too small to access the index that we want, fallback + // to a hard-coded value. + result = UNICODE_STRING_SIMPLE("{1} {0}"); + return true; + } + return getStringByIndex(topLevel.getAlias(), dateTimeFormatOffset, result, status); +} + +template<> +const RelativeDateTimeCacheData *LocaleCacheKey::createObject(const void * /*unused*/, UErrorCode &status) const { + const char *localeId = fLoc.getName(); + LocalUResourceBundlePointer topLevel(ures_open(nullptr, localeId, &status)); + if (U_FAILURE(status)) { + return nullptr; + } + LocalPointer result( + new RelativeDateTimeCacheData()); + if (result.isNull()) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + if (!loadUnitData( + topLevel.getAlias(), + *result, + localeId, + status)) { + return nullptr; + } + UnicodeString dateTimePattern; + if (!getDateTimePattern(fLoc, topLevel.getAlias(), dateTimePattern, status)) { + return nullptr; + } + result->adoptCombinedDateAndTime( + new SimpleFormatter(dateTimePattern, 2, 2, status)); + if (U_FAILURE(status)) { + return nullptr; + } + result->addRef(); + return result.orphan(); +} + + + +static constexpr FormattedStringBuilder::Field kRDTNumericField + = {UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_NUMERIC_FIELD}; + +static constexpr FormattedStringBuilder::Field kRDTLiteralField + = {UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_LITERAL_FIELD}; + +class FormattedRelativeDateTimeData : public FormattedValueStringBuilderImpl { +public: + FormattedRelativeDateTimeData() : FormattedValueStringBuilderImpl(kRDTNumericField) {} + virtual ~FormattedRelativeDateTimeData(); +}; + +FormattedRelativeDateTimeData::~FormattedRelativeDateTimeData() = default; + + +UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedRelativeDateTime) + + +RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) : + fCache(nullptr), + fNumberFormat(nullptr), + fPluralRules(nullptr), + fStyle(UDAT_STYLE_LONG), + fContext(UDISPCTX_CAPITALIZATION_NONE), + fOptBreakIterator(nullptr) { + init(nullptr, nullptr, status); +} + +RelativeDateTimeFormatter::RelativeDateTimeFormatter( + const Locale& locale, UErrorCode& status) : + fCache(nullptr), + fNumberFormat(nullptr), + fPluralRules(nullptr), + fStyle(UDAT_STYLE_LONG), + fContext(UDISPCTX_CAPITALIZATION_NONE), + fOptBreakIterator(nullptr), + fLocale(locale) { + init(nullptr, nullptr, status); +} + +RelativeDateTimeFormatter::RelativeDateTimeFormatter( + const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) : + fCache(nullptr), + fNumberFormat(nullptr), + fPluralRules(nullptr), + fStyle(UDAT_STYLE_LONG), + fContext(UDISPCTX_CAPITALIZATION_NONE), + fOptBreakIterator(nullptr), + fLocale(locale) { + init(nfToAdopt, nullptr, status); +} + +RelativeDateTimeFormatter::RelativeDateTimeFormatter( + const Locale& locale, + NumberFormat *nfToAdopt, + UDateRelativeDateTimeFormatterStyle styl, + UDisplayContext capitalizationContext, + UErrorCode& status) : + fCache(nullptr), + fNumberFormat(nullptr), + fPluralRules(nullptr), + fStyle(styl), + fContext(capitalizationContext), + fOptBreakIterator(nullptr), + fLocale(locale) { + if (U_FAILURE(status)) { + return; + } + if ((capitalizationContext >> 8) != UDISPCTX_TYPE_CAPITALIZATION) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) { + BreakIterator *bi = BreakIterator::createSentenceInstance(locale, status); + if (U_FAILURE(status)) { + return; + } + init(nfToAdopt, bi, status); + } else { + init(nfToAdopt, nullptr, status); + } +} + +RelativeDateTimeFormatter::RelativeDateTimeFormatter( + const RelativeDateTimeFormatter& other) + : UObject(other), + fCache(other.fCache), + fNumberFormat(other.fNumberFormat), + fPluralRules(other.fPluralRules), + fStyle(other.fStyle), + fContext(other.fContext), + fOptBreakIterator(other.fOptBreakIterator), + fLocale(other.fLocale) { + fCache->addRef(); + fNumberFormat->addRef(); + fPluralRules->addRef(); + if (fOptBreakIterator != nullptr) { + fOptBreakIterator->addRef(); + } +} + +RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=( + const RelativeDateTimeFormatter& other) { + if (this != &other) { + SharedObject::copyPtr(other.fCache, fCache); + SharedObject::copyPtr(other.fNumberFormat, fNumberFormat); + SharedObject::copyPtr(other.fPluralRules, fPluralRules); + SharedObject::copyPtr(other.fOptBreakIterator, fOptBreakIterator); + fStyle = other.fStyle; + fContext = other.fContext; + fLocale = other.fLocale; + } + return *this; +} + +RelativeDateTimeFormatter::~RelativeDateTimeFormatter() { + if (fCache != nullptr) { + fCache->removeRef(); + } + if (fNumberFormat != nullptr) { + fNumberFormat->removeRef(); + } + if (fPluralRules != nullptr) { + fPluralRules->removeRef(); + } + if (fOptBreakIterator != nullptr) { + fOptBreakIterator->removeRef(); + } +} + +const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const { + return **fNumberFormat; +} + +UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const { + return fContext; +} + +UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const { + return fStyle; +} + + +// To reduce boilerplate code, we use a helper function that forwards variadic +// arguments to the formatImpl function. + +template +UnicodeString& RelativeDateTimeFormatter::doFormat( + F callback, + UnicodeString& appendTo, + UErrorCode& status, + Args... args) const { + FormattedRelativeDateTimeData output; + (this->*callback)(std::forward(args)..., output, status); + if (U_FAILURE(status)) { + return appendTo; + } + UnicodeString result = output.getStringRef().toUnicodeString(); + return appendTo.append(adjustForContext(result)); +} + +template +FormattedRelativeDateTime RelativeDateTimeFormatter::doFormatToValue( + F callback, + UErrorCode& status, + Args... args) const { + if (!checkNoAdjustForContext(status)) { + return FormattedRelativeDateTime(status); + } + LocalPointer output( + new FormattedRelativeDateTimeData(), status); + if (U_FAILURE(status)) { + return FormattedRelativeDateTime(status); + } + (this->*callback)(std::forward(args)..., *output, status); + output->getStringRef().writeTerminator(status); + return FormattedRelativeDateTime(output.orphan()); +} + +UnicodeString& RelativeDateTimeFormatter::format( + double quantity, + UDateDirection direction, + UDateRelativeUnit unit, + UnicodeString& appendTo, + UErrorCode& status) const { + return doFormat( + &RelativeDateTimeFormatter::formatImpl, + appendTo, + status, + quantity, + direction, + unit); +} + +FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue( + double quantity, + UDateDirection direction, + UDateRelativeUnit unit, + UErrorCode& status) const { + return doFormatToValue( + &RelativeDateTimeFormatter::formatImpl, + status, + quantity, + direction, + unit); +} + +void RelativeDateTimeFormatter::formatImpl( + double quantity, + UDateDirection direction, + UDateRelativeUnit unit, + FormattedRelativeDateTimeData& output, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0; + + StandardPlural::Form pluralForm; + QuantityFormatter::formatAndSelect( + quantity, + **fNumberFormat, + **fPluralRules, + output.getStringRef(), + pluralForm, + status); + if (U_FAILURE(status)) { + return; + } + + const SimpleFormatter* formatter = + fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralForm); + if (formatter == nullptr) { + // TODO: WARN - look at quantity formatter's action with an error. + status = U_INVALID_FORMAT_ERROR; + return; + } + + number::impl::SimpleModifier modifier(*formatter, kRDTLiteralField, false); + modifier.formatAsPrefixSuffix( + output.getStringRef(), 0, output.getStringRef().length(), status); +} + +UnicodeString& RelativeDateTimeFormatter::formatNumeric( + double offset, + URelativeDateTimeUnit unit, + UnicodeString& appendTo, + UErrorCode& status) const { + return doFormat( + &RelativeDateTimeFormatter::formatNumericImpl, + appendTo, + status, + offset, + unit); +} + +FormattedRelativeDateTime RelativeDateTimeFormatter::formatNumericToValue( + double offset, + URelativeDateTimeUnit unit, + UErrorCode& status) const { + return doFormatToValue( + &RelativeDateTimeFormatter::formatNumericImpl, + status, + offset, + unit); +} + +void RelativeDateTimeFormatter::formatNumericImpl( + double offset, + URelativeDateTimeUnit unit, + FormattedRelativeDateTimeData& output, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + UDateDirection direction = UDAT_DIRECTION_NEXT; + if (std::signbit(offset)) { // needed to handle -0.0 + direction = UDAT_DIRECTION_LAST; + offset = -offset; + } + if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0; + + StandardPlural::Form pluralForm; + QuantityFormatter::formatAndSelect( + offset, + **fNumberFormat, + **fPluralRules, + output.getStringRef(), + pluralForm, + status); + if (U_FAILURE(status)) { + return; + } + + const SimpleFormatter* formatter = + fCache->getRelativeDateTimeUnitFormatter(fStyle, unit, bFuture, pluralForm); + if (formatter == nullptr) { + // TODO: WARN - look at quantity formatter's action with an error. + status = U_INVALID_FORMAT_ERROR; + return; + } + + number::impl::SimpleModifier modifier(*formatter, kRDTLiteralField, false); + modifier.formatAsPrefixSuffix( + output.getStringRef(), 0, output.getStringRef().length(), status); +} + +UnicodeString& RelativeDateTimeFormatter::format( + UDateDirection direction, + UDateAbsoluteUnit unit, + UnicodeString& appendTo, + UErrorCode& status) const { + return doFormat( + &RelativeDateTimeFormatter::formatAbsoluteImpl, + appendTo, + status, + direction, + unit); +} + +FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue( + UDateDirection direction, + UDateAbsoluteUnit unit, + UErrorCode& status) const { + return doFormatToValue( + &RelativeDateTimeFormatter::formatAbsoluteImpl, + status, + direction, + unit); +} + +void RelativeDateTimeFormatter::formatAbsoluteImpl( + UDateDirection direction, + UDateAbsoluteUnit unit, + FormattedRelativeDateTimeData& output, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + // Get string using fallback. + output.getStringRef().append( + fCache->getAbsoluteUnitString(fStyle, unit, direction), + kRDTLiteralField, + status); +} + +UnicodeString& RelativeDateTimeFormatter::format( + double offset, + URelativeDateTimeUnit unit, + UnicodeString& appendTo, + UErrorCode& status) const { + return doFormat( + &RelativeDateTimeFormatter::formatRelativeImpl, + appendTo, + status, + offset, + unit); +} + +FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue( + double offset, + URelativeDateTimeUnit unit, + UErrorCode& status) const { + return doFormatToValue( + &RelativeDateTimeFormatter::formatRelativeImpl, + status, + offset, + unit); +} + +void RelativeDateTimeFormatter::formatRelativeImpl( + double offset, + URelativeDateTimeUnit unit, + FormattedRelativeDateTimeData& output, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + // TODO: + // The full implementation of this depends on CLDR data that is not yet available, + // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data. + // In the meantime do a quick bring-up by calling the old format method; this + // leaves some holes (even for data that is currently available, such as quarter). + // When the new CLDR data is available, update the data storage accordingly, + // rewrite this to use it directly, and rewrite the old format method to call this + // new one; that is covered by https://unicode-org.atlassian.net/browse/ICU-12171. + UDateDirection direction = UDAT_DIRECTION_COUNT; + if (offset > -2.1 && offset < 2.1) { + // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST + double offsetx100 = offset * 100.0; + int32_t intoffset = (offsetx100 < 0)? (int32_t)(offsetx100-0.5) : (int32_t)(offsetx100+0.5); + switch (intoffset) { + case -200/*-2*/: direction = UDAT_DIRECTION_LAST_2; break; + case -100/*-1*/: direction = UDAT_DIRECTION_LAST; break; + case 0/* 0*/: direction = UDAT_DIRECTION_THIS; break; + case 100/* 1*/: direction = UDAT_DIRECTION_NEXT; break; + case 200/* 2*/: direction = UDAT_DIRECTION_NEXT_2; break; + default: break; + } + } + UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT; + switch (unit) { + case UDAT_REL_UNIT_YEAR: absunit = UDAT_ABSOLUTE_YEAR; break; + case UDAT_REL_UNIT_QUARTER: absunit = UDAT_ABSOLUTE_QUARTER; break; + case UDAT_REL_UNIT_MONTH: absunit = UDAT_ABSOLUTE_MONTH; break; + case UDAT_REL_UNIT_WEEK: absunit = UDAT_ABSOLUTE_WEEK; break; + case UDAT_REL_UNIT_DAY: absunit = UDAT_ABSOLUTE_DAY; break; + case UDAT_REL_UNIT_SECOND: + if (direction == UDAT_DIRECTION_THIS) { + absunit = UDAT_ABSOLUTE_NOW; + direction = UDAT_DIRECTION_PLAIN; + } + break; + case UDAT_REL_UNIT_SUNDAY: absunit = UDAT_ABSOLUTE_SUNDAY; break; + case UDAT_REL_UNIT_MONDAY: absunit = UDAT_ABSOLUTE_MONDAY; break; + case UDAT_REL_UNIT_TUESDAY: absunit = UDAT_ABSOLUTE_TUESDAY; break; + case UDAT_REL_UNIT_WEDNESDAY: absunit = UDAT_ABSOLUTE_WEDNESDAY; break; + case UDAT_REL_UNIT_THURSDAY: absunit = UDAT_ABSOLUTE_THURSDAY; break; + case UDAT_REL_UNIT_FRIDAY: absunit = UDAT_ABSOLUTE_FRIDAY; break; + case UDAT_REL_UNIT_SATURDAY: absunit = UDAT_ABSOLUTE_SATURDAY; break; + case UDAT_REL_UNIT_HOUR: absunit = UDAT_ABSOLUTE_HOUR; break; + case UDAT_REL_UNIT_MINUTE: absunit = UDAT_ABSOLUTE_MINUTE; break; + default: break; + } + if (direction != UDAT_DIRECTION_COUNT && absunit != UDAT_ABSOLUTE_UNIT_COUNT) { + formatAbsoluteImpl(direction, absunit, output, status); + if (output.getStringRef().length() != 0) { + return; + } + } + // otherwise fallback to formatNumeric + formatNumericImpl(offset, unit, output, status); +} + +UnicodeString& RelativeDateTimeFormatter::combineDateAndTime( + const UnicodeString& relativeDateString, const UnicodeString& timeString, + UnicodeString& appendTo, UErrorCode& status) const { + return fCache->getCombinedDateAndTime()->format( + timeString, relativeDateString, appendTo, status); +} + +UnicodeString& RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const { + if (fOptBreakIterator == nullptr + || str.length() == 0 || !u_islower(str.char32At(0))) { + return str; + } + + // Must guarantee that one thread at a time accesses the shared break + // iterator. + static UMutex gBrkIterMutex; + Mutex lock(&gBrkIterMutex); + str.toTitle( + fOptBreakIterator->get(), + fLocale, + U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); + return str; +} + +UBool RelativeDateTimeFormatter::checkNoAdjustForContext(UErrorCode& status) const { + // This is unsupported because it's hard to keep fields in sync with title + // casing. The code could be written and tested if there is demand. + if (fOptBreakIterator != nullptr) { + status = U_UNSUPPORTED_ERROR; + return false; + } + return true; +} + +void RelativeDateTimeFormatter::init( + NumberFormat *nfToAdopt, + BreakIterator *biToAdopt, + UErrorCode &status) { + LocalPointer nf(nfToAdopt); + LocalPointer bi(biToAdopt); + UnifiedCache::getByLocale(fLocale, fCache, status); + if (U_FAILURE(status)) { + return; + } + const SharedPluralRules *pr = PluralRules::createSharedInstance( + fLocale, UPLURAL_TYPE_CARDINAL, status); + if (U_FAILURE(status)) { + return; + } + SharedObject::copyPtr(pr, fPluralRules); + pr->removeRef(); + if (nf.isNull()) { + const SharedNumberFormat *shared = NumberFormat::createSharedInstance( + fLocale, UNUM_DECIMAL, status); + if (U_FAILURE(status)) { + return; + } + SharedObject::copyPtr(shared, fNumberFormat); + shared->removeRef(); + } else { + SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias()); + if (shared == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + nf.orphan(); + SharedObject::copyPtr(shared, fNumberFormat); + } + if (bi.isNull()) { + SharedObject::clearPtr(fOptBreakIterator); + } else { + SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias()); + if (shared == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + bi.orphan(); + SharedObject::copyPtr(shared, fOptBreakIterator); + } +} + +U_NAMESPACE_END + +// Plain C API + +U_NAMESPACE_USE + + +// Magic number: "FRDT" (FormattedRelativeDateTime) in ASCII +UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL( + FormattedRelativeDateTime, + UFormattedRelativeDateTime, + UFormattedRelativeDateTimeImpl, + UFormattedRelativeDateTimeApiHelper, + ureldatefmt, + 0x46524454) + + +U_CAPI URelativeDateTimeFormatter* U_EXPORT2 +ureldatefmt_open( const char* locale, + UNumberFormat* nfToAdopt, + UDateRelativeDateTimeFormatterStyle width, + UDisplayContext capitalizationContext, + UErrorCode* status ) +{ + if (U_FAILURE(*status)) { + return nullptr; + } + LocalPointer formatter(new RelativeDateTimeFormatter(Locale(locale), + (NumberFormat*)nfToAdopt, width, + capitalizationContext, *status), *status); + if (U_FAILURE(*status)) { + return nullptr; + } + return (URelativeDateTimeFormatter*)formatter.orphan(); +} + +U_CAPI void U_EXPORT2 +ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt) +{ + delete (RelativeDateTimeFormatter*)reldatefmt; +} + +U_CAPI int32_t U_EXPORT2 +ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt, + double offset, + URelativeDateTimeUnit unit, + char16_t* result, + int32_t resultCapacity, + UErrorCode* status) +{ + if (U_FAILURE(*status)) { + return 0; + } + if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString res; + if (result != nullptr) { + // nullptr destination for pure preflighting: empty dummy string + // otherwise, alias the destination buffer (copied from udat_format) + res.setTo(result, 0, resultCapacity); + } + ((RelativeDateTimeFormatter*)reldatefmt)->formatNumeric(offset, unit, res, *status); + if (U_FAILURE(*status)) { + return 0; + } + return res.extract(result, resultCapacity, *status); +} + +U_CAPI void U_EXPORT2 +ureldatefmt_formatNumericToResult( + const URelativeDateTimeFormatter* reldatefmt, + double offset, + URelativeDateTimeUnit unit, + UFormattedRelativeDateTime* result, + UErrorCode* status) { + if (U_FAILURE(*status)) { + return; + } + auto* fmt = reinterpret_cast(reldatefmt); + auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status); + resultImpl->fImpl = fmt->formatNumericToValue(offset, unit, *status); +} + +U_CAPI int32_t U_EXPORT2 +ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt, + double offset, + URelativeDateTimeUnit unit, + char16_t* result, + int32_t resultCapacity, + UErrorCode* status) +{ + if (U_FAILURE(*status)) { + return 0; + } + if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString res; + if (result != nullptr) { + // nullptr destination for pure preflighting: empty dummy string + // otherwise, alias the destination buffer (copied from udat_format) + res.setTo(result, 0, resultCapacity); + } + ((RelativeDateTimeFormatter*)reldatefmt)->format(offset, unit, res, *status); + if (U_FAILURE(*status)) { + return 0; + } + return res.extract(result, resultCapacity, *status); +} + +U_CAPI void U_EXPORT2 +ureldatefmt_formatToResult( + const URelativeDateTimeFormatter* reldatefmt, + double offset, + URelativeDateTimeUnit unit, + UFormattedRelativeDateTime* result, + UErrorCode* status) { + if (U_FAILURE(*status)) { + return; + } + auto* fmt = reinterpret_cast(reldatefmt); + auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status); + resultImpl->fImpl = fmt->formatToValue(offset, unit, *status); +} + +U_CAPI int32_t U_EXPORT2 +ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt, + const char16_t * relativeDateString, + int32_t relativeDateStringLen, + const char16_t * timeString, + int32_t timeStringLen, + char16_t* result, + int32_t resultCapacity, + UErrorCode* status ) +{ + if (U_FAILURE(*status)) { + return 0; + } + if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0 || + (relativeDateString == nullptr ? relativeDateStringLen != 0 : relativeDateStringLen < -1) || + (timeString == nullptr ? timeStringLen != 0 : timeStringLen < -1)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString relDateStr((UBool)(relativeDateStringLen == -1), relativeDateString, relativeDateStringLen); + UnicodeString timeStr((UBool)(timeStringLen == -1), timeString, timeStringLen); + UnicodeString res(result, 0, resultCapacity); + ((RelativeDateTimeFormatter*)reldatefmt)->combineDateAndTime(relDateStr, timeStr, res, *status); + if (U_FAILURE(*status)) { + return 0; + } + return res.extract(result, resultCapacity, *status); +} + +#endif /* !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/reldtfmt.cpp b/deps/icu-small/source/i18n/reldtfmt.cpp index f381bf8981a93b..b59183b822cfc1 100644 --- a/deps/icu-small/source/i18n/reldtfmt.cpp +++ b/deps/icu-small/source/i18n/reldtfmt.cpp @@ -1,595 +1,595 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2007-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include - -#include "unicode/datefmt.h" -#include "unicode/reldatefmt.h" -#include "unicode/simpleformatter.h" -#include "unicode/smpdtfmt.h" -#include "unicode/udisplaycontext.h" -#include "unicode/uchar.h" -#include "unicode/brkiter.h" -#include "unicode/ucasemap.h" -#include "reldtfmt.h" -#include "cmemory.h" -#include "uresimp.h" - -U_NAMESPACE_BEGIN - - -/** - * An array of URelativeString structs is used to store the resource data loaded out of the bundle. - */ -struct URelativeString { - int32_t offset; /** offset of this item, such as, the relative date **/ - int32_t len; /** length of the string **/ - const UChar* string; /** string, or NULL if not set **/ -}; - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat) - -RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) : - DateFormat(other), fDateTimeFormatter(NULL), fDatePattern(other.fDatePattern), - fTimePattern(other.fTimePattern), fCombinedFormat(NULL), - fDateStyle(other.fDateStyle), fLocale(other.fLocale), - fDatesLen(other.fDatesLen), fDates(NULL), - fCombinedHasDateAtStart(other.fCombinedHasDateAtStart), - fCapitalizationInfoSet(other.fCapitalizationInfoSet), - fCapitalizationOfRelativeUnitsForUIListMenu(other.fCapitalizationOfRelativeUnitsForUIListMenu), - fCapitalizationOfRelativeUnitsForStandAlone(other.fCapitalizationOfRelativeUnitsForStandAlone), - fCapitalizationBrkIter(NULL) -{ - if(other.fDateTimeFormatter != NULL) { - fDateTimeFormatter = other.fDateTimeFormatter->clone(); - } - if(other.fCombinedFormat != NULL) { - fCombinedFormat = new SimpleFormatter(*other.fCombinedFormat); - } - if (fDatesLen > 0) { - fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*(size_t)fDatesLen); - uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*(size_t)fDatesLen); - } -#if !UCONFIG_NO_BREAK_ITERATION - if (other.fCapitalizationBrkIter != NULL) { - fCapitalizationBrkIter = (other.fCapitalizationBrkIter)->clone(); - } -#endif -} - -RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, - const Locale& locale, UErrorCode& status) : - DateFormat(), fDateTimeFormatter(NULL), fDatePattern(), fTimePattern(), fCombinedFormat(NULL), - fDateStyle(dateStyle), fLocale(locale), fDatesLen(0), fDates(NULL), - fCombinedHasDateAtStart(false), fCapitalizationInfoSet(false), - fCapitalizationOfRelativeUnitsForUIListMenu(false), fCapitalizationOfRelativeUnitsForStandAlone(false), - fCapitalizationBrkIter(NULL) -{ - if(U_FAILURE(status) ) { - return; - } - - if (timeStyle < UDAT_NONE || timeStyle > UDAT_SHORT) { - // don't support other time styles (e.g. relative styles), for now - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - UDateFormatStyle baseDateStyle = (dateStyle > UDAT_SHORT)? (UDateFormatStyle)(dateStyle & ~UDAT_RELATIVE): dateStyle; - DateFormat * df; - // Get fDateTimeFormatter from either date or time style (does not matter, we will override the pattern). - // We do need to get separate patterns for the date & time styles. - if (baseDateStyle != UDAT_NONE) { - df = createDateInstance((EStyle)baseDateStyle, locale); - fDateTimeFormatter=dynamic_cast(df); - if (fDateTimeFormatter == NULL) { - status = U_UNSUPPORTED_ERROR; - return; - } - fDateTimeFormatter->toPattern(fDatePattern); - if (timeStyle != UDAT_NONE) { - df = createTimeInstance((EStyle)timeStyle, locale); - SimpleDateFormat *sdf = dynamic_cast(df); - if (sdf != NULL) { - sdf->toPattern(fTimePattern); - delete sdf; - } - } - } else { - // does not matter whether timeStyle is UDAT_NONE, we need something for fDateTimeFormatter - df = createTimeInstance((EStyle)timeStyle, locale); - fDateTimeFormatter=dynamic_cast(df); - if (fDateTimeFormatter == NULL) { - status = U_UNSUPPORTED_ERROR; - delete df; - return; - } - fDateTimeFormatter->toPattern(fTimePattern); - } - - // Initialize the parent fCalendar, so that parse() works correctly. - initializeCalendar(NULL, locale, status); - loadDates(status); -} - -RelativeDateFormat::~RelativeDateFormat() { - delete fDateTimeFormatter; - delete fCombinedFormat; - uprv_free(fDates); -#if !UCONFIG_NO_BREAK_ITERATION - delete fCapitalizationBrkIter; -#endif -} - - -RelativeDateFormat* RelativeDateFormat::clone() const { - return new RelativeDateFormat(*this); -} - -bool RelativeDateFormat::operator==(const Format& other) const { - if(DateFormat::operator==(other)) { - // The DateFormat::operator== check for fCapitalizationContext equality above - // is sufficient to check equality of all derived context-related data. - // DateFormat::operator== guarantees following cast is safe - RelativeDateFormat* that = (RelativeDateFormat*)&other; - return (fDateStyle==that->fDateStyle && - fDatePattern==that->fDatePattern && - fTimePattern==that->fTimePattern && - fLocale==that->fLocale ); - } - return false; -} - -static const UChar APOSTROPHE = (UChar)0x0027; - -UnicodeString& RelativeDateFormat::format( Calendar& cal, - UnicodeString& appendTo, - FieldPosition& pos) const { - - UErrorCode status = U_ZERO_ERROR; - UnicodeString relativeDayString; - UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status); - - // calculate the difference, in days, between 'cal' and now. - int dayDiff = dayDifference(cal, status); - - // look up string - int32_t len = 0; - const UChar *theString = getStringForDay(dayDiff, len, status); - if(U_SUCCESS(status) && (theString!=NULL)) { - // found a relative string - relativeDayString.setTo(theString, len); - } - - if ( relativeDayString.length() > 0 && !fDatePattern.isEmpty() && - (fTimePattern.isEmpty() || fCombinedFormat == NULL || fCombinedHasDateAtStart)) { -#if !UCONFIG_NO_BREAK_ITERATION - // capitalize relativeDayString according to context for relative, set formatter no context - if ( u_islower(relativeDayString.char32At(0)) && fCapitalizationBrkIter!= NULL && - ( capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || - (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) || - (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone) ) ) { - // titlecase first word of relativeDayString - relativeDayString.toTitle(fCapitalizationBrkIter, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); - } -#endif - fDateTimeFormatter->setContext(UDISPCTX_CAPITALIZATION_NONE, status); - } else { - // set our context for the formatter - fDateTimeFormatter->setContext(capitalizationContext, status); - } - - if (fDatePattern.isEmpty()) { - fDateTimeFormatter->applyPattern(fTimePattern); - fDateTimeFormatter->format(cal,appendTo,pos); - } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { - if (relativeDayString.length() > 0) { - appendTo.append(relativeDayString); - } else { - fDateTimeFormatter->applyPattern(fDatePattern); - fDateTimeFormatter->format(cal,appendTo,pos); - } - } else { - UnicodeString datePattern; - if (relativeDayString.length() > 0) { - // Need to quote the relativeDayString to make it a legal date pattern - relativeDayString.findAndReplace(UNICODE_STRING("'", 1), UNICODE_STRING("''", 2)); // double any existing APOSTROPHE - relativeDayString.insert(0, APOSTROPHE); // add APOSTROPHE at beginning... - relativeDayString.append(APOSTROPHE); // and at end - datePattern.setTo(relativeDayString); - } else { - datePattern.setTo(fDatePattern); - } - UnicodeString combinedPattern; - fCombinedFormat->format(fTimePattern, datePattern, combinedPattern, status); - fDateTimeFormatter->applyPattern(combinedPattern); - fDateTimeFormatter->format(cal,appendTo,pos); - } - - return appendTo; -} - - - -UnicodeString& -RelativeDateFormat::format(const Formattable& obj, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode& status) const -{ - // this is just here to get around the hiding problem - // (the previous format() override would hide the version of - // format() on DateFormat that this function correspond to, so we - // have to redefine it here) - return DateFormat::format(obj, appendTo, pos, status); -} - - -void RelativeDateFormat::parse( const UnicodeString& text, - Calendar& cal, - ParsePosition& pos) const { - - int32_t startIndex = pos.getIndex(); - if (fDatePattern.isEmpty()) { - // no date pattern, try parsing as time - fDateTimeFormatter->applyPattern(fTimePattern); - fDateTimeFormatter->parse(text,cal,pos); - } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { - // no time pattern or way to combine, try parsing as date - // first check whether text matches a relativeDayString - UBool matchedRelative = false; - for (int n=0; n < fDatesLen && !matchedRelative; n++) { - if (fDates[n].string != NULL && - text.compare(startIndex, fDates[n].len, fDates[n].string) == 0) { - // it matched, handle the relative day string - UErrorCode status = U_ZERO_ERROR; - matchedRelative = true; - - // Set the calendar to now+offset - cal.setTime(Calendar::getNow(),status); - cal.add(UCAL_DATE,fDates[n].offset, status); - - if(U_FAILURE(status)) { - // failure in setting calendar field, set offset to beginning of rel day string - pos.setErrorIndex(startIndex); - } else { - pos.setIndex(startIndex + fDates[n].len); - } - } - } - if (!matchedRelative) { - // just parse as normal date - fDateTimeFormatter->applyPattern(fDatePattern); - fDateTimeFormatter->parse(text,cal,pos); - } - } else { - // Here we replace any relativeDayString in text with the equivalent date - // formatted per fDatePattern, then parse text normally using the combined pattern. - UnicodeString modifiedText(text); - FieldPosition fPos; - int32_t dateStart = 0, origDateLen = 0, modDateLen = 0; - UErrorCode status = U_ZERO_ERROR; - for (int n=0; n < fDatesLen; n++) { - int32_t relativeStringOffset; - if (fDates[n].string != NULL && - (relativeStringOffset = modifiedText.indexOf(fDates[n].string, fDates[n].len, startIndex)) >= startIndex) { - // it matched, replace the relative date with a real one for parsing - UnicodeString dateString; - Calendar * tempCal = cal.clone(); - - // Set the calendar to now+offset - tempCal->setTime(Calendar::getNow(),status); - tempCal->add(UCAL_DATE,fDates[n].offset, status); - if(U_FAILURE(status)) { - pos.setErrorIndex(startIndex); - delete tempCal; - return; - } - - fDateTimeFormatter->applyPattern(fDatePattern); - fDateTimeFormatter->format(*tempCal, dateString, fPos); - dateStart = relativeStringOffset; - origDateLen = fDates[n].len; - modDateLen = dateString.length(); - modifiedText.replace(dateStart, origDateLen, dateString); - delete tempCal; - break; - } - } - UnicodeString combinedPattern; - fCombinedFormat->format(fTimePattern, fDatePattern, combinedPattern, status); - fDateTimeFormatter->applyPattern(combinedPattern); - fDateTimeFormatter->parse(modifiedText,cal,pos); - - // Adjust offsets - UBool noError = (pos.getErrorIndex() < 0); - int32_t offset = (noError)? pos.getIndex(): pos.getErrorIndex(); - if (offset >= dateStart + modDateLen) { - // offset at or after the end of the replaced text, - // correct by the difference between original and replacement - offset -= (modDateLen - origDateLen); - } else if (offset >= dateStart) { - // offset in the replaced text, set it to the beginning of that text - // (i.e. the beginning of the relative day string) - offset = dateStart; - } - if (noError) { - pos.setIndex(offset); - } else { - pos.setErrorIndex(offset); - } - } -} - -UDate -RelativeDateFormat::parse( const UnicodeString& text, - ParsePosition& pos) const { - // redefined here because the other parse() function hides this function's - // counterpart on DateFormat - return DateFormat::parse(text, pos); -} - -UDate -RelativeDateFormat::parse(const UnicodeString& text, UErrorCode& status) const -{ - // redefined here because the other parse() function hides this function's - // counterpart on DateFormat - return DateFormat::parse(text, status); -} - - -const UChar *RelativeDateFormat::getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const { - if(U_FAILURE(status)) { - return NULL; - } - - // Is it inside the resource bundle's range? - int n = day + UDAT_DIRECTION_THIS; - if (n >= 0 && n < fDatesLen) { - if (fDates[n].offset == day && fDates[n].string != NULL) { - len = fDates[n].len; - return fDates[n].string; - } - } - return NULL; // not found. -} - -UnicodeString& -RelativeDateFormat::toPattern(UnicodeString& result, UErrorCode& status) const -{ - if (!U_FAILURE(status)) { - result.remove(); - if (fDatePattern.isEmpty()) { - result.setTo(fTimePattern); - } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { - result.setTo(fDatePattern); - } else { - fCombinedFormat->format(fTimePattern, fDatePattern, result, status); - } - } - return result; -} - -UnicodeString& -RelativeDateFormat::toPatternDate(UnicodeString& result, UErrorCode& status) const -{ - if (!U_FAILURE(status)) { - result.remove(); - result.setTo(fDatePattern); - } - return result; -} - -UnicodeString& -RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) const -{ - if (!U_FAILURE(status)) { - result.remove(); - result.setTo(fTimePattern); - } - return result; -} - -void -RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status) -{ - if (!U_FAILURE(status)) { - fDatePattern.setTo(datePattern); - fTimePattern.setTo(timePattern); - } -} - -const DateFormatSymbols* -RelativeDateFormat::getDateFormatSymbols() const -{ - return fDateTimeFormatter->getDateFormatSymbols(); -} - -// override the DateFormat implementation in order to -// lazily initialize relevant items -void -RelativeDateFormat::setContext(UDisplayContext value, UErrorCode& status) -{ - DateFormat::setContext(value, status); - if (U_SUCCESS(status)) { - if (!fCapitalizationInfoSet && - (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE)) { - initCapitalizationContextInfo(fLocale); - fCapitalizationInfoSet = true; - } -#if !UCONFIG_NO_BREAK_ITERATION - if ( fCapitalizationBrkIter == NULL && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || - (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) || - (value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone)) ) { - status = U_ZERO_ERROR; - fCapitalizationBrkIter = BreakIterator::createSentenceInstance(fLocale, status); - if (U_FAILURE(status)) { - delete fCapitalizationBrkIter; - fCapitalizationBrkIter = NULL; - } - } -#endif - } -} - -void -RelativeDateFormat::initCapitalizationContextInfo(const Locale& thelocale) -{ -#if !UCONFIG_NO_BREAK_ITERATION - const char * localeID = (thelocale != NULL)? thelocale.getBaseName(): NULL; - UErrorCode status = U_ZERO_ERROR; - LocalUResourceBundlePointer rb(ures_open(NULL, localeID, &status)); - ures_getByKeyWithFallback(rb.getAlias(), - "contextTransforms/relative", - rb.getAlias(), &status); - if (U_SUCCESS(status) && rb != NULL) { - int32_t len = 0; - const int32_t * intVector = ures_getIntVector(rb.getAlias(), - &len, &status); - if (U_SUCCESS(status) && intVector != NULL && len >= 2) { - fCapitalizationOfRelativeUnitsForUIListMenu = static_cast(intVector[0]); - fCapitalizationOfRelativeUnitsForStandAlone = static_cast(intVector[1]); - } - } -#endif -} - -namespace { - -/** - * Sink for getting data from fields/day/relative data. - * For loading relative day names, e.g., "yesterday", "today". - */ - -struct RelDateFmtDataSink : public ResourceSink { - URelativeString *fDatesPtr; - int32_t fDatesLen; - - RelDateFmtDataSink(URelativeString* fDates, int32_t len) : fDatesPtr(fDates), fDatesLen(len) { - for (int32_t i = 0; i < fDatesLen; ++i) { - fDatesPtr[i].offset = 0; - fDatesPtr[i].string = NULL; - fDatesPtr[i].len = -1; - } - } - - virtual ~RelDateFmtDataSink(); - - virtual void put(const char *key, ResourceValue &value, - UBool /*noFallback*/, UErrorCode &errorCode) override { - ResourceTable relDayTable = value.getTable(errorCode); - int32_t n = 0; - int32_t len = 0; - for (int32_t i = 0; relDayTable.getKeyAndValue(i, key, value); ++i) { - // Find the relative offset. - int32_t offset = atoi(key); - - // Put in the proper spot, but don't override existing data. - n = offset + UDAT_DIRECTION_THIS; // Converts to index in UDAT_R - if (n < fDatesLen && fDatesPtr[n].string == NULL) { - // Not found and n is an empty slot. - fDatesPtr[n].offset = offset; - fDatesPtr[n].string = value.getString(len, errorCode); - fDatesPtr[n].len = len; - } - } - } -}; - - -// Virtual destructors must be defined out of line. -RelDateFmtDataSink::~RelDateFmtDataSink() {} - -} // Namespace - - -static const UChar patItem1[] = {0x7B,0x31,0x7D}; // "{1}" -static const int32_t patItem1Len = 3; - -void RelativeDateFormat::loadDates(UErrorCode &status) { - UResourceBundle *rb = ures_open(NULL, fLocale.getBaseName(), &status); - LocalUResourceBundlePointer dateTimePatterns( - ures_getByKeyWithFallback(rb, - "calendar/gregorian/DateTimePatterns", - (UResourceBundle*)NULL, &status)); - if(U_SUCCESS(status)) { - int32_t patternsSize = ures_getSize(dateTimePatterns.getAlias()); - if (patternsSize > kDateTime) { - int32_t resStrLen = 0; - int32_t glueIndex = kDateTime; - if (patternsSize >= (kDateTimeOffset + kShort + 1)) { - int32_t offsetIncrement = (fDateStyle & ~kRelative); // Remove relative bit. - if (offsetIncrement >= (int32_t)kFull && - offsetIncrement <= (int32_t)kShortRelative) { - glueIndex = kDateTimeOffset + offsetIncrement; - } - } - - const UChar *resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), glueIndex, &resStrLen, &status); - if (U_SUCCESS(status) && resStrLen >= patItem1Len && u_strncmp(resStr,patItem1,patItem1Len)==0) { - fCombinedHasDateAtStart = true; - } - fCombinedFormat = new SimpleFormatter(UnicodeString(true, resStr, resStrLen), 2, 2, status); - } - } - - // Data loading for relative names, e.g., "yesterday", "today", "tomorrow". - fDatesLen = UDAT_DIRECTION_COUNT; // Maximum defined by data. - fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); - - RelDateFmtDataSink sink(fDates, fDatesLen); - ures_getAllItemsWithFallback(rb, "fields/day/relative", sink, status); - - ures_close(rb); - - if(U_FAILURE(status)) { - fDatesLen=0; - return; - } -} - -//---------------------------------------------------------------------- - -// this should to be in DateFormat, instead it was copied from SimpleDateFormat. - -Calendar* -RelativeDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status) -{ - if(!U_FAILURE(status)) { - fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status); - } - if (U_SUCCESS(status) && fCalendar == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return fCalendar; -} - -int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) { - if(U_FAILURE(status)) { - return 0; - } - // TODO: Cache the nowCal to avoid heap allocs? Would be difficult, don't know the calendar type - Calendar *nowCal = cal.clone(); - nowCal->setTime(Calendar::getNow(), status); - - // For the day difference, we are interested in the difference in the (modified) julian day number - // which is midnight to midnight. Using fieldDifference() is NOT correct here, because - // 6pm Jan 4th to 10am Jan 5th should be considered "tomorrow". - int32_t dayDiff = cal.get(UCAL_JULIAN_DAY, status) - nowCal->get(UCAL_JULIAN_DAY, status); - - delete nowCal; - return dayDiff; -} - -U_NAMESPACE_END - -#endif /* !UCONFIG_NO_FORMATTING */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2007-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include + +#include "unicode/datefmt.h" +#include "unicode/reldatefmt.h" +#include "unicode/simpleformatter.h" +#include "unicode/smpdtfmt.h" +#include "unicode/udisplaycontext.h" +#include "unicode/uchar.h" +#include "unicode/brkiter.h" +#include "unicode/ucasemap.h" +#include "reldtfmt.h" +#include "cmemory.h" +#include "uresimp.h" + +U_NAMESPACE_BEGIN + + +/** + * An array of URelativeString structs is used to store the resource data loaded out of the bundle. + */ +struct URelativeString { + int32_t offset; /** offset of this item, such as, the relative date **/ + int32_t len; /** length of the string **/ + const char16_t* string; /** string, or nullptr if not set **/ +}; + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat) + +RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) : + DateFormat(other), fDateTimeFormatter(nullptr), fDatePattern(other.fDatePattern), + fTimePattern(other.fTimePattern), fCombinedFormat(nullptr), + fDateStyle(other.fDateStyle), fLocale(other.fLocale), + fDatesLen(other.fDatesLen), fDates(nullptr), + fCombinedHasDateAtStart(other.fCombinedHasDateAtStart), + fCapitalizationInfoSet(other.fCapitalizationInfoSet), + fCapitalizationOfRelativeUnitsForUIListMenu(other.fCapitalizationOfRelativeUnitsForUIListMenu), + fCapitalizationOfRelativeUnitsForStandAlone(other.fCapitalizationOfRelativeUnitsForStandAlone), + fCapitalizationBrkIter(nullptr) +{ + if(other.fDateTimeFormatter != nullptr) { + fDateTimeFormatter = other.fDateTimeFormatter->clone(); + } + if(other.fCombinedFormat != nullptr) { + fCombinedFormat = new SimpleFormatter(*other.fCombinedFormat); + } + if (fDatesLen > 0) { + fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*(size_t)fDatesLen); + uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*(size_t)fDatesLen); + } +#if !UCONFIG_NO_BREAK_ITERATION + if (other.fCapitalizationBrkIter != nullptr) { + fCapitalizationBrkIter = (other.fCapitalizationBrkIter)->clone(); + } +#endif +} + +RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, + const Locale& locale, UErrorCode& status) : + DateFormat(), fDateTimeFormatter(nullptr), fDatePattern(), fTimePattern(), fCombinedFormat(nullptr), + fDateStyle(dateStyle), fLocale(locale), fDatesLen(0), fDates(nullptr), + fCombinedHasDateAtStart(false), fCapitalizationInfoSet(false), + fCapitalizationOfRelativeUnitsForUIListMenu(false), fCapitalizationOfRelativeUnitsForStandAlone(false), + fCapitalizationBrkIter(nullptr) +{ + if(U_FAILURE(status) ) { + return; + } + + if (timeStyle < UDAT_NONE || timeStyle > UDAT_SHORT) { + // don't support other time styles (e.g. relative styles), for now + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + UDateFormatStyle baseDateStyle = (dateStyle > UDAT_SHORT)? (UDateFormatStyle)(dateStyle & ~UDAT_RELATIVE): dateStyle; + DateFormat * df; + // Get fDateTimeFormatter from either date or time style (does not matter, we will override the pattern). + // We do need to get separate patterns for the date & time styles. + if (baseDateStyle != UDAT_NONE) { + df = createDateInstance((EStyle)baseDateStyle, locale); + fDateTimeFormatter=dynamic_cast(df); + if (fDateTimeFormatter == nullptr) { + status = U_UNSUPPORTED_ERROR; + return; + } + fDateTimeFormatter->toPattern(fDatePattern); + if (timeStyle != UDAT_NONE) { + df = createTimeInstance((EStyle)timeStyle, locale); + SimpleDateFormat *sdf = dynamic_cast(df); + if (sdf != nullptr) { + sdf->toPattern(fTimePattern); + delete sdf; + } + } + } else { + // does not matter whether timeStyle is UDAT_NONE, we need something for fDateTimeFormatter + df = createTimeInstance((EStyle)timeStyle, locale); + fDateTimeFormatter=dynamic_cast(df); + if (fDateTimeFormatter == nullptr) { + status = U_UNSUPPORTED_ERROR; + delete df; + return; + } + fDateTimeFormatter->toPattern(fTimePattern); + } + + // Initialize the parent fCalendar, so that parse() works correctly. + initializeCalendar(nullptr, locale, status); + loadDates(status); +} + +RelativeDateFormat::~RelativeDateFormat() { + delete fDateTimeFormatter; + delete fCombinedFormat; + uprv_free(fDates); +#if !UCONFIG_NO_BREAK_ITERATION + delete fCapitalizationBrkIter; +#endif +} + + +RelativeDateFormat* RelativeDateFormat::clone() const { + return new RelativeDateFormat(*this); +} + +bool RelativeDateFormat::operator==(const Format& other) const { + if(DateFormat::operator==(other)) { + // The DateFormat::operator== check for fCapitalizationContext equality above + // is sufficient to check equality of all derived context-related data. + // DateFormat::operator== guarantees following cast is safe + RelativeDateFormat* that = (RelativeDateFormat*)&other; + return (fDateStyle==that->fDateStyle && + fDatePattern==that->fDatePattern && + fTimePattern==that->fTimePattern && + fLocale==that->fLocale ); + } + return false; +} + +static const char16_t APOSTROPHE = (char16_t)0x0027; + +UnicodeString& RelativeDateFormat::format( Calendar& cal, + UnicodeString& appendTo, + FieldPosition& pos) const { + + UErrorCode status = U_ZERO_ERROR; + UnicodeString relativeDayString; + UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status); + + // calculate the difference, in days, between 'cal' and now. + int dayDiff = dayDifference(cal, status); + + // look up string + int32_t len = 0; + const char16_t *theString = getStringForDay(dayDiff, len, status); + if(U_SUCCESS(status) && (theString!=nullptr)) { + // found a relative string + relativeDayString.setTo(theString, len); + } + + if ( relativeDayString.length() > 0 && !fDatePattern.isEmpty() && + (fTimePattern.isEmpty() || fCombinedFormat == nullptr || fCombinedHasDateAtStart)) { +#if !UCONFIG_NO_BREAK_ITERATION + // capitalize relativeDayString according to context for relative, set formatter no context + if ( u_islower(relativeDayString.char32At(0)) && fCapitalizationBrkIter!= nullptr && + ( capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || + (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) || + (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone) ) ) { + // titlecase first word of relativeDayString + relativeDayString.toTitle(fCapitalizationBrkIter, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); + } +#endif + fDateTimeFormatter->setContext(UDISPCTX_CAPITALIZATION_NONE, status); + } else { + // set our context for the formatter + fDateTimeFormatter->setContext(capitalizationContext, status); + } + + if (fDatePattern.isEmpty()) { + fDateTimeFormatter->applyPattern(fTimePattern); + fDateTimeFormatter->format(cal,appendTo,pos); + } else if (fTimePattern.isEmpty() || fCombinedFormat == nullptr) { + if (relativeDayString.length() > 0) { + appendTo.append(relativeDayString); + } else { + fDateTimeFormatter->applyPattern(fDatePattern); + fDateTimeFormatter->format(cal,appendTo,pos); + } + } else { + UnicodeString datePattern; + if (relativeDayString.length() > 0) { + // Need to quote the relativeDayString to make it a legal date pattern + relativeDayString.findAndReplace(UNICODE_STRING("'", 1), UNICODE_STRING("''", 2)); // double any existing APOSTROPHE + relativeDayString.insert(0, APOSTROPHE); // add APOSTROPHE at beginning... + relativeDayString.append(APOSTROPHE); // and at end + datePattern.setTo(relativeDayString); + } else { + datePattern.setTo(fDatePattern); + } + UnicodeString combinedPattern; + fCombinedFormat->format(fTimePattern, datePattern, combinedPattern, status); + fDateTimeFormatter->applyPattern(combinedPattern); + fDateTimeFormatter->format(cal,appendTo,pos); + } + + return appendTo; +} + + + +UnicodeString& +RelativeDateFormat::format(const Formattable& obj, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& status) const +{ + // this is just here to get around the hiding problem + // (the previous format() override would hide the version of + // format() on DateFormat that this function correspond to, so we + // have to redefine it here) + return DateFormat::format(obj, appendTo, pos, status); +} + + +void RelativeDateFormat::parse( const UnicodeString& text, + Calendar& cal, + ParsePosition& pos) const { + + int32_t startIndex = pos.getIndex(); + if (fDatePattern.isEmpty()) { + // no date pattern, try parsing as time + fDateTimeFormatter->applyPattern(fTimePattern); + fDateTimeFormatter->parse(text,cal,pos); + } else if (fTimePattern.isEmpty() || fCombinedFormat == nullptr) { + // no time pattern or way to combine, try parsing as date + // first check whether text matches a relativeDayString + UBool matchedRelative = false; + for (int n=0; n < fDatesLen && !matchedRelative; n++) { + if (fDates[n].string != nullptr && + text.compare(startIndex, fDates[n].len, fDates[n].string) == 0) { + // it matched, handle the relative day string + UErrorCode status = U_ZERO_ERROR; + matchedRelative = true; + + // Set the calendar to now+offset + cal.setTime(Calendar::getNow(),status); + cal.add(UCAL_DATE,fDates[n].offset, status); + + if(U_FAILURE(status)) { + // failure in setting calendar field, set offset to beginning of rel day string + pos.setErrorIndex(startIndex); + } else { + pos.setIndex(startIndex + fDates[n].len); + } + } + } + if (!matchedRelative) { + // just parse as normal date + fDateTimeFormatter->applyPattern(fDatePattern); + fDateTimeFormatter->parse(text,cal,pos); + } + } else { + // Here we replace any relativeDayString in text with the equivalent date + // formatted per fDatePattern, then parse text normally using the combined pattern. + UnicodeString modifiedText(text); + FieldPosition fPos; + int32_t dateStart = 0, origDateLen = 0, modDateLen = 0; + UErrorCode status = U_ZERO_ERROR; + for (int n=0; n < fDatesLen; n++) { + int32_t relativeStringOffset; + if (fDates[n].string != nullptr && + (relativeStringOffset = modifiedText.indexOf(fDates[n].string, fDates[n].len, startIndex)) >= startIndex) { + // it matched, replace the relative date with a real one for parsing + UnicodeString dateString; + Calendar * tempCal = cal.clone(); + + // Set the calendar to now+offset + tempCal->setTime(Calendar::getNow(),status); + tempCal->add(UCAL_DATE,fDates[n].offset, status); + if(U_FAILURE(status)) { + pos.setErrorIndex(startIndex); + delete tempCal; + return; + } + + fDateTimeFormatter->applyPattern(fDatePattern); + fDateTimeFormatter->format(*tempCal, dateString, fPos); + dateStart = relativeStringOffset; + origDateLen = fDates[n].len; + modDateLen = dateString.length(); + modifiedText.replace(dateStart, origDateLen, dateString); + delete tempCal; + break; + } + } + UnicodeString combinedPattern; + fCombinedFormat->format(fTimePattern, fDatePattern, combinedPattern, status); + fDateTimeFormatter->applyPattern(combinedPattern); + fDateTimeFormatter->parse(modifiedText,cal,pos); + + // Adjust offsets + UBool noError = (pos.getErrorIndex() < 0); + int32_t offset = (noError)? pos.getIndex(): pos.getErrorIndex(); + if (offset >= dateStart + modDateLen) { + // offset at or after the end of the replaced text, + // correct by the difference between original and replacement + offset -= (modDateLen - origDateLen); + } else if (offset >= dateStart) { + // offset in the replaced text, set it to the beginning of that text + // (i.e. the beginning of the relative day string) + offset = dateStart; + } + if (noError) { + pos.setIndex(offset); + } else { + pos.setErrorIndex(offset); + } + } +} + +UDate +RelativeDateFormat::parse( const UnicodeString& text, + ParsePosition& pos) const { + // redefined here because the other parse() function hides this function's + // counterpart on DateFormat + return DateFormat::parse(text, pos); +} + +UDate +RelativeDateFormat::parse(const UnicodeString& text, UErrorCode& status) const +{ + // redefined here because the other parse() function hides this function's + // counterpart on DateFormat + return DateFormat::parse(text, status); +} + + +const char16_t *RelativeDateFormat::getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const { + if(U_FAILURE(status)) { + return nullptr; + } + + // Is it inside the resource bundle's range? + int n = day + UDAT_DIRECTION_THIS; + if (n >= 0 && n < fDatesLen) { + if (fDates[n].offset == day && fDates[n].string != nullptr) { + len = fDates[n].len; + return fDates[n].string; + } + } + return nullptr; // not found. +} + +UnicodeString& +RelativeDateFormat::toPattern(UnicodeString& result, UErrorCode& status) const +{ + if (!U_FAILURE(status)) { + result.remove(); + if (fDatePattern.isEmpty()) { + result.setTo(fTimePattern); + } else if (fTimePattern.isEmpty() || fCombinedFormat == nullptr) { + result.setTo(fDatePattern); + } else { + fCombinedFormat->format(fTimePattern, fDatePattern, result, status); + } + } + return result; +} + +UnicodeString& +RelativeDateFormat::toPatternDate(UnicodeString& result, UErrorCode& status) const +{ + if (!U_FAILURE(status)) { + result.remove(); + result.setTo(fDatePattern); + } + return result; +} + +UnicodeString& +RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) const +{ + if (!U_FAILURE(status)) { + result.remove(); + result.setTo(fTimePattern); + } + return result; +} + +void +RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status) +{ + if (!U_FAILURE(status)) { + fDatePattern.setTo(datePattern); + fTimePattern.setTo(timePattern); + } +} + +const DateFormatSymbols* +RelativeDateFormat::getDateFormatSymbols() const +{ + return fDateTimeFormatter->getDateFormatSymbols(); +} + +// override the DateFormat implementation in order to +// lazily initialize relevant items +void +RelativeDateFormat::setContext(UDisplayContext value, UErrorCode& status) +{ + DateFormat::setContext(value, status); + if (U_SUCCESS(status)) { + if (!fCapitalizationInfoSet && + (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE)) { + initCapitalizationContextInfo(fLocale); + fCapitalizationInfoSet = true; + } +#if !UCONFIG_NO_BREAK_ITERATION + if ( fCapitalizationBrkIter == nullptr && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || + (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) || + (value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone)) ) { + status = U_ZERO_ERROR; + fCapitalizationBrkIter = BreakIterator::createSentenceInstance(fLocale, status); + if (U_FAILURE(status)) { + delete fCapitalizationBrkIter; + fCapitalizationBrkIter = nullptr; + } + } +#endif + } +} + +void +RelativeDateFormat::initCapitalizationContextInfo(const Locale& thelocale) +{ +#if !UCONFIG_NO_BREAK_ITERATION + const char * localeID = (thelocale != nullptr)? thelocale.getBaseName(): nullptr; + UErrorCode status = U_ZERO_ERROR; + LocalUResourceBundlePointer rb(ures_open(nullptr, localeID, &status)); + ures_getByKeyWithFallback(rb.getAlias(), + "contextTransforms/relative", + rb.getAlias(), &status); + if (U_SUCCESS(status) && rb != nullptr) { + int32_t len = 0; + const int32_t * intVector = ures_getIntVector(rb.getAlias(), + &len, &status); + if (U_SUCCESS(status) && intVector != nullptr && len >= 2) { + fCapitalizationOfRelativeUnitsForUIListMenu = static_cast(intVector[0]); + fCapitalizationOfRelativeUnitsForStandAlone = static_cast(intVector[1]); + } + } +#endif +} + +namespace { + +/** + * Sink for getting data from fields/day/relative data. + * For loading relative day names, e.g., "yesterday", "today". + */ + +struct RelDateFmtDataSink : public ResourceSink { + URelativeString *fDatesPtr; + int32_t fDatesLen; + + RelDateFmtDataSink(URelativeString* fDates, int32_t len) : fDatesPtr(fDates), fDatesLen(len) { + for (int32_t i = 0; i < fDatesLen; ++i) { + fDatesPtr[i].offset = 0; + fDatesPtr[i].string = nullptr; + fDatesPtr[i].len = -1; + } + } + + virtual ~RelDateFmtDataSink(); + + virtual void put(const char *key, ResourceValue &value, + UBool /*noFallback*/, UErrorCode &errorCode) override { + ResourceTable relDayTable = value.getTable(errorCode); + int32_t n = 0; + int32_t len = 0; + for (int32_t i = 0; relDayTable.getKeyAndValue(i, key, value); ++i) { + // Find the relative offset. + int32_t offset = atoi(key); + + // Put in the proper spot, but don't override existing data. + n = offset + UDAT_DIRECTION_THIS; // Converts to index in UDAT_R + if (n < fDatesLen && fDatesPtr[n].string == nullptr) { + // Not found and n is an empty slot. + fDatesPtr[n].offset = offset; + fDatesPtr[n].string = value.getString(len, errorCode); + fDatesPtr[n].len = len; + } + } + } +}; + + +// Virtual destructors must be defined out of line. +RelDateFmtDataSink::~RelDateFmtDataSink() {} + +} // Namespace + + +static const char16_t patItem1[] = {0x7B,0x31,0x7D}; // "{1}" +static const int32_t patItem1Len = 3; + +void RelativeDateFormat::loadDates(UErrorCode &status) { + UResourceBundle *rb = ures_open(nullptr, fLocale.getBaseName(), &status); + LocalUResourceBundlePointer dateTimePatterns( + ures_getByKeyWithFallback(rb, + "calendar/gregorian/DateTimePatterns", + (UResourceBundle*)nullptr, &status)); + if(U_SUCCESS(status)) { + int32_t patternsSize = ures_getSize(dateTimePatterns.getAlias()); + if (patternsSize > kDateTime) { + int32_t resStrLen = 0; + int32_t glueIndex = kDateTime; + if (patternsSize >= (kDateTimeOffset + kShort + 1)) { + int32_t offsetIncrement = (fDateStyle & ~kRelative); // Remove relative bit. + if (offsetIncrement >= (int32_t)kFull && + offsetIncrement <= (int32_t)kShortRelative) { + glueIndex = kDateTimeOffset + offsetIncrement; + } + } + + const char16_t *resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), glueIndex, &resStrLen, &status); + if (U_SUCCESS(status) && resStrLen >= patItem1Len && u_strncmp(resStr,patItem1,patItem1Len)==0) { + fCombinedHasDateAtStart = true; + } + fCombinedFormat = new SimpleFormatter(UnicodeString(true, resStr, resStrLen), 2, 2, status); + } + } + + // Data loading for relative names, e.g., "yesterday", "today", "tomorrow". + fDatesLen = UDAT_DIRECTION_COUNT; // Maximum defined by data. + fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); + + RelDateFmtDataSink sink(fDates, fDatesLen); + ures_getAllItemsWithFallback(rb, "fields/day/relative", sink, status); + + ures_close(rb); + + if(U_FAILURE(status)) { + fDatesLen=0; + return; + } +} + +//---------------------------------------------------------------------- + +// this should to be in DateFormat, instead it was copied from SimpleDateFormat. + +Calendar* +RelativeDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status) +{ + if(!U_FAILURE(status)) { + fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status); + } + if (U_SUCCESS(status) && fCalendar == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return fCalendar; +} + +int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) { + if(U_FAILURE(status)) { + return 0; + } + // TODO: Cache the nowCal to avoid heap allocs? Would be difficult, don't know the calendar type + Calendar *nowCal = cal.clone(); + nowCal->setTime(Calendar::getNow(), status); + + // For the day difference, we are interested in the difference in the (modified) julian day number + // which is midnight to midnight. Using fieldDifference() is NOT correct here, because + // 6pm Jan 4th to 10am Jan 5th should be considered "tomorrow". + int32_t dayDiff = cal.get(UCAL_JULIAN_DAY, status) - nowCal->get(UCAL_JULIAN_DAY, status); + + delete nowCal; + return dayDiff; +} + +U_NAMESPACE_END + +#endif /* !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/reldtfmt.h b/deps/icu-small/source/i18n/reldtfmt.h index 98b333a02be205..a728c912cd1f6b 100644 --- a/deps/icu-small/source/i18n/reldtfmt.h +++ b/deps/icu-small/source/i18n/reldtfmt.h @@ -1,338 +1,338 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2007-2016, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - -#ifndef RELDTFMT_H -#define RELDTFMT_H - -#include "unicode/utypes.h" - -/** - * \file - * \brief C++ API: Format and parse relative dates and times. - */ - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/datefmt.h" -#include "unicode/smpdtfmt.h" -#include "unicode/brkiter.h" - -U_NAMESPACE_BEGIN - -// forward declarations -class DateFormatSymbols; -class SimpleFormatter; - -// internal structure used for caching strings -struct URelativeString; - -/** - * This class is normally accessed using the kRelative or k...Relative values of EStyle as - * parameters to DateFormat::createDateInstance. - * - * Example: - * DateFormat *fullrelative = DateFormat::createDateInstance(DateFormat::kFullRelative, loc); - * - * @internal ICU 3.8 - */ - -class RelativeDateFormat : public DateFormat { -public: - RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const Locale& locale, UErrorCode& status); - - // overrides - /** - * Copy constructor. - * @internal ICU 3.8 - */ - RelativeDateFormat(const RelativeDateFormat&); - - /** - * Assignment operator. - * @internal ICU 3.8 - */ - RelativeDateFormat& operator=(const RelativeDateFormat&); - - /** - * Destructor. - * @internal ICU 3.8 - */ - virtual ~RelativeDateFormat(); - - /** - * Clone this Format object polymorphically. The caller owns the result and - * should delete it when done. - * @return A copy of the object. - * @internal ICU 3.8 - */ - virtual RelativeDateFormat* clone() const override; - - /** - * 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. - * @internal ICU 3.8 - */ - virtual bool operator==(const Format& other) const override; - - - using DateFormat::format; - - /** - * Format a date or time, which is the standard millis since 24:00 GMT, Jan - * 1, 1970. Overrides DateFormat pure virtual method. - *

      - * Example: using the US locale: "yyyy.MM.dd e 'at' HH:mm:ss zzz" ->> - * 1996.07.10 AD at 15:08:56 PDT - * - * @param cal Calendar set to the date and time to be formatted - * into a date/time string. - * @param appendTo Output parameter to receive result. - * Result is appended to existing contents. - * @param pos The formatting position. On input: an alignment field, - * if desired. On output: the offsets of the alignment field. - * @return Reference to 'appendTo' parameter. - * @internal ICU 3.8 - */ - virtual UnicodeString& format( Calendar& cal, - UnicodeString& appendTo, - FieldPosition& pos) const override; - - /** - * Format an object to produce a string. This method handles Formattable - * objects with a UDate type. If a the Formattable object type is not a Date, - * then it returns a failing UErrorCode. - * - * @param obj The object to format. Must be a Date. - * @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 ICU 3.8 - */ - virtual UnicodeString& format(const Formattable& obj, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode& status) const override; - - - /** - * Parse a date/time string beginning at the given parse position. For - * example, a time text "07/10/96 4:5 PM, PDT" will be parsed into a Date - * that is equivalent to Date(837039928046). - *

      - * By default, parsing is lenient: If the input is not in the form used by - * this object's format method but can still be parsed as a date, then the - * parse succeeds. Clients may insist on strict adherence to the format by - * calling setLenient(false). - * - * @param text The date/time string to be parsed - * @param cal a Calendar set to the date and time to be formatted - * into a date/time string. - * @param pos On input, the position at which to start parsing; on - * output, the position at which parsing terminated, or the - * start position if the parse failed. - * @return A valid UDate if the input could be parsed. - * @internal ICU 3.8 - */ - virtual void parse( const UnicodeString& text, - Calendar& cal, - ParsePosition& pos) const override; - - /** - * Parse a date/time string starting at the given parse position. For - * example, a time text "07/10/96 4:5 PM, PDT" will be parsed into a Date - * that is equivalent to Date(837039928046). - *

      - * By default, parsing is lenient: If the input is not in the form used by - * this object's format method but can still be parsed as a date, then the - * parse succeeds. Clients may insist on strict adherence to the format by - * calling setLenient(false). - * - * @see DateFormat::setLenient(boolean) - * - * @param text The date/time string to be parsed - * @param pos On input, the position at which to start parsing; on - * output, the position at which parsing terminated, or the - * start position if the parse failed. - * @return A valid UDate if the input could be parsed. - * @internal ICU 3.8 - */ - UDate parse( const UnicodeString& text, - ParsePosition& pos) const; - - - /** - * Parse a date/time string. For example, a time text "07/10/96 4:5 PM, PDT" - * will be parsed into a UDate that is equivalent to Date(837039928046). - * Parsing begins at the beginning of the string and proceeds as far as - * possible. Assuming no parse errors were encountered, this function - * doesn't return any information about how much of the string was consumed - * by the parsing. If you need that information, use the version of - * parse() that takes a ParsePosition. - * - * @param text The date/time string to be parsed - * @param status Filled in with U_ZERO_ERROR if the parse was successful, and with - * an error value if there was a parse error. - * @return A valid UDate if the input could be parsed. - * @internal ICU 3.8 - */ - virtual UDate parse( const UnicodeString& text, - UErrorCode& status) const override; - - /** - * Return a single pattern string generated by combining the patterns for the - * date and time formatters associated with this object. - * @param result Output param to receive the pattern. - * @return A reference to 'result'. - * @internal ICU 4.2 technology preview - */ - virtual UnicodeString& toPattern(UnicodeString& result, UErrorCode& status) const; - - /** - * Get the date pattern for the the date formatter associated with this object. - * @param result Output param to receive the date pattern. - * @return A reference to 'result'. - * @internal ICU 4.2 technology preview - */ - virtual UnicodeString& toPatternDate(UnicodeString& result, UErrorCode& status) const; - - /** - * Get the time pattern for the the time formatter associated with this object. - * @param result Output param to receive the time pattern. - * @return A reference to 'result'. - * @internal ICU 4.2 technology preview - */ - virtual UnicodeString& toPatternTime(UnicodeString& result, UErrorCode& status) const; - - /** - * Apply the given unlocalized date & time pattern strings to this relative date format. - * (i.e., after this call, this formatter will format dates and times according to - * the new patterns) - * - * @param datePattern The date pattern to be applied. - * @param timePattern The time pattern to be applied. - * @internal ICU 4.2 technology preview - */ - virtual void applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status); - - /** - * Gets the date/time formatting symbols (this is an object carrying - * the various strings and other symbols used in formatting: e.g., month - * names and abbreviations, time zone names, AM/PM strings, etc.) - * @return a copy of the date-time formatting data associated - * with this date-time formatter. - * @internal ICU 4.8 - */ - virtual const DateFormatSymbols* getDateFormatSymbols(void) const; - - /** - * Set a particular UDisplayContext value in the formatter, such as - * UDISPCTX_CAPITALIZATION_FOR_STANDALONE. Note: For getContext, see - * DateFormat. - * @param value The UDisplayContext value to set. - * @param status Input/output status. If at entry this indicates a failure - * status, the function will do nothing; otherwise this will be - * updated with any new status from the function. - * @internal ICU 53 - */ - virtual void setContext(UDisplayContext value, UErrorCode& status) override; - -private: - SimpleDateFormat *fDateTimeFormatter; - UnicodeString fDatePattern; - UnicodeString fTimePattern; - SimpleFormatter *fCombinedFormat; // the {0} {1} format. - - UDateFormatStyle fDateStyle; - Locale fLocale; - - int32_t fDatesLen; // Length of array - URelativeString *fDates; // array of strings - - UBool fCombinedHasDateAtStart; - UBool fCapitalizationInfoSet; - UBool fCapitalizationOfRelativeUnitsForUIListMenu; - UBool fCapitalizationOfRelativeUnitsForStandAlone; -#if !UCONFIG_NO_BREAK_ITERATION - BreakIterator* fCapitalizationBrkIter; -#else - UObject* fCapitalizationBrkIter; -#endif - - /** - * Get the string at a specific offset. - * @param day day offset ( -1, 0, 1, etc.. ) - * @param len on output, length of string. - * @return the string, or NULL if none at that location. - */ - const UChar *getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const; - - /** - * Load the Date string array - */ - void loadDates(UErrorCode &status); - - /** - * Set fCapitalizationOfRelativeUnitsForUIListMenu, fCapitalizationOfRelativeUnitsForStandAlone - */ - void initCapitalizationContextInfo(const Locale& thelocale); - - /** - * @return the number of days in "until-now" - */ - static int32_t dayDifference(Calendar &until, UErrorCode &status); - - /** - * initializes fCalendar from parameters. Returns fCalendar as a convenience. - * @param adoptZone Zone to be adopted, or NULL for TimeZone::createDefault(). - * @param locale Locale of the calendar - * @param status Error code - * @return the newly constructed fCalendar - * @internal ICU 3.8 - */ - Calendar* initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status); - -public: - /** - * Return the class ID for this class. This is useful only for comparing to - * a return value from getDynamicClassID(). For example: - *

      -     * .   Base* polymorphic_pointer = createPolymorphicObject();
      -     * .   if (polymorphic_pointer->getDynamicClassID() ==
      -     * .       erived::getStaticClassID()) ...
      -     * 
      - * @return The class ID for all objects of this class. - * @internal ICU 3.8 - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(void); - - /** - * Returns a unique class ID POLYMORPHICALLY. Pure virtual override. This - * method is to implement a simple version of RTTI, since not all C++ - * compilers support genuine RTTI. Polymorphic operator==() and clone() - * methods call this method. - * - * @return The class ID for this object. All objects of a - * given class have the same class ID. Objects of - * other classes have different class IDs. - * @internal ICU 3.8 - */ - virtual UClassID getDynamicClassID(void) const override; -}; - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -#endif // RELDTFMT_H +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 2007-2016, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +*/ + +#ifndef RELDTFMT_H +#define RELDTFMT_H + +#include "unicode/utypes.h" + +/** + * \file + * \brief C++ API: Format and parse relative dates and times. + */ + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/datefmt.h" +#include "unicode/smpdtfmt.h" +#include "unicode/brkiter.h" + +U_NAMESPACE_BEGIN + +// forward declarations +class DateFormatSymbols; +class SimpleFormatter; + +// internal structure used for caching strings +struct URelativeString; + +/** + * This class is normally accessed using the kRelative or k...Relative values of EStyle as + * parameters to DateFormat::createDateInstance. + * + * Example: + * DateFormat *fullrelative = DateFormat::createDateInstance(DateFormat::kFullRelative, loc); + * + * @internal ICU 3.8 + */ + +class RelativeDateFormat : public DateFormat { +public: + RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const Locale& locale, UErrorCode& status); + + // overrides + /** + * Copy constructor. + * @internal ICU 3.8 + */ + RelativeDateFormat(const RelativeDateFormat&); + + /** + * Assignment operator. + * @internal ICU 3.8 + */ + RelativeDateFormat& operator=(const RelativeDateFormat&); + + /** + * Destructor. + * @internal ICU 3.8 + */ + virtual ~RelativeDateFormat(); + + /** + * Clone this Format object polymorphically. The caller owns the result and + * should delete it when done. + * @return A copy of the object. + * @internal ICU 3.8 + */ + virtual RelativeDateFormat* clone() const override; + + /** + * 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. + * @internal ICU 3.8 + */ + virtual bool operator==(const Format& other) const override; + + + using DateFormat::format; + + /** + * Format a date or time, which is the standard millis since 24:00 GMT, Jan + * 1, 1970. Overrides DateFormat pure virtual method. + *

      + * Example: using the US locale: "yyyy.MM.dd e 'at' HH:mm:ss zzz" ->> + * 1996.07.10 AD at 15:08:56 PDT + * + * @param cal Calendar set to the date and time to be formatted + * into a date/time string. + * @param appendTo Output parameter to receive result. + * Result is appended to existing contents. + * @param pos The formatting position. On input: an alignment field, + * if desired. On output: the offsets of the alignment field. + * @return Reference to 'appendTo' parameter. + * @internal ICU 3.8 + */ + virtual UnicodeString& format( Calendar& cal, + UnicodeString& appendTo, + FieldPosition& pos) const override; + + /** + * Format an object to produce a string. This method handles Formattable + * objects with a UDate type. If a the Formattable object type is not a Date, + * then it returns a failing UErrorCode. + * + * @param obj The object to format. Must be a Date. + * @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 ICU 3.8 + */ + virtual UnicodeString& format(const Formattable& obj, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& status) const override; + + + /** + * Parse a date/time string beginning at the given parse position. For + * example, a time text "07/10/96 4:5 PM, PDT" will be parsed into a Date + * that is equivalent to Date(837039928046). + *

      + * By default, parsing is lenient: If the input is not in the form used by + * this object's format method but can still be parsed as a date, then the + * parse succeeds. Clients may insist on strict adherence to the format by + * calling setLenient(false). + * + * @param text The date/time string to be parsed + * @param cal a Calendar set to the date and time to be formatted + * into a date/time string. + * @param pos On input, the position at which to start parsing; on + * output, the position at which parsing terminated, or the + * start position if the parse failed. + * @return A valid UDate if the input could be parsed. + * @internal ICU 3.8 + */ + virtual void parse( const UnicodeString& text, + Calendar& cal, + ParsePosition& pos) const override; + + /** + * Parse a date/time string starting at the given parse position. For + * example, a time text "07/10/96 4:5 PM, PDT" will be parsed into a Date + * that is equivalent to Date(837039928046). + *

      + * By default, parsing is lenient: If the input is not in the form used by + * this object's format method but can still be parsed as a date, then the + * parse succeeds. Clients may insist on strict adherence to the format by + * calling setLenient(false). + * + * @see DateFormat::setLenient(boolean) + * + * @param text The date/time string to be parsed + * @param pos On input, the position at which to start parsing; on + * output, the position at which parsing terminated, or the + * start position if the parse failed. + * @return A valid UDate if the input could be parsed. + * @internal ICU 3.8 + */ + UDate parse( const UnicodeString& text, + ParsePosition& pos) const; + + + /** + * Parse a date/time string. For example, a time text "07/10/96 4:5 PM, PDT" + * will be parsed into a UDate that is equivalent to Date(837039928046). + * Parsing begins at the beginning of the string and proceeds as far as + * possible. Assuming no parse errors were encountered, this function + * doesn't return any information about how much of the string was consumed + * by the parsing. If you need that information, use the version of + * parse() that takes a ParsePosition. + * + * @param text The date/time string to be parsed + * @param status Filled in with U_ZERO_ERROR if the parse was successful, and with + * an error value if there was a parse error. + * @return A valid UDate if the input could be parsed. + * @internal ICU 3.8 + */ + virtual UDate parse( const UnicodeString& text, + UErrorCode& status) const override; + + /** + * Return a single pattern string generated by combining the patterns for the + * date and time formatters associated with this object. + * @param result Output param to receive the pattern. + * @return A reference to 'result'. + * @internal ICU 4.2 technology preview + */ + virtual UnicodeString& toPattern(UnicodeString& result, UErrorCode& status) const; + + /** + * Get the date pattern for the the date formatter associated with this object. + * @param result Output param to receive the date pattern. + * @return A reference to 'result'. + * @internal ICU 4.2 technology preview + */ + virtual UnicodeString& toPatternDate(UnicodeString& result, UErrorCode& status) const; + + /** + * Get the time pattern for the the time formatter associated with this object. + * @param result Output param to receive the time pattern. + * @return A reference to 'result'. + * @internal ICU 4.2 technology preview + */ + virtual UnicodeString& toPatternTime(UnicodeString& result, UErrorCode& status) const; + + /** + * Apply the given unlocalized date & time pattern strings to this relative date format. + * (i.e., after this call, this formatter will format dates and times according to + * the new patterns) + * + * @param datePattern The date pattern to be applied. + * @param timePattern The time pattern to be applied. + * @internal ICU 4.2 technology preview + */ + virtual void applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status); + + /** + * Gets the date/time formatting symbols (this is an object carrying + * the various strings and other symbols used in formatting: e.g., month + * names and abbreviations, time zone names, AM/PM strings, etc.) + * @return a copy of the date-time formatting data associated + * with this date-time formatter. + * @internal ICU 4.8 + */ + virtual const DateFormatSymbols* getDateFormatSymbols() const; + + /** + * Set a particular UDisplayContext value in the formatter, such as + * UDISPCTX_CAPITALIZATION_FOR_STANDALONE. Note: For getContext, see + * DateFormat. + * @param value The UDisplayContext value to set. + * @param status Input/output status. If at entry this indicates a failure + * status, the function will do nothing; otherwise this will be + * updated with any new status from the function. + * @internal ICU 53 + */ + virtual void setContext(UDisplayContext value, UErrorCode& status) override; + +private: + SimpleDateFormat *fDateTimeFormatter; + UnicodeString fDatePattern; + UnicodeString fTimePattern; + SimpleFormatter *fCombinedFormat; // the {0} {1} format. + + UDateFormatStyle fDateStyle; + Locale fLocale; + + int32_t fDatesLen; // Length of array + URelativeString *fDates; // array of strings + + UBool fCombinedHasDateAtStart; + UBool fCapitalizationInfoSet; + UBool fCapitalizationOfRelativeUnitsForUIListMenu; + UBool fCapitalizationOfRelativeUnitsForStandAlone; +#if !UCONFIG_NO_BREAK_ITERATION + BreakIterator* fCapitalizationBrkIter; +#else + UObject* fCapitalizationBrkIter; +#endif + + /** + * Get the string at a specific offset. + * @param day day offset ( -1, 0, 1, etc.. ) + * @param len on output, length of string. + * @return the string, or nullptr if none at that location. + */ + const char16_t *getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const; + + /** + * Load the Date string array + */ + void loadDates(UErrorCode &status); + + /** + * Set fCapitalizationOfRelativeUnitsForUIListMenu, fCapitalizationOfRelativeUnitsForStandAlone + */ + void initCapitalizationContextInfo(const Locale& thelocale); + + /** + * @return the number of days in "until-now" + */ + static int32_t dayDifference(Calendar &until, UErrorCode &status); + + /** + * initializes fCalendar from parameters. Returns fCalendar as a convenience. + * @param adoptZone Zone to be adopted, or nullptr for TimeZone::createDefault(). + * @param locale Locale of the calendar + * @param status Error code + * @return the newly constructed fCalendar + * @internal ICU 3.8 + */ + Calendar* initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status); + +public: + /** + * Return the class ID for this class. This is useful only for comparing to + * a return value from getDynamicClassID(). For example: + *

      +     * .   Base* polymorphic_pointer = createPolymorphicObject();
      +     * .   if (polymorphic_pointer->getDynamicClassID() ==
      +     * .       erived::getStaticClassID()) ...
      +     * 
      + * @return The class ID for all objects of this class. + * @internal ICU 3.8 + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + + /** + * Returns a unique class ID POLYMORPHICALLY. Pure virtual override. This + * method is to implement a simple version of RTTI, since not all C++ + * compilers support genuine RTTI. Polymorphic operator==() and clone() + * methods call this method. + * + * @return The class ID for this object. All objects of a + * given class have the same class ID. Objects of + * other classes have different class IDs. + * @internal ICU 3.8 + */ + virtual UClassID getDynamicClassID() const override; +}; + + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif // RELDTFMT_H diff --git a/deps/icu-small/source/i18n/rematch.cpp b/deps/icu-small/source/i18n/rematch.cpp index e74ca3a659f4fd..0908bc67b3dba3 100644 --- a/deps/icu-small/source/i18n/rematch.cpp +++ b/deps/icu-small/source/i18n/rematch.cpp @@ -1,5733 +1,5733 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -************************************************************************** -* Copyright (C) 2002-2016 International Business Machines Corporation -* and others. All rights reserved. -************************************************************************** -*/ -// -// file: rematch.cpp -// -// Contains the implementation of class RegexMatcher, -// which is one of the main API classes for the ICU regular expression package. -// - -#include "unicode/utypes.h" -#if !UCONFIG_NO_REGULAR_EXPRESSIONS - -#include "unicode/regex.h" -#include "unicode/uniset.h" -#include "unicode/uchar.h" -#include "unicode/ustring.h" -#include "unicode/rbbi.h" -#include "unicode/utf.h" -#include "unicode/utf16.h" -#include "uassert.h" -#include "cmemory.h" -#include "cstr.h" -#include "uvector.h" -#include "uvectr32.h" -#include "uvectr64.h" -#include "regeximp.h" -#include "regexst.h" -#include "regextxt.h" -#include "ucase.h" - -// #include // Needed for heapcheck testing - - -U_NAMESPACE_BEGIN - -// Default limit for the size of the back track stack, to avoid system -// failures causedby heap exhaustion. Units are in 32 bit words, not bytes. -// This value puts ICU's limits higher than most other regexp implementations, -// which use recursion rather than the heap, and take more storage per -// backtrack point. -// -static const int32_t DEFAULT_BACKTRACK_STACK_CAPACITY = 8000000; - -// Time limit counter constant. -// Time limits for expression evaluation are in terms of quanta of work by -// the engine, each of which is 10,000 state saves. -// This constant determines that state saves per tick number. -static const int32_t TIMER_INITIAL_VALUE = 10000; - - -// Test for any of the Unicode line terminating characters. -static inline UBool isLineTerminator(UChar32 c) { - if (c & ~(0x0a | 0x0b | 0x0c | 0x0d | 0x85 | 0x2028 | 0x2029)) { - return false; - } - return (c<=0x0d && c>=0x0a) || c==0x85 || c==0x2028 || c==0x2029; -} - -//----------------------------------------------------------------------------- -// -// Constructor and Destructor -// -//----------------------------------------------------------------------------- -RegexMatcher::RegexMatcher(const RegexPattern *pat) { - fDeferredStatus = U_ZERO_ERROR; - init(fDeferredStatus); - if (U_FAILURE(fDeferredStatus)) { - return; - } - if (pat==NULL) { - fDeferredStatus = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - fPattern = pat; - init2(RegexStaticSets::gStaticSets->fEmptyText, fDeferredStatus); -} - - - -RegexMatcher::RegexMatcher(const UnicodeString ®exp, const UnicodeString &input, - uint32_t flags, UErrorCode &status) { - init(status); - if (U_FAILURE(status)) { - return; - } - UParseError pe; - fPatternOwned = RegexPattern::compile(regexp, flags, pe, status); - fPattern = fPatternOwned; - - UText inputText = UTEXT_INITIALIZER; - utext_openConstUnicodeString(&inputText, &input, &status); - init2(&inputText, status); - utext_close(&inputText); - - fInputUniStrMaybeMutable = true; -} - - -RegexMatcher::RegexMatcher(UText *regexp, UText *input, - uint32_t flags, UErrorCode &status) { - init(status); - if (U_FAILURE(status)) { - return; - } - UParseError pe; - fPatternOwned = RegexPattern::compile(regexp, flags, pe, status); - if (U_FAILURE(status)) { - return; - } - - fPattern = fPatternOwned; - init2(input, status); -} - - -RegexMatcher::RegexMatcher(const UnicodeString ®exp, - uint32_t flags, UErrorCode &status) { - init(status); - if (U_FAILURE(status)) { - return; - } - UParseError pe; - fPatternOwned = RegexPattern::compile(regexp, flags, pe, status); - if (U_FAILURE(status)) { - return; - } - fPattern = fPatternOwned; - init2(RegexStaticSets::gStaticSets->fEmptyText, status); -} - -RegexMatcher::RegexMatcher(UText *regexp, - uint32_t flags, UErrorCode &status) { - init(status); - if (U_FAILURE(status)) { - return; - } - UParseError pe; - fPatternOwned = RegexPattern::compile(regexp, flags, pe, status); - if (U_FAILURE(status)) { - return; - } - - fPattern = fPatternOwned; - init2(RegexStaticSets::gStaticSets->fEmptyText, status); -} - - - - -RegexMatcher::~RegexMatcher() { - delete fStack; - if (fData != fSmallData) { - uprv_free(fData); - fData = NULL; - } - if (fPatternOwned) { - delete fPatternOwned; - fPatternOwned = NULL; - fPattern = NULL; - } - - if (fInput) { - delete fInput; - } - if (fInputText) { - utext_close(fInputText); - } - if (fAltInputText) { - utext_close(fAltInputText); - } - - #if UCONFIG_NO_BREAK_ITERATION==0 - delete fWordBreakItr; - delete fGCBreakItr; - #endif -} - -// -// init() common initialization for use by all constructors. -// Initialize all fields, get the object into a consistent state. -// This must be done even when the initial status shows an error, -// so that the object is initialized sufficiently well for the destructor -// to run safely. -// -void RegexMatcher::init(UErrorCode &status) { - fPattern = NULL; - fPatternOwned = NULL; - fFrameSize = 0; - fRegionStart = 0; - fRegionLimit = 0; - fAnchorStart = 0; - fAnchorLimit = 0; - fLookStart = 0; - fLookLimit = 0; - fActiveStart = 0; - fActiveLimit = 0; - fTransparentBounds = false; - fAnchoringBounds = true; - fMatch = false; - fMatchStart = 0; - fMatchEnd = 0; - fLastMatchEnd = -1; - fAppendPosition = 0; - fHitEnd = false; - fRequireEnd = false; - fStack = NULL; - fFrame = NULL; - fTimeLimit = 0; - fTime = 0; - fTickCounter = 0; - fStackLimit = DEFAULT_BACKTRACK_STACK_CAPACITY; - fCallbackFn = NULL; - fCallbackContext = NULL; - fFindProgressCallbackFn = NULL; - fFindProgressCallbackContext = NULL; - fTraceDebug = false; - fDeferredStatus = status; - fData = fSmallData; - fWordBreakItr = NULL; - fGCBreakItr = NULL; - - fStack = NULL; - fInputText = NULL; - fAltInputText = NULL; - fInput = NULL; - fInputLength = 0; - fInputUniStrMaybeMutable = false; -} - -// -// init2() Common initialization for use by RegexMatcher constructors, part 2. -// This handles the common setup to be done after the Pattern is available. -// -void RegexMatcher::init2(UText *input, UErrorCode &status) { - if (U_FAILURE(status)) { - fDeferredStatus = status; - return; - } - - if (fPattern->fDataSize > UPRV_LENGTHOF(fSmallData)) { - fData = (int64_t *)uprv_malloc(fPattern->fDataSize * sizeof(int64_t)); - if (fData == NULL) { - status = fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - - fStack = new UVector64(status); - if (fStack == NULL) { - status = fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - - reset(input); - setStackLimit(DEFAULT_BACKTRACK_STACK_CAPACITY, status); - if (U_FAILURE(status)) { - fDeferredStatus = status; - return; - } -} - - -static const UChar BACKSLASH = 0x5c; -static const UChar DOLLARSIGN = 0x24; -static const UChar LEFTBRACKET = 0x7b; -static const UChar RIGHTBRACKET = 0x7d; - -//-------------------------------------------------------------------------------- -// -// appendReplacement -// -//-------------------------------------------------------------------------------- -RegexMatcher &RegexMatcher::appendReplacement(UnicodeString &dest, - const UnicodeString &replacement, - UErrorCode &status) { - UText replacementText = UTEXT_INITIALIZER; - - utext_openConstUnicodeString(&replacementText, &replacement, &status); - if (U_SUCCESS(status)) { - UText resultText = UTEXT_INITIALIZER; - utext_openUnicodeString(&resultText, &dest, &status); - - if (U_SUCCESS(status)) { - appendReplacement(&resultText, &replacementText, status); - utext_close(&resultText); - } - utext_close(&replacementText); - } - - return *this; -} - -// -// appendReplacement, UText mode -// -RegexMatcher &RegexMatcher::appendReplacement(UText *dest, - UText *replacement, - UErrorCode &status) { - if (U_FAILURE(status)) { - return *this; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return *this; - } - if (fMatch == false) { - status = U_REGEX_INVALID_STATE; - return *this; - } - - // Copy input string from the end of previous match to start of current match - int64_t destLen = utext_nativeLength(dest); - if (fMatchStart > fAppendPosition) { - if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { - destLen += utext_replace(dest, destLen, destLen, fInputText->chunkContents+fAppendPosition, - (int32_t)(fMatchStart-fAppendPosition), &status); - } else { - int32_t len16; - if (UTEXT_USES_U16(fInputText)) { - len16 = (int32_t)(fMatchStart-fAppendPosition); - } else { - UErrorCode lengthStatus = U_ZERO_ERROR; - len16 = utext_extract(fInputText, fAppendPosition, fMatchStart, NULL, 0, &lengthStatus); - } - UChar *inputChars = (UChar *)uprv_malloc(sizeof(UChar)*(len16+1)); - if (inputChars == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - utext_extract(fInputText, fAppendPosition, fMatchStart, inputChars, len16+1, &status); - destLen += utext_replace(dest, destLen, destLen, inputChars, len16, &status); - uprv_free(inputChars); - } - } - fAppendPosition = fMatchEnd; - - - // scan the replacement text, looking for substitutions ($n) and \escapes. - // TODO: optimize this loop by efficiently scanning for '$' or '\', - // move entire ranges not containing substitutions. - UTEXT_SETNATIVEINDEX(replacement, 0); - for (UChar32 c = UTEXT_NEXT32(replacement); U_SUCCESS(status) && c != U_SENTINEL; c = UTEXT_NEXT32(replacement)) { - if (c == BACKSLASH) { - // Backslash Escape. Copy the following char out without further checks. - // Note: Surrogate pairs don't need any special handling - // The second half wont be a '$' or a '\', and - // will move to the dest normally on the next - // loop iteration. - c = UTEXT_CURRENT32(replacement); - if (c == U_SENTINEL) { - break; - } - - if (c==0x55/*U*/ || c==0x75/*u*/) { - // We have a \udddd or \Udddddddd escape sequence. - int32_t offset = 0; - struct URegexUTextUnescapeCharContext context = U_REGEX_UTEXT_UNESCAPE_CONTEXT(replacement); - UChar32 escapedChar = u_unescapeAt(uregex_utext_unescape_charAt, &offset, INT32_MAX, &context); - if (escapedChar != (UChar32)0xFFFFFFFF) { - if (U_IS_BMP(escapedChar)) { - UChar c16 = (UChar)escapedChar; - destLen += utext_replace(dest, destLen, destLen, &c16, 1, &status); - } else { - UChar surrogate[2]; - surrogate[0] = U16_LEAD(escapedChar); - surrogate[1] = U16_TRAIL(escapedChar); - if (U_SUCCESS(status)) { - destLen += utext_replace(dest, destLen, destLen, surrogate, 2, &status); - } - } - // TODO: Report errors for mal-formed \u escapes? - // As this is, the original sequence is output, which may be OK. - if (context.lastOffset == offset) { - (void)UTEXT_PREVIOUS32(replacement); - } else if (context.lastOffset != offset-1) { - utext_moveIndex32(replacement, offset - context.lastOffset - 1); - } - } - } else { - (void)UTEXT_NEXT32(replacement); - // Plain backslash escape. Just put out the escaped character. - if (U_IS_BMP(c)) { - UChar c16 = (UChar)c; - destLen += utext_replace(dest, destLen, destLen, &c16, 1, &status); - } else { - UChar surrogate[2]; - surrogate[0] = U16_LEAD(c); - surrogate[1] = U16_TRAIL(c); - if (U_SUCCESS(status)) { - destLen += utext_replace(dest, destLen, destLen, surrogate, 2, &status); - } - } - } - } else if (c != DOLLARSIGN) { - // Normal char, not a $. Copy it out without further checks. - if (U_IS_BMP(c)) { - UChar c16 = (UChar)c; - destLen += utext_replace(dest, destLen, destLen, &c16, 1, &status); - } else { - UChar surrogate[2]; - surrogate[0] = U16_LEAD(c); - surrogate[1] = U16_TRAIL(c); - if (U_SUCCESS(status)) { - destLen += utext_replace(dest, destLen, destLen, surrogate, 2, &status); - } - } - } else { - // We've got a $. Pick up a capture group name or number if one follows. - // Consume digits so long as the resulting group number <= the number of - // number of capture groups in the pattern. - - int32_t groupNum = 0; - int32_t numDigits = 0; - UChar32 nextChar = utext_current32(replacement); - if (nextChar == LEFTBRACKET) { - // Scan for a Named Capture Group, ${name}. - UnicodeString groupName; - utext_next32(replacement); - while(U_SUCCESS(status) && nextChar != RIGHTBRACKET) { - nextChar = utext_next32(replacement); - if (nextChar == U_SENTINEL) { - status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; - } else if ((nextChar >= 0x41 && nextChar <= 0x5a) || // A..Z - (nextChar >= 0x61 && nextChar <= 0x7a) || // a..z - (nextChar >= 0x31 && nextChar <= 0x39)) { // 0..9 - groupName.append(nextChar); - } else if (nextChar == RIGHTBRACKET) { - groupNum = fPattern->fNamedCaptureMap ? uhash_geti(fPattern->fNamedCaptureMap, &groupName) : 0; - if (groupNum == 0) { - status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; - } - } else { - // Character was something other than a name char or a closing '}' - status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; - } - } - - } else if (u_isdigit(nextChar)) { - // $n Scan for a capture group number - int32_t numCaptureGroups = fPattern->fGroupMap->size(); - for (;;) { - nextChar = UTEXT_CURRENT32(replacement); - if (nextChar == U_SENTINEL) { - break; - } - if (u_isdigit(nextChar) == false) { - break; - } - int32_t nextDigitVal = u_charDigitValue(nextChar); - if (groupNum*10 + nextDigitVal > numCaptureGroups) { - // Don't consume the next digit if it makes the capture group number too big. - if (numDigits == 0) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - } - break; - } - (void)UTEXT_NEXT32(replacement); - groupNum=groupNum*10 + nextDigitVal; - ++numDigits; - } - } else { - // $ not followed by capture group name or number. - status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; - } - - if (U_SUCCESS(status)) { - destLen += appendGroup(groupNum, dest, status); - } - } // End of $ capture group handling - } // End of per-character loop through the replacement string. - - return *this; -} - - - -//-------------------------------------------------------------------------------- -// -// appendTail Intended to be used in conjunction with appendReplacement() -// To the destination string, append everything following -// the last match position from the input string. -// -// Note: Match ranges do not affect appendTail or appendReplacement -// -//-------------------------------------------------------------------------------- -UnicodeString &RegexMatcher::appendTail(UnicodeString &dest) { - UErrorCode status = U_ZERO_ERROR; - UText resultText = UTEXT_INITIALIZER; - utext_openUnicodeString(&resultText, &dest, &status); - - if (U_SUCCESS(status)) { - appendTail(&resultText, status); - utext_close(&resultText); - } - - return dest; -} - -// -// appendTail, UText mode -// -UText *RegexMatcher::appendTail(UText *dest, UErrorCode &status) { - if (U_FAILURE(status)) { - return dest; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return dest; - } - - if (fInputLength > fAppendPosition) { - if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { - int64_t destLen = utext_nativeLength(dest); - utext_replace(dest, destLen, destLen, fInputText->chunkContents+fAppendPosition, - (int32_t)(fInputLength-fAppendPosition), &status); - } else { - int32_t len16; - if (UTEXT_USES_U16(fInputText)) { - len16 = (int32_t)(fInputLength-fAppendPosition); - } else { - len16 = utext_extract(fInputText, fAppendPosition, fInputLength, NULL, 0, &status); - status = U_ZERO_ERROR; // buffer overflow - } - - UChar *inputChars = (UChar *)uprv_malloc(sizeof(UChar)*(len16)); - if (inputChars == NULL) { - fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; - } else { - utext_extract(fInputText, fAppendPosition, fInputLength, inputChars, len16, &status); // unterminated - int64_t destLen = utext_nativeLength(dest); - utext_replace(dest, destLen, destLen, inputChars, len16, &status); - uprv_free(inputChars); - } - } - } - return dest; -} - - - -//-------------------------------------------------------------------------------- -// -// end -// -//-------------------------------------------------------------------------------- -int32_t RegexMatcher::end(UErrorCode &err) const { - return end(0, err); -} - -int64_t RegexMatcher::end64(UErrorCode &err) const { - return end64(0, err); -} - -int64_t RegexMatcher::end64(int32_t group, UErrorCode &err) const { - if (U_FAILURE(err)) { - return -1; - } - if (fMatch == false) { - err = U_REGEX_INVALID_STATE; - return -1; - } - if (group < 0 || group > fPattern->fGroupMap->size()) { - err = U_INDEX_OUTOFBOUNDS_ERROR; - return -1; - } - int64_t e = -1; - if (group == 0) { - e = fMatchEnd; - } else { - // Get the position within the stack frame of the variables for - // this capture group. - int32_t groupOffset = fPattern->fGroupMap->elementAti(group-1); - U_ASSERT(groupOffset < fPattern->fFrameSize); - U_ASSERT(groupOffset >= 0); - e = fFrame->fExtra[groupOffset + 1]; - } - - return e; -} - -int32_t RegexMatcher::end(int32_t group, UErrorCode &err) const { - return (int32_t)end64(group, err); -} - -//-------------------------------------------------------------------------------- -// -// findProgressInterrupt This function is called once for each advance in the target -// string from the find() function, and calls the user progress callback -// function if there is one installed. -// -// Return: true if the find operation is to be terminated. -// false if the find operation is to continue running. -// -//-------------------------------------------------------------------------------- -UBool RegexMatcher::findProgressInterrupt(int64_t pos, UErrorCode &status) { - if (fFindProgressCallbackFn && !(*fFindProgressCallbackFn)(fFindProgressCallbackContext, pos)) { - status = U_REGEX_STOPPED_BY_CALLER; - return true; - } - return false; -} - -//-------------------------------------------------------------------------------- -// -// find() -// -//-------------------------------------------------------------------------------- -UBool RegexMatcher::find() { - if (U_FAILURE(fDeferredStatus)) { - return false; - } - UErrorCode status = U_ZERO_ERROR; - UBool result = find(status); - return result; -} - -//-------------------------------------------------------------------------------- -// -// find() -// -//-------------------------------------------------------------------------------- -UBool RegexMatcher::find(UErrorCode &status) { - // Start at the position of the last match end. (Will be zero if the - // matcher has been reset.) - // - if (U_FAILURE(status)) { - return false; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return false; - } - - if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { - return findUsingChunk(status); - } - - int64_t startPos = fMatchEnd; - if (startPos==0) { - startPos = fActiveStart; - } - - if (fMatch) { - // Save the position of any previous successful match. - fLastMatchEnd = fMatchEnd; - - if (fMatchStart == fMatchEnd) { - // Previous match had zero length. Move start position up one position - // to avoid sending find() into a loop on zero-length matches. - if (startPos >= fActiveLimit) { - fMatch = false; - fHitEnd = true; - return false; - } - UTEXT_SETNATIVEINDEX(fInputText, startPos); - (void)UTEXT_NEXT32(fInputText); - startPos = UTEXT_GETNATIVEINDEX(fInputText); - } - } else { - if (fLastMatchEnd >= 0) { - // A previous find() failed to match. Don't try again. - // (without this test, a pattern with a zero-length match - // could match again at the end of an input string.) - fHitEnd = true; - return false; - } - } - - - // Compute the position in the input string beyond which a match can not begin, because - // the minimum length match would extend past the end of the input. - // Note: some patterns that cannot match anything will have fMinMatchLength==Max Int. - // Be aware of possible overflows if making changes here. - int64_t testStartLimit; - if (UTEXT_USES_U16(fInputText)) { - testStartLimit = fActiveLimit - fPattern->fMinMatchLen; - if (startPos > testStartLimit) { - fMatch = false; - fHitEnd = true; - return false; - } - } else { - // We don't know exactly how long the minimum match length is in native characters. - // Treat anything > 0 as 1. - testStartLimit = fActiveLimit - (fPattern->fMinMatchLen > 0 ? 1 : 0); - } - - UChar32 c; - U_ASSERT(startPos >= 0); - - switch (fPattern->fStartType) { - case START_NO_INFO: - // No optimization was found. - // Try a match at each input position. - for (;;) { - MatchAt(startPos, false, status); - if (U_FAILURE(status)) { - return false; - } - if (fMatch) { - return true; - } - if (startPos >= testStartLimit) { - fHitEnd = true; - return false; - } - UTEXT_SETNATIVEINDEX(fInputText, startPos); - (void)UTEXT_NEXT32(fInputText); - startPos = UTEXT_GETNATIVEINDEX(fInputText); - // Note that it's perfectly OK for a pattern to have a zero-length - // match at the end of a string, so we must make sure that the loop - // runs with startPos == testStartLimit the last time through. - if (findProgressInterrupt(startPos, status)) - return false; - } - UPRV_UNREACHABLE_EXIT; - - case START_START: - // Matches are only possible at the start of the input string - // (pattern begins with ^ or \A) - if (startPos > fActiveStart) { - fMatch = false; - return false; - } - MatchAt(startPos, false, status); - if (U_FAILURE(status)) { - return false; - } - return fMatch; - - - case START_SET: - { - // Match may start on any char from a pre-computed set. - U_ASSERT(fPattern->fMinMatchLen > 0); - UTEXT_SETNATIVEINDEX(fInputText, startPos); - for (;;) { - int64_t pos = startPos; - c = UTEXT_NEXT32(fInputText); - startPos = UTEXT_GETNATIVEINDEX(fInputText); - // c will be -1 (U_SENTINEL) at end of text, in which case we - // skip this next block (so we don't have a negative array index) - // and handle end of text in the following block. - if (c >= 0 && ((c<256 && fPattern->fInitialChars8->contains(c)) || - (c>=256 && fPattern->fInitialChars->contains(c)))) { - MatchAt(pos, false, status); - if (U_FAILURE(status)) { - return false; - } - if (fMatch) { - return true; - } - UTEXT_SETNATIVEINDEX(fInputText, pos); - } - if (startPos > testStartLimit) { - fMatch = false; - fHitEnd = true; - return false; - } - if (findProgressInterrupt(startPos, status)) - return false; - } - } - UPRV_UNREACHABLE_EXIT; - - case START_STRING: - case START_CHAR: - { - // Match starts on exactly one char. - U_ASSERT(fPattern->fMinMatchLen > 0); - UChar32 theChar = fPattern->fInitialChar; - UTEXT_SETNATIVEINDEX(fInputText, startPos); - for (;;) { - int64_t pos = startPos; - c = UTEXT_NEXT32(fInputText); - startPos = UTEXT_GETNATIVEINDEX(fInputText); - if (c == theChar) { - MatchAt(pos, false, status); - if (U_FAILURE(status)) { - return false; - } - if (fMatch) { - return true; - } - UTEXT_SETNATIVEINDEX(fInputText, startPos); - } - if (startPos > testStartLimit) { - fMatch = false; - fHitEnd = true; - return false; - } - if (findProgressInterrupt(startPos, status)) - return false; - } - } - UPRV_UNREACHABLE_EXIT; - - case START_LINE: - { - UChar32 ch; - if (startPos == fAnchorStart) { - MatchAt(startPos, false, status); - if (U_FAILURE(status)) { - return false; - } - if (fMatch) { - return true; - } - UTEXT_SETNATIVEINDEX(fInputText, startPos); - ch = UTEXT_NEXT32(fInputText); - startPos = UTEXT_GETNATIVEINDEX(fInputText); - } else { - UTEXT_SETNATIVEINDEX(fInputText, startPos); - ch = UTEXT_PREVIOUS32(fInputText); - UTEXT_SETNATIVEINDEX(fInputText, startPos); - } - - if (fPattern->fFlags & UREGEX_UNIX_LINES) { - for (;;) { - if (ch == 0x0a) { - MatchAt(startPos, false, status); - if (U_FAILURE(status)) { - return false; - } - if (fMatch) { - return true; - } - UTEXT_SETNATIVEINDEX(fInputText, startPos); - } - if (startPos >= testStartLimit) { - fMatch = false; - fHitEnd = true; - return false; - } - ch = UTEXT_NEXT32(fInputText); - startPos = UTEXT_GETNATIVEINDEX(fInputText); - // Note that it's perfectly OK for a pattern to have a zero-length - // match at the end of a string, so we must make sure that the loop - // runs with startPos == testStartLimit the last time through. - if (findProgressInterrupt(startPos, status)) - return false; - } - } else { - for (;;) { - if (isLineTerminator(ch)) { - if (ch == 0x0d && startPos < fActiveLimit && UTEXT_CURRENT32(fInputText) == 0x0a) { - (void)UTEXT_NEXT32(fInputText); - startPos = UTEXT_GETNATIVEINDEX(fInputText); - } - MatchAt(startPos, false, status); - if (U_FAILURE(status)) { - return false; - } - if (fMatch) { - return true; - } - UTEXT_SETNATIVEINDEX(fInputText, startPos); - } - if (startPos >= testStartLimit) { - fMatch = false; - fHitEnd = true; - return false; - } - ch = UTEXT_NEXT32(fInputText); - startPos = UTEXT_GETNATIVEINDEX(fInputText); - // Note that it's perfectly OK for a pattern to have a zero-length - // match at the end of a string, so we must make sure that the loop - // runs with startPos == testStartLimit the last time through. - if (findProgressInterrupt(startPos, status)) - return false; - } - } - } - - default: - UPRV_UNREACHABLE_ASSERT; - // Unknown value in fPattern->fStartType, should be from StartOfMatch enum. But - // we have reports of this in production code, don't use UPRV_UNREACHABLE_EXIT. - // See ICU-21669. - status = U_INTERNAL_PROGRAM_ERROR; - return false; - } - - UPRV_UNREACHABLE_EXIT; -} - - - -UBool RegexMatcher::find(int64_t start, UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return false; - } - this->reset(); // Note: Reset() is specified by Java Matcher documentation. - // This will reset the region to be the full input length. - if (start < 0) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - return false; - } - - int64_t nativeStart = start; - if (nativeStart < fActiveStart || nativeStart > fActiveLimit) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - return false; - } - fMatchEnd = nativeStart; - return find(status); -} - - -//-------------------------------------------------------------------------------- -// -// findUsingChunk() -- like find(), but with the advance knowledge that the -// entire string is available in the UText's chunk buffer. -// -//-------------------------------------------------------------------------------- -UBool RegexMatcher::findUsingChunk(UErrorCode &status) { - // Start at the position of the last match end. (Will be zero if the - // matcher has been reset. - // - - int32_t startPos = (int32_t)fMatchEnd; - if (startPos==0) { - startPos = (int32_t)fActiveStart; - } - - const UChar *inputBuf = fInputText->chunkContents; - - if (fMatch) { - // Save the position of any previous successful match. - fLastMatchEnd = fMatchEnd; - - if (fMatchStart == fMatchEnd) { - // Previous match had zero length. Move start position up one position - // to avoid sending find() into a loop on zero-length matches. - if (startPos >= fActiveLimit) { - fMatch = false; - fHitEnd = true; - return false; - } - U16_FWD_1(inputBuf, startPos, fInputLength); - } - } else { - if (fLastMatchEnd >= 0) { - // A previous find() failed to match. Don't try again. - // (without this test, a pattern with a zero-length match - // could match again at the end of an input string.) - fHitEnd = true; - return false; - } - } - - - // Compute the position in the input string beyond which a match can not begin, because - // the minimum length match would extend past the end of the input. - // Note: some patterns that cannot match anything will have fMinMatchLength==Max Int. - // Be aware of possible overflows if making changes here. - // Note: a match can begin at inputBuf + testLen; it is an inclusive limit. - int32_t testLen = (int32_t)(fActiveLimit - fPattern->fMinMatchLen); - if (startPos > testLen) { - fMatch = false; - fHitEnd = true; - return false; - } - - UChar32 c; - U_ASSERT(startPos >= 0); - - switch (fPattern->fStartType) { - case START_NO_INFO: - // No optimization was found. - // Try a match at each input position. - for (;;) { - MatchChunkAt(startPos, false, status); - if (U_FAILURE(status)) { - return false; - } - if (fMatch) { - return true; - } - if (startPos >= testLen) { - fHitEnd = true; - return false; - } - U16_FWD_1(inputBuf, startPos, fActiveLimit); - // Note that it's perfectly OK for a pattern to have a zero-length - // match at the end of a string, so we must make sure that the loop - // runs with startPos == testLen the last time through. - if (findProgressInterrupt(startPos, status)) - return false; - } - UPRV_UNREACHABLE_EXIT; - - case START_START: - // Matches are only possible at the start of the input string - // (pattern begins with ^ or \A) - if (startPos > fActiveStart) { - fMatch = false; - return false; - } - MatchChunkAt(startPos, false, status); - if (U_FAILURE(status)) { - return false; - } - return fMatch; - - - case START_SET: - { - // Match may start on any char from a pre-computed set. - U_ASSERT(fPattern->fMinMatchLen > 0); - for (;;) { - int32_t pos = startPos; - U16_NEXT(inputBuf, startPos, fActiveLimit, c); // like c = inputBuf[startPos++]; - if ((c<256 && fPattern->fInitialChars8->contains(c)) || - (c>=256 && fPattern->fInitialChars->contains(c))) { - MatchChunkAt(pos, false, status); - if (U_FAILURE(status)) { - return false; - } - if (fMatch) { - return true; - } - } - if (startPos > testLen) { - fMatch = false; - fHitEnd = true; - return false; - } - if (findProgressInterrupt(startPos, status)) - return false; - } - } - UPRV_UNREACHABLE_EXIT; - - case START_STRING: - case START_CHAR: - { - // Match starts on exactly one char. - U_ASSERT(fPattern->fMinMatchLen > 0); - UChar32 theChar = fPattern->fInitialChar; - for (;;) { - int32_t pos = startPos; - U16_NEXT(inputBuf, startPos, fActiveLimit, c); // like c = inputBuf[startPos++]; - if (c == theChar) { - MatchChunkAt(pos, false, status); - if (U_FAILURE(status)) { - return false; - } - if (fMatch) { - return true; - } - } - if (startPos > testLen) { - fMatch = false; - fHitEnd = true; - return false; - } - if (findProgressInterrupt(startPos, status)) - return false; - } - } - UPRV_UNREACHABLE_EXIT; - - case START_LINE: - { - UChar32 ch; - if (startPos == fAnchorStart) { - MatchChunkAt(startPos, false, status); - if (U_FAILURE(status)) { - return false; - } - if (fMatch) { - return true; - } - U16_FWD_1(inputBuf, startPos, fActiveLimit); - } - - if (fPattern->fFlags & UREGEX_UNIX_LINES) { - for (;;) { - ch = inputBuf[startPos-1]; - if (ch == 0x0a) { - MatchChunkAt(startPos, false, status); - if (U_FAILURE(status)) { - return false; - } - if (fMatch) { - return true; - } - } - if (startPos >= testLen) { - fMatch = false; - fHitEnd = true; - return false; - } - U16_FWD_1(inputBuf, startPos, fActiveLimit); - // Note that it's perfectly OK for a pattern to have a zero-length - // match at the end of a string, so we must make sure that the loop - // runs with startPos == testLen the last time through. - if (findProgressInterrupt(startPos, status)) - return false; - } - } else { - for (;;) { - ch = inputBuf[startPos-1]; - if (isLineTerminator(ch)) { - if (ch == 0x0d && startPos < fActiveLimit && inputBuf[startPos] == 0x0a) { - startPos++; - } - MatchChunkAt(startPos, false, status); - if (U_FAILURE(status)) { - return false; - } - if (fMatch) { - return true; - } - } - if (startPos >= testLen) { - fMatch = false; - fHitEnd = true; - return false; - } - U16_FWD_1(inputBuf, startPos, fActiveLimit); - // Note that it's perfectly OK for a pattern to have a zero-length - // match at the end of a string, so we must make sure that the loop - // runs with startPos == testLen the last time through. - if (findProgressInterrupt(startPos, status)) - return false; - } - } - } - - default: - UPRV_UNREACHABLE_ASSERT; - // Unknown value in fPattern->fStartType, should be from StartOfMatch enum. But - // we have reports of this in production code, don't use UPRV_UNREACHABLE_EXIT. - // See ICU-21669. - status = U_INTERNAL_PROGRAM_ERROR; - return false; - } - - UPRV_UNREACHABLE_EXIT; -} - - - -//-------------------------------------------------------------------------------- -// -// group() -// -//-------------------------------------------------------------------------------- -UnicodeString RegexMatcher::group(UErrorCode &status) const { - return group(0, status); -} - -// Return immutable shallow clone -UText *RegexMatcher::group(UText *dest, int64_t &group_len, UErrorCode &status) const { - return group(0, dest, group_len, status); -} - -// Return immutable shallow clone -UText *RegexMatcher::group(int32_t groupNum, UText *dest, int64_t &group_len, UErrorCode &status) const { - group_len = 0; - if (U_FAILURE(status)) { - return dest; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - } else if (fMatch == false) { - status = U_REGEX_INVALID_STATE; - } else if (groupNum < 0 || groupNum > fPattern->fGroupMap->size()) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - } - - if (U_FAILURE(status)) { - return dest; - } - - int64_t s, e; - if (groupNum == 0) { - s = fMatchStart; - e = fMatchEnd; - } else { - int32_t groupOffset = fPattern->fGroupMap->elementAti(groupNum-1); - U_ASSERT(groupOffset < fPattern->fFrameSize); - U_ASSERT(groupOffset >= 0); - s = fFrame->fExtra[groupOffset]; - e = fFrame->fExtra[groupOffset+1]; - } - - if (s < 0) { - // A capture group wasn't part of the match - return utext_clone(dest, fInputText, false, true, &status); - } - U_ASSERT(s <= e); - group_len = e - s; - - dest = utext_clone(dest, fInputText, false, true, &status); - if (dest) - UTEXT_SETNATIVEINDEX(dest, s); - return dest; -} - -UnicodeString RegexMatcher::group(int32_t groupNum, UErrorCode &status) const { - UnicodeString result; - int64_t groupStart = start64(groupNum, status); - int64_t groupEnd = end64(groupNum, status); - if (U_FAILURE(status) || groupStart == -1 || groupStart == groupEnd) { - return result; - } - - // Get the group length using a utext_extract preflight. - // UText is actually pretty efficient at this when underlying encoding is UTF-16. - int32_t length = utext_extract(fInputText, groupStart, groupEnd, NULL, 0, &status); - if (status != U_BUFFER_OVERFLOW_ERROR) { - return result; - } - - status = U_ZERO_ERROR; - UChar *buf = result.getBuffer(length); - if (buf == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - int32_t extractLength = utext_extract(fInputText, groupStart, groupEnd, buf, length, &status); - result.releaseBuffer(extractLength); - U_ASSERT(length == extractLength); - } - return result; -} - - -//-------------------------------------------------------------------------------- -// -// appendGroup() -- currently internal only, appends a group to a UText rather -// than replacing its contents -// -//-------------------------------------------------------------------------------- - -int64_t RegexMatcher::appendGroup(int32_t groupNum, UText *dest, UErrorCode &status) const { - if (U_FAILURE(status)) { - return 0; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return 0; - } - int64_t destLen = utext_nativeLength(dest); - - if (fMatch == false) { - status = U_REGEX_INVALID_STATE; - return utext_replace(dest, destLen, destLen, NULL, 0, &status); - } - if (groupNum < 0 || groupNum > fPattern->fGroupMap->size()) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - return utext_replace(dest, destLen, destLen, NULL, 0, &status); - } - - int64_t s, e; - if (groupNum == 0) { - s = fMatchStart; - e = fMatchEnd; - } else { - int32_t groupOffset = fPattern->fGroupMap->elementAti(groupNum-1); - U_ASSERT(groupOffset < fPattern->fFrameSize); - U_ASSERT(groupOffset >= 0); - s = fFrame->fExtra[groupOffset]; - e = fFrame->fExtra[groupOffset+1]; - } - - if (s < 0) { - // A capture group wasn't part of the match - return utext_replace(dest, destLen, destLen, NULL, 0, &status); - } - U_ASSERT(s <= e); - - int64_t deltaLen; - if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { - U_ASSERT(e <= fInputLength); - deltaLen = utext_replace(dest, destLen, destLen, fInputText->chunkContents+s, (int32_t)(e-s), &status); - } else { - int32_t len16; - if (UTEXT_USES_U16(fInputText)) { - len16 = (int32_t)(e-s); - } else { - UErrorCode lengthStatus = U_ZERO_ERROR; - len16 = utext_extract(fInputText, s, e, NULL, 0, &lengthStatus); - } - UChar *groupChars = (UChar *)uprv_malloc(sizeof(UChar)*(len16+1)); - if (groupChars == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - utext_extract(fInputText, s, e, groupChars, len16+1, &status); - - deltaLen = utext_replace(dest, destLen, destLen, groupChars, len16, &status); - uprv_free(groupChars); - } - return deltaLen; -} - - - -//-------------------------------------------------------------------------------- -// -// groupCount() -// -//-------------------------------------------------------------------------------- -int32_t RegexMatcher::groupCount() const { - return fPattern->fGroupMap->size(); -} - -//-------------------------------------------------------------------------------- -// -// hasAnchoringBounds() -// -//-------------------------------------------------------------------------------- -UBool RegexMatcher::hasAnchoringBounds() const { - return fAnchoringBounds; -} - - -//-------------------------------------------------------------------------------- -// -// hasTransparentBounds() -// -//-------------------------------------------------------------------------------- -UBool RegexMatcher::hasTransparentBounds() const { - return fTransparentBounds; -} - - - -//-------------------------------------------------------------------------------- -// -// hitEnd() -// -//-------------------------------------------------------------------------------- -UBool RegexMatcher::hitEnd() const { - return fHitEnd; -} - - -//-------------------------------------------------------------------------------- -// -// input() -// -//-------------------------------------------------------------------------------- -const UnicodeString &RegexMatcher::input() const { - if (!fInput) { - UErrorCode status = U_ZERO_ERROR; - int32_t len16; - if (UTEXT_USES_U16(fInputText)) { - len16 = (int32_t)fInputLength; - } else { - len16 = utext_extract(fInputText, 0, fInputLength, NULL, 0, &status); - status = U_ZERO_ERROR; // overflow, length status - } - UnicodeString *result = new UnicodeString(len16, 0, 0); - - UChar *inputChars = result->getBuffer(len16); - utext_extract(fInputText, 0, fInputLength, inputChars, len16, &status); // unterminated warning - result->releaseBuffer(len16); - - (*(const UnicodeString **)&fInput) = result; // pointer assignment, rather than operator= - } - - return *fInput; -} - -//-------------------------------------------------------------------------------- -// -// inputText() -// -//-------------------------------------------------------------------------------- -UText *RegexMatcher::inputText() const { - return fInputText; -} - - -//-------------------------------------------------------------------------------- -// -// getInput() -- like inputText(), but makes a clone or copies into another UText -// -//-------------------------------------------------------------------------------- -UText *RegexMatcher::getInput (UText *dest, UErrorCode &status) const { - if (U_FAILURE(status)) { - return dest; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return dest; - } - - if (dest) { - if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { - utext_replace(dest, 0, utext_nativeLength(dest), fInputText->chunkContents, (int32_t)fInputLength, &status); - } else { - int32_t input16Len; - if (UTEXT_USES_U16(fInputText)) { - input16Len = (int32_t)fInputLength; - } else { - UErrorCode lengthStatus = U_ZERO_ERROR; - input16Len = utext_extract(fInputText, 0, fInputLength, NULL, 0, &lengthStatus); // buffer overflow error - } - UChar *inputChars = (UChar *)uprv_malloc(sizeof(UChar)*(input16Len)); - if (inputChars == NULL) { - return dest; - } - - status = U_ZERO_ERROR; - utext_extract(fInputText, 0, fInputLength, inputChars, input16Len, &status); // not terminated warning - status = U_ZERO_ERROR; - utext_replace(dest, 0, utext_nativeLength(dest), inputChars, input16Len, &status); - - uprv_free(inputChars); - } - return dest; - } else { - return utext_clone(NULL, fInputText, false, true, &status); - } -} - - -static UBool compat_SyncMutableUTextContents(UText *ut); -static UBool compat_SyncMutableUTextContents(UText *ut) { - UBool retVal = false; - - // In the following test, we're really only interested in whether the UText should switch - // between heap and stack allocation. If length hasn't changed, we won't, so the chunkContents - // will still point to the correct data. - if (utext_nativeLength(ut) != ut->nativeIndexingLimit) { - UnicodeString *us=(UnicodeString *)ut->context; - - // Update to the latest length. - // For example, (utext_nativeLength(ut) != ut->nativeIndexingLimit). - int32_t newLength = us->length(); - - // Update the chunk description. - // The buffer may have switched between stack- and heap-based. - ut->chunkContents = us->getBuffer(); - ut->chunkLength = newLength; - ut->chunkNativeLimit = newLength; - ut->nativeIndexingLimit = newLength; - retVal = true; - } - - return retVal; -} - -//-------------------------------------------------------------------------------- -// -// lookingAt() -// -//-------------------------------------------------------------------------------- -UBool RegexMatcher::lookingAt(UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return false; - } - - if (fInputUniStrMaybeMutable) { - if (compat_SyncMutableUTextContents(fInputText)) { - fInputLength = utext_nativeLength(fInputText); - reset(); - } - } - else { - resetPreserveRegion(); - } - if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { - MatchChunkAt((int32_t)fActiveStart, false, status); - } else { - MatchAt(fActiveStart, false, status); - } - return fMatch; -} - - -UBool RegexMatcher::lookingAt(int64_t start, UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return false; - } - reset(); - - if (start < 0) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - return false; - } - - if (fInputUniStrMaybeMutable) { - if (compat_SyncMutableUTextContents(fInputText)) { - fInputLength = utext_nativeLength(fInputText); - reset(); - } - } - - int64_t nativeStart; - nativeStart = start; - if (nativeStart < fActiveStart || nativeStart > fActiveLimit) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - return false; - } - - if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { - MatchChunkAt((int32_t)nativeStart, false, status); - } else { - MatchAt(nativeStart, false, status); - } - return fMatch; -} - - - -//-------------------------------------------------------------------------------- -// -// matches() -// -//-------------------------------------------------------------------------------- -UBool RegexMatcher::matches(UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return false; - } - - if (fInputUniStrMaybeMutable) { - if (compat_SyncMutableUTextContents(fInputText)) { - fInputLength = utext_nativeLength(fInputText); - reset(); - } - } - else { - resetPreserveRegion(); - } - - if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { - MatchChunkAt((int32_t)fActiveStart, true, status); - } else { - MatchAt(fActiveStart, true, status); - } - return fMatch; -} - - -UBool RegexMatcher::matches(int64_t start, UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return false; - } - reset(); - - if (start < 0) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - return false; - } - - if (fInputUniStrMaybeMutable) { - if (compat_SyncMutableUTextContents(fInputText)) { - fInputLength = utext_nativeLength(fInputText); - reset(); - } - } - - int64_t nativeStart; - nativeStart = start; - if (nativeStart < fActiveStart || nativeStart > fActiveLimit) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - return false; - } - - if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { - MatchChunkAt((int32_t)nativeStart, true, status); - } else { - MatchAt(nativeStart, true, status); - } - return fMatch; -} - - - -//-------------------------------------------------------------------------------- -// -// pattern -// -//-------------------------------------------------------------------------------- -const RegexPattern &RegexMatcher::pattern() const { - return *fPattern; -} - - - -//-------------------------------------------------------------------------------- -// -// region -// -//-------------------------------------------------------------------------------- -RegexMatcher &RegexMatcher::region(int64_t regionStart, int64_t regionLimit, int64_t startIndex, UErrorCode &status) { - if (U_FAILURE(status)) { - return *this; - } - - if (regionStart>regionLimit || regionStart<0 || regionLimit<0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } - - int64_t nativeStart = regionStart; - int64_t nativeLimit = regionLimit; - if (nativeStart > fInputLength || nativeLimit > fInputLength) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } - - if (startIndex == -1) - this->reset(); - else - resetPreserveRegion(); - - fRegionStart = nativeStart; - fRegionLimit = nativeLimit; - fActiveStart = nativeStart; - fActiveLimit = nativeLimit; - - if (startIndex != -1) { - if (startIndex < fActiveStart || startIndex > fActiveLimit) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - } - fMatchEnd = startIndex; - } - - if (!fTransparentBounds) { - fLookStart = nativeStart; - fLookLimit = nativeLimit; - } - if (fAnchoringBounds) { - fAnchorStart = nativeStart; - fAnchorLimit = nativeLimit; - } - return *this; -} - -RegexMatcher &RegexMatcher::region(int64_t start, int64_t limit, UErrorCode &status) { - return region(start, limit, -1, status); -} - -//-------------------------------------------------------------------------------- -// -// regionEnd -// -//-------------------------------------------------------------------------------- -int32_t RegexMatcher::regionEnd() const { - return (int32_t)fRegionLimit; -} - -int64_t RegexMatcher::regionEnd64() const { - return fRegionLimit; -} - -//-------------------------------------------------------------------------------- -// -// regionStart -// -//-------------------------------------------------------------------------------- -int32_t RegexMatcher::regionStart() const { - return (int32_t)fRegionStart; -} - -int64_t RegexMatcher::regionStart64() const { - return fRegionStart; -} - - -//-------------------------------------------------------------------------------- -// -// replaceAll -// -//-------------------------------------------------------------------------------- -UnicodeString RegexMatcher::replaceAll(const UnicodeString &replacement, UErrorCode &status) { - UText replacementText = UTEXT_INITIALIZER; - UText resultText = UTEXT_INITIALIZER; - UnicodeString resultString; - if (U_FAILURE(status)) { - return resultString; - } - - utext_openConstUnicodeString(&replacementText, &replacement, &status); - utext_openUnicodeString(&resultText, &resultString, &status); - - replaceAll(&replacementText, &resultText, status); - - utext_close(&resultText); - utext_close(&replacementText); - - return resultString; -} - - -// -// replaceAll, UText mode -// -UText *RegexMatcher::replaceAll(UText *replacement, UText *dest, UErrorCode &status) { - if (U_FAILURE(status)) { - return dest; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return dest; - } - - if (dest == NULL) { - UnicodeString emptyString; - UText empty = UTEXT_INITIALIZER; - - utext_openUnicodeString(&empty, &emptyString, &status); - dest = utext_clone(NULL, &empty, true, false, &status); - utext_close(&empty); - } - - if (U_SUCCESS(status)) { - reset(); - while (find()) { - appendReplacement(dest, replacement, status); - if (U_FAILURE(status)) { - break; - } - } - appendTail(dest, status); - } - - return dest; -} - - -//-------------------------------------------------------------------------------- -// -// replaceFirst -// -//-------------------------------------------------------------------------------- -UnicodeString RegexMatcher::replaceFirst(const UnicodeString &replacement, UErrorCode &status) { - UText replacementText = UTEXT_INITIALIZER; - UText resultText = UTEXT_INITIALIZER; - UnicodeString resultString; - - utext_openConstUnicodeString(&replacementText, &replacement, &status); - utext_openUnicodeString(&resultText, &resultString, &status); - - replaceFirst(&replacementText, &resultText, status); - - utext_close(&resultText); - utext_close(&replacementText); - - return resultString; -} - -// -// replaceFirst, UText mode -// -UText *RegexMatcher::replaceFirst(UText *replacement, UText *dest, UErrorCode &status) { - if (U_FAILURE(status)) { - return dest; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return dest; - } - - reset(); - if (!find()) { - return getInput(dest, status); - } - - if (dest == NULL) { - UnicodeString emptyString; - UText empty = UTEXT_INITIALIZER; - - utext_openUnicodeString(&empty, &emptyString, &status); - dest = utext_clone(NULL, &empty, true, false, &status); - utext_close(&empty); - } - - appendReplacement(dest, replacement, status); - appendTail(dest, status); - - return dest; -} - - -//-------------------------------------------------------------------------------- -// -// requireEnd -// -//-------------------------------------------------------------------------------- -UBool RegexMatcher::requireEnd() const { - return fRequireEnd; -} - - -//-------------------------------------------------------------------------------- -// -// reset -// -//-------------------------------------------------------------------------------- -RegexMatcher &RegexMatcher::reset() { - fRegionStart = 0; - fRegionLimit = fInputLength; - fActiveStart = 0; - fActiveLimit = fInputLength; - fAnchorStart = 0; - fAnchorLimit = fInputLength; - fLookStart = 0; - fLookLimit = fInputLength; - resetPreserveRegion(); - return *this; -} - - - -void RegexMatcher::resetPreserveRegion() { - fMatchStart = 0; - fMatchEnd = 0; - fLastMatchEnd = -1; - fAppendPosition = 0; - fMatch = false; - fHitEnd = false; - fRequireEnd = false; - fTime = 0; - fTickCounter = TIMER_INITIAL_VALUE; - //resetStack(); // more expensive than it looks... -} - - -RegexMatcher &RegexMatcher::reset(const UnicodeString &input) { - fInputText = utext_openConstUnicodeString(fInputText, &input, &fDeferredStatus); - if (fPattern->fNeedsAltInput) { - fAltInputText = utext_clone(fAltInputText, fInputText, false, true, &fDeferredStatus); - } - if (U_FAILURE(fDeferredStatus)) { - return *this; - } - fInputLength = utext_nativeLength(fInputText); - - reset(); - delete fInput; - fInput = NULL; - - // Do the following for any UnicodeString. - // This is for compatibility for those clients who modify the input string "live" during regex operations. - fInputUniStrMaybeMutable = true; - -#if UCONFIG_NO_BREAK_ITERATION==0 - if (fWordBreakItr) { - fWordBreakItr->setText(fInputText, fDeferredStatus); - } - if (fGCBreakItr) { - fGCBreakItr->setText(fInputText, fDeferredStatus); - } -#endif - - return *this; -} - - -RegexMatcher &RegexMatcher::reset(UText *input) { - if (fInputText != input) { - fInputText = utext_clone(fInputText, input, false, true, &fDeferredStatus); - if (fPattern->fNeedsAltInput) fAltInputText = utext_clone(fAltInputText, fInputText, false, true, &fDeferredStatus); - if (U_FAILURE(fDeferredStatus)) { - return *this; - } - fInputLength = utext_nativeLength(fInputText); - - delete fInput; - fInput = NULL; - -#if UCONFIG_NO_BREAK_ITERATION==0 - if (fWordBreakItr) { - fWordBreakItr->setText(input, fDeferredStatus); - } - if (fGCBreakItr) { - fGCBreakItr->setText(fInputText, fDeferredStatus); - } -#endif - } - reset(); - fInputUniStrMaybeMutable = false; - - return *this; -} - -/*RegexMatcher &RegexMatcher::reset(const UChar *) { - fDeferredStatus = U_INTERNAL_PROGRAM_ERROR; - return *this; -}*/ - -RegexMatcher &RegexMatcher::reset(int64_t position, UErrorCode &status) { - if (U_FAILURE(status)) { - return *this; - } - reset(); // Reset also resets the region to be the entire string. - - if (position < 0 || position > fActiveLimit) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - return *this; - } - fMatchEnd = position; - return *this; -} - - -//-------------------------------------------------------------------------------- -// -// refresh -// -//-------------------------------------------------------------------------------- -RegexMatcher &RegexMatcher::refreshInputText(UText *input, UErrorCode &status) { - if (U_FAILURE(status)) { - return *this; - } - if (input == NULL) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return *this; - } - if (utext_nativeLength(fInputText) != utext_nativeLength(input)) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return *this; - } - int64_t pos = utext_getNativeIndex(fInputText); - // Shallow read-only clone of the new UText into the existing input UText - fInputText = utext_clone(fInputText, input, false, true, &status); - if (U_FAILURE(status)) { - return *this; - } - utext_setNativeIndex(fInputText, pos); - - if (fAltInputText != NULL) { - pos = utext_getNativeIndex(fAltInputText); - fAltInputText = utext_clone(fAltInputText, input, false, true, &status); - if (U_FAILURE(status)) { - return *this; - } - utext_setNativeIndex(fAltInputText, pos); - } - return *this; -} - - - -//-------------------------------------------------------------------------------- -// -// setTrace -// -//-------------------------------------------------------------------------------- -void RegexMatcher::setTrace(UBool state) { - fTraceDebug = state; -} - - - -/** - * UText, replace entire contents of the destination UText with a substring of the source UText. - * - * @param src The source UText - * @param dest The destination UText. Must be writable. - * May be NULL, in which case a new UText will be allocated. - * @param start Start index of source substring. - * @param limit Limit index of source substring. - * @param status An error code. - */ -static UText *utext_extract_replace(UText *src, UText *dest, int64_t start, int64_t limit, UErrorCode *status) { - if (U_FAILURE(*status)) { - return dest; - } - if (start == limit) { - if (dest) { - utext_replace(dest, 0, utext_nativeLength(dest), NULL, 0, status); - return dest; - } else { - return utext_openUChars(NULL, NULL, 0, status); - } - } - int32_t length = utext_extract(src, start, limit, NULL, 0, status); - if (*status != U_BUFFER_OVERFLOW_ERROR && U_FAILURE(*status)) { - return dest; - } - *status = U_ZERO_ERROR; - MaybeStackArray buffer; - if (length >= buffer.getCapacity()) { - UChar *newBuf = buffer.resize(length+1); // Leave space for terminating Nul. - if (newBuf == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - } - } - utext_extract(src, start, limit, buffer.getAlias(), length+1, status); - if (dest) { - utext_replace(dest, 0, utext_nativeLength(dest), buffer.getAlias(), length, status); - return dest; - } - - // Caller did not provide a preexisting UText. - // Open a new one, and have it adopt the text buffer storage. - if (U_FAILURE(*status)) { - return NULL; - } - int32_t ownedLength = 0; - UChar *ownedBuf = buffer.orphanOrClone(length+1, ownedLength); - if (ownedBuf == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - UText *result = utext_openUChars(NULL, ownedBuf, length, status); - if (U_FAILURE(*status)) { - uprv_free(ownedBuf); - return NULL; - } - result->providerProperties |= (1 << UTEXT_PROVIDER_OWNS_TEXT); - return result; -} - - -//--------------------------------------------------------------------- -// -// split -// -//--------------------------------------------------------------------- -int32_t RegexMatcher::split(const UnicodeString &input, - UnicodeString dest[], - int32_t destCapacity, - UErrorCode &status) -{ - UText inputText = UTEXT_INITIALIZER; - utext_openConstUnicodeString(&inputText, &input, &status); - if (U_FAILURE(status)) { - return 0; - } - - UText **destText = (UText **)uprv_malloc(sizeof(UText*)*destCapacity); - if (destText == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return 0; - } - int32_t i; - for (i = 0; i < destCapacity; i++) { - destText[i] = utext_openUnicodeString(NULL, &dest[i], &status); - } - - int32_t fieldCount = split(&inputText, destText, destCapacity, status); - - for (i = 0; i < destCapacity; i++) { - utext_close(destText[i]); - } - - uprv_free(destText); - utext_close(&inputText); - return fieldCount; -} - -// -// split, UText mode -// -int32_t RegexMatcher::split(UText *input, - UText *dest[], - int32_t destCapacity, - UErrorCode &status) -{ - // - // Check arguments for validity - // - if (U_FAILURE(status)) { - return 0; - } - - if (destCapacity < 1) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - // - // Reset for the input text - // - reset(input); - int64_t nextOutputStringStart = 0; - if (fActiveLimit == 0) { - return 0; - } - - // - // Loop through the input text, searching for the delimiter pattern - // - int32_t i; - int32_t numCaptureGroups = fPattern->fGroupMap->size(); - for (i=0; ; i++) { - if (i>=destCapacity-1) { - // There is one or zero output string left. - // Fill the last output string with whatever is left from the input, then exit the loop. - // ( i will be == destCapacity if we filled the output array while processing - // capture groups of the delimiter expression, in which case we will discard the - // last capture group saved in favor of the unprocessed remainder of the - // input string.) - i = destCapacity-1; - if (fActiveLimit > nextOutputStringStart) { - if (UTEXT_FULL_TEXT_IN_CHUNK(input, fInputLength)) { - if (dest[i]) { - utext_replace(dest[i], 0, utext_nativeLength(dest[i]), - input->chunkContents+nextOutputStringStart, - (int32_t)(fActiveLimit-nextOutputStringStart), &status); - } else { - UText remainingText = UTEXT_INITIALIZER; - utext_openUChars(&remainingText, input->chunkContents+nextOutputStringStart, - fActiveLimit-nextOutputStringStart, &status); - dest[i] = utext_clone(NULL, &remainingText, true, false, &status); - utext_close(&remainingText); - } - } else { - UErrorCode lengthStatus = U_ZERO_ERROR; - int32_t remaining16Length = - utext_extract(input, nextOutputStringStart, fActiveLimit, NULL, 0, &lengthStatus); - UChar *remainingChars = (UChar *)uprv_malloc(sizeof(UChar)*(remaining16Length+1)); - if (remainingChars == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - - utext_extract(input, nextOutputStringStart, fActiveLimit, remainingChars, remaining16Length+1, &status); - if (dest[i]) { - utext_replace(dest[i], 0, utext_nativeLength(dest[i]), remainingChars, remaining16Length, &status); - } else { - UText remainingText = UTEXT_INITIALIZER; - utext_openUChars(&remainingText, remainingChars, remaining16Length, &status); - dest[i] = utext_clone(NULL, &remainingText, true, false, &status); - utext_close(&remainingText); - } - - uprv_free(remainingChars); - } - } - break; - } - if (find()) { - // We found another delimiter. Move everything from where we started looking - // up until the start of the delimiter into the next output string. - if (UTEXT_FULL_TEXT_IN_CHUNK(input, fInputLength)) { - if (dest[i]) { - utext_replace(dest[i], 0, utext_nativeLength(dest[i]), - input->chunkContents+nextOutputStringStart, - (int32_t)(fMatchStart-nextOutputStringStart), &status); - } else { - UText remainingText = UTEXT_INITIALIZER; - utext_openUChars(&remainingText, input->chunkContents+nextOutputStringStart, - fMatchStart-nextOutputStringStart, &status); - dest[i] = utext_clone(NULL, &remainingText, true, false, &status); - utext_close(&remainingText); - } - } else { - UErrorCode lengthStatus = U_ZERO_ERROR; - int32_t remaining16Length = utext_extract(input, nextOutputStringStart, fMatchStart, NULL, 0, &lengthStatus); - UChar *remainingChars = (UChar *)uprv_malloc(sizeof(UChar)*(remaining16Length+1)); - if (remainingChars == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - utext_extract(input, nextOutputStringStart, fMatchStart, remainingChars, remaining16Length+1, &status); - if (dest[i]) { - utext_replace(dest[i], 0, utext_nativeLength(dest[i]), remainingChars, remaining16Length, &status); - } else { - UText remainingText = UTEXT_INITIALIZER; - utext_openUChars(&remainingText, remainingChars, remaining16Length, &status); - dest[i] = utext_clone(NULL, &remainingText, true, false, &status); - utext_close(&remainingText); - } - - uprv_free(remainingChars); - } - nextOutputStringStart = fMatchEnd; - - // If the delimiter pattern has capturing parentheses, the captured - // text goes out into the next n destination strings. - int32_t groupNum; - for (groupNum=1; groupNum<=numCaptureGroups; groupNum++) { - if (i >= destCapacity-2) { - // Never fill the last available output string with capture group text. - // It will filled with the last field, the remainder of the - // unsplit input text. - break; - } - i++; - dest[i] = utext_extract_replace(fInputText, dest[i], - start64(groupNum, status), end64(groupNum, status), &status); - } - - if (nextOutputStringStart == fActiveLimit) { - // The delimiter was at the end of the string. We're done, but first - // we output one last empty string, for the empty field following - // the delimiter at the end of input. - if (i+1 < destCapacity) { - ++i; - if (dest[i] == NULL) { - dest[i] = utext_openUChars(NULL, NULL, 0, &status); - } else { - static const UChar emptyString[] = {(UChar)0}; - utext_replace(dest[i], 0, utext_nativeLength(dest[i]), emptyString, 0, &status); - } - } - break; - - } - } - else - { - // We ran off the end of the input while looking for the next delimiter. - // All the remaining text goes into the current output string. - if (UTEXT_FULL_TEXT_IN_CHUNK(input, fInputLength)) { - if (dest[i]) { - utext_replace(dest[i], 0, utext_nativeLength(dest[i]), - input->chunkContents+nextOutputStringStart, - (int32_t)(fActiveLimit-nextOutputStringStart), &status); - } else { - UText remainingText = UTEXT_INITIALIZER; - utext_openUChars(&remainingText, input->chunkContents+nextOutputStringStart, - fActiveLimit-nextOutputStringStart, &status); - dest[i] = utext_clone(NULL, &remainingText, true, false, &status); - utext_close(&remainingText); - } - } else { - UErrorCode lengthStatus = U_ZERO_ERROR; - int32_t remaining16Length = utext_extract(input, nextOutputStringStart, fActiveLimit, NULL, 0, &lengthStatus); - UChar *remainingChars = (UChar *)uprv_malloc(sizeof(UChar)*(remaining16Length+1)); - if (remainingChars == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - - utext_extract(input, nextOutputStringStart, fActiveLimit, remainingChars, remaining16Length+1, &status); - if (dest[i]) { - utext_replace(dest[i], 0, utext_nativeLength(dest[i]), remainingChars, remaining16Length, &status); - } else { - UText remainingText = UTEXT_INITIALIZER; - utext_openUChars(&remainingText, remainingChars, remaining16Length, &status); - dest[i] = utext_clone(NULL, &remainingText, true, false, &status); - utext_close(&remainingText); - } - - uprv_free(remainingChars); - } - break; - } - if (U_FAILURE(status)) { - break; - } - } // end of for loop - return i+1; -} - - -//-------------------------------------------------------------------------------- -// -// start -// -//-------------------------------------------------------------------------------- -int32_t RegexMatcher::start(UErrorCode &status) const { - return start(0, status); -} - -int64_t RegexMatcher::start64(UErrorCode &status) const { - return start64(0, status); -} - -//-------------------------------------------------------------------------------- -// -// start(int32_t group, UErrorCode &status) -// -//-------------------------------------------------------------------------------- - -int64_t RegexMatcher::start64(int32_t group, UErrorCode &status) const { - if (U_FAILURE(status)) { - return -1; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return -1; - } - if (fMatch == false) { - status = U_REGEX_INVALID_STATE; - return -1; - } - if (group < 0 || group > fPattern->fGroupMap->size()) { - status = U_INDEX_OUTOFBOUNDS_ERROR; - return -1; - } - int64_t s; - if (group == 0) { - s = fMatchStart; - } else { - int32_t groupOffset = fPattern->fGroupMap->elementAti(group-1); - U_ASSERT(groupOffset < fPattern->fFrameSize); - U_ASSERT(groupOffset >= 0); - s = fFrame->fExtra[groupOffset]; - } - - return s; -} - - -int32_t RegexMatcher::start(int32_t group, UErrorCode &status) const { - return (int32_t)start64(group, status); -} - -//-------------------------------------------------------------------------------- -// -// useAnchoringBounds -// -//-------------------------------------------------------------------------------- -RegexMatcher &RegexMatcher::useAnchoringBounds(UBool b) { - fAnchoringBounds = b; - fAnchorStart = (fAnchoringBounds ? fRegionStart : 0); - fAnchorLimit = (fAnchoringBounds ? fRegionLimit : fInputLength); - return *this; -} - - -//-------------------------------------------------------------------------------- -// -// useTransparentBounds -// -//-------------------------------------------------------------------------------- -RegexMatcher &RegexMatcher::useTransparentBounds(UBool b) { - fTransparentBounds = b; - fLookStart = (fTransparentBounds ? 0 : fRegionStart); - fLookLimit = (fTransparentBounds ? fInputLength : fRegionLimit); - return *this; -} - -//-------------------------------------------------------------------------------- -// -// setTimeLimit -// -//-------------------------------------------------------------------------------- -void RegexMatcher::setTimeLimit(int32_t limit, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return; - } - if (limit < 0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - fTimeLimit = limit; -} - - -//-------------------------------------------------------------------------------- -// -// getTimeLimit -// -//-------------------------------------------------------------------------------- -int32_t RegexMatcher::getTimeLimit() const { - return fTimeLimit; -} - - -//-------------------------------------------------------------------------------- -// -// setStackLimit -// -//-------------------------------------------------------------------------------- -void RegexMatcher::setStackLimit(int32_t limit, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return; - } - if (limit < 0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - - // Reset the matcher. This is needed here in case there is a current match - // whose final stack frame (containing the match results, pointed to by fFrame) - // would be lost by resizing to a smaller stack size. - reset(); - - if (limit == 0) { - // Unlimited stack expansion - fStack->setMaxCapacity(0); - } else { - // Change the units of the limit from bytes to ints, and bump the size up - // to be big enough to hold at least one stack frame for the pattern, - // if it isn't there already. - int32_t adjustedLimit = limit / sizeof(int32_t); - if (adjustedLimit < fPattern->fFrameSize) { - adjustedLimit = fPattern->fFrameSize; - } - fStack->setMaxCapacity(adjustedLimit); - } - fStackLimit = limit; -} - - -//-------------------------------------------------------------------------------- -// -// getStackLimit -// -//-------------------------------------------------------------------------------- -int32_t RegexMatcher::getStackLimit() const { - return fStackLimit; -} - - -//-------------------------------------------------------------------------------- -// -// setMatchCallback -// -//-------------------------------------------------------------------------------- -void RegexMatcher::setMatchCallback(URegexMatchCallback *callback, - const void *context, - UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - fCallbackFn = callback; - fCallbackContext = context; -} - - -//-------------------------------------------------------------------------------- -// -// getMatchCallback -// -//-------------------------------------------------------------------------------- -void RegexMatcher::getMatchCallback(URegexMatchCallback *&callback, - const void *&context, - UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - callback = fCallbackFn; - context = fCallbackContext; -} - - -//-------------------------------------------------------------------------------- -// -// setMatchCallback -// -//-------------------------------------------------------------------------------- -void RegexMatcher::setFindProgressCallback(URegexFindProgressCallback *callback, - const void *context, - UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - fFindProgressCallbackFn = callback; - fFindProgressCallbackContext = context; -} - - -//-------------------------------------------------------------------------------- -// -// getMatchCallback -// -//-------------------------------------------------------------------------------- -void RegexMatcher::getFindProgressCallback(URegexFindProgressCallback *&callback, - const void *&context, - UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - callback = fFindProgressCallbackFn; - context = fFindProgressCallbackContext; -} - - -//================================================================================ -// -// Code following this point in this file is the internal -// Match Engine Implementation. -// -//================================================================================ - - -//-------------------------------------------------------------------------------- -// -// resetStack -// Discard any previous contents of the state save stack, and initialize a -// new stack frame to all -1. The -1s are needed for capture group limits, -// where they indicate that a group has not yet matched anything. -//-------------------------------------------------------------------------------- -REStackFrame *RegexMatcher::resetStack() { - // Discard any previous contents of the state save stack, and initialize a - // new stack frame with all -1 data. The -1s are needed for capture group limits, - // where they indicate that a group has not yet matched anything. - fStack->removeAllElements(); - - REStackFrame *iFrame = (REStackFrame *)fStack->reserveBlock(fPattern->fFrameSize, fDeferredStatus); - if(U_FAILURE(fDeferredStatus)) { - return NULL; - } - - int32_t i; - for (i=0; ifFrameSize-RESTACKFRAME_HDRCOUNT; i++) { - iFrame->fExtra[i] = -1; - } - return iFrame; -} - - - -//-------------------------------------------------------------------------------- -// -// isWordBoundary -// in perl, "xab..cd..", \b is true at positions 0,3,5,7 -// For us, -// If the current char is a combining mark, -// \b is false. -// Else Scan backwards to the first non-combining char. -// We are at a boundary if the this char and the original chars are -// opposite in membership in \w set -// -// parameters: pos - the current position in the input buffer -// -// TODO: double-check edge cases at region boundaries. -// -//-------------------------------------------------------------------------------- -UBool RegexMatcher::isWordBoundary(int64_t pos) { - UBool isBoundary = false; - UBool cIsWord = false; - - if (pos >= fLookLimit) { - fHitEnd = true; - } else { - // Determine whether char c at current position is a member of the word set of chars. - // If we're off the end of the string, behave as though we're not at a word char. - UTEXT_SETNATIVEINDEX(fInputText, pos); - UChar32 c = UTEXT_CURRENT32(fInputText); - if (u_hasBinaryProperty(c, UCHAR_GRAPHEME_EXTEND) || u_charType(c) == U_FORMAT_CHAR) { - // Current char is a combining one. Not a boundary. - return false; - } - cIsWord = RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET].contains(c); - } - - // Back up until we come to a non-combining char, determine whether - // that char is a word char. - UBool prevCIsWord = false; - for (;;) { - if (UTEXT_GETNATIVEINDEX(fInputText) <= fLookStart) { - break; - } - UChar32 prevChar = UTEXT_PREVIOUS32(fInputText); - if (!(u_hasBinaryProperty(prevChar, UCHAR_GRAPHEME_EXTEND) - || u_charType(prevChar) == U_FORMAT_CHAR)) { - prevCIsWord = RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET].contains(prevChar); - break; - } - } - isBoundary = cIsWord ^ prevCIsWord; - return isBoundary; -} - -UBool RegexMatcher::isChunkWordBoundary(int32_t pos) { - UBool isBoundary = false; - UBool cIsWord = false; - - const UChar *inputBuf = fInputText->chunkContents; - - if (pos >= fLookLimit) { - fHitEnd = true; - } else { - // Determine whether char c at current position is a member of the word set of chars. - // If we're off the end of the string, behave as though we're not at a word char. - UChar32 c; - U16_GET(inputBuf, fLookStart, pos, fLookLimit, c); - if (u_hasBinaryProperty(c, UCHAR_GRAPHEME_EXTEND) || u_charType(c) == U_FORMAT_CHAR) { - // Current char is a combining one. Not a boundary. - return false; - } - cIsWord = RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET].contains(c); - } - - // Back up until we come to a non-combining char, determine whether - // that char is a word char. - UBool prevCIsWord = false; - for (;;) { - if (pos <= fLookStart) { - break; - } - UChar32 prevChar; - U16_PREV(inputBuf, fLookStart, pos, prevChar); - if (!(u_hasBinaryProperty(prevChar, UCHAR_GRAPHEME_EXTEND) - || u_charType(prevChar) == U_FORMAT_CHAR)) { - prevCIsWord = RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET].contains(prevChar); - break; - } - } - isBoundary = cIsWord ^ prevCIsWord; - return isBoundary; -} - -//-------------------------------------------------------------------------------- -// -// isUWordBoundary -// -// Test for a word boundary using RBBI word break. -// -// parameters: pos - the current position in the input buffer -// -//-------------------------------------------------------------------------------- -UBool RegexMatcher::isUWordBoundary(int64_t pos, UErrorCode &status) { - UBool returnVal = false; - -#if UCONFIG_NO_BREAK_ITERATION==0 - // Note: this point will never be reached if break iteration is configured out. - // Regex patterns that would require this function will fail to compile. - - // If we haven't yet created a break iterator for this matcher, do it now. - if (fWordBreakItr == nullptr) { - fWordBreakItr = BreakIterator::createWordInstance(Locale::getEnglish(), status); - if (U_FAILURE(status)) { - return false; - } - fWordBreakItr->setText(fInputText, status); - } - - // Note: zero width boundary tests like \b see through transparent region bounds, - // which is why fLookLimit is used here, rather than fActiveLimit. - if (pos >= fLookLimit) { - fHitEnd = true; - returnVal = true; // With Unicode word rules, only positions within the interior of "real" - // words are not boundaries. All non-word chars stand by themselves, - // with word boundaries on both sides. - } else { - returnVal = fWordBreakItr->isBoundary((int32_t)pos); - } -#endif - return returnVal; -} - - -int64_t RegexMatcher::followingGCBoundary(int64_t pos, UErrorCode &status) { - int64_t result = pos; - -#if UCONFIG_NO_BREAK_ITERATION==0 - // Note: this point will never be reached if break iteration is configured out. - // Regex patterns that would require this function will fail to compile. - - // If we haven't yet created a break iterator for this matcher, do it now. - if (fGCBreakItr == nullptr) { - fGCBreakItr = BreakIterator::createCharacterInstance(Locale::getEnglish(), status); - if (U_FAILURE(status)) { - return pos; - } - fGCBreakItr->setText(fInputText, status); - } - result = fGCBreakItr->following(pos); - if (result == BreakIterator::DONE) { - result = pos; - } -#endif - return result; -} - -//-------------------------------------------------------------------------------- -// -// IncrementTime This function is called once each TIMER_INITIAL_VALUE state -// saves. Increment the "time" counter, and call the -// user callback function if there is one installed. -// -// If the match operation needs to be aborted, either for a time-out -// or because the user callback asked for it, just set an error status. -// The engine will pick that up and stop in its outer loop. -// -//-------------------------------------------------------------------------------- -void RegexMatcher::IncrementTime(UErrorCode &status) { - fTickCounter = TIMER_INITIAL_VALUE; - fTime++; - if (fCallbackFn != NULL) { - if ((*fCallbackFn)(fCallbackContext, fTime) == false) { - status = U_REGEX_STOPPED_BY_CALLER; - return; - } - } - if (fTimeLimit > 0 && fTime >= fTimeLimit) { - status = U_REGEX_TIME_OUT; - } -} - -//-------------------------------------------------------------------------------- -// -// StateSave -// Make a new stack frame, initialized as a copy of the current stack frame. -// Set the pattern index in the original stack frame from the operand value -// in the opcode. Execution of the engine continues with the state in -// the newly created stack frame -// -// Note that reserveBlock() may grow the stack, resulting in the -// whole thing being relocated in memory. -// -// Parameters: -// fp The top frame pointer when called. At return, a new -// fame will be present -// savePatIdx An index into the compiled pattern. Goes into the original -// (not new) frame. If execution ever back-tracks out of the -// new frame, this will be where we continue from in the pattern. -// Return -// The new frame pointer. -// -//-------------------------------------------------------------------------------- -inline REStackFrame *RegexMatcher::StateSave(REStackFrame *fp, int64_t savePatIdx, UErrorCode &status) { - if (U_FAILURE(status)) { - return fp; - } - // push storage for a new frame. - int64_t *newFP = fStack->reserveBlock(fFrameSize, status); - if (U_FAILURE(status)) { - // Failure on attempted stack expansion. - // Stack function set some other error code, change it to a more - // specific one for regular expressions. - status = U_REGEX_STACK_OVERFLOW; - // We need to return a writable stack frame, so just return the - // previous frame. The match operation will stop quickly - // because of the error status, after which the frame will never - // be looked at again. - return fp; - } - fp = (REStackFrame *)(newFP - fFrameSize); // in case of realloc of stack. - - // New stack frame = copy of old top frame. - int64_t *source = (int64_t *)fp; - int64_t *dest = newFP; - for (;;) { - *dest++ = *source++; - if (source == newFP) { - break; - } - } - - fTickCounter--; - if (fTickCounter <= 0) { - IncrementTime(status); // Re-initializes fTickCounter - } - fp->fPatIdx = savePatIdx; - return (REStackFrame *)newFP; -} - -#if defined(REGEX_DEBUG) -namespace { -UnicodeString StringFromUText(UText *ut) { - UnicodeString result; - for (UChar32 c = utext_next32From(ut, 0); c != U_SENTINEL; c = UTEXT_NEXT32(ut)) { - result.append(c); - } - return result; -} -} -#endif // REGEX_DEBUG - - -//-------------------------------------------------------------------------------- -// -// MatchAt This is the actual matching engine. -// -// startIdx: begin matching a this index. -// toEnd: if true, match must extend to end of the input region -// -//-------------------------------------------------------------------------------- -void RegexMatcher::MatchAt(int64_t startIdx, UBool toEnd, UErrorCode &status) { - UBool isMatch = false; // True if the we have a match. - - int64_t backSearchIndex = U_INT64_MAX; // used after greedy single-character matches for searching backwards - - int32_t op; // Operation from the compiled pattern, split into - int32_t opType; // the opcode - int32_t opValue; // and the operand value. - -#ifdef REGEX_RUN_DEBUG - if (fTraceDebug) { - printf("MatchAt(startIdx=%ld)\n", startIdx); - printf("Original Pattern: \"%s\"\n", CStr(StringFromUText(fPattern->fPattern))()); - printf("Input String: \"%s\"\n\n", CStr(StringFromUText(fInputText))()); - } -#endif - - if (U_FAILURE(status)) { - return; - } - - // Cache frequently referenced items from the compiled pattern - // - int64_t *pat = fPattern->fCompiledPat->getBuffer(); - - const UChar *litText = fPattern->fLiteralText.getBuffer(); - UVector *fSets = fPattern->fSets; - - fFrameSize = fPattern->fFrameSize; - REStackFrame *fp = resetStack(); - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return; - } - - fp->fPatIdx = 0; - fp->fInputIdx = startIdx; - - // Zero out the pattern's static data - int32_t i; - for (i = 0; ifDataSize; i++) { - fData[i] = 0; - } - - // - // Main loop for interpreting the compiled pattern. - // One iteration of the loop per pattern operation performed. - // - for (;;) { - op = (int32_t)pat[fp->fPatIdx]; - opType = URX_TYPE(op); - opValue = URX_VAL(op); -#ifdef REGEX_RUN_DEBUG - if (fTraceDebug) { - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - printf("inputIdx=%ld inputChar=%x sp=%3ld activeLimit=%ld ", fp->fInputIdx, - UTEXT_CURRENT32(fInputText), (int64_t *)fp-fStack->getBuffer(), fActiveLimit); - fPattern->dumpOp(fp->fPatIdx); - } -#endif - fp->fPatIdx++; - - switch (opType) { - - - case URX_NOP: - break; - - - case URX_BACKTRACK: - // Force a backtrack. In some circumstances, the pattern compiler - // will notice that the pattern can't possibly match anything, and will - // emit one of these at that point. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - - - case URX_ONECHAR: - if (fp->fInputIdx < fActiveLimit) { - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - UChar32 c = UTEXT_NEXT32(fInputText); - if (c == opValue) { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - break; - } - } else { - fHitEnd = true; - } - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - - - case URX_STRING: - { - // Test input against a literal string. - // Strings require two slots in the compiled pattern, one for the - // offset to the string text, and one for the length. - - int32_t stringStartIdx = opValue; - op = (int32_t)pat[fp->fPatIdx]; // Fetch the second operand - fp->fPatIdx++; - opType = URX_TYPE(op); - int32_t stringLen = URX_VAL(op); - U_ASSERT(opType == URX_STRING_LEN); - U_ASSERT(stringLen >= 2); - - const UChar *patternString = litText+stringStartIdx; - int32_t patternStringIndex = 0; - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - UChar32 inputChar; - UChar32 patternChar; - UBool success = true; - while (patternStringIndex < stringLen) { - if (UTEXT_GETNATIVEINDEX(fInputText) >= fActiveLimit) { - success = false; - fHitEnd = true; - break; - } - inputChar = UTEXT_NEXT32(fInputText); - U16_NEXT(patternString, patternStringIndex, stringLen, patternChar); - if (patternChar != inputChar) { - success = false; - break; - } - } - - if (success) { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_STATE_SAVE: - fp = StateSave(fp, opValue, status); - break; - - - case URX_END: - // The match loop will exit via this path on a successful match, - // when we reach the end of the pattern. - if (toEnd && fp->fInputIdx != fActiveLimit) { - // The pattern matched, but not to the end of input. Try some more. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - isMatch = true; - goto breakFromLoop; - - // Start and End Capture stack frame variables are laid out out like this: - // fp->fExtra[opValue] - The start of a completed capture group - // opValue+1 - The end of a completed capture group - // opValue+2 - the start of a capture group whose end - // has not yet been reached (and might not ever be). - case URX_START_CAPTURE: - U_ASSERT(opValue >= 0 && opValue < fFrameSize-3); - fp->fExtra[opValue+2] = fp->fInputIdx; - break; - - - case URX_END_CAPTURE: - U_ASSERT(opValue >= 0 && opValue < fFrameSize-3); - U_ASSERT(fp->fExtra[opValue+2] >= 0); // Start pos for this group must be set. - fp->fExtra[opValue] = fp->fExtra[opValue+2]; // Tentative start becomes real. - fp->fExtra[opValue+1] = fp->fInputIdx; // End position - U_ASSERT(fp->fExtra[opValue] <= fp->fExtra[opValue+1]); - break; - - - case URX_DOLLAR: // $, test for End of line - // or for position before new line at end of input - { - if (fp->fInputIdx >= fAnchorLimit) { - // We really are at the end of input. Success. - fHitEnd = true; - fRequireEnd = true; - break; - } - - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - - // If we are positioned just before a new-line that is located at the - // end of input, succeed. - UChar32 c = UTEXT_NEXT32(fInputText); - if (UTEXT_GETNATIVEINDEX(fInputText) >= fAnchorLimit) { - if (isLineTerminator(c)) { - // If not in the middle of a CR/LF sequence - if ( !(c==0x0a && fp->fInputIdx>fAnchorStart && ((void)UTEXT_PREVIOUS32(fInputText), UTEXT_PREVIOUS32(fInputText))==0x0d)) { - // At new-line at end of input. Success - fHitEnd = true; - fRequireEnd = true; - - break; - } - } - } else { - UChar32 nextC = UTEXT_NEXT32(fInputText); - if (c == 0x0d && nextC == 0x0a && UTEXT_GETNATIVEINDEX(fInputText) >= fAnchorLimit) { - fHitEnd = true; - fRequireEnd = true; - break; // At CR/LF at end of input. Success - } - } - - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_DOLLAR_D: // $, test for End of Line, in UNIX_LINES mode. - if (fp->fInputIdx >= fAnchorLimit) { - // Off the end of input. Success. - fHitEnd = true; - fRequireEnd = true; - break; - } else { - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - UChar32 c = UTEXT_NEXT32(fInputText); - // Either at the last character of input, or off the end. - if (c == 0x0a && UTEXT_GETNATIVEINDEX(fInputText) == fAnchorLimit) { - fHitEnd = true; - fRequireEnd = true; - break; - } - } - - // Not at end of input. Back-track out. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - - - case URX_DOLLAR_M: // $, test for End of line in multi-line mode - { - if (fp->fInputIdx >= fAnchorLimit) { - // We really are at the end of input. Success. - fHitEnd = true; - fRequireEnd = true; - break; - } - // If we are positioned just before a new-line, succeed. - // It makes no difference where the new-line is within the input. - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - UChar32 c = UTEXT_CURRENT32(fInputText); - if (isLineTerminator(c)) { - // At a line end, except for the odd chance of being in the middle of a CR/LF sequence - // In multi-line mode, hitting a new-line just before the end of input does not - // set the hitEnd or requireEnd flags - if ( !(c==0x0a && fp->fInputIdx>fAnchorStart && UTEXT_PREVIOUS32(fInputText)==0x0d)) { - break; - } - } - // not at a new line. Fail. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_DOLLAR_MD: // $, test for End of line in multi-line and UNIX_LINES mode - { - if (fp->fInputIdx >= fAnchorLimit) { - // We really are at the end of input. Success. - fHitEnd = true; - fRequireEnd = true; // Java set requireEnd in this case, even though - break; // adding a new-line would not lose the match. - } - // If we are not positioned just before a new-line, the test fails; backtrack out. - // It makes no difference where the new-line is within the input. - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - if (UTEXT_CURRENT32(fInputText) != 0x0a) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_CARET: // ^, test for start of line - if (fp->fInputIdx != fAnchorStart) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_CARET_M: // ^, test for start of line in mulit-line mode - { - if (fp->fInputIdx == fAnchorStart) { - // We are at the start input. Success. - break; - } - // Check whether character just before the current pos is a new-line - // unless we are at the end of input - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - UChar32 c = UTEXT_PREVIOUS32(fInputText); - if ((fp->fInputIdx < fAnchorLimit) && isLineTerminator(c)) { - // It's a new-line. ^ is true. Success. - // TODO: what should be done with positions between a CR and LF? - break; - } - // Not at the start of a line. Fail. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_CARET_M_UNIX: // ^, test for start of line in mulit-line + Unix-line mode - { - U_ASSERT(fp->fInputIdx >= fAnchorStart); - if (fp->fInputIdx <= fAnchorStart) { - // We are at the start input. Success. - break; - } - // Check whether character just before the current pos is a new-line - U_ASSERT(fp->fInputIdx <= fAnchorLimit); - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - UChar32 c = UTEXT_PREVIOUS32(fInputText); - if (c != 0x0a) { - // Not at the start of a line. Back-track out. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - case URX_BACKSLASH_B: // Test for word boundaries - { - UBool success = isWordBoundary(fp->fInputIdx); - success ^= (UBool)(opValue != 0); // flip sense for \B - if (!success) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_BACKSLASH_BU: // Test for word boundaries, Unicode-style - { - UBool success = isUWordBoundary(fp->fInputIdx, status); - success ^= (UBool)(opValue != 0); // flip sense for \B - if (!success) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_BACKSLASH_D: // Test for decimal digit - { - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - - UChar32 c = UTEXT_NEXT32(fInputText); - int8_t ctype = u_charType(c); // TODO: make a unicode set for this. Will be faster. - UBool success = (ctype == U_DECIMAL_DIGIT_NUMBER); - success ^= (UBool)(opValue != 0); // flip sense for \D - if (success) { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_BACKSLASH_G: // Test for position at end of previous match - if (!((fMatch && fp->fInputIdx==fMatchEnd) || (fMatch==false && fp->fInputIdx==fActiveStart))) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_BACKSLASH_H: // Test for \h, horizontal white space. - { - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - UChar32 c = UTEXT_NEXT32(fInputText); - int8_t ctype = u_charType(c); - UBool success = (ctype == U_SPACE_SEPARATOR || c == 9); // SPACE_SEPARATOR || TAB - success ^= (UBool)(opValue != 0); // flip sense for \H - if (success) { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_BACKSLASH_R: // Test for \R, any line break sequence. - { - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - UChar32 c = UTEXT_NEXT32(fInputText); - if (isLineTerminator(c)) { - if (c == 0x0d && utext_current32(fInputText) == 0x0a) { - utext_next32(fInputText); - } - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_BACKSLASH_V: // \v, any single line ending character. - { - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - UChar32 c = UTEXT_NEXT32(fInputText); - UBool success = isLineTerminator(c); - success ^= (UBool)(opValue != 0); // flip sense for \V - if (success) { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_BACKSLASH_X: - // Match a Grapheme, as defined by Unicode UAX 29. - - // Fail if at end of input - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - fp->fInputIdx = followingGCBoundary(fp->fInputIdx, status); - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp->fInputIdx = fActiveLimit; - } - break; - - - case URX_BACKSLASH_Z: // Test for end of Input - if (fp->fInputIdx < fAnchorLimit) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } else { - fHitEnd = true; - fRequireEnd = true; - } - break; - - - - case URX_STATIC_SETREF: - { - // Test input character against one of the predefined sets - // (Word Characters, for example) - // The high bit of the op value is a flag for the match polarity. - // 0: success if input char is in set. - // 1: success if input char is not in set. - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - UBool success = ((opValue & URX_NEG_SET) == URX_NEG_SET); - opValue &= ~URX_NEG_SET; - U_ASSERT(opValue > 0 && opValue < URX_LAST_SET); - - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - UChar32 c = UTEXT_NEXT32(fInputText); - if (c < 256) { - Regex8BitSet &s8 = RegexStaticSets::gStaticSets->fPropSets8[opValue]; - if (s8.contains(c)) { - success = !success; - } - } else { - const UnicodeSet &s = RegexStaticSets::gStaticSets->fPropSets[opValue]; - if (s.contains(c)) { - success = !success; - } - } - if (success) { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - } else { - // the character wasn't in the set. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_STAT_SETREF_N: - { - // Test input character for NOT being a member of one of - // the predefined sets (Word Characters, for example) - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - U_ASSERT(opValue > 0 && opValue < URX_LAST_SET); - - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - - UChar32 c = UTEXT_NEXT32(fInputText); - if (c < 256) { - Regex8BitSet &s8 = RegexStaticSets::gStaticSets->fPropSets8[opValue]; - if (s8.contains(c) == false) { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - break; - } - } else { - const UnicodeSet &s = RegexStaticSets::gStaticSets->fPropSets[opValue]; - if (s.contains(c) == false) { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - break; - } - } - // the character wasn't in the set. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_SETREF: - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } else { - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - - // There is input left. Pick up one char and test it for set membership. - UChar32 c = UTEXT_NEXT32(fInputText); - U_ASSERT(opValue > 0 && opValue < fSets->size()); - if (c<256) { - Regex8BitSet *s8 = &fPattern->fSets8[opValue]; - if (s8->contains(c)) { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - break; - } - } else { - UnicodeSet *s = (UnicodeSet *)fSets->elementAt(opValue); - if (s->contains(c)) { - // The character is in the set. A Match. - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - break; - } - } - - // the character wasn't in the set. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_DOTANY: - { - // . matches anything, but stops at end-of-line. - if (fp->fInputIdx >= fActiveLimit) { - // At end of input. Match failed. Backtrack out. - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - - // There is input left. Advance over one char, unless we've hit end-of-line - UChar32 c = UTEXT_NEXT32(fInputText); - if (isLineTerminator(c)) { - // End of line in normal mode. . does not match. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - } - break; - - - case URX_DOTANY_ALL: - { - // ., in dot-matches-all (including new lines) mode - if (fp->fInputIdx >= fActiveLimit) { - // At end of input. Match failed. Backtrack out. - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - - // There is input left. Advance over one char, except if we are - // at a cr/lf, advance over both of them. - UChar32 c; - c = UTEXT_NEXT32(fInputText); - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - if (c==0x0d && fp->fInputIdx < fActiveLimit) { - // In the case of a CR/LF, we need to advance over both. - UChar32 nextc = UTEXT_CURRENT32(fInputText); - if (nextc == 0x0a) { - (void)UTEXT_NEXT32(fInputText); - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - } - } - } - break; - - - case URX_DOTANY_UNIX: - { - // '.' operator, matches all, but stops at end-of-line. - // UNIX_LINES mode, so 0x0a is the only recognized line ending. - if (fp->fInputIdx >= fActiveLimit) { - // At end of input. Match failed. Backtrack out. - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - - // There is input left. Advance over one char, unless we've hit end-of-line - UChar32 c = UTEXT_NEXT32(fInputText); - if (c == 0x0a) { - // End of line in normal mode. '.' does not match the \n - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } else { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - } - } - break; - - - case URX_JMP: - fp->fPatIdx = opValue; - break; - - case URX_FAIL: - isMatch = false; - goto breakFromLoop; - - case URX_JMP_SAV: - U_ASSERT(opValue < fPattern->fCompiledPat->size()); - fp = StateSave(fp, fp->fPatIdx, status); // State save to loc following current - fp->fPatIdx = opValue; // Then JMP. - break; - - case URX_JMP_SAV_X: - // This opcode is used with (x)+, when x can match a zero length string. - // Same as JMP_SAV, except conditional on the match having made forward progress. - // Destination of the JMP must be a URX_STO_INP_LOC, from which we get the - // data address of the input position at the start of the loop. - { - U_ASSERT(opValue > 0 && opValue < fPattern->fCompiledPat->size()); - int32_t stoOp = (int32_t)pat[opValue-1]; - U_ASSERT(URX_TYPE(stoOp) == URX_STO_INP_LOC); - int32_t frameLoc = URX_VAL(stoOp); - U_ASSERT(frameLoc >= 0 && frameLoc < fFrameSize); - int64_t prevInputIdx = fp->fExtra[frameLoc]; - U_ASSERT(prevInputIdx <= fp->fInputIdx); - if (prevInputIdx < fp->fInputIdx) { - // The match did make progress. Repeat the loop. - fp = StateSave(fp, fp->fPatIdx, status); // State save to loc following current - fp->fPatIdx = opValue; - fp->fExtra[frameLoc] = fp->fInputIdx; - } - // If the input position did not advance, we do nothing here, - // execution will fall out of the loop. - } - break; - - case URX_CTR_INIT: - { - U_ASSERT(opValue >= 0 && opValue < fFrameSize-2); - fp->fExtra[opValue] = 0; // Set the loop counter variable to zero - - // Pick up the three extra operands that CTR_INIT has, and - // skip the pattern location counter past - int32_t instrOperandLoc = (int32_t)fp->fPatIdx; - fp->fPatIdx += 3; - int32_t loopLoc = URX_VAL(pat[instrOperandLoc]); - int32_t minCount = (int32_t)pat[instrOperandLoc+1]; - int32_t maxCount = (int32_t)pat[instrOperandLoc+2]; - U_ASSERT(minCount>=0); - U_ASSERT(maxCount>=minCount || maxCount==-1); - U_ASSERT(loopLoc>=fp->fPatIdx); - - if (minCount == 0) { - fp = StateSave(fp, loopLoc+1, status); - } - if (maxCount == -1) { - fp->fExtra[opValue+1] = fp->fInputIdx; // For loop breaking. - } else if (maxCount == 0) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - case URX_CTR_LOOP: - { - U_ASSERT(opValue>0 && opValue < fp->fPatIdx-2); - int32_t initOp = (int32_t)pat[opValue]; - U_ASSERT(URX_TYPE(initOp) == URX_CTR_INIT); - int64_t *pCounter = &fp->fExtra[URX_VAL(initOp)]; - int32_t minCount = (int32_t)pat[opValue+2]; - int32_t maxCount = (int32_t)pat[opValue+3]; - (*pCounter)++; - if ((uint64_t)*pCounter >= (uint32_t)maxCount && maxCount != -1) { - U_ASSERT(*pCounter == maxCount); - break; - } - if (*pCounter >= minCount) { - if (maxCount == -1) { - // Loop has no hard upper bound. - // Check that it is progressing through the input, break if it is not. - int64_t *pLastInputIdx = &fp->fExtra[URX_VAL(initOp) + 1]; - if (fp->fInputIdx == *pLastInputIdx) { - break; - } else { - *pLastInputIdx = fp->fInputIdx; - } - } - fp = StateSave(fp, fp->fPatIdx, status); - } else { - // Increment time-out counter. (StateSave() does it if count >= minCount) - fTickCounter--; - if (fTickCounter <= 0) { - IncrementTime(status); // Re-initializes fTickCounter - } - } - - fp->fPatIdx = opValue + 4; // Loop back. - } - break; - - case URX_CTR_INIT_NG: - { - // Initialize a non-greedy loop - U_ASSERT(opValue >= 0 && opValue < fFrameSize-2); - fp->fExtra[opValue] = 0; // Set the loop counter variable to zero - - // Pick up the three extra operands that CTR_INIT_NG has, and - // skip the pattern location counter past - int32_t instrOperandLoc = (int32_t)fp->fPatIdx; - fp->fPatIdx += 3; - int32_t loopLoc = URX_VAL(pat[instrOperandLoc]); - int32_t minCount = (int32_t)pat[instrOperandLoc+1]; - int32_t maxCount = (int32_t)pat[instrOperandLoc+2]; - U_ASSERT(minCount>=0); - U_ASSERT(maxCount>=minCount || maxCount==-1); - U_ASSERT(loopLoc>fp->fPatIdx); - if (maxCount == -1) { - fp->fExtra[opValue+1] = fp->fInputIdx; // Save initial input index for loop breaking. - } - - if (minCount == 0) { - if (maxCount != 0) { - fp = StateSave(fp, fp->fPatIdx, status); - } - fp->fPatIdx = loopLoc+1; // Continue with stuff after repeated block - } - } - break; - - case URX_CTR_LOOP_NG: - { - // Non-greedy {min, max} loops - U_ASSERT(opValue>0 && opValue < fp->fPatIdx-2); - int32_t initOp = (int32_t)pat[opValue]; - U_ASSERT(URX_TYPE(initOp) == URX_CTR_INIT_NG); - int64_t *pCounter = &fp->fExtra[URX_VAL(initOp)]; - int32_t minCount = (int32_t)pat[opValue+2]; - int32_t maxCount = (int32_t)pat[opValue+3]; - - (*pCounter)++; - if ((uint64_t)*pCounter >= (uint32_t)maxCount && maxCount != -1) { - // The loop has matched the maximum permitted number of times. - // Break out of here with no action. Matching will - // continue with the following pattern. - U_ASSERT(*pCounter == maxCount); - break; - } - - if (*pCounter < minCount) { - // We haven't met the minimum number of matches yet. - // Loop back for another one. - fp->fPatIdx = opValue + 4; // Loop back. - // Increment time-out counter. (StateSave() does it if count >= minCount) - fTickCounter--; - if (fTickCounter <= 0) { - IncrementTime(status); // Re-initializes fTickCounter - } - } else { - // We do have the minimum number of matches. - - // If there is no upper bound on the loop iterations, check that the input index - // is progressing, and stop the loop if it is not. - if (maxCount == -1) { - int64_t *pLastInputIdx = &fp->fExtra[URX_VAL(initOp) + 1]; - if (fp->fInputIdx == *pLastInputIdx) { - break; - } - *pLastInputIdx = fp->fInputIdx; - } - - // Loop Continuation: we will fall into the pattern following the loop - // (non-greedy, don't execute loop body first), but first do - // a state save to the top of the loop, so that a match failure - // in the following pattern will try another iteration of the loop. - fp = StateSave(fp, opValue + 4, status); - } - } - break; - - case URX_STO_SP: - U_ASSERT(opValue >= 0 && opValue < fPattern->fDataSize); - fData[opValue] = fStack->size(); - break; - - case URX_LD_SP: - { - U_ASSERT(opValue >= 0 && opValue < fPattern->fDataSize); - int32_t newStackSize = (int32_t)fData[opValue]; - U_ASSERT(newStackSize <= fStack->size()); - int64_t *newFP = fStack->getBuffer() + newStackSize - fFrameSize; - if (newFP == (int64_t *)fp) { - break; - } - int32_t j; - for (j=0; jsetSize(newStackSize); - } - break; - - case URX_BACKREF: - { - U_ASSERT(opValue < fFrameSize); - int64_t groupStartIdx = fp->fExtra[opValue]; - int64_t groupEndIdx = fp->fExtra[opValue+1]; - U_ASSERT(groupStartIdx <= groupEndIdx); - if (groupStartIdx < 0) { - // This capture group has not participated in the match thus far, - fp = (REStackFrame *)fStack->popFrame(fFrameSize); // FAIL, no match. - break; - } - UTEXT_SETNATIVEINDEX(fAltInputText, groupStartIdx); - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - - // Note: if the capture group match was of an empty string the backref - // match succeeds. Verified by testing: Perl matches succeed - // in this case, so we do too. - - UBool success = true; - for (;;) { - if (utext_getNativeIndex(fAltInputText) >= groupEndIdx) { - success = true; - break; - } - if (utext_getNativeIndex(fInputText) >= fActiveLimit) { - success = false; - fHitEnd = true; - break; - } - UChar32 captureGroupChar = utext_next32(fAltInputText); - UChar32 inputChar = utext_next32(fInputText); - if (inputChar != captureGroupChar) { - success = false; - break; - } - } - - if (success) { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - - case URX_BACKREF_I: - { - U_ASSERT(opValue < fFrameSize); - int64_t groupStartIdx = fp->fExtra[opValue]; - int64_t groupEndIdx = fp->fExtra[opValue+1]; - U_ASSERT(groupStartIdx <= groupEndIdx); - if (groupStartIdx < 0) { - // This capture group has not participated in the match thus far, - fp = (REStackFrame *)fStack->popFrame(fFrameSize); // FAIL, no match. - break; - } - utext_setNativeIndex(fAltInputText, groupStartIdx); - utext_setNativeIndex(fInputText, fp->fInputIdx); - CaseFoldingUTextIterator captureGroupItr(*fAltInputText); - CaseFoldingUTextIterator inputItr(*fInputText); - - // Note: if the capture group match was of an empty string the backref - // match succeeds. Verified by testing: Perl matches succeed - // in this case, so we do too. - - UBool success = true; - for (;;) { - if (!captureGroupItr.inExpansion() && utext_getNativeIndex(fAltInputText) >= groupEndIdx) { - success = true; - break; - } - if (!inputItr.inExpansion() && utext_getNativeIndex(fInputText) >= fActiveLimit) { - success = false; - fHitEnd = true; - break; - } - UChar32 captureGroupChar = captureGroupItr.next(); - UChar32 inputChar = inputItr.next(); - if (inputChar != captureGroupChar) { - success = false; - break; - } - } - - if (success && inputItr.inExpansion()) { - // We obtained a match by consuming part of a string obtained from - // case-folding a single code point of the input text. - // This does not count as an overall match. - success = false; - } - - if (success) { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - - } - break; - - case URX_STO_INP_LOC: - { - U_ASSERT(opValue >= 0 && opValue < fFrameSize); - fp->fExtra[opValue] = fp->fInputIdx; - } - break; - - case URX_JMPX: - { - int32_t instrOperandLoc = (int32_t)fp->fPatIdx; - fp->fPatIdx += 1; - int32_t dataLoc = URX_VAL(pat[instrOperandLoc]); - U_ASSERT(dataLoc >= 0 && dataLoc < fFrameSize); - int64_t savedInputIdx = fp->fExtra[dataLoc]; - U_ASSERT(savedInputIdx <= fp->fInputIdx); - if (savedInputIdx < fp->fInputIdx) { - fp->fPatIdx = opValue; // JMP - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); // FAIL, no progress in loop. - } - } - break; - - case URX_LA_START: - { - // Entering a look around block. - // Save Stack Ptr, Input Pos. - U_ASSERT(opValue>=0 && opValue+3fDataSize); - fData[opValue] = fStack->size(); - fData[opValue+1] = fp->fInputIdx; - fData[opValue+2] = fActiveStart; - fData[opValue+3] = fActiveLimit; - fActiveStart = fLookStart; // Set the match region change for - fActiveLimit = fLookLimit; // transparent bounds. - } - break; - - case URX_LA_END: - { - // Leaving a look-ahead block. - // restore Stack Ptr, Input Pos to positions they had on entry to block. - U_ASSERT(opValue>=0 && opValue+3fDataSize); - int32_t stackSize = fStack->size(); - int32_t newStackSize =(int32_t)fData[opValue]; - U_ASSERT(stackSize >= newStackSize); - if (stackSize > newStackSize) { - // Copy the current top frame back to the new (cut back) top frame. - // This makes the capture groups from within the look-ahead - // expression available. - int64_t *newFP = fStack->getBuffer() + newStackSize - fFrameSize; - int32_t j; - for (j=0; jsetSize(newStackSize); - } - fp->fInputIdx = fData[opValue+1]; - - // Restore the active region bounds in the input string; they may have - // been changed because of transparent bounds on a Region. - fActiveStart = fData[opValue+2]; - fActiveLimit = fData[opValue+3]; - U_ASSERT(fActiveStart >= 0); - U_ASSERT(fActiveLimit <= fInputLength); - } - break; - - case URX_ONECHAR_I: - // Case insensitive one char. The char from the pattern is already case folded. - // Input text is not, but case folding the input can not reduce two or more code - // points to one. - if (fp->fInputIdx < fActiveLimit) { - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - - UChar32 c = UTEXT_NEXT32(fInputText); - if (u_foldCase(c, U_FOLD_CASE_DEFAULT) == opValue) { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - break; - } - } else { - fHitEnd = true; - } - - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - - case URX_STRING_I: - { - // Case-insensitive test input against a literal string. - // Strings require two slots in the compiled pattern, one for the - // offset to the string text, and one for the length. - // The compiled string has already been case folded. - { - const UChar *patternString = litText + opValue; - int32_t patternStringIdx = 0; - - op = (int32_t)pat[fp->fPatIdx]; - fp->fPatIdx++; - opType = URX_TYPE(op); - opValue = URX_VAL(op); - U_ASSERT(opType == URX_STRING_LEN); - int32_t patternStringLen = opValue; // Length of the string from the pattern. - - - UChar32 cPattern; - UChar32 cText; - UBool success = true; - - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - CaseFoldingUTextIterator inputIterator(*fInputText); - while (patternStringIdx < patternStringLen) { - if (!inputIterator.inExpansion() && UTEXT_GETNATIVEINDEX(fInputText) >= fActiveLimit) { - success = false; - fHitEnd = true; - break; - } - U16_NEXT(patternString, patternStringIdx, patternStringLen, cPattern); - cText = inputIterator.next(); - if (cText != cPattern) { - success = false; - break; - } - } - if (inputIterator.inExpansion()) { - success = false; - } - - if (success) { - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - } - break; - - case URX_LB_START: - { - // Entering a look-behind block. - // Save Stack Ptr, Input Pos and active input region. - // TODO: implement transparent bounds. Ticket #6067 - U_ASSERT(opValue>=0 && opValue+4fDataSize); - fData[opValue] = fStack->size(); - fData[opValue+1] = fp->fInputIdx; - // Save input string length, then reset to pin any matches to end at - // the current position. - fData[opValue+2] = fActiveStart; - fData[opValue+3] = fActiveLimit; - fActiveStart = fRegionStart; - fActiveLimit = fp->fInputIdx; - // Init the variable containing the start index for attempted matches. - fData[opValue+4] = -1; - } - break; - - - case URX_LB_CONT: - { - // Positive Look-Behind, at top of loop checking for matches of LB expression - // at all possible input starting positions. - - // Fetch the min and max possible match lengths. They are the operands - // of this op in the pattern. - int32_t minML = (int32_t)pat[fp->fPatIdx++]; - int32_t maxML = (int32_t)pat[fp->fPatIdx++]; - if (!UTEXT_USES_U16(fInputText)) { - // utf-8 fix to maximum match length. The pattern compiler assumes utf-16. - // The max length need not be exact; it just needs to be >= actual maximum. - maxML *= 3; - } - U_ASSERT(minML <= maxML); - U_ASSERT(minML >= 0); - - // Fetch (from data) the last input index where a match was attempted. - U_ASSERT(opValue>=0 && opValue+4fDataSize); - int64_t &lbStartIdx = fData[opValue+4]; - if (lbStartIdx < 0) { - // First time through loop. - lbStartIdx = fp->fInputIdx - minML; - if (lbStartIdx > 0) { - // move index to a code point boundary, if it's not on one already. - UTEXT_SETNATIVEINDEX(fInputText, lbStartIdx); - lbStartIdx = UTEXT_GETNATIVEINDEX(fInputText); - } - } else { - // 2nd through nth time through the loop. - // Back up start position for match by one. - if (lbStartIdx == 0) { - (lbStartIdx)--; - } else { - UTEXT_SETNATIVEINDEX(fInputText, lbStartIdx); - (void)UTEXT_PREVIOUS32(fInputText); - lbStartIdx = UTEXT_GETNATIVEINDEX(fInputText); - } - } - - if (lbStartIdx < 0 || lbStartIdx < fp->fInputIdx - maxML) { - // We have tried all potential match starting points without - // getting a match. Backtrack out, and out of the - // Look Behind altogether. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - fActiveStart = fData[opValue+2]; - fActiveLimit = fData[opValue+3]; - U_ASSERT(fActiveStart >= 0); - U_ASSERT(fActiveLimit <= fInputLength); - break; - } - - // Save state to this URX_LB_CONT op, so failure to match will repeat the loop. - // (successful match will fall off the end of the loop.) - fp = StateSave(fp, fp->fPatIdx-3, status); - fp->fInputIdx = lbStartIdx; - } - break; - - case URX_LB_END: - // End of a look-behind block, after a successful match. - { - U_ASSERT(opValue>=0 && opValue+4fDataSize); - if (fp->fInputIdx != fActiveLimit) { - // The look-behind expression matched, but the match did not - // extend all the way to the point that we are looking behind from. - // FAIL out of here, which will take us back to the LB_CONT, which - // will retry the match starting at another position or fail - // the look-behind altogether, whichever is appropriate. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - // Look-behind match is good. Restore the original input string region, - // which had been truncated to pin the end of the lookbehind match to the - // position being looked-behind. - fActiveStart = fData[opValue+2]; - fActiveLimit = fData[opValue+3]; - U_ASSERT(fActiveStart >= 0); - U_ASSERT(fActiveLimit <= fInputLength); - } - break; - - - case URX_LBN_CONT: - { - // Negative Look-Behind, at top of loop checking for matches of LB expression - // at all possible input starting positions. - - // Fetch the extra parameters of this op. - int32_t minML = (int32_t)pat[fp->fPatIdx++]; - int32_t maxML = (int32_t)pat[fp->fPatIdx++]; - if (!UTEXT_USES_U16(fInputText)) { - // utf-8 fix to maximum match length. The pattern compiler assumes utf-16. - // The max length need not be exact; it just needs to be >= actual maximum. - maxML *= 3; - } - int32_t continueLoc = (int32_t)pat[fp->fPatIdx++]; - continueLoc = URX_VAL(continueLoc); - U_ASSERT(minML <= maxML); - U_ASSERT(minML >= 0); - U_ASSERT(continueLoc > fp->fPatIdx); - - // Fetch (from data) the last input index where a match was attempted. - U_ASSERT(opValue>=0 && opValue+4fDataSize); - int64_t &lbStartIdx = fData[opValue+4]; - if (lbStartIdx < 0) { - // First time through loop. - lbStartIdx = fp->fInputIdx - minML; - if (lbStartIdx > 0) { - // move index to a code point boundary, if it's not on one already. - UTEXT_SETNATIVEINDEX(fInputText, lbStartIdx); - lbStartIdx = UTEXT_GETNATIVEINDEX(fInputText); - } - } else { - // 2nd through nth time through the loop. - // Back up start position for match by one. - if (lbStartIdx == 0) { - (lbStartIdx)--; - } else { - UTEXT_SETNATIVEINDEX(fInputText, lbStartIdx); - (void)UTEXT_PREVIOUS32(fInputText); - lbStartIdx = UTEXT_GETNATIVEINDEX(fInputText); - } - } - - if (lbStartIdx < 0 || lbStartIdx < fp->fInputIdx - maxML) { - // We have tried all potential match starting points without - // getting a match, which means that the negative lookbehind as - // a whole has succeeded. Jump forward to the continue location - fActiveStart = fData[opValue+2]; - fActiveLimit = fData[opValue+3]; - U_ASSERT(fActiveStart >= 0); - U_ASSERT(fActiveLimit <= fInputLength); - fp->fPatIdx = continueLoc; - break; - } - - // Save state to this URX_LB_CONT op, so failure to match will repeat the loop. - // (successful match will cause a FAIL out of the loop altogether.) - fp = StateSave(fp, fp->fPatIdx-4, status); - fp->fInputIdx = lbStartIdx; - } - break; - - case URX_LBN_END: - // End of a negative look-behind block, after a successful match. - { - U_ASSERT(opValue>=0 && opValue+4fDataSize); - if (fp->fInputIdx != fActiveLimit) { - // The look-behind expression matched, but the match did not - // extend all the way to the point that we are looking behind from. - // FAIL out of here, which will take us back to the LB_CONT, which - // will retry the match starting at another position or succeed - // the look-behind altogether, whichever is appropriate. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - // Look-behind expression matched, which means look-behind test as - // a whole Fails - - // Restore the original input string length, which had been truncated - // inorder to pin the end of the lookbehind match - // to the position being looked-behind. - fActiveStart = fData[opValue+2]; - fActiveLimit = fData[opValue+3]; - U_ASSERT(fActiveStart >= 0); - U_ASSERT(fActiveLimit <= fInputLength); - - // Restore original stack position, discarding any state saved - // by the successful pattern match. - U_ASSERT(opValue>=0 && opValue+1fDataSize); - int32_t newStackSize = (int32_t)fData[opValue]; - U_ASSERT(fStack->size() > newStackSize); - fStack->setSize(newStackSize); - - // FAIL, which will take control back to someplace - // prior to entering the look-behind test. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_LOOP_SR_I: - // Loop Initialization for the optimized implementation of - // [some character set]* - // This op scans through all matching input. - // The following LOOP_C op emulates stack unwinding if the following pattern fails. - { - U_ASSERT(opValue > 0 && opValue < fSets->size()); - Regex8BitSet *s8 = &fPattern->fSets8[opValue]; - UnicodeSet *s = (UnicodeSet *)fSets->elementAt(opValue); - - // Loop through input, until either the input is exhausted or - // we reach a character that is not a member of the set. - int64_t ix = fp->fInputIdx; - UTEXT_SETNATIVEINDEX(fInputText, ix); - for (;;) { - if (ix >= fActiveLimit) { - fHitEnd = true; - break; - } - UChar32 c = UTEXT_NEXT32(fInputText); - if (c<256) { - if (s8->contains(c) == false) { - break; - } - } else { - if (s->contains(c) == false) { - break; - } - } - ix = UTEXT_GETNATIVEINDEX(fInputText); - } - - // If there were no matching characters, skip over the loop altogether. - // The loop doesn't run at all, a * op always succeeds. - if (ix == fp->fInputIdx) { - fp->fPatIdx++; // skip the URX_LOOP_C op. - break; - } - - // Peek ahead in the compiled pattern, to the URX_LOOP_C that - // must follow. It's operand is the stack location - // that holds the starting input index for the match of this [set]* - int32_t loopcOp = (int32_t)pat[fp->fPatIdx]; - U_ASSERT(URX_TYPE(loopcOp) == URX_LOOP_C); - int32_t stackLoc = URX_VAL(loopcOp); - U_ASSERT(stackLoc >= 0 && stackLoc < fFrameSize); - fp->fExtra[stackLoc] = fp->fInputIdx; - fp->fInputIdx = ix; - - // Save State to the URX_LOOP_C op that follows this one, - // so that match failures in the following code will return to there. - // Then bump the pattern idx so the LOOP_C is skipped on the way out of here. - fp = StateSave(fp, fp->fPatIdx, status); - fp->fPatIdx++; - } - break; - - - case URX_LOOP_DOT_I: - // Loop Initialization for the optimized implementation of .* - // This op scans through all remaining input. - // The following LOOP_C op emulates stack unwinding if the following pattern fails. - { - // Loop through input until the input is exhausted (we reach an end-of-line) - // In DOTALL mode, we can just go straight to the end of the input. - int64_t ix; - if ((opValue & 1) == 1) { - // Dot-matches-All mode. Jump straight to the end of the string. - ix = fActiveLimit; - fHitEnd = true; - } else { - // NOT DOT ALL mode. Line endings do not match '.' - // Scan forward until a line ending or end of input. - ix = fp->fInputIdx; - UTEXT_SETNATIVEINDEX(fInputText, ix); - for (;;) { - if (ix >= fActiveLimit) { - fHitEnd = true; - break; - } - UChar32 c = UTEXT_NEXT32(fInputText); - if ((c & 0x7f) <= 0x29) { // Fast filter of non-new-line-s - if ((c == 0x0a) || // 0x0a is newline in both modes. - (((opValue & 2) == 0) && // IF not UNIX_LINES mode - isLineTerminator(c))) { - // char is a line ending. Exit the scanning loop. - break; - } - } - ix = UTEXT_GETNATIVEINDEX(fInputText); - } - } - - // If there were no matching characters, skip over the loop altogether. - // The loop doesn't run at all, a * op always succeeds. - if (ix == fp->fInputIdx) { - fp->fPatIdx++; // skip the URX_LOOP_C op. - break; - } - - // Peek ahead in the compiled pattern, to the URX_LOOP_C that - // must follow. It's operand is the stack location - // that holds the starting input index for the match of this .* - int32_t loopcOp = (int32_t)pat[fp->fPatIdx]; - U_ASSERT(URX_TYPE(loopcOp) == URX_LOOP_C); - int32_t stackLoc = URX_VAL(loopcOp); - U_ASSERT(stackLoc >= 0 && stackLoc < fFrameSize); - fp->fExtra[stackLoc] = fp->fInputIdx; - fp->fInputIdx = ix; - - // Save State to the URX_LOOP_C op that follows this one, - // so that match failures in the following code will return to there. - // Then bump the pattern idx so the LOOP_C is skipped on the way out of here. - fp = StateSave(fp, fp->fPatIdx, status); - fp->fPatIdx++; - } - break; - - - case URX_LOOP_C: - { - U_ASSERT(opValue>=0 && opValuefExtra[opValue]; - U_ASSERT(backSearchIndex <= fp->fInputIdx); - if (backSearchIndex == fp->fInputIdx) { - // We've backed up the input idx to the point that the loop started. - // The loop is done. Leave here without saving state. - // Subsequent failures won't come back here. - break; - } - // Set up for the next iteration of the loop, with input index - // backed up by one from the last time through, - // and a state save to this instruction in case the following code fails again. - // (We're going backwards because this loop emulates stack unwinding, not - // the initial scan forward.) - U_ASSERT(fp->fInputIdx > 0); - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - UChar32 prevC = UTEXT_PREVIOUS32(fInputText); - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - - UChar32 twoPrevC = UTEXT_PREVIOUS32(fInputText); - if (prevC == 0x0a && - fp->fInputIdx > backSearchIndex && - twoPrevC == 0x0d) { - int32_t prevOp = (int32_t)pat[fp->fPatIdx-2]; - if (URX_TYPE(prevOp) == URX_LOOP_DOT_I) { - // .*, stepping back over CRLF pair. - fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); - } - } - - - fp = StateSave(fp, fp->fPatIdx-1, status); - } - break; - - - - default: - // Trouble. The compiled pattern contains an entry with an - // unrecognized type tag. - UPRV_UNREACHABLE_ASSERT; - // Unknown opcode type in opType = URX_TYPE(pat[fp->fPatIdx]). But we have - // reports of this in production code, don't use UPRV_UNREACHABLE_EXIT. - // See ICU-21669. - status = U_INTERNAL_PROGRAM_ERROR; - } - - if (U_FAILURE(status)) { - isMatch = false; - break; - } - } - -breakFromLoop: - fMatch = isMatch; - if (isMatch) { - fLastMatchEnd = fMatchEnd; - fMatchStart = startIdx; - fMatchEnd = fp->fInputIdx; - } - -#ifdef REGEX_RUN_DEBUG - if (fTraceDebug) { - if (isMatch) { - printf("Match. start=%ld end=%ld\n\n", fMatchStart, fMatchEnd); - } else { - printf("No match\n\n"); - } - } -#endif - - fFrame = fp; // The active stack frame when the engine stopped. - // Contains the capture group results that we need to - // access later. - return; -} - - -//-------------------------------------------------------------------------------- -// -// MatchChunkAt This is the actual matching engine. Like MatchAt, but with the -// assumption that the entire string is available in the UText's -// chunk buffer. For now, that means we can use int32_t indexes, -// except for anything that needs to be saved (like group starts -// and ends). -// -// startIdx: begin matching a this index. -// toEnd: if true, match must extend to end of the input region -// -//-------------------------------------------------------------------------------- -void RegexMatcher::MatchChunkAt(int32_t startIdx, UBool toEnd, UErrorCode &status) { - UBool isMatch = false; // True if the we have a match. - - int32_t backSearchIndex = INT32_MAX; // used after greedy single-character matches for searching backwards - - int32_t op; // Operation from the compiled pattern, split into - int32_t opType; // the opcode - int32_t opValue; // and the operand value. - -#ifdef REGEX_RUN_DEBUG - if (fTraceDebug) { - printf("MatchAt(startIdx=%d)\n", startIdx); - printf("Original Pattern: \"%s\"\n", CStr(StringFromUText(fPattern->fPattern))()); - printf("Input String: \"%s\"\n\n", CStr(StringFromUText(fInputText))()); - } -#endif - - if (U_FAILURE(status)) { - return; - } - - // Cache frequently referenced items from the compiled pattern - // - int64_t *pat = fPattern->fCompiledPat->getBuffer(); - - const UChar *litText = fPattern->fLiteralText.getBuffer(); - UVector *fSets = fPattern->fSets; - - const UChar *inputBuf = fInputText->chunkContents; - - fFrameSize = fPattern->fFrameSize; - REStackFrame *fp = resetStack(); - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return; - } - - fp->fPatIdx = 0; - fp->fInputIdx = startIdx; - - // Zero out the pattern's static data - int32_t i; - for (i = 0; ifDataSize; i++) { - fData[i] = 0; - } - - // - // Main loop for interpreting the compiled pattern. - // One iteration of the loop per pattern operation performed. - // - for (;;) { - op = (int32_t)pat[fp->fPatIdx]; - opType = URX_TYPE(op); - opValue = URX_VAL(op); -#ifdef REGEX_RUN_DEBUG - if (fTraceDebug) { - UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); - printf("inputIdx=%ld inputChar=%x sp=%3ld activeLimit=%ld ", fp->fInputIdx, - UTEXT_CURRENT32(fInputText), (int64_t *)fp-fStack->getBuffer(), fActiveLimit); - fPattern->dumpOp(fp->fPatIdx); - } -#endif - fp->fPatIdx++; - - switch (opType) { - - - case URX_NOP: - break; - - - case URX_BACKTRACK: - // Force a backtrack. In some circumstances, the pattern compiler - // will notice that the pattern can't possibly match anything, and will - // emit one of these at that point. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - - - case URX_ONECHAR: - if (fp->fInputIdx < fActiveLimit) { - UChar32 c; - U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); - if (c == opValue) { - break; - } - } else { - fHitEnd = true; - } - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - - - case URX_STRING: - { - // Test input against a literal string. - // Strings require two slots in the compiled pattern, one for the - // offset to the string text, and one for the length. - int32_t stringStartIdx = opValue; - int32_t stringLen; - - op = (int32_t)pat[fp->fPatIdx]; // Fetch the second operand - fp->fPatIdx++; - opType = URX_TYPE(op); - stringLen = URX_VAL(op); - U_ASSERT(opType == URX_STRING_LEN); - U_ASSERT(stringLen >= 2); - - const UChar * pInp = inputBuf + fp->fInputIdx; - const UChar * pInpLimit = inputBuf + fActiveLimit; - const UChar * pPat = litText+stringStartIdx; - const UChar * pEnd = pInp + stringLen; - UBool success = true; - while (pInp < pEnd) { - if (pInp >= pInpLimit) { - fHitEnd = true; - success = false; - break; - } - if (*pInp++ != *pPat++) { - success = false; - break; - } - } - - if (success) { - fp->fInputIdx += stringLen; - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_STATE_SAVE: - fp = StateSave(fp, opValue, status); - break; - - - case URX_END: - // The match loop will exit via this path on a successful match, - // when we reach the end of the pattern. - if (toEnd && fp->fInputIdx != fActiveLimit) { - // The pattern matched, but not to the end of input. Try some more. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - isMatch = true; - goto breakFromLoop; - - // Start and End Capture stack frame variables are laid out out like this: - // fp->fExtra[opValue] - The start of a completed capture group - // opValue+1 - The end of a completed capture group - // opValue+2 - the start of a capture group whose end - // has not yet been reached (and might not ever be). - case URX_START_CAPTURE: - U_ASSERT(opValue >= 0 && opValue < fFrameSize-3); - fp->fExtra[opValue+2] = fp->fInputIdx; - break; - - - case URX_END_CAPTURE: - U_ASSERT(opValue >= 0 && opValue < fFrameSize-3); - U_ASSERT(fp->fExtra[opValue+2] >= 0); // Start pos for this group must be set. - fp->fExtra[opValue] = fp->fExtra[opValue+2]; // Tentative start becomes real. - fp->fExtra[opValue+1] = fp->fInputIdx; // End position - U_ASSERT(fp->fExtra[opValue] <= fp->fExtra[opValue+1]); - break; - - - case URX_DOLLAR: // $, test for End of line - // or for position before new line at end of input - if (fp->fInputIdx < fAnchorLimit-2) { - // We are no where near the end of input. Fail. - // This is the common case. Keep it first. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - if (fp->fInputIdx >= fAnchorLimit) { - // We really are at the end of input. Success. - fHitEnd = true; - fRequireEnd = true; - break; - } - - // If we are positioned just before a new-line that is located at the - // end of input, succeed. - if (fp->fInputIdx == fAnchorLimit-1) { - UChar32 c; - U16_GET(inputBuf, fAnchorStart, fp->fInputIdx, fAnchorLimit, c); - - if (isLineTerminator(c)) { - if ( !(c==0x0a && fp->fInputIdx>fAnchorStart && inputBuf[fp->fInputIdx-1]==0x0d)) { - // At new-line at end of input. Success - fHitEnd = true; - fRequireEnd = true; - break; - } - } - } else if (fp->fInputIdx == fAnchorLimit-2 && - inputBuf[fp->fInputIdx]==0x0d && inputBuf[fp->fInputIdx+1]==0x0a) { - fHitEnd = true; - fRequireEnd = true; - break; // At CR/LF at end of input. Success - } - - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - - break; - - - case URX_DOLLAR_D: // $, test for End of Line, in UNIX_LINES mode. - if (fp->fInputIdx >= fAnchorLimit-1) { - // Either at the last character of input, or off the end. - if (fp->fInputIdx == fAnchorLimit-1) { - // At last char of input. Success if it's a new line. - if (inputBuf[fp->fInputIdx] == 0x0a) { - fHitEnd = true; - fRequireEnd = true; - break; - } - } else { - // Off the end of input. Success. - fHitEnd = true; - fRequireEnd = true; - break; - } - } - - // Not at end of input. Back-track out. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - - - case URX_DOLLAR_M: // $, test for End of line in multi-line mode - { - if (fp->fInputIdx >= fAnchorLimit) { - // We really are at the end of input. Success. - fHitEnd = true; - fRequireEnd = true; - break; - } - // If we are positioned just before a new-line, succeed. - // It makes no difference where the new-line is within the input. - UChar32 c = inputBuf[fp->fInputIdx]; - if (isLineTerminator(c)) { - // At a line end, except for the odd chance of being in the middle of a CR/LF sequence - // In multi-line mode, hitting a new-line just before the end of input does not - // set the hitEnd or requireEnd flags - if ( !(c==0x0a && fp->fInputIdx>fAnchorStart && inputBuf[fp->fInputIdx-1]==0x0d)) { - break; - } - } - // not at a new line. Fail. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_DOLLAR_MD: // $, test for End of line in multi-line and UNIX_LINES mode - { - if (fp->fInputIdx >= fAnchorLimit) { - // We really are at the end of input. Success. - fHitEnd = true; - fRequireEnd = true; // Java set requireEnd in this case, even though - break; // adding a new-line would not lose the match. - } - // If we are not positioned just before a new-line, the test fails; backtrack out. - // It makes no difference where the new-line is within the input. - if (inputBuf[fp->fInputIdx] != 0x0a) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_CARET: // ^, test for start of line - if (fp->fInputIdx != fAnchorStart) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_CARET_M: // ^, test for start of line in mulit-line mode - { - if (fp->fInputIdx == fAnchorStart) { - // We are at the start input. Success. - break; - } - // Check whether character just before the current pos is a new-line - // unless we are at the end of input - UChar c = inputBuf[fp->fInputIdx - 1]; - if ((fp->fInputIdx < fAnchorLimit) && - isLineTerminator(c)) { - // It's a new-line. ^ is true. Success. - // TODO: what should be done with positions between a CR and LF? - break; - } - // Not at the start of a line. Fail. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_CARET_M_UNIX: // ^, test for start of line in mulit-line + Unix-line mode - { - U_ASSERT(fp->fInputIdx >= fAnchorStart); - if (fp->fInputIdx <= fAnchorStart) { - // We are at the start input. Success. - break; - } - // Check whether character just before the current pos is a new-line - U_ASSERT(fp->fInputIdx <= fAnchorLimit); - UChar c = inputBuf[fp->fInputIdx - 1]; - if (c != 0x0a) { - // Not at the start of a line. Back-track out. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - case URX_BACKSLASH_B: // Test for word boundaries - { - UBool success = isChunkWordBoundary((int32_t)fp->fInputIdx); - success ^= (UBool)(opValue != 0); // flip sense for \B - if (!success) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_BACKSLASH_BU: // Test for word boundaries, Unicode-style - { - UBool success = isUWordBoundary(fp->fInputIdx, status); - success ^= (UBool)(opValue != 0); // flip sense for \B - if (!success) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_BACKSLASH_D: // Test for decimal digit - { - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - UChar32 c; - U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); - int8_t ctype = u_charType(c); // TODO: make a unicode set for this. Will be faster. - UBool success = (ctype == U_DECIMAL_DIGIT_NUMBER); - success ^= (UBool)(opValue != 0); // flip sense for \D - if (!success) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_BACKSLASH_G: // Test for position at end of previous match - if (!((fMatch && fp->fInputIdx==fMatchEnd) || (fMatch==false && fp->fInputIdx==fActiveStart))) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_BACKSLASH_H: // Test for \h, horizontal white space. - { - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - UChar32 c; - U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); - int8_t ctype = u_charType(c); - UBool success = (ctype == U_SPACE_SEPARATOR || c == 9); // SPACE_SEPARATOR || TAB - success ^= (UBool)(opValue != 0); // flip sense for \H - if (!success) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_BACKSLASH_R: // Test for \R, any line break sequence. - { - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - UChar32 c; - U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); - if (isLineTerminator(c)) { - if (c == 0x0d && fp->fInputIdx < fActiveLimit) { - // Check for CR/LF sequence. Consume both together when found. - UChar c2; - U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c2); - if (c2 != 0x0a) { - U16_PREV(inputBuf, 0, fp->fInputIdx, c2); - } - } - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_BACKSLASH_V: // Any single code point line ending. - { - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - UChar32 c; - U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); - UBool success = isLineTerminator(c); - success ^= (UBool)(opValue != 0); // flip sense for \V - if (!success) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_BACKSLASH_X: - // Match a Grapheme, as defined by Unicode UAX 29. - - // Fail if at end of input - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - fp->fInputIdx = followingGCBoundary(fp->fInputIdx, status); - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp->fInputIdx = fActiveLimit; - } - break; - - - case URX_BACKSLASH_Z: // Test for end of Input - if (fp->fInputIdx < fAnchorLimit) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } else { - fHitEnd = true; - fRequireEnd = true; - } - break; - - - - case URX_STATIC_SETREF: - { - // Test input character against one of the predefined sets - // (Word Characters, for example) - // The high bit of the op value is a flag for the match polarity. - // 0: success if input char is in set. - // 1: success if input char is not in set. - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - UBool success = ((opValue & URX_NEG_SET) == URX_NEG_SET); - opValue &= ~URX_NEG_SET; - U_ASSERT(opValue > 0 && opValue < URX_LAST_SET); - - UChar32 c; - U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); - if (c < 256) { - Regex8BitSet &s8 = RegexStaticSets::gStaticSets->fPropSets8[opValue]; - if (s8.contains(c)) { - success = !success; - } - } else { - const UnicodeSet &s = RegexStaticSets::gStaticSets->fPropSets[opValue]; - if (s.contains(c)) { - success = !success; - } - } - if (!success) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_STAT_SETREF_N: - { - // Test input character for NOT being a member of one of - // the predefined sets (Word Characters, for example) - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - U_ASSERT(opValue > 0 && opValue < URX_LAST_SET); - - UChar32 c; - U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); - if (c < 256) { - Regex8BitSet &s8 = RegexStaticSets::gStaticSets->fPropSets8[opValue]; - if (s8.contains(c) == false) { - break; - } - } else { - const UnicodeSet &s = RegexStaticSets::gStaticSets->fPropSets[opValue]; - if (s.contains(c) == false) { - break; - } - } - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_SETREF: - { - if (fp->fInputIdx >= fActiveLimit) { - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - U_ASSERT(opValue > 0 && opValue < fSets->size()); - - // There is input left. Pick up one char and test it for set membership. - UChar32 c; - U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); - if (c<256) { - Regex8BitSet *s8 = &fPattern->fSets8[opValue]; - if (s8->contains(c)) { - // The character is in the set. A Match. - break; - } - } else { - UnicodeSet *s = (UnicodeSet *)fSets->elementAt(opValue); - if (s->contains(c)) { - // The character is in the set. A Match. - break; - } - } - - // the character wasn't in the set. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_DOTANY: - { - // . matches anything, but stops at end-of-line. - if (fp->fInputIdx >= fActiveLimit) { - // At end of input. Match failed. Backtrack out. - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - // There is input left. Advance over one char, unless we've hit end-of-line - UChar32 c; - U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); - if (isLineTerminator(c)) { - // End of line in normal mode. . does not match. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - } - break; - - - case URX_DOTANY_ALL: - { - // . in dot-matches-all (including new lines) mode - if (fp->fInputIdx >= fActiveLimit) { - // At end of input. Match failed. Backtrack out. - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - // There is input left. Advance over one char, except if we are - // at a cr/lf, advance over both of them. - UChar32 c; - U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); - if (c==0x0d && fp->fInputIdx < fActiveLimit) { - // In the case of a CR/LF, we need to advance over both. - if (inputBuf[fp->fInputIdx] == 0x0a) { - U16_FWD_1(inputBuf, fp->fInputIdx, fActiveLimit); - } - } - } - break; - - - case URX_DOTANY_UNIX: - { - // '.' operator, matches all, but stops at end-of-line. - // UNIX_LINES mode, so 0x0a is the only recognized line ending. - if (fp->fInputIdx >= fActiveLimit) { - // At end of input. Match failed. Backtrack out. - fHitEnd = true; - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - // There is input left. Advance over one char, unless we've hit end-of-line - UChar32 c; - U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); - if (c == 0x0a) { - // End of line in normal mode. '.' does not match the \n - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - - case URX_JMP: - fp->fPatIdx = opValue; - break; - - case URX_FAIL: - isMatch = false; - goto breakFromLoop; - - case URX_JMP_SAV: - U_ASSERT(opValue < fPattern->fCompiledPat->size()); - fp = StateSave(fp, fp->fPatIdx, status); // State save to loc following current - fp->fPatIdx = opValue; // Then JMP. - break; - - case URX_JMP_SAV_X: - // This opcode is used with (x)+, when x can match a zero length string. - // Same as JMP_SAV, except conditional on the match having made forward progress. - // Destination of the JMP must be a URX_STO_INP_LOC, from which we get the - // data address of the input position at the start of the loop. - { - U_ASSERT(opValue > 0 && opValue < fPattern->fCompiledPat->size()); - int32_t stoOp = (int32_t)pat[opValue-1]; - U_ASSERT(URX_TYPE(stoOp) == URX_STO_INP_LOC); - int32_t frameLoc = URX_VAL(stoOp); - U_ASSERT(frameLoc >= 0 && frameLoc < fFrameSize); - int32_t prevInputIdx = (int32_t)fp->fExtra[frameLoc]; - U_ASSERT(prevInputIdx <= fp->fInputIdx); - if (prevInputIdx < fp->fInputIdx) { - // The match did make progress. Repeat the loop. - fp = StateSave(fp, fp->fPatIdx, status); // State save to loc following current - fp->fPatIdx = opValue; - fp->fExtra[frameLoc] = fp->fInputIdx; - } - // If the input position did not advance, we do nothing here, - // execution will fall out of the loop. - } - break; - - case URX_CTR_INIT: - { - U_ASSERT(opValue >= 0 && opValue < fFrameSize-2); - fp->fExtra[opValue] = 0; // Set the loop counter variable to zero - - // Pick up the three extra operands that CTR_INIT has, and - // skip the pattern location counter past - int32_t instrOperandLoc = (int32_t)fp->fPatIdx; - fp->fPatIdx += 3; - int32_t loopLoc = URX_VAL(pat[instrOperandLoc]); - int32_t minCount = (int32_t)pat[instrOperandLoc+1]; - int32_t maxCount = (int32_t)pat[instrOperandLoc+2]; - U_ASSERT(minCount>=0); - U_ASSERT(maxCount>=minCount || maxCount==-1); - U_ASSERT(loopLoc>=fp->fPatIdx); - - if (minCount == 0) { - fp = StateSave(fp, loopLoc+1, status); - } - if (maxCount == -1) { - fp->fExtra[opValue+1] = fp->fInputIdx; // For loop breaking. - } else if (maxCount == 0) { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - case URX_CTR_LOOP: - { - U_ASSERT(opValue>0 && opValue < fp->fPatIdx-2); - int32_t initOp = (int32_t)pat[opValue]; - U_ASSERT(URX_TYPE(initOp) == URX_CTR_INIT); - int64_t *pCounter = &fp->fExtra[URX_VAL(initOp)]; - int32_t minCount = (int32_t)pat[opValue+2]; - int32_t maxCount = (int32_t)pat[opValue+3]; - (*pCounter)++; - if ((uint64_t)*pCounter >= (uint32_t)maxCount && maxCount != -1) { - U_ASSERT(*pCounter == maxCount); - break; - } - if (*pCounter >= minCount) { - if (maxCount == -1) { - // Loop has no hard upper bound. - // Check that it is progressing through the input, break if it is not. - int64_t *pLastInputIdx = &fp->fExtra[URX_VAL(initOp) + 1]; - if (fp->fInputIdx == *pLastInputIdx) { - break; - } else { - *pLastInputIdx = fp->fInputIdx; - } - } - fp = StateSave(fp, fp->fPatIdx, status); - } else { - // Increment time-out counter. (StateSave() does it if count >= minCount) - fTickCounter--; - if (fTickCounter <= 0) { - IncrementTime(status); // Re-initializes fTickCounter - } - } - fp->fPatIdx = opValue + 4; // Loop back. - } - break; - - case URX_CTR_INIT_NG: - { - // Initialize a non-greedy loop - U_ASSERT(opValue >= 0 && opValue < fFrameSize-2); - fp->fExtra[opValue] = 0; // Set the loop counter variable to zero - - // Pick up the three extra operands that CTR_INIT_NG has, and - // skip the pattern location counter past - int32_t instrOperandLoc = (int32_t)fp->fPatIdx; - fp->fPatIdx += 3; - int32_t loopLoc = URX_VAL(pat[instrOperandLoc]); - int32_t minCount = (int32_t)pat[instrOperandLoc+1]; - int32_t maxCount = (int32_t)pat[instrOperandLoc+2]; - U_ASSERT(minCount>=0); - U_ASSERT(maxCount>=minCount || maxCount==-1); - U_ASSERT(loopLoc>fp->fPatIdx); - if (maxCount == -1) { - fp->fExtra[opValue+1] = fp->fInputIdx; // Save initial input index for loop breaking. - } - - if (minCount == 0) { - if (maxCount != 0) { - fp = StateSave(fp, fp->fPatIdx, status); - } - fp->fPatIdx = loopLoc+1; // Continue with stuff after repeated block - } - } - break; - - case URX_CTR_LOOP_NG: - { - // Non-greedy {min, max} loops - U_ASSERT(opValue>0 && opValue < fp->fPatIdx-2); - int32_t initOp = (int32_t)pat[opValue]; - U_ASSERT(URX_TYPE(initOp) == URX_CTR_INIT_NG); - int64_t *pCounter = &fp->fExtra[URX_VAL(initOp)]; - int32_t minCount = (int32_t)pat[opValue+2]; - int32_t maxCount = (int32_t)pat[opValue+3]; - - (*pCounter)++; - if ((uint64_t)*pCounter >= (uint32_t)maxCount && maxCount != -1) { - // The loop has matched the maximum permitted number of times. - // Break out of here with no action. Matching will - // continue with the following pattern. - U_ASSERT(*pCounter == maxCount); - break; - } - - if (*pCounter < minCount) { - // We haven't met the minimum number of matches yet. - // Loop back for another one. - fp->fPatIdx = opValue + 4; // Loop back. - fTickCounter--; - if (fTickCounter <= 0) { - IncrementTime(status); // Re-initializes fTickCounter - } - } else { - // We do have the minimum number of matches. - - // If there is no upper bound on the loop iterations, check that the input index - // is progressing, and stop the loop if it is not. - if (maxCount == -1) { - int64_t *pLastInputIdx = &fp->fExtra[URX_VAL(initOp) + 1]; - if (fp->fInputIdx == *pLastInputIdx) { - break; - } - *pLastInputIdx = fp->fInputIdx; - } - - // Loop Continuation: we will fall into the pattern following the loop - // (non-greedy, don't execute loop body first), but first do - // a state save to the top of the loop, so that a match failure - // in the following pattern will try another iteration of the loop. - fp = StateSave(fp, opValue + 4, status); - } - } - break; - - case URX_STO_SP: - U_ASSERT(opValue >= 0 && opValue < fPattern->fDataSize); - fData[opValue] = fStack->size(); - break; - - case URX_LD_SP: - { - U_ASSERT(opValue >= 0 && opValue < fPattern->fDataSize); - int32_t newStackSize = (int32_t)fData[opValue]; - U_ASSERT(newStackSize <= fStack->size()); - int64_t *newFP = fStack->getBuffer() + newStackSize - fFrameSize; - if (newFP == (int64_t *)fp) { - break; - } - int32_t j; - for (j=0; jsetSize(newStackSize); - } - break; - - case URX_BACKREF: - { - U_ASSERT(opValue < fFrameSize); - int64_t groupStartIdx = fp->fExtra[opValue]; - int64_t groupEndIdx = fp->fExtra[opValue+1]; - U_ASSERT(groupStartIdx <= groupEndIdx); - int64_t inputIndex = fp->fInputIdx; - if (groupStartIdx < 0) { - // This capture group has not participated in the match thus far, - fp = (REStackFrame *)fStack->popFrame(fFrameSize); // FAIL, no match. - break; - } - UBool success = true; - for (int64_t groupIndex = groupStartIdx; groupIndex < groupEndIdx; ++groupIndex,++inputIndex) { - if (inputIndex >= fActiveLimit) { - success = false; - fHitEnd = true; - break; - } - if (inputBuf[groupIndex] != inputBuf[inputIndex]) { - success = false; - break; - } - } - if (success && groupStartIdx < groupEndIdx && U16_IS_LEAD(inputBuf[groupEndIdx-1]) && - inputIndex < fActiveLimit && U16_IS_TRAIL(inputBuf[inputIndex])) { - // Capture group ended with an unpaired lead surrogate. - // Back reference is not permitted to match lead only of a surrogatge pair. - success = false; - } - if (success) { - fp->fInputIdx = inputIndex; - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - case URX_BACKREF_I: - { - U_ASSERT(opValue < fFrameSize); - int64_t groupStartIdx = fp->fExtra[opValue]; - int64_t groupEndIdx = fp->fExtra[opValue+1]; - U_ASSERT(groupStartIdx <= groupEndIdx); - if (groupStartIdx < 0) { - // This capture group has not participated in the match thus far, - fp = (REStackFrame *)fStack->popFrame(fFrameSize); // FAIL, no match. - break; - } - CaseFoldingUCharIterator captureGroupItr(inputBuf, groupStartIdx, groupEndIdx); - CaseFoldingUCharIterator inputItr(inputBuf, fp->fInputIdx, fActiveLimit); - - // Note: if the capture group match was of an empty string the backref - // match succeeds. Verified by testing: Perl matches succeed - // in this case, so we do too. - - UBool success = true; - for (;;) { - UChar32 captureGroupChar = captureGroupItr.next(); - if (captureGroupChar == U_SENTINEL) { - success = true; - break; - } - UChar32 inputChar = inputItr.next(); - if (inputChar == U_SENTINEL) { - success = false; - fHitEnd = true; - break; - } - if (inputChar != captureGroupChar) { - success = false; - break; - } - } - - if (success && inputItr.inExpansion()) { - // We obtained a match by consuming part of a string obtained from - // case-folding a single code point of the input text. - // This does not count as an overall match. - success = false; - } - - if (success) { - fp->fInputIdx = inputItr.getIndex(); - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - case URX_STO_INP_LOC: - { - U_ASSERT(opValue >= 0 && opValue < fFrameSize); - fp->fExtra[opValue] = fp->fInputIdx; - } - break; - - case URX_JMPX: - { - int32_t instrOperandLoc = (int32_t)fp->fPatIdx; - fp->fPatIdx += 1; - int32_t dataLoc = URX_VAL(pat[instrOperandLoc]); - U_ASSERT(dataLoc >= 0 && dataLoc < fFrameSize); - int32_t savedInputIdx = (int32_t)fp->fExtra[dataLoc]; - U_ASSERT(savedInputIdx <= fp->fInputIdx); - if (savedInputIdx < fp->fInputIdx) { - fp->fPatIdx = opValue; // JMP - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); // FAIL, no progress in loop. - } - } - break; - - case URX_LA_START: - { - // Entering a look around block. - // Save Stack Ptr, Input Pos. - U_ASSERT(opValue>=0 && opValue+3fDataSize); - fData[opValue] = fStack->size(); - fData[opValue+1] = fp->fInputIdx; - fData[opValue+2] = fActiveStart; - fData[opValue+3] = fActiveLimit; - fActiveStart = fLookStart; // Set the match region change for - fActiveLimit = fLookLimit; // transparent bounds. - } - break; - - case URX_LA_END: - { - // Leaving a look around block. - // restore Stack Ptr, Input Pos to positions they had on entry to block. - U_ASSERT(opValue>=0 && opValue+3fDataSize); - int32_t stackSize = fStack->size(); - int32_t newStackSize = (int32_t)fData[opValue]; - U_ASSERT(stackSize >= newStackSize); - if (stackSize > newStackSize) { - // Copy the current top frame back to the new (cut back) top frame. - // This makes the capture groups from within the look-ahead - // expression available. - int64_t *newFP = fStack->getBuffer() + newStackSize - fFrameSize; - int32_t j; - for (j=0; jsetSize(newStackSize); - } - fp->fInputIdx = fData[opValue+1]; - - // Restore the active region bounds in the input string; they may have - // been changed because of transparent bounds on a Region. - fActiveStart = fData[opValue+2]; - fActiveLimit = fData[opValue+3]; - U_ASSERT(fActiveStart >= 0); - U_ASSERT(fActiveLimit <= fInputLength); - } - break; - - case URX_ONECHAR_I: - if (fp->fInputIdx < fActiveLimit) { - UChar32 c; - U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); - if (u_foldCase(c, U_FOLD_CASE_DEFAULT) == opValue) { - break; - } - } else { - fHitEnd = true; - } - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - - case URX_STRING_I: - // Case-insensitive test input against a literal string. - // Strings require two slots in the compiled pattern, one for the - // offset to the string text, and one for the length. - // The compiled string has already been case folded. - { - const UChar *patternString = litText + opValue; - - op = (int32_t)pat[fp->fPatIdx]; - fp->fPatIdx++; - opType = URX_TYPE(op); - opValue = URX_VAL(op); - U_ASSERT(opType == URX_STRING_LEN); - int32_t patternStringLen = opValue; // Length of the string from the pattern. - - UChar32 cText; - UChar32 cPattern; - UBool success = true; - int32_t patternStringIdx = 0; - CaseFoldingUCharIterator inputIterator(inputBuf, fp->fInputIdx, fActiveLimit); - while (patternStringIdx < patternStringLen) { - U16_NEXT(patternString, patternStringIdx, patternStringLen, cPattern); - cText = inputIterator.next(); - if (cText != cPattern) { - success = false; - if (cText == U_SENTINEL) { - fHitEnd = true; - } - break; - } - } - if (inputIterator.inExpansion()) { - success = false; - } - - if (success) { - fp->fInputIdx = inputIterator.getIndex(); - } else { - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - } - break; - - case URX_LB_START: - { - // Entering a look-behind block. - // Save Stack Ptr, Input Pos and active input region. - // TODO: implement transparent bounds. Ticket #6067 - U_ASSERT(opValue>=0 && opValue+4fDataSize); - fData[opValue] = fStack->size(); - fData[opValue+1] = fp->fInputIdx; - // Save input string length, then reset to pin any matches to end at - // the current position. - fData[opValue+2] = fActiveStart; - fData[opValue+3] = fActiveLimit; - fActiveStart = fRegionStart; - fActiveLimit = fp->fInputIdx; - // Init the variable containing the start index for attempted matches. - fData[opValue+4] = -1; - } - break; - - - case URX_LB_CONT: - { - // Positive Look-Behind, at top of loop checking for matches of LB expression - // at all possible input starting positions. - - // Fetch the min and max possible match lengths. They are the operands - // of this op in the pattern. - int32_t minML = (int32_t)pat[fp->fPatIdx++]; - int32_t maxML = (int32_t)pat[fp->fPatIdx++]; - U_ASSERT(minML <= maxML); - U_ASSERT(minML >= 0); - - // Fetch (from data) the last input index where a match was attempted. - U_ASSERT(opValue>=0 && opValue+4fDataSize); - int64_t &lbStartIdx = fData[opValue+4]; - if (lbStartIdx < 0) { - // First time through loop. - lbStartIdx = fp->fInputIdx - minML; - if (lbStartIdx > 0 && lbStartIdx < fInputLength) { - U16_SET_CP_START(inputBuf, 0, lbStartIdx); - } - } else { - // 2nd through nth time through the loop. - // Back up start position for match by one. - if (lbStartIdx == 0) { - lbStartIdx--; - } else { - U16_BACK_1(inputBuf, 0, lbStartIdx); - } - } - - if (lbStartIdx < 0 || lbStartIdx < fp->fInputIdx - maxML) { - // We have tried all potential match starting points without - // getting a match. Backtrack out, and out of the - // Look Behind altogether. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - fActiveStart = fData[opValue+2]; - fActiveLimit = fData[opValue+3]; - U_ASSERT(fActiveStart >= 0); - U_ASSERT(fActiveLimit <= fInputLength); - break; - } - - // Save state to this URX_LB_CONT op, so failure to match will repeat the loop. - // (successful match will fall off the end of the loop.) - fp = StateSave(fp, fp->fPatIdx-3, status); - fp->fInputIdx = lbStartIdx; - } - break; - - case URX_LB_END: - // End of a look-behind block, after a successful match. - { - U_ASSERT(opValue>=0 && opValue+4fDataSize); - if (fp->fInputIdx != fActiveLimit) { - // The look-behind expression matched, but the match did not - // extend all the way to the point that we are looking behind from. - // FAIL out of here, which will take us back to the LB_CONT, which - // will retry the match starting at another position or fail - // the look-behind altogether, whichever is appropriate. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - // Look-behind match is good. Restore the original input string region, - // which had been truncated to pin the end of the lookbehind match to the - // position being looked-behind. - fActiveStart = fData[opValue+2]; - fActiveLimit = fData[opValue+3]; - U_ASSERT(fActiveStart >= 0); - U_ASSERT(fActiveLimit <= fInputLength); - } - break; - - - case URX_LBN_CONT: - { - // Negative Look-Behind, at top of loop checking for matches of LB expression - // at all possible input starting positions. - - // Fetch the extra parameters of this op. - int32_t minML = (int32_t)pat[fp->fPatIdx++]; - int32_t maxML = (int32_t)pat[fp->fPatIdx++]; - int32_t continueLoc = (int32_t)pat[fp->fPatIdx++]; - continueLoc = URX_VAL(continueLoc); - U_ASSERT(minML <= maxML); - U_ASSERT(minML >= 0); - U_ASSERT(continueLoc > fp->fPatIdx); - - // Fetch (from data) the last input index where a match was attempted. - U_ASSERT(opValue>=0 && opValue+4fDataSize); - int64_t &lbStartIdx = fData[opValue+4]; - if (lbStartIdx < 0) { - // First time through loop. - lbStartIdx = fp->fInputIdx - minML; - if (lbStartIdx > 0 && lbStartIdx < fInputLength) { - U16_SET_CP_START(inputBuf, 0, lbStartIdx); - } - } else { - // 2nd through nth time through the loop. - // Back up start position for match by one. - if (lbStartIdx == 0) { - lbStartIdx--; // Because U16_BACK is unsafe starting at 0. - } else { - U16_BACK_1(inputBuf, 0, lbStartIdx); - } - } - - if (lbStartIdx < 0 || lbStartIdx < fp->fInputIdx - maxML) { - // We have tried all potential match starting points without - // getting a match, which means that the negative lookbehind as - // a whole has succeeded. Jump forward to the continue location - fActiveStart = fData[opValue+2]; - fActiveLimit = fData[opValue+3]; - U_ASSERT(fActiveStart >= 0); - U_ASSERT(fActiveLimit <= fInputLength); - fp->fPatIdx = continueLoc; - break; - } - - // Save state to this URX_LB_CONT op, so failure to match will repeat the loop. - // (successful match will cause a FAIL out of the loop altogether.) - fp = StateSave(fp, fp->fPatIdx-4, status); - fp->fInputIdx = lbStartIdx; - } - break; - - case URX_LBN_END: - // End of a negative look-behind block, after a successful match. - { - U_ASSERT(opValue>=0 && opValue+4fDataSize); - if (fp->fInputIdx != fActiveLimit) { - // The look-behind expression matched, but the match did not - // extend all the way to the point that we are looking behind from. - // FAIL out of here, which will take us back to the LB_CONT, which - // will retry the match starting at another position or succeed - // the look-behind altogether, whichever is appropriate. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - break; - } - - // Look-behind expression matched, which means look-behind test as - // a whole Fails - - // Restore the original input string length, which had been truncated - // inorder to pin the end of the lookbehind match - // to the position being looked-behind. - fActiveStart = fData[opValue+2]; - fActiveLimit = fData[opValue+3]; - U_ASSERT(fActiveStart >= 0); - U_ASSERT(fActiveLimit <= fInputLength); - - // Restore original stack position, discarding any state saved - // by the successful pattern match. - U_ASSERT(opValue>=0 && opValue+1fDataSize); - int32_t newStackSize = (int32_t)fData[opValue]; - U_ASSERT(fStack->size() > newStackSize); - fStack->setSize(newStackSize); - - // FAIL, which will take control back to someplace - // prior to entering the look-behind test. - fp = (REStackFrame *)fStack->popFrame(fFrameSize); - } - break; - - - case URX_LOOP_SR_I: - // Loop Initialization for the optimized implementation of - // [some character set]* - // This op scans through all matching input. - // The following LOOP_C op emulates stack unwinding if the following pattern fails. - { - U_ASSERT(opValue > 0 && opValue < fSets->size()); - Regex8BitSet *s8 = &fPattern->fSets8[opValue]; - UnicodeSet *s = (UnicodeSet *)fSets->elementAt(opValue); - - // Loop through input, until either the input is exhausted or - // we reach a character that is not a member of the set. - int32_t ix = (int32_t)fp->fInputIdx; - for (;;) { - if (ix >= fActiveLimit) { - fHitEnd = true; - break; - } - UChar32 c; - U16_NEXT(inputBuf, ix, fActiveLimit, c); - if (c<256) { - if (s8->contains(c) == false) { - U16_BACK_1(inputBuf, 0, ix); - break; - } - } else { - if (s->contains(c) == false) { - U16_BACK_1(inputBuf, 0, ix); - break; - } - } - } - - // If there were no matching characters, skip over the loop altogether. - // The loop doesn't run at all, a * op always succeeds. - if (ix == fp->fInputIdx) { - fp->fPatIdx++; // skip the URX_LOOP_C op. - break; - } - - // Peek ahead in the compiled pattern, to the URX_LOOP_C that - // must follow. It's operand is the stack location - // that holds the starting input index for the match of this [set]* - int32_t loopcOp = (int32_t)pat[fp->fPatIdx]; - U_ASSERT(URX_TYPE(loopcOp) == URX_LOOP_C); - int32_t stackLoc = URX_VAL(loopcOp); - U_ASSERT(stackLoc >= 0 && stackLoc < fFrameSize); - fp->fExtra[stackLoc] = fp->fInputIdx; - fp->fInputIdx = ix; - - // Save State to the URX_LOOP_C op that follows this one, - // so that match failures in the following code will return to there. - // Then bump the pattern idx so the LOOP_C is skipped on the way out of here. - fp = StateSave(fp, fp->fPatIdx, status); - fp->fPatIdx++; - } - break; - - - case URX_LOOP_DOT_I: - // Loop Initialization for the optimized implementation of .* - // This op scans through all remaining input. - // The following LOOP_C op emulates stack unwinding if the following pattern fails. - { - // Loop through input until the input is exhausted (we reach an end-of-line) - // In DOTALL mode, we can just go straight to the end of the input. - int32_t ix; - if ((opValue & 1) == 1) { - // Dot-matches-All mode. Jump straight to the end of the string. - ix = (int32_t)fActiveLimit; - fHitEnd = true; - } else { - // NOT DOT ALL mode. Line endings do not match '.' - // Scan forward until a line ending or end of input. - ix = (int32_t)fp->fInputIdx; - for (;;) { - if (ix >= fActiveLimit) { - fHitEnd = true; - break; - } - UChar32 c; - U16_NEXT(inputBuf, ix, fActiveLimit, c); // c = inputBuf[ix++] - if ((c & 0x7f) <= 0x29) { // Fast filter of non-new-line-s - if ((c == 0x0a) || // 0x0a is newline in both modes. - (((opValue & 2) == 0) && // IF not UNIX_LINES mode - isLineTerminator(c))) { - // char is a line ending. Put the input pos back to the - // line ending char, and exit the scanning loop. - U16_BACK_1(inputBuf, 0, ix); - break; - } - } - } - } - - // If there were no matching characters, skip over the loop altogether. - // The loop doesn't run at all, a * op always succeeds. - if (ix == fp->fInputIdx) { - fp->fPatIdx++; // skip the URX_LOOP_C op. - break; - } - - // Peek ahead in the compiled pattern, to the URX_LOOP_C that - // must follow. It's operand is the stack location - // that holds the starting input index for the match of this .* - int32_t loopcOp = (int32_t)pat[fp->fPatIdx]; - U_ASSERT(URX_TYPE(loopcOp) == URX_LOOP_C); - int32_t stackLoc = URX_VAL(loopcOp); - U_ASSERT(stackLoc >= 0 && stackLoc < fFrameSize); - fp->fExtra[stackLoc] = fp->fInputIdx; - fp->fInputIdx = ix; - - // Save State to the URX_LOOP_C op that follows this one, - // so that match failures in the following code will return to there. - // Then bump the pattern idx so the LOOP_C is skipped on the way out of here. - fp = StateSave(fp, fp->fPatIdx, status); - fp->fPatIdx++; - } - break; - - - case URX_LOOP_C: - { - U_ASSERT(opValue>=0 && opValuefExtra[opValue]; - U_ASSERT(backSearchIndex <= fp->fInputIdx); - if (backSearchIndex == fp->fInputIdx) { - // We've backed up the input idx to the point that the loop started. - // The loop is done. Leave here without saving state. - // Subsequent failures won't come back here. - break; - } - // Set up for the next iteration of the loop, with input index - // backed up by one from the last time through, - // and a state save to this instruction in case the following code fails again. - // (We're going backwards because this loop emulates stack unwinding, not - // the initial scan forward.) - U_ASSERT(fp->fInputIdx > 0); - UChar32 prevC; - U16_PREV(inputBuf, 0, fp->fInputIdx, prevC); // !!!: should this 0 be one of f*Limit? - - if (prevC == 0x0a && - fp->fInputIdx > backSearchIndex && - inputBuf[fp->fInputIdx-1] == 0x0d) { - int32_t prevOp = (int32_t)pat[fp->fPatIdx-2]; - if (URX_TYPE(prevOp) == URX_LOOP_DOT_I) { - // .*, stepping back over CRLF pair. - U16_BACK_1(inputBuf, 0, fp->fInputIdx); - } - } - - - fp = StateSave(fp, fp->fPatIdx-1, status); - } - break; - - - - default: - // Trouble. The compiled pattern contains an entry with an - // unrecognized type tag. - UPRV_UNREACHABLE_ASSERT; - // Unknown opcode type in opType = URX_TYPE(pat[fp->fPatIdx]). But we have - // reports of this in production code, don't use UPRV_UNREACHABLE_EXIT. - // See ICU-21669. - status = U_INTERNAL_PROGRAM_ERROR; - } - - if (U_FAILURE(status)) { - isMatch = false; - break; - } - } - -breakFromLoop: - fMatch = isMatch; - if (isMatch) { - fLastMatchEnd = fMatchEnd; - fMatchStart = startIdx; - fMatchEnd = fp->fInputIdx; - } - -#ifdef REGEX_RUN_DEBUG - if (fTraceDebug) { - if (isMatch) { - printf("Match. start=%ld end=%ld\n\n", fMatchStart, fMatchEnd); - } else { - printf("No match\n\n"); - } - } -#endif - - fFrame = fp; // The active stack frame when the engine stopped. - // Contains the capture group results that we need to - // access later. - - return; -} - - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RegexMatcher) - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_REGULAR_EXPRESSIONS - +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +************************************************************************** +* Copyright (C) 2002-2016 International Business Machines Corporation +* and others. All rights reserved. +************************************************************************** +*/ +// +// file: rematch.cpp +// +// Contains the implementation of class RegexMatcher, +// which is one of the main API classes for the ICU regular expression package. +// + +#include "unicode/utypes.h" +#if !UCONFIG_NO_REGULAR_EXPRESSIONS + +#include "unicode/regex.h" +#include "unicode/uniset.h" +#include "unicode/uchar.h" +#include "unicode/ustring.h" +#include "unicode/rbbi.h" +#include "unicode/utf.h" +#include "unicode/utf16.h" +#include "uassert.h" +#include "cmemory.h" +#include "cstr.h" +#include "uvector.h" +#include "uvectr32.h" +#include "uvectr64.h" +#include "regeximp.h" +#include "regexst.h" +#include "regextxt.h" +#include "ucase.h" + +// #include // Needed for heapcheck testing + + +U_NAMESPACE_BEGIN + +// Default limit for the size of the back track stack, to avoid system +// failures causedby heap exhaustion. Units are in 32 bit words, not bytes. +// This value puts ICU's limits higher than most other regexp implementations, +// which use recursion rather than the heap, and take more storage per +// backtrack point. +// +static const int32_t DEFAULT_BACKTRACK_STACK_CAPACITY = 8000000; + +// Time limit counter constant. +// Time limits for expression evaluation are in terms of quanta of work by +// the engine, each of which is 10,000 state saves. +// This constant determines that state saves per tick number. +static const int32_t TIMER_INITIAL_VALUE = 10000; + + +// Test for any of the Unicode line terminating characters. +static inline UBool isLineTerminator(UChar32 c) { + if (c & ~(0x0a | 0x0b | 0x0c | 0x0d | 0x85 | 0x2028 | 0x2029)) { + return false; + } + return (c<=0x0d && c>=0x0a) || c==0x85 || c==0x2028 || c==0x2029; +} + +//----------------------------------------------------------------------------- +// +// Constructor and Destructor +// +//----------------------------------------------------------------------------- +RegexMatcher::RegexMatcher(const RegexPattern *pat) { + fDeferredStatus = U_ZERO_ERROR; + init(fDeferredStatus); + if (U_FAILURE(fDeferredStatus)) { + return; + } + if (pat==nullptr) { + fDeferredStatus = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + fPattern = pat; + init2(RegexStaticSets::gStaticSets->fEmptyText, fDeferredStatus); +} + + + +RegexMatcher::RegexMatcher(const UnicodeString ®exp, const UnicodeString &input, + uint32_t flags, UErrorCode &status) { + init(status); + if (U_FAILURE(status)) { + return; + } + UParseError pe; + fPatternOwned = RegexPattern::compile(regexp, flags, pe, status); + fPattern = fPatternOwned; + + UText inputText = UTEXT_INITIALIZER; + utext_openConstUnicodeString(&inputText, &input, &status); + init2(&inputText, status); + utext_close(&inputText); + + fInputUniStrMaybeMutable = true; +} + + +RegexMatcher::RegexMatcher(UText *regexp, UText *input, + uint32_t flags, UErrorCode &status) { + init(status); + if (U_FAILURE(status)) { + return; + } + UParseError pe; + fPatternOwned = RegexPattern::compile(regexp, flags, pe, status); + if (U_FAILURE(status)) { + return; + } + + fPattern = fPatternOwned; + init2(input, status); +} + + +RegexMatcher::RegexMatcher(const UnicodeString ®exp, + uint32_t flags, UErrorCode &status) { + init(status); + if (U_FAILURE(status)) { + return; + } + UParseError pe; + fPatternOwned = RegexPattern::compile(regexp, flags, pe, status); + if (U_FAILURE(status)) { + return; + } + fPattern = fPatternOwned; + init2(RegexStaticSets::gStaticSets->fEmptyText, status); +} + +RegexMatcher::RegexMatcher(UText *regexp, + uint32_t flags, UErrorCode &status) { + init(status); + if (U_FAILURE(status)) { + return; + } + UParseError pe; + fPatternOwned = RegexPattern::compile(regexp, flags, pe, status); + if (U_FAILURE(status)) { + return; + } + + fPattern = fPatternOwned; + init2(RegexStaticSets::gStaticSets->fEmptyText, status); +} + + + + +RegexMatcher::~RegexMatcher() { + delete fStack; + if (fData != fSmallData) { + uprv_free(fData); + fData = nullptr; + } + if (fPatternOwned) { + delete fPatternOwned; + fPatternOwned = nullptr; + fPattern = nullptr; + } + + if (fInput) { + delete fInput; + } + if (fInputText) { + utext_close(fInputText); + } + if (fAltInputText) { + utext_close(fAltInputText); + } + + #if UCONFIG_NO_BREAK_ITERATION==0 + delete fWordBreakItr; + delete fGCBreakItr; + #endif +} + +// +// init() common initialization for use by all constructors. +// Initialize all fields, get the object into a consistent state. +// This must be done even when the initial status shows an error, +// so that the object is initialized sufficiently well for the destructor +// to run safely. +// +void RegexMatcher::init(UErrorCode &status) { + fPattern = nullptr; + fPatternOwned = nullptr; + fFrameSize = 0; + fRegionStart = 0; + fRegionLimit = 0; + fAnchorStart = 0; + fAnchorLimit = 0; + fLookStart = 0; + fLookLimit = 0; + fActiveStart = 0; + fActiveLimit = 0; + fTransparentBounds = false; + fAnchoringBounds = true; + fMatch = false; + fMatchStart = 0; + fMatchEnd = 0; + fLastMatchEnd = -1; + fAppendPosition = 0; + fHitEnd = false; + fRequireEnd = false; + fStack = nullptr; + fFrame = nullptr; + fTimeLimit = 0; + fTime = 0; + fTickCounter = 0; + fStackLimit = DEFAULT_BACKTRACK_STACK_CAPACITY; + fCallbackFn = nullptr; + fCallbackContext = nullptr; + fFindProgressCallbackFn = nullptr; + fFindProgressCallbackContext = nullptr; + fTraceDebug = false; + fDeferredStatus = status; + fData = fSmallData; + fWordBreakItr = nullptr; + fGCBreakItr = nullptr; + + fStack = nullptr; + fInputText = nullptr; + fAltInputText = nullptr; + fInput = nullptr; + fInputLength = 0; + fInputUniStrMaybeMutable = false; +} + +// +// init2() Common initialization for use by RegexMatcher constructors, part 2. +// This handles the common setup to be done after the Pattern is available. +// +void RegexMatcher::init2(UText *input, UErrorCode &status) { + if (U_FAILURE(status)) { + fDeferredStatus = status; + return; + } + + if (fPattern->fDataSize > UPRV_LENGTHOF(fSmallData)) { + fData = (int64_t *)uprv_malloc(fPattern->fDataSize * sizeof(int64_t)); + if (fData == nullptr) { + status = fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + + fStack = new UVector64(status); + if (fStack == nullptr) { + status = fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; + return; + } + + reset(input); + setStackLimit(DEFAULT_BACKTRACK_STACK_CAPACITY, status); + if (U_FAILURE(status)) { + fDeferredStatus = status; + return; + } +} + + +static const char16_t BACKSLASH = 0x5c; +static const char16_t DOLLARSIGN = 0x24; +static const char16_t LEFTBRACKET = 0x7b; +static const char16_t RIGHTBRACKET = 0x7d; + +//-------------------------------------------------------------------------------- +// +// appendReplacement +// +//-------------------------------------------------------------------------------- +RegexMatcher &RegexMatcher::appendReplacement(UnicodeString &dest, + const UnicodeString &replacement, + UErrorCode &status) { + UText replacementText = UTEXT_INITIALIZER; + + utext_openConstUnicodeString(&replacementText, &replacement, &status); + if (U_SUCCESS(status)) { + UText resultText = UTEXT_INITIALIZER; + utext_openUnicodeString(&resultText, &dest, &status); + + if (U_SUCCESS(status)) { + appendReplacement(&resultText, &replacementText, status); + utext_close(&resultText); + } + utext_close(&replacementText); + } + + return *this; +} + +// +// appendReplacement, UText mode +// +RegexMatcher &RegexMatcher::appendReplacement(UText *dest, + UText *replacement, + UErrorCode &status) { + if (U_FAILURE(status)) { + return *this; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return *this; + } + if (fMatch == false) { + status = U_REGEX_INVALID_STATE; + return *this; + } + + // Copy input string from the end of previous match to start of current match + int64_t destLen = utext_nativeLength(dest); + if (fMatchStart > fAppendPosition) { + if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { + destLen += utext_replace(dest, destLen, destLen, fInputText->chunkContents+fAppendPosition, + (int32_t)(fMatchStart-fAppendPosition), &status); + } else { + int32_t len16; + if (UTEXT_USES_U16(fInputText)) { + len16 = (int32_t)(fMatchStart-fAppendPosition); + } else { + UErrorCode lengthStatus = U_ZERO_ERROR; + len16 = utext_extract(fInputText, fAppendPosition, fMatchStart, nullptr, 0, &lengthStatus); + } + char16_t *inputChars = (char16_t *)uprv_malloc(sizeof(char16_t)*(len16+1)); + if (inputChars == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + utext_extract(fInputText, fAppendPosition, fMatchStart, inputChars, len16+1, &status); + destLen += utext_replace(dest, destLen, destLen, inputChars, len16, &status); + uprv_free(inputChars); + } + } + fAppendPosition = fMatchEnd; + + + // scan the replacement text, looking for substitutions ($n) and \escapes. + // TODO: optimize this loop by efficiently scanning for '$' or '\', + // move entire ranges not containing substitutions. + UTEXT_SETNATIVEINDEX(replacement, 0); + for (UChar32 c = UTEXT_NEXT32(replacement); U_SUCCESS(status) && c != U_SENTINEL; c = UTEXT_NEXT32(replacement)) { + if (c == BACKSLASH) { + // Backslash Escape. Copy the following char out without further checks. + // Note: Surrogate pairs don't need any special handling + // The second half wont be a '$' or a '\', and + // will move to the dest normally on the next + // loop iteration. + c = UTEXT_CURRENT32(replacement); + if (c == U_SENTINEL) { + break; + } + + if (c==0x55/*U*/ || c==0x75/*u*/) { + // We have a \udddd or \Udddddddd escape sequence. + int32_t offset = 0; + struct URegexUTextUnescapeCharContext context = U_REGEX_UTEXT_UNESCAPE_CONTEXT(replacement); + UChar32 escapedChar = u_unescapeAt(uregex_utext_unescape_charAt, &offset, INT32_MAX, &context); + if (escapedChar != (UChar32)0xFFFFFFFF) { + if (U_IS_BMP(escapedChar)) { + char16_t c16 = (char16_t)escapedChar; + destLen += utext_replace(dest, destLen, destLen, &c16, 1, &status); + } else { + char16_t surrogate[2]; + surrogate[0] = U16_LEAD(escapedChar); + surrogate[1] = U16_TRAIL(escapedChar); + if (U_SUCCESS(status)) { + destLen += utext_replace(dest, destLen, destLen, surrogate, 2, &status); + } + } + // TODO: Report errors for mal-formed \u escapes? + // As this is, the original sequence is output, which may be OK. + if (context.lastOffset == offset) { + (void)UTEXT_PREVIOUS32(replacement); + } else if (context.lastOffset != offset-1) { + utext_moveIndex32(replacement, offset - context.lastOffset - 1); + } + } + } else { + (void)UTEXT_NEXT32(replacement); + // Plain backslash escape. Just put out the escaped character. + if (U_IS_BMP(c)) { + char16_t c16 = (char16_t)c; + destLen += utext_replace(dest, destLen, destLen, &c16, 1, &status); + } else { + char16_t surrogate[2]; + surrogate[0] = U16_LEAD(c); + surrogate[1] = U16_TRAIL(c); + if (U_SUCCESS(status)) { + destLen += utext_replace(dest, destLen, destLen, surrogate, 2, &status); + } + } + } + } else if (c != DOLLARSIGN) { + // Normal char, not a $. Copy it out without further checks. + if (U_IS_BMP(c)) { + char16_t c16 = (char16_t)c; + destLen += utext_replace(dest, destLen, destLen, &c16, 1, &status); + } else { + char16_t surrogate[2]; + surrogate[0] = U16_LEAD(c); + surrogate[1] = U16_TRAIL(c); + if (U_SUCCESS(status)) { + destLen += utext_replace(dest, destLen, destLen, surrogate, 2, &status); + } + } + } else { + // We've got a $. Pick up a capture group name or number if one follows. + // Consume digits so long as the resulting group number <= the number of + // number of capture groups in the pattern. + + int32_t groupNum = 0; + int32_t numDigits = 0; + UChar32 nextChar = utext_current32(replacement); + if (nextChar == LEFTBRACKET) { + // Scan for a Named Capture Group, ${name}. + UnicodeString groupName; + utext_next32(replacement); + while(U_SUCCESS(status) && nextChar != RIGHTBRACKET) { + nextChar = utext_next32(replacement); + if (nextChar == U_SENTINEL) { + status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; + } else if ((nextChar >= 0x41 && nextChar <= 0x5a) || // A..Z + (nextChar >= 0x61 && nextChar <= 0x7a) || // a..z + (nextChar >= 0x31 && nextChar <= 0x39)) { // 0..9 + groupName.append(nextChar); + } else if (nextChar == RIGHTBRACKET) { + groupNum = fPattern->fNamedCaptureMap ? uhash_geti(fPattern->fNamedCaptureMap, &groupName) : 0; + if (groupNum == 0) { + status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; + } + } else { + // Character was something other than a name char or a closing '}' + status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; + } + } + + } else if (u_isdigit(nextChar)) { + // $n Scan for a capture group number + int32_t numCaptureGroups = fPattern->fGroupMap->size(); + for (;;) { + nextChar = UTEXT_CURRENT32(replacement); + if (nextChar == U_SENTINEL) { + break; + } + if (u_isdigit(nextChar) == false) { + break; + } + int32_t nextDigitVal = u_charDigitValue(nextChar); + if (groupNum*10 + nextDigitVal > numCaptureGroups) { + // Don't consume the next digit if it makes the capture group number too big. + if (numDigits == 0) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + } + break; + } + (void)UTEXT_NEXT32(replacement); + groupNum=groupNum*10 + nextDigitVal; + ++numDigits; + } + } else { + // $ not followed by capture group name or number. + status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; + } + + if (U_SUCCESS(status)) { + destLen += appendGroup(groupNum, dest, status); + } + } // End of $ capture group handling + } // End of per-character loop through the replacement string. + + return *this; +} + + + +//-------------------------------------------------------------------------------- +// +// appendTail Intended to be used in conjunction with appendReplacement() +// To the destination string, append everything following +// the last match position from the input string. +// +// Note: Match ranges do not affect appendTail or appendReplacement +// +//-------------------------------------------------------------------------------- +UnicodeString &RegexMatcher::appendTail(UnicodeString &dest) { + UErrorCode status = U_ZERO_ERROR; + UText resultText = UTEXT_INITIALIZER; + utext_openUnicodeString(&resultText, &dest, &status); + + if (U_SUCCESS(status)) { + appendTail(&resultText, status); + utext_close(&resultText); + } + + return dest; +} + +// +// appendTail, UText mode +// +UText *RegexMatcher::appendTail(UText *dest, UErrorCode &status) { + if (U_FAILURE(status)) { + return dest; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return dest; + } + + if (fInputLength > fAppendPosition) { + if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { + int64_t destLen = utext_nativeLength(dest); + utext_replace(dest, destLen, destLen, fInputText->chunkContents+fAppendPosition, + (int32_t)(fInputLength-fAppendPosition), &status); + } else { + int32_t len16; + if (UTEXT_USES_U16(fInputText)) { + len16 = (int32_t)(fInputLength-fAppendPosition); + } else { + len16 = utext_extract(fInputText, fAppendPosition, fInputLength, nullptr, 0, &status); + status = U_ZERO_ERROR; // buffer overflow + } + + char16_t *inputChars = (char16_t *)uprv_malloc(sizeof(char16_t)*(len16)); + if (inputChars == nullptr) { + fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; + } else { + utext_extract(fInputText, fAppendPosition, fInputLength, inputChars, len16, &status); // unterminated + int64_t destLen = utext_nativeLength(dest); + utext_replace(dest, destLen, destLen, inputChars, len16, &status); + uprv_free(inputChars); + } + } + } + return dest; +} + + + +//-------------------------------------------------------------------------------- +// +// end +// +//-------------------------------------------------------------------------------- +int32_t RegexMatcher::end(UErrorCode &err) const { + return end(0, err); +} + +int64_t RegexMatcher::end64(UErrorCode &err) const { + return end64(0, err); +} + +int64_t RegexMatcher::end64(int32_t group, UErrorCode &err) const { + if (U_FAILURE(err)) { + return -1; + } + if (fMatch == false) { + err = U_REGEX_INVALID_STATE; + return -1; + } + if (group < 0 || group > fPattern->fGroupMap->size()) { + err = U_INDEX_OUTOFBOUNDS_ERROR; + return -1; + } + int64_t e = -1; + if (group == 0) { + e = fMatchEnd; + } else { + // Get the position within the stack frame of the variables for + // this capture group. + int32_t groupOffset = fPattern->fGroupMap->elementAti(group-1); + U_ASSERT(groupOffset < fPattern->fFrameSize); + U_ASSERT(groupOffset >= 0); + e = fFrame->fExtra[groupOffset + 1]; + } + + return e; +} + +int32_t RegexMatcher::end(int32_t group, UErrorCode &err) const { + return (int32_t)end64(group, err); +} + +//-------------------------------------------------------------------------------- +// +// findProgressInterrupt This function is called once for each advance in the target +// string from the find() function, and calls the user progress callback +// function if there is one installed. +// +// Return: true if the find operation is to be terminated. +// false if the find operation is to continue running. +// +//-------------------------------------------------------------------------------- +UBool RegexMatcher::findProgressInterrupt(int64_t pos, UErrorCode &status) { + if (fFindProgressCallbackFn && !(*fFindProgressCallbackFn)(fFindProgressCallbackContext, pos)) { + status = U_REGEX_STOPPED_BY_CALLER; + return true; + } + return false; +} + +//-------------------------------------------------------------------------------- +// +// find() +// +//-------------------------------------------------------------------------------- +UBool RegexMatcher::find() { + if (U_FAILURE(fDeferredStatus)) { + return false; + } + UErrorCode status = U_ZERO_ERROR; + UBool result = find(status); + return result; +} + +//-------------------------------------------------------------------------------- +// +// find() +// +//-------------------------------------------------------------------------------- +UBool RegexMatcher::find(UErrorCode &status) { + // Start at the position of the last match end. (Will be zero if the + // matcher has been reset.) + // + if (U_FAILURE(status)) { + return false; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return false; + } + + if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { + return findUsingChunk(status); + } + + int64_t startPos = fMatchEnd; + if (startPos==0) { + startPos = fActiveStart; + } + + if (fMatch) { + // Save the position of any previous successful match. + fLastMatchEnd = fMatchEnd; + + if (fMatchStart == fMatchEnd) { + // Previous match had zero length. Move start position up one position + // to avoid sending find() into a loop on zero-length matches. + if (startPos >= fActiveLimit) { + fMatch = false; + fHitEnd = true; + return false; + } + UTEXT_SETNATIVEINDEX(fInputText, startPos); + (void)UTEXT_NEXT32(fInputText); + startPos = UTEXT_GETNATIVEINDEX(fInputText); + } + } else { + if (fLastMatchEnd >= 0) { + // A previous find() failed to match. Don't try again. + // (without this test, a pattern with a zero-length match + // could match again at the end of an input string.) + fHitEnd = true; + return false; + } + } + + + // Compute the position in the input string beyond which a match can not begin, because + // the minimum length match would extend past the end of the input. + // Note: some patterns that cannot match anything will have fMinMatchLength==Max Int. + // Be aware of possible overflows if making changes here. + int64_t testStartLimit; + if (UTEXT_USES_U16(fInputText)) { + testStartLimit = fActiveLimit - fPattern->fMinMatchLen; + if (startPos > testStartLimit) { + fMatch = false; + fHitEnd = true; + return false; + } + } else { + // We don't know exactly how long the minimum match length is in native characters. + // Treat anything > 0 as 1. + testStartLimit = fActiveLimit - (fPattern->fMinMatchLen > 0 ? 1 : 0); + } + + UChar32 c; + U_ASSERT(startPos >= 0); + + switch (fPattern->fStartType) { + case START_NO_INFO: + // No optimization was found. + // Try a match at each input position. + for (;;) { + MatchAt(startPos, false, status); + if (U_FAILURE(status)) { + return false; + } + if (fMatch) { + return true; + } + if (startPos >= testStartLimit) { + fHitEnd = true; + return false; + } + UTEXT_SETNATIVEINDEX(fInputText, startPos); + (void)UTEXT_NEXT32(fInputText); + startPos = UTEXT_GETNATIVEINDEX(fInputText); + // Note that it's perfectly OK for a pattern to have a zero-length + // match at the end of a string, so we must make sure that the loop + // runs with startPos == testStartLimit the last time through. + if (findProgressInterrupt(startPos, status)) + return false; + } + UPRV_UNREACHABLE_EXIT; + + case START_START: + // Matches are only possible at the start of the input string + // (pattern begins with ^ or \A) + if (startPos > fActiveStart) { + fMatch = false; + return false; + } + MatchAt(startPos, false, status); + if (U_FAILURE(status)) { + return false; + } + return fMatch; + + + case START_SET: + { + // Match may start on any char from a pre-computed set. + U_ASSERT(fPattern->fMinMatchLen > 0); + UTEXT_SETNATIVEINDEX(fInputText, startPos); + for (;;) { + int64_t pos = startPos; + c = UTEXT_NEXT32(fInputText); + startPos = UTEXT_GETNATIVEINDEX(fInputText); + // c will be -1 (U_SENTINEL) at end of text, in which case we + // skip this next block (so we don't have a negative array index) + // and handle end of text in the following block. + if (c >= 0 && ((c<256 && fPattern->fInitialChars8->contains(c)) || + (c>=256 && fPattern->fInitialChars->contains(c)))) { + MatchAt(pos, false, status); + if (U_FAILURE(status)) { + return false; + } + if (fMatch) { + return true; + } + UTEXT_SETNATIVEINDEX(fInputText, pos); + } + if (startPos > testStartLimit) { + fMatch = false; + fHitEnd = true; + return false; + } + if (findProgressInterrupt(startPos, status)) + return false; + } + } + UPRV_UNREACHABLE_EXIT; + + case START_STRING: + case START_CHAR: + { + // Match starts on exactly one char. + U_ASSERT(fPattern->fMinMatchLen > 0); + UChar32 theChar = fPattern->fInitialChar; + UTEXT_SETNATIVEINDEX(fInputText, startPos); + for (;;) { + int64_t pos = startPos; + c = UTEXT_NEXT32(fInputText); + startPos = UTEXT_GETNATIVEINDEX(fInputText); + if (c == theChar) { + MatchAt(pos, false, status); + if (U_FAILURE(status)) { + return false; + } + if (fMatch) { + return true; + } + UTEXT_SETNATIVEINDEX(fInputText, startPos); + } + if (startPos > testStartLimit) { + fMatch = false; + fHitEnd = true; + return false; + } + if (findProgressInterrupt(startPos, status)) + return false; + } + } + UPRV_UNREACHABLE_EXIT; + + case START_LINE: + { + UChar32 ch; + if (startPos == fAnchorStart) { + MatchAt(startPos, false, status); + if (U_FAILURE(status)) { + return false; + } + if (fMatch) { + return true; + } + UTEXT_SETNATIVEINDEX(fInputText, startPos); + ch = UTEXT_NEXT32(fInputText); + startPos = UTEXT_GETNATIVEINDEX(fInputText); + } else { + UTEXT_SETNATIVEINDEX(fInputText, startPos); + ch = UTEXT_PREVIOUS32(fInputText); + UTEXT_SETNATIVEINDEX(fInputText, startPos); + } + + if (fPattern->fFlags & UREGEX_UNIX_LINES) { + for (;;) { + if (ch == 0x0a) { + MatchAt(startPos, false, status); + if (U_FAILURE(status)) { + return false; + } + if (fMatch) { + return true; + } + UTEXT_SETNATIVEINDEX(fInputText, startPos); + } + if (startPos >= testStartLimit) { + fMatch = false; + fHitEnd = true; + return false; + } + ch = UTEXT_NEXT32(fInputText); + startPos = UTEXT_GETNATIVEINDEX(fInputText); + // Note that it's perfectly OK for a pattern to have a zero-length + // match at the end of a string, so we must make sure that the loop + // runs with startPos == testStartLimit the last time through. + if (findProgressInterrupt(startPos, status)) + return false; + } + } else { + for (;;) { + if (isLineTerminator(ch)) { + if (ch == 0x0d && startPos < fActiveLimit && UTEXT_CURRENT32(fInputText) == 0x0a) { + (void)UTEXT_NEXT32(fInputText); + startPos = UTEXT_GETNATIVEINDEX(fInputText); + } + MatchAt(startPos, false, status); + if (U_FAILURE(status)) { + return false; + } + if (fMatch) { + return true; + } + UTEXT_SETNATIVEINDEX(fInputText, startPos); + } + if (startPos >= testStartLimit) { + fMatch = false; + fHitEnd = true; + return false; + } + ch = UTEXT_NEXT32(fInputText); + startPos = UTEXT_GETNATIVEINDEX(fInputText); + // Note that it's perfectly OK for a pattern to have a zero-length + // match at the end of a string, so we must make sure that the loop + // runs with startPos == testStartLimit the last time through. + if (findProgressInterrupt(startPos, status)) + return false; + } + } + } + + default: + UPRV_UNREACHABLE_ASSERT; + // Unknown value in fPattern->fStartType, should be from StartOfMatch enum. But + // we have reports of this in production code, don't use UPRV_UNREACHABLE_EXIT. + // See ICU-21669. + status = U_INTERNAL_PROGRAM_ERROR; + return false; + } + + UPRV_UNREACHABLE_EXIT; +} + + + +UBool RegexMatcher::find(int64_t start, UErrorCode &status) { + if (U_FAILURE(status)) { + return false; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return false; + } + this->reset(); // Note: Reset() is specified by Java Matcher documentation. + // This will reset the region to be the full input length. + if (start < 0) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + return false; + } + + int64_t nativeStart = start; + if (nativeStart < fActiveStart || nativeStart > fActiveLimit) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + return false; + } + fMatchEnd = nativeStart; + return find(status); +} + + +//-------------------------------------------------------------------------------- +// +// findUsingChunk() -- like find(), but with the advance knowledge that the +// entire string is available in the UText's chunk buffer. +// +//-------------------------------------------------------------------------------- +UBool RegexMatcher::findUsingChunk(UErrorCode &status) { + // Start at the position of the last match end. (Will be zero if the + // matcher has been reset. + // + + int32_t startPos = (int32_t)fMatchEnd; + if (startPos==0) { + startPos = (int32_t)fActiveStart; + } + + const char16_t *inputBuf = fInputText->chunkContents; + + if (fMatch) { + // Save the position of any previous successful match. + fLastMatchEnd = fMatchEnd; + + if (fMatchStart == fMatchEnd) { + // Previous match had zero length. Move start position up one position + // to avoid sending find() into a loop on zero-length matches. + if (startPos >= fActiveLimit) { + fMatch = false; + fHitEnd = true; + return false; + } + U16_FWD_1(inputBuf, startPos, fInputLength); + } + } else { + if (fLastMatchEnd >= 0) { + // A previous find() failed to match. Don't try again. + // (without this test, a pattern with a zero-length match + // could match again at the end of an input string.) + fHitEnd = true; + return false; + } + } + + + // Compute the position in the input string beyond which a match can not begin, because + // the minimum length match would extend past the end of the input. + // Note: some patterns that cannot match anything will have fMinMatchLength==Max Int. + // Be aware of possible overflows if making changes here. + // Note: a match can begin at inputBuf + testLen; it is an inclusive limit. + int32_t testLen = (int32_t)(fActiveLimit - fPattern->fMinMatchLen); + if (startPos > testLen) { + fMatch = false; + fHitEnd = true; + return false; + } + + UChar32 c; + U_ASSERT(startPos >= 0); + + switch (fPattern->fStartType) { + case START_NO_INFO: + // No optimization was found. + // Try a match at each input position. + for (;;) { + MatchChunkAt(startPos, false, status); + if (U_FAILURE(status)) { + return false; + } + if (fMatch) { + return true; + } + if (startPos >= testLen) { + fHitEnd = true; + return false; + } + U16_FWD_1(inputBuf, startPos, fActiveLimit); + // Note that it's perfectly OK for a pattern to have a zero-length + // match at the end of a string, so we must make sure that the loop + // runs with startPos == testLen the last time through. + if (findProgressInterrupt(startPos, status)) + return false; + } + UPRV_UNREACHABLE_EXIT; + + case START_START: + // Matches are only possible at the start of the input string + // (pattern begins with ^ or \A) + if (startPos > fActiveStart) { + fMatch = false; + return false; + } + MatchChunkAt(startPos, false, status); + if (U_FAILURE(status)) { + return false; + } + return fMatch; + + + case START_SET: + { + // Match may start on any char from a pre-computed set. + U_ASSERT(fPattern->fMinMatchLen > 0); + for (;;) { + int32_t pos = startPos; + U16_NEXT(inputBuf, startPos, fActiveLimit, c); // like c = inputBuf[startPos++]; + if ((c<256 && fPattern->fInitialChars8->contains(c)) || + (c>=256 && fPattern->fInitialChars->contains(c))) { + MatchChunkAt(pos, false, status); + if (U_FAILURE(status)) { + return false; + } + if (fMatch) { + return true; + } + } + if (startPos > testLen) { + fMatch = false; + fHitEnd = true; + return false; + } + if (findProgressInterrupt(startPos, status)) + return false; + } + } + UPRV_UNREACHABLE_EXIT; + + case START_STRING: + case START_CHAR: + { + // Match starts on exactly one char. + U_ASSERT(fPattern->fMinMatchLen > 0); + UChar32 theChar = fPattern->fInitialChar; + for (;;) { + int32_t pos = startPos; + U16_NEXT(inputBuf, startPos, fActiveLimit, c); // like c = inputBuf[startPos++]; + if (c == theChar) { + MatchChunkAt(pos, false, status); + if (U_FAILURE(status)) { + return false; + } + if (fMatch) { + return true; + } + } + if (startPos > testLen) { + fMatch = false; + fHitEnd = true; + return false; + } + if (findProgressInterrupt(startPos, status)) + return false; + } + } + UPRV_UNREACHABLE_EXIT; + + case START_LINE: + { + UChar32 ch; + if (startPos == fAnchorStart) { + MatchChunkAt(startPos, false, status); + if (U_FAILURE(status)) { + return false; + } + if (fMatch) { + return true; + } + U16_FWD_1(inputBuf, startPos, fActiveLimit); + } + + if (fPattern->fFlags & UREGEX_UNIX_LINES) { + for (;;) { + ch = inputBuf[startPos-1]; + if (ch == 0x0a) { + MatchChunkAt(startPos, false, status); + if (U_FAILURE(status)) { + return false; + } + if (fMatch) { + return true; + } + } + if (startPos >= testLen) { + fMatch = false; + fHitEnd = true; + return false; + } + U16_FWD_1(inputBuf, startPos, fActiveLimit); + // Note that it's perfectly OK for a pattern to have a zero-length + // match at the end of a string, so we must make sure that the loop + // runs with startPos == testLen the last time through. + if (findProgressInterrupt(startPos, status)) + return false; + } + } else { + for (;;) { + ch = inputBuf[startPos-1]; + if (isLineTerminator(ch)) { + if (ch == 0x0d && startPos < fActiveLimit && inputBuf[startPos] == 0x0a) { + startPos++; + } + MatchChunkAt(startPos, false, status); + if (U_FAILURE(status)) { + return false; + } + if (fMatch) { + return true; + } + } + if (startPos >= testLen) { + fMatch = false; + fHitEnd = true; + return false; + } + U16_FWD_1(inputBuf, startPos, fActiveLimit); + // Note that it's perfectly OK for a pattern to have a zero-length + // match at the end of a string, so we must make sure that the loop + // runs with startPos == testLen the last time through. + if (findProgressInterrupt(startPos, status)) + return false; + } + } + } + + default: + UPRV_UNREACHABLE_ASSERT; + // Unknown value in fPattern->fStartType, should be from StartOfMatch enum. But + // we have reports of this in production code, don't use UPRV_UNREACHABLE_EXIT. + // See ICU-21669. + status = U_INTERNAL_PROGRAM_ERROR; + return false; + } + + UPRV_UNREACHABLE_EXIT; +} + + + +//-------------------------------------------------------------------------------- +// +// group() +// +//-------------------------------------------------------------------------------- +UnicodeString RegexMatcher::group(UErrorCode &status) const { + return group(0, status); +} + +// Return immutable shallow clone +UText *RegexMatcher::group(UText *dest, int64_t &group_len, UErrorCode &status) const { + return group(0, dest, group_len, status); +} + +// Return immutable shallow clone +UText *RegexMatcher::group(int32_t groupNum, UText *dest, int64_t &group_len, UErrorCode &status) const { + group_len = 0; + if (U_FAILURE(status)) { + return dest; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + } else if (fMatch == false) { + status = U_REGEX_INVALID_STATE; + } else if (groupNum < 0 || groupNum > fPattern->fGroupMap->size()) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + } + + if (U_FAILURE(status)) { + return dest; + } + + int64_t s, e; + if (groupNum == 0) { + s = fMatchStart; + e = fMatchEnd; + } else { + int32_t groupOffset = fPattern->fGroupMap->elementAti(groupNum-1); + U_ASSERT(groupOffset < fPattern->fFrameSize); + U_ASSERT(groupOffset >= 0); + s = fFrame->fExtra[groupOffset]; + e = fFrame->fExtra[groupOffset+1]; + } + + if (s < 0) { + // A capture group wasn't part of the match + return utext_clone(dest, fInputText, false, true, &status); + } + U_ASSERT(s <= e); + group_len = e - s; + + dest = utext_clone(dest, fInputText, false, true, &status); + if (dest) + UTEXT_SETNATIVEINDEX(dest, s); + return dest; +} + +UnicodeString RegexMatcher::group(int32_t groupNum, UErrorCode &status) const { + UnicodeString result; + int64_t groupStart = start64(groupNum, status); + int64_t groupEnd = end64(groupNum, status); + if (U_FAILURE(status) || groupStart == -1 || groupStart == groupEnd) { + return result; + } + + // Get the group length using a utext_extract preflight. + // UText is actually pretty efficient at this when underlying encoding is UTF-16. + int32_t length = utext_extract(fInputText, groupStart, groupEnd, nullptr, 0, &status); + if (status != U_BUFFER_OVERFLOW_ERROR) { + return result; + } + + status = U_ZERO_ERROR; + char16_t *buf = result.getBuffer(length); + if (buf == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + int32_t extractLength = utext_extract(fInputText, groupStart, groupEnd, buf, length, &status); + result.releaseBuffer(extractLength); + U_ASSERT(length == extractLength); + } + return result; +} + + +//-------------------------------------------------------------------------------- +// +// appendGroup() -- currently internal only, appends a group to a UText rather +// than replacing its contents +// +//-------------------------------------------------------------------------------- + +int64_t RegexMatcher::appendGroup(int32_t groupNum, UText *dest, UErrorCode &status) const { + if (U_FAILURE(status)) { + return 0; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return 0; + } + int64_t destLen = utext_nativeLength(dest); + + if (fMatch == false) { + status = U_REGEX_INVALID_STATE; + return utext_replace(dest, destLen, destLen, nullptr, 0, &status); + } + if (groupNum < 0 || groupNum > fPattern->fGroupMap->size()) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + return utext_replace(dest, destLen, destLen, nullptr, 0, &status); + } + + int64_t s, e; + if (groupNum == 0) { + s = fMatchStart; + e = fMatchEnd; + } else { + int32_t groupOffset = fPattern->fGroupMap->elementAti(groupNum-1); + U_ASSERT(groupOffset < fPattern->fFrameSize); + U_ASSERT(groupOffset >= 0); + s = fFrame->fExtra[groupOffset]; + e = fFrame->fExtra[groupOffset+1]; + } + + if (s < 0) { + // A capture group wasn't part of the match + return utext_replace(dest, destLen, destLen, nullptr, 0, &status); + } + U_ASSERT(s <= e); + + int64_t deltaLen; + if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { + U_ASSERT(e <= fInputLength); + deltaLen = utext_replace(dest, destLen, destLen, fInputText->chunkContents+s, (int32_t)(e-s), &status); + } else { + int32_t len16; + if (UTEXT_USES_U16(fInputText)) { + len16 = (int32_t)(e-s); + } else { + UErrorCode lengthStatus = U_ZERO_ERROR; + len16 = utext_extract(fInputText, s, e, nullptr, 0, &lengthStatus); + } + char16_t *groupChars = (char16_t *)uprv_malloc(sizeof(char16_t)*(len16+1)); + if (groupChars == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + utext_extract(fInputText, s, e, groupChars, len16+1, &status); + + deltaLen = utext_replace(dest, destLen, destLen, groupChars, len16, &status); + uprv_free(groupChars); + } + return deltaLen; +} + + + +//-------------------------------------------------------------------------------- +// +// groupCount() +// +//-------------------------------------------------------------------------------- +int32_t RegexMatcher::groupCount() const { + return fPattern->fGroupMap->size(); +} + +//-------------------------------------------------------------------------------- +// +// hasAnchoringBounds() +// +//-------------------------------------------------------------------------------- +UBool RegexMatcher::hasAnchoringBounds() const { + return fAnchoringBounds; +} + + +//-------------------------------------------------------------------------------- +// +// hasTransparentBounds() +// +//-------------------------------------------------------------------------------- +UBool RegexMatcher::hasTransparentBounds() const { + return fTransparentBounds; +} + + + +//-------------------------------------------------------------------------------- +// +// hitEnd() +// +//-------------------------------------------------------------------------------- +UBool RegexMatcher::hitEnd() const { + return fHitEnd; +} + + +//-------------------------------------------------------------------------------- +// +// input() +// +//-------------------------------------------------------------------------------- +const UnicodeString &RegexMatcher::input() const { + if (!fInput) { + UErrorCode status = U_ZERO_ERROR; + int32_t len16; + if (UTEXT_USES_U16(fInputText)) { + len16 = (int32_t)fInputLength; + } else { + len16 = utext_extract(fInputText, 0, fInputLength, nullptr, 0, &status); + status = U_ZERO_ERROR; // overflow, length status + } + UnicodeString *result = new UnicodeString(len16, 0, 0); + + char16_t *inputChars = result->getBuffer(len16); + utext_extract(fInputText, 0, fInputLength, inputChars, len16, &status); // unterminated warning + result->releaseBuffer(len16); + + (*(const UnicodeString **)&fInput) = result; // pointer assignment, rather than operator= + } + + return *fInput; +} + +//-------------------------------------------------------------------------------- +// +// inputText() +// +//-------------------------------------------------------------------------------- +UText *RegexMatcher::inputText() const { + return fInputText; +} + + +//-------------------------------------------------------------------------------- +// +// getInput() -- like inputText(), but makes a clone or copies into another UText +// +//-------------------------------------------------------------------------------- +UText *RegexMatcher::getInput (UText *dest, UErrorCode &status) const { + if (U_FAILURE(status)) { + return dest; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return dest; + } + + if (dest) { + if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { + utext_replace(dest, 0, utext_nativeLength(dest), fInputText->chunkContents, (int32_t)fInputLength, &status); + } else { + int32_t input16Len; + if (UTEXT_USES_U16(fInputText)) { + input16Len = (int32_t)fInputLength; + } else { + UErrorCode lengthStatus = U_ZERO_ERROR; + input16Len = utext_extract(fInputText, 0, fInputLength, nullptr, 0, &lengthStatus); // buffer overflow error + } + char16_t *inputChars = (char16_t *)uprv_malloc(sizeof(char16_t)*(input16Len)); + if (inputChars == nullptr) { + return dest; + } + + status = U_ZERO_ERROR; + utext_extract(fInputText, 0, fInputLength, inputChars, input16Len, &status); // not terminated warning + status = U_ZERO_ERROR; + utext_replace(dest, 0, utext_nativeLength(dest), inputChars, input16Len, &status); + + uprv_free(inputChars); + } + return dest; + } else { + return utext_clone(nullptr, fInputText, false, true, &status); + } +} + + +static UBool compat_SyncMutableUTextContents(UText *ut); +static UBool compat_SyncMutableUTextContents(UText *ut) { + UBool retVal = false; + + // In the following test, we're really only interested in whether the UText should switch + // between heap and stack allocation. If length hasn't changed, we won't, so the chunkContents + // will still point to the correct data. + if (utext_nativeLength(ut) != ut->nativeIndexingLimit) { + UnicodeString *us=(UnicodeString *)ut->context; + + // Update to the latest length. + // For example, (utext_nativeLength(ut) != ut->nativeIndexingLimit). + int32_t newLength = us->length(); + + // Update the chunk description. + // The buffer may have switched between stack- and heap-based. + ut->chunkContents = us->getBuffer(); + ut->chunkLength = newLength; + ut->chunkNativeLimit = newLength; + ut->nativeIndexingLimit = newLength; + retVal = true; + } + + return retVal; +} + +//-------------------------------------------------------------------------------- +// +// lookingAt() +// +//-------------------------------------------------------------------------------- +UBool RegexMatcher::lookingAt(UErrorCode &status) { + if (U_FAILURE(status)) { + return false; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return false; + } + + if (fInputUniStrMaybeMutable) { + if (compat_SyncMutableUTextContents(fInputText)) { + fInputLength = utext_nativeLength(fInputText); + reset(); + } + } + else { + resetPreserveRegion(); + } + if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { + MatchChunkAt((int32_t)fActiveStart, false, status); + } else { + MatchAt(fActiveStart, false, status); + } + return fMatch; +} + + +UBool RegexMatcher::lookingAt(int64_t start, UErrorCode &status) { + if (U_FAILURE(status)) { + return false; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return false; + } + reset(); + + if (start < 0) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + return false; + } + + if (fInputUniStrMaybeMutable) { + if (compat_SyncMutableUTextContents(fInputText)) { + fInputLength = utext_nativeLength(fInputText); + reset(); + } + } + + int64_t nativeStart; + nativeStart = start; + if (nativeStart < fActiveStart || nativeStart > fActiveLimit) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + return false; + } + + if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { + MatchChunkAt((int32_t)nativeStart, false, status); + } else { + MatchAt(nativeStart, false, status); + } + return fMatch; +} + + + +//-------------------------------------------------------------------------------- +// +// matches() +// +//-------------------------------------------------------------------------------- +UBool RegexMatcher::matches(UErrorCode &status) { + if (U_FAILURE(status)) { + return false; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return false; + } + + if (fInputUniStrMaybeMutable) { + if (compat_SyncMutableUTextContents(fInputText)) { + fInputLength = utext_nativeLength(fInputText); + reset(); + } + } + else { + resetPreserveRegion(); + } + + if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { + MatchChunkAt((int32_t)fActiveStart, true, status); + } else { + MatchAt(fActiveStart, true, status); + } + return fMatch; +} + + +UBool RegexMatcher::matches(int64_t start, UErrorCode &status) { + if (U_FAILURE(status)) { + return false; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return false; + } + reset(); + + if (start < 0) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + return false; + } + + if (fInputUniStrMaybeMutable) { + if (compat_SyncMutableUTextContents(fInputText)) { + fInputLength = utext_nativeLength(fInputText); + reset(); + } + } + + int64_t nativeStart; + nativeStart = start; + if (nativeStart < fActiveStart || nativeStart > fActiveLimit) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + return false; + } + + if (UTEXT_FULL_TEXT_IN_CHUNK(fInputText, fInputLength)) { + MatchChunkAt((int32_t)nativeStart, true, status); + } else { + MatchAt(nativeStart, true, status); + } + return fMatch; +} + + + +//-------------------------------------------------------------------------------- +// +// pattern +// +//-------------------------------------------------------------------------------- +const RegexPattern &RegexMatcher::pattern() const { + return *fPattern; +} + + + +//-------------------------------------------------------------------------------- +// +// region +// +//-------------------------------------------------------------------------------- +RegexMatcher &RegexMatcher::region(int64_t regionStart, int64_t regionLimit, int64_t startIndex, UErrorCode &status) { + if (U_FAILURE(status)) { + return *this; + } + + if (regionStart>regionLimit || regionStart<0 || regionLimit<0) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } + + int64_t nativeStart = regionStart; + int64_t nativeLimit = regionLimit; + if (nativeStart > fInputLength || nativeLimit > fInputLength) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } + + if (startIndex == -1) + this->reset(); + else + resetPreserveRegion(); + + fRegionStart = nativeStart; + fRegionLimit = nativeLimit; + fActiveStart = nativeStart; + fActiveLimit = nativeLimit; + + if (startIndex != -1) { + if (startIndex < fActiveStart || startIndex > fActiveLimit) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + } + fMatchEnd = startIndex; + } + + if (!fTransparentBounds) { + fLookStart = nativeStart; + fLookLimit = nativeLimit; + } + if (fAnchoringBounds) { + fAnchorStart = nativeStart; + fAnchorLimit = nativeLimit; + } + return *this; +} + +RegexMatcher &RegexMatcher::region(int64_t start, int64_t limit, UErrorCode &status) { + return region(start, limit, -1, status); +} + +//-------------------------------------------------------------------------------- +// +// regionEnd +// +//-------------------------------------------------------------------------------- +int32_t RegexMatcher::regionEnd() const { + return (int32_t)fRegionLimit; +} + +int64_t RegexMatcher::regionEnd64() const { + return fRegionLimit; +} + +//-------------------------------------------------------------------------------- +// +// regionStart +// +//-------------------------------------------------------------------------------- +int32_t RegexMatcher::regionStart() const { + return (int32_t)fRegionStart; +} + +int64_t RegexMatcher::regionStart64() const { + return fRegionStart; +} + + +//-------------------------------------------------------------------------------- +// +// replaceAll +// +//-------------------------------------------------------------------------------- +UnicodeString RegexMatcher::replaceAll(const UnicodeString &replacement, UErrorCode &status) { + UText replacementText = UTEXT_INITIALIZER; + UText resultText = UTEXT_INITIALIZER; + UnicodeString resultString; + if (U_FAILURE(status)) { + return resultString; + } + + utext_openConstUnicodeString(&replacementText, &replacement, &status); + utext_openUnicodeString(&resultText, &resultString, &status); + + replaceAll(&replacementText, &resultText, status); + + utext_close(&resultText); + utext_close(&replacementText); + + return resultString; +} + + +// +// replaceAll, UText mode +// +UText *RegexMatcher::replaceAll(UText *replacement, UText *dest, UErrorCode &status) { + if (U_FAILURE(status)) { + return dest; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return dest; + } + + if (dest == nullptr) { + UnicodeString emptyString; + UText empty = UTEXT_INITIALIZER; + + utext_openUnicodeString(&empty, &emptyString, &status); + dest = utext_clone(nullptr, &empty, true, false, &status); + utext_close(&empty); + } + + if (U_SUCCESS(status)) { + reset(); + while (find()) { + appendReplacement(dest, replacement, status); + if (U_FAILURE(status)) { + break; + } + } + appendTail(dest, status); + } + + return dest; +} + + +//-------------------------------------------------------------------------------- +// +// replaceFirst +// +//-------------------------------------------------------------------------------- +UnicodeString RegexMatcher::replaceFirst(const UnicodeString &replacement, UErrorCode &status) { + UText replacementText = UTEXT_INITIALIZER; + UText resultText = UTEXT_INITIALIZER; + UnicodeString resultString; + + utext_openConstUnicodeString(&replacementText, &replacement, &status); + utext_openUnicodeString(&resultText, &resultString, &status); + + replaceFirst(&replacementText, &resultText, status); + + utext_close(&resultText); + utext_close(&replacementText); + + return resultString; +} + +// +// replaceFirst, UText mode +// +UText *RegexMatcher::replaceFirst(UText *replacement, UText *dest, UErrorCode &status) { + if (U_FAILURE(status)) { + return dest; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return dest; + } + + reset(); + if (!find()) { + return getInput(dest, status); + } + + if (dest == nullptr) { + UnicodeString emptyString; + UText empty = UTEXT_INITIALIZER; + + utext_openUnicodeString(&empty, &emptyString, &status); + dest = utext_clone(nullptr, &empty, true, false, &status); + utext_close(&empty); + } + + appendReplacement(dest, replacement, status); + appendTail(dest, status); + + return dest; +} + + +//-------------------------------------------------------------------------------- +// +// requireEnd +// +//-------------------------------------------------------------------------------- +UBool RegexMatcher::requireEnd() const { + return fRequireEnd; +} + + +//-------------------------------------------------------------------------------- +// +// reset +// +//-------------------------------------------------------------------------------- +RegexMatcher &RegexMatcher::reset() { + fRegionStart = 0; + fRegionLimit = fInputLength; + fActiveStart = 0; + fActiveLimit = fInputLength; + fAnchorStart = 0; + fAnchorLimit = fInputLength; + fLookStart = 0; + fLookLimit = fInputLength; + resetPreserveRegion(); + return *this; +} + + + +void RegexMatcher::resetPreserveRegion() { + fMatchStart = 0; + fMatchEnd = 0; + fLastMatchEnd = -1; + fAppendPosition = 0; + fMatch = false; + fHitEnd = false; + fRequireEnd = false; + fTime = 0; + fTickCounter = TIMER_INITIAL_VALUE; + //resetStack(); // more expensive than it looks... +} + + +RegexMatcher &RegexMatcher::reset(const UnicodeString &input) { + fInputText = utext_openConstUnicodeString(fInputText, &input, &fDeferredStatus); + if (fPattern->fNeedsAltInput) { + fAltInputText = utext_clone(fAltInputText, fInputText, false, true, &fDeferredStatus); + } + if (U_FAILURE(fDeferredStatus)) { + return *this; + } + fInputLength = utext_nativeLength(fInputText); + + reset(); + delete fInput; + fInput = nullptr; + + // Do the following for any UnicodeString. + // This is for compatibility for those clients who modify the input string "live" during regex operations. + fInputUniStrMaybeMutable = true; + +#if UCONFIG_NO_BREAK_ITERATION==0 + if (fWordBreakItr) { + fWordBreakItr->setText(fInputText, fDeferredStatus); + } + if (fGCBreakItr) { + fGCBreakItr->setText(fInputText, fDeferredStatus); + } +#endif + + return *this; +} + + +RegexMatcher &RegexMatcher::reset(UText *input) { + if (fInputText != input) { + fInputText = utext_clone(fInputText, input, false, true, &fDeferredStatus); + if (fPattern->fNeedsAltInput) fAltInputText = utext_clone(fAltInputText, fInputText, false, true, &fDeferredStatus); + if (U_FAILURE(fDeferredStatus)) { + return *this; + } + fInputLength = utext_nativeLength(fInputText); + + delete fInput; + fInput = nullptr; + +#if UCONFIG_NO_BREAK_ITERATION==0 + if (fWordBreakItr) { + fWordBreakItr->setText(input, fDeferredStatus); + } + if (fGCBreakItr) { + fGCBreakItr->setText(fInputText, fDeferredStatus); + } +#endif + } + reset(); + fInputUniStrMaybeMutable = false; + + return *this; +} + +/*RegexMatcher &RegexMatcher::reset(const char16_t *) { + fDeferredStatus = U_INTERNAL_PROGRAM_ERROR; + return *this; +}*/ + +RegexMatcher &RegexMatcher::reset(int64_t position, UErrorCode &status) { + if (U_FAILURE(status)) { + return *this; + } + reset(); // Reset also resets the region to be the entire string. + + if (position < 0 || position > fActiveLimit) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + return *this; + } + fMatchEnd = position; + return *this; +} + + +//-------------------------------------------------------------------------------- +// +// refresh +// +//-------------------------------------------------------------------------------- +RegexMatcher &RegexMatcher::refreshInputText(UText *input, UErrorCode &status) { + if (U_FAILURE(status)) { + return *this; + } + if (input == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return *this; + } + if (utext_nativeLength(fInputText) != utext_nativeLength(input)) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return *this; + } + int64_t pos = utext_getNativeIndex(fInputText); + // Shallow read-only clone of the new UText into the existing input UText + fInputText = utext_clone(fInputText, input, false, true, &status); + if (U_FAILURE(status)) { + return *this; + } + utext_setNativeIndex(fInputText, pos); + + if (fAltInputText != nullptr) { + pos = utext_getNativeIndex(fAltInputText); + fAltInputText = utext_clone(fAltInputText, input, false, true, &status); + if (U_FAILURE(status)) { + return *this; + } + utext_setNativeIndex(fAltInputText, pos); + } + return *this; +} + + + +//-------------------------------------------------------------------------------- +// +// setTrace +// +//-------------------------------------------------------------------------------- +void RegexMatcher::setTrace(UBool state) { + fTraceDebug = state; +} + + + +/** + * UText, replace entire contents of the destination UText with a substring of the source UText. + * + * @param src The source UText + * @param dest The destination UText. Must be writable. + * May be nullptr, in which case a new UText will be allocated. + * @param start Start index of source substring. + * @param limit Limit index of source substring. + * @param status An error code. + */ +static UText *utext_extract_replace(UText *src, UText *dest, int64_t start, int64_t limit, UErrorCode *status) { + if (U_FAILURE(*status)) { + return dest; + } + if (start == limit) { + if (dest) { + utext_replace(dest, 0, utext_nativeLength(dest), nullptr, 0, status); + return dest; + } else { + return utext_openUChars(nullptr, nullptr, 0, status); + } + } + int32_t length = utext_extract(src, start, limit, nullptr, 0, status); + if (*status != U_BUFFER_OVERFLOW_ERROR && U_FAILURE(*status)) { + return dest; + } + *status = U_ZERO_ERROR; + MaybeStackArray buffer; + if (length >= buffer.getCapacity()) { + char16_t *newBuf = buffer.resize(length+1); // Leave space for terminating Nul. + if (newBuf == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + } + } + utext_extract(src, start, limit, buffer.getAlias(), length+1, status); + if (dest) { + utext_replace(dest, 0, utext_nativeLength(dest), buffer.getAlias(), length, status); + return dest; + } + + // Caller did not provide a preexisting UText. + // Open a new one, and have it adopt the text buffer storage. + if (U_FAILURE(*status)) { + return nullptr; + } + int32_t ownedLength = 0; + char16_t *ownedBuf = buffer.orphanOrClone(length+1, ownedLength); + if (ownedBuf == nullptr) { + *status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + UText *result = utext_openUChars(nullptr, ownedBuf, length, status); + if (U_FAILURE(*status)) { + uprv_free(ownedBuf); + return nullptr; + } + result->providerProperties |= (1 << UTEXT_PROVIDER_OWNS_TEXT); + return result; +} + + +//--------------------------------------------------------------------- +// +// split +// +//--------------------------------------------------------------------- +int32_t RegexMatcher::split(const UnicodeString &input, + UnicodeString dest[], + int32_t destCapacity, + UErrorCode &status) +{ + UText inputText = UTEXT_INITIALIZER; + utext_openConstUnicodeString(&inputText, &input, &status); + if (U_FAILURE(status)) { + return 0; + } + + UText **destText = (UText **)uprv_malloc(sizeof(UText*)*destCapacity); + if (destText == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + int32_t i; + for (i = 0; i < destCapacity; i++) { + destText[i] = utext_openUnicodeString(nullptr, &dest[i], &status); + } + + int32_t fieldCount = split(&inputText, destText, destCapacity, status); + + for (i = 0; i < destCapacity; i++) { + utext_close(destText[i]); + } + + uprv_free(destText); + utext_close(&inputText); + return fieldCount; +} + +// +// split, UText mode +// +int32_t RegexMatcher::split(UText *input, + UText *dest[], + int32_t destCapacity, + UErrorCode &status) +{ + // + // Check arguments for validity + // + if (U_FAILURE(status)) { + return 0; + } + + if (destCapacity < 1) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + // + // Reset for the input text + // + reset(input); + int64_t nextOutputStringStart = 0; + if (fActiveLimit == 0) { + return 0; + } + + // + // Loop through the input text, searching for the delimiter pattern + // + int32_t i; + int32_t numCaptureGroups = fPattern->fGroupMap->size(); + for (i=0; ; i++) { + if (i>=destCapacity-1) { + // There is one or zero output string left. + // Fill the last output string with whatever is left from the input, then exit the loop. + // ( i will be == destCapacity if we filled the output array while processing + // capture groups of the delimiter expression, in which case we will discard the + // last capture group saved in favor of the unprocessed remainder of the + // input string.) + i = destCapacity-1; + if (fActiveLimit > nextOutputStringStart) { + if (UTEXT_FULL_TEXT_IN_CHUNK(input, fInputLength)) { + if (dest[i]) { + utext_replace(dest[i], 0, utext_nativeLength(dest[i]), + input->chunkContents+nextOutputStringStart, + (int32_t)(fActiveLimit-nextOutputStringStart), &status); + } else { + UText remainingText = UTEXT_INITIALIZER; + utext_openUChars(&remainingText, input->chunkContents+nextOutputStringStart, + fActiveLimit-nextOutputStringStart, &status); + dest[i] = utext_clone(nullptr, &remainingText, true, false, &status); + utext_close(&remainingText); + } + } else { + UErrorCode lengthStatus = U_ZERO_ERROR; + int32_t remaining16Length = + utext_extract(input, nextOutputStringStart, fActiveLimit, nullptr, 0, &lengthStatus); + char16_t *remainingChars = (char16_t *)uprv_malloc(sizeof(char16_t)*(remaining16Length+1)); + if (remainingChars == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + break; + } + + utext_extract(input, nextOutputStringStart, fActiveLimit, remainingChars, remaining16Length+1, &status); + if (dest[i]) { + utext_replace(dest[i], 0, utext_nativeLength(dest[i]), remainingChars, remaining16Length, &status); + } else { + UText remainingText = UTEXT_INITIALIZER; + utext_openUChars(&remainingText, remainingChars, remaining16Length, &status); + dest[i] = utext_clone(nullptr, &remainingText, true, false, &status); + utext_close(&remainingText); + } + + uprv_free(remainingChars); + } + } + break; + } + if (find()) { + // We found another delimiter. Move everything from where we started looking + // up until the start of the delimiter into the next output string. + if (UTEXT_FULL_TEXT_IN_CHUNK(input, fInputLength)) { + if (dest[i]) { + utext_replace(dest[i], 0, utext_nativeLength(dest[i]), + input->chunkContents+nextOutputStringStart, + (int32_t)(fMatchStart-nextOutputStringStart), &status); + } else { + UText remainingText = UTEXT_INITIALIZER; + utext_openUChars(&remainingText, input->chunkContents+nextOutputStringStart, + fMatchStart-nextOutputStringStart, &status); + dest[i] = utext_clone(nullptr, &remainingText, true, false, &status); + utext_close(&remainingText); + } + } else { + UErrorCode lengthStatus = U_ZERO_ERROR; + int32_t remaining16Length = utext_extract(input, nextOutputStringStart, fMatchStart, nullptr, 0, &lengthStatus); + char16_t *remainingChars = (char16_t *)uprv_malloc(sizeof(char16_t)*(remaining16Length+1)); + if (remainingChars == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + break; + } + utext_extract(input, nextOutputStringStart, fMatchStart, remainingChars, remaining16Length+1, &status); + if (dest[i]) { + utext_replace(dest[i], 0, utext_nativeLength(dest[i]), remainingChars, remaining16Length, &status); + } else { + UText remainingText = UTEXT_INITIALIZER; + utext_openUChars(&remainingText, remainingChars, remaining16Length, &status); + dest[i] = utext_clone(nullptr, &remainingText, true, false, &status); + utext_close(&remainingText); + } + + uprv_free(remainingChars); + } + nextOutputStringStart = fMatchEnd; + + // If the delimiter pattern has capturing parentheses, the captured + // text goes out into the next n destination strings. + int32_t groupNum; + for (groupNum=1; groupNum<=numCaptureGroups; groupNum++) { + if (i >= destCapacity-2) { + // Never fill the last available output string with capture group text. + // It will filled with the last field, the remainder of the + // unsplit input text. + break; + } + i++; + dest[i] = utext_extract_replace(fInputText, dest[i], + start64(groupNum, status), end64(groupNum, status), &status); + } + + if (nextOutputStringStart == fActiveLimit) { + // The delimiter was at the end of the string. We're done, but first + // we output one last empty string, for the empty field following + // the delimiter at the end of input. + if (i+1 < destCapacity) { + ++i; + if (dest[i] == nullptr) { + dest[i] = utext_openUChars(nullptr, nullptr, 0, &status); + } else { + static const char16_t emptyString[] = {(char16_t)0}; + utext_replace(dest[i], 0, utext_nativeLength(dest[i]), emptyString, 0, &status); + } + } + break; + + } + } + else + { + // We ran off the end of the input while looking for the next delimiter. + // All the remaining text goes into the current output string. + if (UTEXT_FULL_TEXT_IN_CHUNK(input, fInputLength)) { + if (dest[i]) { + utext_replace(dest[i], 0, utext_nativeLength(dest[i]), + input->chunkContents+nextOutputStringStart, + (int32_t)(fActiveLimit-nextOutputStringStart), &status); + } else { + UText remainingText = UTEXT_INITIALIZER; + utext_openUChars(&remainingText, input->chunkContents+nextOutputStringStart, + fActiveLimit-nextOutputStringStart, &status); + dest[i] = utext_clone(nullptr, &remainingText, true, false, &status); + utext_close(&remainingText); + } + } else { + UErrorCode lengthStatus = U_ZERO_ERROR; + int32_t remaining16Length = utext_extract(input, nextOutputStringStart, fActiveLimit, nullptr, 0, &lengthStatus); + char16_t *remainingChars = (char16_t *)uprv_malloc(sizeof(char16_t)*(remaining16Length+1)); + if (remainingChars == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + break; + } + + utext_extract(input, nextOutputStringStart, fActiveLimit, remainingChars, remaining16Length+1, &status); + if (dest[i]) { + utext_replace(dest[i], 0, utext_nativeLength(dest[i]), remainingChars, remaining16Length, &status); + } else { + UText remainingText = UTEXT_INITIALIZER; + utext_openUChars(&remainingText, remainingChars, remaining16Length, &status); + dest[i] = utext_clone(nullptr, &remainingText, true, false, &status); + utext_close(&remainingText); + } + + uprv_free(remainingChars); + } + break; + } + if (U_FAILURE(status)) { + break; + } + } // end of for loop + return i+1; +} + + +//-------------------------------------------------------------------------------- +// +// start +// +//-------------------------------------------------------------------------------- +int32_t RegexMatcher::start(UErrorCode &status) const { + return start(0, status); +} + +int64_t RegexMatcher::start64(UErrorCode &status) const { + return start64(0, status); +} + +//-------------------------------------------------------------------------------- +// +// start(int32_t group, UErrorCode &status) +// +//-------------------------------------------------------------------------------- + +int64_t RegexMatcher::start64(int32_t group, UErrorCode &status) const { + if (U_FAILURE(status)) { + return -1; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return -1; + } + if (fMatch == false) { + status = U_REGEX_INVALID_STATE; + return -1; + } + if (group < 0 || group > fPattern->fGroupMap->size()) { + status = U_INDEX_OUTOFBOUNDS_ERROR; + return -1; + } + int64_t s; + if (group == 0) { + s = fMatchStart; + } else { + int32_t groupOffset = fPattern->fGroupMap->elementAti(group-1); + U_ASSERT(groupOffset < fPattern->fFrameSize); + U_ASSERT(groupOffset >= 0); + s = fFrame->fExtra[groupOffset]; + } + + return s; +} + + +int32_t RegexMatcher::start(int32_t group, UErrorCode &status) const { + return (int32_t)start64(group, status); +} + +//-------------------------------------------------------------------------------- +// +// useAnchoringBounds +// +//-------------------------------------------------------------------------------- +RegexMatcher &RegexMatcher::useAnchoringBounds(UBool b) { + fAnchoringBounds = b; + fAnchorStart = (fAnchoringBounds ? fRegionStart : 0); + fAnchorLimit = (fAnchoringBounds ? fRegionLimit : fInputLength); + return *this; +} + + +//-------------------------------------------------------------------------------- +// +// useTransparentBounds +// +//-------------------------------------------------------------------------------- +RegexMatcher &RegexMatcher::useTransparentBounds(UBool b) { + fTransparentBounds = b; + fLookStart = (fTransparentBounds ? 0 : fRegionStart); + fLookLimit = (fTransparentBounds ? fInputLength : fRegionLimit); + return *this; +} + +//-------------------------------------------------------------------------------- +// +// setTimeLimit +// +//-------------------------------------------------------------------------------- +void RegexMatcher::setTimeLimit(int32_t limit, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return; + } + if (limit < 0) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + fTimeLimit = limit; +} + + +//-------------------------------------------------------------------------------- +// +// getTimeLimit +// +//-------------------------------------------------------------------------------- +int32_t RegexMatcher::getTimeLimit() const { + return fTimeLimit; +} + + +//-------------------------------------------------------------------------------- +// +// setStackLimit +// +//-------------------------------------------------------------------------------- +void RegexMatcher::setStackLimit(int32_t limit, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return; + } + if (limit < 0) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + // Reset the matcher. This is needed here in case there is a current match + // whose final stack frame (containing the match results, pointed to by fFrame) + // would be lost by resizing to a smaller stack size. + reset(); + + if (limit == 0) { + // Unlimited stack expansion + fStack->setMaxCapacity(0); + } else { + // Change the units of the limit from bytes to ints, and bump the size up + // to be big enough to hold at least one stack frame for the pattern, + // if it isn't there already. + int32_t adjustedLimit = limit / sizeof(int32_t); + if (adjustedLimit < fPattern->fFrameSize) { + adjustedLimit = fPattern->fFrameSize; + } + fStack->setMaxCapacity(adjustedLimit); + } + fStackLimit = limit; +} + + +//-------------------------------------------------------------------------------- +// +// getStackLimit +// +//-------------------------------------------------------------------------------- +int32_t RegexMatcher::getStackLimit() const { + return fStackLimit; +} + + +//-------------------------------------------------------------------------------- +// +// setMatchCallback +// +//-------------------------------------------------------------------------------- +void RegexMatcher::setMatchCallback(URegexMatchCallback *callback, + const void *context, + UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + fCallbackFn = callback; + fCallbackContext = context; +} + + +//-------------------------------------------------------------------------------- +// +// getMatchCallback +// +//-------------------------------------------------------------------------------- +void RegexMatcher::getMatchCallback(URegexMatchCallback *&callback, + const void *&context, + UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + callback = fCallbackFn; + context = fCallbackContext; +} + + +//-------------------------------------------------------------------------------- +// +// setMatchCallback +// +//-------------------------------------------------------------------------------- +void RegexMatcher::setFindProgressCallback(URegexFindProgressCallback *callback, + const void *context, + UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + fFindProgressCallbackFn = callback; + fFindProgressCallbackContext = context; +} + + +//-------------------------------------------------------------------------------- +// +// getMatchCallback +// +//-------------------------------------------------------------------------------- +void RegexMatcher::getFindProgressCallback(URegexFindProgressCallback *&callback, + const void *&context, + UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + callback = fFindProgressCallbackFn; + context = fFindProgressCallbackContext; +} + + +//================================================================================ +// +// Code following this point in this file is the internal +// Match Engine Implementation. +// +//================================================================================ + + +//-------------------------------------------------------------------------------- +// +// resetStack +// Discard any previous contents of the state save stack, and initialize a +// new stack frame to all -1. The -1s are needed for capture group limits, +// where they indicate that a group has not yet matched anything. +//-------------------------------------------------------------------------------- +REStackFrame *RegexMatcher::resetStack() { + // Discard any previous contents of the state save stack, and initialize a + // new stack frame with all -1 data. The -1s are needed for capture group limits, + // where they indicate that a group has not yet matched anything. + fStack->removeAllElements(); + + REStackFrame *iFrame = (REStackFrame *)fStack->reserveBlock(fPattern->fFrameSize, fDeferredStatus); + if(U_FAILURE(fDeferredStatus)) { + return nullptr; + } + + int32_t i; + for (i=0; ifFrameSize-RESTACKFRAME_HDRCOUNT; i++) { + iFrame->fExtra[i] = -1; + } + return iFrame; +} + + + +//-------------------------------------------------------------------------------- +// +// isWordBoundary +// in perl, "xab..cd..", \b is true at positions 0,3,5,7 +// For us, +// If the current char is a combining mark, +// \b is false. +// Else Scan backwards to the first non-combining char. +// We are at a boundary if the this char and the original chars are +// opposite in membership in \w set +// +// parameters: pos - the current position in the input buffer +// +// TODO: double-check edge cases at region boundaries. +// +//-------------------------------------------------------------------------------- +UBool RegexMatcher::isWordBoundary(int64_t pos) { + UBool isBoundary = false; + UBool cIsWord = false; + + if (pos >= fLookLimit) { + fHitEnd = true; + } else { + // Determine whether char c at current position is a member of the word set of chars. + // If we're off the end of the string, behave as though we're not at a word char. + UTEXT_SETNATIVEINDEX(fInputText, pos); + UChar32 c = UTEXT_CURRENT32(fInputText); + if (u_hasBinaryProperty(c, UCHAR_GRAPHEME_EXTEND) || u_charType(c) == U_FORMAT_CHAR) { + // Current char is a combining one. Not a boundary. + return false; + } + cIsWord = RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET].contains(c); + } + + // Back up until we come to a non-combining char, determine whether + // that char is a word char. + UBool prevCIsWord = false; + for (;;) { + if (UTEXT_GETNATIVEINDEX(fInputText) <= fLookStart) { + break; + } + UChar32 prevChar = UTEXT_PREVIOUS32(fInputText); + if (!(u_hasBinaryProperty(prevChar, UCHAR_GRAPHEME_EXTEND) + || u_charType(prevChar) == U_FORMAT_CHAR)) { + prevCIsWord = RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET].contains(prevChar); + break; + } + } + isBoundary = cIsWord ^ prevCIsWord; + return isBoundary; +} + +UBool RegexMatcher::isChunkWordBoundary(int32_t pos) { + UBool isBoundary = false; + UBool cIsWord = false; + + const char16_t *inputBuf = fInputText->chunkContents; + + if (pos >= fLookLimit) { + fHitEnd = true; + } else { + // Determine whether char c at current position is a member of the word set of chars. + // If we're off the end of the string, behave as though we're not at a word char. + UChar32 c; + U16_GET(inputBuf, fLookStart, pos, fLookLimit, c); + if (u_hasBinaryProperty(c, UCHAR_GRAPHEME_EXTEND) || u_charType(c) == U_FORMAT_CHAR) { + // Current char is a combining one. Not a boundary. + return false; + } + cIsWord = RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET].contains(c); + } + + // Back up until we come to a non-combining char, determine whether + // that char is a word char. + UBool prevCIsWord = false; + for (;;) { + if (pos <= fLookStart) { + break; + } + UChar32 prevChar; + U16_PREV(inputBuf, fLookStart, pos, prevChar); + if (!(u_hasBinaryProperty(prevChar, UCHAR_GRAPHEME_EXTEND) + || u_charType(prevChar) == U_FORMAT_CHAR)) { + prevCIsWord = RegexStaticSets::gStaticSets->fPropSets[URX_ISWORD_SET].contains(prevChar); + break; + } + } + isBoundary = cIsWord ^ prevCIsWord; + return isBoundary; +} + +//-------------------------------------------------------------------------------- +// +// isUWordBoundary +// +// Test for a word boundary using RBBI word break. +// +// parameters: pos - the current position in the input buffer +// +//-------------------------------------------------------------------------------- +UBool RegexMatcher::isUWordBoundary(int64_t pos, UErrorCode &status) { + UBool returnVal = false; + +#if UCONFIG_NO_BREAK_ITERATION==0 + // Note: this point will never be reached if break iteration is configured out. + // Regex patterns that would require this function will fail to compile. + + // If we haven't yet created a break iterator for this matcher, do it now. + if (fWordBreakItr == nullptr) { + fWordBreakItr = BreakIterator::createWordInstance(Locale::getEnglish(), status); + if (U_FAILURE(status)) { + return false; + } + fWordBreakItr->setText(fInputText, status); + } + + // Note: zero width boundary tests like \b see through transparent region bounds, + // which is why fLookLimit is used here, rather than fActiveLimit. + if (pos >= fLookLimit) { + fHitEnd = true; + returnVal = true; // With Unicode word rules, only positions within the interior of "real" + // words are not boundaries. All non-word chars stand by themselves, + // with word boundaries on both sides. + } else { + returnVal = fWordBreakItr->isBoundary((int32_t)pos); + } +#endif + return returnVal; +} + + +int64_t RegexMatcher::followingGCBoundary(int64_t pos, UErrorCode &status) { + int64_t result = pos; + +#if UCONFIG_NO_BREAK_ITERATION==0 + // Note: this point will never be reached if break iteration is configured out. + // Regex patterns that would require this function will fail to compile. + + // If we haven't yet created a break iterator for this matcher, do it now. + if (fGCBreakItr == nullptr) { + fGCBreakItr = BreakIterator::createCharacterInstance(Locale::getEnglish(), status); + if (U_FAILURE(status)) { + return pos; + } + fGCBreakItr->setText(fInputText, status); + } + result = fGCBreakItr->following(pos); + if (result == BreakIterator::DONE) { + result = pos; + } +#endif + return result; +} + +//-------------------------------------------------------------------------------- +// +// IncrementTime This function is called once each TIMER_INITIAL_VALUE state +// saves. Increment the "time" counter, and call the +// user callback function if there is one installed. +// +// If the match operation needs to be aborted, either for a time-out +// or because the user callback asked for it, just set an error status. +// The engine will pick that up and stop in its outer loop. +// +//-------------------------------------------------------------------------------- +void RegexMatcher::IncrementTime(UErrorCode &status) { + fTickCounter = TIMER_INITIAL_VALUE; + fTime++; + if (fCallbackFn != nullptr) { + if ((*fCallbackFn)(fCallbackContext, fTime) == false) { + status = U_REGEX_STOPPED_BY_CALLER; + return; + } + } + if (fTimeLimit > 0 && fTime >= fTimeLimit) { + status = U_REGEX_TIME_OUT; + } +} + +//-------------------------------------------------------------------------------- +// +// StateSave +// Make a new stack frame, initialized as a copy of the current stack frame. +// Set the pattern index in the original stack frame from the operand value +// in the opcode. Execution of the engine continues with the state in +// the newly created stack frame +// +// Note that reserveBlock() may grow the stack, resulting in the +// whole thing being relocated in memory. +// +// Parameters: +// fp The top frame pointer when called. At return, a new +// fame will be present +// savePatIdx An index into the compiled pattern. Goes into the original +// (not new) frame. If execution ever back-tracks out of the +// new frame, this will be where we continue from in the pattern. +// Return +// The new frame pointer. +// +//-------------------------------------------------------------------------------- +inline REStackFrame *RegexMatcher::StateSave(REStackFrame *fp, int64_t savePatIdx, UErrorCode &status) { + if (U_FAILURE(status)) { + return fp; + } + // push storage for a new frame. + int64_t *newFP = fStack->reserveBlock(fFrameSize, status); + if (U_FAILURE(status)) { + // Failure on attempted stack expansion. + // Stack function set some other error code, change it to a more + // specific one for regular expressions. + status = U_REGEX_STACK_OVERFLOW; + // We need to return a writable stack frame, so just return the + // previous frame. The match operation will stop quickly + // because of the error status, after which the frame will never + // be looked at again. + return fp; + } + fp = (REStackFrame *)(newFP - fFrameSize); // in case of realloc of stack. + + // New stack frame = copy of old top frame. + int64_t *source = (int64_t *)fp; + int64_t *dest = newFP; + for (;;) { + *dest++ = *source++; + if (source == newFP) { + break; + } + } + + fTickCounter--; + if (fTickCounter <= 0) { + IncrementTime(status); // Re-initializes fTickCounter + } + fp->fPatIdx = savePatIdx; + return (REStackFrame *)newFP; +} + +#if defined(REGEX_DEBUG) +namespace { +UnicodeString StringFromUText(UText *ut) { + UnicodeString result; + for (UChar32 c = utext_next32From(ut, 0); c != U_SENTINEL; c = UTEXT_NEXT32(ut)) { + result.append(c); + } + return result; +} +} +#endif // REGEX_DEBUG + + +//-------------------------------------------------------------------------------- +// +// MatchAt This is the actual matching engine. +// +// startIdx: begin matching a this index. +// toEnd: if true, match must extend to end of the input region +// +//-------------------------------------------------------------------------------- +void RegexMatcher::MatchAt(int64_t startIdx, UBool toEnd, UErrorCode &status) { + UBool isMatch = false; // True if the we have a match. + + int64_t backSearchIndex = U_INT64_MAX; // used after greedy single-character matches for searching backwards + + int32_t op; // Operation from the compiled pattern, split into + int32_t opType; // the opcode + int32_t opValue; // and the operand value. + +#ifdef REGEX_RUN_DEBUG + if (fTraceDebug) { + printf("MatchAt(startIdx=%ld)\n", startIdx); + printf("Original Pattern: \"%s\"\n", CStr(StringFromUText(fPattern->fPattern))()); + printf("Input String: \"%s\"\n\n", CStr(StringFromUText(fInputText))()); + } +#endif + + if (U_FAILURE(status)) { + return; + } + + // Cache frequently referenced items from the compiled pattern + // + int64_t *pat = fPattern->fCompiledPat->getBuffer(); + + const char16_t *litText = fPattern->fLiteralText.getBuffer(); + UVector *fSets = fPattern->fSets; + + fFrameSize = fPattern->fFrameSize; + REStackFrame *fp = resetStack(); + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return; + } + + fp->fPatIdx = 0; + fp->fInputIdx = startIdx; + + // Zero out the pattern's static data + int32_t i; + for (i = 0; ifDataSize; i++) { + fData[i] = 0; + } + + // + // Main loop for interpreting the compiled pattern. + // One iteration of the loop per pattern operation performed. + // + for (;;) { + op = (int32_t)pat[fp->fPatIdx]; + opType = URX_TYPE(op); + opValue = URX_VAL(op); +#ifdef REGEX_RUN_DEBUG + if (fTraceDebug) { + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + printf("inputIdx=%ld inputChar=%x sp=%3ld activeLimit=%ld ", fp->fInputIdx, + UTEXT_CURRENT32(fInputText), (int64_t *)fp-fStack->getBuffer(), fActiveLimit); + fPattern->dumpOp(fp->fPatIdx); + } +#endif + fp->fPatIdx++; + + switch (opType) { + + + case URX_NOP: + break; + + + case URX_BACKTRACK: + // Force a backtrack. In some circumstances, the pattern compiler + // will notice that the pattern can't possibly match anything, and will + // emit one of these at that point. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + + + case URX_ONECHAR: + if (fp->fInputIdx < fActiveLimit) { + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + UChar32 c = UTEXT_NEXT32(fInputText); + if (c == opValue) { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + break; + } + } else { + fHitEnd = true; + } + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + + + case URX_STRING: + { + // Test input against a literal string. + // Strings require two slots in the compiled pattern, one for the + // offset to the string text, and one for the length. + + int32_t stringStartIdx = opValue; + op = (int32_t)pat[fp->fPatIdx]; // Fetch the second operand + fp->fPatIdx++; + opType = URX_TYPE(op); + int32_t stringLen = URX_VAL(op); + U_ASSERT(opType == URX_STRING_LEN); + U_ASSERT(stringLen >= 2); + + const char16_t *patternString = litText+stringStartIdx; + int32_t patternStringIndex = 0; + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + UChar32 inputChar; + UChar32 patternChar; + UBool success = true; + while (patternStringIndex < stringLen) { + if (UTEXT_GETNATIVEINDEX(fInputText) >= fActiveLimit) { + success = false; + fHitEnd = true; + break; + } + inputChar = UTEXT_NEXT32(fInputText); + U16_NEXT(patternString, patternStringIndex, stringLen, patternChar); + if (patternChar != inputChar) { + success = false; + break; + } + } + + if (success) { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_STATE_SAVE: + fp = StateSave(fp, opValue, status); + break; + + + case URX_END: + // The match loop will exit via this path on a successful match, + // when we reach the end of the pattern. + if (toEnd && fp->fInputIdx != fActiveLimit) { + // The pattern matched, but not to the end of input. Try some more. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + isMatch = true; + goto breakFromLoop; + + // Start and End Capture stack frame variables are laid out out like this: + // fp->fExtra[opValue] - The start of a completed capture group + // opValue+1 - The end of a completed capture group + // opValue+2 - the start of a capture group whose end + // has not yet been reached (and might not ever be). + case URX_START_CAPTURE: + U_ASSERT(opValue >= 0 && opValue < fFrameSize-3); + fp->fExtra[opValue+2] = fp->fInputIdx; + break; + + + case URX_END_CAPTURE: + U_ASSERT(opValue >= 0 && opValue < fFrameSize-3); + U_ASSERT(fp->fExtra[opValue+2] >= 0); // Start pos for this group must be set. + fp->fExtra[opValue] = fp->fExtra[opValue+2]; // Tentative start becomes real. + fp->fExtra[opValue+1] = fp->fInputIdx; // End position + U_ASSERT(fp->fExtra[opValue] <= fp->fExtra[opValue+1]); + break; + + + case URX_DOLLAR: // $, test for End of line + // or for position before new line at end of input + { + if (fp->fInputIdx >= fAnchorLimit) { + // We really are at the end of input. Success. + fHitEnd = true; + fRequireEnd = true; + break; + } + + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + + // If we are positioned just before a new-line that is located at the + // end of input, succeed. + UChar32 c = UTEXT_NEXT32(fInputText); + if (UTEXT_GETNATIVEINDEX(fInputText) >= fAnchorLimit) { + if (isLineTerminator(c)) { + // If not in the middle of a CR/LF sequence + if ( !(c==0x0a && fp->fInputIdx>fAnchorStart && ((void)UTEXT_PREVIOUS32(fInputText), UTEXT_PREVIOUS32(fInputText))==0x0d)) { + // At new-line at end of input. Success + fHitEnd = true; + fRequireEnd = true; + + break; + } + } + } else { + UChar32 nextC = UTEXT_NEXT32(fInputText); + if (c == 0x0d && nextC == 0x0a && UTEXT_GETNATIVEINDEX(fInputText) >= fAnchorLimit) { + fHitEnd = true; + fRequireEnd = true; + break; // At CR/LF at end of input. Success + } + } + + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_DOLLAR_D: // $, test for End of Line, in UNIX_LINES mode. + if (fp->fInputIdx >= fAnchorLimit) { + // Off the end of input. Success. + fHitEnd = true; + fRequireEnd = true; + break; + } else { + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + UChar32 c = UTEXT_NEXT32(fInputText); + // Either at the last character of input, or off the end. + if (c == 0x0a && UTEXT_GETNATIVEINDEX(fInputText) == fAnchorLimit) { + fHitEnd = true; + fRequireEnd = true; + break; + } + } + + // Not at end of input. Back-track out. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + + + case URX_DOLLAR_M: // $, test for End of line in multi-line mode + { + if (fp->fInputIdx >= fAnchorLimit) { + // We really are at the end of input. Success. + fHitEnd = true; + fRequireEnd = true; + break; + } + // If we are positioned just before a new-line, succeed. + // It makes no difference where the new-line is within the input. + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + UChar32 c = UTEXT_CURRENT32(fInputText); + if (isLineTerminator(c)) { + // At a line end, except for the odd chance of being in the middle of a CR/LF sequence + // In multi-line mode, hitting a new-line just before the end of input does not + // set the hitEnd or requireEnd flags + if ( !(c==0x0a && fp->fInputIdx>fAnchorStart && UTEXT_PREVIOUS32(fInputText)==0x0d)) { + break; + } + } + // not at a new line. Fail. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_DOLLAR_MD: // $, test for End of line in multi-line and UNIX_LINES mode + { + if (fp->fInputIdx >= fAnchorLimit) { + // We really are at the end of input. Success. + fHitEnd = true; + fRequireEnd = true; // Java set requireEnd in this case, even though + break; // adding a new-line would not lose the match. + } + // If we are not positioned just before a new-line, the test fails; backtrack out. + // It makes no difference where the new-line is within the input. + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + if (UTEXT_CURRENT32(fInputText) != 0x0a) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_CARET: // ^, test for start of line + if (fp->fInputIdx != fAnchorStart) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_CARET_M: // ^, test for start of line in mulit-line mode + { + if (fp->fInputIdx == fAnchorStart) { + // We are at the start input. Success. + break; + } + // Check whether character just before the current pos is a new-line + // unless we are at the end of input + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + UChar32 c = UTEXT_PREVIOUS32(fInputText); + if ((fp->fInputIdx < fAnchorLimit) && isLineTerminator(c)) { + // It's a new-line. ^ is true. Success. + // TODO: what should be done with positions between a CR and LF? + break; + } + // Not at the start of a line. Fail. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_CARET_M_UNIX: // ^, test for start of line in mulit-line + Unix-line mode + { + U_ASSERT(fp->fInputIdx >= fAnchorStart); + if (fp->fInputIdx <= fAnchorStart) { + // We are at the start input. Success. + break; + } + // Check whether character just before the current pos is a new-line + U_ASSERT(fp->fInputIdx <= fAnchorLimit); + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + UChar32 c = UTEXT_PREVIOUS32(fInputText); + if (c != 0x0a) { + // Not at the start of a line. Back-track out. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + case URX_BACKSLASH_B: // Test for word boundaries + { + UBool success = isWordBoundary(fp->fInputIdx); + success ^= (UBool)(opValue != 0); // flip sense for \B + if (!success) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_BACKSLASH_BU: // Test for word boundaries, Unicode-style + { + UBool success = isUWordBoundary(fp->fInputIdx, status); + success ^= (UBool)(opValue != 0); // flip sense for \B + if (!success) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_BACKSLASH_D: // Test for decimal digit + { + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + + UChar32 c = UTEXT_NEXT32(fInputText); + int8_t ctype = u_charType(c); // TODO: make a unicode set for this. Will be faster. + UBool success = (ctype == U_DECIMAL_DIGIT_NUMBER); + success ^= (UBool)(opValue != 0); // flip sense for \D + if (success) { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_BACKSLASH_G: // Test for position at end of previous match + if (!((fMatch && fp->fInputIdx==fMatchEnd) || (fMatch==false && fp->fInputIdx==fActiveStart))) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_BACKSLASH_H: // Test for \h, horizontal white space. + { + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + UChar32 c = UTEXT_NEXT32(fInputText); + int8_t ctype = u_charType(c); + UBool success = (ctype == U_SPACE_SEPARATOR || c == 9); // SPACE_SEPARATOR || TAB + success ^= (UBool)(opValue != 0); // flip sense for \H + if (success) { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_BACKSLASH_R: // Test for \R, any line break sequence. + { + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + UChar32 c = UTEXT_NEXT32(fInputText); + if (isLineTerminator(c)) { + if (c == 0x0d && utext_current32(fInputText) == 0x0a) { + utext_next32(fInputText); + } + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_BACKSLASH_V: // \v, any single line ending character. + { + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + UChar32 c = UTEXT_NEXT32(fInputText); + UBool success = isLineTerminator(c); + success ^= (UBool)(opValue != 0); // flip sense for \V + if (success) { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_BACKSLASH_X: + // Match a Grapheme, as defined by Unicode UAX 29. + + // Fail if at end of input + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + fp->fInputIdx = followingGCBoundary(fp->fInputIdx, status); + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp->fInputIdx = fActiveLimit; + } + break; + + + case URX_BACKSLASH_Z: // Test for end of Input + if (fp->fInputIdx < fAnchorLimit) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } else { + fHitEnd = true; + fRequireEnd = true; + } + break; + + + + case URX_STATIC_SETREF: + { + // Test input character against one of the predefined sets + // (Word Characters, for example) + // The high bit of the op value is a flag for the match polarity. + // 0: success if input char is in set. + // 1: success if input char is not in set. + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + UBool success = ((opValue & URX_NEG_SET) == URX_NEG_SET); + opValue &= ~URX_NEG_SET; + U_ASSERT(opValue > 0 && opValue < URX_LAST_SET); + + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + UChar32 c = UTEXT_NEXT32(fInputText); + if (c < 256) { + Regex8BitSet &s8 = RegexStaticSets::gStaticSets->fPropSets8[opValue]; + if (s8.contains(c)) { + success = !success; + } + } else { + const UnicodeSet &s = RegexStaticSets::gStaticSets->fPropSets[opValue]; + if (s.contains(c)) { + success = !success; + } + } + if (success) { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + } else { + // the character wasn't in the set. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_STAT_SETREF_N: + { + // Test input character for NOT being a member of one of + // the predefined sets (Word Characters, for example) + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + U_ASSERT(opValue > 0 && opValue < URX_LAST_SET); + + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + + UChar32 c = UTEXT_NEXT32(fInputText); + if (c < 256) { + Regex8BitSet &s8 = RegexStaticSets::gStaticSets->fPropSets8[opValue]; + if (s8.contains(c) == false) { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + break; + } + } else { + const UnicodeSet &s = RegexStaticSets::gStaticSets->fPropSets[opValue]; + if (s.contains(c) == false) { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + break; + } + } + // the character wasn't in the set. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_SETREF: + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } else { + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + + // There is input left. Pick up one char and test it for set membership. + UChar32 c = UTEXT_NEXT32(fInputText); + U_ASSERT(opValue > 0 && opValue < fSets->size()); + if (c<256) { + Regex8BitSet *s8 = &fPattern->fSets8[opValue]; + if (s8->contains(c)) { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + break; + } + } else { + UnicodeSet *s = (UnicodeSet *)fSets->elementAt(opValue); + if (s->contains(c)) { + // The character is in the set. A Match. + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + break; + } + } + + // the character wasn't in the set. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_DOTANY: + { + // . matches anything, but stops at end-of-line. + if (fp->fInputIdx >= fActiveLimit) { + // At end of input. Match failed. Backtrack out. + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + + // There is input left. Advance over one char, unless we've hit end-of-line + UChar32 c = UTEXT_NEXT32(fInputText); + if (isLineTerminator(c)) { + // End of line in normal mode. . does not match. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + } + break; + + + case URX_DOTANY_ALL: + { + // ., in dot-matches-all (including new lines) mode + if (fp->fInputIdx >= fActiveLimit) { + // At end of input. Match failed. Backtrack out. + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + + // There is input left. Advance over one char, except if we are + // at a cr/lf, advance over both of them. + UChar32 c; + c = UTEXT_NEXT32(fInputText); + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + if (c==0x0d && fp->fInputIdx < fActiveLimit) { + // In the case of a CR/LF, we need to advance over both. + UChar32 nextc = UTEXT_CURRENT32(fInputText); + if (nextc == 0x0a) { + (void)UTEXT_NEXT32(fInputText); + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + } + } + } + break; + + + case URX_DOTANY_UNIX: + { + // '.' operator, matches all, but stops at end-of-line. + // UNIX_LINES mode, so 0x0a is the only recognized line ending. + if (fp->fInputIdx >= fActiveLimit) { + // At end of input. Match failed. Backtrack out. + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + + // There is input left. Advance over one char, unless we've hit end-of-line + UChar32 c = UTEXT_NEXT32(fInputText); + if (c == 0x0a) { + // End of line in normal mode. '.' does not match the \n + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } else { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + } + } + break; + + + case URX_JMP: + fp->fPatIdx = opValue; + break; + + case URX_FAIL: + isMatch = false; + goto breakFromLoop; + + case URX_JMP_SAV: + U_ASSERT(opValue < fPattern->fCompiledPat->size()); + fp = StateSave(fp, fp->fPatIdx, status); // State save to loc following current + fp->fPatIdx = opValue; // Then JMP. + break; + + case URX_JMP_SAV_X: + // This opcode is used with (x)+, when x can match a zero length string. + // Same as JMP_SAV, except conditional on the match having made forward progress. + // Destination of the JMP must be a URX_STO_INP_LOC, from which we get the + // data address of the input position at the start of the loop. + { + U_ASSERT(opValue > 0 && opValue < fPattern->fCompiledPat->size()); + int32_t stoOp = (int32_t)pat[opValue-1]; + U_ASSERT(URX_TYPE(stoOp) == URX_STO_INP_LOC); + int32_t frameLoc = URX_VAL(stoOp); + U_ASSERT(frameLoc >= 0 && frameLoc < fFrameSize); + int64_t prevInputIdx = fp->fExtra[frameLoc]; + U_ASSERT(prevInputIdx <= fp->fInputIdx); + if (prevInputIdx < fp->fInputIdx) { + // The match did make progress. Repeat the loop. + fp = StateSave(fp, fp->fPatIdx, status); // State save to loc following current + fp->fPatIdx = opValue; + fp->fExtra[frameLoc] = fp->fInputIdx; + } + // If the input position did not advance, we do nothing here, + // execution will fall out of the loop. + } + break; + + case URX_CTR_INIT: + { + U_ASSERT(opValue >= 0 && opValue < fFrameSize-2); + fp->fExtra[opValue] = 0; // Set the loop counter variable to zero + + // Pick up the three extra operands that CTR_INIT has, and + // skip the pattern location counter past + int32_t instrOperandLoc = (int32_t)fp->fPatIdx; + fp->fPatIdx += 3; + int32_t loopLoc = URX_VAL(pat[instrOperandLoc]); + int32_t minCount = (int32_t)pat[instrOperandLoc+1]; + int32_t maxCount = (int32_t)pat[instrOperandLoc+2]; + U_ASSERT(minCount>=0); + U_ASSERT(maxCount>=minCount || maxCount==-1); + U_ASSERT(loopLoc>=fp->fPatIdx); + + if (minCount == 0) { + fp = StateSave(fp, loopLoc+1, status); + } + if (maxCount == -1) { + fp->fExtra[opValue+1] = fp->fInputIdx; // For loop breaking. + } else if (maxCount == 0) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + case URX_CTR_LOOP: + { + U_ASSERT(opValue>0 && opValue < fp->fPatIdx-2); + int32_t initOp = (int32_t)pat[opValue]; + U_ASSERT(URX_TYPE(initOp) == URX_CTR_INIT); + int64_t *pCounter = &fp->fExtra[URX_VAL(initOp)]; + int32_t minCount = (int32_t)pat[opValue+2]; + int32_t maxCount = (int32_t)pat[opValue+3]; + (*pCounter)++; + if ((uint64_t)*pCounter >= (uint32_t)maxCount && maxCount != -1) { + U_ASSERT(*pCounter == maxCount); + break; + } + if (*pCounter >= minCount) { + if (maxCount == -1) { + // Loop has no hard upper bound. + // Check that it is progressing through the input, break if it is not. + int64_t *pLastInputIdx = &fp->fExtra[URX_VAL(initOp) + 1]; + if (fp->fInputIdx == *pLastInputIdx) { + break; + } else { + *pLastInputIdx = fp->fInputIdx; + } + } + fp = StateSave(fp, fp->fPatIdx, status); + } else { + // Increment time-out counter. (StateSave() does it if count >= minCount) + fTickCounter--; + if (fTickCounter <= 0) { + IncrementTime(status); // Re-initializes fTickCounter + } + } + + fp->fPatIdx = opValue + 4; // Loop back. + } + break; + + case URX_CTR_INIT_NG: + { + // Initialize a non-greedy loop + U_ASSERT(opValue >= 0 && opValue < fFrameSize-2); + fp->fExtra[opValue] = 0; // Set the loop counter variable to zero + + // Pick up the three extra operands that CTR_INIT_NG has, and + // skip the pattern location counter past + int32_t instrOperandLoc = (int32_t)fp->fPatIdx; + fp->fPatIdx += 3; + int32_t loopLoc = URX_VAL(pat[instrOperandLoc]); + int32_t minCount = (int32_t)pat[instrOperandLoc+1]; + int32_t maxCount = (int32_t)pat[instrOperandLoc+2]; + U_ASSERT(minCount>=0); + U_ASSERT(maxCount>=minCount || maxCount==-1); + U_ASSERT(loopLoc>fp->fPatIdx); + if (maxCount == -1) { + fp->fExtra[opValue+1] = fp->fInputIdx; // Save initial input index for loop breaking. + } + + if (minCount == 0) { + if (maxCount != 0) { + fp = StateSave(fp, fp->fPatIdx, status); + } + fp->fPatIdx = loopLoc+1; // Continue with stuff after repeated block + } + } + break; + + case URX_CTR_LOOP_NG: + { + // Non-greedy {min, max} loops + U_ASSERT(opValue>0 && opValue < fp->fPatIdx-2); + int32_t initOp = (int32_t)pat[opValue]; + U_ASSERT(URX_TYPE(initOp) == URX_CTR_INIT_NG); + int64_t *pCounter = &fp->fExtra[URX_VAL(initOp)]; + int32_t minCount = (int32_t)pat[opValue+2]; + int32_t maxCount = (int32_t)pat[opValue+3]; + + (*pCounter)++; + if ((uint64_t)*pCounter >= (uint32_t)maxCount && maxCount != -1) { + // The loop has matched the maximum permitted number of times. + // Break out of here with no action. Matching will + // continue with the following pattern. + U_ASSERT(*pCounter == maxCount); + break; + } + + if (*pCounter < minCount) { + // We haven't met the minimum number of matches yet. + // Loop back for another one. + fp->fPatIdx = opValue + 4; // Loop back. + // Increment time-out counter. (StateSave() does it if count >= minCount) + fTickCounter--; + if (fTickCounter <= 0) { + IncrementTime(status); // Re-initializes fTickCounter + } + } else { + // We do have the minimum number of matches. + + // If there is no upper bound on the loop iterations, check that the input index + // is progressing, and stop the loop if it is not. + if (maxCount == -1) { + int64_t *pLastInputIdx = &fp->fExtra[URX_VAL(initOp) + 1]; + if (fp->fInputIdx == *pLastInputIdx) { + break; + } + *pLastInputIdx = fp->fInputIdx; + } + + // Loop Continuation: we will fall into the pattern following the loop + // (non-greedy, don't execute loop body first), but first do + // a state save to the top of the loop, so that a match failure + // in the following pattern will try another iteration of the loop. + fp = StateSave(fp, opValue + 4, status); + } + } + break; + + case URX_STO_SP: + U_ASSERT(opValue >= 0 && opValue < fPattern->fDataSize); + fData[opValue] = fStack->size(); + break; + + case URX_LD_SP: + { + U_ASSERT(opValue >= 0 && opValue < fPattern->fDataSize); + int32_t newStackSize = (int32_t)fData[opValue]; + U_ASSERT(newStackSize <= fStack->size()); + int64_t *newFP = fStack->getBuffer() + newStackSize - fFrameSize; + if (newFP == (int64_t *)fp) { + break; + } + int32_t j; + for (j=0; jsetSize(newStackSize); + } + break; + + case URX_BACKREF: + { + U_ASSERT(opValue < fFrameSize); + int64_t groupStartIdx = fp->fExtra[opValue]; + int64_t groupEndIdx = fp->fExtra[opValue+1]; + U_ASSERT(groupStartIdx <= groupEndIdx); + if (groupStartIdx < 0) { + // This capture group has not participated in the match thus far, + fp = (REStackFrame *)fStack->popFrame(fFrameSize); // FAIL, no match. + break; + } + UTEXT_SETNATIVEINDEX(fAltInputText, groupStartIdx); + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + + // Note: if the capture group match was of an empty string the backref + // match succeeds. Verified by testing: Perl matches succeed + // in this case, so we do too. + + UBool success = true; + for (;;) { + if (utext_getNativeIndex(fAltInputText) >= groupEndIdx) { + success = true; + break; + } + if (utext_getNativeIndex(fInputText) >= fActiveLimit) { + success = false; + fHitEnd = true; + break; + } + UChar32 captureGroupChar = utext_next32(fAltInputText); + UChar32 inputChar = utext_next32(fInputText); + if (inputChar != captureGroupChar) { + success = false; + break; + } + } + + if (success) { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + + case URX_BACKREF_I: + { + U_ASSERT(opValue < fFrameSize); + int64_t groupStartIdx = fp->fExtra[opValue]; + int64_t groupEndIdx = fp->fExtra[opValue+1]; + U_ASSERT(groupStartIdx <= groupEndIdx); + if (groupStartIdx < 0) { + // This capture group has not participated in the match thus far, + fp = (REStackFrame *)fStack->popFrame(fFrameSize); // FAIL, no match. + break; + } + utext_setNativeIndex(fAltInputText, groupStartIdx); + utext_setNativeIndex(fInputText, fp->fInputIdx); + CaseFoldingUTextIterator captureGroupItr(*fAltInputText); + CaseFoldingUTextIterator inputItr(*fInputText); + + // Note: if the capture group match was of an empty string the backref + // match succeeds. Verified by testing: Perl matches succeed + // in this case, so we do too. + + UBool success = true; + for (;;) { + if (!captureGroupItr.inExpansion() && utext_getNativeIndex(fAltInputText) >= groupEndIdx) { + success = true; + break; + } + if (!inputItr.inExpansion() && utext_getNativeIndex(fInputText) >= fActiveLimit) { + success = false; + fHitEnd = true; + break; + } + UChar32 captureGroupChar = captureGroupItr.next(); + UChar32 inputChar = inputItr.next(); + if (inputChar != captureGroupChar) { + success = false; + break; + } + } + + if (success && inputItr.inExpansion()) { + // We obtained a match by consuming part of a string obtained from + // case-folding a single code point of the input text. + // This does not count as an overall match. + success = false; + } + + if (success) { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + + } + break; + + case URX_STO_INP_LOC: + { + U_ASSERT(opValue >= 0 && opValue < fFrameSize); + fp->fExtra[opValue] = fp->fInputIdx; + } + break; + + case URX_JMPX: + { + int32_t instrOperandLoc = (int32_t)fp->fPatIdx; + fp->fPatIdx += 1; + int32_t dataLoc = URX_VAL(pat[instrOperandLoc]); + U_ASSERT(dataLoc >= 0 && dataLoc < fFrameSize); + int64_t savedInputIdx = fp->fExtra[dataLoc]; + U_ASSERT(savedInputIdx <= fp->fInputIdx); + if (savedInputIdx < fp->fInputIdx) { + fp->fPatIdx = opValue; // JMP + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); // FAIL, no progress in loop. + } + } + break; + + case URX_LA_START: + { + // Entering a look around block. + // Save Stack Ptr, Input Pos. + U_ASSERT(opValue>=0 && opValue+3fDataSize); + fData[opValue] = fStack->size(); + fData[opValue+1] = fp->fInputIdx; + fData[opValue+2] = fActiveStart; + fData[opValue+3] = fActiveLimit; + fActiveStart = fLookStart; // Set the match region change for + fActiveLimit = fLookLimit; // transparent bounds. + } + break; + + case URX_LA_END: + { + // Leaving a look-ahead block. + // restore Stack Ptr, Input Pos to positions they had on entry to block. + U_ASSERT(opValue>=0 && opValue+3fDataSize); + int32_t stackSize = fStack->size(); + int32_t newStackSize =(int32_t)fData[opValue]; + U_ASSERT(stackSize >= newStackSize); + if (stackSize > newStackSize) { + // Copy the current top frame back to the new (cut back) top frame. + // This makes the capture groups from within the look-ahead + // expression available. + int64_t *newFP = fStack->getBuffer() + newStackSize - fFrameSize; + int32_t j; + for (j=0; jsetSize(newStackSize); + } + fp->fInputIdx = fData[opValue+1]; + + // Restore the active region bounds in the input string; they may have + // been changed because of transparent bounds on a Region. + fActiveStart = fData[opValue+2]; + fActiveLimit = fData[opValue+3]; + U_ASSERT(fActiveStart >= 0); + U_ASSERT(fActiveLimit <= fInputLength); + } + break; + + case URX_ONECHAR_I: + // Case insensitive one char. The char from the pattern is already case folded. + // Input text is not, but case folding the input can not reduce two or more code + // points to one. + if (fp->fInputIdx < fActiveLimit) { + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + + UChar32 c = UTEXT_NEXT32(fInputText); + if (u_foldCase(c, U_FOLD_CASE_DEFAULT) == opValue) { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + break; + } + } else { + fHitEnd = true; + } + + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + + case URX_STRING_I: + { + // Case-insensitive test input against a literal string. + // Strings require two slots in the compiled pattern, one for the + // offset to the string text, and one for the length. + // The compiled string has already been case folded. + { + const char16_t *patternString = litText + opValue; + int32_t patternStringIdx = 0; + + op = (int32_t)pat[fp->fPatIdx]; + fp->fPatIdx++; + opType = URX_TYPE(op); + opValue = URX_VAL(op); + U_ASSERT(opType == URX_STRING_LEN); + int32_t patternStringLen = opValue; // Length of the string from the pattern. + + + UChar32 cPattern; + UChar32 cText; + UBool success = true; + + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + CaseFoldingUTextIterator inputIterator(*fInputText); + while (patternStringIdx < patternStringLen) { + if (!inputIterator.inExpansion() && UTEXT_GETNATIVEINDEX(fInputText) >= fActiveLimit) { + success = false; + fHitEnd = true; + break; + } + U16_NEXT(patternString, patternStringIdx, patternStringLen, cPattern); + cText = inputIterator.next(); + if (cText != cPattern) { + success = false; + break; + } + } + if (inputIterator.inExpansion()) { + success = false; + } + + if (success) { + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + } + break; + + case URX_LB_START: + { + // Entering a look-behind block. + // Save Stack Ptr, Input Pos and active input region. + // TODO: implement transparent bounds. Ticket #6067 + U_ASSERT(opValue>=0 && opValue+4fDataSize); + fData[opValue] = fStack->size(); + fData[opValue+1] = fp->fInputIdx; + // Save input string length, then reset to pin any matches to end at + // the current position. + fData[opValue+2] = fActiveStart; + fData[opValue+3] = fActiveLimit; + fActiveStart = fRegionStart; + fActiveLimit = fp->fInputIdx; + // Init the variable containing the start index for attempted matches. + fData[opValue+4] = -1; + } + break; + + + case URX_LB_CONT: + { + // Positive Look-Behind, at top of loop checking for matches of LB expression + // at all possible input starting positions. + + // Fetch the min and max possible match lengths. They are the operands + // of this op in the pattern. + int32_t minML = (int32_t)pat[fp->fPatIdx++]; + int32_t maxML = (int32_t)pat[fp->fPatIdx++]; + if (!UTEXT_USES_U16(fInputText)) { + // utf-8 fix to maximum match length. The pattern compiler assumes utf-16. + // The max length need not be exact; it just needs to be >= actual maximum. + maxML *= 3; + } + U_ASSERT(minML <= maxML); + U_ASSERT(minML >= 0); + + // Fetch (from data) the last input index where a match was attempted. + U_ASSERT(opValue>=0 && opValue+4fDataSize); + int64_t &lbStartIdx = fData[opValue+4]; + if (lbStartIdx < 0) { + // First time through loop. + lbStartIdx = fp->fInputIdx - minML; + if (lbStartIdx > 0) { + // move index to a code point boundary, if it's not on one already. + UTEXT_SETNATIVEINDEX(fInputText, lbStartIdx); + lbStartIdx = UTEXT_GETNATIVEINDEX(fInputText); + } + } else { + // 2nd through nth time through the loop. + // Back up start position for match by one. + if (lbStartIdx == 0) { + (lbStartIdx)--; + } else { + UTEXT_SETNATIVEINDEX(fInputText, lbStartIdx); + (void)UTEXT_PREVIOUS32(fInputText); + lbStartIdx = UTEXT_GETNATIVEINDEX(fInputText); + } + } + + if (lbStartIdx < 0 || lbStartIdx < fp->fInputIdx - maxML) { + // We have tried all potential match starting points without + // getting a match. Backtrack out, and out of the + // Look Behind altogether. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + fActiveStart = fData[opValue+2]; + fActiveLimit = fData[opValue+3]; + U_ASSERT(fActiveStart >= 0); + U_ASSERT(fActiveLimit <= fInputLength); + break; + } + + // Save state to this URX_LB_CONT op, so failure to match will repeat the loop. + // (successful match will fall off the end of the loop.) + fp = StateSave(fp, fp->fPatIdx-3, status); + fp->fInputIdx = lbStartIdx; + } + break; + + case URX_LB_END: + // End of a look-behind block, after a successful match. + { + U_ASSERT(opValue>=0 && opValue+4fDataSize); + if (fp->fInputIdx != fActiveLimit) { + // The look-behind expression matched, but the match did not + // extend all the way to the point that we are looking behind from. + // FAIL out of here, which will take us back to the LB_CONT, which + // will retry the match starting at another position or fail + // the look-behind altogether, whichever is appropriate. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + // Look-behind match is good. Restore the original input string region, + // which had been truncated to pin the end of the lookbehind match to the + // position being looked-behind. + fActiveStart = fData[opValue+2]; + fActiveLimit = fData[opValue+3]; + U_ASSERT(fActiveStart >= 0); + U_ASSERT(fActiveLimit <= fInputLength); + } + break; + + + case URX_LBN_CONT: + { + // Negative Look-Behind, at top of loop checking for matches of LB expression + // at all possible input starting positions. + + // Fetch the extra parameters of this op. + int32_t minML = (int32_t)pat[fp->fPatIdx++]; + int32_t maxML = (int32_t)pat[fp->fPatIdx++]; + if (!UTEXT_USES_U16(fInputText)) { + // utf-8 fix to maximum match length. The pattern compiler assumes utf-16. + // The max length need not be exact; it just needs to be >= actual maximum. + maxML *= 3; + } + int32_t continueLoc = (int32_t)pat[fp->fPatIdx++]; + continueLoc = URX_VAL(continueLoc); + U_ASSERT(minML <= maxML); + U_ASSERT(minML >= 0); + U_ASSERT(continueLoc > fp->fPatIdx); + + // Fetch (from data) the last input index where a match was attempted. + U_ASSERT(opValue>=0 && opValue+4fDataSize); + int64_t &lbStartIdx = fData[opValue+4]; + if (lbStartIdx < 0) { + // First time through loop. + lbStartIdx = fp->fInputIdx - minML; + if (lbStartIdx > 0) { + // move index to a code point boundary, if it's not on one already. + UTEXT_SETNATIVEINDEX(fInputText, lbStartIdx); + lbStartIdx = UTEXT_GETNATIVEINDEX(fInputText); + } + } else { + // 2nd through nth time through the loop. + // Back up start position for match by one. + if (lbStartIdx == 0) { + (lbStartIdx)--; + } else { + UTEXT_SETNATIVEINDEX(fInputText, lbStartIdx); + (void)UTEXT_PREVIOUS32(fInputText); + lbStartIdx = UTEXT_GETNATIVEINDEX(fInputText); + } + } + + if (lbStartIdx < 0 || lbStartIdx < fp->fInputIdx - maxML) { + // We have tried all potential match starting points without + // getting a match, which means that the negative lookbehind as + // a whole has succeeded. Jump forward to the continue location + fActiveStart = fData[opValue+2]; + fActiveLimit = fData[opValue+3]; + U_ASSERT(fActiveStart >= 0); + U_ASSERT(fActiveLimit <= fInputLength); + fp->fPatIdx = continueLoc; + break; + } + + // Save state to this URX_LB_CONT op, so failure to match will repeat the loop. + // (successful match will cause a FAIL out of the loop altogether.) + fp = StateSave(fp, fp->fPatIdx-4, status); + fp->fInputIdx = lbStartIdx; + } + break; + + case URX_LBN_END: + // End of a negative look-behind block, after a successful match. + { + U_ASSERT(opValue>=0 && opValue+4fDataSize); + if (fp->fInputIdx != fActiveLimit) { + // The look-behind expression matched, but the match did not + // extend all the way to the point that we are looking behind from. + // FAIL out of here, which will take us back to the LB_CONT, which + // will retry the match starting at another position or succeed + // the look-behind altogether, whichever is appropriate. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + // Look-behind expression matched, which means look-behind test as + // a whole Fails + + // Restore the original input string length, which had been truncated + // inorder to pin the end of the lookbehind match + // to the position being looked-behind. + fActiveStart = fData[opValue+2]; + fActiveLimit = fData[opValue+3]; + U_ASSERT(fActiveStart >= 0); + U_ASSERT(fActiveLimit <= fInputLength); + + // Restore original stack position, discarding any state saved + // by the successful pattern match. + U_ASSERT(opValue>=0 && opValue+1fDataSize); + int32_t newStackSize = (int32_t)fData[opValue]; + U_ASSERT(fStack->size() > newStackSize); + fStack->setSize(newStackSize); + + // FAIL, which will take control back to someplace + // prior to entering the look-behind test. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_LOOP_SR_I: + // Loop Initialization for the optimized implementation of + // [some character set]* + // This op scans through all matching input. + // The following LOOP_C op emulates stack unwinding if the following pattern fails. + { + U_ASSERT(opValue > 0 && opValue < fSets->size()); + Regex8BitSet *s8 = &fPattern->fSets8[opValue]; + UnicodeSet *s = (UnicodeSet *)fSets->elementAt(opValue); + + // Loop through input, until either the input is exhausted or + // we reach a character that is not a member of the set. + int64_t ix = fp->fInputIdx; + UTEXT_SETNATIVEINDEX(fInputText, ix); + for (;;) { + if (ix >= fActiveLimit) { + fHitEnd = true; + break; + } + UChar32 c = UTEXT_NEXT32(fInputText); + if (c<256) { + if (s8->contains(c) == false) { + break; + } + } else { + if (s->contains(c) == false) { + break; + } + } + ix = UTEXT_GETNATIVEINDEX(fInputText); + } + + // If there were no matching characters, skip over the loop altogether. + // The loop doesn't run at all, a * op always succeeds. + if (ix == fp->fInputIdx) { + fp->fPatIdx++; // skip the URX_LOOP_C op. + break; + } + + // Peek ahead in the compiled pattern, to the URX_LOOP_C that + // must follow. It's operand is the stack location + // that holds the starting input index for the match of this [set]* + int32_t loopcOp = (int32_t)pat[fp->fPatIdx]; + U_ASSERT(URX_TYPE(loopcOp) == URX_LOOP_C); + int32_t stackLoc = URX_VAL(loopcOp); + U_ASSERT(stackLoc >= 0 && stackLoc < fFrameSize); + fp->fExtra[stackLoc] = fp->fInputIdx; + fp->fInputIdx = ix; + + // Save State to the URX_LOOP_C op that follows this one, + // so that match failures in the following code will return to there. + // Then bump the pattern idx so the LOOP_C is skipped on the way out of here. + fp = StateSave(fp, fp->fPatIdx, status); + fp->fPatIdx++; + } + break; + + + case URX_LOOP_DOT_I: + // Loop Initialization for the optimized implementation of .* + // This op scans through all remaining input. + // The following LOOP_C op emulates stack unwinding if the following pattern fails. + { + // Loop through input until the input is exhausted (we reach an end-of-line) + // In DOTALL mode, we can just go straight to the end of the input. + int64_t ix; + if ((opValue & 1) == 1) { + // Dot-matches-All mode. Jump straight to the end of the string. + ix = fActiveLimit; + fHitEnd = true; + } else { + // NOT DOT ALL mode. Line endings do not match '.' + // Scan forward until a line ending or end of input. + ix = fp->fInputIdx; + UTEXT_SETNATIVEINDEX(fInputText, ix); + for (;;) { + if (ix >= fActiveLimit) { + fHitEnd = true; + break; + } + UChar32 c = UTEXT_NEXT32(fInputText); + if ((c & 0x7f) <= 0x29) { // Fast filter of non-new-line-s + if ((c == 0x0a) || // 0x0a is newline in both modes. + (((opValue & 2) == 0) && // IF not UNIX_LINES mode + isLineTerminator(c))) { + // char is a line ending. Exit the scanning loop. + break; + } + } + ix = UTEXT_GETNATIVEINDEX(fInputText); + } + } + + // If there were no matching characters, skip over the loop altogether. + // The loop doesn't run at all, a * op always succeeds. + if (ix == fp->fInputIdx) { + fp->fPatIdx++; // skip the URX_LOOP_C op. + break; + } + + // Peek ahead in the compiled pattern, to the URX_LOOP_C that + // must follow. It's operand is the stack location + // that holds the starting input index for the match of this .* + int32_t loopcOp = (int32_t)pat[fp->fPatIdx]; + U_ASSERT(URX_TYPE(loopcOp) == URX_LOOP_C); + int32_t stackLoc = URX_VAL(loopcOp); + U_ASSERT(stackLoc >= 0 && stackLoc < fFrameSize); + fp->fExtra[stackLoc] = fp->fInputIdx; + fp->fInputIdx = ix; + + // Save State to the URX_LOOP_C op that follows this one, + // so that match failures in the following code will return to there. + // Then bump the pattern idx so the LOOP_C is skipped on the way out of here. + fp = StateSave(fp, fp->fPatIdx, status); + fp->fPatIdx++; + } + break; + + + case URX_LOOP_C: + { + U_ASSERT(opValue>=0 && opValuefExtra[opValue]; + U_ASSERT(backSearchIndex <= fp->fInputIdx); + if (backSearchIndex == fp->fInputIdx) { + // We've backed up the input idx to the point that the loop started. + // The loop is done. Leave here without saving state. + // Subsequent failures won't come back here. + break; + } + // Set up for the next iteration of the loop, with input index + // backed up by one from the last time through, + // and a state save to this instruction in case the following code fails again. + // (We're going backwards because this loop emulates stack unwinding, not + // the initial scan forward.) + U_ASSERT(fp->fInputIdx > 0); + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + UChar32 prevC = UTEXT_PREVIOUS32(fInputText); + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + + UChar32 twoPrevC = UTEXT_PREVIOUS32(fInputText); + if (prevC == 0x0a && + fp->fInputIdx > backSearchIndex && + twoPrevC == 0x0d) { + int32_t prevOp = (int32_t)pat[fp->fPatIdx-2]; + if (URX_TYPE(prevOp) == URX_LOOP_DOT_I) { + // .*, stepping back over CRLF pair. + fp->fInputIdx = UTEXT_GETNATIVEINDEX(fInputText); + } + } + + + fp = StateSave(fp, fp->fPatIdx-1, status); + } + break; + + + + default: + // Trouble. The compiled pattern contains an entry with an + // unrecognized type tag. + UPRV_UNREACHABLE_ASSERT; + // Unknown opcode type in opType = URX_TYPE(pat[fp->fPatIdx]). But we have + // reports of this in production code, don't use UPRV_UNREACHABLE_EXIT. + // See ICU-21669. + status = U_INTERNAL_PROGRAM_ERROR; + } + + if (U_FAILURE(status)) { + isMatch = false; + break; + } + } + +breakFromLoop: + fMatch = isMatch; + if (isMatch) { + fLastMatchEnd = fMatchEnd; + fMatchStart = startIdx; + fMatchEnd = fp->fInputIdx; + } + +#ifdef REGEX_RUN_DEBUG + if (fTraceDebug) { + if (isMatch) { + printf("Match. start=%ld end=%ld\n\n", fMatchStart, fMatchEnd); + } else { + printf("No match\n\n"); + } + } +#endif + + fFrame = fp; // The active stack frame when the engine stopped. + // Contains the capture group results that we need to + // access later. + return; +} + + +//-------------------------------------------------------------------------------- +// +// MatchChunkAt This is the actual matching engine. Like MatchAt, but with the +// assumption that the entire string is available in the UText's +// chunk buffer. For now, that means we can use int32_t indexes, +// except for anything that needs to be saved (like group starts +// and ends). +// +// startIdx: begin matching a this index. +// toEnd: if true, match must extend to end of the input region +// +//-------------------------------------------------------------------------------- +void RegexMatcher::MatchChunkAt(int32_t startIdx, UBool toEnd, UErrorCode &status) { + UBool isMatch = false; // True if the we have a match. + + int32_t backSearchIndex = INT32_MAX; // used after greedy single-character matches for searching backwards + + int32_t op; // Operation from the compiled pattern, split into + int32_t opType; // the opcode + int32_t opValue; // and the operand value. + +#ifdef REGEX_RUN_DEBUG + if (fTraceDebug) { + printf("MatchAt(startIdx=%d)\n", startIdx); + printf("Original Pattern: \"%s\"\n", CStr(StringFromUText(fPattern->fPattern))()); + printf("Input String: \"%s\"\n\n", CStr(StringFromUText(fInputText))()); + } +#endif + + if (U_FAILURE(status)) { + return; + } + + // Cache frequently referenced items from the compiled pattern + // + int64_t *pat = fPattern->fCompiledPat->getBuffer(); + + const char16_t *litText = fPattern->fLiteralText.getBuffer(); + UVector *fSets = fPattern->fSets; + + const char16_t *inputBuf = fInputText->chunkContents; + + fFrameSize = fPattern->fFrameSize; + REStackFrame *fp = resetStack(); + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return; + } + + fp->fPatIdx = 0; + fp->fInputIdx = startIdx; + + // Zero out the pattern's static data + int32_t i; + for (i = 0; ifDataSize; i++) { + fData[i] = 0; + } + + // + // Main loop for interpreting the compiled pattern. + // One iteration of the loop per pattern operation performed. + // + for (;;) { + op = (int32_t)pat[fp->fPatIdx]; + opType = URX_TYPE(op); + opValue = URX_VAL(op); +#ifdef REGEX_RUN_DEBUG + if (fTraceDebug) { + UTEXT_SETNATIVEINDEX(fInputText, fp->fInputIdx); + printf("inputIdx=%ld inputChar=%x sp=%3ld activeLimit=%ld ", fp->fInputIdx, + UTEXT_CURRENT32(fInputText), (int64_t *)fp-fStack->getBuffer(), fActiveLimit); + fPattern->dumpOp(fp->fPatIdx); + } +#endif + fp->fPatIdx++; + + switch (opType) { + + + case URX_NOP: + break; + + + case URX_BACKTRACK: + // Force a backtrack. In some circumstances, the pattern compiler + // will notice that the pattern can't possibly match anything, and will + // emit one of these at that point. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + + + case URX_ONECHAR: + if (fp->fInputIdx < fActiveLimit) { + UChar32 c; + U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); + if (c == opValue) { + break; + } + } else { + fHitEnd = true; + } + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + + + case URX_STRING: + { + // Test input against a literal string. + // Strings require two slots in the compiled pattern, one for the + // offset to the string text, and one for the length. + int32_t stringStartIdx = opValue; + int32_t stringLen; + + op = (int32_t)pat[fp->fPatIdx]; // Fetch the second operand + fp->fPatIdx++; + opType = URX_TYPE(op); + stringLen = URX_VAL(op); + U_ASSERT(opType == URX_STRING_LEN); + U_ASSERT(stringLen >= 2); + + const char16_t * pInp = inputBuf + fp->fInputIdx; + const char16_t * pInpLimit = inputBuf + fActiveLimit; + const char16_t * pPat = litText+stringStartIdx; + const char16_t * pEnd = pInp + stringLen; + UBool success = true; + while (pInp < pEnd) { + if (pInp >= pInpLimit) { + fHitEnd = true; + success = false; + break; + } + if (*pInp++ != *pPat++) { + success = false; + break; + } + } + + if (success) { + fp->fInputIdx += stringLen; + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_STATE_SAVE: + fp = StateSave(fp, opValue, status); + break; + + + case URX_END: + // The match loop will exit via this path on a successful match, + // when we reach the end of the pattern. + if (toEnd && fp->fInputIdx != fActiveLimit) { + // The pattern matched, but not to the end of input. Try some more. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + isMatch = true; + goto breakFromLoop; + + // Start and End Capture stack frame variables are laid out out like this: + // fp->fExtra[opValue] - The start of a completed capture group + // opValue+1 - The end of a completed capture group + // opValue+2 - the start of a capture group whose end + // has not yet been reached (and might not ever be). + case URX_START_CAPTURE: + U_ASSERT(opValue >= 0 && opValue < fFrameSize-3); + fp->fExtra[opValue+2] = fp->fInputIdx; + break; + + + case URX_END_CAPTURE: + U_ASSERT(opValue >= 0 && opValue < fFrameSize-3); + U_ASSERT(fp->fExtra[opValue+2] >= 0); // Start pos for this group must be set. + fp->fExtra[opValue] = fp->fExtra[opValue+2]; // Tentative start becomes real. + fp->fExtra[opValue+1] = fp->fInputIdx; // End position + U_ASSERT(fp->fExtra[opValue] <= fp->fExtra[opValue+1]); + break; + + + case URX_DOLLAR: // $, test for End of line + // or for position before new line at end of input + if (fp->fInputIdx < fAnchorLimit-2) { + // We are no where near the end of input. Fail. + // This is the common case. Keep it first. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + if (fp->fInputIdx >= fAnchorLimit) { + // We really are at the end of input. Success. + fHitEnd = true; + fRequireEnd = true; + break; + } + + // If we are positioned just before a new-line that is located at the + // end of input, succeed. + if (fp->fInputIdx == fAnchorLimit-1) { + UChar32 c; + U16_GET(inputBuf, fAnchorStart, fp->fInputIdx, fAnchorLimit, c); + + if (isLineTerminator(c)) { + if ( !(c==0x0a && fp->fInputIdx>fAnchorStart && inputBuf[fp->fInputIdx-1]==0x0d)) { + // At new-line at end of input. Success + fHitEnd = true; + fRequireEnd = true; + break; + } + } + } else if (fp->fInputIdx == fAnchorLimit-2 && + inputBuf[fp->fInputIdx]==0x0d && inputBuf[fp->fInputIdx+1]==0x0a) { + fHitEnd = true; + fRequireEnd = true; + break; // At CR/LF at end of input. Success + } + + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + + break; + + + case URX_DOLLAR_D: // $, test for End of Line, in UNIX_LINES mode. + if (fp->fInputIdx >= fAnchorLimit-1) { + // Either at the last character of input, or off the end. + if (fp->fInputIdx == fAnchorLimit-1) { + // At last char of input. Success if it's a new line. + if (inputBuf[fp->fInputIdx] == 0x0a) { + fHitEnd = true; + fRequireEnd = true; + break; + } + } else { + // Off the end of input. Success. + fHitEnd = true; + fRequireEnd = true; + break; + } + } + + // Not at end of input. Back-track out. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + + + case URX_DOLLAR_M: // $, test for End of line in multi-line mode + { + if (fp->fInputIdx >= fAnchorLimit) { + // We really are at the end of input. Success. + fHitEnd = true; + fRequireEnd = true; + break; + } + // If we are positioned just before a new-line, succeed. + // It makes no difference where the new-line is within the input. + UChar32 c = inputBuf[fp->fInputIdx]; + if (isLineTerminator(c)) { + // At a line end, except for the odd chance of being in the middle of a CR/LF sequence + // In multi-line mode, hitting a new-line just before the end of input does not + // set the hitEnd or requireEnd flags + if ( !(c==0x0a && fp->fInputIdx>fAnchorStart && inputBuf[fp->fInputIdx-1]==0x0d)) { + break; + } + } + // not at a new line. Fail. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_DOLLAR_MD: // $, test for End of line in multi-line and UNIX_LINES mode + { + if (fp->fInputIdx >= fAnchorLimit) { + // We really are at the end of input. Success. + fHitEnd = true; + fRequireEnd = true; // Java set requireEnd in this case, even though + break; // adding a new-line would not lose the match. + } + // If we are not positioned just before a new-line, the test fails; backtrack out. + // It makes no difference where the new-line is within the input. + if (inputBuf[fp->fInputIdx] != 0x0a) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_CARET: // ^, test for start of line + if (fp->fInputIdx != fAnchorStart) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_CARET_M: // ^, test for start of line in mulit-line mode + { + if (fp->fInputIdx == fAnchorStart) { + // We are at the start input. Success. + break; + } + // Check whether character just before the current pos is a new-line + // unless we are at the end of input + char16_t c = inputBuf[fp->fInputIdx - 1]; + if ((fp->fInputIdx < fAnchorLimit) && + isLineTerminator(c)) { + // It's a new-line. ^ is true. Success. + // TODO: what should be done with positions between a CR and LF? + break; + } + // Not at the start of a line. Fail. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_CARET_M_UNIX: // ^, test for start of line in mulit-line + Unix-line mode + { + U_ASSERT(fp->fInputIdx >= fAnchorStart); + if (fp->fInputIdx <= fAnchorStart) { + // We are at the start input. Success. + break; + } + // Check whether character just before the current pos is a new-line + U_ASSERT(fp->fInputIdx <= fAnchorLimit); + char16_t c = inputBuf[fp->fInputIdx - 1]; + if (c != 0x0a) { + // Not at the start of a line. Back-track out. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + case URX_BACKSLASH_B: // Test for word boundaries + { + UBool success = isChunkWordBoundary((int32_t)fp->fInputIdx); + success ^= (UBool)(opValue != 0); // flip sense for \B + if (!success) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_BACKSLASH_BU: // Test for word boundaries, Unicode-style + { + UBool success = isUWordBoundary(fp->fInputIdx, status); + success ^= (UBool)(opValue != 0); // flip sense for \B + if (!success) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_BACKSLASH_D: // Test for decimal digit + { + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + UChar32 c; + U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); + int8_t ctype = u_charType(c); // TODO: make a unicode set for this. Will be faster. + UBool success = (ctype == U_DECIMAL_DIGIT_NUMBER); + success ^= (UBool)(opValue != 0); // flip sense for \D + if (!success) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_BACKSLASH_G: // Test for position at end of previous match + if (!((fMatch && fp->fInputIdx==fMatchEnd) || (fMatch==false && fp->fInputIdx==fActiveStart))) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_BACKSLASH_H: // Test for \h, horizontal white space. + { + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + UChar32 c; + U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); + int8_t ctype = u_charType(c); + UBool success = (ctype == U_SPACE_SEPARATOR || c == 9); // SPACE_SEPARATOR || TAB + success ^= (UBool)(opValue != 0); // flip sense for \H + if (!success) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_BACKSLASH_R: // Test for \R, any line break sequence. + { + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + UChar32 c; + U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); + if (isLineTerminator(c)) { + if (c == 0x0d && fp->fInputIdx < fActiveLimit) { + // Check for CR/LF sequence. Consume both together when found. + char16_t c2; + U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c2); + if (c2 != 0x0a) { + U16_PREV(inputBuf, 0, fp->fInputIdx, c2); + } + } + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_BACKSLASH_V: // Any single code point line ending. + { + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + UChar32 c; + U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); + UBool success = isLineTerminator(c); + success ^= (UBool)(opValue != 0); // flip sense for \V + if (!success) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_BACKSLASH_X: + // Match a Grapheme, as defined by Unicode UAX 29. + + // Fail if at end of input + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + fp->fInputIdx = followingGCBoundary(fp->fInputIdx, status); + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp->fInputIdx = fActiveLimit; + } + break; + + + case URX_BACKSLASH_Z: // Test for end of Input + if (fp->fInputIdx < fAnchorLimit) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } else { + fHitEnd = true; + fRequireEnd = true; + } + break; + + + + case URX_STATIC_SETREF: + { + // Test input character against one of the predefined sets + // (Word Characters, for example) + // The high bit of the op value is a flag for the match polarity. + // 0: success if input char is in set. + // 1: success if input char is not in set. + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + UBool success = ((opValue & URX_NEG_SET) == URX_NEG_SET); + opValue &= ~URX_NEG_SET; + U_ASSERT(opValue > 0 && opValue < URX_LAST_SET); + + UChar32 c; + U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); + if (c < 256) { + Regex8BitSet &s8 = RegexStaticSets::gStaticSets->fPropSets8[opValue]; + if (s8.contains(c)) { + success = !success; + } + } else { + const UnicodeSet &s = RegexStaticSets::gStaticSets->fPropSets[opValue]; + if (s.contains(c)) { + success = !success; + } + } + if (!success) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_STAT_SETREF_N: + { + // Test input character for NOT being a member of one of + // the predefined sets (Word Characters, for example) + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + U_ASSERT(opValue > 0 && opValue < URX_LAST_SET); + + UChar32 c; + U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); + if (c < 256) { + Regex8BitSet &s8 = RegexStaticSets::gStaticSets->fPropSets8[opValue]; + if (s8.contains(c) == false) { + break; + } + } else { + const UnicodeSet &s = RegexStaticSets::gStaticSets->fPropSets[opValue]; + if (s.contains(c) == false) { + break; + } + } + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_SETREF: + { + if (fp->fInputIdx >= fActiveLimit) { + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + U_ASSERT(opValue > 0 && opValue < fSets->size()); + + // There is input left. Pick up one char and test it for set membership. + UChar32 c; + U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); + if (c<256) { + Regex8BitSet *s8 = &fPattern->fSets8[opValue]; + if (s8->contains(c)) { + // The character is in the set. A Match. + break; + } + } else { + UnicodeSet *s = (UnicodeSet *)fSets->elementAt(opValue); + if (s->contains(c)) { + // The character is in the set. A Match. + break; + } + } + + // the character wasn't in the set. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_DOTANY: + { + // . matches anything, but stops at end-of-line. + if (fp->fInputIdx >= fActiveLimit) { + // At end of input. Match failed. Backtrack out. + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + // There is input left. Advance over one char, unless we've hit end-of-line + UChar32 c; + U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); + if (isLineTerminator(c)) { + // End of line in normal mode. . does not match. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + } + break; + + + case URX_DOTANY_ALL: + { + // . in dot-matches-all (including new lines) mode + if (fp->fInputIdx >= fActiveLimit) { + // At end of input. Match failed. Backtrack out. + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + // There is input left. Advance over one char, except if we are + // at a cr/lf, advance over both of them. + UChar32 c; + U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); + if (c==0x0d && fp->fInputIdx < fActiveLimit) { + // In the case of a CR/LF, we need to advance over both. + if (inputBuf[fp->fInputIdx] == 0x0a) { + U16_FWD_1(inputBuf, fp->fInputIdx, fActiveLimit); + } + } + } + break; + + + case URX_DOTANY_UNIX: + { + // '.' operator, matches all, but stops at end-of-line. + // UNIX_LINES mode, so 0x0a is the only recognized line ending. + if (fp->fInputIdx >= fActiveLimit) { + // At end of input. Match failed. Backtrack out. + fHitEnd = true; + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + // There is input left. Advance over one char, unless we've hit end-of-line + UChar32 c; + U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); + if (c == 0x0a) { + // End of line in normal mode. '.' does not match the \n + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + + case URX_JMP: + fp->fPatIdx = opValue; + break; + + case URX_FAIL: + isMatch = false; + goto breakFromLoop; + + case URX_JMP_SAV: + U_ASSERT(opValue < fPattern->fCompiledPat->size()); + fp = StateSave(fp, fp->fPatIdx, status); // State save to loc following current + fp->fPatIdx = opValue; // Then JMP. + break; + + case URX_JMP_SAV_X: + // This opcode is used with (x)+, when x can match a zero length string. + // Same as JMP_SAV, except conditional on the match having made forward progress. + // Destination of the JMP must be a URX_STO_INP_LOC, from which we get the + // data address of the input position at the start of the loop. + { + U_ASSERT(opValue > 0 && opValue < fPattern->fCompiledPat->size()); + int32_t stoOp = (int32_t)pat[opValue-1]; + U_ASSERT(URX_TYPE(stoOp) == URX_STO_INP_LOC); + int32_t frameLoc = URX_VAL(stoOp); + U_ASSERT(frameLoc >= 0 && frameLoc < fFrameSize); + int32_t prevInputIdx = (int32_t)fp->fExtra[frameLoc]; + U_ASSERT(prevInputIdx <= fp->fInputIdx); + if (prevInputIdx < fp->fInputIdx) { + // The match did make progress. Repeat the loop. + fp = StateSave(fp, fp->fPatIdx, status); // State save to loc following current + fp->fPatIdx = opValue; + fp->fExtra[frameLoc] = fp->fInputIdx; + } + // If the input position did not advance, we do nothing here, + // execution will fall out of the loop. + } + break; + + case URX_CTR_INIT: + { + U_ASSERT(opValue >= 0 && opValue < fFrameSize-2); + fp->fExtra[opValue] = 0; // Set the loop counter variable to zero + + // Pick up the three extra operands that CTR_INIT has, and + // skip the pattern location counter past + int32_t instrOperandLoc = (int32_t)fp->fPatIdx; + fp->fPatIdx += 3; + int32_t loopLoc = URX_VAL(pat[instrOperandLoc]); + int32_t minCount = (int32_t)pat[instrOperandLoc+1]; + int32_t maxCount = (int32_t)pat[instrOperandLoc+2]; + U_ASSERT(minCount>=0); + U_ASSERT(maxCount>=minCount || maxCount==-1); + U_ASSERT(loopLoc>=fp->fPatIdx); + + if (minCount == 0) { + fp = StateSave(fp, loopLoc+1, status); + } + if (maxCount == -1) { + fp->fExtra[opValue+1] = fp->fInputIdx; // For loop breaking. + } else if (maxCount == 0) { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + case URX_CTR_LOOP: + { + U_ASSERT(opValue>0 && opValue < fp->fPatIdx-2); + int32_t initOp = (int32_t)pat[opValue]; + U_ASSERT(URX_TYPE(initOp) == URX_CTR_INIT); + int64_t *pCounter = &fp->fExtra[URX_VAL(initOp)]; + int32_t minCount = (int32_t)pat[opValue+2]; + int32_t maxCount = (int32_t)pat[opValue+3]; + (*pCounter)++; + if ((uint64_t)*pCounter >= (uint32_t)maxCount && maxCount != -1) { + U_ASSERT(*pCounter == maxCount); + break; + } + if (*pCounter >= minCount) { + if (maxCount == -1) { + // Loop has no hard upper bound. + // Check that it is progressing through the input, break if it is not. + int64_t *pLastInputIdx = &fp->fExtra[URX_VAL(initOp) + 1]; + if (fp->fInputIdx == *pLastInputIdx) { + break; + } else { + *pLastInputIdx = fp->fInputIdx; + } + } + fp = StateSave(fp, fp->fPatIdx, status); + } else { + // Increment time-out counter. (StateSave() does it if count >= minCount) + fTickCounter--; + if (fTickCounter <= 0) { + IncrementTime(status); // Re-initializes fTickCounter + } + } + fp->fPatIdx = opValue + 4; // Loop back. + } + break; + + case URX_CTR_INIT_NG: + { + // Initialize a non-greedy loop + U_ASSERT(opValue >= 0 && opValue < fFrameSize-2); + fp->fExtra[opValue] = 0; // Set the loop counter variable to zero + + // Pick up the three extra operands that CTR_INIT_NG has, and + // skip the pattern location counter past + int32_t instrOperandLoc = (int32_t)fp->fPatIdx; + fp->fPatIdx += 3; + int32_t loopLoc = URX_VAL(pat[instrOperandLoc]); + int32_t minCount = (int32_t)pat[instrOperandLoc+1]; + int32_t maxCount = (int32_t)pat[instrOperandLoc+2]; + U_ASSERT(minCount>=0); + U_ASSERT(maxCount>=minCount || maxCount==-1); + U_ASSERT(loopLoc>fp->fPatIdx); + if (maxCount == -1) { + fp->fExtra[opValue+1] = fp->fInputIdx; // Save initial input index for loop breaking. + } + + if (minCount == 0) { + if (maxCount != 0) { + fp = StateSave(fp, fp->fPatIdx, status); + } + fp->fPatIdx = loopLoc+1; // Continue with stuff after repeated block + } + } + break; + + case URX_CTR_LOOP_NG: + { + // Non-greedy {min, max} loops + U_ASSERT(opValue>0 && opValue < fp->fPatIdx-2); + int32_t initOp = (int32_t)pat[opValue]; + U_ASSERT(URX_TYPE(initOp) == URX_CTR_INIT_NG); + int64_t *pCounter = &fp->fExtra[URX_VAL(initOp)]; + int32_t minCount = (int32_t)pat[opValue+2]; + int32_t maxCount = (int32_t)pat[opValue+3]; + + (*pCounter)++; + if ((uint64_t)*pCounter >= (uint32_t)maxCount && maxCount != -1) { + // The loop has matched the maximum permitted number of times. + // Break out of here with no action. Matching will + // continue with the following pattern. + U_ASSERT(*pCounter == maxCount); + break; + } + + if (*pCounter < minCount) { + // We haven't met the minimum number of matches yet. + // Loop back for another one. + fp->fPatIdx = opValue + 4; // Loop back. + fTickCounter--; + if (fTickCounter <= 0) { + IncrementTime(status); // Re-initializes fTickCounter + } + } else { + // We do have the minimum number of matches. + + // If there is no upper bound on the loop iterations, check that the input index + // is progressing, and stop the loop if it is not. + if (maxCount == -1) { + int64_t *pLastInputIdx = &fp->fExtra[URX_VAL(initOp) + 1]; + if (fp->fInputIdx == *pLastInputIdx) { + break; + } + *pLastInputIdx = fp->fInputIdx; + } + + // Loop Continuation: we will fall into the pattern following the loop + // (non-greedy, don't execute loop body first), but first do + // a state save to the top of the loop, so that a match failure + // in the following pattern will try another iteration of the loop. + fp = StateSave(fp, opValue + 4, status); + } + } + break; + + case URX_STO_SP: + U_ASSERT(opValue >= 0 && opValue < fPattern->fDataSize); + fData[opValue] = fStack->size(); + break; + + case URX_LD_SP: + { + U_ASSERT(opValue >= 0 && opValue < fPattern->fDataSize); + int32_t newStackSize = (int32_t)fData[opValue]; + U_ASSERT(newStackSize <= fStack->size()); + int64_t *newFP = fStack->getBuffer() + newStackSize - fFrameSize; + if (newFP == (int64_t *)fp) { + break; + } + int32_t j; + for (j=0; jsetSize(newStackSize); + } + break; + + case URX_BACKREF: + { + U_ASSERT(opValue < fFrameSize); + int64_t groupStartIdx = fp->fExtra[opValue]; + int64_t groupEndIdx = fp->fExtra[opValue+1]; + U_ASSERT(groupStartIdx <= groupEndIdx); + int64_t inputIndex = fp->fInputIdx; + if (groupStartIdx < 0) { + // This capture group has not participated in the match thus far, + fp = (REStackFrame *)fStack->popFrame(fFrameSize); // FAIL, no match. + break; + } + UBool success = true; + for (int64_t groupIndex = groupStartIdx; groupIndex < groupEndIdx; ++groupIndex,++inputIndex) { + if (inputIndex >= fActiveLimit) { + success = false; + fHitEnd = true; + break; + } + if (inputBuf[groupIndex] != inputBuf[inputIndex]) { + success = false; + break; + } + } + if (success && groupStartIdx < groupEndIdx && U16_IS_LEAD(inputBuf[groupEndIdx-1]) && + inputIndex < fActiveLimit && U16_IS_TRAIL(inputBuf[inputIndex])) { + // Capture group ended with an unpaired lead surrogate. + // Back reference is not permitted to match lead only of a surrogatge pair. + success = false; + } + if (success) { + fp->fInputIdx = inputIndex; + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + case URX_BACKREF_I: + { + U_ASSERT(opValue < fFrameSize); + int64_t groupStartIdx = fp->fExtra[opValue]; + int64_t groupEndIdx = fp->fExtra[opValue+1]; + U_ASSERT(groupStartIdx <= groupEndIdx); + if (groupStartIdx < 0) { + // This capture group has not participated in the match thus far, + fp = (REStackFrame *)fStack->popFrame(fFrameSize); // FAIL, no match. + break; + } + CaseFoldingUCharIterator captureGroupItr(inputBuf, groupStartIdx, groupEndIdx); + CaseFoldingUCharIterator inputItr(inputBuf, fp->fInputIdx, fActiveLimit); + + // Note: if the capture group match was of an empty string the backref + // match succeeds. Verified by testing: Perl matches succeed + // in this case, so we do too. + + UBool success = true; + for (;;) { + UChar32 captureGroupChar = captureGroupItr.next(); + if (captureGroupChar == U_SENTINEL) { + success = true; + break; + } + UChar32 inputChar = inputItr.next(); + if (inputChar == U_SENTINEL) { + success = false; + fHitEnd = true; + break; + } + if (inputChar != captureGroupChar) { + success = false; + break; + } + } + + if (success && inputItr.inExpansion()) { + // We obtained a match by consuming part of a string obtained from + // case-folding a single code point of the input text. + // This does not count as an overall match. + success = false; + } + + if (success) { + fp->fInputIdx = inputItr.getIndex(); + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + case URX_STO_INP_LOC: + { + U_ASSERT(opValue >= 0 && opValue < fFrameSize); + fp->fExtra[opValue] = fp->fInputIdx; + } + break; + + case URX_JMPX: + { + int32_t instrOperandLoc = (int32_t)fp->fPatIdx; + fp->fPatIdx += 1; + int32_t dataLoc = URX_VAL(pat[instrOperandLoc]); + U_ASSERT(dataLoc >= 0 && dataLoc < fFrameSize); + int32_t savedInputIdx = (int32_t)fp->fExtra[dataLoc]; + U_ASSERT(savedInputIdx <= fp->fInputIdx); + if (savedInputIdx < fp->fInputIdx) { + fp->fPatIdx = opValue; // JMP + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); // FAIL, no progress in loop. + } + } + break; + + case URX_LA_START: + { + // Entering a look around block. + // Save Stack Ptr, Input Pos. + U_ASSERT(opValue>=0 && opValue+3fDataSize); + fData[opValue] = fStack->size(); + fData[opValue+1] = fp->fInputIdx; + fData[opValue+2] = fActiveStart; + fData[opValue+3] = fActiveLimit; + fActiveStart = fLookStart; // Set the match region change for + fActiveLimit = fLookLimit; // transparent bounds. + } + break; + + case URX_LA_END: + { + // Leaving a look around block. + // restore Stack Ptr, Input Pos to positions they had on entry to block. + U_ASSERT(opValue>=0 && opValue+3fDataSize); + int32_t stackSize = fStack->size(); + int32_t newStackSize = (int32_t)fData[opValue]; + U_ASSERT(stackSize >= newStackSize); + if (stackSize > newStackSize) { + // Copy the current top frame back to the new (cut back) top frame. + // This makes the capture groups from within the look-ahead + // expression available. + int64_t *newFP = fStack->getBuffer() + newStackSize - fFrameSize; + int32_t j; + for (j=0; jsetSize(newStackSize); + } + fp->fInputIdx = fData[opValue+1]; + + // Restore the active region bounds in the input string; they may have + // been changed because of transparent bounds on a Region. + fActiveStart = fData[opValue+2]; + fActiveLimit = fData[opValue+3]; + U_ASSERT(fActiveStart >= 0); + U_ASSERT(fActiveLimit <= fInputLength); + } + break; + + case URX_ONECHAR_I: + if (fp->fInputIdx < fActiveLimit) { + UChar32 c; + U16_NEXT(inputBuf, fp->fInputIdx, fActiveLimit, c); + if (u_foldCase(c, U_FOLD_CASE_DEFAULT) == opValue) { + break; + } + } else { + fHitEnd = true; + } + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + + case URX_STRING_I: + // Case-insensitive test input against a literal string. + // Strings require two slots in the compiled pattern, one for the + // offset to the string text, and one for the length. + // The compiled string has already been case folded. + { + const char16_t *patternString = litText + opValue; + + op = (int32_t)pat[fp->fPatIdx]; + fp->fPatIdx++; + opType = URX_TYPE(op); + opValue = URX_VAL(op); + U_ASSERT(opType == URX_STRING_LEN); + int32_t patternStringLen = opValue; // Length of the string from the pattern. + + UChar32 cText; + UChar32 cPattern; + UBool success = true; + int32_t patternStringIdx = 0; + CaseFoldingUCharIterator inputIterator(inputBuf, fp->fInputIdx, fActiveLimit); + while (patternStringIdx < patternStringLen) { + U16_NEXT(patternString, patternStringIdx, patternStringLen, cPattern); + cText = inputIterator.next(); + if (cText != cPattern) { + success = false; + if (cText == U_SENTINEL) { + fHitEnd = true; + } + break; + } + } + if (inputIterator.inExpansion()) { + success = false; + } + + if (success) { + fp->fInputIdx = inputIterator.getIndex(); + } else { + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + } + break; + + case URX_LB_START: + { + // Entering a look-behind block. + // Save Stack Ptr, Input Pos and active input region. + // TODO: implement transparent bounds. Ticket #6067 + U_ASSERT(opValue>=0 && opValue+4fDataSize); + fData[opValue] = fStack->size(); + fData[opValue+1] = fp->fInputIdx; + // Save input string length, then reset to pin any matches to end at + // the current position. + fData[opValue+2] = fActiveStart; + fData[opValue+3] = fActiveLimit; + fActiveStart = fRegionStart; + fActiveLimit = fp->fInputIdx; + // Init the variable containing the start index for attempted matches. + fData[opValue+4] = -1; + } + break; + + + case URX_LB_CONT: + { + // Positive Look-Behind, at top of loop checking for matches of LB expression + // at all possible input starting positions. + + // Fetch the min and max possible match lengths. They are the operands + // of this op in the pattern. + int32_t minML = (int32_t)pat[fp->fPatIdx++]; + int32_t maxML = (int32_t)pat[fp->fPatIdx++]; + U_ASSERT(minML <= maxML); + U_ASSERT(minML >= 0); + + // Fetch (from data) the last input index where a match was attempted. + U_ASSERT(opValue>=0 && opValue+4fDataSize); + int64_t &lbStartIdx = fData[opValue+4]; + if (lbStartIdx < 0) { + // First time through loop. + lbStartIdx = fp->fInputIdx - minML; + if (lbStartIdx > 0 && lbStartIdx < fInputLength) { + U16_SET_CP_START(inputBuf, 0, lbStartIdx); + } + } else { + // 2nd through nth time through the loop. + // Back up start position for match by one. + if (lbStartIdx == 0) { + lbStartIdx--; + } else { + U16_BACK_1(inputBuf, 0, lbStartIdx); + } + } + + if (lbStartIdx < 0 || lbStartIdx < fp->fInputIdx - maxML) { + // We have tried all potential match starting points without + // getting a match. Backtrack out, and out of the + // Look Behind altogether. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + fActiveStart = fData[opValue+2]; + fActiveLimit = fData[opValue+3]; + U_ASSERT(fActiveStart >= 0); + U_ASSERT(fActiveLimit <= fInputLength); + break; + } + + // Save state to this URX_LB_CONT op, so failure to match will repeat the loop. + // (successful match will fall off the end of the loop.) + fp = StateSave(fp, fp->fPatIdx-3, status); + fp->fInputIdx = lbStartIdx; + } + break; + + case URX_LB_END: + // End of a look-behind block, after a successful match. + { + U_ASSERT(opValue>=0 && opValue+4fDataSize); + if (fp->fInputIdx != fActiveLimit) { + // The look-behind expression matched, but the match did not + // extend all the way to the point that we are looking behind from. + // FAIL out of here, which will take us back to the LB_CONT, which + // will retry the match starting at another position or fail + // the look-behind altogether, whichever is appropriate. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + // Look-behind match is good. Restore the original input string region, + // which had been truncated to pin the end of the lookbehind match to the + // position being looked-behind. + fActiveStart = fData[opValue+2]; + fActiveLimit = fData[opValue+3]; + U_ASSERT(fActiveStart >= 0); + U_ASSERT(fActiveLimit <= fInputLength); + } + break; + + + case URX_LBN_CONT: + { + // Negative Look-Behind, at top of loop checking for matches of LB expression + // at all possible input starting positions. + + // Fetch the extra parameters of this op. + int32_t minML = (int32_t)pat[fp->fPatIdx++]; + int32_t maxML = (int32_t)pat[fp->fPatIdx++]; + int32_t continueLoc = (int32_t)pat[fp->fPatIdx++]; + continueLoc = URX_VAL(continueLoc); + U_ASSERT(minML <= maxML); + U_ASSERT(minML >= 0); + U_ASSERT(continueLoc > fp->fPatIdx); + + // Fetch (from data) the last input index where a match was attempted. + U_ASSERT(opValue>=0 && opValue+4fDataSize); + int64_t &lbStartIdx = fData[opValue+4]; + if (lbStartIdx < 0) { + // First time through loop. + lbStartIdx = fp->fInputIdx - minML; + if (lbStartIdx > 0 && lbStartIdx < fInputLength) { + U16_SET_CP_START(inputBuf, 0, lbStartIdx); + } + } else { + // 2nd through nth time through the loop. + // Back up start position for match by one. + if (lbStartIdx == 0) { + lbStartIdx--; // Because U16_BACK is unsafe starting at 0. + } else { + U16_BACK_1(inputBuf, 0, lbStartIdx); + } + } + + if (lbStartIdx < 0 || lbStartIdx < fp->fInputIdx - maxML) { + // We have tried all potential match starting points without + // getting a match, which means that the negative lookbehind as + // a whole has succeeded. Jump forward to the continue location + fActiveStart = fData[opValue+2]; + fActiveLimit = fData[opValue+3]; + U_ASSERT(fActiveStart >= 0); + U_ASSERT(fActiveLimit <= fInputLength); + fp->fPatIdx = continueLoc; + break; + } + + // Save state to this URX_LB_CONT op, so failure to match will repeat the loop. + // (successful match will cause a FAIL out of the loop altogether.) + fp = StateSave(fp, fp->fPatIdx-4, status); + fp->fInputIdx = lbStartIdx; + } + break; + + case URX_LBN_END: + // End of a negative look-behind block, after a successful match. + { + U_ASSERT(opValue>=0 && opValue+4fDataSize); + if (fp->fInputIdx != fActiveLimit) { + // The look-behind expression matched, but the match did not + // extend all the way to the point that we are looking behind from. + // FAIL out of here, which will take us back to the LB_CONT, which + // will retry the match starting at another position or succeed + // the look-behind altogether, whichever is appropriate. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + break; + } + + // Look-behind expression matched, which means look-behind test as + // a whole Fails + + // Restore the original input string length, which had been truncated + // inorder to pin the end of the lookbehind match + // to the position being looked-behind. + fActiveStart = fData[opValue+2]; + fActiveLimit = fData[opValue+3]; + U_ASSERT(fActiveStart >= 0); + U_ASSERT(fActiveLimit <= fInputLength); + + // Restore original stack position, discarding any state saved + // by the successful pattern match. + U_ASSERT(opValue>=0 && opValue+1fDataSize); + int32_t newStackSize = (int32_t)fData[opValue]; + U_ASSERT(fStack->size() > newStackSize); + fStack->setSize(newStackSize); + + // FAIL, which will take control back to someplace + // prior to entering the look-behind test. + fp = (REStackFrame *)fStack->popFrame(fFrameSize); + } + break; + + + case URX_LOOP_SR_I: + // Loop Initialization for the optimized implementation of + // [some character set]* + // This op scans through all matching input. + // The following LOOP_C op emulates stack unwinding if the following pattern fails. + { + U_ASSERT(opValue > 0 && opValue < fSets->size()); + Regex8BitSet *s8 = &fPattern->fSets8[opValue]; + UnicodeSet *s = (UnicodeSet *)fSets->elementAt(opValue); + + // Loop through input, until either the input is exhausted or + // we reach a character that is not a member of the set. + int32_t ix = (int32_t)fp->fInputIdx; + for (;;) { + if (ix >= fActiveLimit) { + fHitEnd = true; + break; + } + UChar32 c; + U16_NEXT(inputBuf, ix, fActiveLimit, c); + if (c<256) { + if (s8->contains(c) == false) { + U16_BACK_1(inputBuf, 0, ix); + break; + } + } else { + if (s->contains(c) == false) { + U16_BACK_1(inputBuf, 0, ix); + break; + } + } + } + + // If there were no matching characters, skip over the loop altogether. + // The loop doesn't run at all, a * op always succeeds. + if (ix == fp->fInputIdx) { + fp->fPatIdx++; // skip the URX_LOOP_C op. + break; + } + + // Peek ahead in the compiled pattern, to the URX_LOOP_C that + // must follow. It's operand is the stack location + // that holds the starting input index for the match of this [set]* + int32_t loopcOp = (int32_t)pat[fp->fPatIdx]; + U_ASSERT(URX_TYPE(loopcOp) == URX_LOOP_C); + int32_t stackLoc = URX_VAL(loopcOp); + U_ASSERT(stackLoc >= 0 && stackLoc < fFrameSize); + fp->fExtra[stackLoc] = fp->fInputIdx; + fp->fInputIdx = ix; + + // Save State to the URX_LOOP_C op that follows this one, + // so that match failures in the following code will return to there. + // Then bump the pattern idx so the LOOP_C is skipped on the way out of here. + fp = StateSave(fp, fp->fPatIdx, status); + fp->fPatIdx++; + } + break; + + + case URX_LOOP_DOT_I: + // Loop Initialization for the optimized implementation of .* + // This op scans through all remaining input. + // The following LOOP_C op emulates stack unwinding if the following pattern fails. + { + // Loop through input until the input is exhausted (we reach an end-of-line) + // In DOTALL mode, we can just go straight to the end of the input. + int32_t ix; + if ((opValue & 1) == 1) { + // Dot-matches-All mode. Jump straight to the end of the string. + ix = (int32_t)fActiveLimit; + fHitEnd = true; + } else { + // NOT DOT ALL mode. Line endings do not match '.' + // Scan forward until a line ending or end of input. + ix = (int32_t)fp->fInputIdx; + for (;;) { + if (ix >= fActiveLimit) { + fHitEnd = true; + break; + } + UChar32 c; + U16_NEXT(inputBuf, ix, fActiveLimit, c); // c = inputBuf[ix++] + if ((c & 0x7f) <= 0x29) { // Fast filter of non-new-line-s + if ((c == 0x0a) || // 0x0a is newline in both modes. + (((opValue & 2) == 0) && // IF not UNIX_LINES mode + isLineTerminator(c))) { + // char is a line ending. Put the input pos back to the + // line ending char, and exit the scanning loop. + U16_BACK_1(inputBuf, 0, ix); + break; + } + } + } + } + + // If there were no matching characters, skip over the loop altogether. + // The loop doesn't run at all, a * op always succeeds. + if (ix == fp->fInputIdx) { + fp->fPatIdx++; // skip the URX_LOOP_C op. + break; + } + + // Peek ahead in the compiled pattern, to the URX_LOOP_C that + // must follow. It's operand is the stack location + // that holds the starting input index for the match of this .* + int32_t loopcOp = (int32_t)pat[fp->fPatIdx]; + U_ASSERT(URX_TYPE(loopcOp) == URX_LOOP_C); + int32_t stackLoc = URX_VAL(loopcOp); + U_ASSERT(stackLoc >= 0 && stackLoc < fFrameSize); + fp->fExtra[stackLoc] = fp->fInputIdx; + fp->fInputIdx = ix; + + // Save State to the URX_LOOP_C op that follows this one, + // so that match failures in the following code will return to there. + // Then bump the pattern idx so the LOOP_C is skipped on the way out of here. + fp = StateSave(fp, fp->fPatIdx, status); + fp->fPatIdx++; + } + break; + + + case URX_LOOP_C: + { + U_ASSERT(opValue>=0 && opValuefExtra[opValue]; + U_ASSERT(backSearchIndex <= fp->fInputIdx); + if (backSearchIndex == fp->fInputIdx) { + // We've backed up the input idx to the point that the loop started. + // The loop is done. Leave here without saving state. + // Subsequent failures won't come back here. + break; + } + // Set up for the next iteration of the loop, with input index + // backed up by one from the last time through, + // and a state save to this instruction in case the following code fails again. + // (We're going backwards because this loop emulates stack unwinding, not + // the initial scan forward.) + U_ASSERT(fp->fInputIdx > 0); + UChar32 prevC; + U16_PREV(inputBuf, 0, fp->fInputIdx, prevC); // !!!: should this 0 be one of f*Limit? + + if (prevC == 0x0a && + fp->fInputIdx > backSearchIndex && + inputBuf[fp->fInputIdx-1] == 0x0d) { + int32_t prevOp = (int32_t)pat[fp->fPatIdx-2]; + if (URX_TYPE(prevOp) == URX_LOOP_DOT_I) { + // .*, stepping back over CRLF pair. + U16_BACK_1(inputBuf, 0, fp->fInputIdx); + } + } + + + fp = StateSave(fp, fp->fPatIdx-1, status); + } + break; + + + + default: + // Trouble. The compiled pattern contains an entry with an + // unrecognized type tag. + UPRV_UNREACHABLE_ASSERT; + // Unknown opcode type in opType = URX_TYPE(pat[fp->fPatIdx]). But we have + // reports of this in production code, don't use UPRV_UNREACHABLE_EXIT. + // See ICU-21669. + status = U_INTERNAL_PROGRAM_ERROR; + } + + if (U_FAILURE(status)) { + isMatch = false; + break; + } + } + +breakFromLoop: + fMatch = isMatch; + if (isMatch) { + fLastMatchEnd = fMatchEnd; + fMatchStart = startIdx; + fMatchEnd = fp->fInputIdx; + } + +#ifdef REGEX_RUN_DEBUG + if (fTraceDebug) { + if (isMatch) { + printf("Match. start=%ld end=%ld\n\n", fMatchStart, fMatchEnd); + } else { + printf("No match\n\n"); + } + } +#endif + + fFrame = fp; // The active stack frame when the engine stopped. + // Contains the capture group results that we need to + // access later. + + return; +} + + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RegexMatcher) + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_REGULAR_EXPRESSIONS + diff --git a/deps/icu-small/source/i18n/remtrans.cpp b/deps/icu-small/source/i18n/remtrans.cpp index 957ac480fbff57..775ca91565e85c 100644 --- a/deps/icu-small/source/i18n/remtrans.cpp +++ b/deps/icu-small/source/i18n/remtrans.cpp @@ -1,71 +1,71 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2001-2011, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 04/02/2001 aliu Creation. -********************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "remtrans.h" -#include "unicode/unifilt.h" - -static const UChar CURR_ID[] = {65, 110, 121, 45, 0x52, 0x65, 0x6D, 0x6F, 0x76, 0x65, 0x00}; /* "Any-Remove" */ - -U_NAMESPACE_BEGIN - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RemoveTransliterator) - -/** - * Factory method - */ -static Transliterator* RemoveTransliterator_create(const UnicodeString& /*ID*/, - Transliterator::Token /*context*/) { - /* We don't need the ID or context. We just remove data */ - return new RemoveTransliterator(); -} - -/** - * System registration hook. - */ -void RemoveTransliterator::registerIDs() { - - Transliterator::_registerFactory(UnicodeString(true, ::CURR_ID, -1), - RemoveTransliterator_create, integerToken(0)); - - Transliterator::_registerSpecialInverse(UNICODE_STRING_SIMPLE("Remove"), - UNICODE_STRING_SIMPLE("Null"), false); -} - -RemoveTransliterator::RemoveTransliterator() : Transliterator(UnicodeString(true, ::CURR_ID, -1), 0) {} - -RemoveTransliterator::~RemoveTransliterator() {} - -RemoveTransliterator* RemoveTransliterator::clone() const { - RemoveTransliterator* result = new RemoveTransliterator(); - if (result != NULL && getFilter() != 0) { - result->adoptFilter(getFilter()->clone()); - } - return result; -} - -void RemoveTransliterator::handleTransliterate(Replaceable& text, UTransPosition& index, - UBool /*isIncremental*/) const { - // Our caller (filteredTransliterate) has already narrowed us - // to an unfiltered run. Delete it. - UnicodeString empty; - text.handleReplaceBetween(index.start, index.limit, empty); - int32_t len = index.limit - index.start; - index.contextLimit -= len; - index.limit -= len; -} -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2001-2011, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 04/02/2001 aliu Creation. +********************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "remtrans.h" +#include "unicode/unifilt.h" + +static const char16_t CURR_ID[] = {65, 110, 121, 45, 0x52, 0x65, 0x6D, 0x6F, 0x76, 0x65, 0x00}; /* "Any-Remove" */ + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RemoveTransliterator) + +/** + * Factory method + */ +static Transliterator* RemoveTransliterator_create(const UnicodeString& /*ID*/, + Transliterator::Token /*context*/) { + /* We don't need the ID or context. We just remove data */ + return new RemoveTransliterator(); +} + +/** + * System registration hook. + */ +void RemoveTransliterator::registerIDs() { + + Transliterator::_registerFactory(UnicodeString(true, ::CURR_ID, -1), + RemoveTransliterator_create, integerToken(0)); + + Transliterator::_registerSpecialInverse(UNICODE_STRING_SIMPLE("Remove"), + UNICODE_STRING_SIMPLE("Null"), false); +} + +RemoveTransliterator::RemoveTransliterator() : Transliterator(UnicodeString(true, ::CURR_ID, -1), 0) {} + +RemoveTransliterator::~RemoveTransliterator() {} + +RemoveTransliterator* RemoveTransliterator::clone() const { + RemoveTransliterator* result = new RemoveTransliterator(); + if (result != nullptr && getFilter() != 0) { + result->adoptFilter(getFilter()->clone()); + } + return result; +} + +void RemoveTransliterator::handleTransliterate(Replaceable& text, UTransPosition& index, + UBool /*isIncremental*/) const { + // Our caller (filteredTransliterate) has already narrowed us + // to an unfiltered run. Delete it. + UnicodeString empty; + text.handleReplaceBetween(index.start, index.limit, empty); + int32_t len = index.limit - index.start; + index.contextLimit -= len; + index.limit -= len; +} +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ diff --git a/deps/icu-small/source/i18n/remtrans.h b/deps/icu-small/source/i18n/remtrans.h index 398cc5177cd57c..7e547b5f3b22eb 100644 --- a/deps/icu-small/source/i18n/remtrans.h +++ b/deps/icu-small/source/i18n/remtrans.h @@ -1,80 +1,80 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2001-2007, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* Date Name Description -* 04/02/2001 aliu Creation. -********************************************************************** -*/ -#ifndef REMTRANS_H -#define REMTRANS_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_TRANSLITERATION - -#include "unicode/translit.h" - -U_NAMESPACE_BEGIN - -/** - * A transliterator that removes text. - * @author Alan Liu - */ -class RemoveTransliterator : public Transliterator { - -public: - - /** - * Constructs a transliterator. - */ - RemoveTransliterator(); - - /** - * Destructor. - */ - virtual ~RemoveTransliterator(); - - /** - * System registration hook. - */ - static void registerIDs(); - - /** - * Transliterator API. - * @return A copy of the object. - */ - virtual RemoveTransliterator* clone() const override; - - /** - * Implements {@link Transliterator#handleTransliterate}. - * @param text the buffer holding transliterated and - * untransliterated text - * @param offset the start and limit of the text, the position - * of the cursor, and the start and limit of transliteration. - * @param incremental if true, assume more text may be coming after - * pos.contextLimit. Otherwise, assume the text is complete. - */ - virtual void handleTransliterate(Replaceable& text, UTransPosition& offset, - UBool isIncremental) const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for the actual class. - */ - virtual UClassID getDynamicClassID() const override; - - /** - * ICU "poor man's RTTI", returns a UClassID for this class. - */ - U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); - -}; - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_TRANSLITERATION */ - -#endif +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (c) 2001-2007, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Date Name Description +* 04/02/2001 aliu Creation. +********************************************************************** +*/ +#ifndef REMTRANS_H +#define REMTRANS_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_TRANSLITERATION + +#include "unicode/translit.h" + +U_NAMESPACE_BEGIN + +/** + * A transliterator that removes text. + * @author Alan Liu + */ +class RemoveTransliterator : public Transliterator { + +public: + + /** + * Constructs a transliterator. + */ + RemoveTransliterator(); + + /** + * Destructor. + */ + virtual ~RemoveTransliterator(); + + /** + * System registration hook. + */ + static void registerIDs(); + + /** + * Transliterator API. + * @return A copy of the object. + */ + virtual RemoveTransliterator* clone() const override; + + /** + * Implements {@link Transliterator#handleTransliterate}. + * @param text the buffer holding transliterated and + * untransliterated text + * @param offset the start and limit of the text, the position + * of the cursor, and the start and limit of transliteration. + * @param incremental if true, assume more text may be coming after + * pos.contextLimit. Otherwise, assume the text is complete. + */ + virtual void handleTransliterate(Replaceable& text, UTransPosition& offset, + UBool isIncremental) const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + */ + virtual UClassID getDynamicClassID() const override; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(); + +}; + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + +#endif diff --git a/deps/icu-small/source/i18n/repattrn.cpp b/deps/icu-small/source/i18n/repattrn.cpp index 0ef85bdf6ce833..65494256a8985c 100644 --- a/deps/icu-small/source/i18n/repattrn.cpp +++ b/deps/icu-small/source/i18n/repattrn.cpp @@ -1,875 +1,875 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -// -// file: repattrn.cpp -// -/* -*************************************************************************** -* Copyright (C) 2002-2016 International Business Machines Corporation -* and others. All rights reserved. -*************************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_REGULAR_EXPRESSIONS - -#include "unicode/regex.h" -#include "unicode/uclean.h" -#include "cmemory.h" -#include "cstr.h" -#include "uassert.h" -#include "uhash.h" -#include "uvector.h" -#include "uvectr32.h" -#include "uvectr64.h" -#include "regexcmp.h" -#include "regeximp.h" -#include "regexst.h" - -U_NAMESPACE_BEGIN - -//-------------------------------------------------------------------------- -// -// RegexPattern Default Constructor -// -//-------------------------------------------------------------------------- -RegexPattern::RegexPattern() { - // Init all of this instances data. - init(); -} - - -//-------------------------------------------------------------------------- -// -// Copy Constructor Note: This is a rather inefficient implementation, -// but it probably doesn't matter. -// -//-------------------------------------------------------------------------- -RegexPattern::RegexPattern(const RegexPattern &other) : UObject(other) { - init(); - *this = other; -} - - - -//-------------------------------------------------------------------------- -// -// Assignment Operator -// -//-------------------------------------------------------------------------- -RegexPattern &RegexPattern::operator = (const RegexPattern &other) { - if (this == &other) { - // Source and destination are the same. Don't do anything. - return *this; - } - - // Clean out any previous contents of object being assigned to. - zap(); - - // Give target object a default initialization - init(); - - // Copy simple fields - fDeferredStatus = other.fDeferredStatus; - - if (U_FAILURE(fDeferredStatus)) { - return *this; - } - - if (other.fPatternString == NULL) { - fPatternString = NULL; - fPattern = utext_clone(fPattern, other.fPattern, false, true, &fDeferredStatus); - } else { - fPatternString = new UnicodeString(*(other.fPatternString)); - if (fPatternString == NULL) { - fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; - } else { - fPattern = utext_openConstUnicodeString(NULL, fPatternString, &fDeferredStatus); - } - } - if (U_FAILURE(fDeferredStatus)) { - return *this; - } - - fFlags = other.fFlags; - fLiteralText = other.fLiteralText; - fMinMatchLen = other.fMinMatchLen; - fFrameSize = other.fFrameSize; - fDataSize = other.fDataSize; - - fStartType = other.fStartType; - fInitialStringIdx = other.fInitialStringIdx; - fInitialStringLen = other.fInitialStringLen; - *fInitialChars = *other.fInitialChars; - fInitialChar = other.fInitialChar; - *fInitialChars8 = *other.fInitialChars8; - fNeedsAltInput = other.fNeedsAltInput; - - // Copy the pattern. It's just values, nothing deep to copy. - fCompiledPat->assign(*other.fCompiledPat, fDeferredStatus); - fGroupMap->assign(*other.fGroupMap, fDeferredStatus); - - // Copy the Unicode Sets. - // Could be made more efficient if the sets were reference counted and shared, - // but I doubt that pattern copying will be particularly common. - // Note: init() already added an empty element zero to fSets - int32_t i; - int32_t numSets = other.fSets->size(); - fSets8 = new Regex8BitSet[numSets]; - if (fSets8 == NULL) { - fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - for (i=1; ielementAt(i); - UnicodeSet *newSet = new UnicodeSet(*sourceSet); - if (newSet == NULL) { - fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; - break; - } - fSets->addElement(newSet, fDeferredStatus); - fSets8[i] = other.fSets8[i]; - } - - // Copy the named capture group hash map. - if (other.fNamedCaptureMap != nullptr && initNamedCaptureMap()) { - int32_t hashPos = UHASH_FIRST; - while (const UHashElement *hashEl = uhash_nextElement(other.fNamedCaptureMap, &hashPos)) { - if (U_FAILURE(fDeferredStatus)) { - break; - } - const UnicodeString *name = (const UnicodeString *)hashEl->key.pointer; - UnicodeString *key = new UnicodeString(*name); - int32_t val = hashEl->value.integer; - if (key == NULL) { - fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; - } else { - uhash_puti(fNamedCaptureMap, key, val, &fDeferredStatus); - } - } - } - return *this; -} - - -//-------------------------------------------------------------------------- -// -// init Shared initialization for use by constructors. -// Bring an uninitialized RegexPattern up to a default state. -// -//-------------------------------------------------------------------------- -void RegexPattern::init() { - fFlags = 0; - fCompiledPat = 0; - fLiteralText.remove(); - fSets = NULL; - fSets8 = NULL; - fDeferredStatus = U_ZERO_ERROR; - fMinMatchLen = 0; - fFrameSize = 0; - fDataSize = 0; - fGroupMap = NULL; - fStartType = START_NO_INFO; - fInitialStringIdx = 0; - fInitialStringLen = 0; - fInitialChars = NULL; - fInitialChar = 0; - fInitialChars8 = NULL; - fNeedsAltInput = false; - fNamedCaptureMap = NULL; - - fPattern = NULL; // will be set later - fPatternString = NULL; // may be set later - fCompiledPat = new UVector64(fDeferredStatus); - fGroupMap = new UVector32(fDeferredStatus); - fSets = new UVector(fDeferredStatus); - fInitialChars = new UnicodeSet; - fInitialChars8 = new Regex8BitSet; - if (U_FAILURE(fDeferredStatus)) { - return; - } - if (fCompiledPat == NULL || fGroupMap == NULL || fSets == NULL || - fInitialChars == NULL || fInitialChars8 == NULL) { - fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - - // Slot zero of the vector of sets is reserved. Fill it here. - fSets->addElement((int32_t)0, fDeferredStatus); -} - - -bool RegexPattern::initNamedCaptureMap() { - if (fNamedCaptureMap) { - return true; - } - fNamedCaptureMap = uhash_openSize(uhash_hashUnicodeString, // Key hash function - uhash_compareUnicodeString, // Key comparator function - uhash_compareLong, // Value comparator function - 7, // Initial table capacity - &fDeferredStatus); - if (U_FAILURE(fDeferredStatus)) { - return false; - } - - // fNamedCaptureMap owns its key strings, type (UnicodeString *) - uhash_setKeyDeleter(fNamedCaptureMap, uprv_deleteUObject); - return true; -} - -//-------------------------------------------------------------------------- -// -// zap Delete everything owned by this RegexPattern. -// -//-------------------------------------------------------------------------- -void RegexPattern::zap() { - delete fCompiledPat; - fCompiledPat = NULL; - int i; - for (i=1; isize(); i++) { - UnicodeSet *s; - s = (UnicodeSet *)fSets->elementAt(i); - if (s != NULL) { - delete s; - } - } - delete fSets; - fSets = NULL; - delete[] fSets8; - fSets8 = NULL; - delete fGroupMap; - fGroupMap = NULL; - delete fInitialChars; - fInitialChars = NULL; - delete fInitialChars8; - fInitialChars8 = NULL; - if (fPattern != NULL) { - utext_close(fPattern); - fPattern = NULL; - } - if (fPatternString != NULL) { - delete fPatternString; - fPatternString = NULL; - } - if (fNamedCaptureMap != NULL) { - uhash_close(fNamedCaptureMap); - fNamedCaptureMap = NULL; - } -} - - -//-------------------------------------------------------------------------- -// -// Destructor -// -//-------------------------------------------------------------------------- -RegexPattern::~RegexPattern() { - zap(); -} - - -//-------------------------------------------------------------------------- -// -// Clone -// -//-------------------------------------------------------------------------- -RegexPattern *RegexPattern::clone() const { - RegexPattern *copy = new RegexPattern(*this); - return copy; -} - - -//-------------------------------------------------------------------------- -// -// operator == (comparison) Consider to patterns to be == if the -// pattern strings and the flags are the same. -// Note that pattern strings with the same -// characters can still be considered different. -// -//-------------------------------------------------------------------------- -bool RegexPattern::operator ==(const RegexPattern &other) const { - if (this->fFlags == other.fFlags && this->fDeferredStatus == other.fDeferredStatus) { - if (this->fPatternString != NULL && other.fPatternString != NULL) { - return *(this->fPatternString) == *(other.fPatternString); - } else if (this->fPattern == NULL) { - if (other.fPattern == NULL) { - return true; - } - } else if (other.fPattern != NULL) { - UTEXT_SETNATIVEINDEX(this->fPattern, 0); - UTEXT_SETNATIVEINDEX(other.fPattern, 0); - return utext_equals(this->fPattern, other.fPattern); - } - } - return false; -} - -//--------------------------------------------------------------------- -// -// compile -// -//--------------------------------------------------------------------- -RegexPattern * U_EXPORT2 -RegexPattern::compile(const UnicodeString ®ex, - uint32_t flags, - UParseError &pe, - UErrorCode &status) -{ - if (U_FAILURE(status)) { - return NULL; - } - - const uint32_t allFlags = UREGEX_CANON_EQ | UREGEX_CASE_INSENSITIVE | UREGEX_COMMENTS | - UREGEX_DOTALL | UREGEX_MULTILINE | UREGEX_UWORD | - UREGEX_ERROR_ON_UNKNOWN_ESCAPES | UREGEX_UNIX_LINES | UREGEX_LITERAL; - - if ((flags & ~allFlags) != 0) { - status = U_REGEX_INVALID_FLAG; - return NULL; - } - - if ((flags & UREGEX_CANON_EQ) != 0) { - status = U_REGEX_UNIMPLEMENTED; - return NULL; - } - - RegexPattern *This = new RegexPattern; - if (This == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - if (U_FAILURE(This->fDeferredStatus)) { - status = This->fDeferredStatus; - delete This; - return NULL; - } - This->fFlags = flags; - - RegexCompile compiler(This, status); - compiler.compile(regex, pe, status); - - if (U_FAILURE(status)) { - delete This; - This = NULL; - } - - return This; -} - - -// -// compile, UText mode -// -RegexPattern * U_EXPORT2 -RegexPattern::compile(UText *regex, - uint32_t flags, - UParseError &pe, - UErrorCode &status) -{ - if (U_FAILURE(status)) { - return NULL; - } - - const uint32_t allFlags = UREGEX_CANON_EQ | UREGEX_CASE_INSENSITIVE | UREGEX_COMMENTS | - UREGEX_DOTALL | UREGEX_MULTILINE | UREGEX_UWORD | - UREGEX_ERROR_ON_UNKNOWN_ESCAPES | UREGEX_UNIX_LINES | UREGEX_LITERAL; - - if ((flags & ~allFlags) != 0) { - status = U_REGEX_INVALID_FLAG; - return NULL; - } - - if ((flags & UREGEX_CANON_EQ) != 0) { - status = U_REGEX_UNIMPLEMENTED; - return NULL; - } - - RegexPattern *This = new RegexPattern; - if (This == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - if (U_FAILURE(This->fDeferredStatus)) { - status = This->fDeferredStatus; - delete This; - return NULL; - } - This->fFlags = flags; - - RegexCompile compiler(This, status); - compiler.compile(regex, pe, status); - - if (U_FAILURE(status)) { - delete This; - This = NULL; - } - - return This; -} - -// -// compile with default flags. -// -RegexPattern * U_EXPORT2 -RegexPattern::compile(const UnicodeString ®ex, - UParseError &pe, - UErrorCode &err) -{ - return compile(regex, 0, pe, err); -} - - -// -// compile with default flags, UText mode -// -RegexPattern * U_EXPORT2 -RegexPattern::compile(UText *regex, - UParseError &pe, - UErrorCode &err) -{ - return compile(regex, 0, pe, err); -} - - -// -// compile with no UParseErr parameter. -// -RegexPattern * U_EXPORT2 -RegexPattern::compile(const UnicodeString ®ex, - uint32_t flags, - UErrorCode &err) -{ - UParseError pe; - return compile(regex, flags, pe, err); -} - - -// -// compile with no UParseErr parameter, UText mode -// -RegexPattern * U_EXPORT2 -RegexPattern::compile(UText *regex, - uint32_t flags, - UErrorCode &err) -{ - UParseError pe; - return compile(regex, flags, pe, err); -} - - -//--------------------------------------------------------------------- -// -// flags -// -//--------------------------------------------------------------------- -uint32_t RegexPattern::flags() const { - return fFlags; -} - - -//--------------------------------------------------------------------- -// -// matcher(UnicodeString, err) -// -//--------------------------------------------------------------------- -RegexMatcher *RegexPattern::matcher(const UnicodeString &input, - UErrorCode &status) const { - RegexMatcher *retMatcher = matcher(status); - if (retMatcher != NULL) { - retMatcher->fDeferredStatus = status; - retMatcher->reset(input); - } - return retMatcher; -} - - -//--------------------------------------------------------------------- -// -// matcher(status) -// -//--------------------------------------------------------------------- -RegexMatcher *RegexPattern::matcher(UErrorCode &status) const { - RegexMatcher *retMatcher = NULL; - - if (U_FAILURE(status)) { - return NULL; - } - if (U_FAILURE(fDeferredStatus)) { - status = fDeferredStatus; - return NULL; - } - - retMatcher = new RegexMatcher(this); - if (retMatcher == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - return retMatcher; -} - - - -//--------------------------------------------------------------------- -// -// matches Convenience function to test for a match, starting -// with a pattern string and a data string. -// -//--------------------------------------------------------------------- -UBool U_EXPORT2 RegexPattern::matches(const UnicodeString ®ex, - const UnicodeString &input, - UParseError &pe, - UErrorCode &status) { - - if (U_FAILURE(status)) {return false;} - - UBool retVal; - RegexPattern *pat = NULL; - RegexMatcher *matcher = NULL; - - pat = RegexPattern::compile(regex, 0, pe, status); - matcher = pat->matcher(input, status); - retVal = matcher->matches(status); - - delete matcher; - delete pat; - return retVal; -} - - -// -// matches, UText mode -// -UBool U_EXPORT2 RegexPattern::matches(UText *regex, - UText *input, - UParseError &pe, - UErrorCode &status) { - - if (U_FAILURE(status)) {return false;} - - UBool retVal = false; - RegexPattern *pat = NULL; - RegexMatcher *matcher = NULL; - - pat = RegexPattern::compile(regex, 0, pe, status); - matcher = pat->matcher(status); - if (U_SUCCESS(status)) { - matcher->reset(input); - retVal = matcher->matches(status); - } - - delete matcher; - delete pat; - return retVal; -} - - - - - -//--------------------------------------------------------------------- -// -// pattern -// -//--------------------------------------------------------------------- -UnicodeString RegexPattern::pattern() const { - if (fPatternString != NULL) { - return *fPatternString; - } else if (fPattern == NULL) { - return UnicodeString(); - } else { - UErrorCode status = U_ZERO_ERROR; - int64_t nativeLen = utext_nativeLength(fPattern); - int32_t len16 = utext_extract(fPattern, 0, nativeLen, NULL, 0, &status); // buffer overflow error - UnicodeString result; - - status = U_ZERO_ERROR; - UChar *resultChars = result.getBuffer(len16); - utext_extract(fPattern, 0, nativeLen, resultChars, len16, &status); // unterminated warning - result.releaseBuffer(len16); - - return result; - } -} - - - - -//--------------------------------------------------------------------- -// -// patternText -// -//--------------------------------------------------------------------- -UText *RegexPattern::patternText(UErrorCode &status) const { - if (U_FAILURE(status)) {return NULL;} - status = U_ZERO_ERROR; - - if (fPattern != NULL) { - return fPattern; - } else { - RegexStaticSets::initGlobals(&status); - return RegexStaticSets::gStaticSets->fEmptyText; - } -} - - -//-------------------------------------------------------------------------------- -// -// groupNumberFromName() -// -//-------------------------------------------------------------------------------- -int32_t RegexPattern::groupNumberFromName(const UnicodeString &groupName, UErrorCode &status) const { - if (U_FAILURE(status)) { - return 0; - } - - // No need to explicitly check for syntactically valid names. - // Invalid ones will never be in the map, and the lookup will fail. - - int32_t number = fNamedCaptureMap ? uhash_geti(fNamedCaptureMap, &groupName) : 0; - if (number == 0) { - status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; - } - return number; -} - -int32_t RegexPattern::groupNumberFromName(const char *groupName, int32_t nameLength, UErrorCode &status) const { - if (U_FAILURE(status)) { - return 0; - } - UnicodeString name(groupName, nameLength, US_INV); - return groupNumberFromName(name, status); -} - - -//--------------------------------------------------------------------- -// -// split -// -//--------------------------------------------------------------------- -int32_t RegexPattern::split(const UnicodeString &input, - UnicodeString dest[], - int32_t destCapacity, - UErrorCode &status) const -{ - if (U_FAILURE(status)) { - return 0; - } - - RegexMatcher m(this); - int32_t r = 0; - // Check m's status to make sure all is ok. - if (U_SUCCESS(m.fDeferredStatus)) { - r = m.split(input, dest, destCapacity, status); - } - return r; -} - -// -// split, UText mode -// -int32_t RegexPattern::split(UText *input, - UText *dest[], - int32_t destCapacity, - UErrorCode &status) const -{ - if (U_FAILURE(status)) { - return 0; - } - - RegexMatcher m(this); - int32_t r = 0; - // Check m's status to make sure all is ok. - if (U_SUCCESS(m.fDeferredStatus)) { - r = m.split(input, dest, destCapacity, status); - } - return r; -} - - -//--------------------------------------------------------------------- -// -// dump Output the compiled form of the pattern. -// Debugging function only. -// -//--------------------------------------------------------------------- -void RegexPattern::dumpOp(int32_t index) const { - (void)index; // Suppress warnings in non-debug build. -#if defined(REGEX_DEBUG) - static const char * const opNames[] = {URX_OPCODE_NAMES}; - int32_t op = fCompiledPat->elementAti(index); - int32_t val = URX_VAL(op); - int32_t type = URX_TYPE(op); - int32_t pinnedType = type; - if ((uint32_t)pinnedType >= UPRV_LENGTHOF(opNames)) { - pinnedType = 0; - } - - printf("%4d %08x %-15s ", index, op, opNames[pinnedType]); - switch (type) { - case URX_NOP: - case URX_DOTANY: - case URX_DOTANY_ALL: - case URX_FAIL: - case URX_CARET: - case URX_DOLLAR: - case URX_BACKSLASH_G: - case URX_BACKSLASH_X: - case URX_END: - case URX_DOLLAR_M: - case URX_CARET_M: - // Types with no operand field of interest. - break; - - case URX_RESERVED_OP: - case URX_START_CAPTURE: - case URX_END_CAPTURE: - case URX_STATE_SAVE: - case URX_JMP: - case URX_JMP_SAV: - case URX_JMP_SAV_X: - case URX_BACKSLASH_B: - case URX_BACKSLASH_BU: - case URX_BACKSLASH_D: - case URX_BACKSLASH_Z: - case URX_STRING_LEN: - case URX_CTR_INIT: - case URX_CTR_INIT_NG: - case URX_CTR_LOOP: - case URX_CTR_LOOP_NG: - case URX_RELOC_OPRND: - case URX_STO_SP: - case URX_LD_SP: - case URX_BACKREF: - case URX_STO_INP_LOC: - case URX_JMPX: - case URX_LA_START: - case URX_LA_END: - case URX_BACKREF_I: - case URX_LB_START: - case URX_LB_CONT: - case URX_LB_END: - case URX_LBN_CONT: - case URX_LBN_END: - case URX_LOOP_C: - case URX_LOOP_DOT_I: - case URX_BACKSLASH_H: - case URX_BACKSLASH_R: - case URX_BACKSLASH_V: - // types with an integer operand field. - printf("%d", val); - break; - - case URX_ONECHAR: - case URX_ONECHAR_I: - if (val < 0x20) { - printf("%#x", val); - } else { - printf("'%s'", CStr(UnicodeString(val))()); - } - break; - - case URX_STRING: - case URX_STRING_I: - { - int32_t lengthOp = fCompiledPat->elementAti(index+1); - U_ASSERT(URX_TYPE(lengthOp) == URX_STRING_LEN); - int32_t length = URX_VAL(lengthOp); - UnicodeString str(fLiteralText, val, length); - printf("%s", CStr(str)()); - } - break; - - case URX_SETREF: - case URX_LOOP_SR_I: - { - UnicodeString s; - UnicodeSet *set = (UnicodeSet *)fSets->elementAt(val); - set->toPattern(s, true); - printf("%s", CStr(s)()); - } - break; - - case URX_STATIC_SETREF: - case URX_STAT_SETREF_N: - { - UnicodeString s; - if (val & URX_NEG_SET) { - printf("NOT "); - val &= ~URX_NEG_SET; - } - UnicodeSet &set = RegexStaticSets::gStaticSets->fPropSets[val]; - set.toPattern(s, true); - printf("%s", CStr(s)()); - } - break; - - - default: - printf("??????"); - break; - } - printf("\n"); -#endif -} - - -void RegexPattern::dumpPattern() const { -#if defined(REGEX_DEBUG) - int index; - - UnicodeString patStr; - for (UChar32 c = utext_next32From(fPattern, 0); c != U_SENTINEL; c = utext_next32(fPattern)) { - patStr.append(c); - } - printf("Original Pattern: \"%s\"\n", CStr(patStr)()); - printf(" Min Match Length: %d\n", fMinMatchLen); - printf(" Match Start Type: %s\n", START_OF_MATCH_STR(fStartType)); - if (fStartType == START_STRING) { - UnicodeString initialString(fLiteralText,fInitialStringIdx, fInitialStringLen); - printf(" Initial match string: \"%s\"\n", CStr(initialString)()); - } else if (fStartType == START_SET) { - UnicodeString s; - fInitialChars->toPattern(s, true); - printf(" Match First Chars: %s\n", CStr(s)()); - - } else if (fStartType == START_CHAR) { - printf(" First char of Match: "); - if (fInitialChar > 0x20) { - printf("'%s'\n", CStr(UnicodeString(fInitialChar))()); - } else { - printf("%#x\n", fInitialChar); - } - } - - printf("Named Capture Groups:\n"); - if (!fNamedCaptureMap || uhash_count(fNamedCaptureMap) == 0) { - printf(" None\n"); - } else { - int32_t pos = UHASH_FIRST; - const UHashElement *el = NULL; - while ((el = uhash_nextElement(fNamedCaptureMap, &pos))) { - const UnicodeString *name = (const UnicodeString *)el->key.pointer; - int32_t number = el->value.integer; - printf(" %d\t%s\n", number, CStr(*name)()); - } - } - - printf("\nIndex Binary Type Operand\n" \ - "-------------------------------------------\n"); - for (index = 0; indexsize(); index++) { - dumpOp(index); - } - printf("\n\n"); -#endif -} - - - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RegexPattern) - -U_NAMESPACE_END -#endif // !UCONFIG_NO_REGULAR_EXPRESSIONS +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// file: repattrn.cpp +// +/* +*************************************************************************** +* Copyright (C) 2002-2016 International Business Machines Corporation +* and others. All rights reserved. +*************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_REGULAR_EXPRESSIONS + +#include "unicode/regex.h" +#include "unicode/uclean.h" +#include "cmemory.h" +#include "cstr.h" +#include "uassert.h" +#include "uhash.h" +#include "uvector.h" +#include "uvectr32.h" +#include "uvectr64.h" +#include "regexcmp.h" +#include "regeximp.h" +#include "regexst.h" + +U_NAMESPACE_BEGIN + +//-------------------------------------------------------------------------- +// +// RegexPattern Default Constructor +// +//-------------------------------------------------------------------------- +RegexPattern::RegexPattern() { + // Init all of this instances data. + init(); +} + + +//-------------------------------------------------------------------------- +// +// Copy Constructor Note: This is a rather inefficient implementation, +// but it probably doesn't matter. +// +//-------------------------------------------------------------------------- +RegexPattern::RegexPattern(const RegexPattern &other) : UObject(other) { + init(); + *this = other; +} + + + +//-------------------------------------------------------------------------- +// +// Assignment Operator +// +//-------------------------------------------------------------------------- +RegexPattern &RegexPattern::operator = (const RegexPattern &other) { + if (this == &other) { + // Source and destination are the same. Don't do anything. + return *this; + } + + // Clean out any previous contents of object being assigned to. + zap(); + + // Give target object a default initialization + init(); + + // Copy simple fields + fDeferredStatus = other.fDeferredStatus; + + if (U_FAILURE(fDeferredStatus)) { + return *this; + } + + if (other.fPatternString == nullptr) { + fPatternString = nullptr; + fPattern = utext_clone(fPattern, other.fPattern, false, true, &fDeferredStatus); + } else { + fPatternString = new UnicodeString(*(other.fPatternString)); + if (fPatternString == nullptr) { + fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; + } else { + fPattern = utext_openConstUnicodeString(nullptr, fPatternString, &fDeferredStatus); + } + } + if (U_FAILURE(fDeferredStatus)) { + return *this; + } + + fFlags = other.fFlags; + fLiteralText = other.fLiteralText; + fMinMatchLen = other.fMinMatchLen; + fFrameSize = other.fFrameSize; + fDataSize = other.fDataSize; + + fStartType = other.fStartType; + fInitialStringIdx = other.fInitialStringIdx; + fInitialStringLen = other.fInitialStringLen; + *fInitialChars = *other.fInitialChars; + fInitialChar = other.fInitialChar; + *fInitialChars8 = *other.fInitialChars8; + fNeedsAltInput = other.fNeedsAltInput; + + // Copy the pattern. It's just values, nothing deep to copy. + fCompiledPat->assign(*other.fCompiledPat, fDeferredStatus); + fGroupMap->assign(*other.fGroupMap, fDeferredStatus); + + // Copy the Unicode Sets. + // Could be made more efficient if the sets were reference counted and shared, + // but I doubt that pattern copying will be particularly common. + // Note: init() already added an empty element zero to fSets + int32_t i; + int32_t numSets = other.fSets->size(); + fSets8 = new Regex8BitSet[numSets]; + if (fSets8 == nullptr) { + fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + for (i=1; ielementAt(i); + UnicodeSet *newSet = new UnicodeSet(*sourceSet); + if (newSet == nullptr) { + fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; + break; + } + fSets->addElement(newSet, fDeferredStatus); + fSets8[i] = other.fSets8[i]; + } + + // Copy the named capture group hash map. + if (other.fNamedCaptureMap != nullptr && initNamedCaptureMap()) { + int32_t hashPos = UHASH_FIRST; + while (const UHashElement *hashEl = uhash_nextElement(other.fNamedCaptureMap, &hashPos)) { + if (U_FAILURE(fDeferredStatus)) { + break; + } + const UnicodeString *name = (const UnicodeString *)hashEl->key.pointer; + UnicodeString *key = new UnicodeString(*name); + int32_t val = hashEl->value.integer; + if (key == nullptr) { + fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; + } else { + uhash_puti(fNamedCaptureMap, key, val, &fDeferredStatus); + } + } + } + return *this; +} + + +//-------------------------------------------------------------------------- +// +// init Shared initialization for use by constructors. +// Bring an uninitialized RegexPattern up to a default state. +// +//-------------------------------------------------------------------------- +void RegexPattern::init() { + fFlags = 0; + fCompiledPat = 0; + fLiteralText.remove(); + fSets = nullptr; + fSets8 = nullptr; + fDeferredStatus = U_ZERO_ERROR; + fMinMatchLen = 0; + fFrameSize = 0; + fDataSize = 0; + fGroupMap = nullptr; + fStartType = START_NO_INFO; + fInitialStringIdx = 0; + fInitialStringLen = 0; + fInitialChars = nullptr; + fInitialChar = 0; + fInitialChars8 = nullptr; + fNeedsAltInput = false; + fNamedCaptureMap = nullptr; + + fPattern = nullptr; // will be set later + fPatternString = nullptr; // may be set later + fCompiledPat = new UVector64(fDeferredStatus); + fGroupMap = new UVector32(fDeferredStatus); + fSets = new UVector(fDeferredStatus); + fInitialChars = new UnicodeSet; + fInitialChars8 = new Regex8BitSet; + if (U_FAILURE(fDeferredStatus)) { + return; + } + if (fCompiledPat == nullptr || fGroupMap == nullptr || fSets == nullptr || + fInitialChars == nullptr || fInitialChars8 == nullptr) { + fDeferredStatus = U_MEMORY_ALLOCATION_ERROR; + return; + } + + // Slot zero of the vector of sets is reserved. Fill it here. + fSets->addElement((int32_t)0, fDeferredStatus); +} + + +bool RegexPattern::initNamedCaptureMap() { + if (fNamedCaptureMap) { + return true; + } + fNamedCaptureMap = uhash_openSize(uhash_hashUnicodeString, // Key hash function + uhash_compareUnicodeString, // Key comparator function + uhash_compareLong, // Value comparator function + 7, // Initial table capacity + &fDeferredStatus); + if (U_FAILURE(fDeferredStatus)) { + return false; + } + + // fNamedCaptureMap owns its key strings, type (UnicodeString *) + uhash_setKeyDeleter(fNamedCaptureMap, uprv_deleteUObject); + return true; +} + +//-------------------------------------------------------------------------- +// +// zap Delete everything owned by this RegexPattern. +// +//-------------------------------------------------------------------------- +void RegexPattern::zap() { + delete fCompiledPat; + fCompiledPat = nullptr; + int i; + for (i=1; isize(); i++) { + UnicodeSet *s; + s = (UnicodeSet *)fSets->elementAt(i); + if (s != nullptr) { + delete s; + } + } + delete fSets; + fSets = nullptr; + delete[] fSets8; + fSets8 = nullptr; + delete fGroupMap; + fGroupMap = nullptr; + delete fInitialChars; + fInitialChars = nullptr; + delete fInitialChars8; + fInitialChars8 = nullptr; + if (fPattern != nullptr) { + utext_close(fPattern); + fPattern = nullptr; + } + if (fPatternString != nullptr) { + delete fPatternString; + fPatternString = nullptr; + } + if (fNamedCaptureMap != nullptr) { + uhash_close(fNamedCaptureMap); + fNamedCaptureMap = nullptr; + } +} + + +//-------------------------------------------------------------------------- +// +// Destructor +// +//-------------------------------------------------------------------------- +RegexPattern::~RegexPattern() { + zap(); +} + + +//-------------------------------------------------------------------------- +// +// Clone +// +//-------------------------------------------------------------------------- +RegexPattern *RegexPattern::clone() const { + RegexPattern *copy = new RegexPattern(*this); + return copy; +} + + +//-------------------------------------------------------------------------- +// +// operator == (comparison) Consider to patterns to be == if the +// pattern strings and the flags are the same. +// Note that pattern strings with the same +// characters can still be considered different. +// +//-------------------------------------------------------------------------- +bool RegexPattern::operator ==(const RegexPattern &other) const { + if (this->fFlags == other.fFlags && this->fDeferredStatus == other.fDeferredStatus) { + if (this->fPatternString != nullptr && other.fPatternString != nullptr) { + return *(this->fPatternString) == *(other.fPatternString); + } else if (this->fPattern == nullptr) { + if (other.fPattern == nullptr) { + return true; + } + } else if (other.fPattern != nullptr) { + UTEXT_SETNATIVEINDEX(this->fPattern, 0); + UTEXT_SETNATIVEINDEX(other.fPattern, 0); + return utext_equals(this->fPattern, other.fPattern); + } + } + return false; +} + +//--------------------------------------------------------------------- +// +// compile +// +//--------------------------------------------------------------------- +RegexPattern * U_EXPORT2 +RegexPattern::compile(const UnicodeString ®ex, + uint32_t flags, + UParseError &pe, + UErrorCode &status) +{ + if (U_FAILURE(status)) { + return nullptr; + } + + const uint32_t allFlags = UREGEX_CANON_EQ | UREGEX_CASE_INSENSITIVE | UREGEX_COMMENTS | + UREGEX_DOTALL | UREGEX_MULTILINE | UREGEX_UWORD | + UREGEX_ERROR_ON_UNKNOWN_ESCAPES | UREGEX_UNIX_LINES | UREGEX_LITERAL; + + if ((flags & ~allFlags) != 0) { + status = U_REGEX_INVALID_FLAG; + return nullptr; + } + + if ((flags & UREGEX_CANON_EQ) != 0) { + status = U_REGEX_UNIMPLEMENTED; + return nullptr; + } + + RegexPattern *This = new RegexPattern; + if (This == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + if (U_FAILURE(This->fDeferredStatus)) { + status = This->fDeferredStatus; + delete This; + return nullptr; + } + This->fFlags = flags; + + RegexCompile compiler(This, status); + compiler.compile(regex, pe, status); + + if (U_FAILURE(status)) { + delete This; + This = nullptr; + } + + return This; +} + + +// +// compile, UText mode +// +RegexPattern * U_EXPORT2 +RegexPattern::compile(UText *regex, + uint32_t flags, + UParseError &pe, + UErrorCode &status) +{ + if (U_FAILURE(status)) { + return nullptr; + } + + const uint32_t allFlags = UREGEX_CANON_EQ | UREGEX_CASE_INSENSITIVE | UREGEX_COMMENTS | + UREGEX_DOTALL | UREGEX_MULTILINE | UREGEX_UWORD | + UREGEX_ERROR_ON_UNKNOWN_ESCAPES | UREGEX_UNIX_LINES | UREGEX_LITERAL; + + if ((flags & ~allFlags) != 0) { + status = U_REGEX_INVALID_FLAG; + return nullptr; + } + + if ((flags & UREGEX_CANON_EQ) != 0) { + status = U_REGEX_UNIMPLEMENTED; + return nullptr; + } + + RegexPattern *This = new RegexPattern; + if (This == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + if (U_FAILURE(This->fDeferredStatus)) { + status = This->fDeferredStatus; + delete This; + return nullptr; + } + This->fFlags = flags; + + RegexCompile compiler(This, status); + compiler.compile(regex, pe, status); + + if (U_FAILURE(status)) { + delete This; + This = nullptr; + } + + return This; +} + +// +// compile with default flags. +// +RegexPattern * U_EXPORT2 +RegexPattern::compile(const UnicodeString ®ex, + UParseError &pe, + UErrorCode &err) +{ + return compile(regex, 0, pe, err); +} + + +// +// compile with default flags, UText mode +// +RegexPattern * U_EXPORT2 +RegexPattern::compile(UText *regex, + UParseError &pe, + UErrorCode &err) +{ + return compile(regex, 0, pe, err); +} + + +// +// compile with no UParseErr parameter. +// +RegexPattern * U_EXPORT2 +RegexPattern::compile(const UnicodeString ®ex, + uint32_t flags, + UErrorCode &err) +{ + UParseError pe; + return compile(regex, flags, pe, err); +} + + +// +// compile with no UParseErr parameter, UText mode +// +RegexPattern * U_EXPORT2 +RegexPattern::compile(UText *regex, + uint32_t flags, + UErrorCode &err) +{ + UParseError pe; + return compile(regex, flags, pe, err); +} + + +//--------------------------------------------------------------------- +// +// flags +// +//--------------------------------------------------------------------- +uint32_t RegexPattern::flags() const { + return fFlags; +} + + +//--------------------------------------------------------------------- +// +// matcher(UnicodeString, err) +// +//--------------------------------------------------------------------- +RegexMatcher *RegexPattern::matcher(const UnicodeString &input, + UErrorCode &status) const { + RegexMatcher *retMatcher = matcher(status); + if (retMatcher != nullptr) { + retMatcher->fDeferredStatus = status; + retMatcher->reset(input); + } + return retMatcher; +} + + +//--------------------------------------------------------------------- +// +// matcher(status) +// +//--------------------------------------------------------------------- +RegexMatcher *RegexPattern::matcher(UErrorCode &status) const { + RegexMatcher *retMatcher = nullptr; + + if (U_FAILURE(status)) { + return nullptr; + } + if (U_FAILURE(fDeferredStatus)) { + status = fDeferredStatus; + return nullptr; + } + + retMatcher = new RegexMatcher(this); + if (retMatcher == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + return retMatcher; +} + + + +//--------------------------------------------------------------------- +// +// matches Convenience function to test for a match, starting +// with a pattern string and a data string. +// +//--------------------------------------------------------------------- +UBool U_EXPORT2 RegexPattern::matches(const UnicodeString ®ex, + const UnicodeString &input, + UParseError &pe, + UErrorCode &status) { + + if (U_FAILURE(status)) {return false;} + + UBool retVal; + RegexPattern *pat = nullptr; + RegexMatcher *matcher = nullptr; + + pat = RegexPattern::compile(regex, 0, pe, status); + matcher = pat->matcher(input, status); + retVal = matcher->matches(status); + + delete matcher; + delete pat; + return retVal; +} + + +// +// matches, UText mode +// +UBool U_EXPORT2 RegexPattern::matches(UText *regex, + UText *input, + UParseError &pe, + UErrorCode &status) { + + if (U_FAILURE(status)) {return false;} + + UBool retVal = false; + RegexPattern *pat = nullptr; + RegexMatcher *matcher = nullptr; + + pat = RegexPattern::compile(regex, 0, pe, status); + matcher = pat->matcher(status); + if (U_SUCCESS(status)) { + matcher->reset(input); + retVal = matcher->matches(status); + } + + delete matcher; + delete pat; + return retVal; +} + + + + + +//--------------------------------------------------------------------- +// +// pattern +// +//--------------------------------------------------------------------- +UnicodeString RegexPattern::pattern() const { + if (fPatternString != nullptr) { + return *fPatternString; + } else if (fPattern == nullptr) { + return UnicodeString(); + } else { + UErrorCode status = U_ZERO_ERROR; + int64_t nativeLen = utext_nativeLength(fPattern); + int32_t len16 = utext_extract(fPattern, 0, nativeLen, nullptr, 0, &status); // buffer overflow error + UnicodeString result; + + status = U_ZERO_ERROR; + char16_t *resultChars = result.getBuffer(len16); + utext_extract(fPattern, 0, nativeLen, resultChars, len16, &status); // unterminated warning + result.releaseBuffer(len16); + + return result; + } +} + + + + +//--------------------------------------------------------------------- +// +// patternText +// +//--------------------------------------------------------------------- +UText *RegexPattern::patternText(UErrorCode &status) const { + if (U_FAILURE(status)) {return nullptr;} + status = U_ZERO_ERROR; + + if (fPattern != nullptr) { + return fPattern; + } else { + RegexStaticSets::initGlobals(&status); + return RegexStaticSets::gStaticSets->fEmptyText; + } +} + + +//-------------------------------------------------------------------------------- +// +// groupNumberFromName() +// +//-------------------------------------------------------------------------------- +int32_t RegexPattern::groupNumberFromName(const UnicodeString &groupName, UErrorCode &status) const { + if (U_FAILURE(status)) { + return 0; + } + + // No need to explicitly check for syntactically valid names. + // Invalid ones will never be in the map, and the lookup will fail. + + int32_t number = fNamedCaptureMap ? uhash_geti(fNamedCaptureMap, &groupName) : 0; + if (number == 0) { + status = U_REGEX_INVALID_CAPTURE_GROUP_NAME; + } + return number; +} + +int32_t RegexPattern::groupNumberFromName(const char *groupName, int32_t nameLength, UErrorCode &status) const { + if (U_FAILURE(status)) { + return 0; + } + UnicodeString name(groupName, nameLength, US_INV); + return groupNumberFromName(name, status); +} + + +//--------------------------------------------------------------------- +// +// split +// +//--------------------------------------------------------------------- +int32_t RegexPattern::split(const UnicodeString &input, + UnicodeString dest[], + int32_t destCapacity, + UErrorCode &status) const +{ + if (U_FAILURE(status)) { + return 0; + } + + RegexMatcher m(this); + int32_t r = 0; + // Check m's status to make sure all is ok. + if (U_SUCCESS(m.fDeferredStatus)) { + r = m.split(input, dest, destCapacity, status); + } + return r; +} + +// +// split, UText mode +// +int32_t RegexPattern::split(UText *input, + UText *dest[], + int32_t destCapacity, + UErrorCode &status) const +{ + if (U_FAILURE(status)) { + return 0; + } + + RegexMatcher m(this); + int32_t r = 0; + // Check m's status to make sure all is ok. + if (U_SUCCESS(m.fDeferredStatus)) { + r = m.split(input, dest, destCapacity, status); + } + return r; +} + + +//--------------------------------------------------------------------- +// +// dump Output the compiled form of the pattern. +// Debugging function only. +// +//--------------------------------------------------------------------- +void RegexPattern::dumpOp(int32_t index) const { + (void)index; // Suppress warnings in non-debug build. +#if defined(REGEX_DEBUG) + static const char * const opNames[] = {URX_OPCODE_NAMES}; + int32_t op = fCompiledPat->elementAti(index); + int32_t val = URX_VAL(op); + int32_t type = URX_TYPE(op); + int32_t pinnedType = type; + if ((uint32_t)pinnedType >= UPRV_LENGTHOF(opNames)) { + pinnedType = 0; + } + + printf("%4d %08x %-15s ", index, op, opNames[pinnedType]); + switch (type) { + case URX_NOP: + case URX_DOTANY: + case URX_DOTANY_ALL: + case URX_FAIL: + case URX_CARET: + case URX_DOLLAR: + case URX_BACKSLASH_G: + case URX_BACKSLASH_X: + case URX_END: + case URX_DOLLAR_M: + case URX_CARET_M: + // Types with no operand field of interest. + break; + + case URX_RESERVED_OP: + case URX_START_CAPTURE: + case URX_END_CAPTURE: + case URX_STATE_SAVE: + case URX_JMP: + case URX_JMP_SAV: + case URX_JMP_SAV_X: + case URX_BACKSLASH_B: + case URX_BACKSLASH_BU: + case URX_BACKSLASH_D: + case URX_BACKSLASH_Z: + case URX_STRING_LEN: + case URX_CTR_INIT: + case URX_CTR_INIT_NG: + case URX_CTR_LOOP: + case URX_CTR_LOOP_NG: + case URX_RELOC_OPRND: + case URX_STO_SP: + case URX_LD_SP: + case URX_BACKREF: + case URX_STO_INP_LOC: + case URX_JMPX: + case URX_LA_START: + case URX_LA_END: + case URX_BACKREF_I: + case URX_LB_START: + case URX_LB_CONT: + case URX_LB_END: + case URX_LBN_CONT: + case URX_LBN_END: + case URX_LOOP_C: + case URX_LOOP_DOT_I: + case URX_BACKSLASH_H: + case URX_BACKSLASH_R: + case URX_BACKSLASH_V: + // types with an integer operand field. + printf("%d", val); + break; + + case URX_ONECHAR: + case URX_ONECHAR_I: + if (val < 0x20) { + printf("%#x", val); + } else { + printf("'%s'", CStr(UnicodeString(val))()); + } + break; + + case URX_STRING: + case URX_STRING_I: + { + int32_t lengthOp = fCompiledPat->elementAti(index+1); + U_ASSERT(URX_TYPE(lengthOp) == URX_STRING_LEN); + int32_t length = URX_VAL(lengthOp); + UnicodeString str(fLiteralText, val, length); + printf("%s", CStr(str)()); + } + break; + + case URX_SETREF: + case URX_LOOP_SR_I: + { + UnicodeString s; + UnicodeSet *set = (UnicodeSet *)fSets->elementAt(val); + set->toPattern(s, true); + printf("%s", CStr(s)()); + } + break; + + case URX_STATIC_SETREF: + case URX_STAT_SETREF_N: + { + UnicodeString s; + if (val & URX_NEG_SET) { + printf("NOT "); + val &= ~URX_NEG_SET; + } + UnicodeSet &set = RegexStaticSets::gStaticSets->fPropSets[val]; + set.toPattern(s, true); + printf("%s", CStr(s)()); + } + break; + + + default: + printf("??????"); + break; + } + printf("\n"); +#endif +} + + +void RegexPattern::dumpPattern() const { +#if defined(REGEX_DEBUG) + int index; + + UnicodeString patStr; + for (UChar32 c = utext_next32From(fPattern, 0); c != U_SENTINEL; c = utext_next32(fPattern)) { + patStr.append(c); + } + printf("Original Pattern: \"%s\"\n", CStr(patStr)()); + printf(" Min Match Length: %d\n", fMinMatchLen); + printf(" Match Start Type: %s\n", START_OF_MATCH_STR(fStartType)); + if (fStartType == START_STRING) { + UnicodeString initialString(fLiteralText,fInitialStringIdx, fInitialStringLen); + printf(" Initial match string: \"%s\"\n", CStr(initialString)()); + } else if (fStartType == START_SET) { + UnicodeString s; + fInitialChars->toPattern(s, true); + printf(" Match First Chars: %s\n", CStr(s)()); + + } else if (fStartType == START_CHAR) { + printf(" First char of Match: "); + if (fInitialChar > 0x20) { + printf("'%s'\n", CStr(UnicodeString(fInitialChar))()); + } else { + printf("%#x\n", fInitialChar); + } + } + + printf("Named Capture Groups:\n"); + if (!fNamedCaptureMap || uhash_count(fNamedCaptureMap) == 0) { + printf(" None\n"); + } else { + int32_t pos = UHASH_FIRST; + const UHashElement *el = nullptr; + while ((el = uhash_nextElement(fNamedCaptureMap, &pos))) { + const UnicodeString *name = (const UnicodeString *)el->key.pointer; + int32_t number = el->value.integer; + printf(" %d\t%s\n", number, CStr(*name)()); + } + } + + printf("\nIndex Binary Type Operand\n" \ + "-------------------------------------------\n"); + for (index = 0; indexsize(); index++) { + dumpOp(index); + } + printf("\n\n"); +#endif +} + + + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RegexPattern) + +U_NAMESPACE_END +#endif // !UCONFIG_NO_REGULAR_EXPRESSIONS diff --git a/deps/icu-small/source/i18n/rulebasedcollator.cpp b/deps/icu-small/source/i18n/rulebasedcollator.cpp index a240295b679eaa..68faaf574de2e6 100644 --- a/deps/icu-small/source/i18n/rulebasedcollator.cpp +++ b/deps/icu-small/source/i18n/rulebasedcollator.cpp @@ -1,1656 +1,1656 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1996-2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* rulebasedcollator.cpp -* -* (replaced the former tblcoll.cpp) -* -* created on: 2012feb14 with new and old collation code -* created by: Markus W. Scherer -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_COLLATION - -#include "unicode/coll.h" -#include "unicode/coleitr.h" -#include "unicode/localpointer.h" -#include "unicode/locid.h" -#include "unicode/sortkey.h" -#include "unicode/tblcoll.h" -#include "unicode/ucol.h" -#include "unicode/uiter.h" -#include "unicode/uloc.h" -#include "unicode/uniset.h" -#include "unicode/unistr.h" -#include "unicode/usetiter.h" -#include "unicode/utf8.h" -#include "unicode/uversion.h" -#include "bocsu.h" -#include "charstr.h" -#include "cmemory.h" -#include "collation.h" -#include "collationcompare.h" -#include "collationdata.h" -#include "collationdatareader.h" -#include "collationfastlatin.h" -#include "collationiterator.h" -#include "collationkeys.h" -#include "collationroot.h" -#include "collationsets.h" -#include "collationsettings.h" -#include "collationtailoring.h" -#include "cstring.h" -#include "uassert.h" -#include "ucol_imp.h" -#include "uhash.h" -#include "uitercollationiterator.h" -#include "ustr_imp.h" -#include "utf16collationiterator.h" -#include "utf8collationiterator.h" -#include "uvectr64.h" - -U_NAMESPACE_BEGIN - -namespace { - -class FixedSortKeyByteSink : public SortKeyByteSink { -public: - FixedSortKeyByteSink(char *dest, int32_t destCapacity) - : SortKeyByteSink(dest, destCapacity) {} - virtual ~FixedSortKeyByteSink(); - -private: - virtual void AppendBeyondCapacity(const char *bytes, int32_t n, int32_t length) override; - virtual UBool Resize(int32_t appendCapacity, int32_t length) override; -}; - -FixedSortKeyByteSink::~FixedSortKeyByteSink() {} - -void -FixedSortKeyByteSink::AppendBeyondCapacity(const char *bytes, int32_t /*n*/, int32_t length) { - // buffer_ != NULL && bytes != NULL && n > 0 && appended_ > capacity_ - // Fill the buffer completely. - int32_t available = capacity_ - length; - if (available > 0) { - uprv_memcpy(buffer_ + length, bytes, available); - } -} - -UBool -FixedSortKeyByteSink::Resize(int32_t /*appendCapacity*/, int32_t /*length*/) { - return false; -} - -} // namespace - -// Not in an anonymous namespace, so that it can be a friend of CollationKey. -class CollationKeyByteSink : public SortKeyByteSink { -public: - CollationKeyByteSink(CollationKey &key) - : SortKeyByteSink(reinterpret_cast(key.getBytes()), key.getCapacity()), - key_(key) {} - virtual ~CollationKeyByteSink(); - -private: - virtual void AppendBeyondCapacity(const char *bytes, int32_t n, int32_t length) override; - virtual UBool Resize(int32_t appendCapacity, int32_t length) override; - - CollationKey &key_; -}; - -CollationKeyByteSink::~CollationKeyByteSink() {} - -void -CollationKeyByteSink::AppendBeyondCapacity(const char *bytes, int32_t n, int32_t length) { - // buffer_ != NULL && bytes != NULL && n > 0 && appended_ > capacity_ - if (Resize(n, length)) { - uprv_memcpy(buffer_ + length, bytes, n); - } -} - -UBool -CollationKeyByteSink::Resize(int32_t appendCapacity, int32_t length) { - if (buffer_ == NULL) { - return false; // allocation failed before already - } - int32_t newCapacity = 2 * capacity_; - int32_t altCapacity = length + 2 * appendCapacity; - if (newCapacity < altCapacity) { - newCapacity = altCapacity; - } - if (newCapacity < 200) { - newCapacity = 200; - } - uint8_t *newBuffer = key_.reallocate(newCapacity, length); - if (newBuffer == NULL) { - SetNotOk(); - return false; - } - buffer_ = reinterpret_cast(newBuffer); - capacity_ = newCapacity; - return true; -} - -RuleBasedCollator::RuleBasedCollator(const RuleBasedCollator &other) - : Collator(other), - data(other.data), - settings(other.settings), - tailoring(other.tailoring), - cacheEntry(other.cacheEntry), - validLocale(other.validLocale), - explicitlySetAttributes(other.explicitlySetAttributes), - actualLocaleIsSameAsValid(other.actualLocaleIsSameAsValid) { - settings->addRef(); - cacheEntry->addRef(); -} - -RuleBasedCollator::RuleBasedCollator(const uint8_t *bin, int32_t length, - const RuleBasedCollator *base, UErrorCode &errorCode) - : data(NULL), - settings(NULL), - tailoring(NULL), - cacheEntry(NULL), - validLocale(""), - explicitlySetAttributes(0), - actualLocaleIsSameAsValid(false) { - if(U_FAILURE(errorCode)) { return; } - if(bin == NULL || length == 0 || base == NULL) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - const CollationTailoring *root = CollationRoot::getRoot(errorCode); - if(U_FAILURE(errorCode)) { return; } - if(base->tailoring != root) { - errorCode = U_UNSUPPORTED_ERROR; - return; - } - LocalPointer t(new CollationTailoring(base->tailoring->settings)); - if(t.isNull() || t->isBogus()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - CollationDataReader::read(base->tailoring, bin, length, *t, errorCode); - if(U_FAILURE(errorCode)) { return; } - t->actualLocale.setToBogus(); - adoptTailoring(t.orphan(), errorCode); -} - -RuleBasedCollator::RuleBasedCollator(const CollationCacheEntry *entry) - : data(entry->tailoring->data), - settings(entry->tailoring->settings), - tailoring(entry->tailoring), - cacheEntry(entry), - validLocale(entry->validLocale), - explicitlySetAttributes(0), - actualLocaleIsSameAsValid(false) { - settings->addRef(); - cacheEntry->addRef(); -} - -RuleBasedCollator::~RuleBasedCollator() { - SharedObject::clearPtr(settings); - SharedObject::clearPtr(cacheEntry); -} - -void -RuleBasedCollator::adoptTailoring(CollationTailoring *t, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { - t->deleteIfZeroRefCount(); - return; - } - U_ASSERT(settings == NULL && data == NULL && tailoring == NULL && cacheEntry == NULL); - cacheEntry = new CollationCacheEntry(t->actualLocale, t); - if(cacheEntry == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - t->deleteIfZeroRefCount(); - return; - } - data = t->data; - settings = t->settings; - settings->addRef(); - tailoring = t; - cacheEntry->addRef(); - validLocale = t->actualLocale; - actualLocaleIsSameAsValid = false; -} - -RuleBasedCollator * -RuleBasedCollator::clone() const { - return new RuleBasedCollator(*this); -} - -RuleBasedCollator &RuleBasedCollator::operator=(const RuleBasedCollator &other) { - if(this == &other) { return *this; } - SharedObject::copyPtr(other.settings, settings); - tailoring = other.tailoring; - SharedObject::copyPtr(other.cacheEntry, cacheEntry); - data = tailoring->data; - validLocale = other.validLocale; - explicitlySetAttributes = other.explicitlySetAttributes; - actualLocaleIsSameAsValid = other.actualLocaleIsSameAsValid; - return *this; -} - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedCollator) - -bool -RuleBasedCollator::operator==(const Collator& other) const { - if(this == &other) { return true; } - if(!Collator::operator==(other)) { return false; } - const RuleBasedCollator &o = static_cast(other); - if(*settings != *o.settings) { return false; } - if(data == o.data) { return true; } - UBool thisIsRoot = data->base == NULL; - UBool otherIsRoot = o.data->base == NULL; - U_ASSERT(!thisIsRoot || !otherIsRoot); // otherwise their data pointers should be == - if(thisIsRoot != otherIsRoot) { return false; } - if((thisIsRoot || !tailoring->rules.isEmpty()) && - (otherIsRoot || !o.tailoring->rules.isEmpty())) { - // Shortcut: If both collators have valid rule strings, then compare those. - if(tailoring->rules == o.tailoring->rules) { return true; } - } - // Different rule strings can result in the same or equivalent tailoring. - // The rule strings are optional in ICU resource bundles, although included by default. - // cloneBinary() drops the rule string. - UErrorCode errorCode = U_ZERO_ERROR; - LocalPointer thisTailored(getTailoredSet(errorCode)); - LocalPointer otherTailored(o.getTailoredSet(errorCode)); - if(U_FAILURE(errorCode)) { return false; } - if(*thisTailored != *otherTailored) { return false; } - // For completeness, we should compare all of the mappings; - // or we should create a list of strings, sort it with one collator, - // and check if both collators compare adjacent strings the same - // (order & strength, down to quaternary); or similar. - // Testing equality of collators seems unusual. - return true; -} - -int32_t -RuleBasedCollator::hashCode() const { - int32_t h = settings->hashCode(); - if(data->base == NULL) { return h; } // root collator - // Do not rely on the rule string, see comments in operator==(). - UErrorCode errorCode = U_ZERO_ERROR; - LocalPointer set(getTailoredSet(errorCode)); - if(U_FAILURE(errorCode)) { return 0; } - UnicodeSetIterator iter(*set); - while(iter.next() && !iter.isString()) { - h ^= data->getCE32(iter.getCodepoint()); - } - return h; -} - -void -RuleBasedCollator::setLocales(const Locale &requested, const Locale &valid, - const Locale &actual) { - if(actual == tailoring->actualLocale) { - actualLocaleIsSameAsValid = false; - } else { - U_ASSERT(actual == valid); - actualLocaleIsSameAsValid = true; - } - // Do not modify tailoring.actualLocale: - // We cannot be sure that that would be thread-safe. - validLocale = valid; - (void)requested; // Ignore, see also ticket #10477. -} - -Locale -RuleBasedCollator::getLocale(ULocDataLocaleType type, UErrorCode& errorCode) const { - if(U_FAILURE(errorCode)) { - return Locale::getRoot(); - } - switch(type) { - case ULOC_ACTUAL_LOCALE: - return actualLocaleIsSameAsValid ? validLocale : tailoring->actualLocale; - case ULOC_VALID_LOCALE: - return validLocale; - case ULOC_REQUESTED_LOCALE: - default: - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return Locale::getRoot(); - } -} - -const char * -RuleBasedCollator::internalGetLocaleID(ULocDataLocaleType type, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { - return NULL; - } - const Locale *result; - switch(type) { - case ULOC_ACTUAL_LOCALE: - result = actualLocaleIsSameAsValid ? &validLocale : &tailoring->actualLocale; - break; - case ULOC_VALID_LOCALE: - result = &validLocale; - break; - case ULOC_REQUESTED_LOCALE: - default: - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - if(result->isBogus()) { return NULL; } - const char *id = result->getName(); - return id[0] == 0 ? "root" : id; -} - -const UnicodeString& -RuleBasedCollator::getRules() const { - return tailoring->rules; -} - -void -RuleBasedCollator::getRules(UColRuleOption delta, UnicodeString &buffer) const { - if(delta == UCOL_TAILORING_ONLY) { - buffer = tailoring->rules; - return; - } - // UCOL_FULL_RULES - buffer.remove(); - CollationLoader::appendRootRules(buffer); - buffer.append(tailoring->rules).getTerminatedBuffer(); -} - -void -RuleBasedCollator::getVersion(UVersionInfo version) const { - uprv_memcpy(version, tailoring->version, U_MAX_VERSION_LENGTH); - version[0] += (UCOL_RUNTIME_VERSION << 4) + (UCOL_RUNTIME_VERSION >> 4); -} - -UnicodeSet * -RuleBasedCollator::getTailoredSet(UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return NULL; } - UnicodeSet *tailored = new UnicodeSet(); - if(tailored == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - if(data->base != NULL) { - TailoredSet(tailored).forData(data, errorCode); - if(U_FAILURE(errorCode)) { - delete tailored; - return NULL; - } - } - return tailored; -} - -void -RuleBasedCollator::internalGetContractionsAndExpansions( - UnicodeSet *contractions, UnicodeSet *expansions, - UBool addPrefixes, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return; } - if(contractions != NULL) { - contractions->clear(); - } - if(expansions != NULL) { - expansions->clear(); - } - ContractionsAndExpansions(contractions, expansions, NULL, addPrefixes).forData(data, errorCode); -} - -void -RuleBasedCollator::internalAddContractions(UChar32 c, UnicodeSet &set, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return; } - ContractionsAndExpansions(&set, NULL, NULL, false).forCodePoint(data, c, errorCode); -} - -const CollationSettings & -RuleBasedCollator::getDefaultSettings() const { - return *tailoring->settings; -} - -UColAttributeValue -RuleBasedCollator::getAttribute(UColAttribute attr, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return UCOL_DEFAULT; } - int32_t option; - switch(attr) { - case UCOL_FRENCH_COLLATION: - option = CollationSettings::BACKWARD_SECONDARY; - break; - case UCOL_ALTERNATE_HANDLING: - return settings->getAlternateHandling(); - case UCOL_CASE_FIRST: - return settings->getCaseFirst(); - case UCOL_CASE_LEVEL: - option = CollationSettings::CASE_LEVEL; - break; - case UCOL_NORMALIZATION_MODE: - option = CollationSettings::CHECK_FCD; - break; - case UCOL_STRENGTH: - return (UColAttributeValue)settings->getStrength(); - case UCOL_HIRAGANA_QUATERNARY_MODE: - // Deprecated attribute, unsettable. - return UCOL_OFF; - case UCOL_NUMERIC_COLLATION: - option = CollationSettings::NUMERIC; - break; - default: - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return UCOL_DEFAULT; - } - return ((settings->options & option) == 0) ? UCOL_OFF : UCOL_ON; -} - -void -RuleBasedCollator::setAttribute(UColAttribute attr, UColAttributeValue value, - UErrorCode &errorCode) { - UColAttributeValue oldValue = getAttribute(attr, errorCode); - if(U_FAILURE(errorCode)) { return; } - if(value == oldValue) { - setAttributeExplicitly(attr); - return; - } - const CollationSettings &defaultSettings = getDefaultSettings(); - if(settings == &defaultSettings) { - if(value == UCOL_DEFAULT) { - setAttributeDefault(attr); - return; - } - } - CollationSettings *ownedSettings = SharedObject::copyOnWrite(settings); - if(ownedSettings == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - - switch(attr) { - case UCOL_FRENCH_COLLATION: - ownedSettings->setFlag(CollationSettings::BACKWARD_SECONDARY, value, - defaultSettings.options, errorCode); - break; - case UCOL_ALTERNATE_HANDLING: - ownedSettings->setAlternateHandling(value, defaultSettings.options, errorCode); - break; - case UCOL_CASE_FIRST: - ownedSettings->setCaseFirst(value, defaultSettings.options, errorCode); - break; - case UCOL_CASE_LEVEL: - ownedSettings->setFlag(CollationSettings::CASE_LEVEL, value, - defaultSettings.options, errorCode); - break; - case UCOL_NORMALIZATION_MODE: - ownedSettings->setFlag(CollationSettings::CHECK_FCD, value, - defaultSettings.options, errorCode); - break; - case UCOL_STRENGTH: - ownedSettings->setStrength(value, defaultSettings.options, errorCode); - break; - case UCOL_HIRAGANA_QUATERNARY_MODE: - // Deprecated attribute. Check for valid values but do not change anything. - if(value != UCOL_OFF && value != UCOL_ON && value != UCOL_DEFAULT) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - } - break; - case UCOL_NUMERIC_COLLATION: - ownedSettings->setFlag(CollationSettings::NUMERIC, value, defaultSettings.options, errorCode); - break; - default: - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - break; - } - if(U_FAILURE(errorCode)) { return; } - setFastLatinOptions(*ownedSettings); - if(value == UCOL_DEFAULT) { - setAttributeDefault(attr); - } else { - setAttributeExplicitly(attr); - } -} - -Collator & -RuleBasedCollator::setMaxVariable(UColReorderCode group, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return *this; } - // Convert the reorder code into a MaxVariable number, or UCOL_DEFAULT=-1. - int32_t value; - if(group == UCOL_REORDER_CODE_DEFAULT) { - value = UCOL_DEFAULT; - } else if(UCOL_REORDER_CODE_FIRST <= group && group <= UCOL_REORDER_CODE_CURRENCY) { - value = group - UCOL_REORDER_CODE_FIRST; - } else { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return *this; - } - CollationSettings::MaxVariable oldValue = settings->getMaxVariable(); - if(value == oldValue) { - setAttributeExplicitly(ATTR_VARIABLE_TOP); - return *this; - } - const CollationSettings &defaultSettings = getDefaultSettings(); - if(settings == &defaultSettings) { - if(value == UCOL_DEFAULT) { - setAttributeDefault(ATTR_VARIABLE_TOP); - return *this; - } - } - CollationSettings *ownedSettings = SharedObject::copyOnWrite(settings); - if(ownedSettings == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - - if(group == UCOL_REORDER_CODE_DEFAULT) { - group = (UColReorderCode)( - UCOL_REORDER_CODE_FIRST + int32_t{defaultSettings.getMaxVariable()}); - } - uint32_t varTop = data->getLastPrimaryForGroup(group); - U_ASSERT(varTop != 0); - ownedSettings->setMaxVariable(value, defaultSettings.options, errorCode); - if(U_FAILURE(errorCode)) { return *this; } - ownedSettings->variableTop = varTop; - setFastLatinOptions(*ownedSettings); - if(value == UCOL_DEFAULT) { - setAttributeDefault(ATTR_VARIABLE_TOP); - } else { - setAttributeExplicitly(ATTR_VARIABLE_TOP); - } - return *this; -} - -UColReorderCode -RuleBasedCollator::getMaxVariable() const { - return (UColReorderCode)(UCOL_REORDER_CODE_FIRST + int32_t{settings->getMaxVariable()}); -} - -uint32_t -RuleBasedCollator::getVariableTop(UErrorCode & /*errorCode*/) const { - return settings->variableTop; -} - -uint32_t -RuleBasedCollator::setVariableTop(const UChar *varTop, int32_t len, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return 0; } - if(varTop == NULL && len !=0) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - if(len < 0) { len = u_strlen(varTop); } - if(len == 0) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UBool numeric = settings->isNumeric(); - int64_t ce1, ce2; - if(settings->dontCheckFCD()) { - UTF16CollationIterator ci(data, numeric, varTop, varTop, varTop + len); - ce1 = ci.nextCE(errorCode); - ce2 = ci.nextCE(errorCode); - } else { - FCDUTF16CollationIterator ci(data, numeric, varTop, varTop, varTop + len); - ce1 = ci.nextCE(errorCode); - ce2 = ci.nextCE(errorCode); - } - if(ce1 == Collation::NO_CE || ce2 != Collation::NO_CE) { - errorCode = U_CE_NOT_FOUND_ERROR; - return 0; - } - setVariableTop((uint32_t)(ce1 >> 32), errorCode); - return settings->variableTop; -} - -uint32_t -RuleBasedCollator::setVariableTop(const UnicodeString &varTop, UErrorCode &errorCode) { - return setVariableTop(varTop.getBuffer(), varTop.length(), errorCode); -} - -void -RuleBasedCollator::setVariableTop(uint32_t varTop, UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - if(varTop != settings->variableTop) { - // Pin the variable top to the end of the reordering group which contains it. - // Only a few special groups are supported. - int32_t group = data->getGroupForPrimary(varTop); - if(group < UCOL_REORDER_CODE_FIRST || UCOL_REORDER_CODE_CURRENCY < group) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - uint32_t v = data->getLastPrimaryForGroup(group); - U_ASSERT(v != 0 && v >= varTop); - varTop = v; - if(varTop != settings->variableTop) { - CollationSettings *ownedSettings = SharedObject::copyOnWrite(settings); - if(ownedSettings == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - ownedSettings->setMaxVariable(group - UCOL_REORDER_CODE_FIRST, - getDefaultSettings().options, errorCode); - if(U_FAILURE(errorCode)) { return; } - ownedSettings->variableTop = varTop; - setFastLatinOptions(*ownedSettings); - } - } - if(varTop == getDefaultSettings().variableTop) { - setAttributeDefault(ATTR_VARIABLE_TOP); - } else { - setAttributeExplicitly(ATTR_VARIABLE_TOP); - } -} - -int32_t -RuleBasedCollator::getReorderCodes(int32_t *dest, int32_t capacity, - UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return 0; } - if(capacity < 0 || (dest == NULL && capacity > 0)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - int32_t length = settings->reorderCodesLength; - if(length == 0) { return 0; } - if(length > capacity) { - errorCode = U_BUFFER_OVERFLOW_ERROR; - return length; - } - uprv_memcpy(dest, settings->reorderCodes, length * 4); - return length; -} - -void -RuleBasedCollator::setReorderCodes(const int32_t *reorderCodes, int32_t length, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - if(length < 0 || (reorderCodes == NULL && length > 0)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if(length == 1 && reorderCodes[0] == UCOL_REORDER_CODE_NONE) { - length = 0; - } - if(length == settings->reorderCodesLength && - uprv_memcmp(reorderCodes, settings->reorderCodes, length * 4) == 0) { - return; - } - const CollationSettings &defaultSettings = getDefaultSettings(); - if(length == 1 && reorderCodes[0] == UCOL_REORDER_CODE_DEFAULT) { - if(settings != &defaultSettings) { - CollationSettings *ownedSettings = SharedObject::copyOnWrite(settings); - if(ownedSettings == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - ownedSettings->copyReorderingFrom(defaultSettings, errorCode); - setFastLatinOptions(*ownedSettings); - } - return; - } - CollationSettings *ownedSettings = SharedObject::copyOnWrite(settings); - if(ownedSettings == NULL) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - return; - } - ownedSettings->setReordering(*data, reorderCodes, length, errorCode); - setFastLatinOptions(*ownedSettings); -} - -void -RuleBasedCollator::setFastLatinOptions(CollationSettings &ownedSettings) const { - ownedSettings.fastLatinOptions = CollationFastLatin::getOptions( - data, ownedSettings, - ownedSettings.fastLatinPrimaries, UPRV_LENGTHOF(ownedSettings.fastLatinPrimaries)); -} - -UCollationResult -RuleBasedCollator::compare(const UnicodeString &left, const UnicodeString &right, - UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } - return doCompare(left.getBuffer(), left.length(), - right.getBuffer(), right.length(), errorCode); -} - -UCollationResult -RuleBasedCollator::compare(const UnicodeString &left, const UnicodeString &right, - int32_t length, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode) || length == 0) { return UCOL_EQUAL; } - if(length < 0) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return UCOL_EQUAL; - } - int32_t leftLength = left.length(); - int32_t rightLength = right.length(); - if(leftLength > length) { leftLength = length; } - if(rightLength > length) { rightLength = length; } - return doCompare(left.getBuffer(), leftLength, - right.getBuffer(), rightLength, errorCode); -} - -UCollationResult -RuleBasedCollator::compare(const UChar *left, int32_t leftLength, - const UChar *right, int32_t rightLength, - UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } - if((left == NULL && leftLength != 0) || (right == NULL && rightLength != 0)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return UCOL_EQUAL; - } - // Make sure both or neither strings have a known length. - // We do not optimize for mixed length/termination. - if(leftLength >= 0) { - if(rightLength < 0) { rightLength = u_strlen(right); } - } else { - if(rightLength >= 0) { leftLength = u_strlen(left); } - } - return doCompare(left, leftLength, right, rightLength, errorCode); -} - -UCollationResult -RuleBasedCollator::compareUTF8(const StringPiece &left, const StringPiece &right, - UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } - const uint8_t *leftBytes = reinterpret_cast(left.data()); - const uint8_t *rightBytes = reinterpret_cast(right.data()); - if((leftBytes == NULL && !left.empty()) || (rightBytes == NULL && !right.empty())) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return UCOL_EQUAL; - } - return doCompare(leftBytes, left.length(), rightBytes, right.length(), errorCode); -} - -UCollationResult -RuleBasedCollator::internalCompareUTF8(const char *left, int32_t leftLength, - const char *right, int32_t rightLength, - UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } - if((left == NULL && leftLength != 0) || (right == NULL && rightLength != 0)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return UCOL_EQUAL; - } - // Make sure both or neither strings have a known length. - // We do not optimize for mixed length/termination. - if(leftLength >= 0) { - if(rightLength < 0) { rightLength = static_cast(uprv_strlen(right)); } - } else { - if(rightLength >= 0) { leftLength = static_cast(uprv_strlen(left)); } - } - return doCompare(reinterpret_cast(left), leftLength, - reinterpret_cast(right), rightLength, errorCode); -} - -namespace { - -/** - * Abstract iterator for identical-level string comparisons. - * Returns FCD code points and handles temporary switching to NFD. - */ -class NFDIterator : public UObject { -public: - NFDIterator() : index(-1), length(0) {} - virtual ~NFDIterator() {} - /** - * Returns the next code point from the internal normalization buffer, - * or else the next text code point. - * Returns -1 at the end of the text. - */ - UChar32 nextCodePoint() { - if(index >= 0) { - if(index == length) { - index = -1; - } else { - UChar32 c; - U16_NEXT_UNSAFE(decomp, index, c); - return c; - } - } - return nextRawCodePoint(); - } - /** - * @param nfcImpl - * @param c the last code point returned by nextCodePoint() or nextDecomposedCodePoint() - * @return the first code point in c's decomposition, - * or c itself if it was decomposed already or if it does not decompose - */ - UChar32 nextDecomposedCodePoint(const Normalizer2Impl &nfcImpl, UChar32 c) { - if(index >= 0) { return c; } - decomp = nfcImpl.getDecomposition(c, buffer, length); - if(decomp == NULL) { return c; } - index = 0; - U16_NEXT_UNSAFE(decomp, index, c); - return c; - } -protected: - /** - * Returns the next text code point in FCD order. - * Returns -1 at the end of the text. - */ - virtual UChar32 nextRawCodePoint() = 0; -private: - const UChar *decomp; - UChar buffer[4]; - int32_t index; - int32_t length; -}; - -class UTF16NFDIterator : public NFDIterator { -public: - UTF16NFDIterator(const UChar *text, const UChar *textLimit) : s(text), limit(textLimit) {} -protected: - virtual UChar32 nextRawCodePoint() override { - if(s == limit) { return U_SENTINEL; } - UChar32 c = *s++; - if(limit == NULL && c == 0) { - s = NULL; - return U_SENTINEL; - } - UChar trail; - if(U16_IS_LEAD(c) && s != limit && U16_IS_TRAIL(trail = *s)) { - ++s; - c = U16_GET_SUPPLEMENTARY(c, trail); - } - return c; - } - - const UChar *s; - const UChar *limit; -}; - -class FCDUTF16NFDIterator : public UTF16NFDIterator { -public: - FCDUTF16NFDIterator(const Normalizer2Impl &nfcImpl, const UChar *text, const UChar *textLimit) - : UTF16NFDIterator(NULL, NULL) { - UErrorCode errorCode = U_ZERO_ERROR; - const UChar *spanLimit = nfcImpl.makeFCD(text, textLimit, NULL, errorCode); - if(U_FAILURE(errorCode)) { return; } - if(spanLimit == textLimit || (textLimit == NULL && *spanLimit == 0)) { - s = text; - limit = spanLimit; - } else { - str.setTo(text, (int32_t)(spanLimit - text)); - { - ReorderingBuffer r_buffer(nfcImpl, str); - if(r_buffer.init(str.length(), errorCode)) { - nfcImpl.makeFCD(spanLimit, textLimit, &r_buffer, errorCode); - } - } - if(U_SUCCESS(errorCode)) { - s = str.getBuffer(); - limit = s + str.length(); - } - } - } -private: - UnicodeString str; -}; - -class UTF8NFDIterator : public NFDIterator { -public: - UTF8NFDIterator(const uint8_t *text, int32_t textLength) - : s(text), pos(0), length(textLength) {} -protected: - virtual UChar32 nextRawCodePoint() override { - if(pos == length || (s[pos] == 0 && length < 0)) { return U_SENTINEL; } - UChar32 c; - U8_NEXT_OR_FFFD(s, pos, length, c); - return c; - } - - const uint8_t *s; - int32_t pos; - int32_t length; -}; - -class FCDUTF8NFDIterator : public NFDIterator { -public: - FCDUTF8NFDIterator(const CollationData *data, const uint8_t *text, int32_t textLength) - : u8ci(data, false, text, 0, textLength) {} -protected: - virtual UChar32 nextRawCodePoint() override { - UErrorCode errorCode = U_ZERO_ERROR; - return u8ci.nextCodePoint(errorCode); - } -private: - FCDUTF8CollationIterator u8ci; -}; - -class UIterNFDIterator : public NFDIterator { -public: - UIterNFDIterator(UCharIterator &it) : iter(it) {} -protected: - virtual UChar32 nextRawCodePoint() override { - return uiter_next32(&iter); - } -private: - UCharIterator &iter; -}; - -class FCDUIterNFDIterator : public NFDIterator { -public: - FCDUIterNFDIterator(const CollationData *data, UCharIterator &it, int32_t startIndex) - : uici(data, false, it, startIndex) {} -protected: - virtual UChar32 nextRawCodePoint() override { - UErrorCode errorCode = U_ZERO_ERROR; - return uici.nextCodePoint(errorCode); - } -private: - FCDUIterCollationIterator uici; -}; - -UCollationResult compareNFDIter(const Normalizer2Impl &nfcImpl, - NFDIterator &left, NFDIterator &right) { - for(;;) { - // Fetch the next FCD code point from each string. - UChar32 leftCp = left.nextCodePoint(); - UChar32 rightCp = right.nextCodePoint(); - if(leftCp == rightCp) { - if(leftCp < 0) { break; } - continue; - } - // If they are different, then decompose each and compare again. - if(leftCp < 0) { - leftCp = -2; // end of string - } else if(leftCp == 0xfffe) { - leftCp = -1; // U+FFFE: merge separator - } else { - leftCp = left.nextDecomposedCodePoint(nfcImpl, leftCp); - } - if(rightCp < 0) { - rightCp = -2; // end of string - } else if(rightCp == 0xfffe) { - rightCp = -1; // U+FFFE: merge separator - } else { - rightCp = right.nextDecomposedCodePoint(nfcImpl, rightCp); - } - if(leftCp < rightCp) { return UCOL_LESS; } - if(leftCp > rightCp) { return UCOL_GREATER; } - } - return UCOL_EQUAL; -} - -} // namespace - -UCollationResult -RuleBasedCollator::doCompare(const UChar *left, int32_t leftLength, - const UChar *right, int32_t rightLength, - UErrorCode &errorCode) const { - // U_FAILURE(errorCode) checked by caller. - if(left == right && leftLength == rightLength) { - return UCOL_EQUAL; - } - - // Identical-prefix test. - const UChar *leftLimit; - const UChar *rightLimit; - int32_t equalPrefixLength = 0; - if(leftLength < 0) { - leftLimit = NULL; - rightLimit = NULL; - UChar c; - while((c = left[equalPrefixLength]) == right[equalPrefixLength]) { - if(c == 0) { return UCOL_EQUAL; } - ++equalPrefixLength; - } - } else { - leftLimit = left + leftLength; - rightLimit = right + rightLength; - for(;;) { - if(equalPrefixLength == leftLength) { - if(equalPrefixLength == rightLength) { return UCOL_EQUAL; } - break; - } else if(equalPrefixLength == rightLength || - left[equalPrefixLength] != right[equalPrefixLength]) { - break; - } - ++equalPrefixLength; - } - } - - UBool numeric = settings->isNumeric(); - if(equalPrefixLength > 0) { - if((equalPrefixLength != leftLength && - data->isUnsafeBackward(left[equalPrefixLength], numeric)) || - (equalPrefixLength != rightLength && - data->isUnsafeBackward(right[equalPrefixLength], numeric))) { - // Identical prefix: Back up to the start of a contraction or reordering sequence. - while(--equalPrefixLength > 0 && - data->isUnsafeBackward(left[equalPrefixLength], numeric)) {} - } - // Notes: - // - A longer string can compare equal to a prefix of it if only ignorables follow. - // - With a backward level, a longer string can compare less-than a prefix of it. - - // Pass the actual start of each string into the CollationIterators, - // plus the equalPrefixLength position, - // so that prefix matches back into the equal prefix work. - } - - int32_t result; - int32_t fastLatinOptions = settings->fastLatinOptions; - if(fastLatinOptions >= 0 && - (equalPrefixLength == leftLength || - left[equalPrefixLength] <= CollationFastLatin::LATIN_MAX) && - (equalPrefixLength == rightLength || - right[equalPrefixLength] <= CollationFastLatin::LATIN_MAX)) { - if(leftLength >= 0) { - result = CollationFastLatin::compareUTF16(data->fastLatinTable, - settings->fastLatinPrimaries, - fastLatinOptions, - left + equalPrefixLength, - leftLength - equalPrefixLength, - right + equalPrefixLength, - rightLength - equalPrefixLength); - } else { - result = CollationFastLatin::compareUTF16(data->fastLatinTable, - settings->fastLatinPrimaries, - fastLatinOptions, - left + equalPrefixLength, -1, - right + equalPrefixLength, -1); - } - } else { - result = CollationFastLatin::BAIL_OUT_RESULT; - } - - if(result == CollationFastLatin::BAIL_OUT_RESULT) { - if(settings->dontCheckFCD()) { - UTF16CollationIterator leftIter(data, numeric, - left, left + equalPrefixLength, leftLimit); - UTF16CollationIterator rightIter(data, numeric, - right, right + equalPrefixLength, rightLimit); - result = CollationCompare::compareUpToQuaternary(leftIter, rightIter, *settings, errorCode); - } else { - FCDUTF16CollationIterator leftIter(data, numeric, - left, left + equalPrefixLength, leftLimit); - FCDUTF16CollationIterator rightIter(data, numeric, - right, right + equalPrefixLength, rightLimit); - result = CollationCompare::compareUpToQuaternary(leftIter, rightIter, *settings, errorCode); - } - } - if(result != UCOL_EQUAL || settings->getStrength() < UCOL_IDENTICAL || U_FAILURE(errorCode)) { - return (UCollationResult)result; - } - - // Note: If NUL-terminated, we could get the actual limits from the iterators now. - // That would complicate the iterators a bit, NUL-terminated strings are only a C convenience, - // and the benefit seems unlikely to be measurable. - - // Compare identical level. - const Normalizer2Impl &nfcImpl = data->nfcImpl; - left += equalPrefixLength; - right += equalPrefixLength; - if(settings->dontCheckFCD()) { - UTF16NFDIterator leftIter(left, leftLimit); - UTF16NFDIterator rightIter(right, rightLimit); - return compareNFDIter(nfcImpl, leftIter, rightIter); - } else { - FCDUTF16NFDIterator leftIter(nfcImpl, left, leftLimit); - FCDUTF16NFDIterator rightIter(nfcImpl, right, rightLimit); - return compareNFDIter(nfcImpl, leftIter, rightIter); - } -} - -UCollationResult -RuleBasedCollator::doCompare(const uint8_t *left, int32_t leftLength, - const uint8_t *right, int32_t rightLength, - UErrorCode &errorCode) const { - // U_FAILURE(errorCode) checked by caller. - if(left == right && leftLength == rightLength) { - return UCOL_EQUAL; - } - - // Identical-prefix test. - int32_t equalPrefixLength = 0; - if(leftLength < 0) { - uint8_t c; - while((c = left[equalPrefixLength]) == right[equalPrefixLength]) { - if(c == 0) { return UCOL_EQUAL; } - ++equalPrefixLength; - } - } else { - for(;;) { - if(equalPrefixLength == leftLength) { - if(equalPrefixLength == rightLength) { return UCOL_EQUAL; } - break; - } else if(equalPrefixLength == rightLength || - left[equalPrefixLength] != right[equalPrefixLength]) { - break; - } - ++equalPrefixLength; - } - } - // Back up to the start of a partially-equal code point. - if(equalPrefixLength > 0 && - ((equalPrefixLength != leftLength && U8_IS_TRAIL(left[equalPrefixLength])) || - (equalPrefixLength != rightLength && U8_IS_TRAIL(right[equalPrefixLength])))) { - while(--equalPrefixLength > 0 && U8_IS_TRAIL(left[equalPrefixLength])) {} - } - - UBool numeric = settings->isNumeric(); - if(equalPrefixLength > 0) { - UBool unsafe = false; - if(equalPrefixLength != leftLength) { - int32_t i = equalPrefixLength; - UChar32 c; - U8_NEXT_OR_FFFD(left, i, leftLength, c); - unsafe = data->isUnsafeBackward(c, numeric); - } - if(!unsafe && equalPrefixLength != rightLength) { - int32_t i = equalPrefixLength; - UChar32 c; - U8_NEXT_OR_FFFD(right, i, rightLength, c); - unsafe = data->isUnsafeBackward(c, numeric); - } - if(unsafe) { - // Identical prefix: Back up to the start of a contraction or reordering sequence. - UChar32 c; - do { - U8_PREV_OR_FFFD(left, 0, equalPrefixLength, c); - } while(equalPrefixLength > 0 && data->isUnsafeBackward(c, numeric)); - } - // See the notes in the UTF-16 version. - - // Pass the actual start of each string into the CollationIterators, - // plus the equalPrefixLength position, - // so that prefix matches back into the equal prefix work. - } - - int32_t result; - int32_t fastLatinOptions = settings->fastLatinOptions; - if(fastLatinOptions >= 0 && - (equalPrefixLength == leftLength || - left[equalPrefixLength] <= CollationFastLatin::LATIN_MAX_UTF8_LEAD) && - (equalPrefixLength == rightLength || - right[equalPrefixLength] <= CollationFastLatin::LATIN_MAX_UTF8_LEAD)) { - if(leftLength >= 0) { - result = CollationFastLatin::compareUTF8(data->fastLatinTable, - settings->fastLatinPrimaries, - fastLatinOptions, - left + equalPrefixLength, - leftLength - equalPrefixLength, - right + equalPrefixLength, - rightLength - equalPrefixLength); - } else { - result = CollationFastLatin::compareUTF8(data->fastLatinTable, - settings->fastLatinPrimaries, - fastLatinOptions, - left + equalPrefixLength, -1, - right + equalPrefixLength, -1); - } - } else { - result = CollationFastLatin::BAIL_OUT_RESULT; - } - - if(result == CollationFastLatin::BAIL_OUT_RESULT) { - if(settings->dontCheckFCD()) { - UTF8CollationIterator leftIter(data, numeric, left, equalPrefixLength, leftLength); - UTF8CollationIterator rightIter(data, numeric, right, equalPrefixLength, rightLength); - result = CollationCompare::compareUpToQuaternary(leftIter, rightIter, *settings, errorCode); - } else { - FCDUTF8CollationIterator leftIter(data, numeric, left, equalPrefixLength, leftLength); - FCDUTF8CollationIterator rightIter(data, numeric, right, equalPrefixLength, rightLength); - result = CollationCompare::compareUpToQuaternary(leftIter, rightIter, *settings, errorCode); - } - } - if(result != UCOL_EQUAL || settings->getStrength() < UCOL_IDENTICAL || U_FAILURE(errorCode)) { - return (UCollationResult)result; - } - - // Note: If NUL-terminated, we could get the actual limits from the iterators now. - // That would complicate the iterators a bit, NUL-terminated strings are only a C convenience, - // and the benefit seems unlikely to be measurable. - - // Compare identical level. - const Normalizer2Impl &nfcImpl = data->nfcImpl; - left += equalPrefixLength; - right += equalPrefixLength; - if(leftLength > 0) { - leftLength -= equalPrefixLength; - rightLength -= equalPrefixLength; - } - if(settings->dontCheckFCD()) { - UTF8NFDIterator leftIter(left, leftLength); - UTF8NFDIterator rightIter(right, rightLength); - return compareNFDIter(nfcImpl, leftIter, rightIter); - } else { - FCDUTF8NFDIterator leftIter(data, left, leftLength); - FCDUTF8NFDIterator rightIter(data, right, rightLength); - return compareNFDIter(nfcImpl, leftIter, rightIter); - } -} - -UCollationResult -RuleBasedCollator::compare(UCharIterator &left, UCharIterator &right, - UErrorCode &errorCode) const { - if(U_FAILURE(errorCode) || &left == &right) { return UCOL_EQUAL; } - UBool numeric = settings->isNumeric(); - - // Identical-prefix test. - int32_t equalPrefixLength = 0; - { - UChar32 leftUnit; - UChar32 rightUnit; - while((leftUnit = left.next(&left)) == (rightUnit = right.next(&right))) { - if(leftUnit < 0) { return UCOL_EQUAL; } - ++equalPrefixLength; - } - - // Back out the code units that differed, for the real collation comparison. - if(leftUnit >= 0) { left.previous(&left); } - if(rightUnit >= 0) { right.previous(&right); } - - if(equalPrefixLength > 0) { - if((leftUnit >= 0 && data->isUnsafeBackward(leftUnit, numeric)) || - (rightUnit >= 0 && data->isUnsafeBackward(rightUnit, numeric))) { - // Identical prefix: Back up to the start of a contraction or reordering sequence. - do { - --equalPrefixLength; - leftUnit = left.previous(&left); - right.previous(&right); - } while(equalPrefixLength > 0 && data->isUnsafeBackward(leftUnit, numeric)); - } - // See the notes in the UTF-16 version. - } - } - - UCollationResult result; - if(settings->dontCheckFCD()) { - UIterCollationIterator leftIter(data, numeric, left); - UIterCollationIterator rightIter(data, numeric, right); - result = CollationCompare::compareUpToQuaternary(leftIter, rightIter, *settings, errorCode); - } else { - FCDUIterCollationIterator leftIter(data, numeric, left, equalPrefixLength); - FCDUIterCollationIterator rightIter(data, numeric, right, equalPrefixLength); - result = CollationCompare::compareUpToQuaternary(leftIter, rightIter, *settings, errorCode); - } - if(result != UCOL_EQUAL || settings->getStrength() < UCOL_IDENTICAL || U_FAILURE(errorCode)) { - return result; - } - - // Compare identical level. - left.move(&left, equalPrefixLength, UITER_ZERO); - right.move(&right, equalPrefixLength, UITER_ZERO); - const Normalizer2Impl &nfcImpl = data->nfcImpl; - if(settings->dontCheckFCD()) { - UIterNFDIterator leftIter(left); - UIterNFDIterator rightIter(right); - return compareNFDIter(nfcImpl, leftIter, rightIter); - } else { - FCDUIterNFDIterator leftIter(data, left, equalPrefixLength); - FCDUIterNFDIterator rightIter(data, right, equalPrefixLength); - return compareNFDIter(nfcImpl, leftIter, rightIter); - } -} - -CollationKey & -RuleBasedCollator::getCollationKey(const UnicodeString &s, CollationKey &key, - UErrorCode &errorCode) const { - return getCollationKey(s.getBuffer(), s.length(), key, errorCode); -} - -CollationKey & -RuleBasedCollator::getCollationKey(const UChar *s, int32_t length, CollationKey& key, - UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { - return key.setToBogus(); - } - if(s == NULL && length != 0) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return key.setToBogus(); - } - key.reset(); // resets the "bogus" state - CollationKeyByteSink sink(key); - writeSortKey(s, length, sink, errorCode); - if(U_FAILURE(errorCode)) { - key.setToBogus(); - } else if(key.isBogus()) { - errorCode = U_MEMORY_ALLOCATION_ERROR; - } else { - key.setLength(sink.NumberOfBytesAppended()); - } - return key; -} - -int32_t -RuleBasedCollator::getSortKey(const UnicodeString &s, - uint8_t *dest, int32_t capacity) const { - return getSortKey(s.getBuffer(), s.length(), dest, capacity); -} - -int32_t -RuleBasedCollator::getSortKey(const UChar *s, int32_t length, - uint8_t *dest, int32_t capacity) const { - if((s == NULL && length != 0) || capacity < 0 || (dest == NULL && capacity > 0)) { - return 0; - } - uint8_t noDest[1] = { 0 }; - if(dest == NULL) { - // Distinguish pure preflighting from an allocation error. - dest = noDest; - capacity = 0; - } - FixedSortKeyByteSink sink(reinterpret_cast(dest), capacity); - UErrorCode errorCode = U_ZERO_ERROR; - writeSortKey(s, length, sink, errorCode); - return U_SUCCESS(errorCode) ? sink.NumberOfBytesAppended() : 0; -} - -void -RuleBasedCollator::writeSortKey(const UChar *s, int32_t length, - SortKeyByteSink &sink, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return; } - const UChar *limit = (length >= 0) ? s + length : NULL; - UBool numeric = settings->isNumeric(); - CollationKeys::LevelCallback callback; - if(settings->dontCheckFCD()) { - UTF16CollationIterator iter(data, numeric, s, s, limit); - CollationKeys::writeSortKeyUpToQuaternary(iter, data->compressibleBytes, *settings, - sink, Collation::PRIMARY_LEVEL, - callback, true, errorCode); - } else { - FCDUTF16CollationIterator iter(data, numeric, s, s, limit); - CollationKeys::writeSortKeyUpToQuaternary(iter, data->compressibleBytes, *settings, - sink, Collation::PRIMARY_LEVEL, - callback, true, errorCode); - } - if(settings->getStrength() == UCOL_IDENTICAL) { - writeIdenticalLevel(s, limit, sink, errorCode); - } - static const char terminator = 0; // TERMINATOR_BYTE - sink.Append(&terminator, 1); -} - -void -RuleBasedCollator::writeIdenticalLevel(const UChar *s, const UChar *limit, - SortKeyByteSink &sink, UErrorCode &errorCode) const { - // NFD quick check - const UChar *nfdQCYesLimit = data->nfcImpl.decompose(s, limit, NULL, errorCode); - if(U_FAILURE(errorCode)) { return; } - sink.Append(Collation::LEVEL_SEPARATOR_BYTE); - UChar32 prev = 0; - if(nfdQCYesLimit != s) { - prev = u_writeIdenticalLevelRun(prev, s, (int32_t)(nfdQCYesLimit - s), sink); - } - // Is there non-NFD text? - int32_t destLengthEstimate; - if(limit != NULL) { - if(nfdQCYesLimit == limit) { return; } - destLengthEstimate = (int32_t)(limit - nfdQCYesLimit); - } else { - // s is NUL-terminated - if(*nfdQCYesLimit == 0) { return; } - destLengthEstimate = -1; - } - UnicodeString nfd; - data->nfcImpl.decompose(nfdQCYesLimit, limit, nfd, destLengthEstimate, errorCode); - u_writeIdenticalLevelRun(prev, nfd.getBuffer(), nfd.length(), sink); -} - -namespace { - -/** - * internalNextSortKeyPart() calls CollationKeys::writeSortKeyUpToQuaternary() - * with an instance of this callback class. - * When another level is about to be written, the callback - * records the level and the number of bytes that will be written until - * the sink (which is actually a FixedSortKeyByteSink) fills up. - * - * When internalNextSortKeyPart() is called again, it restarts with the last level - * and ignores as many bytes as were written previously for that level. - */ -class PartLevelCallback : public CollationKeys::LevelCallback { -public: - PartLevelCallback(const SortKeyByteSink &s) - : sink(s), level(Collation::PRIMARY_LEVEL) { - levelCapacity = sink.GetRemainingCapacity(); - } - virtual ~PartLevelCallback() {} - virtual UBool needToWrite(Collation::Level l) override { - if(!sink.Overflowed()) { - // Remember a level that will be at least partially written. - level = l; - levelCapacity = sink.GetRemainingCapacity(); - return true; - } else { - return false; - } - } - Collation::Level getLevel() const { return level; } - int32_t getLevelCapacity() const { return levelCapacity; } - -private: - const SortKeyByteSink &sink; - Collation::Level level; - int32_t levelCapacity; -}; - -} // namespace - -int32_t -RuleBasedCollator::internalNextSortKeyPart(UCharIterator *iter, uint32_t state[2], - uint8_t *dest, int32_t count, UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return 0; } - if(iter == NULL || state == NULL || count < 0 || (count > 0 && dest == NULL)) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - if(count == 0) { return 0; } - - FixedSortKeyByteSink sink(reinterpret_cast(dest), count); - sink.IgnoreBytes((int32_t)state[1]); - iter->move(iter, 0, UITER_START); - - Collation::Level level = (Collation::Level)state[0]; - if(level <= Collation::QUATERNARY_LEVEL) { - UBool numeric = settings->isNumeric(); - PartLevelCallback callback(sink); - if(settings->dontCheckFCD()) { - UIterCollationIterator ci(data, numeric, *iter); - CollationKeys::writeSortKeyUpToQuaternary(ci, data->compressibleBytes, *settings, - sink, level, callback, false, errorCode); - } else { - FCDUIterCollationIterator ci(data, numeric, *iter, 0); - CollationKeys::writeSortKeyUpToQuaternary(ci, data->compressibleBytes, *settings, - sink, level, callback, false, errorCode); - } - if(U_FAILURE(errorCode)) { return 0; } - if(sink.NumberOfBytesAppended() > count) { - state[0] = (uint32_t)callback.getLevel(); - state[1] = (uint32_t)callback.getLevelCapacity(); - return count; - } - // All of the normal levels are done. - if(settings->getStrength() == UCOL_IDENTICAL) { - level = Collation::IDENTICAL_LEVEL; - iter->move(iter, 0, UITER_START); - } - // else fall through to setting ZERO_LEVEL - } - - if(level == Collation::IDENTICAL_LEVEL) { - int32_t levelCapacity = sink.GetRemainingCapacity(); - UnicodeString s; - for(;;) { - UChar32 c = iter->next(iter); - if(c < 0) { break; } - s.append((UChar)c); - } - const UChar *sArray = s.getBuffer(); - writeIdenticalLevel(sArray, sArray + s.length(), sink, errorCode); - if(U_FAILURE(errorCode)) { return 0; } - if(sink.NumberOfBytesAppended() > count) { - state[0] = (uint32_t)level; - state[1] = (uint32_t)levelCapacity; - return count; - } - } - - // ZERO_LEVEL: Fill the remainder of dest with 00 bytes. - state[0] = (uint32_t)Collation::ZERO_LEVEL; - state[1] = 0; - int32_t length = sink.NumberOfBytesAppended(); - int32_t i = length; - while(i < count) { dest[i++] = 0; } - return length; -} - -void -RuleBasedCollator::internalGetCEs(const UnicodeString &str, UVector64 &ces, - UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return; } - const UChar *s = str.getBuffer(); - const UChar *limit = s + str.length(); - UBool numeric = settings->isNumeric(); - if(settings->dontCheckFCD()) { - UTF16CollationIterator iter(data, numeric, s, s, limit); - int64_t ce; - while((ce = iter.nextCE(errorCode)) != Collation::NO_CE) { - ces.addElement(ce, errorCode); - } - } else { - FCDUTF16CollationIterator iter(data, numeric, s, s, limit); - int64_t ce; - while((ce = iter.nextCE(errorCode)) != Collation::NO_CE) { - ces.addElement(ce, errorCode); - } - } -} - -namespace { - -void appendSubtag(CharString &s, char letter, const char *subtag, int32_t length, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode) || length == 0) { return; } - if(!s.isEmpty()) { - s.append('_', errorCode); - } - s.append(letter, errorCode); - for(int32_t i = 0; i < length; ++i) { - s.append(uprv_toupper(subtag[i]), errorCode); - } -} - -void appendAttribute(CharString &s, char letter, UColAttributeValue value, - UErrorCode &errorCode) { - if(U_FAILURE(errorCode)) { return; } - if(!s.isEmpty()) { - s.append('_', errorCode); - } - static const char *valueChars = "1234...........IXO..SN..LU......"; - s.append(letter, errorCode); - s.append(valueChars[value], errorCode); -} - -} // namespace - -int32_t -RuleBasedCollator::internalGetShortDefinitionString(const char *locale, - char *buffer, int32_t capacity, - UErrorCode &errorCode) const { - if(U_FAILURE(errorCode)) { return 0; } - if(buffer == NULL ? capacity != 0 : capacity < 0) { - errorCode = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - if(locale == NULL) { - locale = internalGetLocaleID(ULOC_VALID_LOCALE, errorCode); - } - - char resultLocale[ULOC_FULLNAME_CAPACITY + 1]; - int32_t length = ucol_getFunctionalEquivalent(resultLocale, ULOC_FULLNAME_CAPACITY, - "collation", locale, - NULL, &errorCode); - if(U_FAILURE(errorCode)) { return 0; } - resultLocale[length] = 0; - - // Append items in alphabetic order of their short definition letters. - CharString result; - char subtag[ULOC_KEYWORD_AND_VALUES_CAPACITY]; - - if(attributeHasBeenSetExplicitly(UCOL_ALTERNATE_HANDLING)) { - appendAttribute(result, 'A', getAttribute(UCOL_ALTERNATE_HANDLING, errorCode), errorCode); - } - // ATTR_VARIABLE_TOP not supported because 'B' was broken. - // See ICU tickets #10372 and #10386. - if(attributeHasBeenSetExplicitly(UCOL_CASE_FIRST)) { - appendAttribute(result, 'C', getAttribute(UCOL_CASE_FIRST, errorCode), errorCode); - } - if(attributeHasBeenSetExplicitly(UCOL_NUMERIC_COLLATION)) { - appendAttribute(result, 'D', getAttribute(UCOL_NUMERIC_COLLATION, errorCode), errorCode); - } - if(attributeHasBeenSetExplicitly(UCOL_CASE_LEVEL)) { - appendAttribute(result, 'E', getAttribute(UCOL_CASE_LEVEL, errorCode), errorCode); - } - if(attributeHasBeenSetExplicitly(UCOL_FRENCH_COLLATION)) { - appendAttribute(result, 'F', getAttribute(UCOL_FRENCH_COLLATION, errorCode), errorCode); - } - // Note: UCOL_HIRAGANA_QUATERNARY_MODE is deprecated and never changes away from default. - length = uloc_getKeywordValue(resultLocale, "collation", subtag, UPRV_LENGTHOF(subtag), &errorCode); - appendSubtag(result, 'K', subtag, length, errorCode); - length = uloc_getLanguage(resultLocale, subtag, UPRV_LENGTHOF(subtag), &errorCode); - if (length == 0) { - appendSubtag(result, 'L', "root", 4, errorCode); - } else { - appendSubtag(result, 'L', subtag, length, errorCode); - } - if(attributeHasBeenSetExplicitly(UCOL_NORMALIZATION_MODE)) { - appendAttribute(result, 'N', getAttribute(UCOL_NORMALIZATION_MODE, errorCode), errorCode); - } - length = uloc_getCountry(resultLocale, subtag, UPRV_LENGTHOF(subtag), &errorCode); - appendSubtag(result, 'R', subtag, length, errorCode); - if(attributeHasBeenSetExplicitly(UCOL_STRENGTH)) { - appendAttribute(result, 'S', getAttribute(UCOL_STRENGTH, errorCode), errorCode); - } - length = uloc_getVariant(resultLocale, subtag, UPRV_LENGTHOF(subtag), &errorCode); - appendSubtag(result, 'V', subtag, length, errorCode); - length = uloc_getScript(resultLocale, subtag, UPRV_LENGTHOF(subtag), &errorCode); - appendSubtag(result, 'Z', subtag, length, errorCode); - - if(U_FAILURE(errorCode)) { return 0; } - return result.extract(buffer, capacity, errorCode); -} - -UBool -RuleBasedCollator::isUnsafe(UChar32 c) const { - return data->isUnsafeBackward(c, settings->isNumeric()); -} - -void U_CALLCONV -RuleBasedCollator::computeMaxExpansions(const CollationTailoring *t, UErrorCode &errorCode) { - t->maxExpansions = CollationElementIterator::computeMaxExpansions(t->data, errorCode); -} - -UBool -RuleBasedCollator::initMaxExpansions(UErrorCode &errorCode) const { - umtx_initOnce(tailoring->maxExpansionsInitOnce, computeMaxExpansions, tailoring, errorCode); - return U_SUCCESS(errorCode); -} - -CollationElementIterator * -RuleBasedCollator::createCollationElementIterator(const UnicodeString& source) const { - UErrorCode errorCode = U_ZERO_ERROR; - if(!initMaxExpansions(errorCode)) { return NULL; } - CollationElementIterator *cei = new CollationElementIterator(source, this, errorCode); - if(U_FAILURE(errorCode)) { - delete cei; - return NULL; - } - return cei; -} - -CollationElementIterator * -RuleBasedCollator::createCollationElementIterator(const CharacterIterator& source) const { - UErrorCode errorCode = U_ZERO_ERROR; - if(!initMaxExpansions(errorCode)) { return NULL; } - CollationElementIterator *cei = new CollationElementIterator(source, this, errorCode); - if(U_FAILURE(errorCode)) { - delete cei; - return NULL; - } - return cei; -} - -int32_t -RuleBasedCollator::getMaxExpansion(int32_t order) const { - UErrorCode errorCode = U_ZERO_ERROR; - (void)initMaxExpansions(errorCode); - return CollationElementIterator::getMaxExpansion(tailoring->maxExpansions, order); -} - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_COLLATION +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1996-2015, International Business Machines +* Corporation and others. All Rights Reserved. +******************************************************************************* +* rulebasedcollator.cpp +* +* (replaced the former tblcoll.cpp) +* +* created on: 2012feb14 with new and old collation code +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_COLLATION + +#include "unicode/coll.h" +#include "unicode/coleitr.h" +#include "unicode/localpointer.h" +#include "unicode/locid.h" +#include "unicode/sortkey.h" +#include "unicode/tblcoll.h" +#include "unicode/ucol.h" +#include "unicode/uiter.h" +#include "unicode/uloc.h" +#include "unicode/uniset.h" +#include "unicode/unistr.h" +#include "unicode/usetiter.h" +#include "unicode/utf8.h" +#include "unicode/uversion.h" +#include "bocsu.h" +#include "charstr.h" +#include "cmemory.h" +#include "collation.h" +#include "collationcompare.h" +#include "collationdata.h" +#include "collationdatareader.h" +#include "collationfastlatin.h" +#include "collationiterator.h" +#include "collationkeys.h" +#include "collationroot.h" +#include "collationsets.h" +#include "collationsettings.h" +#include "collationtailoring.h" +#include "cstring.h" +#include "uassert.h" +#include "ucol_imp.h" +#include "uhash.h" +#include "uitercollationiterator.h" +#include "ustr_imp.h" +#include "utf16collationiterator.h" +#include "utf8collationiterator.h" +#include "uvectr64.h" + +U_NAMESPACE_BEGIN + +namespace { + +class FixedSortKeyByteSink : public SortKeyByteSink { +public: + FixedSortKeyByteSink(char *dest, int32_t destCapacity) + : SortKeyByteSink(dest, destCapacity) {} + virtual ~FixedSortKeyByteSink(); + +private: + virtual void AppendBeyondCapacity(const char *bytes, int32_t n, int32_t length) override; + virtual UBool Resize(int32_t appendCapacity, int32_t length) override; +}; + +FixedSortKeyByteSink::~FixedSortKeyByteSink() {} + +void +FixedSortKeyByteSink::AppendBeyondCapacity(const char *bytes, int32_t /*n*/, int32_t length) { + // buffer_ != nullptr && bytes != nullptr && n > 0 && appended_ > capacity_ + // Fill the buffer completely. + int32_t available = capacity_ - length; + if (available > 0) { + uprv_memcpy(buffer_ + length, bytes, available); + } +} + +UBool +FixedSortKeyByteSink::Resize(int32_t /*appendCapacity*/, int32_t /*length*/) { + return false; +} + +} // namespace + +// Not in an anonymous namespace, so that it can be a friend of CollationKey. +class CollationKeyByteSink : public SortKeyByteSink { +public: + CollationKeyByteSink(CollationKey &key) + : SortKeyByteSink(reinterpret_cast(key.getBytes()), key.getCapacity()), + key_(key) {} + virtual ~CollationKeyByteSink(); + +private: + virtual void AppendBeyondCapacity(const char *bytes, int32_t n, int32_t length) override; + virtual UBool Resize(int32_t appendCapacity, int32_t length) override; + + CollationKey &key_; +}; + +CollationKeyByteSink::~CollationKeyByteSink() {} + +void +CollationKeyByteSink::AppendBeyondCapacity(const char *bytes, int32_t n, int32_t length) { + // buffer_ != nullptr && bytes != nullptr && n > 0 && appended_ > capacity_ + if (Resize(n, length)) { + uprv_memcpy(buffer_ + length, bytes, n); + } +} + +UBool +CollationKeyByteSink::Resize(int32_t appendCapacity, int32_t length) { + if (buffer_ == nullptr) { + return false; // allocation failed before already + } + int32_t newCapacity = 2 * capacity_; + int32_t altCapacity = length + 2 * appendCapacity; + if (newCapacity < altCapacity) { + newCapacity = altCapacity; + } + if (newCapacity < 200) { + newCapacity = 200; + } + uint8_t *newBuffer = key_.reallocate(newCapacity, length); + if (newBuffer == nullptr) { + SetNotOk(); + return false; + } + buffer_ = reinterpret_cast(newBuffer); + capacity_ = newCapacity; + return true; +} + +RuleBasedCollator::RuleBasedCollator(const RuleBasedCollator &other) + : Collator(other), + data(other.data), + settings(other.settings), + tailoring(other.tailoring), + cacheEntry(other.cacheEntry), + validLocale(other.validLocale), + explicitlySetAttributes(other.explicitlySetAttributes), + actualLocaleIsSameAsValid(other.actualLocaleIsSameAsValid) { + settings->addRef(); + cacheEntry->addRef(); +} + +RuleBasedCollator::RuleBasedCollator(const uint8_t *bin, int32_t length, + const RuleBasedCollator *base, UErrorCode &errorCode) + : data(nullptr), + settings(nullptr), + tailoring(nullptr), + cacheEntry(nullptr), + validLocale(""), + explicitlySetAttributes(0), + actualLocaleIsSameAsValid(false) { + if(U_FAILURE(errorCode)) { return; } + if(bin == nullptr || length == 0 || base == nullptr) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + const CollationTailoring *root = CollationRoot::getRoot(errorCode); + if(U_FAILURE(errorCode)) { return; } + if(base->tailoring != root) { + errorCode = U_UNSUPPORTED_ERROR; + return; + } + LocalPointer t(new CollationTailoring(base->tailoring->settings)); + if(t.isNull() || t->isBogus()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + CollationDataReader::read(base->tailoring, bin, length, *t, errorCode); + if(U_FAILURE(errorCode)) { return; } + t->actualLocale.setToBogus(); + adoptTailoring(t.orphan(), errorCode); +} + +RuleBasedCollator::RuleBasedCollator(const CollationCacheEntry *entry) + : data(entry->tailoring->data), + settings(entry->tailoring->settings), + tailoring(entry->tailoring), + cacheEntry(entry), + validLocale(entry->validLocale), + explicitlySetAttributes(0), + actualLocaleIsSameAsValid(false) { + settings->addRef(); + cacheEntry->addRef(); +} + +RuleBasedCollator::~RuleBasedCollator() { + SharedObject::clearPtr(settings); + SharedObject::clearPtr(cacheEntry); +} + +void +RuleBasedCollator::adoptTailoring(CollationTailoring *t, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { + t->deleteIfZeroRefCount(); + return; + } + U_ASSERT(settings == nullptr && data == nullptr && tailoring == nullptr && cacheEntry == nullptr); + cacheEntry = new CollationCacheEntry(t->actualLocale, t); + if(cacheEntry == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + t->deleteIfZeroRefCount(); + return; + } + data = t->data; + settings = t->settings; + settings->addRef(); + tailoring = t; + cacheEntry->addRef(); + validLocale = t->actualLocale; + actualLocaleIsSameAsValid = false; +} + +RuleBasedCollator * +RuleBasedCollator::clone() const { + return new RuleBasedCollator(*this); +} + +RuleBasedCollator &RuleBasedCollator::operator=(const RuleBasedCollator &other) { + if(this == &other) { return *this; } + SharedObject::copyPtr(other.settings, settings); + tailoring = other.tailoring; + SharedObject::copyPtr(other.cacheEntry, cacheEntry); + data = tailoring->data; + validLocale = other.validLocale; + explicitlySetAttributes = other.explicitlySetAttributes; + actualLocaleIsSameAsValid = other.actualLocaleIsSameAsValid; + return *this; +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedCollator) + +bool +RuleBasedCollator::operator==(const Collator& other) const { + if(this == &other) { return true; } + if(!Collator::operator==(other)) { return false; } + const RuleBasedCollator &o = static_cast(other); + if(*settings != *o.settings) { return false; } + if(data == o.data) { return true; } + UBool thisIsRoot = data->base == nullptr; + UBool otherIsRoot = o.data->base == nullptr; + U_ASSERT(!thisIsRoot || !otherIsRoot); // otherwise their data pointers should be == + if(thisIsRoot != otherIsRoot) { return false; } + if((thisIsRoot || !tailoring->rules.isEmpty()) && + (otherIsRoot || !o.tailoring->rules.isEmpty())) { + // Shortcut: If both collators have valid rule strings, then compare those. + if(tailoring->rules == o.tailoring->rules) { return true; } + } + // Different rule strings can result in the same or equivalent tailoring. + // The rule strings are optional in ICU resource bundles, although included by default. + // cloneBinary() drops the rule string. + UErrorCode errorCode = U_ZERO_ERROR; + LocalPointer thisTailored(getTailoredSet(errorCode)); + LocalPointer otherTailored(o.getTailoredSet(errorCode)); + if(U_FAILURE(errorCode)) { return false; } + if(*thisTailored != *otherTailored) { return false; } + // For completeness, we should compare all of the mappings; + // or we should create a list of strings, sort it with one collator, + // and check if both collators compare adjacent strings the same + // (order & strength, down to quaternary); or similar. + // Testing equality of collators seems unusual. + return true; +} + +int32_t +RuleBasedCollator::hashCode() const { + int32_t h = settings->hashCode(); + if(data->base == nullptr) { return h; } // root collator + // Do not rely on the rule string, see comments in operator==(). + UErrorCode errorCode = U_ZERO_ERROR; + LocalPointer set(getTailoredSet(errorCode)); + if(U_FAILURE(errorCode)) { return 0; } + UnicodeSetIterator iter(*set); + while(iter.next() && !iter.isString()) { + h ^= data->getCE32(iter.getCodepoint()); + } + return h; +} + +void +RuleBasedCollator::setLocales(const Locale &requested, const Locale &valid, + const Locale &actual) { + if(actual == tailoring->actualLocale) { + actualLocaleIsSameAsValid = false; + } else { + U_ASSERT(actual == valid); + actualLocaleIsSameAsValid = true; + } + // Do not modify tailoring.actualLocale: + // We cannot be sure that that would be thread-safe. + validLocale = valid; + (void)requested; // Ignore, see also ticket #10477. +} + +Locale +RuleBasedCollator::getLocale(ULocDataLocaleType type, UErrorCode& errorCode) const { + if(U_FAILURE(errorCode)) { + return Locale::getRoot(); + } + switch(type) { + case ULOC_ACTUAL_LOCALE: + return actualLocaleIsSameAsValid ? validLocale : tailoring->actualLocale; + case ULOC_VALID_LOCALE: + return validLocale; + case ULOC_REQUESTED_LOCALE: + default: + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return Locale::getRoot(); + } +} + +const char * +RuleBasedCollator::internalGetLocaleID(ULocDataLocaleType type, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return nullptr; + } + const Locale *result; + switch(type) { + case ULOC_ACTUAL_LOCALE: + result = actualLocaleIsSameAsValid ? &validLocale : &tailoring->actualLocale; + break; + case ULOC_VALID_LOCALE: + result = &validLocale; + break; + case ULOC_REQUESTED_LOCALE: + default: + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + if(result->isBogus()) { return nullptr; } + const char *id = result->getName(); + return id[0] == 0 ? "root" : id; +} + +const UnicodeString& +RuleBasedCollator::getRules() const { + return tailoring->rules; +} + +void +RuleBasedCollator::getRules(UColRuleOption delta, UnicodeString &buffer) const { + if(delta == UCOL_TAILORING_ONLY) { + buffer = tailoring->rules; + return; + } + // UCOL_FULL_RULES + buffer.remove(); + CollationLoader::appendRootRules(buffer); + buffer.append(tailoring->rules).getTerminatedBuffer(); +} + +void +RuleBasedCollator::getVersion(UVersionInfo version) const { + uprv_memcpy(version, tailoring->version, U_MAX_VERSION_LENGTH); + version[0] += (UCOL_RUNTIME_VERSION << 4) + (UCOL_RUNTIME_VERSION >> 4); +} + +UnicodeSet * +RuleBasedCollator::getTailoredSet(UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return nullptr; } + UnicodeSet *tailored = new UnicodeSet(); + if(tailored == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + if(data->base != nullptr) { + TailoredSet(tailored).forData(data, errorCode); + if(U_FAILURE(errorCode)) { + delete tailored; + return nullptr; + } + } + return tailored; +} + +void +RuleBasedCollator::internalGetContractionsAndExpansions( + UnicodeSet *contractions, UnicodeSet *expansions, + UBool addPrefixes, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return; } + if(contractions != nullptr) { + contractions->clear(); + } + if(expansions != nullptr) { + expansions->clear(); + } + ContractionsAndExpansions(contractions, expansions, nullptr, addPrefixes).forData(data, errorCode); +} + +void +RuleBasedCollator::internalAddContractions(UChar32 c, UnicodeSet &set, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return; } + ContractionsAndExpansions(&set, nullptr, nullptr, false).forCodePoint(data, c, errorCode); +} + +const CollationSettings & +RuleBasedCollator::getDefaultSettings() const { + return *tailoring->settings; +} + +UColAttributeValue +RuleBasedCollator::getAttribute(UColAttribute attr, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return UCOL_DEFAULT; } + int32_t option; + switch(attr) { + case UCOL_FRENCH_COLLATION: + option = CollationSettings::BACKWARD_SECONDARY; + break; + case UCOL_ALTERNATE_HANDLING: + return settings->getAlternateHandling(); + case UCOL_CASE_FIRST: + return settings->getCaseFirst(); + case UCOL_CASE_LEVEL: + option = CollationSettings::CASE_LEVEL; + break; + case UCOL_NORMALIZATION_MODE: + option = CollationSettings::CHECK_FCD; + break; + case UCOL_STRENGTH: + return (UColAttributeValue)settings->getStrength(); + case UCOL_HIRAGANA_QUATERNARY_MODE: + // Deprecated attribute, unsettable. + return UCOL_OFF; + case UCOL_NUMERIC_COLLATION: + option = CollationSettings::NUMERIC; + break; + default: + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return UCOL_DEFAULT; + } + return ((settings->options & option) == 0) ? UCOL_OFF : UCOL_ON; +} + +void +RuleBasedCollator::setAttribute(UColAttribute attr, UColAttributeValue value, + UErrorCode &errorCode) { + UColAttributeValue oldValue = getAttribute(attr, errorCode); + if(U_FAILURE(errorCode)) { return; } + if(value == oldValue) { + setAttributeExplicitly(attr); + return; + } + const CollationSettings &defaultSettings = getDefaultSettings(); + if(settings == &defaultSettings) { + if(value == UCOL_DEFAULT) { + setAttributeDefault(attr); + return; + } + } + CollationSettings *ownedSettings = SharedObject::copyOnWrite(settings); + if(ownedSettings == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + + switch(attr) { + case UCOL_FRENCH_COLLATION: + ownedSettings->setFlag(CollationSettings::BACKWARD_SECONDARY, value, + defaultSettings.options, errorCode); + break; + case UCOL_ALTERNATE_HANDLING: + ownedSettings->setAlternateHandling(value, defaultSettings.options, errorCode); + break; + case UCOL_CASE_FIRST: + ownedSettings->setCaseFirst(value, defaultSettings.options, errorCode); + break; + case UCOL_CASE_LEVEL: + ownedSettings->setFlag(CollationSettings::CASE_LEVEL, value, + defaultSettings.options, errorCode); + break; + case UCOL_NORMALIZATION_MODE: + ownedSettings->setFlag(CollationSettings::CHECK_FCD, value, + defaultSettings.options, errorCode); + break; + case UCOL_STRENGTH: + ownedSettings->setStrength(value, defaultSettings.options, errorCode); + break; + case UCOL_HIRAGANA_QUATERNARY_MODE: + // Deprecated attribute. Check for valid values but do not change anything. + if(value != UCOL_OFF && value != UCOL_ON && value != UCOL_DEFAULT) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + break; + case UCOL_NUMERIC_COLLATION: + ownedSettings->setFlag(CollationSettings::NUMERIC, value, defaultSettings.options, errorCode); + break; + default: + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + break; + } + if(U_FAILURE(errorCode)) { return; } + setFastLatinOptions(*ownedSettings); + if(value == UCOL_DEFAULT) { + setAttributeDefault(attr); + } else { + setAttributeExplicitly(attr); + } +} + +Collator & +RuleBasedCollator::setMaxVariable(UColReorderCode group, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return *this; } + // Convert the reorder code into a MaxVariable number, or UCOL_DEFAULT=-1. + int32_t value; + if(group == UCOL_REORDER_CODE_DEFAULT) { + value = UCOL_DEFAULT; + } else if(UCOL_REORDER_CODE_FIRST <= group && group <= UCOL_REORDER_CODE_CURRENCY) { + value = group - UCOL_REORDER_CODE_FIRST; + } else { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return *this; + } + CollationSettings::MaxVariable oldValue = settings->getMaxVariable(); + if(value == oldValue) { + setAttributeExplicitly(ATTR_VARIABLE_TOP); + return *this; + } + const CollationSettings &defaultSettings = getDefaultSettings(); + if(settings == &defaultSettings) { + if(value == UCOL_DEFAULT) { + setAttributeDefault(ATTR_VARIABLE_TOP); + return *this; + } + } + CollationSettings *ownedSettings = SharedObject::copyOnWrite(settings); + if(ownedSettings == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return *this; + } + + if(group == UCOL_REORDER_CODE_DEFAULT) { + group = (UColReorderCode)( + UCOL_REORDER_CODE_FIRST + int32_t{defaultSettings.getMaxVariable()}); + } + uint32_t varTop = data->getLastPrimaryForGroup(group); + U_ASSERT(varTop != 0); + ownedSettings->setMaxVariable(value, defaultSettings.options, errorCode); + if(U_FAILURE(errorCode)) { return *this; } + ownedSettings->variableTop = varTop; + setFastLatinOptions(*ownedSettings); + if(value == UCOL_DEFAULT) { + setAttributeDefault(ATTR_VARIABLE_TOP); + } else { + setAttributeExplicitly(ATTR_VARIABLE_TOP); + } + return *this; +} + +UColReorderCode +RuleBasedCollator::getMaxVariable() const { + return (UColReorderCode)(UCOL_REORDER_CODE_FIRST + int32_t{settings->getMaxVariable()}); +} + +uint32_t +RuleBasedCollator::getVariableTop(UErrorCode & /*errorCode*/) const { + return settings->variableTop; +} + +uint32_t +RuleBasedCollator::setVariableTop(const char16_t *varTop, int32_t len, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return 0; } + if(varTop == nullptr && len !=0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + if(len < 0) { len = u_strlen(varTop); } + if(len == 0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UBool numeric = settings->isNumeric(); + int64_t ce1, ce2; + if(settings->dontCheckFCD()) { + UTF16CollationIterator ci(data, numeric, varTop, varTop, varTop + len); + ce1 = ci.nextCE(errorCode); + ce2 = ci.nextCE(errorCode); + } else { + FCDUTF16CollationIterator ci(data, numeric, varTop, varTop, varTop + len); + ce1 = ci.nextCE(errorCode); + ce2 = ci.nextCE(errorCode); + } + if(ce1 == Collation::NO_CE || ce2 != Collation::NO_CE) { + errorCode = U_CE_NOT_FOUND_ERROR; + return 0; + } + setVariableTop((uint32_t)(ce1 >> 32), errorCode); + return settings->variableTop; +} + +uint32_t +RuleBasedCollator::setVariableTop(const UnicodeString &varTop, UErrorCode &errorCode) { + return setVariableTop(varTop.getBuffer(), varTop.length(), errorCode); +} + +void +RuleBasedCollator::setVariableTop(uint32_t varTop, UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + if(varTop != settings->variableTop) { + // Pin the variable top to the end of the reordering group which contains it. + // Only a few special groups are supported. + int32_t group = data->getGroupForPrimary(varTop); + if(group < UCOL_REORDER_CODE_FIRST || UCOL_REORDER_CODE_CURRENCY < group) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + uint32_t v = data->getLastPrimaryForGroup(group); + U_ASSERT(v != 0 && v >= varTop); + varTop = v; + if(varTop != settings->variableTop) { + CollationSettings *ownedSettings = SharedObject::copyOnWrite(settings); + if(ownedSettings == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + ownedSettings->setMaxVariable(group - UCOL_REORDER_CODE_FIRST, + getDefaultSettings().options, errorCode); + if(U_FAILURE(errorCode)) { return; } + ownedSettings->variableTop = varTop; + setFastLatinOptions(*ownedSettings); + } + } + if(varTop == getDefaultSettings().variableTop) { + setAttributeDefault(ATTR_VARIABLE_TOP); + } else { + setAttributeExplicitly(ATTR_VARIABLE_TOP); + } +} + +int32_t +RuleBasedCollator::getReorderCodes(int32_t *dest, int32_t capacity, + UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return 0; } + if(capacity < 0 || (dest == nullptr && capacity > 0)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + int32_t length = settings->reorderCodesLength; + if(length == 0) { return 0; } + if(length > capacity) { + errorCode = U_BUFFER_OVERFLOW_ERROR; + return length; + } + uprv_memcpy(dest, settings->reorderCodes, length * 4); + return length; +} + +void +RuleBasedCollator::setReorderCodes(const int32_t *reorderCodes, int32_t length, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + if(length < 0 || (reorderCodes == nullptr && length > 0)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + if(length == 1 && reorderCodes[0] == UCOL_REORDER_CODE_NONE) { + length = 0; + } + if(length == settings->reorderCodesLength && + uprv_memcmp(reorderCodes, settings->reorderCodes, length * 4) == 0) { + return; + } + const CollationSettings &defaultSettings = getDefaultSettings(); + if(length == 1 && reorderCodes[0] == UCOL_REORDER_CODE_DEFAULT) { + if(settings != &defaultSettings) { + CollationSettings *ownedSettings = SharedObject::copyOnWrite(settings); + if(ownedSettings == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + ownedSettings->copyReorderingFrom(defaultSettings, errorCode); + setFastLatinOptions(*ownedSettings); + } + return; + } + CollationSettings *ownedSettings = SharedObject::copyOnWrite(settings); + if(ownedSettings == nullptr) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + return; + } + ownedSettings->setReordering(*data, reorderCodes, length, errorCode); + setFastLatinOptions(*ownedSettings); +} + +void +RuleBasedCollator::setFastLatinOptions(CollationSettings &ownedSettings) const { + ownedSettings.fastLatinOptions = CollationFastLatin::getOptions( + data, ownedSettings, + ownedSettings.fastLatinPrimaries, UPRV_LENGTHOF(ownedSettings.fastLatinPrimaries)); +} + +UCollationResult +RuleBasedCollator::compare(const UnicodeString &left, const UnicodeString &right, + UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } + return doCompare(left.getBuffer(), left.length(), + right.getBuffer(), right.length(), errorCode); +} + +UCollationResult +RuleBasedCollator::compare(const UnicodeString &left, const UnicodeString &right, + int32_t length, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode) || length == 0) { return UCOL_EQUAL; } + if(length < 0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return UCOL_EQUAL; + } + int32_t leftLength = left.length(); + int32_t rightLength = right.length(); + if(leftLength > length) { leftLength = length; } + if(rightLength > length) { rightLength = length; } + return doCompare(left.getBuffer(), leftLength, + right.getBuffer(), rightLength, errorCode); +} + +UCollationResult +RuleBasedCollator::compare(const char16_t *left, int32_t leftLength, + const char16_t *right, int32_t rightLength, + UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } + if((left == nullptr && leftLength != 0) || (right == nullptr && rightLength != 0)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return UCOL_EQUAL; + } + // Make sure both or neither strings have a known length. + // We do not optimize for mixed length/termination. + if(leftLength >= 0) { + if(rightLength < 0) { rightLength = u_strlen(right); } + } else { + if(rightLength >= 0) { leftLength = u_strlen(left); } + } + return doCompare(left, leftLength, right, rightLength, errorCode); +} + +UCollationResult +RuleBasedCollator::compareUTF8(const StringPiece &left, const StringPiece &right, + UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } + const uint8_t *leftBytes = reinterpret_cast(left.data()); + const uint8_t *rightBytes = reinterpret_cast(right.data()); + if((leftBytes == nullptr && !left.empty()) || (rightBytes == nullptr && !right.empty())) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return UCOL_EQUAL; + } + return doCompare(leftBytes, left.length(), rightBytes, right.length(), errorCode); +} + +UCollationResult +RuleBasedCollator::internalCompareUTF8(const char *left, int32_t leftLength, + const char *right, int32_t rightLength, + UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return UCOL_EQUAL; } + if((left == nullptr && leftLength != 0) || (right == nullptr && rightLength != 0)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return UCOL_EQUAL; + } + // Make sure both or neither strings have a known length. + // We do not optimize for mixed length/termination. + if(leftLength >= 0) { + if(rightLength < 0) { rightLength = static_cast(uprv_strlen(right)); } + } else { + if(rightLength >= 0) { leftLength = static_cast(uprv_strlen(left)); } + } + return doCompare(reinterpret_cast(left), leftLength, + reinterpret_cast(right), rightLength, errorCode); +} + +namespace { + +/** + * Abstract iterator for identical-level string comparisons. + * Returns FCD code points and handles temporary switching to NFD. + */ +class NFDIterator : public UObject { +public: + NFDIterator() : index(-1), length(0) {} + virtual ~NFDIterator() {} + /** + * Returns the next code point from the internal normalization buffer, + * or else the next text code point. + * Returns -1 at the end of the text. + */ + UChar32 nextCodePoint() { + if(index >= 0) { + if(index == length) { + index = -1; + } else { + UChar32 c; + U16_NEXT_UNSAFE(decomp, index, c); + return c; + } + } + return nextRawCodePoint(); + } + /** + * @param nfcImpl + * @param c the last code point returned by nextCodePoint() or nextDecomposedCodePoint() + * @return the first code point in c's decomposition, + * or c itself if it was decomposed already or if it does not decompose + */ + UChar32 nextDecomposedCodePoint(const Normalizer2Impl &nfcImpl, UChar32 c) { + if(index >= 0) { return c; } + decomp = nfcImpl.getDecomposition(c, buffer, length); + if(decomp == nullptr) { return c; } + index = 0; + U16_NEXT_UNSAFE(decomp, index, c); + return c; + } +protected: + /** + * Returns the next text code point in FCD order. + * Returns -1 at the end of the text. + */ + virtual UChar32 nextRawCodePoint() = 0; +private: + const char16_t *decomp; + char16_t buffer[4]; + int32_t index; + int32_t length; +}; + +class UTF16NFDIterator : public NFDIterator { +public: + UTF16NFDIterator(const char16_t *text, const char16_t *textLimit) : s(text), limit(textLimit) {} +protected: + virtual UChar32 nextRawCodePoint() override { + if(s == limit) { return U_SENTINEL; } + UChar32 c = *s++; + if(limit == nullptr && c == 0) { + s = nullptr; + return U_SENTINEL; + } + char16_t trail; + if(U16_IS_LEAD(c) && s != limit && U16_IS_TRAIL(trail = *s)) { + ++s; + c = U16_GET_SUPPLEMENTARY(c, trail); + } + return c; + } + + const char16_t *s; + const char16_t *limit; +}; + +class FCDUTF16NFDIterator : public UTF16NFDIterator { +public: + FCDUTF16NFDIterator(const Normalizer2Impl &nfcImpl, const char16_t *text, const char16_t *textLimit) + : UTF16NFDIterator(nullptr, nullptr) { + UErrorCode errorCode = U_ZERO_ERROR; + const char16_t *spanLimit = nfcImpl.makeFCD(text, textLimit, nullptr, errorCode); + if(U_FAILURE(errorCode)) { return; } + if(spanLimit == textLimit || (textLimit == nullptr && *spanLimit == 0)) { + s = text; + limit = spanLimit; + } else { + str.setTo(text, (int32_t)(spanLimit - text)); + { + ReorderingBuffer r_buffer(nfcImpl, str); + if(r_buffer.init(str.length(), errorCode)) { + nfcImpl.makeFCD(spanLimit, textLimit, &r_buffer, errorCode); + } + } + if(U_SUCCESS(errorCode)) { + s = str.getBuffer(); + limit = s + str.length(); + } + } + } +private: + UnicodeString str; +}; + +class UTF8NFDIterator : public NFDIterator { +public: + UTF8NFDIterator(const uint8_t *text, int32_t textLength) + : s(text), pos(0), length(textLength) {} +protected: + virtual UChar32 nextRawCodePoint() override { + if(pos == length || (s[pos] == 0 && length < 0)) { return U_SENTINEL; } + UChar32 c; + U8_NEXT_OR_FFFD(s, pos, length, c); + return c; + } + + const uint8_t *s; + int32_t pos; + int32_t length; +}; + +class FCDUTF8NFDIterator : public NFDIterator { +public: + FCDUTF8NFDIterator(const CollationData *data, const uint8_t *text, int32_t textLength) + : u8ci(data, false, text, 0, textLength) {} +protected: + virtual UChar32 nextRawCodePoint() override { + UErrorCode errorCode = U_ZERO_ERROR; + return u8ci.nextCodePoint(errorCode); + } +private: + FCDUTF8CollationIterator u8ci; +}; + +class UIterNFDIterator : public NFDIterator { +public: + UIterNFDIterator(UCharIterator &it) : iter(it) {} +protected: + virtual UChar32 nextRawCodePoint() override { + return uiter_next32(&iter); + } +private: + UCharIterator &iter; +}; + +class FCDUIterNFDIterator : public NFDIterator { +public: + FCDUIterNFDIterator(const CollationData *data, UCharIterator &it, int32_t startIndex) + : uici(data, false, it, startIndex) {} +protected: + virtual UChar32 nextRawCodePoint() override { + UErrorCode errorCode = U_ZERO_ERROR; + return uici.nextCodePoint(errorCode); + } +private: + FCDUIterCollationIterator uici; +}; + +UCollationResult compareNFDIter(const Normalizer2Impl &nfcImpl, + NFDIterator &left, NFDIterator &right) { + for(;;) { + // Fetch the next FCD code point from each string. + UChar32 leftCp = left.nextCodePoint(); + UChar32 rightCp = right.nextCodePoint(); + if(leftCp == rightCp) { + if(leftCp < 0) { break; } + continue; + } + // If they are different, then decompose each and compare again. + if(leftCp < 0) { + leftCp = -2; // end of string + } else if(leftCp == 0xfffe) { + leftCp = -1; // U+FFFE: merge separator + } else { + leftCp = left.nextDecomposedCodePoint(nfcImpl, leftCp); + } + if(rightCp < 0) { + rightCp = -2; // end of string + } else if(rightCp == 0xfffe) { + rightCp = -1; // U+FFFE: merge separator + } else { + rightCp = right.nextDecomposedCodePoint(nfcImpl, rightCp); + } + if(leftCp < rightCp) { return UCOL_LESS; } + if(leftCp > rightCp) { return UCOL_GREATER; } + } + return UCOL_EQUAL; +} + +} // namespace + +UCollationResult +RuleBasedCollator::doCompare(const char16_t *left, int32_t leftLength, + const char16_t *right, int32_t rightLength, + UErrorCode &errorCode) const { + // U_FAILURE(errorCode) checked by caller. + if(left == right && leftLength == rightLength) { + return UCOL_EQUAL; + } + + // Identical-prefix test. + const char16_t *leftLimit; + const char16_t *rightLimit; + int32_t equalPrefixLength = 0; + if(leftLength < 0) { + leftLimit = nullptr; + rightLimit = nullptr; + char16_t c; + while((c = left[equalPrefixLength]) == right[equalPrefixLength]) { + if(c == 0) { return UCOL_EQUAL; } + ++equalPrefixLength; + } + } else { + leftLimit = left + leftLength; + rightLimit = right + rightLength; + for(;;) { + if(equalPrefixLength == leftLength) { + if(equalPrefixLength == rightLength) { return UCOL_EQUAL; } + break; + } else if(equalPrefixLength == rightLength || + left[equalPrefixLength] != right[equalPrefixLength]) { + break; + } + ++equalPrefixLength; + } + } + + UBool numeric = settings->isNumeric(); + if(equalPrefixLength > 0) { + if((equalPrefixLength != leftLength && + data->isUnsafeBackward(left[equalPrefixLength], numeric)) || + (equalPrefixLength != rightLength && + data->isUnsafeBackward(right[equalPrefixLength], numeric))) { + // Identical prefix: Back up to the start of a contraction or reordering sequence. + while(--equalPrefixLength > 0 && + data->isUnsafeBackward(left[equalPrefixLength], numeric)) {} + } + // Notes: + // - A longer string can compare equal to a prefix of it if only ignorables follow. + // - With a backward level, a longer string can compare less-than a prefix of it. + + // Pass the actual start of each string into the CollationIterators, + // plus the equalPrefixLength position, + // so that prefix matches back into the equal prefix work. + } + + int32_t result; + int32_t fastLatinOptions = settings->fastLatinOptions; + if(fastLatinOptions >= 0 && + (equalPrefixLength == leftLength || + left[equalPrefixLength] <= CollationFastLatin::LATIN_MAX) && + (equalPrefixLength == rightLength || + right[equalPrefixLength] <= CollationFastLatin::LATIN_MAX)) { + if(leftLength >= 0) { + result = CollationFastLatin::compareUTF16(data->fastLatinTable, + settings->fastLatinPrimaries, + fastLatinOptions, + left + equalPrefixLength, + leftLength - equalPrefixLength, + right + equalPrefixLength, + rightLength - equalPrefixLength); + } else { + result = CollationFastLatin::compareUTF16(data->fastLatinTable, + settings->fastLatinPrimaries, + fastLatinOptions, + left + equalPrefixLength, -1, + right + equalPrefixLength, -1); + } + } else { + result = CollationFastLatin::BAIL_OUT_RESULT; + } + + if(result == CollationFastLatin::BAIL_OUT_RESULT) { + if(settings->dontCheckFCD()) { + UTF16CollationIterator leftIter(data, numeric, + left, left + equalPrefixLength, leftLimit); + UTF16CollationIterator rightIter(data, numeric, + right, right + equalPrefixLength, rightLimit); + result = CollationCompare::compareUpToQuaternary(leftIter, rightIter, *settings, errorCode); + } else { + FCDUTF16CollationIterator leftIter(data, numeric, + left, left + equalPrefixLength, leftLimit); + FCDUTF16CollationIterator rightIter(data, numeric, + right, right + equalPrefixLength, rightLimit); + result = CollationCompare::compareUpToQuaternary(leftIter, rightIter, *settings, errorCode); + } + } + if(result != UCOL_EQUAL || settings->getStrength() < UCOL_IDENTICAL || U_FAILURE(errorCode)) { + return (UCollationResult)result; + } + + // Note: If NUL-terminated, we could get the actual limits from the iterators now. + // That would complicate the iterators a bit, NUL-terminated strings are only a C convenience, + // and the benefit seems unlikely to be measurable. + + // Compare identical level. + const Normalizer2Impl &nfcImpl = data->nfcImpl; + left += equalPrefixLength; + right += equalPrefixLength; + if(settings->dontCheckFCD()) { + UTF16NFDIterator leftIter(left, leftLimit); + UTF16NFDIterator rightIter(right, rightLimit); + return compareNFDIter(nfcImpl, leftIter, rightIter); + } else { + FCDUTF16NFDIterator leftIter(nfcImpl, left, leftLimit); + FCDUTF16NFDIterator rightIter(nfcImpl, right, rightLimit); + return compareNFDIter(nfcImpl, leftIter, rightIter); + } +} + +UCollationResult +RuleBasedCollator::doCompare(const uint8_t *left, int32_t leftLength, + const uint8_t *right, int32_t rightLength, + UErrorCode &errorCode) const { + // U_FAILURE(errorCode) checked by caller. + if(left == right && leftLength == rightLength) { + return UCOL_EQUAL; + } + + // Identical-prefix test. + int32_t equalPrefixLength = 0; + if(leftLength < 0) { + uint8_t c; + while((c = left[equalPrefixLength]) == right[equalPrefixLength]) { + if(c == 0) { return UCOL_EQUAL; } + ++equalPrefixLength; + } + } else { + for(;;) { + if(equalPrefixLength == leftLength) { + if(equalPrefixLength == rightLength) { return UCOL_EQUAL; } + break; + } else if(equalPrefixLength == rightLength || + left[equalPrefixLength] != right[equalPrefixLength]) { + break; + } + ++equalPrefixLength; + } + } + // Back up to the start of a partially-equal code point. + if(equalPrefixLength > 0 && + ((equalPrefixLength != leftLength && U8_IS_TRAIL(left[equalPrefixLength])) || + (equalPrefixLength != rightLength && U8_IS_TRAIL(right[equalPrefixLength])))) { + while(--equalPrefixLength > 0 && U8_IS_TRAIL(left[equalPrefixLength])) {} + } + + UBool numeric = settings->isNumeric(); + if(equalPrefixLength > 0) { + UBool unsafe = false; + if(equalPrefixLength != leftLength) { + int32_t i = equalPrefixLength; + UChar32 c; + U8_NEXT_OR_FFFD(left, i, leftLength, c); + unsafe = data->isUnsafeBackward(c, numeric); + } + if(!unsafe && equalPrefixLength != rightLength) { + int32_t i = equalPrefixLength; + UChar32 c; + U8_NEXT_OR_FFFD(right, i, rightLength, c); + unsafe = data->isUnsafeBackward(c, numeric); + } + if(unsafe) { + // Identical prefix: Back up to the start of a contraction or reordering sequence. + UChar32 c; + do { + U8_PREV_OR_FFFD(left, 0, equalPrefixLength, c); + } while(equalPrefixLength > 0 && data->isUnsafeBackward(c, numeric)); + } + // See the notes in the UTF-16 version. + + // Pass the actual start of each string into the CollationIterators, + // plus the equalPrefixLength position, + // so that prefix matches back into the equal prefix work. + } + + int32_t result; + int32_t fastLatinOptions = settings->fastLatinOptions; + if(fastLatinOptions >= 0 && + (equalPrefixLength == leftLength || + left[equalPrefixLength] <= CollationFastLatin::LATIN_MAX_UTF8_LEAD) && + (equalPrefixLength == rightLength || + right[equalPrefixLength] <= CollationFastLatin::LATIN_MAX_UTF8_LEAD)) { + if(leftLength >= 0) { + result = CollationFastLatin::compareUTF8(data->fastLatinTable, + settings->fastLatinPrimaries, + fastLatinOptions, + left + equalPrefixLength, + leftLength - equalPrefixLength, + right + equalPrefixLength, + rightLength - equalPrefixLength); + } else { + result = CollationFastLatin::compareUTF8(data->fastLatinTable, + settings->fastLatinPrimaries, + fastLatinOptions, + left + equalPrefixLength, -1, + right + equalPrefixLength, -1); + } + } else { + result = CollationFastLatin::BAIL_OUT_RESULT; + } + + if(result == CollationFastLatin::BAIL_OUT_RESULT) { + if(settings->dontCheckFCD()) { + UTF8CollationIterator leftIter(data, numeric, left, equalPrefixLength, leftLength); + UTF8CollationIterator rightIter(data, numeric, right, equalPrefixLength, rightLength); + result = CollationCompare::compareUpToQuaternary(leftIter, rightIter, *settings, errorCode); + } else { + FCDUTF8CollationIterator leftIter(data, numeric, left, equalPrefixLength, leftLength); + FCDUTF8CollationIterator rightIter(data, numeric, right, equalPrefixLength, rightLength); + result = CollationCompare::compareUpToQuaternary(leftIter, rightIter, *settings, errorCode); + } + } + if(result != UCOL_EQUAL || settings->getStrength() < UCOL_IDENTICAL || U_FAILURE(errorCode)) { + return (UCollationResult)result; + } + + // Note: If NUL-terminated, we could get the actual limits from the iterators now. + // That would complicate the iterators a bit, NUL-terminated strings are only a C convenience, + // and the benefit seems unlikely to be measurable. + + // Compare identical level. + const Normalizer2Impl &nfcImpl = data->nfcImpl; + left += equalPrefixLength; + right += equalPrefixLength; + if(leftLength > 0) { + leftLength -= equalPrefixLength; + rightLength -= equalPrefixLength; + } + if(settings->dontCheckFCD()) { + UTF8NFDIterator leftIter(left, leftLength); + UTF8NFDIterator rightIter(right, rightLength); + return compareNFDIter(nfcImpl, leftIter, rightIter); + } else { + FCDUTF8NFDIterator leftIter(data, left, leftLength); + FCDUTF8NFDIterator rightIter(data, right, rightLength); + return compareNFDIter(nfcImpl, leftIter, rightIter); + } +} + +UCollationResult +RuleBasedCollator::compare(UCharIterator &left, UCharIterator &right, + UErrorCode &errorCode) const { + if(U_FAILURE(errorCode) || &left == &right) { return UCOL_EQUAL; } + UBool numeric = settings->isNumeric(); + + // Identical-prefix test. + int32_t equalPrefixLength = 0; + { + UChar32 leftUnit; + UChar32 rightUnit; + while((leftUnit = left.next(&left)) == (rightUnit = right.next(&right))) { + if(leftUnit < 0) { return UCOL_EQUAL; } + ++equalPrefixLength; + } + + // Back out the code units that differed, for the real collation comparison. + if(leftUnit >= 0) { left.previous(&left); } + if(rightUnit >= 0) { right.previous(&right); } + + if(equalPrefixLength > 0) { + if((leftUnit >= 0 && data->isUnsafeBackward(leftUnit, numeric)) || + (rightUnit >= 0 && data->isUnsafeBackward(rightUnit, numeric))) { + // Identical prefix: Back up to the start of a contraction or reordering sequence. + do { + --equalPrefixLength; + leftUnit = left.previous(&left); + right.previous(&right); + } while(equalPrefixLength > 0 && data->isUnsafeBackward(leftUnit, numeric)); + } + // See the notes in the UTF-16 version. + } + } + + UCollationResult result; + if(settings->dontCheckFCD()) { + UIterCollationIterator leftIter(data, numeric, left); + UIterCollationIterator rightIter(data, numeric, right); + result = CollationCompare::compareUpToQuaternary(leftIter, rightIter, *settings, errorCode); + } else { + FCDUIterCollationIterator leftIter(data, numeric, left, equalPrefixLength); + FCDUIterCollationIterator rightIter(data, numeric, right, equalPrefixLength); + result = CollationCompare::compareUpToQuaternary(leftIter, rightIter, *settings, errorCode); + } + if(result != UCOL_EQUAL || settings->getStrength() < UCOL_IDENTICAL || U_FAILURE(errorCode)) { + return result; + } + + // Compare identical level. + left.move(&left, equalPrefixLength, UITER_ZERO); + right.move(&right, equalPrefixLength, UITER_ZERO); + const Normalizer2Impl &nfcImpl = data->nfcImpl; + if(settings->dontCheckFCD()) { + UIterNFDIterator leftIter(left); + UIterNFDIterator rightIter(right); + return compareNFDIter(nfcImpl, leftIter, rightIter); + } else { + FCDUIterNFDIterator leftIter(data, left, equalPrefixLength); + FCDUIterNFDIterator rightIter(data, right, equalPrefixLength); + return compareNFDIter(nfcImpl, leftIter, rightIter); + } +} + +CollationKey & +RuleBasedCollator::getCollationKey(const UnicodeString &s, CollationKey &key, + UErrorCode &errorCode) const { + return getCollationKey(s.getBuffer(), s.length(), key, errorCode); +} + +CollationKey & +RuleBasedCollator::getCollationKey(const char16_t *s, int32_t length, CollationKey& key, + UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { + return key.setToBogus(); + } + if(s == nullptr && length != 0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return key.setToBogus(); + } + key.reset(); // resets the "bogus" state + CollationKeyByteSink sink(key); + writeSortKey(s, length, sink, errorCode); + if(U_FAILURE(errorCode)) { + key.setToBogus(); + } else if(key.isBogus()) { + errorCode = U_MEMORY_ALLOCATION_ERROR; + } else { + key.setLength(sink.NumberOfBytesAppended()); + } + return key; +} + +int32_t +RuleBasedCollator::getSortKey(const UnicodeString &s, + uint8_t *dest, int32_t capacity) const { + return getSortKey(s.getBuffer(), s.length(), dest, capacity); +} + +int32_t +RuleBasedCollator::getSortKey(const char16_t *s, int32_t length, + uint8_t *dest, int32_t capacity) const { + if((s == nullptr && length != 0) || capacity < 0 || (dest == nullptr && capacity > 0)) { + return 0; + } + uint8_t noDest[1] = { 0 }; + if(dest == nullptr) { + // Distinguish pure preflighting from an allocation error. + dest = noDest; + capacity = 0; + } + FixedSortKeyByteSink sink(reinterpret_cast(dest), capacity); + UErrorCode errorCode = U_ZERO_ERROR; + writeSortKey(s, length, sink, errorCode); + return U_SUCCESS(errorCode) ? sink.NumberOfBytesAppended() : 0; +} + +void +RuleBasedCollator::writeSortKey(const char16_t *s, int32_t length, + SortKeyByteSink &sink, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return; } + const char16_t *limit = (length >= 0) ? s + length : nullptr; + UBool numeric = settings->isNumeric(); + CollationKeys::LevelCallback callback; + if(settings->dontCheckFCD()) { + UTF16CollationIterator iter(data, numeric, s, s, limit); + CollationKeys::writeSortKeyUpToQuaternary(iter, data->compressibleBytes, *settings, + sink, Collation::PRIMARY_LEVEL, + callback, true, errorCode); + } else { + FCDUTF16CollationIterator iter(data, numeric, s, s, limit); + CollationKeys::writeSortKeyUpToQuaternary(iter, data->compressibleBytes, *settings, + sink, Collation::PRIMARY_LEVEL, + callback, true, errorCode); + } + if(settings->getStrength() == UCOL_IDENTICAL) { + writeIdenticalLevel(s, limit, sink, errorCode); + } + static const char terminator = 0; // TERMINATOR_BYTE + sink.Append(&terminator, 1); +} + +void +RuleBasedCollator::writeIdenticalLevel(const char16_t *s, const char16_t *limit, + SortKeyByteSink &sink, UErrorCode &errorCode) const { + // NFD quick check + const char16_t *nfdQCYesLimit = data->nfcImpl.decompose(s, limit, nullptr, errorCode); + if(U_FAILURE(errorCode)) { return; } + sink.Append(Collation::LEVEL_SEPARATOR_BYTE); + UChar32 prev = 0; + if(nfdQCYesLimit != s) { + prev = u_writeIdenticalLevelRun(prev, s, (int32_t)(nfdQCYesLimit - s), sink); + } + // Is there non-NFD text? + int32_t destLengthEstimate; + if(limit != nullptr) { + if(nfdQCYesLimit == limit) { return; } + destLengthEstimate = (int32_t)(limit - nfdQCYesLimit); + } else { + // s is NUL-terminated + if(*nfdQCYesLimit == 0) { return; } + destLengthEstimate = -1; + } + UnicodeString nfd; + data->nfcImpl.decompose(nfdQCYesLimit, limit, nfd, destLengthEstimate, errorCode); + u_writeIdenticalLevelRun(prev, nfd.getBuffer(), nfd.length(), sink); +} + +namespace { + +/** + * internalNextSortKeyPart() calls CollationKeys::writeSortKeyUpToQuaternary() + * with an instance of this callback class. + * When another level is about to be written, the callback + * records the level and the number of bytes that will be written until + * the sink (which is actually a FixedSortKeyByteSink) fills up. + * + * When internalNextSortKeyPart() is called again, it restarts with the last level + * and ignores as many bytes as were written previously for that level. + */ +class PartLevelCallback : public CollationKeys::LevelCallback { +public: + PartLevelCallback(const SortKeyByteSink &s) + : sink(s), level(Collation::PRIMARY_LEVEL) { + levelCapacity = sink.GetRemainingCapacity(); + } + virtual ~PartLevelCallback() {} + virtual UBool needToWrite(Collation::Level l) override { + if(!sink.Overflowed()) { + // Remember a level that will be at least partially written. + level = l; + levelCapacity = sink.GetRemainingCapacity(); + return true; + } else { + return false; + } + } + Collation::Level getLevel() const { return level; } + int32_t getLevelCapacity() const { return levelCapacity; } + +private: + const SortKeyByteSink &sink; + Collation::Level level; + int32_t levelCapacity; +}; + +} // namespace + +int32_t +RuleBasedCollator::internalNextSortKeyPart(UCharIterator *iter, uint32_t state[2], + uint8_t *dest, int32_t count, UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return 0; } + if(iter == nullptr || state == nullptr || count < 0 || (count > 0 && dest == nullptr)) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + if(count == 0) { return 0; } + + FixedSortKeyByteSink sink(reinterpret_cast(dest), count); + sink.IgnoreBytes((int32_t)state[1]); + iter->move(iter, 0, UITER_START); + + Collation::Level level = (Collation::Level)state[0]; + if(level <= Collation::QUATERNARY_LEVEL) { + UBool numeric = settings->isNumeric(); + PartLevelCallback callback(sink); + if(settings->dontCheckFCD()) { + UIterCollationIterator ci(data, numeric, *iter); + CollationKeys::writeSortKeyUpToQuaternary(ci, data->compressibleBytes, *settings, + sink, level, callback, false, errorCode); + } else { + FCDUIterCollationIterator ci(data, numeric, *iter, 0); + CollationKeys::writeSortKeyUpToQuaternary(ci, data->compressibleBytes, *settings, + sink, level, callback, false, errorCode); + } + if(U_FAILURE(errorCode)) { return 0; } + if(sink.NumberOfBytesAppended() > count) { + state[0] = (uint32_t)callback.getLevel(); + state[1] = (uint32_t)callback.getLevelCapacity(); + return count; + } + // All of the normal levels are done. + if(settings->getStrength() == UCOL_IDENTICAL) { + level = Collation::IDENTICAL_LEVEL; + iter->move(iter, 0, UITER_START); + } + // else fall through to setting ZERO_LEVEL + } + + if(level == Collation::IDENTICAL_LEVEL) { + int32_t levelCapacity = sink.GetRemainingCapacity(); + UnicodeString s; + for(;;) { + UChar32 c = iter->next(iter); + if(c < 0) { break; } + s.append((char16_t)c); + } + const char16_t *sArray = s.getBuffer(); + writeIdenticalLevel(sArray, sArray + s.length(), sink, errorCode); + if(U_FAILURE(errorCode)) { return 0; } + if(sink.NumberOfBytesAppended() > count) { + state[0] = (uint32_t)level; + state[1] = (uint32_t)levelCapacity; + return count; + } + } + + // ZERO_LEVEL: Fill the remainder of dest with 00 bytes. + state[0] = (uint32_t)Collation::ZERO_LEVEL; + state[1] = 0; + int32_t length = sink.NumberOfBytesAppended(); + int32_t i = length; + while(i < count) { dest[i++] = 0; } + return length; +} + +void +RuleBasedCollator::internalGetCEs(const UnicodeString &str, UVector64 &ces, + UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return; } + const char16_t *s = str.getBuffer(); + const char16_t *limit = s + str.length(); + UBool numeric = settings->isNumeric(); + if(settings->dontCheckFCD()) { + UTF16CollationIterator iter(data, numeric, s, s, limit); + int64_t ce; + while((ce = iter.nextCE(errorCode)) != Collation::NO_CE) { + ces.addElement(ce, errorCode); + } + } else { + FCDUTF16CollationIterator iter(data, numeric, s, s, limit); + int64_t ce; + while((ce = iter.nextCE(errorCode)) != Collation::NO_CE) { + ces.addElement(ce, errorCode); + } + } +} + +namespace { + +void appendSubtag(CharString &s, char letter, const char *subtag, int32_t length, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode) || length == 0) { return; } + if(!s.isEmpty()) { + s.append('_', errorCode); + } + s.append(letter, errorCode); + for(int32_t i = 0; i < length; ++i) { + s.append(uprv_toupper(subtag[i]), errorCode); + } +} + +void appendAttribute(CharString &s, char letter, UColAttributeValue value, + UErrorCode &errorCode) { + if(U_FAILURE(errorCode)) { return; } + if(!s.isEmpty()) { + s.append('_', errorCode); + } + static const char *valueChars = "1234...........IXO..SN..LU......"; + s.append(letter, errorCode); + s.append(valueChars[value], errorCode); +} + +} // namespace + +int32_t +RuleBasedCollator::internalGetShortDefinitionString(const char *locale, + char *buffer, int32_t capacity, + UErrorCode &errorCode) const { + if(U_FAILURE(errorCode)) { return 0; } + if(buffer == nullptr ? capacity != 0 : capacity < 0) { + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + if(locale == nullptr) { + locale = internalGetLocaleID(ULOC_VALID_LOCALE, errorCode); + } + + char resultLocale[ULOC_FULLNAME_CAPACITY + 1]; + int32_t length = ucol_getFunctionalEquivalent(resultLocale, ULOC_FULLNAME_CAPACITY, + "collation", locale, + nullptr, &errorCode); + if(U_FAILURE(errorCode)) { return 0; } + resultLocale[length] = 0; + + // Append items in alphabetic order of their short definition letters. + CharString result; + char subtag[ULOC_KEYWORD_AND_VALUES_CAPACITY]; + + if(attributeHasBeenSetExplicitly(UCOL_ALTERNATE_HANDLING)) { + appendAttribute(result, 'A', getAttribute(UCOL_ALTERNATE_HANDLING, errorCode), errorCode); + } + // ATTR_VARIABLE_TOP not supported because 'B' was broken. + // See ICU tickets #10372 and #10386. + if(attributeHasBeenSetExplicitly(UCOL_CASE_FIRST)) { + appendAttribute(result, 'C', getAttribute(UCOL_CASE_FIRST, errorCode), errorCode); + } + if(attributeHasBeenSetExplicitly(UCOL_NUMERIC_COLLATION)) { + appendAttribute(result, 'D', getAttribute(UCOL_NUMERIC_COLLATION, errorCode), errorCode); + } + if(attributeHasBeenSetExplicitly(UCOL_CASE_LEVEL)) { + appendAttribute(result, 'E', getAttribute(UCOL_CASE_LEVEL, errorCode), errorCode); + } + if(attributeHasBeenSetExplicitly(UCOL_FRENCH_COLLATION)) { + appendAttribute(result, 'F', getAttribute(UCOL_FRENCH_COLLATION, errorCode), errorCode); + } + // Note: UCOL_HIRAGANA_QUATERNARY_MODE is deprecated and never changes away from default. + length = uloc_getKeywordValue(resultLocale, "collation", subtag, UPRV_LENGTHOF(subtag), &errorCode); + appendSubtag(result, 'K', subtag, length, errorCode); + length = uloc_getLanguage(resultLocale, subtag, UPRV_LENGTHOF(subtag), &errorCode); + if (length == 0) { + appendSubtag(result, 'L', "root", 4, errorCode); + } else { + appendSubtag(result, 'L', subtag, length, errorCode); + } + if(attributeHasBeenSetExplicitly(UCOL_NORMALIZATION_MODE)) { + appendAttribute(result, 'N', getAttribute(UCOL_NORMALIZATION_MODE, errorCode), errorCode); + } + length = uloc_getCountry(resultLocale, subtag, UPRV_LENGTHOF(subtag), &errorCode); + appendSubtag(result, 'R', subtag, length, errorCode); + if(attributeHasBeenSetExplicitly(UCOL_STRENGTH)) { + appendAttribute(result, 'S', getAttribute(UCOL_STRENGTH, errorCode), errorCode); + } + length = uloc_getVariant(resultLocale, subtag, UPRV_LENGTHOF(subtag), &errorCode); + appendSubtag(result, 'V', subtag, length, errorCode); + length = uloc_getScript(resultLocale, subtag, UPRV_LENGTHOF(subtag), &errorCode); + appendSubtag(result, 'Z', subtag, length, errorCode); + + if(U_FAILURE(errorCode)) { return 0; } + return result.extract(buffer, capacity, errorCode); +} + +UBool +RuleBasedCollator::isUnsafe(UChar32 c) const { + return data->isUnsafeBackward(c, settings->isNumeric()); +} + +void U_CALLCONV +RuleBasedCollator::computeMaxExpansions(const CollationTailoring *t, UErrorCode &errorCode) { + t->maxExpansions = CollationElementIterator::computeMaxExpansions(t->data, errorCode); +} + +UBool +RuleBasedCollator::initMaxExpansions(UErrorCode &errorCode) const { + umtx_initOnce(tailoring->maxExpansionsInitOnce, computeMaxExpansions, tailoring, errorCode); + return U_SUCCESS(errorCode); +} + +CollationElementIterator * +RuleBasedCollator::createCollationElementIterator(const UnicodeString& source) const { + UErrorCode errorCode = U_ZERO_ERROR; + if(!initMaxExpansions(errorCode)) { return nullptr; } + CollationElementIterator *cei = new CollationElementIterator(source, this, errorCode); + if(U_FAILURE(errorCode)) { + delete cei; + return nullptr; + } + return cei; +} + +CollationElementIterator * +RuleBasedCollator::createCollationElementIterator(const CharacterIterator& source) const { + UErrorCode errorCode = U_ZERO_ERROR; + if(!initMaxExpansions(errorCode)) { return nullptr; } + CollationElementIterator *cei = new CollationElementIterator(source, this, errorCode); + if(U_FAILURE(errorCode)) { + delete cei; + return nullptr; + } + return cei; +} + +int32_t +RuleBasedCollator::getMaxExpansion(int32_t order) const { + UErrorCode errorCode = U_ZERO_ERROR; + (void)initMaxExpansions(errorCode); + return CollationElementIterator::getMaxExpansion(tailoring->maxExpansions, order); +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_COLLATION diff --git a/deps/icu-small/source/i18n/scientificnumberformatter.cpp b/deps/icu-small/source/i18n/scientificnumberformatter.cpp index 99b990708ab676..046c7d58855c37 100644 --- a/deps/icu-small/source/i18n/scientificnumberformatter.cpp +++ b/deps/icu-small/source/i18n/scientificnumberformatter.cpp @@ -1,305 +1,305 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (c) 2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -*/ -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/scientificnumberformatter.h" -#include "unicode/dcfmtsym.h" -#include "unicode/fpositer.h" -#include "unicode/utf16.h" -#include "unicode/uniset.h" -#include "unicode/decimfmt.h" -#include "static_unicode_sets.h" - -U_NAMESPACE_BEGIN - -static const UChar kSuperscriptDigits[] = { - 0x2070, - 0xB9, - 0xB2, - 0xB3, - 0x2074, - 0x2075, - 0x2076, - 0x2077, - 0x2078, - 0x2079}; - -static const UChar kSuperscriptPlusSign = 0x207A; -static const UChar kSuperscriptMinusSign = 0x207B; - -static UBool copyAsSuperscript( - const UnicodeString &s, - int32_t beginIndex, - int32_t endIndex, - UnicodeString &result, - UErrorCode &status) { - if (U_FAILURE(status)) { - return false; - } - for (int32_t i = beginIndex; i < endIndex;) { - UChar32 c = s.char32At(i); - int32_t digit = u_charDigitValue(c); - if (digit < 0) { - status = U_INVALID_CHAR_FOUND; - return false; - } - result.append(kSuperscriptDigits[digit]); - i += U16_LENGTH(c); - } - return true; -} - -ScientificNumberFormatter *ScientificNumberFormatter::createSuperscriptInstance( - DecimalFormat *fmtToAdopt, UErrorCode &status) { - return createInstance(fmtToAdopt, new SuperscriptStyle(), status); -} - -ScientificNumberFormatter *ScientificNumberFormatter::createSuperscriptInstance( - const Locale &locale, UErrorCode &status) { - return createInstance( - static_cast( - DecimalFormat::createScientificInstance(locale, status)), - new SuperscriptStyle(), - status); -} - -ScientificNumberFormatter *ScientificNumberFormatter::createMarkupInstance( - DecimalFormat *fmtToAdopt, - const UnicodeString &beginMarkup, - const UnicodeString &endMarkup, - UErrorCode &status) { - return createInstance( - fmtToAdopt, - new MarkupStyle(beginMarkup, endMarkup), - status); -} - -ScientificNumberFormatter *ScientificNumberFormatter::createMarkupInstance( - const Locale &locale, - const UnicodeString &beginMarkup, - const UnicodeString &endMarkup, - UErrorCode &status) { - return createInstance( - static_cast( - DecimalFormat::createScientificInstance(locale, status)), - new MarkupStyle(beginMarkup, endMarkup), - status); -} - -ScientificNumberFormatter *ScientificNumberFormatter::createInstance( - DecimalFormat *fmtToAdopt, - Style *styleToAdopt, - UErrorCode &status) { - LocalPointer fmt(fmtToAdopt); - LocalPointer